summaryrefslogtreecommitdiffstats
path: root/source
diff options
context:
space:
mode:
Diffstat (limited to 'source')
-rw-r--r--source/.cvsignore31
-rw-r--r--source/.dmallocrc2
-rw-r--r--source/Doxyfile176
-rw-r--r--source/Makefile.in1526
-rw-r--r--source/VERSION123
-rw-r--r--source/aclocal.m4775
-rw-r--r--source/aparser/Makefile17
-rwxr-xr-xsource/aparser/build13
-rw-r--r--source/aparser/cifs.struct1029
-rw-r--r--source/aparser/dump.awk70
-rw-r--r--source/aparser/harness.awk17
-rw-r--r--source/aparser/header.awk80
-rw-r--r--source/aparser/main.awk25
-rw-r--r--source/aparser/parsefn.awk271
-rw-r--r--source/aparser/parser.c471
-rw-r--r--source/aparser/parser.h103
-rw-r--r--source/aparser/parserel.awk213
-rw-r--r--source/aparser/parsetree.awk224
-rw-r--r--source/aparser/spool.struct90
-rw-r--r--source/aparser/spool_io_printer_driver_info_level_3.prsbin0 -> 5636 bytes
-rw-r--r--source/aparser/spool_io_printer_driver_info_level_6.prsbin0 -> 5636 bytes
-rw-r--r--source/aparser/srvsvc.struct184
-rw-r--r--source/aparser/srvsvc2.struct655
-rw-r--r--source/aparser/template.awk18
-rw-r--r--source/aparser/templates/fn_end.tpl13
-rw-r--r--source/aparser/templates/fn_end0.tpl8
-rw-r--r--source/aparser/templates/fn_i_end.tpl12
-rw-r--r--source/aparser/templates/fn_i_start.tpl15
-rw-r--r--source/aparser/templates/fn_mid.tpl6
-rw-r--r--source/aparser/templates/fn_start.tpl17
-rw-r--r--source/aparser/templates/harness.tpl5
-rw-r--r--source/aparser/templates/harness_end.tpl7
-rw-r--r--source/aparser/templates/harness_start.tpl7
-rw-r--r--source/aparser/templates/ifptr_end.tpl1
-rw-r--r--source/aparser/templates/ifptr_start.tpl2
-rw-r--r--source/aparser/templates/module_end.tpl3
-rw-r--r--source/aparser/templates/module_start.tpl5
-rw-r--r--source/aparser/templates/prs_.align.tpl1
-rw-r--r--source/aparser/templates/prs_align2.tpl1
-rw-r--r--source/aparser/templates/prs_align4.tpl1
-rw-r--r--source/aparser/templates/prs_array.tpl8
-rw-r--r--source/aparser/templates/prs_array_optional.tpl5
-rw-r--r--source/aparser/templates/prs_array_remainder.tpl17
-rw-r--r--source/aparser/templates/prs_break.tpl1
-rw-r--r--source/aparser/templates/prs_case.tpl1
-rw-r--r--source/aparser/templates/prs_case_end.tpl1
-rw-r--r--source/aparser/templates/prs_element.tpl1
-rw-r--r--source/aparser/templates/prs_pointer.tpl2
-rw-r--r--source/aparser/templates/prs_struct.tpl1
-rw-r--r--source/aparser/templates/prs_struct_alloc.tpl1
-rw-r--r--source/aparser/templates/prs_uint16.tpl1
-rw-r--r--source/aparser/templates/prs_uint32.tpl1
-rw-r--r--source/aparser/templates/prs_uint8s.tpl2
-rw-r--r--source/aparser/templates/prs_uint8s_fixed.tpl1
-rw-r--r--source/aparser/templates/prs_wstring.tpl2
-rw-r--r--source/aparser/templates/prs_wstring_fixed.tpl2
-rw-r--r--source/aparser/templates/union_end.tpl5
-rw-r--r--source/aparser/templates/union_start.tpl1
-rw-r--r--source/aparser/token.awk180
-rw-r--r--source/aparser/util.awk39
-rw-r--r--source/aparser/util.c112
-rw-r--r--source/aparser/vluke.c41
-rw-r--r--source/auth/auth.c534
-rw-r--r--source/auth/auth_builtin.c175
-rw-r--r--source/auth/auth_compat.c122
-rw-r--r--source/auth/auth_domain.c424
-rw-r--r--source/auth/auth_ntlmssp.c200
-rw-r--r--source/auth/auth_rhosts.c257
-rw-r--r--source/auth/auth_sam.c343
-rw-r--r--source/auth/auth_server.c417
-rw-r--r--source/auth/auth_unix.c136
-rw-r--r--source/auth/auth_util.c1500
-rw-r--r--source/auth/auth_winbind.c171
-rw-r--r--source/auth/pampass.c875
-rw-r--r--source/auth/pass_check.c783
-rwxr-xr-xsource/autogen.sh65
-rw-r--r--source/bin/.cvsignore56
-rw-r--r--source/change-log1878
-rw-r--r--source/client/.cvsignore1
-rw-r--r--source/client/client.c3085
-rw-r--r--source/client/clitar.c1807
-rwxr-xr-xsource/client/mount.cifs.c825
-rw-r--r--source/client/smbmnt.c306
-rw-r--r--source/client/smbmount.c940
-rw-r--r--source/client/smbspool.c363
-rw-r--r--source/client/smbumount.c186
-rw-r--r--source/client/tree.c811
-rw-r--r--source/codepages/.cvsignore0
-rw-r--r--source/codepages/lowcase.datbin0 -> 131072 bytes
-rw-r--r--source/codepages/upcase.datbin0 -> 131072 bytes
-rw-r--r--source/codepages/valid.datbin0 -> 65536 bytes
-rwxr-xr-xsource/config.guess1409
-rwxr-xr-xsource/config.sub1421
-rwxr-xr-xsource/configure.developer2
-rw-r--r--source/configure.in4442
-rw-r--r--source/dynconfig.c72
-rw-r--r--source/groupdb/.cvsignore2
-rw-r--r--source/groupdb/mapping.c1438
-rw-r--r--source/include/.cvsignore7
-rw-r--r--source/include/MacExtensions.h246
-rw-r--r--source/include/ads.h226
-rw-r--r--source/include/adt_tree.h38
-rw-r--r--source/include/asn_1.h69
-rw-r--r--source/include/auth.h172
-rw-r--r--source/include/authdata.h152
-rw-r--r--source/include/byteorder.h171
-rw-r--r--source/include/charset.h127
-rw-r--r--source/include/client.h169
-rw-r--r--source/include/clitar.h41
-rw-r--r--source/include/debug.h201
-rw-r--r--source/include/dlinklist.h78
-rw-r--r--source/include/doserr.h233
-rw-r--r--source/include/dynconfig.h38
-rw-r--r--source/include/fake_file.h46
-rw-r--r--source/include/genparser.h78
-rw-r--r--source/include/genparser_samba.h63
-rw-r--r--source/include/gums.h272
-rw-r--r--source/include/hash.h74
-rw-r--r--source/include/hmacmd5.h32
-rw-r--r--source/include/idmap.h57
-rw-r--r--source/include/includes.h1333
-rw-r--r--source/include/interfaces.h12
-rw-r--r--source/include/intl.h25
-rw-r--r--source/include/libsmb_internal.h67
-rw-r--r--source/include/libsmbclient.h1947
-rw-r--r--source/include/local.h233
-rw-r--r--source/include/mangle.h14
-rw-r--r--source/include/mapping.h33
-rw-r--r--source/include/md5.h24
-rw-r--r--source/include/messages.h71
-rw-r--r--source/include/modconf.h34
-rw-r--r--source/include/module.h38
-rw-r--r--source/include/msdfs.h85
-rw-r--r--source/include/nameserv.h627
-rw-r--r--source/include/nt_printing.h484
-rw-r--r--source/include/nt_status.h63
-rw-r--r--source/include/ntdomain.h417
-rw-r--r--source/include/nterr.h564
-rw-r--r--source/include/ntioctl.h87
-rw-r--r--source/include/ntlmssp.h169
-rw-r--r--source/include/ntquotas.h97
-rw-r--r--source/include/passdb.h573
-rw-r--r--source/include/popt_common.h49
-rw-r--r--source/include/printing.h91
-rw-r--r--source/include/privileges.h99
-rw-r--r--source/include/pstring.h36
-rwxr-xr-xsource/include/rap.h507
-rw-r--r--source/include/rpc_brs.h80
-rw-r--r--source/include/rpc_client.h28
-rw-r--r--source/include/rpc_creds.h96
-rw-r--r--source/include/rpc_dce.h361
-rw-r--r--source/include/rpc_dfs.h197
-rw-r--r--source/include/rpc_ds.h171
-rw-r--r--source/include/rpc_echo.h75
-rw-r--r--source/include/rpc_epmapper.h118
-rw-r--r--source/include/rpc_lsa.h744
-rw-r--r--source/include/rpc_misc.h408
-rw-r--r--source/include/rpc_netlogon.h930
-rw-r--r--source/include/rpc_parse.h30
-rw-r--r--source/include/rpc_reg.h644
-rw-r--r--source/include/rpc_samr.h1862
-rw-r--r--source/include/rpc_secdes.h469
-rw-r--r--source/include/rpc_shutdown.h70
-rwxr-xr-xsource/include/rpc_spoolss.h2234
-rw-r--r--source/include/rpc_srvsvc.h954
-rw-r--r--source/include/rpc_wkssvc.h72
-rw-r--r--source/include/safe_string.h228
-rw-r--r--source/include/samba_linux_quota.h336
-rw-r--r--source/include/samba_xfs_quota.h165
-rw-r--r--source/include/secrets.h105
-rw-r--r--source/include/session.h40
-rw-r--r--source/include/smb.h1679
-rw-r--r--source/include/smb_acls.h275
-rw-r--r--source/include/smb_macros.h264
-rw-r--r--source/include/smbldap.h159
-rw-r--r--source/include/smbprofile.h485
-rw-r--r--source/include/spnego.h65
-rw-r--r--source/include/srvstr.h33
-rw-r--r--source/include/stamp-h.in1
-rw-r--r--source/include/sysquotas.h76
-rw-r--r--source/include/talloc.h72
-rw-r--r--source/include/tdbsam2_parse_info.h2
-rw-r--r--source/include/trans2.h455
-rw-r--r--source/include/util_getent.h61
-rw-r--r--source/include/vfs.h489
-rw-r--r--source/include/vfs_macros.h318
-rw-r--r--source/include/xfile.h49
-rwxr-xr-xsource/install-sh238
-rw-r--r--source/intl/.cvsignore2
-rw-r--r--source/intl/lang_tdb.c269
-rw-r--r--source/intl/linux-msg.sed100
-rw-r--r--source/lib/.cvsignore3
-rw-r--r--source/lib/access.c333
-rw-r--r--source/lib/account_pol.c162
-rw-r--r--source/lib/adt_tree.c464
-rw-r--r--source/lib/afs.c486
-rw-r--r--source/lib/bitmap.c177
-rw-r--r--source/lib/charcnv.c1339
-rw-r--r--source/lib/clobber.c60
-rw-r--r--source/lib/crc32.c67
-rw-r--r--source/lib/data_blob.c115
-rw-r--r--source/lib/debug.c994
-rw-r--r--source/lib/dmallocmsg.c72
-rw-r--r--source/lib/dprintf.c113
-rw-r--r--source/lib/dummyroot.c33
-rw-r--r--source/lib/dummysmbd.c29
-rw-r--r--source/lib/fault.c83
-rw-r--r--source/lib/fsusage.c148
-rw-r--r--source/lib/gencache.c379
-rw-r--r--source/lib/genparser.c783
-rw-r--r--source/lib/genparser_samba.c218
-rw-r--r--source/lib/genrand.c267
-rw-r--r--source/lib/getsmbpass.c181
-rw-r--r--source/lib/hash.c316
-rw-r--r--source/lib/hmacmd5.c134
-rw-r--r--source/lib/iconv.c591
-rw-r--r--source/lib/interface.c338
-rw-r--r--source/lib/interfaces.c407
-rw-r--r--source/lib/ldap_escape.c90
-rw-r--r--source/lib/md4.c169
-rw-r--r--source/lib/md5.c247
-rw-r--r--source/lib/messages.c596
-rw-r--r--source/lib/module.c304
-rw-r--r--source/lib/ms_fnmatch.c249
-rw-r--r--source/lib/pam_errors.c126
-rw-r--r--source/lib/pidfile.c109
-rw-r--r--source/lib/popt_common.c394
-rw-r--r--source/lib/privileges.c442
-rw-r--r--source/lib/readline.c161
-rw-r--r--source/lib/replace.c452
-rw-r--r--source/lib/replace1.c42
-rw-r--r--source/lib/secace.c285
-rw-r--r--source/lib/secacl.c118
-rw-r--r--source/lib/secdesc.c522
-rw-r--r--source/lib/select.c159
-rw-r--r--source/lib/sendfile.c382
-rw-r--r--source/lib/server_mutex.c56
-rw-r--r--source/lib/signal.c139
-rw-r--r--source/lib/smbldap.c1234
-rw-r--r--source/lib/smbldap_util.c203
-rw-r--r--source/lib/smbrun.c180
-rw-r--r--source/lib/snprintf.c1021
-rw-r--r--source/lib/sock_exec.c115
-rw-r--r--source/lib/substitute.c802
-rw-r--r--source/lib/sysacls.c3198
-rw-r--r--source/lib/sysquotas.c505
-rw-r--r--source/lib/sysquotas_4A.c339
-rw-r--r--source/lib/sysquotas_linux.c560
-rw-r--r--source/lib/sysquotas_xfs.c333
-rw-r--r--source/lib/system.c1582
-rw-r--r--source/lib/system_smbd.c137
-rw-r--r--source/lib/talloc.c449
-rw-r--r--source/lib/tallocmsg.c58
-rw-r--r--source/lib/talloctort.c65
-rw-r--r--source/lib/time.c756
-rw-r--r--source/lib/ufc.c771
-rw-r--r--source/lib/username.c690
-rw-r--r--source/lib/util.c2501
-rw-r--r--source/lib/util_file.c606
-rw-r--r--source/lib/util_getent.c306
-rw-r--r--source/lib/util_pw.c89
-rw-r--r--source/lib/util_seaccess.c357
-rw-r--r--source/lib/util_sec.c467
-rw-r--r--source/lib/util_sid.c636
-rw-r--r--source/lib/util_smbd.c65
-rw-r--r--source/lib/util_sock.c1078
-rw-r--r--source/lib/util_str.c2047
-rw-r--r--source/lib/util_unistr.c847
-rw-r--r--source/lib/util_uuid.c174
-rw-r--r--source/lib/version.c42
-rw-r--r--source/lib/wins_srv.c356
-rw-r--r--source/lib/xfile.c378
-rw-r--r--source/libads/.cvsignore2
-rw-r--r--source/libads/ads_ldap.c234
-rw-r--r--source/libads/ads_status.c142
-rw-r--r--source/libads/ads_struct.c143
-rw-r--r--source/libads/ads_utils.c137
-rw-r--r--source/libads/authdata.c617
-rw-r--r--source/libads/disp_sec.c159
-rw-r--r--source/libads/kerberos.c175
-rw-r--r--source/libads/kerberos_verify.c271
-rw-r--r--source/libads/krb5_setpw.c690
-rw-r--r--source/libads/ldap.c2135
-rw-r--r--source/libads/ldap_printer.c354
-rw-r--r--source/libads/ldap_user.c119
-rw-r--r--source/libads/ldap_utils.c108
-rw-r--r--source/libads/sasl.c438
-rw-r--r--source/libads/util.c62
-rw-r--r--source/libsmb/.cvsignore3
-rw-r--r--source/libsmb/asn1.c441
-rw-r--r--source/libsmb/cliconnect.c1683
-rw-r--r--source/libsmb/clidgram.c264
-rw-r--r--source/libsmb/clientgen.c455
-rw-r--r--source/libsmb/clierror.c405
-rw-r--r--source/libsmb/clifile.c1417
-rw-r--r--source/libsmb/clifsinfo.c76
-rw-r--r--source/libsmb/clikrb5.c421
-rw-r--r--source/libsmb/clilist.c487
-rw-r--r--source/libsmb/climessage.c156
-rw-r--r--source/libsmb/clioplock.c68
-rw-r--r--source/libsmb/cliprint.c158
-rw-r--r--source/libsmb/cliquota.c633
-rw-r--r--source/libsmb/clirap.c743
-rw-r--r--source/libsmb/clirap2.c1970
-rw-r--r--source/libsmb/clireadwrite.c418
-rw-r--r--source/libsmb/clisecdesc.c139
-rw-r--r--source/libsmb/clispnego.c539
-rw-r--r--source/libsmb/clistr.c58
-rw-r--r--source/libsmb/clitrans.c658
-rw-r--r--source/libsmb/conncache.c158
-rw-r--r--source/libsmb/credentials.c215
-rw-r--r--source/libsmb/doserr.c92
-rw-r--r--source/libsmb/errormap.c1548
-rw-r--r--source/libsmb/libsmb_cache.c196
-rw-r--r--source/libsmb/libsmb_compat.c434
-rw-r--r--source/libsmb/libsmbclient.c4534
-rw-r--r--source/libsmb/namecache.c321
-rw-r--r--source/libsmb/namequery.c1406
-rw-r--r--source/libsmb/namequery_dc.c185
-rw-r--r--source/libsmb/nmblib.c1343
-rw-r--r--source/libsmb/nterr.c719
-rw-r--r--source/libsmb/ntlm_check.c377
-rw-r--r--source/libsmb/ntlmssp.c1112
-rw-r--r--source/libsmb/ntlmssp_parse.c315
-rw-r--r--source/libsmb/ntlmssp_sign.c407
-rw-r--r--source/libsmb/passchange.c152
-rw-r--r--source/libsmb/pwd_cache.c63
-rw-r--r--source/libsmb/samlogon_cache.c238
-rw-r--r--source/libsmb/smb_signing.c1052
-rw-r--r--source/libsmb/smbdes.c455
-rw-r--r--source/libsmb/smbencrypt.c526
-rw-r--r--source/libsmb/smberr.c256
-rw-r--r--source/libsmb/spnego.c343
-rw-r--r--source/libsmb/trustdom_cache.c342
-rw-r--r--source/libsmb/trusts_util.c190
-rw-r--r--source/libsmb/unexpected.c164
-rw-r--r--source/locking/brlock.c714
-rw-r--r--source/locking/locking.c881
-rw-r--r--source/locking/posix.c1312
-rw-r--r--source/mainpage.dox7
-rw-r--r--source/modules/.cvsignore1
-rw-r--r--source/modules/CP437.c136
-rw-r--r--source/modules/CP850.c122
-rw-r--r--source/modules/charset_macosxfs.c602
-rw-r--r--source/modules/developer.c132
-rw-r--r--source/modules/getdate.c2460
-rw-r--r--source/modules/getdate.h46
-rw-r--r--source/modules/getdate.y1115
-rw-r--r--source/modules/vfs_audit.c280
-rw-r--r--source/modules/vfs_cap.c448
-rw-r--r--source/modules/vfs_default_quota.c180
-rw-r--r--source/modules/vfs_expand_msdfs.c191
-rw-r--r--source/modules/vfs_extd_audit.c343
-rw-r--r--source/modules/vfs_fake_perms.c81
-rw-r--r--source/modules/vfs_netatalk.c401
-rw-r--r--source/modules/vfs_readonly.c98
-rw-r--r--source/modules/vfs_recycle.c498
-rw-r--r--source/modules/weird.c131
-rw-r--r--source/msdfs/msdfs.c1017
-rw-r--r--source/nmbd/.cvsignore0
-rw-r--r--source/nmbd/asyncdns.c353
-rw-r--r--source/nmbd/nmbd.c771
-rw-r--r--source/nmbd/nmbd_become_dmb.c386
-rw-r--r--source/nmbd/nmbd_become_lmb.c594
-rw-r--r--source/nmbd/nmbd_browserdb.c175
-rw-r--r--source/nmbd/nmbd_browsesync.c676
-rw-r--r--source/nmbd/nmbd_elections.c391
-rw-r--r--source/nmbd/nmbd_incomingdgrams.c844
-rw-r--r--source/nmbd/nmbd_incomingrequests.c587
-rw-r--r--source/nmbd/nmbd_lmhosts.c93
-rw-r--r--source/nmbd/nmbd_logonnames.c173
-rw-r--r--source/nmbd/nmbd_mynames.c224
-rw-r--r--source/nmbd/nmbd_namelistdb.c584
-rw-r--r--source/nmbd/nmbd_namequery.c268
-rw-r--r--source/nmbd/nmbd_nameregister.c537
-rw-r--r--source/nmbd/nmbd_namerelease.c222
-rw-r--r--source/nmbd/nmbd_nodestatus.c92
-rw-r--r--source/nmbd/nmbd_packets.c1885
-rw-r--r--source/nmbd/nmbd_processlogon.c522
-rw-r--r--source/nmbd/nmbd_responserecordsdb.c248
-rw-r--r--source/nmbd/nmbd_sendannounce.c581
-rw-r--r--source/nmbd/nmbd_serverlistdb.c422
-rw-r--r--source/nmbd/nmbd_subnetdb.c344
-rw-r--r--source/nmbd/nmbd_synclists.c304
-rw-r--r--source/nmbd/nmbd_winsproxy.c213
-rw-r--r--source/nmbd/nmbd_winsserver.c2015
-rw-r--r--source/nmbd/nmbd_workgroupdb.c319
-rw-r--r--source/nsswitch/.cvsignore4
-rw-r--r--source/nsswitch/pam_winbind.c754
-rw-r--r--source/nsswitch/pam_winbind.h96
-rw-r--r--source/nsswitch/wb_client.c693
-rw-r--r--source/nsswitch/wb_common.c494
-rw-r--r--source/nsswitch/wbinfo.c1313
-rw-r--r--source/nsswitch/winbind_client.h16
-rw-r--r--source/nsswitch/winbind_nss.h71
-rw-r--r--source/nsswitch/winbind_nss_aix.c1019
-rw-r--r--source/nsswitch/winbind_nss_config.h142
-rw-r--r--source/nsswitch/winbind_nss_freebsd.c81
-rw-r--r--source/nsswitch/winbind_nss_hpux.h139
-rw-r--r--source/nsswitch/winbind_nss_irix.c515
-rw-r--r--source/nsswitch/winbind_nss_irix.h48
-rw-r--r--source/nsswitch/winbind_nss_linux.c1149
-rw-r--r--source/nsswitch/winbind_nss_linux.h35
-rw-r--r--source/nsswitch/winbind_nss_solaris.c315
-rw-r--r--source/nsswitch/winbind_nss_solaris.h61
-rw-r--r--source/nsswitch/winbindd.c947
-rw-r--r--source/nsswitch/winbindd.h238
-rw-r--r--source/nsswitch/winbindd_acct.c1223
-rw-r--r--source/nsswitch/winbindd_ads.c1018
-rw-r--r--source/nsswitch/winbindd_cache.c1377
-rw-r--r--source/nsswitch/winbindd_cm.c773
-rw-r--r--source/nsswitch/winbindd_dual.c214
-rw-r--r--source/nsswitch/winbindd_group.c1211
-rw-r--r--source/nsswitch/winbindd_misc.c307
-rw-r--r--source/nsswitch/winbindd_nss.h288
-rw-r--r--source/nsswitch/winbindd_pam.c568
-rw-r--r--source/nsswitch/winbindd_passdb.c339
-rw-r--r--source/nsswitch/winbindd_rpc.c1007
-rw-r--r--source/nsswitch/winbindd_sid.c456
-rw-r--r--source/nsswitch/winbindd_user.c667
-rw-r--r--source/nsswitch/winbindd_util.c994
-rw-r--r--source/nsswitch/winbindd_wins.c226
-rw-r--r--source/nsswitch/wins.c379
-rw-r--r--source/pam_smbpass/.cvsignore1
-rw-r--r--source/pam_smbpass/CHANGELOG31
-rw-r--r--source/pam_smbpass/INSTALL64
-rw-r--r--source/pam_smbpass/README60
-rw-r--r--source/pam_smbpass/TODO7
-rw-r--r--source/pam_smbpass/general.h130
-rw-r--r--source/pam_smbpass/pam_smb_acct.c121
-rw-r--r--source/pam_smbpass/pam_smb_auth.c256
-rw-r--r--source/pam_smbpass/pam_smb_passwd.c349
-rw-r--r--source/pam_smbpass/samples/README3
-rw-r--r--source/pam_smbpass/samples/kdc-pdc15
-rw-r--r--source/pam_smbpass/samples/password-mature14
-rw-r--r--source/pam_smbpass/samples/password-migration18
-rw-r--r--source/pam_smbpass/samples/password-sync15
-rw-r--r--source/pam_smbpass/support.c635
-rw-r--r--source/pam_smbpass/support.h50
-rw-r--r--source/param/.cvsignore3
-rw-r--r--source/param/config_ldap.c351
-rw-r--r--source/param/loadparm.c4277
-rw-r--r--source/param/modconf.c96
-rw-r--r--source/param/params.c599
-rw-r--r--source/passdb/.cvsignore2
-rw-r--r--source/passdb/login_cache.c174
-rw-r--r--source/passdb/lookup_sid.c529
-rw-r--r--source/passdb/machine_sid.c213
-rw-r--r--source/passdb/passdb.c2388
-rw-r--r--source/passdb/pdb_compat.c104
-rw-r--r--source/passdb/pdb_get_set.c1158
-rw-r--r--source/passdb/pdb_guest.c179
-rw-r--r--source/passdb/pdb_gums.c464
-rw-r--r--source/passdb/pdb_interface.c1601
-rw-r--r--source/passdb/pdb_ldap.c3117
-rw-r--r--source/passdb/pdb_mysql.c497
-rw-r--r--source/passdb/pdb_pgsql.c488
-rw-r--r--source/passdb/pdb_plugin.c80
-rw-r--r--source/passdb/pdb_smbpasswd.c1517
-rw-r--r--source/passdb/pdb_sql.c493
-rw-r--r--source/passdb/pdb_tdb.c1374
-rw-r--r--source/passdb/pdb_xml.c563
-rw-r--r--source/passdb/privileges.c341
-rw-r--r--source/passdb/secrets.c824
-rw-r--r--source/passdb/util_sam_sid.c332
-rw-r--r--source/po/de.msg593
-rw-r--r--source/po/en.msg593
-rw-r--r--source/po/fr.msg593
-rwxr-xr-xsource/po/genmsg40
-rw-r--r--source/po/it.msg593
-rw-r--r--source/po/ja.msg595
-rw-r--r--source/po/nl.msg593
-rw-r--r--source/po/pl.msg593
-rw-r--r--source/po/tr.msg594
-rw-r--r--source/popt/.cvsignore8
-rw-r--r--source/popt/CHANGES43
-rw-r--r--source/popt/COPYING22
-rw-r--r--source/popt/README18
-rw-r--r--source/popt/dummy.in0
-rw-r--r--source/popt/findme.c46
-rw-r--r--source/popt/findme.h10
-rw-r--r--source/popt/popt.c782
-rw-r--r--source/popt/popt.h130
-rw-r--r--source/popt/poptconfig.c142
-rw-r--r--source/popt/popthelp.c301
-rw-r--r--source/popt/poptint.h71
-rw-r--r--source/popt/poptparse.c102
-rw-r--r--source/popt/system.h53
-rw-r--r--source/printing/.cvsignore0
-rw-r--r--source/printing/load.c73
-rw-r--r--source/printing/lpq_parse.c1016
-rw-r--r--source/printing/notify.c555
-rw-r--r--source/printing/nt_printing.c4990
-rw-r--r--source/printing/pcap.c413
-rw-r--r--source/printing/print_cups.c1303
-rw-r--r--source/printing/print_generic.c247
-rw-r--r--source/printing/print_svid.c144
-rw-r--r--source/printing/printfsp.c120
-rw-r--r--source/printing/printing.c2420
-rw-r--r--source/printing/printing_db.c206
-rw-r--r--source/profile/profile.c166
-rw-r--r--source/python/.cvsignore1
-rw-r--r--source/python/README28
-rwxr-xr-xsource/python/examples/spoolss/changeid.py34
-rwxr-xr-xsource/python/examples/spoolss/enumprinters.py36
-rwxr-xr-xsource/python/examples/spoolss/psec.py88
-rw-r--r--source/python/examples/tdbpack/.cvsignore2
-rw-r--r--source/python/examples/tdbpack/oldtdbutil.py144
-rwxr-xr-xsource/python/examples/tdbpack/tdbtimetrial.py12
-rwxr-xr-xsource/python/examples/tdbpack/test_tdbpack.py253
-rwxr-xr-xsource/python/gprinterdata39
-rwxr-xr-xsource/python/gtdbtool39
-rwxr-xr-xsource/python/gtkdictbrowser.py272
-rw-r--r--source/python/py_common.c259
-rw-r--r--source/python/py_common.h67
-rw-r--r--source/python/py_conv.c223
-rw-r--r--source/python/py_conv.h44
-rw-r--r--source/python/py_lsa.c484
-rw-r--r--source/python/py_lsa.h41
-rw-r--r--source/python/py_ntsec.c288
-rw-r--r--source/python/py_samr.c676
-rw-r--r--source/python/py_samr.h86
-rw-r--r--source/python/py_samr_conv.c131
-rw-r--r--source/python/py_smb.c442
-rw-r--r--source/python/py_smb.h39
-rw-r--r--source/python/py_spoolss.c485
-rw-r--r--source/python/py_spoolss.h160
-rw-r--r--source/python/py_spoolss_common.c35
-rw-r--r--source/python/py_spoolss_drivers.c422
-rw-r--r--source/python/py_spoolss_drivers_conv.c179
-rw-r--r--source/python/py_spoolss_forms.c266
-rw-r--r--source/python/py_spoolss_forms_conv.c91
-rw-r--r--source/python/py_spoolss_jobs.c377
-rw-r--r--source/python/py_spoolss_jobs_conv.c102
-rw-r--r--source/python/py_spoolss_ports.c137
-rw-r--r--source/python/py_spoolss_ports_conv.c58
-rw-r--r--source/python/py_spoolss_printerdata.c450
-rw-r--r--source/python/py_spoolss_printers.c475
-rw-r--r--source/python/py_spoolss_printers_conv.c354
-rw-r--r--source/python/py_srvsvc.c215
-rw-r--r--source/python/py_srvsvc.h30
-rw-r--r--source/python/py_srvsvc_conv.c41
-rw-r--r--source/python/py_tdb.c702
-rw-r--r--source/python/py_tdb.h26
-rw-r--r--source/python/py_tdbpack.c721
-rw-r--r--source/python/py_winbind.c800
-rw-r--r--source/python/py_winbind.h30
-rw-r--r--source/python/py_winbind_conv.c41
-rw-r--r--source/python/py_winreg.c82
-rw-r--r--source/python/py_winreg.h26
-rw-r--r--source/python/samba/.cvsignore1
-rw-r--r--source/python/samba/__init__.py7
-rw-r--r--source/python/samba/printerdata.py65
-rwxr-xr-xsource/python/setup.py200
-rw-r--r--source/registry/.cvsignore2
-rw-r--r--source/registry/reg_cachehook.c112
-rw-r--r--source/registry/reg_db.c311
-rw-r--r--source/registry/reg_frontend.c260
-rw-r--r--source/registry/reg_objects.c374
-rw-r--r--source/registry/reg_printing.c826
-rw-r--r--source/rpc_client/.cvsignore3
-rw-r--r--source/rpc_client/cli_dfs.c247
-rw-r--r--source/rpc_client/cli_ds.c157
-rw-r--r--source/rpc_client/cli_echo.c187
-rw-r--r--source/rpc_client/cli_epmapper.c61
-rw-r--r--source/rpc_client/cli_lsarpc.c1466
-rw-r--r--source/rpc_client/cli_netlogon.c802
-rw-r--r--source/rpc_client/cli_pipe.c1651
-rw-r--r--source/rpc_client/cli_reg.c103
-rw-r--r--source/rpc_client/cli_samr.c2102
-rw-r--r--source/rpc_client/cli_shutdown.c104
-rw-r--r--source/rpc_client/cli_spoolss.c2466
-rw-r--r--source/rpc_client/cli_spoolss_notify.c272
-rw-r--r--source/rpc_client/cli_srvsvc.c442
-rw-r--r--source/rpc_client/cli_wkssvc.c93
-rw-r--r--source/rpc_parse/.cvsignore2
-rw-r--r--source/rpc_parse/parse_dfs.c546
-rw-r--r--source/rpc_parse/parse_ds.c302
-rw-r--r--source/rpc_parse/parse_echo.c166
-rw-r--r--source/rpc_parse/parse_epmapper.c482
-rw-r--r--source/rpc_parse/parse_lsa.c2474
-rw-r--r--source/rpc_parse/parse_misc.c1765
-rw-r--r--source/rpc_parse/parse_net.c3048
-rw-r--r--source/rpc_parse/parse_prs.c1624
-rw-r--r--source/rpc_parse/parse_reg.c1856
-rw-r--r--source/rpc_parse/parse_rpc.c1219
-rw-r--r--source/rpc_parse/parse_samr.c7387
-rw-r--r--source/rpc_parse/parse_sec.c366
-rw-r--r--source/rpc_parse/parse_shutdown.c163
-rw-r--r--source/rpc_parse/parse_spoolss.c7741
-rw-r--r--source/rpc_parse/parse_srv.c3575
-rw-r--r--source/rpc_parse/parse_wks.c178
-rw-r--r--source/rpc_server/.cvsignore0
-rw-r--r--source/rpc_server/srv_dfs.c179
-rw-r--r--source/rpc_server/srv_dfs_nt.c377
-rw-r--r--source/rpc_server/srv_echo.c150
-rw-r--r--source/rpc_server/srv_echo_nt.c78
-rw-r--r--source/rpc_server/srv_epmapper.c88
-rw-r--r--source/rpc_server/srv_epmapper_nt.c70
-rw-r--r--source/rpc_server/srv_lsa.c734
-rw-r--r--source/rpc_server/srv_lsa_ds.c93
-rw-r--r--source/rpc_server/srv_lsa_ds_nt.c127
-rw-r--r--source/rpc_server/srv_lsa_hnd.c265
-rw-r--r--source/rpc_server/srv_lsa_nt.c1342
-rw-r--r--source/rpc_server/srv_netlog.c385
-rw-r--r--source/rpc_server/srv_netlog_nt.c795
-rw-r--r--source/rpc_server/srv_pipe.c1625
-rw-r--r--source/rpc_server/srv_pipe_hnd.c1169
-rw-r--r--source/rpc_server/srv_reg.c402
-rw-r--r--source/rpc_server/srv_reg_nt.c664
-rw-r--r--source/rpc_server/srv_samr.c1508
-rw-r--r--source/rpc_server/srv_samr_nt.c4476
-rw-r--r--source/rpc_server/srv_samr_util.c554
-rwxr-xr-xsource/rpc_server/srv_spoolss.c1652
-rw-r--r--source/rpc_server/srv_spoolss_nt.c9188
-rw-r--r--source/rpc_server/srv_srvsvc.c561
-rw-r--r--source/rpc_server/srv_srvsvc_nt.c2195
-rw-r--r--source/rpc_server/srv_util.c580
-rw-r--r--source/rpc_server/srv_wkssvc.c78
-rw-r--r--source/rpc_server/srv_wkssvc_nt.c79
-rw-r--r--source/rpcclient/cmd_dfs.c237
-rw-r--r--source/rpcclient/cmd_ds.c79
-rw-r--r--source/rpcclient/cmd_echo.c159
-rw-r--r--source/rpcclient/cmd_epmapper.c76
-rw-r--r--source/rpcclient/cmd_lsarpc.c762
-rw-r--r--source/rpcclient/cmd_netlogon.c345
-rw-r--r--source/rpcclient/cmd_reg.c1007
-rw-r--r--source/rpcclient/cmd_samr.c1562
-rw-r--r--source/rpcclient/cmd_shutdown.c112
-rw-r--r--source/rpcclient/cmd_spoolss.c2338
-rw-r--r--source/rpcclient/cmd_srvsvc.c361
-rw-r--r--source/rpcclient/cmd_wkssvc.c84
-rw-r--r--source/rpcclient/display_sec.c144
-rw-r--r--source/rpcclient/rpcclient.c803
-rw-r--r--source/rpcclient/rpcclient.h42
-rw-r--r--source/sam/account.c305
-rw-r--r--source/sam/group.c193
-rw-r--r--source/sam/gums.c173
-rw-r--r--source/sam/gums_api.c1426
-rw-r--r--source/sam/gums_helper.c383
-rw-r--r--source/sam/gums_tdbsam2.c1220
-rw-r--r--source/sam/idmap.c317
-rw-r--r--source/sam/idmap_ldap.c790
-rw-r--r--source/sam/idmap_tdb.c676
-rw-r--r--source/sam/idmap_util.c295
-rw-r--r--source/sam/interface.c1338
-rw-r--r--source/script/.cvsignore2
-rwxr-xr-xsource/script/build_env.sh41
-rwxr-xr-xsource/script/creategroup27
-rwxr-xr-xsource/script/extract_allparms.sh2
-rwxr-xr-xsource/script/findsmb.in161
-rwxr-xr-xsource/script/findstatic.pl70
-rw-r--r--source/script/gap.awk39
-rw-r--r--source/script/gaptab.awk48
-rw-r--r--source/script/gen-8bit-gap.awk18
-rwxr-xr-xsource/script/gen-8bit-gap.sh.in49
-rwxr-xr-xsource/script/genstruct.pl299
-rwxr-xr-xsource/script/installbin.sh45
-rwxr-xr-xsource/script/installdat.sh23
-rwxr-xr-xsource/script/installdirs.sh19
-rwxr-xr-xsource/script/installman.sh71
-rwxr-xr-xsource/script/installmodules.sh27
-rw-r--r--source/script/installmsg.sh23
-rwxr-xr-xsource/script/installscripts.sh47
-rwxr-xr-xsource/script/installswat.sh153
-rwxr-xr-xsource/script/linkmodules.sh12
-rw-r--r--source/script/makeunicodecasemap.awk59
-rw-r--r--source/script/mkbuildoptions.awk262
-rwxr-xr-xsource/script/mkinstalldirs38
-rwxr-xr-xsource/script/mknissmbpasswd.sh31
-rwxr-xr-xsource/script/mknissmbpwdtbl.sh42
-rw-r--r--source/script/mkproto.awk155
-rwxr-xr-xsource/script/mkproto.sh43
-rwxr-xr-xsource/script/mksmbpasswd.sh6
-rwxr-xr-xsource/script/mkversion.sh87
-rwxr-xr-xsource/script/revert.sh18
-rwxr-xr-xsource/script/scancvslog.pl112
-rw-r--r--source/script/smbtar165
-rwxr-xr-xsource/script/uninstallbin.sh42
-rwxr-xr-xsource/script/uninstallman.sh37
-rwxr-xr-xsource/script/uninstallmodules.sh37
-rwxr-xr-xsource/script/uninstallscripts.sh36
-rwxr-xr-xsource/script/updatesmbpasswd.sh14
-rw-r--r--source/smbadduser.in79
-rw-r--r--source/smbd/.cvsignore3
-rw-r--r--source/smbd/blocking.c726
-rw-r--r--source/smbd/change_trust_pw.c99
-rw-r--r--source/smbd/chgpasswd.c1054
-rw-r--r--source/smbd/close.c311
-rw-r--r--source/smbd/conn.c305
-rw-r--r--source/smbd/connection.c243
-rw-r--r--source/smbd/dfree.c164
-rw-r--r--source/smbd/dir.c1096
-rw-r--r--source/smbd/dosmode.c483
-rw-r--r--source/smbd/error.c137
-rw-r--r--source/smbd/fake_file.c166
-rw-r--r--source/smbd/fileio.c773
-rw-r--r--source/smbd/filename.c497
-rw-r--r--source/smbd/files.c449
-rw-r--r--source/smbd/ipc.c599
-rw-r--r--source/smbd/lanman.c3619
-rw-r--r--source/smbd/mangle.c124
-rw-r--r--source/smbd/mangle_hash.c783
-rw-r--r--source/smbd/mangle_hash2.c709
-rw-r--r--source/smbd/mangle_map.c212
-rw-r--r--source/smbd/message.c237
-rw-r--r--source/smbd/negprot.c550
-rw-r--r--source/smbd/noquotas.c38
-rw-r--r--source/smbd/notify.c225
-rw-r--r--source/smbd/notify_hash.c225
-rw-r--r--source/smbd/notify_kernel.c245
-rw-r--r--source/smbd/ntquotas.c262
-rw-r--r--source/smbd/nttrans.c2780
-rw-r--r--source/smbd/open.c1462
-rw-r--r--source/smbd/oplock.c1260
-rw-r--r--source/smbd/oplock_irix.c285
-rw-r--r--source/smbd/oplock_linux.c309
-rw-r--r--source/smbd/password.c551
-rw-r--r--source/smbd/pipes.c264
-rw-r--r--source/smbd/posix_acls.c3382
-rw-r--r--source/smbd/process.c1367
-rw-r--r--source/smbd/quotas.c1279
-rw-r--r--source/smbd/reply.c4974
-rw-r--r--source/smbd/sec_ctx.c449
-rw-r--r--source/smbd/server.c909
-rw-r--r--source/smbd/service.c838
-rw-r--r--source/smbd/session.c250
-rw-r--r--source/smbd/sesssetup.c938
-rw-r--r--source/smbd/srvstr.c44
-rw-r--r--source/smbd/statcache.c339
-rw-r--r--source/smbd/tdbutil.c85
-rw-r--r--source/smbd/trans2.c4165
-rw-r--r--source/smbd/uid.c430
-rw-r--r--source/smbd/utmp.c595
-rw-r--r--source/smbd/vfs-wrap.c1031
-rw-r--r--source/smbd/vfs.c951
-rw-r--r--source/smbwrapper/.cvsignore8
-rw-r--r--source/smbwrapper/PORTING77
-rw-r--r--source/smbwrapper/README94
-rw-r--r--source/smbwrapper/realcalls.c48
-rw-r--r--source/smbwrapper/realcalls.h263
-rw-r--r--source/smbwrapper/shared.c203
-rw-r--r--source/smbwrapper/smbsh.c127
-rw-r--r--source/smbwrapper/smbsh.in54
-rw-r--r--source/smbwrapper/smbw.c1562
-rw-r--r--source/smbwrapper/smbw.h71
-rw-r--r--source/smbwrapper/smbw_cache.c207
-rw-r--r--source/smbwrapper/smbw_dir.c689
-rw-r--r--source/smbwrapper/smbw_stat.c254
-rw-r--r--source/smbwrapper/wrapped.c705
-rw-r--r--source/stf/.cvsignore2
-rw-r--r--source/stf/README.stf3
-rw-r--r--source/stf/comfychair.py445
-rwxr-xr-xsource/stf/example.py38
-rwxr-xr-xsource/stf/info3cache.py54
-rw-r--r--source/stf/notes.txt175
-rwxr-xr-xsource/stf/osver.py55
-rwxr-xr-xsource/stf/pythoncheck.py48
-rw-r--r--source/stf/sambalib.py41
-rwxr-xr-xsource/stf/smbcontrol.py238
-rwxr-xr-xsource/stf/spoolss.py288
-rw-r--r--source/stf/standardcheck.py34
-rwxr-xr-xsource/stf/stf.py101
-rwxr-xr-xsource/stf/strings.py151
-rwxr-xr-xsource/stf/test.py33
-rw-r--r--source/stf/unicodenames.py33
-rw-r--r--source/tdb/.cvsignore11
-rw-r--r--source/tdb/Makefile29
-rw-r--r--source/tdb/README167
-rw-r--r--source/tdb/spinlock.c481
-rw-r--r--source/tdb/spinlock.h59
-rw-r--r--source/tdb/tdb.c2090
-rw-r--r--source/tdb/tdb.h151
-rw-r--r--source/tdb/tdb.magic10
-rw-r--r--source/tdb/tdbback.c211
-rw-r--r--source/tdb/tdbback.h23
-rw-r--r--source/tdb/tdbbackup.c149
-rw-r--r--source/tdb/tdbdump.c88
-rw-r--r--source/tdb/tdbtest.c263
-rw-r--r--source/tdb/tdbtool.c547
-rw-r--r--source/tdb/tdbtorture.c227
-rw-r--r--source/tdb/tdbutil.c829
-rw-r--r--source/tdb/tdbutil.h38
-rw-r--r--source/tests/.cvsignore1
-rw-r--r--source/tests/README10
-rw-r--r--source/tests/crack.c12
-rw-r--r--source/tests/crypttest.c852
-rw-r--r--source/tests/fcntl_lock.c121
-rw-r--r--source/tests/fcntl_lock64.c96
-rw-r--r--source/tests/ftruncate.c27
-rw-r--r--source/tests/getgroups.c66
-rw-r--r--source/tests/shared_mmap.c68
-rw-r--r--source/tests/shlib.c6
-rw-r--r--source/tests/summary.c30
-rw-r--r--source/tests/sysquotas.c94
-rw-r--r--source/tests/trivial.c4
-rw-r--r--source/tests/unixsock.c93
-rw-r--r--source/torture/cmd_vfs.c1060
-rw-r--r--source/torture/denytest.c1577
-rw-r--r--source/torture/locktest.c678
-rw-r--r--source/torture/locktest2.c560
-rw-r--r--source/torture/mangle_test.c213
-rw-r--r--source/torture/masktest.c538
-rw-r--r--source/torture/msgtest.c91
-rw-r--r--source/torture/nbio.c318
-rw-r--r--source/torture/nsstest.c451
-rw-r--r--source/torture/rpctorture.c541
-rw-r--r--source/torture/samtest.h38
-rw-r--r--source/torture/scanner.c430
-rw-r--r--source/torture/smbiconv.c243
-rw-r--r--source/torture/t_doschar.c42
-rw-r--r--source/torture/t_push_ucs2.c54
-rw-r--r--source/torture/t_strcmp.c32
-rw-r--r--source/torture/t_stringoverflow.c23
-rw-r--r--source/torture/t_strstr.c35
-rw-r--r--source/torture/torture.c4994
-rw-r--r--source/torture/utable.c193
-rw-r--r--source/torture/vfstest.c571
-rw-r--r--source/torture/vfstest.h45
-rw-r--r--source/ubiqx/.cvsignore3
-rw-r--r--source/ubiqx/COPYING.LIB481
-rw-r--r--source/ubiqx/README.UBI18
-rw-r--r--source/ubiqx/debugparse.c308
-rw-r--r--source/ubiqx/debugparse.h127
-rw-r--r--source/ubiqx/sys_include.h52
-rw-r--r--source/ubiqx/ubi_BinTree.c1132
-rw-r--r--source/ubiqx/ubi_BinTree.h864
-rw-r--r--source/ubiqx/ubi_Cache.c505
-rw-r--r--source/ubiqx/ubi_Cache.h412
-rw-r--r--source/ubiqx/ubi_SplayTree.c512
-rw-r--r--source/ubiqx/ubi_SplayTree.h377
-rw-r--r--source/ubiqx/ubi_dLinkList.c171
-rw-r--r--source/ubiqx/ubi_dLinkList.h242
-rw-r--r--source/ubiqx/ubi_sLinkList.c187
-rw-r--r--source/ubiqx/ubi_sLinkList.h254
-rw-r--r--source/utils/.cvsignore1
-rw-r--r--source/utils/debug2html.c253
-rw-r--r--source/utils/editreg.c4145
-rw-r--r--source/utils/log2pcaphex.c294
-rw-r--r--source/utils/net.c796
-rw-r--r--source/utils/net.h69
-rw-r--r--source/utils/net_ads.c1328
-rw-r--r--source/utils/net_ads_cldap.c364
-rw-r--r--source/utils/net_cache.c348
-rw-r--r--source/utils/net_groupmap.c767
-rw-r--r--source/utils/net_help.c220
-rw-r--r--source/utils/net_idmap.c264
-rw-r--r--source/utils/net_lookup.c258
-rw-r--r--source/utils/net_privileges.c362
-rw-r--r--source/utils/net_rap.c1054
-rw-r--r--source/utils/net_rpc.c3396
-rw-r--r--source/utils/net_rpc_join.c389
-rw-r--r--source/utils/net_rpc_samsync.c1080
-rw-r--r--source/utils/net_status.c257
-rw-r--r--source/utils/net_time.c180
-rw-r--r--source/utils/nmblookup.c292
-rw-r--r--source/utils/ntlm_auth.c2182
-rw-r--r--source/utils/pdbedit.c1179
-rw-r--r--source/utils/profiles.c742
-rw-r--r--source/utils/rpccheck.c62
-rw-r--r--source/utils/smbcacls.c877
-rw-r--r--source/utils/smbcontrol.c747
-rw-r--r--source/utils/smbcquotas.c546
-rw-r--r--source/utils/smbfilter.c245
-rw-r--r--source/utils/smbget.c589
-rw-r--r--source/utils/smbpasswd.c614
-rw-r--r--source/utils/smbtree.c237
-rw-r--r--source/utils/smbw_sample.c94
-rw-r--r--source/utils/status.c693
-rw-r--r--source/utils/testparm.c367
-rw-r--r--source/utils/testprns.c61
-rw-r--r--source/web/.cvsignore1
-rw-r--r--source/web/cgi.c626
-rw-r--r--source/web/diagnose.c79
-rw-r--r--source/web/neg_lang.c117
-rw-r--r--source/web/startstop.c131
-rw-r--r--source/web/statuspage.c441
-rw-r--r--source/web/swat.c1389
-rw-r--r--source/wrepld/parser.c759
-rw-r--r--source/wrepld/partners.c200
-rw-r--r--source/wrepld/process.c983
-rw-r--r--source/wrepld/server.c666
-rw-r--r--source/wrepld/socket.c69
-rw-r--r--source/wrepld/wins_repl.h161
884 files changed, 421757 insertions, 0 deletions
diff --git a/source/.cvsignore b/source/.cvsignore
new file mode 100644
index 00000000000..31dd2c3a7a9
--- /dev/null
+++ b/source/.cvsignore
@@ -0,0 +1,31 @@
+*.po
+*.po32
+.headers.stamp
+.inslog2
+.ix*
+.proto.check
+.proto.stamp
+autom4te.cache
+autom4te-2.53.cache
+ID
+Makefile
+bin
+build
+config.cache
+config.log
+config.status
+configure.tridge
+cvs.log
+diffs
+dmalloc.log
+dmallog.log
+dox
+libtool
+so_locations
+tca.log
+testdir
+testtmp
+trace.out
+typescript*
+configure
+smbadduser
diff --git a/source/.dmallocrc b/source/.dmallocrc
new file mode 100644
index 00000000000..5e5c45e1244
--- /dev/null
+++ b/source/.dmallocrc
@@ -0,0 +1,2 @@
+samba allow-free-null, log-stats, log-non-free, log-trans, \
+ check-fence, check-heap, check-lists, error-abort \ No newline at end of file
diff --git a/source/Doxyfile b/source/Doxyfile
new file mode 100644
index 00000000000..c1040781d84
--- /dev/null
+++ b/source/Doxyfile
@@ -0,0 +1,176 @@
+# Doxyfile 0.1
+
+#---------------------------------------------------------------------------
+# General configuration options
+#---------------------------------------------------------------------------
+PROJECT_NAME = Samba
+PROJECT_NUMBER = HEAD
+
+# NOTE: By default, Doxygen writes into the dox/ subdirectory of the
+# invocation directory. If you want to put it somewhere else, for
+# example, to write straight into a webserver directory, then override
+# this variable in a configuration concatenated to this one: Doxygen
+# doesn't mind variables being redefined.
+
+OUTPUT_DIRECTORY = dox
+
+OUTPUT_LANGUAGE = English
+EXTRACT_ALL = YES
+EXTRACT_PRIVATE = YES
+EXTRACT_STATIC = YES
+HIDE_UNDOC_MEMBERS = NO
+HIDE_UNDOC_CLASSES = NO
+BRIEF_MEMBER_DESC = YES
+REPEAT_BRIEF = YES
+ALWAYS_DETAILED_SEC = NO
+FULL_PATH_NAMES = YES
+STRIP_FROM_PATH = $(PWD)/
+INTERNAL_DOCS = YES
+CLASS_DIAGRAMS = YES
+SOURCE_BROWSER = YES
+INLINE_SOURCES = YES
+STRIP_CODE_COMMENTS = NO
+CASE_SENSE_NAMES = YES
+SHORT_NAMES = NO
+HIDE_SCOPE_NAMES = YES
+VERBATIM_HEADERS = YES
+SHOW_INCLUDE_FILES = YES
+JAVADOC_AUTOBRIEF = YES
+INHERIT_DOCS = YES
+INLINE_INFO = YES
+SORT_MEMBER_DOCS = NO
+DISTRIBUTE_GROUP_DOC = NO
+TAB_SIZE = 8
+GENERATE_TODOLIST = YES
+GENERATE_TESTLIST = YES
+GENERATE_BUGLIST = YES
+ALIASES =
+ENABLED_SECTIONS =
+MAX_INITIALIZER_LINES = 30
+OPTIMIZE_OUTPUT_FOR_C = YES
+SHOW_USED_FILES = YES
+REFERENCED_BY_RELATION = YES
+#---------------------------------------------------------------------------
+# configuration options related to warning and progress messages
+#---------------------------------------------------------------------------
+QUIET = YES
+WARNINGS = NO
+WARN_IF_UNDOCUMENTED = NO
+WARN_FORMAT = "$file:$line: $text"
+WARN_LOGFILE =
+#---------------------------------------------------------------------------
+# configuration options related to the input files
+#---------------------------------------------------------------------------
+INPUT = .
+FILE_PATTERNS = *.c \
+ *.h \
+ *.idl
+RECURSIVE = YES
+EXCLUDE = include/includes.h \
+ include/proto.h
+EXCLUDE_PATTERNS =
+EXAMPLE_PATH =
+EXAMPLE_PATTERNS =
+IMAGE_PATH =
+INPUT_FILTER =
+FILTER_SOURCE_FILES = NO
+#---------------------------------------------------------------------------
+# configuration options related to the alphabetical class index
+#---------------------------------------------------------------------------
+ALPHABETICAL_INDEX = YES
+COLS_IN_ALPHA_INDEX = 1
+IGNORE_PREFIX =
+#---------------------------------------------------------------------------
+# configuration options related to the HTML output
+#---------------------------------------------------------------------------
+GENERATE_HTML = YES
+HTML_OUTPUT = .
+HTML_HEADER =
+HTML_FOOTER =
+HTML_STYLESHEET =
+HTML_ALIGN_MEMBERS = YES
+GENERATE_HTMLHELP = NO
+GENERATE_CHI = NO
+BINARY_TOC = NO
+TOC_EXPAND = NO
+DISABLE_INDEX = NO
+ENUM_VALUES_PER_LINE = 3
+GENERATE_TREEVIEW = NO
+TREEVIEW_WIDTH = 250
+#---------------------------------------------------------------------------
+# configuration options related to the LaTeX output
+#---------------------------------------------------------------------------
+GENERATE_LATEX = NO
+LATEX_OUTPUT = latex
+COMPACT_LATEX = NO
+PAPER_TYPE = a4wide
+EXTRA_PACKAGES =
+LATEX_HEADER =
+PDF_HYPERLINKS = YES
+USE_PDFLATEX = YES
+LATEX_BATCHMODE = YES
+#---------------------------------------------------------------------------
+# configuration options related to the RTF output
+#---------------------------------------------------------------------------
+GENERATE_RTF = NO
+RTF_OUTPUT = rtf
+COMPACT_RTF = NO
+RTF_HYPERLINKS = NO
+RTF_STYLESHEET_FILE =
+RTF_EXTENSIONS_FILE =
+#---------------------------------------------------------------------------
+# configuration options related to the man page output
+#---------------------------------------------------------------------------
+GENERATE_MAN = NO
+MAN_OUTPUT = man
+MAN_EXTENSION = .3
+MAN_LINKS = NO
+#---------------------------------------------------------------------------
+# configuration options related to the XML output
+#---------------------------------------------------------------------------
+GENERATE_XML = NO
+#---------------------------------------------------------------------------
+# configuration options related to the preprocessor
+#---------------------------------------------------------------------------
+ENABLE_PREPROCESSING = NO
+MACRO_EXPANSION = NO
+EXPAND_ONLY_PREDEF = NO
+SEARCH_INCLUDES = YES
+INCLUDE_PATH =
+INCLUDE_FILE_PATTERNS =
+PREDEFINED =
+EXPAND_AS_DEFINED =
+SKIP_FUNCTION_MACROS = YES
+#---------------------------------------------------------------------------
+# configuration::additions related to external references
+#---------------------------------------------------------------------------
+TAGFILES =
+GENERATE_TAGFILE =
+ALLEXTERNALS = NO
+PERL_PATH = /usr/bin/perl
+#---------------------------------------------------------------------------
+# configuration options related to the dot tool
+#---------------------------------------------------------------------------
+HAVE_DOT = NO
+CLASS_GRAPH = YES
+COLLABORATION_GRAPH = YES
+TEMPLATE_RELATIONS = YES
+INCLUDE_GRAPH = YES
+INCLUDED_BY_GRAPH = YES
+GRAPHICAL_HIERARCHY = YES
+DOT_PATH =
+DOTFILE_DIRS =
+MAX_DOT_GRAPH_WIDTH = 1024
+MAX_DOT_GRAPH_HEIGHT = 1024
+GENERATE_LEGEND = YES
+DOT_CLEANUP = YES
+#---------------------------------------------------------------------------
+# configuration::additions related to the search engine
+#---------------------------------------------------------------------------
+SEARCHENGINE = NO
+CGI_NAME = search.cgi
+CGI_URL =
+DOC_URL =
+DOC_ABSPATH =
+BIN_ABSPATH = /usr/local/bin/
+EXT_DOC_PATHS =
diff --git a/source/Makefile.in b/source/Makefile.in
new file mode 100644
index 00000000000..145905332ad
--- /dev/null
+++ b/source/Makefile.in
@@ -0,0 +1,1526 @@
+#########################################################################
+# Makefile.in for Samba - rewritten for autoconf support
+# Copyright Andrew Tridgell 1992-1998
+# Copyright (C) 2001 by Martin Pool <mbp@samba.org>
+# Copyright Andrew Bartlett 2002
+# Copyright (C) 2003 Jim McDonough <jmcd@us.ibm.com>
+# Copyright (C) 2002-2003 Jelmer Vernooij <jelmer@samba.org>
+###########################################################################
+
+prefix=@prefix@
+exec_prefix=@exec_prefix@
+
+LIBS=@LIBS@
+CC=@CC@
+SHLD=@SHLD@
+CFLAGS=@CFLAGS@
+CPPFLAGS=@CPPFLAGS@
+EXEEXT=@EXEEXT@
+LDFLAGS=@LDFLAGS@
+AR=@AR@
+LDSHFLAGS=@LDSHFLAGS@ @LDFLAGS@
+WINBIND_NSS_LDSHFLAGS=@WINBIND_NSS_LDSHFLAGS@ @LDFLAGS@
+AWK=@AWK@
+DYNEXP=@DYNEXP@
+PYTHON=@PYTHON@
+PERL=@PERL@
+
+TERMLDFLAGS=@TERMLDFLAGS@
+TERMLIBS=@TERMLIBS@
+PRINT_LIBS=@PRINT_LIBS@
+AUTH_LIBS=@AUTH_LIBS@
+ACL_LIBS=@ACL_LIBS@
+PASSDB_LIBS=@PASSDB_LIBS@
+IDMAP_LIBS=@IDMAP_LIBS@
+KRB5LIBS=@KRB5_LIBS@
+LDAP_LIBS=@LDAP_LIBS@
+
+LINK=$(CC) $(FLAGS) $(LDFLAGS)
+
+INSTALLCMD=@INSTALL@
+INSTALLCLIENTCMD_SH=@INSTALLCLIENTCMD_SH@
+INSTALLCLIENTCMD_A=@INSTALLCLIENTCMD_A@
+
+VPATH=@srcdir@
+srcdir=@abs_srcdir@
+builddir=@abs_builddir@
+SHELL=/bin/sh
+DESTDIR=/
+
+# XXX: Perhaps this should be @SHELL@ instead -- apparently autoconf
+# will search for a POSIX-compliant shell, and that might not be
+# /bin/sh on some platforms. I guess it's not a big problem -- mbp
+
+# See the autoconf manual "Installation Directory Variables" for a
+# discussion of the subtle use of these variables.
+
+BASEDIR= @prefix@
+BINDIR = @bindir@
+# sbindir is mapped to bindir when compiling SAMBA in 2.0.x compatibility mode.
+SBINDIR = @sbindir@
+LIBDIR = @libdir@
+VFSLIBDIR = $(LIBDIR)/vfs
+PDBLIBDIR = $(LIBDIR)/pdb
+RPCLIBDIR = $(LIBDIR)/rpc
+IDMAPLIBDIR = $(LIBDIR)/idmap
+CHARSETLIBDIR = $(LIBDIR)/charset
+AUTHLIBDIR = $(LIBDIR)/auth
+CONFIGLIBDIR = $(LIBDIR)/config
+CONFIGDIR = @configdir@
+VARDIR = @localstatedir@
+MANDIR = @mandir@
+DATADIR = @datadir@
+
+# The permissions to give the executables
+INSTALLPERMS = 0755
+
+# set these to where to find various files
+# These can be overridden by command line switches (see smbd(8))
+# or in smb.conf (see smb.conf(5))
+LOGFILEBASE = @logfilebase@
+CONFIGFILE = $(CONFIGDIR)/smb.conf
+LMHOSTSFILE = $(CONFIGDIR)/lmhosts
+
+# This is where smbpasswd et al go
+PRIVATEDIR = @privatedir@
+
+SMB_PASSWD_FILE = $(PRIVATEDIR)/smbpasswd
+PRIVATE_DIR = $(PRIVATEDIR)
+
+# This is where SWAT images and help files go
+SWATDIR = @swatdir@
+
+# the directory where lock files go
+LOCKDIR = @lockdir@
+
+# the directory where pid files go
+PIDDIR = @piddir@
+# man pages language(s)
+man_langs = "@manlangs@"
+
+LIBSMBCLIENT=bin/libsmbclient.a @LIBSMBCLIENT_SHARED@
+LIBSMBCLIENT_MAJOR=0
+LIBSMBCLIENT_MINOR=1
+
+
+FLAGS1 = $(CFLAGS) @FLAGS1@ -Iinclude -I$(srcdir)/include -I$(srcdir)/ubiqx -I$(srcdir)/smbwrapper -I. $(CPPFLAGS) -I$(srcdir)
+FLAGS2 =
+FLAGS3 =
+FLAGS4 =
+FLAGS5 = $(FLAGS1) $(FLAGS2) $(FLAGS3) $(FLAGS4)
+FLAGS = $(ISA) $(FLAGS5)
+
+PASSWD_FLAGS = -DSMB_PASSWD_FILE=\"$(SMB_PASSWD_FILE)\" -DPRIVATE_DIR=\"$(PRIVATE_DIR)\"
+PATH_FLAGS1 = -DCONFIGFILE=\"$(CONFIGFILE)\" -DSBINDIR=\"$(SBINDIR)\"
+PATH_FLAGS2 = $(PATH_FLAGS1) -DBINDIR=\"$(BINDIR)\" -DDRIVERFILE=\"$(DRIVERFILE)\"
+PATH_FLAGS3 = $(PATH_FLAGS2) -DLMHOSTSFILE=\"$(LMHOSTSFILE)\"
+PATH_FLAGS4 = $(PATH_FLAGS3) -DSWATDIR=\"$(SWATDIR)\" -DLOCKDIR=\"$(LOCKDIR)\" -DPIDDIR=\"$(PIDDIR)\"
+PATH_FLAGS5 = $(PATH_FLAGS4) -DLIBDIR=\"$(LIBDIR)\" \
+ -DLOGFILEBASE=\"$(LOGFILEBASE)\" -DSHLIBEXT=\"@SHLIBEXT@\"
+PATH_FLAGS6 = $(PATH_FLAGS5) -DCONFIGDIR=\"$(CONFIGDIR)\"
+PATH_FLAGS = $(PATH_FLAGS6) $(PASSWD_FLAGS)
+
+# Note that all executable programs now provide for an optional executable suffix.
+
+SBIN_PROGS = bin/smbd@EXEEXT@ bin/nmbd@EXEEXT@ bin/swat@EXEEXT@ @EXTRA_SBIN_PROGS@
+
+BIN_PROGS1 = bin/smbclient@EXEEXT@ bin/net@EXEEXT@ bin/smbspool@EXEEXT@ \
+ bin/testparm@EXEEXT@ bin/testprns@EXEEXT@ bin/smbstatus@EXEEXT@
+BIN_PROGS2 = bin/smbcontrol@EXEEXT@ bin/smbtree@EXEEXT@ bin/tdbbackup@EXEEXT@ \
+ bin/nmblookup@EXEEXT@ bin/pdbedit@EXEEXT@ bin/tdbdump@EXEEXT@
+BIN_PROGS3 = bin/smbpasswd@EXEEXT@ bin/rpcclient@EXEEXT@ bin/smbcacls@EXEEXT@ \
+ bin/profiles@EXEEXT@ bin/ntlm_auth@EXEEXT@ \
+ bin/smbcquotas@EXEEXT@
+
+# editreg removed from standard build until it is portable. It needs a major rewrite to
+# achieve this (tridge)
+# bin/editreg@EXEEXT@
+
+TORTURE_PROGS = bin/smbtorture@EXEEXT@ bin/msgtest@EXEEXT@ \
+ bin/masktest@EXEEXT@ bin/locktest@EXEEXT@ \
+ bin/locktest2@EXEEXT@ bin/nsstest@EXEEXT@ bin/vfstest@EXEEXT@
+
+BIN_PROGS = $(BIN_PROGS1) $(BIN_PROGS2) $(BIN_PROGS3) @EXTRA_BIN_PROGS@
+
+EVERYTHING_PROGS = bin/debug2html@EXEEXT@ bin/smbfilter@EXEEXT@ bin/talloctort@EXEEXT@ \
+ bin/log2pcap@EXEEXT@
+
+SHLIBS = @SHLIB_PROGS@ @LIBSMBCLIENT@
+
+SCRIPTS = $(srcdir)/script/smbtar $(builddir)/script/findsmb
+
+VFS_MODULES = @VFS_MODULES@
+PDB_MODULES = @PDB_MODULES@
+RPC_MODULES = @RPC_MODULES@
+IDMAP_MODULES = @IDMAP_MODULES@
+CHARSET_MODULES = @CHARSET_MODULES@
+AUTH_MODULES = @AUTH_MODULES@
+CONFIG_MODULES = @CONFIG_MODULES@
+MODULES = $(VFS_MODULES) $(PDB_MODULES) $(RPC_MODULES) $(IDMAP_MODULES) $(CHARSET_MODULES) $(AUTH_MODULES) $(CONFIG_MODULES)
+
+######################################################################
+# object file lists
+######################################################################
+
+TDBBASE_OBJ = tdb/tdb.o tdb/spinlock.o
+TDB_OBJ = $(TDBBASE_OBJ) tdb/tdbutil.o tdb/tdbback.o
+
+SMBLDAP_OBJ = @SMBLDAP@ @SMBLDAPUTIL@
+
+LIB_OBJ = lib/version.o lib/charcnv.o lib/debug.o lib/fault.o \
+ lib/getsmbpass.o lib/interface.o lib/md4.o \
+ lib/interfaces.o lib/pidfile.o lib/replace.o lib/replace1.o \
+ lib/signal.o lib/system.o lib/sendfile.o lib/time.o \
+ lib/ufc.o lib/genrand.o lib/username.o \
+ lib/util_getent.o lib/util_pw.o lib/access.o lib/smbrun.o \
+ lib/bitmap.o lib/crc32.o lib/snprintf.o lib/dprintf.o \
+ lib/xfile.o lib/wins_srv.o \
+ lib/util_str.o lib/clobber.o lib/util_sid.o lib/util_uuid.o \
+ lib/util_unistr.o lib/util_file.o lib/data_blob.o \
+ lib/util.o lib/util_sock.o lib/sock_exec.o lib/util_sec.o \
+ lib/talloc.o lib/hash.o lib/substitute.o lib/fsusage.o \
+ lib/ms_fnmatch.o lib/select.o lib/messages.o \
+ lib/tallocmsg.o lib/dmallocmsg.o libsmb/smb_signing.o \
+ lib/md5.o lib/hmacmd5.o lib/iconv.o \
+ nsswitch/wb_client.o nsswitch/wb_common.o \
+ lib/pam_errors.o intl/lang_tdb.o lib/account_pol.o \
+ lib/adt_tree.o lib/gencache.o $(TDB_OBJ) \
+ lib/module.o lib/ldap_escape.o @CHARSET_STATIC@ \
+ lib/privileges.o lib/secdesc.o lib/secace.o lib/secacl.o \
+ lib/genparser.o lib/genparser_samba.o
+
+LIB_SMBD_OBJ = lib/system_smbd.o lib/util_smbd.o
+
+LIB_NONSMBD_OBJ = $(LIB_OBJ) lib/dummysmbd.o
+
+READLINE_OBJ = lib/readline.o
+
+# Also depends on $(SECRETS_OBJ) $(LIBSAMBA_OBJ)
+# Be sure to include them into your application
+POPT_LIB_OBJ = lib/popt_common.o
+
+UBIQX_OBJ = ubiqx/ubi_BinTree.o ubiqx/ubi_Cache.o ubiqx/ubi_SplayTree.o \
+ ubiqx/ubi_dLinkList.o ubiqx/ubi_sLinkList.o
+
+PARAM_OBJ = dynconfig.o param/loadparm.o param/params.o param/modconf.o
+
+KRBCLIENT_OBJ = libads/kerberos.o libads/ads_status.o
+
+LIBADS_OBJ = libads/ldap.o libads/ldap_printer.o libads/sasl.o \
+ libads/krb5_setpw.o libads/ldap_user.o \
+ libads/ads_struct.o \
+ libads/disp_sec.o libads/ads_utils.o libads/ldap_utils.o \
+ libads/ads_ldap.o libads/authdata.o
+
+LIBADS_SERVER_OBJ = libads/util.o libads/kerberos_verify.o
+
+SECRETS_OBJ = passdb/secrets.o passdb/machine_sid.o
+
+LIBNMB_OBJ = libsmb/unexpected.o libsmb/namecache.o libsmb/nmblib.o \
+ libsmb/namequery.o libsmb/conncache.o
+
+LIBSAMBA_OBJ = libsmb/nterr.o libsmb/smbdes.o libsmb/smbencrypt.o libsmb/ntlm_check.o \
+ libsmb/ntlmssp.o libsmb/ntlmssp_parse.o libsmb/ntlmssp_sign.o
+
+LIBSMB_OBJ = libsmb/clientgen.o libsmb/cliconnect.o libsmb/clifile.o \
+ libsmb/clikrb5.o libsmb/clispnego.o libsmb/asn1.o \
+ libsmb/clirap.o libsmb/clierror.o libsmb/climessage.o \
+ libsmb/clireadwrite.o libsmb/clilist.o libsmb/cliprint.o \
+ libsmb/clitrans.o libsmb/clisecdesc.o libsmb/clidgram.o \
+ libsmb/clistr.o lib/util_seaccess.o \
+ libsmb/cliquota.o libsmb/clifsinfo.o \
+ libsmb/smberr.o libsmb/credentials.o libsmb/pwd_cache.o \
+ libsmb/clioplock.o libsmb/errormap.o libsmb/clirap2.o \
+ libsmb/doserr.o \
+ $(RPC_PARSE_OBJ1) $(LIBSAMBA_OBJ) $(LIBNMB_OBJ)
+
+LIBMSRPC_OBJ = rpc_client/cli_lsarpc.o rpc_client/cli_samr.o \
+ rpc_client/cli_netlogon.o rpc_client/cli_srvsvc.o \
+ rpc_client/cli_wkssvc.o rpc_client/cli_dfs.o \
+ rpc_client/cli_reg.o rpc_client/cli_pipe.o \
+ rpc_client/cli_spoolss.o rpc_client/cli_spoolss_notify.o \
+ rpc_client/cli_ds.o rpc_client/cli_echo.o \
+ rpc_client/cli_shutdown.o rpc_client/cli_epmapper.o
+
+REGOBJS_OBJ = registry/reg_objects.o
+REGISTRY_OBJ = registry/reg_frontend.o registry/reg_cachehook.o registry/reg_printing.o \
+ registry/reg_db.o
+
+RPC_LSA_OBJ = rpc_server/srv_lsa.o rpc_server/srv_lsa_nt.o
+
+RPC_NETLOG_OBJ = rpc_server/srv_netlog.o rpc_server/srv_netlog_nt.o
+
+RPC_SAMR_OBJ = rpc_server/srv_samr.o rpc_server/srv_samr_nt.o \
+ rpc_server/srv_samr_util.o
+
+RPC_REG_OBJ = rpc_server/srv_reg.o rpc_server/srv_reg_nt.o
+
+RPC_LSA_DS_OBJ = rpc_server/srv_lsa_ds.o rpc_server/srv_lsa_ds_nt.o
+
+RPC_SVC_OBJ = rpc_server/srv_srvsvc.o rpc_server/srv_srvsvc_nt.o
+
+RPC_WKS_OBJ = rpc_server/srv_wkssvc.o rpc_server/srv_wkssvc_nt.o
+
+RPC_DFS_OBJ = rpc_server/srv_dfs.o rpc_server/srv_dfs_nt.o
+RPC_SPOOLSS_OBJ = rpc_server/srv_spoolss.o rpc_server/srv_spoolss_nt.o
+
+RPC_PIPE_OBJ = rpc_server/srv_pipe_hnd.o rpc_server/srv_util.o \
+ rpc_server/srv_pipe.o rpc_server/srv_lsa_hnd.o
+
+RPC_ECHO_OBJ = rpc_server/srv_echo.o rpc_server/srv_echo_nt.o
+
+RPC_EPMAPPER_OBJ = rpc_server/srv_epmapper.o rpc_server/srv_epmapper_nt.o
+
+RPC_SERVER_OBJ = @RPC_STATIC@ $(RPC_PIPE_OBJ)
+
+# this includes only the low level parse code, not stuff
+# that requires knowledge of security contexts
+RPC_PARSE_OBJ1 = rpc_parse/parse_prs.o rpc_parse/parse_sec.o \
+ rpc_parse/parse_misc.o
+
+RPC_PARSE_OBJ = rpc_parse/parse_lsa.o rpc_parse/parse_net.o \
+ rpc_parse/parse_reg.o rpc_parse/parse_rpc.o \
+ rpc_parse/parse_samr.o rpc_parse/parse_srv.o \
+ rpc_parse/parse_wks.o rpc_parse/parse_ds.o \
+ rpc_parse/parse_spoolss.o rpc_parse/parse_dfs.o \
+ rpc_parse/parse_echo.o rpc_parse/parse_shutdown.o \
+ rpc_parse/parse_epmapper.o $(REGOBJS_OBJ)
+
+
+RPC_CLIENT_OBJ = rpc_client/cli_pipe.o
+
+LOCKING_OBJ = locking/locking.o locking/brlock.o locking/posix.o smbd/tdbutil.o
+
+GUMS_OBJ = sam/gums.o sam/gums_api.o sam/gums_helper.o @GUMS_STATIC@
+
+PASSDB_GET_SET_OBJ = passdb/pdb_get_set.o
+
+PASSDB_OBJ = $(PASSDB_GET_SET_OBJ) passdb/passdb.o passdb/pdb_interface.o \
+ passdb/util_sam_sid.o passdb/pdb_compat.o \
+ passdb/privileges.o passdb/lookup_sid.o \
+ passdb/login_cache.o @PDB_STATIC@ passdb/pdb_sql.o
+
+XML_OBJ = passdb/pdb_xml.o
+MYSQL_OBJ = passdb/pdb_mysql.o
+PGSQL_OBJ = passdb/pdb_pgsql.o
+DEVEL_HELP_WEIRD_OBJ = modules/weird.o
+CP850_OBJ = modules/CP850.o
+CP437_OBJ = modules/CP437.o
+CHARSET_MACOSXFS_OBJ = modules/charset_macosxfs.o
+
+GROUPDB_OBJ = groupdb/mapping.o
+
+PROFILE_OBJ = profile/profile.o
+PROFILES_OBJ = utils/profiles.o
+EDITREG_OBJ = utils/editreg.o
+
+OPLOCK_OBJ = smbd/oplock.o smbd/oplock_irix.o smbd/oplock_linux.o
+
+NOTIFY_OBJ = smbd/notify.o smbd/notify_hash.o smbd/notify_kernel.o
+
+VFS_AUDIT_OBJ = modules/vfs_audit.o
+VFS_EXTD_AUDIT_OBJ = modules/vfs_extd_audit.o
+VFS_FAKE_PERMS_OBJ = modules/vfs_fake_perms.o
+VFS_RECYCLE_OBJ = modules/vfs_recycle.o
+VFS_NETATALK_OBJ = modules/vfs_netatalk.o
+VFS_DEFAULT_QUOTA_OBJ = modules/vfs_default_quota.o
+VFS_READONLY_OBJ = modules/vfs_readonly.o modules/getdate.o
+VFS_CAP_OBJ = modules/vfs_cap.o
+VFS_EXPAND_MSDFS_OBJ = modules/vfs_expand_msdfs.o
+
+PLAINTEXT_AUTH_OBJ = auth/pampass.o auth/pass_check.o
+
+SLCACHE_OBJ = libsmb/samlogon_cache.o
+
+DCUTIL_OBJ = libsmb/namequery_dc.o libsmb/trustdom_cache.o libsmb/trusts_util.o
+
+AUTH_BUILTIN_OBJ = auth/auth_builtin.o
+AUTH_DOMAIN_OBJ = auth/auth_domain.o
+AUTH_SAM_OBJ = auth/auth_sam.o
+AUTH_RHOSTS_OBJ = auth/auth_rhosts.o
+AUTH_SERVER_OBJ = auth/auth_server.o
+AUTH_UNIX_OBJ = auth/auth_unix.o
+AUTH_WINBIND_OBJ = auth/auth_winbind.o
+
+AUTH_OBJ = auth/auth.o @AUTH_STATIC@ auth/auth_util.o auth/auth_compat.o \
+ auth/auth_ntlmssp.o \
+ $(PLAINTEXT_AUTH_OBJ) $(SLCACHE_OBJ) $(DCUTIL_OBJ)
+
+MANGLE_OBJ = smbd/mangle.o smbd/mangle_hash.o smbd/mangle_map.o smbd/mangle_hash2.o
+
+CONFIG_LDAP_OBJ = param/config_ldap.o
+
+SMBD_OBJ_MAIN = smbd/server.o
+
+BUILDOPT_OBJ = smbd/build_options.o
+
+SMBD_OBJ_SRV = smbd/files.o smbd/chgpasswd.o smbd/connection.o \
+ smbd/utmp.o smbd/session.o \
+ smbd/dfree.o smbd/dir.o smbd/password.o smbd/conn.o smbd/fileio.o \
+ smbd/ipc.o smbd/lanman.o smbd/negprot.o \
+ smbd/message.o smbd/nttrans.o smbd/pipes.o \
+ smbd/reply.o smbd/sesssetup.o smbd/trans2.o smbd/uid.o \
+ smbd/dosmode.o smbd/filename.o smbd/open.o smbd/close.o \
+ smbd/blocking.o smbd/sec_ctx.o smbd/srvstr.o \
+ smbd/vfs.o smbd/vfs-wrap.o smbd/statcache.o \
+ smbd/posix_acls.o lib/sysacls.o lib/server_mutex.o \
+ smbd/process.o smbd/service.o smbd/error.o \
+ printing/printfsp.o lib/sysquotas.o lib/sysquotas_linux.o \
+ lib/sysquotas_xfs.o lib/sysquotas_4A.o \
+ smbd/change_trust_pw.o smbd/fake_file.o \
+ smbd/quotas.o smbd/ntquotas.o lib/afs.o \
+ $(MANGLE_OBJ) @VFS_STATIC@
+
+SMBD_OBJ_BASE = $(PARAM_OBJ) $(SMBD_OBJ_SRV) $(MSDFS_OBJ) $(LIBSMB_OBJ) \
+ $(RPC_SERVER_OBJ) $(RPC_PARSE_OBJ) $(SECRETS_OBJ) \
+ $(LOCKING_OBJ) $(PASSDB_OBJ) $(PRINTING_OBJ) $(PROFILE_OBJ) \
+ $(LIB_OBJ) $(PRINTBACKEND_OBJ) $(OPLOCK_OBJ) \
+ $(NOTIFY_OBJ) $(GROUPDB_OBJ) $(AUTH_OBJ) \
+ $(LIBMSRPC_OBJ) \
+ $(LIBADS_OBJ) $(KRBCLIENT_OBJ) $(LIBADS_SERVER_OBJ) \
+ $(LIB_SMBD_OBJ) $(REGISTRY_OBJ) $(POPT_LIB_OBJ) \
+ $(UBIQX_OBJ) $(BUILDOPT_OBJ) $(SMBLDAP_OBJ)
+
+PRINTING_OBJ = printing/pcap.o printing/print_svid.o \
+ printing/print_cups.o printing/print_generic.o \
+ printing/lpq_parse.o printing/load.o
+
+PRINTBACKEND_OBJ = printing/printing.o printing/nt_printing.o printing/notify.o \
+ printing/printing_db.o
+
+MSDFS_OBJ = msdfs/msdfs.o
+
+SMBD_OBJ = $(SMBD_OBJ_BASE) $(SMBD_OBJ_MAIN)
+NMBD_OBJ1 = nmbd/asyncdns.o nmbd/nmbd.o nmbd/nmbd_become_dmb.o \
+ nmbd/nmbd_become_lmb.o nmbd/nmbd_browserdb.o \
+ nmbd/nmbd_browsesync.o nmbd/nmbd_elections.o \
+ nmbd/nmbd_incomingdgrams.o nmbd/nmbd_incomingrequests.o \
+ nmbd/nmbd_lmhosts.o nmbd/nmbd_logonnames.o nmbd/nmbd_mynames.o \
+ nmbd/nmbd_namelistdb.o nmbd/nmbd_namequery.o \
+ nmbd/nmbd_nameregister.o nmbd/nmbd_namerelease.o \
+ nmbd/nmbd_nodestatus.o nmbd/nmbd_packets.o \
+ nmbd/nmbd_processlogon.o nmbd/nmbd_responserecordsdb.o \
+ nmbd/nmbd_sendannounce.o nmbd/nmbd_serverlistdb.o \
+ nmbd/nmbd_subnetdb.o nmbd/nmbd_winsproxy.o nmbd/nmbd_winsserver.o \
+ nmbd/nmbd_workgroupdb.o nmbd/nmbd_synclists.o
+
+NMBD_OBJ = $(NMBD_OBJ1) $(PARAM_OBJ) $(LIBSMB_OBJ) $(KRBCLIENT_OBJ) $(UBIQX_OBJ) \
+ $(PROFILE_OBJ) $(LIB_NONSMBD_OBJ) $(SECRETS_OBJ) $(POPT_LIB_OBJ)
+
+WREPL_OBJ1 = wrepld/server.o wrepld/process.o wrepld/parser.o wrepld/socket.o \
+ wrepld/partners.o
+
+WREPL_OBJ = $(WREPL_OBJ1) $(PARAM_OBJ) $(UBIQX_OBJ) \
+ $(PROFILE_OBJ) $(LIB_NONSMBD_OBJ) $(POPT_LIB_OBJ) $(SECRETS_OBJ) \
+ $(LIBSAMBA_OBJ)
+
+SWAT_OBJ1 = web/cgi.o web/diagnose.o web/startstop.o web/statuspage.o \
+ web/swat.o web/neg_lang.o
+
+SWAT_OBJ = $(SWAT_OBJ1) $(PARAM_OBJ) $(PRINTING_OBJ) $(LIBSMB_OBJ) \
+ $(LOCKING_OBJ) $(PASSDB_OBJ) $(SECRETS_OBJ) $(KRBCLIENT_OBJ) \
+ $(UBIQX_OBJ) $(LIB_NONSMBD_OBJ) $(GROUPDB_OBJ) $(PLAINTEXT_AUTH_OBJ) \
+ $(POPT_LIB_OBJ) $(SMBLDAP_OBJ) $(RPC_PARSE_OBJ) $(LIBMSRPC_OBJ) \
+ libsmb/passchange.o lib/dummyroot.o
+
+SMBSH_OBJ = smbwrapper/smbsh.o smbwrapper/shared.o \
+ $(PARAM_OBJ) $(UBIQX_OBJ) $(LIB_NONSMBD_OBJ)
+
+STATUS_OBJ = utils/status.o $(LOCKING_OBJ) $(PARAM_OBJ) \
+ $(UBIQX_OBJ) $(PROFILE_OBJ) $(LIB_NONSMBD_OBJ) $(POPT_LIB_OBJ) \
+ $(SECRETS_OBJ) $(LIBSAMBA_OBJ) lib/dummyroot.o libsmb/errormap.o
+
+
+SMBCONTROL_OBJ = utils/smbcontrol.o $(LOCKING_OBJ) $(PARAM_OBJ) \
+ $(UBIQX_OBJ) $(PROFILE_OBJ) $(LIB_NONSMBD_OBJ) $(POPT_LIB_OBJ) \
+ $(SECRETS_OBJ) $(LIBSAMBA_OBJ) \
+ printing/notify.o printing/printing_db.o lib/dummyroot.o libsmb/errormap.o
+
+SMBTREE_OBJ = utils/smbtree.o $(PARAM_OBJ) \
+ $(UBIQX_OBJ) $(PROFILE_OBJ) $(LIB_NONSMBD_OBJ) $(LIBSMB_OBJ) \
+ $(KRBCLIENT_OBJ) $(POPT_LIB_OBJ) $(SECRETS_OBJ)
+
+TESTPARM_OBJ = utils/testparm.o \
+ $(PARAM_OBJ) $(UBIQX_OBJ) $(LIB_NONSMBD_OBJ) $(POPT_LIB_OBJ) \
+ $(SECRETS_OBJ) $(LIBSAMBA_OBJ)
+
+TESTPRNS_OBJ = utils/testprns.o $(PARAM_OBJ) $(PRINTING_OBJ) $(UBIQX_OBJ) \
+ $(LIB_NONSMBD_OBJ)
+
+SMBPASSWD_OBJ = utils/smbpasswd.o libsmb/passchange.o $(PARAM_OBJ) $(SECRETS_OBJ) \
+ $(LIBSMB_OBJ) $(PASSDB_OBJ) $(GROUPDB_OBJ)\
+ $(UBIQX_OBJ) $(LIB_NONSMBD_OBJ) $(KRBCLIENT_OBJ) \
+ $(SMBLDAP_OBJ) $(RPC_PARSE_OBJ) $(LIBMSRPC_OBJ) lib/dummyroot.o
+
+PDBEDIT_OBJ = utils/pdbedit.o $(PARAM_OBJ) $(PASSDB_OBJ) $(LIBSMB_OBJ) $(LIBMSRPC_OBJ) \
+ $(UBIQX_OBJ) $(LIB_NONSMBD_OBJ) $(GROUPDB_OBJ) $(SECRETS_OBJ) \
+ $(POPT_LIB_OBJ) $(SMBLDAP_OBJ) $(KRBCLIENT_OBJ) $(RPC_PARSE_OBJ) lib/dummyroot.o
+
+SMBGET_OBJ = utils/smbget.o $(POPT_LIB_OBJ) $(LIBSMBCLIENT_OBJ) $(SECRETS_OBJ)
+
+RPCCLIENT_OBJ1 = rpcclient/rpcclient.o rpcclient/cmd_lsarpc.o \
+ rpcclient/cmd_samr.o rpcclient/cmd_spoolss.o \
+ rpcclient/cmd_netlogon.o rpcclient/cmd_srvsvc.o \
+ rpcclient/cmd_dfs.o rpcclient/cmd_reg.o \
+ rpcclient/display_sec.o rpcclient/cmd_ds.o \
+ rpcclient/cmd_echo.o rpcclient/cmd_shutdown.o \
+ rpcclient/cmd_epmapper.o
+
+RPCCLIENT_OBJ = $(RPCCLIENT_OBJ1) \
+ $(PARAM_OBJ) $(LIBSMB_OBJ) $(UBIQX_OBJ) $(LIB_NONSMBD_OBJ) \
+ $(RPC_PARSE_OBJ) $(PASSDB_OBJ) $(LIBMSRPC_OBJ) \
+ $(READLINE_OBJ) $(GROUPDB_OBJ) $(KRBCLIENT_OBJ) \
+ $(LIBADS_OBJ) $(SECRETS_OBJ) $(POPT_LIB_OBJ) \
+ $(SMBLDAP_OBJ) $(DCUTIL_OBJ) lib/dummyroot.o
+
+PAM_WINBIND_PICOBJ = nsswitch/pam_winbind.@PICSUFFIX@ \
+ nsswitch/wb_common.@PICSUFFIX@ lib/replace1.@PICSUFFIX@ \
+ lib/snprintf.@PICSUFFIX@
+
+SMBW_OBJ1 = smbwrapper/smbw.o \
+ smbwrapper/smbw_dir.o smbwrapper/smbw_stat.o \
+ smbwrapper/realcalls.o smbwrapper/shared.o \
+ smbwrapper/smbw_cache.o
+
+SMBW_OBJ = $(SMBW_OBJ1) $(LIBSMB_OBJ) $(KRBCLIENT_OBJ) $(PARAM_OBJ) \
+ $(UBIQX_OBJ) $(LIB_NONSMBD_OBJ)
+
+SMBWRAPPER_OBJ1 = smbwrapper/wrapped.o
+
+SMBWRAPPER_OBJ = $(SMBW_OBJ) $(SMBWRAPPER_OBJ1)
+
+LIBSMBCLIENT_OBJ = libsmb/libsmbclient.o libsmb/libsmb_compat.o \
+ libsmb/libsmb_cache.o \
+ $(PARAM_OBJ) $(LIB_NONSMBD_OBJ) \
+ $(LIBSMB_OBJ) $(KRBCLIENT_OBJ) $(UBIQX_OBJ) \
+ $(LIBMSRPC_OBJ) $(RPC_PARSE_OBJ) $(PASSDB_GET_SET_OBJ)
+
+# This shared library is intended for linking with unit test programs
+# to test Samba internals. It's called libbigballofmud.so to
+# discourage casual usage.
+
+LIBBIGBALLOFMUD_MAJOR = 0
+
+LIBBIGBALLOFMUD_OBJ = $(PARAM_OBJ) $(LIB_NONSMBD_OBJ) $(UBIQX_OBJ) $(SECRETS_OBJ) \
+ $(LIBSMB_OBJ) $(LIBMSRPC_OBJ) $(RPC_PARSE_OBJ) $(PASSDB_OBJ) \
+ $(GROUPDB_OBJ) $(KRBCLIENT_OBJ) $(SMBLDAP_OBJ) lib/dummyroot.o
+
+LIBBIGBALLOFMUD_PICOBJS = $(LIBBIGBALLOFMUD_OBJ:.o=.@PICSUFFIX@)
+
+CLIENT_OBJ1 = client/client.o client/clitar.o
+
+CLIENT_OBJ = $(CLIENT_OBJ1) $(PARAM_OBJ) $(LIBSMB_OBJ) $(UBIQX_OBJ) \
+ $(LIB_NONSMBD_OBJ) $(KRBCLIENT_OBJ) \
+ $(READLINE_OBJ) $(POPT_LIB_OBJ) $(SECRETS_OBJ)
+
+NET_OBJ1 = utils/net.o utils/net_ads.o utils/net_ads_cldap.o utils/net_help.o \
+ utils/net_rap.o utils/net_rpc.o utils/net_rpc_samsync.o \
+ utils/net_rpc_join.o utils/net_time.o utils/net_lookup.o \
+ utils/net_cache.o utils/net_groupmap.o utils/net_idmap.o \
+ utils/net_status.o utils/net_privileges.o
+
+NET_OBJ = $(NET_OBJ1) $(PARAM_OBJ) $(SECRETS_OBJ) $(LIBSMB_OBJ) \
+ $(RPC_PARSE_OBJ) $(PASSDB_OBJ) $(GROUPDB_OBJ) \
+ $(KRBCLIENT_OBJ) $(UBIQX_OBJ) $(LIB_NONSMBD_OBJ) \
+ $(LIBMSRPC_OBJ) $(IDMAP_OBJ) \
+ $(LIBADS_OBJ) $(LIBADS_SERVER_OBJ) $(POPT_LIB_OBJ) \
+ $(SMBLDAP_OBJ) $(DCUTIL_OBJ) lib/dummyroot.o lib/server_mutex.o lib/afs.o
+
+CUPS_OBJ = client/smbspool.o $(PARAM_OBJ) $(LIBSMB_OBJ) $(UBIQX_OBJ) \
+ $(LIB_NONSMBD_OBJ) $(KRBCLIENT_OBJ) $(SECRETS_OBJ)
+
+MOUNT_OBJ = client/smbmount.o \
+ $(PARAM_OBJ) $(LIBSMB_OBJ) $(KRBCLIENT_OBJ) $(UBIQX_OBJ) $(LIB_NONSMBD_OBJ)
+
+MNT_OBJ = client/smbmnt.o lib/version.o lib/snprintf.o
+
+UMOUNT_OBJ = client/smbumount.o
+
+NMBLOOKUP_OBJ = utils/nmblookup.o $(PARAM_OBJ) $(UBIQX_OBJ) $(LIBNMB_OBJ) \
+ $(LIB_NONSMBD_OBJ) $(POPT_LIB_OBJ) $(SECRETS_OBJ) $(LIBSAMBA_OBJ)
+
+SMBTORTURE_OBJ1 = torture/torture.o torture/nbio.o torture/scanner.o torture/utable.o \
+ torture/denytest.o torture/mangle_test.o
+
+SMBTORTURE_OBJ = $(SMBTORTURE_OBJ1) $(PARAM_OBJ) \
+ $(LIBSMB_OBJ) $(KRBCLIENT_OBJ) $(UBIQX_OBJ) $(LIB_NONSMBD_OBJ)
+
+MASKTEST_OBJ = torture/masktest.o $(PARAM_OBJ) $(LIBSMB_OBJ) $(KRBCLIENT_OBJ) \
+ $(UBIQX_OBJ) $(LIB_NONSMBD_OBJ)
+
+MSGTEST_OBJ = torture/msgtest.o $(PARAM_OBJ) $(LIBSMB_OBJ) $(KRBCLIENT_OBJ) \
+ $(UBIQX_OBJ) $(LIB_NONSMBD_OBJ)
+
+LOCKTEST_OBJ = torture/locktest.o $(PARAM_OBJ) $(LOCKING_OBJ) $(KRBCLIENT_OBJ) \
+ $(LIBSMB_OBJ) $(UBIQX_OBJ) $(LIB_NONSMBD_OBJ) lib/dummyroot.o
+
+NSSTEST_OBJ = torture/nsstest.o $(PARAM_OBJ) $(LIBSMB_OBJ) $(KRBCLIENT_OBJ) \
+ $(UBIQX_OBJ) $(LIB_NONSMBD_OBJ)
+
+VFSTEST_OBJ = torture/cmd_vfs.o torture/vfstest.o $(SMBD_OBJ_BASE) $(READLINE_OBJ)
+
+SMBICONV_OBJ = $(PARAM_OBJ) torture/smbiconv.o $(LIB_NONSMBD_OBJ) $(UBIQX_OBJ) $(POPT_LIB_OBJ) $(SECRETS_OBJ) $(LIBSAMBA_OBJ)
+
+LOG2PCAP_OBJ = utils/log2pcaphex.o
+
+LOCKTEST2_OBJ = torture/locktest2.o $(PARAM_OBJ) $(LOCKING_OBJ) $(LIBSMB_OBJ) \
+ $(KRBCLIENT_OBJ) $(UBIQX_OBJ) $(LIB_NONSMBD_OBJ) lib/dummyroot.o
+
+SMBCACLS_OBJ = utils/smbcacls.o $(PARAM_OBJ) $(LIBSMB_OBJ) \
+ $(KRBCLIENT_OBJ) $(UBIQX_OBJ) $(LIB_NONSMBD_OBJ) $(RPC_PARSE_OBJ) \
+ $(PASSDB_GET_SET_OBJ) $(LIBMSRPC_OBJ) $(SECRETS_OBJ) \
+ $(POPT_LIB_OBJ) $(DCUTIL_OBJ) $(LIBADS_OBJ)
+
+SMBCQUOTAS_OBJ = utils/smbcquotas.o $(LIBSMB_OBJ) $(KRBCLIENT_OBJ) \
+ $(PARAM_OBJ) \
+ $(UBIQX_OBJ) $(LIB_NONSMBD_OBJ) $(RPC_PARSE_OBJ) $(PASSDB_GET_SET_OBJ) \
+ $(LIBMSRPC_OBJ) $(SECRETS_OBJ) $(POPT_LIB_OBJ)
+
+TALLOCTORT_OBJ = lib/talloctort.o $(PARAM_OBJ) $(LIB_NONSMBD_OBJ) $(UBIQX_OBJ)
+
+RPCTORTURE_OBJ = torture/rpctorture.o \
+ rpcclient/display.o \
+ rpcclient/cmd_lsarpc.o \
+ rpcclient/cmd_wkssvc.o \
+ rpcclient/cmd_samr.o \
+ rpcclient/cmd_srvsvc.o \
+ rpcclient/cmd_netlogon.o \
+ $(PARAM_OBJ) $(LIBSMB_OBJ) $(UBIQX_OBJ) $(LIB_NONSMBD_OBJ) $(KRBCLIENT_OBJ) \
+ $(RPC_CLIENT_OBJ) $(RPC_PARSE_OBJ) $(PASSDB_GET_SET_OBJ)
+
+DEBUG2HTML_OBJ = utils/debug2html.o ubiqx/debugparse.o
+
+SMBFILTER_OBJ = utils/smbfilter.o $(PARAM_OBJ) $(LIBSMB_OBJ) \
+ $(UBIQX_OBJ) $(LIB_NONSMBD_OBJ) $(KRBCLIENT_OBJ)
+
+PROTO_OBJ = $(SMBD_OBJ_MAIN) \
+ $(SMBD_OBJ_SRV) $(NMBD_OBJ1) $(SWAT_OBJ1) $(LIB_OBJ) $(LIBSMB_OBJ) \
+ $(SMBW_OBJ1) $(SMBWRAPPER_OBJ1) $(SMBTORTURE_OBJ1) $(RPCCLIENT_OBJ1) \
+ $(LIBMSRPC_OBJ) $(RPC_CLIENT_OBJ) \
+ $(RPC_PIPE_OBJ) $(RPC_PARSE_OBJ) $(KRBCLIENT_OBJ) \
+ $(AUTH_OBJ) $(PARAM_OBJ) $(LOCKING_OBJ) $(SECRETS_OBJ) \
+ $(PRINTING_OBJ) $(PRINTBACKEND_OBJ) $(OPLOCK_OBJ) $(NOTIFY_OBJ) \
+ $(PASSDB_OBJ) $(GROUPDB_OBJ) $(MSDFS_OBJ) \
+ $(READLINE_OBJ) $(PROFILE_OBJ) $(LIBADS_OBJ) $(LIBADS_SERVER_OBJ) \
+ $(LIB_SMBD_OBJ) $(AUTH_SAM_OBJ) $(REGISTRY_OBJ) $(POPT_LIB_OBJ) \
+ $(RPC_LSA_OBJ) $(RPC_NETLOG_OBJ) $(RPC_SAMR_OBJ) $(RPC_REG_OBJ) \
+ $(RPC_LSA_DS_OBJ) $(RPC_SVC_OBJ) $(RPC_WKS_OBJ) $(RPC_DFS_OBJ) \
+ $(RPC_SPOOLSS_OBJ) $(RPC_ECHO_OBJ) $(RPC_EPMAPPER_OBJ) \
+ $(SMBLDAP_OBJ) $(IDMAP_OBJ) libsmb/spnego.o libsmb/passchange.o
+
+WINBIND_WINS_NSS_OBJ = nsswitch/wins.o $(PARAM_OBJ) $(UBIQX_OBJ) \
+ $(LIBSMB_OBJ) $(LIB_NONSMBD_OBJ) $(NSSWINS_OBJ) $(KRBCLIENT_OBJ)
+
+WINBIND_WINS_NSS_PICOBJS = $(WINBIND_WINS_NSS_OBJ:.o=.@PICSUFFIX@)
+
+PICOBJS = $(SMBWRAPPER_OBJ:.o=.@PICSUFFIX@)
+LIBSMBCLIENT_PICOBJS = $(LIBSMBCLIENT_OBJ:.o=.@PICSUFFIX@)
+
+PAM_SMBPASS_OBJ_0 = pam_smbpass/pam_smb_auth.o pam_smbpass/pam_smb_passwd.o \
+ pam_smbpass/pam_smb_acct.o pam_smbpass/support.o \
+ lib/dummyroot.o $(PARAM_OBJ) $(LIB_NONSMBD_OBJ) $(PASSDB_OBJ) $(GROUPDB_OBJ) \
+ $(SECRETS_OBJ) $(UBIQX_OBJ) $(SMBLDAP_OBJ) $(LIBSAMBA_OBJ)
+
+PAM_SMBPASS_PICOOBJ = $(PAM_SMBPASS_OBJ_0:.o=.@PICSUFFIX@)
+
+IDMAP_OBJ = sam/idmap.o sam/idmap_util.o @IDMAP_STATIC@
+
+WINBINDD_OBJ1 = \
+ nsswitch/winbindd.o \
+ nsswitch/winbindd_user.o \
+ nsswitch/winbindd_group.o \
+ nsswitch/winbindd_util.o \
+ nsswitch/winbindd_cache.o \
+ nsswitch/winbindd_pam.o \
+ nsswitch/winbindd_sid.o \
+ nsswitch/winbindd_misc.o \
+ nsswitch/winbindd_cm.o \
+ nsswitch/winbindd_wins.o \
+ nsswitch/winbindd_rpc.o \
+ nsswitch/winbindd_ads.o \
+ nsswitch/winbindd_passdb.o \
+ nsswitch/winbindd_dual.o \
+ nsswitch/winbindd_acct.o
+
+WINBINDD_OBJ = \
+ $(WINBINDD_OBJ1) $(PASSDB_OBJ) $(GROUPDB_OBJ) \
+ $(PARAM_OBJ) $(UBIQX_OBJ) $(LIB_NONSMBD_OBJ) \
+ $(LIBSMB_OBJ) $(LIBMSRPC_OBJ) $(RPC_PARSE_OBJ) \
+ $(PROFILE_OBJ) $(SLCACHE_OBJ) $(SMBLDAP_OBJ) \
+ $(SECRETS_OBJ) $(LIBADS_OBJ) $(KRBCLIENT_OBJ) $(POPT_LIB_OBJ) \
+ $(DCUTIL_OBJ) $(IDMAP_OBJ) lib/dummyroot.o lib/afs.o
+
+WBINFO_OBJ = nsswitch/wbinfo.o $(LIBSAMBA_OBJ) $(PARAM_OBJ) $(LIB_NONSMBD_OBJ) \
+ $(UBIQX_OBJ) $(SECRETS_OBJ) $(POPT_LIB_OBJ) lib/afs.o
+
+WINBIND_NSS_OBJ = nsswitch/wb_common.o lib/replace1.o @WINBIND_NSS_EXTRA_OBJS@
+
+WINBIND_NSS_PICOBJS = $(WINBIND_NSS_OBJ:.o=.@PICSUFFIX@) lib/snprintf.@PICSUFFIX@
+
+POPT_OBJS=popt/findme.o popt/popt.o popt/poptconfig.o \
+ popt/popthelp.o popt/poptparse.o
+
+TDBBACKUP_OBJ = tdb/tdbbackup.o tdb/tdbback.o lib/snprintf.o $(TDBBASE_OBJ)
+
+TDBDUMP_OBJ = tdb/tdbdump.o $(TDBBASE_OBJ)
+
+NTLM_AUTH_OBJ = utils/ntlm_auth.o $(LIBSAMBA_OBJ) $(POPT_LIB_OBJ) \
+ libsmb/asn1.o libsmb/spnego.o libsmb/clikrb5.o libads/kerberos.o \
+ libads/kerberos_verify.o $(SECRETS_OBJ) lib/server_mutex.o \
+ libads/authdata.o rpc_parse/parse_prs.o rpc_parse/parse_misc.o \
+ libsmb/doserr.o
+
+######################################################################
+# now the rules...
+######################################################################
+all : SHOWFLAGS proto_exists $(SBIN_PROGS) $(BIN_PROGS) $(SHLIBS) \
+ $(MODULES) @EXTRA_ALL_TARGETS@
+
+pam_smbpass : SHOWFLAGS bin/pam_smbpass.@SHLIBEXT@
+
+smbwrapper : SHOWFLAGS @SMBWRAPPER@
+
+torture : SHOWFLAGS $(TORTURE_PROGS)
+
+smbtorture : SHOWFLAGS bin/smbtorture@EXEEXT@
+
+masktest : SHOWFLAGS bin/masktest@EXEEXT@
+
+msgtest : SHOWFLAGS bin/msgtest@EXEEXT@
+
+locktest : SHOWFLAGS bin/locktest@EXEEXT@
+
+smbcacls : SHOWFLAGS bin/smbcacls@EXEEXT@
+
+smbcquotas : SHOWFLAGS bin/smbcquotas@EXEEXT@
+
+locktest2 : SHOWFLAGS bin/locktest2@EXEEXT@
+
+rpctorture : SHOWFLAGS bin/rpctorture@EXEEXT@
+
+debug2html : SHOWFLAGS bin/debug2html@EXEEXT@
+
+smbfilter : SHOWFLAGS bin/smbfilter@EXEEXT@
+
+talloctort : SHOWFLAGS bin/talloctort@EXEEXT@
+
+nsswitch : SHOWFLAGS bin/winbindd@EXEEXT@ bin/wbinfo@EXEEXT@ @WINBIND_NSS@ \
+ @WINBIND_WINS_NSS@ nsswitch/pam_winbind.@SHLIBEXT@
+
+wins : SHOWFLAGS @WINBIND_WINS_NSS@
+
+modules: SHOWFLAGS proto_exists $(MODULES)
+
+everything: all libsmbclient debug2html smbfilter talloctort modules torture \
+ $(EVERYTHING_PROGS)
+
+.SUFFIXES:
+.SUFFIXES: .c .o .@PICSUFFIX@ .lo
+
+SHOWFLAGS:
+ @echo "Using FLAGS = $(FLAGS)"
+ @echo " LIBS = $(LIBS)"
+ @echo " LDSHFLAGS = $(LDSHFLAGS)"
+ @echo " LDFLAGS = $(LDFLAGS)"
+
+MAKEDIR = || exec false; \
+ if test -d "$$dir"; then :; else \
+ echo mkdir "$$dir"; \
+ mkdir -p "$$dir" >/dev/null 2>&1 || \
+ test -d "$$dir" || \
+ mkdir "$$dir" || \
+ exec false; fi || exec false
+
+.c.o:
+ @if (: >> $@ || : > $@) >/dev/null 2>&1; then rm -f $@; else \
+ dir=`echo $@ | sed 's,/[^/]*$$,,;s,^$$,.,'` $(MAKEDIR); fi
+ @echo Compiling $*.c
+ @$(CC) -I. -I$(srcdir) $(FLAGS) -c $< \
+ -o $@
+@BROKEN_CC@ -mv `echo $@ | sed 's%^.*/%%g'` $@
+
+# this adds support for precompiled headers. To use it, install a snapshot
+# of gcc-3.4 and run 'make pch' before you do the main build.
+pch:
+ rm -f $(srcdir)/include/includes.h.gch
+ $(CC) -I. -I$(srcdir) $(FLAGS) -c $(srcdir)/include/includes.h -o $(srcdir)/include/includes.h.gch
+
+# These dependencies are only approximately correct: we want to make
+# sure Samba's paths are updated if ./configure is re-run. Really it
+# would be nice if "make prefix=/opt/samba all" also rebuilt things,
+# but since we also require "make install prefix=/opt/samba" *not* to
+# rebuild it's a bit hard.
+
+dynconfig.o: dynconfig.c Makefile
+ @echo Compiling $*.c
+ @$(CC) $(FLAGS) $(PATH_FLAGS) -c $< -o $@
+
+dynconfig.@PICSUFFIX@: dynconfig.c Makefile
+ @if (: >> $@ || : > $@) >/dev/null 2>&1; then rm -f $@; else \
+ dir=`echo $@ | sed 's,/[^/]*$$,,;s,^$$,.,'` $(MAKEDIR); fi
+ @echo Compiling $*.c with @PICFLAGS@
+ @$(CC) -I. -I$(srcdir) $(FLAGS) $(PATH_FLAGS) @PICFLAGS@ -c $< -o $*.@PICSUFFIX@
+@BROKEN_CC@ -mv `echo $@ | sed -e 's%^.*/%%g' -e 's%\.@PICSUFFIX@$$%.o%'` $@
+
+lib/version.o: lib/version.c include/version.h
+ @echo Compiling $*.c
+ @$(CC) -I. -I$(srcdir) $(FLAGS) $(PATH_FLAGS) -c $< -o $@
+
+lib/version.@PICSUFFIX@: lib/version.c include/version.h
+ @if (: >> $@ || : > $@) >/dev/null 2>&1; then rm -f $@; else \
+ dir=`echo $@ | sed 's,/[^/]*$$,,;s,^$$,.,'` $(MAKEDIR); fi
+ @echo Compiling $*.c with @PICFLAGS@
+ @$(CC) -I. -I$(srcdir) $(FLAGS) $(PATH_FLAGS) @PICFLAGS@ -c $< -o $*.@PICSUFFIX@
+@BROKEN_CC@ -mv `echo $@ | sed -e 's%^.*/%%g' -e 's%\.@PICSUFFIX@$$%.o%'` $@
+
+smbd/build_options.o: smbd/build_options.c Makefile include/config.h include/build_env.h include/proto.h
+ @echo Compiling $*.c
+ @$(CC) $(FLAGS) $(PATH_FLAGS) -c $< -o $@
+
+smbd/build_options.c: include/config.h.in script/mkbuildoptions.awk
+ @echo Generating $@
+ @dir=smbd $(MAKEDIR) && $(AWK) -f $(srcdir)/script/mkbuildoptions.awk > $(builddir)/smbd/build_options.c < $(srcdir)/include/config.h.in
+
+.c.@PICSUFFIX@:
+ @if (: >> $@ || : > $@) >/dev/null 2>&1; then rm -f $@; else \
+ dir=`echo $@ | sed 's,/[^/]*$$,,;s,^$$,.,'` $(MAKEDIR); fi
+ @echo Compiling $*.c with @PICFLAGS@
+ @$(CC) -I. -I$(srcdir) $(FLAGS) @PICFLAGS@ -c $< -o $*.@PICSUFFIX@
+@BROKEN_CC@ -mv `echo $@ | sed -e 's%^.*/%%g' -e 's%\.@PICSUFFIX@$$%.o%'` $@
+
+bin/.dummy:
+ @if (: >> $@ || : > $@) >/dev/null 2>&1; then :; else \
+ dir=bin $(MAKEDIR); fi
+ @: >> $@ || : > $@ # what a fancy emoticon!
+
+bin/smbd@EXEEXT@: $(SMBD_OBJ) @BUILD_POPT@ bin/.dummy
+ @echo Linking $@
+ @$(CC) $(FLAGS) -o $@ $(SMBD_OBJ) $(LDFLAGS) $(LDAP_LIBS) \
+ $(KRB5LIBS) $(DYNEXP) $(PRINT_LIBS) $(AUTH_LIBS) \
+ $(ACL_LIBS) $(PASSDB_LIBS) $(LIBS) @POPTLIBS@
+
+bin/nmbd@EXEEXT@: $(NMBD_OBJ) @BUILD_POPT@ bin/.dummy
+ @echo Linking $@
+ @$(CC) $(FLAGS) -o $@ $(NMBD_OBJ) $(LDFLAGS) $(DYNEXP) $(LIBS) @POPTLIBS@ $(KRB5LIBS) $(LDAP_LIBS)
+
+bin/wrepld@EXEEXT@: $(WREPL_OBJ) @BUILD_POPT@ bin/.dummy
+ @echo Linking $@
+ @$(CC) $(FLAGS) -o $@ $(WREPL_OBJ) $(LDFLAGS) $(DYNEXP) $(LIBS) @POPTLIBS@
+
+bin/swat@EXEEXT@: $(SWAT_OBJ) @BUILD_POPT@ bin/.dummy
+ @echo Linking $@
+ @$(CC) $(FLAGS) -o $@ $(SWAT_OBJ) $(LDFLAGS) $(DYNEXP) $(PRINT_LIBS) \
+ $(AUTH_LIBS) $(LIBS) $(PASSDB_LIBS) @POPTLIBS@ $(KRB5LIBS) $(LDAP_LIBS)
+
+bin/rpcclient@EXEEXT@: $(RPCCLIENT_OBJ) @BUILD_POPT@ bin/.dummy
+ @echo Linking $@
+ @$(CC) $(FLAGS) -o $@ $(LDFLAGS) $(PASSDB_LIBS) $(RPCCLIENT_OBJ) \
+ $(DYNEXP) $(TERMLDFLAGS) $(TERMLIBS) $(LIBS) @POPTLIBS@ \
+ $(KRB5LIBS) $(LDAP_LIBS)
+
+bin/smbclient@EXEEXT@: $(CLIENT_OBJ) @BUILD_POPT@ bin/.dummy
+ @echo Linking $@
+ @$(CC) $(FLAGS) -o $@ $(CLIENT_OBJ) $(LDFLAGS) $(DYNEXP) $(TERMLDFLAGS) $(TERMLIBS) $(LIBS) @POPTLIBS@ $(KRB5LIBS) $(LDAP_LIBS)
+
+bin/net@EXEEXT@: $(NET_OBJ) @BUILD_POPT@ bin/.dummy
+ @echo Linking $@
+ @$(CC) $(FLAGS) -o $@ $(NET_OBJ) $(DYNEXP) $(LDFLAGS) $(LIBS) @POPTLIBS@ $(KRB5LIBS) $(LDAP_LIBS) $(PASSDB_LIBS)
+
+bin/profiles@EXEEXT@: $(PROFILES_OBJ) @BUILD_POPT@ bin/.dummy
+ @echo Linking $@
+ @$(CC) $(FLAGS) -o $@ $(PROFILES_OBJ) $(LDFLAGS) $(LIBS) @POPTLIBS@
+
+bin/editreg@EXEEXT@: $(EDITREG_OBJ) @BUILD_POPT@ bin/.dummy
+ @echo Linking $@
+ @$(CC) $(FLAGS) -o $@ $(EDITREG_OBJ) $(LDFLAGS) $(LIBS) @POPTLIBS@
+
+bin/smbspool@EXEEXT@: $(CUPS_OBJ) bin/.dummy
+ @echo Linking $@
+ @$(CC) $(FLAGS) -o $@ $(CUPS_OBJ) $(LDFLAGS) $(LIBS) $(KRB5LIBS) $(LDAP_LIBS)
+
+bin/smbmount@EXEEXT@: $(MOUNT_OBJ) bin/.dummy
+ @echo Linking $@
+ @$(CC) $(FLAGS) -o $@ $(MOUNT_OBJ) $(LDFLAGS) $(LIBS) $(KRB5LIBS) $(LDAP_LIBS)
+
+bin/smbmnt@EXEEXT@: $(MNT_OBJ) bin/.dummy
+ @echo Linking $@
+ @$(CC) $(FLAGS) -o $@ $(MNT_OBJ) $(LDFLAGS)
+
+bin/smbumount@EXEEXT@: $(UMOUNT_OBJ) bin/.dummy
+ @echo Linking $@
+ @$(CC) $(FLAGS) -o $@ $(UMOUNT_OBJ) $(LDFLAGS)
+
+bin/testparm@EXEEXT@: $(TESTPARM_OBJ) @BUILD_POPT@ bin/.dummy
+ @echo Linking $@
+ @$(CC) $(FLAGS) -o $@ $(TESTPARM_OBJ) $(LDFLAGS) $(DYNEXP) $(LIBS) @POPTLIBS@
+
+bin/testprns@EXEEXT@: $(TESTPRNS_OBJ) bin/.dummy
+ @echo Linking $@
+ @$(CC) $(FLAGS) -o $@ $(TESTPRNS_OBJ) $(LDFLAGS) $(DYNEXP) $(PRINT_LIBS) $(LIBS)
+
+bin/smbstatus@EXEEXT@: $(STATUS_OBJ) @BUILD_POPT@ bin/.dummy
+ @echo Linking $@
+ @$(CC) $(FLAGS) -o $@ $(STATUS_OBJ) $(LDFLAGS) $(DYNEXP) $(LIBS) \
+ @POPTLIBS@
+
+bin/smbcontrol@EXEEXT@: $(SMBCONTROL_OBJ) @BUILD_POPT@ bin/.dummy
+ @echo Linking $@
+ @$(CC) -DUSING_SMBCONTROL $(FLAGS) -o $@ $(SMBCONTROL_OBJ) $(DYNEXP) \
+ $(LDFLAGS) $(LIBS) \
+ @POPTLIBS@
+
+bin/smbtree@EXEEXT@: $(SMBTREE_OBJ) @BUILD_POPT@ bin/.dummy
+ @echo Linking $@
+ @$(CC) $(FLAGS) -o $@ $(SMBTREE_OBJ) $(LDFLAGS) $(DYNEXP) $(LIBS) @POPTLIBS@ $(KRB5LIBS) $(LDAP_LIBS)
+
+bin/smbpasswd@EXEEXT@: $(SMBPASSWD_OBJ) bin/.dummy
+ @echo Linking $@
+ @$(CC) $(FLAGS) -o $@ $(SMBPASSWD_OBJ) $(LDFLAGS) $(PASSDB_LIBS) \
+ $(DYNEXP) $(LIBS) $(KRB5LIBS) $(LDAP_LIBS)
+
+bin/pdbedit@EXEEXT@: $(PDBEDIT_OBJ) @BUILD_POPT@ bin/.dummy
+ @echo Linking $@
+ @$(CC) $(FLAGS) -o $@ $(PDBEDIT_OBJ) $(LDFLAGS) $(DYNEXP) $(LIBS) @POPTLIBS@ $(PASSDB_LIBS) $(LDAP_LIBS) $(KRB5LIBS)
+
+bin/smbget@EXEEXT@: $(SMBGET_OBJ) @BUILD_POPT@ bin/.dummy
+ @echo Linking $@
+ @$(CC) $(FLAGS) -o $@ $(SMBGET_OBJ) $(LDFLAGS) $(DYNEXP) $(LIBS) @POPTLIBS@ $(KRB5LIBS) $(LDAP_LIBS)
+
+bin/samtest@EXEEXT@: $(SAMTEST_OBJ) @BUILD_POPT@ bin/.dummy
+ @echo Linking $@
+ @$(CC) $(FLAGS) -o $@ $(SAMTEST_OBJ) $(LDFLAGS) $(TERMLDFLAGS) $(TERMLIBS) $(DYNEXP) $(LIBS) @POPTLIBS@ $(PASSDB_LIBS) $(KRB5LIBS) $(LDAP_LIBS)
+
+bin/nmblookup@EXEEXT@: $(NMBLOOKUP_OBJ) @BUILD_POPT@ bin/.dummy
+ @echo Linking $@
+ @$(CC) $(FLAGS) -o $@ $(NMBLOOKUP_OBJ) $(LDFLAGS) $(DYNEXP) $(LIBS) @POPTLIBS@ $(LDAP_LIBS)
+
+bin/smbtorture@EXEEXT@: $(SMBTORTURE_OBJ) bin/.dummy
+ @echo Linking $@
+ @$(CC) $(FLAGS) -o $@ $(SMBTORTURE_OBJ) $(LDFLAGS) $(DYNEXP) $(LIBS) $(KRB5LIBS) $(LDAP_LIBS) $(SECRETS_OBJ)
+
+bin/talloctort@EXEEXT@: $(TALLOCTORT_OBJ) bin/.dummy
+ @echo Linking $@
+ @$(CC) $(FLAGS) -o $@ $(TALLOCTORT_OBJ) $(LDFLAGS) $(LIBS)
+
+bin/masktest@EXEEXT@: $(MASKTEST_OBJ) bin/.dummy
+ @echo Linking $@
+ @$(CC) $(FLAGS) -o $@ $(MASKTEST_OBJ) $(LDFLAGS) $(LIBS) $(KRB5LIBS) $(LDAP_LIBS) $(SECRETS_OBJ)
+
+bin/msgtest@EXEEXT@: $(MSGTEST_OBJ) bin/.dummy
+ @echo Linking $@
+ @$(CC) $(FLAGS) -o $@ $(MSGTEST_OBJ) $(LDFLAGS) $(LIBS) $(KRB5LIBS) $(LDAP_LIBS)
+
+bin/smbcacls@EXEEXT@: $(SMBCACLS_OBJ) @BUILD_POPT@ bin/.dummy
+ @echo Linking $@
+ @$(CC) $(FLAGS) -o $@ $(SMBCACLS_OBJ) $(DYNEXP) $(LDFLAGS) $(LIBS) @POPTLIBS@ $(KRB5LIBS) $(LDAP_LIBS)
+
+bin/smbcquotas@EXEEXT@: $(SMBCQUOTAS_OBJ) @BUILD_POPT@ bin/.dummy
+ @echo Linking $@
+ @$(CC) $(FLAGS) -o $@ $(SMBCQUOTAS_OBJ) $(DYNEXP) $(LDFLAGS) $(LIBS) @POPTLIBS@ $(KRB5LIBS) $(LDAP_LIBS)
+
+bin/locktest@EXEEXT@: $(LOCKTEST_OBJ) bin/.dummy
+ @echo Linking $@
+ @$(CC) $(FLAGS) -o $@ $(LOCKTEST_OBJ) $(LDFLAGS) $(LIBS) $(KRB5LIBS) $(LDAP_LIBS)
+
+bin/nsstest@EXEEXT@: $(NSSTEST_OBJ) bin/.dummy
+ @echo Linking $@
+ @$(CC) $(FLAGS) -o $@ $(NSSTEST_OBJ) $(LDFLAGS) $(LIBS) $(KRB5LIBS) $(LDAP_LIBS)
+
+bin/vfstest@EXEEXT@: $(VFSTEST_OBJ) @BUILD_POPT@ bin/.dummy
+ @echo Linking $@
+ @$(CC) $(FLAGS) -o $@ $(VFSTEST_OBJ) $(LDFLAGS) $(TERMLDFLAGS) $(TERMLIBS) $(DYNEXP) $(PRINT_LIBS) $(AUTH_LIBS) $(ACL_LIBS) $(LIBS) @POPTLIBS@ $(KRB5LIBS) $(LDAP_LIBS)
+
+bin/smbiconv@EXEEXT@: $(SMBICONV_OBJ) @BUILD_POPT@ bin/.dummy
+ @echo Linking $@
+ @$(CC) $(FLAGS) -o $@ $(SMBICONV_OBJ) $(LDFLAGS) $(TERMLDFLAGS) $(TERMLIBS) $(DYNEXP) $(LIBS) @POPTLIBS@
+
+bin/log2pcap@EXEEXT@: $(LOG2PCAP_OBJ) @BUILD_POPT@ bin/.dummy
+ @echo Linking $@
+ @$(CC) $(FLAGS) -o $@ $(LOG2PCAP_OBJ) $(LDFLAGS) @POPTLIBS@ $(LIBS)
+
+bin/locktest2@EXEEXT@: $(LOCKTEST2_OBJ) bin/.dummy
+ @echo Linking $@
+ @$(CC) $(FLAGS) -o $@ $(LOCKTEST2_OBJ) $(LDFLAGS) $(LIBS) $(KRB5LIBS) $(LDAP_LIBS)
+
+bin/rpctorture@EXEEXT@: $(RPCTORTURE_OBJ) bin/.dummy
+ @echo Linking $@
+ @$(CC) $(FLAGS) -o $@ $(RPCTORTURE_OBJ) $(DYNEXP) $(LDFLAGS) $(LIBS) $(KRB5LIBS) $(LDAP_LIBS)
+
+bin/debug2html@EXEEXT@: $(DEBUG2HTML_OBJ) bin/.dummy
+ @echo Linking $@
+ @$(CC) $(FLAGS) -o $@ $(DEBUG2HTML_OBJ) $(LDFLAGS) $(LIBS)
+
+bin/smbfilter@EXEEXT@: $(SMBFILTER_OBJ) bin/.dummy
+ @echo Linking $@
+ @$(CC) $(FLAGS) -o $@ $(SMBFILTER_OBJ) $(LDFLAGS) $(LIBS) $(KRB5LIBS) $(LDAP_LIBS)
+
+bin/smbw_sample@EXEEXT@: $(SMBW_OBJ) utils/smbw_sample.o bin/.dummy
+ @echo Linking $@
+ @$(CC) $(FLAGS) -o $@ $(SMBW_OBJ) utils/smbw_sample.o $(LDFLAGS) $(LIBS) $(KRB5LIBS) $(LDAP_LIBS)
+
+bin/smbsh@EXEEXT@: $(SMBSH_OBJ) bin/.dummy
+ @echo Linking $@
+ @$(CC) $(FLAGS) -o $@ $(SMBSH_OBJ) $(LDFLAGS) $(DYNEXP) $(LIBS)
+
+bin/smbwrapper.@SHLIBEXT@: $(PICOBJS) bin/.dummy
+ @echo Linking shared library $@
+ @$(SHLD) $(LDSHFLAGS) -o $@ $(PICOBJS) $(LIBS) \
+ $(KRB5LIBS) $(LDAP_LIBS) \
+ @SONAMEFLAG@`basename $@`
+
+bin/libsmbclient.@SHLIBEXT@: $(LIBSMBCLIENT_PICOBJS)
+ @echo Linking libsmbclient shared library $@
+ @$(SHLD) $(LDSHFLAGS) -o $@ $(LIBSMBCLIENT_PICOBJS) $(LDFLAGS) $(DYNEXP) $(LIBS) \
+ $(KRB5LIBS) $(LDAP_LIBS) \
+ @SONAMEFLAG@`basename $@`.$(LIBSMBCLIENT_MAJOR)
+
+bin/libsmbclient.a: $(LIBSMBCLIENT_PICOBJS)
+ @echo Linking libsmbclient non-shared library $@
+ @-$(AR) -rc $@ $(LIBSMBCLIENT_PICOBJS)
+
+# This is probably wrong for anything other than the GNU linker.
+bin/libbigballofmud.@SHLIBEXT@: $(LIBBIGBALLOFMUD_PICOBJS)
+ @echo Linking bigballofmud shared library $@
+ @$(SHLD) $(LDSHFLAGS) -o $@ $(LIBBIGBALLOFMUD_PICOBJS) $(LIBS) \
+ $(PASSDB_LIBS) $(IDMAP_LIBS) $(KRB5LIBS) $(LDAP_LIBS) \
+ @SONAMEFLAG@`basename $@`.$(LIBBIGBALLOFMUD_MAJOR)
+ ln -snf libbigballofmud.so bin/libbigballofmud.so.0
+
+# It would be nice to build a static bigballofmud too, but when I try
+# I get linker errors about dl_open and similar things. I'm not sure if
+# it can be fixed or if they just can't be called from a static
+# library.
+
+libsmbclient: $(LIBSMBCLIENT)
+
+bin/librpc_lsarpc.@SHLIBEXT@: $(RPC_LSA_OBJ)
+ @echo "Linking $@"
+ @$(SHLD) $(LDSHFLAGS) -o $@ $(RPC_LSA_OBJ) -lc \
+ @SONAMEFLAG@`basename $@`
+
+bin/librpc_samr.@SHLIBEXT@: $(RPC_SAMR_OBJ)
+ @echo "Linking $@"
+ @$(SHLD) $(LDSHFLAGS) -o $@ $(RPC_SAMR_OBJ) -lc \
+ @SONAMEFLAG@`basename $@`
+
+bin/librpc_srvsvc.@SHLIBEXT@: $(RPC_SVC_OBJ)
+ @echo "Linking $@"
+ @$(SHLD) $(LDSHFLAGS) -o $@ $(RPC_SVC_OBJ) -lc \
+ @SONAMEFLAG@`basename $@`
+
+bin/librpc_wkssvc.@SHLIBEXT@: $(RPC_WKS_OBJ)
+ @echo "Linking $@"
+ @$(SHLD) $(LDSHFLAGS) -o $@ $(RPC_WKS_OBJ) -lc \
+ @SONAMEFLAG@`basename $@`
+
+bin/librpc_NETLOGON.@SHLIBEXT@: $(RPC_NETLOG_OBJ)
+ @echo "Linking $@"
+ @$(SHLD) $(LDSHFLAGS) -o $@ $(RPC_NETLOG_OBJ) -lc \
+ @SONAMEFLAG@`basename $@`
+
+bin/librpc_winreg.@SHLIBEXT@: $(RPC_REG_OBJ)
+ @echo "Linking $@"
+ @$(SHLD) $(LDSHFLAGS) -o $@ $(RPC_REG_OBJ) -lc \
+ @SONAMEFLAG@`basename $@`
+
+bin/librpc_lsa_ds.@SHLIBEXT@: $(RPC_LSA_DS_OBJ)
+ @echo "Linking $@"
+ @$(SHLD) $(LDSHFLAGS) -o $@ $(RPC_LSA_DS_OBJ) -lc \
+ @SONAMEFLAG@`basename $@`
+
+bin/librpc_spoolss.@SHLIBEXT@: $(RPC_SPOOLSS_OBJ)
+ @echo "Linking $@"
+ @$(SHLD) $(LDSHFLAGS) -o $@ $(RPC_SPOOLSS_OBJ) -lc \
+ @SONAMEFLAG@`basename $@`
+
+bin/librpc_netdfs.@SHLIBEXT@: $(RPC_DFS_OBJ)
+ @echo "Linking $@"
+ @$(SHLD) $(LDSHFLAGS) -o $@ $(RPC_DFS_OBJ) -lc \
+ @SONAMEFLAG@`basename $@`
+
+bin/librpc_echo.@SHLIBEXT@: $(RPC_ECHO_OBJ)
+ @echo "Linking $@"
+ @$(SHLD) $(LDSHFLAGS) -o $@ $(RPC_ECHO_OBJ) -lc \
+ @SONAMEFLAG@`basename $@`
+
+bin/librpc_epmapper.@SHLIBEXT@: $(RPC_EPMAPPER_OBJ)
+ @echo "Linking $@"
+ @$(SHLD) $(LDSHFLAGS) -o $@ $(RPC_EPMAPPER_OBJ) -lc \
+ @SONAMEFLAG@`basename $@`
+
+bin/winbindd@EXEEXT@: $(WINBINDD_OBJ) @BUILD_POPT@ bin/.dummy
+ @echo "Linking $@"
+ @$(LINK) -o $@ $(WINBINDD_OBJ) $(DYNEXP) $(LIBS) @POPTLIBS@ $(KRB5LIBS) $(LDAP_LIBS) $(PASSDB_LIBS)
+
+# Please don't add .o files to libnss_winbind, libnss_wins, or the pam_winbind
+# libraries. Add to the appropriate PICOBJ variable instead.
+
+@WINBIND_NSS@: $(WINBIND_NSS_PICOBJS)
+ @echo "Linking $@"
+ @$(SHLD) $(WINBIND_NSS_LDSHFLAGS) -o $@ $(WINBIND_NSS_PICOBJS) \
+ @WINBIND_NSS_EXTRA_LIBS@ @SONAMEFLAG@`basename $@`
+
+@WINBIND_WINS_NSS@: $(WINBIND_WINS_NSS_PICOBJS)
+ @echo "Linking $@"
+ @$(SHLD) $(LDSHFLAGS) -o $@ $(WINBIND_WINS_NSS_PICOBJS) \
+ $(LDAP_LIBS) $(KRB5LIBS) -lc \
+ @SONAMEFLAG@`basename $@`
+
+nsswitch/pam_winbind.@SHLIBEXT@: $(PAM_WINBIND_PICOBJ) bin/.dummy
+ @echo "Linking $@"
+ @$(SHLD) $(LDSHFLAGS) -o $@ $(PAM_WINBIND_PICOBJ) \
+ @SONAMEFLAG@`basename $@` -lpam
+
+bin/rhosts.@SHLIBEXT@: $(AUTH_RHOSTS_OBJ:.o=.@PICSUFFIX@)
+ @echo "Building plugin $@"
+ @$(SHLD) $(LDSHFLAGS) -o $@ $(AUTH_RHOSTS_OBJ:.o=.@PICSUFFIX@) @SONAMEFLAG@`basename $@`
+
+bin/builtin.@SHLIBEXT@: $(AUTH_BUILTIN_OBJ:.o=.@PICSUFFIX@)
+ @echo "Building plugin $@"
+ @$(SHLD) $(LDSHFLAGS) -o $@ $(AUTH_BUILTIN_OBJ:.o=.@PICSUFFIX@) @SONAMEFLAG@`basename $@`
+
+bin/domain.@SHLIBEXT@: $(AUTH_DOMAIN_OBJ:.o=.@PICSUFFIX@)
+ @echo "Building plugin $@"
+ @$(SHLD) $(LDSHFLAGS) -o $@ $(AUTH_DOMAIN_OBJ:.o=.@PICSUFFIX@) @SONAMEFLAG@`basename $@`
+
+bin/smbserver.@SHLIBEXT@: $(AUTH_SERVER_OBJ:.o=.@PICSUFFIX@)
+ @echo "Building plugin $@"
+ @$(SHLD) $(LDSHFLAGS) -o $@ $(AUTH_SERVER_OBJ:.o=.@PICSUFFIX@) @SONAMEFLAG@`basename $@`
+
+bin/winbind.@SHLIBEXT@: $(AUTH_WINBIND_OBJ:.o=.@PICSUFFIX@)
+ @echo "Building plugin $@"
+ @$(SHLD) $(LDSHFLAGS) -o $@ $(AUTH_WINBIND_OBJ:.o=.@PICSUFFIX@) @SONAMEFLAG@`basename $@`
+
+bin/unix.@SHLIBEXT@: $(AUTH_UNIX_OBJ:.o=.@PICSUFFIX@)
+ @echo "Building plugin $@"
+ @$(SHLD) $(LDSHFLAGS) -o $@ $(AUTH_UNIX_OBJ:.o=.@PICSUFFIX@) @SONAMEFLAG@`basename $@`
+
+bin/sam.@SHLIBEXT@: $(AUTH_SAM_OBJ:.o=.@PICSUFFIX@)
+ @echo "Building plugin $@"
+ @$(SHLD) $(LDSHFLAGS) -o $@ $(AUTH_SAM_OBJ:.o=.@PICSUFFIX@) @SONAMEFLAG@`basename $@`
+
+bin/mysql.@SHLIBEXT@: $(MYSQL_OBJ:.o=.@PICSUFFIX@)
+ @echo "Building plugin $@"
+ @$(SHLD) $(LDSHFLAGS) -o $@ $(MYSQL_OBJ:.o=.@PICSUFFIX@) @MYSQL_LIBS@ \
+ @SONAMEFLAG@`basename $@`
+
+bin/pgsql.@SHLIBEXT@: $(PGSQL_OBJ:.o=.@PICSUFFIX@)
+ @echo "Building plugin $@"
+ @$(SHLD) $(LDSHFLAGS) -o $@ $(PGSQL_OBJ:.o=.@PICSUFFIX@) @PGSQL_LIBS@ \
+ @SONAMEFLAG@`basename $@`
+
+bin/ldapsam.@SHLIBEXT@: passdb/pdb_ldap.@PICSUFFIX@
+ @echo "Building plugin $@"
+ @$(SHLD) $(LDSHFLAGS) $(LDAP_LIBS) -o $@ passdb/pdb_ldap.@PICSUFFIX@ \
+ @SONAMEFLAG@`basename $@`
+
+bin/tdbsam.@SHLIBEXT@: passdb/pdb_tdb.@PICSUFFIX@
+ @echo "Building plugin $@"
+ @$(SHLD) $(LDSHFLAGS) -o $@ passdb/pdb_tdb.@PICSUFFIX@ \
+ @SONAMEFLAG@`basename $@`
+
+bin/smbpasswd.@SHLIBEXT@: passdb/pdb_smbpasswd.@PICSUFFIX@
+ @echo "Building plugin $@"
+ @$(SHLD) $(LDSHFLAGS) -o $@ passdb/pdb_smbpasswd.@PICSUFFIX@ \
+ @SONAMEFLAG@`basename $@`
+
+bin/weird.@SHLIBEXT@: $(DEVEL_HELP_WEIRD_OBJ:.o=.@PICSUFFIX@)
+ @echo "Building plugin $@"
+ @$(SHLD) $(LDSHFLAGS) -o $@ $(DEVEL_HELP_WEIRD_OBJ:.o=.@PICSUFFIX@) \
+ @SONAMEFLAG@`basename $@`
+
+bin/CP850.@SHLIBEXT@: $(CP850_OBJ:.o=.@PICSUFFIX@)
+ @echo "Building plugin $@"
+ @$(SHLD) $(LDSHFLAGS) -o $@ $(CP850_OBJ:.o=.@PICSUFFIX@) \
+ @SONAMEFLAG@`basename $@`
+
+bin/CP437.@SHLIBEXT@: $(CP437_OBJ:.o=.@PICSUFFIX@)
+ @echo "Building plugin $@"
+ @$(SHLD) $(LDSHFLAGS) -o $@ $(CP437_OBJ:.o=.@PICSUFFIX@) \
+ @SONAMEFLAG@`basename $@`
+
+bin/macosxfs.@SHLIBEXT@: $(CHARSET_MACOSXFS_OBJ:.o=.@PICSUFFIX@)
+ @echo "Building plugin $@"
+ @$(SHLD) $(LDSHFLAGS) -o $@ $(CHARSET_MACOSXFS_OBJ:.o=.@PICSUFFIX@) \
+ -framework CoreFoundation @SONAMEFLAG@`basename $@`
+
+bin/xml.@SHLIBEXT@: $(XML_OBJ:.o=.@PICSUFFIX@)
+ @echo "Building plugin $@"
+ @$(SHLD) $(LDSHFLAGS) -o $@ $(XML_OBJ:.o=.@PICSUFFIX@) @XML_LIBS@ \
+ @SONAMEFLAG@`basename $@`
+
+bin/audit.@SHLIBEXT@: $(VFS_AUDIT_OBJ:.o=.@PICSUFFIX@)
+ @echo "Building plugin $@"
+ @$(SHLD) $(LDSHFLAGS) -o $@ $(VFS_AUDIT_OBJ:.o=.@PICSUFFIX@) \
+ @SONAMEFLAG@`basename $@`
+
+bin/extd_audit.@SHLIBEXT@: $(VFS_EXTD_AUDIT_OBJ:.o=.@PICSUFFIX@)
+ @echo "Building plugin $@"
+ @$(SHLD) $(LDSHFLAGS) -o $@ $(VFS_EXTD_AUDIT_OBJ:.o=.@PICSUFFIX@) \
+ @SONAMEFLAG@`basename $@`
+
+bin/recycle.@SHLIBEXT@: $(VFS_RECYCLE_OBJ:.o=.@PICSUFFIX@)
+ @echo "Building plugin $@"
+ @$(SHLD) $(LDSHFLAGS) -o $@ $(VFS_RECYCLE_OBJ:.o=.@PICSUFFIX@) \
+ @SONAMEFLAG@`basename $@`
+
+bin/netatalk.@SHLIBEXT@: $(VFS_NETATALK_OBJ:.o=.@PICSUFFIX@)
+ @echo "Building plugin $@"
+ @$(SHLD) $(LDSHFLAGS) -o $@ $(VFS_NETATALK_OBJ:.o=.@PICSUFFIX@) \
+ @SONAMEFLAG@`basename $@`
+
+bin/fake_perms.@SHLIBEXT@: $(VFS_FAKE_PERMS_OBJ:.o=.@PICSUFFIX@)
+ @echo "Building plugin $@"
+ @$(SHLD) $(LDSHFLAGS) -o $@ $(VFS_FAKE_PERMS_OBJ:.o=.@PICSUFFIX@) \
+ @SONAMEFLAG@`basename $@`
+
+bin/default_quota.@SHLIBEXT@: $(VFS_DEFAULT_QUOTA_OBJ:.o=.@PICSUFFIX@)
+ @echo "Building plugin $@"
+ @$(SHLD) $(LDSHFLAGS) -o $@ $(VFS_DEFAULT_QUOTA_OBJ:.o=.@PICSUFFIX@) \
+ @SONAMEFLAG@`basename $@`
+
+bin/readonly.@SHLIBEXT@: $(VFS_READONLY_OBJ:.o=.@PICSUFFIX@)
+ @echo "Building plugin $@"
+ @$(SHLD) $(LDSHFLAGS) -o $@ $(VFS_READONLY_OBJ:.o=.@PICSUFFIX@) \
+ @SONAMEFLAG@`basename $@`
+
+bin/cap.@SHLIBEXT@: $(VFS_CAP_OBJ:.o=.@PICSUFFIX@)
+ @echo "Building plugin $@"
+ @$(SHLD) $(LDSHFLAGS) -o $@ $(VFS_CAP_OBJ:.o=.@PICSUFFIX@) \
+ @SONAMEFLAG@`basename $@`
+
+bin/expand_msdfs.@SHLIBEXT@: $(VFS_EXPAND_MSDFS_OBJ:.o=.@PICSUFFIX@)
+ @echo "Building plugin $@"
+ @$(SHLD) $(LDSHFLAGS) -o $@ $(VFS_EXPAND_MSDFS_OBJ:.o=.@PICSUFFIX@) \
+ @SONAMEFLAG@`basename $@`
+
+bin/config_ldap.@SHLIBEXT@: $(CONFIG_LDAP_OBJ:.o=.@PICSUFFIX@)
+ @echo "Building plugin $@"
+ @$(SHLD) $(LDSHFLAGS) -o $@ $(CONFIG_LDAP_OBJ:.o=.@PICSUFFIX@) \
+ @SMBLDAP@ @LDAP_LIBS@ @SONAMEFLAG@`basename $@`
+
+bin/wbinfo@EXEEXT@: $(WBINFO_OBJ) @BUILD_POPT@ bin/.dummy
+ @echo Linking $@
+ @$(LINK) -o $@ $(WBINFO_OBJ) $(LIBS) @POPTLIBS@ -lcrypto
+
+bin/ntlm_auth@EXEEXT@: $(NTLM_AUTH_OBJ) $(PARAM_OBJ) $(LIB_NONSMBD_OBJ) \
+ $(UBIQX_OBJ) @BUILD_POPT@ bin/.dummy
+ @echo Linking $@
+ @$(LINK) -o $@ $(NTLM_AUTH_OBJ) $(PARAM_OBJ) $(LIB_NONSMBD_OBJ) \
+ $(UBIQX_OBJ) $(LIBS) @POPTLIBS@ $(KRB5LIBS) $(LDAP_LIBS)
+
+bin/pam_smbpass.@SHLIBEXT@: $(PAM_SMBPASS_PICOOBJ)
+ @echo "Linking shared library $@"
+ @$(SHLD) $(LDSHFLAGS) -o $@ $(PAM_SMBPASS_PICOOBJ) -lpam $(DYNEXP) $(LIBS) -lc $(LDAP_LIBS) $(KRB5LIBS)
+
+bin/libmsrpc.a: $(LIBMSRPC_PICOBJ)
+ @-$(AR) -rc $@ $(LIBMSRPC_PICOBJ)
+
+bin/tdbbackup@EXEEXT@: $(TDBBACKUP_OBJ) bin/.dummy
+ @echo Linking $@
+ @$(CC) $(FLAGS) -o $@ $(TDBBACKUP_OBJ)
+
+bin/tdbdump@EXEEXT@: $(TDBDUMP_OBJ) bin/.dummy
+ @echo Linking $@
+ @$(CC) $(FLAGS) -o $@ $(TDBDUMP_OBJ)
+
+bin/t_strcmp@EXEEXT@: bin/libbigballofmud.@SHLIBEXT@ torture/t_strcmp.o
+ $(CC) $(FLAGS) -o $@ $(LIBS) torture/t_strcmp.o -L ./bin -lbigballofmud
+
+bin/t_strstr@EXEEXT@: bin/libbigballofmud.@SHLIBEXT@ torture/t_strstr.o
+ $(CC) $(FLAGS) -o $@ $(LIBS) torture/t_strstr.o -L ./bin -lbigballofmud
+
+bin/t_stringoverflow@EXEEXT@: bin/libbigballofmud.@SHLIBEXT@ torture/t_stringoverflow.o
+ $(CC) $(FLAGS) -o $@ torture/t_stringoverflow.o -L./bin -lbigballofmud
+
+bin/t_doschar@EXEEXT@: bin/libbigballofmud.@SHLIBEXT@ torture/t_doschar.o
+ $(CC) $(FLAGS) -o $@ $(LIBS) torture/t_doschar.o -L ./bin -lbigballofmud
+bin/t_push_ucs2@EXEEXT@: bin/libbigballofmud.@SHLIBEXT@ torture/t_push_ucs2.o
+ $(CC) $(FLAGS) -o $@ $(LIBS) torture/t_push_ucs2.o -L ./bin -lbigballofmud
+
+bin/t_snprintf@EXEEXT@: lib/snprintf.c
+ $(CC) $(FLAGS) -o $@ -DTEST_SNPRINTF lib/snprintf.c -lm
+install: installbin installman installscripts installdat installswat installmodules @INSTALLCLIENT@
+
+install-everything: install installmodules
+
+# DESTDIR is used here to prevent packagers wasting their time
+# duplicating the Makefile. Remove it and you will have the privelege
+# of package each samba release for muliple 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)$(LIBDIR) $(DESTDIR)$(VARDIR) $(DESTDIR)$(PRIVATEDIR) $(DESTDIR)$(PIDDIR) $(DESTDIR)$(LOCKDIR) $(DESTDIR)$(MANDIR)
+
+installservers: all installdirs
+ @$(SHELL) $(srcdir)/script/installbin.sh $(INSTALLPERMS) $(DESTDIR)$(BASEDIR) $(DESTDIR)$(SBINDIR) $(DESTDIR)$(LIBDIR) $(DESTDIR)$(VARDIR) $(SBIN_PROGS)
+
+installbin: all installdirs
+ @$(SHELL) $(srcdir)/script/installbin.sh $(INSTALLPERMS) $(DESTDIR) $(DESTDIR)$(BASEDIR) $(DESTDIR)$(SBINDIR) $(DESTDIR)$(LIBDIR) $(DESTDIR)$(VARDIR) $(SBIN_PROGS)
+ @$(SHELL) $(srcdir)/script/installbin.sh $(INSTALLPERMS) $(DESTDIR) $(DESTDIR)$(BASEDIR) $(DESTDIR)$(BINDIR) $(DESTDIR)$(LIBDIR) $(DESTDIR)$(VARDIR) $(BIN_PROGS)
+
+
+# Some symlinks are required for the 'probing' of modules.
+# This mechanism should go at some point..
+installmodules: modules installdirs
+ @$(SHELL) $(srcdir)/script/installmodules.sh $(INSTALLPERMS) $(DESTDIR)$(BASEDIR) $(DESTDIR)$(VFSLIBDIR) $(VFS_MODULES)
+ @$(SHELL) $(srcdir)/script/installmodules.sh $(INSTALLPERMS) $(DESTDIR)$(BASEDIR) $(DESTDIR)$(PDBLIBDIR) $(PDB_MODULES)
+ @$(SHELL) $(srcdir)/script/installmodules.sh $(INSTALLPERMS) $(DESTDIR)$(BASEDIR) $(DESTDIR)$(RPCLIBDIR) $(RPC_MODULES)
+ @$(SHELL) $(srcdir)/script/installmodules.sh $(INSTALLPERMS) $(DESTDIR)$(BASEDIR) $(DESTDIR)$(IDMAPLIBDIR) $(IDMAP_MODULES)
+ @$(SHELL) $(srcdir)/script/installmodules.sh $(INSTALLPERMS) $(DESTDIR)$(BASEDIR) $(DESTDIR)$(CHARSETLIBDIR) $(CHARSET_MODULES)
+ @$(SHELL) $(srcdir)/script/installmodules.sh $(INSTALLPERMS) $(DESTDIR)$(BASEDIR) $(DESTDIR)$(AUTHLIBDIR) $(AUTH_MODULES)
+ @$(SHELL) $(srcdir)/script/linkmodules.sh $(DESTDIR)$(PDBLIBDIR) ldapsam.@SHLIBEXT@ ldapsam_compat.@SHLIBEXT@
+ @$(SHELL) $(srcdir)/script/linkmodules.sh $(DESTDIR)$(AUTHLIBDIR) rhosts.@SHLIBEXT@ hostsequiv.@SHLIBEXT@
+ @$(SHELL) $(srcdir)/script/linkmodules.sh $(DESTDIR)$(AUTHLIBDIR) sam.@SHLIBEXT@ sam_ignoredomain.@SHLIBEXT@
+ @$(SHELL) $(srcdir)/script/linkmodules.sh $(DESTDIR)$(AUTHLIBDIR) domain.@SHLIBEXT@ trustdomain.@SHLIBEXT@ ntdomain.@SHLIBEXT@
+ @$(SHELL) $(srcdir)/script/linkmodules.sh $(DESTDIR)$(AUTHLIBDIR) builtin.@SHLIBEXT@ guest.@SHLIBEXT@ fixed_challenge.@SHLIBEXT@ name_to_ntstatus.@SHLIBEXT@
+
+installscripts: installdirs
+ @$(SHELL) $(srcdir)/script/installscripts.sh $(INSTALLPERMS) $(DESTDIR)$(BINDIR) $(SCRIPTS)
+
+installdat: installdirs
+ @$(SHELL) $(srcdir)/script/installdat.sh $(DESTDIR)$(LIBDIR) $(srcdir)
+
+installmsg: installdirs
+ @$(SHELL) $(srcdir)/script/installmsg.sh $(DESTDIR)$(LIBDIR) $(srcdir)
+
+installswat: installdirs installmsg
+ @$(SHELL) $(srcdir)/script/installswat.sh $(DESTDIR)$(SWATDIR) $(srcdir)
+
+installclientlib: installdirs libsmbclient
+ @$(SHELL) $(srcdir)/script/installdirs.sh $(DESTDIR)${prefix}/lib
+ -$(INSTALLCLIENTCMD_SH) bin/libsmbclient.@SHLIBEXT@ $(DESTDIR)${prefix}/lib
+ -$(INSTALLCLIENTCMD_A) bin/libsmbclient.a $(DESTDIR)${prefix}/lib
+ @$(SHELL) $(srcdir)/script/installdirs.sh $(DESTDIR)${prefix}/include
+ -$(INSTALLCMD) $(srcdir)/include/libsmbclient.h $(DESTDIR)${prefix}/include
+
+# Python extensions
+
+PYTHON_OBJS = $(PARAM_OBJ) $(LIB_NONSMBD_OBJ) $(LIBSMB_OBJ) $(RPC_PARSE_OBJ) \
+ $(UBIQX_OBJ) $(LIBMSRPC_OBJ) $(PASSDB_OBJ) $(GROUPDB_OBJ) \
+ $(SECRETS_OBJ) $(KRBCLIENT_OBJ) $(SMBLDAP_OBJ) lib/dummyroot.o
+
+PYTHON_PICOBJS = $(PYTHON_OBJS:.o=.@PICSUFFIX@)
+
+python_ext: $(PYTHON_PICOBJS)
+ @if test -z "$(PYTHON)"; then \
+ echo Use the option --with-python to configure python; \
+ exit 1; \
+ fi
+ PYTHON_OBJS="$(PYTHON_PICOBJS)" \
+ PYTHON_CFLAGS="$(CFLAGS) $(CPPFLAGS) $(FLAGS)" \
+ LIBS="$(LDFLAGS) $(LIBS) $(PASSDB_LIBS) $(IDMAP_LIBS) $(KRB5LIBS) $(LDAP_LIBS)" \
+ $(PYTHON) python/setup.py build
+
+python_install: $(PYTHON_PICOBJS)
+ @if test -z "$(PYTHON)"; then \
+ echo Use the option --with-python to configure python; \
+ exit 1; \
+ fi
+ PYTHON_OBJS="$(PYTHON_PICOBJS)" \
+ PYTHON_CFLAGS="$(CFLAGS) $(CPPFLAGS)" \
+ LIBS="$(LDFLAGS) $(LIBS)" \
+ $(PYTHON) python/setup.py install
+
+python_clean:
+ @-if test -n "$(PYTHON)"; then $(PYTHON) python/setup.py clean; fi
+
+# revert to the previously installed version
+revert:
+ @$(SHELL) $(srcdir)/script/revert.sh $(SBINDIR) $(SBIN_PROGS)
+ @$(SHELL) $(srcdir)/script/revert.sh $(BINDIR) $(BIN_PROGS) $(SCRIPTS)
+
+installman: installdirs
+ @$(SHELL) $(srcdir)/script/installman.sh $(DESTDIR)$(MANDIR) $(srcdir) $(man_langs) "@ROFF@"
+
+.PHONY: showlayout
+
+showlayout:
+ @echo "Samba will be installed into:"
+ @echo " basedir: $(BASEDIR)"
+ @echo " bindir: $(BINDIR)"
+ @echo " sbindir: $(SBINDIR)"
+ @echo " libdir: $(LIBDIR)"
+ @echo " vardir: $(VARDIR)"
+ @echo " mandir: $(MANDIR)"
+ @echo " privatedir: $(PRIVATE_DIR)"
+ @echo " configdir: $(CONFIGDIR)"
+ @echo " lockdir: $(LOCKDIR)"
+ @echo " piddir: $(PIDDIR)"
+ @echo " swatdir: $(SWATDIR)"
+
+
+uninstall: uninstallman uninstallbin uninstallscripts uninstallmodules
+
+uninstallman:
+ @$(SHELL) $(srcdir)/script/uninstallman.sh $(DESTDIR)$(MANDIR) $(srcdir) $(man_langs)
+
+uninstallbin:
+ @$(SHELL) $(srcdir)/script/uninstallbin.sh $(INSTALLPERMS) $(DESTDIR)$(BASEDIR) $(DESTDIR)$(SBINDIR) $(DESTDIR)$(LIBDIR) $(DESTDIR)$(VARDIR) $(DESTDIR)$(SBIN_PROGS)
+ @$(SHELL) $(srcdir)/script/uninstallbin.sh $(INSTALLPERMS) $(DESTDIR)$(BASEDIR) $(DESTDIR)$(BINDIR) $(DESTDIR)$(LIBDIR) $(DESTDIR)$(VARDIR) $(DESTDIR)$(BIN_PROGS)
+
+uninstallmodules:
+ @$(SHELL) $(srcdir)/script/uninstallmodules.sh $(INSTALLPERMS) $(DESTDIR)$(BASEDIR) $(DESTDIR)$(VFSLIBDIR) $(DESTDIR)$(VFS_MODULES)
+ @$(SHELL) $(srcdir)/script/uninstallmodules.sh $(INSTALLPERMS) $(DESTDIR)$(BASEDIR) $(DESTDIR)$(PDBLIBDIR) $(DESTDIR)$(PDB_MODULES)
+ @$(SHELL) $(srcdir)/script/uninstallmodules.sh $(INSTALLPERMS) $(DESTDIR)$(BASEDIR) $(DESTDIR)$(RPCLIBDIR) $(DESTDIR)$(RPC_MODULES)
+ @$(SHELL) $(srcdir)/script/uninstallmodules.sh $(INSTALLPERMS) $(DESTDIR)$(BASEDIR) $(DESTDIR)$(CHARSETLIBDIR) $(DESTDIR)$(CHARSET_MODULES)
+ @$(SHELL) $(srcdir)/script/uninstallmodules.sh $(INSTALLPERMS) $(DESTDIR)$(BASEDIR) $(DESTDIR)$(AUTHLIBDIR) $(DESTDIR)$(AUTH_MODULES)
+
+uninstallscripts:
+ @$(SHELL) $(srcdir)/script/uninstallscripts.sh $(INSTALLPERMS) $(DESTDIR)$(BINDIR) $(SCRIPTS)
+
+# Toplevel clean files
+TOPFILES=dynconfig.o dynconfig.@PICSUFFIX@
+
+clean: delheaders python_clean
+ -rm -f core */*~ *~ */*.o */*.@PICSUFFIX@ */*.@SHLIBEXT@ \
+ $(TOPFILES) $(BIN_PROGS) $(SBIN_PROGS) $(MODULES) $(TORTURE_PROGS) \
+ $(LIBSMBCLIENT) $(EVERYTHING_PROGS) .headers.stamp
+
+# Making this target will just make sure that the prototype files
+# exist, not necessarily that they are up to date. Since they're
+# removed by "make clean" this will always be run when you do anything
+# afterwards.
+proto_exists: include/proto.h include/wrepld_proto.h include/build_env.h \
+ nsswitch/winbindd_proto.h web/swat_proto.h \
+ client/client_proto.h utils/net_proto.h smbd/build_options.c
+
+delheaders:
+ @echo Removing prototype headers
+ @rm -f include/proto.h include/build_env.h include/wrepld_proto.h \
+ nsswitch/winbindd_proto.h web/swat_proto.h \
+ client/client_proto.h utils/net_proto.h \
+ smbd/build_options.c
+
+MKPROTO_SH = $(srcdir)/script/mkproto.sh
+
+include/proto.h: smbd/build_options.c
+ @echo Building include/proto.h
+ @cd $(srcdir) && $(SHELL) $(MKPROTO_SH) $(AWK) \
+ -h _PROTO_H_ $(builddir)/include/proto.h \
+ $(PROTO_OBJ)
+
+include/build_env.h: script/build_env.sh
+ @echo Building include/build_env.h
+ @$(SHELL) $(srcdir)/script/build_env.sh $(srcdir) $(builddir) $(CC) \
+ > $(builddir)/include/build_env.h
+
+include/wrepld_proto.h:
+ @echo Building include/wrepld_proto.h
+ @cd $(srcdir) && $(SHELL) $(MKPROTO_SH) $(AWK) \
+ -h _WREPLD_PROTO_H_ $(builddir)/include/wrepld_proto.h \
+ $(WREPL_OBJ1)
+
+nsswitch/winbindd_proto.h:
+ @cd $(srcdir) && $(SHELL) $(MKPROTO_SH) $(AWK) \
+ -h _WINBINDD_PROTO_H_ $(builddir)/nsswitch/winbindd_proto.h \
+ $(WINBINDD_OBJ1)
+
+web/swat_proto.h:
+ @cd $(srcdir) && $(SHELL) $(MKPROTO_SH) $(AWK) \
+ -h _SWAT_PROTO_H_ $(builddir)/web/swat_proto.h \
+ $(SWAT_OBJ1)
+
+client/client_proto.h:
+ @cd $(srcdir) && $(SHELL) $(MKPROTO_SH) $(AWK) \
+ -h _CLIENT_PROTO_H_ $(builddir)/client/client_proto.h \
+ $(CLIENT_OBJ1)
+
+utils/net_proto.h:
+ @cd $(srcdir) && $(SHELL) $(MKPROTO_SH) $(AWK) \
+ -h _NET_PROTO_H_ $(builddir)/utils/net_proto.h \
+ $(NET_OBJ1)
+
+include/tdbsam2_parse_info.h:
+ @if test -n "$(PERL)"; then \
+ cd $(srcdir) && @PERL@ -w script/genstruct.pl \
+ -o include/tdbsam2_parse_info.h $(CC) -E -O2 -g \
+ include/gums.h; \
+ else \
+ echo Unable to build $@, continuing; \
+ fi
+
+# "make headers" or "make proto" calls a subshell because we need to
+# make sure these commands are executed in sequence even for a
+# parallel make.
+headers:
+ $(MAKE) delheaders; \
+ $(MAKE) smbd/build_options.c; \
+ $(MAKE) include/proto.h; \
+ $(MAKE) include/build_env.h; \
+ $(MAKE) include/wrepld_proto.h; \
+ $(MAKE) nsswitch/winbindd_proto.h; \
+ $(MAKE) web/swat_proto.h; \
+ $(MAKE) client/client_proto.h; \
+ $(MAKE) utils/net_proto.h;
+
+prebuiltheaders:
+ $(MAKE) include/tdbsam2_parse_info.h
+
+genparse: prebuiltheaders
+
+proto: headers
+
+.PHONY: headers proto
+
+etags:
+ etags `find $(srcdir) -name "*.[ch]" | grep -v /CVS/`
+
+ctags:
+ ctags `find $(srcdir) -name "*.[ch]" | grep -v /CVS/`
+
+realclean: clean delheaders
+ -rm -f config.log bin/.dummy script/findsmb
+
+distclean: realclean
+ -rm -f include/stamp-h
+ -rm -f include/config.h Makefile
+ -rm -f config.status config.cache so_locations
+ -rm -rf .deps TAGS
+
+realdistclean: distclean
+ -rm -f include/config.h.in
+ -rm -f include/version.h
+ -rm -f configure
+
+# this target is really just for my use. It only works on a limited
+# range of machines and is used to produce a list of potentially
+# dead (ie. unused) functions in the code. (tridge)
+finddead:
+ nm */*.o |grep 'U ' | awk '{print $$2}' | sort -u > nmused.txt
+ nm */*.o |grep 'T ' | awk '{print $$3}' | sort -u > nmfns.txt
+ comm -13 nmused.txt nmfns.txt
+
+
+# when configure.in is updated, reconfigure
+$(srcdir)/configure: $(srcdir)/configure.in
+ @echo "WARNING: you need to rerun ./autogen.sh"
+
+config.status: $(srcdir)/configure
+ @echo "WARNING: you need to run ./configure"
+
+Makefile: $(srcdir)/Makefile.in config.status
+ @echo "WARNING: you need to run ./config.status"
+
+######################################################################
+# Samba Testing Framework
+
+# FIXME: LD_LIBRARY_PATH is not portable, but in the absence of
+# libtool I don't know a better way to do it. Perhaps we should fix
+# libbigballofmud to link statically?
+
+check: check-programs
+ LD_LIBRARY_PATH="`pwd`/bin:$$LD_LIBRARY_PATH" \
+ PATH="`pwd`/bin:$$PATH" \
+ python stf/standardcheck.py; \
+ if test -n "$(PYTHON)"; then \
+ python stf/pythoncheck.py; \
+ fi
+
+# These are called by the test suite and need to be built before
+# running it. For the time being we don't build all of BIN_PROGS,
+# because they're not all needed.
+check-programs: bin/t_strcmp bin/t_strstr bin/t_push_ucs2 bin/smbcontrol bin/t_snprintf
diff --git a/source/VERSION b/source/VERSION
new file mode 100644
index 00000000000..d7f386ab42b
--- /dev/null
+++ b/source/VERSION
@@ -0,0 +1,123 @@
+########################################################
+# SAMBA Version #
+# #
+# 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=3
+SAMBA_VERSION_MINOR=1
+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_PRE_RELEASE=a #
+# -> "2.2.8a" #
+########################################################
+SAMBA_VERSION_REVISION=
+
+########################################################
+# 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=
+
+########################################################
+# For 'beta' releases the version will be #
+# #
+# <MAJOR>.<MINOR>.<RELEASE>beta<BETA_RELEASE> #
+# #
+# e.g. SAMBA_VERSION_BETA_RELEASE=3 #
+# -> "3.0.0beta3" #
+########################################################
+SAMBA_VERSION_BETA_RELEASE=
+
+########################################################
+# For 'alpha' releases the version will be #
+# #
+# <MAJOR>.<MINOR>.<RELEASE>alpha<ALPHA_RELEASE> #
+# #
+# e.g. SAMBA_VERSION_ALPHA_RELEASE=1 #
+# -> "4.0.0alpha1" #
+########################################################
+SAMBA_VERSION_ALPHA_RELEASE=1
+
+########################################################
+# For 'test' releases the version will be #
+# #
+# <MAJOR>.<MINOR>.<RELEASE>test<TEST_RELEASE> #
+# #
+# e.g. SAMBA_VERSION_TEST_RELEASE=1 #
+# -> "4.0.0test1" #
+########################################################
+SAMBA_VERSION_TEST_RELEASE=
+
+########################################################
+# To mark CVS 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>[...]cvs #
+# #
+# e.g. SAMBA_VERSION_IS_CVS_SNAPSHOT=yes #
+# -> "CVS 3.0.0rc2" #
+########################################################
+SAMBA_VERSION_IS_CVS_SNAPSHOT=yes
+
+########################################################
+# 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" #
+# -> "CVS 3.0.0rc2-VendorVersion" #
+# #
+# Note: If you want to use a function, #
+# then patch lib/version.c and add this function #
+# there, because the symbol must be available in #
+# binary. #
+# #
+# const char *vendor_version(void) #
+# { #
+# return "VendorVersion"; #
+# } #
+# #
+# e.g. SAMBA_VERSION_VENDOR_SUFFIX=vendor_version() #
+# -> "CVS 3.0.0rc2-VendorVersion" #
+########################################################
+SAMBA_VERSION_VENDOR_SUFFIX=
diff --git a/source/aclocal.m4 b/source/aclocal.m4
new file mode 100644
index 00000000000..6de11953b44
--- /dev/null
+++ b/source/aclocal.m4
@@ -0,0 +1,775 @@
+dnl AC_VALIDATE_CACHE_SYSTEM_TYPE[(cmd)]
+dnl if the cache file is inconsistent with the current host,
+dnl target and build system types, execute CMD or print a default
+dnl error message.
+AC_DEFUN(AC_VALIDATE_CACHE_SYSTEM_TYPE, [
+ AC_REQUIRE([AC_CANONICAL_SYSTEM])
+ AC_MSG_CHECKING([config.cache system type])
+ if { test x"${ac_cv_host_system_type+set}" = x"set" &&
+ test x"$ac_cv_host_system_type" != x"$host"; } ||
+ { test x"${ac_cv_build_system_type+set}" = x"set" &&
+ test x"$ac_cv_build_system_type" != x"$build"; } ||
+ { test x"${ac_cv_target_system_type+set}" = x"set" &&
+ test x"$ac_cv_target_system_type" != x"$target"; }; then
+ AC_MSG_RESULT([different])
+ ifelse($#, 1, [$1],
+ [AC_MSG_ERROR(["you must remove config.cache and restart configure"])])
+ else
+ AC_MSG_RESULT([same])
+ fi
+ ac_cv_host_system_type="$host"
+ ac_cv_build_system_type="$build"
+ ac_cv_target_system_type="$target"
+])
+
+
+dnl test whether dirent has a d_off member
+AC_DEFUN(AC_DIRENT_D_OFF,
+[AC_CACHE_CHECK([for d_off in dirent], ac_cv_dirent_d_off,
+[AC_TRY_COMPILE([
+#include <unistd.h>
+#include <sys/types.h>
+#include <dirent.h>], [struct dirent d; d.d_off;],
+ac_cv_dirent_d_off=yes, ac_cv_dirent_d_off=no)])
+if test $ac_cv_dirent_d_off = yes; then
+ AC_DEFINE(HAVE_DIRENT_D_OFF,1,[Whether dirent has a d_off member])
+fi
+])
+
+dnl Mark specified module as shared
+dnl SMB_MODULE(name,static_files,shared_files,subsystem,whatif-static,whatif-shared)
+AC_DEFUN(SMB_MODULE,
+[
+ AC_MSG_CHECKING([how to build $1])
+ if test "$[MODULE_][$1]"; then
+ DEST=$[MODULE_][$1]
+ elif test "$[MODULE_]translit([$4], [A-Z], [a-z])" -a "$[MODULE_DEFAULT_][$1]"; then
+ DEST=$[MODULE_]translit([$4], [A-Z], [a-z])
+ else
+ DEST=$[MODULE_DEFAULT_][$1]
+ fi
+
+ if test x"$DEST" = xSHARED; then
+ AC_DEFINE([$1][_init], [init_module], [Whether to build $1 as shared module])
+ $4_MODULES="$$4_MODULES $3"
+ AC_MSG_RESULT([shared])
+ [$6]
+ string_shared_modules="$string_shared_modules $1"
+ elif test x"$DEST" = xSTATIC; then
+ [init_static_modules_]translit([$4], [A-Z], [a-z])="$[init_static_modules_]translit([$4], [A-Z], [a-z]) $1_init();"
+ string_static_modules="$string_static_modules $1"
+ $4_STATIC="$$4_STATIC $2"
+ AC_SUBST($4_STATIC)
+ [$5]
+ AC_MSG_RESULT([static])
+ else
+ string_ignored_modules="$string_ignored_modules $1"
+ AC_MSG_RESULT([not])
+ fi
+])
+
+AC_DEFUN(SMB_SUBSYSTEM,
+[
+ AC_SUBST($1_STATIC)
+ AC_SUBST($1_MODULES)
+ AC_DEFINE_UNQUOTED([static_init_]translit([$1], [A-Z], [a-z]), [{$init_static_modules_]translit([$1], [A-Z], [a-z])[}], [Static init functions])
+ ifelse([$2], , :, [rm -f $2])
+])
+
+dnl AC_PROG_CC_FLAG(flag)
+AC_DEFUN(AC_PROG_CC_FLAG,
+[AC_CACHE_CHECK(whether ${CC-cc} accepts -$1, ac_cv_prog_cc_$1,
+[echo 'void f(){}' > conftest.c
+if test -z "`${CC-cc} -$1 -c conftest.c 2>&1`"; then
+ ac_cv_prog_cc_$1=yes
+else
+ ac_cv_prog_cc_$1=no
+fi
+rm -f conftest*
+])])
+
+dnl see if a declaration exists for a function or variable
+dnl defines HAVE_function_DECL if it exists
+dnl AC_HAVE_DECL(var, includes)
+AC_DEFUN(AC_HAVE_DECL,
+[
+ AC_CACHE_CHECK([for $1 declaration],ac_cv_have_$1_decl,[
+ AC_TRY_COMPILE([$2],[int i = (int)$1],
+ ac_cv_have_$1_decl=yes,ac_cv_have_$1_decl=no)])
+ if test x"$ac_cv_have_$1_decl" = x"yes"; then
+ AC_DEFINE([HAVE_]translit([$1], [a-z], [A-Z])[_DECL],1,[Whether $1() is available])
+ fi
+])
+
+
+dnl check for a function in a library, but don't
+dnl keep adding the same library to the LIBS variable.
+dnl AC_LIBTESTFUNC(lib,func)
+AC_DEFUN(AC_LIBTESTFUNC,
+[case "$LIBS" in
+ *-l$1*) AC_CHECK_FUNCS($2) ;;
+ *) AC_CHECK_LIB($1, $2)
+ AC_CHECK_FUNCS($2)
+ ;;
+ esac
+])
+
+# AC_CHECK_LIB_EXT(LIBRARY, [EXT_LIBS], [FUNCTION],
+# [ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND],
+# [ADD-ACTION-IF-FOUND],[OTHER-LIBRARIES])
+# ------------------------------------------------------
+#
+# Use a cache variable name containing both the library and function name,
+# because the test really is for library $1 defining function $3, not
+# just for library $1. Separate tests with the same $1 and different $3s
+# may have different results.
+#
+# Note that using directly AS_VAR_PUSHDEF([ac_Lib], [ac_cv_lib_$1_$3])
+# is asking for trouble, since AC_CHECK_LIB($lib, fun) would give
+# ac_cv_lib_$lib_fun, which is definitely not what was meant. Hence
+# the AS_LITERAL_IF indirection.
+#
+# FIXME: This macro is extremely suspicious. It DEFINEs unconditionally,
+# whatever the FUNCTION, in addition to not being a *S macro. Note
+# that the cache does depend upon the function we are looking for.
+#
+# It is on purpose we used `ac_check_lib_ext_save_LIBS' and not just
+# `ac_save_LIBS': there are many macros which don't want to see `LIBS'
+# changed but still want to use AC_CHECK_LIB_EXT, so they save `LIBS'.
+# And ``ac_save_LIBS' is too tempting a name, so let's leave them some
+# freedom.
+AC_DEFUN([AC_CHECK_LIB_EXT],
+[
+AH_CHECK_LIB_EXT([$1])
+ac_check_lib_ext_save_LIBS=$LIBS
+LIBS="-l$1 $$2 $7 $LIBS"
+AS_LITERAL_IF([$1],
+ [AS_VAR_PUSHDEF([ac_Lib_ext], [ac_cv_lib_ext_$1])],
+ [AS_VAR_PUSHDEF([ac_Lib_ext], [ac_cv_lib_ext_$1''])])dnl
+
+m4_ifval([$3],
+ [
+ AH_CHECK_FUNC_EXT([$3])
+ AS_LITERAL_IF([$1],
+ [AS_VAR_PUSHDEF([ac_Lib_func], [ac_cv_lib_ext_$1_$3])],
+ [AS_VAR_PUSHDEF([ac_Lib_func], [ac_cv_lib_ext_$1''_$3])])dnl
+ AC_CACHE_CHECK([for $3 in -l$1], ac_Lib_func,
+ [AC_TRY_LINK_FUNC($3,
+ [AS_VAR_SET(ac_Lib_func, yes);
+ AS_VAR_SET(ac_Lib_ext, yes)],
+ [AS_VAR_SET(ac_Lib_func, no);
+ AS_VAR_SET(ac_Lib_ext, no)])
+ ])
+ AS_IF([test AS_VAR_GET(ac_Lib_func) = yes],
+ [AC_DEFINE_UNQUOTED(AS_TR_CPP(HAVE_$3))])dnl
+ AS_VAR_POPDEF([ac_Lib_func])dnl
+ ],[
+ AC_CACHE_CHECK([for -l$1], ac_Lib_ext,
+ [AC_TRY_LINK_FUNC([main],
+ [AS_VAR_SET(ac_Lib_ext, yes)],
+ [AS_VAR_SET(ac_Lib_ext, no)])
+ ])
+ ])
+LIBS=$ac_check_lib_ext_save_LIBS
+
+AS_IF([test AS_VAR_GET(ac_Lib_ext) = yes],
+ [m4_default([$4],
+ [AC_DEFINE_UNQUOTED(AS_TR_CPP(HAVE_LIB$1))
+ case "$$2" in
+ *-l$1*)
+ ;;
+ *)
+ $2="-l$1 $$2"
+ ;;
+ esac])
+ [$6]
+ ],
+ [$5])dnl
+AS_VAR_POPDEF([ac_Lib_ext])dnl
+])# AC_CHECK_LIB_EXT
+
+# AH_CHECK_LIB_EXT(LIBNAME)
+# ---------------------
+m4_define([AH_CHECK_LIB_EXT],
+[AH_TEMPLATE(AS_TR_CPP(HAVE_LIB$1),
+ [Define to 1 if you have the `]$1[' library (-l]$1[).])])
+
+# AC_CHECK_FUNCS_EXT(FUNCTION, [ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND])
+# -----------------------------------------------------------------
+dnl check for a function in a $LIBS and $OTHER_LIBS libraries variable.
+dnl AC_CHECK_FUNC_EXT(func,OTHER_LIBS,IF-TRUE,IF-FALSE)
+AC_DEFUN([AC_CHECK_FUNC_EXT],
+[
+ AH_CHECK_FUNC_EXT($1)
+ ac_check_func_ext_save_LIBS=$LIBS
+ LIBS="$2 $LIBS"
+ AS_VAR_PUSHDEF([ac_var], [ac_cv_func_ext_$1])dnl
+ AC_CACHE_CHECK([for $1], ac_var,
+ [AC_LINK_IFELSE([AC_LANG_FUNC_LINK_TRY([$1])],
+ [AS_VAR_SET(ac_var, yes)],
+ [AS_VAR_SET(ac_var, no)])])
+ LIBS=$ac_check_func_ext_save_LIBS
+ AS_IF([test AS_VAR_GET(ac_var) = yes],
+ [AC_DEFINE_UNQUOTED(AS_TR_CPP([HAVE_$1])) $3],
+ [$4])dnl
+AS_VAR_POPDEF([ac_var])dnl
+])# AC_CHECK_FUNC
+
+# AH_CHECK_FUNC_EXT(FUNCNAME)
+# ---------------------
+m4_define([AH_CHECK_FUNC_EXT],
+[AH_TEMPLATE(AS_TR_CPP(HAVE_$1),
+ [Define to 1 if you have the `]$1[' function.])])
+
+dnl Define an AC_DEFINE with ifndef guard.
+dnl AC_N_DEFINE(VARIABLE [, VALUE])
+define(AC_N_DEFINE,
+[cat >> confdefs.h <<\EOF
+[#ifndef] $1
+[#define] $1 ifelse($#, 2, [$2], $#, 3, [$2], 1)
+[#endif]
+EOF
+])
+
+dnl Add an #include
+dnl AC_ADD_INCLUDE(VARIABLE)
+define(AC_ADD_INCLUDE,
+[cat >> confdefs.h <<\EOF
+[#include] $1
+EOF
+])
+
+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])
+])
+
+# Configure paths for LIBXML2
+# Toshio Kuratomi 2001-04-21
+# Adapted from:
+# Configure paths for GLIB
+# Owen Taylor 97-11-3
+
+dnl AM_PATH_XML2([MINIMUM-VERSION, [ACTION-IF-FOUND [, ACTION-IF-NOT-FOUND]]])
+dnl Test for XML, and define XML_CFLAGS and XML_LIBS
+dnl
+AC_DEFUN(AM_PATH_XML2,[
+AC_ARG_WITH(xml-prefix,
+ [ --with-xml-prefix=PFX Prefix where libxml is installed (optional)],
+ xml_config_prefix="$withval", xml_config_prefix="")
+AC_ARG_WITH(xml-exec-prefix,
+ [ --with-xml-exec-prefix=PFX Exec prefix where libxml is installed (optional)],
+ xml_config_exec_prefix="$withval", xml_config_exec_prefix="")
+AC_ARG_ENABLE(xmltest,
+ [ --disable-xmltest Do not try to compile and run a test LIBXML program],,
+ enable_xmltest=yes)
+
+ if test x$xml_config_exec_prefix != x ; then
+ xml_config_args="$xml_config_args --exec-prefix=$xml_config_exec_prefix"
+ if test x${XML2_CONFIG+set} != xset ; then
+ XML2_CONFIG=$xml_config_exec_prefix/bin/xml2-config
+ fi
+ fi
+ if test x$xml_config_prefix != x ; then
+ xml_config_args="$xml_config_args --prefix=$xml_config_prefix"
+ if test x${XML2_CONFIG+set} != xset ; then
+ XML2_CONFIG=$xml_config_prefix/bin/xml2-config
+ fi
+ fi
+
+ AC_PATH_PROG(XML2_CONFIG, xml2-config, no)
+ min_xml_version=ifelse([$1], ,2.0.0,[$1])
+ AC_MSG_CHECKING(for libxml - version >= $min_xml_version)
+ no_xml=""
+ if test "$XML2_CONFIG" = "no" ; then
+ no_xml=yes
+ else
+ XML_CFLAGS=`$XML2_CONFIG $xml_config_args --cflags`
+ XML_LIBS=`$XML2_CONFIG $xml_config_args --libs`
+ xml_config_major_version=`$XML2_CONFIG $xml_config_args --version | \
+ sed 's/\([[0-9]]*\).\([[0-9]]*\).\([[0-9]]*\)/\1/'`
+ xml_config_minor_version=`$XML2_CONFIG $xml_config_args --version | \
+ sed 's/\([[0-9]]*\).\([[0-9]]*\).\([[0-9]]*\)/\2/'`
+ xml_config_micro_version=`$XML2_CONFIG $xml_config_args --version | \
+ sed 's/\([[0-9]]*\).\([[0-9]]*\).\([[0-9]]*\)/\3/'`
+ if test "x$enable_xmltest" = "xyes" ; then
+ ac_save_CFLAGS="$CFLAGS"
+ ac_save_LIBS="$LIBS"
+ CFLAGS="$CFLAGS $XML_CFLAGS"
+ LIBS="$XML_LIBS $LIBS"
+dnl
+dnl Now check if the installed libxml is sufficiently new.
+dnl (Also sanity checks the results of xml2-config to some extent)
+dnl
+ rm -f conf.xmltest
+ AC_TRY_RUN([
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <libxml/xmlversion.h>
+
+int
+main()
+{
+ int xml_major_version, xml_minor_version, xml_micro_version;
+ int major, minor, micro;
+ char *tmp_version;
+
+ system("touch conf.xmltest");
+
+ /* Capture xml2-config output via autoconf/configure variables */
+ /* HP/UX 9 (%@#!) writes to sscanf strings */
+ tmp_version = (char *)strdup("$min_xml_version");
+ if (sscanf(tmp_version, "%d.%d.%d", &major, &minor, &micro) != 3) {
+ printf("%s, bad version string from xml2-config\n", "$min_xml_version");
+ exit(1);
+ }
+ free(tmp_version);
+
+ /* Capture the version information from the header files */
+ tmp_version = (char *)strdup(LIBXML_DOTTED_VERSION);
+ if (sscanf(tmp_version, "%d.%d.%d", &xml_major_version, &xml_minor_version, &xml_micro_version) != 3) {
+ printf("%s, bad version string from libxml includes\n", "LIBXML_DOTTED_VERSION");
+ exit(1);
+ }
+ free(tmp_version);
+
+ /* Compare xml2-config output to the libxml headers */
+ if ((xml_major_version != $xml_config_major_version) ||
+ (xml_minor_version != $xml_config_minor_version) ||
+ (xml_micro_version != $xml_config_micro_version))
+ {
+ printf("*** libxml header files (version %d.%d.%d) do not match\n",
+ xml_major_version, xml_minor_version, xml_micro_version);
+ printf("*** xml2-config (version %d.%d.%d)\n",
+ $xml_config_major_version, $xml_config_minor_version, $xml_config_micro_version);
+ return 1;
+ }
+/* Compare the headers to the library to make sure we match */
+ /* Less than ideal -- doesn't provide us with return value feedback,
+ * only exits if there's a serious mismatch between header and library.
+ */
+ LIBXML_TEST_VERSION;
+
+ /* Test that the library is greater than our minimum version */
+ if ((xml_major_version > major) ||
+ ((xml_major_version == major) && (xml_minor_version > minor)) ||
+ ((xml_major_version == major) && (xml_minor_version == minor) &&
+ (xml_micro_version >= micro)))
+ {
+ return 0;
+ }
+ else
+ {
+ printf("\n*** An old version of libxml (%d.%d.%d) was found.\n",
+ xml_major_version, xml_minor_version, xml_micro_version);
+ printf("*** You need a version of libxml newer than %d.%d.%d. The latest version of\n",
+ major, minor, micro);
+ printf("*** libxml is always available from ftp://ftp.xmlsoft.org.\n");
+ printf("***\n");
+ printf("*** If you have already installed a sufficiently new version, this error\n");
+ printf("*** probably means that the wrong copy of the xml2-config shell script is\n");
+ printf("*** being found. The easiest way to fix this is to remove the old version\n");
+ printf("*** of LIBXML, but you can also set the XML2_CONFIG environment to point to the\n");
+ printf("*** correct copy of xml2-config. (In this case, you will have to\n");
+ printf("*** modify your LD_LIBRARY_PATH enviroment variable, or edit /etc/ld.so.conf\n");
+ printf("*** so that the correct libraries are found at run-time))\n");
+ }
+ return 1;
+}
+],, no_xml=yes,[echo $ac_n "cross compiling; assumed OK... $ac_c"])
+ CFLAGS="$ac_save_CFLAGS"
+ LIBS="$ac_save_LIBS"
+ fi
+ fi
+
+ if test "x$no_xml" = x ; then
+ AC_MSG_RESULT(yes (version $xml_config_major_version.$xml_config_minor_version.$xml_config_micro_version))
+ ifelse([$2], , :, [$2])
+ else
+ AC_MSG_RESULT(no)
+ if test "$XML2_CONFIG" = "no" ; then
+ echo "*** The xml2-config script installed by LIBXML could not be found"
+ echo "*** If libxml was installed in PREFIX, make sure PREFIX/bin is in"
+ echo "*** your path, or set the XML2_CONFIG environment variable to the"
+ echo "*** full path to xml2-config."
+ else
+ if test -f conf.xmltest ; then
+ :
+ else
+ echo "*** Could not run libxml test program, checking why..."
+ CFLAGS="$CFLAGS $XML_CFLAGS"
+ LIBS="$LIBS $XML_LIBS"
+ AC_TRY_LINK([
+#include <libxml/xmlversion.h>
+#include <stdio.h>
+], [ LIBXML_TEST_VERSION; return 0;],
+ [ echo "*** The test program compiled, but did not run. This usually means"
+ echo "*** that the run-time linker is not finding LIBXML or finding the wrong"
+ echo "*** version of LIBXML. If it is not finding LIBXML, you'll need to set your"
+ echo "*** LD_LIBRARY_PATH environment variable, or edit /etc/ld.so.conf to point"
+ echo "*** to the installed location Also, make sure you have run ldconfig if that"
+ echo "*** is required on your system"
+ echo "***"
+ echo "*** If you have an old version installed, it is best to remove it, although"
+ echo "*** you may also be able to get things to work by modifying LD_LIBRARY_PATH" ],
+ [ echo "*** The test program failed to compile or link. See the file config.log for the"
+ echo "*** exact error that occured. This usually means LIBXML was incorrectly installed"
+ echo "*** or that you have moved LIBXML since it was installed. In the latter case, you"
+ echo "*** may want to edit the xml2-config script: $XML2_CONFIG" ])
+ CFLAGS="$ac_save_CFLAGS"
+ LIBS="$ac_save_LIBS"
+ fi
+ fi
+
+ XML_CFLAGS=""
+ XML_LIBS=""
+ ifelse([$3], , :, [$3])
+ fi
+ AC_SUBST(XML_CFLAGS)
+ AC_SUBST(XML_LIBS)
+ rm -f conf.xmltest
+])
+
+# =========================================================================
+# AM_PATH_MYSQL : MySQL library
+
+dnl AM_PATH_MYSQL([MINIMUM-VERSION, [ACTION-IF-FOUND [, ACTION-IF-NOT-FOUND]]])
+dnl Test for MYSQL, and define MYSQL_CFLAGS and MYSQL_LIBS
+dnl
+AC_DEFUN(AM_PATH_MYSQL,
+[dnl
+dnl Get the cflags and libraries from the mysql_config script
+dnl
+AC_ARG_WITH(mysql-prefix,[ --with-mysql-prefix=PFX Prefix where MYSQL is installed (optional)],
+ mysql_prefix="$withval", mysql_prefix="")
+AC_ARG_WITH(mysql-exec-prefix,[ --with-mysql-exec-prefix=PFX Exec prefix where MYSQL is installed (optional)],
+ mysql_exec_prefix="$withval", mysql_exec_prefix="")
+
+ if test x$mysql_exec_prefix != x ; then
+ mysql_args="$mysql_args --exec-prefix=$mysql_exec_prefix"
+ if test x${MYSQL_CONFIG+set} != xset ; then
+ MYSQL_CONFIG=$mysql_exec_prefix/bin/mysql_config
+ fi
+ fi
+ if test x$mysql_prefix != x ; then
+ mysql_args="$mysql_args --prefix=$mysql_prefix"
+ if test x${MYSQL_CONFIG+set} != xset ; then
+ MYSQL_CONFIG=$mysql_prefix/bin/mysql_config
+ fi
+ fi
+
+ AC_REQUIRE([AC_CANONICAL_TARGET])
+ AC_PATH_PROG(MYSQL_CONFIG, mysql_config, no)
+ AC_MSG_CHECKING(for MYSQL)
+ no_mysql=""
+ if test "$MYSQL_CONFIG" = "no" ; then
+ MYSQL_CFLAGS=""
+ MYSQL_LIBS=""
+ AC_MSG_RESULT(no)
+ ifelse([$2], , :, [$2])
+ else
+ MYSQL_CFLAGS=`$MYSQL_CONFIG $mysqlconf_args --cflags | sed -e "s/'//g"`
+ MYSQL_LIBS=`$MYSQL_CONFIG $mysqlconf_args --libs | sed -e "s/'//g"`
+ AC_MSG_RESULT(yes)
+ ifelse([$1], , :, [$1])
+ fi
+ AC_SUBST(MYSQL_CFLAGS)
+ AC_SUBST(MYSQL_LIBS)
+])
+
+# =========================================================================
+# AM_PATH_PGSQL : pgSQL library
+
+dnl AM_PATH_PGSQL([MINIMUM-VERSION, [ACTION-IF-FOUND [, ACTION-IF-NOT-FOUND]]])
+dnl Test for PGSQL, and define PGSQL_CFLAGS and PGSQL_LIBS
+dnl
+AC_DEFUN(AM_PATH_PGSQL,
+[dnl
+dnl Get the cflags and libraries from the pg_config script
+dnl
+AC_ARG_WITH(pgsql-prefix,[ --with-pgsql-prefix=PFX Prefix where PostgreSQL is installed (optional)],
+ pgsql_prefix="$withval", pgsql_prefix="")
+AC_ARG_WITH(pgsql-exec-prefix,[ --with-pgsql-exec-prefix=PFX Exec prefix where PostgreSQL is installed (optional)],
+ pgsql_exec_prefix="$withval", pgsql_exec_prefix="")
+
+ if test x$pgsql_exec_prefix != x ; then
+ if test x${PGSQL_CONFIG+set} != xset ; then
+ PGSQL_CONFIG=$pgsql_exec_prefix/bin/pg_config
+ fi
+ fi
+ if test x$pgsql_prefix != x ; then
+ if test x${PGSQL_CONFIG+set} != xset ; then
+ PGSQL_CONFIG=$pgsql_prefix/bin/pg_config
+ fi
+ fi
+
+ AC_REQUIRE([AC_CANONICAL_TARGET])
+ AC_PATH_PROG(PGSQL_CONFIG, pg_config, no, [$PATH:/usr/lib/postgresql/bin])
+ AC_MSG_CHECKING(for PGSQL)
+ no_pgsql=""
+ if test "$PGSQL_CONFIG" = "no" ; then
+ PGSQL_CFLAGS=""
+ PGSQL_LIBS=""
+ AC_MSG_RESULT(no)
+ ifelse([$2], , :, [$2])
+ else
+ PGSQL_CFLAGS=-I`$PGSQL_CONFIG --includedir`
+ PGSQL_LIBS="-lpq -L`$PGSQL_CONFIG --libdir`"
+ AC_MSG_RESULT(yes)
+ ifelse([$1], , :, [$1])
+ fi
+ AC_SUBST(PGSQL_CFLAGS)
+ AC_SUBST(PGSQL_LIBS)
+])
+
+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/? from given variable
+AC_DEFUN(LIB_REMOVE_USR_LIB,[
+ ac_new_flags=""
+ for i in [$]$1; do
+ case [$]i in
+ -L/usr/lib|-L/usr/lib/) ;;
+ *) ac_new_flags="[$]ac_new_flags [$]i" ;;
+ esac
+ done
+ $1=[$]ac_new_flags
+])
+
+dnl From Bruno Haible.
+
+AC_DEFUN(jm_ICONV,
+[
+ dnl Some systems have iconv in libc, some have it in libiconv (OSF/1 and
+ dnl those with the standalone portable libiconv installed).
+ AC_MSG_CHECKING(for iconv in $1)
+ jm_cv_func_iconv="no"
+ jm_cv_lib_iconv=""
+ jm_cv_giconv=no
+ jm_save_LIBS="$LIBS"
+ LIBS="$LIBS -lbiconv"
+ AC_TRY_LINK([#include <stdlib.h>
+#include <biconv.h>],
+ [iconv_t cd = iconv_open("","");
+ iconv(cd,NULL,NULL,NULL,NULL);
+ iconv_close(cd);],
+ jm_cv_func_iconv=yes
+ jm_cv_biconv=yes
+ jm_cv_include="biconv.h"
+ jm_cv_lib_iconv="biconv")
+ LIBS="$jm_save_LIBS"
+
+ dnl Check for include in funny place but no lib needed
+ if test "$jm_cv_func_iconv" != yes; then
+ AC_TRY_LINK([#include <stdlib.h>
+#include <giconv.h>],
+ [iconv_t cd = iconv_open("","");
+ iconv(cd,NULL,NULL,NULL,NULL);
+ iconv_close(cd);],
+ jm_cv_func_iconv=yes
+ jm_cv_include="giconv.h"
+ jm_cv_giconv="yes"
+ jm_cv_lib_iconv="")
+
+ dnl Standard iconv.h include, lib in glibc or libc ...
+ if test "$jm_cv_func_iconv" != yes; then
+ AC_TRY_LINK([#include <stdlib.h>
+#include <iconv.h>],
+ [iconv_t cd = iconv_open("","");
+ iconv(cd,NULL,NULL,NULL,NULL);
+ iconv_close(cd);],
+ jm_cv_include="iconv.h"
+ jm_cv_func_iconv=yes
+ jm_cv_lib_iconv="")
+
+ if test "$jm_cv_lib_iconv" != yes; then
+ jm_save_LIBS="$LIBS"
+ LIBS="$LIBS -lgiconv"
+ AC_TRY_LINK([#include <stdlib.h>
+#include <giconv.h>],
+ [iconv_t cd = iconv_open("","");
+ iconv(cd,NULL,NULL,NULL,NULL);
+ iconv_close(cd);],
+ jm_cv_lib_iconv=yes
+ jm_cv_func_iconv=yes
+ jm_cv_include="giconv.h"
+ jm_cv_giconv=yes
+ jm_cv_lib_iconv="giconv")
+
+ LIBS="$jm_save_LIBS"
+
+ if test "$jm_cv_func_iconv" != yes; then
+ jm_save_LIBS="$LIBS"
+ LIBS="$LIBS -liconv"
+ AC_TRY_LINK([#include <stdlib.h>
+#include <iconv.h>],
+ [iconv_t cd = iconv_open("","");
+ iconv(cd,NULL,NULL,NULL,NULL);
+ iconv_close(cd);],
+ jm_cv_include="iconv.h"
+ jm_cv_func_iconv=yes
+ jm_cv_lib_iconv="iconv")
+ LIBS="$jm_save_LIBS"
+ fi
+ fi
+ fi
+ fi
+ if test "$jm_cv_func_iconv" = yes; then
+ if test "$jm_cv_giconv" = yes; then
+ AC_DEFINE(HAVE_GICONV, 1, [What header to include for iconv() function: giconv.h])
+ AC_MSG_RESULT(yes)
+ ICONV_FOUND=yes
+ else
+ if test "$jm_cv_biconv" = yes; then
+ AC_DEFINE(HAVE_BICONV, 1, [What header to include for iconv() function: biconv.h])
+ AC_MSG_RESULT(yes)
+ ICONV_FOUND=yes
+ else
+ AC_DEFINE(HAVE_ICONV, 1, [What header to include for iconv() function: iconv.h])
+ AC_MSG_RESULT(yes)
+ ICONV_FOUND=yes
+ fi
+ fi
+ else
+ AC_MSG_RESULT(no)
+ fi
+])
+
+AC_DEFUN(rjs_CHARSET,[
+ dnl Find out if we can convert from $1 to UCS2-LE
+ AC_MSG_CHECKING([can we convert from $1 to UCS2-LE?])
+ AC_TRY_RUN([
+#include <$jm_cv_include>
+main(){
+ iconv_t cd = iconv_open("$1", "UCS-2LE");
+ if (cd == 0 || cd == (iconv_t)-1) {
+ return -1;
+ }
+ return 0;
+}
+ ],ICONV_CHARSET=$1,ICONV_CHARSET=no,ICONV_CHARSET=cross)
+ AC_MSG_RESULT($ICONV_CHARSET)
+])
+
+dnl CFLAGS_ADD_DIR(CFLAGS, $INCDIR)
+dnl This function doesn't add -I/usr/include into CFLAGS
+AC_DEFUN(CFLAGS_ADD_DIR,[
+if test "$2" != "/usr/include" ; then
+ $1="$$1 -I$2"
+fi
+])
+
+dnl LIB_ADD_DIR(LDFLAGS, $LIBDIR)
+dnl This function doesn't add -L/usr/lib into LDFLAGS
+AC_DEFUN(LIB_ADD_DIR,[
+if test "$2" != "/usr/lib" ; then
+ $1="$$1 -L$2"
+fi
+])
+
+dnl AC_ENABLE_SHARED - implement the --enable-shared flag
+dnl Usage: AC_ENABLE_SHARED[(DEFAULT)]
+dnl Where DEFAULT is either `yes' or `no'. If omitted, it defaults to
+dnl `yes'.
+AC_DEFUN([AC_ENABLE_SHARED],
+[define([AC_ENABLE_SHARED_DEFAULT], ifelse($1, no, no, yes))dnl
+AC_ARG_ENABLE(shared,
+changequote(<<, >>)dnl
+<< --enable-shared[=PKGS] build shared libraries [default=>>AC_ENABLE_SHARED_DEFAULT],
+changequote([, ])dnl
+[p=${PACKAGE-default}
+case $enableval in
+yes) enable_shared=yes ;;
+no) enable_shared=no ;;
+*)
+ enable_shared=no
+ # Look at the argument we got. We use all the common list separators.
+ IFS="${IFS= }"; ac_save_ifs="$IFS"; IFS="${IFS}:,"
+ for pkg in $enableval; do
+ if test "X$pkg" = "X$p"; then
+ enable_shared=yes
+ fi
+
+ done
+ IFS="$ac_save_ifs"
+ ;;
+esac],
+enable_shared=AC_ENABLE_SHARED_DEFAULT)dnl
+])
+
+dnl AC_ENABLE_STATIC - implement the --enable-static flag
+dnl Usage: AC_ENABLE_STATIC[(DEFAULT)]
+dnl Where DEFAULT is either `yes' or `no'. If omitted, it defaults to
+dnl `yes'.
+AC_DEFUN([AC_ENABLE_STATIC],
+[define([AC_ENABLE_STATIC_DEFAULT], ifelse($1, no, no, yes))dnl
+AC_ARG_ENABLE(static,
+changequote(<<, >>)dnl
+<< --enable-static[=PKGS] build static libraries [default=>>AC_ENABLE_STATIC_DEFAULT],
+changequote([, ])dnl
+[p=${PACKAGE-default}
+case $enableval in
+yes) enable_static=yes ;;
+no) enable_static=no ;;
+*)
+ enable_static=no
+ # Look at the argument we got. We use all the common list separators.
+ IFS="${IFS= }"; ac_save_ifs="$IFS"; IFS="${IFS}:,"
+ for pkg in $enableval; do
+ if test "X$pkg" = "X$p"; then
+ enable_static=yes
+ fi
+ done
+ IFS="$ac_save_ifs"
+ ;;
+esac],
+enable_static=AC_ENABLE_STATIC_DEFAULT)dnl
+])
+
+dnl AC_DISABLE_STATIC - set the default static flag to --disable-static
+AC_DEFUN([AC_DISABLE_STATIC],
+[AC_BEFORE([$0],[AC_LIBTOOL_SETUP])dnl
+AC_ENABLE_STATIC(no)])
+
+dnl AC_TRY_RUN_STRICT(PROGRAM,CFLAGS,CPPFLAGS,LDFLAGS,
+dnl [ACTION-IF-TRUE],[ACTION-IF-FALSE],
+dnl [ACTION-IF-CROSS-COMPILING = RUNTIME-ERROR])
+AC_DEFUN( [AC_TRY_RUN_STRICT],
+[
+ old_CFLAGS="$CFLAGS";
+ CFLAGS="$2";
+ export CFLAGS;
+ old_CPPFLAGS="$CPPFLAGS";
+ CPPFLAGS="$3";
+ export CPPFLAGS;
+ old_LDFLAGS="$LDFLAGS";
+ LDFLAGS="$4";
+ export LDFLAGS;
+ AC_TRY_RUN([$1],[$5],[$6],[$7]);
+ CFLAGS="$old_CFLAGS";
+ old_CFLAGS="";
+ export CFLAGS;
+ CPPFLAGS="$old_CPPFLAGS";
+ old_CPPFLAGS="";
+ export CPPFLAGS;
+ LDFLAGS="$old_LDFLAGS";
+ old_LDFLAGS="";
+ export LDFLAGS;
+])
diff --git a/source/aparser/Makefile b/source/aparser/Makefile
new file mode 100644
index 00000000000..953743b234d
--- /dev/null
+++ b/source/aparser/Makefile
@@ -0,0 +1,17 @@
+CFLAGS=-Wall -g
+CC=gcc
+
+OBJ = vluke.o parser.o
+AWKPROGS=dump.awk harness.awk header.awk parsefn.awk main.awk parsetree.awk template.awk util.awk
+
+all: test.h vluke
+
+test.h : $(AWKPROGS)
+ igawk -f main.awk srvsvc.struct
+
+vluke: test.h $(OBJ)
+ $(CC) $(CFLAGS) -o vluke $(OBJ)
+
+clean:
+ rm -f *.o test.h prs_*.[ch]
+
diff --git a/source/aparser/build b/source/aparser/build
new file mode 100755
index 00000000000..4cdf2901f80
--- /dev/null
+++ b/source/aparser/build
@@ -0,0 +1,13 @@
+#!/bin/sh
+
+file=$1
+
+if ! igawk -f main.awk $file; then
+ echo parse failed;
+ exit 1;
+fi
+
+echo compiling vluke
+gcc -Wall -g -o vluke parser.c vluke.c util.c
+echo done.
+
diff --git a/source/aparser/cifs.struct b/source/aparser/cifs.struct
new file mode 100644
index 00000000000..202f0d7e619
--- /dev/null
+++ b/source/aparser/cifs.struct
@@ -0,0 +1,1029 @@
+module cifs
+
+option autoalign False
+
+
+#define BOOL uint32
+#define UCHAR uint8
+#define WCHAR uint16
+#define USHORT uint16
+#define LONG uint32
+#define ULONG uint32
+#define DWORD uint32
+#define SMB_TIME uint16
+#define SMB_DATE uint16
+
+typedef struct {
+ ULONG low;
+ LONG high;
+} TIME;
+
+typedef struct {
+ ULONG low;
+ ULONG high;
+} hyper;
+
+typedef struct {
+ uint8 cmd;
+ uint8 reserved;
+ uint16 offset;
+} ANDX_INFO;
+
+
+typedef struct {
+ uint8 tag2;
+ STRING protocol;
+} BUF2;
+
+typedef struct {
+ uint16 bcount;
+ BUF2 protocol[*];
+} Q_NEGPROT_0;
+
+typedef struct {
+ uint8 wcount;
+ union ctr[wcount] {
+ case 0 Q_NEGPROT_0 q0;
+ }
+} Q_NEGPROT;
+
+typedef struct {
+ USHORT DialectIndex; /* Index of selected dialect */
+ USHORT SecurityMode; /* Security mode: */
+ /* bit 0: 0 = share, 1 = user */
+ /* bit 1: 1 = use challenge/response */
+ /* authentication */
+ USHORT MaxBufferSize; /* Max transmit buffer size (>= 1024) */
+ USHORT MaxMpxCount; /* Max pending multiplexed requests */
+ USHORT MaxNumberVcs; /* Max VCs between client and server */
+ USHORT RawMode; /* Raw modes supported: */
+ /* bit 0: 1 = Read Raw supported */
+ /* bit 1: 1 = Write Raw supported */
+ ULONG SessionKey; /* Unique token identifying this session */
+ SMB_TIME ServerTime; /* Current time at server */
+ SMB_DATE ServerDate; /* Current date at server */
+ USHORT ServerTimeZone; /* Current time zone at server */
+ USHORT ChallengeLength; /* Length of Challenge; MBZ if not LM2.1
+ /* dialect or later */
+ USHORT Reserved; /* MBZ */
+ USHORT ByteCount; /* Count of data bytes */
+ UCHAR Challenge[ChallengeLength]; /* The challenge */
+ STRING PrimaryDomain; /* The server's primary domain */
+
+} R_NEGPROT_12;
+
+typedef struct {
+ USHORT DialectIndex; /*Index of selected dialect */
+ UCHAR SecurityMode; /*Security mode: */
+ /* bit 0: 0 = share, 1 = user */
+ /* bit 1: 1 = use challenge/response */
+ /* authentication */
+ /* bit 2: 1 = Security Signatures (SMB integrity */
+ /* check) enabled */
+ /* bit 3: 1 = Security Signatures (SMB integrity */
+ /* check) required */
+ USHORT MaxMpxCount; /*Max pending outstanding requests */
+ USHORT MaxNumberVcs; /*Max VCs between client and server */
+ ULONG MaxBufferSize; /*Max transmit buffer size */
+ ULONG MaxRawSize; /*Maximum raw buffer size */
+ ULONG SessionKey; /*Unique token identifying this session */
+ ULONG Capabilities; /*Server capabilities */
+ ULONG SystemTimeLow; /*System (UTC) time of the server (low). */
+ ULONG SystemTimeHigh; /*System (UTC) time of the server (high). */
+ USHORT ServerTimeZone;/*Time zone of server (minutes from UTC) */
+ UCHAR SecurityBlobLength;/*Length of SecurityBlob */
+
+ USHORT bcount; /*Count of data bytes */
+ /*UCHAR GUID[16]; A globally unique identifier assigned to the */
+ /* server; present only when */
+ /* CAP_EXTENDED_SECURITY is on in the */
+ /* Capabilities field. */
+ UCHAR SecurityBlob[SecurityBlobLength]; /*Opaque Security Blob associated with the */
+ /* security package if CAP_EXTENDED_SECURITY is */
+ /* on in the Capabilities field; else challenge */
+ /* for CIFS challenge/response authentication. */
+ STRING OemDomainName[+]; /*The name of the domain (in OEM chars); not */
+ /* present if CAP_EXTENDED_SECURITY is on in the */
+ /* Capabilities field */
+} R_NEGPROT_17;
+
+
+typedef struct {
+ uint8 wcount;
+ union ctr[wcount] {
+ case 17 R_NEGPROT_17 r17;
+ }
+} R_NEGPROT;
+
+typedef struct {
+ uint8 wcount;
+ uint16 vwv[wcount];
+ uint16 bcount;
+ uint8 none[bcount];
+} Q_TDIS;
+
+typedef struct {
+ uint8 wcount;
+ uint16 vwv[wcount];
+ uint16 bcount;
+ uint8 none[bcount];
+} R_TDIS;
+
+typedef struct {
+ ANDX_INFO andx;
+ uint16 bcount;
+ uint8 none[bcount];
+} R_ULOGOFF_ANDX_2;
+
+typedef struct {
+ uint8 wcount;
+ union ctr[wcount] {
+ case 2 R_ULOGOFF_ANDX_2 q2;
+ }
+} R_ULOGOFF_ANDX;
+
+typedef struct {
+ ANDX_INFO andx;
+ uint16 bcount;
+ uint8 none[bcount];
+} Q_ULOGOFF_ANDX_2;
+
+typedef struct {
+ uint8 wcount;
+ union ctr[wcount] {
+ case 2 Q_ULOGOFF_ANDX_2 q2;
+ }
+} Q_ULOGOFF_ANDX;
+
+typedef struct {
+ ANDX_INFO andx;
+ uint16 bufsize;
+ uint16 max_mpx;
+ uint16 vc;
+ ULONG sess_key;
+ uint16 pwlen;
+ ULONG reserved;
+
+ uint16 bcount;
+ uint8 password[pwlen];
+ STRING domain;
+ STRING os;
+ STRING server;
+
+} Q_SESSION_SETUP_ANDX_10;
+
+typedef struct {
+ ANDX_INFO andx;
+ uint16 bufsize;
+ uint16 max_mpx;
+ uint16 vc;
+ ULONG sess_key;
+ uint16 pwlen;
+ uint16 upwlen;
+ ULONG capabilities;
+ ULONG reserved;
+
+ uint16 bcount;
+ uint8 password[pwlen];
+ uint8 upassword[upwlen];
+ STRING user;
+ STRING domain;
+ STRING os;
+ STRING server;
+
+} Q_SESSION_SETUP_ANDX_13;
+
+typedef struct _Q_SESSION_SETUP_ANDX {
+ uint8 wcount;
+ union ctr[wcount] {
+ case 10 Q_SESSION_SETUP_ANDX_10 q10;
+ case 13 Q_SESSION_SETUP_ANDX_13 q13;
+ }
+} Q_SESSION_SETUP_ANDX;
+
+typedef struct {
+ ANDX_INFO andx;
+ uint16 vwv2;
+ uint16 passlen;
+ uint16 bcount;
+ uint8 password[passlen];
+ STRING path;
+ STRING device;
+} Q_TCON_ANDX_4;
+
+typedef struct _Q_TCON_ANDX {
+ uint8 wcount;
+ union ctr[wcount] {
+ case 4 Q_TCON_ANDX_4 q4;
+ }
+} Q_TCON_ANDX;
+
+typedef struct {
+ ANDX_INFO andx;
+ uint16 vwv2;
+ uint16 bcount;
+ STRING share;
+} R_TCON_ANDX_3;
+
+typedef struct _R_TCON_ANDX {
+ uint8 wcount;
+ union ctr[wcount] {
+ case 3 R_TCON_ANDX_3 q3;
+ }
+} R_TCON_ANDX;
+
+typedef struct {
+ ANDX_INFO andx;
+ uint16 action;
+
+ uint16 count;
+ STRING os;
+ STRING server;
+ STRING domain;
+} R_SESSION_SETUP_ANDX_10;
+
+typedef struct _R_SESSION_SETUP_ANDX {
+ uint8 wcount;
+ union ctr[wcount] {
+ case 3 R_SESSION_SETUP_ANDX_10 r3;
+ }
+} R_SESSION_SETUP_ANDX;
+
+
+typedef struct _R_CLOSE {
+ uint8 wcount;
+ uint16 count;
+ uint8 none[count];
+
+} R_CLOSE;
+
+typedef struct _Q_CLOSE {
+ uint8 wcount;
+ uint16 fnum;
+ uint32 vwv1;
+
+ uint16 count;
+ uint8 none[count];
+
+} Q_CLOSE;
+
+typedef struct {
+ uint16 dsize;
+ uint16 bsizehi;
+ uint16 bsizelo;
+ uint16 avail;
+ uint16 vwv4;
+
+ uint16 bcount;
+ uint8 none[bcount];
+
+} R_DSKATTR_5;
+
+typedef struct {
+ uint8 wcount;
+ union ctr[wcount] {
+ case 5 R_DSKATTR_5 r5;
+ }
+} R_DSKATTR;
+
+typedef struct {
+ uint16 count;
+ uint8 none[count];
+
+} Q_DSKATTR_0;
+
+typedef struct _Q_DSKATTR {
+ uint8 wcount;
+ union ctr[wcount] {
+ case 0 Q_DSKATTR_0 q1;
+ }
+
+} Q_DSKATTR;
+
+typedef struct {
+ ANDX_INFO andx;
+
+ uint16 bcount;
+ uint8 none[bcount];
+
+} R_LOCKING_2;
+
+typedef struct {
+ uint8 wcount;
+ union ctr[wcount] {
+ case 2 R_LOCKING_2 r2;
+ }
+} R_LOCKING_ANDX;
+
+/* XXXX must do a switch on bit 0x10 to do large locks XXXX */
+/* LockType Flag Name Value Description */
+
+#define LOCKING_ANDX_SHARED_LOCK 0x01 /* Read-only lock */
+#define LOCKING_ANDX_OPLOCK_RELEASE 0x02 /* Oplock break notification */
+#define LOCKING_ANDX_CHANGE_LOCKTYPE 0x04 /* Change lock type */
+#define LOCKING_ANDX_CANCEL_LOCK 0x08 /* Cancel outstanding request */
+#define LOCKING_ANDX_LARGE_FILES 0x10 /* Large file locking format */
+
+typedef struct {
+ USHORT Pid; /* PID of process "owning" lock */
+ ULONG Offset; /* Offset to bytes to [un]lock */
+ ULONG Length; /* Number of bytes to [un]lock */
+} LOCKING_ANDX_RANGE_SHORT;
+
+typedef struct {
+ USHORT Pid; /* PID of process "owning" lock */
+ .align4 0;
+ ULONG OffsetHigh; /* Offset to bytes to [un]lock (high) */
+ ULONG OffsetLow; /* Offset to bytes to [un]lock (low) */
+ ULONG LengthHigh; /* Number of bytes to [un]lock (high) */
+ ULONG LengthLow; /* Number of bytes to [un]lock (low) */
+
+} LOCKING_ANDX_RANGE_LARGE;
+
+/* typedef struct { */
+ /* union ctr[LockType&0x10] { */
+ /* case 0 LOCKING_ANDX_RANGE_SHORT ls; */
+ /* case 0x10 LOCKING_ANDX_RANGE_LARGE ll; */
+ /* } */
+/* } LOCKING_ANDX_RANGE; */
+
+typedef struct {
+ ANDX_INFO andx;
+
+ USHORT Fid; /* File handle */
+ UCHAR LockType; /* See LockType table below */
+ UCHAR OplockLevel; /* The new oplock level */
+ ULONG Timeout; /* Milliseconds to wait for unlock */
+ USHORT NumberOfUnlocks; /* Num. unlock range structs following */
+ USHORT NumberOfLocks; /* Num. lock range structs following */
+
+ USHORT ByteCount; /* Count of data bytes */
+ LOCKING_ANDX_RANGE_SHORT Unlocks[NumberOfUnlocks]; /* Unlock ranges */
+ LOCKING_ANDX_RANGE_SHORT Locks[NumberOfLocks]; /* Lock ranges */
+
+} Q_LOCKING_8;
+
+typedef struct _Q_LOCKING {
+ uint8 wcount;
+ union ctr[wcount] {
+ case 8 Q_LOCKING_8 q8;
+ }
+
+} Q_LOCKING_ANDX;
+
+
+typedef struct {
+ uint16 bcount;
+ uint8 protocols[bcount];
+
+} R_UNLINK_0;
+
+typedef struct {
+ uint8 wcount;
+ union ctr[wcount] {
+ case 0 R_UNLINK_0 r0;
+ }
+} R_UNLINK;
+
+typedef struct {
+ uint16 dirtype;
+
+ uint16 count;
+ uint8 fname[count];
+
+} Q_UNLINK_1;
+
+typedef struct _Q_UNLINK {
+ uint8 wcount;
+ union ctr[wcount] {
+ case 1 Q_UNLINK_1 q1;
+ }
+
+} Q_UNLINK;
+
+typedef struct _R_OPEN_ANDX{
+ uint8 wcount;
+ ANDX_INFO andx;
+ uint16 fnum;
+ uint16 fmode;
+ uint32 mtime;
+ uint32 size;
+ uint16 rmode;
+ uint16 vwv9;
+ uint16 vwv10;
+ uint16 smb_action;
+ uint16 vwv12;
+ uint16 vwv13;
+ uint16 vwv14;
+
+ uint16 count;
+ uint8 none[count];
+
+} R_OPEN_ANDX;
+
+typedef struct _Q_OPEN_ANDX{
+ uint8 wcount;
+ ANDX_INFO andx;
+ uint16 fnum;
+ uint16 fmode;
+ uint32 mtime;
+ uint32 size;
+ uint16 rmode;
+ uint16 vwv9;
+ uint16 vwv10;
+ uint16 smb_action;
+ uint16 vwv12;
+ uint16 vwv13;
+ uint16 vwv14;
+
+ uint16 count;
+ uint8 fname[count];
+
+} Q_OPEN_ANDX;
+
+typedef struct _R_READ_ANDX {
+ uint8 wcount;
+ ANDX_INFO andx;
+ uint16 vwv2;
+ uint16 vwv3;
+ uint16 vwv4;
+ uint16 nread;
+ uint16 offset;
+ uint16 vwv7;
+ uint16 vwv8;
+ uint16 vwv9;
+ uint16 vwv10;
+ uint16 vwv11;
+
+ uint16 count;
+ uint8 data[count];
+
+} R_READ_ANDX;
+
+typedef struct _Q_READ_ANDX_10 {
+ ANDX_INFO andx;
+ uint16 fnum;
+ uint32 startpos;
+ uint16 smb_maxcnt;
+ uint16 smb_mincnt;
+ uint16 vwv7;
+ uint16 vwv8;
+ uint16 vwv9;
+
+ uint16 count;
+ uint8 none[count];
+
+} Q_READ_ANDX_10;
+
+typedef struct _Q_READ_ANDX_12 {
+ ANDX_INFO andx;
+ uint16 fnum;
+ uint32 startpos;
+ uint16 smb_maxcnt;
+ uint16 smb_mincnt;
+ uint16 vwv7;
+ uint16 vwv8;
+ uint16 vwv9;
+ uint32 startposhi;
+
+ uint16 count;
+ uint8 none[count];
+
+} Q_READ_ANDX_12;
+
+typedef struct _Q_READ_ANDX {
+ uint8 wcount;
+ union ctr[wcount] {
+ case 10 Q_READ_ANDX_10 q10;
+ case 12 Q_READ_ANDX_12 q12;
+ }
+} Q_READ_ANDX;
+
+typedef struct _R_WRITE_ANDX {
+ uint8 wcount;
+ ANDX_INFO andx;
+ uint16 nwritten;
+ uint16 vwv3;
+ uint16 vwv4;
+ uint16 vwv5;
+
+ uint16 count;
+ uint8 none[count];
+
+} R_WRITE_ANDX;
+
+typedef struct _Q_WRITE_ANDX_12 {
+ ANDX_INFO andx;
+ uint16 fnum;
+ uint32 startpos;
+ uint16 vwv5;
+ uint16 vwv6;
+ uint16 write_through;
+ uint16 vwv8;
+ uint16 vwv9;
+ uint16 numtowrite;
+ uint16 smb_doff;
+
+ uint16 count;
+ uint8 data[count];
+
+} Q_WRITE_ANDX_12;
+
+typedef struct _Q_WRITE_ANDX_14 {
+ ANDX_INFO andx;
+ uint16 fnum;
+ uint32 startpos;
+ uint16 vwv5;
+ uint16 vwv6;
+ uint16 write_through;
+ uint16 vwv8;
+ uint16 vwv9;
+ uint16 numtowrite;
+ uint16 smb_doff;
+ uint32 startposhi;
+
+ uint16 count;
+ uint8 data[count];
+
+} Q_WRITE_ANDX_14;
+
+typedef struct _Q_WRITE_ANDX {
+ uint8 wcount;
+ union ctr[wcount] {
+ case 12 Q_WRITE_ANDX_12 q12;
+ case 14 Q_WRITE_ANDX_14 q14;
+ }
+} Q_WRITE_ANDX;
+
+
+
+typedef struct _Q_NTTRANS_19 {
+ UCHAR MaxSetupCount; /* Max setup words to return */
+ USHORT Reserved;
+ ULONG TotalParameterCount; /* Total parameter bytes being sent */
+ ULONG TotalDataCount; /* Total data bytes being sent */
+ ULONG MaxParameterCount; /* Max parameter bytes to return */
+ ULONG MaxDataCount; /* Max data bytes to return */
+ ULONG ParameterCount; /* Parameter bytes sent this buffer */
+ ULONG ParameterOffset; /* Offset (from header start) to */
+ /* Parameters */
+ ULONG DataCount; /* Data bytes sent this buffer */
+ ULONG DataOffset; /* Offset (from header start) to data */
+ UCHAR SetupCount; /* Count of setup words */
+ USHORT Function; /* The transaction function code */
+ UCHAR Buffer[1];
+ USHORT Setup[SetupCount]; /* Setup words */
+ USHORT ByteCount; /* Count of data bytes */
+ .align4 0;
+ UCHAR Parameters[ParameterCount];/* Parameter bytes */
+ .align4 0;
+ UCHAR Data[DataCount]; /* Data bytes */
+
+} Q_NTTRANS_19;
+
+typedef struct _Q_NTTRANS {
+ uint8 wcount;
+ union ctr[wcount] {
+ case 19 Q_NTTRANS_19 q19;
+ }
+} Q_NTTRANS;
+
+typedef struct _R_NTTRANS_18 {
+ UCHAR Reserved[3];
+ ULONG TotalParameterCount; /* Total parameter bytes being sent */
+ ULONG TotalDataCount; /* Total data bytes being sent */
+ ULONG ParameterCount; /* Parameter bytes sent this buffer */
+ ULONG ParameterOffset; /* Offset (from header start) to */
+ /* Parameters */
+ ULONG ParameterDisplacement; /* Specifies the offset from the start */
+ /* of the overall parameter block to */
+ /* the parameter bytes that are */
+ /* contained in this message */
+ ULONG DataCount; /* Data bytes sent this buffer */
+ ULONG DataOffset; /* Offset (from header start) to data */
+ ULONG DataDisplacement; /* Specifies the offset from the start */
+ /* of the overall data block to the */
+ /* data bytes that are contained in */
+ /* this message. */
+ UCHAR SetupCount; /* Count of setup words */
+ USHORT Setup[SetupCount]; /* Setup words */
+ USHORT ByteCount; /* Count of data bytes */
+ .align4 0;
+ UCHAR Parameters[ParameterCount]; /* Parameter bytes */
+ .align4 0;
+ UCHAR Data[DataCount]; /* Data bytes */
+} R_NTTRANS_18;
+
+typedef struct _R_NTTRANS {
+ uint8 wcount;
+ union ctr[wcount] {
+ case 18 R_NTTRANS_18 q18;
+ }
+ .align4 2;
+} R_NTTRANS;
+
+/*Setup[0] Transaction2 Value Description */
+/*Subcommand Code */
+/*=============================== ===== ============================= */
+
+#define TRANS2_OPEN2 0x00 /* Create file with extended attributes */
+#define TRANS2_FIND_FIRST2 0x01 /* Begin search for files */
+#define TRANS2_FIND_NEXT2 0x02 /* Resume search for files */
+#define TRANS2_QUERY_FS_INFO 0x03 /* Get file system information
+#define TRANS2_RESERVED4 0x04 /* Reserved */
+#define TRANS2_QUERY_PATH_INFO 0x05 /* Get information about a named file or directory */
+#define TRANS2_SET_PATH_INFO 0x06 /* Set information about a named file or directory */
+#define TRANS2_QUERY_FILE_INFO 0x07 /* Get information about a handle */
+#define TRANS2_SET_FILE_INFO 0x08 /* Set information by handle */
+#define TRANS2_FSCTL 0x09 /* Not implemented by NT server */
+#define TRANS2_IOCTL2 0x0A /* Not implemented by NT server */
+#define TRANS2_FIND_NOTIFY_FIRST 0x0B /* Not implemented by NT server */
+#define TRANS2_FIND_NOTIFY_NEXT 0x0C /* Not implemented by NT server */
+#define TRANS2_CREATE_DIRECTORY 0x0D /* Create directory with extended attributes */
+#define TRANS2_SESSION_SETUP 0x0E /* Session setup with extended security information */
+#define TRANS2_GET_DFS_REFERRAL 0x10 /* Get a DFS referral */
+#define TRANS2_REPORT_DFS_INCONSISTENCY 0x11 /* Report a DFS knowledge inconsistency */
+
+typedef struct {
+ USHORT InformationLevel; /* Level of information requested */
+} TRANS2_QUERY_FS_INFO_STRUCT;
+
+#define SMB_INFO_STANDARD 1
+#define SMB_INFO_QUERY_EA_SIZE 2
+#define SMB_SET_FILE_BASIC_INFO 0x101
+#define SMB_SET_FILE_DISPOSITION_INFO 0x102
+#define SMB_SET_FILE_ALLOCATION_INFO 0x103
+#define SMB_SET_FILE_END_OF_FILE_INFO 0x104
+
+
+typedef struct {
+ hyper CreationTime;
+ hyper LastAccessTime;
+ hyper LastWriteTime;
+ hyper ChangeTime;
+ USHORT Attributes;
+ .align4 0;
+} SMB_QUERY_FILE_BASIC_INFO_STRUCT;
+
+
+typedef struct {
+ ULONG fs_atr;
+ LONG max_len_filename;
+ ULONG length;
+ uint8 fs[length];
+ .align4 2;
+} SMB_QUERY_FS_ATTRIBUTE_INFO_STRUCT;
+
+#define FILE_CASE_SENSITIVE_SEARCH 0x00000001
+#define FILE_CASE_PRESERVED_NAMES 0x00000002
+#define FILE_PRSISTENT_ACLS 0x00000004
+#define FILE_FILE_COMPRESSION 0x00000008
+#define FILE_VOLUME_QUOTAS 0x00000010
+#define FILE_DEVICE_IS_MOUNTED 0x00000020
+#define FILE_VOLUME_IS_COMPRESSED 0x00008000
+
+typedef struct {
+ USHORT Fid;
+ USHORT InformationLevel;
+ USHORT Reserved;
+ .align4 0;
+
+ union ctr[InformationLevel] {
+ case 0x101 SMB_QUERY_FILE_BASIC_INFO_STRUCT t101;
+ }
+
+} TRANS2_SET_FILE_INFO_STRUCT;
+
+typedef struct {
+ USHORT InformationLevel; /* Level of information requested */
+ ULONG Reserved; /* Must be zero */
+ STRING FileName; /* File or directory name */
+} TRANS2_QUERY_PATH_INFO_STRUCT;
+
+typedef struct {
+ USHORT SearchAttributes;
+ USHORT SearchCount;
+ USHORT Flags;
+ USHORT InformationLevel;
+ ULONG SearchStorageType;
+ STRING FileName;
+} TRANS2_FIND_FIRST2_STRUCT;
+
+typedef struct _Q_TRANS2_15 {
+ USHORT TotalParameterCount; /* Total parameter bytes being sent */
+ USHORT TotalDataCount; /* Total data bytes being sent */
+ USHORT MaxParameterCount; /* Max parameter bytes to return */
+ USHORT MaxDataCount; /* Max data bytes to return */
+ UCHAR MaxSetupCount; /* Max setup words to return */
+ UCHAR Reserved;
+ USHORT Flags; /* Additional information: */
+ /* bit 0 - also disconnect TID in TID */
+ ULONG Timeout;
+ USHORT Reserved2;
+ USHORT ParameterCount; /* Parameter bytes sent this buffer */
+ USHORT ParameterOffset; /* Offset (from header start) to */
+ /* Parameters */
+ USHORT DataCount; /* Data bytes sent this buffer */
+ USHORT DataOffset; /* Offset (from header start) to data */
+ UCHAR SetupCount; /* Count of setup words */
+ UCHAR Reserved3; /* Reserved (pad above to word) */
+ USHORT Setup[SetupCount]; /* Setup words (# = SetupWordCount) */
+ USHORT ByteCount; /* Count of data bytes */
+ .align4 0;
+ union ctr[Setup[0]] {
+ case 1 TRANS2_FIND_FIRST2_STRUCT t1;
+ case 3 TRANS2_QUERY_FS_INFO_STRUCT t3;
+ case 5 TRANS2_QUERY_PATH_INFO_STRUCT t5;
+ case 8 TRANS2_SET_FILE_INFO_STRUCT t8;
+ }
+
+} Q_TRANS2_15;
+
+typedef struct _Q_TRANS2 {
+ uint8 wcount;
+ union ctr[wcount] {
+ case 15 Q_TRANS2_15 q15;
+ }
+} Q_TRANS2;
+
+typedef struct {
+ ULONG NextEntryOffset;
+ ULONG FileIndex;
+ hyper CreationTime;
+ hyper LastAccessTime;
+ hyper LastWriteTime;
+ hyper ChangeTime;
+ hyper EndOfFile;
+ hyper AllocationSize;
+ ULONG ExtFileAttributes;
+ ULONG FileNameLength;
+ ULONG EaSize;
+ UCHAR ShortNameLength;
+ UCHAR Reserved;
+ uint8 ShortName[24];
+ UCHAR FileName[FileNameLength];
+ .align4 2;
+} SMB_FIND_FILE_BOTH_DIRECTORY_INFO;
+
+typedef struct {
+ .align2 0;
+} R_TRANS2_D0;
+
+typedef struct {
+ .align4 2;
+} R_TRANS2_P0;
+
+typedef struct {
+ USHORT Reserved;
+} R_TRANS2_P2;
+
+typedef struct {
+ USHORT Sid; /* Search handle */
+ USHORT SearchCount; /* Number of entries returned */
+ USHORT EndOfSearch; /* Was last entry returned? */
+ USHORT EaErrorOffset; /* Offset into EA list if EA error */
+ USHORT LastNameOffset; /* Offset into data to file name of last */
+ /* entry, if server needs it to resume */
+ /* search; else 0 */
+ .align4 2;
+ SMB_FIND_FILE_BOTH_DIRECTORY_INFO i104[SearchCount];
+} R_TRANS2_FIND_FIRST2_STRUCT;
+
+typedef struct {
+ SMB_QUERY_FILE_BASIC_INFO_STRUCT i101;
+ .align4 2;
+} R_TRANS2_FILE_BASIC_STRUCT;
+
+typedef struct _R_TRANS2_10 {
+ USHORT TotalParameterCount;/* Total parameter bytes being sent */
+ USHORT TotalDataCount; /* Total data bytes being sent */
+ USHORT Reserved2;
+ USHORT ParameterCount; /* Parameter bytes sent this buffer */
+ USHORT ParameterOffset; /* Offset (from header start) to */
+ /* Parameters */
+ USHORT ParameterDisplacement; /* Specifies the offset from the start */
+ /* of the overall parameter block to */
+ /* the parameter bytes that are */
+ /* contained in this message */
+ USHORT DataCount; /* Data bytes sent this buffer */
+ USHORT DataOffset; /* Offset (from header start) to data */
+ USHORT DataDisplacement; /* Specifies the offset from the start */
+ /* of the overall data block to the */
+ /* data bytes that are contained in */
+ /* this message. */
+ UCHAR SetupCount; /* Count of setup words */
+ UCHAR Reserved3; /* Reserved (pad above to word) */
+ USHORT Setup[SetupCount]; /* Setup words */
+ USHORT ByteCount; /* Count of data bytes */
+ .align4 2;
+ union pctr[ParameterCount] {
+ case 0 R_TRANS2_P0 p0;
+ case 2 R_TRANS2_P2 p2;
+ case 10 R_TRANS2_FIND_FIRST2_STRUCT r10;
+ }
+ union dctr[DataCount] {
+ case 0 R_TRANS2_D0 d0;
+ case 0x24 R_TRANS2_FILE_BASIC_STRUCT r24;
+ case 0x14 SMB_QUERY_FS_ATTRIBUTE_INFO_STRUCT r14;
+ }
+} R_TRANS2_10;
+
+typedef struct {
+ USHORT ByteCount; /* Count of data bytes */
+} R_TRANS2_0;
+
+typedef struct _R_TRANS2 {
+ uint8 wcount;
+ union ctr[wcount] {
+ case 0 R_TRANS2_0 q0;
+ case 10 R_TRANS2_10 q10;
+ }
+} R_TRANS2;
+
+typedef struct _Q_TRANS_16 {
+ USHORT TotalParameterCount; /* Total parameter bytes being sent */
+ USHORT TotalDataCount; /* Total data bytes being sent */
+ USHORT MaxParameterCount; /* Max parameter bytes to return */
+ USHORT MaxDataCount; /* Max data bytes to return */
+ UCHAR MaxSetupCount; /* Max setup words to return */
+ UCHAR Reserved;
+ USHORT Flags; /* Additional information: */
+ /* bit 0 - also disconnect TID in TID */
+ ULONG Timeout;
+ USHORT Reserved2;
+ USHORT ParameterCount; /* Parameter bytes sent this buffer */
+ USHORT ParameterOffset; /* Offset (from header start) to */
+ /* Parameters */
+ USHORT DataCount; /* Data bytes sent this buffer */
+ USHORT DataOffset; /* Offset (from header start) to data */
+ UCHAR SetupCount; /* Count of setup words */
+ UCHAR Reserved3; /* Reserved (pad above to word) */
+ USHORT Setup[SetupCount]; /* Setup words (# = SetupWordCount) */
+ USHORT ByteCount; /* Count of data bytes */
+ STRING Name; /* Must be NULL */
+ .align4 0;
+ UCHAR Parameters[ParameterCount];/* Parameter bytes (# = ParameterCount) */
+ .align4 0;
+ UCHAR Data[DataCount]; /* Data bytes (# = DataCount) */
+
+} Q_TRANS_16;
+
+typedef struct _Q_TRANS {
+ uint8 wcount;
+ union ctr[wcount] {
+ case 16 Q_TRANS_16 q16;
+ }
+} Q_TRANS;
+
+typedef struct _R_TRANS_10 {
+ USHORT TotalParameterCount;/* Total parameter bytes being sent */
+ USHORT TotalDataCount; /* Total data bytes being sent */
+ USHORT Reserved2;
+ USHORT ParameterCount; /* Parameter bytes sent this buffer */
+ USHORT ParameterOffset; /* Offset (from header start) to */
+ /* Parameters */
+ USHORT ParameterDisplacement; /* Specifies the offset from the start */
+ /* of the overall parameter block to */
+ /* the parameter bytes that are */
+ /* contained in this message */
+ USHORT DataCount; /* Data bytes sent this buffer */
+ USHORT DataOffset; /* Offset (from header start) to data */
+ USHORT DataDisplacement; /* Specifies the offset from the start */
+ /* of the overall data block to the */
+ /* data bytes that are contained in */
+ /* this message. */
+ UCHAR SetupCount; /* Count of setup words */
+ UCHAR Reserved3; /* Reserved (pad above to word) */
+ USHORT Setup[SetupCount]; /* Setup words */
+ USHORT ByteCount; /* Count of data bytes */
+ .align4 0;
+ UCHAR Parameters[ParameterCount];/* Parameter bytes */
+ .align4 0;
+ UCHAR Data[DataCount]; /* Data bytes */
+} R_TRANS_10;
+
+typedef struct _R_TRANS {
+ uint8 wcount;
+ union ctr[wcount] {
+ case 10 R_TRANS_10 q10;
+ }
+} R_TRANS;
+
+typedef struct _Q_NT_CREATE_ANDX_24 {
+ ANDX_INFO andx;
+ uint8 reserved;
+ uint16 name_len;
+ ULONG flags;
+ ULONG rootfid;
+ ULONG access;
+ hyper allocsize;
+ ULONG attribs;
+ ULONG sharing;
+ ULONG creat_disp;
+ ULONG creat_options;
+ ULONG impersonation;
+ uint8 sec_flags;
+
+ uint16 count;
+ uint8 name[name_len];
+
+} Q_NTCREATE_ANDX_24;
+
+typedef struct _Q_NTCREATE_ANDX{
+ uint8 wcount;
+ union ctr[wcount] {
+ case 24 Q_NTCREATE_ANDX_24 q24;
+ }
+} Q_NTCREATE_ANDX;
+
+typedef struct {
+ ANDX_INFO andx;
+ uint8 oplock_level;
+ uint16 fid;
+ ULONG action;
+ TIME create_time;
+ TIME access_time;
+ TIME write_time;
+ TIME change_time;
+ ULONG ext_attribs;
+ hyper allocsize;
+ hyper size;
+ uint16 type;
+ uint16 state;
+ uint8 directory;
+
+ uint16 count;
+ uint8 none[count];
+
+} R_NTCREATE_ANDX_34;
+
+typedef struct _R_NTCREATE_ANDX{
+ uint8 wcount;
+ union ctr[wcount] {
+ case 34 R_NTCREATE_ANDX_34 q34;
+ }
+} R_NTCREATE_ANDX;
+
+typedef struct {
+ ULONG smbhdr;
+ uint8 com;
+ uint8 rcls;
+ uint8 reh;
+ uint16 err;
+ uint8 flg;
+ uint16 flg2;
+ uint16 reserved;
+ uint8 SecuritySignature[8];
+ uint16 pad;
+ uint16 tid;
+ uint16 pid;
+ uint16 uid;
+ uint16 mid;
+} SMB_HDR;
+
+typedef struct _R_SMB {
+ ULONG nbhdr;
+ SMB_HDR hdr;
+ union ctr[hdr.com] {
+ case 4 R_CLOSE r4;
+ case 6 R_UNLINK r6;
+ case 36 R_LOCKING_ANDX r36;
+ case 37 R_TRANS r37;
+ case 45 R_OPEN_ANDX r45;
+ case 46 R_READ_ANDX r46;
+ case 47 R_WRITE_ANDX r47;
+ case 50 R_TRANS2 q50;
+ case 113 R_TDIS r113;
+ case 114 R_NEGPROT r114;
+ case 115 R_SESSION_SETUP_ANDX r115;
+ case 116 R_ULOGOFF_ANDX r116;
+ case 117 R_TCON_ANDX r117;
+ case 128 R_DSKATTR r128;
+ case 160 R_NTTRANS r160;
+ case 162 R_NTCREATE_ANDX r162;
+ }
+} R_SMB;
+
+typedef struct _Q_SMB {
+ ULONG nbhdr;
+ SMB_HDR hdr;
+ union ctr[hdr.com] {
+ case 4 Q_CLOSE q4;
+ case 6 Q_UNLINK q6;
+ case 36 Q_LOCKING_ANDX q36;
+ case 37 Q_TRANS q37;
+ case 45 Q_OPEN_ANDX q45;
+ case 46 Q_READ_ANDX q46;
+ case 47 Q_WRITE_ANDX q47;
+ case 50 Q_TRANS2 q50;
+ case 113 Q_TDIS q113;
+ case 114 Q_NEGPROT q114;
+ case 115 Q_SESSION_SETUP_ANDX q115;
+ case 116 Q_ULOGOFF_ANDX q116;
+ case 117 Q_TCON_ANDX q117;
+ case 128 Q_DSKATTR q128;
+ case 160 Q_NTTRANS q160;
+ case 162 Q_NTCREATE_ANDX q162;
+ }
+} Q_SMB;
+
diff --git a/source/aparser/dump.awk b/source/aparser/dump.awk
new file mode 100644
index 00000000000..11bfb107e46
--- /dev/null
+++ b/source/aparser/dump.awk
@@ -0,0 +1,70 @@
+# dump the current parse tree
+
+
+function element_string(elnum,
+ LOCAL, elem)
+{
+ elem = elements[elnum, "elem"];
+ if (elements[elnum, "ptr"]=="1") elem="*"elem;
+ if (elements[elnum, "array_len"]!="")
+ elem=elem"["elements[elnum, "array_len"]"]";
+ if (elements[elnum, "switch"]!="")
+ elem=elem"["elements[elnum, "switch"]"]";
+ return elem;
+}
+
+function dump_element(f, elnum,
+ LOCAL, elem, type)
+{
+ type = elements[elnum, "type"];
+ case = elements[elnum, "case"];
+ elem = element_string(elnum);
+ if (case != "") {
+ xprintf(f,"\t\tcase %d %s %s;\n", case, type, elem);
+ } else {
+ xprintf(f,"\t%s %s;\n", type, elem);
+ }
+}
+
+function dump_union(f, elnum,
+ LOCAL, i)
+{
+ xprintf(f,"\tunion %s {\n", element_string(elnum));
+ for (i=0;i<unions[elnum, "num_elems"];i++) {
+ dump_element(f, unions[elnum, i]);
+ }
+ xprintf(f,"\t}\n");
+}
+
+function dump_elem(f, struct_num, elem_num,
+ LOCAL, enum)
+{
+ elnum = structs[struct_num, elem_num];
+
+ if (elements[elnum, "type"] == "union") {
+ dump_union(f, elnum);
+ } else {
+ dump_element(f, elnum);
+ }
+}
+
+function dump_structs(f, NIL,
+ LOCAL, i, j)
+{
+ xprintf(f,"/* dump of parsed structures */\n\n\n");
+
+ for (i=0;i < num_options;i++) {
+ xprintf(f,"option %s %s\n", options[i, "name"], options[i, "value"]);
+ }
+ xprintf(f,"\n\n");
+
+ for (i=0;i < num_structs;i++) {
+ xprintf(f,"/* structure %d */\n", i);
+ xprintf(f,"struct %s {\n", structs[i, "name"]);
+ for (j=0;j<structs[i, "num_elems"];j++) {
+ dump_elem(f, i, j);
+ }
+ xprintf(f,"};\n\n");
+ }
+ xprintf(f,"/* end dump */\n\n");
+}
diff --git a/source/aparser/harness.awk b/source/aparser/harness.awk
new file mode 100644
index 00000000000..6c4c35c40f2
--- /dev/null
+++ b/source/aparser/harness.awk
@@ -0,0 +1,17 @@
+function produce_harness(f,
+ LOCAL, v, struct_num, i)
+{
+ struct_num=structs[test];
+
+ v["MODULE"]=module;
+
+ print_template(f, "harness_start.tpl", v);
+
+ for (i=0;i<num_structs;i++) {
+ v["TEST"] = structs[i, "name"];
+ print_template(f, "harness.tpl", v);
+ }
+
+ print_template(f, "harness_end.tpl", v);
+}
+
diff --git a/source/aparser/header.awk b/source/aparser/header.awk
new file mode 100644
index 00000000000..ba7117436b8
--- /dev/null
+++ b/source/aparser/header.awk
@@ -0,0 +1,80 @@
+# produce a header file for a parsed struct file
+
+function header_elstring(elnum,
+ LOCAL, elem)
+{
+ array_len = elements[elnum, "array_len"];
+ elem=elements[elnum, "elem"];
+ if (elements[elnum, "ptr"]=="1") elem="*"elem;
+ if (array_len!="") {
+ if (is_constant(array_len) == 1) {
+ elem=elem"["array_len"]";
+ } else {
+ elem="*"elem;
+ }
+ }
+ return elem;
+}
+
+function header_element(f, elnum,
+ LOCAL, type)
+{
+ type=elements[elnum, "type"];
+ if (substr(type,1,1) == ".") return;
+ xprintf(f,"\t%s %s;\n", type, header_elstring(elnum));
+}
+
+function header_union(f, elnum,
+ LOCAL, i)
+{
+ xprintf(f,"\tunion {\n");
+ for (i=0;i<unions[elnum, "num_elems"];i++) {
+ header_element(f, unions[elnum, i]);
+ }
+ xprintf(f,"\t} %s;\n", header_elstring(elnum));
+}
+
+function header_elem(f, elnum)
+{
+
+ if (elements[elnum, "type"] == "union") {
+ header_union(f, elnum);
+ } else {
+ header_element(f, elnum);
+ }
+}
+
+function header_struct(f, struct_num,
+ LOCAL, i)
+{
+ xprintf(f,"/* structure %s */\n",
+ structs[struct_num, "name"]);
+ xprintf(f,"typedef struct {\n");
+ for (i=0;i < structs[struct_num, "num_elems"];i++) {
+ header_elem(f, structs[struct_num, i]);
+ }
+ xprintf(f,"} %s;\n\n\n", structs[struct_num, "name"]);
+}
+
+
+function produce_headers(f, NIL,
+ LOCAL, i)
+{
+ xprintf(f,"/* auto-generated headers for %s */\n\n\n", module);
+ xprintf(f,"#ifndef _%s_\n", module);
+ xprintf(f,"#define _%s_\n", module);
+
+ xprintf(f,"\n\n");
+ for (i=0;i < num_options;i++) {
+ xprintf(f,"#define OPTION_%s %s\n",
+ options[i, "name"], options[i, "value"]);
+ }
+ xprintf(f,"\n\n");
+
+ for (i=0;i < num_structs;i++) {
+ header_struct(f, i);
+ }
+ xprintf(f,"/* end auto-generated headers */\n\n");
+ xprintf(f,"#endif /* _%s_ */\n", module);
+}
+
diff --git a/source/aparser/main.awk b/source/aparser/main.awk
new file mode 100644
index 00000000000..4969f2217a2
--- /dev/null
+++ b/source/aparser/main.awk
@@ -0,0 +1,25 @@
+# the main program
+
+@include dump.awk
+@include header.awk
+@include util.awk
+@include template.awk
+#@include parsefn.awk
+@include parserel.awk
+@include harness.awk
+@include parsetree.awk
+@include token.awk
+
+END {
+ dump_structs("dump.out");
+ printf("Producing headers...\n");
+ produce_headers("prs_"module".h");
+# printf("Producing parsers...\n");
+# produce_parsers("prs_"module".c", "mod_"module".c");
+ printf("Producing relative parsers...\n");
+ produce_relative("prs_"module".c");
+ printf("Producing harness...\n");
+ produce_harness("test.h");
+ printf("Done.\n");
+ exit 0;
+}
diff --git a/source/aparser/parsefn.awk b/source/aparser/parsefn.awk
new file mode 100644
index 00000000000..2bebd765e66
--- /dev/null
+++ b/source/aparser/parsefn.awk
@@ -0,0 +1,271 @@
+# build parse functions for a parsed struct file
+
+function elem_name(v, elem)
+{
+ return v["UNION"]elem;
+}
+
+function parse_array(f, v, elnum, flags,
+ LOCAL, type, elem, array_len)
+{
+ type = elements[elnum, "type"];
+ elem = elements[elnum, "elem"];
+ array_len = elements[elnum, "array_len"];
+ v["ELEM"] = elem_name(v, elem);
+ v["TYPE"] = type;
+ v["FLAGS"] = flags;
+ v["ARRAY_LEN"] = array_len;
+
+ if (array_len=="+") {
+ print_template(f,"prs_array_optional.tpl", v);
+ return;
+ }
+
+ if (array_len=="*") {
+ print_template(f,"prs_array_remainder.tpl", v);
+ return;
+ }
+
+ if (type == "wchar" || type == "uint16") {
+ if (match(array_len,"[0-9]") == 1) {
+ print_template(f, "prs_wstring_fixed.tpl", v);
+ } else {
+ print_template(f, "prs_wstring.tpl", v);
+ }
+ } else if (type == "uint8") {
+ if (match(array_len,"[0-9]") == 1) {
+ print_template(f, "prs_uint8s_fixed.tpl", v);
+ } else {
+ print_template(f, "prs_uint8s.tpl", v);
+ }
+ } else {
+ print_template(f, "prs_array.tpl", v);
+ }
+}
+
+
+function parse_element(f, v, elnum, flags,
+ LOCAL, type, elem)
+{
+ if (elements[elnum,"nowire"] != "") {
+ return;
+ }
+ type = elements[elnum, "type"];
+ if (substr(type,1,1) == ".") return;
+ elem = elements[elnum, "elem"];
+ if (elements[elnum,"ptr"] == "") {
+ v["PTR"] = "\\&";
+ } else {
+ v["PTR"] = " ";
+ }
+ v["ELEM"] = elem_name(v, elem);
+ v["TYPE"] = type;
+ v["FLAGS"] = flags;
+ print_template(f, "prs_element.tpl", v);
+}
+
+function parse_union(f, v, elnum, flags,
+ LOCAL, i)
+{
+ v["UNION"] = elements[elnum, "elem"];
+ v["SWITCH"] = elements[elnum, "switch"];
+
+ if (elements[elnum, "ptr"] == "1") {
+ v["UNION"] = v["UNION"]"->";
+ } else {
+ v["UNION"] = v["UNION"]".";
+ }
+
+ print_template(f, "union_start.tpl", v);
+ for (i=0;i<unions[elnum, "num_elems"];i++) {
+ v["CASE"] = elements[unions[elnum, i], "case"];
+ print_template(f, "prs_case.tpl", v);
+ if (elements[elnum, "ptr"] == "1") {
+ parse_scalars(f, v, unions[elnum, i], "PARSE_SCALARS");
+ parse_buffers(f, v, unions[elnum, i], "PARSE_BUFFERS");
+ } else {
+ if (flags == "PARSE_SCALARS") {
+ parse_scalars(f, v, unions[elnum, i], flags);
+ } else {
+ parse_buffers(f, v, unions[elnum, i], flags);
+ }
+ }
+ print_template(f, "prs_break.tpl", v);
+ }
+ v["UNION"] = "";
+
+ print_template(f, "union_end.tpl", v);
+}
+
+function parse_scalar(f, v, elnum, flags)
+{
+ if (elements[elnum, "type"] == "union") {
+ parse_union(f, v, elnum, flags);
+ } else if (elements[elnum, "array_len"]!="") {
+ parse_array(f, v, elnum, flags);
+ } else {
+ parse_element(f, v, elnum, flags);
+ }
+}
+
+function parse_align2(f, v, elnum, flags,
+ LOCAL, elem)
+{
+ elem = elements[elnum, "elem"];
+ v["OFFSET"] = elem_name(v, elem);
+ print_template(f, "prs_align2.tpl", v);
+}
+
+function parse_align4(f, v, elnum, flags,
+ LOCAL, elem)
+{
+ elem = elements[elnum, "elem"];
+ v["OFFSET"] = elem_name(v, elem);
+ print_template(f, "prs_align4.tpl", v);
+}
+
+function parse_pointer(f, v, elnum, flags,
+ LOCAL, elem)
+{
+ elem = elements[elnum, "elem"];
+ v["ELEM"] = elem_name(v, elem);
+ v["FLAGS"] = flags;
+ print_template(f, "prs_pointer.tpl", v);
+}
+
+function parse_scalar_fn(m, v, elnum,
+ LOCAL, elem, type)
+{
+ elem = elements[elnum, "elem"];
+ type = elements[elnum, "type"]
+ xprintf(m, "%s %s", type, elem_name(v, elem));
+ if (type == "union") {
+ } else if (elements[elnum, "array_len"]!="") {
+ } else {
+ }
+}
+
+function parse_pointer_fn(f, v, elnum, flags,
+ LOCAL, elem)
+{
+ elem = elements[elnum, "elem"];
+ v["ELEM"] = elem_name(v, elem);
+ v["FLAGS"] = flags;
+ xprintf(m, "%s\n", v["ELEM"]);
+}
+
+function parse_scalars_fn(m, v, elnum, flags)
+{
+ if (elements[elnum, "type"] == ".align2") {
+ }
+ else if (elements[elnum, "type"] == ".align4") {
+ }
+ else if (elements[elnum, "ptr"] == "1") {
+ parse_pointer_fn(m, v, elnum, flags);
+ } else {
+ parse_scalar_fn(m, v, elnum, flags);
+ }
+}
+
+function parse_scalars(f, v, elnum, flags)
+{
+ if (elements[elnum, "type"] == ".align2") {
+ parse_align2(f, v, elnum, flags);
+ }
+ else if (elements[elnum, "type"] == ".align4") {
+ parse_align4(f, v, elnum, flags);
+ }
+ else if (elements[elnum, "ptr"] == "1") {
+ parse_pointer(f, v, elnum, flags);
+ } else {
+ parse_scalar(f, v, elnum, flags);
+ }
+}
+
+function parse_buffers(f, v, elnum, flags,
+ LOCAL, elem, type)
+{
+ elem = elements[elnum, "elem"];
+ type = elements[elnum, "type"];
+ v["ELEM"] = elem_name(v, elem);
+ if (elements[elnum, "type"] == ".align2") {
+ }
+ else if (elements[elnum, "type"] == ".align4") {
+ } else if (elements[elnum, "ptr"] == "1") {
+ print_template(f, "ifptr_start.tpl", v);
+ parse_scalar(f, v, elnum, "PARSE_SCALARS|PARSE_BUFFERS");
+ print_template(f, "ifptr_end.tpl", v);
+ } else {
+ parse_scalar(f, v, elnum, flags);
+ }
+}
+
+function struct_parser(f, m, v, struct_num,
+ LOCAL, i, n1, f1, num_elems)
+{
+ f1 = -1;
+ num_elems = structs[struct_num, "num_elems"];
+ v["STRUCTNAME"] = structs[struct_num, "name"];
+ v["FUNCNAME"] = "io_" v["STRUCTNAME"];
+ print_template(f, "fn_start.tpl", v);
+
+ for (n1=0;n1<num_elems;n1++) {
+ if (elements[structs[struct_num, n1], "type"] == ".trailer") {
+ f1 = n1;
+ break;
+ }
+ }
+
+ # first all the structure pointers, scalars and arrays
+ for (i=0;i<n1;i++) {
+ parse_scalars(f, v, structs[struct_num, i], "PARSE_SCALARS");
+ }
+
+ print_template(f, "fn_mid.tpl", v);
+
+ # now the buffers
+ for (i=0;i<n1;i++) {
+ parse_buffers(f, v, structs[struct_num, i], "PARSE_BUFFERS");
+ }
+
+ # and any trailers
+ for (i=n1;i<num_elems;i++) {
+ parse_scalars(f, v, structs[struct_num, i], "PARSE_SCALARS");
+ parse_buffers(f, v, structs[struct_num, i], "PARSE_BUFFERS");
+ }
+
+ if (i > 0) {
+ print_template(f, "fn_end.tpl", v);
+ }
+ else {
+ print_template(f, "fn_end0.tpl", v);
+ }
+
+ if (f1 == -1)
+ return;
+
+ xprintf(m, "void fn_%s(\n", v["STRUCTNAME"]);
+
+ for (i=f1+1;i<num_elems;i++) {
+ parse_scalars_fn(m, v, structs[struct_num, i]);
+ if (i != num_elems-1)
+ xprintf(m, ", \n");
+
+ }
+
+ xprintf(m, ")\n{\n}\n");
+}
+
+function produce_parsers(f, m,
+ LOCAL, v, i)
+{
+ v["MODULE"]=module;
+
+ print_template(f, "module_start.tpl", v);
+
+ for (i=0;i < num_structs;i++) {
+ struct_parser(f, m, v, i);
+ }
+
+ print_template(f, "module_end.tpl", v);
+}
diff --git a/source/aparser/parser.c b/source/aparser/parser.c
new file mode 100644
index 00000000000..0c7153e1fb7
--- /dev/null
+++ b/source/aparser/parser.c
@@ -0,0 +1,471 @@
+#include "parser.h"
+
+/*******************************************************************
+ Attempt, if needed, to grow a data buffer.
+ Also depends on the data stream mode (io).
+ ********************************************************************/
+
+BOOL io_grow(io_struct *ps, uint32 extra_space)
+{
+ uint32 new_size;
+ char *new_data;
+
+ ps->grow_size = MAX(ps->grow_size, ps->data_offset + extra_space);
+
+ if(ps->data_offset + extra_space <= ps->buffer_size)
+ return True;
+
+ /*
+ * We cannot grow the buffer if we're not reading
+ * into the io_struct, or if we don't own the memory.
+ */
+
+ if(UNMARSHALLING(ps) || !ps->is_dynamic) {
+ DEBUG(0,("io_grow: Buffer overflow - unable to expand buffer by %u bytes.\n",
+ (unsigned int)extra_space));
+ return False;
+ }
+
+ /*
+ * Decide how much extra space we really need.
+ */
+
+ extra_space -= (ps->buffer_size - ps->data_offset);
+ if(ps->buffer_size == 0) {
+ new_size = extra_space;
+
+ if((new_data = malloc(new_size)) == NULL) {
+ DEBUG(0,("io_grow: Malloc failure for size %u.\n", (unsigned int)new_size));
+ return False;
+ }
+ memset(new_data, '\0', new_size );
+ } else {
+ /*
+ * If the current buffer size is bigger than the space needed, just
+ * double it, else add extra_space.
+ */
+ new_size = MAX(ps->buffer_size*2, ps->buffer_size + extra_space);
+
+ if ((new_data = Realloc(ps->data_p, new_size)) == NULL) {
+ DEBUG(0,("io_grow: Realloc failure for size %u.\n",
+ (unsigned int)new_size));
+ return False;
+ }
+ }
+ ps->buffer_size = new_size;
+ ps->data_p = new_data;
+
+ return True;
+}
+
+
+/*******************************************************************
+ Ensure we can read/write to a given offset.
+ ********************************************************************/
+
+char *io_mem_get(io_struct *ps, uint32 extra_size)
+{
+ if(UNMARSHALLING(ps)) {
+ /*
+ * If reading, ensure that we can read the requested size item.
+ */
+ if (ps->data_offset + extra_size > ps->buffer_size) {
+ DEBUG(0,("io_mem_get: reading data of size %u would overrun buffer.\n",
+ (unsigned int)extra_size ));
+ return NULL;
+ }
+ } else {
+ /*
+ * Writing - grow the buffer if needed.
+ */
+ if(!io_grow(ps, extra_size))
+ return False;
+ }
+ return &ps->data_p[ps->data_offset];
+}
+
+/*******************************************************************
+ Initialise a parse structure - malloc the data if requested.
+ ********************************************************************/
+
+BOOL io_init(io_struct *ps, uint32 size, BOOL io)
+{
+ ZERO_STRUCTP(ps);
+ ps->io = io;
+ ps->bigendian_data = False;
+ ps->is_dynamic = False;
+ ps->data_offset = 0;
+ ps->buffer_size = 0;
+ ps->data_p = NULL;
+
+ if (size != 0) {
+ ps->buffer_size = size;
+ if((ps->data_p = (char *)malloc((size_t)size)) == NULL) {
+ DEBUG(0,("io_init: malloc fail for %u bytes.\n", (unsigned int)size));
+ return False;
+ }
+ ps->is_dynamic = True; /* We own this memory. */
+ }
+
+ return True;
+}
+
+/*******************************************************************
+ debug output for parsing info.
+
+ XXXX side-effect of this function is to increase the debug depth XXXX
+
+ ********************************************************************/
+void io_debug(io_struct *ps, int depth, char *desc, char *fn_name)
+{
+ DEBUG(5+depth, ("%s%06x %s %s\n", tab_depth(depth), ps->data_offset, fn_name, desc));
+}
+
+/*******************************************************************
+ Align a the data_len to a multiple of align bytes - filling with
+ zeros.
+ ********************************************************************/
+
+BOOL io_align2(io_struct *ps, int offset)
+{
+ uint32 mod = (ps->data_offset + offset) & (2-1);
+
+ if (mod != 0) {
+ uint32 extra_space = (2 - mod);
+ if(!io_grow(ps, extra_space))
+ return False;
+ memset(&ps->data_p[ps->data_offset], '\0', (size_t)extra_space);
+ ps->data_offset += extra_space;
+ }
+
+ return True;
+}
+
+BOOL io_align4(io_struct *ps, int offset)
+{
+ uint32 mod = (ps->data_offset + offset) & (4-1);
+
+ if (mod != 0) {
+ uint32 extra_space = (4 - mod);
+ if(!io_grow(ps, extra_space))
+ return False;
+ memset(&ps->data_p[ps->data_offset], '\0', (size_t)extra_space);
+ ps->data_offset += extra_space;
+ }
+
+ return True;
+}
+
+/*******************************************************************
+ Align a the data_len to a multiple of align bytes - filling with
+ zeros.
+ ********************************************************************/
+
+BOOL io_align(io_struct *ps, int align)
+{
+ uint32 mod;
+
+ if (!ps->autoalign) return True;
+
+ mod = ps->data_offset & (align-1);
+
+ if (align != 0 && mod != 0) {
+ uint32 extra_space = (align - mod);
+ if(!io_grow(ps, extra_space))
+ return False;
+ memset(&ps->data_p[ps->data_offset], '\0', (size_t)extra_space);
+ ps->data_offset += extra_space;
+ }
+
+ return True;
+}
+
+
+/*******************************************************************
+ read from a socket into memory.
+ ********************************************************************/
+BOOL io_read(io_struct *ps, int fd, size_t len, int timeout)
+{
+ BOOL ok;
+ size_t prev_size = ps->buffer_size;
+ if (!io_grow(ps, len))
+ {
+ return False;
+ }
+
+ if (timeout > 0)
+ {
+ ok = (read(fd, &ps->data_p[prev_size], len) == len);
+ }
+ else
+ {
+ ok = (read(fd, &ps->data_p[prev_size], len) == len);
+ }
+ return ok;
+}
+
+
+/*******************************************************************
+ do IO on a uint32.
+ ********************************************************************/
+BOOL io_uint32(char *name, io_struct *ps, int depth, uint32 *data32, unsigned flags)
+{
+ char *q;
+
+ if (!(flags & PARSE_SCALARS)) return True;
+
+ if (!io_align(ps, 4)) return False;
+
+ q = io_mem_get(ps, sizeof(uint32));
+ if (q == NULL) return False;
+
+ DBG_RW_IVAL(name, depth, ps->data_offset, ps->io, ps->bigendian_data, q, *data32)
+ ps->data_offset += sizeof(uint32);
+
+ return True;
+}
+
+/*******************************************************************
+ do IO on a uint16.
+ ********************************************************************/
+BOOL io_uint16(char *name, io_struct *ps, int depth, uint16 *data16, unsigned flags)
+{
+ char *q;
+
+ if (!(flags & PARSE_SCALARS)) return True;
+
+ if (!io_align(ps, 2)) return False;
+
+ q = io_mem_get(ps, sizeof(uint16));
+ if (q == NULL) return False;
+
+ DBG_RW_SVAL(name, depth, ps->data_offset, ps->io, ps->bigendian_data, q, *data16)
+ ps->data_offset += sizeof(uint16);
+
+ return True;
+}
+
+/*******************************************************************
+ do IO on a uint8.
+ ********************************************************************/
+BOOL io_uint8(char *name, io_struct *ps, int depth, uint8 *data8, unsigned flags)
+{
+ char *q;
+
+ if (!(flags & PARSE_SCALARS)) return True;
+
+ q = io_mem_get(ps, sizeof(uint8));
+ if (q == NULL) return False;
+
+ DBG_RW_IVAL(name, depth, ps->data_offset, ps->io, ps->bigendian_data, q, *data8)
+ ps->data_offset += sizeof(uint8);
+
+ return True;
+}
+
+/*******************************************************************
+ do IO on a pointer
+ ********************************************************************/
+BOOL io_pointer(char *desc, io_struct *ps, int depth, void **p, unsigned flags)
+{
+ uint32 v;
+
+ if (!(flags & PARSE_SCALARS)) return True;
+
+ v = (*p) ? 0xdeadbeef : 0;
+ if (!io_uint32(desc, ps, depth, &v, flags)) return False;
+ *p = (void *) (v ? 0xdeadbeef : 0);
+ return True;
+}
+
+/*******************************************************************
+ Stream a null-terminated string.
+ ********************************************************************/
+BOOL io_SMBSTR(char *name, io_struct *ps, int depth, char **str, unsigned flags)
+{
+ char *q;
+ uint8 *start;
+ int i;
+ size_t len;
+ int start_offset = ps->data_offset;
+
+ if (!(flags & PARSE_SCALARS)) return True;
+
+ if (UNMARSHALLING(ps)) {
+ *str = io_mem_get(ps, 0);
+ if (*str == NULL)
+ return False;
+ len = strlen(*str);
+ ps->data_offset += len + 1;
+ }
+ else
+ {
+ len = strlen(*str)+1;
+ start = (uint8*)q;
+
+ for(i = 0; i < len; i++) {
+ q = io_mem_get(ps, 1);
+ if (q == NULL)
+ return False;
+
+ RW_CVAL(ps->io, q, (*str)[i],0);
+ ps->data_offset++;
+ }
+ }
+
+ DEBUG(5,("%s%04x %s: %s\n", tab_depth(depth),
+ start_offset, name, *str));
+ return True;
+}
+
+/******************************************************************
+ do IO on a byte array
+ ********************************************************************/
+BOOL io_uint8s(char *name, io_struct *ps, int depth, uint8 **data8s, int len, unsigned flags)
+{
+ char *q;
+ size_t num_bytes = len * sizeof(uint8);
+
+ if (!(flags & PARSE_SCALARS)) return True;
+
+ q = io_mem_get(ps, num_bytes);
+ if (q == NULL) return False;
+
+ if (MARSHALLING(ps))
+ {
+ DBG_RW_PCVAL(True, name, depth, ps->data_offset, ps->io, q, *data8s, len)
+ }
+ else
+ {
+ *data8s = q;
+ dump_data(depth+5, *data8s, num_bytes);
+ }
+ ps->data_offset += num_bytes;
+
+ return True;
+}
+/******************************************************************
+ do IO on a fixed-size byte array
+ ********************************************************************/
+BOOL io_uint8s_fixed(char *name, io_struct *ps, int depth, uint8 *data8s, int len, unsigned flags)
+{
+ char *q;
+ size_t num_bytes = len * sizeof(uint8);
+
+ if (!(flags & PARSE_SCALARS)) return True;
+
+ q = io_mem_get(ps, num_bytes);
+ if (q == NULL) return False;
+
+ DBG_RW_PCVAL(True, name, depth, ps->data_offset, ps->io, q, data8s, len)
+ ps->data_offset += num_bytes;
+
+ return True;
+}
+
+
+/******************************************************************
+ do IO on an io (eh?? :)
+ ********************************************************************/
+BOOL io_io_struct(char *name, io_struct *ps, int depth, io_struct *io, unsigned flags)
+{
+ char *q;
+ uint16 len;
+
+ if (!(flags & PARSE_SCALARS)) return True;
+
+ q = io_mem_get(ps, sizeof(uint16));
+ if (q == NULL) return False;
+
+ /* length first */
+ if (MARSHALLING(ps))
+ {
+ len = io->data_offset;
+ }
+ if (!io_uint16("len", ps, depth+1, &len, flags))
+ {
+ return False;
+ }
+ if (UNMARSHALLING(ps))
+ {
+ if (!io_init(io, len, UNMARSHALL))
+ {
+ return False;
+ }
+ }
+
+ /* now data */
+ q = io_mem_get(ps, len * sizeof(uint8));
+ if (q == NULL) return False;
+
+ if (MARSHALLING(ps))
+ {
+ DBG_RW_PCVAL(False, name, depth+1, ps->data_offset, ps->io, q, io->data_p, len)
+ }
+ else
+ {
+ io->data_p = q;
+ dump_data(depth+5, q, len);
+ }
+ ps->data_offset += len;
+
+ return True;
+}
+
+/******************************************************************
+ do IO on a unicode array
+ ********************************************************************/
+BOOL io_wstring(char *name, io_struct *ps, int depth, uint16 *data16s, int len, unsigned flags)
+{
+ char *q;
+
+ if (!(flags & PARSE_SCALARS)) return True;
+
+ if (!io_align(ps, 2)) return False;
+
+ q = io_mem_get(ps, len * sizeof(uint16));
+ if (q == NULL) return False;
+
+ DBG_RW_PSVAL(True, name, depth, ps->data_offset, ps->io, ps->bigendian_data, q, data16s, len)
+ ps->data_offset += (len * sizeof(uint16));
+
+ return True;
+}
+
+
+/******************************************************************
+allocate some memory for a parse structure
+ ********************************************************************/
+void io_free(io_struct *ps)
+{
+ if (ps->is_dynamic && ps->data_p)
+ {
+ free(ps->data_p);
+ ps->data_p = NULL;
+ }
+}
+
+/******************************************************************
+allocate some memory for a parse structure
+ ********************************************************************/
+BOOL io_alloc(char *name, io_struct *ps, void **ptr, unsigned size)
+{
+ (*ptr) = (void *)malloc(size);
+ if (*ptr) return True;
+ return False;
+}
+
+/******************************************************************
+realloc some memory for a parse structure
+ ********************************************************************/
+BOOL io_realloc(char *name, io_struct *ps, void **ptr, unsigned size)
+{
+ BOOL ret = True;
+ void *tp;
+
+ tp = (void *)Realloc(*ptr, size);
+ if (tp) *ptr = tp;
+ else ret = False;
+ return ret;
+}
+
diff --git a/source/aparser/parser.h b/source/aparser/parser.h
new file mode 100644
index 00000000000..319aeb5d138
--- /dev/null
+++ b/source/aparser/parser.h
@@ -0,0 +1,103 @@
+#include <ctype.h>
+#include <stdio.h>
+#include <malloc.h>
+#include <unistd.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include "../include/byteorder.h"
+
+#define PARSE_SCALARS (1<<0)
+#define PARSE_BUFFERS (1<<1)
+
+#ifndef MIN
+#define MIN(a,b) ((a)<(b)?(a):(b))
+#endif
+
+#ifndef MAX
+#define MAX(a,b) ((a)>(b)?(a):(b))
+#endif
+
+#define DEBUG(lvl, str) printf str;
+#define DEBUGADD(lvl, str) printf str;
+
+#define MARSHALL 0
+#define UNMARSHALL 1
+
+#define MARSHALLING(ps) (!(ps)->io)
+#define UNMARSHALLING(ps) ((ps)->io)
+
+typedef int BOOL;
+typedef unsigned char uint8;
+typedef unsigned char uchar;
+typedef unsigned short uint16;
+typedef unsigned short wchar;
+typedef unsigned uint32;
+typedef char *SMBSTR;
+
+/* a null terminated unicode string */
+typedef uint16 ZUSTRING;
+
+#ifndef _PSTRING
+
+#define PSTRING_LEN 1024
+#define FSTRING_LEN 128
+
+typedef char pstring[PSTRING_LEN];
+typedef char fstring[FSTRING_LEN];
+
+#define _PSTRING
+
+#endif
+#define False 0
+#define True 1
+
+/* zero a structure given a pointer to the structure */
+#define ZERO_STRUCTP(x) do { if ((x) != NULL) memset((char *)(x), 0, sizeof(*(x))); } while(0)
+
+#define MAX_UNISTRLEN 256
+#define MAX_STRINGLEN 256
+#define MAX_BUFFERLEN 512
+
+typedef struct _io_struct
+{
+ BOOL io; /* parsing in or out of data stream */
+ /*
+ * If the (incoming) data is big-endian. On output we are
+ * always little-endian.
+ */
+ BOOL bigendian_data;
+ BOOL is_dynamic; /* Do we own this memory or not ? */
+ BOOL autoalign; /* should we auto-align all elements? */
+ uint32 data_offset; /* Current working offset into data. */
+ uint32 buffer_size; /* Current size of the buffer. */
+ uint32 grow_size; /* size requested via io_grow() calls */
+ char *data_p; /* The buffer itself. */
+} io_struct;
+
+
+char *io_mem_get(io_struct *ps, uint32 extra_size);
+BOOL io_init(io_struct *ps, uint32 size, BOOL io);
+void io_debug(io_struct *ps, int depth, char *desc, char *fn_name);
+BOOL io_align(io_struct *ps, int align);
+BOOL io_align4(io_struct *ps, int align);
+BOOL io_align2(io_struct *ps, int align);
+BOOL io_read(io_struct *ps, int fd, size_t len, int timeout);
+void dump_data(int level,char *buf1,int len);
+BOOL io_alloc(char *name, io_struct *ps, void **ptr, unsigned size);
+BOOL io_uint32(char *name, io_struct *ps, int depth, uint32 *data32, unsigned flags);
+BOOL io_uint16(char *name, io_struct *ps, int depth, uint16 *data16, unsigned flags);
+BOOL io_uint8(char *name, io_struct *ps, int depth, uint8 *data8, unsigned flags);
+BOOL io_pointer(char *desc, io_struct *ps, int depth, void **p, unsigned flags);
+BOOL io_SMBSTR(char *name, io_struct *ps, int depth, char **str, unsigned flags);
+BOOL io_io_struct(char *name, io_struct *ps, int depth, io_struct *io, unsigned flags);
+BOOL io_wstring(char *name, io_struct *ps, int depth, uint16 *data16s, int len, unsigned flags);
+BOOL io_uint8s_fixed(char *name, io_struct *ps, int depth, uint8 *data8s, int len, unsigned flags);
+BOOL io_uint8s(char *name, io_struct *ps, int depth, uint8 **data8s, int len, unsigned flags);
+
+char *tab_depth(int depth);
+void *Realloc(void *p,size_t size);
+void dump_data(int level,char *buf1,int len);
+void print_asc(int level, uchar const *buf, int len);
+BOOL io_ZUSTRING(char *name, io_struct *ps, int depth, uint16 **ustr, unsigned flags);
+size_t strlen_w(void *src);
+
diff --git a/source/aparser/parserel.awk b/source/aparser/parserel.awk
new file mode 100644
index 00000000000..6d80f0607e4
--- /dev/null
+++ b/source/aparser/parserel.awk
@@ -0,0 +1,213 @@
+# build parse functions for a parsed struct file
+
+function elem_name(v, elem)
+{
+ return v["UNION"]elem;
+}
+
+function parse_array(f, v, elnum, flags,
+ LOCAL, type, elem, array_len)
+{
+ type = elements[elnum, "type"];
+ elem = elements[elnum, "elem"];
+ array_len = elements[elnum, "array_len"];
+ v["ELEM"] = elem_name(v, elem);
+ v["TYPE"] = type;
+ v["FLAGS"] = flags;
+ v["ARRAY_LEN"] = array_len;
+
+ if (array_len=="+") {
+ print_template(f,"prs_array_optional.tpl", v);
+ return;
+ }
+
+ if (array_len=="&") {
+ print_template(f,"prs_array_null.tpl", v);
+ return;
+ }
+
+ if (array_len=="*") {
+ print_template(f,"prs_array_remainder.tpl", v);
+ return;
+ }
+
+ if (type == "wchar" || type == "uint16") {
+ if (match(array_len,"[0-9]") == 1) {
+ print_template(f, "prs_wstring_fixed.tpl", v);
+ } else {
+ print_template(f, "prs_wstring.tpl", v);
+ }
+ } else if (type == "uint8") {
+ if (match(array_len,"[0-9]") == 1) {
+ print_template(f, "prs_uint8s_fixed.tpl", v);
+ } else {
+ print_template(f, "prs_uint8s.tpl", v);
+ }
+ } else {
+ print_template(f, "prs_array.tpl", v);
+ }
+}
+
+
+function parse_element(f, v, elnum, flags,
+ LOCAL, type, elem)
+{
+ if (elements[elnum,"nowire"] != "") {
+ return;
+ }
+ type = elements[elnum, "type"];
+ if (substr(type,1,1) == ".") return;
+ elem = elements[elnum, "elem"];
+ if (elements[elnum,"ptr"] == "") {
+ v["PTR"] = "\\&";
+ } else {
+ v["PTR"] = " ";
+ }
+ v["ELEM"] = elem_name(v, elem);
+ v["TYPE"] = type;
+ v["FLAGS"] = flags;
+ print_template(f, "prs_element.tpl", v);
+}
+
+function parse_union(f, v, elnum, flags,
+ LOCAL, i)
+{
+ v["UNION"] = elements[elnum, "elem"];
+ v["SWITCH"] = elements[elnum, "switch"];
+
+ if (elements[elnum, "ptr"] == "1") {
+ v["UNION"] = v["UNION"]"->";
+ } else {
+ v["UNION"] = v["UNION"]".";
+ }
+
+ print_template(f, "union_start.tpl", v);
+ for (i=0;i<unions[elnum, "num_elems"];i++) {
+ v["CASE"] = elements[unions[elnum, i], "case"];
+ print_template(f, "prs_case.tpl", v);
+ if (elements[elnum, "ptr"] == "1") {
+ parse_scalars(f, v, unions[elnum, i], "PARSE_SCALARS");
+ parse_buffers(f, v, unions[elnum, i], "PARSE_BUFFERS");
+ } else {
+ if (flags == "PARSE_SCALARS") {
+ parse_scalars(f, v, unions[elnum, i], flags);
+ } else {
+ parse_buffers(f, v, unions[elnum, i], flags);
+ }
+ }
+ print_template(f, "prs_break.tpl", v);
+ }
+ v["UNION"] = "";
+
+ print_template(f, "union_end.tpl", v);
+}
+
+function parse_scalar(f, v, elnum, flags)
+{
+ if (elements[elnum, "type"] == "union") {
+ parse_union(f, v, elnum, flags);
+ } else if (elements[elnum, "array_len"]!="") {
+ parse_array(f, v, elnum, flags);
+ } else {
+ parse_element(f, v, elnum, flags);
+ }
+}
+
+function parse_pointer(f, v, elnum, flags,
+ LOCAL, elem)
+{
+ elem = elements[elnum, "elem"];
+ v["ELEM"] = elem_name(v, elem);
+ v["FLAGS"] = flags;
+ print_template(f, "prs_pointer.tpl", v);
+}
+
+function parse_scalars(f, v, elnum, flags)
+{
+ if (elements[elnum, "ptr"] == "1") {
+ parse_pointer(f, v, elnum, flags);
+ } else {
+ parse_scalar(f, v, elnum, flags);
+ }
+}
+
+function parse_buffers(f, v, elnum, flags,
+ LOCAL, elem, type)
+{
+ elem = elements[elnum, "elem"];
+ type = elements[elnum, "type"];
+ v["ELEM"] = elem_name(v, elem);
+ if (elements[elnum, "ptr"] == "1") {
+ print_template(f, "ifptr_start.tpl", v);
+ parse_scalar(f, v, elnum, "PARSE_SCALARS|PARSE_BUFFERS");
+ print_template(f, "ifptr_end.tpl", v);
+ } else {
+ parse_scalar(f, v, elnum, flags);
+ }
+}
+
+function struct_immediate(f, v, struct_num,
+ LOCAL, i, n1, num_elems)
+{
+ num_elems = structs[struct_num, "num_elems"];
+ v["STRUCTNAME"] = structs[struct_num, "name"];
+ v["FUNCNAME"] = "io_" v["STRUCTNAME"];
+
+ print_template(f, "fn_i_start.tpl", v);
+
+ for (i=0;i<num_elems;i++) {
+ parse_scalars(f, v, structs[struct_num, i], "PARSE_SCALARS");
+ parse_buffers(f, v, structs[struct_num, i], "PARSE_BUFFERS");
+ }
+
+ print_template(f, "fn_i_end.tpl", v);
+}
+
+
+function struct_recursive(f, v, struct_num,
+ LOCAL, i, n1, num_elems)
+{
+ num_elems = structs[struct_num, "num_elems"];
+ v["STRUCTNAME"] = structs[struct_num, "name"];
+ v["FUNCNAME"] = "io_" v["STRUCTNAME"];
+
+ print_template(f, "fn_start.tpl", v);
+
+# first all the structure pointers, scalars and arrays
+ for (i=0;i<num_elems;i++) {
+ parse_scalars(f, v, structs[struct_num, i], "PARSE_SCALARS");
+ }
+
+ print_template(f, "fn_mid.tpl", v);
+
+# now the buffers
+ for (i=0;i<num_elems;i++) {
+ parse_buffers(f, v, structs[struct_num, i], "PARSE_BUFFERS");
+ }
+
+ print_template(f, "fn_end.tpl", v);
+}
+
+function struct_parser(f, v, struct_num,
+ LOCAL, i, n1, num_elems)
+{
+ if (structs[struct_num, "recurse"] == "True") {
+ struct_recursive(f, v, struct_num);
+ } else {
+ struct_immediate(f, v, struct_num);
+ }
+}
+
+function produce_relative(f,
+ LOCAL, v, i)
+{
+ v["MODULE"]=module;
+
+ print_template(f, "module_start.tpl", v);
+
+ for (i=0;i < num_structs;i++) {
+ struct_parser(f, v, i);
+ }
+
+ print_template(f, "module_end.tpl", v);
+}
diff --git a/source/aparser/parsetree.awk b/source/aparser/parsetree.awk
new file mode 100644
index 00000000000..80587a01116
--- /dev/null
+++ b/source/aparser/parsetree.awk
@@ -0,0 +1,224 @@
+# build the parse tree for a struct file
+
+function find_structure(name,
+ LOCAL, i)
+{
+ for (i=0;i<num_structs;i++) {
+ if (structs[i, "name"] == name) return i;
+ }
+ return "-1";
+}
+
+function start_module(name)
+{
+ module=name;
+ num_structs=0;
+ num_elements=0;
+ num_unions=0;
+ num_tests=0;
+ num_options=0;
+}
+
+function set_option(name, value)
+{
+ options[name] = value;
+ options[num_options, "name"] = name;
+ options[num_options, "value"] = value;
+ num_options++;
+}
+
+function parse_define(def1, def2,
+ LOCAL, type, i)
+{
+ defines[def1]=def2;
+}
+
+function start_struct(name)
+{
+ current_struct=num_structs;
+ structs[name]=current_struct;
+ structs[current_struct, "name"]=name;
+ structs[current_struct, "num_elems"]=0;
+ structs[current_struct, "num_unions"]=0;
+ structs[current_struct, "recurse"] = options["recurse"];
+}
+
+function end_struct(name)
+{
+ if (name!="") structs[num_structs, "name"]=name;
+ printf("struct %s with %d elements\n",
+ structs[num_structs, "name"],
+ structs[num_structs, "num_elems"]);
+ num_structs++;
+ current_struct="";
+}
+
+function add_element(type, elem, case,
+ LOCAL, elem_num, i, v)
+{
+ while (defines[type]!="") {
+ type=defines[type];
+ }
+ elem_num=num_elements;
+
+ if (substr(elem, 1, 1) == ".") {
+ elem=substr(elem, 2);
+ elements[elem_num, "nowire"]=1;
+ }
+
+ if (substr(elem, 1, 1) == "*") {
+ elem=substr(elem, 2);
+ elements[elem_num, "ptr"]=1;
+ }
+
+ i=match(elem,"[[]");
+ if (i != 0) {
+ v = substr(elem, i+1, length(elem)-i-1);
+ elem=substr(elem, 1, i-1);
+ if (type=="union") {
+ elements[elem_num, "switch"] = v;
+ } else {
+ elements[elem_num, "array_len"] = v;
+ }
+ }
+
+ elements[elem_num, "type"] = type;
+ elements[elem_num, "elem"] = elem;
+ elements[elem_num, "case"] = case;
+
+ num_elements++;
+ return elem_num;
+}
+
+function add_struct_elem(type, elem, case,
+ LOCAL, elem_num)
+{
+ elem_num=structs[current_struct, "num_elems"];
+ structs[current_struct, elem_num] = add_element(type, elem, case);
+ structs[current_struct, "num_elems"]++;
+ return structs[current_struct, elem_num];
+}
+
+function start_union(elem)
+{
+ current_union = add_struct_elem("union", elem);
+ unions[current_union, "num_elems"] = 0;
+}
+
+function start_union_notencap(switch)
+{
+ add_struct_elem("uint32", "switch_"switch);
+ start_union("UNKNOWN[switch_"switch"]");
+}
+
+function start_union_encap(struct, type, switch, union)
+{
+ start_struct(struct);
+ add_struct_elem(type, switch);
+ add_struct_elem(type, "switch_"switch);
+ start_union(union"[switch_"switch"]");
+ encap_union="1";
+}
+
+function parse_case(case, type, elem,
+ LOCAL, elem_num)
+{
+ split(case, a, "[:]");
+ case = a[1];
+ elem_num = unions[current_union, "num_elems"];
+ unions[current_union, elem_num] = add_element(type, elem, case);
+ unions[current_union, "num_elems"]++;
+}
+
+function end_union(name)
+{
+ if (name!="") {
+ elements[current_union, "elem"] = name;
+ }
+ current_union="";
+ if (encap_union=="1") {
+ end_struct(name);
+ encap_union="0";
+ }
+}
+
+function delete_element(struct, elnum,
+ LOCAL, i)
+{
+ for (i=elnum;i<structs[struct,"num_elems"]-1;i++) {
+ structs[struct, i] = structs[struct, i+1];
+ }
+ structs[struct, "num_elems"]--;
+}
+
+function copy_struct(from, to,
+ LOCAL, i)
+{
+ for (i=0;i<structs[from,"num_elems"];i++) {
+ structs[to, i] = structs[from, i];
+ }
+ structs[to, "name"] = structs[from, "name"];
+ structs[to, "num_elems"] = structs[from, "num_elems"];
+ structs[to, "num_unions"] = structs[from, "num_unions"];
+}
+
+function add_sizeis_array(count, type, elem)
+{
+ copy_struct(current_struct, current_struct+1);
+ elem=substr(elem,2);
+ start_struct("array_"current_struct"_"elem);
+ add_struct_elem("uint32", count);
+ add_struct_elem(type, elem"["count"]");
+ end_struct("");
+ current_struct=num_structs;
+ add_struct_elem("array_"current_struct-1"_"elem, "*"elem"_ptr");
+}
+
+
+function start_function(type, fname)
+{
+ start_struct(fname);
+ structs[current_struct, "recurse"] = "False";
+}
+
+function end_function(LOCAL, i)
+{
+ copy_struct(num_structs, num_structs+1);
+ structs[num_structs, "name"] = "Q_"structs[num_structs, "name"];
+ for (i=0;i<structs[num_structs, "num_elems"];i++) {
+ if (match(elements[structs[num_structs, i], "properties"], "in") == 0) {
+ delete_element(num_structs, i);
+ i--;
+ }
+ }
+ end_struct();
+ current_struct=num_structs;
+ structs[num_structs, "name"] = "R_"structs[num_structs, "name"];
+ for (i=0;i<structs[num_structs, "num_elems"];i++) {
+ if (match(elements[structs[num_structs, i], "properties"], "out") == 0) {
+ delete_element(num_structs, i);
+ i--;
+ }
+ }
+ if (return_result!="void")
+ add_function_param("[out]", return_result, "status");
+ end_struct();
+}
+
+function add_function_param(properties, type, elem,
+ LOCAL, elnum, len)
+{
+ len=length(type);
+ if (substr(type, len) == "*") {
+ type=substr(type, 1, len-1);
+ elem="*"elem;
+ }
+ if (substr(elem,1,1) == "*" &&
+ (match(properties,"in") == 0 ||
+ find_structure(type) != "-1")) {
+ elem=substr(elem, 2);
+ }
+ elnum = add_struct_elem(type, elem);
+ elements[elnum, "properties"] = properties;
+}
+
diff --git a/source/aparser/spool.struct b/source/aparser/spool.struct
new file mode 100644
index 00000000000..1563ba5be07
--- /dev/null
+++ b/source/aparser/spool.struct
@@ -0,0 +1,90 @@
+module spool
+
+struct BUFFER5 {
+ uint32 buf_len;
+ uint16 buffer[buf_len];
+};
+
+struct BUFFERP {
+ uint32 buf_len;
+ BUFFER5 *buf;
+};
+
+struct UNISTR2 {
+ uint32 max_len;
+ uint32 undoc;
+ uint32 str_len;
+ uint16 buffer[str_len];
+};
+
+struct LPWSTR {
+ UNISTR2 *str;
+};
+
+struct VERSION {
+ uint32 version;
+ uint32 build;
+ uint32 osversion;
+};
+
+struct NTTIME {
+ uint32 low;
+ uint32 high;
+};
+
+struct DWORD {
+ uint32 x;
+};
+
+struct PRINTER_DRIVER_INFO_LEVEL_3 {
+ DWORD cversion;
+ LPWSTR name;
+ LPWSTR environment;
+ LPWSTR driverpath;
+ LPWSTR datafile;
+ LPWSTR configfile;
+ LPWSTR helpfile;
+ LPWSTR monitorname;
+ LPWSTR defaultdatatype;
+ BUFFERP dependentfiles;
+};
+
+struct PRINTER_DRIVER_INFO_LEVEL_6 {
+ DWORD dummy1;
+ DWORD version;
+ LPWSTR name;
+ LPWSTR environment;
+ LPWSTR driverpath;
+ LPWSTR datafile;
+ LPWSTR configfile;
+ LPWSTR helpfile;
+ LPWSTR monitorname;
+ LPWSTR defaultdatatype;
+ BUFFERP dependentfiles;
+ BUFFERP previousnames;
+ NTTIME driverdate;
+ VERSION driverversion;
+ LPWSTR mfgname;
+ LPWSTR oemurl;
+ LPWSTR hardwareid;
+ LPWSTR provider;
+};
+
+
+struct PRINTER_DRIVER_INFO {
+ uint32 level;
+ union *info[level] {
+ case 3 PRINTER_DRIVER_INFO_LEVEL_3 info_3;
+ case 6 PRINTER_DRIVER_INFO_LEVEL_6 info_6;
+ }
+};
+
+
+struct R_GETPRINTERDATA {
+ uint32 type;
+ uint32 size;
+ uint8 *data;
+ uint32 needed;
+ uint32 status;
+};
+
diff --git a/source/aparser/spool_io_printer_driver_info_level_3.prs b/source/aparser/spool_io_printer_driver_info_level_3.prs
new file mode 100644
index 00000000000..baa32ef7c7b
--- /dev/null
+++ b/source/aparser/spool_io_printer_driver_info_level_3.prs
Binary files differ
diff --git a/source/aparser/spool_io_printer_driver_info_level_6.prs b/source/aparser/spool_io_printer_driver_info_level_6.prs
new file mode 100644
index 00000000000..3c5a47e7a3c
--- /dev/null
+++ b/source/aparser/spool_io_printer_driver_info_level_6.prs
Binary files differ
diff --git a/source/aparser/srvsvc.struct b/source/aparser/srvsvc.struct
new file mode 100644
index 00000000000..aa40c8f15e4
--- /dev/null
+++ b/source/aparser/srvsvc.struct
@@ -0,0 +1,184 @@
+module srvsvc
+
+typedef uint32 LONG;
+typedef uint32 *ENUM_HND;
+
+typedef struct _UNISTR2 {
+ uint32 max_len;
+ uint32 undoc;
+ uint32 str_len;
+ wchar buffer[str_len];
+} UNISTR2;
+
+typedef UNISTR2 *LPWSTR;
+
+/* function 8 */
+struct CONN_INFO_0 {
+ uint32 id; /* connection id. */
+};
+
+struct CONN_INFO_1 {
+ uint32 id;
+ uint32 type;
+ uint32 num_opens;
+ uint32 num_users;
+ uint32 open_time;
+ LPWSTR usr_name;
+ LPWSTR net_name;
+};
+
+struct CONN_ENUM_CTR {
+ uint32 level;
+ uint32 level2;
+ uint32 num_entries;
+ uint32 num_entries2;
+ union *info[level] {
+ case 0 CONN_INFO_0 info0[num_entries];
+ case 1 CONN_INFO_1 info1[num_entries];
+ }
+};
+
+struct SRV_R_NET_CONN_ENUM {
+ .trailer;
+ CONN_ENUM_CTR ctr;
+ uint32 num_entries;
+ ENUM_HND handle;
+ uint32 status2;
+};
+
+struct SRV_Q_NET_CONN_ENUM {
+ .trailer;
+ LPWSTR dest_srv;
+ LPWSTR qual_srv;
+ uint32 level;
+ uint32 level2;
+ CONN_ENUM_CTR *ctr;
+ uint32 max_len;
+ ENUM_HND handle;
+};
+
+/* function 9 */
+struct FILE_INFO_3 {
+ uint32 id; /* file index */
+ uint32 perms; /* file permissions. don't know what format */
+ uint32 num_locks; /* file locks */
+ LPWSTR path_name; /* file name */
+ LPWSTR user_name; /* file owner */
+};
+
+struct SRV_FILE_INFO_CTR {
+ uint32 level;
+ uint32 num_entries;
+ uint32 dummy;
+ union *file[level] {
+ case 3 FILE_INFO_3 info3[num_entries];
+ }
+};
+
+struct SRV_Q_NET_FILE_ENUM {
+ .trailer;
+ LPWSTR srv_name;
+ LPWSTR qual_name;
+ uint32 dummy;
+ uint32 level;
+ SRV_FILE_INFO_CTR ctr;
+ uint32 *status;
+ uint32 preferred_len;
+ ENUM_HND enum_hnd;
+};
+
+
+struct SRV_R_NET_FILE_ENUM {
+ .trailer;
+ uint32 level;
+ uint32 dummy;
+ SRV_FILE_INFO_CTR *ctr;
+ uint32 total_entries; /* total number of files */
+ ENUM_HND enum_hnd;
+ uint32 status; /* return status */
+};
+
+
+/* function 15 */
+struct SRV_SHARE_INFO_1 {
+ LPWSTR uni_netname;
+ uint32 type;
+ LPWSTR uni_remark;
+};
+
+struct SRV_SHARE_INFO_2 {
+ LPWSTR uni_netname;
+ uint32 type;
+ LPWSTR uni_remark;
+ uint32 perms;
+ uint32 max_uses;
+ uint32 num_uses;
+ LPWSTR path;
+ LPWSTR passwd;
+};
+
+struct SRV_R_NET_SHARE_ENUM {
+ uint32 level;
+ uint32 level2;
+ uint32 *ret_count;
+ uint32 num_entries;
+ union *info[level] {
+ case 1 SRV_SHARE_INFO_1 info1[num_entries];
+ case 2 SRV_SHARE_INFO_2 info2[num_entries];
+ }
+ .trailer;
+ uint32 count;
+ ENUM_HND handle;
+ uint32 status;
+};
+
+
+
+/* function 21 */
+struct SERVER_INFO_100 {
+ uint32 dwPlatformID;
+ LPWSTR pszName;
+};
+
+struct SERVER_INFO_101 {
+ uint32 dwPlatformID;
+ LPWSTR pszName;
+ uint32 dwVerMajor;
+ uint32 dwVerMinor;
+ uint32 dwType;
+ LPWSTR pszComment;
+};
+
+struct SERVER_INFO_102 {
+ uint32 dwPlatformID;
+ LPWSTR pszName;
+ uint32 dwVerMajor;
+ uint32 dwVerMinor;
+ uint32 dwType;
+ LPWSTR pszComment;
+ uint32 dwUsers;
+ uint32 lDisc;
+ uint32 bHidden;
+ uint32 dwAnnounce;
+ uint32 dwAnnDelta;
+ uint32 dwLicenses;
+ LPWSTR pszUserPath;
+};
+
+struct SRV_R_NET_SERVER_INFO {
+ .trailer;
+ uint32 level;
+ union *info[level] {
+ case 100 SERVER_INFO_100 sv100;
+ case 101 SERVER_INFO_101 sv101;
+ case 102 SERVER_INFO_102 sv102;
+ }
+ uint32 status;
+};
+
+struct SRV_Q_NET_SERVER_INFO {
+ .trailer;
+ LPWSTR server;
+ uint32 level;
+};
+
diff --git a/source/aparser/srvsvc2.struct b/source/aparser/srvsvc2.struct
new file mode 100644
index 00000000000..362d121e378
--- /dev/null
+++ b/source/aparser/srvsvc2.struct
@@ -0,0 +1,655 @@
+module srvsvc
+
+option autoalign True
+option relative False
+option recurse True
+option foo blah
+
+#define BOOL uint32
+#define LONG uint32
+#define DWORD uint32
+#define STATUS uint32
+
+typedef struct _UNISTR2 {
+ uint32 max_len;
+ uint32 undoc;
+ uint32 str_len;
+ wchar buffer[str_len];
+} UNISTR2;
+
+struct LPWSTR {
+ UNISTR2 *str;
+};
+
+
+
+ /* -- CHARACTER DEVICE INFORMATION -- */
+
+ typedef struct _CHARDEV_INFO_0 {
+ LPWSTR pszName;
+ } CHARDEV_INFO_0;
+
+ typedef struct _CHARDEV_INFO_1 {
+ LPWSTR pszName;
+ DWORD dwStatus;
+ LPWSTR pszUser;
+ DWORD dwTime;
+ } CHARDEV_INFO_1;
+
+ typedef union _CHARDEV_INFO switch (DWORD dwLevel) ctr {
+ case 1: CHARDEV_INFO_0 *ci0;
+ case 2: CHARDEV_INFO_1 *ci1;
+ } CHARDEV_INFO;
+
+ typedef struct _CHARDEV_ENUM_0 {
+ DWORD dwEntries;
+ [size_is(dwEntries)] CHARDEV_INFO_0 *ci0;
+ } CHARDEV_ENUM_0;
+
+ typedef struct _CHARDEV_ENUM_1 {
+ DWORD dwEntries;
+ [size_is(dwEntries)] CHARDEV_INFO_1 *ci1;
+ } CHARDEV_ENUM_1;
+
+ typedef struct _CHARDEV_ENUM {
+ DWORD dwLevel;
+ [switch_is(dwLevel)] union {
+ [case(0)] CHARDEV_ENUM_0 *ce0;
+ [case(1)] CHARDEV_ENUM_1 *ce1;
+ } ctr;
+ } CHARDEV_ENUM;
+
+ STATUS NetrCharDevEnum( /* Function 0x00 */
+ [in,unique] LPWSTR pszServer,
+ [in,out] CHARDEV_ENUM* pCharDevEnum,
+ [in] DWORD dwMaxLen,
+ [out] DWORD* dwEntries,
+ [in,out] DWORD* hResume
+ );
+
+ STATUS NetrCharDevGetInfo( /* Function 0x01 */
+ [in,unique] LPWSTR pszServer,
+ [in,ref] LPWSTR pszDevice,
+ [in] DWORD dwLevel,
+ [out] CHARDEV_INFO* pCharDevInfo
+ );
+
+ STATUS NetrCharDevControl( /* Function 0x02 */
+ [in,unique] LPWSTR pszServer,
+ [in,ref] LPWSTR pszDevice,
+ [in] DWORD dwOpcode
+ );
+
+ /* -- CHARACTER DEVICE QUEUE INFORMATION -- */
+
+ typedef struct _CHARDEVQ_INFO_0 {
+ LPWSTR pszName;
+ } CHARDEVQ_INFO_0;
+
+ typedef struct _CHARDEVQ_INFO_1 {
+ LPWSTR pszName;
+ DWORD dwPriority;
+ LPWSTR pszDevices;
+ DWORD dwNumUsers;
+ DWORD dwNumAhead;
+ } CHARDEVQ_INFO_1;
+
+ typedef union _CHARDEVQ_INFO switch (DWORD dwLevel) ctr {
+ case 1: CHARDEVQ_INFO_0 *ci0;
+ case 2: CHARDEVQ_INFO_1 *ci1;
+ } CHARDEVQ_INFO;
+
+ typedef struct _CHARDEVQ_ENUM_0 {
+ DWORD dwEntries;
+ [size_is(dwEntries)] CHARDEVQ_INFO_0 *ci0;
+ } CHARDEVQ_ENUM_0;
+
+ typedef struct _CHARDEVQ_ENUM_1 {
+ DWORD dwEntries;
+ [size_is(dwEntries)] CHARDEVQ_INFO_1 *ci1;
+ } CHARDEVQ_ENUM_1;
+
+ typedef struct _CHARDEVQ_ENUM {
+ DWORD dwLevel;
+ [switch_is(dwLevel)] union {
+ [case(0)] CHARDEVQ_ENUM_0 *ce0;
+ [case(1)] CHARDEVQ_ENUM_1 *ce1;
+ } ctr;
+ } CHARDEVQ_ENUM;
+
+ STATUS NetrCharDevQEnum( /* Function 0x03 */
+ [in,unique] LPWSTR pszServer,
+ [in,unique] LPWSTR pszUser,
+ [in,out] CHARDEVQ_ENUM* pCharDevQEnum,
+ [in] DWORD dwMaxLen,
+ [out] DWORD* dwEntries,
+ [in,out] DWORD* hResume
+ );
+
+ STATUS NetrCharDevQGetInfo( /* Function 0x04 */
+ [in,unique] LPWSTR pszServer,
+ [in,ref] LPWSTR pszQueue,
+ [in,ref] LPWSTR pszUser,
+ [in] DWORD dwLevel,
+ [out] CHARDEVQ_INFO* pCharDevQInfo
+ );
+
+ STATUS NetrCharDevQSetInfo( /* Function 0x05 */
+ [in,unique] LPWSTR pszServer,
+ [in,ref] LPWSTR pszQueue,
+ [in] DWORD dwLevel,
+ [in] CHARDEVQ_INFO* pCharDevQInfo,
+ [in,out] DWORD* dwParmError
+ );
+
+ STATUS NetrCharDevQPurge( /* Function 0x06 */
+ [in,unique] LPWSTR pszServer,
+ [in,ref] LPWSTR pszQueue
+ );
+
+ STATUS NetrCharDevQPurgeSelf( /* Function 0x07 */
+ [in,unique] LPWSTR pszServer,
+ [in,ref] LPWSTR pszQueue,
+ [in,ref] LPWSTR pszComputer
+ );
+
+ /* -- CONNECTION INFORMATION -- */
+
+ typedef struct _CONNECTION_INFO_0 {
+ DWORD dwConnID;
+ } CONNECTION_INFO_0;
+
+ typedef struct _CONNECTION_INFO_1 {
+ DWORD dwConnID;
+ DWORD dwType;
+ DWORD dwNumOpens;
+ DWORD dwNumUsers;
+ DWORD dwTime;
+ LPWSTR pszUser;
+ LPWSTR pszShare;
+ } CONNECTION_INFO_1;
+
+ typedef struct _CONNECTION_ENUM_0 {
+ DWORD dwEntries;
+ [size_is(dwEntries)] CONNECTION_INFO_0 *ci0;
+ } CONNECTION_ENUM_0;
+
+ typedef struct _CONNECTION_ENUM_1 {
+ DWORD dwEntries;
+ [size_is(dwEntries)] CONNECTION_INFO_1 *ci1;
+ } CONNECTION_ENUM_1;
+
+ typedef struct _CONNECTION_ENUM {
+ DWORD dwLevel;
+ [switch_is(dwLevel)] union {
+ [case(0)] CONNECTION_ENUM_0 *ce0;
+ [case(1)] CONNECTION_ENUM_1 *ce1;
+ } ctr;
+ } CONNECTION_ENUM;
+
+ STATUS NetrConnectionEnum( /* Function 0x08 */
+ [in,unique] LPWSTR pszServer,
+ [in,unique] LPWSTR pszClient,
+ [in,out] CONNECTION_ENUM* pConnectionEnum,
+ [in] DWORD dwMaxLen,
+ [out] DWORD* dwEntries,
+ [in,out] DWORD* hResume
+ );
+
+ /* -- FILE INFORMATION -- */
+
+ typedef struct _FILE_INFO_2 {
+ DWORD dwFileID;
+ } FILE_INFO_2;
+
+ typedef struct _FILE_INFO_3 {
+ DWORD dwFileID;
+ DWORD dwPermissions;
+ DWORD dwNumLocks;
+ LPWSTR pszPath;
+ LPWSTR pszUser;
+ } FILE_INFO_3;
+
+ typedef union _FILE_INFO switch (DWORD dwLevel) ctr {
+ case 2: FILE_INFO_2 *fi2;
+ case 3: FILE_INFO_3 *fi3;
+ } FILE_INFO;
+
+ typedef struct _FILE_ENUM_2 {
+ DWORD dwEntries;
+ [size_is(dwEntries)] FILE_INFO_2 *fi2;
+ } FILE_ENUM_2;
+
+ typedef struct _FILE_ENUM_3 {
+ DWORD dwEntries;
+ [size_is(dwEntries)] FILE_INFO_3 *fi3;
+ } FILE_ENUM_3;
+
+ typedef struct _FILE_ENUM {
+ DWORD dwLevel;
+ [switch_is(dwLevel)] union {
+ [case(2)] FILE_ENUM_2 *fe2;
+ [case(3)] FILE_ENUM_3 *fe3;
+ } ctr;
+ } FILE_ENUM;
+
+ STATUS NetrFileEnum( /* Function 0x09 */
+ [in,unique] LPWSTR pszServer,
+ [in,unique] LPWSTR pszBasePath,
+ [in,unique] LPWSTR pszUser,
+ [in,out] FILE_ENUM* pFileEnum,
+ [in] DWORD dwMaxLen,
+ [out] DWORD* dwEntries,
+ [in,out] DWORD* hResume
+ );
+
+ STATUS NetrFileGetInfo( /* Function 0x0A */
+ [in,unique] LPWSTR pszServer,
+ [in] DWORD dwFileID,
+ [in] DWORD dwLevel,
+ [out] FILE_INFO* pFileInfo
+ );
+
+ STATUS NetrFileClose( /* Function 0x0B */
+ [in,unique] LPWSTR pszServer,
+ [in] DWORD dwFileID
+ );
+
+ /* -- SESSION INFORMATION -- */
+
+ typedef struct _SESSION_INFO_0 {
+ LPWSTR pszClient;
+ } SESSION_INFO_0;
+
+ typedef struct _SESSION_INFO_1 {
+ LPWSTR pszClient;
+ LPWSTR pszUser;
+ DWORD dwOpens;
+ DWORD dwTime;
+ DWORD dwIdleTime;
+ DWORD dwUserFlags;
+ } SESSION_INFO_1;
+
+ typedef struct _SESSION_INFO_2 {
+ LPWSTR pszClient;
+ LPWSTR pszUser;
+ DWORD dwOpens;
+ DWORD dwTime;
+ DWORD dwIdleTime;
+ DWORD dwUserFlags;
+ LPWSTR pszClientType;
+ } SESSION_INFO_2;
+
+ typedef struct _SESSION_ENUM_0 {
+ DWORD dwEntries;
+ [size_is(dwEntries)] SESSION_INFO_0 *si0;
+ } SESSION_ENUM_0;
+
+ typedef struct _SESSION_ENUM_1 {
+ DWORD dwEntries;
+ [size_is(dwEntries)] SESSION_INFO_1 *si1;
+ } SESSION_ENUM_1;
+
+ typedef struct _SESSION_ENUM_2 {
+ DWORD dwEntries;
+ [size_is(dwEntries)] SESSION_INFO_2 *si2;
+ } SESSION_ENUM_2;
+
+ typedef struct _SESSION_ENUM {
+ DWORD dwLevel;
+ [switch_is(dwLevel)] union {
+ [case(0)] SESSION_ENUM_0 *se0;
+ [case(1)] SESSION_ENUM_1 *se1;
+ [case(2)] SESSION_ENUM_2 *se2;
+ } ctr;
+ } SESSION_ENUM;
+
+ STATUS NetrSessionEnum( /* Function 0x0C */
+ [in,unique] LPWSTR pszServer,
+ [in,unique] LPWSTR pszClient,
+ [in,unique] LPWSTR pszUser,
+ [in,out] SESSION_ENUM* pFileEnum,
+ [in] DWORD dwMaxLen,
+ [out] DWORD* dwEntries,
+ [in,out] DWORD* hResume
+ );
+
+ STATUS NetrSessionDel( /* Function 0x0D */
+ [in,unique] LPWSTR pszServer,
+ [in,ref] LPWSTR pszClient,
+ [in,ref] LPWSTR pszUser
+ );
+
+ /* -- SHARE INFORMATION -- */
+
+ typedef struct _SHARE_INFO_0 {
+ LPWSTR pszName;
+ } SHARE_INFO_0;
+
+ typedef struct _SHARE_INFO_1 {
+ LPWSTR pszName;
+ DWORD dwType;
+ LPWSTR pszComment;
+ } SHARE_INFO_1;
+
+ typedef struct _SHARE_INFO_2 {
+ LPWSTR pszName;
+ DWORD dwType;
+ LPWSTR pszComment;
+ DWORD dwPermissions;
+ DWORD dwMaxUses;
+ DWORD dwCurrentUses;
+ LPWSTR pszPath;
+ LPWSTR pszPasswd;
+ } SHARE_INFO_2;
+
+ typedef union _SHARE_INFO switch (DWORD dwLevel) ctr {
+ case 0: SHARE_INFO_0 *si0;
+ case 1: SHARE_INFO_1 *si1;
+ case 2: SHARE_INFO_2 *si2;
+ } SHARE_INFO;
+
+ typedef struct _SHARE_ENUM_0 {
+ DWORD dwEntries;
+ [size_is(dwEntries)] SHARE_INFO_0 *si0;
+ } SHARE_ENUM_0;
+
+ typedef struct _SHARE_ENUM_1 {
+ DWORD dwEntries;
+ [size_is(dwEntries)] SHARE_INFO_1 *si1;
+ } SHARE_ENUM_1;
+
+ typedef struct _SHARE_ENUM_2 {
+ DWORD dwEntries;
+ [size_is(dwEntries)] SHARE_INFO_2 *si2;
+ } SHARE_ENUM_2;
+
+ typedef struct _SHARE_ENUM {
+ DWORD dwLevel;
+ [switch_is(dwLevel)] union {
+ [case(0)] SHARE_ENUM_0 *se0;
+ [case(1)] SHARE_ENUM_1 *se1;
+ [case(2)] SHARE_ENUM_2 *se2;
+ } ctr;
+ } SHARE_ENUM;
+
+ STATUS NetrShareAdd( /* Function 0x0E */
+ [in,unique] LPWSTR pszServer,
+ [in] DWORD dwLevel,
+ [out] SHARE_INFO* pShareInfo,
+ [in,out] DWORD* dwParmError
+ );
+
+ STATUS NetrShareEnum( /* Function 0x0F */
+ [in,unique] LPWSTR pszServer,
+ [in,out] SHARE_ENUM* pShareEnum,
+ [in] DWORD dwMaxLen,
+ [out] DWORD* dwEntries,
+ [in,out] DWORD* hResume
+ );
+
+ STATUS NetrShareGetInfo( /* Function 0x10 */
+ [in,unique] LPWSTR pszServer,
+ [in,ref] LPWSTR pszShare,
+ [in] DWORD dwLevel,
+ [out] SHARE_INFO* pShareInfo
+ );
+
+ STATUS NetrShareSetInfo( /* Function 0x11 */
+ [in,unique] LPWSTR pszServer,
+ [in,ref] LPWSTR pszShare,
+ [in] DWORD dwLevel,
+ [in] SHARE_INFO* pShareInfo,
+ [in] DWORD dwReserved
+ );
+
+ STATUS NetrShareDel( /* Function 0x12 */
+ [in,unique] LPWSTR pszServer,
+ [in,ref] LPWSTR pszShare,
+ [in] DWORD dwReserved
+ );
+
+ STATUS NetrShareDelSticky( /* Function 0x13 */
+ [in,unique] LPWSTR pszServer,
+ [in,ref] LPWSTR pszShare,
+ [in] DWORD dwReserved
+ );
+
+ STATUS NetrShareCheck( /* Function 0x14 */
+ [in,unique] LPWSTR pszServer,
+ [in,ref] LPWSTR pszDevice,
+ [out] DWORD* dwType
+ );
+
+ /* --- SERVER INFORMATION --- */
+
+ typedef struct _SERVER_INFO_100 {
+ DWORD dwPlatformID;
+ LPWSTR pszName;
+ } SERVER_INFO_100;
+
+ typedef struct _SERVER_INFO_101 {
+ DWORD dwPlatformID;
+ LPWSTR pszName;
+ DWORD dwVerMajor;
+ DWORD dwVerMinor;
+ DWORD dwType;
+ LPWSTR pszComment;
+ } SERVER_INFO_101;
+
+ typedef struct _SERVER_INFO_102 {
+ DWORD dwPlatformID;
+ LPWSTR pszName;
+ DWORD dwVerMajor;
+ DWORD dwVerMinor;
+ DWORD dwType;
+ LPWSTR pszComment;
+ DWORD dwUsers;
+ LONG lDisc;
+ BOOL bHidden;
+ DWORD dwAnnounce;
+ DWORD dwAnnDelta;
+ DWORD dwLicenses;
+ LPWSTR pszUserPath;
+ } SERVER_INFO_102;
+
+ typedef union _SERVER_INFO switch (DWORD dwLevel) ctr {
+ case 100: SERVER_INFO_100 *sv100;
+ case 101: SERVER_INFO_101 *sv101;
+ case 102: SERVER_INFO_102 *sv102;
+ } SERVER_INFO;
+
+ STATUS NetrServerGetInfo( /* Function 0x15 */
+ [in,unique] LPWSTR pszServerName,
+ [in] DWORD dwLevel,
+ [out] SERVER_INFO* pServerInfo
+ );
+
+ STATUS NetrServerSetInfo( /* Function 0x16 */
+ [in,unique] LPWSTR pszServerName,
+ [in] DWORD dwLevel,
+ [in] SERVER_INFO* pServerInfo,
+ [in] DWORD dwReserved
+ );
+
+ typedef struct _DISK_INFO {
+ LPWSTR pszName;
+ } DISK_INFO;
+
+ typedef struct _DISK_ENUM {
+ DWORD dwEntries;
+ [size_is(dwEntries)] DISK_INFO *di;
+ } DISK_ENUM;
+
+ STATUS NetrServerDiskEnum( /* Function 0x17 */
+ [in,unique] LPWSTR pszServer,
+ [in] DWORD dwLevel,
+ [in,out] DISK_ENUM* pDiskEnum,
+ [in] DWORD dwMaxLen,
+ [out] DWORD* dwEntries,
+ [in,out] DWORD* hResume
+ );
+
+ typedef struct _STAT_SERVER {
+ DWORD dwStart;
+ DWORD dwFOpens;
+ DWORD dwDevOpens;
+ DWORD dwJobsQueued;
+ DWORD dwSOpens;
+ DWORD dwSTimedOut;
+ DWORD dwSErrors;
+ DWORD dwPWErrors;
+ DWORD dwPermErrors;
+ DWORD dwSysErrors;
+ DWORD dwBytesSentLow;
+ DWORD dwBytesSentHigh;
+ DWORD dwBytesRcvdLow;
+ DWORD dwBytesRcvdHigh;
+ DWORD dwAVResponse;
+ DWORD dwReqBufNeed;
+ DWORD dwBigBufNeed;
+ } STAT_SERVER;
+
+ STATUS NetrServerStatisticsGet( /* Function 0x18 */
+ [in,unique] LPWSTR pszServer,
+ [in] DWORD dwLevel,
+ [in] DWORD dwOptions,
+ [out] STAT_SERVER* pStatServer
+ );
+
+ typedef struct _TRANSPORT_INFO_0 {
+ LPWSTR pszName;
+ } TRANSPORT_INFO_0;
+
+ typedef union _TRANSPORT_INFO switch (DWORD dwLevel) ctr {
+ case 0: TRANSPORT_INFO_0 *ti0;
+ } TRANSPORT_INFO;
+
+ typedef struct _TRANSPORT_ENUM_0 {
+ DWORD dwEntries;
+ [size_is(dwEntries)] TRANSPORT_INFO_0 *ti0;
+ } TRANSPORT_ENUM_0;
+
+ typedef struct _TRANSPORT_ENUM {
+ DWORD dwLevel;
+ [switch_is(dwLevel)] union {
+ [case(0)] TRANSPORT_ENUM_0 *te0;
+ } ctr;
+ } TRANSPORT_ENUM;
+
+ STATUS NetrServerTransportAdd( /* Function 0x19 */
+ [in,unique] LPWSTR pszServer,
+ [in] DWORD dwLevel,
+ [out] TRANSPORT_INFO* pTransportInfo
+ );
+
+ STATUS NetrServerTransportEnum( /* Function 0x1a */
+ [in,unique] LPWSTR pszServer,
+ [in,out] TRANSPORT_ENUM* pTransportEnum,
+ [in] DWORD dwMaxLen,
+ [out] DWORD* dwEntries,
+ [in,out] DWORD* hResume
+ );
+
+ STATUS NetrServerTransportDel( /* Function 0x1b */
+ [in,unique] LPWSTR pszServer,
+ [in] DWORD dwLevel,
+ [out] TRANSPORT_INFO* pTransportInfo
+ );
+
+ typedef struct _TIME_OF_DAY {
+ DWORD dwElapsedTime;
+ DWORD dwMsecs;
+ DWORD dwHours;
+ DWORD dwMins;
+ DWORD dwSecs;
+ DWORD dwHunds;
+ LONG lTimeZone;
+ DWORD dwInterval;
+ DWORD dwDay;
+ DWORD dwMonth;
+ DWORD dwYear;
+ DWORD dwWeekday;
+ } TIME_OF_DAY;
+
+ STATUS NetrRemoteTOD( /* Function 0x1c */
+ [in,unique] LPWSTR pszServer,
+ [out] TIME_OF_DAY* pTOD
+ );
+
+ STATUS NetrServerSetServiceBits( /* Function 0x1d */
+ [in,unique] LPWSTR pszServer,
+ [in] DWORD hServiceStatus, /* ?? */
+ [in] DWORD dwServiceBits,
+ [in] BOOL bSetBitsOn,
+ [in] BOOL bUpdateImmediately
+ );
+
+ /* --- PATH INFORMATION --- */
+
+ STATUS NetprPathType( /* Function 0x1e */
+ void /* Not known */
+ );
+
+ STATUS NetprPathCanonicalize( /* Function 0x1f */
+ void /* Not known */
+ );
+
+ STATUS NetprPathCompare( /* Function 0x20 */
+ void /* Not known */
+ );
+
+ STATUS NetprNameValidate( /* Function 0x21 */
+ void /* Not known */
+ );
+
+ STATUS NetprNameCanonicalize( /* Function 0x22 */
+ void /* Not known */
+ );
+
+ STATUS NetprNameCompare( /* Function 0x23 */
+ void /* Not known */
+ );
+
+ /* --- LATER ADDITIONS --- */
+
+ STATUS NetrShareEnumSticky( /* Function 0x24 */
+ [in,unique] LPWSTR pszServer,
+ [in,out] SHARE_ENUM* pShareEnum,
+ [in] DWORD dwMaxLen,
+ [out] DWORD* dwEntries,
+ [in,out] DWORD* hResume
+ );
+
+ STATUS NetrShareDelStart( /* Function 0x25 */
+ [in,unique] LPWSTR pszServer,
+ [in,ref] LPWSTR pszShare,
+ [in] DWORD dwReserved /* ? */
+ );
+
+ STATUS NetrShareDelCommit( /* Function 0x26 */
+ [in,unique] LPWSTR pszServer
+ );
+
+ STATUS NetrpGetFileSecurity( /* Function 0x27 */
+ void /* Not known */
+ );
+
+ STATUS NetrpSetFileSecurity( /* Function 0x28 */
+ void /* Not known */
+ );
+
+ STATUS NetrServerTransportAddEx( /* Function 0x29 */
+ [in,unique] LPWSTR pszServer,
+ [in] DWORD dwLevel,
+ [out] TRANSPORT_INFO* pTransportInfo
+ );
+
+ STATUS NetrServerSetServiceBitsEx( /* Function 0x30 */
+ [in,unique] LPWSTR pszServer,
+ [in] DWORD hServiceStatus, /* ?? */
+ [in] DWORD dwServiceBits,
+ [in] BOOL bSetBitsOn,
+ [in] BOOL bUpdateImmediately
+ );
+
diff --git a/source/aparser/template.awk b/source/aparser/template.awk
new file mode 100644
index 00000000000..20157008354
--- /dev/null
+++ b/source/aparser/template.awk
@@ -0,0 +1,18 @@
+# template file handling
+
+function print_template(f, tplname, v,
+ LOCAL, i, pat, line)
+{
+ tplname="templates/"tplname;
+ if (numlines(tplname) <= 0) fatal("no template "tplname);
+ while ((getline line < tplname) > 0) {
+ while ((i = match(line,"@[a-zA-Z_]*@")) != 0) {
+ pat=substr(line,i+1,RLENGTH-2);
+ if (v[pat] == "") fatal("no value for "pat" in "tplname);
+ gsub("@"pat"@", v[pat], line);
+ }
+
+ xprintf(f, "%s\n", line);
+ }
+ close(tplname);
+}
diff --git a/source/aparser/templates/fn_end.tpl b/source/aparser/templates/fn_end.tpl
new file mode 100644
index 00000000000..2275ec4e33c
--- /dev/null
+++ b/source/aparser/templates/fn_end.tpl
@@ -0,0 +1,13 @@
+
+end:
+ /* the parse is OK */
+ return True;
+
+fail:
+ if (UNMARSHALLING(ps)) {
+ ZERO_STRUCTP(il);
+ }
+ return False;
+} /* @FUNCNAME@ */
+
+
diff --git a/source/aparser/templates/fn_end0.tpl b/source/aparser/templates/fn_end0.tpl
new file mode 100644
index 00000000000..6e49a10f538
--- /dev/null
+++ b/source/aparser/templates/fn_end0.tpl
@@ -0,0 +1,8 @@
+
+end:
+ /* the parse is OK */
+ return True;
+
+} /* @FUNCNAME@ */
+
+
diff --git a/source/aparser/templates/fn_i_end.tpl b/source/aparser/templates/fn_i_end.tpl
new file mode 100644
index 00000000000..9de61decb38
--- /dev/null
+++ b/source/aparser/templates/fn_i_end.tpl
@@ -0,0 +1,12 @@
+
+ /* the parse is OK */
+ return True;
+
+fail:
+ if (UNMARSHALLING(ps)) {
+ ZERO_STRUCTP(il);
+ }
+ return False;
+} /* @FUNCNAME@ */
+
+
diff --git a/source/aparser/templates/fn_i_start.tpl b/source/aparser/templates/fn_i_start.tpl
new file mode 100644
index 00000000000..3979d78e7db
--- /dev/null
+++ b/source/aparser/templates/fn_i_start.tpl
@@ -0,0 +1,15 @@
+/*******************************************************************
+parse a @STRUCTNAME@ structure
+********************************************************************/
+BOOL @FUNCNAME@(char *desc, io_struct *ps, int depth,
+ @STRUCTNAME@ *il, unsigned flags)
+{
+ io_debug(ps, depth, desc, "@FUNCNAME@");
+ depth++;
+
+#if 0
+ if (UNMARSHALLING(ps)) {
+ ZERO_STRUCTP(il);
+ }
+#endif
+ /* parse the scalars */
diff --git a/source/aparser/templates/fn_mid.tpl b/source/aparser/templates/fn_mid.tpl
new file mode 100644
index 00000000000..b81de92a5bd
--- /dev/null
+++ b/source/aparser/templates/fn_mid.tpl
@@ -0,0 +1,6 @@
+
+buffers:
+ if (!(flags & PARSE_BUFFERS)) goto end;
+
+ /* now parse the buffers */
+
diff --git a/source/aparser/templates/fn_start.tpl b/source/aparser/templates/fn_start.tpl
new file mode 100644
index 00000000000..a5d58767a6c
--- /dev/null
+++ b/source/aparser/templates/fn_start.tpl
@@ -0,0 +1,17 @@
+/*******************************************************************
+parse a @STRUCTNAME@ structure
+********************************************************************/
+BOOL @FUNCNAME@(char *desc, io_struct *ps, int depth,
+ @STRUCTNAME@ *il, unsigned flags)
+{
+ io_debug(ps, depth, desc, "@FUNCNAME@");
+ depth++;
+
+ if (!(flags & PARSE_SCALARS)) goto buffers;
+
+#if 0
+ if (UNMARSHALLING(ps)) {
+ ZERO_STRUCTP(il);
+ }
+#endif
+ /* parse the scalars */
diff --git a/source/aparser/templates/harness.tpl b/source/aparser/templates/harness.tpl
new file mode 100644
index 00000000000..27c33c0adc1
--- /dev/null
+++ b/source/aparser/templates/harness.tpl
@@ -0,0 +1,5 @@
+
+ if (strcmp(test,"@TEST@")==0) {
+ @TEST@ il;
+ ret = io_@TEST@("@TEST@", ps, 0, &il, flags);
+ } else
diff --git a/source/aparser/templates/harness_end.tpl b/source/aparser/templates/harness_end.tpl
new file mode 100644
index 00000000000..1e15faec167
--- /dev/null
+++ b/source/aparser/templates/harness_end.tpl
@@ -0,0 +1,7 @@
+ {
+ printf("structure %s not found\n", test);
+ ret = False;
+ }
+
+ return ret;
+}
diff --git a/source/aparser/templates/harness_start.tpl b/source/aparser/templates/harness_start.tpl
new file mode 100644
index 00000000000..beba6fc12de
--- /dev/null
+++ b/source/aparser/templates/harness_start.tpl
@@ -0,0 +1,7 @@
+#include "prs_@MODULE@.c"
+
+static BOOL run_test(char *test, io_struct *ps, int flags)
+{
+ BOOL ret;
+
+
diff --git a/source/aparser/templates/ifptr_end.tpl b/source/aparser/templates/ifptr_end.tpl
new file mode 100644
index 00000000000..990635cf453
--- /dev/null
+++ b/source/aparser/templates/ifptr_end.tpl
@@ -0,0 +1 @@
+ }
diff --git a/source/aparser/templates/ifptr_start.tpl b/source/aparser/templates/ifptr_start.tpl
new file mode 100644
index 00000000000..228b84bac93
--- /dev/null
+++ b/source/aparser/templates/ifptr_start.tpl
@@ -0,0 +1,2 @@
+ if (il->@ELEM@) {
+ if (!io_alloc("@ELEM@", ps, (void **)&il->@ELEM@, sizeof(*(il->@ELEM@)))) goto fail;
diff --git a/source/aparser/templates/module_end.tpl b/source/aparser/templates/module_end.tpl
new file mode 100644
index 00000000000..661f7edb95d
--- /dev/null
+++ b/source/aparser/templates/module_end.tpl
@@ -0,0 +1,3 @@
+
+
+/* end auto-generated structure parsers for @MODULE@ */
diff --git a/source/aparser/templates/module_start.tpl b/source/aparser/templates/module_start.tpl
new file mode 100644
index 00000000000..ac6a3c9d98d
--- /dev/null
+++ b/source/aparser/templates/module_start.tpl
@@ -0,0 +1,5 @@
+/* auto-generated structure parsers for @MODULE@
+ generated by aparser
+*/
+#include "prs_@MODULE@.h"
+
diff --git a/source/aparser/templates/prs_.align.tpl b/source/aparser/templates/prs_.align.tpl
new file mode 100644
index 00000000000..25816a23b34
--- /dev/null
+++ b/source/aparser/templates/prs_.align.tpl
@@ -0,0 +1 @@
+ if(!io_align(ps)) goto fail;
diff --git a/source/aparser/templates/prs_align2.tpl b/source/aparser/templates/prs_align2.tpl
new file mode 100644
index 00000000000..54c569b547b
--- /dev/null
+++ b/source/aparser/templates/prs_align2.tpl
@@ -0,0 +1 @@
+ if (!io_align2(ps, @OFFSET@)) goto fail;
diff --git a/source/aparser/templates/prs_align4.tpl b/source/aparser/templates/prs_align4.tpl
new file mode 100644
index 00000000000..702fab13243
--- /dev/null
+++ b/source/aparser/templates/prs_align4.tpl
@@ -0,0 +1 @@
+ if (!io_align4(ps, @OFFSET@)) goto fail;
diff --git a/source/aparser/templates/prs_array.tpl b/source/aparser/templates/prs_array.tpl
new file mode 100644
index 00000000000..4bd6a26c99c
--- /dev/null
+++ b/source/aparser/templates/prs_array.tpl
@@ -0,0 +1,8 @@
+ if ((@FLAGS@ & PARSE_SCALARS) &&
+ !io_alloc("@ELEM@", ps, (void **)&il->@ELEM@, sizeof(*(il->@ELEM@))*(il->@ARRAY_LEN@))) goto fail;
+ {
+ int i;
+ for (i=0;i<il->@ARRAY_LEN@;i++) {
+ if (!io_@TYPE@("@ELEM@...", ps, depth+1, &il->@ELEM@[i], @FLAGS@)) goto fail;
+ }
+ }
diff --git a/source/aparser/templates/prs_array_optional.tpl b/source/aparser/templates/prs_array_optional.tpl
new file mode 100644
index 00000000000..38bd32861f7
--- /dev/null
+++ b/source/aparser/templates/prs_array_optional.tpl
@@ -0,0 +1,5 @@
+ if ((MARSHALLING(ps) && il->@ELEM@) ||
+ ps->data_offset < ps->buffer_size) {
+ if (!io_alloc("@ELEM@", ps, (void **)&il->@ELEM@, sizeof(*(il->@ELEM@)))) goto fail;
+ if (!io_@TYPE@("@ELEM@...", ps, depth+1, il->@ELEM@, @FLAGS@)) goto fail;
+ }
diff --git a/source/aparser/templates/prs_array_remainder.tpl b/source/aparser/templates/prs_array_remainder.tpl
new file mode 100644
index 00000000000..c8b1e2ab5af
--- /dev/null
+++ b/source/aparser/templates/prs_array_remainder.tpl
@@ -0,0 +1,17 @@
+ if (UNMARSHALLING(ps))
+ {
+ int i;
+ for (i=0;ps->data_offset < ps->buffer_size;i++) {
+ if (!io_alloc("@ELEM@", ps, (void **)&il->@ELEM@, sizeof(*(il->@ELEM@))*(i+1))) goto fail;
+ if (!io_@TYPE@("@ELEM@...", ps, depth+1, &il->@ELEM@[i], @FLAGS@)) goto fail;
+ }
+ }
+ else
+ {
+ int i = -1;
+ /* HACK ALERT! */
+ do {
+ i++;
+ if (!io_@TYPE@("@ELEM@...", ps, depth+1, &il->@ELEM@[i], @FLAGS@)) goto fail;
+ } while (il->@ELEM@[i].tag2 != 0);
+ }
diff --git a/source/aparser/templates/prs_break.tpl b/source/aparser/templates/prs_break.tpl
new file mode 100644
index 00000000000..eb540f7be84
--- /dev/null
+++ b/source/aparser/templates/prs_break.tpl
@@ -0,0 +1 @@
+ break;
diff --git a/source/aparser/templates/prs_case.tpl b/source/aparser/templates/prs_case.tpl
new file mode 100644
index 00000000000..06c1bd3ae6e
--- /dev/null
+++ b/source/aparser/templates/prs_case.tpl
@@ -0,0 +1 @@
+ case @CASE@:
diff --git a/source/aparser/templates/prs_case_end.tpl b/source/aparser/templates/prs_case_end.tpl
new file mode 100644
index 00000000000..eb540f7be84
--- /dev/null
+++ b/source/aparser/templates/prs_case_end.tpl
@@ -0,0 +1 @@
+ break;
diff --git a/source/aparser/templates/prs_element.tpl b/source/aparser/templates/prs_element.tpl
new file mode 100644
index 00000000000..e8bf5180cec
--- /dev/null
+++ b/source/aparser/templates/prs_element.tpl
@@ -0,0 +1 @@
+ if (!io_@TYPE@("@ELEM@", ps, depth+1, @PTR@il->@ELEM@, @FLAGS@)) goto fail;
diff --git a/source/aparser/templates/prs_pointer.tpl b/source/aparser/templates/prs_pointer.tpl
new file mode 100644
index 00000000000..4ebcf19d834
--- /dev/null
+++ b/source/aparser/templates/prs_pointer.tpl
@@ -0,0 +1,2 @@
+ if (!io_pointer("@ELEM@_ptr", ps, depth+1,
+ (void **)&il->@ELEM@, @FLAGS@)) goto fail;
diff --git a/source/aparser/templates/prs_struct.tpl b/source/aparser/templates/prs_struct.tpl
new file mode 100644
index 00000000000..ab8246db8e7
--- /dev/null
+++ b/source/aparser/templates/prs_struct.tpl
@@ -0,0 +1 @@
+ if (!@MODULE@_io_@TYPE@("@ELEM@", &il->@ELEM@, ps, depth+1)) goto fail;
diff --git a/source/aparser/templates/prs_struct_alloc.tpl b/source/aparser/templates/prs_struct_alloc.tpl
new file mode 100644
index 00000000000..9eae5c92fca
--- /dev/null
+++ b/source/aparser/templates/prs_struct_alloc.tpl
@@ -0,0 +1 @@
+ if (!@MODULE@_io_@TYPE@_alloc("@ELEM@", &il->@ELEM@, ps, depth+1)) goto fail;
diff --git a/source/aparser/templates/prs_uint16.tpl b/source/aparser/templates/prs_uint16.tpl
new file mode 100644
index 00000000000..b40d6d4216f
--- /dev/null
+++ b/source/aparser/templates/prs_uint16.tpl
@@ -0,0 +1 @@
+ if (!io_uint16("@ELEM@", ps, depth+1, &il->@ELEM@)) goto fail;
diff --git a/source/aparser/templates/prs_uint32.tpl b/source/aparser/templates/prs_uint32.tpl
new file mode 100644
index 00000000000..eb76715d28b
--- /dev/null
+++ b/source/aparser/templates/prs_uint32.tpl
@@ -0,0 +1 @@
+ if (!io_uint32("@ELEM@", ps, depth+1, &il->@ELEM@)) goto fail;
diff --git a/source/aparser/templates/prs_uint8s.tpl b/source/aparser/templates/prs_uint8s.tpl
new file mode 100644
index 00000000000..967162213f4
--- /dev/null
+++ b/source/aparser/templates/prs_uint8s.tpl
@@ -0,0 +1,2 @@
+ if (!io_alloc("@ELEM@", ps, (void **)&il->@ELEM@, sizeof(*(il->@ELEM@))*(il->@ARRAY_LEN@))) goto fail;
+ if (!io_uint8s("@ELEM@", ps, depth+1, &il->@ELEM@, il->@ARRAY_LEN@, @FLAGS@)) goto fail;
diff --git a/source/aparser/templates/prs_uint8s_fixed.tpl b/source/aparser/templates/prs_uint8s_fixed.tpl
new file mode 100644
index 00000000000..26597f419f3
--- /dev/null
+++ b/source/aparser/templates/prs_uint8s_fixed.tpl
@@ -0,0 +1 @@
+ if (!io_uint8s_fixed("@ELEM@", ps, depth+1, il->@ELEM@, @ARRAY_LEN@, @FLAGS@)) goto fail;
diff --git a/source/aparser/templates/prs_wstring.tpl b/source/aparser/templates/prs_wstring.tpl
new file mode 100644
index 00000000000..4de46f093c8
--- /dev/null
+++ b/source/aparser/templates/prs_wstring.tpl
@@ -0,0 +1,2 @@
+ if (!io_alloc("@ELEM@", ps, (void **)&il->@ELEM@, sizeof(*(il->@ELEM@))*(il->@ARRAY_LEN@))) goto fail;
+ if (!io_wstring("@ELEM@", ps, depth+1, il->@ELEM@, il->@ARRAY_LEN@, @FLAGS@)) goto fail;
diff --git a/source/aparser/templates/prs_wstring_fixed.tpl b/source/aparser/templates/prs_wstring_fixed.tpl
new file mode 100644
index 00000000000..e33f7c3d5d2
--- /dev/null
+++ b/source/aparser/templates/prs_wstring_fixed.tpl
@@ -0,0 +1,2 @@
+ if (!io_alloc("@ELEM@", ps, (void **)&il->@ELEM@, sizeof(*(il->@ELEM@))*(@ARRAY_LEN@))) goto fail;
+ if (!io_wstring("@ELEM@", ps, depth+1, il->@ELEM@, @ARRAY_LEN@, @FLAGS@)) goto fail;
diff --git a/source/aparser/templates/union_end.tpl b/source/aparser/templates/union_end.tpl
new file mode 100644
index 00000000000..511adbcf602
--- /dev/null
+++ b/source/aparser/templates/union_end.tpl
@@ -0,0 +1,5 @@
+ default:
+ DEBUG(5,("No handler for case %d in @FUNCNAME@\n",
+ (int)il->@SWITCH@));
+ goto fail;
+ }
diff --git a/source/aparser/templates/union_start.tpl b/source/aparser/templates/union_start.tpl
new file mode 100644
index 00000000000..aa052be6972
--- /dev/null
+++ b/source/aparser/templates/union_start.tpl
@@ -0,0 +1 @@
+ switch (il->@SWITCH@) {
diff --git a/source/aparser/token.awk b/source/aparser/token.awk
new file mode 100644
index 00000000000..fb2982f0777
--- /dev/null
+++ b/source/aparser/token.awk
@@ -0,0 +1,180 @@
+# tokenise the input file
+
+function parse_error(msg) {
+ printf("PARSE ERROR: %s\nLine "NR" : "$0"\n", msg);
+ exit 1;
+}
+
+# ignore multi-line C comments.
+{
+ if (t = index($0, "/*")) {
+ if (t > 1)
+ tmp = substr($0, 1, t - 1)
+ else
+ tmp = ""
+ u = index(substr($0, t + 2), "*/")
+ while (u == 0) {
+ getline
+ t = -1
+ u = index($0, "*/")
+ }
+ if (u <= length($0) - 2)
+ $0 = tmp substr($0, t + u + 3)
+ else
+ $0 = tmp
+ }
+}
+
+# ignore blank lines
+/^[ \t]*$/ {
+ next;
+}
+
+/^\#define.*/ {
+ split($0,a,"[ \t;]*");
+ parse_define(a[2], a[3]);
+ next;
+}
+
+# ignore comments
+/^[ \t]*\#/ {
+ next;
+}
+
+/^[ \t]*module/ {
+ {if (module!="") parse_error("you can only specify one module name");}
+ start_module($2);
+ next;
+}
+
+{if (module=="") parse_error("you must specify the module name first");}
+
+/^[ \t]*option/ {
+ set_option($2, $3);
+ next;
+}
+
+/^[ \t]*typedef struct.*\{/ {
+ {if (current_struct!="") parse_error("you cannot have nested structures");}
+ start_struct($3);
+ next;
+}
+
+/^[ \t]*struct.*\{/ {
+ {if (current_struct!="") parse_error("you cannot have nested structures");}
+ start_struct($2);
+ next;
+}
+
+/^[ \t]*typedef union.*\{/ {
+ {if (current_struct!="") parse_error("this cannot appear inside a structure");}
+ split($0,a,"[ \t;()]*");
+ start_union_encap(a[4], a[6], a[7], a[8]);
+ next;
+}
+
+/^[ \t]*void.*\(/ {
+ {if (current_struct!="") parse_error("you cannot have nested structures");}
+ split($0,a,"[ \t;()]*");
+ start_function(a[2], a[3]);
+ return_result="void";
+ next;
+}
+
+/^[ \t]*STATUS.*\(|^[ \t]*void.*\(/ {
+ {if (current_struct!="") parse_error("you cannot have nested structures");}
+ split($0,a,"[ \t;()]*");
+ start_function(a[2], a[3]);
+ return_result="STATUS";
+ next;
+}
+
+{if (current_struct=="") parse_error("this must appear inside a structure");}
+
+/^[ \t]*union.*\{/ {
+ {if (current_union!="") parse_error("you cannot have nested unions");}
+ start_union($2);
+ next;
+}
+
+/^[ \t]*\[switch_is.*union.*\{/ {
+ {if (current_union!="") parse_error("you cannot have nested unions");}
+ split($0,a,"[ \t;()]*");
+ start_union_notencap(a[3]);
+ next;
+}
+
+/^[ \t]*case.*;/ {
+ {if (current_union=="") parse_error("this must appear inide a union");}
+ split($0,a,"[ \t;]*");
+ parse_case(a[3],a[4],a[5]);
+ next;
+}
+
+/^[ \t]*\[case(.*)\].*;/ {
+ {if (current_union=="") parse_error("this must appear inide a union");}
+ split($0,a,"[ \t;()[\]]*");
+ parse_case(a[6],a[8],a[9]);
+ next;
+}
+
+/^[ \t]*\}$/ {
+ {if (current_union=="") parse_error("this must appear inside a union");}
+ end_union("");
+ next;
+}
+
+/^[ \t]*\} .*;/ {
+ if (current_union!="") {
+ split($2,a,"[ \t;]*");
+ end_union(a[1]);
+ next;
+ }
+}
+
+{if (current_union!="") parse_error("this cannot appear inside a union");}
+
+/^[ \t]*\};/ {
+ end_struct("");
+ next;
+}
+
+/^[ \t]*\} .*;/ {
+ split($2,a,"[ \t;]*");
+ end_struct(a[1]);
+ next;
+}
+
+/^[ \t]*\);/ {
+ end_function();
+ return_result="";
+ next;
+}
+
+/^.*size_is.*\*.*;/ {
+ split($0,a,"[ \t;()]*");
+ add_sizeis_array(a[3], a[5], a[6]);
+ next;
+}
+
+/^.*;/ {
+ split($0,a,"[ \t;]*");
+ add_struct_elem(a[2], a[3]);
+ next;
+}
+
+/^[\t ]*void/ {
+ next;
+}
+
+/^[ \t]*\[.*\].*/ {
+ split($0,a,"[ \t;]*");
+ split(a[4], b, "[,]");
+ add_function_param(a[2], a[3], b[1]);
+ next;
+}
+
+{
+ parse_error("Unknown construct.");
+}
+
diff --git a/source/aparser/util.awk b/source/aparser/util.awk
new file mode 100644
index 00000000000..6c5594da688
--- /dev/null
+++ b/source/aparser/util.awk
@@ -0,0 +1,39 @@
+function isaptr(elem)
+{
+ if (substr(elem, 1, 1) == "*") {
+ return 1;
+ }
+ return 0;
+}
+
+function noptr(elem)
+{
+ if (!isaptr(elem)) return elem;
+ return substr(elem, 2);
+}
+
+function xprintf(f, fmt, v1, v2, v3, v4, v5, v6, v7)
+{
+ printf(fmt, v1, v2, v3, v4, v5, v6) > f;
+}
+
+function fatal(why)
+{
+ printf("FATAL: %s\n", why);
+ exit 1;
+}
+
+function numlines(fname,
+ LOCAL, line, count)
+{
+ count=0;
+ while ((getline line < fname) > 0) count++;
+ close(fname);
+ return count;
+}
+
+# return 1 if the string is a constant
+function is_constant(s)
+{
+ return match(s,"^[0-9]+$");
+}
diff --git a/source/aparser/util.c b/source/aparser/util.c
new file mode 100644
index 00000000000..ffa84dbfabc
--- /dev/null
+++ b/source/aparser/util.c
@@ -0,0 +1,112 @@
+#include "parser.h"
+
+
+/*******************************************************************
+ Count the number of characters (not bytes) in a unicode string.
+********************************************************************/
+size_t strlen_w(void *src)
+{
+ size_t len;
+
+ for (len = 0; SVAL(src, len*2); len++) ;
+
+ return len;
+}
+
+/****************************************************************************
+expand a pointer to be a particular size
+****************************************************************************/
+void *Realloc(void *p,size_t size)
+{
+ void *ret=NULL;
+
+ if (size == 0) {
+ if (p) free(p);
+ DEBUG(5,("Realloc asked for 0 bytes\n"));
+ return NULL;
+ }
+
+ if (!p)
+ ret = (void *)malloc(size);
+ else
+ ret = (void *)realloc(p,size);
+
+ if (!ret)
+ DEBUG(0,("Memory allocation error: failed to expand to %d bytes\n",(int)size));
+
+ return(ret);
+}
+
+
+char *tab_depth(int depth)
+{
+ static pstring spaces;
+ memset(spaces, ' ', depth * 4);
+ spaces[depth * 4] = 0;
+ return spaces;
+}
+
+void print_asc(int level, uchar const *buf, int len)
+{
+ int i;
+ for (i = 0; i < len; i++)
+ {
+ DEBUGADD(level, ("%c", isprint(buf[i]) ? buf[i] : '.'));
+ }
+}
+
+void dump_data(int level, char *buf1, int len)
+{
+ uchar const *buf = (uchar const *)buf1;
+ int i = 0;
+ if (buf == NULL)
+ {
+ DEBUG(level, ("dump_data: NULL, len=%d\n", len));
+ return;
+ }
+ if (len < 0)
+ return;
+ if (len == 0)
+ {
+ DEBUG(level, ("\n"));
+ return;
+ }
+
+ DEBUG(level, ("[%03X] ", i));
+ for (i = 0; i < len;)
+ {
+ DEBUGADD(level, ("%02X ", (int)buf[i]));
+ i++;
+ if (i % 8 == 0)
+ DEBUGADD(level, (" "));
+ if (i % 16 == 0)
+ {
+ print_asc(level, &buf[i - 16], 8);
+ DEBUGADD(level, (" "));
+ print_asc(level, &buf[i - 8], 8);
+ DEBUGADD(level, ("\n"));
+ if (i < len)
+ DEBUGADD(level, ("[%03X] ", i));
+ }
+ }
+
+ if (i % 16 != 0) /* finish off a non-16-char-length row */
+ {
+ int n;
+
+ n = 16 - (i % 16);
+ DEBUGADD(level, (" "));
+ if (n > 8)
+ DEBUGADD(level, (" "));
+ while (n--)
+ DEBUGADD(level, (" "));
+
+ n = MIN(8, i % 16);
+ print_asc(level, &buf[i - (i % 16)], n);
+ DEBUGADD(level, (" "));
+ n = (i % 16) - n;
+ if (n > 0)
+ print_asc(level, &buf[i - n], n);
+ DEBUGADD(level, ("\n"));
+ }
+}
diff --git a/source/aparser/vluke.c b/source/aparser/vluke.c
new file mode 100644
index 00000000000..d3868f2753e
--- /dev/null
+++ b/source/aparser/vluke.c
@@ -0,0 +1,41 @@
+#include "parser.h"
+#include "test.h"
+
+int main(int argc, char *argv[])
+{
+ BOOL ret;
+ char *fname, *test;
+ int fd;
+ struct stat st;
+ io_struct ps;
+
+ if (argc < 3) {
+ printf("usage: vluke <structure> <file>\n");
+ exit(1);
+ }
+
+ test = argv[1];
+ fname = argv[2];
+
+ fd = open(fname,O_RDONLY);
+ if (fd == -1) {
+ perror(fname);
+ exit(1);
+ }
+ fstat(fd, &st);
+
+ io_init(&ps, 0, MARSHALL);
+ ps.is_dynamic=True;
+ io_read(&ps, fd, st.st_size, 0);
+ ps.data_offset = 0;
+ ps.buffer_size = ps.grow_size;
+ ps.io = UNMARSHALL;
+ ps.autoalign = OPTION_autoalign;
+ ret = run_test(test, &ps, PARSE_SCALARS|PARSE_BUFFERS);
+ printf("\nret=%s\n", ret?"OK":"Bad");
+ printf("Trailer is %d bytes\n\n", ps.grow_size - ps.data_offset);
+ if (ps.grow_size - ps.data_offset > 0) {
+ dump_data(0, ps.data_p + ps.data_offset, ps.grow_size - ps.data_offset);
+ }
+ return !ret;
+}
diff --git a/source/auth/auth.c b/source/auth/auth.c
new file mode 100644
index 00000000000..1b49699fbca
--- /dev/null
+++ b/source/auth/auth.c
@@ -0,0 +1,534 @@
+/*
+ Unix SMB/CIFS implementation.
+ Password and authentication handling
+ Copyright (C) Andrew Bartlett 2001-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 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"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_AUTH
+
+static struct auth_init_function_entry *backends = NULL;
+
+static struct auth_init_function_entry *auth_find_backend_entry(const char *name);
+
+NTSTATUS smb_register_auth(int version, const char *name, auth_init_function init)
+{
+ struct auth_init_function_entry *entry = backends;
+
+ if (version != AUTH_INTERFACE_VERSION) {
+ DEBUG(0,("Can't register auth_method!\n"
+ "You tried to register an auth module with AUTH_INTERFACE_VERSION %d, while this version of samba uses %d\n",
+ version,AUTH_INTERFACE_VERSION));
+ return NT_STATUS_OBJECT_TYPE_MISMATCH;
+ }
+
+ if (!name || !init) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ DEBUG(5,("Attempting to register auth backend %s\n", name));
+
+ if (auth_find_backend_entry(name)) {
+ DEBUG(0,("There already is an auth method registered with the name %s!\n", name));
+ return NT_STATUS_OBJECT_NAME_COLLISION;
+ }
+
+ entry = smb_xmalloc(sizeof(struct auth_init_function_entry));
+ entry->name = smb_xstrdup(name);
+ entry->init = init;
+
+ DLIST_ADD(backends, entry);
+ DEBUG(5,("Successfully added auth method '%s'\n", name));
+ return NT_STATUS_OK;
+}
+
+static struct auth_init_function_entry *auth_find_backend_entry(const char *name)
+{
+ struct auth_init_function_entry *entry = backends;
+
+ while(entry) {
+ if (strcmp(entry->name, name)==0) return entry;
+ entry = entry->next;
+ }
+
+ return NULL;
+}
+
+/****************************************************************************
+ Try to get a challenge out of the various authentication modules.
+ Returns a const char of length 8 bytes.
+****************************************************************************/
+
+static const uint8 *get_ntlm_challenge(struct auth_context *auth_context)
+{
+ DATA_BLOB challenge = data_blob(NULL, 0);
+ const char *challenge_set_by = NULL;
+ auth_methods *auth_method;
+ TALLOC_CTX *mem_ctx;
+
+ if (auth_context->challenge.length) {
+ DEBUG(5, ("get_ntlm_challenge (auth subsystem): returning previous challenge by module %s (normal)\n",
+ auth_context->challenge_set_by));
+ return auth_context->challenge.data;
+ }
+
+ auth_context->challenge_may_be_modified = False;
+
+ for (auth_method = auth_context->auth_method_list; auth_method; auth_method = auth_method->next) {
+ if (auth_method->get_chal == NULL) {
+ DEBUG(5, ("auth_get_challenge: module %s did not want to specify a challenge\n", auth_method->name));
+ continue;
+ }
+
+ DEBUG(5, ("auth_get_challenge: getting challenge from module %s\n", auth_method->name));
+ if (challenge_set_by != NULL) {
+ DEBUG(1, ("auth_get_challenge: CONFIGURATION ERROR: authentication method %s has already specified a challenge. Challenge by %s ignored.\n",
+ challenge_set_by, auth_method->name));
+ continue;
+ }
+
+ mem_ctx = talloc_init("auth_get_challenge for module %s", auth_method->name);
+ if (!mem_ctx) {
+ smb_panic("talloc_init() failed!");
+ }
+
+ challenge = auth_method->get_chal(auth_context, &auth_method->private_data, mem_ctx);
+ if (!challenge.length) {
+ DEBUG(3, ("auth_get_challenge: getting challenge from authentication method %s FAILED.\n",
+ auth_method->name));
+ } else {
+ DEBUG(5, ("auth_get_challenge: sucessfully got challenge from module %s\n", auth_method->name));
+ auth_context->challenge = challenge;
+ challenge_set_by = auth_method->name;
+ auth_context->challenge_set_method = auth_method;
+ }
+ talloc_destroy(mem_ctx);
+ }
+
+ if (!challenge_set_by) {
+ uchar chal[8];
+
+ generate_random_buffer(chal, sizeof(chal), False);
+ auth_context->challenge = data_blob_talloc(auth_context->mem_ctx,
+ chal, sizeof(chal));
+
+ challenge_set_by = "random";
+ auth_context->challenge_may_be_modified = True;
+ }
+
+ DEBUG(5, ("auth_context challenge created by %s\n", challenge_set_by));
+ DEBUG(5, ("challenge is: \n"));
+ dump_data(5, (const char *)auth_context->challenge.data, auth_context->challenge.length);
+
+ SMB_ASSERT(auth_context->challenge.length == 8);
+
+ auth_context->challenge_set_by=challenge_set_by;
+
+ return auth_context->challenge.data;
+}
+
+
+/**
+ * Check user is in correct domain (if required)
+ *
+ * @param user Only used to fill in the debug message
+ *
+ * @param domain The domain to be verified
+ *
+ * @return True if the user can connect with that domain,
+ * False otherwise.
+**/
+
+static BOOL check_domain_match(const char *user, const char *domain)
+{
+ /*
+ * If we aren't serving to trusted domains, we must make sure that
+ * the validation request comes from an account in the same domain
+ * as the Samba server
+ */
+
+ if (!lp_allow_trusted_domains() &&
+ !(strequal("", domain) ||
+ strequal(lp_workgroup(), domain) ||
+ is_myname(domain))) {
+ DEBUG(1, ("check_domain_match: Attempt to connect as user %s from domain %s denied.\n", user, domain));
+ return False;
+ } else {
+ return True;
+ }
+}
+
+/**
+ * Check a user's Plaintext, LM or NTLM password.
+ *
+ * Check a user's password, as given in the user_info struct and return various
+ * interesting details in the server_info struct.
+ *
+ * This function does NOT need to be in a become_root()/unbecome_root() pair
+ * as it makes the calls itself when needed.
+ *
+ * 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 user_info Contains the user supplied components, including the passwords.
+ * Must be created with make_user_info() or one of its wrappers.
+ *
+ * @param auth_context 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 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.
+ *
+ **/
+
+static NTSTATUS check_ntlm_password(const struct auth_context *auth_context,
+ const struct auth_usersupplied_info *user_info,
+ struct auth_serversupplied_info **server_info)
+{
+ /* if all the modules say 'not for me' this is reasonable */
+ NTSTATUS nt_status = NT_STATUS_NO_SUCH_USER;
+ const char *unix_username;
+ auth_methods *auth_method;
+ TALLOC_CTX *mem_ctx;
+
+ if (!user_info || !auth_context || !server_info)
+ return NT_STATUS_LOGON_FAILURE;
+
+ DEBUG(3, ("check_ntlm_password: Checking password for unmapped user [%s]\\[%s]@[%s] with the new password interface\n",
+ user_info->client_domain.str, user_info->smb_name.str, user_info->wksta_name.str));
+
+ DEBUG(3, ("check_ntlm_password: mapped user is: [%s]\\[%s]@[%s]\n",
+ user_info->domain.str, user_info->internal_username.str, user_info->wksta_name.str));
+
+ if (auth_context->challenge.length != 8) {
+ DEBUG(0, ("check_ntlm_password: Invalid challenge stored for this auth context - cannot continue\n"));
+ return NT_STATUS_LOGON_FAILURE;
+ }
+
+ if (auth_context->challenge_set_by)
+ DEBUG(10, ("check_ntlm_password: auth_context challenge created by %s\n",
+ auth_context->challenge_set_by));
+
+ DEBUG(10, ("challenge is: \n"));
+ dump_data(5, (const char *)auth_context->challenge.data, auth_context->challenge.length);
+
+#ifdef DEBUG_PASSWORD
+ DEBUG(100, ("user_info has passwords of length %d and %d\n",
+ user_info->lm_resp.length, user_info->nt_resp.length));
+ DEBUG(100, ("lm:\n"));
+ dump_data(100, user_info->lm_resp.data, user_info->lm_resp.length);
+ DEBUG(100, ("nt:\n"));
+ dump_data(100, user_info->nt_resp.data, user_info->nt_resp.length);
+#endif
+
+ /* This needs to be sorted: If it doesn't match, what should we do? */
+ if (!check_domain_match(user_info->smb_name.str, user_info->domain.str))
+ return NT_STATUS_LOGON_FAILURE;
+
+ for (auth_method = auth_context->auth_method_list;auth_method; auth_method = auth_method->next) {
+ NTSTATUS result;
+
+ mem_ctx = talloc_init("%s authentication for user %s\\%s", auth_method->name,
+ user_info->domain.str, user_info->smb_name.str);
+
+ result = auth_method->auth(auth_context, auth_method->private_data, mem_ctx, user_info, server_info);
+
+ /* check if the module did anything */
+ if ( NT_STATUS_V(result) == NT_STATUS_V(NT_STATUS_NOT_IMPLEMENTED) ) {
+ DEBUG(10,("check_ntlm_password: %s had nothing to say\n", auth_method->name));
+ talloc_destroy(mem_ctx);
+ continue;
+ }
+
+ nt_status = result;
+
+ if (NT_STATUS_IS_OK(nt_status)) {
+ DEBUG(3, ("check_ntlm_password: %s authentication for user [%s] succeeded\n",
+ auth_method->name, user_info->smb_name.str));
+ } else {
+ DEBUG(5, ("check_ntlm_password: %s authentication for user [%s] FAILED with error %s\n",
+ auth_method->name, user_info->smb_name.str, nt_errstr(nt_status)));
+ }
+
+ talloc_destroy(mem_ctx);
+
+ if ( NT_STATUS_IS_OK(nt_status))
+ {
+ break;
+ }
+ }
+
+ if (NT_STATUS_IS_OK(nt_status)) {
+ unix_username = (*server_info)->unix_name;
+ if (!(*server_info)->guest) {
+ /* We might not be root if we are an RPC call */
+ become_root();
+ nt_status = smb_pam_accountcheck(unix_username);
+ unbecome_root();
+
+ if (NT_STATUS_IS_OK(nt_status)) {
+ DEBUG(5, ("check_ntlm_password: PAM Account for user [%s] succeeded\n",
+ unix_username));
+ } else {
+ DEBUG(3, ("check_ntlm_password: PAM Account for user [%s] FAILED with error %s\n",
+ unix_username, nt_errstr(nt_status)));
+ }
+ }
+
+ if (NT_STATUS_IS_OK(nt_status)) {
+ DEBUG((*server_info)->guest ? 5 : 2,
+ ("check_ntlm_password: %sauthentication for user [%s] -> [%s] -> [%s] succeeded\n",
+ (*server_info)->guest ? "guest " : "",
+ user_info->smb_name.str,
+ user_info->internal_username.str,
+ unix_username));
+ }
+ }
+
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ DEBUG(2, ("check_ntlm_password: Authentication for user [%s] -> [%s] FAILED with error %s\n",
+ user_info->smb_name.str, user_info->internal_username.str,
+ nt_errstr(nt_status)));
+ ZERO_STRUCTP(server_info);
+ }
+ return nt_status;
+}
+
+/***************************************************************************
+ Clear out a auth_context, and destroy the attached TALLOC_CTX
+***************************************************************************/
+
+static void free_auth_context(struct auth_context **auth_context)
+{
+ auth_methods *auth_method;
+
+ if (*auth_context) {
+ /* Free private data of context's authentication methods */
+ for (auth_method = (*auth_context)->auth_method_list; auth_method; auth_method = auth_method->next) {
+ if (auth_method->free_private_data) {
+ auth_method->free_private_data (&auth_method->private_data);
+ auth_method->private_data = NULL;
+ }
+ }
+
+ talloc_destroy((*auth_context)->mem_ctx);
+ *auth_context = NULL;
+ }
+}
+
+/***************************************************************************
+ Make a auth_info struct
+***************************************************************************/
+
+static NTSTATUS make_auth_context(struct auth_context **auth_context)
+{
+ TALLOC_CTX *mem_ctx;
+
+ mem_ctx = talloc_init("authentication context");
+
+ *auth_context = talloc(mem_ctx, sizeof(**auth_context));
+ if (!*auth_context) {
+ DEBUG(0,("make_auth_context: talloc failed!\n"));
+ talloc_destroy(mem_ctx);
+ return NT_STATUS_NO_MEMORY;
+ }
+ ZERO_STRUCTP(*auth_context);
+
+ (*auth_context)->mem_ctx = mem_ctx;
+ (*auth_context)->check_ntlm_password = check_ntlm_password;
+ (*auth_context)->get_ntlm_challenge = get_ntlm_challenge;
+ (*auth_context)->free = free_auth_context;
+
+ return NT_STATUS_OK;
+}
+
+BOOL load_auth_module(struct auth_context *auth_context,
+ const char *module, auth_methods **ret)
+{
+ static BOOL initialised_static_modules = False;
+
+ struct auth_init_function_entry *entry;
+ char *module_name = smb_xstrdup(module);
+ char *module_params = NULL;
+ char *p;
+ BOOL good = False;
+
+ /* Initialise static modules if not done so yet */
+ if(!initialised_static_modules) {
+ static_init_auth;
+ initialised_static_modules = True;
+ }
+
+ DEBUG(5,("load_auth_module: Attempting to find an auth method to match %s\n",
+ module));
+
+ p = strchr(module_name, ':');
+ if (p) {
+ *p = 0;
+ module_params = p+1;
+ trim_char(module_params, ' ', ' ');
+ }
+
+ trim_char(module_name, ' ', ' ');
+
+ entry = auth_find_backend_entry(module_name);
+
+ if (entry == NULL) {
+ if (NT_STATUS_IS_OK(smb_probe_module("auth", module_name))) {
+ entry = auth_find_backend_entry(module_name);
+ }
+ }
+
+ if (entry != NULL) {
+ if (!NT_STATUS_IS_OK(entry->init(auth_context, module_params, ret))) {
+ DEBUG(0,("load_auth_module: auth method %s did not correctly init\n",
+ module_name));
+ } else {
+ DEBUG(5,("load_auth_module: auth method %s has a valid init\n",
+ module_name));
+ good = True;
+ }
+ } else {
+ DEBUG(0,("load_auth_module: can't find auth method %s!\n", module_name));
+ }
+
+ SAFE_FREE(module_name);
+ return good;
+}
+
+/***************************************************************************
+ Make a auth_info struct for the auth subsystem
+***************************************************************************/
+
+static NTSTATUS make_auth_context_text_list(struct auth_context **auth_context, char **text_list)
+{
+ auth_methods *list = NULL;
+ auth_methods *t = NULL;
+ auth_methods *tmp;
+ NTSTATUS nt_status;
+
+ if (!text_list) {
+ DEBUG(2,("make_auth_context_text_list: No auth method list!?\n"));
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ if (!NT_STATUS_IS_OK(nt_status = make_auth_context(auth_context)))
+ return nt_status;
+
+ for (;*text_list; text_list++) {
+ if (load_auth_module(*auth_context, *text_list, &t)) {
+ DLIST_ADD_END(list, t, tmp);
+ }
+ }
+
+ (*auth_context)->auth_method_list = list;
+
+ return nt_status;
+}
+
+/***************************************************************************
+ Make a auth_context struct for the auth subsystem
+***************************************************************************/
+
+NTSTATUS make_auth_context_subsystem(struct auth_context **auth_context)
+{
+ char **auth_method_list = NULL;
+ NTSTATUS nt_status;
+
+ if (lp_auth_methods() && !str_list_copy(&auth_method_list, lp_auth_methods())) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ if (auth_method_list == NULL) {
+ switch (lp_security())
+ {
+ case SEC_DOMAIN:
+ DEBUG(5,("Making default auth method list for security=domain\n"));
+ auth_method_list = str_list_make("guest sam winbind:ntdomain", NULL);
+ break;
+ case SEC_SERVER:
+ DEBUG(5,("Making default auth method list for security=server\n"));
+ auth_method_list = str_list_make("guest sam smbserver", NULL);
+ break;
+ case SEC_USER:
+ if (lp_encrypted_passwords()) {
+ if ((lp_server_role() == ROLE_DOMAIN_PDC) || (lp_server_role() == ROLE_DOMAIN_BDC)) {
+ DEBUG(5,("Making default auth method list for DC, security=user, encrypt passwords = yes\n"));
+ auth_method_list = str_list_make("guest sam winbind:trustdomain", NULL);
+ } else {
+ DEBUG(5,("Making default auth method list for standalone security=user, encrypt passwords = yes\n"));
+ auth_method_list = str_list_make("guest sam", NULL);
+ }
+ } else {
+ DEBUG(5,("Making default auth method list for security=user, encrypt passwords = no\n"));
+ auth_method_list = str_list_make("guest unix", NULL);
+ }
+ break;
+ case SEC_SHARE:
+ if (lp_encrypted_passwords()) {
+ DEBUG(5,("Making default auth method list for security=share, encrypt passwords = yes\n"));
+ auth_method_list = str_list_make("guest sam", NULL);
+ } else {
+ DEBUG(5,("Making default auth method list for security=share, encrypt passwords = no\n"));
+ auth_method_list = str_list_make("guest unix", NULL);
+ }
+ break;
+ case SEC_ADS:
+ DEBUG(5,("Making default auth method list for security=ADS\n"));
+ auth_method_list = str_list_make("guest sam winbind:ntdomain", NULL);
+ break;
+ default:
+ DEBUG(5,("Unknown auth method!\n"));
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+ } else {
+ DEBUG(5,("Using specified auth order\n"));
+ }
+
+ if (!NT_STATUS_IS_OK(nt_status = make_auth_context_text_list(auth_context, auth_method_list))) {
+ str_list_free(&auth_method_list);
+ return nt_status;
+ }
+
+ str_list_free(&auth_method_list);
+ return nt_status;
+}
+
+/***************************************************************************
+ Make a auth_info struct with a fixed challenge
+***************************************************************************/
+
+NTSTATUS make_auth_context_fixed(struct auth_context **auth_context, uchar chal[8])
+{
+ NTSTATUS nt_status;
+ if (!NT_STATUS_IS_OK(nt_status = make_auth_context_subsystem(auth_context))) {
+ return nt_status;
+ }
+
+ (*auth_context)->challenge = data_blob_talloc((*auth_context)->mem_ctx, chal, 8);
+ (*auth_context)->challenge_set_by = "fixed";
+ return nt_status;
+}
+
+
diff --git a/source/auth/auth_builtin.c b/source/auth/auth_builtin.c
new file mode 100644
index 00000000000..96c2221652e
--- /dev/null
+++ b/source/auth/auth_builtin.c
@@ -0,0 +1,175 @@
+/*
+ Unix SMB/CIFS implementation.
+ Generic authentication types
+ Copyright (C) Andrew Bartlett 2001-2002
+ Copyright (C) Jelmer Vernooij 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 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"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_AUTH
+
+/**
+ * Return a guest logon for guest users (username = "")
+ *
+ * Typically used as the first module in the auth chain, this allows
+ * guest logons to be dealt with in one place. Non-guest logons 'fail'
+ * and pass onto the next module.
+ **/
+
+static NTSTATUS check_guest_security(const struct auth_context *auth_context,
+ void *my_private_data,
+ TALLOC_CTX *mem_ctx,
+ const auth_usersupplied_info *user_info,
+ auth_serversupplied_info **server_info)
+{
+ /* mark this as 'not for me' */
+ NTSTATUS nt_status = NT_STATUS_NOT_IMPLEMENTED;
+
+ if (!(user_info->internal_username.str
+ && *user_info->internal_username.str)) {
+ nt_status = make_server_info_guest(server_info);
+ }
+
+ return nt_status;
+}
+
+/* Guest modules initialisation */
+
+static NTSTATUS auth_init_guest(struct auth_context *auth_context, const char *options, auth_methods **auth_method)
+{
+ if (!make_auth_methods(auth_context, auth_method))
+ return NT_STATUS_NO_MEMORY;
+
+ (*auth_method)->auth = check_guest_security;
+ (*auth_method)->name = "guest";
+ return NT_STATUS_OK;
+}
+
+#ifdef DEVELOPER
+/**
+ * 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 check_name_to_ntstatus_security(const struct auth_context *auth_context,
+ void *my_private_data,
+ TALLOC_CTX *mem_ctx,
+ const auth_usersupplied_info *user_info,
+ auth_serversupplied_info **server_info)
+{
+ NTSTATUS nt_status;
+ fstring user;
+ long error_num;
+ fstrcpy(user, user_info->smb_name.str);
+
+ if (strnequal("NT_STATUS", user, strlen("NT_STATUS"))) {
+ strupper_m(user);
+ return nt_status_string_to_code(user);
+ }
+
+ strlower_m(user);
+ error_num = strtoul(user, NULL, 16);
+
+ DEBUG(5,("check_name_to_ntstatus_security: Error for user %s was %lx\n", user, error_num));
+
+ nt_status = NT_STATUS(error_num);
+
+ return nt_status;
+}
+
+/** Module initialisation function */
+
+static NTSTATUS auth_init_name_to_ntstatus(struct auth_context *auth_context, const char *param, auth_methods **auth_method)
+{
+ if (!make_auth_methods(auth_context, auth_method))
+ return NT_STATUS_NO_MEMORY;
+
+ (*auth_method)->auth = check_name_to_ntstatus_security;
+ (*auth_method)->name = "name_to_ntstatus";
+ return NT_STATUS_OK;
+}
+
+/**
+ * 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 check_fixed_challenge_security(const struct auth_context *auth_context,
+ void *my_private_data,
+ TALLOC_CTX *mem_ctx,
+ const auth_usersupplied_info *user_info,
+ auth_serversupplied_info **server_info)
+{
+ return NT_STATUS_NOT_IMPLEMENTED;
+}
+
+/****************************************************************************
+ Get the challenge out of a password server.
+****************************************************************************/
+
+static DATA_BLOB auth_get_fixed_challenge(const struct auth_context *auth_context,
+ void **my_private_data,
+ TALLOC_CTX *mem_ctx)
+{
+ const char *challenge = "I am a teapot";
+ return data_blob(challenge, 8);
+}
+
+
+/** Module initailisation function */
+
+static NTSTATUS auth_init_fixed_challenge(struct auth_context *auth_context, const char *param, auth_methods **auth_method)
+{
+ if (!make_auth_methods(auth_context, auth_method))
+ return NT_STATUS_NO_MEMORY;
+
+ (*auth_method)->auth = check_fixed_challenge_security;
+ (*auth_method)->get_chal = auth_get_fixed_challenge;
+ (*auth_method)->name = "fixed_challenge";
+ return NT_STATUS_OK;
+}
+#endif /* DEVELOPER */
+
+NTSTATUS auth_builtin_init(void)
+{
+ smb_register_auth(AUTH_INTERFACE_VERSION, "guest", auth_init_guest);
+#ifdef DEVELOPER
+ smb_register_auth(AUTH_INTERFACE_VERSION, "fixed_challenge", auth_init_fixed_challenge);
+ smb_register_auth(AUTH_INTERFACE_VERSION, "name_to_ntstatus", auth_init_name_to_ntstatus);
+#endif
+ return NT_STATUS_OK;
+}
diff --git a/source/auth/auth_compat.c b/source/auth/auth_compat.c
new file mode 100644
index 00000000000..a70f1e98b72
--- /dev/null
+++ b/source/auth/auth_compat.c
@@ -0,0 +1,122 @@
+/*
+ Unix SMB/CIFS implementation.
+ Password and authentication handling
+ Copyright (C) Andrew Bartlett 2001-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 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"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_AUTH
+
+/****************************************************************************
+ COMPATIBILITY INTERFACES:
+ ***************************************************************************/
+
+/****************************************************************************
+check if a username/password is OK assuming the password is a 24 byte
+SMB hash
+return True if the password is correct, False otherwise
+****************************************************************************/
+
+NTSTATUS check_plaintext_password(const char *smb_name, DATA_BLOB plaintext_password, auth_serversupplied_info **server_info)
+{
+ struct auth_context *plaintext_auth_context = NULL;
+ auth_usersupplied_info *user_info = NULL;
+ const uint8 *chal;
+ NTSTATUS nt_status;
+ if (!NT_STATUS_IS_OK(nt_status = make_auth_context_subsystem(&plaintext_auth_context))) {
+ return nt_status;
+ }
+
+ chal = plaintext_auth_context->get_ntlm_challenge(plaintext_auth_context);
+
+ if (!make_user_info_for_reply(&user_info,
+ smb_name, lp_workgroup(), chal,
+ plaintext_password)) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ nt_status = plaintext_auth_context->check_ntlm_password(plaintext_auth_context,
+ user_info, server_info);
+
+ (plaintext_auth_context->free)(&plaintext_auth_context);
+ free_user_info(&user_info);
+ return nt_status;
+}
+
+static NTSTATUS pass_check_smb(const char *smb_name,
+ const char *domain,
+ DATA_BLOB lm_pwd,
+ DATA_BLOB nt_pwd,
+ DATA_BLOB plaintext_password,
+ BOOL encrypted)
+
+{
+ NTSTATUS nt_status;
+ extern struct auth_context *negprot_global_auth_context;
+ auth_serversupplied_info *server_info = NULL;
+ if (encrypted) {
+ auth_usersupplied_info *user_info = NULL;
+ make_user_info_for_reply_enc(&user_info, smb_name,
+ domain,
+ lm_pwd,
+ nt_pwd);
+ nt_status = negprot_global_auth_context->check_ntlm_password(negprot_global_auth_context,
+ user_info, &server_info);
+ free_user_info(&user_info);
+ } else {
+ nt_status = check_plaintext_password(smb_name, plaintext_password, &server_info);
+ }
+ free_server_info(&server_info);
+ return nt_status;
+}
+
+/****************************************************************************
+check if a username/password pair is ok via the auth subsystem.
+return True if the password is correct, False otherwise
+****************************************************************************/
+BOOL password_ok(char *smb_name, DATA_BLOB password_blob)
+{
+
+ DATA_BLOB null_password = data_blob(NULL, 0);
+ extern BOOL global_encrypted_passwords_negotiated;
+ BOOL encrypted = (global_encrypted_passwords_negotiated && password_blob.length == 24);
+
+ if (encrypted) {
+ /*
+ * The password could be either NTLM or plain LM. Try NTLM first,
+ * but fall-through as required.
+ * NTLMv2 makes no sense here.
+ */
+ if (NT_STATUS_IS_OK(pass_check_smb(smb_name, lp_workgroup(), null_password, password_blob, null_password, encrypted))) {
+ return True;
+ }
+
+ if (NT_STATUS_IS_OK(pass_check_smb(smb_name, lp_workgroup(), password_blob, null_password, null_password, encrypted))) {
+ return True;
+ }
+ } else {
+ if (NT_STATUS_IS_OK(pass_check_smb(smb_name, lp_workgroup(), null_password, null_password, password_blob, encrypted))) {
+ return True;
+ }
+ }
+
+ return False;
+}
+
+
diff --git a/source/auth/auth_domain.c b/source/auth/auth_domain.c
new file mode 100644
index 00000000000..fdff0b52f96
--- /dev/null
+++ b/source/auth/auth_domain.c
@@ -0,0 +1,424 @@
+/*
+ Unix SMB/CIFS implementation.
+ Authenticate against a remote domain
+ Copyright (C) Andrew Tridgell 1992-1998
+ Copyright (C) Andrew Bartlett 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 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"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_AUTH
+
+extern BOOL global_machine_password_needs_changing;
+
+/**
+ * Connect to a remote server for (inter)domain security authenticaion.
+ *
+ * @param cli the cli to return containing the active connection
+ * @param server either a machine name or text IP address to
+ * connect to.
+ * @param setup_creds_as domain account to setup credentials as
+ * @param sec_chan a switch value to distinguish between domain
+ * member and interdomain authentication
+ * @param trust_passwd the trust password to establish the
+ * credentials with.
+ *
+ **/
+
+static NTSTATUS connect_to_domain_password_server(struct cli_state **cli,
+ const char *domain, const char *dc_name,
+ struct in_addr dc_ip,
+ const char *setup_creds_as,
+ uint16 sec_chan,
+ const unsigned char *trust_passwd,
+ BOOL *retry)
+{
+ NTSTATUS result;
+
+ /* TODO: Send a SAMLOGON request to determine whether this is a valid
+ logonserver. We can avoid a 30-second timeout if the DC is down
+ if the SAMLOGON request fails as it is only over UDP. */
+
+ /* we use a mutex to prevent two connections at once - when a
+ Win2k PDC get two connections where one hasn't completed a
+ session setup yet it will send a TCP reset to the first
+ connection (tridge) */
+
+ /*
+ * With NT4.x DC's *all* authentication must be serialized to avoid
+ * ACCESS_DENIED errors if 2 auths are done from the same machine. JRA.
+ */
+
+ if (!grab_server_mutex(dc_name))
+ return NT_STATUS_NO_LOGON_SERVERS;
+
+ /* Attempt connection */
+ *retry = True;
+ result = cli_full_connection(cli, global_myname(), dc_name, &dc_ip, 0,
+ "IPC$", "IPC", "", "", "", 0, Undefined, retry);
+
+ if (!NT_STATUS_IS_OK(result)) {
+ /* map to something more useful */
+ if (NT_STATUS_EQUAL(result, NT_STATUS_UNSUCCESSFUL)) {
+ result = NT_STATUS_NO_LOGON_SERVERS;
+ }
+
+ release_server_mutex();
+ return result;
+ }
+
+ /*
+ * We now have an anonymous connection to IPC$ on the domain password server.
+ */
+
+ /*
+ * Even if the connect succeeds we need to setup the netlogon
+ * pipe here. We do this as we may just have changed the domain
+ * account password on the PDC and yet we may be talking to
+ * a BDC that doesn't have this replicated yet. In this case
+ * a successful connect to a DC needs to take the netlogon connect
+ * into account also. This patch from "Bjart Kvarme" <bjart.kvarme@usit.uio.no>.
+ */
+
+ if(cli_nt_session_open(*cli, PI_NETLOGON) == False) {
+ DEBUG(0,("connect_to_domain_password_server: unable to open the domain client session to \
+machine %s. Error was : %s.\n", dc_name, cli_errstr(*cli)));
+ cli_nt_session_close(*cli);
+ cli_ulogoff(*cli);
+ cli_shutdown(*cli);
+ release_server_mutex();
+ return NT_STATUS_NO_LOGON_SERVERS;
+ }
+
+ fstr_sprintf((*cli)->mach_acct, "%s$", setup_creds_as);
+
+ /* This must be the remote domain (not ours) for schannel */
+
+ fstrcpy( (*cli)->domain, domain );
+
+ result = cli_nt_establish_netlogon(*cli, sec_chan, trust_passwd);
+
+ if (!NT_STATUS_IS_OK(result)) {
+ DEBUG(0,("connect_to_domain_password_server: unable to setup the NETLOGON credentials to machine \
+%s. Error was : %s.\n", dc_name, nt_errstr(result)));
+ cli_nt_session_close(*cli);
+ cli_ulogoff(*cli);
+ cli_shutdown(*cli);
+ release_server_mutex();
+ return result;
+ }
+
+ /* We exit here with the mutex *locked*. JRA */
+
+ return NT_STATUS_OK;
+}
+
+/***********************************************************************
+ Do the same as security=server, but using NT Domain calls and a session
+ key from the machine password. If the server parameter is specified
+ use it, otherwise figure out a server from the 'password server' param.
+************************************************************************/
+
+static NTSTATUS domain_client_validate(TALLOC_CTX *mem_ctx,
+ const auth_usersupplied_info *user_info,
+ const char *domain,
+ uchar chal[8],
+ auth_serversupplied_info **server_info,
+ const char *dc_name, struct in_addr dc_ip,
+ const char *setup_creds_as,
+ uint16 sec_chan,
+ unsigned char trust_passwd[16],
+ time_t last_change_time)
+{
+ NET_USER_INFO_3 info3;
+ struct cli_state *cli = NULL;
+ NTSTATUS nt_status = NT_STATUS_NO_LOGON_SERVERS;
+ int i;
+ BOOL retry = True;
+
+ /*
+ * At this point, smb_apasswd points to the lanman response to
+ * the challenge in local_challenge, and smb_ntpasswd points to
+ * the NT response to the challenge in local_challenge. Ship
+ * these over the secure channel to a domain controller and
+ * see if they were valid.
+ */
+
+ /* rety loop for robustness */
+
+ for (i = 0; !NT_STATUS_IS_OK(nt_status) && retry && (i < 3); i++) {
+ nt_status = connect_to_domain_password_server(&cli, domain, dc_name,
+ dc_ip, setup_creds_as, sec_chan, trust_passwd, &retry);
+ }
+
+ if ( !NT_STATUS_IS_OK(nt_status) ) {
+ DEBUG(0,("domain_client_validate: Domain password server not available.\n"));
+ if (NT_STATUS_EQUAL(nt_status, NT_STATUS_ACCESS_DENIED)) {
+ return NT_STATUS_TRUSTED_RELATIONSHIP_FAILURE;
+ }
+ return nt_status;
+ }
+
+ ZERO_STRUCT(info3);
+
+ /*
+ * If this call succeeds, we now have lots of info about the user
+ * in the info3 structure.
+ */
+
+ nt_status = cli_netlogon_sam_network_logon(cli, mem_ctx,
+ NULL, user_info->smb_name.str, user_info->domain.str,
+ user_info->wksta_name.str, chal, user_info->lm_resp,
+ user_info->nt_resp, &info3);
+
+ /* let go as soon as possible so we avoid any potential deadlocks
+ with winbind lookup up users or groups */
+
+ release_server_mutex();
+
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ DEBUG(0,("domain_client_validate: unable to validate password "
+ "for user %s in domain %s to Domain controller %s. "
+ "Error was %s.\n", user_info->smb_name.str,
+ user_info->domain.str, cli->srv_name_slash,
+ nt_errstr(nt_status)));
+
+ /* map to something more useful */
+ if (NT_STATUS_EQUAL(nt_status, NT_STATUS_UNSUCCESSFUL)) {
+ nt_status = NT_STATUS_NO_LOGON_SERVERS;
+ }
+ } else {
+ nt_status = make_server_info_info3(mem_ctx, user_info->internal_username.str,
+ user_info->smb_name.str, domain, server_info, &info3);
+ netsamlogon_cache_store( mem_ctx, &info3 );
+ }
+
+#if 0
+ /*
+ * We don't actually need to do this - plus it fails currently with
+ * NT_STATUS_INVALID_INFO_CLASS - we need to know *exactly* what to
+ * send here. JRA.
+ */
+
+ if (NT_STATUS_IS_OK(status)) {
+ if(cli_nt_logoff(&cli, &ctr) == False) {
+ DEBUG(0,("domain_client_validate: unable to log off user %s in domain \
+%s to Domain controller %s. Error was %s.\n", user, domain, dc_name, cli_errstr(&cli)));
+ nt_status = NT_STATUS_LOGON_FAILURE;
+ }
+ }
+#endif /* 0 */
+
+ /* Note - once the cli stream is shutdown the mem_ctx used
+ to allocate the other_sids and gids structures has been deleted - so
+ these pointers are no longer valid..... */
+
+ cli_nt_session_close(cli);
+ cli_ulogoff(cli);
+ cli_shutdown(cli);
+ return nt_status;
+}
+
+/****************************************************************************
+ Check for a valid username and password in security=domain mode.
+****************************************************************************/
+
+static NTSTATUS check_ntdomain_security(const struct auth_context *auth_context,
+ void *my_private_data,
+ TALLOC_CTX *mem_ctx,
+ const auth_usersupplied_info *user_info,
+ auth_serversupplied_info **server_info)
+{
+ NTSTATUS nt_status = NT_STATUS_LOGON_FAILURE;
+ unsigned char trust_passwd[16];
+ time_t last_change_time;
+ const char *domain = lp_workgroup();
+ uint32 sec_channel_type = 0;
+ fstring dc_name;
+ struct in_addr dc_ip;
+
+ if ( lp_server_role() != ROLE_DOMAIN_MEMBER ) {
+ DEBUG(0,("check_ntdomain_security: Configuration error! Cannot use "
+ "ntdomain auth method when not a member of a domain.\n"));
+ return NT_STATUS_NOT_IMPLEMENTED;
+ }
+
+ if (!user_info || !server_info || !auth_context) {
+ DEBUG(1,("check_ntdomain_security: Critical variables not present. Failing.\n"));
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ /*
+ * Check that the requested domain is not our own machine name.
+ * If it is, we should never check the PDC here, we use our own local
+ * password file.
+ */
+
+ if(strequal(get_global_sam_name(), user_info->domain.str)) {
+ DEBUG(3,("check_ntdomain_security: Requested domain was for this machine.\n"));
+ return NT_STATUS_NOT_IMPLEMENTED;
+ }
+
+ /*
+ * Get the machine account password for our primary domain
+ * No need to become_root() as secrets_init() is done at startup.
+ */
+
+ if (!secrets_fetch_trust_account_password(domain, trust_passwd, &last_change_time, &sec_channel_type))
+ {
+ DEBUG(0, ("check_ntdomain_security: could not fetch trust account password for domain '%s'\n", domain));
+ return NT_STATUS_CANT_ACCESS_DOMAIN_INFO;
+ }
+
+ /* Test if machine password has expired and needs to be changed */
+ if (lp_machine_password_timeout()) {
+ if (last_change_time > 0 &&
+ time(NULL) > (last_change_time +
+ lp_machine_password_timeout())) {
+ global_machine_password_needs_changing = True;
+ }
+ }
+
+ /* we need our DC to send the net_sam_logon() request to */
+
+ if ( !get_dc_name(domain, NULL, dc_name, &dc_ip) ) {
+ DEBUG(5,("check_ntdomain_security: unable to locate a DC for domain %s\n",
+ user_info->domain.str));
+ return NT_STATUS_NO_LOGON_SERVERS;
+ }
+
+ nt_status = domain_client_validate(mem_ctx, user_info, domain,
+ (uchar *)auth_context->challenge.data, server_info, dc_name, dc_ip,
+ global_myname(), sec_channel_type,trust_passwd, last_change_time);
+
+ return nt_status;
+}
+
+/* module initialisation */
+static NTSTATUS auth_init_ntdomain(struct auth_context *auth_context, const char* param, auth_methods **auth_method)
+{
+ if (!make_auth_methods(auth_context, auth_method)) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ (*auth_method)->name = "ntdomain";
+ (*auth_method)->auth = check_ntdomain_security;
+ return NT_STATUS_OK;
+}
+
+
+/****************************************************************************
+ Check for a valid username and password in a trusted domain
+****************************************************************************/
+
+static NTSTATUS check_trustdomain_security(const struct auth_context *auth_context,
+ void *my_private_data,
+ TALLOC_CTX *mem_ctx,
+ const auth_usersupplied_info *user_info,
+ auth_serversupplied_info **server_info)
+{
+ NTSTATUS nt_status = NT_STATUS_LOGON_FAILURE;
+ unsigned char trust_md4_password[16];
+ char *trust_password;
+ time_t last_change_time;
+ DOM_SID sid;
+ fstring dc_name;
+ struct in_addr dc_ip;
+
+ if (!user_info || !server_info || !auth_context) {
+ DEBUG(1,("check_trustdomain_security: Critical variables not present. Failing.\n"));
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ /*
+ * Check that the requested domain is not our own machine name or domain name.
+ */
+
+ if( strequal(get_global_sam_name(), user_info->domain.str)) {
+ DEBUG(3,("check_trustdomain_security: Requested domain [%s] was for this machine.\n",
+ user_info->domain.str));
+ return NT_STATUS_NOT_IMPLEMENTED;
+ }
+
+ /* No point is bothering if this is not a trusted domain.
+ This return makes "map to guest = bad user" work again.
+ The logic is that if we know nothing about the domain, that
+ user is known to us and does not exist */
+
+ if ( !is_trusted_domain( user_info->domain.str ) )
+ return NT_STATUS_NOT_IMPLEMENTED;
+
+ /*
+ * Get the trusted account password for the trusted domain
+ * No need to become_root() as secrets_init() is done at startup.
+ */
+
+ if (!secrets_fetch_trusted_domain_password(user_info->domain.str, &trust_password, &sid, &last_change_time))
+ {
+ DEBUG(0, ("check_trustdomain_security: could not fetch trust account password for domain %s\n", user_info->domain.str));
+ return NT_STATUS_CANT_ACCESS_DOMAIN_INFO;
+ }
+
+#ifdef DEBUG_PASSWORD
+ DEBUG(100, ("Trust password for domain %s is %s\n", user_info->domain.str, trust_password));
+#endif
+ E_md4hash(trust_password, trust_md4_password);
+ SAFE_FREE(trust_password);
+
+#if 0
+ /* Test if machine password is expired and need to be changed */
+ if (time(NULL) > last_change_time + lp_machine_password_timeout())
+ {
+ global_machine_password_needs_changing = True;
+ }
+#endif
+
+ /* use get_dc_name() for consistency even through we know that it will be
+ a netbios name */
+
+ if ( !get_dc_name(user_info->domain.str, NULL, dc_name, &dc_ip) ) {
+ DEBUG(5,("check_trustdomain_security: unable to locate a DC for domain %s\n",
+ user_info->domain.str));
+ return NT_STATUS_NO_LOGON_SERVERS;
+ }
+
+ nt_status = domain_client_validate(mem_ctx, user_info, user_info->domain.str,
+ (uchar *)auth_context->challenge.data, server_info, dc_name, dc_ip,
+ lp_workgroup(), SEC_CHAN_DOMAIN, trust_md4_password, last_change_time);
+
+ return nt_status;
+}
+
+/* module initialisation */
+static NTSTATUS auth_init_trustdomain(struct auth_context *auth_context, const char* param, auth_methods **auth_method)
+{
+ if (!make_auth_methods(auth_context, auth_method)) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ (*auth_method)->name = "trustdomain";
+ (*auth_method)->auth = check_trustdomain_security;
+ return NT_STATUS_OK;
+}
+
+NTSTATUS auth_domain_init(void)
+{
+ smb_register_auth(AUTH_INTERFACE_VERSION, "trustdomain", auth_init_trustdomain);
+ smb_register_auth(AUTH_INTERFACE_VERSION, "ntdomain", auth_init_ntdomain);
+ return NT_STATUS_OK;
+}
diff --git a/source/auth/auth_ntlmssp.c b/source/auth/auth_ntlmssp.c
new file mode 100644
index 00000000000..a5ce101e5e7
--- /dev/null
+++ b/source/auth/auth_ntlmssp.c
@@ -0,0 +1,200 @@
+/*
+ Unix SMB/Netbios implementation.
+ Version 3.0
+ handle NLTMSSP, server side
+
+ Copyright (C) Andrew Tridgell 2001
+ Copyright (C) Andrew Bartlett 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 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"
+
+/**
+ * Return the challenge as determined by the authentication subsystem
+ * @return an 8 byte random challenge
+ */
+
+static const uint8 *auth_ntlmssp_get_challenge(const struct ntlmssp_state *ntlmssp_state)
+{
+ AUTH_NTLMSSP_STATE *auth_ntlmssp_state = ntlmssp_state->auth_context;
+ return auth_ntlmssp_state->auth_context->get_ntlm_challenge(auth_ntlmssp_state->auth_context);
+}
+
+/**
+ * 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 ntlmssp_state *ntlmssp_state)
+{
+ AUTH_NTLMSSP_STATE *auth_ntlmssp_state = ntlmssp_state->auth_context;
+ struct auth_context *auth_context = auth_ntlmssp_state->auth_context;
+
+ return auth_context->challenge_may_be_modified;
+}
+
+/**
+ * NTLM2 authentication modifies the effective challange,
+ * @param challenge The new challenge value
+ */
+static NTSTATUS auth_ntlmssp_set_challenge(struct ntlmssp_state *ntlmssp_state, DATA_BLOB *challenge)
+{
+ AUTH_NTLMSSP_STATE *auth_ntlmssp_state = ntlmssp_state->auth_context;
+ struct auth_context *auth_context = auth_ntlmssp_state->auth_context;
+
+ SMB_ASSERT(challenge->length == 8);
+
+ auth_context->challenge = data_blob_talloc(auth_context->mem_ctx,
+ challenge->data, challenge->length);
+
+ auth_context->challenge_set_by = "NTLMSSP callback (NTLM2)";
+
+ DEBUG(5, ("auth_context challenge set by %s\n", auth_context->challenge_set_by));
+ DEBUG(5, ("challenge is: \n"));
+ dump_data(5, (const char *)auth_context->challenge.data, auth_context->challenge.length);
+ return NT_STATUS_OK;
+}
+
+/**
+ * Check the password on an NTLMSSP login.
+ *
+ * Return the session keys used on the connection.
+ */
+
+static NTSTATUS auth_ntlmssp_check_password(struct ntlmssp_state *ntlmssp_state, DATA_BLOB *nt_session_key, DATA_BLOB *lm_session_key)
+{
+ AUTH_NTLMSSP_STATE *auth_ntlmssp_state = ntlmssp_state->auth_context;
+ uint32 auth_flags = AUTH_FLAG_NONE;
+ auth_usersupplied_info *user_info = NULL;
+ DATA_BLOB plaintext_password = data_blob(NULL, 0);
+ NTSTATUS nt_status;
+
+ if (auth_ntlmssp_state->ntlmssp_state->lm_resp.length) {
+ auth_flags |= AUTH_FLAG_LM_RESP;
+ }
+
+ if (auth_ntlmssp_state->ntlmssp_state->nt_resp.length == 24) {
+ auth_flags |= AUTH_FLAG_NTLM_RESP;
+ } else if (auth_ntlmssp_state->ntlmssp_state->nt_resp.length > 24) {
+ auth_flags |= AUTH_FLAG_NTLMv2_RESP;
+ }
+
+ /* the client has given us its machine name (which we otherwise would not get on port 445).
+ we need to possibly reload smb.conf if smb.conf includes depend on the machine name */
+
+ set_remote_machine_name(auth_ntlmssp_state->ntlmssp_state->workstation, True);
+
+ /* setup the string used by %U */
+ /* sub_set_smb_name checks for weird internally */
+ sub_set_smb_name(auth_ntlmssp_state->ntlmssp_state->user);
+
+ reload_services(True);
+
+ nt_status = make_user_info_map(&user_info,
+ auth_ntlmssp_state->ntlmssp_state->user,
+ auth_ntlmssp_state->ntlmssp_state->domain,
+ auth_ntlmssp_state->ntlmssp_state->workstation,
+ auth_ntlmssp_state->ntlmssp_state->lm_resp,
+ auth_ntlmssp_state->ntlmssp_state->nt_resp,
+ plaintext_password,
+ auth_flags, True);
+
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ return nt_status;
+ }
+
+ nt_status = auth_ntlmssp_state->auth_context->check_ntlm_password(auth_ntlmssp_state->auth_context,
+ user_info, &auth_ntlmssp_state->server_info);
+
+ free_user_info(&user_info);
+
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ return nt_status;
+ }
+ if (auth_ntlmssp_state->server_info->nt_session_key.length) {
+ DEBUG(10, ("Got NT session key of length %u\n", auth_ntlmssp_state->server_info->nt_session_key.length));
+ *nt_session_key = data_blob_talloc(auth_ntlmssp_state->mem_ctx,
+ auth_ntlmssp_state->server_info->nt_session_key.data,
+ auth_ntlmssp_state->server_info->nt_session_key.length);
+ }
+ if (auth_ntlmssp_state->server_info->lm_session_key.length) {
+ DEBUG(10, ("Got LM session key of length %u\n", auth_ntlmssp_state->server_info->lm_session_key.length));
+ *lm_session_key = data_blob_talloc(auth_ntlmssp_state->mem_ctx,
+ auth_ntlmssp_state->server_info->lm_session_key.data,
+ auth_ntlmssp_state->server_info->lm_session_key.length);
+ }
+ return nt_status;
+}
+
+NTSTATUS auth_ntlmssp_start(AUTH_NTLMSSP_STATE **auth_ntlmssp_state)
+{
+ NTSTATUS nt_status;
+ TALLOC_CTX *mem_ctx;
+
+ mem_ctx = talloc_init("AUTH NTLMSSP context");
+
+ *auth_ntlmssp_state = talloc_zero(mem_ctx, sizeof(**auth_ntlmssp_state));
+ if (!*auth_ntlmssp_state) {
+ DEBUG(0,("auth_ntlmssp_start: talloc failed!\n"));
+ talloc_destroy(mem_ctx);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ ZERO_STRUCTP(*auth_ntlmssp_state);
+
+ (*auth_ntlmssp_state)->mem_ctx = mem_ctx;
+
+ if (!NT_STATUS_IS_OK(nt_status = ntlmssp_server_start(&(*auth_ntlmssp_state)->ntlmssp_state))) {
+ return nt_status;
+ }
+
+ if (!NT_STATUS_IS_OK(nt_status = make_auth_context_subsystem(&(*auth_ntlmssp_state)->auth_context))) {
+ return nt_status;
+ }
+
+ (*auth_ntlmssp_state)->ntlmssp_state->auth_context = (*auth_ntlmssp_state);
+ (*auth_ntlmssp_state)->ntlmssp_state->get_challenge = auth_ntlmssp_get_challenge;
+ (*auth_ntlmssp_state)->ntlmssp_state->may_set_challenge = auth_ntlmssp_may_set_challenge;
+ (*auth_ntlmssp_state)->ntlmssp_state->set_challenge = auth_ntlmssp_set_challenge;
+ (*auth_ntlmssp_state)->ntlmssp_state->check_password = auth_ntlmssp_check_password;
+ (*auth_ntlmssp_state)->ntlmssp_state->server_role = lp_server_role();
+
+ return NT_STATUS_OK;
+}
+
+void auth_ntlmssp_end(AUTH_NTLMSSP_STATE **auth_ntlmssp_state)
+{
+ TALLOC_CTX *mem_ctx = (*auth_ntlmssp_state)->mem_ctx;
+
+ if ((*auth_ntlmssp_state)->ntlmssp_state) {
+ ntlmssp_end(&(*auth_ntlmssp_state)->ntlmssp_state);
+ }
+ if ((*auth_ntlmssp_state)->auth_context) {
+ ((*auth_ntlmssp_state)->auth_context->free)(&(*auth_ntlmssp_state)->auth_context);
+ }
+ if ((*auth_ntlmssp_state)->server_info) {
+ free_server_info(&(*auth_ntlmssp_state)->server_info);
+ }
+ talloc_destroy(mem_ctx);
+ *auth_ntlmssp_state = NULL;
+}
+
+NTSTATUS auth_ntlmssp_update(AUTH_NTLMSSP_STATE *auth_ntlmssp_state,
+ const DATA_BLOB request, DATA_BLOB *reply)
+{
+ return ntlmssp_update(auth_ntlmssp_state->ntlmssp_state, request, reply);
+}
diff --git a/source/auth/auth_rhosts.c b/source/auth/auth_rhosts.c
new file mode 100644
index 00000000000..b295df9328f
--- /dev/null
+++ b/source/auth/auth_rhosts.c
@@ -0,0 +1,257 @@
+/*
+ Unix SMB/CIFS implementation.
+ Main SMB reply routines
+ 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 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"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_AUTH
+
+/****************************************************************************
+ Read the a hosts.equiv or .rhosts file and check if it
+ allows this user from this machine.
+****************************************************************************/
+
+static BOOL check_user_equiv(const char *user, const char *remote, const char *equiv_file)
+{
+ int plus_allowed = 1;
+ char *file_host;
+ char *file_user;
+ char **lines = file_lines_load(equiv_file, NULL);
+ int i;
+
+ DEBUG(5, ("check_user_equiv %s %s %s\n", user, remote, equiv_file));
+ if (! lines) return False;
+ for (i=0; lines[i]; i++) {
+ char *buf = lines[i];
+ trim_char(buf,' ',' ');
+
+ if (buf[0] != '#' && buf[0] != '\n')
+ {
+ BOOL is_group = False;
+ int plus = 1;
+ char *bp = buf;
+ if (strcmp(buf, "NO_PLUS\n") == 0)
+ {
+ DEBUG(6, ("check_user_equiv NO_PLUS\n"));
+ plus_allowed = 0;
+ }
+ else {
+ if (buf[0] == '+')
+ {
+ bp++;
+ if (*bp == '\n' && plus_allowed)
+ {
+ /* a bare plus means everbody allowed */
+ DEBUG(6, ("check_user_equiv everybody allowed\n"));
+ file_lines_free(lines);
+ return True;
+ }
+ }
+ else if (buf[0] == '-')
+ {
+ bp++;
+ plus = 0;
+ }
+ if (*bp == '@')
+ {
+ is_group = True;
+ bp++;
+ }
+ file_host = strtok(bp, " \t\n");
+ file_user = strtok(NULL, " \t\n");
+ DEBUG(7, ("check_user_equiv %s %s\n", file_host ? file_host : "(null)",
+ file_user ? file_user : "(null)" ));
+ if (file_host && *file_host)
+ {
+ BOOL host_ok = False;
+
+#if defined(HAVE_NETGROUP) && defined(HAVE_YP_GET_DEFAULT_DOMAIN)
+ if (is_group)
+ {
+ static char *mydomain = NULL;
+ if (!mydomain)
+ yp_get_default_domain(&mydomain);
+ if (mydomain && innetgr(file_host,remote,user,mydomain))
+ host_ok = True;
+ }
+#else
+ if (is_group)
+ {
+ DEBUG(1,("Netgroups not configured\n"));
+ continue;
+ }
+#endif
+
+ /* is it this host */
+ /* the fact that remote has come from a call of gethostbyaddr
+ * means that it may have the fully qualified domain name
+ * so we could look up the file version to get it into
+ * a canonical form, but I would rather just type it
+ * in full in the equiv file
+ */
+ if (!host_ok && !is_group && strequal(remote, file_host))
+ host_ok = True;
+
+ if (!host_ok)
+ continue;
+
+ /* is it this user */
+ if (file_user == 0 || strequal(user, file_user))
+ {
+ DEBUG(5, ("check_user_equiv matched %s%s %s\n",
+ (plus ? "+" : "-"), file_host,
+ (file_user ? file_user : "")));
+ file_lines_free(lines);
+ return (plus ? True : False);
+ }
+ }
+ }
+ }
+ }
+ file_lines_free(lines);
+ return False;
+}
+
+/****************************************************************************
+check for a possible hosts equiv or rhosts entry for the user
+****************************************************************************/
+
+static BOOL check_hosts_equiv(SAM_ACCOUNT *account)
+{
+ uid_t uid;
+ char *fname = NULL;
+
+ fname = lp_hosts_equiv();
+ if (!NT_STATUS_IS_OK(sid_to_uid(pdb_get_user_sid(account), &uid)))
+ return False;
+
+ /* note: don't allow hosts.equiv on root */
+ if (fname && *fname && uid != 0) {
+ if (check_user_equiv(pdb_get_username(account),client_name(),fname))
+ return True;
+ }
+
+ return False;
+}
+
+
+/****************************************************************************
+ Check for a valid .rhosts/hosts.equiv entry for this user
+****************************************************************************/
+
+static NTSTATUS check_hostsequiv_security(const struct auth_context *auth_context,
+ void *my_private_data,
+ TALLOC_CTX *mem_ctx,
+ const auth_usersupplied_info *user_info,
+ auth_serversupplied_info **server_info)
+{
+ NTSTATUS nt_status;
+ SAM_ACCOUNT *account = NULL;
+ if (!NT_STATUS_IS_OK(nt_status =
+ auth_get_sam_account(user_info->internal_username.str,
+ &account))) {
+ if (NT_STATUS_EQUAL(nt_status, NT_STATUS_NO_SUCH_USER))
+ nt_status = NT_STATUS_NOT_IMPLEMENTED;
+ return nt_status;
+ }
+
+ if (check_hosts_equiv(account)) {
+ nt_status = make_server_info_sam(server_info, account);
+ } else {
+ pdb_free_sam(&account);
+ nt_status = NT_STATUS_NOT_IMPLEMENTED;
+ }
+
+ return nt_status;
+}
+
+/* module initialisation */
+static NTSTATUS auth_init_hostsequiv(struct auth_context *auth_context, const char* param, auth_methods **auth_method)
+{
+ if (!make_auth_methods(auth_context, auth_method)) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ (*auth_method)->auth = check_hostsequiv_security;
+ (*auth_method)->name = "hostsequiv";
+ return NT_STATUS_OK;
+}
+
+
+/****************************************************************************
+ Check for a valid .rhosts/hosts.equiv entry for this user
+****************************************************************************/
+
+static NTSTATUS check_rhosts_security(const struct auth_context *auth_context,
+ void *my_private_data,
+ TALLOC_CTX *mem_ctx,
+ const auth_usersupplied_info *user_info,
+ auth_serversupplied_info **server_info)
+{
+ NTSTATUS nt_status;
+ SAM_ACCOUNT *account = NULL;
+ pstring rhostsfile;
+ const char *home;
+
+ if (!NT_STATUS_IS_OK(nt_status =
+ auth_get_sam_account(user_info->internal_username.str,
+ &account))) {
+ if (NT_STATUS_EQUAL(nt_status, NT_STATUS_NO_SUCH_USER))
+ nt_status = NT_STATUS_NOT_IMPLEMENTED;
+ return nt_status;
+ }
+
+ home = pdb_get_unix_homedir(account);
+
+ if (home) {
+ slprintf(rhostsfile, sizeof(rhostsfile)-1, "%s/.rhosts", home);
+ become_root();
+ if (check_user_equiv(pdb_get_username(account),client_name(),rhostsfile)) {
+ nt_status = make_server_info_sam(server_info, account);
+ } else {
+ pdb_free_sam(&account);
+ }
+ unbecome_root();
+ } else {
+ pdb_free_sam(&account);
+ nt_status = NT_STATUS_NOT_IMPLEMENTED;
+ }
+
+ return nt_status;
+}
+
+/* module initialisation */
+static NTSTATUS auth_init_rhosts(struct auth_context *auth_context, const char *param, auth_methods **auth_method)
+{
+ if (!make_auth_methods(auth_context, auth_method)) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ (*auth_method)->auth = check_rhosts_security;
+ (*auth_method)->name = "rhosts";
+ return NT_STATUS_OK;
+}
+
+NTSTATUS auth_rhosts_init(void)
+{
+ smb_register_auth(AUTH_INTERFACE_VERSION, "rhosts", auth_init_rhosts);
+ smb_register_auth(AUTH_INTERFACE_VERSION, "hostsequiv", auth_init_hostsequiv);
+ return NT_STATUS_OK;
+}
diff --git a/source/auth/auth_sam.c b/source/auth/auth_sam.c
new file mode 100644
index 00000000000..b35cc78d6b2
--- /dev/null
+++ b/source/auth/auth_sam.c
@@ -0,0 +1,343 @@
+/*
+ Unix SMB/CIFS implementation.
+ Password and authentication handling
+ Copyright (C) Andrew Tridgell 1992-2000
+ Copyright (C) Luke Kenneth Casson Leighton 1996-2000
+ Copyright (C) Andrew Bartlett 2001-2003
+ Copyright (C) Gerald Carter 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 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"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_AUTH
+
+/****************************************************************************
+ Do a specific test for an smb password being correct, given a smb_password and
+ the lanman and NT responses.
+****************************************************************************/
+
+static NTSTATUS sam_password_ok(const struct auth_context *auth_context,
+ TALLOC_CTX *mem_ctx,
+ SAM_ACCOUNT *sampass,
+ const auth_usersupplied_info *user_info,
+ DATA_BLOB *user_sess_key,
+ DATA_BLOB *lm_sess_key)
+{
+ uint16 acct_ctrl;
+ const uint8 *lm_pw, *nt_pw;
+ const char *username = pdb_get_username(sampass);
+
+ acct_ctrl = pdb_get_acct_ctrl(sampass);
+ if (acct_ctrl & ACB_PWNOTREQ) {
+ if (lp_null_passwords()) {
+ DEBUG(3,("Account for user '%s' has no password and null passwords are allowed.\n", username));
+ return NT_STATUS_OK;
+ } else {
+ DEBUG(3,("Account for user '%s' has no password and null passwords are NOT allowed.\n", username));
+ return NT_STATUS_LOGON_FAILURE;
+ }
+ }
+
+ lm_pw = pdb_get_lanman_passwd(sampass);
+ nt_pw = pdb_get_nt_passwd(sampass);
+
+ return ntlm_password_check(mem_ctx, &auth_context->challenge,
+ &user_info->lm_resp, &user_info->nt_resp,
+ username,
+ user_info->smb_name.str,
+ user_info->client_domain.str,
+ lm_pw, nt_pw, user_sess_key, lm_sess_key);
+}
+
+
+/****************************************************************************
+ Do a specific test for a SAM_ACCOUNT being vaild for this connection
+ (ie not disabled, expired and the like).
+****************************************************************************/
+
+static NTSTATUS sam_account_ok(TALLOC_CTX *mem_ctx,
+ SAM_ACCOUNT *sampass,
+ const auth_usersupplied_info *user_info)
+{
+ uint16 acct_ctrl = pdb_get_acct_ctrl(sampass);
+ char *workstation_list;
+ time_t kickoff_time;
+
+ DEBUG(4,("sam_account_ok: Checking SMB password for user %s\n",pdb_get_username(sampass)));
+
+ /* Quit if the account was disabled. */
+ if (acct_ctrl & ACB_DISABLED) {
+ DEBUG(1,("sam_account_ok: Account for user '%s' was disabled.\n", pdb_get_username(sampass)));
+ return NT_STATUS_ACCOUNT_DISABLED;
+ }
+
+ /* Quit if the account was locked out. */
+ if (acct_ctrl & ACB_AUTOLOCK) {
+ DEBUG(1,("sam_account_ok: Account for user %s was locked out.\n", pdb_get_username(sampass)));
+ return NT_STATUS_ACCOUNT_LOCKED_OUT;
+ }
+
+ /* Test account expire time */
+
+ kickoff_time = pdb_get_kickoff_time(sampass);
+ if (kickoff_time != 0 && time(NULL) > kickoff_time) {
+ DEBUG(1,("sam_account_ok: Account for user '%s' has expired.\n", pdb_get_username(sampass)));
+ DEBUG(3,("sam_account_ok: Account expired at '%ld' unix time.\n", (long)kickoff_time));
+ return NT_STATUS_ACCOUNT_EXPIRED;
+ }
+
+ if (!(pdb_get_acct_ctrl(sampass) & ACB_PWNOEXP)) {
+ time_t must_change_time = pdb_get_pass_must_change_time(sampass);
+ time_t last_set_time = pdb_get_pass_last_set_time(sampass);
+
+ /* check for immediate expiry "must change at next logon" */
+ if (must_change_time == 0 && last_set_time != 0) {
+ DEBUG(1,("sam_account_ok: Account for user '%s' password must change!.\n", pdb_get_username(sampass)));
+ return NT_STATUS_PASSWORD_MUST_CHANGE;
+ }
+
+ /* check for expired password */
+ if (must_change_time < time(NULL) && must_change_time != 0) {
+ DEBUG(1,("sam_account_ok: Account for user '%s' password expired!.\n", pdb_get_username(sampass)));
+ DEBUG(1,("sam_account_ok: Password expired at '%s' (%ld) unix time.\n", http_timestring(must_change_time), (long)must_change_time));
+ return NT_STATUS_PASSWORD_EXPIRED;
+ }
+ }
+
+ /* Test workstation. Workstation list is comma separated. */
+
+ workstation_list = talloc_strdup(mem_ctx, pdb_get_workstations(sampass));
+ if (!workstation_list)
+ return NT_STATUS_NO_MEMORY;
+
+ if (*workstation_list) {
+ BOOL invalid_ws = True;
+ const char *s = workstation_list;
+
+ fstring tok;
+
+ while (next_token(&s, tok, ",", sizeof(tok))) {
+ DEBUG(10,("sam_account_ok: checking for workstation match %s and %s (len=%d)\n",
+ tok, user_info->wksta_name.str, user_info->wksta_name.len));
+ if(strequal(tok, user_info->wksta_name.str)) {
+ invalid_ws = False;
+ break;
+ }
+ }
+
+ if (invalid_ws)
+ return NT_STATUS_INVALID_WORKSTATION;
+ }
+
+ if (acct_ctrl & ACB_DOMTRUST) {
+ DEBUG(2,("sam_account_ok: Domain trust account %s denied by server\n", pdb_get_username(sampass)));
+ return NT_STATUS_NOLOGON_INTERDOMAIN_TRUST_ACCOUNT;
+ }
+
+ if (acct_ctrl & ACB_SVRTRUST) {
+ DEBUG(2,("sam_account_ok: Server trust account %s denied by server\n", pdb_get_username(sampass)));
+ return NT_STATUS_NOLOGON_SERVER_TRUST_ACCOUNT;
+ }
+
+ if (acct_ctrl & ACB_WSTRUST) {
+ DEBUG(4,("sam_account_ok: Wksta trust account %s denied by server\n", pdb_get_username(sampass)));
+ return NT_STATUS_NOLOGON_WORKSTATION_TRUST_ACCOUNT;
+ }
+
+ return NT_STATUS_OK;
+}
+
+/****************************************************************************
+check if a username/password is OK assuming the password is a 24 byte
+SMB hash supplied in the user_info structure
+return an NT_STATUS constant.
+****************************************************************************/
+
+static NTSTATUS check_sam_security(const struct auth_context *auth_context,
+ void *my_private_data,
+ TALLOC_CTX *mem_ctx,
+ const auth_usersupplied_info *user_info,
+ auth_serversupplied_info **server_info)
+{
+ SAM_ACCOUNT *sampass=NULL;
+ BOOL ret;
+ NTSTATUS nt_status;
+ DATA_BLOB user_sess_key = data_blob(NULL, 0);
+ DATA_BLOB lm_sess_key = data_blob(NULL, 0);
+ BOOL updated_autolock = False, updated_badpw = False;
+
+ if (!user_info || !auth_context) {
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ /* Can't use the talloc version here, because the returned struct gets
+ kept on the server_info */
+ if (!NT_STATUS_IS_OK(nt_status = pdb_init_sam(&sampass))) {
+ return nt_status;
+ }
+
+ /* get the account information */
+
+ become_root();
+ ret = pdb_getsampwnam(sampass, user_info->internal_username.str);
+ unbecome_root();
+
+ if (ret == False) {
+ DEBUG(3,("check_sam_security: Couldn't find user '%s' in passdb file.\n", user_info->internal_username.str));
+ pdb_free_sam(&sampass);
+ return NT_STATUS_NO_SUCH_USER;
+ }
+
+ /* see if autolock flag needs to be updated */
+ if (pdb_get_acct_ctrl(sampass) & ACB_NORMAL)
+ pdb_update_autolock_flag(sampass, &updated_autolock);
+ /* Quit if the account was locked out. */
+ if (pdb_get_acct_ctrl(sampass) & ACB_AUTOLOCK) {
+ DEBUG(3,("check_sam_security: Account for user %s was locked out.\n", pdb_get_username(sampass)));
+ return NT_STATUS_ACCOUNT_LOCKED_OUT;
+ }
+
+ nt_status = sam_password_ok(auth_context, mem_ctx, sampass,
+ user_info, &user_sess_key, &lm_sess_key);
+
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ if (NT_STATUS_EQUAL(nt_status,NT_STATUS_WRONG_PASSWORD) &&
+ pdb_get_acct_ctrl(sampass) &ACB_NORMAL) {
+ pdb_increment_bad_password_count(sampass);
+ updated_badpw = True;
+ } else {
+ pdb_update_bad_password_count(sampass,
+ &updated_badpw);
+ }
+ if (updated_autolock || updated_badpw){
+ become_root();
+ if(!pdb_update_sam_account(sampass))
+ DEBUG(1, ("Failed to modify entry.\n"));
+ unbecome_root();
+ }
+ pdb_free_sam(&sampass);
+ return nt_status;
+ }
+
+ if ((pdb_get_acct_ctrl(sampass) & ACB_NORMAL) &&
+ (pdb_get_bad_password_count(sampass) > 0)){
+ pdb_set_bad_password_count(sampass, 0, PDB_CHANGED);
+ pdb_set_bad_password_time(sampass, 0, PDB_CHANGED);
+ updated_badpw = True;
+ }
+
+ if (updated_autolock || updated_badpw){
+ become_root();
+ if(!pdb_update_sam_account(sampass))
+ DEBUG(1, ("Failed to modify entry.\n"));
+ unbecome_root();
+ }
+
+ nt_status = sam_account_ok(mem_ctx, sampass, user_info);
+
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ pdb_free_sam(&sampass);
+ return nt_status;
+ }
+
+ if (!NT_STATUS_IS_OK(nt_status = make_server_info_sam(server_info, sampass))) {
+ DEBUG(0,("check_sam_security: make_server_info_sam() failed with '%s'\n", nt_errstr(nt_status)));
+ return nt_status;
+ }
+
+ (*server_info)->nt_session_key = user_sess_key;
+ (*server_info)->lm_session_key = lm_sess_key;
+
+ return nt_status;
+}
+
+/* module initialisation */
+static NTSTATUS auth_init_sam_ignoredomain(struct auth_context *auth_context, const char *param, auth_methods **auth_method)
+{
+ if (!make_auth_methods(auth_context, auth_method)) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ (*auth_method)->auth = check_sam_security;
+ (*auth_method)->name = "sam_ignoredomain";
+ return NT_STATUS_OK;
+}
+
+
+/****************************************************************************
+Check SAM security (above) but with a few extra checks.
+****************************************************************************/
+
+static NTSTATUS check_samstrict_security(const struct auth_context *auth_context,
+ void *my_private_data,
+ TALLOC_CTX *mem_ctx,
+ const auth_usersupplied_info *user_info,
+ auth_serversupplied_info **server_info)
+{
+ BOOL is_local_name, is_my_domain;
+
+ if (!user_info || !auth_context) {
+ return NT_STATUS_LOGON_FAILURE;
+ }
+
+ is_local_name = is_myname(user_info->domain.str);
+ is_my_domain = strequal(user_info->domain.str, lp_workgroup());
+
+ /* check whether or not we service this domain/workgroup name */
+
+ switch ( lp_server_role() ) {
+ case ROLE_STANDALONE:
+ case ROLE_DOMAIN_MEMBER:
+ if ( !is_local_name ) {
+ DEBUG(6,("check_samstrict_security: %s is not one of my local names (%s)\n",
+ user_info->domain.str, (lp_server_role() == ROLE_DOMAIN_MEMBER
+ ? "ROLE_DOMAIN_MEMBER" : "ROLE_STANDALONE") ));
+ return NT_STATUS_NOT_IMPLEMENTED;
+ }
+ case ROLE_DOMAIN_PDC:
+ case ROLE_DOMAIN_BDC:
+ if ( !is_local_name && !is_my_domain ) {
+ DEBUG(6,("check_samstrict_security: %s is not one of my local names or domain name (DC)\n",
+ user_info->domain.str));
+ return NT_STATUS_NOT_IMPLEMENTED;
+ }
+ default: /* name is ok */
+ break;
+ }
+
+ return check_sam_security(auth_context, my_private_data, mem_ctx, user_info, server_info);
+}
+
+/* module initialisation */
+static NTSTATUS auth_init_sam(struct auth_context *auth_context, const char *param, auth_methods **auth_method)
+{
+ if (!make_auth_methods(auth_context, auth_method)) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ (*auth_method)->auth = check_samstrict_security;
+ (*auth_method)->name = "sam";
+ return NT_STATUS_OK;
+}
+
+NTSTATUS auth_sam_init(void)
+{
+ smb_register_auth(AUTH_INTERFACE_VERSION, "sam", auth_init_sam);
+ smb_register_auth(AUTH_INTERFACE_VERSION, "sam_ignoredomain", auth_init_sam_ignoredomain);
+ return NT_STATUS_OK;
+}
diff --git a/source/auth/auth_server.c b/source/auth/auth_server.c
new file mode 100644
index 00000000000..bc611ec229b
--- /dev/null
+++ b/source/auth/auth_server.c
@@ -0,0 +1,417 @@
+/*
+ Unix SMB/CIFS implementation.
+ Authenticate to a remote server
+ Copyright (C) Andrew Tridgell 1992-1998
+ Copyright (C) Andrew Bartlett 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 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"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_AUTH
+
+extern userdom_struct current_user_info;
+
+/****************************************************************************
+ Support for server level security.
+****************************************************************************/
+
+static struct cli_state *server_cryptkey(TALLOC_CTX *mem_ctx)
+{
+ struct cli_state *cli = NULL;
+ fstring desthost;
+ struct in_addr dest_ip;
+ const char *p;
+ char *pserver;
+ BOOL connected_ok = False;
+
+ if (!(cli = cli_initialise(cli)))
+ return NULL;
+
+ /* security = server just can't function with spnego */
+ cli->use_spnego = False;
+
+ pserver = talloc_strdup(mem_ctx, lp_passwordserver());
+ p = pserver;
+
+ while(next_token( &p, desthost, LIST_SEP, sizeof(desthost))) {
+ standard_sub_basic(current_user_info.smb_name, desthost, sizeof(desthost));
+ strupper_m(desthost);
+
+ if(!resolve_name( desthost, &dest_ip, 0x20)) {
+ DEBUG(1,("server_cryptkey: Can't resolve address for %s\n",desthost));
+ continue;
+ }
+
+ if (ismyip(dest_ip)) {
+ DEBUG(1,("Password server loop - disabling password server %s\n",desthost));
+ continue;
+ }
+
+ /* we use a mutex to prevent two connections at once - when a
+ Win2k PDC get two connections where one hasn't completed a
+ session setup yet it will send a TCP reset to the first
+ connection (tridge) */
+
+ if (!grab_server_mutex(desthost)) {
+ return NULL;
+ }
+
+ if (cli_connect(cli, desthost, &dest_ip)) {
+ DEBUG(3,("connected to password server %s\n",desthost));
+ connected_ok = True;
+ break;
+ }
+ }
+
+ if (!connected_ok) {
+ release_server_mutex();
+ DEBUG(0,("password server not available\n"));
+ cli_shutdown(cli);
+ return NULL;
+ }
+
+ if (!attempt_netbios_session_request(cli, global_myname(),
+ desthost, &dest_ip)) {
+ release_server_mutex();
+ DEBUG(1,("password server fails session request\n"));
+ cli_shutdown(cli);
+ return NULL;
+ }
+
+ if (strequal(desthost,myhostname())) {
+ exit_server("Password server loop!");
+ }
+
+ DEBUG(3,("got session\n"));
+
+ if (!cli_negprot(cli)) {
+ DEBUG(1,("%s rejected the negprot\n",desthost));
+ release_server_mutex();
+ cli_shutdown(cli);
+ return NULL;
+ }
+
+ if (cli->protocol < PROTOCOL_LANMAN2 ||
+ !(cli->sec_mode & NEGOTIATE_SECURITY_USER_LEVEL)) {
+ DEBUG(1,("%s isn't in user level security mode\n",desthost));
+ release_server_mutex();
+ cli_shutdown(cli);
+ return NULL;
+ }
+
+ /* Get the first session setup done quickly, to avoid silly
+ Win2k bugs. (The next connection to the server will kill
+ this one...
+ */
+
+ if (!cli_session_setup(cli, "", "", 0, "", 0,
+ "")) {
+ DEBUG(0,("%s rejected the initial session setup (%s)\n",
+ desthost, cli_errstr(cli)));
+ release_server_mutex();
+ cli_shutdown(cli);
+ return NULL;
+ }
+
+ release_server_mutex();
+
+ DEBUG(3,("password server OK\n"));
+
+ return cli;
+}
+
+/****************************************************************************
+ Clean up our allocated cli.
+****************************************************************************/
+
+static void free_server_private_data(void **private_data_pointer)
+{
+ struct cli_state **cli = (struct cli_state **)private_data_pointer;
+ if (*cli && (*cli)->initialised) {
+ DEBUG(10, ("Shutting down smbserver connection\n"));
+ cli_shutdown(*cli);
+ }
+ *private_data_pointer = NULL;
+}
+
+/****************************************************************************
+ Send a 'keepalive' packet down the cli pipe.
+****************************************************************************/
+
+static void send_server_keepalive(void **private_data_pointer)
+{
+ /* also send a keepalive to the password server if its still
+ connected */
+ if (private_data_pointer) {
+ struct cli_state *cli = (struct cli_state *)(*private_data_pointer);
+ if (cli && cli->initialised) {
+ if (!send_keepalive(cli->fd)) {
+ DEBUG( 2, ( "send_server_keepalive: password server keepalive failed.\n"));
+ cli_shutdown(cli);
+ *private_data_pointer = NULL;
+ }
+ }
+ }
+}
+
+/****************************************************************************
+ Get the challenge out of a password server.
+****************************************************************************/
+
+static DATA_BLOB auth_get_challenge_server(const struct auth_context *auth_context,
+ void **my_private_data,
+ TALLOC_CTX *mem_ctx)
+{
+ struct cli_state *cli = server_cryptkey(mem_ctx);
+
+ if (cli) {
+ DEBUG(3,("using password server validation\n"));
+
+ if ((cli->sec_mode & NEGOTIATE_SECURITY_CHALLENGE_RESPONSE) == 0) {
+ /* We can't work with unencrypted password servers
+ unless 'encrypt passwords = no' */
+ DEBUG(5,("make_auth_info_server: Server is unencrypted, no challenge available..\n"));
+
+ /* However, it is still a perfectly fine connection
+ to pass that unencrypted password over */
+ *my_private_data = (void *)cli;
+ return data_blob(NULL, 0);
+
+ } else if (cli->secblob.length < 8) {
+ /* We can't do much if we don't get a full challenge */
+ DEBUG(2,("make_auth_info_server: Didn't receive a full challenge from server\n"));
+ cli_shutdown(cli);
+ return data_blob(NULL, 0);
+ }
+
+ *my_private_data = (void *)cli;
+
+ /* The return must be allocated on the caller's mem_ctx, as our own will be
+ destoyed just after the call. */
+ return data_blob_talloc(auth_context->mem_ctx, cli->secblob.data,8);
+ } else {
+ return data_blob(NULL, 0);
+ }
+}
+
+
+/****************************************************************************
+ Check for a valid username and password in security=server mode.
+ - Validate a password with the password server.
+****************************************************************************/
+
+static NTSTATUS check_smbserver_security(const struct auth_context *auth_context,
+ void *my_private_data,
+ TALLOC_CTX *mem_ctx,
+ const auth_usersupplied_info *user_info,
+ auth_serversupplied_info **server_info)
+{
+ struct cli_state *cli;
+ static unsigned char badpass[24];
+ static fstring baduser;
+ static BOOL tested_password_server = False;
+ static BOOL bad_password_server = False;
+ NTSTATUS nt_status = NT_STATUS_NOT_IMPLEMENTED;
+ BOOL locally_made_cli = False;
+
+ /*
+ * Check that the requested domain is not our own machine name.
+ * If it is, we should never check the PDC here, we use our own local
+ * password file.
+ */
+
+ if(is_myname(user_info->domain.str)) {
+ DEBUG(3,("check_smbserver_security: Requested domain was for this machine.\n"));
+ return nt_status;
+ }
+
+ cli = my_private_data;
+
+ if (cli) {
+ } else {
+ cli = server_cryptkey(mem_ctx);
+ locally_made_cli = True;
+ }
+
+ if (!cli || !cli->initialised) {
+ DEBUG(1,("password server is not connected (cli not initilised)\n"));
+ return NT_STATUS_LOGON_FAILURE;
+ }
+
+ if ((cli->sec_mode & NEGOTIATE_SECURITY_CHALLENGE_RESPONSE) == 0) {
+ if (user_info->encrypted) {
+ DEBUG(1,("password server %s is plaintext, but we are encrypted. This just can't work :-(\n", cli->desthost));
+ return NT_STATUS_LOGON_FAILURE;
+ }
+ } else {
+ if (memcmp(cli->secblob.data, auth_context->challenge.data, 8) != 0) {
+ DEBUG(1,("the challenge that the password server (%s) supplied us is not the one we gave our client. This just can't work :-(\n", cli->desthost));
+ return NT_STATUS_LOGON_FAILURE;
+ }
+ }
+
+ if(badpass[0] == 0)
+ memset(badpass, 0x1f, sizeof(badpass));
+
+ if((user_info->nt_resp.length == sizeof(badpass)) &&
+ !memcmp(badpass, user_info->nt_resp.data, sizeof(badpass))) {
+ /*
+ * Very unlikely, our random bad password is the same as the users
+ * password.
+ */
+ memset(badpass, badpass[0]+1, sizeof(badpass));
+ }
+
+ if(baduser[0] == 0) {
+ fstrcpy(baduser, INVALID_USER_PREFIX);
+ fstrcat(baduser, global_myname());
+ }
+
+ /*
+ * Attempt a session setup with a totally incorrect password.
+ * If this succeeds with the guest bit *NOT* set then the password
+ * server is broken and is not correctly setting the guest bit. We
+ * need to detect this as some versions of NT4.x are broken. JRA.
+ */
+
+ /* I sure as hell hope that there aren't servers out there that take
+ * NTLMv2 and have this bug, as we don't test for that...
+ * - abartlet@samba.org
+ */
+
+ if ((!tested_password_server) && (lp_paranoid_server_security())) {
+ if (cli_session_setup(cli, baduser, (char *)badpass, sizeof(badpass),
+ (char *)badpass, sizeof(badpass), user_info->domain.str)) {
+
+ /*
+ * We connected to the password server so we
+ * can say we've tested it.
+ */
+ tested_password_server = True;
+
+ if ((SVAL(cli->inbuf,smb_vwv2) & 1) == 0) {
+ DEBUG(0,("server_validate: password server %s allows users as non-guest \
+with a bad password.\n", cli->desthost));
+ DEBUG(0,("server_validate: This is broken (and insecure) behaviour. Please do not \
+use this machine as the password server.\n"));
+ cli_ulogoff(cli);
+
+ /*
+ * Password server has the bug.
+ */
+ bad_password_server = True;
+ return NT_STATUS_LOGON_FAILURE;
+ }
+ cli_ulogoff(cli);
+ }
+ } else {
+
+ /*
+ * We have already tested the password server.
+ * Fail immediately if it has the bug.
+ */
+
+ if(bad_password_server) {
+ DEBUG(0,("server_validate: [1] password server %s allows users as non-guest \
+with a bad password.\n", cli->desthost));
+ DEBUG(0,("server_validate: [1] This is broken (and insecure) behaviour. Please do not \
+use this machine as the password server.\n"));
+ return NT_STATUS_LOGON_FAILURE;
+ }
+ }
+
+ /*
+ * Now we know the password server will correctly set the guest bit, or is
+ * not guest enabled, we can try with the real password.
+ */
+
+ if (!user_info->encrypted) {
+ /* Plaintext available */
+ if (!cli_session_setup(cli, user_info->smb_name.str,
+ (char *)user_info->plaintext_password.data,
+ user_info->plaintext_password.length,
+ NULL, 0,
+ user_info->domain.str)) {
+ DEBUG(1,("password server %s rejected the password\n", cli->desthost));
+ /* Make this cli_nt_error() when the conversion is in */
+ nt_status = cli_nt_error(cli);
+ } else {
+ nt_status = NT_STATUS_OK;
+ }
+ } else {
+ if (!cli_session_setup(cli, user_info->smb_name.str,
+ (char *)user_info->lm_resp.data,
+ user_info->lm_resp.length,
+ (char *)user_info->nt_resp.data,
+ user_info->nt_resp.length,
+ user_info->domain.str)) {
+ DEBUG(1,("password server %s rejected the password\n", cli->desthost));
+ /* Make this cli_nt_error() when the conversion is in */
+ nt_status = cli_nt_error(cli);
+ } else {
+ nt_status = NT_STATUS_OK;
+ }
+ }
+
+ /* if logged in as guest then reject */
+ if ((SVAL(cli->inbuf,smb_vwv2) & 1) != 0) {
+ DEBUG(1,("password server %s gave us guest only\n", cli->desthost));
+ nt_status = NT_STATUS_LOGON_FAILURE;
+ }
+
+ cli_ulogoff(cli);
+
+ if (NT_STATUS_IS_OK(nt_status)) {
+ fstring real_username;
+ struct passwd *pass;
+
+ if ( (pass = smb_getpwnam( user_info->internal_username.str,
+ real_username, True )) != NULL )
+ {
+ nt_status = make_server_info_pw(server_info, pass->pw_name, pass);
+ }
+ else
+ {
+ nt_status = NT_STATUS_NO_SUCH_USER;
+ }
+ }
+
+ if (locally_made_cli) {
+ cli_shutdown(cli);
+ }
+
+ return(nt_status);
+}
+
+static NTSTATUS auth_init_smbserver(struct auth_context *auth_context, const char* param, auth_methods **auth_method)
+{
+ if (!make_auth_methods(auth_context, auth_method)) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ (*auth_method)->name = "smbserver";
+ (*auth_method)->auth = check_smbserver_security;
+ (*auth_method)->get_chal = auth_get_challenge_server;
+ (*auth_method)->send_keepalive = send_server_keepalive;
+ (*auth_method)->free_private_data = free_server_private_data;
+ return NT_STATUS_OK;
+}
+
+NTSTATUS auth_server_init(void)
+{
+ return smb_register_auth(AUTH_INTERFACE_VERSION, "smbserver", auth_init_smbserver);
+}
diff --git a/source/auth/auth_unix.c b/source/auth/auth_unix.c
new file mode 100644
index 00000000000..f744cba0c43
--- /dev/null
+++ b/source/auth/auth_unix.c
@@ -0,0 +1,136 @@
+/*
+ Unix SMB/CIFS implementation.
+ Password and authentication handling
+ Copyright (C) Andrew Bartlett 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 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"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_AUTH
+
+/**
+ * update the encrypted smbpasswd file from the plaintext username and password
+ *
+ * this ugly hack needs to die, but not quite yet, I think people still use it...
+ **/
+static BOOL update_smbpassword_file(const char *user, const char *password)
+{
+ SAM_ACCOUNT *sampass = NULL;
+ BOOL ret;
+
+ pdb_init_sam(&sampass);
+
+ become_root();
+ ret = pdb_getsampwnam(sampass, user);
+ unbecome_root();
+
+ if(ret == False) {
+ DEBUG(0,("pdb_getsampwnam returned NULL\n"));
+ pdb_free_sam(&sampass);
+ return False;
+ }
+
+ /*
+ * Remove the account disabled flag - we are updating the
+ * users password from a login.
+ */
+ if (!pdb_set_acct_ctrl(sampass, pdb_get_acct_ctrl(sampass) & ~ACB_DISABLED, PDB_CHANGED)) {
+ pdb_free_sam(&sampass);
+ return False;
+ }
+
+ if (!pdb_set_plaintext_passwd (sampass, password)) {
+ pdb_free_sam(&sampass);
+ return False;
+ }
+
+ /* Now write it into the file. */
+ become_root();
+
+ ret = pdb_update_sam_account (sampass);
+
+ unbecome_root();
+
+ if (ret) {
+ DEBUG(3,("pdb_update_sam_account returned %d\n",ret));
+ }
+
+ pdb_free_sam(&sampass);
+ return ret;
+}
+
+
+/** Check a plaintext username/password
+ *
+ * Cannot deal with an encrupted password in any manner whatsoever,
+ * unless the account has a null password.
+ **/
+
+static NTSTATUS check_unix_security(const struct auth_context *auth_context,
+ void *my_private_data,
+ TALLOC_CTX *mem_ctx,
+ const auth_usersupplied_info *user_info,
+ auth_serversupplied_info **server_info)
+{
+ NTSTATUS nt_status;
+ struct passwd *pass = NULL;
+
+ become_root();
+ pass = Get_Pwnam(user_info->internal_username.str);
+
+
+ /** @todo This call assumes a ASCII password, no charset transformation is
+ done. We may need to revisit this **/
+ nt_status = pass_check(pass,
+ pass ? pass->pw_name : user_info->internal_username.str,
+ (char *)user_info->plaintext_password.data,
+ user_info->plaintext_password.length-1,
+ lp_update_encrypted() ?
+ update_smbpassword_file : NULL,
+ True);
+
+ unbecome_root();
+
+ if (NT_STATUS_IS_OK(nt_status)) {
+ if (pass) {
+ make_server_info_pw(server_info, pass->pw_name, pass);
+ } else {
+ /* we need to do somthing more useful here */
+ nt_status = NT_STATUS_NO_SUCH_USER;
+ }
+ }
+
+ return nt_status;
+}
+
+/* module initialisation */
+static NTSTATUS auth_init_unix(struct auth_context *auth_context, const char* param, auth_methods **auth_method)
+{
+ if (!make_auth_methods(auth_context, auth_method)) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ (*auth_method)->name = "unix";
+ (*auth_method)->auth = check_unix_security;
+ return NT_STATUS_OK;
+}
+
+NTSTATUS auth_unix_init(void)
+{
+ return smb_register_auth(AUTH_INTERFACE_VERSION, "unix", auth_init_unix);
+}
diff --git a/source/auth/auth_util.c b/source/auth/auth_util.c
new file mode 100644
index 00000000000..a823991bcad
--- /dev/null
+++ b/source/auth/auth_util.c
@@ -0,0 +1,1500 @@
+/*
+ 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
+
+ This program is free software; you can 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"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_AUTH
+
+extern DOM_SID global_sid_World;
+extern DOM_SID global_sid_Network;
+extern DOM_SID global_sid_Builtin_Guests;
+extern DOM_SID global_sid_Authenticated_Users;
+
+
+/****************************************************************************
+ Create a UNIX user on demand.
+****************************************************************************/
+
+static int smb_create_user(const char *domain, const char *unix_username, const char *homedir)
+{
+ pstring add_script;
+ int ret;
+
+ pstrcpy(add_script, lp_adduser_script());
+ if (! *add_script)
+ return -1;
+ all_string_sub(add_script, "%u", unix_username, sizeof(pstring));
+ if (domain)
+ all_string_sub(add_script, "%D", domain, sizeof(pstring));
+ if (homedir)
+ all_string_sub(add_script, "%H", homedir, sizeof(pstring));
+ ret = smbrun(add_script,NULL);
+ DEBUG(3,("smb_create_user: Running the command `%s' gave %d\n",add_script,ret));
+ return ret;
+}
+
+/****************************************************************************
+ Add and Delete UNIX users on demand, based on NTSTATUS codes.
+ We don't care about RID's here so ignore.
+****************************************************************************/
+
+void auth_add_user_script(const char *domain, const char *username)
+{
+ uint32 rid;
+ /*
+ * User validated ok against Domain controller.
+ * If the admin wants us to try and create a UNIX
+ * user on the fly, do so.
+ */
+
+ if ( *lp_adduser_script() )
+ smb_create_user(domain, username, NULL);
+ else {
+ DEBUG(10,("auth_add_user_script: no 'add user script'. Asking winbindd\n"));
+
+ /* should never get here is we a re a domain member running winbindd
+ However, a host set for 'security = server' might run winbindd for
+ account allocation */
+
+ if ( !winbind_create_user(username, NULL) ) {
+ DEBUG(5,("auth_add_user_script: winbindd_create_user() failed\n"));
+ rid = 0;
+ }
+ }
+}
+
+/****************************************************************************
+ Create a SAM_ACCOUNT - either by looking in the pdb, or by faking it up from
+ unix info.
+****************************************************************************/
+
+NTSTATUS auth_get_sam_account(const char *user, SAM_ACCOUNT **account)
+{
+ BOOL pdb_ret;
+ NTSTATUS nt_status;
+ if (!NT_STATUS_IS_OK(nt_status = pdb_init_sam(account))) {
+ return nt_status;
+ }
+
+ become_root();
+ pdb_ret = pdb_getsampwnam(*account, user);
+ unbecome_root();
+
+ if (!pdb_ret) {
+
+ struct passwd *pass = Get_Pwnam(user);
+ if (!pass)
+ return NT_STATUS_NO_SUCH_USER;
+
+ if (!NT_STATUS_IS_OK(nt_status = pdb_fill_sam_pw(*account, pass))) {
+ return nt_status;
+ }
+ }
+ return NT_STATUS_OK;
+}
+
+/****************************************************************************
+ Create an auth_usersupplied_data structure
+****************************************************************************/
+
+static NTSTATUS make_user_info(auth_usersupplied_info **user_info,
+ const char *smb_name,
+ const char *internal_username,
+ const char *client_domain,
+ const char *domain,
+ const char *wksta_name,
+ DATA_BLOB lm_pwd, DATA_BLOB nt_pwd,
+ DATA_BLOB plaintext,
+ uint32 auth_flags, BOOL encrypted)
+{
+
+ DEBUG(5,("attempting to make a user_info for %s (%s)\n", internal_username, smb_name));
+
+ *user_info = malloc(sizeof(**user_info));
+ if (!user_info) {
+ DEBUG(0,("malloc failed for user_info (size %lu)\n", (unsigned long)sizeof(*user_info)));
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ ZERO_STRUCTP(*user_info);
+
+ DEBUG(5,("making strings for %s's user_info struct\n", internal_username));
+
+ (*user_info)->smb_name.str = strdup(smb_name);
+ if ((*user_info)->smb_name.str) {
+ (*user_info)->smb_name.len = strlen(smb_name);
+ } else {
+ free_user_info(user_info);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ (*user_info)->internal_username.str = strdup(internal_username);
+ if ((*user_info)->internal_username.str) {
+ (*user_info)->internal_username.len = strlen(internal_username);
+ } else {
+ free_user_info(user_info);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ (*user_info)->domain.str = strdup(domain);
+ if ((*user_info)->domain.str) {
+ (*user_info)->domain.len = strlen(domain);
+ } else {
+ free_user_info(user_info);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ (*user_info)->client_domain.str = strdup(client_domain);
+ if ((*user_info)->client_domain.str) {
+ (*user_info)->client_domain.len = strlen(client_domain);
+ } else {
+ free_user_info(user_info);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ (*user_info)->wksta_name.str = strdup(wksta_name);
+ if ((*user_info)->wksta_name.str) {
+ (*user_info)->wksta_name.len = strlen(wksta_name);
+ } else {
+ free_user_info(user_info);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ DEBUG(5,("making blobs for %s's user_info struct\n", internal_username));
+
+ (*user_info)->lm_resp = data_blob(lm_pwd.data, lm_pwd.length);
+ (*user_info)->nt_resp = data_blob(nt_pwd.data, nt_pwd.length);
+ (*user_info)->plaintext_password = data_blob(plaintext.data, plaintext.length);
+
+ (*user_info)->encrypted = encrypted;
+ (*user_info)->auth_flags = auth_flags;
+
+ DEBUG(10,("made an %sencrypted user_info for %s (%s)\n", encrypted ? "":"un" , internal_username, smb_name));
+
+ return NT_STATUS_OK;
+}
+
+/****************************************************************************
+ Create an auth_usersupplied_data structure after appropriate mapping.
+****************************************************************************/
+
+NTSTATUS make_user_info_map(auth_usersupplied_info **user_info,
+ const char *smb_name,
+ const char *client_domain,
+ const char *wksta_name,
+ DATA_BLOB lm_pwd, DATA_BLOB nt_pwd,
+ DATA_BLOB plaintext,
+ uint32 ntlmssp_flags, BOOL encrypted)
+{
+ const char *domain;
+ fstring internal_username;
+ fstrcpy(internal_username, smb_name);
+ map_username(internal_username);
+
+ DEBUG(5, ("make_user_info_map: Mapping user [%s]\\[%s] from workstation [%s]\n",
+ client_domain, smb_name, wksta_name));
+
+ /* don't allow "" as a domain, fixes a Win9X bug
+ where it doens't supply a domain for logon script
+ 'net use' commands. */
+
+ if ( *client_domain )
+ domain = client_domain;
+ else
+ domain = lp_workgroup();
+
+ /* do what win2k does. Always map unknown domains to our own
+ and let the "passdb backend" handle unknown users. */
+
+ if ( !is_trusted_domain(domain) && !strequal(domain, get_global_sam_name()) )
+ domain = get_default_sam_name();
+
+ /* we know that it is a trusted domain (and we are allowing them) or it is our domain */
+
+ return make_user_info(user_info, smb_name, internal_username,
+ client_domain, domain, wksta_name, lm_pwd, nt_pwd,
+ plaintext, ntlmssp_flags, encrypted);
+}
+
+/****************************************************************************
+ Create an auth_usersupplied_data, making the DATA_BLOBs here.
+ Decrypt and encrypt the passwords.
+****************************************************************************/
+
+BOOL make_user_info_netlogon_network(auth_usersupplied_info **user_info,
+ const char *smb_name,
+ const char *client_domain,
+ const char *wksta_name,
+ const uchar *lm_network_pwd, int lm_pwd_len,
+ const uchar *nt_network_pwd, int nt_pwd_len)
+{
+ BOOL ret;
+ NTSTATUS nt_status;
+ DATA_BLOB lm_blob = data_blob(lm_network_pwd, lm_pwd_len);
+ DATA_BLOB nt_blob = data_blob(nt_network_pwd, nt_pwd_len);
+ DATA_BLOB plaintext_blob = data_blob(NULL, 0);
+ uint32 auth_flags = AUTH_FLAG_NONE;
+
+ if (lm_pwd_len)
+ auth_flags |= AUTH_FLAG_LM_RESP;
+ if (nt_pwd_len == 24) {
+ auth_flags |= AUTH_FLAG_NTLM_RESP;
+ } else if (nt_pwd_len != 0) {
+ auth_flags |= AUTH_FLAG_NTLMv2_RESP;
+ }
+
+ nt_status = make_user_info_map(user_info,
+ smb_name, client_domain,
+ wksta_name,
+ lm_blob, nt_blob,
+ plaintext_blob,
+ auth_flags, True);
+
+ ret = NT_STATUS_IS_OK(nt_status) ? True : False;
+
+ data_blob_free(&lm_blob);
+ data_blob_free(&nt_blob);
+ return ret;
+}
+
+/****************************************************************************
+ Create an auth_usersupplied_data, making the DATA_BLOBs here.
+ Decrypt and encrypt the passwords.
+****************************************************************************/
+
+BOOL make_user_info_netlogon_interactive(auth_usersupplied_info **user_info,
+ const char *smb_name,
+ const char *client_domain,
+ const char *wksta_name,
+ const uchar chal[8],
+ const uchar lm_interactive_pwd[16],
+ const uchar nt_interactive_pwd[16],
+ const uchar *dc_sess_key)
+{
+ char lm_pwd[16];
+ char nt_pwd[16];
+ unsigned char local_lm_response[24];
+ unsigned char local_nt_response[24];
+ unsigned char key[16];
+ uint32 auth_flags = AUTH_FLAG_NONE;
+
+ ZERO_STRUCT(key);
+ memcpy(key, dc_sess_key, 8);
+
+ if (lm_interactive_pwd) memcpy(lm_pwd, lm_interactive_pwd, sizeof(lm_pwd));
+ if (nt_interactive_pwd) memcpy(nt_pwd, nt_interactive_pwd, sizeof(nt_pwd));
+
+#ifdef DEBUG_PASSWORD
+ DEBUG(100,("key:"));
+ dump_data(100, (char *)key, sizeof(key));
+
+ DEBUG(100,("lm owf password:"));
+ dump_data(100, lm_pwd, sizeof(lm_pwd));
+
+ DEBUG(100,("nt owf password:"));
+ dump_data(100, nt_pwd, sizeof(nt_pwd));
+#endif
+
+ SamOEMhash((uchar *)lm_pwd, key, sizeof(lm_pwd));
+ SamOEMhash((uchar *)nt_pwd, key, sizeof(nt_pwd));
+
+#ifdef DEBUG_PASSWORD
+ DEBUG(100,("decrypt of lm owf password:"));
+ dump_data(100, lm_pwd, sizeof(lm_pwd));
+
+ DEBUG(100,("decrypt of nt owf password:"));
+ dump_data(100, nt_pwd, sizeof(nt_pwd));
+#endif
+
+ SMBOWFencrypt((const unsigned char *)lm_pwd, chal, local_lm_response);
+ SMBOWFencrypt((const unsigned char *)nt_pwd, chal, local_nt_response);
+
+ /* Password info paranoia */
+ ZERO_STRUCT(lm_pwd);
+ ZERO_STRUCT(nt_pwd);
+ ZERO_STRUCT(key);
+
+ {
+ BOOL ret;
+ NTSTATUS nt_status;
+ DATA_BLOB local_lm_blob = data_blob(local_lm_response, sizeof(local_lm_response));
+ DATA_BLOB local_nt_blob = data_blob(local_nt_response, sizeof(local_nt_response));
+ DATA_BLOB plaintext_blob = data_blob(NULL, 0);
+
+ if (lm_interactive_pwd)
+ auth_flags |= AUTH_FLAG_LM_RESP;
+ if (nt_interactive_pwd)
+ auth_flags |= AUTH_FLAG_NTLM_RESP;
+
+ nt_status = make_user_info_map(user_info,
+ smb_name, client_domain,
+ wksta_name,
+ local_lm_blob,
+ local_nt_blob,
+ plaintext_blob,
+ auth_flags, True);
+
+ ret = NT_STATUS_IS_OK(nt_status) ? True : False;
+ data_blob_free(&local_lm_blob);
+ data_blob_free(&local_nt_blob);
+ return ret;
+ }
+}
+
+
+/****************************************************************************
+ Create an auth_usersupplied_data structure
+****************************************************************************/
+
+BOOL make_user_info_for_reply(auth_usersupplied_info **user_info,
+ const char *smb_name,
+ const char *client_domain,
+ const uint8 chal[8],
+ DATA_BLOB plaintext_password)
+{
+
+ DATA_BLOB local_lm_blob;
+ DATA_BLOB local_nt_blob;
+ NTSTATUS ret = NT_STATUS_UNSUCCESSFUL;
+ uint32 auth_flags = AUTH_FLAG_NONE;
+
+ /*
+ * Not encrypted - do so.
+ */
+
+ DEBUG(5,("make_user_info_for_reply: User passwords not in encrypted format.\n"));
+
+ if (plaintext_password.data) {
+ unsigned char local_lm_response[24];
+
+#ifdef DEBUG_PASSWORD
+ DEBUG(10,("Unencrypted password (len %d):\n",plaintext_password.length));
+ dump_data(100, plaintext_password.data, plaintext_password.length);
+#endif
+
+ SMBencrypt( (const char *)plaintext_password.data, (const uchar*)chal, local_lm_response);
+ local_lm_blob = data_blob(local_lm_response, 24);
+
+ /* We can't do an NT hash here, as the password needs to be
+ case insensitive */
+ local_nt_blob = data_blob(NULL, 0);
+
+ auth_flags = (AUTH_FLAG_PLAINTEXT | AUTH_FLAG_LM_RESP);
+ } else {
+ local_lm_blob = data_blob(NULL, 0);
+ local_nt_blob = data_blob(NULL, 0);
+ }
+
+ ret = make_user_info_map(user_info, smb_name,
+ client_domain,
+ get_remote_machine_name(),
+ local_lm_blob,
+ local_nt_blob,
+ plaintext_password,
+ auth_flags, False);
+
+ data_blob_free(&local_lm_blob);
+ return NT_STATUS_IS_OK(ret) ? True : False;
+}
+
+/****************************************************************************
+ Create an auth_usersupplied_data structure
+****************************************************************************/
+
+NTSTATUS make_user_info_for_reply_enc(auth_usersupplied_info **user_info,
+ const char *smb_name,
+ const char *client_domain,
+ DATA_BLOB lm_resp, DATA_BLOB nt_resp)
+{
+ uint32 auth_flags = AUTH_FLAG_NONE;
+
+ DATA_BLOB no_plaintext_blob = data_blob(NULL, 0);
+
+ if (lm_resp.length == 24) {
+ auth_flags |= AUTH_FLAG_LM_RESP;
+ }
+ if (nt_resp.length == 0) {
+ } else if (nt_resp.length == 24) {
+ auth_flags |= AUTH_FLAG_NTLM_RESP;
+ } else {
+ auth_flags |= AUTH_FLAG_NTLMv2_RESP;
+ }
+
+ return make_user_info_map(user_info, smb_name,
+ client_domain,
+ get_remote_machine_name(),
+ lm_resp,
+ nt_resp,
+ no_plaintext_blob,
+ auth_flags, True);
+}
+
+/****************************************************************************
+ Create a guest user_info blob, for anonymous authenticaion.
+****************************************************************************/
+
+BOOL make_user_info_guest(auth_usersupplied_info **user_info)
+{
+ DATA_BLOB lm_blob = data_blob(NULL, 0);
+ DATA_BLOB nt_blob = data_blob(NULL, 0);
+ DATA_BLOB plaintext_blob = data_blob(NULL, 0);
+ uint32 auth_flags = AUTH_FLAG_NONE;
+ NTSTATUS nt_status;
+
+ nt_status = make_user_info(user_info,
+ "","",
+ "","",
+ "",
+ nt_blob, lm_blob,
+ plaintext_blob,
+ auth_flags, True);
+
+ return NT_STATUS_IS_OK(nt_status) ? True : False;
+}
+
+/****************************************************************************
+ prints a NT_USER_TOKEN to debug output.
+****************************************************************************/
+
+void debug_nt_user_token(int dbg_class, int dbg_lev, NT_USER_TOKEN *token)
+{
+ fstring sid_str;
+ size_t i;
+
+ if (!token) {
+ DEBUGC(dbg_class, dbg_lev, ("NT user token: (NULL)\n"));
+ return;
+ }
+
+ DEBUGC(dbg_class, dbg_lev, ("NT user token of user %s\n",
+ sid_to_string(sid_str, &token->user_sids[0]) ));
+ DEBUGADDC(dbg_class, dbg_lev, ("contains %lu SIDs\n", (unsigned long)token->num_sids));
+ for (i = 0; i < token->num_sids; i++)
+ DEBUGADDC(dbg_class, dbg_lev, ("SID[%3lu]: %s\n", (unsigned long)i,
+ sid_to_string(sid_str, &token->user_sids[i])));
+}
+
+/****************************************************************************
+ prints a UNIX 'token' to debug output.
+****************************************************************************/
+
+void debug_unix_user_token(int dbg_class, int dbg_lev, uid_t uid, gid_t gid, int n_groups, gid_t *groups)
+{
+ int i;
+ DEBUGC(dbg_class, dbg_lev, ("UNIX token of user %ld\n", (long int)uid));
+
+ DEBUGADDC(dbg_class, dbg_lev, ("Primary group is %ld and contains %i supplementary groups\n", (long int)gid, n_groups));
+ for (i = 0; i < n_groups; i++)
+ DEBUGADDC(dbg_class, dbg_lev, ("Group[%3i]: %ld\n", i,
+ (long int)groups[i]));
+}
+
+/****************************************************************************
+ Create the SID list for this user.
+****************************************************************************/
+
+static NTSTATUS create_nt_user_token(const DOM_SID *user_sid, const DOM_SID *group_sid,
+ int n_groupSIDs, DOM_SID *groupSIDs,
+ BOOL is_guest, NT_USER_TOKEN **token)
+{
+ NTSTATUS nt_status = NT_STATUS_OK;
+ NT_USER_TOKEN *ptoken;
+ int i;
+ int sid_ndx;
+
+ if ((ptoken = malloc( sizeof(NT_USER_TOKEN) ) ) == NULL) {
+ DEBUG(0, ("create_nt_token: Out of memory allocating token\n"));
+ nt_status = NT_STATUS_NO_MEMORY;
+ return nt_status;
+ }
+
+ ZERO_STRUCTP(ptoken);
+
+ ptoken->num_sids = n_groupSIDs + 5;
+
+ if ((ptoken->user_sids = (DOM_SID *)malloc( sizeof(DOM_SID) * ptoken->num_sids )) == NULL) {
+ DEBUG(0, ("create_nt_token: Out of memory allocating SIDs\n"));
+ nt_status = NT_STATUS_NO_MEMORY;
+ return nt_status;
+ }
+
+ memset((char*)ptoken->user_sids,0,sizeof(DOM_SID) * ptoken->num_sids);
+
+ /*
+ * Note - user SID *MUST* be first in token !
+ * se_access_check depends on this.
+ *
+ * Primary group SID is second in token. Convention.
+ */
+
+ sid_copy(&ptoken->user_sids[PRIMARY_USER_SID_INDEX], user_sid);
+ if (group_sid)
+ sid_copy(&ptoken->user_sids[PRIMARY_GROUP_SID_INDEX], group_sid);
+
+ /*
+ * Finally add the "standard" SIDs.
+ * The only difference between guest and "anonymous" (which we
+ * don't really support) is the addition of Authenticated_Users.
+ */
+
+ sid_copy(&ptoken->user_sids[2], &global_sid_World);
+ sid_copy(&ptoken->user_sids[3], &global_sid_Network);
+
+ if (is_guest)
+ sid_copy(&ptoken->user_sids[4], &global_sid_Builtin_Guests);
+ else
+ sid_copy(&ptoken->user_sids[4], &global_sid_Authenticated_Users);
+
+ sid_ndx = 5; /* next available spot */
+
+ 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 (sid_equal(&ptoken->user_sids[check_sid_idx],
+ &groupSIDs[i])) {
+ break;
+ }
+ }
+
+ if (check_sid_idx >= ptoken->num_sids) /* Not found already */ {
+ sid_copy(&ptoken->user_sids[sid_ndx++], &groupSIDs[i]);
+ } else {
+ ptoken->num_sids--;
+ }
+ }
+
+ debug_nt_user_token(DBGC_AUTH, 10, ptoken);
+
+ *token = ptoken;
+
+ return nt_status;
+}
+
+/****************************************************************************
+ Create the SID list for this user.
+****************************************************************************/
+
+NT_USER_TOKEN *create_nt_token(uid_t uid, gid_t gid, int ngroups, gid_t *groups, BOOL is_guest)
+{
+ DOM_SID user_sid;
+ DOM_SID group_sid;
+ DOM_SID *group_sids;
+ NT_USER_TOKEN *token;
+ int i;
+
+ if (!NT_STATUS_IS_OK(uid_to_sid(&user_sid, uid))) {
+ return NULL;
+ }
+ if (!NT_STATUS_IS_OK(gid_to_sid(&group_sid, gid))) {
+ return NULL;
+ }
+
+ group_sids = malloc(sizeof(DOM_SID) * ngroups);
+ if (!group_sids) {
+ DEBUG(0, ("create_nt_token: malloc() failed for DOM_SID list!\n"));
+ return NULL;
+ }
+
+ for (i = 0; i < ngroups; i++) {
+ if (!NT_STATUS_IS_OK(gid_to_sid(&(group_sids)[i], (groups)[i]))) {
+ DEBUG(1, ("create_nt_token: failed to convert gid %ld to a sid!\n", (long int)groups[i]));
+ SAFE_FREE(group_sids);
+ return NULL;
+ }
+ }
+
+ if (!NT_STATUS_IS_OK(create_nt_user_token(&user_sid, &group_sid,
+ ngroups, group_sids, is_guest, &token))) {
+ SAFE_FREE(group_sids);
+ return NULL;
+ }
+
+ SAFE_FREE(group_sids);
+
+ return token;
+}
+
+/******************************************************************************
+ * this function returns the groups (SIDs) of the local SAM the user is in.
+ * If this samba server is a DC of the domain the user belongs to, it returns
+ * both domain groups and local / builtin groups. If the user is in a trusted
+ * domain, or samba is a member server of a domain, then this function returns
+ * local and builtin groups the user is a member of.
+ *
+ * currently this is a hack, as there is no sam implementation that is capable
+ * of groups.
+ *
+ * NOTE!! This function will fail if you pass in a winbind user without
+ * the domain --jerry
+ ******************************************************************************/
+
+static NTSTATUS get_user_groups(const char *username, uid_t uid, gid_t gid,
+ int *n_groups, DOM_SID **groups, gid_t **unix_groups)
+{
+ int n_unix_groups;
+ int i;
+
+ *n_groups = 0;
+ *groups = NULL;
+
+ /* Try winbind first */
+
+ if ( strchr(username, *lp_winbind_separator()) ) {
+ n_unix_groups = winbind_getgroups( username, unix_groups );
+
+ DEBUG(10,("get_user_groups: winbind_getgroups(%s): result = %s\n", username,
+ n_unix_groups == -1 ? "FAIL" : "SUCCESS"));
+
+ if ( n_unix_groups == -1 )
+ return NT_STATUS_NO_SUCH_USER; /* what should this return value be? */
+ }
+ else {
+ /* fallback to getgrouplist() */
+
+ n_unix_groups = groups_max();
+
+ if ((*unix_groups = malloc( sizeof(gid_t) * n_unix_groups ) ) == NULL) {
+ DEBUG(0, ("get_user_groups: Out of memory allocating unix group list\n"));
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ if (sys_getgrouplist(username, gid, *unix_groups, &n_unix_groups) == -1) {
+
+ gid_t *groups_tmp;
+
+ groups_tmp = Realloc(*unix_groups, sizeof(gid_t) * n_unix_groups);
+
+ if (!groups_tmp) {
+ SAFE_FREE(*unix_groups);
+ return NT_STATUS_NO_MEMORY;
+ }
+ *unix_groups = groups_tmp;
+
+ if (sys_getgrouplist(username, gid, *unix_groups, &n_unix_groups) == -1) {
+ DEBUG(0, ("get_user_groups: failed to get the unix group list\n"));
+ SAFE_FREE(*unix_groups);
+ return NT_STATUS_NO_SUCH_USER; /* what should this return value be? */
+ }
+ }
+ }
+
+ debug_unix_user_token(DBGC_CLASS, 5, uid, gid, n_unix_groups, *unix_groups);
+
+ /* now setup the space for storing the SIDS */
+
+ if (n_unix_groups > 0) {
+
+ *groups = malloc(sizeof(DOM_SID) * n_unix_groups);
+
+ if (!*groups) {
+ DEBUG(0, ("get_user_group: malloc() failed for DOM_SID list!\n"));
+ SAFE_FREE(*unix_groups);
+ return NT_STATUS_NO_MEMORY;
+ }
+ }
+
+ *n_groups = n_unix_groups;
+
+ for (i = 0; i < *n_groups; i++) {
+ if (!NT_STATUS_IS_OK(gid_to_sid(&(*groups)[i], (*unix_groups)[i]))) {
+ DEBUG(1, ("get_user_groups: failed to convert gid %ld to a sid!\n",
+ (long int)(*unix_groups)[i+1]));
+ SAFE_FREE(*groups);
+ SAFE_FREE(*unix_groups);
+ return NT_STATUS_NO_SUCH_USER;
+ }
+ }
+
+ return NT_STATUS_OK;
+}
+
+/***************************************************************************
+ Make a user_info struct
+***************************************************************************/
+
+static NTSTATUS make_server_info(auth_serversupplied_info **server_info)
+{
+ *server_info = malloc(sizeof(**server_info));
+ if (!*server_info) {
+ DEBUG(0,("make_server_info: malloc failed!\n"));
+ return NT_STATUS_NO_MEMORY;
+ }
+ ZERO_STRUCTP(*server_info);
+
+ /* Initialise the uid and gid values to something non-zero
+ which may save us from giving away root access if there
+ is a bug in allocating these fields. */
+
+ (*server_info)->uid = -1;
+ (*server_info)->gid = -1;
+
+ return NT_STATUS_OK;
+}
+
+/***************************************************************************
+Fill a server_info struct from a SAM_ACCOUNT with their groups
+***************************************************************************/
+
+static NTSTATUS add_user_groups(auth_serversupplied_info **server_info,
+ const char * unix_username,
+ SAM_ACCOUNT *sampass,
+ uid_t uid, gid_t gid)
+{
+ NTSTATUS nt_status;
+ const DOM_SID *user_sid = pdb_get_user_sid(sampass);
+ const DOM_SID *group_sid = pdb_get_group_sid(sampass);
+ int n_groupSIDs = 0;
+ DOM_SID *groupSIDs = NULL;
+ gid_t *unix_groups = NULL;
+ NT_USER_TOKEN *token;
+ BOOL is_guest;
+ uint32 rid;
+
+ nt_status = get_user_groups(unix_username, uid, gid,
+ &n_groupSIDs, &groupSIDs, &unix_groups);
+
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ DEBUG(4,("get_user_groups_from_local_sam failed\n"));
+ free_server_info(server_info);
+ return nt_status;
+ }
+
+ is_guest = (sid_peek_rid(user_sid, &rid) && rid == DOMAIN_USER_RID_GUEST);
+
+ if (!NT_STATUS_IS_OK(nt_status = create_nt_user_token(user_sid, group_sid,
+ n_groupSIDs, groupSIDs, is_guest,
+ &token)))
+ {
+ DEBUG(4,("create_nt_user_token failed\n"));
+ SAFE_FREE(groupSIDs);
+ SAFE_FREE(unix_groups);
+ free_server_info(server_info);
+ return nt_status;
+ }
+
+ SAFE_FREE(groupSIDs);
+
+ (*server_info)->n_groups = n_groupSIDs;
+ (*server_info)->groups = unix_groups;
+ (*server_info)->ptok = token;
+
+ return nt_status;
+}
+
+/***************************************************************************
+Fill a server_info struct from a SAM_ACCOUNT with its privileges
+***************************************************************************/
+
+static NTSTATUS add_privileges(auth_serversupplied_info **server_info)
+{
+ PRIVILEGE_SET *privs = NULL;
+
+ init_privilege(&privs);
+ if (!pdb_get_privilege_set((*server_info)->ptok->user_sids, (*server_info)->ptok->num_sids, privs))
+ DEBUG(1, ("Could not add privileges\n"));
+
+ (*server_info)->privs = privs;
+
+ return NT_STATUS_OK;
+}
+
+/***************************************************************************
+ Make (and fill) a user_info struct from a SAM_ACCOUNT
+***************************************************************************/
+
+NTSTATUS make_server_info_sam(auth_serversupplied_info **server_info,
+ SAM_ACCOUNT *sampass)
+{
+ NTSTATUS nt_status;
+ struct passwd *pwd;
+
+ if (!NT_STATUS_IS_OK(nt_status = make_server_info(server_info)))
+ return nt_status;
+
+ (*server_info)->sam_account = sampass;
+
+ if ( !(pwd = getpwnam_alloc(pdb_get_username(sampass))) ) {
+ DEBUG(1, ("User %s in passdb, but getpwnam() fails!\n",
+ pdb_get_username(sampass)));
+ free_server_info(server_info);
+ return NT_STATUS_NO_SUCH_USER;
+ }
+ (*server_info)->unix_name = smb_xstrdup(pwd->pw_name);
+ (*server_info)->gid = pwd->pw_gid;
+ (*server_info)->uid = pwd->pw_uid;
+
+ passwd_free(&pwd);
+
+ if (!NT_STATUS_IS_OK(nt_status = add_user_groups(server_info, pdb_get_username(sampass),
+ sampass,
+ (*server_info)->uid,
+ (*server_info)->gid)))
+ {
+ free_server_info(server_info);
+ return nt_status;
+ }
+
+ if (!NT_STATUS_IS_OK(nt_status = add_privileges(server_info))) {
+ free_server_info(server_info);
+ return nt_status;
+ }
+
+ (*server_info)->sam_fill_level = SAM_FILL_ALL;
+ DEBUG(5,("make_server_info_sam: made server info for user %s -> %s\n",
+ pdb_get_username(sampass),
+ (*server_info)->unix_name));
+
+ return nt_status;
+}
+
+/***************************************************************************
+ Make (and fill) a user_info struct from a 'struct passwd' by conversion
+ to a SAM_ACCOUNT
+***************************************************************************/
+
+NTSTATUS make_server_info_pw(auth_serversupplied_info **server_info,
+ char *unix_username,
+ struct passwd *pwd)
+{
+ NTSTATUS nt_status;
+ SAM_ACCOUNT *sampass = NULL;
+ if (!NT_STATUS_IS_OK(nt_status = pdb_init_sam_pw(&sampass, pwd))) {
+ return nt_status;
+ }
+ if (!NT_STATUS_IS_OK(nt_status = make_server_info(server_info))) {
+ return nt_status;
+ }
+
+ (*server_info)->sam_account = sampass;
+
+ if (!NT_STATUS_IS_OK(nt_status = add_user_groups(server_info, unix_username,
+ sampass, pwd->pw_uid, pwd->pw_gid)))
+ {
+ return nt_status;
+ }
+
+ (*server_info)->unix_name = smb_xstrdup(unix_username);
+
+ (*server_info)->sam_fill_level = SAM_FILL_ALL;
+ (*server_info)->uid = pwd->pw_uid;
+ (*server_info)->gid = pwd->pw_gid;
+ return nt_status;
+}
+
+/***************************************************************************
+ Make (and fill) a user_info struct for a guest login.
+***************************************************************************/
+
+NTSTATUS make_server_info_guest(auth_serversupplied_info **server_info)
+{
+ NTSTATUS nt_status;
+ SAM_ACCOUNT *sampass = NULL;
+ DOM_SID guest_sid;
+
+ if (!NT_STATUS_IS_OK(nt_status = pdb_init_sam(&sampass))) {
+ return nt_status;
+ }
+
+ sid_copy(&guest_sid, get_global_sam_sid());
+ sid_append_rid(&guest_sid, DOMAIN_USER_RID_GUEST);
+
+ become_root();
+ if (!pdb_getsampwsid(sampass, &guest_sid)) {
+ unbecome_root();
+ return NT_STATUS_NO_SUCH_USER;
+ }
+ unbecome_root();
+
+ nt_status = make_server_info_sam(server_info, sampass);
+
+ if (NT_STATUS_IS_OK(nt_status)) {
+ static const char zeros[16];
+ (*server_info)->guest = True;
+
+ /* annoying, but the Guest really does have a session key,
+ and it is all zeros! */
+ (*server_info)->nt_session_key = data_blob(zeros, sizeof(zeros));
+ (*server_info)->lm_session_key = data_blob(zeros, sizeof(zeros));
+ }
+
+ return nt_status;
+}
+
+/***************************************************************************
+ Purely internal function for make_server_info_info3
+ Fill the sam account from getpwnam
+***************************************************************************/
+static NTSTATUS fill_sam_account(TALLOC_CTX *mem_ctx,
+ const char *domain,
+ const char *username,
+ char **found_username,
+ uid_t *uid, gid_t *gid,
+ SAM_ACCOUNT **sam_account)
+{
+ fstring dom_user;
+ fstring real_username;
+ struct passwd *passwd;
+
+ fstr_sprintf(dom_user, "%s%s%s", domain, lp_winbind_separator(),
+ username);
+
+ /* get the passwd struct but don't create the user if he/she
+ does not exist. We were explicitly called from a following
+ a winbindd authentication request so we should assume that
+ nss_winbindd is working */
+
+ if ( !(passwd = smb_getpwnam( dom_user, real_username, True )) )
+ return NT_STATUS_NO_SUCH_USER;
+
+ *uid = passwd->pw_uid;
+ *gid = passwd->pw_gid;
+
+ /* This is pointless -- there is no suport for differing
+ unix and windows names. Make sure to always store the
+ one we actually looked up and succeeded. Have I mentioned
+ why I hate the 'winbind use default domain' parameter?
+ --jerry */
+
+ *found_username = talloc_strdup( mem_ctx, real_username );
+
+ DEBUG(5,("fill_sam_account: located username was [%s]\n",
+ *found_username));
+
+ return pdb_init_sam_pw(sam_account, passwd);
+}
+
+/****************************************************************************
+ Wrapper to allow the getpwnam() call to strip the domain name and
+ try again in case a local UNIX user is already there. Also run through
+ the username if we fallback to the username only.
+ ****************************************************************************/
+
+struct passwd *smb_getpwnam( char *domuser, fstring save_username, BOOL create )
+{
+ struct passwd *pw = NULL;
+ char *p;
+ fstring mapped_username;
+ fstring strip_username;
+
+ /* we only save a copy of the username it has been mangled
+ by winbindd use default domain */
+
+ save_username[0] = '\0';
+
+ /* save a local copy of the username and run it through the
+ username map */
+
+ fstrcpy( mapped_username, domuser );
+ map_username( mapped_username );
+
+ p = strchr_m( mapped_username, *lp_winbind_separator() );
+
+ /* code for a DOMAIN\user string */
+
+ if ( p ) {
+ pw = Get_Pwnam( domuser );
+ if ( pw ) {
+ /* make sure we get the case of the username correct */
+ /* work around 'winbind use default domain = yes' */
+
+ if ( !strchr_m( pw->pw_name, *lp_winbind_separator() ) ) {
+ char *domain;
+
+ domain = mapped_username;
+ *p = '\0';
+ fstr_sprintf(save_username, "%s%c%s", domain, *lp_winbind_separator(), pw->pw_name);
+ }
+ else
+ fstrcpy( save_username, pw->pw_name );
+
+ /* whew -- done! */
+ return pw;
+ }
+
+ /* setup for lookup of just the username */
+ /* remember that p and mapped_username are overlapping memory */
+
+ p++;
+ fstrcpy( strip_username, p );
+ fstrcpy( mapped_username, strip_username );
+ }
+
+ /* just lookup a plain username */
+
+ pw = Get_Pwnam(mapped_username);
+
+ /* Create local user if requested. */
+
+ if ( !pw && create ) {
+ /* Don't add a machine account. */
+ if (mapped_username[strlen(mapped_username)-1] == '$')
+ return NULL;
+
+ auth_add_user_script(NULL, mapped_username);
+ pw = Get_Pwnam(mapped_username);
+ }
+
+ /* one last check for a valid passwd struct */
+
+ if ( pw )
+ fstrcpy( save_username, pw->pw_name );
+
+ return pw;
+}
+
+/***************************************************************************
+ Make a server_info struct from the info3 returned by a domain logon
+***************************************************************************/
+
+NTSTATUS make_server_info_info3(TALLOC_CTX *mem_ctx,
+ const char *internal_username,
+ const char *sent_nt_username,
+ const char *domain,
+ auth_serversupplied_info **server_info,
+ NET_USER_INFO_3 *info3)
+{
+ static const char zeros[16];
+
+ NTSTATUS nt_status = NT_STATUS_OK;
+ char *found_username;
+ const char *nt_domain;
+ const char *nt_username;
+
+ SAM_ACCOUNT *sam_account = NULL;
+ DOM_SID user_sid;
+ DOM_SID group_sid;
+
+ uid_t uid;
+ gid_t gid;
+
+ int n_lgroupSIDs;
+ DOM_SID *lgroupSIDs = NULL;
+
+ gid_t *unix_groups = NULL;
+ NT_USER_TOKEN *token;
+
+ DOM_SID *all_group_SIDs;
+ size_t i;
+
+ /*
+ Here is where we should check the list of
+ trusted domains, and verify that the SID
+ matches.
+ */
+
+ sid_copy(&user_sid, &info3->dom_sid.sid);
+ if (!sid_append_rid(&user_sid, info3->user_rid)) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ sid_copy(&group_sid, &info3->dom_sid.sid);
+ if (!sid_append_rid(&group_sid, info3->group_rid)) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ if (!(nt_username = unistr2_tdup(mem_ctx, &(info3->uni_user_name)))) {
+ /* If the server didn't give us one, just use the one we sent them */
+ nt_username = sent_nt_username;
+ }
+
+ if (!(nt_domain = unistr2_tdup(mem_ctx, &(info3->uni_logon_dom)))) {
+ /* If the server didn't give us one, just use the one we sent them */
+ nt_domain = domain;
+ }
+
+ /* try to fill the SAM account.. If getpwnam() fails, then try the
+ add user script (2.2.x behavior) */
+
+ nt_status = fill_sam_account(mem_ctx, nt_domain, internal_username,
+ &found_username, &uid, &gid, &sam_account);
+
+ if (NT_STATUS_EQUAL(nt_status, NT_STATUS_NO_SUCH_USER)) {
+ DEBUG(3,("User %s does not exist, trying to add it\n",
+ internal_username));
+ auth_add_user_script(nt_domain, internal_username);
+ nt_status = fill_sam_account(mem_ctx, nt_domain,
+ internal_username, &found_username,
+ &uid, &gid, &sam_account);
+ }
+
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ DEBUG(0, ("make_server_info_info3: pdb_init_sam failed!\n"));
+ return nt_status;
+ }
+
+ if (!pdb_set_nt_username(sam_account, nt_username, PDB_CHANGED)) {
+ pdb_free_sam(&sam_account);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ if (!pdb_set_username(sam_account, nt_username, PDB_CHANGED)) {
+ pdb_free_sam(&sam_account);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ if (!pdb_set_domain(sam_account, nt_domain, PDB_CHANGED)) {
+ pdb_free_sam(&sam_account);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ if (!pdb_set_user_sid(sam_account, &user_sid, PDB_CHANGED)) {
+ pdb_free_sam(&sam_account);
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ if (!pdb_set_group_sid(sam_account, &group_sid, PDB_CHANGED)) {
+ pdb_free_sam(&sam_account);
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ if (!pdb_set_fullname(sam_account, unistr2_static(&(info3->uni_full_name)),
+ PDB_CHANGED)) {
+ pdb_free_sam(&sam_account);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ if (!pdb_set_logon_script(sam_account, unistr2_static(&(info3->uni_logon_script)), PDB_CHANGED)) {
+ pdb_free_sam(&sam_account);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ if (!pdb_set_profile_path(sam_account, unistr2_static(&(info3->uni_profile_path)), PDB_CHANGED)) {
+ pdb_free_sam(&sam_account);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ if (!pdb_set_homedir(sam_account, unistr2_static(&(info3->uni_home_dir)), PDB_CHANGED)) {
+ pdb_free_sam(&sam_account);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ if (!pdb_set_dir_drive(sam_account, unistr2_static(&(info3->uni_dir_drive)), PDB_CHANGED)) {
+ pdb_free_sam(&sam_account);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ if (!NT_STATUS_IS_OK(nt_status = make_server_info(server_info))) {
+ DEBUG(4, ("make_server_info failed!\n"));
+ pdb_free_sam(&sam_account);
+ return nt_status;
+ }
+
+ /* save this here to _net_sam_logon() doesn't fail (it assumes a
+ valid SAM_ACCOUNT) */
+
+ (*server_info)->sam_account = sam_account;
+
+ (*server_info)->unix_name = smb_xstrdup(found_username);
+
+ /* Fill in the unix info we found on the way */
+
+ (*server_info)->sam_fill_level = SAM_FILL_ALL;
+ (*server_info)->uid = uid;
+ (*server_info)->gid = gid;
+
+ /* Store the user group information in the server_info
+ returned to the caller. */
+
+ nt_status = get_user_groups((*server_info)->unix_name,
+ uid, gid, &n_lgroupSIDs, &lgroupSIDs, &unix_groups);
+
+ if ( !NT_STATUS_IS_OK(nt_status) ) {
+ DEBUG(4,("get_user_groups failed\n"));
+ return nt_status;
+ }
+
+ (*server_info)->groups = unix_groups;
+ (*server_info)->n_groups = n_lgroupSIDs;
+
+ /* Create a 'combined' list of all SIDs we might want in the SD */
+
+ all_group_SIDs = malloc(sizeof(DOM_SID) * (info3->num_groups2 +info3->num_other_sids));
+
+ if (!all_group_SIDs) {
+ DEBUG(0, ("malloc() failed for DOM_SID list!\n"));
+ SAFE_FREE(lgroupSIDs);
+ free_server_info(server_info);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+#if 0 /* JERRY -- no such thing as local groups in current code */
+ /* Copy the 'local' sids */
+ memcpy(all_group_SIDs, lgroupSIDs, sizeof(DOM_SID) * n_lgroupSIDs);
+ SAFE_FREE(lgroupSIDs);
+#endif
+
+ /* and create (by appending rids) the 'domain' sids */
+
+ for (i = 0; i < info3->num_groups2; i++) {
+
+ sid_copy(&all_group_SIDs[i], &(info3->dom_sid.sid));
+
+ if (!sid_append_rid(&all_group_SIDs[i], info3->gids[i].g_rid)) {
+
+ nt_status = NT_STATUS_INVALID_PARAMETER;
+
+ DEBUG(3,("could not append additional group rid 0x%x\n",
+ info3->gids[i].g_rid));
+
+ SAFE_FREE(lgroupSIDs);
+ free_server_info(server_info);
+
+ return nt_status;
+
+ }
+ }
+
+ /* 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
+ */
+
+ for (i = 0; i < info3->num_other_sids; i++) {
+ sid_copy(&all_group_SIDs[info3->num_groups2 + i],
+ &info3->other_sids[i].sid);
+ }
+
+ /* Where are the 'global' sids... */
+
+ /* can the user be guest? if yes, where is it stored? */
+
+ nt_status = create_nt_user_token(&user_sid, &group_sid,
+ info3->num_groups2 + info3->num_other_sids,
+ all_group_SIDs, False, &token);
+
+ if ( !NT_STATUS_IS_OK(nt_status) ) {
+ DEBUG(4,("create_nt_user_token failed\n"));
+ SAFE_FREE(all_group_SIDs);
+ free_server_info(server_info);
+ return nt_status;
+ }
+
+ (*server_info)->ptok = token;
+
+ SAFE_FREE(all_group_SIDs);
+
+ /* ensure we are never given NULL session keys */
+
+ if (memcmp(info3->user_sess_key, zeros, sizeof(zeros)) == 0) {
+ (*server_info)->nt_session_key = data_blob(NULL, 0);
+ } else {
+ (*server_info)->nt_session_key = data_blob(info3->user_sess_key, sizeof(info3->user_sess_key));
+ }
+
+ if (memcmp(info3->padding, zeros, sizeof(zeros)) == 0) {
+ (*server_info)->lm_session_key = data_blob(NULL, 0);
+ } else {
+ (*server_info)->lm_session_key = data_blob(info3->padding, 16);
+ }
+ return NT_STATUS_OK;
+}
+
+/***************************************************************************
+ Free a user_info struct
+***************************************************************************/
+
+void free_user_info(auth_usersupplied_info **user_info)
+{
+ DEBUG(5,("attempting to free (and zero) a user_info structure\n"));
+ if (*user_info != NULL) {
+ if ((*user_info)->smb_name.str) {
+ DEBUG(10,("structure was created for %s\n", (*user_info)->smb_name.str));
+ }
+ SAFE_FREE((*user_info)->smb_name.str);
+ SAFE_FREE((*user_info)->internal_username.str);
+ SAFE_FREE((*user_info)->client_domain.str);
+ SAFE_FREE((*user_info)->domain.str);
+ SAFE_FREE((*user_info)->wksta_name.str);
+ data_blob_free(&(*user_info)->lm_resp);
+ data_blob_free(&(*user_info)->nt_resp);
+ SAFE_FREE((*user_info)->interactive_password);
+ data_blob_clear_free(&(*user_info)->plaintext_password);
+ ZERO_STRUCT(**user_info);
+ }
+ SAFE_FREE(*user_info);
+}
+
+/***************************************************************************
+ Clear out a server_info struct that has been allocated
+***************************************************************************/
+
+void free_server_info(auth_serversupplied_info **server_info)
+{
+ DEBUG(5,("attempting to free (and zero) a server_info structure\n"));
+ if (*server_info != NULL) {
+ pdb_free_sam(&(*server_info)->sam_account);
+
+ /* call pam_end here, unless we know we are keeping it */
+ delete_nt_token( &(*server_info)->ptok );
+ SAFE_FREE((*server_info)->groups);
+ SAFE_FREE((*server_info)->unix_name);
+ data_blob_free(&(*server_info)->lm_session_key);
+ data_blob_free(&(*server_info)->nt_session_key);
+ ZERO_STRUCT(**server_info);
+ }
+ SAFE_FREE(*server_info);
+}
+
+/***************************************************************************
+ Make an auth_methods struct
+***************************************************************************/
+
+BOOL make_auth_methods(struct auth_context *auth_context, auth_methods **auth_method)
+{
+ if (!auth_context) {
+ smb_panic("no auth_context supplied to make_auth_methods()!\n");
+ }
+
+ if (!auth_method) {
+ smb_panic("make_auth_methods: pointer to auth_method pointer is NULL!\n");
+ }
+
+ *auth_method = talloc(auth_context->mem_ctx, sizeof(**auth_method));
+ if (!*auth_method) {
+ DEBUG(0,("make_auth_method: malloc failed!\n"));
+ return False;
+ }
+ ZERO_STRUCTP(*auth_method);
+
+ return True;
+}
+
+/****************************************************************************
+ Delete a SID token.
+****************************************************************************/
+
+void delete_nt_token(NT_USER_TOKEN **pptoken)
+{
+ if (*pptoken) {
+ NT_USER_TOKEN *ptoken = *pptoken;
+ SAFE_FREE( ptoken->user_sids );
+ ZERO_STRUCTP(ptoken);
+ }
+ SAFE_FREE(*pptoken);
+}
+
+/****************************************************************************
+ Duplicate a SID token.
+****************************************************************************/
+
+NT_USER_TOKEN *dup_nt_token(NT_USER_TOKEN *ptoken)
+{
+ NT_USER_TOKEN *token;
+
+ if (!ptoken)
+ return NULL;
+
+ if ((token = (NT_USER_TOKEN *)malloc( sizeof(NT_USER_TOKEN) ) ) == NULL)
+ return NULL;
+
+ ZERO_STRUCTP(token);
+
+ if ((token->user_sids = (DOM_SID *)memdup( ptoken->user_sids, sizeof(DOM_SID) * ptoken->num_sids )) == NULL) {
+ SAFE_FREE(token);
+ return NULL;
+ }
+
+ token->num_sids = ptoken->num_sids;
+
+ return token;
+}
+
+/**
+ * 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
+ **/
+
+NTSTATUS nt_status_squash(NTSTATUS nt_status)
+{
+ if NT_STATUS_IS_OK(nt_status) {
+ return nt_status;
+ } else 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;
+ } else {
+ return nt_status;
+ }
+}
+
+
+/**
+ * Verify whether or not given domain is trusted.
+ *
+ * @param domain_name name of the domain to be verified
+ * @return true if domain is one of the trusted once or
+ * false if otherwise
+ **/
+
+BOOL is_trusted_domain(const char* dom_name)
+{
+ DOM_SID trustdom_sid;
+ char *pass = NULL;
+ time_t lct;
+ BOOL ret;
+
+ /* no trusted domains for a standalone server */
+
+ if ( lp_server_role() == ROLE_STANDALONE )
+ return False;
+
+ /* if we are a DC, then check for a direct trust relationships */
+
+ if (lp_server_role() == ROLE_DOMAIN_BDC || lp_server_role() == ROLE_DOMAIN_PDC) {
+ become_root();
+ ret = secrets_fetch_trusted_domain_password(dom_name, &pass, &trustdom_sid, &lct);
+ unbecome_root();
+ SAFE_FREE(pass);
+ if (ret)
+ return True;
+ }
+ else {
+ /* if winbindd is not up and we are a domain member) then we need to update the
+ trustdom_cache ourselves */
+
+ if ( !winbind_ping() )
+ update_trustdom_cache();
+ }
+
+ /* now the trustdom cache should be available a DC could still
+ * have a transitive trust so fall back to the cache of trusted
+ * domains (like a domain member would use */
+
+ if ( trustdom_cache_fetch(dom_name, &trustdom_sid) ) {
+ return True;
+ }
+
+ return False;
+}
+
diff --git a/source/auth/auth_winbind.c b/source/auth/auth_winbind.c
new file mode 100644
index 00000000000..0e2820313e3
--- /dev/null
+++ b/source/auth/auth_winbind.c
@@ -0,0 +1,171 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ Winbind authentication mechnism
+
+ Copyright (C) Tim Potter 2000
+ Copyright (C) Andrew Bartlett 2001 - 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 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"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_AUTH
+
+static NTSTATUS get_info3_from_ndr(TALLOC_CTX *mem_ctx, struct winbindd_response *response, NET_USER_INFO_3 *info3)
+{
+ uint8 *info3_ndr;
+ size_t len = response->length - sizeof(struct winbindd_response);
+ prs_struct ps;
+ if (len > 0) {
+ info3_ndr = response->extra_data;
+ if (!prs_init(&ps, len, mem_ctx, UNMARSHALL)) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ prs_copy_data_in(&ps, (char *)info3_ndr, len);
+ prs_set_offset(&ps,0);
+ if (!net_io_user_info3("", info3, &ps, 1, 3)) {
+ DEBUG(2, ("get_info3_from_ndr: could not parse info3 struct!\n"));
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+ prs_mem_free(&ps);
+
+ return NT_STATUS_OK;
+ } else {
+ DEBUG(2, ("get_info3_from_ndr: No info3 struct found!\n"));
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+}
+
+/* Authenticate a user with a challenge/response */
+
+static NTSTATUS check_winbind_security(const struct auth_context *auth_context,
+ void *my_private_data,
+ TALLOC_CTX *mem_ctx,
+ const auth_usersupplied_info *user_info,
+ auth_serversupplied_info **server_info)
+{
+ struct winbindd_request request;
+ struct winbindd_response response;
+ NSS_STATUS result;
+ NTSTATUS nt_status;
+ NET_USER_INFO_3 info3;
+
+ if (!user_info) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ if (!auth_context) {
+ DEBUG(3,("Password for user %s cannot be checked because we have no auth_info to get the challenge from.\n",
+ user_info->internal_username.str));
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ if (strequal(user_info->domain.str, get_global_sam_name())) {
+ DEBUG(3,("check_winbind_security: Not using winbind, requested domain [%s] was for this SAM.\n",
+ user_info->domain.str));
+ return NT_STATUS_NOT_IMPLEMENTED;
+ }
+
+ /* Send off request */
+
+ ZERO_STRUCT(request);
+ ZERO_STRUCT(response);
+
+ request.flags = WBFLAG_PAM_INFO3_NDR;
+
+ push_utf8_fstring(request.data.auth_crap.user,
+ user_info->smb_name.str);
+ push_utf8_fstring(request.data.auth_crap.domain,
+ user_info->domain.str);
+ push_utf8_fstring(request.data.auth_crap.workstation,
+ user_info->wksta_name.str);
+
+ memcpy(request.data.auth_crap.chal, auth_context->challenge.data, sizeof(request.data.auth_crap.chal));
+
+ request.data.auth_crap.lm_resp_len = MIN(user_info->lm_resp.length,
+ sizeof(request.data.auth_crap.lm_resp));
+ request.data.auth_crap.nt_resp_len = MIN(user_info->nt_resp.length,
+ sizeof(request.data.auth_crap.nt_resp));
+
+ memcpy(request.data.auth_crap.lm_resp, user_info->lm_resp.data,
+ request.data.auth_crap.lm_resp_len);
+ memcpy(request.data.auth_crap.nt_resp, user_info->nt_resp.data,
+ request.data.auth_crap.nt_resp_len);
+
+ /* we are contacting the privileged pipe */
+ become_root();
+ result = winbindd_request(WINBINDD_PAM_AUTH_CRAP, &request, &response);
+ unbecome_root();
+
+ if ( result == NSS_STATUS_UNAVAIL ) {
+ struct auth_methods *auth_method = my_private_data;
+
+ if ( auth_method )
+ return auth_method->auth(auth_context, auth_method->private_data,
+ mem_ctx, user_info, server_info);
+ else
+ /* log an error since this should not happen */
+ DEBUG(0,("check_winbind_security: ERROR! my_private_data == NULL!\n"));
+ }
+
+ nt_status = NT_STATUS(response.data.auth.nt_status);
+
+ if (result == NSS_STATUS_SUCCESS && response.extra_data) {
+ if (NT_STATUS_IS_OK(nt_status)) {
+
+ if (NT_STATUS_IS_OK(nt_status = get_info3_from_ndr(mem_ctx, &response, &info3)))
+ {
+ nt_status = make_server_info_info3(mem_ctx,
+ user_info->internal_username.str,
+ user_info->smb_name.str, user_info->domain.str,
+ server_info, &info3);
+ }
+
+ }
+ } else if (NT_STATUS_IS_OK(nt_status)) {
+ nt_status = NT_STATUS_NO_LOGON_SERVERS;
+ }
+
+ return nt_status;
+}
+
+/* module initialisation */
+static NTSTATUS auth_init_winbind(struct auth_context *auth_context, const char *param, auth_methods **auth_method)
+{
+ if (!make_auth_methods(auth_context, auth_method)) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ (*auth_method)->name = "winbind";
+ (*auth_method)->auth = check_winbind_security;
+
+ if (param && *param) {
+ /* we load the 'fallback' module - if winbind isn't here, call this
+ module */
+ if (!load_auth_module(auth_context, param, (auth_methods **)&(*auth_method)->private_data)) {
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ }
+ return NT_STATUS_OK;
+}
+
+NTSTATUS auth_winbind_init(void)
+{
+ return smb_register_auth(AUTH_INTERFACE_VERSION, "winbind", auth_init_winbind);
+}
diff --git a/source/auth/pampass.c b/source/auth/pampass.c
new file mode 100644
index 00000000000..3239686a20d
--- /dev/null
+++ b/source/auth/pampass.c
@@ -0,0 +1,875 @@
+/*
+ Unix SMB/CIFS implementation.
+ PAM Password checking
+ Copyright (C) Andrew Tridgell 1992-2001
+ Copyright (C) John H Terpsta 1999-2001
+ Copyright (C) Andrew Bartlett 2001
+ Copyright (C) Jeremy Allison 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 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.
+*/
+
+/*
+ * This module provides PAM based functions for validation of
+ * username/password pairs, account managment, session and access control.
+ * Note: SMB password checking is done in smbpass.c
+ */
+
+#include "includes.h"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_AUTH
+
+#ifdef WITH_PAM
+
+/*******************************************************************
+ * Handle PAM authentication
+ * - Access, Authentication, Session, Password
+ * Note: See PAM Documentation and refer to local system PAM implementation
+ * which determines what actions/limitations/allowances become affected.
+ *********************************************************************/
+
+#include <security/pam_appl.h>
+
+/*
+ * Structure used to communicate between the conversation function
+ * and the server_login/change password functions.
+ */
+
+struct smb_pam_userdata {
+ const char *PAM_username;
+ const char *PAM_password;
+ const char *PAM_newpassword;
+};
+
+typedef int (*smb_pam_conv_fn)(int, const struct pam_message **, struct pam_response **, void *appdata_ptr);
+
+/*
+ * Macros to help make life easy
+ */
+#define COPY_STRING(s) (s) ? strdup(s) : NULL
+
+/*******************************************************************
+ PAM error handler.
+ *********************************************************************/
+
+static BOOL smb_pam_error_handler(pam_handle_t *pamh, int pam_error, const char *msg, int dbglvl)
+{
+
+ if( pam_error != PAM_SUCCESS) {
+ DEBUG(dbglvl, ("smb_pam_error_handler: PAM: %s : %s\n",
+ msg, pam_strerror(pamh, pam_error)));
+
+ return False;
+ }
+ return True;
+}
+
+/*******************************************************************
+ This function is a sanity check, to make sure that we NEVER report
+ failure as sucess.
+*********************************************************************/
+
+static BOOL smb_pam_nt_status_error_handler(pam_handle_t *pamh, int pam_error,
+ const char *msg, int dbglvl,
+ NTSTATUS *nt_status)
+{
+ *nt_status = pam_to_nt_status(pam_error);
+
+ if (smb_pam_error_handler(pamh, pam_error, msg, dbglvl))
+ return True;
+
+ if (NT_STATUS_IS_OK(*nt_status)) {
+ /* Complain LOUDLY */
+ DEBUG(0, ("smb_pam_nt_status_error_handler: PAM: BUG: PAM and NT_STATUS \
+error MISMATCH, forcing to NT_STATUS_LOGON_FAILURE"));
+ *nt_status = NT_STATUS_LOGON_FAILURE;
+ }
+ return False;
+}
+
+/*
+ * PAM conversation function
+ * Here we assume (for now, at least) that echo on means login name, and
+ * echo off means password.
+ */
+
+static int smb_pam_conv(int num_msg,
+ const struct pam_message **msg,
+ struct pam_response **resp,
+ void *appdata_ptr)
+{
+ int replies = 0;
+ struct pam_response *reply = NULL;
+ struct smb_pam_userdata *udp = (struct smb_pam_userdata *)appdata_ptr;
+
+ *resp = NULL;
+
+ if (num_msg <= 0)
+ return PAM_CONV_ERR;
+
+ /*
+ * Apparantly HPUX has a buggy PAM that doesn't support the
+ * appdata_ptr. Fail if this is the case. JRA.
+ */
+
+ if (udp == NULL) {
+ DEBUG(0,("smb_pam_conv: PAM on this system is broken - appdata_ptr == NULL !\n"));
+ return PAM_CONV_ERR;
+ }
+
+ reply = malloc(sizeof(struct pam_response) * num_msg);
+ if (!reply)
+ return PAM_CONV_ERR;
+
+ memset(reply, '\0', sizeof(struct pam_response) * num_msg);
+
+ for (replies = 0; replies < num_msg; replies++) {
+ switch (msg[replies]->msg_style) {
+ case PAM_PROMPT_ECHO_ON:
+ reply[replies].resp_retcode = PAM_SUCCESS;
+ reply[replies].resp = COPY_STRING(udp->PAM_username);
+ /* PAM frees resp */
+ break;
+
+ case PAM_PROMPT_ECHO_OFF:
+ reply[replies].resp_retcode = PAM_SUCCESS;
+ reply[replies].resp = COPY_STRING(udp->PAM_password);
+ /* PAM frees resp */
+ break;
+
+ case PAM_TEXT_INFO:
+ /* fall through */
+
+ case PAM_ERROR_MSG:
+ /* ignore it... */
+ reply[replies].resp_retcode = PAM_SUCCESS;
+ reply[replies].resp = NULL;
+ break;
+
+ default:
+ /* Must be an error of some sort... */
+ SAFE_FREE(reply);
+ return PAM_CONV_ERR;
+ }
+ }
+ if (reply)
+ *resp = reply;
+ return PAM_SUCCESS;
+}
+
+/*
+ * PAM password change conversation function
+ * Here we assume (for now, at least) that echo on means login name, and
+ * echo off means password.
+ */
+
+static void special_char_sub(char *buf)
+{
+ all_string_sub(buf, "\\n", "", 0);
+ all_string_sub(buf, "\\r", "", 0);
+ all_string_sub(buf, "\\s", " ", 0);
+ all_string_sub(buf, "\\t", "\t", 0);
+}
+
+static void pwd_sub(char *buf, const char *username, const char *oldpass, const char *newpass)
+{
+ fstring_sub(buf, "%u", username);
+ all_string_sub(buf, "%o", oldpass, sizeof(fstring));
+ all_string_sub(buf, "%n", newpass, sizeof(fstring));
+}
+
+
+struct chat_struct {
+ struct chat_struct *next, *prev;
+ fstring prompt;
+ fstring reply;
+};
+
+/**************************************************************
+ Create a linked list containing chat data.
+***************************************************************/
+
+static struct chat_struct *make_pw_chat(char *p)
+{
+ fstring prompt;
+ fstring reply;
+ struct chat_struct *list = NULL;
+ struct chat_struct *t;
+ struct chat_struct *tmp;
+
+ while (1) {
+ t = (struct chat_struct *)malloc(sizeof(*t));
+ if (!t) {
+ DEBUG(0,("make_pw_chat: malloc failed!\n"));
+ return NULL;
+ }
+
+ ZERO_STRUCTP(t);
+
+ DLIST_ADD_END(list, t, tmp);
+
+ if (!next_token(&p, prompt, NULL, sizeof(fstring)))
+ break;
+
+ if (strequal(prompt,"."))
+ fstrcpy(prompt,"*");
+
+ special_char_sub(prompt);
+ fstrcpy(t->prompt, prompt);
+ strlower_m(t->prompt);
+ trim_char(t->prompt, ' ', ' ');
+
+ if (!next_token(&p, reply, NULL, sizeof(fstring)))
+ break;
+
+ if (strequal(reply,"."))
+ fstrcpy(reply,"");
+
+ special_char_sub(reply);
+ fstrcpy(t->reply, reply);
+ strlower_m(t->reply);
+ trim_char(t->reply, ' ', ' ');
+
+ }
+ return list;
+}
+
+static void free_pw_chat(struct chat_struct *list)
+{
+ while (list) {
+ struct chat_struct *old_head = list;
+ DLIST_REMOVE(list, list);
+ SAFE_FREE(old_head);
+ }
+}
+
+static int smb_pam_passchange_conv(int num_msg,
+ const struct pam_message **msg,
+ struct pam_response **resp,
+ void *appdata_ptr)
+{
+ int replies = 0;
+ struct pam_response *reply = NULL;
+ fstring current_prompt;
+ fstring current_reply;
+ struct smb_pam_userdata *udp = (struct smb_pam_userdata *)appdata_ptr;
+ struct chat_struct *pw_chat= make_pw_chat(lp_passwd_chat());
+ struct chat_struct *t;
+ BOOL found;
+ *resp = NULL;
+
+ DEBUG(10,("smb_pam_passchange_conv: starting converstation for %d messages\n", num_msg));
+
+ if (num_msg <= 0)
+ return PAM_CONV_ERR;
+
+ if (pw_chat == NULL)
+ return PAM_CONV_ERR;
+
+ /*
+ * Apparantly HPUX has a buggy PAM that doesn't support the
+ * appdata_ptr. Fail if this is the case. JRA.
+ */
+
+ if (udp == NULL) {
+ DEBUG(0,("smb_pam_passchange_conv: PAM on this system is broken - appdata_ptr == NULL !\n"));
+ free_pw_chat(pw_chat);
+ return PAM_CONV_ERR;
+ }
+
+ reply = malloc(sizeof(struct pam_response) * num_msg);
+ if (!reply) {
+ DEBUG(0,("smb_pam_passchange_conv: malloc for reply failed!\n"));
+ free_pw_chat(pw_chat);
+ return PAM_CONV_ERR;
+ }
+
+ for (replies = 0; replies < num_msg; replies++) {
+ found = False;
+ DEBUG(10,("smb_pam_passchange_conv: Processing message %d\n", replies));
+ switch (msg[replies]->msg_style) {
+ case PAM_PROMPT_ECHO_ON:
+ DEBUG(10,("smb_pam_passchange_conv: PAM_PROMPT_ECHO_ON: PAM said: %s\n", msg[replies]->msg));
+ fstrcpy(current_prompt, msg[replies]->msg);
+ trim_char(current_prompt, ' ', ' ');
+ for (t=pw_chat; t; t=t->next) {
+
+ DEBUG(10,("smb_pam_passchange_conv: PAM_PROMPT_ECHO_ON: trying to match |%s| to |%s|\n",
+ t->prompt, current_prompt ));
+
+ if (unix_wild_match(t->prompt, current_prompt) == 0) {
+ fstrcpy(current_reply, t->reply);
+ DEBUG(10,("smb_pam_passchange_conv: PAM_PROMPT_ECHO_ON: We sent: %s\n", current_reply));
+ pwd_sub(current_reply, udp->PAM_username, udp->PAM_password, udp->PAM_newpassword);
+#ifdef DEBUG_PASSWORD
+ DEBUG(100,("smb_pam_passchange_conv: PAM_PROMPT_ECHO_ON: We actualy sent: %s\n", current_reply));
+#endif
+ reply[replies].resp_retcode = PAM_SUCCESS;
+ reply[replies].resp = COPY_STRING(current_reply);
+ found = True;
+ break;
+ }
+ }
+ /* PAM frees resp */
+ if (!found) {
+ DEBUG(3,("smb_pam_passchange_conv: Could not find reply for PAM prompt: %s\n",msg[replies]->msg));
+ free_pw_chat(pw_chat);
+ SAFE_FREE(reply);
+ return PAM_CONV_ERR;
+ }
+ break;
+
+ case PAM_PROMPT_ECHO_OFF:
+ DEBUG(10,("smb_pam_passchange_conv: PAM_PROMPT_ECHO_OFF: PAM said: %s\n", msg[replies]->msg));
+ fstrcpy(current_prompt, msg[replies]->msg);
+ trim_char(current_prompt, ' ', ' ');
+ for (t=pw_chat; t; t=t->next) {
+
+ DEBUG(10,("smb_pam_passchange_conv: PAM_PROMPT_ECHO_OFF: trying to match |%s| to |%s|\n",
+ t->prompt, current_prompt ));
+
+ if (unix_wild_match(t->prompt, current_prompt) == 0) {
+ fstrcpy(current_reply, t->reply);
+ DEBUG(10,("smb_pam_passchange_conv: PAM_PROMPT_ECHO_OFF: We sent: %s\n", current_reply));
+ pwd_sub(current_reply, udp->PAM_username, udp->PAM_password, udp->PAM_newpassword);
+ reply[replies].resp_retcode = PAM_SUCCESS;
+ reply[replies].resp = COPY_STRING(current_reply);
+#ifdef DEBUG_PASSWORD
+ DEBUG(100,("smb_pam_passchange_conv: PAM_PROMPT_ECHO_OFF: We actualy sent: %s\n", current_reply));
+#endif
+ found = True;
+ break;
+ }
+ }
+ /* PAM frees resp */
+
+ if (!found) {
+ DEBUG(3,("smb_pam_passchange_conv: Could not find reply for PAM prompt: %s\n",msg[replies]->msg));
+ free_pw_chat(pw_chat);
+ SAFE_FREE(reply);
+ return PAM_CONV_ERR;
+ }
+ break;
+
+ case PAM_TEXT_INFO:
+ /* fall through */
+
+ case PAM_ERROR_MSG:
+ /* ignore it... */
+ reply[replies].resp_retcode = PAM_SUCCESS;
+ reply[replies].resp = NULL;
+ break;
+
+ default:
+ /* Must be an error of some sort... */
+ free_pw_chat(pw_chat);
+ SAFE_FREE(reply);
+ return PAM_CONV_ERR;
+ }
+ }
+
+ free_pw_chat(pw_chat);
+ if (reply)
+ *resp = reply;
+ return PAM_SUCCESS;
+}
+
+/***************************************************************************
+ Free up a malloced pam_conv struct.
+****************************************************************************/
+
+static void smb_free_pam_conv(struct pam_conv *pconv)
+{
+ if (pconv)
+ SAFE_FREE(pconv->appdata_ptr);
+
+ SAFE_FREE(pconv);
+}
+
+/***************************************************************************
+ Allocate a pam_conv struct.
+****************************************************************************/
+
+static struct pam_conv *smb_setup_pam_conv(smb_pam_conv_fn smb_pam_conv_fnptr, const char *user,
+ const char *passwd, const char *newpass)
+{
+ struct pam_conv *pconv = (struct pam_conv *)malloc(sizeof(struct pam_conv));
+ struct smb_pam_userdata *udp = (struct smb_pam_userdata *)malloc(sizeof(struct smb_pam_userdata));
+
+ if (pconv == NULL || udp == NULL) {
+ SAFE_FREE(pconv);
+ SAFE_FREE(udp);
+ return NULL;
+ }
+
+ udp->PAM_username = user;
+ udp->PAM_password = passwd;
+ udp->PAM_newpassword = newpass;
+
+ pconv->conv = smb_pam_conv_fnptr;
+ pconv->appdata_ptr = (void *)udp;
+ return pconv;
+}
+
+/*
+ * PAM Closing out cleanup handler
+ */
+
+static BOOL smb_pam_end(pam_handle_t *pamh, struct pam_conv *smb_pam_conv_ptr)
+{
+ int pam_error;
+
+ smb_free_pam_conv(smb_pam_conv_ptr);
+
+ if( pamh != NULL ) {
+ pam_error = pam_end(pamh, 0);
+ if(smb_pam_error_handler(pamh, pam_error, "End Cleanup Failed", 2) == True) {
+ DEBUG(4, ("smb_pam_end: PAM: PAM_END OK.\n"));
+ return True;
+ }
+ }
+ DEBUG(2,("smb_pam_end: PAM: not initialised"));
+ return False;
+}
+
+/*
+ * Start PAM authentication for specified account
+ */
+
+static BOOL smb_pam_start(pam_handle_t **pamh, const char *user, const char *rhost, struct pam_conv *pconv)
+{
+ int pam_error;
+ const char *our_rhost;
+
+ *pamh = (pam_handle_t *)NULL;
+
+ DEBUG(4,("smb_pam_start: PAM: Init user: %s\n", user));
+
+ pam_error = pam_start("samba", user, pconv, pamh);
+ if( !smb_pam_error_handler(*pamh, pam_error, "Init Failed", 0)) {
+ *pamh = (pam_handle_t *)NULL;
+ return False;
+ }
+
+ if (rhost == NULL) {
+ our_rhost = client_name();
+ if (strequal(rhost,"UNKNOWN"))
+ our_rhost = client_addr();
+ } else {
+ our_rhost = rhost;
+ }
+
+#ifdef PAM_RHOST
+ DEBUG(4,("smb_pam_start: PAM: setting rhost to: %s\n", our_rhost));
+ pam_error = pam_set_item(*pamh, PAM_RHOST, our_rhost);
+ if(!smb_pam_error_handler(*pamh, pam_error, "set rhost failed", 0)) {
+ smb_pam_end(*pamh, pconv);
+ *pamh = (pam_handle_t *)NULL;
+ return False;
+ }
+#endif
+#ifdef PAM_TTY
+ DEBUG(4,("smb_pam_start: PAM: setting tty\n"));
+ pam_error = pam_set_item(*pamh, PAM_TTY, "samba");
+ if (!smb_pam_error_handler(*pamh, pam_error, "set tty failed", 0)) {
+ smb_pam_end(*pamh, pconv);
+ *pamh = (pam_handle_t *)NULL;
+ return False;
+ }
+#endif
+ DEBUG(4,("smb_pam_start: PAM: Init passed for user: %s\n", user));
+ return True;
+}
+
+/*
+ * PAM Authentication Handler
+ */
+static NTSTATUS smb_pam_auth(pam_handle_t *pamh, const char *user)
+{
+ int pam_error;
+ NTSTATUS nt_status = NT_STATUS_LOGON_FAILURE;
+
+ /*
+ * 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 | lp_null_passwords() ? 0 : PAM_DISALLOW_NULL_AUTHTOK);
+ switch( pam_error ){
+ case PAM_AUTH_ERR:
+ DEBUG(2, ("smb_pam_auth: PAM: Athentication 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;
+ }
+
+ smb_pam_nt_status_error_handler(pamh, pam_error, "Authentication Failure", 2, &nt_status);
+ return nt_status;
+}
+
+/*
+ * PAM Account Handler
+ */
+static NTSTATUS smb_pam_account(pam_handle_t *pamh, const char * user)
+{
+ int pam_error;
+ NTSTATUS nt_status = NT_STATUS_ACCOUNT_DISABLED;
+
+ 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;
+ }
+
+ smb_pam_nt_status_error_handler(pamh, pam_error, "Account Check Failed", 2, &nt_status);
+ return nt_status;
+}
+
+/*
+ * PAM Credential Setting
+ */
+
+static NTSTATUS smb_pam_setcred(pam_handle_t *pamh, const char * user)
+{
+ int pam_error;
+ NTSTATUS nt_status = NT_STATUS_NO_TOKEN;
+
+ /*
+ * 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;
+ }
+
+ smb_pam_nt_status_error_handler(pamh, pam_error, "Set Credential Failure", 2, &nt_status);
+ return nt_status;
+}
+
+/*
+ * PAM Internal Session Handler
+ */
+static BOOL smb_internal_pam_session(pam_handle_t *pamh, const char *user, const char *tty, BOOL flag)
+{
+ int pam_error;
+
+#ifdef PAM_TTY
+ DEBUG(4,("smb_internal_pam_session: PAM: tty set to: %s\n", tty));
+ pam_error = pam_set_item(pamh, PAM_TTY, tty);
+ if (!smb_pam_error_handler(pamh, pam_error, "set tty failed", 0))
+ return False;
+#endif
+
+ if (flag) {
+ pam_error = pam_open_session(pamh, PAM_SILENT);
+ if (!smb_pam_error_handler(pamh, pam_error, "session setup failed", 0))
+ return False;
+ } else {
+ pam_setcred(pamh, (PAM_DELETE_CRED|PAM_SILENT)); /* We don't care if this fails */
+ pam_error = pam_close_session(pamh, PAM_SILENT); /* This will probably pick up the error anyway */
+ if (!smb_pam_error_handler(pamh, pam_error, "session close failed", 0))
+ return False;
+ }
+ return (True);
+}
+
+/*
+ * Internal PAM Password Changer.
+ */
+
+static BOOL smb_pam_chauthtok(pam_handle_t *pamh, const char * user)
+{
+ int pam_error;
+
+ DEBUG(4,("smb_pam_chauthtok: PAM: Password Change for User: %s\n", user));
+
+ pam_error = pam_chauthtok(pamh, PAM_SILENT); /* Change Password */
+
+ switch( pam_error ) {
+ case PAM_AUTHTOK_ERR:
+ DEBUG(2, ("PAM: unable to obtain the new authentication token - is password to weak?\n"));
+ break;
+
+ /* This doesn't seem to be defined on Solaris. JRA */
+#ifdef PAM_AUTHTOK_RECOVER_ERR
+ case PAM_AUTHTOK_RECOVER_ERR:
+ DEBUG(2, ("PAM: unable to obtain the old authentication token - was the old password wrong?.\n"));
+ break;
+#endif
+
+ case PAM_AUTHTOK_LOCK_BUSY:
+ DEBUG(2, ("PAM: unable to change the authentication token since it is currently locked.\n"));
+ break;
+ case PAM_AUTHTOK_DISABLE_AGING:
+ DEBUG(2, ("PAM: Authentication token aging has been disabled.\n"));
+ break;
+ case PAM_PERM_DENIED:
+ DEBUG(0, ("PAM: Permission denied.\n"));
+ break;
+ case PAM_TRY_AGAIN:
+ DEBUG(0, ("PAM: Could not update all authentication token(s). No authentication tokens were updated.\n"));
+ break;
+ case PAM_USER_UNKNOWN:
+ DEBUG(0, ("PAM: User not known to PAM\n"));
+ break;
+ case PAM_SUCCESS:
+ DEBUG(4, ("PAM: Account OK for User: %s\n", user));
+ break;
+ default:
+ DEBUG(0, ("PAM: UNKNOWN PAM ERROR (%d) for User: %s\n", pam_error, user));
+ }
+
+ if(!smb_pam_error_handler(pamh, pam_error, "Password Change Failed", 2)) {
+ return False;
+ }
+
+ /* If this point is reached, the password has changed. */
+ return True;
+}
+
+/*
+ * PAM Externally accessible Session handler
+ */
+
+BOOL smb_pam_claim_session(char *user, char *tty, char *rhost)
+{
+ pam_handle_t *pamh = NULL;
+ struct pam_conv *pconv = NULL;
+
+ /* Ignore PAM if told to. */
+
+ if (!lp_obey_pam_restrictions())
+ return True;
+
+ if ((pconv = smb_setup_pam_conv(smb_pam_conv, user, NULL, NULL)) == NULL)
+ return False;
+
+ if (!smb_pam_start(&pamh, user, rhost, pconv))
+ return False;
+
+ if (!smb_internal_pam_session(pamh, user, tty, True)) {
+ smb_pam_end(pamh, pconv);
+ return False;
+ }
+
+ return smb_pam_end(pamh, pconv);
+}
+
+/*
+ * PAM Externally accessible Session handler
+ */
+
+BOOL smb_pam_close_session(char *user, char *tty, char *rhost)
+{
+ pam_handle_t *pamh = NULL;
+ struct pam_conv *pconv = NULL;
+
+ /* Ignore PAM if told to. */
+
+ if (!lp_obey_pam_restrictions())
+ return True;
+
+ if ((pconv = smb_setup_pam_conv(smb_pam_conv, user, NULL, NULL)) == NULL)
+ return False;
+
+ if (!smb_pam_start(&pamh, user, rhost, pconv))
+ return False;
+
+ if (!smb_internal_pam_session(pamh, user, tty, False)) {
+ smb_pam_end(pamh, pconv);
+ return False;
+ }
+
+ return smb_pam_end(pamh, pconv);
+}
+
+/*
+ * PAM Externally accessible Account handler
+ */
+
+NTSTATUS smb_pam_accountcheck(const char * user)
+{
+ NTSTATUS nt_status = NT_STATUS_ACCOUNT_DISABLED;
+ pam_handle_t *pamh = NULL;
+ struct pam_conv *pconv = NULL;
+
+ /* Ignore PAM if told to. */
+
+ if (!lp_obey_pam_restrictions())
+ return NT_STATUS_OK;
+
+ if ((pconv = smb_setup_pam_conv(smb_pam_conv, user, NULL, NULL)) == NULL)
+ return NT_STATUS_NO_MEMORY;
+
+ if (!smb_pam_start(&pamh, user, NULL, pconv))
+ return NT_STATUS_ACCOUNT_DISABLED;
+
+ if (!NT_STATUS_IS_OK(nt_status = smb_pam_account(pamh, user)))
+ DEBUG(0, ("smb_pam_accountcheck: PAM: Account Validation Failed - Rejecting User %s!\n", user));
+
+ smb_pam_end(pamh, pconv);
+ return nt_status;
+}
+
+/*
+ * PAM Password Validation Suite
+ */
+
+NTSTATUS smb_pam_passcheck(const char * user, const char * password)
+{
+ pam_handle_t *pamh = NULL;
+ NTSTATUS nt_status = NT_STATUS_LOGON_FAILURE;
+ struct pam_conv *pconv = NULL;
+
+ /*
+ * Note we can't ignore PAM here as this is the only
+ * way of doing auths on plaintext passwords when
+ * compiled --with-pam.
+ */
+
+ if ((pconv = smb_setup_pam_conv(smb_pam_conv, user, password, NULL)) == NULL)
+ return NT_STATUS_LOGON_FAILURE;
+
+ if (!smb_pam_start(&pamh, user, NULL, pconv))
+ return NT_STATUS_LOGON_FAILURE;
+
+ if (!NT_STATUS_IS_OK(nt_status = smb_pam_auth(pamh, user))) {
+ DEBUG(0, ("smb_pam_passcheck: PAM: smb_pam_auth failed - Rejecting User %s !\n", user));
+ smb_pam_end(pamh, pconv);
+ return nt_status;
+ }
+
+ if (!NT_STATUS_IS_OK(nt_status = smb_pam_account(pamh, user))) {
+ DEBUG(0, ("smb_pam_passcheck: PAM: smb_pam_account failed - Rejecting User %s !\n", user));
+ smb_pam_end(pamh, pconv);
+ return nt_status;
+ }
+
+ if (!NT_STATUS_IS_OK(nt_status = smb_pam_setcred(pamh, user))) {
+ DEBUG(0, ("smb_pam_passcheck: PAM: smb_pam_setcred failed - Rejecting User %s !\n", user));
+ smb_pam_end(pamh, pconv);
+ return nt_status;
+ }
+
+ smb_pam_end(pamh, pconv);
+ return nt_status;
+}
+
+/*
+ * PAM Password Change Suite
+ */
+
+BOOL smb_pam_passchange(const char * user, const char * oldpassword, const char * newpassword)
+{
+ /* Appropriate quantities of root should be obtained BEFORE calling this function */
+ struct pam_conv *pconv = NULL;
+ pam_handle_t *pamh = NULL;
+
+ if ((pconv = smb_setup_pam_conv(smb_pam_passchange_conv, user, oldpassword, newpassword)) == NULL)
+ return False;
+
+ if(!smb_pam_start(&pamh, user, NULL, pconv))
+ return False;
+
+ if (!smb_pam_chauthtok(pamh, user)) {
+ DEBUG(0, ("smb_pam_passchange: PAM: Password Change Failed for user %s!\n", user));
+ smb_pam_end(pamh, pconv);
+ return False;
+ }
+
+ return smb_pam_end(pamh, pconv);
+}
+
+#else
+
+/* If PAM not used, no PAM restrictions on accounts. */
+NTSTATUS smb_pam_accountcheck(const char * user)
+{
+ return NT_STATUS_OK;
+}
+
+/* If PAM not used, also no PAM restrictions on sessions. */
+BOOL smb_pam_claim_session(char *user, char *tty, char *rhost)
+{
+ return True;
+}
+
+/* If PAM not used, also no PAM restrictions on sessions. */
+BOOL smb_pam_close_session(char *in_user, char *tty, char *rhost)
+{
+ return True;
+}
+#endif /* WITH_PAM */
diff --git a/source/auth/pass_check.c b/source/auth/pass_check.c
new file mode 100644
index 00000000000..1ac8c1815a6
--- /dev/null
+++ b/source/auth/pass_check.c
@@ -0,0 +1,783 @@
+/*
+ Unix SMB/CIFS implementation.
+ Password checking
+ 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 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.
+*/
+
+/* this module is for checking a username/password against a system
+ password database. The SMB encrypted password support is elsewhere */
+
+#include "includes.h"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_AUTH
+
+/* these are kept here to keep the string_combinations function simple */
+static fstring this_user;
+#if !defined(WITH_PAM)
+static fstring this_salt;
+static fstring this_crypted;
+#endif
+
+#ifdef WITH_AFS
+
+#include <afs/stds.h>
+#include <afs/kautils.h>
+
+/*******************************************************************
+check on AFS authentication
+********************************************************************/
+static BOOL afs_auth(char *user, char *password)
+{
+ long password_expires = 0;
+ char *reason;
+
+ /* For versions of AFS prior to 3.3, this routine has few arguments, */
+ /* but since I can't find the old documentation... :-) */
+ setpag();
+ if (ka_UserAuthenticateGeneral
+ (KA_USERAUTH_VERSION + KA_USERAUTH_DOSETPAG, user, (char *)0, /* instance */
+ (char *)0, /* cell */
+ password, 0, /* lifetime, default */
+ &password_expires, /*days 'til it expires */
+ 0, /* spare 2 */
+ &reason) == 0)
+ {
+ return (True);
+ }
+ DEBUG(1,
+ ("AFS authentication for \"%s\" failed (%s)\n", user, reason));
+ return (False);
+}
+#endif
+
+
+#ifdef WITH_DFS
+
+#include <dce/dce_error.h>
+#include <dce/sec_login.h>
+
+/*****************************************************************
+ This new version of the DFS_AUTH code was donated by Karsten Muuss
+ <muuss@or.uni-bonn.de>. It fixes the following problems with the
+ old code :
+
+ - Server credentials may expire
+ - Client credential cache files have wrong owner
+ - purge_context() function is called with invalid argument
+
+ This new code was modified to ensure that on exit the uid/gid is
+ still root, and the original directory is restored. JRA.
+******************************************************************/
+
+sec_login_handle_t my_dce_sec_context;
+int dcelogin_atmost_once = 0;
+
+/*******************************************************************
+check on a DCE/DFS authentication
+********************************************************************/
+static BOOL dfs_auth(char *user, char *password)
+{
+ error_status_t err;
+ int err2;
+ int prterr;
+ signed32 expire_time, current_time;
+ boolean32 password_reset;
+ struct passwd *pw;
+ sec_passwd_rec_t passwd_rec;
+ sec_login_auth_src_t auth_src = sec_login_auth_src_network;
+ unsigned char dce_errstr[dce_c_error_string_len];
+ gid_t egid;
+
+ if (dcelogin_atmost_once)
+ return (False);
+
+#ifdef HAVE_CRYPT
+ /*
+ * We only go for a DCE login context if the given password
+ * matches that stored in the local password file..
+ * Assumes local passwd file is kept in sync w/ DCE RGY!
+ */
+
+ if (strcmp((char *)crypt(password, this_salt), this_crypted))
+ {
+ return (False);
+ }
+#endif
+
+ sec_login_get_current_context(&my_dce_sec_context, &err);
+ if (err != error_status_ok)
+ {
+ dce_error_inq_text(err, dce_errstr, &err2);
+ DEBUG(0, ("DCE can't get current context. %s\n", dce_errstr));
+
+ return (False);
+ }
+
+ sec_login_certify_identity(my_dce_sec_context, &err);
+ if (err != error_status_ok)
+ {
+ dce_error_inq_text(err, dce_errstr, &err2);
+ DEBUG(0, ("DCE can't get current context. %s\n", dce_errstr));
+
+ return (False);
+ }
+
+ sec_login_get_expiration(my_dce_sec_context, &expire_time, &err);
+ if (err != error_status_ok)
+ {
+ dce_error_inq_text(err, dce_errstr, &err2);
+ DEBUG(0, ("DCE can't get expiration. %s\n", dce_errstr));
+
+ return (False);
+ }
+
+ time(&current_time);
+
+ if (expire_time < (current_time + 60))
+ {
+ struct passwd *pw;
+ sec_passwd_rec_t *key;
+
+ sec_login_get_pwent(my_dce_sec_context,
+ (sec_login_passwd_t *) & pw, &err);
+ if (err != error_status_ok)
+ {
+ dce_error_inq_text(err, dce_errstr, &err2);
+ DEBUG(0, ("DCE can't get pwent. %s\n", dce_errstr));
+
+ return (False);
+ }
+
+ sec_login_refresh_identity(my_dce_sec_context, &err);
+ if (err != error_status_ok)
+ {
+ dce_error_inq_text(err, dce_errstr, &err2);
+ DEBUG(0, ("DCE can't refresh identity. %s\n",
+ dce_errstr));
+
+ return (False);
+ }
+
+ sec_key_mgmt_get_key(rpc_c_authn_dce_secret, NULL,
+ (unsigned char *)pw->pw_name,
+ sec_c_key_version_none,
+ (void **)&key, &err);
+ if (err != error_status_ok)
+ {
+ dce_error_inq_text(err, dce_errstr, &err2);
+ DEBUG(0, ("DCE can't get key for %s. %s\n",
+ pw->pw_name, dce_errstr));
+
+ return (False);
+ }
+
+ sec_login_valid_and_cert_ident(my_dce_sec_context, key,
+ &password_reset, &auth_src,
+ &err);
+ if (err != error_status_ok)
+ {
+ dce_error_inq_text(err, dce_errstr, &err2);
+ DEBUG(0,
+ ("DCE can't validate and certify identity for %s. %s\n",
+ pw->pw_name, dce_errstr));
+ }
+
+ sec_key_mgmt_free_key(key, &err);
+ if (err != error_status_ok)
+ {
+ dce_error_inq_text(err, dce_errstr, &err2);
+ DEBUG(0, ("DCE can't free key.\n", dce_errstr));
+ }
+ }
+
+ if (sec_login_setup_identity((unsigned char *)user,
+ sec_login_no_flags,
+ &my_dce_sec_context, &err) == 0)
+ {
+ dce_error_inq_text(err, dce_errstr, &err2);
+ DEBUG(0, ("DCE Setup Identity for %s failed: %s\n",
+ user, dce_errstr));
+ return (False);
+ }
+
+ sec_login_get_pwent(my_dce_sec_context,
+ (sec_login_passwd_t *) & pw, &err);
+ if (err != error_status_ok)
+ {
+ dce_error_inq_text(err, dce_errstr, &err2);
+ DEBUG(0, ("DCE can't get pwent. %s\n", dce_errstr));
+
+ return (False);
+ }
+
+ sec_login_purge_context(&my_dce_sec_context, &err);
+ if (err != error_status_ok)
+ {
+ dce_error_inq_text(err, dce_errstr, &err2);
+ DEBUG(0, ("DCE can't purge context. %s\n", dce_errstr));
+
+ return (False);
+ }
+
+ /*
+ * NB. I'd like to change these to call something like change_to_user()
+ * instead but currently we don't have a connection
+ * context to become the correct user. This is already
+ * fairly platform specific code however, so I think
+ * this should be ok. I have added code to go
+ * back to being root on error though. JRA.
+ */
+
+ egid = getegid();
+
+ set_effective_gid(pw->pw_gid);
+ set_effective_uid(pw->pw_uid);
+
+ if (sec_login_setup_identity((unsigned char *)user,
+ sec_login_no_flags,
+ &my_dce_sec_context, &err) == 0)
+ {
+ dce_error_inq_text(err, dce_errstr, &err2);
+ DEBUG(0, ("DCE Setup Identity for %s failed: %s\n",
+ user, dce_errstr));
+ goto err;
+ }
+
+ sec_login_get_pwent(my_dce_sec_context,
+ (sec_login_passwd_t *) & pw, &err);
+ if (err != error_status_ok)
+ {
+ dce_error_inq_text(err, dce_errstr, &err2);
+ DEBUG(0, ("DCE can't get pwent. %s\n", dce_errstr));
+ goto err;
+ }
+
+ passwd_rec.version_number = sec_passwd_c_version_none;
+ passwd_rec.pepper = NULL;
+ passwd_rec.key.key_type = sec_passwd_plain;
+ passwd_rec.key.tagged_union.plain = (idl_char *) password;
+
+ sec_login_validate_identity(my_dce_sec_context,
+ &passwd_rec, &password_reset,
+ &auth_src, &err);
+ if (err != error_status_ok)
+ {
+ dce_error_inq_text(err, dce_errstr, &err2);
+ DEBUG(0,
+ ("DCE Identity Validation failed for principal %s: %s\n",
+ user, dce_errstr));
+ goto err;
+ }
+
+ sec_login_certify_identity(my_dce_sec_context, &err);
+ if (err != error_status_ok)
+ {
+ dce_error_inq_text(err, dce_errstr, &err2);
+ DEBUG(0, ("DCE certify identity failed: %s\n", dce_errstr));
+ goto err;
+ }
+
+ if (auth_src != sec_login_auth_src_network)
+ {
+ DEBUG(0, ("DCE context has no network credentials.\n"));
+ }
+
+ sec_login_set_context(my_dce_sec_context, &err);
+ if (err != error_status_ok)
+ {
+ dce_error_inq_text(err, dce_errstr, &err2);
+ DEBUG(0,
+ ("DCE login failed for principal %s, cant set context: %s\n",
+ user, dce_errstr));
+
+ sec_login_purge_context(&my_dce_sec_context, &err);
+ goto err;
+ }
+
+ sec_login_get_pwent(my_dce_sec_context,
+ (sec_login_passwd_t *) & pw, &err);
+ if (err != error_status_ok)
+ {
+ dce_error_inq_text(err, dce_errstr, &err2);
+ DEBUG(0, ("DCE can't get pwent. %s\n", dce_errstr));
+ goto err;
+ }
+
+ DEBUG(0, ("DCE login succeeded for principal %s on pid %d\n",
+ user, sys_getpid()));
+
+ DEBUG(3, ("DCE principal: %s\n"
+ " uid: %d\n"
+ " gid: %d\n",
+ pw->pw_name, pw->pw_uid, pw->pw_gid));
+ DEBUG(3, (" info: %s\n"
+ " dir: %s\n"
+ " shell: %s\n",
+ pw->pw_gecos, pw->pw_dir, pw->pw_shell));
+
+ sec_login_get_expiration(my_dce_sec_context, &expire_time, &err);
+ if (err != error_status_ok)
+ {
+ dce_error_inq_text(err, dce_errstr, &err2);
+ DEBUG(0, ("DCE can't get expiration. %s\n", dce_errstr));
+ goto err;
+ }
+
+ set_effective_uid(0);
+ set_effective_gid(0);
+
+ DEBUG(0,
+ ("DCE context expires: %s", asctime(localtime(&expire_time))));
+
+ dcelogin_atmost_once = 1;
+ return (True);
+
+ err:
+
+ /* Go back to root, JRA. */
+ set_effective_uid(0);
+ set_effective_gid(egid);
+ return (False);
+}
+
+void dfs_unlogin(void)
+{
+ error_status_t err;
+ int err2;
+ unsigned char dce_errstr[dce_c_error_string_len];
+
+ sec_login_purge_context(&my_dce_sec_context, &err);
+ if (err != error_status_ok)
+ {
+ dce_error_inq_text(err, dce_errstr, &err2);
+ DEBUG(0,
+ ("DCE purge login context failed for server instance %d: %s\n",
+ sys_getpid(), dce_errstr));
+ }
+}
+#endif
+
+#ifdef LINUX_BIGCRYPT
+/****************************************************************************
+an enhanced crypt for Linux to handle password longer than 8 characters
+****************************************************************************/
+static int linux_bigcrypt(char *password, char *salt1, char *crypted)
+{
+#define LINUX_PASSWORD_SEG_CHARS 8
+ char salt[3];
+ int i;
+
+ StrnCpy(salt, salt1, 2);
+ crypted += 2;
+
+ for (i = strlen(password); i > 0; i -= LINUX_PASSWORD_SEG_CHARS) {
+ char *p = crypt(password, salt) + 2;
+ if (strncmp(p, crypted, LINUX_PASSWORD_SEG_CHARS) != 0)
+ return (0);
+ password += LINUX_PASSWORD_SEG_CHARS;
+ crypted += strlen(p);
+ }
+
+ return (1);
+}
+#endif
+
+#ifdef OSF1_ENH_SEC
+/****************************************************************************
+an enhanced crypt for OSF1
+****************************************************************************/
+static char *osf1_bigcrypt(char *password, char *salt1)
+{
+ static char result[AUTH_MAX_PASSWD_LENGTH] = "";
+ char *p1;
+ char *p2 = password;
+ char salt[3];
+ int i;
+ int parts = strlen(password) / AUTH_CLEARTEXT_SEG_CHARS;
+ if (strlen(password) % AUTH_CLEARTEXT_SEG_CHARS)
+ parts++;
+
+ StrnCpy(salt, salt1, 2);
+ StrnCpy(result, salt1, 2);
+ result[2] = '\0';
+
+ for (i = 0; i < parts; i++) {
+ p1 = crypt(p2, salt);
+ strncat(result, p1 + 2,
+ AUTH_MAX_PASSWD_LENGTH - strlen(p1 + 2) - 1);
+ StrnCpy(salt, &result[2 + i * AUTH_CIPHERTEXT_SEG_CHARS], 2);
+ p2 += AUTH_CLEARTEXT_SEG_CHARS;
+ }
+
+ return (result);
+}
+#endif
+
+
+/****************************************************************************
+apply a function to upper/lower case combinations
+of a string and return true if one of them returns true.
+try all combinations with N uppercase letters.
+offset is the first char to try and change (start with 0)
+it assumes the string starts lowercased
+****************************************************************************/
+static NTSTATUS string_combinations2(char *s, int offset, NTSTATUS (*fn) (const char *),
+ int N)
+{
+ int len = strlen(s);
+ int i;
+ NTSTATUS nt_status;
+
+#ifdef PASSWORD_LENGTH
+ len = MIN(len, PASSWORD_LENGTH);
+#endif
+
+ if (N <= 0 || offset >= len)
+ return (fn(s));
+
+ for (i = offset; i < (len - (N - 1)); i++) {
+ char c = s[i];
+ if (!islower(c))
+ continue;
+ s[i] = toupper(c);
+ if (!NT_STATUS_EQUAL(nt_status = string_combinations2(s, i + 1, fn, N - 1),NT_STATUS_WRONG_PASSWORD)) {
+ return (nt_status);
+ }
+ s[i] = c;
+ }
+ return (NT_STATUS_WRONG_PASSWORD);
+}
+
+/****************************************************************************
+apply a function to upper/lower case combinations
+of a string and return true if one of them returns true.
+try all combinations with up to N uppercase letters.
+offset is the first char to try and change (start with 0)
+it assumes the string starts lowercased
+****************************************************************************/
+static NTSTATUS string_combinations(char *s, NTSTATUS (*fn) (const char *), int N)
+{
+ int n;
+ NTSTATUS nt_status;
+ for (n = 1; n <= N; n++)
+ if (!NT_STATUS_EQUAL(nt_status = string_combinations2(s, 0, fn, n), NT_STATUS_WRONG_PASSWORD))
+ return nt_status;
+ return NT_STATUS_WRONG_PASSWORD;
+}
+
+
+/****************************************************************************
+core of password checking routine
+****************************************************************************/
+static NTSTATUS password_check(const char *password)
+{
+#ifdef WITH_PAM
+ return smb_pam_passcheck(this_user, password);
+#else
+
+ BOOL ret;
+
+#ifdef WITH_AFS
+ if (afs_auth(this_user, password))
+ return NT_STATUS_OK;
+#endif /* WITH_AFS */
+
+#ifdef WITH_DFS
+ if (dfs_auth(this_user, password))
+ return NT_STATUS_OK;
+#endif /* WITH_DFS */
+
+#ifdef OSF1_ENH_SEC
+
+ ret = (strcmp(osf1_bigcrypt(password, this_salt),
+ this_crypted) == 0);
+ if (!ret) {
+ DEBUG(2,
+ ("OSF1_ENH_SEC failed. Trying normal crypt.\n"));
+ ret = (strcmp((char *)crypt(password, this_salt), this_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, this_salt), this_crypted) == 0);
+ if (ret) {
+ return NT_STATUS_OK;
+ } else {
+ return NT_STATUS_WRONG_PASSWORD;
+ }
+
+#endif /* ULTRIX_AUTH */
+
+#ifdef LINUX_BIGCRYPT
+ ret = (linux_bigcrypt(password, this_salt, this_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, this_salt), this_crypted) == 0)
+ return NT_STATUS_OK;
+ else
+ ret = (strcmp((char *)crypt(password, this_salt), this_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, this_salt), this_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, this_salt), this_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 */
+#endif /* WITH_PAM */
+}
+
+
+
+/****************************************************************************
+CHECK if a username/password is OK
+the function pointer fn() points to a function to call when a successful
+match is found and is used to update the encrypted password file
+return NT_STATUS_OK on correct match, appropriate error otherwise
+****************************************************************************/
+
+NTSTATUS pass_check(const struct passwd *pass, const char *user, const char *password,
+ int pwlen, BOOL (*fn) (const char *, const char *), BOOL run_cracker)
+{
+ pstring pass2;
+ int level = lp_passwordlevel();
+
+ NTSTATUS nt_status;
+
+#if DEBUG_PASSWORD
+ DEBUG(100, ("checking user=[%s] pass=[%s]\n", user, password));
+#endif
+
+ if (!password)
+ return NT_STATUS_LOGON_FAILURE;
+
+ if (((!*password) || (!pwlen)) && !lp_null_passwords())
+ return NT_STATUS_LOGON_FAILURE;
+
+#if defined(WITH_PAM)
+
+ /*
+ * If we're using PAM we want to short-circuit all the
+ * checks below and dive straight into the PAM code.
+ */
+
+ fstrcpy(this_user, user);
+
+ DEBUG(4, ("pass_check: Checking (PAM) password for user %s (l=%d)\n", user, pwlen));
+
+#else /* Not using PAM */
+
+ DEBUG(4, ("pass_check: Checking password for user %s (l=%d)\n", user, pwlen));
+
+ if (!pass) {
+ DEBUG(3, ("Couldn't find user %s\n", user));
+ return NT_STATUS_NO_SUCH_USER;
+ }
+
+
+ /* Copy into global for the convenience of looping code */
+ /* Also the place to keep the 'password' no matter what
+ crazy struct it started in... */
+ fstrcpy(this_crypted, pass->pw_passwd);
+ fstrcpy(this_salt, pass->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(pass->pw_name);
+ if (spass && spass->sp_pwdp) {
+ fstrcpy(this_crypted, spass->sp_pwdp);
+ fstrcpy(this_salt, spass->sp_pwdp);
+ }
+ }
+#elif defined(IA_UINFO)
+ {
+ /* 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(pass->pw_name, &uinfo) != -1)
+ ia_get_logpwd(uinfo, &(pass->pw_passwd));
+ }
+#endif
+
+#ifdef HAVE_GETPRPWNAM
+ {
+ struct pr_passwd *pr_pw = getprpwnam(pass->pw_name);
+ if (pr_pw && pr_pw->ufld.fd_encrypt)
+ fstrcpy(this_crypted, pr_pw->ufld.fd_encrypt);
+ }
+#endif
+
+#ifdef HAVE_GETPWANAM
+ {
+ struct passwd_adjunct *pwret;
+ pwret = getpwanam(s);
+ if (pwret && pwret->pwa_passwd)
+ fstrcpy(this_crypted, pwret->pwa_passwd);
+ }
+#endif
+
+#ifdef OSF1_ENH_SEC
+ {
+ struct pr_passwd *mypasswd;
+ DEBUG(5, ("Checking password for user %s in OSF1_ENH_SEC\n",
+ user));
+ mypasswd = getprpwnam(user);
+ if (mypasswd) {
+ fstrcpy(this_user, mypasswd->ufld.fd_name);
+ fstrcpy(this_crypted, mypasswd->ufld.fd_encrypt);
+ } else {
+ DEBUG(5,
+ ("OSF1_ENH_SEC: No entry for user %s in protected database !\n",
+ user));
+ }
+ }
+#endif
+
+#ifdef ULTRIX_AUTH
+ {
+ AUTHORIZATION *ap = getauthuid(pass->pw_uid);
+ if (ap) {
+ fstrcpy(this_crypted, ap->a_password);
+ endauthent();
+ }
+ }
+#endif
+
+#if defined(HAVE_TRUNCATED_SALT)
+ /* crypt on some platforms (HPUX in particular)
+ won't work with more than 2 salt characters. */
+ this_salt[2] = 0;
+#endif
+
+ if (!*this_crypted) {
+ if (!lp_null_passwords()) {
+ DEBUG(2, ("Disallowing %s with null password\n",
+ this_user));
+ return NT_STATUS_LOGON_FAILURE;
+ }
+ if (!*password) {
+ DEBUG(3,
+ ("Allowing access to %s with null password\n",
+ this_user));
+ return NT_STATUS_OK;
+ }
+ }
+
+#endif /* defined(WITH_PAM) */
+
+ /* try it as it came to us */
+ nt_status = password_check(password);
+ if NT_STATUS_IS_OK(nt_status) {
+ if (fn) {
+ fn(user, password);
+ }
+ 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 (!run_cracker) {
+ 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 */
+ pstrcpy(pass2, password);
+
+ /* try all lowercase if it's currently all uppercase */
+ if (strhasupper(pass2)) {
+ strlower_m(pass2);
+ if NT_STATUS_IS_OK(nt_status = password_check(pass2)) {
+ if (fn)
+ fn(user, pass2);
+ return (nt_status);
+ }
+ }
+
+ /* give up? */
+ if (level < 1) {
+ return NT_STATUS_WRONG_PASSWORD;
+ }
+
+ /* last chance - all combinations of up to level chars upper! */
+ strlower_m(pass2);
+
+ if (NT_STATUS_IS_OK(nt_status = string_combinations(pass2, password_check, level))) {
+ if (fn)
+ fn(user, pass2);
+ return nt_status;
+ }
+
+ return NT_STATUS_WRONG_PASSWORD;
+}
diff --git a/source/autogen.sh b/source/autogen.sh
new file mode 100755
index 00000000000..6042b82cdce
--- /dev/null
+++ b/source/autogen.sh
@@ -0,0 +1,65 @@
+#!/bin/sh
+
+# Run this script to build samba from CVS.
+
+## insert all possible names (only works with
+## autoconf 2.x
+TESTAUTOHEADER="autoheader autoheader-2.53"
+TESTAUTOCONF="autoconf autoconf-2.53"
+
+AUTOHEADERFOUND="0"
+AUTOCONFFOUND="0"
+
+
+##
+## Look for autoheader
+##
+for i in $TESTAUTOHEADER; do
+ if which $i > /dev/null 2>&1; then
+ if [ `$i --version | head -n 1 | cut -d. -f 2` -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 [ `$i --version | head -n 1 | cut -d. -f 2` -ge 53 ]; then
+ AUTOCONF=$i
+ AUTOCONFFOUND="1"
+ break
+ fi
+ fi
+done
+
+
+##
+## do we have it?
+##
+if [ "$AUTOCONFFOUND" = "0" -o "$AUTOHEADERFOUND" = "0" ]; then
+ echo "$0: need autoconf 2.53 or later to build samba from CVS" >&2
+ exit 1
+fi
+
+
+
+echo "$0: running $AUTOHEADER"
+$AUTOHEADER || exit 1
+
+echo "$0: running $AUTOCONF"
+$AUTOCONF || exit 1
+
+echo "$0: running script/mkversion.sh"
+./script/mkversion.sh || exit 1
+
+rm -rf autom4te.cache autom4te-2.53.cache
+
+echo "Now run ./configure and then make."
+exit 0
+
diff --git a/source/bin/.cvsignore b/source/bin/.cvsignore
new file mode 100644
index 00000000000..3144075134e
--- /dev/null
+++ b/source/bin/.cvsignore
@@ -0,0 +1,56 @@
+debug2html
+.dummy
+editreg
+.libs
+locktest
+locktest2
+log2pcap
+make_printerdef
+make_smbcodepage
+make_unicodemap
+masktest
+msgtest
+net
+nmbd
+nmblookup
+nsstest
+ntlm_auth
+pdbedit
+profiles
+rpcclient
+samsync
+samtest
+smbcacls
+smbcacls
+smbclient
+smbcontrol
+smbcquotas
+smbd
+smbfilter
+smbget
+smbgroupedit
+smbiconv
+smbmnt
+smbmount
+smbpasswd
+smbsh
+smbspool
+smbstatus
+smbtorture
+smbtree
+smbumount
+*.so
+swat
+talloctort
+tdbbackup
+tdbdump
+testparm
+testprns
+t_push_ucs2
+t_snprintf
+t_strcmp
+t_stringoverflow
+vfstest
+wbinfo
+winbindd
+wrepld
diff --git a/source/change-log b/source/change-log
new file mode 100644
index 00000000000..1f7798b541f
--- /dev/null
+++ b/source/change-log
@@ -0,0 +1,1878 @@
+SUPERCEDED Change Log for Samba
+^^^^^^^^^^
+
+Unless otherwise attributed, all changes were made by
+Andrew.Tridgell@anu.edu.au. All bugs to samba-bugs@samba.org.
+
+NOTE: THIS LOG IS IN CHRONOLOGICAL ORDER
+
+NOTE: From now on the cvs.log file will be used to give a complete log of
+changes to samba. This change-log is now obsolete.
+
+1.5.00 announced to mailing list
+
+1.5.01 1/12/93
+ - configuration through makefile only
+ - fixed silly bug that made the client not accept dir's from
+ the server
+ - tested and updated include files for ultrix, aix and solaris
+ - several things fixed thanks to pierson@ketje.enet.dec.com
+ who provided invaluable help and advice.
+
+1.5.02 1/12/93
+ - added username option to services file so connection
+ as non guest from lanmanager is possible
+ - made server abort when it can't read/write on a socket
+ - added logging to client
+
+1.5.03 2/12/93
+ - printing now works
+ - fixed a minor bug to do with hidden and system attributes
+
+1.5.04 2/12/93
+ - added reduce_name() call to fill in security hole.
+ - cleanup up debug stuff a little
+
+1.5.05 2/12/93
+ - fixed bug in reduce_name that affects services with base paths
+ that have a soft link in them.
+
+1.5.06 3/12/93
+ - used the reserved server field in the search status to hold the
+ directory pointer. This allows lots of directories to be open
+ at once by clients without stuffing things up.
+ - preserved all the client reserved bytes in the search status
+ in case they actually use them. Hopefully this will fix the annoying
+ empty directory dir bug. (it does)
+
+1.5.07 3/12/93
+ - fixed silly bug that caused volume ids to appear twice
+ - fixed a wrote-too-few bug in smb_send()
+
+1.5.08 3/12/93
+ - did the SMBsearch properly. It can now handle recursive searches.
+ In order to keep the required dir info I encode the dirptr and
+ the current dir offset (from telldir) into 5 bytes by using a table
+ on the last 7 bits of the first byte. The first bit is always on
+ as this byte must by != 0
+ This is all put in the "server reserved" search field.
+
+1.5.09 5/12/93
+ - added a prototype nameserver. It's broken but can at least interpret
+ incoming packets.
+ - minor fixes to the server and client
+
+
+1.5.10 5/12/93
+ - fixed silly unsigned/signed char bug that made dosshell noot see all files
+ - added nmbd to Makefile
+
+1.5.11 6/12/93
+ - made the volume label appear as the service name, rather than "Remote"
+ - made the nmbd actually work (a little) for lanman for dos
+
+1.5.12 7/12/93
+ - fixed broadcasting in the nameserver
+ - the smbd now correctly sets the pid and uid
+ - nmbd now seems to work enough to satisfy the MS client.
+
+
+1.5.13 7/12/93
+ - fixed a silly bug that truncated filenames
+ - added -B option to nameserver to specify bcast address
+ - added -R option to nameserver to prevent name registering
+ - fixed minor read() bug. Does this fix the "cmp" bug?
+
+1.5.14 8/12/93
+ - fixed a bug in send_login() in the client. Thanks to
+ tim.hudson@gslmail.mincom.oz.au for pointing this out.
+ - changed name_mangle() to pad to minimum of 32 bytes with spaces
+ - changed the returned buffer size in reply_connect() to not
+ count the 4 byte length field. This fixes the "can execute" bug
+ and the "comp" bug
+ - once again re-wrote the directory pointer handling code.
+ now "tree" works correctly
+
+1.5.15 9/12/93
+ - fixed name mangle bug introduced in 1.5.14 which stopped
+ nameserver from working
+
+1.5.16 9/12/93
+ - arrgh. another silly bug in name_mangle() causes the client to die.
+
+
+1.5.17 13/12/93
+ - some cosmetic cleanups to the code
+ - changed make_connection not to lower case the password (thanks
+ to bryan@alex.com)
+ - fixed accept() bug not initialising in_addrlen (thanks to
+ bogstad@cs.jhu.edu)
+ - fixed cd bug in client.c (thanks to joergs@toppoint.de)
+ - lots of fixes to the nameserver to read_socket and
+ associated routines. It should now correctly reply to the originating
+ address and use the correct broadcast.
+ (thanks to troyer@saifr00.ateng.az.honeywell.com)
+ - SVR4 patches from mark@scot1.ucsalf.ac.uk
+ - changed the default BUFFER_SIZE to 0xFFFF
+
+1.5.18 15/12/93
+ - minor fix to reply_printqueue() to zero data buffer array.
+ - added print command to client.
+ - fixed minor bug in cmd_put() in client where a handle could
+ be closed without being previously opened.
+ - minor cleanups to the client
+ - minor solaris fixes from lonnie@itg.ti.com
+ - SYSV, shadow password and dfree() fixes from mark@scot1.ucsalf.ac.uk
+ - fixed reply_delete() to not delete read-only files
+ - fixed infinite loop in reply_delete on "del ."
+ Thanks to mark@scot1.ucsalf.ac.uk for pointing this out.
+ - posix mode definitions and changes from mark@scot1.ucsalf.ac.uk
+
+
+1.5.19 18/12/93
+ - another very minor fix to dfree().
+ - minor change to SVR4 makefile entry from rossw@march.co.uk
+ - changed reply_open not to open directories, this fixes the
+ "copy .." bug pointed out by mark@scot1.ucsalf.ac.uk
+ - changed dos_mode() so it doesn't return hidden and system info
+ on directories.
+ - changed get_dir_entry() not to descend into proc/self under linux
+ control this with the DONT_DESCEND define in includes.h
+ - changed smb_setlen() to add in the SMB id. (thanks
+ to troyer@saifr00.ateng.az.honeywell.com)
+ - fixed minor bug in reply_dir() so it won't return a ACCESS_DENIED
+ when searching a directory that is unreadable
+ - removed second stat() from get_dir_entry() (speed up)
+ - made null searches close the dirptr (fixes big filesystem problem)
+ - fixed clean_name for cd .. (from magnus@axiom.se)
+
+
+1.5.20 28/12/93
+ - added debug statement in case of SMBcreate with volid set (leefi@microsoft.com)
+ - fixed a bug in dptr_close() so it sets the next_key to a better
+ value, this fixes a annoying dir bug
+ - LOTS of changes from jeremy@netcom.com (Jeremy Allison). This
+ makes it possible to at least connect to a NT server with the client
+ and also fixes up much of the socket/process code. This also includes
+ stuff for compiling on a sun386
+ - got the client working with the Syntax server (a commercial
+ smb-based server). This required a few minor changes so the xmit
+ sizes were negotiated properly.
+ - added support for OSF1, tested on a DEC3000/400 alpha.
+ - fixed the ifconf support under ultrix
+
+1.5.21 31/12/93
+ - minor cosmetic change to reduce_name()
+ - changes for HPUX from ppk@atk.tpo.fi (Pasi Kaara)
+ - minor fix to nameserver
+ - revamped configuration file format. It now takes a Windows-style
+ (.INI style) configuration file. See the file services for
+ full details of the format. New files: loadparm.c, loadparm.h,
+ params.c, params.h, testparm.c. Several changes to smb.h, local.h,
+ server.c, Makefile. The services structure is no longer visible
+ to the rest of the system. (Karl Auer)
+ - added ability to specify a print command on a per service basis
+ and globally via the configuration file. Also allows guest account
+ to be specified in the configuration file. Made appropriate changes
+ to server.c so that these data items are obtained from the config
+ module rather than from hardcoded strings (though the hardcoded
+ strings are still the source of the defaults). (Karl Auer)
+ - renamed old-style configuration file to services.old (Karl Auer)
+ - changed README to reflect new configuration details. (Karl Auer)
+ - removed an item from the bugs wishlist (now supplied!) (Karl Auer)
+ - protected smb.h against multiple compilation. (Karl Auer)
+ - protected local.h against multiple compilation. (Karl Auer)
+ - made config stuff do dynamic allocation
+ - added "homes" capability
+ - added create_mask to each service in config
+
+1.5.22 3/1/94
+ - added "root dir" option for extra security
+ - added -n option to client (useful for OS/2)
+ - changed operation of -n to nameserver to be more useful
+ - patches from Jeremy Allison (jeremy@netcom.com)
+ fixing bug in set_message(), fixing up wait3() for SYSV,
+ making cd check the path in the client, allowing fetching to stdin
+ in client, and enhancing prompt in client to include directory.
+ - made the -D become_daemon() actually detach from the tty. This
+ may need tuning for different flavors of unix.
+ - added "dont descend" option to each service to prevent infinite
+ loops on recursive filesystems.
+ - updated README to add "running as a daemon" and a simple
+ smb.conf file.
+ - HP/UX fixes from ppk@atk.tpo.fi
+ - made lock calls only if opened with write enabled, as pointed out
+ by gadams@ddrive.demon.co.uk
+
+1.5.23 4/1/94
+ - minor fix to logging of data in receive_smb(). It used to
+ miss the last 4 bytes of packets.
+ - added the pid,uid and mid fields to the negotiation phase of
+ the client.
+ - made client able to print from stdin
+ - added password on command line for client
+ - created a sample printcap input filter "smbprint"
+ - several fixes to client to work with OS/2
+ - added mput, mget, prompt and lcd to client
+
+1.5.24 5/1/94
+ - a resend of 1.5.23 as I managed to not include the new
+ prompt, mput and mget code.
+
+1.5.25 7/1/94
+ - change -B on nameserver so it can override the broadcast address
+ - minor changes to printing in client so OS/2 server can handle it.
+ - fixed reply_access() where OK was not being initialised
+ - added "max xmit" to global parameters.
+ - changed create to open with O_RDWR instead of O_WRONLY
+ - added printmode command to client
+ - made help return extra help on a specified command in client
+ - fixed return code in chkpath
+ - added "recurse" and "lowercase" options to client
+ - fixed some error codes from server
+ - added -I option to client
+ - fix for become_daemon() for HPUX from ppk@atk.tpo.fi
+ - added "hosts allow" and "hosts deny" to server
+ - added keepalives to server
+ - added "access" feature to testparam
+ - NetBSD patches from sreiz@aie.nl
+
+1.5.26 8/1/94
+ - changed semantics of hosts access code to do more sensible defaults
+ when either of "hosts allow" or "hosts deny" is blank
+ - added the SO_KEEPALIVE option to configurations of sockets in the
+ server
+ - made some of the SVAL fns into macros to keep fussy compilers from
+ complaining
+ - fixed several null pointer bugs in check_access(). These bugs
+ made 1.5.25 unuseable for many people.
+ - fixed null pointer reference of lp_dontdescend()
+ - reload services file after each new connection.
+
+1.5.27 11/1/94
+ - fixed opening mode for reply_open() in server
+ - patches from Jeremy Allison (jeremy@netcom.com) to support the
+ "core+" protocol. The patches also inclued some other features, such
+ as a new read_with_timeout() call (used by SMBreadbraw), and auto
+ detection of the need to create a socket.
+ - changed the default KEEPALIVE value to 0, as it caused
+ problems with Lanmanager.
+ - added tar capability to client when getting files
+ - altered unix_mode() to return x bits for directories
+ - fixed bug in trim_string()
+
+1.5.28 12/1/94
+ - cleaned up the debug levels a little so debug level 1 is a practical
+ level for general use
+ - fixed a bug in add_a_service() where a freed pointer was referenced. Thanks
+ to bryan@alex.com for finding the bug.
+ - fixed bug in time structure handling in server and client. Thanks to
+ bryan@alex.com for pointing out the bug.
+
+
+1.5.29 15/1/94
+ - fixed a silly bug in reply_open(). Thanks to
+ jeremy@netcom.com for pointing this out.
+ - fixed debug levels in client to be more sensible
+ - added raw read to client
+ - added -B option to client
+ - fixed several bugs in the client, mostly to do with the tar option
+ - added -E option to client
+
+1.5.30 16/1/94
+ - added lots of prototypes so compilers don't complain
+ - fixed minor bug in reply_rename() (thanks to ppk@atk.tpo.fi)
+ - added more support for LANMAN1.0 protocol.
+ - added SESSION SETUP AND X call
+ - added READ AND X call
+ - added TREE CONNECT AND X call
+ - added support for setbuffer for HPUX (thanks to ppk@atk.tpo.fi)
+
+1.5.31 29/1/94
+ - added support for user level security in smbclient eg:
+ smbclient "\\SERVER\SHARE" -U USERNAME%PASSWORD
+ - added error message decode as per SMB File Sharing
+ protocol extensions. (thanks to merik@blackadder.dsh.oz.au)
+ - added selection masks to smbclient that recurse down directory
+ tree. eg: mget *.* with recurse and mask *.c on will retrieve all
+ *.c files in the tree.
+ - patches for FreeBSD from kuku@acds.physik.rwth-aachen.de
+ - changed reduce_name() to trim ./ from front of strings and / from
+ back
+ - fixed a nasty bug in trim_string().
+ - numerous small changes to lots of stuff that I didn't
+ document while I was doing them. Sorry :-(
+ - slightly updated sockspy
+
+ - The following was done by Karl Auer (Karl.Auer@anu.edu.au)
+ - added processing in configuration file of a [printers] section. Allows
+ connection to any printer specified in /etc/printcap (or the file
+ specified in the global parameter 'printcap name').
+ - added full processing of 'available' flag to configuration file. A
+ service can now be 'turned off' by specifying 'available = no'. Of
+ dubious utility.
+ - added 'printcap =' parameter to [global] section in the configuration
+ file. This allows the normal /etc/printcap to be bypassed when
+ checking printer names for dynamic printer connections via [printers].
+ - added 'printer name =' parameters to both the [global] section and
+ services sections of the configuration file. This allows the printer
+ name only to be set, without having to specify an entire print
+ command.
+ - added some synonyms: 'writable' and 'write ok' have the opposite sense
+ to 'read only'. 'public' may be used instead of 'guest ok'. 'printer'
+ may be used instead of 'printer name'. 'printable' is the same as
+ 'print ok'. 'root' may be used instead of 'root dir' or 'root
+ directory'.
+ - added lots more detail to the sample configuration file to take
+ account of the above.
+ - many minor fixes to internal documentation in the configuration
+ sources.
+ - also - Man pages!
+
+
+1.5.32 3/2/94
+ - addition of smbd, smbclient and testparm man pages
+ from Karl Auer
+ - zombie process fix from lendecke@namu01.gwdg.de
+ - added capability to nmbd to serve names available
+ via gethostbyname().
+
+1.5.33 3/2/94
+ - fixed up getting of netmask so it works on more unix variants
+ - added -N option to nmbd
+ - changed GMT diff calculation. need to check it's right for
+ lots of OSes
+ - fixed a bug in read_and_X() and chain_reply() chaining now
+ seems to work correctly
+
+1.5.34 4/2/94
+ - fixed bug in client that meant it couldn't get/put files from WfWg
+ - fixed a bug in the server that caused lpr to return -1 under sunos
+ - fixed a few errors in the hosts allow section of the
+ smb.conf.5 manual page and added examples
+
+1.5.35 6/2/1994
+ - minor bugfix in reduce_name().
+ - changed width of "size" in client during a dir
+ - patches for NEXT (among other things) from lendecke@namu01.gwdg.de
+ - added -a switch to server, and made default action to append
+ to log file
+ - added deadtime options to [global] section for timing out
+ dead connections to the smbd.
+ - HPUX changes from Pasi.Kaara@atk.tpo.fi
+ - made use of unsigned char more consistent
+ - changed the way of getting the default username and host in the
+ client
+ - made LANMAN1 default to on in the client, off in server.
+ Use -DLANMAN1=1 to make it on in both.
+ - lots of casts and cleanups for various operating systems
+ - changes to the Makefile from Karl to auto-instal the man pages
+ - added a short history of the project to the distribution
+
+1.5.36 15/2/94
+ - fixed minor bug in Debug() (thanks to Pasi.Kaara@atk.tpo.fi)
+ - fixed bug in server.c so -a wasn't accepted.
+ - minor fixes to the client
+ - added hosts file to name server (-H option)
+ - added -G option for groups to nameserver
+ - cleanups and additions from Jeremy Allison, taking us
+ closer to LANMAN1.0. In particular the locking code was cleaned up
+ considerably.
+
+1.5.37 16/2/94
+ - fixed bug introduced in 1.5.36 which disabled SMBcreate
+
+1.5.38 18/2/94
+ - fixed get_broadcast() for ultrix (fix from iversen@dsfys1.fi.uib.no)
+ - added automatic group registration
+ - fixed bug in registration code
+ - made nmbd work better with WfWg, and probably others
+ - updated the man pages to include the new nmbd options.
+ - minor updates to the README
+ - fixed double log_out() in send_packet().
+ - fixed bug in smbclient so that "dir" didn't work correctly
+ with pathworks
+ - possibly fixed bug in server that led to "abort retry ignore" from
+ pathworks client when doing a "dir".
+ - changed behaviour of smbclient login slightly, to try a
+ blank password in SMBtcon if the right password fails, and a
+ session setup has succeeded. Some clients seem to use a blank
+ one if a session setup has succeeded.
+ - ISC patches from imb@asstdc.scgt.oz.au
+ - the client now tries to do name registration using a unicast.
+ Let me know if this helps anyone.
+ - tried to add a "contributed" line to each OS in the Makefile.
+
+1.5.39 18/2/94
+ - fixed silly C code that only worked with some compilers
+ - fixed another silly bug in nameserv.c that caused it to seg fault
+
+1.5.40 21/2/94
+ - removed the from (IP) message so people don't worry about 0.0.0.0,
+ it's redundant anyway.
+ - changed the client so the crypt key isn't printed
+ - changed the structure of switch_message() to use a list of functions.
+ This improves the debug info.
+ - made SMBopen ignore supplied attribute as per X/Open spec
+ - made SMBopen fail if file doesn't exist in all cases. Let me know
+ if this breaks something. It is implied in the X/Open spec. This
+ fixes the pkzip bug.
+ - added dptr_demote() to replace dptr_close() to try and fix
+ pathworks dir bug. This has the potential disadvantage of
+ leaving lots of open file descriptors.
+ - changed mask_match to disallow two .s in a name
+
+1.5.41 2/3/94
+ - added "dfree command" global option to smbd to support an
+ external "disk free" executable (typically a script). This gets
+ around the problem of getting disk free info reliably on lots
+ of systems.
+ - added ffirst and fclose to client
+ - simple SYSVR4 patch from mark@scot1.ucsalf.ac.uk
+ - added better uid/gid reporting for debugging purposes
+ - several changes to the logon procedure for the client, so hopefully
+ it will connect correctly to a wider range of servers.
+ - server should no longer crash if it can't open the debug
+ file (thanks to MGK@newton.npl.co.uk)
+ - added the THANKS file.
+
+1.5.42 6/3/94
+ - lots of changes from Jeremy Allison, implementing more of
+ the LANMAN1.0 protocol, and fixing a few bugs.
+ - fixed delete bug, so hopefully wildcards are correct now
+ - pcap changes from Martin Kiff so non-aliased printers in
+ /etc/printcap are recognised
+ - wrote announce file ready for 1.6
+ - re-wrote browse code in client (still doesn't work)
+ - updates to man-pages from Karl Auer
+ - made raw packet dumps mode 0600 and only if -dA is given
+ - changed socket code to use utility functions in util.c
+
+1.6.00 17/3/94
+ - made server always return to original directory (rather than /)
+ - fixed bug in params.c that caused a seg fault if no parms in a
+ section
+ - minor clean ups for clean compile under solaris
+ - solaris fix for running from inetd from Karl Auer
+ - fixes for dfree() under solaris
+ - minor changes that might help BSDI
+ - changes to the Makefile, manual-pages and sample config file from
+ Karl Auer
+ - fixed dfree for Ultrix
+
+1.6.01 19/3/94
+ - fixed setatr bug that allowed directories to be unusable
+
+1.6.02 27/3/94
+ - added timestamps to connection message in log
+ - added idle timeout of 10 minutes to name server
+ - made HAVE_SYSCONF==0 the default in includes.h
+ - made the client not register by default
+ - ISC patches from imb@asstdc.scgt.oz.au
+ - GetWd() cache code from Martin Kiff
+ - rewrote the locking code in terms of fcntl() calls.
+ - fixed "can't delete directory" bug
+ - added code to close old dirptrs for duplicate searches
+ - removed exchange_uids() and the access() call and replaced them.
+
+1.6.03 28/3/94
+ - tried to clean up the time handling a little (local vs gmt time)
+ - added debug level global to server config
+ - added protocol level global to server config
+ - added SMBecho command to server
+ - included Karl Auers SMBGuide in the distribution.
+
+1.6.04 31/3/94
+ - fixed time zeroing bug in smb_close and smb_setatr
+ - re-wrote the username/password handling to be more flexible
+ - added "guest only" service setting to smb.conf
+ - updated man pages for new username/password handling
+ - fixed parse bug in reply_tconX
+ - improved error return code from tcon
+ - several changes to fix printing from WfWg
+
+1.6.05 2/4/94
+ - changed the name of the whole package to Samba
+ - removed SMBexit call from client to stop exiting error message
+ - added interpret_addr() call to replace inet_addr() so
+ a hostname can be used whenever a IP is required
+
+1.6.06 8/4/94
+ - added random tid choice to reduce problem of clients not
+ detecting a server disconnection.
+ - made client not report spurious time from CORE or COREPLUS server.
+ - minor HPUX fix from gunjkoa@dep.sa.gov.au
+ - turned off GETWD_CACHE until we track down a minor bug in it
+
+1.6.07: 10/4/94
+ - added helpful error messages to connection failure in client.
+ - fixed problem with mput in client
+ - changed server to allow guest-only sesssetup messages with any
+ password. Control this with GUEST_SESSION_SETUP in local.h.
+ - minor change to session setup handling in make_connection()
+ - added check for right number of \s in the client.
+ - made the server not exit on last close if the deadtime is != 0
+ - added malloc and realloc wrappers. enable them with -DWRAP_MALLOC=1
+ - if smbd is started with a debug level of 10 or greater it creates
+ a log file ending in the process number
+
+1.6.08: 18/4/94
+ - updated the THANKS file
+ - changes from marcel@fanout.et.tudelft.nl (Marcel Mol) for AMPM
+ times and error report on connect().
+ - made the get_myname() routine discard any part after the first '.'
+ - added a wrapper for free from Martin Kiff
+ - added simpleminded code to handle trapdoor uid systems (untested)
+ - added Martin Kiffs "paranoid" getwd code.
+ - added default MAXPATHLEN if undefined of 1024
+ - made get_broadcast() continue to get netmask if it can't get
+ broadcast (suggestion from Hannu Martikk)
+ - replaced fchmod() calls with chmod() to satisfy some unixes
+
+
+
+1.6.09: 4/5/94
+ - changed perror() calls to strerror() in server.c
+ - fix for dfree on OSF1 from
+ Maximilian Errath (errath@balu.kfunigraz.ac.at)
+ - fixed server time reporting for protocol >= LANMAN1
+ - fixed TimeDiff() for machines without TIMEZONE or TIMELOCAL
+ (thanks to Vesa S{rkel{ <vesku@rankki.kcl.fi>)
+ - added SYSV defs to AIX and HPUX to fix "memory" problem
+ (actually a signal problem).
+ - added version to client banner in log file
+ - Ultrix patches from Vesa S{rkel{ <vesku@rankki.kcl.fi>
+ - added ! command to client for executing shell commands
+ - fixed ERRnofids bug in server
+ - fixed name_equal bug
+ (thanks to cjkiick@flinx.b11.ingr.com (Chris Kiick))
+ - wrapped gethostbyname() with Get_Hostbyname() to prevent
+ case sensitive problems on name lookups
+ - limit printer tmp filename to 14 chars
+ (from Paul Thomas Mahoney <ptm@xact1.xact.com>)
+ - added ability to understand 64 bit file times
+ (thanks to davidb@ndl.co.uk (David Boreham))
+ - added Gwt_Pwnam() wrapper to cover server case-sensitivity
+ problems (suggestion from J.M.OConnor@massey.ac.nz (John O'Connor))
+ - changed the setuid() calls to try and work for more systems
+ without breaking the ones it currently works for
+ - added version number to usage()
+ (suggestion from peter@prospect.anprod.csiro.au)
+ - added "security=" option for share or user level security
+ - allowed multiple usernames in "user=" field
+ - changed display method for recursive dorectory listings
+ - switched client to use long filenames where supported
+ - added speed reporting to client transfers
+ - several NT fixes to server from jra@vantive.com (Jeremy Allison)
+ - ISC fixes from ptm@xact.demon.co.uk (Paul Mahoney)
+ - fix to README from grif@cs.ucr.edu (Michael A. Griffith)
+ - default netmask and broadcast from Ian A Young <iay@threel.co.uk>
+ - changed default of is_locked() on fcntl() error.
+ - fixed bug in read_with_timeout() that could cause a runaway
+ smbd process.
+ - fixed findnext bug for long filenames in client
+ - changed default protocol level to LANMAN1
+ - change default reported security level to SHARE.
+ - changed password_ok() so that if pwdauth() fails it tries
+ with standard crypt.
+ - added "translate" command to the client to do CR/LF translation
+ for printing, and add a form feed at the end.
+ (thanks to mh2620@sarek.sbc.com (Mark A. Horstman ) )
+ - added "locking=yes/no" toggle for each service
+ - SCO unix patches from Heinz Mauelshagen (mauelsha@ez.da.telekom.de)
+
+1.6.10: 7/5/94
+ - fixed important bug in readbraw/writebraw
+ - added -A option to client
+ - fixed delete bug on long filenames (untested). Thanks to
+ Stefan Wessels <SWESSELS@dos-lan.cs.up.ac.za>
+ - neatened up the byte swapping code
+
+1.6.11: 3/6/94
+ - fixed bug in client in receive_trans2_response() that caused
+ some strange behaviour with LANMAN2.
+ - fixed some offset/alignment problems with lockingX (thanks to
+ Jeremy Allison)
+ - allow locking on O_RDONLY files. Thanks to Martin N Dey <mnd@netmgrs.co.uk>
+ - fixed del bug in client thanks to paulzn@olivetti.nl (Paul van der Zwan)
+ - fixed multiple user= bug thanks to MDGrosen@spectron.COM (Mark Grosen)
+ - added translate ability for all files. Thanks to mh2620@sarek.sbc.com (Mark A. Horstman )
+ - mask out negative lock offsets. Thanks to bgm@atml.co.uk (Barry G Merrick)
+ - more attempts to get the structure alignment better for some machines
+ - cleaned up the machine dependencies a little
+ - ISC fixes from Paul Thomas Mahoney <ptm@xact1.xact.com>
+ - enabled printing with a SMBclose and SMBwrite for NT
+ thanks to jkf@frisky.Franz.COM (Sean Foderaro)
+ - SGI changes from Michael Chua <lpc@solomon.technet.sg>
+ - CLIX patches from cjkiick@ingr.com
+ - NEXT2 and NEXT3_0 patches from Brad Greer (brad@cac.washington.edu)
+ - BSDI changes from tomh@metrics.com (Tom Haapanen)
+ - SCO patches from John Owens (john@micros.com)
+ - fix psz bug in pcap.c (thanks to Karl Auer)
+ - added widelinks option (global and per service). Suggestion from
+ Karl Auer. Defaults to True.
+ - made locking able to be global or local (default is give by global)
+ - added check_name() to dir listings
+ - added "packet size" option to globals. default to 32767. This
+ "fixes" a WfWg bug (thanks to Karl Auer)
+ - fixes for getattrE and setattrE and minor fix in util.c from Jeremy Allison.
+ - Karl updated the man pages o be current
+ - disabled writebraw and readbraw until a possible bug can be investigated further
+
+1.7.00: 14/7/94
+ - added session_users list, to overcome problem of missing usernames in SMBTconX.
+ - added term support to the client
+ - added "default service"
+ - fork for print so user is not root
+ - added name mangling to 8.3 (rudimentary)
+ - fixed bug in in_group()
+ - changed to use gid in place of egid
+ - fixed client connection to OS/2 (1.3 + lanman2.2) and long filenames
+ - added patches from mcochran@wellfeet.com (Marc Cochran)
+ these implement scope ids and fix some udp bugs. It means
+ the -L option to nmbd now works.
+ - made nmbd respond to incoming port rather than only 137
+ - made wide links refuse .. components
+ - fixed "dir foo." bug to stop it showing "foo.???"
+ - improved name mangling (added stack)
+ - added valid FNUM check to most calls
+ - fixed important do_put bug in the client
+ - added magic scripts to the server
+ - re-enabled getwd_cache code
+ - added optional agressive password checking
+ - removed dptr_closepath from SMBsearch to try and stop "dos for loop"
+ bug
+ - DGUX patches from ross@augie.insci.com (ross andrus)
+ - updated the README and THANKS file.
+ - added node status request to -L option of nmbd
+ - stripped trailing spaces in mask_match() (thanks to mike hench hench@cae.uwm.edu)
+ - added COREPLUS style print queue reporting and "lpq command"
+ in globals.
+ - cleaned up date handling and fixed byte order dependancy on dates
+ in SMBgetattrE.
+ - cleaned up the password handling and added "password level" with
+ the possability of checking all case combinations up to N upper
+ case chars.
+ - changed to use recvfrom only on udp ports (fixed read raw!)
+ - added TCB password support for SCO (thanks to lance@fox.com)
+ - updated README, THANKS and announce files.
+ - fixed timezone reporting to be signed (thanks to noses@oink.rhein.de)
+ - disabled max packet as it could cause problems with WfWg (no longer
+ needed now readraw is "fixed")
+ - changed from creat() to open() in mktemp and mknew.
+ - changed umask handling
+ - sped up nmbd by making it cache names
+ - changed idle timeout on nmbd to 2 mins
+ - Netbsd changes from noses@oink.rhein.de
+ - released alpha2
+ - added name timeout to nmbd
+ - changed bind port retry in nmbd
+ - added Limitations sections to README
+ - fixed two . in is_83()
+ - fixed compilations warnings in util.c (thanks to njw@cpsg.com.au)
+ - made [homes] honour multiple user list
+ - fixed mask match bug introduced in alpha1
+ - added "mangled stack" option for stack size
+ - added mangled stack promotion
+ - released alpha3
+ - netbsd-1.0 fix for statfs().
+ - added null_string to util.c to reduce memory usage
+ - changed the way directory structures are put together
+ - added smbrun for system() requests
+ - changed maxmux to 0 in hope of avoiding mpx commands problem
+ - fixed zero response length for session keepalives
+ - removed called name from session users list
+ - added F_RDLCK support to try and handle locks on readonly files
+ - made directory creation honour the lowercase flag in client (thanks
+ to charlie@edina.demon.co.uk)
+ - made checksum for mangling independant of extension if extension is
+ lowercase
+ - added ability to rename files with different extension, preserving
+ root name
+ - released alpha4
+ - better command line error checking in client
+ - changed all debug statements to new format
+ - fixed delete error code reporting
+ - released alpha5
+ - added mangled name support to wildcard delete in server
+ - fixed mask bug in SMBsearch
+ - cleaned up prototypes
+ - released alpha6
+ - fixed important bug in session_setup which made WfWg freeze
+ (maxmux was 0 - this bug was introduced in alpha4)
+ - released alpha7
+ - two printing bug fixes thanks to bgm@atml.co.uk (Barry G Merrick)
+ - uid fix to smbrun (thanks to larry@witch.mitra.com)
+ - man page updates from Karl Auer
+ - FAQ file from Karl Auer
+ - released alpha8
+ - fixed read-only flag in dos_mode() for non writeable services
+ - fixed error code reporting in open() and openX().
+ - minor secureware fix from (thanks to lance@fox.com)
+ - released alpha9
+ - casting cleanups for memcpy().
+ - cleaned up error code names to be more consistant
+
+1.7.01: 17/7/94
+ - minor man page fix from baeder@cadence.com (Scott Baeder)
+ - changed usage() error message in client
+ - made nmbd not exit if can't register own name
+ - made nmbd only register if running as a daemon
+ - fixed stdout problem in smbrun by closing stdin/stdout/stderr
+ - minor fix to lmhosts parsing
+
+
+1.7.02: 20/7/94
+ - made nmbd not call get_broadcast if both -B and -N are used (thanks
+ to Chris Woodrow <Chris.Woodrow@actrix.gen.nz>)
+ - disabled GETWD_CACHE again
+ - fixed INCLUDES list in Makefile to add version.h (thanks to
+ jimw@PE-Nelson.COM (Jim Watt))
+ - made checkname do a become user if it hasn't already done so.
+ - added consistancy check to become_user().
+ - removed mask extension expansion from SMBsearch
+ - small change to chkpth
+ - fix to snum select for lpq status (thanks to Rafi Sadowsky
+ rafi@tavor.openu.ac.il)
+ - changed daemon to is_daemon for NetBSD (thanks to noses@oink.rhein.de)
+ - removed STAFS3 stuff for NETBSD_1_0
+
+
+1.7.03: 29/7/94
+ - updated docs for new distribution structure
+ - made getatr return 0 size for directories (thanks to Bernd Esser
+ esser@pib1.physik.uni-bonn.de)
+ - added valid dos filename checks from Stefan Wessels
+ (swessels@cs.up.ac.za)
+ - added trimming of . in hostnames to -S mode of nmbd
+ - removed become_user() and OPEN_CNUM calls. Now make them
+ in switch_message instead which simplifies a lot of code.
+ - added GETFNUM macro to make chain_fnum more consistant and
+ reliable.
+ - added flags to protocol structures to simplify CAN_WRITE and AS_USER
+ checking
+ - added getwd cache boolean option to globals
+ - added fclose() to lpq status routine thanks to
+ dgb900@durras.anu.edu.au (David Baldwin)
+ - added "only user" option, to limit connection usernames to those
+ in the user= line
+ - changed to badpath from badfile in chkpath despite specs (following
+ what WfWg does). This fixes "file not found" error in copy command.
+ Thanks to rwa@aber.ac.uk for pointing out the bug
+ - changes for apollo from Stephen C. Steel <steve@qv3donald.LeidenUniv.nl>
+ - more changes for Apollo from jmi@csd.cri.dk (John Mills)
+ - released alpha release
+ - added FTRUNCATE_CAN_EXTEND=0 as default to fix problem with word6.
+ Possibly not needed on many OSes? Thanks to Charlie Hussey
+ charlie@edina.demon.co.uk
+ - started adding max connections code
+ - much improved group handling contributed by
+ Ian Heath (ih@ecs.soton.ac.uk)
+
+1.7.04: 29/7/94
+ - fixed one line bug in SMBopenX that got error code wrong.
+
+1.7.05: 2/8/94
+ - added UNIXERROR() macro to get error code from unix errno.
+ - fixed lpq status for MSTCPB3
+ - added @ option for user= line to lookup groups in group file
+ - added become_user optimisation and process timeout (thanks to
+ Jeanette Pauline Middelink (middelin@calvin.iaf.nl)
+ - added malloc optimisation in readbraw
+ - released alpha
+ - patches for OSF1 enhanced security from Udo Linauer <ul@eacpc4.tuwien.ac.at>
+ - made level 2 a more useful debug level (less guff)
+ - added "max connections" and "lock dir" options to allow
+ limiting of the number of connections to a service at one time.
+ - released alpha2
+ - updated man pages
+ - released alpha3
+ - added read prediction code for better read performance
+ - released alpha4
+ - minor tuning to receive_smb()
+ - changed the order of mangled stack checking
+ - bug fix in read_predict().
+ - released alpha5
+ - minor search optimisation
+ - fixed keep alive bug in writebraw and in readbraw in the client
+ - released alpha6
+ - disabled writeraw by default pending a bug fix
+ - added profiling code (off by default)
+ - minor delete tuning
+
+
+1.7.06: 4/8/94
+ - OSF1 crypt fix thanks to Udo Linauer <ul@eacpc4.tuwien.ac.at>
+ - ifdef'd EDQUOT in case you don't have it (thanks to Paul Blackman <ictinus@Lake.canberra.edu.au>)
+ - tidied up UNIXERROR stuff to work on more systems.
+ - made Makefile more sophisticated and added "make revert"
+
+1.7.07: 4/8/94
+ - fixed one line fatal bug in receive_smb. Thanks to bruce@pixar.com
+
+1.7.08: 2/9/94
+ - initgroups call for SCO from lance@fox.com
+ - code cleanups from cap@isac.hces.com (Simon Casady)
+ - use full pathname in print command construction
+ - ISC includes fix from Martin Tomes <mt00@ecl.etherm.co.uk>
+ - added GID_TYPE define to cope with ultrix. Thanks to
+ brad@cac.washington.edu
+ - added umask call to main in server
+ - fixed several minor problems with the max connections
+ code. Thanks to lehmann@klizix.mpi-stuttgart.mpg.de (Arno Lehmann).
+ - fixed filetime in writeclose. Thanks to Andreas Bahrdt
+ <100321.2431@compuserve.com>
+ - df fix for large disks from Andreas Bahrdt
+ - getpwanam support from horn@mickey.jsc.nasa.gov
+ - clean name change from Bernd Esser
+ <be@syli30.physik.uni-bonn.de>
+ - released alpha1
+ - more locking changes to fix Excel problem
+ - released alpha3
+ - another minor locking change
+ - smarter masking in the locking code. Excel now apparently works.
+ - minor FAQ updates
+ - changed max connections refusal error to access denied.
+ - added queue command to client to show the print queue
+ - changed some print queue reporting stuff
+
+1.8.0: 14/10/94
+ - added international chars to valid_dos_char(). Thanks
+ to Daniel.Grandjean@dgr.epfl.ch
+ - volume label fix
+ - released alpha1
+ - important off by 4 fix in the server
+ - readbraw size adaption in the client
+ - released alpha2
+ - wait3 cast for NeXt fixed. Thanks to dbrandon@politics.tamu.edu.
+ - man page fix for max xmit. Thanks to mmoore@wexford (Mike Moore)
+ - is_8_3() fixes from Jochen Roderburg <Roderburg@rrz.Uni-Koeln.DE>
+ - list_match() fix from jkf@soton.ac.uk
+ - statfs3 fix for BSDI from dan@supra.com
+ - changed file open/close/read in server in preparation for mmap()
+ based IO.
+ - added mmap() support for reading files in the server. Optional
+ at compile time. Thanks to suggestion from Roger Binns <rogerb@x.co.uk>
+ - mmap bug fixes
+ - added __SAMBA__ name in nmbd
+ - major changes for support of lanman2 and long filenames from
+ Jeremy Allison (jeremy@netcom.com)
+ - lseek optimisation. Thanks to Linus Torvalds.
+ - released alpha4
+ - date patches for lanman2 from Jeremy Allison
+ - added protocol aliases to handle WfWg (untested)
+ - allow for zero params or data in reply_trans2
+ - small lanman2 patches from jeremy
+ - more prototype additions for clean compilation
+ - postscript patches from tim@fsg.com
+ - more lanman2 patches from Jeremy
+ - added null ioctl support
+ - kanji patches from fujita@ainix.isac.co.jp
+ - released alpha6
+ - disallowed null password access (thanks to Birger Kraegelin krg@iitb.fhg.de)
+ - Makefile fix for ultrix from andrew@d2bsys.demon.co.uk (Andrew Stirling)
+ - added per-service mangled names
+ - totally re-vamped loadparm.c
+ - added "mangling char" parameter
+ - released alpha7
+ - added "default case = lower|upper" service option
+ - change mangling char to a service parameter
+ - ultrix enhanced security patch from steven@gopher.dosli.govt.nz
+ - more changes to loadparm.c
+ - printer name always set in [printers]
+ - string_free() fix thanks to jef_iwaniw@pts.mot.com
+ - changed group handling to be faster and work for large numbers
+ of groups
+ - added dynamic gid_t type determination
+ - released alpha8
+ - fixed become_user() problem for services with invalid
+ directories
+ - added "invalid users" list on per service basis
+ - fixed pointer problems in alpha8 (thanks to murnaghant@a1uproar.yuppy.rdgmts.MTS.dec.com)
+ - fixed some date setting problems
+ - trans2 fixes from jeremy to stop infinite directory listings of
+ long filenames
+ - "standard input" lpq patch from root@tlspu.demon.co.uk (Adrian Hungate)
+ - changed password checking to check session list and validated ids
+ before user list
+ - split off password functions into password.c
+ - added hosts equiv and rhosts code (thanks to Tim Murnaghan <murnaghant@a1uproar.yuppy.hhl.MTS.dec.com>)
+ - released alpha11
+ - added "newer" command to the client
+ - attempt at aix trapdoor uid workaround
+ - released alpha12
+ - minor trans2 bugfix
+ - added ufc crypt (fast crypt) support. Thanks to suggestion from
+ forrest d whitcher <fw@world.std.com>
+ - socket() fix for getting bcast and netmask thanks to
+ Brian.Onn@Canada.Sun.COM
+ - added beginnings of IPC and named pipe support in the server
+ - changed file structure a bit, creating reply.c
+ - finished print queue support for lanman1
+ - changed default protocol to LANMAN2
+ - released alpha13
+ - logged IPC connects at a higher debug level
+ - added netgroup support to hosts equiv search
+ - disallowed root access though hosts.equiv (thanks to Colin.Dean@Smallworld.co.uk)
+ - kanji and password handling fixes from fujita@ainix.isac.co.jp
+ - several bug fixes for lanman and other things from
+ esser@pib1.physik.uni-bonn.de (Bernd Esser)
+ - updated man pages, README and announce files.
+ - released 1.8.00alpha1
+ - reply_close() time change fix from Andreas Bahrdt <100321.2431@compuserve.com>
+ - added valid users list to compliment invalid users list.
+ - aix fixes from tomc@osi.curtin.edu.au (Tom Crawley)
+ - changed testparm output format
+ - support for getting time from the server (nearly untested)
+ - fixed device type error for wild device ????
+ - fixed groups problem when in 0 groups
+ - more IPC fixups
+ - added support for "net view \\server" command to list
+ available services (like browsing)
+ - released 1.8.00alpha2
+ - changed port choice for nmbd -L
+ - added -L option to client to view share list on a host
+ - bug fixes for NetShareEnum code
+ - added "server string" option
+ - changed default print file name to include remote machine name.
+ - added hooks for browsing in nmbd
+ - added browsing to nmbd
+ - freebsd fixed from Steve Sims SimsS@Infi.Net
+ - got rid of tell()
+ - added subnet browsing with the S option in lmhosts
+ - made smbd prime nmbd with a 1 byte dgram
+ - added REUSADDR to open_socket_in() thanks to peter@ifm.liu.se
+
+
+1.8.01: 18/10/94
+
+ - auto add group "LANGROUP" if no group specified in nmbd
+ - made nmbd more responsive at startup
+ - lots of cleanups and consistancy checks
+ - added -C option to nmbd to set "machine comment".
+ - fixed postscript option
+ - force print_file in print_open()
+ - restructured the browsing a little
+ - casesignames fix for lanman-dos
+ - auto-load home directory from session setup
+ - changed to StrnCpy() for safety
+ - fixed "out of file descriptors" bug in the client (a WfWg bug?)
+
+
+1.8.02: 22/10/94
+ - fixed uppercase username problem
+ - added "hide dot files" option
+ - changed auto debug log in nmbd
+ - added LMHOSTS to Makefile
+ - added M flag in lmhosts to specify own netbios name
+ - added "load printers" option to auto-load all printers
+ - substitution of %p in lpq command
+ - substitution of %h and %v in server string and -C option of
+ nmbd
+ - string substitions substitute all occurances of a pattern
+ - added casesignames global option
+ - fix for man pages thanks to David Gardiner <dgardine@cssip.edu.au>
+ - changed debug options a bit
+ - added default for lpq command and lpr command
+ - changed default shell path to /bin/sh
+ - forced lpq under api to run as root - should speed things up
+ - added "group" option to force group of a connection
+ - added "read list" and "write list" options
+ - added max mux option - seems to fix NT browsing?
+ - added "mangled map" option thanks to
+ Martin.Tomes@uk.co.eurotherm.controls
+ - separated mangling functions into mangle.c
+ - allowed all dos chars in mangled names
+ - apollo changes from Helmut Buchsbaum <buc@eze22.siemens.co.at>
+ - password changing code from Bob Nance <Bob.Nance@niehs.nih.gov>
+ it doesn't quite work yet, but it's a start (disabled by default)
+
+
+1.8.03: 25/10/94
+ - made auto loaded services browsable as per default service
+ so you can hide homes but keep home directories.
+ - changed check_name() to handle "direct to network" printing
+ - auto 3 minute deadtime if all connections are closed. This
+ prevents restart when polling the print queue.
+ - fix for newer command in client from Rich-Hoesly@uai.com
+ - changed connection recording method
+ - added the program smbstatus
+ - changed timeout mechanism
+ - "null passwords" option from Pim Zandbergen <pim@cti-software.nl>
+ - made new files with casesignames=False set their case to the default
+ case.
+ - fixed problem of uppercasing first letter of printers in printcap
+ - debug level fixes in trans2 from jimw@PE-Nelson.COM (Jim Watt)
+ - made null printer default to lp
+
+1.8.04: 27/10/94
+ - added OS2.txt from riiber@oslonett.no
+ - another "auto services" fix. A silly strtok() bug :-(
+ - fixed the status locking and max connections (broken in 1.8.03)
+ - released alpha1
+ - added gets_slash so lines can be continued in smb.conf and
+ lmhosts
+ - browse list bugfix
+ - default to "load printers=yes"
+ - rewrote pcap.c
+ - intergraph bugfix from tarjeij@ulrik.uio.no
+ - changed properties flags in nmbd (to fix NT print browsing)
+ - allowed very long lines in printcap parsing.
+
+1.8.05: 28/10/94
+ - lanman2 fix from Jeremy
+
+1.9.00: 22/1/95
+ - only add home if not already there.
+ - added ulogoffX support
+ - PTR_DIFF() cleanups
+ - fixed a bug that caused STATUS..LCK to grow very large
+ - changed mangling to handle names ending in . a little better
+ - added "strip dot" option
+ - SGI and setgroups() fix from bill@sg25.npt.nuwc.navy.mil
+ - fixed password preservation in password_ok() (again?)
+ - unink fix from emer@vssad.enet.dec.com (Joel S. Emer)
+ - changed username part of spool filename to max 10 chars (from 6)
+ - magic script fix from beverly@datacube.com (Beverly Brown)
+ - reply_special() fix from Peter Brouwer <pb@apd.dec.com>
+ - stopped nmbd from listening on 138. It didn't seem to help much.
+ - clix fixes from ttj@sknsws61.sjo.statkart.no
+ - fixed select behaviour under Linux
+ - man page fix from Robin Cutshaw <robin@intercore.com>
+ - ISC block size fix from ralf@rbsoft.sdata.de (Ralf Beck)
+ - ISC fixes from Martin.Tomes@controls.eurotherm.co.uk
+ - attrib bit fix in smbclient (pointed out by Rich-Hoesly@uai.com)
+ - japanese extensions from fujita@ainix.isac.co.jp (Takashi
+ Fujita) and ouki@gssm.otuska.tsukuba.ac.jp.
+ - SCO patches from Stephen.Rothwell@pd.necisa.oz.au
+ - changed the system commands to redirect stderr
+ - changed default printername to service name for all print ops
+ - added ability to delete print queue entries
+ - added warning if you try to print without -P in smbclient
+ - INTERACTIVE patches from cardinal@settimo.italtel.it
+ - patch to handle spaces in group names from GJC@vax1.village.com
+ (GEORGE J. CARRETTE)
+ - lockingX fix from stefank@esi.COM.AU (Stefan Kjellberg)
+ - some fairly radical changes to filename handling. We can now
+ handle mixed case filenames properly
+ - released alpha2
+ - added sysv printing support and improved bsd support
+ - changed the user that does print queues and lprm jobs
+ - return code support in the client from doylen@nbslib.isc-br.com (Doyle Nickless)
+ - added "strict locking" option. Defaults to no.
+
+ - added -I switch to nmbd
+ - fixed DEV bug thanks to Dirk.DeWachter@rug.ac.be
+ - use pw_encrypt() for shadow passords in Linux (from begemot@begemot.iko.kharkov.ua (Dmitry Gorodchanin))
+ - disabled read prediction by default
+ - added varient handling code to ipc.c for printQ and printDel.
+ - released alpha5
+ - AUX patches from root@dolphin.csudh.edu
+ - struct timeval fix from gkb1@york.ac.uk
+ - patches to merge ISC and INTERACTIVE from pim@cti-software.nl
+ - changed to "printing ="
+ - fixed problem with long print queues.
+ - fixed node status request in nmbd to go to non bcast
+ - made default path in services /tmp if not specified
+ - added %u in passwd program
+ - fixed up the password changing code for Linux
+ - no guest sess setup when user level security
+ - changed timeouts to kill dirptrs so cdroms can be unmounted
+ - added auto-reload of smb.conf if changed
+ - added SIGHUP to reload the config files
+ - added -M option to nmbd to search for a master browser
+ - added support for continue bit in trans2findnext
+ - changed to dynamic strings in some more structures
+ - changed default deadtime to 30 minutes
+ - cleaned up the memory swapping code a bit
+ - updated the man pages somewhat
+ - added %m and %u in the "path=" of services
+ - released alpha6
+ - simple testing and fixups for solaris, sunos, aix, ultrix and
+ osf/1 (this is all I have access to).
+ - fixed chdir bug
+ - added hashing to cnum selection
+ - released alpha7
+ - fixed printing bug
+ - reduced chance of "hung" smbd with dead client
+ - fixed do_match() bug (recently introduced)
+ - released alpha8
+ - nameserver fix from W.J.M.vGeest@et.tudelft.nl (W.J.M. van Geest)
+ - rewrote readbraw to try and overlap reads with writes
+ - client optimisations
+ - rewrote getwd cache and enabled it by default
+ - added partial smb packet reads (hopefully faster writes)
+ - added log file and log level options (with subs)
+ - added "read size" option
+ - tried setting some more socket options
+ - can use subs in "config file=" and will auto-reload
+ - added "include" options, with some subs
+ - finally got print manager working with NT
+ - auto-respond in nmbd to non-broadcast (auto WINS server, no -A
+ needed)
+ - released alpha10
+ - auto-delet unused services when reloading
+ - fixed auto-deletion
+ - fixed long names in printing
+ - fixed double loading of services file
+ - added printer file name support
+ - reformatted man pages for better www conversion
+ - renamed to 1.9.00.
+ - added support for RNetServerGetInfo and NetWkstaGetInfo API's
+ - updated the docs a bit
+ - released alpha1
+ - added -M -
+ - changed nmbd announce interval to 10 mins in outgoing packets
+ - hopefully fixed idle timeout reconnects
+ - strupper all command lines in nmbd
+ - added %a substitution for "remote architecture"
+ - added "Samba" protocol (same as lanman2)
+ - added "security = SERVER"
+ - released alpha2
+ - lowercase password fix
+ - fixed connect path length bug (thanks to JOHN YTSENG
+ <jtseng@cory.EECS.Berkeley.EDU>)
+ - added subs on "password server".
+ - fixed printing filename bug from smbclient
+ - disk quotas and hpux printing support from Dirk.DeWachter@rug.ac.be
+ - Makefile patches from pappinm@ayr_srv2.nth.dpi.qld.gov.au
+ - AFS patches from Mike Allard (mgrmja@nextwork.rose-hulman.edu)
+ - fixed grp name = server name problem
+ - man page updates from Charlie Brady (charlieb@budge.apana.org.au)
+ - fixed file search bug by adding "finished" flag
+ - added "max log size". Suggestion from Mark Hastings <mark.hastings@gain.com>
+ - released alpha3
+ - changed the read/write routines to handle partial read/writes
+ - released alpha4
+ - changed "guest account" to per-service
+ - changed so "guest ok" allows access to the guest account,
+ not the "user=" line
+ - changed default readsize to 2048
+ - try bind to 137 in nmbd if possible
+ - added server lookup to -L option in smbclient (gets list of servers)
+ - added -M switch to smbclient for sending winpopup messages
+ - released alpha5
+ - FAQ updates from Paul Blackman ictinus@lake.canberra.edu.au
+
+1.9.01: 23/1/95
+ - changed comment in print Q info to service rather than server comment
+ - fixed smbclient -L to NT when in user level security mode
+ - hopefully finally fixed NT print manager problems
+ - added informative messages during smbclient -M
+ - added node status replies to nmbd
+ - changed the lock offset fixup calculation to be more friendly
+ to dumb lockd daemons.
+ - added sigbus and sigsegv handlers to catch any silly errors and
+ print a message
+ - added message receipt to smbd and "message command =" option
+
+1.9.02: 25/1/95
+ - added argv/argc mangling for people who start the server the
+ wrong way.
+ - some man page updates
+ - added "revalidate" option
+ - added hosts allow/deny access check to messaging access
+ - added timeouts in the client
+ - added check for existance of smbrun binary
+ - man page updates from Colin.Dean@Smallworld.co.uk
+ - freebsd patches from dfr@render.com
+ - added mask sanity check in SMBsearch
+ - added more useful substitutions (%S, %P, %I and %T)
+ - added "exec =" option to execute commands on each connection
+
+1.9.03: 13/3/95
+ - added "socket options" option
+ - close base fd's (0,1 and 2)
+ - use dup(0) for inetd operation
+ - better detection of is_daemon
+ - hopefully finally fixed silly put bug that gave the wrong
+ date on files.
+ - fixed segv in readbraw bug
+ - added improved checing for invalid (or null) print file name
+ - several patches from ad@papyrus.hamburg.com (Andreas Degert)
+ - fixed slow logout bug in smbclient
+ - fixed automounter problems
+ - added subs on lock dir
+ - BSDI patch from John.Terpstra@Aquasoft.com.au
+ - added separate nmb and smb logfile entries in the Makefile
+ - fixed return code error in open calls
+ - added simple status display of printer in lpq parsing
+ - rewrote the directory handling to avoid seekdir (added dir.c)
+ - added uid=65535 check (thanks to grant@gear.torque.net)
+ - enhanced transfer_file() to add header (used in readbraw)
+ - reply_special bugfix from ferret@pc8871.seqeb.gov.au
+ - added HAVE_PATHCONF
+ - RiscIX patches from Jim Barry <jim@ilp.com> and
+ Charles Gay-Jones <charlie@ilp.com>
+ - CLIX patches from ttj@sknsws61.sjo.statkart.no
+ - fixed aix lpq parser from kvintus@acd.com
+ - added substitutions to "include="
+ - M88K_S3 patches from tonyb@plaza.ds.adp.com (Tony D. Birnseth)
+ - fixed mangled stack problem
+ - added code to handle broken readdir() setups on solaris
+ - initgroups() fix from jarit@to.icl.fi
+ - dgux dfree fix from listwork@cloud9.net
+ - dnix support from Peter Olsson <pol@leissner.se>
+ - getgrgid() patch from tpg@bailey.com (Tom Gall)
+ - Makefile patch from obrien@Sea.Legent.com (David O'Brien)
+ - password changing fixes from Dirk.DeWachter@rug.ac.be
+ - minor man page updates
+ - tried to enhance the read prediction code a little bit
+
+1.9.04: 16/3/95
+ - a bit better handling of global include lists
+ - fixed GSTRING bug in loadparm.c (affected "socket options =")
+ - fixed broken lpq parsing code (recent bug).
+ Thanks to Dirk.DeWachter@rug.ac.be
+
+1.9.05: 20/3/95
+ - improved mget in client to take multiple arguments and default
+ to *.*
+ - socket option fixes for both nmbd and smbd
+ - changed the byteorder handling scheme to be more portable (and
+ faster)
+ - lint cleanups from kast@kcs.planet.net (Robert Kast)
+ - added crude segv, sigbus and sighup recovery to nmbd
+ - rewrote lanman2_match to be closer to NT and WfWg behaviour
+ - Cray support from velo@sesun3.epfl.ch (Martin Ouwehand)
+ - "admin users" patch from Tim Leamy <tcleamy@ucdavis.edu>
+ - released alpha1
+ - added samba.7 man page
+ - no chdir when doing non AS_USER protocols
+ - become_guest() returns true in trapdoor uid system
+ - added more sophisticated segv/sigbus reporting (Linux only)
+ - released alpha2
+ - minor code cleanups (output of -Wall)
+ - smbprint fix from James Dryfoos <dryfoos@ll.mit.edu>
+ - improved testparm a little
+ - updated INSTALL.txt a little
+
+
+1.9.06: 21/3/95
+ - added %S substitution to users, valid users and invalid
+ users. This is useful for [homes].
+ - split off printing routines into printing.c and more dir
+ commands into dir.c
+ - postexec patch from jpm@gin.Mens.DE (Jan-Piet Mens)
+ - smbstatus updates from jpm@gin.Mens.DE (Jan-Piet Mens)
+ - reload sighup after use
+ - fixed name ptr offset bug
+ - added %f in print commands
+ - fixed byte ordering in nmbd which caused browsing to fail in
+ 1.9.05
+
+1.9.07: 22/3/95
+ - important directory listing fix
+ - allowed path= in [homes] section
+ - printer status patches from Dirk.DeWachter@rug.ac.be
+
+1.9.08: 24/3/95
+ - fixed . and .. in root dir for lanman2
+ - better default comment in [homes]
+ - added time stamping to directory entries
+ - check directory access at connection time
+ - rlimit code from loebach@homer.atria.com (Thomas M. Loebach)
+ - fixed home dir default comment
+ - totally rewrote dptr handling to overcome a persistant bug
+ - added [globals] as well as [global]
+
+1.9.09: 30/3/95
+ - fixed static string bug in nmbd
+ - better null password handling
+ - split CFLAGS in Makefile
+ - fixed typo in smbclient messaging
+ - made home dir not inherit path from [global]
+ - standard input printing patch from xiao@ic.ac.uk
+ - added O_CREAT to all print opens (bug in Win95)
+ - use /proc for process_exists under Linux and solaris
+ - fixed another segv problem in readbraw
+ - fixed volume label problem
+ - lots of changes to try and support the NT1 protocol
+ - released alpha1
+ - fixed session setup bug with NT in NT1 protocol
+ - released alpha2
+ - fixed "get" bug in smbclient that affected NT3.5
+ - added SO_KEEPALIVE as a default socket option in smbd
+ - changed some error codes to match those that NT 3.5 produces
+ - updated trans2 with some new calls for Win95 and WinNT (better
+ long file support)
+ - released alpha3
+ - fixed "nmbd -D -b" timeouts
+ - added IS_LONG_NAME flag to getattr in NT1
+ - added the NT qfileinfo trans2 commands
+ - merged qpathinfo with qfileinfo
+ - changed idling technique to try and be more friendly to
+ clients
+ - merged setfileinfo with setpathinfo and updated them with the NT fns
+ - improved read prediction a lot
+ - added read prediction to readbraw
+ - improved fault reporting (last packet dump)
+
+1.9.10: 30/3/95
+ - fixed read prediction+readbraw bug for read/write files
+
+1.9.11: 9/4/95
+ - fixed trans2 qpathinfo bug
+ - fixed bug with % in service name when doing print queue requests
+ - default readsize now 16K
+ - minor read prediction changes
+ - fixed status initialisation in print queue reporting
+ - fixed const compile problem for hpux
+ - minor SMBread fix from Volker Lendecke <lendecke@namu01.gwdg.de>
+ - removed space after -P in print commands (for fussy systems)
+ - disabled level2 of setfilepathinfo
+ - changed to a single read dir model, saving all dir names in
+ the Dir structure
+ - disabled NT protocols in the client due to reported problems
+ - fixed QUERY_FS_VOLUME_INFO which caused Win95 to hang on drive
+ properties
+ - minor lseek bug fix
+ - fixed up keepalives
+ - new timezone handling code (hopefully better!)
+ from steve@qv3pluto.LeidenUniv.nl
+ - BSDI interface patch from jrb@csi.compuserve.com
+ - gettimeofday changes from Roger Binns <rogerb@x.co.uk>
+ - added smbrun option
+ - added "root preexec" and "root postexec" options
+
+1.9.12: 12/4/95
+ - hopefully fixed some recently introduced NT problems
+ - fixed a unlink error code problem
+ - minor testparm fix
+ - fixed silly error messages about comments in config files
+ - added "valid chars" option for other languages
+
+1.9.13: 28/4/95
+ - patches from David O'Brien (obrien@Sea.Legent.com) improving the
+ netgroup suport, and adding the "map archive" option, as well as
+ other minor cleanups.
+ - tried to add info level 3 and 4 support for OS/2
+ - default deadtime set to 0 as in docs
+ - cleaned up the trans2 code a little
+ - cleaned up the Makefile a little
+ - added charset.c and charset.h
+ - expanded "valid chars" option to handle case mapping
+ - lots of changes to try and get timezones right
+ - released alpha1
+ - win95 fixups
+ - released alpha2
+ - added %H substitution (gives home directory)
+ - nameserv.c cleanups and minor bug fixes
+ - redid the browse hook logic
+ - fixed daylight saving time offset for logfile messages
+ - added name cacheing to nmbd
+ - added send counts to node status in nmbd
+ - added STRICT_TIMEZONES compile time option (very computationally
+ expensive)
+ - removed the partial read code
+ - cleaned up the permission checking a lot
+ - added share modes (DENY_READ, DENY_WRITE, DENY_ALL, DENY_NONE,
+ DENY_FCB and DENY_DOS)
+ - added "share modes" option
+ - cleaned up the file open calls
+ - released alpha4
+ - fixed important one line bug in open_file()
+ - trans2 client fix from lendecke@namu01.gwdg.de
+ - netgroup patche from David O'Brien (obrien@Sea.Legent.com)
+ - case sensitive fix from lenneis@statrix2.wu-wien.ac.at (Joerg Lenneis)
+ - got long filenames working from Win95 dos prompt
+ - added "workgroup=" option
+ - added "username map" option including multiple maps, group maps etc
+ - fixed password server for NT1 protocol and made it more robust
+ - changed unix_mode() to add IWUSR to read-only directories. This
+ is much closer to what clients expect.
+ - added preservation of unused permission bits when a chmod() is
+ called from a client.
+ - made static those fns that could be
+ - fixed typo in access.c (thanks to Andrew J Cole
+ <A.J.Cole@cbl.leeds.ac.uk>)
+ - added %d substitution for process id
+ (thanks to lenneis@statrix2.wu-wien.ac.at (Joerg Lenneis))
+ - changed share error code to ERRbadshare
+ - added locked files list to smbstatus if share modes is enabled
+ - changed DENY_DOS to allow read by other tasks
+ - added shared_pending checks to server
+ - preserverd all possible permission bits during a chmod, and
+ fixed a trans2 chmod bug
+ - open /dev/null to use up first 3 fds, in an attempt to stop rogue
+ library routines from causing havoc
+ - fixed NT username problem when in server security
+ - added "force user" and "force group" options
+ - cleaned up some of the IPC calls a bit
+ - added writeraw to the client and cleaned up write raw in the server
+ - osf1 big-crypt bugfix from Udo Linauer <ul@eacpc4.tuwien.ac.at>
+ - hopefully better disk-full checking
+ - next uid bugfix from patrick@graphics.cornell.edu
+ - changed share modes so lock directory doesn't need to be world
+ writeable
+ - enabled write-raw by default
+ - added server_info() in client
+ - added level checks in some ipc calls
+ - added defines for the important timeouts in local.h
+ - added print queue deletion to smbclient (untested)
+ - removed the sysconf() calls
+ - optimised writebraw a bit
+ - fixed some file deletion problems
+ - added total_data check for extended attribs in trans2 (for OS/2)
+ - fixed broadcast reply bug in nmbd
+ - added careful core dumping code
+ - added faster password level searches (suggestion
+ by lydick@cvpsun104.csc.ti.com (Dan Lydick))
+
+
+1.9.14: 22/9/95
+ - fixed up level 3 and 4 trans2 requests for OS/2
+ - minor optimisations in a few places
+ - cleaned up the closing of low fds a bit
+ - added SO_REUSEADDR to socket as a daemon
+ - override aDIR bit for directories in dos_chmod()
+ - SGI5 fixes from ymd@biosym.com (Yuri Diomin)
+ - bsize sanity check and removed sunos force to 1k
+ - force the create mode to be at least 0700
+ - SCO and freebsd include changes from Peter Olsson
+ <pol@leissner.se>
+ - check with FQDN in access.c (thanks to Arne Ansper <arne@ioc.ee>)
+ - default broadcast for dnix from Peter Olsson <pol@leissner.se>
+ - solaris patches from Ronald Guilmette <rfg@segfault.us.com>
+ - added EXDEV handling
+ - small AFS Makefile patch from mgrlhc@nextwork.rose-hulman.edu
+ - hopefully fixed the Win95 dates to work in other than my
+ timezone
+ - attempted alignment fixups (to speed up memcpy)
+ - added some DCE/DFS support (thanks to Jim Doyle <doyle@oec.com>)
+ - added fix so that root doesn't have special privilages to open
+ readonly files for writing (but admin users do). This fixes the MS
+ office install problem.
+ - fixed trans2 response bug in client
+ - got dual names working for NT
+ - enabled lock_and_read in NT protocol
+ - added %L macro for "local machine"
+ - changed dfree reporting to use "sectors per unit"
+ - fixed "not enough memory" bug in MS print manger by limiting
+ share name length in share enum.
+ - "short preserve case" option from Rabin Ezra (rabin@acm.org)
+ - added archive option to client
+ - changed openX in client to be able to open hidden and system files
+ - added "sync always" option
+ - rewrote writebmpx and readbmpx
+ - added auto string_sub_basic to all loadparm strings
+ - lots of nmbd fixups (add registration, refresh etc)
+ - released alpha1
+ - added smbtar patches from Ricky Poulten (poultenr@logica.co.uk)
+ - added a lpq cache and the "lpq cache time" option
+ - released alpha 2
+ - sun includes fix from Kimmo Suominen <kim@deshaw.com>
+ - change nmbd -L lookup type to workstation from server
+ - added min print space option
+ - added user and group names to smbstatus (thanks to
+ davide.migliavacca@inferentia.it)
+ - fixed %f in print command bug (thanks to huver@amgraf.com)
+ - added wildcard support to SMBmv
+ - misc patches from David Elm (delm@hookup.net)
+ - changed default of "share modes" to yes
+ - changed default of "status" to yes
+ - aix qconfig parsing from Jean-Pierre.Boulard@univ-rennes1.fr
+ - more long_date fixups
+ - added wildcards to nmbd
+ - extensive changes to ipc.c and miscellaneous other changes
+ from ad@papyrus.hamburg.com (Andreas Degert). Should especially
+ help OS/2 users
+ - added name release to nmbd
+ - relesed alpha4
+ - fixed "SOLARIS" to SUNOS5 in Makefile
+ - several minor fixups to get it to compile on aix, osf1, ultrix,
+ solaris and sunos
+ - released alpha5
+ - minor bug fixes and cleanups in ipc.c
+ - fixed "only user" bug
+ - changed lpq to report guest queue entries as sesssetup_user to
+ allow for deletion by windows
+ - released alpha6
+ - added __SAMBA__ as type 0 in nmbd (was type 20)
+ - fixed null print job bug
+ - added 8 char warnings to testparm and smbclient
+ - changed to 8 char limit for names in pcap.c
+ - added linked list of config files to detect all date changes
+ that require a reload
+ - simplified pcap guessing heuristics
+ - added space trimming to the name mapping
+ - updated Get_Pwnam to add allow_change field for username mapping
+ - fixed MemMove bug (thanks to mass@tanner.com (Massimo
+ Sivilotti))
+ - released alpha7
+ - rewrote MemMove to be a little more efficient
+ - ipc va_arg bug fix from djg@tas.com (Dave Gesswein)
+ - added check for illegal chars in long filenames
+ - fixed name cache init bug in nmbd
+ - Convex patches from Victor Balashov <balashov@cv.jinr.dubna.su>
+ - timestring() bugfix from staale@spacetec.no
+ - changed %H to give path of forced user if one is set
+ - added quoting to smbclient to allow spaces in filenames
+ - convex and other patches from Ulrich Hahn
+ <ulrich.hahn@zdv.uni-tuebingen.de>
+ - released alpha8
+ - fixed rename directory bug
+ - nmbd wins fix from Maximilian Errath <errath@balu.kfunigraz.ac.at>
+ - client and AFS changes + password.c reorganisation + "more" and
+ "pwd" commands in client from Todd j. Derr (tjd@smi.med.pitt.edu)
+ - fixed several nmbd bugs
+ - released alpha9
+ - fixed another "cd" bug in smbclient
+ - password encryption from Jeremy Allison
+ - added "passwd chat" option and chat interpretation code
+ - added "smb passwd file" option
+ - released alpha10
+ - cleaned up chgpasswd.c a little
+ - portability changes to the encryption handling code
+ - added password encryption to smbclient
+ - fixed a share level security encryption bug
+ - added "ENCRYPTION.txt" document
+ - released alpha11
+ - added code to detect a password server loop
+ - fixed typo in chkpath in client.c that broken cd (again)
+ - LINUX_BIGCRYPT from marsj@ida.liu.se
+ - AFS password fixup from jbushey@primenet.com (Jeffrey G. Bushey)
+ - iso/8859-1 charcnv patches from Dan.Oscarsson@malmo.trab.se
+ - strtok/user_in_list fix from roderich@nodebonn.muc.bmw.de
+ - NETGROUP patches from J.W.Schilperoort@research.ptt.nl
+ - trim_string patch from J.W.Schilperoort@research.ptt.nl
+ - fixed problem with files with no extension getting mixed up
+ - ipc bugfix for print job deletion from Rainer Leberle <rleberle@auspex.de>
+ - released alpha12
+ - pwlen fix in NETGROUP from Andrew J Cole <A.J.Cole@cbl.leeds.ac.uk>
+ - lots of uid and encryption changes from Jeremy Allison. WinDD
+ should now work.
+ - released alpha13
+ - fixed max_xmit bug in client
+ - select fix in server (fixed critical drive errors under ISC)
+ - released alpha14
+ - wildcard fix from Jeremy
+ - changes to make IPC code more robust
+ - small select loop change to reduce cleaning of share files
+ - vtp, altos and mktime patches from Christian A. Lademann
+ <cal@zls.com>
+ - EEXIST bugfix in server.c
+ - changed mangled map to apply in all cases
+ - released alpha15
+ - fixed fcb open permissions (should mean apps know when a file is
+ read only)
+ - released alpha16
+ - client help formatting fix and docs fix from Peter Jones
+ <thanatos@drealm.org>
+ - added a directory cache
+ - use /proc whenever possible for pid detection
+ - TCSANOW fix in getsmbpasswd from roderich@nodebonn.muc.bmw.de
+ - fixed default printing mode for sysv systems
+ - make client always expand mask
+ - more minor IPC fixups
+ - pyramid makefile entry from jeffrey@itm.org
+ - client fixups for passlen, maxvcs and session redirect from
+ Charles Hoch <hoch@hplcgh.hpl.hp.com>
+ - finally fixed important IPC bug (varargs bug with int16)
+ - quota patches from Dirk.DeWachter@rug.ac.be
+ - print queue cache changes (per service) and print queue priority
+ additions from Dirk.DeWachter@rug.ac.be
+ - new japanese patches (incomplete) from
+ fujita@ainix.isac.co.jp (Takashi Fujita)
+ - moved a lot more functions into system.c via wrappers
+ - changed a lot of the connection refused error codes to be more
+ informative (or at least different)
+ - released alpha17
+ - changed error return code from cannor chdir() in make_connection
+ - fixed realloc() bug in printing.c
+ - fixed invalid username bug in sesssetupX
+ - released alpha18
+ - made default service change name to asked for service (idea
+ from Ian McEwan <ijm@doc.ic.ac.uk>)
+ - fixed "guest only" bug
+ - sambatar patches from Ricky
+ - printing.c patches from Dirk.DeWachter@rug.ac.be
+ - rewrote become_user()
+ - sunos5 patch from Niels.Baggesen@uni-c.dk
+ - more japanese extensions patches from fujita@ainix.isac.co.jp
+ - released alpha20
+ - added force_user to conn struct
+
+
+1.9.15: 14/11/95
+ - removed bcast override from workgroup announce in nmbd
+ - aix patch, added NO_SYSMOUNTH, from
+ lionel leston <102624.346@compuserve.com>
+ - quick fix in lp_string() to try and stop some core dumps
+ - added uid cache in connections structure
+ to make user level security faster
+ - changed dos_mode() to show read-only on read-only shares only if
+ user w bit not set
+ - added check to stop exit_server() looping
+ - core dump fix in string_sub()
+ - fix client bug for long dirs in NT1 mode.
+ Thanks to Erwin Authried (erwin@ws1.atv.tuwien.ac.at)
+ - switched to a safer (but probably slower) readbraw implementation
+ - released p1
+ - readbraw fix from Stefaan.Eeckels@eunet.lu
+ - fixed groups bug when user is in 1 group
+ - fixed NT1 dir bug
+ - changed default protocol in client to NT1
+ - changed trans2 to not return both names in long listing if long
+ name is 8.3
+ - made stat of "" return RONLY if not writeable drive
+ - wrapped strcpy() to stop nulls propogating (hack)
+ - made rename and unlink look at share locks on file
+ - clitar memory leak fix from jjm@jjm.com
+ - added -p option to smbstatus to list smbd processes
+ - added rename to the client
+ - released p2
+ - fixed SMBmv for case where the destination exists
+ - man page patch from michal@ellpspace.math.ualberta.ca (Michal Jaegermann)
+ - once again redid the time handling, but finally explained what
+ is going on, this is written up in TIME.txt. The "kludge-GMT" used
+ by NT is a bastard and led to a lot of the confusion
+ - kanji patch from fujita@ainix.isac.co.jp (Takashi Fujita)
+ - is08859-1 patches from eauth@mail.cso.co.at
+ - starting rewriting nmbd, new nmbd is nmbd2, old one still around
+ for time being
+ - released p3
+ - rewrote more of nmbd2 to use new structures
+ - CLIX patches from Jason.J.Faultless@bechtel.btx400.co.uk
+ - DirCacheFlush() bugfix from Michael Joosten
+ <joost@ori.cadlab.de>. This bug explains a lot of the crashes.
+ - fixed a bug in ChDir() that caused reversion to / in some
+ situations
+ - ipc fix from Magnus Hyllander <mhy@os.se>
+ - released p4
+ - smbpasswd fix from Jeremy
+ - compilation fixes from Magnus Hyllander <mhy@os.se>
+ - added NetServerEnum to ipc.c (needed for master browser stuff)
+ - Makefile fix from Gunther Mayer <gmayer@physik.uni-kl.de>
+ - cleanups for clean compile on several OSes
+ - added browse mastering code
+ - started integration with smb.conf for nmbd2
+ - released p5
+ - fixed death_time (should be t+ttl*3)
+ - fixed non-removal of dead servers
+ - added smbstatus -u patch from oskarh@spornet.is (Oskar Hannesson)
+ - NETGROUP fix from J.W.Schilperoort@research.kpn.com
+ - select and NO_SETGROUPS patches from lennylim@netcom.com (Lenny
+ Lim)
+ - added LINKS_READ_ONLY define in dos_mode() for LM/X
+ compatability
+ - "dir a.c" bug fixed thanks to roderich@nodebonn.muc.bmw.de
+ (Roderich Schupp)
+ - job cancel fix in client from peo@mtek.chalmers.se
+ - changed nmbd2 to nmbd
+ - fixed "dir a*" under trans2 lookups
+ - added StrnCaseCmp()
+ - updated docs a bit for new browsing stuff
+ - updated INSTALL.txt
+ - hopefully fixed server level security with WfWg
+
+1.9.15 (patches):
+ - major/minor fix for solaris from Jeroen Schipper
+ <Jeroen.Schipper@let.ruu.nl>
+ - fixed critical bug in directory listings
+ - released p1
+ - fixed one of the causes of "out of memory" while browsing
+ - fixed manpage install script (Paul Blackman)
+ - added DNS failures to name cache
+ - fixed writebmpx bug (affects OS/2)
+ - misc OS/2 fixes, mostly for EA handling
+ - added SMBcopy
+ - added "max ttl" option
+ - arch detection patch from Bas Laarhoven <bas@vimec.nl>
+ - released p2
+ - another OS/2 fix - the level 4 getpathinfo for EAs
+ - added "alternate permissions" option
+ - changed client to parse destination names into name + domain
+ - fixed problem with PrimaryGroup and lmhosts loading
+ - added domain master ability to nmbd
+ - added "domain master" option
+ - added "domain controller" option and code
+ - pwd fix to client from Erik Devriendt (de@te6.siemens.be)
+ - fixed problem in smbmv that led to ar not working in mks
+ - added transs2
+ - released p3
+ - updated email addresses
+ - fix for innetgr from Olaf Seibert (rhialto@polder.ubc.kun.nl)
+ - client translate fix from bandc@dircon.co.uk
+ - netbsd bcast fix from from Olaf Seibert (rhialto@polder.ubc.kun.nl)
+ - syslog code from Alex Nash <alex@fa.tca.com>
+ - strip dot fix from Arne Ansper <arne@ioc.ee>
+ - added addtosmbpass + man page from
+ michal@ellpspace.math.ualberta.ca (Michal Jaegermann)
+ - pcap fix for AIX from Jon Christiansen <jchristi@sctcorp.com>
+ - fixed servertype bug in remote announcements
+ - fixed up illegal name checks (should also be faster)
+ - kanji patches from fujita@ainix.isac.co.jp (Takashi Fujita)
+ - fixed bug handling non-encrypted passwords
+ - released p4
+ - fixed makefile for addtosmbpass
+ - DCE/DFS fixes from John Brezak (brezak@ch.hp.com)
+ - client patch for partial command matching from Andrew Wiseman
+ <bandc@dircon.co.uk>
+ - made is_8_3() handle full paths
+ - rewrote open_file_shared() with help from Charles Hoch
+ <hoch@hplcgh.hpl.hp.com>
+ - changed syslog to handle interactive programs
+ - fixed syslog problem with full path in argv[0]
+ - illegal name fixup for kanji from fujita@ainix.isac.co.jp
+ - fixed server level security to allow fallback to encryption
+ - changed reply_read() and reply_lockread() to ignore clients
+ smb_bufsize in order to handle broken lanman clients
+ - fixed NT wildcard problem with old style programs
+ - man page patches from "John M. Sellens"
+ <jmsellen@watdragon.uwaterloo.ca>
+ - partially documented the "character set" option
+ - changed default for MAXDIR to 64
+ - changed default DPTR idle time to 120
+ - released p5
+ - QNX patches from eldo@invisa.satlink.net (Eldo Loguzzo)
+ - made nmbd use the "max log size" option and changed log handling
+ code a bit
+ - sunos patches, remote protocol (%R) addition and arch detection
+ changes to stop compiler warning from Timothy Hunt <tim@fsg.com>
+ - fixed become_user() bug that led to incorrect permissions in
+ some situations.
+ - released p6
+ - is_8_3() fix from Charles Hoch <hoch@hplcgh.hpl.hp.com>
+ - nmblib bugfix from gmk@mhcnet.att.com (George Kull)
+ - aix pcap fix from Jon Christiansen <jchristi@sctcorp.com>
+ - added explicit sig_pipe() in server.c
+ - added domain logins option (not fully implemented)
+ - added HAVE_GMTOFF code
+ - got rid of PM_MAXLINE
+ - minor client fix from goggi@eflir (Garar Georg Nielsen)
+ - added SIGCLD_IGNORE for HPUX (from Tor Lillqvist
+ <tml@hemuli.tte.vtt.fi>)
+ - OSF/1 lpq patch from scooter@GENE.COM (Scooter Morris)
+ - NeXT patches from pmarcos@next.com (Paul Marcos)
+ - dstdiff patch to stop infinite loop from Erwin Authried (eauth@cso.co.at)
+ - password server option can now take a list of password servers
+ - patches to let samba run on OS/2 from Jason Rumney <jasonr@pec.co.nz>
+ - added domain logon and logon script suport
+ - SCO openserver 5 patches from Scott Michel <scottm@intime.intime.com>
+ - Makefile changes from Marty Leisner <leisner@sdsp.mc.xerox.com>
+ - chgpasswd changes from Roman Dumych <roman@nyxis.unibase.com>
+ for SVR4
+ - GUEST_SESSSETUP change from David.Chappell@mail.cc.trincoll.edu
+ - released p7
+ - moved SO_REUSEADDR before bind() (thanks to Thomas Bellman
+ <tbe@ivab.se>)
+ - added more flexible GUEST_SESSSETUP to local.h and restored
+ pre-p7 behaviour as default
+ - released p8
+
+1.9.16:
+ - Makefile fix from Marty Leisner <leisner@sdsp.mc.xerox.com>
+ - added %g and %G substitutions
+ - changed IDLE_CLOSED_TIMEOUT to 60
+ - fixed the "admin user" status in domain logons
+ - hpux 10 "trusted security" patches from David-Michael Lincke
+ (dlincke@sgcl1.unisg.ch)
+ - added nmb lookups to client from Adrian Hill <Adrian.Hill@softimage.co.uk>
+ - svr4 pause/resume printing patch from Brendan O'Dea (bod@tyndall.com.au)
+ - fixed master announcement thanks to Luke Leighton <rah14@dial.pipex.com>
+ - changed srcdir usage in Makefile to be friendly to more systems
+ - NT4 alignment patches from Jeremy Allison (jra@vantive.com)
+ - updated share mode code for new spec
+ - minor client bugfix (for smbclient '\\\')
+ - fix for level 260 when magling disabled. From Martin Tomes
+ <Martin.Tomes@ecl.etherm.co.uk>
+ - SMBtranss2 fix for OS/2 from Jeremy Allison
+ - profiles fixup from Timm Wetzel <twetzel@cage.mpibpc.gwdg.de>
+ - man page updates from Dirk.DeWachter@rug.ac.be
+ - nmbsync fix from Andy Whitcroft <andy@soi.city.ac.uk>
+ - Lynx patches from Manfred Woelfel <woelfel@hpesco1.fzk.de>
+ - new smbtar stuff from Ricky
+ - changed to share mode DENY_NONE for tar
+ - fixed -D option of smbclient when in tar mode
+ - added aARCH to open modes
+ - added code to cope with select/read errors
+ - fixed blank browse entries after smb.conf reread
+ - integrated new browse stuff from Luke into ipc.c
+ - added workgroup list to smbclient -L
+ - improved archive attribute handling in close_file() and
+ write_file()
+ - smbtar fixes from Martin.Kraemer@mch.sni.de
+ - Linux quota patch from xeno@mix.hsv.no
+ - try to work around NT passlen2 problem in session setup
+ - released alpha1
+
+NOTE: From now on the cvs.log file will be used to give a complete log of
+changes to samba. This change-log is now obsolete.
+
+
+==========
+todo:
+
+
+64 bit longs and IP addresses may give problems with unsigned longs?
+
+set archive bit whenever file is modified??
+
+fix man page dates
+
+reply only to own workgroup in server enum
+
+patch to compile with g++ and possibly solaris c++
+
+nmbd needs to keep browse list uptodate by talking to the master if it loses
+an election as others may still think its a valid backup and use it to get
+lists.
+
+leftover lock files can end up belonging to non-smbd processes after a reboot.
+
+hosts allow in nmbd
+
+hosts allow cache
+
+add password command in smbclient
+
+drag long filename to samba under os/2 gives short name
+
+document max ttl option
+
+dup/close 0 for getopt?
+
+implement SMBmove ??
+
+add option to print more info about locked files (full path, share name
+etc)
+
+very slow listing CD, perhaps because of order of stat and readdir or add
+masking to opendir?
+
+protocol drop back in client to avoid openX etc.
+
+handle exported fat drives to a long filename capable client
+
+add check for existance of lpq commands etc (use stat?)
+
+get rid of the silly +4 and -4 by removing NBT stuff
+
+write-only shares
+
+document cnvchar stuff
+
+allow smbd to serve user and group lists to win95
+
+document homes behaviour with WinDD
+
+add "hide file = *.o" "hide dir = .Foo*" "show file = xx*" type options.
+
+ALLOW_PASSWORD_CHANGE only compiles/works on some systems
+
+weird foooooooo/open.exe bug on NT
+
+%a detection can't detect Win95 versus WinNT
+
+reverse mangled maps, so (*.html *.htm) works for new files.
+
+install problems with w95. could be some sort of race?
+
+more efficient Files[] structure to handle thousands of open files
+
+lpd stuff:
+ Tony Aiuto (tony@ics.com)
+
+make max disk size local
+
diff --git a/source/client/.cvsignore b/source/client/.cvsignore
new file mode 100644
index 00000000000..49a52f7616d
--- /dev/null
+++ b/source/client/.cvsignore
@@ -0,0 +1 @@
+client_proto.h \ No newline at end of file
diff --git a/source/client/client.c b/source/client/client.c
new file mode 100644
index 00000000000..1da35fcc439
--- /dev/null
+++ b/source/client/client.c
@@ -0,0 +1,3085 @@
+/*
+ Unix SMB/CIFS implementation.
+ SMB client
+ Copyright (C) Andrew Tridgell 1994-1998
+ Copyright (C) Simo Sorce 2001-2002
+ 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 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.
+*/
+
+#define NO_SYSLOG
+
+#include "includes.h"
+#include "../client/client_proto.h"
+#ifndef REGISTER
+#define REGISTER 0
+#endif
+
+struct cli_state *cli;
+extern BOOL in_client;
+static int port = 0;
+pstring cur_dir = "\\";
+static pstring cd_path = "";
+static pstring service;
+static pstring desthost;
+static pstring username;
+static pstring password;
+static BOOL use_kerberos;
+static BOOL got_pass;
+static BOOL grepable=False;
+static char *cmdstr = NULL;
+
+static int io_bufsize = 64512;
+
+static int name_type = 0x20;
+static int max_protocol = PROTOCOL_NT1;
+
+static int process_tok(pstring tok);
+static int cmd_help(void);
+
+/* 30 second timeout on most commands */
+#define CLIENT_TIMEOUT (30*1000)
+#define SHORT_TIMEOUT (5*1000)
+
+/* value for unused fid field in trans2 secondary request */
+#define FID_UNUSED (0xFFFF)
+
+time_t newer_than = 0;
+static int archive_level = 0;
+
+static BOOL translation = False;
+
+static BOOL have_ip;
+
+/* clitar bits insert */
+extern int blocksize;
+extern BOOL tar_inc;
+extern BOOL tar_reset;
+/* clitar bits end */
+
+
+static BOOL prompt = True;
+
+static int printmode = 1;
+
+static BOOL recurse = False;
+BOOL lowercase = False;
+
+static struct in_addr dest_ip;
+
+#define SEPARATORS " \t\n\r"
+
+static BOOL abort_mget = True;
+
+static pstring fileselection = "";
+
+extern file_info def_finfo;
+
+/* timing globals */
+SMB_BIG_UINT get_total_size = 0;
+unsigned int get_total_time_ms = 0;
+static SMB_BIG_UINT put_total_size = 0;
+static unsigned int put_total_time_ms = 0;
+
+/* totals globals */
+static double dir_total;
+
+#define USENMB
+
+/* some forward declarations */
+static struct cli_state *do_connect(const char *server, const char *share);
+
+/****************************************************************************
+ 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, char *b, int n)
+{
+ 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(char *b, int n, XFILE *f)
+{
+ int i;
+ int c;
+
+ if (!translation)
+ return x_fread(b,1,n,f);
+
+ i = 0;
+ while (i < (n - 1) && (i < BUFFER_SIZE)) {
+ 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(void)
+{
+ int total_len = 0;
+ int grp_id;
+
+ if (!cli_message_start(cli, desthost, username, &grp_id)) {
+ d_printf("message start: %s\n", cli_errstr(cli));
+ 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);
+ pstring msg;
+ int l=0;
+ int c;
+
+ ZERO_ARRAY(msg);
+
+ for (l=0;l<maxlen && (c=fgetc(stdin))!=EOF;l++) {
+ if (c == '\n')
+ msg[l++] = '\r';
+ msg[l] = c;
+ }
+
+ if (!cli_message_text(cli, msg, l, grp_id)) {
+ d_printf("SMBsendtxt failed (%s)\n",cli_errstr(cli));
+ 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 (!cli_message_end(cli, grp_id)) {
+ d_printf("SMBsendend failed (%s)\n",cli_errstr(cli));
+ return;
+ }
+}
+
+/****************************************************************************
+ Check the space on a device.
+****************************************************************************/
+
+static int do_dskattr(void)
+{
+ int total, bsize, avail;
+
+ if (!cli_dskattr(cli, &bsize, &total, &avail)) {
+ d_printf("Error in dskattr: %s\n",cli_errstr(cli));
+ return 1;
+ }
+
+ d_printf("\n\t\t%d blocks of size %d. %d blocks available\n",
+ total, bsize, avail);
+
+ return 0;
+}
+
+/****************************************************************************
+ Show cd/pwd.
+****************************************************************************/
+
+static int cmd_pwd(void)
+{
+ d_printf("Current directory is %s",service);
+ d_printf("%s\n",cur_dir);
+ return 0;
+}
+
+/****************************************************************************
+ Change directory - inner section.
+****************************************************************************/
+
+static int do_cd(char *newdir)
+{
+ char *p = newdir;
+ pstring saved_dir;
+ pstring dname;
+
+ dos_format(newdir);
+
+ /* Save the current directory in case the
+ new directory is invalid */
+ pstrcpy(saved_dir, cur_dir);
+ if (*p == '\\')
+ pstrcpy(cur_dir,p);
+ else
+ pstrcat(cur_dir,p);
+ if (*(cur_dir+strlen(cur_dir)-1) != '\\') {
+ pstrcat(cur_dir, "\\");
+ }
+ dos_clean_name(cur_dir);
+ pstrcpy(dname,cur_dir);
+ pstrcat(cur_dir,"\\");
+ dos_clean_name(cur_dir);
+
+ if (!strequal(cur_dir,"\\")) {
+ if (!cli_chkpath(cli, dname)) {
+ d_printf("cd %s: %s\n", dname, cli_errstr(cli));
+ pstrcpy(cur_dir,saved_dir);
+ }
+ }
+
+ pstrcpy(cd_path,cur_dir);
+
+ return 0;
+}
+
+/****************************************************************************
+ Change directory.
+****************************************************************************/
+
+static int cmd_cd(void)
+{
+ pstring buf;
+ int rc = 0;
+
+ if (next_token_nr(NULL,buf,NULL,sizeof(buf)))
+ rc = do_cd(buf);
+ else
+ d_printf("Current directory is %s\n",cur_dir);
+
+ return rc;
+}
+
+/*******************************************************************
+ Decide if a file should be operated on.
+********************************************************************/
+
+static BOOL do_this_one(file_info *finfo)
+{
+ if (finfo->mode & aDIR)
+ return(True);
+
+ if (*fileselection &&
+ !mask_match(finfo->name,fileselection,False)) {
+ DEBUG(3,("mask_match %s failed\n", finfo->name));
+ return False;
+ }
+
+ if (newer_than && finfo->mtime < newer_than) {
+ DEBUG(3,("newer_than %s failed\n", finfo->name));
+ return(False);
+ }
+
+ if ((archive_level==1 || archive_level==2) && !(finfo->mode & aARCH)) {
+ DEBUG(3,("archive %s failed\n", finfo->name));
+ return(False);
+ }
+
+ return(True);
+}
+
+/****************************************************************************
+ Display info about a file.
+****************************************************************************/
+
+static void display_finfo(file_info *finfo)
+{
+ if (do_this_one(finfo)) {
+ time_t t = finfo->mtime; /* the time is assumed to be passed as GMT */
+ d_printf(" %-30s%7.7s %8.0f %s",
+ finfo->name,
+ attrib_string(finfo->mode),
+ (double)finfo->size,
+ asctime(LocalTime(&t)));
+ dir_total += finfo->size;
+ }
+}
+
+/****************************************************************************
+ Accumulate size of a file.
+****************************************************************************/
+
+static void do_du(file_info *finfo)
+{
+ if (do_this_one(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)(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(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 the starting point of the queue is more than half way through,
+ * move everything toward the beginning.
+ */
+ if (do_list_queue && (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(do_list_queue, 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_base(do_list_queue + do_list_queue_end,
+ entry, do_list_queue, do_list_queue_size);
+ 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(file_info *f, const char *mask, void *state)
+{
+ if (f->mode & aDIR) {
+ if (do_list_dirs && do_this_one(f)) {
+ do_list_fn(f);
+ }
+ if (do_list_recurse &&
+ !strequal(f->name,".") &&
+ !strequal(f->name,"..")) {
+ pstring mask2;
+ char *p;
+
+ if (!f->name[0]) {
+ d_printf("Empty dir name returned. Possible server misconfiguration.\n");
+ return;
+ }
+
+ pstrcpy(mask2, mask);
+ p = strrchr_m(mask2,'\\');
+ if (!p)
+ return;
+ p[1] = 0;
+ pstrcat(mask2, f->name);
+ pstrcat(mask2,"\\*");
+ add_to_do_list_queue(mask2);
+ }
+ return;
+ }
+
+ if (do_this_one(f)) {
+ do_list_fn(f);
+ }
+}
+
+/****************************************************************************
+ A wrapper around cli_list that adds recursion.
+****************************************************************************/
+
+void do_list(const char *mask,uint16 attribute,void (*fn)(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 cli_list. This
+ * would happen if the list were expanded
+ * during the call.
+ * Fix from E. Jay Berkenbilt (ejb@ql.org)
+ */
+ pstring head;
+ pstrcpy(head, do_list_queue_head());
+ cli_list(cli, head, attribute, do_list_helper, NULL);
+ 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 (cli_list(cli, mask, attribute, do_list_helper, NULL) == -1) {
+ d_printf("%s listing %s\n", cli_errstr(cli), mask);
+ }
+ }
+
+ in_do_list = 0;
+ reset_do_list_queue();
+}
+
+/****************************************************************************
+ Get a directory listing.
+****************************************************************************/
+
+static int cmd_dir(void)
+{
+ uint16 attribute = aDIR | aSYSTEM | aHIDDEN;
+ pstring mask;
+ pstring buf;
+ char *p=buf;
+ int rc;
+
+ dir_total = 0;
+ if (strcmp(cur_dir, "\\") != 0) {
+ pstrcpy(mask,cur_dir);
+ if(mask[strlen(mask)-1]!='\\')
+ pstrcat(mask,"\\");
+ } else {
+ *mask = '\0';
+ }
+
+ if (next_token_nr(NULL,buf,NULL,sizeof(buf))) {
+ dos_format(p);
+ if (*p == '\\')
+ pstrcpy(mask,p);
+ else
+ pstrcat(mask,p);
+ } else {
+ pstrcat(mask,"*");
+ }
+
+ do_list(mask, attribute, display_finfo, recurse, True);
+
+ rc = do_dskattr();
+
+ DEBUG(3, ("Total bytes listed: %.0f\n", dir_total));
+
+ return rc;
+}
+
+/****************************************************************************
+ Get a directory listing.
+****************************************************************************/
+
+static int cmd_du(void)
+{
+ uint16 attribute = aDIR | aSYSTEM | aHIDDEN;
+ pstring mask;
+ pstring buf;
+ char *p=buf;
+ int rc;
+
+ dir_total = 0;
+ pstrcpy(mask,cur_dir);
+ if(mask[strlen(mask)-1]!='\\')
+ pstrcat(mask,"\\");
+
+ if (next_token_nr(NULL,buf,NULL,sizeof(buf))) {
+ dos_format(p);
+ if (*p == '\\')
+ pstrcpy(mask,p);
+ else
+ pstrcat(mask,p);
+ } else {
+ pstrcat(mask,"*");
+ }
+
+ do_list(mask, attribute, do_du, recurse, True);
+
+ rc = do_dskattr();
+
+ d_printf("Total number of bytes: %.0f\n", dir_total);
+
+ return rc;
+}
+
+/****************************************************************************
+ Get a file from rname to lname
+****************************************************************************/
+
+static int do_get(char *rname, char *lname, BOOL reget)
+{
+ int handle = 0, fnum;
+ BOOL newhandle = False;
+ char *data;
+ struct timeval tp_start;
+ int read_size = io_bufsize;
+ uint16 attr;
+ size_t size;
+ off_t start = 0;
+ off_t nread = 0;
+ int rc = 0;
+
+ GetTimeOfDay(&tp_start);
+
+ if (lowercase) {
+ strlower_m(lname);
+ }
+
+ fnum = cli_open(cli, rname, O_RDONLY, DENY_NONE);
+
+ if (fnum == -1) {
+ d_printf("%s opening remote file %s\n",cli_errstr(cli),rname);
+ return 1;
+ }
+
+ if(!strcmp(lname,"-")) {
+ handle = fileno(stdout);
+ } else {
+ if (reget) {
+ handle = sys_open(lname, O_WRONLY|O_CREAT, 0644);
+ if (handle >= 0) {
+ start = sys_lseek(handle, 0, SEEK_END);
+ if (start == -1) {
+ d_printf("Error seeking local file\n");
+ return 1;
+ }
+ }
+ } else {
+ handle = sys_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 (!cli_qfileinfo(cli, fnum,
+ &attr, &size, NULL, NULL, NULL, NULL, NULL) &&
+ !cli_getattrE(cli, fnum,
+ &attr, &size, NULL, NULL, NULL)) {
+ d_printf("getattrib: %s\n",cli_errstr(cli));
+ return 1;
+ }
+
+ DEBUG(2,("getting file %s of size %.0f as %s ",
+ rname, (double)size, lname));
+
+ if(!(data = (char *)malloc(read_size))) {
+ d_printf("malloc fail for size %d\n", read_size);
+ cli_close(cli, fnum);
+ return 1;
+ }
+
+ while (1) {
+ int n = cli_read(cli, fnum, data, nread + start, read_size);
+
+ if (n <= 0)
+ break;
+
+ if (writefile(handle,data, n) != 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 (!cli_close(cli, fnum)) {
+ d_printf("Error %s closing remote file\n",cli_errstr(cli));
+ rc = 1;
+ }
+
+ if (newhandle) {
+ close(handle);
+ }
+
+ if (archive_level >= 2 && (attr & aARCH)) {
+ cli_setatr(cli, rname, attr & ~(uint16)aARCH, 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(void)
+{
+ pstring lname;
+ pstring rname;
+ char *p;
+
+ pstrcpy(rname,cur_dir);
+ pstrcat(rname,"\\");
+
+ p = rname + strlen(rname);
+
+ if (!next_token_nr(NULL,p,NULL,sizeof(rname)-strlen(rname))) {
+ d_printf("get <filename>\n");
+ return 1;
+ }
+ pstrcpy(lname,p);
+ dos_clean_name(rname);
+
+ next_token_nr(NULL,lname,NULL,sizeof(lname));
+
+ return do_get(rname, lname, False);
+}
+
+/****************************************************************************
+ Do an mget operation on one file.
+****************************************************************************/
+
+static void do_mget(file_info *finfo)
+{
+ pstring rname;
+ pstring quest;
+ pstring saved_curdir;
+ pstring mget_mask;
+
+ if (strequal(finfo->name,".") || strequal(finfo->name,".."))
+ return;
+
+ if (abort_mget) {
+ d_printf("mget aborted\n");
+ return;
+ }
+
+ if (finfo->mode & aDIR)
+ slprintf(quest,sizeof(pstring)-1,
+ "Get directory %s? ",finfo->name);
+ else
+ slprintf(quest,sizeof(pstring)-1,
+ "Get file %s? ",finfo->name);
+
+ if (prompt && !yesno(quest))
+ return;
+
+ if (!(finfo->mode & aDIR)) {
+ pstrcpy(rname,cur_dir);
+ pstrcat(rname,finfo->name);
+ do_get(rname, finfo->name, False);
+ return;
+ }
+
+ /* handle directories */
+ pstrcpy(saved_curdir,cur_dir);
+
+ pstrcat(cur_dir,finfo->name);
+ pstrcat(cur_dir,"\\");
+
+ unix_format(finfo->name);
+ if (lowercase)
+ strlower_m(finfo->name);
+
+ if (!directory_exist(finfo->name,NULL) &&
+ mkdir(finfo->name,0777) != 0) {
+ d_printf("failed to create directory %s\n",finfo->name);
+ pstrcpy(cur_dir,saved_curdir);
+ return;
+ }
+
+ if (chdir(finfo->name) != 0) {
+ d_printf("failed to chdir to directory %s\n",finfo->name);
+ pstrcpy(cur_dir,saved_curdir);
+ return;
+ }
+
+ pstrcpy(mget_mask,cur_dir);
+ pstrcat(mget_mask,"*");
+
+ do_list(mget_mask, aSYSTEM | aHIDDEN | aDIR,do_mget,False, True);
+ chdir("..");
+ pstrcpy(cur_dir,saved_curdir);
+}
+
+/****************************************************************************
+ View the file using the pager.
+****************************************************************************/
+
+static int cmd_more(void)
+{
+ pstring rname,lname,pager_cmd;
+ char *pager;
+ int fd;
+ int rc = 0;
+
+ pstrcpy(rname,cur_dir);
+ pstrcat(rname,"\\");
+
+ slprintf(lname,sizeof(lname)-1, "%s/smbmore.XXXXXX",tmpdir());
+ fd = smb_mkstemp(lname);
+ if (fd == -1) {
+ d_printf("failed to create temporary file for more\n");
+ return 1;
+ }
+ close(fd);
+
+ if (!next_token_nr(NULL,rname+strlen(rname),NULL,sizeof(rname)-strlen(rname))) {
+ d_printf("more <filename>\n");
+ unlink(lname);
+ return 1;
+ }
+ dos_clean_name(rname);
+
+ rc = do_get(rname, lname, False);
+
+ pager=getenv("PAGER");
+
+ slprintf(pager_cmd,sizeof(pager_cmd)-1,
+ "%s %s",(pager? pager:PAGER), lname);
+ system(pager_cmd);
+ unlink(lname);
+
+ return rc;
+}
+
+/****************************************************************************
+ Do a mget command.
+****************************************************************************/
+
+static int cmd_mget(void)
+{
+ uint16 attribute = aSYSTEM | aHIDDEN;
+ pstring mget_mask;
+ pstring buf;
+ char *p=buf;
+
+ *mget_mask = 0;
+
+ if (recurse)
+ attribute |= aDIR;
+
+ abort_mget = False;
+
+ while (next_token_nr(NULL,p,NULL,sizeof(buf))) {
+ pstrcpy(mget_mask,cur_dir);
+ if(mget_mask[strlen(mget_mask)-1]!='\\')
+ pstrcat(mget_mask,"\\");
+
+ if (*p == '\\')
+ pstrcpy(mget_mask,p);
+ else
+ pstrcat(mget_mask,p);
+ do_list(mget_mask, attribute,do_mget,False,True);
+ }
+
+ if (!*mget_mask) {
+ pstrcpy(mget_mask,cur_dir);
+ if(mget_mask[strlen(mget_mask)-1]!='\\')
+ pstrcat(mget_mask,"\\");
+ pstrcat(mget_mask,"*");
+ do_list(mget_mask, attribute,do_mget,False,True);
+ }
+
+ return 0;
+}
+
+/****************************************************************************
+ Make a directory of name "name".
+****************************************************************************/
+
+static BOOL do_mkdir(char *name)
+{
+ if (!cli_mkdir(cli, name)) {
+ d_printf("%s making remote directory %s\n",
+ cli_errstr(cli),name);
+ return(False);
+ }
+
+ return(True);
+}
+
+/****************************************************************************
+ Show 8.3 name of a file.
+****************************************************************************/
+
+static BOOL do_altname(char *name)
+{
+ pstring altname;
+ if (!NT_STATUS_IS_OK(cli_qpathinfo_alt_name(cli, name, altname))) {
+ d_printf("%s getting alt name for %s\n",
+ cli_errstr(cli),name);
+ return(False);
+ }
+ d_printf("%s\n", altname);
+
+ return(True);
+}
+
+/****************************************************************************
+ Exit client.
+****************************************************************************/
+
+static int cmd_quit(void)
+{
+ cli_shutdown(cli);
+ exit(0);
+ /* NOTREACHED */
+ return 0;
+}
+
+/****************************************************************************
+ Make a directory.
+****************************************************************************/
+
+static int cmd_mkdir(void)
+{
+ pstring mask;
+ pstring buf;
+ char *p=buf;
+
+ pstrcpy(mask,cur_dir);
+
+ if (!next_token_nr(NULL,p,NULL,sizeof(buf))) {
+ if (!recurse)
+ d_printf("mkdir <dirname>\n");
+ return 1;
+ }
+ pstrcat(mask,p);
+
+ if (recurse) {
+ pstring ddir;
+ pstring ddir2;
+ *ddir2 = 0;
+
+ pstrcpy(ddir,mask);
+ trim_char(ddir,'.','\0');
+ p = strtok(ddir,"/\\");
+ while (p) {
+ pstrcat(ddir2,p);
+ if (!cli_chkpath(cli, ddir2)) {
+ do_mkdir(ddir2);
+ }
+ pstrcat(ddir2,"\\");
+ p = strtok(NULL,"/\\");
+ }
+ } else {
+ do_mkdir(mask);
+ }
+
+ return 0;
+}
+
+/****************************************************************************
+ Show alt name.
+****************************************************************************/
+
+static int cmd_altname(void)
+{
+ pstring name;
+ pstring buf;
+ char *p=buf;
+
+ pstrcpy(name,cur_dir);
+
+ if (!next_token_nr(NULL,p,NULL,sizeof(buf))) {
+ d_printf("altname <file>\n");
+ return 1;
+ }
+ pstrcat(name,p);
+
+ do_altname(name);
+
+ return 0;
+}
+
+/****************************************************************************
+ Put a single file.
+****************************************************************************/
+
+static int do_put(char *rname, char *lname, BOOL reput)
+{
+ int fnum;
+ XFILE *f;
+ size_t start = 0;
+ off_t nread = 0;
+ char *buf = NULL;
+ int maxwrite = io_bufsize;
+ int rc = 0;
+
+ struct timeval tp_start;
+ GetTimeOfDay(&tp_start);
+
+ if (reput) {
+ fnum = cli_open(cli, rname, O_RDWR|O_CREAT, DENY_NONE);
+ if (fnum >= 0) {
+ if (!cli_qfileinfo(cli, fnum, NULL, &start, NULL, NULL, NULL, NULL, NULL) &&
+ !cli_getattrE(cli, fnum, NULL, &start, NULL, NULL, NULL)) {
+ d_printf("getattrib: %s\n",cli_errstr(cli));
+ return 1;
+ }
+ }
+ } else {
+ fnum = cli_open(cli, rname, O_RDWR|O_CREAT|O_TRUNC, DENY_NONE);
+ }
+
+ if (fnum == -1) {
+ d_printf("%s opening remote file %s\n",cli_errstr(cli),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 = (char *)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)) < 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 = cli_write(cli, fnum, 0, buf, nread + start, n);
+
+ if (n != ret) {
+ d_printf("Error writing file: %s\n", cli_errstr(cli));
+ rc = 1;
+ break;
+ }
+
+ nread += n;
+ }
+
+ if (!cli_close(cli, fnum)) {
+ d_printf("%s closing remote file %s\n",cli_errstr(cli),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) {
+ cli_shutdown(cli);
+ exit(0);
+ }
+
+ return rc;
+}
+
+/****************************************************************************
+ Put a file.
+****************************************************************************/
+
+static int cmd_put(void)
+{
+ pstring lname;
+ pstring rname;
+ pstring buf;
+ char *p=buf;
+
+ pstrcpy(rname,cur_dir);
+ pstrcat(rname,"\\");
+
+ if (!next_token_nr(NULL,p,NULL,sizeof(buf))) {
+ d_printf("put <filename>\n");
+ return 1;
+ }
+ pstrcpy(lname,p);
+
+ if (next_token_nr(NULL,p,NULL,sizeof(buf)))
+ pstrcat(rname,p);
+ else
+ pstrcat(rname,lname);
+
+ dos_clean_name(rname);
+
+ {
+ SMB_STRUCT_STAT st;
+ /* allow '-' to represent stdin
+ jdblair, 24.jun.98 */
+ if (!file_exist(lname,&st) &&
+ (strcmp(lname,"-"))) {
+ d_printf("%s does not exist\n",lname);
+ return 1;
+ }
+ }
+
+ return do_put(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(void)
+{
+ pstrcpy(fileselection,"");
+ next_token_nr(NULL,fileselection,NULL,sizeof(fileselection));
+
+ return 0;
+}
+
+/****************************************************************************
+ Recursive file matching function act as find
+ match must be always set to True when calling this function
+****************************************************************************/
+
+static int file_find(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 (!strcmp("..", dname))
+ continue;
+ if (!strcmp(".", dname))
+ continue;
+
+ if (asprintf(&path, "%s/%s", directory, dname) <= 0) {
+ continue;
+ }
+
+ isdir = False;
+ if (!match || !gen_fnmatch(expression, dname)) {
+ if (recurse) {
+ ret = stat(path, &statbuf);
+ if (ret == 0) {
+ if (S_ISDIR(statbuf.st_mode)) {
+ isdir = True;
+ ret = file_find(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 = (struct file_list *) malloc(sizeof (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(void)
+{
+ pstring buf;
+ char *p=buf;
+
+ while (next_token_nr(NULL,p,NULL,sizeof(buf))) {
+ int ret;
+ struct file_list *temp_list;
+ char *quest, *lname, *rname;
+
+ file_list = NULL;
+
+ ret = file_find(&file_list, ".", p, 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 (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", cur_dir, lname) < 0) break;
+ dos_format(rname);
+ if (!cli_chkpath(cli, rname) &&
+ !do_mkdir(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 (prompt && !yesno(quest)) /* No */
+ continue;
+
+ /* Yes */
+ SAFE_FREE(rname);
+ if (asprintf(&rname, "%s%s", cur_dir, lname) < 0) break;
+ }
+
+ dos_format(rname);
+
+ do_put(rname, lname, False);
+ }
+ free_file_list(file_list);
+ SAFE_FREE(quest);
+ SAFE_FREE(lname);
+ SAFE_FREE(rname);
+ }
+
+ return 0;
+}
+
+/****************************************************************************
+ Cancel a print job.
+****************************************************************************/
+
+static int do_cancel(int job)
+{
+ if (cli_printjob_del(cli, job)) {
+ d_printf("Job %d cancelled\n",job);
+ return 0;
+ } else {
+ d_printf("Error cancelling job %d : %s\n",job,cli_errstr(cli));
+ return 1;
+ }
+}
+
+/****************************************************************************
+ Cancel a print job.
+****************************************************************************/
+
+static int cmd_cancel(void)
+{
+ pstring buf;
+ int job;
+
+ if (!next_token_nr(NULL,buf,NULL,sizeof(buf))) {
+ d_printf("cancel <jobid> ...\n");
+ return 1;
+ }
+ do {
+ job = atoi(buf);
+ do_cancel(job);
+ } while (next_token_nr(NULL,buf,NULL,sizeof(buf)));
+
+ return 0;
+}
+
+/****************************************************************************
+ Print a file.
+****************************************************************************/
+
+static int cmd_print(void)
+{
+ pstring lname;
+ pstring rname;
+ char *p;
+
+ if (!next_token_nr(NULL,lname,NULL, sizeof(lname))) {
+ d_printf("print <filename>\n");
+ return 1;
+ }
+
+ pstrcpy(rname,lname);
+ p = strrchr_m(rname,'/');
+ if (p) {
+ slprintf(rname, sizeof(rname)-1, "%s-%d", p+1, (int)sys_getpid());
+ }
+
+ if (strequal(lname,"-")) {
+ slprintf(rname, sizeof(rname)-1, "stdin-%d", (int)sys_getpid());
+ }
+
+ return do_put(rname, lname, False);
+}
+
+/****************************************************************************
+ Show a print queue entry.
+****************************************************************************/
+
+static void queue_fn(struct print_job_info *p)
+{
+ d_printf("%-6d %-9d %s\n", (int)p->id, (int)p->size, p->name);
+}
+
+/****************************************************************************
+ Show a print queue.
+****************************************************************************/
+
+static int cmd_queue(void)
+{
+ cli_print_queue(cli, queue_fn);
+
+ return 0;
+}
+
+/****************************************************************************
+ Delete some files.
+****************************************************************************/
+
+static void do_del(file_info *finfo)
+{
+ pstring mask;
+
+ pstrcpy(mask,cur_dir);
+ pstrcat(mask,finfo->name);
+
+ if (finfo->mode & aDIR)
+ return;
+
+ if (!cli_unlink(cli, mask)) {
+ d_printf("%s deleting remote file %s\n",cli_errstr(cli),mask);
+ }
+}
+
+/****************************************************************************
+ Delete some files.
+****************************************************************************/
+
+static int cmd_del(void)
+{
+ pstring mask;
+ pstring buf;
+ uint16 attribute = aSYSTEM | aHIDDEN;
+
+ if (recurse)
+ attribute |= aDIR;
+
+ pstrcpy(mask,cur_dir);
+
+ if (!next_token_nr(NULL,buf,NULL,sizeof(buf))) {
+ d_printf("del <filename>\n");
+ return 1;
+ }
+ pstrcat(mask,buf);
+
+ do_list(mask, attribute,do_del,False,False);
+
+ return 0;
+}
+
+/****************************************************************************
+****************************************************************************/
+
+static int cmd_open(void)
+{
+ pstring mask;
+ pstring buf;
+
+ pstrcpy(mask,cur_dir);
+
+ if (!next_token_nr(NULL,buf,NULL,sizeof(buf))) {
+ d_printf("open <filename>\n");
+ return 1;
+ }
+ pstrcat(mask,buf);
+
+ cli_open(cli, mask, O_RDWR, DENY_ALL);
+
+ return 0;
+}
+
+
+/****************************************************************************
+ Remove a directory.
+****************************************************************************/
+
+static int cmd_rmdir(void)
+{
+ pstring mask;
+ pstring buf;
+
+ pstrcpy(mask,cur_dir);
+
+ if (!next_token_nr(NULL,buf,NULL,sizeof(buf))) {
+ d_printf("rmdir <dirname>\n");
+ return 1;
+ }
+ pstrcat(mask,buf);
+
+ if (!cli_rmdir(cli, mask)) {
+ d_printf("%s removing remote directory file %s\n",
+ cli_errstr(cli),mask);
+ }
+
+ return 0;
+}
+
+/****************************************************************************
+ UNIX hardlink.
+****************************************************************************/
+
+static int cmd_link(void)
+{
+ pstring src,dest;
+ pstring buf,buf2;
+
+ if (!SERVER_HAS_UNIX_CIFS(cli)) {
+ d_printf("Server doesn't support UNIX CIFS calls.\n");
+ return 1;
+ }
+
+ pstrcpy(src,cur_dir);
+ pstrcpy(dest,cur_dir);
+
+ if (!next_token_nr(NULL,buf,NULL,sizeof(buf)) ||
+ !next_token_nr(NULL,buf2,NULL, sizeof(buf2))) {
+ d_printf("link <src> <dest>\n");
+ return 1;
+ }
+
+ pstrcat(src,buf);
+ pstrcat(dest,buf2);
+
+ if (!cli_unix_hardlink(cli, src, dest)) {
+ d_printf("%s linking files (%s -> %s)\n", cli_errstr(cli), src, dest);
+ return 1;
+ }
+
+ return 0;
+}
+
+/****************************************************************************
+ UNIX symlink.
+****************************************************************************/
+
+static int cmd_symlink(void)
+{
+ pstring src,dest;
+ pstring buf,buf2;
+
+ if (!SERVER_HAS_UNIX_CIFS(cli)) {
+ d_printf("Server doesn't support UNIX CIFS calls.\n");
+ return 1;
+ }
+
+ pstrcpy(src,cur_dir);
+ pstrcpy(dest,cur_dir);
+
+ if (!next_token_nr(NULL,buf,NULL,sizeof(buf)) ||
+ !next_token_nr(NULL,buf2,NULL, sizeof(buf2))) {
+ d_printf("symlink <src> <dest>\n");
+ return 1;
+ }
+
+ pstrcat(src,buf);
+ pstrcat(dest,buf2);
+
+ if (!cli_unix_symlink(cli, src, dest)) {
+ d_printf("%s symlinking files (%s -> %s)\n",
+ cli_errstr(cli), src, dest);
+ return 1;
+ }
+
+ return 0;
+}
+
+/****************************************************************************
+ UNIX chmod.
+****************************************************************************/
+
+static int cmd_chmod(void)
+{
+ pstring src;
+ mode_t mode;
+ pstring buf, buf2;
+
+ if (!SERVER_HAS_UNIX_CIFS(cli)) {
+ d_printf("Server doesn't support UNIX CIFS calls.\n");
+ return 1;
+ }
+
+ pstrcpy(src,cur_dir);
+
+ if (!next_token_nr(NULL,buf,NULL,sizeof(buf)) ||
+ !next_token_nr(NULL,buf2,NULL, sizeof(buf2))) {
+ d_printf("chmod mode file\n");
+ return 1;
+ }
+
+ mode = (mode_t)strtol(buf, NULL, 8);
+ pstrcat(src,buf2);
+
+ if (!cli_unix_chmod(cli, src, mode)) {
+ d_printf("%s chmod file %s 0%o\n",
+ cli_errstr(cli), src, (unsigned int)mode);
+ return 1;
+ }
+
+ return 0;
+}
+
+/****************************************************************************
+ UNIX chown.
+****************************************************************************/
+
+static int cmd_chown(void)
+{
+ pstring src;
+ uid_t uid;
+ gid_t gid;
+ pstring buf, buf2, buf3;
+
+ if (!SERVER_HAS_UNIX_CIFS(cli)) {
+ d_printf("Server doesn't support UNIX CIFS calls.\n");
+ return 1;
+ }
+
+ pstrcpy(src,cur_dir);
+
+ if (!next_token_nr(NULL,buf,NULL,sizeof(buf)) ||
+ !next_token_nr(NULL,buf2,NULL, sizeof(buf2)) ||
+ !next_token_nr(NULL,buf3,NULL, sizeof(buf3))) {
+ d_printf("chown uid gid file\n");
+ return 1;
+ }
+
+ uid = (uid_t)atoi(buf);
+ gid = (gid_t)atoi(buf2);
+ pstrcat(src,buf3);
+
+ if (!cli_unix_chown(cli, src, uid, gid)) {
+ d_printf("%s chown file %s uid=%d, gid=%d\n",
+ cli_errstr(cli), src, (int)uid, (int)gid);
+ return 1;
+ }
+
+ return 0;
+}
+
+/****************************************************************************
+ Rename some file.
+****************************************************************************/
+
+static int cmd_rename(void)
+{
+ pstring src,dest;
+ pstring buf,buf2;
+
+ pstrcpy(src,cur_dir);
+ pstrcpy(dest,cur_dir);
+
+ if (!next_token_nr(NULL,buf,NULL,sizeof(buf)) ||
+ !next_token_nr(NULL,buf2,NULL, sizeof(buf2))) {
+ d_printf("rename <src> <dest>\n");
+ return 1;
+ }
+
+ pstrcat(src,buf);
+ pstrcat(dest,buf2);
+
+ if (!cli_rename(cli, src, dest)) {
+ d_printf("%s renaming files\n",cli_errstr(cli));
+ return 1;
+ }
+
+ return 0;
+}
+
+/****************************************************************************
+ Hard link files using the NT call.
+****************************************************************************/
+
+static int cmd_hardlink(void)
+{
+ pstring src,dest;
+ pstring buf,buf2;
+
+ pstrcpy(src,cur_dir);
+ pstrcpy(dest,cur_dir);
+
+ if (!next_token_nr(NULL,buf,NULL,sizeof(buf)) ||
+ !next_token_nr(NULL,buf2,NULL, sizeof(buf2))) {
+ d_printf("hardlink <src> <dest>\n");
+ return 1;
+ }
+
+ pstrcat(src,buf);
+ pstrcat(dest,buf2);
+
+ if (!cli_nt_hardlink(cli, src, dest)) {
+ d_printf("%s doing an NT hard link of files\n",cli_errstr(cli));
+ return 1;
+ }
+
+ return 0;
+}
+
+/****************************************************************************
+ Toggle the prompt flag.
+****************************************************************************/
+
+static int cmd_prompt(void)
+{
+ prompt = !prompt;
+ DEBUG(2,("prompting is now %s\n",prompt?"on":"off"));
+
+ return 1;
+}
+
+/****************************************************************************
+ Set the newer than time.
+****************************************************************************/
+
+static int cmd_newer(void)
+{
+ pstring buf;
+ BOOL ok;
+ SMB_STRUCT_STAT sbuf;
+
+ ok = next_token_nr(NULL,buf,NULL,sizeof(buf));
+ if (ok && (sys_stat(buf,&sbuf) == 0)) {
+ newer_than = sbuf.st_mtime;
+ DEBUG(1,("Getting files newer than %s",
+ asctime(LocalTime(&newer_than))));
+ } else {
+ newer_than = 0;
+ }
+
+ if (ok && newer_than == 0) {
+ d_printf("Error setting newer-than time\n");
+ return 1;
+ }
+
+ return 0;
+}
+
+/****************************************************************************
+ Set the archive level.
+****************************************************************************/
+
+static int cmd_archive(void)
+{
+ pstring buf;
+
+ if (next_token_nr(NULL,buf,NULL,sizeof(buf))) {
+ archive_level = atoi(buf);
+ } else
+ d_printf("Archive level is %d\n",archive_level);
+
+ return 0;
+}
+
+/****************************************************************************
+ Toggle the lowercaseflag.
+****************************************************************************/
+
+static int cmd_lowercase(void)
+{
+ lowercase = !lowercase;
+ DEBUG(2,("filename lowercasing is now %s\n",lowercase?"on":"off"));
+
+ return 0;
+}
+
+/****************************************************************************
+ Toggle the recurse flag.
+****************************************************************************/
+
+static int cmd_recurse(void)
+{
+ recurse = !recurse;
+ DEBUG(2,("directory recursion is now %s\n",recurse?"on":"off"));
+
+ return 0;
+}
+
+/****************************************************************************
+ Toggle the translate flag.
+****************************************************************************/
+
+static int cmd_translate(void)
+{
+ translation = !translation;
+ DEBUG(2,("CR/LF<->LF and print text translation now %s\n",
+ translation?"on":"off"));
+
+ return 0;
+}
+
+/****************************************************************************
+ Do a printmode command.
+****************************************************************************/
+
+static int cmd_printmode(void)
+{
+ fstring buf;
+ fstring mode;
+
+ if (next_token_nr(NULL,buf,NULL,sizeof(buf))) {
+ if (strequal(buf,"text")) {
+ printmode = 0;
+ } else {
+ if (strequal(buf,"graphics"))
+ printmode = 1;
+ else
+ printmode = atoi(buf);
+ }
+ }
+
+ switch(printmode) {
+ case 0:
+ fstrcpy(mode,"text");
+ break;
+ case 1:
+ fstrcpy(mode,"graphics");
+ break;
+ default:
+ slprintf(mode,sizeof(mode)-1,"%d",printmode);
+ break;
+ }
+
+ DEBUG(2,("the printmode is now %s\n",mode));
+
+ return 0;
+}
+
+/****************************************************************************
+ Do the lcd command.
+ ****************************************************************************/
+
+static int cmd_lcd(void)
+{
+ pstring buf;
+ pstring d;
+
+ if (next_token_nr(NULL,buf,NULL,sizeof(buf)))
+ chdir(buf);
+ DEBUG(2,("the local directory is now %s\n",sys_getwd(d)));
+
+ return 0;
+}
+
+/****************************************************************************
+ Get a file restarting at end of local file.
+ ****************************************************************************/
+
+static int cmd_reget(void)
+{
+ pstring local_name;
+ pstring remote_name;
+ char *p;
+
+ pstrcpy(remote_name, cur_dir);
+ pstrcat(remote_name, "\\");
+
+ p = remote_name + strlen(remote_name);
+
+ if (!next_token_nr(NULL, p, NULL, sizeof(remote_name) - strlen(remote_name))) {
+ d_printf("reget <filename>\n");
+ return 1;
+ }
+ pstrcpy(local_name, p);
+ dos_clean_name(remote_name);
+
+ next_token_nr(NULL, local_name, NULL, sizeof(local_name));
+
+ return do_get(remote_name, local_name, True);
+}
+
+/****************************************************************************
+ Put a file restarting at end of local file.
+ ****************************************************************************/
+
+static int cmd_reput(void)
+{
+ pstring local_name;
+ pstring remote_name;
+ pstring buf;
+ char *p = buf;
+ SMB_STRUCT_STAT st;
+
+ pstrcpy(remote_name, cur_dir);
+ pstrcat(remote_name, "\\");
+
+ if (!next_token_nr(NULL, p, NULL, sizeof(buf))) {
+ d_printf("reput <filename>\n");
+ return 1;
+ }
+ pstrcpy(local_name, p);
+
+ if (!file_exist(local_name, &st)) {
+ d_printf("%s does not exist\n", local_name);
+ return 1;
+ }
+
+ if (next_token_nr(NULL, p, NULL, sizeof(buf)))
+ pstrcat(remote_name, p);
+ else
+ pstrcat(remote_name, local_name);
+
+ dos_clean_name(remote_name);
+
+ return do_put(remote_name, local_name, True);
+}
+
+/****************************************************************************
+ List a share name.
+ ****************************************************************************/
+
+static void browse_fn(const char *name, uint32 m,
+ const char *comment, void *state)
+{
+ fstring typestr;
+
+ *typestr=0;
+
+ switch (m)
+ {
+ case STYPE_DISKTREE:
+ fstrcpy(typestr,"Disk"); break;
+ case STYPE_PRINTQ:
+ fstrcpy(typestr,"Printer"); break;
+ case STYPE_DEVICE:
+ fstrcpy(typestr,"Device"); break;
+ case STYPE_IPC:
+ fstrcpy(typestr,"IPC"); break;
+ }
+ /* FIXME: If the remote machine returns non-ascii characters
+ in any of these fields, they can corrupt the output. We
+ should remove them. */
+ if (!grepable) {
+ d_printf("\t%-15s %-10.10s%s\n",
+ name,typestr,comment);
+ } else {
+ d_printf ("%s|%s|%s\n",typestr,name,comment);
+ }
+}
+
+/****************************************************************************
+ Try and browse available connections on a host.
+****************************************************************************/
+
+static BOOL browse_host(BOOL sort)
+{
+ int ret;
+ if (!grepable) {
+ d_printf("\n\tSharename Type Comment\n");
+ d_printf("\t--------- ---- -------\n");
+ }
+
+ if((ret = cli_RNetShareEnum(cli, browse_fn, NULL)) == -1)
+ d_printf("Error returning browse list: %s\n", cli_errstr(cli));
+
+ return (ret != -1);
+}
+
+/****************************************************************************
+ List a server name.
+****************************************************************************/
+
+static void server_fn(const char *name, uint32 m,
+ const char *comment, void *state)
+{
+
+ if (!grepable){
+ d_printf("\t%-16s %s\n", name, comment);
+ } else {
+ d_printf("%s|%s|%s\n",(char *)state, name, comment);
+ }
+}
+
+/****************************************************************************
+ Try and browse available connections on a host.
+****************************************************************************/
+
+static BOOL list_servers(const char *wk_grp)
+{
+ if (!cli->server_domain)
+ return False;
+
+ if (!grepable) {
+ d_printf("\n\tServer Comment\n");
+ d_printf("\t--------- -------\n");
+ };
+ cli_NetServerEnum(cli, cli->server_domain, SV_TYPE_ALL, server_fn,
+ "Server");
+
+ if (!grepable) {
+ d_printf("\n\tWorkgroup Master\n");
+ d_printf("\t--------- -------\n");
+ };
+
+ cli_NetServerEnum(cli, cli->server_domain, SV_TYPE_DOMAIN_ENUM,
+ server_fn, "Workgroup");
+ return True;
+}
+
+/****************************************************************************
+ Print or set current VUID
+****************************************************************************/
+
+static int cmd_vuid(void)
+{
+ fstring buf;
+
+ if (!next_token_nr(NULL,buf,NULL,sizeof(buf))) {
+ d_printf("Current VUID is %d\n", cli->vuid);
+ return 0;
+ }
+
+ cli->vuid = atoi(buf);
+ return 0;
+}
+
+/****************************************************************************
+ Setup a new VUID, by issuing a session setup
+****************************************************************************/
+
+static int cmd_logon(void)
+{
+ pstring l_username, l_password;
+ pstring buf,buf2;
+
+ if (!next_token_nr(NULL,buf,NULL,sizeof(buf))) {
+ d_printf("logon <username> [<password>]\n");
+ return 0;
+ }
+
+ pstrcpy(l_username, buf);
+
+ if (!next_token_nr(NULL,buf2,NULL,sizeof(buf))) {
+ char *pass = getpass("Password: ");
+ if (pass) {
+ pstrcpy(l_password, pass);
+ got_pass = 1;
+ }
+ } else {
+ pstrcpy(l_password, buf2);
+ }
+
+ if (!cli_session_setup(cli, l_username,
+ l_password, strlen(l_password),
+ l_password, strlen(l_password),
+ lp_workgroup())) {
+ d_printf("session setup failed: %s\n", cli_errstr(cli));
+ return -1;
+ }
+
+ d_printf("Current VUID is %d\n", cli->vuid);
+ return 0;
+}
+
+/* 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 */
+
+/* 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)(void);
+ const char *description;
+ char compl_args[2]; /* Completion argument info */
+} commands[] = {
+ {"?",cmd_help,"[command] give help on a command",{COMPL_NONE,COMPL_NONE}},
+ {"altname",cmd_altname,"<file> show alt name",{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}},
+ {"blocksize",cmd_block,"blocksize <number> (default 20)",{COMPL_NONE,COMPL_NONE}},
+ {"cancel",cmd_cancel,"<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}},
+ {"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}},
+ {"exit",cmd_quit,"logoff the server",{COMPL_NONE,COMPL_NONE}},
+ {"get",cmd_get,"<remote name> [local name] get a file",{COMPL_REMOTE,COMPL_LOCAL}},
+ {"hardlink",cmd_hardlink,"<src> <dest> create a Windows hard link",{COMPL_REMOTE,COMPL_REMOTE}},
+ {"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}},
+ {"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}},
+ {"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_queue,"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}},
+ {"setmode",cmd_setmode,"filename <setmode string> change modes of file",{COMPL_REMOTE,COMPL_NONE}},
+ {"symlink",cmd_symlink,"<src> <dest> create a UNIX symlink",{COMPL_REMOTE,COMPL_REMOTE}},
+ {"tar",cmd_tar,"tar <c|x>[IXFqbgNan] current directory to/from <file name>",{COMPL_NONE,COMPL_NONE}},
+ {"tarmode",cmd_tarmode,"<full|inc|reset|noreset> tar's behaviour towards archive bits",{COMPL_NONE,COMPL_NONE}},
+ {"translate",cmd_translate,"toggle text translation for printing",{COMPL_NONE,COMPL_NONE}},
+ {"vuid",cmd_vuid,"change current vuid",{COMPL_NONE,COMPL_NONE}},
+ {"logon",cmd_logon,"establish new logon",{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(pstring 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 (strnequal(commands[i].name, tok, tok_len)) {
+ matches++;
+ cmd = i;
+ }
+ i++;
+ }
+
+ if (matches == 0)
+ return(-1);
+ else if (matches == 1)
+ return(cmd);
+ else
+ return(-2);
+}
+
+/****************************************************************************
+ Help.
+****************************************************************************/
+
+static int cmd_help(void)
+{
+ int i=0,j;
+ pstring buf;
+
+ if (next_token_nr(NULL,buf,NULL,sizeof(buf))) {
+ if ((i = process_tok(buf)) >= 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;
+}
+
+/****************************************************************************
+ Process a -c command string.
+****************************************************************************/
+
+static int process_command_string(char *cmd)
+{
+ pstring line;
+ const char *ptr;
+ int rc = 0;
+
+ /* establish the connection if not already */
+
+ if (!cli) {
+ cli = do_connect(desthost, service);
+ if (!cli)
+ return 0;
+ }
+
+ while (cmd[0] != '\0') {
+ char *p;
+ pstring tok;
+ int i;
+
+ if ((p = strchr_m(cmd, ';')) == 0) {
+ strncpy(line, cmd, 999);
+ line[1000] = '\0';
+ cmd += strlen(cmd);
+ } else {
+ if (p - cmd > 999)
+ p = cmd + 999;
+ strncpy(line, cmd, p - cmd);
+ line[p - cmd] = '\0';
+ cmd = p + 1;
+ }
+
+ /* and get the first part of the command */
+ ptr = line;
+ if (!next_token_nr(&ptr,tok,NULL,sizeof(tok))) continue;
+
+ if ((i = process_tok(tok)) >= 0) {
+ rc = commands[i].fn();
+ } else if (i == -2) {
+ d_printf("%s: command abbreviation ambiguous\n",tok);
+ } else {
+ d_printf("%s: command not found\n",tok);
+ }
+ }
+
+ return rc;
+}
+
+#define MAX_COMPLETIONS 100
+
+typedef struct {
+ pstring dirmask;
+ char **matches;
+ int count, samelen;
+ const char *text;
+ int len;
+} completion_remote_t;
+
+static void completion_remote_filter(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) && (strcmp(f->name, ".") != 0) && (strcmp(f->name, "..") != 0)) {
+ if ((info->dirmask[0] == 0) && !(f->mode & aDIR))
+ info->matches[info->count] = strdup(f->name);
+ else {
+ pstring tmp;
+
+ if (info->dirmask[0] != 0)
+ pstrcpy(tmp, info->dirmask);
+ else
+ tmp[0] = 0;
+ pstrcat(tmp, f->name);
+ if (f->mode & aDIR)
+ pstrcat(tmp, "/");
+ info->matches[info->count] = strdup(tmp);
+ }
+ if (info->matches[info->count] == NULL)
+ return;
+ if (f->mode & aDIR)
+ 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)
+{
+ pstring dirmask;
+ int i;
+ completion_remote_t info = { "", NULL, 1, 0, NULL, 0 };
+
+ /* can't have non-static intialisation on Sun CC, so do it
+ at run time here */
+ info.samelen = len;
+ info.text = text;
+ info.len = len;
+
+ if (len >= PATH_MAX)
+ return(NULL);
+
+ info.matches = (char **)malloc(sizeof(info.matches[0])*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) {
+ strncpy(info.dirmask, text, i+1);
+ info.dirmask[i+1] = 0;
+ pstr_sprintf(dirmask, "%s%*s*", cur_dir, i-1, text);
+ } else
+ pstr_sprintf(dirmask, "%s*", cur_dir);
+
+ if (cli_list(cli, dirmask, aDIR | aSYSTEM | aHIDDEN, completion_remote_filter, &info) < 0)
+ goto cleanup;
+
+ if (info.count == 2)
+ info.matches[0] = strdup(info.matches[1]);
+ else {
+ info.matches[0] = malloc(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, count=1;
+
+ matches = (char **)malloc(sizeof(matches[0])*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(samelen+1);
+ if (!matches[0])
+ goto cleanup;
+ strncpy(matches[0], matches[1], samelen);
+ matches[0][samelen] = 0;
+ }
+ matches[count] = NULL;
+ return matches;
+
+cleanup:
+ for (i = 0; i < count; i++)
+ free(matches[i]);
+
+ free(matches);
+ return NULL;
+ }
+}
+
+/****************************************************************************
+ Make sure we swallow keepalives during idle time.
+****************************************************************************/
+
+static void readline_callback(void)
+{
+ fd_set fds;
+ struct timeval timeout;
+ static time_t last_t;
+ time_t t;
+
+ t = time(NULL);
+
+ if (t - last_t < 5)
+ return;
+
+ last_t = t;
+
+ again:
+
+ if (cli->fd == -1)
+ return;
+
+ FD_ZERO(&fds);
+ FD_SET(cli->fd,&fds);
+
+ timeout.tv_sec = 0;
+ timeout.tv_usec = 0;
+ sys_select_intr(cli->fd+1,&fds,NULL,NULL,&timeout);
+
+ /* We deliberately use receive_smb instead of
+ client_receive_smb as we want to receive
+ session keepalives and then drop them here.
+ */
+ if (FD_ISSET(cli->fd,&fds)) {
+ receive_smb(cli->fd,cli->inbuf,0);
+ goto again;
+ }
+
+ cli_chkpath(cli, "\\");
+}
+
+/****************************************************************************
+ Process commands on stdin.
+****************************************************************************/
+
+static void process_stdin(void)
+{
+ const char *ptr;
+
+ while (1) {
+ pstring tok;
+ pstring the_prompt;
+ char *cline;
+ pstring line;
+ int i;
+
+ /* display a prompt */
+ slprintf(the_prompt, sizeof(the_prompt)-1, "smb: %s> ", cur_dir);
+ cline = smb_readline(the_prompt, readline_callback, completion_fn);
+
+ if (!cline) break;
+
+ pstrcpy(line, cline);
+
+ /* special case - first char is ! */
+ if (*line == '!') {
+ system(line + 1);
+ continue;
+ }
+
+ /* and get the first part of the command */
+ ptr = line;
+ if (!next_token_nr(&ptr,tok,NULL,sizeof(tok))) continue;
+
+ if ((i = process_tok(tok)) >= 0) {
+ commands[i].fn();
+ } else if (i == -2) {
+ d_printf("%s: command abbreviation ambiguous\n",tok);
+ } else {
+ d_printf("%s: command not found\n",tok);
+ }
+ }
+}
+
+/*****************************************************
+ Return a connection to a server.
+*******************************************************/
+
+static struct cli_state *do_connect(const char *server, const char *share)
+{
+ struct cli_state *c;
+ struct nmb_name called, calling;
+ const char *server_n;
+ struct in_addr ip;
+ pstring servicename;
+ char *sharename;
+
+ /* make a copy so we don't modify the global string 'service' */
+ pstrcpy(servicename, share);
+ sharename = servicename;
+ if (*sharename == '\\') {
+ server = sharename+2;
+ sharename = strchr_m(server,'\\');
+ if (!sharename) return NULL;
+ *sharename = 0;
+ sharename++;
+ }
+
+ server_n = server;
+
+ zero_ip(&ip);
+
+ make_nmb_name(&calling, global_myname(), 0x0);
+ make_nmb_name(&called , server, name_type);
+
+ again:
+ zero_ip(&ip);
+ if (have_ip) ip = dest_ip;
+
+ /* have to open a new connection */
+ if (!(c=cli_initialise(NULL)) || (cli_set_port(c, port) != port) ||
+ !cli_connect(c, server_n, &ip)) {
+ d_printf("Connection to %s failed\n", server_n);
+ return NULL;
+ }
+
+ c->protocol = max_protocol;
+ c->use_kerberos = use_kerberos;
+ cli_setup_signing_state(c, cmdline_auth_info.signing_state);
+
+
+ if (!cli_session_request(c, &calling, &called)) {
+ char *p;
+ d_printf("session request to %s failed (%s)\n",
+ called.name, cli_errstr(c));
+ cli_shutdown(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,(" session request ok\n"));
+
+ if (!cli_negprot(c)) {
+ d_printf("protocol negotiation failed\n");
+ cli_shutdown(c);
+ return NULL;
+ }
+
+ if (!got_pass) {
+ char *pass = getpass("Password: ");
+ if (pass) {
+ pstrcpy(password, pass);
+ got_pass = 1;
+ }
+ }
+
+ if (!cli_session_setup(c, username,
+ password, strlen(password),
+ password, strlen(password),
+ lp_workgroup())) {
+ /* if a password was not supplied then try again with a null username */
+ if (password[0] || !username[0] || use_kerberos ||
+ !cli_session_setup(c, "", "", 0, "", 0, lp_workgroup())) {
+ d_printf("session setup failed: %s\n", cli_errstr(c));
+ if (NT_STATUS_V(cli_nt_error(c)) ==
+ NT_STATUS_V(NT_STATUS_MORE_PROCESSING_REQUIRED))
+ d_printf("did you forget to run kinit?\n");
+ cli_shutdown(c);
+ return NULL;
+ }
+ d_printf("Anonymous login successful\n");
+ }
+
+ if (*c->server_domain) {
+ DEBUG(1,("Domain=[%s] OS=[%s] Server=[%s]\n",
+ c->server_domain,c->server_os,c->server_type));
+ } else if (*c->server_os || *c->server_type){
+ DEBUG(1,("OS=[%s] Server=[%s]\n",
+ c->server_os,c->server_type));
+ }
+ DEBUG(4,(" session setup ok\n"));
+
+ if (!cli_send_tconX(c, sharename, "?????",
+ password, strlen(password)+1)) {
+ d_printf("tree connect failed: %s\n", cli_errstr(c));
+ cli_shutdown(c);
+ return NULL;
+ }
+
+ DEBUG(4,(" tconx ok\n"));
+
+ return c;
+}
+
+/****************************************************************************
+ Process commands from the client.
+****************************************************************************/
+
+static int process(char *base_directory)
+{
+ int rc = 0;
+
+ cli = do_connect(desthost, service);
+ if (!cli) {
+ return 1;
+ }
+
+ if (*base_directory) do_cd(base_directory);
+
+ if (cmdstr) {
+ rc = process_command_string(cmdstr);
+ } else {
+ process_stdin();
+ }
+
+ cli_shutdown(cli);
+ return rc;
+}
+
+/****************************************************************************
+ Handle a -L query.
+****************************************************************************/
+
+static int do_host_query(char *query_host)
+{
+ cli = do_connect(query_host, "IPC$");
+ if (!cli)
+ return 1;
+
+ browse_host(True);
+
+ if (port != 139) {
+
+ /* Workgroups simply don't make sense over anything
+ else but port 139... */
+
+ cli_shutdown(cli);
+ port = 139;
+ cli = do_connect(query_host, "IPC$");
+ }
+
+ if (cli == NULL) {
+ d_printf("NetBIOS over TCP disabled -- no workgroup available\n");
+ return 1;
+ }
+
+ list_servers(lp_workgroup());
+
+ cli_shutdown(cli);
+
+ return(0);
+}
+
+
+/****************************************************************************
+ Handle a tar operation.
+****************************************************************************/
+
+static int do_tar_op(char *base_directory)
+{
+ int ret;
+
+ /* do we already have a connection? */
+ if (!cli) {
+ cli = do_connect(desthost, service);
+ if (!cli)
+ return 1;
+ }
+
+ recurse=True;
+
+ if (*base_directory) do_cd(base_directory);
+
+ ret=process_tar();
+
+ cli_shutdown(cli);
+
+ return(ret);
+}
+
+/****************************************************************************
+ Handle a message operation.
+****************************************************************************/
+
+static int do_message_op(void)
+{
+ struct in_addr ip;
+ struct nmb_name called, calling;
+ fstring server_name;
+ char name_type_hex[10];
+
+ make_nmb_name(&calling, global_myname(), 0x0);
+ make_nmb_name(&called , desthost, name_type);
+
+ fstrcpy(server_name, desthost);
+ snprintf(name_type_hex, sizeof(name_type_hex), "#%X", name_type);
+ fstrcat(server_name, name_type_hex);
+
+ zero_ip(&ip);
+ if (have_ip) ip = dest_ip;
+
+ if (!(cli=cli_initialise(NULL)) || (cli_set_port(cli, port) != port) ||
+ !cli_connect(cli, server_name, &ip)) {
+ d_printf("Connection to %s failed\n", desthost);
+ return 1;
+ }
+
+ if (!cli_session_request(cli, &calling, &called)) {
+ d_printf("session request failed\n");
+ cli_shutdown(cli);
+ return 1;
+ }
+
+ send_message();
+ cli_shutdown(cli);
+
+ return 0;
+}
+
+
+/****************************************************************************
+ main program
+****************************************************************************/
+
+ int main(int argc,char *argv[])
+{
+ extern BOOL AllowDebugChange;
+ extern BOOL override_logfile;
+ pstring base_directory;
+ int opt;
+ pstring query_host;
+ BOOL message = False;
+ extern char tar_type;
+ pstring term_code;
+ static const char *new_name_resolve_order = NULL;
+ poptContext pc;
+ char *p;
+ int rc = 0;
+ fstring new_workgroup;
+ struct poptOption long_options[] = {
+ POPT_AUTOHELP
+
+ { "name-resolve", 'R', POPT_ARG_STRING, &new_name_resolve_order, 'R', "Use these name resolution services only", "NAME-RESOLVE-ORDER" },
+ { "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" },
+ { "terminal", 't', POPT_ARG_STRING, NULL, 't', "Terminal I/O code {sjis|euc|jis7|jis8|junet|hex}", "CODE" },
+ { "max-protocol", 'm', POPT_ARG_STRING, NULL, 'm', "Set the max protocol level", "LEVEL" },
+ { "tar", 'T', POPT_ARG_STRING, NULL, 'T', "Command line tar", "<c|x>IXFqgbNan" },
+ { "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, &io_bufsize, 'b', "Changes the transmit/send buffer", "BYTES" },
+ { "port", 'p', POPT_ARG_INT, &port, 'p', "Port to connect to", "PORT" },
+ { "grepable", 'g', POPT_ARG_NONE, NULL, 'g', "Produce grepable output" },
+ POPT_COMMON_SAMBA
+ POPT_COMMON_CONNECTION
+ POPT_COMMON_CREDENTIALS
+ POPT_TABLEEND
+ };
+
+
+#ifdef KANJI
+ pstrcpy(term_code, KANJI);
+#else /* KANJI */
+ *term_code = 0;
+#endif /* KANJI */
+
+ *query_host = 0;
+ *base_directory = 0;
+
+ /* initialize the workgroup name so we can determine whether or
+ not it was set by a command line option */
+
+ set_global_myworkgroup( "" );
+
+ /* set default debug level to 0 regardless of what smb.conf sets */
+ setup_logging( "smbclient", True );
+ DEBUGLEVEL_CLASS[DBGC_ALL] = 1;
+ dbf = x_stderr;
+ x_setbuf( x_stderr, NULL );
+
+ pc = poptGetContext("smbclient", argc, (const char **) argv, long_options,
+ POPT_CONTEXT_KEEP_FIRST);
+ poptSetOtherOptionHelp(pc, "service <password>");
+
+ in_client = True; /* Make sure that we tell lp_load we are */
+
+ 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;
+ pstrcpy(desthost,poptGetOptArg(pc));
+ if( 0 == port )
+ port = 139;
+ message = True;
+ break;
+ case 'I':
+ {
+ dest_ip = *interpret_addr2(poptGetOptArg(pc));
+ if (is_zero_ip(dest_ip))
+ exit(1);
+ have_ip = True;
+ }
+ break;
+ case 'E':
+ dbf = x_stderr;
+ display_set_stderr();
+ break;
+
+ case 'L':
+ pstrcpy(query_host, poptGetOptArg(pc));
+ break;
+ case 't':
+ pstrcpy(term_code, poptGetOptArg(pc));
+ break;
+ case 'm':
+ max_protocol = interpret_protocol(poptGetOptArg(pc), max_protocol);
+ break;
+ case 'T':
+ /* We must use old option processing for this. Find the
+ * position of the -T option in the raw argv[]. */
+ {
+ int i, optnum;
+ for (i = 1; i < argc; i++) {
+ if (strncmp("-T", argv[i],2)==0)
+ break;
+ }
+ i++;
+ if (!(optnum = tar_parseargs(argc, argv, poptGetOptArg(pc), i))) {
+ poptPrintUsage(pc, stderr, 0);
+ exit(1);
+ }
+ /* Now we must eat (optnum - i) options - they have
+ * been processed by tar_parseargs().
+ */
+ optnum -= i;
+ for (i = 0; i < optnum; i++)
+ poptGetOptArg(pc);
+ }
+ break;
+ case 'D':
+ pstrcpy(base_directory,poptGetOptArg(pc));
+ break;
+ case 'g':
+ grepable=True;
+ break;
+ }
+ }
+
+ poptGetArg(pc);
+
+ /*
+ * Don't load debug level from smb.conf. It should be
+ * set by cmdline arg or remain default (0)
+ */
+ AllowDebugChange = False;
+
+ /* save the workgroup...
+
+ FIXME!! do we need to do this for other options as well
+ (or maybe a generic way to keep lp_load() from overwriting
+ everything)? */
+
+ fstrcpy( new_workgroup, lp_workgroup() );
+
+ if ( override_logfile )
+ setup_logging( lp_logfile(), False );
+
+ if (!lp_load(dyn_CONFIGFILE,True,False,False)) {
+ fprintf(stderr, "%s: Can't load %s - run testparm to debug it\n",
+ argv[0], dyn_CONFIGFILE);
+ }
+
+ load_interfaces();
+
+ if ( strlen(new_workgroup) != 0 )
+ set_global_myworkgroup( new_workgroup );
+
+ if(poptPeekArg(pc)) {
+ pstrcpy(service,poptGetArg(pc));
+ /* Convert any '/' characters in the service name to '\' characters */
+ string_replace(service, '/','\\');
+
+ if (count_chars(service,'\\') < 3) {
+ d_printf("\n%s: Not enough '\\' characters in service\n",service);
+ poptPrintUsage(pc, stderr, 0);
+ exit(1);
+ }
+ }
+
+ if (poptPeekArg(pc) && !cmdline_auth_info.got_pass) {
+ cmdline_auth_info.got_pass = True;
+ pstrcpy(cmdline_auth_info.password,poptGetArg(pc));
+ }
+
+ init_names();
+
+ if(new_name_resolve_order)
+ lp_set_name_resolve_order(new_name_resolve_order);
+
+ if (!tar_type && !*query_host && !*service && !message) {
+ poptPrintUsage(pc, stderr, 0);
+ exit(1);
+ }
+
+ poptFreeContext(pc);
+
+ pstrcpy(username, cmdline_auth_info.username);
+ pstrcpy(password, cmdline_auth_info.password);
+
+ use_kerberos = cmdline_auth_info.use_kerberos;
+ got_pass = cmdline_auth_info.got_pass;
+
+ DEBUG(3,("Client started (version %s).\n", SAMBA_VERSION_STRING));
+
+ if (tar_type) {
+ if (cmdstr)
+ process_command_string(cmdstr);
+ return do_tar_op(base_directory);
+ }
+
+ if (*query_host) {
+ char *qhost = query_host;
+ char *slash;
+
+ while (*qhost == '\\' || *qhost == '/')
+ qhost++;
+
+ if ((slash = strchr_m(qhost, '/'))
+ || (slash = strchr_m(qhost, '\\'))) {
+ *slash = 0;
+ }
+
+ if ((p=strchr_m(qhost, '#'))) {
+ *p = 0;
+ p++;
+ sscanf(p, "%x", &name_type);
+ }
+
+ return do_host_query(qhost);
+ }
+
+ if (message) {
+ return do_message_op();
+ }
+
+ if (process(base_directory)) {
+ return 1;
+ }
+
+ return rc;
+}
diff --git a/source/client/clitar.c b/source/client/clitar.c
new file mode 100644
index 00000000000..e43b3e4cc50
--- /dev/null
+++ b/source/client/clitar.c
@@ -0,0 +1,1807 @@
+/*
+ Unix SMB/CIFS implementation.
+ Tar Extensions
+ Copyright (C) Ricky Poulten 1995-1998
+ Copyright (C) Richard Sharpe 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 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.
+*/
+/* The following changes developed by Richard Sharpe for Canon Information
+ Systems Research Australia (CISRA)
+
+ 1. Restore can now restore files with long file names
+ 2. Save now saves directory information so that we can restore
+ directory creation times
+ 3. tar now accepts both UNIX path names and DOS path names. I prefer
+ those lovely /'s to those UGLY \'s :-)
+ 4. the files to exclude can be specified as a regular expression by adding
+ an r flag to the other tar flags. Eg:
+
+ -TcrX file.tar "*.(obj|exe)"
+
+ will skip all .obj and .exe files
+*/
+
+
+#include "includes.h"
+#include "clitar.h"
+#include "../client/client_proto.h"
+
+static int clipfind(char **aret, int ret, char *tok);
+
+typedef struct file_info_struct file_info2;
+
+struct file_info_struct {
+ SMB_BIG_UINT size;
+ uint16 mode;
+ uid_t uid;
+ gid_t gid;
+ /* These times are normally kept in GMT */
+ time_t mtime;
+ time_t atime;
+ time_t ctime;
+ char *name; /* This is dynamically allocate */
+
+ file_info2 *next, *prev; /* Used in the stack ... */
+};
+
+typedef struct {
+ file_info2 *top;
+ int items;
+} stack;
+
+#define SEPARATORS " \t\n\r"
+extern struct cli_state *cli;
+
+/* These defines are for the do_setrattr routine, to indicate
+ * setting and reseting of file attributes in the function call */
+#define ATTRSET 1
+#define ATTRRESET 0
+
+static uint16 attribute = aDIR | aSYSTEM | aHIDDEN;
+
+#ifndef CLIENT_TIMEOUT
+#define CLIENT_TIMEOUT (30*1000)
+#endif
+
+static char *tarbuf, *buffer_p;
+static int tp, ntarf, tbufsiz;
+static double ttarf;
+/* Incremental mode */
+static BOOL tar_inc=False;
+/* Reset archive bit */
+static BOOL tar_reset=False;
+/* Include / exclude mode (true=include, false=exclude) */
+static BOOL tar_excl=True;
+/* use regular expressions for search on file names */
+static BOOL tar_re_search=False;
+#ifdef HAVE_REGEX_H
+regex_t *preg;
+#endif
+/* Do not dump anything, just calculate sizes */
+static BOOL dry_run=False;
+/* Dump files with System attribute */
+static BOOL tar_system=True;
+/* Dump files with Hidden attribute */
+static BOOL tar_hidden=True;
+/* Be noisy - make a catalogue */
+static BOOL tar_noisy=True;
+static BOOL tar_real_noisy=False; /* Don't want to be really noisy by default */
+
+char tar_type='\0';
+static char **cliplist=NULL;
+static int clipn=0;
+static BOOL must_free_cliplist = False;
+
+extern file_info def_finfo;
+extern BOOL lowercase;
+extern uint16 cnum;
+extern BOOL readbraw_supported;
+extern int max_xmit;
+extern pstring cur_dir;
+extern int get_total_time_ms;
+extern int get_total_size;
+
+static int blocksize=20;
+static int tarhandle;
+
+static void writetarheader(int f, const char *aname, SMB_BIG_UINT size, time_t mtime,
+ const char *amode, unsigned char ftype);
+static void do_atar(char *rname,char *lname,file_info *finfo1);
+static void do_tar(file_info *finfo);
+static void oct_it(SMB_BIG_UINT value, int ndgs, char *p);
+static void fixtarname(char *tptr, const char *fp, size_t l);
+static int dotarbuf(int f, char *b, int n);
+static void dozerobuf(int f, int n);
+static void dotareof(int f);
+static void initarbuf(void);
+
+/* restore functions */
+static long readtarheader(union hblock *hb, file_info2 *finfo, char *prefix);
+static long unoct(char *p, int ndgs);
+static void do_tarput(void);
+static void unfixtarname(char *tptr, char *fp, int l, BOOL first);
+
+/*
+ * tar specific utitlities
+ */
+
+/*******************************************************************
+Create a string of size size+1 (for the null)
+*******************************************************************/
+
+static char *string_create_s(int size)
+{
+ char *tmp;
+
+ tmp = (char *)malloc(size+1);
+
+ if (tmp == NULL) {
+ DEBUG(0, ("Out of memory in string_create_s\n"));
+ }
+
+ return(tmp);
+}
+
+/****************************************************************************
+Write a tar header to buffer
+****************************************************************************/
+
+static void writetarheader(int f, const char *aname, SMB_BIG_UINT size, time_t mtime,
+ const char *amode, unsigned char ftype)
+{
+ union hblock hb;
+ int i, chk, l;
+ char *jp;
+
+ DEBUG(5, ("WriteTarHdr, Type = %c, Size= %.0f, Name = %s\n", ftype, (double)size, aname));
+
+ memset(hb.dummy, 0, sizeof(hb.dummy));
+
+ l=strlen(aname);
+ /* We will be prepending a '.' in fixtarheader so use +2 to
+ * take care of the . and terminating zero. JRA.
+ */
+ if (l+2 >= NAMSIZ) {
+ /* write a GNU tar style long header */
+ char *b;
+ b = (char *)malloc(l+TBLOCK+100);
+ if (!b) {
+ DEBUG(0,("out of memory\n"));
+ exit(1);
+ }
+ writetarheader(f, "/./@LongLink", l+2, 0, " 0 \0", 'L');
+ memset(b, 0, l+TBLOCK+100);
+ fixtarname(b, aname, l+2);
+ i = strlen(b)+1;
+ DEBUG(5, ("File name in tar file: %s, size=%d, \n", b, (int)strlen(b)));
+ dotarbuf(f, b, TBLOCK*(((i-1)/TBLOCK)+1));
+ SAFE_FREE(b);
+ }
+
+ fixtarname(hb.dbuf.name, aname, (l+2 >= NAMSIZ) ? NAMSIZ : l + 2);
+
+ if (lowercase)
+ strlower_m(hb.dbuf.name);
+
+ /* write out a "standard" tar format header */
+
+ hb.dbuf.name[NAMSIZ-1]='\0';
+ safe_strcpy(hb.dbuf.mode, amode, sizeof(hb.dbuf.mode)-1);
+ oct_it((SMB_BIG_UINT)0, 8, hb.dbuf.uid);
+ oct_it((SMB_BIG_UINT)0, 8, hb.dbuf.gid);
+ oct_it((SMB_BIG_UINT) size, 13, hb.dbuf.size);
+ if (size > (SMB_BIG_UINT)077777777777LL) {
+
+ /* This is a non-POSIX compatible extention to store files
+ greater than 8GB. */
+
+ memset(hb.dbuf.size, 0, 4);
+ hb.dbuf.size[0]=128;
+ for (i = 8, jp=(char*)&size; i; i--)
+ hb.dbuf.size[i+3] = *(jp++);
+ }
+ oct_it((SMB_BIG_UINT) mtime, 13, hb.dbuf.mtime);
+ memcpy(hb.dbuf.chksum, " ", sizeof(hb.dbuf.chksum));
+ memset(hb.dbuf.linkname, 0, NAMSIZ);
+ hb.dbuf.linkflag=ftype;
+
+ for (chk=0, i=sizeof(hb.dummy), jp=hb.dummy; --i>=0;)
+ chk+=(0xFF & *jp++);
+
+ oct_it((SMB_BIG_UINT) chk, 8, hb.dbuf.chksum);
+ hb.dbuf.chksum[6] = '\0';
+
+ (void) dotarbuf(f, hb.dummy, sizeof(hb.dummy));
+}
+
+/****************************************************************************
+Read a tar header into a hblock structure, and validate
+***************************************************************************/
+
+static long readtarheader(union hblock *hb, file_info2 *finfo, char *prefix)
+{
+ long chk, fchk;
+ int i;
+ char *jp;
+
+ /*
+ * read in a "standard" tar format header - we're not that interested
+ * in that many fields, though
+ */
+
+ /* check the checksum */
+ for (chk=0, i=sizeof(hb->dummy), jp=hb->dummy; --i>=0;)
+ chk+=(0xFF & *jp++);
+
+ if (chk == 0)
+ return chk;
+
+ /* compensate for blanks in chksum header */
+ for (i=sizeof(hb->dbuf.chksum), jp=hb->dbuf.chksum; --i>=0;)
+ chk-=(0xFF & *jp++);
+
+ chk += ' ' * sizeof(hb->dbuf.chksum);
+
+ fchk=unoct(hb->dbuf.chksum, sizeof(hb->dbuf.chksum));
+
+ DEBUG(5, ("checksum totals chk=%ld fchk=%ld chksum=%s\n",
+ chk, fchk, hb->dbuf.chksum));
+
+ if (fchk != chk) {
+ DEBUG(0, ("checksums don't match %ld %ld\n", fchk, chk));
+ dump_data(5, (char *)hb - TBLOCK, TBLOCK *3);
+ return -1;
+ }
+
+ if ((finfo->name = string_create_s(strlen(prefix) + strlen(hb -> dbuf.name) + 3)) == NULL) {
+ DEBUG(0, ("Out of space creating file_info2 for %s\n", hb -> dbuf.name));
+ return(-1);
+ }
+
+ safe_strcpy(finfo->name, prefix, strlen(prefix) + strlen(hb -> dbuf.name) + 3);
+
+ /* use l + 1 to do the null too; do prefix - prefcnt to zap leading slash */
+ unfixtarname(finfo->name + strlen(prefix), hb->dbuf.name,
+ strlen(hb->dbuf.name) + 1, True);
+
+ /* can't handle some links at present */
+ if ((hb->dbuf.linkflag != '0') && (hb -> dbuf.linkflag != '5')) {
+ if (hb->dbuf.linkflag == 0) {
+ DEBUG(6, ("Warning: NULL link flag (gnu tar archive ?) %s\n",
+ finfo->name));
+ } else {
+ if (hb -> dbuf.linkflag == 'L') { /* We have a longlink */
+ /* Do nothing here at the moment. do_tarput will handle this
+ as long as the longlink gets back to it, as it has to advance
+ the buffer pointer, etc */
+ } else {
+ DEBUG(0, ("this tar file appears to contain some kind \
+of link other than a GNUtar Longlink - ignoring\n"));
+ return -2;
+ }
+ }
+ }
+
+ if ((unoct(hb->dbuf.mode, sizeof(hb->dbuf.mode)) & S_IFDIR) ||
+ (*(finfo->name+strlen(finfo->name)-1) == '\\')) {
+ finfo->mode=aDIR;
+ } else {
+ finfo->mode=0; /* we don't care about mode at the moment, we'll
+ * just make it a regular file */
+ }
+
+ /*
+ * Bug fix by richard@sj.co.uk
+ *
+ * REC: restore times correctly (as does tar)
+ * We only get the modification time of the file; set the creation time
+ * from the mod. time, and the access time to current time
+ */
+ finfo->mtime = finfo->ctime = strtol(hb->dbuf.mtime, NULL, 8);
+ finfo->atime = time(NULL);
+ finfo->size = unoct(hb->dbuf.size, sizeof(hb->dbuf.size));
+
+ return True;
+}
+
+/****************************************************************************
+Write out the tar buffer to tape or wherever
+****************************************************************************/
+
+static int dotarbuf(int f, char *b, int n)
+{
+ int fail=1, writ=n;
+
+ if (dry_run) {
+ return writ;
+ }
+ /* This routine and the next one should be the only ones that do write()s */
+ if (tp + n >= tbufsiz) {
+ int diff;
+
+ diff=tbufsiz-tp;
+ memcpy(tarbuf + tp, b, diff);
+ fail=fail && (1+write(f, tarbuf, tbufsiz));
+ n-=diff;
+ b+=diff;
+ tp=0;
+
+ while (n >= tbufsiz) {
+ fail=fail && (1 + write(f, b, tbufsiz));
+ n-=tbufsiz;
+ b+=tbufsiz;
+ }
+ }
+
+ if (n>0) {
+ memcpy(tarbuf+tp, b, n);
+ tp+=n;
+ }
+
+ return(fail ? writ : 0);
+}
+
+/****************************************************************************
+Write zeros to buffer / tape
+****************************************************************************/
+
+static void dozerobuf(int f, int n)
+{
+ /* short routine just to write out n zeros to buffer -
+ * used to round files to nearest block
+ * and to do tar EOFs */
+
+ if (dry_run)
+ return;
+
+ if (n+tp >= tbufsiz) {
+ memset(tarbuf+tp, 0, tbufsiz-tp);
+ write(f, tarbuf, tbufsiz);
+ memset(tarbuf, 0, (tp+=n-tbufsiz));
+ } else {
+ memset(tarbuf+tp, 0, n);
+ tp+=n;
+ }
+}
+
+/****************************************************************************
+Malloc tape buffer
+****************************************************************************/
+
+static void initarbuf(void)
+{
+ /* initialize tar buffer */
+ tbufsiz=blocksize*TBLOCK;
+ tarbuf=malloc(tbufsiz); /* FIXME: We might not get the buffer */
+
+ /* reset tar buffer pointer and tar file counter and total dumped */
+ tp=0; ntarf=0; ttarf=0;
+}
+
+/****************************************************************************
+Write two zero blocks at end of file
+****************************************************************************/
+
+static void dotareof(int f)
+{
+ SMB_STRUCT_STAT stbuf;
+ /* Two zero blocks at end of file, write out full buffer */
+
+ if (dry_run)
+ return;
+
+ (void) dozerobuf(f, TBLOCK);
+ (void) dozerobuf(f, TBLOCK);
+
+ if (sys_fstat(f, &stbuf) == -1) {
+ DEBUG(0, ("Couldn't stat file handle\n"));
+ return;
+ }
+
+ /* Could be a pipe, in which case S_ISREG should fail,
+ * and we should write out at full size */
+ if (tp > 0)
+ write(f, tarbuf, S_ISREG(stbuf.st_mode) ? tp : tbufsiz);
+}
+
+/****************************************************************************
+(Un)mangle DOS pathname, make nonabsolute
+****************************************************************************/
+
+static void fixtarname(char *tptr, const char *fp, size_t l)
+{
+ /* add a '.' to start of file name, convert from ugly dos \'s in path
+ * to lovely unix /'s :-} */
+ *tptr++='.';
+ l--;
+
+ StrnCpy(tptr, fp, l-1);
+ string_replace(tptr, '\\', '/');
+}
+
+/****************************************************************************
+Convert from decimal to octal string
+****************************************************************************/
+
+static void oct_it (SMB_BIG_UINT value, int ndgs, char *p)
+{
+ /* Converts long to octal string, pads with leading zeros */
+
+ /* skip final null, but do final space */
+ --ndgs;
+ p[--ndgs] = ' ';
+
+ /* Loop does at least one digit */
+ do {
+ p[--ndgs] = '0' + (char) (value & 7);
+ value >>= 3;
+ } while (ndgs > 0 && value != 0);
+
+ /* Do leading zeros */
+ while (ndgs > 0)
+ p[--ndgs] = '0';
+}
+
+/****************************************************************************
+Convert from octal string to long
+***************************************************************************/
+
+static long unoct(char *p, int ndgs)
+{
+ long value=0;
+ /* Converts octal string to long, ignoring any non-digit */
+
+ while (--ndgs) {
+ if (isdigit((int)*p))
+ value = (value << 3) | (long) (*p - '0');
+
+ p++;
+ }
+
+ return value;
+}
+
+/****************************************************************************
+Compare two strings in a slash insensitive way, allowing s1 to match s2
+if s1 is an "initial" string (up to directory marker). Thus, if s2 is
+a file in any subdirectory of s1, declare a match.
+***************************************************************************/
+
+static int strslashcmp(char *s1, char *s2)
+{
+ char *s1_0=s1;
+
+ while(*s1 && *s2 && (*s1 == *s2 || tolower(*s1) == tolower(*s2) ||
+ (*s1 == '\\' && *s2=='/') || (*s1 == '/' && *s2=='\\'))) {
+ s1++; s2++;
+ }
+
+ /* if s1 has a trailing slash, it compared equal, so s1 is an "initial"
+ string of s2.
+ */
+ if (!*s1 && s1 != s1_0 && (*(s1-1) == '/' || *(s1-1) == '\\'))
+ return 0;
+
+ /* ignore trailing slash on s1 */
+ if (!*s2 && (*s1 == '/' || *s1 == '\\') && !*(s1+1))
+ return 0;
+
+ /* check for s1 is an "initial" string of s2 */
+ if ((*s2 == '/' || *s2 == '\\') && !*s1)
+ return 0;
+
+ return *s1-*s2;
+}
+
+/****************************************************************************
+Ensure a remote path exists (make if necessary)
+***************************************************************************/
+
+static BOOL ensurepath(char *fname)
+{
+ /* *must* be called with buffer ready malloc'ed */
+ /* ensures path exists */
+
+ char *partpath, *ffname;
+ char *p=fname, *basehack;
+
+ DEBUG(5, ( "Ensurepath called with: %s\n", fname));
+
+ partpath = string_create_s(strlen(fname));
+ ffname = string_create_s(strlen(fname));
+
+ if ((partpath == NULL) || (ffname == NULL)){
+ DEBUG(0, ("Out of memory in ensurepath: %s\n", fname));
+ return(False);
+ }
+
+ *partpath = 0;
+
+ /* fname copied to ffname so can strtok */
+
+ safe_strcpy(ffname, fname, strlen(fname));
+
+ /* do a `basename' on ffname, so don't try and make file name directory */
+ if ((basehack=strrchr_m(ffname, '\\')) == NULL)
+ return True;
+ else
+ *basehack='\0';
+
+ p=strtok(ffname, "\\");
+
+ while (p) {
+ safe_strcat(partpath, p, strlen(fname) + 1);
+
+ if (!cli_chkpath(cli, partpath)) {
+ if (!cli_mkdir(cli, partpath)) {
+ DEBUG(0, ("Error mkdirhiering\n"));
+ return False;
+ } else {
+ DEBUG(3, ("mkdirhiering %s\n", partpath));
+ }
+ }
+
+ safe_strcat(partpath, "\\", strlen(fname) + 1);
+ p = strtok(NULL,"/\\");
+ }
+
+ return True;
+}
+
+static int padit(char *buf, int bufsize, int padsize)
+{
+ int berr= 0;
+ int bytestowrite;
+
+ DEBUG(5, ("Padding with %d zeros\n", padsize));
+ memset(buf, 0, bufsize);
+ while( !berr && padsize > 0 ) {
+ bytestowrite= MIN(bufsize, padsize);
+ berr = dotarbuf(tarhandle, buf, bytestowrite) != bytestowrite;
+ padsize -= bytestowrite;
+ }
+
+ return berr;
+}
+
+static void do_setrattr(char *name, uint16 attr, int set)
+{
+ uint16 oldattr;
+
+ if (!cli_getatr(cli, name, &oldattr, NULL, NULL)) return;
+
+ if (set == ATTRSET) {
+ attr |= oldattr;
+ } else {
+ attr = oldattr & ~attr;
+ }
+
+ if (!cli_setatr(cli, name, attr, 0)) {
+ DEBUG(1,("setatr failed: %s\n", cli_errstr(cli)));
+ }
+}
+
+/****************************************************************************
+append one remote file to the tar file
+***************************************************************************/
+
+static void do_atar(char *rname,char *lname,file_info *finfo1)
+{
+ int fnum;
+ SMB_BIG_UINT nread=0;
+ char ftype;
+ file_info2 finfo;
+ BOOL close_done = False;
+ BOOL shallitime=True;
+ char data[65520];
+ int read_size = 65520;
+ int datalen=0;
+
+ struct timeval tp_start;
+
+ GetTimeOfDay(&tp_start);
+
+ ftype = '0'; /* An ordinary file ... */
+
+ if (finfo1) {
+ finfo.size = finfo1 -> size;
+ finfo.mode = finfo1 -> mode;
+ finfo.uid = finfo1 -> uid;
+ finfo.gid = finfo1 -> gid;
+ finfo.mtime = finfo1 -> mtime;
+ finfo.atime = finfo1 -> atime;
+ finfo.ctime = finfo1 -> ctime;
+ finfo.name = finfo1 -> name;
+ } else {
+ finfo.size = def_finfo.size;
+ finfo.mode = def_finfo.mode;
+ finfo.uid = def_finfo.uid;
+ finfo.gid = def_finfo.gid;
+ finfo.mtime = def_finfo.mtime;
+ finfo.atime = def_finfo.atime;
+ finfo.ctime = def_finfo.ctime;
+ finfo.name = def_finfo.name;
+ }
+
+ if (dry_run) {
+ DEBUG(3,("skipping file %s of size %12.0f bytes\n", finfo.name,
+ (double)finfo.size));
+ shallitime=0;
+ ttarf+=finfo.size + TBLOCK - (finfo.size % TBLOCK);
+ ntarf++;
+ return;
+ }
+
+ fnum = cli_open(cli, rname, O_RDONLY, DENY_NONE);
+
+ dos_clean_name(rname);
+
+ if (fnum == -1) {
+ DEBUG(0,("%s opening remote file %s (%s)\n",
+ cli_errstr(cli),rname, cur_dir));
+ return;
+ }
+
+ finfo.name = string_create_s(strlen(rname));
+ if (finfo.name == NULL) {
+ DEBUG(0, ("Unable to allocate space for finfo.name in do_atar\n"));
+ return;
+ }
+
+ safe_strcpy(finfo.name,rname, strlen(rname));
+ if (!finfo1) {
+ if (!cli_getattrE(cli, fnum, &finfo.mode, &finfo.size, NULL, &finfo.atime, &finfo.mtime)) {
+ DEBUG(0, ("getattrE: %s\n", cli_errstr(cli)));
+ return;
+ }
+ finfo.ctime = finfo.mtime;
+ }
+
+ DEBUG(3,("file %s attrib 0x%X\n",finfo.name,finfo.mode));
+
+ if (tar_inc && !(finfo.mode & aARCH)) {
+ DEBUG(4, ("skipping %s - archive bit not set\n", finfo.name));
+ shallitime=0;
+ } else if (!tar_system && (finfo.mode & aSYSTEM)) {
+ DEBUG(4, ("skipping %s - system bit is set\n", finfo.name));
+ shallitime=0;
+ } else if (!tar_hidden && (finfo.mode & aHIDDEN)) {
+ DEBUG(4, ("skipping %s - hidden bit is set\n", finfo.name));
+ shallitime=0;
+ } else {
+ DEBUG(3,("getting file %s of size %.0f bytes as a tar file %s",
+ finfo.name, (double)finfo.size, lname));
+
+ /* write a tar header, don't bother with mode - just set to 100644 */
+ writetarheader(tarhandle, rname, finfo.size, finfo.mtime, "100644 \0", ftype);
+
+ while (nread < finfo.size && !close_done) {
+
+ DEBUG(3,("nread=%.0f\n",(double)nread));
+
+ datalen = cli_read(cli, fnum, data, nread, read_size);
+
+ if (datalen == -1) {
+ DEBUG(0,("Error reading file %s : %s\n", rname, cli_errstr(cli)));
+ break;
+ }
+
+ nread += datalen;
+
+ /* if file size has increased since we made file size query, truncate
+ read so tar header for this file will be correct.
+ */
+
+ if (nread > finfo.size) {
+ datalen -= nread - finfo.size;
+ DEBUG(0,("File size change - truncating %s to %.0f bytes\n",
+ finfo.name, (double)finfo.size));
+ }
+
+ /* add received bits of file to buffer - dotarbuf will
+ * write out in 512 byte intervals */
+
+ if (dotarbuf(tarhandle,data,datalen) != datalen) {
+ DEBUG(0,("Error writing to tar file - %s\n", strerror(errno)));
+ break;
+ }
+
+ if (datalen == 0) {
+ DEBUG(0,("Error reading file %s. Got 0 bytes\n", rname));
+ break;
+ }
+
+ datalen=0;
+ }
+
+ /* pad tar file with zero's if we couldn't get entire file */
+ if (nread < finfo.size) {
+ DEBUG(0, ("Didn't get entire file. size=%.0f, nread=%d\n",
+ (double)finfo.size, (int)nread));
+ if (padit(data, sizeof(data), finfo.size - nread))
+ DEBUG(0,("Error writing tar file - %s\n", strerror(errno)));
+ }
+
+ /* round tar file to nearest block */
+ if (finfo.size % TBLOCK)
+ dozerobuf(tarhandle, TBLOCK - (finfo.size % TBLOCK));
+
+ ttarf+=finfo.size + TBLOCK - (finfo.size % TBLOCK);
+ ntarf++;
+ }
+
+ cli_close(cli, fnum);
+
+ if (shallitime) {
+ struct timeval tp_end;
+ int this_time;
+
+ /* if shallitime is true then we didn't skip */
+ if (tar_reset && !dry_run)
+ (void) do_setrattr(finfo.name, aARCH, ATTRRESET);
+
+ 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 += finfo.size;
+
+ if (tar_noisy) {
+ DEBUG(0, ("%12.0f (%7.1f kb/s) %s\n",
+ (double)finfo.size, finfo.size / MAX(0.001, (1.024*this_time)),
+ finfo.name));
+ }
+
+ /* Thanks to Carel-Jan Engel (ease@mail.wirehub.nl) for this one */
+ DEBUG(3,("(%g kb/s) (average %g kb/s)\n",
+ finfo.size / MAX(0.001, (1.024*this_time)),
+ get_total_size / MAX(0.001, (1.024*get_total_time_ms))));
+ }
+}
+
+/****************************************************************************
+Append single file to tar file (or not)
+***************************************************************************/
+
+static void do_tar(file_info *finfo)
+{
+ pstring rname;
+
+ if (strequal(finfo->name,"..") || strequal(finfo->name,"."))
+ return;
+
+ /* Is it on the exclude list ? */
+ if (!tar_excl && clipn) {
+ pstring exclaim;
+
+ DEBUG(5, ("Excl: strlen(cur_dir) = %d\n", (int)strlen(cur_dir)));
+
+ pstrcpy(exclaim, cur_dir);
+ *(exclaim+strlen(exclaim)-1)='\0';
+
+ pstrcat(exclaim, "\\");
+ pstrcat(exclaim, finfo->name);
+
+ DEBUG(5, ("...tar_re_search: %d\n", tar_re_search));
+
+ if ((!tar_re_search && clipfind(cliplist, clipn, exclaim)) ||
+#ifdef HAVE_REGEX_H
+ (tar_re_search && !regexec(preg, exclaim, 0, NULL, 0))) {
+#else
+ (tar_re_search && mask_match_list(exclaim, cliplist, clipn, True))) {
+#endif
+ DEBUG(3,("Skipping file %s\n", exclaim));
+ return;
+ }
+ }
+
+ if (finfo->mode & aDIR) {
+ pstring saved_curdir;
+ pstring mtar_mask;
+
+ pstrcpy(saved_curdir, cur_dir);
+
+ DEBUG(5, ("Sizeof(cur_dir)=%d, strlen(cur_dir)=%d, \
+strlen(finfo->name)=%d\nname=%s,cur_dir=%s\n",
+ (int)sizeof(cur_dir), (int)strlen(cur_dir),
+ (int)strlen(finfo->name), finfo->name, cur_dir));
+
+ pstrcat(cur_dir,finfo->name);
+ pstrcat(cur_dir,"\\");
+
+ DEBUG(5, ("Writing a dir, Name = %s\n", cur_dir));
+
+ /* write a tar directory, don't bother with mode - just set it to
+ * 40755 */
+ writetarheader(tarhandle, cur_dir, 0, finfo->mtime, "040755 \0", '5');
+ if (tar_noisy) {
+ DEBUG(0,(" directory %s\n", cur_dir));
+ }
+ ntarf++; /* Make sure we have a file on there */
+ pstrcpy(mtar_mask,cur_dir);
+ pstrcat(mtar_mask,"*");
+ DEBUG(5, ("Doing list with mtar_mask: %s\n", mtar_mask));
+ do_list(mtar_mask, attribute, do_tar, False, True);
+ pstrcpy(cur_dir,saved_curdir);
+ } else {
+ pstrcpy(rname,cur_dir);
+ pstrcat(rname,finfo->name);
+ do_atar(rname,finfo->name,finfo);
+ }
+}
+
+/****************************************************************************
+Convert from UNIX to DOS file names
+***************************************************************************/
+
+static void unfixtarname(char *tptr, char *fp, int l, BOOL first)
+{
+ /* remove '.' from start of file name, convert from unix /'s to
+ * dos \'s in path. Kill any absolute path names. But only if first!
+ */
+
+ DEBUG(5, ("firstb=%lX, secondb=%lX, len=%i\n", (long)tptr, (long)fp, l));
+
+ if (first) {
+ if (*fp == '.') {
+ fp++;
+ l--;
+ }
+ if (*fp == '\\' || *fp == '/') {
+ fp++;
+ l--;
+ }
+ }
+
+ safe_strcpy(tptr, fp, l);
+ string_replace(tptr, '/', '\\');
+}
+
+/****************************************************************************
+Move to the next block in the buffer, which may mean read in another set of
+blocks. FIXME, we should allow more than one block to be skipped.
+****************************************************************************/
+
+static int next_block(char *ltarbuf, char **bufferp, int bufsiz)
+{
+ int bufread, total = 0;
+
+ DEBUG(5, ("Advancing to next block: %0lx\n", (unsigned long)*bufferp));
+ *bufferp += TBLOCK;
+ total = TBLOCK;
+
+ if (*bufferp >= (ltarbuf + bufsiz)) {
+
+ DEBUG(5, ("Reading more data into ltarbuf ...\n"));
+
+ /*
+ * Bugfix from Bob Boehmer <boehmer@worldnet.att.net>
+ * Fixes bug where read can return short if coming from
+ * a pipe.
+ */
+
+ bufread = read(tarhandle, ltarbuf, bufsiz);
+ total = bufread;
+
+ while (total < bufsiz) {
+ if (bufread < 0) { /* An error, return false */
+ return (total > 0 ? -2 : bufread);
+ }
+ if (bufread == 0) {
+ if (total <= 0) {
+ return -2;
+ }
+ break;
+ }
+ bufread = read(tarhandle, &ltarbuf[total], bufsiz - total);
+ total += bufread;
+ }
+
+ DEBUG(5, ("Total bytes read ... %i\n", total));
+
+ *bufferp = ltarbuf;
+ }
+
+ return(total);
+}
+
+/* Skip a file, even if it includes a long file name? */
+static int skip_file(int skipsize)
+{
+ int dsize = skipsize;
+
+ DEBUG(5, ("Skiping file. Size = %i\n", skipsize));
+
+ /* FIXME, we should skip more than one block at a time */
+
+ while (dsize > 0) {
+ if (next_block(tarbuf, &buffer_p, tbufsiz) <= 0) {
+ DEBUG(0, ("Empty file, short tar file, or read error: %s\n", strerror(errno)));
+ return(False);
+ }
+ dsize -= TBLOCK;
+ }
+
+ return(True);
+}
+
+/*************************************************************
+ Get a file from the tar file and store it.
+ When this is called, tarbuf already contains the first
+ file block. This is a bit broken & needs fixing.
+**************************************************************/
+
+static int get_file(file_info2 finfo)
+{
+ int fnum = -1, pos = 0, dsize = 0, bpos = 0;
+ SMB_BIG_UINT rsize = 0;
+
+ DEBUG(5, ("get_file: file: %s, size %.0f\n", finfo.name, (double)finfo.size));
+
+ if (ensurepath(finfo.name) &&
+ (fnum=cli_open(cli, finfo.name, O_RDWR|O_CREAT|O_TRUNC, DENY_NONE)) == -1) {
+ DEBUG(0, ("abandoning restore\n"));
+ return(False);
+ }
+
+ /* read the blocks from the tar file and write to the remote file */
+
+ rsize = finfo.size; /* This is how much to write */
+
+ while (rsize > 0) {
+
+ /* We can only write up to the end of the buffer */
+ dsize = MIN(tbufsiz - (buffer_p - tarbuf) - bpos, 65520); /* Calculate the size to write */
+ dsize = MIN(dsize, rsize); /* Should be only what is left */
+ DEBUG(5, ("writing %i bytes, bpos = %i ...\n", dsize, bpos));
+
+ if (cli_write(cli, fnum, 0, buffer_p + bpos, pos, dsize) != dsize) {
+ DEBUG(0, ("Error writing remote file\n"));
+ return 0;
+ }
+
+ rsize -= dsize;
+ pos += dsize;
+
+ /* Now figure out how much to move in the buffer */
+
+ /* FIXME, we should skip more than one block at a time */
+
+ /* First, skip any initial part of the part written that is left over */
+ /* from the end of the first TBLOCK */
+
+ if ((bpos) && ((bpos + dsize) >= TBLOCK)) {
+ dsize -= (TBLOCK - bpos); /* Get rid of the end of the first block */
+ bpos = 0;
+
+ if (next_block(tarbuf, &buffer_p, tbufsiz) <=0) { /* and skip the block */
+ DEBUG(0, ("Empty file, short tar file, or read error: %s\n", strerror(errno)));
+ return False;
+ }
+ }
+
+ /*
+ * Bugfix from Bob Boehmer <boehmer@worldnet.att.net>.
+ * If the file being extracted is an exact multiple of
+ * TBLOCK bytes then we don't want to extract the next
+ * block from the tarfile here, as it will be done in
+ * the caller of get_file().
+ */
+
+ while (((rsize != 0) && (dsize >= TBLOCK)) ||
+ ((rsize == 0) && (dsize > TBLOCK))) {
+
+ if (next_block(tarbuf, &buffer_p, tbufsiz) <=0) {
+ DEBUG(0, ("Empty file, short tar file, or read error: %s\n", strerror(errno)));
+ return False;
+ }
+
+ dsize -= TBLOCK;
+ }
+ bpos = dsize;
+ }
+
+ /* Now close the file ... */
+
+ if (!cli_close(cli, fnum)) {
+ DEBUG(0, ("Error closing remote file\n"));
+ return(False);
+ }
+
+ /* Now we update the creation date ... */
+ DEBUG(5, ("Updating creation date on %s\n", finfo.name));
+
+ if (!cli_setatr(cli, finfo.name, finfo.mode, finfo.mtime)) {
+ if (tar_real_noisy) {
+ DEBUG(0, ("Could not set time on file: %s\n", finfo.name));
+ /*return(False); */ /* Ignore, as Win95 does not allow changes */
+ }
+ }
+
+ ntarf++;
+ DEBUG(0, ("restore tar file %s of size %.0f bytes\n", finfo.name, (double)finfo.size));
+ return(True);
+}
+
+/* Create a directory. We just ensure that the path exists and return as there
+ is no file associated with a directory
+*/
+static int get_dir(file_info2 finfo)
+{
+ DEBUG(0, ("restore directory %s\n", finfo.name));
+
+ if (!ensurepath(finfo.name)) {
+ DEBUG(0, ("Problems creating directory\n"));
+ return(False);
+ }
+ ntarf++;
+ return(True);
+}
+
+/* Get a file with a long file name ... first file has file name, next file
+ has the data. We only want the long file name, as the loop in do_tarput
+ will deal with the rest.
+*/
+static char *get_longfilename(file_info2 finfo)
+{
+ /* finfo.size here is the length of the filename as written by the "/./@LongLink" name
+ * header call. */
+ int namesize = finfo.size + strlen(cur_dir) + 2;
+ char *longname = malloc(namesize);
+ int offset = 0, left = finfo.size;
+ BOOL first = True;
+
+ DEBUG(5, ("Restoring a long file name: %s\n", finfo.name));
+ DEBUG(5, ("Len = %.0f\n", (double)finfo.size));
+
+ if (longname == NULL) {
+ DEBUG(0, ("could not allocate buffer of size %d for longname\n", namesize));
+ return(NULL);
+ }
+
+ /* First, add cur_dir to the long file name */
+
+ if (strlen(cur_dir) > 0) {
+ strncpy(longname, cur_dir, namesize);
+ offset = strlen(cur_dir);
+ }
+
+ /* Loop through the blocks picking up the name */
+
+ while (left > 0) {
+ if (next_block(tarbuf, &buffer_p, tbufsiz) <= 0) {
+ DEBUG(0, ("Empty file, short tar file, or read error: %s\n", strerror(errno)));
+ return(NULL);
+ }
+
+ unfixtarname(longname + offset, buffer_p, MIN(TBLOCK, finfo.size), first--);
+ DEBUG(5, ("UnfixedName: %s, buffer: %s\n", longname, buffer_p));
+
+ offset += TBLOCK;
+ left -= TBLOCK;
+ }
+
+ return(longname);
+}
+
+static void do_tarput(void)
+{
+ file_info2 finfo;
+ struct timeval tp_start;
+ char *longfilename = NULL, linkflag;
+ int skip = False;
+
+ GetTimeOfDay(&tp_start);
+ DEBUG(5, ("RJS do_tarput called ...\n"));
+
+ buffer_p = tarbuf + tbufsiz; /* init this to force first read */
+
+ /* Now read through those files ... */
+ while (True) {
+ /* Get us to the next block, or the first block first time around */
+ if (next_block(tarbuf, &buffer_p, tbufsiz) <= 0) {
+ DEBUG(0, ("Empty file, short tar file, or read error: %s\n", strerror(errno)));
+ return;
+ }
+
+ DEBUG(5, ("Reading the next header ...\n"));
+
+ switch (readtarheader((union hblock *) buffer_p, &finfo, cur_dir)) {
+ case -2: /* Hmm, not good, but not fatal */
+ DEBUG(0, ("Skipping %s...\n", finfo.name));
+ if ((next_block(tarbuf, &buffer_p, tbufsiz) <= 0) && !skip_file(finfo.size)) {
+ DEBUG(0, ("Short file, bailing out...\n"));
+ return;
+ }
+ break;
+
+ case -1:
+ DEBUG(0, ("abandoning restore, -1 from read tar header\n"));
+ return;
+
+ case 0: /* chksum is zero - looks like an EOF */
+ DEBUG(0, ("tar: restored %d files and directories\n", ntarf));
+ return; /* Hmmm, bad here ... */
+
+ default:
+ /* No action */
+ break;
+ }
+
+ /* Now, do we have a long file name? */
+ if (longfilename != NULL) {
+ SAFE_FREE(finfo.name); /* Free the space already allocated */
+ finfo.name = longfilename;
+ longfilename = NULL;
+ }
+
+ /* Well, now we have a header, process the file ... */
+ /* Should we skip the file? We have the long name as well here */
+ skip = clipn && ((!tar_re_search && clipfind(cliplist, clipn, finfo.name) ^ tar_excl) ||
+#ifdef HAVE_REGEX_H
+ (tar_re_search && !regexec(preg, finfo.name, 0, NULL, 0)));
+#else
+ (tar_re_search && mask_match_list(finfo.name, cliplist, clipn, True)));
+#endif
+
+ DEBUG(5, ("Skip = %i, cliplist=%s, file=%s\n", skip, (cliplist?cliplist[0]:NULL), finfo.name));
+ if (skip) {
+ skip_file(finfo.size);
+ continue;
+ }
+
+ /* We only get this far if we should process the file */
+ linkflag = ((union hblock *)buffer_p) -> dbuf.linkflag;
+ switch (linkflag) {
+ case '0': /* Should use symbolic names--FIXME */
+ /*
+ * Skip to the next block first, so we can get the file, FIXME, should
+ * be in get_file ...
+ * The 'finfo.size != 0' fix is from Bob Boehmer <boehmer@worldnet.att.net>
+ * Fixes bug where file size in tarfile is zero.
+ */
+ if ((finfo.size != 0) && next_block(tarbuf, &buffer_p, tbufsiz) <=0) {
+ DEBUG(0, ("Short file, bailing out...\n"));
+ return;
+ }
+ if (!get_file(finfo)) {
+ DEBUG(0, ("Abandoning restore\n"));
+ return;
+ }
+ break;
+ case '5':
+ if (!get_dir(finfo)) {
+ DEBUG(0, ("Abandoning restore \n"));
+ return;
+ }
+ break;
+ case 'L':
+ longfilename = get_longfilename(finfo);
+ if (!longfilename) {
+ DEBUG(0, ("abandoning restore\n"));
+ return;
+ }
+ DEBUG(5, ("Long file name: %s\n", longfilename));
+ break;
+
+ default:
+ skip_file(finfo.size); /* Don't handle these yet */
+ break;
+ }
+ }
+}
+
+/*
+ * samba interactive commands
+ */
+
+/****************************************************************************
+Blocksize command
+***************************************************************************/
+
+int cmd_block(void)
+{
+ fstring buf;
+ int block;
+
+ if (!next_token_nr(NULL,buf,NULL,sizeof(buf))) {
+ DEBUG(0, ("blocksize <n>\n"));
+ return 1;
+ }
+
+ block=atoi(buf);
+ if (block < 0 || block > 65535) {
+ DEBUG(0, ("blocksize out of range"));
+ return 1;
+ }
+
+ blocksize=block;
+ DEBUG(2,("blocksize is now %d\n", blocksize));
+
+ return 0;
+}
+
+/****************************************************************************
+command to set incremental / reset mode
+***************************************************************************/
+
+int cmd_tarmode(void)
+{
+ fstring buf;
+
+ while (next_token_nr(NULL,buf,NULL,sizeof(buf))) {
+ if (strequal(buf, "full"))
+ tar_inc=False;
+ else if (strequal(buf, "inc"))
+ tar_inc=True;
+ else if (strequal(buf, "reset"))
+ tar_reset=True;
+ else if (strequal(buf, "noreset"))
+ tar_reset=False;
+ else if (strequal(buf, "system"))
+ tar_system=True;
+ else if (strequal(buf, "nosystem"))
+ tar_system=False;
+ else if (strequal(buf, "hidden"))
+ tar_hidden=True;
+ else if (strequal(buf, "nohidden"))
+ tar_hidden=False;
+ else if (strequal(buf, "verbose") || strequal(buf, "noquiet"))
+ tar_noisy=True;
+ else if (strequal(buf, "quiet") || strequal(buf, "noverbose"))
+ tar_noisy=False;
+ else
+ DEBUG(0, ("tarmode: unrecognised option %s\n", buf));
+ }
+
+ DEBUG(0, ("tarmode is now %s, %s, %s, %s, %s\n",
+ tar_inc ? "incremental" : "full",
+ tar_system ? "system" : "nosystem",
+ tar_hidden ? "hidden" : "nohidden",
+ tar_reset ? "reset" : "noreset",
+ tar_noisy ? "verbose" : "quiet"));
+ return 0;
+}
+
+/****************************************************************************
+Feeble attrib command
+***************************************************************************/
+
+int cmd_setmode(void)
+{
+ char *q;
+ fstring buf;
+ pstring fname;
+ uint16 attra[2];
+ int direct=1;
+
+ attra[0] = attra[1] = 0;
+
+ if (!next_token_nr(NULL,buf,NULL,sizeof(buf))) {
+ DEBUG(0, ("setmode <filename> <[+|-]rsha>\n"));
+ return 1;
+ }
+
+ pstrcpy(fname, cur_dir);
+ pstrcat(fname, buf);
+
+ while (next_token_nr(NULL,buf,NULL,sizeof(buf))) {
+ q=buf;
+
+ while(*q) {
+ switch (*q++) {
+ case '+':
+ direct=1;
+ break;
+ case '-':
+ direct=0;
+ break;
+ case 'r':
+ attra[direct]|=aRONLY;
+ break;
+ case 'h':
+ attra[direct]|=aHIDDEN;
+ break;
+ case 's':
+ attra[direct]|=aSYSTEM;
+ break;
+ case 'a':
+ attra[direct]|=aARCH;
+ break;
+ default:
+ DEBUG(0, ("setmode <filename> <perm=[+|-]rsha>\n"));
+ return 1;
+ }
+ }
+ }
+
+ if (attra[ATTRSET]==0 && attra[ATTRRESET]==0) {
+ DEBUG(0, ("setmode <filename> <[+|-]rsha>\n"));
+ return 1;
+ }
+
+ DEBUG(2, ("\nperm set %d %d\n", attra[ATTRSET], attra[ATTRRESET]));
+ do_setrattr(fname, attra[ATTRSET], ATTRSET);
+ do_setrattr(fname, attra[ATTRRESET], ATTRRESET);
+ return 0;
+}
+
+/****************************************************************************
+Principal command for creating / extracting
+***************************************************************************/
+
+int cmd_tar(void)
+{
+ fstring buf;
+ char **argl;
+ int argcl;
+
+ if (!next_token_nr(NULL,buf,NULL,sizeof(buf))) {
+ DEBUG(0,("tar <c|x>[IXbgan] <filename>\n"));
+ return 1;
+ }
+
+ argl=toktocliplist(&argcl, NULL);
+ if (!tar_parseargs(argcl, argl, buf, 0))
+ return 1;
+
+ process_tar();
+ SAFE_FREE(argl);
+ return 0;
+}
+
+/****************************************************************************
+Command line (option) version
+***************************************************************************/
+
+int process_tar(void)
+{
+ initarbuf();
+ switch(tar_type) {
+ case 'x':
+
+#if 0
+ do_tarput2();
+#else
+ do_tarput();
+#endif
+ SAFE_FREE(tarbuf);
+ close(tarhandle);
+ break;
+ case 'r':
+ case 'c':
+ if (clipn && tar_excl) {
+ int i;
+ pstring tarmac;
+
+ for (i=0; i<clipn; i++) {
+ DEBUG(5,("arg %d = %s\n", i, cliplist[i]));
+
+ if (*(cliplist[i]+strlen(cliplist[i])-1)=='\\') {
+ *(cliplist[i]+strlen(cliplist[i])-1)='\0';
+ }
+
+ if (strrchr_m(cliplist[i], '\\')) {
+ pstring saved_dir;
+
+ pstrcpy(saved_dir, cur_dir);
+
+ if (*cliplist[i]=='\\') {
+ pstrcpy(tarmac, cliplist[i]);
+ } else {
+ pstrcpy(tarmac, cur_dir);
+ pstrcat(tarmac, cliplist[i]);
+ }
+ pstrcpy(cur_dir, tarmac);
+ *(strrchr_m(cur_dir, '\\')+1)='\0';
+
+ DEBUG(5, ("process_tar, do_list with tarmac: %s\n", tarmac));
+ do_list(tarmac,attribute,do_tar, False, True);
+ pstrcpy(cur_dir,saved_dir);
+ } else {
+ pstrcpy(tarmac, cur_dir);
+ pstrcat(tarmac, cliplist[i]);
+ DEBUG(5, ("process_tar, do_list with tarmac: %s\n", tarmac));
+ do_list(tarmac,attribute,do_tar, False, True);
+ }
+ }
+ } else {
+ pstring mask;
+ pstrcpy(mask,cur_dir);
+ DEBUG(5, ("process_tar, do_list with mask: %s\n", mask));
+ pstrcat(mask,"\\*");
+ do_list(mask,attribute,do_tar,False, True);
+ }
+
+ if (ntarf)
+ dotareof(tarhandle);
+ close(tarhandle);
+ SAFE_FREE(tarbuf);
+
+ DEBUG(0, ("tar: dumped %d files and directories\n", ntarf));
+ DEBUG(0, ("Total bytes written: %.0f\n", (double)ttarf));
+ break;
+ }
+
+ if (must_free_cliplist) {
+ int i;
+ for (i = 0; i < clipn; ++i) {
+ SAFE_FREE(cliplist[i]);
+ }
+ SAFE_FREE(cliplist);
+ cliplist = NULL;
+ clipn = 0;
+ must_free_cliplist = False;
+ }
+ return(0);
+}
+
+/****************************************************************************
+Find a token (filename) in a clip list
+***************************************************************************/
+
+static int clipfind(char **aret, int ret, char *tok)
+{
+ if (aret==NULL)
+ return 0;
+
+ /* ignore leading slashes or dots in token */
+ while(strchr_m("/\\.", *tok))
+ tok++;
+
+ while(ret--) {
+ char *pkey=*aret++;
+
+ /* ignore leading slashes or dots in list */
+ while(strchr_m("/\\.", *pkey))
+ pkey++;
+
+ if (!strslashcmp(pkey, tok))
+ return 1;
+ }
+ return 0;
+}
+
+/****************************************************************************
+Read list of files to include from the file and initialize cliplist
+accordingly.
+***************************************************************************/
+
+static int read_inclusion_file(char *filename)
+{
+ XFILE *inclusion = NULL;
+ char buf[PATH_MAX + 1];
+ char *inclusion_buffer = NULL;
+ int inclusion_buffer_size = 0;
+ int inclusion_buffer_sofar = 0;
+ char *p;
+ char *tmpstr;
+ int i;
+ int error = 0;
+
+ clipn = 0;
+ buf[PATH_MAX] = '\0'; /* guarantee null-termination */
+ if ((inclusion = x_fopen(filename, O_RDONLY, 0)) == NULL) {
+ /* XXX It would be better to include a reason for failure, but without
+ * autoconf, it's hard to use strerror, sys_errlist, etc.
+ */
+ DEBUG(0,("Unable to open inclusion file %s\n", filename));
+ return 0;
+ }
+
+ while ((! error) && (x_fgets(buf, sizeof(buf)-1, inclusion))) {
+ if (inclusion_buffer == NULL) {
+ inclusion_buffer_size = 1024;
+ if ((inclusion_buffer = malloc(inclusion_buffer_size)) == NULL) {
+ DEBUG(0,("failure allocating buffer to read inclusion file\n"));
+ error = 1;
+ break;
+ }
+ }
+
+ if (buf[strlen(buf)-1] == '\n') {
+ buf[strlen(buf)-1] = '\0';
+ }
+
+ if ((strlen(buf) + 1 + inclusion_buffer_sofar) >= inclusion_buffer_size) {
+ char *ib;
+ inclusion_buffer_size *= 2;
+ ib = Realloc(inclusion_buffer,inclusion_buffer_size);
+ if (! ib) {
+ DEBUG(0,("failure enlarging inclusion buffer to %d bytes\n",
+ inclusion_buffer_size));
+ error = 1;
+ break;
+ } else {
+ inclusion_buffer = ib;
+ }
+ }
+
+ safe_strcpy(inclusion_buffer + inclusion_buffer_sofar, buf, inclusion_buffer_size - inclusion_buffer_sofar);
+ inclusion_buffer_sofar += strlen(buf) + 1;
+ clipn++;
+ }
+ x_fclose(inclusion);
+
+ if (! error) {
+ /* Allocate an array of clipn + 1 char*'s for cliplist */
+ cliplist = malloc((clipn + 1) * sizeof(char *));
+ if (cliplist == NULL) {
+ DEBUG(0,("failure allocating memory for cliplist\n"));
+ error = 1;
+ } else {
+ cliplist[clipn] = NULL;
+ p = inclusion_buffer;
+ for (i = 0; (! error) && (i < clipn); i++) {
+ /* set current item to NULL so array will be null-terminated even if
+ * malloc fails below. */
+ cliplist[i] = NULL;
+ if ((tmpstr = (char *)malloc(strlen(p)+1)) == NULL) {
+ DEBUG(0, ("Could not allocate space for a cliplist item, # %i\n", i));
+ error = 1;
+ } else {
+ unfixtarname(tmpstr, p, strlen(p) + 1, True);
+ cliplist[i] = tmpstr;
+ if ((p = strchr_m(p, '\000')) == NULL) {
+ DEBUG(0,("INTERNAL ERROR: inclusion_buffer is of unexpected contents.\n"));
+ abort();
+ }
+ }
+ ++p;
+ }
+ must_free_cliplist = True;
+ }
+ }
+
+ SAFE_FREE(inclusion_buffer);
+ if (error) {
+ if (cliplist) {
+ char **pp;
+ /* We know cliplist is always null-terminated */
+ for (pp = cliplist; *pp; ++pp) {
+ SAFE_FREE(*pp);
+ }
+ SAFE_FREE(cliplist);
+ cliplist = NULL;
+ must_free_cliplist = False;
+ }
+ return 0;
+ }
+
+ /* cliplist and its elements are freed at the end of process_tar. */
+ return 1;
+}
+
+/****************************************************************************
+Parse tar arguments. Sets tar_type, tar_excl, etc.
+***************************************************************************/
+
+int tar_parseargs(int argc, char *argv[], const char *Optarg, int Optind)
+{
+ int newOptind = Optind;
+ char tar_clipfl='\0';
+
+ /* Reset back to defaults - could be from interactive version
+ * reset mode and archive mode left as they are though
+ */
+ tar_type='\0';
+ tar_excl=True;
+ dry_run=False;
+
+ while (*Optarg) {
+ switch(*Optarg++) {
+ case 'c':
+ tar_type='c';
+ break;
+ case 'x':
+ if (tar_type=='c') {
+ printf("Tar must be followed by only one of c or x.\n");
+ return 0;
+ }
+ tar_type='x';
+ break;
+ case 'b':
+ if (Optind>=argc || !(blocksize=atoi(argv[Optind]))) {
+ DEBUG(0,("Option b must be followed by valid blocksize\n"));
+ return 0;
+ } else {
+ Optind++;
+ newOptind++;
+ }
+ break;
+ case 'g':
+ tar_inc=True;
+ break;
+ case 'N':
+ if (Optind>=argc) {
+ DEBUG(0,("Option N must be followed by valid file name\n"));
+ return 0;
+ } else {
+ SMB_STRUCT_STAT stbuf;
+ extern time_t newer_than;
+
+ if (sys_stat(argv[Optind], &stbuf) == 0) {
+ newer_than = stbuf.st_mtime;
+ DEBUG(1,("Getting files newer than %s",
+ asctime(LocalTime(&newer_than))));
+ newOptind++;
+ Optind++;
+ } else {
+ DEBUG(0,("Error setting newer-than time\n"));
+ return 0;
+ }
+ }
+ break;
+ case 'a':
+ tar_reset=True;
+ break;
+ case 'q':
+ tar_noisy=False;
+ break;
+ case 'I':
+ if (tar_clipfl) {
+ DEBUG(0,("Only one of I,X,F must be specified\n"));
+ return 0;
+ }
+ tar_clipfl='I';
+ break;
+ case 'X':
+ if (tar_clipfl) {
+ DEBUG(0,("Only one of I,X,F must be specified\n"));
+ return 0;
+ }
+ tar_clipfl='X';
+ break;
+ case 'F':
+ if (tar_clipfl) {
+ DEBUG(0,("Only one of I,X,F must be specified\n"));
+ return 0;
+ }
+ tar_clipfl='F';
+ break;
+ case 'r':
+ DEBUG(0, ("tar_re_search set\n"));
+ tar_re_search = True;
+ break;
+ case 'n':
+ if (tar_type == 'c') {
+ DEBUG(0, ("dry_run set\n"));
+ dry_run = True;
+ } else {
+ DEBUG(0, ("n is only meaningful when creating a tar-file\n"));
+ return 0;
+ }
+ break;
+ default:
+ DEBUG(0,("Unknown tar option\n"));
+ return 0;
+ }
+ }
+
+ if (!tar_type) {
+ printf("Option T must be followed by one of c or x.\n");
+ return 0;
+ }
+
+ /* tar_excl is true if cliplist lists files to be included.
+ * Both 'I' and 'F' mean include. */
+ tar_excl=tar_clipfl!='X';
+
+ if (tar_clipfl=='F') {
+ if (argc-Optind-1 != 1) {
+ DEBUG(0,("Option F must be followed by exactly one filename.\n"));
+ return 0;
+ }
+ newOptind++;
+ Optind++;
+ if (! read_inclusion_file(argv[Optind])) {
+ return 0;
+ }
+ } else if (Optind+1<argc && !tar_re_search) { /* For backwards compatibility */
+ char *tmpstr;
+ char **tmplist;
+ int clipcount;
+
+ cliplist=argv+Optind+1;
+ clipn=argc-Optind-1;
+ clipcount = clipn;
+
+ if ((tmplist=malloc(clipn*sizeof(char *))) == NULL) {
+ DEBUG(0, ("Could not allocate space to process cliplist, count = %i\n", clipn));
+ return 0;
+ }
+
+ for (clipcount = 0; clipcount < clipn; clipcount++) {
+
+ DEBUG(5, ("Processing an item, %s\n", cliplist[clipcount]));
+
+ if ((tmpstr = (char *)malloc(strlen(cliplist[clipcount])+1)) == NULL) {
+ DEBUG(0, ("Could not allocate space for a cliplist item, # %i\n", clipcount));
+ return 0;
+ }
+
+ unfixtarname(tmpstr, cliplist[clipcount], strlen(cliplist[clipcount]) + 1, True);
+ tmplist[clipcount] = tmpstr;
+ DEBUG(5, ("Processed an item, %s\n", tmpstr));
+
+ DEBUG(5, ("Cliplist is: %s\n", cliplist[0]));
+ }
+
+ cliplist = tmplist;
+ must_free_cliplist = True;
+
+ newOptind += clipn;
+ }
+
+ if (Optind+1<argc && tar_re_search) { /* Doing regular expression seaches */
+#ifdef HAVE_REGEX_H
+ int errcode;
+
+ if ((preg = (regex_t *)malloc(65536)) == NULL) {
+
+ DEBUG(0, ("Could not allocate buffer for regular expression search\n"));
+ return;
+ }
+
+ if (errcode = regcomp(preg, argv[Optind + 1], REG_EXTENDED)) {
+ char errstr[1024];
+ size_t errlen;
+
+ errlen = regerror(errcode, preg, errstr, sizeof(errstr) - 1);
+ DEBUG(0, ("Could not compile pattern buffer for re search: %s\n%s\n", argv[Optind + 1], errstr));
+ return;
+ }
+#endif
+
+ clipn=argc-Optind-1;
+ cliplist=argv+Optind+1;
+ newOptind += clipn;
+ }
+
+ if (Optind>=argc || !strcmp(argv[Optind], "-")) {
+ /* Sets tar handle to either 0 or 1, as appropriate */
+ tarhandle=(tar_type=='c');
+ /*
+ * Make sure that dbf points to stderr if we are using stdout for
+ * tar output
+ */
+ if (tarhandle == 1) {
+ dbf = x_stderr;
+ }
+ if (!argv[Optind]) {
+ DEBUG(0,("Must specify tar filename\n"));
+ return 0;
+ }
+ if (!strcmp(argv[Optind], "-")) {
+ newOptind++;
+ }
+
+ } else {
+ if (tar_type=='c' && (dry_run || strcmp(argv[Optind], "/dev/null")==0)) {
+ if (!dry_run) {
+ DEBUG(0,("Output is /dev/null, assuming dry_run\n"));
+ dry_run = True;
+ }
+ tarhandle=-1;
+ } else if ((tar_type=='x' && (tarhandle = sys_open(argv[Optind], O_RDONLY, 0)) == -1)
+ || (tar_type=='c' && (tarhandle=sys_creat(argv[Optind], 0644)) < 0)) {
+ DEBUG(0,("Error opening local file %s - %s\n", argv[Optind], strerror(errno)));
+ return(0);
+ }
+ newOptind++;
+ }
+
+ return newOptind;
+}
diff --git a/source/client/mount.cifs.c b/source/client/mount.cifs.c
new file mode 100755
index 00000000000..8c23cc22123
--- /dev/null
+++ b/source/client/mount.cifs.c
@@ -0,0 +1,825 @@
+/*
+ Mount helper utility for Linux CIFS VFS (virtual filesystem) client
+ Copyright (C) 2003 Steve French (sfrench@us.ibm.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 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 _GNU_SOURCE
+#define _GNU_SOURCE
+#endif
+
+#include <stdlib.h>
+#include <stdio.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>
+#include <fcntl.h>
+
+#define MOUNT_CIFS_VERSION_MAJOR "1"
+#define MOUNT_CIFS_VERSION_MINOR "0"
+
+#ifndef MOUNT_CIFS_VENDOR_SUFFIX
+#define MOUNT_CIFS_VENDOR_SUFFIX ""
+#endif
+
+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;
+
+
+/* BB finish BB
+
+ cifs_umount
+ open nofollow - avoid symlink exposure?
+ get owner of dir see if matches self or if root
+ call system(umount argv) etc.
+
+BB end finish BB */
+
+static void mount_cifs_usage(void)
+{
+ printf("\nUsage: %s <remotetarget> <dir> -o <options>\n", thisprogram);
+ printf("\nMount the remote target, specified as a UNC name,");
+ printf(" to a local directory.\n");
+ if(mountpassword) {
+ memset(mountpassword,0,64);
+ free(mountpassword);
+ }
+ exit(1);
+}
+
+/* caller frees username if necessary */
+static char * getusername(void) {
+ char *username = NULL;
+ struct passwd *password = getpwuid(getuid());
+
+ if (password) {
+ username = password->pw_name;
+ }
+ return username;
+}
+
+char * parse_cifs_url(char * unc_name)
+{
+ printf("\ncifs url %s\n",unc_name);
+ return NULL;
+}
+
+static int open_cred_file(char * file_name)
+{
+ char * line_buf;
+ char * temp_val;
+ FILE * fs;
+ int i, length;
+ fs = fopen(file_name,"r");
+ if(fs == NULL)
+ return errno;
+ line_buf = malloc(4096);
+ if(line_buf == NULL)
+ return -ENOMEM;
+
+ while(fgets(line_buf,4096,fs)) {
+ /* parse line from credential file */
+
+ /* eat leading white space */
+ for(i=0;i<4096;i++) {
+ if(line_buf[i] == '\0')
+ break;
+ else if((line_buf[i] != ' ') && (line_buf[i] != '\t'))
+ break;
+ line_buf++;
+ }
+
+ if (strncasecmp("username",line_buf,8) == 0) {
+ temp_val = strchr(line_buf + i,'=');
+ if(temp_val) {
+ /* go past equals sign */
+ temp_val++;
+ length = strlen(temp_val);
+ if(length > 4086) {
+ printf("cifs.mount failed due to malformed username in credentials file");
+ memset(line_buf,0,4096);
+ if(mountpassword) {
+ memset(mountpassword,0,64);
+ }
+ exit(1);
+ } else {
+ got_user = 1;
+ user_name = calloc(1 + length,1);
+ /* BB adding free of user_name string before exit,
+ not really necessary but would be cleaner */
+ strncpy(user_name,temp_val, length);
+ }
+ }
+ } else if (strncasecmp("password",line_buf,8) == 0) {
+ temp_val = strchr(line_buf+i,'=');
+ if(temp_val) {
+ /* go past equals sign */
+ temp_val++;
+ length = strlen(temp_val);
+ if(length > 64) {
+ printf("cifs.mount failed: password in credentials file too long\n");
+ memset(line_buf,0, 4096);
+ if(mountpassword) {
+ memset(mountpassword,0,64);
+ }
+ exit(1);
+ } else {
+ if(mountpassword == NULL) {
+ mountpassword = calloc(65,1);
+ }
+ if(mountpassword) {
+ strncpy(mountpassword,temp_val,64);
+ got_password = 1;
+ }
+ }
+ }
+ }
+ }
+ fclose(fs);
+ if(line_buf) {
+ memset(line_buf,0,4096);
+ free(line_buf);
+ }
+ return 0;
+}
+
+static int get_password_from_file(int file_descript, char * filename)
+{
+ int rc = 0;
+ int i;
+ char c;
+
+ if(mountpassword == NULL)
+ mountpassword = calloc(65,1);
+ else
+ memset(mountpassword, 0, 64);
+
+ if(filename != NULL) {
+ file_descript = open(filename, O_RDONLY);
+ if(file_descript < 0) {
+ printf("cifs.mount failed. %s attempting to open password file %s\n",
+ strerror(errno),filename);
+ exit(1);
+ }
+ }
+ /* else file already open and fd provided */
+
+ for(i=0;i<64;i++) {
+ rc = read(file_descript,&c,1);
+ if(rc < 0) {
+ printf("cifs.mount failed. Error %s reading password file\n",strerror(errno));
+ memset(mountpassword,0,64);
+ if(filename != NULL)
+ close(file_descript);
+ exit(1);
+ } else if(rc == 0) {
+ if(mountpassword[0] == 0) {
+ if(verboseflag)
+ printf("\nWarning: null password used since cifs password file empty");
+ }
+ break;
+ } else /* read valid character */ {
+ if((c == 0) || (c == '\n')) {
+ break;
+ } else
+ mountpassword[i] = c;
+ }
+ }
+ if((i == 64) && (verboseflag)) {
+ printf("\nWarning: password longer than 64 characters specified in cifs password file");
+ }
+ got_password = 1;
+ if(filename != NULL) {
+ close(file_descript);
+ }
+
+ return rc;
+}
+
+static int parse_options(char * options)
+{
+ char * data;
+ char * percent_char = 0;
+ char * value = 0;
+ char * next_keyword = 0;
+ int rc = 0;
+
+ if (!options)
+ return 1;
+ else
+ data = options;
+
+ if(verboseflag)
+ printf("\n parsing options: %s", options);
+
+/* while ((data = strsep(&options, ",")) != NULL) { */
+ while(data != NULL) {
+ /* check if ends with trailing comma */
+ if(*data == 0)
+ break;
+
+ /* format is keyword=value,keyword2=value2,keyword3=value3 etc.) */
+ /* data = next keyword */
+ /* value = next value ie stuff after equal sign */
+
+ next_keyword = strchr(data,',');
+
+ /* temporarily null terminate end of keyword=value pair */
+ if(next_keyword)
+ *next_keyword = 0;
+
+ /* if (!*data)
+ continue; */
+
+ /* temporarily null terminate keyword to make keyword and value distinct */
+ if ((value = strchr(data, '=')) != NULL) {
+ *value = '\0';
+ value++;
+ }
+
+ 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;
+ percent_char = strchr(value,'%');
+ if(percent_char) {
+ *percent_char = ',';
+ if(mountpassword == NULL)
+ mountpassword = calloc(65,1);
+ if(mountpassword) {
+ if(got_password)
+ printf("\ncifs.mount warning - password specified twice\n");
+ got_password = 1;
+ percent_char++;
+ strncpy(mountpassword, percent_char,64);
+ /* remove password from username */
+ while(*percent_char != 0) {
+ *percent_char = ',';
+ percent_char++;
+ }
+ }
+ }
+ } else {
+ printf("username too long\n");
+ return 1;
+ }
+ } else if (strncmp(data, "pass", 4) == 0) {
+ if (!value || !*value) {
+ if(got_password) {
+ printf("\npassword specified twice, ignoring second\n");
+ } else
+ got_password = 1;
+ } else if (strnlen(value, 17) < 17) {
+ if(got_password)
+ printf("\ncifs.mount warning - password specified twice\n");
+ 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, "cred", 4) == 0) {
+ if (value && *value) {
+ rc = open_cred_file(value);
+ if(rc) {
+ printf("error %d opening credential file %s",rc, value);
+ return 1;
+ }
+ } else {
+ printf("invalid credential file name specified\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;
+ }
+ /* fmask and dmask synonyms for people used to smbfs syntax */
+ } else if (strcmp(data, "file_mode") == 0 || strcmp(data, "fmask")==0) {
+ if (!value || !*value) {
+ printf ("Option '%s' requires a numerical argument\n", data);
+ return 1;
+ }
+
+ if (value[0] != '0') {
+ printf ("WARNING: '%s' not expressed in octal.\n", data);
+ }
+
+ if (strcmp (data, "fmask") == 0) {
+ printf ("WARNING: CIFS mount option 'fmask' is deprecated. Use 'file_mode' instead.\n");
+ data = "file_mode";
+ }
+ } else if (strcmp(data, "dir_mode") == 0 || strcmp(data, "dmask")==0) {
+ if (!value || !*value) {
+ printf ("Option '%s' requires a numerical argument\n", data);
+ return 1;
+ }
+
+ if (value[0] != '0') {
+ printf ("WARNING: '%s' not expressed in octal.\n", data);
+ }
+
+ if (strcmp (data, "dmask") == 0) {
+ printf ("WARNING: CIFS mount option 'dmask' is deprecated. Use 'dir_mode' instead.\n");
+ data = "dir_mode";
+ }
+ } /* 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); */
+
+ /* move to next option */
+ data = next_keyword+1;
+
+ /* put overwritten equals sign back */
+ if(value) {
+ value--;
+ *value = '=';
+ }
+
+ /* put previous overwritten comma back */
+ if(next_keyword)
+ *next_keyword = ',';
+ else
+ data = 0;
+
+ }
+ 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;
+
+ 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 {
+ 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[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' },
+ { "rsize",1, 0, 'R' },
+ { "wsize",1, 0, 'W' },
+ { "uid", 1, 0, '1'},
+ { "gid", 1, 0, '2'},
+ { "uuid",1,0,'U' },
+ { "user",1,0,'u'},
+ { "username",1,0,'u'},
+ { "dom",1,0,'d'},
+ { "domain",1,0,'d'},
+ { "password",1,0,'p'},
+ { "pass",1,0,'p'},
+ { "credentials",1,0,'c'},
+ { "port",1,0,'P'},
+ { 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;
+ char * temp;
+ int rc;
+ int rsize = 0;
+ int wsize = 0;
+ int nomtab = 0;
+ int uid = 0;
+ int gid = 0;
+ int optlen = 0;
+ int orgoptlen = 0;
+ struct stat statbuf;
+ struct utsname sysinfo;
+ struct mntent mountent;
+ FILE * pmntfile;
+
+ /* setlocale(LC_ALL, "");
+ bindtextdomain(PACKAGE, LOCALEDIR);
+ textdomain(PACKAGE); */
+
+ 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 sysname %s domain %s\n", sysinfo.nodename,sysinfo.machine,sysinfo.sysname,sysinfo.domainname);
+#endif */
+
+ 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) {
+/* No code to do the following options yet */
+/* case 'l':
+ list_with_volumelabel = 1;
+ break;
+ case 'L':
+ volumelabel = optarg;
+ break; */
+/* case 'a':
+ ++mount_all;
+ break; */
+
+ case '?':
+ case 'h': /* help */
+ mount_cifs_usage ();
+ exit(1);
+ case 'n':
+ ++nomtab;
+ break;
+ case 'o':
+ orgoptions = strdup(optarg);
+ break;
+ case 'r': /* mount readonly */
+ flags |= MS_RDONLY;
+ break;
+ case 'U':
+ uuid = optarg;
+ break;
+ case 'v':
+ ++verboseflag;
+ break;
+ case 'V':
+ printf ("mount.cifs version: %s.%s%s\n",
+ MOUNT_CIFS_VERSION_MAJOR,
+ MOUNT_CIFS_VERSION_MINOR,
+ MOUNT_CIFS_VENDOR_SUFFIX);
+ if(mountpassword) {
+ memset(mountpassword,0,64);
+ }
+ exit (0);
+ case 'w':
+ flags &= ~MS_RDONLY;
+ break;
+ case 'R':
+ rsize = atoi(optarg) ;
+ break;
+ case 'W':
+ wsize = atoi(optarg);
+ break;
+ case '1':
+ uid = atoi(optarg);
+ break;
+ case '2':
+ gid = atoi(optarg);
+ break;
+ case 'u':
+ got_user = 1;
+ user_name = optarg;
+ break;
+ case 'd':
+ domain_name = optarg;
+ break;
+ case 'p':
+ if(mountpassword == NULL)
+ mountpassword = calloc(65,1);
+ if(mountpassword) {
+ got_password = 1;
+ strncpy(mountpassword,optarg,64);
+ }
+ break;
+ case 't':
+ break;
+ default:
+ printf("unknown mount option %c\n",c);
+ mount_cifs_usage();
+ exit(1);
+ }
+ }
+
+ if(argc < 3)
+ mount_cifs_usage();
+
+ if (getenv("PASSWD")) {
+ if(mountpassword == NULL)
+ mountpassword = calloc(65,1);
+ if(mountpassword) {
+ strncpy(mountpassword,getenv("PASSWD"),64);
+ got_password = 1;
+ }
+ } else if (getenv("PASSWD_FD")) {
+ get_password_from_file(atoi(getenv("PASSWD_FD")),NULL);
+ } else if (getenv("PASSWD_FILE")) {
+ get_password_from_file(0, getenv("PASSWD_FILE"));
+ }
+
+ ipaddr = parse_server(share_name);
+
+ if (orgoptions && parse_options(orgoptions))
+ return 1;
+
+ /* BB save off path and pop after mount returns? */
+ /* BB canonicalize the path in argv[1]? */
+
+ if(chdir(mountpoint)) {
+ printf("mount error: can not change directory into mount target %s\n",mountpoint);
+ }
+
+ 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((getuid() != 0) && (geteuid() == 0)) {
+ if((statbuf.st_uid == getuid()) && (S_IRWXU == (statbuf.st_mode & S_IRWXU))) {
+ printf("setuid mount allowed\n");
+ } else {
+ printf("mount error: permission denied or not superuser and cifs.mount not installed SUID\n");
+ return -1;
+ }
+ }
+
+ if(got_user == 0)
+ user_name = getusername();
+
+ if(got_password == 0) {
+ 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);
+ orgoptlen = optlen;
+ } 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);
+ /* scan backwards and reverse direction of slash */
+ temp = strrchr(options, '/');
+ if(temp > options + 6)
+ *temp = '\\';
+ 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_MAJOR);
+
+ if(orgoptions) {
+ strcat(options,",");
+ strcat(options,orgoptions);
+ }
+ if(verboseflag)
+ printf("\ncifs.mount kernel mount options %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");
+ break;
+ case ENODEV:
+ printf("mount error: cifs filesystem not supported by the system\n");
+ break;
+ default:
+ printf("mount error %d = %s\n",errno,strerror(errno));
+ }
+ printf("Refer to the mount.cifs(8) manual page (e.g.man mount.cifs)\n");
+ if(mountpassword) {
+ memset(mountpassword,0,64);
+ }
+ 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");
+ }
+ }
+ if(mountpassword) {
+ memset(mountpassword,0,64);
+ free(mountpassword);
+ }
+
+ if(options) {
+ memset(options,0,optlen);
+ free(options);
+ }
+
+ if(orgoptions) {
+ memset(orgoptions,0,orgoptlen);
+ free(orgoptions);
+ }
+ return 0;
+}
+
diff --git a/source/client/smbmnt.c b/source/client/smbmnt.c
new file mode 100644
index 00000000000..753a31c3139
--- /dev/null
+++ b/source/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 unsigned mount_fmask;
+static unsigned 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",SAMBA_VERSION_STRING);
+ 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[PATH_MAX+1];
+
+ if (strlen(p) > PATH_MAX) {
+ 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, unsigned int 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;
+ unsigned int 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 | MS_NOSUID | MS_NODEV;
+
+ 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/source/client/smbmount.c b/source/client/smbmount.c
new file mode 100644
index 00000000000..6e8d9f5bca7
--- /dev/null
+++ b/source/client/smbmount.c
@@ -0,0 +1,940 @@
+/*
+ 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 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.
+*/
+
+#define NO_SYSLOG
+
+#include "includes.h"
+
+#include <mntent.h>
+#include <asm/types.h>
+#include <linux/smb_fs.h>
+
+extern BOOL in_client;
+extern pstring user_socket_options;
+
+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 unsigned mount_fmask;
+static unsigned mount_dmask;
+static BOOL use_kerberos;
+/* TODO: Add code to detect smbfs version in kernel */
+static BOOL status32_smbfs = False;
+static BOOL smbfs_has_unicode = False;
+static BOOL smbfs_has_lfs = 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 cli_state *do_connection(char *the_service)
+{
+ struct cli_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);
+ make_nmb_name(&called , server, 0x20);
+
+ again:
+ zero_ip(&ip);
+ if (have_ip) ip = dest_ip;
+
+ /* have to open a new connection */
+ if (!(c=cli_initialise(NULL)) || (cli_set_port(c, smb_port) != smb_port) ||
+ !cli_connect(c, server_n, &ip)) {
+ DEBUG(0,("%d: Connection to %s failed\n", sys_getpid(), server_n));
+ if (c) {
+ cli_shutdown(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 (!cli_session_request(c, &calling, &called)) {
+ char *p;
+ DEBUG(0,("%d: session request to %s failed (%s)\n",
+ sys_getpid(), called.name, cli_errstr(c)));
+ cli_shutdown(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 (!cli_negprot(c)) {
+ DEBUG(0,("%d: protocol negotiation failed\n", sys_getpid()));
+ cli_shutdown(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. */
+ c->capabilities &= ~(CAP_NT_SMBS | CAP_NT_FIND | CAP_LEVEL_II_OPLOCKS);
+ if (!smbfs_has_lfs)
+ c->capabilities &= ~CAP_LARGE_FILES;
+ if (!smbfs_has_unicode)
+ c->capabilities &= ~CAP_UNICODE;
+ if (!status32_smbfs) {
+ c->capabilities &= ~CAP_STATUS32;
+ c->force_dos_errors = True;
+ }
+
+ if (!cli_session_setup(c, username,
+ password, strlen(password),
+ password, strlen(password),
+ workgroup)) {
+ /* if a password was not supplied then try again with a
+ null username */
+ if (password[0] || !username[0] ||
+ !cli_session_setup(c, "", "", 0, "", 0, workgroup)) {
+ DEBUG(0,("%d: session setup failed: %s\n",
+ sys_getpid(), cli_errstr(c)));
+ cli_shutdown(c);
+ return NULL;
+ }
+ DEBUG(0,("Anonymous login successful\n"));
+ }
+
+ DEBUG(4,("%d: session setup ok\n", sys_getpid()));
+
+ if (!cli_send_tconX(c, share, "?????",
+ password, strlen(password)+1)) {
+ DEBUG(0,("%d: tree connect failed: %s\n",
+ sys_getpid(), cli_errstr(c)));
+ cli_shutdown(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(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(char *the_service, char *mount_point, struct cli_state *c)
+{
+ int fd, closed = 0, res = 1;
+ pid_t parentpid = getppid();
+ struct smb_conn_opt conn_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. */
+ cli_shutdown(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", False); /* sneaky ... */
+ setup_logging("mount.smbfs", False);
+ 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);
+ }
+ }
+
+ smb_umount(mount_point);
+ DEBUG(2,("mount.smbfs[%d]: exit\n", sys_getpid()));
+ exit(1);
+}
+
+
+/**
+ * Mount a smbfs
+ **/
+static void init_mount(void)
+{
+ char mount_point[PATH_MAX+1];
+ pstring tmp;
+ pstring svc2;
+ struct cli_state *c;
+ char *args[20];
+ int i, status;
+
+ if (realpath(mpoint, mount_point) == NULL) {
+ fprintf(stderr, "Could not resolve mount point %s\n", mpoint);
+ return;
+ }
+
+
+ c = do_connection(service);
+ 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, NULL)) {
+ 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(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 = sys_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 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",SAMBA_VERSION_STRING);
+
+ 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\
+ unicode use unicode when communicating with server\n\
+ lfs large file system support\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(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")) {
+ pstrcpy(user_socket_options,opteq+1);
+ } else if(!strcmp(opts, "scope")) {
+ set_global_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 if(!strcmp(opts, "unicode")) {
+ smbfs_has_unicode = True;
+ } else if(!strcmp(opts, "lfs")) {
+ smbfs_has_lfs = True;
+ } 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;
+
+ DEBUGLEVEL = 1;
+
+ /* here we are interactive, even if run from autofs */
+ setup_logging("mount.smbfs",True);
+
+#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
+
+ in_client = True; /* Make sure that we tell lp_load we are */
+
+ 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_m(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"));
+ }
+
+ if (!lp_load(dyn_CONFIGFILE,True,False,False)) {
+ fprintf(stderr, "Can't load %s - run testparm to debug it\n",
+ dyn_CONFIGFILE);
+ }
+
+ 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", SAMBA_VERSION_STRING));
+
+ if (*workgroup == 0) {
+ pstrcpy(workgroup,lp_workgroup());
+ }
+
+ load_interfaces();
+ if (!*my_netbios_name) {
+ pstrcpy(my_netbios_name, myhostname());
+ }
+ strupper_m(my_netbios_name);
+
+ init_mount();
+ return 0;
+}
diff --git a/source/client/smbspool.c b/source/client/smbspool.c
new file mode 100644
index 00000000000..5daefec5a55
--- /dev/null
+++ b/source/client/smbspool.c
@@ -0,0 +1,363 @@
+/*
+ Unix SMB/CIFS implementation.
+ SMB backend for the Common UNIX Printing System ("CUPS")
+ Copyright 1999 by Easy Software Products
+ Copyright Andrew Tridgell 1994-1998
+ Copyright 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 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.
+*/
+
+#define NO_SYSLOG
+
+#include "includes.h"
+
+/*
+ * Globals...
+ */
+
+extern BOOL in_client; /* Boolean for client library */
+
+
+/*
+ * Local functions...
+ */
+
+static void list_devices(void);
+static struct cli_state *smb_connect(const char *, const char *, const char *, const char *, const char *);
+static int smb_print(struct cli_state *, char *, FILE *);
+
+
+/*
+ * 'main()' - Main entry for SMB backend.
+ */
+
+ int /* O - Exit status */
+ main(int argc, /* I - Number of command-line arguments */
+ char *argv[]) /* I - Command-line arguments */
+{
+ int i; /* Looping var */
+ int copies; /* Number of copies */
+ char uri[1024], /* URI */
+ *sep, /* Pointer to separator */
+ *password; /* Password */
+ const char *username, /* Username */
+ *server, /* Server name */
+ *printer; /* Printer name */
+ const char *workgroup; /* Workgroup */
+ FILE *fp; /* File to print */
+ int status=0; /* Status of LPD job */
+ struct cli_state *cli; /* SMB interface */
+
+ /* we expect the URI in argv[0]. Detect the case where it is in argv[1] and cope */
+ if (argc > 2 && strncmp(argv[0],"smb://", 6) && !strncmp(argv[1],"smb://", 6)) {
+ argv++;
+ argc--;
+ }
+
+ if (argc == 1)
+ {
+ /*
+ * NEW! In CUPS 1.1 the backends are run with no arguments to list the
+ * available devices. These can be devices served by this backend
+ * or any other backends (i.e. you can have an SNMP backend that
+ * is only used to enumerate the available network printers... :)
+ */
+
+ list_devices();
+ return (0);
+ }
+
+ if (argc < 6 || argc > 7)
+ {
+ fprintf(stderr, "Usage: %s [DEVICE_URI] job-id user title copies options [file]\n",
+ argv[0]);
+ fputs(" The DEVICE_URI environment variable can also contain the\n", stderr);
+ fputs(" destination printer:\n", stderr);
+ fputs("\n", stderr);
+ fputs(" smb://[username:password@][workgroup/]server/printer\n", stderr);
+ return (1);
+ }
+
+ /*
+ * If we have 7 arguments, print the file named on the command-line.
+ * Otherwise, print data from stdin...
+ */
+
+ if (argc == 6)
+ {
+ /*
+ * Print from Copy stdin to a temporary file...
+ */
+
+ fp = stdin;
+ copies = 1;
+ }
+ else if ((fp = fopen(argv[6], "rb")) == NULL)
+ {
+ perror("ERROR: Unable to open print file");
+ return (1);
+ }
+ else
+ copies = atoi(argv[4]);
+
+ /*
+ * Find the URI...
+ */
+
+ if (strncmp(argv[0], "smb://", 6) == 0)
+ strncpy(uri, argv[0], sizeof(uri) - 1);
+ else if (getenv("DEVICE_URI") != NULL)
+ strncpy(uri, getenv("DEVICE_URI"), sizeof(uri) - 1);
+ else
+ {
+ fputs("ERROR: No device URI found in argv[0] or DEVICE_URI environment variable!\n", stderr);
+ return (1);
+ }
+
+ uri[sizeof(uri) - 1] = '\0';
+
+ /*
+ * Extract the destination from the URI...
+ */
+
+ if ((sep = strrchr_m(uri, '@')) != NULL)
+ {
+ username = uri + 6;
+ *sep++ = '\0';
+
+ server = sep;
+
+ /*
+ * Extract password as needed...
+ */
+
+ if ((password = strchr_m(username, ':')) != NULL)
+ *password++ = '\0';
+ else
+ password = "";
+ }
+ else
+ {
+ username = "";
+ password = "";
+ server = uri + 6;
+ }
+
+ if ((sep = strchr_m(server, '/')) == NULL)
+ {
+ fputs("ERROR: Bad URI - need printer name!\n", stderr);
+ return (1);
+ }
+
+ *sep++ = '\0';
+ printer = sep;
+
+ if ((sep = strchr_m(printer, '/')) != NULL)
+ {
+ /*
+ * Convert to smb://[username:password@]workgroup/server/printer...
+ */
+
+ *sep++ = '\0';
+
+ workgroup = server;
+ server = printer;
+ printer = sep;
+ }
+ else
+ workgroup = NULL;
+
+ /*
+ * Setup the SAMBA server state...
+ */
+
+ setup_logging("smbspool", True);
+
+ in_client = True; /* Make sure that we tell lp_load we are */
+
+ if (!lp_load(dyn_CONFIGFILE, True, False, False))
+ {
+ fprintf(stderr, "ERROR: Can't load %s - run testparm to debug it\n", dyn_CONFIGFILE);
+ return (1);
+ }
+
+ if (workgroup == NULL)
+ workgroup = lp_workgroup();
+
+ load_interfaces();
+
+ do
+ {
+ if ((cli = smb_connect(workgroup, server, printer, username, password)) == NULL)
+ {
+ if (getenv("CLASS") == NULL)
+ {
+ fprintf(stderr, "ERROR: Unable to connect to SAMBA host, will retry in 60 seconds...");
+ sleep (60);
+ }
+ else
+ {
+ fprintf(stderr, "ERROR: Unable to connect to SAMBA host, trying next printer...");
+ return (1);
+ }
+ }
+ }
+ while (cli == NULL);
+
+ /*
+ * Now that we are connected to the server, ignore SIGTERM so that we
+ * can finish out any page data the driver sends (e.g. to eject the
+ * current page... Only ignore SIGTERM if we are printing data from
+ * stdin (otherwise you can't cancel raw jobs...)
+ */
+
+ if (argc < 7)
+ CatchSignal(SIGTERM, SIG_IGN);
+
+ /*
+ * Queue the job...
+ */
+
+ for (i = 0; i < copies; i ++)
+ if ((status = smb_print(cli, argv[3] /* title */, fp)) != 0)
+ break;
+
+ cli_shutdown(cli);
+
+ /*
+ * Return the queue status...
+ */
+
+ return (status);
+}
+
+
+/*
+ * 'list_devices()' - List the available printers seen on the network...
+ */
+
+static void
+list_devices(void)
+{
+ /*
+ * Eventually, search the local workgroup for available hosts and printers.
+ */
+
+ puts("network smb \"Unknown\" \"Windows Printer via SAMBA\"");
+}
+
+
+/*
+ * 'smb_connect()' - Return a connection to a server.
+ */
+
+static struct cli_state * /* O - SMB connection */
+smb_connect(const char *workgroup, /* I - Workgroup */
+ const char *server, /* I - Server */
+ const char *share, /* I - Printer */
+ const char *username, /* I - Username */
+ const char *password) /* I - Password */
+{
+ struct cli_state *c; /* New connection */
+ pstring myname; /* Client name */
+ NTSTATUS nt_status;
+
+ /*
+ * Get the names and addresses of the client and server...
+ */
+
+ get_myname(myname);
+
+ nt_status = cli_full_connection(&c, myname, server, NULL, 0, share, "?????",
+ username, workgroup, password, 0, Undefined, NULL);
+
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ fprintf(stderr, "ERROR: Connection failed with error %s\n", nt_errstr(nt_status));
+ return NULL;
+ }
+
+ /*
+ * Return the new connection...
+ */
+
+ return (c);
+}
+
+
+/*
+ * 'smb_print()' - Queue a job for printing using the SMB protocol.
+ */
+
+static int /* O - 0 = success, non-0 = failure */
+smb_print(struct cli_state *cli, /* I - SMB connection */
+ char *title, /* I - Title/job name */
+ FILE *fp) /* I - File to print */
+{
+ int fnum; /* File number */
+ int nbytes, /* Number of bytes read */
+ tbytes; /* Total bytes read */
+ char buffer[8192], /* Buffer for copy */
+ *ptr; /* Pointer into tile */
+
+
+ /*
+ * Sanitize the title...
+ */
+
+ for (ptr = title; *ptr; ptr ++)
+ if (!isalnum((int)*ptr) && !isspace((int)*ptr))
+ *ptr = '_';
+
+ /*
+ * Open the printer device...
+ */
+
+ if ((fnum = cli_open(cli, title, O_RDWR | O_CREAT | O_TRUNC, DENY_NONE)) == -1)
+ {
+ fprintf(stderr, "ERROR: %s opening remote file %s\n",
+ cli_errstr(cli), title);
+ return (1);
+ }
+
+ /*
+ * Copy the file to the printer...
+ */
+
+ if (fp != stdin)
+ rewind(fp);
+
+ tbytes = 0;
+
+ while ((nbytes = fread(buffer, 1, sizeof(buffer), fp)) > 0)
+ {
+ if (cli_write(cli, fnum, 0, buffer, tbytes, nbytes) != nbytes)
+ {
+ fprintf(stderr, "ERROR: Error writing file: %s\n", cli_errstr(cli));
+ break;
+ }
+
+ tbytes += nbytes;
+ }
+
+ if (!cli_close(cli, fnum))
+ {
+ fprintf(stderr, "ERROR: %s closing remote file %s\n",
+ cli_errstr(cli), title);
+ return (1);
+ }
+ else
+ return (0);
+}
diff --git a/source/client/smbumount.c b/source/client/smbumount.c
new file mode 100644
index 00000000000..9ea3083a6f9
--- /dev/null
+++ b/source/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/source/client/tree.c b/source/client/tree.c
new file mode 100644
index 00000000000..97ad7742e31
--- /dev/null
+++ b/source/client/tree.c
@@ -0,0 +1,811 @@
+/*
+ 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 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.
+*/
+
+/* 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 */
+
+};
+
+static void tree_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 pstring path_string;
+
+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
+ */
+
+ pstrcpy( 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 = (struct tree_data *)malloc(sizeof(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;
+ pstring path, path1;
+
+ 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 ((strcmp(dirp->name, ".") != 0) &&
+ (strcmp(dirp->name, "..") != 0)) {
+
+ 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));
+
+ tree_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));
+
+ tree_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 &&
+ (strcmp(dirp->name, ".") != 0) &&
+ (strcmp(dirp->name, "..") !=0)){
+
+ 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/source/codepages/.cvsignore b/source/codepages/.cvsignore
new file mode 100644
index 00000000000..e69de29bb2d
--- /dev/null
+++ b/source/codepages/.cvsignore
diff --git a/source/codepages/lowcase.dat b/source/codepages/lowcase.dat
new file mode 100644
index 00000000000..62b6e2e952b
--- /dev/null
+++ b/source/codepages/lowcase.dat
Binary files differ
diff --git a/source/codepages/upcase.dat b/source/codepages/upcase.dat
new file mode 100644
index 00000000000..bb6f9beb4e3
--- /dev/null
+++ b/source/codepages/upcase.dat
Binary files differ
diff --git a/source/codepages/valid.dat b/source/codepages/valid.dat
new file mode 100644
index 00000000000..78c14b33f0f
--- /dev/null
+++ b/source/codepages/valid.dat
Binary files differ
diff --git a/source/config.guess b/source/config.guess
new file mode 100755
index 00000000000..78f6b92cd30
--- /dev/null
+++ b/source/config.guess
@@ -0,0 +1,1409 @@
+#! /bin/sh
+# Attempt to guess a canonical system name.
+# Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999,
+# 2000, 2001, 2002, 2003 Free Software Foundation, Inc.
+
+timestamp='2003-01-10'
+
+# 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 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.
+#
+# 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
+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 0 ;;
+ --version | -v )
+ echo "$version" ; exit 0 ;;
+ --help | --h* | -h )
+ echo "$usage"; exit 0 ;;
+ -- ) # 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) ; } ||
+ { 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 ;'
+
+# 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 0 ;;
+ amiga:OpenBSD:*:*)
+ echo m68k-unknown-openbsd${UNAME_RELEASE}
+ exit 0 ;;
+ arc:OpenBSD:*:*)
+ echo mipsel-unknown-openbsd${UNAME_RELEASE}
+ exit 0 ;;
+ hp300:OpenBSD:*:*)
+ echo m68k-unknown-openbsd${UNAME_RELEASE}
+ exit 0 ;;
+ mac68k:OpenBSD:*:*)
+ echo m68k-unknown-openbsd${UNAME_RELEASE}
+ exit 0 ;;
+ macppc:OpenBSD:*:*)
+ echo powerpc-unknown-openbsd${UNAME_RELEASE}
+ exit 0 ;;
+ mvme68k:OpenBSD:*:*)
+ echo m68k-unknown-openbsd${UNAME_RELEASE}
+ exit 0 ;;
+ mvme88k:OpenBSD:*:*)
+ echo m88k-unknown-openbsd${UNAME_RELEASE}
+ exit 0 ;;
+ mvmeppc:OpenBSD:*:*)
+ echo powerpc-unknown-openbsd${UNAME_RELEASE}
+ exit 0 ;;
+ pmax:OpenBSD:*:*)
+ echo mipsel-unknown-openbsd${UNAME_RELEASE}
+ exit 0 ;;
+ sgi:OpenBSD:*:*)
+ echo mipseb-unknown-openbsd${UNAME_RELEASE}
+ exit 0 ;;
+ sun3:OpenBSD:*:*)
+ echo m68k-unknown-openbsd${UNAME_RELEASE}
+ exit 0 ;;
+ wgrisc:OpenBSD:*:*)
+ echo mipsel-unknown-openbsd${UNAME_RELEASE}
+ exit 0 ;;
+ *:OpenBSD:*:*)
+ echo ${UNAME_MACHINE}-unknown-openbsd${UNAME_RELEASE}
+ exit 0 ;;
+ *:MicroBSD:*:*)
+ echo ${UNAME_MACHINE}-unknown-microbsd${UNAME_RELEASE}
+ exit 0 ;;
+ alpha:OSF1:*:*)
+ if test $UNAME_RELEASE = "V4.0"; then
+ UNAME_RELEASE=`/usr/sbin/sizer -v | awk '{print $3}'`
+ fi
+ # 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.
+ eval $set_cc_for_build
+ cat <<EOF >$dummy.s
+ .data
+\$Lformat:
+ .byte 37,100,45,37,120,10,0 # "%d-%x\n"
+
+ .text
+ .globl main
+ .align 4
+ .ent main
+main:
+ .frame \$30,16,\$26,0
+ ldgp \$29,0(\$27)
+ .prologue 1
+ .long 0x47e03d80 # implver \$0
+ lda \$2,-1
+ .long 0x47e20c21 # amask \$2,\$1
+ lda \$16,\$Lformat
+ mov \$0,\$17
+ not \$1,\$18
+ jsr \$26,printf
+ ldgp \$29,0(\$26)
+ mov 0,\$16
+ jsr \$26,exit
+ .end main
+EOF
+ $CC_FOR_BUILD -o $dummy $dummy.s 2>/dev/null
+ if test "$?" = 0 ; then
+ case `$dummy` in
+ 0-0)
+ UNAME_MACHINE="alpha"
+ ;;
+ 1-0)
+ UNAME_MACHINE="alphaev5"
+ ;;
+ 1-1)
+ UNAME_MACHINE="alphaev56"
+ ;;
+ 1-101)
+ UNAME_MACHINE="alphapca56"
+ ;;
+ 2-303)
+ UNAME_MACHINE="alphaev6"
+ ;;
+ 2-307)
+ UNAME_MACHINE="alphaev67"
+ ;;
+ 2-1307)
+ UNAME_MACHINE="alphaev68"
+ ;;
+ 3-1307)
+ UNAME_MACHINE="alphaev7"
+ ;;
+ esac
+ fi
+ echo ${UNAME_MACHINE}-dec-osf`echo ${UNAME_RELEASE} | sed -e 's/^[VTX]//' | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz'`
+ exit 0 ;;
+ 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 0 ;;
+ 21064:Windows_NT:50:3)
+ echo alpha-dec-winnt3.5
+ exit 0 ;;
+ Amiga*:UNIX_System_V:4.0:*)
+ echo m68k-unknown-sysv4
+ exit 0;;
+ *:[Aa]miga[Oo][Ss]:*:*)
+ echo ${UNAME_MACHINE}-unknown-amigaos
+ exit 0 ;;
+ *:[Mm]orph[Oo][Ss]:*:*)
+ echo ${UNAME_MACHINE}-unknown-morphos
+ exit 0 ;;
+ *:OS/390:*:*)
+ echo i370-ibm-openedition
+ exit 0 ;;
+ arm:RISC*:1.[012]*:*|arm:riscix:1.[012]*:*)
+ echo arm-acorn-riscix${UNAME_RELEASE}
+ exit 0;;
+ SR2?01:HI-UX/MPP:*:* | SR8000:HI-UX/MPP:*:*)
+ echo hppa1.1-hitachi-hiuxmpp
+ exit 0;;
+ 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 0 ;;
+ NILE*:*:*:dcosx)
+ echo pyramid-pyramid-svr4
+ exit 0 ;;
+ DRS?6000:UNIX_SV:4.2*:7*)
+ case `/usr/bin/uname -p` in
+ sparc) echo sparc-icl-nx7 && exit 0 ;;
+ esac ;;
+ sun4H:SunOS:5.*:*)
+ echo sparc-hal-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'`
+ exit 0 ;;
+ sun4*:SunOS:5.*:* | tadpole*:SunOS:5.*:*)
+ echo sparc-sun-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'`
+ exit 0 ;;
+ i86pc:SunOS:5.*:*)
+ echo i386-pc-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'`
+ exit 0 ;;
+ 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 0 ;;
+ 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 0 ;;
+ sun3*:SunOS:*:*)
+ echo m68k-sun-sunos${UNAME_RELEASE}
+ exit 0 ;;
+ 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 0 ;;
+ aushp:SunOS:*:*)
+ echo sparc-auspex-sunos${UNAME_RELEASE}
+ exit 0 ;;
+ # 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 0 ;;
+ atari*:*MiNT:*:* | atari*:*mint:*:* | atarist[e]:*TOS:*:*)
+ echo m68k-atari-mint${UNAME_RELEASE}
+ exit 0 ;;
+ *falcon*:*MiNT:*:* | *falcon*:*mint:*:* | *falcon*:*TOS:*:*)
+ echo m68k-atari-mint${UNAME_RELEASE}
+ exit 0 ;;
+ milan*:*MiNT:*:* | milan*:*mint:*:* | *milan*:*TOS:*:*)
+ echo m68k-milan-mint${UNAME_RELEASE}
+ exit 0 ;;
+ hades*:*MiNT:*:* | hades*:*mint:*:* | *hades*:*TOS:*:*)
+ echo m68k-hades-mint${UNAME_RELEASE}
+ exit 0 ;;
+ *:*MiNT:*:* | *:*mint:*:* | *:*TOS:*:*)
+ echo m68k-unknown-mint${UNAME_RELEASE}
+ exit 0 ;;
+ powerpc:machten:*:*)
+ echo powerpc-apple-machten${UNAME_RELEASE}
+ exit 0 ;;
+ RISC*:Mach:*:*)
+ echo mips-dec-mach_bsd4.3
+ exit 0 ;;
+ RISC*:ULTRIX:*:*)
+ echo mips-dec-ultrix${UNAME_RELEASE}
+ exit 0 ;;
+ VAX*:ULTRIX*:*:*)
+ echo vax-dec-ultrix${UNAME_RELEASE}
+ exit 0 ;;
+ 2020:CLIX:*:* | 2430:CLIX:*:*)
+ echo clipper-intergraph-clix${UNAME_RELEASE}
+ exit 0 ;;
+ 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 \
+ && $dummy `echo "${UNAME_RELEASE}" | sed -n 's/\([0-9]*\).*/\1/p'` \
+ && exit 0
+ echo mips-mips-riscos${UNAME_RELEASE}
+ exit 0 ;;
+ Motorola:PowerMAX_OS:*:*)
+ echo powerpc-motorola-powermax
+ exit 0 ;;
+ Motorola:*:4.3:PL8-*)
+ echo powerpc-harris-powermax
+ exit 0 ;;
+ Night_Hawk:*:*:PowerMAX_OS | Synergy:PowerMAX_OS:*:*)
+ echo powerpc-harris-powermax
+ exit 0 ;;
+ Night_Hawk:Power_UNIX:*:*)
+ echo powerpc-harris-powerunix
+ exit 0 ;;
+ m88k:CX/UX:7*:*)
+ echo m88k-harris-cxux7
+ exit 0 ;;
+ m88k:*:4*:R4*)
+ echo m88k-motorola-sysv4
+ exit 0 ;;
+ m88k:*:3*:R3*)
+ echo m88k-motorola-sysv3
+ exit 0 ;;
+ 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 0 ;;
+ M88*:DolphinOS:*:*) # DolphinOS (SVR3)
+ echo m88k-dolphin-sysv3
+ exit 0 ;;
+ M88*:*:R3*:*)
+ # Delta 88k system running SVR3
+ echo m88k-motorola-sysv3
+ exit 0 ;;
+ XD88*:*:*:*) # Tektronix XD88 system running UTekV (SVR3)
+ echo m88k-tektronix-sysv3
+ exit 0 ;;
+ Tek43[0-9][0-9]:UTek:*:*) # Tektronix 4300 system running UTek (BSD)
+ echo m68k-tektronix-bsd
+ exit 0 ;;
+ *:IRIX*:*:*)
+ echo mips-sgi-irix`echo ${UNAME_RELEASE}|sed -e 's/-/_/g'`
+ exit 0 ;;
+ ????????: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 0 ;; # Note that: echo "'`uname -s`'" gives 'AIX '
+ i*86:AIX:*:*)
+ echo i386-ibm-aix
+ exit 0 ;;
+ 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 0 ;;
+ *: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
+ $CC_FOR_BUILD -o $dummy $dummy.c && $dummy && exit 0
+ echo rs6000-ibm-aix3.2.5
+ 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 0 ;;
+ *: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 0 ;;
+ *:AIX:*:*)
+ echo rs6000-ibm-aix
+ exit 0 ;;
+ ibmrt:4.4BSD:*|romp-ibm:BSD:*)
+ echo romp-ibm-bsd4.4
+ exit 0 ;;
+ ibmrt:*BSD:*|romp-ibm:BSD:*) # covers RT/PC BSD and
+ echo romp-ibm-bsd${UNAME_RELEASE} # 4.3 with uname added to
+ exit 0 ;; # report: romp-ibm BSD 4.3
+ *:BOSX:*:*)
+ echo rs6000-bull-bosx
+ exit 0 ;;
+ DPX/2?00:B.O.S.:*:*)
+ echo m68k-bull-sysv3
+ exit 0 ;;
+ 9000/[34]??:4.3bsd:1.*:*)
+ echo m68k-hp-bsd
+ exit 0 ;;
+ hp300:4.4BSD:*:* | 9000/[34]??:4.3bsd:2.*:*)
+ echo m68k-hp-bsd4.4
+ exit 0 ;;
+ 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
+ # avoid double evaluation of $set_cc_for_build
+ test -n "$CC_FOR_BUILD" || eval $set_cc_for_build
+ if echo __LP64__ | (CCOPTS= $CC_FOR_BUILD -E -) | grep __LP64__ >/dev/null
+ then
+ HP_ARCH="hppa2.0w"
+ else
+ HP_ARCH="hppa64"
+ fi
+ fi
+ echo ${HP_ARCH}-hp-hpux${HPUX_REV}
+ exit 0 ;;
+ ia64:HP-UX:*:*)
+ HPUX_REV=`echo ${UNAME_RELEASE}|sed -e 's/[^.]*.[0B]*//'`
+ echo ia64-hp-hpux${HPUX_REV}
+ exit 0 ;;
+ 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 && $dummy && exit 0
+ echo unknown-hitachi-hiuxwe2
+ exit 0 ;;
+ 9000/7??:4.3bsd:*:* | 9000/8?[79]:4.3bsd:*:* )
+ echo hppa1.1-hp-bsd
+ exit 0 ;;
+ 9000/8??:4.3bsd:*:*)
+ echo hppa1.0-hp-bsd
+ exit 0 ;;
+ *9??*:MPE/iX:*:* | *3000*:MPE/iX:*:*)
+ echo hppa1.0-hp-mpeix
+ exit 0 ;;
+ hp7??:OSF1:*:* | hp8?[79]:OSF1:*:* )
+ echo hppa1.1-hp-osf
+ exit 0 ;;
+ hp8??:OSF1:*:*)
+ echo hppa1.0-hp-osf
+ exit 0 ;;
+ i*86:OSF1:*:*)
+ if [ -x /usr/sbin/sysversion ] ; then
+ echo ${UNAME_MACHINE}-unknown-osf1mk
+ else
+ echo ${UNAME_MACHINE}-unknown-osf1
+ fi
+ exit 0 ;;
+ parisc*:Lites*:*:*)
+ echo hppa1.1-hp-lites
+ exit 0 ;;
+ C1*:ConvexOS:*:* | convex:ConvexOS:C1*:*)
+ echo c1-convex-bsd
+ exit 0 ;;
+ C2*:ConvexOS:*:* | convex:ConvexOS:C2*:*)
+ if getsysinfo -f scalar_acc
+ then echo c32-convex-bsd
+ else echo c2-convex-bsd
+ fi
+ exit 0 ;;
+ C34*:ConvexOS:*:* | convex:ConvexOS:C34*:*)
+ echo c34-convex-bsd
+ exit 0 ;;
+ C38*:ConvexOS:*:* | convex:ConvexOS:C38*:*)
+ echo c38-convex-bsd
+ exit 0 ;;
+ C4*:ConvexOS:*:* | convex:ConvexOS:C4*:*)
+ echo c4-convex-bsd
+ exit 0 ;;
+ CRAY*Y-MP:*:*:*)
+ echo ymp-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/'
+ exit 0 ;;
+ 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 0 ;;
+ CRAY*TS:*:*:*)
+ echo t90-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/'
+ exit 0 ;;
+ CRAY*T3E:*:*:*)
+ echo alphaev5-cray-unicosmk${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/'
+ exit 0 ;;
+ CRAY*SV1:*:*:*)
+ echo sv1-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/'
+ exit 0 ;;
+ *:UNICOS/mp:*:*)
+ echo nv1-cray-unicosmp${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/'
+ exit 0 ;;
+ 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 0 ;;
+ i*86:BSD/386:*:* | i*86:BSD/OS:*:* | *:Ascend\ Embedded/OS:*:*)
+ echo ${UNAME_MACHINE}-pc-bsdi${UNAME_RELEASE}
+ exit 0 ;;
+ sparc*:BSD/OS:*:*)
+ echo sparc-unknown-bsdi${UNAME_RELEASE}
+ exit 0 ;;
+ *:BSD/OS:*:*)
+ echo ${UNAME_MACHINE}-unknown-bsdi${UNAME_RELEASE}
+ exit 0 ;;
+ *:FreeBSD:*:*)
+ # Determine whether the default compiler uses glibc.
+ eval $set_cc_for_build
+ sed 's/^ //' << EOF >$dummy.c
+ #include <features.h>
+ #if __GLIBC__ >= 2
+ LIBC=gnu
+ #else
+ LIBC=
+ #endif
+EOF
+ eval `$CC_FOR_BUILD -E $dummy.c 2>/dev/null | grep ^LIBC=`
+ echo ${UNAME_MACHINE}-unknown-freebsd`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'`${LIBC:+-$LIBC}
+ exit 0 ;;
+ i*:CYGWIN*:*)
+ echo ${UNAME_MACHINE}-pc-cygwin
+ exit 0 ;;
+ i*:MINGW*:*)
+ echo ${UNAME_MACHINE}-pc-mingw32
+ exit 0 ;;
+ i*:PW*:*)
+ echo ${UNAME_MACHINE}-pc-pw32
+ exit 0 ;;
+ x86:Interix*:3*)
+ echo i586-pc-interix3
+ exit 0 ;;
+ [345]86:Windows_95:* | [345]86:Windows_98:* | [345]86:Windows_NT:*)
+ echo i${UNAME_MACHINE}-pc-mks
+ exit 0 ;;
+ 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 0 ;;
+ i*:UWIN*:*)
+ echo ${UNAME_MACHINE}-pc-uwin
+ exit 0 ;;
+ p*:CYGWIN*:*)
+ echo powerpcle-unknown-cygwin
+ exit 0 ;;
+ prep*:SunOS:5.*:*)
+ echo powerpcle-unknown-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'`
+ exit 0 ;;
+ *:GNU:*:*)
+ echo `echo ${UNAME_MACHINE}|sed -e 's,[-/].*$,,'`-unknown-gnu`echo ${UNAME_RELEASE}|sed -e 's,/.*$,,'`
+ exit 0 ;;
+ i*86:Minix:*:*)
+ echo ${UNAME_MACHINE}-pc-minix
+ exit 0 ;;
+ arm*:Linux:*:*)
+ echo ${UNAME_MACHINE}-unknown-linux-gnu
+ exit 0 ;;
+ ia64:Linux:*:*)
+ echo ${UNAME_MACHINE}-unknown-linux-gnu
+ exit 0 ;;
+ m68*:Linux:*:*)
+ echo ${UNAME_MACHINE}-unknown-linux-gnu
+ exit 0 ;;
+ 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 0
+ ;;
+ 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 0
+ ;;
+ ppc:Linux:*:*)
+ echo powerpc-unknown-linux-gnu
+ exit 0 ;;
+ ppc64:Linux:*:*)
+ echo powerpc64-unknown-linux-gnu
+ exit 0 ;;
+ 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 0 ;;
+ 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 0 ;;
+ parisc64:Linux:*:* | hppa64:Linux:*:*)
+ echo hppa64-unknown-linux-gnu
+ exit 0 ;;
+ s390:Linux:*:* | s390x:Linux:*:*)
+ echo ${UNAME_MACHINE}-ibm-linux
+ exit 0 ;;
+ sh*:Linux:*:*)
+ echo ${UNAME_MACHINE}-unknown-linux-gnu
+ exit 0 ;;
+ sparc:Linux:*:* | sparc64:Linux:*:*)
+ echo ${UNAME_MACHINE}-unknown-linux-gnu
+ exit 0 ;;
+ x86_64:Linux:*:*)
+ echo x86_64-unknown-linux-gnu
+ exit 0 ;;
+ 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 0 ;;
+ coff-i386)
+ echo "${UNAME_MACHINE}-pc-linux-gnucoff"
+ exit 0 ;;
+ "")
+ # 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 0 ;;
+ 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
+EOF
+ eval `$CC_FOR_BUILD -E $dummy.c 2>/dev/null | grep ^LIBC=`
+ test x"${LIBC}" != x && echo "${UNAME_MACHINE}-pc-linux-${LIBC}" && exit 0
+ test x"${TENTATIVE}" != x && echo "${TENTATIVE}" && exit 0
+ ;;
+ 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 0 ;;
+ 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 0 ;;
+ 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 0 ;;
+ i*86:XTS-300:*:STOP)
+ echo ${UNAME_MACHINE}-unknown-stop
+ exit 0 ;;
+ i*86:atheos:*:*)
+ echo ${UNAME_MACHINE}-unknown-atheos
+ exit 0 ;;
+ i*86:LynxOS:2.*:* | i*86:LynxOS:3.[01]*:* | i*86:LynxOS:4.0*:*)
+ echo i386-unknown-lynxos${UNAME_RELEASE}
+ exit 0 ;;
+ i*86:*DOS:*:*)
+ echo ${UNAME_MACHINE}-pc-msdosdjgpp
+ exit 0 ;;
+ 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 0 ;;
+ i*86:*:5:[78]*)
+ 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 0 ;;
+ 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 0 ;;
+ 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 0 ;;
+ Intel:Mach:3*:*)
+ echo i386-pc-mach3
+ exit 0 ;;
+ paragon:*:*:*)
+ echo i860-intel-osf1
+ exit 0 ;;
+ 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 0 ;;
+ mini*:CTIX:SYS*5:*)
+ # "miniframe"
+ echo m68010-convergent-sysv
+ exit 0 ;;
+ mc68k:UNIX:SYSTEM5:3.51m)
+ echo m68k-convergent-sysv
+ exit 0 ;;
+ M680?0:D-NIX:5.3:*)
+ echo m68k-diab-dnix
+ exit 0 ;;
+ M68*:*:R3V[567]*:*)
+ test -r /sysV68 && echo 'm68k-motorola-sysv' && exit 0 ;;
+ 3[34]??:*: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)
+ 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 0
+ /bin/uname -p 2>/dev/null | /bin/grep entium >/dev/null \
+ && echo i586-ncr-sysv4.3${OS_REL} && exit 0 ;;
+ 3[34]??:*:4.0:* | 3[34]??,*:*:4.0:*)
+ /bin/uname -p 2>/dev/null | grep 86 >/dev/null \
+ && echo i486-ncr-sysv4 && exit 0 ;;
+ m68*:LynxOS:2.*:* | m68*:LynxOS:3.0*:*)
+ echo m68k-unknown-lynxos${UNAME_RELEASE}
+ exit 0 ;;
+ mc68030:UNIX_System_V:4.*:*)
+ echo m68k-atari-sysv4
+ exit 0 ;;
+ TSUNAMI:LynxOS:2.*:*)
+ echo sparc-unknown-lynxos${UNAME_RELEASE}
+ exit 0 ;;
+ rs6000:LynxOS:2.*:*)
+ echo rs6000-unknown-lynxos${UNAME_RELEASE}
+ exit 0 ;;
+ PowerPC:LynxOS:2.*:* | PowerPC:LynxOS:3.[01]*:* | PowerPC:LynxOS:4.0*:*)
+ echo powerpc-unknown-lynxos${UNAME_RELEASE}
+ exit 0 ;;
+ SM[BE]S:UNIX_SV:*:*)
+ echo mips-dde-sysv${UNAME_RELEASE}
+ exit 0 ;;
+ RM*:ReliantUNIX-*:*:*)
+ echo mips-sni-sysv4
+ exit 0 ;;
+ RM*:SINIX-*:*:*)
+ echo mips-sni-sysv4
+ exit 0 ;;
+ *: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 0 ;;
+ PENTIUM:*:4.0*:*) # Unisys `ClearPath HMP IX 4000' SVR4/MP effort
+ # says <Richard.M.Bartel@ccMail.Census.GOV>
+ echo i586-unisys-sysv4
+ exit 0 ;;
+ *:UNIX_System_V:4*:FTX*)
+ # From Gerald Hewes <hewes@openmarket.com>.
+ # How about differentiating between stratus architectures? -djm
+ echo hppa1.1-stratus-sysv4
+ exit 0 ;;
+ *:*:*:FTX*)
+ # From seanf@swdc.stratus.com.
+ echo i860-stratus-sysv4
+ exit 0 ;;
+ *:VOS:*:*)
+ # From Paul.Green@stratus.com.
+ echo hppa1.1-stratus-vos
+ exit 0 ;;
+ mc68*:A/UX:*:*)
+ echo m68k-apple-aux${UNAME_RELEASE}
+ exit 0 ;;
+ news*:NEWS-OS:6*:*)
+ echo mips-sony-newsos6
+ exit 0 ;;
+ 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 0 ;;
+ BeBox:BeOS:*:*) # BeOS running on hardware made by Be, PPC only.
+ echo powerpc-be-beos
+ exit 0 ;;
+ BeMac:BeOS:*:*) # BeOS running on Mac or Mac clone, PPC only.
+ echo powerpc-apple-beos
+ exit 0 ;;
+ BePC:BeOS:*:*) # BeOS running on Intel PC compatible.
+ echo i586-pc-beos
+ exit 0 ;;
+ SX-4:SUPER-UX:*:*)
+ echo sx4-nec-superux${UNAME_RELEASE}
+ exit 0 ;;
+ SX-5:SUPER-UX:*:*)
+ echo sx5-nec-superux${UNAME_RELEASE}
+ exit 0 ;;
+ SX-6:SUPER-UX:*:*)
+ echo sx6-nec-superux${UNAME_RELEASE}
+ exit 0 ;;
+ Power*:Rhapsody:*:*)
+ echo powerpc-apple-rhapsody${UNAME_RELEASE}
+ exit 0 ;;
+ *:Rhapsody:*:*)
+ echo ${UNAME_MACHINE}-apple-rhapsody${UNAME_RELEASE}
+ exit 0 ;;
+ *:Darwin:*:*)
+ case `uname -p` in
+ *86) UNAME_PROCESSOR=i686 ;;
+ powerpc) UNAME_PROCESSOR=powerpc ;;
+ esac
+ echo ${UNAME_PROCESSOR}-apple-darwin${UNAME_RELEASE}
+ exit 0 ;;
+ *: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 0 ;;
+ *:QNX:*:4*)
+ echo i386-pc-qnx
+ exit 0 ;;
+ NSR-[DGKLNPTVW]:NONSTOP_KERNEL:*:*)
+ echo nsr-tandem-nsk${UNAME_RELEASE}
+ exit 0 ;;
+ *:NonStop-UX:*:*)
+ echo mips-compaq-nonstopux
+ exit 0 ;;
+ BS2000:POSIX*:*:*)
+ echo bs2000-siemens-sysv
+ exit 0 ;;
+ DS/*:UNIX_System_V:*:*)
+ echo ${UNAME_MACHINE}-${UNAME_SYSTEM}-${UNAME_RELEASE}
+ exit 0 ;;
+ *: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 0 ;;
+ *:TOPS-10:*:*)
+ echo pdp10-unknown-tops10
+ exit 0 ;;
+ *:TENEX:*:*)
+ echo pdp10-unknown-tenex
+ exit 0 ;;
+ KS10:TOPS-20:*:* | KL10:TOPS-20:*:* | TYPE4:TOPS-20:*:*)
+ echo pdp10-dec-tops20
+ exit 0 ;;
+ XKL-1:TOPS-20:*:* | TYPE5:TOPS-20:*:*)
+ echo pdp10-xkl-tops20
+ exit 0 ;;
+ *:TOPS-20:*:*)
+ echo pdp10-unknown-tops20
+ exit 0 ;;
+ *:ITS:*:*)
+ echo pdp10-unknown-its
+ exit 0 ;;
+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"); 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 && $dummy && exit 0
+
+# Apollos put the system type in the environment.
+
+test -d /usr/apollo && { echo ${ISP}-apollo-${SYSTYPE}; exit 0; }
+
+# 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 0 ;;
+ c2*)
+ if getsysinfo -f scalar_acc
+ then echo c32-convex-bsd
+ else echo c2-convex-bsd
+ fi
+ exit 0 ;;
+ c34*)
+ echo c34-convex-bsd
+ exit 0 ;;
+ c38*)
+ echo c38-convex-bsd
+ exit 0 ;;
+ c4*)
+ echo c4-convex-bsd
+ exit 0 ;;
+ 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
+
+ ftp://ftp.gnu.org/pub/gnu/config/
+
+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/source/config.sub b/source/config.sub
new file mode 100755
index 00000000000..2476310dff3
--- /dev/null
+++ b/source/config.sub
@@ -0,0 +1,1421 @@
+#! /bin/sh
+# Configuration validation subroutine script.
+# Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001
+# Free Software Foundation, Inc.
+
+timestamp='2001-12-03'
+
+# 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 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.
+
+# 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
+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 0 ;;
+ --version | -v )
+ echo "$version" ; exit 0 ;;
+ --help | --h* | -h )
+ echo "$usage"; exit 0 ;;
+ -- ) # 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 0;;
+
+ * )
+ 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* | storm-chaos* | os2-emx* | windows32-*)
+ 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)
+ 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] \
+ | arc | arm | arm[bl]e | arme[lb] | armv[2345] | armv[345][lb] | avr \
+ | c4x | clipper \
+ | d10v | d30v | dsp16xx \
+ | fr30 \
+ | h8300 | h8500 | hppa | hppa1.[01] | hppa2.0 | hppa2.0[nw] | hppa64 \
+ | i370 | i860 | i960 | ia64 \
+ | m32r | m68000 | m68k | m88k | mcore \
+ | mips16 | mips64 | mips64el | mips64orion | mips64orionel \
+ | mips64vr4100 | mips64vr4100el | mips64vr4300 \
+ | mips64vr4300el | mips64vr5000 | mips64vr5000el \
+ | mipsbe | mipseb | mipsel | mipsle | mipstx39 | mipstx39el \
+ | mipsisa32 \
+ | mn10200 | mn10300 \
+ | ns16k | ns32k \
+ | openrisc \
+ | pdp10 | pdp11 | pj | pjl \
+ | powerpc | powerpc64 | powerpc64le | powerpcle | ppcbe \
+ | pyramid \
+ | sh | sh[34] | sh[34]eb | shbe | shle \
+ | sparc | sparc64 | sparclet | sparclite | sparcv9 | sparcv9b \
+ | strongarm \
+ | tahoe | thumb | tic80 | tron \
+ | v850 | v850e \
+ | we32k \
+ | x86 | xscale | xstormy16 | xtensa \
+ | z8k)
+ 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]-* \
+ | alphapca5[67]-* | arc-* \
+ | arm-* | armbe-* | armle-* | armv*-* \
+ | avr-* \
+ | bs2000-* \
+ | c[123]* | c30-* | [cjt]90-* | c54x-* \
+ | clipper-* | cray2-* | cydra-* \
+ | d10v-* | d30v-* \
+ | elxsi-* \
+ | f30[01]-* | f700-* | fr30-* | fx80-* \
+ | h8300-* | h8500-* \
+ | hppa-* | hppa1.[01]-* | hppa2.0-* | hppa2.0[nw]-* | hppa64-* \
+ | i*86-* | i860-* | i960-* | ia64-* \
+ | m32r-* \
+ | m68000-* | m680[01234]0-* | m68360-* | m683?2-* | m68k-* \
+ | m88110-* | m88k-* | mcore-* \
+ | mips-* | mips16-* | mips64-* | mips64el-* | mips64orion-* \
+ | mips64orionel-* | mips64vr4100-* | mips64vr4100el-* \
+ | mips64vr4300-* | mips64vr4300el-* | mipsbe-* | mipseb-* \
+ | mipsle-* | mipsel-* | mipstx39-* | mipstx39el-* \
+ | none-* | np1-* | ns16k-* | ns32k-* \
+ | orion-* \
+ | pdp10-* | pdp11-* | pj-* | pjl-* | pn-* | power-* \
+ | powerpc-* | powerpc64-* | powerpc64le-* | powerpcle-* | ppcbe-* \
+ | pyramid-* \
+ | romp-* | rs6000-* \
+ | sh-* | sh[34]-* | sh[34]eb-* | shbe-* | shle-* \
+ | sparc-* | sparc64-* | sparc86x-* | sparclite-* \
+ | sparcv9-* | sparcv9b-* | strongarm-* | sv1-* \
+ | t3e-* | tahoe-* | thumb-* | tic30-* | tic54x-* | tic80-* | tron-* \
+ | v850-* | v850e-* | vax-* \
+ | we32k-* \
+ | x86-* | x86_64-* | xmp-* | xps100-* | xscale-* | xstormy16-* \
+ | xtensa-* \
+ | ymp-* \
+ | z8k-*)
+ ;;
+ # 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
+ ;;
+ 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
+ ;;
+ 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
+ ;;
+ 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 | ymp)
+ basic_machine=ymp-cray
+ os=-unicos
+ ;;
+ cray2)
+ basic_machine=cray2-cray
+ os=-unicos
+ ;;
+ [cjt]90)
+ basic_machine=${basic_machine}-cray
+ os=-unicos
+ ;;
+ crds | unos)
+ basic_machine=m68k-crds
+ ;;
+ cris | cris-* | etrax*)
+ basic_machine=cris-axis
+ ;;
+ da30 | da30-*)
+ basic_machine=m68k-da30
+ ;;
+ decstation | decstation-3100 | pmax | pmax-* | pmin | dec3100 | decstatn)
+ basic_machine=mips-dec
+ ;;
+ delta | 3300 | motorola-3300 | motorola-delta \
+ | 3300-motorola | delta-motorola)
+ basic_machine=m68k-motorola
+ ;;
+ delta88)
+ basic_machine=m88k-motorola
+ os=-sysv3
+ ;;
+ 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
+ ;;
+ mipsel*-linux*)
+ basic_machine=mipsel-unknown
+ os=-linux-gnu
+ ;;
+ mips*-linux*)
+ basic_machine=mips-unknown
+ os=-linux-gnu
+ ;;
+ mips3*-*)
+ basic_machine=`echo $basic_machine | sed -e 's/mips3/mips64/'`
+ ;;
+ mips3*)
+ basic_machine=`echo $basic_machine | sed -e 's/mips3/mips64/'`-unknown
+ ;;
+ mmix*)
+ basic_machine=mmix-knuth
+ os=-mmixware
+ ;;
+ monitor)
+ basic_machine=m68k-rom68k
+ os=-coff
+ ;;
+ 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
+ ;;
+ 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)
+ basic_machine=i686-pc
+ ;;
+ pentiumii | pentium2)
+ basic_machine=i686-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-*)
+ basic_machine=i686-`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
+ ;;
+ sequent)
+ basic_machine=i386-sequent
+ ;;
+ sh)
+ basic_machine=sh-hitachi
+ os=-hms
+ ;;
+ 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=t3e-cray
+ os=-unicos
+ ;;
+ tic54x | c54x*)
+ basic_machine=tic54x-unknown
+ os=-coff
+ ;;
+ tx39)
+ basic_machine=mipstx39-unknown
+ ;;
+ tx39el)
+ basic_machine=mipstx39el-unknown
+ ;;
+ tower | tower-32)
+ basic_machine=m68k-ncr
+ ;;
+ 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
+ ;;
+ windows32)
+ basic_machine=i386-pc
+ os=-windows32-msvcrt
+ ;;
+ xmp)
+ basic_machine=xmp-cray
+ os=-unicos
+ ;;
+ xps | xps100)
+ basic_machine=xps100-honeywell
+ ;;
+ 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
+ ;;
+ mips)
+ if [ x$os = x-linux-gnu ]; then
+ basic_machine=mips-unknown
+ else
+ basic_machine=mips-mips
+ fi
+ ;;
+ romp)
+ basic_machine=romp-ibm
+ ;;
+ 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
+ ;;
+ sh3 | sh4 | sh3eb | sh4eb)
+ basic_machine=sh-unknown
+ ;;
+ sparc | 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
+ ;;
+ c4x*)
+ basic_machine=c4x-none
+ os=-coff
+ ;;
+ *-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* | -netbsd* | -openbsd* | -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* | -uxpv* | -beos* | -mpeix* | -udk* \
+ | -interix* | -uwin* | -rhapsody* | -darwin* | -opened* \
+ | -openstep* | -oskit* | -conix* | -pw32* | -nonstopux* \
+ | -storm-chaos* | -tops10* | -tenex* | -tops20* | -its* \
+ | -os2* | -vos* | -palmos* | -uclinux* | -nucleus*)
+ # Remember, each alternative MUST END IN *, to match a version number.
+ ;;
+ -qnx*)
+ case $basic_machine in
+ x86-* | i*86-*)
+ ;;
+ *)
+ os=-nto$os
+ ;;
+ esac
+ ;;
+ -nto*)
+ os=-nto-qnx
+ ;;
+ -sim | -es1800* | -hms* | -xray | -os68k* | -none* | -v88r* \
+ | -windows* | -osx | -abug | -netware* | -os9* | -beos* \
+ | -macos* | -mpw* | -magic* | -mmixware* | -mon960* | -lnews*)
+ ;;
+ -mac*)
+ os=`echo $os | sed -e 's|mac|macos|'`
+ ;;
+ -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
+ ;;
+ -wince*)
+ os=-wince
+ ;;
+ -osfrose*)
+ os=-osfrose
+ ;;
+ -osf*)
+ os=-osf
+ ;;
+ -utek*)
+ os=-bsd
+ ;;
+ -dynix*)
+ os=-bsd
+ ;;
+ -acis*)
+ os=-aos
+ ;;
+ -atheos*)
+ os=-atheos
+ ;;
+ -386bsd)
+ os=-bsd
+ ;;
+ -ctix* | -uts*)
+ os=-sysv
+ ;;
+ -ns2 )
+ os=-nextstep2
+ ;;
+ -nsk*)
+ os=-nsk
+ ;;
+ # Preserve the version number of sinix5.
+ -sinix5.*)
+ os=`echo $os | sed -e 's|sinix|sysv|'`
+ ;;
+ -sinix*)
+ os=-sysv4
+ ;;
+ -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
+ ;;
+ -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
+ ;;
+ 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
+ ;;
+ *-tti) # must be before sparc entry or we get the wrong os.
+ os=-sysv3
+ ;;
+ sparc-* | *-sun)
+ os=-sunos4.1.1
+ ;;
+ *-be)
+ os=-beos
+ ;;
+ *-ibm)
+ os=-aix
+ ;;
+ *-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
+ ;;
+ -ptx*)
+ vendor=sequent
+ ;;
+ -vxsim* | -vxworks*)
+ 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 0
+
+# 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/source/configure.developer b/source/configure.developer
new file mode 100755
index 00000000000..a7074acfd3a
--- /dev/null
+++ b/source/configure.developer
@@ -0,0 +1,2 @@
+#!/bin/sh
+`dirname $0`/configure --enable-developer "$@"
diff --git a/source/configure.in b/source/configure.in
new file mode 100644
index 00000000000..f546069e08a
--- /dev/null
+++ b/source/configure.in
@@ -0,0 +1,4442 @@
+dnl Process this file with autoconf to produce a configure script.
+
+dnl We must use autotools 2.53 or above
+AC_PREREQ(2.53)
+AC_INIT(include/includes.h)
+AC_CONFIG_HEADER(include/config.h)
+
+AC_DISABLE_STATIC
+AC_ENABLE_SHARED
+
+#################################################
+# Directory handling stuff to support both the
+# legacy SAMBA directories and FHS compliant
+# ones...
+AC_PREFIX_DEFAULT(/usr/local/samba)
+
+lockdir="\${VARDIR}/locks"
+piddir="\${VARDIR}/locks"
+mandir="\${prefix}/man"
+logfilebase="\${VARDIR}"
+privatedir="\${prefix}/private"
+libdir="\${prefix}/lib"
+configdir="\${LIBDIR}"
+swatdir="\${prefix}/swat"
+
+AC_ARG_WITH(fhs,
+[ --with-fhs Use FHS-compliant paths (default=no)],
+[ case "$withval" in
+ yes)
+ lockdir="\${VARDIR}/lib/samba"
+ piddir="\${VARDIR}/run"
+ mandir="\${prefix}/share/man"
+ logfilebase="\${VARDIR}/log/samba"
+ privatedir="\${CONFIGDIR}/private"
+ libdir="\${prefix}/lib/samba"
+ configdir="${sysconfdir}/samba"
+ swatdir="\${DATADIR}/samba/swat"
+ ;;
+ esac])
+
+#################################################
+# set private directory location
+AC_ARG_WITH(privatedir,
+[ --with-privatedir=DIR Where to put smbpasswd ($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 lock directory location
+AC_ARG_WITH(lockdir,
+[ --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,
+[ --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 SWAT directory location
+AC_ARG_WITH(swatdir,
+[ --with-swatdir=DIR Where to put SWAT files ($ac_default_prefix/swat)],
+[ case "$withval" in
+ yes|no)
+ #
+ # Just in case anybody does it
+ #
+ AC_MSG_WARN([--with-swatdir called without argument - will use default])
+ ;;
+ * )
+ swatdir="$withval"
+ ;;
+ esac])
+
+#################################################
+# set configuration directory location
+AC_ARG_WITH(configdir,
+[ --with-configdir=DIR Where to put configuration files ($libdir)],
+[ case "$withval" in
+ yes|no)
+ #
+ # Just in case anybody does it
+ #
+ AC_MSG_WARN([--with-configdir called without argument - will use default])
+ ;;
+ * )
+ configdir="$withval"
+ ;;
+ esac])
+
+#################################################
+# set log directory location
+AC_ARG_WITH(logfilebase,
+[ --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])
+
+#################################################
+# set lib directory location
+AC_ARG_WITH(libdir,
+[ --with-libdir=DIR Where to put libdir ($libdir)],
+[ case "$withval" in
+ yes|no)
+ #
+ # Just in case anybody does it
+ #
+ AC_MSG_WARN([--with-libdir without argument - will use default])
+ ;;
+ * )
+ libdir="$withval"
+ ;;
+ esac])
+
+#################################################
+# set lib directory location
+AC_ARG_WITH(mandir,
+[ --with-mandir=DIR Where to put man pages ($mandir)],
+[ case "$withval" in
+ yes|no)
+ #
+ # Just in case anybody does it
+ #
+ AC_MSG_WARN([--with-mandir without argument - will use default])
+ ;;
+ * )
+ mandir="$withval"
+ ;;
+ esac])
+
+AC_ARG_WITH(cfenc,
+[ --with-cfenc=HEADERDIR Use internal CoreFoundation encoding API
+ for optimization (Mac OS X/Darwin only)],
+[
+# May be in source $withval/CoreFoundation/StringEncodings.subproj.
+# Should have been in framework $withval/CoreFoundation.framework/Headers.
+for d in \
+ $withval/CoreFoundation/StringEncodings.subproj \
+ $withval/StringEncodings.subproj \
+ $withval/CoreFoundation.framework/Headers \
+ $withval/Headers \
+ $withval
+do
+ if test -r $d/CFStringEncodingConverter.h; then
+ ln -sfh $d include/CoreFoundation
+ fi
+done
+])
+
+AC_SUBST(configdir)
+AC_SUBST(lockdir)
+AC_SUBST(piddir)
+AC_SUBST(logfilebase)
+AC_SUBST(privatedir)
+AC_SUBST(swatdir)
+AC_SUBST(bindir)
+AC_SUBST(sbindir)
+
+dnl Unique-to-Samba variables we'll be playing with.
+AC_SUBST(SHELL)
+AC_SUBST(LDSHFLAGS)
+AC_SUBST(SONAMEFLAG)
+AC_SUBST(SHLD)
+AC_SUBST(HOST_OS)
+AC_SUBST(PICFLAGS)
+AC_SUBST(PICSUFFIX)
+AC_SUBST(SHLIBEXT)
+AC_SUBST(INSTALLCLIENT)
+AC_SUBST(INSTALLCLIENTCMD_SH)
+AC_SUBST(INSTALLCLIENTCMD_A)
+AC_SUBST(LIBSMBCLIENT_SHARED)
+AC_SUBST(LIBSMBCLIENT)
+AC_SUBST(PRINT_LIBS)
+AC_SUBST(AUTH_LIBS)
+AC_SUBST(ACL_LIBS)
+AC_SUBST(PASSDB_LIBS)
+AC_SUBST(IDMAP_LIBS)
+AC_SUBST(KRB5_LIBS)
+AC_SUBST(LDAP_LIBS)
+AC_SUBST(SHLIB_PROGS)
+AC_SUBST(SMBWRAPPER)
+AC_SUBST(EXTRA_BIN_PROGS)
+AC_SUBST(EXTRA_SBIN_PROGS)
+AC_SUBST(EXTRA_ALL_TARGETS)
+AC_SUBST(CONFIG_LIBS)
+
+AC_ARG_ENABLE(debug,
+[ --enable-debug Turn on compiler debugging information (default=no)],
+ [if eval "test x$enable_debug = xyes"; then
+ CFLAGS="${CFLAGS} -g"
+ fi])
+
+AC_ARG_ENABLE(developer, [ --enable-developer Turn on developer warnings and debugging (default=no)],
+ [if eval "test x$enable_developer = xyes"; then
+ developer=yes
+ CFLAGS="${CFLAGS} -gstabs -Wall -Wshadow -Wstrict-prototypes -Wpointer-arith -Wcast-qual -Wcast-align -Wwrite-strings -DDEBUG_PASSWORD -DDEVELOPER"
+ fi])
+
+AC_ARG_ENABLE(krb5developer, [ --enable-krb5developer Turn on developer warnings and debugging, except -Wstrict-prototypes (default=no)],
+ [if eval "test x$enable_krb5developer = xyes"; then
+ developer=yes
+ CFLAGS="${CFLAGS} -gstabs -Wall -Wshadow -Wpointer-arith -Wcast-qual -Wcast-align -Wwrite-strings -DDEBUG_PASSWORD -DDEVELOPER"
+ fi])
+
+AC_ARG_ENABLE(dmalloc, [ --enable-dmalloc Enable heap debugging [default=no]])
+
+if test "x$enable_dmalloc" = xyes
+then
+ AC_DEFINE(ENABLE_DMALLOC, 1, [Define to turn on dmalloc debugging])
+ AC_DEFINE(DMALLOC_FUNC_CHECK, 1,
+ [Define to check invariants around some common functions])
+ LIBS="$LIBS -ldmalloc"
+fi
+
+dnl Checks for programs.
+
+##
+## for some reason this macro resets the CFLAGS
+## so save and restore
+##
+OLD_CFLAGS=${CFLAGS}
+AC_PROG_CC
+CFLAGS=${OLD_CFLAGS}
+
+OLD_CFLAGS=${CFLAGS}
+AC_PROG_CPP
+CFLAGS=${OLD_CFLAGS}
+
+AC_PROG_INSTALL
+AC_PROG_AWK
+AC_PATH_PROG(PERL, perl)
+
+AC_CHECK_TOOL(AR, ar)
+
+# compile with optimization and without debugging by default, but
+# allow people to set their own preference.
+if test "x$CFLAGS" = x
+then
+ CFLAGS="-O ${CFLAGS}"
+fi
+
+dnl Check if we use GNU ld
+LD=ld
+AC_PROG_LD_GNU
+
+dnl needed before AC_TRY_COMPILE
+AC_ISC_POSIX
+
+dnl look for executable suffix
+AC_EXEEXT
+
+dnl Check if C compiler understands -c and -o at the same time
+AC_PROG_CC_C_O
+if eval "test \"`echo '$ac_cv_prog_cc_'${ac_cc}_c_o`\" = no"; then
+ BROKEN_CC=
+else
+ BROKEN_CC=#
+fi
+AC_SUBST(BROKEN_CC)
+
+dnl Check if the C compiler understands -Werror
+AC_CACHE_CHECK([that the C compiler understands -Werror],samba_cv_HAVE_Werror, [
+ AC_TRY_RUN_STRICT([
+ int main(void)
+ {
+ return 0;
+ }],[-Werror],[$CPPFLAGS],[$LDFLAGS],
+ samba_cv_HAVE_Werror=yes,samba_cv_HAVE_Werror=no,samba_cv_HAVE_Werror=cross)])
+if test x"$samba_cv_HAVE_Werror" = x"yes"; then
+ Werror_FLAGS="-Werror"
+else
+dnl Check if the C compiler understands -w2
+AC_CACHE_CHECK([that the C compiler understands -w2],samba_cv_HAVE_w2, [
+ AC_TRY_RUN_STRICT([
+ int main(void)
+ {
+ return 0;
+ }],[-w2],[$CPPFLAGS],[$LDFLAGS],
+ samba_cv_HAVE_w2=yes,samba_cv_HAVE_w2=no,samba_cv_HAVE_w2=cross)])
+if test x"$samba_cv_HAVE_w2" = x"yes"; then
+ Werror_FLAGS="-w2"
+fi
+fi
+
+dnl Check if the C compiler understands volatile (it should, being ANSI).
+AC_CACHE_CHECK([that the C compiler understands volatile],samba_cv_volatile, [
+ AC_TRY_COMPILE([#include <sys/types.h>],[volatile int i = 0],
+ samba_cv_volatile=yes,samba_cv_volatile=no)])
+if test x"$samba_cv_volatile" = x"yes"; then
+ AC_DEFINE(HAVE_VOLATILE, 1, [Whether the C compiler understands volatile])
+fi
+
+UNAME_S=`(uname -s) 2>/dev/null` || UNAME_S="unknown"
+AC_MSG_CHECKING(uname -s)
+AC_MSG_RESULT(${UNAME_S})
+
+UNAME_R=`(uname -r) 2>/dev/null` || UNAME_R="unknown"
+AC_MSG_CHECKING(uname -r)
+AC_MSG_RESULT(${UNAME_R})
+
+UNAME_M=`(uname -m) 2>/dev/null` || UNAME_M="unknown"
+AC_MSG_CHECKING(uname -m)
+AC_MSG_RESULT(${UNAME_M})
+
+UNAME_P=`(uname -p) 2>/dev/null` || UNAME_P="unknown"
+AC_MSG_CHECKING(uname -p)
+AC_MSG_RESULT(${UNAME_P})
+
+AC_CANONICAL_SYSTEM
+
+dnl Add #include for broken IRIX header files
+ case "$host_os" in
+ *irix6*) AC_ADD_INCLUDE(<standards.h>)
+ ;;
+esac
+
+AC_VALIDATE_CACHE_SYSTEM_TYPE
+
+DYNEXP=
+
+dnl Add modules that have to be built by default here
+dnl These have to be built static:
+default_static_modules="pdb_smbpasswd pdb_tdbsam rpc_lsa rpc_samr rpc_reg rpc_lsa_ds rpc_wks rpc_net rpc_dfs rpc_srv rpc_spoolss rpc_epmapper auth_rhosts auth_sam auth_unix auth_winbind auth_server auth_domain auth_builtin"
+
+dnl These are preferably build shared, and static if dlopen() is not available
+default_shared_modules="vfs_recycle vfs_audit vfs_extd_audit vfs_netatalk vfs_fake_perms vfs_default_quota vfs_readonly vfs_cap vfs_expand_msdfs charset_CP850 charset_CP437"
+
+if test "x$developer" = xyes; then
+ default_static_modules="$default_static_modules rpc_echo"
+ default_shared_modules="$default_shared_modules charset_weird"
+fi
+
+#
+# Config CPPFLAG settings for strange OS's that must be set
+# before other tests.
+#
+case "$host_os" in
+# Try to work out if this is the native HPUX compiler that uses the -Ae flag.
+ *hpux*)
+
+ AC_PROG_CC_FLAG(Ae)
+ # mmap on HPUX is completely broken...
+ AC_DEFINE(MMAP_BLACKLIST, 1, [Whether MMAP is broken])
+ if test $ac_cv_prog_cc_Ae = yes; then
+ CPPFLAGS="$CPPFLAGS -Ae"
+ fi
+#
+# Defines needed for HPUX support.
+# HPUX has bigcrypt but (sometimes?) doesn't use it for
+# password hashing - hence the USE_BOTH_CRYPT_CALLS define.
+#
+ case `uname -r` in
+ *9*|*10*)
+ CPPFLAGS="$CPPFLAGS -D_HPUX_SOURCE -D_POSIX_SOURCE -D_ALIGNMENT_REQUIRED=1 -D_MAX_ALIGNMENT=4 -DMAX_POSITIVE_LOCK_OFFSET=0x1ffffffffffLL"
+ AC_DEFINE(USE_BOTH_CRYPT_CALLS, 1, [Whether to use both of HPUX' crypt calls])
+ AC_DEFINE(_HPUX_SOURCE, 1, [Whether to use HPUX extensions])
+ AC_DEFINE(_POSIX_SOURCE, 1, [Whether to use POSIX compatible functions])
+ AC_DEFINE(_ALIGNMENT_REQUIRED,1,[Required alignment])
+ AC_DEFINE(_MAX_ALIGNMENT,4,[Maximum alignment])
+ ;;
+ *11*)
+ CPPFLAGS="$CPPFLAGS -D_HPUX_SOURCE -D_POSIX_SOURCE -D_LARGEFILE64_SOURCE -D_ALIGNMENT_REQUIRED=1 -D_MAX_ALIGNMENT=4 -DMAX_POSITIVE_LOCK_OFFSET=0x1ffffffffffLL"
+ AC_DEFINE(USE_BOTH_CRYPT_CALLS, 1, [Whether to use both of HPUX' crypt calls])
+ AC_DEFINE(_HPUX_SOURCE, 1, [Whether to use HPUX extensions])
+ AC_DEFINE(_POSIX_SOURCE, 1, [Whether to use POSIX compatible functions])
+ AC_DEFINE(_LARGEFILE64_SOURCE, 1, [Whether to use large file support])
+ AC_DEFINE(_ALIGNMENT_REQUIRED, 1, [Required alignment])
+ AC_DEFINE(_MAX_ALIGNMENT, 4, [Maximum alignment])
+ ;;
+ esac
+ DYNEXP="-Wl,-E"
+ ;;
+
+#
+# CRAY Unicos has broken const handling
+ *unicos*)
+ AC_MSG_RESULT([disabling const])
+ CPPFLAGS="$CPPFLAGS -Dconst="
+ ;;
+
+#
+# AIX4.x doesn't even admit to having large
+# files *at all* unless the -D_LARGE_FILE or -D_LARGE_FILE_API flags are set.
+#
+ *aix4*)
+ AC_MSG_RESULT([enabling large file support])
+ CPPFLAGS="$CPPFLAGS -D_LARGE_FILES"
+ AC_DEFINE(_LARGE_FILES, 1, [Whether to enable large file support])
+ ;;
+#
+# Defines needed for Solaris 2.6/2.7 aka 7.0 to make it admit
+# to the existance of large files..
+# Note that -D_LARGEFILE64_SOURCE is different from the Sun
+# recommendations on large file support, however it makes the
+# compile work using gcc 2.7 and 2.8, whereas using the Sun
+# recommendation makes the compile fail on gcc2.7. JRA.
+#
+# Solaris uses SYSV printing. Make sure to set that here. --jerry
+#
+ *solaris*)
+ AC_DEFINE(SYSV, 1, [Whether to enable System V compatibility])
+ case `uname -r` in
+ 5.0*|5.1*|5.2*|5.3*|5.5*)
+ AC_MSG_RESULT([no large file support])
+ ;;
+ 5.*)
+ AC_MSG_RESULT([enabling large file support])
+ if test "$ac_cv_prog_gcc" = yes; then
+ ${CC-cc} -v >conftest.c 2>&1
+ ac_cv_gcc_compiler_version_number=`grep 'gcc version' conftest.c`
+ rm -fr conftest.c
+ case "$ac_cv_gcc_compiler_version_number" in
+ *"gcc version 2.6"*|*"gcc version 2.7"*)
+ CPPFLAGS="$CPPFLAGS -D_LARGEFILE64_SOURCE"
+ AC_DEFINE(_LARGEFILE64_SOURCE, 1, [Whether to enable large file support])
+ ;;
+ *)
+ CPPFLAGS="$CPPFLAGS -D_LARGEFILE_SOURCE -D_FILE_OFFSET_BITS=64"
+ AC_DEFINE(_LARGEFILE64_SOURCE, 1, [Whether to enable large file support])
+ AC_DEFINE(_FILE_OFFSET_BITS, 64, [File offset bits])
+ ;;
+ esac
+ else
+ CPPFLAGS="$CPPFLAGS -D_LARGEFILE_SOURCE -D_FILE_OFFSET_BITS=64"
+ AC_DEFINE(_LARGEFILE64_SOURCE, 1, [Whether to enable large file support])
+ AC_DEFINE(_FILE_OFFSET_BITS, 64, [File offset bits])
+ fi
+ ;;
+ esac
+ ;;
+#
+# IRIX uses SYSV printing. Make sure to set that here
+#
+ *irix*)
+ AC_DEFINE(SYSV, 1, [Whether to enable System V compatibility])
+ ;;
+ *freebsd*)
+ AC_DEFINE(FREEBSD, 1, [Whether the host os is FreeBSD])
+ ;;
+#
+# VOS may need to have POSIX support and System V compatibility enabled.
+#
+ *vos*)
+ case "$CPPFLAGS" in
+ *-D_POSIX_C_SOURCE*)
+ ;;
+ *)
+ CPPFLAGS="$CPPFLAGS -D_POSIX_C_SOURCE=199506L"
+ AC_DEFINE(_POSIX_C_SOURCE, 199506L, [Whether to enable POSIX support])
+ ;;
+ esac
+ case "$CPPFLAGS" in
+ *-D_SYSV*|*-D_SVID_SOURCE*)
+ ;;
+ *)
+ CPPFLAGS="$CPPFLAGS -D_SYSV"
+ AC_DEFINE(_SYSV, 1, [Whether to enable System V compatibility])
+ esac
+ ;;
+#
+# Tests needed for SINIX large file support.
+#
+ *sysv4*)
+ if test $host = mips-sni-sysv4 ; then
+ AC_MSG_CHECKING([for LFS support])
+ old_CPPFLAGS="$CPPFLAGS"
+ CPPFLAGS="-D_LARGEFILE64_SOURCE $CPPFLAGS"
+ AC_TRY_RUN([
+#include <unistd.h>
+main () {
+#if _LFS64_LARGEFILE == 1
+exit(0);
+#else
+exit(1);
+#endif
+}], [SINIX_LFS_SUPPORT=yes], [SINIX_LFS_SUPPORT=no], [SINIX_LFS_SUPPORT=cross])
+ CPPFLAGS="$old_CPPFLAGS"
+ if test x$SINIX_LFS_SUPPORT = xyes ; then
+ CPPFLAGS="-D_LARGEFILE64_SOURCE $CPPFLAGS"
+ AC_DEFINE(_LARGEFILE64_SOURCE, 1, [Whether to enable large file support])
+ CFLAGS="`getconf LFS64_CFLAGS` $CFLAGS"
+ LDFLAGS="`getconf LFS64_LDFLAGS` $LDFLAGS"
+ LIBS="`getconf LFS64_LIBS` $LIBS"
+ fi
+ AC_MSG_RESULT([$SINIX_LFS_SUPPORT])
+ fi
+ ;;
+
+# Tests for linux LFS support. Need kernel 2.4 and glibc2.2 or greater support.
+#
+ *linux*)
+ AC_MSG_CHECKING([for LFS support])
+ old_CPPFLAGS="$CPPFLAGS"
+ CPPFLAGS="-D_LARGEFILE64_SOURCE -D_FILE_OFFSET_BITS=64 -D_GNU_SOURCE $CPPFLAGS"
+ AC_TRY_RUN([
+#include <unistd.h>
+#include <sys/utsname.h>
+#include <string.h>
+#include <stdlib.h>
+main() {
+#if _LFS64_LARGEFILE == 1
+ struct utsname uts;
+ char *release;
+ int major, minor;
+
+ /* Ensure this is glibc 2.2 or higher */
+#if defined(__GLIBC__) && defined(__GLIBC_MINOR__)
+ int libc_major = __GLIBC__;
+ int libc_minor = __GLIBC_MINOR__;
+
+ if (libc_major < 2)
+ exit(1);
+ if (libc_minor < 2)
+ exit(1);
+#endif
+
+ /* Ensure this is kernel 2.4 or higher */
+
+ uname(&uts);
+ release = strdup(uts.release);
+ major = atoi(strsep(&release, "."));
+ minor = atoi(strsep(&release, "."));
+
+ if (major > 2 || (major == 2 && minor > 3))
+ exit(0);
+ exit(1);
+#else
+ exit(1);
+#endif
+}
+], [LINUX_LFS_SUPPORT=yes], [LINUX_LFS_SUPPORT=no], [LINUX_LFS_SUPPORT=cross])
+ CPPFLAGS="$old_CPPFLAGS"
+ if test x$LINUX_LFS_SUPPORT = xyes ; then
+ CPPFLAGS="-D_LARGEFILE64_SOURCE -D_FILE_OFFSET_BITS=64 -D_GNU_SOURCE $CPPFLAGS"
+ AC_DEFINE(_LARGEFILE64_SOURCE, 1, [Whether to enable large file support])
+ AC_DEFINE(_FILE_OFFSET_BITS, 64, [File offset bits])
+ AC_DEFINE(_GNU_SOURCE, 1, [Whether to use GNU libc extensions])
+ fi
+ AC_MSG_RESULT([$LINUX_LFS_SUPPORT])
+ ;;
+
+#
+# MacOS X is the *only* system that uses compose character in utf8. This
+# is so horribly broken....
+#
+ *darwin*)
+ AC_DEFINE(BROKEN_UNICODE_COMPOSE_CHARACTERS, 1, [Does this system use unicode compose characters])
+# Add Fink directories for various packages, like dlcompat.
+# Note: iconv does that explicitly below, but other packages
+# don't.
+ CPPFLAGS="$CPPFLAGS -I/sw/include"
+ LDFLAGS="$LDFLAGS -L/sw/lib"
+
+# If we have dlsym_prepend_underscore (from Fink's dlcompat),
+# use that instead of plain dlsym.
+
+ AC_CHECK_LIB(dl,dlopen)
+ AC_CHECK_FUNCS(dlsym_prepend_underscore,[CPPFLAGS="$CPPFLAGS -Ddlsym=dlsym_prepend_underscore"])
+
+#Add a system specific charset module.
+
+ default_shared_modules="$default_shared_modules charset_macosxfs"
+ old_CPPFLAGS="$CPPFLAGS"
+ CPPFLAGS="-Iinclude $CPPFLAGS"
+ AC_CHECK_HEADERS([CoreFoundation/CFStringEncodingConverter.h], [], [AC_CHECK_HEADERS([CFStringEncodingConverter.h])])
+ CPPFLAGS="$old_CPPFLAGS"
+ ;;
+ *hurd*)
+ AC_MSG_CHECKING([for LFS support])
+ old_CPPFLAGS="$CPPFLAGS"
+ CPPFLAGS="-D_LARGEFILE64_SOURCE -D_GNU_SOURCE $CPPFLAGS"
+ AC_TRY_RUN([
+#include <unistd.h>
+main () {
+#if _LFS64_LARGEFILE == 1
+exit(0);
+#else
+exit(1);
+#endif
+}], [GLIBC_LFS_SUPPORT=yes], [GLIBC_LFS_SUPPORT=no], [GLIBC_LFS_SUPPORT=cross])
+ CPPFLAGS="$old_CPPFLAGS"
+ if test x$GLIBC_LFS_SUPPORT = xyes ; then
+ CPPFLAGS="-D_LARGEFILE64_SOURCE -D_GNU_SOURCE $CPPFLAGS"
+ AC_DEFINE(_LARGEFILE64_SOURCE, 1, [Whether to enable large file support])
+ AC_DEFINE(_GNU_SOURCE, 1, [Whether to use GNU libc extensions])
+ fi
+ AC_MSG_RESULT([$GLIBC_LFS_SUPPORT])
+ ;;
+
+esac
+
+AC_INLINE
+AC_HEADER_STDC
+AC_HEADER_DIRENT
+AC_HEADER_TIME
+AC_HEADER_SYS_WAIT
+AC_CHECK_HEADERS(arpa/inet.h sys/fcntl.h sys/select.h fcntl.h sys/time.h sys/unistd.h)
+AC_CHECK_HEADERS(unistd.h utime.h grp.h sys/id.h limits.h memory.h net/if.h)
+AC_CHECK_HEADERS(rpc/rpc.h rpcsvc/nis.h rpcsvc/yp_prot.h rpcsvc/ypclnt.h)
+AC_CHECK_HEADERS(sys/param.h ctype.h sys/wait.h sys/resource.h sys/ioctl.h sys/ipc.h sys/mode.h)
+AC_CHECK_HEADERS(sys/mman.h sys/filio.h sys/priv.h sys/shm.h string.h strings.h stdlib.h sys/socket.h)
+AC_CHECK_HEADERS(sys/mount.h sys/vfs.h sys/fs/s5param.h sys/filsys.h termios.h termio.h)
+AC_CHECK_HEADERS(sys/termio.h sys/statfs.h sys/dustat.h sys/statvfs.h stdarg.h sys/sockio.h)
+AC_CHECK_HEADERS(security/pam_modules.h security/_pam_macros.h dlfcn.h)
+AC_CHECK_HEADERS(sys/syslog.h syslog.h execinfo.h)
+AC_CHECK_HEADERS(langinfo.h locale.h)
+
+# In valgrind 1.0.x, it's just valgrind.h. In 1.9.x+ there's a
+# subdirectory of headers.
+AC_CHECK_HEADERS(valgrind.h valgrind/valgrind.h valgrind/memcheck.h)
+
+#
+# HPUX has a bug in that including shadow.h causes a re-definition of MAXINT.
+# This causes configure to fail to detect it. Check for shadow separately on HPUX.
+#
+case "$host_os" in
+ *hpux*)
+ AC_TRY_COMPILE([#include <shadow.h>],[struct spwd testme],
+ ac_cv_header_shadow_h=yes,ac_cv_header_shadow_h=no)
+ if test x"$ac_cv_header_shadow_h" = x"yes"; then
+ AC_DEFINE(HAVE_SHADOW_H,1,[Whether we have shadow.h])
+ fi
+ ;;
+esac
+AC_CHECK_HEADERS(shadow.h netinet/ip.h netinet/tcp.h netinet/in_systm.h netinet/in_ip.h)
+AC_CHECK_HEADERS(nss.h nss_common.h nsswitch.h ns_api.h sys/security.h security/pam_appl.h security/pam_modules.h)
+AC_CHECK_HEADERS(stropts.h poll.h)
+AC_CHECK_HEADERS(sys/capability.h syscall.h sys/syscall.h)
+AC_CHECK_HEADERS(sys/acl.h sys/attributes.h attr/xattr.h sys/xattr.h sys/cdefs.h glob.h)
+
+# For experimental utmp support (lastlog on some BSD-like systems)
+AC_CHECK_HEADERS(utmp.h utmpx.h lastlog.h)
+
+AC_CHECK_SIZEOF(int,cross)
+AC_CHECK_SIZEOF(long,cross)
+AC_CHECK_SIZEOF(short,cross)
+
+AC_C_CONST
+AC_C_INLINE
+AC_C_BIGENDIAN
+AC_C_CHAR_UNSIGNED
+
+AC_TYPE_SIGNAL
+AC_TYPE_UID_T
+AC_TYPE_MODE_T
+AC_TYPE_OFF_T
+AC_TYPE_SIZE_T
+AC_TYPE_PID_T
+AC_STRUCT_ST_RDEV
+AC_DIRENT_D_OFF
+AC_CHECK_TYPE(ino_t,unsigned)
+AC_CHECK_TYPE(loff_t,off_t)
+AC_CHECK_TYPE(offset_t,loff_t)
+AC_CHECK_TYPE(ssize_t, int)
+AC_CHECK_TYPE(wchar_t, unsigned short)
+
+############################################
+# for cups support we need libcups, and a handful of header files
+
+AC_ARG_ENABLE(cups,
+[ --enable-cups Turn on CUPS support (default=auto)])
+
+if test x$enable_cups != xno; then
+ AC_PATH_PROG(CUPS_CONFIG, cups-config)
+
+ if test "x$CUPS_CONFIG" != x; then
+ AC_DEFINE(HAVE_CUPS,1,[Whether we have CUPS])
+ CFLAGS="$CFLAGS `$CUPS_CONFIG --cflags`"
+ LDFLAGS="$LDFLAGS `$CUPS_CONFIG --ldflags`"
+ PRINT_LIBS="$PRINT_LIBS `$CUPS_CONFIG --libs`"
+ fi
+fi
+
+############################################
+# we need dlopen/dlclose/dlsym/dlerror for PAM, the password database plugins and the plugin loading code
+AC_SEARCH_LIBS(dlopen, [dl])
+# dlopen/dlclose/dlsym/dlerror will be checked again later and defines will be set then
+
+############################################
+# check if the compiler can do immediate structures
+AC_CACHE_CHECK([for immediate structures],samba_cv_immediate_structures, [
+ AC_TRY_COMPILE([
+#include <stdio.h>],
+[
+ typedef struct {unsigned x;} FOOBAR;
+ #define X_FOOBAR(x) ((FOOBAR) { x })
+ #define FOO_ONE X_FOOBAR(1)
+ FOOBAR f = FOO_ONE;
+ static struct {
+ FOOBAR y;
+ } f2[] = {
+ {FOO_ONE}
+ };
+],
+ samba_cv_immediate_structures=yes,samba_cv_immediate_structures=no)])
+if test x"$samba_cv_immediate_structures" = x"yes"; then
+ AC_DEFINE(HAVE_IMMEDIATE_STRUCTURES,1,[Whether the compiler supports immediate structures])
+fi
+
+############################################
+# check if the compiler can do immediate structures
+AC_CACHE_CHECK([if the compiler will optimize out function calls],samba_cv_optimize_out_funcation_calls, [
+ AC_TRY_LINK([
+#include <stdio.h>],
+[
+ if (0) {
+ this_function_does_not_exist();
+ } else {
+ return 1;
+ }
+
+],
+ samba_cv_optimize_out_funcation_calls=yes,samba_cv_optimize_out_funcation_calls=no)])
+if test x"$samba_cv_optimize_out_funcation_calls" = x"yes"; then
+ AC_DEFINE(HAVE_COMPILER_WILL_OPTIMIZE_OUT_FNS,1,[Whether the compiler will optimize out function calls])
+fi
+
+############################################
+# check for unix domain sockets
+AC_CACHE_CHECK([for unix domain sockets],samba_cv_unixsocket, [
+ AC_TRY_COMPILE([
+#include <sys/types.h>
+#include <stdlib.h>
+#include <stddef.h>
+#include <sys/socket.h>
+#include <sys/un.h>],
+[
+ struct sockaddr_un sunaddr;
+ sunaddr.sun_family = AF_UNIX;
+],
+ samba_cv_unixsocket=yes,samba_cv_unixsocket=no)])
+if test x"$samba_cv_unixsocket" = x"yes"; then
+ AC_DEFINE(HAVE_UNIXSOCKET,1,[If we need to build with unixscoket support])
+fi
+
+
+AC_CACHE_CHECK([for socklen_t type],samba_cv_socklen_t, [
+ AC_TRY_COMPILE([
+#include <sys/types.h>
+#if STDC_HEADERS
+#include <stdlib.h>
+#include <stddef.h>
+#endif
+#include <sys/socket.h>],[socklen_t i = 0],
+ samba_cv_socklen_t=yes,samba_cv_socklen_t=no)])
+if test x"$samba_cv_socklen_t" = x"yes"; then
+ AC_DEFINE(HAVE_SOCKLEN_T_TYPE,1,[Whether we have the variable type socklen_t])
+fi
+
+AC_CACHE_CHECK([for sig_atomic_t type],samba_cv_sig_atomic_t, [
+ AC_TRY_COMPILE([
+#include <sys/types.h>
+#if STDC_HEADERS
+#include <stdlib.h>
+#include <stddef.h>
+#endif
+#include <signal.h>],[sig_atomic_t i = 0],
+ samba_cv_sig_atomic_t=yes,samba_cv_sig_atomic_t=no)])
+if test x"$samba_cv_sig_atomic_t" = x"yes"; then
+ AC_DEFINE(HAVE_SIG_ATOMIC_T_TYPE,1,[Whether we have the atomic_t variable type])
+fi
+
+# stupid headers have the functions but no declaration. grrrr.
+AC_HAVE_DECL(errno, [#include <errno.h>])
+AC_HAVE_DECL(setresuid, [#include <unistd.h>])
+AC_HAVE_DECL(setresgid, [#include <unistd.h>])
+AC_HAVE_DECL(asprintf, [#include <stdio.h>])
+AC_HAVE_DECL(vasprintf, [#include <stdio.h>])
+AC_HAVE_DECL(vsnprintf, [#include <stdio.h>])
+AC_HAVE_DECL(snprintf, [#include <stdio.h>])
+
+# and glibc has setresuid under linux but the function does
+# nothing until kernel 2.1.44! very dumb.
+AC_CACHE_CHECK([for real setresuid],samba_cv_have_setresuid,[
+ AC_TRY_RUN([#include <errno.h>
+main() { setresuid(1,1,1); setresuid(2,2,2); exit(errno==EPERM?0:1);}],
+ samba_cv_have_setresuid=yes,samba_cv_have_setresuid=no,samba_cv_have_setresuid=cross)])
+if test x"$samba_cv_have_setresuid" = x"yes"; then
+ AC_DEFINE(HAVE_SETRESUID,1,[Whether the system has setresuid])
+fi
+
+# Do the same check for setresguid...
+#
+AC_CACHE_CHECK([for real setresgid],samba_cv_have_setresgid,[
+ AC_TRY_RUN([#include <unistd.h>
+#include <errno.h>
+main() { errno = 0; setresgid(1,1,1); exit(errno != 0 ? (errno==EPERM ? 0 : 1) : 0);}],
+ samba_cv_have_setresgid=yes,samba_cv_have_setresgid=no,samba_cv_have_setresgid=cross)])
+if test x"$samba_cv_have_setresgid" = x"yes"; then
+ AC_DEFINE(HAVE_SETRESGID,1,[Whether the system has setresgid])
+fi
+
+AC_FUNC_MEMCMP
+
+###############################################
+# Readline included by default unless explicitly asked not to
+test "${with_readline+set}" != "set" && with_readline=yes
+
+# test for where we get readline() from
+AC_MSG_CHECKING(whether to use readline)
+AC_ARG_WITH(readline,
+[ --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"
+ AC_DEFINE(HAVE_LIBREADLINE,1,[Whether the system has readline])
+ 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"
+ CPPFLAGS="-I$with_readline/include $CPPFLAGS"
+ TERMLIBS="-lreadline $TERMLIBS"
+ AC_DEFINE(HAVE_LIBREADLINE,1,[Whether the system has readline])
+ break], [TERMLIBS= CPPFLAGS=$_cppflags], $TERMLIBS)])
+
+ LDFLAGS=$_ldflags
+ ;;
+ esac],
+ AC_MSG_RESULT(no)
+)
+AC_SUBST(TERMLIBS)
+AC_SUBST(TERMLDFLAGS)
+
+# 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])
+
+# The following test taken from the cvs sources
+# If we can't find connect, try looking in -lsocket, -lnsl, and -linet.
+# The Irix 5 libc.so has connect and gethostbyname, but Irix 5 also has
+# libsocket.so which has a bad implementation of gethostbyname (it
+# only looks in /etc/hosts), so we only look for -lsocket if we need
+# it.
+AC_CHECK_FUNCS(connect)
+if test x"$ac_cv_func_connect" = x"no"; then
+ case "$LIBS" in
+ *-lnsl*) ;;
+ *) AC_CHECK_LIB(nsl_s, printf) ;;
+ esac
+ case "$LIBS" in
+ *-lnsl*) ;;
+ *) AC_CHECK_LIB(nsl, printf) ;;
+ esac
+ case "$LIBS" in
+ *-lsocket*) ;;
+ *) AC_CHECK_LIB(socket, connect) ;;
+ esac
+ case "$LIBS" in
+ *-linet*) ;;
+ *) AC_CHECK_LIB(inet, connect) ;;
+ esac
+ dnl We can't just call AC_CHECK_FUNCS(connect) here, because the value
+ dnl has been cached.
+ if test x"$ac_cv_lib_socket_connect" = x"yes" ||
+ test x"$ac_cv_lib_inet_connect" = x"yes"; then
+ # ac_cv_func_connect=yes
+ # don't! it would cause AC_CHECK_FUNC to succeed next time configure is run
+ AC_DEFINE(HAVE_CONNECT,1,[Whether the system has connect()])
+ fi
+fi
+
+###############################################
+# test for where we get yp_get_default_domain() from
+AC_SEARCH_LIBS(yp_get_default_domain, [nsl])
+AC_CHECK_FUNCS(yp_get_default_domain)
+
+# Check if we have execl, if not we need to compile smbrun.
+AC_CHECK_FUNCS(execl)
+if test x"$ac_cv_func_execl" = x"no"; then
+ EXTRA_BIN_PROGS="$EXTRA_BIN_PROGS bin/smbrun\$(EXEEXT)"
+fi
+
+AC_CHECK_FUNCS(dlopen dlclose dlsym dlerror waitpid getcwd strdup strndup strnlen strtoul strerror chown fchown chmod fchmod chroot link mknod mknod64)
+AC_CHECK_FUNCS(fstat strchr utime utimes getrlimit fsync bzero memset strlcpy strlcat setpgid)
+AC_CHECK_FUNCS(memmove vsnprintf snprintf asprintf vasprintf setsid glob strpbrk pipe crypt16 getauthuid)
+AC_CHECK_FUNCS(strftime sigprocmask sigblock sigaction sigset innetgr setnetgrent getnetgrent endnetgrent)
+AC_CHECK_FUNCS(initgroups select poll rdchk getgrnam getgrent pathconf realpath)
+AC_CHECK_FUNCS(setpriv setgidx setuidx setgroups sysconf mktime rename ftruncate stat64 fstat64)
+AC_CHECK_FUNCS(lstat64 fopen64 atexit grantpt dup2 lseek64 ftruncate64 readdir64)
+AC_CHECK_FUNCS(fseek64 fseeko64 ftell64 ftello64 setluid getpwanam setlinebuf)
+AC_CHECK_FUNCS(srandom random srand rand setenv usleep strcasecmp fcvt fcvtl symlink readlink)
+AC_CHECK_FUNCS(syslog vsyslog timegm)
+AC_CHECK_FUNCS(setlocale nl_langinfo)
+# setbuffer, shmget, shm_open are needed for smbtorture
+AC_CHECK_FUNCS(setbuffer shmget shm_open backtrace_symbols)
+AC_CHECK_HEADERS(libexc.h)
+AC_CHECK_LIB(exc, trace_back_stack)
+
+# syscall() is needed for smbwrapper.
+AC_CHECK_FUNCS(syscall)
+
+AC_CHECK_FUNCS(_dup _dup2 _opendir _readdir _seekdir _telldir _closedir)
+AC_CHECK_FUNCS(__dup __dup2 __opendir __readdir __seekdir __telldir __closedir)
+AC_CHECK_FUNCS(__getcwd _getcwd)
+AC_CHECK_FUNCS(__xstat __fxstat __lxstat)
+AC_CHECK_FUNCS(_stat _lstat _fstat __stat __lstat __fstat)
+AC_CHECK_FUNCS(_acl __acl _facl __facl _open __open _chdir __chdir)
+AC_CHECK_FUNCS(_close __close _fchdir __fchdir _fcntl __fcntl)
+AC_CHECK_FUNCS(getdents _getdents __getdents _lseek __lseek _read __read)
+AC_CHECK_FUNCS(getdirentries _write __write _fork __fork)
+AC_CHECK_FUNCS(_stat64 __stat64 _fstat64 __fstat64 _lstat64 __lstat64)
+AC_CHECK_FUNCS(__sys_llseek llseek _llseek __llseek readdir64 _readdir64 __readdir64)
+AC_CHECK_FUNCS(pread _pread __pread pread64 _pread64 __pread64)
+AC_CHECK_FUNCS(pwrite _pwrite __pwrite pwrite64 _pwrite64 __pwrite64)
+AC_CHECK_FUNCS(open64 _open64 __open64 creat64)
+
+#
+#
+#
+case "$host_os" in
+ *linux*)
+ # glibc <= 2.3.2 has a broken getgrouplist
+ AC_TRY_RUN([
+#include <unistd.h>
+#include <sys/utsname.h>
+main() {
+ /* glibc up to 2.3 has a broken getgrouplist */
+#if defined(__GLIBC__) && defined(__GLIBC_MINOR__)
+ int libc_major = __GLIBC__;
+ int libc_minor = __GLIBC_MINOR__;
+
+ if (libc_major < 2)
+ exit(1);
+ if ((libc_major == 2) && (libc_minor <= 3))
+ exit(1);
+#endif
+ exit(0);
+}
+], [linux_getgrouplist_ok=yes], [linux_getgrouplist_ok=no])
+ if test x"$linux_getgrouplist_ok" = x"yes"; then
+ AC_DEFINE(HAVE_GETGROUPLIST, 1, [Have good getgrouplist])
+ fi
+ ;;
+ *)
+ AC_CHECK_FUNCS(getgrouplist)
+ ;;
+esac
+
+#
+# stat64 family may need <sys/stat.h> on some systems, notably ReliantUNIX
+#
+
+if test x$ac_cv_func_stat64 = xno ; then
+ AC_MSG_CHECKING([for stat64 in <sys/stat.h>])
+ AC_TRY_LINK([
+#if defined(HAVE_UNISTD_H)
+#include <unistd.h>
+#endif
+#include <sys/stat.h>
+], [struct stat64 st64; exit(stat64(".",&st64));], [ac_cv_func_stat64=yes])
+ AC_MSG_RESULT([$ac_cv_func_stat64])
+ if test x$ac_cv_func_stat64 = xyes ; then
+ AC_DEFINE(HAVE_STAT64,1,[Whether stat64() is available])
+ fi
+fi
+
+if test x$ac_cv_func_lstat64 = xno ; then
+ AC_MSG_CHECKING([for lstat64 in <sys/stat.h>])
+ AC_TRY_LINK([
+#if defined(HAVE_UNISTD_H)
+#include <unistd.h>
+#endif
+#include <sys/stat.h>
+], [struct stat64 st64; exit(lstat64(".",&st64));], [ac_cv_func_lstat64=yes])
+ AC_MSG_RESULT([$ac_cv_func_lstat64])
+ if test x$ac_cv_func_lstat64 = xyes ; then
+ AC_DEFINE(HAVE_LSTAT64,[Whether lstat64() is available])
+ fi
+fi
+
+if test x$ac_cv_func_fstat64 = xno ; then
+ AC_MSG_CHECKING([for fstat64 in <sys/stat.h>])
+ AC_TRY_LINK([
+#if defined(HAVE_UNISTD_H)
+#include <unistd.h>
+#endif
+#include <sys/stat.h>
+], [struct stat64 st64; exit(fstat64(0,&st64));], [ac_cv_func_fstat64=yes])
+ AC_MSG_RESULT([$ac_cv_func_fstat64])
+ if test x$ac_cv_func_fstat64 = xyes ; then
+ AC_DEFINE(HAVE_FSTAT64,1,[Whether fstat64() is available])
+ fi
+fi
+
+#####################################
+# we might need the resolv library on some systems
+AC_CHECK_LIB(resolv, dn_expand)
+
+#
+# Check for the functions putprpwnam, set_auth_parameters,
+# getspnam, bigcrypt and getprpwnam in -lsec and -lsecurity
+# Needed for OSF1 and HPUX.
+#
+
+AC_LIBTESTFUNC(security, putprpwnam)
+AC_LIBTESTFUNC(sec, putprpwnam)
+
+AC_LIBTESTFUNC(security, set_auth_parameters)
+AC_LIBTESTFUNC(sec, set_auth_parameters)
+
+# UnixWare 7.x has its getspnam in -lgen
+AC_LIBTESTFUNC(gen, getspnam)
+
+AC_LIBTESTFUNC(security, getspnam)
+AC_LIBTESTFUNC(sec, getspnam)
+
+AC_LIBTESTFUNC(security, bigcrypt)
+AC_LIBTESTFUNC(sec, bigcrypt)
+
+AC_LIBTESTFUNC(security, getprpwnam)
+AC_LIBTESTFUNC(sec, getprpwnam)
+
+############################################
+# Check if we have libattr
+AC_SEARCH_LIBS(getxattr, [attr])
+AC_CHECK_FUNCS(getxattr lgetxattr fgetxattr listxattr llistxattr)
+AC_CHECK_FUNCS(flistxattr removexattr lremovexattr fremovexattr)
+AC_CHECK_FUNCS(setxattr lsetxattr fsetxattr)
+AC_CHECK_FUNCS(attr_get attr_list attr_set attr_remove)
+AC_CHECK_FUNCS(attr_getf attr_listf attr_setf attr_removef)
+
+# Assume non-shared by default and override below
+BLDSHARED="false"
+
+# these are the defaults, good for lots of systems
+HOST_OS="$host_os"
+LDSHFLAGS="-shared"
+SONAMEFLAG="#"
+SHLD="\${CC} \${CFLAGS}"
+PICFLAGS=""
+PICSUFFIX="po"
+SHLIBEXT="so"
+
+if test "$enable_shared" = "yes"; then
+ # this bit needs to be modified for each OS that is suported by
+ # smbwrapper. You need to specify how to created a shared library and
+ # how to compile C code to produce PIC object files
+
+ AC_MSG_CHECKING([ability to build shared libraries])
+
+ # and these are for particular systems
+ case "$host_os" in
+ *linux*) AC_DEFINE(LINUX,1,[Whether the host os is linux])
+ BLDSHARED="true"
+ LDSHFLAGS="-shared"
+ DYNEXP="-Wl,--export-dynamic"
+ PICFLAGS="-fPIC"
+ SONAMEFLAG="-Wl,-soname="
+ AC_DEFINE(STAT_ST_BLOCKSIZE,512)
+ ;;
+ *solaris*) AC_DEFINE(SUNOS5,1,[Whether the host os is solaris])
+ BLDSHARED="true"
+ LDSHFLAGS="-G"
+ SONAMEFLAG="-h "
+ if test "${GCC}" = "yes"; then
+ PICFLAGS="-fPIC"
+ if test "${ac_cv_prog_gnu_ld}" = "yes"; then
+ DYNEXP="-Wl,-E"
+ fi
+ else
+ PICFLAGS="-KPIC"
+ ## ${CFLAGS} added for building 64-bit shared
+ ## libs using Sun's Compiler
+ LDSHFLAGS="-G \${CFLAGS}"
+ PICSUFFIX="po.o"
+ fi
+ AC_DEFINE(STAT_ST_BLOCKSIZE,512,[The size of a block])
+ AC_DEFINE(BROKEN_GETGRNAM,1,[Does getgrnam work correctly])
+ ;;
+ *sunos*) AC_DEFINE(SUNOS4,1,[Whether the host os is sunos4])
+ BLDSHARED="true"
+ LDSHFLAGS="-G"
+ SONAMEFLAG="-Wl,-h,"
+ PICFLAGS="-KPIC" # Is this correct for SunOS
+ AC_DEFINE(STAT_ST_BLOCKSIZE,512)
+ AC_DEFINE(BROKEN_GETGRNAM,1,[Does getgrnam work correctly])
+ ;;
+ *netbsd* | *freebsd*) BLDSHARED="true"
+ LDSHFLAGS="-shared"
+ DYNEXP="-Wl,--export-dynamic"
+ SONAMEFLAG="-Wl,-soname,"
+ PICFLAGS="-fPIC -DPIC"
+ AC_DEFINE(STAT_ST_BLOCKSIZE,512,[The size of a block])
+ AC_DEFINE(BROKEN_GETGRNAM,1,[Does getgrnam work correctly])
+ ;;
+ *openbsd*) BLDSHARED="true"
+ LDSHFLAGS="-shared"
+ DYNEXP="-Wl,-Bdynamic"
+ SONAMEFLAG="-Wl,-soname,"
+ PICFLAGS="-fPIC"
+ AC_DEFINE(STAT_ST_BLOCKSIZE,512,[The size of a block])
+ AC_DEFINE(BROKEN_GETGRNAM,1,[Does getgrnam work correctly])
+ ;;
+ *irix*) AC_DEFINE(IRIX,1,[Whether the host os is irix])
+ case "$host_os" in
+ *irix6*) AC_DEFINE(IRIX6,1,[Whether the host os is irix6])
+ ;;
+ esac
+ BLDSHARED="true"
+ LDSHFLAGS="-set_version sgi1.0 -shared"
+ SONAMEFLAG="-soname "
+ SHLD="\${LD}"
+ if test "${GCC}" = "yes"; then
+ PICFLAGS="-fPIC"
+ else
+ PICFLAGS="-KPIC"
+ fi
+ AC_DEFINE(STAT_ST_BLOCKSIZE,512,[The size of a block])
+ ;;
+ *aix*) AC_DEFINE(AIX,1,[Whether the host os is aix])
+ BLDSHARED="true"
+ LDSHFLAGS="-Wl,-bexpall,-bM:SRE,-bnoentry,-berok"
+ DYNEXP="-Wl,-brtl,-bexpall"
+ PICFLAGS="-O2"
+ if test "${GCC}" != "yes"; then
+ ## for funky AIX compiler using strncpy()
+ CFLAGS="$CFLAGS -D_LINUX_SOURCE_COMPAT -qmaxmem=32000"
+ fi
+
+ AC_DEFINE(STAT_ST_BLOCKSIZE,DEV_BSIZE,[The size of a block])
+ ;;
+ *hpux*) AC_DEFINE(HPUX,1,[Whether the host os is HPUX])
+ SHLIBEXT="sl"
+ # Use special PIC flags for the native HP-UX compiler.
+ if test $ac_cv_prog_cc_Ae = yes; then
+ BLDSHARED="true"
+ SHLD="/usr/bin/ld"
+ LDSHFLAGS="-B symbolic -b -z"
+ SONAMEFLAG="+h "
+ PICFLAGS="+z"
+ elif test "${GCC}" = "yes"; then
+ PICFLAGS="-fPIC"
+ fi
+ DYNEXP="-Wl,-E"
+ AC_DEFINE(STAT_ST_BLOCKSIZE,8192,[The size of a block])
+ AC_DEFINE(POSIX_ACL_NEEDS_MASK,1,[Does a POSIX ACL need a mask element])
+ ;;
+ *qnx*) AC_DEFINE(QNX,1,[Whether the host os is qnx])
+ AC_DEFINE(STAT_ST_BLOCKSIZE,512)
+ ;;
+ *osf*) AC_DEFINE(OSF1,1,[Whether the host os is osf1])
+ BLDSHARED="true"
+ LDSHFLAGS="-shared"
+ SONAMEFLAG="-Wl,-soname,"
+ PICFLAGS="-fPIC"
+ AC_DEFINE(STAT_ST_BLOCKSIZE,512)
+ AC_DEFINE(BROKEN_GETGRNAM,1,[Does getgrnam work correctly])
+ ;;
+ *sco*) AC_DEFINE(SCO,1,[Whether the host os is sco unix])
+ AC_DEFINE(STAT_ST_BLOCKSIZE,512)
+ ;;
+ *unixware*) AC_DEFINE(UNIXWARE,1,[Whether the host os is unixware])
+ BLDSHARED="true"
+ LDSHFLAGS="-shared"
+ SONAMEFLAG="-Wl,-soname,"
+ PICFLAGS="-KPIC"
+ AC_DEFINE(STAT_ST_BLOCKSIZE,512)
+ ;;
+ *next2*) AC_DEFINE(NEXT2,1,[Whether the host os is NeXT v2])
+ AC_DEFINE(STAT_ST_BLOCKSIZE,512)
+ ;;
+ *dgux*) AC_CHECK_PROG( ROFF, groff, [groff -etpsR -Tascii -man])
+ AC_DEFINE(STAT_ST_BLOCKSIZE,512)
+ ;;
+ *sysv4*) AC_DEFINE(SYSV,1,[Whether this is a system V system])
+ case "$host" in
+ *-univel-*) if [ test "$GCC" != yes ]; then
+ AC_DEFINE(HAVE_MEMSET,1,[Whether memset() is available])
+ fi
+ LDSHFLAGS="-G"
+ DYNEXP="-Bexport"
+ ;;
+ *mips-sni-sysv4*) AC_DEFINE(RELIANTUNIX,1,[Whether the host os is reliantunix]);;
+ esac
+ AC_DEFINE(STAT_ST_BLOCKSIZE,512)
+ ;;
+
+ *sysv5*) AC_DEFINE(SYSV,1,[Whether this is a system V system])
+ if [ test "$GCC" != yes ]; then
+ AC_DEFINE(HAVE_MEMSET,1,[Whether memset() is available])
+ fi
+ LDSHFLAGS="-G"
+ AC_DEFINE(STAT_ST_BLOCKSIZE,512)
+ ;;
+ *vos*) AC_DEFINE(STAT_ST_BLOCKSIZE,4096)
+ BLDSHARED="false"
+ LDSHFLAGS=""
+ ;;
+
+ *darwin*) AC_DEFINE(DARWINOS,1,[Whether the host os is Darwin/MacOSX])
+ BLDSHARED="true"
+ LDSHFLAGS="-bundle -flat_namespace -undefined suppress"
+ SHLIBEXT="dylib"
+ AC_DEFINE(STAT_ST_BLOCKSIZE,512)
+ ;;
+
+ *)
+ AC_DEFINE(STAT_ST_BLOCKSIZE,512)
+ ;;
+ esac
+ AC_SUBST(DYNEXP)
+ AC_MSG_RESULT($BLDSHARED)
+ AC_MSG_CHECKING([linker flags for shared libraries])
+ AC_MSG_RESULT([$LDSHFLAGS])
+ AC_MSG_CHECKING([compiler flags for position-independent code])
+ AC_MSG_RESULT([$PICFLAGS])
+fi
+
+#######################################################
+# test whether building a shared library actually works
+if test $BLDSHARED = true; then
+AC_CACHE_CHECK([whether building shared libraries actually works],
+ [ac_cv_shlib_works],[
+ ac_cv_shlib_works=no
+ # try building a trivial shared library
+ $CC $CPPFLAGS $CFLAGS $PICFLAGS -c -o \
+ shlib.$PICSUFFIX ${srcdir-.}/tests/shlib.c && \
+ $CC $CPPFLAGS $CFLAGS `eval echo $LDSHFLAGS` -o "shlib.$SHLIBEXT" \
+ shlib.$PICSUFFIX && \
+ ac_cv_shlib_works=yes
+ rm -f "shlib.$SHLIBEXT" shlib.$PICSUFFIX
+])
+if test $ac_cv_shlib_works = no; then
+ BLDSHARED=false
+fi
+fi
+
+################
+
+AC_CACHE_CHECK([for long long],samba_cv_have_longlong,[
+AC_TRY_RUN([#include <stdio.h>
+main() { long long x = 1000000; x *= x; exit(((x/1000000) == 1000000)? 0: 1); }],
+samba_cv_have_longlong=yes,samba_cv_have_longlong=no,samba_cv_have_longlong=cross)])
+if test x"$samba_cv_have_longlong" = x"yes"; then
+ AC_DEFINE(HAVE_LONGLONG,1,[Whether the host supports long long's])
+fi
+
+#
+# Check if the compiler supports the LL prefix on long long integers.
+# AIX needs this.
+
+AC_CACHE_CHECK([for LL suffix on long long integers],samba_cv_compiler_supports_ll, [
+ AC_TRY_COMPILE([#include <stdio.h>],[long long i = 0x8000000000LL],
+ samba_cv_compiler_supports_ll=yes,samba_cv_compiler_supports_ll=no)])
+if test x"$samba_cv_compiler_supports_ll" = x"yes"; then
+ AC_DEFINE(COMPILER_SUPPORTS_LL,1,[Whether the compiler supports the LL prefix on long long integers])
+fi
+
+
+AC_CACHE_CHECK([for 64 bit off_t],samba_cv_SIZEOF_OFF_T,[
+AC_TRY_RUN([#include <stdio.h>
+#include <sys/stat.h>
+main() { exit((sizeof(off_t) == 8) ? 0 : 1); }],
+samba_cv_SIZEOF_OFF_T=yes,samba_cv_SIZEOF_OFF_T=no,samba_cv_SIZEOF_OFF_T=cross)])
+if test x"$samba_cv_SIZEOF_OFF_T" = x"yes"; then
+ AC_DEFINE(SIZEOF_OFF_T,8,[The size of the 'off_t' type])
+fi
+
+AC_CACHE_CHECK([for off64_t],samba_cv_HAVE_OFF64_T,[
+AC_TRY_RUN([
+#if defined(HAVE_UNISTD_H)
+#include <unistd.h>
+#endif
+#include <stdio.h>
+#include <sys/stat.h>
+main() { struct stat64 st; off64_t s; if (sizeof(off_t) == sizeof(off64_t)) exit(1); exit((lstat64("/dev/null", &st)==0)?0:1); }],
+samba_cv_HAVE_OFF64_T=yes,samba_cv_HAVE_OFF64_T=no,samba_cv_HAVE_OFF64_T=cross)])
+if test x"$samba_cv_HAVE_OFF64_T" = x"yes"; then
+ AC_DEFINE(HAVE_OFF64_T,1,[Whether off64_t is available])
+fi
+
+AC_CACHE_CHECK([for 64 bit ino_t],samba_cv_SIZEOF_INO_T,[
+AC_TRY_RUN([#include <stdio.h>
+#include <sys/stat.h>
+main() { exit((sizeof(ino_t) == 8) ? 0 : 1); }],
+samba_cv_SIZEOF_INO_T=yes,samba_cv_SIZEOF_INO_T=no,samba_cv_SIZEOF_INO_T=cross)])
+if test x"$samba_cv_SIZEOF_INO_T" = x"yes"; then
+ AC_DEFINE(SIZEOF_INO_T,8,[The size of the 'ino_t' type])
+fi
+
+AC_CACHE_CHECK([for ino64_t],samba_cv_HAVE_INO64_T,[
+AC_TRY_RUN([
+#if defined(HAVE_UNISTD_H)
+#include <unistd.h>
+#endif
+#include <stdio.h>
+#include <sys/stat.h>
+main() { struct stat64 st; ino64_t s; if (sizeof(ino_t) == sizeof(ino64_t)) exit(1); exit((lstat64("/dev/null", &st)==0)?0:1); }],
+samba_cv_HAVE_INO64_T=yes,samba_cv_HAVE_INO64_T=no,samba_cv_HAVE_INO64_T=cross)])
+if test x"$samba_cv_HAVE_INO64_T" = x"yes"; then
+ AC_DEFINE(HAVE_INO64_T,1,[Whether the 'ino64_t' type is available])
+fi
+
+AC_CACHE_CHECK([for dev64_t],samba_cv_HAVE_DEV64_T,[
+AC_TRY_RUN([
+#if defined(HAVE_UNISTD_H)
+#include <unistd.h>
+#endif
+#include <stdio.h>
+#include <sys/stat.h>
+main() { struct stat64 st; dev64_t s; if (sizeof(dev_t) == sizeof(dev64_t)) exit(1); exit((lstat64("/dev/null", &st)==0)?0:1); }],
+samba_cv_HAVE_DEV64_T=yes,samba_cv_HAVE_DEV64_T=no,samba_cv_HAVE_DEV64_T=cross)])
+if test x"$samba_cv_HAVE_DEV64_T" = x"yes"; then
+ AC_DEFINE(HAVE_DEV64_T,1,[Whether the 'dev64_t' type is available])
+fi
+
+AC_CACHE_CHECK([for struct dirent64],samba_cv_HAVE_STRUCT_DIRENT64,[
+AC_TRY_COMPILE([
+#if defined(HAVE_UNISTD_H)
+#include <unistd.h>
+#endif
+#include <sys/types.h>
+#include <dirent.h>],
+[struct dirent64 de;],
+samba_cv_HAVE_STRUCT_DIRENT64=yes,samba_cv_HAVE_STRUCT_DIRENT64=no)])
+if test x"$samba_cv_HAVE_STRUCT_DIRENT64" = x"yes" && test x"$ac_cv_func_readdir64" = x"yes"; then
+ AC_DEFINE(HAVE_STRUCT_DIRENT64,1,[Whether the 'dirent64' struct is available])
+fi
+
+AC_CACHE_CHECK([for major macro],samba_cv_HAVE_DEVICE_MAJOR_FN,[
+AC_TRY_RUN([
+#if defined(HAVE_UNISTD_H)
+#include <unistd.h>
+#endif
+#include <sys/types.h>
+main() { dev_t dev; int i = major(dev); return 0; }],
+samba_cv_HAVE_DEVICE_MAJOR_FN=yes,samba_cv_HAVE_DEVICE_MAJOR_FN=no,samba_cv_HAVE_DEVICE_MAJOR_FN=cross)])
+if test x"$samba_cv_HAVE_DEVICE_MAJOR_FN" = x"yes"; then
+ AC_DEFINE(HAVE_DEVICE_MAJOR_FN,1,[Whether the major macro for dev_t is available])
+fi
+
+AC_CACHE_CHECK([for minor macro],samba_cv_HAVE_DEVICE_MINOR_FN,[
+AC_TRY_RUN([
+#if defined(HAVE_UNISTD_H)
+#include <unistd.h>
+#endif
+#include <sys/types.h>
+main() { dev_t dev; int i = minor(dev); return 0; }],
+samba_cv_HAVE_DEVICE_MINOR_FN=yes,samba_cv_HAVE_DEVICE_MINOR_FN=no,samba_cv_HAVE_DEVICE_MINOR_FN=cross)])
+if test x"$samba_cv_HAVE_DEVICE_MINOR_FN" = x"yes"; then
+ AC_DEFINE(HAVE_DEVICE_MINOR_FN,1,[Whether the minor macro for dev_t is available])
+fi
+
+AC_CACHE_CHECK([for unsigned char],samba_cv_HAVE_UNSIGNED_CHAR,[
+AC_TRY_RUN([#include <stdio.h>
+main() { char c; c=250; exit((c > 0)?0:1); }],
+samba_cv_HAVE_UNSIGNED_CHAR=yes,samba_cv_HAVE_UNSIGNED_CHAR=no,samba_cv_HAVE_UNSIGNED_CHAR=cross)])
+if test x"$samba_cv_HAVE_UNSIGNED_CHAR" = x"yes"; then
+ AC_DEFINE(HAVE_UNSIGNED_CHAR,1,[Whether the 'unsigned char' type is available])
+fi
+
+AC_CACHE_CHECK([for sin_len in sock],samba_cv_HAVE_SOCK_SIN_LEN,[
+AC_TRY_COMPILE([#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>],
+[struct sockaddr_in sock; sock.sin_len = sizeof(sock);],
+samba_cv_HAVE_SOCK_SIN_LEN=yes,samba_cv_HAVE_SOCK_SIN_LEN=no)])
+if test x"$samba_cv_HAVE_SOCK_SIN_LEN" = x"yes"; then
+ AC_DEFINE(HAVE_SOCK_SIN_LEN,1,[Whether the sockaddr_in struct has a sin_len property])
+fi
+
+AC_CACHE_CHECK([whether seekdir returns void],samba_cv_SEEKDIR_RETURNS_VOID,[
+AC_TRY_COMPILE([#include <sys/types.h>
+#include <dirent.h>
+void seekdir(DIR *d, long loc) { return; }],[return 0;],
+samba_cv_SEEKDIR_RETURNS_VOID=yes,samba_cv_SEEKDIR_RETURNS_VOID=no)])
+if test x"$samba_cv_SEEKDIR_RETURNS_VOID" = x"yes"; then
+ AC_DEFINE(SEEKDIR_RETURNS_VOID,1,[Whether seekdir returns void])
+fi
+
+AC_CACHE_CHECK([for __FUNCTION__ macro],samba_cv_HAVE_FUNCTION_MACRO,[
+AC_TRY_COMPILE([#include <stdio.h>], [printf("%s\n", __FUNCTION__);],
+samba_cv_HAVE_FUNCTION_MACRO=yes,samba_cv_HAVE_FUNCTION_MACRO=no)])
+if test x"$samba_cv_HAVE_FUNCTION_MACRO" = x"yes"; then
+ AC_DEFINE(HAVE_FUNCTION_MACRO,1,[Whether there is a __FUNCTION__ macro])
+fi
+
+AC_CACHE_CHECK([if gettimeofday takes tz argument],samba_cv_HAVE_GETTIMEOFDAY_TZ,[
+AC_TRY_RUN([
+#include <sys/time.h>
+#include <unistd.h>
+main() { struct timeval tv; exit(gettimeofday(&tv, NULL));}],
+ samba_cv_HAVE_GETTIMEOFDAY_TZ=yes,samba_cv_HAVE_GETTIMEOFDAY_TZ=no,samba_cv_HAVE_GETTIMEOFDAY_TZ=cross)])
+if test x"$samba_cv_HAVE_GETTIMEOFDAY_TZ" = x"yes"; then
+ AC_DEFINE(HAVE_GETTIMEOFDAY_TZ,1,[Whether gettimeofday() is available])
+fi
+
+AC_CACHE_CHECK([for va_copy],samba_cv_HAVE_VA_COPY,[
+AC_TRY_LINK([#include <stdarg.h>
+va_list ap1,ap2;], [va_copy(ap1,ap2);],
+samba_cv_HAVE_VA_COPY=yes,
+samba_cv_HAVE_VA_COPY=no)])
+if test x"$samba_cv_HAVE_VA_COPY" = x"yes"; then
+ AC_DEFINE(HAVE_VA_COPY,1,[Whether va_copy() is available])
+else
+ AC_CACHE_CHECK([for __va_copy],samba_cv_HAVE___VA_COPY,[
+ AC_TRY_LINK([#include <stdarg.h>
+ va_list ap1,ap2;], [__va_copy(ap1,ap2);],
+ samba_cv_HAVE___VA_COPY=yes,
+ samba_cv_HAVE___VA_COPY=no)])
+ if test x"$samba_cv_HAVE___VA_COPY" = x"yes"; then
+ AC_DEFINE(HAVE___VA_COPY,1,[Whether __va_copy() is available])
+ fi
+fi
+
+AC_CACHE_CHECK([for C99 vsnprintf],samba_cv_HAVE_C99_VSNPRINTF,[
+AC_TRY_RUN([
+#include <sys/types.h>
+#include <stdarg.h>
+void foo(const char *format, ...) {
+ va_list ap;
+ int len;
+ char buf[5];
+
+ va_start(ap, format);
+ len = vsnprintf(buf, 0, format, ap);
+ va_end(ap);
+ if (len != 5) exit(1);
+
+ va_start(ap, format);
+ len = vsnprintf(0, 0, format, ap);
+ va_end(ap);
+ if (len != 5) exit(1);
+
+ if (snprintf(buf, 3, "hello") != 5 || strcmp(buf, "he") != 0) exit(1);
+
+ exit(0);
+}
+main() { foo("hello"); }
+],
+samba_cv_HAVE_C99_VSNPRINTF=yes,samba_cv_HAVE_C99_VSNPRINTF=no,samba_cv_HAVE_C99_VSNPRINTF=cross)])
+if test x"$samba_cv_HAVE_C99_VSNPRINTF" = x"yes"; then
+ AC_DEFINE(HAVE_C99_VSNPRINTF,1,[Whether there is a C99 compliant vsnprintf])
+fi
+
+AC_CACHE_CHECK([for broken readdir],samba_cv_HAVE_BROKEN_READDIR,[
+AC_TRY_RUN([#include <sys/types.h>
+#include <dirent.h>
+main() { struct dirent *di; DIR *d = opendir("."); di = readdir(d);
+if (di && di->d_name[-2] == '.' && di->d_name[-1] == 0 &&
+di->d_name[0] == 0) exit(0); exit(1);} ],
+samba_cv_HAVE_BROKEN_READDIR=yes,samba_cv_HAVE_BROKEN_READDIR=no,samba_cv_HAVE_BROKEN_READDIR=cross)])
+if test x"$samba_cv_HAVE_BROKEN_READDIR" = x"yes"; then
+ AC_DEFINE(HAVE_BROKEN_READDIR,1,[Whether readdir() is broken])
+fi
+
+AC_CACHE_CHECK([for utimbuf],samba_cv_HAVE_UTIMBUF,[
+AC_TRY_COMPILE([#include <sys/types.h>
+#include <utime.h>],
+[struct utimbuf tbuf; tbuf.actime = 0; tbuf.modtime = 1; exit(utime("foo.c",&tbuf));],
+samba_cv_HAVE_UTIMBUF=yes,samba_cv_HAVE_UTIMBUF=no,samba_cv_HAVE_UTIMBUF=cross)])
+if test x"$samba_cv_HAVE_UTIMBUF" = x"yes"; then
+ AC_DEFINE(HAVE_UTIMBUF,1,[Whether struct utimbuf is available])
+fi
+
+##############
+# Check utmp details, but only if our OS offers utmp.h
+if test x"$ac_cv_header_utmp_h" = x"yes"; then
+dnl utmp and utmpx come in many flavours
+dnl We need to check for many of them
+dnl But we don't need to do each and every one, because our code uses
+dnl mostly just the utmp (not utmpx) fields.
+
+AC_CHECK_FUNCS(pututline pututxline updwtmp updwtmpx getutmpx)
+
+AC_CACHE_CHECK([for ut_name in utmp],samba_cv_HAVE_UT_UT_NAME,[
+AC_TRY_COMPILE([#include <sys/types.h>
+#include <utmp.h>],
+[struct utmp ut; ut.ut_name[0] = 'a';],
+samba_cv_HAVE_UT_UT_NAME=yes,samba_cv_HAVE_UT_UT_NAME=no,samba_cv_HAVE_UT_UT_NAME=cross)])
+if test x"$samba_cv_HAVE_UT_UT_NAME" = x"yes"; then
+ AC_DEFINE(HAVE_UT_UT_NAME,1,[Whether the utmp struct has a property ut_name])
+fi
+
+AC_CACHE_CHECK([for ut_user in utmp],samba_cv_HAVE_UT_UT_USER,[
+AC_TRY_COMPILE([#include <sys/types.h>
+#include <utmp.h>],
+[struct utmp ut; ut.ut_user[0] = 'a';],
+samba_cv_HAVE_UT_UT_USER=yes,samba_cv_HAVE_UT_UT_USER=no,samba_cv_HAVE_UT_UT_USER=cross)])
+if test x"$samba_cv_HAVE_UT_UT_USER" = x"yes"; then
+ AC_DEFINE(HAVE_UT_UT_USER,1,[Whether the utmp struct has a property ut_user])
+fi
+
+AC_CACHE_CHECK([for ut_id in utmp],samba_cv_HAVE_UT_UT_ID,[
+AC_TRY_COMPILE([#include <sys/types.h>
+#include <utmp.h>],
+[struct utmp ut; ut.ut_id[0] = 'a';],
+samba_cv_HAVE_UT_UT_ID=yes,samba_cv_HAVE_UT_UT_ID=no,samba_cv_HAVE_UT_UT_ID=cross)])
+if test x"$samba_cv_HAVE_UT_UT_ID" = x"yes"; then
+ AC_DEFINE(HAVE_UT_UT_ID,1,[Whether the utmp struct has a property ut_id])
+fi
+
+AC_CACHE_CHECK([for ut_host in utmp],samba_cv_HAVE_UT_UT_HOST,[
+AC_TRY_COMPILE([#include <sys/types.h>
+#include <utmp.h>],
+[struct utmp ut; ut.ut_host[0] = 'a';],
+samba_cv_HAVE_UT_UT_HOST=yes,samba_cv_HAVE_UT_UT_HOST=no,samba_cv_HAVE_UT_UT_HOST=cross)])
+if test x"$samba_cv_HAVE_UT_UT_HOST" = x"yes"; then
+ AC_DEFINE(HAVE_UT_UT_HOST,1,[Whether the utmp struct has a property ut_host])
+fi
+
+AC_CACHE_CHECK([for ut_time in utmp],samba_cv_HAVE_UT_UT_TIME,[
+AC_TRY_COMPILE([#include <sys/types.h>
+#include <utmp.h>],
+[struct utmp ut; time_t t; ut.ut_time = t;],
+samba_cv_HAVE_UT_UT_TIME=yes,samba_cv_HAVE_UT_UT_TIME=no,samba_cv_HAVE_UT_UT_TIME=cross)])
+if test x"$samba_cv_HAVE_UT_UT_TIME" = x"yes"; then
+ AC_DEFINE(HAVE_UT_UT_TIME,1,[Whether the utmp struct has a property ut_time])
+fi
+
+AC_CACHE_CHECK([for ut_tv in utmp],samba_cv_HAVE_UT_UT_TV,[
+AC_TRY_COMPILE([#include <sys/types.h>
+#include <utmp.h>],
+[struct utmp ut; struct timeval tv; ut.ut_tv = tv;],
+samba_cv_HAVE_UT_UT_TV=yes,samba_cv_HAVE_UT_UT_TV=no,samba_cv_HAVE_UT_UT_TV=cross)])
+if test x"$samba_cv_HAVE_UT_UT_TV" = x"yes"; then
+ AC_DEFINE(HAVE_UT_UT_TV,1,[Whether the utmp struct has a property ut_tv])
+fi
+
+AC_CACHE_CHECK([for ut_type in utmp],samba_cv_HAVE_UT_UT_TYPE,[
+AC_TRY_COMPILE([#include <sys/types.h>
+#include <utmp.h>],
+[struct utmp ut; ut.ut_type = 0;],
+samba_cv_HAVE_UT_UT_TYPE=yes,samba_cv_HAVE_UT_UT_TYPE=no,samba_cv_HAVE_UT_UT_TYPE=cross)])
+if test x"$samba_cv_HAVE_UT_UT_TYPE" = x"yes"; then
+ AC_DEFINE(HAVE_UT_UT_TYPE,1,[Whether the utmp struct has a property ut_type])
+fi
+
+AC_CACHE_CHECK([for ut_pid in utmp],samba_cv_HAVE_UT_UT_PID,[
+AC_TRY_COMPILE([#include <sys/types.h>
+#include <utmp.h>],
+[struct utmp ut; ut.ut_pid = 0;],
+samba_cv_HAVE_UT_UT_PID=yes,samba_cv_HAVE_UT_UT_PID=no,samba_cv_HAVE_UT_UT_PID=cross)])
+if test x"$samba_cv_HAVE_UT_UT_PID" = x"yes"; then
+ AC_DEFINE(HAVE_UT_UT_PID,1,[Whether the utmp struct has a property ut_pid])
+fi
+
+AC_CACHE_CHECK([for ut_exit in utmp],samba_cv_HAVE_UT_UT_EXIT,[
+AC_TRY_COMPILE([#include <sys/types.h>
+#include <utmp.h>],
+[struct utmp ut; ut.ut_exit.e_exit = 0;],
+samba_cv_HAVE_UT_UT_EXIT=yes,samba_cv_HAVE_UT_UT_EXIT=no,samba_cv_HAVE_UT_UT_EXIT=cross)])
+if test x"$samba_cv_HAVE_UT_UT_EXIT" = x"yes"; then
+ AC_DEFINE(HAVE_UT_UT_EXIT,1,[Whether the utmp struct has a property ut_exit])
+fi
+
+AC_CACHE_CHECK([for ut_addr in utmp],samba_cv_HAVE_UT_UT_ADDR,[
+AC_TRY_COMPILE([#include <sys/types.h>
+#include <utmp.h>],
+[struct utmp ut; ut.ut_addr = 0;],
+samba_cv_HAVE_UT_UT_ADDR=yes,samba_cv_HAVE_UT_UT_ADDR=no,samba_cv_HAVE_UT_UT_ADDR=cross)])
+if test x"$samba_cv_HAVE_UT_UT_ADDR" = x"yes"; then
+ AC_DEFINE(HAVE_UT_UT_ADDR,1,[Whether the utmp struct has a property ut_addr])
+fi
+
+if test x$ac_cv_func_pututline = xyes ; then
+ AC_CACHE_CHECK([whether pututline returns pointer],samba_cv_PUTUTLINE_RETURNS_UTMP,[
+ AC_TRY_COMPILE([#include <sys/types.h>
+#include <utmp.h>],
+ [struct utmp utarg; struct utmp *utreturn; utreturn = pututline(&utarg);],
+ samba_cv_PUTUTLINE_RETURNS_UTMP=yes,samba_cv_PUTUTLINE_RETURNS_UTMP=no)])
+ if test x"$samba_cv_PUTUTLINE_RETURNS_UTMP" = x"yes"; then
+ AC_DEFINE(PUTUTLINE_RETURNS_UTMP,1,[Whether pututline returns pointer])
+ fi
+fi
+
+AC_CACHE_CHECK([for ut_syslen in utmpx],samba_cv_HAVE_UX_UT_SYSLEN,[
+AC_TRY_COMPILE([#include <sys/types.h>
+#include <utmpx.h>],
+[struct utmpx ux; ux.ut_syslen = 0;],
+samba_cv_HAVE_UX_UT_SYSLEN=yes,samba_cv_HAVE_UX_UT_SYSLEN=no,samba_cv_HAVE_UX_UT_SYSLEN=cross)])
+if test x"$samba_cv_HAVE_UX_UT_SYSLEN" = x"yes"; then
+ AC_DEFINE(HAVE_UX_UT_SYSLEN,1,[Whether the utmpx struct has a property ut_syslen])
+fi
+
+fi
+# end utmp details
+
+
+ICONV_LOCATION=standard
+LOOK_DIRS="/usr /usr/local /sw"
+AC_ARG_WITH(libiconv,
+[ --with-libiconv=BASEDIR Use libiconv in BASEDIR/lib and BASEDIR/include (default=auto) ],
+[
+ if test "$withval" = "no" ; then
+ AC_MSG_ERROR([argument to --with-libiconv must be a directory])
+ else
+ if test "$withval" != "yes" ; then
+ ICONV_PATH_SPEC=yes
+ LOOK_DIRS="$withval"
+ fi
+ fi
+])
+
+ICONV_FOUND="no"
+for i in $LOOK_DIRS ; do
+ save_LIBS=$LIBS
+ save_LDFLAGS=$LDFLAGS
+ save_CPPFLAGS=$CPPFLAGS
+ CPPFLAGS="$CPPFLAGS -I$i/include"
+dnl This is here to handle -withval stuff for --with-libiconv
+dnl Perhaps we should always add a -L
+ LDFLAGS="$LDFLAGS -L$i/lib"
+ LIBS=
+ export LDFLAGS LIBS CPPFLAGS
+dnl Try to find iconv(3)
+ jm_ICONV($i)
+
+ if test "$ICONV_FOUND" = yes; then
+ LDFLAGS=$save_LDFLAGS
+ LIB_ADD_DIR(LDFLAGS, "$i/lib")
+ CFLAGS_ADD_DIR(CPPFLAGS, "$i/include")
+ LIBS="$save_LIBS"
+ ICONV_LOCATION=$i
+ export LDFLAGS LIBS CPPFLAGS
+dnl Now, check for a working iconv ... we want to do it here because
+dnl there might be a working iconv further down the list of LOOK_DIRS
+
+ ############
+ # check for iconv in libc
+ ic_save_LIBS="$LIBS"
+ if test x"$ICONV_PATH_SPEC" = "xyes" ; then
+ LIBS="$LIBS -L$ICONV_LOCATION/lib"
+ fi
+ if test x"$jm_cv_lib_iconv" != x; then
+ LIBS="$LIBS -l$jm_cv_lib_iconv"
+ fi
+dnl AC_CACHE_CHECK([for working iconv],samba_cv_HAVE_NATIVE_ICONV,[
+ default_dos_charset=no
+ default_display_charset=no
+ default_unix_charset=no
+
+ # check for default dos charset name
+ for j in CP850 IBM850 ; do
+ rjs_CHARSET($j)
+ if test x"$ICONV_CHARSET" = x"$j"; then
+ default_dos_charset="\"$j\""
+ break
+ fi
+ done
+ # check for default display charset name
+ for j in ASCII 646 ; do
+ rjs_CHARSET($j)
+ if test x"$ICONV_CHARSET" = x"$j"; then
+ default_display_charset="\"$j\""
+ break
+ fi
+ done
+ # check for default unix charset name
+ for j in UTF-8 UTF8 ; do
+ rjs_CHARSET($j)
+ if test x"$ICONV_CHARSET" = x"$j"; then
+ default_unix_charset="\"$j\""
+ break
+ fi
+ done
+
+ if test "$default_dos_charset" != "no" -a \
+ "$default_dos_charset" != "cross" -a \
+ "$default_display_charset" != "no" -a \
+ "$default_display_charset" != "cross" -a \
+ "$default_unix_charset" != "no" -a \
+ "$default_unix_charset" != "cross"
+ then
+ samba_cv_HAVE_NATIVE_ICONV=yes
+ else if test "$default_dos_charset" = "cross" -o \
+ "$default_display_charset" = "cross" -o \
+ "$default_unix_charset" = "cross"
+ then
+ samba_cv_HAVE_NATIVE_ICONV=cross
+ else
+ samba_cv_HAVE_NATIVE_ICONV=no
+ fi
+ fi
+dnl ])
+
+ LIBS="$ic_save_LIBS"
+ if test x"$samba_cv_HAVE_NATIVE_ICONV" = x"yes"; then
+ CPPFLAGS=$save_CPPFLAGS
+ LDFLAGS=$save_LDFLAGS
+ LIBS=$save_LIBS
+ if test x"$jm_cv_lib_iconv" != x; then
+ LIBS="$LIBS -l$jm_cv_lib_iconv"
+ fi
+ dnl Add the flags we need to CPPFLAGS and LDFLAGS
+ CFLAGS_ADD_DIR(CPPFLAGS, "$i/include")
+ LIB_ADD_DIR(LDFLAGS, "$i/lib")
+ export CPPFLAGS
+ AC_DEFINE(HAVE_NATIVE_ICONV,1,[Whether to use native iconv])
+ AC_DEFINE_UNQUOTED(DEFAULT_DOS_CHARSET,$default_dos_charset,[Default dos charset name])
+ AC_DEFINE_UNQUOTED(DEFAULT_DISPLAY_CHARSET,$default_display_charset,[Default display charset name])
+ AC_DEFINE_UNQUOTED(DEFAULT_UNIX_CHARSET,$default_unix_charset,[Default unix charset name])
+ break
+ fi
+dnl We didn't find a working iconv, so keep going
+ fi
+dnl We only need to clean these up here for the next pass through the loop
+ CPPFLAGS=$save_CPPFLAGS
+ LDFLAGS=$save_LDFLAGS
+ LIBS=$save_LIBS
+ export LDFLAGS LIBS CPPFLAGS
+done
+
+
+if test x"$ICONV_FOUND" = x"no" -o x"$samba_cv_HAVE_NATIVE_ICONV" != x"yes" ; then
+ AC_MSG_WARN([Sufficient support for iconv function was not found.
+ Install libiconv from http://freshmeat.net/projects/libiconv/ for better charset compatibility!])
+ AC_DEFINE_UNQUOTED(DEFAULT_DOS_CHARSET,"ASCII",[Default dos charset name])
+ AC_DEFINE_UNQUOTED(DEFAULT_DISPLAY_CHARSET,"ASCII",[Default display charset name])
+ AC_DEFINE_UNQUOTED(DEFAULT_UNIX_CHARSET,"UTF8",[Default unix charset name])
+fi
+
+
+AC_CACHE_CHECK([for Linux kernel oplocks],samba_cv_HAVE_KERNEL_OPLOCKS_LINUX,[
+AC_TRY_RUN([
+#include <sys/types.h>
+#include <fcntl.h>
+#ifndef F_GETLEASE
+#define F_GETLEASE 1025
+#endif
+main() {
+ int fd = open("/dev/null", O_RDONLY);
+ return fcntl(fd, F_GETLEASE, 0) == -1;
+}
+],
+samba_cv_HAVE_KERNEL_OPLOCKS_LINUX=yes,samba_cv_HAVE_KERNEL_OPLOCKS_LINUX=no,samba_cv_HAVE_KERNEL_OPLOCKS_LINUX=cross)])
+if test x"$samba_cv_HAVE_KERNEL_OPLOCKS_LINUX" = x"yes"; then
+ AC_DEFINE(HAVE_KERNEL_OPLOCKS_LINUX,1,[Whether to use linux kernel oplocks])
+fi
+
+AC_CACHE_CHECK([for kernel change notify support],samba_cv_HAVE_KERNEL_CHANGE_NOTIFY,[
+AC_TRY_RUN([
+#include <sys/types.h>
+#include <fcntl.h>
+#include <signal.h>
+#ifndef F_NOTIFY
+#define F_NOTIFY 1026
+#endif
+main() {
+ exit(fcntl(open("/tmp", O_RDONLY), F_NOTIFY, 0) == -1 ? 1 : 0);
+}
+],
+samba_cv_HAVE_KERNEL_CHANGE_NOTIFY=yes,samba_cv_HAVE_KERNEL_CHANGE_NOTIFY=no,samba_cv_HAVE_KERNEL_CHANGE_NOTIFY=cross)])
+if test x"$samba_cv_HAVE_KERNEL_CHANGE_NOTIFY" = x"yes"; then
+ AC_DEFINE(HAVE_KERNEL_CHANGE_NOTIFY,1,[Whether kernel notifies changes])
+fi
+
+AC_CACHE_CHECK([for kernel share modes],samba_cv_HAVE_KERNEL_SHARE_MODES,[
+AC_TRY_RUN([
+#include <sys/types.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <sys/file.h>
+#ifndef LOCK_MAND
+#define LOCK_MAND 32
+#define LOCK_READ 64
+#endif
+main() {
+ exit(flock(open("/dev/null", O_RDWR), LOCK_MAND|LOCK_READ) != 0);
+}
+],
+samba_cv_HAVE_KERNEL_SHARE_MODES=yes,samba_cv_HAVE_KERNEL_SHARE_MODES=no,samba_cv_HAVE_KERNEL_SHARE_MODES=cross)])
+if test x"$samba_cv_HAVE_KERNEL_SHARE_MODES" = x"yes"; then
+ AC_DEFINE(HAVE_KERNEL_SHARE_MODES,1,[Whether the kernel supports share modes])
+fi
+
+
+
+
+AC_CACHE_CHECK([for IRIX kernel oplock type definitions],samba_cv_HAVE_KERNEL_OPLOCKS_IRIX,[
+AC_TRY_COMPILE([#include <sys/types.h>
+#include <fcntl.h>],
+[oplock_stat_t t; t.os_state = OP_REVOKE; t.os_dev = 1; t.os_ino = 1;],
+samba_cv_HAVE_KERNEL_OPLOCKS_IRIX=yes,samba_cv_HAVE_KERNEL_OPLOCKS_IRIX=no)])
+if test x"$samba_cv_HAVE_KERNEL_OPLOCKS_IRIX" = x"yes"; then
+ AC_DEFINE(HAVE_KERNEL_OPLOCKS_IRIX,1,[Whether IRIX kernel oplock type definitions are available])
+fi
+
+AC_CACHE_CHECK([for irix specific capabilities],samba_cv_HAVE_IRIX_SPECIFIC_CAPABILITIES,[
+AC_TRY_RUN([#include <sys/types.h>
+#include <sys/capability.h>
+main() {
+ cap_t cap;
+ if ((cap = cap_get_proc()) == NULL)
+ exit(1);
+ cap->cap_effective |= CAP_NETWORK_MGT;
+ cap->cap_inheritable |= CAP_NETWORK_MGT;
+ cap_set_proc(cap);
+ exit(0);
+}
+],
+samba_cv_HAVE_IRIX_SPECIFIC_CAPABILITIES=yes,samba_cv_HAVE_IRIX_SPECIFIC_CAPABILITIES=no,samba_cv_HAVE_IRIX_SPECIFIC_CAPABILITIES=cross)])
+if test x"$samba_cv_HAVE_IRIX_SPECIFIC_CAPABILITIES" = x"yes"; then
+ AC_DEFINE(HAVE_IRIX_SPECIFIC_CAPABILITIES,1,[Whether IRIX specific capabilities are available])
+fi
+
+#
+# Check for int16, uint16, int32 and uint32 in rpc/types.h included from rpc/rpc.h
+# This is *really* broken but some systems (DEC OSF1) do this.... JRA.
+#
+
+AC_CACHE_CHECK([for int16 typedef included by rpc/rpc.h],samba_cv_HAVE_INT16_FROM_RPC_RPC_H,[
+AC_TRY_COMPILE([#include <sys/types.h>
+#if defined(HAVE_RPC_RPC_H)
+#include <rpc/rpc.h>
+#endif],
+[int16 testvar;],
+samba_cv_HAVE_INT16_FROM_RPC_RPC_H=yes,samba_cv_HAVE_INT16_FROM_RPC_RPC_H=no)])
+if test x"$samba_cv_HAVE_INT16_FROM_RPC_RPC_H" = x"yes"; then
+ AC_DEFINE(HAVE_INT16_FROM_RPC_RPC_H,1,[Whether int16 typedef is included by rpc/rpc.h])
+fi
+
+AC_CACHE_CHECK([for uint16 typedef included by rpc/rpc.h],samba_cv_HAVE_UINT16_FROM_RPC_RPC_H,[
+AC_TRY_COMPILE([#include <sys/types.h>
+#if defined(HAVE_RPC_RPC_H)
+#include <rpc/rpc.h>
+#endif],
+[uint16 testvar;],
+samba_cv_HAVE_UINT16_FROM_RPC_RPC_H=yes,samba_cv_HAVE_UINT16_FROM_RPC_RPC_H=no)])
+if test x"$samba_cv_HAVE_UINT16_FROM_RPC_RPC_H" = x"yes"; then
+ AC_DEFINE(HAVE_UINT16_FROM_RPC_RPC_H,1,[Whether uint16 typedef is included by rpc/rpc.h])
+fi
+
+AC_CACHE_CHECK([for int32 typedef included by rpc/rpc.h],samba_cv_HAVE_INT32_FROM_RPC_RPC_H,[
+AC_TRY_COMPILE([#include <sys/types.h>
+#if defined(HAVE_RPC_RPC_H)
+#include <rpc/rpc.h>
+#endif],
+[int32 testvar;],
+samba_cv_HAVE_INT32_FROM_RPC_RPC_H=yes,samba_cv_HAVE_INT32_FROM_RPC_RPC_H=no)])
+if test x"$samba_cv_HAVE_INT32_FROM_RPC_RPC_H" = x"yes"; then
+ AC_DEFINE(HAVE_INT32_FROM_RPC_RPC_H,1,[Whether int32 typedef is included by rpc/rpc.h])
+fi
+
+AC_CACHE_CHECK([for uint32 typedef included by rpc/rpc.h],samba_cv_HAVE_UINT32_FROM_RPC_RPC_H,[
+AC_TRY_COMPILE([#include <sys/types.h>
+#if defined(HAVE_RPC_RPC_H)
+#include <rpc/rpc.h>
+#endif],
+[uint32 testvar;],
+samba_cv_HAVE_UINT32_FROM_RPC_RPC_H=yes,samba_cv_HAVE_UINT32_FROM_RPC_RPC_H=no)])
+if test x"$samba_cv_HAVE_UINT32_FROM_RPC_RPC_H" = x"yes"; then
+ AC_DEFINE(HAVE_UINT32_FROM_RPC_RPC_H,1,[Whether uint32 typedef is included by rpc/rpc.h])
+fi
+
+dnl
+dnl Some systems (SCO) have a problem including
+dnl <prot.h> and <rpc/rpc.h> due to AUTH_ERROR being defined
+dnl as a #define in <prot.h> and as part of an enum
+dnl in <rpc/rpc.h>.
+dnl
+
+AC_CACHE_CHECK([for conflicting AUTH_ERROR define in rpc/rpc.h],samba_cv_HAVE_RPC_AUTH_ERROR_CONFLICT,[
+AC_TRY_COMPILE([#include <sys/types.h>
+#ifdef HAVE_SYS_SECURITY_H
+#include <sys/security.h>
+#include <prot.h>
+#endif /* HAVE_SYS_SECURITY_H */
+#if defined(HAVE_RPC_RPC_H)
+#include <rpc/rpc.h>
+#endif],
+[int testvar;],
+samba_cv_HAVE_RPC_AUTH_ERROR_CONFLICT=no,samba_cv_HAVE_RPC_AUTH_ERROR_CONFLICT=yes)])
+if test x"$samba_cv_HAVE_RPC_AUTH_ERROR_CONFLICT" = x"yes"; then
+ AC_DEFINE(HAVE_RPC_AUTH_ERROR_CONFLICT,1,[Whether there is a conflicting AUTH_ERROR define in rpc/rpc.h])
+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]))
+
+AC_CACHE_CHECK([for ftruncate extend],samba_cv_HAVE_FTRUNCATE_EXTEND,[
+AC_TRY_RUN([#include "${srcdir-.}/tests/ftruncate.c"],
+ samba_cv_HAVE_FTRUNCATE_EXTEND=yes,samba_cv_HAVE_FTRUNCATE_EXTEND=no,samba_cv_HAVE_FTRUNCATE_EXTEND=cross)])
+if test x"$samba_cv_HAVE_FTRUNCATE_EXTEND" = x"yes"; then
+ AC_DEFINE(HAVE_FTRUNCATE_EXTEND,1,[Truncate extend])
+fi
+
+AC_CACHE_CHECK([for AF_LOCAL socket support], samba_cv_HAVE_WORKING_AF_LOCAL, [
+AC_TRY_RUN([#include "${srcdir-.}/tests/unixsock.c"],
+ samba_cv_HAVE_WORKING_AF_LOCAL=yes,
+ samba_cv_HAVE_WORKING_AF_LOCAL=no,
+ samba_cv_HAVE_WORKING_AF_LOCAL=cross)])
+if test x"$samba_cv_HAVE_WORKING_AF_LOCAL" != xno
+then
+ AC_DEFINE(HAVE_WORKING_AF_LOCAL, 1, [Define if you have working AF_LOCAL sockets])
+fi
+
+AC_CACHE_CHECK([for broken getgroups],samba_cv_HAVE_BROKEN_GETGROUPS,[
+AC_TRY_RUN([#include "${srcdir-.}/tests/getgroups.c"],
+ samba_cv_HAVE_BROKEN_GETGROUPS=yes,samba_cv_HAVE_BROKEN_GETGROUPS=no,samba_cv_HAVE_BROKEN_GETGROUPS=cross)])
+if test x"$samba_cv_HAVE_BROKEN_GETGROUPS" = x"yes"; then
+ AC_DEFINE(HAVE_BROKEN_GETGROUPS,1,[Whether getgroups is broken])
+fi
+
+AC_CACHE_CHECK([whether getpass should be replaced],samba_cv_REPLACE_GETPASS,[
+SAVE_CPPFLAGS="$CPPFLAGS"
+CPPFLAGS="$CPPFLAGS -I${srcdir-.}/ -I${srcdir-.}/include -I${srcdir-.}/ubiqx -I${srcdir-.}/popt -I${srcdir-.}/smbwrapper"
+AC_TRY_COMPILE([
+#define REPLACE_GETPASS 1
+#define NO_PROTO_H 1
+#define NO_CONFIG_H 1
+#define main dont_declare_main
+#include "${srcdir-.}/lib/getsmbpass.c"
+#undef main
+],[],samba_cv_REPLACE_GETPASS=yes,samba_cv_REPLACE_GETPASS=no)
+CPPFLAGS="$SAVE_CPPFLAGS"
+])
+if test x"$samba_cv_REPLACE_GETPASS" = x"yes"; then
+ AC_DEFINE(REPLACE_GETPASS,1,[Whether getpass should be replaced])
+fi
+
+AC_CACHE_CHECK([for broken inet_ntoa],samba_cv_REPLACE_INET_NTOA,[
+AC_TRY_RUN([
+#include <stdio.h>
+#include <sys/types.h>
+#include <netinet/in.h>
+#ifdef HAVE_ARPA_INET_H
+#include <arpa/inet.h>
+#endif
+main() { struct in_addr ip; ip.s_addr = 0x12345678;
+if (strcmp(inet_ntoa(ip),"18.52.86.120") &&
+ strcmp(inet_ntoa(ip),"120.86.52.18")) { exit(0); }
+exit(1);}],
+ samba_cv_REPLACE_INET_NTOA=yes,samba_cv_REPLACE_INET_NTOA=no,samba_cv_REPLACE_INET_NTOA=cross)])
+if test x"$samba_cv_REPLACE_INET_NTOA" = x"yes"; then
+ AC_DEFINE(REPLACE_INET_NTOA,1,[Whether inet_ntoa should be replaced])
+fi
+
+AC_CACHE_CHECK([for secure mkstemp],samba_cv_HAVE_SECURE_MKSTEMP,[
+AC_TRY_RUN([#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+main() {
+ struct stat st;
+ char tpl[20]="/tmp/test.XXXXXX";
+ int fd = mkstemp(tpl);
+ if (fd == -1) exit(1);
+ unlink(tpl);
+ if (fstat(fd, &st) != 0) exit(1);
+ if ((st.st_mode & 0777) != 0600) exit(1);
+ exit(0);
+}],
+samba_cv_HAVE_SECURE_MKSTEMP=yes,
+samba_cv_HAVE_SECURE_MKSTEMP=no,
+samba_cv_HAVE_SECURE_MKSTEMP=cross)])
+if test x"$samba_cv_HAVE_SECURE_MKSTEMP" = x"yes"; then
+ AC_DEFINE(HAVE_SECURE_MKSTEMP,1,[Whether mkstemp is secure])
+fi
+
+AC_CACHE_CHECK([for sysconf(_SC_NGROUPS_MAX)],samba_cv_SYSCONF_SC_NGROUPS_MAX,[
+AC_TRY_RUN([#include <unistd.h>
+main() { exit(sysconf(_SC_NGROUPS_MAX) == -1 ? 1 : 0); }],
+samba_cv_SYSCONF_SC_NGROUPS_MAX=yes,samba_cv_SYSCONF_SC_NGROUPS_MAX=no,samba_cv_SYSCONF_SC_NGROUPS_MAX=cross)])
+if test x"$samba_cv_SYSCONF_SC_NGROUPS_MAX" = x"yes"; then
+ AC_DEFINE(SYSCONF_SC_NGROUPS_MAX,1,[Whether sysconf(_SC_NGROUPS_MAX) is available])
+fi
+
+AC_CACHE_CHECK([for sysconf(_SC_NPROC_ONLN)],samba_cv_SYSCONF_SC_NPROC_ONLN,[
+AC_TRY_RUN([#include <unistd.h>
+main() { exit(sysconf(_SC_NPROC_ONLN) == -1 ? 1 : 0); }],
+samba_cv_SYSCONF_SC_NPROC_ONLN=yes,samba_cv_SYSCONF_SC_NPROC_ONLN=no,samba_cv_SYSCONF_SC_NPROC_ONLN=cross)])
+if test x"$samba_cv_SYSCONF_SC_NPROC_ONLN" = x"yes"; then
+ AC_DEFINE(SYSCONF_SC_NPROC_ONLN,1,[Whether sysconf(_SC_NPROC_ONLN) is available])
+fi
+
+AC_CACHE_CHECK([for root],samba_cv_HAVE_ROOT,[
+AC_TRY_RUN([main() { exit(getuid() != 0); }],
+ samba_cv_HAVE_ROOT=yes,samba_cv_HAVE_ROOT=no,samba_cv_HAVE_ROOT=cross)])
+if test x"$samba_cv_HAVE_ROOT" = x"yes"; then
+ AC_DEFINE(HAVE_ROOT,1,[Whether current user is root])
+else
+ AC_MSG_WARN(running as non-root will disable some tests)
+fi
+
+##################
+# look for a method of finding the list of network interfaces
+iface=no;
+AC_CACHE_CHECK([for iface AIX],samba_cv_HAVE_IFACE_AIX,[
+AC_TRY_RUN([
+#define HAVE_IFACE_AIX 1
+#define AUTOCONF_TEST 1
+#include "confdefs.h"
+#include "${srcdir-.}/lib/interfaces.c"],
+ samba_cv_HAVE_IFACE_AIX=yes,samba_cv_HAVE_IFACE_AIX=no,samba_cv_HAVE_IFACE_AIX=cross)])
+if test x"$samba_cv_HAVE_IFACE_AIX" = x"yes"; then
+ iface=yes;AC_DEFINE(HAVE_IFACE_AIX,1,[Whether iface AIX is available])
+fi
+
+if test $iface = no; then
+AC_CACHE_CHECK([for iface ifconf],samba_cv_HAVE_IFACE_IFCONF,[
+AC_TRY_RUN([
+#define HAVE_IFACE_IFCONF 1
+#define AUTOCONF_TEST 1
+#include "confdefs.h"
+#include "${srcdir-.}/lib/interfaces.c"],
+ samba_cv_HAVE_IFACE_IFCONF=yes,samba_cv_HAVE_IFACE_IFCONF=no,samba_cv_HAVE_IFACE_IFCONF=cross)])
+if test x"$samba_cv_HAVE_IFACE_IFCONF" = x"yes"; then
+ iface=yes;AC_DEFINE(HAVE_IFACE_IFCONF,1,[Whether iface ifconf is available])
+fi
+fi
+
+if test $iface = no; then
+AC_CACHE_CHECK([for iface ifreq],samba_cv_HAVE_IFACE_IFREQ,[
+AC_TRY_RUN([
+#define HAVE_IFACE_IFREQ 1
+#define AUTOCONF_TEST 1
+#include "confdefs.h"
+#include "${srcdir-.}/lib/interfaces.c"],
+ samba_cv_HAVE_IFACE_IFREQ=yes,samba_cv_HAVE_IFACE_IFREQ=no,samba_cv_HAVE_IFACE_IFREQ=cross)])
+if test x"$samba_cv_HAVE_IFACE_IFREQ" = x"yes"; then
+ iface=yes;AC_DEFINE(HAVE_IFACE_IFREQ,1,[Whether iface ifreq is available])
+fi
+fi
+
+
+################################################
+# look for a method of setting the effective uid
+seteuid=no;
+if test $seteuid = no; then
+AC_CACHE_CHECK([for setresuid],samba_cv_USE_SETRESUID,[
+AC_TRY_RUN([
+#define AUTOCONF_TEST 1
+#define USE_SETRESUID 1
+#include "confdefs.h"
+#include "${srcdir-.}/lib/util_sec.c"],
+ samba_cv_USE_SETRESUID=yes,samba_cv_USE_SETRESUID=no,samba_cv_USE_SETRESUID=cross)])
+if test x"$samba_cv_USE_SETRESUID" = x"yes"; then
+ seteuid=yes;AC_DEFINE(USE_SETRESUID,1,[Whether setresuid() is available])
+fi
+fi
+
+
+if test $seteuid = no; then
+AC_CACHE_CHECK([for setreuid],samba_cv_USE_SETREUID,[
+AC_TRY_RUN([
+#define AUTOCONF_TEST 1
+#define USE_SETREUID 1
+#include "confdefs.h"
+#include "${srcdir-.}/lib/util_sec.c"],
+ samba_cv_USE_SETREUID=yes,samba_cv_USE_SETREUID=no,samba_cv_USE_SETREUID=cross)])
+if test x"$samba_cv_USE_SETREUID" = x"yes"; then
+ seteuid=yes;AC_DEFINE(USE_SETREUID,1,[Whether setreuid() is available])
+fi
+fi
+
+if test $seteuid = no; then
+AC_CACHE_CHECK([for seteuid],samba_cv_USE_SETEUID,[
+AC_TRY_RUN([
+#define AUTOCONF_TEST 1
+#define USE_SETEUID 1
+#include "confdefs.h"
+#include "${srcdir-.}/lib/util_sec.c"],
+ samba_cv_USE_SETEUID=yes,samba_cv_USE_SETEUID=no,samba_cv_USE_SETEUID=cross)])
+if test x"$samba_cv_USE_SETEUID" = x"yes"; then
+ seteuid=yes;AC_DEFINE(USE_SETEUID,1,[Whether seteuid() is available])
+fi
+fi
+
+if test $seteuid = no; then
+AC_CACHE_CHECK([for setuidx],samba_cv_USE_SETUIDX,[
+AC_TRY_RUN([
+#define AUTOCONF_TEST 1
+#define USE_SETUIDX 1
+#include "confdefs.h"
+#include "${srcdir-.}/lib/util_sec.c"],
+ samba_cv_USE_SETUIDX=yes,samba_cv_USE_SETUIDX=no,samba_cv_USE_SETUIDX=cross)])
+if test x"$samba_cv_USE_SETUIDX" = x"yes"; then
+ seteuid=yes;AC_DEFINE(USE_SETUIDX,1,[Whether setuidx() is available])
+fi
+fi
+
+
+AC_CACHE_CHECK([for working mmap],samba_cv_HAVE_MMAP,[
+AC_TRY_RUN([#include "${srcdir-.}/tests/shared_mmap.c"],
+ samba_cv_HAVE_MMAP=yes,samba_cv_HAVE_MMAP=no,samba_cv_HAVE_MMAP=cross)])
+if test x"$samba_cv_HAVE_MMAP" = x"yes"; then
+ AC_DEFINE(HAVE_MMAP,1,[Whether mmap works])
+fi
+
+AC_CACHE_CHECK([for ftruncate needs root],samba_cv_FTRUNCATE_NEEDS_ROOT,[
+AC_TRY_RUN([#include "${srcdir-.}/tests/ftruncroot.c"],
+ samba_cv_FTRUNCATE_NEEDS_ROOT=yes,samba_cv_FTRUNCATE_NEEDS_ROOT=no,samba_cv_FTRUNCATE_NEEDS_ROOT=cross)])
+if test x"$samba_cv_FTRUNCATE_NEEDS_ROOT" = x"yes"; then
+ AC_DEFINE(FTRUNCATE_NEEDS_ROOT,1,[Whether ftruncate() needs root])
+fi
+
+AC_CACHE_CHECK([for fcntl locking],samba_cv_HAVE_FCNTL_LOCK,[
+AC_TRY_RUN([#include "${srcdir-.}/tests/fcntl_lock.c"],
+ samba_cv_HAVE_FCNTL_LOCK=yes,samba_cv_HAVE_FCNTL_LOCK=no,samba_cv_HAVE_FCNTL_LOCK=cross)])
+if test x"$samba_cv_HAVE_FCNTL_LOCK" = x"yes"; then
+ AC_DEFINE(HAVE_FCNTL_LOCK,1,[Whether fcntl locking is available])
+fi
+
+AC_CACHE_CHECK([for broken (glibc2.1/x86) 64 bit fcntl locking],samba_cv_HAVE_BROKEN_FCNTL64_LOCKS,[
+AC_TRY_RUN([#include "${srcdir-.}/tests/fcntl_lock64.c"],
+ samba_cv_HAVE_BROKEN_FCNTL64_LOCKS=yes,samba_cv_HAVE_BROKEN_FCNTL64_LOCKS=no,samba_cv_HAVE_BROKEN_FCNTL64_LOCKS=cross)])
+if test x"$samba_cv_HAVE_BROKEN_FCNTL64_LOCKS" = x"yes"; then
+ AC_DEFINE(HAVE_BROKEN_FCNTL64_LOCKS,1,[Whether fcntl64 locks are broken])
+
+else
+
+dnl
+dnl Don't check for 64 bit fcntl locking if we know that the
+dnl glibc2.1 broken check has succeeded.
+dnl
+
+ AC_CACHE_CHECK([for 64 bit fcntl locking],samba_cv_HAVE_STRUCT_FLOCK64,[
+ AC_TRY_RUN([
+#if defined(HAVE_UNISTD_H)
+#include <unistd.h>
+#endif
+#include <stdio.h>
+#include <stdlib.h>
+
+#ifdef HAVE_FCNTL_H
+#include <fcntl.h>
+#endif
+
+#ifdef HAVE_SYS_FCNTL_H
+#include <sys/fcntl.h>
+#endif
+main() { struct flock64 fl64;
+#if defined(F_SETLKW64) && defined(F_SETLK64) && defined(F_GETLK64)
+exit(0);
+#else
+exit(1);
+#endif
+}],
+ samba_cv_HAVE_STRUCT_FLOCK64=yes,samba_cv_HAVE_STRUCT_FLOCK64=no,samba_cv_HAVE_STRUCT_FLOCK64=cross)])
+
+ if test x"$samba_cv_HAVE_STRUCT_FLOCK64" = x"yes"; then
+ AC_DEFINE(HAVE_STRUCT_FLOCK64,1,[Whether the flock64 struct is available])
+ fi
+fi
+
+AC_CACHE_CHECK([for st_blocks in struct stat],samba_cv_HAVE_STAT_ST_BLOCKS,[
+AC_TRY_COMPILE([#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>],
+[struct stat st; st.st_blocks = 0;],
+samba_cv_HAVE_STAT_ST_BLOCKS=yes,samba_cv_HAVE_STAT_ST_BLOCKS=no,samba_cv_HAVE_STAT_ST_BLOCKS=cross)])
+if test x"$samba_cv_HAVE_STAT_ST_BLOCKS" = x"yes"; then
+ AC_DEFINE(HAVE_STAT_ST_BLOCKS,1,[Whether the stat struct has a st_block property])
+fi
+
+AC_CACHE_CHECK([for st_blksize in struct stat],samba_cv_HAVE_STAT_ST_BLKSIZE,[
+AC_TRY_COMPILE([#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>],
+[struct stat st; st.st_blksize = 0;],
+samba_cv_HAVE_STAT_ST_BLKSIZE=yes,samba_cv_HAVE_STAT_ST_BLKSIZE=no,samba_cv_HAVE_STAT_ST_BLKSIZE=cross)])
+if test x"$samba_cv_HAVE_STAT_ST_BLKSIZE" = x"yes"; then
+ AC_DEFINE(HAVE_STAT_ST_BLKSIZE,1,[Whether the stat struct has a st_blksize property])
+fi
+
+case "$host_os" in
+*linux*)
+AC_CACHE_CHECK([for broken RedHat 7.2 system header files],samba_cv_BROKEN_REDHAT_7_SYSTEM_HEADERS,[
+AC_TRY_COMPILE([
+#ifdef HAVE_SYS_VFS_H
+#include <sys/vfs.h>
+#endif
+#ifdef HAVE_SYS_CAPABILITY_H
+#include <sys/capability.h>
+#endif
+],[int i;],
+ samba_cv_BROKEN_REDHAT_7_SYSTEM_HEADERS=no,samba_cv_BROKEN_REDHAT_7_SYSTEM_HEADERS=yes)])
+if test x"$samba_cv_BROKEN_REDHAT_7_SYSTEM_HEADERS" = x"yes"; then
+ AC_DEFINE(BROKEN_REDHAT_7_SYSTEM_HEADERS,1,[Broken RedHat 7.2 system header files])
+fi
+;;
+esac
+
+AC_CACHE_CHECK([for broken nisplus include files],samba_cv_BROKEN_NISPLUS_INCLUDE_FILES,[
+AC_TRY_COMPILE([
+#include <sys/types.h>
+#include <sys/acl.h>
+#if defined(HAVE_RPCSVC_NIS_H)
+#include <rpcsvc/nis.h>
+#endif],
+[int i;],
+samba_cv_BROKEN_NISPLUS_INCLUDE_FILES=no,samba_cv_BROKEN_NISPLUS_INCLUDE_FILES=yes)])
+if test x"$samba_cv_BROKEN_NISPLUS_INCLUDE_FILES" = x"yes"; then
+ AC_DEFINE(BROKEN_NISPLUS_INCLUDE_FILES,1,[Whether the nisplus include files are broken])
+fi
+
+
+#################################################
+# check for smbwrapper support
+AC_MSG_CHECKING(whether to use smbwrapper)
+AC_ARG_WITH(smbwrapper,
+[ --with-smbwrapper Include SMB wrapper support (default=no) ],
+[ case "$withval" in
+ yes)
+ AC_MSG_RESULT(yes)
+ AC_DEFINE(WITH_SMBWRAPPER,1,[Whether to include smbwrapper support])
+ WRAPPROG="bin/smbsh\$(EXEEXT)"
+ WRAP="bin/smbwrapper.$SHLIBEXT"
+
+# Conditions under which smbwrapper should not be built.
+
+ if test x$PICFLAGS = x; then
+ echo No support for PIC code - disabling smbwrapper and smbsh
+ WRAPPROG=""
+ WRAP=""
+ elif test x$ac_cv_func_syscall = xno; then
+ AC_MSG_RESULT([No syscall() -- disabling smbwrapper and smbsh])
+ WRAPPROG=""
+ WRAP=""
+ fi
+ EXTRA_ALL_TARGETS="$EXTRA_ALL_TARGETS $WRAPPROG $WRAP"
+ SMBWRAPPER="$WRAPPROG $WRAP"
+ ;;
+ *)
+ AC_MSG_RESULT(no)
+ ;;
+ esac ],
+ AC_MSG_RESULT(no)
+)
+
+#################################################
+# check for AFS clear-text auth support
+samba_cv_WITH_AFS=no
+AC_MSG_CHECKING(whether to use AFS clear-text auth)
+AC_ARG_WITH(afs,
+[ --with-afs Include AFS clear-text auth support (default=no) ],
+[ case "$withval" in
+ yes|auto)
+ AC_MSG_RESULT($withval)
+ samba_cv_WITH_AFS=$withval
+ ;;
+ *)
+ AC_MSG_RESULT(no)
+ ;;
+ esac ],
+ AC_MSG_RESULT(no)
+)
+
+####################################################
+# check for Linux-specific AFS fake-kaserver support
+samba_cv_WITH_FAKE_KASERVER=no
+AC_MSG_CHECKING(whether to use AFS fake-kaserver)
+AC_ARG_WITH(fake-kaserver,
+[ --with-fake-kaserver Include AFS fake-kaserver support (default=no) ],
+[ case "$withval" in
+ yes|auto)
+ AC_MSG_RESULT($withval)
+ samba_cv_WITH_FAKE_KASERVER=$withval
+ ;;
+ *)
+ AC_MSG_RESULT(no)
+ ;;
+ esac ],
+ AC_MSG_RESULT(no)
+)
+
+#################################################
+# decide whether we can support WITH_AFS and / or WITH_FAKE_KASERVER
+if test x"$samba_cv_WITH_AFS" != x"no" ||
+ test x"$samba_cv_WITH_FAKE_KASERVER" != x"no"; then
+
+ # see if this box has the afs-headers in /usr/include/afs
+ AC_MSG_CHECKING(for /usr/include/afs)
+ if test -d /usr/include/afs; then
+ CFLAGS="$CFLAGS -I/usr/include/afs"
+ CPPFLAGS="$CPPFLAGS -I/usr/include/afs"
+ AC_MSG_RESULT(yes)
+ else
+ AC_MSG_RESULT(no)
+ fi
+
+ # check for afs.h
+ have_afs_headers=no
+ AC_CHECK_HEADERS(afs.h afs/afs.h)
+ if test x"$ac_cv_header_afs_h" = x"no" && test x"$ac_cv_header_afs_afs_h" = x"no"; then
+ if test x"$samba_cv_WITH_FAKE_KASERVER" = x"auto" ||
+ test x"$samba_cv_WITH_AFS" = x"auto"; then
+ AC_MSG_WARN([AFS cannot be supported without afs.h])
+ else
+ AC_MSG_ERROR([AFS cannot be supported without afs.h])
+ fi
+ else
+ have_afs_headers=yes
+ fi
+fi
+
+if test x"$samba_cv_WITH_FAKE_KASERVER" != x"no" && test x"$have_afs_headers" == x"yes"; then
+ AC_DEFINE(WITH_FAKE_KASERVER,1,[Whether to include AFS fake-kaserver support])
+fi
+
+if test x"$samba_cv_WITH_AFS" != x"no" && test x"$have_afs_headers" = x"yes"; then
+ AC_DEFINE(WITH_AFS,1,[Whether to include AFS clear-text auth support])
+fi
+
+#################################################
+# check for the DFS clear-text auth system
+AC_MSG_CHECKING(whether to use DFS clear-text auth)
+AC_ARG_WITH(dfs,
+[ --with-dce-dfs Include DCE/DFS clear-text auth support (default=no)],
+[ case "$withval" in
+ yes)
+ AC_MSG_RESULT(yes)
+ AC_DEFINE(WITH_DFS,1,[Whether to include DFS support])
+ ;;
+ *)
+ AC_MSG_RESULT(no)
+ ;;
+ esac ],
+ AC_MSG_RESULT(no)
+)
+
+########################################################
+# Compile with LDAP support?
+
+with_ldap_support=auto
+AC_MSG_CHECKING([for LDAP support])
+
+AC_ARG_WITH(ldap,
+[ --with-ldap LDAP support (default yes)],
+[ case "$withval" in
+ yes|no)
+ with_ldap_support=$withval
+ ;;
+ esac ])
+
+AC_MSG_RESULT($with_ldap_support)
+
+SMBLDAP=""
+AC_SUBST(SMBLDAP)
+SMBLDAPUTIL=""
+AC_SUBST(SMBLDAPUTIL)
+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])
+ default_static_modules="$default_static_modules pdb_ldap idmap_ldap";
+ default_shared_modules="$default_shared_modules config_ldap";
+ SMBLDAP="lib/smbldap.o"
+ SMBLDAPUTIL="lib/smbldap_util.o"
+ with_ldap_support=yes
+ AC_MSG_CHECKING(whether LDAP support is used)
+ AC_MSG_RESULT(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
+
+
+#################################################
+# active directory support
+
+with_ads_support=auto
+AC_MSG_CHECKING([for Active Directory and krb5 support])
+
+AC_ARG_WITH(ads,
+[ --with-ads Active Directory support (default auto)],
+[ case "$withval" in
+ yes|no)
+ with_ads_support="$withval"
+ ;;
+ esac ])
+
+AC_MSG_RESULT($with_ads_support)
+
+FOUND_KRB5=no
+KRB5_LIBS=""
+
+if test x"$with_ldap_support" != x"yes"; then
+ if test x"$with_ads_support" = x"yes"; then
+ AC_MSG_ERROR(Active Directory Support requires LDAP support)
+ elif test x"$with_ads_support" != x"no"; then
+ AC_MSG_WARN(Active Directory Support requires LDAP support)
+ fi
+ with_ads_support=no
+fi
+
+if test x"$with_ads_support" != x"no"; then
+
+ # Do no harm to the values of CFLAGS and LIBS while testing for
+ # Kerberos support.
+
+ #################################################
+ # check for krb5-config from recent MIT and Heimdal kerberos 5
+ AC_PATH_PROG(KRB5_CONFIG, krb5-config)
+ AC_MSG_CHECKING(for working krb5-config)
+ if test -x "$KRB5_CONFIG"; then
+ ac_save_CFLAGS=$CFLAGS
+ CFLAGS="";export CFLAGS
+ ac_save_LDFLAGS=$LDFLAGS
+ LDFLAGS="";export LDFLAGS
+ KRB5_LIBS="`$KRB5_CONFIG --libs gssapi`"
+ KRB5_CFLAGS="`$KRB5_CONFIG --cflags | sed s/@INCLUDE_des@//`"
+ KRB5_CPPFLAGS="`$KRB5_CONFIG --cflags | sed s/@INCLUDE_des@//`"
+ CFLAGS=$ac_save_CFLAGS;export CFLAGS
+ LDFLAGS=$ac_save_LDFLAGS;export LDFLAGS
+ FOUND_KRB5=yes
+ AC_MSG_RESULT(yes)
+ else
+ AC_MSG_RESULT(no. Fallback to previous krb5 detection strategy)
+ fi
+
+ if test x$FOUND_KRB5 = x"no"; then
+ #################################################
+ # check for location of Kerberos 5 install
+ AC_MSG_CHECKING(for kerberos 5 install path)
+ AC_ARG_WITH(krb5,
+ [ --with-krb5=base-dir Locate Kerberos 5 support (default=/usr)],
+ [ case "$withval" in
+ no)
+ AC_MSG_RESULT(no krb5-path given)
+ ;;
+ yes)
+ AC_MSG_RESULT(/usr)
+ FOUND_KRB5=yes
+ ;;
+ *)
+ AC_MSG_RESULT($withval)
+ KRB5_CFLAGS="-I$withval/include"
+ KRB5_CPPFLAGS="-I$withval/include"
+ KRB5_LDFLAGS="-L$withval/lib"
+ FOUND_KRB5=yes
+ ;;
+ esac ],
+ AC_MSG_RESULT(no krb5-path given)
+ )
+ fi
+
+ if test x$FOUND_KRB5 = x"no"; 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"no"; 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
+
+ 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 AD support was not explicitly requested,
+ # i.e with_ads_support = auto, otherwise die with an error.
+
+ if test x"$with_ads_support" = x"yes"; then
+ AC_MSG_ERROR([Active Directory cannot be supported without krb5.h])
+ else
+ AC_MSG_WARN([Active Directory cannot be supported without krb5.h])
+ fi
+
+ # Turn off AD support and restore CFLAGS and LIBS variables
+
+ with_ads_support="no"
+
+ CFLAGS=$ac_save_CFLAGS
+ CPPFLAGS=$ac_save_CPPFLAGS
+ LDFLAGS=$ac_save_LDFLAGS
+ fi
+fi
+
+# Now we have determined whether we really want ADS support
+
+if test x"$with_ads_support" != x"no"; then
+ ac_save_LIBS=$LIBS
+
+ # now check for gssapi headers. This is also done here to allow for
+ # different kerberos include paths
+ AC_CHECK_HEADERS(gssapi.h gssapi/gssapi_generic.h gssapi/gssapi.h com_err.h)
+
+ ##################################################################
+ # 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)
+
+ # Heimdal checks.
+ 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)
+
+ # 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)
+
+ ########################################################
+ # now see if we can find the gssapi libs in standard paths
+ AC_CHECK_LIB_EXT(gssapi_krb5, KRB5_LIBS,gss_display_status,[],[],
+ AC_DEFINE(HAVE_GSSAPI,1,[Whether GSSAPI is available]))
+
+ 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)
+
+ LIBS="$LIBS $KRB5_LIBS"
+
+ 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 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 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
+
+ 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_DEFINE(WITH_ADS,1,[Whether to include Active Directory support])
+ AC_MSG_CHECKING(whether Active Directory and krb5 support is used)
+ AC_MSG_RESULT(yes)
+ else
+ if test x"$with_ads_support" = x"yes"; then
+ AC_MSG_ERROR(libkrb5 is needed for Active Directory support)
+ else
+ AC_MSG_WARN(libkrb5 is needed for Active Directory support)
+ fi
+ KRB5_LIBS=""
+ with_ads_support=no
+ fi
+ LIBS="$ac_save_LIBS"
+fi
+
+########################################################
+# Compile experimental passdb backends?
+# (pdb_xml, pdb_mysql, pdb_pgsql)
+AC_MSG_CHECKING(whether to build experimental passdb libraries)
+AC_ARG_WITH(expsam,
+[ --with-expsam=<list> Include experimental passdb libraries (default=no)]
+[ Valid choices include (comma separated list): ]
+[ xml, mysql & pgsql],
+[ expsam_pdb_modules=`echo "$withval" | sed 's/,/ /g'`
+ if test "z$expsam_pdb_modules" = "zyes"; then
+ expsam_pdb_modules="xml mysql pgsql"
+ fi
+ AC_MSG_RESULT($expsam_pdb_modules)
+ for i in $expsam_pdb_modules
+ do
+ case "$i" in
+ xml|all|yes)
+ ## pdb_xml
+ AM_PATH_XML2([2.0.0],[default_shared_modules="$default_shared_modules pdb_xml"],[AC_MSG_ERROR([Can't find XML libraries while XML support is requested])])
+ CFLAGS="$CFLAGS $XML_CFLAGS"
+ ;;
+ mysql|all|yes)
+ ## pdb_mysql
+ AM_PATH_MYSQL([default_shared_modules="$default_shared_modules pdb_mysql"],[AC_MSG_ERROR([Can't find MySQL libraries while MySQL support is requested])])
+ CFLAGS="$CFLAGS $MYSQL_CFLAGS"
+ ;;
+ pgsql|all|yes)
+ ## pdb_pgsql
+ AM_PATH_PGSQL([default_shared_modules="$default_shared_modules pdb_pgsql"],[])
+ CFLAGS="$CFLAGS $PGSQL_CFLAGS"
+ ;;
+ no)
+ ;;
+ *)
+ echo "Unknown module name \"$i\"! Exiting..."
+ exit 1
+ ;;
+ esac
+ done ],
+ AC_MSG_RESULT(no)
+)
+
+#################################################
+# check for automount support
+AC_MSG_CHECKING(whether to use automount)
+AC_ARG_WITH(automount,
+[ --with-automount Include automount support (default=no)],
+[ case "$withval" in
+ yes)
+ AC_MSG_RESULT(yes)
+ AC_DEFINE(WITH_AUTOMOUNT,1,[Whether to include automount support])
+ ;;
+ *)
+ AC_MSG_RESULT(no)
+ ;;
+ esac ],
+ AC_MSG_RESULT(no)
+)
+
+#################################################
+# check for smbmount support
+AC_MSG_CHECKING(whether to use smbmount)
+AC_ARG_WITH(smbmount,
+[ --with-smbmount Include smbmount (Linux only) support (default=no)],
+[ case "$withval" in
+ yes)
+ case "$host_os" in
+ *linux*)
+ AC_MSG_RESULT(yes)
+ AC_DEFINE(WITH_SMBMOUNT,1,[Whether to build smbmount])
+ EXTRA_BIN_PROGS="$EXTRA_BIN_PROGS bin/smbmount bin/smbmnt bin/smbumount"
+ ;;
+ *)
+ AC_MSG_ERROR(not on a linux system!)
+ ;;
+ esac
+ ;;
+ *)
+ AC_MSG_RESULT(no)
+ ;;
+ esac ],
+ AC_MSG_RESULT(no)
+)
+
+
+#################################################
+# check for a PAM clear-text auth, accounts, password and session support
+with_pam_for_crypt=no
+AC_MSG_CHECKING(whether to use PAM)
+AC_ARG_WITH(pam,
+[ --with-pam Include PAM support (default=no)],
+[ case "$withval" in
+ yes)
+ AC_MSG_RESULT(yes)
+ if test x"$ac_cv_header_security_pam_appl_h" = x"no"; then
+ if test x"$ac_cv_header_security_pam_modules_h" = x"no"; then
+ if test x"$ac_cv_header_security__pam_macros_h" = x"no"; then
+ AC_MSG_ERROR(--with-pam specified but no PAM headers found)
+ fi
+ fi
+ fi
+ AC_DEFINE(WITH_PAM,1,[Whether to include PAM support])
+ AUTH_LIBS="$AUTH_LIBS -lpam"
+ with_pam_for_crypt=yes
+ ;;
+ *)
+ AC_MSG_RESULT(no)
+ ;;
+ esac ],
+ AC_MSG_RESULT(no)
+)
+
+# we can't build a pam module if we don't have pam.
+AC_CHECK_LIB(pam, pam_get_data, [AC_DEFINE(HAVE_LIBPAM,1,[Whether libpam is available])])
+
+#################################################
+# check for pam_smbpass support
+AC_MSG_CHECKING(whether to use pam_smbpass)
+AC_ARG_WITH(pam_smbpass,
+[ --with-pam_smbpass Build PAM module for authenticating against passdb backends (default=no)],
+[ case "$withval" in
+ yes)
+ AC_MSG_RESULT(yes)
+
+ # Conditions under which pam_smbpass should not be built.
+
+ if test x$PICFLAGS = x; then
+ AC_MSG_ERROR([No support for PIC code])
+ elif test x"$ac_cv_header_security_pam_appl_h" = x"no"; then
+ AC_MSG_ERROR([No security/pam_appl.h found])
+ elif test x$ac_cv_lib_pam_pam_get_data = xno; then
+ AC_MSG_ERROR([No libpam found])
+ else
+ AUTH_LIBS="$AUTH_LIBS -lpam"
+ SHLIB_PROGS="$SHLIB_PROGS bin/pam_smbpass.$SHLIBEXT"
+ fi
+ ;;
+ *)
+ AC_MSG_RESULT(no)
+ ;;
+ esac ],
+ AC_MSG_RESULT(no)
+)
+
+
+###############################################
+# test for where we get crypt() from
+AC_SEARCH_LIBS(crypt, [crypt],
+ [test "$ac_cv_search_crypt" = "none required" || AUTH_LIBS="-lcrypt $AUTH_LIBS"
+ AC_DEFINE(HAVE_CRYPT,1,[Whether the system has the crypt() function])])
+
+##
+## moved after the check for -lcrypt in order to
+## ensure that the necessary libraries are included
+## check checking for truncated salt. Wrapped by the
+## $with_pam_for_crypt variable as above --jerry
+##
+if test $with_pam_for_crypt = no; then
+AC_CACHE_CHECK([for a crypt that needs truncated salt],samba_cv_HAVE_TRUNCATED_SALT,[
+crypt_LIBS="$LIBS"
+LIBS="$AUTH_LIBS $LIBS"
+AC_TRY_RUN([#include "${srcdir-.}/tests/crypttest.c"],
+ samba_cv_HAVE_TRUNCATED_SALT=no,samba_cv_HAVE_TRUNCATED_SALT=yes,samba_cv_HAVE_TRUNCATED_SALT=cross)
+LIBS="$crypt_LIBS"])
+if test x"$samba_cv_HAVE_TRUNCATED_SALT" = x"yes"; then
+ AC_DEFINE(HAVE_TRUNCATED_SALT,1,[Whether crypt needs truncated salt])
+fi
+fi
+
+
+dictpath="/usr/lib/cracklib_dict"
+with_cracklib=yes
+###############################################
+# test for where we get FaciestCheck from
+AC_MSG_CHECKING(where to use cracklib from (default=$dictpath))
+AC_ARG_WITH(cracklib,
+[ --with-cracklib[=DIR] Look for cracklib dictionary in this location ],
+[ case "$withval" in
+ yes)
+ AC_MSG_RESULT(${dictpath})
+ ;;
+ no)
+ AC_MSG_RESULT(no)
+ dictpath=""
+ ;;
+ *)
+ dictpath="$withval"
+ AC_MSG_RESULT(${dictpath})
+ ;;
+ esac ],
+ dictpath=""
+ AC_MSG_RESULT(no)
+)
+
+if test x$dictpath != x""; then
+ AC_SEARCH_LIBS(FascistCheck, [crack],
+ [test "$ac_cv_search_crack" = "none required" || samba_cv_found_crack="yes"
+ AC_DEFINE(HAVE_CRACK,1,[Whether the system has the FaciestCheck function from cracklib])])
+
+ crack_saved_libs=$LIBS;
+
+ if test x$samba_cv_found_crack=x"yes"; then
+ AC_SEARCH_LIBS(CRACKLIB_DICTPATH, [crypt],
+ AC_DEFINE(HAVE_CRACKLIB_DICTPATH, 1, [Whether we have given a CRACKLIB_DICTPATH in our headers])
+ )
+
+ AC_DEFINE_UNQUOTED(SAMBA_CRACKLIB_DICTPATH, "$dictpath", [Where the cracklib dictionay is])
+ AC_MSG_CHECKING(Whether we have a working cracklib)
+ AC_TRY_RUN([
+ #include "${srcdir-.}/tests/crack.c"],
+ AC_MSG_RESULT(yes)
+ AC_DEFINE(HAVE_WORKING_CRACKLIB,1,[Whether we have a working cracklib])
+ AUTH_LIBS="-lcrack $AUTH_LIBS",
+
+ AC_MSG_RESULT(no)
+ AC_MSG_WARN(cracklib exists - but does not function correctly),
+
+ AC_MSG_RESULT(no)
+ AC_MSG_WARN(cannot test-run when cross-compiling)
+ )
+ else
+ AC_MSG_CHECKING(Whether we have cracklib)
+ AC_MSG_RESULT(no)
+ fi
+ LIBS=$crack_saved_libs
+fi
+
+########################################################################################
+##
+## TESTS FOR SAM BACKENDS. KEEP THESE GROUPED TOGETHER
+##
+########################################################################################
+
+#################################################
+# check for a LDAP password database configuration backwards compatibility
+AC_MSG_CHECKING(whether to use LDAP SAM 2.2 compatible configuration)
+AC_ARG_WITH(ldapsam,
+[ --with-ldapsam Include LDAP SAM 2.2 compatible configuration (default=no)],
+[ case "$withval" in
+ yes)
+ AC_MSG_RESULT(yes)
+ AC_DEFINE(WITH_LDAP_SAMCONFIG,1,[Whether to include 2.2 compatible LDAP SAM configuration])
+ ;;
+ *)
+ AC_MSG_RESULT(no)
+ ;;
+ esac ],
+ AC_MSG_RESULT(no)
+)
+
+########################################################################################
+##
+## END OF TESTS FOR SAM BACKENDS.
+##
+########################################################################################
+
+#################################################
+# check for a NISPLUS_HOME support
+AC_MSG_CHECKING(whether to use NISPLUS_HOME)
+AC_ARG_WITH(nisplus-home,
+[ --with-nisplus-home Include NISPLUS_HOME support (default=no)],
+[ case "$withval" in
+ yes)
+ AC_MSG_RESULT(yes)
+ AC_DEFINE(WITH_NISPLUS_HOME,1,[Whether to include nisplus_home support])
+ ;;
+ *)
+ AC_MSG_RESULT(no)
+ ;;
+ esac ],
+ AC_MSG_RESULT(no)
+)
+
+#################################################
+# check for syslog logging
+AC_MSG_CHECKING(whether to use syslog logging)
+AC_ARG_WITH(syslog,
+[ --with-syslog Include experimental SYSLOG support (default=no)],
+[ case "$withval" in
+ yes)
+ AC_MSG_RESULT(yes)
+ AC_DEFINE(WITH_SYSLOG,1,[Whether to include experimental syslog support])
+ ;;
+ *)
+ AC_MSG_RESULT(no)
+ ;;
+ esac ],
+ AC_MSG_RESULT(no)
+)
+
+#################################################
+# check for a shared memory profiling support
+AC_MSG_CHECKING(whether to use profiling)
+AC_ARG_WITH(profiling-data,
+[ --with-profiling-data Include gathering source code profile information (default=no)],
+[ case "$withval" in
+ yes)
+ AC_MSG_RESULT(yes)
+ AC_DEFINE(WITH_PROFILE,1,[Whether to use profiling])
+ ;;
+ *)
+ AC_MSG_RESULT(no)
+ ;;
+ esac ],
+ AC_MSG_RESULT(no)
+)
+
+
+#################################################
+# check for experimental disk-quotas support
+
+samba_cv_WITH_QUOTAS=auto
+samba_cv_TRY_QUOTAS=no
+samba_cv_RUN_QUOTA_TESTS=auto
+samba_cv_WITH_SYS_QUOTAS=auto
+samba_cv_TRY_SYS_QUOTAS=no
+samba_cv_SYSQUOTA_FOUND=no;
+
+AC_MSG_CHECKING(whether to try disk-quotas support)
+AC_ARG_WITH(quotas,
+[ --with-quotas Include disk-quota support (default=no)],
+[ case "$withval" in
+ yes)
+ AC_MSG_RESULT(yes)
+ samba_cv_WITH_QUOTAS=yes
+ samba_cv_TRY_QUOTAS=yes
+ samba_cv_RUN_QUOTA_TESTS=yes
+ #set sys quotas to auto in this case
+ samba_cv_TRY_SYS_QUOTAS=auto
+ ;;
+ auto)
+ AC_MSG_RESULT(auto)
+ samba_cv_WITH_QUOTAS=auto
+ samba_cv_TRY_QUOTAS=auto
+ samba_cv_RUN_QUOTA_TESTS=auto
+ #set sys quotas to auto in this case
+ samba_cv_TRY_SYS_QUOTAS=auto
+ ;;
+ no)
+ AC_MSG_RESULT(no)
+ samba_cv_WITH_QUOTAS=no
+ samba_cv_TRY_QUOTAS=no
+ samba_cv_RUN_QUOTA_TESTS=no
+ ;;
+ *)
+ AC_MSG_RESULT(${samba_cv_TRY_QUOTAS})
+ ;;
+ esac ],
+ AC_MSG_RESULT(${samba_cv_TRY_QUOTAS})
+)
+
+AC_MSG_CHECKING(whether to try the new lib/sysquotas.c interface)
+AC_ARG_WITH(sys-quotas,
+[ --with-sys-quotas Include lib/sysquotas.c support (default=auto)],
+[ case "$withval" in
+ yes)
+ AC_MSG_RESULT(yes)
+ samba_cv_WITH_SYS_QUOTAS=yes
+ samba_cv_TRY_SYS_QUOTAS=yes
+ samba_cv_RUN_QUOTA_TESTS=yes
+ ;;
+ auto)
+ AC_MSG_RESULT(auto)
+ samba_cv_WITH_SYS_QUOTAS=auto
+ samba_cv_TRY_SYS_QUOTAS=auto
+ samba_cv_RUN_QUOTA_TESTS=auto
+ ;;
+ no)
+ AC_MSG_RESULT(no)
+ samba_cv_WITH_SYS_QUOTAS=no
+ samba_cv_TRY_SYS_QUOTAS=no
+ ;;
+ *)
+ AC_MSG_RESULT(${samba_cv_TRY_SYS_QUOTAS})
+ ;;
+ esac ],
+ AC_MSG_RESULT(${samba_cv_TRY_SYS_QUOTAS})
+)
+
+if test x"$samba_cv_TRY_SYS_QUOTAS" = x"auto"; then
+AC_MSG_CHECKING(whether to try the lib/sysquotas.c interface on ${host_os})
+ case "$host_os" in
+ *linux*)
+ AC_MSG_RESULT(yes)
+ samba_cv_TRY_SYS_QUOTAS=yes
+ samba_cv_RUN_QUOTA_TESTS=yes
+ samba_cv_SYSQUOTA_FOUND=yes
+ AC_DEFINE(HAVE_QUOTACTL_LINUX,1,[Whether Linux quota support is available])
+ samba_cv_sysquotas_file="lib/sysquotas_linux.c"
+ AC_DEFINE(HAVE_LINUX_XFS_QUOTAS,1,[Whether Linux xfs quota support is available])
+ samba_cv_found_xfs_header=yes
+ ;;
+ *)
+ AC_MSG_RESULT(no)
+ samba_cv_TRY_SYS_QUOTAS=no
+ ;;
+ esac
+fi
+
+#############################################
+# only check for quota stuff if --with-quotas
+if test x"$samba_cv_RUN_QUOTA_TESTS" != x"no"; then
+
+# some broken header files need this
+AC_CHECK_HEADER(asm/types.h,[
+ AC_DEFINE(HAVE_ASM_TYPES_H,1,[check for <asm/types.h>])
+ AC_ADD_INCLUDE(<asm/types.h>)
+ ])
+
+# For quotas on Veritas VxFS filesystems
+AC_CHECK_HEADERS(sys/fs/vx_quota.h)
+
+# For sys/quota.h and linux/quota.h
+AC_CHECK_HEADERS(sys/quota.h)
+
+if test x"$samba_cv_found_xfs_header" != x"yes"; then
+# if we have xfs quota support <sys/quota.h> (IRIX) we should use it
+AC_CACHE_CHECK([for XFS QUOTA in <sys/quota.h>],samba_cv_HAVE_SYS_QUOTA_XFS, [
+AC_TRY_COMPILE([
+#include "confdefs.h"
+#ifdef HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+#ifdef HAVE_ASM_TYPES_H
+#include <asm/types.h>
+#endif
+#include <sys/quota.h>
+],[int i = Q_XGETQUOTA;],
+samba_cv_HAVE_SYS_QUOTA_XFS=yes,samba_cv_HAVE_SYS_QUOTA_XFS=no)])
+if test "$samba_cv_HAVE_SYS_QUOTA_XFS"x = "yes"x; then
+ samba_cv_found_xfs_header=yes
+fi
+fi
+
+# if we have struct dqblk .dqb_fsoftlimit instead of .dqb_isoftlimit on IRIX
+AC_CACHE_CHECK([if struct dqblk has .dqb_fsoftlimit],samba_cv_HAVE_DQB_FSOFTLIMIT, [
+AC_TRY_COMPILE([
+#include "confdefs.h"
+#ifdef HAVE_SYS_QUOTA_H
+#include <sys/quota.h>
+#endif
+],[
+struct dqblk D;
+D.dqb_fsoftlimit = 0;],
+samba_cv_HAVE_DQB_FSOFTLIMIT=yes,samba_cv_HAVE_DQB_FSOFTLIMIT=no)])
+if test "$samba_cv_HAVE_DQB_FSOFTLIMIT"x = "yes"x; then
+ AC_DEFINE(HAVE_DQB_FSOFTLIMIT,1,[struct dqblk .dqb_fsoftlimit])
+fi
+
+##################
+# look for a working quota system
+
+if test x"$samba_cv_SYSQUOTA_FOUND" != x"yes"; then
+AC_CACHE_CHECK([for long quotactl(int cmd, char *special, qid_t id, caddr_t addr)],samba_cv_HAVE_QUOTACTL_4A,[
+AC_TRY_RUN_STRICT([
+#define HAVE_QUOTACTL_4A 1
+#define AUTOCONF_TEST 1
+#include "confdefs.h"
+#include "${srcdir-.}/tests/sysquotas.c"],[$Werror_FLAGS],[$CPPFLAGS],[$LDFLAGS],
+ samba_cv_HAVE_QUOTACTL_4A=yes,samba_cv_HAVE_QUOTACTL_4A=no,samba_cv_HAVE_QUOTACTL_4A=cross)])
+if test x"$samba_cv_HAVE_QUOTACTL_4A" = x"yes"; then
+ samba_cv_SYSQUOTA_FOUND=yes;
+ AC_DEFINE(HAVE_QUOTACTL_4A,1,[Whether long quotactl(int cmd, char *special, qid_t id, caddr_t addr) is available])
+ samba_cv_sysquotas_file="lib/sysquotas_4A.c"
+fi
+fi
+
+if test x"$samba_cv_SYSQUOTA_FOUND" != x"yes"; then
+AC_CACHE_CHECK([for int quotactl(const char *path, int cmd, int id, char *addr)],samba_cv_HAVE_QUOTACTL_4B,[
+AC_TRY_RUN_STRICT([
+#define HAVE_QUOTACTL_4B 1
+#define AUTOCONF_TEST 1
+#include "confdefs.h"
+#include "${srcdir-.}/tests/sysquotas.c"],[$Werror_FLAGS],[$CPPFLAGS],[$LDFLAGS],
+ samba_cv_HAVE_QUOTACTL_4B=yes,samba_cv_HAVE_QUOTACTL_4B=no,samba_cv_HAVE_QUOTACTL_4B=cross)])
+if test x"$samba_cv_HAVE_QUOTACTL_4B" = x"yes"; then
+ echo "int quotactl(const char *path, int cmd, int id, char *addr) is not reworked for the new sys_quota api"
+ samba_cv_SYSQUOTA_FOUND=yes;
+ AC_DEFINE(HAVE_QUOTACTL_4B,1,[Whether int quotactl(const char *path, int cmd, int id, char *addr) is available])
+ samba_cv_sysquotas_file="lib/sysquotas_4B.c"
+fi
+fi
+
+if test x"$samba_cv_SYSQUOTA_FOUND" != x"yes"; then
+AC_CACHE_CHECK([for CRAY int quotactl (char *spec, int request, char *arg)],samba_cv_HAVE_QUOTACTL_3,[
+AC_TRY_RUN_STRICT([
+#define HAVE_QUOTACTL_3 1
+#define AUTOCONF_TEST 1
+#include "confdefs.h"
+#include "${srcdir-.}/tests/sysquotas.c"],[$Werror_FLAGS],[$CPPFLAGS],[$LDFLAGS],
+ samba_cv_HAVE_QUOTACTL_3=yes,samba_cv_HAVE_QUOTACTL_3=no,samba_cv_HAVE_QUOTACTL_3=cross)])
+if test x"$samba_cv_HAVE_QUOTACTL_3" = x"yes"; then
+ echo "CRAY int quotactl (char *spec, int request, char *arg) is NOT reworked for the sys_quota api"
+ samba_cv_SYSQUOTA_FOUND=yes;
+ AC_DEFINE(HAVE_QUOTACTL_3,1,[Whether CRAY int quotactl (char *spec, int request, char *arg); is available])
+ samba_cv_sysquotas_file="lib/sysquotas_3.c"
+fi
+fi
+
+#################################################
+# check for mntent.h and struct mntent
+AC_CHECK_HEADERS(mntent.h)
+#################################################
+# check for setmntent,getmntent,endmntent
+AC_CHECK_FUNCS(setmntent getmntent endmntent)
+
+#################################################
+# check for devnm.h and struct mntent
+AC_CHECK_HEADERS(devnm.h)
+#################################################
+# check for devnm
+AC_CHECK_FUNCS(devnm)
+
+if test x"$samba_cv_WITH_SYS_QUOTAS" = x"yes"; then
+ if test x"$samba_cv_SYSQUOTA_FOUND" != x"yes"; then
+ # if --with-sys-quotas=yes then build it
+ # you have can use the get/set quota command smb.conf
+ # options then
+ samba_cv_SYSQUOTA_FOUND=auto
+ fi
+ if test x"$samba_cv_TRY_SYS_QUOTAS" != x"yes"; then
+ # if --with-sys-quotas=yes then build it
+ # you have can use the get/set quota command smb.conf
+ # options then
+ samba_cv_TRY_SYS_QUOTAS=auto
+ fi
+fi
+
+if test x"$samba_cv_SYSQUOTA_FOUND" != x"no"; then
+AC_CACHE_CHECK([whether the sys_quota interface works],samba_cv_SYSQUOTA_WORKS,[
+SAVE_CPPFLAGS="$CPPFLAGS"
+CPPFLAGS="$CPPFLAGS -I${srcdir-.}/ -I. -I${srcdir-.}/include -I${srcdir-.}/ubiqx -I${srcdir-.}/popt -I${srcdir-.}/smbwrapper -I${srcdir-.}/nsswitch"
+AC_TRY_COMPILE([
+#include "confdefs.h"
+#define NO_PROTO_H 1
+#define NO_CONFIG_H 1
+#define HAVE_SYS_QUOTAS 1
+#include "${srcdir-.}/${samba_cv_sysquotas_file}"
+#include "${srcdir-.}/lib/sysquotas.c"
+],[],samba_cv_SYSQUOTA_WORKS=yes,samba_cv_SYSQUOTA_WORKS=no)
+CPPFLAGS="$SAVE_CPPFLAGS"
+])
+if test x"$samba_cv_SYSQUOTA_WORKS" = x"yes"; then
+AC_MSG_CHECKING(whether to use the new lib/sysquotas.c interface)
+ if test x"$samba_cv_TRY_SYS_QUOTAS" != x"no"; then
+ AC_DEFINE(WITH_QUOTAS,1,[Whether to use disk quota support])
+ AC_DEFINE(HAVE_SYS_QUOTAS,1,[Whether the new lib/sysquotas.c interface can be used])
+ samba_cv_WE_USE_SYS_QUOTAS=yes
+ AC_MSG_RESULT(yes)
+ else
+ AC_MSG_RESULT(no)
+ fi
+fi
+fi
+
+if test x"$samba_cv_SYSQUOTA_FOUND" != x"no" -a x"$samba_cv_found_xfs_header" = x"yes"; then
+AC_CACHE_CHECK([whether the sys_quota interface works with XFS],samba_cv_SYSQUOTA_WORKS_XFS,[
+SAVE_CPPFLAGS="$CPPFLAGS"
+CPPFLAGS="$CPPFLAGS -I${srcdir-.}/ -I. -I${srcdir-.}/include -I${srcdir-.}/ubiqx -I${srcdir-.}/popt -I${srcdir-.}/smbwrapper -I${srcdir-.}/nsswitch"
+AC_TRY_COMPILE([
+#include "confdefs.h"
+#define NO_PROTO_H 1
+#define NO_CONFIG_H 1
+#define HAVE_SYS_QUOTAS 1
+#define HAVE_XFS_QUOTAS 1
+#include "${srcdir-.}/lib/sysquotas_xfs.c"
+],[],samba_cv_SYSQUOTA_WORKS_XFS=yes,samba_cv_SYSQUOTA_WORKS_XFS=no)
+CPPFLAGS="$SAVE_CPPFLAGS"
+])
+if test x"$samba_cv_SYSQUOTA_WORKS_XFS" = x"yes"; then
+ if test x"$samba_cv_WE_USE_SYS_QUOTAS" = x"yes"; then
+ AC_DEFINE(HAVE_XFS_QUOTAS,1,[Whether xfs quota support is available])
+ fi
+fi
+fi
+
+AC_CACHE_CHECK([whether the old quota support works],samba_cv_QUOTA_WORKS,[
+SAVE_CPPFLAGS="$CPPFLAGS"
+CPPFLAGS="$CPPFLAGS -I${srcdir-.}/ -I. -I${srcdir-.}/include -I${srcdir-.}/ubiqx -I${srcdir-.}/popt -I${srcdir-.}/smbwrapper -I${srcdir-.}/nsswitch"
+AC_TRY_COMPILE([
+#include "confdefs.h"
+#define NO_PROTO_H 1
+#define NO_CONFIG_H 1
+#include "${srcdir-.}/smbd/quotas.c"
+],[],samba_cv_QUOTA_WORKS=yes,samba_cv_QUOTA_WORKS=no)
+CPPFLAGS="$SAVE_CPPFLAGS"
+])
+if test x"$samba_cv_QUOTA_WORKS" = x"yes"; then
+AC_MSG_CHECKING(whether to use the old quota support)
+ if test x"$samba_cv_WE_USE_SYS_QUOTAS" != x"yes"; then
+ if test x"$samba_cv_TRY_QUOTAS" != x"no"; then
+ AC_DEFINE(WITH_QUOTAS,1,[Whether to use disk quota support])
+ AC_MSG_RESULT(yes)
+ else
+ AC_MSG_RESULT(no)
+ fi
+ else
+ AC_MSG_RESULT(no)
+ fi
+fi
+
+####################
+# End of quota check samba_cv_RUN_QUOTA_TESTS
+fi
+
+#################################################
+# check for experimental utmp accounting
+
+AC_MSG_CHECKING(whether to support utmp accounting)
+WITH_UTMP=yes
+AC_ARG_WITH(utmp,
+[ --with-utmp Include utmp accounting (default, if supported by OS)],
+[ case "$withval" in
+ no)
+ WITH_UTMP=no
+ ;;
+ *)
+ WITH_UTMP=yes
+ ;;
+ esac ],
+)
+
+# utmp requires utmp.h
+# Note similar check earlier, when checking utmp details.
+
+if test x"$WITH_UTMP" = x"yes" -a x"$ac_cv_header_utmp_h" = x"no"; then
+ utmp_no_reason=", no utmp.h on $host_os"
+ WITH_UTMP=no
+fi
+
+# Display test results
+
+if test x"$WITH_UTMP" = x"yes"; then
+ AC_MSG_RESULT(yes)
+ AC_DEFINE(WITH_UTMP,1,[Whether to include experimental utmp accounting])
+else
+ AC_MSG_RESULT(no$utmp_no_reason)
+fi
+
+#################################################
+# choose native language(s) of man pages
+AC_MSG_CHECKING(chosen man pages' language(s))
+AC_ARG_WITH(manpages-langs,
+[ --with-manpages-langs={en,ja,pl} Choose man pages' language(s). (en)],
+[ case "$withval" in
+ yes|no)
+ AC_MSG_WARN(--with-manpages-langs called without argument - will use default)
+ manlangs="en"
+ ;;
+ *)
+ manlangs="$withval"
+ ;;
+ esac
+
+ AC_MSG_RESULT($manlangs)
+ manlangs=`echo $manlangs | sed "s/,/ /g"` # replacing commas with spaces to produce a list
+ AC_SUBST(manlangs)],
+
+ [manlangs="en"
+ AC_MSG_RESULT($manlangs)
+ AC_SUBST(manlangs)]
+)
+
+#################################################
+# should we build libsmbclient?
+
+INSTALLCLIENTCMD_SH=:
+INSTALLCLIENTCMD_A=:
+INSTALLCLIENT=
+LIBSMBCLIENT_SHARED=
+LIBSMBCLIENT=
+AC_MSG_CHECKING(whether to build the libsmbclient shared library)
+AC_ARG_WITH(libsmbclient,
+[ --with-libsmbclient Build the libsmbclient shared library (default=yes if shared libs supported)],
+[ case "$withval" in
+ no)
+ AC_MSG_RESULT(no)
+ ;;
+ *)
+ if test $BLDSHARED = true; then
+ INSTALLCLIENTCMD_SH="\$(INSTALLCMD)"
+ ## build the static version of libsmbclient as well
+ INSTALLCLIENTCMD_A="\$(INSTALLCMD)"
+ LIBSMBCLIENT_SHARED=bin/libsmbclient.$SHLIBEXT
+ LIBSMBCLIENT=libsmbclient
+ AC_MSG_RESULT(yes)
+ else
+ enable_static=yes
+ AC_MSG_RESULT(no shared library support -- will supply static library)
+ fi
+ if test $enable_static = yes; then
+ INSTALLCLIENTCMD_A="\$(INSTALLCMD)"
+ LIBSMBCLIENT=libsmbclient
+ fi
+ INSTALLCLIENT=installclientlib
+ ;;
+ esac ],
+[
+# if unspecified, default is to built it iff possible.
+ if test $BLDSHARED = true; then
+ INSTALLCLIENTCMD_SH="\$(INSTALLCMD)"
+ LIBSMBCLIENT_SHARED=bin/libsmbclient.$SHLIBEXT
+ LIBSMBCLIENT=libsmbclient
+ AC_MSG_RESULT(yes)
+ else
+ enable_static=yes
+ AC_MSG_RESULT(no shared library support -- will supply static library)
+ fi
+ if test $enable_static = yes; then
+ INSTALLCLIENTCMD_A="\$(INSTALLCMD)"
+ LIBSMBCLIENT=libsmbclient
+ fi]
+ INSTALLCLIENT=installclientlib
+)
+
+
+#################################################
+# these tests are taken from the GNU fileutils package
+AC_CHECKING(how to get filesystem space usage)
+space=no
+
+# Test for statvfs64.
+if test $space = no; then
+ # SVR4
+ AC_CACHE_CHECK([statvfs64 function (SVR4)], fu_cv_sys_stat_statvfs64,
+ [AC_TRY_RUN([
+#if defined(HAVE_UNISTD_H)
+#include <unistd.h>
+#endif
+#include <sys/types.h>
+#include <sys/statvfs.h>
+ main ()
+ {
+ struct statvfs64 fsd;
+ exit (statvfs64 (".", &fsd));
+ }],
+ fu_cv_sys_stat_statvfs64=yes,
+ fu_cv_sys_stat_statvfs64=no,
+ fu_cv_sys_stat_statvfs64=cross)])
+ if test $fu_cv_sys_stat_statvfs64 = yes; then
+ space=yes
+ AC_DEFINE(STAT_STATVFS64,1,[Whether statvfs64() is available])
+ fi
+fi
+
+# Perform only the link test since it seems there are no variants of the
+# statvfs function. This check is more than just AC_CHECK_FUNCS(statvfs)
+# because that got a false positive on SCO OSR5. Adding the declaration
+# of a `struct statvfs' causes this test to fail (as it should) on such
+# systems. That system is reported to work fine with STAT_STATFS4 which
+# is what it gets when this test fails.
+if test $space = no; then
+ # SVR4
+ AC_CACHE_CHECK([statvfs function (SVR4)], fu_cv_sys_stat_statvfs,
+ [AC_TRY_LINK([#include <sys/types.h>
+#include <sys/statvfs.h>],
+ [struct statvfs fsd; statvfs (0, &fsd);],
+ fu_cv_sys_stat_statvfs=yes,
+ fu_cv_sys_stat_statvfs=no)])
+ if test $fu_cv_sys_stat_statvfs = yes; then
+ space=yes
+ AC_DEFINE(STAT_STATVFS,1,[Whether statvfs() is available])
+ fi
+fi
+
+if test $space = no; then
+ # DEC Alpha running OSF/1
+ AC_MSG_CHECKING([for 3-argument statfs function (DEC OSF/1)])
+ AC_CACHE_VAL(fu_cv_sys_stat_statfs3_osf1,
+ [AC_TRY_RUN([
+#include <sys/param.h>
+#include <sys/types.h>
+#include <sys/mount.h>
+ main ()
+ {
+ struct statfs fsd;
+ fsd.f_fsize = 0;
+ exit (statfs (".", &fsd, sizeof (struct statfs)));
+ }],
+ fu_cv_sys_stat_statfs3_osf1=yes,
+ fu_cv_sys_stat_statfs3_osf1=no,
+ fu_cv_sys_stat_statfs3_osf1=no)])
+ AC_MSG_RESULT($fu_cv_sys_stat_statfs3_osf1)
+ if test $fu_cv_sys_stat_statfs3_osf1 = yes; then
+ space=yes
+ AC_DEFINE(STAT_STATFS3_OSF1,1,[Whether statfs requires 3 arguments])
+ fi
+fi
+
+if test $space = no; then
+# AIX
+ AC_MSG_CHECKING([for two-argument statfs with statfs.bsize dnl
+member (AIX, 4.3BSD)])
+ AC_CACHE_VAL(fu_cv_sys_stat_statfs2_bsize,
+ [AC_TRY_RUN([
+#ifdef HAVE_SYS_PARAM_H
+#include <sys/param.h>
+#endif
+#ifdef HAVE_SYS_MOUNT_H
+#include <sys/mount.h>
+#endif
+#ifdef HAVE_SYS_VFS_H
+#include <sys/vfs.h>
+#endif
+ main ()
+ {
+ struct statfs fsd;
+ fsd.f_bsize = 0;
+ exit (statfs (".", &fsd));
+ }],
+ fu_cv_sys_stat_statfs2_bsize=yes,
+ fu_cv_sys_stat_statfs2_bsize=no,
+ fu_cv_sys_stat_statfs2_bsize=no)])
+ AC_MSG_RESULT($fu_cv_sys_stat_statfs2_bsize)
+ if test $fu_cv_sys_stat_statfs2_bsize = yes; then
+ space=yes
+ AC_DEFINE(STAT_STATFS2_BSIZE,1,[Whether statfs requires two arguments and struct statfs has bsize property])
+ fi
+fi
+
+if test $space = no; then
+# SVR3
+ AC_MSG_CHECKING([for four-argument statfs (AIX-3.2.5, SVR3)])
+ AC_CACHE_VAL(fu_cv_sys_stat_statfs4,
+ [AC_TRY_RUN([#include <sys/types.h>
+#include <sys/statfs.h>
+ main ()
+ {
+ struct statfs fsd;
+ exit (statfs (".", &fsd, sizeof fsd, 0));
+ }],
+ fu_cv_sys_stat_statfs4=yes,
+ fu_cv_sys_stat_statfs4=no,
+ fu_cv_sys_stat_statfs4=no)])
+ AC_MSG_RESULT($fu_cv_sys_stat_statfs4)
+ if test $fu_cv_sys_stat_statfs4 = yes; then
+ space=yes
+ AC_DEFINE(STAT_STATFS4,1,[Whether statfs requires 4 arguments])
+ fi
+fi
+
+if test $space = no; then
+# 4.4BSD and NetBSD
+ AC_MSG_CHECKING([for two-argument statfs with statfs.fsize dnl
+member (4.4BSD and NetBSD)])
+ AC_CACHE_VAL(fu_cv_sys_stat_statfs2_fsize,
+ [AC_TRY_RUN([#include <sys/types.h>
+#ifdef HAVE_SYS_PARAM_H
+#include <sys/param.h>
+#endif
+#ifdef HAVE_SYS_MOUNT_H
+#include <sys/mount.h>
+#endif
+ main ()
+ {
+ struct statfs fsd;
+ fsd.f_fsize = 0;
+ exit (statfs (".", &fsd));
+ }],
+ fu_cv_sys_stat_statfs2_fsize=yes,
+ fu_cv_sys_stat_statfs2_fsize=no,
+ fu_cv_sys_stat_statfs2_fsize=no)])
+ AC_MSG_RESULT($fu_cv_sys_stat_statfs2_fsize)
+ if test $fu_cv_sys_stat_statfs2_fsize = yes; then
+ space=yes
+ AC_DEFINE(STAT_STATFS2_FSIZE,1,[Whether statfs requires 2 arguments and struct statfs has fsize])
+ fi
+fi
+
+if test $space = no; then
+ # Ultrix
+ AC_MSG_CHECKING([for two-argument statfs with struct fs_data (Ultrix)])
+ AC_CACHE_VAL(fu_cv_sys_stat_fs_data,
+ [AC_TRY_RUN([#include <sys/types.h>
+#ifdef HAVE_SYS_PARAM_H
+#include <sys/param.h>
+#endif
+#ifdef HAVE_SYS_MOUNT_H
+#include <sys/mount.h>
+#endif
+#ifdef HAVE_SYS_FS_TYPES_H
+#include <sys/fs_types.h>
+#endif
+ main ()
+ {
+ struct fs_data fsd;
+ /* Ultrix's statfs returns 1 for success,
+ 0 for not mounted, -1 for failure. */
+ exit (statfs (".", &fsd) != 1);
+ }],
+ fu_cv_sys_stat_fs_data=yes,
+ fu_cv_sys_stat_fs_data=no,
+ fu_cv_sys_stat_fs_data=no)])
+ AC_MSG_RESULT($fu_cv_sys_stat_fs_data)
+ if test $fu_cv_sys_stat_fs_data = yes; then
+ space=yes
+ AC_DEFINE(STAT_STATFS2_FS_DATA,1,[Whether statfs requires 2 arguments and struct fs_data is available])
+ fi
+fi
+
+#
+# As a gating factor for large file support, in order to
+# use <4GB files we must have the following minimal support
+# available.
+# long long, and a 64 bit off_t or off64_t.
+# If we don't have all of these then disable large
+# file support.
+#
+AC_MSG_CHECKING([if large file support can be enabled])
+AC_TRY_COMPILE([
+#if defined(HAVE_LONGLONG) && (defined(HAVE_OFF64_T) || (defined(SIZEOF_OFF_T) && (SIZEOF_OFF_T == 8)))
+#include <sys/types.h>
+#else
+__COMPILE_ERROR_
+#endif
+],
+[int i],
+samba_cv_HAVE_EXPLICIT_LARGEFILE_SUPPORT=yes,samba_cv_HAVE_EXPLICIT_LARGEFILE_SUPPORT=no)
+if test x"$samba_cv_HAVE_EXPLICIT_LARGEFILE_SUPPORT" = x"yes"; then
+ AC_DEFINE(HAVE_EXPLICIT_LARGEFILE_SUPPORT,1,[Whether large file support can be enabled])
+fi
+AC_MSG_RESULT([$samba_cv_HAVE_EXPLICIT_LARGEFILE_SUPPORT])
+
+AC_ARG_WITH(spinlocks,
+[ --with-spinlocks Use spin locks instead of fcntl locks (default=no) ])
+if test "x$with_spinlocks" = "xyes"; then
+ AC_DEFINE(USE_SPINLOCKS,1,[Whether to use spin locks instead of fcntl locks])
+
+ case "$host_cpu" in
+ sparc)
+ AC_DEFINE(SPARC_SPINLOCKS,1,[Whether to use sparc spinlocks])
+ ;;
+
+ i386|i486|i586|i686)
+ AC_DEFINE(INTEL_SPINLOCKS,1,[Whether to use intel spinlocks])
+ ;;
+
+ mips)
+ AC_DEFINE(MIPS_SPINLOCKS,1,[Whether to use mips spinlocks])
+ ;;
+
+ powerpc)
+ AC_DEFINE(POWERPC_SPINLOCKS,1,[Whether to use powerpc spinlocks])
+ ;;
+ esac
+fi
+
+#################################################
+# check for ACL support
+
+AC_MSG_CHECKING(whether to support ACLs)
+AC_ARG_WITH(acl-support,
+[ --with-acl-support Include ACL support (default=no)],
+[ case "$withval" in
+ yes)
+
+ case "$host_os" in
+ *sysv5*)
+ AC_MSG_RESULT(Using UnixWare ACLs)
+ AC_DEFINE(HAVE_UNIXWARE_ACLS,1,[Whether UnixWare ACLs are available])
+ ;;
+ *solaris*)
+ AC_MSG_RESULT(Using solaris ACLs)
+ AC_DEFINE(HAVE_SOLARIS_ACLS,1,[Whether solaris ACLs are available])
+ ;;
+ *hpux*)
+ AC_MSG_RESULT(Using HPUX ACLs)
+ AC_DEFINE(HAVE_HPUX_ACLS,1,[Whether HPUX ACLs are available])
+ ;;
+ *irix*)
+ AC_MSG_RESULT(Using IRIX ACLs)
+ AC_DEFINE(HAVE_IRIX_ACLS,1,[Whether IRIX ACLs are available])
+ ;;
+ *aix*)
+ AC_MSG_RESULT(Using AIX ACLs)
+ AC_DEFINE(HAVE_AIX_ACLS,1,[Whether AIX ACLs are available])
+ ;;
+ *osf*)
+ AC_MSG_RESULT(Using Tru64 ACLs)
+ AC_DEFINE(HAVE_TRU64_ACLS,1,[Whether Tru64 ACLs are available])
+ ACL_LIBS="$ACL_LIBS -lpacl"
+ ;;
+ *freebsd5*)
+ AC_MSG_RESULT(Using FreeBSD posix ACLs)
+ AC_DEFINE(HAVE_POSIX_ACLS,1,[Whether FreeBSD POSIX ACLs are available])
+ AC_DEFINE(HAVE_ACL_GET_PERM_NP,1,[Whether acl_get_perm_np() is available])
+ ;;
+ *linux*)
+ AC_CHECK_LIB(attr,getxattr,[ACL_LIBS="$ACL_LIBS -lattr"])
+ AC_CHECK_LIB(acl,acl_get_file,[ACL_LIBS="$ACL_LIBS -lacl"])
+ AC_CACHE_CHECK([for ACL support],samba_cv_HAVE_POSIX_ACLS,[
+ acl_LIBS=$LIBS
+ LIBS="$LIBS -lacl"
+ AC_TRY_LINK([#include <sys/types.h>
+#include <sys/acl.h>],
+[ acl_t acl; int entry_id; acl_entry_t *entry_p; return acl_get_entry( acl, entry_id, entry_p);],
+samba_cv_HAVE_POSIX_ACLS=yes,samba_cv_HAVE_POSIX_ACLS=no)
+ LIBS=$acl_LIBS])
+ if test x"$samba_cv_HAVE_POSIX_ACLS" = x"yes"; then
+ AC_MSG_RESULT(Using posix ACLs)
+ AC_DEFINE(HAVE_POSIX_ACLS,1,[Whether POSIX ACLs are available])
+ AC_CACHE_CHECK([for acl_get_perm_np],samba_cv_HAVE_ACL_GET_PERM_NP,[
+ acl_LIBS=$LIBS
+ LIBS="$LIBS -lacl"
+ AC_TRY_LINK([#include <sys/types.h>
+#include <sys/acl.h>],
+[ acl_permset_t permset_d; acl_perm_t perm; return acl_get_perm_np( permset_d, perm);],
+samba_cv_HAVE_ACL_GET_PERM_NP=yes,samba_cv_HAVE_ACL_GET_PERM_NP=no)
+ LIBS=$acl_LIBS])
+ if test x"$samba_cv_HAVE_ACL_GET_PERM_NP" = x"yes"; then
+ AC_DEFINE(HAVE_ACL_GET_PERM_NP,1,[Whether acl_get_perm_np() is available])
+ fi
+ fi
+ ;;
+ *)
+ AC_CHECK_LIB(acl,acl_get_file,[ACL_LIBS="$ACL_LIBS -lacl"])
+ AC_CACHE_CHECK([for ACL support],samba_cv_HAVE_POSIX_ACLS,[
+ acl_LIBS=$LIBS
+ LIBS="$LIBS -lacl"
+ AC_TRY_LINK([#include <sys/types.h>
+#include <sys/acl.h>],
+[ acl_t acl; int entry_id; acl_entry_t *entry_p; return acl_get_entry( acl, entry_id, entry_p);],
+samba_cv_HAVE_POSIX_ACLS=yes,samba_cv_HAVE_POSIX_ACLS=no)
+ LIBS=$acl_LIBS])
+ if test x"$samba_cv_HAVE_POSIX_ACLS" = x"yes"; then
+ AC_MSG_RESULT(Using posix ACLs)
+ AC_DEFINE(HAVE_POSIX_ACLS,1,[Whether POSIX ACLs are available])
+ AC_CACHE_CHECK([for acl_get_perm_np],samba_cv_HAVE_ACL_GET_PERM_NP,[
+ acl_LIBS=$LIBS
+ LIBS="$LIBS -lacl"
+ AC_TRY_LINK([#include <sys/types.h>
+#include <sys/acl.h>],
+[ acl_permset_t permset_d; acl_perm_t perm; return acl_get_perm_np( permset_d, perm);],
+samba_cv_HAVE_ACL_GET_PERM_NP=yes,samba_cv_HAVE_ACL_GET_PERM_NP=no)
+ LIBS=$acl_LIBS])
+ if test x"$samba_cv_HAVE_ACL_GET_PERM_NP" = x"yes"; then
+ AC_DEFINE(HAVE_ACL_GET_PERM_NP,1,[Whether acl_get_perm_np() is available])
+ fi
+ fi
+ ;;
+ esac
+ ;;
+ *)
+ AC_MSG_RESULT(no)
+ AC_DEFINE(HAVE_NO_ACLS,1,[Whether no ACLs support is available])
+ ;;
+ esac ],
+ AC_DEFINE(HAVE_NO_ACLS,1,[Whether no ACLs support should be built in])
+ AC_MSG_RESULT(no)
+)
+
+#################################################
+# check for sendfile support
+
+with_sendfile_support=yes
+AC_MSG_CHECKING(whether to check to support sendfile)
+AC_ARG_WITH(sendfile-support,
+[ --with-sendfile-support Check for sendfile support (default=yes)],
+[ case "$withval" in
+ yes)
+
+ AC_MSG_RESULT(yes);
+
+ case "$host_os" in
+ *linux*)
+ AC_CACHE_CHECK([for linux sendfile64 support],samba_cv_HAVE_SENDFILE64,[
+ AC_TRY_LINK([#include <sys/sendfile.h>],
+[\
+int tofd, fromfd;
+off64_t offset;
+size_t total;
+ssize_t nwritten = sendfile64(tofd, fromfd, &offset, total);
+],
+samba_cv_HAVE_SENDFILE64=yes,samba_cv_HAVE_SENDFILE64=no)])
+
+ AC_CACHE_CHECK([for linux sendfile support],samba_cv_HAVE_SENDFILE,[
+ AC_TRY_LINK([#include <sys/sendfile.h>],
+[\
+int tofd, fromfd;
+off_t offset;
+size_t total;
+ssize_t nwritten = sendfile(tofd, fromfd, &offset, total);
+],
+samba_cv_HAVE_SENDFILE=yes,samba_cv_HAVE_SENDFILE=no)])
+
+# Try and cope with broken Linux sendfile....
+ AC_CACHE_CHECK([for broken linux sendfile support],samba_cv_HAVE_BROKEN_LINUX_SENDFILE,[
+ AC_TRY_LINK([\
+#if defined(_FILE_OFFSET_BITS) && (_FILE_OFFSET_BITS == 64)
+#undef _FILE_OFFSET_BITS
+#endif
+#include <sys/sendfile.h>],
+[\
+int tofd, fromfd;
+off_t offset;
+size_t total;
+ssize_t nwritten = sendfile(tofd, fromfd, &offset, total);
+],
+samba_cv_HAVE_BROKEN_LINUX_SENDFILE=yes,samba_cv_HAVE_BROKEN_LINUX_SENDFILE=no)])
+
+ if test x"$samba_cv_HAVE_SENDFILE64" = x"yes"; then
+ AC_DEFINE(HAVE_SENDFILE64,1,[Whether 64-bit sendfile() is available])
+ AC_DEFINE(LINUX_SENDFILE_API,1,[Whether linux sendfile() API is available])
+ AC_DEFINE(WITH_SENDFILE,1,[Whether sendfile() should be used])
+ elif test x"$samba_cv_HAVE_SENDFILE" = x"yes"; then
+ AC_DEFINE(HAVE_SENDFILE,1,[Whether sendfile() is available])
+ AC_DEFINE(LINUX_SENDFILE_API,1,[Whether linux sendfile() API is available])
+ AC_DEFINE(WITH_SENDFILE,1,[Whether sendfile() should be used])
+ elif test x"$samba_cv_HAVE_BROKEN_LINUX_SENDFILE" = x"yes"; then
+ AC_DEFINE(LINUX_BROKEN_SENDFILE_API,1,[Whether (linux) sendfile() is broken])
+ AC_DEFINE(WITH_SENDFILE,1,[Whether sendfile should be used])
+ else
+ AC_MSG_RESULT(no);
+ fi
+
+ ;;
+ *freebsd*)
+ AC_CACHE_CHECK([for freebsd sendfile support],samba_cv_HAVE_SENDFILE,[
+ AC_TRY_LINK([\
+#include <sys/types.h>
+#include <unistd.h>
+#include <sys/socket.h>
+#include <sys/uio.h>],
+[\
+ int fromfd, tofd, ret, total=0;
+ off_t offset, nwritten;
+ struct sf_hdtr hdr;
+ struct iovec hdtrl;
+ hdr.headers = &hdtrl;
+ hdr.hdr_cnt = 1;
+ hdr.trailers = NULL;
+ hdr.trl_cnt = 0;
+ hdtrl.iov_base = NULL;
+ hdtrl.iov_len = 0;
+ ret = sendfile(fromfd, tofd, offset, total, &hdr, &nwritten, 0);
+],
+samba_cv_HAVE_SENDFILE=yes,samba_cv_HAVE_SENDFILE=no)])
+
+ if test x"$samba_cv_HAVE_SENDFILE" = x"yes"; then
+ AC_DEFINE(HAVE_SENDFILE,1,[Whether sendfile() support is available])
+ AC_DEFINE(FREEBSD_SENDFILE_API,1,[Whether the FreeBSD sendfile() API is available])
+ AC_DEFINE(WITH_SENDFILE,1,[Whether sendfile() support should be included])
+ else
+ AC_MSG_RESULT(no);
+ fi
+ ;;
+
+ *hpux*)
+ AC_CACHE_CHECK([for hpux sendfile64 support],samba_cv_HAVE_SENDFILE64,[
+ AC_TRY_LINK([\
+#include <sys/socket.h>
+#include <sys/uio.h>],
+[\
+ int fromfd, tofd;
+ size_t total=0;
+ struct iovec hdtrl[2];
+ ssize_t nwritten;
+ off64_t offset;
+
+ hdtrl[0].iov_base = 0;
+ hdtrl[0].iov_len = 0;
+
+ nwritten = sendfile64(tofd, fromfd, offset, total, &hdtrl[0], 0);
+],
+samba_cv_HAVE_SENDFILE64=yes,samba_cv_HAVE_SENDFILE64=no)])
+ if test x"$samba_cv_HAVE_SENDFILE64" = x"yes"; then
+ AC_DEFINE(HAVE_SENDFILE64,1,[Whether sendfile64() is available])
+ AC_DEFINE(HPUX_SENDFILE_API,1,[Whether the hpux sendfile() API is available])
+ AC_DEFINE(WITH_SENDFILE,1,[Whether sendfile() support should be included])
+ else
+ AC_MSG_RESULT(no);
+ fi
+
+ AC_CACHE_CHECK([for hpux sendfile support],samba_cv_HAVE_SENDFILE,[
+ AC_TRY_LINK([\
+#include <sys/socket.h>
+#include <sys/uio.h>],
+[\
+ int fromfd, tofd;
+ size_t total=0;
+ struct iovec hdtrl[2];
+ ssize_t nwritten;
+ off_t offset;
+
+ hdtrl[0].iov_base = 0;
+ hdtrl[0].iov_len = 0;
+
+ nwritten = sendfile(tofd, fromfd, offset, total, &hdtrl[0], 0);
+],
+samba_cv_HAVE_SENDFILE=yes,samba_cv_HAVE_SENDFILE=no)])
+ if test x"$samba_cv_HAVE_SENDFILE" = x"yes"; then
+ AC_DEFINE(HAVE_SENDFILE,1,[Whether sendfile() is available])
+ AC_DEFINE(HPUX_SENDFILE_API,1,[Whether the hpux sendfile() API is available])
+ AC_DEFINE(WITH_SENDFILE,1,[Whether sendfile() support should be included])
+ else
+ AC_MSG_RESULT(no);
+ fi
+ ;;
+
+ *solaris*)
+ AC_CHECK_LIB(sendfile,sendfilev)
+ AC_CACHE_CHECK([for solaris sendfilev64 support],samba_cv_HAVE_SENDFILEV64,[
+ AC_TRY_LINK([\
+#include <sys/sendfile.h>],
+[\
+ int sfvcnt;
+ size_t xferred;
+ struct sendfilevec vec[2];
+ ssize_t nwritten;
+ int tofd;
+
+ sfvcnt = 2;
+
+ vec[0].sfv_fd = SFV_FD_SELF;
+ vec[0].sfv_flag = 0;
+ vec[0].sfv_off = 0;
+ vec[0].sfv_len = 0;
+
+ vec[1].sfv_fd = 0;
+ vec[1].sfv_flag = 0;
+ vec[1].sfv_off = 0;
+ vec[1].sfv_len = 0;
+ nwritten = sendfilev64(tofd, vec, sfvcnt, &xferred);
+],
+samba_cv_HAVE_SENDFILEV64=yes,samba_cv_HAVE_SENDFILEV64=no)])
+
+ if test x"$samba_cv_HAVE_SENDFILEV64" = x"yes"; then
+ AC_DEFINE(HAVE_SENDFILEV64,1,[Whether sendfilev64() is available])
+ AC_DEFINE(SOLARIS_SENDFILE_API,1,[Whether the soloris sendfile() API is available])
+ AC_DEFINE(WITH_SENDFILE,1,[Whether sendfile() support should be included])
+ else
+ AC_MSG_RESULT(no);
+ fi
+
+ AC_CACHE_CHECK([for solaris sendfilev support],samba_cv_HAVE_SENDFILEV,[
+ AC_TRY_LINK([\
+#include <sys/sendfile.h>],
+[\
+ int sfvcnt;
+ size_t xferred;
+ struct sendfilevec vec[2];
+ ssize_t nwritten;
+ int tofd;
+
+ sfvcnt = 2;
+
+ vec[0].sfv_fd = SFV_FD_SELF;
+ vec[0].sfv_flag = 0;
+ vec[0].sfv_off = 0;
+ vec[0].sfv_len = 0;
+
+ vec[1].sfv_fd = 0;
+ vec[1].sfv_flag = 0;
+ vec[1].sfv_off = 0;
+ vec[1].sfv_len = 0;
+ nwritten = sendfilev(tofd, vec, sfvcnt, &xferred);
+],
+samba_cv_HAVE_SENDFILEV=yes,samba_cv_HAVE_SENDFILEV=no)])
+
+ if test x"$samba_cv_HAVE_SENDFILEV" = x"yes"; then
+ AC_DEFINE(HAVE_SENDFILEV,1,[Whether sendfilev() is available])
+ AC_DEFINE(SOLARIS_SENDFILE_API,1,[Whether the solaris sendfile() API is available])
+ AC_DEFINE(WITH_SENDFILE,1,[Whether to include sendfile() support])
+ else
+ AC_MSG_RESULT(no);
+ fi
+ ;;
+
+ *)
+ ;;
+ esac
+ ;;
+ *)
+ AC_MSG_RESULT(no)
+ ;;
+ esac ],
+ AC_MSG_RESULT(yes)
+)
+
+
+#################################################
+# Check whether winbind is supported on this platform. If so we need to
+# build and install client programs, sbin programs and shared libraries
+
+AC_MSG_CHECKING(whether to build winbind)
+
+# Initially, the value of $host_os decides whether winbind is supported
+
+HAVE_WINBIND=yes
+
+# Define the winbind shared library name and any specific linker flags
+# it needs to be built with.
+
+WINBIND_NSS="nsswitch/libnss_winbind.$SHLIBEXT"
+WINBIND_WINS_NSS="nsswitch/libnss_wins.$SHLIBEXT"
+WINBIND_NSS_LDSHFLAGS=$LDSHFLAGS
+
+case "$host_os" in
+ *linux*)
+ WINBIND_NSS_EXTRA_OBJS="nsswitch/winbind_nss_linux.o"
+ ;;
+ *freebsd5*)
+ # FreeBSD winbind client is implemented as a wrapper around
+ # the Linux version.
+ WINBIND_NSS_EXTRA_OBJS="nsswitch/winbind_nss_freebsd.o \
+ nsswitch/winbind_nss_linux.o"
+ ;;
+ *irix*)
+ # IRIX has differently named shared libraries
+ WINBIND_NSS_EXTRA_OBJS="nsswitch/winbind_nss_irix.o"
+ WINBIND_NSS="nsswitch/libns_winbind.$SHLIBEXT"
+ WINBIND_WINS_NSS="nsswitch/libns_wins.$SHLIBEXT"
+ ;;
+ *solaris*)
+ # Solaris winbind client is implemented as a wrapper around
+ # the Linux version.
+ WINBIND_NSS_EXTRA_OBJS="nsswitch/winbind_nss_solaris.o \
+ nsswitch/winbind_nss_linux.o"
+ WINBIND_NSS_EXTRA_LIBS="-lsocket"
+ ;;
+ *hpux11*)
+ WINBIND_NSS_EXTRA_OBJS="nsswitch/winbind_nss_solaris.o"
+ ;;
+ *aix*)
+ # AIX has even differently named shared libraries. No
+ # WINS support has been implemented yet.
+ WINBIND_NSS_EXTRA_OBJS="nsswitch/winbind_nss_aix.o"
+ WINBIND_NSS_LDSHFLAGS="-Wl,-bexpall,-bM:SRE,-ewb_aix_init"
+ WINBIND_NSS="nsswitch/WINBIND"
+ WINBIND_WINS_NSS=""
+ ;;
+ *)
+ HAVE_WINBIND=no
+ winbind_no_reason=", unsupported on $host_os"
+ ;;
+esac
+
+AC_SUBST(WINBIND_NSS)
+AC_SUBST(WINBIND_WINS_NSS)
+AC_SUBST(WINBIND_NSS_LDSHFLAGS)
+AC_SUBST(WINBIND_NSS_EXTRA_OBJS)
+AC_SUBST(WINBIND_NSS_EXTRA_LIBS)
+
+# Check the setting of --with-winbindd
+
+AC_ARG_WITH(winbind,
+[ --with-winbind Build winbind (default, if supported by OS)],
+[
+ case "$withval" in
+ yes)
+ HAVE_WINBIND=yes
+ ;;
+ no)
+ HAVE_WINBIND=no
+ winbind_reason=""
+ ;;
+ esac ],
+)
+
+# We need unix domain sockets for winbind
+
+if test x"$HAVE_WINBIND" = x"yes"; then
+ if test x"$samba_cv_unixsocket" = x"no"; then
+ winbind_no_reason=", no unix domain socket support on $host_os"
+ HAVE_WINBIND=no
+ fi
+fi
+
+# Display test results
+
+if test x"$HAVE_WINBIND" = x"yes"; then
+ AC_MSG_RESULT(yes)
+ AC_DEFINE(WITH_WINBIND,1,[Whether to build winbind])
+
+ EXTRA_BIN_PROGS="$EXTRA_BIN_PROGS bin/wbinfo\$(EXEEXT)"
+ EXTRA_SBIN_PROGS="$EXTRA_SBIN_PROGS bin/winbindd\$(EXEEXT)"
+ if test x"$BLDSHARED" = x"true"; then
+ SHLIB_PROGS="$SHLIB_PROGS $WINBIND_NSS $WINBIND_WINS_NSS"
+
+ if test x"$with_pam" = x"yes"; then
+ SHLIB_PROGS="$SHLIB_PROGS nsswitch/pam_winbind.$SHLIBEXT"
+ fi
+ fi
+else
+ AC_MSG_RESULT(no$winbind_no_reason)
+fi
+
+# Solaris has some extra fields in struct passwd that need to be
+# initialised otherwise nscd crashes.
+
+AC_CHECK_MEMBER(struct passwd.pw_comment,
+ AC_DEFINE(HAVE_PASSWD_PW_COMMENT, 1, [Defined if struct passwd has pw_comment field]),,
+ [#include <pwd.h>])
+
+AC_CHECK_MEMBER(struct passwd.pw_age,
+ AC_DEFINE(HAVE_PASSWD_PW_AGE, 1, [Defined if struct passwd has pw_age field]),,
+ [#include <pwd.h>])
+
+#################################################
+# Check to see if we should use the included popt
+
+AC_ARG_WITH(included-popt,
+[ --with-included-popt use bundled popt library, not from system],
+[
+ case "$withval" in
+ yes)
+ INCLUDED_POPT=yes
+ ;;
+ no)
+ INCLUDED_POPT=no
+ ;;
+ esac ],
+)
+if test x"$INCLUDED_POPT" != x"yes"; then
+ AC_CHECK_LIB(popt, poptGetContext,
+ INCLUDED_POPT=no, INCLUDED_POPT=yes)
+fi
+
+AC_MSG_CHECKING(whether to use included popt)
+if test x"$INCLUDED_POPT" = x"yes"; then
+ AC_MSG_RESULT(yes)
+ BUILD_POPT='$(POPT_OBJS)'
+ POPTLIBS='$(POPT_OBJS)'
+ FLAGS1="-I$srcdir/popt"
+else
+ AC_MSG_RESULT(no)
+ BUILD_POPT=""
+ POPTLIBS="-lpopt"
+fi
+AC_SUBST(BUILD_POPT)
+AC_SUBST(POPTLIBS)
+AC_SUBST(FLAGS1)
+
+#################################################
+# Check if the user wants Python
+
+# At the moment, you can use this to set which Python binary to link
+# against. (Libraries built for Python2.2 can't be used by 2.1,
+# though they can coexist in different directories.) In the future
+# this might make the Python stuff be built by default.
+
+# Defaulting python breaks the clean target if python isn't installed
+
+PYTHON=
+
+AC_ARG_WITH(python,
+[ --with-python=PYTHONNAME build Python libraries],
+[ case "${withval-python}" in
+ yes)
+ PYTHON=python
+ EXTRA_ALL_TARGETS="$EXTRA_ALL_TARGETS python_ext"
+ ;;
+ no)
+ PYTHON=
+ ;;
+ *)
+ PYTHON=${withval-python}
+ ;;
+ esac ])
+AC_SUBST(PYTHON)
+
+for i in `echo $default_static_modules | sed -e's/,/ /g'`
+do
+ eval MODULE_DEFAULT_$i=STATIC
+done
+
+for i in `echo $default_shared_modules | sed -e's/,/ /g'`
+do
+ dnl Fall back to static if dlopen() is not available
+ eval MODULE_DEFAULT_$i=STATIC
+
+ if test x"$ac_cv_func_dlopen" = xyes; then
+ eval MODULE_DEFAULT_$i=SHARED
+ fi
+done
+
+dnl Always built these modules static
+MODULE_pdb_guest=STATIC
+MODULE_rpc_spoolss=STATIC
+MODULE_rpc_srv=STATIC
+MODULE_idmap_tdb=STATIC
+MODULE_gums_tdbsam2=STATIC
+
+AC_ARG_WITH(static-modules,
+[ --with-static-modules=MODULES Comma-seperated list of names of modules to statically link in],
+[ if test $withval; then
+ for i in `echo $withval | sed -e's/,/ /g'`
+ do
+ eval MODULE_$i=STATIC
+ done
+fi ])
+
+AC_ARG_WITH(shared-modules,
+[ --with-shared-modules=MODULES Comma-seperated list of names of modules to build shared],
+[ if test $withval; then
+ for i in `echo $withval | sed -e's/,/ /g'`
+ do
+ eval MODULE_$i=SHARED
+ done
+fi ])
+
+###########################################################################
+## contributed pdb_modules
+
+SMB_MODULE(pdb_xml, passdb/pdb_xml.o, "bin/xml.$SHLIBEXT", PDB,
+ [ PASSDB_LIBS="$PASSDB_LIBS $XML_LIBS" ] )
+SMB_MODULE(pdb_mysql, passdb/pdb_mysql.o, "bin/mysql.$SHLIBEXT", PDB,
+ [ PASSDB_LIBS="$PASSDB_LIBS $MYSQL_LIBS" ] )
+SMB_MODULE(pdb_pgsql, passdb/pdb_pgsql.o, "bin/pgsql.$SHLIBEXT", PDB,
+ [ PASSDB_LIBS="$PASSDB_LIBS $PGSQL_LIBS" ] )
+
+## end of contributed pdb_modules
+###########################################################################
+
+SMB_MODULE(pdb_ldap, passdb/pdb_ldap.o, "bin/ldapsam.$SHLIBEXT", PDB,
+ [ PASSDB_LIBS="$PASSDB_LIBS $LDAP_LIBS" ] )
+SMB_MODULE(pdb_smbpasswd, passdb/pdb_smbpasswd.o, "bin/smbpasswd.$SHLIBEXT", PDB)
+SMB_MODULE(pdb_tdbsam, passdb/pdb_tdb.o, "bin/tdbsam.$SHLIBEXT", PDB)
+SMB_MODULE(pdb_guest, passdb/pdb_guest.o, "bin/guest.$SHLIBEXT", PDB)
+SMB_MODULE(pdb_gums, [passdb/pdb_gums.o \$(GUMS_OBJ)], "bin/gums.$SHLIBEXT", PDB)
+SMB_SUBSYSTEM(PDB,passdb/pdb_interface.o)
+
+SMB_MODULE(gums_tdbsam2, sam/gums_tdbsam2.o, "bin/tdbsam2.$SHLIBEXT", GUMS)
+SMB_SUBSYSTEM(GUMS)
+
+SMB_MODULE(rpc_lsa, \$(RPC_LSA_OBJ), "bin/librpc_lsarpc.$SHLIBEXT", RPC)
+SMB_MODULE(rpc_reg, \$(RPC_REG_OBJ), "bin/librpc_winreg.$SHLIBEXT", RPC)
+SMB_MODULE(rpc_lsa_ds, \$(RPC_LSA_DS_OBJ), "bin/librpc_lsa_ds.$SHLIBEXT", RPC)
+SMB_MODULE(rpc_wks, \$(RPC_WKS_OBJ), "bin/librpc_wkssvc.$SHLIBEXT", RPC)
+SMB_MODULE(rpc_net, \$(RPC_NETLOG_OBJ), "bin/librpc_NETLOGON.$SHLIBEXT", RPC)
+SMB_MODULE(rpc_dfs, \$(RPC_DFS_OBJ), "bin/librpc_netdfs.$SHLIBEXT", RPC)
+SMB_MODULE(rpc_srv, \$(RPC_SVC_OBJ), "bin/librpc_srvsvc.$SHLIBEXT", RPC)
+SMB_MODULE(rpc_spoolss, \$(RPC_SPOOLSS_OBJ), "bin/librpc_spoolss.$SHLIBEXT", RPC)
+SMB_MODULE(rpc_samr, \$(RPC_SAMR_OBJ), "bin/librpc_samr.$SHLIBEXT", RPC)
+SMB_MODULE(rpc_echo, \$(RPC_ECHO_OBJ), "bin/librpc_echo.$SHLIBEXT", RPC)
+SMB_MODULE(rpc_epmapper, \$(RPC_EPMAPPER_OBJ), "bin/librpc_epmapper.$SHLIBEXT",
+ RPC)
+SMB_SUBSYSTEM(RPC,smbd/server.o)
+
+SMB_MODULE(idmap_ldap, sam/idmap_ldap.o, "bin/idmap_ldap.$SHLIBEXT", IDMAP)
+SMB_MODULE(idmap_tdb, sam/idmap_tdb.o, "bin/idmap_tdb.$SHLIBEXT", IDMAP)
+SMB_SUBSYSTEM(IDMAP,sam/idmap.o)
+
+SMB_MODULE(charset_weird, modules/weird.o, "bin/weird.$SHLIBEXT", CHARSET)
+SMB_MODULE(charset_CP850, modules/CP850.o, "bin/CP850.$SHLIBEXT", CHARSET)
+SMB_MODULE(charset_CP437, modules/CP437.o, "bin/CP437.$SHLIBEXT", CHARSET)
+SMB_MODULE(charset_macosxfs, modules/charset_macosxfs.o,"bin/macosxfs.$SHLIBEXT", CHARSET)
+SMB_SUBSYSTEM(CHARSET,lib/iconv.o)
+
+SMB_MODULE(auth_rhosts, \$(AUTH_RHOSTS_OBJ), "bin/rhosts.$SHLIBEXT", AUTH)
+SMB_MODULE(auth_sam, \$(AUTH_SAM_OBJ), "bin/sam.$SHLIBEXT", AUTH)
+SMB_MODULE(auth_unix, \$(AUTH_UNIX_OBJ), "bin/unix.$SHLIBEXT", AUTH)
+SMB_MODULE(auth_winbind, \$(AUTH_WINBIND_OBJ), "bin/winbind.$SHLIBEXT", AUTH)
+SMB_MODULE(auth_server, \$(AUTH_SERVER_OBJ), "bin/smbserver.$SHLIBEXT", AUTH)
+SMB_MODULE(auth_domain, \$(AUTH_DOMAIN_OBJ), "bin/domain.$SHLIBEXT", AUTH)
+SMB_MODULE(auth_builtin, \$(AUTH_BUILTIN_OBJ), "bin/builtin.$SHLIBEXT", AUTH)
+SMB_SUBSYSTEM(AUTH,auth/auth.o)
+
+SMB_MODULE(vfs_recycle, \$(VFS_RECYCLE_OBJ), "bin/recycle.$SHLIBEXT", VFS)
+SMB_MODULE(vfs_audit, \$(VFS_AUDIT_OBJ), "bin/audit.$SHLIBEXT", VFS)
+SMB_MODULE(vfs_extd_audit, \$(VFS_EXTD_AUDIT_OBJ), "bin/extd_audit.$SHLIBEXT", VFS)
+SMB_MODULE(vfs_netatalk, \$(VFS_NETATALK_OBJ), "bin/netatalk.$SHLIBEXT", VFS)
+SMB_MODULE(vfs_fake_perms, \$(VFS_FAKE_PERMS_OBJ), "bin/fake_perms.$SHLIBEXT", VFS)
+SMB_MODULE(vfs_default_quota, \$(VFS_DEFAULT_QUOTA_OBJ), "bin/default_quota.$SHLIBEXT", VFS)
+SMB_MODULE(vfs_readonly, \$(VFS_READONLY_OBJ), "bin/readonly.$SHLIBEXT", VFS)
+SMB_MODULE(vfs_cap, \$(VFS_CAP_OBJ), "bin/cap.$SHLIBEXT", VFS)
+SMB_MODULE(vfs_expand_msdfs, \$(VFS_EXPAND_MSDFS_OBJ), "bin/expand_msdfs.$SHLIBEXT", VFS)
+SMB_SUBSYSTEM(VFS,smbd/vfs.o)
+
+SMB_MODULE(config_ldap, param/config_ldap.o, "bin/config_ldap.$SHLIBEXT", CONFIG, [ CONFIG_LIBS="$CONFIG_LIBS $LDAP_LIBS" "$SMBLDAP" ])
+SMB_SUBSYSTEM(CONFIG, param/modconf.o)
+
+AC_DEFINE_UNQUOTED(STRING_STATIC_MODULES, "$string_static_modules", [String list of builtin modules])
+
+#################################################
+# do extra things if we are running insure
+
+if test "${ac_cv_prog_CC}" = "insure"; then
+ CPPFLAGS="$CPPFLAGS -D__INSURE__"
+fi
+
+#################################################
+# Display summary of libraries detected
+
+AC_MSG_RESULT([Using libraries:])
+AC_MSG_RESULT([ LIBS = $LIBS])
+if test x"$with_ads_support" != x"no"; then
+ AC_MSG_RESULT([ KRB5_LIBS = $KRB5_LIBS])
+fi
+if test x"$with_ldap_support" != x"no"; then
+ AC_MSG_RESULT([ LDAP_LIBS = $LDAP_LIBS])
+fi
+AC_MSG_RESULT([ AUTH_LIBS = $AUTH_LIBS])
+
+#################################################
+# 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]))
+
+builddir=`pwd`
+AC_SUBST(builddir)
+
+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_OUTPUT(include/stamp-h Makefile script/findsmb smbadduser script/gen-8bit-gap.sh)
+
+#################################################
+# Print very concise instructions on building/use
+if test "x$enable_dmalloc" = xyes
+then
+ AC_MSG_RESULT([Note: The dmalloc debug library will be included. To turn it on use])
+ AC_MSG_RESULT([ \$ eval \`dmalloc samba\`.])
+fi
diff --git a/source/dynconfig.c b/source/dynconfig.c
new file mode 100644
index 00000000000..34c716926cc
--- /dev/null
+++ b/source/dynconfig.c
@@ -0,0 +1,72 @@
+/*
+ Unix SMB/CIFS implementation.
+ Copyright (C) 2001 by Martin Pool <mbp@samba.org>
+ Copyright (C) 2003 by Jim McDonough <jmcd@us.ibm.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 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"
+
+/**
+ * @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...
+ **/
+
+char const *dyn_SBINDIR = SBINDIR,
+ *dyn_BINDIR = BINDIR,
+ *dyn_SWATDIR = SWATDIR;
+
+pstring dyn_CONFIGFILE = CONFIGFILE; /**< Location of smb.conf file. **/
+
+/** Log file directory. **/
+pstring dyn_LOGFILEBASE = LOGFILEBASE;
+
+/** Statically configured LanMan hosts. **/
+pstring dyn_LMHOSTSFILE = LMHOSTSFILE;
+
+/**
+ * @brief Samba library directory.
+ *
+ * @sa lib_path() to get the path to a file inside the LIBDIR.
+ **/
+pstring dyn_LIBDIR = LIBDIR;
+const fstring dyn_SHLIBEXT = SHLIBEXT;
+
+/**
+ * @brief Directory holding lock files.
+ *
+ * Not writable, but used to set a default in the parameter table.
+ **/
+const pstring dyn_LOCKDIR = LOCKDIR;
+const pstring dyn_PIDDIR = PIDDIR;
+
+const pstring dyn_SMB_PASSWD_FILE = SMB_PASSWD_FILE;
+const pstring dyn_PRIVATE_DIR = PRIVATE_DIR;
diff --git a/source/groupdb/.cvsignore b/source/groupdb/.cvsignore
new file mode 100644
index 00000000000..5f2a5c4cf75
--- /dev/null
+++ b/source/groupdb/.cvsignore
@@ -0,0 +1,2 @@
+*.po
+*.po32
diff --git a/source/groupdb/mapping.c b/source/groupdb/mapping.c
new file mode 100644
index 00000000000..d476f5cac13
--- /dev/null
+++ b/source/groupdb/mapping.c
@@ -0,0 +1,1438 @@
+/*
+ * Unix SMB/CIFS implementation.
+ * RPC Pipe client / server routines
+ * Copyright (C) Andrew Tridgell 1992-2000,
+ * Copyright (C) Jean François Micouleau 1998-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 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"
+
+static TDB_CONTEXT *tdb; /* used for driver files */
+
+#define DATABASE_VERSION_V1 1 /* native byte format. */
+#define DATABASE_VERSION_V2 2 /* le format. */
+
+#define 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.
+ */
+#define MEMBEROF_PREFIX "MEMBEROF/"
+
+/****************************************************************************
+dump the mapping group mapping to a text file
+****************************************************************************/
+char *decode_sid_name_use(fstring group, enum SID_NAME_USE name_use)
+{
+ static fstring group_type;
+
+ switch(name_use) {
+ case SID_NAME_USER:
+ fstrcpy(group_type,"User");
+ break;
+ case SID_NAME_DOM_GRP:
+ fstrcpy(group_type,"Domain group");
+ break;
+ case SID_NAME_DOMAIN:
+ fstrcpy(group_type,"Domain");
+ break;
+ case SID_NAME_ALIAS:
+ fstrcpy(group_type,"Local group");
+ break;
+ case SID_NAME_WKN_GRP:
+ fstrcpy(group_type,"Builtin group");
+ break;
+ case SID_NAME_DELETED:
+ fstrcpy(group_type,"Deleted");
+ break;
+ case SID_NAME_INVALID:
+ fstrcpy(group_type,"Invalid");
+ break;
+ case SID_NAME_UNKNOWN:
+ default:
+ fstrcpy(group_type,"Unknown type");
+ break;
+ }
+
+ fstrcpy(group, group_type);
+ return group_type;
+}
+
+/****************************************************************************
+initialise first time the mapping list - called from init_group_mapping()
+****************************************************************************/
+static BOOL default_group_mapping(void)
+{
+ DOM_SID sid_admins;
+ DOM_SID sid_users;
+ DOM_SID sid_guests;
+ fstring str_admins;
+ fstring str_users;
+ fstring str_guests;
+
+ /* Add the Wellknown groups */
+
+ add_initial_entry(-1, "S-1-5-32-544", SID_NAME_WKN_GRP, "Administrators", "");
+ add_initial_entry(-1, "S-1-5-32-545", SID_NAME_WKN_GRP, "Users", "");
+ add_initial_entry(-1, "S-1-5-32-546", SID_NAME_WKN_GRP, "Guests", "");
+ add_initial_entry(-1, "S-1-5-32-547", SID_NAME_WKN_GRP, "Power Users", "");
+ add_initial_entry(-1, "S-1-5-32-548", SID_NAME_WKN_GRP, "Account Operators", "");
+ add_initial_entry(-1, "S-1-5-32-549", SID_NAME_WKN_GRP, "System Operators", "");
+ add_initial_entry(-1, "S-1-5-32-550", SID_NAME_WKN_GRP, "Print Operators", "");
+ add_initial_entry(-1, "S-1-5-32-551", SID_NAME_WKN_GRP, "Backup Operators", "");
+ add_initial_entry(-1, "S-1-5-32-552", SID_NAME_WKN_GRP, "Replicators", "");
+
+ /* Add the defaults domain groups */
+
+ sid_copy(&sid_admins, get_global_sam_sid());
+ sid_append_rid(&sid_admins, DOMAIN_GROUP_RID_ADMINS);
+ sid_to_string(str_admins, &sid_admins);
+ add_initial_entry(-1, str_admins, SID_NAME_DOM_GRP, "Domain Admins", "");
+
+ sid_copy(&sid_users, get_global_sam_sid());
+ sid_append_rid(&sid_users, DOMAIN_GROUP_RID_USERS);
+ sid_to_string(str_users, &sid_users);
+ add_initial_entry(-1, str_users, SID_NAME_DOM_GRP, "Domain Users", "");
+
+ sid_copy(&sid_guests, get_global_sam_sid());
+ sid_append_rid(&sid_guests, DOMAIN_GROUP_RID_GUESTS);
+ sid_to_string(str_guests, &sid_guests);
+ add_initial_entry(-1, str_guests, SID_NAME_DOM_GRP, "Domain Guests", "");
+
+ return True;
+}
+
+/****************************************************************************
+ Open the group mapping tdb.
+****************************************************************************/
+
+static BOOL init_group_mapping(void)
+{
+ static pid_t local_pid;
+ const char *vstring = "INFO/version";
+ int32 vers_id;
+
+ if (tdb && local_pid == sys_getpid())
+ return True;
+ tdb = tdb_open_log(lock_path("group_mapping.tdb"), 0, TDB_DEFAULT, O_RDWR|O_CREAT, 0600);
+ if (!tdb) {
+ DEBUG(0,("Failed to open group mapping database\n"));
+ return False;
+ }
+
+ local_pid = sys_getpid();
+
+ /* handle a Samba upgrade */
+ tdb_lock_bystring(tdb, vstring, 0);
+
+ /* Cope with byte-reversed older versions of the db. */
+ vers_id = tdb_fetch_int32(tdb, vstring);
+ if ((vers_id == DATABASE_VERSION_V1) || (IREV(vers_id) == DATABASE_VERSION_V1)) {
+ /* Written on a bigendian machine with old fetch_int code. Save as le. */
+ tdb_store_int32(tdb, vstring, DATABASE_VERSION_V2);
+ vers_id = DATABASE_VERSION_V2;
+ }
+
+ if (vers_id != DATABASE_VERSION_V2) {
+ tdb_traverse(tdb, tdb_traverse_delete_fn, NULL);
+ tdb_store_int32(tdb, vstring, DATABASE_VERSION_V2);
+ }
+
+ tdb_unlock_bystring(tdb, vstring);
+
+ /* write a list of default groups */
+ if(!default_group_mapping())
+ return False;
+
+ return True;
+}
+
+/****************************************************************************
+****************************************************************************/
+static BOOL add_mapping_entry(GROUP_MAP *map, int flag)
+{
+ TDB_DATA kbuf, dbuf;
+ pstring key, buf;
+ fstring string_sid="";
+ int len;
+
+ if(!init_group_mapping()) {
+ DEBUG(0,("failed to initialize group mapping\n"));
+ return(False);
+ }
+
+ sid_to_string(string_sid, &map->sid);
+
+ len = tdb_pack(buf, sizeof(buf), "ddff",
+ map->gid, map->sid_name_use, map->nt_name, map->comment);
+
+ if (len > sizeof(buf))
+ return False;
+
+ slprintf(key, sizeof(key), "%s%s", GROUP_PREFIX, string_sid);
+
+ kbuf.dsize = strlen(key)+1;
+ kbuf.dptr = key;
+ dbuf.dsize = len;
+ dbuf.dptr = buf;
+ if (tdb_store(tdb, kbuf, dbuf, flag) != 0) return False;
+
+ return True;
+}
+
+/****************************************************************************
+initialise first time the mapping list
+****************************************************************************/
+BOOL add_initial_entry(gid_t gid, const char *sid, enum SID_NAME_USE sid_name_use, const char *nt_name, const char *comment)
+{
+ GROUP_MAP map;
+
+ if(!init_group_mapping()) {
+ DEBUG(0,("failed to initialize group mapping\n"));
+ return(False);
+ }
+
+ map.gid=gid;
+ if (!string_to_sid(&map.sid, sid)) {
+ DEBUG(0, ("string_to_sid failed: %s", sid));
+ return False;
+ }
+
+ map.sid_name_use=sid_name_use;
+ fstrcpy(map.nt_name, nt_name);
+ fstrcpy(map.comment, comment);
+
+ return pdb_add_group_mapping_entry(&map);
+}
+
+/****************************************************************************
+ Return the sid and the type of the unix group.
+****************************************************************************/
+
+static BOOL get_group_map_from_sid(DOM_SID sid, GROUP_MAP *map)
+{
+ TDB_DATA kbuf, dbuf;
+ pstring key;
+ fstring string_sid;
+ int ret = 0;
+
+ if(!init_group_mapping()) {
+ DEBUG(0,("failed to initialize group mapping\n"));
+ return(False);
+ }
+
+ /* the key is the SID, retrieving is direct */
+
+ sid_to_string(string_sid, &sid);
+ slprintf(key, sizeof(key), "%s%s", GROUP_PREFIX, string_sid);
+
+ kbuf.dptr = key;
+ kbuf.dsize = strlen(key)+1;
+
+ dbuf = tdb_fetch(tdb, kbuf);
+ if (!dbuf.dptr)
+ return False;
+
+ ret = tdb_unpack(dbuf.dptr, dbuf.dsize, "ddff",
+ &map->gid, &map->sid_name_use, &map->nt_name, &map->comment);
+
+ SAFE_FREE(dbuf.dptr);
+
+ if ( ret == -1 ) {
+ DEBUG(3,("get_group_map_from_sid: tdb_unpack failure\n"));
+ return False;
+ }
+
+ sid_copy(&map->sid, &sid);
+
+ return True;
+}
+
+/****************************************************************************
+ Return the sid and the type of the unix group.
+****************************************************************************/
+
+static BOOL get_group_map_from_gid(gid_t gid, GROUP_MAP *map)
+{
+ TDB_DATA kbuf, dbuf, newkey;
+ fstring string_sid;
+ int ret;
+
+ if(!init_group_mapping()) {
+ DEBUG(0,("failed to initialize group mapping\n"));
+ return(False);
+ }
+
+ /* we need to enumerate the TDB to find the GID */
+
+ for (kbuf = tdb_firstkey(tdb);
+ kbuf.dptr;
+ newkey = tdb_nextkey(tdb, kbuf), safe_free(kbuf.dptr), kbuf=newkey) {
+
+ if (strncmp(kbuf.dptr, GROUP_PREFIX, strlen(GROUP_PREFIX)) != 0) continue;
+
+ dbuf = tdb_fetch(tdb, kbuf);
+ if (!dbuf.dptr)
+ continue;
+
+ fstrcpy(string_sid, kbuf.dptr+strlen(GROUP_PREFIX));
+
+ string_to_sid(&map->sid, string_sid);
+
+ ret = tdb_unpack(dbuf.dptr, dbuf.dsize, "ddff",
+ &map->gid, &map->sid_name_use, &map->nt_name, &map->comment);
+
+ SAFE_FREE(dbuf.dptr);
+
+ if ( ret == -1 ) {
+ DEBUG(3,("get_group_map_from_gid: tdb_unpack failure\n"));
+ return False;
+ }
+
+ if (gid==map->gid) {
+ SAFE_FREE(kbuf.dptr);
+ return True;
+ }
+ }
+
+ return False;
+}
+
+/****************************************************************************
+ Return the sid and the type of the unix group.
+****************************************************************************/
+
+static BOOL get_group_map_from_ntname(const char *name, GROUP_MAP *map)
+{
+ TDB_DATA kbuf, dbuf, newkey;
+ fstring string_sid;
+ int ret;
+
+ if(!init_group_mapping()) {
+ DEBUG(0,("get_group_map_from_ntname:failed to initialize group mapping\n"));
+ return(False);
+ }
+
+ /* we need to enumerate the TDB to find the name */
+
+ for (kbuf = tdb_firstkey(tdb);
+ kbuf.dptr;
+ newkey = tdb_nextkey(tdb, kbuf), safe_free(kbuf.dptr), kbuf=newkey) {
+
+ if (strncmp(kbuf.dptr, GROUP_PREFIX, strlen(GROUP_PREFIX)) != 0) continue;
+
+ dbuf = tdb_fetch(tdb, kbuf);
+ if (!dbuf.dptr)
+ continue;
+
+ fstrcpy(string_sid, kbuf.dptr+strlen(GROUP_PREFIX));
+
+ string_to_sid(&map->sid, string_sid);
+
+ ret = tdb_unpack(dbuf.dptr, dbuf.dsize, "ddff",
+ &map->gid, &map->sid_name_use, &map->nt_name, &map->comment);
+
+ SAFE_FREE(dbuf.dptr);
+
+ if ( ret == -1 ) {
+ DEBUG(3,("get_group_map_from_ntname: tdb_unpack failure\n"));
+ return False;
+ }
+
+ if (StrCaseCmp(name, map->nt_name)==0) {
+ SAFE_FREE(kbuf.dptr);
+ return True;
+ }
+ }
+
+ return False;
+}
+
+/****************************************************************************
+ Remove a group mapping entry.
+****************************************************************************/
+
+static BOOL group_map_remove(const DOM_SID *sid)
+{
+ TDB_DATA kbuf, dbuf;
+ pstring key;
+ fstring string_sid;
+
+ if(!init_group_mapping()) {
+ DEBUG(0,("failed to initialize group mapping\n"));
+ return(False);
+ }
+
+ /* the key is the SID, retrieving is direct */
+
+ sid_to_string(string_sid, sid);
+ slprintf(key, sizeof(key), "%s%s", GROUP_PREFIX, string_sid);
+
+ kbuf.dptr = key;
+ kbuf.dsize = strlen(key)+1;
+
+ dbuf = tdb_fetch(tdb, kbuf);
+ if (!dbuf.dptr)
+ return False;
+
+ SAFE_FREE(dbuf.dptr);
+
+ if(tdb_delete(tdb, kbuf) != TDB_SUCCESS)
+ return False;
+
+ return True;
+}
+
+/****************************************************************************
+ Enumerate the group mapping.
+****************************************************************************/
+
+static BOOL enum_group_mapping(enum SID_NAME_USE sid_name_use, GROUP_MAP **rmap,
+ int *num_entries, BOOL unix_only)
+{
+ TDB_DATA kbuf, dbuf, newkey;
+ fstring string_sid;
+ fstring group_type;
+ GROUP_MAP map;
+ GROUP_MAP *mapt;
+ int ret;
+ int entries=0;
+
+ if(!init_group_mapping()) {
+ DEBUG(0,("failed to initialize group mapping\n"));
+ return(False);
+ }
+
+ *num_entries=0;
+ *rmap=NULL;
+
+ for (kbuf = tdb_firstkey(tdb);
+ kbuf.dptr;
+ newkey = tdb_nextkey(tdb, kbuf), safe_free(kbuf.dptr), kbuf=newkey) {
+
+ if (strncmp(kbuf.dptr, GROUP_PREFIX, strlen(GROUP_PREFIX)) != 0)
+ continue;
+
+ dbuf = tdb_fetch(tdb, kbuf);
+ if (!dbuf.dptr)
+ continue;
+
+ fstrcpy(string_sid, kbuf.dptr+strlen(GROUP_PREFIX));
+
+ ret = tdb_unpack(dbuf.dptr, dbuf.dsize, "ddff",
+ &map.gid, &map.sid_name_use, &map.nt_name, &map.comment);
+
+ SAFE_FREE(dbuf.dptr);
+
+ if ( ret == -1 ) {
+ DEBUG(3,("enum_group_mapping: tdb_unpack failure\n"));
+ continue;
+ }
+
+ /* list only the type or everything if UNKNOWN */
+ if (sid_name_use!=SID_NAME_UNKNOWN && sid_name_use!=map.sid_name_use) {
+ DEBUG(11,("enum_group_mapping: group %s is not of the requested type\n", map.nt_name));
+ continue;
+ }
+
+ if (unix_only==ENUM_ONLY_MAPPED && map.gid==-1) {
+ DEBUG(11,("enum_group_mapping: group %s is non mapped\n", map.nt_name));
+ continue;
+ }
+
+ string_to_sid(&map.sid, string_sid);
+
+ decode_sid_name_use(group_type, map.sid_name_use);
+ DEBUG(11,("enum_group_mapping: returning group %s of type %s\n", map.nt_name ,group_type));
+
+ mapt=(GROUP_MAP *)Realloc((*rmap), (entries+1)*sizeof(GROUP_MAP));
+ if (!mapt) {
+ DEBUG(0,("enum_group_mapping: Unable to enlarge group map!\n"));
+ SAFE_FREE(*rmap);
+ return False;
+ }
+ else
+ (*rmap) = mapt;
+
+ mapt[entries].gid = map.gid;
+ sid_copy( &mapt[entries].sid, &map.sid);
+ mapt[entries].sid_name_use = map.sid_name_use;
+ fstrcpy(mapt[entries].nt_name, map.nt_name);
+ fstrcpy(mapt[entries].comment, map.comment);
+
+ entries++;
+
+ }
+
+ *num_entries=entries;
+
+ return True;
+}
+
+/* This operation happens on session setup, so it should better be fast. We
+ * store a list of aliases a SID is member of hanging off MEMBEROF/SID. */
+
+static NTSTATUS alias_memberships(const DOM_SID *sid, DOM_SID **sids, int *num)
+{
+ fstring key, string_sid;
+ TDB_DATA kbuf, dbuf;
+ const char *p;
+
+ *num = 0;
+ *sids = NULL;
+
+ if (!init_group_mapping()) {
+ DEBUG(0,("failed to initialize group mapping\n"));
+ return NT_STATUS_ACCESS_DENIED;
+ }
+
+ sid_to_string(string_sid, sid);
+ slprintf(key, sizeof(key), "%s%s", MEMBEROF_PREFIX, string_sid);
+
+ kbuf.dsize = strlen(key)+1;
+ kbuf.dptr = key;
+
+ dbuf = tdb_fetch(tdb, kbuf);
+
+ if (dbuf.dptr == NULL) {
+ return NT_STATUS_OK;
+ }
+
+ p = dbuf.dptr;
+
+ while (next_token(&p, string_sid, " ", sizeof(string_sid))) {
+
+ DOM_SID alias;
+
+ if (!string_to_sid(&alias, string_sid))
+ continue;
+
+ add_sid_to_array(&alias, sids, num);
+
+ if (sids == NULL)
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ SAFE_FREE(dbuf.dptr);
+ return NT_STATUS_OK;
+}
+
+static BOOL is_aliasmem(const DOM_SID *alias, const DOM_SID *member)
+{
+ DOM_SID *sids;
+ int i, num;
+
+ /* This feels the wrong way round, but the on-disk data structure
+ * dictates it this way. */
+ if (!NT_STATUS_IS_OK(alias_memberships(member, &sids, &num)))
+ return False;
+
+ for (i=0; i<num; i++) {
+ if (sid_compare(alias, &sids[i]) == 0) {
+ SAFE_FREE(sids);
+ return True;
+ }
+ }
+ SAFE_FREE(sids);
+ return False;
+}
+
+static NTSTATUS add_aliasmem(const DOM_SID *alias, const DOM_SID *member)
+{
+ GROUP_MAP map;
+ TDB_DATA kbuf, dbuf;
+ pstring key;
+ fstring string_sid;
+ char *new_memberstring;
+ int result;
+
+ if(!init_group_mapping()) {
+ DEBUG(0,("failed to initialize group mapping\n"));
+ return NT_STATUS_ACCESS_DENIED;
+ }
+
+ if (!get_group_map_from_sid(*alias, &map))
+ return NT_STATUS_NO_SUCH_ALIAS;
+
+ if ( (map.sid_name_use != SID_NAME_ALIAS) &&
+ (map.sid_name_use != SID_NAME_WKN_GRP) )
+ return NT_STATUS_NO_SUCH_ALIAS;
+
+ if (is_aliasmem(alias, member))
+ return NT_STATUS_MEMBER_IN_ALIAS;
+
+ sid_to_string(string_sid, member);
+ slprintf(key, sizeof(key), "%s%s", MEMBEROF_PREFIX, string_sid);
+
+ kbuf.dsize = strlen(key)+1;
+ kbuf.dptr = key;
+
+ dbuf = tdb_fetch(tdb, kbuf);
+
+ sid_to_string(string_sid, alias);
+
+ if (dbuf.dptr != NULL) {
+ asprintf(&new_memberstring, "%s %s", (char *)(dbuf.dptr),
+ string_sid);
+ } else {
+ new_memberstring = strdup(string_sid);
+ }
+
+ if (new_memberstring == NULL)
+ return NT_STATUS_NO_MEMORY;
+
+ SAFE_FREE(dbuf.dptr);
+ dbuf.dsize = strlen(new_memberstring)+1;
+ dbuf.dptr = new_memberstring;
+
+ result = tdb_store(tdb, kbuf, dbuf, 0);
+
+ SAFE_FREE(new_memberstring);
+
+ return (result == 0 ? NT_STATUS_OK : NT_STATUS_ACCESS_DENIED);
+}
+
+struct aliasmem_closure {
+ const DOM_SID *alias;
+ DOM_SID **sids;
+ int *num;
+};
+
+static int collect_aliasmem(TDB_CONTEXT *tdb_ctx, TDB_DATA key, TDB_DATA data,
+ void *state)
+{
+ struct aliasmem_closure *closure = (struct aliasmem_closure *)state;
+ const char *p;
+ fstring alias_string;
+
+ if (strncmp(key.dptr, MEMBEROF_PREFIX,
+ strlen(MEMBEROF_PREFIX)) != 0)
+ return 0;
+
+ p = data.dptr;
+
+ while (next_token(&p, alias_string, " ", sizeof(alias_string))) {
+
+ DOM_SID alias, member;
+ const char *member_string;
+
+
+ if (!string_to_sid(&alias, alias_string))
+ continue;
+
+ if (sid_compare(closure->alias, &alias) != 0)
+ continue;
+
+ /* Ok, we found the alias we're looking for in the membership
+ * list currently scanned. The key represents the alias
+ * member. Add that. */
+
+ member_string = strchr(key.dptr, '/');
+
+ /* Above we tested for MEMBEROF_PREFIX which includes the
+ * slash. */
+
+ SMB_ASSERT(member_string != NULL);
+ member_string += 1;
+
+ if (!string_to_sid(&member, member_string))
+ continue;
+
+ add_sid_to_array(&member, closure->sids, closure->num);
+ }
+
+ return 0;
+}
+
+static NTSTATUS enum_aliasmem(const DOM_SID *alias, DOM_SID **sids, int *num)
+{
+ GROUP_MAP map;
+ struct aliasmem_closure closure;
+
+ if(!init_group_mapping()) {
+ DEBUG(0,("failed to initialize group mapping\n"));
+ return NT_STATUS_ACCESS_DENIED;
+ }
+
+ if (!get_group_map_from_sid(*alias, &map))
+ return NT_STATUS_NO_SUCH_ALIAS;
+
+ if ( (map.sid_name_use != SID_NAME_ALIAS) &&
+ (map.sid_name_use != SID_NAME_WKN_GRP) )
+ return NT_STATUS_NO_SUCH_ALIAS;
+
+ *sids = NULL;
+ *num = 0;
+
+ closure.alias = alias;
+ closure.sids = sids;
+ closure.num = num;
+
+ tdb_traverse(tdb, collect_aliasmem, &closure);
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS del_aliasmem(const DOM_SID *alias, const DOM_SID *member)
+{
+ NTSTATUS result;
+ DOM_SID *sids;
+ int i, num;
+ BOOL found = False;
+ char *member_string;
+ TDB_DATA kbuf, dbuf;
+ pstring key;
+ fstring sid_string;
+
+ result = alias_memberships(member, &sids, &num);
+
+ if (!NT_STATUS_IS_OK(result))
+ return result;
+
+ for (i=0; i<num; i++) {
+ if (sid_compare(&sids[i], alias) == 0) {
+ found = True;
+ break;
+ }
+ }
+
+ if (!found) {
+ SAFE_FREE(sids);
+ return NT_STATUS_MEMBER_NOT_IN_ALIAS;
+ }
+
+ if (i < num)
+ sids[i] = sids[num-1];
+
+ num -= 1;
+
+ sid_to_string(sid_string, member);
+ slprintf(key, sizeof(key), "%s%s", MEMBEROF_PREFIX, sid_string);
+
+ kbuf.dsize = strlen(key)+1;
+ kbuf.dptr = key;
+
+ if (num == 0)
+ return tdb_delete(tdb, kbuf) == 0 ?
+ NT_STATUS_OK : NT_STATUS_UNSUCCESSFUL;
+
+ member_string = strdup("");
+
+ if (member_string == NULL) {
+ SAFE_FREE(sids);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ for (i=0; i<num; i++) {
+ char *s = member_string;
+
+ sid_to_string(sid_string, &sids[i]);
+ asprintf(&member_string, "%s %s", s, sid_string);
+
+ SAFE_FREE(s);
+ if (member_string == NULL) {
+ SAFE_FREE(sids);
+ return NT_STATUS_NO_MEMORY;
+ }
+ }
+
+ dbuf.dsize = strlen(member_string)+1;
+ dbuf.dptr = member_string;
+
+ result = tdb_store(tdb, kbuf, dbuf, 0) == 0 ?
+ NT_STATUS_OK : NT_STATUS_ACCESS_DENIED;
+
+ SAFE_FREE(sids);
+ SAFE_FREE(member_string);
+
+ return result;
+}
+
+/*
+ *
+ * High level functions
+ * better to use them than the lower ones.
+ *
+ * we are checking if the group is in the mapping file
+ * and if the group is an existing unix group
+ *
+ */
+
+/* get a domain group from it's SID */
+
+BOOL get_domain_group_from_sid(DOM_SID sid, GROUP_MAP *map)
+{
+ struct group *grp;
+ BOOL ret;
+
+ if(!init_group_mapping()) {
+ DEBUG(0,("failed to initialize group mapping\n"));
+ return(False);
+ }
+
+ DEBUG(10, ("get_domain_group_from_sid\n"));
+
+ /* if the group is NOT in the database, it CAN NOT be a domain group */
+
+ become_root();
+ ret = pdb_getgrsid(map, sid);
+ unbecome_root();
+
+ if ( !ret )
+ return False;
+
+ DEBUG(10, ("get_domain_group_from_sid: SID found in the TDB\n"));
+
+ /* if it's not a domain group, continue */
+ if (map->sid_name_use!=SID_NAME_DOM_GRP) {
+ return False;
+ }
+
+ DEBUG(10, ("get_domain_group_from_sid: SID is a domain group\n"));
+
+ if (map->gid==-1) {
+ return False;
+ }
+
+ DEBUG(10, ("get_domain_group_from_sid: SID is mapped to gid:%lu\n",(unsigned long)map->gid));
+
+ grp = getgrgid(map->gid);
+ if ( !grp ) {
+ DEBUG(10, ("get_domain_group_from_sid: gid DOESN'T exist in UNIX security\n"));
+ return False;
+ }
+
+ DEBUG(10, ("get_domain_group_from_sid: gid exists in UNIX security\n"));
+
+ return True;
+}
+
+
+/* get a local (alias) group from it's SID */
+
+BOOL get_local_group_from_sid(DOM_SID *sid, GROUP_MAP *map)
+{
+ BOOL ret;
+
+ if(!init_group_mapping()) {
+ DEBUG(0,("failed to initialize group mapping\n"));
+ return(False);
+ }
+
+ /* The group is in the mapping table */
+ become_root();
+ ret = pdb_getgrsid(map, *sid);
+ unbecome_root();
+
+ if ( !ret )
+ return False;
+
+ if ( ( (map->sid_name_use != SID_NAME_ALIAS) &&
+ (map->sid_name_use != SID_NAME_WKN_GRP) )
+ || (map->gid == -1)
+ || (getgrgid(map->gid) == NULL) )
+ {
+ return False;
+ }
+
+#if 1 /* JERRY */
+ /* local groups only exist in the group mapping DB so this
+ is not necessary */
+
+ else {
+ /* the group isn't in the mapping table.
+ * make one based on the unix information */
+ uint32 alias_rid;
+ struct group *grp;
+
+ sid_peek_rid(sid, &alias_rid);
+ map->gid=pdb_group_rid_to_gid(alias_rid);
+
+ grp = getgrgid(map->gid);
+ if ( !grp ) {
+ DEBUG(3,("get_local_group_from_sid: No unix group for [%ul]\n", map->gid));
+ return False;
+ }
+
+ map->sid_name_use=SID_NAME_ALIAS;
+
+ fstrcpy(map->nt_name, grp->gr_name);
+ fstrcpy(map->comment, "Local Unix Group");
+
+ sid_copy(&map->sid, sid);
+ }
+#endif
+
+ return True;
+}
+
+/* get a builtin group from it's SID */
+
+BOOL get_builtin_group_from_sid(DOM_SID *sid, GROUP_MAP *map)
+{
+ struct group *grp;
+ BOOL ret;
+
+
+ if(!init_group_mapping()) {
+ DEBUG(0,("failed to initialize group mapping\n"));
+ return(False);
+ }
+
+ become_root();
+ ret = pdb_getgrsid(map, *sid);
+ unbecome_root();
+
+ if ( !ret )
+ return False;
+
+ if (map->sid_name_use!=SID_NAME_WKN_GRP) {
+ return False;
+ }
+
+ if (map->gid==-1) {
+ return False;
+ }
+
+ if ( (grp=getgrgid(map->gid)) == NULL) {
+ return False;
+ }
+
+ return True;
+}
+
+
+
+/****************************************************************************
+Returns a GROUP_MAP struct based on the gid.
+****************************************************************************/
+BOOL get_group_from_gid(gid_t gid, GROUP_MAP *map)
+{
+ struct group *grp;
+ BOOL ret;
+
+ if(!init_group_mapping()) {
+ DEBUG(0,("failed to initialize group mapping\n"));
+ return(False);
+ }
+
+ if ( (grp=getgrgid(gid)) == NULL)
+ return False;
+
+ /*
+ * make a group map from scratch if doesn't exist.
+ */
+
+ become_root();
+ ret = pdb_getgrgid(map, gid);
+ unbecome_root();
+
+ if ( !ret ) {
+ map->gid=gid;
+ map->sid_name_use=SID_NAME_ALIAS;
+
+ /* interim solution until we have a last RID allocated */
+
+ sid_copy(&map->sid, get_global_sam_sid());
+ sid_append_rid(&map->sid, pdb_gid_to_group_rid(gid));
+
+ fstrcpy(map->nt_name, grp->gr_name);
+ fstrcpy(map->comment, "Local Unix Group");
+ }
+
+ return True;
+}
+
+/****************************************************************************
+ Create a UNIX group on demand.
+****************************************************************************/
+
+int smb_create_group(char *unix_group, gid_t *new_gid)
+{
+ pstring add_script;
+ int ret = -1;
+ int fd = 0;
+
+ *new_gid = 0;
+
+ /* defer to scripts */
+
+ if ( *lp_addgroup_script() ) {
+ pstrcpy(add_script, lp_addgroup_script());
+ pstring_sub(add_script, "%g", unix_group);
+ ret = smbrun(add_script, (new_gid!=NULL) ? &fd : NULL);
+ DEBUG(3,("smb_create_group: Running the command `%s' gave %d\n",add_script,ret));
+ if (ret != 0)
+ return ret;
+
+ if (fd != 0) {
+ fstring output;
+
+ *new_gid = 0;
+ if (read(fd, output, sizeof(output)) > 0) {
+ *new_gid = (gid_t)strtoul(output, NULL, 10);
+ }
+
+ close(fd);
+ }
+
+ } else if ( winbind_create_group( unix_group, NULL ) ) {
+
+ DEBUG(3,("smb_create_group: winbindd created the group (%s)\n",
+ unix_group));
+ ret = 0;
+ }
+
+ if (*new_gid == 0) {
+ struct group *grp = getgrnam(unix_group);
+
+ if (grp != NULL)
+ *new_gid = grp->gr_gid;
+ }
+
+ return ret;
+}
+
+/****************************************************************************
+ Delete a UNIX group on demand.
+****************************************************************************/
+
+int smb_delete_group(char *unix_group)
+{
+ pstring del_script;
+ int ret;
+
+ /* defer to scripts */
+
+ if ( *lp_delgroup_script() ) {
+ pstrcpy(del_script, lp_delgroup_script());
+ pstring_sub(del_script, "%g", unix_group);
+ ret = smbrun(del_script,NULL);
+ DEBUG(3,("smb_delete_group: Running the command `%s' gave %d\n",del_script,ret));
+ return ret;
+ }
+
+ if ( winbind_delete_group( unix_group ) ) {
+ DEBUG(3,("smb_delete_group: winbindd deleted the group (%s)\n",
+ unix_group));
+ return 0;
+ }
+
+ return -1;
+}
+
+/****************************************************************************
+ Set a user's primary UNIX group.
+****************************************************************************/
+int smb_set_primary_group(const char *unix_group, const char* unix_user)
+{
+ pstring add_script;
+ int ret;
+
+ /* defer to scripts */
+
+ if ( *lp_setprimarygroup_script() ) {
+ pstrcpy(add_script, lp_setprimarygroup_script());
+ all_string_sub(add_script, "%g", unix_group, sizeof(add_script));
+ all_string_sub(add_script, "%u", unix_user, sizeof(add_script));
+ ret = smbrun(add_script,NULL);
+ DEBUG(3,("smb_set_primary_group: "
+ "Running the command `%s' gave %d\n",add_script,ret));
+ return ret;
+ }
+
+ /* Try winbindd */
+
+ if ( winbind_set_user_primary_group( unix_user, unix_group ) ) {
+ DEBUG(3,("smb_delete_group: winbindd set the group (%s) as the primary group for user (%s)\n",
+ unix_group, unix_user));
+ return 0;
+ }
+
+ return -1;
+}
+
+/****************************************************************************
+ Add a user to a UNIX group.
+****************************************************************************/
+
+int smb_add_user_group(char *unix_group, char *unix_user)
+{
+ pstring add_script;
+ int ret;
+
+ /* defer to scripts */
+
+ if ( *lp_addusertogroup_script() ) {
+ pstrcpy(add_script, lp_addusertogroup_script());
+ pstring_sub(add_script, "%g", unix_group);
+ pstring_sub(add_script, "%u", unix_user);
+ ret = smbrun(add_script,NULL);
+ DEBUG(3,("smb_add_user_group: Running the command `%s' gave %d\n",add_script,ret));
+ return ret;
+ }
+
+ /* Try winbindd */
+
+ if ( winbind_add_user_to_group( unix_user, unix_group ) ) {
+ DEBUG(3,("smb_delete_group: winbindd added user (%s) to the group (%s)\n",
+ unix_user, unix_group));
+ return -1;
+ }
+
+ return -1;
+}
+
+/****************************************************************************
+ Delete a user from a UNIX group
+****************************************************************************/
+
+int smb_delete_user_group(const char *unix_group, const char *unix_user)
+{
+ pstring del_script;
+ int ret;
+
+ /* defer to scripts */
+
+ if ( *lp_deluserfromgroup_script() ) {
+ pstrcpy(del_script, lp_deluserfromgroup_script());
+ pstring_sub(del_script, "%g", unix_group);
+ pstring_sub(del_script, "%u", unix_user);
+ ret = smbrun(del_script,NULL);
+ DEBUG(3,("smb_delete_user_group: Running the command `%s' gave %d\n",del_script,ret));
+ return ret;
+ }
+
+ /* Try winbindd */
+
+ if ( winbind_remove_user_from_group( unix_user, unix_group ) ) {
+ DEBUG(3,("smb_delete_group: winbindd removed user (%s) from the group (%s)\n",
+ unix_user, unix_group));
+ return 0;
+ }
+
+ return -1;
+}
+
+
+NTSTATUS pdb_default_getgrsid(struct pdb_methods *methods, GROUP_MAP *map,
+ DOM_SID sid)
+{
+ return get_group_map_from_sid(sid, map) ?
+ NT_STATUS_OK : NT_STATUS_UNSUCCESSFUL;
+}
+
+NTSTATUS pdb_default_getgrgid(struct pdb_methods *methods, GROUP_MAP *map,
+ gid_t gid)
+{
+ return get_group_map_from_gid(gid, map) ?
+ NT_STATUS_OK : NT_STATUS_UNSUCCESSFUL;
+}
+
+NTSTATUS pdb_default_getgrnam(struct pdb_methods *methods, GROUP_MAP *map,
+ const char *name)
+{
+ return get_group_map_from_ntname(name, map) ?
+ NT_STATUS_OK : NT_STATUS_UNSUCCESSFUL;
+}
+
+NTSTATUS pdb_default_add_group_mapping_entry(struct pdb_methods *methods,
+ GROUP_MAP *map)
+{
+ return add_mapping_entry(map, TDB_INSERT) ?
+ NT_STATUS_OK : NT_STATUS_UNSUCCESSFUL;
+}
+
+NTSTATUS pdb_default_update_group_mapping_entry(struct pdb_methods *methods,
+ GROUP_MAP *map)
+{
+ return add_mapping_entry(map, TDB_REPLACE) ?
+ NT_STATUS_OK : NT_STATUS_UNSUCCESSFUL;
+}
+
+NTSTATUS pdb_default_delete_group_mapping_entry(struct pdb_methods *methods,
+ DOM_SID sid)
+{
+ return group_map_remove(&sid) ?
+ NT_STATUS_OK : NT_STATUS_UNSUCCESSFUL;
+}
+
+NTSTATUS pdb_default_enum_group_mapping(struct pdb_methods *methods,
+ enum SID_NAME_USE sid_name_use,
+ GROUP_MAP **rmap, int *num_entries,
+ BOOL unix_only)
+{
+ return enum_group_mapping(sid_name_use, rmap, num_entries, unix_only) ?
+ NT_STATUS_OK : NT_STATUS_UNSUCCESSFUL;
+}
+
+NTSTATUS pdb_default_find_alias(struct pdb_methods *methods,
+ const char *name, DOM_SID *sid)
+{
+ GROUP_MAP map;
+
+ if (!pdb_getgrnam(&map, name))
+ return NT_STATUS_NO_SUCH_ALIAS;
+
+ if ((map.sid_name_use != SID_NAME_WKN_GRP) &&
+ (map.sid_name_use != SID_NAME_ALIAS))
+ return NT_STATUS_OBJECT_TYPE_MISMATCH;
+
+ sid_copy(sid, &map.sid);
+ return NT_STATUS_OK;
+}
+
+NTSTATUS pdb_default_create_alias(struct pdb_methods *methods,
+ const char *name, uint32 *rid)
+{
+ DOM_SID sid;
+ enum SID_NAME_USE type;
+ uint32 new_rid;
+ gid_t gid;
+
+ GROUP_MAP map;
+
+ if (lookup_name(get_global_sam_name(), name, &sid, &type))
+ return NT_STATUS_ALIAS_EXISTS;
+
+ if (!winbind_allocate_rid(&new_rid))
+ return NT_STATUS_ACCESS_DENIED;
+
+ sid_copy(&sid, get_global_sam_sid());
+ sid_append_rid(&sid, new_rid);
+
+ /* Here we allocate the gid */
+ if (!winbind_sid_to_gid(&gid, &sid)) {
+ DEBUG(0, ("Could not get gid for new RID\n"));
+ return NT_STATUS_ACCESS_DENIED;
+ }
+
+ map.gid = gid;
+ sid_copy(&map.sid, &sid);
+ map.sid_name_use = SID_NAME_ALIAS;
+ fstrcpy(map.nt_name, name);
+ fstrcpy(map.comment, "");
+
+ if (!pdb_add_group_mapping_entry(&map)) {
+ DEBUG(0, ("Could not add group mapping entry for alias %s\n",
+ name));
+ return NT_STATUS_ACCESS_DENIED;
+ }
+
+ *rid = new_rid;
+
+ return NT_STATUS_OK;
+}
+
+NTSTATUS pdb_default_delete_alias(struct pdb_methods *methods,
+ const DOM_SID *sid)
+{
+ return pdb_delete_group_mapping_entry(*sid) ?
+ NT_STATUS_OK : NT_STATUS_ACCESS_DENIED;
+}
+
+NTSTATUS pdb_default_enum_aliases(struct pdb_methods *methods,
+ const DOM_SID *sid,
+ uint32 start_idx, uint32 max_entries,
+ uint32 *num_aliases,
+ struct acct_info **info)
+{
+ extern DOM_SID global_sid_Builtin;
+
+ GROUP_MAP *map;
+ int i, num_maps;
+ enum SID_NAME_USE type = SID_NAME_UNKNOWN;
+
+ if (sid_compare(sid, get_global_sam_sid()) == 0)
+ type = SID_NAME_ALIAS;
+
+ if (sid_compare(sid, &global_sid_Builtin) == 0)
+ type = SID_NAME_WKN_GRP;
+
+ if (!pdb_enum_group_mapping(type, &map, &num_maps, False) ||
+ (num_maps == 0)) {
+ *num_aliases = 0;
+ *info = NULL;
+ goto done;
+ }
+
+ if (start_idx > num_maps) {
+ *num_aliases = 0;
+ *info = NULL;
+ goto done;
+ }
+
+ *num_aliases = num_maps - start_idx;
+
+ if (*num_aliases > max_entries)
+ *num_aliases = max_entries;
+
+ *info = malloc(sizeof(struct acct_info) * (*num_aliases));
+
+ for (i=0; i<*num_aliases; i++) {
+ fstrcpy((*info)[i].acct_name, map[i+start_idx].nt_name);
+ fstrcpy((*info)[i].acct_desc, map[i+start_idx].comment);
+ sid_peek_rid(&map[i].sid, &(*info)[i+start_idx].rid);
+ }
+
+ done:
+ SAFE_FREE(map);
+ return NT_STATUS_OK;
+}
+
+NTSTATUS pdb_default_get_aliasinfo(struct pdb_methods *methods,
+ const DOM_SID *sid,
+ struct acct_info *info)
+{
+ GROUP_MAP map;
+
+ if (!pdb_getgrsid(&map, *sid))
+ return NT_STATUS_NO_SUCH_ALIAS;
+
+ fstrcpy(info->acct_name, map.nt_name);
+ fstrcpy(info->acct_desc, map.comment);
+ sid_peek_rid(&map.sid, &info->rid);
+ return NT_STATUS_OK;
+}
+
+NTSTATUS pdb_default_set_aliasinfo(struct pdb_methods *methods,
+ const DOM_SID *sid,
+ struct acct_info *info)
+{
+ GROUP_MAP map;
+
+ if (!pdb_getgrsid(&map, *sid))
+ return NT_STATUS_NO_SUCH_ALIAS;
+
+ fstrcpy(map.comment, info->acct_desc);
+
+ if (!pdb_update_group_mapping_entry(&map))
+ return NT_STATUS_ACCESS_DENIED;
+
+ return NT_STATUS_OK;
+}
+
+NTSTATUS pdb_default_add_aliasmem(struct pdb_methods *methods,
+ const DOM_SID *alias, const DOM_SID *member)
+{
+ return add_aliasmem(alias, member);
+}
+
+NTSTATUS pdb_default_del_aliasmem(struct pdb_methods *methods,
+ const DOM_SID *alias, const DOM_SID *member)
+{
+ return del_aliasmem(alias, member);
+}
+
+NTSTATUS pdb_default_enum_aliasmem(struct pdb_methods *methods,
+ const DOM_SID *alias, DOM_SID **members,
+ int *num_members)
+{
+ return enum_aliasmem(alias, members, num_members);
+}
+
+NTSTATUS pdb_default_alias_memberships(struct pdb_methods *methods,
+ const DOM_SID *sid,
+ DOM_SID **aliases, int *num)
+{
+ return alias_memberships(sid, aliases, num);
+}
+
+/**********************************************************************
+ no ops for passdb backends that don't implement group mapping
+ *********************************************************************/
+
+NTSTATUS pdb_nop_getgrsid(struct pdb_methods *methods, GROUP_MAP *map,
+ DOM_SID sid)
+{
+ return NT_STATUS_UNSUCCESSFUL;
+}
+
+NTSTATUS pdb_nop_getgrgid(struct pdb_methods *methods, GROUP_MAP *map,
+ gid_t gid)
+{
+ return NT_STATUS_UNSUCCESSFUL;
+}
+
+NTSTATUS pdb_nop_getgrnam(struct pdb_methods *methods, GROUP_MAP *map,
+ const char *name)
+{
+ return NT_STATUS_UNSUCCESSFUL;
+}
+
+NTSTATUS pdb_nop_add_group_mapping_entry(struct pdb_methods *methods,
+ GROUP_MAP *map)
+{
+ return NT_STATUS_UNSUCCESSFUL;
+}
+
+NTSTATUS pdb_nop_update_group_mapping_entry(struct pdb_methods *methods,
+ GROUP_MAP *map)
+{
+ return NT_STATUS_UNSUCCESSFUL;
+}
+
+NTSTATUS pdb_nop_delete_group_mapping_entry(struct pdb_methods *methods,
+ DOM_SID sid)
+{
+ return NT_STATUS_UNSUCCESSFUL;
+}
+
+NTSTATUS pdb_nop_enum_group_mapping(struct pdb_methods *methods,
+ enum SID_NAME_USE sid_name_use,
+ GROUP_MAP **rmap, int *num_entries,
+ BOOL unix_only)
+{
+ return NT_STATUS_UNSUCCESSFUL;
+}
+
+/****************************************************************************
+ These need to be redirected through pdb_interface.c
+****************************************************************************/
+BOOL pdb_get_dom_grp_info(const DOM_SID *sid, struct acct_info *info)
+{
+ GROUP_MAP map;
+ BOOL res;
+
+ become_root();
+ res = get_domain_group_from_sid(*sid, &map);
+ unbecome_root();
+
+ if (!res)
+ return False;
+
+ fstrcpy(info->acct_name, map.nt_name);
+ fstrcpy(info->acct_desc, map.comment);
+ sid_peek_rid(sid, &info->rid);
+ return True;
+}
+
+BOOL pdb_set_dom_grp_info(const DOM_SID *sid, const struct acct_info *info)
+{
+ GROUP_MAP map;
+
+ if (!get_domain_group_from_sid(*sid, &map))
+ return False;
+
+ fstrcpy(map.nt_name, info->acct_name);
+ fstrcpy(map.comment, info->acct_desc);
+
+ return pdb_update_group_mapping_entry(&map);
+}
+
+
diff --git a/source/include/.cvsignore b/source/include/.cvsignore
new file mode 100644
index 00000000000..7dff121f143
--- /dev/null
+++ b/source/include/.cvsignore
@@ -0,0 +1,7 @@
+build_env.h
+config.h
+stamp-h
+proto.h
+wrepld_proto.h
+config.h.in
+version.h
diff --git a/source/include/MacExtensions.h b/source/include/MacExtensions.h
new file mode 100644
index 00000000000..d09370ed9f7
--- /dev/null
+++ b/source/include/MacExtensions.h
@@ -0,0 +1,246 @@
+/*
+ Unix SMB/CIFS implementation.
+ SMB parameters and setup
+ Copyright (C) Andrew Tridgell 1992-1998
+ Copyright (C) John H Terpstra 1996-1998
+ Copyright (C) Luke Kenneth Casson Leighton 1996-1998
+ Copyright (C) Paul Ashton 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 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 _MAC_EXTENSIONS_H
+#define _MAC_EXTENSIONS_H
+
+/* Folder that holds the stream info */
+#define STREAM_FOLDER ".streams"
+#define STREAM_FOLDER_SLASH ".streams/"
+
+/* Common Streams Names*/
+#define DefaultStreamTestLen 6
+#define DefaultStreamTest ":$DATA"
+#define AFPDATA_STREAM "::$DATA"
+#define AFPINFO_STREAM ":AFP_AfpInfo:$DATA"
+#define AFPRESOURCE_STREAM ":AFP_Resource:$DATA"
+#define AFPCOMMENTS_STREAM ":Comments:$DATA"
+#define AFPDESKTOP_STREAM ":AFP_DeskTop:$DATA"
+#define AFPIDINDEX_STREAM ":AFP_IdIndex:$DATA"
+
+/*
+** NT's AFP_AfpInfo stream structure
+*/
+#define APF_INFO_SIZE 0x3c
+#define AFP_Signature 0x41465000
+#define AFP_Version 0x00000100
+#define AFP_BackupTime 0x00000080
+#define AFP_FinderSize 32
+/*
+** Orginal AFP_AfpInfo stream used by NT
+** We needed a way to store the create date so SAMBA
+** AFP_AfpInfo adds for bytes to this structrure
+** and call's it _SambaAfpInfo
+*/
+typedef struct _AfpInfo
+{
+ uint32 afpi_Signature; /* Must be *(PDWORD)"AFP" */
+ uint32 afpi_Version; /* Must be 0x00010000 */
+ uint32 afpi_Reserved1;
+ uint32 afpi_BackupTime; /* Backup time for the file/dir */
+ unsigned char afpi_FinderInfo[AFP_FinderSize]; /* Finder Info (32 bytes) */
+ unsigned char afpi_ProDosInfo[6]; /* ProDos Info (6 bytes) # */
+ unsigned char afpi_Reserved2[6];
+} AfpInfo;
+
+typedef struct _SambaAfpInfo
+{
+ AfpInfo afp;
+ unsigned long createtime;
+} SambaAfpInfo;
+
+/*
+** On SAMBA this structrue is followed by 4 bytes that store the create
+** date of the file or folder asociated with it.
+*/
+
+/*
+** These extentions are only supported with the NT LM 0.12 Dialect. These extentions
+** will be process on a share by share bases.
+*/
+
+/*
+** Trans2_Query_FS_Information Call is used by the MacCIFS extentions for three reasons.
+** First to see if the remote server share supports the basic Macintosh CIFS extentions.
+** Second to return some basic need information about the share to the Macintosh.
+** Third to see if this share support any other Macintosh extentions.
+**
+** We will be using infromation levels that are betwwen 0x300 and 0x399 for all Macintosh
+** extentions calls. The first of these will be the SMB_MAC_QUERY_FS_INFO level which
+** will allow the server to return the MacQueryFSInfo structure. All fields are Little
+** Endian unless other wise specified.
+*/
+#define SMB_MAC_QUERY_FS_INFO 0x301
+
+
+
+/*
+** The server will return folder access control in the Trans2_Find_First2
+** and Trans2_Find_Next2 message described later in this document.
+*/
+#define SUPPORT_MAC_ACCESS_CNTRL 0x0010
+/*
+** The server supports setting/getting comments using the mechanism in this
+** document instead of using the NTFS format described in the Introduction.
+*/
+#define SUPPORT_MAC_GETSETCOMMENTS 0x0020
+/*
+** The Server supports setting and getting Macintosh desktop database information
+** using the mechanism in this document.
+*/
+#define SUPPORT_MAC_DESKTOPDB_CALLS 0x0040
+/*
+** The server will return a unique id for files and directories in the
+** Trans2_Find_First2 and Trans2_Find_Next2 message described later in this document.
+*/
+#define SUPPORT_MAC_UNIQUE_IDS 0x0080
+/*
+** The server will return this flag telling the client that the server does
+** not support streams or the Macintosh extensions. The rest of this message
+** will be ignored by the client.
+*/
+#define NO_STREAMS_OR_MAC_SUPPORT 0x0100
+
+/*
+** We will be adding a new info level to the Trans2_Find_First2 and Trans2_Find_Next2.
+** This info level will be SMB_MAC_FIND_BOTH_HFS_INFO and will support the server
+** return additional information need by the Macintosh. All fields are Little
+** Endian unless other wise specified.
+*/
+
+#define SMB_MAC_FIND_BOTH_HFS_INFO 0x302
+
+enum {
+ ownerRead = 0x0400,
+ ownerWrite = 0x0200,
+ ownerSearch = 0x0100,
+ groupRead = 0x0040,
+ groupWrite = 0x0020,
+ groupSearch = 0x0010,
+ otherRead = 0x0004,
+ otherWrite = 0x0002,
+ otherSearch = 0x0001,
+ Owner = 0x0800
+};
+
+
+/*
+** We will be adding a new info level to the Trans2_Set_Path_Information.
+** This info level will be SMB_MAC_SET_FINDER_INFO and will support the client
+** setting information on the server need by the Macintosh. All fields are Little
+** Endian unless other wise specified.
+*/
+
+#define SMB_MAC_SET_FINDER_INFO 0x303
+
+enum {
+ SetCreateDate = 0x01, /* If this is set then set the create date of the file/folder */
+ SetModDate = 0x02, /* If this is set then set the modify date of the file/folder */
+ SetFLAttrib = 0x04, /* If this is set then set the Macintosh lock bit of the file/folder */
+ FndrInfo1 = 0x08, /* If this is set then set the first 16 bytes of finder info */
+ FndrInfo2 = 0x10, /* If this is set then set the second 16 bytes of finder info */
+ SetHidden = 0x20 /* We are either setting or unsetting the hidden bit */
+};
+
+
+/*
+** We will be adding some new info level to the Trans2_Set_Path_Information and Trans2_Query_Path_Information.
+** These info levels will allow the client to add, get, and remove desktop inforamtion from the
+** server. How the server stores this information is up to them.
+*/
+
+/*
+** We need to be able to store an application name and its creator in a database. We send a
+** Trans2_Set_Path_Information call with the full path of the application in the path field.
+** We will send an info level that represents adding an application name and creator to the database.
+** We will pass the File Creator in the data message.
+**
+** The server should just respond with no error or an error.
+*/
+#define SMB_MAC_DT_ADD_APPL 0x304
+
+/*
+** We need to be able to remove an application name and its creator from a database. We send a
+** Trans2_Set_Path_Information call with the full path of the application in the path field.
+** We will send an info level that represents removing an application name and creator from the database.
+** We will pass the File Creator in the data message.
+**
+** The server should just respond with no error or an error.
+*/
+#define SMB_MAC_DT_REMOVE_APPL 0x305
+
+
+/*
+** We need to be able to get an application name and its creator from a database. We send a
+** Trans2_Query_Path_Information call in which the name field is just ignore.
+** We will send an info level that represents getting an application name with a structure that
+** contains the File Creator and index. Were index has the following meaning.
+** Index = 0; Get the application path from the database with the most current date.
+** Index > 0; Use the index to find the application path from the database.
+** e.g. index of 5 means get the fifth entry of this application name in the database.
+** if not entry return an error.
+**
+** The server returns with a structure that contains the full path to the appication and
+** its creator's date.
+*/
+#define SMB_MAC_DT_GET_APPL 0x306
+
+
+/*
+** We need to be able to get an icon from a database. We send a Trans2_Query_Path_Information call in
+** which the path name is ignore. We will send an info level that represents getting an icon with a structure
+** that contains the Requested size of the icon, the Icon type, File Creator, and File Type.
+**
+** The server returns with a structure that contains the actual size of the icon
+** (must be less than requested length) and the icon bit map.
+*/
+#define SMB_MAC_DT_GET_ICON 0x307
+
+
+/*
+** We need to be able to get an icon from a database. We send a Trans2_Query_Path_Information call in
+** which the path name is ignore. We will send an info level that represents getting an icon with a structure
+** that contains the index and File Creator. The index allows the client to make repeated calls to the server
+** gathering all icon stored by this file creator.
+**
+**
+** The server returns with a structure that contains the actual size of the icon
+** (must be less than requested length) and the icon bit map, File Type, and Icon Type.
+*/
+#define SMB_MAC_DT_GET_ICON_INFO 0x308
+
+
+
+/*
+** We need to be able to add an icon to a database. We send a Trans2_Set_Path_Information call in
+** which the path name is ignore. We will send an info level that represents setting an icon with a structure
+** that contains the icon data, icon size, icon type, the file type, and file creator.
+**
+**
+** The server returns only that the call was succesfull or not.
+*/
+#define SMB_MAC_DT_ADD_ICON 0x309
+
+#endif /* _MAC_EXTENSIONS_H */
+
+/* _MAC_EXTENSIONS_H */
+
diff --git a/source/include/ads.h b/source/include/ads.h
new file mode 100644
index 00000000000..4daa65e796d
--- /dev/null
+++ b/source/include/ads.h
@@ -0,0 +1,226 @@
+/*
+ header for ads (active directory) library routines
+
+ basically this is a wrapper around ldap
+*/
+
+typedef struct {
+ void *ld; /* the active ldap structure */
+ struct in_addr ldap_ip; /* the ip of the active connection, if any */
+ time_t last_attempt; /* last attempt to reconnect */
+ int ldap_port;
+
+ int is_mine; /* do I own this structure's memory? */
+
+ /* info needed to find the server */
+ struct {
+ char *realm;
+ char *workgroup;
+ char *ldap_server;
+ char *ldap_uri;
+ int foreign; /* set to 1 if connecting to a foreign realm */
+ } server;
+
+ /* info needed to authenticate */
+ struct {
+ char *realm;
+ char *password;
+ char *user_name;
+ char *kdc_server;
+ unsigned flags;
+ int time_offset;
+ time_t expire;
+ } auth;
+
+ /* info derived from the servers config */
+ struct {
+ char *realm;
+ char *bind_path;
+ char *ldap_server_name;
+ time_t current_time;
+ } config;
+} ADS_STRUCT;
+
+/* there are 5 possible types of errors the ads subsystem can produce */
+enum ads_error_type {ENUM_ADS_ERROR_KRB5, ENUM_ADS_ERROR_GSS,
+ ENUM_ADS_ERROR_LDAP, ENUM_ADS_ERROR_SYSTEM, ENUM_ADS_ERROR_NT};
+
+typedef struct {
+ enum ads_error_type error_type;
+ union err_state{
+ int rc;
+ NTSTATUS nt_status;
+ } err;
+ /* For error_type = ENUM_ADS_ERROR_GSS minor_status describe GSS API error */
+ /* Where rc represents major_status of GSS API error */
+ int minor_status;
+} ADS_STATUS;
+
+#ifdef HAVE_ADS
+typedef LDAPMod **ADS_MODLIST;
+#else
+typedef void **ADS_MODLIST;
+#endif
+
+/* macros to simplify error returning */
+#define ADS_ERROR(rc) ADS_ERROR_LDAP(rc)
+#define ADS_ERROR_LDAP(rc) ads_build_error(ENUM_ADS_ERROR_LDAP, rc, 0)
+#define ADS_ERROR_SYSTEM(rc) ads_build_error(ENUM_ADS_ERROR_SYSTEM, rc?rc:EINVAL, 0)
+#define ADS_ERROR_KRB5(rc) ads_build_error(ENUM_ADS_ERROR_KRB5, rc, 0)
+#define ADS_ERROR_GSS(rc, minor) ads_build_error(ENUM_ADS_ERROR_GSS, rc, minor)
+#define ADS_ERROR_NT(rc) ads_build_nt_error(ENUM_ADS_ERROR_NT,rc)
+
+#define ADS_ERR_OK(status) ((status.error_type == ENUM_ADS_ERROR_NT) ? NT_STATUS_IS_OK(status.err.nt_status):(status.err.rc == 0))
+#define ADS_SUCCESS ADS_ERROR(0)
+
+/* time between reconnect attempts */
+#define ADS_RECONNECT_TIME 5
+
+/* timeout on searches */
+#define ADS_SEARCH_TIMEOUT 10
+
+/* ldap control oids */
+#define ADS_PAGE_CTL_OID "1.2.840.113556.1.4.319"
+#define ADS_NO_REFERRALS_OID "1.2.840.113556.1.4.1339"
+#define ADS_SERVER_SORT_OID "1.2.840.113556.1.4.473"
+#define ADS_PERMIT_MODIFY_OID "1.2.840.113556.1.4.1413"
+
+/* UserFlags for userAccountControl */
+#define UF_SCRIPT 0x00000001
+#define UF_ACCOUNTDISABLE 0x00000002
+#define UF_UNUSED_1 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
+#define UF_NORMAL_ACCOUNT 0x00000200
+#define UF_UNUSED_2 0x00000400
+#define UF_INTERDOMAIN_TRUST_ACCOUNT 0x00000800
+
+#define UF_WORKSTATION_TRUST_ACCOUNT 0x00001000
+#define UF_SERVER_TRUST_ACCOUNT 0x00002000
+#define UF_UNUSED_3 0x00004000
+#define UF_UNUSED_4 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_UNUSED_5 0x00800000
+
+#define UF_UNUSED_6 0x01000000
+#define UF_UNUSED_7 0x02000000
+#define UF_UNUSED_8 0x04000000
+#define UF_UNUSED_9 0x08000000
+
+#define UF_UNUSED_10 0x10000000
+#define UF_UNUSED_11 0x20000000
+#define UF_UNUSED_12 0x40000000
+#define UF_UNUSED_13 0x80000000
+
+#define UF_MACHINE_ACCOUNT_MASK (\
+ UF_INTERDOMAIN_TRUST_ACCOUNT |\
+ UF_WORKSTATION_TRUST_ACCOUNT |\
+ UF_SERVER_TRUST_ACCOUNT \
+ )
+
+#define UF_ACCOUNT_TYPE_MASK (\
+ UF_TEMP_DUPLICATE_ACCOUNT |\
+ UF_NORMAL_ACCOUNT |\
+ UF_INTERDOMAIN_TRUST_ACCOUNT |\
+ UF_WORKSTATION_TRUST_ACCOUNT |\
+ UF_SERVER_TRUST_ACCOUNT \
+ )
+
+#define UF_SETTABLE_BITS (\
+ UF_SCRIPT |\
+ UF_ACCOUNTDISABLE |\
+ UF_HOMEDIR_REQUIRED |\
+ UF_LOCKOUT |\
+ UF_PASSWD_NOTREQD |\
+ UF_PASSWD_CANT_CHANGE |\
+ UF_ACCOUNT_TYPE_MASK | \
+ UF_DONT_EXPIRE_PASSWD | \
+ UF_MNS_LOGON_ACCOUNT |\
+ UF_ENCRYPTED_TEXT_PASSWORD_ALLOWED |\
+ UF_SMARTCARD_REQUIRED |\
+ UF_TRUSTED_FOR_DELEGATION |\
+ UF_NOT_DELEGATED |\
+ UF_USE_DES_KEY_ONLY |\
+ UF_DONT_REQUIRE_PREAUTH \
+ )
+
+/* 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 GTYPE_SECURITY_BUILTIN_LOCAL_GROUP 0x80000005 /* -2147483643 */
+#define GTYPE_SECURITY_DOMAIN_LOCAL_GROUP 0x80000004 /* -2147483644 */
+#define GTYPE_SECURITY_GLOBAL_GROUP 0x80000002 /* -2147483646 */
+#define GTYPE_DISTRIBUTION_GLOBAL_GROUP 0x00000002 /* 2 */
+#define GTYPE_DISTRIBUTION_DOMAIN_LOCAL_GROUP 0x00000004 /* 4 */
+#define GTYPE_DISTRIBUTION_UNIVERSAL_GROUP 0x00000008 /* 8 */
+
+/* Mailslot or cldap getdcname response flags */
+#define ADS_PDC 0x00000001 /* DC is PDC */
+#define ADS_GC 0x00000004 /* DC is a GC of forest */
+#define ADS_LDAP 0x00000008 /* DC is an LDAP server */
+#define ADS_DS 0x00000010 /* DC supports DS */
+#define ADS_KDC 0x00000020 /* DC is running KDC */
+#define ADS_TIMESERV 0x00000040 /* DC is running time services */
+#define ADS_CLOSEST 0x00000080 /* DC is closest to client */
+#define ADS_WRITABLE 0x00000100 /* DC has writable DS */
+#define ADS_GOOD_TIMESERV 0x00000200 /* DC has hardware clock
+ (and running time) */
+#define ADS_NDNC 0x00000400 /* DomainName is non-domain NC serviced
+ by LDAP server */
+#define ADS_PINGS 0x0000FFFF /* Ping response */
+#define ADS_DNS_CONTROLLER 0x20000000 /* DomainControllerName is a DNS name*/
+#define ADS_DNS_DOMAIN 0x40000000 /* DomainName is a DNS name */
+#define ADS_DNS_FOREST 0x80000000 /* DnsForestName is a DNS name */
+
+/* DomainCntrollerAddressType */
+#define ADS_INET_ADDRESS 0x00000001
+#define ADS_NETBIOS_ADDRESS 0x00000002
+
+
+/* ads auth control flags */
+#define ADS_AUTH_DISABLE_KERBEROS 0x01
+#define ADS_AUTH_NO_BIND 0x02
+#define ADS_AUTH_ANON_BIND 0x04
+#define ADS_AUTH_SIMPLE_BIND 0x08
+#define ADS_AUTH_ALLOW_NTLMSSP 0x10
+
+/* Kerberos environment variable names */
+#define KRB5_ENV_CCNAME "KRB5CCNAME"
+
+/* Heimdal uses a slightly different name */
+#if defined(HAVE_ENCTYPE_ARCFOUR_HMAC_MD5)
+#define ENCTYPE_ARCFOUR_HMAC ENCTYPE_ARCFOUR_HMAC_MD5
+#endif
+
+/* The older versions of heimdal that don't have this
+ define don't seem to use it anyway. I'm told they
+ always use a subkey */
+#ifndef HAVE_AP_OPTS_USE_SUBKEY
+#define AP_OPTS_USE_SUBKEY 0
+#endif
diff --git a/source/include/adt_tree.h b/source/include/adt_tree.h
new file mode 100644
index 00000000000..12e2ea5cc53
--- /dev/null
+++ b/source/include/adt_tree.h
@@ -0,0 +1,38 @@
+/*
+ * Unix SMB/CIFS implementation.
+ * Generic Abstract Data Types
+ * Copyright (C) Gerald Carter 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 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 ADT_TREE_H
+#define ADT_TREE_H
+
+typedef struct _tree_node {
+ struct _tree_node *parent;
+ struct _tree_node **children;
+ int num_children;
+ char *key;
+ void *data_p;
+} TREE_NODE;
+
+typedef struct _tree_root {
+ TREE_NODE *root;
+ int (*compare)(void* x, void *y);
+ void (*free_func)(void *p);
+} SORTED_TREE;
+
+#endif
diff --git a/source/include/asn_1.h b/source/include/asn_1.h
new file mode 100644
index 00000000000..796c8bb7404
--- /dev/null
+++ b/source/include/asn_1.h
@@ -0,0 +1,69 @@
+/*
+ Unix SMB/CIFS implementation.
+ simple ASN1 code
+ 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 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 _ASN_1_H
+#define _ASN_1_H
+
+struct nesting {
+ off_t start;
+ size_t taglen; /* for parsing */
+ struct nesting *next;
+};
+
+typedef struct {
+ uint8 *data;
+ size_t length;
+ off_t ofs;
+ struct nesting *nesting;
+ BOOL has_error;
+} ASN1_DATA;
+
+
+#define ASN1_APPLICATION(x) ((x)+0x60)
+#define ASN1_SEQUENCE(x) ((x)+0x30)
+#define ASN1_CONTEXT(x) ((x)+0xa0)
+#define ASN1_GENERAL_STRING 0x1b
+#define ASN1_OCTET_STRING 0x4
+#define ASN1_OID 0x6
+#define ASN1_BOOLEAN 0x1
+#define ASN1_INTEGER 0x2
+#define ASN1_ENUMERATED 0xa
+#define ASN1_SET 0x31
+
+#define ASN1_MAX_OIDS 20
+
+/* some well known object IDs */
+#define OID_SPNEGO "1 3 6 1 5 5 2"
+#define OID_NTLMSSP "1 3 6 1 4 1 311 2 2 10"
+#define OID_KERBEROS5_OLD "1 2 840 48018 1 2 2"
+#define OID_KERBEROS5 "1 2 840 113554 1 2 2"
+
+#define SPNEGO_NEG_RESULT_ACCEPT 0
+#define SPNEGO_NEG_RESULT_INCOMPLETE 1
+#define SPNEGO_NEG_RESULT_REJECT 2
+
+/* not really ASN.1, but RFC 1964 */
+#define TOK_ID_KRB_AP_REQ (uchar*)"\x01\x00"
+#define TOK_ID_KRB_AP_REP (uchar*)"\x02\x00"
+#define TOK_ID_KRB_ERROR (uchar*)"\x03\x00"
+#define TOK_ID_GSS_GETMIC (uchar*)"\x01\x01"
+#define TOK_ID_GSS_WRAP (uchar*)"\x02\x01"
+
+#endif /* _ASN_1_H */
diff --git a/source/include/auth.h b/source/include/auth.h
new file mode 100644
index 00000000000..27cdc1e3f5f
--- /dev/null
+++ b/source/include/auth.h
@@ -0,0 +1,172 @@
+#ifndef _SMBAUTH_H_
+#define _SMBAUTH_H_
+/*
+ Unix SMB/CIFS implementation.
+ Standardised Authentication types
+ Copyright (C) Andrew Bartlett 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 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.
+*/
+
+/* AUTH_STR - string */
+typedef struct normal_string
+{
+ int len;
+ char *str;
+} AUTH_STR;
+
+/* AUTH_UNISTR - unicode string or buffer */
+typedef struct unicode_string
+{
+ int len;
+ uchar *unistr;
+} AUTH_UNISTR;
+
+typedef struct interactive_password
+{
+ OWF_INFO lm_owf; /* LM OWF Password */
+ OWF_INFO nt_owf; /* NT OWF Password */
+} auth_interactive_password;
+
+#define AUTH_FLAG_NONE 0x000000
+#define AUTH_FLAG_PLAINTEXT 0x000001
+#define AUTH_FLAG_LM_RESP 0x000002
+#define AUTH_FLAG_NTLM_RESP 0x000004
+#define AUTH_FLAG_NTLMv2_RESP 0x000008
+
+typedef struct auth_usersupplied_info
+{
+
+ DATA_BLOB lm_resp;
+ DATA_BLOB nt_resp;
+ auth_interactive_password * interactive_password;
+ DATA_BLOB plaintext_password;
+
+ BOOL encrypted;
+
+ uint32 auth_flags;
+
+ AUTH_STR client_domain; /* domain name string */
+ AUTH_STR domain; /* domain name after mapping */
+ AUTH_STR internal_username; /* username after mapping */
+ AUTH_STR smb_name; /* username before mapping */
+ AUTH_STR wksta_name; /* workstation name (netbios calling name) unicode string */
+
+} auth_usersupplied_info;
+
+#define SAM_FILL_NAME 0x01
+#define SAM_FILL_INFO3 0x02
+#define SAM_FILL_SAM 0x04
+#define SAM_FILL_UNIX 0x08
+#define SAM_FILL_ALL (SAM_FILL_NAME | SAM_FILL_INFO3 | SAM_FILL_SAM | SAM_FILL_UNIX)
+
+typedef struct auth_serversupplied_info
+{
+ BOOL guest;
+
+ uid_t uid;
+ gid_t gid;
+
+ /* This groups info is needed for when we become_user() for this uid */
+ int n_groups;
+ gid_t *groups;
+
+ /* NT group information taken from the info3 structure */
+
+ NT_USER_TOKEN *ptok;
+ PRIVILEGE_SET *privs;
+
+ DATA_BLOB nt_session_key;
+ DATA_BLOB lm_session_key;
+
+ uint32 sam_fill_level; /* How far is this structure filled? */
+
+ SAM_ACCOUNT *sam_account;
+
+ void *pam_handle;
+
+ char *unix_name;
+
+} auth_serversupplied_info;
+
+struct auth_context {
+ DATA_BLOB challenge;
+
+ /* Who set this up in the first place? */
+ const char *challenge_set_by;
+
+ BOOL challenge_may_be_modified;
+
+ struct auth_methods *challenge_set_method;
+ /* What order are the various methods in? Try to stop it changing under us */
+ struct auth_methods *auth_method_list;
+
+ TALLOC_CTX *mem_ctx;
+ const uint8 *(*get_ntlm_challenge)(struct auth_context *auth_context);
+ NTSTATUS (*check_ntlm_password)(const struct auth_context *auth_context,
+ const struct auth_usersupplied_info *user_info,
+ struct auth_serversupplied_info **server_info);
+ NTSTATUS (*nt_status_squash)(NTSTATUS nt_status);
+ void (*free)(struct auth_context **auth_context);
+};
+
+typedef struct auth_methods
+{
+ struct auth_methods *prev, *next;
+ const char *name; /* What name got this module */
+
+ NTSTATUS (*auth)(const struct auth_context *auth_context,
+ void *my_private_data,
+ TALLOC_CTX *mem_ctx,
+ const struct auth_usersupplied_info *user_info,
+ auth_serversupplied_info **server_info);
+
+ DATA_BLOB (*get_chal)(const struct auth_context *auth_context,
+ void **my_private_data,
+ TALLOC_CTX *mem_ctx);
+
+ /* Used to keep tabs on things like the cli for SMB server authentication */
+ void *private_data;
+
+ /* Function to clean up the above arbitary structure */
+ void (*free_private_data)(void **private_data);
+
+ /* Function to send a keepalive message on the above structure */
+ void (*send_keepalive)(void **private_data);
+
+} auth_methods;
+
+typedef NTSTATUS (*auth_init_function)(struct auth_context *, const char *, struct auth_methods **);
+
+struct auth_init_function_entry {
+ const char *name;
+ /* Function to create a member of the authmethods list */
+
+ auth_init_function init;
+
+ struct auth_init_function_entry *prev, *next;
+};
+
+typedef struct auth_ntlmssp_state
+{
+ TALLOC_CTX *mem_ctx;
+ struct auth_context *auth_context;
+ struct auth_serversupplied_info *server_info;
+ struct ntlmssp_state *ntlmssp_state;
+} AUTH_NTLMSSP_STATE;
+
+#define AUTH_INTERFACE_VERSION 1
+
+#endif /* _SMBAUTH_H_ */
diff --git a/source/include/authdata.h b/source/include/authdata.h
new file mode 100644
index 00000000000..9d80745fb06
--- /dev/null
+++ b/source/include/authdata.h
@@ -0,0 +1,152 @@
+/*
+ Unix SMB/CIFS implementation.
+ Kerberos authorization data
+ 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 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 _AUTHDATA_H
+#define _AUTHDATA_H
+
+#include "rpc_misc.h"
+
+#define PAC_TYPE_LOGON_INFO 1
+#define PAC_TYPE_SERVER_CHECKSUM 6
+#define PAC_TYPE_PRIVSVR_CHECKSUM 7
+#define PAC_TYPE_UNKNOWN_10 10
+
+typedef struct unknown_type_10 {
+ NTTIME unknown_time;
+ uint16 len;
+ uint16 *username; /* might not be null terminated, so not UNISTR */
+} UNKNOWN_TYPE_10;
+
+typedef struct pac_signature_data {
+ uint32 type;
+ uint8 *signature;
+} PAC_SIGNATURE_DATA;
+
+typedef struct group_membership {
+ uint32 rid;
+ uint32 attrs;
+} GROUP_MEMBERSHIP;
+
+typedef struct group_membership_array {
+ uint32 count;
+ GROUP_MEMBERSHIP *group_membership;
+} GROUP_MEMBERSHIP_ARRAY;
+
+typedef struct krb_sid_and_attrs {
+ uint32 sid_ptr;
+ uint32 attrs;
+ DOM_SID2 *sid;
+} KRB_SID_AND_ATTRS;
+
+typedef struct krb_sid_and_attr_array {
+ uint32 count;
+ KRB_SID_AND_ATTRS *krb_sid_and_attrs;
+} KRB_SID_AND_ATTR_ARRAY;
+
+
+/* This is awfully similar to a samr_user_info_23, but not identical.
+ Many of the field names have been swiped from there, because it is
+ so similar that they are likely the same, but many have been verified.
+ Some are in a different order, though... */
+typedef struct pac_logon_info {
+ NTTIME logon_time; /* logon time */
+ NTTIME logoff_time; /* logoff time */
+ NTTIME kickoff_time; /* kickoff time */
+ NTTIME pass_last_set_time; /* password last set time */
+ NTTIME pass_can_change_time; /* password can change time */
+ NTTIME pass_must_change_time; /* password must change time */
+
+ UNIHDR hdr_user_name; /* user name unicode string header */
+ UNIHDR hdr_full_name; /* user's full name unicode string header */
+ UNIHDR hdr_logon_script; /* these last 4 appear to be in a different */
+ UNIHDR hdr_profile_path; /* order than in the info23 */
+ UNIHDR hdr_home_dir;
+ UNIHDR hdr_dir_drive;
+
+ uint16 logon_count; /* number of times user has logged onto domain */
+ uint16 reserved12;
+
+ uint32 user_rid;
+ uint32 group_rid;
+ uint32 group_count;
+ uint32 group_membership_ptr;
+ uint32 user_flags;
+
+ uint32 reserved13[4];
+ UNIHDR hdr_dom_controller;
+ UNIHDR hdr_dom_name;
+
+ uint32 ptr_dom_sid;
+
+ uint32 reserved16[2];
+ uint32 reserved17; /* looks like it may be acb_info */
+ uint32 reserved18[7];
+
+ uint32 sid_count;
+ uint32 ptr_extra_sids;
+
+ uint32 ptr_res_group_dom_sid;
+ uint32 res_group_count;
+ uint32 ptr_res_groups;
+
+ UNISTR2 uni_user_name; /* user name unicode string header */
+ UNISTR2 uni_full_name; /* user's full name unicode string header */
+ UNISTR2 uni_logon_script; /* these last 4 appear to be in a different*/
+ UNISTR2 uni_profile_path; /* order than in the info23 */
+ UNISTR2 uni_home_dir;
+ UNISTR2 uni_dir_drive;
+ UNISTR2 uni_dom_controller;
+ UNISTR2 uni_dom_name;
+ DOM_SID2 dom_sid;
+ GROUP_MEMBERSHIP_ARRAY groups;
+ KRB_SID_AND_ATTR_ARRAY extra_sids;
+ DOM_SID2 res_group_dom_sid;
+ GROUP_MEMBERSHIP_ARRAY res_groups;
+
+} PAC_LOGON_INFO;
+
+typedef struct pac_info_ctr
+{
+ union
+ {
+ PAC_LOGON_INFO *logon_info;
+ PAC_SIGNATURE_DATA *srv_cksum;
+ PAC_SIGNATURE_DATA *privsrv_cksum;
+ UNKNOWN_TYPE_10 *type_10;
+ } pac;
+} PAC_INFO_CTR;
+
+typedef struct pac_info_hdr {
+ uint32 type;
+ uint32 size;
+ uint32 offset;
+ uint32 offsethi;
+ PAC_INFO_CTR *ctr;
+} PAC_INFO_HDR;
+
+typedef struct pac_data {
+ uint32 num_buffers;
+ uint32 version;
+ PAC_INFO_HDR *pac_info_hdr_ptr;
+} PAC_DATA;
+
+
+#endif
diff --git a/source/include/byteorder.h b/source/include/byteorder.h
new file mode 100644
index 00000000000..0eef5573066
--- /dev/null
+++ b/source/include/byteorder.h
@@ -0,0 +1,171 @@
+/*
+ Unix SMB/CIFS implementation.
+ SMB Byte handling
+ 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 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 _BYTEORDER_H
+#define _BYTEORDER_H
+
+/*
+ This file implements macros for machine independent short and
+ int manipulation
+
+Here is a description of this file that I emailed to the samba list once:
+
+> I am confused about the way that byteorder.h works in Samba. I have
+> looked at it, and I would have thought that you might make a distinction
+> between LE and BE machines, but you only seem to distinguish between 386
+> and all other architectures.
+>
+> Can you give me a clue?
+
+sure.
+
+The distinction between 386 and other architectures is only there as
+an optimisation. You can take it out completely and it will make no
+difference. The routines (macros) in byteorder.h are totally byteorder
+independent. The 386 optimsation just takes advantage of the fact that
+the x86 processors don't care about alignment, so we don't have to
+align ints on int boundaries etc. If there are other processors out
+there that aren't alignment sensitive then you could also define
+CAREFUL_ALIGNMENT=0 on those processors as well.
+
+Ok, now to the macros themselves. I'll take a simple example, say we
+want to extract a 2 byte integer from a SMB packet and put it into a
+type called uint16 that is in the local machines byte order, and you
+want to do it with only the assumption that uint16 is _at_least_ 16
+bits long (this last condition is very important for architectures
+that don't have any int types that are 2 bytes long)
+
+You do this:
+
+#define CVAL(buf,pos) (((unsigned char *)(buf))[pos])
+#define PVAL(buf,pos) ((unsigned)CVAL(buf,pos))
+#define SVAL(buf,pos) (PVAL(buf,pos)|PVAL(buf,(pos)+1)<<8)
+
+then to extract a uint16 value at offset 25 in a buffer you do this:
+
+char *buffer = foo_bar();
+uint16 xx = SVAL(buffer,25);
+
+We are using the byteoder independence of the ANSI C bitshifts to do
+the work. A good optimising compiler should turn this into efficient
+code, especially if it happens to have the right byteorder :-)
+
+I know these macros can be made a bit tidier by removing some of the
+casts, but you need to look at byteorder.h as a whole to see the
+reasoning behind them. byteorder.h defines the following macros:
+
+SVAL(buf,pos) - extract a 2 byte SMB value
+IVAL(buf,pos) - extract a 4 byte SMB value
+SVALS(buf,pos) signed version of SVAL()
+IVALS(buf,pos) signed version of IVAL()
+
+SSVAL(buf,pos,val) - put a 2 byte SMB value into a buffer
+SIVAL(buf,pos,val) - put a 4 byte SMB value into a buffer
+SSVALS(buf,pos,val) - signed version of SSVAL()
+SIVALS(buf,pos,val) - signed version of SIVAL()
+
+RSVAL(buf,pos) - like SVAL() but for NMB byte ordering
+RSVALS(buf,pos) - like SVALS() but for NMB byte ordering
+RIVAL(buf,pos) - like IVAL() but for NMB byte ordering
+RIVALS(buf,pos) - like IVALS() but for NMB byte ordering
+RSSVAL(buf,pos,val) - like SSVAL() but for NMB ordering
+RSIVAL(buf,pos,val) - like SIVAL() but for NMB ordering
+RSIVALS(buf,pos,val) - like SIVALS() but for NMB ordering
+
+it also defines lots of intermediate macros, just ignore those :-)
+
+*/
+
+#undef CAREFUL_ALIGNMENT
+
+/* we know that the 386 can handle misalignment and has the "right"
+ byteorder */
+#ifdef __i386__
+#define CAREFUL_ALIGNMENT 0
+#endif
+
+#ifndef CAREFUL_ALIGNMENT
+#define CAREFUL_ALIGNMENT 1
+#endif
+
+#define CVAL(buf,pos) ((unsigned)(((const unsigned char *)(buf))[pos]))
+#define CVAL_NC(buf,pos) (((unsigned char *)(buf))[pos]) /* Non-const version of CVAL */
+#define PVAL(buf,pos) (CVAL(buf,pos))
+#define SCVAL(buf,pos,val) (CVAL_NC(buf,pos) = (val))
+
+
+#if CAREFUL_ALIGNMENT
+
+#define SVAL(buf,pos) (PVAL(buf,pos)|PVAL(buf,(pos)+1)<<8)
+#define IVAL(buf,pos) (SVAL(buf,pos)|SVAL(buf,(pos)+2)<<16)
+#define SSVALX(buf,pos,val) (CVAL_NC(buf,pos)=(unsigned char)((val)&0xFF),CVAL_NC(buf,pos+1)=(unsigned char)((val)>>8))
+#define SIVALX(buf,pos,val) (SSVALX(buf,pos,val&0xFFFF),SSVALX(buf,pos+2,val>>16))
+#define SVALS(buf,pos) ((int16)SVAL(buf,pos))
+#define IVALS(buf,pos) ((int32)IVAL(buf,pos))
+#define SSVAL(buf,pos,val) SSVALX((buf),(pos),((uint16)(val)))
+#define SIVAL(buf,pos,val) SIVALX((buf),(pos),((uint32)(val)))
+#define SSVALS(buf,pos,val) SSVALX((buf),(pos),((int16)(val)))
+#define SIVALS(buf,pos,val) SIVALX((buf),(pos),((int32)(val)))
+
+#else /* CAREFUL_ALIGNMENT */
+
+/* this handles things for architectures like the 386 that can handle
+ alignment errors */
+/*
+ WARNING: This section is dependent on the length of int16 and int32
+ being correct
+*/
+
+/* get single value from an SMB buffer */
+#define SVAL(buf,pos) (*(const uint16 *)((const char *)(buf) + (pos)))
+#define SVAL_NC(buf,pos) (*(uint16 *)((char *)(buf) + (pos))) /* Non const version of above. */
+#define IVAL(buf,pos) (*(const uint32 *)((const char *)(buf) + (pos)))
+#define IVAL_NC(buf,pos) (*(uint32 *)((char *)(buf) + (pos))) /* Non const version of above. */
+#define SVALS(buf,pos) (*(const int16 *)((const char *)(buf) + (pos)))
+#define SVALS_NC(buf,pos) (*(int16 *)((char *)(buf) + (pos))) /* Non const version of above. */
+#define IVALS(buf,pos) (*(const int32 *)((const char *)(buf) + (pos)))
+#define IVALS_NC(buf,pos) (*(int32 *)((char *)(buf) + (pos))) /* Non const version of above. */
+
+/* store single value in an SMB buffer */
+#define SSVAL(buf,pos,val) SVAL_NC(buf,pos)=((uint16)(val))
+#define SIVAL(buf,pos,val) IVAL_NC(buf,pos)=((uint32)(val))
+#define SSVALS(buf,pos,val) SVALS_NC(buf,pos)=((int16)(val))
+#define SIVALS(buf,pos,val) IVALS_NC(buf,pos)=((int32)(val))
+
+#endif /* CAREFUL_ALIGNMENT */
+
+/* now the reverse routines - these are used in nmb packets (mostly) */
+#define SREV(x) ((((x)&0xFF)<<8) | (((x)>>8)&0xFF))
+#define IREV(x) ((SREV(x)<<16) | (SREV((x)>>16)))
+
+#define RSVAL(buf,pos) SREV(SVAL(buf,pos))
+#define RSVALS(buf,pos) SREV(SVALS(buf,pos))
+#define RIVAL(buf,pos) IREV(IVAL(buf,pos))
+#define RIVALS(buf,pos) IREV(IVALS(buf,pos))
+#define RSSVAL(buf,pos,val) SSVAL(buf,pos,SREV(val))
+#define RSSVALS(buf,pos,val) SSVALS(buf,pos,SREV(val))
+#define RSIVAL(buf,pos,val) SIVAL(buf,pos,IREV(val))
+#define RSIVALS(buf,pos,val) SIVALS(buf,pos,IREV(val))
+
+/* Alignment macros. */
+#define ALIGN4(p,base) ((p) + ((4 - (PTR_DIFF((p), (base)) & 3)) & 3))
+#define ALIGN2(p,base) ((p) + ((2 - (PTR_DIFF((p), (base)) & 1)) & 1))
+
+#endif /* _BYTEORDER_H */
diff --git a/source/include/charset.h b/source/include/charset.h
new file mode 100644
index 00000000000..7a9b12ef55d
--- /dev/null
+++ b/source/include/charset.h
@@ -0,0 +1,127 @@
+/*
+ Unix SMB/CIFS implementation.
+ charset defines
+ Copyright (C) Andrew Tridgell 2001
+ Copyright (C) Jelmer Vernooij 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 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.
+*/
+
+/* this defines the charset types used in samba */
+typedef enum {CH_UCS2=0, CH_UNIX=1, CH_DISPLAY=2, CH_DOS=3, CH_UTF8=4} charset_t;
+
+#define NUM_CHARSETS 5
+
+/*
+ * for each charset we have a function that pushes from that charset to a ucs2
+ * buffer, and a function that pulls from ucs2 buffer to that charset.
+ * */
+
+struct charset_functions {
+ const char *name;
+ size_t (*pull)(void *, char **inbuf, size_t *inbytesleft,
+ char **outbuf, size_t *outbytesleft);
+ size_t (*push)(void *, char **inbuf, size_t *inbytesleft,
+ char **outbuf, size_t *outbytesleft);
+ struct charset_functions *prev, *next;
+};
+
+/*
+ * This is auxiliary struct used by source/script/gen-8-bit-gap.sh script
+ * during generation of an encoding table for charset module
+ * */
+
+struct charset_gap_table {
+ uint16 start;
+ uint16 end;
+ int32 idx;
+};
+
+/*
+ * Define stub for charset module which implements 8-bit encoding with gaps.
+ * Encoding tables for such module should be produced from glibc's CHARMAPs
+ * using script source/script/gen-8bit-gap.sh
+ * CHARSETNAME is CAPITALIZED charset name
+ *
+ * */
+#define SMB_GENERATE_CHARSET_MODULE_8_BIT_GAP(CHARSETNAME) \
+static size_t CHARSETNAME ## _push(void *cd, char **inbuf, size_t *inbytesleft, \
+ char **outbuf, size_t *outbytesleft) \
+{ \
+ while (*inbytesleft >= 2 && *outbytesleft >= 1) { \
+ int i; \
+ int done = 0; \
+ \
+ uint16 ch = SVAL(*inbuf,0); \
+ \
+ for (i=0; from_idx[i].start != 0xffff; i++) { \
+ if ((from_idx[i].start <= ch) && (from_idx[i].end >= ch)) { \
+ ((unsigned char*)(*outbuf))[0] = from_ucs2[from_idx[i].idx+ch]; \
+ (*inbytesleft) -= 2; \
+ (*outbytesleft) -= 1; \
+ (*inbuf) += 2; \
+ (*outbuf) += 1; \
+ done = 1; \
+ break; \
+ } \
+ } \
+ if (!done) { \
+ errno = EINVAL; \
+ return -1; \
+ } \
+ \
+ } \
+ \
+ if (*inbytesleft == 1) { \
+ errno = EINVAL; \
+ return -1; \
+ } \
+ \
+ if (*inbytesleft > 1) { \
+ errno = E2BIG; \
+ return -1; \
+ } \
+ \
+ return 0; \
+} \
+ \
+static size_t CHARSETNAME ## _pull(void *cd, char **inbuf, size_t *inbytesleft, \
+ char **outbuf, size_t *outbytesleft) \
+{ \
+ while (*inbytesleft >= 1 && *outbytesleft >= 2) { \
+ *(uint16*)(*outbuf) = to_ucs2[((unsigned char*)(*inbuf))[0]]; \
+ (*inbytesleft) -= 1; \
+ (*outbytesleft) -= 2; \
+ (*inbuf) += 1; \
+ (*outbuf) += 2; \
+ } \
+ \
+ if (*inbytesleft > 0) { \
+ errno = E2BIG; \
+ return -1; \
+ } \
+ \
+ return 0; \
+} \
+ \
+struct charset_functions CHARSETNAME ## _functions = \
+ {#CHARSETNAME, CHARSETNAME ## _pull, CHARSETNAME ## _push}; \
+ \
+NTSTATUS charset_ ## CHARSETNAME ## _init(void) \
+{ \
+ return smb_register_charset(& CHARSETNAME ## _functions); \
+} \
+
+
diff --git a/source/include/client.h b/source/include/client.h
new file mode 100644
index 00000000000..968b73f0b41
--- /dev/null
+++ b/source/include/client.h
@@ -0,0 +1,169 @@
+/*
+ Unix SMB/CIFS implementation.
+ SMB parameters and setup
+ Copyright (C) Andrew Tridgell 1992-1998
+ Copyright (C) Luke Kenneth Casson Leighton 1996-1998
+ Copyright (C) Jeremy Allison 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 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 _CLIENT_H
+#define _CLIENT_H
+
+/* the client asks for a smaller buffer to save ram and also to get more
+ overlap on the wire. This size gives us a nice read/write size, which
+ will be a multiple of the page size on almost any system */
+#define CLI_BUFFER_SIZE (0xFFFF)
+
+
+/*
+ * These definitions depend on smb.h
+ */
+
+typedef struct file_info
+{
+ SMB_BIG_UINT size;
+ uint16 mode;
+ uid_t uid;
+ gid_t gid;
+ /* these times are normally kept in GMT */
+ time_t mtime;
+ time_t atime;
+ time_t ctime;
+ pstring name;
+ char short_name[13*3]; /* the *3 is to cope with multi-byte */
+} file_info;
+
+struct print_job_info
+{
+ uint16 id;
+ uint16 priority;
+ size_t size;
+ fstring user;
+ fstring name;
+ time_t t;
+};
+
+struct cli_state {
+ int port;
+ int fd;
+ int smb_rw_error; /* Copy of last read or write error. */
+ uint16 cnum;
+ uint16 pid;
+ uint16 mid;
+ uint16 vuid;
+ int protocol;
+ int sec_mode;
+ int rap_error;
+ int privileges;
+
+ fstring desthost;
+ fstring user_name;
+ fstring domain;
+
+ /*
+ * The following strings are the
+ * ones returned by the server if
+ * the protocol > NT1.
+ */
+ fstring server_type;
+ fstring server_os;
+ fstring server_domain;
+
+ fstring share;
+ fstring dev;
+ struct nmb_name called;
+ struct nmb_name calling;
+ fstring full_dest_host_name;
+ struct in_addr dest_ip;
+
+ struct pwd_info pwd;
+ DATA_BLOB secblob; /* cryptkey or negTokenInit */
+ uint32 sesskey;
+ int serverzone;
+ uint32 servertime;
+ int readbraw_supported;
+ int writebraw_supported;
+ int timeout; /* in milliseconds. */
+ size_t max_xmit;
+ size_t max_mux;
+ char *outbuf;
+ char *inbuf;
+ unsigned int bufsize;
+ int initialised;
+ int win95;
+ uint32 capabilities;
+
+ TALLOC_CTX *mem_ctx;
+
+ smb_sign_info sign_info;
+
+ /* the session key for this CLI, outside
+ any per-pipe authenticaion */
+ DATA_BLOB user_session_key;
+
+ /*
+ * Only used in NT domain calls.
+ */
+
+ int pipe_idx; /* Index (into list of known pipes)
+ of the pipe we're talking to,
+ if any */
+
+ uint16 nt_pipe_fnum; /* Pipe handle. */
+
+ /* Secure pipe parameters */
+ int pipe_auth_flags;
+
+ uint16 saved_netlogon_pipe_fnum; /* The "first" pipe to get
+ the session key for the
+ schannel. */
+ struct netsec_auth_struct auth_info;
+
+ NTLMSSP_STATE *ntlmssp_pipe_state;
+
+ unsigned char sess_key[16]; /* Current session key. */
+ DOM_CRED clnt_cred; /* Client credential. */
+ fstring mach_acct; /* MYNAME$. */
+ fstring srv_name_slash; /* \\remote server. */
+ fstring clnt_name_slash; /* \\local client. */
+ uint16 max_xmit_frag;
+ uint16 max_recv_frag;
+
+ BOOL use_kerberos;
+ BOOL use_spnego;
+
+ BOOL use_oplocks; /* should we use oplocks? */
+ BOOL use_level_II_oplocks; /* should we use level II oplocks? */
+
+ /* a oplock break request handler */
+ BOOL (*oplock_handler)(struct cli_state *cli, int fnum, unsigned char level);
+
+ BOOL force_dos_errors;
+
+ /* was this structure allocated by cli_initialise? If so, then
+ free in cli_shutdown() */
+ BOOL allocated;
+
+ /* Name of the pipe we're talking to, if any */
+ fstring pipe_name;
+};
+
+#define CLI_FULL_CONNECTION_DONT_SPNEGO 0x0001
+#define CLI_FULL_CONNECTION_USE_KERBEROS 0x0002
+#define CLI_FULL_CONNECTION_ANNONYMOUS_FALLBACK 0x0004
+
+#endif /* _CLIENT_H */
diff --git a/source/include/clitar.h b/source/include/clitar.h
new file mode 100644
index 00000000000..b7731172d61
--- /dev/null
+++ b/source/include/clitar.h
@@ -0,0 +1,41 @@
+/*
+ * Unix SMB/CIFS implementation.
+ * clitar file format
+ * 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 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 _CLITAR_H
+#define _CLITAR_H
+
+#define TBLOCK 512
+#define NAMSIZ 100
+union hblock {
+ char dummy[TBLOCK];
+ struct header {
+ char name[NAMSIZ];
+ char mode[8];
+ char uid[8];
+ char gid[8];
+ char size[12];
+ char mtime[12];
+ char chksum[8];
+ char linkflag;
+ char linkname[NAMSIZ];
+ } dbuf;
+};
+
+#endif /* _CLITAR_H */
diff --git a/source/include/debug.h b/source/include/debug.h
new file mode 100644
index 00000000000..55f07d8a18e
--- /dev/null
+++ b/source/include/debug.h
@@ -0,0 +1,201 @@
+/*
+ Unix SMB/CIFS implementation.
+ SMB debug stuff
+ Copyright (C) Andrew Tridgell 1992-1998
+ Copyright (C) John H Terpstra 1996-1998
+ Copyright (C) Luke Kenneth Casson Leighton 1996-1998
+ Copyright (C) Paul Ashton 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 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 _DEBUG_H
+#define _DEBUG_H
+
+/* -------------------------------------------------------------------------- **
+ * Debugging code. See also debug.c
+ */
+
+/* mkproto.awk has trouble with ifdef'd function definitions (it ignores
+ * the #ifdef directive and will read both definitions, thus creating two
+ * diffferent prototype declarations), so we must do these by hand.
+ */
+/* I know the __attribute__ stuff is ugly, but it does ensure we get the
+ arguemnts to DEBUG() right. We have got them wrong too often in the
+ past.
+ The PRINTFLIKE comment does the equivalent for SGI MIPSPro.
+ */
+/* PRINTFLIKE1 */
+int Debug1( const char *, ... ) PRINTF_ATTRIBUTE(1,2);
+/* PRINTFLIKE1 */
+BOOL dbgtext( const char *, ... ) PRINTF_ATTRIBUTE(1,2);
+BOOL dbghdr( int level, const char *file, const char *func, int line );
+
+extern XFILE *dbf;
+extern pstring debugf;
+
+/* If we have these macros, we can add additional info to the header. */
+
+#ifdef HAVE_FUNCTION_MACRO
+#define FUNCTION_MACRO (__FUNCTION__)
+#else
+#define FUNCTION_MACRO ("")
+#endif
+
+/*
+ * Redefine DEBUGLEVEL because so we don't have to change every source file
+ * that *unnecessarily* references it. Source files neeed not extern reference
+ * DEBUGLEVEL, as it's extern in includes.h (which all source files include).
+ * Eventually, all these references should be removed, and all references to
+ * DEBUGLEVEL should be references to DEBUGLEVEL_CLASS[DBGC_ALL]. This could
+ * still be through a macro still called DEBUGLEVEL. This cannot be done now
+ * because some references would expand incorrectly.
+ */
+#define DEBUGLEVEL *debug_level
+extern int DEBUGLEVEL;
+
+/*
+ * Define all new debug classes here. A class is represented by an entry in
+ * the DEBUGLEVEL_CLASS array. Index zero of this arrray is equivalent to the
+ * old DEBUGLEVEL. Any source file that does NOT add the following lines:
+ *
+ * #undef DBGC_CLASS
+ * #define DBGC_CLASS DBGC_<your class name here>
+ *
+ * at the start of the file (after #include "includes.h") will default to
+ * using index zero, so it will behaive just like it always has.
+ */
+#define DBGC_ALL 0 /* index equivalent to DEBUGLEVEL */
+
+#define DBGC_TDB 1
+#define DBGC_PRINTDRIVERS 2
+#define DBGC_LANMAN 3
+#define DBGC_SMB 4
+#define DBGC_RPC_PARSE 5
+#define DBGC_RPC_SRV 6
+#define DBGC_RPC_CLI 7
+#define DBGC_PASSDB 8
+#define DBGC_SAM 9
+#define DBGC_AUTH 10
+#define DBGC_WINBIND 11
+#define DBGC_VFS 12
+#define DBGC_IDMAP 13
+#define DBGC_QUOTA 14
+
+/* So you can define DBGC_CLASS before including debug.h */
+#ifndef DBGC_CLASS
+#define DBGC_CLASS 0 /* override as shown above */
+#endif
+
+extern int *DEBUGLEVEL_CLASS;
+extern BOOL *DEBUGLEVEL_CLASS_ISSET;
+
+/* Debugging macros
+ *
+ * DEBUGLVL()
+ * If the 'file specific' debug class level >= level OR the system-wide
+ * DEBUGLEVEL (synomym for DEBUGLEVEL_CLASS[ DBGC_ALL ]) >= level then
+ * generate a header using the default macros for file, line, and
+ * function name. Returns True if the debug level was <= DEBUGLEVEL.
+ *
+ * Example: if( DEBUGLVL( 2 ) ) dbgtext( "Some text.\n" );
+ *
+ * DEBUGLVLC()
+ * If the 'macro specified' debug class level >= level OR the system-wide
+ * DEBUGLEVEL (synomym for DEBUGLEVEL_CLASS[ DBGC_ALL ]) >= level then
+ * generate a header using the default macros for file, line, and
+ * function name. Returns True if the debug level was <= DEBUGLEVEL.
+ *
+ * Example: if( DEBUGLVLC( DBGC_TDB, 2 ) ) dbgtext( "Some text.\n" );
+ *
+ * DEBUG()
+ * If the 'file specific' debug class level >= level OR the system-wide
+ * DEBUGLEVEL (synomym for DEBUGLEVEL_CLASS[ DBGC_ALL ]) >= level then
+ * generate a header using the default macros for file, line, and
+ * function name. Each call to DEBUG() generates a new header *unless* the
+ * previous debug output was unterminated (i.e. no '\n').
+ * See debug.c:dbghdr() for more info.
+ *
+ * Example: DEBUG( 2, ("Some text and a value %d.\n", value) );
+ *
+ * DEBUGC()
+ * If the 'macro specified' debug class level >= level OR the system-wide
+ * DEBUGLEVEL (synomym for DEBUGLEVEL_CLASS[ DBGC_ALL ]) >= level then
+ * generate a header using the default macros for file, line, and
+ * function name. Each call to DEBUG() generates a new header *unless* the
+ * previous debug output was unterminated (i.e. no '\n').
+ * See debug.c:dbghdr() for more info.
+ *
+ * Example: DEBUGC( DBGC_TDB, 2, ("Some text and a value %d.\n", value) );
+ *
+ * DEBUGADD(), DEBUGADDC()
+ * Same as DEBUG() and DEBUGC() except the text is appended to the previous
+ * DEBUG(), DEBUGC(), DEBUGADD(), DEBUGADDC() with out another interviening
+ * header.
+ *
+ * Example: DEBUGADD( 2, ("Some text and a value %d.\n", value) );
+ * DEBUGADDC( DBGC_TDB, 2, ("Some text and a value %d.\n", value) );
+ *
+ * Note: If the debug class has not be redeined (see above) then the optimizer
+ * will remove the extra conditional test.
+ */
+
+#define DEBUGLVL( level ) \
+ ( ((level) <= MAX_DEBUG_LEVEL) && \
+ ((DEBUGLEVEL_CLASS[ DBGC_CLASS ] >= (level))|| \
+ (!DEBUGLEVEL_CLASS_ISSET[ DBGC_CLASS ] && \
+ DEBUGLEVEL_CLASS[ DBGC_ALL ] >= (level)) ) \
+ && dbghdr( level, __FILE__, FUNCTION_MACRO, (__LINE__) ) )
+
+
+#define DEBUGLVLC( dbgc_class, level ) \
+ ( ((level) <= MAX_DEBUG_LEVEL) && \
+ ((DEBUGLEVEL_CLASS[ dbgc_class ] >= (level))|| \
+ (!DEBUGLEVEL_CLASS_ISSET[ dbgc_class ] && \
+ DEBUGLEVEL_CLASS[ DBGC_ALL ] >= (level)) ) \
+ && dbghdr( level, __FILE__, FUNCTION_MACRO, (__LINE__) ) )
+
+
+#define DEBUG( level, body ) \
+ (void)( ((level) <= MAX_DEBUG_LEVEL) && \
+ ((DEBUGLEVEL_CLASS[ DBGC_CLASS ] >= (level))|| \
+ (!DEBUGLEVEL_CLASS_ISSET[ DBGC_CLASS ] && \
+ DEBUGLEVEL_CLASS[ DBGC_ALL ] >= (level)) ) \
+ && (dbghdr( level, __FILE__, FUNCTION_MACRO, (__LINE__) )) \
+ && (dbgtext body) )
+
+#define DEBUGC( dbgc_class, level, body ) \
+ (void)( ((level) <= MAX_DEBUG_LEVEL) && \
+ ((DEBUGLEVEL_CLASS[ dbgc_class ] >= (level))|| \
+ (!DEBUGLEVEL_CLASS_ISSET[ dbgc_class ] && \
+ DEBUGLEVEL_CLASS[ DBGC_ALL ] >= (level)) ) \
+ && (dbghdr( level, __FILE__, FUNCTION_MACRO, (__LINE__) )) \
+ && (dbgtext body) )
+
+#define DEBUGADD( level, body ) \
+ (void)( ((level) <= MAX_DEBUG_LEVEL) && \
+ ((DEBUGLEVEL_CLASS[ DBGC_CLASS ] >= (level))|| \
+ (!DEBUGLEVEL_CLASS_ISSET[ DBGC_CLASS ] && \
+ DEBUGLEVEL_CLASS[ DBGC_ALL ] >= (level)) ) \
+ && (dbgtext body) )
+
+#define DEBUGADDC( dbgc_class, level, body ) \
+ (void)( ((level) <= MAX_DEBUG_LEVEL) && \
+ ((DEBUGLEVEL_CLASS[ dbgc_class ] >= (level))|| \
+ (!DEBUGLEVEL_CLASS_ISSET[ dbgc_class ] && \
+ DEBUGLEVEL_CLASS[ DBGC_ALL ] >= (level)) ) \
+ && (dbgtext body) )
+
+#endif
diff --git a/source/include/dlinklist.h b/source/include/dlinklist.h
new file mode 100644
index 00000000000..794aea75766
--- /dev/null
+++ b/source/include/dlinklist.h
@@ -0,0 +1,78 @@
+/*
+ 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 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.
+*/
+
+/* To use these macros you must have a structure containing a next and
+ prev pointer */
+
+
+/* hook into the front of the list */
+#define DLIST_ADD(list, p) \
+{ \
+ if (!(list)) { \
+ (list) = (p); \
+ (p)->next = (p)->prev = NULL; \
+ } else { \
+ (list)->prev = (p); \
+ (p)->next = (list); \
+ (p)->prev = NULL; \
+ (list) = (p); \
+ }\
+}
+
+/* remove an element from a list - element doesn't have to be in list. */
+#define DLIST_REMOVE(list, p) \
+{ \
+ 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; \
+}
+
+/* promote an element to the top of the list */
+#define DLIST_PROMOTE(list, p) \
+{ \
+ DLIST_REMOVE(list, p) \
+ DLIST_ADD(list, p) \
+}
+
+/* hook into the end of the list - needs a tmp pointer */
+#define DLIST_ADD_END(list, p, tmp) \
+{ \
+ if (!(list)) { \
+ (list) = (p); \
+ (p)->next = (p)->prev = NULL; \
+ } else { \
+ for ((tmp) = (list); (tmp)->next; (tmp) = (tmp)->next) ; \
+ (tmp)->next = (p); \
+ (p)->next = NULL; \
+ (p)->prev = (tmp); \
+ } \
+}
+
+/* demote an element to the top of the list, needs a tmp pointer */
+#define DLIST_DEMOTE(list, p, tmp) \
+{ \
+ DLIST_REMOVE(list, p) \
+ DLIST_ADD_END(list, p, tmp) \
+}
diff --git a/source/include/doserr.h b/source/include/doserr.h
new file mode 100644
index 00000000000..576aeda2bf7
--- /dev/null
+++ b/source/include/doserr.h
@@ -0,0 +1,233 @@
+/*
+ Unix SMB/CIFS implementation.
+ DOS error code constants
+ Copyright (C) Andrew Tridgell 1992-2000
+ Copyright (C) John H Terpstra 1996-2000
+ Copyright (C) Luke Kenneth Casson Leighton 1996-2000
+ Copyright (C) Paul Ashton 1998-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 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 _DOSERR_H
+#define _DOSERR_H
+
+/* Error classes */
+
+#define ERRDOS 0x01 /* Error is from the core DOS operating system set. */
+#define ERRSRV 0x02 /* Error is generated by the server network file manager.*/
+#define ERRHRD 0x03 /* Error is an hardware error. */
+#define ERRCMD 0xFF /* Command was not in the "SMB" format. */
+
+/* SMB X/Open error codes for the ERRDOS error class */
+#define ERRsuccess 0 /* No error */
+#define ERRbadfunc 1 /* Invalid function (or system call) */
+#define ERRbadfile 2 /* File not found (pathname error) */
+#define ERRbadpath 3 /* Directory not found */
+#define ERRnofids 4 /* Too many open files */
+#define ERRnoaccess 5 /* Access denied */
+#define ERRbadfid 6 /* Invalid fid */
+#define ERRbadmcb 7 /* Memory control blocks destroyed. */
+#define ERRnomem 8 /* Out of memory */
+#define ERRbadmem 9 /* Invalid memory block address */
+#define ERRbadenv 10 /* Invalid environment */
+#define ERRbadaccess 12 /* Invalid open mode */
+#define ERRbaddata 13 /* Invalid data (only from ioctl call) */
+#define ERRres 14 /* reserved */
+#define ERRbaddrive 15 /* Invalid drive */
+#define ERRremcd 16 /* Attempt to delete current directory */
+#define ERRdiffdevice 17 /* rename/move across different filesystems */
+#define ERRnofiles 18 /* no more files found in file search */
+#define ERRgeneral 31 /* General failure */
+#define ERRbadshare 32 /* Share mode on file conflict with open mode */
+#define ERRlock 33 /* Lock request conflicts with existing lock */
+#define ERRunsup 50 /* Request unsupported, returned by Win 95, RJS 20Jun98 */
+#define ERRnetnamedel 64 /* Network name deleted or not available */
+#define ERRnosuchshare 67 /* You specified an invalid share name */
+#define ERRfilexists 80 /* File in operation already exists */
+#define ERRinvalidparam 87
+#define ERRcannotopen 110 /* Cannot open the file specified */
+#define ERRinsufficientbuffer 122
+#define ERRinvalidname 123 /* Invalid name */
+#define ERRunknownlevel 124
+#define ERRnotlocked 158 /* This region is not locked by this locking context. */
+#define ERRrename 183
+#define ERRbadpipe 230 /* Named pipe invalid */
+#define ERRpipebusy 231 /* All instances of pipe are busy */
+#define ERRpipeclosing 232 /* named pipe close in progress */
+#define ERRnotconnected 233 /* No process on other end of named pipe */
+#define ERRmoredata 234 /* More data to be returned */
+#define ERRnomoreitems 259
+#define ERRbaddirectory 267 /* Invalid directory name in a path. */
+#define ERReasnotsupported 282 /* Extended attributes */
+#define ERRlogonfailure 1326 /* Unknown username or bad password */
+#define ERRbuftoosmall 2123
+#define ERRunknownipc 2142
+#define ERRnosuchprintjob 2151
+#define ERRinvgroup 2455
+
+/* here's a special one from observing NT */
+#define ERRnoipc 66 /* don't support ipc */
+
+/* These errors seem to be only returned by the NT printer driver system */
+#define ERRdriveralreadyinstalled 1795 /* ERROR_PRINTER_DRIVER_ALREADY_INSTALLED */
+#define ERRunknownprinterport 1796 /* ERROR_UNKNOWN_PORT */
+#define ERRunknownprinterdriver 1797 /* ERROR_UNKNOWN_PRINTER_DRIVER */
+#define ERRunknownprintprocessor 1798 /* ERROR_UNKNOWN_PRINTPROCESSOR */
+#define ERRinvalidseparatorfile 1799 /* ERROR_INVALID_SEPARATOR_FILE */
+#define ERRinvalidjobpriority 1800 /* ERROR_INVALID_PRIORITY */
+#define ERRinvalidprintername 1801 /* ERROR_INVALID_PRINTER_NAME */
+#define ERRprinteralreadyexists 1802 /* ERROR_PRINTER_ALREADY_EXISTS */
+#define ERRinvalidprintercommand 1803 /* ERROR_INVALID_PRINTER_COMMAND */
+#define ERRinvaliddatatype 1804 /* ERROR_INVALID_DATATYPE */
+#define ERRinvalidenvironment 1805 /* ERROR_INVALID_ENVIRONMENT */
+
+#define ERRunknownprintmonitor 3000 /* ERROR_UNKNOWN_PRINT_MONITOR */
+#define ERRprinterdriverinuse 3001 /* ERROR_PRINTER_DRIVER_IN_USE */
+#define ERRspoolfilenotfound 3002 /* ERROR_SPOOL_FILE_NOT_FOUND */
+#define ERRnostartdoc 3003 /* ERROR_SPL_NO_STARTDOC */
+#define ERRnoaddjob 3004 /* ERROR_SPL_NO_ADDJOB */
+#define ERRprintprocessoralreadyinstalled 3005 /* ERROR_PRINT_PROCESSOR_ALREADY_INSTALLED */
+#define ERRprintmonitoralreadyinstalled 3006 /* ERROR_PRINT_MONITOR_ALREADY_INSTALLED */
+#define ERRinvalidprintmonitor 3007 /* ERROR_INVALID_PRINT_MONITOR */
+#define ERRprintmonitorinuse 3008 /* ERROR_PRINT_MONITOR_IN_USE */
+#define ERRprinterhasjobsqueued 3009 /* ERROR_PRINTER_HAS_JOBS_QUEUED */
+
+/* Error codes for the ERRSRV class */
+
+#define ERRerror 1 /* Non specific error code */
+#define ERRbadpw 2 /* Bad password */
+#define ERRbadtype 3 /* reserved */
+#define ERRaccess 4 /* No permissions to do the requested operation */
+#define ERRinvnid 5 /* tid invalid */
+#define ERRinvnetname 6 /* Invalid servername */
+#define ERRinvdevice 7 /* Invalid device */
+#define ERRqfull 49 /* Print queue full */
+#define ERRqtoobig 50 /* Queued item too big */
+#define ERRinvpfid 52 /* Invalid print file in smb_fid */
+#define ERRsmbcmd 64 /* Unrecognised command */
+#define ERRsrverror 65 /* smb server internal error */
+#define ERRfilespecs 67 /* fid and pathname invalid combination */
+#define ERRbadlink 68 /* reserved */
+#define ERRbadpermits 69 /* Access specified for a file is not valid */
+#define ERRbadpid 70 /* reserved */
+#define ERRsetattrmode 71 /* attribute mode invalid */
+#define ERRpaused 81 /* Message server paused */
+#define ERRmsgoff 82 /* Not receiving messages */
+#define ERRnoroom 83 /* No room for message */
+#define ERRrmuns 87 /* too many remote usernames */
+#define ERRtimeout 88 /* operation timed out */
+#define ERRnoresource 89 /* No resources currently available for request. */
+#define ERRtoomanyuids 90 /* too many userids */
+#define ERRbaduid 91 /* bad userid */
+#define ERRuseMPX 250 /* temporarily unable to use raw mode, use MPX mode */
+#define ERRuseSTD 251 /* temporarily unable to use raw mode, use standard mode */
+#define ERRcontMPX 252 /* resume MPX mode */
+#define ERRbadPW /* reserved */
+#define ERRnosupport 0xFFFF
+#define ERRunknownsmb 22 /* from NT 3.5 response */
+
+/* Error codes for the ERRHRD class */
+
+#define ERRnowrite 19 /* read only media */
+#define ERRbadunit 20 /* Unknown device */
+#define ERRnotready 21 /* Drive not ready */
+#define ERRbadcmd 22 /* Unknown command */
+#define ERRdata 23 /* Data (CRC) error */
+#define ERRbadreq 24 /* Bad request structure length */
+#define ERRseek 25
+#define ERRbadmedia 26
+#define ERRbadsector 27
+#define ERRnopaper 28
+#define ERRwrite 29 /* write fault */
+#define ERRread 30 /* read fault */
+#define ERRgeneral 31 /* General hardware failure */
+#define ERRwrongdisk 34
+#define ERRFCBunavail 35
+#define ERRsharebufexc 36 /* share buffer exceeded */
+#define ERRdiskfull 39
+
+
+/* these are win32 error codes. There are only a few places where
+ these matter for Samba, primarily in the NT printing code */
+#define WERR_OK W_ERROR(0)
+#define WERR_BADFUNC W_ERROR(1)
+#define WERR_BADFILE W_ERROR(2)
+#define WERR_ACCESS_DENIED W_ERROR(5)
+#define WERR_BADFID W_ERROR(6)
+#define WERR_NOMEM W_ERROR(8)
+#define WERR_GENERAL_FAILURE W_ERROR(31)
+#define WERR_NOT_SUPPORTED W_ERROR(50)
+#define WERR_PRINTQ_FULL W_ERROR(61)
+#define WERR_NO_SPOOL_SPACE W_ERROR(62)
+#define WERR_NO_SUCH_SHARE W_ERROR(67)
+#define WERR_ALREADY_EXISTS W_ERROR(80)
+#define WERR_BAD_PASSWORD W_ERROR(86)
+#define WERR_INVALID_PARAM W_ERROR(87)
+#define WERR_INSUFFICIENT_BUFFER W_ERROR(122)
+#define WERR_INVALID_NAME W_ERROR(123)
+#define WERR_UNKNOWN_LEVEL W_ERROR(124)
+#define WERR_OBJECT_PATH_INVALID W_ERROR(161)
+#define WERR_NO_MORE_ITEMS W_ERROR(259)
+#define WERR_MORE_DATA W_ERROR(234)
+#define WERR_INVALID_OWNER W_ERROR(1307)
+#define WERR_CAN_NOT_COMPLETE W_ERROR(1003)
+#define WERR_INVALID_SECURITY_DESCRIPTOR W_ERROR(1338)
+#define WERR_SERVER_UNAVAILABLE W_ERROR(1722)
+#define WERR_INVALID_FORM_NAME W_ERROR(1902)
+#define WERR_INVALID_FORM_SIZE W_ERROR(1903)
+#define WERR_BUF_TOO_SMALL W_ERROR(2123)
+#define WERR_JOB_NOT_FOUND W_ERROR(2151)
+#define WERR_DEST_NOT_FOUND W_ERROR(2152)
+#define WERR_NOT_LOCAL_DOMAIN W_ERROR(2320)
+#define WERR_STATUS_MORE_ENTRIES W_ERROR(0x0105)
+
+#define WERR_PRINTER_DRIVER_ALREADY_INSTALLED W_ERROR(ERRdriveralreadyinstalled)
+#define WERR_UNKNOWN_PORT W_ERROR(ERRunknownprinterport)
+#define WERR_UNKNOWN_PRINTER_DRIVER W_ERROR(ERRunknownprinterdriver)
+#define WERR_UNKNOWN_PRINTPROCESSOR W_ERROR(ERRunknownprintprocessor)
+#define WERR_INVALID_SEPARATOR_FILE W_ERROR(ERRinvalidseparatorfile)
+#define WERR_INVALID_PRIORITY W_ERROR(ERRinvalidjobpriority)
+#define WERR_INVALID_PRINTER_NAME W_ERROR(ERRinvalidprintername)
+#define WERR_PRINTER_ALREADY_EXISTS W_ERROR(ERRprinteralreadyexists)
+#define WERR_INVALID_PRINTER_COMMAND W_ERROR(ERRinvalidprintercommand)
+#define WERR_INVALID_DATATYPE W_ERROR(ERRinvaliddatatype)
+#define WERR_INVALID_ENVIRONMENT W_ERROR(ERRinvalidenvironment)
+
+#define WERR_UNKNOWN_PRINT_MONITOR W_ERROR(ERRunknownprintmonitor)
+#define WERR_PRINTER_DRIVER_IN_USE W_ERROR(ERRprinterdriverinuse)
+#define WERR_SPOOL_FILE_NOT_FOUND W_ERROR(ERRspoolfilenotfound)
+#define WERR_SPL_NO_STARTDOC W_ERROR(ERRnostartdoc)
+#define WERR_SPL_NO_ADDJOB W_ERROR(ERRnoaddjob)
+#define WERR_PRINT_PROCESSOR_ALREADY_INSTALLED W_ERROR(ERRprintprocessoralreadyinstalled)
+#define WERR_PRINT_MONITOR_ALREADY_INSTALLED W_ERROR(ERRprintmonitoralreadyinstalled)
+#define WERR_INVALID_PRINT_MONITOR W_ERROR(ERRinvalidprintmonitor)
+#define WERR_PRINT_MONITOR_IN_USE W_ERROR(ERRprintmonitorinuse)
+#define WERR_PRINTER_HAS_JOBS_QUEUED W_ERROR(ERRprinterhasjobsqueued)
+
+
+/* DFS errors */
+
+#ifndef NERR_BASE
+#define NERR_BASE (2100)
+#endif
+
+#define WERR_DFS_NO_SUCH_VOL W_ERROR(NERR_BASE+562)
+#define WERR_DFS_NO_SUCH_SHARE W_ERROR(NERR_BASE+565)
+#define WERR_DFS_NO_SUCH_SERVER W_ERROR(NERR_BASE+573)
+#define WERR_DFS_INTERNAL_ERROR W_ERROR(NERR_BASE+590)
+#define WERR_DFS_CANT_CREATE_JUNCT W_ERROR(NERR_BASE+569)
+
+#endif /* _DOSERR_H */
diff --git a/source/include/dynconfig.h b/source/include/dynconfig.h
new file mode 100644
index 00000000000..a74d77e41f7
--- /dev/null
+++ b/source/include/dynconfig.h
@@ -0,0 +1,38 @@
+/*
+ Unix SMB/CIFS implementation.
+ Copyright (C) 2001 by Martin Pool <mbp@samba.org>
+ Copyright (C) 2003 by Jim McDonough <jmcd@us.ibm.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 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.
+*/
+
+/**
+ * @file dynconfig.h
+ *
+ * @brief Exported global configurations.
+ **/
+
+extern char const *dyn_SBINDIR,
+ *dyn_BINDIR,
+ *dyn_SWATDIR;
+
+extern pstring dyn_CONFIGFILE;
+extern pstring dyn_LOGFILEBASE, dyn_LMHOSTSFILE;
+extern pstring dyn_LIBDIR;
+extern const fstring dyn_SHLIBEXT;
+extern const pstring dyn_LOCKDIR;
+extern const pstring dyn_PIDDIR;
+extern const pstring dyn_SMB_PASSWD_FILE;
+extern const pstring dyn_PRIVATE_DIR;
diff --git a/source/include/fake_file.h b/source/include/fake_file.h
new file mode 100644
index 00000000000..3fe60072e9e
--- /dev/null
+++ b/source/include/fake_file.h
@@ -0,0 +1,46 @@
+/*
+ Unix SMB/CIFS implementation.
+ FAKE FILE suppport, for faking up special files windows want access to
+ 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 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 _FAKE_FILE_H
+#define _FAKE_FILE_H
+
+enum FAKE_FILE_TYPE {
+ FAKE_FILE_TYPE_NONE = 0,
+ FAKE_FILE_TYPE_QUOTA
+};
+
+#define FAKE_FILE_NAME_QUOTA "\\$Extend\\$Quota:$Q:$INDEX_ALLOCATION"
+
+typedef struct _FAKE_FILE_HANDLE {
+ enum FAKE_FILE_TYPE type;
+ TALLOC_CTX *mem_ctx;
+ void *pd; /* for private data */
+ void (*free_pd)(void **pd); /* free private_data */
+} FAKE_FILE_HANDLE;
+
+typedef struct _FAKE_FILE {
+ const char *name;
+ enum FAKE_FILE_TYPE type;
+ void *(*init_pd)(TALLOC_CTX *men_ctx);
+ void (*free_pd)(void **pd);
+} FAKE_FILE;
+
+
+#endif /* _FAKE_FILE_H */
diff --git a/source/include/genparser.h b/source/include/genparser.h
new file mode 100644
index 00000000000..f28cd78249d
--- /dev/null
+++ b/source/include/genparser.h
@@ -0,0 +1,78 @@
+/*
+ Copyright (C) Andrew Tridgell <genstruct@tridgell.net> 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 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 _GENPARSER_H
+#define _GENPARSER_H
+
+/* these macros are needed for genstruct auto-parsers */
+#ifndef GENSTRUCT
+#define GENSTRUCT
+#define _LEN(x)
+#define _NULLTERM
+#endif
+
+/*
+ automatic marshalling/unmarshalling system for C structures
+*/
+
+/* flag to mark a fixed size array as actually being null terminated */
+#define FLAG_NULLTERM 1
+#define FLAG_ALWAYS 2
+
+struct enum_struct {
+ const char *name;
+ unsigned value;
+};
+
+/* intermediate dumps are stored in one of these */
+struct parse_string {
+ unsigned allocated;
+ unsigned length;
+ char *s;
+};
+
+typedef int (*gen_dump_fn)(TALLOC_CTX *, struct parse_string *, const char *ptr, unsigned indent);
+typedef int (*gen_parse_fn)(TALLOC_CTX *, char *ptr, const char *str);
+
+/* genstruct.pl generates arrays of these */
+struct parse_struct {
+ const char *name;
+ unsigned ptr_count;
+ unsigned size;
+ unsigned offset;
+ unsigned array_len;
+ const char *dynamic_len;
+ unsigned flags;
+ gen_dump_fn dump_fn;
+ gen_parse_fn parse_fn;
+};
+
+#define DUMP_PARSE_DECL(type) \
+ int gen_dump_ ## type(TALLOC_CTX *, struct parse_string *, const char *, unsigned); \
+ int gen_parse_ ## type(TALLOC_CTX *, char *, const char *);
+
+DUMP_PARSE_DECL(char)
+DUMP_PARSE_DECL(int)
+DUMP_PARSE_DECL(unsigned)
+DUMP_PARSE_DECL(double)
+DUMP_PARSE_DECL(float)
+
+#define gen_dump_unsigned_char gen_dump_char
+#define gen_parse_unsigned_char gen_parse_char
+
+#endif /* _GENPARSER_H */
diff --git a/source/include/genparser_samba.h b/source/include/genparser_samba.h
new file mode 100644
index 00000000000..213d51da876
--- /dev/null
+++ b/source/include/genparser_samba.h
@@ -0,0 +1,63 @@
+/*
+ Copyright (C) Simo Sorce <idra@samba.org> 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 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 _GENPARSER_SAMBA_H
+#define _GENPARSER_SAMBA_H
+
+const struct parse_struct pinfo_security_ace_info[] = {
+{"type", 0, sizeof(uint8), offsetof(struct security_ace_info, type), 0, NULL, 0, gen_dump_uint8, gen_parse_uint8},
+{"flags", 0, sizeof(uint8), offsetof(struct security_ace_info, flags), 0, NULL, 0, gen_dump_uint8, gen_parse_uint8},
+{"size", 0, sizeof(uint16), offsetof(struct security_ace_info, size), 0, NULL, 0, gen_dump_uint16, gen_parse_uint16},
+{"info", 0, sizeof(char), offsetof(struct security_ace_info, info), 0, NULL, 0, gen_dump_SEC_ACCESS, gen_parse_SEC_ACCESS},
+{"obj_flags", 0, sizeof(uint32), offsetof(struct security_ace_info, obj_flags), 0, NULL, 0, gen_dump_uint32, gen_parse_uint32},
+{"obj_guid", 0, sizeof(char), offsetof(struct security_ace_info, obj_guid), 0, NULL, 0, gen_dump_GUID, gen_parse_GUID},
+{"inh_guid", 0, sizeof(char), offsetof(struct security_ace_info, inh_guid), 0, NULL, 0, gen_dump_GUID, gen_parse_GUID},
+{"trustee", 0, sizeof(char), offsetof(struct security_ace_info, trustee), 0, NULL, 0, gen_dump_DOM_SID, gen_parse_DOM_SID},
+{NULL, 0, 0, 0, 0, NULL, 0, NULL, NULL}};
+
+const struct parse_struct pinfo_security_acl_info[] = {
+{"revision", 0, sizeof(uint16), offsetof(struct security_acl_info, revision), 0, NULL, 0, gen_dump_uint16, gen_parse_uint16},
+{"size", 0, sizeof(uint16), offsetof(struct security_acl_info, size), 0, NULL, 0, gen_dump_uint16, gen_parse_uint16},
+{"num_aces", 0, sizeof(uint32), offsetof(struct security_acl_info, num_aces), 0, NULL, 0, gen_dump_uint32, gen_parse_uint32},
+{"ace", 1, sizeof(struct security_ace_info), offsetof(struct security_acl_info, ace), 0, "size", 0, gen_dump_SEC_ACE, gen_parse_SEC_ACE},
+{NULL, 0, 0, 0, 0, NULL, 0, NULL, NULL}};
+
+const struct parse_struct pinfo_security_descriptor_info[] = {
+{"revision", 0, sizeof(uint16), offsetof(struct security_descriptor_info, revision), 0, NULL, 0, gen_dump_uint16, gen_parse_uint16},
+{"type", 0, sizeof(uint16), offsetof(struct security_descriptor_info, type), 0, NULL, 0, gen_dump_uint16, gen_parse_uint16},
+{"off_owner_sid", 0, sizeof(uint32), offsetof(struct security_descriptor_info, off_owner_sid), 0, NULL, 0, gen_dump_uint32, gen_parse_uint32},
+{"off_grp_sid", 0, sizeof(uint32), offsetof(struct security_descriptor_info, off_grp_sid), 0, NULL, 0, gen_dump_uint32, gen_parse_uint32},
+{"off_sacl", 0, sizeof(uint32), offsetof(struct security_descriptor_info, off_sacl), 0, NULL, 0, gen_dump_uint32, gen_parse_uint32},
+{"off_dacl", 0, sizeof(uint32), offsetof(struct security_descriptor_info, off_dacl), 0, NULL, 0, gen_dump_uint32, gen_parse_uint32},
+{"dacl", 1, sizeof(struct security_acl_info), offsetof(struct security_descriptor_info, dacl), 0, NULL, 0, gen_dump_SEC_ACL, gen_parse_SEC_ACL},
+{"sacl", 1, sizeof(struct security_acl_info), offsetof(struct security_descriptor_info, sacl), 0, NULL, 0, gen_dump_SEC_ACL, gen_parse_SEC_ACL},
+{"owner_sid", 1, sizeof(char), offsetof(struct security_descriptor_info, owner_sid), 0, NULL, 0, gen_dump_DOM_SID, gen_parse_DOM_SID},
+{"grp_sid", 1, sizeof(char), offsetof(struct security_descriptor_info, grp_sid), 0, NULL, 0, gen_dump_DOM_SID, gen_parse_DOM_SID},
+{NULL, 0, 0, 0, 0, NULL, 0, NULL, NULL}};
+
+const struct parse_struct pinfo_luid_attr_info[] = {
+{"attr", 0, sizeof(uint32), offsetof(struct LUID_ATTR, attr), 0, NULL, 0, gen_dump_uint32, gen_parse_uint32},
+{"luid", 1, sizeof(LUID), offsetof(struct LUID_ATTR, luid), 0, NULL, 0, gen_dump_LUID, gen_parse_LUID},
+{NULL, 0, 0, 0, 0, NULL, 0, NULL, NULL}};
+
+const struct parse_struct pinfo_data_blob_info[] = {
+{"length", 0, sizeof(int), offsetof(DATA_BLOB, length), 0, NULL, 0, gen_dump_int, gen_parse_int},
+{"data", 1, sizeof(char), offsetof(DATA_BLOB, data), 0, "length", 0, gen_dump_char, gen_parse_char},
+{NULL, 0, 0, 0, 0, NULL, 0, NULL, NULL}};
+
+#endif /* _GENPARSER_SAMBA_H */
diff --git a/source/include/gums.h b/source/include/gums.h
new file mode 100644
index 00000000000..d16a839bc4b
--- /dev/null
+++ b/source/include/gums.h
@@ -0,0 +1,272 @@
+/*
+ Unix SMB/CIFS implementation.
+ GUMS structures
+ Copyright (C) Simo Sorce 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 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 _GUMS_H
+#define _GUMS_H
+
+#define GUMS_VERSION_MAJOR 0
+#define GUMS_VERSION_MINOR 1
+#define GUMS_OBJECT_VERSION 1
+#define GUMS_PRIVILEGE_VERSION 1
+#define GUMS_INTERFACE_VERSION 1
+
+#define GUMS_OBJ_DOMAIN 0x10
+#define GUMS_OBJ_NORMAL_USER 0x20
+#define GUMS_OBJ_GROUP 0x30
+#define GUMS_OBJ_ALIAS 0x31
+
+/* define value types */
+#define GUMS_SET_PRIMARY_GROUP 0x1
+#define GUMS_SET_SEC_DESC 0x2
+
+#define GUMS_SET_NAME 0x10
+#define GUMS_SET_DESCRIPTION 0x11
+#define GUMS_SET_FULL_NAME 0x12
+
+/* user specific type values */
+#define GUMS_SET_LOGON_TIME 0x20
+#define GUMS_SET_LOGOFF_TIME 0x21
+#define GUMS_SET_KICKOFF_TIME 0x23
+#define GUMS_SET_PASS_LAST_SET_TIME 0x24
+#define GUMS_SET_PASS_CAN_CHANGE_TIME 0x25
+#define GUMS_SET_PASS_MUST_CHANGE_TIME 0x26
+
+
+#define GUMS_SET_HOME_DIRECTORY 0x31
+#define GUMS_SET_DRIVE 0x32
+#define GUMS_SET_LOGON_SCRIPT 0x33
+#define GUMS_SET_PROFILE_PATH 0x34
+#define GUMS_SET_WORKSTATIONS 0x35
+#define GUMS_SET_UNKNOWN_STRING 0x36
+#define GUMS_SET_MUNGED_DIAL 0x37
+
+#define GUMS_SET_LM_PASSWORD 0x40
+#define GUMS_SET_NT_PASSWORD 0x41
+#define GUMS_SET_PLAINTEXT_PASSWORD 0x42
+#define GUMS_SET_UNKNOWN_3 0x43
+#define GUMS_SET_LOGON_DIVS 0x44
+#define GUMS_SET_HOURS_LEN 0x45
+#define GUMS_SET_HOURS 0x46
+#define GUMS_SET_BAD_PASSWORD_COUNT 0x47
+#define GUMS_SET_LOGON_COUNT 0x48
+#define GUMS_SET_UNKNOWN_6 0x49
+
+#define GUMS_SET_MUST_CHANGE_PASS 0x50
+#define GUMS_SET_CANNOT_CHANGE_PASS 0x51
+#define GUMS_SET_PASS_NEVER_EXPIRE 0x52
+#define GUMS_SET_ACCOUNT_DISABLED 0x53
+#define GUMS_SET_ACCOUNT_LOCKOUT 0x54
+
+/*group specific type values */
+#define GUMS_ADD_SID_LIST 0x60
+#define GUMS_DEL_SID_LIST 0x61
+#define GUMS_SET_SID_LIST 0x62
+
+GENSTRUCT struct gums_user
+{
+ DOM_SID *group_sid; /* Primary Group SID */
+
+ NTTIME logon_time; /* logon time */
+ NTTIME logoff_time; /* logoff time */
+ NTTIME kickoff_time; /* kickoff time */
+ NTTIME pass_last_set_time; /* password last set time */
+ NTTIME pass_can_change_time; /* password can change time */
+ NTTIME pass_must_change_time; /* password must change time */
+
+ char *full_name; _NULLTERM /* user's full name string */
+ char *home_dir; _NULLTERM /* home directory string */
+ char *dir_drive; _NULLTERM /* home directory drive string */
+ char *logon_script; _NULLTERM /* logon script string */
+ char *profile_path; _NULLTERM /* profile path string */
+ char *workstations; _NULLTERM /* login from workstations string */
+ char *unknown_str; _NULLTERM /* don't know what this is, yet. */
+ char *munged_dial; _NULLTERM /* munged path name and dial-back tel number */
+
+ DATA_BLOB lm_pw; /* .data is Null if no password */
+ DATA_BLOB nt_pw; /* .data is Null if no password */
+
+ uint16 acct_ctrl; /* account type & status flags */
+ uint16 logon_divs; /* 168 - number of hours in a week */
+ uint32 hours_len; /* normally 21 bytes */
+ uint8 *hours; _LEN(hours_len) /* normally 21 bytes (depends on hours_len) */
+
+ uint16 bad_password_count; /* 0 */
+ uint16 logon_count; /* 0 */
+ uint32 unknown_3; /* 0x00ff ffff */
+ uint32 unknown_6; /* 0x0000 04ec */
+
+};
+
+GENSTRUCT struct gums_group
+{
+ uint32 count; /* Number of SIDs */
+ DOM_SID *members; _LEN(count) /* SID array */
+
+};
+
+GENSTRUCT struct gums_domain
+{
+ uint32 next_rid;
+
+};
+
+GENSTRUCT struct gums_object
+{
+ TALLOC_CTX *mem_ctx;
+
+ uint32 type; /* Object Type */
+ uint32 version; /* Object Version */
+ uint32 seq_num; /* Object Sequence Number */
+
+ SEC_DESC *sec_desc; /* Security Descriptor */
+
+ DOM_SID *sid; /* Object Sid */
+ char *name; _NULLTERM /* Object Name - it should be in DOMAIN\NAME format */
+ char *description; _NULLTERM /* Object Description */
+
+ struct gums_user *user;
+ struct gums_group *group;
+ struct gums_domain *domain;
+
+};
+
+GENSTRUCT struct gums_privilege
+{
+ TALLOC_CTX *mem_ctx;
+
+ uint32 version; /* Object Version */
+ uint32 seq_num; /* Object Sequence Number */
+
+ char *name; _NULLTERM /* Object Name */
+ char *description; _NULLTERM /* Object Description */
+
+ LUID_ATTR *privilege; /* Privilege Type */
+
+ uint32 count;
+ DOM_SID *members; _LEN(count)
+
+};
+
+typedef struct gums_user GUMS_USER;
+typedef struct gums_group GUMS_GROUP;
+typedef struct gums_domain GUMS_DOMAIN;
+typedef struct gums_object GUMS_OBJECT;
+typedef struct gums_privilege GUMS_PRIVILEGE;
+
+typedef struct gums_data_set
+{
+ int type; /* GUMS_SET_xxx */
+ void *data;
+
+} GUMS_DATA_SET;
+
+typedef struct gums_commit_set
+{
+ TALLOC_CTX *mem_ctx;
+
+ uint32 type; /* Object type */
+ DOM_SID sid; /* Object Sid */
+ uint32 count; /* number of changes */
+ GUMS_DATA_SET *data;
+
+} GUMS_COMMIT_SET;
+
+typedef struct gums_priv_commit_set
+{
+ TALLOC_CTX *mem_ctx;
+
+ uint32 type; /* Object type */
+ char *name; /* Object Sid */
+ uint32 count; /* number of changes */
+ GUMS_DATA_SET *data;
+
+} GUMS_PRIV_COMMIT_SET;
+
+
+typedef struct gums_functions
+{
+ /* module data */
+ TALLOC_CTX *mem_ctx;
+ char *name;
+ void *private_data;
+ void (*free_private_data)(void **);
+
+ /* Generic object functions */
+
+ NTSTATUS (*get_domain_sid) (DOM_SID *sid, const char* name);
+ NTSTATUS (*set_domain_sid) (const DOM_SID *sid);
+
+ NTSTATUS (*get_sequence_number) (void);
+
+ NTSTATUS (*new_object) (DOM_SID *sid, const char *name, const int obj_type);
+ NTSTATUS (*delete_object) (const DOM_SID *sid);
+
+ NTSTATUS (*get_object_from_sid) (GUMS_OBJECT **object, const DOM_SID *sid, const int obj_type);
+ NTSTATUS (*get_object_from_name) (GUMS_OBJECT **object, const char *domain, const char *name, const int obj_type);
+ /* This function is used to get the list of all objects changed since b_time, it is
+ used to support PDC<->BDC synchronization */
+ NTSTATUS (*get_updated_objects) (GUMS_OBJECT **objects, const NTTIME base_time);
+
+ NTSTATUS (*enumerate_objects_start) (void **handle, const DOM_SID *sid, const int obj_type);
+ NTSTATUS (*enumerate_objects_get_next) (GUMS_OBJECT **object, void *handle);
+ NTSTATUS (*enumerate_objects_stop) (void *handle);
+
+ /* This function MUST be used ONLY by PDC<->BDC replication code or recovery tools.
+ Never use this function to update an object in the database, use set_object_values() */
+ NTSTATUS (*set_object) (GUMS_OBJECT *object);
+
+ /* set object values function */
+ NTSTATUS (*set_object_values) (DOM_SID *sid, uint32 count, GUMS_DATA_SET *data_set);
+
+ /* Group related functions */
+ NTSTATUS (*add_members_to_group) (const DOM_SID *group, const DOM_SID **members);
+ NTSTATUS (*delete_members_from_group) (const DOM_SID *group, const DOM_SID **members);
+ NTSTATUS (*enumerate_group_members) (DOM_SID **members, const DOM_SID *sid, const int type);
+
+ NTSTATUS (*get_sid_groups) (DOM_SID **groups, const DOM_SID *sid);
+
+ NTSTATUS (*lock_sid) (const DOM_SID *sid);
+ NTSTATUS (*unlock_sid) (const DOM_SID *sid);
+
+ /* privileges related functions */
+
+ NTSTATUS (*get_privilege) (GUMS_OBJECT **object, const char *name);
+ NTSTATUS (*add_members_to_privilege) (const char *name, const DOM_SID **members);
+ NTSTATUS (*delete_members_from_privilege) (const char *name, const DOM_SID **members);
+ NTSTATUS (*enumerate_privilege_members) (const char *name, DOM_SID **members);
+ NTSTATUS (*get_sid_privileges) (const DOM_SID *sid, const char **privs);
+
+ /* warning!: set_privilege will overwrite a prior existing privilege if such exist */
+ NTSTATUS (*set_privilege) (GUMS_PRIVILEGE *priv);
+
+} GUMS_FUNCTIONS;
+
+typedef NTSTATUS (*gums_init_function)(
+ struct gums_functions *,
+ const char *);
+
+struct gums_init_function_entry {
+
+ const char *name;
+ gums_init_function init_fn;
+ struct gums_init_function_entry *prev, *next;
+};
+
+#endif /* _GUMS_H */
diff --git a/source/include/hash.h b/source/include/hash.h
new file mode 100644
index 00000000000..40cc8b7cab3
--- /dev/null
+++ b/source/include/hash.h
@@ -0,0 +1,74 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ Copyright (C) Ying Chen 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 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 _HASH_H_
+#define _HASH_H_
+
+#define MAX_HASH_TABLE_SIZE 16384
+#define HASH_TABLE_INCREMENT 2
+
+typedef int (*compare_function)(char *, char *);
+typedef int (*hash_function)(int, char *);
+
+/*
+ * lru_link: links the node to the LRU list.
+ * hash_elem: the pointer to the element that is tied onto the link.
+ */
+typedef struct lru_node {
+ ubi_dlNode lru_link;
+ void *hash_elem;
+} lru_node;
+
+/*
+ * bucket_link: link the hash element to the bucket chain that it belongs to.
+ * lru_link: this element ties the hash element to the lru list.
+ * bucket: a pointer to the hash bucket that this element belongs to.
+ * value: a pointer to the hash element content. It can be anything.
+ * key: stores the string key. The hash_element is always allocated with
+ * more memory space than the structure shown below to accomodate the space
+ * used for the whole string. But the memory is always appended at the
+ * end of the structure, so keep "key" at the end of the structure.
+ * Don't move it.
+ */
+typedef struct hash_element {
+ ubi_dlNode bucket_link;
+ lru_node lru_link;
+ ubi_dlList *bucket;
+ void *value;
+ char key[1];
+} hash_element;
+
+/*
+ * buckets: a list of buckets, implemented as a dLinkList.
+ * lru_chain: the lru list of all the hash elements.
+ * num_elements: the # of elements in the hash table.
+ * size: the hash table size.
+ * comp_func: the compare function used during hash key comparisons.
+ */
+
+typedef struct hash_table {
+ ubi_dlList *buckets;
+ ubi_dlList lru_chain;
+ unsigned num_elements;
+ unsigned size;
+ compare_function comp_func;
+} hash_table;
+
+#endif /* _HASH_H_ */
diff --git a/source/include/hmacmd5.h b/source/include/hmacmd5.h
new file mode 100644
index 00000000000..6b53a6fd074
--- /dev/null
+++ b/source/include/hmacmd5.h
@@ -0,0 +1,32 @@
+/*
+ Unix SMB/CIFS implementation.
+ Interface header: Scheduler service
+ Copyright (C) Luke Kenneth Casson Leighton 1996-1999
+ Copyright (C) Andrew Tridgell 1992-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 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 _HMAC_MD5_H
+
+typedef struct
+{
+ struct MD5Context ctx;
+ uchar k_ipad[65];
+ uchar k_opad[65];
+
+} HMACMD5Context;
+
+#endif /* _HMAC_MD5_H */
diff --git a/source/include/idmap.h b/source/include/idmap.h
new file mode 100644
index 00000000000..20b1015285e
--- /dev/null
+++ b/source/include/idmap.h
@@ -0,0 +1,57 @@
+#ifndef _IDMAP_H_
+#define _IDMAP_H_
+/*
+ Unix SMB/CIFS implementation.
+
+ Idmap headers
+
+ Copyright (C) Jim McDonough <jmcd@us.ibm.com> 2003
+ Copyright (C) Simo Sorce 2003
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 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 Library General Public
+ License along with this library; if not, write to the
+ Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+*/
+
+#define SMB_IDMAP_INTERFACE_VERSION 2
+
+
+#define ID_EMPTY 0x00
+#define ID_USERID 0x01
+#define ID_GROUPID 0x02
+#define ID_OTHER 0x04
+
+#define ID_TYPEMASK 0x0f
+
+#define ID_QUERY_ONLY 0x10
+
+/* Filled out by IDMAP backends */
+struct idmap_methods {
+
+ /* Called when backend is first loaded */
+ NTSTATUS (*init)( char *params );
+
+ NTSTATUS (*allocate_rid)(uint32 *rid, int rid_type);
+ NTSTATUS (*allocate_id)(unid_t *id, int id_type);
+ NTSTATUS (*get_sid_from_id)(DOM_SID *sid, unid_t id, int id_type);
+ NTSTATUS (*get_id_from_sid)(unid_t *id, int *id_type, const DOM_SID *sid);
+ NTSTATUS (*set_mapping)(const DOM_SID *sid, unid_t id, int id_type);
+
+ /* Called when backend is unloaded */
+ NTSTATUS (*close)(void);
+
+ /* Called to dump backend status */
+ void (*status)(void);
+};
+#endif /* _IDMAP_H_ */
diff --git a/source/include/includes.h b/source/include/includes.h
new file mode 100644
index 00000000000..dd93c813d3d
--- /dev/null
+++ b/source/include/includes.h
@@ -0,0 +1,1333 @@
+#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 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 NO_CONFIG_H /* for some tests */
+#include "config.h"
+#endif
+
+#include "local.h"
+
+#ifdef AIX
+#define DEFAULT_PRINTING PRINT_AIX
+#define PRINTCAP_NAME "/etc/qconfig"
+#endif
+
+#ifdef HPUX
+#define DEFAULT_PRINTING PRINT_HPUX
+#endif
+
+#ifdef QNX
+#define DEFAULT_PRINTING PRINT_QNX
+#endif
+
+#ifdef SUNOS4
+/* on SUNOS4 termios.h conflicts with sys/ioctl.h */
+#undef HAVE_TERMIOS_H
+#endif
+
+#ifdef __GNUC__
+/** Use gcc attribute to check printf fns. a1 is the 1-based index of
+ * the parameter containing the format, and a2 the index of the first
+ * argument. **/
+#define PRINTF_ATTRIBUTE(a1, a2) __attribute__ ((format (__printf__, a1, a2)))
+#else
+#define PRINTF_ATTRIBUTE(a1, a2)
+#endif
+
+#ifdef __GNUC__
+/** gcc attribute used on function parameters so that it does not emit
+ * warnings about them being unused. **/
+# define UNUSED(param) param __attribute__ ((unused))
+#else
+# define UNUSED(param) param
+/** Feel free to add definitions for other compilers here. */
+#endif
+
+#ifdef RELIANTUNIX
+/*
+ * <unistd.h> has to be included before any other to get
+ * large file support on Reliant UNIX. Yes, it's broken :-).
+ */
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#endif /* RELIANTUNIX */
+
+#include <sys/types.h>
+
+#ifdef TIME_WITH_SYS_TIME
+#include <sys/time.h>
+#include <time.h>
+#else
+#ifdef HAVE_SYS_TIME_H
+#include <sys/time.h>
+#else
+#include <time.h>
+#endif
+#endif
+
+#ifdef HAVE_SYS_RESOURCE_H
+#include <sys/resource.h>
+#endif
+
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#include <stdio.h>
+#include <stddef.h>
+
+#ifdef HAVE_SYS_PARAM_H
+#include <sys/param.h>
+#endif
+
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+
+#ifdef HAVE_SYS_SOCKET_H
+#include <sys/socket.h>
+#endif
+
+#ifdef HAVE_UNIXSOCKET
+#include <sys/un.h>
+#endif
+
+#ifdef HAVE_SYS_SYSCALL_H
+#include <sys/syscall.h>
+#elif HAVE_SYSCALL_H
+#include <syscall.h>
+#endif
+
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+
+#ifdef HAVE_STRINGS_H
+#include <strings.h>
+#endif
+
+#ifdef HAVE_MEMORY_H
+#include <memory.h>
+#endif
+
+#ifdef HAVE_MALLOC_H
+#include <malloc.h>
+#endif
+
+#ifdef HAVE_FCNTL_H
+#include <fcntl.h>
+#else
+#ifdef HAVE_SYS_FCNTL_H
+#include <sys/fcntl.h>
+#endif
+#endif
+
+#include <sys/stat.h>
+
+#ifdef HAVE_LIMITS_H
+#include <limits.h>
+#endif
+
+#ifdef HAVE_SYS_IOCTL_H
+#include <sys/ioctl.h>
+#endif
+
+#ifdef HAVE_SYS_FILIO_H
+#include <sys/filio.h>
+#endif
+
+#include <signal.h>
+
+#ifdef HAVE_SYS_WAIT_H
+#include <sys/wait.h>
+#endif
+#ifdef HAVE_CTYPE_H
+#include <ctype.h>
+#endif
+#ifdef HAVE_GRP_H
+#include <grp.h>
+#endif
+#ifdef HAVE_SYS_PRIV_H
+#include <sys/priv.h>
+#endif
+#ifdef HAVE_SYS_ID_H
+#include <sys/id.h>
+#endif
+
+#include <errno.h>
+
+#ifdef HAVE_UTIME_H
+#include <utime.h>
+#endif
+
+#ifdef HAVE_SYS_SELECT_H
+#include <sys/select.h>
+#endif
+
+#ifdef HAVE_SYS_MODE_H
+/* apparently AIX needs this for S_ISLNK */
+#ifndef S_ISLNK
+#include <sys/mode.h>
+#endif
+#endif
+
+#ifdef HAVE_GLOB_H
+#include <glob.h>
+#endif
+
+#include <pwd.h>
+
+#ifdef HAVE_STDARG_H
+#include <stdarg.h>
+#else
+#include <varargs.h>
+#endif
+
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+
+#ifdef HAVE_SYSLOG_H
+#include <syslog.h>
+#else
+#ifdef HAVE_SYS_SYSLOG_H
+#include <sys/syslog.h>
+#endif
+#endif
+
+#include <sys/file.h>
+
+#ifdef HAVE_NETINET_TCP_H
+#include <netinet/tcp.h>
+#endif
+
+/*
+ * The next three defines are needed to access the IPTOS_* options
+ * on some systems.
+ */
+
+#ifdef HAVE_NETINET_IN_SYSTM_H
+#include <netinet/in_systm.h>
+#endif
+
+#ifdef HAVE_NETINET_IN_IP_H
+#include <netinet/in_ip.h>
+#endif
+
+#ifdef HAVE_NETINET_IP_H
+#include <netinet/ip.h>
+#endif
+
+#if defined(HAVE_TERMIOS_H)
+/* POSIX terminal handling. */
+#include <termios.h>
+#elif defined(HAVE_TERMIO_H)
+/* Older SYSV terminal handling - don't use if we can avoid it. */
+#include <termio.h>
+#elif defined(HAVE_SYS_TERMIO_H)
+/* Older SYSV terminal handling - don't use if we can avoid it. */
+#include <sys/termio.h>
+#endif
+
+#if HAVE_DIRENT_H
+# include <dirent.h>
+# define NAMLEN(dirent) strlen((dirent)->d_name)
+#else
+# define dirent direct
+# define NAMLEN(dirent) (dirent)->d_namlen
+# if HAVE_SYS_NDIR_H
+# include <sys/ndir.h>
+# endif
+# if HAVE_SYS_DIR_H
+# include <sys/dir.h>
+# endif
+# if HAVE_NDIR_H
+# include <ndir.h>
+# endif
+#endif
+
+#ifdef HAVE_SYS_MMAN_H
+#include <sys/mman.h>
+#endif
+
+#ifdef HAVE_NET_IF_H
+#include <net/if.h>
+#endif
+
+
+#ifdef HAVE_SYS_MOUNT_H
+#include <sys/mount.h>
+#endif
+
+#ifdef HAVE_SYS_VFS_H
+#include <sys/vfs.h>
+#endif
+
+#ifdef HAVE_SYS_ACL_H
+#include <sys/acl.h>
+#endif
+
+#ifdef HAVE_SYS_FS_S5PARAM_H
+#include <sys/fs/s5param.h>
+#endif
+
+#if defined (HAVE_SYS_FILSYS_H) && !defined (_CRAY)
+#include <sys/filsys.h>
+#endif
+
+#ifdef HAVE_SYS_STATFS_H
+# include <sys/statfs.h>
+#endif
+
+#ifdef HAVE_DUSTAT_H
+#include <sys/dustat.h>
+#endif
+
+#ifdef HAVE_SYS_STATVFS_H
+#include <sys/statvfs.h>
+#endif
+
+#ifdef HAVE_SHADOW_H
+#include <shadow.h>
+#endif
+
+#ifdef HAVE_GETPWANAM
+#include <sys/label.h>
+#include <sys/audit.h>
+#include <pwdadj.h>
+#endif
+
+#ifdef HAVE_SYS_SECURITY_H
+#include <sys/security.h>
+#include <prot.h>
+#define PASSWORD_LENGTH 16
+#endif /* HAVE_SYS_SECURITY_H */
+
+#ifdef HAVE_STROPTS_H
+#include <stropts.h>
+#endif
+
+#ifdef HAVE_POLL_H
+#include <poll.h>
+#endif
+
+#ifdef HAVE_EXECINFO_H
+#include <execinfo.h>
+#endif
+
+#ifdef HAVE_SYS_CAPABILITY_H
+
+#if defined(BROKEN_REDHAT_7_SYSTEM_HEADERS) && !defined(_I386_STATFS_H) && !defined(_PPC_STATFS_H)
+#define _I386_STATFS_H
+#define _PPC_STATFS_H
+#define BROKEN_REDHAT_7_STATFS_WORKAROUND
+#endif
+
+#include <sys/capability.h>
+
+#ifdef BROKEN_REDHAT_7_STATFS_WORKAROUND
+#undef _I386_STATFS_H
+#undef _PPC_STATFS_H
+#undef BROKEN_REDHAT_7_STATFS_WORKAROUND
+#endif
+
+#endif
+
+#if defined(HAVE_RPC_RPC_H)
+/*
+ * Check for AUTH_ERROR define conflict with rpc/rpc.h in prot.h.
+ */
+#if defined(HAVE_SYS_SECURITY_H) && defined(HAVE_RPC_AUTH_ERROR_CONFLICT)
+#undef AUTH_ERROR
+#endif
+#include <rpc/rpc.h>
+#endif
+
+#if defined(HAVE_YP_GET_DEFAULT_DOMAIN) && defined(HAVE_SETNETGRENT) && defined(HAVE_ENDNETGRENT) && defined(HAVE_GETNETGRENT)
+#define HAVE_NETGROUP 1
+#endif
+
+#if defined (HAVE_NETGROUP)
+#if defined(HAVE_RPCSVC_YP_PROT_H)
+#include <rpcsvc/yp_prot.h>
+#endif
+#if defined(HAVE_RPCSVC_YPCLNT_H)
+#include <rpcsvc/ypclnt.h>
+#endif
+#endif /* HAVE_NETGROUP */
+
+#if defined(HAVE_SYS_IPC_H)
+#include <sys/ipc.h>
+#endif /* HAVE_SYS_IPC_H */
+
+#if defined(HAVE_SYS_SHM_H)
+#include <sys/shm.h>
+#endif /* HAVE_SYS_SHM_H */
+
+#ifdef HAVE_NATIVE_ICONV
+#ifdef HAVE_ICONV
+#include <iconv.h>
+#endif
+#ifdef HAVE_GICONV
+#include <giconv.h>
+#endif
+#ifdef HAVE_BICONV
+#include <biconv.h>
+#endif
+#endif
+
+#if HAVE_KRB5_H
+#include <krb5.h>
+#else
+#undef HAVE_KRB5
+#endif
+
+#if HAVE_LBER_H
+#include <lber.h>
+#endif
+
+#if HAVE_LDAP_H
+#include <ldap.h>
+#else
+#undef HAVE_LDAP
+#endif
+
+#if HAVE_GSSAPI_H
+#include <gssapi.h>
+#endif
+
+#if HAVE_GSSAPI_GSSAPI_H
+#include <gssapi/gssapi.h>
+#endif
+
+#if HAVE_GSSAPI_GSSAPI_GENERIC_H
+#include <gssapi/gssapi_generic.h>
+#endif
+
+#if HAVE_COM_ERR_H
+#include <com_err.h>
+#endif
+
+#if HAVE_SYS_ATTRIBUTES_H
+#include <sys/attributes.h>
+#endif
+
+/* mutually exclusive (SuSE 8.2) */
+#if HAVE_ATTR_XATTR_H
+#include <attr/xattr.h>
+#elif HAVE_SYS_XATTR_H
+#include <sys/xattr.h>
+#endif
+
+#if HAVE_LOCALE_H
+#include <locale.h>
+#endif
+
+#if HAVE_LANGINFO_H
+#include <langinfo.h>
+#endif
+
+/* Special macros that are no-ops except when run under Valgrind on
+ * x86. They've moved a little bit from valgrind 1.0.4 to 1.9.4 */
+#if HAVE_VALGRIND_MEMCHECK_H
+ /* memcheck.h includes valgrind.h */
+#include <valgrind/memcheck.h>
+#elif HAVE_VALGRIND_H
+#include <valgrind.h>
+#endif
+
+/* If we have --enable-developer and the valgrind header is present,
+ * then we're OK to use it. Set a macro so this logic can be done only
+ * once. */
+#if defined(DEVELOPER) && (HAVE_VALGRIND_H || HAVE_VALGRIND_VALGRIND_H)
+#define VALGRIND
+#endif
+
+
+/* we support ADS if we want it and have krb5 and ldap libs */
+#if defined(WITH_ADS) && defined(HAVE_KRB5) && defined(HAVE_LDAP)
+#define HAVE_ADS
+#endif
+
+/*
+ * Define VOLATILE if needed.
+ */
+
+#if defined(HAVE_VOLATILE)
+#define VOLATILE volatile
+#else
+#define VOLATILE
+#endif
+
+/*
+ * Define additional missing types
+ */
+#if defined(HAVE_SIG_ATOMIC_T_TYPE) && defined(AIX)
+typedef sig_atomic_t SIG_ATOMIC_T;
+#elif defined(HAVE_SIG_ATOMIC_T_TYPE) && !defined(AIX)
+typedef sig_atomic_t VOLATILE SIG_ATOMIC_T;
+#else
+typedef int VOLATILE SIG_ATOMIC_T;
+#endif
+
+#ifndef HAVE_SOCKLEN_T_TYPE
+typedef int socklen_t;
+#endif
+
+
+#ifndef uchar
+#define uchar unsigned char
+#endif
+
+#ifdef HAVE_UNSIGNED_CHAR
+#define schar signed char
+#else
+#define schar char
+#endif
+
+/*
+ Samba needs type definitions for int16, int32, uint16 and uint32.
+
+ Normally these are signed and unsigned 16 and 32 bit integers, but
+ they actually only need to be at least 16 and 32 bits
+ respectively. Thus if your word size is 8 bytes just defining them
+ as signed and unsigned int will work.
+*/
+
+#ifndef uint8
+#define uint8 unsigned char
+#endif
+
+#if !defined(int16) && !defined(HAVE_INT16_FROM_RPC_RPC_H)
+#if (SIZEOF_SHORT == 4)
+#define int16 __ERROR___CANNOT_DETERMINE_TYPE_FOR_INT16;
+#else /* SIZEOF_SHORT != 4 */
+#define int16 short
+#endif /* SIZEOF_SHORT != 4 */
+#endif
+
+/*
+ * Note we duplicate the size tests in the unsigned
+ * case as int16 may be a typedef from rpc/rpc.h
+ */
+
+#if !defined(uint16) && !defined(HAVE_UINT16_FROM_RPC_RPC_H)
+#if (SIZEOF_SHORT == 4)
+#define uint16 __ERROR___CANNOT_DETERMINE_TYPE_FOR_INT16;
+#else /* SIZEOF_SHORT != 4 */
+#define uint16 unsigned short
+#endif /* SIZEOF_SHORT != 4 */
+#endif
+
+#if !defined(int32) && !defined(HAVE_INT32_FROM_RPC_RPC_H)
+#if (SIZEOF_INT == 4)
+#define int32 int
+#elif (SIZEOF_LONG == 4)
+#define int32 long
+#elif (SIZEOF_SHORT == 4)
+#define int32 short
+#else
+/* uggh - no 32 bit type?? probably a CRAY. just hope this works ... */
+#define int32 int
+#endif
+#endif
+
+/*
+ * Note we duplicate the size tests in the unsigned
+ * case as int32 may be a typedef from rpc/rpc.h
+ */
+
+#if !defined(uint32) && !defined(HAVE_UINT32_FROM_RPC_RPC_H)
+#if (SIZEOF_INT == 4)
+#define uint32 unsigned int
+#elif (SIZEOF_LONG == 4)
+#define uint32 unsigned long
+#elif (SIZEOF_SHORT == 4)
+#define uint32 unsigned short
+#else
+/* uggh - no 32 bit type?? probably a CRAY. just hope this works ... */
+#define uint32 unsigned
+#endif
+#endif
+
+/*
+ * Types for devices, inodes and offsets.
+ */
+
+#ifndef SMB_DEV_T
+# if defined(HAVE_EXPLICIT_LARGEFILE_SUPPORT) && defined(HAVE_DEV64_T)
+# define SMB_DEV_T dev64_t
+# else
+# define SMB_DEV_T dev_t
+# endif
+#endif
+
+/*
+ * Setup the correctly sized inode type.
+ */
+
+#ifndef SMB_INO_T
+# if defined(HAVE_EXPLICIT_LARGEFILE_SUPPORT) && defined(HAVE_INO64_T)
+# define SMB_INO_T ino64_t
+# else
+# define SMB_INO_T ino_t
+# endif
+#endif
+
+#ifndef LARGE_SMB_INO_T
+# if (defined(HAVE_EXPLICIT_LARGEFILE_SUPPORT) && defined(HAVE_INO64_T)) || (defined(SIZEOF_INO_T) && (SIZEOF_INO_T == 8))
+# define LARGE_SMB_INO_T 1
+# endif
+#endif
+
+#ifdef LARGE_SMB_INO_T
+#define SINO_T(p, ofs, v) (SIVAL(p,ofs,(v)&0xFFFFFFFF), SIVAL(p,(ofs)+4,(v)>>32))
+#else
+#define SINO_T(p, ofs, v) (SIVAL(p,ofs,v),SIVAL(p,(ofs)+4,0))
+#endif
+
+#ifndef SMB_OFF_T
+# if defined(HAVE_EXPLICIT_LARGEFILE_SUPPORT) && defined(HAVE_OFF64_T)
+# define SMB_OFF_T off64_t
+# else
+# define SMB_OFF_T off_t
+# endif
+#endif
+
+#if defined(HAVE_LONGLONG)
+#define SMB_BIG_UINT unsigned long long
+#define SMB_BIG_INT long long
+#define SBIG_UINT(p, ofs, v) (SIVAL(p,ofs,(v)&0xFFFFFFFF), SIVAL(p,(ofs)+4,(v)>>32))
+#else
+#define SMB_BIG_UINT unsigned long
+#define SMB_BIG_INT long
+#define SBIG_UINT(p, ofs, v) (SIVAL(p,ofs,v),SIVAL(p,(ofs)+4,0))
+#endif
+
+#define SMB_BIG_UINT_BITS (sizeof(SMB_BIG_UINT)*8)
+
+/* this should really be a 64 bit type if possible */
+#define br_off SMB_BIG_UINT
+
+#define SMB_OFF_T_BITS (sizeof(SMB_OFF_T)*8)
+
+/*
+ * Set the define that tells us if we can do 64 bit
+ * NT SMB calls.
+ */
+
+#ifndef LARGE_SMB_OFF_T
+# if (defined(HAVE_EXPLICIT_LARGEFILE_SUPPORT) && defined(HAVE_OFF64_T)) || (defined(SIZEOF_OFF_T) && (SIZEOF_OFF_T == 8))
+# define LARGE_SMB_OFF_T 1
+# endif
+#endif
+
+#ifdef LARGE_SMB_OFF_T
+#define SOFF_T(p, ofs, v) (SIVAL(p,ofs,(v)&0xFFFFFFFF), SIVAL(p,(ofs)+4,(v)>>32))
+#define SOFF_T_R(p, ofs, v) (SIVAL(p,(ofs)+4,(v)&0xFFFFFFFF), SIVAL(p,ofs,(v)>>32))
+#define IVAL_TO_SMB_OFF_T(buf,off) ((SMB_OFF_T)(( ((SMB_BIG_UINT)(IVAL((buf),(off)))) & ((SMB_BIG_UINT)0xFFFFFFFF) )))
+#define IVAL2_TO_SMB_BIG_UINT(buf,off) ( (((SMB_BIG_UINT)(IVAL((buf),(off)))) & ((SMB_BIG_UINT)0xFFFFFFFF)) | \
+ (( ((SMB_BIG_UINT)(IVAL((buf),(off+4)))) & ((SMB_BIG_UINT)0xFFFFFFFF) ) << 32 ) )
+#else
+#define SOFF_T(p, ofs, v) (SIVAL(p,ofs,v),SIVAL(p,(ofs)+4,0))
+#define SOFF_T_R(p, ofs, v) (SIVAL(p,(ofs)+4,v),SIVAL(p,ofs,0))
+#define IVAL_TO_SMB_OFF_T(buf,off) ((SMB_OFF_T)(( ((uint32)(IVAL((buf),(off)))) & 0xFFFFFFFF )))
+#define IVAL2_TO_SMB_BIG_UINT(buf,off) ( (((SMB_BIG_UINT)(IVAL((buf),(off)))) & ((SMB_BIG_UINT)0xFFFFFFFF)) | \
+ (( ((SMB_BIG_UINT)(IVAL((buf),(off+4)))) & ((SMB_BIG_UINT)0xFFFFFFFF) ) << 32 ) )
+#endif
+
+/*
+ * Type for stat structure.
+ */
+
+#ifndef SMB_STRUCT_STAT
+# if defined(HAVE_EXPLICIT_LARGEFILE_SUPPORT) && defined(HAVE_STAT64) && defined(HAVE_OFF64_T)
+# define SMB_STRUCT_STAT struct stat64
+# else
+# define SMB_STRUCT_STAT struct stat
+# endif
+#endif
+
+/*
+ * Type for dirent structure.
+ */
+
+#ifndef SMB_STRUCT_DIRENT
+# if defined(HAVE_EXPLICIT_LARGEFILE_SUPPORT) && defined(HAVE_STRUCT_DIRENT64)
+# define SMB_STRUCT_DIRENT struct dirent64
+# else
+# define SMB_STRUCT_DIRENT struct dirent
+# endif
+#endif
+
+/*
+ * Defines for 64 bit fcntl locks.
+ */
+
+#ifndef SMB_STRUCT_FLOCK
+# if defined(HAVE_EXPLICIT_LARGEFILE_SUPPORT) && defined(HAVE_STRUCT_FLOCK64) && defined(HAVE_OFF64_T)
+# define SMB_STRUCT_FLOCK struct flock64
+# else
+# define SMB_STRUCT_FLOCK struct flock
+# endif
+#endif
+
+#ifndef SMB_F_SETLKW
+# if defined(HAVE_EXPLICIT_LARGEFILE_SUPPORT) && defined(HAVE_STRUCT_FLOCK64) && defined(HAVE_OFF64_T)
+# define SMB_F_SETLKW F_SETLKW64
+# else
+# define SMB_F_SETLKW F_SETLKW
+# endif
+#endif
+
+#ifndef SMB_F_SETLK
+# if defined(HAVE_EXPLICIT_LARGEFILE_SUPPORT) && defined(HAVE_STRUCT_FLOCK64) && defined(HAVE_OFF64_T)
+# define SMB_F_SETLK F_SETLK64
+# else
+# define SMB_F_SETLK F_SETLK
+# endif
+#endif
+
+#ifndef SMB_F_GETLK
+# if defined(HAVE_EXPLICIT_LARGEFILE_SUPPORT) && defined(HAVE_STRUCT_FLOCK64) && defined(HAVE_OFF64_T)
+# define SMB_F_GETLK F_GETLK64
+# else
+# define SMB_F_GETLK F_GETLK
+# endif
+#endif
+
+#ifndef MIN
+#define MIN(a,b) ((a)<(b)?(a):(b))
+#endif
+
+#ifndef MAX
+#define MAX(a,b) ((a)>(b)?(a):(b))
+#endif
+
+#ifndef HAVE_STRERROR
+extern char *sys_errlist[];
+#define strerror(i) sys_errlist[i]
+#endif
+
+#ifndef HAVE_ERRNO_DECL
+extern int errno;
+#endif
+
+#ifdef HAVE_BROKEN_GETGROUPS
+#define GID_T int
+#else
+#define GID_T gid_t
+#endif
+
+#ifndef NGROUPS_MAX
+#define NGROUPS_MAX 32 /* Guess... */
+#endif
+
+/* Our own pstrings and fstrings */
+#include "pstring.h"
+
+/* Lists, trees, caching, database... */
+#include "xfile.h"
+#include "intl.h"
+#include "ubi_sLinkList.h"
+#include "ubi_dLinkList.h"
+#include "dlinklist.h"
+#include "../tdb/tdb.h"
+#include "../tdb/spinlock.h"
+#include "../tdb/tdbutil.h"
+#include "talloc.h"
+#include "nt_status.h"
+#include "ads.h"
+#include "interfaces.h"
+#include "hash.h"
+#include "trans2.h"
+#include "nterr.h"
+#include "ntioctl.h"
+#include "messages.h"
+#include "charset.h"
+#include "dynconfig.h"
+#include "adt_tree.h"
+
+#include "util_getent.h"
+
+#ifndef UBI_BINTREE_H
+#include "ubi_Cache.h"
+#endif /* UBI_BINTREE_H */
+
+#include "debugparse.h"
+
+#include "version.h"
+
+#include "privileges.h"
+
+#include "smb.h"
+
+#include "nameserv.h"
+
+#include "secrets.h"
+
+#include "byteorder.h"
+
+#include "rpc_creds.h"
+
+#include "mapping.h"
+
+#include "passdb.h"
+
+#include "ntdomain.h"
+
+#include "rpc_misc.h"
+
+#include "rpc_secdes.h"
+
+#include "genparser.h"
+
+#include "gums.h"
+
+#include "nt_printing.h"
+
+#include "msdfs.h"
+
+#include "smbprofile.h"
+
+#include "rap.h"
+
+#include "md5.h"
+#include "hmacmd5.h"
+
+#include "ntlmssp.h"
+
+#include "auth.h"
+
+#include "idmap.h"
+
+#include "client.h"
+
+#include "smbw.h"
+
+#include "session.h"
+
+#include "asn_1.h"
+
+#include "popt.h"
+
+#include "mangle.h"
+
+#include "module.h"
+
+#include "nsswitch/winbind_client.h"
+
+#include "spnego.h"
+
+/*
+ * Type for wide character dirent structure.
+ * Only d_name is defined by POSIX.
+ */
+
+typedef struct smb_wdirent {
+ wpstring d_name;
+} SMB_STRUCT_WDIRENT;
+
+/*
+ * Type for wide character passwd structure.
+ */
+
+typedef struct smb_wpasswd {
+ wfstring pw_name;
+ char *pw_passwd;
+ uid_t pw_uid;
+ gid_t pw_gid;
+ wpstring pw_gecos;
+ wpstring pw_dir;
+ wpstring pw_shell;
+} SMB_STRUCT_WPASSWD;
+
+/* used in net.c */
+struct functable {
+ const char *funcname;
+ int (*fn)(int argc, const char **argv);
+};
+
+
+/* Defines for wisXXX functions. */
+#define UNI_UPPER 0x1
+#define UNI_LOWER 0x2
+#define UNI_DIGIT 0x4
+#define UNI_XDIGIT 0x8
+#define UNI_SPACE 0x10
+
+#include "nsswitch/winbind_nss.h"
+
+/* forward declaration from printing.h to get around
+ header file dependencies */
+
+struct printjob;
+
+struct smb_ldap_privates;
+
+/* forward declarations from smbldap.c */
+
+#include "smbldap.h"
+#include "modconf.h"
+
+/***** automatically generated prototypes *****/
+#ifndef NO_PROTO_H
+#include "proto.h"
+#endif
+
+/* String routines */
+
+#include "srvstr.h"
+#include "safe_string.h"
+
+#ifdef __COMPAR_FN_T
+#define QSORT_CAST (__compar_fn_t)
+#endif
+
+#ifndef QSORT_CAST
+#define QSORT_CAST (int (*)(const void *, const void *))
+#endif
+
+#ifndef DEFAULT_PRINTING
+#ifdef HAVE_CUPS
+#define DEFAULT_PRINTING PRINT_CUPS
+#define PRINTCAP_NAME "cups"
+#elif defined(SYSV)
+#define DEFAULT_PRINTING PRINT_SYSV
+#define PRINTCAP_NAME "lpstat"
+#else
+#define DEFAULT_PRINTING PRINT_BSD
+#define PRINTCAP_NAME "/etc/printcap"
+#endif
+#endif
+
+#ifndef PRINTCAP_NAME
+#define PRINTCAP_NAME "/etc/printcap"
+#endif
+
+#ifndef SIGCLD
+#define SIGCLD SIGCHLD
+#endif
+
+#ifndef SIGRTMIN
+#define SIGRTMIN 32
+#endif
+
+#ifndef MAP_FILE
+#define MAP_FILE 0
+#endif
+
+#if defined(HAVE_PUTPRPWNAM) && defined(AUTH_CLEARTEXT_SEG_CHARS)
+#define OSF1_ENH_SEC 1
+#endif
+
+#ifndef ALLOW_CHANGE_PASSWORD
+#if (defined(HAVE_TERMIOS_H) && defined(HAVE_DUP2) && defined(HAVE_SETSID))
+#define ALLOW_CHANGE_PASSWORD 1
+#endif
+#endif
+
+/* what is the longest significant password available on your system?
+ Knowing this speeds up password searches a lot */
+#ifndef PASSWORD_LENGTH
+#define PASSWORD_LENGTH 8
+#endif
+
+#ifdef REPLACE_INET_NTOA
+#define inet_ntoa rep_inet_ntoa
+#endif
+
+#ifndef HAVE_PIPE
+#define SYNC_DNS 1
+#endif
+
+#ifndef SEEK_SET
+#define SEEK_SET 0
+#endif
+
+#ifndef INADDR_LOOPBACK
+#define INADDR_LOOPBACK 0x7f000001
+#endif
+
+#ifndef INADDR_NONE
+#define INADDR_NONE 0xffffffff
+#endif
+
+#ifndef HAVE_CRYPT
+#define crypt ufc_crypt
+#endif
+
+#ifndef O_ACCMODE
+#define O_ACCMODE (O_RDONLY | O_WRONLY | O_RDWR)
+#endif
+
+#if defined(HAVE_CRYPT16) && defined(HAVE_GETAUTHUID)
+#define ULTRIX_AUTH 1
+#endif
+
+#ifndef HAVE_STRDUP
+char *strdup(const char *s);
+#endif
+
+#ifndef HAVE_STRNDUP
+char *strndup(const char *s, size_t size);
+#endif
+
+#ifndef HAVE_MEMMOVE
+void *memmove(void *dest,const void *src,int size);
+#endif
+
+#ifndef HAVE_INITGROUPS
+int initgroups(char *name,gid_t id);
+#endif
+
+#ifndef HAVE_RENAME
+int rename(const char *zfrom, const char *zto);
+#endif
+
+#ifndef HAVE_MKTIME
+time_t mktime(struct tm *t);
+#endif
+
+#ifndef HAVE_STRLCPY
+size_t strlcpy(char *d, const char *s, size_t bufsize);
+#endif
+
+#ifndef HAVE_STRLCAT
+size_t strlcat(char *d, const char *s, size_t bufsize);
+#endif
+
+#ifndef HAVE_FTRUNCATE
+int ftruncate(int f,long l);
+#endif
+
+#ifndef HAVE_STRNDUP
+char *strndup(const char *s, size_t n);
+#endif
+
+#ifndef HAVE_STRNLEN
+size_t strnlen(const char *s, size_t n);
+#endif
+
+#ifndef HAVE_STRTOUL
+unsigned long strtoul(const char *nptr, char **endptr, int base);
+#endif
+
+#ifndef HAVE_SETENV
+int setenv(const char *name, const char *value, int overwrite);
+#endif
+
+#if (defined(USE_SETRESUID) && !defined(HAVE_SETRESUID_DECL))
+/* stupid glibc */
+int setresuid(uid_t ruid, uid_t euid, uid_t suid);
+#endif
+#if (defined(USE_SETRESUID) && !defined(HAVE_SETRESGID_DECL))
+int setresgid(gid_t rgid, gid_t egid, gid_t sgid);
+#endif
+#ifndef HAVE_VASPRINTF_DECL
+int vasprintf(char **ptr, const char *format, va_list ap);
+#endif
+
+#ifdef REPLACE_GETPASS
+#define getpass(prompt) getsmbpass((prompt))
+#endif
+
+/*
+ * Some older systems seem not to have MAXHOSTNAMELEN
+ * defined.
+ */
+#ifndef MAXHOSTNAMELEN
+#define MAXHOSTNAMELEN 254
+#endif
+
+/* yuck, I'd like a better way of doing this */
+#define DIRP_SIZE (256 + 32)
+
+/*
+ * glibc on linux doesn't seem to have MSG_WAITALL
+ * defined. I think the kernel has it though..
+ */
+
+#ifndef MSG_WAITALL
+#define MSG_WAITALL 0
+#endif
+
+/* default socket options. Dave Miller thinks we should default to TCP_NODELAY
+ given the socket IO pattern that Samba uses */
+#ifdef TCP_NODELAY
+#define DEFAULT_SOCKET_OPTIONS "TCP_NODELAY"
+#else
+#define DEFAULT_SOCKET_OPTIONS ""
+#endif
+
+/* Load header file for dynamic linking stuff */
+
+#ifdef HAVE_DLFCN_H
+#include <dlfcn.h>
+#endif
+
+/* dmalloc -- free heap debugger (dmalloc.org). This should be near
+ * the *bottom* of include files so as not to conflict. */
+#ifdef ENABLE_DMALLOC
+# include <dmalloc.h>
+#endif
+
+
+/* Some POSIX definitions for those without */
+
+#ifndef S_IFDIR
+#define S_IFDIR 0x4000
+#endif
+#ifndef S_ISDIR
+#define S_ISDIR(mode) ((mode & 0xF000) == S_IFDIR)
+#endif
+#ifndef S_IRWXU
+#define S_IRWXU 00700 /* read, write, execute: owner */
+#endif
+#ifndef S_IRUSR
+#define S_IRUSR 00400 /* read permission: owner */
+#endif
+#ifndef S_IWUSR
+#define S_IWUSR 00200 /* write permission: owner */
+#endif
+#ifndef S_IXUSR
+#define S_IXUSR 00100 /* execute permission: owner */
+#endif
+#ifndef S_IRWXG
+#define S_IRWXG 00070 /* read, write, execute: group */
+#endif
+#ifndef S_IRGRP
+#define S_IRGRP 00040 /* read permission: group */
+#endif
+#ifndef S_IWGRP
+#define S_IWGRP 00020 /* write permission: group */
+#endif
+#ifndef S_IXGRP
+#define S_IXGRP 00010 /* execute permission: group */
+#endif
+#ifndef S_IRWXO
+#define S_IRWXO 00007 /* read, write, execute: other */
+#endif
+#ifndef S_IROTH
+#define S_IROTH 00004 /* read permission: other */
+#endif
+#ifndef S_IWOTH
+#define S_IWOTH 00002 /* write permission: other */
+#endif
+#ifndef S_IXOTH
+#define S_IXOTH 00001 /* execute permission: other */
+#endif
+
+/* For sys_adminlog(). */
+#ifndef LOG_EMERG
+#define LOG_EMERG 0 /* system is unusable */
+#endif
+
+#ifndef LOG_ALERT
+#define LOG_ALERT 1 /* action must be taken immediately */
+#endif
+
+#ifndef LOG_CRIT
+#define LOG_CRIT 2 /* critical conditions */
+#endif
+
+#ifndef LOG_ERR
+#define LOG_ERR 3 /* error conditions */
+#endif
+
+#ifndef LOG_WARNING
+#define LOG_WARNING 4 /* warning conditions */
+#endif
+
+#ifndef LOG_NOTICE
+#define LOG_NOTICE 5 /* normal but significant condition */
+#endif
+
+#ifndef LOG_INFO
+#define LOG_INFO 6 /* informational */
+#endif
+
+#ifndef LOG_DEBUG
+#define LOG_DEBUG 7 /* debug-level messages */
+#endif
+
+/* NetBSD doesn't have these */
+#ifndef SHM_R
+#define SHM_R 0400
+#endif
+
+#ifndef SHM_W
+#define SHM_W 0200
+#endif
+
+#if HAVE_KERNEL_SHARE_MODES
+#ifndef LOCK_MAND
+#define LOCK_MAND 32 /* This is a mandatory flock */
+#define LOCK_READ 64 /* ... Which allows concurrent read operations */
+#define LOCK_WRITE 128 /* ... Which allows concurrent write operations */
+#define LOCK_RW 192 /* ... Which allows concurrent read & write ops */
+#endif
+#endif
+
+extern int DEBUGLEVEL;
+
+#define MAX_SEC_CTX_DEPTH 8 /* Maximum number of security contexts */
+
+
+#ifdef GLIBC_HACK_FCNTL64
+/* this is a gross hack. 64 bit locking is completely screwed up on
+ i386 Linux in glibc 2.1.95 (which ships with RedHat 7.0). This hack
+ "fixes" the problem with the current 2.4.0test kernels
+*/
+#define fcntl fcntl64
+#undef F_SETLKW
+#undef F_SETLK
+#define F_SETLK 13
+#define F_SETLKW 14
+#endif
+
+
+/* Needed for sys_dlopen/sys_dlsym/sys_dlclose */
+#ifndef RTLD_GLOBAL
+#define RTLD_GLOBAL 0
+#endif
+
+#ifndef RTLD_LAZY
+#define RTLD_LAZY 0
+#endif
+
+#ifndef RTLD_NOW
+#define RTLD_NOW 0
+#endif
+
+/* needed for some systems without iconv. Doesn't really matter
+ what error code we use */
+#ifndef EILSEQ
+#define EILSEQ EIO
+#endif
+
+/* add varargs prototypes with printf checking */
+int fdprintf(int , const char *, ...) PRINTF_ATTRIBUTE(2,3);
+int d_printf(const char *, ...) PRINTF_ATTRIBUTE(1,2);
+int d_fprintf(FILE *f, const char *, ...) PRINTF_ATTRIBUTE(2,3);
+#ifndef HAVE_SNPRINTF_DECL
+int snprintf(char *,size_t ,const char *, ...) PRINTF_ATTRIBUTE(3,4);
+#endif
+#ifndef HAVE_ASPRINTF_DECL
+int asprintf(char **,const char *, ...) PRINTF_ATTRIBUTE(2,3);
+#endif
+
+/* Fix prototype problem with non-C99 compliant snprintf implementations, esp
+ HPUX 11. Don't change the sense of this #if statement. Read the comments
+ in lib/snprint.c if you think you need to. See also bugzilla bug 174. */
+
+#if !defined(HAVE_SNPRINTF) || !defined(HAVE_C99_VSNPRINTF)
+#define snprintf smb_snprintf
+#endif
+
+void sys_adminlog(int priority, const char *format_str, ...) PRINTF_ATTRIBUTE(2,3);
+
+int pstr_sprintf(pstring s, const char *fmt, ...) PRINTF_ATTRIBUTE(2,3);
+int fstr_sprintf(fstring s, const char *fmt, ...) PRINTF_ATTRIBUTE(2,3);
+
+int d_vfprintf(FILE *f, const char *format, va_list ap) PRINTF_ATTRIBUTE(2,0);
+
+int smb_xvasprintf(char **ptr, const char *format, va_list ap) PRINTF_ATTRIBUTE(2,0);
+
+/* we used to use these fns, but now we have good replacements
+ for snprintf and vsnprintf */
+#define slprintf snprintf
+#define vslprintf vsnprintf
+
+
+/* we need to use __va_copy() on some platforms */
+#ifdef HAVE_VA_COPY
+#define VA_COPY(dest, src) va_copy(dest, src)
+#else
+#ifdef HAVE___VA_COPY
+#define VA_COPY(dest, src) __va_copy(dest, src)
+#else
+#define VA_COPY(dest, src) (dest) = (src)
+#endif
+#endif
+
+#ifndef HAVE_TIMEGM
+time_t timegm(struct tm *tm);
+#endif
+
+/*
+ * Veritas File System. Often in addition to native.
+ * Quotas different.
+ */
+#if defined(HAVE_SYS_FS_VX_QUOTA_H)
+#define VXFS_QUOTA
+#endif
+
+#if defined(HAVE_KRB5)
+
+#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
+
+/* 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);
+void get_auth_data_from_tkt(DATA_BLOB *auth_data, krb5_ticket *tkt);
+krb5_const_principal get_principal_from_tkt(krb5_ticket *tkt);
+krb5_error_code krb5_locate_kdc(krb5_context ctx, const krb5_data *realm, struct sockaddr **addr_pp, int *naddrs, int get_masters);
+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);
+#endif /* HAVE_KRB5 */
+
+/* TRUE and FALSE are part of the C99 standard and gcc, but
+ unfortunately many vendor compilers don't support them. Use True
+ and False instead. */
+
+#ifdef TRUE
+#undef TRUE
+#endif
+#define TRUE __ERROR__XX__DONT_USE_TRUE
+
+#ifdef FALSE
+#undef FALSE
+#endif
+#define FALSE __ERROR__XX__DONT_USE_FALSE
+
+/* If we have blacklisted mmap() try to avoid using it accidentally by
+ undefining the HAVE_MMAP symbol. */
+
+#ifdef MMAP_BLACKLIST
+#undef HAVE_MMAP
+#endif
+
+#endif /* _INCLUDES_H */
diff --git a/source/include/interfaces.h b/source/include/interfaces.h
new file mode 100644
index 00000000000..3b786f1ebcb
--- /dev/null
+++ b/source/include/interfaces.h
@@ -0,0 +1,12 @@
+/*
+ This structure is used by lib/interfaces.c to return the list of network
+ interfaces on the machine
+*/
+
+#define MAX_INTERFACES 128
+
+struct iface_struct {
+ char name[16];
+ struct in_addr ip;
+ struct in_addr netmask;
+};
diff --git a/source/include/intl.h b/source/include/intl.h
new file mode 100644
index 00000000000..01fa3bad976
--- /dev/null
+++ b/source/include/intl.h
@@ -0,0 +1,25 @@
+/*
+ Unix SMB/CIFS implementation.
+ internationalisation headers
+ 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 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.
+*/
+
+
+/* ideally we would have a static mapping, but that precludes
+ dynamic loading. This is a reasonable compromise */
+#define _(x) lang_msg_rotate(x)
+#define N_(x) (x)
diff --git a/source/include/libsmb_internal.h b/source/include/libsmb_internal.h
new file mode 100644
index 00000000000..21fe47d4b29
--- /dev/null
+++ b/source/include/libsmb_internal.h
@@ -0,0 +1,67 @@
+#ifndef _LIBSMB_INTERNAL_H_
+#define _LIBSMB_INTERNAL_H_
+
+#define SMBC_MAX_NAME 1023
+#define SMBC_FILE_MODE (S_IFREG | 0444)
+#define SMBC_DIR_MODE (S_IFDIR | 0555)
+
+
+#include "../include/libsmbclient.h"
+
+
+struct _SMBCSRV {
+ struct cli_state cli;
+ dev_t dev;
+ BOOL no_pathinfo2;
+ int server_fd;
+
+ SMBCSRV *next, *prev;
+
+};
+
+/*
+ * Keep directory entries in a list
+ */
+struct smbc_dir_list {
+ struct smbc_dir_list *next;
+ struct smbc_dirent *dirent;
+};
+
+
+/*
+ * Structure for open file management
+ */
+struct _SMBCFILE {
+ int cli_fd;
+ char *fname;
+ off_t offset;
+ struct _SMBCSRV *srv;
+ BOOL file;
+ struct smbc_dir_list *dir_list, *dir_end, *dir_next;
+ int dir_type, dir_error;
+
+ SMBCFILE *next, *prev;
+};
+
+
+struct smbc_internal_data {
+
+ /** INTERNAL: is this handle initialized ?
+ */
+ int _initialized;
+
+ /** INTERNAL: dirent pointer location
+ */
+ char _dirent[512];
+
+ /** INTERNAL: server connection list
+ */
+ SMBCSRV * _servers;
+
+ /** INTERNAL: open file/dir list
+ */
+ SMBCFILE * _files;
+};
+
+
+#endif
diff --git a/source/include/libsmbclient.h b/source/include/libsmbclient.h
new file mode 100644
index 00000000000..68c4a053d1b
--- /dev/null
+++ b/source/include/libsmbclient.h
@@ -0,0 +1,1947 @@
+/*=====================================================================
+ Unix SMB/Netbios implementation.
+ SMB client library API definitions
+ Copyright (C) Andrew Tridgell 1998
+ Copyright (C) Richard Sharpe 2000
+ Copyright (C) John Terpsra 2000
+ Copyright (C) Tom Jansen (Ninja ISD) 2002
+ Copyright (C) Derrell Lipman 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 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 SMBCLIENT_H_INCLUDED
+#define SMBCLIENT_H_INCLUDED
+
+/*-------------------------------------------------------------------*/
+/* The following are special comments to instruct DOXYGEN (automated
+ * documentation tool:
+*/
+/** \defgroup libsmbclient
+*/
+/** \defgroup structure Data Structures Type and Constants
+* \ingroup libsmbclient
+* Data structures, types, and constants
+*/
+/** \defgroup callback Callback function types
+* \ingroup libsmbclient
+* Callback functions
+*/
+/** \defgroup file File Functions
+* \ingroup libsmbclient
+* Functions used to access individual file contents
+*/
+/** \defgroup directory Directory Functions
+* \ingroup libsmbclient
+* Functions used to access directory entries
+*/
+/** \defgroup attribute Attributes Functions
+* \ingroup libsmbclient
+* Functions used to view or change file and directory attributes
+*/
+/** \defgroup print Print Functions
+* \ingroup libsmbclient
+* Functions used to access printing functionality
+*/
+/** \defgroup misc Miscellaneous Functions
+* \ingroup libsmbclient
+* Functions that don't fit in to other categories
+*/
+/*-------------------------------------------------------------------*/
+
+/* Make sure we have the following includes for now ... */
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <utime.h>
+
+#define SMBC_BASE_FD 10000 /* smallest file descriptor returned */
+
+#define SMBC_WORKGROUP 1
+#define SMBC_SERVER 2
+#define SMBC_FILE_SHARE 3
+#define SMBC_PRINTER_SHARE 4
+#define SMBC_COMMS_SHARE 5
+#define SMBC_IPC_SHARE 6
+#define SMBC_DIR 7
+#define SMBC_FILE 8
+#define SMBC_LINK 9
+
+/**@ingroup structure
+ * Structure that represents a directory entry.
+ *
+ */
+struct smbc_dirent
+{
+ /** Type of entity.
+ SMBC_WORKGROUP=1,
+ SMBC_SERVER=2,
+ SMBC_FILE_SHARE=3,
+ SMBC_PRINTER_SHARE=4,
+ SMBC_COMMS_SHARE=5,
+ SMBC_IPC_SHARE=6,
+ SMBC_DIR=7,
+ SMBC_FILE=8,
+ SMBC_LINK=9,*/
+ unsigned int smbc_type;
+
+ /** Length of this smbc_dirent in bytes
+ */
+ unsigned int dirlen;
+ /** The length of the comment string in bytes (includes null
+ * terminator)
+ */
+ unsigned int commentlen;
+ /** Points to the null terminated comment string
+ */
+ char *comment;
+ /** The length of the name string in bytes (includes null
+ * terminator)
+ */
+ unsigned int namelen;
+ /** Points to the null terminated name string
+ */
+ char name[1];
+};
+
+/*
+ * Flags for smbc_setxattr()
+ * Specify a bitwise OR of these, or 0 to add or replace as necessary
+ */
+#define SMBC_XATTR_FLAG_CREATE 0x1 /* fail if attr already exists */
+#define SMBC_XATTR_FLAG_REPLACE 0x2 /* fail if attr does not exist */
+
+
+#ifndef ENOATTR
+# define ENOATTR ENOENT /* No such attribute */
+#endif
+
+
+
+
+/**@ingroup structure
+ * Structure that represents a print job.
+ *
+ */
+#ifndef _CLIENT_H
+struct print_job_info
+{
+ /** numeric ID of the print job
+ */
+ unsigned short id;
+
+ /** represents print job priority (lower numbers mean higher priority)
+ */
+ unsigned short priority;
+
+ /** Size of the print job
+ */
+ size_t size;
+
+ /** Name of the user that owns the print job
+ */
+ char user[128];
+
+ /** Name of the print job. This will have no name if an anonymous print
+ * file was opened. Ie smb://server/printer
+ */
+ char name[128];
+
+ /** Time the print job was spooled
+ */
+ time_t t;
+};
+#endif /* _CLIENT_H */
+
+
+/**@ingroup structure
+ * Server handle
+ */
+typedef struct _SMBCSRV SMBCSRV;
+
+/**@ingroup structure
+ * File or directory handle
+ */
+typedef struct _SMBCFILE SMBCFILE;
+
+/**@ingroup structure
+ * File or directory handle
+ */
+typedef struct _SMBCCTX SMBCCTX;
+
+
+
+
+
+/**@ingroup callback
+ * Authentication callback function type.
+ *
+ * Type for the the authentication function called by the library to
+ * obtain authentication credentals
+ *
+ * @param srv Server being authenticated to
+ *
+ * @param shr Share being authenticated to
+ *
+ * @param wg Pointer to buffer containing a "hint" for the
+ * workgroup to be authenticated. Should be filled in
+ * with the correct workgroup if the hint is wrong.
+ *
+ * @param wglen The size of the workgroup buffer in bytes
+ *
+ * @param un Pointer to buffer containing a "hint" for the
+ * user name to be use for authentication. Should be
+ * filled in with the correct workgroup if the hint is
+ * wrong.
+ *
+ * @param unlen The size of the username buffer in bytes
+ *
+ * @param pw Pointer to buffer containing to which password
+ * copied
+ *
+ * @param pwlen The size of the password buffer in bytes
+ *
+ */
+typedef void (*smbc_get_auth_data_fn)(const char *srv,
+ const char *shr,
+ char *wg, int wglen,
+ char *un, int unlen,
+ char *pw, int pwlen);
+
+
+/**@ingroup callback
+ * Print job info callback function type.
+ *
+ * @param i pointer to print job information structure
+ *
+ */
+typedef void (*smbc_list_print_job_fn)(struct print_job_info *i);
+
+
+/**@ingroup callback
+ * Check if a server is still good
+ *
+ * @param c pointer to smb context
+ *
+ * @param srv pointer to server to check
+ *
+ * @return 0 when connection is good. 1 on error.
+ *
+ */
+typedef int (*smbc_check_server_fn)(SMBCCTX * c, SMBCSRV *srv);
+
+/**@ingroup callback
+ * Remove a server if unused
+ *
+ * @param c pointer to smb context
+ *
+ * @param srv pointer to server to remove
+ *
+ * @return 0 on success. 1 on failure.
+ *
+ */
+typedef int (*smbc_remove_unused_server_fn)(SMBCCTX * c, SMBCSRV *srv);
+
+
+/**@ingroup callback
+ * Add a server to the cache system
+ *
+ * @param c pointer to smb context
+ *
+ * @param srv pointer to server to add
+ *
+ * @param server server name
+ *
+ * @param share share name
+ *
+ * @param workgroup workgroup used to connect
+ *
+ * @param username username used to connect
+ *
+ * @return 0 on success. 1 on failure.
+ *
+ */
+typedef int (*smbc_add_cached_srv_fn) (SMBCCTX * c, SMBCSRV *srv,
+ const char * server, const char * share,
+ const char * workgroup, const char * username);
+
+/**@ingroup callback
+ * Look up a server in the cache system
+ *
+ * @param c pointer to smb context
+ *
+ * @param server server name to match
+ *
+ * @param share share name to match
+ *
+ * @param workgroup workgroup to match
+ *
+ * @param username username to match
+ *
+ * @return pointer to SMBCSRV on success. NULL on failure.
+ *
+ */
+typedef SMBCSRV * (*smbc_get_cached_srv_fn) (SMBCCTX * c, const char * server,
+ const char * share, const char * workgroup,
+ const char * username);
+
+/**@ingroup callback
+ * Check if a server is still good
+ *
+ * @param c pointer to smb context
+ *
+ * @param srv pointer to server to remove
+ *
+ * @return 0 when found and removed. 1 on failure.
+ *
+ */
+typedef int (*smbc_remove_cached_srv_fn)(SMBCCTX * c, SMBCSRV *srv);
+
+
+/**@ingroup callback
+ * Try to remove all servers from the cache system and disconnect
+ *
+ * @param c pointer to smb context
+ *
+ * @return 0 when found and removed. 1 on failure.
+ *
+ */
+typedef int (*smbc_purge_cached_fn) (SMBCCTX * c);
+
+
+
+
+/**@ingroup structure
+ * Structure that contains a client context information
+ * This structure is know as SMBCCTX
+ */
+struct _SMBCCTX {
+ /** debug level
+ */
+ int debug;
+
+ /** netbios name used for making connections
+ */
+ char * netbios_name;
+
+ /** workgroup name used for making connections
+ */
+ char * workgroup;
+
+ /** username used for making connections
+ */
+ char * user;
+
+ /** timeout used for waiting on connections / response data (in milliseconds)
+ */
+ int timeout;
+
+ /** callable functions for files:
+ * For usage and return values see the smbc_* functions
+ */
+ SMBCFILE * (*open) (SMBCCTX *c, const char *fname, int flags, mode_t mode);
+ SMBCFILE * (*creat) (SMBCCTX *c, const char *path, mode_t mode);
+ ssize_t (*read) (SMBCCTX *c, SMBCFILE *file, void *buf, size_t count);
+ ssize_t (*write) (SMBCCTX *c, SMBCFILE *file, void *buf, size_t count);
+ int (*unlink) (SMBCCTX *c, const char *fname);
+ int (*rename) (SMBCCTX *ocontext, const char *oname,
+ SMBCCTX *ncontext, const char *nname);
+ off_t (*lseek) (SMBCCTX *c, SMBCFILE * file, off_t offset, int whence);
+ int (*stat) (SMBCCTX *c, const char *fname, struct stat *st);
+ int (*fstat) (SMBCCTX *c, SMBCFILE *file, struct stat *st);
+ int (*close) (SMBCCTX *c, SMBCFILE *file);
+
+ /** callable functions for dirs
+ */
+ SMBCFILE * (*opendir) (SMBCCTX *c, const char *fname);
+ int (*closedir)(SMBCCTX *c, SMBCFILE *dir);
+ struct smbc_dirent * (*readdir)(SMBCCTX *c, SMBCFILE *dir);
+ int (*getdents)(SMBCCTX *c, SMBCFILE *dir,
+ struct smbc_dirent *dirp, int count);
+ int (*mkdir) (SMBCCTX *c, const char *fname, mode_t mode);
+ int (*rmdir) (SMBCCTX *c, const char *fname);
+ off_t (*telldir) (SMBCCTX *c, SMBCFILE *dir);
+ int (*lseekdir)(SMBCCTX *c, SMBCFILE *dir, off_t offset);
+ int (*fstatdir)(SMBCCTX *c, SMBCFILE *dir, struct stat *st);
+ int (*chmod)(SMBCCTX *c, const char *fname, mode_t mode);
+ int (*utimes)(SMBCCTX *c,
+ const char *fname, struct timeval *tbuf);
+ int (*setxattr)(SMBCCTX *context,
+ const char *fname,
+ const char *name,
+ const void *value,
+ size_t size,
+ int flags);
+ int (*getxattr)(SMBCCTX *context,
+ const char *fname,
+ const char *name,
+ const void *value,
+ size_t size);
+ int (*removexattr)(SMBCCTX *context,
+ const char *fname,
+ const char *name);
+ int (*listxattr)(SMBCCTX *context,
+ const char *fname,
+ char *list,
+ size_t size);
+
+ /** callable functions for printing
+ */
+ int (*print_file)(SMBCCTX *c_file, const char *fname,
+ SMBCCTX *c_print, const char *printq);
+ SMBCFILE * (*open_print_job)(SMBCCTX *c, const char *fname);
+ int (*list_print_jobs)(SMBCCTX *c, const char *fname, smbc_list_print_job_fn fn);
+ int (*unlink_print_job)(SMBCCTX *c, const char *fname, int id);
+
+
+ /** Callbacks
+ * These callbacks _always_ have to be initialized because they will not be checked
+ * at dereference for increased speed.
+ */
+ struct _smbc_callbacks {
+ /** authentication function callback: called upon auth requests
+ */
+ smbc_get_auth_data_fn auth_fn;
+
+ /** check if a server is still good
+ */
+ smbc_check_server_fn check_server_fn;
+
+ /** remove a server if unused
+ */
+ smbc_remove_unused_server_fn remove_unused_server_fn;
+
+ /** Cache subsystem
+ * For an example cache system see samba/source/libsmb/libsmb_cache.c
+ * Cache subsystem functions follow.
+ */
+
+ /** server cache addition
+ */
+ smbc_add_cached_srv_fn add_cached_srv_fn;
+
+ /** server cache lookup
+ */
+ smbc_get_cached_srv_fn get_cached_srv_fn;
+
+ /** server cache removal
+ */
+ smbc_remove_cached_srv_fn remove_cached_srv_fn;
+
+ /** server cache purging, try to remove all cached servers (disconnect)
+ */
+ smbc_purge_cached_fn purge_cached_fn;
+ } callbacks;
+
+
+ /** Space to store private data of the server cache.
+ */
+ struct smbc_server_cache * server_cache;
+
+ /** INTERNAL DATA
+ * do _NOT_ touch this from your program !
+ */
+ struct smbc_internal_data * internal;
+
+};
+
+
+/**@ingroup misc
+ * Create a new SBMCCTX (a context).
+ *
+ * Must be called before the context is passed to smbc_context_init()
+ *
+ * @return The given SMBCCTX pointer on success, NULL on error with errno set:
+ * - ENOMEM Out of memory
+ *
+ * @see smbc_free_context(), smbc_init_context()
+ *
+ * @note Do not forget to smbc_init_context() the returned SMBCCTX pointer !
+ */
+SMBCCTX * smbc_new_context(void);
+
+/**@ingroup misc
+ * Delete a SBMCCTX (a context) acquired from smbc_new_context().
+ *
+ * The context will be deleted if possible.
+ *
+ * @param context A pointer to a SMBCCTX obtained from smbc_new_context()
+ *
+ * @param shutdown_ctx If 1, all connections and files will be closed even if they are busy.
+ *
+ *
+ * @return Returns 0 on succes. Returns 1 on failure with errno set:
+ * - EBUSY Server connections are still used, Files are open or cache
+ * could not be purged
+ * - EBADF context == NULL
+ *
+ * @see smbc_new_context()
+ *
+ * @note It is advised to clean up all the contexts with shutdown_ctx set to 1
+ * just before exit()'ing. When shutdown_ctx is 0, this function can be
+ * use in periodical cleanup functions for example.
+ */
+int smbc_free_context(SMBCCTX * context, int shutdown_ctx);
+
+
+/**@ingroup misc
+ * Initialize a SBMCCTX (a context).
+ *
+ * Must be called before using any SMBCCTX API function
+ *
+ * @param context A pointer to a SMBCCTX obtained from smbc_new_context()
+ *
+ * @return A pointer to the given SMBCCTX on success, NULL on error with errno set:
+ * - EBADF NULL context given
+ * - ENOMEM Out of memory
+ * - ENOENT The smb.conf file would not load
+ *
+ * @see smbc_new_context()
+ *
+ * @note my_context = smbc_init_context(smbc_new_context()) is perfectly safe,
+ * but it might leak memory on smbc_context_init() failure. Avoid this.
+ * You'll have to call smbc_free_context() yourself on failure.
+ */
+
+SMBCCTX * smbc_init_context(SMBCCTX * context);
+
+/**@ingroup misc
+ * Initialize the samba client library.
+ *
+ * Must be called before using any of the smbclient API function
+ *
+ * @param fn The function that will be called to obtaion
+ * authentication credentials.
+ *
+ * @param debug Allows caller to set the debug level. Can be
+ * changed in smb.conf file. Allows caller to set
+ * debugging if no smb.conf.
+ *
+ * @return 0 on success, < 0 on error with errno set:
+ * - ENOMEM Out of memory
+ * - ENOENT The smb.conf file would not load
+ *
+ */
+
+int smbc_init(smbc_get_auth_data_fn fn, int debug);
+
+/**@ingroup misc
+ * Set or retrieve the compatibility library's context pointer
+ *
+ * @param context New context to use, or NULL. If a new context is provided,
+ * it must have allocated with smbc_new_context() and
+ * initialized with smbc_init_context(), followed, optionally,
+ * by some manual changes to some of the non-internal fields.
+ *
+ * @return The old context.
+ *
+ * @see smbc_new_context(), smbc_init_context(), smbc_init()
+ *
+ * @note This function may be called prior to smbc_init() to force
+ * use of the next context without any internal calls to
+ * smbc_new_context() or smbc_init_context(). It may also
+ * be called after smbc_init() has already called those two
+ * functions, to replace the existing context with a new one.
+ * Care should be taken, in this latter case, to ensure that
+ * the server cache and any data allocated by the
+ * authentication functions have been freed, if necessary.
+ */
+
+SMBCCTX * smbc_set_context(SMBCCTX * new_context);
+
+/**@ingroup file
+ * Open a file on an SMB server.
+ *
+ * @param furl The smb url of the file to be opened.
+ *
+ * @param flags Is one of O_RDONLY, O_WRONLY or O_RDWR which
+ * request opening the file read-only,write-only
+ * or read/write. flags may also be bitwise-or'd with
+ * one or more of the following:
+ * O_CREAT - If the file does not exist it will be
+ * created.
+ * O_EXCL - When used with O_CREAT, if the file
+ * already exists it is an error and the open will
+ * fail.
+ * O_TRUNC - If the file already exists it will be
+ * truncated.
+ * O_APPEND The file is opened in append mode
+ *
+ * @param mode mode specifies the permissions to use if a new
+ * file is created. It is modified by the
+ * process's umask in the usual way: the permissions
+ * of the created file are (mode & ~umask)
+ *
+ * Not currently use, but there for future use.
+ * We will map this to SYSTEM, HIDDEN, etc bits
+ * that reverses the mapping that smbc_fstat does.
+ *
+ * @return Valid file handle, < 0 on error with errno set:
+ * - ENOMEM Out of memory
+ * - EINVAL if an invalid parameter passed, like no
+ * file, or smbc_init not called.
+ * - EEXIST pathname already exists and O_CREAT and
+ * O_EXCL were used.
+ * - EISDIR pathname refers to a directory and
+ * the access requested involved writing.
+ * - EACCES The requested access to the file is not
+ * allowed
+ * - ENODEV The requested share does not exist
+ * - ENOTDIR A file on the path is not a directory
+ * - ENOENT A directory component in pathname does
+ * not exist.
+ *
+ * @see smbc_creat()
+ *
+ * @note This call uses an underlying routine that may create
+ * a new connection to the server specified in the URL.
+ * If the credentials supplied in the URL, or via the
+ * auth_fn in the smbc_init call, fail, this call will
+ * try again with an empty username and password. This
+ * often gets mapped to the guest account on some machines.
+ */
+
+int smbc_open(const char *furl, int flags, mode_t mode);
+
+/**@ingroup file
+ * Create a file on an SMB server.
+ *
+ * Same as calling smbc_open() with flags = O_CREAT|O_WRONLY|O_TRUNC
+ *
+ * @param furl The smb url of the file to be created
+ *
+ * @param mode mode specifies the permissions to use if a new
+ * file is created. It is modified by the
+ * process's umask in the usual way: the permissions
+ * of the created file are (mode & ~umask)
+ *
+ * NOTE, the above is not true. We are dealing with
+ * an SMB server, which has no concept of a umask!
+ *
+ * @return Valid file handle, < 0 on error with errno set:
+ * - ENOMEM Out of memory
+ * - EINVAL if an invalid parameter passed, like no
+ * file, or smbc_init not called.
+ * - EEXIST pathname already exists and O_CREAT and
+ * O_EXCL were used.
+ * - EISDIR pathname refers to a directory and
+ * the access requested involved writing.
+ * - EACCES The requested access to the file is not
+ * allowed
+ * - ENOENT A directory component in pathname does
+ * not exist.
+ * - ENODEV The requested share does not exist.
+ * @see smbc_open()
+ *
+ */
+
+int smbc_creat(const char *furl, mode_t mode);
+
+/**@ingroup file
+ * Read from a file using an opened file handle.
+ *
+ * @param fd Open file handle from smbc_open() or smbc_creat()
+ *
+ * @param buf Pointer to buffer to recieve read data
+ *
+ * @param bufsize Size of buf in bytes
+ *
+ * @return Number of bytes read, < 0 on error with errno set:
+ * - EISDIR fd refers to a directory
+ * - EBADF fd is not a valid file descriptor or
+ * is not open for reading.
+ * - EINVAL fd is attached to an object which is
+ * unsuitable for reading, or no buffer passed or
+ * smbc_init not called.
+ *
+ * @see smbc_open(), smbc_write()
+ *
+ */
+ssize_t smbc_read(int fd, void *buf, size_t bufsize);
+
+
+/**@ingroup file
+ * Write to a file using an opened file handle.
+ *
+ * @param fd Open file handle from smbc_open() or smbc_creat()
+ *
+ * @param buf Pointer to buffer to recieve read data
+ *
+ * @param bufsize Size of buf in bytes
+ *
+ * @return Number of bytes written, < 0 on error with errno set:
+ * - EISDIR fd refers to a directory.
+ * - EBADF fd is not a valid file descriptor or
+ * is not open for reading.
+ * - EINVAL fd is attached to an object which is
+ * unsuitable for reading, or no buffer passed or
+ * smbc_init not called.
+ *
+ * @see smbc_open(), smbc_read()
+ *
+ */
+ssize_t smbc_write(int fd, void *buf, size_t bufsize);
+
+
+/**@ingroup file
+ * Seek to a specific location in a file.
+ *
+ * @param fd Open file handle from smbc_open() or smbc_creat()
+ *
+ * @param offset Offset in bytes from whence
+ *
+ * @param whence A location in the file:
+ * - SEEK_SET The offset is set to offset bytes from
+ * the beginning of the file
+ * - SEEK_CUR The offset is set to current location
+ * plus offset bytes.
+ * - SEEK_END The offset is set to the size of the
+ * file plus offset bytes.
+ *
+ * @return Upon successful completion, lseek returns the
+ * resulting offset location as measured in bytes
+ * from the beginning of the file. Otherwise, a value
+ * of (off_t)-1 is returned and errno is set to
+ * indicate the error:
+ * - EBADF Fildes is not an open file descriptor.
+ * - EINVAL Whence is not a proper value or smbc_init
+ * not called.
+ *
+ * @todo Are all the whence values really supported?
+ *
+ * @todo Are errno values complete and correct?
+ */
+off_t smbc_lseek(int fd, off_t offset, int whence);
+
+
+/**@ingroup file
+ * Close an open file handle.
+ *
+ * @param fd The file handle to close
+ *
+ * @return 0 on success, < 0 on error with errno set:
+ * - EBADF fd isn't a valid open file descriptor
+ * - EINVAL smbc_init() failed or has not been called
+ *
+ * @see smbc_open(), smbc_creat()
+ */
+int smbc_close(int fd);
+
+
+/**@ingroup directory
+ * Unlink (delete) a file or directory.
+ *
+ * @param furl The smb url of the file to delete
+ *
+ * @return 0 on success, < 0 on error with errno set:
+ * - EACCES or EPERM Write access to the directory
+ * containing pathname is not allowed or one
+ * of the directories in pathname did not allow
+ * search (execute) permission
+ * - ENOENT A directory component in pathname does
+ * not exist
+ * - EINVAL NULL was passed in the file param or
+ * smbc_init not called.
+ * - EACCES You do not have access to the file
+ * - ENOMEM Insufficient kernel memory was available
+ *
+ * @see smbc_rmdir()s
+ *
+ * @todo Are errno values complete and correct?
+ */
+int smbc_unlink(const char *furl);
+
+
+/**@ingroup directory
+ * Rename or move a file or directory.
+ *
+ * @param ourl The original smb url (source url) of file or
+ * directory to be moved
+ *
+ * @param nurl The new smb url (destination url) of the file
+ * or directory after the move. Currently nurl must
+ * be on the same share as ourl.
+ *
+ * @return 0 on success, < 0 on error with errno set:
+ * - EISDIR nurl is an existing directory, but ourl is
+ * not a directory.
+ * - EEXIST nurl is a non-empty directory,
+ * i.e., contains entries other than "." and ".."
+ * - EINVAL The new url contained a path prefix
+ * of the old, or, more generally, an attempt was
+ * made to make a directory a subdirectory of itself
+ * or smbc_init not called.
+ * - ENOTDIR A component used as a directory in ourl
+ * or nurl path is not, in fact, a directory. Or,
+ * ourl is a directory, and newpath exists but is not
+ * a directory.
+ * - EACCES or EPERM Write access to the directory
+ * containing ourl or nurl is not allowed for the
+ * process's effective uid, or one of the
+ * directories in ourl or nurl did not allow search
+ * (execute) permission, or ourl was a directory
+ * and did not allow write permission.
+ * - ENOENT A directory component in ourl or nurl
+ * does not exist.
+ * - EXDEV Rename across shares not supported.
+ * - ENOMEM Insufficient kernel memory was available.
+ * - EEXIST The target file, nurl, already exists.
+ *
+ *
+ * @todo Are we going to support copying when urls are not on the same
+ * share? I say no... NOTE. I agree for the moment.
+ *
+ */
+int smbc_rename(const char *ourl, const char *nurl);
+
+
+/**@ingroup directory
+ * Open a directory used to obtain directory entries.
+ *
+ * @param durl The smb url of the directory to open
+ *
+ * @return Valid directory handle. < 0 on error with errno set:
+ * - EACCES Permission denied.
+ * - EINVAL A NULL file/URL was passed, or the URL would
+ * not parse, or was of incorrect form or smbc_init not
+ * called.
+ * - ENOENT durl does not exist, or name is an
+ * - ENOMEM Insufficient memory to complete the
+ * operation.
+ * - ENOTDIR name is not a directory.
+ * - EPERM the workgroup could not be found.
+ * - ENODEV the workgroup or server could not be found.
+ *
+ * @see smbc_getdents(), smbc_readdir(), smbc_closedir()
+ *
+ */
+int smbc_opendir(const char *durl);
+
+
+/**@ingroup directory
+ * Close a directory handle opened by smbc_opendir().
+ *
+ * @param dh Directory handle to close
+ *
+ * @return 0 on success, < 0 on error with errno set:
+ * - EBADF dh is an invalid directory handle
+ *
+ * @see smbc_opendir()
+ */
+int smbc_closedir(int dh);
+
+
+/**@ingroup directory
+ * Get multiple directory entries.
+ *
+ * smbc_getdents() reads as many dirent structures from the an open
+ * directory handle into a specified memory area as will fit.
+ *
+ * @param dh Valid directory as returned by smbc_opendir()
+ *
+ * @param dirp pointer to buffer that will receive the directory
+ * entries.
+ *
+ * @param count The size of the dirp buffer in bytes
+ *
+ * @returns If any dirents returned, return will indicate the
+ * total size. If there were no more dirents available,
+ * 0 is returned. < 0 indicates an error.
+ * - EBADF Invalid directory handle
+ * - EINVAL Result buffer is too small or smbc_init
+ * not called.
+ * - ENOENT No such directory.
+ * @see , smbc_dirent, smbc_readdir(), smbc_open()
+ *
+ * @todo Are errno values complete and correct?
+ *
+ * @todo Add example code so people know how to parse buffers.
+ */
+int smbc_getdents(unsigned int dh, struct smbc_dirent *dirp, int count);
+
+
+/**@ingroup directory
+ * Get a single directory entry.
+ *
+ * @param dh Valid directory as returned by smbc_opendir()
+ *
+ * @return A pointer to a smbc_dirent structure, or NULL if an
+ * error occurs or end-of-directory is reached:
+ * - EBADF Invalid directory handle
+ * - EINVAL smbc_init() failed or has not been called
+ *
+ * @see smbc_dirent, smbc_getdents(), smbc_open()
+ */
+struct smbc_dirent* smbc_readdir(unsigned int dh);
+
+
+/**@ingroup directory
+ * Get the current directory offset.
+ *
+ * smbc_telldir() may be used in conjunction with smbc_readdir() and
+ * smbc_lseekdir().
+ *
+ * @param dh Valid directory as returned by smbc_opendir()
+ *
+ * @return The current location in the directory stream or -1
+ * if an error occur. The current location is not
+ * an offset. Becuase of the implementation, it is a
+ * handle that allows the library to find the entry
+ * later.
+ * - EBADF dh is not a valid directory handle
+ * - EINVAL smbc_init() failed or has not been called
+ * - ENOTDIR if dh is not a directory
+ *
+ * @see smbc_readdir()
+ *
+ */
+off_t smbc_telldir(int dh);
+
+
+/**@ingroup directory
+ * lseek on directories.
+ *
+ * smbc_lseekdir() may be used in conjunction with smbc_readdir() and
+ * smbc_telldir(). (rewind by smbc_lseekdir(fd, NULL))
+ *
+ * @param fd Valid directory as returned by smbc_opendir()
+ *
+ * @param offset The offset (as returned by smbc_telldir). Can be
+ * NULL, in which case we will rewind
+ *
+ * @return 0 on success, -1 on failure
+ * - EBADF dh is not a valid directory handle
+ * - ENOTDIR if dh is not a directory
+ * - EINVAL offset did not refer to a valid dirent or
+ * smbc_init not called.
+ *
+ * @see smbc_telldir()
+ *
+ *
+ * @todo In what does the reture and errno values mean?
+ */
+int smbc_lseekdir(int fd, off_t offset);
+
+/**@ingroup directory
+ * Create a directory.
+ *
+ * @param durl The url of the directory to create
+ *
+ * @param mode Specifies the permissions to use. It is modified
+ * by the process's umask in the usual way: the
+ * permissions of the created file are (mode & ~umask).
+ *
+ * @return 0 on success, < 0 on error with errno set:
+ * - EEXIST directory url already exists
+ * - EACCES The parent directory does not allow write
+ * permission to the process, or one of the directories
+ * - ENOENT A directory component in pathname does not
+ * exist.
+ * - EINVAL NULL durl passed or smbc_init not called.
+ * - ENOMEM Insufficient memory was available.
+ *
+ * @see smbc_rmdir()
+ *
+ */
+int smbc_mkdir(const char *durl, mode_t mode);
+
+
+/**@ingroup directory
+ * Remove a directory.
+ *
+ * @param durl The smb url of the directory to remove
+ *
+ * @return 0 on success, < 0 on error with errno set:
+ * - EACCES or EPERM Write access to the directory
+ * containing pathname was not allowed.
+ * - EINVAL durl is NULL or smbc_init not called.
+ * - ENOENT A directory component in pathname does not
+ * exist.
+ * - ENOTEMPTY directory contains entries.
+ * - ENOMEM Insufficient kernel memory was available.
+ *
+ * @see smbc_mkdir(), smbc_unlink()
+ *
+ * @todo Are errno values complete and correct?
+ */
+int smbc_rmdir(const char *durl);
+
+
+/**@ingroup attribute
+ * Get information about a file or directory.
+ *
+ * @param url The smb url to get information for
+ *
+ * @param st pointer to a buffer that will be filled with
+ * standard Unix struct stat information.
+ *
+ * @return 0 on success, < 0 on error with errno set:
+ * - ENOENT A component of the path file_name does not
+ * exist.
+ * - EINVAL a NULL url was passed or smbc_init not called.
+ * - EACCES Permission denied.
+ * - ENOMEM Out of memory
+ * - ENOTDIR The target dir, url, is not a directory.
+ *
+ * @see Unix stat()
+ *
+ */
+int smbc_stat(const char *url, struct stat *st);
+
+
+/**@ingroup attribute
+ * Get file information via an file descriptor.
+ *
+ * @param fd Open file handle from smbc_open() or smbc_creat()
+ *
+ * @param st pointer to a buffer that will be filled with
+ * standard Unix struct stat information.
+ *
+ * @return EBADF filedes is bad.
+ * - EACCES Permission denied.
+ * - EBADF fd is not a valid file descriptor
+ * - EINVAL Problems occurred in the underlying routines
+ * or smbc_init not called.
+ * - ENOMEM Out of memory
+ *
+ * @see smbc_stat(), Unix stat()
+ *
+ */
+int smbc_fstat(int fd, struct stat *st);
+
+
+/**@ingroup attribue
+ * Change the ownership of a file or directory.
+ *
+ * @param url The smb url of the file or directory to change
+ * ownership of.
+ *
+ * @param owner I have no idea?
+ *
+ * @param group I have not idea?
+ *
+ * @return 0 on success, < 0 on error with errno set:
+ * - EPERM The effective UID does not match the owner
+ * of the file, and is not zero; or the owner or group
+ * were specified incorrectly.
+ * - ENOENT The file does not exist.
+ * - ENOMEM Insufficient was available.
+ * - ENOENT file or directory does not exist
+ *
+ * @todo Are we actually going to be able to implement this function
+ *
+ * @todo How do we abstract owner and group uid and gid?
+ *
+ */
+int smbc_chown(const char *url, uid_t owner, gid_t group);
+
+
+/**@ingroup attribute
+ * Change the permissions of a file.
+ *
+ * @param url The smb url of the file or directory to change
+ * permissions of
+ *
+ * @param mode The permissions to set:
+ * - Put good explaination of permissions here!
+ *
+ * @return 0 on success, < 0 on error with errno set:
+ * - EPERM The effective UID does not match the owner
+ * of the file, and is not zero
+ * - ENOENT The file does not exist.
+ * - ENOMEM Insufficient was available.
+ * - ENOENT file or directory does not exist
+ *
+ * @todo Actually implement this fuction?
+ *
+ * @todo Are errno values complete and correct?
+ */
+int smbc_chmod(const char *url, mode_t mode);
+
+/**@ingroup attribute
+ * Change the last modification time on a file
+ *
+ * @param url The smb url of the file or directory to change
+ * the modification time of
+ *
+ * @param tbuf A timeval structure which contains the desired
+ * modification time. NOTE: Only the tv_sec field is
+ * used. The tv_usec (microseconds) portion is ignored.
+ *
+ * @return 0 on success, < 0 on error with errno set:
+ * - EINVAL The client library is not properly initialized
+ * - EPERM Permission was denied.
+ *
+ */
+int smbc_utimes(const char *url, struct timeval *tbuf);
+
+#ifdef HAVE_UTIME_H
+/**@ingroup attribute
+ * Change the last modification time on a file
+ *
+ * @param url The smb url of the file or directory to change
+ * the modification time of
+ *
+ * @param utbuf A utimebuf structure which contains the desired
+ * modification time. NOTE: Although the structure contains
+ * an access time as well, the access time value is ignored.
+ *
+ * @return 0 on success, < 0 on error with errno set:
+ * - EINVAL The client library is not properly initialized
+ * - ENOMEM No memory was available for internal needs
+ * - EPERM Permission was denied.
+ *
+ */
+int smbc_utime(const char *fname, struct utimbuf *utbuf);
+#endif
+
+/**@ingroup attribute
+ * Set extended attributes for a file. This is used for modifying a file's
+ * security descriptor (i.e. owner, group, and access control list)
+ *
+ * @param url The smb url of the file or directory to set extended
+ * attributes for.
+ *
+ * @param name The name of an attribute to be changed. Names are of
+ * one of the following forms:
+ *
+ * system.nt_sec_desc.<attribute name>
+ * system.nt_sec_desc.*
+ * system.nt_sec_desc.*+
+ *
+ * where <attribute name> is one of:
+ *
+ * revision
+ * owner
+ * owner+
+ * group
+ * group+
+ * acl:<name or sid>
+ * acl+:<name or sid>
+ *
+ * In the forms "system.nt_sec_desc.*" and
+ * "system.nt_sec_desc.*+", the asterisk and plus signs are
+ * literal, i.e. the string is provided exactly as shown, and
+ * the value parameter should contain a complete security
+ * descriptor with name:value pairs separated by tabs,
+ * commas, or newlines (not spaces!).
+ *
+ * The plus sign ('+') indicates that SIDs should be mapped
+ * to names. Without the plus sign, SIDs are not mapped;
+ * rather they are simply converted to a string format.
+ *
+ * @param value The value to be assigned to the specified attribute name.
+ * This buffer should contain only the attribute value if the
+ * name was of the "system.nt_sec_desc.<attribute_name>"
+ * form. If the name was of the "system.nt_sec_desc.*" form
+ * then a complete security descriptor, with name:value pairs
+ * separated by tabs, commas, or newlines (not spaces!),
+ * should be provided in this value buffer. A complete
+ * security descriptor will contain one or more entries
+ * selected from the following:
+ *
+ * REVISION:<revision number>
+ * OWNER:<sid or name>
+ * GROUP:<sid or name>
+ * ACL:<sid or name>:<type>/<flags>/<mask>
+ *
+ * The revision of the ACL specifies the internal Windows NT
+ * ACL revision for the security descriptor. If not specified
+ * it defaults to 1. Using values other than 1 may cause
+ * strange behaviour.
+ *
+ * The owner and group specify the owner and group sids for
+ * the object. If the attribute name (either '*+' with a
+ * complete security descriptor, or individual 'owner+' or
+ * 'group+' attribute names) ended with a plus sign, the
+ * specified name is resolved to a SID value, using the
+ * server on which the file or directory resides. Otherwise,
+ * the value should be provided in SID-printable format as
+ * S-1-x-y-z, and is used directly. The <sid or name>
+ * associated with the ACL: attribute should be provided
+ * similarly.
+ *
+ * @param size The number of the bytes of data in the value buffer
+ *
+ * @param flags A bit-wise OR of zero or more of the following:
+ * SMBC_XATTR_FLAG_CREATE -
+ * fail if the named attribute already exists
+ * SMBC_XATTR_FLAG_REPLACE -
+ * fail if the attribute does not already exist
+ *
+ * If neither flag is specified, the specified attributes
+ * will be added or replace existing attributes of the same
+ * name, as necessary.
+ *
+ * @return 0 on success, < 0 on error with errno set:
+ * - EINVAL The client library is not properly initialized
+ * or one of the parameters is not of a correct
+ * form
+ * - ENOMEM No memory was available for internal needs
+ * - EEXIST If the attribute already exists and the flag
+ * SMBC_XATTR_FLAG_CREAT was specified
+ * - ENOATTR If the attribute does not exist and the flag
+ * SMBC_XATTR_FLAG_REPLACE was specified
+ * - EPERM Permission was denied.
+ * - ENOTSUP The referenced file system does not support
+ * extended attributes
+ *
+ * @note Attribute names are compared in a case-insensitive
+ * fashion. All of the following are equivalent, although
+ * the all-lower-case name is the preferred format:
+ * system.nt_sec_desc.owner
+ * SYSTEM.NT_SEC_DESC.OWNER
+ * sYsTeM.nt_sEc_desc.owNER
+ *
+ */
+int smbc_setxattr(const char *url,
+ const char *name,
+ const void *value,
+ size_t size,
+ int flags);
+
+
+/**@ingroup attribute
+ * Set extended attributes for a file. This is used for modifying a file's
+ * security descriptor (i.e. owner, group, and access control list). The
+ * POSIX function which this maps to would act on a symbolic link rather than
+ * acting on what the symbolic link points to, but with no symbolic links in
+ * SMB file systems, this function is functionally identical to
+ * smbc_setxattr().
+ *
+ * @param url The smb url of the file or directory to set extended
+ * attributes for.
+ *
+ * @param name The name of an attribute to be changed. Names are of
+ * one of the following forms:
+ *
+ * system.nt_sec_desc.<attribute name>
+ * system.nt_sec_desc.*
+ * system.nt_sec_desc.*+
+ *
+ * where <attribute name> is one of:
+ *
+ * revision
+ * owner
+ * owner+
+ * group
+ * group+
+ * acl:<name or sid>
+ * acl+:<name or sid>
+ *
+ * In the forms "system.nt_sec_desc.*" and
+ * "system.nt_sec_desc.*+", the asterisk and plus signs are
+ * literal, i.e. the string is provided exactly as shown, and
+ * the value parameter should contain a complete security
+ * descriptor with name:value pairs separated by tabs,
+ * commas, or newlines (not spaces!).
+ *
+ * The plus sign ('+') indicates that SIDs should be mapped
+ * to names. Without the plus sign, SIDs are not mapped;
+ * rather they are simply converted to a string format.
+ *
+ * @param value The value to be assigned to the specified attribute name.
+ * This buffer should contain only the attribute value if the
+ * name was of the "system.nt_sec_desc.<attribute_name>"
+ * form. If the name was of the "system.nt_sec_desc.*" form
+ * then a complete security descriptor, with name:value pairs
+ * separated by tabs, commas, or newlines (not spaces!),
+ * should be provided in this value buffer. A complete
+ * security descriptor will contain one or more entries
+ * selected from the following:
+ *
+ * REVISION:<revision number>
+ * OWNER:<sid or name>
+ * GROUP:<sid or name>
+ * ACL:<sid or name>:<type>/<flags>/<mask>
+ *
+ * The revision of the ACL specifies the internal Windows NT
+ * ACL revision for the security descriptor. If not specified
+ * it defaults to 1. Using values other than 1 may cause
+ * strange behaviour.
+ *
+ * The owner and group specify the owner and group sids for
+ * the object. If the attribute name (either '*+' with a
+ * complete security descriptor, or individual 'owner+' or
+ * 'group+' attribute names) ended with a plus sign, the
+ * specified name is resolved to a SID value, using the
+ * server on which the file or directory resides. Otherwise,
+ * the value should be provided in SID-printable format as
+ * S-1-x-y-z, and is used directly. The <sid or name>
+ * associated with the ACL: attribute should be provided
+ * similarly.
+ *
+ * @param size The number of the bytes of data in the value buffer
+ *
+ * @param flags A bit-wise OR of zero or more of the following:
+ * SMBC_XATTR_FLAG_CREATE -
+ * fail if the named attribute already exists
+ * SMBC_XATTR_FLAG_REPLACE -
+ * fail if the attribute does not already exist
+ *
+ * If neither flag is specified, the specified attributes
+ * will be added or replace existing attributes of the same
+ * name, as necessary.
+ *
+ * @return 0 on success, < 0 on error with errno set:
+ * - EINVAL The client library is not properly initialized
+ * or one of the parameters is not of a correct
+ * form
+ * - ENOMEM No memory was available for internal needs
+ * - EEXIST If the attribute already exists and the flag
+ * SMBC_XATTR_FLAG_CREAT was specified
+ * - ENOATTR If the attribute does not exist and the flag
+ * SMBC_XATTR_FLAG_REPLACE was specified
+ * - EPERM Permission was denied.
+ * - ENOTSUP The referenced file system does not support
+ * extended attributes
+ *
+ * @note Attribute names are compared in a case-insensitive
+ * fashion. All of the following are equivalent, although
+ * the all-lower-case name is the preferred format:
+ * system.nt_sec_desc.owner
+ * SYSTEM.NT_SEC_DESC.OWNER
+ * sYsTeM.nt_sEc_desc.owNER
+ *
+ */
+int smbc_lsetxattr(const char *url,
+ const char *name,
+ const void *value,
+ size_t size,
+ int flags);
+
+
+/**@ingroup attribute
+ * Set extended attributes for a file. This is used for modifying a file's
+ * security descriptor (i.e. owner, group, and access control list)
+ *
+ * @param fd A file descriptor associated with an open file (as
+ * previously returned by smbc_open(), to get extended
+ * attributes for.
+ *
+ * @param name The name of an attribute to be changed. Names are of
+ * one of the following forms:
+ *
+ * system.nt_sec_desc.<attribute name>
+ * system.nt_sec_desc.*
+ * system.nt_sec_desc.*+
+ *
+ * where <attribute name> is one of:
+ *
+ * revision
+ * owner
+ * owner+
+ * group
+ * group+
+ * acl:<name or sid>
+ * acl+:<name or sid>
+ *
+ * In the forms "system.nt_sec_desc.*" and
+ * "system.nt_sec_desc.*+", the asterisk and plus signs are
+ * literal, i.e. the string is provided exactly as shown, and
+ * the value parameter should contain a complete security
+ * descriptor with name:value pairs separated by tabs,
+ * commas, or newlines (not spaces!).
+ *
+ * The plus sign ('+') indicates that SIDs should be mapped
+ * to names. Without the plus sign, SIDs are not mapped;
+ * rather they are simply converted to a string format.
+ *
+ * @param value The value to be assigned to the specified attribute name.
+ * This buffer should contain only the attribute value if the
+ * name was of the "system.nt_sec_desc.<attribute_name>"
+ * form. If the name was of the "system.nt_sec_desc.*" form
+ * then a complete security descriptor, with name:value pairs
+ * separated by tabs, commas, or newlines (not spaces!),
+ * should be provided in this value buffer. A complete
+ * security descriptor will contain one or more entries
+ * selected from the following:
+ *
+ * REVISION:<revision number>
+ * OWNER:<sid or name>
+ * GROUP:<sid or name>
+ * ACL:<sid or name>:<type>/<flags>/<mask>
+ *
+ * The revision of the ACL specifies the internal Windows NT
+ * ACL revision for the security descriptor. If not specified
+ * it defaults to 1. Using values other than 1 may cause
+ * strange behaviour.
+ *
+ * The owner and group specify the owner and group sids for
+ * the object. If the attribute name (either '*+' with a
+ * complete security descriptor, or individual 'owner+' or
+ * 'group+' attribute names) ended with a plus sign, the
+ * specified name is resolved to a SID value, using the
+ * server on which the file or directory resides. Otherwise,
+ * the value should be provided in SID-printable format as
+ * S-1-x-y-z, and is used directly. The <sid or name>
+ * associated with the ACL: attribute should be provided
+ * similarly.
+ *
+ * @param size The number of the bytes of data in the value buffer
+ *
+ * @param flags A bit-wise OR of zero or more of the following:
+ * SMBC_XATTR_FLAG_CREATE -
+ * fail if the named attribute already exists
+ * SMBC_XATTR_FLAG_REPLACE -
+ * fail if the attribute does not already exist
+ *
+ * If neither flag is specified, the specified attributes
+ * will be added or replace existing attributes of the same
+ * name, as necessary.
+ *
+ * @return 0 on success, < 0 on error with errno set:
+ * - EINVAL The client library is not properly initialized
+ * or one of the parameters is not of a correct
+ * form
+ * - ENOMEM No memory was available for internal needs
+ * - EEXIST If the attribute already exists and the flag
+ * SMBC_XATTR_FLAG_CREAT was specified
+ * - ENOATTR If the attribute does not exist and the flag
+ * SMBC_XATTR_FLAG_REPLACE was specified
+ * - EPERM Permission was denied.
+ * - ENOTSUP The referenced file system does not support
+ * extended attributes
+ *
+ * @note Attribute names are compared in a case-insensitive
+ * fashion. All of the following are equivalent, although
+ * the all-lower-case name is the preferred format:
+ * system.nt_sec_desc.owner
+ * SYSTEM.NT_SEC_DESC.OWNER
+ * sYsTeM.nt_sEc_desc.owNER
+ *
+ */
+int smbc_fsetxattr(int fd,
+ const char *name,
+ const void *value,
+ size_t size,
+ int flags);
+
+
+/**@ingroup attribute
+ * Get extended attributes for a file.
+ *
+ * @param url The smb url of the file or directory to get extended
+ * attributes for.
+ *
+ * @param name The name of an attribute to be retrieved. Names are of
+ * one of the following forms:
+ *
+ * system.nt_sec_desc.<attribute name>
+ * system.nt_sec_desc.*
+ * system.nt_sec_desc.*+
+ *
+ * where <attribute name> is one of:
+ *
+ * revision
+ * owner
+ * owner+
+ * group
+ * group+
+ * acl:<name or sid>
+ * acl+:<name or sid>
+ *
+ * In the forms "system.nt_sec_desc.*" and
+ * "system.nt_sec_desc.*+", the asterisk and plus signs are
+ * literal, i.e. the string is provided exactly as shown, and
+ * the value parameter will return a complete security
+ * descriptor with name:value pairs separated by tabs,
+ * commas, or newlines (not spaces!).
+ *
+ * The plus sign ('+') indicates that SIDs should be mapped
+ * to names. Without the plus sign, SIDs are not mapped;
+ * rather they are simply converted to a string format.
+ *
+ * @param value A pointer to a buffer in which the value of the specified
+ * attribute will be placed (unless size is zero).
+ *
+ * @param size The size of the buffer pointed to by value. This parameter
+ * may also be zero, in which case the size of the buffer
+ * required to hold the attribute value will be returned,
+ * but nothing will be placed into the value buffer.
+ *
+ * @return 0 on success, < 0 on error with errno set:
+ * - EINVAL The client library is not properly initialized
+ * or one of the parameters is not of a correct
+ * form
+ * - ENOMEM No memory was available for internal needs
+ * - EEXIST If the attribute already exists and the flag
+ * SMBC_XATTR_FLAG_CREAT was specified
+ * - ENOATTR If the attribute does not exist and the flag
+ * SMBC_XATTR_FLAG_REPLACE was specified
+ * - EPERM Permission was denied.
+ * - ENOTSUP The referenced file system does not support
+ * extended attributes
+ *
+ */
+int smbc_getxattr(const char *url,
+ const char *name,
+ const void *value,
+ size_t size);
+
+
+/**@ingroup attribute
+ * Get extended attributes for a file. The POSIX function which this maps to
+ * would act on a symbolic link rather than acting on what the symbolic link
+ * points to, but with no symbolic links in SMB file systems, this function
+ * is functionally identical to smbc_getxattr().
+ *
+ * @param url The smb url of the file or directory to get extended
+ * attributes for.
+ *
+ * @param name The name of an attribute to be retrieved. Names are of
+ * one of the following forms:
+ *
+ * system.nt_sec_desc.<attribute name>
+ * system.nt_sec_desc.*
+ * system.nt_sec_desc.*+
+ *
+ * where <attribute name> is one of:
+ *
+ * revision
+ * owner
+ * owner+
+ * group
+ * group+
+ * acl:<name or sid>
+ * acl+:<name or sid>
+ *
+ * In the forms "system.nt_sec_desc.*" and
+ * "system.nt_sec_desc.*+", the asterisk and plus signs are
+ * literal, i.e. the string is provided exactly as shown, and
+ * the value parameter will return a complete security
+ * descriptor with name:value pairs separated by tabs,
+ * commas, or newlines (not spaces!).
+ *
+ * The plus sign ('+') indicates that SIDs should be mapped
+ * to names. Without the plus sign, SIDs are not mapped;
+ * rather they are simply converted to a string format.
+ *
+ * @param value A pointer to a buffer in which the value of the specified
+ * attribute will be placed (unless size is zero).
+ *
+ * @param size The size of the buffer pointed to by value. This parameter
+ * may also be zero, in which case the size of the buffer
+ * required to hold the attribute value will be returned,
+ * but nothing will be placed into the value buffer.
+ *
+ * @return 0 on success, < 0 on error with errno set:
+ * - EINVAL The client library is not properly initialized
+ * or one of the parameters is not of a correct
+ * form
+ * - ENOMEM No memory was available for internal needs
+ * - EEXIST If the attribute already exists and the flag
+ * SMBC_XATTR_FLAG_CREAT was specified
+ * - ENOATTR If the attribute does not exist and the flag
+ * SMBC_XATTR_FLAG_REPLACE was specified
+ * - EPERM Permission was denied.
+ * - ENOTSUP The referenced file system does not support
+ * extended attributes
+ *
+ */
+int smbc_lgetxattr(const char *url,
+ const char *name,
+ const void *value,
+ size_t size);
+
+
+/**@ingroup attribute
+ * Get extended attributes for a file.
+ *
+ * @param fd A file descriptor associated with an open file (as
+ * previously returned by smbc_open(), to get extended
+ * attributes for.
+ *
+ * @param name The name of an attribute to be retrieved. Names are of
+ * one of the following forms:
+ *
+ * system.nt_sec_desc.<attribute name>
+ * system.nt_sec_desc.*
+ * system.nt_sec_desc.*+
+ *
+ * where <attribute name> is one of:
+ *
+ * revision
+ * owner
+ * owner+
+ * group
+ * group+
+ * acl:<name or sid>
+ * acl+:<name or sid>
+ *
+ * In the forms "system.nt_sec_desc.*" and
+ * "system.nt_sec_desc.*+", the asterisk and plus signs are
+ * literal, i.e. the string is provided exactly as shown, and
+ * the value parameter will return a complete security
+ * descriptor with name:value pairs separated by tabs,
+ * commas, or newlines (not spaces!).
+ *
+ * The plus sign ('+') indicates that SIDs should be mapped
+ * to names. Without the plus sign, SIDs are not mapped;
+ * rather they are simply converted to a string format.
+ *
+ * @param value A pointer to a buffer in which the value of the specified
+ * attribute will be placed (unless size is zero).
+ *
+ * @param size The size of the buffer pointed to by value. This parameter
+ * may also be zero, in which case the size of the buffer
+ * required to hold the attribute value will be returned,
+ * but nothing will be placed into the value buffer.
+ *
+ * @return 0 on success, < 0 on error with errno set:
+ * - EINVAL The client library is not properly initialized
+ * or one of the parameters is not of a correct
+ * form
+ * - ENOMEM No memory was available for internal needs
+ * - EEXIST If the attribute already exists and the flag
+ * SMBC_XATTR_FLAG_CREAT was specified
+ * - ENOATTR If the attribute does not exist and the flag
+ * SMBC_XATTR_FLAG_REPLACE was specified
+ * - EPERM Permission was denied.
+ * - ENOTSUP The referenced file system does not support
+ * extended attributes
+ *
+ */
+int smbc_fgetxattr(int fd,
+ const char *name,
+ const void *value,
+ size_t size);
+
+
+/**@ingroup attribute
+ * Remove extended attributes for a file. This is used for modifying a file's
+ * security descriptor (i.e. owner, group, and access control list)
+ *
+ * @param url The smb url of the file or directory to remove the extended
+ * attributes for.
+ *
+ * @param name The name of an attribute to be removed. Names are of
+ * one of the following forms:
+ *
+ * system.nt_sec_desc.<attribute name>
+ * system.nt_sec_desc.*
+ * system.nt_sec_desc.*+
+ *
+ * where <attribute name> is one of:
+ *
+ * revision
+ * owner
+ * owner+
+ * group
+ * group+
+ * acl:<name or sid>
+ * acl+:<name or sid>
+ *
+ * In the forms "system.nt_sec_desc.*" and
+ * "system.nt_sec_desc.*+", the asterisk and plus signs are
+ * literal, i.e. the string is provided exactly as shown, and
+ * the value parameter will return a complete security
+ * descriptor with name:value pairs separated by tabs,
+ * commas, or newlines (not spaces!).
+ *
+ * The plus sign ('+') indicates that SIDs should be mapped
+ * to names. Without the plus sign, SIDs are not mapped;
+ * rather they are simply converted to a string format.
+ *
+ * @return 0 on success, < 0 on error with errno set:
+ * - EINVAL The client library is not properly initialized
+ * - ENOMEM No memory was available for internal needs
+ * - EPERM Permission was denied.
+ * - ENOTSUP The referenced file system does not support
+ * extended attributes
+ *
+ */
+int smbc_removexattr(const char *url,
+ const char *name);
+
+
+/**@ingroup attribute
+ * Remove extended attributes for a file. This is used for modifying a file's
+ * security descriptor (i.e. owner, group, and access control list) The POSIX
+ * function which this maps to would act on a symbolic link rather than acting
+ * on what the symbolic link points to, but with no symbolic links in SMB file
+ * systems, this function is functionally identical to smbc_removexattr().
+ *
+ * @param url The smb url of the file or directory to remove the extended
+ * attributes for.
+ *
+ * @param name The name of an attribute to be removed. Names are of
+ * one of the following forms:
+ *
+ * system.nt_sec_desc.<attribute name>
+ * system.nt_sec_desc.*
+ * system.nt_sec_desc.*+
+ *
+ * where <attribute name> is one of:
+ *
+ * revision
+ * owner
+ * owner+
+ * group
+ * group+
+ * acl:<name or sid>
+ * acl+:<name or sid>
+ *
+ * In the forms "system.nt_sec_desc.*" and
+ * "system.nt_sec_desc.*+", the asterisk and plus signs are
+ * literal, i.e. the string is provided exactly as shown, and
+ * the value parameter will return a complete security
+ * descriptor with name:value pairs separated by tabs,
+ * commas, or newlines (not spaces!).
+ *
+ * The plus sign ('+') indicates that SIDs should be mapped
+ * to names. Without the plus sign, SIDs are not mapped;
+ * rather they are simply converted to a string format.
+ *
+ * @return 0 on success, < 0 on error with errno set:
+ * - EINVAL The client library is not properly initialized
+ * - ENOMEM No memory was available for internal needs
+ * - EPERM Permission was denied.
+ * - ENOTSUP The referenced file system does not support
+ * extended attributes
+ *
+ */
+int smbc_lremovexattr(const char *url,
+ const char *name);
+
+
+/**@ingroup attribute
+ * Remove extended attributes for a file. This is used for modifying a file's
+ * security descriptor (i.e. owner, group, and access control list)
+ *
+ * @param fd A file descriptor associated with an open file (as
+ * previously returned by smbc_open(), to get extended
+ * attributes for.
+ *
+ * @param name The name of an attribute to be removed. Names are of
+ * one of the following forms:
+ *
+ * system.nt_sec_desc.<attribute name>
+ * system.nt_sec_desc.*
+ * system.nt_sec_desc.*+
+ *
+ * where <attribute name> is one of:
+ *
+ * revision
+ * owner
+ * owner+
+ * group
+ * group+
+ * acl:<name or sid>
+ * acl+:<name or sid>
+ *
+ * In the forms "system.nt_sec_desc.*" and
+ * "system.nt_sec_desc.*+", the asterisk and plus signs are
+ * literal, i.e. the string is provided exactly as shown, and
+ * the value parameter will return a complete security
+ * descriptor with name:value pairs separated by tabs,
+ * commas, or newlines (not spaces!).
+ *
+ * The plus sign ('+') indicates that SIDs should be mapped
+ * to names. Without the plus sign, SIDs are not mapped;
+ * rather they are simply converted to a string format.
+ *
+ * @return 0 on success, < 0 on error with errno set:
+ * - EINVAL The client library is not properly initialized
+ * - ENOMEM No memory was available for internal needs
+ * - EPERM Permission was denied.
+ * - ENOTSUP The referenced file system does not support
+ * extended attributes
+ *
+ */
+int smbc_fremovexattr(int fd,
+ const char *name);
+
+
+/**@ingroup attribute
+ * List the supported extended attribute names associated with a file
+ *
+ * @param url The smb url of the file or directory to list the extended
+ * attributes for.
+ *
+ * @param list A pointer to a buffer in which the list of attributes for
+ * the specified file or directory will be placed (unless
+ * size is zero).
+ *
+ * @param size The size of the buffer pointed to by list. This parameter
+ * may also be zero, in which case the size of the buffer
+ * required to hold all of the attribute names will be
+ * returned, but nothing will be placed into the list buffer.
+ *
+ * @return 0 on success, < 0 on error with errno set:
+ * - EINVAL The client library is not properly initialized
+ * - ENOMEM No memory was available for internal needs
+ * - EPERM Permission was denied.
+ * - ENOTSUP The referenced file system does not support
+ * extended attributes
+ *
+ * @note This function always returns all attribute names supported
+ * by NT file systems, regardless of wether the referenced
+ * file system supports extended attributes (e.g. a Windows
+ * 2000 machine supports extended attributes if NTFS is used,
+ * but not if FAT is used, and Windows 98 doesn't support
+ * extended attributes at all. Whether this is a feature or
+ * a bug is yet to be decided.
+ */
+int smbc_listxattr(const char *url,
+ char *list,
+ size_t size);
+
+/**@ingroup attribute
+ * List the supported extended attribute names associated with a file The
+ * POSIX function which this maps to would act on a symbolic link rather than
+ * acting on what the symbolic link points to, but with no symbolic links in
+ * SMB file systems, this function is functionally identical to
+ * smbc_listxattr().
+ *
+ * @param url The smb url of the file or directory to list the extended
+ * attributes for.
+ *
+ * @param list A pointer to a buffer in which the list of attributes for
+ * the specified file or directory will be placed (unless
+ * size is zero).
+ *
+ * @param size The size of the buffer pointed to by list. This parameter
+ * may also be zero, in which case the size of the buffer
+ * required to hold all of the attribute names will be
+ * returned, but nothing will be placed into the list buffer.
+ *
+ * @return 0 on success, < 0 on error with errno set:
+ * - EINVAL The client library is not properly initialized
+ * - ENOMEM No memory was available for internal needs
+ * - EPERM Permission was denied.
+ * - ENOTSUP The referenced file system does not support
+ * extended attributes
+ *
+ * @note This function always returns all attribute names supported
+ * by NT file systems, regardless of wether the referenced
+ * file system supports extended attributes (e.g. a Windows
+ * 2000 machine supports extended attributes if NTFS is used,
+ * but not if FAT is used, and Windows 98 doesn't support
+ * extended attributes at all. Whether this is a feature or
+ * a bug is yet to be decided.
+ */
+int smbc_llistxattr(const char *url,
+ char *list,
+ size_t size);
+
+/**@ingroup attribute
+ * List the supported extended attribute names associated with a file
+ *
+ * @param fd A file descriptor associated with an open file (as
+ * previously returned by smbc_open(), to get extended
+ * attributes for.
+ *
+ * @param list A pointer to a buffer in which the list of attributes for
+ * the specified file or directory will be placed (unless
+ * size is zero).
+ *
+ * @param size The size of the buffer pointed to by list. This parameter
+ * may also be zero, in which case the size of the buffer
+ * required to hold all of the attribute names will be
+ * returned, but nothing will be placed into the list buffer.
+ *
+ * @return 0 on success, < 0 on error with errno set:
+ * - EINVAL The client library is not properly initialized
+ * - ENOMEM No memory was available for internal needs
+ * - EPERM Permission was denied.
+ * - ENOTSUP The referenced file system does not support
+ * extended attributes
+ *
+ * @note This function always returns all attribute names supported
+ * by NT file systems, regardless of wether the referenced
+ * file system supports extended attributes (e.g. a Windows
+ * 2000 machine supports extended attributes if NTFS is used,
+ * but not if FAT is used, and Windows 98 doesn't support
+ * extended attributes at all. Whether this is a feature or
+ * a bug is yet to be decided.
+ */
+int smbc_flistxattr(int fd,
+ char *list,
+ size_t size);
+
+/**@ingroup print
+ * Print a file given the name in fname. It would be a URL ...
+ *
+ * @param fname The URL of a file on a remote SMB server that the
+ * caller wants printed
+ *
+ * @param printq The URL of the print share to print the file to.
+ *
+ * @return 0 on success, < 0 on error with errno set:
+ *
+ * - EINVAL fname or printq was NULL or smbc_init not
+ * not called.
+ * and errors returned by smbc_open
+ *
+ */
+int smbc_print_file(const char *fname, const char *printq);
+
+/**@ingroup print
+ * Open a print file that can be written to by other calls. This simply
+ * does an smbc_open call after checking if there is a file name on the
+ * URI. If not, a temporary name is added ...
+ *
+ * @param fname The URL of the print share to print to?
+ *
+ * @returns A file handle for the print file if successful.
+ * Returns -1 if an error ocurred and errno has the values
+ * - EINVAL fname was NULL or smbc_init not called.
+ * - all errors returned by smbc_open
+ *
+ */
+int smbc_open_print_job(const char *fname);
+
+/**@ingroup print
+ * List the print jobs on a print share, for the moment, pass a callback
+ *
+ * @param purl The url of the print share to list the jobs of
+ *
+ * @param fn Callback function the receives printjob info
+ *
+ * @return 0 on success, < 0 on error with errno set:
+ * - EINVAL fname was NULL or smbc_init not called
+ * - EACCES ???
+ */
+int smbc_list_print_jobs(const char *purl, smbc_list_print_job_fn fn);
+
+/**@ingroup print
+ * Delete a print job
+ *
+ * @param purl Url of the print share
+ *
+ * @param id The id of the job to delete
+ *
+ * @return 0 on success, < 0 on error with errno set:
+ * - EINVAL fname was NULL or smbc_init not called
+ *
+ * @todo what errno values are possible here?
+ */
+int smbc_unlink_print_job(const char *purl, int id);
+
+/**@ingroup callback
+ * Remove a server from the cached server list it's unused.
+ *
+ * @param context pointer to smb context
+ *
+ * @param srv pointer to server to remove
+ *
+ * @return On success, 0 is returned. 1 is returned if the server could not
+ * be removed. Also useable outside libsmbclient.
+ */
+int smbc_remove_unused_server(SMBCCTX * context, SMBCSRV * srv);
+
+#endif /* SMBCLIENT_H_INCLUDED */
diff --git a/source/include/local.h b/source/include/local.h
new file mode 100644
index 00000000000..540365047a2
--- /dev/null
+++ b/source/include/local.h
@@ -0,0 +1,233 @@
+/* Copyright (C) 1995-1998 Samba-Team */
+/* Copyright (C) 1998 John H Terpstra <jht@aquasoft.com.au> */
+
+/* local definitions for file server */
+#ifndef _LOCAL_H
+#define _LOCAL_H
+
+/* The default workgroup - usually overridden in smb.conf */
+#ifndef WORKGROUP
+#define WORKGROUP "WORKGROUP"
+#endif
+
+/* the maximum debug level to compile into the code. This assumes a good
+ optimising compiler that can remove unused code
+ for embedded or low-memory systems set this to a value like 2 to get
+ only important messages. This gives *much* smaller binaries
+*/
+#ifndef MAX_DEBUG_LEVEL
+#define MAX_DEBUG_LEVEL 1000
+#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. */
+#define GLOBAL_NAME "global"
+#define GLOBAL_NAME2 "globals"
+
+/* This defines the section name in the configuration file that will
+ refer to the special "homes" service */
+#define HOMES_NAME "homes"
+
+/* This defines the section name in the configuration file that will
+ refer to the special "printers" service */
+#define PRINTERS_NAME "printers"
+
+/* Yves Gaige <yvesg@hptnodur.grenoble.hp.com> requested this set this */
+/* to a maximum of 8 if old smb clients break because of long printer names. */
+#define MAXPRINTERLEN 15
+
+/* max number of directories open at once */
+/* note that with the new directory code this no longer requires a
+ file handle per directory, but large numbers do use more memory */
+#define MAX_OPEN_DIRECTORIES 256
+
+/* max number of directory handles */
+/* As this now uses the bitmap code this can be
+ quite large. */
+#define MAX_DIRECTORY_HANDLES 2048
+
+/* maximum number of file caches per smbd */
+#define MAX_WRITE_CACHES 10
+
+/* define what facility to use for syslog */
+#ifndef SYSLOG_FACILITY
+#define SYSLOG_FACILITY LOG_DAEMON
+#endif
+
+/*
+ * Default number of maximum open files per smbd. This is
+ * also limited by the maximum available file descriptors
+ * per process and can also be set in smb.conf as "max open files"
+ * in the [global] section.
+ */
+
+#ifndef MAX_OPEN_FILES
+#define MAX_OPEN_FILES 10000
+#endif
+
+#define WORDMAX 0xFFFF
+
+/* the maximum password length before we declare a likely attack */
+#define MAX_PASS_LEN 200
+
+/* separators for lists */
+#define LIST_SEP " \t,;\n\r"
+
+/* wchar separators for lists */
+#define LIST_SEP_W wchar_list_sep
+
+/* this is where browse lists are kept in the lock dir */
+#define SERVER_LIST "browse.dat"
+
+/* shall filenames with illegal chars in them get mangled in long
+ filename listings? */
+#define MANGLE_LONG_FILENAMES
+
+/* define this if you want to stop spoofing with .. and soft links
+ NOTE: This also slows down the server considerably */
+#define REDUCE_PATHS
+
+/* the size of the directory cache */
+#define DIRCACHESIZE 20
+
+/* what default type of filesystem do we want this to show up as in a
+ NT file manager window? */
+#define FSTYPE_STRING "NTFS"
+
+/* the default guest account - normally set in the Makefile or smb.conf */
+#ifndef GUEST_ACCOUNT
+#define GUEST_ACCOUNT "nobody"
+#endif
+
+/* user to test password server with as invalid in security=server mode. */
+#ifndef INVALID_USER_PREFIX
+#define INVALID_USER_PREFIX "sambatest"
+#endif
+
+/* the default pager to use for the client "more" command. Users can
+ override this with the PAGER environment variable */
+#ifndef PAGER
+#define PAGER "more"
+#endif
+
+/* the size of the uid cache used to reduce valid user checks */
+#define VUID_CACHE_SIZE 32
+
+/* the following control timings of various actions. Don't change
+ them unless you know what you are doing. These are all in seconds */
+#define DEFAULT_SMBD_TIMEOUT (60*60*24*7)
+#define SMBD_RELOAD_CHECK (180)
+#define IDLE_CLOSED_TIMEOUT (60)
+#define DPTR_IDLE_TIMEOUT (120)
+#define SMBD_SELECT_TIMEOUT (60)
+#define NMBD_SELECT_LOOP (10)
+#define BROWSE_INTERVAL (60)
+#define REGISTRATION_INTERVAL (10*60)
+#define NMBD_INETD_TIMEOUT (120)
+#define NMBD_MAX_TTL (24*60*60)
+#define LPQ_LOCK_TIMEOUT (5)
+#define NMBD_INTERFACES_RELOAD (120)
+#define NMBD_UNEXPECTED_TIMEOUT (15)
+
+/* the following are in milliseconds */
+#define LOCK_RETRY_TIMEOUT (100)
+
+/* do you want to dump core (carefully!) when an internal error is
+ encountered? Samba will be careful to make the core file only
+ accessible to root */
+#define DUMP_CORE 1
+
+/* shall we support browse requests via a FIFO to nmbd? */
+#define ENABLE_FIFO 1
+
+/* how long (in miliseconds) to wait for a socket connect to happen */
+#define LONG_CONNECT_TIMEOUT 30000
+#define SHORT_CONNECT_TIMEOUT 5000
+
+/* the default netbios keepalive timeout */
+#define DEFAULT_KEEPALIVE 300
+
+/* the directory to sit in when idle */
+/* #define IDLE_DIR "/" */
+
+/* Timout (in seconds) to wait for an oplock break
+ message to return from the client. */
+
+#define OPLOCK_BREAK_TIMEOUT 30
+
+/* Timout (in seconds) to add to the oplock break timeout
+ to wait for the smbd to smbd message to return. */
+
+#define OPLOCK_BREAK_TIMEOUT_FUDGEFACTOR 2
+
+/* the read preciction code has been disabled until some problems with
+ it are worked out */
+#define USE_READ_PREDICTION 0
+
+/*
+ * Default passwd chat script.
+ */
+
+#define DEFAULT_PASSWD_CHAT "*new*password* %n\\n *new*password* %n\\n *changed*"
+
+/* Minimum length of allowed password when changing UNIX password. */
+#define MINPASSWDLENGTH 5
+
+/* maximum ID number used for session control. This cannot be larger
+ than 62*62 for the current code */
+#define MAX_SESSION_ID 3000
+
+/* For the benifit of PAM and the 'session exec' scripts, we fake up a terminal
+ name. This can be in one of two forms: The first for systems not using
+ utmp (and therefore not constrained as to length or the need for a number
+ < 3000 or so) and the second for systems with this 'well behaved terminal
+ like name' constraint.
+*/
+
+#ifndef SESSION_TEMPLATE
+/* Paramaters are 'pid' and 'vuid' */
+#define SESSION_TEMPLATE "smb/%lu/%d"
+#endif
+
+#ifndef SESSION_UTMP_TEMPLATE
+#define SESSION_UTMP_TEMPLATE "smb/%d"
+#endif
+
+/* the maximum age in seconds of a password. Should be a lp_ parameter */
+#define MAX_PASSWORD_AGE (21*24*60*60)
+
+/* Allocation roundup. */
+#define SMB_ROUNDUP_ALLOCATION_SIZE 0x100000
+
+/* shall we deny oplocks to clients that get timeouts? */
+#define FASCIST_OPLOCK_BACKOFF 1
+
+/* this enables the "rabbit pellet" fix for SMBwritebraw */
+#define RABBIT_PELLET_FIX 1
+
+/* Max number of jobs per print queue. */
+#define PRINT_MAX_JOBID 10000
+
+/* Max number of open RPC pipes. */
+#define MAX_OPEN_PIPES 2048
+
+/* Tuning for server auth mutex. */
+#define CLI_AUTH_TIMEOUT 5000 /* In milli-seconds. */
+#define NUM_CLI_AUTH_CONNECT_RETRIES 3
+/* Number in seconds to wait for the mutex. This must be less than 30 seconds. */
+#define SERVER_MUTEX_WAIT_TIME ( ((NUM_CLI_AUTH_CONNECT_RETRIES) * ((CLI_AUTH_TIMEOUT)/1000)) + 5)
+/* Number in seconds for winbindd to wait for the mutex. Make this 2 * smbd wait time. */
+#define WINBIND_SERVER_MUTEX_WAIT_TIME (( ((NUM_CLI_AUTH_CONNECT_RETRIES) * ((CLI_AUTH_TIMEOUT)/1000)) + 5)*2)
+
+/* Max number of simultaneous winbindd socket connections. */
+#define WINBINDD_MAX_SIMULTANEOUS_CLIENTS 200
+
+/* Buffer size to use when printing backtraces */
+#define BACKTRACE_STACK_SIZE 64
+
+/* size of listen() backlog in smbd */
+#define SMBD_LISTEN_BACKLOG 50
+
+#endif
diff --git a/source/include/mangle.h b/source/include/mangle.h
new file mode 100644
index 00000000000..769278d828b
--- /dev/null
+++ b/source/include/mangle.h
@@ -0,0 +1,14 @@
+#ifndef _MANGLE_H_
+#define _MANGLE_H_
+/*
+ header for 8.3 name mangling interface
+*/
+
+struct mangle_fns {
+ BOOL (*is_mangled)(const char *s);
+ BOOL (*is_8_3)(const char *fname, BOOL check_case, BOOL allow_wildcards);
+ void (*reset)(void);
+ BOOL (*check_cache)(char *s);
+ void (*name_map)(char *OutName, BOOL need83, BOOL cache83);
+};
+#endif /* _MANGLE_H_ */
diff --git a/source/include/mapping.h b/source/include/mapping.h
new file mode 100644
index 00000000000..fdaa2b04532
--- /dev/null
+++ b/source/include/mapping.h
@@ -0,0 +1,33 @@
+/*
+ * Unix SMB/CIFS implementation.
+ * RPC Pipe client / server routines
+ * Copyright (C) Andrew Tridgell 1992-2000,
+ * Copyright (C) Jean François Micouleau 1998-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 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.
+ */
+
+#define ENUM_ONLY_MAPPED True
+#define ENUM_ALL_MAPPED False
+
+typedef struct _GROUP_MAP {
+ struct pdb_methods *methods;
+ gid_t gid;
+ DOM_SID sid;
+ enum SID_NAME_USE sid_name_use;
+ fstring nt_name;
+ fstring comment;
+} GROUP_MAP;
+
diff --git a/source/include/md5.h b/source/include/md5.h
new file mode 100644
index 00000000000..6665171e7c5
--- /dev/null
+++ b/source/include/md5.h
@@ -0,0 +1,24 @@
+#ifndef MD5_H
+#define MD5_H
+#ifndef HEADER_MD5_H
+/* Try to avoid clashes with OpenSSL */
+#define HEADER_MD5_H
+#endif
+
+struct MD5Context {
+ uint32 buf[4];
+ uint32 bits[2];
+ unsigned char in[64];
+};
+
+void MD5Init(struct MD5Context *context);
+void MD5Update(struct MD5Context *context, unsigned char const *buf,
+ unsigned len);
+void MD5Final(unsigned char digest[16], struct MD5Context *context);
+
+/*
+ * This is needed to make RSAREF happy on some MS-DOS compilers.
+ */
+typedef struct MD5Context MD5_CTX;
+
+#endif /* !MD5_H */
diff --git a/source/include/messages.h b/source/include/messages.h
new file mode 100644
index 00000000000..37e9372cdaa
--- /dev/null
+++ b/source/include/messages.h
@@ -0,0 +1,71 @@
+/*
+ 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 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 _MESSAGES_H_
+#define _MESSAGES_H_
+
+/* general messages */
+#define MSG_DEBUG 1
+#define MSG_PING 2
+#define MSG_PONG 3
+#define MSG_PROFILE 4
+#define MSG_REQ_DEBUGLEVEL 5
+#define MSG_DEBUGLEVEL 6
+#define MSG_REQ_PROFILELEVEL 7
+#define MSG_PROFILELEVEL 8
+#define MSG_REQ_POOL_USAGE 9
+#define MSG_POOL_USAGE 10
+
+/* If dmalloc is included, set a steady-state mark */
+#define MSG_REQ_DMALLOC_MARK 11
+
+/* If dmalloc is included, dump to the dmalloc log a description of
+ * what has changed since the last MARK */
+#define MSG_REQ_DMALLOC_LOG_CHANGED 12
+
+#define MSG_SHUTDOWN 13
+
+/* nmbd messages */
+#define MSG_FORCE_ELECTION 1001
+#define MSG_WINS_NEW_ENTRY 1002
+
+/* printing messages */
+/* #define MSG_PRINTER_NOTIFY 2001*/ /* Obsolete */
+#define MSG_PRINTER_DRVUPGRADE 2002
+#define MSG_PRINTER_NOTIFY2 2003
+#define MSG_PRINTERDATA_INIT_RESET 2004
+
+/* smbd messages */
+#define MSG_SMB_CONF_UPDATED 3001
+#define MSG_SMB_FORCE_TDIS 3002
+#define MSG_SMB_SAM_SYNC 3003
+#define MSG_SMB_SAM_REPL 3004
+#define MSG_SMB_UNLOCK 3005
+
+/* Flags to classify messages - used in message_send_all() */
+/* Sender will filter by flag. */
+
+#define FLAG_MSG_GENERAL 0x0001
+#define FLAG_MSG_SMBD 0x0002
+#define FLAG_MSG_NMBD 0x0004
+#define FLAG_MSG_PRINTING 0x0008
+
+#endif
diff --git a/source/include/modconf.h b/source/include/modconf.h
new file mode 100644
index 00000000000..f5cc5ef4889
--- /dev/null
+++ b/source/include/modconf.h
@@ -0,0 +1,34 @@
+#ifndef _MODCONF_H_
+#define _MODCONF_H_
+/*
+ Unix SMB/CIFS implementation.
+
+ ModConf headers
+
+ Copyright (C) Simo Sorce 2003
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 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 Library General Public
+ License along with this library; if not, write to the
+ Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+*/
+
+#define SAMBA_CONFIG_INTERFACE_VERSION 1
+
+/* Filled out by config backends */
+struct config_functions {
+ NTSTATUS (*init)(char *params);
+ NTSTATUS (*load)(BOOL (*sfunc)(const char *),BOOL (*pfunc)(const char *, const char *));
+ NTSTATUS (*close)(void);
+};
+#endif /* _MODCONF_H_ */
diff --git a/source/include/module.h b/source/include/module.h
new file mode 100644
index 00000000000..c41310c7f75
--- /dev/null
+++ b/source/include/module.h
@@ -0,0 +1,38 @@
+/*
+ Unix SMB/CIFS implementation.
+ Handling of idle/exit events
+ 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 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 _MODULE_H
+#define _MODULE_H
+
+/* Module support */
+typedef NTSTATUS (init_module_function) (void);
+
+
+typedef int smb_event_id_t;
+#define SMB_EVENT_ID_INVALID (-1)
+
+#define SMB_IDLE_EVENT_DEFAULT_INTERVAL 180
+#define SMB_IDLE_EVENT_MIN_INTERVAL 30
+
+typedef void (smb_idle_event_fn)(void **data,time_t *interval,time_t now);
+
+typedef void (smb_exit_event_fn)(void **data);
+
+#endif /* _MODULE_H */
diff --git a/source/include/msdfs.h b/source/include/msdfs.h
new file mode 100644
index 00000000000..32aa7ecef25
--- /dev/null
+++ b/source/include/msdfs.h
@@ -0,0 +1,85 @@
+/*
+ Unix SMB/Netbios implementation.
+ Version 3.0
+ MSDfs services for Samba
+ Copyright (C) Shirish Kalele 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 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 _MSDFS_H
+#define _MSDFS_H
+
+#define REFERRAL_TTL 600
+
+/* Flags used in trans2 Get Referral reply */
+#define DFSREF_REFERRAL_SERVER 0x1
+#define DFSREF_STORAGE_SERVER 0x2
+
+/* Referral sizes */
+#define VERSION2_REFERRAL_SIZE 0x16
+#define VERSION3_REFERRAL_SIZE 0x22
+#define REFERRAL_HEADER_SIZE 0x08
+
+/* Maximum number of referrals for each Dfs volume */
+#define MAX_REFERRAL_COUNT 256
+
+struct referral
+{
+ pstring alternate_path; /* contains the path referred */
+ uint32 proximity;
+ uint32 ttl; /* how long should client cache referral */
+};
+
+struct junction_map
+{
+ pstring service_name;
+ pstring volume_name;
+ int referral_count;
+ struct referral* referral_list;
+};
+
+struct dfs_path
+{
+ pstring hostname;
+ pstring servicename;
+ pstring reqpath;
+};
+
+#define RESOLVE_DFSPATH(name, conn, inbuf, outbuf) \
+{ if ((SVAL(inbuf,smb_flg2) & FLAGS2_DFS_PATHNAMES) && \
+ lp_host_msdfs() && lp_msdfs_root(SNUM(conn)) && \
+ dfs_redirect(name,conn,False)) \
+ return ERROR_BOTH(NT_STATUS_PATH_NOT_COVERED, \
+ ERRSRV, ERRbadpath);; }
+
+#define RESOLVE_FINDFIRST_DFSPATH(name, conn, inbuf, outbuf) \
+{ if ( (SVAL(inbuf,smb_flg2) & FLAGS2_DFS_PATHNAMES) || \
+ ((get_remote_arch() == RA_WIN95) && lp_msdfs_root(SNUM(conn))) ) \
+ if (lp_host_msdfs() && dfs_redirect(name,conn,True)) \
+ return ERROR_BOTH(NT_STATUS_PATH_NOT_COVERED, \
+ ERRSRV, ERRbadpath);; }
+
+
+#define init_dfsroot(conn, inbuf, outbuf) \
+{ if (lp_msdfs_root(SNUM(conn)) && lp_host_msdfs()) { \
+ DEBUG(2,("Serving %s as a Dfs root\n", \
+ lp_servicename(SNUM(conn)) )); \
+ SSVAL(outbuf, smb_vwv2, SMB_SHARE_IN_DFS \
+ | SVAL(outbuf, smb_vwv2)); \
+} }
+
+#endif /* _MSDFS_H */
diff --git a/source/include/nameserv.h b/source/include/nameserv.h
new file mode 100644
index 00000000000..ec3d56c06b7
--- /dev/null
+++ b/source/include/nameserv.h
@@ -0,0 +1,627 @@
+#ifndef _NAMESERV_H_
+#define _NAMESERV_H_
+/*
+ Unix SMB/CIFS implementation.
+ NBT netbios header - version 2
+ Copyright (C) Andrew Tridgell 1994-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 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.
+
+*/
+
+#define INFO_VERSION "INFO/version"
+#define INFO_COUNT "INFO/num_entries"
+#define INFO_ID_HIGH "INFO/id_high"
+#define INFO_ID_LOW "INFO/id_low"
+#define ENTRY_PREFIX "ENTRY/"
+
+#define PERMANENT_TTL 0
+
+/* NTAS uses 2, NT uses 1, WfWg uses 0 */
+#define MAINTAIN_LIST 2
+#define ELECTION_VERSION 1
+
+#define MAX_DGRAM_SIZE (576) /* tcp/ip datagram limit is 576 bytes */
+#define MIN_DGRAM_SIZE 12
+
+/*********************************************************
+ Types of reply packet.
+**********************************************************/
+
+enum netbios_reply_type_code { NMB_QUERY, NMB_STATUS, NMB_REG, NMB_REG_REFRESH,
+ NMB_REL, NMB_WAIT_ACK, NMB_MULTIHOMED_REG,
+ WINS_REG, WINS_QUERY };
+
+/* From rfc1002, 4.2.1.2 */
+/* Question types. */
+#define QUESTION_TYPE_NB_QUERY 0x20
+#define QUESTION_TYPE_NB_STATUS 0x21
+
+/* Question class */
+#define QUESTION_CLASS_IN 0x1
+
+/* Opcode definitions */
+#define NMB_NAME_QUERY_OPCODE 0x0
+#define NMB_NAME_REG_OPCODE 0x05 /* see rfc1002.txt 4.2.2,3,5,6,7,8 */
+#define NMB_NAME_RELEASE_OPCODE 0x06 /* see rfc1002.txt 4.2.9,10,11 */
+#define NMB_WACK_OPCODE 0x07 /* see rfc1002.txt 4.2.16 */
+/* Ambiguity in rfc1002 about which of these is correct. */
+/* WinNT uses 8 by default but can be made to use 9. */
+#define NMB_NAME_REFRESH_OPCODE_8 0x08 /* see rfc1002.txt 4.2.4 */
+#define NMB_NAME_REFRESH_OPCODE_9 0x09 /* see rfc1002.txt 4.2.4 */
+#define NMB_NAME_MULTIHOMED_REG_OPCODE 0x0F /* Invented by Microsoft. */
+
+/* XXXX what about all the other types?? 0x1, 0x2, 0x3, 0x4, 0x8? */
+
+/* Resource record types. rfc1002 4.2.1.3 */
+#define RR_TYPE_A 0x1
+#define RR_TYPE_NS 0x2
+#define RR_TYPE_NULL 0xA
+#define RR_TYPE_NB 0x20
+#define RR_TYPE_NBSTAT 0x21
+
+/* Resource record class. */
+#define RR_CLASS_IN 0x1
+
+/* NetBIOS flags */
+#define NB_GROUP 0x80
+#define NB_PERM 0x02
+#define NB_ACTIVE 0x04
+#define NB_CONFL 0x08
+#define NB_DEREG 0x10
+#define NB_BFLAG 0x00 /* Broadcast node type. */
+#define NB_PFLAG 0x20 /* Point-to-point node type. */
+#define NB_MFLAG 0x40 /* Mixed bcast & p-p node type. */
+#define NB_HFLAG 0x60 /* Microsoft 'hybrid' node type. */
+#define NB_NODETYPEMASK 0x60
+/* Mask applied to outgoing NetBIOS flags. */
+#define NB_FLGMSK 0xE0
+
+/* The wins flags. Looks like the nbflags ! */
+#define WINS_UNIQUE 0x00 /* Unique record */
+#define WINS_NGROUP 0x01 /* Normal Group eg: 1B */
+#define WINS_SGROUP 0x02 /* Special Group eg: 1C */
+#define WINS_MHOMED 0x03 /* MultiHomed */
+
+#define WINS_ACTIVE 0x00 /* active record */
+#define WINS_RELEASED 0x04 /* released record */
+#define WINS_TOMBSTONED 0x08 /* tombstoned record */
+#define WINS_DELETED 0x0C /* deleted record */
+
+#define WINS_STATE_MASK 0x0C
+
+#define WINS_LOCAL 0x00 /* local record */
+#define WINS_REMOTE 0x10 /* remote record */
+
+#define WINS_BNODE 0x00 /* Broadcast node */
+#define WINS_PNODE 0x20 /* PtP node */
+#define WINS_MNODE 0x40 /* Mixed node */
+#define WINS_HNODE 0x60 /* Hybrid node */
+
+#define WINS_NONSTATIC 0x00 /* dynamic record */
+#define WINS_STATIC 0x80 /* static record */
+
+#define WINS_STATE_ACTIVE(p) (((p)->data.wins_flags & WINS_STATE_MASK) == WINS_ACTIVE)
+
+
+/* NetBIOS flag identifier. */
+#define NAME_GROUP(p) ((p)->data.nb_flags & NB_GROUP)
+#define NAME_BFLAG(p) (((p)->data.nb_flags & NB_NODETYPEMASK) == NB_BFLAG)
+#define NAME_PFLAG(p) (((p)->data.nb_flags & NB_NODETYPEMASK) == NB_PFLAG)
+#define NAME_MFLAG(p) (((p)->data.nb_flags & NB_NODETYPEMASK) == NB_MFLAG)
+#define NAME_HFLAG(p) (((p)->data.nb_flags & NB_NODETYPEMASK) == NB_HFLAG)
+
+/* Samba name state for a name in a namelist. */
+#define NAME_IS_ACTIVE(p) ((p)->data.nb_flags & NB_ACTIVE)
+#define NAME_IN_CONFLICT(p) ((p)->data.nb_flags & NB_CONFL)
+#define NAME_IS_DEREGISTERING(p) ((p)->data.nb_flags & NB_DEREG)
+
+/* Error codes for NetBIOS requests. */
+#define FMT_ERR 0x1 /* Packet format error. */
+#define SRV_ERR 0x2 /* Internal server error. */
+#define NAM_ERR 0x3 /* Name does not exist. */
+#define IMP_ERR 0x4 /* Request not implemented. */
+#define RFS_ERR 0x5 /* Request refused. */
+#define ACT_ERR 0x6 /* Active error - name owned by another host. */
+#define CFT_ERR 0x7 /* Name in conflict error. */
+
+#define REFRESH_TIME (15*60)
+#define NAME_POLL_REFRESH_TIME (5*60)
+#define NAME_POLL_INTERVAL 15
+
+/* Workgroup state identifiers. */
+#define AM_POTENTIAL_MASTER_BROWSER(work) ((work)->mst_state == MST_POTENTIAL)
+#define AM_LOCAL_MASTER_BROWSER(work) ((work)->mst_state == MST_BROWSER)
+#define AM_DOMAIN_MASTER_BROWSER(work) ((work)->dom_state == DOMAIN_MST)
+#define AM_DOMAIN_MEMBER(work) ((work)->log_state == LOGON_SRV)
+
+/* Microsoft browser NetBIOS name. */
+#define MSBROWSE "\001\002__MSBROWSE__\002"
+
+/* Mail slots. */
+#define BROWSE_MAILSLOT "\\MAILSLOT\\BROWSE"
+#define NET_LOGON_MAILSLOT "\\MAILSLOT\\NET\\NETLOGON"
+#define NT_LOGON_MAILSLOT "\\MAILSLOT\\NET\\NTLOGON"
+#define LANMAN_MAILSLOT "\\MAILSLOT\\LANMAN"
+
+/* Samba definitions for find_name_on_subnet(). */
+#define FIND_ANY_NAME 0
+#define FIND_SELF_NAME 1
+
+/*
+ * The different name types that can be in namelists.
+ *
+ * SELF_NAME should only be on the broadcast and unicast subnets.
+ * LMHOSTS_NAME should only be in the remote_broadcast_subnet.
+ * REGISTER_NAME, DNS_NAME, DNSFAIL_NAME should only be in the wins_server_subnet.
+ * WINS_PROXY_NAME should only be on the broadcast subnets.
+ * PERMANENT_NAME can be on all subnets except remote_broadcast_subnet.
+ *
+ */
+
+enum name_source {LMHOSTS_NAME, REGISTER_NAME, SELF_NAME, DNS_NAME,
+ DNSFAIL_NAME, PERMANENT_NAME, WINS_PROXY_NAME};
+enum node_type {B_NODE=0, P_NODE=1, M_NODE=2, NBDD_NODE=3};
+enum packet_type {NMB_PACKET, DGRAM_PACKET};
+
+enum master_state {
+ MST_NONE,
+ MST_POTENTIAL,
+ MST_BACKUP,
+ MST_MSB,
+ MST_BROWSER,
+ MST_UNBECOMING_MASTER
+};
+
+enum domain_state {
+ DOMAIN_NONE,
+ DOMAIN_WAIT,
+ DOMAIN_MST
+};
+
+enum logon_state {
+ LOGON_NONE,
+ LOGON_WAIT,
+ LOGON_SRV
+};
+
+struct subnet_record;
+
+struct nmb_data {
+ uint16 nb_flags; /* Netbios flags. */
+ int num_ips; /* Number of ip entries. */
+ struct in_addr *ip; /* The ip list for this name. */
+
+ enum name_source source; /* Where the name came from. */
+
+ time_t death_time; /* The time the record must be removed (do not remove if 0). */
+ time_t refresh_time; /* The time the record should be refreshed. */
+
+ SMB_BIG_UINT id; /* unique id */
+ struct in_addr wins_ip; /* the adress of the wins server this record comes from */
+
+ int wins_flags; /* similar to the netbios flags but different ! */
+};
+
+/* This structure represents an entry in a local netbios name list. */
+struct name_record {
+ ubi_trNode node[1];
+ struct subnet_record *subnet;
+ struct nmb_name name; /* The netbios name. */
+ struct nmb_data data; /* The netbios data. */
+};
+
+/* Browser cache for synchronising browse lists. */
+struct browse_cache_record {
+ ubi_dlNode node[1];
+ unstring lmb_name;
+ unstring work_group;
+ struct in_addr ip;
+ time_t sync_time;
+ time_t death_time; /* The time the record must be removed. */
+};
+
+/* This is used to hold the list of servers in my domain, and is
+ contained within lists of domains. */
+
+struct server_record {
+ struct server_record *next;
+ struct server_record *prev;
+
+ struct subnet_record *subnet;
+
+ struct server_info_struct serv;
+ time_t death_time;
+};
+
+/* A workgroup structure. It contains a list of servers. */
+struct work_record {
+ struct work_record *next;
+ struct work_record *prev;
+
+ struct subnet_record *subnet;
+
+ struct server_record *serverlist;
+
+ /* Stage of development from non-local-master up to local-master browser. */
+ enum master_state mst_state;
+
+ /* Stage of development from non-domain-master to domain-master browser. */
+ enum domain_state dom_state;
+
+ /* Stage of development from non-logon-server to logon server. */
+ enum logon_state log_state;
+
+ /* Work group info. */
+ unstring work_group;
+ int token; /* Used when communicating with backup browsers. */
+ unstring local_master_browser_name; /* Current local master browser. */
+
+ /* Announce info. */
+ time_t lastannounce_time;
+ int announce_interval;
+ BOOL needannounce;
+
+ /* Timeout time for this workgroup. 0 means permanent. */
+ time_t death_time;
+
+ /* Election info */
+ BOOL RunningElection;
+ BOOL needelection;
+ int ElectionCount;
+ uint32 ElectionCriterion;
+
+ /* Domain master browser info. Used for efficient syncs. */
+ struct nmb_name dmb_name;
+ struct in_addr dmb_addr;
+};
+
+/* typedefs needed to define copy & free functions for userdata. */
+struct userdata_struct;
+
+typedef struct userdata_struct * (*userdata_copy_fn)(struct userdata_struct *);
+typedef void (*userdata_free_fn)(struct userdata_struct *);
+
+/* Structure to define any userdata passed around. */
+
+struct userdata_struct {
+ userdata_copy_fn copy_fn;
+ userdata_free_fn free_fn;
+ unsigned int userdata_len;
+ char data[16]; /* 16 is to ensure alignment/padding on all systems */
+};
+
+struct response_record;
+struct packet_struct;
+struct res_rec;
+
+/* typedef to define the function called when this response packet comes in. */
+typedef void (*response_function)(struct subnet_record *, struct response_record *,
+ struct packet_struct *);
+
+/* typedef to define the function called when this response record times out. */
+typedef void (*timeout_response_function)(struct subnet_record *,
+ struct response_record *);
+
+/* typedef to define the function called when the request that caused this
+ response record to be created is successful. */
+typedef void (*success_function)(struct subnet_record *, struct userdata_struct *, ...);
+
+/* typedef to define the function called when the request that caused this
+ response record to be created is unsuccessful. */
+typedef void (*fail_function)(struct subnet_record *, struct response_record *, ...);
+
+/* List of typedefs for success and fail functions of the different query
+ types. Used to catch any compile time prototype errors. */
+
+typedef void (*register_name_success_function)( struct subnet_record *,
+ struct userdata_struct *,
+ struct nmb_name *,
+ uint16,
+ int,
+ struct in_addr);
+typedef void (*register_name_fail_function)( struct subnet_record *,
+ struct response_record *,
+ struct nmb_name *);
+
+typedef void (*release_name_success_function)( struct subnet_record *,
+ struct userdata_struct *,
+ struct nmb_name *,
+ struct in_addr);
+typedef void (*release_name_fail_function)( struct subnet_record *,
+ struct response_record *,
+ struct nmb_name *);
+
+typedef void (*refresh_name_success_function)( struct subnet_record *,
+ struct userdata_struct *,
+ struct nmb_name *,
+ uint16,
+ int,
+ struct in_addr);
+typedef void (*refresh_name_fail_function)( struct subnet_record *,
+ struct response_record *,
+ struct nmb_name *);
+
+typedef void (*query_name_success_function)( struct subnet_record *,
+ struct userdata_struct *,
+ struct nmb_name *,
+ struct in_addr,
+ struct res_rec *answers);
+
+typedef void (*query_name_fail_function)( struct subnet_record *,
+ struct response_record *,
+ struct nmb_name *,
+ int);
+
+typedef void (*node_status_success_function)( struct subnet_record *,
+ struct userdata_struct *,
+ struct res_rec *,
+ struct in_addr);
+typedef void (*node_status_fail_function)( struct subnet_record *,
+ struct response_record *);
+
+/* Initiated name queries are recorded in this list to track any responses. */
+
+struct response_record {
+ struct response_record *next;
+ struct response_record *prev;
+
+ uint16 response_id;
+
+ /* Callbacks for packets received or not. */
+ response_function resp_fn;
+ timeout_response_function timeout_fn;
+
+ /* Callbacks for the request succeeding or not. */
+ success_function success_fn;
+ fail_function fail_fn;
+
+ struct packet_struct *packet;
+
+ struct userdata_struct *userdata;
+
+ int num_msgs;
+
+ time_t repeat_time;
+ time_t repeat_interval;
+ int repeat_count;
+
+ /* Recursion protection. */
+ BOOL in_expiration_processing;
+};
+
+/* A subnet structure. It contains a list of workgroups and netbios names. */
+
+/*
+ B nodes will have their own, totally separate subnet record, with their
+ own netbios name set. These do NOT interact with other subnet records'
+ netbios names.
+*/
+
+enum subnet_type {
+ NORMAL_SUBNET = 0, /* Subnet listed in interfaces list. */
+ UNICAST_SUBNET = 1, /* Subnet for unicast packets. */
+ REMOTE_BROADCAST_SUBNET = 2, /* Subnet for remote broadcasts. */
+ WINS_SERVER_SUBNET = 3 /* Only created if we are a WINS server. */
+};
+
+struct subnet_record {
+ struct subnet_record *next;
+ struct subnet_record *prev;
+
+ char *subnet_name; /* For Debug identification. */
+ enum subnet_type type; /* To catagorize the subnet. */
+
+ struct work_record *workgrouplist; /* List of workgroups. */
+ ubi_trRoot namelist[1]; /* List of netbios names. */
+ struct response_record *responselist; /* List of responses expected. */
+
+ BOOL namelist_changed;
+ BOOL work_changed;
+
+ struct in_addr bcast_ip;
+ struct in_addr mask_ip;
+ struct in_addr myip;
+ int nmb_sock; /* socket to listen for unicast 137. */
+ int dgram_sock; /* socket to listen for unicast 138. */
+};
+
+/* A resource record. */
+struct res_rec {
+ struct nmb_name rr_name;
+ int rr_type;
+ int rr_class;
+ int ttl;
+ int rdlength;
+ char rdata[MAX_DGRAM_SIZE];
+};
+
+/* Define these so we can pass info back to caller of name_query */
+#define NM_FLAGS_RS 0x80 /* Response. Cheat */
+#define NM_FLAGS_AA 0x40 /* Authoritative */
+#define NM_FLAGS_TC 0x20 /* Truncated */
+#define NM_FLAGS_RD 0x10 /* Recursion Desired */
+#define NM_FLAGS_RA 0x08 /* Recursion Available */
+#define NM_FLAGS_B 0x01 /* Broadcast */
+
+/* An nmb packet. */
+struct nmb_packet {
+ struct {
+ int name_trn_id;
+ int opcode;
+ BOOL response;
+ struct {
+ BOOL bcast;
+ BOOL recursion_available;
+ BOOL recursion_desired;
+ BOOL trunc;
+ BOOL authoritative;
+ } nm_flags;
+ int rcode;
+ int qdcount;
+ int ancount;
+ int nscount;
+ int arcount;
+ } header;
+
+ struct {
+ struct nmb_name question_name;
+ int question_type;
+ int question_class;
+ } question;
+
+ struct res_rec *answers;
+ struct res_rec *nsrecs;
+ struct res_rec *additional;
+};
+
+/* msg_type field options - from rfc1002. */
+
+#define DGRAM_UNIQUE 0x10
+#define DGRAM_GROUP 0x11
+#define DGRAM_BROADCAST 0x12
+#define DGRAM_ERROR 0x13
+#define DGRAM_QUERY_REQUEST 0x14
+#define DGRAM_POSITIVE_QUERY_RESPONSE 0x15
+#define DGRAM_NEGATIVE_QUERT_RESPONSE 0x16
+
+/* A datagram - this normally contains SMB data in the data[] array. */
+
+struct dgram_packet {
+ struct {
+ int msg_type;
+ struct {
+ enum node_type node_type;
+ BOOL first;
+ BOOL more;
+ } flags;
+ int dgm_id;
+ struct in_addr source_ip;
+ int source_port;
+ int dgm_length;
+ int packet_offset;
+ } header;
+ struct nmb_name source_name;
+ struct nmb_name dest_name;
+ int datasize;
+ char data[MAX_DGRAM_SIZE];
+};
+
+/* Define a structure used to queue packets. This will be a linked
+ list of nmb packets. */
+
+struct packet_struct
+{
+ struct packet_struct *next;
+ struct packet_struct *prev;
+ BOOL locked;
+ struct in_addr ip;
+ int port;
+ int fd;
+ time_t timestamp;
+ enum packet_type packet_type;
+ union {
+ struct nmb_packet nmb;
+ struct dgram_packet dgram;
+ } packet;
+};
+
+/* NETLOGON opcodes */
+
+#define QUERYFORPDC 7 /* Query for PDC. */
+#define SAM_UAS_CHANGE 10 /* Announce change to UAS or SAM. */
+#define QUERYFORPDC_R 12 /* Response to Query for PDC. */
+#define SAMLOGON 18
+#define SAMLOGON_R 19
+#define SAMLOGON_UNK_R 21
+#define SAMLOGON_AD_UNK_R 23
+#define SAMLOGON_AD_R 25
+
+/* Ids for netbios packet types. */
+
+#define ANN_HostAnnouncement 1
+#define ANN_AnnouncementRequest 2
+#define ANN_Election 8
+#define ANN_GetBackupListReq 9
+#define ANN_GetBackupListResp 10
+#define ANN_BecomeBackup 11
+#define ANN_DomainAnnouncement 12
+#define ANN_MasterAnnouncement 13
+#define ANN_ResetBrowserState 14
+#define ANN_LocalMasterAnnouncement 15
+
+
+/* Broadcast packet announcement intervals, in minutes. */
+
+/* Attempt to add domain logon and domain master names. */
+#define CHECK_TIME_ADD_DOM_NAMES 5
+
+/* Search for master browsers of workgroups samba knows about,
+ except default. */
+#define CHECK_TIME_MST_BROWSE 5
+
+/* Request backup browser announcements from other servers. */
+#define CHECK_TIME_ANNOUNCE_BACKUP 15
+
+/* Request host announcements from other servers: min and max of interval. */
+#define CHECK_TIME_MIN_HOST_ANNCE 3
+#define CHECK_TIME_MAX_HOST_ANNCE 12
+
+/* Announce as master to WINS server and any Primary Domain Controllers. */
+#define CHECK_TIME_MST_ANNOUNCE 15
+
+/* Time between syncs from domain master browser to local master browsers. */
+#define CHECK_TIME_DMB_TO_LMB_SYNC 15
+
+/* Do all remote announcements this often. */
+#define REMOTE_ANNOUNCE_INTERVAL 180
+
+/* what is the maximum period between name refreshes. Note that this only
+ affects non-permanent self names (in seconds) */
+#define MAX_REFRESH_TIME (60*20)
+
+/* The Extinction interval: 4 days, time a node will stay in released state */
+#define EXTINCTION_INTERVAL (4*24*60*60)
+
+/* The Extinction time-out: 1 day, time a node will stay in deleted state */
+#define EXTINCTION_TIMEOUT (24*60*60)
+
+/* Macro's to enumerate subnets either with or without
+ the UNICAST subnet. */
+
+extern struct subnet_record *subnetlist;
+extern struct subnet_record *unicast_subnet;
+extern struct subnet_record *wins_server_subnet;
+extern struct subnet_record *remote_broadcast_subnet;
+
+#define FIRST_SUBNET subnetlist
+#define NEXT_SUBNET_EXCLUDING_UNICAST(x) ((x)->next)
+#define NEXT_SUBNET_INCLUDING_UNICAST(x) (get_next_subnet_maybe_unicast((x)))
+
+/* wins replication record used between nmbd and wrepld */
+typedef struct _WINS_RECORD {
+ char name[17];
+ char type;
+ int nb_flags;
+ int wins_flags;
+ SMB_BIG_UINT id;
+ int num_ips;
+ struct in_addr ip[25];
+ struct in_addr wins_ip;
+} WINS_RECORD;
+
+/* To be removed. */
+enum state_type { TEST };
+#endif /* _NAMESERV_H_ */
diff --git a/source/include/nt_printing.h b/source/include/nt_printing.h
new file mode 100644
index 00000000000..762b1c69170
--- /dev/null
+++ b/source/include/nt_printing.h
@@ -0,0 +1,484 @@
+/*
+ Unix SMB/Netbios implementation.
+ Version 1.9.
+ SMB parameters and setup
+ Copyright (C) Andrew Tridgell 1992-2000,
+ Copyright (C) Jean Francois Micouleau 1998-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 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 NT_PRINTING_H_
+#define NT_PRINTING_H_
+
+#define ORIENTATION 0x00000001L
+#define PAPERSIZE 0x00000002L
+#define PAPERLENGTH 0x00000004L
+#define PAPERWIDTH 0x00000008L
+#define SCALE 0x00000010L
+#define COPIES 0x00000100L
+#define DEFAULTSOURCE 0x00000200L
+#define PRINTQUALITY 0x00000400L
+#define COLOR 0x00000800L
+#define DUPLEX 0x00001000L
+#define YRESOLUTION 0x00002000L
+#define TTOPTION 0x00004000L
+#define COLLATE 0x00008000L
+#define FORMNAME 0x00010000L
+#define LOGPIXELS 0x00020000L
+#define BITSPERPEL 0x00040000L
+#define PELSWIDTH 0x00080000L
+#define PELSHEIGHT 0x00100000L
+#define DISPLAYFLAGS 0x00200000L
+#define DISPLAYFREQUENCY 0x00400000L
+#define PANNINGWIDTH 0x00800000L
+#define PANNINGHEIGHT 0x01000000L
+
+#define ORIENT_PORTRAIT 1
+#define ORIENT_LANDSCAPE 2
+
+#define PAPER_FIRST PAPER_LETTER
+#define PAPER_LETTER 1 /* Letter 8 1/2 x 11 in */
+#define PAPER_LETTERSMALL 2 /* Letter Small 8 1/2 x 11 in */
+#define PAPER_TABLOID 3 /* Tabloid 11 x 17 in */
+#define PAPER_LEDGER 4 /* Ledger 17 x 11 in */
+#define PAPER_LEGAL 5 /* Legal 8 1/2 x 14 in */
+#define PAPER_STATEMENT 6 /* Statement 5 1/2 x 8 1/2 in */
+#define PAPER_EXECUTIVE 7 /* Executive 7 1/4 x 10 1/2 in */
+#define PAPER_A3 8 /* A3 297 x 420 mm */
+#define PAPER_A4 9 /* A4 210 x 297 mm */
+#define PAPER_A4SMALL 10 /* A4 Small 210 x 297 mm */
+#define PAPER_A5 11 /* A5 148 x 210 mm */
+#define PAPER_B4 12 /* B4 (JIS) 250 x 354 */
+#define PAPER_B5 13 /* B5 (JIS) 182 x 257 mm */
+#define PAPER_FOLIO 14 /* Folio 8 1/2 x 13 in */
+#define PAPER_QUARTO 15 /* Quarto 215 x 275 mm */
+#define PAPER_10X14 16 /* 10x14 in */
+#define PAPER_11X17 17 /* 11x17 in */
+#define PAPER_NOTE 18 /* Note 8 1/2 x 11 in */
+#define PAPER_ENV_9 19 /* Envelope #9 3 7/8 x 8 7/8 */
+#define PAPER_ENV_10 20 /* Envelope #10 4 1/8 x 9 1/2 */
+#define PAPER_ENV_11 21 /* Envelope #11 4 1/2 x 10 3/8 */
+#define PAPER_ENV_12 22 /* Envelope #12 4 \276 x 11 */
+#define PAPER_ENV_14 23 /* Envelope #14 5 x 11 1/2 */
+#define PAPER_CSHEET 24 /* C size sheet */
+#define PAPER_DSHEET 25 /* D size sheet */
+#define PAPER_ESHEET 26 /* E size sheet */
+#define PAPER_ENV_DL 27 /* Envelope DL 110 x 220mm */
+#define PAPER_ENV_C5 28 /* Envelope C5 162 x 229 mm */
+#define PAPER_ENV_C3 29 /* Envelope C3 324 x 458 mm */
+#define PAPER_ENV_C4 30 /* Envelope C4 229 x 324 mm */
+#define PAPER_ENV_C6 31 /* Envelope C6 114 x 162 mm */
+#define PAPER_ENV_C65 32 /* Envelope C65 114 x 229 mm */
+#define PAPER_ENV_B4 33 /* Envelope B4 250 x 353 mm */
+#define PAPER_ENV_B5 34 /* Envelope B5 176 x 250 mm */
+#define PAPER_ENV_B6 35 /* Envelope B6 176 x 125 mm */
+#define PAPER_ENV_ITALY 36 /* Envelope 110 x 230 mm */
+#define PAPER_ENV_MONARCH 37 /* Envelope Monarch 3.875 x 7.5 in */
+#define PAPER_ENV_PERSONAL 38 /* 6 3/4 Envelope 3 5/8 x 6 1/2 in */
+#define PAPER_FANFOLD_US 39 /* US Std Fanfold 14 7/8 x 11 in */
+#define PAPER_FANFOLD_STD_GERMAN 40 /* German Std Fanfold 8 1/2 x 12 in */
+#define PAPER_FANFOLD_LGL_GERMAN 41 /* German Legal Fanfold 8 1/2 x 13 in */
+
+#define PAPER_LAST PAPER_FANFOLD_LGL_GERMAN
+#define PAPER_USER 256
+
+#define BIN_FIRST BIN_UPPER
+#define BIN_UPPER 1
+#define BIN_ONLYONE 1
+#define BIN_LOWER 2
+#define BIN_MIDDLE 3
+#define BIN_MANUAL 4
+#define BIN_ENVELOPE 5
+#define BIN_ENVMANUAL 6
+#define BIN_AUTO 7
+#define BIN_TRACTOR 8
+#define BIN_SMALLFMT 9
+#define BIN_LARGEFMT 10
+#define BIN_LARGECAPACITY 11
+#define BIN_CASSETTE 14
+#define BIN_FORMSOURCE 15
+#define BIN_LAST BIN_FORMSOURCE
+
+#define BIN_USER 256 /* device specific bins start here */
+
+#define RES_DRAFT (-1)
+#define RES_LOW (-2)
+#define RES_MEDIUM (-3)
+#define RES_HIGH (-4)
+
+#define COLOR_MONOCHROME 1
+#define COLOR_COLOR 2
+
+#define DUP_SIMPLEX 1
+#define DUP_VERTICAL 2
+#define DUP_HORIZONTAL 3
+
+#define TT_BITMAP 1 /* print TT fonts as graphics */
+#define TT_DOWNLOAD 2 /* download TT fonts as soft fonts */
+#define TT_SUBDEV 3 /* substitute device fonts for TT fonts */
+
+#define COLLATE_FALSE 0
+#define COLLATE_TRUE 1
+
+typedef struct nt_printer_driver_info_level_3
+{
+ uint32 cversion;
+
+ fstring name;
+ fstring environment;
+ fstring driverpath;
+ fstring datafile;
+ fstring configfile;
+ fstring helpfile;
+ fstring monitorname;
+ fstring defaultdatatype;
+ fstring *dependentfiles;
+} NT_PRINTER_DRIVER_INFO_LEVEL_3;
+
+/* SPOOL_PRINTER_DRIVER_INFO_LEVEL_6 structure */
+typedef struct {
+ uint32 version;
+ fstring name;
+ fstring environment;
+ fstring driverpath;
+ fstring datafile;
+ fstring configfile;
+ fstring helpfile;
+ fstring monitorname;
+ fstring defaultdatatype;
+ fstring mfgname;
+ fstring oemurl;
+ fstring hardwareid;
+ fstring provider;
+ fstring *dependentfiles;
+ fstring *previousnames;
+} NT_PRINTER_DRIVER_INFO_LEVEL_6;
+
+
+typedef struct nt_printer_driver_info_level
+{
+ NT_PRINTER_DRIVER_INFO_LEVEL_3 *info_3;
+ NT_PRINTER_DRIVER_INFO_LEVEL_6 *info_6;
+} NT_PRINTER_DRIVER_INFO_LEVEL;
+
+/* predefined registry key names for printer data */
+
+#define SPOOL_PRINTERDATA_KEY "PrinterDriverData"
+#define SPOOL_DSSPOOLER_KEY "DsSpooler"
+#define SPOOL_DSDRIVER_KEY "DsDriver"
+#define SPOOL_DSUSER_KEY "DsUser"
+#define SPOOL_PNPDATA_KEY "PnPData"
+#define SPOOL_OID_KEY "OID"
+
+/* predefined value names for printer data */
+#define SPOOL_REG_ASSETNUMBER "assetNumber"
+#define SPOOL_REG_BYTESPERMINUTE "bytesPerMinute"
+#define SPOOL_REG_DEFAULTPRIORITY "defaultPriority"
+#define SPOOL_REG_DESCRIPTION "description"
+#define SPOOL_REG_DRIVERNAME "driverName"
+#define SPOOL_REG_DRIVERVERSION "driverVersion"
+#define SPOOL_REG_FLAGS "flags"
+#define SPOOL_REG_LOCATION "location"
+#define SPOOL_REG_OPERATINGSYSTEM "operatingSystem"
+#define SPOOL_REG_OPERATINGSYSTEMHOTFIX "operatingSystemHotfix"
+#define SPOOL_REG_OPERATINGSYSTEMSERVICEPACK "operatingSystemServicePack"
+#define SPOOL_REG_OPERATINGSYSTEMVERSION "operatingSystemVersion"
+#define SPOOL_REG_PORTNAME "portName"
+#define SPOOL_REG_PRINTATTRIBUTES "printAttributes"
+#define SPOOL_REG_PRINTBINNAMES "printBinNames"
+#define SPOOL_REG_PRINTCOLLATE "printCollate"
+#define SPOOL_REG_PRINTCOLOR "printColor"
+#define SPOOL_REG_PRINTDUPLEXSUPPORTED "printDuplexSupported"
+#define SPOOL_REG_PRINTENDTIME "printEndTime"
+#define SPOOL_REG_PRINTERNAME "printerName"
+#define SPOOL_REG_PRINTFORMNAME "printFormName"
+#define SPOOL_REG_PRINTKEEPPRINTEDJOBS "printKeepPrintedJobs"
+#define SPOOL_REG_PRINTLANGUAGE "printLanguage"
+#define SPOOL_REG_PRINTMACADDRESS "printMACAddress"
+#define SPOOL_REG_PRINTMAXCOPIES "printMaxCopies"
+#define SPOOL_REG_PRINTMAXRESOLUTIONSUPPORTED "printMaxResolutionSupported"
+#define SPOOL_REG_PRINTMAXXEXTENT "printMaxXExtent"
+#define SPOOL_REG_PRINTMAXYEXTENT "printMaxYExtent"
+#define SPOOL_REG_PRINTMEDIAREADY "printMediaReady"
+#define SPOOL_REG_PRINTMEDIASUPPORTED "printMediaSupported"
+#define SPOOL_REG_PRINTMEMORY "printMemory"
+#define SPOOL_REG_PRINTMINXEXTENT "printMinXExtent"
+#define SPOOL_REG_PRINTMINYEXTENT "printMinYExtent"
+#define SPOOL_REG_PRINTNETWORKADDRESS "printNetworkAddress"
+#define SPOOL_REG_PRINTNOTIFY "printNotify"
+#define SPOOL_REG_PRINTNUMBERUP "printNumberUp"
+#define SPOOL_REG_PRINTORIENTATIONSSUPPORTED "printOrientationsSupported"
+#define SPOOL_REG_PRINTOWNER "printOwner"
+#define SPOOL_REG_PRINTPAGESPERMINUTE "printPagesPerMinute"
+#define SPOOL_REG_PRINTRATE "printRate"
+#define SPOOL_REG_PRINTRATEUNIT "printRateUnit"
+#define SPOOL_REG_PRINTSEPARATORFILE "printSeparatorFile"
+#define SPOOL_REG_PRINTSHARENAME "printShareName"
+#define SPOOL_REG_PRINTSPOOLING "printSpooling"
+#define SPOOL_REGVAL_PRINTWHILESPOOLING "PrintWhileSpooling"
+#define SPOOL_REGVAL_PRINTAFTERSPOOLED "PrintAfterSpooled"
+#define SPOOL_REGVAL_PRINTDIRECT "PrintDirect"
+#define SPOOL_REG_PRINTSTAPLINGSUPPORTED "printStaplingSupported"
+#define SPOOL_REG_PRINTSTARTTIME "printStartTime"
+#define SPOOL_REG_PRINTSTATUS "printStatus"
+#define SPOOL_REG_PRIORITY "priority"
+#define SPOOL_REG_SERVERNAME "serverName"
+#define SPOOL_REG_SHORTSERVERNAME "shortServerName"
+#define SPOOL_REG_UNCNAME "uNCName"
+#define SPOOL_REG_URL "url"
+#define SPOOL_REG_VERSIONNUMBER "versionNumber"
+
+/* container for a single registry key */
+
+typedef struct {
+ char *name;
+ REGVAL_CTR values;
+} NT_PRINTER_KEY;
+
+/* container for all printer data */
+
+typedef struct {
+ int num_keys;
+ NT_PRINTER_KEY *keys;
+} NT_PRINTER_DATA;
+
+#define MAXDEVICENAME 32
+
+typedef struct ntdevicemode
+{
+ fstring devicename;
+ fstring formname;
+
+ uint16 specversion;
+ uint16 driverversion;
+ uint16 size;
+ uint16 driverextra;
+ uint16 orientation;
+ uint16 papersize;
+ uint16 paperlength;
+ uint16 paperwidth;
+ uint16 scale;
+ uint16 copies;
+ uint16 defaultsource;
+ uint16 printquality;
+ uint16 color;
+ uint16 duplex;
+ uint16 yresolution;
+ uint16 ttoption;
+ uint16 collate;
+ uint16 logpixels;
+
+ uint32 fields;
+ uint32 bitsperpel;
+ uint32 pelswidth;
+ uint32 pelsheight;
+ uint32 displayflags;
+ uint32 displayfrequency;
+ uint32 icmmethod;
+ uint32 icmintent;
+ uint32 mediatype;
+ uint32 dithertype;
+ uint32 reserved1;
+ uint32 reserved2;
+ uint32 panningwidth;
+ uint32 panningheight;
+ uint8 *private;
+} NT_DEVICEMODE;
+
+typedef struct nt_printer_info_level_2
+{
+ uint32 attributes;
+ uint32 priority;
+ uint32 default_priority;
+ uint32 starttime;
+ uint32 untiltime;
+ uint32 status;
+ uint32 cjobs;
+ uint32 averageppm;
+ fstring servername;
+ fstring printername;
+ fstring sharename;
+ fstring portname;
+ fstring drivername;
+ pstring comment;
+ fstring location;
+ NT_DEVICEMODE *devmode;
+ fstring sepfile;
+ fstring printprocessor;
+ fstring datatype;
+ fstring parameters;
+ NT_PRINTER_DATA data;
+ SEC_DESC_BUF *secdesc_buf;
+ uint32 changeid;
+ uint32 c_setprinter;
+ uint32 setuptime;
+} NT_PRINTER_INFO_LEVEL_2;
+
+typedef struct nt_printer_info_level
+{
+ NT_PRINTER_INFO_LEVEL_2 *info_2;
+} NT_PRINTER_INFO_LEVEL;
+
+typedef struct
+{
+ fstring name;
+ uint32 flag;
+ uint32 width;
+ uint32 length;
+ uint32 left;
+ uint32 top;
+ uint32 right;
+ uint32 bottom;
+} nt_forms_struct;
+
+/*
+typedef struct _form
+{
+ uint32 flags;
+ uint32 name_ptr;
+ uint32 size_x;
+ uint32 size_y;
+ uint32 left;
+ uint32 top;
+ uint32 right;
+ uint32 bottom;
+ UNISTR2 name;
+} FORM;
+*/
+
+#ifndef SAMBA_PRINTER_PORT_NAME
+#define SAMBA_PRINTER_PORT_NAME "Samba Printer Port"
+#endif
+
+/* DOS header format */
+#define DOS_HEADER_SIZE 64
+#define DOS_HEADER_MAGIC_OFFSET 0
+#define DOS_HEADER_MAGIC 0x5A4D
+#define DOS_HEADER_LFANEW_OFFSET 60
+
+/* New Executable format (Win or OS/2 1.x segmented) */
+#define NE_HEADER_SIZE 64
+#define NE_HEADER_SIGNATURE_OFFSET 0
+#define NE_HEADER_SIGNATURE 0x454E
+#define NE_HEADER_TARGET_OS_OFFSET 54
+#define NE_HEADER_TARGOS_WIN 0x02
+#define NE_HEADER_MINOR_VER_OFFSET 62
+#define NE_HEADER_MAJOR_VER_OFFSET 63
+
+/* Portable Executable format */
+#define PE_HEADER_SIZE 248
+#define PE_HEADER_SIGNATURE_OFFSET 0
+#define PE_HEADER_SIGNATURE 0x00004550
+#define PE_HEADER_MACHINE_OFFSET 4
+#define PE_HEADER_MACHINE_I386 0x14c
+#define PE_HEADER_NUMBER_OF_SECTIONS 6
+#define PE_HEADER_MAJOR_OS_VER_OFFSET 64
+#define PE_HEADER_MINOR_OS_VER_OFFSET 66
+#define PE_HEADER_MAJOR_IMG_VER_OFFSET 68
+#define PE_HEADER_MINOR_IMG_VER_OFFSET 70
+#define PE_HEADER_MAJOR_SS_VER_OFFSET 72
+#define PE_HEADER_MINOR_SS_VER_OFFSET 74
+#define PE_HEADER_SECT_HEADER_SIZE 40
+#define PE_HEADER_SECT_NAME_OFFSET 0
+#define PE_HEADER_SECT_SIZE_DATA_OFFSET 16
+#define PE_HEADER_SECT_PTR_DATA_OFFSET 20
+
+/* Microsoft file version format */
+#define VS_SIGNATURE "VS_VERSION_INFO"
+#define VS_MAGIC_VALUE 0xfeef04bd
+#define VS_MAJOR_OFFSET 8
+#define VS_MINOR_OFFSET 12
+#define VS_VERSION_INFO_UNICODE_SIZE (sizeof(VS_SIGNATURE)*2+4+VS_MINOR_OFFSET+4) /* not true size! */
+#define VS_VERSION_INFO_SIZE (sizeof(VS_SIGNATURE)+4+VS_MINOR_OFFSET+4) /* not true size! */
+#define VS_NE_BUF_SIZE 4096 /* Must be > 2*VS_VERSION_INFO_SIZE */
+
+/* Notify spoolss clients that something has changed. The
+ notification data is either stored in two uint32 values or a
+ variable length array. */
+
+#define SPOOLSS_NOTIFY_MSG_UNIX_JOBID 0x0001 /* Job id is unix */
+
+typedef struct spoolss_notify_msg {
+ fstring printer; /* Name of printer notified */
+ uint32 type; /* Printer or job notify */
+ uint32 field; /* Notify field changed */
+ uint32 id; /* Job id */
+ uint32 len; /* Length of data, 0 for two uint32 value */
+ uint32 flags;
+ union {
+ uint32 value[2];
+ char *data;
+ } notify;
+} SPOOLSS_NOTIFY_MSG;
+
+typedef struct {
+ fstring printername;
+ uint32 num_msgs;
+ SPOOLSS_NOTIFY_MSG *msgs;
+} SPOOLSS_NOTIFY_MSG_GROUP;
+
+typedef struct {
+ TALLOC_CTX *ctx;
+ uint32 num_groups;
+ SPOOLSS_NOTIFY_MSG_GROUP *msg_groups;
+} SPOOLSS_NOTIFY_MSG_CTR;
+
+#define PRINTER_HANDLE_IS_PRINTER 0
+#define PRINTER_HANDLE_IS_PRINTSERVER 1
+
+/* structure to store the printer handles */
+/* and a reference to what it's pointing to */
+/* and the notify info asked about */
+/* that's the central struct */
+typedef struct _Printer{
+ struct _Printer *prev, *next;
+ BOOL document_started;
+ BOOL page_started;
+ uint32 jobid; /* jobid in printing backend */
+ BOOL printer_type;
+ TALLOC_CTX *ctx;
+ union {
+ fstring handlename;
+ fstring printerservername;
+ } dev;
+ uint32 type;
+ uint32 access_granted;
+ struct {
+ uint32 flags;
+ uint32 options;
+ fstring localmachine;
+ uint32 printerlocal;
+ SPOOL_NOTIFY_OPTION *option;
+ POLICY_HND client_hnd;
+ BOOL client_connected;
+ uint32 change;
+ /* are we in a FindNextPrinterChangeNotify() call? */
+ BOOL fnpcn;
+ } notify;
+ struct {
+ fstring machine;
+ fstring user;
+ } client;
+
+ /* devmode sent in the OpenPrinter() call */
+ NT_DEVICEMODE *nt_devmode;
+
+ /* cache the printer info */
+ NT_PRINTER_INFO_LEVEL *printer_info;
+
+} Printer_entry;
+
+#endif /* NT_PRINTING_H_ */
diff --git a/source/include/nt_status.h b/source/include/nt_status.h
new file mode 100644
index 00000000000..9747f73eb18
--- /dev/null
+++ b/source/include/nt_status.h
@@ -0,0 +1,63 @@
+/*
+ Unix SMB/CIFS implementation.
+ SMB parameters and setup, plus a whole lot more.
+
+ 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 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 _NT_STATUS_H
+#define _NT_STATUS_H
+
+/* The Splint code analysis tool doesn't like immediate structures. */
+
+#ifdef _SPLINT_ /* http://www.splint.org */
+#undef HAVE_IMMEDIATE_STRUCTURES
+#endif
+
+/* the following rather strange looking definitions of NTSTATUS and WERROR
+ and there in order to catch common coding errors where different error types
+ are mixed up. This is especially important as we slowly convert Samba
+ from using BOOL for internal functions
+*/
+
+#if defined(HAVE_IMMEDIATE_STRUCTURES)
+typedef struct {uint32 v;} NTSTATUS;
+#define NT_STATUS(x) ((NTSTATUS) { x })
+#define NT_STATUS_V(x) ((x).v)
+#else
+typedef uint32 NTSTATUS;
+#define NT_STATUS(x) (x)
+#define NT_STATUS_V(x) (x)
+#endif
+
+#if defined(HAVE_IMMEDIATE_STRUCTURES)
+typedef struct {uint32 v;} WERROR;
+#define W_ERROR(x) ((WERROR) { x })
+#define W_ERROR_V(x) ((x).v)
+#else
+typedef uint32 WERROR;
+#define W_ERROR(x) (x)
+#define W_ERROR_V(x) (x)
+#endif
+
+#define NT_STATUS_IS_OK(x) (NT_STATUS_V(x) == 0)
+#define NT_STATUS_IS_ERR(x) ((NT_STATUS_V(x) & 0xc0000000) == 0xc0000000)
+#define NT_STATUS_EQUAL(x,y) (NT_STATUS_V(x) == NT_STATUS_V(y))
+#define W_ERROR_IS_OK(x) (W_ERROR_V(x) == 0)
+#define W_ERROR_EQUAL(x,y) (W_ERROR_V(x) == W_ERROR_V(y))
+
+#endif
diff --git a/source/include/ntdomain.h b/source/include/ntdomain.h
new file mode 100644
index 00000000000..4e6795a85d5
--- /dev/null
+++ b/source/include/ntdomain.h
@@ -0,0 +1,417 @@
+/*
+ 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 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 _NT_DOMAIN_H /* _NT_DOMAIN_H */
+#define _NT_DOMAIN_H
+
+struct uuid
+{
+ uint32 time_low;
+ uint16 time_mid;
+ uint16 time_hi_and_version;
+ uint8 clock_seq[2];
+ uint8 node[6];
+};
+#define UUID_SIZE 16
+
+#define UUID_FLAT_SIZE 16
+typedef struct uuid_flat
+{
+ uint8 info[UUID_FLAT_SIZE];
+} UUID_FLAT;
+
+/* dce/rpc support */
+#include "rpc_dce.h"
+
+/* miscellaneous structures / defines */
+#include "rpc_misc.h"
+
+#include "rpc_creds.h"
+
+#include "talloc.h"
+
+/*
+ * A bunch of stuff that was put into smb.h
+ * in the NTDOM branch - it didn't belong there.
+ */
+
+typedef struct _prs_struct
+{
+ BOOL io; /* parsing in or out of data stream */
+ /*
+ * If the (incoming) data is big-endian. On output we are
+ * always little-endian.
+ */
+ BOOL bigendian_data;
+ uint8 align; /* data alignment */
+ BOOL is_dynamic; /* Do we own this memory or not ? */
+ uint32 data_offset; /* Current working offset into data. */
+ uint32 buffer_size; /* Current allocated size of the buffer. */
+ uint32 grow_size; /* size requested via prs_grow() calls */
+ char *data_p; /* The buffer itself. */
+ TALLOC_CTX *mem_ctx; /* When unmarshalling, use this.... */
+} prs_struct;
+
+/*
+ * Defines for io member of prs_struct.
+ */
+
+#define MARSHALL 0
+#define UNMARSHALL 1
+
+#define MARSHALLING(ps) (!(ps)->io)
+#define UNMARSHALLING(ps) ((ps)->io)
+
+#define RPC_BIG_ENDIAN 1
+#define RPC_LITTLE_ENDIAN 0
+
+#define RPC_PARSE_ALIGN 4
+
+typedef struct _output_data {
+ /*
+ * Raw RPC output data. This does not include RPC headers or footers.
+ */
+ prs_struct rdata;
+
+ /* The amount of data sent from the current rdata struct. */
+ uint32 data_sent_length;
+
+ /*
+ * The current PDU being returned. This inclues
+ * headers, data and authentication footer.
+ */
+ unsigned char current_pdu[MAX_PDU_FRAG_LEN];
+
+ /* The amount of data in the current_pdu buffer. */
+ uint32 current_pdu_len;
+
+ /* The amount of data sent from the current PDU. */
+ uint32 current_pdu_sent;
+} output_data;
+
+typedef struct _input_data {
+ /*
+ * This is the current incoming pdu. The data here
+ * is collected via multiple writes until a complete
+ * pdu is seen, then the data is copied into the in_data
+ * structure. The maximum size of this is 0x1630 (MAX_PDU_FRAG_LEN).
+ */
+ unsigned char current_in_pdu[MAX_PDU_FRAG_LEN];
+
+ /*
+ * The amount of data needed to complete the in_pdu.
+ * If this is zero, then we are at the start of a new
+ * pdu.
+ */
+ uint32 pdu_needed_len;
+
+ /*
+ * The amount of data received so far in the in_pdu.
+ * If this is zero, then we are at the start of a new
+ * pdu.
+ */
+ uint32 pdu_received_len;
+
+ /*
+ * This is the collection of input data with all
+ * the rpc headers and auth footers removed.
+ * The maximum length of this (1Mb) is strictly enforced.
+ */
+ prs_struct data;
+} input_data;
+
+/*
+ * Handle database - stored per pipe.
+ */
+
+struct policy
+{
+ struct policy *next, *prev;
+
+ POLICY_HND pol_hnd;
+
+ void *data_ptr;
+ void (*free_fn)(void *);
+
+};
+
+struct handle_list {
+ struct policy *Policy; /* List of policies. */
+ size_t count; /* Current number of handles. */
+ size_t pipe_ref_count; /* Number of pipe handles referring to this list. */
+};
+
+/* Domain controller authentication protocol info */
+struct dcinfo
+{
+ DOM_CHAL clnt_chal; /* Initial challenge received from client */
+ DOM_CHAL srv_chal; /* Initial server challenge */
+ DOM_CRED clnt_cred; /* Last client credential */
+ DOM_CRED srv_cred; /* Last server credential */
+
+ uchar sess_key[8]; /* Session key */
+ uchar md4pw[16]; /* md4(machine password) */
+
+ fstring mach_acct; /* Machine name we've authenticated. */
+
+ fstring remote_machine; /* Machine name we've authenticated. */
+
+ BOOL challenge_sent;
+ BOOL got_session_key;
+ BOOL authenticated;
+
+};
+
+typedef struct pipe_rpc_fns {
+
+ struct pipe_rpc_fns *next, *prev;
+
+ /* RPC function table associated with the current rpc_bind (associated by context) */
+
+ struct api_struct *cmds;
+ int n_cmds;
+ uint32 context_id;
+
+} PIPE_RPC_FNS;
+
+/*
+ * DCE/RPC-specific samba-internal-specific handling of data on
+ * NamedPipes.
+ */
+
+typedef struct pipes_struct
+{
+ struct pipes_struct *next, *prev;
+
+ connection_struct *conn;
+ uint16 vuid; /* points to the unauthenticated user that opened this pipe. */
+
+ fstring name;
+ fstring pipe_srv_name;
+
+ /* linked list of rpc dispatch tables associated
+ with the open rpc contexts */
+
+ PIPE_RPC_FNS *contexts;
+
+ RPC_HDR hdr; /* Incoming RPC header. */
+ RPC_HDR_REQ hdr_req; /* Incoming request header. */
+
+ uint32 ntlmssp_chal_flags; /* Client challenge flags. */
+ BOOL ntlmssp_auth_requested; /* If the client wanted authenticated rpc. */
+ BOOL ntlmssp_auth_validated; /* If the client *got* authenticated rpc. */
+ unsigned char challenge[8];
+ unsigned char ntlmssp_hash[258];
+ uint32 ntlmssp_seq_num;
+ struct dcinfo dc; /* Keeps the creds data. */
+
+ /* Hmm. In my understanding the authentication happens
+ implicitly later, so there are no two stages for
+ schannel. */
+
+ BOOL netsec_auth_validated;
+ struct netsec_auth_struct netsec_auth;
+
+ /*
+ * Windows user info.
+ */
+ fstring user_name;
+ fstring domain;
+ fstring wks;
+
+ /*
+ * Unix user name and credentials.
+ */
+
+ fstring pipe_user_name;
+ struct current_user pipe_user;
+
+ DATA_BLOB session_key;
+
+ /*
+ * Set to true when an RPC bind has been done on this pipe.
+ */
+
+ BOOL pipe_bound;
+
+ /*
+ * Set to true when we should return fault PDU's for everything.
+ */
+
+ BOOL fault_state;
+
+ /*
+ * Set to true when we should return fault PDU's for a bad handle.
+ */
+
+ BOOL bad_handle_fault_state;
+
+ /*
+ * Set to RPC_BIG_ENDIAN when dealing with big-endian PDU's
+ */
+
+ BOOL endian;
+
+ /*
+ * Struct to deal with multiple pdu inputs.
+ */
+
+ input_data in_data;
+
+ /*
+ * Struct to deal with multiple pdu outputs.
+ */
+
+ output_data out_data;
+
+ /* talloc context to use when allocating memory on this pipe. */
+ TALLOC_CTX *mem_ctx;
+
+ /* handle database to use on this pipe. */
+ struct handle_list *pipe_handles;
+
+} pipes_struct;
+
+typedef struct smb_np_struct
+{
+ struct smb_np_struct *next, *prev;
+ int pnum;
+ connection_struct *conn;
+ uint16 vuid; /* points to the unauthenticated user that opened this pipe. */
+ BOOL open; /* open connection */
+ uint16 device_state;
+ uint16 priority;
+ fstring name;
+
+ /* When replying to an SMBtrans, this is the maximum amount of
+ data that can be sent in the initial reply. */
+ int max_trans_reply;
+
+ /*
+ * NamedPipe state information.
+ *
+ * (e.g. typecast a np_struct, above).
+ */
+ void *np_state;
+
+ /*
+ * NamedPipe functions, to be called to perform
+ * Named Pipe transactions on request from an
+ * SMB client.
+ */
+
+ /* call to create a named pipe connection.
+ * returns: state information representing the connection.
+ * is stored in np_state, above.
+ */
+ void * (*namedpipe_create)(char *pipe_name,
+ connection_struct *conn, uint16 vuid);
+
+ /* call to perform a write / read namedpipe transaction.
+ * TransactNamedPipe is weird: it returns whether there
+ * is more data outstanding to be read, and the
+ * caller is expected to take note and follow up with
+ * read requests.
+ */
+ ssize_t (*namedpipe_transact)(void *np_state,
+ char *data, int len,
+ char *rdata, int rlen,
+ BOOL *pipe_outstanding);
+
+ /* call to perform a write namedpipe operation
+ */
+ ssize_t (*namedpipe_write)(void * np_state,
+ char *data, size_t n);
+
+ /* call to perform a read namedpipe operation.
+ *
+ * NOTE: the only reason that the pipe_outstanding
+ * argument is here is because samba does not use
+ * the namedpipe_transact function yet: instead,
+ * it performs the same as what namedpipe_transact
+ * does - a write, followed by a read.
+ *
+ * when samba is modified to use namedpipe_transact,
+ * the pipe_outstanding argument may be removed.
+ */
+ ssize_t (*namedpipe_read)(void * np_state,
+ char *data, size_t max_len,
+ BOOL *pipe_outstanding);
+
+ /* call to close a namedpipe.
+ * function is expected to perform all cleanups
+ * necessary, free all memory etc.
+ *
+ * returns True if cleanup was successful (not that
+ * we particularly care).
+ */
+ BOOL (*namedpipe_close)(void * np_state);
+
+} smb_np_struct;
+
+struct api_struct
+{
+ const char *name;
+ uint8 opnum;
+ BOOL (*fn) (pipes_struct *);
+};
+
+typedef struct
+{
+ uint32 rid;
+ const char *name;
+
+} rid_name;
+
+/*
+ * higher order functions for use with msrpc client code
+ */
+
+#define PRINT_INFO_FN(fn)\
+ void (*fn)(const char*, uint32, uint32, void *const *const)
+#define JOB_INFO_FN(fn)\
+ void (*fn)(const char*, const char*, uint32, uint32, void *const *const)
+
+/* end higher order functions */
+
+
+/* security descriptor structures */
+#include "rpc_secdes.h"
+
+/* pac */
+#include "authdata.h"
+
+/* different dce/rpc pipes */
+#include "rpc_lsa.h"
+#include "rpc_netlogon.h"
+#include "rpc_reg.h"
+#include "rpc_samr.h"
+#include "rpc_srvsvc.h"
+#include "rpc_wkssvc.h"
+#include "rpc_spoolss.h"
+#include "rpc_dfs.h"
+#include "rpc_ds.h"
+#include "rpc_echo.h"
+#include "rpc_epmapper.h"
+#include "rpc_shutdown.h"
+
+#endif /* _NT_DOMAIN_H */
diff --git a/source/include/nterr.h b/source/include/nterr.h
new file mode 100644
index 00000000000..19c70cffcc9
--- /dev/null
+++ b/source/include/nterr.h
@@ -0,0 +1,564 @@
+/*
+ Unix SMB/CIFS implementation.
+ NT error code constants
+ Copyright (C) Andrew Tridgell 1992-2000
+ Copyright (C) John H Terpstra 1996-2000
+ Copyright (C) Luke Kenneth Casson Leighton 1996-2000
+ Copyright (C) Paul Ashton 1998-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 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 _NTERR_H
+#define _NTERR_H
+
+/* Win32 Status codes. */
+
+#define STATUS_BUFFER_OVERFLOW NT_STATUS(0x80000005)
+#define NT_STATUS_NO_MORE_ENTRIES NT_STATUS(0x8000001a)
+
+#define STATUS_MORE_ENTRIES NT_STATUS(0x0105)
+#define STATUS_SOME_UNMAPPED NT_STATUS(0x0107)
+#define ERROR_INVALID_PARAMETER NT_STATUS(0x0057)
+#define ERROR_INSUFFICIENT_BUFFER NT_STATUS(0x007a)
+#define STATUS_NOTIFY_ENUM_DIR NT_STATUS(0x010c)
+#define ERROR_INVALID_DATATYPE NT_STATUS(0x070c)
+
+/* Win32 Error codes extracted using a loop in smbclient then printing a
+ netmon sniff to a file. */
+
+/*
+ --------------
+ / \
+ / REST \
+ / IN \
+ / PEACE \
+ / \
+ | NT_STATUS_NOPROBLEMO |
+ | |
+ | |
+ | 4 September |
+ | |
+ | 2001 |
+ *| * * * | *
+ _________)/\\_//(\/(/\)/\//\/\///|_)_______
+*/
+
+#define NT_STATUS_OK NT_STATUS(0x0000)
+#define NT_STATUS_UNSUCCESSFUL NT_STATUS(0xC0000000 | 0x0001)
+#define NT_STATUS_NOT_IMPLEMENTED NT_STATUS(0xC0000000 | 0x0002)
+#define NT_STATUS_INVALID_INFO_CLASS NT_STATUS(0xC0000000 | 0x0003)
+#define NT_STATUS_INFO_LENGTH_MISMATCH NT_STATUS(0xC0000000 | 0x0004)
+#define NT_STATUS_ACCESS_VIOLATION NT_STATUS(0xC0000000 | 0x0005)
+#define NT_STATUS_IN_PAGE_ERROR NT_STATUS(0xC0000000 | 0x0006)
+#define NT_STATUS_PAGEFILE_QUOTA NT_STATUS(0xC0000000 | 0x0007)
+#define NT_STATUS_INVALID_HANDLE NT_STATUS(0xC0000000 | 0x0008)
+#define NT_STATUS_BAD_INITIAL_STACK NT_STATUS(0xC0000000 | 0x0009)
+#define NT_STATUS_BAD_INITIAL_PC NT_STATUS(0xC0000000 | 0x000a)
+#define NT_STATUS_INVALID_CID NT_STATUS(0xC0000000 | 0x000b)
+#define NT_STATUS_TIMER_NOT_CANCELED NT_STATUS(0xC0000000 | 0x000c)
+#define NT_STATUS_INVALID_PARAMETER NT_STATUS(0xC0000000 | 0x000d)
+#define NT_STATUS_NO_SUCH_DEVICE NT_STATUS(0xC0000000 | 0x000e)
+#define NT_STATUS_NO_SUCH_FILE NT_STATUS(0xC0000000 | 0x000f)
+#define NT_STATUS_INVALID_DEVICE_REQUEST NT_STATUS(0xC0000000 | 0x0010)
+#define NT_STATUS_END_OF_FILE NT_STATUS(0xC0000000 | 0x0011)
+#define NT_STATUS_WRONG_VOLUME NT_STATUS(0xC0000000 | 0x0012)
+#define NT_STATUS_NO_MEDIA_IN_DEVICE NT_STATUS(0xC0000000 | 0x0013)
+#define NT_STATUS_UNRECOGNIZED_MEDIA NT_STATUS(0xC0000000 | 0x0014)
+#define NT_STATUS_NONEXISTENT_SECTOR NT_STATUS(0xC0000000 | 0x0015)
+#define NT_STATUS_MORE_PROCESSING_REQUIRED NT_STATUS(0xC0000000 | 0x0016)
+#define NT_STATUS_NO_MEMORY NT_STATUS(0xC0000000 | 0x0017)
+#define NT_STATUS_CONFLICTING_ADDRESSES NT_STATUS(0xC0000000 | 0x0018)
+#define NT_STATUS_NOT_MAPPED_VIEW NT_STATUS(0xC0000000 | 0x0019)
+#define NT_STATUS_UNABLE_TO_FREE_VM NT_STATUS(0xC0000000 | 0x001a)
+#define NT_STATUS_UNABLE_TO_DELETE_SECTION NT_STATUS(0xC0000000 | 0x001b)
+#define NT_STATUS_INVALID_SYSTEM_SERVICE NT_STATUS(0xC0000000 | 0x001c)
+#define NT_STATUS_ILLEGAL_INSTRUCTION NT_STATUS(0xC0000000 | 0x001d)
+#define NT_STATUS_INVALID_LOCK_SEQUENCE NT_STATUS(0xC0000000 | 0x001e)
+#define NT_STATUS_INVALID_VIEW_SIZE NT_STATUS(0xC0000000 | 0x001f)
+#define NT_STATUS_INVALID_FILE_FOR_SECTION NT_STATUS(0xC0000000 | 0x0020)
+#define NT_STATUS_ALREADY_COMMITTED NT_STATUS(0xC0000000 | 0x0021)
+#define NT_STATUS_ACCESS_DENIED NT_STATUS(0xC0000000 | 0x0022)
+#define NT_STATUS_BUFFER_TOO_SMALL NT_STATUS(0xC0000000 | 0x0023)
+#define NT_STATUS_OBJECT_TYPE_MISMATCH NT_STATUS(0xC0000000 | 0x0024)
+#define NT_STATUS_NONCONTINUABLE_EXCEPTION NT_STATUS(0xC0000000 | 0x0025)
+#define NT_STATUS_INVALID_DISPOSITION NT_STATUS(0xC0000000 | 0x0026)
+#define NT_STATUS_UNWIND NT_STATUS(0xC0000000 | 0x0027)
+#define NT_STATUS_BAD_STACK NT_STATUS(0xC0000000 | 0x0028)
+#define NT_STATUS_INVALID_UNWIND_TARGET NT_STATUS(0xC0000000 | 0x0029)
+#define NT_STATUS_NOT_LOCKED NT_STATUS(0xC0000000 | 0x002a)
+#define NT_STATUS_PARITY_ERROR NT_STATUS(0xC0000000 | 0x002b)
+#define NT_STATUS_UNABLE_TO_DECOMMIT_VM NT_STATUS(0xC0000000 | 0x002c)
+#define NT_STATUS_NOT_COMMITTED NT_STATUS(0xC0000000 | 0x002d)
+#define NT_STATUS_INVALID_PORT_ATTRIBUTES NT_STATUS(0xC0000000 | 0x002e)
+#define NT_STATUS_PORT_MESSAGE_TOO_LONG NT_STATUS(0xC0000000 | 0x002f)
+#define NT_STATUS_INVALID_PARAMETER_MIX NT_STATUS(0xC0000000 | 0x0030)
+#define NT_STATUS_INVALID_QUOTA_LOWER NT_STATUS(0xC0000000 | 0x0031)
+#define NT_STATUS_DISK_CORRUPT_ERROR NT_STATUS(0xC0000000 | 0x0032)
+#define NT_STATUS_OBJECT_NAME_INVALID NT_STATUS(0xC0000000 | 0x0033)
+#define NT_STATUS_OBJECT_NAME_NOT_FOUND NT_STATUS(0xC0000000 | 0x0034)
+#define NT_STATUS_OBJECT_NAME_COLLISION NT_STATUS(0xC0000000 | 0x0035)
+#define NT_STATUS_HANDLE_NOT_WAITABLE NT_STATUS(0xC0000000 | 0x0036)
+#define NT_STATUS_PORT_DISCONNECTED NT_STATUS(0xC0000000 | 0x0037)
+#define NT_STATUS_DEVICE_ALREADY_ATTACHED NT_STATUS(0xC0000000 | 0x0038)
+#define NT_STATUS_OBJECT_PATH_INVALID NT_STATUS(0xC0000000 | 0x0039)
+#define NT_STATUS_OBJECT_PATH_NOT_FOUND NT_STATUS(0xC0000000 | 0x003a)
+#define NT_STATUS_OBJECT_PATH_SYNTAX_BAD NT_STATUS(0xC0000000 | 0x003b)
+#define NT_STATUS_DATA_OVERRUN NT_STATUS(0xC0000000 | 0x003c)
+#define NT_STATUS_DATA_LATE_ERROR NT_STATUS(0xC0000000 | 0x003d)
+#define NT_STATUS_DATA_ERROR NT_STATUS(0xC0000000 | 0x003e)
+#define NT_STATUS_CRC_ERROR NT_STATUS(0xC0000000 | 0x003f)
+#define NT_STATUS_SECTION_TOO_BIG NT_STATUS(0xC0000000 | 0x0040)
+#define NT_STATUS_PORT_CONNECTION_REFUSED NT_STATUS(0xC0000000 | 0x0041)
+#define NT_STATUS_INVALID_PORT_HANDLE NT_STATUS(0xC0000000 | 0x0042)
+#define NT_STATUS_SHARING_VIOLATION NT_STATUS(0xC0000000 | 0x0043)
+#define NT_STATUS_QUOTA_EXCEEDED NT_STATUS(0xC0000000 | 0x0044)
+#define NT_STATUS_INVALID_PAGE_PROTECTION NT_STATUS(0xC0000000 | 0x0045)
+#define NT_STATUS_MUTANT_NOT_OWNED NT_STATUS(0xC0000000 | 0x0046)
+#define NT_STATUS_SEMAPHORE_LIMIT_EXCEEDED NT_STATUS(0xC0000000 | 0x0047)
+#define NT_STATUS_PORT_ALREADY_SET NT_STATUS(0xC0000000 | 0x0048)
+#define NT_STATUS_SECTION_NOT_IMAGE NT_STATUS(0xC0000000 | 0x0049)
+#define NT_STATUS_SUSPEND_COUNT_EXCEEDED NT_STATUS(0xC0000000 | 0x004a)
+#define NT_STATUS_THREAD_IS_TERMINATING NT_STATUS(0xC0000000 | 0x004b)
+#define NT_STATUS_BAD_WORKING_SET_LIMIT NT_STATUS(0xC0000000 | 0x004c)
+#define NT_STATUS_INCOMPATIBLE_FILE_MAP NT_STATUS(0xC0000000 | 0x004d)
+#define NT_STATUS_SECTION_PROTECTION NT_STATUS(0xC0000000 | 0x004e)
+#define NT_STATUS_EAS_NOT_SUPPORTED NT_STATUS(0xC0000000 | 0x004f)
+#define NT_STATUS_EA_TOO_LARGE NT_STATUS(0xC0000000 | 0x0050)
+#define NT_STATUS_NONEXISTENT_EA_ENTRY NT_STATUS(0xC0000000 | 0x0051)
+#define NT_STATUS_NO_EAS_ON_FILE NT_STATUS(0xC0000000 | 0x0052)
+#define NT_STATUS_EA_CORRUPT_ERROR NT_STATUS(0xC0000000 | 0x0053)
+#define NT_STATUS_FILE_LOCK_CONFLICT NT_STATUS(0xC0000000 | 0x0054)
+#define NT_STATUS_LOCK_NOT_GRANTED NT_STATUS(0xC0000000 | 0x0055)
+#define NT_STATUS_DELETE_PENDING NT_STATUS(0xC0000000 | 0x0056)
+#define NT_STATUS_CTL_FILE_NOT_SUPPORTED NT_STATUS(0xC0000000 | 0x0057)
+#define NT_STATUS_UNKNOWN_REVISION NT_STATUS(0xC0000000 | 0x0058)
+#define NT_STATUS_REVISION_MISMATCH NT_STATUS(0xC0000000 | 0x0059)
+#define NT_STATUS_INVALID_OWNER NT_STATUS(0xC0000000 | 0x005a)
+#define NT_STATUS_INVALID_PRIMARY_GROUP NT_STATUS(0xC0000000 | 0x005b)
+#define NT_STATUS_NO_IMPERSONATION_TOKEN NT_STATUS(0xC0000000 | 0x005c)
+#define NT_STATUS_CANT_DISABLE_MANDATORY NT_STATUS(0xC0000000 | 0x005d)
+#define NT_STATUS_NO_LOGON_SERVERS NT_STATUS(0xC0000000 | 0x005e)
+#define NT_STATUS_NO_SUCH_LOGON_SESSION NT_STATUS(0xC0000000 | 0x005f)
+#define NT_STATUS_NO_SUCH_PRIVILEGE NT_STATUS(0xC0000000 | 0x0060)
+#define NT_STATUS_PRIVILEGE_NOT_HELD NT_STATUS(0xC0000000 | 0x0061)
+#define NT_STATUS_INVALID_ACCOUNT_NAME NT_STATUS(0xC0000000 | 0x0062)
+#define NT_STATUS_USER_EXISTS NT_STATUS(0xC0000000 | 0x0063)
+#define NT_STATUS_NO_SUCH_USER NT_STATUS(0xC0000000 | 0x0064)
+#define NT_STATUS_GROUP_EXISTS NT_STATUS(0xC0000000 | 0x0065)
+#define NT_STATUS_NO_SUCH_GROUP NT_STATUS(0xC0000000 | 0x0066)
+#define NT_STATUS_MEMBER_IN_GROUP NT_STATUS(0xC0000000 | 0x0067)
+#define NT_STATUS_MEMBER_NOT_IN_GROUP NT_STATUS(0xC0000000 | 0x0068)
+#define NT_STATUS_LAST_ADMIN NT_STATUS(0xC0000000 | 0x0069)
+#define NT_STATUS_WRONG_PASSWORD NT_STATUS(0xC0000000 | 0x006a)
+#define NT_STATUS_ILL_FORMED_PASSWORD NT_STATUS(0xC0000000 | 0x006b)
+#define NT_STATUS_PASSWORD_RESTRICTION NT_STATUS(0xC0000000 | 0x006c)
+#define NT_STATUS_LOGON_FAILURE NT_STATUS(0xC0000000 | 0x006d)
+#define NT_STATUS_ACCOUNT_RESTRICTION NT_STATUS(0xC0000000 | 0x006e)
+#define NT_STATUS_INVALID_LOGON_HOURS NT_STATUS(0xC0000000 | 0x006f)
+#define NT_STATUS_INVALID_WORKSTATION NT_STATUS(0xC0000000 | 0x0070)
+#define NT_STATUS_PASSWORD_EXPIRED NT_STATUS(0xC0000000 | 0x0071)
+#define NT_STATUS_ACCOUNT_DISABLED NT_STATUS(0xC0000000 | 0x0072)
+#define NT_STATUS_NONE_MAPPED NT_STATUS(0xC0000000 | 0x0073)
+#define NT_STATUS_TOO_MANY_LUIDS_REQUESTED NT_STATUS(0xC0000000 | 0x0074)
+#define NT_STATUS_LUIDS_EXHAUSTED NT_STATUS(0xC0000000 | 0x0075)
+#define NT_STATUS_INVALID_SUB_AUTHORITY NT_STATUS(0xC0000000 | 0x0076)
+#define NT_STATUS_INVALID_ACL NT_STATUS(0xC0000000 | 0x0077)
+#define NT_STATUS_INVALID_SID NT_STATUS(0xC0000000 | 0x0078)
+#define NT_STATUS_INVALID_SECURITY_DESCR NT_STATUS(0xC0000000 | 0x0079)
+#define NT_STATUS_PROCEDURE_NOT_FOUND NT_STATUS(0xC0000000 | 0x007a)
+#define NT_STATUS_INVALID_IMAGE_FORMAT NT_STATUS(0xC0000000 | 0x007b)
+#define NT_STATUS_NO_TOKEN NT_STATUS(0xC0000000 | 0x007c)
+#define NT_STATUS_BAD_INHERITANCE_ACL NT_STATUS(0xC0000000 | 0x007d)
+#define NT_STATUS_RANGE_NOT_LOCKED NT_STATUS(0xC0000000 | 0x007e)
+#define NT_STATUS_DISK_FULL NT_STATUS(0xC0000000 | 0x007f)
+#define NT_STATUS_SERVER_DISABLED NT_STATUS(0xC0000000 | 0x0080)
+#define NT_STATUS_SERVER_NOT_DISABLED NT_STATUS(0xC0000000 | 0x0081)
+#define NT_STATUS_TOO_MANY_GUIDS_REQUESTED NT_STATUS(0xC0000000 | 0x0082)
+#define NT_STATUS_GUIDS_EXHAUSTED NT_STATUS(0xC0000000 | 0x0083)
+#define NT_STATUS_INVALID_ID_AUTHORITY NT_STATUS(0xC0000000 | 0x0084)
+#define NT_STATUS_AGENTS_EXHAUSTED NT_STATUS(0xC0000000 | 0x0085)
+#define NT_STATUS_INVALID_VOLUME_LABEL NT_STATUS(0xC0000000 | 0x0086)
+#define NT_STATUS_SECTION_NOT_EXTENDED NT_STATUS(0xC0000000 | 0x0087)
+#define NT_STATUS_NOT_MAPPED_DATA NT_STATUS(0xC0000000 | 0x0088)
+#define NT_STATUS_RESOURCE_DATA_NOT_FOUND NT_STATUS(0xC0000000 | 0x0089)
+#define NT_STATUS_RESOURCE_TYPE_NOT_FOUND NT_STATUS(0xC0000000 | 0x008a)
+#define NT_STATUS_RESOURCE_NAME_NOT_FOUND NT_STATUS(0xC0000000 | 0x008b)
+#define NT_STATUS_ARRAY_BOUNDS_EXCEEDED NT_STATUS(0xC0000000 | 0x008c)
+#define NT_STATUS_FLOAT_DENORMAL_OPERAND NT_STATUS(0xC0000000 | 0x008d)
+#define NT_STATUS_FLOAT_DIVIDE_BY_ZERO NT_STATUS(0xC0000000 | 0x008e)
+#define NT_STATUS_FLOAT_INEXACT_RESULT NT_STATUS(0xC0000000 | 0x008f)
+#define NT_STATUS_FLOAT_INVALID_OPERATION NT_STATUS(0xC0000000 | 0x0090)
+#define NT_STATUS_FLOAT_OVERFLOW NT_STATUS(0xC0000000 | 0x0091)
+#define NT_STATUS_FLOAT_STACK_CHECK NT_STATUS(0xC0000000 | 0x0092)
+#define NT_STATUS_FLOAT_UNDERFLOW NT_STATUS(0xC0000000 | 0x0093)
+#define NT_STATUS_INTEGER_DIVIDE_BY_ZERO NT_STATUS(0xC0000000 | 0x0094)
+#define NT_STATUS_INTEGER_OVERFLOW NT_STATUS(0xC0000000 | 0x0095)
+#define NT_STATUS_PRIVILEGED_INSTRUCTION NT_STATUS(0xC0000000 | 0x0096)
+#define NT_STATUS_TOO_MANY_PAGING_FILES NT_STATUS(0xC0000000 | 0x0097)
+#define NT_STATUS_FILE_INVALID NT_STATUS(0xC0000000 | 0x0098)
+#define NT_STATUS_ALLOTTED_SPACE_EXCEEDED NT_STATUS(0xC0000000 | 0x0099)
+#define NT_STATUS_INSUFFICIENT_RESOURCES NT_STATUS(0xC0000000 | 0x009a)
+#define NT_STATUS_DFS_EXIT_PATH_FOUND NT_STATUS(0xC0000000 | 0x009b)
+#define NT_STATUS_DEVICE_DATA_ERROR NT_STATUS(0xC0000000 | 0x009c)
+#define NT_STATUS_DEVICE_NOT_CONNECTED NT_STATUS(0xC0000000 | 0x009d)
+#define NT_STATUS_DEVICE_POWER_FAILURE NT_STATUS(0xC0000000 | 0x009e)
+#define NT_STATUS_FREE_VM_NOT_AT_BASE NT_STATUS(0xC0000000 | 0x009f)
+#define NT_STATUS_MEMORY_NOT_ALLOCATED NT_STATUS(0xC0000000 | 0x00a0)
+#define NT_STATUS_WORKING_SET_QUOTA NT_STATUS(0xC0000000 | 0x00a1)
+#define NT_STATUS_MEDIA_WRITE_PROTECTED NT_STATUS(0xC0000000 | 0x00a2)
+#define NT_STATUS_DEVICE_NOT_READY NT_STATUS(0xC0000000 | 0x00a3)
+#define NT_STATUS_INVALID_GROUP_ATTRIBUTES NT_STATUS(0xC0000000 | 0x00a4)
+#define NT_STATUS_BAD_IMPERSONATION_LEVEL NT_STATUS(0xC0000000 | 0x00a5)
+#define NT_STATUS_CANT_OPEN_ANONYMOUS NT_STATUS(0xC0000000 | 0x00a6)
+#define NT_STATUS_BAD_VALIDATION_CLASS NT_STATUS(0xC0000000 | 0x00a7)
+#define NT_STATUS_BAD_TOKEN_TYPE NT_STATUS(0xC0000000 | 0x00a8)
+#define NT_STATUS_BAD_MASTER_BOOT_RECORD NT_STATUS(0xC0000000 | 0x00a9)
+#define NT_STATUS_INSTRUCTION_MISALIGNMENT NT_STATUS(0xC0000000 | 0x00aa)
+#define NT_STATUS_INSTANCE_NOT_AVAILABLE NT_STATUS(0xC0000000 | 0x00ab)
+#define NT_STATUS_PIPE_NOT_AVAILABLE NT_STATUS(0xC0000000 | 0x00ac)
+#define NT_STATUS_INVALID_PIPE_STATE NT_STATUS(0xC0000000 | 0x00ad)
+#define NT_STATUS_PIPE_BUSY NT_STATUS(0xC0000000 | 0x00ae)
+#define NT_STATUS_ILLEGAL_FUNCTION NT_STATUS(0xC0000000 | 0x00af)
+#define NT_STATUS_PIPE_DISCONNECTED NT_STATUS(0xC0000000 | 0x00b0)
+#define NT_STATUS_PIPE_CLOSING NT_STATUS(0xC0000000 | 0x00b1)
+#define NT_STATUS_PIPE_CONNECTED NT_STATUS(0xC0000000 | 0x00b2)
+#define NT_STATUS_PIPE_LISTENING NT_STATUS(0xC0000000 | 0x00b3)
+#define NT_STATUS_INVALID_READ_MODE NT_STATUS(0xC0000000 | 0x00b4)
+#define NT_STATUS_IO_TIMEOUT NT_STATUS(0xC0000000 | 0x00b5)
+#define NT_STATUS_FILE_FORCED_CLOSED NT_STATUS(0xC0000000 | 0x00b6)
+#define NT_STATUS_PROFILING_NOT_STARTED NT_STATUS(0xC0000000 | 0x00b7)
+#define NT_STATUS_PROFILING_NOT_STOPPED NT_STATUS(0xC0000000 | 0x00b8)
+#define NT_STATUS_COULD_NOT_INTERPRET NT_STATUS(0xC0000000 | 0x00b9)
+#define NT_STATUS_FILE_IS_A_DIRECTORY NT_STATUS(0xC0000000 | 0x00ba)
+#define NT_STATUS_NOT_SUPPORTED NT_STATUS(0xC0000000 | 0x00bb)
+#define NT_STATUS_REMOTE_NOT_LISTENING NT_STATUS(0xC0000000 | 0x00bc)
+#define NT_STATUS_DUPLICATE_NAME NT_STATUS(0xC0000000 | 0x00bd)
+#define NT_STATUS_BAD_NETWORK_PATH NT_STATUS(0xC0000000 | 0x00be)
+#define NT_STATUS_NETWORK_BUSY NT_STATUS(0xC0000000 | 0x00bf)
+#define NT_STATUS_DEVICE_DOES_NOT_EXIST NT_STATUS(0xC0000000 | 0x00c0)
+#define NT_STATUS_TOO_MANY_COMMANDS NT_STATUS(0xC0000000 | 0x00c1)
+#define NT_STATUS_ADAPTER_HARDWARE_ERROR NT_STATUS(0xC0000000 | 0x00c2)
+#define NT_STATUS_INVALID_NETWORK_RESPONSE NT_STATUS(0xC0000000 | 0x00c3)
+#define NT_STATUS_UNEXPECTED_NETWORK_ERROR NT_STATUS(0xC0000000 | 0x00c4)
+#define NT_STATUS_BAD_REMOTE_ADAPTER NT_STATUS(0xC0000000 | 0x00c5)
+#define NT_STATUS_PRINT_QUEUE_FULL NT_STATUS(0xC0000000 | 0x00c6)
+#define NT_STATUS_NO_SPOOL_SPACE NT_STATUS(0xC0000000 | 0x00c7)
+#define NT_STATUS_PRINT_CANCELLED NT_STATUS(0xC0000000 | 0x00c8)
+#define NT_STATUS_NETWORK_NAME_DELETED NT_STATUS(0xC0000000 | 0x00c9)
+#define NT_STATUS_NETWORK_ACCESS_DENIED NT_STATUS(0xC0000000 | 0x00ca)
+#define NT_STATUS_BAD_DEVICE_TYPE NT_STATUS(0xC0000000 | 0x00cb)
+#define NT_STATUS_BAD_NETWORK_NAME NT_STATUS(0xC0000000 | 0x00cc)
+#define NT_STATUS_TOO_MANY_NAMES NT_STATUS(0xC0000000 | 0x00cd)
+#define NT_STATUS_TOO_MANY_SESSIONS NT_STATUS(0xC0000000 | 0x00ce)
+#define NT_STATUS_SHARING_PAUSED NT_STATUS(0xC0000000 | 0x00cf)
+#define NT_STATUS_REQUEST_NOT_ACCEPTED NT_STATUS(0xC0000000 | 0x00d0)
+#define NT_STATUS_REDIRECTOR_PAUSED NT_STATUS(0xC0000000 | 0x00d1)
+#define NT_STATUS_NET_WRITE_FAULT NT_STATUS(0xC0000000 | 0x00d2)
+#define NT_STATUS_PROFILING_AT_LIMIT NT_STATUS(0xC0000000 | 0x00d3)
+#define NT_STATUS_NOT_SAME_DEVICE NT_STATUS(0xC0000000 | 0x00d4)
+#define NT_STATUS_FILE_RENAMED NT_STATUS(0xC0000000 | 0x00d5)
+#define NT_STATUS_VIRTUAL_CIRCUIT_CLOSED NT_STATUS(0xC0000000 | 0x00d6)
+#define NT_STATUS_NO_SECURITY_ON_OBJECT NT_STATUS(0xC0000000 | 0x00d7)
+#define NT_STATUS_CANT_WAIT NT_STATUS(0xC0000000 | 0x00d8)
+#define NT_STATUS_PIPE_EMPTY NT_STATUS(0xC0000000 | 0x00d9)
+#define NT_STATUS_CANT_ACCESS_DOMAIN_INFO NT_STATUS(0xC0000000 | 0x00da)
+#define NT_STATUS_CANT_TERMINATE_SELF NT_STATUS(0xC0000000 | 0x00db)
+#define NT_STATUS_INVALID_SERVER_STATE NT_STATUS(0xC0000000 | 0x00dc)
+#define NT_STATUS_INVALID_DOMAIN_STATE NT_STATUS(0xC0000000 | 0x00dd)
+#define NT_STATUS_INVALID_DOMAIN_ROLE NT_STATUS(0xC0000000 | 0x00de)
+#define NT_STATUS_NO_SUCH_DOMAIN NT_STATUS(0xC0000000 | 0x00df)
+#define NT_STATUS_DOMAIN_EXISTS NT_STATUS(0xC0000000 | 0x00e0)
+#define NT_STATUS_DOMAIN_LIMIT_EXCEEDED NT_STATUS(0xC0000000 | 0x00e1)
+#define NT_STATUS_OPLOCK_NOT_GRANTED NT_STATUS(0xC0000000 | 0x00e2)
+#define NT_STATUS_INVALID_OPLOCK_PROTOCOL NT_STATUS(0xC0000000 | 0x00e3)
+#define NT_STATUS_INTERNAL_DB_CORRUPTION NT_STATUS(0xC0000000 | 0x00e4)
+#define NT_STATUS_INTERNAL_ERROR NT_STATUS(0xC0000000 | 0x00e5)
+#define NT_STATUS_GENERIC_NOT_MAPPED NT_STATUS(0xC0000000 | 0x00e6)
+#define NT_STATUS_BAD_DESCRIPTOR_FORMAT NT_STATUS(0xC0000000 | 0x00e7)
+#define NT_STATUS_INVALID_USER_BUFFER NT_STATUS(0xC0000000 | 0x00e8)
+#define NT_STATUS_UNEXPECTED_IO_ERROR NT_STATUS(0xC0000000 | 0x00e9)
+#define NT_STATUS_UNEXPECTED_MM_CREATE_ERR NT_STATUS(0xC0000000 | 0x00ea)
+#define NT_STATUS_UNEXPECTED_MM_MAP_ERROR NT_STATUS(0xC0000000 | 0x00eb)
+#define NT_STATUS_UNEXPECTED_MM_EXTEND_ERR NT_STATUS(0xC0000000 | 0x00ec)
+#define NT_STATUS_NOT_LOGON_PROCESS NT_STATUS(0xC0000000 | 0x00ed)
+#define NT_STATUS_LOGON_SESSION_EXISTS NT_STATUS(0xC0000000 | 0x00ee)
+#define NT_STATUS_INVALID_PARAMETER_1 NT_STATUS(0xC0000000 | 0x00ef)
+#define NT_STATUS_INVALID_PARAMETER_2 NT_STATUS(0xC0000000 | 0x00f0)
+#define NT_STATUS_INVALID_PARAMETER_3 NT_STATUS(0xC0000000 | 0x00f1)
+#define NT_STATUS_INVALID_PARAMETER_4 NT_STATUS(0xC0000000 | 0x00f2)
+#define NT_STATUS_INVALID_PARAMETER_5 NT_STATUS(0xC0000000 | 0x00f3)
+#define NT_STATUS_INVALID_PARAMETER_6 NT_STATUS(0xC0000000 | 0x00f4)
+#define NT_STATUS_INVALID_PARAMETER_7 NT_STATUS(0xC0000000 | 0x00f5)
+#define NT_STATUS_INVALID_PARAMETER_8 NT_STATUS(0xC0000000 | 0x00f6)
+#define NT_STATUS_INVALID_PARAMETER_9 NT_STATUS(0xC0000000 | 0x00f7)
+#define NT_STATUS_INVALID_PARAMETER_10 NT_STATUS(0xC0000000 | 0x00f8)
+#define NT_STATUS_INVALID_PARAMETER_11 NT_STATUS(0xC0000000 | 0x00f9)
+#define NT_STATUS_INVALID_PARAMETER_12 NT_STATUS(0xC0000000 | 0x00fa)
+#define NT_STATUS_REDIRECTOR_NOT_STARTED NT_STATUS(0xC0000000 | 0x00fb)
+#define NT_STATUS_REDIRECTOR_STARTED NT_STATUS(0xC0000000 | 0x00fc)
+#define NT_STATUS_STACK_OVERFLOW NT_STATUS(0xC0000000 | 0x00fd)
+#define NT_STATUS_NO_SUCH_PACKAGE NT_STATUS(0xC0000000 | 0x00fe)
+#define NT_STATUS_BAD_FUNCTION_TABLE NT_STATUS(0xC0000000 | 0x00ff)
+#define NT_STATUS_DIRECTORY_NOT_EMPTY NT_STATUS(0xC0000000 | 0x0101)
+#define NT_STATUS_FILE_CORRUPT_ERROR NT_STATUS(0xC0000000 | 0x0102)
+#define NT_STATUS_NOT_A_DIRECTORY NT_STATUS(0xC0000000 | 0x0103)
+#define NT_STATUS_BAD_LOGON_SESSION_STATE NT_STATUS(0xC0000000 | 0x0104)
+#define NT_STATUS_LOGON_SESSION_COLLISION NT_STATUS(0xC0000000 | 0x0105)
+#define NT_STATUS_NAME_TOO_LONG NT_STATUS(0xC0000000 | 0x0106)
+#define NT_STATUS_FILES_OPEN NT_STATUS(0xC0000000 | 0x0107)
+#define NT_STATUS_CONNECTION_IN_USE NT_STATUS(0xC0000000 | 0x0108)
+#define NT_STATUS_MESSAGE_NOT_FOUND NT_STATUS(0xC0000000 | 0x0109)
+#define NT_STATUS_PROCESS_IS_TERMINATING NT_STATUS(0xC0000000 | 0x010a)
+#define NT_STATUS_INVALID_LOGON_TYPE NT_STATUS(0xC0000000 | 0x010b)
+#define NT_STATUS_NO_GUID_TRANSLATION NT_STATUS(0xC0000000 | 0x010c)
+#define NT_STATUS_CANNOT_IMPERSONATE NT_STATUS(0xC0000000 | 0x010d)
+#define NT_STATUS_IMAGE_ALREADY_LOADED NT_STATUS(0xC0000000 | 0x010e)
+#define NT_STATUS_ABIOS_NOT_PRESENT NT_STATUS(0xC0000000 | 0x010f)
+#define NT_STATUS_ABIOS_LID_NOT_EXIST NT_STATUS(0xC0000000 | 0x0110)
+#define NT_STATUS_ABIOS_LID_ALREADY_OWNED NT_STATUS(0xC0000000 | 0x0111)
+#define NT_STATUS_ABIOS_NOT_LID_OWNER NT_STATUS(0xC0000000 | 0x0112)
+#define NT_STATUS_ABIOS_INVALID_COMMAND NT_STATUS(0xC0000000 | 0x0113)
+#define NT_STATUS_ABIOS_INVALID_LID NT_STATUS(0xC0000000 | 0x0114)
+#define NT_STATUS_ABIOS_SELECTOR_NOT_AVAILABLE NT_STATUS(0xC0000000 | 0x0115)
+#define NT_STATUS_ABIOS_INVALID_SELECTOR NT_STATUS(0xC0000000 | 0x0116)
+#define NT_STATUS_NO_LDT NT_STATUS(0xC0000000 | 0x0117)
+#define NT_STATUS_INVALID_LDT_SIZE NT_STATUS(0xC0000000 | 0x0118)
+#define NT_STATUS_INVALID_LDT_OFFSET NT_STATUS(0xC0000000 | 0x0119)
+#define NT_STATUS_INVALID_LDT_DESCRIPTOR NT_STATUS(0xC0000000 | 0x011a)
+#define NT_STATUS_INVALID_IMAGE_NE_FORMAT NT_STATUS(0xC0000000 | 0x011b)
+#define NT_STATUS_RXACT_INVALID_STATE NT_STATUS(0xC0000000 | 0x011c)
+#define NT_STATUS_RXACT_COMMIT_FAILURE NT_STATUS(0xC0000000 | 0x011d)
+#define NT_STATUS_MAPPED_FILE_SIZE_ZERO NT_STATUS(0xC0000000 | 0x011e)
+#define NT_STATUS_TOO_MANY_OPENED_FILES NT_STATUS(0xC0000000 | 0x011f)
+#define NT_STATUS_CANCELLED NT_STATUS(0xC0000000 | 0x0120)
+#define NT_STATUS_CANNOT_DELETE NT_STATUS(0xC0000000 | 0x0121)
+#define NT_STATUS_INVALID_COMPUTER_NAME NT_STATUS(0xC0000000 | 0x0122)
+#define NT_STATUS_FILE_DELETED NT_STATUS(0xC0000000 | 0x0123)
+#define NT_STATUS_SPECIAL_ACCOUNT NT_STATUS(0xC0000000 | 0x0124)
+#define NT_STATUS_SPECIAL_GROUP NT_STATUS(0xC0000000 | 0x0125)
+#define NT_STATUS_SPECIAL_USER NT_STATUS(0xC0000000 | 0x0126)
+#define NT_STATUS_MEMBERS_PRIMARY_GROUP NT_STATUS(0xC0000000 | 0x0127)
+#define NT_STATUS_FILE_CLOSED NT_STATUS(0xC0000000 | 0x0128)
+#define NT_STATUS_TOO_MANY_THREADS NT_STATUS(0xC0000000 | 0x0129)
+#define NT_STATUS_THREAD_NOT_IN_PROCESS NT_STATUS(0xC0000000 | 0x012a)
+#define NT_STATUS_TOKEN_ALREADY_IN_USE NT_STATUS(0xC0000000 | 0x012b)
+#define NT_STATUS_PAGEFILE_QUOTA_EXCEEDED NT_STATUS(0xC0000000 | 0x012c)
+#define NT_STATUS_COMMITMENT_LIMIT NT_STATUS(0xC0000000 | 0x012d)
+#define NT_STATUS_INVALID_IMAGE_LE_FORMAT NT_STATUS(0xC0000000 | 0x012e)
+#define NT_STATUS_INVALID_IMAGE_NOT_MZ NT_STATUS(0xC0000000 | 0x012f)
+#define NT_STATUS_INVALID_IMAGE_PROTECT NT_STATUS(0xC0000000 | 0x0130)
+#define NT_STATUS_INVALID_IMAGE_WIN_16 NT_STATUS(0xC0000000 | 0x0131)
+#define NT_STATUS_LOGON_SERVER_CONFLICT NT_STATUS(0xC0000000 | 0x0132)
+#define NT_STATUS_TIME_DIFFERENCE_AT_DC NT_STATUS(0xC0000000 | 0x0133)
+#define NT_STATUS_SYNCHRONIZATION_REQUIRED NT_STATUS(0xC0000000 | 0x0134)
+#define NT_STATUS_DLL_NOT_FOUND NT_STATUS(0xC0000000 | 0x0135)
+#define NT_STATUS_OPEN_FAILED NT_STATUS(0xC0000000 | 0x0136)
+#define NT_STATUS_IO_PRIVILEGE_FAILED NT_STATUS(0xC0000000 | 0x0137)
+#define NT_STATUS_ORDINAL_NOT_FOUND NT_STATUS(0xC0000000 | 0x0138)
+#define NT_STATUS_ENTRYPOINT_NOT_FOUND NT_STATUS(0xC0000000 | 0x0139)
+#define NT_STATUS_CONTROL_C_EXIT NT_STATUS(0xC0000000 | 0x013a)
+#define NT_STATUS_LOCAL_DISCONNECT NT_STATUS(0xC0000000 | 0x013b)
+#define NT_STATUS_REMOTE_DISCONNECT NT_STATUS(0xC0000000 | 0x013c)
+#define NT_STATUS_REMOTE_RESOURCES NT_STATUS(0xC0000000 | 0x013d)
+#define NT_STATUS_LINK_FAILED NT_STATUS(0xC0000000 | 0x013e)
+#define NT_STATUS_LINK_TIMEOUT NT_STATUS(0xC0000000 | 0x013f)
+#define NT_STATUS_INVALID_CONNECTION NT_STATUS(0xC0000000 | 0x0140)
+#define NT_STATUS_INVALID_ADDRESS NT_STATUS(0xC0000000 | 0x0141)
+#define NT_STATUS_DLL_INIT_FAILED NT_STATUS(0xC0000000 | 0x0142)
+#define NT_STATUS_MISSING_SYSTEMFILE NT_STATUS(0xC0000000 | 0x0143)
+#define NT_STATUS_UNHANDLED_EXCEPTION NT_STATUS(0xC0000000 | 0x0144)
+#define NT_STATUS_APP_INIT_FAILURE NT_STATUS(0xC0000000 | 0x0145)
+#define NT_STATUS_PAGEFILE_CREATE_FAILED NT_STATUS(0xC0000000 | 0x0146)
+#define NT_STATUS_NO_PAGEFILE NT_STATUS(0xC0000000 | 0x0147)
+#define NT_STATUS_INVALID_LEVEL NT_STATUS(0xC0000000 | 0x0148)
+#define NT_STATUS_WRONG_PASSWORD_CORE NT_STATUS(0xC0000000 | 0x0149)
+#define NT_STATUS_ILLEGAL_FLOAT_CONTEXT NT_STATUS(0xC0000000 | 0x014a)
+#define NT_STATUS_PIPE_BROKEN NT_STATUS(0xC0000000 | 0x014b)
+#define NT_STATUS_REGISTRY_CORRUPT NT_STATUS(0xC0000000 | 0x014c)
+#define NT_STATUS_REGISTRY_IO_FAILED NT_STATUS(0xC0000000 | 0x014d)
+#define NT_STATUS_NO_EVENT_PAIR NT_STATUS(0xC0000000 | 0x014e)
+#define NT_STATUS_UNRECOGNIZED_VOLUME NT_STATUS(0xC0000000 | 0x014f)
+#define NT_STATUS_SERIAL_NO_DEVICE_INITED NT_STATUS(0xC0000000 | 0x0150)
+#define NT_STATUS_NO_SUCH_ALIAS NT_STATUS(0xC0000000 | 0x0151)
+#define NT_STATUS_MEMBER_NOT_IN_ALIAS NT_STATUS(0xC0000000 | 0x0152)
+#define NT_STATUS_MEMBER_IN_ALIAS NT_STATUS(0xC0000000 | 0x0153)
+#define NT_STATUS_ALIAS_EXISTS NT_STATUS(0xC0000000 | 0x0154)
+#define NT_STATUS_LOGON_NOT_GRANTED NT_STATUS(0xC0000000 | 0x0155)
+#define NT_STATUS_TOO_MANY_SECRETS NT_STATUS(0xC0000000 | 0x0156)
+#define NT_STATUS_SECRET_TOO_LONG NT_STATUS(0xC0000000 | 0x0157)
+#define NT_STATUS_INTERNAL_DB_ERROR NT_STATUS(0xC0000000 | 0x0158)
+#define NT_STATUS_FULLSCREEN_MODE NT_STATUS(0xC0000000 | 0x0159)
+#define NT_STATUS_TOO_MANY_CONTEXT_IDS NT_STATUS(0xC0000000 | 0x015a)
+#define NT_STATUS_LOGON_TYPE_NOT_GRANTED NT_STATUS(0xC0000000 | 0x015b)
+#define NT_STATUS_NOT_REGISTRY_FILE NT_STATUS(0xC0000000 | 0x015c)
+#define NT_STATUS_NT_CROSS_ENCRYPTION_REQUIRED NT_STATUS(0xC0000000 | 0x015d)
+#define NT_STATUS_DOMAIN_CTRLR_CONFIG_ERROR NT_STATUS(0xC0000000 | 0x015e)
+#define NT_STATUS_FT_MISSING_MEMBER NT_STATUS(0xC0000000 | 0x015f)
+#define NT_STATUS_ILL_FORMED_SERVICE_ENTRY NT_STATUS(0xC0000000 | 0x0160)
+#define NT_STATUS_ILLEGAL_CHARACTER NT_STATUS(0xC0000000 | 0x0161)
+#define NT_STATUS_UNMAPPABLE_CHARACTER NT_STATUS(0xC0000000 | 0x0162)
+#define NT_STATUS_UNDEFINED_CHARACTER NT_STATUS(0xC0000000 | 0x0163)
+#define NT_STATUS_FLOPPY_VOLUME NT_STATUS(0xC0000000 | 0x0164)
+#define NT_STATUS_FLOPPY_ID_MARK_NOT_FOUND NT_STATUS(0xC0000000 | 0x0165)
+#define NT_STATUS_FLOPPY_WRONG_CYLINDER NT_STATUS(0xC0000000 | 0x0166)
+#define NT_STATUS_FLOPPY_UNKNOWN_ERROR NT_STATUS(0xC0000000 | 0x0167)
+#define NT_STATUS_FLOPPY_BAD_REGISTERS NT_STATUS(0xC0000000 | 0x0168)
+#define NT_STATUS_DISK_RECALIBRATE_FAILED NT_STATUS(0xC0000000 | 0x0169)
+#define NT_STATUS_DISK_OPERATION_FAILED NT_STATUS(0xC0000000 | 0x016a)
+#define NT_STATUS_DISK_RESET_FAILED NT_STATUS(0xC0000000 | 0x016b)
+#define NT_STATUS_SHARED_IRQ_BUSY NT_STATUS(0xC0000000 | 0x016c)
+#define NT_STATUS_FT_ORPHANING NT_STATUS(0xC0000000 | 0x016d)
+#define NT_STATUS_PARTITION_FAILURE NT_STATUS(0xC0000000 | 0x0172)
+#define NT_STATUS_INVALID_BLOCK_LENGTH NT_STATUS(0xC0000000 | 0x0173)
+#define NT_STATUS_DEVICE_NOT_PARTITIONED NT_STATUS(0xC0000000 | 0x0174)
+#define NT_STATUS_UNABLE_TO_LOCK_MEDIA NT_STATUS(0xC0000000 | 0x0175)
+#define NT_STATUS_UNABLE_TO_UNLOAD_MEDIA NT_STATUS(0xC0000000 | 0x0176)
+#define NT_STATUS_EOM_OVERFLOW NT_STATUS(0xC0000000 | 0x0177)
+#define NT_STATUS_NO_MEDIA NT_STATUS(0xC0000000 | 0x0178)
+#define NT_STATUS_NO_SUCH_MEMBER NT_STATUS(0xC0000000 | 0x017a)
+#define NT_STATUS_INVALID_MEMBER NT_STATUS(0xC0000000 | 0x017b)
+#define NT_STATUS_KEY_DELETED NT_STATUS(0xC0000000 | 0x017c)
+#define NT_STATUS_NO_LOG_SPACE NT_STATUS(0xC0000000 | 0x017d)
+#define NT_STATUS_TOO_MANY_SIDS NT_STATUS(0xC0000000 | 0x017e)
+#define NT_STATUS_LM_CROSS_ENCRYPTION_REQUIRED NT_STATUS(0xC0000000 | 0x017f)
+#define NT_STATUS_KEY_HAS_CHILDREN NT_STATUS(0xC0000000 | 0x0180)
+#define NT_STATUS_CHILD_MUST_BE_VOLATILE NT_STATUS(0xC0000000 | 0x0181)
+#define NT_STATUS_DEVICE_CONFIGURATION_ERROR NT_STATUS(0xC0000000 | 0x0182)
+#define NT_STATUS_DRIVER_INTERNAL_ERROR NT_STATUS(0xC0000000 | 0x0183)
+#define NT_STATUS_INVALID_DEVICE_STATE NT_STATUS(0xC0000000 | 0x0184)
+#define NT_STATUS_IO_DEVICE_ERROR NT_STATUS(0xC0000000 | 0x0185)
+#define NT_STATUS_DEVICE_PROTOCOL_ERROR NT_STATUS(0xC0000000 | 0x0186)
+#define NT_STATUS_BACKUP_CONTROLLER NT_STATUS(0xC0000000 | 0x0187)
+#define NT_STATUS_LOG_FILE_FULL NT_STATUS(0xC0000000 | 0x0188)
+#define NT_STATUS_TOO_LATE NT_STATUS(0xC0000000 | 0x0189)
+#define NT_STATUS_NO_TRUST_LSA_SECRET NT_STATUS(0xC0000000 | 0x018a)
+#define NT_STATUS_NO_TRUST_SAM_ACCOUNT NT_STATUS(0xC0000000 | 0x018b)
+#define NT_STATUS_TRUSTED_DOMAIN_FAILURE NT_STATUS(0xC0000000 | 0x018c)
+#define NT_STATUS_TRUSTED_RELATIONSHIP_FAILURE NT_STATUS(0xC0000000 | 0x018d)
+#define NT_STATUS_EVENTLOG_FILE_CORRUPT NT_STATUS(0xC0000000 | 0x018e)
+#define NT_STATUS_EVENTLOG_CANT_START NT_STATUS(0xC0000000 | 0x018f)
+#define NT_STATUS_TRUST_FAILURE NT_STATUS(0xC0000000 | 0x0190)
+#define NT_STATUS_MUTANT_LIMIT_EXCEEDED NT_STATUS(0xC0000000 | 0x0191)
+#define NT_STATUS_NETLOGON_NOT_STARTED NT_STATUS(0xC0000000 | 0x0192)
+#define NT_STATUS_ACCOUNT_EXPIRED NT_STATUS(0xC0000000 | 0x0193)
+#define NT_STATUS_POSSIBLE_DEADLOCK NT_STATUS(0xC0000000 | 0x0194)
+#define NT_STATUS_NETWORK_CREDENTIAL_CONFLICT NT_STATUS(0xC0000000 | 0x0195)
+#define NT_STATUS_REMOTE_SESSION_LIMIT NT_STATUS(0xC0000000 | 0x0196)
+#define NT_STATUS_EVENTLOG_FILE_CHANGED NT_STATUS(0xC0000000 | 0x0197)
+#define NT_STATUS_NOLOGON_INTERDOMAIN_TRUST_ACCOUNT NT_STATUS(0xC0000000 | 0x0198)
+#define NT_STATUS_NOLOGON_WORKSTATION_TRUST_ACCOUNT NT_STATUS(0xC0000000 | 0x0199)
+#define NT_STATUS_NOLOGON_SERVER_TRUST_ACCOUNT NT_STATUS(0xC0000000 | 0x019a)
+#define NT_STATUS_DOMAIN_TRUST_INCONSISTENT NT_STATUS(0xC0000000 | 0x019b)
+#define NT_STATUS_FS_DRIVER_REQUIRED NT_STATUS(0xC0000000 | 0x019c)
+#define NT_STATUS_NO_USER_SESSION_KEY NT_STATUS(0xC0000000 | 0x0202)
+#define NT_STATUS_USER_SESSION_DELETED NT_STATUS(0xC0000000 | 0x0203)
+#define NT_STATUS_RESOURCE_LANG_NOT_FOUND NT_STATUS(0xC0000000 | 0x0204)
+#define NT_STATUS_INSUFF_SERVER_RESOURCES NT_STATUS(0xC0000000 | 0x0205)
+#define NT_STATUS_INVALID_BUFFER_SIZE NT_STATUS(0xC0000000 | 0x0206)
+#define NT_STATUS_INVALID_ADDRESS_COMPONENT NT_STATUS(0xC0000000 | 0x0207)
+#define NT_STATUS_INVALID_ADDRESS_WILDCARD NT_STATUS(0xC0000000 | 0x0208)
+#define NT_STATUS_TOO_MANY_ADDRESSES NT_STATUS(0xC0000000 | 0x0209)
+#define NT_STATUS_ADDRESS_ALREADY_EXISTS NT_STATUS(0xC0000000 | 0x020a)
+#define NT_STATUS_ADDRESS_CLOSED NT_STATUS(0xC0000000 | 0x020b)
+#define NT_STATUS_CONNECTION_DISCONNECTED NT_STATUS(0xC0000000 | 0x020c)
+#define NT_STATUS_CONNECTION_RESET NT_STATUS(0xC0000000 | 0x020d)
+#define NT_STATUS_TOO_MANY_NODES NT_STATUS(0xC0000000 | 0x020e)
+#define NT_STATUS_TRANSACTION_ABORTED NT_STATUS(0xC0000000 | 0x020f)
+#define NT_STATUS_TRANSACTION_TIMED_OUT NT_STATUS(0xC0000000 | 0x0210)
+#define NT_STATUS_TRANSACTION_NO_RELEASE NT_STATUS(0xC0000000 | 0x0211)
+#define NT_STATUS_TRANSACTION_NO_MATCH NT_STATUS(0xC0000000 | 0x0212)
+#define NT_STATUS_TRANSACTION_RESPONDED NT_STATUS(0xC0000000 | 0x0213)
+#define NT_STATUS_TRANSACTION_INVALID_ID NT_STATUS(0xC0000000 | 0x0214)
+#define NT_STATUS_TRANSACTION_INVALID_TYPE NT_STATUS(0xC0000000 | 0x0215)
+#define NT_STATUS_NOT_SERVER_SESSION NT_STATUS(0xC0000000 | 0x0216)
+#define NT_STATUS_NOT_CLIENT_SESSION NT_STATUS(0xC0000000 | 0x0217)
+#define NT_STATUS_CANNOT_LOAD_REGISTRY_FILE NT_STATUS(0xC0000000 | 0x0218)
+#define NT_STATUS_DEBUG_ATTACH_FAILED NT_STATUS(0xC0000000 | 0x0219)
+#define NT_STATUS_SYSTEM_PROCESS_TERMINATED NT_STATUS(0xC0000000 | 0x021a)
+#define NT_STATUS_DATA_NOT_ACCEPTED NT_STATUS(0xC0000000 | 0x021b)
+#define NT_STATUS_NO_BROWSER_SERVERS_FOUND NT_STATUS(0xC0000000 | 0x021c)
+#define NT_STATUS_VDM_HARD_ERROR NT_STATUS(0xC0000000 | 0x021d)
+#define NT_STATUS_DRIVER_CANCEL_TIMEOUT NT_STATUS(0xC0000000 | 0x021e)
+#define NT_STATUS_REPLY_MESSAGE_MISMATCH NT_STATUS(0xC0000000 | 0x021f)
+#define NT_STATUS_MAPPED_ALIGNMENT NT_STATUS(0xC0000000 | 0x0220)
+#define NT_STATUS_IMAGE_CHECKSUM_MISMATCH NT_STATUS(0xC0000000 | 0x0221)
+#define NT_STATUS_LOST_WRITEBEHIND_DATA NT_STATUS(0xC0000000 | 0x0222)
+#define NT_STATUS_CLIENT_SERVER_PARAMETERS_INVALID NT_STATUS(0xC0000000 | 0x0223)
+#define NT_STATUS_PASSWORD_MUST_CHANGE NT_STATUS(0xC0000000 | 0x0224)
+#define NT_STATUS_NOT_FOUND NT_STATUS(0xC0000000 | 0x0225)
+#define NT_STATUS_NOT_TINY_STREAM NT_STATUS(0xC0000000 | 0x0226)
+#define NT_STATUS_RECOVERY_FAILURE NT_STATUS(0xC0000000 | 0x0227)
+#define NT_STATUS_STACK_OVERFLOW_READ NT_STATUS(0xC0000000 | 0x0228)
+#define NT_STATUS_FAIL_CHECK NT_STATUS(0xC0000000 | 0x0229)
+#define NT_STATUS_DUPLICATE_OBJECTID NT_STATUS(0xC0000000 | 0x022a)
+#define NT_STATUS_OBJECTID_EXISTS NT_STATUS(0xC0000000 | 0x022b)
+#define NT_STATUS_CONVERT_TO_LARGE NT_STATUS(0xC0000000 | 0x022c)
+#define NT_STATUS_RETRY NT_STATUS(0xC0000000 | 0x022d)
+#define NT_STATUS_FOUND_OUT_OF_SCOPE NT_STATUS(0xC0000000 | 0x022e)
+#define NT_STATUS_ALLOCATE_BUCKET NT_STATUS(0xC0000000 | 0x022f)
+#define NT_STATUS_PROPSET_NOT_FOUND NT_STATUS(0xC0000000 | 0x0230)
+#define NT_STATUS_MARSHALL_OVERFLOW NT_STATUS(0xC0000000 | 0x0231)
+#define NT_STATUS_INVALID_VARIANT NT_STATUS(0xC0000000 | 0x0232)
+#define NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND NT_STATUS(0xC0000000 | 0x0233)
+#define NT_STATUS_ACCOUNT_LOCKED_OUT NT_STATUS(0xC0000000 | 0x0234)
+#define NT_STATUS_HANDLE_NOT_CLOSABLE NT_STATUS(0xC0000000 | 0x0235)
+#define NT_STATUS_CONNECTION_REFUSED NT_STATUS(0xC0000000 | 0x0236)
+#define NT_STATUS_GRACEFUL_DISCONNECT NT_STATUS(0xC0000000 | 0x0237)
+#define NT_STATUS_ADDRESS_ALREADY_ASSOCIATED NT_STATUS(0xC0000000 | 0x0238)
+#define NT_STATUS_ADDRESS_NOT_ASSOCIATED NT_STATUS(0xC0000000 | 0x0239)
+#define NT_STATUS_CONNECTION_INVALID NT_STATUS(0xC0000000 | 0x023a)
+#define NT_STATUS_CONNECTION_ACTIVE NT_STATUS(0xC0000000 | 0x023b)
+#define NT_STATUS_NETWORK_UNREACHABLE NT_STATUS(0xC0000000 | 0x023c)
+#define NT_STATUS_HOST_UNREACHABLE NT_STATUS(0xC0000000 | 0x023d)
+#define NT_STATUS_PROTOCOL_UNREACHABLE NT_STATUS(0xC0000000 | 0x023e)
+#define NT_STATUS_PORT_UNREACHABLE NT_STATUS(0xC0000000 | 0x023f)
+#define NT_STATUS_REQUEST_ABORTED NT_STATUS(0xC0000000 | 0x0240)
+#define NT_STATUS_CONNECTION_ABORTED NT_STATUS(0xC0000000 | 0x0241)
+#define NT_STATUS_BAD_COMPRESSION_BUFFER NT_STATUS(0xC0000000 | 0x0242)
+#define NT_STATUS_USER_MAPPED_FILE NT_STATUS(0xC0000000 | 0x0243)
+#define NT_STATUS_AUDIT_FAILED NT_STATUS(0xC0000000 | 0x0244)
+#define NT_STATUS_TIMER_RESOLUTION_NOT_SET NT_STATUS(0xC0000000 | 0x0245)
+#define NT_STATUS_CONNECTION_COUNT_LIMIT NT_STATUS(0xC0000000 | 0x0246)
+#define NT_STATUS_LOGIN_TIME_RESTRICTION NT_STATUS(0xC0000000 | 0x0247)
+#define NT_STATUS_LOGIN_WKSTA_RESTRICTION NT_STATUS(0xC0000000 | 0x0248)
+#define NT_STATUS_IMAGE_MP_UP_MISMATCH NT_STATUS(0xC0000000 | 0x0249)
+#define NT_STATUS_INSUFFICIENT_LOGON_INFO NT_STATUS(0xC0000000 | 0x0250)
+#define NT_STATUS_BAD_DLL_ENTRYPOINT NT_STATUS(0xC0000000 | 0x0251)
+#define NT_STATUS_BAD_SERVICE_ENTRYPOINT NT_STATUS(0xC0000000 | 0x0252)
+#define NT_STATUS_LPC_REPLY_LOST NT_STATUS(0xC0000000 | 0x0253)
+#define NT_STATUS_IP_ADDRESS_CONFLICT1 NT_STATUS(0xC0000000 | 0x0254)
+#define NT_STATUS_IP_ADDRESS_CONFLICT2 NT_STATUS(0xC0000000 | 0x0255)
+#define NT_STATUS_REGISTRY_QUOTA_LIMIT NT_STATUS(0xC0000000 | 0x0256)
+#define NT_STATUS_PATH_NOT_COVERED NT_STATUS(0xC0000000 | 0x0257)
+#define NT_STATUS_NO_CALLBACK_ACTIVE NT_STATUS(0xC0000000 | 0x0258)
+#define NT_STATUS_LICENSE_QUOTA_EXCEEDED NT_STATUS(0xC0000000 | 0x0259)
+#define NT_STATUS_PWD_TOO_SHORT NT_STATUS(0xC0000000 | 0x025a)
+#define NT_STATUS_PWD_TOO_RECENT NT_STATUS(0xC0000000 | 0x025b)
+#define NT_STATUS_PWD_HISTORY_CONFLICT NT_STATUS(0xC0000000 | 0x025c)
+#define NT_STATUS_PLUGPLAY_NO_DEVICE NT_STATUS(0xC0000000 | 0x025e)
+#define NT_STATUS_UNSUPPORTED_COMPRESSION NT_STATUS(0xC0000000 | 0x025f)
+#define NT_STATUS_INVALID_HW_PROFILE NT_STATUS(0xC0000000 | 0x0260)
+#define NT_STATUS_INVALID_PLUGPLAY_DEVICE_PATH NT_STATUS(0xC0000000 | 0x0261)
+#define NT_STATUS_DRIVER_ORDINAL_NOT_FOUND NT_STATUS(0xC0000000 | 0x0262)
+#define NT_STATUS_DRIVER_ENTRYPOINT_NOT_FOUND NT_STATUS(0xC0000000 | 0x0263)
+#define NT_STATUS_RESOURCE_NOT_OWNED NT_STATUS(0xC0000000 | 0x0264)
+#define NT_STATUS_TOO_MANY_LINKS NT_STATUS(0xC0000000 | 0x0265)
+#define NT_STATUS_QUOTA_LIST_INCONSISTENT NT_STATUS(0xC0000000 | 0x0266)
+#define NT_STATUS_FILE_IS_OFFLINE NT_STATUS(0xC0000000 | 0x0267)
+#define NT_STATUS_NOT_A_REPARSE_POINT NT_STATUS(0xC0000000 | 0x0275)
+#define NT_STATUS_NO_SUCH_JOB NT_STATUS(0xC0000000 | 0xEDE) /* scheduler */
+
+#endif /* _NTERR_H */
diff --git a/source/include/ntioctl.h b/source/include/ntioctl.h
new file mode 100644
index 00000000000..9814c88e5e5
--- /dev/null
+++ b/source/include/ntioctl.h
@@ -0,0 +1,87 @@
+/*
+ Unix SMB/CIFS implementation.
+ NT ioctl code constants
+ 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 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.
+*/
+
+/*
+ I'm guessing we will need to support a bunch of these eventually. For now
+ we only need the sparse flag
+*/
+
+#ifndef _NTIOCTL_H
+#define _NTIOCTL_H
+
+/* IOCTL information */
+/* List of ioctl function codes that look to be of interest to remote clients like this. */
+/* Need to do some experimentation to make sure they all work remotely. */
+/* Some of the following such as the encryption/compression ones would be */
+/* invoked from tools via a specialized hook into the VFS rather than via the */
+/* standard vfs entry points */
+#define FSCTL_REQUEST_OPLOCK_LEVEL_1 0x00090000
+#define FSCTL_REQUEST_OPLOCK_LEVEL_2 0x00090004
+#define FSCTL_REQUEST_BATCH_OPLOCK 0x00090008
+#define FSCTL_LOCK_VOLUME 0x00090018
+#define FSCTL_UNLOCK_VOLUME 0x0009001C
+#define FSCTL_GET_COMPRESSION 0x0009003C
+#define FSCTL_SET_COMPRESSION 0x0009C040
+#define FSCTL_REQUEST_FILTER_OPLOCK 0x0009008C
+#define FSCTL_FIND_FILES_BY_SID 0x0009008F
+#define FSCTL_FILESYS_GET_STATISTICS 0x00090090
+#define FSCTL_SET_REPARSE_POINT 0x000900A4
+#define FSCTL_GET_REPARSE_POINT 0x000900A8
+#define FSCTL_DELETE_REPARSE_POINT 0x000900AC
+#define FSCTL_0x000900C0 0x000900C0
+#define FSCTL_SET_SPARSE 0x000900C4
+#define FSCTL_SET_ZERO_DATA 0x000900C8
+#define FSCTL_SET_ENCRYPTION 0x000900D7
+#define FSCTL_ENCRYPTION_FSCTL_IO 0x000900DB
+#define FSCTL_WRITE_RAW_ENCRYPTED 0x000900DF
+#define FSCTL_READ_RAW_ENCRYPTED 0x000900E3
+#define FSCTL_SIS_COPYFILE 0x00090100
+#define FSCTL_SIS_LINK_FILES 0x0009C104
+
+#define FSCTL_GET_SHADOW_COPY_DATA 0x00144064 /* KJC -- Shadow Copy information */
+
+#if 0
+#define FSCTL_SECURITY_ID_CHECK
+#define FSCTL_DISMOUNT_VOLUME
+#define FSCTL_GET_NTFS_FILE_RECORD
+#define FSCTL_ALLOW_EXTENDED_DASD_IO
+#define FSCTL_RECALL_FILE
+#define FSCTL_QUERY_ALLOCATED_RANGES
+
+#endif
+
+#define IO_REPARSE_TAG_MOUNT_POINT 0xA0000003
+#define IO_REPARSE_TAG_HSM 0xC0000004
+#define IO_REPARSE_TAG_SIS 0x80000007
+
+
+/* For FSCTL_GET_SHADOW_COPY_DATA ...*/
+typedef char SHADOW_COPY_LABEL[25];
+
+typedef struct shadow_copy_data {
+ TALLOC_CTX *mem_ctx;
+ /* Total number of shadow volumes currently mounted */
+ uint32 num_volumes;
+ /* Concatenated list of labels */
+ SHADOW_COPY_LABEL *labels;
+} SHADOW_COPY_DATA;
+
+
+#endif /* _NTIOCTL_H */
diff --git a/source/include/ntlmssp.h b/source/include/ntlmssp.h
new file mode 100644
index 00000000000..24ac7967615
--- /dev/null
+++ b/source/include/ntlmssp.h
@@ -0,0 +1,169 @@
+/*
+ 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 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.
+*/
+
+/* NTLMSSP mode */
+enum NTLMSSP_ROLE
+{
+ NTLMSSP_SERVER,
+ NTLMSSP_CLIENT
+};
+
+/* NTLMSSP message types */
+enum NTLM_MESSAGE_TYPE
+{
+ NTLMSSP_INITIAL = 0 /* samba internal state */,
+ NTLMSSP_NEGOTIATE = 1,
+ NTLMSSP_CHALLENGE = 2,
+ NTLMSSP_AUTH = 3,
+ NTLMSSP_UNKNOWN = 4
+};
+
+/* 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_NEGOTIATE_128 0x20000000 /* 128-bit encryption */
+#define NTLMSSP_NEGOTIATE_KEY_EXCH 0x40000000
+#define NTLMSSP_NEGOTIATE_080000000 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
+
+typedef struct ntlmssp_state
+{
+ TALLOC_CTX *mem_ctx;
+ unsigned int ref_count;
+ enum NTLMSSP_ROLE role;
+ enum server_types server_role;
+ uint32 expected_state;
+
+ BOOL unicode;
+ BOOL use_ntlmv2;
+ char *user;
+ char *domain;
+ char *workstation;
+ char *password;
+ 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 neg_flags; /* the current state of negotiation with the NTLMSSP partner */
+
+ void *auth_context;
+
+ /**
+ * Callback to get the 'challenge' used for NTLM authentication.
+ *
+ * @param ntlmssp_state This structure
+ * @return 8 bytes of challnege data, determined by the server to be the challenge for NTLM authentication
+ *
+ */
+ const uint8 *(*get_challenge)(const struct ntlmssp_state *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 ntlmssp_state *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 challange 8 bytes of data, agreed by the client and server to be the effective challenge for NTLM2 authentication
+ *
+ */
+ NTSTATUS (*set_challenge)(struct ntlmssp_state *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 ntlmssp_state *ntlmssp_state, DATA_BLOB *nt_session_key, DATA_BLOB *lm_session_key);
+
+ const char *(*get_global_myname)(void);
+ const char *(*get_domain)(void);
+
+ /* SMB Signing */
+
+ uint32 ntlmssp_seq_num;
+
+ /* ntlmv2 */
+ unsigned char send_sign_const[16];
+ unsigned char send_seal_const[16];
+ unsigned char recv_sign_const[16];
+ unsigned char recv_seal_const[16];
+
+ unsigned char send_sign_hash[258];
+ unsigned char send_seal_hash[258];
+ unsigned char recv_sign_hash[258];
+ unsigned char recv_seal_hash[258];
+
+ /* ntlmv1 */
+ unsigned char ntlmssp_hash[258];
+
+ /* it turns out that we don't always get the
+ response in at the time we want to process it.
+ Store it here, until we need it */
+ DATA_BLOB stored_response;
+
+} NTLMSSP_STATE;
+
diff --git a/source/include/ntquotas.h b/source/include/ntquotas.h
new file mode 100644
index 00000000000..dac1173770b
--- /dev/null
+++ b/source/include/ntquotas.h
@@ -0,0 +1,97 @@
+/*
+ Unix SMB/CIFS implementation.
+ NT QUOTA code constants
+ 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 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 _NTQUOTAS_H
+#define _NTQUOTAS_H
+
+/*
+ * details for Quota Flags:
+ *
+ * 0x20 Log Limit: log if the user exceeds his Hard Quota
+ * 0x10 Log Warn: log if the user exceeds his Soft Quota
+ * 0x02 Deny Disk: deny disk access when the user exceeds his Hard Quota
+ * 0x01 Enable Quotas: enable quota for this fs
+ *
+ */
+
+#define QUOTAS_ENABLED 0x0001
+#define QUOTAS_DENY_DISK 0x0002
+#define QUOTAS_LOG_VIOLATIONS 0x0004
+#define CONTENT_INDEX_DISABLED 0x0008
+#define QUOTAS_LOG_THRESHOLD 0x0010
+#define QUOTAS_LOG_LIMIT 0x0020
+#define LOG_VOLUME_THRESHOLD 0x0040
+#define LOG_VOLUME_LIMIT 0x0080
+#define QUOTAS_INCOMPLETE 0x0100
+#define QUOTAS_REBUILDING 0x0200
+#define QUOTAS_0400 0x0400
+#define QUOTAS_0800 0x0800
+#define QUOTAS_1000 0x1000
+#define QUOTAS_2000 0x2000
+#define QUOTAS_4000 0x4000
+#define QUOTAS_8000 0x8000
+
+#define SMB_NTQUOTAS_NO_LIMIT ((SMB_BIG_UINT)(-1))
+#define SMB_NTQUOTAS_NO_ENTRY ((SMB_BIG_UINT)(-2))
+#define SMB_NTQUOTAS_NO_SPACE ((SMB_BIG_UINT)(0))
+#define SMB_NTQUOTAS_1_B (SMB_BIG_UINT)0x0000000000000001
+#define SMB_NTQUOTAS_1KB (SMB_BIG_UINT)0x0000000000000400
+#define SMB_NTQUOTAS_1MB (SMB_BIG_UINT)0x0000000000100000
+#define SMB_NTQUOTAS_1GB (SMB_BIG_UINT)0x0000000040000000
+#define SMB_NTQUOTAS_1TB (SMB_BIG_UINT)0x0000010000000000
+#define SMB_NTQUOTAS_1PB (SMB_BIG_UINT)0x0004000000000000
+#define SMB_NTQUOTAS_1EB (SMB_BIG_UINT)0x1000000000000000
+
+enum SMB_QUOTA_TYPE {
+ SMB_INVALID_QUOTA_TYPE = -1,
+ SMB_USER_FS_QUOTA_TYPE = 1,
+ SMB_USER_QUOTA_TYPE = 2,
+ SMB_GROUP_FS_QUOTA_TYPE = 3,/* not used yet */
+ SMB_GROUP_QUOTA_TYPE = 4 /* not in use yet, maybe for disk_free queries */
+};
+
+typedef struct _SMB_NTQUOTA_STRUCT {
+ enum SMB_QUOTA_TYPE qtype;
+ SMB_BIG_UINT usedspace;
+ SMB_BIG_UINT softlim;
+ SMB_BIG_UINT hardlim;
+ uint32 qflags;
+ DOM_SID sid;
+} SMB_NTQUOTA_STRUCT;
+
+typedef struct _SMB_NTQUOTA_LIST {
+ struct _SMB_NTQUOTA_LIST *prev,*next;
+ TALLOC_CTX *mem_ctx;
+ uid_t uid;
+ SMB_NTQUOTA_STRUCT *quotas;
+} SMB_NTQUOTA_LIST;
+
+typedef struct _SMB_NTQUOTA_HANDLE {
+ BOOL valid;
+ SMB_NTQUOTA_LIST *quota_list;
+ SMB_NTQUOTA_LIST *tmp_list;
+} SMB_NTQUOTA_HANDLE;
+
+#define CHECK_NTQUOTA_HANDLE_OK(fsp,conn) (FNUM_OK(fsp,conn) &&\
+ (fsp)->fake_file_handle &&\
+ ((fsp)->fake_file_handle->type == FAKE_FILE_TYPE_QUOTA) &&\
+ (fsp)->fake_file_handle->pd)
+
+#endif /*_NTQUOTAS_H */
diff --git a/source/include/passdb.h b/source/include/passdb.h
new file mode 100644
index 00000000000..baf0e23a20c
--- /dev/null
+++ b/source/include/passdb.h
@@ -0,0 +1,573 @@
+/*
+ Unix SMB/CIFS implementation.
+ passdb structures and parameters
+ Copyright (C) Gerald Carter 2001
+ Copyright (C) Luke Kenneth Casson Leighton 1998 - 2000
+ Copyright (C) Andrew Bartlett 2002
+ Copyright (C) Simo Sorce 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 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 _PASSDB_H
+#define _PASSDB_H
+
+
+/*
+ * fields_present flags meanings
+ * same names as found in samba4 idl files
+ */
+
+#define ACCT_USERNAME 0x00000001
+#define ACCT_FULL_NAME 0x00000002
+#define ACCT_RID 0x00000004
+#define ACCT_PRIMARY_GID 0x00000008
+#define ACCT_ADMIN_DESC 0x00000010
+#define ACCT_DESCRIPTION 0x00000020
+#define ACCT_HOME_DIR 0x00000040
+#define ACCT_HOME_DRIVE 0x00000080
+#define ACCT_LOGON_SCRIPT 0x00000100
+#define ACCT_PROFILE 0x00000200
+#define ACCT_WORKSTATIONS 0x00000400
+#define ACCT_LAST_LOGON 0x00000800
+#define ACCT_LAST_LOGOFF 0x00001000
+#define ACCT_LOGON_HOURS 0x00002000
+#define ACCT_BAD_PWD_COUNT 0x00004000
+#define ACCT_NUM_LOGONS 0x00008000
+#define ACCT_ALLOW_PWD_CHANGE 0x00010000
+#define ACCT_FORCE_PWD_CHANGE 0x00020000
+#define ACCT_LAST_PWD_CHANGE 0x00040000
+#define ACCT_EXPIRY 0x00080000
+#define ACCT_FLAGS 0x00100000
+#define ACCT_CALLBACK 0x00200000
+#define ACCT_COUNTRY_CODE 0x00400000
+#define ACCT_CODE_PAGE 0x00800000
+#define ACCT_NT_PWD_SET 0x01000000
+#define ACCT_LM_PWD_SET 0x02000000
+#define ACCT_PRIVATEDATA 0x04000000
+#define ACCT_EXPIRED_FLAG 0x08000000
+#define ACCT_SEC_DESC 0x10000000
+#define ACCT_OWF_PWD 0x20000000
+
+/*
+ * bit flags representing initialized fields in SAM_ACCOUNT
+ */
+enum pdb_elements {
+ PDB_UNINIT,
+ PDB_SMBHOME,
+ PDB_PROFILE,
+ PDB_DRIVE,
+ PDB_LOGONSCRIPT,
+ PDB_LOGONTIME,
+ PDB_LOGOFFTIME,
+ PDB_KICKOFFTIME,
+ PDB_BAD_PASSWORD_TIME,
+ PDB_CANCHANGETIME,
+ PDB_MUSTCHANGETIME,
+ PDB_PLAINTEXT_PW,
+ PDB_USERNAME,
+ PDB_FULLNAME,
+ PDB_DOMAIN,
+ PDB_NTUSERNAME,
+ PDB_HOURSLEN,
+ PDB_LOGONDIVS,
+ PDB_USERSID,
+ PDB_GROUPSID,
+ PDB_ACCTCTRL,
+ PDB_PASSLASTSET,
+ PDB_UNIXHOMEDIR,
+ PDB_ACCTDESC,
+ PDB_WORKSTATIONS,
+ PDB_UNKNOWNSTR,
+ PDB_MUNGEDDIAL,
+ PDB_HOURS,
+ PDB_FIELDS_PRESENT,
+ PDB_BAD_PASSWORD_COUNT,
+ PDB_LOGON_COUNT,
+ PDB_UNKNOWN6,
+ PDB_LMPASSWD,
+ PDB_NTPASSWD,
+ PDB_BACKEND_PRIVATE_DATA,
+
+ /* this must be the last element */
+ PDB_COUNT
+};
+
+enum pdb_group_elements {
+ PDB_GROUP_NAME,
+ PDB_GROUP_SID,
+ PDB_GROUP_SID_NAME_USE,
+ PDB_GROUP_MEMBERS,
+
+ /* this must be the last element */
+ PDB_GROUP_COUNT
+};
+
+enum pdb_trust_passwd_elements {
+ PDB_TRUST_PASS,
+ PDB_TRUST_SID,
+ PDB_TRUST_NAME,
+ PDB_TRUST_MODTIME,
+ PDB_TRUST_FLAGS,
+
+ PDB_TRUST_COUNT
+};
+
+enum pdb_value_state {
+ PDB_DEFAULT=0,
+ PDB_SET,
+ PDB_CHANGED
+};
+
+#define IS_SAM_SET(x, flag) (pdb_get_init_flags(x, flag) == PDB_SET)
+#define IS_SAM_CHANGED(x, flag) (pdb_get_init_flags(x, flag) == PDB_CHANGED)
+#define IS_SAM_DEFAULT(x, flag) (pdb_get_init_flags(x, flag) == PDB_DEFAULT)
+
+/* cache for bad password lockout data, to be used on replicated SAMs */
+typedef struct logon_cache_struct
+{
+ time_t entry_timestamp;
+ uint16 acct_ctrl;
+ uint16 bad_password_count;
+ time_t bad_password_time;
+} LOGIN_CACHE;
+
+typedef struct sam_passwd
+{
+ TALLOC_CTX *mem_ctx;
+
+ void (*free_fn)(struct sam_passwd **);
+
+ struct pdb_methods *methods;
+
+ struct user_data {
+ /* initialization flags */
+ struct bitmap *change_flags;
+ struct bitmap *set_flags;
+
+ time_t logon_time; /* logon time */
+ time_t logoff_time; /* logoff time */
+ time_t kickoff_time; /* kickoff time */
+ time_t bad_password_time; /* last bad password entered */
+ time_t pass_last_set_time; /* password last set time */
+ time_t pass_can_change_time; /* password can change time */
+ time_t pass_must_change_time; /* password must change time */
+
+ const char * username; /* UNIX username string */
+ const char * domain; /* Windows Domain name */
+ const char * nt_username; /* Windows username string */
+ const char * full_name; /* user's full name string */
+ const char * unix_home_dir; /* UNIX home directory string */
+ const char * home_dir; /* home directory string */
+ const char * dir_drive; /* home directory drive string */
+ const char * logon_script; /* logon script string */
+ const char * profile_path; /* profile path string */
+ const char * acct_desc ; /* user description string */
+ const char * workstations; /* login from workstations string */
+ const char * unknown_str ; /* don't know what this is, yet. */
+ const char * munged_dial ; /* munged path name and dial-back tel number */
+
+ DOM_SID user_sid; /* Primary User SID */
+ DOM_SID group_sid; /* Primary Group SID */
+
+ DATA_BLOB lm_pw; /* .data is Null if no password */
+ DATA_BLOB nt_pw; /* .data is Null if no password */
+ char* plaintext_pw; /* is Null if not available */
+
+ uint16 acct_ctrl; /* account info (ACB_xxxx bit-mask) */
+ uint32 fields_present; /* 0x00ff ffff */
+
+ uint16 logon_divs; /* 168 - number of hours in a week */
+ uint32 hours_len; /* normally 21 bytes */
+ uint8 hours[MAX_HOURS_LEN];
+
+ /* Was unknown_5. */
+ uint16 bad_password_count;
+ uint16 logon_count;
+
+ uint32 unknown_6; /* 0x0000 04ec */
+ /* a tag for who added the private methods */
+ const struct pdb_methods *backend_private_methods;
+ void *backend_private_data;
+ void (*backend_private_data_free_fn)(void **);
+ } private;
+
+ /* Lets see if the remaining code can get the hint that you
+ are meant to use the pdb_...() functions. */
+
+} SAM_ACCOUNT;
+
+typedef struct sam_group {
+ TALLOC_CTX *mem_ctx;
+
+ void (*free_fn)(struct sam_group **);
+
+ struct pdb_methods *methods;
+
+ struct group_data {
+ /* initialization flags */
+ struct bitmap *change_flags;
+ struct bitmap *set_flags;
+
+ const char *name; /* Windows group name string */
+
+ DOM_SID sid; /* Group SID */
+ enum SID_NAME_USE sid_name_use; /* Group type */
+
+ uint32 mem_num; /* Number of member SIDs */
+ DOM_SID *members; /* SID array */
+ } private;
+
+} SAM_GROUP;
+
+
+typedef struct _GROUP_INFO {
+ struct pdb_methods *methods;
+ DOM_SID sid;
+ enum SID_NAME_USE sid_name_use;
+ fstring nt_name;
+ fstring comment;
+} GROUP_INFO;
+
+struct acct_info
+{
+ fstring acct_name; /* account name */
+ fstring acct_desc; /* account name */
+ uint32 rid; /* domain-relative RID */
+};
+
+typedef struct sam_trust_passwd {
+ TALLOC_CTX *mem_ctx;
+
+ void (*free_fn)(struct sam_trust_passwd **);
+
+ struct pdb_methods *methods;
+
+ struct trust_passwd_data {
+ uint16 flags; /* flags */
+ size_t uni_name_len; /* unicode name length */
+ smb_ucs2_t uni_name[32]; /* unicode domain name */
+ fstring pass; /* trust password */
+ time_t mod_time; /* last change time */
+ DOM_SID domain_sid; /* trusted domain sid */
+ } private;
+
+} SAM_TRUST_PASSWD;
+
+
+
+/*****************************************************************
+ Functions to be implemented by the new (v2) passdb API
+****************************************************************/
+
+/*
+ * This next constant specifies the version number of the PASSDB interface
+ * this SAMBA will load. Increment this if *ANY* changes are made to the interface.
+ */
+
+#define PASSDB_INTERFACE_VERSION 7
+
+typedef struct pdb_context
+{
+ struct pdb_methods *pdb_methods;
+ struct pdb_methods *pwent_methods;
+
+ /* These functions are wrappers for the functions listed above.
+ They may do extra things like re-reading a SAM_ACCOUNT on update */
+
+ NTSTATUS (*pdb_setsampwent)(struct pdb_context *, BOOL update);
+
+ void (*pdb_endsampwent)(struct pdb_context *);
+
+ NTSTATUS (*pdb_getsampwent)(struct pdb_context *, SAM_ACCOUNT *user);
+
+ NTSTATUS (*pdb_getsampwnam)(struct pdb_context *, SAM_ACCOUNT *sam_acct, const char *username);
+
+ NTSTATUS (*pdb_getsampwsid)(struct pdb_context *, SAM_ACCOUNT *sam_acct, const DOM_SID *sid);
+
+ NTSTATUS (*pdb_add_sam_account)(struct pdb_context *, SAM_ACCOUNT *sampass);
+
+ NTSTATUS (*pdb_update_sam_account)(struct pdb_context *, SAM_ACCOUNT *sampass);
+
+ NTSTATUS (*pdb_delete_sam_account)(struct pdb_context *, SAM_ACCOUNT *username);
+
+ /* group mapping functions: to be removed */
+
+ NTSTATUS (*pdb_getgrsid)(struct pdb_context *context, GROUP_MAP *map, DOM_SID sid);
+
+ NTSTATUS (*pdb_getgrgid)(struct pdb_context *context, GROUP_MAP *map, gid_t gid);
+
+ NTSTATUS (*pdb_getgrnam)(struct pdb_context *context, GROUP_MAP *map, const char *name);
+
+ NTSTATUS (*pdb_add_group_mapping_entry)(struct pdb_context *context,
+ GROUP_MAP *map);
+
+ NTSTATUS (*pdb_update_group_mapping_entry)(struct pdb_context *context,
+ GROUP_MAP *map);
+
+ NTSTATUS (*pdb_delete_group_mapping_entry)(struct pdb_context *context,
+ DOM_SID sid);
+
+ NTSTATUS (*pdb_enum_group_mapping)(struct pdb_context *context,
+ enum SID_NAME_USE sid_name_use,
+ GROUP_MAP **rmap, int *num_entries,
+ BOOL unix_only);
+
+ NTSTATUS (*pdb_find_alias)(struct pdb_context *context,
+ const char *name, DOM_SID *sid);
+
+ NTSTATUS (*pdb_create_alias)(struct pdb_context *context,
+ const char *name, uint32 *rid);
+
+ NTSTATUS (*pdb_delete_alias)(struct pdb_context *context,
+ const DOM_SID *sid);
+
+ NTSTATUS (*pdb_enum_aliases)(struct pdb_context *context,
+ const DOM_SID *domain_sid,
+ uint32 start_idx, uint32 num_entries,
+ uint32 *num_aliases,
+ struct acct_info **aliases);
+
+ NTSTATUS (*pdb_get_aliasinfo)(struct pdb_context *context,
+ const DOM_SID *sid,
+ struct acct_info *info);
+
+ NTSTATUS (*pdb_set_aliasinfo)(struct pdb_context *context,
+ const DOM_SID *sid,
+ struct acct_info *info);
+
+ NTSTATUS (*pdb_add_aliasmem)(struct pdb_context *context,
+ const DOM_SID *alias,
+ const DOM_SID *member);
+
+ NTSTATUS (*pdb_del_aliasmem)(struct pdb_context *context,
+ const DOM_SID *alias,
+ const DOM_SID *member);
+
+ NTSTATUS (*pdb_enum_aliasmem)(struct pdb_context *context,
+ const DOM_SID *alias,
+ DOM_SID **members, int *num_members);
+
+ NTSTATUS (*pdb_enum_alias_memberships)(struct pdb_context *context,
+ const DOM_SID *alias,
+ DOM_SID **aliases,
+ int *num);
+
+ /* group functions */
+
+ NTSTATUS (*pdb_get_group_info_by_sid)(struct pdb_context *context, GROUP_INFO *info, const DOM_SID *group);
+
+ NTSTATUS (*pdb_get_group_list)(struct pdb_context *context, GROUP_INFO **info, const enum SID_NAME_USE sid_name_use, int *num_groups);
+
+ NTSTATUS (*pdb_get_group_sids)(struct pdb_context *context, const DOM_SID *group, DOM_SID **members, int *num_members);
+
+ NTSTATUS (*pdb_add_group)(struct pdb_context *context, const SAM_GROUP *group);
+
+ NTSTATUS (*pdb_update_group)(struct pdb_context *context, const SAM_GROUP *group);
+
+ NTSTATUS (*pdb_delete_group)(struct pdb_context *context, const DOM_SID *group);
+
+ NTSTATUS (*pdb_add_sid_to_group)(struct pdb_context *context, const DOM_SID *group, const DOM_SID *member);
+
+ NTSTATUS (*pdb_remove_sid_from_group)(struct pdb_context *context, const DOM_SID *group, const DOM_SID *member);
+
+ NTSTATUS (*pdb_get_group_info_by_name)(struct pdb_context *context, GROUP_INFO *info, const char *name);
+
+ NTSTATUS (*pdb_get_group_info_by_nt_name)(struct pdb_context *context, GROUP_INFO *info, const char *nt_name);
+
+ NTSTATUS (*pdb_get_group_uids)(struct pdb_context *context, const DOM_SID *group, uid_t **members, int *num_members);
+
+ /* trust password functions */
+
+ NTSTATUS (*pdb_settrustpwent)(struct pdb_context *context);
+
+ NTSTATUS (*pdb_gettrustpwent)(struct pdb_context *context, SAM_TRUST_PASSWD *trust);
+
+ NTSTATUS (*pdb_gettrustpwnam)(struct pdb_context *context, SAM_TRUST_PASSWD *trust, const char *dom_name);
+
+ NTSTATUS (*pdb_gettrustpwsid)(struct pdb_context *context, SAM_TRUST_PASSWD *trust, const DOM_SID *sid);
+
+ NTSTATUS (*pdb_add_trust_passwd)(struct pdb_context *context, SAM_TRUST_PASSWD* trust);
+
+ NTSTATUS (*pdb_update_trust_passwd)(struct pdb_context *context, SAM_TRUST_PASSWD* trust);
+
+ NTSTATUS (*pdb_delete_trust_passwd)(struct pdb_context *context, SAM_TRUST_PASSWD* trust);
+
+ /* privileges functions */
+
+ NTSTATUS (*pdb_add_sid_to_privilege)(struct pdb_context *context, const char *priv_name, const DOM_SID *sid);
+
+ NTSTATUS (*pdb_remove_sid_from_privilege)(struct pdb_context *context, const char *priv_name, const DOM_SID *sid);
+
+ NTSTATUS (*pdb_get_privilege_set)(struct pdb_context *context, DOM_SID *user_sids, int num_sids, PRIVILEGE_SET *privs);
+
+ NTSTATUS (*pdb_get_privilege_entry)(struct pdb_context *context, const char *privname, char **sid_list);
+
+ void (*free_fn)(struct pdb_context **);
+
+ TALLOC_CTX *mem_ctx;
+
+} PDB_CONTEXT;
+
+typedef struct pdb_methods
+{
+ const char *name; /* What name got this module */
+ struct pdb_context *parent;
+
+ /* Use macros from dlinklist.h on these two */
+ struct pdb_methods *next;
+ struct pdb_methods *prev;
+
+ NTSTATUS (*setsampwent)(struct pdb_methods *, BOOL update);
+
+ void (*endsampwent)(struct pdb_methods *);
+
+ NTSTATUS (*getsampwent)(struct pdb_methods *, SAM_ACCOUNT *user);
+
+ NTSTATUS (*getsampwnam)(struct pdb_methods *, SAM_ACCOUNT *sam_acct, const char *username);
+
+ NTSTATUS (*getsampwsid)(struct pdb_methods *, SAM_ACCOUNT *sam_acct, const DOM_SID *sid);
+
+ NTSTATUS (*add_sam_account)(struct pdb_methods *, SAM_ACCOUNT *sampass);
+
+ NTSTATUS (*update_sam_account)(struct pdb_methods *, SAM_ACCOUNT *sampass);
+
+ NTSTATUS (*delete_sam_account)(struct pdb_methods *, SAM_ACCOUNT *username);
+
+ /* group mapping functions: to be removed */
+
+ NTSTATUS (*getgrsid)(struct pdb_methods *methods, GROUP_MAP *map, DOM_SID sid);
+
+ NTSTATUS (*getgrgid)(struct pdb_methods *methods, GROUP_MAP *map, gid_t gid);
+
+ NTSTATUS (*getgrnam)(struct pdb_methods *methods, GROUP_MAP *map, const char *name);
+
+ NTSTATUS (*add_group_mapping_entry)(struct pdb_methods *methods,
+ GROUP_MAP *map);
+
+ NTSTATUS (*update_group_mapping_entry)(struct pdb_methods *methods,
+ GROUP_MAP *map);
+
+ NTSTATUS (*delete_group_mapping_entry)(struct pdb_methods *methods,
+ DOM_SID sid);
+
+ NTSTATUS (*enum_group_mapping)(struct pdb_methods *methods,
+ enum SID_NAME_USE sid_name_use,
+ GROUP_MAP **rmap, int *num_entries,
+ BOOL unix_only);
+
+ NTSTATUS (*find_alias)(struct pdb_methods *methods,
+ const char *name, DOM_SID *sid);
+
+ NTSTATUS (*create_alias)(struct pdb_methods *methods,
+ const char *name, uint32 *rid);
+
+ NTSTATUS (*delete_alias)(struct pdb_methods *methods,
+ const DOM_SID *sid);
+
+ NTSTATUS (*enum_aliases)(struct pdb_methods *methods,
+ const DOM_SID *domain_sid,
+ uint32 start_idx, uint32 max_entries,
+ uint32 *num_aliases, struct acct_info **info);
+
+ NTSTATUS (*get_aliasinfo)(struct pdb_methods *methods,
+ const DOM_SID *sid,
+ struct acct_info *info);
+
+ NTSTATUS (*set_aliasinfo)(struct pdb_methods *methods,
+ const DOM_SID *sid,
+ struct acct_info *info);
+
+ NTSTATUS (*add_aliasmem)(struct pdb_methods *methods,
+ const DOM_SID *alias, const DOM_SID *member);
+ NTSTATUS (*del_aliasmem)(struct pdb_methods *methods,
+ const DOM_SID *alias, const DOM_SID *member);
+ NTSTATUS (*enum_aliasmem)(struct pdb_methods *methods,
+ const DOM_SID *alias, DOM_SID **members,
+ int *num_members);
+ NTSTATUS (*enum_alias_memberships)(struct pdb_methods *methods,
+ const DOM_SID *sid,
+ DOM_SID **aliases, int *num);
+
+ /* group functions */
+
+ NTSTATUS (*get_group_info_by_sid)(struct pdb_methods *methods, GROUP_INFO *info, const DOM_SID *group);
+
+ NTSTATUS (*get_group_list)(struct pdb_methods *methods, GROUP_INFO **info, const enum SID_NAME_USE sid_name_use, int *num_groups);
+
+ NTSTATUS (*get_group_sids)(struct pdb_methods *methods, const DOM_SID *group, DOM_SID **members, int *num_members);
+
+ NTSTATUS (*add_group)(struct pdb_methods *methods, const SAM_GROUP *group);
+
+ NTSTATUS (*update_group)(struct pdb_methods *methods, const SAM_GROUP *group);
+
+ NTSTATUS (*delete_group)(struct pdb_methods *methods, const DOM_SID *group);
+
+ NTSTATUS (*add_sid_to_group)(struct pdb_methods *methods, const DOM_SID *group, const DOM_SID *member);
+
+ NTSTATUS (*remove_sid_from_group)(struct pdb_methods *methods, const DOM_SID *group, const DOM_SID *member);
+
+ NTSTATUS (*get_group_info_by_name)(struct pdb_methods *methods, GROUP_INFO *info, const char *name);
+
+ NTSTATUS (*get_group_info_by_nt_name)(struct pdb_methods *methods, GROUP_INFO *info, const char *nt_name);
+
+ NTSTATUS (*get_group_uids)(struct pdb_methods *methods, const DOM_SID *group, uid_t **members, int *num_members);
+
+ void *private_data; /* Private data of some kind */
+
+ void (*free_private_data)(void **);
+
+ /* trust password functions */
+
+ NTSTATUS (*settrustpwent)(struct pdb_methods *methods);
+
+ NTSTATUS (*gettrustpwent)(struct pdb_methods *methods, SAM_TRUST_PASSWD *trust);
+
+ NTSTATUS (*gettrustpwnam)(struct pdb_methods *methods, SAM_TRUST_PASSWD *trust, const char *name);
+
+ NTSTATUS (*gettrustpwsid)(struct pdb_methods *methods, SAM_TRUST_PASSWD *trust, const DOM_SID *sid);
+
+ NTSTATUS (*add_trust_passwd)(struct pdb_methods *methods, const SAM_TRUST_PASSWD* trust);
+
+ NTSTATUS (*update_trust_passwd)(struct pdb_methods *methods, const SAM_TRUST_PASSWD* trust);
+
+ NTSTATUS (*delete_trust_passwd)(struct pdb_methods *methods, const SAM_TRUST_PASSWD* trust);
+
+ /* privileges functions */
+
+ NTSTATUS (*add_sid_to_privilege)(struct pdb_methods *methods, const char *priv_name, const DOM_SID *sid);
+
+ NTSTATUS (*remove_sid_from_privilege)(struct pdb_methods *methods, const char *priv_name, const DOM_SID *sid);
+
+ NTSTATUS (*get_privilege_set)(struct pdb_methods *methods, DOM_SID *user_sids, int num_sids, PRIVILEGE_SET *privs);
+
+ NTSTATUS (*get_privilege_entry)(struct pdb_methods *methods, const char *privname, char **sid_list);
+
+} PDB_METHODS;
+
+typedef NTSTATUS (*pdb_init_function)(struct pdb_context *,
+ struct pdb_methods **,
+ const char *);
+
+struct pdb_init_function_entry {
+ const char *name;
+ /* Function to create a member of the pdb_methods list */
+ pdb_init_function init;
+ struct pdb_init_function_entry *prev, *next;
+};
+
+enum sql_search_field { SQL_SEARCH_NONE = 0, SQL_SEARCH_USER_SID = 1, SQL_SEARCH_USER_NAME = 2};
+
+#endif /* _PASSDB_H */
diff --git a/source/include/popt_common.h b/source/include/popt_common.h
new file mode 100644
index 00000000000..6db30fbc0ac
--- /dev/null
+++ b/source/include/popt_common.h
@@ -0,0 +1,49 @@
+/*
+ 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 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 _POPT_COMMON_H
+#define _POPT_COMMON_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[];
+
+#ifndef POPT_TABLEEND
+#define POPT_TABLEEND { NULL, '\0', 0, 0, 0, NULL, NULL }
+#endif
+
+#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 },
+
+struct user_auth_info {
+ pstring username;
+ pstring password;
+ BOOL got_pass;
+ BOOL use_kerberos;
+ int signing_state;
+};
+
+extern struct user_auth_info cmdline_auth_info;
+
+#endif /* _POPT_COMMON_H */
diff --git a/source/include/printing.h b/source/include/printing.h
new file mode 100644
index 00000000000..bf7c61b251e
--- /dev/null
+++ b/source/include/printing.h
@@ -0,0 +1,91 @@
+#ifndef PRINTING_H_
+#define PRINTING_H_
+
+/*
+ Unix SMB/CIFS implementation.
+ printing definitions
+ Copyright (C) Andrew Tridgell 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 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.
+*/
+
+/*
+ This file defines the low-level printing system interfaces used by the
+ SAMBA printing subsystem.
+*/
+
+/* Information for print jobs */
+struct printjob {
+ pid_t pid; /* which process launched the job */
+ int sysjob; /* the system (lp) job number */
+ int fd; /* file descriptor of open file if open */
+ time_t starttime; /* when the job started spooling */
+ int status; /* the status of this job */
+ size_t size; /* the size of the job so far */
+ int page_count; /* then number of pages so far */
+ BOOL spooled; /* has it been sent to the spooler yet? */
+ BOOL smbjob; /* set if the job is a SMB job */
+ fstring filename; /* the filename used to spool the file */
+ fstring jobname; /* the job name given to us by the client */
+ fstring user; /* the user who started the job */
+ fstring queuename; /* service number of printer for this job */
+ NT_DEVICEMODE *nt_devmode;
+};
+
+/* Information for print interfaces */
+struct printif
+{
+ int (*queue_get)(int snum, print_queue_struct **q,
+ print_status_struct *status);
+ int (*queue_pause)(int snum);
+ int (*queue_resume)(int snum);
+ int (*job_delete)(int snum, struct printjob *pjob);
+ int (*job_pause)(int snum, struct printjob *pjob);
+ int (*job_resume)(int snum, struct printjob *pjob);
+ int (*job_submit)(int snum, struct printjob *pjob);
+};
+
+extern struct printif generic_printif;
+
+#ifdef HAVE_CUPS
+extern struct printif cups_printif;
+#endif /* HAVE_CUPS */
+
+/* PRINT_MAX_JOBID is now defined in local.h */
+#define UNIX_JOB_START PRINT_MAX_JOBID
+#define NEXT_JOBID(j) ((j+1) % PRINT_MAX_JOBID > 0 ? (j+1) % PRINT_MAX_JOBID : 1)
+
+#define MAX_CACHE_VALID_TIME 3600
+
+#define PRINT_SPOOL_PREFIX "smbprn."
+#define PRINT_DATABASE_VERSION 5
+
+/* There can be this many printing tdb's open, plus any locked ones. */
+#define MAX_PRINT_DBS_OPEN 1
+
+struct tdb_print_db {
+ struct tdb_print_db *next, *prev;
+ TDB_CONTEXT *tdb;
+ int ref_count;
+ fstring printer_name;
+};
+
+/*
+ * Used for print notify
+ */
+
+#define NOTIFY_PID_LIST_KEY "NOTIFY_PID_LIST"
+
+#endif /* PRINTING_H_ */
diff --git a/source/include/privileges.h b/source/include/privileges.h
new file mode 100644
index 00000000000..289afa234ec
--- /dev/null
+++ b/source/include/privileges.h
@@ -0,0 +1,99 @@
+/*
+ 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 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 PRIVILEGES_H
+#define PRIVILEGES_H
+
+#define PRIV_ALL_INDEX 30
+
+#define SE_NONE 0
+#define SE_ASSIGN_PRIMARY_TOKEN 1
+#define SE_CREATE_TOKEN 2
+#define SE_LOCK_MEMORY 3
+#define SE_INCREASE_QUOTA 4
+#define SE_UNSOLICITED_INPUT 5
+#define SE_MACHINE_ACCOUNT 6
+#define SE_TCB 7
+#define SE_SECURITY 8
+#define SE_TAKE_OWNERSHIP 9
+#define SE_LOAD_DRIVER 10
+#define SE_SYSTEM_PROFILE 11
+#define SE_SYSTEM_TIME 12
+#define SE_PROF_SINGLE_PROCESS 13
+#define SE_INC_BASE_PRIORITY 14
+#define SE_CREATE_PAGEFILE 15
+#define SE_CREATE_PERMANENT 16
+#define SE_BACKUP 17
+#define SE_RESTORE 18
+#define SE_SHUTDOWN 19
+#define SE_DEBUG 20
+#define SE_AUDIT 21
+#define SE_SYSTEM_ENVIRONMENT 22
+#define SE_CHANGE_NOTIFY 23
+#define SE_REMOTE_SHUTDOWN 24
+#define SE_UNDOCK 25
+#define SE_SYNC_AGENT 26
+#define SE_ENABLE_DELEGATION 27
+#define SE_PRINT_OPERATOR 28
+#define SE_ADD_USERS 29
+#define SE_ALL_PRIVS 0xffff
+
+#define PR_NONE 0x0000
+#define PR_LOG_ON_LOCALLY 0x0001
+#define PR_ACCESS_FROM_NETWORK 0x0002
+#define PR_LOG_ON_BATCH_JOB 0x0004
+#define PR_LOG_ON_SERVICE 0x0010
+
+#ifndef _BOOL
+typedef int BOOL;
+#define _BOOL /* So we don't typedef BOOL again in vfs.h */
+#endif
+
+typedef struct LUID
+{
+ uint32 low;
+ uint32 high;
+} LUID;
+
+typedef struct LUID_ATTR
+{
+ LUID luid;
+ uint32 attr;
+} LUID_ATTR;
+
+typedef struct privilege_set
+{
+ TALLOC_CTX *mem_ctx;
+ BOOL ext_ctx;
+ uint32 count;
+ uint32 control;
+ LUID_ATTR *set;
+} PRIVILEGE_SET;
+
+typedef struct _PRIVS {
+ uint32 se_priv;
+ const char *priv;
+ const char *description;
+} PRIVS;
+
+
+#endif /* PRIVILEGES_H */
diff --git a/source/include/pstring.h b/source/include/pstring.h
new file mode 100644
index 00000000000..92870e4cae5
--- /dev/null
+++ b/source/include/pstring.h
@@ -0,0 +1,36 @@
+/*
+ samba -- Unix SMB/CIFS implementation.
+ Safe standardized string types
+
+ Copyright (C) Andrew Tridgell 1992-2000
+ Copyright (C) John H Terpstra 1996-2000
+ Copyright (C) Luke Kenneth Casson Leighton 1996-2000
+ Copyright (C) Paul Ashton 1998-2000
+ 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 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 _PSTRING
+
+#define PSTRING_LEN 1024
+#define FSTRING_LEN 256
+
+typedef char pstring[PSTRING_LEN];
+typedef char fstring[FSTRING_LEN];
+
+#define _PSTRING
+
+#endif /* ndef _PSTRING */
diff --git a/source/include/rap.h b/source/include/rap.h
new file mode 100755
index 00000000000..993dfa7e335
--- /dev/null
+++ b/source/include/rap.h
@@ -0,0 +1,507 @@
+/*
+ Samba Unix/Linux SMB client library
+ RAP (SMB Remote Procedure Calls) defines and structures
+ Copyright (C) Steve French 2001 (sfrench@us.ibm.com)
+ Copyright (C) Jim McDonough 2001 (jmcd@us.ibm.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 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 _RAP_H_
+#define _RAP_H_
+
+/*****************************************************/
+/* */
+/* Additional RAP functionality */
+/* */
+/* RAP is the original SMB RPC, documented */
+/* by Microsoft and X/Open in the 1990s and */
+/* supported by most SMB/CIFS servers although */
+/* it is unlikely that any one implementation */
+/* supports all RAP command codes since some */
+/* are quite obsolete and a few are specific */
+/* to a particular network operating system */
+/* */
+/* Although it has largely been replaced */
+/* for complex remote admistration and management */
+/* (of servers) by the relatively newer */
+/* DCE/RPC based remote API (which better handles */
+/* large >64K data structures), there are many */
+/* important administrative and resource location */
+/* tasks and user tasks (e.g. password change) */
+/* that are performed via RAP. */
+/* */
+/* Although a few of the RAP calls are implemented */
+/* in the Samba client library already (clirap.c) */
+/* the new ones are in clirap2.c for easy patching */
+/* and integration and a corresponding header */
+/* file, rap.h, has been created. */
+/* */
+/* This is based on data from the CIFS spec */
+/* and the LAN Server and LAN Manager */
+/* Programming Reference books and published */
+/* RAP document and CIFS forum postings and */
+/* lots of trial and error. Additional */
+/* background information is available from the */
+/* X/Open reference book in their PC Interworking */
+/* series "IPC for SMB" and also from the */
+/* interoperability documentation in */
+/* ftp://ftp.microsoft.com/developr/drg/cifs */
+/* */
+/* Function names changed from API_ (as they are */
+/* in the CIFS specification to RAP_ in order */
+/* to avoid confusion with other API calls */
+/* sent via DCE RPC */
+/* */
+/*****************************************************/
+
+/*****************************************************/
+/* */
+/* Although without pound defines (of this header) */
+/* cifsrap.c already includes support for: */
+/* */
+/* WshareEnum (API number 0, level 1) */
+/* NetServerEnum2 (API num 104, level 1) */
+/* WWkstaUserLogon (132) */
+/* SamOEMchgPasswordUser2_P (214) */
+/* */
+/* and cifsprint.c already includes support for: */
+/* */
+/* WPrintJobEnum (API num 76, level 2) */
+/* WPrintJobDel (API num 81) */
+/* */
+/*****************************************************/
+
+#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
+
+
+/* Parameter description strings for RAP calls */
+/* Names are defined name for RAP call with _REQ */
+/* appended to end. */
+
+#define RAP_WFileEnum2_REQ "zzWrLehb8g8"
+#define RAP_WFileGetInfo2_REQ "DWrLh"
+#define RAP_WFileClose2_REQ "D"
+
+#define RAP_NetGroupEnum_REQ "WrLeh"
+#define RAP_NetGroupAdd_REQ "WsT"
+#define RAP_NetGroupDel_REQ "z"
+#define RAP_NetGroupAddUser_REQ "zz"
+#define RAP_NetGroupDelUser_REQ "zz"
+#define RAP_NetGroupGetUsers_REQ "zWrLeh"
+#define RAP_NetGroupSetUsers_REQ "zWsTW"
+
+#define RAP_NetUserAdd2_REQ "WsTWW"
+#define RAP_NetUserEnum_REQ "WrLeh"
+#define RAP_NetUserEnum2_REQ "WrLDieh"
+#define RAP_NetUserGetGroups_REQ "zWrLeh"
+#define RAP_NetUserSetGroups_REQ "zWsTW"
+#define RAP_NetUserPasswordSet_REQ "zb16b16w"
+#define RAP_NetUserPasswordSet2_REQ "zb16b16WW"
+#define RAP_SAMOEMChgPasswordUser2_REQ "B516B16"
+#define RAP_NetUserValidate2_REQ "Wb62WWrLhWW"
+
+#define RAP_NetServerEnum2_REQ "WrLehDz"
+#define RAP_WserverGetInfo_REQ "WrLh"
+#define RAP_NetWkstatGetInfo "WrLh"
+
+#define RAP_WShareAdd_REQ "WsT"
+#define RAP_WShareEnum_REQ "WrLeh"
+#define RAP_WShareDel_REQ "zW"
+#define RAP_WWkstaGetInfo_REQ "WrLh"
+
+#define RAP_NetPrintQEnum_REQ "WrLeh"
+#define RAP_NetPrintQGetInfo_REQ "zWrLh"
+
+#define RAP_NetServerAdminCommand_REQ "zhrLeh"
+#define RAP_NetServiceEnum_REQ "WrLeh"
+#define RAP_NetServiceControl_REQ "zWWrL"
+#define RAP_NetServiceInstall_REQ "zF88sg88T"
+#define RAP_NetServiceGetInfo_REQ "zWrLh"
+#define RAP_NetSessionEnum_REQ "WrLeh"
+#define RAP_NetSessionGetInfo_REQ "zWrLh"
+#define RAP_NetSessionDel_REQ "zW"
+
+#define RAP_NetConnectionEnum_REQ "zWrLeh"
+
+#define RAP_NetWkstaUserLogoff_REQ "zzWb38WrLh"
+
+/* Description strings for returned data in RAP calls */
+/* I use all caps here in part to avoid accidental */
+/* name collisions */
+
+#define RAP_FILE_INFO_L2 "D"
+#define RAP_FILE_INFO_L3 "DWWzz"
+
+#define RAP_GROUP_INFO_L0 "B21"
+#define RAP_GROUP_INFO_L1 "B21Bz"
+#define RAP_GROUP_USERS_INFO_0 "B21"
+#define RAP_GROUP_USERS_INFO_1 "B21BN"
+
+#define RAP_USER_INFO_L0 "B21"
+#define RAP_USER_INFO_L1 "B21BB16DWzzWz"
+
+#define RAP_SERVER_INFO_L0 "B16"
+#define RAP_SERVER_INFO_L1 "B16BBDz"
+#define RAP_SERVER_INFO_L2 "B16BBDzDDDWWzWWWWWWWB21BzWWWWWWWWWWWWWWWWWWWWWWz"
+#define RAP_SERVER_INFO_L3 "B16BBDzDDDWWzWWWWWWWB21BzWWWWWWWWWWWWWWWWWWWWWWzDWz"
+#define RAP_SERVICE_INFO_L0 "B16"
+#define RAP_SERVICE_INFO_L2 "B16WDWB64"
+#define RAP_SHARE_INFO_L0 "B13"
+#define RAP_SHARE_INFO_L1 "B13BWz"
+#define RAP_SHARE_INFO_L2 "B13BWzWWWzB9B"
+
+#define RAP_PRINTQ_INFO_L2 "B13BWWWzzzzzWN"
+#define RAP_SMB_PRINT_JOB_L1 "WB21BB16B10zWWzDDz"
+
+#define RAP_SESSION_INFO_L2 "zzWWWDDDz"
+#define RAP_CONNECTION_INFO_L1 "WWWWDzz"
+
+#define RAP_USER_LOGOFF_INFO_L1 "WDW"
+
+#define RAP_WKSTA_INFO_L1 "WDzzzzBBDWDWWWWWWWWWWWWWWWWWWWzzWzzW"
+#define RAP_WKSTA_INFO_L10 "zzzBBzz"
+
+/* BB explicit packing would help in structs below */
+
+/* sizes of fixed-length fields, including null terminator */
+#define RAP_GROUPNAME_LEN 21
+#define RAP_USERNAME_LEN 21
+#define RAP_SHARENAME_LEN 13
+#define RAP_UPASSWD_LEN 16 /* user password */
+#define RAP_SPASSWD_LEN 9 /* share password */
+#define RAP_MACHNAME_LEN 16
+#define RAP_SRVCNAME_LEN 16
+#define RAP_SRVCCMNT_LEN 64
+#define RAP_DATATYPE_LEN 10
+
+
+typedef struct rap_group_info_1
+{
+ char group_name[RAP_GROUPNAME_LEN];
+ char reserved1;
+ char * comment;
+} RAP_GROUP_INFO_1;
+
+typedef struct rap_user_info_1
+{
+ char user_name[RAP_USERNAME_LEN];
+ char reserved1;
+ char passwrd[RAP_UPASSWD_LEN];
+ uint32 pwage;
+ uint16 priv;
+ char * home_dir;
+ char * comment;
+ uint16 userflags;
+ char * logon_script;
+} RAP_USER_INFO_1;
+
+typedef struct rap_service_info_2
+{
+ char service_name[RAP_SRVCNAME_LEN];
+ uint16 status;
+ uint32 installcode;
+ uint16 process_num;
+ char * comment;
+} RAP_SERVICE_INFO_2;
+
+
+typedef struct rap_share_info_0
+{
+ char share_name[RAP_SHARENAME_LEN];
+} RAP_SHARE_INFO_0;
+
+typedef struct rap_share_info_1
+{
+ char share_name[RAP_SHARENAME_LEN];
+ char reserved1;
+ uint16 share_type;
+ char * comment;
+} RAP_SHARE_INFO_1;
+
+typedef struct rap_share_info_2
+{
+ char share_name[RAP_SHARENAME_LEN];
+ char reserved1;
+ uint16 share_type;
+ char * comment;
+ uint16 perms;
+ uint16 maximum_users;
+ uint16 active_users;
+ char * path;
+ char password[RAP_SPASSWD_LEN];
+ char reserved2;
+} RAP_SHARE_INFO_2;
+
+#endif /* _RAP_H_ */
diff --git a/source/include/rpc_brs.h b/source/include/rpc_brs.h
new file mode 100644
index 00000000000..cd0928d470f
--- /dev/null
+++ b/source/include/rpc_brs.h
@@ -0,0 +1,80 @@
+/*
+ Unix SMB/CIFS implementation.
+ SMB parameters and setup
+ Copyright (C) Andrew Tridgell 1992-1999
+ Copyright (C) Luke Kenneth Casson Leighton 1996-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 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 _RPC_BRS_H /* _RPC_BRS_H */
+#define _RPC_BRS_H
+
+
+/* brssvc pipe */
+#define BRS_QUERY_INFO 0x02
+
+
+/* BRS_Q_QUERY_INFO - probably a capabilities request */
+typedef struct q_brs_query_info_info
+{
+ uint32 ptr_srv_name; /* pointer (to server name?) */
+ UNISTR2 uni_srv_name; /* unicode server name starting with '\\' */
+
+ uint16 switch_value1; /* info level 100 (0x64) */
+ /* align */
+ uint16 switch_value2; /* info level 100 (0x64) */
+
+ uint32 ptr;
+ uint32 pad1;
+ uint32 pad2;
+
+} BRS_Q_QUERY_INFO;
+
+
+/* BRS_INFO_100 - level 100 info */
+typedef struct brs_info_100_info
+{
+ uint32 pad1;
+ uint32 ptr2;
+ uint32 pad2;
+ uint32 pad3;
+
+} BRS_INFO_100;
+
+
+/* BRS_R_QUERY_INFO - probably a capabilities request */
+typedef struct r_brs_query_info_info
+{
+ uint16 switch_value1; /* 100 (0x64) - switch value */
+ /* align */
+ uint16 switch_value2; /* info level 100 (0x64) */
+
+ /* for now, only level 100 is supported. this should be an enum container */
+ uint32 ptr_1; /* pointer 1 */
+
+ union
+ {
+ BRS_INFO_100 *brs100; /* browser info level 100 */
+ void *id;
+
+ } info;
+
+ NTSTATUS status; /* return status */
+
+} BRS_R_QUERY_INFO;
+
+#endif /* _RPC_BRS_H */
+
diff --git a/source/include/rpc_client.h b/source/include/rpc_client.h
new file mode 100644
index 00000000000..bce9ec7f273
--- /dev/null
+++ b/source/include/rpc_client.h
@@ -0,0 +1,28 @@
+/*
+ Unix SMB/CIFS implementation.
+ SMB parameters and setup
+ Copyright (C) Elrond 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 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 _RPC_CLIENT_H
+#define _RPC_CLIENT_H
+
+#if 0 /* JERRY */
+#include "rpc_client_proto.h"
+#endif
+
+#endif /* _RPC_CLIENT_H */
diff --git a/source/include/rpc_creds.h b/source/include/rpc_creds.h
new file mode 100644
index 00000000000..3022b172899
--- /dev/null
+++ b/source/include/rpc_creds.h
@@ -0,0 +1,96 @@
+/*
+ Unix SMB/CIFS implementation.
+ SMB parameters and setup
+ Copyright (C) Andrew Tridgell 1992-1999
+ Copyright (C) Luke Kenneth Casson Leighton 1996-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 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 _RPC_CREDS_H /* _RPC_CREDS_H */
+#define _RPC_CREDS_H
+
+typedef struct ntuser_creds
+{
+ fstring user_name;
+ fstring domain;
+ struct pwd_info pwd;
+
+ uint32 ntlmssp_flags;
+
+} CREDS_NT;
+
+typedef struct unixuser_creds
+{
+ fstring user_name;
+ fstring requested_name;
+ fstring real_name;
+ BOOL guest;
+
+} CREDS_UNIX;
+
+typedef struct unixsec_creds
+{
+ uint32 uid;
+ uint32 gid;
+ int num_grps;
+ uint32 *grps;
+
+} CREDS_UNIX_SEC;
+
+typedef struct ntsec_creds
+{
+ DOM_SID sid;
+ uint32 num_grps;
+ uint32 *grp_rids;
+
+} CREDS_NT_SEC;
+
+typedef struct user_creds
+{
+ BOOL reuse;
+
+ uint32 ptr_ntc;
+ uint32 ptr_uxc;
+ uint32 ptr_nts;
+ uint32 ptr_uxs;
+ uint32 ptr_ssk;
+
+ CREDS_NT ntc;
+ CREDS_UNIX uxc;
+
+ CREDS_NT_SEC nts;
+ CREDS_UNIX_SEC uxs;
+
+ uchar usr_sess_key[16];
+
+} CREDS_HYBRID;
+
+typedef struct cred_command
+{
+ uint16 version;
+ uint16 command;
+ uint32 pid; /* unique process id */
+
+ fstring name;
+
+ uint32 ptr_creds;
+ CREDS_HYBRID *cred;
+
+} CREDS_CMD;
+
+#endif /* _RPC_CREDS_H */
+
diff --git a/source/include/rpc_dce.h b/source/include/rpc_dce.h
new file mode 100644
index 00000000000..8266fc861f1
--- /dev/null
+++ b/source/include/rpc_dce.h
@@ -0,0 +1,361 @@
+/*
+ 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 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 _DCE_RPC_H /* _DCE_RPC_H */
+#define _DCE_RPC_H
+
+#include "rpc_misc.h" /* this only pulls in STRHDR */
+
+
+/* DCE/RPC packet types */
+
+enum RPC_PKT_TYPE
+{
+ RPC_REQUEST = 0x00,
+ RPC_RESPONSE = 0x02,
+ RPC_FAULT = 0x03,
+ RPC_BIND = 0x0B,
+ RPC_BINDACK = 0x0C,
+ RPC_BINDNACK = 0x0D,
+ RPC_ALTCONT = 0x0E,
+ RPC_ALTCONTRESP = 0x0F,
+ RPC_BINDRESP = 0x10 /* not the real name! this is undocumented! */
+};
+
+/* DCE/RPC flags */
+#define RPC_FLG_FIRST 0x01
+#define RPC_FLG_LAST 0x02
+#define RPC_FLG_NOCALL 0x20
+
+#define SMBD_NTLMSSP_NEG_FLAGS 0x000082b1 /* ALWAYS_SIGN|NEG_NTLM|NEG_LM|NEG_SEAL|NEG_SIGN|NEG_UNICODE */
+
+/* NTLMSSP signature version */
+#define NTLMSSP_SIGN_VERSION 0x01
+
+/* NTLMSSP auth type */
+#define NTLMSSP_AUTH_TYPE 0xa
+
+/* DCE-RPC standard identifiers to indicate
+ signing or sealing of an RPC pipe */
+#define RPC_PIPE_AUTH_SIGN_LEVEL 0x5
+#define RPC_PIPE_AUTH_SEAL_LEVEL 0x6
+
+/* Netlogon schannel auth type and level */
+#define NETSEC_AUTH_TYPE 0x44
+#define NETSEC_SIGN_SIGNATURE { 0x77, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00 }
+#define NETSEC_SEAL_SIGNATURE { 0x77, 0x00, 0x7a, 0x00, 0xff, 0xff, 0x00, 0x00 }
+#define RPC_AUTH_NETSEC_CHK_LEN 0x20
+
+/* 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
+
+#define NETLOGON_NEG_SCHANNEL 0x40000000
+
+enum netsec_direction
+{
+ SENDER_IS_INITIATOR,
+ SENDER_IS_ACCEPTOR
+};
+
+/* Internal Flags to indicate what type of authentication on the pipe */
+#define AUTH_PIPE_SIGN 0x0001
+#define AUTH_PIPE_SEAL 0x0002
+#define AUTH_PIPE_NTLMSSP 0x0004
+#define AUTH_PIPE_NETSEC 0x0008
+
+/* Maximum PDU fragment size. */
+/* #define MAX_PDU_FRAG_LEN 0x1630 this is what wnt sets */
+#define MAX_PDU_FRAG_LEN 0x10b8 /* this is what w2k sets */
+
+/* RPC_IFACE */
+typedef struct rpc_iface_info
+{
+ struct uuid uuid; /* 16 bytes of rpc interface identification */
+ uint32 version; /* the interface version number */
+
+} RPC_IFACE;
+
+#define RPC_IFACE_LEN (UUID_SIZE + 4)
+
+struct pipe_id_info
+{
+ /* the names appear not to matter: the syntaxes _do_ matter */
+
+ const char *client_pipe;
+ RPC_IFACE abstr_syntax; /* this one is the abstract syntax id */
+
+ const char *server_pipe; /* this one is the secondary syntax name */
+ RPC_IFACE trans_syntax; /* this one is the primary syntax id */
+};
+
+/* RPC_HDR - dce rpc header */
+typedef struct rpc_hdr_info
+{
+ uint8 major; /* 5 - RPC major version */
+ uint8 minor; /* 0 - RPC minor version */
+ uint8 pkt_type; /* RPC_PKT_TYPE - RPC response packet */
+ uint8 flags; /* DCE/RPC flags */
+ uint8 pack_type[4]; /* 0x1000 0000 - little-endian packed data representation */
+ uint16 frag_len; /* fragment length - data size (bytes) inc header and tail. */
+ uint16 auth_len; /* 0 - authentication length */
+ uint32 call_id; /* call identifier. matches 12th uint32 of incoming RPC data. */
+
+} RPC_HDR;
+
+#define RPC_HEADER_LEN 16
+
+/* RPC_HDR_REQ - ms request rpc header */
+typedef struct rpc_hdr_req_info
+{
+ uint32 alloc_hint; /* allocation hint - data size (bytes) minus header and tail. */
+ uint16 context_id; /* presentation context identifier */
+ uint16 opnum; /* opnum */
+
+} RPC_HDR_REQ;
+
+#define RPC_HDR_REQ_LEN 8
+
+/* RPC_HDR_RESP - ms response rpc header */
+typedef struct rpc_hdr_resp_info
+{
+ uint32 alloc_hint; /* allocation hint - data size (bytes) minus header and tail. */
+ uint16 context_id; /* 0 - presentation context identifier */
+ uint8 cancel_count; /* 0 - cancel count */
+ uint8 reserved; /* 0 - reserved. */
+
+} RPC_HDR_RESP;
+
+#define RPC_HDR_RESP_LEN 8
+
+/* RPC_HDR_FAULT - fault rpc header */
+typedef struct rpc_hdr_fault_info
+{
+ NTSTATUS status;
+ uint32 reserved; /* 0x0000 0000 */
+} RPC_HDR_FAULT;
+
+#define RPC_HDR_FAULT_LEN 8
+
+/* this seems to be the same string name depending on the name of the pipe,
+ * but is more likely to be linked to the interface name
+ * "srvsvc", "\\PIPE\\ntsvcs"
+ * "samr", "\\PIPE\\lsass"
+ * "wkssvc", "\\PIPE\\wksvcs"
+ * "NETLOGON", "\\PIPE\\NETLOGON"
+ */
+/* RPC_ADDR_STR */
+typedef struct rpc_addr_info
+{
+ uint16 len; /* length of the string including null terminator */
+ fstring str; /* the string above in single byte, null terminated form */
+
+} RPC_ADDR_STR;
+
+/* RPC_HDR_BBA */
+typedef struct rpc_hdr_bba_info
+{
+ uint16 max_tsize; /* maximum transmission fragment size (0x1630) */
+ uint16 max_rsize; /* max receive fragment size (0x1630) */
+ uint32 assoc_gid; /* associated group id (0x0) */
+
+} RPC_HDR_BBA;
+
+#define RPC_HDR_BBA_LEN 8
+
+/* RPC_HDR_AUTHA */
+typedef struct rpc_hdr_autha_info
+{
+ uint16 max_tsize; /* maximum transmission fragment size (0x1630) */
+ uint16 max_rsize; /* max receive fragment size (0x1630) */
+
+ uint8 auth_type; /* 0x0a */
+ uint8 auth_level; /* 0x06 */
+ uint8 stub_type_len; /* don't know */
+ uint8 padding; /* padding */
+
+ uint32 unknown; /* 0x0014a0c0 */
+
+} RPC_HDR_AUTHA;
+
+#define RPC_HDR_AUTHA_LEN 12
+
+/* RPC_HDR_AUTH */
+typedef struct rpc_hdr_auth_info
+{
+ uint8 auth_type; /* 0x0a */
+ uint8 auth_level; /* 0x06 */
+ uint8 padding;
+ uint8 reserved; /* padding */
+
+ uint32 auth_context; /* pointer */
+
+} RPC_HDR_AUTH;
+
+#define RPC_HDR_AUTH_LEN 8
+
+/* this is TEMPORARILY coded up as a specific structure */
+/* this structure comes after the bind request */
+/* RPC_AUTH_NETSEC_NEG */
+typedef struct rpc_auth_netsec_neg_info
+{
+ uint32 type1; /* Always zero ? */
+ uint32 type2; /* Types 0x3 and 0x13 seen. Check AcquireSecurityContext() docs.... */
+ fstring domain; /* calling workstations's domain */
+ fstring myname; /* calling workstation's name */
+} RPC_AUTH_NETSEC_NEG;
+
+/* attached to the end of encrypted rpc requests and responses */
+/* RPC_AUTH_NETSEC_CHK */
+typedef struct rpc_auth_netsec_chk_info
+{
+ uint8 sig [8]; /* 77 00 7a 00 ff ff 00 00 */
+ uint8 packet_digest[8]; /* checksum over the packet, MD5'ed with session key */
+ uint8 seq_num[8]; /* verifier, seq num */
+ uint8 confounder[8]; /* random 8-byte nonce */
+} RPC_AUTH_NETSEC_CHK;
+
+struct netsec_auth_struct
+{
+ uchar sess_key[16];
+ uint32 seq_num;
+ int auth_flags;
+};
+
+/* RPC_BIND_REQ - ms req bind */
+typedef struct rpc_bind_req_info
+{
+ RPC_HDR_BBA bba;
+
+ uint32 num_elements; /* the number of elements (0x1) */
+ uint16 context_id; /* presentation context identifier (0x0) */
+ uint8 num_syntaxes; /* the number of syntaxes (has always been 1?)(0x1) */
+
+ RPC_IFACE abstract; /* num and vers. of interface client is using */
+ RPC_IFACE transfer; /* num and vers. of interface to use for replies */
+
+} RPC_HDR_RB;
+
+/*
+ * The following length is 8 bytes RPC_HDR_BBA_LEN, 8 bytes internals
+ * (with 3 bytes padding), + 2 x RPC_IFACE_LEN bytes for RPC_IFACE structs.
+ */
+
+#define RPC_HDR_RB_LEN (RPC_HDR_BBA_LEN + 8 + (2*RPC_IFACE_LEN))
+
+/* RPC_RESULTS - can only cope with one reason, right now... */
+typedef struct rpc_results_info
+{
+/* uint8[] # 4-byte alignment padding, against SMB header */
+
+ uint8 num_results; /* the number of results (0x01) */
+
+/* uint8[] # 4-byte alignment padding, against SMB header */
+
+ uint16 result; /* result (0x00 = accept) */
+ uint16 reason; /* reason (0x00 = no reason specified) */
+
+} RPC_RESULTS;
+
+/* RPC_HDR_BA */
+typedef struct rpc_hdr_ba_info
+{
+ RPC_HDR_BBA bba;
+
+ RPC_ADDR_STR addr ; /* the secondary address string, as described earlier */
+ RPC_RESULTS res ; /* results and reasons */
+ RPC_IFACE transfer; /* the transfer syntax from the request */
+
+} RPC_HDR_BA;
+
+/* RPC_AUTH_VERIFIER */
+typedef struct rpc_auth_verif_info
+{
+ fstring signature; /* "NTLMSSP".. Ok, not quite anymore */
+ uint32 msg_type; /* NTLMSSP_MESSAGE_TYPE (1,2,3) and 5 for schannel */
+
+} RPC_AUTH_VERIFIER;
+
+/* this is TEMPORARILY coded up as a specific structure */
+/* this structure comes after the bind request */
+/* RPC_AUTH_NTLMSSP_NEG */
+typedef struct rpc_auth_ntlmssp_neg_info
+{
+ uint32 neg_flgs; /* 0x0000 b2b3 */
+
+ STRHDR hdr_myname; /* offset is against START of this structure */
+ STRHDR hdr_domain; /* offset is against START of this structure */
+
+ fstring myname; /* calling workstation's name */
+ fstring domain; /* calling workstations's domain */
+
+} RPC_AUTH_NTLMSSP_NEG;
+
+/* this is TEMPORARILY coded up as a specific structure */
+/* this structure comes after the bind acknowledgement */
+/* RPC_AUTH_NTLMSSP_CHAL */
+typedef struct rpc_auth_ntlmssp_chal_info
+{
+ uint32 unknown_1; /* 0x0000 0000 */
+ uint32 unknown_2; /* 0x0000 0028 */
+ uint32 neg_flags; /* 0x0000 82b1 */
+
+ uint8 challenge[8]; /* ntlm challenge */
+ uint8 reserved [8]; /* zeros */
+
+} RPC_AUTH_NTLMSSP_CHAL;
+
+
+/* RPC_AUTH_NTLMSSP_RESP */
+typedef struct rpc_auth_ntlmssp_resp_info
+{
+ STRHDR hdr_lm_resp; /* 24 byte response */
+ STRHDR hdr_nt_resp; /* 24 byte response */
+ STRHDR hdr_domain;
+ STRHDR hdr_usr;
+ STRHDR hdr_wks;
+ STRHDR hdr_sess_key; /* NULL unless negotiated */
+ uint32 neg_flags; /* 0x0000 82b1 */
+
+ fstring sess_key;
+ fstring wks;
+ fstring user;
+ fstring domain;
+ fstring nt_resp;
+ fstring lm_resp;
+
+} RPC_AUTH_NTLMSSP_RESP;
+
+/* attached to the end of encrypted rpc requests and responses */
+/* RPC_AUTH_NTLMSSP_CHK */
+typedef struct rpc_auth_ntlmssp_chk_info
+{
+ uint32 ver; /* 0x0000 0001 */
+ uint32 reserved;
+ uint32 crc32; /* checksum using 0xEDB8 8320 as a polynomial */
+ uint32 seq_num;
+
+} RPC_AUTH_NTLMSSP_CHK;
+
+#define RPC_AUTH_NTLMSSP_CHK_LEN 16
+
+#endif /* _DCE_RPC_H */
diff --git a/source/include/rpc_dfs.h b/source/include/rpc_dfs.h
new file mode 100644
index 00000000000..39316a5d541
--- /dev/null
+++ b/source/include/rpc_dfs.h
@@ -0,0 +1,197 @@
+/*
+ Unix SMB/CIFS implementation.
+ Samba parameters and setup
+ Copyright (C) Andrew Tridgell 1992-2000
+ Copyright (C) Luke Kenneth Casson Leighton 1996 - 2000
+ Copyright (C) Shirish Kalele 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 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 _RPC_DFS_H
+#define _RPC_DFS_H
+
+/* NETDFS pipe: calls */
+#define DFS_EXIST 0x00
+#define DFS_ADD 0x01
+#define DFS_REMOVE 0x02
+#define DFS_GET_INFO 0x04
+#define DFS_ENUM 0x05
+
+/* dfsadd flags */
+#define DFSFLAG_ADD_VOLUME 0x00000001
+#define DFSFLAG_RESTORE_VOLUME 0x00000002
+
+typedef struct dfs_q_dfs_exist
+{
+ uint32 dummy;
+}
+DFS_Q_DFS_EXIST;
+
+/* status == 1 if dfs exists. */
+typedef struct dfs_r_dfs_exist
+{
+ uint32 status; /* Not a WERROR or NTSTATUS code */
+}
+DFS_R_DFS_EXIST;
+
+typedef struct dfs_q_dfs_add
+{
+ uint32 ptr_DfsEntryPath;
+ UNISTR2 DfsEntryPath;
+ uint32 ptr_ServerName;
+ UNISTR2 ServerName;
+ uint32 ptr_ShareName;
+ UNISTR2 ShareName;
+ uint32 ptr_Comment;
+ UNISTR2 Comment;
+ uint32 Flags;
+}
+DFS_Q_DFS_ADD;
+
+typedef struct dfs_r_dfs_add
+{
+ WERROR status;
+}
+DFS_R_DFS_ADD;
+
+/********************************************/
+typedef struct dfs_q_dfs_remove
+{
+ UNISTR2 DfsEntryPath;
+ uint32 ptr_ServerName;
+ UNISTR2 ServerName;
+ uint32 ptr_ShareName;
+ UNISTR2 ShareName;
+}
+DFS_Q_DFS_REMOVE;
+
+typedef struct dfs_r_dfs_remove
+{
+ WERROR status;
+}
+DFS_R_DFS_REMOVE;
+
+/********************************************/
+typedef struct dfs_info_1
+{
+ uint32 ptr_entrypath;
+ UNISTR2 entrypath;
+}
+DFS_INFO_1;
+
+typedef struct dfs_info_2
+{
+ uint32 ptr_entrypath;
+ UNISTR2 entrypath;
+ uint32 ptr_comment;
+ UNISTR2 comment;
+ uint32 state;
+ uint32 num_storages;
+}
+DFS_INFO_2;
+
+typedef struct dfs_storage_info
+{
+ uint32 state;
+ uint32 ptr_servername;
+ UNISTR2 servername;
+ uint32 ptr_sharename;
+ UNISTR2 sharename;
+}
+DFS_STORAGE_INFO;
+
+typedef struct dfs_info_3
+{
+ uint32 ptr_entrypath;
+ UNISTR2 entrypath;
+ uint32 ptr_comment;
+ UNISTR2 comment;
+ uint32 state;
+ uint32 num_storages;
+ uint32 ptr_storages;
+ uint32 num_storage_infos;
+ DFS_STORAGE_INFO* storages;
+}
+DFS_INFO_3;
+
+typedef struct dfs_info_ctr
+{
+
+ uint32 switch_value;
+ uint32 num_entries;
+ uint32 ptr_dfs_ctr; /* pointer to dfs info union */
+ union
+ {
+ DFS_INFO_1 *info1;
+ DFS_INFO_2 *info2;
+ DFS_INFO_3 *info3;
+ } dfs;
+}
+DFS_INFO_CTR;
+
+typedef struct dfs_q_dfs_get_info
+{
+ UNISTR2 uni_path;
+
+ uint32 ptr_server;
+ UNISTR2 uni_server;
+
+ uint32 ptr_share;
+ UNISTR2 uni_share;
+
+ uint32 level;
+}
+DFS_Q_DFS_GET_INFO;
+
+typedef struct dfs_r_dfs_get_info
+{
+ uint32 level;
+ uint32 ptr_ctr;
+ DFS_INFO_CTR ctr;
+ WERROR status;
+}
+DFS_R_DFS_GET_INFO;
+
+typedef struct dfs_q_dfs_enum
+{
+ uint32 level;
+ uint32 maxpreflen;
+ uint32 ptr_buffer;
+ uint32 level2;
+ uint32 ptr_num_entries;
+ uint32 num_entries;
+ uint32 ptr_num_entries2;
+ uint32 num_entries2;
+ ENUM_HND reshnd;
+}
+DFS_Q_DFS_ENUM;
+
+typedef struct dfs_r_dfs_enum
+{
+ DFS_INFO_CTR *ctr;
+ uint32 ptr_buffer;
+ uint32 level;
+ uint32 level2;
+ uint32 ptr_num_entries;
+ uint32 num_entries;
+ uint32 ptr_num_entries2;
+ uint32 num_entries2;
+ ENUM_HND reshnd;
+ WERROR status;
+}
+DFS_R_DFS_ENUM;
+
+#endif
diff --git a/source/include/rpc_ds.h b/source/include/rpc_ds.h
new file mode 100644
index 00000000000..e06918730a4
--- /dev/null
+++ b/source/include/rpc_ds.h
@@ -0,0 +1,171 @@
+/*
+ Unix SMB/CIFS implementation.
+ SMB parameters and setup
+ Copyright (C) Gerald Carter 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 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 _RPC_DS_H /* _RPC_LSA_H */
+#define _RPC_DS_H
+
+#include "rpc_misc.h"
+
+
+/* Opcodes available on PIPE_LSARPC_DS */
+
+#define DS_GETPRIMDOMINFO 0x00
+#define DS_NOP 0xFF /* no op -- placeholder */
+
+/* Opcodes available on PIPE_NETLOGON */
+
+#define DS_ENUM_DOM_TRUSTS 0x28
+
+
+/* macros for RPC's */
+
+/* DSROLE_PRIMARY_DOMAIN_INFO_BASIC */
+
+/* flags */
+
+#define DSROLE_PRIMARY_DS_RUNNING 0x00000001
+#define DSROLE_PRIMARY_DS_MIXED_MODE 0x00000002
+#define DSROLE_UPGRADE_IN_PROGRESS 0x00000004
+#define DSROLE_PRIMARY_DOMAIN_GUID_PRESENT 0x01000000
+
+/* machine role */
+
+#define DSROLE_STANDALONE_SRV 2
+#define DSROLE_DOMAIN_MEMBER_SRV 3
+#define DSROLE_BDC 4
+#define DSROLE_PDC 5
+
+
+typedef struct
+{
+ uint16 machine_role;
+ uint16 unknown; /* 0x6173 -- maybe just alignment? */
+
+ uint32 flags;
+
+ uint32 netbios_ptr;
+ uint32 dnsname_ptr;
+ uint32 forestname_ptr;
+
+ struct uuid domain_guid;
+
+ UNISTR2 netbios_domain;
+
+ UNISTR2 dns_domain; /* our dns domain */
+ UNISTR2 forest_domain; /* root domain of the forest to which we belong */
+} DSROLE_PRIMARY_DOMAIN_INFO_BASIC;
+
+typedef struct
+{
+ DSROLE_PRIMARY_DOMAIN_INFO_BASIC *basic;
+} DS_DOMINFO_CTR;
+
+/* info levels for ds_getprimdominfo() */
+
+#define DsRolePrimaryDomainInfoBasic 1
+
+
+/* DS_Q_GETPRIMDOMINFO - DsGetPrimaryDomainInformation() request */
+typedef struct
+{
+ uint16 level;
+} DS_Q_GETPRIMDOMINFO;
+
+/* DS_R_GETPRIMDOMINFO - DsGetPrimaryDomainInformation() response */
+typedef struct
+{
+ uint32 ptr;
+
+ uint16 level;
+ uint16 unknown0; /* 0x455c -- maybe just alignment? */
+
+ DS_DOMINFO_CTR info;
+
+ NTSTATUS status;
+} DS_R_GETPRIMDOMINFO;
+
+typedef struct {
+ /* static portion of structure */
+ uint32 netbios_ptr;
+ uint32 dns_ptr;
+ uint32 flags;
+ uint32 parent_index;
+ uint32 trust_type;
+ uint32 trust_attributes;
+ uint32 sid_ptr;
+ struct uuid guid;
+
+ UNISTR2 netbios_domain;
+ UNISTR2 dns_domain;
+ DOM_SID2 sid;
+
+} DS_DOMAIN_TRUSTS;
+
+struct ds_domain_trust {
+ /* static portion of structure */
+ uint32 flags;
+ uint32 parent_index;
+ uint32 trust_type;
+ uint32 trust_attributes;
+ struct uuid guid;
+
+ DOM_SID sid;
+ char *netbios_domain;
+ char *dns_domain;
+};
+
+typedef struct {
+
+ uint32 ptr;
+ uint32 max_count;
+ DS_DOMAIN_TRUSTS *trusts;
+
+} DS_DOMAIN_TRUSTS_CTR;
+
+#define DS_DOMAIN_IN_FOREST 0x0001 /* domains in the forest to which
+ we belong; even different domain trees */
+#define DS_DOMAIN_DIRECT_OUTBOUND 0x0002 /* trusted domains */
+#define DS_DOMAIN_TREE_ROOT 0x0004 /* root of our forest; also available in
+ DsRoleGetPrimaryDomainInfo() */
+#define DS_DOMAIN_PRIMARY 0x0008 /* our domain */
+#define DS_DOMAIN_NATIVE_MODE 0x0010 /* native mode AD servers */
+#define DS_DOMAIN_DIRECT_INBOUND 0x0020 /* trusting domains */
+
+/* DS_Q_ENUM_DOM_TRUSTS - DsEnumerateDomainTrusts() request */
+typedef struct
+{
+ uint32 server_ptr;
+ UNISTR2 server;
+ uint32 flags;
+
+} DS_Q_ENUM_DOM_TRUSTS;
+
+/* DS_R_ENUM_DOM_TRUSTS - DsEnumerateDomainTrusts() response */
+typedef struct
+{
+ uint32 num_domains;
+ DS_DOMAIN_TRUSTS_CTR domains;
+
+ NTSTATUS status;
+
+} DS_R_ENUM_DOM_TRUSTS;
+
+
+#endif /* _RPC_DS_H */
diff --git a/source/include/rpc_echo.h b/source/include/rpc_echo.h
new file mode 100644
index 00000000000..6b4ea6abfba
--- /dev/null
+++ b/source/include/rpc_echo.h
@@ -0,0 +1,75 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ Samba rpcecho definitions.
+
+ 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 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 _RPC_ECHO_H
+#define _RPC_ECHO_H
+
+#define ECHO_ADD_ONE 0x00
+#define ECHO_DATA 0x01
+#define ECHO_SINK_DATA 0x02
+#define ECHO_SOURCE_DATA 0x03
+
+typedef struct echo_q_add_one
+{
+ uint32 request;
+} ECHO_Q_ADD_ONE;
+
+typedef struct echo_r_add_one
+{
+ uint32 response;
+} ECHO_R_ADD_ONE;
+
+typedef struct echo_q_echo_data
+{
+ uint32 size;
+ char *data;
+} ECHO_Q_ECHO_DATA;
+
+typedef struct echo_r_echo_data
+{
+ uint32 size;
+ char *data;
+} ECHO_R_ECHO_DATA;
+
+typedef struct echo_q_source_data
+{
+ uint32 size;
+} ECHO_Q_SOURCE_DATA;
+
+typedef struct echo_r_source_data
+{
+ uint32 size;
+ char *data;
+} ECHO_R_SOURCE_DATA;
+
+typedef struct echo_q_sink_data
+{
+ uint32 size;
+ char *data;
+} ECHO_Q_SINK_DATA;
+
+typedef struct echo_r_sink_data
+{
+ int dummy; /* unused */
+} ECHO_R_SINK_DATA;
+
+#endif
diff --git a/source/include/rpc_epmapper.h b/source/include/rpc_epmapper.h
new file mode 100644
index 00000000000..bbca6ac1f28
--- /dev/null
+++ b/source/include/rpc_epmapper.h
@@ -0,0 +1,118 @@
+/*
+ Unix SMB/CIFS implementation.
+ Endpoint mapper data definitions
+ 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 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.
+*/
+
+#define EPM_HANDLE_LEN 20
+
+/* ordinal for the mapping interface */
+
+#define EPM_MAP_PIPE_NAME 0x03
+
+/* some of the different connection protocols and their IDs from Windows */
+
+#define EPM_FLOOR_UUID 0x0d /* floor contains UUID */
+#define EPM_FLOOR_RPC 0x0b /* tower is for connection-oriented rpc */
+#define EPM_FLOOR_TCP 0x07 /* floor contains tcp port number */
+#define EPM_FLOOR_IP 0x09 /* floor contains IP address */
+#define EPM_FLOOR_NMPIPES 0x0f /* floor contains remote named pipe name */
+#define EPM_FLOOR_LRPC 0x10 /* floor contains local named pipe name */
+#define EPM_FLOOR_NETBIOS 0x11 /* floor contains netbios address */
+#define EPM_FLOOR_NETBEUI 0x12 /* floor contains netbeui address */
+#define EPM_FLOOR_SOCKET 0x20
+
+#define EPM_PIPE_NM "epmapper"
+
+#define MAX_TOWERS 1
+
+typedef struct
+{
+ uint8 data[EPM_HANDLE_LEN];
+} EPM_HANDLE;
+
+typedef struct
+{
+ struct {
+ uint16 length;
+ uint8 protocol;
+ struct {
+ struct uuid uuid;
+ uint16 version;
+ } uuid;
+ } lhs;
+ struct {
+ uint16 length;
+ uint16 unknown;
+ struct {
+ uint16 port;
+ } tcp;
+ struct {
+ uint8 addr[4];
+ } ip;
+ char string[MAXHOSTNAMELEN+3]; /* hostname + \\ + null term */
+ } rhs;
+} EPM_FLOOR;
+
+typedef struct
+{
+ uint32 max_length;
+ uint32 length;
+ uint16 num_floors;
+ EPM_FLOOR *floors;
+ uint8 unknown;
+} EPM_TOWER;
+
+typedef struct
+{
+ EPM_HANDLE handle;
+ uint32 tower_ref_id;
+ EPM_TOWER *tower;
+ EPM_HANDLE term_handle; /* in/out */
+ uint32 max_towers;
+} EPM_Q_MAP;
+
+typedef struct
+{
+ uint32 max_count;
+ uint32 offset;
+ uint32 count;
+ uint32 *tower_ref_ids;
+ EPM_TOWER *towers;
+} EPM_TOWER_ARRAY;
+
+typedef struct
+{
+ EPM_HANDLE handle;
+ uint32 num_results;
+ EPM_TOWER_ARRAY *results;
+ uint32 status;
+} EPM_R_MAP;
+
+
+/* port mapping entries to be read */
+
+typedef struct _mapper_entries{
+ uint8 protocol ;
+ RPC_IFACE uuid_info ; /* needs to be zeroed if no specific uuid */
+ uint16 port ;
+ char pipe_name[40] ;
+ char srv_name[20] ;
+ uint8 srv_port[4] ;
+ char func_name[16][16]; /* array of up to 16 functions available */
+} mapper_entries;
+
diff --git a/source/include/rpc_lsa.h b/source/include/rpc_lsa.h
new file mode 100644
index 00000000000..29a9cd7306b
--- /dev/null
+++ b/source/include/rpc_lsa.h
@@ -0,0 +1,744 @@
+/*
+ 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 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 _RPC_LSA_H /* _RPC_LSA_H */
+#define _RPC_LSA_H
+
+#include "rpc_misc.h"
+
+/* Opcodes available on PIPE_LSARPC */
+
+#if 0 /* UNIMPLEMENTED */
+
+#define LSA_LOOKUPSIDS2 0x39
+
+#endif
+
+#define LSA_CLOSE 0x00
+#define LSA_DELETE 0x01
+#define LSA_ENUM_PRIVS 0x02
+#define LSA_QUERYSECOBJ 0x03
+#define LSA_SETSECOBJ 0x04
+#define LSA_CHANGEPASSWORD 0x05
+#define LSA_OPENPOLICY 0x06
+#define LSA_QUERYINFOPOLICY 0x07
+#define LSA_SETINFOPOLICY 0x08
+#define LSA_CLEARAUDITLOG 0x09
+#define LSA_CREATEACCOUNT 0x0a
+#define LSA_ENUM_ACCOUNTS 0x0b
+#define LSA_CREATETRUSTDOM 0x0c
+#define LSA_ENUMTRUSTDOM 0x0d
+#define LSA_LOOKUPNAMES 0x0e
+#define LSA_LOOKUPSIDS 0x0f
+#define LSA_CREATESECRET 0x10
+#define LSA_OPENACCOUNT 0x11
+#define LSA_ENUMPRIVSACCOUNT 0x12
+#define LSA_ADDPRIVS 0x13
+#define LSA_REMOVEPRIVS 0x14
+#define LSA_GETQUOTAS 0x15
+#define LSA_SETQUOTAS 0x16
+#define LSA_GETSYSTEMACCOUNT 0x17
+#define LSA_SETSYSTEMACCOUNT 0x18
+#define LSA_OPENTRUSTDOM 0x19
+#define LSA_QUERYTRUSTDOM 0x1a
+#define LSA_SETINFOTRUSTDOM 0x1b
+#define LSA_OPENSECRET 0x1c
+#define LSA_SETSECRET 0x1d
+#define LSA_QUERYSECRET 0x1e
+#define LSA_LOOKUPPRIVVALUE 0x1f
+#define LSA_LOOKUPPRIVNAME 0x20
+#define LSA_PRIV_GET_DISPNAME 0x21
+#define LSA_DELETEOBJECT 0x22
+#define LSA_ENUMACCTWITHRIGHT 0x23
+#define LSA_ENUMACCTRIGHTS 0x24
+#define LSA_ADDACCTRIGHTS 0x25
+#define LSA_REMOVEACCTRIGHTS 0x26
+#define LSA_QUERYTRUSTDOMINFO 0x27
+#define LSA_SETTRUSTDOMINFO 0x28
+#define LSA_DELETETRUSTDOM 0x29
+#define LSA_STOREPRIVDATA 0x2a
+#define LSA_RETRPRIVDATA 0x2b
+#define LSA_OPENPOLICY2 0x2c
+#define LSA_UNK_GET_CONNUSER 0x2d /* LsaGetConnectedCredentials ? */
+#define LSA_QUERYINFO2 0x2e
+
+/* XXXX these are here to get a compile! */
+#define LSA_LOOKUPRIDS 0xFD
+
+/* DOM_QUERY - info class 3 and 5 LSA Query response */
+typedef struct dom_query_info
+{
+ uint16 uni_dom_max_len; /* domain name string length * 2 */
+ uint16 uni_dom_str_len; /* domain name string length * 2 */
+ uint32 buffer_dom_name; /* undocumented domain name string buffer pointer */
+ uint32 buffer_dom_sid; /* undocumented domain SID string buffer pointer */
+ UNISTR2 uni_domain_name; /* domain name (unicode string) */
+ DOM_SID2 dom_sid; /* domain SID */
+
+} DOM_QUERY;
+
+/* level 5 is same as level 3. */
+typedef DOM_QUERY DOM_QUERY_3;
+typedef DOM_QUERY DOM_QUERY_5;
+
+/* level 2 is auditing settings */
+typedef struct dom_query_2
+{
+ uint32 auditing_enabled;
+ uint32 count1; /* usualy 7, at least on nt4sp4 */
+ uint32 count2; /* the same */
+ uint32 *auditsettings;
+} DOM_QUERY_2;
+
+/* level 6 is server role information */
+typedef struct dom_query_6
+{
+ uint16 server_role; /* 2=backup, 3=primary */
+} DOM_QUERY_6;
+
+typedef struct seq_qos_info
+{
+ uint32 len; /* 12 */
+ uint16 sec_imp_level; /* 0x02 - impersonation level */
+ uint8 sec_ctxt_mode; /* 0x01 - context tracking mode */
+ uint8 effective_only; /* 0x00 - effective only */
+
+} LSA_SEC_QOS;
+
+typedef struct obj_attr_info
+{
+ uint32 len; /* 0x18 - length (in bytes) inc. the length field. */
+ uint32 ptr_root_dir; /* 0 - root directory (pointer) */
+ uint32 ptr_obj_name; /* 0 - object name (pointer) */
+ uint32 attributes; /* 0 - attributes (undocumented) */
+ uint32 ptr_sec_desc; /* 0 - security descriptior (pointer) */
+ uint32 ptr_sec_qos; /* security quality of service */
+ LSA_SEC_QOS *sec_qos;
+
+} LSA_OBJ_ATTR;
+
+/* LSA_Q_OPEN_POL - LSA Query Open Policy */
+typedef struct lsa_q_open_pol_info
+{
+ uint32 ptr; /* undocumented buffer pointer */
+ uint16 system_name; /* 0x5c - system name */
+ LSA_OBJ_ATTR attr ; /* object attributes */
+
+ uint32 des_access; /* desired access attributes */
+
+} LSA_Q_OPEN_POL;
+
+/* LSA_R_OPEN_POL - response to LSA Open Policy */
+typedef struct lsa_r_open_pol_info
+{
+ POLICY_HND pol; /* policy handle */
+ NTSTATUS status; /* return code */
+
+} LSA_R_OPEN_POL;
+
+/* LSA_Q_OPEN_POL2 - LSA Query Open Policy */
+typedef struct lsa_q_open_pol2_info
+{
+ uint32 ptr; /* undocumented buffer pointer */
+ UNISTR2 uni_server_name; /* server name, starting with two '\'s */
+ LSA_OBJ_ATTR attr ; /* object attributes */
+
+ uint32 des_access; /* desired access attributes */
+
+} LSA_Q_OPEN_POL2;
+
+/* LSA_R_OPEN_POL2 - response to LSA Open Policy */
+typedef struct lsa_r_open_pol2_info
+{
+ POLICY_HND pol; /* policy handle */
+ NTSTATUS status; /* return code */
+
+} LSA_R_OPEN_POL2;
+
+
+#define POLICY_VIEW_LOCAL_INFORMATION 0x00000001
+#define POLICY_VIEW_AUDIT_INFORMATION 0x00000002
+#define POLICY_GET_PRIVATE_INFORMATION 0x00000004
+#define POLICY_TRUST_ADMIN 0x00000008
+#define POLICY_CREATE_ACCOUNT 0x00000010
+#define POLICY_CREATE_SECRET 0x00000020
+#define POLICY_CREATE_PRIVILEGE 0x00000040
+#define POLICY_SET_DEFAULT_QUOTA_LIMITS 0x00000080
+#define POLICY_SET_AUDIT_REQUIREMENTS 0x00000100
+#define POLICY_AUDIT_LOG_ADMIN 0x00000200
+#define POLICY_SERVER_ADMIN 0x00000400
+#define POLICY_LOOKUP_NAMES 0x00000800
+
+#define POLICY_ALL_ACCESS ( STANDARD_RIGHTS_REQUIRED_ACCESS |\
+ POLICY_VIEW_LOCAL_INFORMATION |\
+ POLICY_VIEW_AUDIT_INFORMATION |\
+ POLICY_GET_PRIVATE_INFORMATION |\
+ POLICY_TRUST_ADMIN |\
+ POLICY_CREATE_ACCOUNT |\
+ POLICY_CREATE_SECRET |\
+ POLICY_CREATE_PRIVILEGE |\
+ POLICY_SET_DEFAULT_QUOTA_LIMITS |\
+ POLICY_SET_AUDIT_REQUIREMENTS |\
+ POLICY_AUDIT_LOG_ADMIN |\
+ POLICY_SERVER_ADMIN |\
+ POLICY_LOOKUP_NAMES )
+
+
+#define POLICY_READ ( STANDARD_RIGHTS_READ_ACCESS |\
+ POLICY_VIEW_AUDIT_INFORMATION |\
+ POLICY_GET_PRIVATE_INFORMATION)
+
+#define POLICY_WRITE ( STANDARD_RIGHTS_WRITE_ACCESS |\
+ POLICY_TRUST_ADMIN |\
+ POLICY_CREATE_ACCOUNT |\
+ POLICY_CREATE_SECRET |\
+ POLICY_CREATE_PRIVILEGE |\
+ POLICY_SET_DEFAULT_QUOTA_LIMITS |\
+ POLICY_SET_AUDIT_REQUIREMENTS |\
+ POLICY_AUDIT_LOG_ADMIN |\
+ POLICY_SERVER_ADMIN)
+
+#define POLICY_EXECUTE ( STANDARD_RIGHTS_EXECUTE_ACCESS |\
+ POLICY_VIEW_LOCAL_INFORMATION |\
+ POLICY_LOOKUP_NAMES )
+
+/* LSA_Q_QUERY_SEC_OBJ - LSA query security */
+typedef struct lsa_query_sec_obj_info
+{
+ POLICY_HND pol; /* policy handle */
+ uint32 sec_info;
+
+} LSA_Q_QUERY_SEC_OBJ;
+
+/* LSA_R_QUERY_SEC_OBJ - probably an open */
+typedef struct r_lsa_query_sec_obj_info
+{
+ uint32 ptr;
+ SEC_DESC_BUF *buf;
+
+ NTSTATUS status; /* return status */
+
+} LSA_R_QUERY_SEC_OBJ;
+
+/* LSA_Q_QUERY_INFO - LSA query info policy */
+typedef struct lsa_query_info
+{
+ POLICY_HND pol; /* policy handle */
+ uint16 info_class; /* info class */
+
+} LSA_Q_QUERY_INFO;
+
+/* LSA_INFO_UNION */
+typedef union lsa_info_union
+{
+ DOM_QUERY_2 id2;
+ DOM_QUERY_3 id3;
+ DOM_QUERY_5 id5;
+ DOM_QUERY_6 id6;
+} LSA_INFO_UNION;
+
+/* LSA_R_QUERY_INFO - response to LSA query info policy */
+typedef struct lsa_r_query_info
+{
+ uint32 undoc_buffer; /* undocumented buffer pointer */
+ uint16 info_class; /* info class (same as info class in request) */
+
+ LSA_INFO_UNION dom;
+
+ NTSTATUS status; /* return code */
+
+} LSA_R_QUERY_INFO;
+
+/* LSA_DNS_DOM_INFO - DNS domain info - info class 12*/
+typedef struct lsa_dns_dom_info
+{
+ UNIHDR hdr_nb_dom_name; /* netbios domain name */
+ UNIHDR hdr_dns_dom_name;
+ UNIHDR hdr_forest_name;
+
+ struct uuid dom_guid; /* domain GUID */
+
+ UNISTR2 uni_nb_dom_name;
+ UNISTR2 uni_dns_dom_name;
+ UNISTR2 uni_forest_name;
+
+ uint32 ptr_dom_sid;
+ DOM_SID2 dom_sid; /* domain SID */
+} LSA_DNS_DOM_INFO;
+
+typedef union lsa_info2_union
+{
+ LSA_DNS_DOM_INFO dns_dom_info;
+} LSA_INFO2_UNION;
+
+/* LSA_Q_QUERY_INFO2 - LSA query info */
+typedef struct lsa_q_query_info2
+{
+ POLICY_HND pol; /* policy handle */
+ uint16 info_class; /* info class */
+} LSA_Q_QUERY_INFO2;
+
+typedef struct lsa_r_query_info2
+{
+ uint32 ptr; /* pointer to info struct */
+ uint16 info_class;
+ LSA_INFO2_UNION info; /* so far the only one */
+ NTSTATUS status;
+} LSA_R_QUERY_INFO2;
+
+/* LSA_Q_ENUM_TRUST_DOM - LSA enumerate trusted domains */
+typedef struct lsa_enum_trust_dom_info
+{
+ POLICY_HND pol; /* policy handle */
+ uint32 enum_context; /* enumeration context handle */
+ uint32 preferred_len; /* preferred maximum length */
+
+} LSA_Q_ENUM_TRUST_DOM;
+
+/* LSA_R_ENUM_TRUST_DOM - response to LSA enumerate trusted domains */
+typedef struct lsa_r_enum_trust_dom_info
+{
+ uint32 enum_context; /* enumeration context handle */
+ uint32 num_domains; /* number of domains */
+ uint32 ptr_enum_domains; /* buffer pointer to num domains */
+
+ /* this lot is only added if ptr_enum_domains is non-NULL */
+ uint32 num_domains2; /* number of domains */
+ UNIHDR2 *hdr_domain_name;
+ UNISTR2 *uni_domain_name;
+ DOM_SID2 *domain_sid;
+
+ NTSTATUS status; /* return code */
+
+} LSA_R_ENUM_TRUST_DOM;
+
+/* LSA_Q_CLOSE */
+typedef struct lsa_q_close_info
+{
+ POLICY_HND pol; /* policy handle */
+
+} LSA_Q_CLOSE;
+
+/* LSA_R_CLOSE */
+typedef struct lsa_r_close_info
+{
+ POLICY_HND pol; /* policy handle. should be all zeros. */
+
+ NTSTATUS status; /* return code */
+
+} LSA_R_CLOSE;
+
+
+#define MAX_REF_DOMAINS 32
+
+/* DOM_TRUST_HDR */
+typedef struct dom_trust_hdr
+{
+ UNIHDR hdr_dom_name; /* referenced domain unicode string headers */
+ uint32 ptr_dom_sid;
+
+} DOM_TRUST_HDR;
+
+/* DOM_TRUST_INFO */
+typedef struct dom_trust_info
+{
+ UNISTR2 uni_dom_name; /* domain name unicode string */
+ DOM_SID2 ref_dom ; /* referenced domain SID */
+
+} DOM_TRUST_INFO;
+
+/* DOM_R_REF */
+typedef struct dom_ref_info
+{
+ uint32 num_ref_doms_1; /* num referenced domains */
+ uint32 ptr_ref_dom; /* pointer to referenced domains */
+ uint32 max_entries; /* 32 - max number of entries */
+ uint32 num_ref_doms_2; /* num referenced domains */
+
+ DOM_TRUST_HDR hdr_ref_dom[MAX_REF_DOMAINS]; /* referenced domains */
+ DOM_TRUST_INFO ref_dom [MAX_REF_DOMAINS]; /* referenced domains */
+
+} DOM_R_REF;
+
+/* the domain_idx points to a SID associated with the name */
+
+/* LSA_TRANS_NAME - translated name */
+typedef struct lsa_trans_name_info
+{
+ uint16 sid_name_use; /* value is 5 for a well-known group; 2 for a domain group; 1 for a user... */
+ UNIHDR hdr_name;
+ uint32 domain_idx; /* index into DOM_R_REF array of SIDs */
+
+} LSA_TRANS_NAME;
+
+/* This number purly arbitary - just to prevent a client from requesting large amounts of memory */
+#define MAX_LOOKUP_SIDS 256
+
+/* LSA_TRANS_NAME_ENUM - LSA Translated Name Enumeration container */
+typedef struct lsa_trans_name_enum_info
+{
+ uint32 num_entries;
+ uint32 ptr_trans_names;
+ uint32 num_entries2;
+
+ LSA_TRANS_NAME *name; /* translated names */
+ UNISTR2 *uni_name;
+
+} LSA_TRANS_NAME_ENUM;
+
+/* LSA_SID_ENUM - LSA SID enumeration container */
+typedef struct lsa_sid_enum_info
+{
+ uint32 num_entries;
+ uint32 ptr_sid_enum;
+ uint32 num_entries2;
+
+ uint32 *ptr_sid; /* domain SID pointers to be looked up. */
+ DOM_SID2 *sid; /* domain SIDs to be looked up. */
+
+} LSA_SID_ENUM;
+
+/* LSA_Q_LOOKUP_SIDS - LSA Lookup SIDs */
+typedef struct lsa_q_lookup_sids
+{
+ POLICY_HND pol; /* policy handle */
+ LSA_SID_ENUM sids;
+ LSA_TRANS_NAME_ENUM names;
+ LOOKUP_LEVEL level;
+ uint32 mapped_count;
+
+} LSA_Q_LOOKUP_SIDS;
+
+/* LSA_R_LOOKUP_SIDS - response to LSA Lookup SIDs */
+typedef struct lsa_r_lookup_sids
+{
+ uint32 ptr_dom_ref;
+ DOM_R_REF *dom_ref; /* domain reference info */
+
+ LSA_TRANS_NAME_ENUM *names;
+ uint32 mapped_count;
+
+ NTSTATUS status; /* return code */
+
+} LSA_R_LOOKUP_SIDS;
+
+/* LSA_Q_LOOKUP_NAMES - LSA Lookup NAMEs */
+typedef struct lsa_q_lookup_names
+{
+ POLICY_HND pol; /* policy handle */
+ uint32 num_entries;
+ uint32 num_entries2;
+ UNIHDR *hdr_name; /* name buffer pointers */
+ UNISTR2 *uni_name; /* names to be looked up */
+
+ uint32 num_trans_entries;
+ uint32 ptr_trans_sids; /* undocumented domain SID buffer pointer */
+ uint32 lookup_level;
+ uint32 mapped_count;
+
+} LSA_Q_LOOKUP_NAMES;
+
+/* LSA_R_LOOKUP_NAMES - response to LSA Lookup NAMEs by name */
+typedef struct lsa_r_lookup_names
+{
+ uint32 ptr_dom_ref;
+ DOM_R_REF *dom_ref; /* domain reference info */
+
+ uint32 num_entries;
+ uint32 ptr_entries;
+ uint32 num_entries2;
+ DOM_RID2 *dom_rid; /* domain RIDs being looked up */
+
+ uint32 mapped_count;
+
+ NTSTATUS status; /* return code */
+} LSA_R_LOOKUP_NAMES;
+
+/* This is probably a policy handle but at the moment we
+ never read it - so use a dummy struct. */
+
+typedef struct lsa_q_open_secret
+{
+ uint32 dummy;
+} LSA_Q_OPEN_SECRET;
+
+/* We always return "not found" at present - so just marshal the minimum. */
+
+typedef struct lsa_r_open_secret
+{
+ uint32 dummy1;
+ uint32 dummy2;
+ uint32 dummy3;
+ uint32 dummy4;
+ NTSTATUS status;
+} LSA_R_OPEN_SECRET;
+
+typedef struct lsa_enum_priv_entry
+{
+ UNIHDR hdr_name;
+ uint32 luid_low;
+ uint32 luid_high;
+ UNISTR2 name;
+
+} LSA_PRIV_ENTRY;
+
+/* LSA_Q_ENUM_PRIVS - LSA enum privileges */
+typedef struct lsa_q_enum_privs
+{
+ POLICY_HND pol; /* policy handle */
+ uint32 enum_context;
+ uint32 pref_max_length;
+} LSA_Q_ENUM_PRIVS;
+
+typedef struct lsa_r_enum_privs
+{
+ uint32 enum_context;
+ uint32 count;
+ uint32 ptr;
+ uint32 count1;
+
+ LSA_PRIV_ENTRY *privs;
+
+ NTSTATUS status;
+} LSA_R_ENUM_PRIVS;
+
+/* LSA_Q_ENUM_ACCT_RIGHTS - LSA enum account rights */
+typedef struct
+{
+ POLICY_HND pol; /* policy handle */
+ DOM_SID2 sid;
+} LSA_Q_ENUM_ACCT_RIGHTS;
+
+/* LSA_R_ENUM_ACCT_RIGHTS - LSA enum account rights */
+typedef struct
+{
+ uint32 count;
+ UNISTR2_ARRAY rights;
+ NTSTATUS status;
+} LSA_R_ENUM_ACCT_RIGHTS;
+
+
+/* LSA_Q_ADD_ACCT_RIGHTS - LSA add account rights */
+typedef struct
+{
+ POLICY_HND pol; /* policy handle */
+ DOM_SID2 sid;
+ UNISTR2_ARRAY rights;
+ uint32 count;
+} LSA_Q_ADD_ACCT_RIGHTS;
+
+/* LSA_R_ADD_ACCT_RIGHTS - LSA add account rights */
+typedef struct
+{
+ NTSTATUS status;
+} LSA_R_ADD_ACCT_RIGHTS;
+
+
+/* LSA_Q_REMOVE_ACCT_RIGHTS - LSA remove account rights */
+typedef struct
+{
+ POLICY_HND pol; /* policy handle */
+ DOM_SID2 sid;
+ uint32 removeall;
+ UNISTR2_ARRAY rights;
+ uint32 count;
+} LSA_Q_REMOVE_ACCT_RIGHTS;
+
+/* LSA_R_REMOVE_ACCT_RIGHTS - LSA remove account rights */
+typedef struct
+{
+ NTSTATUS status;
+} LSA_R_REMOVE_ACCT_RIGHTS;
+
+
+/* LSA_Q_PRIV_GET_DISPNAME - LSA get privilege display name */
+typedef struct lsa_q_priv_get_dispname
+{
+ POLICY_HND pol; /* policy handle */
+ UNIHDR hdr_name;
+ UNISTR2 name;
+ uint16 lang_id;
+ uint16 lang_id_sys;
+} LSA_Q_PRIV_GET_DISPNAME;
+
+typedef struct lsa_r_priv_get_dispname
+{
+ uint32 ptr_info;
+ UNIHDR hdr_desc;
+ UNISTR2 desc;
+ /* Don't align ! */
+ uint16 lang_id;
+ /* align */
+ NTSTATUS status;
+} LSA_R_PRIV_GET_DISPNAME;
+
+/* LSA_Q_ENUM_ACCOUNTS */
+typedef struct lsa_q_enum_accounts
+{
+ POLICY_HND pol; /* policy handle */
+ uint32 enum_context;
+ uint32 pref_max_length;
+} LSA_Q_ENUM_ACCOUNTS;
+
+/* LSA_R_ENUM_ACCOUNTS */
+typedef struct lsa_r_enum_accounts
+{
+ uint32 enum_context;
+ LSA_SID_ENUM sids;
+ NTSTATUS status;
+} LSA_R_ENUM_ACCOUNTS;
+
+/* LSA_Q_UNK_GET_CONNUSER - gets username\domain of connected user
+ called when "Take Ownership" is clicked -SK */
+typedef struct lsa_q_unk_get_connuser
+{
+ uint32 ptr_srvname;
+ UNISTR2 uni2_srvname;
+ uint32 unk1; /* 3 unknown uint32's are seen right after uni2_srvname */
+ uint32 unk2; /* unk2 appears to be a ptr, unk1 = unk3 = 0 usually */
+ uint32 unk3;
+} LSA_Q_UNK_GET_CONNUSER;
+
+/* LSA_R_UNK_GET_CONNUSER */
+typedef struct lsa_r_unk_get_connuser
+{
+ uint32 ptr_user_name;
+ UNIHDR hdr_user_name;
+ UNISTR2 uni2_user_name;
+
+ uint32 unk1;
+
+ uint32 ptr_dom_name;
+ UNIHDR hdr_dom_name;
+ UNISTR2 uni2_dom_name;
+
+ NTSTATUS status;
+} LSA_R_UNK_GET_CONNUSER;
+
+
+typedef struct lsa_q_createaccount
+{
+ POLICY_HND pol; /* policy handle */
+ DOM_SID2 sid;
+ uint32 access; /* access */
+} LSA_Q_CREATEACCOUNT;
+
+typedef struct lsa_r_createaccount
+{
+ POLICY_HND pol; /* policy handle */
+ NTSTATUS status;
+} LSA_R_CREATEACCOUNT;
+
+
+typedef struct lsa_q_openaccount
+{
+ POLICY_HND pol; /* policy handle */
+ DOM_SID2 sid;
+ uint32 access; /* desired access */
+} LSA_Q_OPENACCOUNT;
+
+typedef struct lsa_r_openaccount
+{
+ POLICY_HND pol; /* policy handle */
+ NTSTATUS status;
+} LSA_R_OPENACCOUNT;
+
+typedef struct lsa_q_enumprivsaccount
+{
+ POLICY_HND pol; /* policy handle */
+} LSA_Q_ENUMPRIVSACCOUNT;
+
+typedef struct lsa_r_enumprivsaccount
+{
+ uint32 ptr;
+ uint32 count;
+ PRIVILEGE_SET *set;
+ NTSTATUS status;
+} LSA_R_ENUMPRIVSACCOUNT;
+
+typedef struct lsa_q_getsystemaccount
+{
+ POLICY_HND pol; /* policy handle */
+} LSA_Q_GETSYSTEMACCOUNT;
+
+typedef struct lsa_r_getsystemaccount
+{
+ uint32 access;
+ NTSTATUS status;
+} LSA_R_GETSYSTEMACCOUNT;
+
+
+typedef struct lsa_q_setsystemaccount
+{
+ POLICY_HND pol; /* policy handle */
+ uint32 access;
+} LSA_Q_SETSYSTEMACCOUNT;
+
+typedef struct lsa_r_setsystemaccount
+{
+ NTSTATUS status;
+} LSA_R_SETSYSTEMACCOUNT;
+
+
+typedef struct lsa_q_lookupprivvalue
+{
+ POLICY_HND pol; /* policy handle */
+ UNIHDR hdr_right;
+ UNISTR2 uni2_right;
+} LSA_Q_LOOKUPPRIVVALUE;
+
+typedef struct lsa_r_lookupprivvalue
+{
+ LUID luid;
+ NTSTATUS status;
+} LSA_R_LOOKUPPRIVVALUE;
+
+
+typedef struct lsa_q_addprivs
+{
+ POLICY_HND pol; /* policy handle */
+ uint32 count;
+ PRIVILEGE_SET *set;
+} LSA_Q_ADDPRIVS;
+
+typedef struct lsa_r_addprivs
+{
+ NTSTATUS status;
+} LSA_R_ADDPRIVS;
+
+
+typedef struct lsa_q_removeprivs
+{
+ POLICY_HND pol; /* policy handle */
+ uint32 allrights;
+ uint32 ptr;
+ uint32 count;
+ PRIVILEGE_SET *set;
+} LSA_Q_REMOVEPRIVS;
+
+typedef struct lsa_r_removeprivs
+{
+ NTSTATUS status;
+} LSA_R_REMOVEPRIVS;
+
+
+#endif /* _RPC_LSA_H */
diff --git a/source/include/rpc_misc.h b/source/include/rpc_misc.h
new file mode 100644
index 00000000000..0c6eee3650a
--- /dev/null
+++ b/source/include/rpc_misc.h
@@ -0,0 +1,408 @@
+/*
+ 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 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 "ntdomain.h"
+#include "rpc_dce.h"
+
+#ifndef _RPC_MISC_H /* _RPC_MISC_H */
+#define _RPC_MISC_H
+
+#define SMB_RPC_INTERFACE_VERSION 1
+
+/* well-known RIDs - Relative IDs */
+
+/* RIDs - Well-known users ... */
+#define DOMAIN_USER_RID_ADMIN (0x000001F4L)
+#define DOMAIN_USER_RID_GUEST (0x000001F5L)
+#define DOMAIN_USER_RID_KRBTGT (0x000001F6L)
+
+/* RIDs - well-known groups ... */
+#define DOMAIN_GROUP_RID_ADMINS (0x00000200L)
+#define DOMAIN_GROUP_RID_USERS (0x00000201L)
+#define DOMAIN_GROUP_RID_GUESTS (0x00000202L)
+#define DOMAIN_GROUP_RID_COMPUTERS (0x00000203L)
+
+#define DOMAIN_GROUP_RID_CONTROLLERS (0x00000204L)
+#define DOMAIN_GROUP_RID_CERT_ADMINS (0x00000205L)
+#define DOMAIN_GROUP_RID_SCHEMA_ADMINS (0x00000206L)
+#define DOMAIN_GROUP_RID_ENTERPRISE_ADMINS (0x00000207L)
+
+/* is the following the right number? I bet it is --simo
+#define DOMAIN_GROUP_RID_POLICY_ADMINS (0x00000208L)
+*/
+
+/* RIDs - well-known aliases ... */
+#define BUILTIN_ALIAS_RID_ADMINS (0x00000220L)
+#define BUILTIN_ALIAS_RID_USERS (0x00000221L)
+#define BUILTIN_ALIAS_RID_GUESTS (0x00000222L)
+#define BUILTIN_ALIAS_RID_POWER_USERS (0x00000223L)
+
+#define BUILTIN_ALIAS_RID_ACCOUNT_OPS (0x00000224L)
+#define BUILTIN_ALIAS_RID_SYSTEM_OPS (0x00000225L)
+#define BUILTIN_ALIAS_RID_PRINT_OPS (0x00000226L)
+#define BUILTIN_ALIAS_RID_BACKUP_OPS (0x00000227L)
+
+#define BUILTIN_ALIAS_RID_REPLICATOR (0x00000228L)
+#define BUILTIN_ALIAS_RID_RAS_SERVERS (0x00000229L)
+#define BUILTIN_ALIAS_RID_PRE_2K_ACCESS (0x0000022aL)
+
+/*
+ * Masks for mappings between unix uid and gid types and
+ * NT RIDS.
+ */
+
+
+#define BASE_RID (0x000003E8L)
+
+/* Take the bottom bit. */
+#define RID_TYPE_MASK 1
+#define RID_MULTIPLIER 2
+
+/* The two common types. */
+#define USER_RID_TYPE 0
+#define GROUP_RID_TYPE 1
+
+/* ENUM_HND */
+typedef struct enum_hnd_info
+{
+ uint32 ptr_hnd; /* pointer to enumeration handle */
+ uint32 handle; /* enumeration handle */
+} ENUM_HND;
+
+/* LOOKUP_LEVEL - switch value */
+typedef struct lookup_level_info
+{
+ uint16 value;
+} LOOKUP_LEVEL;
+
+/* DOM_SID2 - security id */
+typedef struct sid_info_2
+{
+ uint32 num_auths; /* length, bytes, including length of len :-) */
+ DOM_SID sid;
+} DOM_SID2;
+
+/* STRHDR - string header */
+typedef struct header_info
+{
+ uint16 str_str_len;
+ uint16 str_max_len;
+ uint32 buffer; /* non-zero */
+} STRHDR;
+
+/* UNIHDR - unicode string header */
+typedef struct unihdr_info
+{
+ uint16 uni_str_len;
+ uint16 uni_max_len;
+ uint32 buffer; /* usually has a value of 4 */
+} UNIHDR;
+
+/* UNIHDR2 - unicode string header and undocumented buffer */
+typedef struct unihdr2_info
+{
+ UNIHDR unihdr;
+ uint32 buffer; /* 32 bit buffer pointer */
+} UNIHDR2;
+
+/* clueless as to what maximum length should be */
+#define MAX_UNISTRLEN 256
+#define MAX_STRINGLEN 256
+#define MAX_BUFFERLEN 512
+
+/* UNISTR - unicode string size and buffer */
+typedef struct unistr_info
+{
+ /* unicode characters. ***MUST*** be little-endian. ***MUST*** be null-terminated */
+ uint16 *buffer;
+} UNISTR;
+
+/* BUFHDR - buffer header */
+typedef struct bufhdr_info
+{
+ uint32 buf_max_len;
+ uint32 buf_len;
+} BUFHDR;
+
+/* BUFFER2 - unicode string, size (in uint8 ascii chars) and buffer */
+/* pathetic. some stupid team of \PIPE\winreg writers got the concept */
+/* of a unicode string different from the other \PIPE\ writers */
+typedef struct buffer2_info
+{
+ uint32 buf_max_len;
+ uint32 offset;
+ uint32 buf_len;
+ /* unicode characters. ***MUST*** be little-endian. **NOT** necessarily null-terminated */
+ uint16 *buffer;
+} BUFFER2;
+
+/* BUFFER3 */
+typedef struct buffer3_info
+{
+ uint32 buf_max_len;
+ uint8 *buffer; /* Data */
+ uint32 buf_len;
+} BUFFER3;
+
+/* BUFFER5 */
+typedef struct buffer5_info
+{
+ uint32 buf_len;
+ uint16 *buffer; /* data */
+} BUFFER5;
+
+/* UNISTR2 - unicode string size (in uint16 unicode chars) and buffer */
+typedef struct unistr2_info
+{
+ uint32 uni_max_len;
+ uint32 offset;
+ uint32 uni_str_len;
+ /* unicode characters. ***MUST*** be little-endian.
+ **must** be null-terminated and the uni_str_len should include
+ the NULL character */
+ uint16 *buffer;
+} UNISTR2;
+
+/* STRING2 - string size (in uint8 chars) and buffer */
+typedef struct string2_info
+{
+ uint32 str_max_len;
+ uint32 offset;
+ uint32 str_str_len;
+ uint8 *buffer; /* uint8 characters. **NOT** necessarily null-terminated */
+} STRING2;
+
+/* UNISTR3 - XXXX not sure about this structure */
+typedef struct unistr3_info
+{
+ uint32 uni_str_len;
+ UNISTR str;
+
+} UNISTR3;
+
+/* an element in a unicode string array */
+typedef struct
+{
+ uint16 length;
+ uint16 size;
+ uint32 ref_id;
+ UNISTR2 string;
+} UNISTR2_ARRAY_EL;
+
+/* an array of unicode strings */
+typedef struct
+{
+ uint32 ref_id;
+ uint32 count;
+ UNISTR2_ARRAY_EL *strings;
+} UNISTR2_ARRAY;
+
+
+/* an element in a sid array */
+typedef struct
+{
+ uint32 ref_id;
+ DOM_SID2 sid;
+} SID_ARRAY_EL;
+
+/* an array of sids */
+typedef struct
+{
+ uint32 ref_id;
+ uint32 count;
+ SID_ARRAY_EL *sids;
+} SID_ARRAY;
+
+/* DOM_RID2 - domain RID structure for ntlsa pipe */
+typedef struct domrid2_info
+{
+ uint8 type; /* value is SID_NAME_USE enum */
+ uint32 rid;
+ uint32 rid_idx; /* referenced domain index */
+
+} DOM_RID2;
+
+/* DOM_RID3 - domain RID structure for samr pipe */
+typedef struct domrid3_info
+{
+ uint32 rid; /* domain-relative (to a SID) id */
+ uint32 type1; /* value is 0x1 */
+ uint32 ptr_type; /* undocumented pointer */
+ uint32 type2; /* value is 0x1 */
+ uint32 unk; /* value is 0x2 */
+
+} DOM_RID3;
+
+/* DOM_RID4 - rid + user attributes */
+typedef struct domrid4_info
+{
+ uint32 unknown;
+ uint16 attr;
+ uint32 rid; /* user RID */
+} DOM_RID4;
+
+/* DOM_CLNT_SRV - client / server names */
+typedef struct clnt_srv_info
+{
+ uint32 undoc_buffer; /* undocumented 32 bit buffer pointer */
+ UNISTR2 uni_logon_srv; /* logon server name */
+ uint32 undoc_buffer2; /* undocumented 32 bit buffer pointer */
+ UNISTR2 uni_comp_name; /* client machine name */
+} DOM_CLNT_SRV;
+
+/* DOM_LOG_INFO - login info */
+typedef struct log_info
+{
+ uint32 undoc_buffer; /* undocumented 32 bit buffer pointer */
+ UNISTR2 uni_logon_srv; /* logon server name */
+ UNISTR2 uni_acct_name; /* account name */
+ uint16 sec_chan; /* secure channel type */
+ UNISTR2 uni_comp_name; /* client machine name */
+} DOM_LOG_INFO;
+
+/* DOM_CHAL - challenge info */
+typedef struct chal_info
+{
+ uchar data[8]; /* credentials */
+} DOM_CHAL;
+
+/* DOM_CREDs - timestamped client or server credentials */
+typedef struct cred_info
+{
+ DOM_CHAL challenge; /* credentials */
+ UTIME timestamp; /* credential time-stamp */
+} DOM_CRED;
+
+/* DOM_CLNT_INFO - client info */
+typedef struct clnt_info
+{
+ DOM_LOG_INFO login;
+ DOM_CRED cred;
+} DOM_CLNT_INFO;
+
+/* DOM_CLNT_INFO2 - client info */
+typedef struct clnt_info2
+{
+ DOM_CLNT_SRV login;
+ uint32 ptr_cred;
+ DOM_CRED cred;
+} DOM_CLNT_INFO2;
+
+/* DOM_LOGON_ID - logon id */
+typedef struct logon_info
+{
+ uint32 low;
+ uint32 high;
+} DOM_LOGON_ID;
+
+/* OWF INFO */
+typedef struct owf_info
+{
+ uint8 data[16];
+} OWF_INFO;
+
+
+/* DOM_GID - group id + user attributes */
+typedef struct gid_info
+{
+ uint32 g_rid; /* a group RID */
+ uint32 attr;
+} DOM_GID;
+
+/* POLICY_HND */
+typedef struct lsa_policy_info
+{
+ uint32 data1;
+ uint32 data2;
+ uint16 data3;
+ uint16 data4;
+ uint8 data5[8];
+#ifdef __INSURE__
+
+ /* To prevent the leakage of policy handles mallocate a bit of
+ memory when a policy handle is created and free it when the
+ handle is closed. This should cause Insure to flag an error
+ when policy handles are overwritten or fall out of scope without
+ being freed. */
+
+ char *marker;
+#endif
+} POLICY_HND;
+
+/*
+ * A client connection's state, pipe name,
+ * user credentials, etc...
+ */
+typedef struct _cli_auth_fns cli_auth_fns;
+struct user_creds;
+struct cli_connection {
+
+ char *srv_name;
+ char *pipe_name;
+ struct user_creds usr_creds;
+
+ struct cli_state *pCli_state;
+
+ cli_auth_fns *auth;
+
+ void *auth_info;
+ void *auth_creds;
+};
+
+
+/*
+ * Associate a POLICY_HND with a cli_connection
+ */
+typedef struct rpc_hnd_node {
+
+ POLICY_HND hnd;
+ struct cli_connection *cli;
+
+} RPC_HND_NODE;
+
+typedef struct uint64_s
+{
+ uint32 low;
+ uint32 high;
+} UINT64_S;
+
+/* BUFHDR2 - another buffer header, with info level */
+typedef struct bufhdr2_info
+{
+ uint32 info_level;
+ uint32 length; /* uint8 chars */
+ uint32 buffer;
+
+}
+BUFHDR2;
+
+/* BUFFER4 - simple length and buffer */
+typedef struct buffer4_info
+{
+ uint32 buf_len;
+ uint8 buffer[MAX_BUFFERLEN];
+
+}
+BUFFER4;
+
+enum unistr2_term_codes { UNI_FLAGS_NONE = 0, UNI_STR_TERMINATE = 1, UNI_MAXLEN_TERMINATE = 2, UNI_BROKEN_NON_NULL = 3 };
+#endif /* _RPC_MISC_H */
diff --git a/source/include/rpc_netlogon.h b/source/include/rpc_netlogon.h
new file mode 100644
index 00000000000..a5b93b0238a
--- /dev/null
+++ b/source/include/rpc_netlogon.h
@@ -0,0 +1,930 @@
+/*
+ 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
+ Copyright (C) Jean François Micouleau 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 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 _RPC_NETLOGON_H /* _RPC_NETLOGON_H */
+#define _RPC_NETLOGON_H
+
+
+/* NETLOGON pipe */
+#define NET_SAMLOGON 0x02
+#define NET_SAMLOGOFF 0x03
+#define NET_REQCHAL 0x04
+#define NET_AUTH 0x05
+#define NET_SRVPWSET 0x06
+#define NET_SAM_DELTAS 0x07
+#define NET_LOGON_CTRL 0x0c
+#define NET_GETDCNAME 0x0d
+#define NET_AUTH2 0x0f
+#define NET_LOGON_CTRL2 0x0e
+#define NET_SAM_SYNC 0x10
+#define NET_TRUST_DOM_LIST 0x13
+#define NET_AUTH3 0x1a
+
+/* Secure Channel types. used in NetrServerAuthenticate negotiation */
+#define SEC_CHAN_WKSTA 2
+#define SEC_CHAN_DOMAIN 4
+#define SEC_CHAN_BDC 6
+
+/* Returned delta types */
+#define SAM_DELTA_DOMAIN_INFO 0x01
+#define SAM_DELTA_GROUP_INFO 0x02
+#define SAM_DELTA_RENAME_GROUP 0x04
+#define SAM_DELTA_ACCOUNT_INFO 0x05
+#define SAM_DELTA_RENAME_USER 0x07
+#define SAM_DELTA_GROUP_MEM 0x08
+#define SAM_DELTA_ALIAS_INFO 0x09
+#define SAM_DELTA_RENAME_ALIAS 0x0b
+#define SAM_DELTA_ALIAS_MEM 0x0c
+#define SAM_DELTA_POLICY_INFO 0x0d
+#define SAM_DELTA_TRUST_DOMS 0x0e
+#define SAM_DELTA_PRIVS_INFO 0x10 /* DT_DELTA_ACCOUNTS */
+#define SAM_DELTA_SECRET_INFO 0x12
+#define SAM_DELTA_DELETE_GROUP 0x14
+#define SAM_DELTA_DELETE_USER 0x15
+#define SAM_DELTA_MODIFIED_COUNT 0x16
+
+/* SAM database types */
+#define SAM_DATABASE_DOMAIN 0x00 /* Domain users and groups */
+#define SAM_DATABASE_BUILTIN 0x01 /* BUILTIN users and groups */
+#define SAM_DATABASE_PRIVS 0x02 /* Privileges */
+
+#if 0
+/* I think this is correct - it's what gets parsed on the wire. JRA. */
+/* NET_USER_INFO_2 */
+typedef struct net_user_info_2
+{
+ uint32 ptr_user_info;
+
+ NTTIME logon_time; /* logon time */
+ NTTIME logoff_time; /* logoff time */
+ NTTIME kickoff_time; /* kickoff time */
+ NTTIME pass_last_set_time; /* password last set time */
+ NTTIME pass_can_change_time; /* password can change time */
+ NTTIME pass_must_change_time; /* password must change time */
+
+ UNIHDR hdr_user_name; /* username unicode string header */
+ UNIHDR hdr_full_name; /* user's full name unicode string header */
+ UNIHDR hdr_logon_script; /* logon script unicode string header */
+ UNIHDR hdr_profile_path; /* profile path unicode string header */
+ UNIHDR hdr_home_dir; /* home directory unicode string header */
+ UNIHDR hdr_dir_drive; /* home directory drive unicode string header */
+
+ uint16 logon_count; /* logon count */
+ uint16 bad_pw_count; /* bad password count */
+
+ uint32 user_id; /* User ID */
+ uint32 group_id; /* Group ID */
+ uint32 num_groups; /* num groups */
+ uint32 buffer_groups; /* undocumented buffer pointer to groups. */
+ uint32 user_flgs; /* user flags */
+
+ uint8 user_sess_key[16]; /* unused user session key */
+
+ UNIHDR hdr_logon_srv; /* logon server unicode string header */
+ UNIHDR hdr_logon_dom; /* logon domain unicode string header */
+
+ uint32 buffer_dom_id; /* undocumented logon domain id pointer */
+ uint8 padding[40]; /* unused padding bytes. expansion room */
+
+ UNISTR2 uni_user_name; /* username unicode string */
+ UNISTR2 uni_full_name; /* user's full name unicode string */
+ UNISTR2 uni_logon_script; /* logon script unicode string */
+ UNISTR2 uni_profile_path; /* profile path unicode string */
+ UNISTR2 uni_home_dir; /* home directory unicode string */
+ UNISTR2 uni_dir_drive; /* home directory drive unicode string */
+
+ uint32 num_groups2; /* num groups */
+ DOM_GID *gids; /* group info */
+
+ UNISTR2 uni_logon_srv; /* logon server unicode string */
+ UNISTR2 uni_logon_dom; /* logon domain unicode string */
+
+ DOM_SID2 dom_sid; /* domain SID */
+
+ uint32 num_other_groups; /* other groups */
+ DOM_GID *other_gids; /* group info */
+ DOM_SID2 *other_sids; /* undocumented - domain SIDs */
+
+} NET_USER_INFO_2;
+#endif
+
+/* NET_USER_INFO_3 */
+typedef struct net_user_info_3
+{
+ uint32 ptr_user_info;
+
+ NTTIME logon_time; /* logon time */
+ NTTIME logoff_time; /* logoff time */
+ NTTIME kickoff_time; /* kickoff time */
+ NTTIME pass_last_set_time; /* password last set time */
+ NTTIME pass_can_change_time; /* password can change time */
+ NTTIME pass_must_change_time; /* password must change time */
+
+ UNIHDR hdr_user_name; /* username unicode string header */
+ UNIHDR hdr_full_name; /* user's full name unicode string header */
+ UNIHDR hdr_logon_script; /* logon script unicode string header */
+ UNIHDR hdr_profile_path; /* profile path unicode string header */
+ UNIHDR hdr_home_dir; /* home directory unicode string header */
+ UNIHDR hdr_dir_drive; /* home directory drive unicode string header */
+
+ uint16 logon_count; /* logon count */
+ uint16 bad_pw_count; /* bad password count */
+
+ uint32 user_rid; /* User RID */
+ uint32 group_rid; /* Group RID */
+
+ uint32 num_groups; /* num groups */
+ uint32 buffer_groups; /* undocumented buffer pointer to groups. */
+ uint32 user_flgs; /* user flags */
+
+ uint8 user_sess_key[16]; /* user session key */
+
+ UNIHDR hdr_logon_srv; /* logon server unicode string header */
+ UNIHDR hdr_logon_dom; /* logon domain unicode string header */
+
+ uint32 buffer_dom_id; /* undocumented logon domain id pointer */
+ uint8 padding[40]; /* unused padding bytes. expansion room */
+
+ uint32 num_other_sids; /* number of foreign/trusted domain sids */
+ uint32 buffer_other_sids;
+
+ UNISTR2 uni_user_name; /* username unicode string */
+ UNISTR2 uni_full_name; /* user's full name unicode string */
+ UNISTR2 uni_logon_script; /* logon script unicode string */
+ UNISTR2 uni_profile_path; /* profile path unicode string */
+ UNISTR2 uni_home_dir; /* home directory unicode string */
+ UNISTR2 uni_dir_drive; /* home directory drive unicode string */
+
+ uint32 num_groups2; /* num groups */
+ DOM_GID *gids; /* group info */
+
+ UNISTR2 uni_logon_srv; /* logon server unicode string */
+ UNISTR2 uni_logon_dom; /* logon domain unicode string */
+
+ DOM_SID2 dom_sid; /* domain SID */
+
+ uint32 num_other_groups; /* other groups */
+ DOM_GID *other_gids; /* group info */
+ DOM_SID2 *other_sids; /* foreign/trusted domain SIDs */
+
+} NET_USER_INFO_3;
+
+
+/* NETLOGON_INFO_1 - pdc status info, i presume */
+typedef struct netlogon_1_info
+{
+ uint32 flags; /* 0x0 - undocumented */
+ uint32 pdc_status; /* 0x0 - undocumented */
+
+} NETLOGON_INFO_1;
+
+/* NETLOGON_INFO_2 - pdc status info, plus trusted domain info */
+typedef struct netlogon_2_info
+{
+ uint32 flags; /* 0x0 - undocumented */
+ uint32 pdc_status; /* 0x0 - undocumented */
+ uint32 ptr_trusted_dc_name; /* pointer to trusted domain controller name */
+ uint32 tc_status; /* 0x051f - ERROR_NO_LOGON_SERVERS */
+ UNISTR2 uni_trusted_dc_name; /* unicode string - trusted dc name */
+
+} NETLOGON_INFO_2;
+
+/* NETLOGON_INFO_3 - logon status info, i presume */
+typedef struct netlogon_3_info
+{
+ uint32 flags; /* 0x0 - undocumented */
+ uint32 logon_attempts; /* number of logon attempts */
+ uint32 reserved_1; /* 0x0 - undocumented */
+ uint32 reserved_2; /* 0x0 - undocumented */
+ uint32 reserved_3; /* 0x0 - undocumented */
+ uint32 reserved_4; /* 0x0 - undocumented */
+ uint32 reserved_5; /* 0x0 - undocumented */
+
+} NETLOGON_INFO_3;
+
+/********************************************************
+ Logon Control Query
+
+ This is generated by a nltest /bdc_query:DOMAIN
+
+ query_level 0x1, function_code 0x1
+
+ ********************************************************/
+
+/* NET_Q_LOGON_CTRL - LSA Netr Logon Control */
+
+typedef struct net_q_logon_ctrl_info
+{
+ uint32 ptr;
+ UNISTR2 uni_server_name;
+ uint32 function_code;
+ uint32 query_level;
+} NET_Q_LOGON_CTRL;
+
+/* NET_R_LOGON_CTRL - LSA Netr Logon Control */
+
+typedef struct net_r_logon_ctrl_info
+{
+ uint32 switch_value;
+ uint32 ptr;
+
+ union {
+ NETLOGON_INFO_1 info1;
+ } logon;
+
+ NTSTATUS status;
+} NET_R_LOGON_CTRL;
+
+/********************************************************
+ Logon Control2 Query
+
+ query_level 0x1 - pdc status
+ query_level 0x3 - number of logon attempts.
+
+ ********************************************************/
+
+/* NET_Q_LOGON_CTRL2 - LSA Netr Logon Control 2 */
+typedef struct net_q_logon_ctrl2_info
+{
+ uint32 ptr; /* undocumented buffer pointer */
+ UNISTR2 uni_server_name; /* server name, starting with two '\'s */
+
+ uint32 function_code; /* 0x1 */
+ uint32 query_level; /* 0x1, 0x3 */
+ uint32 switch_value; /* 0x1 */
+
+} NET_Q_LOGON_CTRL2;
+
+/*******************************************************
+ Logon Control Response
+
+ switch_value is same as query_level in request
+ *******************************************************/
+
+/* NET_R_LOGON_CTRL2 - response to LSA Logon Control2 */
+typedef struct net_r_logon_ctrl2_info
+{
+ uint32 switch_value; /* 0x1, 0x3 */
+ uint32 ptr;
+
+ union
+ {
+ NETLOGON_INFO_1 info1;
+ NETLOGON_INFO_2 info2;
+ NETLOGON_INFO_3 info3;
+
+ } logon;
+
+ NTSTATUS status; /* return code */
+
+} NET_R_LOGON_CTRL2;
+
+/* NET_Q_GETDCNAME - Ask a DC for a trusted DC name */
+
+typedef struct net_q_getdcname
+{
+ uint32 ptr_logon_server;
+ UNISTR2 uni_logon_server;
+ uint32 ptr_domainname;
+ UNISTR2 uni_domainname;
+} NET_Q_GETDCNAME;
+
+/* NET_R_GETDCNAME - Ask a DC for a trusted DC name */
+
+typedef struct net_r_getdcname
+{
+ uint32 ptr_dcname;
+ UNISTR2 uni_dcname;
+ NTSTATUS status;
+} NET_R_GETDCNAME;
+
+/* NET_Q_TRUST_DOM_LIST - LSA Query Trusted Domains */
+typedef struct net_q_trust_dom_info
+{
+ uint32 ptr; /* undocumented buffer pointer */
+ UNISTR2 uni_server_name; /* server name, starting with two '\'s */
+
+} NET_Q_TRUST_DOM_LIST;
+
+#define MAX_TRUST_DOMS 1
+
+/* NET_R_TRUST_DOM_LIST - response to LSA Trusted Domains */
+typedef struct net_r_trust_dom_info
+{
+ UNISTR2 uni_trust_dom_name[MAX_TRUST_DOMS];
+
+ NTSTATUS status; /* return code */
+
+} NET_R_TRUST_DOM_LIST;
+
+
+/* NEG_FLAGS */
+typedef struct neg_flags_info
+{
+ uint32 neg_flags; /* negotiated flags */
+
+} NEG_FLAGS;
+
+
+/* NET_Q_REQ_CHAL */
+typedef struct net_q_req_chal_info
+{
+ uint32 undoc_buffer; /* undocumented buffer pointer */
+ UNISTR2 uni_logon_srv; /* logon server unicode string */
+ UNISTR2 uni_logon_clnt; /* logon client unicode string */
+ DOM_CHAL clnt_chal; /* client challenge */
+
+} NET_Q_REQ_CHAL;
+
+
+/* NET_R_REQ_CHAL */
+typedef struct net_r_req_chal_info
+{
+ DOM_CHAL srv_chal; /* server challenge */
+ NTSTATUS status; /* return code */
+} NET_R_REQ_CHAL;
+
+/* NET_Q_AUTH */
+typedef struct net_q_auth_info
+{
+ DOM_LOG_INFO clnt_id; /* client identification info */
+ DOM_CHAL clnt_chal; /* client-calculated credentials */
+} NET_Q_AUTH;
+
+/* NET_R_AUTH */
+typedef struct net_r_auth_info
+{
+ DOM_CHAL srv_chal; /* server-calculated credentials */
+ NTSTATUS status; /* return code */
+} NET_R_AUTH;
+
+/* NET_Q_AUTH_2 */
+typedef struct net_q_auth2_info
+{
+ DOM_LOG_INFO clnt_id; /* client identification info */
+ DOM_CHAL clnt_chal; /* client-calculated credentials */
+
+ NEG_FLAGS clnt_flgs; /* usually 0x0000 01ff */
+
+} NET_Q_AUTH_2;
+
+
+/* NET_R_AUTH_2 */
+typedef struct net_r_auth2_info
+{
+ DOM_CHAL srv_chal; /* server-calculated credentials */
+ NEG_FLAGS srv_flgs; /* usually 0x0000 01ff */
+ NTSTATUS status; /* return code */
+} NET_R_AUTH_2;
+
+/* NET_Q_AUTH_3 */
+typedef struct net_q_auth3_info
+{
+ DOM_LOG_INFO clnt_id; /* client identification info */
+ DOM_CHAL clnt_chal; /* client-calculated credentials */
+ NEG_FLAGS clnt_flgs; /* usually 0x6007 ffff */
+} NET_Q_AUTH_3;
+
+/* NET_R_AUTH_3 */
+typedef struct net_r_auth3_info
+{
+ DOM_CHAL srv_chal; /* server-calculated credentials */
+ NEG_FLAGS srv_flgs; /* usually 0x6007 ffff */
+ uint32 unknown; /* 0x0000045b */
+ NTSTATUS status; /* return code */
+} NET_R_AUTH_3;
+
+
+/* NET_Q_SRV_PWSET */
+typedef struct net_q_srv_pwset_info
+{
+ DOM_CLNT_INFO clnt_id; /* client identification/authentication info */
+ uint8 pwd[16]; /* new password - undocumented. */
+
+} NET_Q_SRV_PWSET;
+
+/* NET_R_SRV_PWSET */
+typedef struct net_r_srv_pwset_info
+{
+ DOM_CRED srv_cred; /* server-calculated credentials */
+
+ NTSTATUS status; /* return code */
+
+} NET_R_SRV_PWSET;
+
+/* NET_ID_INFO_2 */
+typedef struct net_network_info_2
+{
+ uint32 ptr_id_info2; /* pointer to id_info_2 */
+ UNIHDR hdr_domain_name; /* domain name unicode header */
+ uint32 param_ctrl; /* param control (0x2) */
+ DOM_LOGON_ID logon_id; /* logon ID */
+ UNIHDR hdr_user_name; /* user name unicode header */
+ UNIHDR hdr_wksta_name; /* workstation name unicode header */
+ uint8 lm_chal[8]; /* lan manager 8 byte challenge */
+ STRHDR hdr_nt_chal_resp; /* nt challenge response */
+ STRHDR hdr_lm_chal_resp; /* lm challenge response */
+
+ UNISTR2 uni_domain_name; /* domain name unicode string */
+ UNISTR2 uni_user_name; /* user name unicode string */
+ UNISTR2 uni_wksta_name; /* workgroup name unicode string */
+ STRING2 nt_chal_resp; /* nt challenge response */
+ STRING2 lm_chal_resp; /* lm challenge response */
+
+} NET_ID_INFO_2;
+
+/* NET_ID_INFO_1 */
+typedef struct id_info_1
+{
+ uint32 ptr_id_info1; /* pointer to id_info_1 */
+ UNIHDR hdr_domain_name; /* domain name unicode header */
+ uint32 param_ctrl; /* param control */
+ DOM_LOGON_ID logon_id; /* logon ID */
+ UNIHDR hdr_user_name; /* user name unicode header */
+ UNIHDR hdr_wksta_name; /* workstation name unicode header */
+ OWF_INFO lm_owf; /* LM OWF Password */
+ OWF_INFO nt_owf; /* NT OWF Password */
+ UNISTR2 uni_domain_name; /* domain name unicode string */
+ UNISTR2 uni_user_name; /* user name unicode string */
+ UNISTR2 uni_wksta_name; /* workgroup name unicode string */
+
+} NET_ID_INFO_1;
+
+#define INTERACTIVE_LOGON_TYPE 1
+#define NET_LOGON_TYPE 2
+
+/* NET_ID_INFO_CTR */
+typedef struct net_id_info_ctr_info
+{
+ uint16 switch_value;
+
+ union
+ {
+ NET_ID_INFO_1 id1; /* auth-level 1 - interactive user login */
+ NET_ID_INFO_2 id2; /* auth-level 2 - workstation referred login */
+
+ } auth;
+
+} NET_ID_INFO_CTR;
+
+/* SAM_INFO - sam logon/off id structure */
+typedef struct sam_info
+{
+ DOM_CLNT_INFO2 client;
+ uint32 ptr_rtn_cred; /* pointer to return credentials */
+ DOM_CRED rtn_cred; /* return credentials */
+ uint16 logon_level;
+ NET_ID_INFO_CTR *ctr;
+
+} DOM_SAM_INFO;
+
+/* NET_Q_SAM_LOGON */
+typedef struct net_q_sam_logon_info
+{
+ DOM_SAM_INFO sam_id;
+ uint16 validation_level;
+
+} NET_Q_SAM_LOGON;
+
+/* NET_R_SAM_LOGON */
+typedef struct net_r_sam_logon_info
+{
+ uint32 buffer_creds; /* undocumented buffer pointer */
+ DOM_CRED srv_creds; /* server credentials. server time stamp appears to be ignored. */
+
+ uint16 switch_value; /* 3 - indicates type of USER INFO */
+ NET_USER_INFO_3 *user;
+
+ uint32 auth_resp; /* 1 - Authoritative response; 0 - Non-Auth? */
+
+ NTSTATUS status; /* return code */
+
+} NET_R_SAM_LOGON;
+
+
+/* NET_Q_SAM_LOGOFF */
+typedef struct net_q_sam_logoff_info
+{
+ DOM_SAM_INFO sam_id;
+
+} NET_Q_SAM_LOGOFF;
+
+/* NET_R_SAM_LOGOFF */
+typedef struct net_r_sam_logoff_info
+{
+ uint32 buffer_creds; /* undocumented buffer pointer */
+ DOM_CRED srv_creds; /* server credentials. server time stamp appears to be ignored. */
+
+ NTSTATUS status; /* return code */
+
+} NET_R_SAM_LOGOFF;
+
+/* NET_Q_SAM_SYNC */
+typedef struct net_q_sam_sync_info
+{
+ UNISTR2 uni_srv_name; /* \\PDC */
+ UNISTR2 uni_cli_name; /* BDC */
+ DOM_CRED cli_creds;
+ DOM_CRED ret_creds;
+
+ uint32 database_id;
+ uint32 restart_state;
+ uint32 sync_context;
+
+ uint32 max_size; /* preferred maximum length */
+
+} NET_Q_SAM_SYNC;
+
+/* SAM_DELTA_HDR */
+typedef struct sam_delta_hdr_info
+{
+ uint16 type; /* type of structure attached */
+ uint16 type2;
+ uint32 target_rid;
+
+ uint32 type3;
+ uint32 ptr_delta;
+
+} SAM_DELTA_HDR;
+
+/* SAM_DOMAIN_INFO (0x1) */
+typedef struct sam_domain_info_info
+{
+ UNIHDR hdr_dom_name;
+ UNIHDR hdr_oem_info;
+
+ UINT64_S force_logoff;
+ uint16 min_pwd_len;
+ uint16 pwd_history_len;
+ UINT64_S max_pwd_age;
+ UINT64_S min_pwd_age;
+ UINT64_S dom_mod_count;
+ NTTIME creation_time;
+
+ BUFHDR2 hdr_sec_desc; /* security descriptor */
+ UNIHDR hdr_unknown;
+ uint8 reserved[40];
+
+ UNISTR2 uni_dom_name;
+ UNISTR2 buf_oem_info; /* never seen */
+
+ BUFFER4 buf_sec_desc;
+ UNISTR2 buf_unknown;
+
+} SAM_DOMAIN_INFO;
+
+/* SAM_GROUP_INFO (0x2) */
+typedef struct sam_group_info_info
+{
+ UNIHDR hdr_grp_name;
+ DOM_GID gid;
+ UNIHDR hdr_grp_desc;
+ BUFHDR2 hdr_sec_desc; /* security descriptor */
+ uint8 reserved[48];
+
+ UNISTR2 uni_grp_name;
+ UNISTR2 uni_grp_desc;
+ BUFFER4 buf_sec_desc;
+
+} SAM_GROUP_INFO;
+
+/* SAM_PWD */
+typedef struct sam_passwd_info
+{
+ /* this structure probably contains password history */
+ /* this is probably a count of lm/nt pairs */
+ uint32 unk_0; /* 0x0000 0002 */
+
+ UNIHDR hdr_lm_pwd;
+ uint8 buf_lm_pwd[16];
+
+ UNIHDR hdr_nt_pwd;
+ uint8 buf_nt_pwd[16];
+
+ UNIHDR hdr_empty_lm;
+ UNIHDR hdr_empty_nt;
+
+} SAM_PWD;
+
+/* SAM_ACCOUNT_INFO (0x5) */
+typedef struct sam_account_info_info
+{
+ UNIHDR hdr_acct_name;
+ UNIHDR hdr_full_name;
+
+ uint32 user_rid;
+ uint32 group_rid;
+
+ UNIHDR hdr_home_dir;
+ UNIHDR hdr_dir_drive;
+ UNIHDR hdr_logon_script;
+ UNIHDR hdr_acct_desc;
+ UNIHDR hdr_workstations;
+
+ NTTIME logon_time;
+ NTTIME logoff_time;
+
+ uint32 logon_divs; /* 0xA8 */
+ uint32 ptr_logon_hrs;
+
+ uint16 bad_pwd_count;
+ uint16 logon_count;
+ NTTIME pwd_last_set_time;
+ NTTIME acct_expiry_time;
+
+ uint32 acb_info;
+ uint8 nt_pwd[16];
+ uint8 lm_pwd[16];
+ uint8 nt_pwd_present;
+ uint8 lm_pwd_present;
+ uint8 pwd_expired;
+
+ UNIHDR hdr_comment;
+ UNIHDR hdr_parameters;
+ uint16 country;
+ uint16 codepage;
+
+ BUFHDR2 hdr_sec_desc; /* security descriptor */
+
+ UNIHDR hdr_profile;
+ UNIHDR hdr_reserved[3]; /* space for more strings */
+ uint32 dw_reserved[4]; /* space for more data - first two seem to
+ be an NTTIME */
+
+ UNISTR2 uni_acct_name;
+ UNISTR2 uni_full_name;
+ UNISTR2 uni_home_dir;
+ UNISTR2 uni_dir_drive;
+ UNISTR2 uni_logon_script;
+ UNISTR2 uni_acct_desc;
+ UNISTR2 uni_workstations;
+
+ uint32 unknown1; /* 0x4EC */
+ uint32 unknown2; /* 0 */
+
+ BUFFER4 buf_logon_hrs;
+ UNISTR2 uni_comment;
+ UNISTR2 uni_parameters;
+ SAM_PWD pass;
+ BUFFER4 buf_sec_desc;
+ UNISTR2 uni_profile;
+
+} SAM_ACCOUNT_INFO;
+
+/* SAM_GROUP_MEM_INFO (0x8) */
+typedef struct sam_group_mem_info_info
+{
+ uint32 ptr_rids;
+ uint32 ptr_attribs;
+ uint32 num_members;
+ uint8 unknown[16];
+
+ uint32 num_members2;
+ uint32 *rids;
+
+ uint32 num_members3;
+ uint32 *attribs;
+
+} SAM_GROUP_MEM_INFO;
+
+/* SAM_ALIAS_INFO (0x9) */
+typedef struct sam_alias_info_info
+{
+ UNIHDR hdr_als_name;
+ uint32 als_rid;
+ BUFHDR2 hdr_sec_desc; /* security descriptor */
+ UNIHDR hdr_als_desc;
+ uint8 reserved[40];
+
+ UNISTR2 uni_als_name;
+ BUFFER4 buf_sec_desc;
+ UNISTR2 uni_als_desc;
+
+} SAM_ALIAS_INFO;
+
+/* SAM_ALIAS_MEM_INFO (0xC) */
+typedef struct sam_alias_mem_info_info
+{
+ uint32 num_members;
+ uint32 ptr_members;
+ uint8 unknown[16];
+
+ uint32 num_sids;
+ uint32 *ptr_sids;
+ DOM_SID2 *sids;
+
+} SAM_ALIAS_MEM_INFO;
+
+
+/* SAM_DELTA_POLICY (0x0D) */
+typedef struct
+{
+ uint32 max_log_size; /* 0x5000 */
+ UINT64_S audit_retention_period; /* 0 */
+ uint32 auditing_mode; /* 0 */
+ uint32 num_events;
+ uint32 ptr_events;
+ UNIHDR hdr_dom_name;
+ uint32 sid_ptr;
+
+ uint32 paged_pool_limit; /* 0x02000000 */
+ uint32 non_paged_pool_limit; /* 0x00100000 */
+ uint32 min_workset_size; /* 0x00010000 */
+ uint32 max_workset_size; /* 0x0f000000 */
+ uint32 page_file_limit; /* 0 */
+ UINT64_S time_limit; /* 0 */
+ NTTIME modify_time; /* 0x3c*/
+ NTTIME create_time; /* a7080110 */
+ BUFHDR2 hdr_sec_desc;
+
+ uint32 num_event_audit_options;
+ uint32 event_audit_option;
+
+ UNISTR2 domain_name;
+ DOM_SID2 domain_sid;
+
+ BUFFER4 buf_sec_desc;
+} SAM_DELTA_POLICY;
+
+/* SAM_DELTA_TRUST_DOMS */
+typedef struct
+{
+ uint32 buf_size;
+ SEC_DESC *sec_desc;
+ DOM_SID2 sid;
+ UNIHDR hdr_domain;
+
+ uint32 unknown0;
+ uint32 unknown1;
+ uint32 unknown2;
+
+ uint32 buf_size2;
+ uint32 ptr;
+
+ uint32 unknown3;
+ UNISTR2 domain;
+
+} SAM_DELTA_TRUSTDOMS;
+
+/* SAM_DELTA_PRIVS (0x10) */
+typedef struct
+{
+ DOM_SID2 sid;
+
+ uint32 priv_count;
+ uint32 priv_control;
+
+ uint32 priv_attr_ptr;
+ uint32 priv_name_ptr;
+
+ uint32 paged_pool_limit; /* 0x02000000 */
+ uint32 non_paged_pool_limit; /* 0x00100000 */
+ uint32 min_workset_size; /* 0x00010000 */
+ uint32 max_workset_size; /* 0x0f000000 */
+ uint32 page_file_limit; /* 0 */
+ UINT64_S time_limit; /* 0 */
+ uint32 system_flags; /* 1 */
+ BUFHDR2 hdr_sec_desc;
+
+ uint32 buf_size2;
+
+ uint32 attribute_count;
+ uint32 *attributes;
+
+ uint32 privlist_count;
+ UNIHDR *hdr_privslist;
+ UNISTR2 *uni_privslist;
+
+ BUFFER4 buf_sec_desc;
+} SAM_DELTA_PRIVS;
+
+/* SAM_DELTA_SECRET */
+typedef struct
+{
+ uint32 buf_size;
+ SEC_DESC *sec_desc;
+ UNISTR2 secret;
+
+ uint32 count1;
+ uint32 count2;
+ uint32 ptr;
+ NTTIME time1;
+ uint32 count3;
+ uint32 count4;
+ uint32 ptr2;
+ NTTIME time2;
+ uint32 unknow1;
+
+ uint32 buf_size2;
+ uint32 ptr3;
+ uint32 unknow2; /* 0x0 12 times */
+
+ uint32 chal_len;
+ uint32 reserved1; /* 0 */
+ uint32 chal_len2;
+ uint8 chal[16];
+
+ uint32 key_len;
+ uint32 reserved2; /* 0 */
+ uint32 key_len2;
+ uint8 key[8];
+
+ uint32 buf_size3;
+ SEC_DESC *sec_desc2;
+
+} SAM_DELTA_SECRET;
+
+/* SAM_DELTA_MOD_COUNT (0x16) */
+typedef struct
+{
+ uint32 seqnum;
+ uint32 dom_mod_count_ptr;
+ UINT64_S dom_mod_count; /* domain mod count at last sync */
+} SAM_DELTA_MOD_COUNT;
+
+typedef union sam_delta_ctr_info
+{
+ SAM_DOMAIN_INFO domain_info ;
+ SAM_GROUP_INFO group_info ;
+ SAM_ACCOUNT_INFO account_info;
+ SAM_GROUP_MEM_INFO grp_mem_info;
+ SAM_ALIAS_INFO alias_info ;
+ SAM_ALIAS_MEM_INFO als_mem_info;
+ SAM_DELTA_POLICY policy_info;
+ SAM_DELTA_PRIVS privs_info;
+ SAM_DELTA_MOD_COUNT mod_count;
+ SAM_DELTA_TRUSTDOMS trustdoms_info;
+ SAM_DELTA_SECRET secret_info;
+} SAM_DELTA_CTR;
+
+/* NET_R_SAM_SYNC */
+typedef struct net_r_sam_sync_info
+{
+ DOM_CRED srv_creds;
+
+ uint32 sync_context;
+
+ uint32 ptr_deltas;
+ uint32 num_deltas;
+ uint32 ptr_deltas2;
+ uint32 num_deltas2;
+
+ SAM_DELTA_HDR *hdr_deltas;
+ SAM_DELTA_CTR *deltas;
+
+ NTSTATUS status;
+} NET_R_SAM_SYNC;
+
+/* NET_Q_SAM_DELTAS */
+typedef struct net_q_sam_deltas_info
+{
+ UNISTR2 uni_srv_name;
+ UNISTR2 uni_cli_name;
+ DOM_CRED cli_creds;
+ DOM_CRED ret_creds;
+
+ uint32 database_id;
+ UINT64_S dom_mod_count; /* domain mod count at last sync */
+
+ uint32 max_size; /* preferred maximum length */
+
+} NET_Q_SAM_DELTAS;
+
+/* NET_R_SAM_DELTAS */
+typedef struct net_r_sam_deltas_info
+{
+ DOM_CRED srv_creds;
+
+ UINT64_S dom_mod_count; /* new domain mod count */
+
+ uint32 ptr_deltas;
+ uint32 num_deltas;
+ uint32 num_deltas2;
+
+ SAM_DELTA_HDR *hdr_deltas;
+ SAM_DELTA_CTR *deltas;
+
+ NTSTATUS status;
+} NET_R_SAM_DELTAS;
+
+#endif /* _RPC_NETLOGON_H */
diff --git a/source/include/rpc_parse.h b/source/include/rpc_parse.h
new file mode 100644
index 00000000000..73fbcb2b1be
--- /dev/null
+++ b/source/include/rpc_parse.h
@@ -0,0 +1,30 @@
+/*
+ Unix SMB/CIFS implementation.
+ SMB parameters and setup
+ Copyright (C) Andrew Tridgell 1992-2000
+ Copyright (C) Luke Kenneth Casson Leighton 1996-2000
+ Copyright (C) Elrond 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 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 _RPC_PARSE_H
+#define _RPC_PARSE_H
+
+/* different dce/rpc pipes */
+#include "rpc_reg.h"
+#include "rpc_brs.h"
+
+#endif /* _RPC_PARSE_H */
diff --git a/source/include/rpc_reg.h b/source/include/rpc_reg.h
new file mode 100644
index 00000000000..46ec88283df
--- /dev/null
+++ b/source/include/rpc_reg.h
@@ -0,0 +1,644 @@
+/*
+ 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.
+ Copyright (C) Gerald Carter 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 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 _RPC_REG_H /* _RPC_REG_H */
+#define _RPC_REG_H
+
+
+/* winreg pipe defines
+ NOT IMPLEMENTED !!
+#define _REG_UNK_01 0x01
+#define _REG_UNK_03 0x03
+#define REG_CREATE_KEY 0x06
+#define REG_DELETE_KEY 0x07
+#define REG_DELETE_VALUE 0x08
+#define REG_FLUSH_KEY 0x0b
+#define REG_GET_KEY_SEC 0x0c
+#define _REG_UNK_0D 0x0d
+#define _REG_UNK_0E 0x0e
+#define _REG_UNK_12 0x12
+#define _REG_UNK_13 0x13
+#define REG_SET_KEY_SEC 0x15
+#define REG_CREATE_VALUE 0x16
+#define _REG_UNK_17 0x17
+*/
+
+/* Implemented */
+#define REG_OPEN_HKCR 0x00
+#define REG_OPEN_HKLM 0x02
+#define REG_OPEN_HKU 0x04
+#define REG_CLOSE 0x05
+#define REG_ENUM_KEY 0x09
+#define REG_ENUM_VALUE 0x0a
+#define REG_OPEN_ENTRY 0x0f
+#define REG_QUERY_KEY 0x10
+#define REG_INFO 0x11
+#define REG_SHUTDOWN 0x18
+#define REG_ABORT_SHUTDOWN 0x19
+#define REG_SAVE_KEY 0x14 /* no idea what the real name is */
+#define REG_UNKNOWN_1A 0x1a
+
+
+#define HKEY_CLASSES_ROOT 0x80000000
+#define HKEY_CURRENT_USER 0x80000001
+#define HKEY_LOCAL_MACHINE 0x80000002
+#define HKEY_USERS 0x80000003
+
+#define KEY_HKLM "HKLM"
+#define KEY_HKU "HKU"
+#define KEY_HKCR "HKCR"
+#define KEY_PRINTING "HKLM\\SYSTEM\\CurrentControlSet\\Control\\Print"
+#define KEY_TREE_ROOT ""
+
+/* Registry data types */
+
+#define REG_NONE 0
+#define REG_SZ 1
+#define REG_EXPAND_SZ 2
+#define REG_BINARY 3
+#define REG_DWORD 4
+#define REG_DWORD_LE 4 /* DWORD, little endian */
+#define REG_DWORD_BE 5 /* DWORD, big endian */
+#define REG_LINK 6
+#define REG_MULTI_SZ 7
+#define REG_RESOURCE_LIST 8
+#define REG_FULL_RESOURCE_DESCRIPTOR 9
+#define REG_RESOURCE_REQUIREMENTS_LIST 10
+
+/* structure to contain registry values */
+
+typedef struct {
+ fstring valuename;
+ uint16 type;
+ uint32 size; /* in bytes */
+ uint8 *data_p;
+} REGISTRY_VALUE;
+
+/* container for regostry values */
+
+typedef struct {
+ TALLOC_CTX *ctx;
+ uint32 num_values;
+ REGISTRY_VALUE **values;
+} REGVAL_CTR;
+
+/* container for registry subkey names */
+
+typedef struct {
+ TALLOC_CTX *ctx;
+ uint32 num_subkeys;
+ char **subkeys;
+} REGSUBKEY_CTR;
+
+
+/*
+ * container for function pointers to enumeration routines
+ * for vitural registry view
+ */
+
+typedef struct {
+ /* functions for enumerating subkeys and values */
+ int (*subkey_fn)( char *key, REGSUBKEY_CTR *subkeys);
+ int (*value_fn) ( char *key, REGVAL_CTR *val );
+ BOOL (*store_subkeys_fn)( char *key, REGSUBKEY_CTR *subkeys );
+ BOOL (*store_values_fn)( char *key, REGVAL_CTR *val );
+} REGISTRY_OPS;
+
+typedef struct {
+ const char *keyname; /* full path to name of key */
+ REGISTRY_OPS *ops; /* registry function hooks */
+} REGISTRY_HOOK;
+
+
+
+/* structure to store the registry handles */
+
+typedef struct _RegistryKey {
+
+ struct _RegistryKey *prev, *next;
+
+ POLICY_HND hnd;
+ pstring name; /* full name of registry key */
+ REGISTRY_HOOK *hook;
+
+} REGISTRY_KEY;
+
+
+/* REG_Q_OPEN_HKCR */
+typedef struct q_reg_open_hkcr_info
+{
+ uint32 ptr;
+ uint16 unknown_0; /* 0x5428 - 16 bit unknown */
+ uint16 unknown_1; /* random. changes */
+ uint32 level; /* 0x0000 0002 - 32 bit unknown */
+
+} REG_Q_OPEN_HKCR ;
+
+/* REG_R_OPEN_HKCR */
+typedef struct r_reg_open_hkcr_info
+{
+ POLICY_HND pol; /* policy handle */
+ NTSTATUS status; /* return status */
+
+} REG_R_OPEN_HKCR;
+
+
+/* REG_Q_OPEN_HKLM */
+typedef struct q_reg_open_hklm_info
+{
+ uint32 ptr;
+ uint16 unknown_0; /* 0xE084 - 16 bit unknown */
+ uint16 unknown_1; /* random. changes */
+ uint32 access_mask;
+
+}
+REG_Q_OPEN_HKLM;
+
+/* REG_R_OPEN_HKLM */
+typedef struct r_reg_open_hklm_info
+{
+ POLICY_HND pol; /* policy handle */
+ NTSTATUS status; /* return status */
+
+}
+REG_R_OPEN_HKLM;
+
+
+/* REG_Q_OPEN_HKU */
+typedef struct q_reg_open_hku_info
+{
+ uint32 ptr;
+ uint16 unknown_0;
+ uint16 unknown_1;
+ uint32 access_mask;
+
+} REG_Q_OPEN_HKU;
+
+/* REG_R_OPEN_HKU */
+typedef struct r_reg_open_hku_info
+{
+ POLICY_HND pol; /* policy handle */
+ NTSTATUS status; /* return status */
+
+} REG_R_OPEN_HKU;
+
+
+/* REG_Q_FLUSH_KEY */
+typedef struct q_reg_open_flush_key_info
+{
+ POLICY_HND pol; /* policy handle */
+
+} REG_Q_FLUSH_KEY;
+
+/* REG_R_FLUSH_KEY */
+typedef struct r_reg_open_flush_key_info
+{
+ NTSTATUS status; /* return status */
+
+} REG_R_FLUSH_KEY;
+
+
+/* REG_Q_SET_KEY_SEC */
+typedef struct q_reg_set_key_sec_info
+{
+ POLICY_HND pol; /* policy handle */
+
+ uint32 sec_info; /* xxxx_SECURITY_INFORMATION */
+
+ uint32 ptr; /* pointer */
+ BUFHDR hdr_sec; /* header for security data */
+ SEC_DESC_BUF *data; /* security data */
+
+} REG_Q_SET_KEY_SEC;
+
+/* REG_R_SET_KEY_SEC */
+typedef struct r_reg_set_key_sec_info
+{
+ NTSTATUS status;
+
+} REG_R_SET_KEY_SEC;
+
+
+/* REG_Q_GET_KEY_SEC */
+typedef struct q_reg_get_key_sec_info
+{
+ POLICY_HND pol; /* policy handle */
+
+ uint32 sec_info; /* xxxx_SECURITY_INFORMATION */
+
+ uint32 ptr; /* pointer */
+ BUFHDR hdr_sec; /* header for security data */
+ SEC_DESC_BUF *data; /* security data */
+
+} REG_Q_GET_KEY_SEC;
+
+/* REG_R_GET_KEY_SEC */
+typedef struct r_reg_get_key_sec_info
+{
+ uint32 sec_info; /* xxxx_SECURITY_INFORMATION */
+
+ uint32 ptr; /* pointer */
+ BUFHDR hdr_sec; /* header for security data */
+ SEC_DESC_BUF *data; /* security data */
+
+ NTSTATUS status;
+
+} REG_R_GET_KEY_SEC;
+
+/* REG_Q_CREATE_VALUE */
+typedef struct q_reg_create_value_info
+{
+ POLICY_HND pol; /* policy handle */
+
+ UNIHDR hdr_name; /* name of value */
+ UNISTR2 uni_name;
+
+ uint32 type; /* 1 = UNISTR, 3 = BYTES, 4 = DWORD, 7 = MULTI_UNISTR */
+
+ BUFFER3 *buf_value; /* value, in byte buffer */
+
+} REG_Q_CREATE_VALUE;
+
+/* REG_R_CREATE_VALUE */
+typedef struct r_reg_create_value_info
+{
+ NTSTATUS status; /* return status */
+
+} REG_R_CREATE_VALUE;
+
+/* REG_Q_ENUM_VALUE */
+typedef struct q_reg_query_value_info
+{
+ POLICY_HND pol; /* policy handle */
+
+ uint32 val_index; /* index */
+
+ UNIHDR hdr_name; /* name of value */
+ UNISTR2 uni_name;
+
+ uint32 ptr_type; /* pointer */
+ uint32 type; /* 1 = UNISTR, 3 = BYTES, 4 = DWORD, 7 = MULTI_UNISTR */
+
+ uint32 ptr_value; /* pointer */
+ BUFFER2 buf_value; /* value, in byte buffer */
+
+ uint32 ptr1; /* pointer */
+ uint32 len_value1; /* */
+
+ uint32 ptr2; /* pointer */
+ uint32 len_value2; /* */
+
+
+} REG_Q_ENUM_VALUE;
+
+/* REG_R_ENUM_VALUE */
+typedef struct r_reg_enum_value_info
+{
+ UNIHDR hdr_name; /* name of value */
+ UNISTR2 uni_name;
+
+ uint32 ptr_type; /* pointer */
+ uint32 type; /* 1 = UNISTR, 3 = BYTES, 4 = DWORD, 7 = MULTI_UNISTR */
+
+ uint32 ptr_value; /* pointer */
+ BUFFER2 buf_value; /* value, in byte buffer */
+
+ uint32 ptr1; /* pointer */
+ uint32 len_value1; /* */
+
+ uint32 ptr2; /* pointer */
+ uint32 len_value2; /* */
+
+ NTSTATUS status; /* return status */
+
+} REG_R_ENUM_VALUE;
+
+/* REG_Q_CREATE_KEY */
+typedef struct q_reg_create_key_info
+{
+ POLICY_HND pnt_pol; /* parent key policy handle */
+
+ UNIHDR hdr_name;
+ UNISTR2 uni_name;
+
+ UNIHDR hdr_class;
+ UNISTR2 uni_class;
+
+ uint32 reserved; /* 0x0000 0000 */
+ SEC_ACCESS sam_access; /* access rights flags, see rpc_secdes.h */
+
+ uint32 ptr1;
+ uint32 sec_info; /* xxxx_SECURITY_INFORMATION */
+
+ uint32 ptr2; /* pointer */
+ BUFHDR hdr_sec; /* header for security data */
+ uint32 ptr3; /* pointer */
+ SEC_DESC_BUF *data;
+
+ uint32 unknown_2; /* 0x0000 0000 */
+
+} REG_Q_CREATE_KEY;
+
+/* REG_R_CREATE_KEY */
+typedef struct r_reg_create_key_info
+{
+ POLICY_HND key_pol; /* policy handle */
+ uint32 unknown; /* 0x0000 0000 */
+
+ NTSTATUS status; /* return status */
+
+} REG_R_CREATE_KEY;
+
+/* REG_Q_DELETE_KEY */
+typedef struct q_reg_delete_key_info
+{
+ POLICY_HND pnt_pol; /* parent key policy handle */
+
+ UNIHDR hdr_name;
+ UNISTR2 uni_name;
+} REG_Q_DELETE_KEY;
+
+/* REG_R_DELETE_KEY */
+typedef struct r_reg_delete_key_info
+{
+ POLICY_HND key_pol; /* policy handle */
+
+ NTSTATUS status; /* return status */
+
+} REG_R_DELETE_KEY;
+
+/* REG_Q_DELETE_VALUE */
+typedef struct q_reg_delete_val_info
+{
+ POLICY_HND pnt_pol; /* parent key policy handle */
+
+ UNIHDR hdr_name;
+ UNISTR2 uni_name;
+
+} REG_Q_DELETE_VALUE;
+
+/* REG_R_DELETE_VALUE */
+typedef struct r_reg_delete_val_info
+{
+ POLICY_HND key_pol; /* policy handle */
+
+ NTSTATUS status; /* return status */
+
+} REG_R_DELETE_VALUE;
+
+/* REG_Q_QUERY_KEY */
+typedef struct q_reg_query_info
+{
+ POLICY_HND pol; /* policy handle */
+ UNIHDR hdr_class;
+ UNISTR2 uni_class;
+
+} REG_Q_QUERY_KEY;
+
+/* REG_R_QUERY_KEY */
+typedef struct r_reg_query_key_info
+{
+ UNIHDR hdr_class;
+ UNISTR2 uni_class;
+
+ uint32 num_subkeys;
+ uint32 max_subkeylen;
+ uint32 reserved; /* 0x0000 0000 - according to MSDN (max_subkeysize?) */
+ uint32 num_values;
+ uint32 max_valnamelen;
+ uint32 max_valbufsize;
+ uint32 sec_desc; /* 0x0000 0078 */
+ NTTIME mod_time; /* modified time */
+
+ NTSTATUS status; /* return status */
+
+} REG_R_QUERY_KEY;
+
+
+/* REG_Q_UNKNOWN_1A */
+typedef struct q_reg_unk_1a_info
+{
+ POLICY_HND pol; /* policy handle */
+
+} REG_Q_UNKNOWN_1A;
+
+/* REG_R_UNKNOWN_1A */
+typedef struct r_reg_unk_1a_info
+{
+ uint32 unknown; /* 0x0500 0000 */
+ NTSTATUS status; /* return status */
+
+} REG_R_UNKNOWN_1A;
+
+
+/* REG_Q_UNKNOWN_1A */
+typedef struct q_reg_unknown_14
+{
+ POLICY_HND pol; /* policy handle */
+
+ UNIHDR hdr_file; /* unicode product type header */
+ UNISTR2 uni_file; /* local filename to save key as from regedt32.exe */
+ /* e.g. "c:\temp\test.dat" */
+
+ uint32 unknown; /* 0x0000 0000 */
+
+} REG_Q_SAVE_KEY;
+
+
+/* REG_R_UNKNOWN_1A */
+typedef struct r_reg_unknown_14
+{
+ NTSTATUS status; /* return status */
+
+} REG_R_SAVE_KEY;
+
+
+
+/* REG_Q_CLOSE */
+typedef struct reg_q_close_info
+{
+ POLICY_HND pol; /* policy handle */
+
+} REG_Q_CLOSE;
+
+/* REG_R_CLOSE */
+typedef struct reg_r_close_info
+{
+ POLICY_HND pol; /* policy handle. should be all zeros. */
+
+ NTSTATUS status; /* return code */
+
+} REG_R_CLOSE;
+
+
+/* REG_Q_ENUM_KEY */
+typedef struct q_reg_enum_value_info
+{
+ POLICY_HND pol; /* policy handle */
+
+ uint32 key_index;
+
+ uint16 key_name_len; /* 0x0000 */
+ uint16 unknown_1; /* 0x0414 */
+
+ uint32 ptr1; /* pointer */
+ uint32 unknown_2; /* 0x0000 020A */
+ uint8 pad1[8]; /* padding - zeros */
+
+ uint32 ptr2; /* pointer */
+ uint8 pad2[8]; /* padding - zeros */
+
+ uint32 ptr3; /* pointer */
+ NTTIME time; /* current time? */
+
+} REG_Q_ENUM_KEY;
+
+/* REG_R_ENUM_KEY */
+typedef struct r_reg_enum_key_info
+{
+ uint16 key_name_len; /* number of bytes in key name */
+ uint16 unknown_1; /* 0x0414 - matches with query unknown_1 */
+
+ uint32 ptr1; /* pointer */
+ uint32 unknown_2; /* 0x0000 020A */
+ uint32 unknown_3; /* 0x0000 0000 */
+
+ UNISTR3 key_name;
+
+ uint32 ptr2; /* pointer */
+ uint8 pad2[8]; /* padding - zeros */
+
+ uint32 ptr3; /* pointer */
+ NTTIME time; /* current time? */
+
+ NTSTATUS status; /* return status */
+
+} REG_R_ENUM_KEY;
+
+
+/* REG_Q_INFO */
+typedef struct q_reg_info_info
+{
+ POLICY_HND pol; /* policy handle */
+
+ UNIHDR hdr_type; /* unicode product type header */
+ UNISTR2 uni_type; /* unicode product type - "ProductType" */
+
+ uint32 ptr_reserved; /* pointer */
+
+ uint32 ptr_buf; /* the next three fields follow if ptr_buf != 0 */
+ uint32 ptr_bufsize;
+ uint32 bufsize;
+ uint32 buf_unk;
+
+ uint32 unk1;
+ uint32 ptr_buflen;
+ uint32 buflen;
+
+ uint32 ptr_buflen2;
+ uint32 buflen2;
+
+} REG_Q_INFO;
+
+/* REG_R_INFO */
+typedef struct r_reg_info_info
+{
+ uint32 ptr_type; /* key type pointer */
+ uint32 type; /* key datatype */
+
+ uint32 ptr_uni_val; /* key value pointer */
+ BUFFER2 uni_val; /* key value */
+
+ uint32 ptr_max_len;
+ uint32 buf_max_len;
+
+ uint32 ptr_len;
+ uint32 buf_len;
+
+ NTSTATUS status; /* return status */
+
+} REG_R_INFO;
+
+
+/* REG_Q_OPEN_ENTRY */
+typedef struct q_reg_open_entry_info
+{
+ POLICY_HND pol; /* policy handle */
+
+ UNIHDR hdr_name; /* unicode registry string header */
+ UNISTR2 uni_name; /* unicode registry string name */
+
+ uint32 unknown_0; /* 32 bit unknown - 0x0000 0000 */
+ uint32 access_desired;
+
+} REG_Q_OPEN_ENTRY;
+
+
+
+/* REG_R_OPEN_ENTRY */
+typedef struct r_reg_open_entry_info
+{
+ POLICY_HND pol; /* policy handle */
+ NTSTATUS status; /* return status */
+
+} REG_R_OPEN_ENTRY;
+
+/* REG_Q_SHUTDOWN */
+typedef struct q_reg_shutdown_info
+{
+ uint32 ptr_0;
+ uint32 ptr_1;
+ uint32 ptr_2;
+ UNIHDR hdr_msg; /* shutdown message */
+ UNISTR2 uni_msg; /* seconds */
+ uint32 timeout; /* seconds */
+ uint8 force; /* boolean: force shutdown */
+ uint8 reboot; /* boolean: reboot on shutdown */
+
+} REG_Q_SHUTDOWN;
+
+/* REG_R_SHUTDOWN */
+typedef struct r_reg_shutdown_info
+{
+ NTSTATUS status; /* return status */
+
+} REG_R_SHUTDOWN;
+
+/* REG_Q_ABORT_SHUTDOWN */
+typedef struct q_reg_abort_shutdown_info
+{
+ uint32 ptr_server;
+ uint16 server;
+
+} REG_Q_ABORT_SHUTDOWN;
+
+/* REG_R_ABORT_SHUTDOWN */
+typedef struct r_reg_abort_shutdown_info
+{
+ NTSTATUS status; /* return status */
+
+} REG_R_ABORT_SHUTDOWN;
+
+
+#endif /* _RPC_REG_H */
+
diff --git a/source/include/rpc_samr.h b/source/include/rpc_samr.h
new file mode 100644
index 00000000000..111e62c3553
--- /dev/null
+++ b/source/include/rpc_samr.h
@@ -0,0 +1,1862 @@
+/*
+ Unix SMB/CIFS implementation.
+ SMB parameters and setup
+ Copyright (C) Andrew Tridgell 1992-2000
+ Copyright (C) Luke Kenneth Casson Leighton 1996-2000
+ Copyright (C) Paul Ashton 1997-2000
+ Copyright (C) Jean François Micouleau 1998-2001
+ Copyright (C) Jim McDonough <jmcd@us.ibm.com> 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 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 _RPC_SAMR_H /* _RPC_SAMR_H */
+#define _RPC_SAMR_H
+
+#include "rpc_misc.h"
+
+/*******************************************************************
+ the following information comes from a QuickView on samsrv.dll,
+ and gives an idea of exactly what is needed:
+
+x SamrAddMemberToAlias
+x SamrAddMemberToGroup
+SamrAddMultipleMembersToAlias
+x SamrChangePasswordUser
+x SamrCloseHandle
+x SamrConnect
+x SamrCreateAliasInDomain
+x SamrCreateGroupInDomain
+x SamrCreateUserInDomain
+? SamrDeleteAlias
+SamrDeleteGroup
+x SamrDeleteUser
+x SamrEnumerateAliasesInDomain
+SamrEnumerateDomainsInSamServer
+x SamrEnumerateGroupsInDomain
+x SamrEnumerateUsersInDomain
+SamrGetUserDomainPasswordInformation
+SamrLookupDomainInSamServer
+? SamrLookupIdsInDomain
+x SamrLookupNamesInDomain
+x SamrOpenAlias
+x SamrOpenDomain
+x SamrOpenGroup
+x SamrOpenUser
+x SamrQueryDisplayInformation
+x SamrQueryInformationAlias
+SamrQueryInformationDomain
+? SamrQueryInformationUser
+x SamrQuerySecurityObject
+SamrRemoveMemberFromAlias
+SamrRemoveMemberFromForiegnDomain
+SamrRemoveMemberFromGroup
+SamrRemoveMultipleMembersFromAlias
+x SamrSetInformationAlias
+SamrSetInformationDomain
+x SamrSetInformationGroup
+x SamrSetInformationUser
+SamrSetMemberAttributesOfGroup
+SamrSetSecurityObject
+SamrShutdownSamServer
+SamrTestPrivateFunctionsDomain
+SamrTestPrivateFunctionsUser
+
+********************************************************************/
+
+#define SAMR_CONNECT_ANON 0x00
+#define SAMR_CLOSE_HND 0x01
+#define SAMR_SET_SEC_OBJECT 0x02
+#define SAMR_QUERY_SEC_OBJECT 0x03
+
+#define SAMR_UNKNOWN_4 0x04 /* profile info? */
+#define SAMR_LOOKUP_DOMAIN 0x05
+#define SAMR_ENUM_DOMAINS 0x06
+#define SAMR_OPEN_DOMAIN 0x07
+#define SAMR_QUERY_DOMAIN_INFO 0x08
+#define SAMR_SET_DOMAIN_INFO 0x09
+
+#define SAMR_CREATE_DOM_GROUP 0x0a
+#define SAMR_ENUM_DOM_GROUPS 0x0b
+#define SAMR_ENUM_DOM_USERS 0x0d
+#define SAMR_CREATE_DOM_ALIAS 0x0e
+#define SAMR_ENUM_DOM_ALIASES 0x0f
+#define SAMR_QUERY_USERALIASES 0x10
+
+#define SAMR_LOOKUP_NAMES 0x11
+#define SAMR_LOOKUP_RIDS 0x12
+
+#define SAMR_OPEN_GROUP 0x13
+#define SAMR_QUERY_GROUPINFO 0x14
+#define SAMR_SET_GROUPINFO 0x15
+#define SAMR_ADD_GROUPMEM 0x16
+#define SAMR_DELETE_DOM_GROUP 0x17
+#define SAMR_DEL_GROUPMEM 0x18
+#define SAMR_QUERY_GROUPMEM 0x19
+#define SAMR_UNKNOWN_1A 0x1a
+
+#define SAMR_OPEN_ALIAS 0x1b
+#define SAMR_QUERY_ALIASINFO 0x1c
+#define SAMR_SET_ALIASINFO 0x1d
+#define SAMR_DELETE_DOM_ALIAS 0x1e
+#define SAMR_ADD_ALIASMEM 0x1f
+#define SAMR_DEL_ALIASMEM 0x20
+#define SAMR_QUERY_ALIASMEM 0x21
+
+#define SAMR_OPEN_USER 0x22
+#define SAMR_DELETE_DOM_USER 0x23
+#define SAMR_QUERY_USERINFO 0x24
+#define SAMR_SET_USERINFO2 0x25
+#define SAMR_QUERY_USERGROUPS 0x27
+
+#define SAMR_QUERY_DISPINFO 0x28
+#define SAMR_UNKNOWN_29 0x29
+#define SAMR_UNKNOWN_2a 0x2a
+#define SAMR_UNKNOWN_2b 0x2b
+#define SAMR_GET_USRDOM_PWINFO 0x2c
+#define SAMR_REMOVE_SID_FOREIGN_DOMAIN 0x2d
+#define SAMR_UNKNOWN_2E 0x2e /* looks like an alias for SAMR_QUERY_DOMAIN_INFO */
+#define SAMR_UNKNOWN_2f 0x2f
+#define SAMR_QUERY_DISPINFO3 0x30 /* Alias for SAMR_QUERY_DISPINFO
+ with info level 3 */
+#define SAMR_UNKNOWN_31 0x31
+#define SAMR_CREATE_USER 0x32
+#define SAMR_QUERY_DISPINFO4 0x33 /* Alias for SAMR_QUERY_DISPINFO
+ with info level 4 */
+#define SAMR_ADDMULTI_ALIASMEM 0x34
+
+#define SAMR_UNKNOWN_35 0x35
+#define SAMR_UNKNOWN_36 0x36
+#define SAMR_CHGPASSWD_USER 0x37
+#define SAMR_GET_DOM_PWINFO 0x38
+#define SAMR_CONNECT 0x39
+#define SAMR_SET_USERINFO 0x3A
+#define SAMR_CONNECT4 0x3E
+
+typedef struct logon_hours_info
+{
+ uint32 len; /* normally 21 bytes */
+ uint8 hours[32];
+
+} LOGON_HRS;
+
+/* SAM_USER_INFO_23 */
+typedef struct sam_user_info_23
+{
+ /* TIMES MAY NOT IN RIGHT ORDER!!!! */
+ NTTIME logon_time; /* logon time */
+ NTTIME logoff_time; /* logoff time */
+ NTTIME kickoff_time; /* kickoff time */
+ NTTIME pass_last_set_time; /* password last set time */
+ NTTIME pass_can_change_time; /* password can change time */
+ NTTIME pass_must_change_time; /* password must change time */
+
+ UNIHDR hdr_user_name; /* NULL - user name unicode string header */
+ UNIHDR hdr_full_name; /* user's full name unicode string header */
+ UNIHDR hdr_home_dir; /* home directory unicode string header */
+ UNIHDR hdr_dir_drive; /* home drive unicode string header */
+ UNIHDR hdr_logon_script; /* logon script unicode string header */
+ UNIHDR hdr_profile_path; /* profile path unicode string header */
+ UNIHDR hdr_acct_desc ; /* user description */
+ UNIHDR hdr_workstations; /* comma-separated workstations user can log in from */
+ UNIHDR hdr_unknown_str ; /* don't know what this is, yet. */
+ UNIHDR hdr_munged_dial ; /* munged path name and dial-back tel number */
+
+ uint8 lm_pwd[16]; /* lm user passwords */
+ uint8 nt_pwd[16]; /* nt user passwords */
+
+ uint32 user_rid; /* Primary User ID */
+ uint32 group_rid; /* Primary Group ID */
+
+ uint32 acb_info; /* account info (ACB_xxxx bit-mask) */
+
+ uint32 fields_present; /* 0x09f8 27fa */
+
+ uint16 logon_divs; /* 0x0000 00a8 which is 168 which is num hrs in a week */
+ /* uint8 pad[2] */
+ uint32 ptr_logon_hrs; /* pointer to logon hours */
+
+ /* Was unknown_5. */
+ uint16 bad_password_count;
+ uint16 logon_count;
+
+ uint8 padding1[6];
+
+ uint8 passmustchange; /* 0x00 must change = 0x01 */
+
+ uint8 padding2;
+
+ uint8 pass[516];
+
+ UNISTR2 uni_user_name; /* NULL - username unicode string */
+ UNISTR2 uni_full_name; /* user's full name unicode string */
+ UNISTR2 uni_home_dir; /* home directory unicode string */
+ UNISTR2 uni_dir_drive; /* home directory drive unicode string */
+ UNISTR2 uni_logon_script; /* logon script unicode string */
+ UNISTR2 uni_profile_path; /* profile path unicode string */
+ UNISTR2 uni_acct_desc ; /* user description unicode string */
+ UNISTR2 uni_workstations; /* login from workstations unicode string */
+ UNISTR2 uni_unknown_str ; /* don't know what this is, yet. */
+ UNISTR2 uni_munged_dial ; /* munged path name and dial-back tel no */
+
+ uint32 unknown_6; /* 0x0000 04ec */
+ uint32 padding4;
+
+ LOGON_HRS logon_hrs;
+
+} SAM_USER_INFO_23;
+
+/* SAM_USER_INFO_24 */
+typedef struct sam_user_info_24
+{
+ uint8 pass[516];
+ uint16 pw_len;
+} SAM_USER_INFO_24;
+
+/*
+ * NB. This structure is *definately* incorrect. It's my best guess
+ * currently for W2K SP2. The password field is encrypted in a different
+ * way than normal... And there are definately other problems. JRA.
+ */
+
+/* SAM_USER_INFO_25 */
+typedef struct sam_user_info_25
+{
+ /* TIMES MAY NOT IN RIGHT ORDER!!!! */
+ NTTIME logon_time; /* logon time */
+ NTTIME logoff_time; /* logoff time */
+ NTTIME kickoff_time; /* kickoff time */
+ NTTIME pass_last_set_time; /* password last set time */
+ NTTIME pass_can_change_time; /* password can change time */
+ NTTIME pass_must_change_time; /* password must change time */
+
+ UNIHDR hdr_user_name; /* NULL - user name unicode string header */
+ UNIHDR hdr_full_name; /* user's full name unicode string header */
+ UNIHDR hdr_home_dir; /* home directory unicode string header */
+ UNIHDR hdr_dir_drive; /* home drive unicode string header */
+ UNIHDR hdr_logon_script; /* logon script unicode string header */
+ UNIHDR hdr_profile_path; /* profile path unicode string header */
+ UNIHDR hdr_acct_desc ; /* user description */
+ UNIHDR hdr_workstations; /* comma-separated workstations user can log in from */
+ UNIHDR hdr_unknown_str ; /* don't know what this is, yet. */
+ UNIHDR hdr_munged_dial ; /* munged path name and dial-back tel number */
+
+ uint8 lm_pwd[16]; /* lm user passwords */
+ uint8 nt_pwd[16]; /* nt user passwords */
+
+ uint32 user_rid; /* Primary User ID */
+ uint32 group_rid; /* Primary Group ID */
+
+ uint32 acb_info; /* account info (ACB_xxxx bit-mask) */
+
+ uint32 unknown_6[6];
+
+ uint8 pass[532];
+
+ UNISTR2 uni_user_name; /* NULL - username unicode string */
+ UNISTR2 uni_full_name; /* user's full name unicode string */
+ UNISTR2 uni_home_dir; /* home directory unicode string */
+ UNISTR2 uni_dir_drive; /* home directory drive unicode string */
+ UNISTR2 uni_logon_script; /* logon script unicode string */
+ UNISTR2 uni_profile_path; /* profile path unicode string */
+ UNISTR2 uni_acct_desc ; /* user description unicode string */
+ UNISTR2 uni_workstations; /* login from workstations unicode string */
+ UNISTR2 uni_unknown_str ; /* don't know what this is, yet. */
+ UNISTR2 uni_munged_dial ; /* munged path name and dial-back tel no */
+} SAM_USER_INFO_25;
+
+
+/* SAM_USER_INFO_21 */
+typedef struct sam_user_info_21
+{
+ NTTIME logon_time; /* logon time */
+ NTTIME logoff_time; /* logoff time */
+ NTTIME kickoff_time; /* kickoff time */
+ NTTIME pass_last_set_time; /* password last set time */
+ NTTIME pass_can_change_time; /* password can change time */
+ NTTIME pass_must_change_time; /* password must change time */
+
+ UNIHDR hdr_user_name; /* username unicode string header */
+ UNIHDR hdr_full_name; /* user's full name unicode string header */
+ UNIHDR hdr_home_dir; /* home directory unicode string header */
+ UNIHDR hdr_dir_drive; /* home drive unicode string header */
+ UNIHDR hdr_logon_script; /* logon script unicode string header */
+ UNIHDR hdr_profile_path; /* profile path unicode string header */
+ UNIHDR hdr_acct_desc ; /* user description */
+ UNIHDR hdr_workstations; /* comma-separated workstations user can log in from */
+ UNIHDR hdr_unknown_str ; /* don't know what this is, yet. */
+ UNIHDR hdr_munged_dial ; /* munged path name and dial-back tel number */
+
+ uint8 lm_pwd[16]; /* lm user passwords */
+ uint8 nt_pwd[16]; /* nt user passwords */
+
+ uint32 user_rid; /* Primary User ID */
+ uint32 group_rid; /* Primary Group ID */
+
+ uint32 acb_info; /* account info (ACB_xxxx bit-mask) */
+
+ /* Was unknown_3 */
+ uint32 fields_present; /* 0x00ff ffff */
+
+ uint16 logon_divs; /* 0x0000 00a8 which is 168 which is num hrs in a week */
+ /* uint8 pad[2] */
+ uint32 ptr_logon_hrs; /* unknown pointer */
+
+ /* Was unknown_5. */
+ uint16 bad_password_count;
+ uint16 logon_count;
+
+ uint8 padding1[6];
+
+ uint8 passmustchange; /* 0x00 must change = 0x01 */
+
+ uint8 padding2;
+
+ UNISTR2 uni_user_name; /* username unicode string */
+ UNISTR2 uni_full_name; /* user's full name unicode string */
+ UNISTR2 uni_home_dir; /* home directory unicode string */
+ UNISTR2 uni_dir_drive; /* home directory drive unicode string */
+ UNISTR2 uni_logon_script; /* logon script unicode string */
+ UNISTR2 uni_profile_path; /* profile path unicode string */
+ UNISTR2 uni_acct_desc ; /* user description unicode string */
+ UNISTR2 uni_workstations; /* login from workstations unicode string */
+ UNISTR2 uni_unknown_str ; /* don't know what this is, yet. */
+ UNISTR2 uni_munged_dial ; /* munged path name and dial-back tel number */
+
+ uint32 unknown_6; /* 0x0000 04ec */
+ uint32 padding4;
+
+ LOGON_HRS logon_hrs;
+
+} SAM_USER_INFO_21;
+
+#define PASS_MUST_CHANGE_AT_NEXT_LOGON 0x01
+#define PASS_DONT_CHANGE_AT_NEXT_LOGON 0x00
+
+/* SAM_USER_INFO_20 */
+typedef struct sam_user_info_20
+{
+ UNIHDR hdr_munged_dial ; /* munged path name and dial-back tel number */
+
+ UNISTR2 uni_munged_dial ; /* munged path name and dial-back tel number */
+
+} SAM_USER_INFO_20;
+
+/* SAM_USER_INFO_12 */
+typedef struct sam_user_info_12
+{
+ uint8 lm_pwd[16]; /* lm user passwords */
+ uint8 nt_pwd[16]; /* nt user passwords */
+
+ uint8 lm_pwd_active;
+ uint8 nt_pwd_active;
+
+} SAM_USER_INFO_12;
+
+/* SAM_USER_INFO_11 */
+typedef struct sam_user_info_11
+{
+ uint8 padding_0[16]; /* 0 - padding 16 bytes */
+ NTTIME expiry; /* expiry time or something? */
+ uint8 padding_1[24]; /* 0 - padding 24 bytes */
+
+ UNIHDR hdr_mach_acct; /* unicode header for machine account */
+ uint32 padding_2; /* 0 - padding 4 bytes */
+
+ uint32 ptr_1; /* pointer */
+ uint8 padding_3[32]; /* 0 - padding 32 bytes */
+ uint32 padding_4; /* 0 - padding 4 bytes */
+
+ uint32 ptr_2; /* pointer */
+ uint32 padding_5; /* 0 - padding 4 bytes */
+
+ uint32 ptr_3; /* pointer */
+ uint8 padding_6[32]; /* 0 - padding 32 bytes */
+
+ uint32 rid_user; /* user RID */
+ uint32 rid_group; /* group RID */
+
+ uint16 acct_ctrl; /* 0080 - ACB_XXXX */
+ uint16 unknown_3; /* 16 bit padding */
+
+ uint16 unknown_4; /* 0x003f - 16 bit unknown */
+ uint16 unknown_5; /* 0x003c - 16 bit unknown */
+
+ uint8 padding_7[16]; /* 0 - padding 16 bytes */
+ uint32 padding_8; /* 0 - padding 4 bytes */
+
+ UNISTR2 uni_mach_acct; /* unicode string for machine account */
+
+ uint8 padding_9[48]; /* 0 - padding 48 bytes */
+
+} SAM_USER_INFO_11;
+
+
+/* SAM_USER_INFO_10 */
+typedef struct sam_user_info_10
+{
+ uint32 acb_info;
+
+} SAM_USER_INFO_10;
+
+
+
+/* SAMR_Q_CLOSE_HND - probably a policy handle close */
+typedef struct q_samr_close_hnd_info
+{
+ POLICY_HND pol; /* policy handle */
+
+} SAMR_Q_CLOSE_HND;
+
+
+/* SAMR_R_CLOSE_HND - probably a policy handle close */
+typedef struct r_samr_close_hnd_info
+{
+ POLICY_HND pol; /* policy handle */
+ NTSTATUS status; /* return status */
+
+} SAMR_R_CLOSE_HND;
+
+
+/****************************************************************************
+SAMR_Q_GET_USRDOM_PWINFO - a "set user info" occurs just after this
+*****************************************************************************/
+
+/* SAMR_Q_GET_USRDOM_PWINFO */
+typedef struct q_samr_usrdom_pwinfo_info
+{
+ POLICY_HND user_pol; /* policy handle */
+
+} SAMR_Q_GET_USRDOM_PWINFO;
+
+
+/****************************************************************************
+SAMR_R_GET_USRDOM_PWINFO - a "set user info" occurs just after this
+*****************************************************************************/
+
+/* SAMR_R_GET_USRDOM_PWINFO */
+typedef struct r_samr_usrdom_pwinfo_info
+{
+ uint16 unknown_0; /* 0000 */
+ uint16 unknown_1; /* 0x0016 or 0x0015 */
+ uint32 unknown_2; /* 0x0000 0000 */
+ NTSTATUS status;
+
+} SAMR_R_GET_USRDOM_PWINFO;
+
+/****************************************************************************
+SAMR_Q_SET_SEC_OBJ - info level 4.
+*****************************************************************************/
+
+/* SAMR_Q_SET_SEC_OBJ - */
+typedef struct q_samr_set_sec_obj_info
+{
+ POLICY_HND pol; /* policy handle */
+ uint32 sec_info; /* xxxx_SECURITY_INFORMATION 0x0000 0004 */
+ SEC_DESC_BUF *buf;
+
+} SAMR_Q_SET_SEC_OBJ;
+
+/* SAMR_R_SET_SEC_OBJ - */
+typedef struct r_samr_set_sec_obj_info
+{
+ NTSTATUS status; /* return status */
+
+} SAMR_R_SET_SEC_OBJ;
+
+
+/****************************************************************************
+SAMR_Q_QUERY_SEC_OBJ - info level 4. returns SIDs.
+*****************************************************************************/
+
+/* SAMR_Q_QUERY_SEC_OBJ - probably get domain info... */
+typedef struct q_samr_query_sec_obj_info
+{
+ POLICY_HND user_pol; /* policy handle */
+ uint32 sec_info; /* xxxx_SECURITY_INFORMATION 0x0000 0004 */
+
+} SAMR_Q_QUERY_SEC_OBJ;
+
+/* SAMR_R_QUERY_SEC_OBJ - probably an open */
+typedef struct r_samr_query_sec_obj_info
+{
+ uint32 ptr;
+ SEC_DESC_BUF *buf;
+
+ NTSTATUS status; /* return status */
+
+} SAMR_R_QUERY_SEC_OBJ;
+
+
+/****************************************************************************
+SAMR_Q_QUERY_DOMAIN_INFO - probably a query on domain group info.
+*****************************************************************************/
+
+/* SAMR_Q_QUERY_DOMAIN_INFO - */
+typedef struct q_samr_query_domain_info
+{
+ POLICY_HND domain_pol; /* policy handle */
+ uint16 switch_value; /* 0x0002, 0x0001 */
+
+} SAMR_Q_QUERY_DOMAIN_INFO;
+
+typedef struct sam_unknown_info_3_info
+{
+ NTTIME logout;
+ /* 0x8000 0000 */ /* DON'T forcibly disconnect remote users from server when logon hours expire*/
+
+ /* 0x0000 0000 */ /* forcibly disconnect remote users from server when logon hours expire*/
+
+} SAM_UNK_INFO_3;
+
+typedef struct sam_unknown_info_6_info
+{
+ uint32 unknown_0; /* 0x0000 0000 */
+
+ uint32 ptr_0; /* pointer to unknown structure */
+ uint8 padding[12]; /* 12 bytes zeros */
+
+} SAM_UNK_INFO_6;
+
+typedef struct sam_unknown_info_7_info
+{
+ uint16 unknown_0; /* 0x0003 */
+
+} SAM_UNK_INFO_7;
+
+typedef struct sam_unknown_info_12_inf
+{
+ NTTIME duration;
+ NTTIME reset_count;
+ uint16 bad_attempt_lockout;
+
+} SAM_UNK_INFO_12;
+
+typedef struct sam_unknown_info_5_inf
+{
+ UNIHDR hdr_server; /* server name unicode header */
+ UNISTR2 uni_server; /* server name unicode string */
+
+} SAM_UNK_INFO_5;
+
+typedef struct sam_unknown_info_2_inf
+{
+ uint32 unknown_0; /* 0x0000 0000 */
+ uint32 unknown_1; /* 0x8000 0000 */
+ uint32 unknown_2; /* 0x0000 0000 */
+
+ uint32 ptr_0; /* pointer to unknown structure */
+ UNIHDR hdr_domain; /* domain name unicode header */
+ UNIHDR hdr_server; /* server name unicode header */
+
+ /* put all the data in here, at the moment, including what the above
+ pointer is referring to
+ */
+
+ uint32 seq_num; /* some sort of incrementing sequence number? */
+ uint32 unknown_3; /* 0x0000 0000 */
+
+ uint32 unknown_4; /* 0x0000 0001 */
+ uint32 unknown_5; /* 0x0000 0003 */
+ uint32 unknown_6; /* 0x0000 0001 */
+ uint32 num_domain_usrs; /* number of users in domain */
+ uint32 num_domain_grps; /* number of domain groups in domain */
+ uint32 num_local_grps; /* number of local groups in domain */
+
+ uint8 padding[12]; /* 12 bytes zeros */
+
+ UNISTR2 uni_domain; /* domain name unicode string */
+ UNISTR2 uni_server; /* server name unicode string */
+
+} SAM_UNK_INFO_2;
+
+typedef struct sam_unknown_info_1_inf
+{
+ uint16 min_length_password;
+ uint16 password_history;
+ uint32 flag;
+ NTTIME expire;
+ NTTIME min_passwordage;
+
+} SAM_UNK_INFO_1;
+
+
+typedef struct sam_unknown_ctr_info
+{
+ union
+ {
+ SAM_UNK_INFO_1 inf1;
+ SAM_UNK_INFO_2 inf2;
+ SAM_UNK_INFO_3 inf3;
+ SAM_UNK_INFO_5 inf5;
+ SAM_UNK_INFO_6 inf6;
+ SAM_UNK_INFO_7 inf7;
+ SAM_UNK_INFO_12 inf12;
+
+ } info;
+
+} SAM_UNK_CTR;
+
+
+/* SAMR_R_QUERY_DOMAIN_INFO - */
+typedef struct r_samr_query_domain_info
+{
+ uint32 ptr_0;
+ uint16 switch_value; /* same as in query */
+
+ SAM_UNK_CTR *ctr;
+
+ NTSTATUS status; /* return status */
+
+} SAMR_R_QUERY_DOMAIN_INFO;
+
+
+/* SAMR_Q_LOOKUP_DOMAIN - obtain SID for a local domain */
+typedef struct q_samr_lookup_domain_info
+{
+ POLICY_HND connect_pol;
+
+ UNIHDR hdr_domain;
+ UNISTR2 uni_domain;
+
+} SAMR_Q_LOOKUP_DOMAIN;
+
+
+/* SAMR_R_LOOKUP_DOMAIN */
+typedef struct r_samr_lookup_domain_info
+{
+ uint32 ptr_sid;
+ DOM_SID2 dom_sid;
+
+ NTSTATUS status;
+
+} SAMR_R_LOOKUP_DOMAIN;
+
+
+/****************************************************************************
+SAMR_Q_OPEN_DOMAIN - unknown_0 values seen associated with SIDs:
+
+0x0000 03f1 and a specific domain sid - S-1-5-21-44c01ca6-797e5c3d-33f83fd0
+0x0000 0200 and a specific domain sid - S-1-5-21-44c01ca6-797e5c3d-33f83fd0
+*****************************************************************************/
+
+/* SAMR_Q_OPEN_DOMAIN */
+typedef struct q_samr_open_domain_info
+{
+ POLICY_HND pol; /* policy handle */
+ uint32 flags; /* 0x2000 0000; 0x0000 0211; 0x0000 0280; 0x0000 0200 - flags? */
+ DOM_SID2 dom_sid; /* domain SID */
+
+} SAMR_Q_OPEN_DOMAIN;
+
+
+/* SAMR_R_OPEN_DOMAIN - probably an open */
+typedef struct r_samr_open_domain_info
+{
+ POLICY_HND domain_pol; /* policy handle associated with the SID */
+ NTSTATUS status; /* return status */
+
+} SAMR_R_OPEN_DOMAIN;
+
+#define MAX_SAM_ENTRIES_W2K 0x400
+#define MAX_SAM_ENTRIES_W95 50
+/* The following should be the greater of the preceeding two. */
+#define MAX_SAM_ENTRIES MAX_SAM_ENTRIES_W2K
+
+typedef struct samr_entry_info
+{
+ uint32 rid;
+ UNIHDR hdr_name;
+
+} SAM_ENTRY;
+
+
+/* SAMR_Q_ENUM_DOMAINS - SAM rids and names */
+typedef struct q_samr_enum_domains_info
+{
+ POLICY_HND pol; /* policy handle */
+
+ uint32 start_idx; /* enumeration handle */
+ uint32 max_size; /* 0x0000 ffff */
+
+} SAMR_Q_ENUM_DOMAINS;
+
+/* SAMR_R_ENUM_DOMAINS - SAM rids and Domain names */
+typedef struct r_samr_enum_domains_info
+{
+ uint32 next_idx; /* next starting index required for enum */
+ uint32 ptr_entries1;
+
+ uint32 num_entries2;
+ uint32 ptr_entries2;
+
+ uint32 num_entries3;
+
+ SAM_ENTRY *sam;
+ UNISTR2 *uni_dom_name;
+
+ uint32 num_entries4;
+
+ NTSTATUS status;
+
+} SAMR_R_ENUM_DOMAINS;
+
+/* SAMR_Q_ENUM_DOM_USERS - SAM rids and names */
+typedef struct q_samr_enum_dom_users_info
+{
+ POLICY_HND pol; /* policy handle */
+
+ uint32 start_idx; /* number of values (0 indicates unlimited?) */
+ uint16 acb_mask; /* 0x0000 indicates all */
+ uint16 unknown_1; /* 0x0000 */
+
+ uint32 max_size; /* 0x0000 ffff */
+
+} SAMR_Q_ENUM_DOM_USERS;
+
+
+/* SAMR_R_ENUM_DOM_USERS - SAM rids and names */
+typedef struct r_samr_enum_dom_users_info
+{
+ uint32 next_idx; /* next starting index required for enum */
+ uint32 ptr_entries1;
+
+ uint32 num_entries2;
+ uint32 ptr_entries2;
+
+ uint32 num_entries3;
+
+ SAM_ENTRY *sam;
+ UNISTR2 *uni_acct_name;
+
+ uint32 num_entries4;
+
+ NTSTATUS status;
+
+} SAMR_R_ENUM_DOM_USERS;
+
+
+/* SAMR_Q_ENUM_DOM_GROUPS - SAM rids and names */
+typedef struct q_samr_enum_dom_groups_info
+{
+ POLICY_HND pol; /* policy handle */
+
+ /* this is possibly an enumeration context handle... */
+ uint32 start_idx; /* 0x0000 0000 */
+
+ uint32 max_size; /* 0x0000 ffff */
+
+} SAMR_Q_ENUM_DOM_GROUPS;
+
+
+/* SAMR_R_ENUM_DOM_GROUPS - SAM rids and names */
+typedef struct r_samr_enum_dom_groups_info
+{
+ uint32 next_idx;
+ uint32 ptr_entries1;
+
+ uint32 num_entries2;
+ uint32 ptr_entries2;
+
+ uint32 num_entries3;
+
+ SAM_ENTRY *sam;
+ UNISTR2 *uni_grp_name;
+
+ uint32 num_entries4;
+
+ NTSTATUS status;
+
+} SAMR_R_ENUM_DOM_GROUPS;
+
+
+/* SAMR_Q_ENUM_DOM_ALIASES - SAM rids and names */
+typedef struct q_samr_enum_dom_aliases_info
+{
+ POLICY_HND pol; /* policy handle */
+
+ /* this is possibly an enumeration context handle... */
+ uint32 start_idx; /* 0x0000 0000 */
+
+ uint32 max_size; /* 0x0000 ffff */
+
+} SAMR_Q_ENUM_DOM_ALIASES;
+
+
+/* SAMR_R_ENUM_DOM_ALIASES - SAM rids and names */
+typedef struct r_samr_enum_dom_aliases_info
+{
+ uint32 next_idx;
+ uint32 ptr_entries1;
+
+ uint32 num_entries2;
+ uint32 ptr_entries2;
+
+ uint32 num_entries3;
+
+ SAM_ENTRY *sam;
+ UNISTR2 *uni_grp_name;
+
+ uint32 num_entries4;
+
+ NTSTATUS status;
+
+} SAMR_R_ENUM_DOM_ALIASES;
+
+
+/* -- Level 1 Display Info - User Information -- */
+
+typedef struct samr_entry_info1
+{
+ uint32 user_idx;
+
+ uint32 rid_user;
+ uint16 acb_info;
+
+ UNIHDR hdr_acct_name;
+ UNIHDR hdr_user_name;
+ UNIHDR hdr_user_desc;
+
+} SAM_ENTRY1;
+
+typedef struct samr_str_entry_info1
+{
+ UNISTR2 uni_acct_name;
+ UNISTR2 uni_full_name;
+ UNISTR2 uni_acct_desc;
+
+} SAM_STR1;
+
+typedef struct sam_entry_info_1
+{
+ SAM_ENTRY1 *sam;
+ SAM_STR1 *str;
+
+} SAM_DISPINFO_1;
+
+
+/* -- Level 2 Display Info - Trust Account Information -- */
+
+typedef struct samr_entry_info2
+{
+ uint32 user_idx;
+
+ uint32 rid_user;
+ uint16 acb_info;
+
+ UNIHDR hdr_srv_name;
+ UNIHDR hdr_srv_desc;
+
+} SAM_ENTRY2;
+
+typedef struct samr_str_entry_info2
+{
+ UNISTR2 uni_srv_name;
+ UNISTR2 uni_srv_desc;
+
+} SAM_STR2;
+
+typedef struct sam_entry_info_2
+{
+ SAM_ENTRY2 *sam;
+ SAM_STR2 *str;
+
+} SAM_DISPINFO_2;
+
+
+/* -- Level 3 Display Info - Domain Group Information -- */
+
+typedef struct samr_entry_info3
+{
+ uint32 grp_idx;
+
+ uint32 rid_grp;
+ uint32 attr; /* SE_GROUP_xxx, usually 7 */
+
+ UNIHDR hdr_grp_name;
+ UNIHDR hdr_grp_desc;
+
+} SAM_ENTRY3;
+
+typedef struct samr_str_entry_info3
+{
+ UNISTR2 uni_grp_name;
+ UNISTR2 uni_grp_desc;
+
+} SAM_STR3;
+
+typedef struct sam_entry_info_3
+{
+ SAM_ENTRY3 *sam;
+ SAM_STR3 *str;
+
+} SAM_DISPINFO_3;
+
+
+/* -- Level 4 Display Info - User List (ASCII) -- */
+
+typedef struct samr_entry_info4
+{
+ uint32 user_idx;
+ STRHDR hdr_acct_name;
+
+} SAM_ENTRY4;
+
+typedef struct samr_str_entry_info4
+{
+ STRING2 acct_name;
+
+} SAM_STR4;
+
+typedef struct sam_entry_info_4
+{
+ SAM_ENTRY4 *sam;
+ SAM_STR4 *str;
+
+} SAM_DISPINFO_4;
+
+
+/* -- Level 5 Display Info - Group List (ASCII) -- */
+
+typedef struct samr_entry_info5
+{
+ uint32 grp_idx;
+ STRHDR hdr_grp_name;
+
+} SAM_ENTRY5;
+
+typedef struct samr_str_entry_info5
+{
+ STRING2 grp_name;
+
+} SAM_STR5;
+
+typedef struct sam_entry_info_5
+{
+ SAM_ENTRY5 *sam;
+ SAM_STR5 *str;
+
+} SAM_DISPINFO_5;
+
+
+typedef struct sam_dispinfo_ctr_info
+{
+ union
+ {
+ SAM_DISPINFO_1 *info1; /* users/names/descriptions */
+ SAM_DISPINFO_2 *info2; /* trust accounts */
+ SAM_DISPINFO_3 *info3; /* domain groups/descriptions */
+ SAM_DISPINFO_4 *info4; /* user list (ASCII) - used by Win95 */
+ SAM_DISPINFO_5 *info5; /* group list (ASCII) */
+ void *info; /* allows assignment without typecasting, */
+
+ } sam;
+
+} SAM_DISPINFO_CTR;
+
+
+/* SAMR_Q_QUERY_DISPINFO - SAM rids, names and descriptions */
+typedef struct q_samr_query_disp_info
+{
+ POLICY_HND domain_pol;
+
+ uint16 switch_level; /* see SAM_DISPINFO_CTR above */
+ /* align */
+
+ uint32 start_idx; /* start enumeration index */
+ uint32 max_entries; /* maximum number of entries to return */
+ uint32 max_size; /* recommended data size; if exceeded server
+ should return STATUS_MORE_ENTRIES */
+
+} SAMR_Q_QUERY_DISPINFO;
+
+
+/* SAMR_R_QUERY_DISPINFO */
+typedef struct r_samr_query_dispinfo_info
+{
+ uint32 total_size; /* total data size for all matching entries
+ (0 = uncalculated) */
+ uint32 data_size; /* actual data size returned = size of SAM_ENTRY
+ structures + total length of strings */
+
+ uint16 switch_level; /* see SAM_DISPINFO_CTR above */
+ /* align */
+
+ uint32 num_entries; /* number of entries returned */
+ uint32 ptr_entries;
+ uint32 num_entries2;
+
+ SAM_DISPINFO_CTR *ctr;
+
+ NTSTATUS status;
+
+} SAMR_R_QUERY_DISPINFO;
+
+
+/* SAMR_Q_DELETE_DOM_GROUP - delete domain group */
+typedef struct q_samr_delete_dom_group_info
+{
+ POLICY_HND group_pol; /* policy handle */
+
+} SAMR_Q_DELETE_DOM_GROUP;
+
+
+/* SAMR_R_DELETE_DOM_GROUP - delete domain group */
+typedef struct r_samr_delete_dom_group_info
+{
+ POLICY_HND pol; /* policy handle */
+ NTSTATUS status; /* return status */
+
+} SAMR_R_DELETE_DOM_GROUP;
+
+
+/* SAMR_Q_CREATE_DOM_GROUP - SAM create group */
+typedef struct q_samr_create_dom_group_info
+{
+ POLICY_HND pol; /* policy handle */
+
+ UNIHDR hdr_acct_desc;
+ UNISTR2 uni_acct_desc;
+
+ uint32 access_mask;
+
+} SAMR_Q_CREATE_DOM_GROUP;
+
+/* SAMR_R_CREATE_DOM_GROUP - SAM create group */
+typedef struct r_samr_create_dom_group_info
+{
+ POLICY_HND pol; /* policy handle */
+
+ uint32 rid;
+ NTSTATUS status;
+
+} SAMR_R_CREATE_DOM_GROUP;
+
+/* SAMR_Q_QUERY_GROUPINFO - SAM Group Info */
+typedef struct q_samr_query_group_info
+{
+ POLICY_HND pol; /* policy handle */
+
+ uint16 switch_level; /* 0x0001 seen */
+
+} SAMR_Q_QUERY_GROUPINFO;
+
+typedef struct samr_group_info1
+{
+ UNIHDR hdr_acct_name;
+
+ uint32 unknown_1; /* 0x0000 0003 - number of group members? */
+ uint32 num_members; /* 0x0000 0001 - number of group members? */
+
+ UNIHDR hdr_acct_desc;
+
+ UNISTR2 uni_acct_name;
+ UNISTR2 uni_acct_desc;
+
+} GROUP_INFO1;
+
+typedef struct samr_group_info3
+{
+ uint32 unknown_1; /* 0x0000 0003 - number of group members? */
+
+} GROUP_INFO3;
+
+typedef struct samr_group_info4
+{
+ uint16 level;
+ UNIHDR hdr_acct_desc;
+ UNISTR2 uni_acct_desc;
+
+} GROUP_INFO4;
+
+/* GROUP_INFO_CTR */
+typedef struct group_info_ctr
+{
+ uint16 switch_value1;
+
+ union
+ {
+ GROUP_INFO1 info1;
+ GROUP_INFO3 info3;
+ GROUP_INFO4 info4;
+
+ } group;
+
+} GROUP_INFO_CTR;
+
+/* SAMR_R_QUERY_GROUPINFO - SAM Group Info */
+typedef struct r_samr_query_groupinfo_info
+{
+ uint32 ptr;
+ GROUP_INFO_CTR *ctr;
+
+ NTSTATUS status;
+
+} SAMR_R_QUERY_GROUPINFO;
+
+
+/* SAMR_Q_SET_GROUPINFO - SAM Group Info */
+typedef struct q_samr_set_group_info
+{
+ POLICY_HND pol; /* policy handle */
+ GROUP_INFO_CTR *ctr;
+
+} SAMR_Q_SET_GROUPINFO;
+
+/* SAMR_R_SET_GROUPINFO - SAM Group Info */
+typedef struct r_samr_set_group_info
+{
+ NTSTATUS status;
+
+} SAMR_R_SET_GROUPINFO;
+
+
+/* SAMR_Q_DELETE_DOM_ALIAS - delete domain alias */
+typedef struct q_samr_delete_dom_alias_info
+{
+ POLICY_HND alias_pol; /* policy handle */
+
+} SAMR_Q_DELETE_DOM_ALIAS;
+
+
+/* SAMR_R_DELETE_DOM_ALIAS - delete domain alias */
+typedef struct r_samr_delete_dom_alias_info
+{
+ POLICY_HND pol; /* policy handle */
+ NTSTATUS status; /* return status */
+
+} SAMR_R_DELETE_DOM_ALIAS;
+
+
+/* SAMR_Q_CREATE_DOM_ALIAS - SAM create alias */
+typedef struct q_samr_create_dom_alias_info
+{
+ POLICY_HND dom_pol; /* policy handle */
+
+ UNIHDR hdr_acct_desc;
+ UNISTR2 uni_acct_desc;
+
+ uint32 access_mask; /* 0x001f000f */
+
+} SAMR_Q_CREATE_DOM_ALIAS;
+
+/* SAMR_R_CREATE_DOM_ALIAS - SAM create alias */
+typedef struct r_samr_create_dom_alias_info
+{
+ POLICY_HND alias_pol; /* policy handle */
+
+ uint32 rid;
+ NTSTATUS status;
+
+} SAMR_R_CREATE_DOM_ALIAS;
+
+/* SAMR_Q_QUERY_ALIASINFO - SAM Alias Info */
+typedef struct q_samr_query_alias_info
+{
+ POLICY_HND pol; /* policy handle */
+
+ uint16 switch_level; /* 0x0003 seen */
+
+} SAMR_Q_QUERY_ALIASINFO;
+
+typedef struct samr_alias_info1
+{
+ UNIHDR hdr_acct_name;
+ UNIHDR hdr_acct_desc;
+ uint32 num_member;
+ UNISTR2 uni_acct_name;
+ UNISTR2 uni_acct_desc;
+
+} ALIAS_INFO1;
+
+typedef struct samr_alias_info3
+{
+ UNIHDR hdr_acct_desc;
+ UNISTR2 uni_acct_desc;
+
+} ALIAS_INFO3;
+
+/* ALIAS_INFO_CTR */
+typedef struct alias_info_ctr
+{
+ uint16 switch_value1;
+ uint16 switch_value2;
+
+ union
+ {
+ ALIAS_INFO1 info1;
+ ALIAS_INFO3 info3;
+
+ } alias;
+
+} ALIAS_INFO_CTR;
+
+/* SAMR_R_QUERY_ALIASINFO - SAM alias info */
+typedef struct r_samr_query_aliasinfo_info
+{
+ uint32 ptr;
+ ALIAS_INFO_CTR ctr;
+
+ NTSTATUS status;
+
+} SAMR_R_QUERY_ALIASINFO;
+
+
+/* SAMR_Q_SET_ALIASINFO - SAM Alias Info */
+typedef struct q_samr_set_alias_info
+{
+ POLICY_HND alias_pol; /* policy handle */
+ ALIAS_INFO_CTR ctr;
+
+} SAMR_Q_SET_ALIASINFO;
+
+/* SAMR_R_SET_ALIASINFO - SAM alias info */
+typedef struct r_samr_set_aliasinfo_info
+{
+ NTSTATUS status;
+
+} SAMR_R_SET_ALIASINFO;
+
+
+/* SAMR_Q_QUERY_USERGROUPS - */
+typedef struct q_samr_query_usergroup_info
+{
+ POLICY_HND pol; /* policy handle associated with unknown id */
+
+} SAMR_Q_QUERY_USERGROUPS;
+
+/* SAMR_R_QUERY_USERGROUPS - probably a get sam info */
+typedef struct r_samr_query_usergroup_info
+{
+ uint32 ptr_0; /* pointer */
+ uint32 num_entries; /* number of RID groups */
+ uint32 ptr_1; /* pointer */
+ uint32 num_entries2; /* number of RID groups */
+
+ DOM_GID *gid; /* group info */
+
+ NTSTATUS status; /* return status */
+
+} SAMR_R_QUERY_USERGROUPS;
+
+/* SAM_USERINFO_CTR - sam user info */
+typedef struct sam_userinfo_ctr_info
+{
+ uint16 switch_value;
+
+ union
+ {
+ SAM_USER_INFO_10 *id10; /* auth-level 0x10 */
+ SAM_USER_INFO_11 *id11; /* auth-level 0x11 */
+ SAM_USER_INFO_12 *id12; /* auth-level 0x12 */
+ SAM_USER_INFO_20 *id20; /* auth-level 20 */
+ SAM_USER_INFO_21 *id21; /* auth-level 21 */
+ SAM_USER_INFO_23 *id23; /* auth-level 0x17 */
+ SAM_USER_INFO_24 *id24; /* auth-level 0x18 */
+ SAM_USER_INFO_25 *id25; /* auth-level 0x19 */
+ void* id; /* to make typecasting easy */
+
+ } info;
+
+} SAM_USERINFO_CTR;
+
+
+/* SAMR_Q_SET_USERINFO2 - set sam info */
+typedef struct q_samr_set_user_info2
+{
+ POLICY_HND pol; /* policy handle associated with user */
+ uint16 switch_value; /* 0x0010 */
+
+ SAM_USERINFO_CTR *ctr;
+
+} SAMR_Q_SET_USERINFO2;
+
+/* SAMR_R_SET_USERINFO2 - set sam info */
+typedef struct r_samr_set_user_info2
+{
+ NTSTATUS status; /* return status */
+
+} SAMR_R_SET_USERINFO2;
+
+/* SAMR_Q_SET_USERINFO - set sam info */
+typedef struct q_samr_set_user_info
+{
+ POLICY_HND pol; /* policy handle associated with user */
+ uint16 switch_value;
+ SAM_USERINFO_CTR *ctr;
+
+} SAMR_Q_SET_USERINFO;
+
+/* SAMR_R_SET_USERINFO - set sam info */
+typedef struct r_samr_set_user_info
+{
+ NTSTATUS status; /* return status */
+
+} SAMR_R_SET_USERINFO;
+
+
+/* SAMR_Q_QUERY_USERINFO - probably a get sam info */
+typedef struct q_samr_query_user_info
+{
+ POLICY_HND pol; /* policy handle associated with unknown id */
+ uint16 switch_value; /* 0x0015, 0x0011 or 0x0010 - 16 bit unknown */
+
+} SAMR_Q_QUERY_USERINFO;
+
+/* SAMR_R_QUERY_USERINFO - probably a get sam info */
+typedef struct r_samr_query_user_info
+{
+ uint32 ptr; /* pointer */
+ SAM_USERINFO_CTR *ctr;
+
+ NTSTATUS status; /* return status */
+
+} SAMR_R_QUERY_USERINFO;
+
+
+/****************************************************************************
+SAMR_Q_QUERY_USERALIASES - do a conversion from name to RID.
+
+the policy handle allocated by an "samr open secret" call is associated
+with a SID. this policy handle is what is queried here, *not* the SID
+itself. the response to the lookup rids is relative to this SID.
+*****************************************************************************/
+/* SAMR_Q_QUERY_USERALIASES */
+typedef struct q_samr_query_useraliases_info
+{
+ POLICY_HND pol; /* policy handle */
+
+ uint32 num_sids1; /* number of rids being looked up */
+ uint32 ptr; /* buffer pointer */
+ uint32 num_sids2; /* number of rids being looked up */
+
+ uint32 *ptr_sid; /* pointers to sids to be looked up */
+ DOM_SID2 *sid ; /* sids to be looked up. */
+
+} SAMR_Q_QUERY_USERALIASES;
+
+
+/* SAMR_R_QUERY_USERALIASES */
+typedef struct r_samr_query_useraliases_info
+{
+ uint32 num_entries;
+ uint32 ptr; /* undocumented buffer pointer */
+
+ uint32 num_entries2;
+ uint32 *rid; /* domain RIDs being looked up */
+
+ NTSTATUS status; /* return code */
+
+} SAMR_R_QUERY_USERALIASES;
+
+
+/****************************************************************************
+SAMR_Q_LOOKUP_NAMES - do a conversion from Names to RIDs+types.
+*****************************************************************************/
+/* SAMR_Q_LOOKUP_NAMES */
+typedef struct q_samr_lookup_names_info
+{
+ POLICY_HND pol; /* policy handle */
+
+ uint32 num_names1; /* number of names being looked up */
+ uint32 flags; /* 0x0000 03e8 - unknown */
+ uint32 ptr; /* 0x0000 0000 - 32 bit unknown */
+ uint32 num_names2; /* number of names being looked up */
+
+ UNIHDR *hdr_name; /* unicode account name header */
+ UNISTR2 *uni_name; /* unicode account name string */
+
+} SAMR_Q_LOOKUP_NAMES;
+
+
+/* SAMR_R_LOOKUP_NAMES */
+typedef struct r_samr_lookup_names_info
+{
+ uint32 num_rids1; /* number of aliases being looked up */
+ uint32 ptr_rids; /* pointer to aliases */
+ uint32 num_rids2; /* number of aliases being looked up */
+
+ uint32 *rids; /* rids */
+
+ uint32 num_types1; /* number of users in aliases being looked up */
+ uint32 ptr_types; /* pointer to users in aliases */
+ uint32 num_types2; /* number of users in aliases being looked up */
+
+ uint32 *types; /* SID_ENUM type */
+
+ NTSTATUS status; /* return code */
+
+} SAMR_R_LOOKUP_NAMES;
+
+
+/****************************************************************************
+SAMR_Q_LOOKUP_RIDS - do a conversion from RID groups to something.
+
+called to resolve domain RID groups.
+*****************************************************************************/
+/* SAMR_Q_LOOKUP_RIDS */
+typedef struct q_samr_lookup_rids_info
+{
+ POLICY_HND pol; /* policy handle */
+
+ uint32 num_rids1; /* number of rids being looked up */
+ uint32 flags; /* 0x0000 03e8 - unknown */
+ uint32 ptr; /* 0x0000 0000 - 32 bit unknown */
+ uint32 num_rids2; /* number of rids being looked up */
+
+ uint32 *rid; /* domain RIDs being looked up */
+
+} SAMR_Q_LOOKUP_RIDS;
+
+
+/****************************************************************************
+SAMR_R_LOOKUP_RIDS - do a conversion from group RID to names
+
+*****************************************************************************/
+/* SAMR_R_LOOKUP_RIDS */
+typedef struct r_samr_lookup_rids_info
+{
+ uint32 num_names1; /* number of aliases being looked up */
+ uint32 ptr_names; /* pointer to aliases */
+ uint32 num_names2; /* number of aliases being looked up */
+
+ UNIHDR *hdr_name; /* unicode account name header */
+ UNISTR2 *uni_name; /* unicode account name string */
+
+ uint32 num_types1; /* number of users in aliases being looked up */
+ uint32 ptr_types; /* pointer to users in aliases */
+ uint32 num_types2; /* number of users in aliases being looked up */
+
+ uint32 *type; /* SID_ENUM type */
+
+ NTSTATUS status;
+
+} SAMR_R_LOOKUP_RIDS;
+
+
+/* SAMR_Q_OPEN_USER - probably an open */
+typedef struct q_samr_open_user_info
+{
+ POLICY_HND domain_pol; /* policy handle */
+ uint32 access_mask; /* 32 bit unknown - 0x02011b */
+ uint32 user_rid; /* user RID */
+
+} SAMR_Q_OPEN_USER;
+
+
+/* SAMR_R_OPEN_USER - probably an open */
+typedef struct r_samr_open_user_info
+{
+ POLICY_HND user_pol; /* policy handle associated with unknown id */
+ NTSTATUS status; /* return status */
+
+} SAMR_R_OPEN_USER;
+
+
+/* SAMR_Q_CREATE_USER - probably a create */
+typedef struct q_samr_create_user_info
+{
+ POLICY_HND domain_pol; /* policy handle */
+
+ UNIHDR hdr_name; /* unicode account name header */
+ UNISTR2 uni_name; /* unicode account name */
+
+ uint32 acb_info; /* account control info */
+ uint32 access_mask; /* 0xe005 00b0 */
+
+} SAMR_Q_CREATE_USER;
+
+
+/* SAMR_R_CREATE_USER - probably a create */
+typedef struct r_samr_create_user_info
+{
+ POLICY_HND user_pol; /* policy handle associated with user */
+
+ uint32 access_granted;
+ uint32 user_rid; /* user RID */
+ NTSTATUS status; /* return status */
+
+} SAMR_R_CREATE_USER;
+
+
+/* SAMR_Q_DELETE_DOM_USER - delete domain user */
+typedef struct q_samr_delete_dom_user_info
+{
+ POLICY_HND user_pol; /* policy handle */
+
+} SAMR_Q_DELETE_DOM_USER;
+
+
+/* SAMR_R_DELETE_DOM_USER - delete domain user */
+typedef struct r_samr_delete_dom_user_info
+{
+ POLICY_HND pol; /* policy handle */
+ NTSTATUS status; /* return status */
+
+} SAMR_R_DELETE_DOM_USER;
+
+
+/* SAMR_Q_QUERY_GROUPMEM - query group members */
+typedef struct q_samr_query_groupmem_info
+{
+ POLICY_HND group_pol; /* policy handle */
+
+} SAMR_Q_QUERY_GROUPMEM;
+
+
+/* SAMR_R_QUERY_GROUPMEM - query group members */
+typedef struct r_samr_query_groupmem_info
+{
+ uint32 ptr;
+ uint32 num_entries;
+
+ uint32 ptr_rids;
+ uint32 ptr_attrs;
+
+ uint32 num_rids;
+ uint32 *rid;
+
+ uint32 num_attrs;
+ uint32 *attr;
+
+ NTSTATUS status;
+
+} SAMR_R_QUERY_GROUPMEM;
+
+
+/* SAMR_Q_DEL_GROUPMEM - probably an del group member */
+typedef struct q_samr_del_group_mem_info
+{
+ POLICY_HND pol; /* policy handle */
+ uint32 rid; /* rid */
+
+} SAMR_Q_DEL_GROUPMEM;
+
+
+/* SAMR_R_DEL_GROUPMEM - probably an del group member */
+typedef struct r_samr_del_group_mem_info
+{
+ NTSTATUS status; /* return status */
+
+} SAMR_R_DEL_GROUPMEM;
+
+
+/* SAMR_Q_ADD_GROUPMEM - probably an add group member */
+typedef struct q_samr_add_group_mem_info
+{
+ POLICY_HND pol; /* policy handle */
+
+ uint32 rid; /* rid */
+ uint32 unknown; /* 0x0000 0005 */
+
+} SAMR_Q_ADD_GROUPMEM;
+
+
+/* SAMR_R_ADD_GROUPMEM - probably an add group member */
+typedef struct r_samr_add_group_mem_info
+{
+ NTSTATUS status; /* return status */
+
+} SAMR_R_ADD_GROUPMEM;
+
+
+/* SAMR_Q_OPEN_GROUP - probably an open */
+typedef struct q_samr_open_group_info
+{
+ POLICY_HND domain_pol; /* policy handle */
+ uint32 access_mask; /* 0x0000 0001, 0x0000 0003, 0x0000 001f */
+ uint32 rid_group; /* rid */
+
+} SAMR_Q_OPEN_GROUP;
+
+
+/* SAMR_R_OPEN_GROUP - probably an open */
+typedef struct r_samr_open_group_info
+{
+ POLICY_HND pol; /* policy handle */
+ NTSTATUS status; /* return status */
+
+} SAMR_R_OPEN_GROUP;
+
+
+/* SAMR_Q_QUERY_ALIASMEM - query alias members */
+typedef struct q_samr_query_aliasmem_info
+{
+ POLICY_HND alias_pol; /* policy handle */
+
+} SAMR_Q_QUERY_ALIASMEM;
+
+
+/* SAMR_R_QUERY_ALIASMEM - query alias members */
+typedef struct r_samr_query_aliasmem_info
+{
+ uint32 num_sids;
+ uint32 ptr;
+ uint32 num_sids1;
+
+ DOM_SID2 *sid;
+
+ NTSTATUS status;
+
+} SAMR_R_QUERY_ALIASMEM;
+
+
+/* SAMR_Q_ADD_ALIASMEM - add alias member */
+typedef struct q_samr_add_alias_mem_info
+{
+ POLICY_HND alias_pol; /* policy handle */
+
+ DOM_SID2 sid; /* member sid to be added to the alias */
+
+} SAMR_Q_ADD_ALIASMEM;
+
+
+/* SAMR_R_ADD_ALIASMEM - add alias member */
+typedef struct r_samr_add_alias_mem_info
+{
+ NTSTATUS status; /* return status */
+
+} SAMR_R_ADD_ALIASMEM;
+
+
+/* SAMR_Q_DEL_ALIASMEM - add an add alias member */
+typedef struct q_samr_del_alias_mem_info
+{
+ POLICY_HND alias_pol; /* policy handle */
+
+ DOM_SID2 sid; /* member sid to be added to alias */
+
+} SAMR_Q_DEL_ALIASMEM;
+
+
+/* SAMR_R_DEL_ALIASMEM - delete alias member */
+typedef struct r_samr_del_alias_mem_info
+{
+ NTSTATUS status; /* return status */
+
+} SAMR_R_DEL_ALIASMEM;
+
+
+
+/* SAMR_Q_OPEN_ALIAS - probably an open */
+typedef struct q_samr_open_alias_info
+{
+ POLICY_HND dom_pol;
+
+ uint32 access_mask;
+ uint32 rid_alias;
+
+} SAMR_Q_OPEN_ALIAS;
+
+
+/* SAMR_R_OPEN_ALIAS - probably an open */
+typedef struct r_samr_open_alias_info
+{
+ POLICY_HND pol; /* policy handle */
+ NTSTATUS status; /* return status */
+
+} SAMR_R_OPEN_ALIAS;
+
+
+/* SAMR_Q_CONNECT_ANON - probably an open */
+typedef struct q_samr_connect_anon_info
+{
+ uint32 ptr; /* ptr? */
+ uint16 unknown_0; /* 0x005c */
+ uint16 unknown_1; /* 0x0001 */
+ uint32 access_mask;
+
+} SAMR_Q_CONNECT_ANON;
+
+/* SAMR_R_CONNECT_ANON - probably an open */
+typedef struct r_samr_connect_anon_info
+{
+ POLICY_HND connect_pol; /* policy handle */
+ NTSTATUS status; /* return status */
+
+} SAMR_R_CONNECT_ANON;
+
+/* SAMR_Q_CONNECT - probably an open */
+typedef struct q_samr_connect_info
+{
+ uint32 ptr_srv_name; /* pointer (to server name?) */
+ UNISTR2 uni_srv_name; /* unicode server name starting with '\\' */
+
+ uint32 access_mask;
+
+} SAMR_Q_CONNECT;
+
+
+/* SAMR_R_CONNECT - probably an open */
+typedef struct r_samr_connect_info
+{
+ POLICY_HND connect_pol; /* policy handle */
+ NTSTATUS status; /* return status */
+
+} SAMR_R_CONNECT;
+
+/* SAMR_Q_CONNECT4 */
+typedef struct q_samr_connect4_info
+{
+ uint32 ptr_srv_name; /* pointer to server name */
+ UNISTR2 uni_srv_name;
+
+ uint32 unk_0; /* possible server name type, 1 for IP num, 2 for name */
+ uint32 access_mask;
+} SAMR_Q_CONNECT4;
+
+/* SAMR_R_CONNECT4 - same format as connect */
+typedef struct r_samr_connect_info SAMR_R_CONNECT4;
+
+/* SAMR_Q_GET_DOM_PWINFO */
+typedef struct q_samr_get_dom_pwinfo
+{
+ uint32 ptr;
+ UNIHDR hdr_srv_name;
+ UNISTR2 uni_srv_name;
+
+} SAMR_Q_GET_DOM_PWINFO;
+
+/* SAMR_R_GET_DOM_PWINFO */
+typedef struct r_samr_get_dom_pwinfo
+{
+ /*
+ * Previously this was 3 uint16's. However, after some tests
+ * it appears that the data len for the signing needs to be 16.
+ * Not sure how 3 unit16's ever worked since the length always
+ * turned out to 12. 3 uint32's + NT_STATUS == 16 bytes. Tested
+ * using NT and 2k. --jerry
+ */
+ uint32 unk_0;
+ uint32 unk_1;
+ uint32 unk_2;
+ NTSTATUS status;
+
+} SAMR_R_GET_DOM_PWINFO;
+
+/* SAMR_ENC_PASSWD */
+typedef struct enc_passwd_info
+{
+ uint32 ptr;
+ uint8 pass[516];
+
+} SAMR_ENC_PASSWD;
+
+/* SAMR_ENC_HASH */
+typedef struct enc_hash_info
+{
+ uint32 ptr;
+ uint8 hash[16];
+
+} SAMR_ENC_HASH;
+
+/* SAMR_Q_CHGPASSWD_USER */
+typedef struct q_samr_chgpasswd_user_info
+{
+ uint32 ptr_0;
+
+ UNIHDR hdr_dest_host; /* server name unicode header */
+ UNISTR2 uni_dest_host; /* server name unicode string */
+
+ UNIHDR hdr_user_name; /* username unicode string header */
+ UNISTR2 uni_user_name; /* username unicode string */
+
+ SAMR_ENC_PASSWD nt_newpass;
+ SAMR_ENC_HASH nt_oldhash;
+
+ uint32 unknown; /* 0x0000 0001 */
+
+ SAMR_ENC_PASSWD lm_newpass;
+ SAMR_ENC_HASH lm_oldhash;
+
+} SAMR_Q_CHGPASSWD_USER;
+
+/* SAMR_R_CHGPASSWD_USER */
+typedef struct r_samr_chgpasswd_user_info
+{
+ NTSTATUS status; /* 0 == OK, C000006A (NT_STATUS_WRONG_PASSWORD) */
+
+} SAMR_R_CHGPASSWD_USER;
+
+
+/* SAMR_Q_REMOVE_SID_FOREIGN_DOMAIN */
+typedef struct q_samr_remove_sid_foreign_domain_info
+{
+ POLICY_HND dom_pol; /* policy handle */
+ DOM_SID2 sid; /* SID */
+
+} SAMR_Q_REMOVE_SID_FOREIGN_DOMAIN;
+
+
+/* SAMR_R_REMOVE_SID_FOREIGN_DOMAIN */
+typedef struct r_samr_remove_sid_foreign_domain_info
+{
+ NTSTATUS status; /* return status */
+
+} SAMR_R_REMOVE_SID_FOREIGN_DOMAIN;
+
+
+
+/* these are from the old rpc_samr.h - they are needed while the merge
+ is still going on */
+#define MAX_SAM_SIDS 15
+
+/* DOM_SID3 - security id */
+typedef struct sid_info_3
+{
+ uint16 len; /* length, bytes, including length of len :-) */
+ /* uint8 pad[2]; */
+
+ DOM_SID sid;
+
+} DOM_SID3;
+
+/* SAMR_Q_UNKNOWN_2E */
+typedef struct q_samr_unknown_2e_info
+{
+ POLICY_HND domain_pol; /* policy handle */
+ uint16 switch_value;
+
+} SAMR_Q_UNKNOWN_2E;
+
+/* SAMR_R_UNKNOWN_2E */
+typedef struct r_samr_unknown_2e_info
+{
+ uint32 ptr_0;
+ uint16 switch_value;
+ SAM_UNK_CTR *ctr;
+ NTSTATUS status; /* return status */
+
+} SAMR_R_UNKNOWN_2E;
+
+/* SAMR_Q_SET_DOMAIN_INFO */
+typedef struct q_samr_set_domain_info
+{
+ POLICY_HND domain_pol; /* policy handle */
+ uint16 switch_value0;
+ uint16 switch_value;
+ SAM_UNK_CTR *ctr;
+
+} SAMR_Q_SET_DOMAIN_INFO;
+
+/* SAMR_R_SET_DOMAIN_INFO */
+typedef struct r_samr_set_domain_info
+{
+ NTSTATUS status; /* return status */
+
+} SAMR_R_SET_DOMAIN_INFO;
+
+#endif /* _RPC_SAMR_H */
diff --git a/source/include/rpc_secdes.h b/source/include/rpc_secdes.h
new file mode 100644
index 00000000000..56145ac024c
--- /dev/null
+++ b/source/include/rpc_secdes.h
@@ -0,0 +1,469 @@
+/*
+ Unix SMB/CIFS implementation.
+ SMB parameters and setup
+ Copyright (C) Andrew Tridgell 1992-2000
+ 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 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 _RPC_SECDES_H /* _RPC_SECDES_H */
+#define _RPC_SECDES_H
+
+#define SEC_RIGHTS_QUERY_VALUE 0x00000001
+#define SEC_RIGHTS_SET_VALUE 0x00000002
+#define SEC_RIGHTS_CREATE_SUBKEY 0x00000004
+#define SEC_RIGHTS_ENUM_SUBKEYS 0x00000008
+#define SEC_RIGHTS_NOTIFY 0x00000010
+#define SEC_RIGHTS_CREATE_LINK 0x00000020
+#define SEC_RIGHTS_READ 0x00020019
+#define SEC_RIGHTS_FULL_CONTROL 0x000f003f
+#define SEC_RIGHTS_MAXIMUM_ALLOWED 0x02000000
+
+/* for ADS */
+#define SEC_RIGHTS_LIST_CONTENTS 0x4
+#define SEC_RIGHTS_LIST_OBJECT 0x80
+#define SEC_RIGHTS_READ_ALL_PROP 0x10
+#define SEC_RIGHTS_READ_PERMS 0x20000
+#define SEC_RIGHTS_WRITE_ALL_VALID 0x8
+#define SEC_RIGHTS_WRITE_ALL_PROP 0x20
+#define SEC_RIGHTS_MODIFY_OWNER 0x80000
+#define SEC_RIGHTS_MODIFY_PERMS 0x40000
+#define SEC_RIGHTS_CREATE_CHILD 0x1
+#define SEC_RIGHTS_DELETE_CHILD 0x2
+#define SEC_RIGHTS_DELETE_SUBTREE 0x40
+#define SEC_RIGHTS_DELETE 0x10000 /* advanced/special/object/delete */
+#define SEC_RIGHTS_EXTENDED 0x100 /* change/reset password, receive/send as*/
+#define SEC_RIGHTS_CHANGE_PASSWD SEC_RIGHTS_EXTENDED
+#define SEC_RIGHTS_RESET_PASSWD SEC_RIGHTS_EXTENDED
+#define SEC_RIGHTS_FULL_CTRL 0xf01ff
+
+#define SEC_ACE_OBJECT_PRESENT 0x00000001 /* thanks for Jim McDonough <jmcd@us.ibm.com> */
+#define SEC_ACE_OBJECT_INHERITED_PRESENT 0x00000002
+
+#define SEC_ACE_FLAG_OBJECT_INHERIT 0x1
+#define SEC_ACE_FLAG_CONTAINER_INHERIT 0x2
+#define SEC_ACE_FLAG_NO_PROPAGATE_INHERIT 0x4
+#define SEC_ACE_FLAG_INHERIT_ONLY 0x8
+#define SEC_ACE_FLAG_INHERITED_ACE 0x10 /* New for Windows 2000 */
+#define SEC_ACE_FLAG_VALID_INHERIT 0xf
+#define SEC_ACE_FLAG_SUCCESSFUL_ACCESS 0x40
+#define SEC_ACE_FLAG_FAILED_ACCESS 0x80
+
+#define SEC_ACE_TYPE_ACCESS_ALLOWED 0x0
+#define SEC_ACE_TYPE_ACCESS_DENIED 0x1
+#define SEC_ACE_TYPE_SYSTEM_AUDIT 0x2
+#define SEC_ACE_TYPE_SYSTEM_ALARM 0x3
+#define SEC_ACE_TYPE_ALLOWED_COMPOUND 0x4
+#define SEC_ACE_TYPE_ACCESS_ALLOWED_OBJECT 0x5
+#define SEC_ACE_TYPE_ACCESS_DENIED_OBJECT 0x6
+#define SEC_ACE_TYPE_SYSTEM_AUDIT_OBJECT 0x7
+#define SEC_ACE_TYPE_SYSTEM_ALARM_OBJECT 0x8
+
+#define SEC_DESC_OWNER_DEFAULTED 0x0001
+#define SEC_DESC_GROUP_DEFAULTED 0x0002
+#define SEC_DESC_DACL_PRESENT 0x0004
+#define SEC_DESC_DACL_DEFAULTED 0x0008
+#define SEC_DESC_SACL_PRESENT 0x0010
+#define SEC_DESC_SACL_DEFAULTED 0x0020
+#define SEC_DESC_DACL_TRUSTED 0x0040
+#define SEC_DESC_SERVER_SECURITY 0x0080
+/*
+ * New Windows 2000 bits.
+ */
+#define SE_DESC_DACL_AUTO_INHERIT_REQ 0x0100
+#define SE_DESC_SACL_AUTO_INHERIT_REQ 0x0200
+#define SE_DESC_DACL_AUTO_INHERITED 0x0400
+#define SE_DESC_SACL_AUTO_INHERITED 0x0800
+#define SE_DESC_DACL_PROTECTED 0x1000
+#define SE_DESC_SACL_PROTECTED 0x2000
+
+/* Don't know what this means. */
+#define SEC_DESC_RM_CONTROL_VALID 0x4000
+
+#define SEC_DESC_SELF_RELATIVE 0x8000
+
+/* security information */
+#define OWNER_SECURITY_INFORMATION 0x00000001
+#define GROUP_SECURITY_INFORMATION 0x00000002
+#define DACL_SECURITY_INFORMATION 0x00000004
+#define SACL_SECURITY_INFORMATION 0x00000008
+/* Extra W2K flags. */
+#define UNPROTECTED_SACL_SECURITY_INFORMATION 0x10000000
+#define UNPROTECTED_DACL_SECURITY_INFORMATION 0x20000000
+#define PROTECTED_SACL_SECURITY_INFORMATION 0x40000000
+#define PROTECTED_DACL_SECURITY_INFORMATION 0x80000000
+
+#define ALL_SECURITY_INFORMATION (OWNER_SECURITY_INFORMATION|GROUP_SECURITY_INFORMATION|\
+ DACL_SECURITY_INFORMATION|SACL_SECURITY_INFORMATION|\
+ UNPROTECTED_SACL_SECURITY_INFORMATION|\
+ UNPROTECTED_DACL_SECURITY_INFORMATION|\
+ PROTECTED_SACL_SECURITY_INFORMATION|\
+ PROTECTED_DACL_SECURITY_INFORMATION)
+
+/* SEC_ACCESS */
+typedef struct security_info_info
+{
+ uint32 mask;
+
+} SEC_ACCESS;
+
+/* SEC_ACE */
+typedef struct security_ace_info
+{
+ uint8 type; /* xxxx_xxxx_ACE_TYPE - e.g allowed / denied etc */
+ uint8 flags; /* xxxx_INHERIT_xxxx - e.g OBJECT_INHERIT_ACE */
+ uint16 size;
+
+ SEC_ACCESS info;
+
+ /* this stuff may be present when type is XXXX_TYPE_XXXX_OBJECT */
+ uint32 obj_flags; /* xxxx_ACE_OBJECT_xxxx e.g present/inherited present etc */
+ struct uuid obj_guid; /* object GUID */
+ struct uuid inh_guid; /* inherited object GUID */
+ /* eof object stuff */
+
+ DOM_SID trustee;
+
+} SEC_ACE;
+#define SEC_ACE_HEADER_SIZE (2 * sizeof(uint8) + sizeof(uint16) + sizeof(uint32))
+
+#ifndef ACL_REVISION
+#define ACL_REVISION 0x3
+#endif
+
+#ifndef NT4_ACL_REVISION
+#define NT4_ACL_REVISION 0x2
+#endif
+
+#ifndef _SEC_ACL
+/* SEC_ACL */
+typedef struct security_acl_info
+{
+ uint16 revision; /* 0x0003 */
+ uint16 size; /* size in bytes of the entire ACL structure */
+ uint32 num_aces; /* number of Access Control Entries */
+
+ SEC_ACE *ace;
+
+} SEC_ACL;
+#define SEC_ACL_HEADER_SIZE (2 * sizeof(uint16) + sizeof(uint32))
+#define _SEC_ACL
+#endif
+
+#ifndef SEC_DESC_REVISION
+#define SEC_DESC_REVISION 0x1
+#endif
+
+#ifndef _SEC_DESC
+/* SEC_DESC */
+typedef struct security_descriptor_info
+{
+ uint16 revision; /* 0x0001 */
+ uint16 type; /* SEC_DESC_xxxx flags */
+
+ uint32 off_owner_sid; /* offset to owner sid */
+ uint32 off_grp_sid ; /* offset to group sid */
+ uint32 off_sacl ; /* offset to system list of permissions */
+ uint32 off_dacl ; /* offset to list of permissions */
+
+ SEC_ACL *dacl; /* user ACL */
+ SEC_ACL *sacl; /* system ACL */
+ DOM_SID *owner_sid;
+ DOM_SID *grp_sid;
+
+} SEC_DESC;
+#define SEC_DESC_HEADER_SIZE (2 * sizeof(uint16) + 4 * sizeof(uint32))
+#define _SEC_DESC
+#endif
+
+#ifndef _SEC_DESC_BUF
+/* SEC_DESC_BUF */
+typedef struct sec_desc_buf_info
+{
+ uint32 max_len;
+ uint32 ptr;
+ uint32 len;
+
+ SEC_DESC *sec;
+
+} SEC_DESC_BUF;
+#define _SEC_DESC_BUF
+#endif
+
+/* A type to describe the mapping of generic access rights to object
+ specific access rights. */
+
+typedef struct generic_mapping {
+ uint32 generic_read;
+ uint32 generic_write;
+ uint32 generic_execute;
+ uint32 generic_all;
+} GENERIC_MAPPING;
+
+typedef struct standard_mapping {
+ uint32 std_read;
+ uint32 std_write;
+ uint32 std_execute;
+ uint32 std_all;
+} STANDARD_MAPPING;
+
+
+/* Security Access Masks Rights */
+
+#define SPECIFIC_RIGHTS_MASK 0x0000FFFF
+#define STANDARD_RIGHTS_MASK 0x00FF0000
+#define GENERIC_RIGHTS_MASK 0xF0000000
+
+#define SEC_RIGHT_SYSTEM_SECURITY 0x01000000
+#define SEC_RIGHT_MAXIMUM_ALLOWED 0x02000000
+
+/* Generic access rights */
+
+#define GENERIC_RIGHT_ALL_ACCESS 0x10000000
+#define GENERIC_RIGHT_EXECUTE_ACCESS 0x20000000
+#define GENERIC_RIGHT_WRITE_ACCESS 0x40000000
+#define GENERIC_RIGHT_READ_ACCESS 0x80000000
+
+/* Standard access rights. */
+
+#define STD_RIGHT_DELETE_ACCESS 0x00010000
+#define STD_RIGHT_READ_CONTROL_ACCESS 0x00020000
+#define STD_RIGHT_WRITE_DAC_ACCESS 0x00040000
+#define STD_RIGHT_WRITE_OWNER_ACCESS 0x00080000
+#define STD_RIGHT_SYNCHRONIZE_ACCESS 0x00100000
+
+#define STD_RIGHT_ALL_ACCESS 0x001F0000
+
+/* Combinations of standard masks. */
+#define STANDARD_RIGHTS_ALL_ACCESS STD_RIGHT_ALL_ACCESS /* 0x001f0000 */
+#define STANDARD_RIGHTS_EXECUTE_ACCESS STD_RIGHT_READ_CONTROL_ACCESS /* 0x00020000 */
+#define STANDARD_RIGHTS_READ_ACCESS STD_RIGHT_READ_CONTROL_ACCESS /* 0x00020000 */
+#define STANDARD_RIGHTS_WRITE_ACCESS STD_RIGHT_READ_CONTROL_ACCESS /* 0x00020000 */
+#define STANDARD_RIGHTS_REQUIRED_ACCESS \
+ (STD_RIGHT_DELETE_ACCESS | \
+ STD_RIGHT_READ_CONTROL_ACCESS | \
+ STD_RIGHT_WRITE_DAC_ACCESS | \
+ STD_RIGHT_WRITE_OWNER_ACCESS) /* 0x000f0000 */
+
+/* File Object specific access rights */
+
+#define SA_RIGHT_FILE_READ_DATA 0x00000001
+#define SA_RIGHT_FILE_WRITE_DATA 0x00000002
+#define SA_RIGHT_FILE_APPEND_DATA 0x00000004
+#define SA_RIGHT_FILE_READ_EA 0x00000008
+#define SA_RIGHT_FILE_WRITE_EA 0x00000010
+#define SA_RIGHT_FILE_EXECUTE 0x00000020
+#define SA_RIGHT_FILE_DELETE_CHILD 0x00000040
+#define SA_RIGHT_FILE_READ_ATTRIBUTES 0x00000080
+#define SA_RIGHT_FILE_WRITE_ATTRIBUTES 0x00000100
+
+#define SA_RIGHT_FILE_ALL_ACCESS 0x000001FF
+
+#define GENERIC_RIGHTS_FILE_ALL_ACCESS \
+ (STANDARD_RIGHTS_REQUIRED_ACCESS| \
+ STD_RIGHT_SYNCHRONIZE_ACCESS | \
+ SA_RIGHT_FILE_ALL_ACCESS)
+
+#define GENERIC_RIGHTS_FILE_READ \
+ (STANDARD_RIGHTS_READ_ACCESS | \
+ STD_RIGHT_SYNCHRONIZE_ACCESS | \
+ SA_RIGHT_FILE_READ_DATA | \
+ SA_RIGHT_FILE_READ_ATTRIBUTES | \
+ SA_RIGHT_FILE_READ_EA)
+
+#define GENERIC_RIGHTS_FILE_WRITE \
+ (STANDARD_RIGHTS_WRITE_ACCESS | \
+ STD_RIGHT_SYNCHRONIZE_ACCESS | \
+ SA_RIGHT_FILE_WRITE_DATA | \
+ SA_RIGHT_FILE_WRITE_ATTRIBUTES | \
+ SA_RIGHT_FILE_WRITE_EA | \
+ SA_RIGHT_FILE_APPEND_DATA)
+
+#define GENERIC_RIGHTS_FILE_EXECUTE \
+ (STANDARD_RIGHTS_EXECUTE_ACCESS | \
+ SA_RIGHT_FILE_READ_ATTRIBUTES | \
+ SA_RIGHT_FILE_EXECUTE)
+
+
+/* SAM server specific access rights */
+
+#define SA_RIGHT_SAM_CONNECT_SERVER 0x00000001
+#define SA_RIGHT_SAM_SHUTDOWN_SERVER 0x00000002
+#define SA_RIGHT_SAM_INITIALISE_SERVER 0x00000004
+#define SA_RIGHT_SAM_CREATE_DOMAIN 0x00000008
+#define SA_RIGHT_SAM_ENUM_DOMAINS 0x00000010
+#define SA_RIGHT_SAM_OPEN_DOMAIN 0x00000020
+
+#define SA_RIGHT_SAM_ALL_ACCESS 0x0000003F
+
+#define GENERIC_RIGHTS_SAM_ALL_ACCESS \
+ (STANDARD_RIGHTS_REQUIRED_ACCESS| \
+ SA_RIGHT_SAM_ALL_ACCESS)
+
+#define GENERIC_RIGHTS_SAM_READ \
+ (STANDARD_RIGHTS_READ_ACCESS | \
+ SA_RIGHT_SAM_ENUM_DOMAINS)
+
+#define GENERIC_RIGHTS_SAM_WRITE \
+ (STANDARD_RIGHTS_WRITE_ACCESS | \
+ SA_RIGHT_SAM_CREATE_DOMAIN | \
+ SA_RIGHT_SAM_INITIALISE_SERVER | \
+ SA_RIGHT_SAM_SHUTDOWN_SERVER)
+
+#define GENERIC_RIGHTS_SAM_EXECUTE \
+ (STANDARD_RIGHTS_EXECUTE_ACCESS | \
+ SA_RIGHT_SAM_OPEN_DOMAIN | \
+ SA_RIGHT_SAM_CONNECT_SERVER)
+
+
+/* Domain Object specific access rights */
+
+#define SA_RIGHT_DOMAIN_LOOKUP_INFO_1 0x00000001
+#define SA_RIGHT_DOMAIN_SET_INFO_1 0x00000002
+#define SA_RIGHT_DOMAIN_LOOKUP_INFO_2 0x00000004
+#define SA_RIGHT_DOMAIN_SET_INFO_2 0x00000008
+#define SA_RIGHT_DOMAIN_CREATE_USER 0x00000010
+#define SA_RIGHT_DOMAIN_CREATE_GROUP 0x00000020
+#define SA_RIGHT_DOMAIN_CREATE_ALIAS 0x00000040
+#define SA_RIGHT_DOMAIN_LOOKUP_ALIAS_BY_MEM 0x00000080
+#define SA_RIGHT_DOMAIN_ENUM_ACCOUNTS 0x00000100
+#define SA_RIGHT_DOMAIN_OPEN_ACCOUNT 0x00000200
+#define SA_RIGHT_DOMAIN_SET_INFO_3 0x00000400
+
+#define SA_RIGHT_DOMAIN_ALL_ACCESS 0x000007FF
+
+#define GENERIC_RIGHTS_DOMAIN_ALL_ACCESS \
+ (STANDARD_RIGHTS_REQUIRED_ACCESS| \
+ SA_RIGHT_DOMAIN_ALL_ACCESS)
+
+#define GENERIC_RIGHTS_DOMAIN_READ \
+ (STANDARD_RIGHTS_READ_ACCESS | \
+ SA_RIGHT_DOMAIN_LOOKUP_ALIAS_BY_MEM | \
+ SA_RIGHT_DOMAIN_LOOKUP_INFO_2)
+
+#define GENERIC_RIGHTS_DOMAIN_WRITE \
+ (STANDARD_RIGHTS_WRITE_ACCESS | \
+ SA_RIGHT_DOMAIN_SET_INFO_3 | \
+ SA_RIGHT_DOMAIN_CREATE_ALIAS | \
+ SA_RIGHT_DOMAIN_CREATE_GROUP | \
+ SA_RIGHT_DOMAIN_CREATE_USER | \
+ SA_RIGHT_DOMAIN_SET_INFO_2 | \
+ SA_RIGHT_DOMAIN_SET_INFO_1)
+
+#define GENERIC_RIGHTS_DOMAIN_EXECUTE \
+ (STANDARD_RIGHTS_EXECUTE_ACCESS | \
+ SA_RIGHT_DOMAIN_OPEN_ACCOUNT | \
+ SA_RIGHT_DOMAIN_ENUM_ACCOUNTS | \
+ SA_RIGHT_DOMAIN_LOOKUP_INFO_1)
+
+
+/* User Object specific access rights */
+
+#define SA_RIGHT_USER_GET_NAME_ETC 0x00000001
+#define SA_RIGHT_USER_GET_LOCALE 0x00000002
+#define SA_RIGHT_USER_SET_LOC_COM 0x00000004
+#define SA_RIGHT_USER_GET_LOGONINFO 0x00000008
+#define SA_RIGHT_USER_ACCT_FLAGS_EXPIRY 0x00000010
+#define SA_RIGHT_USER_SET_ATTRIBUTES 0x00000020
+#define SA_RIGHT_USER_CHANGE_PASSWORD 0x00000040
+#define SA_RIGHT_USER_SET_PASSWORD 0x00000080
+#define SA_RIGHT_USER_GET_GROUPS 0x00000100
+#define SA_RIGHT_USER_READ_GROUP_MEM 0x00000200
+#define SA_RIGHT_USER_CHANGE_GROUP_MEM 0x00000400
+
+#define SA_RIGHT_USER_ALL_ACCESS 0x000007FF
+
+#define GENERIC_RIGHTS_USER_ALL_ACCESS \
+ (STANDARD_RIGHTS_REQUIRED_ACCESS| \
+ SA_RIGHT_USER_ALL_ACCESS) /* 0x000f07ff */
+
+#define GENERIC_RIGHTS_USER_READ \
+ (STANDARD_RIGHTS_READ_ACCESS | \
+ SA_RIGHT_USER_READ_GROUP_MEM | \
+ SA_RIGHT_USER_GET_GROUPS | \
+ SA_RIGHT_USER_ACCT_FLAGS_EXPIRY | \
+ SA_RIGHT_USER_GET_LOGONINFO | \
+ SA_RIGHT_USER_GET_LOCALE) /* 0x0002031a */
+
+#define GENERIC_RIGHTS_USER_WRITE \
+ (STANDARD_RIGHTS_WRITE_ACCESS | \
+ SA_RIGHT_USER_CHANGE_PASSWORD | \
+ SA_RIGHT_USER_SET_LOC_COM) /* 0x00020044 */
+
+#define GENERIC_RIGHTS_USER_EXECUTE \
+ (STANDARD_RIGHTS_EXECUTE_ACCESS | \
+ SA_RIGHT_USER_CHANGE_PASSWORD | \
+ SA_RIGHT_USER_GET_NAME_ETC ) /* 0x00020041 */
+
+
+/* Group Object specific access rights */
+
+#define SA_RIGHT_GROUP_LOOKUP_INFO 0x00000001
+#define SA_RIGHT_GROUP_SET_INFO 0x00000002
+#define SA_RIGHT_GROUP_ADD_MEMBER 0x00000004
+#define SA_RIGHT_GROUP_REMOVE_MEMBER 0x00000008
+#define SA_RIGHT_GROUP_GET_MEMBERS 0x00000010
+
+#define SA_RIGHT_GROUP_ALL_ACCESS 0x0000001F
+
+#define GENERIC_RIGHTS_GROUP_ALL_ACCESS \
+ (STANDARD_RIGHTS_REQUIRED_ACCESS| \
+ SA_RIGHT_GROUP_ALL_ACCESS) /* 0x000f001f */
+
+#define GENERIC_RIGHTS_GROUP_READ \
+ (STANDARD_RIGHTS_READ_ACCESS | \
+ SA_RIGHT_GROUP_GET_MEMBERS) /* 0x00020010 */
+
+#define GENERIC_RIGHTS_GROUP_WRITE \
+ (STANDARD_RIGHTS_WRITE_ACCESS | \
+ SA_RIGHT_GROUP_REMOVE_MEMBER | \
+ SA_RIGHT_GROUP_ADD_MEMBER | \
+ SA_RIGHT_GROUP_SET_INFO ) /* 0x0002000e */
+
+#define GENERIC_RIGHTS_GROUP_EXECUTE \
+ (STANDARD_RIGHTS_EXECUTE_ACCESS | \
+ SA_RIGHT_GROUP_LOOKUP_INFO) /* 0x00020001 */
+
+
+/* Alias Object specific access rights */
+
+#define SA_RIGHT_ALIAS_ADD_MEMBER 0x00000001
+#define SA_RIGHT_ALIAS_REMOVE_MEMBER 0x00000002
+#define SA_RIGHT_ALIAS_GET_MEMBERS 0x00000004
+#define SA_RIGHT_ALIAS_LOOKUP_INFO 0x00000008
+#define SA_RIGHT_ALIAS_SET_INFO 0x00000010
+
+#define SA_RIGHT_ALIAS_ALL_ACCESS 0x0000001F
+
+#define GENERIC_RIGHTS_ALIAS_ALL_ACCESS \
+ (STANDARD_RIGHTS_REQUIRED_ACCESS| \
+ SA_RIGHT_ALIAS_ALL_ACCESS) /* 0x000f001f */
+
+#define GENERIC_RIGHTS_ALIAS_READ \
+ (STANDARD_RIGHTS_READ_ACCESS | \
+ SA_RIGHT_ALIAS_GET_MEMBERS ) /* 0x00020004 */
+
+#define GENERIC_RIGHTS_ALIAS_WRITE \
+ (STANDARD_RIGHTS_WRITE_ACCESS | \
+ SA_RIGHT_ALIAS_REMOVE_MEMBER | \
+ SA_RIGHT_ALIAS_ADD_MEMBER | \
+ SA_RIGHT_ALIAS_SET_INFO ) /* 0x00020013 */
+
+#define GENERIC_RIGHTS_ALIAS_EXECUTE \
+ (STANDARD_RIGHTS_EXECUTE_ACCESS | \
+ SA_RIGHT_ALIAS_LOOKUP_INFO ) /* 0x00020008 */
+
+#endif /* _RPC_SECDES_H */
diff --git a/source/include/rpc_shutdown.h b/source/include/rpc_shutdown.h
new file mode 100644
index 00000000000..b8e50b835f5
--- /dev/null
+++ b/source/include/rpc_shutdown.h
@@ -0,0 +1,70 @@
+/*
+ Unix SMB/CIFS implementation.
+ SMB parameters and setup
+ 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 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 _RPC_SHUTDOWN_H /* _RPC_SHUTDOWN_H */
+#define _RPC_SHUTDOWN_H
+
+
+/* Implemented */
+#define SHUTDOWN_INIT 0x00
+#define SHUTDOWN_ABORT 0x01
+/* NOT IMPLEMENTED
+#define SHUTDOWN_INIT_EX 0x02
+*/
+
+/* SHUTDOWN_Q_INIT */
+typedef struct q_shutodwn_init_info
+{
+ uint32 ptr_server;
+ uint16 server;
+ uint32 ptr_msg;
+ UNIHDR hdr_msg; /* shutdown message */
+ UNISTR2 uni_msg; /* seconds */
+ uint32 timeout; /* seconds */
+ uint8 force; /* boolean: force shutdown */
+ uint8 reboot; /* boolean: reboot on shutdown */
+
+} SHUTDOWN_Q_INIT;
+
+/* SHUTDOWN_R_INIT */
+typedef struct r_shutdown_init_info
+{
+ NTSTATUS status; /* return status */
+
+} SHUTDOWN_R_INIT;
+
+/* SHUTDOWN_Q_ABORT */
+typedef struct q_shutdown_abort_info
+{
+ uint32 ptr_server;
+ uint16 server;
+
+} SHUTDOWN_Q_ABORT;
+
+/* SHUTDOWN_R_ABORT */
+typedef struct r_shutdown_abort_info
+{
+ NTSTATUS status; /* return status */
+
+} SHUTDOWN_R_ABORT;
+
+
+#endif /* _RPC_SHUTDOWN_H */
+
diff --git a/source/include/rpc_spoolss.h b/source/include/rpc_spoolss.h
new file mode 100755
index 00000000000..d9fc0c6a6ab
--- /dev/null
+++ b/source/include/rpc_spoolss.h
@@ -0,0 +1,2234 @@
+/*
+ Unix SMB/Netbios implementation.
+ Version 1.9.
+ SMB parameters and setup
+ Copyright (C) Andrew Tridgell 1992-2000,
+ Copyright (C) Luke Kenneth Casson Leighton 1996-2000,
+ Copyright (C) Jean Francois Micouleau 1998-2000.
+ Copyright (C) Gerald Carter 2001-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 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 _RPC_SPOOLSS_H /* _RPC_SPOOLSS_H */
+#define _RPC_SPOOLSS_H
+
+/* spoolss pipe: this are the calls which are not implemented ...
+#define SPOOLSS_GETPRINTERDRIVER 0x0b
+#define SPOOLSS_READPRINTER 0x16
+#define SPOOLSS_WAITFORPRINTERCHANGE 0x1c
+#define SPOOLSS_ADDPORT 0x25
+#define SPOOLSS_CONFIGUREPORT 0x26
+#define SPOOLSS_DELETEPORT 0x27
+#define SPOOLSS_CREATEPRINTERIC 0x28
+#define SPOOLSS_PLAYGDISCRIPTONPRINTERIC 0x29
+#define SPOOLSS_DELETEPRINTERIC 0x2a
+#define SPOOLSS_ADDPRINTERCONNECTION 0x2b
+#define SPOOLSS_DELETEPRINTERCONNECTION 0x2c
+#define SPOOLSS_PRINTERMESSAGEBOX 0x2d
+#define SPOOLSS_ADDMONITOR 0x2e
+#define SPOOLSS_DELETEMONITOR 0x2f
+#define SPOOLSS_DELETEPRINTPROCESSOR 0x30
+#define SPOOLSS_ADDPRINTPROVIDOR 0x31
+#define SPOOLSS_DELETEPRINTPROVIDOR 0x32
+#define SPOOLSS_FINDFIRSTPRINTERCHANGENOTIFICATION 0x36
+#define SPOOLSS_FINDNEXTPRINTERCHANGENOTIFICATION 0x37
+#define SPOOLSS_ROUTERFINDFIRSTPRINTERNOTIFICATIONOLD 0x39
+#define SPOOLSS_ADDPORTEX 0x3d
+#define SPOOLSS_REMOTEFINDFIRSTPRINTERCHANGENOTIFICATION0x3e
+#define SPOOLSS_SPOOLERINIT 0x3f
+#define SPOOLSS_RESETPRINTEREX 0x40
+*/
+
+/* those are implemented */
+#define SPOOLSS_ENUMPRINTERS 0x00
+#define SPOOLSS_OPENPRINTER 0x01
+#define SPOOLSS_SETJOB 0x02
+#define SPOOLSS_GETJOB 0x03
+#define SPOOLSS_ENUMJOBS 0x04
+#define SPOOLSS_ADDPRINTER 0x05
+#define SPOOLSS_DELETEPRINTER 0x06
+#define SPOOLSS_SETPRINTER 0x07
+#define SPOOLSS_GETPRINTER 0x08
+#define SPOOLSS_ADDPRINTERDRIVER 0x09
+#define SPOOLSS_ENUMPRINTERDRIVERS 0x0a
+#define SPOOLSS_GETPRINTERDRIVERDIRECTORY 0x0c
+#define SPOOLSS_DELETEPRINTERDRIVER 0x0d
+#define SPOOLSS_ADDPRINTPROCESSOR 0x0e
+#define SPOOLSS_ENUMPRINTPROCESSORS 0x0f
+#define SPOOLSS_GETPRINTPROCESSORDIRECTORY 0x10
+#define SPOOLSS_STARTDOCPRINTER 0x11
+#define SPOOLSS_STARTPAGEPRINTER 0x12
+#define SPOOLSS_WRITEPRINTER 0x13
+#define SPOOLSS_ENDPAGEPRINTER 0x14
+#define SPOOLSS_ABORTPRINTER 0x15
+#define SPOOLSS_ENDDOCPRINTER 0x17
+#define SPOOLSS_ADDJOB 0x18
+#define SPOOLSS_SCHEDULEJOB 0x19
+#define SPOOLSS_GETPRINTERDATA 0x1a
+#define SPOOLSS_SETPRINTERDATA 0x1b
+#define SPOOLSS_CLOSEPRINTER 0x1d
+#define SPOOLSS_ADDFORM 0x1e
+#define SPOOLSS_DELETEFORM 0x1f
+#define SPOOLSS_GETFORM 0x20
+#define SPOOLSS_SETFORM 0x21
+#define SPOOLSS_ENUMFORMS 0x22
+#define SPOOLSS_ENUMPORTS 0x23
+#define SPOOLSS_ENUMMONITORS 0x24
+#define SPOOLSS_ENUMPRINTPROCDATATYPES 0x33
+#define SPOOLSS_RESETPRINTER 0x34
+#define SPOOLSS_GETPRINTERDRIVER2 0x35
+#define SPOOLSS_FCPN 0x38 /* FindClosePrinterNotify */
+#define SPOOLSS_REPLYOPENPRINTER 0x3a
+#define SPOOLSS_ROUTERREPLYPRINTER 0x3b
+#define SPOOLSS_REPLYCLOSEPRINTER 0x3c
+#define SPOOLSS_RFFPCNEX 0x41 /* RemoteFindFirstPrinterChangeNotifyEx */
+#define SPOOLSS_RRPCN 0x42 /* RouteRefreshPrinterChangeNotification */
+#define SPOOLSS_RFNPCNEX 0x43 /* RemoteFindNextPrinterChangeNotifyEx */
+#define SPOOLSS_OPENPRINTEREX 0x45
+#define SPOOLSS_ADDPRINTEREX 0x46
+#define SPOOLSS_ENUMPRINTERDATA 0x48
+#define SPOOLSS_DELETEPRINTERDATA 0x49
+#define SPOOLSS_SETPRINTERDATAEX 0x4d
+#define SPOOLSS_GETPRINTERDATAEX 0x4e
+#define SPOOLSS_ENUMPRINTERDATAEX 0x4f
+#define SPOOLSS_ENUMPRINTERKEY 0x50
+#define SPOOLSS_DELETEPRINTERDATAEX 0x51
+#define SPOOLSS_DELETEPRINTERKEY 0x52
+#define SPOOLSS_DELETEPRINTERDRIVEREX 0x54
+#define SPOOLSS_ADDPRINTERDRIVEREX 0x59
+
+
+#define PRINTER_CONTROL_UNPAUSE 0x00000000
+#define PRINTER_CONTROL_PAUSE 0x00000001
+#define PRINTER_CONTROL_RESUME 0x00000002
+#define PRINTER_CONTROL_PURGE 0x00000003
+#define PRINTER_CONTROL_SET_STATUS 0x00000004
+
+#define PRINTER_STATUS_OK 0x00000000
+#define PRINTER_STATUS_PAUSED 0x00000001
+#define PRINTER_STATUS_ERROR 0x00000002
+#define PRINTER_STATUS_PENDING_DELETION 0x00000004
+#define PRINTER_STATUS_PAPER_JAM 0x00000008
+
+#define PRINTER_STATUS_PAPER_OUT 0x00000010
+#define PRINTER_STATUS_MANUAL_FEED 0x00000020
+#define PRINTER_STATUS_PAPER_PROBLEM 0x00000040
+#define PRINTER_STATUS_OFFLINE 0x00000080
+
+#define PRINTER_STATUS_IO_ACTIVE 0x00000100
+#define PRINTER_STATUS_BUSY 0x00000200
+#define PRINTER_STATUS_PRINTING 0x00000400
+#define PRINTER_STATUS_OUTPUT_BIN_FULL 0x00000800
+
+#define PRINTER_STATUS_NOT_AVAILABLE 0x00001000
+#define PRINTER_STATUS_WAITING 0x00002000
+#define PRINTER_STATUS_PROCESSING 0x00004000
+#define PRINTER_STATUS_INITIALIZING 0x00008000
+
+#define PRINTER_STATUS_WARMING_UP 0x00010000
+#define PRINTER_STATUS_TONER_LOW 0x00020000
+#define PRINTER_STATUS_NO_TONER 0x00040000
+#define PRINTER_STATUS_PAGE_PUNT 0x00080000
+
+#define PRINTER_STATUS_USER_INTERVENTION 0x00100000
+#define PRINTER_STATUS_OUT_OF_MEMORY 0x00200000
+#define PRINTER_STATUS_DOOR_OPEN 0x00400000
+#define PRINTER_STATUS_SERVER_UNKNOWN 0x00800000
+
+#define PRINTER_STATUS_POWER_SAVE 0x01000000
+
+#define SERVER_ACCESS_ADMINISTER 0x00000001
+#define SERVER_ACCESS_ENUMERATE 0x00000002
+#define PRINTER_ACCESS_ADMINISTER 0x00000004
+#define PRINTER_ACCESS_USE 0x00000008
+#define JOB_ACCESS_ADMINISTER 0x00000010
+
+/* JOB status codes. */
+
+#define JOB_STATUS_QUEUED 0x0000
+#define JOB_STATUS_PAUSED 0x0001
+#define JOB_STATUS_ERROR 0x0002
+#define JOB_STATUS_DELETING 0x0004
+#define JOB_STATUS_SPOOLING 0x0008
+#define JOB_STATUS_PRINTING 0x0010
+#define JOB_STATUS_OFFLINE 0x0020
+#define JOB_STATUS_PAPEROUT 0x0040
+#define JOB_STATUS_PRINTED 0x0080
+#define JOB_STATUS_DELETED 0x0100
+#define JOB_STATUS_BLOCKED 0x0200
+#define JOB_STATUS_USER_INTERVENTION 0x0400
+
+/* ACE masks for the various print permissions */
+
+#define PRINTER_ACE_FULL_CONTROL GENERIC_ALL_ACCESS
+#define PRINTER_ACE_MANAGE_DOCUMENTS READ_CONTROL_ACCESS
+#define PRINTER_ACE_PRINT \
+ (GENERIC_READ_ACCESS | GENERIC_WRITE_ACCESS | GENERIC_EXECUTE_ACCESS)
+
+/* Access rights for print servers */
+#define SERVER_ALL_ACCESS STANDARD_RIGHTS_REQUIRED_ACCESS|SERVER_ACCESS_ADMINISTER|SERVER_ACCESS_ENUMERATE
+#define SERVER_READ STANDARD_RIGHTS_READ_ACCESS|SERVER_ACCESS_ENUMERATE
+#define SERVER_WRITE STANDARD_RIGHTS_WRITE_ACCESS|SERVER_ACCESS_ADMINISTER|SERVER_ACCESS_ENUMERATE
+#define SERVER_EXECUTE STANDARD_RIGHTS_EXECUTE_ACCESS|SERVER_ACCESS_ENUMERATE
+
+/* Access rights for printers */
+#define PRINTER_ALL_ACCESS STANDARD_RIGHTS_REQUIRED_ACCESS|PRINTER_ACCESS_ADMINISTER|PRINTER_ACCESS_USE
+#define PRINTER_READ STANDARD_RIGHTS_READ_ACCESS|PRINTER_ACCESS_USE
+#define PRINTER_WRITE STANDARD_RIGHTS_WRITE_ACCESS|PRINTER_ACCESS_USE
+#define PRINTER_EXECUTE STANDARD_RIGHTS_EXECUTE_ACCESS|PRINTER_ACCESS_USE
+
+/* Access rights for jobs */
+#define JOB_ALL_ACCESS STANDARD_RIGHTS_REQUIRED_ACCESS|JOB_ACCESS_ADMINISTER
+#define JOB_READ STANDARD_RIGHTS_READ_ACCESS|JOB_ACCESS_ADMINISTER
+#define JOB_WRITE STANDARD_RIGHTS_WRITE_ACCESS|JOB_ACCESS_ADMINISTER
+#define JOB_EXECUTE STANDARD_RIGHTS_EXECUTE_ACCESS|JOB_ACCESS_ADMINISTER
+
+/* Notify field types */
+
+#define NOTIFY_ONE_VALUE 1 /* Notify data is stored in value1 */
+#define NOTIFY_TWO_VALUE 2 /* Notify data is stored in value2 */
+#define NOTIFY_POINTER 3 /* Data is a pointer to a buffer */
+#define NOTIFY_STRING 4 /* Data is a pointer to a buffer w/length */
+#define NOTIFY_SECDESC 5 /* Data is a security descriptor */
+
+#define PRINTER_NOTIFY_TYPE 0x00
+#define JOB_NOTIFY_TYPE 0x01
+
+#define MAX_PRINTER_NOTIFY 26
+#define MAX_JOB_NOTIFY 24
+
+#define MAX_NOTIFY_TYPE_FOR_NOW 26
+
+#define PRINTER_NOTIFY_SERVER_NAME 0x00
+#define PRINTER_NOTIFY_PRINTER_NAME 0x01
+#define PRINTER_NOTIFY_SHARE_NAME 0x02
+#define PRINTER_NOTIFY_PORT_NAME 0x03
+#define PRINTER_NOTIFY_DRIVER_NAME 0x04
+#define PRINTER_NOTIFY_COMMENT 0x05
+#define PRINTER_NOTIFY_LOCATION 0x06
+#define PRINTER_NOTIFY_DEVMODE 0x07
+#define PRINTER_NOTIFY_SEPFILE 0x08
+#define PRINTER_NOTIFY_PRINT_PROCESSOR 0x09
+#define PRINTER_NOTIFY_PARAMETERS 0x0A
+#define PRINTER_NOTIFY_DATATYPE 0x0B
+#define PRINTER_NOTIFY_SECURITY_DESCRIPTOR 0x0C
+#define PRINTER_NOTIFY_ATTRIBUTES 0x0D
+#define PRINTER_NOTIFY_PRIORITY 0x0E
+#define PRINTER_NOTIFY_DEFAULT_PRIORITY 0x0F
+#define PRINTER_NOTIFY_START_TIME 0x10
+#define PRINTER_NOTIFY_UNTIL_TIME 0x11
+#define PRINTER_NOTIFY_STATUS 0x12
+#define PRINTER_NOTIFY_STATUS_STRING 0x13
+#define PRINTER_NOTIFY_CJOBS 0x14
+#define PRINTER_NOTIFY_AVERAGE_PPM 0x15
+#define PRINTER_NOTIFY_TOTAL_PAGES 0x16
+#define PRINTER_NOTIFY_PAGES_PRINTED 0x17
+#define PRINTER_NOTIFY_TOTAL_BYTES 0x18
+#define PRINTER_NOTIFY_BYTES_PRINTED 0x19
+
+#define JOB_NOTIFY_PRINTER_NAME 0x00
+#define JOB_NOTIFY_MACHINE_NAME 0x01
+#define JOB_NOTIFY_PORT_NAME 0x02
+#define JOB_NOTIFY_USER_NAME 0x03
+#define JOB_NOTIFY_NOTIFY_NAME 0x04
+#define JOB_NOTIFY_DATATYPE 0x05
+#define JOB_NOTIFY_PRINT_PROCESSOR 0x06
+#define JOB_NOTIFY_PARAMETERS 0x07
+#define JOB_NOTIFY_DRIVER_NAME 0x08
+#define JOB_NOTIFY_DEVMODE 0x09
+#define JOB_NOTIFY_STATUS 0x0A
+#define JOB_NOTIFY_STATUS_STRING 0x0B
+#define JOB_NOTIFY_SECURITY_DESCRIPTOR 0x0C
+#define JOB_NOTIFY_DOCUMENT 0x0D
+#define JOB_NOTIFY_PRIORITY 0x0E
+#define JOB_NOTIFY_POSITION 0x0F
+#define JOB_NOTIFY_SUBMITTED 0x10
+#define JOB_NOTIFY_START_TIME 0x11
+#define JOB_NOTIFY_UNTIL_TIME 0x12
+#define JOB_NOTIFY_TIME 0x13
+#define JOB_NOTIFY_TOTAL_PAGES 0x14
+#define JOB_NOTIFY_PAGES_PRINTED 0x15
+#define JOB_NOTIFY_TOTAL_BYTES 0x16
+#define JOB_NOTIFY_BYTES_PRINTED 0x17
+
+#define PRINTER_NOTIFY_OPTIONS_REFRESH 0x01
+
+#define PRINTER_CHANGE_ADD_PRINTER 0x00000001
+#define PRINTER_CHANGE_SET_PRINTER 0x00000002
+#define PRINTER_CHANGE_DELETE_PRINTER 0x00000004
+#define PRINTER_CHANGE_FAILED_CONNECTION_PRINTER 0x00000008
+#define PRINTER_CHANGE_PRINTER (PRINTER_CHANGE_ADD_PRINTER | \
+ PRINTER_CHANGE_SET_PRINTER | \
+ PRINTER_CHANGE_DELETE_PRINTER | \
+ PRINTER_CHANGE_FAILED_CONNECTION_PRINTER )
+
+#define PRINTER_CHANGE_ADD_JOB 0x00000100
+#define PRINTER_CHANGE_SET_JOB 0x00000200
+#define PRINTER_CHANGE_DELETE_JOB 0x00000400
+#define PRINTER_CHANGE_WRITE_JOB 0x00000800
+#define PRINTER_CHANGE_JOB (PRINTER_CHANGE_ADD_JOB | \
+ PRINTER_CHANGE_SET_JOB | \
+ PRINTER_CHANGE_DELETE_JOB | \
+ PRINTER_CHANGE_WRITE_JOB )
+
+#define PRINTER_CHANGE_ADD_FORM 0x00010000
+#define PRINTER_CHANGE_SET_FORM 0x00020000
+#define PRINTER_CHANGE_DELETE_FORM 0x00040000
+#define PRINTER_CHANGE_FORM (PRINTER_CHANGE_ADD_FORM | \
+ PRINTER_CHANGE_SET_FORM | \
+ PRINTER_CHANGE_DELETE_FORM )
+
+#define PRINTER_CHANGE_ADD_PORT 0x00100000
+#define PRINTER_CHANGE_CONFIGURE_PORT 0x00200000
+#define PRINTER_CHANGE_DELETE_PORT 0x00400000
+#define PRINTER_CHANGE_PORT (PRINTER_CHANGE_ADD_PORT | \
+ PRINTER_CHANGE_CONFIGURE_PORT | \
+ PRINTER_CHANGE_DELETE_PORT )
+
+#define PRINTER_CHANGE_ADD_PRINT_PROCESSOR 0x01000000
+#define PRINTER_CHANGE_DELETE_PRINT_PROCESSOR 0x04000000
+#define PRINTER_CHANGE_PRINT_PROCESSOR (PRINTER_CHANGE_ADD_PRINT_PROCESSOR | \
+ PRINTER_CHANGE_DELETE_PRINT_PROCESSOR )
+
+#define PRINTER_CHANGE_ADD_PRINTER_DRIVER 0x10000000
+#define PRINTER_CHANGE_SET_PRINTER_DRIVER 0x20000000
+#define PRINTER_CHANGE_DELETE_PRINTER_DRIVER 0x40000000
+#define PRINTER_CHANGE_PRINTER_DRIVER (PRINTER_CHANGE_ADD_PRINTER_DRIVER | \
+ PRINTER_CHANGE_SET_PRINTER_DRIVER | \
+ PRINTER_CHANGE_DELETE_PRINTER_DRIVER )
+
+#define PRINTER_CHANGE_TIMEOUT 0x80000000
+#define PRINTER_CHANGE_ALL (PRINTER_CHANGE_JOB | \
+ PRINTER_CHANGE_FORM | \
+ PRINTER_CHANGE_PORT | \
+ PRINTER_CHANGE_PRINT_PROCESSOR | \
+ PRINTER_CHANGE_PRINTER_DRIVER )
+
+#define PRINTER_NOTIFY_INFO_DISCARDED 0x1
+
+/*
+ * Set of macros for flagging what changed in the PRINTER_INFO_2 struct
+ * when sending messages to other smbd's
+ */
+#define PRINTER_MESSAGE_NULL 0x00000000
+#define PRINTER_MESSAGE_DRIVER 0x00000001
+#define PRINTER_MESSAGE_COMMENT 0x00000002
+#define PRINTER_MESSAGE_PRINTERNAME 0x00000004
+#define PRINTER_MESSAGE_LOCATION 0x00000008
+#define PRINTER_MESSAGE_DEVMODE 0x00000010 /* not curently supported */
+#define PRINTER_MESSAGE_SEPFILE 0x00000020
+#define PRINTER_MESSAGE_PRINTPROC 0x00000040
+#define PRINTER_MESSAGE_PARAMS 0x00000080
+#define PRINTER_MESSAGE_DATATYPE 0x00000100
+#define PRINTER_MESSAGE_SECDESC 0x00000200
+#define PRINTER_MESSAGE_CJOBS 0x00000400
+#define PRINTER_MESSAGE_PORT 0x00000800
+#define PRINTER_MESSAGE_SHARENAME 0x00001000
+#define PRINTER_MESSAGE_ATTRIBUTES 0x00002000
+
+typedef struct printer_message_info {
+ uint32 low; /* PRINTER_CHANGE_XXX */
+ uint32 high; /* PRINTER_CHANGE_XXX */
+ fstring printer_name;
+ uint32 flags; /* PRINTER_MESSAGE_XXX */
+}
+PRINTER_MESSAGE_INFO;
+
+/*
+ * The printer attributes.
+ * I #defined all of them (grabbed form MSDN)
+ * I'm only using:
+ * ( SHARED | NETWORK | RAW_ONLY )
+ * RAW_ONLY _MUST_ be present otherwise NT will send an EMF file
+ */
+
+#define PRINTER_ATTRIBUTE_QUEUED 0x00000001
+#define PRINTER_ATTRIBUTE_DIRECT 0x00000002
+#define PRINTER_ATTRIBUTE_DEFAULT 0x00000004
+#define PRINTER_ATTRIBUTE_SHARED 0x00000008
+
+#define PRINTER_ATTRIBUTE_NETWORK 0x00000010
+#define PRINTER_ATTRIBUTE_HIDDEN 0x00000020
+#define PRINTER_ATTRIBUTE_LOCAL 0x00000040
+#define PRINTER_ATTRIBUTE_ENABLE_DEVQ 0x00000080
+
+#define PRINTER_ATTRIBUTE_KEEPPRINTEDJOBS 0x00000100
+#define PRINTER_ATTRIBUTE_DO_COMPLETE_FIRST 0x00000200
+#define PRINTER_ATTRIBUTE_WORK_OFFLINE 0x00000400
+#define PRINTER_ATTRIBUTE_ENABLE_BIDI 0x00000800
+
+#define PRINTER_ATTRIBUTE_RAW_ONLY 0x00001000
+#define PRINTER_ATTRIBUTE_PUBLISHED 0x00002000
+
+#define PRINTER_ATTRIBUTE_SAMBA (PRINTER_ATTRIBUTE_RAW_ONLY|\
+ PRINTER_ATTRIBUTE_SHARED|\
+ PRINTER_ATTRIBUTE_LOCAL)
+
+#define NO_PRIORITY 0
+#define MAX_PRIORITY 99
+#define MIN_PRIORITY 1
+#define DEF_PRIORITY 1
+
+/* the flags of the query */
+#define PRINTER_ENUM_DEFAULT 0x00000001
+#define PRINTER_ENUM_LOCAL 0x00000002
+#define PRINTER_ENUM_CONNECTIONS 0x00000004
+#define PRINTER_ENUM_FAVORITE 0x00000004
+#define PRINTER_ENUM_NAME 0x00000008
+#define PRINTER_ENUM_REMOTE 0x00000010
+#define PRINTER_ENUM_SHARED 0x00000020
+#define PRINTER_ENUM_NETWORK 0x00000040
+
+/* the flags of each printers */
+#define PRINTER_ENUM_UNKNOWN_8 0x00000008
+#define PRINTER_ENUM_EXPAND 0x00004000
+#define PRINTER_ENUM_CONTAINER 0x00008000
+#define PRINTER_ENUM_ICONMASK 0x00ff0000
+#define PRINTER_ENUM_ICON1 0x00010000
+#define PRINTER_ENUM_ICON2 0x00020000
+#define PRINTER_ENUM_ICON3 0x00040000
+#define PRINTER_ENUM_ICON4 0x00080000
+#define PRINTER_ENUM_ICON5 0x00100000
+#define PRINTER_ENUM_ICON6 0x00200000
+#define PRINTER_ENUM_ICON7 0x00400000
+#define PRINTER_ENUM_ICON8 0x00800000
+
+/* FLAGS for SPOOLSS_DELETEPRINTERDRIVEREX */
+
+#define DPD_DELETE_UNUSED_FILES 0x00000001
+#define DPD_DELETE_SPECIFIC_VERSION 0x00000002
+#define DPD_DELETE_ALL_FILES 0x00000004
+
+#define DRIVER_ANY_VERSION 0xffffffff
+#define DRIVER_MAX_VERSION 4
+
+/* FLAGS for SPOOLSS_ADDPRINTERDRIVEREX */
+
+#define APD_STRICT_UPGRADE 0x00000001
+#define APD_STRICT_DOWNGRADE 0x00000002
+#define APD_COPY_ALL_FILES 0x00000004
+#define APD_COPY_NEW_FILES 0x00000008
+
+
+/* this struct is undocumented */
+/* thanks to the ddk ... */
+typedef struct spool_user_1
+{
+ uint32 size; /* length of user_name & client_name + 2? */
+ uint32 client_name_ptr;
+ uint32 user_name_ptr;
+ uint32 build;
+ uint32 major;
+ uint32 minor;
+ uint32 processor;
+ UNISTR2 client_name;
+ UNISTR2 user_name;
+}
+SPOOL_USER_1;
+
+typedef struct spool_user_ctr_info
+{
+ uint32 level;
+ uint32 ptr;
+ SPOOL_USER_1 user1;
+}
+SPOOL_USER_CTR;
+
+/*
+ * various bits in the DEVICEMODE.fields member
+ */
+
+#define DEVMODE_ORIENTATION 0x00000001
+#define DEVMODE_PAPERSIZE 0x00000002
+#define DEVMODE_PAPERLENGTH 0x00000004
+#define DEVMODE_PAPERWIDTH 0x00000008
+#define DEVMODE_SCALE 0x00000010
+#define DEVMODE_POSITION 0x00000020
+#define DEVMODE_NUP 0x00000040
+#define DEVMODE_COPIES 0x00000100
+#define DEVMODE_DEFAULTSOURCE 0x00000200
+#define DEVMODE_PRINTQUALITY 0x00000400
+#define DEVMODE_COLOR 0x00000800
+#define DEVMODE_DUPLEX 0x00001000
+#define DEVMODE_YRESOLUTION 0x00002000
+#define DEVMODE_TTOPTION 0x00004000
+#define DEVMODE_COLLATE 0x00008000
+#define DEVMODE_FORMNAME 0x00010000
+#define DEVMODE_LOGPIXELS 0x00020000
+#define DEVMODE_BITSPERPEL 0x00040000
+#define DEVMODE_PELSWIDTH 0x00080000
+#define DEVMODE_PELSHEIGHT 0x00100000
+#define DEVMODE_DISPLAYFLAGS 0x00200000
+#define DEVMODE_DISPLAYFREQUENCY 0x00400000
+#define DEVMODE_ICMMETHOD 0x00800000
+#define DEVMODE_ICMINTENT 0x01000000
+#define DEVMODE_MEDIATYPE 0x02000000
+#define DEVMODE_DITHERTYPE 0x04000000
+#define DEVMODE_PANNINGWIDTH 0x08000000
+#define DEVMODE_PANNINGHEIGHT 0x10000000
+
+
+/*
+ * Devicemode structure
+ */
+
+typedef struct devicemode
+{
+ UNISTR devicename;
+ uint16 specversion;
+ uint16 driverversion;
+ uint16 size;
+ uint16 driverextra;
+ uint32 fields;
+ uint16 orientation;
+ uint16 papersize;
+ uint16 paperlength;
+ uint16 paperwidth;
+ uint16 scale;
+ uint16 copies;
+ uint16 defaultsource;
+ uint16 printquality;
+ uint16 color;
+ uint16 duplex;
+ uint16 yresolution;
+ uint16 ttoption;
+ uint16 collate;
+ UNISTR formname;
+ uint16 logpixels;
+ uint32 bitsperpel;
+ uint32 pelswidth;
+ uint32 pelsheight;
+ uint32 displayflags;
+ uint32 displayfrequency;
+ uint32 icmmethod;
+ uint32 icmintent;
+ uint32 mediatype;
+ uint32 dithertype;
+ uint32 reserved1;
+ uint32 reserved2;
+ uint32 panningwidth;
+ uint32 panningheight;
+ uint8 *private;
+}
+DEVICEMODE;
+
+typedef struct _devmode_cont
+{
+ uint32 size;
+ uint32 devmode_ptr;
+ DEVICEMODE *devmode;
+}
+DEVMODE_CTR;
+
+typedef struct _printer_default
+{
+ uint32 datatype_ptr;
+ UNISTR2 datatype;
+ DEVMODE_CTR devmode_cont;
+ uint32 access_required;
+}
+PRINTER_DEFAULT;
+
+/* SPOOL_Q_OPEN_PRINTER request to open a printer */
+typedef struct spool_q_open_printer
+{
+ uint32 printername_ptr;
+ UNISTR2 printername;
+ PRINTER_DEFAULT printer_default;
+}
+SPOOL_Q_OPEN_PRINTER;
+
+/* SPOOL_R_OPEN_PRINTER reply to an open printer */
+typedef struct spool_r_open_printer
+{
+ POLICY_HND handle; /* handle used along all transactions (20*uint8) */
+ WERROR status;
+}
+SPOOL_R_OPEN_PRINTER;
+
+/* SPOOL_Q_OPEN_PRINTER_EX request to open a printer */
+typedef struct spool_q_open_printer_ex
+{
+ uint32 printername_ptr;
+ UNISTR2 printername;
+ PRINTER_DEFAULT printer_default;
+ uint32 user_switch;
+ SPOOL_USER_CTR user_ctr;
+}
+SPOOL_Q_OPEN_PRINTER_EX;
+
+/* SPOOL_R_OPEN_PRINTER_EX reply to an open printer */
+typedef struct spool_r_open_printer_ex
+{
+ POLICY_HND handle; /* handle used along all transactions (20*uint8) */
+ WERROR status;
+}
+SPOOL_R_OPEN_PRINTER_EX;
+
+typedef struct spool_notify_option_type
+{
+ uint16 type;
+ uint16 reserved0;
+ uint32 reserved1;
+ uint32 reserved2;
+ uint32 count;
+ uint32 fields_ptr;
+ uint32 count2;
+ uint16 fields[MAX_NOTIFY_TYPE_FOR_NOW];
+}
+SPOOL_NOTIFY_OPTION_TYPE;
+
+typedef struct spool_notify_option_type_ctr
+{
+ uint32 count;
+ SPOOL_NOTIFY_OPTION_TYPE *type;
+}
+SPOOL_NOTIFY_OPTION_TYPE_CTR;
+
+
+
+typedef struct s_header_type
+{
+ uint32 type;
+ union
+ {
+ uint32 value;
+ UNISTR string;
+ }
+ data;
+}
+HEADER_TYPE;
+
+typedef struct new_buffer
+{
+ uint32 ptr;
+ uint32 size;
+ prs_struct prs;
+ uint32 struct_start;
+ uint32 string_at_end;
+}
+NEW_BUFFER;
+
+typedef struct spool_q_getprinterdata
+{
+ POLICY_HND handle;
+ UNISTR2 valuename;
+ uint32 size;
+}
+SPOOL_Q_GETPRINTERDATA;
+
+typedef struct spool_r_getprinterdata
+{
+ uint32 type;
+ uint32 size;
+ uint8 *data;
+ uint32 needed;
+ WERROR status;
+}
+SPOOL_R_GETPRINTERDATA;
+
+typedef struct spool_q_deleteprinterdata
+{
+ POLICY_HND handle;
+ UNISTR2 valuename;
+}
+SPOOL_Q_DELETEPRINTERDATA;
+
+typedef struct spool_r_deleteprinterdata
+{
+ WERROR status;
+}
+SPOOL_R_DELETEPRINTERDATA;
+
+typedef struct spool_q_closeprinter
+{
+ POLICY_HND handle;
+}
+SPOOL_Q_CLOSEPRINTER;
+
+typedef struct spool_r_closeprinter
+{
+ POLICY_HND handle;
+ WERROR status;
+}
+SPOOL_R_CLOSEPRINTER;
+
+typedef struct spool_q_startpageprinter
+{
+ POLICY_HND handle;
+}
+SPOOL_Q_STARTPAGEPRINTER;
+
+typedef struct spool_r_startpageprinter
+{
+ WERROR status;
+}
+SPOOL_R_STARTPAGEPRINTER;
+
+typedef struct spool_q_endpageprinter
+{
+ POLICY_HND handle;
+}
+SPOOL_Q_ENDPAGEPRINTER;
+
+typedef struct spool_r_endpageprinter
+{
+ WERROR status;
+}
+SPOOL_R_ENDPAGEPRINTER;
+
+
+typedef struct spool_q_deleteprinterdriver
+{
+ uint32 server_ptr;
+ UNISTR2 server;
+ UNISTR2 arch;
+ UNISTR2 driver;
+}
+SPOOL_Q_DELETEPRINTERDRIVER;
+
+typedef struct spool_r_deleteprinterdriver
+{
+ WERROR status;
+}
+SPOOL_R_DELETEPRINTERDRIVER;
+
+typedef struct spool_q_deleteprinterdriverex
+{
+ uint32 server_ptr;
+ UNISTR2 server;
+ UNISTR2 arch;
+ UNISTR2 driver;
+ uint32 delete_flags;
+ uint32 version;
+}
+SPOOL_Q_DELETEPRINTERDRIVEREX;
+
+typedef struct spool_r_deleteprinterdriverex
+{
+ WERROR status;
+}
+SPOOL_R_DELETEPRINTERDRIVEREX;
+
+
+typedef struct spool_doc_info_1
+{
+ uint32 p_docname;
+ uint32 p_outputfile;
+ uint32 p_datatype;
+ UNISTR2 docname;
+ UNISTR2 outputfile;
+ UNISTR2 datatype;
+}
+DOC_INFO_1;
+
+typedef struct spool_doc_info
+{
+ uint32 switch_value;
+ DOC_INFO_1 doc_info_1;
+}
+DOC_INFO;
+
+typedef struct spool_doc_info_container
+{
+ uint32 level;
+ DOC_INFO docinfo;
+}
+DOC_INFO_CONTAINER;
+
+typedef struct spool_q_startdocprinter
+{
+ POLICY_HND handle;
+ DOC_INFO_CONTAINER doc_info_container;
+}
+SPOOL_Q_STARTDOCPRINTER;
+
+typedef struct spool_r_startdocprinter
+{
+ uint32 jobid;
+ WERROR status;
+}
+SPOOL_R_STARTDOCPRINTER;
+
+typedef struct spool_q_enddocprinter
+{
+ POLICY_HND handle;
+}
+SPOOL_Q_ENDDOCPRINTER;
+
+typedef struct spool_r_enddocprinter
+{
+ WERROR status;
+}
+SPOOL_R_ENDDOCPRINTER;
+
+typedef struct spool_q_writeprinter
+{
+ POLICY_HND handle;
+ uint32 buffer_size;
+ uint8 *buffer;
+ uint32 buffer_size2;
+}
+SPOOL_Q_WRITEPRINTER;
+
+typedef struct spool_r_writeprinter
+{
+ uint32 buffer_written;
+ WERROR status;
+}
+SPOOL_R_WRITEPRINTER;
+
+typedef struct spool_notify_option
+{
+ uint32 version;
+ uint32 flags;
+ uint32 count;
+ uint32 option_type_ptr;
+ SPOOL_NOTIFY_OPTION_TYPE_CTR ctr;
+}
+SPOOL_NOTIFY_OPTION;
+
+typedef struct spool_notify_info_data
+{
+ uint16 type;
+ uint16 field;
+ uint32 reserved;
+ uint32 id;
+ union {
+ uint32 value[2];
+ struct {
+ uint32 length;
+ uint16 *string;
+ } data;
+ struct {
+ uint32 size;
+ SEC_DESC *desc;
+ } sd;
+ }
+ notify_data;
+ uint32 size;
+ BOOL enc_type;
+} SPOOL_NOTIFY_INFO_DATA;
+
+typedef struct spool_notify_info
+{
+ uint32 version;
+ uint32 flags;
+ uint32 count;
+ SPOOL_NOTIFY_INFO_DATA *data;
+}
+SPOOL_NOTIFY_INFO;
+
+/* If the struct name looks obscure, yes it is ! */
+/* RemoteFindFirstPrinterChangeNotificationEx query struct */
+typedef struct spoolss_q_rffpcnex
+{
+ POLICY_HND handle;
+ uint32 flags;
+ uint32 options;
+ uint32 localmachine_ptr;
+ UNISTR2 localmachine;
+ uint32 printerlocal;
+ uint32 option_ptr;
+ SPOOL_NOTIFY_OPTION *option;
+}
+SPOOL_Q_RFFPCNEX;
+
+typedef struct spool_r_rffpcnex
+{
+ WERROR status;
+}
+SPOOL_R_RFFPCNEX;
+
+/* Remote Find Next Printer Change Notify Ex */
+typedef struct spool_q_rfnpcnex
+{
+ POLICY_HND handle;
+ uint32 change;
+ uint32 option_ptr;
+ SPOOL_NOTIFY_OPTION *option;
+}
+SPOOL_Q_RFNPCNEX;
+
+typedef struct spool_r_rfnpcnex
+{
+ uint32 info_ptr;
+ SPOOL_NOTIFY_INFO info;
+ WERROR status;
+}
+SPOOL_R_RFNPCNEX;
+
+/* Find Close Printer Notify */
+typedef struct spool_q_fcpn
+{
+ POLICY_HND handle;
+}
+SPOOL_Q_FCPN;
+
+typedef struct spool_r_fcpn
+{
+ WERROR status;
+}
+SPOOL_R_FCPN;
+
+
+typedef struct printer_info_0
+{
+ UNISTR printername;
+ UNISTR servername;
+ uint32 cjobs;
+ uint32 total_jobs;
+ uint32 total_bytes;
+
+ uint16 year;
+ uint16 month;
+ uint16 dayofweek;
+ uint16 day;
+ uint16 hour;
+ uint16 minute;
+ uint16 second;
+ uint16 milliseconds;
+
+ uint32 global_counter;
+ uint32 total_pages;
+
+ uint16 major_version;
+ uint16 build_version;
+
+ uint32 unknown7;
+ uint32 unknown8;
+ uint32 unknown9;
+ uint32 session_counter;
+ uint32 unknown11;
+ uint32 printer_errors;
+ uint32 unknown13;
+ uint32 unknown14;
+ uint32 unknown15;
+ uint32 unknown16;
+ uint32 change_id;
+ uint32 unknown18;
+ uint32 status;
+ uint32 unknown20;
+ uint32 c_setprinter;
+
+ uint16 unknown22;
+ uint16 unknown23;
+ uint16 unknown24;
+ uint16 unknown25;
+ uint16 unknown26;
+ uint16 unknown27;
+ uint16 unknown28;
+ uint16 unknown29;
+} PRINTER_INFO_0;
+
+typedef struct printer_info_1
+{
+ uint32 flags;
+ UNISTR description;
+ UNISTR name;
+ UNISTR comment;
+}
+PRINTER_INFO_1;
+
+typedef struct printer_info_2
+{
+ UNISTR servername;
+ UNISTR printername;
+ UNISTR sharename;
+ UNISTR portname;
+ UNISTR drivername;
+ UNISTR comment;
+ UNISTR location;
+ DEVICEMODE *devmode;
+ UNISTR sepfile;
+ UNISTR printprocessor;
+ UNISTR datatype;
+ UNISTR parameters;
+ SEC_DESC *secdesc;
+ uint32 attributes;
+ uint32 priority;
+ uint32 defaultpriority;
+ uint32 starttime;
+ uint32 untiltime;
+ uint32 status;
+ uint32 cjobs;
+ uint32 averageppm;
+}
+PRINTER_INFO_2;
+
+typedef struct printer_info_3
+{
+ uint32 flags;
+ SEC_DESC *secdesc;
+}
+PRINTER_INFO_3;
+
+typedef struct printer_info_4
+{
+ UNISTR printername;
+ UNISTR servername;
+ uint32 attributes;
+}
+PRINTER_INFO_4;
+
+typedef struct printer_info_5
+{
+ UNISTR printername;
+ UNISTR portname;
+ uint32 attributes;
+ uint32 device_not_selected_timeout;
+ uint32 transmission_retry_timeout;
+}
+PRINTER_INFO_5;
+
+#define SPOOL_DS_PUBLISH 1
+#define SPOOL_DS_UPDATE 2
+#define SPOOL_DS_UNPUBLISH 4
+#define SPOOL_DS_PENDING 0x80000000
+
+typedef struct printer_info_7
+{
+ UNISTR guid; /* text form of printer guid */
+ uint32 action;
+}
+PRINTER_INFO_7;
+
+typedef struct spool_q_enumprinters
+{
+ uint32 flags;
+ uint32 servername_ptr;
+ UNISTR2 servername;
+ uint32 level;
+ NEW_BUFFER *buffer;
+ uint32 offered;
+}
+SPOOL_Q_ENUMPRINTERS;
+
+typedef struct printer_info_ctr_info
+{
+ PRINTER_INFO_0 *printers_0;
+ PRINTER_INFO_1 *printers_1;
+ PRINTER_INFO_2 *printers_2;
+ PRINTER_INFO_3 *printers_3;
+ PRINTER_INFO_4 *printers_4;
+ PRINTER_INFO_5 *printers_5;
+}
+PRINTER_INFO_CTR;
+
+typedef struct spool_r_enumprinters
+{
+ NEW_BUFFER *buffer;
+ uint32 needed; /* bytes needed */
+ uint32 returned; /* number of printers */
+ WERROR status;
+}
+SPOOL_R_ENUMPRINTERS;
+
+
+typedef struct spool_q_getprinter
+{
+ POLICY_HND handle;
+ uint32 level;
+ NEW_BUFFER *buffer;
+ uint32 offered;
+}
+SPOOL_Q_GETPRINTER;
+
+typedef struct printer_info_info
+{
+ union
+ {
+ PRINTER_INFO_0 *info0;
+ PRINTER_INFO_1 *info1;
+ PRINTER_INFO_2 *info2;
+ void *info;
+ } printer;
+} PRINTER_INFO;
+
+typedef struct spool_r_getprinter
+{
+ NEW_BUFFER *buffer;
+ uint32 needed;
+ WERROR status;
+} SPOOL_R_GETPRINTER;
+
+typedef struct driver_info_1
+{
+ UNISTR name;
+} DRIVER_INFO_1;
+
+typedef struct driver_info_2
+{
+ uint32 version;
+ UNISTR name;
+ UNISTR architecture;
+ UNISTR driverpath;
+ UNISTR datafile;
+ UNISTR configfile;
+} DRIVER_INFO_2;
+
+typedef struct driver_info_3
+{
+ uint32 version;
+ UNISTR name;
+ UNISTR architecture;
+ UNISTR driverpath;
+ UNISTR datafile;
+ UNISTR configfile;
+ UNISTR helpfile;
+ uint16 *dependentfiles;
+ UNISTR monitorname;
+ UNISTR defaultdatatype;
+}
+DRIVER_INFO_3;
+
+typedef struct driver_info_6
+{
+ uint32 version;
+ UNISTR name;
+ UNISTR architecture;
+ UNISTR driverpath;
+ UNISTR datafile;
+ UNISTR configfile;
+ UNISTR helpfile;
+ uint16 *dependentfiles;
+ UNISTR monitorname;
+ UNISTR defaultdatatype;
+ uint16* previousdrivernames;
+ NTTIME driver_date;
+ uint32 padding;
+ uint32 driver_version_low;
+ uint32 driver_version_high;
+ UNISTR mfgname;
+ UNISTR oem_url;
+ UNISTR hardware_id;
+ UNISTR provider;
+}
+DRIVER_INFO_6;
+
+typedef struct driver_info_info
+{
+ DRIVER_INFO_1 *info1;
+ DRIVER_INFO_2 *info2;
+ DRIVER_INFO_3 *info3;
+ DRIVER_INFO_6 *info6;
+}
+PRINTER_DRIVER_CTR;
+
+typedef struct spool_q_getprinterdriver2
+{
+ POLICY_HND handle;
+ uint32 architecture_ptr;
+ UNISTR2 architecture;
+ uint32 level;
+ NEW_BUFFER *buffer;
+ uint32 offered;
+ uint32 clientmajorversion;
+ uint32 clientminorversion;
+}
+SPOOL_Q_GETPRINTERDRIVER2;
+
+typedef struct spool_r_getprinterdriver2
+{
+ NEW_BUFFER *buffer;
+ uint32 needed;
+ uint32 servermajorversion;
+ uint32 serverminorversion;
+ WERROR status;
+}
+SPOOL_R_GETPRINTERDRIVER2;
+
+
+typedef struct add_jobinfo_1
+{
+ UNISTR path;
+ uint32 job_number;
+}
+ADD_JOBINFO_1;
+
+
+typedef struct spool_q_addjob
+{
+ POLICY_HND handle;
+ uint32 level;
+ NEW_BUFFER *buffer;
+ uint32 offered;
+}
+SPOOL_Q_ADDJOB;
+
+typedef struct spool_r_addjob
+{
+ NEW_BUFFER *buffer;
+ uint32 needed;
+ WERROR status;
+}
+SPOOL_R_ADDJOB;
+
+/*
+ * I'm really wondering how many different time formats
+ * I will have to cope with
+ *
+ * JFM, 09/13/98 In a mad mood ;-(
+*/
+typedef struct systemtime
+{
+ uint16 year;
+ uint16 month;
+ uint16 dayofweek;
+ uint16 day;
+ uint16 hour;
+ uint16 minute;
+ uint16 second;
+ uint16 milliseconds;
+}
+SYSTEMTIME;
+
+typedef struct s_job_info_1
+{
+ uint32 jobid;
+ UNISTR printername;
+ UNISTR machinename;
+ UNISTR username;
+ UNISTR document;
+ UNISTR datatype;
+ UNISTR text_status;
+ uint32 status;
+ uint32 priority;
+ uint32 position;
+ uint32 totalpages;
+ uint32 pagesprinted;
+ SYSTEMTIME submitted;
+}
+JOB_INFO_1;
+
+typedef struct s_job_info_2
+{
+ uint32 jobid;
+ UNISTR printername;
+ UNISTR machinename;
+ UNISTR username;
+ UNISTR document;
+ UNISTR notifyname;
+ UNISTR datatype;
+ UNISTR printprocessor;
+ UNISTR parameters;
+ UNISTR drivername;
+ DEVICEMODE *devmode;
+ UNISTR text_status;
+/* SEC_DESC sec_desc;*/
+ uint32 status;
+ uint32 priority;
+ uint32 position;
+ uint32 starttime;
+ uint32 untiltime;
+ uint32 totalpages;
+ uint32 size;
+ SYSTEMTIME submitted;
+ uint32 timeelapsed;
+ uint32 pagesprinted;
+}
+JOB_INFO_2;
+
+typedef struct spool_q_enumjobs
+{
+ POLICY_HND handle;
+ uint32 firstjob;
+ uint32 numofjobs;
+ uint32 level;
+ NEW_BUFFER *buffer;
+ uint32 offered;
+}
+SPOOL_Q_ENUMJOBS;
+
+typedef struct job_info_ctr_info
+{
+ union
+ {
+ JOB_INFO_1 *job_info_1;
+ JOB_INFO_2 *job_info_2;
+ void *info;
+ } job;
+
+} JOB_INFO_CTR;
+
+typedef struct spool_r_enumjobs
+{
+ NEW_BUFFER *buffer;
+ uint32 needed;
+ uint32 returned;
+ WERROR status;
+}
+SPOOL_R_ENUMJOBS;
+
+typedef struct spool_q_schedulejob
+{
+ POLICY_HND handle;
+ uint32 jobid;
+}
+SPOOL_Q_SCHEDULEJOB;
+
+typedef struct spool_r_schedulejob
+{
+ WERROR status;
+}
+SPOOL_R_SCHEDULEJOB;
+
+typedef struct s_port_info_1
+{
+ UNISTR port_name;
+}
+PORT_INFO_1;
+
+typedef struct s_port_info_2
+{
+ UNISTR port_name;
+ UNISTR monitor_name;
+ UNISTR description;
+ uint32 port_type;
+ uint32 reserved;
+}
+PORT_INFO_2;
+
+/* Port Type bits */
+#define PORT_TYPE_WRITE 0x0001
+#define PORT_TYPE_READ 0x0002
+#define PORT_TYPE_REDIRECTED 0x0004
+#define PORT_TYPE_NET_ATTACHED 0x0008
+
+typedef struct spool_q_enumports
+{
+ uint32 name_ptr;
+ UNISTR2 name;
+ uint32 level;
+ NEW_BUFFER *buffer;
+ uint32 offered;
+}
+SPOOL_Q_ENUMPORTS;
+
+typedef struct port_info_ctr_info
+{
+ union
+ {
+ PORT_INFO_1 *info_1;
+ PORT_INFO_2 *info_2;
+ }
+ port;
+
+}
+PORT_INFO_CTR;
+
+typedef struct spool_r_enumports
+{
+ NEW_BUFFER *buffer;
+ uint32 needed; /* bytes needed */
+ uint32 returned; /* number of printers */
+ WERROR status;
+}
+SPOOL_R_ENUMPORTS;
+
+#define JOB_CONTROL_PAUSE 1
+#define JOB_CONTROL_RESUME 2
+#define JOB_CONTROL_CANCEL 3
+#define JOB_CONTROL_RESTART 4
+#define JOB_CONTROL_DELETE 5
+
+typedef struct job_info_info
+{
+ union
+ {
+ JOB_INFO_1 job_info_1;
+ JOB_INFO_2 job_info_2;
+ }
+ job;
+
+}
+JOB_INFO;
+
+typedef struct spool_q_setjob
+{
+ POLICY_HND handle;
+ uint32 jobid;
+ uint32 level;
+ JOB_INFO ctr;
+ uint32 command;
+
+}
+SPOOL_Q_SETJOB;
+
+typedef struct spool_r_setjob
+{
+ WERROR status;
+
+}
+SPOOL_R_SETJOB;
+
+typedef struct spool_q_enumprinterdrivers
+{
+ uint32 name_ptr;
+ UNISTR2 name;
+ uint32 environment_ptr;
+ UNISTR2 environment;
+ uint32 level;
+ NEW_BUFFER *buffer;
+ uint32 offered;
+}
+SPOOL_Q_ENUMPRINTERDRIVERS;
+
+typedef struct spool_r_enumprinterdrivers
+{
+ NEW_BUFFER *buffer;
+ uint32 needed;
+ uint32 returned;
+ WERROR status;
+}
+SPOOL_R_ENUMPRINTERDRIVERS;
+
+#define FORM_USER 0
+#define FORM_BUILTIN 1
+#define FORM_PRINTER 2
+
+typedef struct spool_form_1
+{
+ uint32 flag;
+ UNISTR name;
+ uint32 width;
+ uint32 length;
+ uint32 left;
+ uint32 top;
+ uint32 right;
+ uint32 bottom;
+}
+FORM_1;
+
+typedef struct spool_q_enumforms
+{
+ POLICY_HND handle;
+ uint32 level;
+ NEW_BUFFER *buffer;
+ uint32 offered;
+}
+SPOOL_Q_ENUMFORMS;
+
+typedef struct spool_r_enumforms
+{
+ NEW_BUFFER *buffer;
+ uint32 needed;
+ uint32 numofforms;
+ WERROR status;
+}
+SPOOL_R_ENUMFORMS;
+
+typedef struct spool_q_getform
+{
+ POLICY_HND handle;
+ UNISTR2 formname;
+ uint32 level;
+ NEW_BUFFER *buffer;
+ uint32 offered;
+}
+SPOOL_Q_GETFORM;
+
+typedef struct spool_r_getform
+{
+ NEW_BUFFER *buffer;
+ uint32 needed;
+ WERROR status;
+}
+SPOOL_R_GETFORM;
+
+typedef struct spool_printer_info_level_1
+{
+ uint32 flags;
+ uint32 description_ptr;
+ uint32 name_ptr;
+ uint32 comment_ptr;
+ UNISTR2 description;
+ UNISTR2 name;
+ UNISTR2 comment;
+} SPOOL_PRINTER_INFO_LEVEL_1;
+
+typedef struct spool_printer_info_level_2
+{
+ uint32 servername_ptr;
+ uint32 printername_ptr;
+ uint32 sharename_ptr;
+ uint32 portname_ptr;
+ uint32 drivername_ptr;
+ uint32 comment_ptr;
+ uint32 location_ptr;
+ uint32 devmode_ptr;
+ uint32 sepfile_ptr;
+ uint32 printprocessor_ptr;
+ uint32 datatype_ptr;
+ uint32 parameters_ptr;
+ uint32 secdesc_ptr;
+ uint32 attributes;
+ uint32 priority;
+ uint32 default_priority;
+ uint32 starttime;
+ uint32 untiltime;
+ uint32 status;
+ uint32 cjobs;
+ uint32 averageppm;
+ UNISTR2 servername;
+ UNISTR2 printername;
+ UNISTR2 sharename;
+ UNISTR2 portname;
+ UNISTR2 drivername;
+ UNISTR2 comment;
+ UNISTR2 location;
+ UNISTR2 sepfile;
+ UNISTR2 printprocessor;
+ UNISTR2 datatype;
+ UNISTR2 parameters;
+}
+SPOOL_PRINTER_INFO_LEVEL_2;
+
+typedef struct spool_printer_info_level_3
+{
+ uint32 secdesc_ptr;
+}
+SPOOL_PRINTER_INFO_LEVEL_3;
+
+typedef struct spool_printer_info_level_7
+{
+ uint32 guid_ptr;
+ uint32 action;
+ UNISTR2 guid;
+}
+SPOOL_PRINTER_INFO_LEVEL_7;
+
+typedef struct spool_printer_info_level
+{
+ uint32 level;
+ uint32 info_ptr;
+ SPOOL_PRINTER_INFO_LEVEL_1 *info_1;
+ SPOOL_PRINTER_INFO_LEVEL_2 *info_2;
+ SPOOL_PRINTER_INFO_LEVEL_3 *info_3;
+ SPOOL_PRINTER_INFO_LEVEL_7 *info_7;
+}
+SPOOL_PRINTER_INFO_LEVEL;
+
+typedef struct spool_printer_driver_info_level_3
+{
+ uint32 cversion;
+ uint32 name_ptr;
+ uint32 environment_ptr;
+ uint32 driverpath_ptr;
+ uint32 datafile_ptr;
+ uint32 configfile_ptr;
+ uint32 helpfile_ptr;
+ uint32 monitorname_ptr;
+ uint32 defaultdatatype_ptr;
+ uint32 dependentfilessize;
+ uint32 dependentfiles_ptr;
+
+ UNISTR2 name;
+ UNISTR2 environment;
+ UNISTR2 driverpath;
+ UNISTR2 datafile;
+ UNISTR2 configfile;
+ UNISTR2 helpfile;
+ UNISTR2 monitorname;
+ UNISTR2 defaultdatatype;
+ BUFFER5 dependentfiles;
+
+}
+SPOOL_PRINTER_DRIVER_INFO_LEVEL_3;
+
+/* SPOOL_PRINTER_DRIVER_INFO_LEVEL_6 structure */
+typedef struct {
+ uint32 version;
+ uint32 name_ptr;
+ uint32 environment_ptr;
+ uint32 driverpath_ptr;
+ uint32 datafile_ptr;
+ uint32 configfile_ptr;
+ uint32 helpfile_ptr;
+ uint32 monitorname_ptr;
+ uint32 defaultdatatype_ptr;
+ uint32 dependentfiles_len;
+ uint32 dependentfiles_ptr;
+ uint32 previousnames_len;
+ uint32 previousnames_ptr;
+ NTTIME driverdate;
+ UINT64_S driverversion;
+ uint32 dummy4;
+ uint32 mfgname_ptr;
+ uint32 oemurl_ptr;
+ uint32 hardwareid_ptr;
+ uint32 provider_ptr;
+ UNISTR2 name;
+ UNISTR2 environment;
+ UNISTR2 driverpath;
+ UNISTR2 datafile;
+ UNISTR2 configfile;
+ UNISTR2 helpfile;
+ UNISTR2 monitorname;
+ UNISTR2 defaultdatatype;
+ BUFFER5 dependentfiles;
+ BUFFER5 previousnames;
+ UNISTR2 mfgname;
+ UNISTR2 oemurl;
+ UNISTR2 hardwareid;
+ UNISTR2 provider;
+} SPOOL_PRINTER_DRIVER_INFO_LEVEL_6;
+
+
+typedef struct spool_printer_driver_info_level
+{
+ uint32 level;
+ uint32 ptr;
+ SPOOL_PRINTER_DRIVER_INFO_LEVEL_3 *info_3;
+ SPOOL_PRINTER_DRIVER_INFO_LEVEL_6 *info_6;
+}
+SPOOL_PRINTER_DRIVER_INFO_LEVEL;
+
+
+/* this struct is undocumented */
+/* thanks to the ddk ... */
+typedef struct spool_user_level_1
+{
+ uint32 size;
+ uint32 client_name_ptr;
+ uint32 user_name_ptr;
+ uint32 build;
+ uint32 major;
+ uint32 minor;
+ uint32 processor;
+ UNISTR2 client_name;
+ UNISTR2 user_name;
+}
+SPOOL_USER_LEVEL_1;
+
+typedef struct spool_user_level
+{
+ SPOOL_USER_LEVEL_1 *user_level_1;
+}
+SPOOL_USER_LEVEL;
+
+typedef struct spool_q_setprinter
+{
+ POLICY_HND handle;
+ uint32 level;
+ SPOOL_PRINTER_INFO_LEVEL info;
+ SEC_DESC_BUF *secdesc_ctr;
+ DEVMODE_CTR devmode_ctr;
+
+ uint32 command;
+
+}
+SPOOL_Q_SETPRINTER;
+
+typedef struct spool_r_setprinter
+{
+ WERROR status;
+}
+SPOOL_R_SETPRINTER;
+
+typedef struct spool_q_addprinter
+{
+ UNISTR2 server_name;
+ uint32 level;
+ SPOOL_PRINTER_INFO_LEVEL info;
+ DEVMODE_CTR devmode_ctr;
+ SEC_DESC_BUF *secdesc_ctr;
+ uint32 user_level;
+ SPOOL_USER_LEVEL user;
+}
+SPOOL_Q_ADDPRINTER;
+
+typedef struct spool_r_addprinter
+{
+ WERROR status;
+}
+SPOOL_R_ADDPRINTER;
+
+typedef struct spool_q_deleteprinter
+{
+ POLICY_HND handle;
+}
+SPOOL_Q_DELETEPRINTER;
+
+typedef struct spool_r_deleteprinter
+{
+ POLICY_HND handle;
+ WERROR status;
+}
+SPOOL_R_DELETEPRINTER;
+
+typedef struct spool_q_abortprinter
+{
+ POLICY_HND handle;
+}
+SPOOL_Q_ABORTPRINTER;
+
+typedef struct spool_r_abortprinter
+{
+ WERROR status;
+}
+SPOOL_R_ABORTPRINTER;
+
+
+typedef struct spool_q_addprinterex
+{
+ uint32 server_name_ptr;
+ UNISTR2 server_name;
+ uint32 level;
+ SPOOL_PRINTER_INFO_LEVEL info;
+ DEVMODE_CTR devmode_ctr;
+ SEC_DESC_BUF *secdesc_ctr;
+ uint32 user_switch;
+ SPOOL_USER_CTR user_ctr;
+}
+SPOOL_Q_ADDPRINTEREX;
+
+typedef struct spool_r_addprinterex
+{
+ POLICY_HND handle;
+ WERROR status;
+}
+SPOOL_R_ADDPRINTEREX;
+
+
+typedef struct spool_q_addprinterdriver
+{
+ uint32 server_name_ptr;
+ UNISTR2 server_name;
+ uint32 level;
+ SPOOL_PRINTER_DRIVER_INFO_LEVEL info;
+}
+SPOOL_Q_ADDPRINTERDRIVER;
+
+typedef struct spool_r_addprinterdriver
+{
+ WERROR status;
+}
+SPOOL_R_ADDPRINTERDRIVER;
+
+typedef struct spool_q_addprinterdriverex
+{
+ uint32 server_name_ptr;
+ UNISTR2 server_name;
+ uint32 level;
+ SPOOL_PRINTER_DRIVER_INFO_LEVEL info;
+ uint32 copy_flags;
+}
+SPOOL_Q_ADDPRINTERDRIVEREX;
+
+typedef struct spool_r_addprinterdriverex
+{
+ WERROR status;
+}
+SPOOL_R_ADDPRINTERDRIVEREX;
+
+
+typedef struct driver_directory_1
+{
+ UNISTR name;
+}
+DRIVER_DIRECTORY_1;
+
+typedef struct driver_info_ctr_info
+{
+ DRIVER_DIRECTORY_1 *info1;
+}
+DRIVER_DIRECTORY_CTR;
+
+typedef struct spool_q_getprinterdriverdirectory
+{
+ uint32 name_ptr;
+ UNISTR2 name;
+ uint32 environment_ptr;
+ UNISTR2 environment;
+ uint32 level;
+ NEW_BUFFER *buffer;
+ uint32 offered;
+}
+SPOOL_Q_GETPRINTERDRIVERDIR;
+
+typedef struct spool_r_getprinterdriverdirectory
+{
+ NEW_BUFFER *buffer;
+ uint32 needed;
+ WERROR status;
+}
+SPOOL_R_GETPRINTERDRIVERDIR;
+
+typedef struct spool_q_addprintprocessor
+{
+ uint32 server_ptr;
+ UNISTR2 server;
+ UNISTR2 environment;
+ UNISTR2 path;
+ UNISTR2 name;
+}
+SPOOL_Q_ADDPRINTPROCESSOR;
+
+typedef struct spool_r_addprintprocessor
+{
+ WERROR status;
+}
+SPOOL_R_ADDPRINTPROCESSOR;
+
+
+typedef struct spool_q_enumprintprocessors
+{
+ uint32 name_ptr;
+ UNISTR2 name;
+ uint32 environment_ptr;
+ UNISTR2 environment;
+ uint32 level;
+ NEW_BUFFER *buffer;
+ uint32 offered;
+}
+SPOOL_Q_ENUMPRINTPROCESSORS;
+
+typedef struct printprocessor_1
+{
+ UNISTR name;
+}
+PRINTPROCESSOR_1;
+
+typedef struct spool_r_enumprintprocessors
+{
+ NEW_BUFFER *buffer;
+ uint32 needed;
+ uint32 returned;
+ WERROR status;
+}
+SPOOL_R_ENUMPRINTPROCESSORS;
+
+typedef struct spool_q_enumprintprocdatatypes
+{
+ uint32 name_ptr;
+ UNISTR2 name;
+ uint32 processor_ptr;
+ UNISTR2 processor;
+ uint32 level;
+ NEW_BUFFER *buffer;
+ uint32 offered;
+}
+SPOOL_Q_ENUMPRINTPROCDATATYPES;
+
+typedef struct ppdatatype_1
+{
+ UNISTR name;
+}
+PRINTPROCDATATYPE_1;
+
+typedef struct spool_r_enumprintprocdatatypes
+{
+ NEW_BUFFER *buffer;
+ uint32 needed;
+ uint32 returned;
+ WERROR status;
+}
+SPOOL_R_ENUMPRINTPROCDATATYPES;
+
+typedef struct printmonitor_1
+{
+ UNISTR name;
+}
+PRINTMONITOR_1;
+
+typedef struct printmonitor_2
+{
+ UNISTR name;
+ UNISTR environment;
+ UNISTR dll_name;
+}
+PRINTMONITOR_2;
+
+typedef struct spool_q_enumprintmonitors
+{
+ uint32 name_ptr;
+ UNISTR2 name;
+ uint32 level;
+ NEW_BUFFER *buffer;
+ uint32 offered;
+}
+SPOOL_Q_ENUMPRINTMONITORS;
+
+typedef struct spool_r_enumprintmonitors
+{
+ NEW_BUFFER *buffer;
+ uint32 needed;
+ uint32 returned;
+ WERROR status;
+}
+SPOOL_R_ENUMPRINTMONITORS;
+
+
+typedef struct spool_q_enumprinterdata
+{
+ POLICY_HND handle;
+ uint32 index;
+ uint32 valuesize;
+ uint32 datasize;
+}
+SPOOL_Q_ENUMPRINTERDATA;
+
+typedef struct spool_r_enumprinterdata
+{
+ uint32 valuesize;
+ uint16 *value;
+ uint32 realvaluesize;
+ uint32 type;
+ uint32 datasize;
+ uint8 *data;
+ uint32 realdatasize;
+ WERROR status;
+}
+SPOOL_R_ENUMPRINTERDATA;
+
+typedef struct spool_q_setprinterdata
+{
+ POLICY_HND handle;
+ UNISTR2 value;
+ uint32 type;
+ uint32 max_len;
+ uint8 *data;
+ uint32 real_len;
+ uint32 numeric_data;
+}
+SPOOL_Q_SETPRINTERDATA;
+
+typedef struct spool_r_setprinterdata
+{
+ WERROR status;
+}
+SPOOL_R_SETPRINTERDATA;
+
+typedef struct spool_q_resetprinter
+{
+ POLICY_HND handle;
+ uint32 datatype_ptr;
+ UNISTR2 datatype;
+ DEVMODE_CTR devmode_ctr;
+
+} SPOOL_Q_RESETPRINTER;
+
+typedef struct spool_r_resetprinter
+{
+ WERROR status;
+}
+SPOOL_R_RESETPRINTER;
+
+
+
+typedef struct _form
+{
+ uint32 flags;
+ uint32 name_ptr;
+ uint32 size_x;
+ uint32 size_y;
+ uint32 left;
+ uint32 top;
+ uint32 right;
+ uint32 bottom;
+ UNISTR2 name;
+}
+FORM;
+
+typedef struct spool_q_addform
+{
+ POLICY_HND handle;
+ uint32 level;
+ uint32 level2; /* This should really be part of the FORM structure */
+ FORM form;
+}
+SPOOL_Q_ADDFORM;
+
+typedef struct spool_r_addform
+{
+ WERROR status;
+}
+SPOOL_R_ADDFORM;
+
+typedef struct spool_q_setform
+{
+ POLICY_HND handle;
+ UNISTR2 name;
+ uint32 level;
+ uint32 level2;
+ FORM form;
+}
+SPOOL_Q_SETFORM;
+
+typedef struct spool_r_setform
+{
+ WERROR status;
+}
+SPOOL_R_SETFORM;
+
+typedef struct spool_q_deleteform
+{
+ POLICY_HND handle;
+ UNISTR2 name;
+}
+SPOOL_Q_DELETEFORM;
+
+typedef struct spool_r_deleteform
+{
+ WERROR status;
+}
+SPOOL_R_DELETEFORM;
+
+typedef struct spool_q_getjob
+{
+ POLICY_HND handle;
+ uint32 jobid;
+ uint32 level;
+ NEW_BUFFER *buffer;
+ uint32 offered;
+}
+SPOOL_Q_GETJOB;
+
+typedef struct pjob_info_info
+{
+ union
+ {
+ JOB_INFO_1 *job_info_1;
+ JOB_INFO_2 *job_info_2;
+ void *info;
+ }
+ job;
+
+}
+PJOB_INFO;
+
+typedef struct spool_r_getjob
+{
+ NEW_BUFFER *buffer;
+ uint32 needed;
+ WERROR status;
+}
+SPOOL_R_GETJOB;
+
+typedef struct spool_q_replyopenprinter
+{
+ UNISTR2 string;
+ uint32 printer;
+ uint32 type;
+ uint32 unknown0;
+ uint32 unknown1;
+}
+SPOOL_Q_REPLYOPENPRINTER;
+
+typedef struct spool_r_replyopenprinter
+{
+ POLICY_HND handle;
+ WERROR status;
+}
+SPOOL_R_REPLYOPENPRINTER;
+
+typedef struct spool_q_routerreplyprinter
+{
+ POLICY_HND handle;
+ uint32 condition;
+ uint32 unknown1; /* 0x00000001 */
+ uint32 change_id;
+ uint8 unknown2[5]; /* 0x0000000001 */
+}
+SPOOL_Q_ROUTERREPLYPRINTER;
+
+typedef struct spool_r_routerreplyprinter
+{
+ WERROR status;
+}
+SPOOL_R_ROUTERREPLYPRINTER;
+
+typedef struct spool_q_replycloseprinter
+{
+ POLICY_HND handle;
+}
+SPOOL_Q_REPLYCLOSEPRINTER;
+
+typedef struct spool_r_replycloseprinter
+{
+ POLICY_HND handle;
+ WERROR status;
+}
+SPOOL_R_REPLYCLOSEPRINTER;
+
+typedef struct spool_q_rrpcn
+{
+ POLICY_HND handle;
+ uint32 change_low;
+ uint32 change_high;
+ uint32 unknown0;
+ uint32 unknown1;
+ uint32 info_ptr;
+ SPOOL_NOTIFY_INFO info;
+}
+SPOOL_Q_REPLY_RRPCN;
+
+typedef struct spool_r_rrpcn
+{
+ uint32 unknown0;
+ WERROR status;
+}
+SPOOL_R_REPLY_RRPCN;
+
+typedef struct spool_q_getprinterdataex
+{
+ POLICY_HND handle;
+ UNISTR2 keyname;
+ UNISTR2 valuename;
+ uint32 size;
+}
+SPOOL_Q_GETPRINTERDATAEX;
+
+typedef struct spool_r_getprinterdataex
+{
+ uint32 type;
+ uint32 size;
+ uint8 *data;
+ uint32 needed;
+ WERROR status;
+}
+SPOOL_R_GETPRINTERDATAEX;
+
+typedef struct spool_q_setprinterdataex
+{
+ POLICY_HND handle;
+ UNISTR2 key;
+ UNISTR2 value;
+ uint32 type;
+ uint32 max_len;
+ uint8 *data;
+ uint32 real_len;
+ uint32 numeric_data;
+}
+SPOOL_Q_SETPRINTERDATAEX;
+
+typedef struct spool_r_setprinterdataex
+{
+ WERROR status;
+}
+SPOOL_R_SETPRINTERDATAEX;
+
+
+typedef struct spool_q_deleteprinterdataex
+{
+ POLICY_HND handle;
+ UNISTR2 keyname;
+ UNISTR2 valuename;
+}
+SPOOL_Q_DELETEPRINTERDATAEX;
+
+typedef struct spool_r_deleteprinterdataex
+{
+ WERROR status;
+}
+SPOOL_R_DELETEPRINTERDATAEX;
+
+
+typedef struct spool_q_enumprinterkey
+{
+ POLICY_HND handle;
+ UNISTR2 key;
+ uint32 size;
+}
+SPOOL_Q_ENUMPRINTERKEY;
+
+typedef struct spool_r_enumprinterkey
+{
+ BUFFER5 keys;
+ uint32 needed; /* in bytes */
+ WERROR status;
+}
+SPOOL_R_ENUMPRINTERKEY;
+
+typedef struct spool_q_deleteprinterkey
+{
+ POLICY_HND handle;
+ UNISTR2 keyname;
+}
+SPOOL_Q_DELETEPRINTERKEY;
+
+typedef struct spool_r_deleteprinterkey
+{
+ WERROR status;
+}
+SPOOL_R_DELETEPRINTERKEY;
+
+typedef struct printer_enum_values
+{
+ UNISTR valuename;
+ uint32 value_len;
+ uint32 type;
+ uint8 *data;
+ uint32 data_len;
+
+}
+PRINTER_ENUM_VALUES;
+
+typedef struct printer_enum_values_ctr
+{
+ uint32 size;
+ uint32 size_of_array;
+ PRINTER_ENUM_VALUES *values;
+}
+PRINTER_ENUM_VALUES_CTR;
+
+typedef struct spool_q_enumprinterdataex
+{
+ POLICY_HND handle;
+ UNISTR2 key;
+ uint32 size;
+}
+SPOOL_Q_ENUMPRINTERDATAEX;
+
+typedef struct spool_r_enumprinterdataex
+{
+ PRINTER_ENUM_VALUES_CTR ctr;
+ uint32 needed;
+ uint32 returned;
+ WERROR status;
+}
+SPOOL_R_ENUMPRINTERDATAEX;
+
+typedef struct printprocessor_directory_1
+{
+ UNISTR name;
+}
+PRINTPROCESSOR_DIRECTORY_1;
+
+typedef struct spool_q_getprintprocessordirectory
+{
+ UNISTR2 name;
+ UNISTR2 environment;
+ uint32 level;
+ NEW_BUFFER *buffer;
+ uint32 offered;
+}
+SPOOL_Q_GETPRINTPROCESSORDIRECTORY;
+
+typedef struct spool_r_getprintprocessordirectory
+{
+ NEW_BUFFER *buffer;
+ uint32 needed;
+ WERROR status;
+}
+SPOOL_R_GETPRINTPROCESSORDIRECTORY;
+
+#define PRINTER_DRIVER_VERSION 2
+#define PRINTER_DRIVER_ARCHITECTURE "Windows NT x86"
+
+#endif /* _RPC_SPOOLSS_H */
+
diff --git a/source/include/rpc_srvsvc.h b/source/include/rpc_srvsvc.h
new file mode 100644
index 00000000000..5ebb77a8c21
--- /dev/null
+++ b/source/include/rpc_srvsvc.h
@@ -0,0 +1,954 @@
+/*
+ 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
+ Copyright (C) Nigel Williams 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 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 _RPC_SRVSVC_H /* _RPC_SRVSVC_H */
+#define _RPC_SRVSVC_H
+
+/* srvsvc pipe */
+#define SRV_NET_CONN_ENUM 0x08
+#define SRV_NET_FILE_ENUM 0x09
+#define SRV_NET_FILE_CLOSE 0x0b
+#define SRV_NET_SESS_ENUM 0x0c
+#define SRV_NET_SHARE_ADD 0x0e
+#define SRV_NET_SHARE_ENUM_ALL 0x0f
+#define SRV_NET_SHARE_GET_INFO 0x10
+#define SRV_NET_SHARE_SET_INFO 0x11
+#define SRV_NET_SHARE_DEL 0x12
+#define SRV_NET_SHARE_DEL_STICKY 0x13
+#define SRV_NET_SRV_GET_INFO 0x15
+#define SRV_NET_SRV_SET_INFO 0x16
+#define SRV_NET_DISK_ENUM 0x17
+#define SRV_NET_REMOTE_TOD 0x1c
+#define SRV_NET_NAME_VALIDATE 0x21
+#define SRV_NET_SHARE_ENUM 0x24
+#define SRV_NET_FILE_QUERY_SECDESC 0x27
+#define SRV_NET_FILE_SET_SECDESC 0x28
+
+#define MAX_SERVER_DISK_ENTRIES 15
+
+typedef struct disk_info {
+ uint32 unknown;
+ UNISTR3 disk_name;
+} DISK_INFO;
+
+typedef struct disk_enum_container {
+ uint32 level;
+ uint32 entries_read;
+ uint32 unknown;
+ uint32 disk_info_ptr;
+ DISK_INFO *disk_info;
+} DISK_ENUM_CONTAINER;
+
+typedef struct net_srv_disk_enum {
+ uint32 ptr_srv_name; /* pointer (to server name?) */
+ UNISTR2 uni_srv_name; /* server name */
+
+ DISK_ENUM_CONTAINER disk_enum_ctr;
+
+ uint32 preferred_len; /* preferred maximum length (0xffff ffff) */
+ uint32 total_entries; /* total number of entries */
+ ENUM_HND enum_hnd;
+ WERROR status; /* return status */
+} SRV_Q_NET_DISK_ENUM, SRV_R_NET_DISK_ENUM;
+
+typedef struct net_name_validate {
+ uint32 ptr_srv_name;
+ UNISTR2 uni_srv_name;
+ UNISTR2 uni_name; /*name to validate*/
+ uint32 type;
+ uint32 flags;
+ WERROR status;
+} SRV_Q_NET_NAME_VALIDATE, SRV_R_NET_NAME_VALIDATE;
+
+/* SESS_INFO_0 (pointers to level 0 session info strings) */
+typedef struct ptr_sess_info0
+{
+ uint32 ptr_name; /* pointer to name. */
+
+} SESS_INFO_0;
+
+/* SESS_INFO_0_STR (level 0 session info strings) */
+typedef struct str_sess_info0
+{
+ UNISTR2 uni_name; /* unicode string of name */
+
+} SESS_INFO_0_STR;
+
+/* oops - this is going to take up a *massive* amount of stack. */
+/* the UNISTR2s already have 1024 uint16 chars in them... */
+#define MAX_SESS_ENTRIES 32
+
+/* SRV_SESS_INFO_0 */
+typedef struct srv_sess_info_0_info
+{
+ uint32 num_entries_read; /* EntriesRead */
+ uint32 ptr_sess_info; /* Buffer */
+ uint32 num_entries_read2; /* EntriesRead */
+
+ SESS_INFO_0 info_0 [MAX_SESS_ENTRIES]; /* session entry pointers */
+ SESS_INFO_0_STR info_0_str[MAX_SESS_ENTRIES]; /* session entry strings */
+
+} SRV_SESS_INFO_0;
+
+/* SESS_INFO_1 (pointers to level 1 session info strings) */
+typedef struct ptr_sess_info1
+{
+ uint32 ptr_name; /* pointer to name. */
+ uint32 ptr_user; /* pointer to user name. */
+
+ uint32 num_opens;
+ uint32 open_time;
+ uint32 idle_time;
+ uint32 user_flags;
+
+} SESS_INFO_1;
+
+/* SESS_INFO_1_STR (level 1 session info strings) */
+typedef struct str_sess_info1
+{
+ UNISTR2 uni_name; /* unicode string of name */
+ UNISTR2 uni_user; /* unicode string of user */
+
+} SESS_INFO_1_STR;
+
+/* SRV_SESS_INFO_1 */
+typedef struct srv_sess_info_1_info
+{
+ uint32 num_entries_read; /* EntriesRead */
+ uint32 ptr_sess_info; /* Buffer */
+ uint32 num_entries_read2; /* EntriesRead */
+
+ SESS_INFO_1 info_1 [MAX_SESS_ENTRIES]; /* session entry pointers */
+ SESS_INFO_1_STR info_1_str[MAX_SESS_ENTRIES]; /* session entry strings */
+
+} SRV_SESS_INFO_1;
+
+/* SRV_SESS_INFO_CTR */
+typedef struct srv_sess_info_ctr_info
+{
+ uint32 switch_value; /* switch value */
+ uint32 ptr_sess_ctr; /* pointer to sess info union */
+ union
+ {
+ SRV_SESS_INFO_0 info0; /* session info level 0 */
+ SRV_SESS_INFO_1 info1; /* session info level 1 */
+
+ } sess;
+
+} SRV_SESS_INFO_CTR;
+
+
+/* SRV_Q_NET_SESS_ENUM */
+typedef struct q_net_sess_enum_info
+{
+ uint32 ptr_srv_name; /* pointer (to server name?) */
+ UNISTR2 uni_srv_name; /* server name */
+
+ uint32 ptr_qual_name; /* pointer (to qualifier name) */
+ UNISTR2 uni_qual_name; /* qualifier name "\\qualifier" */
+
+ uint32 ptr_user_name; /* pointer (to user name */
+ UNISTR2 uni_user_name; /* user name */
+
+ uint32 sess_level; /* session level */
+
+ SRV_SESS_INFO_CTR *ctr;
+
+ uint32 preferred_len; /* preferred maximum length (0xffff ffff) */
+ ENUM_HND enum_hnd;
+
+} SRV_Q_NET_SESS_ENUM;
+
+/* SRV_R_NET_SESS_ENUM */
+typedef struct r_net_sess_enum_info
+{
+ uint32 sess_level; /* share level */
+
+ SRV_SESS_INFO_CTR *ctr;
+
+ uint32 total_entries; /* total number of entries */
+ ENUM_HND enum_hnd;
+
+ WERROR status; /* return status */
+
+} SRV_R_NET_SESS_ENUM;
+
+/* CONN_INFO_0 (pointers to level 0 connection info strings) */
+typedef struct ptr_conn_info0
+{
+ uint32 id; /* connection id. */
+
+} CONN_INFO_0;
+
+/* oops - this is going to take up a *massive* amount of stack. */
+/* the UNISTR2s already have 1024 uint16 chars in them... */
+#define MAX_CONN_ENTRIES 32
+
+/* SRV_CONN_INFO_0 */
+typedef struct srv_conn_info_0_info
+{
+ uint32 num_entries_read; /* EntriesRead */
+ uint32 ptr_conn_info; /* Buffer */
+ uint32 num_entries_read2; /* EntriesRead */
+
+ CONN_INFO_0 info_0 [MAX_CONN_ENTRIES]; /* connection entry pointers */
+
+} SRV_CONN_INFO_0;
+
+/* CONN_INFO_1 (pointers to level 1 connection info strings) */
+typedef struct ptr_conn_info1
+{
+ uint32 id; /* connection id */
+ uint32 type; /* 0x3 */
+ uint32 num_opens;
+ uint32 num_users;
+ uint32 open_time;
+
+ uint32 ptr_usr_name; /* pointer to user name. */
+ uint32 ptr_net_name; /* pointer to network name (e.g IPC$). */
+
+} CONN_INFO_1;
+
+/* CONN_INFO_1_STR (level 1 connection info strings) */
+typedef struct str_conn_info1
+{
+ UNISTR2 uni_usr_name; /* unicode string of user */
+ UNISTR2 uni_net_name; /* unicode string of name */
+
+} CONN_INFO_1_STR;
+
+/* SRV_CONN_INFO_1 */
+typedef struct srv_conn_info_1_info
+{
+ uint32 num_entries_read; /* EntriesRead */
+ uint32 ptr_conn_info; /* Buffer */
+ uint32 num_entries_read2; /* EntriesRead */
+
+ CONN_INFO_1 info_1 [MAX_CONN_ENTRIES]; /* connection entry pointers */
+ CONN_INFO_1_STR info_1_str[MAX_CONN_ENTRIES]; /* connection entry strings */
+
+} SRV_CONN_INFO_1;
+
+/* SRV_CONN_INFO_CTR */
+typedef struct srv_conn_info_ctr_info
+{
+ uint32 switch_value; /* switch value */
+ uint32 ptr_conn_ctr; /* pointer to conn info union */
+ union
+ {
+ SRV_CONN_INFO_0 info0; /* connection info level 0 */
+ SRV_CONN_INFO_1 info1; /* connection info level 1 */
+
+ } conn;
+
+} SRV_CONN_INFO_CTR;
+
+
+/* SRV_Q_NET_CONN_ENUM */
+typedef struct q_net_conn_enum_info
+{
+ uint32 ptr_srv_name; /* pointer (to server name) */
+ UNISTR2 uni_srv_name; /* server name "\\server" */
+
+ uint32 ptr_qual_name; /* pointer (to qualifier name) */
+ UNISTR2 uni_qual_name; /* qualifier name "\\qualifier" */
+
+ uint32 conn_level; /* connection level */
+
+ SRV_CONN_INFO_CTR *ctr;
+
+ uint32 preferred_len; /* preferred maximum length (0xffff ffff) */
+ ENUM_HND enum_hnd;
+
+} SRV_Q_NET_CONN_ENUM;
+
+/* SRV_R_NET_CONN_ENUM */
+typedef struct r_net_conn_enum_info
+{
+ uint32 conn_level; /* share level */
+
+ SRV_CONN_INFO_CTR *ctr;
+
+ uint32 total_entries; /* total number of entries */
+ ENUM_HND enum_hnd;
+
+ WERROR status; /* return status */
+
+} SRV_R_NET_CONN_ENUM;
+
+/* SH_INFO_0 */
+typedef struct ptr_share_info0
+{
+ uint32 ptr_netname; /* pointer to net name. */
+} SH_INFO_0;
+
+/* SH_INFO_0_STR (level 0 share info strings) */
+typedef struct str_share_info0
+{
+ SH_INFO_0 *ptrs;
+
+ UNISTR2 uni_netname; /* unicode string of net name */
+
+} SH_INFO_0_STR;
+
+/* SRV_SHARE_INFO_0 */
+typedef struct share_info_0_info
+{
+ SH_INFO_0 info_0;
+ SH_INFO_0_STR info_0_str;
+
+} SRV_SHARE_INFO_0;
+
+/* SH_INFO_1 (pointers to level 1 share info strings) */
+typedef struct ptr_share_info1
+{
+ uint32 ptr_netname; /* pointer to net name. */
+ uint32 type; /* ipc, print, disk ... */
+ uint32 ptr_remark; /* pointer to comment. */
+
+} SH_INFO_1;
+
+/* SH_INFO_1_STR (level 1 share info strings) */
+typedef struct str_share_info1
+{
+ SH_INFO_1 *ptrs;
+
+ UNISTR2 uni_netname; /* unicode string of net name */
+ UNISTR2 uni_remark; /* unicode string of comment */
+
+} SH_INFO_1_STR;
+
+/* SRV_SHARE_INFO_1 */
+typedef struct share_info_1_info
+{
+ SH_INFO_1 info_1;
+ SH_INFO_1_STR info_1_str;
+
+} SRV_SHARE_INFO_1;
+
+/* SH_INFO_2 (pointers to level 2 share info strings) */
+typedef struct ptr_share_info2
+{
+ uint32 ptr_netname; /* pointer to net name. */
+ uint32 type; /* ipc, print, disk ... */
+ uint32 ptr_remark; /* pointer to comment. */
+ uint32 perms; /* permissions */
+ uint32 max_uses; /* maximum uses */
+ uint32 num_uses; /* current uses */
+ uint32 ptr_path; /* pointer to path name */
+ uint32 ptr_passwd; /* pointer to password */
+
+} SH_INFO_2;
+
+/* SH_INFO_2_STR (level 2 share info strings) */
+typedef struct str_share_info2
+{
+ SH_INFO_2 *ptrs;
+
+ UNISTR2 uni_netname; /* unicode string of net name (e.g NETLOGON) */
+ UNISTR2 uni_remark; /* unicode string of comment (e.g "Logon server share") */
+ UNISTR2 uni_path; /* unicode string of local path (e.g c:\winnt\system32\repl\import\scripts) */
+ UNISTR2 uni_passwd; /* unicode string of password - presumably for share level security (e.g NULL) */
+
+} SH_INFO_2_STR;
+
+/* SRV_SHARE_INFO_2 */
+typedef struct share_info_2_info
+{
+ SH_INFO_2 info_2;
+ SH_INFO_2_STR info_2_str;
+
+} SRV_SHARE_INFO_2;
+
+typedef struct ptr_share_info501
+{
+ uint32 ptr_netname; /* pointer to net name */
+ uint32 type; /* ipc, print, disk */
+ uint32 ptr_remark; /* pointer to comment */
+ uint32 csc_policy; /* client-side offline caching policy << 4 */
+} SH_INFO_501;
+
+typedef struct str_share_info501
+{
+ UNISTR2 uni_netname; /* unicode string of net name */
+ UNISTR2 uni_remark; /* unicode string of comment */
+} SH_INFO_501_STR;
+
+/* SRV_SHARE_INFO_501 */
+typedef struct share_info_501_info
+{
+ SH_INFO_501 info_501;
+ SH_INFO_501_STR info_501_str;
+} SRV_SHARE_INFO_501;
+
+/* SH_INFO_502 (pointers to level 502 share info strings) */
+typedef struct ptr_share_info502
+{
+ uint32 ptr_netname; /* pointer to net name. */
+ uint32 type; /* ipc, print, disk ... */
+ uint32 ptr_remark; /* pointer to comment. */
+ uint32 perms; /* permissions */
+ uint32 max_uses; /* maximum uses */
+ uint32 num_uses; /* current uses */
+ uint32 ptr_path; /* pointer to path name */
+ uint32 ptr_passwd; /* pointer to password */
+ uint32 reserved; /* this holds the space taken by the sd in the rpc packet */
+ uint32 reserved_offset; /* required for _post operation when marshalling */
+ uint32 sd_size; /* size of security descriptor */
+ uint32 ptr_sd; /* pointer to security descriptor */
+
+} SH_INFO_502;
+
+/* SH_INFO_502_STR (level 502 share info strings) */
+typedef struct str_share_info502
+{
+ SH_INFO_502 *ptrs;
+
+ UNISTR2 uni_netname; /* unicode string of net name (e.g NETLOGON) */
+ UNISTR2 uni_remark; /* unicode string of comment (e.g "Logon server share") */
+ UNISTR2 uni_path; /* unicode string of local path (e.g c:\winnt\system32\repl\import\scripts) */
+ UNISTR2 uni_passwd; /* unicode string of password - presumably for share level security (e.g NULL) */
+
+ uint32 reserved;
+ uint32 sd_size;
+ SEC_DESC *sd;
+
+} SH_INFO_502_STR;
+
+/* SRV_SHARE_INFO_502 */
+typedef struct share_info_502_info
+{
+ SH_INFO_502 info_502;
+ SH_INFO_502_STR info_502_str;
+
+} SRV_SHARE_INFO_502;
+
+typedef struct ptr_share_info1004
+{
+ uint32 ptr_remark;
+
+} SH_INFO_1004;
+
+typedef struct str_share_info1004
+{
+ SH_INFO_1004 *ptrs;
+
+ UNISTR2 uni_remark;
+
+} SH_INFO_1004_STR;
+
+typedef struct ptr_info_1004_info
+{
+ SH_INFO_1004 info_1004;
+ SH_INFO_1004_STR info_1004_str;
+} SRV_SHARE_INFO_1004;
+
+#define SHARE_1005_IN_DFS 0x00000001
+#define SHARE_1005_DFS_ROOT 0x00000002
+/* use the CSC policy mask and shift to match up with the smb.conf parm */
+#define SHARE_1005_CSC_POLICY_MASK 0x00000030
+#define SHARE_1005_CSC_POLICY_SHIFT 4
+
+typedef struct share_info_1005_info
+{
+ uint32 share_info_flags;
+} SRV_SHARE_INFO_1005;
+
+typedef struct share_info_1006_info
+{
+ uint32 max_uses;
+} SRV_SHARE_INFO_1006;
+
+typedef struct ptr_share_info1007
+{
+ uint32 flags;
+ uint32 ptr_AlternateDirectoryName;
+
+} SH_INFO_1007;
+
+typedef struct str_share_info1007
+{
+ SH_INFO_1007 *ptrs;
+
+ UNISTR2 uni_AlternateDirectoryName;
+
+} SH_INFO_1007_STR;
+
+typedef struct ptr_info_1007_info
+{
+ SH_INFO_1007 info_1007;
+ SH_INFO_1007_STR info_1007_str;
+} SRV_SHARE_INFO_1007;
+
+/* SRV_SHARE_INFO_1501 */
+typedef struct share_info_1501_info
+{
+ SEC_DESC_BUF *sdb;
+} SRV_SHARE_INFO_1501;
+
+/* SRV_SHARE_INFO_CTR */
+typedef struct srv_share_info_ctr_info
+{
+ uint32 info_level;
+ uint32 switch_value;
+ uint32 ptr_share_info;
+
+ uint32 num_entries;
+ uint32 ptr_entries;
+ uint32 num_entries2;
+
+ union {
+ SRV_SHARE_INFO_0 *info0;
+ SRV_SHARE_INFO_1 *info1; /* share info level 1 */
+ SRV_SHARE_INFO_2 *info2; /* share info level 2 */
+ SRV_SHARE_INFO_501 *info501; /* share info level 501 */
+ SRV_SHARE_INFO_502 *info502; /* share info level 502 */
+ SRV_SHARE_INFO_1004 *info1004;
+ SRV_SHARE_INFO_1005 *info1005;
+ SRV_SHARE_INFO_1006 *info1006;
+ SRV_SHARE_INFO_1007 *info1007;
+ SRV_SHARE_INFO_1501 *info1501;
+ void *info;
+
+ } share;
+
+} SRV_SHARE_INFO_CTR;
+
+/* SRV_Q_NET_SHARE_ENUM */
+typedef struct q_net_share_enum_info
+{
+ uint32 ptr_srv_name; /* pointer (to server name?) */
+ UNISTR2 uni_srv_name; /* server name */
+
+ SRV_SHARE_INFO_CTR ctr; /* share info container */
+
+ uint32 preferred_len; /* preferred maximum length (0xffff ffff) */
+
+ ENUM_HND enum_hnd;
+
+} SRV_Q_NET_SHARE_ENUM;
+
+
+/* SRV_R_NET_SHARE_ENUM */
+typedef struct r_net_share_enum_info
+{
+ SRV_SHARE_INFO_CTR ctr; /* share info container */
+
+ uint32 total_entries; /* total number of entries */
+ ENUM_HND enum_hnd;
+
+ WERROR status; /* return status */
+
+} SRV_R_NET_SHARE_ENUM;
+
+
+/* SRV_Q_NET_SHARE_GET_INFO */
+typedef struct q_net_share_get_info_info
+{
+ uint32 ptr_srv_name;
+ UNISTR2 uni_srv_name;
+
+ UNISTR2 uni_share_name;
+ uint32 info_level;
+
+} SRV_Q_NET_SHARE_GET_INFO;
+
+/* SRV_SHARE_INFO */
+typedef struct srv_share_info {
+ uint32 switch_value;
+ uint32 ptr_share_ctr;
+
+ union {
+ SRV_SHARE_INFO_0 info0;
+ SRV_SHARE_INFO_1 info1;
+ SRV_SHARE_INFO_2 info2;
+ SRV_SHARE_INFO_501 info501;
+ SRV_SHARE_INFO_502 info502;
+ SRV_SHARE_INFO_1004 info1004;
+ SRV_SHARE_INFO_1005 info1005;
+ SRV_SHARE_INFO_1006 info1006;
+ SRV_SHARE_INFO_1007 info1007;
+ SRV_SHARE_INFO_1501 info1501;
+ } share;
+} SRV_SHARE_INFO;
+
+/* SRV_R_NET_SHARE_GET_INFO */
+typedef struct r_net_share_get_info_info
+{
+ SRV_SHARE_INFO info;
+ WERROR status;
+
+} SRV_R_NET_SHARE_GET_INFO;
+
+/* SRV_Q_NET_SHARE_SET_INFO */
+typedef struct q_net_share_set_info_info
+{
+ uint32 ptr_srv_name;
+ UNISTR2 uni_srv_name;
+
+ UNISTR2 uni_share_name;
+ uint32 info_level;
+
+ SRV_SHARE_INFO info;
+
+ uint32 ptr_parm_error;
+ uint32 parm_error;
+
+} SRV_Q_NET_SHARE_SET_INFO;
+
+/* SRV_R_NET_SHARE_SET_INFO */
+typedef struct r_net_share_set_info
+{
+ uint32 ptr_parm_error;
+ uint32 parm_error;
+
+ WERROR status; /* return status */
+
+} SRV_R_NET_SHARE_SET_INFO;
+
+/* SRV_Q_NET_SHARE_ADD */
+typedef struct q_net_share_add
+{
+ uint32 ptr_srv_name;
+ UNISTR2 uni_srv_name;
+
+ uint32 info_level;
+
+ SRV_SHARE_INFO info;
+
+ uint32 ptr_err_index; /* pointer to error index */
+ uint32 err_index; /* index in info to field in error */
+
+} SRV_Q_NET_SHARE_ADD;
+
+/* SRV_R_NET_SHARE_ADD */
+typedef struct r_net_share_add
+{
+
+ uint32 ptr_parm_error;
+ uint32 parm_error;
+
+ WERROR status; /* return status */
+
+} SRV_R_NET_SHARE_ADD;
+
+/* SRV_Q_NET_SHARE_DEL */
+typedef struct q_net_share_del
+{
+ uint32 ptr_srv_name;
+ UNISTR2 uni_srv_name;
+ UNISTR2 uni_share_name;
+ uint32 reserved;
+
+} SRV_Q_NET_SHARE_DEL;
+
+/* SRV_R_NET_SHARE_DEL */
+typedef struct r_net_share_del
+{
+ WERROR status; /* return status */
+
+} SRV_R_NET_SHARE_DEL;
+
+/* FILE_INFO_3 (level 3 file info strings) */
+typedef struct file_info3_info
+{
+ uint32 id; /* file index */
+ uint32 perms; /* file permissions. don't know what format */
+ uint32 num_locks; /* file locks */
+ uint32 ptr_path_name; /* file name */
+ uint32 ptr_user_name; /* file owner */
+
+} FILE_INFO_3;
+
+/* FILE_INFO_3_STR (level 3 file info strings) */
+typedef struct str_file_info3_info
+{
+ UNISTR2 uni_path_name; /* unicode string of file name */
+ UNISTR2 uni_user_name; /* unicode string of file owner. */
+
+} FILE_INFO_3_STR;
+
+/* SRV_FILE_INFO_3 */
+typedef struct srv_file_info_3
+{
+ uint32 num_entries_read; /* EntriesRead */
+ uint32 ptr_file_info; /* Buffer */
+
+ uint32 num_entries_read2; /* EntriesRead */
+ FILE_INFO_3 info_3; /* file entry details */
+ FILE_INFO_3_STR info_3_str; /* file entry strings */
+} SRV_FILE_INFO_3;
+
+/* SRV_FILE_INFO_CTR */
+typedef struct srv_file_info_3_info
+{
+ uint32 switch_value; /* switch value */
+ uint32 ptr_file_info; /* pointer to file info union */
+
+ uint32 num_entries;
+ uint32 ptr_entries;
+ uint32 num_entries2;
+ union
+ {
+ SRV_FILE_INFO_3 *info3;
+ } file;
+
+} SRV_FILE_INFO_CTR;
+
+
+/* SRV_Q_NET_FILE_ENUM */
+typedef struct q_net_file_enum_info
+{
+ uint32 ptr_srv_name; /* pointer (to server name?) */
+ UNISTR2 uni_srv_name; /* server name */
+
+ uint32 ptr_qual_name; /* pointer (to qualifier name) */
+ UNISTR2 uni_qual_name; /* qualifier name "\\qualifier" */
+
+ uint32 ptr_user_name; /* pointer (to user name) */
+ UNISTR2 uni_user_name; /* user name */
+
+ uint32 file_level; /* file level */
+
+ SRV_FILE_INFO_CTR ctr;
+
+ uint32 preferred_len; /* preferred maximum length (0xffff ffff) */
+ ENUM_HND enum_hnd;
+
+} SRV_Q_NET_FILE_ENUM;
+
+
+/* SRV_R_NET_FILE_ENUM */
+typedef struct r_net_file_enum_info
+{
+ uint32 file_level; /* file level */
+
+ SRV_FILE_INFO_CTR ctr;
+
+ uint32 total_entries; /* total number of files */
+ ENUM_HND enum_hnd;
+
+ WERROR status; /* return status */
+
+} SRV_R_NET_FILE_ENUM;
+
+/* SRV_Q_NET_FILE_CLOSE */
+typedef struct q_net_file_close
+{
+ uint32 ptr_srv_name; /* pointer to server name */
+ UNISTR2 uni_srv_name; /* server name */
+
+ uint32 file_id;
+} SRV_Q_NET_FILE_CLOSE;
+
+/* SRV_R_NET_FILE_CLOSE */
+typedef struct r_net_file_close
+{
+ WERROR status; /* return status */
+} SRV_R_NET_FILE_CLOSE;
+
+/* SRV_INFO_100 */
+typedef struct srv_info_100_info
+{
+ uint32 platform_id; /* 0x500 */
+ uint32 ptr_name; /* pointer to server name */
+
+ UNISTR2 uni_name; /* server name "server" */
+
+} SRV_INFO_100;
+
+/* SRV_INFO_101 */
+typedef struct srv_info_101_info
+{
+ uint32 platform_id; /* 0x500 */
+ uint32 ptr_name; /* pointer to server name */
+ uint32 ver_major; /* 0x4 */
+ uint32 ver_minor; /* 0x2 */
+ uint32 srv_type; /* browse etc type */
+ uint32 ptr_comment; /* pointer to server comment */
+
+ UNISTR2 uni_name; /* server name "server" */
+ UNISTR2 uni_comment; /* server comment "samba x.x.x blah" */
+
+} SRV_INFO_101;
+
+/* SRV_INFO_102 */
+typedef struct srv_info_102_info
+{
+ uint32 platform_id; /* 0x500 */
+ uint32 ptr_name; /* pointer to server name */
+ uint32 ver_major; /* 0x4 */
+ uint32 ver_minor; /* 0x2 */
+ uint32 srv_type; /* browse etc type */
+ uint32 ptr_comment; /* pointer to server comment */
+ uint32 users; /* 0xffff ffff*/
+ uint32 disc; /* 0xf */
+ uint32 hidden; /* 0x0 */
+ uint32 announce; /* 240 */
+ uint32 ann_delta; /* 3000 */
+ uint32 licenses; /* 0 */
+ uint32 ptr_usr_path; /* pointer to user path */
+
+ UNISTR2 uni_name; /* server name "server" */
+ UNISTR2 uni_comment; /* server comment "samba x.x.x blah" */
+ UNISTR2 uni_usr_path; /* "c:\" (eh?) */
+
+} SRV_INFO_102;
+
+
+/* SRV_INFO_CTR */
+typedef struct srv_info_ctr_info
+{
+ uint32 switch_value; /* switch value */
+ uint32 ptr_srv_ctr; /* pointer to server info */
+ union
+ {
+ SRV_INFO_102 sv102; /* server info level 102 */
+ SRV_INFO_101 sv101; /* server info level 101 */
+ SRV_INFO_100 sv100; /* server info level 100 */
+
+ } srv;
+
+} SRV_INFO_CTR;
+
+/* SRV_Q_NET_SRV_GET_INFO */
+typedef struct q_net_srv_get_info
+{
+ uint32 ptr_srv_name;
+ UNISTR2 uni_srv_name; /* "\\server" */
+ uint32 switch_value;
+
+} SRV_Q_NET_SRV_GET_INFO;
+
+/* SRV_R_NET_SRV_GET_INFO */
+typedef struct r_net_srv_get_info
+{
+ SRV_INFO_CTR *ctr;
+
+ WERROR status; /* return status */
+
+} SRV_R_NET_SRV_GET_INFO;
+
+/* SRV_Q_NET_SRV_SET_INFO */
+typedef struct q_net_srv_set_info
+{
+ uint32 ptr_srv_name;
+ UNISTR2 uni_srv_name; /* "\\server" */
+ uint32 switch_value;
+
+ SRV_INFO_CTR *ctr;
+
+} SRV_Q_NET_SRV_SET_INFO;
+
+
+/* SRV_R_NET_SRV_SET_INFO */
+typedef struct r_net_srv_set_info
+{
+ uint32 switch_value; /* switch value */
+
+ WERROR status; /* return status */
+
+} SRV_R_NET_SRV_SET_INFO;
+
+/* SRV_Q_NET_REMOTE_TOD */
+typedef struct q_net_remote_tod
+{
+ uint32 ptr_srv_name;
+ UNISTR2 uni_srv_name; /* "\\server" */
+
+} SRV_Q_NET_REMOTE_TOD;
+
+/* TIME_OF_DAY_INFO */
+typedef struct time_of_day_info
+{
+ uint32 elapsedt;
+ uint32 msecs;
+ uint32 hours;
+ uint32 mins;
+ uint32 secs;
+ uint32 hunds;
+ uint32 zone;
+ uint32 tintervals;
+ uint32 day;
+ uint32 month;
+ uint32 year;
+ uint32 weekday;
+
+} TIME_OF_DAY_INFO;
+
+/* SRV_R_NET_REMOTE_TOD */
+typedef struct r_net_remote_tod
+{
+ uint32 ptr_srv_tod; /* pointer to TOD */
+ TIME_OF_DAY_INFO *tod;
+
+ WERROR status; /* return status */
+
+} SRV_R_NET_REMOTE_TOD;
+
+/* SRV_Q_NET_FILE_QUERY_SECDESC */
+typedef struct q_net_file_query_secdesc
+{
+ uint32 ptr_srv_name;
+ UNISTR2 uni_srv_name;
+ uint32 ptr_qual_name;
+ UNISTR2 uni_qual_name;
+ UNISTR2 uni_file_name;
+ uint32 unknown1;
+ uint32 unknown2;
+ uint32 unknown3;
+} SRV_Q_NET_FILE_QUERY_SECDESC;
+
+/* SRV_R_NET_FILE_QUERY_SECDESC */
+typedef struct r_net_file_query_secdesc
+{
+ uint32 ptr_response;
+ uint32 size_response;
+ uint32 ptr_secdesc;
+ uint32 size_secdesc;
+ SEC_DESC *sec_desc;
+ WERROR status;
+} SRV_R_NET_FILE_QUERY_SECDESC;
+
+/* SRV_Q_NET_FILE_SET_SECDESC */
+typedef struct q_net_file_set_secdesc
+{
+ uint32 ptr_srv_name;
+ UNISTR2 uni_srv_name;
+ uint32 ptr_qual_name;
+ UNISTR2 uni_qual_name;
+ UNISTR2 uni_file_name;
+ uint32 sec_info;
+ uint32 size_set;
+ uint32 ptr_secdesc;
+ uint32 size_secdesc;
+ SEC_DESC *sec_desc;
+} SRV_Q_NET_FILE_SET_SECDESC;
+
+/* SRV_R_NET_FILE_SET_SECDESC */
+typedef struct r_net_file_set_secdesc
+{
+ WERROR status;
+} SRV_R_NET_FILE_SET_SECDESC;
+
+#endif /* _RPC_SRVSVC_H */
diff --git a/source/include/rpc_wkssvc.h b/source/include/rpc_wkssvc.h
new file mode 100644
index 00000000000..adc37c255b2
--- /dev/null
+++ b/source/include/rpc_wkssvc.h
@@ -0,0 +1,72 @@
+/*
+ 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 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 _RPC_WKS_H /* _RPC_WKS_H */
+#define _RPC_WKS_H
+
+
+/* wkssvc pipe */
+#define WKS_QUERY_INFO 0x00
+
+
+/* WKS_Q_QUERY_INFO - probably a capabilities request */
+typedef struct q_wks_query_info_info
+{
+ uint32 ptr_srv_name; /* pointer (to server name?) */
+ UNISTR2 uni_srv_name; /* unicode server name starting with '\\' */
+
+ uint16 switch_value; /* info level 100 (0x64) */
+
+} WKS_Q_QUERY_INFO;
+
+
+/* WKS_INFO_100 - level 100 info */
+typedef struct wks_info_100_info
+{
+ uint32 platform_id; /* 0x0000 01f4 - unknown */
+ uint32 ptr_compname; /* pointer to server name */
+ uint32 ptr_lan_grp ; /* pointer to domain name */
+ uint32 ver_major; /* 4 - unknown */
+ uint32 ver_minor; /* 0 - unknown */
+
+ UNISTR2 uni_compname; /* unicode server name */
+ UNISTR2 uni_lan_grp ; /* unicode domain name */
+
+} WKS_INFO_100;
+
+
+/* WKS_R_QUERY_INFO - probably a capabilities request */
+typedef struct r_wks_query_info_info
+{
+ uint16 switch_value; /* 100 (0x64) - switch value */
+
+ /* for now, only level 100 is supported. this should be an enum container */
+ uint32 ptr_1; /* pointer 1 */
+ WKS_INFO_100 *wks100; /* workstation info level 100 */
+
+ NTSTATUS status; /* return status */
+
+} WKS_R_QUERY_INFO;
+
+
+#endif /* _RPC_WKS_H */
+
diff --git a/source/include/safe_string.h b/source/include/safe_string.h
new file mode 100644
index 00000000000..b22c5efcc99
--- /dev/null
+++ b/source/include/safe_string.h
@@ -0,0 +1,228 @@
+/*
+ Unix SMB/CIFS implementation.
+ Safe string handling routines.
+ Copyright (C) Andrew Tridgell 1994-1998
+ Copyright (C) Andrew Bartlett <abartlet@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 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 _SAFE_STRING_H
+#define _SAFE_STRING_H
+
+#ifndef _SPLINT_ /* http://www.splint.org */
+
+/* Some macros to ensure people don't use buffer overflow vulnerable string
+ functions. */
+
+#ifdef bcopy
+#undef bcopy
+#endif /* bcopy */
+#define bcopy(src,dest,size) __ERROR__XX__NEVER_USE_BCOPY___;
+
+#ifdef strcpy
+#undef strcpy
+#endif /* strcpy */
+#define strcpy(dest,src) __ERROR__XX__NEVER_USE_STRCPY___;
+
+#ifdef strcat
+#undef strcat
+#endif /* strcat */
+#define strcat(dest,src) __ERROR__XX__NEVER_USE_STRCAT___;
+
+#ifdef sprintf
+#undef sprintf
+#endif /* sprintf */
+#define sprintf __ERROR__XX__NEVER_USE_SPRINTF__;
+
+/*
+ * strcasecmp/strncasecmp aren't an error, but it means you're not thinking about
+ * multibyte. Don't use them. JRA.
+ */
+#ifdef strcasecmp
+#undef strcasecmp
+#endif
+#define strcasecmp __ERROR__XX__NEVER_USE_STRCASECMP__;
+
+#ifdef strncasecmp
+#undef strncasecmp
+#endif
+#define strncasecmp __ERROR__XX__NEVER_USE_STRCASECMP__;
+
+#endif /* !_SPLINT_ */
+
+#ifdef DEVELOPER
+#define SAFE_STRING_FUNCTION_NAME FUNCTION_MACRO
+#define SAFE_STRING_LINE __LINE__
+#else
+#define SAFE_STRING_FUNCTION_NAME ("")
+#define SAFE_STRING_LINE (0)
+#endif
+
+/* We need a number of different prototypes for our
+ non-existant fuctions */
+char * __unsafe_string_function_usage_here__(void);
+
+size_t __unsafe_string_function_usage_here_size_t__(void);
+
+size_t __unsafe_string_function_usage_here_char__(void);
+
+#ifdef HAVE_COMPILER_WILL_OPTIMIZE_OUT_FNS
+
+/* if the compiler will optimize out function calls, then use this to tell if we are
+ have the correct types (this works only where sizeof() returns the size of the buffer, not
+ the size of the pointer). */
+
+#define CHECK_STRING_SIZE(d, len) (sizeof(d) != (len) && sizeof(d) != sizeof(char *))
+
+#define fstrterminate(d) (CHECK_STRING_SIZE(d, sizeof(fstring)) \
+ ? __unsafe_string_function_usage_here_char__() \
+ : (((d)[sizeof(fstring)-1]) = '\0'))
+#define pstrterminate(d) (CHECK_STRING_SIZE(d, sizeof(pstring)) \
+ ? __unsafe_string_function_usage_here_char__() \
+ : (((d)[sizeof(pstring)-1]) = '\0'))
+
+#define wpstrcpy(d,s) ((sizeof(d) != sizeof(wpstring) && sizeof(d) != sizeof(smb_ucs2_t *)) \
+ ? __unsafe_string_function_usage_here__() \
+ : safe_strcpy_w((d),(s),sizeof(wpstring)))
+#define wpstrcat(d,s) ((sizeof(d) != sizeof(wpstring) && sizeof(d) != sizeof(smb_ucs2_t *)) \
+ ? __unsafe_string_function_usage_here__() \
+ : safe_strcat_w((d),(s),sizeof(wpstring)))
+#define wfstrcpy(d,s) ((sizeof(d) != sizeof(wfstring) && sizeof(d) != sizeof(smb_ucs2_t *)) \
+ ? __unsafe_string_function_usage_here__() \
+ : safe_strcpy_w((d),(s),sizeof(wfstring)))
+#define wfstrcat(d,s) ((sizeof(d) != sizeof(wfstring) && sizeof(d) != sizeof(smb_ucs2_t *)) \
+ ? __unsafe_string_function_usage_here__() \
+ : safe_strcat_w((d),(s),sizeof(wfstring)))
+
+#define push_pstring_base(dest, src, pstring_base) \
+ (CHECK_STRING_SIZE(pstring_base, sizeof(pstring)) \
+ ? __unsafe_string_function_usage_here_size_t__() \
+ : push_ascii(dest, src, sizeof(pstring)-PTR_DIFF(dest,pstring_base)-1, STR_TERMINATE))
+
+#else /* HAVE_COMPILER_WILL_OPTIMIZE_OUT_FNS */
+
+#define fstrterminate(d) (((d)[sizeof(fstring)-1]) = '\0')
+#define pstrterminate(d) (((d)[sizeof(pstring)-1]) = '\0')
+
+#define wpstrcpy(d,s) safe_strcpy_w((d),(s),sizeof(wpstring))
+#define wpstrcat(d,s) safe_strcat_w((d),(s),sizeof(wpstring))
+#define wfstrcpy(d,s) safe_strcpy_w((d),(s),sizeof(wfstring))
+#define wfstrcat(d,s) safe_strcat_w((d),(s),sizeof(wfstring))
+
+#define push_pstring_base(dest, src, pstring_base) \
+ push_ascii(dest, src, sizeof(pstring)-PTR_DIFF(dest,pstring_base)-1, STR_TERMINATE)
+
+#endif /* HAVE_COMPILER_WILL_OPTIMIZE_OUT_FNS */
+
+#define safe_strcpy_base(dest, src, base, size) \
+ safe_strcpy(dest, src, size-PTR_DIFF(dest,base)-1)
+
+/* String copy functions - macro hell below adds 'type checking' (limited,
+ but the best we can do in C) and may tag with function name/number to
+ record the last 'clobber region' on that string */
+
+#define pstrcpy(d,s) safe_strcpy((d), (s),sizeof(pstring)-1)
+#define pstrcat(d,s) safe_strcat((d), (s),sizeof(pstring)-1)
+#define fstrcpy(d,s) safe_strcpy((d),(s),sizeof(fstring)-1)
+#define fstrcat(d,s) safe_strcat((d),(s),sizeof(fstring)-1)
+#define nstrcpy(d,s) safe_strcpy((d), (s),sizeof(nstring)-1)
+#define unstrcpy(d,s) safe_strcpy((d), (s),sizeof(unstring)-1)
+
+/* the addition of the DEVELOPER checks in safe_strcpy means we must
+ * update a lot of code. To make this a little easier here are some
+ * functions that provide the lengths with less pain */
+#define pstrcpy_base(dest, src, pstring_base) \
+ safe_strcpy(dest, src, sizeof(pstring)-PTR_DIFF(dest,pstring_base)-1)
+
+
+/* Inside the _fn variants of these is a call to clobber_region(), -
+ * which might destroy the stack on a buggy function. We help the
+ * debugging process by putting the function and line who last caused
+ * a clobbering into a static buffer. If the program crashes at
+ * address 0xf1f1f1f1 then this function is probably, but not
+ * necessarily, to blame. */
+
+/* overmalloc_safe_strcpy: DEPRECATED! Used when you know the
+ * destination buffer is longer than maxlength, but you don't know how
+ * long. This is not a good situation, because we can't do the normal
+ * sanity checks. Don't use in new code! */
+
+#define overmalloc_safe_strcpy(dest,src,maxlength) safe_strcpy_fn(SAFE_STRING_FUNCTION_NAME, SAFE_STRING_LINE,dest,src,maxlength)
+#define safe_strcpy(dest,src,maxlength) safe_strcpy_fn2(SAFE_STRING_FUNCTION_NAME, SAFE_STRING_LINE,dest,src,maxlength)
+#define safe_strcat(dest,src,maxlength) safe_strcat_fn2(SAFE_STRING_FUNCTION_NAME, SAFE_STRING_LINE,dest,src,maxlength)
+#define push_string(base_ptr, dest, src, dest_len, flags) push_string_fn2(SAFE_STRING_FUNCTION_NAME, SAFE_STRING_LINE, base_ptr, dest, src, dest_len, flags)
+#define pull_string(base_ptr, dest, src, dest_len, src_len, flags) pull_string_fn2(SAFE_STRING_FUNCTION_NAME, SAFE_STRING_LINE, base_ptr, dest, src, dest_len, src_len, flags)
+#define clistr_push(cli, dest, src, dest_len, flags) clistr_push_fn2(SAFE_STRING_FUNCTION_NAME, SAFE_STRING_LINE, cli, dest, src, dest_len, flags)
+#define clistr_pull(cli, dest, src, dest_len, src_len, flags) clistr_pull_fn2(SAFE_STRING_FUNCTION_NAME, SAFE_STRING_LINE, cli, dest, src, dest_len, src_len, flags)
+#define srvstr_push(base_ptr, dest, src, dest_len, flags) srvstr_push_fn2(SAFE_STRING_FUNCTION_NAME, SAFE_STRING_LINE, base_ptr, dest, src, dest_len, flags)
+
+#define alpha_strcpy(dest,src,other_safe_chars,maxlength) alpha_strcpy_fn(SAFE_STRING_FUNCTION_NAME,SAFE_STRING_LINE,dest,src,other_safe_chars,maxlength)
+#define StrnCpy(dest,src,n) StrnCpy_fn(SAFE_STRING_FUNCTION_NAME,SAFE_STRING_LINE,dest,src,n)
+
+#ifdef HAVE_COMPILER_WILL_OPTIMIZE_OUT_FNS
+
+/* if the compiler will optimize out function calls, then use this to tell if we are
+ have the correct types (this works only where sizeof() returns the size of the buffer, not
+ the size of the pointer). */
+
+#define safe_strcpy_fn2(fn_name, fn_line, d, s, max_len) \
+ (CHECK_STRING_SIZE(d, max_len+1) \
+ ? __unsafe_string_function_usage_here__() \
+ : safe_strcpy_fn(fn_name, fn_line, (d), (s), (max_len)))
+
+#define safe_strcat_fn2(fn_name, fn_line, d, s, max_len) \
+ (CHECK_STRING_SIZE(d, max_len+1) \
+ ? __unsafe_string_function_usage_here__() \
+ : safe_strcat_fn(fn_name, fn_line, (d), (s), (max_len)))
+
+#define push_string_fn2(fn_name, fn_line, base_ptr, dest, src, dest_len, flags) \
+ (CHECK_STRING_SIZE(dest, dest_len) \
+ ? __unsafe_string_function_usage_here_size_t__() \
+ : push_string_fn(fn_name, fn_line, base_ptr, dest, src, dest_len, flags))
+
+#define pull_string_fn2(fn_name, fn_line, base_ptr, dest, src, dest_len, src_len, flags) \
+ (CHECK_STRING_SIZE(dest, dest_len) \
+ ? __unsafe_string_function_usage_here_size_t__() \
+ : pull_string_fn(fn_name, fn_line, base_ptr, dest, src, dest_len, src_len, flags))
+
+#define clistr_push_fn2(fn_name, fn_line, cli, dest, src, dest_len, flags) \
+ (CHECK_STRING_SIZE(dest, dest_len) \
+ ? __unsafe_string_function_usage_here_size_t__() \
+ : clistr_push_fn(fn_name, fn_line, cli, dest, src, dest_len, flags))
+
+#define clistr_pull_fn2(fn_name, fn_line, cli, dest, src, dest_len, srclen, flags) \
+ (CHECK_STRING_SIZE(dest, dest_len) \
+ ? __unsafe_string_function_usage_here_size_t__() \
+ : clistr_pull_fn(fn_name, fn_line, cli, dest, src, dest_len, srclen, flags))
+
+#define srvstr_push_fn2(fn_name, fn_line, base_ptr, dest, src, dest_len, flags) \
+ (CHECK_STRING_SIZE(dest, dest_len) \
+ ? __unsafe_string_function_usage_here_size_t__() \
+ : srvstr_push_fn(fn_name, fn_line, base_ptr, dest, src, dest_len, flags))
+
+#else
+
+#define safe_strcpy_fn2 safe_strcpy_fn
+#define safe_strcat_fn2 safe_strcat_fn
+#define push_string_fn2 push_string_fn
+#define pull_string_fn2 pull_string_fn
+#define clistr_push_fn2 clistr_push_fn
+#define clistr_pull_fn2 clistr_pull_fn
+#define srvstr_push_fn2 srvstr_push_fn
+
+#endif
+
+#endif
diff --git a/source/include/samba_linux_quota.h b/source/include/samba_linux_quota.h
new file mode 100644
index 00000000000..02b3e5169b1
--- /dev/null
+++ b/source/include/samba_linux_quota.h
@@ -0,0 +1,336 @@
+#ifndef _SAMBA_LINUX_QUOTA_H_
+#define _SAMBA_LINUX_QUOTA_H_
+/*
+ Unix SMB/CIFS implementation.
+ Copyright (C) Andrew Tridgell 1994-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 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.
+*/
+
+/*
+ This file is needed because Quota support on Linux has
+ been broken since Linus kernel 2.4.x. It will only get
+ better (and this file be removed) when all the distributions
+ ship a glibc with a working quota.h file. This is very
+ bad. JRA.
+
+ Original file came from Christoph Hellwig <hch@infradead.org>.
+ Massaged into one nasty include file (to stop us having to
+ add multiple files into Samba just for Linux braindamage)
+ by JRA.
+*/
+
+#undef QUOTABLOCK_SIZE
+
+#ifndef _QUOTAIO_LINUX_V1
+#define _QUOTAIO_LINUX_V1
+
+/*
+ * Headerfile for old quotafile format
+ */
+
+#include <sys/types.h>
+
+#define V1_DQBLK_SIZE_BITS 10
+#define V1_DQBLK_SIZE (1 << V1_DQBLK_SIZE_BITS) /* Size of one quota block in bytes in old format */
+
+#define V1_DQOFF(__id) ((loff_t) ((__id) * sizeof(struct v1_disk_dqblk)))
+
+/* Structure of quota on disk */
+struct v1_disk_dqblk {
+ u_int32_t dqb_bhardlimit; /* absolute limit on disk blks alloc */
+ u_int32_t dqb_bsoftlimit; /* preferred limit on disk blks */
+ u_int32_t dqb_curblocks; /* current block count */
+ u_int32_t dqb_ihardlimit; /* maximum # allocated inodes */
+ u_int32_t dqb_isoftlimit; /* preferred limit on inodes */
+ u_int32_t dqb_curinodes; /* current # allocated inodes */
+ time_t dqb_btime; /* time limit for excessive disk use */
+ time_t dqb_itime; /* time limit for excessive files */
+} __attribute__ ((packed));
+
+/* Structure used for communication with kernel */
+struct v1_kern_dqblk {
+ u_int32_t dqb_bhardlimit; /* absolute limit on disk blks alloc */
+ u_int32_t dqb_bsoftlimit; /* preferred limit on disk blks */
+ u_int32_t dqb_curblocks; /* current block count */
+ u_int32_t dqb_ihardlimit; /* maximum # allocated inodes */
+ u_int32_t dqb_isoftlimit; /* preferred inode limit */
+ u_int32_t dqb_curinodes; /* current # allocated inodes */
+ time_t dqb_btime; /* time limit for excessive disk use */
+ time_t dqb_itime; /* time limit for excessive files */
+};
+
+struct v1_dqstats {
+ u_int32_t lookups;
+ u_int32_t drops;
+ u_int32_t reads;
+ u_int32_t writes;
+ u_int32_t cache_hits;
+ u_int32_t allocated_dquots;
+ u_int32_t free_dquots;
+ u_int32_t syncs;
+};
+
+#ifndef Q_V1_GETQUOTA
+#define Q_V1_GETQUOTA 0x300
+#endif
+#ifndef Q_V1_SETQUOTA
+#define Q_V1_SETQUOTA 0x400
+#endif
+
+#endif /* _QUOTAIO_LINUX_V1 */
+
+/*
+ *
+ * Header file for disk format of new quotafile format
+ *
+ */
+
+#ifndef _QUOTAIO_LINUX_V2
+#define _QUOTAIO_LINUX_V2
+
+#include <sys/types.h>
+
+#ifndef _QUOTA_LINUX
+#define _QUOTA_LINUX
+
+#include <sys/types.h>
+
+typedef u_int32_t qid_t; /* Type in which we store ids in memory */
+typedef u_int64_t qsize_t; /* Type in which we store size limitations */
+
+#define MAXQUOTAS 2
+#define USRQUOTA 0 /* element used for user quotas */
+#define GRPQUOTA 1 /* element used for group quotas */
+
+/*
+ * Definitions for the default names of the quotas files.
+ */
+#define INITQFNAMES { \
+ "user", /* USRQUOTA */ \
+ "group", /* GRPQUOTA */ \
+ "undefined", \
+}
+
+/*
+ * Definitions of magics and versions of current quota files
+ */
+#define INITQMAGICS {\
+ 0xd9c01f11, /* USRQUOTA */\
+ 0xd9c01927 /* GRPQUOTA */\
+}
+
+/* Size of blocks in which are counted size limits in generic utility parts */
+#define QUOTABLOCK_BITS 10
+#define QUOTABLOCK_SIZE (1 << QUOTABLOCK_BITS)
+
+/* Conversion routines from and to quota blocks */
+#define qb2kb(x) ((x) << (QUOTABLOCK_BITS-10))
+#define kb2qb(x) ((x) >> (QUOTABLOCK_BITS-10))
+#define toqb(x) (((x) + QUOTABLOCK_SIZE - 1) >> QUOTABLOCK_BITS)
+
+/*
+ * Command definitions for the 'quotactl' system call.
+ * The commands are broken into a main command defined below
+ * and a subcommand that is used to convey the type of
+ * quota that is being manipulated (see above).
+ */
+#define SUBCMDMASK 0x00ff
+#define SUBCMDSHIFT 8
+#define QCMD(cmd, type) (((cmd) << SUBCMDSHIFT) | ((type) & SUBCMDMASK))
+
+#define Q_6_5_QUOTAON 0x0100 /* enable quotas */
+#define Q_6_5_QUOTAOFF 0x0200 /* disable quotas */
+#define Q_6_5_SYNC 0x0600 /* sync disk copy of a filesystems quotas */
+
+#define Q_SYNC 0x800001 /* sync disk copy of a filesystems quotas */
+#define Q_QUOTAON 0x800002 /* turn quotas on */
+#define Q_QUOTAOFF 0x800003 /* turn quotas off */
+#define Q_GETFMT 0x800004 /* get quota format used on given filesystem */
+#define Q_GETINFO 0x800005 /* get information about quota files */
+#define Q_SETINFO 0x800006 /* set information about quota files */
+#define Q_GETQUOTA 0x800007 /* get user quota structure */
+#define Q_SETQUOTA 0x800008 /* set user quota structure */
+
+/*
+ * Quota structure used for communication with userspace via quotactl
+ * Following flags are used to specify which fields are valid
+ */
+#define QIF_BLIMITS 1
+#define QIF_SPACE 2
+#define QIF_ILIMITS 4
+#define QIF_INODES 8
+#define QIF_BTIME 16
+#define QIF_ITIME 32
+#define QIF_LIMITS (QIF_BLIMITS | QIF_ILIMITS)
+#define QIF_USAGE (QIF_SPACE | QIF_INODES)
+#define QIF_TIMES (QIF_BTIME | QIF_ITIME)
+#define QIF_ALL (QIF_LIMITS | QIF_USAGE | QIF_TIMES)
+
+struct if_dqblk {
+ u_int64_t dqb_bhardlimit;
+ u_int64_t dqb_bsoftlimit;
+ u_int64_t dqb_curspace;
+ u_int64_t dqb_ihardlimit;
+ u_int64_t dqb_isoftlimit;
+ u_int64_t dqb_curinodes;
+ u_int64_t dqb_btime;
+ u_int64_t dqb_itime;
+ u_int32_t dqb_valid;
+};
+
+/*
+ * Structure used for setting quota information about file via quotactl
+ * Following flags are used to specify which fields are valid
+ */
+#define IIF_BGRACE 1
+#define IIF_IGRACE 2
+#define IIF_FLAGS 4
+#define IIF_ALL (IIF_BGRACE | IIF_IGRACE | IIF_FLAGS)
+
+struct if_dqinfo {
+ u_int64_t dqi_bgrace;
+ u_int64_t dqi_igrace;
+ u_int32_t dqi_flags;
+ u_int32_t dqi_valid;
+};
+
+/* Quota format identifiers */
+#define QFMT_VFS_OLD 1
+#define QFMT_VFS_V0 2
+
+/* Flags supported by kernel */
+#define V1_DQF_RSQUASH 1
+
+/* Ioctl for getting quota size */
+#include <sys/ioctl.h>
+#ifndef FIOQSIZE
+ #if defined(__alpha__) || defined(__powerpc__) || defined(__sh__) || defined(__sparc__) || defined(__sparc64__)
+ #define FIOQSIZE _IOR('f', 128, loff_t)
+ #elif defined(__arm__) || defined(__mc68000__) || defined(__s390__)
+ #define FIOQSIZE 0x545E
+ #elif defined(__i386__) || defined(__i486__) || defined(__i586__) || defined(__ia64__) || defined(__parisc__) || defined(__cris__) || defined(__hppa__)
+ #define FIOQSIZE 0x5460
+ #elif defined(__mips__) || defined(__mips64__)
+ #define FIOQSIZE 0x6667
+ #endif
+#endif
+
+long quotactl __P((int, const char *, qid_t, caddr_t));
+
+#endif /* _QUOTA_LINUX */
+
+#define V2_DQINFOOFF sizeof(struct v2_disk_dqheader) /* Offset of info header in file */
+#define V2_DQBLKSIZE_BITS 10
+#define V2_DQBLKSIZE (1 << V2_DQBLKSIZE_BITS) /* Size of block with quota structures */
+#define V2_DQTREEOFF 1 /* Offset of tree in file in blOcks */
+#define V2_DQTREEDEPTH 4 /* Depth of quota tree */
+#define V2_DQSTRINBLK ((V2_DQBLKSIZE - sizeof(struct v2_disk_dqdbheader)) / sizeof(struct v2_disk_dqblk)) /* Number of entries in one blocks */
+#define V2_GETIDINDEX(id, depth) (((id) >> ((V2_DQTREEDEPTH-(depth)-1)*8)) & 0xff)
+#define V2_GETENTRIES(buf) ((struct v2_disk_dqblk *)(((char *)(buf)) + sizeof(struct v2_disk_dqdbheader)))
+#define INIT_V2_VERSIONS { 0, 0}
+
+struct v2_disk_dqheader {
+ u_int32_t dqh_magic; /* Magic number identifying file */
+ u_int32_t dqh_version; /* File version */
+} __attribute__ ((packed));
+
+/* Flags for version specific files */
+#define V2_DQF_MASK 0x0000 /* Mask for all valid ondisk flags */
+
+/* Header with type and version specific information */
+struct v2_disk_dqinfo {
+ u_int32_t dqi_bgrace; /* Time before block soft limit becomes hard limit */
+ u_int32_t dqi_igrace; /* Time before inode soft limit becomes hard limit */
+ u_int32_t dqi_flags; /* Flags for quotafile (DQF_*) */
+ u_int32_t dqi_blocks; /* Number of blocks in file */
+ u_int32_t dqi_free_blk; /* Number of first free block in the list */
+ u_int32_t dqi_free_entry; /* Number of block with at least one free entry */
+} __attribute__ ((packed));
+
+/*
+ * Structure of header of block with quota structures. It is padded to 16 bytes so
+ * there will be space for exactly 18 quota-entries in a block
+ */
+struct v2_disk_dqdbheader {
+ u_int32_t dqdh_next_free; /* Number of next block with free entry */
+ u_int32_t dqdh_prev_free; /* Number of previous block with free entry */
+ u_int16_t dqdh_entries; /* Number of valid entries in block */
+ u_int16_t dqdh_pad1;
+ u_int32_t dqdh_pad2;
+} __attribute__ ((packed));
+
+/* Structure of quota for one user on disk */
+struct v2_disk_dqblk {
+ u_int32_t dqb_id; /* id this quota applies to */
+ u_int32_t dqb_ihardlimit; /* absolute limit on allocated inodes */
+ u_int32_t dqb_isoftlimit; /* preferred inode limit */
+ u_int32_t dqb_curinodes; /* current # allocated inodes */
+ u_int32_t dqb_bhardlimit; /* absolute limit on disk space (in QUOTABLOCK_SIZE) */
+ u_int32_t dqb_bsoftlimit; /* preferred limit on disk space (in QUOTABLOCK_SIZE) */
+ u_int64_t dqb_curspace; /* current space occupied (in bytes) */
+ u_int64_t dqb_btime; /* time limit for excessive disk use */
+ u_int64_t dqb_itime; /* time limit for excessive inode use */
+} __attribute__ ((packed));
+
+/* Structure of quota for communication with kernel */
+struct v2_kern_dqblk {
+ unsigned int dqb_ihardlimit;
+ unsigned int dqb_isoftlimit;
+ unsigned int dqb_curinodes;
+ unsigned int dqb_bhardlimit;
+ unsigned int dqb_bsoftlimit;
+ qsize_t dqb_curspace;
+ time_t dqb_btime;
+ time_t dqb_itime;
+};
+
+/* Structure of quotafile info for communication with kernel */
+struct v2_kern_dqinfo {
+ unsigned int dqi_bgrace;
+ unsigned int dqi_igrace;
+ unsigned int dqi_flags;
+ unsigned int dqi_blocks;
+ unsigned int dqi_free_blk;
+ unsigned int dqi_free_entry;
+};
+
+/* Structure with gathered statistics from kernel */
+struct v2_dqstats {
+ u_int32_t lookups;
+ u_int32_t drops;
+ u_int32_t reads;
+ u_int32_t writes;
+ u_int32_t cache_hits;
+ u_int32_t allocated_dquots;
+ u_int32_t free_dquots;
+ u_int32_t syncs;
+ u_int32_t version;
+};
+
+#ifndef Q_V2_GETQUOTA
+#define Q_V2_GETQUOTA 0x0D00
+#endif
+#ifndef Q_V2_SETQUOTA
+#define Q_V2_SETQUOTA 0x0E00
+#endif
+
+#endif /* _QUOTAIO_LINUX_V2 */
+
+#ifndef QUOTABLOCK_SIZE
+#define QUOTABLOCK_SIZE 1024
+#endif
+
+#endif /* _SAMBA_LINUX_QUOTA_H_ */
diff --git a/source/include/samba_xfs_quota.h b/source/include/samba_xfs_quota.h
new file mode 100644
index 00000000000..1db435064a6
--- /dev/null
+++ b/source/include/samba_xfs_quota.h
@@ -0,0 +1,165 @@
+#ifndef _SAMBA_LINUX_XFS_H_
+#define _SAMBA_LINUX_XFS_H_
+
+#ifndef _QUOTAIO_LINUX_XFS
+#define _QUOTAIO_LINUX_XFS
+
+/*
+ * Copyright (c) 2000 Silicon Graphics, Inc. All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it would be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * Further, this software is distributed without any warranty that it is
+ * free of the rightful claim of any third person regarding infringement
+ * or the like. Any license provided herein, whether implied or
+ * otherwise, applies only to this software file. Patent licenses, if
+ * any, provided herein do not apply to combinations of this program with
+ * other software, or any other product whatsoever.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write the Free Software Foundation, Inc., 59
+ * Temple Place - Suite 330, Boston MA 02111-1307, USA.
+ *
+ * Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy,
+ * Mountain View, CA 94043, or:
+ *
+ * http://www.sgi.com
+ *
+ * For further information regarding this notice, see:
+ *
+ * http://oss.sgi.com/projects/GenInfo/SGIGPLNoticeExplan/
+ */
+
+#include <linux/types.h>
+
+#define XQM_CMD(cmd) ( ('X'<<8)+(cmd) )
+#define IS_XQM_CMD(cmd) ( ((int)(cmd)>>8) == 'X' )
+
+/*
+ * Disk quota - quotactl(2) commands for XFS Quota Manager (XQM).
+ */
+#define Q_XQUOTAON XQM_CMD(0x1) /* enable quota accounting/enforcement */
+#define Q_XQUOTAOFF XQM_CMD(0x2) /* disable quota accounting/enforcement */
+#define Q_XGETQUOTA XQM_CMD(0x3) /* get disk limits & usage */
+#define Q_XSETQLIM XQM_CMD(0x4) /* set disk limits only */
+#define Q_XGETQSTAT XQM_CMD(0x5) /* returns fs_quota_stat_t struct */
+#define Q_XQUOTARM XQM_CMD(0x6) /* free quota files' space */
+
+/*
+ * fs_disk_quota structure:
+ *
+ * This contains the current quota information regarding a user/proj/group.
+ * It is 64-bit aligned, and all the blk units are in BBs (Basic Blocks) of
+ * 512 bytes.
+ */
+#define FS_DQUOT_VERSION 1 /* fs_disk_quota.d_version */
+typedef struct fs_disk_quota {
+ u_int8_t d_version; /* version of this structure */
+ u_int8_t d_flags; /* XFS_{USER,PROJ,GROUP}_QUOTA */
+ u_int16_t d_fieldmask; /* field specifier */
+ u_int32_t d_id; /* user, project, or group ID */
+ u_int64_t d_blk_hardlimit; /* absolute limit on disk blks */
+ u_int64_t d_blk_softlimit; /* preferred limit on disk blks */
+ u_int64_t d_ino_hardlimit; /* maximum # allocated inodes */
+ u_int64_t d_ino_softlimit; /* preferred inode limit */
+ u_int64_t d_bcount; /* # disk blocks owned by the user */
+ u_int64_t d_icount; /* # inodes owned by the user */
+ int32_t d_itimer; /* zero if within inode limits */
+ /* if not, we refuse service */
+ int32_t d_btimer; /* similar to above; for disk blocks */
+ u_int16_t d_iwarns; /* # warnings issued wrt num inodes */
+ u_int16_t d_bwarns; /* # warnings issued wrt disk blocks */
+ int32_t d_padding2; /* padding2 - for future use */
+ u_int64_t d_rtb_hardlimit; /* absolute limit on realtime blks */
+ u_int64_t d_rtb_softlimit; /* preferred limit on RT disk blks */
+ u_int64_t d_rtbcount; /* # realtime blocks owned */
+ int32_t d_rtbtimer; /* similar to above; for RT disk blks */
+ u_int16_t d_rtbwarns; /* # warnings issued wrt RT disk blks */
+ int16_t d_padding3; /* padding3 - for future use */
+ char d_padding4[8]; /* yet more padding */
+} fs_disk_quota_t;
+
+/*
+ * These fields are sent to Q_XSETQLIM to specify fields that need to change.
+ */
+#define FS_DQ_ISOFT (1<<0)
+#define FS_DQ_IHARD (1<<1)
+#define FS_DQ_BSOFT (1<<2)
+#define FS_DQ_BHARD (1<<3)
+#define FS_DQ_RTBSOFT (1<<4)
+#define FS_DQ_RTBHARD (1<<5)
+#define FS_DQ_LIMIT_MASK (FS_DQ_ISOFT | FS_DQ_IHARD | FS_DQ_BSOFT | \
+ FS_DQ_BHARD | FS_DQ_RTBSOFT | FS_DQ_RTBHARD)
+/*
+ * These timers can only be set in super user's dquot. For others, timers are
+ * automatically started and stopped. Superusers timer values set the limits
+ * for the rest. In case these values are zero, the DQ_{F,B}TIMELIMIT values
+ * defined below are used.
+ * These values also apply only to the d_fieldmask field for Q_XSETQLIM.
+ */
+#define FS_DQ_BTIMER (1<<6)
+#define FS_DQ_ITIMER (1<<7)
+#define FS_DQ_RTBTIMER (1<<8)
+#define FS_DQ_TIMER_MASK (FS_DQ_BTIMER | FS_DQ_ITIMER | FS_DQ_RTBTIMER)
+
+/*
+ * The following constants define the default amount of time given a user
+ * before the soft limits are treated as hard limits (usually resulting
+ * in an allocation failure). These may be modified by the quotactl(2)
+ * system call with the Q_XSETQLIM command.
+ */
+#define DQ_FTIMELIMIT (7 * 24*60*60) /* 1 week */
+#define DQ_BTIMELIMIT (7 * 24*60*60) /* 1 week */
+
+/*
+ * Various flags related to quotactl(2). Only relevant to XFS filesystems.
+ */
+#define XFS_QUOTA_UDQ_ACCT (1<<0) /* user quota accounting */
+#define XFS_QUOTA_UDQ_ENFD (1<<1) /* user quota limits enforcement */
+#define XFS_QUOTA_GDQ_ACCT (1<<2) /* group quota accounting */
+#define XFS_QUOTA_GDQ_ENFD (1<<3) /* group quota limits enforcement */
+
+#define XFS_USER_QUOTA (1<<0) /* user quota type */
+#define XFS_PROJ_QUOTA (1<<1) /* (IRIX) project quota type */
+#define XFS_GROUP_QUOTA (1<<2) /* group quota type */
+
+/*
+ * fs_quota_stat is the struct returned in Q_XGETQSTAT for a given file system.
+ * Provides a centralized way to get meta infomation about the quota subsystem.
+ * eg. space taken up for user and group quotas, number of dquots currently
+ * incore.
+ */
+#define FS_QSTAT_VERSION 1 /* fs_quota_stat.qs_version */
+
+/*
+ * Some basic infomation about 'quota files'.
+ */
+typedef struct fs_qfilestat {
+ u_int64_t qfs_ino; /* inode number */
+ u_int64_t qfs_nblks; /* number of BBs 512-byte-blks */
+ u_int32_t qfs_nextents; /* number of extents */
+} fs_qfilestat_t;
+
+typedef struct fs_quota_stat {
+ u_int8_t qs_version; /* version number for future changes */
+ u_int16_t qs_flags; /* XFS_QUOTA_{U,P,G}DQ_{ACCT,ENFD} */
+ u_int8_t qs_pad; /* unused */
+ fs_qfilestat_t qs_uquota; /* user quota storage information */
+ fs_qfilestat_t qs_gquota; /* group quota storage information */
+ u_int32_t qs_incoredqs; /* number of dquots incore */
+ int32_t qs_btimelimit; /* limit for blks timer */
+ int32_t qs_itimelimit; /* limit for inodes timer */
+ int32_t qs_rtbtimelimit; /* limit for rt blks timer */
+ u_int16_t qs_bwarnlimit; /* limit for num warnings */
+ u_int16_t qs_iwarnlimit; /* limit for num warnings */
+} fs_quota_stat_t;
+
+#endif /* _QUOTAIO_LINUX_XFS */
+
+#endif /* _SAMBA_LINUX_XFS_H_ */
diff --git a/source/include/secrets.h b/source/include/secrets.h
new file mode 100644
index 00000000000..8c393940586
--- /dev/null
+++ b/source/include/secrets.h
@@ -0,0 +1,105 @@
+/*
+ * 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 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 _SECRETS_H
+#define _SECRETS_H
+
+/* the first one is for the hashed password (NT4 style) the latter
+ for plaintext (ADS)
+*/
+#define SECRETS_MACHINE_ACCT_PASS "SECRETS/$MACHINE.ACC"
+#define SECRETS_MACHINE_PASSWORD "SECRETS/MACHINE_PASSWORD"
+#define SECRETS_MACHINE_LAST_CHANGE_TIME "SECRETS/MACHINE_LAST_CHANGE_TIME"
+#define SECRETS_MACHINE_SEC_CHANNEL_TYPE "SECRETS/MACHINE_SEC_CHANNEL_TYPE"
+#define SECRETS_MACHINE_TRUST_ACCOUNT_NAME "SECRETS/SECRETS_MACHINE_TRUST_ACCOUNT_NAME"
+/* this one is for storing trusted domain account password */
+#define SECRETS_DOMTRUST_ACCT_PASS "SECRETS/$DOMTRUST.ACC"
+
+/* The domain sid and our sid are stored here even though they aren't
+ really secret. */
+#define SECRETS_DOMAIN_SID "SECRETS/SID"
+#define SECRETS_SAM_SID "SAM/SID"
+
+/* The domain GUID and server GUID (NOT the same) are also not secret */
+#define SECRETS_DOMAIN_GUID "SECRETS/DOMGUID"
+#define SECRETS_SERVER_GUID "SECRETS/GUID"
+
+#define SECRETS_LDAP_BIND_PW "SECRETS/LDAP_BIND_PW"
+
+/* Authenticated user info is stored in secrets.tdb under these keys */
+
+#define SECRETS_AUTH_USER "SECRETS/AUTH_USER"
+#define SECRETS_AUTH_DOMAIN "SECRETS/AUTH_DOMAIN"
+#define SECRETS_AUTH_PASSWORD "SECRETS/AUTH_PASSWORD"
+
+/* Trust password type flags */
+#define PASS_MACHINE_TRUST_NT 0x0001
+#define PASS_SERVER_TRUST_NT 0x0002
+#define PASS_DOMAIN_TRUST_NT 0x0004
+#define PASS_MACHINE_TRUST_ADS 0x0008
+#define PASS_DOMAIN_TRUST_ADS 0x0010
+
+/* structure for storing machine account password
+ (ie. when samba server is member of a domain */
+struct machine_acct_pass {
+ uint8 hash[16];
+ time_t mod_time;
+};
+
+/*
+ * storage structure for trusted domain
+ */
+typedef struct trusted_dom_pass {
+ size_t uni_name_len;
+ smb_ucs2_t uni_name[32]; /* unicode domain name */
+ size_t pass_len;
+ fstring pass; /* trust relationship's password */
+ time_t mod_time;
+ DOM_SID domain_sid; /* remote domain's sid */
+} TRUSTED_DOM_PASS;
+
+/*
+ * trusted domain entry/entries returned by secrets_get_trusted_domains
+ * (used in _lsa_enum_trust_dom call)
+ */
+typedef struct trustdom {
+ smb_ucs2_t *name;
+ DOM_SID sid;
+} TRUSTDOM;
+
+/*
+ * Format of an OpenAFS keyfile
+ */
+
+#define SECRETS_AFS_MAXKEYS 8
+
+struct afs_key {
+ uint32 kvno;
+ char key[8];
+};
+
+struct afs_keyfile {
+ uint32 nkeys;
+ struct afs_key entry[SECRETS_AFS_MAXKEYS];
+};
+
+#define SECRETS_AFS_KEYFILE "SECRETS/AFS_KEYFILE"
+
+#endif /* _SECRETS_H */
diff --git a/source/include/session.h b/source/include/session.h
new file mode 100644
index 00000000000..f613afee09a
--- /dev/null
+++ b/source/include/session.h
@@ -0,0 +1,40 @@
+/*
+ Unix SMB/CIFS implementation.
+ session handling for recording currently vailid vuids
+ Copyright (C) tridge@samba.org 2001
+ Copyright (C) Andew Bartlett <abartlet@samba.org> 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 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.
+*/
+
+/* a "session" is claimed when we do a SessionSetupX operation
+ and is yielded when the corresponding vuid is destroyed.
+
+ sessions are used to populate utmp and PAM session structures
+*/
+
+struct sessionid {
+ uid_t uid;
+ gid_t gid;
+ fstring username;
+ fstring hostname;
+ fstring netbios_name;
+ fstring remote_machine;
+ fstring id_str;
+ uint32 id_num;
+ uint32 pid;
+ fstring ip_addr;
+};
+
diff --git a/source/include/smb.h b/source/include/smb.h
new file mode 100644
index 00000000000..6de50c8afa1
--- /dev/null
+++ b/source/include/smb.h
@@ -0,0 +1,1679 @@
+/*
+ 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 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 _SMB_H
+#define _SMB_H
+
+#if defined(LARGE_SMB_OFF_T)
+#define BUFFER_SIZE (128*1024)
+#else /* no large readwrite possible */
+#define BUFFER_SIZE (0xFFFF)
+#endif
+
+#define SAFETY_MARGIN 1024
+#define LARGE_WRITEX_HDR_SIZE 65
+
+#define NMB_PORT 137
+#define DGRAM_PORT 138
+#define SMB_PORT1 445
+#define SMB_PORT2 139
+#define SMB_PORTS "445 139"
+
+#define Undefined (-1)
+#define False (0)
+#define True (1)
+#define Auto (2)
+#define Required (3)
+
+#ifndef _BOOL
+typedef int BOOL;
+#define _BOOL /* So we don't typedef BOOL again in vfs.h */
+#endif
+
+/* limiting size of ipc replies */
+#define REALLOC(ptr,size) Realloc(ptr,MAX((size),4*1024))
+
+#define SIZEOFWORD 2
+
+#ifndef DEF_CREATE_MASK
+#define DEF_CREATE_MASK (0755)
+#endif
+
+/* string manipulation flags - see clistr.c and srvstr.c */
+#define STR_TERMINATE 1
+#define STR_UPPER 2
+#define STR_ASCII 4
+#define STR_UNICODE 8
+#define STR_NOALIGN 16
+#define STR_TERMINATE_ASCII 128
+
+/* how long to wait for secondary SMB packets (milli-seconds) */
+#define SMB_SECONDARY_WAIT (60*1000)
+
+/* Debugging stuff */
+#include "debug.h"
+
+/* this defines the error codes that receive_smb can put in smb_read_error */
+#define READ_TIMEOUT 1
+#define READ_EOF 2
+#define READ_ERROR 3
+#define WRITE_ERROR 4 /* This error code can go into the client smb_rw_error. */
+#define READ_BAD_SIG 5
+
+#define DIR_STRUCT_SIZE 43
+
+/* these define the attribute byte as seen by DOS */
+#define aRONLY (1L<<0) /* 0x01 */
+#define aHIDDEN (1L<<1) /* 0x02 */
+#define aSYSTEM (1L<<2) /* 0x04 */
+#define aVOLID (1L<<3) /* 0x08 */
+#define aDIR (1L<<4) /* 0x10 */
+#define aARCH (1L<<5) /* 0x20 */
+
+/* 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
+
+/* define shifts and masks for share and open modes. */
+#define OPEN_MODE_MASK 0xF
+#define SHARE_MODE_SHIFT 4
+#define SHARE_MODE_MASK 0x7
+#define GET_OPEN_MODE(x) ((x) & OPEN_MODE_MASK)
+#define SET_OPEN_MODE(x) ((x) & OPEN_MODE_MASK)
+#define GET_DENY_MODE(x) (((x)>>SHARE_MODE_SHIFT) & SHARE_MODE_MASK)
+#define SET_DENY_MODE(x) (((x) & SHARE_MODE_MASK) <<SHARE_MODE_SHIFT)
+
+/* Sync on open file (not sure if used anymore... ?) */
+#define FILE_SYNC_OPENMODE (1<<14)
+#define GET_FILE_SYNC_OPENMODE(x) (((x) & FILE_SYNC_OPENMODE) ? True : False)
+
+/* allow delete on open file mode (used by NT SMB's). */
+#define ALLOW_SHARE_DELETE (1<<15)
+#define GET_ALLOW_SHARE_DELETE(x) (((x) & ALLOW_SHARE_DELETE) ? True : False)
+#define SET_ALLOW_SHARE_DELETE(x) ((x) ? ALLOW_SHARE_DELETE : 0)
+
+/* delete on close flag (used by NT SMB's). */
+#define DELETE_ON_CLOSE_FLAG (1<<16)
+#define GET_DELETE_ON_CLOSE_FLAG(x) (((x) & DELETE_ON_CLOSE_FLAG) ? True : False)
+#define SET_DELETE_ON_CLOSE_FLAG(x) ((x) ? DELETE_ON_CLOSE_FLAG : 0)
+
+/* open disposition values */
+#define FILE_EXISTS_FAIL 0
+#define FILE_EXISTS_OPEN 1
+#define FILE_EXISTS_TRUNCATE 2
+
+/* mask for open disposition. */
+#define FILE_OPEN_MASK 0x3
+
+#define GET_FILE_OPEN_DISPOSITION(x) ((x) & FILE_OPEN_MASK)
+#define SET_FILE_OPEN_DISPOSITION(x) ((x) & FILE_OPEN_MASK)
+
+/* The above can be OR'ed with... */
+#define FILE_CREATE_IF_NOT_EXIST 0x10
+#define FILE_FAIL_IF_NOT_EXIST 0
+
+#define GET_FILE_CREATE_DISPOSITION(x) ((x) & (FILE_CREATE_IF_NOT_EXIST|FILE_FAIL_IF_NOT_EXIST))
+
+/* share types */
+#define STYPE_DISKTREE 0 /* Disk drive */
+#define STYPE_PRINTQ 1 /* Spooler queue */
+#define STYPE_DEVICE 2 /* Serial device */
+#define STYPE_IPC 3 /* Interprocess communication (IPC) */
+#define STYPE_HIDDEN 0x80000000 /* share is a hidden one (ends with $) */
+
+#include "doserr.h"
+
+typedef union unid_t {
+ uid_t uid;
+ gid_t gid;
+} unid_t;
+
+/*
+ * SMB UCS2 (16-bit unicode) internal type.
+ */
+
+typedef uint16 smb_ucs2_t;
+
+/* ucs2 string types. */
+typedef smb_ucs2_t wpstring[PSTRING_LEN];
+typedef smb_ucs2_t wfstring[FSTRING_LEN];
+
+#ifdef WORDS_BIGENDIAN
+#define UCS2_SHIFT 8
+#else
+#define UCS2_SHIFT 0
+#endif
+
+/* turn a 7 bit character into a ucs2 character */
+#define UCS2_CHAR(c) ((c) << UCS2_SHIFT)
+
+/* pipe string names */
+#define PIPE_LANMAN "\\PIPE\\LANMAN"
+#define PIPE_SRVSVC "\\PIPE\\srvsvc"
+#define PIPE_SAMR "\\PIPE\\samr"
+#define PIPE_WINREG "\\PIPE\\winreg"
+#define PIPE_WKSSVC "\\PIPE\\wkssvc"
+#define PIPE_NETLOGON "\\PIPE\\NETLOGON"
+#define PIPE_NTLSA "\\PIPE\\ntlsa"
+#define PIPE_NTSVCS "\\PIPE\\ntsvcs"
+#define PIPE_LSASS "\\PIPE\\lsass"
+#define PIPE_LSARPC "\\PIPE\\lsarpc"
+#define PIPE_SPOOLSS "\\PIPE\\spoolss"
+#define PIPE_NETDFS "\\PIPE\\netdfs"
+#define PIPE_ECHO "\\PIPE\\rpcecho"
+#define PIPE_SHUTDOWN "\\PIPE\\initshutdown"
+#define PIPE_EPM "\\PIPE\\epmapper"
+
+#define PIPE_NETLOGON_PLAIN "\\NETLOGON"
+
+#define PI_LSARPC 0
+#define PI_LSARPC_DS 1
+#define PI_SAMR 2
+#define PI_NETLOGON 3
+#define PI_SRVSVC 4
+#define PI_WKSSVC 5
+#define PI_WINREG 6
+#define PI_SPOOLSS 7
+#define PI_NETDFS 8
+#define PI_ECHO 9
+#define PI_SHUTDOWN 10
+#define PI_EPM 11
+#define PI_MAX_PIPES 12
+
+/* 64 bit time (100usec) since ????? - cifs6.txt, section 3.5, page 30 */
+typedef struct nttime_info
+{
+ uint32 low;
+ uint32 high;
+} NTTIME;
+
+
+/* Allowable account control bits */
+#define ACB_DISABLED 0x0001 /* 1 = User account disabled */
+#define ACB_HOMDIRREQ 0x0002 /* 1 = Home directory required */
+#define ACB_PWNOTREQ 0x0004 /* 1 = User password not required */
+#define ACB_TEMPDUP 0x0008 /* 1 = Temporary duplicate account */
+#define ACB_NORMAL 0x0010 /* 1 = Normal user account */
+#define ACB_MNS 0x0020 /* 1 = MNS logon user account */
+#define ACB_DOMTRUST 0x0040 /* 1 = Interdomain trust account */
+#define ACB_WSTRUST 0x0080 /* 1 = Workstation trust account */
+#define ACB_SVRTRUST 0x0100 /* 1 = Server trust account */
+#define ACB_PWNOEXP 0x0200 /* 1 = User password does not expire */
+#define ACB_AUTOLOCK 0x0400 /* 1 = Account auto locked */
+
+#define MAX_HOURS_LEN 32
+
+/*
+ * window during which we must talk to the PDC to avoid
+ * sam sync delays; expressed in seconds (15 minutes is the
+ * default period for SAM replication under Windows NT 4.0
+ */
+#define SAM_SYNC_WINDOW 900
+
+
+#ifndef MAXSUBAUTHS
+#define MAXSUBAUTHS 15 /* max sub authorities in a SID */
+#endif
+
+#define SID_MAX_SIZE ((size_t)(8+(MAXSUBAUTHS*4)))
+
+/* SID Types */
+enum SID_NAME_USE
+{
+ SID_NAME_USE_NONE = 0,
+ SID_NAME_USER = 1, /* user */
+ SID_NAME_DOM_GRP, /* domain group */
+ SID_NAME_DOMAIN, /* domain sid */
+ SID_NAME_ALIAS, /* local group */
+ SID_NAME_WKN_GRP, /* well-known group */
+ SID_NAME_DELETED, /* deleted account: needed for c2 rating */
+ SID_NAME_INVALID, /* invalid account */
+ SID_NAME_UNKNOWN, /* unknown sid type */
+ SID_NAME_COMPUTER /* sid for a computer */
+};
+
+/**
+ * @brief Security Identifier
+ *
+ * @sa http://msdn.microsoft.com/library/default.asp?url=/library/en-us/security/accctrl_38yn.asp
+ **/
+typedef struct sid_info
+{
+ uint8 sid_rev_num; /**< SID revision number */
+ uint8 num_auths; /**< Number of sub-authorities */
+ uint8 id_auth[6]; /**< Identifier Authority */
+ /*
+ * Pointer to sub-authorities.
+ *
+ * @note The values in these uint32's are in *native* byteorder, not
+ * neccessarily little-endian...... JRA.
+ */
+ uint32 sub_auths[MAXSUBAUTHS];
+
+} DOM_SID;
+
+/*
+ * The complete list of SIDS belonging to this user.
+ * Created when a vuid is registered.
+ * The definition of the user_sids array is as follows :
+ *
+ * token->user_sids[0] = primary user SID.
+ * token->user_sids[1] = primary group SID.
+ * token->user_sids[2..num_sids] = supplementary group SIDS.
+ */
+
+#define PRIMARY_USER_SID_INDEX 0
+#define PRIMARY_GROUP_SID_INDEX 1
+
+typedef struct _nt_user_token {
+ size_t num_sids;
+ DOM_SID *user_sids;
+} NT_USER_TOKEN;
+
+/*** query a local group, get a list of these: shows who is in that group ***/
+
+/* local group member info */
+typedef struct local_grp_member_info
+{
+ DOM_SID sid ; /* matches with name */
+ uint8 sid_use; /* usr=1 grp=2 dom=3 alias=4 wkng=5 del=6 inv=7 unk=8 */
+ fstring name ; /* matches with sid: must be of the form "DOMAIN\account" */
+
+} LOCAL_GRP_MEMBER;
+
+/* enumerate these to get list of local groups */
+
+/* local group info */
+typedef struct local_grp_info
+{
+ fstring name;
+ fstring comment;
+
+} LOCAL_GRP;
+
+/*** enumerate these to get list of domain groups ***/
+
+/* domain group member info */
+typedef struct domain_grp_info
+{
+ fstring name;
+ fstring comment;
+ uint32 rid; /* group rid */
+ uint8 attr; /* attributes forced to be set to 0x7: SE_GROUP_xxx */
+
+} DOMAIN_GRP;
+
+/*** query a domain group, get a list of these: shows who is in that group ***/
+
+/* domain group info */
+typedef struct domain_grp_member_info
+{
+ fstring name;
+ uint8 attr; /* attributes forced to be set to 0x7: SE_GROUP_xxx */
+
+} DOMAIN_GRP_MEMBER;
+
+/* 32 bit time (sec) since 01jan1970 - cifs6.txt, section 3.5, page 30 */
+typedef struct time_info
+{
+ uint32 time;
+} UTIME;
+
+/* Structure used when SMBwritebmpx is active */
+typedef struct
+{
+ size_t wr_total_written; /* So we know when to discard this */
+ int32 wr_timeout;
+ int32 wr_errclass;
+ int32 wr_error; /* Cached errors */
+ BOOL wr_mode; /* write through mode) */
+ BOOL wr_discard; /* discard all further data */
+} write_bmpx_struct;
+
+typedef struct write_cache
+{
+ SMB_OFF_T file_size;
+ SMB_OFF_T offset;
+ size_t alloc_size;
+ size_t data_size;
+ char *data;
+} write_cache;
+
+typedef struct
+{
+ smb_ucs2_t *origname;
+ smb_ucs2_t *filename;
+ SMB_STRUCT_STAT *statinfo;
+} smb_filename;
+
+#include "fake_file.h"
+
+typedef struct files_struct
+{
+ struct files_struct *next, *prev;
+ int fnum;
+ struct connection_struct *conn;
+ int fd;
+ uint16 rap_print_jobid;
+ SMB_DEV_T dev;
+ SMB_INO_T inode;
+ BOOL delete_on_close;
+ SMB_OFF_T pos;
+ SMB_BIG_UINT size;
+ SMB_BIG_UINT initial_allocation_size; /* Faked up initial allocation on disk. */
+ SMB_BIG_UINT position_information;
+ mode_t mode;
+ uint16 file_pid;
+ uint16 vuid;
+ write_bmpx_struct *wbmpx_ptr;
+ write_cache *wcp;
+ struct timeval open_time;
+ int share_mode;
+ uint32 desired_access;
+ time_t pending_modtime;
+ int oplock_type;
+ int sent_oplock_break;
+ unsigned long file_id;
+ BOOL can_lock;
+ BOOL can_read;
+ BOOL can_write;
+ BOOL print_file;
+ BOOL modified;
+ BOOL is_directory;
+ BOOL is_stat;
+ BOOL directory_delete_on_close;
+ char *fsp_name;
+ FAKE_FILE_HANDLE *fake_file_handle;
+} files_struct;
+
+#include "ntquotas.h"
+#include "sysquotas.h"
+
+/* used to hold an arbitrary blob of data */
+typedef struct data_blob
+{
+ uint8 *data;
+ size_t length;
+ void (*free)(struct data_blob *data_blob);
+} DATA_BLOB;
+
+/*
+ * Structure used to keep directory state information around.
+ * Used in NT change-notify code.
+ */
+
+typedef struct
+{
+ time_t modify_time;
+ time_t status_time;
+} dir_status_struct;
+
+struct vuid_cache_entry
+{
+ uint16 vuid;
+ BOOL read_only;
+ BOOL admin_user;
+};
+
+struct vuid_cache
+{
+ unsigned int entries;
+ struct vuid_cache_entry array[VUID_CACHE_SIZE];
+};
+
+typedef struct
+{
+ char *name;
+ BOOL is_wild;
+} name_compare_entry;
+
+/* Include VFS stuff */
+
+#include "smb_acls.h"
+#include "vfs.h"
+
+typedef struct connection_struct
+{
+ struct connection_struct *next, *prev;
+ TALLOC_CTX *mem_ctx;
+ unsigned cnum; /* an index passed over the wire */
+ int service;
+ BOOL force_user;
+ BOOL force_group;
+ struct vuid_cache vuid_cache;
+ void *dirptr;
+ BOOL printer;
+ BOOL ipc;
+ BOOL read_only; /* Attributes for the current user of the share. */
+ BOOL admin_user; /* Attributes for the current user of the share. */
+ char *dirpath;
+ char *connectpath;
+ char *origpath;
+
+ struct vfs_ops vfs; /* Filesystem operations */
+ struct vfs_ops vfs_opaque; /* OPAQUE Filesystem operations */
+ struct vfs_handle_struct *vfs_handles; /* for the new plugins */
+
+ char *user; /* name of user who *opened* this connection */
+ uid_t uid; /* uid of user who *opened* this connection */
+ gid_t gid; /* gid of user who *opened* this connection */
+ char client_address[18]; /* String version of client IP address. */
+
+ uint16 vuid; /* vuid of user who *opened* this connection, or UID_FIELD_INVALID */
+
+ /* following groups stuff added by ih */
+
+ /* This groups info is valid for the user that *opened* the connection */
+ int ngroups;
+ gid_t *groups;
+ NT_USER_TOKEN *nt_user_token;
+ PRIVILEGE_SET *privs;
+
+ time_t lastused;
+ BOOL used;
+ int num_files_open;
+ name_compare_entry *hide_list; /* Per-share list of files to return as hidden. */
+ name_compare_entry *veto_list; /* Per-share list of files to veto (never show). */
+ name_compare_entry *veto_oplock_list; /* Per-share list of files to refuse oplocks on. */
+
+} connection_struct;
+
+struct current_user
+{
+ connection_struct *conn;
+ uint16 vuid;
+ uid_t uid;
+ gid_t gid;
+ int ngroups;
+ gid_t *groups;
+ NT_USER_TOKEN *nt_user_token;
+ PRIVILEGE_SET *privs;
+};
+
+/* Defines for the sent_oplock_break field above. */
+#define NO_BREAK_SENT 0
+#define EXCLUSIVE_BREAK_SENT 1
+#define LEVEL_II_BREAK_SENT 2
+
+typedef struct {
+ fstring smb_name; /* user name from the client */
+ fstring unix_name; /* unix user name of a validated user */
+ fstring full_name; /* to store full name (such as "Joe Bloggs") from gecos field of password file */
+ fstring domain; /* domain that the client specified */
+} userdom_struct;
+
+/* Extra fields above "LPQ_PRINTING" are used to map extra NT status codes. */
+
+enum {LPQ_QUEUED=0,LPQ_PAUSED,LPQ_SPOOLING,LPQ_PRINTING,LPQ_ERROR,LPQ_DELETING,
+ LPQ_OFFLINE,LPQ_PAPEROUT,LPQ_PRINTED,LPQ_DELETED,LPQ_BLOCKED,LPQ_USER_INTERVENTION};
+
+typedef struct _print_queue_struct
+{
+ int job; /* normally the SMB jobid -- see note in
+ printing.c:traverse_fn_delete() */
+ int size;
+ int page_count;
+ int status;
+ int priority;
+ time_t time;
+ fstring fs_user;
+ fstring fs_file;
+} print_queue_struct;
+
+enum {LPSTAT_OK, LPSTAT_STOPPED, LPSTAT_ERROR};
+
+typedef struct
+{
+ fstring message;
+ int qcount;
+ int status;
+} print_status_struct;
+
+/* used for server information: client, nameserv and ipc */
+struct server_info_struct
+{
+ fstring name;
+ uint32 type;
+ fstring comment;
+ fstring domain; /* used ONLY in ipc.c NOT namework.c */
+ BOOL server_added; /* used ONLY in ipc.c NOT namework.c */
+};
+
+
+/* used for network interfaces */
+struct interface
+{
+ struct interface *next, *prev;
+ struct in_addr ip;
+ struct in_addr bcast;
+ struct in_addr nmask;
+};
+
+/* struct returned by get_share_modes */
+typedef struct {
+ pid_t pid;
+ uint16 op_port;
+ uint16 op_type;
+ int share_mode;
+ uint32 desired_access;
+ struct timeval time;
+ SMB_DEV_T dev;
+ SMB_INO_T inode;
+ unsigned long share_file_id;
+} share_mode_entry;
+
+
+#define SHAREMODE_FN_CAST() \
+ void (*)(share_mode_entry *, char*)
+
+#define SHAREMODE_FN(fn) \
+ void (*fn)(share_mode_entry *, char*)
+
+#define NT_HASH_LEN 16
+#define LM_HASH_LEN 16
+
+/*
+ * Flags for account policy.
+ */
+#define AP_MIN_PASSWORD_LEN 1
+#define AP_PASSWORD_HISTORY 2
+#define AP_USER_MUST_LOGON_TO_CHG_PASS 3
+#define AP_MAX_PASSWORD_AGE 4
+#define AP_MIN_PASSWORD_AGE 5
+#define AP_LOCK_ACCOUNT_DURATION 6
+#define AP_RESET_COUNT_TIME 7
+#define AP_BAD_ATTEMPT_LOCKOUT 8
+#define AP_TIME_TO_LOGOUT 9
+
+
+/*
+ * Flags for local user manipulation.
+ */
+
+#define LOCAL_ADD_USER 0x1
+#define LOCAL_DELETE_USER 0x2
+#define LOCAL_DISABLE_USER 0x4
+#define LOCAL_ENABLE_USER 0x8
+#define LOCAL_TRUST_ACCOUNT 0x10
+#define LOCAL_SET_NO_PASSWORD 0x20
+#define LOCAL_SET_PASSWORD 0x40
+#define LOCAL_SET_LDAP_ADMIN_PW 0x80
+#define LOCAL_INTERDOM_ACCOUNT 0x100
+#define LOCAL_AM_ROOT 0x200 /* Act as root */
+
+/* key and data in the connections database - used in smbstatus and smbd */
+struct connections_key {
+ pid_t pid;
+ int cnum;
+ fstring name;
+};
+
+struct connections_data {
+ int magic;
+ pid_t pid;
+ int cnum;
+ uid_t uid;
+ gid_t gid;
+ char name[24];
+ char addr[24];
+ char machine[FSTRING_LEN];
+ time_t start;
+ uint32 bcast_msg_flags;
+};
+
+
+/* key and data records in the tdb locking database */
+struct locking_key {
+ SMB_DEV_T dev;
+ SMB_INO_T inode;
+};
+
+struct locking_data {
+ union {
+ int num_share_mode_entries;
+ share_mode_entry dummy; /* Needed for alignment. */
+ } u;
+ /* the following two entries are implicit
+ share_mode_entry modes[num_share_mode_entries];
+ char file_name[];
+ */
+};
+
+
+/* the following are used by loadparm for option lists */
+typedef enum
+{
+ P_BOOL,P_BOOLREV,P_CHAR,P_INTEGER,P_OCTAL,P_LIST,
+ P_STRING,P_USTRING,P_GSTRING,P_UGSTRING,P_ENUM,P_SEP
+} parm_type;
+
+typedef enum
+{
+ P_LOCAL,P_GLOBAL,P_SEPARATOR,P_NONE
+} parm_class;
+
+/* passed to br lock code */
+enum brl_type {READ_LOCK, WRITE_LOCK, PENDING_LOCK};
+
+struct enum_list {
+ int value;
+ const char *name;
+};
+
+#define BRLOCK_FN_CAST() \
+ void (*)(SMB_DEV_T dev, SMB_INO_T ino, int pid, \
+ enum brl_type lock_type, \
+ br_off start, br_off size)
+#define BRLOCK_FN(fn) \
+ void (*fn)(SMB_DEV_T dev, SMB_INO_T ino, int pid, \
+ enum brl_type lock_type, \
+ br_off start, br_off size)
+struct parm_struct
+{
+ const char *label;
+ parm_type type;
+ parm_class class;
+ void *ptr;
+ BOOL (*special)(const char *, char **);
+ const struct enum_list *enum_list;
+ unsigned flags;
+ union {
+ BOOL bvalue;
+ int ivalue;
+ char *svalue;
+ char cvalue;
+ char **lvalue;
+ } def;
+};
+
+struct bitmap {
+ uint32 *b;
+ unsigned int n;
+};
+
+/* The following flags are used in SWAT */
+#define FLAG_BASIC 0x0001 /* Display only in BASIC view */
+#define FLAG_SHARE 0x0002 /* file sharing options */
+#define FLAG_PRINT 0x0004 /* printing options */
+#define FLAG_GLOBAL 0x0008 /* local options that should be globally settable in SWAT */
+#define FLAG_WIZARD 0x0010 /* Parameters that the wizard will operate on */
+#define FLAG_ADVANCED 0x0020 /* Parameters that will be visible in advanced view */
+#define FLAG_DEVELOPER 0x0040 /* No longer used */
+#define FLAG_DEPRECATED 0x1000 /* options that should no longer be used */
+#define FLAG_HIDE 0x2000 /* options that should be hidden in SWAT */
+#define FLAG_DOS_STRING 0x4000 /* convert from UNIX to DOS codepage when reading this string. */
+
+#ifndef LOCKING_VERSION
+#define LOCKING_VERSION 4
+#endif /* LOCKING_VERSION */
+
+
+/* the basic packet size, assuming no words or bytes */
+#define smb_size 39
+
+/* offsets into message for common items */
+#define smb_com 8
+#define smb_rcls 9
+#define smb_reh 10
+#define smb_err 11
+#define smb_flg 13
+#define smb_flg2 14
+#define smb_pidhigh 16
+#define smb_ss_field 18
+#define smb_tid 28
+#define smb_pid 30
+#define smb_uid 32
+#define smb_mid 34
+#define smb_wct 36
+#define smb_vwv 37
+#define smb_vwv0 37
+#define smb_vwv1 39
+#define smb_vwv2 41
+#define smb_vwv3 43
+#define smb_vwv4 45
+#define smb_vwv5 47
+#define smb_vwv6 49
+#define smb_vwv7 51
+#define smb_vwv8 53
+#define smb_vwv9 55
+#define smb_vwv10 57
+#define smb_vwv11 59
+#define smb_vwv12 61
+#define smb_vwv13 63
+#define smb_vwv14 65
+#define smb_vwv15 67
+#define smb_vwv16 69
+#define smb_vwv17 71
+
+/* 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 /* Unlock a range then write */
+#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 */
+
+/* These are the trans subcommands */
+#define TRANSACT_SETNAMEDPIPEHANDLESTATE 0x01
+#define TRANSACT_DCERPCCMD 0x26
+#define TRANSACT_WAITNAMEDPIPEHANDLESTATE 0x53
+
+/* These are the TRANS2 sub commands */
+#define TRANSACT2_OPEN 0x00
+#define TRANSACT2_FINDFIRST 0x01
+#define TRANSACT2_FINDNEXT 0x02
+#define TRANSACT2_QFSINFO 0x03
+#define TRANSACT2_SETFSINFO 0x04
+#define TRANSACT2_QPATHINFO 0x05
+#define TRANSACT2_SETPATHINFO 0x06
+#define TRANSACT2_QFILEINFO 0x07
+#define TRANSACT2_SETFILEINFO 0x08
+#define TRANSACT2_FSCTL 0x09
+#define TRANSACT2_IOCTL 0x0A
+#define TRANSACT2_FINDNOTIFYFIRST 0x0B
+#define TRANSACT2_FINDNOTIFYNEXT 0x0C
+#define TRANSACT2_MKDIR 0x0D
+#define TRANSACT2_SESSION_SETUP 0x0E
+#define TRANSACT2_GET_DFS_REFERRAL 0x10
+#define TRANSACT2_REPORT_DFS_INCONSISTANCY 0x11
+
+/* 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
+#define NT_TRANSACT_GET_USER_QUOTA 7
+#define NT_TRANSACT_SET_USER_QUOTA 8
+
+/* These are the NT transact_get_user_quota sub commands */
+#define TRANSACT_GET_USER_QUOTA_LIST_CONTINUE 0x0000
+#define TRANSACT_GET_USER_QUOTA_LIST_START 0x0100
+#define TRANSACT_GET_USER_QUOTA_FOR_SID 0x0101
+
+/* Relevant IOCTL codes */
+#define IOCTL_QUERY_JOB_INFO 0x530060
+
+/* these are the trans2 sub fields for primary requests */
+#define smb_tpscnt smb_vwv0
+#define smb_tdscnt smb_vwv1
+#define smb_mprcnt smb_vwv2
+#define smb_mdrcnt smb_vwv3
+#define smb_msrcnt smb_vwv4
+#define smb_flags smb_vwv5
+#define smb_timeout smb_vwv6
+#define smb_pscnt smb_vwv9
+#define smb_psoff smb_vwv10
+#define smb_dscnt smb_vwv11
+#define smb_dsoff smb_vwv12
+#define smb_suwcnt smb_vwv13
+#define smb_setup smb_vwv14
+#define smb_setup0 smb_setup
+#define smb_setup1 (smb_setup+2)
+#define smb_setup2 (smb_setup+4)
+
+/* these are for the secondary requests */
+#define smb_spscnt smb_vwv2
+#define smb_spsoff smb_vwv3
+#define smb_spsdisp smb_vwv4
+#define smb_sdscnt smb_vwv5
+#define smb_sdsoff smb_vwv6
+#define smb_sdsdisp smb_vwv7
+#define smb_sfid smb_vwv8
+
+/* and these for responses */
+#define smb_tprcnt smb_vwv0
+#define smb_tdrcnt smb_vwv1
+#define smb_prcnt smb_vwv3
+#define smb_proff smb_vwv4
+#define smb_prdisp smb_vwv5
+#define smb_drcnt smb_vwv6
+#define smb_droff smb_vwv7
+#define smb_drdisp smb_vwv8
+
+/* these are for the NT trans primary request. */
+#define smb_nt_MaxSetupCount smb_vwv0
+#define smb_nt_Flags (smb_vwv0 + 1)
+#define smb_nt_TotalParameterCount (smb_vwv0 + 3)
+#define smb_nt_TotalDataCount (smb_vwv0 + 7)
+#define smb_nt_MaxParameterCount (smb_vwv0 + 11)
+#define smb_nt_MaxDataCount (smb_vwv0 + 15)
+#define smb_nt_ParameterCount (smb_vwv0 + 19)
+#define smb_nt_ParameterOffset (smb_vwv0 + 23)
+#define smb_nt_DataCount (smb_vwv0 + 27)
+#define smb_nt_DataOffset (smb_vwv0 + 31)
+#define smb_nt_SetupCount (smb_vwv0 + 35)
+#define smb_nt_Function (smb_vwv0 + 36)
+#define smb_nt_SetupStart (smb_vwv0 + 38)
+
+/* these are for the NT trans secondary request. */
+#define smb_nts_TotalParameterCount (smb_vwv0 + 3)
+#define smb_nts_TotalDataCount (smb_vwv0 + 7)
+#define smb_nts_ParameterCount (smb_vwv0 + 11)
+#define smb_nts_ParameterOffset (smb_vwv0 + 15)
+#define smb_nts_ParameterDisplacement (smb_vwv0 + 19)
+#define smb_nts_DataCount (smb_vwv0 + 23)
+#define smb_nts_DataOffset (smb_vwv0 + 27)
+#define smb_nts_DataDisplacement (smb_vwv0 + 31)
+
+/* these are for the NT trans reply. */
+#define smb_ntr_TotalParameterCount (smb_vwv0 + 3)
+#define smb_ntr_TotalDataCount (smb_vwv0 + 7)
+#define smb_ntr_ParameterCount (smb_vwv0 + 11)
+#define smb_ntr_ParameterOffset (smb_vwv0 + 15)
+#define smb_ntr_ParameterDisplacement (smb_vwv0 + 19)
+#define smb_ntr_DataCount (smb_vwv0 + 23)
+#define smb_ntr_DataOffset (smb_vwv0 + 27)
+#define smb_ntr_DataDisplacement (smb_vwv0 + 31)
+
+/* these are for the NT create_and_X */
+#define smb_ntcreate_NameLength (smb_vwv0 + 5)
+#define smb_ntcreate_Flags (smb_vwv0 + 7)
+#define smb_ntcreate_RootDirectoryFid (smb_vwv0 + 11)
+#define smb_ntcreate_DesiredAccess (smb_vwv0 + 15)
+#define smb_ntcreate_AllocationSize (smb_vwv0 + 19)
+#define smb_ntcreate_FileAttributes (smb_vwv0 + 27)
+#define smb_ntcreate_ShareAccess (smb_vwv0 + 31)
+#define smb_ntcreate_CreateDisposition (smb_vwv0 + 35)
+#define smb_ntcreate_CreateOptions (smb_vwv0 + 39)
+#define smb_ntcreate_ImpersonationLevel (smb_vwv0 + 43)
+#define smb_ntcreate_SecurityFlags (smb_vwv0 + 47)
+
+/* 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
+
+/* File Specific access rights */
+#define FILE_READ_DATA 0x00000001
+#define FILE_WRITE_DATA 0x00000002
+#define FILE_APPEND_DATA 0x00000004
+#define FILE_READ_EA 0x00000008 /* File and directory */
+#define FILE_WRITE_EA 0x00000010 /* File and directory */
+#define FILE_EXECUTE 0x00000020
+#define FILE_DELETE_CHILD 0x00000040
+#define FILE_READ_ATTRIBUTES 0x00000080
+#define FILE_WRITE_ATTRIBUTES 0x00000100
+
+#define FILE_ALL_ACCESS 0x000001FF
+
+/* Directory specific access rights */
+#define FILE_LIST_DIRECTORY 0x00000001
+#define FILE_ADD_FILE 0x00000002
+#define FILE_ADD_SUBDIRECTORY 0x00000004
+#define FILE_TRAVERSE 0x00000020
+#define FILE_DELETE_CHILD 0x00000040
+
+/* the desired access to use when opening a pipe */
+#define DESIRED_ACCESS_PIPE 0x2019f
+
+/* Generic access masks & rights. */
+#define DELETE_ACCESS (1L<<16) /* 0x00010000 */
+#define READ_CONTROL_ACCESS (1L<<17) /* 0x00020000 */
+#define WRITE_DAC_ACCESS (1L<<18) /* 0x00040000 */
+#define WRITE_OWNER_ACCESS (1L<<19) /* 0x00080000 */
+#define SYNCHRONIZE_ACCESS (1L<<20) /* 0x00100000 */
+
+#define SYSTEM_SECURITY_ACCESS (1L<<24) /* 0x01000000 */
+#define MAXIMUM_ALLOWED_ACCESS (1L<<25) /* 0x02000000 */
+#define GENERIC_ALL_ACCESS (1<<28) /* 0x10000000 */
+#define GENERIC_EXECUTE_ACCESS (1<<29) /* 0x20000000 */
+#define GENERIC_WRITE_ACCESS (1<<30) /* 0x40000000 */
+#define GENERIC_READ_ACCESS (((unsigned)1)<<31) /* 0x80000000 */
+
+/* Mapping of generic access rights for files to specific rights. */
+
+#define FILE_GENERIC_ALL (STANDARD_RIGHTS_REQUIRED_ACCESS| SYNCHRONIZE_ACCESS|FILE_ALL_ACCESS)
+
+#define FILE_GENERIC_READ (STANDARD_RIGHTS_READ_ACCESS|FILE_READ_DATA|FILE_READ_ATTRIBUTES|\
+ FILE_READ_EA|SYNCHRONIZE_ACCESS)
+
+#define FILE_GENERIC_WRITE (STANDARD_RIGHTS_WRITE_ACCESS|FILE_WRITE_DATA|FILE_WRITE_ATTRIBUTES|\
+ FILE_WRITE_EA|FILE_APPEND_DATA|SYNCHRONIZE_ACCESS)
+
+#define FILE_GENERIC_EXECUTE (STANDARD_RIGHTS_EXECUTE_ACCESS|FILE_READ_ATTRIBUTES|\
+ FILE_EXECUTE|SYNCHRONIZE_ACCESS)
+
+/* Mapping of access rights to UNIX perms. */
+#define UNIX_ACCESS_RWX FILE_GENERIC_ALL
+#define UNIX_ACCESS_R FILE_GENERIC_READ
+#define UNIX_ACCESS_W FILE_GENERIC_WRITE
+#define UNIX_ACCESS_X FILE_GENERIC_EXECUTE
+
+#if 0
+/*
+ * This is the old mapping we used to use. To get W2KSP2 profiles
+ * working we need to map to the canonical file perms.
+ */
+#define UNIX_ACCESS_RWX (UNIX_ACCESS_R|UNIX_ACCESS_W|UNIX_ACCESS_X)
+#define UNIX_ACCESS_R (READ_CONTROL_ACCESS|SYNCHRONIZE_ACCESS|\
+ FILE_READ_ATTRIBUTES|FILE_READ_EA|FILE_READ_DATA)
+#define UNIX_ACCESS_W (READ_CONTROL_ACCESS|SYNCHRONIZE_ACCESS|\
+ FILE_WRITE_ATTRIBUTES|FILE_WRITE_EA|\
+ FILE_APPEND_DATA|FILE_WRITE_DATA)
+#define UNIX_ACCESS_X (READ_CONTROL_ACCESS|SYNCHRONIZE_ACCESS|\
+ FILE_EXECUTE|FILE_READ_ATTRIBUTES)
+#endif
+
+#define UNIX_ACCESS_NONE (WRITE_OWNER_ACCESS)
+
+/* Flags field. */
+#define REQUEST_OPLOCK 2
+#define REQUEST_BATCH_OPLOCK 4
+#define OPEN_DIRECTORY 8
+#define EXTENDED_RESPONSE_REQUIRED 0x10
+
+/* ShareAccess field. */
+#define FILE_SHARE_NONE 0 /* Cannot be used in bitmask. */
+#define FILE_SHARE_READ 1
+#define FILE_SHARE_WRITE 2
+#define FILE_SHARE_DELETE 4
+
+/* FileAttributesField */
+#define FILE_ATTRIBUTE_READONLY 0x001L
+#define FILE_ATTRIBUTE_HIDDEN 0x002L
+#define FILE_ATTRIBUTE_SYSTEM 0x004L
+#define FILE_ATTRIBUTE_DIRECTORY 0x010L
+#define FILE_ATTRIBUTE_ARCHIVE 0x020L
+#define FILE_ATTRIBUTE_NORMAL 0x080L
+#define FILE_ATTRIBUTE_TEMPORARY 0x100L
+#define FILE_ATTRIBUTE_SPARSE 0x200L
+#define FILE_ATTRIBUTE_COMPRESSED 0x800L
+#define FILE_ATTRIBUTE_NONINDEXED 0x2000L
+#define SAMBA_ATTRIBUTES_MASK 0x7F
+
+/* 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
+#define FILE_FLAG_POSIX_SEMANTICS 0x01000000L
+
+/* CreateDisposition field. */
+#define FILE_SUPERSEDE 0
+#define FILE_OPEN 1
+#define FILE_CREATE 2
+#define FILE_OPEN_IF 3
+#define FILE_OVERWRITE 4
+#define FILE_OVERWRITE_IF 5
+
+/* CreateOptions field. */
+#define FILE_DIRECTORY_FILE 0x0001
+#define FILE_WRITE_THROUGH 0x0002
+#define FILE_SEQUENTIAL_ONLY 0x0004
+#define FILE_NON_DIRECTORY_FILE 0x0040
+#define FILE_NO_EA_KNOWLEDGE 0x0200
+#define FILE_EIGHT_DOT_THREE_ONLY 0x0400
+#define FILE_RANDOM_ACCESS 0x0800
+#define FILE_DELETE_ON_CLOSE 0x1000
+#define FILE_OPEN_BY_FILE_ID 0x2000
+
+/* 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 (from Samba4) */
+#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 0x00000001
+#define FILE_CASE_PRESERVED_NAMES 0x00000002
+#define FILE_UNICODE_ON_DISK 0x00000004
+/* According to cifs9f, this is 4, not 8 */
+/* Acconding to testing, this actually sets the security attribute! */
+#define FILE_PERSISTENT_ACLS 0x00000008
+#define FILE_FILE_COMPRESSION 0x00000010
+#define FILE_VOLUME_QUOTAS 0x00000020
+#define FILE_SUPPORTS_SPARSE_FILES 0x00000040
+#define FILE_SUPPORTS_REPARSE_POINTS 0x00000080
+#define FILE_SUPPORTS_REMOTE_STORAGE 0x00000100
+#define FS_LFN_APIS 0x00004000
+#define FILE_VOLUME_IS_COMPRESSED 0x00008000
+#define FILE_SUPPORTS_OBJECT_IDS 0x00010000
+#define FILE_SUPPORTS_ENCRYPTION 0x00020000
+#define FILE_NAMED_STREAMS 0x00040000
+#define FILE_READ_ONLY_VOLUME 0x00080000
+
+/* ChangeNotify flags. */
+#define FILE_NOTIFY_CHANGE_FILE 0x001
+#define FILE_NOTIFY_CHANGE_DIR_NAME 0x002
+#define FILE_NOTIFY_CHANGE_ATTRIBUTES 0x004
+#define FILE_NOTIFY_CHANGE_SIZE 0x008
+#define FILE_NOTIFY_CHANGE_LAST_WRITE 0x010
+#define FILE_NOTIFY_CHANGE_LAST_ACCESS 0x020
+#define FILE_NOTIFY_CHANGE_CREATION 0x040
+#define FILE_NOTIFY_CHANGE_EA 0x080
+#define FILE_NOTIFY_CHANGE_SECURITY 0x100
+#define FILE_NOTIFY_CHANGE_FILE_NAME 0x200
+
+/* where to find the base of the SMB packet proper */
+#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
+
+
+#define SMB_SUCCESS 0 /* The request was successful. */
+
+#ifdef WITH_DFS
+void dfs_unlogin(void);
+extern int dcelogin_atmost_once;
+#endif
+
+#ifdef NOSTRDUP
+char *strdup(char *s);
+#endif
+
+#ifndef SIGNAL_CAST
+#define SIGNAL_CAST (RETSIGTYPE (*)(int))
+#endif
+
+#ifndef SELECT_CAST
+#define SELECT_CAST
+#endif
+
+/* these are used in NetServerEnum to choose what to receive */
+#define SV_TYPE_WORKSTATION 0x00000001
+#define SV_TYPE_SERVER 0x00000002
+#define SV_TYPE_SQLSERVER 0x00000004
+#define SV_TYPE_DOMAIN_CTRL 0x00000008
+#define SV_TYPE_DOMAIN_BAKCTRL 0x00000010
+#define SV_TYPE_TIME_SOURCE 0x00000020
+#define SV_TYPE_AFP 0x00000040
+#define SV_TYPE_NOVELL 0x00000080
+#define SV_TYPE_DOMAIN_MEMBER 0x00000100
+#define SV_TYPE_PRINTQ_SERVER 0x00000200
+#define SV_TYPE_DIALIN_SERVER 0x00000400
+#define SV_TYPE_SERVER_UNIX 0x00000800
+#define SV_TYPE_NT 0x00001000
+#define SV_TYPE_WFW 0x00002000
+#define SV_TYPE_SERVER_MFPN 0x00004000
+#define SV_TYPE_SERVER_NT 0x00008000
+#define SV_TYPE_POTENTIAL_BROWSER 0x00010000
+#define SV_TYPE_BACKUP_BROWSER 0x00020000
+#define SV_TYPE_MASTER_BROWSER 0x00040000
+#define SV_TYPE_DOMAIN_MASTER 0x00080000
+#define SV_TYPE_SERVER_OSF 0x00100000
+#define SV_TYPE_SERVER_VMS 0x00200000
+#define SV_TYPE_WIN95_PLUS 0x00400000
+#define SV_TYPE_DFS_SERVER 0x00800000
+#define SV_TYPE_ALTERNATE_XPORT 0x20000000
+#define SV_TYPE_LOCAL_LIST_ONLY 0x40000000
+#define SV_TYPE_DOMAIN_ENUM 0x80000000
+#define SV_TYPE_ALL 0xFFFFFFFF
+
+/* 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_NO_EXECUTE 0x2000
+#define FLAGS2_32_BIT_ERROR_CODES 0x4000
+#define FLAGS2_UNICODE_STRINGS 0x8000
+
+#define FLAGS2_WIN2K_SIGNATURE 0xC852 /* Hack alert ! For now... JRA. */
+
+/* Capabilities. see ftp.microsoft.com/developr/drg/cifs/cifs/cifs4.txt */
+
+#define CAP_RAW_MODE 0x0001
+#define CAP_MPX_MODE 0x0002
+#define CAP_UNICODE 0x0004
+#define CAP_LARGE_FILES 0x0008
+#define CAP_NT_SMBS 0x0010
+#define CAP_RPC_REMOTE_APIS 0x0020
+#define CAP_STATUS32 0x0040
+#define CAP_LEVEL_II_OPLOCKS 0x0080
+#define CAP_LOCK_AND_READ 0x0100
+#define CAP_NT_FIND 0x0200
+#define CAP_DFS 0x1000
+#define CAP_W2K_SMBS 0x2000
+#define CAP_LARGE_READX 0x4000
+#define CAP_LARGE_WRITEX 0x8000
+#define CAP_UNIX 0x800000 /* Capabilities for UNIX extensions. Created by HP. */
+#define CAP_EXTENDED_SECURITY 0x80000000
+
+/* protocol types. It assumes that higher protocols include lower protocols
+ as subsets */
+enum protocol_types {PROTOCOL_NONE,PROTOCOL_CORE,PROTOCOL_COREPLUS,PROTOCOL_LANMAN1,PROTOCOL_LANMAN2,PROTOCOL_NT1};
+
+/* security levels */
+enum security_types {SEC_SHARE,SEC_USER,SEC_SERVER,SEC_DOMAIN,SEC_ADS};
+
+/* server roles */
+enum server_types
+{
+ ROLE_STANDALONE,
+ ROLE_DOMAIN_MEMBER,
+ ROLE_DOMAIN_BDC,
+ ROLE_DOMAIN_PDC
+};
+
+/* printing types */
+enum printing_types {PRINT_BSD,PRINT_SYSV,PRINT_AIX,PRINT_HPUX,
+ PRINT_QNX,PRINT_PLP,PRINT_LPRNG,PRINT_SOFTQ,
+ PRINT_CUPS,PRINT_LPRNT,PRINT_LPROS2
+#ifdef DEVELOPER
+,PRINT_TEST,PRINT_VLP
+#endif /* DEVELOPER */
+};
+
+/* LDAP schema types */
+enum schema_types {SCHEMA_COMPAT, SCHEMA_AD, SCHEMA_SAMBA};
+
+/* LDAP SSL options */
+enum ldap_ssl_types {LDAP_SSL_ON, LDAP_SSL_OFF, LDAP_SSL_START_TLS};
+
+/* LDAP PASSWD SYNC methods */
+enum ldap_passwd_sync_types {LDAP_PASSWD_SYNC_ON, LDAP_PASSWD_SYNC_OFF, LDAP_PASSWD_SYNC_ONLY};
+
+/* Remote architectures we know about. */
+enum remote_arch_types {RA_UNKNOWN, RA_WFWG, RA_OS2, RA_WIN95, RA_WINNT, RA_WIN2K, RA_WINXP, RA_WIN2K3, RA_SAMBA};
+
+/* case handling */
+enum case_handling {CASE_LOWER,CASE_UPPER};
+
+/*
+ * Global value meaing that the smb_uid field should be
+ * ingored (in share level security and protocol level == CORE)
+ */
+
+#define UID_FIELD_INVALID 0
+#define VUID_OFFSET 100 /* Amount to bias returned vuid numbers */
+
+/*
+ * Size of buffer to use when moving files across filesystems.
+ */
+#define COPYBUF_SIZE (8*1024)
+
+/*
+ * Values used to override error codes.
+ */
+extern int unix_ERR_class;
+extern int unix_ERR_code;
+extern NTSTATUS unix_ERR_ntstatus;
+
+/*
+ * Used in chaining code.
+ */
+extern int chain_size;
+
+/*
+ * Map the Core and Extended Oplock requesst bits down
+ * to common bits (EXCLUSIVE_OPLOCK & BATCH_OPLOCK).
+ */
+
+/*
+ * Core protocol.
+ */
+#define CORE_OPLOCK_REQUEST(inbuf) \
+ ((CVAL(inbuf,smb_flg)&(FLAG_REQUEST_OPLOCK|FLAG_REQUEST_BATCH_OPLOCK))>>5)
+
+/*
+ * Extended protocol.
+ */
+#define EXTENDED_OPLOCK_REQUEST(inbuf) ((SVAL(inbuf,smb_vwv2)&((1<<1)|(1<<2)))>>1)
+
+/* Lock types. */
+#define LOCKING_ANDX_SHARED_LOCK 0x1
+#define LOCKING_ANDX_OPLOCK_RELEASE 0x2
+#define LOCKING_ANDX_CHANGE_LOCKTYPE 0x4
+#define LOCKING_ANDX_CANCEL_LOCK 0x8
+#define LOCKING_ANDX_LARGE_FILES 0x10
+
+/* Oplock levels */
+#define OPLOCKLEVEL_NONE 0
+#define OPLOCKLEVEL_II 1
+
+/*
+ * Bits we test with.
+ */
+
+#define NO_OPLOCK 0
+#define EXCLUSIVE_OPLOCK 1
+#define BATCH_OPLOCK 2
+#define LEVEL_II_OPLOCK 4
+
+#define EXCLUSIVE_OPLOCK_TYPE(lck) ((lck) & (EXCLUSIVE_OPLOCK|BATCH_OPLOCK))
+#define BATCH_OPLOCK_TYPE(lck) ((lck) & BATCH_OPLOCK)
+#define LEVEL_II_OPLOCK_TYPE(lck) ((lck) & LEVEL_II_OPLOCK)
+
+#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
+
+/*
+ * Loopback command offsets.
+ */
+
+#define OPBRK_CMD_LEN_OFFSET 0
+#define OPBRK_CMD_PORT_OFFSET 4
+#define OPBRK_CMD_HEADER_LEN 6
+
+#define OPBRK_MESSAGE_CMD_OFFSET 0
+
+/*
+ * Oplock break command code to send over the udp socket.
+ * The same message is sent for both exlusive and level II breaks.
+ *
+ * The form of this is :
+ *
+ * 0 2 2+pid 2+pid+dev 2+pid+dev+ino
+ * +----+--------+-------+--------+---------+
+ * | cmd| pid | dev | inode | fileid |
+ * +----+--------+-------+--------+---------+
+ */
+
+#define OPLOCK_BREAK_PID_OFFSET 2
+#define OPLOCK_BREAK_DEV_OFFSET (OPLOCK_BREAK_PID_OFFSET + sizeof(pid_t))
+#define OPLOCK_BREAK_INODE_OFFSET (OPLOCK_BREAK_DEV_OFFSET + sizeof(SMB_DEV_T))
+#define OPLOCK_BREAK_FILEID_OFFSET (OPLOCK_BREAK_INODE_OFFSET + sizeof(SMB_INO_T))
+#define OPLOCK_BREAK_MSG_LEN (OPLOCK_BREAK_FILEID_OFFSET + sizeof(unsigned long))
+
+/* Message types */
+#define OPLOCK_BREAK_CMD 0x1
+#define KERNEL_OPLOCK_BREAK_CMD 0x2
+#define LEVEL_II_OPLOCK_BREAK_CMD 0x3
+#define ASYNC_LEVEL_II_OPLOCK_BREAK_CMD 0x4
+
+/*
+ * Capabilities abstracted for different systems.
+ */
+
+#define KERNEL_OPLOCK_CAPABILITY 0x1
+
+/*
+ * Oplock break command code sent via the kernel interface (if it exists).
+ *
+ * Form of this is :
+ *
+ * 0 2 2+devsize 2+devsize+inodesize
+ * +----+--------+--------+----------+
+ * | cmd| dev | inode | fileid |
+ * +----+--------+--------+----------+
+ */
+#define KERNEL_OPLOCK_BREAK_DEV_OFFSET 2
+#define KERNEL_OPLOCK_BREAK_INODE_OFFSET (KERNEL_OPLOCK_BREAK_DEV_OFFSET + sizeof(SMB_DEV_T))
+#define KERNEL_OPLOCK_BREAK_FILEID_OFFSET (KERNEL_OPLOCK_BREAK_INODE_OFFSET + sizeof(SMB_INO_T))
+#define KERNEL_OPLOCK_BREAK_MSG_LEN (KERNEL_OPLOCK_BREAK_FILEID_OFFSET + sizeof(unsigned long))
+
+
+/* if a kernel does support oplocks then a structure of the following
+ typee is used to describe how to interact with the kernel */
+struct kernel_oplocks {
+ BOOL (*receive_message)(fd_set *fds, char *buffer, int buffer_len);
+ BOOL (*set_oplock)(files_struct *fsp, int oplock_type);
+ void (*release_oplock)(files_struct *fsp);
+ BOOL (*parse_message)(char *msg_start, int msg_len, SMB_INO_T *inode, SMB_DEV_T *dev, unsigned long *file_id);
+ BOOL (*msg_waiting)(fd_set *fds);
+ int notification_fd;
+};
+
+
+#define CMD_REPLY 0x8000
+
+/* this structure defines the functions for doing change notify in
+ various implementations */
+struct cnotify_fns {
+ void * (*register_notify)(connection_struct *conn, char *path, uint32 flags);
+ BOOL (*check_notify)(connection_struct *conn, uint16 vuid, char *path, uint32 flags, void *data, time_t t);
+ void (*remove_notify)(void *data);
+ int select_time;
+};
+
+
+
+#include "smb_macros.h"
+
+#define MAX_NETBIOSNAME_LEN 16
+/* DOS character, NetBIOS namestring. Type used on the wire. */
+typedef char nstring[MAX_NETBIOSNAME_LEN];
+/* Unix character, NetBIOS namestring. Type used to manipulate name in nmbd. */
+typedef char unstring[MAX_NETBIOSNAME_LEN*4];
+
+/* A netbios name structure. */
+struct nmb_name {
+ nstring name;
+ char scope[64];
+ unsigned int name_type;
+};
+
+
+/* A netbios node status array element. */
+struct node_status {
+ nstring name;
+ unsigned char type;
+ unsigned char flags;
+};
+
+struct pwd_info
+{
+ BOOL null_pwd;
+ BOOL cleartext;
+
+ fstring password;
+
+};
+
+typedef struct user_struct
+{
+ struct user_struct *next, *prev;
+ uint16 vuid; /* Tag for this entry. */
+ uid_t uid; /* uid of a validated user */
+ gid_t gid; /* gid of a validated user */
+
+ userdom_struct user;
+ char *homedir;
+ char *unix_homedir;
+ char *logon_script;
+
+ BOOL guest;
+
+ /* following groups stuff added by ih */
+ /* This groups info is needed for when we become_user() for this uid */
+ int n_groups;
+ gid_t *groups;
+
+ NT_USER_TOKEN *nt_user_token;
+ PRIVILEGE_SET *privs;
+
+ DATA_BLOB session_key;
+
+ char *session_keystr; /* used by utmp and pam session code.
+ TDB key string */
+ int homes_snum;
+
+ struct auth_serversupplied_info *server_info;
+
+} user_struct;
+
+
+struct unix_error_map {
+ int unix_error;
+ int dos_class;
+ int dos_code;
+ NTSTATUS nt_error;
+};
+
+/*
+#include "ntdomain.h"
+
+#include "client.h"
+*/
+
+/*
+ * Size of new password account encoding string. This is enough space to
+ * hold 11 ACB characters, plus the surrounding [] and a terminating null.
+ * Do not change unless you are adding new ACB bits!
+ */
+
+#define NEW_PW_FORMAT_SPACE_PADDED_LEN 14
+
+/*
+ Do you want session setups at user level security with a invalid
+ password to be rejected or allowed in as guest? WinNT rejects them
+ but it can be a pain as it means "net view" needs to use a password
+
+ You have 3 choices in the setting of map_to_guest:
+
+ "NEVER_MAP_TO_GUEST" means session setups with an invalid password
+ are rejected. This is the default.
+
+ "MAP_TO_GUEST_ON_BAD_USER" means session setups with an invalid password
+ are rejected, unless the username does not exist, in which case it
+ is treated as a guest login
+
+ "MAP_TO_GUEST_ON_BAD_PASSWORD" means session setups with an invalid password
+ are treated as a guest login
+
+ Note that map_to_guest only has an effect in user or server
+ level security.
+*/
+
+#define NEVER_MAP_TO_GUEST 0
+#define MAP_TO_GUEST_ON_BAD_USER 1
+#define MAP_TO_GUEST_ON_BAD_PASSWORD 2
+
+#define SAFE_NETBIOS_CHARS ". -_"
+
+/* generic iconv conversion structure */
+typedef struct {
+ size_t (*direct)(void *cd, char **inbuf, size_t *inbytesleft,
+ char **outbuf, size_t *outbytesleft);
+ size_t (*pull)(void *cd, char **inbuf, size_t *inbytesleft,
+ char **outbuf, size_t *outbytesleft);
+ size_t (*push)(void *cd, char **inbuf, size_t *inbytesleft,
+ char **outbuf, size_t *outbytesleft);
+ void *cd_direct, *cd_pull, *cd_push;
+ char *from_name, *to_name;
+} *smb_iconv_t;
+
+/* 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
+
+#include "popt_common.h"
+
+#define PORT_NONE 0
+#ifndef LDAP_PORT
+#define LDAP_PORT 389
+#endif
+
+/* used by the IP comparison function */
+struct ip_service {
+ struct in_addr ip;
+ unsigned port;
+};
+
+/* Used by the SMB signing functions. */
+
+typedef struct smb_sign_info {
+ void (*sign_outgoing_message)(char *outbuf, struct smb_sign_info *si);
+ BOOL (*check_incoming_message)(char *inbuf, struct smb_sign_info *si, BOOL expected_ok);
+ void (*free_signing_context)(struct smb_sign_info *si);
+ void *signing_context;
+
+ BOOL negotiated_smb_signing;
+ BOOL allow_smb_signing;
+ BOOL doing_signing;
+ BOOL mandatory_signing;
+ BOOL seen_valid; /* Have I ever seen a validly signed packet? */
+} smb_sign_info;
+
+struct ea_struct {
+ uint8 flags;
+ char *name;
+ DATA_BLOB value;
+};
+
+/* EA names used internally in Samba. KEEP UP TO DATE with prohibited_ea_names in trans2.c !. */
+#define SAMBA_POSIX_INHERITANCE_EA_NAME "user.SAMBA_PAI"
+/* EA to use for DOS attributes */
+#define SAMBA_XATTR_DOS_ATTRIB "user.DOSATTRIB"
+
+#endif /* _SMB_H */
diff --git a/source/include/smb_acls.h b/source/include/smb_acls.h
new file mode 100644
index 00000000000..2bde6caeda1
--- /dev/null
+++ b/source/include/smb_acls.h
@@ -0,0 +1,275 @@
+/*
+ Unix SMB/CIFS implementation.
+ Portable SMB ACL interface
+ Copyright (C) Jeremy Allison 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 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 _SMB_ACLS_H
+#define _SMB_ACLS_H
+#if defined(HAVE_POSIX_ACLS)
+
+/* This is an identity mapping (just remove the SMB_). */
+
+#define SMB_ACL_TAG_T acl_tag_t
+#define SMB_ACL_TYPE_T acl_type_t
+#define SMB_ACL_PERMSET_T acl_permset_t
+#define SMB_ACL_PERM_T acl_perm_t
+#define SMB_ACL_READ ACL_READ
+#define SMB_ACL_WRITE ACL_WRITE
+#define SMB_ACL_EXECUTE ACL_EXECUTE
+
+/* Types of ACLs. */
+#define SMB_ACL_USER ACL_USER
+#define SMB_ACL_USER_OBJ ACL_USER_OBJ
+#define SMB_ACL_GROUP ACL_GROUP
+#define SMB_ACL_GROUP_OBJ ACL_GROUP_OBJ
+#define SMB_ACL_OTHER ACL_OTHER
+#define SMB_ACL_MASK ACL_MASK
+
+#define SMB_ACL_T acl_t
+
+#define SMB_ACL_ENTRY_T acl_entry_t
+
+#define SMB_ACL_FIRST_ENTRY ACL_FIRST_ENTRY
+#define SMB_ACL_NEXT_ENTRY ACL_NEXT_ENTRY
+
+#define SMB_ACL_TYPE_ACCESS ACL_TYPE_ACCESS
+#define SMB_ACL_TYPE_DEFAULT ACL_TYPE_DEFAULT
+
+#elif defined(HAVE_TRU64_ACLS)
+
+/* This is for DEC/Compaq Tru64 UNIX */
+
+#define SMB_ACL_TAG_T acl_tag_t
+#define SMB_ACL_TYPE_T acl_type_t
+#define SMB_ACL_PERMSET_T acl_permset_t
+#define SMB_ACL_PERM_T acl_perm_t
+#define SMB_ACL_READ ACL_READ
+#define SMB_ACL_WRITE ACL_WRITE
+#define SMB_ACL_EXECUTE ACL_EXECUTE
+
+/* Types of ACLs. */
+#define SMB_ACL_USER ACL_USER
+#define SMB_ACL_USER_OBJ ACL_USER_OBJ
+#define SMB_ACL_GROUP ACL_GROUP
+#define SMB_ACL_GROUP_OBJ ACL_GROUP_OBJ
+#define SMB_ACL_OTHER ACL_OTHER
+#define SMB_ACL_MASK ACL_MASK
+
+#define SMB_ACL_T acl_t
+
+#define SMB_ACL_ENTRY_T acl_entry_t
+
+#define SMB_ACL_FIRST_ENTRY 0
+#define SMB_ACL_NEXT_ENTRY 1
+
+#define SMB_ACL_TYPE_ACCESS ACL_TYPE_ACCESS
+#define SMB_ACL_TYPE_DEFAULT ACL_TYPE_DEFAULT
+
+#elif defined(HAVE_UNIXWARE_ACLS) || defined(HAVE_SOLARIS_ACLS)
+/*
+ * Donated by Michael Davidson <md@sco.COM> for UnixWare / OpenUNIX.
+ * Modified by Toomas Soome <tsoome@ut.ee> for Solaris.
+ */
+
+/* SVR4.2 ES/MP ACLs */
+typedef int SMB_ACL_TAG_T;
+typedef int SMB_ACL_TYPE_T;
+typedef ushort *SMB_ACL_PERMSET_T;
+typedef ushort SMB_ACL_PERM_T;
+#define SMB_ACL_READ 4
+#define SMB_ACL_WRITE 2
+#define SMB_ACL_EXECUTE 1
+
+/* Types of ACLs. */
+#define SMB_ACL_USER USER
+#define SMB_ACL_USER_OBJ USER_OBJ
+#define SMB_ACL_GROUP GROUP
+#define SMB_ACL_GROUP_OBJ GROUP_OBJ
+#define SMB_ACL_OTHER OTHER_OBJ
+#define SMB_ACL_MASK CLASS_OBJ
+
+typedef struct SMB_ACL_T {
+ int size;
+ int count;
+ int next;
+ struct acl acl[1];
+} *SMB_ACL_T;
+
+typedef struct acl *SMB_ACL_ENTRY_T;
+
+#define SMB_ACL_FIRST_ENTRY 0
+#define SMB_ACL_NEXT_ENTRY 1
+
+#define SMB_ACL_TYPE_ACCESS 0
+#define SMB_ACL_TYPE_DEFAULT 1
+
+#elif defined(HAVE_HPUX_ACLS)
+
+/*
+ * Based on the Solaris & UnixWare code.
+ */
+
+#undef GROUP
+#include <sys/aclv.h>
+
+/* SVR4.2 ES/MP ACLs */
+typedef int SMB_ACL_TAG_T;
+typedef int SMB_ACL_TYPE_T;
+typedef ushort *SMB_ACL_PERMSET_T;
+typedef ushort SMB_ACL_PERM_T;
+#define SMB_ACL_READ 4
+#define SMB_ACL_WRITE 2
+#define SMB_ACL_EXECUTE 1
+
+/* Types of ACLs. */
+#define SMB_ACL_USER USER
+#define SMB_ACL_USER_OBJ USER_OBJ
+#define SMB_ACL_GROUP GROUP
+#define SMB_ACL_GROUP_OBJ GROUP_OBJ
+#define SMB_ACL_OTHER OTHER_OBJ
+#define SMB_ACL_MASK CLASS_OBJ
+
+typedef struct SMB_ACL_T {
+ int size;
+ int count;
+ int next;
+ struct acl acl[1];
+} *SMB_ACL_T;
+
+typedef struct acl *SMB_ACL_ENTRY_T;
+
+#define SMB_ACL_FIRST_ENTRY 0
+#define SMB_ACL_NEXT_ENTRY 1
+
+#define SMB_ACL_TYPE_ACCESS 0
+#define SMB_ACL_TYPE_DEFAULT 1
+
+#elif defined(HAVE_IRIX_ACLS)
+
+#define SMB_ACL_TAG_T acl_tag_t
+#define SMB_ACL_TYPE_T acl_type_t
+#define SMB_ACL_PERMSET_T acl_permset_t
+#define SMB_ACL_PERM_T acl_perm_t
+#define SMB_ACL_READ ACL_READ
+#define SMB_ACL_WRITE ACL_WRITE
+#define SMB_ACL_EXECUTE ACL_EXECUTE
+
+/* Types of ACLs. */
+#define SMB_ACL_USER ACL_USER
+#define SMB_ACL_USER_OBJ ACL_USER_OBJ
+#define SMB_ACL_GROUP ACL_GROUP
+#define SMB_ACL_GROUP_OBJ ACL_GROUP_OBJ
+#define SMB_ACL_OTHER ACL_OTHER_OBJ
+#define SMB_ACL_MASK ACL_MASK
+
+typedef struct SMB_ACL_T {
+ int next;
+ BOOL freeaclp;
+ struct acl *aclp;
+} *SMB_ACL_T;
+
+#define SMB_ACL_ENTRY_T acl_entry_t
+
+#define SMB_ACL_FIRST_ENTRY 0
+#define SMB_ACL_NEXT_ENTRY 1
+
+#define SMB_ACL_TYPE_ACCESS ACL_TYPE_ACCESS
+#define SMB_ACL_TYPE_DEFAULT ACL_TYPE_DEFAULT
+
+#elif defined(HAVE_AIX_ACLS)
+
+/* Donated by Medha Date, mdate@austin.ibm.com, for IBM */
+
+#include <acl.h>
+
+typedef uint *SMB_ACL_PERMSET_T;
+
+struct acl_entry_link{
+ struct acl_entry_link *prevp;
+ struct new_acl_entry *entryp;
+ struct acl_entry_link *nextp;
+ int count;
+};
+
+struct new_acl_entry{
+ unsigned short ace_len;
+ unsigned short ace_type;
+ unsigned int ace_access;
+ struct ace_id ace_id[1];
+};
+
+#define SMB_ACL_ENTRY_T struct new_acl_entry*
+#define SMB_ACL_T struct acl_entry_link*
+
+#define SMB_ACL_TAG_T unsigned short
+#define SMB_ACL_TYPE_T int
+#define SMB_ACL_PERM_T uint
+#define SMB_ACL_READ S_IRUSR
+#define SMB_ACL_WRITE S_IWUSR
+#define SMB_ACL_EXECUTE S_IXUSR
+
+/* Types of ACLs. */
+#define SMB_ACL_USER ACEID_USER
+#define SMB_ACL_USER_OBJ 3
+#define SMB_ACL_GROUP ACEID_GROUP
+#define SMB_ACL_GROUP_OBJ 4
+#define SMB_ACL_OTHER 5
+#define SMB_ACL_MASK 6
+
+
+#define SMB_ACL_FIRST_ENTRY 1
+#define SMB_ACL_NEXT_ENTRY 2
+
+#define SMB_ACL_TYPE_ACCESS 0
+#define SMB_ACL_TYPE_DEFAULT 1
+
+#else /* No ACLs. */
+
+/* No ACLS - fake it. */
+#define SMB_ACL_TAG_T int
+#define SMB_ACL_TYPE_T int
+#define SMB_ACL_PERMSET_T mode_t
+#define SMB_ACL_PERM_T mode_t
+#define SMB_ACL_READ S_IRUSR
+#define SMB_ACL_WRITE S_IWUSR
+#define SMB_ACL_EXECUTE S_IXUSR
+
+/* Types of ACLs. */
+#define SMB_ACL_USER 0
+#define SMB_ACL_USER_OBJ 1
+#define SMB_ACL_GROUP 2
+#define SMB_ACL_GROUP_OBJ 3
+#define SMB_ACL_OTHER 4
+#define SMB_ACL_MASK 5
+
+typedef struct SMB_ACL_T {
+ int dummy;
+} *SMB_ACL_T;
+
+typedef struct SMB_ACL_ENTRY_T {
+ int dummy;
+} *SMB_ACL_ENTRY_T;
+
+#define SMB_ACL_FIRST_ENTRY 0
+#define SMB_ACL_NEXT_ENTRY 1
+
+#define SMB_ACL_TYPE_ACCESS 0
+#define SMB_ACL_TYPE_DEFAULT 1
+
+#endif /* No ACLs. */
+#endif /* _SMB_ACLS_H */
diff --git a/source/include/smb_macros.h b/source/include/smb_macros.h
new file mode 100644
index 00000000000..c3bdba30b15
--- /dev/null
+++ b/source/include/smb_macros.h
@@ -0,0 +1,264 @@
+/*
+ Unix SMB/CIFS implementation.
+ SMB parameters and setup
+ Copyright (C) Andrew Tridgell 1992-1999
+ Copyright (C) John H Terpstra 1996-1999
+ Copyright (C) Luke Kenneth Casson Leighton 1996-1999
+ Copyright (C) Paul Ashton 1998 - 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 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 _SMB_MACROS_H
+#define _SMB_MACROS_H
+
+/* Misc bit macros */
+#define BOOLSTR(b) ((b) ? "Yes" : "No")
+#define BITSETB(ptr,bit) ((((char *)ptr)[0] & (1<<(bit)))!=0)
+#define BITSETW(ptr,bit) ((SVAL(ptr,0) & (1<<(bit)))!=0)
+
+/* for readability... */
+#define IS_DOS_READONLY(test_mode) (((test_mode) & aRONLY) != 0)
+#define IS_DOS_DIR(test_mode) (((test_mode) & aDIR) != 0)
+#define IS_DOS_ARCHIVE(test_mode) (((test_mode) & aARCH) != 0)
+#define IS_DOS_SYSTEM(test_mode) (((test_mode) & aSYSTEM) != 0)
+#define IS_DOS_HIDDEN(test_mode) (((test_mode) & aHIDDEN) != 0)
+
+#ifndef SAFE_FREE /* Oh no this is also defined in tdb.h */
+
+/**
+ * Free memory if the pointer and zero the pointer.
+ *
+ * @note You are explicitly allowed to pass NULL pointers -- they will
+ * always be ignored.
+ **/
+#define SAFE_FREE(x) do { if ((x) != NULL) {free(x); x=NULL;} } while(0)
+#endif
+
+/* zero a structure */
+#define ZERO_STRUCT(x) memset((char *)&(x), 0, sizeof(x))
+
+/* zero a structure given a pointer to the structure */
+#define ZERO_STRUCTP(x) do { if ((x) != NULL) memset((char *)(x), 0, sizeof(*(x))); } while(0)
+
+/* zero a structure given a pointer to the structure - no zero check */
+#define ZERO_STRUCTPN(x) memset((char *)(x), 0, sizeof(*(x)))
+
+/* zero an array - note that sizeof(array) must work - ie. it must not be a
+ pointer */
+#define ZERO_ARRAY(x) memset((char *)(x), 0, sizeof(x))
+
+/* pointer difference macro */
+#define PTR_DIFF(p1,p2) ((ptrdiff_t)(((const char *)(p1)) - (const char *)(p2)))
+
+/* work out how many elements there are in a static array */
+#define ARRAY_SIZE(a) (sizeof(a)/sizeof(a[0]))
+
+/* assert macros */
+#define SMB_ASSERT(b) ((b)?(void)0: \
+ (DEBUG(0,("PANIC: assert failed at %s(%d)\n", \
+ __FILE__, __LINE__)), smb_panic("assert failed")))
+#define SMB_ASSERT_ARRAY(a,n) SMB_ASSERT((sizeof(a)/sizeof((a)[0])) >= (n))
+
+/* these are useful macros for checking validity of handles */
+#define OPEN_FSP(fsp) ((fsp) && !(fsp)->is_directory)
+#define OPEN_CONN(conn) ((conn) && (conn)->open)
+#define IS_IPC(conn) ((conn) && (conn)->ipc)
+#define IS_PRINT(conn) ((conn) && (conn)->printer)
+#define FSP_BELONGS_CONN(fsp,conn) do {\
+ extern struct current_user current_user;\
+ if (!((fsp) && (conn) && ((conn)==(fsp)->conn) && (current_user.vuid==(fsp)->vuid))) \
+ return(ERROR_DOS(ERRDOS,ERRbadfid));\
+ } while(0)
+
+#define FNUM_OK(fsp,c) (OPEN_FSP(fsp) && (c)==(fsp)->conn && current_user.vuid==(fsp)->vuid)
+
+#define CHECK_FSP(fsp,conn) do {\
+ extern struct current_user current_user;\
+ if (!FNUM_OK(fsp,conn)) \
+ return(ERROR_DOS(ERRDOS,ERRbadfid)); \
+ else if((fsp)->fd == -1) \
+ return(ERROR_DOS(ERRDOS,ERRbadaccess));\
+ } while(0)
+
+#define CHECK_READ(fsp) if (!(fsp)->can_read) \
+ return(ERROR_DOS(ERRDOS,ERRbadaccess))
+#define CHECK_WRITE(fsp) if (!(fsp)->can_write) \
+ return(ERROR_DOS(ERRDOS,ERRbadaccess))
+
+#define CHECK_ERROR(fsp) if (HAS_CACHED_ERROR(fsp)) \
+ return(CACHED_ERROR(fsp))
+
+#define ERROR_WAS_LOCK_DENIED(status) (NT_STATUS_EQUAL((status), NT_STATUS_LOCK_NOT_GRANTED) || \
+ NT_STATUS_EQUAL((status), NT_STATUS_FILE_LOCK_CONFLICT) )
+
+/* the service number for the [globals] defaults */
+#define GLOBAL_SECTION_SNUM (-1)
+/* translates a connection number into a service number */
+#define SNUM(conn) ((conn)?(conn)->service:GLOBAL_SECTION_SNUM)
+
+
+/* access various service details */
+#define SERVICE(snum) (lp_servicename(snum))
+#define PRINTERNAME(snum) (lp_printername(snum))
+#define CAN_WRITE(conn) (!conn->read_only)
+#define VALID_SNUM(snum) (lp_snum_ok(snum))
+#define GUEST_OK(snum) (VALID_SNUM(snum) && lp_guest_ok(snum))
+#define GUEST_ONLY(snum) (VALID_SNUM(snum) && lp_guest_only(snum))
+#define CAN_SETDIR(snum) (!lp_no_set_dir(snum))
+#define CAN_PRINT(conn) ((conn) && lp_print_ok((conn)->service))
+#define MAP_HIDDEN(conn) ((conn) && lp_map_hidden((conn)->service))
+#define MAP_SYSTEM(conn) ((conn) && lp_map_system((conn)->service))
+#define MAP_ARCHIVE(conn) ((conn) && lp_map_archive((conn)->service))
+#define IS_HIDDEN_PATH(conn,path) ((conn) && is_in_path((path),(conn)->hide_list))
+#define IS_VETO_PATH(conn,path) ((conn) && is_in_path((path),(conn)->veto_list))
+#define IS_VETO_OPLOCK_PATH(conn,path) ((conn) && is_in_path((path),(conn)->veto_oplock_list))
+
+/*
+ * Used by the stat cache code to check if a returned
+ * stat structure is valid.
+ */
+
+#define VALID_STAT(st) ((st).st_nlink != 0)
+#define VALID_STAT_OF_DIR(st) (VALID_STAT(st) && S_ISDIR((st).st_mode))
+
+#ifndef MIN
+#define MIN(a,b) ((a)<(b)?(a):(b))
+#endif
+#ifndef MAX
+#define MAX(a,b) ((a)>(b)?(a):(b))
+#endif
+
+#ifndef ABS
+#define ABS(a) ((a)>0?(a):(-(a)))
+#endif
+
+/* Macros to get at offsets within smb_lkrng and smb_unlkrng
+ structures. We cannot define these as actual structures
+ due to possible differences in structure packing
+ on different machines/compilers. */
+
+#define SMB_LPID_OFFSET(indx) (10 * (indx))
+#define SMB_LKOFF_OFFSET(indx) ( 2 + (10 * (indx)))
+#define SMB_LKLEN_OFFSET(indx) ( 6 + (10 * (indx)))
+#define SMB_LARGE_LPID_OFFSET(indx) (20 * (indx))
+#define SMB_LARGE_LKOFF_OFFSET_HIGH(indx) (4 + (20 * (indx)))
+#define SMB_LARGE_LKOFF_OFFSET_LOW(indx) (8 + (20 * (indx)))
+#define SMB_LARGE_LKLEN_OFFSET_HIGH(indx) (12 + (20 * (indx)))
+#define SMB_LARGE_LKLEN_OFFSET_LOW(indx) (16 + (20 * (indx)))
+
+/* Macro to cache an error in a write_bmpx_struct */
+#define CACHE_ERROR(w,c,e) ((w)->wr_errclass = (c), (w)->wr_error = (e), \
+ w->wr_discard = True, -1)
+/* Macro to test if an error has been cached for this fnum */
+#define HAS_CACHED_ERROR(fsp) ((fsp)->wbmpx_ptr && \
+ (fsp)->wbmpx_ptr->wr_discard)
+/* Macro to turn the cached error into an error packet */
+#define CACHED_ERROR(fsp) cached_error_packet(outbuf,fsp,__LINE__,__FILE__)
+
+/* these are the datagram types */
+#define DGRAM_DIRECT_UNIQUE 0x10
+
+#define ERROR_DOS(class,code) error_packet(outbuf,NT_STATUS_OK,class,code,__LINE__,__FILE__)
+#define ERROR_NT(status) error_packet(outbuf,status,0,0,__LINE__,__FILE__)
+#define ERROR_BOTH(status,class,code) error_packet(outbuf,status,class,code,__LINE__,__FILE__)
+
+/* this is how errors are generated */
+#define UNIXERROR(defclass,deferror) unix_error_packet(outbuf,defclass,deferror,__LINE__,__FILE__)
+
+#define SMB_ROUNDUP(x,r) ( ((x)%(r)) ? ( (((x)+(r))/(r))*(r) ) : (x))
+
+/* Extra macros added by Ying Chen at IBM - speed increase by inlining. */
+#define smb_buf(buf) (((char *)(buf)) + smb_size + CVAL(buf,smb_wct)*2)
+#define smb_buflen(buf) (SVAL(buf,smb_vwv0 + (int)CVAL(buf, smb_wct)*2))
+
+/* the remaining number of bytes in smb buffer 'buf' from pointer 'p'. */
+#define smb_bufrem(buf, p) (smb_buflen(buf)-PTR_DIFF(p, smb_buf(buf)))
+
+/* Note that chain_size must be available as an extern int to this macro. */
+#define smb_offset(p,buf) (PTR_DIFF(p,buf+4) + chain_size)
+
+#define smb_len(buf) (PVAL(buf,3)|(PVAL(buf,2)<<8)|((PVAL(buf,1)&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)
+
+/*******************************************************************
+find the difference in milliseconds between two struct timeval
+values
+********************************************************************/
+
+#define TvalDiff(tvalold,tvalnew) \
+ (((tvalnew)->tv_sec - (tvalold)->tv_sec)*1000 + \
+ ((int)(tvalnew)->tv_usec - (int)(tvalold)->tv_usec)/1000)
+
+/****************************************************************************
+true if two IP addresses are equal
+****************************************************************************/
+
+#define ip_equal(ip1,ip2) ((ip1).s_addr == (ip2).s_addr)
+#define ip_service_equal(ip1,ip2) ( ((ip1).ip.s_addr == (ip2).ip.s_addr) && ((ip1).port == (ip2).port) )
+
+/*****************************************************************
+ splits out the last subkey of a key
+ *****************************************************************/
+
+#define reg_get_subkey(full_keyname, key_name, subkey_name) \
+ split_at_last_component(full_keyname, key_name, '\\', subkey_name)
+
+/****************************************************************************
+ Used by dptr_zero.
+****************************************************************************/
+
+#define DPTR_MASK ((uint32)(((uint32)1)<<31))
+
+/****************************************************************************
+ Return True if the offset is at zero.
+****************************************************************************/
+
+#define dptr_zero(buf) ((IVAL(buf,1)&~DPTR_MASK) == 0)
+
+/*******************************************************************
+copy an IP address from one buffer to another
+********************************************************************/
+
+#define putip(dest,src) memcpy(dest,src,4)
+
+/*******************************************************************
+ Return True if a server has CIFS UNIX capabilities.
+********************************************************************/
+
+#define SERVER_HAS_UNIX_CIFS(c) ((c)->capabilities & CAP_UNIX)
+
+/****************************************************************************
+ Make a filename into unix format.
+****************************************************************************/
+
+#define IS_DIRECTORY_SEP(c) ((c) == '\\' || (c) == '/')
+#define unix_format(fname) string_replace(fname,'\\','/')
+#define unix_format_w(fname) string_replace_w(fname, UCS2_CHAR('\\'), UCS2_CHAR('/'))
+
+/****************************************************************************
+ Make a file into DOS format.
+****************************************************************************/
+
+#define dos_format(fname) string_replace(fname,'/','\\')
+
+/*****************************************************************************
+ Check to see if we are a DO for this domain
+*****************************************************************************/
+
+#define IS_DC (lp_server_role()==ROLE_DOMAIN_PDC || lp_server_role()==ROLE_DOMAIN_BDC)
+
+#endif /* _SMB_MACROS_H */
diff --git a/source/include/smbldap.h b/source/include/smbldap.h
new file mode 100644
index 00000000000..119479f218d
--- /dev/null
+++ b/source/include/smbldap.h
@@ -0,0 +1,159 @@
+/*
+ Unix SMB/CIFS mplementation.
+ LDAP protocol helper functions for SAMBA
+ Copyright (C) Gerald Carter 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 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 _SMBLDAP_H
+#define _SMBLDAP_H
+
+#ifdef HAVE_LDAP
+
+/* specify schema versions between 2.2. and 3.0 */
+
+#define SCHEMAVER_SAMBAACCOUNT 1
+#define SCHEMAVER_SAMBASAMACCOUNT 2
+
+/* objectclass names */
+
+#define LDAP_OBJ_SAMBASAMACCOUNT "sambaSamAccount"
+#define LDAP_OBJ_SAMBAACCOUNT "sambaAccount"
+#define LDAP_OBJ_GROUPMAP "sambaGroupMapping"
+#define LDAP_OBJ_DOMINFO "sambaDomain"
+#define LDAP_OBJ_IDPOOL "sambaUnixIdPool"
+#define LDAP_OBJ_IDMAP_ENTRY "sambaIdmapEntry"
+#define LDAP_OBJ_SID_ENTRY "sambaSidEntry"
+#define LDAP_OBJ_PRIVILEGE "sambaPrivilege"
+
+#define LDAP_OBJ_ACCOUNT "account"
+#define LDAP_OBJ_POSIXACCOUNT "posixAccount"
+#define LDAP_OBJ_POSIXGROUP "posixGroup"
+#define LDAP_OBJ_OU "organizationalUnit"
+
+/* some generic attributes that get reused a lot */
+
+#define LDAP_ATTRIBUTE_SID "sambaSID"
+#define LDAP_ATTRIBUTE_UIDNUMBER "uidNumber"
+#define LDAP_ATTRIBUTE_GIDNUMBER "gidNumber"
+#define LDAP_ATTRIBUTE_SID_LIST "sambaSIDList"
+
+/* attribute map table indexes */
+
+#define LDAP_ATTR_LIST_END 0
+#define LDAP_ATTR_UID 1
+#define LDAP_ATTR_UIDNUMBER 2
+#define LDAP_ATTR_GIDNUMBER 3
+#define LDAP_ATTR_UNIX_HOME 4
+#define LDAP_ATTR_PWD_LAST_SET 5
+#define LDAP_ATTR_PWD_CAN_CHANGE 6
+#define LDAP_ATTR_PWD_MUST_CHANGE 7
+#define LDAP_ATTR_LOGON_TIME 8
+#define LDAP_ATTR_LOGOFF_TIME 9
+#define LDAP_ATTR_KICKOFF_TIME 10
+#define LDAP_ATTR_CN 11
+#define LDAP_ATTR_DISPLAY_NAME 12
+#define LDAP_ATTR_HOME_PATH 13
+#define LDAP_ATTR_LOGON_SCRIPT 14
+#define LDAP_ATTR_PROFILE_PATH 15
+#define LDAP_ATTR_DESC 16
+#define LDAP_ATTR_USER_WKS 17
+#define LDAP_ATTR_USER_SID 18
+#define LDAP_ATTR_USER_RID 18
+#define LDAP_ATTR_PRIMARY_GROUP_SID 19
+#define LDAP_ATTR_PRIMARY_GROUP_RID 20
+#define LDAP_ATTR_LMPW 21
+#define LDAP_ATTR_NTPW 22
+#define LDAP_ATTR_DOMAIN 23
+#define LDAP_ATTR_OBJCLASS 24
+#define LDAP_ATTR_ACB_INFO 25
+#define LDAP_ATTR_NEXT_USERRID 26
+#define LDAP_ATTR_NEXT_GROUPRID 27
+#define LDAP_ATTR_DOM_SID 28
+#define LDAP_ATTR_HOME_DRIVE 29
+#define LDAP_ATTR_GROUP_SID 30
+#define LDAP_ATTR_GROUP_TYPE 31
+#define LDAP_ATTR_SID 32
+#define LDAP_ATTR_ALGORITHMIC_RID_BASE 33
+#define LDAP_ATTR_NEXT_RID 34
+#define LDAP_ATTR_BAD_PASSWORD_COUNT 35
+#define LDAP_ATTR_LOGON_COUNT 36
+#define LDAP_ATTR_MUNGED_DIAL 37
+#define LDAP_ATTR_BAD_PASSWORD_TIME 38
+#define LDAP_ATTR_MOD_TIMESTAMP 39
+#define LDAP_ATTR_SID_LIST 40
+
+typedef struct _attrib_map_entry {
+ int attrib;
+ const char *name;
+} ATTRIB_MAP_ENTRY;
+
+
+/* structures */
+
+extern ATTRIB_MAP_ENTRY attrib_map_v22[];
+extern ATTRIB_MAP_ENTRY attrib_map_v30[];
+extern ATTRIB_MAP_ENTRY dominfo_attr_list[];
+extern ATTRIB_MAP_ENTRY groupmap_attr_list[];
+extern ATTRIB_MAP_ENTRY privilege_attr_list[];
+extern ATTRIB_MAP_ENTRY groupmap_attr_list_to_delete[];
+extern ATTRIB_MAP_ENTRY idpool_attr_list[];
+extern ATTRIB_MAP_ENTRY sidmap_attr_list[];
+
+/* Function declarations -- not included in proto.h so we don't
+ have to worry about LDAP structure types */
+
+const char* get_attr_key2string( ATTRIB_MAP_ENTRY table[], int key );
+char** get_attr_list( ATTRIB_MAP_ENTRY table[] );
+void free_attr_list( char **list );
+void smbldap_set_mod (LDAPMod *** modlist, int modop, const char *attribute, const char *value);
+void smbldap_make_mod(LDAP *ldap_struct, LDAPMessage *existing,
+ LDAPMod ***mods,
+ const char *attribute, const char *newval);
+BOOL smbldap_get_single_attribute (LDAP * ldap_struct, LDAPMessage * entry,
+ const char *attribute, char *value,
+ int max_len);
+BOOL smbldap_get_single_pstring (LDAP * ldap_struct, LDAPMessage * entry,
+ const char *attribute, pstring value);
+
+/**
+ * Struct to keep the state for all the ldap stuff
+ *
+ */
+
+struct smbldap_state {
+ LDAP *ldap_struct;
+ time_t last_ping;
+ /* retrive-once info */
+ const char *uri;
+ char *bind_dn;
+ char *bind_secret;
+
+ unsigned int num_failures;
+
+ time_t last_use;
+ smb_event_id_t event_id;
+
+ struct timeval last_rebind;
+};
+
+#endif /* HAVE_LDAP */
+
+struct smbldap_state;
+
+#endif /* _SMBLDAP_H */
+
diff --git a/source/include/smbprofile.h b/source/include/smbprofile.h
new file mode 100644
index 00000000000..e494faf7da6
--- /dev/null
+++ b/source/include/smbprofile.h
@@ -0,0 +1,485 @@
+#ifndef _PROFILE_H_
+#define _PROFILE_H_
+/*
+ Unix SMB/CIFS implementation.
+ store smbd profiling information in shared memory
+ 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 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.
+
+*/
+
+/*
+ * Reasons for cache flush.
+ */
+
+#define NUM_FLUSH_REASONS 8 /* Keep this in sync with the enum below. */
+enum flush_reason_enum { SEEK_FLUSH, READ_FLUSH, WRITE_FLUSH, READRAW_FLUSH,
+ OPLOCK_RELEASE_FLUSH, CLOSE_FLUSH, SYNC_FLUSH, SIZECHANGE_FLUSH };
+
+/* this file defines the profile structure in the profile shared
+ memory area */
+
+#define PROF_SHMEM_KEY ((key_t)0x07021999)
+#define PROF_SHM_MAGIC 0x6349985
+#define PROF_SHM_VERSION 9
+
+/* time values in the following structure are in microseconds */
+
+struct profile_stats {
+/* general counters */
+ unsigned smb_count; /* how many SMB packets we have processed */
+ unsigned uid_changes; /* how many times we change our effective uid */
+/* system call counters */
+ unsigned syscall_opendir_count;
+ unsigned syscall_opendir_time;
+ unsigned syscall_readdir_count;
+ unsigned syscall_readdir_time;
+ unsigned syscall_mkdir_count;
+ unsigned syscall_mkdir_time;
+ unsigned syscall_rmdir_count;
+ unsigned syscall_rmdir_time;
+ unsigned syscall_closedir_count;
+ unsigned syscall_closedir_time;
+ unsigned syscall_open_count;
+ unsigned syscall_open_time;
+ unsigned syscall_close_count;
+ unsigned syscall_close_time;
+ unsigned syscall_read_count;
+ unsigned syscall_read_time;
+ unsigned syscall_read_bytes; /* bytes read with read syscall */
+ unsigned syscall_pread_count;
+ unsigned syscall_pread_time;
+ unsigned syscall_pread_bytes; /* bytes read with pread syscall */
+ unsigned syscall_write_count;
+ unsigned syscall_write_time;
+ unsigned syscall_write_bytes; /* bytes written with write syscall */
+ unsigned syscall_pwrite_count;
+ unsigned syscall_pwrite_time;
+ unsigned syscall_pwrite_bytes; /* bytes written with pwrite syscall */
+ unsigned syscall_lseek_count;
+ unsigned syscall_lseek_time;
+ unsigned syscall_sendfile_count;
+ unsigned syscall_sendfile_time;
+ unsigned syscall_sendfile_bytes; /* bytes read with sendfile syscall */
+ unsigned syscall_rename_count;
+ unsigned syscall_rename_time;
+ unsigned syscall_fsync_count;
+ unsigned syscall_fsync_time;
+ unsigned syscall_stat_count;
+ unsigned syscall_stat_time;
+ unsigned syscall_fstat_count;
+ unsigned syscall_fstat_time;
+ unsigned syscall_lstat_count;
+ unsigned syscall_lstat_time;
+ unsigned syscall_unlink_count;
+ unsigned syscall_unlink_time;
+ unsigned syscall_chmod_count;
+ unsigned syscall_chmod_time;
+ unsigned syscall_fchmod_count;
+ unsigned syscall_fchmod_time;
+ unsigned syscall_chown_count;
+ unsigned syscall_chown_time;
+ unsigned syscall_fchown_count;
+ unsigned syscall_fchown_time;
+ unsigned syscall_chdir_count;
+ unsigned syscall_chdir_time;
+ unsigned syscall_getwd_count;
+ unsigned syscall_getwd_time;
+ unsigned syscall_utime_count;
+ unsigned syscall_utime_time;
+ unsigned syscall_ftruncate_count;
+ unsigned syscall_ftruncate_time;
+ unsigned syscall_fcntl_lock_count;
+ unsigned syscall_fcntl_lock_time;
+ unsigned syscall_readlink_count;
+ unsigned syscall_readlink_time;
+ unsigned syscall_symlink_count;
+ unsigned syscall_symlink_time;
+ unsigned syscall_link_count;
+ unsigned syscall_link_time;
+ unsigned syscall_mknod_count;
+ unsigned syscall_mknod_time;
+ unsigned syscall_realpath_count;
+ unsigned syscall_realpath_time;
+ unsigned syscall_get_quota_count;
+ unsigned syscall_get_quota_time;
+ unsigned syscall_set_quota_count;
+ unsigned syscall_set_quota_time;
+/* stat cache counters */
+ unsigned statcache_lookups;
+ unsigned statcache_misses;
+ unsigned statcache_hits;
+/* write cache counters */
+ unsigned writecache_read_hits;
+ unsigned writecache_abutted_writes;
+ unsigned writecache_total_writes;
+ unsigned writecache_non_oplock_writes;
+ unsigned writecache_direct_writes;
+ unsigned writecache_init_writes;
+ unsigned writecache_flushed_writes[NUM_FLUSH_REASONS];
+ unsigned writecache_num_perfect_writes;
+ unsigned writecache_num_write_caches;
+ unsigned writecache_allocated_write_caches;
+/* counters for individual SMB types */
+ unsigned SMBmkdir_count; /* create directory */
+ unsigned SMBmkdir_time;
+ unsigned SMBrmdir_count; /* delete directory */
+ unsigned SMBrmdir_time;
+ unsigned SMBopen_count; /* open file */
+ unsigned SMBopen_time;
+ unsigned SMBcreate_count; /* create file */
+ unsigned SMBcreate_time;
+ unsigned SMBclose_count; /* close file */
+ unsigned SMBclose_time;
+ unsigned SMBflush_count; /* flush file */
+ unsigned SMBflush_time;
+ unsigned SMBunlink_count; /* delete file */
+ unsigned SMBunlink_time;
+ unsigned SMBmv_count; /* rename file */
+ unsigned SMBmv_time;
+ unsigned SMBgetatr_count; /* get file attributes */
+ unsigned SMBgetatr_time;
+ unsigned SMBsetatr_count; /* set file attributes */
+ unsigned SMBsetatr_time;
+ unsigned SMBread_count; /* read from file */
+ unsigned SMBread_time;
+ unsigned SMBwrite_count; /* write to file */
+ unsigned SMBwrite_time;
+ unsigned SMBlock_count; /* lock byte range */
+ unsigned SMBlock_time;
+ unsigned SMBunlock_count; /* unlock byte range */
+ unsigned SMBunlock_time;
+ unsigned SMBctemp_count; /* create temporary file */
+ unsigned SMBctemp_time;
+ /* SMBmknew stats are currently combined with SMBcreate */
+ unsigned SMBmknew_count; /* make new file */
+ unsigned SMBmknew_time;
+ unsigned SMBchkpth_count; /* check directory path */
+ unsigned SMBchkpth_time;
+ unsigned SMBexit_count; /* process exit */
+ unsigned SMBexit_time;
+ unsigned SMBlseek_count; /* seek */
+ unsigned SMBlseek_time;
+ unsigned SMBlockread_count; /* Lock a range and read */
+ unsigned SMBlockread_time;
+ unsigned SMBwriteunlock_count; /* Unlock a range then write */
+ unsigned SMBwriteunlock_time;
+ unsigned SMBreadbraw_count; /* read a block of data with no smb header */
+ unsigned SMBreadbraw_time;
+ unsigned SMBreadBmpx_count; /* read block multiplexed */
+ unsigned SMBreadBmpx_time;
+ unsigned SMBreadBs_count; /* read block (secondary response) */
+ unsigned SMBreadBs_time;
+ unsigned SMBwritebraw_count; /* write a block of data with no smb header */
+ unsigned SMBwritebraw_time;
+ unsigned SMBwriteBmpx_count; /* write block multiplexed */
+ unsigned SMBwriteBmpx_time;
+ unsigned SMBwriteBs_count; /* write block (secondary request) */
+ unsigned SMBwriteBs_time;
+ unsigned SMBwritec_count; /* secondary write request */
+ unsigned SMBwritec_time;
+ unsigned SMBsetattrE_count; /* set file attributes expanded */
+ unsigned SMBsetattrE_time;
+ unsigned SMBgetattrE_count; /* get file attributes expanded */
+ unsigned SMBgetattrE_time;
+ unsigned SMBlockingX_count; /* lock/unlock byte ranges and X */
+ unsigned SMBlockingX_time;
+ unsigned SMBtrans_count; /* transaction - name, bytes in/out */
+ unsigned SMBtrans_time;
+ unsigned SMBtranss_count; /* transaction (secondary request/response) */
+ unsigned SMBtranss_time;
+ unsigned SMBioctl_count; /* IOCTL */
+ unsigned SMBioctl_time;
+ unsigned SMBioctls_count; /* IOCTL (secondary request/response) */
+ unsigned SMBioctls_time;
+ unsigned SMBcopy_count; /* copy */
+ unsigned SMBcopy_time;
+ unsigned SMBmove_count; /* move */
+ unsigned SMBmove_time;
+ unsigned SMBecho_count; /* echo */
+ unsigned SMBecho_time;
+ unsigned SMBwriteclose_count; /* write a file then close it */
+ unsigned SMBwriteclose_time;
+ unsigned SMBopenX_count; /* open and X */
+ unsigned SMBopenX_time;
+ unsigned SMBreadX_count; /* read and X */
+ unsigned SMBreadX_time;
+ unsigned SMBwriteX_count; /* write and X */
+ unsigned SMBwriteX_time;
+ unsigned SMBtrans2_count; /* TRANS2 protocol set */
+ unsigned SMBtrans2_time;
+ unsigned SMBtranss2_count; /* TRANS2 protocol set, secondary command */
+ unsigned SMBtranss2_time;
+ unsigned SMBfindclose_count; /* Terminate a TRANSACT2_FINDFIRST */
+ unsigned SMBfindclose_time;
+ unsigned SMBfindnclose_count; /* Terminate a TRANSACT2_FINDNOTIFYFIRST */
+ unsigned SMBfindnclose_time;
+ unsigned SMBtcon_count; /* tree connect */
+ unsigned SMBtcon_time;
+ unsigned SMBtdis_count; /* tree disconnect */
+ unsigned SMBtdis_time;
+ unsigned SMBnegprot_count; /* negotiate protocol */
+ unsigned SMBnegprot_time;
+ unsigned SMBsesssetupX_count; /* Session Set Up & X (including User Logon) */
+ unsigned SMBsesssetupX_time;
+ unsigned SMBulogoffX_count; /* user logoff */
+ unsigned SMBulogoffX_time;
+ unsigned SMBtconX_count; /* tree connect and X*/
+ unsigned SMBtconX_time;
+ unsigned SMBdskattr_count; /* get disk attributes */
+ unsigned SMBdskattr_time;
+ unsigned SMBsearch_count; /* search directory */
+ unsigned SMBsearch_time;
+ /* SBMffirst stats combined with SMBsearch */
+ unsigned SMBffirst_count; /* find first */
+ unsigned SMBffirst_time;
+ /* SBMfunique stats combined with SMBsearch */
+ unsigned SMBfunique_count; /* find unique */
+ unsigned SMBfunique_time;
+ unsigned SMBfclose_count; /* find close */
+ unsigned SMBfclose_time;
+ unsigned SMBnttrans_count; /* NT transact */
+ unsigned SMBnttrans_time;
+ unsigned SMBnttranss_count; /* NT transact secondary */
+ unsigned SMBnttranss_time;
+ unsigned SMBntcreateX_count; /* NT create and X */
+ unsigned SMBntcreateX_time;
+ unsigned SMBntcancel_count; /* NT cancel */
+ unsigned SMBntcancel_time;
+ unsigned SMBntrename_count; /* NT rename file */
+ unsigned SMBntrename_time;
+ unsigned SMBsplopen_count; /* open print spool file */
+ unsigned SMBsplopen_time;
+ unsigned SMBsplwr_count; /* write to print spool file */
+ unsigned SMBsplwr_time;
+ unsigned SMBsplclose_count; /* close print spool file */
+ unsigned SMBsplclose_time;
+ unsigned SMBsplretq_count; /* return print queue */
+ unsigned SMBsplretq_time;
+ unsigned SMBsends_count; /* send single block message */
+ unsigned SMBsends_time;
+ unsigned SMBsendb_count; /* send broadcast message */
+ unsigned SMBsendb_time;
+ unsigned SMBfwdname_count; /* forward user name */
+ unsigned SMBfwdname_time;
+ unsigned SMBcancelf_count; /* cancel forward */
+ unsigned SMBcancelf_time;
+ unsigned SMBgetmac_count; /* get machine name */
+ unsigned SMBgetmac_time;
+ unsigned SMBsendstrt_count; /* send start of multi-block message */
+ unsigned SMBsendstrt_time;
+ unsigned SMBsendend_count; /* send end of multi-block message */
+ unsigned SMBsendend_time;
+ unsigned SMBsendtxt_count; /* send text of multi-block message */
+ unsigned SMBsendtxt_time;
+ unsigned SMBinvalid_count; /* invalid command */
+ unsigned SMBinvalid_time;
+/* Pathworks setdir command */
+ unsigned pathworks_setdir_count;
+ unsigned pathworks_setdir_time;
+/* These are the TRANS2 sub commands */
+ unsigned Trans2_open_count;
+ unsigned Trans2_open_time;
+ unsigned Trans2_findfirst_count;
+ unsigned Trans2_findfirst_time;
+ unsigned Trans2_findnext_count;
+ unsigned Trans2_findnext_time;
+ unsigned Trans2_qfsinfo_count;
+ unsigned Trans2_qfsinfo_time;
+ unsigned Trans2_setfsinfo_count;
+ unsigned Trans2_setfsinfo_time;
+ unsigned Trans2_qpathinfo_count;
+ unsigned Trans2_qpathinfo_time;
+ unsigned Trans2_setpathinfo_count;
+ unsigned Trans2_setpathinfo_time;
+ unsigned Trans2_qfileinfo_count;
+ unsigned Trans2_qfileinfo_time;
+ unsigned Trans2_setfileinfo_count;
+ unsigned Trans2_setfileinfo_time;
+ unsigned Trans2_fsctl_count;
+ unsigned Trans2_fsctl_time;
+ unsigned Trans2_ioctl_count;
+ unsigned Trans2_ioctl_time;
+ unsigned Trans2_findnotifyfirst_count;
+ unsigned Trans2_findnotifyfirst_time;
+ unsigned Trans2_findnotifynext_count;
+ unsigned Trans2_findnotifynext_time;
+ unsigned Trans2_mkdir_count;
+ unsigned Trans2_mkdir_time;
+ unsigned Trans2_session_setup_count;
+ unsigned Trans2_session_setup_time;
+ unsigned Trans2_get_dfs_referral_count;
+ unsigned Trans2_get_dfs_referral_time;
+ unsigned Trans2_report_dfs_inconsistancy_count;
+ unsigned Trans2_report_dfs_inconsistancy_time;
+/* These are the NT transact sub commands. */
+ unsigned NT_transact_create_count;
+ unsigned NT_transact_create_time;
+ unsigned NT_transact_ioctl_count;
+ unsigned NT_transact_ioctl_time;
+ unsigned NT_transact_set_security_desc_count;
+ unsigned NT_transact_set_security_desc_time;
+ unsigned NT_transact_notify_change_count;
+ unsigned NT_transact_notify_change_time;
+ unsigned NT_transact_rename_count;
+ unsigned NT_transact_rename_time;
+ unsigned NT_transact_query_security_desc_count;
+ unsigned NT_transact_query_security_desc_time;
+ unsigned NT_transact_get_user_quota_count;
+ unsigned NT_transact_get_user_quota_time;
+ unsigned NT_transact_set_user_quota_count;
+ unsigned NT_transact_set_user_quota_time;
+/* These are ACL manipulation calls */
+ unsigned get_nt_acl_count;
+ unsigned get_nt_acl_time;
+ unsigned fget_nt_acl_count;
+ unsigned fget_nt_acl_time;
+ unsigned set_nt_acl_count;
+ unsigned set_nt_acl_time;
+ unsigned fset_nt_acl_count;
+ unsigned fset_nt_acl_time;
+ unsigned chmod_acl_count;
+ unsigned chmod_acl_time;
+ unsigned fchmod_acl_count;
+ unsigned fchmod_acl_time;
+/* These are nmbd stats */
+ unsigned name_release_count;
+ unsigned name_release_time;
+ unsigned name_refresh_count;
+ unsigned name_refresh_time;
+ unsigned name_registration_count;
+ unsigned name_registration_time;
+ unsigned node_status_count;
+ unsigned node_status_time;
+ unsigned name_query_count;
+ unsigned name_query_time;
+ unsigned host_announce_count;
+ unsigned host_announce_time;
+ unsigned workgroup_announce_count;
+ unsigned workgroup_announce_time;
+ unsigned local_master_announce_count;
+ unsigned local_master_announce_time;
+ unsigned master_browser_announce_count;
+ unsigned master_browser_announce_time;
+ unsigned lm_host_announce_count;
+ unsigned lm_host_announce_time;
+ unsigned get_backup_list_count;
+ unsigned get_backup_list_time;
+ unsigned reset_browser_count;
+ unsigned reset_browser_time;
+ unsigned announce_request_count;
+ unsigned announce_request_time;
+ unsigned lm_announce_request_count;
+ unsigned lm_announce_request_time;
+ unsigned domain_logon_count;
+ unsigned domain_logon_time;
+ unsigned sync_browse_lists_count;
+ unsigned sync_browse_lists_time;
+ unsigned run_elections_count;
+ unsigned run_elections_time;
+ unsigned election_count;
+ unsigned election_time;
+};
+
+struct profile_header {
+ int prof_shm_magic;
+ int prof_shm_version;
+ struct profile_stats stats;
+};
+
+extern struct profile_header *profile_h;
+extern struct profile_stats *profile_p;
+extern struct timeval profile_starttime;
+extern struct timeval profile_endtime;
+extern struct timeval profile_starttime_nested;
+extern struct timeval profile_endtime_nested;
+extern BOOL do_profile_flag;
+extern BOOL do_profile_times;
+
+/* these are helper macros - do not call them directly in the code
+ * use the DO_PROFILE_* START_PROFILE and END_PROFILE ones
+ * below which test for the profile flage first
+ */
+#define INC_PROFILE_COUNT(x) profile_p->x++
+#define DEC_PROFILE_COUNT(x) profile_p->x--
+#define ADD_PROFILE_COUNT(x,y) profile_p->x += (y)
+#define PROFILE_TIME \
+ ((profile_endtime.tv_sec - profile_starttime.tv_sec) *1000000 + \
+ ((int)profile_endtime.tv_usec - (int)profile_starttime.tv_usec))
+#define PROFILE_TIME_NESTED \
+ ((profile_endtime_nested.tv_sec - profile_starttime_nested.tv_sec) *1000000 + \
+ ((int)profile_endtime_nested.tv_usec - (int)profile_starttime_nested.tv_usec))
+
+#ifdef WITH_PROFILE
+#define DO_PROFILE_INC(x) \
+ if (do_profile_flag) { \
+ INC_PROFILE_COUNT(x); \
+ }
+#define DO_PROFILE_DEC(x) \
+ if (do_profile_flag) { \
+ DEC_PROFILE_COUNT(x); \
+ }
+#define DO_PROFILE_DEC_INC(x,y) \
+ if (do_profile_flag) { \
+ DEC_PROFILE_COUNT(x); \
+ INC_PROFILE_COUNT(y); \
+ }
+#define DO_PROFILE_ADD(x,n) \
+ if (do_profile_flag) { \
+ ADD_PROFILE_COUNT(x,n); \
+ }
+#define START_PROFILE(x) \
+ if (do_profile_flag) { \
+ if (do_profile_times) \
+ GetTimeOfDay(&profile_starttime); \
+ INC_PROFILE_COUNT(x##_count); \
+ }
+#define START_PROFILE_NESTED(x) \
+ if (do_profile_flag) { \
+ if (do_profile_times) \
+ GetTimeOfDay(&profile_starttime_nested); \
+ INC_PROFILE_COUNT(x##_count); \
+ }
+#define START_PROFILE_BYTES(x,n) \
+ if (do_profile_flag) { \
+ if (do_profile_times) \
+ GetTimeOfDay(&profile_starttime); \
+ INC_PROFILE_COUNT(x##_count); \
+ ADD_PROFILE_COUNT(x##_bytes,n); \
+ }
+#define END_PROFILE(x) \
+ if (do_profile_times) { \
+ GetTimeOfDay(&profile_endtime); \
+ ADD_PROFILE_COUNT(x##_time,PROFILE_TIME); \
+ }
+#define END_PROFILE_NESTED(x) \
+ if (do_profile_times) { \
+ GetTimeOfDay(&profile_endtime_nested); \
+ ADD_PROFILE_COUNT(x##_time,PROFILE_TIME_NESTED); \
+ }
+#else
+#define DO_PROFILE_INC(x)
+#define DO_PROFILE_DEC(x)
+#define DO_PROFILE_DEC_INC(x,y)
+#define DO_PROFILE_ADD(x,n)
+#define START_PROFILE(x)
+#define START_PROFILE_NESTED(x)
+#define START_PROFILE_BYTES(x,n)
+#define END_PROFILE(x)
+#define END_PROFILE_NESTED(x)
+#endif
+
+#endif
diff --git a/source/include/spnego.h b/source/include/spnego.h
new file mode 100644
index 00000000000..b6492ee3c8a
--- /dev/null
+++ b/source/include/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 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 SAMBA_SPNEGO_H
+#define SAMBA_SPNEGO_H
+
+#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
+
+#define SPNEGO_NEG_TOKEN_INIT 0
+#define SPNEGO_NEG_TOKEN_TARG 1
+
+typedef enum _spnego_negResult {
+ SPNEGO_ACCEPT_COMPLETED = 0,
+ SPNEGO_ACCEPT_INCOMPLETE = 1,
+ SPNEGO_REJECT = 2
+} negResult_t;
+
+typedef struct spnego_negTokenInit {
+ const char **mechTypes;
+ int reqFlags;
+ DATA_BLOB mechToken;
+ DATA_BLOB mechListMIC;
+} negTokenInit_t;
+
+typedef struct spnego_negTokenTarg {
+ uint8 negResult;
+ const char *supportedMech;
+ DATA_BLOB responseToken;
+ DATA_BLOB mechListMIC;
+} negTokenTarg_t;
+
+typedef struct spnego_spnego {
+ int type;
+ negTokenInit_t negTokenInit;
+ negTokenTarg_t negTokenTarg;
+} SPNEGO_DATA;
+
+#endif
diff --git a/source/include/srvstr.h b/source/include/srvstr.h
new file mode 100644
index 00000000000..04db59cf012
--- /dev/null
+++ b/source/include/srvstr.h
@@ -0,0 +1,33 @@
+/*
+ Unix SMB/CIFS implementation.
+ server specific string 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 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"
+
+#define srvstr_pull(base_ptr, dest, src, dest_len, src_len, flags) \
+ pull_string(base_ptr, dest, src, dest_len, src_len, flags)
+
+/* pull a string from the smb_buf part of a packet. In this case the
+ string can either be null terminated or it can be terminated by the
+ end of the smbbuf area
+*/
+
+#define srvstr_pull_buf(inbuf, dest, src, dest_len, flags) \
+ pull_string(inbuf, dest, src, dest_len, smb_bufrem(inbuf, src), flags)
+
diff --git a/source/include/stamp-h.in b/source/include/stamp-h.in
new file mode 100644
index 00000000000..c9061b3ad3d
--- /dev/null
+++ b/source/include/stamp-h.in
@@ -0,0 +1 @@
+Sun Jul 18 20:32:29 UTC 1999
diff --git a/source/include/sysquotas.h b/source/include/sysquotas.h
new file mode 100644
index 00000000000..bfb9466b392
--- /dev/null
+++ b/source/include/sysquotas.h
@@ -0,0 +1,76 @@
+/*
+ Unix SMB/CIFS implementation.
+ SYS QUOTA code constants
+ 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 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 _SYSQUOTAS_H
+#define _SYSQUOTAS_H
+
+#ifdef HAVE_SYS_QUOTAS
+
+#if defined(HAVE_MNTENT_H)&&defined(HAVE_SETMNTENT)&&defined(HAVE_GETMNTENT)&&defined(HAVE_ENDMNTENT)
+#include <mntent.h>
+#define HAVE_MNTENT 1
+/*#endif defined(HAVE_MNTENT_H)&&defined(HAVE_SETMNTENT)&&defined(HAVE_GETMNTENT)&&defined(HAVE_ENDMNTENT) */
+#elif defined(HAVE_DEVNM_H)&&defined(HAVE_DEVNM)
+#include <devnm.h>
+#endif /* defined(HAVE_DEVNM_H)&&defined(HAVE_DEVNM) */
+
+#endif /* HAVE_SYS_QUOTAS */
+
+
+/**************************************************
+ Some stuff for the sys_quota api.
+ **************************************************/
+
+#define SMB_QUOTAS_NO_LIMIT ((SMB_BIG_UINT)(0))
+#define SMB_QUOTAS_NO_SPACE ((SMB_BIG_UINT)(1))
+
+#define SMB_QUOTAS_SET_NO_LIMIT(dp) \
+{\
+ (dp)->softlimit = SMB_QUOTAS_NO_LIMIT;\
+ (dp)->hardlimit = SMB_QUOTAS_NO_LIMIT;\
+ (dp)->isoftlimit = SMB_QUOTAS_NO_LIMIT;\
+ (dp)->ihardlimit = SMB_QUOTAS_NO_LIMIT;\
+}
+
+#define SMB_QUOTAS_SET_NO_SPACE(dp) \
+{\
+ (dp)->softlimit = SMB_QUOTAS_NO_SPACE;\
+ (dp)->hardlimit = SMB_QUOTAS_NO_SPACE;\
+ (dp)->isoftlimit = SMB_QUOTAS_NO_SPACE;\
+ (dp)->ihardlimit = SMB_QUOTAS_NO_SPACE;\
+}
+
+typedef struct _SMB_DISK_QUOTA {
+ enum SMB_QUOTA_TYPE qtype;
+ SMB_BIG_UINT bsize;
+ SMB_BIG_UINT hardlimit; /* In bsize units. */
+ SMB_BIG_UINT softlimit; /* In bsize units. */
+ SMB_BIG_UINT curblocks; /* In bsize units. */
+ SMB_BIG_UINT ihardlimit; /* inode hard limit. */
+ SMB_BIG_UINT isoftlimit; /* inode soft limit. */
+ SMB_BIG_UINT curinodes; /* Current used inodes. */
+ uint32 qflags;
+} SMB_DISK_QUOTA;
+
+#ifndef QUOTABLOCK_SIZE
+#define QUOTABLOCK_SIZE 1024
+#endif
+
+#endif /*_SYSQUOTAS_H */
diff --git a/source/include/talloc.h b/source/include/talloc.h
new file mode 100644
index 00000000000..433b52ec954
--- /dev/null
+++ b/source/include/talloc.h
@@ -0,0 +1,72 @@
+#ifndef _TALLOC_H_
+#define _TALLOC_H_
+/*
+ Unix SMB/CIFS implementation.
+ Samba temporary memory allocation functions
+ Copyright (C) Andrew Tridgell 2000
+ Copyright (C) 2001 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 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.
+*/
+
+/**
+ * @ingroup talloc
+ * @{
+ * @sa talloc.c
+ */
+
+/**
+ * talloc allocation pool. All allocated blocks can be freed in one go.
+ **/
+
+struct talloc_chunk {
+ struct talloc_chunk *next;
+ size_t size;
+ void *ptr;
+};
+
+struct talloc_ctx {
+ struct talloc_chunk *list;
+ size_t total_alloc_size;
+
+ /** The name recorded for this pool, if any. Should describe
+ * the purpose for which it was allocated. The string is
+ * allocated within the pool. **/
+ char *name;
+
+ /** Pointer to the next allocate talloc pool, so that we can
+ * summarize all talloc memory usage. **/
+ struct talloc_ctx *next_ctx;
+};
+
+typedef struct talloc_ctx TALLOC_CTX;
+
+TALLOC_CTX *talloc_init(char const *fmt, ...) PRINTF_ATTRIBUTE(1, 2);
+
+char *talloc_vasprintf(TALLOC_CTX *t, const char *fmt, va_list ap)
+ PRINTF_ATTRIBUTE(2, 0);
+
+char *talloc_asprintf(TALLOC_CTX *t, const char *fmt, ...)
+ PRINTF_ATTRIBUTE(2, 3);
+
+char *talloc_vasprintf_append(TALLOC_CTX *t, char *, const char *, va_list ap)
+ PRINTF_ATTRIBUTE(3, 0);
+
+char *talloc_asprintf_append(TALLOC_CTX *t, char *, const char *, ...)
+ PRINTF_ATTRIBUTE(3, 4);
+
+/** @} */
+
+#endif /* ndef _TALLOC_H_ */
diff --git a/source/include/tdbsam2_parse_info.h b/source/include/tdbsam2_parse_info.h
new file mode 100644
index 00000000000..35eeaeb2d2c
--- /dev/null
+++ b/source/include/tdbsam2_parse_info.h
@@ -0,0 +1,2 @@
+/* This is an automatically generated file - DO NOT EDIT! */
+
diff --git a/source/include/trans2.h b/source/include/trans2.h
new file mode 100644
index 00000000000..168e6477210
--- /dev/null
+++ b/source/include/trans2.h
@@ -0,0 +1,455 @@
+/*
+ Unix SMB/CIFS implementation.
+ SMB transaction2 handling
+ Copyright (C) Jeremy Allison 1994-2002.
+
+ Extensively modified by Andrew Tridgell, 1995
+
+ This program is free software; you can 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 _TRANS2_H_
+#define _TRANS2_H_
+
+/* Define the structures needed for the trans2 calls. */
+
+/*******************************************************
+ For DosFindFirst/DosFindNext - level 1
+
+MAXFILENAMELEN = 255;
+FDATE == uint16
+FTIME == uint16
+ULONG == uint32
+USHORT == uint16
+
+typedef struct _FILEFINDBUF {
+Byte offset Type name description
+-------------+-------+-------------------+--------------
+0 FDATE fdateCreation;
+2 FTIME ftimeCreation;
+4 FDATE fdateLastAccess;
+6 FTIME ftimeLastAccess;
+8 FDATE fdateLastWrite;
+10 FTIME ftimeLastWrite;
+12 ULONG cbFile file length in bytes
+16 ULONG cbFileAlloc size of file allocation unit
+20 USHORT attrFile
+22 UCHAR cchName length of name to follow (not including zero)
+23 UCHAR achName[MAXFILENAMELEN]; Null terminated name
+} FILEFINDBUF;
+*********************************************************/
+
+#define l1_fdateCreation 0
+#define l1_fdateLastAccess 4
+#define l1_fdateLastWrite 8
+#define l1_cbFile 12
+#define l1_cbFileAlloc 16
+#define l1_attrFile 20
+#define l1_cchName 22
+#define l1_achName 23
+
+/**********************************************************
+For DosFindFirst/DosFindNext - level 2
+
+typedef struct _FILEFINDBUF2 {
+Byte offset Type name description
+-------------+-------+-------------------+--------------
+0 FDATE fdateCreation;
+2 FTIME ftimeCreation;
+4 FDATE fdateLastAccess;
+6 FTIME ftimeLastAccess;
+8 FDATE fdateLastWrite;
+10 FTIME ftimeLastWrite;
+12 ULONG cbFile file length in bytes
+16 ULONG cbFileAlloc size of file allocation unit
+20 USHORT attrFile
+22 ULONG cbList Extended attribute list (always 0)
+26 UCHAR cchName length of name to follow (not including zero)
+27 UCHAR achName[MAXFILENAMELEN]; Null terminated name
+} FILEFINDBUF2;
+*************************************************************/
+
+#define l2_fdateCreation 0
+#define l2_fdateLastAccess 4
+#define l2_fdateLastWrite 8
+#define l2_cbFile 12
+#define l2_cbFileAlloc 16
+#define l2_attrFile 20
+#define l2_cbList 22
+#define l2_cchName 26
+#define l2_achName 27
+
+
+/**********************************************************
+For DosFindFirst/DosFindNext - level 260
+
+typedef struct _FILEFINDBUF260 {
+Byte offset Type name description
+-------------+-------+-------------------+--------------
+0 ULONG NextEntryOffset;
+4 ULONG FileIndex;
+8 LARGE_INTEGER CreationTime;
+16 LARGE_INTEGER LastAccessTime;
+24 LARGE_INTEGER LastWriteTime;
+32 LARGE_INTEGER ChangeTime;
+40 LARGE_INTEGER EndOfFile;
+48 LARGE_INTEGER AllocationSize;
+56 ULONG FileAttributes;
+60 ULONG FileNameLength;
+64 ULONG EaSize;
+68 CHAR ShortNameLength;
+70 UNICODE ShortName[12];
+94 UNICODE FileName[];
+*************************************************************/
+
+#define l260_achName 94
+
+
+/**********************************************************
+For DosQueryPathInfo/DosQueryFileInfo/DosSetPathInfo/
+DosSetFileInfo - level 1
+
+typedef struct _FILESTATUS {
+Byte offset Type name description
+-------------+-------+-------------------+--------------
+0 FDATE fdateCreation;
+2 FTIME ftimeCreation;
+4 FDATE fdateLastAccess;
+6 FTIME ftimeLastAccess;
+8 FDATE fdateLastWrite;
+10 FTIME ftimeLastWrite;
+12 ULONG cbFile file length in bytes
+16 ULONG cbFileAlloc size of file allocation unit
+20 USHORT attrFile
+} FILESTATUS;
+*************************************************************/
+
+/* Use the l1_ defines from DosFindFirst */
+
+/**********************************************************
+For DosQueryPathInfo/DosQueryFileInfo/DosSetPathInfo/
+DosSetFileInfo - level 2
+
+typedef struct _FILESTATUS2 {
+Byte offset Type name description
+-------------+-------+-------------------+--------------
+0 FDATE fdateCreation;
+2 FTIME ftimeCreation;
+4 FDATE fdateLastAccess;
+6 FTIME ftimeLastAccess;
+8 FDATE fdateLastWrite;
+10 FTIME ftimeLastWrite;
+12 ULONG cbFile file length in bytes
+16 ULONG cbFileAlloc size of file allocation unit
+20 USHORT attrFile
+22 ULONG cbList Length of EA's (0)
+} FILESTATUS2;
+*************************************************************/
+
+/* Use the l2_ #defines from DosFindFirst */
+
+/**********************************************************
+For DosQFSInfo/DosSetFSInfo - level 1
+
+typedef struct _FSALLOCATE {
+Byte offset Type name description
+-------------+-------+-------------------+--------------
+0 ULONG idFileSystem id of file system
+4 ULONG cSectorUnit number of sectors per allocation unit
+8 ULONG cUnit number of allocation units
+12 ULONG cUnitAvail Available allocation units
+16 USHORT cbSector bytes per sector
+} FSALLOCATE;
+*************************************************************/
+
+#define l1_idFileSystem 0
+#define l1_cSectorUnit 4
+#define l1_cUnit 8
+#define l1_cUnitAvail 12
+#define l1_cbSector 16
+
+/**********************************************************
+For DosQFSInfo/DosSetFSInfo - level 2
+
+typedef struct _FSINFO {
+Byte offset Type name description
+-------------+-------+-------------------+--------------
+0 FDATE vol_fdateCreation
+2 FTIME vol_ftimeCreation
+4 UCHAR vol_cch length of volume name (excluding NULL)
+5 UCHAR vol_szVolLabel[12] volume name
+} FSINFO;
+*************************************************************/
+
+#define SMB_INFO_STANDARD 1 /* FILESTATUS3 struct */
+#define SMB_INFO_SET_EA 2 /* EAOP2 struct, only valid on set not query */
+#define SMB_INFO_QUERY_EA_SIZE 2 /* FILESTATUS4 struct, only valid on query not set */
+#define SMB_INFO_QUERY_EAS_FROM_LIST 3 /* only valid on query not set */
+#define SMB_INFO_QUERY_ALL_EAS 4 /* only valid on query not set */
+#define SMB_INFO_IS_NAME_VALID 6
+#define SMB_INFO_STANDARD_LONG 11 /* similar to level 1, ie struct FileStatus3 */
+#define SMB_QUERY_EA_SIZE_LONG 12 /* similar to level 2, ie struct FileStatus4 */
+#define SMB_QUERY_FS_LABEL_INFO 0x101
+#define SMB_QUERY_FS_VOLUME_INFO 0x102
+#define SMB_QUERY_FS_SIZE_INFO 0x103
+#define SMB_QUERY_FS_DEVICE_INFO 0x104
+#define SMB_QUERY_FS_ATTRIBUTE_INFO 0x105
+#if 0
+#define SMB_QUERY_FS_QUOTA_INFO
+#endif
+
+#define l2_vol_fdateCreation 0
+#define l2_vol_cch 4
+#define l2_vol_szVolLabel 5
+
+
+#define SMB_QUERY_FILE_BASIC_INFO 0x101
+#define SMB_QUERY_FILE_STANDARD_INFO 0x102
+#define SMB_QUERY_FILE_EA_INFO 0x103
+#define SMB_QUERY_FILE_NAME_INFO 0x104
+#define SMB_QUERY_FILE_ALLOCATION_INFO 0x105
+#define SMB_QUERY_FILE_END_OF_FILEINFO 0x106
+#define SMB_QUERY_FILE_ALL_INFO 0x107
+#define SMB_QUERY_FILE_ALT_NAME_INFO 0x108
+#define SMB_QUERY_FILE_STREAM_INFO 0x109
+#define SMB_QUERY_COMPRESSION_INFO 0x10b
+
+#define SMB_FIND_FILE_DIRECTORY_INFO 0x101
+#define SMB_FIND_FILE_FULL_DIRECTORY_INFO 0x102
+#define SMB_FIND_FILE_NAMES_INFO 0x103
+#define SMB_FIND_FILE_BOTH_DIRECTORY_INFO 0x104
+#define SMB_FIND_FILE_LEVEL_261 0x105
+#define SMB_FIND_FILE_LEVEL_262 0x106
+
+#define SMB_SET_FILE_BASIC_INFO 0x101
+#define SMB_SET_FILE_DISPOSITION_INFO 0x102
+#define SMB_SET_FILE_ALLOCATION_INFO 0x103
+#define SMB_SET_FILE_END_OF_FILE_INFO 0x104
+
+/* Query FS info. */
+#define SMB_INFO_ALLOCATION 1
+#define SMB_INFO_VOLUME 2
+
+/*
+ * 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_MAC_QUERY_FS_INFO 0x301
+
+#define DIRLEN_GUESS (45+MAX(l1_achName,l2_achName))
+
+/*
+ * DeviceType and Characteristics returned in a
+ * SMB_QUERY_FS_DEVICE_INFO call.
+ */
+
+#define DEVICETYPE_CD_ROM 0x2
+#define DEVICETYPE_CD_ROM_FILE_SYSTEM 0x3
+#define DEVICETYPE_DISK 0x7
+#define DEVICETYPE_DISK_FILE_SYSTEM 0x8
+#define DEVICETYPE_FILE_SYSTEM 0x9
+
+/* Characteristics. */
+#define TYPE_REMOVABLE_MEDIA 0x1
+#define TYPE_READ_ONLY_DEVICE 0x2
+#define TYPE_FLOPPY 0x4
+#define TYPE_WORM 0x8
+#define TYPE_REMOTE 0x10
+#define TYPE_MOUNTED 0x20
+#define TYPE_VIRTUAL 0x40
+
+/* NT passthrough levels... */
+
+#define SMB_FILE_DIRECTORY_INFORMATION 1001
+#define SMB_FILE_FULL_DIRECTORY_INFORMATION 1002
+#define SMB_FILE_BOTH_DIRECTORY_INFORMATION 1003
+#define SMB_FILE_BASIC_INFORMATION 1004
+#define SMB_FILE_STANDARD_INFORMATION 1005
+#define SMB_FILE_INTERNAL_INFORMATION 1006
+#define SMB_FILE_EA_INFORMATION 1007
+#define SMB_FILE_ACCESS_INFORMATION 1008
+#define SMB_FILE_NAME_INFORMATION 1009
+#define SMB_FILE_RENAME_INFORMATION 1010
+#define SMB_FILE_LINK_INFORMATION 1011
+#define SMB_FILE_NAMES_INFORMATION 1012
+#define SMB_FILE_DISPOSITION_INFORMATION 1013
+#define SMB_FILE_POSITION_INFORMATION 1014
+#define SMB_FILE_FULL_EA_INFORMATION 1015
+#define SMB_FILE_MODE_INFORMATION 1016
+#define SMB_FILE_ALIGNMENT_INFORMATION 1017
+#define SMB_FILE_ALL_INFORMATION 1018
+#define SMB_FILE_ALLOCATION_INFORMATION 1019
+#define SMB_FILE_END_OF_FILE_INFORMATION 1020
+#define SMB_FILE_ALTERNATE_NAME_INFORMATION 1021
+#define SMB_FILE_STREAM_INFORMATION 1022
+#define SMB_FILE_PIPE_INFORMATION 1023
+#define SMB_FILE_PIPE_LOCAL_INFORMATION 1024
+#define SMB_FILE_PIPE_REMOTE_INFORMATION 1025
+#define SMB_FILE_MAILSLOT_QUERY_INFORMATION 1026
+#define SMB_FILE_MAILSLOT_SET_INFORMATION 1027
+#define SMB_FILE_COMPRESSION_INFORMATION 1028
+#define SMB_FILE_OBJECTID_INFORMATION 1029
+#define SMB_FILE_COMPLETION_INFORMATION 1030
+#define SMB_FILE_MOVE_CLUSTER_INFORMATION 1031
+#define SMB_FILE_QUOTA_INFORMATION 1032
+#define SMB_FILE_REPARSEPOINT_INFORMATION 1033
+#define SMB_FILE_NETWORK_OPEN_INFORMATION 1034
+#define SMB_FILE_ATTRIBUTE_TAG_INFORMATION 1035
+#define SMB_FILE_TRACKING_INFORMATION 1036
+#define SMB_FILE_MAXIMUM_INFORMATION 1037
+
+/* NT passthough levels for qfsinfo. */
+
+#define SMB_FS_VOLUME_INFORMATION 1001
+#define SMB_FS_LABEL_INFORMATION 1002
+#define SMB_FS_SIZE_INFORMATION 1003
+#define SMB_FS_DEVICE_INFORMATION 1004
+#define SMB_FS_ATTRIBUTE_INFORMATION 1005
+#define SMB_FS_QUOTA_INFORMATION 1006
+#define SMB_FS_FULL_SIZE_INFORMATION 1007
+#define SMB_FS_OBJECTID_INFORMATION 1008
+
+/* 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_QUERY_FILE_UNIX_BASIC 0x200 /* UNIX File Info*/
+#define SMB_SET_FILE_UNIX_BASIC 0x200
+
+#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
+
+/*
+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.
+*/
+
+/* 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
+
+#define SMB_QUERY_FILE_UNIX_LINK 0x201
+#define SMB_SET_FILE_UNIX_LINK 0x201
+#define SMB_SET_FILE_UNIX_HLINK 0x203
+
+#define SMB_FIND_FILE_UNIX 0x202
+
+/*
+ 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/source/include/util_getent.h b/source/include/util_getent.h
new file mode 100644
index 00000000000..b67758ba230
--- /dev/null
+++ b/source/include/util_getent.h
@@ -0,0 +1,61 @@
+/*
+ Unix SMB/CIFS implementation.
+ Samba utility functions
+ Copyright (C) Simo Sorce 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 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 _UTIL_GETENT_H
+#define _UTIL_GETENT_H
+
+/* Element for a single linked list of group entries */
+/* Replace the use of struct group in some cases */
+/* Used by getgrent_list() */
+
+struct sys_grent {
+ char *gr_name;
+ char *gr_passwd;
+ gid_t gr_gid;
+ char **gr_mem;
+ struct sys_grent *next;
+};
+
+/* Element for a single linked list of passwd entries */
+/* Replace the use of struct passwd in some cases */
+/* Used by getpwent_list() */
+
+struct sys_pwent {
+ char *pw_name;
+ char *pw_passwd;
+ uid_t pw_uid;
+ gid_t pw_gid;
+ char *pw_gecos;
+ char *pw_dir;
+ char *pw_shell;
+ struct sys_pwent *next;
+};
+
+/* Element for a single linked list of user names in a group. */
+/* Used to return group lists that may span multiple lines in
+ /etc/group file. */
+/* Used by get_users_in_group() */
+
+struct sys_userlist {
+ struct sys_userlist *next, *prev;
+ char *unix_name;
+};
+
+#endif /* _UTIL_GETENT_H */
diff --git a/source/include/vfs.h b/source/include/vfs.h
new file mode 100644
index 00000000000..8caf64fd99d
--- /dev/null
+++ b/source/include/vfs.h
@@ -0,0 +1,489 @@
+/*
+ Unix SMB/CIFS implementation.
+ VFS structures and parameters
+ Copyright (C) Jeremy Allison 1999-2003
+ Copyright (C) Tim Potter 1999
+ Copyright (C) Alexander Bokovoy 2002
+ 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 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.
+
+ This work was sponsored by Optifacio Software Services, Inc.
+*/
+
+#ifndef _VFS_H
+#define _VFS_H
+
+/* Avoid conflict with an AIX include file */
+
+#ifdef vfs_ops
+#undef vfs_ops
+#endif
+
+/*
+ * As we're now (thanks Andrew ! :-) using file_structs and connection
+ * structs in the vfs - then anyone writing a vfs must include includes.h...
+ */
+
+/*
+ * This next constant specifies the version number of the VFS interface
+ * this smbd will load. Increment this if *ANY* changes are made to the
+ * vfs_ops below. JRA.
+ */
+
+/* Changed to version 2 for CIFS UNIX extensions (mknod and link added). JRA. */
+/* Changed to version 3 for POSIX acl extensions. JRA. */
+/* Changed to version 4 for cascaded VFS interface. Alexander Bokovoy. */
+/* Changed to version 5 for sendfile addition. JRA. */
+/* Changed to version 6 for the new module system, fixed cascading and quota functions. --metze */
+/* Changed to version 7 to include the get_nt_acl info parameter. JRA. */
+/* Changed to version 8 includes EA calls. JRA. */
+/* Changed to version 9 to include the get_shadow_data call. --metze */
+/* Changed to version 10 to include pread/pwrite calls. */
+#define SMB_VFS_INTERFACE_VERSION 10
+
+
+/* to bug old modules witch are trying to compile with the old functions */
+#define vfs_init __ERROR_please_port_this_module_to_SMB_VFS_INTERFACE_VERSION_8_donot_use_vfs_init_anymore(void) { __ERROR_please_port_this_module_to_SMB_VFS_INTERFACE_VERSION_8_donot_use_vfs_init_anymore };
+#define lp_parm_string __ERROR_please_port_lp_parm_string_to_lp_parm_const_string_or_lp_parm_talloc_string { \
+ __ERROR_please_port_lp_parm_string_to_lp_parm_const_string_or_lp_parm_talloc_string };
+#define lp_vfs_options __ERROR_please_donot_use_lp_vfs_options_anymore_use_lp_parm_xxxx_functions_instead { \
+ __ERROR_please_donot_use_lp_vfs_options_anymore_use_lp_parm_xxxx_functions_instead };
+
+/*
+ All intercepted VFS operations must be declared as static functions inside module source
+ in order to keep smbd namespace unpolluted. See source of audit, extd_audit, fake_perms and recycle
+ example VFS modules for more details.
+*/
+
+/* VFS operations structure */
+
+struct vfs_handle_struct;
+struct connection_struct;
+struct files_struct;
+struct security_descriptor_info;
+
+/*
+ Available VFS operations. These values must be in sync with vfs_ops struct
+ (struct vfs_fn_pointers and struct vfs_handle_pointers inside of struct vfs_ops).
+ In particular, if new operations are added to vfs_ops, appropriate constants
+ should be added to vfs_op_type so that order of them kept same as in vfs_ops.
+*/
+
+typedef enum _vfs_op_type {
+ SMB_VFS_OP_NOOP = -1,
+
+ /* Disk operations */
+
+ SMB_VFS_OP_CONNECT = 0,
+ SMB_VFS_OP_DISCONNECT,
+ SMB_VFS_OP_DISK_FREE,
+ SMB_VFS_OP_GET_QUOTA,
+ SMB_VFS_OP_SET_QUOTA,
+ SMB_VFS_OP_GET_SHADOW_COPY_DATA,
+
+
+ /* Directory operations */
+
+ SMB_VFS_OP_OPENDIR,
+ SMB_VFS_OP_READDIR,
+ SMB_VFS_OP_MKDIR,
+ SMB_VFS_OP_RMDIR,
+ SMB_VFS_OP_CLOSEDIR,
+
+ /* File operations */
+
+ SMB_VFS_OP_OPEN,
+ SMB_VFS_OP_CLOSE,
+ SMB_VFS_OP_READ,
+ SMB_VFS_OP_PREAD,
+ SMB_VFS_OP_WRITE,
+ SMB_VFS_OP_PWRITE,
+ SMB_VFS_OP_LSEEK,
+ SMB_VFS_OP_SENDFILE,
+ SMB_VFS_OP_RENAME,
+ SMB_VFS_OP_FSYNC,
+ SMB_VFS_OP_STAT,
+ SMB_VFS_OP_FSTAT,
+ SMB_VFS_OP_LSTAT,
+ SMB_VFS_OP_UNLINK,
+ SMB_VFS_OP_CHMOD,
+ SMB_VFS_OP_FCHMOD,
+ SMB_VFS_OP_CHOWN,
+ SMB_VFS_OP_FCHOWN,
+ SMB_VFS_OP_CHDIR,
+ SMB_VFS_OP_GETWD,
+ SMB_VFS_OP_UTIME,
+ SMB_VFS_OP_FTRUNCATE,
+ SMB_VFS_OP_LOCK,
+ SMB_VFS_OP_SYMLINK,
+ SMB_VFS_OP_READLINK,
+ SMB_VFS_OP_LINK,
+ SMB_VFS_OP_MKNOD,
+ SMB_VFS_OP_REALPATH,
+
+ /* NT ACL operations. */
+
+ SMB_VFS_OP_FGET_NT_ACL,
+ SMB_VFS_OP_GET_NT_ACL,
+ SMB_VFS_OP_FSET_NT_ACL,
+ SMB_VFS_OP_SET_NT_ACL,
+
+ /* POSIX ACL operations. */
+
+ SMB_VFS_OP_CHMOD_ACL,
+ SMB_VFS_OP_FCHMOD_ACL,
+
+ SMB_VFS_OP_SYS_ACL_GET_ENTRY,
+ SMB_VFS_OP_SYS_ACL_GET_TAG_TYPE,
+ SMB_VFS_OP_SYS_ACL_GET_PERMSET,
+ SMB_VFS_OP_SYS_ACL_GET_QUALIFIER,
+ SMB_VFS_OP_SYS_ACL_GET_FILE,
+ SMB_VFS_OP_SYS_ACL_GET_FD,
+ SMB_VFS_OP_SYS_ACL_CLEAR_PERMS,
+ SMB_VFS_OP_SYS_ACL_ADD_PERM,
+ SMB_VFS_OP_SYS_ACL_TO_TEXT,
+ SMB_VFS_OP_SYS_ACL_INIT,
+ SMB_VFS_OP_SYS_ACL_CREATE_ENTRY,
+ SMB_VFS_OP_SYS_ACL_SET_TAG_TYPE,
+ SMB_VFS_OP_SYS_ACL_SET_QUALIFIER,
+ SMB_VFS_OP_SYS_ACL_SET_PERMSET,
+ SMB_VFS_OP_SYS_ACL_VALID,
+ SMB_VFS_OP_SYS_ACL_SET_FILE,
+ SMB_VFS_OP_SYS_ACL_SET_FD,
+ SMB_VFS_OP_SYS_ACL_DELETE_DEF_FILE,
+ SMB_VFS_OP_SYS_ACL_GET_PERM,
+ SMB_VFS_OP_SYS_ACL_FREE_TEXT,
+ SMB_VFS_OP_SYS_ACL_FREE_ACL,
+ SMB_VFS_OP_SYS_ACL_FREE_QUALIFIER,
+
+ /* EA operations. */
+ SMB_VFS_OP_GETXATTR,
+ SMB_VFS_OP_LGETXATTR,
+ SMB_VFS_OP_FGETXATTR,
+ SMB_VFS_OP_LISTXATTR,
+ SMB_VFS_OP_LLISTXATTR,
+ SMB_VFS_OP_FLISTXATTR,
+ SMB_VFS_OP_REMOVEXATTR,
+ SMB_VFS_OP_LREMOVEXATTR,
+ SMB_VFS_OP_FREMOVEXATTR,
+ SMB_VFS_OP_SETXATTR,
+ SMB_VFS_OP_LSETXATTR,
+ SMB_VFS_OP_FSETXATTR,
+
+ /* This should always be last enum value */
+
+ SMB_VFS_OP_LAST
+} vfs_op_type;
+
+/*
+ Please keep vfs_op_type, struct vfs_fn_pointers and struct vfs_handles_pointers in sync.
+*/
+struct vfs_ops {
+ struct vfs_fn_pointers {
+ /* Disk operations */
+
+ int (*connect)(struct vfs_handle_struct *handle, struct connection_struct *conn, const char *service, const char *user);
+ void (*disconnect)(struct vfs_handle_struct *handle, struct connection_struct *conn);
+ SMB_BIG_UINT (*disk_free)(struct vfs_handle_struct *handle, struct connection_struct *conn, const char *path, BOOL small_query, SMB_BIG_UINT *bsize,
+ SMB_BIG_UINT *dfree, SMB_BIG_UINT *dsize);
+ int (*get_quota)(struct vfs_handle_struct *handle, struct connection_struct *conn, enum SMB_QUOTA_TYPE qtype, unid_t id, SMB_DISK_QUOTA *qt);
+ int (*set_quota)(struct vfs_handle_struct *handle, struct connection_struct *conn, enum SMB_QUOTA_TYPE qtype, unid_t id, SMB_DISK_QUOTA *qt);
+ int (*get_shadow_copy_data)(struct vfs_handle_struct *handle, struct files_struct *fsp, SHADOW_COPY_DATA *shadow_copy_data, BOOL labels);
+
+ /* Directory operations */
+
+ DIR *(*opendir)(struct vfs_handle_struct *handle, struct connection_struct *conn, const char *fname);
+ struct dirent *(*readdir)(struct vfs_handle_struct *handle, struct connection_struct *conn, DIR *dirp);
+ int (*mkdir)(struct vfs_handle_struct *handle, struct connection_struct *conn, const char *path, mode_t mode);
+ int (*rmdir)(struct vfs_handle_struct *handle, struct connection_struct *conn, const char *path);
+ int (*closedir)(struct vfs_handle_struct *handle, struct connection_struct *conn, DIR *dir);
+
+ /* File operations */
+
+ int (*open)(struct vfs_handle_struct *handle, struct connection_struct *conn, const char *fname, int flags, mode_t mode);
+ int (*close)(struct vfs_handle_struct *handle, struct files_struct *fsp, int fd);
+ ssize_t (*read)(struct vfs_handle_struct *handle, struct files_struct *fsp, int fd, void *data, size_t n);
+ ssize_t (*pread)(struct vfs_handle_struct *handle, struct files_struct *fsp, int fd, void *data, size_t n, SMB_OFF_T offset);
+ ssize_t (*write)(struct vfs_handle_struct *handle, struct files_struct *fsp, int fd, const void *data, size_t n);
+ ssize_t (*pwrite)(struct vfs_handle_struct *handle, struct files_struct *fsp, int fd, const void *data, size_t n, SMB_OFF_T offset);
+ SMB_OFF_T (*lseek)(struct vfs_handle_struct *handle, struct files_struct *fsp, int fd, SMB_OFF_T offset, int whence);
+ ssize_t (*sendfile)(struct vfs_handle_struct *handle, int tofd, files_struct *fsp, int fromfd, const DATA_BLOB *header, SMB_OFF_T offset, size_t count);
+ int (*rename)(struct vfs_handle_struct *handle, struct connection_struct *conn, const char *old, const char *new);
+ int (*fsync)(struct vfs_handle_struct *handle, struct files_struct *fsp, int fd);
+ int (*stat)(struct vfs_handle_struct *handle, struct connection_struct *conn, const char *fname, SMB_STRUCT_STAT *sbuf);
+ int (*fstat)(struct vfs_handle_struct *handle, struct files_struct *fsp, int fd, SMB_STRUCT_STAT *sbuf);
+ int (*lstat)(struct vfs_handle_struct *handle, struct connection_struct *conn, const char *path, SMB_STRUCT_STAT *sbuf);
+ int (*unlink)(struct vfs_handle_struct *handle, struct connection_struct *conn, const char *path);
+ int (*chmod)(struct vfs_handle_struct *handle, struct connection_struct *conn, const char *path, mode_t mode);
+ int (*fchmod)(struct vfs_handle_struct *handle, struct files_struct *fsp, int fd, mode_t mode);
+ int (*chown)(struct vfs_handle_struct *handle, struct connection_struct *conn, const char *path, uid_t uid, gid_t gid);
+ int (*fchown)(struct vfs_handle_struct *handle, struct files_struct *fsp, int fd, uid_t uid, gid_t gid);
+ int (*chdir)(struct vfs_handle_struct *handle, struct connection_struct *conn, const char *path);
+ char *(*getwd)(struct vfs_handle_struct *handle, struct connection_struct *conn, char *buf);
+ int (*utime)(struct vfs_handle_struct *handle, struct connection_struct *conn, const char *path, struct utimbuf *times);
+ int (*ftruncate)(struct vfs_handle_struct *handle, struct files_struct *fsp, int fd, SMB_OFF_T offset);
+ BOOL (*lock)(struct vfs_handle_struct *handle, struct files_struct *fsp, int fd, int op, SMB_OFF_T offset, SMB_OFF_T count, int type);
+ int (*symlink)(struct vfs_handle_struct *handle, struct connection_struct *conn, const char *oldpath, const char *newpath);
+ int (*readlink)(struct vfs_handle_struct *handle, struct connection_struct *conn, const char *path, char *buf, size_t bufsiz);
+ int (*link)(struct vfs_handle_struct *handle, struct connection_struct *conn, const char *oldpath, const char *newpath);
+ int (*mknod)(struct vfs_handle_struct *handle, struct connection_struct *conn, const char *path, mode_t mode, SMB_DEV_T dev);
+ char *(*realpath)(struct vfs_handle_struct *handle, struct connection_struct *conn, const char *path, char *resolved_path);
+
+ /* NT ACL operations. */
+
+ size_t (*fget_nt_acl)(struct vfs_handle_struct *handle, struct files_struct *fsp, int fd, uint32 security_info, struct security_descriptor_info **ppdesc);
+ size_t (*get_nt_acl)(struct vfs_handle_struct *handle, struct files_struct *fsp, const char *name, uint32 security_info, struct security_descriptor_info **ppdesc);
+ BOOL (*fset_nt_acl)(struct vfs_handle_struct *handle, struct files_struct *fsp, int fd, uint32 security_info_sent, struct security_descriptor_info *psd);
+ BOOL (*set_nt_acl)(struct vfs_handle_struct *handle, struct files_struct *fsp, const char *name, uint32 security_info_sent, struct security_descriptor_info *psd);
+
+ /* POSIX ACL operations. */
+
+ int (*chmod_acl)(struct vfs_handle_struct *handle, struct connection_struct *conn, const char *name, mode_t mode);
+ int (*fchmod_acl)(struct vfs_handle_struct *handle, struct files_struct *fsp, int fd, mode_t mode);
+
+ int (*sys_acl_get_entry)(struct vfs_handle_struct *handle, struct connection_struct *conn, SMB_ACL_T theacl, int entry_id, SMB_ACL_ENTRY_T *entry_p);
+ int (*sys_acl_get_tag_type)(struct vfs_handle_struct *handle, struct connection_struct *conn, SMB_ACL_ENTRY_T entry_d, SMB_ACL_TAG_T *tag_type_p);
+ int (*sys_acl_get_permset)(struct vfs_handle_struct *handle, struct connection_struct *conn, SMB_ACL_ENTRY_T entry_d, SMB_ACL_PERMSET_T *permset_p);
+ void * (*sys_acl_get_qualifier)(struct vfs_handle_struct *handle, struct connection_struct *conn, SMB_ACL_ENTRY_T entry_d);
+ SMB_ACL_T (*sys_acl_get_file)(struct vfs_handle_struct *handle, struct connection_struct *conn, const char *path_p, SMB_ACL_TYPE_T type);
+ SMB_ACL_T (*sys_acl_get_fd)(struct vfs_handle_struct *handle, struct files_struct *fsp, int fd);
+ int (*sys_acl_clear_perms)(struct vfs_handle_struct *handle, struct connection_struct *conn, SMB_ACL_PERMSET_T permset);
+ int (*sys_acl_add_perm)(struct vfs_handle_struct *handle, struct connection_struct *conn, SMB_ACL_PERMSET_T permset, SMB_ACL_PERM_T perm);
+ char * (*sys_acl_to_text)(struct vfs_handle_struct *handle, struct connection_struct *conn, SMB_ACL_T theacl, ssize_t *plen);
+ SMB_ACL_T (*sys_acl_init)(struct vfs_handle_struct *handle, struct connection_struct *conn, int count);
+ int (*sys_acl_create_entry)(struct vfs_handle_struct *handle, struct connection_struct *conn, SMB_ACL_T *pacl, SMB_ACL_ENTRY_T *pentry);
+ int (*sys_acl_set_tag_type)(struct vfs_handle_struct *handle, struct connection_struct *conn, SMB_ACL_ENTRY_T entry, SMB_ACL_TAG_T tagtype);
+ int (*sys_acl_set_qualifier)(struct vfs_handle_struct *handle, struct connection_struct *conn, SMB_ACL_ENTRY_T entry, void *qual);
+ int (*sys_acl_set_permset)(struct vfs_handle_struct *handle, struct connection_struct *conn, SMB_ACL_ENTRY_T entry, SMB_ACL_PERMSET_T permset);
+ int (*sys_acl_valid)(struct vfs_handle_struct *handle, struct connection_struct *conn, SMB_ACL_T theacl );
+ int (*sys_acl_set_file)(struct vfs_handle_struct *handle, struct connection_struct *conn, const char *name, SMB_ACL_TYPE_T acltype, SMB_ACL_T theacl);
+ int (*sys_acl_set_fd)(struct vfs_handle_struct *handle, struct files_struct *fsp, int fd, SMB_ACL_T theacl);
+ int (*sys_acl_delete_def_file)(struct vfs_handle_struct *handle, struct connection_struct *conn, const char *path);
+ int (*sys_acl_get_perm)(struct vfs_handle_struct *handle, struct connection_struct *conn, SMB_ACL_PERMSET_T permset, SMB_ACL_PERM_T perm);
+ int (*sys_acl_free_text)(struct vfs_handle_struct *handle, struct connection_struct *conn, char *text);
+ int (*sys_acl_free_acl)(struct vfs_handle_struct *handle, struct connection_struct *conn, SMB_ACL_T posix_acl);
+ int (*sys_acl_free_qualifier)(struct vfs_handle_struct *handle, struct connection_struct *conn, void *qualifier, SMB_ACL_TAG_T tagtype);
+
+ /* EA operations. */
+ ssize_t (*getxattr)(struct vfs_handle_struct *handle,struct connection_struct *conn,const char *path, const char *name, void *value, size_t size);
+ ssize_t (*lgetxattr)(struct vfs_handle_struct *handle,struct connection_struct *conn,const char *path, const char *name, void *value, size_t size);
+ ssize_t (*fgetxattr)(struct vfs_handle_struct *handle, struct files_struct *fsp,int fd, const char *name, void *value, size_t size);
+ ssize_t (*listxattr)(struct vfs_handle_struct *handle, struct connection_struct *conn,const char *path, char *list, size_t size);
+ ssize_t (*llistxattr)(struct vfs_handle_struct *handle, struct connection_struct *conn,const char *path, char *list, size_t size);
+ ssize_t (*flistxattr)(struct vfs_handle_struct *handle, struct files_struct *fsp,int fd, char *list, size_t size);
+ int (*removexattr)(struct vfs_handle_struct *handle, struct connection_struct *conn,const char *path, const char *name);
+ int (*lremovexattr)(struct vfs_handle_struct *handle, struct connection_struct *conn,const char *path, const char *name);
+ int (*fremovexattr)(struct vfs_handle_struct *handle, struct files_struct *fsp,int filedes, const char *name);
+ int (*setxattr)(struct vfs_handle_struct *handle, struct connection_struct *conn,const char *path, const char *name, const void *value, size_t size, int flags);
+ int (*lsetxattr)(struct vfs_handle_struct *handle, struct connection_struct *conn,const char *path, const char *name, const void *value, size_t size, int flags);
+ int (*fsetxattr)(struct vfs_handle_struct *handle, struct files_struct *fsp,int filedes, const char *name, const void *value, size_t size, int flags);
+
+ } ops;
+
+ struct vfs_handles_pointers {
+ /* Disk operations */
+
+ struct vfs_handle_struct *connect;
+ struct vfs_handle_struct *disconnect;
+ struct vfs_handle_struct *disk_free;
+ struct vfs_handle_struct *get_quota;
+ struct vfs_handle_struct *set_quota;
+ struct vfs_handle_struct *get_shadow_copy_data;
+
+ /* Directory operations */
+
+ struct vfs_handle_struct *opendir;
+ struct vfs_handle_struct *readdir;
+ struct vfs_handle_struct *mkdir;
+ struct vfs_handle_struct *rmdir;
+ struct vfs_handle_struct *closedir;
+
+ /* File operations */
+
+ struct vfs_handle_struct *open;
+ struct vfs_handle_struct *close;
+ struct vfs_handle_struct *read;
+ struct vfs_handle_struct *pread;
+ struct vfs_handle_struct *write;
+ struct vfs_handle_struct *pwrite;
+ struct vfs_handle_struct *lseek;
+ struct vfs_handle_struct *sendfile;
+ struct vfs_handle_struct *rename;
+ struct vfs_handle_struct *fsync;
+ struct vfs_handle_struct *stat;
+ struct vfs_handle_struct *fstat;
+ struct vfs_handle_struct *lstat;
+ struct vfs_handle_struct *unlink;
+ struct vfs_handle_struct *chmod;
+ struct vfs_handle_struct *fchmod;
+ struct vfs_handle_struct *chown;
+ struct vfs_handle_struct *fchown;
+ struct vfs_handle_struct *chdir;
+ struct vfs_handle_struct *getwd;
+ struct vfs_handle_struct *utime;
+ struct vfs_handle_struct *ftruncate;
+ struct vfs_handle_struct *lock;
+ struct vfs_handle_struct *symlink;
+ struct vfs_handle_struct *readlink;
+ struct vfs_handle_struct *link;
+ struct vfs_handle_struct *mknod;
+ struct vfs_handle_struct *realpath;
+
+ /* NT ACL operations. */
+
+ struct vfs_handle_struct *fget_nt_acl;
+ struct vfs_handle_struct *get_nt_acl;
+ struct vfs_handle_struct *fset_nt_acl;
+ struct vfs_handle_struct *set_nt_acl;
+
+ /* POSIX ACL operations. */
+
+ struct vfs_handle_struct *chmod_acl;
+ struct vfs_handle_struct *fchmod_acl;
+
+ struct vfs_handle_struct *sys_acl_get_entry;
+ struct vfs_handle_struct *sys_acl_get_tag_type;
+ struct vfs_handle_struct *sys_acl_get_permset;
+ struct vfs_handle_struct *sys_acl_get_qualifier;
+ struct vfs_handle_struct *sys_acl_get_file;
+ struct vfs_handle_struct *sys_acl_get_fd;
+ struct vfs_handle_struct *sys_acl_clear_perms;
+ struct vfs_handle_struct *sys_acl_add_perm;
+ struct vfs_handle_struct *sys_acl_to_text;
+ struct vfs_handle_struct *sys_acl_init;
+ struct vfs_handle_struct *sys_acl_create_entry;
+ struct vfs_handle_struct *sys_acl_set_tag_type;
+ struct vfs_handle_struct *sys_acl_set_qualifier;
+ struct vfs_handle_struct *sys_acl_set_permset;
+ struct vfs_handle_struct *sys_acl_valid;
+ struct vfs_handle_struct *sys_acl_set_file;
+ struct vfs_handle_struct *sys_acl_set_fd;
+ struct vfs_handle_struct *sys_acl_delete_def_file;
+ struct vfs_handle_struct *sys_acl_get_perm;
+ struct vfs_handle_struct *sys_acl_free_text;
+ struct vfs_handle_struct *sys_acl_free_acl;
+ struct vfs_handle_struct *sys_acl_free_qualifier;
+
+ /* EA operations. */
+ struct vfs_handle_struct *getxattr;
+ struct vfs_handle_struct *lgetxattr;
+ struct vfs_handle_struct *fgetxattr;
+ struct vfs_handle_struct *listxattr;
+ struct vfs_handle_struct *llistxattr;
+ struct vfs_handle_struct *flistxattr;
+ struct vfs_handle_struct *removexattr;
+ struct vfs_handle_struct *lremovexattr;
+ struct vfs_handle_struct *fremovexattr;
+ struct vfs_handle_struct *setxattr;
+ struct vfs_handle_struct *lsetxattr;
+ struct vfs_handle_struct *fsetxattr;
+
+ } handles;
+};
+
+/*
+ Possible VFS operation layers (per-operation)
+
+ These values are used by VFS subsystem when building vfs_ops for connection
+ from multiple VFS modules. Internally, Samba differentiates only opaque and
+ transparent layers at this process. Other types are used for providing better
+ diagnosing facilities.
+
+ Most modules will provide transparent layers. Opaque layer is for modules
+ which implement actual file system calls (like DB-based VFS). For example,
+ default POSIX VFS which is built in into Samba is an opaque VFS module.
+
+ Other layer types (audit, splitter, scanner) were designed to provide different
+ degree of transparency and for diagnosing VFS module behaviour.
+
+ Each module can implement several layers at the same time provided that only
+ one layer is used per each operation.
+
+*/
+
+typedef enum _vfs_op_layer {
+ SMB_VFS_LAYER_NOOP = -1, /* - For using in VFS module to indicate end of array */
+ /* of operations description */
+ SMB_VFS_LAYER_OPAQUE = 0, /* - Final level, does not call anything beyond itself */
+ SMB_VFS_LAYER_TRANSPARENT, /* - Normal operation, calls underlying layer after */
+ /* possibly changing passed data */
+ SMB_VFS_LAYER_LOGGER, /* - Logs data, calls underlying layer, logging may not */
+ /* use Samba VFS */
+ SMB_VFS_LAYER_SPLITTER, /* - Splits operation, calls underlying layer _and_ own facility, */
+ /* then combines result */
+ SMB_VFS_LAYER_SCANNER /* - Checks data and possibly initiates additional */
+ /* file activity like logging to files _inside_ samba VFS */
+} vfs_op_layer;
+
+/*
+ VFS operation description. Each VFS module registers an array of vfs_op_tuple to VFS subsystem,
+ which describes all operations this module is willing to intercept.
+ VFS subsystem initializes then the conn->vfs_ops and conn->vfs_opaque_ops structs
+ using this information.
+*/
+
+typedef struct _vfs_op_tuple {
+ void* op;
+ vfs_op_type type;
+ vfs_op_layer layer;
+} vfs_op_tuple;
+
+
+typedef struct vfs_handle_struct {
+ struct vfs_handle_struct *next, *prev;
+ const char *param;
+ struct vfs_ops vfs_next;
+ struct connection_struct *conn;
+ void *data;
+ void (*free_data)(void **data);
+} vfs_handle_struct;
+
+
+#define SMB_VFS_HANDLE_GET_DATA(handle, datap, type, ret) { \
+ if (!(handle)||((datap=(type *)(handle)->data)==NULL)) { \
+ DEBUG(0,("%s() failed to get vfs_handle->data!\n",FUNCTION_MACRO)); \
+ ret; \
+ } \
+}
+
+#define SMB_VFS_HANDLE_SET_DATA(handle, datap, free_fn, type, ret) { \
+ if (!(handle)) { \
+ DEBUG(0,("%s() failed to set handle->data!\n",FUNCTION_MACRO)); \
+ ret; \
+ } else { \
+ if ((handle)->free_data) { \
+ (handle)->free_data(&(handle)->data); \
+ } \
+ (handle)->data = (void *)datap; \
+ (handle)->free_data = free_fn; \
+ } \
+}
+
+#define SMB_VFS_HANDLE_FREE_DATA(handle) { \
+ if ((handle) && (handle)->free_data) { \
+ (handle)->free_data(&(handle)->data); \
+ } \
+}
+
+/* Check whether module-specific data handle was already allocated or not */
+#define SMB_VFS_HANDLE_TEST_DATA(handle) ( !(handle) || !(handle)->data ? False : True )
+
+#define SMB_VFS_OP(x) ((void *) x)
+
+
+#include "vfs_macros.h"
+
+#endif /* _VFS_H */
diff --git a/source/include/vfs_macros.h b/source/include/vfs_macros.h
new file mode 100644
index 00000000000..1ec1c5a7789
--- /dev/null
+++ b/source/include/vfs_macros.h
@@ -0,0 +1,318 @@
+/*
+ Unix SMB/CIFS implementation.
+ VFS wrapper macros
+ 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 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 _VFS_MACROS_H
+#define _VFS_MACROS_H
+
+/*******************************************************************
+ Don't access conn->vfs.ops.* directly!!!
+ Use this macros!
+ (Fixes should go also into the vfs_opaque_* and vfs_next_* macros!)
+********************************************************************/
+
+/* Disk operations */
+#define SMB_VFS_CONNECT(conn, service, user) ((conn)->vfs.ops.connect((conn)->vfs.handles.connect, (conn), (service), (user)))
+#define SMB_VFS_DISCONNECT(conn) ((conn)->vfs.ops.disconnect((conn)->vfs.handles.disconnect, (conn)))
+#define SMB_VFS_DISK_FREE(conn, path, small_query, bsize, dfree ,dsize) ((conn)->vfs.ops.disk_free((conn)->vfs.handles.disk_free, (conn), (path), (small_query), (bsize), (dfree), (dsize)))
+#define SMB_VFS_GET_QUOTA(conn, qtype, id, qt) ((conn)->vfs.ops.get_quota((conn)->vfs.handles.get_quota, (conn), (qtype), (id), (qt)))
+#define SMB_VFS_SET_QUOTA(conn, qtype, id, qt) ((conn)->vfs.ops.set_quota((conn)->vfs.handles.set_quota, (conn), (qtype), (id), (qt)))
+#define SMB_VFS_GET_SHADOW_COPY_DATA(fsp,shadow_copy_data,labels) ((fsp)->conn->vfs.ops.get_shadow_copy_data((fsp)->conn->vfs.handles.get_shadow_copy_data,(fsp),(shadow_copy_data),(labels)))
+
+/* Directory operations */
+#define SMB_VFS_OPENDIR(conn, fname) ((conn)->vfs.ops.opendir((conn)->vfs.handles.opendir, (conn), (fname)))
+#define SMB_VFS_READDIR(conn, dirp) ((conn)->vfs.ops.readdir((conn)->vfs.handles.readdir, (conn), (dirp)))
+#define SMB_VFS_MKDIR(conn, path, mode) ((conn)->vfs.ops.mkdir((conn)->vfs.handles.mkdir,(conn), (path), (mode)))
+#define SMB_VFS_RMDIR(conn, path) ((conn)->vfs.ops.rmdir((conn)->vfs.handles.rmdir, (conn), (path)))
+#define SMB_VFS_CLOSEDIR(conn, dir) ((conn)->vfs.ops.closedir((conn)->vfs.handles.closedir, (conn), dir))
+
+/* File operations */
+#define SMB_VFS_OPEN(conn, fname, flags, mode) ((conn)->vfs.ops.open((conn)->vfs.handles.open, (conn), (fname), (flags), (mode)))
+#define SMB_VFS_CLOSE(fsp, fd) ((fsp)->conn->vfs.ops.close((fsp)->conn->vfs.handles.close, (fsp), (fd)))
+#define SMB_VFS_READ(fsp, fd, data, n) ((fsp)->conn->vfs.ops.read((fsp)->conn->vfs.handles.read, (fsp), (fd), (data), (n)))
+#define SMB_VFS_PREAD(fsp, fd, data, n, off) ((fsp)->conn->vfs.ops.pread((fsp)->conn->vfs.handles.pread, (fsp), (fd), (data), (n), (off)))
+#define SMB_VFS_WRITE(fsp, fd, data, n) ((fsp)->conn->vfs.ops.write((fsp)->conn->vfs.handles.write, (fsp), (fd), (data), (n)))
+#define SMB_VFS_PWRITE(fsp, fd, data, n, off) ((fsp)->conn->vfs.ops.pwrite((fsp)->conn->vfs.handles.pwrite, (fsp), (fd), (data), (n), (off)))
+#define SMB_VFS_LSEEK(fsp, fd, offset, whence) ((fsp)->conn->vfs.ops.lseek((fsp)->conn->vfs.handles.lseek, (fsp), (fd), (offset), (whence)))
+#define SMB_VFS_SENDFILE(tofd, fsp, fromfd, header, offset, count) ((fsp)->conn->vfs.ops.sendfile((fsp)->conn->vfs.handles.sendfile, (tofd), (fsp), (fromfd), (header), (offset), (count)))
+#define SMB_VFS_RENAME(conn, old, new) ((conn)->vfs.ops.rename((conn)->vfs.handles.rename, (conn), (old), (new)))
+#define SMB_VFS_FSYNC(fsp, fd) ((fsp)->conn->vfs.ops.fsync((fsp)->conn->vfs.handles.fsync, (fsp), (fd)))
+#define SMB_VFS_STAT(conn, fname, sbuf) ((conn)->vfs.ops.stat((conn)->vfs.handles.stat, (conn), (fname), (sbuf)))
+#define SMB_VFS_FSTAT(fsp, fd, sbuf) ((fsp)->conn->vfs.ops.fstat((fsp)->conn->vfs.handles.fstat, (fsp) ,(fd) ,(sbuf)))
+#define SMB_VFS_LSTAT(conn, path, sbuf) ((conn)->vfs.ops.lstat((conn)->vfs.handles.lstat, (conn), (path), (sbuf)))
+#define SMB_VFS_UNLINK(conn, path) ((conn)->vfs.ops.unlink((conn)->vfs.handles.unlink, (conn), (path)))
+#define SMB_VFS_CHMOD(conn, path, mode) ((conn)->vfs.ops.chmod((conn)->vfs.handles.chmod, (conn), (path), (mode)))
+#define SMB_VFS_FCHMOD(fsp, fd, mode) ((fsp)->conn->vfs.ops.fchmod((fsp)->conn->vfs.handles.fchmod, (fsp), (fd), (mode)))
+#define SMB_VFS_CHOWN(conn, path, uid, gid) ((conn)->vfs.ops.chown((conn)->vfs.handles.chown, (conn), (path), (uid), (gid)))
+#define SMB_VFS_FCHOWN(fsp, fd, uid, gid) ((fsp)->conn->vfs.ops.fchown((fsp)->conn->vfs.handles.fchown, (fsp), (fd), (uid), (gid)))
+#define SMB_VFS_CHDIR(conn, path) ((conn)->vfs.ops.chdir((conn)->vfs.handles.chdir, (conn), (path)))
+#define SMB_VFS_GETWD(conn, buf) ((conn)->vfs.ops.getwd((conn)->vfs.handles.getwd, (conn), (buf)))
+#define SMB_VFS_UTIME(conn, path, times) ((conn)->vfs.ops.utime((conn)->vfs.handles.utime, (conn), (path), (times)))
+#define SMB_VFS_FTRUNCATE(fsp, fd, offset) ((fsp)->conn->vfs.ops.ftruncate((fsp)->conn->vfs.handles.ftruncate, (fsp), (fd), (offset)))
+#define SMB_VFS_LOCK(fsp, fd, op, offset, count, type) ((fsp)->conn->vfs.ops.lock((fsp)->conn->vfs.handles.lock, (fsp), (fd) ,(op), (offset), (count), (type)))
+#define SMB_VFS_SYMLINK(conn, oldpath, newpath) ((conn)->vfs.ops.symlink((conn)->vfs.handles.symlink, (conn), (oldpath), (newpath)))
+#define SMB_VFS_READLINK(conn, path, buf, bufsiz) ((conn)->vfs.ops.readlink((conn)->vfs.handles.readlink, (conn), (path), (buf), (bufsiz)))
+#define SMB_VFS_LINK(conn, oldpath, newpath) ((conn)->vfs.ops.link((conn)->vfs.handles.link, (conn), (oldpath), (newpath)))
+#define SMB_VFS_MKNOD(conn, path, mode, dev) ((conn)->vfs.ops.mknod((conn)->vfs.handles.mknod, (conn), (path), (mode), (dev)))
+#define SMB_VFS_REALPATH(conn, path, resolved_path) ((conn)->vfs.ops.realpath((conn)->vfs.handles.realpath, (conn), (path), (resolved_path)))
+
+/* NT ACL operations. */
+#define SMB_VFS_FGET_NT_ACL(fsp, fd, security_info, ppdesc) ((fsp)->conn->vfs.ops.fget_nt_acl((fsp)->conn->vfs.handles.fget_nt_acl, (fsp), (fd), (security_info), (ppdesc)))
+#define SMB_VFS_GET_NT_ACL(fsp, name, security_info, ppdesc) ((fsp)->conn->vfs.ops.get_nt_acl((fsp)->conn->vfs.handles.get_nt_acl, (fsp), (name), (security_info), (ppdesc)))
+#define SMB_VFS_FSET_NT_ACL(fsp, fd, security_info_sent, psd) ((fsp)->conn->vfs.ops.fset_nt_acl((fsp)->conn->vfs.handles.fset_nt_acl, (fsp), (fd), (security_info_sent), (psd)))
+#define SMB_VFS_SET_NT_ACL(fsp, name, security_info_sent, psd) ((fsp)->conn->vfs.ops.set_nt_acl((fsp)->conn->vfs.handles.set_nt_acl, (fsp), (name), (security_info_sent), (psd)))
+
+/* POSIX ACL operations. */
+#define SMB_VFS_CHMOD_ACL(conn, name, mode) ((conn)->vfs.ops.chmod_acl((conn)->vfs.handles.chmod_acl, (conn), (name), (mode)))
+#define SMB_VFS_FCHMOD_ACL(fsp, fd, mode) ((fsp)->conn->vfs.ops.fchmod_acl((fsp)->conn->vfs.handles.chmod_acl, (fsp), (fd), (mode)))
+
+#define SMB_VFS_SYS_ACL_GET_ENTRY(conn, theacl, entry_id, entry_p) ((conn)->vfs.ops.sys_acl_get_entry((conn)->vfs.handles.sys_acl_get_entry, (conn), (theacl), (entry_id), (entry_p)))
+#define SMB_VFS_SYS_ACL_GET_TAG_TYPE(conn, entry_d, tag_type_p) ((conn)->vfs.ops.sys_acl_get_tag_type((conn)->vfs.handles.sys_acl_get_tag_type, (conn), (entry_d), (tag_type_p)))
+#define SMB_VFS_SYS_ACL_GET_PERMSET(conn, entry_d, permset_p) ((conn)->vfs.ops.sys_acl_get_permset((conn)->vfs.handles.sys_acl_get_permset, (conn), (entry_d), (permset_p)))
+#define SMB_VFS_SYS_ACL_GET_QUALIFIER(conn, entry_d) ((conn)->vfs.ops.sys_acl_get_qualifier((conn)->vfs.handles.sys_acl_get_qualifier, (conn), (entry_d)))
+#define SMB_VFS_SYS_ACL_GET_FILE(conn, path_p, type) ((conn)->vfs.ops.sys_acl_get_file((conn)->vfs.handles.sys_acl_get_file, (conn), (path_p), (type)))
+#define SMB_VFS_SYS_ACL_GET_FD(fsp, fd) ((fsp)->conn->vfs.ops.sys_acl_get_fd((fsp)->conn->vfs.handles.sys_acl_get_fd, (fsp), (fd)))
+#define SMB_VFS_SYS_ACL_CLEAR_PERMS(conn, permset) ((conn)->vfs.ops.sys_acl_clear_perms((conn)->vfs.handles.sys_acl_clear_perms, (conn), (permset)))
+#define SMB_VFS_SYS_ACL_ADD_PERM(conn, permset, perm) ((conn)->vfs.ops.sys_acl_add_perm((conn)->vfs.handles.sys_acl_add_perm, (conn), (permset), (perm)))
+#define SMB_VFS_SYS_ACL_TO_TEXT(conn, theacl, plen) ((conn)->vfs.ops.sys_acl_to_text((conn)->vfs.handles.sys_acl_to_text, (conn), (theacl), (plen)))
+#define SMB_VFS_SYS_ACL_INIT(conn, count) ((conn)->vfs.ops.sys_acl_init((conn)->vfs.handles.sys_acl_init, (conn), (count)))
+#define SMB_VFS_SYS_ACL_CREATE_ENTRY(conn, pacl, pentry) ((conn)->vfs.ops.sys_acl_create_entry((conn)->vfs.handles.sys_acl_create_entry, (conn), (pacl), (pentry)))
+#define SMB_VFS_SYS_ACL_SET_TAG_TYPE(conn, entry, tagtype) ((conn)->vfs.ops.sys_acl_set_tag_type((conn)->vfs.handles.sys_acl_set_tag_type, (conn), (entry), (tagtype)))
+#define SMB_VFS_SYS_ACL_SET_QUALIFIER(conn, entry, qual) ((conn)->vfs.ops.sys_acl_set_qualifier((conn)->vfs.handles.sys_acl_set_qualifier, (conn), (entry), (qual)))
+#define SMB_VFS_SYS_ACL_SET_PERMSET(conn, entry, permset) ((conn)->vfs.ops.sys_acl_set_permset((conn)->vfs.handles.sys_acl_set_permset, (conn), (entry), (permset)))
+#define SMB_VFS_SYS_ACL_VALID(conn, theacl) ((conn)->vfs.ops.sys_acl_valid((conn)->vfs.handles.sys_acl_valid, (conn), (theacl)))
+#define SMB_VFS_SYS_ACL_SET_FILE(conn, name, acltype, theacl) ((conn)->vfs.ops.sys_acl_set_file((conn)->vfs.handles.sys_acl_set_file, (conn), (name), (acltype), (theacl)))
+#define SMB_VFS_SYS_ACL_SET_FD(fsp, fd, theacl) ((fsp)->conn->vfs.ops.sys_acl_set_fd((fsp)->conn->vfs.handles.sys_acl_set_fd, (fsp), (fd), (theacl)))
+#define SMB_VFS_SYS_ACL_DELETE_DEF_FILE(conn, path) ((conn)->vfs.ops.sys_acl_delete_def_file((conn)->vfs.handles.sys_acl_delete_def_file, (conn), (path)))
+#define SMB_VFS_SYS_ACL_GET_PERM(conn, permset, perm) ((conn)->vfs.ops.sys_acl_get_perm((conn)->vfs.handles.sys_acl_get_perm, (conn), (permset), (perm)))
+#define SMB_VFS_SYS_ACL_FREE_TEXT(conn, text) ((conn)->vfs.ops.sys_acl_free_text((conn)->vfs.handles.sys_acl_free_text, (conn), (text)))
+#define SMB_VFS_SYS_ACL_FREE_ACL(conn, posix_acl) ((conn)->vfs.ops.sys_acl_free_acl((conn)->vfs.handles.sys_acl_free_acl, (conn), (posix_acl)))
+#define SMB_VFS_SYS_ACL_FREE_QUALIFIER(conn, qualifier, tagtype) ((conn)->vfs.ops.sys_acl_free_qualifier((conn)->vfs.handles.sys_acl_free_qualifier, (conn), (qualifier), (tagtype)))
+
+/* EA operations. */
+#define SMB_VFS_GETXATTR(conn,path,name,value,size) ((conn)->vfs.ops.getxattr((conn)->vfs.handles.getxattr,(conn),(path),(name),(value),(size)))
+#define SMB_VFS_LGETXATTR(conn,path,name,value,size) ((conn)->vfs.ops.lgetxattr((conn)->vfs.handles.lgetxattr,(conn),(path),(name),(value),(size)))
+#define SMB_VFS_FGETXATTR(fsp,fd,name,value,size) ((fsp)->conn->vfs.ops.fgetxattr((fsp)->conn->vfs.handles.fgetxattr,(fsp),(fd),(name),(value),(size)))
+#define SMB_VFS_LISTXATTR(conn,path,list,size) ((conn)->vfs.ops.listxattr((conn)->vfs.handles.listxattr,(conn),(path),(list),(size)))
+#define SMB_VFS_LLISTXATTR(conn,path,list,size) ((conn)->vfs.ops.llistxattr((conn)->vfs.handles.llistxattr,(conn),(path),(list),(size)))
+#define SMB_VFS_FLISTXATTR(fsp,fd,list,size) ((fsp)->conn->vfs.ops.flistxattr((fsp)->conn->vfs.handles.flistxattr,(fsp),(fd),(list),(size)))
+#define SMB_VFS_REMOVEXATTR(conn,path,name) ((conn)->vfs.ops.removexattr((conn)->vfs.handles.removexattr,(conn),(path),(name)))
+#define SMB_VFS_LREMOVEXATTR(conn,path,name) ((conn)->vfs.ops.lremovexattr((conn)->vfs.handles.lremovexattr,(conn),(path),(name)))
+#define SMB_VFS_FREMOVEXATTR(fsp,fd,name) ((fsp)->conn->vfs.ops.fremovexattr((fsp)->conn->vfs.handles.fremovexattr,(fsp),(fd),(name)))
+#define SMB_VFS_SETXATTR(conn,path,name,value,size,flags) ((conn)->vfs.ops.setxattr((conn)->vfs.handles.setxattr,(conn),(path),(name),(value),(size),(flags)))
+#define SMB_VFS_LSETXATTR(conn,path,name,value,size,flags) ((conn)->vfs.ops.lsetxattr((conn)->vfs.handles.lsetxattr,(conn),(path),(name),(value),(size),(flags)))
+#define SMB_VFS_FSETXATTR(fsp,fd,name,value,size,flags) ((fsp)->conn->vfs.ops.fsetxattr((fsp)->conn->vfs.handles.fsetxattr,(fsp),(fd),(name),(value),(size),(flags)))
+
+/*******************************************************************
+ Don't access conn->vfs_opaque.ops directly!!!
+ Use this macros!
+ (Fixes should also go into the vfs_* and vfs_next_* macros!)
+********************************************************************/
+
+/* Disk operations */
+#define SMB_VFS_OPAQUE_CONNECT(conn, service, user) ((conn)->vfs_opaque.ops.connect((conn)->vfs_opaque.handles.connect, (conn), (service), (user)))
+#define SMB_VFS_OPAQUE_DISCONNECT(conn) ((conn)->vfs_opaque.ops.disconnect((conn)->vfs_opaque.handles.disconnect, (conn)))
+#define SMB_VFS_OPAQUE_DISK_FREE(conn, path, small_query, bsize, dfree ,dsize) ((conn)->vfs_opaque.ops.disk_free((conn)->vfs_opaque.handles.disk_free, (conn), (path), (small_query), (bsize), (dfree), (dsize)))
+#define SMB_VFS_OPAQUE_GET_QUOTA(conn, qtype, id, qt) ((conn)->vfs_opaque.ops.get_quota((conn)->vfs_opaque.handles.get_quota, (conn), (qtype), (id), (qt)))
+#define SMB_VFS_OPAQUE_SET_QUOTA(conn, qtype, id, qt) ((conn)->vfs_opaque.ops.set_quota((conn)->vfs_opaque.handles.set_quota, (conn), (qtype), (id), (qt)))
+#define SMB_VFS_OPAQUE_GET_SHADOW_COPY_DATA(fsp,shadow_copy_data,labels) ((fsp)->conn->vfs_opaque.ops.get_shadow_copy_data((fsp)->conn->vfs_opaque.handles.get_shadow_copy_data,(fsp),(shadow_copy_data),(labels)))
+
+/* Directory operations */
+#define SMB_VFS_OPAQUE_OPENDIR(conn, fname) ((conn)->vfs_opaque.ops.opendir((conn)->vfs_opaque.handles.opendir, (conn), (fname)))
+#define SMB_VFS_OPAQUE_READDIR(conn, dirp) ((conn)->vfs_opaque.ops.readdir((conn)->vfs_opaque.handles.readdir, (conn), (dirp)))
+#define SMB_VFS_OPAQUE_MKDIR(conn, path, mode) ((conn)->vfs_opaque.ops.mkdir((conn)->vfs_opaque.handles.mkdir,(conn), (path), (mode)))
+#define SMB_VFS_OPAQUE_RMDIR(conn, path) ((conn)->vfs_opaque.ops.rmdir((conn)->vfs_opaque.handles.rmdir, (conn), (path)))
+#define SMB_VFS_OPAQUE_CLOSEDIR(conn, dir) ((conn)->vfs_opaque.ops.closedir((conn)->vfs_opaque.handles.closedir, (conn), dir))
+
+/* File operations */
+#define SMB_VFS_OPAQUE_OPEN(conn, fname, flags, mode) ((conn)->vfs_opaque.ops.open((conn)->vfs_opaque.handles.open, (conn), (fname), (flags), (mode)))
+#define SMB_VFS_OPAQUE_CLOSE(fsp, fd) ((fsp)->conn->vfs_opaque.ops.close((fsp)->conn->vfs_opaque.handles.close, (fsp), (fd)))
+#define SMB_VFS_OPAQUE_READ(fsp, fd, data, n) ((fsp)->conn->vfs_opaque.ops.read((fsp)->conn->vfs_opaque.handles.read, (fsp), (fd), (data), (n)))
+#define SMB_VFS_OPAQUE_PREAD(fsp, fd, data, n, off) ((fsp)->conn->vfs_opaque.ops.pread((fsp)->conn->vfs_opaque.handles.pread, (fsp), (fd), (data), (n), (off)))
+#define SMB_VFS_OPAQUE_WRITE(fsp, fd, data, n) ((fsp)->conn->vfs_opaque.ops.write((fsp)->conn->vfs_opaque.handles.write, (fsp), (fd), (data), (n)))
+#define SMB_VFS_OPAQUE_PWRITE(fsp, fd, data, n, off) ((fsp)->conn->vfs_opaque.ops.pwrite((fsp)->conn->vfs_opaque.handles.pwrite, (fsp), (fd), (data), (n), (off)))
+#define SMB_VFS_OPAQUE_LSEEK(fsp, fd, offset, whence) ((fsp)->conn->vfs_opaque.ops.lseek((fsp)->conn->vfs_opaque.handles.lseek, (fsp), (fd), (offset), (whence)))
+#define SMB_VFS_OPAQUE_SENDFILE(tofd, fsp, fromfd, header, offset, count) ((fsp)->conn->vfs_opaque.ops.sendfile((fsp)->conn->vfs_opaque.handles.sendfile, (tofd), (fsp), (fromfd), (header), (offset), (count)))
+#define SMB_VFS_OPAQUE_RENAME(conn, old, new) ((conn)->vfs_opaque.ops.rename((conn)->vfs_opaque.handles.rename, (conn), (old), (new)))
+#define SMB_VFS_OPAQUE_FSYNC(fsp, fd) ((fsp)->conn->vfs_opaque.ops.fsync((fsp)->conn->vfs_opaque.handles.fsync, (fsp), (fd)))
+#define SMB_VFS_OPAQUE_STAT(conn, fname, sbuf) ((conn)->vfs_opaque.ops.stat((conn)->vfs_opaque.handles.stat, (conn), (fname), (sbuf)))
+#define SMB_VFS_OPAQUE_FSTAT(fsp, fd, sbuf) ((fsp)->conn->vfs_opaque.ops.fstat((fsp)->conn->vfs_opaque.handles.fstat, (fsp) ,(fd) ,(sbuf)))
+#define SMB_VFS_OPAQUE_LSTAT(conn, path, sbuf) ((conn)->vfs_opaque.ops.lstat((conn)->vfs_opaque.handles.lstat, (conn), (path), (sbuf)))
+#define SMB_VFS_OPAQUE_UNLINK(conn, path) ((conn)->vfs_opaque.ops.unlink((conn)->vfs_opaque.handles.unlink, (conn), (path)))
+#define SMB_VFS_OPAQUE_CHMOD(conn, path, mode) ((conn)->vfs_opaque.ops.chmod((conn)->vfs_opaque.handles.chmod, (conn), (path), (mode)))
+#define SMB_VFS_OPAQUE_FCHMOD(fsp, fd, mode) ((fsp)->conn->vfs_opaque.ops.fchmod((fsp)->conn->vfs_opaque.handles.fchmod, (fsp), (fd), (mode)))
+#define SMB_VFS_OPAQUE_CHOWN(conn, path, uid, gid) ((conn)->vfs_opaque.ops.chown((conn)->vfs_opaque.handles.chown, (conn), (path), (uid), (gid)))
+#define SMB_VFS_OPAQUE_FCHOWN(fsp, fd, uid, gid) ((fsp)->conn->vfs_opaque.ops.fchown((fsp)->conn->vfs_opaque.handles.fchown, (fsp), (fd), (uid), (gid)))
+#define SMB_VFS_OPAQUE_CHDIR(conn, path) ((conn)->vfs_opaque.ops.chdir((conn)->vfs_opaque.handles.chdir, (conn), (path)))
+#define SMB_VFS_OPAQUE_GETWD(conn, buf) ((conn)->vfs_opaque.ops.getwd((conn)->vfs_opaque.handles.getwd, (conn), (buf)))
+#define SMB_VFS_OPAQUE_UTIME(conn, path, times) ((conn)->vfs_opaque.ops.utime((conn)->vfs_opaque.handles.utime, (conn), (path), (times)))
+#define SMB_VFS_OPAQUE_FTRUNCATE(fsp, fd, offset) ((fsp)->conn->vfs_opaque.ops.ftruncate((fsp)->conn->vfs_opaque.handles.ftruncate, (fsp), (fd), (offset)))
+#define SMB_VFS_OPAQUE_LOCK(fsp, fd, op, offset, count, type) ((fsp)->conn->vfs_opaque.ops.lock((fsp)->conn->vfs_opaque.handles.lock, (fsp), (fd) ,(op), (offset), (count), (type)))
+#define SMB_VFS_OPAQUE_SYMLINK(conn, oldpath, newpath) ((conn)->vfs_opaque.ops.symlink((conn)->vfs_opaque.handles.symlink, (conn), (oldpath), (newpath)))
+#define SMB_VFS_OPAQUE_READLINK(conn, path, buf, bufsiz) ((conn)->vfs_opaque.ops.readlink((conn)->vfs_opaque.handles.readlink, (conn), (path), (buf), (bufsiz)))
+#define SMB_VFS_OPAQUE_LINK(conn, oldpath, newpath) ((conn)->vfs_opaque.ops.link((conn)->vfs_opaque.handles.link, (conn), (oldpath), (newpath)))
+#define SMB_VFS_OPAQUE_MKNOD(conn, path, mode, dev) ((conn)->vfs_opaque.ops.mknod((conn)->vfs_opaque.handles.mknod, (conn), (path), (mode), (dev)))
+#define SMB_VFS_OPAQUE_REALPATH(conn, path, resolved_path) ((conn)->vfs_opaque.ops.realpath((conn)->vfs_opaque.handles.realpath, (conn), (path), (resolved_path)))
+
+/* NT ACL operations. */
+#define SMB_VFS_OPAQUE_FGET_NT_ACL(fsp, fd, security_info, ppdesc) ((fsp)->conn->vfs_opaque.ops.fget_nt_acl((fsp)->conn->vfs_opaque.handles.fget_nt_acl, (fsp), (fd), (security_info), (ppdesc)))
+#define SMB_VFS_OPAQUE_GET_NT_ACL(fsp, name, security_info, ppdesc) ((fsp)->conn->vfs_opaque.ops.get_nt_acl((fsp)->conn->vfs_opaque.handles.get_nt_acl, (fsp), (name), (security_info), (ppdesc)))
+#define SMB_VFS_OPAQUE_FSET_NT_ACL(fsp, fd, security_info_sent, psd) ((fsp)->conn->vfs_opaque.ops.fset_nt_acl((fsp)->conn->vfs_opaque.handles.fset_nt_acl, (fsp), (fd), (security_info_sent), (psd)))
+#define SMB_VFS_OPAQUE_SET_NT_ACL(fsp, name, security_info_sent, psd) ((fsp)->conn->vfs_opaque.ops.set_nt_acl((fsp)->conn->vfs_opaque.handles.set_nt_acl, (fsp), (name), (security_info_sent), (psd)))
+
+/* POSIX ACL operations. */
+#define SMB_VFS_OPAQUE_CHMOD_ACL(conn, name, mode) ((conn)->vfs_opaque.ops.chmod_acl((conn)->vfs_opaque.handles.chmod_acl, (conn), (name), (mode)))
+#define SMB_VFS_OPAQUE_FCHMOD_ACL(fsp, fd, mode) ((fsp)->conn->vfs_opaque.ops.fchmod_acl((fsp)->conn->vfs_opaque.handles.chmod_acl, (fsp), (fd), (mode)))
+
+#define SMB_VFS_OPAQUE_SYS_ACL_GET_ENTRY(conn, theacl, entry_id, entry_p) ((conn)->vfs_opaque.ops.sys_acl_get_entry((conn)->vfs_opaque.handles.sys_acl_get_entry, (conn), (theacl), (entry_id), (entry_p)))
+#define SMB_VFS_OPAQUE_SYS_ACL_GET_TAG_TYPE(conn, entry_d, tag_type_p) ((conn)->vfs_opaque.ops.sys_acl_get_tag_type((conn)->vfs_opaque.handles.sys_acl_get_tag_type, (conn), (entry_d), (tag_type_p)))
+#define SMB_VFS_OPAQUE_SYS_ACL_GET_PERMSET(conn, entry_d, permset_p) ((conn)->vfs_opaque.ops.sys_acl_get_permset((conn)->vfs_opaque.handles.sys_acl_get_permset, (conn), (entry_d), (permset_p)))
+#define SMB_VFS_OPAQUE_SYS_ACL_GET_QUALIFIER(conn, entry_d) ((conn)->vfs_opaque.ops.sys_acl_get_qualifier((conn)->vfs_opaque.handles.sys_acl_get_qualifier, (conn), (entry_d)))
+#define SMB_VFS_OPAQUE_SYS_ACL_GET_FILE(conn, path_p, type) ((conn)->vfs_opaque.ops.sys_acl_get_file((conn)->vfs_opaque.handles.sys_acl_get_file, (conn), (path_p), (type)))
+#define SMB_VFS_OPAQUE_SYS_ACL_GET_FD(fsp, fd) ((fsp)->conn->vfs_opaque.ops.sys_acl_get_fd((fsp)->conn->vfs_opaque.handles.sys_acl_get_fd, (fsp), (fd)))
+#define SMB_VFS_OPAQUE_SYS_ACL_CLEAR_PERMS(conn, permset) ((conn)->vfs_opaque.ops.sys_acl_clear_perms((conn)->vfs_opaque.handles.sys_acl_clear_perms, (conn), (permset)))
+#define SMB_VFS_OPAQUE_SYS_ACL_ADD_PERM(conn, permset, perm) ((conn)->vfs_opaque.ops.sys_acl_add_perm((conn)->vfs_opaque.handles.sys_acl_add_perm, (conn), (permset), (perm)))
+#define SMB_VFS_OPAQUE_SYS_ACL_TO_TEXT(conn, theacl, plen) ((conn)->vfs_opaque.ops.sys_acl_to_text((conn)->vfs_opaque.handles.sys_acl_to_text, (conn), (theacl), (plen)))
+#define SMB_VFS_OPAQUE_SYS_ACL_INIT(conn, count) ((conn)->vfs_opaque.ops.sys_acl_init((conn)->vfs_opaque.handles.sys_acl_init, (conn), (count)))
+#define SMB_VFS_OPAQUE_SYS_ACL_CREATE_ENTRY(conn, pacl, pentry) ((conn)->vfs_opaque.ops.sys_acl_create_entry((conn)->vfs_opaque.handles.sys_acl_create_entry, (conn), (pacl), (pentry)))
+#define SMB_VFS_OPAQUE_SYS_ACL_SET_TAG_TYPE(conn, entry, tagtype) ((conn)->vfs_opaque.ops.sys_acl_set_tag_type((conn)->vfs_opaque.handles.sys_acl_set_tag_type, (conn), (entry), (tagtype)))
+#define SMB_VFS_OPAQUE_SYS_ACL_SET_QUALIFIER(conn, entry, qual) ((conn)->vfs_opaque.ops.sys_acl_set_qualifier((conn)->vfs_opaque.handles.sys_acl_set_qualifier, (conn), (entry), (qual)))
+#define SMB_VFS_OPAQUE_SYS_ACL_SET_PERMSET(conn, entry, permset) ((conn)->vfs_opaque.ops.sys_acl_set_permset((conn)->vfs_opaque.handles.sys_acl_set_permset, (conn), (entry), (permset)))
+#define SMB_VFS_OPAQUE_SYS_ACL_VALID(conn, theacl) ((conn)->vfs_opaque.ops.sys_acl_valid((conn)->vfs_opaque.handles.sys_acl_valid, (conn), (theacl)))
+#define SMB_VFS_OPAQUE_SYS_ACL_SET_FILE(conn, name, acltype, theacl) ((conn)->vfs_opaque.ops.sys_acl_set_file((conn)->vfs_opaque.handles.sys_acl_set_file, (conn), (name), (acltype), (theacl)))
+#define SMB_VFS_OPAQUE_SYS_ACL_SET_FD(fsp, fd, theacl) ((fsp)->conn->vfs_opaque.ops.sys_acl_set_fd((fsp)->conn->vfs_opaque.handles.sys_acl_set_fd, (fsp), (fd), (theacl)))
+#define SMB_VFS_OPAQUE_SYS_ACL_DELETE_DEF_FILE(conn, path) ((conn)->vfs_opaque.ops.sys_acl_delete_def_file((conn)->vfs_opaque.handles.sys_acl_delete_def_file, (conn), (path)))
+#define SMB_VFS_OPAQUE_SYS_ACL_GET_PERM(conn, permset, perm) ((conn)->vfs_opaque.ops.sys_acl_get_perm((conn)->vfs_opaque.handles.sys_acl_get_perm, (conn), (permset), (perm)))
+#define SMB_VFS_OPAQUE_SYS_ACL_FREE_TEXT(conn, text) ((conn)->vfs_opaque.ops.sys_acl_free_text((conn)->vfs_opaque.handles.sys_acl_free_text, (conn), (text)))
+#define SMB_VFS_OPAQUE_SYS_ACL_FREE_ACL(conn, posix_acl) ((conn)->vfs_opaque.ops.sys_acl_free_acl((conn)->vfs_opaque.handles.sys_acl_free_acl, (conn), (posix_acl)))
+#define SMB_VFS_OPAQUE_SYS_ACL_FREE_QUALIFIER(conn, qualifier, tagtype) ((conn)->vfs_opaque.ops.sys_acl_free_qualifier((conn)->vfs_opaque.handles.sys_acl_free_qualifier, (conn), (qualifier), (tagtype)))
+
+/* EA operations. */
+#define SMB_VFS_OPAQUE_GETXATTR(conn,path,name,value,size) ((conn)->vfs_opaque.ops.getxattr((conn)->vfs_opaque.handles.getxattr,(conn),(path),(name),(value),(size)))
+#define SMB_VFS_OPAQUE_LGETXATTR(conn,path,name,value,size) ((conn)->vfs_opaque.ops.lgetxattr((conn)->vfs_opaque.handles.lgetxattr,(conn),(path),(name),(value),(size)))
+#define SMB_VFS_OPAQUE_FGETXATTR(fsp,fd,name,value,size) ((fsp)->conn->vfs_opaque.ops.fgetxattr((fsp)->conn->vfs_opaque.handles.fgetxattr,(fsp),(fd),(name),(value),(size)))
+#define SMB_VFS_OPAQUE_LISTXATTR(conn,path,list,size) ((conn)->vfs_opaque.ops.listxattr((conn)->vfs_opaque.handles.listxattr,(conn),(path),(list),(size)))
+#define SMB_VFS_OPAQUE_LLISTXATTR(conn,path,list,size) ((conn)->vfs_opaque.ops.llistxattr((conn)->vfs_opaque.handles.llistxattr,(conn),(path),(list),(size)))
+#define SMB_VFS_OPAQUE_FLISTXATTR(fsp,fd,list,size) ((fsp)->conn->vfs_opaque.ops.flistxattr((fsp)->conn->vfs_opaque.handles.flistxattr,(fsp),(fd),(list),(size)))
+#define SMB_VFS_OPAQUE_REMOVEXATTR(conn,path,name) ((conn)->vfs_opaque.ops.removexattr((conn)->vfs_opaque.handles.removexattr,(conn),(path),(name)))
+#define SMB_VFS_OPAQUE_LREMOVEXATTR(conn,path,name) ((conn)->vfs_opaque.ops.lremovexattr((conn)->vfs_opaque.handles.lremovexattr,(conn),(path),(name)))
+#define SMB_VFS_OPAQUE_FREMOVEXATTR(fsp,fd,name) ((fsp)->conn->vfs_opaque.ops.fremovexattr((fsp)->conn->vfs_opaque.handles.fremovexattr,(fsp),(fd),(name)))
+#define SMB_VFS_OPAQUE_SETXATTR(conn,path,name,value,size,flags) ((conn)->vfs_opaque.ops.setxattr((conn)->vfs_opaque.handles.setxattr,(conn),(path),(name),(value),(size),(flags)))
+#define SMB_VFS_OPAQUE_LSETXATTR(conn,path,name,value,size,flags) ((conn)->vfs_opaque.ops.lsetxattr((conn)->vfs_opaque.handles.lsetxattr,(conn),(path),(name),(value),(size),(flags)))
+#define SMB_VFS_OPAQUE_FSETXATTR(fsp,fd,name,value,size,flags) ((fsp)->conn->vfs_opaque.ops.fsetxattr((fsp)->conn->vfs_opaque.handles.fsetxattr,(fsp),(fd),(name),(value),(size),(flags)))
+
+/*******************************************************************
+ Don't access handle->vfs_next.ops.* directly!!!
+ Use this macros!
+ (Fixes should go also into the vfs_* and vfs_opaque_* macros!)
+********************************************************************/
+
+/* Disk operations */
+#define SMB_VFS_NEXT_CONNECT(handle, conn, service, user) ((handle)->vfs_next.ops.connect((handle)->vfs_next.handles.connect, (conn), (service), (user)))
+#define SMB_VFS_NEXT_DISCONNECT(handle, conn) ((handle)->vfs_next.ops.disconnect((handle)->vfs_next.handles.disconnect, (conn)))
+#define SMB_VFS_NEXT_DISK_FREE(handle, conn, path, small_query, bsize, dfree ,dsize) ((handle)->vfs_next.ops.disk_free((handle)->vfs_next.handles.disk_free, (conn), (path), (small_query), (bsize), (dfree), (dsize)))
+#define SMB_VFS_NEXT_GET_QUOTA(handle, conn, qtype, id, qt) ((handle)->vfs_next.ops.get_quota((handle)->vfs_next.handles.get_quota, (conn), (qtype), (id), (qt)))
+#define SMB_VFS_NEXT_SET_QUOTA(handle, conn, qtype, id, qt) ((handle)->vfs_next.ops.set_quota((handle)->vfs_next.handles.set_quota, (conn), (qtype), (id), (qt)))
+#define SMB_VFS_NEXT_GET_SHADOW_COPY_DATA(handle, fsp, shadow_copy_data ,labels) ((handle)->vfs_next.ops.get_shadow_copy_data((handle)->vfs_next.handles.get_shadow_copy_data,(fsp),(shadow_copy_data),(labels)))
+
+/* Directory operations */
+#define SMB_VFS_NEXT_OPENDIR(handle, conn, fname) ((handle)->vfs_next.ops.opendir((handle)->vfs_next.handles.opendir, (conn), (fname)))
+#define SMB_VFS_NEXT_READDIR(handle, conn, dirp) ((handle)->vfs_next.ops.readdir((handle)->vfs_next.handles.readdir, (conn), (dirp)))
+#define SMB_VFS_NEXT_MKDIR(handle, conn, path, mode) ((handle)->vfs_next.ops.mkdir((handle)->vfs_next.handles.mkdir,(conn), (path), (mode)))
+#define SMB_VFS_NEXT_RMDIR(handle, conn, path) ((handle)->vfs_next.ops.rmdir((handle)->vfs_next.handles.rmdir, (conn), (path)))
+#define SMB_VFS_NEXT_CLOSEDIR(handle, conn, dir) ((handle)->vfs_next.ops.closedir((handle)->vfs_next.handles.closedir, (conn), dir))
+
+/* File operations */
+#define SMB_VFS_NEXT_OPEN(handle, conn, fname, flags, mode) ((handle)->vfs_next.ops.open((handle)->vfs_next.handles.open, (conn), (fname), (flags), (mode)))
+#define SMB_VFS_NEXT_CLOSE(handle, fsp, fd) ((handle)->vfs_next.ops.close((handle)->vfs_next.handles.close, (fsp), (fd)))
+#define SMB_VFS_NEXT_READ(handle, fsp, fd, data, n) ((handle)->vfs_next.ops.read((handle)->vfs_next.handles.read, (fsp), (fd), (data), (n)))
+#define SMB_VFS_NEXT_PREAD(handle, fsp, fd, data, n, off) ((handle)->vfs_next.ops.pread((handle)->vfs_next.handles.pread, (fsp), (fd), (data), (n), (off)))
+#define SMB_VFS_NEXT_WRITE(handle, fsp, fd, data, n) ((handle)->vfs_next.ops.write((handle)->vfs_next.handles.write, (fsp), (fd), (data), (n)))
+#define SMB_VFS_NEXT_PWRITE(handle, fsp, fd, data, n, off) ((handle)->vfs_next.ops.pwrite((handle)->vfs_next.handles.pwrite, (fsp), (fd), (data), (n), (off)))
+#define SMB_VFS_NEXT_LSEEK(handle, fsp, fd, offset, whence) ((handle)->vfs_next.ops.lseek((handle)->vfs_next.handles.lseek, (fsp), (fd), (offset), (whence)))
+#define SMB_VFS_NEXT_SENDFILE(handle, tofd, fsp, fromfd, header, offset, count) ((handle)->vfs_next.ops.sendfile((handle)->vfs_next.handles.sendfile, (tofd), (fsp), (fromfd), (header), (offset), (count)))
+#define SMB_VFS_NEXT_RENAME(handle, conn, old, new) ((handle)->vfs_next.ops.rename((handle)->vfs_next.handles.rename, (conn), (old), (new)))
+#define SMB_VFS_NEXT_FSYNC(handle, fsp, fd) ((handle)->vfs_next.ops.fsync((handle)->vfs_next.handles.fsync, (fsp), (fd)))
+#define SMB_VFS_NEXT_STAT(handle, conn, fname, sbuf) ((handle)->vfs_next.ops.stat((handle)->vfs_next.handles.stat, (conn), (fname), (sbuf)))
+#define SMB_VFS_NEXT_FSTAT(handle, fsp, fd, sbuf) ((handle)->vfs_next.ops.fstat((handle)->vfs_next.handles.fstat, (fsp) ,(fd) ,(sbuf)))
+#define SMB_VFS_NEXT_LSTAT(handle, conn, path, sbuf) ((handle)->vfs_next.ops.lstat((handle)->vfs_next.handles.lstat, (conn), (path), (sbuf)))
+#define SMB_VFS_NEXT_UNLINK(handle, conn, path) ((handle)->vfs_next.ops.unlink((handle)->vfs_next.handles.unlink, (conn), (path)))
+#define SMB_VFS_NEXT_CHMOD(handle, conn, path, mode) ((handle)->vfs_next.ops.chmod((handle)->vfs_next.handles.chmod, (conn), (path), (mode)))
+#define SMB_VFS_NEXT_FCHMOD(handle, fsp, fd, mode) ((handle)->vfs_next.ops.fchmod((handle)->vfs_next.handles.fchmod, (fsp), (fd), (mode)))
+#define SMB_VFS_NEXT_CHOWN(handle, conn, path, uid, gid) ((handle)->vfs_next.ops.chown((handle)->vfs_next.handles.chown, (conn), (path), (uid), (gid)))
+#define SMB_VFS_NEXT_FCHOWN(handle, fsp, fd, uid, gid) ((handle)->vfs_next.ops.fchown((handle)->vfs_next.handles.fchown, (fsp), (fd), (uid), (gid)))
+#define SMB_VFS_NEXT_CHDIR(handle, conn, path) ((handle)->vfs_next.ops.chdir((handle)->vfs_next.handles.chdir, (conn), (path)))
+#define SMB_VFS_NEXT_GETWD(handle, conn, buf) ((handle)->vfs_next.ops.getwd((handle)->vfs_next.handles.getwd, (conn), (buf)))
+#define SMB_VFS_NEXT_UTIME(handle, conn, path, times) ((handle)->vfs_next.ops.utime((handle)->vfs_next.handles.utime, (conn), (path), (times)))
+#define SMB_VFS_NEXT_FTRUNCATE(handle, fsp, fd, offset) ((handle)->vfs_next.ops.ftruncate((handle)->vfs_next.handles.ftruncate, (fsp), (fd), (offset)))
+#define SMB_VFS_NEXT_LOCK(handle, fsp, fd, op, offset, count, type) ((handle)->vfs_next.ops.lock((handle)->vfs_next.handles.lock, (fsp), (fd) ,(op), (offset), (count), (type)))
+#define SMB_VFS_NEXT_SYMLINK(handle, conn, oldpath, newpath) ((handle)->vfs_next.ops.symlink((handle)->vfs_next.handles.symlink, (conn), (oldpath), (newpath)))
+#define SMB_VFS_NEXT_READLINK(handle, conn, path, buf, bufsiz) ((handle)->vfs_next.ops.readlink((handle)->vfs_next.handles.readlink, (conn), (path), (buf), (bufsiz)))
+#define SMB_VFS_NEXT_LINK(handle, conn, oldpath, newpath) ((handle)->vfs_next.ops.link((handle)->vfs_next.handles.link, (conn), (oldpath), (newpath)))
+#define SMB_VFS_NEXT_MKNOD(handle, conn, path, mode, dev) ((handle)->vfs_next.ops.mknod((handle)->vfs_next.handles.mknod, (conn), (path), (mode), (dev)))
+#define SMB_VFS_NEXT_REALPATH(handle, conn, path, resolved_path) ((handle)->vfs_next.ops.realpath((handle)->vfs_next.handles.realpath, (conn), (path), (resolved_path)))
+
+/* NT ACL operations. */
+#define SMB_VFS_NEXT_FGET_NT_ACL(handle, fsp, fd, security_info, ppdesc) ((handle)->vfs_next.ops.fget_nt_acl((handle)->vfs_next.handles.fget_nt_acl, (fsp), (fd), (security_info), (ppdesc)))
+#define SMB_VFS_NEXT_GET_NT_ACL(handle, fsp, name, security_info, ppdesc) ((handle)->vfs_next.ops.get_nt_acl((handle)->vfs_next.handles.get_nt_acl, (fsp), (name), (security_info), (ppdesc)))
+#define SMB_VFS_NEXT_FSET_NT_ACL(handle, fsp, fd, security_info_sent, psd) ((handle)->vfs_next.ops.fset_nt_acl((handle)->vfs_next.handles.fset_nt_acl, (fsp), (fd), (security_info_sent), (psd)))
+#define SMB_VFS_NEXT_SET_NT_ACL(handle, fsp, name, security_info_sent, psd) ((handle)->vfs_next.ops.set_nt_acl((handle)->vfs_next.handles.set_nt_acl, (fsp), (name), (security_info_sent), (psd)))
+
+/* POSIX ACL operations. */
+#define SMB_VFS_NEXT_CHMOD_ACL(handle, conn, name, mode) ((handle)->vfs_next.ops.chmod_acl((handle)->vfs_next.handles.chmod_acl, (conn), (name), (mode)))
+#define SMB_VFS_NEXT_FCHMOD_ACL(handle, fsp, fd, mode) ((handle)->vfs_next.ops.fchmod_acl((handle)->vfs_next.handles.chmod_acl, (fsp), (fd), (mode)))
+
+#define SMB_VFS_NEXT_SYS_ACL_GET_ENTRY(handle, conn, theacl, entry_id, entry_p) ((handle)->vfs_next.ops.sys_acl_get_entry((handle)->vfs_next.handles.sys_acl_get_entry, (conn), (theacl), (entry_id), (entry_p)))
+#define SMB_VFS_NEXT_SYS_ACL_GET_TAG_TYPE(handle, conn, entry_d, tag_type_p) ((handle)->vfs_next.ops.sys_acl_get_tag_type((handle)->vfs_next.handles.sys_acl_get_tag_type, (conn), (entry_d), (tag_type_p)))
+#define SMB_VFS_NEXT_SYS_ACL_GET_PERMSET(handle, conn, entry_d, permset_p) ((handle)->vfs_next.ops.sys_acl_get_permset((handle)->vfs_next.handles.sys_acl_get_permset, (conn), (entry_d), (permset_p)))
+#define SMB_VFS_NEXT_SYS_ACL_GET_QUALIFIER(handle, conn, entry_d) ((handle)->vfs_next.ops.sys_acl_get_qualifier((handle)->vfs_next.handles.sys_acl_get_qualifier, (conn), (entry_d)))
+#define SMB_VFS_NEXT_SYS_ACL_GET_FILE(handle, conn, path_p, type) ((handle)->vfs_next.ops.sys_acl_get_file((handle)->vfs_next.handles.sys_acl_get_file, (conn), (path_p), (type)))
+#define SMB_VFS_NEXT_SYS_ACL_GET_FD(handle, fsp, fd) ((handle)->vfs_next.ops.sys_acl_get_fd((handle)->vfs_next.handles.sys_acl_get_fd, (fsp), (fd)))
+#define SMB_VFS_NEXT_SYS_ACL_CLEAR_PERMS(handle, conn, permset) ((handle)->vfs_next.ops.sys_acl_clear_perms((handle)->vfs_next.handles.sys_acl_clear_perms, (conn), (permset)))
+#define SMB_VFS_NEXT_SYS_ACL_ADD_PERM(handle, conn, permset, perm) ((handle)->vfs_next.ops.sys_acl_add_perm((handle)->vfs_next.handles.sys_acl_add_perm, (conn), (permset), (perm)))
+#define SMB_VFS_NEXT_SYS_ACL_TO_TEXT(handle, conn, theacl, plen) ((handle)->vfs_next.ops.sys_acl_to_text((handle)->vfs_next.handles.sys_acl_to_text, (conn), (theacl), (plen)))
+#define SMB_VFS_NEXT_SYS_ACL_INIT(handle, conn, count) ((handle)->vfs_next.ops.sys_acl_init((handle)->vfs_next.handles.sys_acl_init, (conn), (count)))
+#define SMB_VFS_NEXT_SYS_ACL_CREATE_ENTRY(handle, conn, pacl, pentry) ((handle)->vfs_next.ops.sys_acl_create_entry((handle)->vfs_next.handles.sys_acl_create_entry, (conn), (pacl), (pentry)))
+#define SMB_VFS_NEXT_SYS_ACL_SET_TAG_TYPE(handle, conn, entry, tagtype) ((handle)->vfs_next.ops.sys_acl_set_tag_type((handle)->vfs_next.handles.sys_acl_set_tag_type, (conn), (entry), (tagtype)))
+#define SMB_VFS_NEXT_SYS_ACL_SET_QUALIFIER(handle, conn, entry, qual) ((handle)->vfs_next.ops.sys_acl_set_qualifier((handle)->vfs_next.handles.sys_acl_set_qualifier, (conn), (entry), (qual)))
+#define SMB_VFS_NEXT_SYS_ACL_SET_PERMSET(handle, conn, entry, permset) ((handle)->vfs_next.ops.sys_acl_set_permset((handle)->vfs_next.handles.sys_acl_set_permset, (conn), (entry), (permset)))
+#define SMB_VFS_NEXT_SYS_ACL_VALID(handle, conn, theacl) ((handle)->vfs_next.ops.sys_acl_valid((handle)->vfs_next.handles.sys_acl_valid, (conn), (theacl)))
+#define SMB_VFS_NEXT_SYS_ACL_SET_FILE(handle, conn, name, acltype, theacl) ((handle)->vfs_next.ops.sys_acl_set_file((handle)->vfs_next.handles.sys_acl_set_file, (conn), (name), (acltype), (theacl)))
+#define SMB_VFS_NEXT_SYS_ACL_SET_FD(handle, fsp, fd, theacl) ((handle)->vfs_next.ops.sys_acl_set_fd((handle)->vfs_next.handles.sys_acl_set_fd, (fsp), (fd), (theacl)))
+#define SMB_VFS_NEXT_SYS_ACL_DELETE_DEF_FILE(handle, conn, path) ((handle)->vfs_next.ops.sys_acl_delete_def_file((handle)->vfs_next.handles.sys_acl_delete_def_file, (conn), (path)))
+#define SMB_VFS_NEXT_SYS_ACL_GET_PERM(handle, conn, permset, perm) ((handle)->vfs_next.ops.sys_acl_get_perm((handle)->vfs_next.handles.sys_acl_get_perm, (conn), (permset), (perm)))
+#define SMB_VFS_NEXT_SYS_ACL_FREE_TEXT(handle, conn, text) ((handle)->vfs_next.ops.sys_acl_free_text((handle)->vfs_next.handles.sys_acl_free_text, (conn), (text)))
+#define SMB_VFS_NEXT_SYS_ACL_FREE_ACL(handle, conn, posix_acl) ((handle)->vfs_next.ops.sys_acl_free_acl((handle)->vfs_next.handles.sys_acl_free_acl, (conn), (posix_acl)))
+#define SMB_VFS_NEXT_SYS_ACL_FREE_QUALIFIER(handle, conn, qualifier, tagtype) ((handle)->vfs_next.ops.sys_acl_free_qualifier((handle)->vfs_next.handles.sys_acl_free_qualifier, (conn), (qualifier), (tagtype)))
+
+/* EA operations. */
+#define SMB_VFS_NEXT_GETXATTR(handle,conn,path,name,value,size) ((handle)->vfs_next.ops.getxattr((handle)->vfs_next.handles.getxattr,(conn),(path),(name),(value),(size)))
+#define SMB_VFS_NEXT_LGETXATTR(handle,conn,path,name,value,size) ((handle)->vfs_next.ops.lgetxattr((handle)->vfs_next.handles.lgetxattr,(conn),(path),(name),(value),(size)))
+#define SMB_VFS_NEXT_FGETXATTR(handle,fsp,fd,name,value,size) ((handle)->vfs_next.ops.fgetxattr((handle)->vfs_next.handles.fgetxattr,(fsp),(fd),(name),(value),(size)))
+#define SMB_VFS_NEXT_LISTXATTR(handle,conn,path,list,size) ((handle)->vfs_next.ops.listxattr((handle)->vfs_next.handles.listxattr,(conn),(path),(list),(size)))
+#define SMB_VFS_NEXT_LLISTXATTR(handle,conn,path,list,size) ((handle)->vfs_next.ops.llistxattr((handle)->vfs_next.handles.llistxattr,(conn),(path),(list),(size)))
+#define SMB_VFS_NEXT_FLISTXATTR(handle,fsp,fd,list,size) ((handle)->vfs_next.ops.flistxattr((handle)->vfs_next.handles.flistxattr,(fsp),(fd),(list),(size)))
+#define SMB_VFS_NEXT_REMOVEXATTR(handle,conn,path,name) ((handle)->vfs_next.ops.removexattr((handle)->vfs_next.handles.removexattr,(conn),(path),(name)))
+#define SMB_VFS_NEXT_LREMOVEXATTR(handle,conn,path,name) ((handle)->vfs_next.ops.lremovexattr((handle)->vfs_next.handles.lremovexattr,(conn),(path),(name)))
+#define SMB_VFS_NEXT_FREMOVEXATTR(handle,fsp,fd,name) ((handle)->vfs_next.ops.fremovexattr((handle)->vfs_next.handles.fremovexattr,(fsp),(fd),(name)))
+#define SMB_VFS_NEXT_SETXATTR(handle,conn,path,name,value,size,flags) ((handle)->vfs_next.ops.setxattr((handle)->vfs_next.handles.setxattr,(conn),(path),(name),(value),(size),(flags)))
+#define SMB_VFS_NEXT_LSETXATTR(handle,conn,path,name,value,size,flags) ((handle)->vfs_next.ops.lsetxattr((handle)->vfs_next.handles.lsetxattr,(conn),(path),(name),(value),(size),(flags)))
+#define SMB_VFS_NEXT_FSETXATTR(handle,fsp,fd,name,value,size,flags) ((handle)->vfs_next.ops.fsetxattr((handle)->vfs_next.handles.fsetxattr,(fsp),(fd),(name),(value),(size),(flags)))
+
+#endif /* _VFS_MACROS_H */
diff --git a/source/include/xfile.h b/source/include/xfile.h
new file mode 100644
index 00000000000..89fa9d1e118
--- /dev/null
+++ b/source/include/xfile.h
@@ -0,0 +1,49 @@
+/*
+ Unix SMB/CIFS implementation.
+ stdio replacement
+ 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 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 _XFILE_H_
+#define _XFILE_H_
+/*
+ see xfile.c for explanations
+*/
+
+typedef struct {
+ int fd;
+ char *buf;
+ char *next;
+ int bufsize;
+ int bufused;
+ int open_flags;
+ int buftype;
+ int flags;
+} XFILE;
+
+extern XFILE *x_stdin, *x_stdout, *x_stderr;
+
+/* buffering type */
+#define X_IOFBF 0
+#define X_IOLBF 1
+#define X_IONBF 2
+
+#define x_getc(f) x_fgetc(f)
+
+int x_vfprintf(XFILE *f, const char *format, va_list ap) PRINTF_ATTRIBUTE(2, 0);
+int x_fprintf(XFILE *f, const char *format, ...) PRINTF_ATTRIBUTE(2, 3);
+#endif /* _XFILE_H_ */
diff --git a/source/install-sh b/source/install-sh
new file mode 100755
index 00000000000..58719246f04
--- /dev/null
+++ b/source/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/source/intl/.cvsignore b/source/intl/.cvsignore
new file mode 100644
index 00000000000..5f2a5c4cf75
--- /dev/null
+++ b/source/intl/.cvsignore
@@ -0,0 +1,2 @@
+*.po
+*.po32
diff --git a/source/intl/lang_tdb.c b/source/intl/lang_tdb.c
new file mode 100644
index 00000000000..fe2ad5b2fce
--- /dev/null
+++ b/source/intl/lang_tdb.c
@@ -0,0 +1,269 @@
+/*
+ Unix SMB/CIFS implementation.
+ tdb based replacement for gettext
+ 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 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"
+
+static TDB_CONTEXT *tdb;
+
+/* the currently selected language */
+static char *current_lang;
+
+
+/* load a msg file into the tdb */
+static BOOL load_msg(const char *msg_file)
+{
+ char **lines;
+ int num_lines, i;
+ char *msgid, *msgstr;
+ TDB_DATA key, data;
+
+ lines = file_lines_load(msg_file, &num_lines);
+
+ if (!lines) {
+ return False;
+ }
+
+ if (tdb_lockall(tdb) != 0) return False;
+
+ /* wipe the db */
+ tdb_traverse(tdb, tdb_traverse_delete_fn, NULL);
+
+ msgid = NULL;
+
+ for (i=0;i<num_lines;i++) {
+ if (strncmp(lines[i], "msgid \"", 7) == 0) {
+ msgid = lines[i] + 7;
+ }
+ if (msgid && strncmp(lines[i], "msgstr \"", 8) == 0) {
+ msgstr = lines[i] + 8;
+ trim_char(msgid, '\0', '\"');
+ trim_char(msgstr, '\0', '\"');
+ if (*msgstr == 0) {
+ msgstr = msgid;
+ }
+ all_string_sub(msgid, "\\n", "\n", 0);
+ all_string_sub(msgstr, "\\n", "\n", 0);
+ key.dptr = msgid;
+ key.dsize = strlen(msgid)+1;
+ data.dptr = msgstr;
+ data.dsize = strlen(msgstr)+1;
+ tdb_store(tdb, key, data, 0);
+ msgid = NULL;
+ }
+ }
+
+ file_lines_free(lines);
+ tdb_unlockall(tdb);
+
+ return True;
+}
+
+
+/* work out what language to use from locale variables */
+static const char *get_lang(void)
+{
+ const char *vars[] = {"LANGUAGE", "LC_ALL", "LC_LANG", "LANG", NULL};
+ int i;
+ char *p;
+
+ for (i=0; vars[i]; i++) {
+ if ((p = getenv(vars[i]))) {
+ return p;
+ }
+ }
+
+ return NULL;
+}
+
+/* initialise the message translation subsystem. If the "lang" argument
+ is NULL then get the language from the normal environment variables */
+BOOL lang_tdb_init(const char *lang)
+{
+ char *path = NULL;
+ char *msg_path = NULL;
+ struct stat st;
+ static int initialised;
+ time_t loadtime;
+ BOOL result = False;
+
+ /* we only want to init once per process, unless given
+ an override */
+ if (initialised && !lang)
+ return True;
+
+ if (initialised) {
+ /* we are re-initialising, free up any old init */
+ if (tdb) {
+ tdb_close(tdb);
+ tdb = NULL;
+ }
+ SAFE_FREE(current_lang);
+ }
+
+ initialised = 1;
+
+ if (!lang) {
+ /* no lang given, use environment */
+ lang = get_lang();
+ }
+
+ /* if no lang then we don't translate */
+ if (!lang)
+ return True;
+
+ asprintf(&msg_path, "%s.msg", lib_path((const char *)lang));
+ if (stat(msg_path, &st) != 0) {
+ /* the msg file isn't available */
+ DEBUG(10, ("lang_tdb_init: %s: %s\n", msg_path,
+ strerror(errno)));
+ goto done;
+ }
+
+ asprintf(&path, "%s%s.tdb", lock_path("lang_"), lang);
+
+ DEBUG(10, ("lang_tdb_init: loading %s\n", path));
+
+ tdb = tdb_open_log(path, 0, TDB_DEFAULT, O_RDWR|O_CREAT, 0644);
+ if (!tdb) {
+ tdb = tdb_open_log(path, 0, TDB_DEFAULT, O_RDONLY, 0);
+ if (!tdb) {
+ DEBUG(10, ("lang_tdb_init: %s: %s\n", path,
+ strerror(errno)));
+ goto done;
+ }
+ current_lang = strdup(lang);
+ result = True;
+ goto done;
+ }
+
+ loadtime = tdb_fetch_int32(tdb, "/LOADTIME/");
+
+ if (loadtime == -1 || loadtime < st.st_mtime) {
+ load_msg(msg_path);
+ tdb_store_int32(tdb, "/LOADTIME/", (int)time(NULL));
+ }
+
+ current_lang = strdup(lang);
+ result = True;
+
+ done:
+ SAFE_FREE(msg_path);
+ SAFE_FREE(path);
+
+ return result;
+}
+
+/* translate a msgid to a message string in the current language
+ returns a string that must be freed by calling lang_msg_free()
+*/
+const char *lang_msg(const char *msgid)
+{
+ TDB_DATA key, data;
+ const char *p;
+ char *q, *msgid_quoted;
+ int count;
+
+ lang_tdb_init(NULL);
+
+ if (!tdb) return msgid;
+
+ /* Due to the way quotes in msgids are escaped in the msg file we
+ must replace " with \" before doing a lookup in the tdb. */
+
+ count = 0;
+
+ for(p = msgid; *p; p++) {
+ if (*p == '\"')
+ count++;
+ }
+
+ if (!(msgid_quoted = malloc(strlen(msgid) + count + 1)))
+ return msgid;
+
+ /* string_sub() is unsuitable here as it replaces some punctuation
+ chars with underscores. */
+
+ for(p = msgid, q = msgid_quoted; *p; p++) {
+ if (*p == '\"') {
+ *q = '\\';
+ q++;
+ }
+ *q = *p;
+ q++;
+ }
+
+ *q = 0;
+
+ key.dptr = (char *)msgid_quoted;
+ key.dsize = strlen(msgid_quoted)+1;
+
+ data = tdb_fetch(tdb, key);
+
+ free(msgid_quoted);
+
+ /* if the message isn't found then we still need to return a pointer
+ that can be freed. Pity. */
+ if (!data.dptr)
+ return strdup(msgid);
+
+ return (const char *)data.dptr;
+}
+
+
+/* free up a string from lang_msg() */
+void lang_msg_free(const char *msgstr)
+{
+ if (!tdb) return;
+ free((void *)msgstr);
+}
+
+
+/*
+ when the _() translation macro is used there is no obvious place to free
+ the resulting string and there is no easy way to give a static pointer.
+ All we can do is rotate between some static buffers and hope a single d_printf()
+ doesn't have more calls to _() than the number of buffers
+*/
+const char *lang_msg_rotate(const char *msgid)
+{
+#define NUM_LANG_BUFS 16
+ char *msgstr;
+ static pstring bufs[NUM_LANG_BUFS];
+ static int next;
+
+ msgstr = (char *)lang_msg(msgid);
+ if (!msgstr) return msgid;
+
+ pstrcpy(bufs[next], msgstr);
+ msgstr = bufs[next];
+
+ next = (next+1) % NUM_LANG_BUFS;
+
+ return msgstr;
+}
+
+
+/*
+ return the current language - needed for language file mappings
+*/
+char *lang_tdb_current(void)
+{
+ return current_lang;
+}
diff --git a/source/intl/linux-msg.sed b/source/intl/linux-msg.sed
new file mode 100644
index 00000000000..5918e720a9a
--- /dev/null
+++ b/source/intl/linux-msg.sed
@@ -0,0 +1,100 @@
+# po2msg.sed - Convert Uniforum style .po file to Linux style .msg file
+# Copyright (C) 1995 Free Software Foundation, Inc.
+# Ulrich Drepper <drepper@gnu.ai.mit.edu>, 1995.
+#
+# This program is free software; you can 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+#
+#
+# The first directive in the .msg should be the definition of the
+# message set number. We use always set number 1.
+#
+1 {
+ i\
+$set 1 # Automatically created by po2msg.sed
+ h
+ s/.*/0/
+ x
+}
+#
+# Mitch's old catalog format does not allow comments.
+#
+# We copy the original message as a comment into the .msg file.
+#
+/^msgid/ {
+ s/msgid[ ]*"//
+#
+# This does not work now with the new format.
+# /"$/! {
+# s/\\$//
+# s/$/ ... (more lines following)"/
+# }
+ x
+# The following nice solution is by
+# Bruno <Haible@ma2s2.mathematik.uni-karlsruhe.de>
+ td
+# Increment a decimal number in pattern space.
+# First hide trailing `9' digits.
+ :d
+ s/9\(_*\)$/_\1/
+ td
+# Assure at least one digit is available.
+ s/^\(_*\)$/0\1/
+# Increment the last digit.
+ s/8\(_*\)$/9\1/
+ s/7\(_*\)$/8\1/
+ s/6\(_*\)$/7\1/
+ s/5\(_*\)$/6\1/
+ s/4\(_*\)$/5\1/
+ s/3\(_*\)$/4\1/
+ s/2\(_*\)$/3\1/
+ s/1\(_*\)$/2\1/
+ s/0\(_*\)$/1\1/
+# Convert the hidden `9' digits to `0's.
+ s/_/0/g
+ x
+ G
+ s/\(.*\)"\n\([0-9]*\)/$ #\2 Original Message:(\1)/p
+}
+#
+# The .msg file contains, other then the .po file, only the translations
+# but each given a unique ID. Starting from 1 and incrementing by 1 for
+# each message we assign them to the messages.
+# It is important that the .po file used to generate the cat-id-tbl.c file
+# (with po-to-tbl) is the same as the one used here. (At least the order
+# of declarations must not be changed.)
+#
+/^msgstr/ {
+ s/msgstr[ ]*"\(.*\)"/# \1/
+# Clear substitution flag.
+ tb
+# Append the next line.
+ :b
+ N
+# Look whether second part is continuation line.
+ s/\(.*\n\)"\(.*\)"/\1\2/
+# Yes, then branch.
+ ta
+ P
+ D
+# Note that D includes a jump to the start!!
+# We found a continuation line. But before printing insert '\'.
+ :a
+ s/\(.*\)\(\n.*\)/\1\\\2/
+ P
+# We cannot use D here.
+ s/.*\n\(.*\)/\1/
+ tb
+}
+d
diff --git a/source/lib/.cvsignore b/source/lib/.cvsignore
new file mode 100644
index 00000000000..07da2225c72
--- /dev/null
+++ b/source/lib/.cvsignore
@@ -0,0 +1,3 @@
+*.po
+*.po32
+
diff --git a/source/lib/access.c b/source/lib/access.c
new file mode 100644
index 00000000000..f03f5daf333
--- /dev/null
+++ b/source/lib/access.c
@@ -0,0 +1,333 @@
+/*
+ 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"
+
+#define FAIL (-1)
+
+#define ALLONES ((uint32)0xFFFFFFFF)
+
+/* masked_match - match address against netnumber/netmask */
+static BOOL masked_match(const char *tok, const char *slash, const char *s)
+{
+ uint32 net;
+ uint32 mask;
+ uint32 addr;
+ fstring tok_cpy;
+
+ if ((addr = interpret_addr(s)) == INADDR_NONE)
+ return (False);
+
+ fstrcpy(tok_cpy, tok);
+ tok_cpy[PTR_DIFF(slash,tok)] = '\0';
+ net = interpret_addr(tok_cpy);
+ tok_cpy[PTR_DIFF(slash,tok)] = '/';
+
+ if (strlen(slash + 1) > 2) {
+ mask = interpret_addr(slash + 1);
+ } else {
+ mask = (uint32)((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(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))
+ && strequal(tok, s + str_len - tok_len))
+ return (True);
+ } else if (tok[0] == '@') { /* netgroup: look it up */
+#ifdef HAVE_NETGROUP
+ static char *mydomain = NULL;
+ char *hostname = NULL;
+ BOOL netgroup_ok = False;
+
+ if (!mydomain)
+ yp_get_default_domain(&mydomain);
+
+ if (!mydomain) {
+ DEBUG(0,("Unable to get default yp domain.\n"));
+ return False;
+ }
+ if (!(hostname = strdup(s))) {
+ DEBUG(1,("out of memory for strdup!\n"));
+ return False;
+ }
+
+ netgroup_ok = innetgr(tok + 1, hostname, (char *) 0, mydomain);
+
+ DEBUG(5,("looking for %s of domain %s in netgroup %s gave %s\n",
+ hostname,
+ mydomain,
+ tok+1,
+ BOOLSTR(netgroup_ok)));
+
+ SAFE_FREE(hostname);
+
+ if (netgroup_ok)
+ return(True);
+#else
+ DEBUG(0,("access: netgroup support is not configured\n"));
+ return (False);
+#endif
+ } else if (strequal(tok, "ALL")) { /* all: match any */
+ return (True);
+ } else if (strequal(tok, "FAIL")) { /* fail: match any */
+ return (FAIL);
+ } else if (strequal(tok, "LOCAL")) { /* local: no dots */
+ if (strchr_m(s, '.') == 0 && !strequal(s, "unknown"))
+ return (True);
+ } else if (strequal(tok, s)) { /* 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_m(tok, '/')) != 0) { /* netnumber/netmask */
+ if (isdigit((int)s[0]) && masked_match(tok, cut, s))
+ return (True);
+ } else if (strchr_m(tok, '*') != 0) {
+ *invalid_char = '*';
+ } else if (strchr_m(tok, '?') != 0) {
+ *invalid_char = '?';
+ }
+ return (False);
+}
+
+/* client_match - match host name and address against token */
+static BOOL client_match(const char *tok, const char *item)
+{
+ const char **client = (const char **)item;
+ 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(tok, client[1], &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[0][0] != 0)
+ match = string_match(tok, client[0], &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(const char **list,const char *item,
+ BOOL (*match_fn)(const char *, const char *))
+{
+ 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 (strequal(*list, "EXCEPT")) /* EXCEPT: give up */
+ break;
+ if ((match = (*match_fn) (*list, item))) /* True or FAIL */
+ break;
+ }
+ /* Process exceptions to True or FAIL matches. */
+
+ if (match != False) {
+ while (*list && !strequal(*list, "EXCEPT"))
+ list++;
+
+ for (; *list; list++) {
+ if ((*match_fn) (*list, item)) /* Exception Found */
+ return False;
+ }
+ }
+
+ return (match);
+}
+
+/* return true if access should be allowed */
+static BOOL allow_access_internal(const char **deny_list,const char **allow_list,
+ const char *cname, const char *caddr)
+{
+ const char *client[2];
+
+ client[0] = cname;
+ client[1] = 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(deny_list,(const char *)client,client_match) &&
+ (!allow_list ||
+ !list_match(allow_list,(const char *)client, client_match))) {
+ 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(allow_list,(const char *)client,client_match));
+
+ /* 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(deny_list,(const char *)client,client_match));
+
+ /* if there are both types of list then allow all hosts on the
+ allow list */
+ if (list_match(allow_list,(const char *)client,client_match))
+ 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(deny_list,(const char *)client,client_match))
+ return (False);
+
+ return (True);
+}
+
+/* return true if access should be allowed */
+BOOL allow_access(const char **deny_list, const char **allow_list,
+ const char *cname, const char *caddr)
+{
+ BOOL ret;
+ char *nc_cname = smb_xstrdup(cname);
+ char *nc_caddr = smb_xstrdup(caddr);
+
+ ret = allow_access_internal(deny_list, allow_list, nc_cname, nc_caddr);
+
+ SAFE_FREE(nc_cname);
+ SAFE_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 (strequal(*list, "ALL") || strequal(*list, "FAIL") ||
+ strequal(*list, "EXCEPT")) {
+ 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_m(*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 check_access(int sock, const char **allow_list, const char **deny_list)
+{
+ BOOL ret = False;
+ BOOL only_ip = False;
+
+ if ((!deny_list || *deny_list==0) && (!allow_list || *allow_list==0))
+ ret = True;
+
+ if (!ret) {
+ /* bypass gethostbyaddr() calls if the lists only contain IP addrs */
+ if (only_ipaddrs_in_list(allow_list) && only_ipaddrs_in_list(deny_list)) {
+ only_ip = True;
+ DEBUG (3, ("check_access: no hostnames in host allow/deny list.\n"));
+ ret = allow_access(deny_list,allow_list, "", get_peer_addr(sock));
+ } else {
+ DEBUG (3, ("check_access: hostnames in host allow/deny list.\n"));
+ ret = allow_access(deny_list,allow_list, get_peer_name(sock,True),
+ get_peer_addr(sock));
+ }
+
+ if (ret) {
+ DEBUG(2,("Allowed connection from %s (%s)\n",
+ only_ip ? "" : get_peer_name(sock,True),
+ get_peer_addr(sock)));
+ } else {
+ DEBUG(0,("Denied connection from %s (%s)\n",
+ only_ip ? "" : get_peer_name(sock,True),
+ get_peer_addr(sock)));
+ }
+ }
+
+ return(ret);
+}
diff --git a/source/lib/account_pol.c b/source/lib/account_pol.c
new file mode 100644
index 00000000000..c2c63493a6f
--- /dev/null
+++ b/source/lib/account_pol.c
@@ -0,0 +1,162 @@
+/*
+ * Unix SMB/CIFS implementation.
+ * account policy storage
+ * Copyright (C) Jean François Micouleau 1998-2001.
+ * 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 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"
+static TDB_CONTEXT *tdb; /* used for driver files */
+
+#define DATABASE_VERSION 1
+
+/****************************************************************************
+ Open the account policy tdb.
+****************************************************************************/
+
+BOOL init_account_policy(void)
+{
+ static pid_t local_pid;
+ const char *vstring = "INFO/version";
+ uint32 version;
+
+ if (tdb && local_pid == sys_getpid())
+ return True;
+ tdb = tdb_open_log(lock_path("account_policy.tdb"), 0, TDB_DEFAULT, O_RDWR|O_CREAT, 0600);
+ if (!tdb) {
+ DEBUG(0,("Failed to open account policy database\n"));
+ return False;
+ }
+
+ local_pid = sys_getpid();
+
+ /* handle a Samba upgrade */
+ tdb_lock_bystring(tdb, vstring,0);
+ if (!tdb_fetch_uint32(tdb, vstring, &version) || version != DATABASE_VERSION) {
+ tdb_traverse(tdb, tdb_traverse_delete_fn, NULL);
+ tdb_store_uint32(tdb, vstring, DATABASE_VERSION);
+
+ account_policy_set(AP_MIN_PASSWORD_LEN, MINPASSWDLENGTH); /* 5 chars minimum */
+ account_policy_set(AP_PASSWORD_HISTORY, 0); /* don't keep any old password */
+ account_policy_set(AP_USER_MUST_LOGON_TO_CHG_PASS, 0); /* don't force user to logon */
+ account_policy_set(AP_MAX_PASSWORD_AGE, (uint32)-1); /* don't expire */
+ account_policy_set(AP_MIN_PASSWORD_AGE, 0); /* 0 days */
+ account_policy_set(AP_LOCK_ACCOUNT_DURATION, 30); /* lockout for 30 minutes */
+ account_policy_set(AP_RESET_COUNT_TIME, 30); /* reset after 30 minutes */
+ account_policy_set(AP_BAD_ATTEMPT_LOCKOUT, 0); /* don't lockout */
+ account_policy_set(AP_TIME_TO_LOGOUT, -1); /* don't force logout */
+ }
+ tdb_unlock_bystring(tdb, vstring);
+
+ return True;
+}
+
+static const struct {
+ int field;
+ const char *string;
+} account_policy_names[] = {
+ {AP_MIN_PASSWORD_LEN, "min password length"},
+ {AP_PASSWORD_HISTORY, "password history"},
+ {AP_USER_MUST_LOGON_TO_CHG_PASS, "user must logon to change password"},
+ {AP_MAX_PASSWORD_AGE, "maximum password age"},
+ {AP_MIN_PASSWORD_AGE,"minimum password age"},
+ {AP_LOCK_ACCOUNT_DURATION, "lockout duration"},
+ {AP_RESET_COUNT_TIME, "reset count minutes"},
+ {AP_BAD_ATTEMPT_LOCKOUT, "bad lockout attempt"},
+ {AP_TIME_TO_LOGOUT, "disconnect time"},
+ {0, NULL}
+};
+
+/****************************************************************************
+Get the account policy name as a string from its #define'ed number
+****************************************************************************/
+
+static const char *decode_account_policy_name(int field)
+{
+ int i;
+ for (i=0; account_policy_names[i].string; i++) {
+ if (field == account_policy_names[i].field)
+ return account_policy_names[i].string;
+ }
+ return NULL;
+
+}
+
+/****************************************************************************
+Get the account policy name as a string from its #define'ed number
+****************************************************************************/
+
+int account_policy_name_to_fieldnum(const char *name)
+{
+ int i;
+ for (i=0; account_policy_names[i].string; i++) {
+ if (strcmp(name, account_policy_names[i].string) == 0)
+ return account_policy_names[i].field;
+ }
+ return 0;
+
+}
+
+
+/****************************************************************************
+****************************************************************************/
+BOOL account_policy_get(int field, uint32 *value)
+{
+ fstring name;
+
+ if(!init_account_policy())return False;
+
+ *value = 0;
+
+ fstrcpy(name, decode_account_policy_name(field));
+ if (!*name) {
+ DEBUG(1, ("account_policy_get: Field %d is not a valid account policy type! Cannot get, returning 0.\n", field));
+ return False;
+ }
+ if (!tdb_fetch_uint32(tdb, name, value)) {
+ DEBUG(1, ("account_policy_get: tdb_fetch_uint32 failed for efild %d (%s), returning 0", field, name));
+ return False;
+ }
+ DEBUG(10,("account_policy_get: %s:%d\n", name, *value));
+ return True;
+}
+
+
+/****************************************************************************
+****************************************************************************/
+BOOL account_policy_set(int field, uint32 value)
+{
+ fstring name;
+
+ if(!init_account_policy())return False;
+
+ fstrcpy(name, decode_account_policy_name(field));
+ if (!*name) {
+ DEBUG(1, ("Field %d is not a valid account policy type! Cannot set.\n", field));
+ return False;
+ }
+
+ if (!tdb_store_uint32(tdb, name, value)) {
+ DEBUG(1, ("tdb_store_uint32 failed for field %d (%s) on value %u", field, name, value));
+ return False;
+ }
+
+ DEBUG(10,("account_policy_set: %s:%d\n", name, value));
+
+ return True;
+}
+
diff --git a/source/lib/adt_tree.c b/source/lib/adt_tree.c
new file mode 100644
index 00000000000..bd857e205ac
--- /dev/null
+++ b/source/lib/adt_tree.c
@@ -0,0 +1,464 @@
+/*
+ * Unix SMB/CIFS implementation.
+ * Generic Abstract Data Types
+ * Copyright (C) Gerald Carter 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 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"
+
+
+/**************************************************************************
+ Initialize the tree's root. The cmp_fn is a callback function used
+ for comparision of two children
+ *************************************************************************/
+
+static BOOL trim_tree_keypath( char *path, char **base, char **new_path )
+{
+ char *p;
+
+ *new_path = *base = NULL;
+
+ if ( !path )
+ return False;
+
+ *base = path;
+
+ p = strchr( path, '/' );
+
+ if ( p ) {
+ *p = '\0';
+ *new_path = p+1;
+ }
+
+ return True;
+}
+
+
+/**************************************************************************
+ Initialize the tree's root. The cmp_fn is a callback function used
+ for comparision of two children
+ *************************************************************************/
+
+SORTED_TREE* sorted_tree_init( void *data_p,
+ int (cmp_fn)(void*, void*),
+ void (free_fn)(void*) )
+{
+ SORTED_TREE *tree = NULL;
+
+ if ( !(tree = (SORTED_TREE*)malloc( sizeof(SORTED_TREE) )) )
+ return NULL;
+
+ ZERO_STRUCTP( tree );
+
+ tree->compare = cmp_fn;
+ tree->free_func = free_fn;
+
+ if ( !(tree->root = (TREE_NODE*)malloc( sizeof(TREE_NODE) )) ) {
+ SAFE_FREE( tree );
+ return NULL;
+ }
+
+ ZERO_STRUCTP( tree->root );
+ tree->root->data_p = data_p;
+
+ return tree;
+}
+
+
+/**************************************************************************
+ Delete a tree and free all allocated memory
+ *************************************************************************/
+
+static void sorted_tree_destroy_children( TREE_NODE *root )
+{
+ int i;
+
+ if ( !root )
+ return;
+
+ for ( i=0; i<root->num_children; i++ )
+ {
+ sorted_tree_destroy_children( root->children[i] );
+ }
+
+ SAFE_FREE( root->children );
+ SAFE_FREE( root->key );
+
+ return;
+}
+
+/**************************************************************************
+ Delete a tree and free all allocated memory
+ *************************************************************************/
+
+void sorted_tree_destroy( SORTED_TREE *tree )
+{
+ if ( tree->root )
+ sorted_tree_destroy_children( tree->root );
+
+ if ( tree->free_func )
+ tree->free_func( tree->root );
+
+ SAFE_FREE( tree );
+}
+
+/**************************************************************************
+ Find the next child given a key string
+ *************************************************************************/
+
+static TREE_NODE* sorted_tree_birth_child( TREE_NODE *node, char* key )
+{
+ TREE_NODE *infant = NULL;
+ TREE_NODE **siblings;
+ int i;
+
+ if ( !(infant = (TREE_NODE*)malloc( sizeof(TREE_NODE) )) )
+ return NULL;
+
+ ZERO_STRUCTP( infant );
+
+ infant->key = strdup( key );
+ infant->parent = node;
+
+ siblings = Realloc( node->children, sizeof(TREE_NODE*)*(node->num_children+1) );
+
+ if ( siblings )
+ node->children = siblings;
+
+ node->num_children++;
+
+ /* first child */
+
+ if ( node->num_children == 1 ) {
+ DEBUG(11,("sorted_tree_birth_child: First child of node [%s]! [%s]\n",
+ node->key ? node->key : "NULL", infant->key ));
+ node->children[0] = infant;
+ }
+ else
+ {
+ /*
+ * multiple siblings .... (at least 2 children)
+ *
+ * work from the end of the list forward
+ * The last child is not set at this point
+ * Insert the new infanct in ascending order
+ * from left to right
+ */
+
+ for ( i = node->num_children-1; i>=1; i-- )
+ {
+ DEBUG(11,("sorted_tree_birth_child: Looking for crib; infant -> [%s], child -> [%s]\n",
+ infant->key, node->children[i-1]->key));
+
+ /* the strings should never match assuming that we
+ have called sorted_tree_find_child() first */
+
+ if ( StrCaseCmp( infant->key, node->children[i-1]->key ) > 0 ) {
+ DEBUG(11,("sorted_tree_birth_child: storing infant in i == [%d]\n",
+ i));
+ node->children[i] = infant;
+ break;
+ }
+
+ /* bump everything towards the end on slot */
+
+ node->children[i] = node->children[i-1];
+ }
+
+ DEBUG(11,("sorted_tree_birth_child: Exiting loop (i == [%d])\n", i ));
+
+ /* if we haven't found the correct slot yet, the child
+ will be first in the list */
+
+ if ( i == 0 )
+ node->children[0] = infant;
+ }
+
+ return infant;
+}
+
+/**************************************************************************
+ Find the next child given a key string
+ *************************************************************************/
+
+static TREE_NODE* sorted_tree_find_child( TREE_NODE *node, char* key )
+{
+ TREE_NODE *next = NULL;
+ int i, result;
+
+ if ( !node ) {
+ DEBUG(0,("sorted_tree_find_child: NULL node passed into function!\n"));
+ return NULL;
+ }
+
+ if ( !key ) {
+ DEBUG(0,("sorted_tree_find_child: NULL key string passed into function!\n"));
+ return NULL;
+ }
+
+ for ( i=0; i<node->num_children; i++ )
+ {
+ DEBUG(11,("sorted_tree_find_child: child key => [%s]\n",
+ node->children[i]->key));
+
+ result = StrCaseCmp( node->children[i]->key, key );
+
+ if ( result == 0 )
+ next = node->children[i];
+
+ /* if result > 0 then we've gone to far because
+ the list of children is sorted by key name
+ If result == 0, then we have a match */
+
+ if ( result > 0 )
+ break;
+ }
+
+ DEBUG(11,("sorted_tree_find_child: %s [%s]\n",
+ next ? "Found" : "Did not find", key ));
+
+ return next;
+}
+
+/**************************************************************************
+ Add a new node into the tree given a key path and a blob of data
+ *************************************************************************/
+
+BOOL sorted_tree_add( SORTED_TREE *tree, const char *path, void *data_p )
+{
+ char *str, *base, *path2;
+ TREE_NODE *current, *next;
+ BOOL ret = True;
+
+ DEBUG(8,("sorted_tree_add: Enter\n"));
+
+ if ( !path || *path != '/' ) {
+ DEBUG(0,("sorted_tree_add: Attempt to add a node with a bad path [%s]\n",
+ path ? path : "NULL" ));
+ return False;
+ }
+
+ if ( !tree ) {
+ DEBUG(0,("sorted_tree_add: Attempt to add a node to an uninitialized tree!\n"));
+ return False;
+ }
+
+ /* move past the first '/' */
+
+ path++;
+ path2 = strdup( path );
+ if ( !path2 ) {
+ DEBUG(0,("sorted_tree_add: strdup() failed on string [%s]!?!?!\n", path));
+ return False;
+ }
+
+
+ /*
+ * this works sort of like a 'mkdir -p' call, possibly
+ * creating an entire path to the new node at once
+ * The path should be of the form /<key1>/<key2>/...
+ */
+
+ base = path2;
+ str = path2;
+ current = tree->root;
+
+ do {
+ /* break off the remaining part of the path */
+
+ str = strchr( str, '/' );
+ if ( str )
+ *str = '\0';
+
+ /* iterate to the next child--birth it if necessary */
+
+ next = sorted_tree_find_child( current, base );
+ if ( !next ) {
+ next = sorted_tree_birth_child( current, base );
+ if ( !next ) {
+ DEBUG(0,("sorted_tree_add: Failed to create new child!\n"));
+ ret = False;
+ goto done;
+ }
+ }
+ current = next;
+
+ /* setup the next part of the path */
+
+ base = str;
+ if ( base ) {
+ *base = '/';
+ base++;
+ str = base;
+ }
+
+ } while ( base != NULL );
+
+ current->data_p = data_p;
+
+ DEBUG(10,("sorted_tree_add: Successfully added node [%s] to tree\n",
+ path ));
+
+ DEBUG(8,("sorted_tree_add: Exit\n"));
+
+done:
+ SAFE_FREE( path2 );
+ return ret;
+}
+
+
+/**************************************************************************
+ Recursive routine to print out all children of a TREE_NODE
+ *************************************************************************/
+
+static void sorted_tree_print_children( TREE_NODE *node, int debug, const char *path )
+{
+ int i;
+ int num_children;
+ pstring path2;
+
+ if ( !node )
+ return;
+
+
+ if ( node->key )
+ DEBUG(debug,("%s: [%s] (%s)\n", path ? path : "NULL", node->key,
+ node->data_p ? "data" : "NULL" ));
+
+ *path2 = '\0';
+ if ( path )
+ pstrcpy( path2, path );
+ pstrcat( path2, node->key ? node->key : "NULL" );
+ pstrcat( path2, "/" );
+
+ num_children = node->num_children;
+ for ( i=0; i<num_children; i++ )
+ sorted_tree_print_children( node->children[i], debug, path2 );
+
+
+}
+
+/**************************************************************************
+ Dump the kys for a tree to the log file
+ *************************************************************************/
+
+void sorted_tree_print_keys( SORTED_TREE *tree, int debug )
+{
+ int i;
+ int num_children = tree->root->num_children;
+
+ if ( tree->root->key )
+ DEBUG(debug,("ROOT/: [%s] (%s)\n", tree->root->key,
+ tree->root->data_p ? "data" : "NULL" ));
+
+ for ( i=0; i<num_children; i++ ) {
+ sorted_tree_print_children( tree->root->children[i], debug,
+ tree->root->key ? tree->root->key : "ROOT/" );
+ }
+
+}
+
+/**************************************************************************
+ return the data_p for for the node in tree matching the key string
+ The key string is the full path. We must break it apart and walk
+ the tree
+ *************************************************************************/
+
+void* sorted_tree_find( SORTED_TREE *tree, char *key )
+{
+ char *keystr, *base, *str, *p;
+ TREE_NODE *current;
+ void *result = NULL;
+
+ DEBUG(10,("sorted_tree_find: Enter [%s]\n", key ? key : "NULL" ));
+
+ /* sanity checks first */
+
+ if ( !key ) {
+ DEBUG(0,("sorted_tree_find: Attempt to search tree using NULL search string!\n"));
+ return NULL;
+ }
+
+ if ( !tree ) {
+ DEBUG(0,("sorted_tree_find: Attempt to search an uninitialized tree using string [%s]!\n",
+ key ? key : "NULL" ));
+ return NULL;
+ }
+
+ if ( !tree->root )
+ return NULL;
+
+ /* make a copy to play with */
+
+ if ( *key == '/' )
+ keystr = strdup( key+1 );
+ else
+ keystr = strdup( key );
+
+ if ( !keystr ) {
+ DEBUG(0,("sorted_tree_find: strdup() failed on string [%s]!?!?!\n", key));
+ return NULL;
+ }
+
+ /* start breaking the path apart */
+
+ p = keystr;
+ current = tree->root;
+
+ if ( tree->root->data_p )
+ result = tree->root->data_p;
+
+ do
+ {
+ /* break off the remaining part of the path */
+
+ trim_tree_keypath( p, &base, &str );
+
+ DEBUG(11,("sorted_tree_find: [loop] base => [%s], new_path => [%s]\n",
+ base, str));
+
+ /* iterate to the next child */
+
+ current = sorted_tree_find_child( current, base );
+
+ /*
+ * the idea is that the data_p for a parent should
+ * be inherited by all children, but allow it to be
+ * overridden farther down
+ */
+
+ if ( current && current->data_p )
+ result = current->data_p;
+
+ /* reset the path pointer 'p' to the remaining part of the key string */
+
+ p = str;
+
+ } while ( str && current );
+
+ /* result should be the data_p from the lowest match node in the tree */
+ if ( result )
+ DEBUG(11,("sorted_tree_find: Found data_p!\n"));
+
+ SAFE_FREE( keystr );
+
+ DEBUG(10,("sorted_tree_find: Exit\n"));
+
+ return result;
+}
+
+
diff --git a/source/lib/afs.c b/source/lib/afs.c
new file mode 100644
index 00000000000..ce972ec27b7
--- /dev/null
+++ b/source/lib/afs.c
@@ -0,0 +1,486 @@
+/*
+ * Unix SMB/CIFS implementation.
+ * Generate AFS tickets
+ * Copyright (C) Volker Lendecke 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 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"
+
+#ifdef WITH_FAKE_KASERVER
+
+#include <afs/stds.h>
+#include <afs/afs.h>
+#include <afs/auth.h>
+#include <afs/venus.h>
+#include <asm/unistd.h>
+#include <openssl/des.h>
+
+_syscall5(int, afs_syscall, int, subcall,
+ char *, path,
+ int, cmd,
+ char *, cmarg,
+ int, follow);
+
+struct ClearToken {
+ uint32 AuthHandle;
+ char HandShakeKey[8];
+ uint32 ViceId;
+ uint32 BeginTimestamp;
+ uint32 EndTimestamp;
+};
+
+static char *afs_encode_token(const char *cell, const DATA_BLOB ticket,
+ const struct ClearToken *ct)
+{
+ char *base64_ticket;
+ char *result;
+
+ DATA_BLOB key = data_blob(ct->HandShakeKey, 8);
+ char *base64_key;
+
+ base64_ticket = base64_encode_data_blob(ticket);
+ if (base64_ticket == NULL)
+ return NULL;
+
+ base64_key = base64_encode_data_blob(key);
+ if (base64_key == NULL) {
+ free(base64_ticket);
+ return NULL;
+ }
+
+ asprintf(&result, "%s\n%u\n%s\n%u\n%u\n%u\n%s\n", cell,
+ ct->AuthHandle, base64_key, ct->ViceId, ct->BeginTimestamp,
+ ct->EndTimestamp, base64_ticket);
+
+ DEBUG(10, ("Got ticket string:\n%s\n", result));
+
+ free(base64_ticket);
+ free(base64_key);
+
+ return result;
+}
+
+static BOOL afs_decode_token(const char *string, char **cell,
+ DATA_BLOB *ticket, struct ClearToken *ct)
+{
+ DATA_BLOB blob;
+ struct ClearToken result_ct;
+
+ char *s = strdup(string);
+
+ char *t;
+
+ if ((t = strtok(s, "\n")) == NULL) {
+ DEBUG(10, ("strtok failed\n"));
+ return False;
+ }
+
+ *cell = strdup(t);
+
+ if ((t = strtok(NULL, "\n")) == NULL) {
+ DEBUG(10, ("strtok failed\n"));
+ return False;
+ }
+
+ if (sscanf(t, "%u", &result_ct.AuthHandle) != 1) {
+ DEBUG(10, ("sscanf AuthHandle failed\n"));
+ return False;
+ }
+
+ if ((t = strtok(NULL, "\n")) == NULL) {
+ DEBUG(10, ("strtok failed\n"));
+ return False;
+ }
+
+ blob = base64_decode_data_blob(t);
+
+ if ( (blob.data == NULL) ||
+ (blob.length != sizeof(result_ct.HandShakeKey) )) {
+ DEBUG(10, ("invalid key: %x/%d\n", (uint32)blob.data,
+ blob.length));
+ return False;
+ }
+
+ memcpy(result_ct.HandShakeKey, blob.data, blob.length);
+
+ data_blob_free(&blob);
+
+ if ((t = strtok(NULL, "\n")) == NULL) {
+ DEBUG(10, ("strtok failed\n"));
+ return False;
+ }
+
+ if (sscanf(t, "%u", &result_ct.ViceId) != 1) {
+ DEBUG(10, ("sscanf ViceId failed\n"));
+ return False;
+ }
+
+ if ((t = strtok(NULL, "\n")) == NULL) {
+ DEBUG(10, ("strtok failed\n"));
+ return False;
+ }
+
+ if (sscanf(t, "%u", &result_ct.BeginTimestamp) != 1) {
+ DEBUG(10, ("sscanf BeginTimestamp failed\n"));
+ return False;
+ }
+
+ if ((t = strtok(NULL, "\n")) == NULL) {
+ DEBUG(10, ("strtok failed\n"));
+ return False;
+ }
+
+ if (sscanf(t, "%u", &result_ct.EndTimestamp) != 1) {
+ DEBUG(10, ("sscanf EndTimestamp failed\n"));
+ return False;
+ }
+
+ if ((t = strtok(NULL, "\n")) == NULL) {
+ DEBUG(10, ("strtok failed\n"));
+ return False;
+ }
+
+ blob = base64_decode_data_blob(t);
+
+ if (blob.data == NULL) {
+ DEBUG(10, ("Could not get ticket\n"));
+ return False;
+ }
+
+ *ticket = blob;
+ *ct = result_ct;
+
+ return True;
+}
+
+/*
+ Put an AFS token into the Kernel so that it can authenticate against
+ the AFS server. This assumes correct local uid settings.
+
+ This is currently highly Linux and OpenAFS-specific. The correct API
+ call for this would be ktc_SetToken. But to do that we would have to
+ import a REALLY big bunch of libraries which I would currently like
+ to avoid.
+*/
+
+static BOOL afs_settoken(const char *cell,
+ const struct ClearToken *ctok,
+ DATA_BLOB ticket)
+{
+ int ret;
+ struct {
+ char *in, *out;
+ uint16 in_size, out_size;
+ } iob;
+
+ char buf[1024];
+ char *p = buf;
+ int tmp;
+
+ memcpy(p, &ticket.length, sizeof(uint32));
+ p += sizeof(uint32);
+ memcpy(p, ticket.data, ticket.length);
+ p += ticket.length;
+
+ tmp = sizeof(struct ClearToken);
+ memcpy(p, &tmp, sizeof(uint32));
+ p += sizeof(uint32);
+ memcpy(p, ctok, tmp);
+ p += tmp;
+
+ tmp = 0;
+
+ memcpy(p, &tmp, sizeof(uint32));
+ p += sizeof(uint32);
+
+ tmp = strlen(cell);
+ if (tmp >= MAXKTCREALMLEN) {
+ DEBUG(1, ("Realm too long\n"));
+ return False;
+ }
+
+ strncpy(p, cell, tmp);
+ p += tmp;
+ *p = 0;
+ p +=1;
+
+ iob.in = buf;
+ iob.in_size = PTR_DIFF(p,buf);
+ iob.out = buf;
+ iob.out_size = sizeof(buf);
+
+#if 0
+ file_save("/tmp/ioctlbuf", iob.in, iob.in_size);
+#endif
+
+ ret = afs_syscall(AFSCALL_PIOCTL, 0, VIOCSETTOK, (char *)&iob, 0);
+
+ DEBUG(10, ("afs VIOCSETTOK returned %d\n", ret));
+ return (ret == 0);
+}
+
+BOOL afs_settoken_str(const char *token_string)
+{
+ DATA_BLOB ticket;
+ struct ClearToken ct;
+ BOOL result;
+ char *cell;
+
+ if (!afs_decode_token(token_string, &cell, &ticket, &ct))
+ return False;
+
+ if (geteuid() != 0)
+ ct.ViceId = getuid();
+
+ result = afs_settoken(cell, &ct, ticket);
+
+ SAFE_FREE(cell);
+ data_blob_free(&ticket);
+
+ return result;
+ }
+
+/* Create a ClearToken and an encrypted ticket. ClearToken has not yet the
+ * ViceId set, this should be set by the caller. */
+
+static BOOL afs_createtoken(const char *username, const char *cell,
+ DATA_BLOB *ticket, struct ClearToken *ct)
+{
+ fstring clear_ticket;
+ char *p = clear_ticket;
+ uint32 len;
+ uint32 now;
+
+ struct afs_key key;
+ des_key_schedule key_schedule;
+
+ if (!secrets_init())
+ return False;
+
+ if (!secrets_fetch_afs_key(cell, &key)) {
+ DEBUG(1, ("Could not fetch AFS service key\n"));
+ return False;
+ }
+
+ ct->AuthHandle = key.kvno;
+
+ /* Build the ticket. This is going to be encrypted, so in our
+ way we fill in ct while we still have the unencrypted
+ form. */
+
+ p = clear_ticket;
+
+ /* The byte-order */
+ *p = 1;
+ p += 1;
+
+ /* "Alice", the client username */
+ strncpy(p, username, sizeof(clear_ticket)-PTR_DIFF(p,clear_ticket)-1);
+ p += strlen(p)+1;
+ strncpy(p, "", sizeof(clear_ticket)-PTR_DIFF(p,clear_ticket)-1);
+ p += strlen(p)+1;
+ strncpy(p, cell, sizeof(clear_ticket)-PTR_DIFF(p,clear_ticket)-1);
+ p += strlen(p)+1;
+
+ /* Alice's network layer address. At least Openafs-1.2.10
+ ignores this, so we fill in a dummy value here. */
+ SIVAL(p, 0, 0);
+ p += 4;
+
+ /* We need to create a session key */
+ generate_random_buffer(p, 8, False);
+
+ /* Our client code needs the the key in the clear, it does not
+ know the server-key ... */
+ memcpy(ct->HandShakeKey, p, 8);
+
+ p += 8;
+
+ /* Ticket lifetime. We fake everything here, so go as long as
+ possible. This is in 5-minute intervals, so 255 is 21 hours
+ and 15 minutes.*/
+ *p = 255;
+ p += 1;
+
+ /* Ticket creation time */
+ now = time(NULL);
+ SIVAL(p, 0, now);
+ ct->BeginTimestamp = now;
+
+ ct->EndTimestamp = now + (255*60*5);
+ if (((ct->EndTimestamp - ct->BeginTimestamp) & 1) == 1) {
+ ct->BeginTimestamp += 1; /* Lifetime must be even */
+ }
+ p += 4;
+
+ /* And here comes Bob's name and instance, in this case the
+ AFS server. */
+ strncpy(p, "afs", sizeof(clear_ticket)-PTR_DIFF(p,clear_ticket)-1);
+ p += strlen(p)+1;
+ strncpy(p, "", sizeof(clear_ticket)-PTR_DIFF(p,clear_ticket)-1);
+ p += strlen(p)+1;
+
+ /* And zero-pad to a multiple of 8 bytes */
+ len = PTR_DIFF(p, clear_ticket);
+ if (len & 7) {
+ uint32 extra_space = 8-(len & 7);
+ memset(p, 0, extra_space);
+ p+=extra_space;
+ }
+ len = PTR_DIFF(p, clear_ticket);
+
+ des_key_sched((const_des_cblock *)key.key, key_schedule);
+ des_pcbc_encrypt(clear_ticket, clear_ticket,
+ len, key_schedule, (C_Block *)key.key, 1);
+
+ ZERO_STRUCT(key);
+
+ *ticket = data_blob(clear_ticket, len);
+
+ return True;
+}
+
+char *afs_createtoken_str(const char *username, const char *cell)
+{
+ DATA_BLOB ticket;
+ struct ClearToken ct;
+ char *result;
+
+ if (!afs_createtoken(username, cell, &ticket, &ct))
+ return NULL;
+
+ result = afs_encode_token(cell, ticket, &ct);
+
+ data_blob_free(&ticket);
+
+ return result;
+}
+
+/*
+ This routine takes a radical approach completely bypassing the
+ Kerberos idea of security and using AFS simply as an intelligent
+ file backend. Samba has persuaded itself somehow that the user is
+ actually correctly identified and then we create a ticket that the
+ AFS server hopefully accepts using its KeyFile that the admin has
+ kindly stored to our secrets.tdb.
+
+ Thanks to the book "Network Security -- PRIVATE Communication in a
+ PUBLIC World" by Charlie Kaufman, Radia Perlman and Mike Speciner
+ Kerberos 4 tickets are not really hard to construct.
+
+ For the comments "Alice" is the User to be auth'ed, and "Bob" is the
+ AFS server. */
+
+BOOL afs_login(connection_struct *conn)
+{
+ DATA_BLOB ticket;
+ pstring afs_username;
+ char *cell;
+ BOOL result;
+
+ struct ClearToken ct;
+
+ pstrcpy(afs_username, lp_afs_username_map());
+ standard_sub_conn(conn, afs_username, sizeof(afs_username));
+
+ /* The pts command always generates completely lower-case user
+ * names. */
+ strlower_m(afs_username);
+
+ cell = strchr(afs_username, '@');
+
+ if (cell == NULL) {
+ DEBUG(1, ("AFS username doesn't contain a @, "
+ "could not find cell\n"));
+ return False;
+ }
+
+ *cell = '\0';
+ cell += 1;
+
+ DEBUG(10, ("Trying to log into AFS for user %s@%s\n",
+ afs_username, cell));
+
+ if (!afs_createtoken(afs_username, cell, &ticket, &ct))
+ return False;
+
+ /* For which Unix-UID do we want to set the token? */
+ ct.ViceId = getuid();
+
+ {
+ char *str, *new_cell;
+ DATA_BLOB test_ticket;
+ struct ClearToken test_ct;
+
+ hex_encode(ct.HandShakeKey, sizeof(ct.HandShakeKey), &str);
+ DEBUG(10, ("Key: %s\n", str));
+ free(str);
+
+ str = afs_encode_token(cell, ticket, &ct);
+
+ if (!afs_decode_token(str, &new_cell, &test_ticket,
+ &test_ct)) {
+ DEBUG(0, ("Could not decode token"));
+ goto decode_failed;
+ }
+
+ if (strcmp(cell, new_cell) != 0) {
+ DEBUG(0, ("cell changed\n"));
+ }
+
+ if ((ticket.length != test_ticket.length) ||
+ (memcmp(ticket.data, test_ticket.data,
+ ticket.length) != 0)) {
+ DEBUG(0, ("Ticket changed\n"));
+ }
+
+ if (memcmp(&ct, &test_ct, sizeof(ct)) != 0) {
+ DEBUG(0, ("ClearToken changed\n"));
+ }
+
+ data_blob_free(&test_ticket);
+
+ decode_failed:
+ SAFE_FREE(str);
+ SAFE_FREE(new_cell);
+ }
+
+ result = afs_settoken(cell, &ct, ticket);
+
+ data_blob_free(&ticket);
+
+ return result;
+}
+
+#else
+
+BOOL afs_login(connection_struct *conn)
+{
+ return True;
+}
+
+BOOL afs_settoken_str(const char *token_string)
+{
+ return False;
+}
+
+char *afs_createtoken_str(const char *username, const char *cell)
+{
+ return False;
+}
+
+#endif /* WITH_FAKE_KASERVER */
diff --git a/source/lib/bitmap.c b/source/lib/bitmap.c
new file mode 100644
index 00000000000..3fa20cdd112
--- /dev/null
+++ b/source/lib/bitmap.c
@@ -0,0 +1,177 @@
+/*
+ Unix SMB/CIFS implementation.
+ simple bitmap functions
+ 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 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"
+
+/* these functions provide a simple way to allocate integers from a
+ pool without repetition */
+
+/****************************************************************************
+allocate a bitmap of the specified size
+****************************************************************************/
+struct bitmap *bitmap_allocate(int n)
+{
+ struct bitmap *bm;
+
+ bm = (struct bitmap *)malloc(sizeof(*bm));
+
+ if (!bm) return NULL;
+
+ bm->n = n;
+ bm->b = (uint32 *)malloc(sizeof(bm->b[0])*(n+31)/32);
+ if (!bm->b) {
+ SAFE_FREE(bm);
+ return NULL;
+ }
+
+ memset(bm->b, 0, sizeof(bm->b[0])*(n+31)/32);
+
+ return bm;
+}
+
+/****************************************************************************
+free a bitmap.
+****************************************************************************/
+
+void bitmap_free(struct bitmap *bm)
+{
+ if (!bm)
+ return;
+
+ SAFE_FREE(bm->b);
+ SAFE_FREE(bm);
+}
+
+/****************************************************************************
+talloc a bitmap
+****************************************************************************/
+struct bitmap *bitmap_talloc(TALLOC_CTX *mem_ctx, int n)
+{
+ struct bitmap *bm;
+
+ if (!mem_ctx) return NULL;
+
+ bm = (struct bitmap *)talloc(mem_ctx, sizeof(*bm));
+
+ if (!bm) return NULL;
+
+ bm->n = n;
+ bm->b = (uint32 *)talloc(mem_ctx, sizeof(bm->b[0])*(n+31)/32);
+ if (!bm->b) {
+ return NULL;
+ }
+
+ memset(bm->b, 0, sizeof(bm->b[0])*(n+31)/32);
+
+ return bm;
+}
+
+/****************************************************************************
+copy as much of the source bitmap as will fit in the destination bitmap.
+****************************************************************************/
+
+int bitmap_copy(struct bitmap * const dst, const struct bitmap * const src)
+{
+ int count = MIN(dst->n, src->n);
+
+ SMB_ASSERT(dst->b != src->b);
+ memcpy(dst->b, src->b, sizeof(dst->b[0])*(count+31)/32);
+
+ return count;
+}
+
+/****************************************************************************
+set a bit in a bitmap
+****************************************************************************/
+BOOL bitmap_set(struct bitmap *bm, unsigned i)
+{
+ if (i >= bm->n) {
+ DEBUG(0,("Setting invalid bitmap entry %d (of %d)\n",
+ i, bm->n));
+ return False;
+ }
+ bm->b[i/32] |= (1<<(i%32));
+ return True;
+}
+
+/****************************************************************************
+clear a bit in a bitmap
+****************************************************************************/
+BOOL bitmap_clear(struct bitmap *bm, unsigned i)
+{
+ if (i >= bm->n) {
+ DEBUG(0,("clearing invalid bitmap entry %d (of %d)\n",
+ i, bm->n));
+ return False;
+ }
+ bm->b[i/32] &= ~(1<<(i%32));
+ return True;
+}
+
+/****************************************************************************
+query a bit in a bitmap
+****************************************************************************/
+BOOL bitmap_query(struct bitmap *bm, unsigned i)
+{
+ if (i >= bm->n) return False;
+ if (bm->b[i/32] & (1<<(i%32))) {
+ return True;
+ }
+ return False;
+}
+
+/****************************************************************************
+find a zero bit in a bitmap starting at the specified offset, with
+wraparound
+****************************************************************************/
+int bitmap_find(struct bitmap *bm, unsigned ofs)
+{
+ unsigned int i, j;
+
+ if (ofs > bm->n) ofs = 0;
+
+ i = ofs;
+ while (i < bm->n) {
+ if (~(bm->b[i/32])) {
+ j = i;
+ do {
+ if (!bitmap_query(bm, j)) return j;
+ j++;
+ } while (j & 31 && j < bm->n);
+ }
+ i += 32;
+ i &= ~31;
+ }
+
+ i = 0;
+ while (i < ofs) {
+ if (~(bm->b[i/32])) {
+ j = i;
+ do {
+ if (!bitmap_query(bm, j)) return j;
+ j++;
+ } while (j & 31 && j < bm->n);
+ }
+ i += 32;
+ i &= ~31;
+ }
+
+ return -1;
+}
diff --git a/source/lib/charcnv.c b/source/lib/charcnv.c
new file mode 100644
index 00000000000..b9791931a35
--- /dev/null
+++ b/source/lib/charcnv.c
@@ -0,0 +1,1339 @@
+/*
+ Unix SMB/CIFS implementation.
+ Character set conversion Extensions
+ Copyright (C) Igor Vergeichik <iverg@mail.ru> 2001
+ Copyright (C) Andrew Tridgell 2001
+ Copyright (C) Simo Sorce 2001
+ Copyright (C) Martin Pool 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 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"
+
+/**
+ * @file
+ *
+ * @brief Character-set conversion routines built on our iconv.
+ *
+ * @note Samba's internal character set (at least in the 3.0 series)
+ * is always the same as the one for the Unix filesystem. It is
+ * <b>not</b> necessarily UTF-8 and may be different on machines that
+ * need i18n filenames to be compatible with Unix software. It does
+ * have to be a superset of ASCII. All multibyte sequences must start
+ * with a byte with the high bit set.
+ *
+ * @sa lib/iconv.c
+ */
+
+
+static smb_iconv_t conv_handles[NUM_CHARSETS][NUM_CHARSETS];
+static BOOL conv_silent; /* Should we do a debug if the conversion fails ? */
+
+/**
+ * Return the name of a charset to give to iconv().
+ **/
+static const char *charset_name(charset_t ch)
+{
+ const char *ret = NULL;
+
+ if (ch == CH_UCS2) ret = "UCS-2LE";
+ else if (ch == CH_UNIX) ret = lp_unix_charset();
+ else if (ch == CH_DOS) ret = lp_dos_charset();
+ else if (ch == CH_DISPLAY) ret = lp_display_charset();
+ else if (ch == CH_UTF8) ret = "UTF8";
+
+#if defined(HAVE_NL_LANGINFO) && defined(CODESET)
+ if (ret && !strcmp(ret, "LOCALE")) {
+ const char *ln = NULL;
+
+#ifdef HAVE_SETLOCALE
+ setlocale(LC_ALL, "");
+#endif
+ ln = nl_langinfo(CODESET);
+ if (ln) {
+ /* Check whether the charset name is supported
+ by iconv */
+ smb_iconv_t handle = smb_iconv_open(ln,"UCS-2LE");
+ if (handle == (smb_iconv_t) -1) {
+ DEBUG(5,("Locale charset '%s' unsupported, using ASCII instead\n", ln));
+ ln = NULL;
+ } else {
+ DEBUG(5,("Substituting charset '%s' for LOCALE\n", ln));
+ smb_iconv_close(handle);
+ }
+ }
+ ret = ln;
+ }
+#endif
+
+ if (!ret || !*ret) ret = "ASCII";
+ return ret;
+}
+
+void lazy_initialize_conv(void)
+{
+ static int initialized = False;
+
+ if (!initialized) {
+ initialized = True;
+ load_case_tables();
+ init_iconv();
+ }
+}
+
+/**
+ * Initialize iconv conversion descriptors.
+ *
+ * This is called the first time it is needed, and also called again
+ * every time the configuration is reloaded, because the charset or
+ * codepage might have changed.
+ **/
+void init_iconv(void)
+{
+ int c1, c2;
+ BOOL did_reload = False;
+
+ /* so that charset_name() works we need to get the UNIX<->UCS2 going
+ first */
+ if (!conv_handles[CH_UNIX][CH_UCS2])
+ conv_handles[CH_UNIX][CH_UCS2] = smb_iconv_open("UCS-2LE", "ASCII");
+
+ if (!conv_handles[CH_UCS2][CH_UNIX])
+ conv_handles[CH_UCS2][CH_UNIX] = smb_iconv_open("ASCII", "UCS-2LE");
+
+ for (c1=0;c1<NUM_CHARSETS;c1++) {
+ for (c2=0;c2<NUM_CHARSETS;c2++) {
+ const char *n1 = charset_name((charset_t)c1);
+ const char *n2 = charset_name((charset_t)c2);
+ if (conv_handles[c1][c2] &&
+ strcmp(n1, conv_handles[c1][c2]->from_name) == 0 &&
+ strcmp(n2, conv_handles[c1][c2]->to_name) == 0)
+ continue;
+
+ did_reload = True;
+
+ if (conv_handles[c1][c2])
+ smb_iconv_close(conv_handles[c1][c2]);
+
+ conv_handles[c1][c2] = smb_iconv_open(n2,n1);
+ if (conv_handles[c1][c2] == (smb_iconv_t)-1) {
+ DEBUG(0,("init_iconv: Conversion from %s to %s not supported\n",
+ charset_name((charset_t)c1), charset_name((charset_t)c2)));
+ if (c1 != CH_UCS2) {
+ n1 = "ASCII";
+ }
+ if (c2 != CH_UCS2) {
+ n2 = "ASCII";
+ }
+ DEBUG(0,("init_iconv: Attempting to replace with conversion from %s to %s\n",
+ n1, n2 ));
+ conv_handles[c1][c2] = smb_iconv_open(n2,n1);
+ if (!conv_handles[c1][c2]) {
+ DEBUG(0,("init_iconv: Conversion from %s to %s failed", n1, n2));
+ smb_panic("init_iconv: conv_handle initialization failed.");
+ }
+ }
+ }
+ }
+
+ if (did_reload) {
+ /* XXX: Does this really get called every time the dos
+ * codepage changes? */
+ /* XXX: Is the did_reload test too strict? */
+ conv_silent = True;
+ init_doschar_table();
+ init_valid_table();
+ conv_silent = False;
+ }
+}
+
+/**
+ * Convert string from one encoding to another, making error checking etc
+ * Slow path version - uses (slow) iconv.
+ *
+ * @param src pointer to source string (multibyte or singlebyte)
+ * @param srclen length of the source string in bytes
+ * @param dest pointer to destination string (multibyte or singlebyte)
+ * @param destlen maximal length allowed for string
+ * @param allow_bad_conv determines if a "best effort" conversion is acceptable (never returns errors)
+ * @returns the number of bytes occupied in the destination
+ *
+ * Ensure the srclen contains the terminating zero.
+ *
+ **/
+
+static size_t convert_string_internal(charset_t from, charset_t to,
+ void const *src, size_t srclen,
+ void *dest, size_t destlen, BOOL allow_bad_conv)
+{
+ size_t i_len, o_len;
+ size_t retval;
+ const char* inbuf = (const char*)src;
+ char* outbuf = (char*)dest;
+ smb_iconv_t descriptor;
+
+ lazy_initialize_conv();
+
+ descriptor = conv_handles[from][to];
+
+ if (srclen == (size_t)-1) {
+ if (from == CH_UCS2) {
+ srclen = (strlen_w((const smb_ucs2_t *)src)+1) * 2;
+ } else {
+ srclen = strlen((const char *)src)+1;
+ }
+ }
+
+
+ if (descriptor == (smb_iconv_t)-1 || descriptor == (smb_iconv_t)0) {
+ if (!conv_silent)
+ DEBUG(0,("convert_string_internal: Conversion not supported.\n"));
+ return (size_t)-1;
+ }
+
+ i_len=srclen;
+ o_len=destlen;
+
+ again:
+
+ retval = smb_iconv(descriptor, (char **)&inbuf, &i_len, &outbuf, &o_len);
+ if(retval==(size_t)-1) {
+ const char *reason="unknown error";
+ switch(errno) {
+ case EINVAL:
+ reason="Incomplete multibyte sequence";
+ if (!conv_silent)
+ DEBUG(3,("convert_string_internal: Conversion error: %s(%s)\n",reason,inbuf));
+ if (allow_bad_conv)
+ goto use_as_is;
+ break;
+ case E2BIG:
+ reason="No more room";
+ if (!conv_silent)
+ DEBUG(3, ("convert_string_internal: Required %lu, available %lu\n",
+ (unsigned long)srclen, (unsigned long)destlen));
+ /* we are not sure we need srclen bytes,
+ may be more, may be less.
+ We only know we need more than destlen
+ bytes ---simo */
+ break;
+ case EILSEQ:
+ reason="Illegal multibyte sequence";
+ if (!conv_silent)
+ DEBUG(3,("convert_string_internal: Conversion error: %s(%s)\n",reason,inbuf));
+ if (allow_bad_conv)
+ goto use_as_is;
+ break;
+ default:
+ if (!conv_silent)
+ DEBUG(0,("convert_string_internal: Conversion error: %s(%s)\n",reason,inbuf));
+ break;
+ }
+ /* smb_panic(reason); */
+ }
+ return destlen-o_len;
+
+ use_as_is:
+
+ /*
+ * Conversion not supported. This is actually an error, but there are so
+ * many misconfigured iconv systems and smb.conf's out there we can't just
+ * fail. Do a very bad conversion instead.... JRA.
+ */
+
+ {
+ if (o_len == 0 || i_len == 0)
+ return destlen - o_len;
+
+ if (from == CH_UCS2 && to != CH_UCS2) {
+ /* Can't convert from ucs2 to multibyte. Just truncate this char to ascii. */
+ if (i_len < 2)
+ return destlen - o_len;
+ if (i_len >= 2) {
+ *outbuf = inbuf[0];
+
+ outbuf++;
+ o_len--;
+
+ inbuf += 2;
+ i_len -= 2;
+ }
+
+ if (o_len == 0 || i_len == 0)
+ return destlen - o_len;
+
+ /* Keep trying with the next char... */
+ goto again;
+
+ } else if (from != CH_UCS2 && to == CH_UCS2) {
+ /* Can't convert to ucs2 - just widen by adding zero. */
+ if (o_len < 2)
+ return destlen - o_len;
+
+ outbuf[0] = inbuf[0];
+ outbuf[1] = '\0';
+
+ inbuf++;
+ i_len--;
+
+ outbuf += 2;
+ o_len -= 2;
+
+ if (o_len == 0 || i_len == 0)
+ return destlen - o_len;
+
+ /* Keep trying with the next char... */
+ goto again;
+
+ } else if (from != CH_UCS2 && to != CH_UCS2) {
+ /* Failed multibyte to multibyte. Just copy 1 char and
+ try again. */
+ outbuf[0] = inbuf[0];
+
+ inbuf++;
+ i_len--;
+
+ outbuf++;
+ o_len--;
+
+ if (o_len == 0 || i_len == 0)
+ return destlen - o_len;
+
+ /* Keep trying with the next char... */
+ goto again;
+
+ } else {
+ /* Keep compiler happy.... */
+ return destlen - o_len;
+ }
+ }
+}
+
+/**
+ * Convert string from one encoding to another, making error checking etc
+ * Fast path version - handles ASCII first.
+ *
+ * @param src pointer to source string (multibyte or singlebyte)
+ * @param srclen length of the source string in bytes, or -1 for nul terminated.
+ * @param dest pointer to destination string (multibyte or singlebyte)
+ * @param destlen maximal length allowed for string - *NEVER* -1.
+ * @param allow_bad_conv determines if a "best effort" conversion is acceptable (never returns errors)
+ * @returns the number of bytes occupied in the destination
+ *
+ * Ensure the srclen contains the terminating zero.
+ *
+ * This function has been hand-tuned to provide a fast path.
+ * Don't change unless you really know what you are doing. JRA.
+ **/
+
+size_t convert_string(charset_t from, charset_t to,
+ void const *src, size_t srclen,
+ void *dest, size_t destlen, BOOL allow_bad_conv)
+{
+ /*
+ * NB. We deliberately don't do a strlen here if srclen == -1.
+ * This is very expensive over millions of calls and is taken
+ * care of in the slow path in convert_string_internal. JRA.
+ */
+
+#ifdef DEVELOPER
+ SMB_ASSERT(destlen != (size_t)-1);
+#endif
+
+ if (srclen == 0)
+ return 0;
+
+ if (from != CH_UCS2 && to != CH_UCS2) {
+ const unsigned char *p = (const unsigned char *)src;
+ unsigned char *q = (unsigned char *)dest;
+ size_t slen = srclen;
+ size_t dlen = destlen;
+ unsigned char lastp;
+ size_t retval = 0;
+
+ /* If all characters are ascii, fast path here. */
+ while (slen && dlen) {
+ if ((lastp = *p) <= 0x7f) {
+ *q++ = *p++;
+ if (slen != (size_t)-1) {
+ slen--;
+ }
+ dlen--;
+ retval++;
+ if (!lastp)
+ break;
+ } else {
+#ifdef BROKEN_UNICODE_COMPOSE_CHARACTERS
+ goto general_case;
+#else
+ return retval + convert_string_internal(from, to, p, slen, q, dlen, allow_bad_conv);
+#endif
+ }
+ }
+ return retval;
+ } else if (from == CH_UCS2 && to != CH_UCS2) {
+ const unsigned char *p = (const unsigned char *)src;
+ unsigned char *q = (unsigned char *)dest;
+ size_t retval = 0;
+ size_t slen = srclen;
+ size_t dlen = destlen;
+ unsigned char lastp;
+
+ /* If all characters are ascii, fast path here. */
+ while (((slen == (size_t)-1) || (slen >= 2)) && dlen) {
+ if (((lastp = *p) <= 0x7f) && (p[1] == 0)) {
+ *q++ = *p;
+ if (slen != (size_t)-1) {
+ slen -= 2;
+ }
+ p += 2;
+ dlen--;
+ retval++;
+ if (!lastp)
+ break;
+ } else {
+#ifdef BROKEN_UNICODE_COMPOSE_CHARACTERS
+ goto general_case;
+#else
+ return retval + convert_string_internal(from, to, p, slen, q, dlen, allow_bad_conv);
+#endif
+ }
+ }
+ return retval;
+ } else if (from != CH_UCS2 && to == CH_UCS2) {
+ const unsigned char *p = (const unsigned char *)src;
+ unsigned char *q = (unsigned char *)dest;
+ size_t retval = 0;
+ size_t slen = srclen;
+ size_t dlen = destlen;
+ unsigned char lastp;
+
+ /* If all characters are ascii, fast path here. */
+ while (slen && (dlen >= 2)) {
+ if ((lastp = *p) <= 0x7F) {
+ *q++ = *p++;
+ *q++ = '\0';
+ if (slen != (size_t)-1) {
+ slen--;
+ }
+ dlen -= 2;
+ retval += 2;
+ if (!lastp)
+ break;
+ } else {
+#ifdef BROKEN_UNICODE_COMPOSE_CHARACTERS
+ goto general_case;
+#else
+ return retval + convert_string_internal(from, to, p, slen, q, dlen, allow_bad_conv);
+#endif
+ }
+ }
+ return retval;
+ }
+
+#ifdef BROKEN_UNICODE_COMPOSE_CHARACTERS
+ general_case:
+#endif
+ return convert_string_internal(from, to, src, srclen, dest, destlen, allow_bad_conv);
+}
+
+/**
+ * Convert between character sets, allocating a new buffer for the result.
+ *
+ * @param ctx TALLOC_CTX to use to allocate with. If NULL use malloc.
+ * @param srclen length of source buffer.
+ * @param dest always set at least to NULL
+ * @note -1 is not accepted for srclen.
+ *
+ * @returns Size in bytes of the converted string; or -1 in case of error.
+ *
+ * Ensure the srclen contains the terminating zero.
+ *
+ * I hate the goto's in this function. It's embarressing.....
+ * There has to be a cleaner way to do this. JRA.
+ **/
+
+size_t convert_string_allocate(TALLOC_CTX *ctx, charset_t from, charset_t to,
+ void const *src, size_t srclen, void **dest, BOOL allow_bad_conv)
+{
+ size_t i_len, o_len, destlen = MAX(srclen, 512);
+ size_t retval;
+ const char *inbuf = (const char *)src;
+ char *outbuf = NULL, *ob = NULL;
+ smb_iconv_t descriptor;
+
+ *dest = NULL;
+
+ if (src == NULL || srclen == (size_t)-1)
+ return (size_t)-1;
+ if (srclen == 0)
+ return 0;
+
+ lazy_initialize_conv();
+
+ descriptor = conv_handles[from][to];
+
+ if (descriptor == (smb_iconv_t)-1 || descriptor == (smb_iconv_t)0) {
+ if (!conv_silent)
+ DEBUG(0,("convert_string_allocate: Conversion not supported.\n"));
+ return (size_t)-1;
+ }
+
+ convert:
+
+ if ((destlen*2) < destlen) {
+ /* wrapped ! abort. */
+ if (!conv_silent)
+ DEBUG(0, ("convert_string_allocate: destlen wrapped !\n"));
+ if (!ctx)
+ SAFE_FREE(outbuf);
+ return (size_t)-1;
+ } else {
+ destlen = destlen * 2;
+ }
+
+ if (ctx)
+ ob = (char *)talloc_realloc(ctx, ob, destlen);
+ else
+ ob = (char *)Realloc(ob, destlen);
+
+ if (!ob) {
+ DEBUG(0, ("convert_string_allocate: realloc failed!\n"));
+ if (!ctx)
+ SAFE_FREE(outbuf);
+ return (size_t)-1;
+ } else {
+ outbuf = ob;
+ }
+ i_len = srclen;
+ o_len = destlen;
+
+ again:
+
+ retval = smb_iconv(descriptor,
+ (char **)&inbuf, &i_len,
+ &outbuf, &o_len);
+ if(retval == (size_t)-1) {
+ const char *reason="unknown error";
+ switch(errno) {
+ case EINVAL:
+ reason="Incomplete multibyte sequence";
+ if (!conv_silent)
+ DEBUG(3,("convert_string_allocate: Conversion error: %s(%s)\n",reason,inbuf));
+ if (allow_bad_conv)
+ goto use_as_is;
+ break;
+ case E2BIG:
+ goto convert;
+ case EILSEQ:
+ reason="Illegal multibyte sequence";
+ if (!conv_silent)
+ DEBUG(3,("convert_string_allocate: Conversion error: %s(%s)\n",reason,inbuf));
+ if (allow_bad_conv)
+ goto use_as_is;
+ break;
+ }
+ if (!conv_silent)
+ DEBUG(0,("Conversion error: %s(%s)\n",reason,inbuf));
+ /* smb_panic(reason); */
+ return (size_t)-1;
+ }
+
+ out:
+
+ destlen = destlen - o_len;
+ if (ctx)
+ *dest = (char *)talloc_realloc(ctx,ob,destlen);
+ else
+ *dest = (char *)Realloc(ob,destlen);
+ if (destlen && !*dest) {
+ DEBUG(0, ("convert_string_allocate: out of memory!\n"));
+ if (!ctx)
+ SAFE_FREE(ob);
+ return (size_t)-1;
+ }
+
+ return destlen;
+
+ use_as_is:
+
+ /*
+ * Conversion not supported. This is actually an error, but there are so
+ * many misconfigured iconv systems and smb.conf's out there we can't just
+ * fail. Do a very bad conversion instead.... JRA.
+ */
+
+ {
+ if (o_len == 0 || i_len == 0)
+ goto out;
+
+ if (from == CH_UCS2 && to != CH_UCS2) {
+ /* Can't convert from ucs2 to multibyte. Just truncate this char to ascii. */
+ if (i_len < 2)
+ goto out;
+
+ if (i_len >= 2) {
+ *outbuf = inbuf[0];
+
+ outbuf++;
+ o_len--;
+
+ inbuf += 2;
+ i_len -= 2;
+ }
+
+ if (o_len == 0 || i_len == 0)
+ goto out;
+
+ /* Keep trying with the next char... */
+ goto again;
+
+ } else if (from != CH_UCS2 && to == CH_UCS2) {
+ /* Can't convert to ucs2 - just widen by adding zero. */
+ if (o_len < 2)
+ goto out;
+
+ outbuf[0] = inbuf[0];
+ outbuf[1] = '\0';
+
+ inbuf++;
+ i_len--;
+
+ outbuf += 2;
+ o_len -= 2;
+
+ if (o_len == 0 || i_len == 0)
+ goto out;
+
+ /* Keep trying with the next char... */
+ goto again;
+
+ } else if (from != CH_UCS2 && to != CH_UCS2) {
+ /* Failed multibyte to multibyte. Just copy 1 char and
+ try again. */
+ outbuf[0] = inbuf[0];
+
+ inbuf++;
+ i_len--;
+
+ outbuf++;
+ o_len--;
+
+ if (o_len == 0 || i_len == 0)
+ goto out;
+
+ /* Keep trying with the next char... */
+ goto again;
+
+ } else {
+ /* Keep compiler happy.... */
+ goto out;
+ }
+ }
+}
+
+/**
+ * Convert between character sets, allocating a new buffer using talloc for the result.
+ *
+ * @param srclen length of source buffer.
+ * @param dest always set at least to NULL
+ * @note -1 is not accepted for srclen.
+ *
+ * @returns Size in bytes of the converted string; or -1 in case of error.
+ **/
+static size_t convert_string_talloc(TALLOC_CTX *ctx, charset_t from, charset_t to,
+ void const *src, size_t srclen, void **dest, BOOL allow_bad_conv)
+{
+ size_t dest_len;
+
+ *dest = NULL;
+ dest_len=convert_string_allocate(ctx, from, to, src, srclen, dest, allow_bad_conv);
+ if (dest_len == (size_t)-1)
+ return (size_t)-1;
+ if (*dest == NULL)
+ return (size_t)-1;
+ return dest_len;
+}
+
+size_t unix_strupper(const char *src, size_t srclen, char *dest, size_t destlen)
+{
+ size_t size;
+ smb_ucs2_t *buffer;
+
+ size = push_ucs2_allocate(&buffer, src);
+ if (size == (size_t)-1) {
+ smb_panic("failed to create UCS2 buffer");
+ }
+ if (!strupper_w(buffer) && (dest == src)) {
+ free(buffer);
+ return srclen;
+ }
+
+ size = convert_string(CH_UCS2, CH_UNIX, buffer, size, dest, destlen, True);
+ free(buffer);
+ return size;
+}
+
+/**
+ strdup() a unix string to upper case.
+ Max size is pstring.
+**/
+
+char *strdup_upper(const char *s)
+{
+ pstring out_buffer;
+ const unsigned char *p = (const unsigned char *)s;
+ unsigned char *q = (unsigned char *)out_buffer;
+
+ /* this is quite a common operation, so we want it to be
+ fast. We optimise for the ascii case, knowing that all our
+ supported multi-byte character sets are ascii-compatible
+ (ie. they match for the first 128 chars) */
+
+ while (1) {
+ if (*p & 0x80)
+ break;
+ *q++ = toupper(*p);
+ if (!*p)
+ break;
+ p++;
+ if (p - ( const unsigned char *)s >= sizeof(pstring))
+ break;
+ }
+
+ if (*p) {
+ /* MB case. */
+ size_t size;
+ wpstring buffer;
+ size = convert_string(CH_UNIX, CH_UCS2, s, -1, buffer, sizeof(buffer), True);
+ if (size == (size_t)-1) {
+ return NULL;
+ }
+
+ strupper_w(buffer);
+
+ size = convert_string(CH_UCS2, CH_UNIX, buffer, -1, out_buffer, sizeof(out_buffer), True);
+ if (size == (size_t)-1) {
+ return NULL;
+ }
+ }
+
+ return strdup(out_buffer);
+}
+
+size_t unix_strlower(const char *src, size_t srclen, char *dest, size_t destlen)
+{
+ size_t size;
+ smb_ucs2_t *buffer = NULL;
+
+ size = convert_string_allocate(NULL, CH_UNIX, CH_UCS2, src, srclen,
+ (void **) &buffer, True);
+ if (size == (size_t)-1 || !buffer) {
+ smb_panic("failed to create UCS2 buffer");
+ }
+ if (!strlower_w(buffer) && (dest == src)) {
+ SAFE_FREE(buffer);
+ return srclen;
+ }
+ size = convert_string(CH_UCS2, CH_UNIX, buffer, size, dest, destlen, True);
+ SAFE_FREE(buffer);
+ return size;
+}
+
+/**
+ strdup() a unix string to lower case.
+**/
+
+char *strdup_lower(const char *s)
+{
+ size_t size;
+ smb_ucs2_t *buffer = NULL;
+ char *out_buffer;
+
+ size = push_ucs2_allocate(&buffer, s);
+ if (size == -1 || !buffer) {
+ return NULL;
+ }
+
+ strlower_w(buffer);
+
+ size = pull_ucs2_allocate(&out_buffer, buffer);
+ SAFE_FREE(buffer);
+
+ if (size == (size_t)-1) {
+ return NULL;
+ }
+
+ return out_buffer;
+}
+
+static size_t ucs2_align(const void *base_ptr, const void *p, int flags)
+{
+ if (flags & (STR_NOALIGN|STR_ASCII))
+ return 0;
+ return PTR_DIFF(p, base_ptr) & 1;
+}
+
+
+/**
+ * Copy a string from a char* unix src to a dos codepage string destination.
+ *
+ * @return the number of bytes occupied by the string in the destination.
+ *
+ * @param flags can include
+ * <dl>
+ * <dt>STR_TERMINATE</dt> <dd>means include the null termination</dd>
+ * <dt>STR_UPPER</dt> <dd>means uppercase in the destination</dd>
+ * </dl>
+ *
+ * @param dest_len the maximum length in bytes allowed in the
+ * destination. If @p dest_len is -1 then no maximum is used.
+ **/
+size_t push_ascii(void *dest, const char *src, size_t dest_len, int flags)
+{
+ size_t src_len = strlen(src);
+ pstring tmpbuf;
+
+ /* treat a pstring as "unlimited" length */
+ if (dest_len == (size_t)-1)
+ dest_len = sizeof(pstring);
+
+ if (flags & STR_UPPER) {
+ pstrcpy(tmpbuf, src);
+ strupper_m(tmpbuf);
+ src = tmpbuf;
+ }
+
+ if (flags & (STR_TERMINATE | STR_TERMINATE_ASCII))
+ src_len++;
+
+ return convert_string(CH_UNIX, CH_DOS, src, src_len, dest, dest_len, True);
+}
+
+size_t push_ascii_fstring(void *dest, const char *src)
+{
+ return push_ascii(dest, src, sizeof(fstring), STR_TERMINATE);
+}
+
+size_t push_ascii_pstring(void *dest, const char *src)
+{
+ return push_ascii(dest, src, sizeof(pstring), STR_TERMINATE);
+}
+
+/********************************************************************
+ Push an nstring - ensure null terminated. Written by
+ moriyama@miraclelinux.com (MORIYAMA Masayuki).
+********************************************************************/
+
+size_t push_ascii_nstring(void *dest, const char *src)
+{
+ size_t i, buffer_len, dest_len;
+ smb_ucs2_t *buffer;
+
+ conv_silent = True;
+ buffer_len = push_ucs2_allocate(&buffer, src);
+ if (buffer_len == (size_t)-1) {
+ smb_panic("failed to create UCS2 buffer");
+ }
+
+ /* We're using buffer_len below to count ucs2 characters, not bytes. */
+ buffer_len /= sizeof(smb_ucs2_t);
+
+ dest_len = 0;
+ for (i = 0; buffer[i] != 0 && (i < buffer_len); i++) {
+ unsigned char mb[10];
+ /* Convert one smb_ucs2_t character at a time. */
+ size_t mb_len = convert_string(CH_UCS2, CH_DOS, buffer+i, sizeof(smb_ucs2_t), mb, sizeof(mb), False);
+ if ((mb_len != (size_t)-1) && (dest_len + mb_len <= MAX_NETBIOSNAME_LEN - 1)) {
+ memcpy((char *)dest + dest_len, mb, mb_len);
+ dest_len += mb_len;
+ } else {
+ errno = E2BIG;
+ break;
+ }
+ }
+ ((char *)dest)[dest_len] = '\0';
+
+ SAFE_FREE(buffer);
+ conv_silent = False;
+ return dest_len;
+}
+
+/**
+ * Copy a string from a dos codepage source to a unix char* destination.
+ *
+ * The resulting string in "dest" is always null terminated.
+ *
+ * @param flags can have:
+ * <dl>
+ * <dt>STR_TERMINATE</dt>
+ * <dd>STR_TERMINATE means the string in @p src
+ * is null terminated, and src_len is ignored.</dd>
+ * </dl>
+ *
+ * @param src_len is the length of the source area in bytes.
+ * @returns the number of bytes occupied by the string in @p src.
+ **/
+size_t pull_ascii(char *dest, const void *src, size_t dest_len, size_t src_len, int flags)
+{
+ size_t ret;
+
+ if (dest_len == (size_t)-1)
+ dest_len = sizeof(pstring);
+
+ if (flags & STR_TERMINATE) {
+ if (src_len == (size_t)-1) {
+ src_len = strlen(src) + 1;
+ } else {
+ size_t len = strnlen(src, src_len);
+ if (len < src_len)
+ len++;
+ src_len = len;
+ }
+ }
+
+ ret = convert_string(CH_DOS, CH_UNIX, src, src_len, dest, dest_len, True);
+ if (ret == (size_t)-1) {
+ dest_len = 0;
+ }
+
+ if (dest_len)
+ dest[MIN(ret, dest_len-1)] = 0;
+ else
+ dest[0] = 0;
+
+ return src_len;
+}
+
+size_t pull_ascii_pstring(char *dest, const void *src)
+{
+ return pull_ascii(dest, src, sizeof(pstring), -1, STR_TERMINATE);
+}
+
+size_t pull_ascii_fstring(char *dest, const void *src)
+{
+ return pull_ascii(dest, src, sizeof(fstring), -1, STR_TERMINATE);
+}
+
+/* When pulling an nstring it can expand into a larger size (dos cp -> utf8). Cope with this. */
+
+size_t pull_ascii_nstring(char *dest, size_t dest_len, const void *src)
+{
+ return pull_ascii(dest, src, dest_len, sizeof(nstring), STR_TERMINATE);
+}
+
+/**
+ * Copy a string from a char* src to a unicode destination.
+ *
+ * @returns the number of bytes occupied by the string in the destination.
+ *
+ * @param flags can have:
+ *
+ * <dl>
+ * <dt>STR_TERMINATE <dd>means include the null termination.
+ * <dt>STR_UPPER <dd>means uppercase in the destination.
+ * <dt>STR_NOALIGN <dd>means don't do alignment.
+ * </dl>
+ *
+ * @param dest_len is the maximum length allowed in the
+ * destination. If dest_len is -1 then no maxiumum is used.
+ **/
+
+size_t push_ucs2(const void *base_ptr, void *dest, const char *src, size_t dest_len, int flags)
+{
+ size_t len=0;
+ size_t src_len;
+ size_t ret;
+
+ /* treat a pstring as "unlimited" length */
+ if (dest_len == (size_t)-1)
+ dest_len = sizeof(pstring);
+
+ if (flags & STR_TERMINATE)
+ src_len = (size_t)-1;
+ else
+ src_len = strlen(src);
+
+ if (ucs2_align(base_ptr, dest, flags)) {
+ *(char *)dest = 0;
+ dest = (void *)((char *)dest + 1);
+ if (dest_len)
+ dest_len--;
+ len++;
+ }
+
+ /* ucs2 is always a multiple of 2 bytes */
+ dest_len &= ~1;
+
+ ret = convert_string(CH_UNIX, CH_UCS2, src, src_len, dest, dest_len, True);
+ if (ret == (size_t)-1) {
+ return 0;
+ }
+
+ len += ret;
+
+ if (flags & STR_UPPER) {
+ smb_ucs2_t *dest_ucs2 = dest;
+ size_t i;
+ for (i = 0; i < (dest_len / 2) && dest_ucs2[i]; i++) {
+ smb_ucs2_t v = toupper_w(dest_ucs2[i]);
+ if (v != dest_ucs2[i]) {
+ dest_ucs2[i] = v;
+ }
+ }
+ }
+
+ return len;
+}
+
+
+/**
+ * Copy a string from a unix char* src to a UCS2 destination,
+ * allocating a buffer using talloc().
+ *
+ * @param dest always set at least to NULL
+ *
+ * @returns The number of bytes occupied by the string in the destination
+ * or -1 in case of error.
+ **/
+size_t push_ucs2_talloc(TALLOC_CTX *ctx, smb_ucs2_t **dest, const char *src)
+{
+ size_t src_len = strlen(src)+1;
+
+ *dest = NULL;
+ return convert_string_talloc(ctx, CH_UNIX, CH_UCS2, src, src_len, (void **)dest, True);
+}
+
+
+/**
+ * Copy a string from a unix char* src to a UCS2 destination, allocating a buffer
+ *
+ * @param dest always set at least to NULL
+ *
+ * @returns The number of bytes occupied by the string in the destination
+ * or -1 in case of error.
+ **/
+
+size_t push_ucs2_allocate(smb_ucs2_t **dest, const char *src)
+{
+ size_t src_len = strlen(src)+1;
+
+ *dest = NULL;
+ return convert_string_allocate(NULL, CH_UNIX, CH_UCS2, src, src_len, (void **)dest, True);
+}
+
+/**
+ Copy a string from a char* src to a UTF-8 destination.
+ Return the number of bytes occupied by the string in the destination
+ Flags can have:
+ STR_TERMINATE means include the null termination
+ STR_UPPER means uppercase in the destination
+ dest_len is the maximum length allowed in the destination. If dest_len
+ is -1 then no maxiumum is used.
+**/
+
+static size_t push_utf8(void *dest, const char *src, size_t dest_len, int flags)
+{
+ size_t src_len = strlen(src);
+ pstring tmpbuf;
+
+ /* treat a pstring as "unlimited" length */
+ if (dest_len == (size_t)-1)
+ dest_len = sizeof(pstring);
+
+ if (flags & STR_UPPER) {
+ pstrcpy(tmpbuf, src);
+ strupper_m(tmpbuf);
+ src = tmpbuf;
+ }
+
+ if (flags & STR_TERMINATE)
+ src_len++;
+
+ return convert_string(CH_UNIX, CH_UTF8, src, src_len, dest, dest_len, True);
+}
+
+size_t push_utf8_fstring(void *dest, const char *src)
+{
+ return push_utf8(dest, src, sizeof(fstring), STR_TERMINATE);
+}
+
+/**
+ * Copy a string from a unix char* src to a UTF-8 destination, allocating a buffer using talloc
+ *
+ * @param dest always set at least to NULL
+ *
+ * @returns The number of bytes occupied by the string in the destination
+ **/
+
+size_t push_utf8_talloc(TALLOC_CTX *ctx, char **dest, const char *src)
+{
+ size_t src_len = strlen(src)+1;
+
+ *dest = NULL;
+ return convert_string_talloc(ctx, CH_UNIX, CH_UTF8, src, src_len, (void**)dest, True);
+}
+
+/**
+ * Copy a string from a unix char* src to a UTF-8 destination, allocating a buffer
+ *
+ * @param dest always set at least to NULL
+ *
+ * @returns The number of bytes occupied by the string in the destination
+ **/
+
+size_t push_utf8_allocate(char **dest, const char *src)
+{
+ size_t src_len = strlen(src)+1;
+
+ *dest = NULL;
+ return convert_string_allocate(NULL, CH_UNIX, CH_UTF8, src, src_len, (void **)dest, True);
+}
+
+/**
+ Copy a string from a ucs2 source to a unix char* destination.
+ Flags can have:
+ STR_TERMINATE means the string in src is null terminated.
+ STR_NOALIGN means don't try to align.
+ if STR_TERMINATE is set then src_len is ignored if it is -1.
+ src_len is the length of the source area in bytes
+ Return the number of bytes occupied by the string in src.
+ The resulting string in "dest" is always null terminated.
+**/
+
+size_t pull_ucs2(const void *base_ptr, char *dest, const void *src, size_t dest_len, size_t src_len, int flags)
+{
+ size_t ret;
+
+ if (dest_len == (size_t)-1)
+ dest_len = sizeof(pstring);
+
+ if (ucs2_align(base_ptr, src, flags)) {
+ src = (const void *)((const char *)src + 1);
+ if (src_len != (size_t)-1)
+ src_len--;
+ }
+
+ if (flags & STR_TERMINATE) {
+ /* src_len -1 is the default for null terminated strings. */
+ if (src_len != (size_t)-1) {
+ size_t len = strnlen_w(src, src_len/2);
+ if (len < src_len/2)
+ len++;
+ src_len = len*2;
+ }
+ }
+
+ /* ucs2 is always a multiple of 2 bytes */
+ if (src_len != (size_t)-1)
+ src_len &= ~1;
+
+ ret = convert_string(CH_UCS2, CH_UNIX, src, src_len, dest, dest_len, True);
+ if (ret == (size_t)-1) {
+ return 0;
+ }
+
+ if (src_len == (size_t)-1)
+ src_len = ret*2;
+
+ if (dest_len)
+ dest[MIN(ret, dest_len-1)] = 0;
+ else
+ dest[0] = 0;
+
+ return src_len;
+}
+
+size_t pull_ucs2_pstring(char *dest, const void *src)
+{
+ return pull_ucs2(NULL, dest, src, sizeof(pstring), -1, STR_TERMINATE);
+}
+
+size_t pull_ucs2_fstring(char *dest, const void *src)
+{
+ return pull_ucs2(NULL, dest, src, sizeof(fstring), -1, STR_TERMINATE);
+}
+
+/**
+ * Copy a string from a UCS2 src to a unix char * destination, allocating a buffer using talloc
+ *
+ * @param dest always set at least to NULL
+ *
+ * @returns The number of bytes occupied by the string in the destination
+ **/
+
+size_t pull_ucs2_talloc(TALLOC_CTX *ctx, char **dest, const smb_ucs2_t *src)
+{
+ size_t src_len = (strlen_w(src)+1) * sizeof(smb_ucs2_t);
+ *dest = NULL;
+ return convert_string_talloc(ctx, CH_UCS2, CH_UNIX, src, src_len, (void **)dest, True);
+}
+
+/**
+ * Copy a string from a UCS2 src to a unix char * destination, allocating a buffer
+ *
+ * @param dest always set at least to NULL
+ *
+ * @returns The number of bytes occupied by the string in the destination
+ **/
+
+size_t pull_ucs2_allocate(char **dest, const smb_ucs2_t *src)
+{
+ size_t src_len = (strlen_w(src)+1) * sizeof(smb_ucs2_t);
+ *dest = NULL;
+ return convert_string_allocate(NULL, CH_UCS2, CH_UNIX, src, src_len, (void **)dest, True);
+}
+
+/**
+ * Copy a string from a UTF-8 src to a unix char * destination, allocating a buffer using talloc
+ *
+ * @param dest always set at least to NULL
+ *
+ * @returns The number of bytes occupied by the string in the destination
+ **/
+
+size_t pull_utf8_talloc(TALLOC_CTX *ctx, char **dest, const char *src)
+{
+ size_t src_len = strlen(src)+1;
+ *dest = NULL;
+ return convert_string_talloc(ctx, CH_UTF8, CH_UNIX, src, src_len, (void **)dest, True);
+}
+
+/**
+ * Copy a string from a UTF-8 src to a unix char * destination, allocating a buffer
+ *
+ * @param dest always set at least to NULL
+ *
+ * @returns The number of bytes occupied by the string in the destination
+ **/
+
+size_t pull_utf8_allocate(char **dest, const char *src)
+{
+ size_t src_len = strlen(src)+1;
+ *dest = NULL;
+ return convert_string_allocate(NULL, CH_UTF8, CH_UNIX, src, src_len, (void **)dest, True);
+}
+
+/**
+ Copy a string from a char* src to a unicode or ascii
+ dos codepage destination choosing unicode or ascii based on the
+ flags in the SMB buffer starting at base_ptr.
+ Return the number of bytes occupied by the string in the destination.
+ flags can have:
+ STR_TERMINATE means include the null termination.
+ STR_UPPER means uppercase in the destination.
+ STR_ASCII use ascii even with unicode packet.
+ STR_NOALIGN means don't do alignment.
+ dest_len is the maximum length allowed in the destination. If dest_len
+ is -1 then no maxiumum is used.
+**/
+
+size_t push_string_fn(const char *function, unsigned int line, const void *base_ptr, void *dest, const char *src, size_t dest_len, int flags)
+{
+#ifdef DEVELOPER
+ /* We really need to zero fill here, not clobber
+ * region, as we want to ensure that valgrind thinks
+ * all of the outgoing buffer has been written to
+ * so a send() or write() won't trap an error.
+ * JRA.
+ */
+#if 0
+ if (dest_len != (size_t)-1)
+ clobber_region(function, line, dest, dest_len);
+#else
+ if (dest_len != (size_t)-1)
+ memset(dest, '\0', dest_len);
+#endif
+#endif
+
+ if (!(flags & STR_ASCII) && \
+ ((flags & STR_UNICODE || \
+ (SVAL(base_ptr, smb_flg2) & FLAGS2_UNICODE_STRINGS)))) {
+ return push_ucs2(base_ptr, dest, src, dest_len, flags);
+ }
+ return push_ascii(dest, src, dest_len, flags);
+}
+
+
+/**
+ Copy a string from a unicode or ascii source (depending on
+ the packet flags) to a char* destination.
+ Flags can have:
+ STR_TERMINATE means the string in src is null terminated.
+ STR_UNICODE means to force as unicode.
+ STR_ASCII use ascii even with unicode packet.
+ STR_NOALIGN means don't do alignment.
+ if STR_TERMINATE is set then src_len is ignored is it is -1
+ src_len is the length of the source area in bytes.
+ Return the number of bytes occupied by the string in src.
+ The resulting string in "dest" is always null terminated.
+**/
+
+size_t pull_string_fn(const char *function, unsigned int line, const void *base_ptr, char *dest, const void *src, size_t dest_len, size_t src_len, int flags)
+{
+#ifdef DEVELOPER
+ if (dest_len != (size_t)-1)
+ clobber_region(function, line, dest, dest_len);
+#endif
+
+ if (!(flags & STR_ASCII) && \
+ ((flags & STR_UNICODE || \
+ (SVAL(base_ptr, smb_flg2) & FLAGS2_UNICODE_STRINGS)))) {
+ return pull_ucs2(base_ptr, dest, src, dest_len, src_len, flags);
+ }
+ return pull_ascii(dest, src, dest_len, src_len, flags);
+}
+
+size_t align_string(const void *base_ptr, const char *p, int flags)
+{
+ if (!(flags & STR_ASCII) && \
+ ((flags & STR_UNICODE || \
+ (SVAL(base_ptr, smb_flg2) & FLAGS2_UNICODE_STRINGS)))) {
+ return ucs2_align(base_ptr, p, flags);
+ }
+ return 0;
+}
+
+/****************************************************************
+ Calculate the size (in bytes) of the next multibyte character in
+ our internal character set. Note that p must be pointing to a
+ valid mb char, not within one.
+****************************************************************/
+
+size_t next_mb_char_size(const char *s)
+{
+ size_t i;
+
+ if (!(*s & 0x80))
+ return 1; /* ascii. */
+
+ conv_silent = True;
+ for ( i = 1; i <=4; i++ ) {
+ smb_ucs2_t uc;
+ if (convert_string(CH_UNIX, CH_UCS2, s, i, &uc, 2, False) == 2) {
+#if 0 /* JRATEST */
+ DEBUG(10,("next_mb_char_size: size %u at string %s\n",
+ (unsigned int)i, s));
+#endif
+ conv_silent = False;
+ return i;
+ }
+ }
+ /* We're hosed - we don't know how big this is... */
+ DEBUG(10,("next_mb_char_size: unknown size at string %s\n", s));
+ conv_silent = False;
+ return 1;
+}
diff --git a/source/lib/clobber.c b/source/lib/clobber.c
new file mode 100644
index 00000000000..fb3a0dc2815
--- /dev/null
+++ b/source/lib/clobber.c
@@ -0,0 +1,60 @@
+/*
+ Unix SMB/CIFS implementation.
+ Samba utility functions
+ Copyright (C) Martin Pool 2003
+ Copyright (C) Andrew Bartlett 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 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"
+
+#ifdef DEVELOPER
+const char *global_clobber_region_function;
+unsigned int global_clobber_region_line;
+#endif
+
+/**
+ * In developer builds, clobber a region of memory.
+ *
+ * If we think a string buffer is longer than it really is, this ought
+ * to make the failure obvious, by segfaulting (if in the heap) or by
+ * killing the return address (on the stack), or by trapping under a
+ * memory debugger.
+ *
+ * This is meant to catch possible string overflows, even if the
+ * actual string copied is not big enough to cause an overflow.
+ *
+ * In addition, under Valgrind the buffer is marked as uninitialized.
+ **/
+void clobber_region(const char *fn, unsigned int line, char *dest, size_t len)
+{
+#ifdef DEVELOPER
+ global_clobber_region_function = fn;
+ global_clobber_region_line = line;
+
+ /* F1 is odd and 0xf1f1f1f1 shouldn't be a valid pointer */
+ memset(dest, 0xF1, len);
+#ifdef VALGRIND
+ /* Even though we just wrote to this, from the application's
+ * point of view it is not initialized.
+ *
+ * (This is not redundant with the clobbering above. The
+ * marking might not actually take effect if we're not running
+ * under valgrind.) */
+ VALGRIND_MAKE_WRITABLE(dest, len);
+#endif /* VALGRIND */
+#endif /* DEVELOPER */
+}
diff --git a/source/lib/crc32.c b/source/lib/crc32.c
new file mode 100644
index 00000000000..da3aeaa901d
--- /dev/null
+++ b/source/lib/crc32.c
@@ -0,0 +1,67 @@
+/*
+ * Copyright Francesco Ferrara, 1998 <francesco@aerra.it>
+ *
+ * Used by kind permission, 14th October 1998. http://www.aerre.it/francesco
+ *
+ *
+ */
+
+#include "includes.h"
+
+static const unsigned long CRCTable[256] =
+{
+ 0x00000000,0x77073096,0xEE0E612C,0x990951BA,0x076DC419,0x706AF48F,
+ 0xE963A535,0x9E6495A3,0x0EDB8832,0x79DCB8A4,0xE0D5E91E,0x97D2D988,
+ 0x09B64C2B,0x7EB17CBD,0xE7B82D07,0x90BF1D91,0x1DB71064,0x6AB020F2,
+ 0xF3B97148,0x84BE41DE,0x1ADAD47D,0x6DDDE4EB,0xF4D4B551,0x83D385C7,
+ 0x136C9856,0x646BA8C0,0xFD62F97A,0x8A65C9EC,0x14015C4F,0x63066CD9,
+ 0xFA0F3D63,0x8D080DF5,0x3B6E20C8,0x4C69105E,0xD56041E4,0xA2677172,
+ 0x3C03E4D1,0x4B04D447,0xD20D85FD,0xA50AB56B,0x35B5A8FA,0x42B2986C,
+ 0xDBBBC9D6,0xACBCF940,0x32D86CE3,0x45DF5C75,0xDCD60DCF,0xABD13D59,
+ 0x26D930AC,0x51DE003A,0xC8D75180,0xBFD06116,0x21B4F4B5,0x56B3C423,
+ 0xCFBA9599,0xB8BDA50F,0x2802B89E,0x5F058808,0xC60CD9B2,0xB10BE924,
+ 0x2F6F7C87,0x58684C11,0xC1611DAB,0xB6662D3D,0x76DC4190,0x01DB7106,
+ 0x98D220BC,0xEFD5102A,0x71B18589,0x06B6B51F,0x9FBFE4A5,0xE8B8D433,
+ 0x7807C9A2,0x0F00F934,0x9609A88E,0xE10E9818,0x7F6A0DBB,0x086D3D2D,
+ 0x91646C97,0xE6635C01,0x6B6B51F4,0x1C6C6162,0x856530D8,0xF262004E,
+ 0x6C0695ED,0x1B01A57B,0x8208F4C1,0xF50FC457,0x65B0D9C6,0x12B7E950,
+ 0x8BBEB8EA,0xFCB9887C,0x62DD1DDF,0x15DA2D49,0x8CD37CF3,0xFBD44C65,
+ 0x4DB26158,0x3AB551CE,0xA3BC0074,0xD4BB30E2,0x4ADFA541,0x3DD895D7,
+ 0xA4D1C46D,0xD3D6F4FB,0x4369E96A,0x346ED9FC,0xAD678846,0xDA60B8D0,
+ 0x44042D73,0x33031DE5,0xAA0A4C5F,0xDD0D7CC9,0x5005713C,0x270241AA,
+ 0xBE0B1010,0xC90C2086,0x5768B525,0x206F85B3,0xB966D409,0xCE61E49F,
+ 0x5EDEF90E,0x29D9C998,0xB0D09822,0xC7D7A8B4,0x59B33D17,0x2EB40D81,
+ 0xB7BD5C3B,0xC0BA6CAD,0xEDB88320,0x9ABFB3B6,0x03B6E20C,0x74B1D29A,
+ 0xEAD54739,0x9DD277AF,0x04DB2615,0x73DC1683,0xE3630B12,0x94643B84,
+ 0x0D6D6A3E,0x7A6A5AA8,0xE40ECF0B,0x9309FF9D,0x0A00AE27,0x7D079EB1,
+ 0xF00F9344,0x8708A3D2,0x1E01F268,0x6906C2FE,0xF762575D,0x806567CB,
+ 0x196C3671,0x6E6B06E7,0xFED41B76,0x89D32BE0,0x10DA7A5A,0x67DD4ACC,
+ 0xF9B9DF6F,0x8EBEEFF9,0x17B7BE43,0x60B08ED5,0xD6D6A3E8,0xA1D1937E,
+ 0x38D8C2C4,0x4FDFF252,0xD1BB67F1,0xA6BC5767,0x3FB506DD,0x48B2364B,
+ 0xD80D2BDA,0xAF0A1B4C,0x36034AF6,0x41047A60,0xDF60EFC3,0xA867DF55,
+ 0x316E8EEF,0x4669BE79,0xCB61B38C,0xBC66831A,0x256FD2A0,0x5268E236,
+ 0xCC0C7795,0xBB0B4703,0x220216B9,0x5505262F,0xC5BA3BBE,0xB2BD0B28,
+ 0x2BB45A92,0x5CB36A04,0xC2D7FFA7,0xB5D0CF31,0x2CD99E8B,0x5BDEAE1D,
+ 0x9B64C2B0,0xEC63F226,0x756AA39C,0x026D930A,0x9C0906A9,0xEB0E363F,
+ 0x72076785,0x05005713,0x95BF4A82,0xE2B87A14,0x7BB12BAE,0x0CB61B38,
+ 0x92D28E9B,0xE5D5BE0D,0x7CDCEFB7,0x0BDBDF21,0x86D3D2D4,0xF1D4E242,
+ 0x68DDB3F8,0x1FDA836E,0x81BE16CD,0xF6B9265B,0x6FB077E1,0x18B74777,
+ 0x88085AE6,0xFF0F6A70,0x66063BCA,0x11010B5C,0x8F659EFF,0xF862AE69,
+ 0x616BFFD3,0x166CCF45,0xA00AE278,0xD70DD2EE,0x4E048354,0x3903B3C2,
+ 0xA7672661,0xD06016F7,0x4969474D,0x3E6E77DB,0xAED16A4A,0xD9D65ADC,
+ 0x40DF0B66,0x37D83BF0,0xA9BCAE53,0xDEBB9EC5,0x47B2CF7F,0x30B5FFE9,
+ 0xBDBDF21C,0xCABAC28A,0x53B39330,0x24B4A3A6,0xBAD03605,0xCDD70693,
+ 0x54DE5729,0x23D967BF,0xB3667A2E,0xC4614AB8,0x5D681B02,0x2A6F2B94,
+ 0xB40BBE37,0xC30C8EA1,0x5A05DF1B,0x2D02EF8D
+};
+
+uint32 crc32_calc_buffer( const char *buffer, uint32 count)
+{
+ uint32 crc=0xffffffff, i;
+ for(i=0;i<count;i++)
+ crc = (crc>>8) ^ CRCTable[(buffer[i] ^ crc) & 0xff];
+ crc^=0xffffffff;
+ DEBUG(10,("crc32_calc_buffer: %x\n", crc));
+ dump_data(100, buffer, count);
+ return crc;
+}
diff --git a/source/lib/data_blob.c b/source/lib/data_blob.c
new file mode 100644
index 00000000000..83afc591a15
--- /dev/null
+++ b/source/lib/data_blob.c
@@ -0,0 +1,115 @@
+/*
+ Unix SMB/CIFS implementation.
+ Easy management of byte-length data
+ Copyright (C) Andrew Tridgell 2001
+ Copyright (C) Andrew Bartlett 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 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"
+
+/*******************************************************************
+ free() a data blob
+*******************************************************************/
+static void free_data_blob(DATA_BLOB *d)
+{
+ if ((d) && (d->free)) {
+ SAFE_FREE(d->data);
+ }
+}
+
+/*******************************************************************
+ construct a data blob, must be freed with data_blob_free()
+ you can pass NULL for p and get a blank data blob
+*******************************************************************/
+DATA_BLOB data_blob(const void *p, size_t length)
+{
+ DATA_BLOB ret;
+
+ if (!length) {
+ ZERO_STRUCT(ret);
+ return ret;
+ }
+
+ if (p) {
+ ret.data = smb_xmemdup(p, length);
+ } else {
+ ret.data = smb_xmalloc(length);
+ }
+ ret.length = length;
+ ret.free = free_data_blob;
+ return ret;
+}
+
+/*******************************************************************
+ construct a data blob, using supplied TALLOC_CTX
+*******************************************************************/
+DATA_BLOB data_blob_talloc(TALLOC_CTX *mem_ctx, const void *p, size_t length)
+{
+ DATA_BLOB ret;
+
+ if (!length) {
+ ZERO_STRUCT(ret);
+ return ret;
+ }
+
+ if (p) {
+ ret.data = talloc_memdup(mem_ctx, p, length);
+ if (ret.data == NULL)
+ smb_panic("data_blob_talloc: talloc_memdup failed.\n");
+ } else {
+ ret.data = talloc(mem_ctx, length);
+ if (ret.data == NULL)
+ smb_panic("data_blob_talloc: talloc failed.\n");
+ }
+
+ ret.length = length;
+ ret.free = NULL;
+ return ret;
+}
+
+/*******************************************************************
+free a data blob
+*******************************************************************/
+void data_blob_free(DATA_BLOB *d)
+{
+ if (d) {
+ if (d->free) {
+ (d->free)(d);
+ }
+ d->length = 0;
+ }
+}
+
+/*******************************************************************
+clear a DATA_BLOB's contents
+*******************************************************************/
+static void data_blob_clear(DATA_BLOB *d)
+{
+ if (d->data) {
+ memset(d->data, 0, d->length);
+ }
+}
+
+/*******************************************************************
+free a data blob and clear its contents
+*******************************************************************/
+void data_blob_clear_free(DATA_BLOB *d)
+{
+ data_blob_clear(d);
+ data_blob_free(d);
+}
+
diff --git a/source/lib/debug.c b/source/lib/debug.c
new file mode 100644
index 00000000000..1a926053bb0
--- /dev/null
+++ b/source/lib/debug.c
@@ -0,0 +1,994 @@
+/*
+ Unix SMB/CIFS implementation.
+ Samba utility functions
+ Copyright (C) Andrew Tridgell 1992-1998
+ Copyright (C) Elrond 2002
+ Copyright (C) Simo Sorce 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 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"
+
+/* -------------------------------------------------------------------------- **
+ * Defines...
+ *
+ * FORMAT_BUFR_MAX - Index of the last byte of the format buffer;
+ * format_bufr[FORMAT_BUFR_MAX] should always be reserved
+ * for a terminating null byte.
+ */
+
+#define FORMAT_BUFR_MAX ( sizeof( format_bufr ) - 1 )
+
+/* -------------------------------------------------------------------------- **
+ * This module implements Samba's debugging utility.
+ *
+ * The syntax of a debugging log file is represented as:
+ *
+ * <debugfile> :== { <debugmsg> }
+ *
+ * <debugmsg> :== <debughdr> '\n' <debugtext>
+ *
+ * <debughdr> :== '[' TIME ',' LEVEL ']' [ [FILENAME ':'] [FUNCTION '()'] ]
+ *
+ * <debugtext> :== { <debugline> }
+ *
+ * <debugline> :== TEXT '\n'
+ *
+ * TEXT is a string of characters excluding the newline character.
+ * LEVEL is the DEBUG level of the message (an integer in the range 0..10).
+ * TIME is a timestamp.
+ * FILENAME is the name of the file from which the debug message was generated.
+ * FUNCTION is the function from which the debug message was generated.
+ *
+ * Basically, what that all means is:
+ *
+ * - A debugging log file is made up of debug messages.
+ *
+ * - Each debug message is made up of a header and text. The header is
+ * separated from the text by a newline.
+ *
+ * - The header begins with the timestamp and debug level of the message
+ * enclosed in brackets. The filename and function from which the
+ * message was generated may follow. The filename is terminated by a
+ * colon, and the function name is terminated by parenthesis.
+ *
+ * - The message text is made up of zero or more lines, each terminated by
+ * a newline.
+ */
+
+/* -------------------------------------------------------------------------- **
+ * External variables.
+ *
+ * dbf - Global debug file handle.
+ * debugf - Debug file name.
+ * DEBUGLEVEL - System-wide debug message limit. Messages with message-
+ * levels higher than DEBUGLEVEL will not be processed.
+ */
+
+XFILE *dbf = NULL;
+pstring debugf = "";
+BOOL debug_warn_unknown_class = True;
+BOOL debug_auto_add_unknown_class = True;
+BOOL AllowDebugChange = True;
+
+/*
+ used to check if the user specified a
+ logfile on the command line
+*/
+BOOL override_logfile;
+
+
+/*
+ * This is to allow assignment to DEBUGLEVEL before the debug
+ * system has been initialised.
+ */
+static int debug_all_class_hack = 1;
+static BOOL debug_all_class_isset_hack = True;
+
+static int debug_num_classes = 0;
+int *DEBUGLEVEL_CLASS = &debug_all_class_hack;
+BOOL *DEBUGLEVEL_CLASS_ISSET = &debug_all_class_isset_hack;
+
+/* DEBUGLEVEL is #defined to *debug_level */
+int DEBUGLEVEL = &debug_all_class_hack;
+
+
+/* -------------------------------------------------------------------------- **
+ * Internal variables.
+ *
+ * stdout_logging - Default False, if set to True then dbf will be set to
+ * stdout and debug output will go to dbf only, and not
+ * to syslog. Set in setup_logging() and read in Debug1().
+ *
+ * debug_count - Number of debug messages that have been output.
+ * Used to check log size.
+ *
+ * syslog_level - Internal copy of the message debug level. Written by
+ * dbghdr() and read by Debug1().
+ *
+ * format_bufr - Used to format debug messages. The dbgtext() function
+ * prints debug messages to a string, and then passes the
+ * string to format_debug_text(), which uses format_bufr
+ * to build the formatted output.
+ *
+ * format_pos - Marks the first free byte of the format_bufr.
+ *
+ *
+ * log_overflow - When this variable is True, never attempt to check the
+ * size of the log. This is a hack, so that we can write
+ * a message using DEBUG, from open_logs() when we
+ * are unable to open a new log file for some reason.
+ */
+
+static BOOL stdout_logging = False;
+static int debug_count = 0;
+#ifdef WITH_SYSLOG
+static int syslog_level = 0;
+#endif
+static pstring format_bufr = { '\0' };
+static size_t format_pos = 0;
+static BOOL log_overflow = False;
+
+/*
+ * Define all the debug class selection names here. Names *MUST NOT* contain
+ * white space. There must be one name for each DBGC_<class name>, and they
+ * must be in the table in the order of DBGC_<class name>..
+ */
+static const char *default_classname_table[] = {
+ "all", /* DBGC_ALL; index refs traditional DEBUGLEVEL */
+ "tdb", /* DBGC_TDB */
+ "printdrivers", /* DBGC_PRINTDRIVERS */
+ "lanman", /* DBGC_LANMAN */
+ "smb", /* DBGC_SMB */
+ "rpc_parse", /* DBGC_RPC_PARSE */
+ "rpc_srv", /* DBGC_RPC_SRV */
+ "rpc_cli", /* DBGC_RPC_CLI */
+ "passdb", /* DBGC_PASSDB */
+ "sam", /* DBGC_SAM */
+ "auth", /* DBGC_AUTH */
+ "winbind", /* DBGC_WINBIND */
+ "vfs", /* DBGC_VFS */
+ "idmap", /* DBGC_IDMAP */
+ "quota", /* DBGC_QUOTA */
+ NULL
+};
+
+static char **classname_table = NULL;
+
+
+/* -------------------------------------------------------------------------- **
+ * Functions...
+ */
+
+
+/****************************************************************************
+utility lists registered debug class names's
+****************************************************************************/
+
+#define MAX_CLASS_NAME_SIZE 1024
+
+static char *debug_list_class_names_and_levels(void)
+{
+ int i, dim;
+ char **list;
+ char *buf = NULL;
+ char *b;
+ BOOL err = False;
+
+ if (DEBUGLEVEL_CLASS == &debug_all_class_hack)
+ return NULL;
+
+ list = calloc(debug_num_classes + 1, sizeof(char *));
+ if (!list)
+ return NULL;
+
+ /* prepare strings */
+ for (i = 0, dim = 0; i < debug_num_classes; i++) {
+ int l = asprintf(&list[i],
+ "%s:%d ",
+ classname_table[i],
+ DEBUGLEVEL_CLASS_ISSET[i]?DEBUGLEVEL_CLASS[i]:DEBUGLEVEL);
+ if (l < 0 || l > MAX_CLASS_NAME_SIZE) {
+ err = True;
+ goto done;
+ }
+ dim += l;
+ }
+
+ /* create single string list */
+ b = buf = malloc(dim);
+ if (!buf) {
+ err = True;
+ goto done;
+ }
+ for (i = 0; i < debug_num_classes; i++) {
+ int l = strlen(list[i]);
+ strncpy(b, list[i], l);
+ b = b + l;
+ }
+ b[-1] = '\0';
+
+done:
+ /* free strings list */
+ for (i = 0; i < debug_num_classes; i++)
+ if (list[i]) free(list[i]);
+ free(list);
+
+ if (err) {
+ if (buf)
+ free(buf);
+ return NULL;
+ } else {
+ return buf;
+ }
+}
+
+/****************************************************************************
+utility access to debug class names's
+****************************************************************************/
+const char *debug_classname_from_index(int ndx)
+{
+ if (ndx < 0 || ndx >= debug_num_classes)
+ return NULL;
+ else
+ return classname_table[ndx];
+}
+
+/****************************************************************************
+utility to translate names to debug class index's (internal version)
+****************************************************************************/
+static int debug_lookup_classname_int(const char* classname)
+{
+ int i;
+
+ if (!classname) return -1;
+
+ for (i=0; i < debug_num_classes; i++) {
+ if (strcmp(classname, classname_table[i])==0)
+ return i;
+ }
+ return -1;
+}
+
+/****************************************************************************
+Add a new debug class to the system
+****************************************************************************/
+int debug_add_class(const char *classname)
+{
+ int ndx;
+ void *new_ptr;
+
+ if (!classname)
+ return -1;
+
+ /* check the init has yet been called */
+ debug_init();
+
+ ndx = debug_lookup_classname_int(classname);
+ if (ndx >= 0)
+ return ndx;
+ ndx = debug_num_classes;
+
+ new_ptr = DEBUGLEVEL_CLASS;
+ if (DEBUGLEVEL_CLASS == &debug_all_class_hack)
+ {
+ /* Initial loading... */
+ new_ptr = NULL;
+ }
+ new_ptr = Realloc(new_ptr,
+ sizeof(int) * (debug_num_classes + 1));
+ if (!new_ptr)
+ return -1;
+ DEBUGLEVEL_CLASS = new_ptr;
+ DEBUGLEVEL_CLASS[ndx] = 0;
+
+ /* debug_level is the pointer used for the DEBUGLEVEL-thingy */
+ if (ndx==0)
+ {
+ /* Transfer the initial level from debug_all_class_hack */
+ DEBUGLEVEL_CLASS[ndx] = DEBUGLEVEL;
+ }
+ debug_level = DEBUGLEVEL_CLASS;
+
+ new_ptr = DEBUGLEVEL_CLASS_ISSET;
+ if (new_ptr == &debug_all_class_isset_hack)
+ {
+ new_ptr = NULL;
+ }
+ new_ptr = Realloc(new_ptr,
+ sizeof(BOOL) * (debug_num_classes + 1));
+ if (!new_ptr)
+ return -1;
+ DEBUGLEVEL_CLASS_ISSET = new_ptr;
+ DEBUGLEVEL_CLASS_ISSET[ndx] = False;
+
+ new_ptr = Realloc(classname_table,
+ sizeof(char *) * (debug_num_classes + 1));
+ if (!new_ptr)
+ return -1;
+ classname_table = new_ptr;
+
+ classname_table[ndx] = strdup(classname);
+ if (! classname_table[ndx])
+ return -1;
+
+ debug_num_classes++;
+
+ return ndx;
+}
+
+/****************************************************************************
+utility to translate names to debug class index's (public version)
+****************************************************************************/
+int debug_lookup_classname(const char *classname)
+{
+ int ndx;
+
+ if (!classname || !*classname) return -1;
+
+ ndx = debug_lookup_classname_int(classname);
+
+ if (ndx != -1)
+ return ndx;
+
+ if (debug_warn_unknown_class)
+ {
+ DEBUG(0, ("debug_lookup_classname(%s): Unknown class\n",
+ classname));
+ }
+ if (debug_auto_add_unknown_class)
+ {
+ return debug_add_class(classname);
+ }
+ return -1;
+}
+
+
+/****************************************************************************
+dump the current registered debug levels
+****************************************************************************/
+static void debug_dump_status(int level)
+{
+ int q;
+
+ DEBUG(level, ("INFO: Current debug levels:\n"));
+ for (q = 0; q < debug_num_classes; q++)
+ {
+ DEBUGADD(level, (" %s: %s/%d\n",
+ classname_table[q],
+ (DEBUGLEVEL_CLASS_ISSET[q]
+ ? "True" : "False"),
+ DEBUGLEVEL_CLASS[q]));
+ }
+}
+
+/****************************************************************************
+parse the debug levels from smbcontrol. Example debug level parameter:
+ printdrivers:7
+****************************************************************************/
+static BOOL debug_parse_params(char **params)
+{
+ int i, ndx;
+ char *class_name;
+ char *class_level;
+
+ if (!params)
+ return False;
+
+ /* Allow DBGC_ALL to be specified w/o requiring its class name e.g."10"
+ * v.s. "all:10", this is the traditional way to set DEBUGLEVEL
+ */
+ if (isdigit((int)params[0][0])) {
+ DEBUGLEVEL_CLASS[DBGC_ALL] = atoi(params[0]);
+ DEBUGLEVEL_CLASS_ISSET[DBGC_ALL] = True;
+ i = 1; /* start processing at the next params */
+ }
+ else
+ i = 0; /* DBGC_ALL not specified OR class name was included */
+
+ /* Fill in new debug class levels */
+ for (; i < debug_num_classes && params[i]; i++) {
+ if ((class_name=strtok(params[i],":")) &&
+ (class_level=strtok(NULL, "\0")) &&
+ ((ndx = debug_lookup_classname(class_name)) != -1)) {
+ DEBUGLEVEL_CLASS[ndx] = atoi(class_level);
+ DEBUGLEVEL_CLASS_ISSET[ndx] = True;
+ } else {
+ DEBUG(0,("debug_parse_params: unrecognized debug class name or format [%s]\n", params[i]));
+ return False;
+ }
+ }
+
+ return True;
+}
+
+/****************************************************************************
+parse the debug levels from smb.conf. Example debug level string:
+ 3 tdb:5 printdrivers:7
+Note: the 1st param has no "name:" preceeding it.
+****************************************************************************/
+BOOL debug_parse_levels(const char *params_str)
+{
+ char **params;
+
+ /* Just in case */
+ debug_init();
+
+ if (AllowDebugChange == False)
+ return True;
+
+ params = str_list_make(params_str, NULL);
+
+ if (debug_parse_params(params))
+ {
+ debug_dump_status(5);
+ str_list_free(&params);
+ return True;
+ } else {
+ str_list_free(&params);
+ return False;
+ }
+}
+
+/****************************************************************************
+receive a "set debug level" message
+****************************************************************************/
+static void debug_message(int msg_type, pid_t src, void *buf, size_t len)
+{
+ const char *params_str = buf;
+
+ /* Check, it's a proper string! */
+ if (params_str[len-1] != '\0')
+ {
+ DEBUG(1, ("Invalid debug message from pid %u to pid %u\n",
+ (unsigned int)src, (unsigned int)getpid()));
+ return;
+ }
+
+ DEBUG(3, ("INFO: Remote set of debug to `%s' (pid %u from pid %u)\n",
+ params_str, (unsigned int)getpid(), (unsigned int)src));
+
+ debug_parse_levels(params_str);
+}
+
+
+/****************************************************************************
+send a "set debug level" message
+****************************************************************************/
+void debug_message_send(pid_t pid, const char *params_str)
+{
+ if (!params_str)
+ return;
+ message_send_pid(pid, MSG_DEBUG, params_str, strlen(params_str) + 1,
+ False);
+}
+
+/****************************************************************************
+ Return current debug level.
+****************************************************************************/
+
+static void debuglevel_message(int msg_type, pid_t src, void *buf, size_t len)
+{
+ char *message = debug_list_class_names_and_levels();
+
+ DEBUG(1,("INFO: Received REQ_DEBUGLEVEL message from PID %u\n",(unsigned int)src));
+ message_send_pid(src, MSG_DEBUGLEVEL, message, strlen(message) + 1, True);
+
+ SAFE_FREE(message);
+}
+
+/****************************************************************************
+Init debugging (one time stuff)
+****************************************************************************/
+void debug_init(void)
+{
+ static BOOL initialised = False;
+ const char **p;
+
+ if (initialised)
+ return;
+
+ initialised = True;
+
+ message_register(MSG_DEBUG, debug_message);
+ message_register(MSG_REQ_DEBUGLEVEL, debuglevel_message);
+
+ for(p = default_classname_table; *p; p++)
+ {
+ debug_add_class(*p);
+ }
+}
+
+
+/* ************************************************************************** **
+ * get ready for syslog stuff
+ * ************************************************************************** **
+ */
+void setup_logging(const char *pname, BOOL interactive)
+{
+ debug_init();
+
+ /* reset to allow multiple setup calls, going from interactive to
+ non-interactive */
+ stdout_logging = False;
+ dbf = NULL;
+
+ if (interactive) {
+ stdout_logging = True;
+ dbf = x_stdout;
+ x_setbuf( x_stdout, NULL );
+ }
+#ifdef WITH_SYSLOG
+ else {
+ const char *p = strrchr_m( pname,'/' );
+ if (p)
+ pname = p + 1;
+#ifdef LOG_DAEMON
+ openlog( pname, LOG_PID, SYSLOG_FACILITY );
+#else
+ /* for old systems that have no facility codes. */
+ openlog( pname, LOG_PID );
+#endif
+ }
+#endif
+} /* setup_logging */
+
+/* ************************************************************************** **
+ * reopen the log files
+ * note that we now do this unconditionally
+ * We attempt to open the new debug fp before closing the old. This means
+ * if we run out of fd's we just keep using the old fd rather than aborting.
+ * Fix from dgibson@linuxcare.com.
+ * ************************************************************************** **
+ */
+
+BOOL reopen_logs( void )
+{
+ pstring fname;
+ mode_t oldumask;
+ XFILE *new_dbf = NULL;
+ XFILE *old_dbf = NULL;
+ BOOL ret = True;
+
+ if (stdout_logging)
+ return True;
+
+ oldumask = umask( 022 );
+
+ pstrcpy(fname, debugf );
+
+ if (lp_loaded()) {
+ char *logfname;
+
+ logfname = lp_logfile();
+ if (*logfname)
+ pstrcpy(fname, logfname);
+ }
+
+ pstrcpy( debugf, fname );
+ new_dbf = x_fopen( debugf, O_WRONLY|O_APPEND|O_CREAT, 0644);
+
+ if (!new_dbf) {
+ log_overflow = True;
+ DEBUG(0, ("Unable to open new log file %s: %s\n", debugf, strerror(errno)));
+ log_overflow = False;
+ if (dbf)
+ x_fflush(dbf);
+ ret = False;
+ } else {
+ x_setbuf(new_dbf, NULL);
+ old_dbf = dbf;
+ dbf = new_dbf;
+ if (old_dbf)
+ (void) x_fclose(old_dbf);
+ }
+
+ /* Fix from klausr@ITAP.Physik.Uni-Stuttgart.De
+ * to fix problem where smbd's that generate less
+ * than 100 messages keep growing the log.
+ */
+ force_check_log_size();
+ (void)umask(oldumask);
+
+ /* Take over stderr to catch ouput into logs */
+ if (dbf && sys_dup2(x_fileno(dbf), 2) == -1) {
+ close_low_fds(True); /* Close stderr too, if dup2 can't point it
+ at the logfile */
+ }
+
+ return ret;
+}
+
+/* ************************************************************************** **
+ * Force a check of the log size.
+ * ************************************************************************** **
+ */
+void force_check_log_size( void )
+{
+ debug_count = 100;
+}
+
+/***************************************************************************
+ Check to see if there is any need to check if the logfile has grown too big.
+**************************************************************************/
+
+BOOL need_to_check_log_size( void )
+{
+ int maxlog;
+
+ if( debug_count < 100 )
+ return( False );
+
+ maxlog = lp_max_log_size() * 1024;
+ if( !dbf || maxlog <= 0 ) {
+ debug_count = 0;
+ return(False);
+ }
+ return( True );
+}
+
+/* ************************************************************************** **
+ * Check to see if the log has grown to be too big.
+ * ************************************************************************** **
+ */
+
+void check_log_size( void )
+{
+ int maxlog;
+ SMB_STRUCT_STAT st;
+
+ /*
+ * We need to be root to check/change log-file, skip this and let the main
+ * loop check do a new check as root.
+ */
+
+ if( geteuid() != 0 )
+ return;
+
+ if(log_overflow || !need_to_check_log_size() )
+ return;
+
+ maxlog = lp_max_log_size() * 1024;
+
+ if( sys_fstat( x_fileno( dbf ), &st ) == 0 && st.st_size > maxlog ) {
+ (void)reopen_logs();
+ if( dbf && get_file_size( debugf ) > maxlog ) {
+ pstring name;
+
+ slprintf( name, sizeof(name)-1, "%s.old", debugf );
+ (void)rename( debugf, name );
+
+ if (!reopen_logs()) {
+ /* We failed to reopen a log - continue using the old name. */
+ (void)rename(name, debugf);
+ }
+ }
+ }
+
+ /*
+ * Here's where we need to panic if dbf == NULL..
+ */
+
+ if(dbf == NULL) {
+ /* This code should only be reached in very strange
+ * circumstances. If we merely fail to open the new log we
+ * should stick with the old one. ergo this should only be
+ * reached when opening the logs for the first time: at
+ * startup or when the log level is increased from zero.
+ * -dwg 6 June 2000
+ */
+ dbf = x_fopen( "/dev/console", O_WRONLY, 0);
+ if(dbf) {
+ DEBUG(0,("check_log_size: open of debug file %s failed - using console.\n",
+ debugf ));
+ } else {
+ /*
+ * We cannot continue without a debug file handle.
+ */
+ abort();
+ }
+ }
+ debug_count = 0;
+} /* check_log_size */
+
+/* ************************************************************************** **
+ * Write an debug message on the debugfile.
+ * This is called by dbghdr() and format_debug_text().
+ * ************************************************************************** **
+ */
+ int Debug1( const char *format_str, ... )
+{
+ va_list ap;
+ int old_errno = errno;
+
+ debug_count++;
+
+ if( stdout_logging )
+ {
+ va_start( ap, format_str );
+ if(dbf)
+ (void)x_vfprintf( dbf, format_str, ap );
+ va_end( ap );
+ errno = old_errno;
+ return( 0 );
+ }
+
+#ifdef WITH_SYSLOG
+ if( !lp_syslog_only() )
+#endif
+ {
+ if( !dbf )
+ {
+ mode_t oldumask = umask( 022 );
+
+ dbf = x_fopen( debugf, O_WRONLY|O_APPEND|O_CREAT, 0644 );
+ (void)umask( oldumask );
+ if( dbf )
+ {
+ x_setbuf( dbf, NULL );
+ }
+ else
+ {
+ errno = old_errno;
+ return(0);
+ }
+ }
+ }
+
+#ifdef WITH_SYSLOG
+ if( syslog_level < lp_syslog() )
+ {
+ /* map debug levels to syslog() priorities
+ * note that not all DEBUG(0, ...) calls are
+ * necessarily errors
+ */
+ static int priority_map[] = {
+ LOG_ERR, /* 0 */
+ LOG_WARNING, /* 1 */
+ LOG_NOTICE, /* 2 */
+ LOG_INFO, /* 3 */
+ };
+ int priority;
+ pstring msgbuf;
+
+ if( syslog_level >= ( sizeof(priority_map) / sizeof(priority_map[0]) )
+ || syslog_level < 0)
+ priority = LOG_DEBUG;
+ else
+ priority = priority_map[syslog_level];
+
+ va_start( ap, format_str );
+ vslprintf( msgbuf, sizeof(msgbuf)-1, format_str, ap );
+ va_end( ap );
+
+ msgbuf[255] = '\0';
+ syslog( priority, "%s", msgbuf );
+ }
+#endif
+
+ check_log_size();
+
+#ifdef WITH_SYSLOG
+ if( !lp_syslog_only() )
+#endif
+ {
+ va_start( ap, format_str );
+ if(dbf)
+ (void)x_vfprintf( dbf, format_str, ap );
+ va_end( ap );
+ if(dbf)
+ (void)x_fflush( dbf );
+ }
+
+ errno = old_errno;
+
+ return( 0 );
+ } /* Debug1 */
+
+
+/* ************************************************************************** **
+ * Print the buffer content via Debug1(), then reset the buffer.
+ *
+ * Input: none
+ * Output: none
+ *
+ * ************************************************************************** **
+ */
+static void bufr_print( void )
+ {
+ format_bufr[format_pos] = '\0';
+ (void)Debug1( "%s", format_bufr );
+ format_pos = 0;
+ } /* bufr_print */
+
+/* ************************************************************************** **
+ * Format the debug message text.
+ *
+ * Input: msg - Text to be added to the "current" debug message text.
+ *
+ * Output: none.
+ *
+ * Notes: The purpose of this is two-fold. First, each call to syslog()
+ * (used by Debug1(), see above) generates a new line of syslog
+ * output. This is fixed by storing the partial lines until the
+ * newline character is encountered. Second, printing the debug
+ * message lines when a newline is encountered allows us to add
+ * spaces, thus indenting the body of the message and making it
+ * more readable.
+ *
+ * ************************************************************************** **
+ */
+static void format_debug_text( char *msg )
+ {
+ size_t i;
+ BOOL timestamp = (!stdout_logging && (lp_timestamp_logs() ||
+ !(lp_loaded())));
+
+ for( i = 0; msg[i]; i++ )
+ {
+ /* Indent two spaces at each new line. */
+ if(timestamp && 0 == format_pos)
+ {
+ format_bufr[0] = format_bufr[1] = ' ';
+ format_pos = 2;
+ }
+
+ /* If there's room, copy the character to the format buffer. */
+ if( format_pos < FORMAT_BUFR_MAX )
+ format_bufr[format_pos++] = msg[i];
+
+ /* If a newline is encountered, print & restart. */
+ if( '\n' == msg[i] )
+ bufr_print();
+
+ /* If the buffer is full dump it out, reset it, and put out a line
+ * continuation indicator.
+ */
+ if( format_pos >= FORMAT_BUFR_MAX )
+ {
+ bufr_print();
+ (void)Debug1( " +>\n" );
+ }
+ }
+
+ /* Just to be safe... */
+ format_bufr[format_pos] = '\0';
+ } /* format_debug_text */
+
+/* ************************************************************************** **
+ * Flush debug output, including the format buffer content.
+ *
+ * Input: none
+ * Output: none
+ *
+ * ************************************************************************** **
+ */
+void dbgflush( void )
+ {
+ bufr_print();
+ if(dbf)
+ (void)x_fflush( dbf );
+ } /* dbgflush */
+
+/* ************************************************************************** **
+ * Print a Debug Header.
+ *
+ * Input: level - Debug level of the message (not the system-wide debug
+ * level. )
+ * file - Pointer to a string containing the name of the file
+ * from which this function was called, or an empty string
+ * if the __FILE__ macro is not implemented.
+ * func - Pointer to a string containing the name of the function
+ * from which this function was called, or an empty string
+ * if the __FUNCTION__ macro is not implemented.
+ * line - line number of the call to dbghdr, assuming __LINE__
+ * works.
+ *
+ * Output: Always True. This makes it easy to fudge a call to dbghdr()
+ * in a macro, since the function can be called as part of a test.
+ * Eg: ( (level <= DEBUGLEVEL) && (dbghdr(level,"",line)) )
+ *
+ * Notes: This function takes care of setting syslog_level.
+ *
+ * ************************************************************************** **
+ */
+
+BOOL dbghdr( int level, const char *file, const char *func, int line )
+{
+ /* Ensure we don't lose any real errno value. */
+ int old_errno = errno;
+
+ if( format_pos ) {
+ /* This is a fudge. If there is stuff sitting in the format_bufr, then
+ * the *right* thing to do is to call
+ * format_debug_text( "\n" );
+ * to write the remainder, and then proceed with the new header.
+ * Unfortunately, there are several places in the code at which
+ * the DEBUG() macro is used to build partial lines. That in mind,
+ * we'll work under the assumption that an incomplete line indicates
+ * that a new header is *not* desired.
+ */
+ return( True );
+ }
+
+#ifdef WITH_SYSLOG
+ /* Set syslog_level. */
+ syslog_level = level;
+#endif
+
+ /* Don't print a header if we're logging to stdout. */
+ if( stdout_logging )
+ return( True );
+
+ /* Print the header if timestamps are turned on. If parameters are
+ * not yet loaded, then default to timestamps on.
+ */
+ if( lp_timestamp_logs() || !(lp_loaded()) ) {
+ char header_str[200];
+
+ header_str[0] = '\0';
+
+ if( lp_debug_pid())
+ slprintf(header_str,sizeof(header_str)-1,", pid=%u",(unsigned int)sys_getpid());
+
+ if( lp_debug_uid()) {
+ size_t hs_len = strlen(header_str);
+ slprintf(header_str + hs_len,
+ sizeof(header_str) - 1 - hs_len,
+ ", effective(%u, %u), real(%u, %u)",
+ (unsigned int)geteuid(), (unsigned int)getegid(),
+ (unsigned int)getuid(), (unsigned int)getgid());
+ }
+
+ /* Print it all out at once to prevent split syslog output. */
+ (void)Debug1( "[%s, %d%s] %s:%s(%d)\n",
+ timestring(lp_debug_hires_timestamp()), level,
+ header_str, file, func, line );
+ }
+
+ errno = old_errno;
+ return( True );
+}
+
+/* ************************************************************************** **
+ * Add text to the body of the "current" debug message via the format buffer.
+ *
+ * Input: format_str - Format string, as used in printf(), et. al.
+ * ... - Variable argument list.
+ *
+ * ..or.. va_alist - Old style variable parameter list starting point.
+ *
+ * Output: Always True. See dbghdr() for more info, though this is not
+ * likely to be used in the same way.
+ *
+ * ************************************************************************** **
+ */
+ BOOL dbgtext( const char *format_str, ... )
+ {
+ va_list ap;
+ pstring msgbuf;
+
+ va_start( ap, format_str );
+ vslprintf( msgbuf, sizeof(msgbuf)-1, format_str, ap );
+ va_end( ap );
+
+ format_debug_text( msgbuf );
+
+ return( True );
+ } /* dbgtext */
+
+
+/* ************************************************************************** */
diff --git a/source/lib/dmallocmsg.c b/source/lib/dmallocmsg.c
new file mode 100644
index 00000000000..a83ed518d7c
--- /dev/null
+++ b/source/lib/dmallocmsg.c
@@ -0,0 +1,72 @@
+/*
+ samba -- Unix SMB/CIFS implementation.
+ Copyright (C) 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 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"
+
+/**
+ * @file dmallocmsg.c
+ *
+ * Glue code to cause dmalloc info to come out when we receive a prod
+ * over samba messaging.
+ **/
+
+#ifdef ENABLE_DMALLOC
+static unsigned long our_dm_mark = 0;
+#endif /* ENABLE_DMALLOC */
+
+
+/**
+ * Respond to a POOL_USAGE message by sending back string form of memory
+ * usage stats.
+ **/
+static void msg_req_dmalloc_mark(int UNUSED(msg_type), pid_t UNUSED(src_pid),
+ void *UNUSED(buf), size_t UNUSED(len))
+{
+#ifdef ENABLE_DMALLOC
+ our_dm_mark = dmalloc_mark();
+ DEBUG(2,("Got MSG_REQ_DMALLOC_MARK: mark set\n"));
+#else
+ DEBUG(2,("Got MSG_REQ_DMALLOC_MARK but dmalloc not in this process\n"));
+#endif
+}
+
+
+
+static void msg_req_dmalloc_log_changed(int UNUSED(msg_type),
+ pid_t UNUSED(src_pid),
+ void *UNUSED(buf), size_t UNUSED(len))
+{
+#ifdef ENABLE_DMALLOC
+ dmalloc_log_changed(our_dm_mark, True, True, True);
+ DEBUG(2,("Got MSG_REQ_DMALLOC_LOG_CHANGED: done\n"));
+#else
+ DEBUG(2,("Got MSG_REQ_DMALLOC_LOG_CHANGED but dmalloc not in this process\n"));
+#endif
+}
+
+
+/**
+ * Register handler for MSG_REQ_POOL_USAGE
+ **/
+void register_dmalloc_msgs(void)
+{
+ message_register(MSG_REQ_DMALLOC_MARK, msg_req_dmalloc_mark);
+ message_register(MSG_REQ_DMALLOC_LOG_CHANGED, msg_req_dmalloc_log_changed);
+ DEBUG(2, ("Registered MSG_REQ_DMALLOC_MARK and LOG_CHANGED\n"));
+}
diff --git a/source/lib/dprintf.c b/source/lib/dprintf.c
new file mode 100644
index 00000000000..c62a1f41d10
--- /dev/null
+++ b/source/lib/dprintf.c
@@ -0,0 +1,113 @@
+/*
+ Unix SMB/CIFS implementation.
+ display print 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 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.
+*/
+
+
+/*
+ this module provides functions for printing internal strings in the "display charset"
+ This charset may be quite different from the chosen unix charset
+
+ Eventually these functions will need to take care of column count constraints
+
+ The d_ prefix on print functions in Samba refers to the display character set
+ conversion
+*/
+
+#include "includes.h"
+
+ int d_vfprintf(FILE *f, const char *format, va_list ap)
+{
+ char *p, *p2;
+ int ret, maxlen, clen;
+ const char *msgstr;
+ va_list ap2;
+
+ /* do any message translations */
+ msgstr = lang_msg(format);
+ if (!msgstr) return -1;
+
+ VA_COPY(ap2, ap);
+
+ ret = vasprintf(&p, msgstr, ap2);
+
+ lang_msg_free(msgstr);
+
+ if (ret <= 0) return ret;
+
+ /* now we have the string in unix format, convert it to the display
+ charset, but beware of it growing */
+ maxlen = ret*2;
+again:
+ p2 = malloc(maxlen);
+ if (!p2) {
+ SAFE_FREE(p);
+ return -1;
+ }
+ clen = convert_string(CH_UNIX, CH_DISPLAY, p, ret, p2, maxlen, True);
+
+ if (clen >= maxlen) {
+ /* it didn't fit - try a larger buffer */
+ maxlen *= 2;
+ SAFE_FREE(p2);
+ goto again;
+ }
+
+ /* good, its converted OK */
+ SAFE_FREE(p);
+ ret = fwrite(p2, 1, clen, f);
+ SAFE_FREE(p2);
+
+ return ret;
+}
+
+
+ int d_fprintf(FILE *f, const char *format, ...)
+{
+ int ret;
+ va_list ap;
+
+ va_start(ap, format);
+ ret = d_vfprintf(f, format, ap);
+ va_end(ap);
+
+ return ret;
+}
+
+static FILE *outfile;
+
+ int d_printf(const char *format, ...)
+{
+ int ret;
+ va_list ap;
+
+ if (!outfile) outfile = stdout;
+
+ va_start(ap, format);
+ ret = d_vfprintf(outfile, format, ap);
+ va_end(ap);
+
+ return ret;
+}
+
+/* interactive programs need a way of tell d_*() to write to stderr instead
+ of stdout */
+void display_set_stderr(void)
+{
+ outfile = stderr;
+}
diff --git a/source/lib/dummyroot.c b/source/lib/dummyroot.c
new file mode 100644
index 00000000000..c8465cb791a
--- /dev/null
+++ b/source/lib/dummyroot.c
@@ -0,0 +1,33 @@
+/*
+ Unix SMB/CIFS implementation.
+ RPC pipe client
+
+ 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 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.
+*/
+
+/* Stupid dummy functions required due to the horrible dependency mess
+ in Samba. */
+
+void become_root(void)
+{
+ return;
+}
+
+void unbecome_root(void)
+{
+ return;
+}
diff --git a/source/lib/dummysmbd.c b/source/lib/dummysmbd.c
new file mode 100644
index 00000000000..17bc3217743
--- /dev/null
+++ b/source/lib/dummysmbd.c
@@ -0,0 +1,29 @@
+/*
+ Unix SMB/CIFS implementation.
+ RPC pipe client
+
+ Copyright (C) Gerald (Jerry) Carter 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 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.
+*/
+
+/* Stupid dummy functions required due to the horrible dependency mess
+ in Samba. */
+
+void decrement_smbd_process_count( void )
+{
+ return;
+}
+
diff --git a/source/lib/fault.c b/source/lib/fault.c
new file mode 100644
index 00000000000..d8364ff2257
--- /dev/null
+++ b/source/lib/fault.c
@@ -0,0 +1,83 @@
+/*
+ Unix SMB/CIFS implementation.
+ Critical Fault handling
+ 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 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"
+
+static void (*cont_fn)(void *);
+
+/*******************************************************************
+report a fault
+********************************************************************/
+static void fault_report(int sig)
+{
+ static int counter;
+
+ if (counter) _exit(1);
+
+ counter++;
+
+ DEBUG(0,("===============================================================\n"));
+ DEBUG(0,("INTERNAL ERROR: Signal %d in pid %d (%s)",sig,(int)sys_getpid(),SAMBA_VERSION_STRING));
+ DEBUG(0,("\nPlease read the appendix Bugs of the Samba HOWTO collection\n"));
+ DEBUG(0,("===============================================================\n"));
+
+ smb_panic("internal error");
+
+ if (cont_fn) {
+ cont_fn(NULL);
+#ifdef SIGSEGV
+ CatchSignal(SIGSEGV,SIGNAL_CAST SIG_DFL);
+#endif
+#ifdef SIGBUS
+ CatchSignal(SIGBUS,SIGNAL_CAST SIG_DFL);
+#endif
+#ifdef SIGABRT
+ CatchSignal(SIGABRT,SIGNAL_CAST SIG_DFL);
+#endif
+ return; /* this should cause a core dump */
+ }
+ exit(1);
+}
+
+/****************************************************************************
+catch serious errors
+****************************************************************************/
+static void sig_fault(int sig)
+{
+ fault_report(sig);
+}
+
+/*******************************************************************
+setup our fault handlers
+********************************************************************/
+void fault_setup(void (*fn)(void *))
+{
+ cont_fn = fn;
+
+#ifdef SIGSEGV
+ CatchSignal(SIGSEGV,SIGNAL_CAST sig_fault);
+#endif
+#ifdef SIGBUS
+ CatchSignal(SIGBUS,SIGNAL_CAST sig_fault);
+#endif
+#ifdef SIGABRT
+ CatchSignal(SIGABRT,SIGNAL_CAST sig_fault);
+#endif
+}
diff --git a/source/lib/fsusage.c b/source/lib/fsusage.c
new file mode 100644
index 00000000000..bb7cff06453
--- /dev/null
+++ b/source/lib/fsusage.c
@@ -0,0 +1,148 @@
+/*
+ Unix SMB/CIFS implementation.
+ functions to calculate the free disk space
+ Copyright (C) Andrew Tridgell 1998-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 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"
+
+
+/* Return the number of TOSIZE-byte blocks used by
+ BLOCKS FROMSIZE-byte blocks, rounding away from zero.
+*/
+static SMB_BIG_UINT adjust_blocks(SMB_BIG_UINT blocks, SMB_BIG_UINT fromsize, SMB_BIG_UINT tosize)
+{
+ if (fromsize == tosize) /* e.g., from 512 to 512 */
+ return blocks;
+ else if (fromsize > tosize) /* e.g., from 2048 to 512 */
+ return blocks * (fromsize / tosize);
+ else /* e.g., from 256 to 512 */
+ return (blocks + 1) / (tosize / fromsize);
+}
+
+/* this does all of the system specific guff to get the free disk space.
+ It is derived from code in the GNU fileutils package, but has been
+ considerably mangled for use here
+
+ results are returned in *dfree and *dsize, in 512 byte units
+*/
+int sys_fsusage(const char *path, SMB_BIG_UINT *dfree, SMB_BIG_UINT *dsize)
+{
+#ifdef STAT_STATFS3_OSF1
+#define CONVERT_BLOCKS(B) adjust_blocks ((SMB_BIG_UINT)(B), (SMB_BIG_UINT)fsd.f_fsize, (SMB_BIG_UINT)512)
+ struct statfs fsd;
+
+ if (statfs (path, &fsd, sizeof (struct statfs)) != 0)
+ return -1;
+#endif /* STAT_STATFS3_OSF1 */
+
+#ifdef STAT_STATFS2_FS_DATA /* Ultrix */
+#define CONVERT_BLOCKS(B) adjust_blocks ((SMB_BIG_UINT)(B), (SMB_BIG_UINT)1024, (SMB_BIG_UINT)512)
+ struct fs_data fsd;
+
+ if (statfs (path, &fsd) != 1)
+ return -1;
+
+ (*dsize) = CONVERT_BLOCKS (fsd.fd_req.btot);
+ (*dfree) = CONVERT_BLOCKS (fsd.fd_req.bfreen);
+#endif /* STAT_STATFS2_FS_DATA */
+
+#ifdef STAT_STATFS2_BSIZE /* 4.3BSD, SunOS 4, HP-UX, AIX */
+#define CONVERT_BLOCKS(B) adjust_blocks ((SMB_BIG_UINT)(B), (SMB_BIG_UINT)fsd.f_bsize, (SMB_BIG_UINT)512)
+ struct statfs fsd;
+
+ if (statfs (path, &fsd) < 0)
+ return -1;
+
+#ifdef STATFS_TRUNCATES_BLOCK_COUNTS
+ /* In SunOS 4.1.2, 4.1.3, and 4.1.3_U1, the block counts in the
+ struct statfs are truncated to 2GB. These conditions detect that
+ truncation, presumably without botching the 4.1.1 case, in which
+ the values are not truncated. The correct counts are stored in
+ undocumented spare fields. */
+ if (fsd.f_blocks == 0x1fffff && fsd.f_spare[0] > 0) {
+ fsd.f_blocks = fsd.f_spare[0];
+ fsd.f_bfree = fsd.f_spare[1];
+ fsd.f_bavail = fsd.f_spare[2];
+ }
+#endif /* STATFS_TRUNCATES_BLOCK_COUNTS */
+#endif /* STAT_STATFS2_BSIZE */
+
+
+#ifdef STAT_STATFS2_FSIZE /* 4.4BSD */
+#define CONVERT_BLOCKS(B) adjust_blocks ((SMB_BIG_UINT)(B), (SMB_BIG_UINT)fsd.f_fsize, (SMB_BIG_UINT)512)
+
+ struct statfs fsd;
+
+ if (statfs (path, &fsd) < 0)
+ return -1;
+#endif /* STAT_STATFS2_FSIZE */
+
+#ifdef STAT_STATFS4 /* SVR3, Dynix, Irix, AIX */
+# if _AIX || defined(_CRAY)
+# define CONVERT_BLOCKS(B) adjust_blocks ((SMB_BIG_UINT)(B), (SMB_BIG_UINT)fsd.f_bsize, (SMB_BIG_UINT)512)
+# ifdef _CRAY
+# define f_bavail f_bfree
+# endif
+# else
+# define CONVERT_BLOCKS(B) ((SMB_BIG_UINT)B)
+# ifndef _SEQUENT_ /* _SEQUENT_ is DYNIX/ptx */
+# ifndef DOLPHIN /* DOLPHIN 3.8.alfa/7.18 has f_bavail */
+# define f_bavail f_bfree
+# endif
+# endif
+# endif
+
+ struct statfs fsd;
+
+ if (statfs (path, &fsd, sizeof fsd, 0) < 0)
+ return -1;
+ /* Empirically, the block counts on most SVR3 and SVR3-derived
+ systems seem to always be in terms of 512-byte blocks,
+ no matter what value f_bsize has. */
+
+#endif /* STAT_STATFS4 */
+
+#if defined(STAT_STATVFS) || defined(STAT_STATVFS64) /* SVR4 */
+# define CONVERT_BLOCKS(B) \
+ adjust_blocks ((SMB_BIG_UINT)(B), fsd.f_frsize ? (SMB_BIG_UINT)fsd.f_frsize : (SMB_BIG_UINT)fsd.f_bsize, (SMB_BIG_UINT)512)
+
+#ifdef STAT_STATVFS64
+ struct statvfs64 fsd;
+ if (statvfs64(path, &fsd) < 0) return -1;
+#else
+ struct statvfs fsd;
+ if (statvfs(path, &fsd) < 0) return -1;
+#endif
+
+ /* f_frsize isn't guaranteed to be supported. */
+
+#endif /* STAT_STATVFS */
+
+#ifndef CONVERT_BLOCKS
+ /* we don't have any dfree code! */
+ return -1;
+#else
+#if !defined(STAT_STATFS2_FS_DATA)
+ /* !Ultrix */
+ (*dsize) = CONVERT_BLOCKS (fsd.f_blocks);
+ (*dfree) = CONVERT_BLOCKS (fsd.f_bavail);
+#endif /* not STAT_STATFS2_FS_DATA */
+#endif
+
+ return 0;
+}
diff --git a/source/lib/gencache.c b/source/lib/gencache.c
new file mode 100644
index 00000000000..39e727c24fa
--- /dev/null
+++ b/source/lib/gencache.c
@@ -0,0 +1,379 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ Generic, persistent and shared between processes cache mechanism for use
+ by various parts of the Samba code
+
+ 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 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"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_TDB
+
+#define TIMEOUT_LEN 12
+#define CACHE_DATA_FMT "%12u/%s"
+
+static TDB_CONTEXT *cache;
+
+/**
+ * @file gencache.c
+ * @brief Generic, persistent and shared between processes cache mechanism
+ * for use by various parts of the Samba code
+ *
+ **/
+
+
+/**
+ * Cache initialisation function. Opens cache tdb file or creates
+ * it if does not exist.
+ *
+ * @return true on successful initialisation of the cache or
+ * false on failure
+ **/
+
+BOOL gencache_init(void)
+{
+ char* cache_fname = NULL;
+
+ /* skip file open if it's already opened */
+ if (cache) return True;
+
+ asprintf(&cache_fname, "%s/%s", lp_lockdir(), "gencache.tdb");
+ if (cache_fname)
+ DEBUG(5, ("Opening cache file at %s\n", cache_fname));
+ else {
+ DEBUG(0, ("Filename allocation failed.\n"));
+ return False;
+ }
+
+ cache = tdb_open_log(cache_fname, 0, TDB_DEFAULT,
+ O_RDWR|O_CREAT, 0644);
+
+ SAFE_FREE(cache_fname);
+ if (!cache) {
+ DEBUG(5, ("Attempt to open gencache.tdb has failed.\n"));
+ return False;
+ }
+ return True;
+}
+
+
+/**
+ * Cache shutdown function. Closes opened cache tdb file.
+ *
+ * @return true on successful closing the cache or
+ * false on failure during cache shutdown
+ **/
+
+BOOL gencache_shutdown(void)
+{
+ /* tdb_close routine returns -1 on error */
+ if (!cache) return False;
+ DEBUG(5, ("Closing cache file\n"));
+ return tdb_close(cache) != -1;
+}
+
+
+/**
+ * Set an entry in the cache file. If there's no such
+ * one, then add it.
+ *
+ * @param keystr string that represents a key of this entry
+ * @param value text representation value being cached
+ * @param timeout time when the value is expired
+ *
+ * @retval true when entry is successfuly stored
+ * @retval false on failure
+ **/
+
+BOOL gencache_set(const char *keystr, const char *value, time_t timeout)
+{
+ int ret;
+ TDB_DATA keybuf, databuf;
+ char* valstr = NULL;
+
+ /* fail completely if get null pointers passed */
+ SMB_ASSERT(keystr && value);
+
+ if (!gencache_init()) return False;
+
+ asprintf(&valstr, CACHE_DATA_FMT, (int)timeout, value);
+ if (!valstr)
+ return False;
+
+ keybuf.dptr = strdup(keystr);
+ keybuf.dsize = strlen(keystr)+1;
+ databuf.dptr = strdup(valstr);
+ databuf.dsize = strlen(valstr)+1;
+ DEBUG(10, ("Adding cache entry with key = %s; value = %s and timeout ="
+ " %s (%d seconds %s)\n", keybuf.dptr, value,ctime(&timeout),
+ (int)(timeout - time(NULL)),
+ timeout > time(NULL) ? "ahead" : "in the past"));
+
+ ret = tdb_store(cache, keybuf, databuf, 0);
+ SAFE_FREE(valstr);
+ SAFE_FREE(keybuf.dptr);
+ SAFE_FREE(databuf.dptr);
+
+ return ret == 0;
+}
+
+
+/**
+ * Set existing entry to the cache file.
+ *
+ * @param keystr string that represents a key of this entry
+ * @param valstr text representation value being cached
+ * @param timeout time when the value is expired
+ *
+ * @retval true when entry is successfuly set
+ * @retval false on failure
+ **/
+
+BOOL gencache_set_only(const char *keystr, const char *valstr, time_t timeout)
+{
+ int ret = -1;
+ TDB_DATA keybuf, databuf;
+ char *old_valstr, *datastr;
+ time_t old_timeout;
+
+ /* fail completely if get null pointers passed */
+ SMB_ASSERT(keystr && valstr);
+
+ if (!gencache_init()) return False;
+
+ /*
+ * Check whether entry exists in the cache
+ * Don't verify gencache_get exit code, since the entry may be expired
+ */
+ gencache_get(keystr, &old_valstr, &old_timeout);
+
+ if (!(old_valstr && old_timeout)) return False;
+
+ DEBUG(10, ("Setting cache entry with key = %s; old value = %s and old timeout \
+ = %s\n", keystr, old_valstr, ctime(&old_timeout)));
+
+ asprintf(&datastr, CACHE_DATA_FMT, (int)timeout, valstr);
+ keybuf.dptr = strdup(keystr);
+ keybuf.dsize = strlen(keystr)+1;
+ databuf.dptr = strdup(datastr);
+ databuf.dsize = strlen(datastr)+1;
+ DEBUGADD(10, ("New value = %s, new timeout = %s (%d seconds %s)", valstr,
+ ctime(&timeout), (int)(timeout - time(NULL)),
+ timeout > time(NULL) ? "ahead" : "in the past"));
+
+
+ ret = tdb_store(cache, keybuf, databuf, TDB_REPLACE);
+
+ SAFE_FREE(datastr);
+ SAFE_FREE(old_valstr);
+ SAFE_FREE(keybuf.dptr);
+ SAFE_FREE(databuf.dptr);
+
+ return ret == 0;
+}
+
+
+/**
+ * Delete one entry from the cache file.
+ *
+ * @param keystr string that represents a key of this entry
+ *
+ * @retval true upon successful deletion
+ * @retval false in case of failure
+ **/
+
+BOOL gencache_del(const char *keystr)
+{
+ int ret;
+ TDB_DATA keybuf;
+
+ /* fail completely if get null pointers passed */
+ SMB_ASSERT(keystr);
+
+ if (!gencache_init()) return False;
+
+ keybuf.dptr = strdup(keystr);
+ keybuf.dsize = strlen(keystr)+1;
+ DEBUG(10, ("Deleting cache entry (key = %s)\n", keystr));
+ ret = tdb_delete(cache, keybuf);
+
+ SAFE_FREE(keybuf.dptr);
+ return ret == 0;
+}
+
+
+/**
+ * Get existing entry from the cache file.
+ *
+ * @param keystr string that represents a key of this entry
+ * @param valstr buffer that is allocated and filled with the entry value
+ * buffer's disposing must be done outside
+ * @param timeout pointer to a time_t that is filled with entry's
+ * timeout
+ *
+ * @retval true when entry is successfuly fetched
+ * @retval False for failure
+ **/
+
+BOOL gencache_get(const char *keystr, char **valstr, time_t *timeout)
+{
+ TDB_DATA keybuf, databuf;
+
+ /* fail completely if get null pointers passed */
+ SMB_ASSERT(keystr);
+
+ if (!gencache_init())
+ return False;
+
+ keybuf.dptr = strdup(keystr);
+ keybuf.dsize = strlen(keystr)+1;
+ databuf = tdb_fetch(cache, keybuf);
+ SAFE_FREE(keybuf.dptr);
+
+ if (databuf.dptr && databuf.dsize > TIMEOUT_LEN) {
+ char* entry_buf = strndup(databuf.dptr, databuf.dsize);
+ char *v;
+ time_t t;
+
+ v = (char*)malloc(sizeof(char) *
+ (databuf.dsize - TIMEOUT_LEN));
+
+ SAFE_FREE(databuf.dptr);
+ sscanf(entry_buf, CACHE_DATA_FMT, (int*)&t, v);
+ SAFE_FREE(entry_buf);
+
+ DEBUG(10, ("Returning %s cache entry: key = %s, value = %s, "
+ "timeout = %s\n", t > time(NULL) ? "valid" :
+ "expired", keystr, v, ctime(&t)));
+
+ if (valstr)
+ *valstr = v;
+ else
+ SAFE_FREE(v);
+
+ if (timeout)
+ *timeout = t;
+
+ return t > time(NULL);
+
+ } else {
+ SAFE_FREE(databuf.dptr);
+
+ if (valstr)
+ *valstr = NULL;
+
+ if (timeout)
+ timeout = NULL;
+
+ DEBUG(10, ("Cache entry with key = %s couldn't be found\n",
+ keystr));
+
+ return False;
+ }
+}
+
+
+/**
+ * Iterate through all entries which key matches to specified pattern
+ *
+ * @param fn pointer to the function that will be supplied with each single
+ * matching cache entry (key, value and timeout) as an arguments
+ * @param data void pointer to an arbitrary data that is passed directly to the fn
+ * function on each call
+ * @param keystr_pattern pattern the existing entries' keys are matched to
+ *
+ **/
+
+void gencache_iterate(void (*fn)(const char* key, const char *value, time_t timeout, void* dptr),
+ void* data, const char* keystr_pattern)
+{
+ TDB_LIST_NODE *node, *first_node;
+ TDB_DATA databuf;
+ char *keystr = NULL, *valstr = NULL, *entry = NULL;
+ time_t timeout = 0;
+
+ /* fail completely if get null pointers passed */
+ SMB_ASSERT(fn && keystr_pattern);
+
+ if (!gencache_init()) return;
+
+ DEBUG(5, ("Searching cache keys with pattern %s\n", keystr_pattern));
+ node = tdb_search_keys(cache, keystr_pattern);
+ first_node = node;
+
+ while (node) {
+ /* ensure null termination of the key string */
+ keystr = strndup(node->node_key.dptr, node->node_key.dsize);
+
+ /*
+ * We don't use gencache_get function, because we need to iterate through
+ * all of the entries. Validity verification is up to fn routine.
+ */
+ databuf = tdb_fetch(cache, node->node_key);
+ if (!databuf.dptr || databuf.dsize <= TIMEOUT_LEN) {
+ SAFE_FREE(databuf.dptr);
+ SAFE_FREE(keystr);
+ node = node->next;
+ continue;
+ }
+ entry = strndup(databuf.dptr, databuf.dsize);
+ SAFE_FREE(databuf.dptr);
+ valstr = (char*)malloc(sizeof(char) * (databuf.dsize - TIMEOUT_LEN));
+ sscanf(entry, CACHE_DATA_FMT, (int*)(&timeout), valstr);
+
+ DEBUG(10, ("Calling function with arguments (key = %s, value = %s, timeout = %s)\n",
+ keystr, valstr, ctime(&timeout)));
+ fn(keystr, valstr, timeout, data);
+
+ SAFE_FREE(valstr);
+ SAFE_FREE(entry);
+ SAFE_FREE(keystr);
+ node = node->next;
+ }
+
+ tdb_search_list_free(first_node);
+}
+
+/********************************************************************
+ lock a key
+********************************************************************/
+
+int gencache_lock_entry( const char *key )
+{
+ if (!gencache_init())
+ return -1;
+
+ return tdb_lock_bystring(cache, key, 0);
+}
+
+/********************************************************************
+ unlock a key
+********************************************************************/
+
+void gencache_unlock_entry( const char *key )
+{
+ if (!gencache_init())
+ return;
+
+ tdb_unlock_bystring(cache, key);
+ return;
+}
+
+
diff --git a/source/lib/genparser.c b/source/lib/genparser.c
new file mode 100644
index 00000000000..7476b5d0aff
--- /dev/null
+++ b/source/lib/genparser.c
@@ -0,0 +1,783 @@
+/*
+ Copyright (C) Andrew Tridgell <genstruct@tridgell.net> 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 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.
+*/
+
+/*
+ automatic marshalling/unmarshalling system for C structures
+*/
+
+#include "includes.h"
+
+/* see if a range of memory is all zero. Used to prevent dumping of zero elements */
+static int all_zero(const char *ptr, unsigned size)
+{
+ int i;
+ if (!ptr) return 1;
+ for (i=0;i<size;i++) {
+ if (ptr[i]) return 0;
+ }
+ return 1;
+}
+
+/* encode a buffer of bytes into a escaped string */
+static char *encode_bytes(TALLOC_CTX *mem_ctx, const char *ptr, unsigned len)
+{
+ const char *hexdig = "0123456789abcdef";
+ char *ret, *p;
+ unsigned i;
+ ret = talloc(mem_ctx, len*3 + 1); /* worst case size */
+ if (!ret) return NULL;
+ for (p=ret,i=0;i<len;i++) {
+ if (isalnum(ptr[i]) || isspace(ptr[i]) ||
+ (ispunct(ptr[i]) && !strchr("\\{}", ptr[i]))) {
+ *p++ = ptr[i];
+ } else {
+ unsigned char c = *(unsigned char *)(ptr+i);
+ if (c == 0 && all_zero(ptr+i, len-i)) break;
+ p[0] = '\\';
+ p[1] = hexdig[c>>4];
+ p[2] = hexdig[c&0xF];
+ p += 3;
+ }
+ }
+
+ *p = 0;
+
+ return ret;
+}
+
+/* decode an escaped string from encode_bytes() into a buffer */
+static char *decode_bytes(TALLOC_CTX *mem_ctx, const char *s, unsigned *len)
+{
+ char *ret, *p;
+ unsigned i;
+ int slen = strlen(s) + 1;
+
+ ret = talloc(mem_ctx, slen); /* worst case length */
+ if (!ret)
+ return NULL;
+ memset(ret, 0, slen);
+
+ if (*s == '{') s++;
+
+ for (p=ret,i=0;s[i];i++) {
+ if (s[i] == '}') {
+ break;
+ } else if (s[i] == '\\') {
+ unsigned v;
+ if (sscanf(&s[i+1], "%02x", &v) != 1 || v > 255) {
+ return NULL;
+ }
+ *(unsigned char *)p = v;
+ p++;
+ i += 2;
+ } else {
+ *p++ = s[i];
+ }
+ }
+ *p = 0;
+
+ (*len) = (unsigned)(p - ret);
+
+ return ret;
+}
+
+/* the add*() functions deal with adding things to a struct
+ parse_string */
+
+/* allocate more space if needed */
+static int addgen_alloc(TALLOC_CTX *mem_ctx, struct parse_string *p, int n)
+{
+ if (p->length + n <= p->allocated) return 0;
+ p->allocated = p->length + n + 200;
+ p->s = talloc_realloc(mem_ctx, p->s, p->allocated);
+ if (!p->s) {
+ errno = ENOMEM;
+ return -1;
+ }
+ return 0;
+}
+
+/* add a character to the buffer */
+static int addchar(TALLOC_CTX *mem_ctx, struct parse_string *p, char c)
+{
+ if (addgen_alloc(mem_ctx, p, 2) != 0) {
+ return -1;
+ }
+ p->s[p->length++] = c;
+ p->s[p->length] = 0;
+ return 0;
+}
+
+/* add a string to the buffer */
+int addstr(TALLOC_CTX *mem_ctx, struct parse_string *p, const char *s)
+{
+ int len = strlen(s);
+ if (addgen_alloc(mem_ctx, p, len+1) != 0) {
+ return -1;
+ }
+ memcpy(p->s + p->length, s, len+1);
+ p->length += len;
+ return 0;
+}
+
+/* add a string to the buffer with a tab prefix */
+static int addtabbed(TALLOC_CTX *mem_ctx, struct parse_string *p, const char *s, unsigned indent)
+{
+ int len = strlen(s);
+ if (addgen_alloc(mem_ctx, p, indent+len+1) != 0) {
+ return -1;
+ }
+ while (indent--) {
+ p->s[p->length++] = '\t';
+ }
+ memcpy(p->s + p->length, s, len+1);
+ p->length += len;
+ return 0;
+}
+
+/* note! this can only be used for results up to 60 chars wide! */
+int addshort(TALLOC_CTX *mem_ctx, struct parse_string *p, const char *fmt, ...)
+{
+ char buf[60];
+ int n;
+ va_list ap;
+ va_start(ap, fmt);
+ n = vsnprintf(buf, sizeof(buf), fmt, ap);
+ va_end(ap);
+ if (addgen_alloc(mem_ctx, p, n + 1) != 0) {
+ return -1;
+ }
+ if (n != 0) {
+ memcpy(p->s + p->length, buf, n);
+ }
+ p->length += n;
+ p->s[p->length] = 0;
+ return 0;
+}
+
+/*
+ this is here to make it easier for people to write dump functions
+ for their own types
+ */
+int gen_addgen(TALLOC_CTX *mem_ctx, struct parse_string *p, const char *fmt, ...)
+{
+ char *buf = NULL;
+ int n;
+ va_list ap;
+ va_start(ap, fmt);
+ n = vasprintf(&buf, fmt, ap);
+ va_end(ap);
+ if (addgen_alloc(mem_ctx, p, n + 1) != 0) {
+ if (buf) free(buf);
+ return -1;
+ }
+ if (n != 0) {
+ memcpy(p->s + p->length, buf, n);
+ }
+ p->length += n;
+ p->s[p->length] = 0;
+ if (buf) free(buf);
+ return 0;
+}
+
+/* dump a enumerated type */
+int gen_dump_enum(TALLOC_CTX *mem_ctx,
+ const struct enum_struct *einfo,
+ struct parse_string *p,
+ const char *ptr,
+ unsigned indent)
+{
+ unsigned v = *(unsigned *)ptr;
+ int i;
+ for (i=0;einfo[i].name;i++) {
+ if (v == einfo[i].value) {
+ addstr(mem_ctx, p, einfo[i].name);
+ return 0;
+ }
+ }
+ /* hmm, maybe we should just fail? */
+ return gen_dump_unsigned(mem_ctx, p, ptr, indent);
+}
+
+/* dump a single non-array element, hanlding struct and enum */
+static int gen_dump_one(TALLOC_CTX *mem_ctx,
+ struct parse_string *p,
+ const struct parse_struct *pinfo,
+ const char *ptr,
+ unsigned indent)
+{
+ if (pinfo->dump_fn == gen_dump_char && pinfo->ptr_count == 1) {
+ char *s = encode_bytes(mem_ctx, ptr, strlen(ptr));
+ if (addchar(mem_ctx, p,'{') ||
+ addstr(mem_ctx, p, s) ||
+ addstr(mem_ctx, p, "}")) {
+ return -1;
+ }
+ return 0;
+ }
+
+ return pinfo->dump_fn(mem_ctx, p, ptr, indent);
+}
+
+/* handle dumping of an array of arbitrary type */
+static int gen_dump_array(TALLOC_CTX *mem_ctx,
+ struct parse_string *p,
+ const struct parse_struct *pinfo,
+ const char *ptr,
+ int array_len,
+ int indent)
+{
+ int i, count=0;
+
+ /* special handling of fixed length strings */
+ if (array_len != 0 &&
+ pinfo->ptr_count == 0 &&
+ pinfo->dump_fn == gen_dump_char) {
+ char *s = encode_bytes(mem_ctx, ptr, array_len);
+ if (!s) return -1;
+ if (addtabbed(mem_ctx, p, pinfo->name, indent) ||
+ addstr(mem_ctx, p, " = {") ||
+ addstr(mem_ctx, p, s) ||
+ addstr(mem_ctx, p, "}\n")) {
+ return -1;
+ }
+ return 0;
+ }
+
+ for (i=0;i<array_len;i++) {
+ const char *p2 = ptr;
+ unsigned size = pinfo->size;
+
+ /* generic pointer dereference */
+ if (pinfo->ptr_count) {
+ p2 = *(const char **)ptr;
+ size = sizeof(void *);
+ }
+
+ if ((count || pinfo->ptr_count) &&
+ !(pinfo->flags & FLAG_ALWAYS) &&
+ all_zero(ptr, size)) {
+ ptr += size;
+ continue;
+ }
+ if (count == 0) {
+ if (addtabbed(mem_ctx, p, pinfo->name, indent) ||
+ addshort(mem_ctx, p, " = %u:", i)) {
+ return -1;
+ }
+ } else {
+ if (addshort(mem_ctx, p, ", %u:", i) != 0) {
+ return -1;
+ }
+ }
+ if (gen_dump_one(mem_ctx, p, pinfo, p2, indent) != 0) {
+ return -1;
+ }
+ ptr += size;
+ count++;
+ }
+ if (count) {
+ return addstr(mem_ctx, p, "\n");
+ }
+ return 0;
+}
+
+/* find a variable by name in a loaded structure and return its value
+ as an integer. Used to support dynamic arrays */
+static int find_var(const struct parse_struct *pinfo,
+ const char *data,
+ const char *var)
+{
+ int i;
+ const char *ptr;
+
+ /* this allows for constant lengths */
+ if (isdigit(*var)) {
+ return atoi(var);
+ }
+
+ for (i=0;pinfo[i].name;i++) {
+ if (strcmp(pinfo[i].name, var) == 0) break;
+ }
+ if (!pinfo[i].name) return -1;
+
+ ptr = data + pinfo[i].offset;
+
+ switch (pinfo[i].size) {
+ case sizeof(int):
+ return *(int *)ptr;
+ case sizeof(char):
+ return *(char *)ptr;
+ }
+
+ return -1;
+}
+
+
+int gen_dump_struct(TALLOC_CTX *mem_ctx,
+ const struct parse_struct *pinfo,
+ struct parse_string *p,
+ const char *ptr,
+ unsigned indent)
+{
+ char *s = gen_dump(mem_ctx, pinfo, ptr, indent+1);
+ if (!s) return -1;
+ if (addstr(mem_ctx, p, "{\n") ||
+ addstr(mem_ctx, p, s) ||
+ addtabbed(mem_ctx, p, "}", indent)) {
+ return -1;
+ }
+ return 0;
+}
+
+static int gen_dump_string(TALLOC_CTX *mem_ctx,
+ struct parse_string *p,
+ const struct parse_struct *pinfo,
+ const char *data,
+ unsigned indent)
+{
+ const char *ptr = *(char **)data;
+ char *s = encode_bytes(mem_ctx, ptr, strlen(ptr));
+ if (addtabbed(mem_ctx, p, pinfo->name, indent) ||
+ addstr(mem_ctx, p, " = ") ||
+ addchar(mem_ctx, p, '{') ||
+ addstr(mem_ctx, p, s) ||
+ addstr(mem_ctx, p, "}\n")) {
+ return -1;
+ }
+ return 0;
+}
+
+/*
+ find the length of a nullterm array
+*/
+static int len_nullterm(const char *ptr, int size, int array_len)
+{
+ int len;
+
+ if (size == 1) {
+ len = strnlen(ptr, array_len);
+ } else {
+ for (len=0; len < array_len; len++) {
+ if (all_zero(ptr+len*size, size)) break;
+ }
+ }
+
+ if (len == 0) len = 1;
+
+ return len;
+}
+
+
+/* the generic dump routine. Scans the parse information for this structure
+ and processes it recursively */
+char *gen_dump(TALLOC_CTX *mem_ctx,
+ const struct parse_struct *pinfo,
+ const char *data,
+ unsigned indent)
+{
+ struct parse_string p;
+ int i;
+
+ p.length = 0;
+ p.allocated = 0;
+ p.s = NULL;
+
+ if (addstr(mem_ctx, &p, "") != 0) {
+ return NULL;
+ }
+
+ for (i=0;pinfo[i].name;i++) {
+ const char *ptr = data + pinfo[i].offset;
+ unsigned size = pinfo[i].size;
+
+ if (pinfo[i].ptr_count) {
+ size = sizeof(void *);
+ }
+
+ /* special handling for array types */
+ if (pinfo[i].array_len) {
+ unsigned len = pinfo[i].array_len;
+ if (pinfo[i].flags & FLAG_NULLTERM) {
+ len = len_nullterm(ptr, size, len);
+ }
+ if (gen_dump_array(mem_ctx, &p, &pinfo[i], ptr,
+ len, indent)) {
+ goto failed;
+ }
+ continue;
+ }
+
+ /* and dynamically sized arrays */
+ if (pinfo[i].dynamic_len) {
+ int len = find_var(pinfo, data, pinfo[i].dynamic_len);
+ struct parse_struct p2 = pinfo[i];
+ if (len < 0) {
+ goto failed;
+ }
+ if (len > 0) {
+ if (pinfo[i].flags & FLAG_NULLTERM) {
+ len = len_nullterm(*(char **)ptr,
+ pinfo[i].size, len);
+ }
+ p2.ptr_count--;
+ p2.dynamic_len = NULL;
+ if (gen_dump_array(mem_ctx, &p, &p2,
+ *(char **)ptr,
+ len, indent) != 0) {
+ goto failed;
+ }
+ }
+ continue;
+ }
+
+ /* don't dump zero elements */
+ if (!(pinfo[i].flags & FLAG_ALWAYS) && all_zero(ptr, size)) continue;
+
+ /* assume char* is a null terminated string */
+ if (pinfo[i].size == 1 && pinfo[i].ptr_count == 1 &&
+ pinfo[i].dump_fn == gen_dump_char) {
+ if (gen_dump_string(mem_ctx, &p, &pinfo[i], ptr, indent) != 0) {
+ goto failed;
+ }
+ continue;
+ }
+
+ /* generic pointer dereference */
+ if (pinfo[i].ptr_count) {
+ ptr = *(const char **)ptr;
+ }
+
+ if (addtabbed(mem_ctx, &p, pinfo[i].name, indent) ||
+ addstr(mem_ctx, &p, " = ") ||
+ gen_dump_one(mem_ctx, &p, &pinfo[i], ptr, indent) ||
+ addstr(mem_ctx, &p, "\n")) {
+ goto failed;
+ }
+ }
+ return p.s;
+
+failed:
+ return NULL;
+}
+
+/* search for a character in a string, skipping over sections within
+ matching braces */
+static char *match_braces(char *s, char c)
+{
+ int depth = 0;
+ while (*s) {
+ switch (*s) {
+ case '}':
+ depth--;
+ break;
+ case '{':
+ depth++;
+ break;
+ }
+ if (depth == 0 && *s == c) {
+ return s;
+ }
+ s++;
+ }
+ return s;
+}
+
+/* parse routine for enumerated types */
+int gen_parse_enum(TALLOC_CTX *mem_ctx,
+ const struct enum_struct *einfo,
+ char *ptr,
+ const char *str)
+{
+ unsigned v;
+ int i;
+
+ if (isdigit(*str)) {
+ if (sscanf(str, "%u", &v) != 1) {
+ errno = EINVAL;
+ return -1;
+ }
+ *(unsigned *)ptr = v;
+ return 0;
+ }
+
+ for (i=0;einfo[i].name;i++) {
+ if (strcmp(einfo[i].name, str) == 0) {
+ *(unsigned *)ptr = einfo[i].value;
+ return 0;
+ }
+ }
+
+ /* unknown enum value?? */
+ return -1;
+}
+
+
+/* parse all base types */
+static int gen_parse_base(TALLOC_CTX *mem_ctx,
+ const struct parse_struct *pinfo,
+ char *ptr,
+ const char *str)
+{
+ if (pinfo->parse_fn == gen_parse_char && pinfo->ptr_count==1) {
+ unsigned len;
+ char *s = decode_bytes(mem_ctx, str, &len);
+ if (!s) return -1;
+ *(char **)ptr = s;
+ return 0;
+ }
+
+ if (pinfo->ptr_count) {
+ unsigned size = pinfo->ptr_count>1?sizeof(void *):pinfo->size;
+ struct parse_struct p2 = *pinfo;
+ *(void **)ptr = talloc(mem_ctx, size);
+ if (! *(void **)ptr) {
+ return -1;
+ }
+ memset(*(void **)ptr, 0, size);
+ ptr = *(char **)ptr;
+ p2.ptr_count--;
+ return gen_parse_base(mem_ctx, &p2, ptr, str);
+ }
+
+ return pinfo->parse_fn(mem_ctx, ptr, str);
+}
+
+/* parse a generic array */
+static int gen_parse_array(TALLOC_CTX *mem_ctx,
+ const struct parse_struct *pinfo,
+ char *ptr,
+ const char *str,
+ int array_len)
+{
+ char *p, *p2;
+ unsigned size = pinfo->size;
+
+ /* special handling of fixed length strings */
+ if (array_len != 0 &&
+ pinfo->ptr_count == 0 &&
+ pinfo->dump_fn == gen_dump_char) {
+ unsigned len = 0;
+ char *s = decode_bytes(mem_ctx, str, &len);
+ if (!s || (len > array_len)) return -1;
+ memset(ptr, 0, array_len);
+ memcpy(ptr, s, len);
+ return 0;
+ }
+
+ if (pinfo->ptr_count) {
+ size = sizeof(void *);
+ }
+
+ while (*str) {
+ unsigned idx;
+ int done;
+
+ idx = atoi(str);
+ p = strchr(str,':');
+ if (!p) break;
+ p++;
+ p2 = match_braces(p, ',');
+ done = (*p2 != ',');
+ *p2 = 0;
+
+ if (*p == '{') {
+ p++;
+ p[strlen(p)-1] = 0;
+ }
+
+ if (gen_parse_base(mem_ctx, pinfo, ptr + idx*size, p) != 0) {
+ return -1;
+ }
+
+ if (done) break;
+ str = p2+1;
+ }
+
+ return 0;
+}
+
+/* parse one element, hanlding dynamic and static arrays */
+static int gen_parse_one(TALLOC_CTX *mem_ctx,
+ const struct parse_struct *pinfo,
+ const char *name,
+ char *data,
+ const char *str)
+{
+ int i;
+ for (i=0;pinfo[i].name;i++) {
+ if (strcmp(pinfo[i].name, name) == 0) {
+ break;
+ }
+ }
+ if (pinfo[i].name == NULL) {
+ return 0;
+ }
+
+ if (pinfo[i].array_len) {
+ return gen_parse_array(mem_ctx, &pinfo[i],
+ data+pinfo[i].offset,
+ str, pinfo[i].array_len);
+ }
+
+ if (pinfo[i].dynamic_len) {
+ int len = find_var(pinfo, data, pinfo[i].dynamic_len);
+ if (len < 0) {
+ errno = EINVAL;
+ return -1;
+ }
+ if (len > 0) {
+ struct parse_struct p2 = pinfo[i];
+ char *ptr;
+ unsigned size = pinfo[i].ptr_count>1?sizeof(void*):pinfo[i].size;
+ ptr = talloc(mem_ctx, len*size);
+ if (!ptr) {
+ errno = ENOMEM;
+ return -1;
+ }
+ memset(ptr, 0, len*size);
+ *((char **)(data + pinfo[i].offset)) = ptr;
+ p2.ptr_count--;
+ p2.dynamic_len = NULL;
+ return gen_parse_array(mem_ctx, &p2, ptr, str, len);
+ }
+ return 0;
+ }
+
+ return gen_parse_base(mem_ctx, &pinfo[i], data + pinfo[i].offset, str);
+}
+
+int gen_parse_struct(TALLOC_CTX * mem_ctx, const struct parse_struct *pinfo, char *ptr, const char *str)
+{
+ return gen_parse(mem_ctx, pinfo, ptr, str);
+}
+
+/* the main parse routine */
+int gen_parse(TALLOC_CTX *mem_ctx, const struct parse_struct *pinfo, char *data, const char *s)
+{
+ char *str, *s0;
+
+ s0 = talloc_strdup(mem_ctx, s);
+ str = s0;
+
+ while (*str) {
+ char *p;
+ char *name;
+ char *value;
+
+ /* skip leading whitespace */
+ while (isspace(*str)) str++;
+
+ p = strchr(str, '=');
+ if (!p) break;
+ value = p+1;
+ while (p > str && isspace(*(p-1))) {
+ p--;
+ }
+
+ *p = 0;
+ name = str;
+
+ while (isspace(*value)) value++;
+
+ if (*value == '{') {
+ str = match_braces(value, '}');
+ value++;
+ } else {
+ str = match_braces(value, '\n');
+ }
+
+ *str++ = 0;
+
+ if (gen_parse_one(mem_ctx, pinfo, name, data, value) != 0) {
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+
+
+/* for convenience supply some standard dumpers and parsers here */
+
+int gen_parse_char(TALLOC_CTX *mem_ctx, char *ptr, const char *str)
+{
+ *(unsigned char *)ptr = atoi(str);
+ return 0;
+}
+
+int gen_parse_int(TALLOC_CTX *mem_ctx, char *ptr, const char *str)
+{
+ *(int *)ptr = atoi(str);
+ return 0;
+}
+
+int gen_parse_unsigned(TALLOC_CTX *mem_ctx, char *ptr, const char *str)
+{
+ *(unsigned *)ptr = strtoul(str, NULL, 10);
+ return 0;
+}
+
+int gen_parse_time_t(TALLOC_CTX *mem_ctx, char *ptr, const char *str)
+{
+ *(time_t *)ptr = strtoul(str, NULL, 10);
+ return 0;
+}
+
+int gen_parse_double(TALLOC_CTX *mem_ctx, char *ptr, const char *str)
+{
+ *(double *)ptr = atof(str);
+ return 0;
+}
+
+int gen_parse_float(TALLOC_CTX *mem_ctx, char *ptr, const char *str)
+{
+ *(float *)ptr = atof(str);
+ return 0;
+}
+
+int gen_dump_char(TALLOC_CTX *mem_ctx, struct parse_string *p, const char *ptr, unsigned indent)
+{
+ return addshort(mem_ctx, p, "%u", *(unsigned char *)(ptr));
+}
+
+int gen_dump_int(TALLOC_CTX *mem_ctx, struct parse_string *p, const char *ptr, unsigned indent)
+{
+ return addshort(mem_ctx, p, "%d", *(int *)(ptr));
+}
+
+int gen_dump_unsigned(TALLOC_CTX *mem_ctx, struct parse_string *p, const char *ptr, unsigned indent)
+{
+ return addshort(mem_ctx, p, "%u", *(unsigned *)(ptr));
+}
+
+int gen_dump_time_t(TALLOC_CTX *mem_ctx, struct parse_string *p, const char *ptr, unsigned indent)
+{
+ return addshort(mem_ctx, p, "%u", *(time_t *)(ptr));
+}
+
+int gen_dump_double(TALLOC_CTX *mem_ctx, struct parse_string *p, const char *ptr, unsigned indent)
+{
+ return addshort(mem_ctx, p, "%lg", *(double *)(ptr));
+}
+
+int gen_dump_float(TALLOC_CTX *mem_ctx, struct parse_string *p, const char *ptr, unsigned indent)
+{
+ return addshort(mem_ctx, p, "%g", *(float *)(ptr));
+}
diff --git a/source/lib/genparser_samba.c b/source/lib/genparser_samba.c
new file mode 100644
index 00000000000..8f469a46d6a
--- /dev/null
+++ b/source/lib/genparser_samba.c
@@ -0,0 +1,218 @@
+/*
+ Copyright (C) Andrew Tridgell <genstruct@tridgell.net> 2002
+ Copyright (C) Simo Sorce <idra@samba.org> 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 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 "genparser_samba.h"
+
+/* PARSE functions */
+
+int gen_parse_uint8(TALLOC_CTX *mem_ctx, char *ptr, const char *str)
+{
+ *(uint8 *)ptr = atoi(str);
+ return 0;
+}
+
+int gen_parse_uint16(TALLOC_CTX *mem_ctx, char *ptr, const char *str)
+{
+ *(uint16 *)ptr = atoi(str);
+ return 0;
+}
+
+int gen_parse_uint32(TALLOC_CTX *mem_ctx, char *ptr, const char *str)
+{
+ *(uint32 *)ptr = strtoul(str, NULL, 10);
+ return 0;
+}
+
+int gen_parse_NTTIME(TALLOC_CTX *mem_ctx, char *ptr, const char *str)
+{
+ if(sscanf(str, "%u,%u", &(((NTTIME *)(ptr))->high), &(((NTTIME *)(ptr))->low)) != 2) {
+ errno = EINVAL;
+ return -1;
+ }
+ return 0;
+}
+
+int gen_parse_DOM_SID(TALLOC_CTX *mem_ctx, char *ptr, const char *str)
+{
+ if(!string_to_sid((DOM_SID *)ptr, str)) return -1;
+ return 0;
+}
+
+int gen_parse_SEC_ACCESS(TALLOC_CTX *mem_ctx, char *ptr, const char *str)
+{
+ ((SEC_ACCESS *)ptr)->mask = strtoul(str, NULL, 10);
+ return 0;
+}
+
+int gen_parse_GUID(TALLOC_CTX *mem_ctx, char *ptr, const char *str)
+{
+ int info[UUID_FLAT_SIZE];
+ int i;
+ char *sc;
+ char *p;
+ char *m;
+
+ m = strdup(str);
+ if (!m) return -1;
+ sc = m;
+
+ memset(info, 0, sizeof(info));
+ for (i = 0; i < UUID_FLAT_SIZE; i++) {
+ p = strchr(sc, ',');
+ if (p != NULL) p = '\0';
+ info[i] = atoi(sc);
+ if (p != NULL) sc = p + 1;
+ }
+ free(m);
+
+ for (i = 0; i < UUID_FLAT_SIZE; i++) {
+ ((UUID_FLAT *)ptr)->info[i] = info[i];
+ }
+
+ return 0;
+}
+
+int gen_parse_SEC_ACE(TALLOC_CTX *mem_ctx, char *ptr, const char *str)
+{
+ return gen_parse_struct(mem_ctx, pinfo_security_ace_info, ptr, str);
+}
+
+int gen_parse_SEC_ACL(TALLOC_CTX *mem_ctx, char *ptr, const char *str)
+{
+ return gen_parse_struct(mem_ctx, pinfo_security_acl_info, ptr, str);
+}
+
+int gen_parse_SEC_DESC(TALLOC_CTX *mem_ctx, char *ptr, const char *str)
+{
+ return gen_parse_struct(mem_ctx, pinfo_security_descriptor_info, ptr, str);
+}
+
+int gen_parse_LUID_ATTR(TALLOC_CTX *mem_ctx, char *ptr, const char *str)
+{
+ return gen_parse_struct(mem_ctx, pinfo_luid_attr_info, ptr, str);
+}
+
+int gen_parse_LUID(TALLOC_CTX *mem_ctx, char *ptr, const char *str)
+{
+ if(sscanf(str, "%u,%u", &(((LUID *)(ptr))->high), &(((LUID *)(ptr))->low)) != 2) {
+ errno = EINVAL;
+ return -1;
+ }
+ return 0;
+}
+
+int gen_parse_DATA_BLOB(TALLOC_CTX *mem_ctx, char *ptr, const char *str)
+{
+ return gen_parse_struct(mem_ctx, pinfo_data_blob_info, ptr, str);
+}
+
+int gen_parse_TALLOC_CTX(TALLOC_CTX *mem_ctx, char *ptr, const char *str)
+{
+ (TALLOC_CTX *)ptr = NULL;
+ return 0;
+}
+
+/* DUMP functions */
+
+int gen_dump_uint8(TALLOC_CTX *mem_ctx, struct parse_string *p, const char *ptr, unsigned indent)
+{
+ return addshort(mem_ctx, p, "%u", *(uint8 *)(ptr));
+}
+
+int gen_dump_uint16(TALLOC_CTX *mem_ctx, struct parse_string *p, const char *ptr, unsigned indent)
+{
+ return addshort(mem_ctx, p, "%u", *(uint16 *)(ptr));
+}
+
+int gen_dump_uint32(TALLOC_CTX *mem_ctx, struct parse_string *p, const char *ptr, unsigned indent)
+{
+ return addshort(mem_ctx, p, "%u", *(uint32 *)(ptr));
+}
+
+int gen_dump_NTTIME(TALLOC_CTX *mem_ctx, struct parse_string *p, const char *ptr, unsigned indent)
+{
+ uint32 low, high;
+
+ high = ((NTTIME *)(ptr))->high;
+ low = ((NTTIME *)(ptr))->low;
+ return addshort(mem_ctx, p, "%u,%u", high, low);
+}
+
+int gen_dump_DOM_SID(TALLOC_CTX *mem_ctx, struct parse_string *p, const char *ptr, unsigned indent)
+{
+ fstring sidstr;
+
+ sid_to_string(sidstr, (DOM_SID *)ptr);
+ return addstr(mem_ctx, p, sidstr);
+}
+
+int gen_dump_SEC_ACCESS(TALLOC_CTX *mem_ctx, struct parse_string *p, const char *ptr, unsigned indent)
+{
+ return addshort(mem_ctx, p, "%u", ((SEC_ACCESS *)ptr)->mask);
+}
+
+int gen_dump_GUID(TALLOC_CTX *mem_ctx, struct parse_string *p, const char *ptr, unsigned indent)
+{
+ int i, r;
+
+ for (i = 0; i < (UUID_FLAT_SIZE - 1); i++) {
+ if (!(r = addshort(mem_ctx, p, "%d,", ((UUID_FLAT *)ptr)->info[i]))) return r;
+ }
+ return addshort(mem_ctx, p, "%d", ((UUID_FLAT *)ptr)->info[i]);
+}
+
+int gen_dump_SEC_ACE(TALLOC_CTX *mem_ctx, struct parse_string *p, const char *ptr, unsigned indent)
+{
+ return gen_dump_struct(mem_ctx, pinfo_security_ace_info, p, ptr, indent);
+}
+
+int gen_dump_SEC_ACL(TALLOC_CTX *mem_ctx, struct parse_string *p, const char *ptr, unsigned indent)
+{
+ return gen_dump_struct(mem_ctx, pinfo_security_acl_info, p, ptr, indent);
+}
+
+int gen_dump_SEC_DESC(TALLOC_CTX *mem_ctx, struct parse_string *p, const char *ptr, unsigned indent)
+{
+ return gen_dump_struct(mem_ctx, pinfo_security_descriptor_info, p, ptr, indent);
+}
+
+int gen_dump_LUID_ATTR(TALLOC_CTX *mem_ctx, struct parse_string *p, const char *ptr, unsigned indent)
+{
+ return gen_dump_struct(mem_ctx, pinfo_luid_attr_info, p, ptr, indent);
+}
+
+int gen_dump_LUID(TALLOC_CTX *mem_ctx, struct parse_string *p, const char *ptr, unsigned indent)
+{
+ uint32 low, high;
+
+ high = ((LUID *)(ptr))->high;
+ low = ((LUID *)(ptr))->low;
+ return addshort(mem_ctx, p, "%u,%u", high, low);
+}
+
+int gen_dump_DATA_BLOB(TALLOC_CTX *mem_ctx, struct parse_string *p, const char *ptr, unsigned indent)
+{
+ return gen_dump_struct(mem_ctx, pinfo_data_blob_info, p, ptr, indent);
+}
+
+int gen_dump_TALLOC_CTX(TALLOC_CTX *mem_ctx, struct parse_string *p, const char *ptr, unsigned indent)
+{
+ return addshort(mem_ctx, p, "TALLOC_CTX");
+}
diff --git a/source/lib/genrand.c b/source/lib/genrand.c
new file mode 100644
index 00000000000..bc9f21c6403
--- /dev/null
+++ b/source/lib/genrand.c
@@ -0,0 +1,267 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ Functions to create reasonable random numbers for crypto use.
+
+ Copyright (C) Jeremy Allison 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 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"
+
+static unsigned char hash[258];
+static uint32 counter;
+static unsigned char *reseed_data;
+static size_t reseed_data_size;
+
+/****************************************************************
+ Copy any user given reseed data.
+*****************************************************************/
+
+void set_rand_reseed_data(unsigned char *data, size_t len)
+{
+ SAFE_FREE(reseed_data);
+ reseed_data_size = 0;
+
+ reseed_data = (unsigned char *)memdup(data, len);
+ if (reseed_data)
+ reseed_data_size = len;
+}
+
+/****************************************************************
+ Setup the seed.
+*****************************************************************/
+
+static void seed_random_stream(unsigned char *seedval, size_t seedlen)
+{
+ unsigned char j = 0;
+ size_t ind;
+
+ for (ind = 0; ind < 256; ind++)
+ hash[ind] = (unsigned char)ind;
+
+ for( ind = 0; ind < 256; ind++) {
+ unsigned char tc;
+
+ j += (hash[ind] + seedval[ind%seedlen]);
+
+ tc = hash[ind];
+ hash[ind] = hash[j];
+ hash[j] = tc;
+ }
+
+ hash[256] = 0;
+ hash[257] = 0;
+}
+
+/****************************************************************
+ Get datasize bytes worth of random data.
+*****************************************************************/
+
+static void get_random_stream(unsigned char *data, size_t datasize)
+{
+ unsigned char index_i = hash[256];
+ unsigned char index_j = hash[257];
+ size_t ind;
+
+ for( ind = 0; ind < datasize; ind++) {
+ unsigned char tc;
+ unsigned char t;
+
+ index_i++;
+ index_j += hash[index_i];
+
+ tc = hash[index_i];
+ hash[index_i] = hash[index_j];
+ hash[index_j] = tc;
+
+ t = hash[index_i] + hash[index_j];
+ data[ind] = hash[t];
+ }
+
+ hash[256] = index_i;
+ hash[257] = index_j;
+}
+
+/****************************************************************
+ Get a 16 byte hash from the contents of a file.
+ Note that the hash is not initialised.
+*****************************************************************/
+
+static void do_filehash(const char *fname, unsigned char *the_hash)
+{
+ unsigned char buf[1011]; /* deliberate weird size */
+ unsigned char tmp_md4[16];
+ int fd, n;
+
+ fd = sys_open(fname,O_RDONLY,0);
+ if (fd == -1)
+ return;
+
+ while ((n = read(fd, (char *)buf, sizeof(buf))) > 0) {
+ mdfour(tmp_md4, buf, n);
+ for (n=0;n<16;n++)
+ the_hash[n] ^= tmp_md4[n];
+ }
+ close(fd);
+}
+
+/**************************************************************
+ Try and get a good random number seed. Try a number of
+ different factors. Firstly, try /dev/urandom - use if exists.
+
+ We use /dev/urandom as a read of /dev/random can block if
+ the entropy pool dries up. This leads clients to timeout
+ or be very slow on connect.
+
+ If we can't use /dev/urandom then seed the stream random generator
+ above...
+**************************************************************/
+
+static int do_reseed(BOOL use_fd, int fd)
+{
+ unsigned char seed_inbuf[40];
+ uint32 v1, v2; struct timeval tval; pid_t mypid;
+ struct passwd *pw;
+
+ if (use_fd) {
+ if (fd != -1)
+ return fd;
+
+ fd = sys_open( "/dev/urandom", O_RDONLY,0);
+ if(fd >= 0)
+ return fd;
+ }
+
+ /* Add in some secret file contents */
+
+ do_filehash("/etc/shadow", &seed_inbuf[0]);
+ do_filehash(lp_smb_passwd_file(), &seed_inbuf[16]);
+
+ /*
+ * Add in the root encrypted password.
+ * On any system where security is taken
+ * seriously this will be secret.
+ */
+
+ pw = getpwnam_alloc("root");
+ if (pw && pw->pw_passwd) {
+ size_t i;
+ unsigned char md4_tmp[16];
+ mdfour(md4_tmp, (unsigned char *)pw->pw_passwd, strlen(pw->pw_passwd));
+ for (i=0;i<16;i++)
+ seed_inbuf[8+i] ^= md4_tmp[i];
+ passwd_free(&pw);
+ }
+
+ /*
+ * Add the counter, time of day, and pid.
+ */
+
+ GetTimeOfDay(&tval);
+ mypid = sys_getpid();
+ v1 = (counter++) + mypid + tval.tv_sec;
+ v2 = (counter++) * mypid + tval.tv_usec;
+
+ SIVAL(seed_inbuf, 32, v1 ^ IVAL(seed_inbuf, 32));
+ SIVAL(seed_inbuf, 36, v2 ^ IVAL(seed_inbuf, 36));
+
+ /*
+ * Add any user-given reseed data.
+ */
+
+ if (reseed_data) {
+ size_t i;
+ for (i = 0; i < sizeof(seed_inbuf); i++)
+ seed_inbuf[i] ^= reseed_data[i % reseed_data_size];
+ }
+
+ seed_random_stream(seed_inbuf, sizeof(seed_inbuf));
+
+ return -1;
+}
+
+/*******************************************************************
+ Interface to the (hopefully) good crypto random number generator.
+********************************************************************/
+
+void generate_random_buffer( unsigned char *out, int len, BOOL do_reseed_now)
+{
+ static BOOL done_reseed = False;
+ static int urand_fd = -1;
+ unsigned char md4_buf[64];
+ unsigned char tmp_buf[16];
+ unsigned char *p;
+
+ if(!done_reseed || do_reseed_now) {
+ urand_fd = do_reseed(True, urand_fd);
+ done_reseed = True;
+ }
+
+ if (urand_fd != -1 && len > 0) {
+
+ if (read(urand_fd, out, len) == len)
+ return; /* len bytes of random data read from urandom. */
+
+ /* Read of urand error, drop back to non urand method. */
+ close(urand_fd);
+ urand_fd = -1;
+ do_reseed(False, -1);
+ done_reseed = True;
+ }
+
+ /*
+ * Generate random numbers in chunks of 64 bytes,
+ * then md4 them & copy to the output buffer.
+ * This way the raw state of the stream is never externally
+ * seen.
+ */
+
+ p = out;
+ while(len > 0) {
+ int copy_len = len > 16 ? 16 : len;
+
+ get_random_stream(md4_buf, sizeof(md4_buf));
+ mdfour(tmp_buf, md4_buf, sizeof(md4_buf));
+ memcpy(p, tmp_buf, copy_len);
+ p += copy_len;
+ len -= copy_len;
+ }
+}
+
+/*******************************************************************
+ Use the random number generator to generate a random string.
+********************************************************************/
+
+static char c_list[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+_-#.,";
+
+char *generate_random_str(size_t len)
+{
+ static unsigned char retstr[256];
+ size_t i;
+
+ memset(retstr, '\0', sizeof(retstr));
+
+ if (len > sizeof(retstr)-1)
+ len = sizeof(retstr) -1;
+ generate_random_buffer( retstr, len, False);
+ for (i = 0; i < len; i++)
+ retstr[i] = c_list[ retstr[i] % (sizeof(c_list)-1) ];
+
+ retstr[i] = '\0';
+
+ return (char *)retstr;
+}
diff --git a/source/lib/getsmbpass.c b/source/lib/getsmbpass.c
new file mode 100644
index 00000000000..df5e0359aa2
--- /dev/null
+++ b/source/lib/getsmbpass.c
@@ -0,0 +1,181 @@
+/* Copyright (C) 1992-1998 Free Software Foundation, Inc.
+This file is part of the GNU C Library.
+
+The GNU C Library is free software; you can redistribute it and/or
+modify it under the terms of the GNU Library General Public License as
+published by the Free Software Foundation; either version 2 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
+Library General Public License for more details.
+
+You should have received a copy of the GNU Library General Public
+License along with the GNU C Library; see the file COPYING.LIB. If
+not, write to the Free Software Foundation, Inc., 675 Mass Ave,
+Cambridge, MA 02139, USA. */
+
+/* Modified to use with samba by Jeremy Allison, 8th July 1995. */
+
+#include "includes.h"
+
+#ifdef REPLACE_GETPASS
+
+#ifdef SYSV_TERMIO
+
+/* SYSTEM V TERMIO HANDLING */
+
+static struct termio t;
+
+#define ECHO_IS_ON(t) ((t).c_lflag & ECHO)
+#define TURN_ECHO_OFF(t) ((t).c_lflag &= ~ECHO)
+#define TURN_ECHO_ON(t) ((t).c_lflag |= ECHO)
+
+#ifndef TCSAFLUSH
+#define TCSAFLUSH 1
+#endif
+
+#ifndef TCSANOW
+#define TCSANOW 0
+#endif
+
+static int tcgetattr(int fd, struct termio *t)
+{
+ return ioctl(fd, TCGETA, t);
+}
+
+static int tcsetattr(int fd, int flags, struct termio *t)
+{
+ if(flags & TCSAFLUSH)
+ ioctl(fd, TCFLSH, TCIOFLUSH);
+ return ioctl(fd, TCSETS, t);
+}
+
+#elif !defined(TCSAFLUSH)
+
+/* BSD TERMIO HANDLING */
+
+static struct sgttyb t;
+
+#define ECHO_IS_ON(t) ((t).sg_flags & ECHO)
+#define TURN_ECHO_OFF(t) ((t).sg_flags &= ~ECHO)
+#define TURN_ECHO_ON(t) ((t).sg_flags |= ECHO)
+
+#define TCSAFLUSH 1
+#define TCSANOW 0
+
+static int tcgetattr(int fd, struct sgttyb *t)
+{
+ return ioctl(fd, TIOCGETP, (char *)t);
+}
+
+static int tcsetattr(int fd, int flags, struct sgttyb *t)
+{
+ return ioctl(fd, TIOCSETP, (char *)t);
+}
+
+#else /* POSIX TERMIO HANDLING */
+#define ECHO_IS_ON(t) ((t).c_lflag & ECHO)
+#define TURN_ECHO_OFF(t) ((t).c_lflag &= ~ECHO)
+#define TURN_ECHO_ON(t) ((t).c_lflag |= ECHO)
+
+static struct termios t;
+#endif /* SYSV_TERMIO */
+
+static SIG_ATOMIC_T gotintr;
+static int in_fd = -1;
+
+/***************************************************************
+ Signal function to tell us were ^C'ed.
+****************************************************************/
+
+static void gotintr_sig(void)
+{
+ gotintr = 1;
+ if (in_fd != -1)
+ close(in_fd); /* Safe way to force a return. */
+ in_fd = -1;
+}
+
+char *getsmbpass(const char *prompt)
+{
+ FILE *in, *out;
+ int echo_off;
+ static char buf[256];
+ static size_t bufsize = sizeof(buf);
+ size_t nread;
+
+ /* Catch problematic signals */
+ CatchSignal(SIGINT, SIGNAL_CAST gotintr_sig);
+
+ /* Try to write to and read from the terminal if we can.
+ If we can't open the terminal, use stderr and stdin. */
+
+ in = fopen ("/dev/tty", "w+");
+ if (in == NULL) {
+ in = stdin;
+ out = stderr;
+ } else {
+ out = in;
+ }
+
+ setvbuf(in, NULL, _IONBF, 0);
+
+ /* Turn echoing off if it is on now. */
+
+ if (tcgetattr (fileno (in), &t) == 0) {
+ if (ECHO_IS_ON(t)) {
+ TURN_ECHO_OFF(t);
+ echo_off = tcsetattr (fileno (in), TCSAFLUSH, &t) == 0;
+ TURN_ECHO_ON(t);
+ } else {
+ echo_off = 0;
+ }
+ } else {
+ echo_off = 0;
+ }
+
+ /* Write the prompt. */
+ fputs(prompt, out);
+ fflush(out);
+
+ /* Read the password. */
+ buf[0] = 0;
+ if (!gotintr) {
+ in_fd = fileno(in);
+ fgets(buf, bufsize, in);
+ }
+ nread = strlen(buf);
+ if (buf[nread - 1] == '\n')
+ buf[nread - 1] = '\0';
+
+ /* Restore echoing. */
+ if (echo_off) {
+ if (gotintr && in_fd == -1)
+ in = fopen ("/dev/tty", "w+");
+ if (in != NULL)
+ tcsetattr (fileno (in), TCSANOW, &t);
+ }
+
+ fprintf(out, "\n");
+ fflush(out);
+
+ if (in != stdin) /* We opened the terminal; now close it. */
+ fclose(in);
+
+ /* Catch problematic signals */
+ CatchSignal(SIGINT, SIGNAL_CAST SIG_DFL);
+
+ if (gotintr) {
+ printf("Interupted by signal.\n");
+ fflush(stdout);
+ exit(1);
+ }
+ return buf;
+}
+
+#else
+ void getsmbpasswd_dummy(void);
+ void getsmbpasswd_dummy(void) {;}
+#endif
diff --git a/source/lib/hash.c b/source/lib/hash.c
new file mode 100644
index 00000000000..18b6534dec2
--- /dev/null
+++ b/source/lib/hash.c
@@ -0,0 +1,316 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ Copyright (C) Ying Chen 2000.
+ Copyright (C) Jeremy Allison 2000.
+ - added some defensive programming.
+
+ This program is free software; you can 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.
+*/
+
+/*
+ * NB. We may end up replacing this functionality in a future 2.x
+ * release to reduce the number of hashing/lookup methods we support. JRA.
+ */
+
+#include "includes.h"
+
+static BOOL enlarge_hash_table(hash_table *table);
+static unsigned primes[] =
+ {17, 37, 67, 131, 257, 521, 1031, 2053, 4099, 8209, 16411};
+
+/****************************************************************************
+ * This function initializes the hash table.
+ * This hash function hashes on string keys.
+ * This number of hash buckets is always rounded up to a power of
+ * 2 first, then to a prime number that is large than the power of two.
+ * Input:
+ * table -- the hash table pointer.
+ * num_buckets -- the number of buckets to be allocated. This
+ * hash function can dynamically increase its size when the
+ * the hash table size becomes small. There is a MAX hash table
+ * size defined in hash.h.
+ * compare_func -- the function pointer to a comparison function
+ * used by the hash key comparison.
+ ****************************************************************************
+ */
+
+BOOL hash_table_init(hash_table *table, unsigned num_buckets, compare_function compare_func)
+{
+ unsigned i;
+ ubi_dlList *bucket;
+
+ table->num_elements = 0;
+ table->size = 2;
+ table->comp_func = compare_func;
+ while (table->size < num_buckets)
+ table->size <<= 1;
+ for (i = 0; i < ARRAY_SIZE(primes); i++) {
+ if (primes[i] > table->size) {
+ table->size = primes[i];
+ break;
+ }
+ }
+
+ DEBUG(5, ("Hash size = %d.\n", table->size));
+
+ if(!(table->buckets = (ubi_dlList *) malloc(sizeof(ubi_dlList) * table->size))) {
+ DEBUG(0,("hash_table_init: malloc fail !\n"));
+ return False;
+ }
+ ubi_dlInitList(&(table->lru_chain));
+ for (i=0, bucket = table->buckets; i < table->size; i++, bucket++)
+ ubi_dlInitList(bucket);
+
+ return True;
+}
+
+/*
+ **************************************************************
+ * Compute a hash value based on a string key value.
+ * Make the string key into an array of int's if possible.
+ * For the last few chars that cannot be int'ed, use char instead.
+ * The function returns the bucket index number for the hashed
+ * key.
+ * JRA. Use a djb-algorithm hash for speed.
+ **************************************************************
+ */
+
+static int string_hash(int hash_size, const char *key)
+{
+ u32 n = 0;
+ const char *p;
+ for (p = key; *p != '\0'; p++) {
+ n = ((n << 5) + n) ^ (u32)(*p);
+ }
+ return (n % hash_size);
+}
+
+/* *************************************************************************
+ * Search the hash table for the entry in the hash chain.
+ * The function returns the pointer to the
+ * element found in the chain or NULL if none is found.
+ * If the element is found, the element is also moved to
+ * the head of the LRU list.
+ *
+ * Input:
+ * table -- The hash table where the element is stored in.
+ * hash_chain -- The pointer to the bucket that stores the
+ * element to be found.
+ * key -- The hash key to be found.
+ ***************************************************************************
+ */
+
+static hash_element *hash_chain_find(hash_table *table, ubi_dlList *hash_chain, char *key)
+{
+ hash_element *hash_elem;
+ ubi_dlNodePtr lru_item;
+ unsigned int i = 0;
+
+ for (hash_elem = (hash_element *)(ubi_dlFirst(hash_chain)); i < hash_chain->count;
+ i++, hash_elem = (hash_element *)(ubi_dlNext(hash_elem))) {
+ if ((table->comp_func)(hash_elem->key, key) == 0) {
+ /* Move to the head of the lru List. */
+ lru_item = ubi_dlRemove(&(table->lru_chain), &(hash_elem->lru_link.lru_link));
+ ubi_dlAddHead(&(table->lru_chain), lru_item);
+ return(hash_elem);
+ }
+ }
+ return ((hash_element *) NULL);
+}
+
+/* ***************************************************************************
+ *
+ * Lookup a hash table for an element with key.
+ * The function returns a pointer to the hash element.
+ * If no element is found, the function returns NULL.
+ *
+ * Input:
+ * table -- The hash table to be searched on.
+ * key -- The key to be found.
+ *****************************************************************************
+ */
+
+hash_element *hash_lookup(hash_table *table, char *key)
+{
+ return (hash_chain_find(table, &table->buckets[string_hash(table->size, key)], key));
+}
+
+/* ***************************************************************
+ *
+ * This function first checks if an element with key "key"
+ * exists in the hash table. If so, the function moves the
+ * element to the front of the LRU list. Otherwise, a new
+ * hash element corresponding to "value" and "key" is allocated
+ * and inserted into the hash table. The new elements are
+ * always inserted in the LRU order to the LRU list as well.
+ *
+ * Input:
+ * table -- The hash table to be inserted in.
+ * value -- The content of the element to be inserted.
+ * key -- The key of the new element to be inserted.
+ *
+ ****************************************************************
+ */
+
+hash_element *hash_insert(hash_table *table, char *value, char *key)
+{
+ hash_element *hash_elem;
+ ubi_dlNodePtr lru_item;
+ ubi_dlList *bucket;
+ size_t string_length;
+
+ /*
+ * If the hash table size has not reached the MAX_HASH_TABLE_SIZE,
+ * the hash table may be enlarged if the current hash table is full.
+ * If the hash table size has reached the MAX_HASH_TABLE_SIZE,
+ * use LRU to remove the oldest element from the hash table.
+ */
+
+ if ((table->num_elements >= table->size) &&
+ (table->num_elements < MAX_HASH_TABLE_SIZE)) {
+ if(!enlarge_hash_table(table))
+ return (hash_element *)NULL;
+ table->num_elements += 1;
+ } else if (table->num_elements >= MAX_HASH_TABLE_SIZE) {
+ /* Do an LRU replacement. */
+ lru_item = ubi_dlLast(&(table->lru_chain));
+ hash_elem = (hash_element *)(((lru_node *)lru_item)->hash_elem);
+ bucket = hash_elem->bucket;
+ ubi_dlRemThis(&(table->lru_chain), &(hash_elem->lru_link.lru_link));
+ ubi_dlRemThis(bucket, (ubi_dlNodePtr)hash_elem);
+ SAFE_FREE(hash_elem->value);
+ SAFE_FREE(hash_elem);
+ } else {
+ table->num_elements += 1;
+ }
+
+ bucket = &table->buckets[string_hash(table->size, key)];
+
+ /* Since we only have 1-byte for the key string, we need to
+ * allocate extra space in the hash_element to store the entire key
+ * string.
+ */
+
+ string_length = strlen(key);
+ if(!(hash_elem = (hash_element *) malloc(sizeof(hash_element) + string_length))) {
+ DEBUG(0,("hash_insert: malloc fail !\n"));
+ return (hash_element *)NULL;
+ }
+
+ safe_strcpy((char *) hash_elem->key, key, string_length);
+
+ hash_elem->value = (char *)value;
+ hash_elem->bucket = bucket;
+ /* Insert in front of the lru list and the bucket list. */
+ ubi_dlAddHead(bucket, hash_elem);
+ hash_elem->lru_link.hash_elem = hash_elem;
+ ubi_dlAddHead(&(table->lru_chain), &(hash_elem->lru_link.lru_link));
+
+ return(hash_elem);
+}
+
+/* **************************************************************************
+ *
+ * Remove a hash element from the hash table. The hash element is
+ * removed from both the LRU list and the hash bucket chain.
+ *
+ * Input:
+ * table -- the hash table to be manipulated on.
+ * hash_elem -- the element to be removed.
+ **************************************************************************
+ */
+
+void hash_remove(hash_table *table, hash_element *hash_elem)
+{
+ if (hash_elem) {
+ ubi_dlRemove(&(table->lru_chain), &(hash_elem->lru_link.lru_link));
+ ubi_dlRemove(hash_elem->bucket, (ubi_dlNodePtr) hash_elem);
+ SAFE_FREE(hash_elem->value);
+ SAFE_FREE(hash_elem);
+ table->num_elements--;
+ }
+}
+
+/* ******************************************************************
+ * Increase the hash table size if it is too small.
+ * The hash table size is increased by the HASH_TABLE_INCREMENT
+ * ratio.
+ * Input:
+ * table -- the hash table to be enlarged.
+ ******************************************************************
+ */
+
+static BOOL enlarge_hash_table(hash_table *table)
+{
+ hash_element *hash_elem;
+ int size, hash_value;
+ ubi_dlList *buckets;
+ ubi_dlList *old_bucket;
+ ubi_dlList *bucket;
+ ubi_dlList lru_chain;
+
+ buckets = table->buckets;
+ lru_chain = table->lru_chain;
+ size = table->size;
+
+ /* Reinitialize the hash table. */
+ if(!hash_table_init(table, table->size * HASH_TABLE_INCREMENT, table->comp_func))
+ return False;
+
+ for (old_bucket = buckets; size > 0; size--, old_bucket++) {
+ while (old_bucket->count != 0) {
+ hash_elem = (hash_element *) ubi_dlRemHead(old_bucket);
+ ubi_dlRemove(&lru_chain, &(hash_elem->lru_link.lru_link));
+ hash_value = string_hash(table->size, (char *) hash_elem->key);
+ bucket = &(table->buckets[hash_value]);
+ ubi_dlAddHead(bucket, hash_elem);
+ ubi_dlAddHead(&(table->lru_chain), &(hash_elem->lru_link.lru_link));
+ hash_elem->bucket = bucket;
+ hash_elem->lru_link.hash_elem = hash_elem;
+ table->num_elements++;
+ }
+ }
+ SAFE_FREE(buckets);
+
+ return True;
+}
+
+/* **********************************************************************
+ *
+ * Remove everything from a hash table and free up the memory it
+ * occupies.
+ * Input:
+ * table -- the hash table to be cleared.
+ *
+ *************************************************************************
+ */
+
+void hash_clear(hash_table *table)
+{
+ unsigned int i;
+ ubi_dlList *bucket = table->buckets;
+ hash_element *hash_elem;
+ for (i = 0; i < table->size; bucket++, i++) {
+ while (bucket->count != 0) {
+ hash_elem = (hash_element *) ubi_dlRemHead(bucket);
+ SAFE_FREE(hash_elem->value);
+ SAFE_FREE(hash_elem);
+ }
+ }
+ table->size = 0;
+ SAFE_FREE(table->buckets);
+ table->buckets = NULL;
+}
diff --git a/source/lib/hmacmd5.c b/source/lib/hmacmd5.c
new file mode 100644
index 00000000000..f436fd30c0e
--- /dev/null
+++ b/source/lib/hmacmd5.c
@@ -0,0 +1,134 @@
+/*
+ Unix SMB/CIFS implementation.
+ HMAC MD5 code for use in NTLMv2
+ Copyright (C) Luke Kenneth Casson Leighton 1996-2000
+ Copyright (C) Andrew Tridgell 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 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.
+*/
+
+/* taken direct from rfc2104 implementation and modified for suitable use
+ * for ntlmv2.
+ */
+
+#include "includes.h"
+
+/***********************************************************************
+ the rfc 2104 version of hmac_md5 initialisation.
+***********************************************************************/
+void hmac_md5_init_rfc2104(uchar* key, int key_len, HMACMD5Context *ctx)
+{
+ int i;
+
+ /* if key is longer than 64 bytes reset it to key=MD5(key) */
+ if (key_len > 64)
+ {
+ uchar tk[16];
+ struct MD5Context tctx;
+
+ MD5Init(&tctx);
+ MD5Update(&tctx, key, key_len);
+ MD5Final(tk, &tctx);
+
+ key = tk;
+ key_len = 16;
+ }
+
+ /* start out by storing key in pads */
+ ZERO_STRUCT(ctx->k_ipad);
+ ZERO_STRUCT(ctx->k_opad);
+ memcpy( ctx->k_ipad, key, key_len);
+ memcpy( ctx->k_opad, key, key_len);
+
+ /* XOR key with ipad and opad values */
+ for (i=0; i<64; i++)
+ {
+ ctx->k_ipad[i] ^= 0x36;
+ ctx->k_opad[i] ^= 0x5c;
+ }
+
+ MD5Init(&ctx->ctx);
+ MD5Update(&ctx->ctx, ctx->k_ipad, 64);
+}
+
+/***********************************************************************
+ the microsoft version of hmac_md5 initialisation.
+***********************************************************************/
+void hmac_md5_init_limK_to_64(const uchar* key, int key_len,
+ HMACMD5Context *ctx)
+{
+ int i;
+
+ /* if key is longer than 64 bytes truncate it */
+ if (key_len > 64)
+ {
+ key_len = 64;
+ }
+
+ /* start out by storing key in pads */
+ ZERO_STRUCT(ctx->k_ipad);
+ ZERO_STRUCT(ctx->k_opad);
+ memcpy( ctx->k_ipad, key, key_len);
+ memcpy( ctx->k_opad, key, key_len);
+
+ /* XOR key with ipad and opad values */
+ for (i=0; i<64; i++) {
+ ctx->k_ipad[i] ^= 0x36;
+ ctx->k_opad[i] ^= 0x5c;
+ }
+
+ MD5Init(&ctx->ctx);
+ MD5Update(&ctx->ctx, ctx->k_ipad, 64);
+}
+
+/***********************************************************************
+ update hmac_md5 "inner" buffer
+***********************************************************************/
+void hmac_md5_update(const uchar* text, int text_len, HMACMD5Context *ctx)
+{
+ MD5Update(&ctx->ctx, text, text_len); /* then text of datagram */
+}
+
+/***********************************************************************
+ finish off hmac_md5 "inner" buffer and generate outer one.
+***********************************************************************/
+void hmac_md5_final(uchar *digest, HMACMD5Context *ctx)
+
+{
+ struct MD5Context ctx_o;
+
+ MD5Final(digest, &ctx->ctx);
+
+ MD5Init(&ctx_o);
+ MD5Update(&ctx_o, ctx->k_opad, 64);
+ MD5Update(&ctx_o, digest, 16);
+ MD5Final(digest, &ctx_o);
+}
+
+/***********************************************************
+ single function to calculate an HMAC MD5 digest from data.
+ use the microsoft hmacmd5 init method because the key is 16 bytes.
+************************************************************/
+void hmac_md5( uchar key[16], uchar* data, int data_len, uchar* digest)
+{
+ HMACMD5Context ctx;
+ hmac_md5_init_limK_to_64(key, 16, &ctx);
+ if (data_len != 0)
+ {
+ hmac_md5_update(data, data_len, &ctx);
+ }
+ hmac_md5_final(digest, &ctx);
+}
+
diff --git a/source/lib/iconv.c b/source/lib/iconv.c
new file mode 100644
index 00000000000..7df73192f24
--- /dev/null
+++ b/source/lib/iconv.c
@@ -0,0 +1,591 @@
+/*
+ Unix SMB/CIFS implementation.
+ minimal iconv implementation
+ Copyright (C) Andrew Tridgell 2001
+ Copyright (C) Jelmer Vernooij 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 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"
+
+/*
+ * We have to use strcasecmp here as the character conversions
+ * haven't been initialised yet. JRA.
+ */
+
+#undef strcasecmp
+
+/**
+ * @file
+ *
+ * @brief Samba wrapper/stub for iconv character set conversion.
+ *
+ * iconv is the XPG2 interface for converting between character
+ * encodings. This file provides a Samba wrapper around it, and also
+ * a simple reimplementation that is used if the system does not
+ * implement iconv.
+ *
+ * Samba only works with encodings that are supersets of ASCII: ascii
+ * characters like whitespace can be tested for directly, multibyte
+ * sequences start with a byte with the high bit set, and strings are
+ * terminated by a nul byte.
+ *
+ * Note that the only function provided by iconv is conversion between
+ * characters. It doesn't directly support operations like
+ * uppercasing or comparison. We have to convert to UCS-2 and compare
+ * there.
+ *
+ * @sa Samba Developers Guide
+ **/
+
+static size_t ascii_pull(void *,char **, size_t *, char **, size_t *);
+static size_t ascii_push(void *,char **, size_t *, char **, size_t *);
+static size_t latin1_push(void *,char **, size_t *, char **, size_t *);
+static size_t utf8_pull(void *,char **, size_t *, char **, size_t *);
+static size_t utf8_push(void *,char **, size_t *, char **, size_t *);
+static size_t ucs2hex_pull(void *,char **, size_t *, char **, size_t *);
+static size_t ucs2hex_push(void *,char **, size_t *, char **, size_t *);
+static size_t iconv_copy(void *,char **, size_t *, char **, size_t *);
+
+static struct charset_functions builtin_functions[] = {
+ {"UCS-2LE", iconv_copy, iconv_copy},
+ {"UTF8", utf8_pull, utf8_push},
+ {"ASCII", ascii_pull, ascii_push},
+ {"646", ascii_pull, ascii_push},
+ {"ISO-8859-1", ascii_pull, latin1_push},
+ {"UCS2-HEX", ucs2hex_pull, ucs2hex_push},
+ {NULL, NULL, NULL}
+};
+
+static struct charset_functions *charsets = NULL;
+
+static struct charset_functions *find_charset_functions(const char *name)
+{
+ struct charset_functions *c = charsets;
+
+ while(c) {
+ if (strcasecmp(name, c->name) == 0) {
+ return c;
+ }
+ c = c->next;
+ }
+
+ return NULL;
+}
+
+NTSTATUS smb_register_charset(struct charset_functions *funcs)
+{
+ if (!funcs) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ DEBUG(5, ("Attempting to register new charset %s\n", funcs->name));
+ /* Check whether we already have this charset... */
+ if (find_charset_functions(funcs->name)) {
+ DEBUG(0, ("Duplicate charset %s, not registering\n", funcs->name));
+ return NT_STATUS_OBJECT_NAME_COLLISION;
+ }
+
+ funcs->next = funcs->prev = NULL;
+ DEBUG(5, ("Registered charset %s\n", funcs->name));
+ DLIST_ADD(charsets, funcs);
+ return NT_STATUS_OK;
+}
+
+static void lazy_initialize_iconv(void)
+{
+ static BOOL initialized;
+ int i;
+
+ if (!initialized) {
+ initialized = True;
+ for(i = 0; builtin_functions[i].name; i++)
+ smb_register_charset(&builtin_functions[i]);
+ static_init_charset;
+ }
+}
+
+/* if there was an error then reset the internal state,
+ this ensures that we don't have a shift state remaining for
+ character sets like SJIS */
+static size_t sys_iconv(void *cd,
+ char **inbuf, size_t *inbytesleft,
+ char **outbuf, size_t *outbytesleft)
+{
+#ifdef HAVE_NATIVE_ICONV
+ size_t ret = iconv((iconv_t)cd,
+ inbuf, inbytesleft,
+ outbuf, outbytesleft);
+ if (ret == (size_t)-1) iconv(cd, NULL, NULL, NULL, NULL);
+ return ret;
+#else
+ errno = EINVAL;
+ return -1;
+#endif
+}
+
+/**
+ * This is a simple portable iconv() implementaion.
+ *
+ * It only knows about a very small number of character sets - just
+ * enough that Samba works on systems that don't have iconv.
+ **/
+size_t smb_iconv(smb_iconv_t cd,
+ char **inbuf, size_t *inbytesleft,
+ char **outbuf, size_t *outbytesleft)
+{
+ char cvtbuf[2048];
+ char *bufp = cvtbuf;
+ size_t bufsize;
+
+ /* in many cases we can go direct */
+ if (cd->direct) {
+ return cd->direct(cd->cd_direct,
+ (char **)inbuf, inbytesleft, outbuf, outbytesleft);
+ }
+
+
+ /* otherwise we have to do it chunks at a time */
+ while (*inbytesleft > 0) {
+ bufp = cvtbuf;
+ bufsize = sizeof(cvtbuf);
+
+ if (cd->pull(cd->cd_pull,
+ (char **)inbuf, inbytesleft, &bufp, &bufsize) == -1
+ && errno != E2BIG) return -1;
+
+ bufp = cvtbuf;
+ bufsize = sizeof(cvtbuf) - bufsize;
+
+ if (cd->push(cd->cd_push,
+ &bufp, &bufsize,
+ outbuf, outbytesleft) == -1) return -1;
+ }
+
+ return 0;
+}
+
+/*
+ simple iconv_open() wrapper
+ */
+smb_iconv_t smb_iconv_open(const char *tocode, const char *fromcode)
+{
+ smb_iconv_t ret;
+ struct charset_functions *from, *to;
+
+ lazy_initialize_iconv();
+ from = charsets;
+ to = charsets;
+
+ ret = (smb_iconv_t)malloc(sizeof(*ret));
+ if (!ret) {
+ errno = ENOMEM;
+ return (smb_iconv_t)-1;
+ }
+ memset(ret, 0, sizeof(*ret));
+
+ ret->from_name = strdup(fromcode);
+ ret->to_name = strdup(tocode);
+
+ /* check for the simplest null conversion */
+ if (strcasecmp(fromcode, tocode) == 0) {
+ ret->direct = iconv_copy;
+ return ret;
+ }
+
+ /* check if we have a builtin function for this conversion */
+ from = find_charset_functions(fromcode);
+ if(from)ret->pull = from->pull;
+
+ to = find_charset_functions(tocode);
+ if(to)ret->push = to->push;
+
+ /* check if we can use iconv for this conversion */
+#ifdef HAVE_NATIVE_ICONV
+ if (!ret->pull) {
+ ret->cd_pull = iconv_open("UCS-2LE", fromcode);
+ if (ret->cd_pull != (iconv_t)-1)
+ ret->pull = sys_iconv;
+ }
+
+ if (!ret->push) {
+ ret->cd_push = iconv_open(tocode, "UCS-2LE");
+ if (ret->cd_push != (iconv_t)-1)
+ ret->push = sys_iconv;
+ }
+#endif
+
+ /* check if there is a module available that can do this conversion */
+ if (!ret->pull && NT_STATUS_IS_OK(smb_probe_module("charset", fromcode))) {
+ if(!(from = find_charset_functions(fromcode)))
+ DEBUG(0, ("Module %s doesn't provide charset %s!\n", fromcode, fromcode));
+ else
+ ret->pull = from->pull;
+ }
+
+ if (!ret->push && NT_STATUS_IS_OK(smb_probe_module("charset", tocode))) {
+ if(!(to = find_charset_functions(tocode)))
+ DEBUG(0, ("Module %s doesn't provide charset %s!\n", tocode, tocode));
+ else
+ ret->push = to->push;
+ }
+
+ if (!ret->push || !ret->pull) {
+ SAFE_FREE(ret->from_name);
+ SAFE_FREE(ret->to_name);
+ SAFE_FREE(ret);
+ errno = EINVAL;
+ return (smb_iconv_t)-1;
+ }
+
+ /* check for conversion to/from ucs2 */
+ if (strcasecmp(fromcode, "UCS-2LE") == 0 && to) {
+ ret->direct = to->push;
+ ret->push = ret->pull = NULL;
+ return ret;
+ }
+
+ if (strcasecmp(tocode, "UCS-2LE") == 0 && from) {
+ ret->direct = from->pull;
+ ret->push = ret->pull = NULL;
+ return ret;
+ }
+
+ /* Check if we can do the conversion direct */
+#ifdef HAVE_NATIVE_ICONV
+ if (strcasecmp(fromcode, "UCS-2LE") == 0) {
+ ret->direct = sys_iconv;
+ ret->cd_direct = ret->cd_push;
+ ret->cd_push = NULL;
+ return ret;
+ }
+ if (strcasecmp(tocode, "UCS-2LE") == 0) {
+ ret->direct = sys_iconv;
+ ret->cd_direct = ret->cd_pull;
+ ret->cd_pull = NULL;
+ return ret;
+ }
+#endif
+
+ return ret;
+}
+
+/*
+ simple iconv_close() wrapper
+*/
+int smb_iconv_close (smb_iconv_t cd)
+{
+#ifdef HAVE_NATIVE_ICONV
+ if (cd->cd_direct) iconv_close((iconv_t)cd->cd_direct);
+ if (cd->cd_pull) iconv_close((iconv_t)cd->cd_pull);
+ if (cd->cd_push) iconv_close((iconv_t)cd->cd_push);
+#endif
+
+ SAFE_FREE(cd->from_name);
+ SAFE_FREE(cd->to_name);
+
+ memset(cd, 0, sizeof(*cd));
+ SAFE_FREE(cd);
+ return 0;
+}
+
+
+/**********************************************************************
+ the following functions implement the builtin character sets in Samba
+ and also the "test" character sets that are designed to test
+ multi-byte character set support for english users
+***********************************************************************/
+
+static size_t ascii_pull(void *cd, char **inbuf, size_t *inbytesleft,
+ char **outbuf, size_t *outbytesleft)
+{
+ while (*inbytesleft >= 1 && *outbytesleft >= 2) {
+ (*outbuf)[0] = (*inbuf)[0];
+ (*outbuf)[1] = 0;
+ (*inbytesleft) -= 1;
+ (*outbytesleft) -= 2;
+ (*inbuf) += 1;
+ (*outbuf) += 2;
+ }
+
+ if (*inbytesleft > 0) {
+ errno = E2BIG;
+ return -1;
+ }
+
+ return 0;
+}
+
+static size_t ascii_push(void *cd, char **inbuf, size_t *inbytesleft,
+ char **outbuf, size_t *outbytesleft)
+{
+ int ir_count=0;
+
+ while (*inbytesleft >= 2 && *outbytesleft >= 1) {
+ (*outbuf)[0] = (*inbuf)[0] & 0x7F;
+ if ((*inbuf)[1]) ir_count++;
+ (*inbytesleft) -= 2;
+ (*outbytesleft) -= 1;
+ (*inbuf) += 2;
+ (*outbuf) += 1;
+ }
+
+ if (*inbytesleft == 1) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ if (*inbytesleft > 1) {
+ errno = E2BIG;
+ return -1;
+ }
+
+ return ir_count;
+}
+
+static size_t latin1_push(void *cd, char **inbuf, size_t *inbytesleft,
+ char **outbuf, size_t *outbytesleft)
+{
+ int ir_count=0;
+
+ while (*inbytesleft >= 2 && *outbytesleft >= 1) {
+ (*outbuf)[0] = (*inbuf)[0];
+ if ((*inbuf)[1]) ir_count++;
+ (*inbytesleft) -= 2;
+ (*outbytesleft) -= 1;
+ (*inbuf) += 2;
+ (*outbuf) += 1;
+ }
+
+ if (*inbytesleft == 1) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ if (*inbytesleft > 1) {
+ errno = E2BIG;
+ return -1;
+ }
+
+ return ir_count;
+}
+
+static size_t ucs2hex_pull(void *cd, char **inbuf, size_t *inbytesleft,
+ char **outbuf, size_t *outbytesleft)
+{
+ while (*inbytesleft >= 1 && *outbytesleft >= 2) {
+ unsigned v;
+
+ if ((*inbuf)[0] != '@') {
+ /* seven bit ascii case */
+ (*outbuf)[0] = (*inbuf)[0];
+ (*outbuf)[1] = 0;
+ (*inbytesleft) -= 1;
+ (*outbytesleft) -= 2;
+ (*inbuf) += 1;
+ (*outbuf) += 2;
+ continue;
+ }
+ /* it's a hex character */
+ if (*inbytesleft < 5) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ if (sscanf(&(*inbuf)[1], "%04x", &v) != 1) {
+ errno = EILSEQ;
+ return -1;
+ }
+
+ (*outbuf)[0] = v&0xff;
+ (*outbuf)[1] = v>>8;
+ (*inbytesleft) -= 5;
+ (*outbytesleft) -= 2;
+ (*inbuf) += 5;
+ (*outbuf) += 2;
+ }
+
+ if (*inbytesleft > 0) {
+ errno = E2BIG;
+ return -1;
+ }
+
+ return 0;
+}
+
+static size_t ucs2hex_push(void *cd, char **inbuf, size_t *inbytesleft,
+ char **outbuf, size_t *outbytesleft)
+{
+ while (*inbytesleft >= 2 && *outbytesleft >= 1) {
+ char buf[6];
+
+ if ((*inbuf)[1] == 0 &&
+ ((*inbuf)[0] & 0x80) == 0 &&
+ (*inbuf)[0] != '@') {
+ (*outbuf)[0] = (*inbuf)[0];
+ (*inbytesleft) -= 2;
+ (*outbytesleft) -= 1;
+ (*inbuf) += 2;
+ (*outbuf) += 1;
+ continue;
+ }
+ if (*outbytesleft < 5) {
+ errno = E2BIG;
+ return -1;
+ }
+ snprintf(buf, 6, "@%04x", SVAL(*inbuf, 0));
+ memcpy(*outbuf, buf, 5);
+ (*inbytesleft) -= 2;
+ (*outbytesleft) -= 5;
+ (*inbuf) += 2;
+ (*outbuf) += 5;
+ }
+
+ if (*inbytesleft == 1) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ if (*inbytesleft > 1) {
+ errno = E2BIG;
+ return -1;
+ }
+
+ return 0;
+}
+
+
+static size_t iconv_copy(void *cd, char **inbuf, size_t *inbytesleft,
+ char **outbuf, size_t *outbytesleft)
+{
+ int n;
+
+ n = MIN(*inbytesleft, *outbytesleft);
+
+ memmove(*outbuf, *inbuf, n);
+
+ (*inbytesleft) -= n;
+ (*outbytesleft) -= n;
+ (*inbuf) += n;
+ (*outbuf) += n;
+
+ if (*inbytesleft > 0) {
+ errno = E2BIG;
+ return -1;
+ }
+
+ return 0;
+}
+
+static size_t utf8_pull(void *cd, char **inbuf, size_t *inbytesleft,
+ char **outbuf, size_t *outbytesleft)
+{
+ while (*inbytesleft >= 1 && *outbytesleft >= 2) {
+ unsigned char *c = (unsigned char *)*inbuf;
+ unsigned char *uc = (unsigned char *)*outbuf;
+ int len = 1;
+
+ if ((c[0] & 0x80) == 0) {
+ uc[0] = c[0];
+ uc[1] = 0;
+ } else if ((c[0] & 0xf0) == 0xe0) {
+ if (*inbytesleft < 3) {
+ DEBUG(0,("short utf8 char\n"));
+ goto badseq;
+ }
+ uc[1] = ((c[0]&0xF)<<4) | ((c[1]>>2)&0xF);
+ uc[0] = (c[1]<<6) | (c[2]&0x3f);
+ len = 3;
+ } else if ((c[0] & 0xe0) == 0xc0) {
+ if (*inbytesleft < 2) {
+ DEBUG(0,("short utf8 char\n"));
+ goto badseq;
+ }
+ uc[1] = (c[0]>>2) & 0x7;
+ uc[0] = (c[0]<<6) | (c[1]&0x3f);
+ len = 2;
+ }
+
+ (*inbuf) += len;
+ (*inbytesleft) -= len;
+ (*outbytesleft) -= 2;
+ (*outbuf) += 2;
+ }
+
+ if (*inbytesleft > 0) {
+ errno = E2BIG;
+ return -1;
+ }
+
+ return 0;
+
+badseq:
+ errno = EINVAL;
+ return -1;
+}
+
+static size_t utf8_push(void *cd, char **inbuf, size_t *inbytesleft,
+ char **outbuf, size_t *outbytesleft)
+{
+ while (*inbytesleft >= 2 && *outbytesleft >= 1) {
+ unsigned char *c = (unsigned char *)*outbuf;
+ unsigned char *uc = (unsigned char *)*inbuf;
+ int len=1;
+
+ if (uc[1] & 0xf8) {
+ if (*outbytesleft < 3) {
+ DEBUG(0,("short utf8 write\n"));
+ goto toobig;
+ }
+ c[0] = 0xe0 | (uc[1]>>4);
+ c[1] = 0x80 | ((uc[1]&0xF)<<2) | (uc[0]>>6);
+ c[2] = 0x80 | (uc[0]&0x3f);
+ len = 3;
+ } else if (uc[1] | (uc[0] & 0x80)) {
+ if (*outbytesleft < 2) {
+ DEBUG(0,("short utf8 write\n"));
+ goto toobig;
+ }
+ c[0] = 0xc0 | (uc[1]<<2) | (uc[0]>>6);
+ c[1] = 0x80 | (uc[0]&0x3f);
+ len = 2;
+ } else {
+ c[0] = uc[0];
+ }
+
+
+ (*inbytesleft) -= 2;
+ (*outbytesleft) -= len;
+ (*inbuf) += 2;
+ (*outbuf) += len;
+ }
+
+ if (*inbytesleft == 1) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ if (*inbytesleft > 1) {
+ errno = E2BIG;
+ return -1;
+ }
+
+ return 0;
+
+toobig:
+ errno = E2BIG;
+ return -1;
+}
diff --git a/source/lib/interface.c b/source/lib/interface.c
new file mode 100644
index 00000000000..4d8010e31bc
--- /dev/null
+++ b/source/lib/interface.c
@@ -0,0 +1,338 @@
+/*
+ Unix SMB/CIFS implementation.
+ multiple interface handling
+ 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 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"
+
+static struct iface_struct *probed_ifaces;
+static int total_probed;
+
+struct in_addr allones_ip;
+struct in_addr loopback_ip;
+
+static struct interface *local_interfaces;
+
+#define ALLONES ((uint32)0xFFFFFFFF)
+#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 in_addr ip, BOOL CheckMask)
+{
+ struct interface *i;
+ if (is_zero_ip(ip)) return local_interfaces;
+
+ for (i=local_interfaces;i;i=i->next)
+ if (CheckMask) {
+ if (same_net(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(struct in_addr ip, struct in_addr nmask)
+{
+ struct interface *iface;
+ if (iface_find(ip, False)) {
+ DEBUG(3,("not adding duplicate interface %s\n",inet_ntoa(ip)));
+ return;
+ }
+
+ if (ip_equal(nmask, allones_ip)) {
+ DEBUG(3,("not adding non-broadcast interface %s\n",inet_ntoa(ip)));
+ return;
+ }
+
+ iface = (struct interface *)malloc(sizeof(*iface));
+ if (!iface) return;
+
+ ZERO_STRUCTPN(iface);
+
+ iface->ip = ip;
+ iface->nmask = nmask;
+ iface->bcast.s_addr = MKBCADDR(iface->ip.s_addr, iface->nmask.s_addr);
+
+ DLIST_ADD(local_interfaces, iface);
+
+ DEBUG(2,("added interface ip=%s ",inet_ntoa(iface->ip)));
+ DEBUG(2,("bcast=%s ",inet_ntoa(iface->bcast)));
+ DEBUG(2,("nmask=%s\n",inet_ntoa(iface->nmask)));
+}
+
+
+
+/****************************************************************************
+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(const char *token)
+{
+ struct in_addr ip, nmask;
+ char *p;
+ int i, added=0;
+
+ zero_ip(&ip);
+ zero_ip(&nmask);
+
+ /* 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(probed_ifaces[i].ip,
+ probed_ifaces[i].netmask);
+ added = 1;
+ }
+ }
+ if (added) return;
+
+ /* maybe it is a DNS name */
+ p = strchr_m(token,'/');
+ if (!p) {
+ ip = *interpret_addr2(token);
+ for (i=0;i<total_probed;i++) {
+ if (ip.s_addr == probed_ifaces[i].ip.s_addr &&
+ !ip_equal(allones_ip, probed_ifaces[i].netmask)) {
+ add_interface(probed_ifaces[i].ip,
+ probed_ifaces[i].netmask);
+ return;
+ }
+ }
+ DEBUG(2,("can't determine netmask for %s\n", token));
+ return;
+ }
+
+ /* parse it into an IP address/netmasklength pair */
+ *p++ = 0;
+
+ ip = *interpret_addr2(token);
+
+ if (strlen(p) > 2) {
+ nmask = *interpret_addr2(p);
+ } 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(ip, probed_ifaces[i].ip, nmask)) {
+ add_interface(probed_ifaces[i].ip, nmask);
+ return;
+ }
+ }
+ DEBUG(2,("Can't determine ip for broadcast address %s\n", token));
+ return;
+ }
+
+ add_interface(ip, nmask);
+}
+
+
+/****************************************************************************
+load the list of network interfaces
+****************************************************************************/
+void load_interfaces(void)
+{
+ const char **ptr;
+ int i;
+ struct iface_struct ifaces[MAX_INTERFACES];
+
+ ptr = lp_interfaces();
+
+ allones_ip = *interpret_addr2("255.255.255.255");
+ loopback_ip = *interpret_addr2("127.0.0.1");
+
+ SAFE_FREE(probed_ifaces);
+
+ /* dump the current interfaces if any */
+ while (local_interfaces) {
+ struct interface *iface = local_interfaces;
+ DLIST_REMOVE(local_interfaces, local_interfaces);
+ ZERO_STRUCTPN(iface);
+ SAFE_FREE(iface);
+ }
+
+ /* probe the kernel for interfaces */
+ total_probed = get_interfaces(ifaces, MAX_INTERFACES);
+
+ if (total_probed > 0) {
+ probed_ifaces = memdup(ifaces, sizeof(ifaces[0])*total_probed);
+ }
+
+ /* if we don't have a interfaces line then use all broadcast capable
+ 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"));
+ exit(1);
+ }
+ for (i=0;i<total_probed;i++) {
+ if (probed_ifaces[i].netmask.s_addr != allones_ip.s_addr &&
+ probed_ifaces[i].ip.s_addr != loopback_ip.s_addr) {
+ add_interface(probed_ifaces[i].ip,
+ probed_ifaces[i].netmask);
+ }
+ }
+ return;
+ }
+
+ if (ptr) {
+ while (*ptr) {
+ interpret_interface(*ptr);
+ ptr++;
+ }
+ }
+
+ if (!local_interfaces) {
+ DEBUG(0,("WARNING: no network interfaces found\n"));
+ }
+}
+
+
+/****************************************************************************
+return True if the list of probed interfaces has changed
+****************************************************************************/
+BOOL interfaces_changed(void)
+{
+ int n;
+ struct iface_struct ifaces[MAX_INTERFACES];
+
+ n = get_interfaces(ifaces, MAX_INTERFACES);
+
+ if ((n > 0 )&& (n != total_probed ||
+ memcmp(ifaces, probed_ifaces, sizeof(ifaces[0])*n))) {
+ return True;
+ }
+
+ return False;
+}
+
+
+/****************************************************************************
+ check if an IP is one of mine
+ **************************************************************************/
+BOOL ismyip(struct in_addr ip)
+{
+ struct interface *i;
+ for (i=local_interfaces;i;i=i->next)
+ if (ip_equal(i->ip,ip)) return True;
+ return False;
+}
+
+/****************************************************************************
+ check if a packet is from a local (known) net
+ **************************************************************************/
+BOOL is_local_net(struct in_addr from)
+{
+ struct interface *i;
+ for (i=local_interfaces;i;i=i->next) {
+ if((from.s_addr & i->nmask.s_addr) ==
+ (i->ip.s_addr & i->nmask.s_addr))
+ return True;
+ }
+ return False;
+}
+
+/****************************************************************************
+ how many interfaces do we have
+ **************************************************************************/
+int iface_count(void)
+{
+ int ret = 0;
+ struct interface *i;
+
+ for (i=local_interfaces;i;i=i->next)
+ ret++;
+ return ret;
+}
+
+/****************************************************************************
+ return the Nth interface
+ **************************************************************************/
+struct interface *get_interface(int n)
+{
+ struct interface *i;
+
+ for (i=local_interfaces;i && n;i=i->next)
+ n--;
+
+ if (i) return i;
+ return NULL;
+}
+
+/****************************************************************************
+ return IP of the Nth interface
+ **************************************************************************/
+struct in_addr *iface_n_ip(int n)
+{
+ struct interface *i;
+
+ for (i=local_interfaces;i && n;i=i->next)
+ n--;
+
+ if (i) return &i->ip;
+ return NULL;
+}
+
+/****************************************************************************
+ return bcast of the Nth interface
+ **************************************************************************/
+struct in_addr *iface_n_bcast(int n)
+{
+ struct interface *i;
+
+ for (i=local_interfaces;i && n;i=i->next)
+ n--;
+
+ if (i) return &i->bcast;
+ return NULL;
+}
+
+
+/* these 3 functions return the ip/bcast/nmask for the interface
+ most appropriate for the given ip address. If they can't find
+ an appropriate interface they return the requested field of the
+ first known interface. */
+
+struct in_addr *iface_ip(struct in_addr ip)
+{
+ struct interface *i = iface_find(ip, True);
+ return(i ? &i->ip : &local_interfaces->ip);
+}
+
+/*
+ return True if a IP is directly reachable on one of our interfaces
+*/
+BOOL iface_local(struct in_addr ip)
+{
+ return iface_find(ip, True) ? True : False;
+}
diff --git a/source/lib/interfaces.c b/source/lib/interfaces.c
new file mode 100644
index 00000000000..96f4b4cd94f
--- /dev/null
+++ b/source/lib/interfaces.c
@@ -0,0 +1,407 @@
+/*
+ Unix SMB/CIFS implementation.
+ return a list of network interfaces
+ 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 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.
+*/
+
+
+/* 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 <unistd.h>
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+#include <sys/ioctl.h>
+#include <sys/time.h>
+#include <net/if.h>
+
+#ifdef AUTOCONF_TEST
+struct iface_struct {
+ char name[16];
+ struct in_addr ip;
+ struct in_addr netmask;
+};
+#else
+#include "config.h"
+#include "interfaces.h"
+#endif
+
+#ifdef HAVE_SYS_TIME_H
+#include <sys/time.h>
+#endif
+
+#ifndef SIOCGIFCONF
+#ifdef HAVE_SYS_SOCKIO_H
+#include <sys/sockio.h>
+#endif
+#endif
+
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+
+#ifdef HAVE_STRINGS_H
+#include <strings.h>
+#endif
+
+#ifdef __COMPAR_FN_T
+#define QSORT_CAST (__compar_fn_t)
+#endif
+
+#ifndef QSORT_CAST
+#define QSORT_CAST (int (*)(const void *, const void *))
+#endif
+
+#if HAVE_IFACE_IFCONF
+
+/* this works for Linux 2.2, Solaris 2.5, SunOS4, HPUX 10.20, OSF1
+ V4.0, Ultrix 4.4, SCO Unix 3.2, IRIX 6.4 and FreeBSD 3.2.
+
+ It probably also works on any BSD style system. */
+
+/****************************************************************************
+ get the netmask address for a local interface
+****************************************************************************/
+static int _get_interfaces(struct iface_struct *ifaces, int max_interfaces)
+{
+ struct ifconf ifc;
+ char buff[8192];
+ int fd, i, n;
+ struct ifreq *ifr=NULL;
+ int total = 0;
+ struct in_addr ipaddr;
+ struct in_addr nmask;
+ char *iname;
+
+ if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) == -1) {
+ return -1;
+ }
+
+ ifc.ifc_len = sizeof(buff);
+ ifc.ifc_buf = buff;
+
+ if (ioctl(fd, SIOCGIFCONF, &ifc) != 0) {
+ close(fd);
+ return -1;
+ }
+
+ ifr = ifc.ifc_req;
+
+ n = ifc.ifc_len / sizeof(struct ifreq);
+
+ /* Loop through interfaces, looking for given IP address */
+ for (i=n-1;i>=0 && total < max_interfaces;i--) {
+ if (ioctl(fd, SIOCGIFADDR, &ifr[i]) != 0) {
+ continue;
+ }
+
+ iname = ifr[i].ifr_name;
+ ipaddr = (*(struct sockaddr_in *)&ifr[i].ifr_addr).sin_addr;
+
+ if (ioctl(fd, SIOCGIFFLAGS, &ifr[i]) != 0) {
+ continue;
+ }
+
+ if (!(ifr[i].ifr_flags & IFF_UP)) {
+ continue;
+ }
+
+ if (ioctl(fd, SIOCGIFNETMASK, &ifr[i]) != 0) {
+ continue;
+ }
+
+ nmask = ((struct sockaddr_in *)&ifr[i].ifr_addr)->sin_addr;
+
+ strncpy(ifaces[total].name, iname, sizeof(ifaces[total].name)-1);
+ ifaces[total].name[sizeof(ifaces[total].name)-1] = 0;
+ ifaces[total].ip = ipaddr;
+ ifaces[total].netmask = nmask;
+ total++;
+ }
+
+ close(fd);
+
+ return total;
+}
+
+#elif HAVE_IFACE_IFREQ
+
+#ifndef I_STR
+#include <sys/stropts.h>
+#endif
+
+/****************************************************************************
+this should cover most of the streams based systems
+Thanks to Andrej.Borsenkow@mow.siemens.ru for several ideas in this code
+****************************************************************************/
+static int _get_interfaces(struct iface_struct *ifaces, int max_interfaces)
+{
+ struct ifreq ifreq;
+ struct strioctl strioctl;
+ char buff[8192];
+ int fd, i, n;
+ struct ifreq *ifr=NULL;
+ int total = 0;
+ struct in_addr ipaddr;
+ struct in_addr nmask;
+ char *iname;
+
+ if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) == -1) {
+ return -1;
+ }
+
+ strioctl.ic_cmd = SIOCGIFCONF;
+ strioctl.ic_dp = buff;
+ strioctl.ic_len = sizeof(buff);
+ if (ioctl(fd, I_STR, &strioctl) < 0) {
+ close(fd);
+ return -1;
+ }
+
+ /* we can ignore the possible sizeof(int) here as the resulting
+ number of interface structures won't change */
+ n = strioctl.ic_len / sizeof(struct ifreq);
+
+ /* we will assume that the kernel returns the length as an int
+ at the start of the buffer if the offered size is a
+ multiple of the structure size plus an int */
+ if (n*sizeof(struct ifreq) + sizeof(int) == strioctl.ic_len) {
+ ifr = (struct ifreq *)(buff + sizeof(int));
+ } else {
+ ifr = (struct ifreq *)buff;
+ }
+
+ /* Loop through interfaces */
+
+ for (i = 0; i<n && total < max_interfaces; i++) {
+ ifreq = ifr[i];
+
+ strioctl.ic_cmd = SIOCGIFFLAGS;
+ strioctl.ic_dp = (char *)&ifreq;
+ strioctl.ic_len = sizeof(struct ifreq);
+ if (ioctl(fd, I_STR, &strioctl) != 0) {
+ continue;
+ }
+
+ if (!(ifreq.ifr_flags & IFF_UP)) {
+ continue;
+ }
+
+ strioctl.ic_cmd = SIOCGIFADDR;
+ strioctl.ic_dp = (char *)&ifreq;
+ strioctl.ic_len = sizeof(struct ifreq);
+ if (ioctl(fd, I_STR, &strioctl) != 0) {
+ continue;
+ }
+
+ ipaddr = (*(struct sockaddr_in *) &ifreq.ifr_addr).sin_addr;
+ iname = ifreq.ifr_name;
+
+ strioctl.ic_cmd = SIOCGIFNETMASK;
+ strioctl.ic_dp = (char *)&ifreq;
+ strioctl.ic_len = sizeof(struct ifreq);
+ if (ioctl(fd, I_STR, &strioctl) != 0) {
+ continue;
+ }
+
+ nmask = ((struct sockaddr_in *)&ifreq.ifr_addr)->sin_addr;
+
+ strncpy(ifaces[total].name, iname, sizeof(ifaces[total].name)-1);
+ ifaces[total].name[sizeof(ifaces[total].name)-1] = 0;
+ ifaces[total].ip = ipaddr;
+ ifaces[total].netmask = nmask;
+
+ total++;
+ }
+
+ close(fd);
+
+ return total;
+}
+
+#elif HAVE_IFACE_AIX
+
+/****************************************************************************
+this one is for AIX (tested on 4.2)
+****************************************************************************/
+static int _get_interfaces(struct iface_struct *ifaces, int max_interfaces)
+{
+ char buff[8192];
+ int fd, i;
+ struct ifconf ifc;
+ struct ifreq *ifr=NULL;
+ struct in_addr ipaddr;
+ struct in_addr nmask;
+ char *iname;
+ int total = 0;
+
+ if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) == -1) {
+ return -1;
+ }
+
+
+ ifc.ifc_len = sizeof(buff);
+ ifc.ifc_buf = buff;
+
+ if (ioctl(fd, SIOCGIFCONF, &ifc) != 0) {
+ close(fd);
+ return -1;
+ }
+
+ ifr = ifc.ifc_req;
+
+ /* Loop through interfaces */
+ i = ifc.ifc_len;
+
+ while (i > 0 && total < max_interfaces) {
+ unsigned inc;
+
+ inc = ifr->ifr_addr.sa_len;
+
+ if (ioctl(fd, SIOCGIFADDR, ifr) != 0) {
+ goto next;
+ }
+
+ ipaddr = (*(struct sockaddr_in *) &ifr->ifr_addr).sin_addr;
+ iname = ifr->ifr_name;
+
+ if (ioctl(fd, SIOCGIFFLAGS, ifr) != 0) {
+ goto next;
+ }
+
+ if (!(ifr->ifr_flags & IFF_UP)) {
+ goto next;
+ }
+
+ if (ioctl(fd, SIOCGIFNETMASK, ifr) != 0) {
+ goto next;
+ }
+
+ nmask = ((struct sockaddr_in *)&ifr->ifr_addr)->sin_addr;
+
+ strncpy(ifaces[total].name, iname, sizeof(ifaces[total].name)-1);
+ ifaces[total].name[sizeof(ifaces[total].name)-1] = 0;
+ ifaces[total].ip = ipaddr;
+ ifaces[total].netmask = nmask;
+
+ total++;
+
+ next:
+ /*
+ * Patch from Archie Cobbs (archie@whistle.com). The
+ * addresses in the SIOCGIFCONF interface list have a
+ * minimum size. Usually this doesn't matter, but if
+ * your machine has tunnel interfaces, etc. that have
+ * a zero length "link address", this does matter. */
+
+ if (inc < sizeof(ifr->ifr_addr))
+ inc = sizeof(ifr->ifr_addr);
+ inc += IFNAMSIZ;
+
+ ifr = (struct ifreq*) (((char*) ifr) + inc);
+ i -= inc;
+ }
+
+
+ close(fd);
+ return total;
+}
+
+#else /* a dummy version */
+static int _get_interfaces(struct iface_struct *ifaces, int max_interfaces)
+{
+ return -1;
+}
+#endif
+
+
+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;
+}
+
+
+#ifdef AUTOCONF_TEST
+/* this is the autoconf driver to test get_interfaces() */
+
+#define MAX_INTERFACES 128
+
+ int main()
+{
+ struct iface_struct ifaces[MAX_INTERFACES];
+ int total = get_interfaces(ifaces, MAX_INTERFACES);
+ int i;
+
+ printf("got %d interfaces:\n", total);
+ if (total <= 0) exit(1);
+
+ for (i=0;i<total;i++) {
+ printf("%-10s ", ifaces[i].name);
+ printf("IP=%s ", inet_ntoa(ifaces[i].ip));
+ printf("NETMASK=%s\n", inet_ntoa(ifaces[i].netmask));
+ }
+ return 0;
+}
+#endif
diff --git a/source/lib/ldap_escape.c b/source/lib/ldap_escape.c
new file mode 100644
index 00000000000..9e88b4999cc
--- /dev/null
+++ b/source/lib/ldap_escape.c
@@ -0,0 +1,90 @@
+/*
+ Unix SMB/CIFS implementation.
+ ldap filter argument escaping
+
+ Copyright (C) 1998, 1999, 2000 Luke Howard <lukeh@padl.com>,
+ Copyright (C) 2003 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 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"
+
+/**
+ * Escape a parameter to an LDAP filter string, so they cannot contain
+ * embeded ( ) * or \ chars which may cause it not to parse correctly.
+ *
+ * @param s The input string
+ *
+ * @return A string allocated with malloc(), containing the escaped string,
+ * and to be free()ed by the caller.
+ **/
+
+char *escape_ldap_string_alloc(const char *s)
+{
+ size_t len = strlen(s)+1;
+ char *output = malloc(len);
+ char *output_tmp;
+ const char *sub;
+ int i = 0;
+ char *p = output;
+
+ while (*s)
+ {
+ switch (*s)
+ {
+ case '*':
+ sub = "\\2a";
+ break;
+ case '(':
+ sub = "\\28";
+ break;
+ case ')':
+ sub = "\\29";
+ break;
+ case '\\':
+ sub = "\\5c";
+ break;
+ default:
+ sub = NULL;
+ break;
+ }
+
+ if (sub) {
+ len = len + 3;
+ output_tmp = realloc(output, len);
+ if (!output_tmp) {
+ SAFE_FREE(output);
+ return NULL;
+ }
+ output = output_tmp;
+
+ p = &output[i];
+ strncpy (p, sub, 3);
+ p += 3;
+ i += 3;
+
+ } else {
+ *p = *s;
+ p++;
+ i++;
+ }
+ s++;
+ }
+
+ *p = '\0';
+ return output;
+}
diff --git a/source/lib/md4.c b/source/lib/md4.c
new file mode 100644
index 00000000000..6803b7e8831
--- /dev/null
+++ b/source/lib/md4.c
@@ -0,0 +1,169 @@
+/*
+ Unix SMB/CIFS implementation.
+ a implementation of MD4 designed for use in the SMB authentication protocol
+ 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 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"
+
+/* NOTE: This code makes no attempt to be fast!
+
+ It assumes that a int is at least 32 bits long
+*/
+
+static uint32 A, B, C, D;
+
+static uint32 F(uint32 X, uint32 Y, uint32 Z)
+{
+ return (X&Y) | ((~X)&Z);
+}
+
+static uint32 G(uint32 X, uint32 Y, uint32 Z)
+{
+ return (X&Y) | (X&Z) | (Y&Z);
+}
+
+static uint32 H(uint32 X, uint32 Y, uint32 Z)
+{
+ return X^Y^Z;
+}
+
+static uint32 lshift(uint32 x, int s)
+{
+ x &= 0xFFFFFFFF;
+ return ((x<<s)&0xFFFFFFFF) | (x>>(32-s));
+}
+
+#define ROUND1(a,b,c,d,k,s) a = lshift(a + F(b,c,d) + X[k], s)
+#define ROUND2(a,b,c,d,k,s) a = lshift(a + G(b,c,d) + X[k] + (uint32)0x5A827999,s)
+#define ROUND3(a,b,c,d,k,s) a = lshift(a + H(b,c,d) + X[k] + (uint32)0x6ED9EBA1,s)
+
+/* this applies md4 to 64 byte chunks */
+static void mdfour64(uint32 *M)
+{
+ int j;
+ uint32 AA, BB, CC, DD;
+ uint32 X[16];
+
+ for (j=0;j<16;j++)
+ X[j] = M[j];
+
+ AA = A; BB = B; CC = C; DD = D;
+
+ ROUND1(A,B,C,D, 0, 3); ROUND1(D,A,B,C, 1, 7);
+ ROUND1(C,D,A,B, 2, 11); ROUND1(B,C,D,A, 3, 19);
+ ROUND1(A,B,C,D, 4, 3); ROUND1(D,A,B,C, 5, 7);
+ ROUND1(C,D,A,B, 6, 11); ROUND1(B,C,D,A, 7, 19);
+ ROUND1(A,B,C,D, 8, 3); ROUND1(D,A,B,C, 9, 7);
+ ROUND1(C,D,A,B, 10, 11); ROUND1(B,C,D,A, 11, 19);
+ ROUND1(A,B,C,D, 12, 3); ROUND1(D,A,B,C, 13, 7);
+ ROUND1(C,D,A,B, 14, 11); ROUND1(B,C,D,A, 15, 19);
+
+ ROUND2(A,B,C,D, 0, 3); ROUND2(D,A,B,C, 4, 5);
+ ROUND2(C,D,A,B, 8, 9); ROUND2(B,C,D,A, 12, 13);
+ ROUND2(A,B,C,D, 1, 3); ROUND2(D,A,B,C, 5, 5);
+ ROUND2(C,D,A,B, 9, 9); ROUND2(B,C,D,A, 13, 13);
+ ROUND2(A,B,C,D, 2, 3); ROUND2(D,A,B,C, 6, 5);
+ ROUND2(C,D,A,B, 10, 9); ROUND2(B,C,D,A, 14, 13);
+ ROUND2(A,B,C,D, 3, 3); ROUND2(D,A,B,C, 7, 5);
+ ROUND2(C,D,A,B, 11, 9); ROUND2(B,C,D,A, 15, 13);
+
+ ROUND3(A,B,C,D, 0, 3); ROUND3(D,A,B,C, 8, 9);
+ ROUND3(C,D,A,B, 4, 11); ROUND3(B,C,D,A, 12, 15);
+ ROUND3(A,B,C,D, 2, 3); ROUND3(D,A,B,C, 10, 9);
+ ROUND3(C,D,A,B, 6, 11); ROUND3(B,C,D,A, 14, 15);
+ ROUND3(A,B,C,D, 1, 3); ROUND3(D,A,B,C, 9, 9);
+ ROUND3(C,D,A,B, 5, 11); ROUND3(B,C,D,A, 13, 15);
+ ROUND3(A,B,C,D, 3, 3); ROUND3(D,A,B,C, 11, 9);
+ ROUND3(C,D,A,B, 7, 11); ROUND3(B,C,D,A, 15, 15);
+
+ A += AA; B += BB; C += CC; D += DD;
+
+ A &= 0xFFFFFFFF; B &= 0xFFFFFFFF;
+ C &= 0xFFFFFFFF; D &= 0xFFFFFFFF;
+
+ for (j=0;j<16;j++)
+ X[j] = 0;
+}
+
+static void copy64(uint32 *M, const unsigned char *in)
+{
+ int i;
+
+ for (i=0;i<16;i++)
+ M[i] = (in[i*4+3]<<24) | (in[i*4+2]<<16) |
+ (in[i*4+1]<<8) | (in[i*4+0]<<0);
+}
+
+static void copy4(unsigned char *out, uint32 x)
+{
+ out[0] = x&0xFF;
+ out[1] = (x>>8)&0xFF;
+ out[2] = (x>>16)&0xFF;
+ out[3] = (x>>24)&0xFF;
+}
+
+/* produce a md4 message digest from data of length n bytes */
+void mdfour(unsigned char *out, const unsigned char *in, int n)
+{
+ unsigned char buf[128];
+ uint32 M[16];
+ uint32 b = n * 8;
+ int i;
+
+ A = 0x67452301;
+ B = 0xefcdab89;
+ C = 0x98badcfe;
+ D = 0x10325476;
+
+ while (n > 64) {
+ copy64(M, in);
+ mdfour64(M);
+ in += 64;
+ n -= 64;
+ }
+
+ for (i=0;i<128;i++)
+ buf[i] = 0;
+ memcpy(buf, in, n);
+ buf[n] = 0x80;
+
+ if (n <= 55) {
+ copy4(buf+56, b);
+ copy64(M, buf);
+ mdfour64(M);
+ } else {
+ copy4(buf+120, b);
+ copy64(M, buf);
+ mdfour64(M);
+ copy64(M, buf+64);
+ mdfour64(M);
+ }
+
+ for (i=0;i<128;i++)
+ buf[i] = 0;
+ copy64(M, buf);
+
+ copy4(out, A);
+ copy4(out+4, B);
+ copy4(out+8, C);
+ copy4(out+12, D);
+
+ A = B = C = D = 0;
+}
+
+
diff --git a/source/lib/md5.c b/source/lib/md5.c
new file mode 100644
index 00000000000..2121b170479
--- /dev/null
+++ b/source/lib/md5.c
@@ -0,0 +1,247 @@
+/*
+ * This code implements the MD5 message-digest algorithm.
+ * The algorithm is due to Ron Rivest. This code was
+ * written by Colin Plumb in 1993, no copyright is claimed.
+ * This code is in the public domain; do with it what you wish.
+ *
+ * Equivalent code is available from RSA Data Security, Inc.
+ * This code has been tested against that, and is equivalent,
+ * except that you don't need to include two pages of legalese
+ * with every copy.
+ *
+ * To compute the message digest of a chunk of bytes, declare an
+ * MD5Context structure, pass it to MD5Init, call MD5Update as
+ * needed on buffers full of bytes, and then call MD5Final, which
+ * will fill a supplied 16-byte array with the digest.
+ */
+
+/* This code slightly modified to fit into Samba by
+ abartlet@samba.org Jun 2001 */
+
+#include "includes.h"
+
+#include "md5.h"
+
+static void MD5Transform(uint32 buf[4], uint32 const in[16]);
+
+/*
+ * Note: this code is harmless on little-endian machines.
+ */
+static void byteReverse(unsigned char *buf, unsigned longs)
+{
+ uint32 t;
+ do {
+ t = (uint32) ((unsigned) buf[3] << 8 | buf[2]) << 16 |
+ ((unsigned) buf[1] << 8 | buf[0]);
+ *(uint32 *) buf = t;
+ buf += 4;
+ } while (--longs);
+}
+
+/*
+ * Start MD5 accumulation. Set bit count to 0 and buffer to mysterious
+ * initialization constants.
+ */
+void MD5Init(struct MD5Context *ctx)
+{
+ ctx->buf[0] = 0x67452301;
+ ctx->buf[1] = 0xefcdab89;
+ ctx->buf[2] = 0x98badcfe;
+ ctx->buf[3] = 0x10325476;
+
+ ctx->bits[0] = 0;
+ ctx->bits[1] = 0;
+}
+
+/*
+ * Update context to reflect the concatenation of another buffer full
+ * of bytes.
+ */
+void MD5Update(struct MD5Context *ctx, unsigned char const *buf, unsigned len)
+{
+ register uint32 t;
+
+ /* Update bitcount */
+
+ t = ctx->bits[0];
+ if ((ctx->bits[0] = t + ((uint32) len << 3)) < t)
+ ctx->bits[1]++; /* Carry from low to high */
+ ctx->bits[1] += len >> 29;
+
+ t = (t >> 3) & 0x3f; /* Bytes already in shsInfo->data */
+
+ /* Handle any leading odd-sized chunks */
+
+ if (t) {
+ unsigned char *p = (unsigned char *) ctx->in + t;
+
+ t = 64 - t;
+ if (len < t) {
+ memmove(p, buf, len);
+ return;
+ }
+ memmove(p, buf, t);
+ byteReverse(ctx->in, 16);
+ MD5Transform(ctx->buf, (uint32 *) ctx->in);
+ buf += t;
+ len -= t;
+ }
+ /* Process data in 64-byte chunks */
+
+ while (len >= 64) {
+ memmove(ctx->in, buf, 64);
+ byteReverse(ctx->in, 16);
+ MD5Transform(ctx->buf, (uint32 *) ctx->in);
+ buf += 64;
+ len -= 64;
+ }
+
+ /* Handle any remaining bytes of data. */
+
+ memmove(ctx->in, buf, len);
+}
+
+/*
+ * Final wrapup - pad to 64-byte boundary with the bit pattern
+ * 1 0* (64-bit count of bits processed, MSB-first)
+ */
+void MD5Final(unsigned char digest[16], struct MD5Context *ctx)
+{
+ unsigned int count;
+ unsigned char *p;
+
+ /* Compute number of bytes mod 64 */
+ count = (ctx->bits[0] >> 3) & 0x3F;
+
+ /* Set the first char of padding to 0x80. This is safe since there is
+ always at least one byte free */
+ p = ctx->in + count;
+ *p++ = 0x80;
+
+ /* Bytes of padding needed to make 64 bytes */
+ count = 64 - 1 - count;
+
+ /* Pad out to 56 mod 64 */
+ if (count < 8) {
+ /* Two lots of padding: Pad the first block to 64 bytes */
+ memset(p, 0, count);
+ byteReverse(ctx->in, 16);
+ MD5Transform(ctx->buf, (uint32 *) ctx->in);
+
+ /* Now fill the next block with 56 bytes */
+ memset(ctx->in, 0, 56);
+ } else {
+ /* Pad block to 56 bytes */
+ memset(p, 0, count - 8);
+ }
+ byteReverse(ctx->in, 14);
+
+ /* Append length in bits and transform */
+ ((uint32 *) ctx->in)[14] = ctx->bits[0];
+ ((uint32 *) ctx->in)[15] = ctx->bits[1];
+
+ MD5Transform(ctx->buf, (uint32 *) ctx->in);
+ byteReverse((unsigned char *) ctx->buf, 4);
+ memmove(digest, ctx->buf, 16);
+ memset(ctx, 0, sizeof(ctx)); /* In case it's sensitive */
+}
+
+/* The four core functions - F1 is optimized somewhat */
+
+/* #define F1(x, y, z) (x & y | ~x & z) */
+#define F1(x, y, z) (z ^ (x & (y ^ z)))
+#define F2(x, y, z) F1(z, x, y)
+#define F3(x, y, z) (x ^ y ^ z)
+#define F4(x, y, z) (y ^ (x | ~z))
+
+/* This is the central step in the MD5 algorithm. */
+#define MD5STEP(f, w, x, y, z, data, s) \
+ ( w += f(x, y, z) + data, w = w<<s | w>>(32-s), w += x )
+
+/*
+ * The core of the MD5 algorithm, this alters an existing MD5 hash to
+ * reflect the addition of 16 longwords of new data. MD5Update blocks
+ * the data and converts bytes into longwords for this routine.
+ */
+static void MD5Transform(uint32 buf[4], uint32 const in[16])
+{
+ register uint32 a, b, c, d;
+
+ a = buf[0];
+ b = buf[1];
+ c = buf[2];
+ d = buf[3];
+
+ MD5STEP(F1, a, b, c, d, in[0] + 0xd76aa478, 7);
+ MD5STEP(F1, d, a, b, c, in[1] + 0xe8c7b756, 12);
+ MD5STEP(F1, c, d, a, b, in[2] + 0x242070db, 17);
+ MD5STEP(F1, b, c, d, a, in[3] + 0xc1bdceee, 22);
+ MD5STEP(F1, a, b, c, d, in[4] + 0xf57c0faf, 7);
+ MD5STEP(F1, d, a, b, c, in[5] + 0x4787c62a, 12);
+ MD5STEP(F1, c, d, a, b, in[6] + 0xa8304613, 17);
+ MD5STEP(F1, b, c, d, a, in[7] + 0xfd469501, 22);
+ MD5STEP(F1, a, b, c, d, in[8] + 0x698098d8, 7);
+ MD5STEP(F1, d, a, b, c, in[9] + 0x8b44f7af, 12);
+ MD5STEP(F1, c, d, a, b, in[10] + 0xffff5bb1, 17);
+ MD5STEP(F1, b, c, d, a, in[11] + 0x895cd7be, 22);
+ MD5STEP(F1, a, b, c, d, in[12] + 0x6b901122, 7);
+ MD5STEP(F1, d, a, b, c, in[13] + 0xfd987193, 12);
+ MD5STEP(F1, c, d, a, b, in[14] + 0xa679438e, 17);
+ MD5STEP(F1, b, c, d, a, in[15] + 0x49b40821, 22);
+
+ MD5STEP(F2, a, b, c, d, in[1] + 0xf61e2562, 5);
+ MD5STEP(F2, d, a, b, c, in[6] + 0xc040b340, 9);
+ MD5STEP(F2, c, d, a, b, in[11] + 0x265e5a51, 14);
+ MD5STEP(F2, b, c, d, a, in[0] + 0xe9b6c7aa, 20);
+ MD5STEP(F2, a, b, c, d, in[5] + 0xd62f105d, 5);
+ MD5STEP(F2, d, a, b, c, in[10] + 0x02441453, 9);
+ MD5STEP(F2, c, d, a, b, in[15] + 0xd8a1e681, 14);
+ MD5STEP(F2, b, c, d, a, in[4] + 0xe7d3fbc8, 20);
+ MD5STEP(F2, a, b, c, d, in[9] + 0x21e1cde6, 5);
+ MD5STEP(F2, d, a, b, c, in[14] + 0xc33707d6, 9);
+ MD5STEP(F2, c, d, a, b, in[3] + 0xf4d50d87, 14);
+ MD5STEP(F2, b, c, d, a, in[8] + 0x455a14ed, 20);
+ MD5STEP(F2, a, b, c, d, in[13] + 0xa9e3e905, 5);
+ MD5STEP(F2, d, a, b, c, in[2] + 0xfcefa3f8, 9);
+ MD5STEP(F2, c, d, a, b, in[7] + 0x676f02d9, 14);
+ MD5STEP(F2, b, c, d, a, in[12] + 0x8d2a4c8a, 20);
+
+ MD5STEP(F3, a, b, c, d, in[5] + 0xfffa3942, 4);
+ MD5STEP(F3, d, a, b, c, in[8] + 0x8771f681, 11);
+ MD5STEP(F3, c, d, a, b, in[11] + 0x6d9d6122, 16);
+ MD5STEP(F3, b, c, d, a, in[14] + 0xfde5380c, 23);
+ MD5STEP(F3, a, b, c, d, in[1] + 0xa4beea44, 4);
+ MD5STEP(F3, d, a, b, c, in[4] + 0x4bdecfa9, 11);
+ MD5STEP(F3, c, d, a, b, in[7] + 0xf6bb4b60, 16);
+ MD5STEP(F3, b, c, d, a, in[10] + 0xbebfbc70, 23);
+ MD5STEP(F3, a, b, c, d, in[13] + 0x289b7ec6, 4);
+ MD5STEP(F3, d, a, b, c, in[0] + 0xeaa127fa, 11);
+ MD5STEP(F3, c, d, a, b, in[3] + 0xd4ef3085, 16);
+ MD5STEP(F3, b, c, d, a, in[6] + 0x04881d05, 23);
+ MD5STEP(F3, a, b, c, d, in[9] + 0xd9d4d039, 4);
+ MD5STEP(F3, d, a, b, c, in[12] + 0xe6db99e5, 11);
+ MD5STEP(F3, c, d, a, b, in[15] + 0x1fa27cf8, 16);
+ MD5STEP(F3, b, c, d, a, in[2] + 0xc4ac5665, 23);
+
+ MD5STEP(F4, a, b, c, d, in[0] + 0xf4292244, 6);
+ MD5STEP(F4, d, a, b, c, in[7] + 0x432aff97, 10);
+ MD5STEP(F4, c, d, a, b, in[14] + 0xab9423a7, 15);
+ MD5STEP(F4, b, c, d, a, in[5] + 0xfc93a039, 21);
+ MD5STEP(F4, a, b, c, d, in[12] + 0x655b59c3, 6);
+ MD5STEP(F4, d, a, b, c, in[3] + 0x8f0ccc92, 10);
+ MD5STEP(F4, c, d, a, b, in[10] + 0xffeff47d, 15);
+ MD5STEP(F4, b, c, d, a, in[1] + 0x85845dd1, 21);
+ MD5STEP(F4, a, b, c, d, in[8] + 0x6fa87e4f, 6);
+ MD5STEP(F4, d, a, b, c, in[15] + 0xfe2ce6e0, 10);
+ MD5STEP(F4, c, d, a, b, in[6] + 0xa3014314, 15);
+ MD5STEP(F4, b, c, d, a, in[13] + 0x4e0811a1, 21);
+ MD5STEP(F4, a, b, c, d, in[4] + 0xf7537e82, 6);
+ MD5STEP(F4, d, a, b, c, in[11] + 0xbd3af235, 10);
+ MD5STEP(F4, c, d, a, b, in[2] + 0x2ad7d2bb, 15);
+ MD5STEP(F4, b, c, d, a, in[9] + 0xeb86d391, 21);
+
+ buf[0] += a;
+ buf[1] += b;
+ buf[2] += c;
+ buf[3] += d;
+}
diff --git a/source/lib/messages.c b/source/lib/messages.c
new file mode 100644
index 00000000000..8706ede7065
--- /dev/null
+++ b/source/lib/messages.c
@@ -0,0 +1,596 @@
+/*
+ Unix SMB/CIFS implementation.
+ Samba internal messaging functions
+ Copyright (C) Andrew Tridgell 2000
+ Copyright (C) 2001 by Martin Pool
+ Copyright (C) 2002 by Jeremy Allison
+
+ This program is free software; you can 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.
+*/
+
+/**
+ @defgroup messages Internal messaging framework
+ @{
+ @file messages.c
+
+ @brief Module for internal messaging between Samba daemons.
+
+ The idea is that if a part of Samba wants to do communication with
+ another Samba process then it will do a message_register() of a
+ dispatch function, and use message_send_pid() to send messages to
+ that process.
+
+ The dispatch function is given the pid of the sender, and it can
+ use that to reply by message_send_pid(). See ping_message() for a
+ simple example.
+
+ @caution Dispatch functions must be able to cope with incoming
+ messages on an *odd* byte boundary.
+
+ This system doesn't have any inherent size limitations but is not
+ very efficient for large messages or when messages are sent in very
+ quick succession.
+
+*/
+
+#include "includes.h"
+
+/* the locking database handle */
+static TDB_CONTEXT *tdb;
+static int received_signal;
+
+/* change the message version with any incompatible changes in the protocol */
+#define MESSAGE_VERSION 1
+
+struct message_rec {
+ int msg_version;
+ int msg_type;
+ pid_t dest;
+ pid_t src;
+ size_t len;
+};
+
+/* we have a linked list of dispatch handlers */
+static struct dispatch_fns {
+ struct dispatch_fns *next, *prev;
+ int msg_type;
+ void (*fn)(int msg_type, pid_t pid, void *buf, size_t len);
+} *dispatch_fns;
+
+/****************************************************************************
+ Notifications come in as signals.
+****************************************************************************/
+
+static void sig_usr1(void)
+{
+ received_signal = 1;
+ sys_select_signal();
+}
+
+/****************************************************************************
+ A useful function for testing the message system.
+****************************************************************************/
+
+static void ping_message(int msg_type, pid_t src, void *buf, size_t len)
+{
+ const char *msg = buf ? buf : "none";
+ DEBUG(1,("INFO: Received PING message from PID %u [%s]\n",(unsigned int)src, msg));
+ message_send_pid(src, MSG_PONG, buf, len, True);
+}
+
+/****************************************************************************
+ Initialise the messaging functions.
+****************************************************************************/
+
+BOOL message_init(void)
+{
+ if (tdb) return True;
+
+ tdb = tdb_open_log(lock_path("messages.tdb"),
+ 0, TDB_CLEAR_IF_FIRST|TDB_DEFAULT,
+ O_RDWR|O_CREAT,0600);
+
+ if (!tdb) {
+ DEBUG(0,("ERROR: Failed to initialise messages database\n"));
+ return False;
+ }
+
+ CatchSignal(SIGUSR1, SIGNAL_CAST sig_usr1);
+
+ message_register(MSG_PING, ping_message);
+
+ /* Register some debugging related messages */
+
+ register_msg_pool_usage();
+ register_dmalloc_msgs();
+
+ return True;
+}
+
+/*******************************************************************
+ Form a static tdb key from a pid.
+******************************************************************/
+
+static TDB_DATA message_key_pid(pid_t pid)
+{
+ static char key[20];
+ TDB_DATA kbuf;
+
+ slprintf(key, sizeof(key)-1, "PID/%d", (int)pid);
+
+ kbuf.dptr = (char *)key;
+ kbuf.dsize = strlen(key)+1;
+ return kbuf;
+}
+
+/****************************************************************************
+ Notify a process that it has a message. If the process doesn't exist
+ then delete its record in the database.
+****************************************************************************/
+
+static BOOL message_notify(pid_t pid)
+{
+ /*
+ * Doing kill with a non-positive pid causes messages to be
+ * sent to places we don't want.
+ */
+
+ SMB_ASSERT(pid > 0);
+
+ if (kill(pid, SIGUSR1) == -1) {
+ if (errno == ESRCH) {
+ DEBUG(2,("pid %d doesn't exist - deleting messages record\n", (int)pid));
+ tdb_delete(tdb, message_key_pid(pid));
+ } else {
+ DEBUG(2,("message to process %d failed - %s\n", (int)pid, strerror(errno)));
+ }
+ return False;
+ }
+ return True;
+}
+
+/****************************************************************************
+ Send a message to a particular pid.
+****************************************************************************/
+
+static BOOL message_send_pid_internal(pid_t pid, int msg_type, const void *buf, size_t len,
+ BOOL duplicates_allowed, unsigned int timeout)
+{
+ TDB_DATA kbuf;
+ TDB_DATA dbuf;
+ TDB_DATA old_dbuf;
+ struct message_rec rec;
+ char *ptr;
+ struct message_rec prec;
+
+ /*
+ * Doing kill with a non-positive pid causes messages to be
+ * sent to places we don't want.
+ */
+
+ SMB_ASSERT(pid > 0);
+
+ rec.msg_version = MESSAGE_VERSION;
+ rec.msg_type = msg_type;
+ rec.dest = pid;
+ rec.src = sys_getpid();
+ rec.len = len;
+
+ kbuf = message_key_pid(pid);
+
+ dbuf.dptr = (void *)malloc(len + sizeof(rec));
+ if (!dbuf.dptr)
+ return False;
+
+ memcpy(dbuf.dptr, &rec, sizeof(rec));
+ if (len > 0)
+ memcpy((void *)((char*)dbuf.dptr+sizeof(rec)), buf, len);
+
+ dbuf.dsize = len + sizeof(rec);
+
+ if (duplicates_allowed) {
+
+ /* If duplicates are allowed we can just append the message and return. */
+
+ /* lock the record for the destination */
+ if (timeout) {
+ if (tdb_chainlock_with_timeout(tdb, kbuf, timeout) == -1) {
+ DEBUG(0,("message_send_pid_internal: failed to get chainlock with timeout %ul.\n", timeout));
+ return False;
+ }
+ } else {
+ if (tdb_chainlock(tdb, kbuf) == -1) {
+ DEBUG(0,("message_send_pid_internal: failed to get chainlock.\n"));
+ return False;
+ }
+ }
+ tdb_append(tdb, kbuf, dbuf);
+ tdb_chainunlock(tdb, kbuf);
+
+ SAFE_FREE(dbuf.dptr);
+ errno = 0; /* paranoia */
+ return message_notify(pid);
+ }
+
+ /* lock the record for the destination */
+ if (timeout) {
+ if (tdb_chainlock_with_timeout(tdb, kbuf, timeout) == -1) {
+ DEBUG(0,("message_send_pid_internal: failed to get chainlock with timeout %ul.\n", timeout));
+ return False;
+ }
+ } else {
+ if (tdb_chainlock(tdb, kbuf) == -1) {
+ DEBUG(0,("message_send_pid_internal: failed to get chainlock.\n"));
+ return False;
+ }
+ }
+
+ old_dbuf = tdb_fetch(tdb, kbuf);
+
+ if (!old_dbuf.dptr) {
+ /* its a new record */
+
+ tdb_store(tdb, kbuf, dbuf, TDB_REPLACE);
+ tdb_chainunlock(tdb, kbuf);
+
+ SAFE_FREE(dbuf.dptr);
+ errno = 0; /* paranoia */
+ return message_notify(pid);
+ }
+
+ /* Not a new record. Check for duplicates. */
+
+ for(ptr = (char *)old_dbuf.dptr; ptr < old_dbuf.dptr + old_dbuf.dsize; ) {
+ /*
+ * First check if the message header matches, then, if it's a non-zero
+ * sized message, check if the data matches. If so it's a duplicate and
+ * we can discard it. JRA.
+ */
+
+ if (!memcmp(ptr, &rec, sizeof(rec))) {
+ if (!len || (len && !memcmp( ptr + sizeof(rec), buf, len))) {
+ tdb_chainunlock(tdb, kbuf);
+ DEBUG(10,("message_send_pid_internal: discarding duplicate message.\n"));
+ SAFE_FREE(dbuf.dptr);
+ SAFE_FREE(old_dbuf.dptr);
+ return True;
+ }
+ }
+ memcpy(&prec, ptr, sizeof(prec));
+ ptr += sizeof(rec) + prec.len;
+ }
+
+ /* we're adding to an existing entry */
+
+ tdb_append(tdb, kbuf, dbuf);
+ tdb_chainunlock(tdb, kbuf);
+
+ SAFE_FREE(old_dbuf.dptr);
+ SAFE_FREE(dbuf.dptr);
+
+ errno = 0; /* paranoia */
+ return message_notify(pid);
+}
+
+/****************************************************************************
+ Send a message to a particular pid - no timeout.
+****************************************************************************/
+
+BOOL message_send_pid(pid_t pid, int msg_type, const void *buf, size_t len, BOOL duplicates_allowed)
+{
+ return message_send_pid_internal(pid, msg_type, buf, len, duplicates_allowed, 0);
+}
+
+/****************************************************************************
+ Send a message to a particular pid, with timeout in seconds.
+****************************************************************************/
+
+BOOL message_send_pid_with_timeout(pid_t pid, int msg_type, const void *buf, size_t len,
+ BOOL duplicates_allowed, unsigned int timeout)
+{
+ return message_send_pid_internal(pid, msg_type, buf, len, duplicates_allowed, timeout);
+}
+
+/****************************************************************************
+ Count the messages pending for a particular pid. Expensive....
+****************************************************************************/
+
+unsigned int messages_pending_for_pid(pid_t pid)
+{
+ TDB_DATA kbuf;
+ TDB_DATA dbuf;
+ char *buf;
+ unsigned int message_count = 0;
+
+ kbuf = message_key_pid(sys_getpid());
+
+ dbuf = tdb_fetch(tdb, kbuf);
+ if (dbuf.dptr == NULL || dbuf.dsize == 0) {
+ SAFE_FREE(dbuf.dptr);
+ return 0;
+ }
+
+ for (buf = dbuf.dptr; dbuf.dsize > sizeof(struct message_rec);) {
+ struct message_rec rec;
+ memcpy(&rec, buf, sizeof(rec));
+ buf += (sizeof(rec) + rec.len);
+ dbuf.dsize -= (sizeof(rec) + rec.len);
+ message_count++;
+ }
+
+ SAFE_FREE(dbuf.dptr);
+ return message_count;
+}
+
+/****************************************************************************
+ Retrieve all messages for the current process.
+****************************************************************************/
+
+static BOOL retrieve_all_messages(char **msgs_buf, size_t *total_len)
+{
+ TDB_DATA kbuf;
+ TDB_DATA dbuf;
+ TDB_DATA null_dbuf;
+
+ ZERO_STRUCT(null_dbuf);
+
+ *msgs_buf = NULL;
+ *total_len = 0;
+
+ kbuf = message_key_pid(sys_getpid());
+
+ if (tdb_chainlock(tdb, kbuf) == -1)
+ return False;
+
+ dbuf = tdb_fetch(tdb, kbuf);
+ /*
+ * Replace with an empty record to keep the allocated
+ * space in the tdb.
+ */
+ tdb_store(tdb, kbuf, null_dbuf, TDB_REPLACE);
+ tdb_chainunlock(tdb, kbuf);
+
+ if (dbuf.dptr == NULL || dbuf.dsize == 0) {
+ SAFE_FREE(dbuf.dptr);
+ return False;
+ }
+
+ *msgs_buf = dbuf.dptr;
+ *total_len = dbuf.dsize;
+
+ return True;
+}
+
+/****************************************************************************
+ Parse out the next message for the current process.
+****************************************************************************/
+
+static BOOL message_recv(char *msgs_buf, size_t total_len, int *msg_type, pid_t *src, char **buf, size_t *len)
+{
+ struct message_rec rec;
+ char *ret_buf = *buf;
+
+ *buf = NULL;
+ *len = 0;
+
+ if (total_len - (ret_buf - msgs_buf) < sizeof(rec))
+ return False;
+
+ memcpy(&rec, ret_buf, sizeof(rec));
+ ret_buf += sizeof(rec);
+
+ if (rec.msg_version != MESSAGE_VERSION) {
+ DEBUG(0,("message version %d received (expected %d)\n", rec.msg_version, MESSAGE_VERSION));
+ return False;
+ }
+
+ if (rec.len > 0) {
+ if (total_len - (ret_buf - msgs_buf) < rec.len)
+ return False;
+ }
+
+ *len = rec.len;
+ *msg_type = rec.msg_type;
+ *src = rec.src;
+ *buf = ret_buf;
+
+ return True;
+}
+
+/****************************************************************************
+ Receive and dispatch any messages pending for this process.
+ Notice that all dispatch handlers for a particular msg_type get called,
+ so you can register multiple handlers for a message.
+ *NOTE*: Dispatch functions must be able to cope with incoming
+ messages on an *odd* byte boundary.
+****************************************************************************/
+
+void message_dispatch(void)
+{
+ int msg_type;
+ pid_t src;
+ char *buf;
+ char *msgs_buf;
+ size_t len, total_len;
+ struct dispatch_fns *dfn;
+ int n_handled;
+
+ if (!received_signal)
+ return;
+
+ DEBUG(10,("message_dispatch: received_signal = %d\n", received_signal));
+
+ received_signal = 0;
+
+ if (!retrieve_all_messages(&msgs_buf, &total_len))
+ return;
+
+ for (buf = msgs_buf; message_recv(msgs_buf, total_len, &msg_type, &src, &buf, &len); buf += len) {
+ DEBUG(10,("message_dispatch: received msg_type=%d src_pid=%u\n",
+ msg_type, (unsigned int) src));
+ n_handled = 0;
+ for (dfn = dispatch_fns; dfn; dfn = dfn->next) {
+ if (dfn->msg_type == msg_type) {
+ DEBUG(10,("message_dispatch: processing message of type %d.\n", msg_type));
+ dfn->fn(msg_type, src, len ? (void *)buf : NULL, len);
+ n_handled++;
+ }
+ }
+ if (!n_handled) {
+ DEBUG(5,("message_dispatch: warning: no handlers registed for "
+ "msg_type %d in pid %u\n",
+ msg_type, (unsigned int)sys_getpid()));
+ }
+ }
+ SAFE_FREE(msgs_buf);
+}
+
+/****************************************************************************
+ Register a dispatch function for a particular message type.
+ *NOTE*: Dispatch functions must be able to cope with incoming
+ messages on an *odd* byte boundary.
+****************************************************************************/
+
+void message_register(int msg_type,
+ void (*fn)(int msg_type, pid_t pid, void *buf, size_t len))
+{
+ struct dispatch_fns *dfn;
+
+ dfn = (struct dispatch_fns *)malloc(sizeof(*dfn));
+
+ if (dfn != NULL) {
+
+ ZERO_STRUCTPN(dfn);
+
+ dfn->msg_type = msg_type;
+ dfn->fn = fn;
+
+ DLIST_ADD(dispatch_fns, dfn);
+ }
+ else {
+
+ DEBUG(0,("message_register: Not enough memory. malloc failed!\n"));
+ }
+}
+
+/****************************************************************************
+ De-register the function for a particular message type.
+****************************************************************************/
+
+void message_deregister(int msg_type)
+{
+ struct dispatch_fns *dfn, *next;
+
+ for (dfn = dispatch_fns; dfn; dfn = next) {
+ next = dfn->next;
+ if (dfn->msg_type == msg_type) {
+ DLIST_REMOVE(dispatch_fns, dfn);
+ SAFE_FREE(dfn);
+ }
+ }
+}
+
+struct msg_all {
+ int msg_type;
+ uint32 msg_flag;
+ const void *buf;
+ size_t len;
+ BOOL duplicates;
+ int n_sent;
+};
+
+/****************************************************************************
+ Send one of the messages for the broadcast.
+****************************************************************************/
+
+static int traverse_fn(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf, void *state)
+{
+ struct connections_data crec;
+ struct msg_all *msg_all = (struct msg_all *)state;
+
+ if (dbuf.dsize != sizeof(crec))
+ return 0;
+
+ memcpy(&crec, dbuf.dptr, sizeof(crec));
+
+ if (crec.cnum != -1)
+ return 0;
+
+ /* Don't send if the receiver hasn't registered an interest. */
+
+ if(!(crec.bcast_msg_flags & msg_all->msg_flag))
+ return 0;
+
+ /* If the msg send fails because the pid was not found (i.e. smbd died),
+ * the msg has already been deleted from the messages.tdb.*/
+
+ if (!message_send_pid(crec.pid, msg_all->msg_type,
+ msg_all->buf, msg_all->len,
+ msg_all->duplicates)) {
+
+ /* If the pid was not found delete the entry from connections.tdb */
+
+ if (errno == ESRCH) {
+ DEBUG(2,("pid %u doesn't exist - deleting connections %d [%s]\n",
+ (unsigned int)crec.pid, crec.cnum, crec.name));
+ tdb_delete(the_tdb, kbuf);
+ }
+ }
+ msg_all->n_sent++;
+ return 0;
+}
+
+/**
+ * Send a message to all smbd processes.
+ *
+ * It isn't very efficient, but should be OK for the sorts of
+ * applications that use it. When we need efficient broadcast we can add
+ * it.
+ *
+ * @param n_sent Set to the number of messages sent. This should be
+ * equal to the number of processes, but be careful for races.
+ *
+ * @retval True for success.
+ **/
+BOOL message_send_all(TDB_CONTEXT *conn_tdb, int msg_type,
+ const void *buf, size_t len,
+ BOOL duplicates_allowed,
+ int *n_sent)
+{
+ struct msg_all msg_all;
+
+ msg_all.msg_type = msg_type;
+ if (msg_type < 1000)
+ msg_all.msg_flag = FLAG_MSG_GENERAL;
+ else if (msg_type > 1000 && msg_type < 2000)
+ msg_all.msg_flag = FLAG_MSG_NMBD;
+ else if (msg_type > 2000 && msg_type < 3000)
+ msg_all.msg_flag = FLAG_MSG_PRINTING;
+ else if (msg_type > 3000 && msg_type < 4000)
+ msg_all.msg_flag = FLAG_MSG_SMBD;
+ else
+ return False;
+
+ msg_all.buf = buf;
+ msg_all.len = len;
+ msg_all.duplicates = duplicates_allowed;
+ msg_all.n_sent = 0;
+
+ tdb_traverse(conn_tdb, traverse_fn, &msg_all);
+ if (n_sent)
+ *n_sent = msg_all.n_sent;
+ return True;
+}
+/** @} **/
diff --git a/source/lib/module.c b/source/lib/module.c
new file mode 100644
index 00000000000..2abe918ef44
--- /dev/null
+++ b/source/lib/module.c
@@ -0,0 +1,304 @@
+/*
+ Unix SMB/CIFS implementation.
+ module loading system
+
+ Copyright (C) Jelmer Vernooij 2002-2003
+ 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 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"
+
+#ifdef HAVE_DLOPEN
+
+/* Load a dynamic module. Only log a level 0 error if we are not checking
+ for the existence of a module (probling). */
+
+static NTSTATUS do_smb_load_module(const char *module_name, BOOL is_probe)
+{
+ void *handle;
+ init_module_function *init;
+ NTSTATUS status;
+ const char *error;
+
+ /* Always try to use LAZY symbol resolving; if the plugin has
+ * backwards compatibility, there might be symbols in the
+ * plugin referencing to old (removed) functions
+ */
+ handle = sys_dlopen(module_name, RTLD_LAZY);
+
+ if(!handle) {
+ int level = is_probe ? 3 : 0;
+ error = sys_dlerror();
+ DEBUG(level, ("Error loading module '%s': %s\n", module_name, error ? error : ""));
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ init = (init_module_function *)sys_dlsym(handle, "init_module");
+
+ /* we must check sys_dlerror() to determine if it worked, because
+ sys_dlsym() can validly return NULL */
+ error = sys_dlerror();
+ if (error) {
+ DEBUG(0, ("Error trying to resolve symbol 'init_module' in %s: %s\n",
+ module_name, error));
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ status = init();
+
+ DEBUG(2, ("Module '%s' loaded\n", module_name));
+
+ return status;
+}
+
+NTSTATUS smb_load_module(const char *module_name)
+{
+ return do_smb_load_module(module_name, False);
+}
+
+/* Load all modules in list and return number of
+ * modules that has been successfully loaded */
+int smb_load_modules(const char **modules)
+{
+ int i;
+ int success = 0;
+
+ for(i = 0; modules[i]; i++){
+ if(NT_STATUS_IS_OK(smb_load_module(modules[i]))) {
+ success++;
+ }
+ }
+
+ DEBUG(2, ("%d modules successfully loaded\n", success));
+
+ return success;
+}
+
+NTSTATUS smb_probe_module(const char *subsystem, const char *module)
+{
+ pstring full_path;
+
+ /* Check for absolute path */
+
+ /* if we make any 'samba multibyte string'
+ calls here, we break
+ for loading string modules */
+
+ DEBUG(5, ("Probing module '%s'\n", module));
+
+ if (module[0] == '/')
+ return do_smb_load_module(module, True);
+
+ pstrcpy(full_path, lib_path(subsystem));
+ pstrcat(full_path, "/");
+ pstrcat(full_path, module);
+ pstrcat(full_path, ".");
+ pstrcat(full_path, shlib_ext());
+
+ DEBUG(5, ("Probing module '%s': Trying to load from %s\n", module, full_path));
+
+ return do_smb_load_module(full_path, True);
+}
+
+#else /* HAVE_DLOPEN */
+
+NTSTATUS smb_load_module(const char *module_name)
+{
+ DEBUG(0,("This samba executable has not been built with plugin support\n"));
+ return NT_STATUS_NOT_SUPPORTED;
+}
+
+int smb_load_modules(const char **modules)
+{
+ DEBUG(0,("This samba executable has not been built with plugin support\n"));
+ return -1;
+}
+
+NTSTATUS smb_probe_module(const char *subsystem, const char *module)
+{
+ DEBUG(0,("This samba executable has not been built with plugin support, not probing\n"));
+ return NT_STATUS_NOT_SUPPORTED;
+}
+
+#endif /* HAVE_DLOPEN */
+
+void init_modules(void)
+{
+ /* FIXME: This can cause undefined symbol errors :
+ * smb_register_vfs() isn't available in nmbd, for example */
+ if(lp_preload_modules())
+ smb_load_modules(lp_preload_modules());
+}
+
+
+/***************************************************************************
+ * This Function registers a idle event
+ *
+ * the registered funtions are run periodically
+ * and maybe shutdown idle connections (e.g. to an LDAP server)
+ ***************************************************************************/
+static smb_event_id_t smb_idle_event_id = 1;
+
+struct smb_idle_list_ent {
+ struct smb_idle_list_ent *prev,*next;
+ smb_event_id_t id;
+ smb_idle_event_fn *fn;
+ void *data;
+ time_t interval;
+ time_t lastrun;
+};
+
+static struct smb_idle_list_ent *smb_idle_event_list = NULL;
+
+smb_event_id_t smb_register_idle_event(smb_idle_event_fn *fn, void *data, time_t interval)
+{
+ struct smb_idle_list_ent *event;
+
+ if (!fn) {
+ return SMB_EVENT_ID_INVALID;
+ }
+
+ event = (struct smb_idle_list_ent *)malloc(sizeof(struct smb_idle_list_ent));
+ if (!event) {
+ DEBUG(0,("malloc() failed!\n"));
+ return SMB_EVENT_ID_INVALID;
+ }
+ event->fn = fn;
+ event->data = data;
+ event->interval = interval;
+ event->lastrun = 0;
+ event->id = smb_idle_event_id++;
+
+ DLIST_ADD(smb_idle_event_list,event);
+
+ return event->id;
+}
+
+BOOL smb_unregister_idle_event(smb_event_id_t id)
+{
+ struct smb_idle_list_ent *event = smb_idle_event_list;
+
+ while(event) {
+ if (event->id == id) {
+ DLIST_REMOVE(smb_idle_event_list,event);
+ SAFE_FREE(event);
+ return True;
+ }
+ event = event->next;
+ }
+
+ return False;
+}
+
+void smb_run_idle_events(time_t now)
+{
+ struct smb_idle_list_ent *event = smb_idle_event_list;
+
+ while (event) {
+ struct smb_idle_list_ent *next = event->next;
+ time_t interval;
+
+ if (event->interval <= 0) {
+ interval = SMB_IDLE_EVENT_DEFAULT_INTERVAL;
+ } else if (event->interval >= SMB_IDLE_EVENT_MIN_INTERVAL) {
+ interval = event->interval;
+ } else {
+ interval = SMB_IDLE_EVENT_MIN_INTERVAL;
+ }
+ if (now >(event->lastrun+interval)) {
+ event->lastrun = now;
+ event->fn(&event->data,&event->interval,now);
+ }
+ event = next;
+ }
+
+ return;
+}
+
+/***************************************************************************
+ * This Function registers a exit event
+ *
+ * the registered functions are run on exit()
+ * and maybe shutdown idle connections (e.g. to an LDAP server)
+ ***************************************************************************/
+
+struct smb_exit_list_ent {
+ struct smb_exit_list_ent *prev,*next;
+ smb_event_id_t id;
+ smb_exit_event_fn *fn;
+ void *data;
+};
+
+static struct smb_exit_list_ent *smb_exit_event_list = NULL;
+
+smb_event_id_t smb_register_exit_event(smb_exit_event_fn *fn, void *data)
+{
+ struct smb_exit_list_ent *event;
+ static smb_event_id_t smb_exit_event_id = 1;
+
+ if (!fn) {
+ return SMB_EVENT_ID_INVALID;
+ }
+
+ event = (struct smb_exit_list_ent *)malloc(sizeof(struct smb_exit_list_ent));
+ if (!event) {
+ DEBUG(0,("malloc() failed!\n"));
+ return SMB_EVENT_ID_INVALID;
+ }
+ event->fn = fn;
+ event->data = data;
+ event->id = smb_exit_event_id++;
+
+ DLIST_ADD(smb_exit_event_list,event);
+
+ return event->id;
+}
+
+BOOL smb_unregister_exit_event(smb_event_id_t id)
+{
+ struct smb_exit_list_ent *event = smb_exit_event_list;
+
+ while(event) {
+ if (event->id == id) {
+ DLIST_REMOVE(smb_exit_event_list,event);
+ SAFE_FREE(event);
+ return True;
+ }
+ event = event->next;
+ }
+
+ return False;
+}
+
+void smb_run_exit_events(void)
+{
+ struct smb_exit_list_ent *event = smb_exit_event_list;
+ struct smb_exit_list_ent *tmp = NULL;
+
+ while (event) {
+ event->fn(&event->data);
+ tmp = event;
+ event = event->next;
+ /* exit event should only run one time :-)*/
+ SAFE_FREE(tmp);
+ }
+
+ /* the list is empty now...*/
+ smb_exit_event_list = NULL;
+
+ return;
+}
diff --git a/source/lib/ms_fnmatch.c b/source/lib/ms_fnmatch.c
new file mode 100644
index 00000000000..24232c3b523
--- /dev/null
+++ b/source/lib/ms_fnmatch.c
@@ -0,0 +1,249 @@
+/*
+ Unix SMB/CIFS implementation.
+ filename matching routine
+ 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 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. */
+
+/*
+ This module was originally based on fnmatch.c copyright by the Free
+ Software Foundation. It bears little resemblence to that code now
+*/
+
+
+#if FNMATCH_TEST
+#include <stdio.h>
+#include <stdlib.h>
+#else
+#include "includes.h"
+#endif
+
+/*
+ bugger. we need a separate wildcard routine for older versions
+ of the protocol. This is not yet perfect, but its a lot
+ better than what we had */
+static int ms_fnmatch_lanman_core(const smb_ucs2_t *pattern,
+ const smb_ucs2_t *string,
+ BOOL case_sensitive)
+{
+ const smb_ucs2_t *p = pattern, *n = string;
+ smb_ucs2_t c;
+
+ if (strcmp_wa(p, "?")==0 && strcmp_wa(n, ".")) goto match;
+
+ while ((c = *p++)) {
+ switch (c) {
+ case UCS2_CHAR('.'):
+ if (! *n) goto next;
+ if (*n != UCS2_CHAR('.')) goto nomatch;
+ n++;
+ break;
+
+ case UCS2_CHAR('?'):
+ if (! *n) goto next;
+ if ((*n == UCS2_CHAR('.') &&
+ n[1] != UCS2_CHAR('.')) || ! *n)
+ goto next;
+ n++;
+ break;
+
+ case UCS2_CHAR('>'):
+ if (! *n) goto next;
+ if (n[0] == UCS2_CHAR('.')) {
+ if (! n[1] && ms_fnmatch_lanman_core(p, n+1, case_sensitive) == 0) goto match;
+ if (ms_fnmatch_lanman_core(p, n, case_sensitive) == 0) goto match;
+ goto nomatch;
+ }
+ n++;
+ break;
+
+ case UCS2_CHAR('*'):
+ if (! *n) goto next;
+ if (! *p) goto match;
+ for (; *n; n++) {
+ if (ms_fnmatch_lanman_core(p, n, case_sensitive) == 0) goto match;
+ }
+ break;
+
+ case UCS2_CHAR('<'):
+ for (; *n; n++) {
+ if (ms_fnmatch_lanman_core(p, n, case_sensitive) == 0) goto match;
+ if (*n == UCS2_CHAR('.') &&
+ !strchr_w(n+1,UCS2_CHAR('.'))) {
+ n++;
+ break;
+ }
+ }
+ break;
+
+ case UCS2_CHAR('"'):
+ if (*n == 0 && ms_fnmatch_lanman_core(p, n, case_sensitive) == 0) goto match;
+ if (*n != UCS2_CHAR('.')) goto nomatch;
+ n++;
+ break;
+
+ default:
+ if (case_sensitive) {
+ if (c != *n) goto nomatch;
+ } else {
+ if (tolower_w(c) != tolower_w(*n)) goto nomatch;
+ }
+ n++;
+ }
+ }
+
+ if (! *n) goto match;
+
+ nomatch:
+ /*
+ if (verbose) printf("NOMATCH pattern=[%s] string=[%s]\n", pattern, string);
+ */
+ return -1;
+
+next:
+ if (ms_fnmatch_lanman_core(p, n, case_sensitive) == 0) goto match;
+ goto nomatch;
+
+ match:
+ /*
+ if (verbose) printf("MATCH pattern=[%s] string=[%s]\n", pattern, string);
+ */
+ return 0;
+}
+
+static int ms_fnmatch_lanman1(const smb_ucs2_t *pattern,
+ const smb_ucs2_t *string, BOOL case_sensitive)
+{
+ if (!strpbrk_wa(pattern, "?*<>\"")) {
+ smb_ucs2_t s[] = {UCS2_CHAR('.'), 0};
+ if (strcmp_wa(string,"..") == 0) string = s;
+ return strcasecmp_w(pattern, string);
+ }
+
+ if (strcmp_wa(string,"..") == 0 || strcmp_wa(string,".") == 0) {
+ smb_ucs2_t dot[] = {UCS2_CHAR('.'), 0};
+ smb_ucs2_t dotdot[] = {UCS2_CHAR('.'), UCS2_CHAR('.'), 0};
+ return ms_fnmatch_lanman_core(pattern, dotdot, case_sensitive) &&
+ ms_fnmatch_lanman_core(pattern, dot, case_sensitive);
+ }
+
+ return ms_fnmatch_lanman_core(pattern, string, case_sensitive);
+}
+
+
+/* the following function was derived using the masktest utility -
+ after years of effort we finally have a perfect MS wildcard
+ matching routine!
+
+ NOTE: this matches only filenames with no directory component
+
+ Returns 0 on match, -1 on fail.
+*/
+static int ms_fnmatch_w(const smb_ucs2_t *pattern, const smb_ucs2_t *string,
+ int protocol, BOOL case_sensitive)
+{
+ const smb_ucs2_t *p = pattern, *n = string;
+ smb_ucs2_t c;
+
+ if (protocol <= PROTOCOL_LANMAN2) {
+ return ms_fnmatch_lanman1(pattern, string, case_sensitive);
+ }
+
+ while ((c = *p++)) {
+ switch (c) {
+ case UCS2_CHAR('?'):
+ if (! *n) return -1;
+ n++;
+ break;
+
+ case UCS2_CHAR('>'):
+ if (n[0] == UCS2_CHAR('.')) {
+ if (! n[1] && ms_fnmatch_w(p, n+1, protocol, case_sensitive) == 0) return 0;
+ if (ms_fnmatch_w(p, n, protocol, case_sensitive) == 0) return 0;
+ return -1;
+ }
+ if (! *n) return ms_fnmatch_w(p, n, protocol, case_sensitive);
+ n++;
+ break;
+
+ case UCS2_CHAR('*'):
+ for (; *n; n++) {
+ if (ms_fnmatch_w(p, n, protocol, case_sensitive) == 0) return 0;
+ }
+ break;
+
+ case UCS2_CHAR('<'):
+ for (; *n; n++) {
+ if (ms_fnmatch_w(p, n, protocol, case_sensitive) == 0) return 0;
+ if (*n == UCS2_CHAR('.') && !strchr_wa(n+1,'.')) {
+ n++;
+ break;
+ }
+ }
+ break;
+
+ case UCS2_CHAR('"'):
+ if (*n == 0 && ms_fnmatch_w(p, n, protocol, case_sensitive) == 0) return 0;
+ if (*n != UCS2_CHAR('.')) return -1;
+ n++;
+ break;
+
+ default:
+ if (case_sensitive) {
+ if (c != *n) return -1;
+ } else {
+ if (tolower_w(c) != tolower_w(*n)) return -1;
+ }
+ n++;
+ }
+ }
+
+ if (! *n) return 0;
+
+ return -1;
+}
+
+int ms_fnmatch(const char *pattern, const char *string, int protocol,
+ BOOL case_senstive)
+{
+ wpstring buffer_pattern, buffer_string;
+ int ret;
+ size_t size;
+
+ size = push_ucs2(NULL, buffer_pattern, pattern, sizeof(buffer_pattern), STR_TERMINATE);
+ if (size == (size_t)-1) {
+ return -1;
+ /* Not quite the right answer, but finding the right one
+ under this failure case is expensive, and it's pretty close */
+ }
+
+ size = push_ucs2(NULL, buffer_string, string, sizeof(buffer_string), STR_TERMINATE);
+ if (size == (size_t)-1) {
+ return -1;
+ /* Not quite the right answer, but finding the right one
+ under this failure case is expensive, and it's pretty close */
+ }
+
+ ret = ms_fnmatch_w(buffer_pattern, buffer_string, protocol, case_senstive);
+ DEBUG(10,("ms_fnmatch(%s,%s) -> %d\n", pattern, string, ret));
+
+ return ret;
+}
+
+/* a generic fnmatch function - uses for non-CIFS pattern matching */
+int gen_fnmatch(const char *pattern, const char *string)
+{
+ return ms_fnmatch(pattern, string, PROTOCOL_NT1, True);
+}
diff --git a/source/lib/pam_errors.c b/source/lib/pam_errors.c
new file mode 100644
index 00000000000..925441fb1d4
--- /dev/null
+++ b/source/lib/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 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"
+
+#ifdef WITH_PAM
+#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/source/lib/pidfile.c b/source/lib/pidfile.c
new file mode 100644
index 00000000000..1a462bf1287
--- /dev/null
+++ b/source/lib/pidfile.c
@@ -0,0 +1,109 @@
+/* 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 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"
+
+#ifndef O_NONBLOCK
+#define O_NONBLOCK
+#endif
+
+/* return the pid in a pidfile. return 0 if the process (or pidfile)
+ does not exist */
+pid_t pidfile_pid(const char *name)
+{
+ int fd;
+ char pidstr[20];
+ unsigned ret;
+ pstring pidFile;
+
+ slprintf(pidFile, sizeof(pidFile)-1, "%s/%s.pid", lp_piddir(), name);
+
+ fd = sys_open(pidFile, O_NONBLOCK | O_RDONLY, 0644);
+ if (fd == -1) {
+ return 0;
+ }
+
+ ZERO_ARRAY(pidstr);
+
+ if (read(fd, pidstr, sizeof(pidstr)-1) <= 0) {
+ goto noproc;
+ }
+
+ ret = atoi(pidstr);
+
+ if (!process_exists((pid_t)ret)) {
+ goto noproc;
+ }
+
+ if (fcntl_lock(fd,SMB_F_SETLK,0,1,F_RDLCK)) {
+ /* we could get the lock - it can't be a Samba process */
+ goto noproc;
+ }
+
+ close(fd);
+ return (pid_t)ret;
+
+ noproc:
+ close(fd);
+ unlink(pidFile);
+ return 0;
+}
+
+/* create a pid file in the pid directory. open it and leave it locked */
+void pidfile_create(const char *name)
+{
+ int fd;
+ char buf[20];
+ pstring pidFile;
+ pid_t pid;
+
+ slprintf(pidFile, sizeof(pidFile)-1, "%s/%s.pid", lp_piddir(), name);
+
+ pid = pidfile_pid(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 = sys_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,SMB_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) sys_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... */
+}
diff --git a/source/lib/popt_common.c b/source/lib/popt_common.c
new file mode 100644
index 00000000000..6c35213d43a
--- /dev/null
+++ b/source/lib/popt_common.c
@@ -0,0 +1,394 @@
+/*
+ Unix SMB/CIFS implementation.
+ Common popt routines
+
+ Copyright (C) Tim Potter 2001,2002
+ Copyright (C) Jelmer Vernooij 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 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"
+
+/* Handle command line options:
+ * -d,--debuglevel
+ * -s,--configfile
+ * -O,--socket-options
+ * -V,--version
+ * -l,--log-base
+ * -n,--netbios-name
+ * -W,--workgroup
+ * -i,--scope
+ */
+
+extern pstring user_socket_options;
+extern BOOL AllowDebugChange;
+extern BOOL override_logfile;
+
+struct user_auth_info cmdline_auth_info;
+
+static void popt_common_callback(poptContext con,
+ enum poptCallbackReason reason,
+ const struct poptOption *opt,
+ const char *arg, const void *data)
+{
+ pstring logfile;
+ const char *pname;
+
+ /* Find out basename of current program */
+ pname = strrchr_m(poptGetInvocationName(con),'/');
+
+ if (!pname)
+ pname = poptGetInvocationName(con);
+ else
+ pname++;
+
+ if (reason == POPT_CALLBACK_REASON_PRE) {
+ pstr_sprintf(logfile, "%s/log.%s", dyn_LOGFILEBASE, pname);
+ lp_set_logfile(logfile);
+ return;
+ }
+
+ switch(opt->val) {
+ case 'd':
+ if (arg) {
+ debug_parse_levels(arg);
+ AllowDebugChange = False;
+ }
+ break;
+
+ case 'V':
+ printf( "Version %s\n", SAMBA_VERSION_STRING);
+ exit(0);
+ break;
+
+ case 'O':
+ if (arg) {
+ pstrcpy(user_socket_options,arg);
+ }
+ break;
+
+ case 's':
+ if (arg) {
+ pstrcpy(dyn_CONFIGFILE, arg);
+ }
+ break;
+
+ case 'n':
+ if (arg) {
+ set_global_myname(arg);
+ }
+ break;
+
+ case 'l':
+ if (arg) {
+ pstr_sprintf(logfile, "%s/log.%s", arg, pname);
+ lp_set_logfile(logfile);
+ override_logfile = True;
+ }
+ break;
+
+ case 'i':
+ if (arg) {
+ set_global_scope(arg);
+ }
+ break;
+
+ case 'W':
+ if (arg) {
+ set_global_myworkgroup(arg);
+ }
+ break;
+ }
+}
+
+struct poptOption popt_common_connection[] = {
+ { NULL, 0, POPT_ARG_CALLBACK, popt_common_callback },
+ { "socket-options", 'O', POPT_ARG_STRING, NULL, 'O', "socket options to use",
+ "SOCKETOPTIONS" },
+ { "netbiosname", 'n', POPT_ARG_STRING, NULL, 'n', "Primary netbios name", "NETBIOSNAME" },
+ { "workgroup", 'W', POPT_ARG_STRING, NULL, 'W', "Set the workgroup name", "WORKGROUP" },
+ { "scope", 'i', POPT_ARG_STRING, NULL, 'i', "Use this Netbios scope", "SCOPE" },
+
+ POPT_TABLEEND
+};
+
+struct poptOption popt_common_samba[] = {
+ { NULL, 0, POPT_ARG_CALLBACK|POPT_CBFLAG_PRE, popt_common_callback },
+ { "debuglevel", 'd', POPT_ARG_STRING, NULL, 'd', "Set debug level", "DEBUGLEVEL" },
+ { "configfile", 's', POPT_ARG_STRING, NULL, 's', "Use alternative configuration file", "CONFIGFILE" },
+ { "log-basename", 'l', POPT_ARG_STRING, NULL, 'l', "Basename for log/debug files", "LOGFILEBASE" },
+ { "version", 'V', POPT_ARG_NONE, NULL, 'V', "Print version" },
+ POPT_TABLEEND
+};
+
+struct poptOption popt_common_version[] = {
+ { NULL, 0, POPT_ARG_CALLBACK, popt_common_callback },
+ { "version", 'V', POPT_ARG_NONE, NULL, 'V', "Print version" },
+ POPT_TABLEEND
+};
+
+
+
+/****************************************************************************
+ * get a password from a a file or file descriptor
+ * exit on failure
+ * ****************************************************************************/
+static void get_password_file(struct user_auth_info *a)
+{
+ 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 = sys_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(a->password, pass);
+ if (close_it)
+ close(fd);
+}
+
+static void get_credentials_file(const char *file, struct user_auth_info *info)
+{
+ XFILE *auth;
+ fstring buf;
+ uint16 len = 0;
+ char *ptr, *val, *param;
+
+ if ((auth=x_fopen(file, O_RDONLY, 0)) == NULL)
+ {
+ /* fail if we can't open the credentials file */
+ d_printf("ERROR: Unable to open credentials file!\n");
+ exit(-1);
+ }
+
+ while (!x_feof(auth))
+ {
+ /* get a line from the file */
+ if (!x_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_m (buf, '=')))
+ continue;
+
+ val = ptr+1;
+ *ptr = '\0';
+
+ /* eat leading white space */
+ while ((*val!='\0') && ((*val==' ') || (*val=='\t')))
+ val++;
+
+ if (strwicmp("password", param) == 0)
+ {
+ pstrcpy(info->password, val);
+ info->got_pass = True;
+ }
+ else if (strwicmp("username", param) == 0)
+ pstrcpy(info->username, val);
+ else if (strwicmp("domain", param) == 0)
+ set_global_myworkgroup(val);
+ memset(buf, 0, sizeof(buf));
+ }
+ x_fclose(auth);
+}
+
+/* Handle command line options:
+ * -U,--user
+ * -A,--authentication-file
+ * -k,--use-kerberos
+ * -N,--no-pass
+ * -S,--signing
+ * -P --machine-pass
+ */
+
+
+static void popt_common_credentials_callback(poptContext con,
+ enum poptCallbackReason reason,
+ const struct poptOption *opt,
+ const char *arg, const void *data)
+{
+ char *p;
+
+ if (reason == POPT_CALLBACK_REASON_PRE) {
+ cmdline_auth_info.use_kerberos = False;
+ cmdline_auth_info.got_pass = False;
+ cmdline_auth_info.signing_state = Undefined;
+ pstrcpy(cmdline_auth_info.username, "GUEST");
+
+ if (getenv("LOGNAME"))pstrcpy(cmdline_auth_info.username,getenv("LOGNAME"));
+
+ if (getenv("USER")) {
+ pstrcpy(cmdline_auth_info.username,getenv("USER"));
+
+ if ((p = strchr_m(cmdline_auth_info.username,'%'))) {
+ *p = 0;
+ pstrcpy(cmdline_auth_info.password,p+1);
+ cmdline_auth_info.got_pass = True;
+ memset(strchr_m(getenv("USER"),'%')+1,'X',strlen(cmdline_auth_info.password));
+ }
+ }
+
+ if (getenv("PASSWD")) {
+ pstrcpy(cmdline_auth_info.password,getenv("PASSWD"));
+ cmdline_auth_info.got_pass = True;
+ }
+
+ if (getenv("PASSWD_FD") || getenv("PASSWD_FILE")) {
+ get_password_file(&cmdline_auth_info);
+ cmdline_auth_info.got_pass = True;
+ }
+
+ return;
+ }
+
+ switch(opt->val) {
+ case 'U':
+ {
+ char *lp;
+
+ pstrcpy(cmdline_auth_info.username,arg);
+ if ((lp=strchr_m(cmdline_auth_info.username,'%'))) {
+ *lp = 0;
+ pstrcpy(cmdline_auth_info.password,lp+1);
+ cmdline_auth_info.got_pass = True;
+ memset(strchr_m(arg,'%')+1,'X',strlen(cmdline_auth_info.password));
+ }
+ }
+ break;
+
+ case 'A':
+ get_credentials_file(arg, &cmdline_auth_info);
+ break;
+
+ case 'k':
+#ifndef HAVE_KRB5
+ d_printf("No kerberos support compiled in\n");
+ exit(1);
+#else
+ cmdline_auth_info.use_kerberos = True;
+ cmdline_auth_info.got_pass = True;
+#endif
+ break;
+
+ case 'S':
+ {
+ cmdline_auth_info.signing_state = -1;
+ if (strequal(arg, "off") || strequal(arg, "no") || strequal(arg, "false"))
+ cmdline_auth_info.signing_state = False;
+ else if (strequal(arg, "on") || strequal(arg, "yes") || strequal(arg, "true") ||
+ strequal(arg, "auto") )
+ cmdline_auth_info.signing_state = True;
+ else if (strequal(arg, "force") || strequal(arg, "required") || strequal(arg, "forced"))
+ cmdline_auth_info.signing_state = Required;
+ else {
+ fprintf(stderr, "Unknown signing option %s\n", arg );
+ exit(1);
+ }
+ }
+ break;
+ case 'P':
+ {
+ char *opt_password = NULL;
+ /* it is very useful to be able to make ads queries as the
+ machine account for testing purposes and for domain leave */
+
+ if (!secrets_init()) {
+ d_printf("ERROR: Unable to open secrets database\n");
+ exit(1);
+ }
+
+ opt_password = secrets_fetch_machine_password(lp_workgroup(), NULL, NULL);
+
+ if (!opt_password) {
+ d_printf("ERROR: Unable to fetch machine password\n");
+ exit(1);
+ }
+ pstr_sprintf(cmdline_auth_info.username, "%s$",
+ global_myname());
+ pstrcpy(cmdline_auth_info.password,opt_password);
+ SAFE_FREE(opt_password);
+
+ /* machine accounts only work with kerberos */
+ cmdline_auth_info.use_kerberos = True;
+ cmdline_auth_info.got_pass = True;
+ }
+ break;
+ }
+}
+
+
+
+struct poptOption popt_common_credentials[] = {
+ { NULL, 0, POPT_ARG_CALLBACK|POPT_CBFLAG_PRE, popt_common_credentials_callback },
+ { "user", 'U', POPT_ARG_STRING, NULL, 'U', "Set the network username", "USERNAME" },
+ { "no-pass", 'N', POPT_ARG_NONE, &cmdline_auth_info.got_pass, 0, "Don't ask for a password" },
+ { "kerberos", 'k', POPT_ARG_NONE, &cmdline_auth_info.use_kerberos, 'k', "Use kerberos (active directory) authentication" },
+ { "authentication-file", 'A', POPT_ARG_STRING, NULL, 'A', "Get the credentials from a file", "FILE" },
+ { "signing", 'S', POPT_ARG_STRING, NULL, 'S', "Set the client signing state", "on|off|required" },
+ {"machine-pass", 'P', POPT_ARG_NONE, NULL, 'P', "Use stored machine account password" },
+ POPT_TABLEEND
+};
diff --git a/source/lib/privileges.c b/source/lib/privileges.c
new file mode 100644
index 00000000000..abbaf112d34
--- /dev/null
+++ b/source/lib/privileges.c
@@ -0,0 +1,442 @@
+/*
+ Unix SMB/CIFS implementation.
+ Privileges handling functions
+ Copyright (C) Jean François Micouleau 1998-2001
+ Copyright (C) Simo Sorce 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 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"
+
+/* defines */
+
+#define ALLOC_CHECK(ptr, err, label, str) do { if ((ptr) == NULL) { DEBUG(0, ("%s: out of memory!\n", str)); err = NT_STATUS_NO_MEMORY; goto label; } } while(0)
+#define NTSTATUS_CHECK(err, label, str1, str2) do { if (!NT_STATUS_IS_OK(err)) { DEBUG(0, ("%s: %s failed!\n", str1, str2)); } } while(0)
+
+
+PRIVS privs[] = {
+ {SE_NONE, "no_privs", "No privilege"}, /* this one MUST be first */
+ {SE_CREATE_TOKEN, "SeCreateTokenPrivilege", "Create Token"},
+ {SE_ASSIGN_PRIMARY_TOKEN, "SeAssignPrimaryTokenPrivilege", "Assign Primary Token"},
+ {SE_LOCK_MEMORY, "SeLockMemoryPrivilege", "Lock Memory"},
+ {SE_INCREASE_QUOTA, "SeIncreaseQuotaPrivilege", "Increase Quota"},
+ {SE_UNSOLICITED_INPUT, "SeUnsolicitedInputPrivilege", "Unsolicited Input"},
+ {SE_MACHINE_ACCOUNT, "SeMachineAccountPrivilege", "Can add Machine Accounts to the Domain"},
+ {SE_TCB, "SeTcbPrivilege", "TCB"},
+ {SE_SECURITY, "SeSecurityPrivilege", "Security Privilege"},
+ {SE_TAKE_OWNERSHIP, "SeTakeOwnershipPrivilege", "Take Ownership Privilege"},
+ {SE_LOAD_DRIVER, "SeLocalDriverPrivilege", "Local Driver Privilege"},
+ {SE_SYSTEM_PROFILE, "SeSystemProfilePrivilege", "System Profile Privilege"},
+ {SE_SYSTEM_TIME, "SeSystemtimePrivilege", "System Time"},
+ {SE_PROF_SINGLE_PROCESS, "SeProfileSingleProcessPrivilege", "Profile Single Process Privilege"},
+ {SE_INC_BASE_PRIORITY, "SeIncreaseBasePriorityPrivilege", "Increase Base Priority Privilege"},
+ {SE_CREATE_PAGEFILE, "SeCreatePagefilePrivilege", "Create Pagefile Privilege"},
+ {SE_CREATE_PERMANENT, "SeCreatePermanentPrivilege", "Create Permanent"},
+ {SE_BACKUP, "SeBackupPrivilege", "Backup Privilege"},
+ {SE_RESTORE, "SeRestorePrivilege", "Restore Privilege"},
+ {SE_SHUTDOWN, "SeShutdownPrivilege", "Shutdown Privilege"},
+ {SE_DEBUG, "SeDebugPrivilege", "Debug Privilege"},
+ {SE_AUDIT, "SeAuditPrivilege", "Audit"},
+ {SE_SYSTEM_ENVIRONMENT, "SeSystemEnvironmentPrivilege", "System Environment Privilege"},
+ {SE_CHANGE_NOTIFY, "SeChangeNotifyPrivilege", "Change Notify"},
+ {SE_REMOTE_SHUTDOWN, "SeRemoteShutdownPrivilege", "Remote Shutdown Privilege"},
+ {SE_UNDOCK, "SeUndockPrivilege", "Undock"},
+ {SE_SYNC_AGENT, "SeSynchronizationAgentPrivilege", "Synchronization Agent"},
+ {SE_ENABLE_DELEGATION, "SeEnableDelegationPrivilege", "Enable Delegation"},
+ {SE_PRINT_OPERATOR, "SePrintOperatorPrivilege", "Printer Operator"},
+ {SE_ADD_USERS, "SeAddUsersPrivilege", "Add Users"},
+ {SE_ALL_PRIVS, "SeAllPrivileges", "All Privileges"}
+};
+
+
+
+/****************************************************************************
+ Check if a user is a mapped group.
+
+ This function will check if the group SID is mapped onto a
+ system managed gid or onto a winbind manged sid.
+ In the first case it will be threated like a mapped group
+ and the backend should take the member list with a getgrgid
+ and ignore any user that have been possibly set into the group
+ object.
+
+ In the second case, the group is a fully SAM managed group
+ served back to the system through winbind. In this case the
+ members of a Local group are "unrolled" to cope with the fact
+ that unix cannot contain groups inside groups.
+ The backend MUST never call any getgr* / getpw* function or
+ loops with winbind may happen.
+ ****************************************************************************/
+
+#if 0
+NTSTATUS is_mapped_group(BOOL *mapped, const DOM_SID *sid)
+{
+ NTSTATUS result;
+ gid_t id;
+
+ /* look if mapping exist, do not make idmap alloc an uid if SID is not found */
+ result = idmap_get_gid_from_sid(&id, sid, False);
+ if (NT_STATUS_IS_OK(result)) {
+ *mapped = gid_is_in_winbind_range(id);
+ } else {
+ *mapped = False;
+ }
+
+ return result;
+}
+#endif
+
+/****************************************************************************
+ duplicate alloc luid_attr
+ ****************************************************************************/
+NTSTATUS dupalloc_luid_attr(TALLOC_CTX *mem_ctx, LUID_ATTR **new_la, LUID_ATTR *old_la, int count)
+{
+ NTSTATUS ret;
+ int i;
+
+ /* don't crash if the source pointer is NULL (since we don't
+ do priviledges now anyways) */
+
+ if ( !old_la )
+ return NT_STATUS_OK;
+
+ *new_la = (LUID_ATTR *)talloc(mem_ctx, count*sizeof(LUID_ATTR));
+ ALLOC_CHECK(new_la, ret, done, "dupalloc_luid_attr");
+
+ for (i=0; i<count; i++) {
+ (*new_la)[i].luid.high = old_la[i].luid.high;
+ (*new_la)[i].luid.low = old_la[i].luid.low;
+ (*new_la)[i].attr = old_la[i].attr;
+ }
+
+ ret = NT_STATUS_OK;
+
+done:
+ return ret;
+}
+
+/****************************************************************************
+ initialise a privilege list
+ ****************************************************************************/
+NTSTATUS init_privilege(PRIVILEGE_SET **priv_set)
+{
+ NTSTATUS ret;
+ TALLOC_CTX *mem_ctx = talloc_init("privilege set");
+ ALLOC_CHECK(mem_ctx, ret, done, "init_privilege");
+
+ *priv_set = talloc_zero(mem_ctx, sizeof(PRIVILEGE_SET));
+ ALLOC_CHECK(*priv_set, ret, done, "init_privilege");
+
+ (*priv_set)->mem_ctx = mem_ctx;
+
+ ret = NT_STATUS_OK;
+
+done:
+ return ret;
+}
+
+NTSTATUS init_priv_with_ctx(TALLOC_CTX *mem_ctx, PRIVILEGE_SET **priv_set)
+{
+ NTSTATUS ret;
+
+ *priv_set = talloc_zero(mem_ctx, sizeof(PRIVILEGE_SET));
+ ALLOC_CHECK(*priv_set, ret, done, "init_privilege");
+
+ (*priv_set)->mem_ctx = mem_ctx;
+ (*priv_set)->ext_ctx = True;
+
+ ret = NT_STATUS_OK;
+
+done:
+ return ret;
+}
+
+void reset_privilege(PRIVILEGE_SET *priv_set)
+{
+ priv_set->count = 0;
+ priv_set->control = 0;
+ priv_set->set = NULL;
+}
+
+void destroy_privilege(PRIVILEGE_SET **priv_set)
+{
+ if (priv_set == NULL || *priv_set == NULL)
+ return;
+
+ reset_privilege(*priv_set);
+ if (!((*priv_set)->ext_ctx))
+ /* mem_ctx is local, destroy it */
+ talloc_destroy((*priv_set)->mem_ctx);
+ *priv_set = NULL;
+}
+
+/****************************************************************************
+ add a privilege to a privilege array
+ ****************************************************************************/
+NTSTATUS add_privilege(PRIVILEGE_SET *priv_set, LUID_ATTR set)
+{
+ NTSTATUS ret;
+ LUID_ATTR *new_set;
+
+ /* check if the privilege is not already in the list */
+ if (NT_STATUS_IS_OK(check_priv_in_privilege(priv_set, set)))
+ return NT_STATUS_UNSUCCESSFUL;
+
+ /* we can allocate memory to add the new privilege */
+
+ new_set = (LUID_ATTR *)talloc_realloc(priv_set->mem_ctx, priv_set->set, (priv_set->count + 1) * (sizeof(LUID_ATTR)));
+ ALLOC_CHECK(new_set, ret, done, "add_privilege");
+
+ new_set[priv_set->count].luid.high = set.luid.high;
+ new_set[priv_set->count].luid.low = set.luid.low;
+ new_set[priv_set->count].attr = set.attr;
+
+ priv_set->count++;
+ priv_set->set = new_set;
+
+ ret = NT_STATUS_OK;
+
+done:
+ return ret;
+}
+
+NTSTATUS add_privilege_by_name(PRIVILEGE_SET *priv_set, const char *name)
+{
+ int e;
+
+ for (e = 0; privs[e].se_priv != SE_ALL_PRIVS; e++) {
+ if (StrCaseCmp(privs[e].priv, name) == 0) {
+ LUID_ATTR la;
+
+ la.attr = 0;
+ la.luid.high = 0;
+ la.luid.low = privs[e].se_priv;
+
+ return add_privilege(priv_set, la);
+ }
+ }
+
+ DEBUG(1, ("add_privilege_by_name: No Such Privilege Found (%s)\n", name));
+
+ return NT_STATUS_UNSUCCESSFUL;
+}
+
+/****************************************************************************
+ add all the privileges to a privilege array
+ ****************************************************************************/
+NTSTATUS add_all_privilege(PRIVILEGE_SET *priv_set)
+{
+ NTSTATUS result = NT_STATUS_OK;
+ LUID_ATTR set;
+
+ set.attr = 0;
+ set.luid.high = 0;
+
+ /* TODO: set a proper list of privileges */
+ set.luid.low = SE_ADD_USERS;
+ result = add_privilege(priv_set, set);
+ NTSTATUS_CHECK(result, done, "add_all_privilege", "add_privilege");
+
+ set.luid.low = SE_MACHINE_ACCOUNT;
+ result = add_privilege(priv_set, set);
+ NTSTATUS_CHECK(result, done, "add_all_privilege", "add_privilege");
+
+ set.luid.low = SE_PRINT_OPERATOR;
+ result = add_privilege(priv_set, set);
+ NTSTATUS_CHECK(result, done, "add_all_privilege", "add_privilege");
+
+ return result;
+}
+
+/****************************************************************************
+ check if the privilege list is empty
+ ****************************************************************************/
+NTSTATUS check_empty_privilege(PRIVILEGE_SET *priv_set)
+{
+ if (!priv_set)
+ return NT_STATUS_INVALID_PARAMETER;
+
+ if (priv_set->count == 0)
+ return NT_STATUS_OK;
+
+ return NT_STATUS_UNSUCCESSFUL;
+}
+
+/****************************************************************************
+ check if the privilege is in the privilege list
+ ****************************************************************************/
+NTSTATUS check_priv_in_privilege(PRIVILEGE_SET *priv_set, LUID_ATTR set)
+{
+ int i;
+
+ if (!priv_set)
+ return NT_STATUS_INVALID_PARAMETER;
+
+ /* if the list is empty, obviously we can't have it */
+ if (NT_STATUS_IS_OK(check_empty_privilege(priv_set)))
+ return NT_STATUS_UNSUCCESSFUL;
+
+ for (i = 0; i < priv_set->count; i++) {
+ LUID_ATTR *cur_set;
+
+ cur_set = &priv_set->set[i];
+ /* check only the low and high part. Checking the attr field has no meaning */
+ if ( (cur_set->luid.low == set.luid.low) &&
+ (cur_set->luid.high == set.luid.high) ) {
+ return NT_STATUS_OK;
+ }
+ }
+
+ return NT_STATUS_UNSUCCESSFUL;
+}
+
+/****************************************************************************
+ remove a privilege from a privilege array
+ ****************************************************************************/
+NTSTATUS remove_privilege(PRIVILEGE_SET *priv_set, LUID_ATTR set)
+{
+ NTSTATUS ret;
+ LUID_ATTR *new_set;
+ LUID_ATTR *old_set;
+ int i,j;
+
+ if (!priv_set)
+ return NT_STATUS_INVALID_PARAMETER;
+
+ /* check if the privilege is in the list */
+ if (!NT_STATUS_IS_OK(check_priv_in_privilege(priv_set, set)))
+ return NT_STATUS_UNSUCCESSFUL;
+
+ /* special case if it's the only privilege in the list */
+ if (priv_set->count == 1) {
+ reset_privilege(priv_set);
+ return NT_STATUS_OK;
+ }
+
+ /*
+ * the privilege is there, create a new list,
+ * and copy the other privileges
+ */
+
+ old_set = priv_set->set;
+
+ new_set = (LUID_ATTR *)talloc(priv_set->mem_ctx, (priv_set->count - 1) * (sizeof(LUID_ATTR)));
+ ALLOC_CHECK(new_set, ret, done, "remove_privilege");
+
+ for (i=0, j=0; i < priv_set->count; i++) {
+ if ( (old_set[i].luid.low == set.luid.low) &&
+ (old_set[i].luid.high == set.luid.high) ) {
+ continue;
+ }
+
+ new_set[j].luid.low = old_set[i].luid.low;
+ new_set[j].luid.high = old_set[i].luid.high;
+ new_set[j].attr = old_set[i].attr;
+
+ j++;
+ }
+
+ if (j != priv_set->count - 1) {
+ DEBUG(0,("remove_privilege: mismatch ! difference is not -1\n"));
+ DEBUGADD(0,("old count:%d, new count:%d\n", priv_set->count, j));
+ return NT_STATUS_INTERNAL_ERROR;
+ }
+
+ /* ok everything is fine */
+
+ priv_set->count--;
+ priv_set->set = new_set;
+
+ ret = NT_STATUS_OK;
+
+done:
+ return ret;
+}
+
+/****************************************************************************
+ duplicates a privilege array
+ the new privilege set must be passed inited
+ (use init_privilege or init_priv_with_ctx)
+ ****************************************************************************/
+NTSTATUS dup_priv_set(PRIVILEGE_SET *new_priv_set, PRIVILEGE_SET *priv_set)
+{
+ NTSTATUS ret;
+ LUID_ATTR *new_set;
+ LUID_ATTR *old_set;
+ int i;
+
+ if (new_priv_set == NULL || priv_set == NULL)
+ return NT_STATUS_INVALID_PARAMETER;
+
+ /* special case if there are no privileges in the list */
+ if (priv_set->count == 0) {
+ return NT_STATUS_OK;
+ }
+
+ /*
+ * create a new list,
+ * and copy the other privileges
+ */
+
+ old_set = priv_set->set;
+
+ new_set = (LUID_ATTR *)talloc(new_priv_set->mem_ctx, (priv_set->count) * (sizeof(LUID_ATTR)));
+ ALLOC_CHECK(new_set, ret, done, "dup_priv_set");
+
+ for (i=0; i < priv_set->count; i++) {
+
+ new_set[i].luid.low = old_set[i].luid.low;
+ new_set[i].luid.high = old_set[i].luid.high;
+ new_set[i].attr = old_set[i].attr;
+ }
+
+ new_priv_set->count = priv_set->count;
+ new_priv_set->control = priv_set->control;
+ new_priv_set->set = new_set;
+
+ ret = NT_STATUS_OK;
+
+done:
+ return ret;
+}
+
+
+NTSTATUS user_has_privilege(struct current_user *user, uint32 privilege)
+{
+ LUID_ATTR set;
+
+ set.attr = 0;
+ set.luid.high = 0;
+ set.luid.low = privilege;
+
+ return check_priv_in_privilege(user->privs, set);
+}
+
+BOOL luid_to_privilege_name(const LUID *set, fstring name)
+{
+ int i;
+
+ if (set->high != 0)
+ return False;
+
+ for (i=1; i<PRIV_ALL_INDEX-1; i++) {
+ if (set->low == privs[i].se_priv) {
+ fstrcpy(name, privs[i].priv);
+ return True;
+ }
+ }
+ return False;
+}
diff --git a/source/lib/readline.c b/source/lib/readline.c
new file mode 100644
index 00000000000..78b99fd7fb0
--- /dev/null
+++ b/source/lib/readline.c
@@ -0,0 +1,161 @@
+/*
+ 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 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"
+
+#ifdef HAVE_LIBREADLINE
+# ifdef HAVE_READLINE_READLINE_H
+# include <readline/readline.h>
+# ifdef HAVE_READLINE_HISTORY_H
+# include <readline/history.h>
+# endif
+# else
+# ifdef HAVE_READLINE_H
+# include <readline.h>
+# ifdef HAVE_HISTORY_H
+# include <history.h>
+# endif
+# else
+# undef HAVE_LIBREADLINE
+# endif
+# endif
+#endif
+
+#ifdef HAVE_NEW_LIBREADLINE
+# define RL_COMPLETION_CAST (rl_completion_func_t *)
+#else
+/* This type is missing from libreadline<4.0 (approximately) */
+# define RL_COMPLETION_CAST
+#endif /* HAVE_NEW_LIBREADLINE */
+
+/****************************************************************************
+ Display the prompt and wait for input. Call callback() regularly
+****************************************************************************/
+
+static char *smb_readline_replacement(char *prompt, void (*callback)(void),
+ char **(completion_fn)(const char *text, int start, int end))
+{
+ fd_set fds;
+ static pstring line;
+ struct timeval timeout;
+ int fd = x_fileno(x_stdin);
+ char *ret;
+
+ x_fprintf(dbf, "%s", prompt);
+ x_fflush(dbf);
+
+ 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, sizeof(line), x_stdin);
+ return ret;
+ }
+ if (callback)
+ callback();
+ }
+}
+
+/****************************************************************************
+ Display the prompt and wait for input. Call callback() regularly.
+****************************************************************************/
+
+char *smb_readline(char *prompt, void (*callback)(void),
+ char **(completion_fn)(const char *text, int start, int end))
+{
+#if HAVE_LIBREADLINE
+ if (isatty(x_fileno(x_stdin))) {
+ 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 (callback)
+ rl_event_hook = (Function *)callback;
+ 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
+}
+
+/****************************************************************************
+history
+****************************************************************************/
+int cmd_history(void)
+{
+#if defined(HAVE_LIBREADLINE)
+ 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;
+}
+
diff --git a/source/lib/replace.c b/source/lib/replace.c
new file mode 100644
index 00000000000..fe1cfc04eb1
--- /dev/null
+++ b/source/lib/replace.c
@@ -0,0 +1,452 @@
+/*
+ Unix SMB/CIFS implementation.
+ replacement routines for broken systems
+ 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 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"
+
+ void replace_dummy(void);
+ void replace_dummy(void) {}
+
+#ifndef HAVE_FTRUNCATE
+ /*******************************************************************
+ftruncate for operating systems that don't have it
+********************************************************************/
+ int ftruncate(int f,SMB_OFF_T l)
+{
+ struct flock fl;
+
+ fl.l_whence = 0;
+ fl.l_len = 0;
+ fl.l_start = l;
+ fl.l_type = F_WRLCK;
+ return fcntl(f, F_FREESP, &fl);
+}
+#endif /* HAVE_FTRUNCATE */
+
+
+#ifndef HAVE_STRLCPY
+/* like strncpy but does not 0 fill the buffer and always null
+ terminates. bufsize is the size of the destination buffer */
+ size_t strlcpy(char *d, const char *s, size_t bufsize)
+{
+ size_t len = strlen(s);
+ size_t ret = len;
+ if (bufsize <= 0) return 0;
+ if (len >= bufsize) len = bufsize-1;
+ memcpy(d, s, len);
+ d[len] = 0;
+ return ret;
+}
+#endif
+
+#ifndef HAVE_STRLCAT
+/* like strncat but does not 0 fill the buffer and always null
+ terminates. bufsize is the length of the buffer, which should
+ be one more than the maximum resulting string length */
+ size_t strlcat(char *d, const char *s, size_t bufsize)
+{
+ size_t len1 = strlen(d);
+ size_t len2 = strlen(s);
+ size_t ret = len1 + len2;
+
+ if (len1 >= bufsize) {
+ return 0;
+ }
+ if (len1+len2 >= bufsize) {
+ len2 = bufsize - (len1+1);
+ }
+ if (len2 > 0) {
+ memcpy(d+len1, s, len2);
+ d[len1+len2] = 0;
+ }
+ return ret;
+}
+#endif
+
+#ifndef HAVE_MKTIME
+/*******************************************************************
+a mktime() replacement for those who don't have it - contributed by
+C.A. Lademann <cal@zls.com>
+Corrections by richard.kettlewell@kewill.com
+********************************************************************/
+
+#define MINUTE 60
+#define HOUR 60*MINUTE
+#define DAY 24*HOUR
+#define YEAR 365*DAY
+ time_t mktime(struct tm *t)
+{
+ struct tm *u;
+ time_t epoch = 0;
+ int n;
+ int mon [] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 },
+ y, m, i;
+
+ if(t->tm_year < 70)
+ return((time_t)-1);
+
+ n = t->tm_year + 1900 - 1;
+ epoch = (t->tm_year - 70) * YEAR +
+ ((n / 4 - n / 100 + n / 400) - (1969 / 4 - 1969 / 100 + 1969 / 400)) * DAY;
+
+ y = t->tm_year + 1900;
+ m = 0;
+
+ for(i = 0; i < t->tm_mon; i++) {
+ epoch += mon [m] * DAY;
+ if(m == 1 && y % 4 == 0 && (y % 100 != 0 || y % 400 == 0))
+ epoch += DAY;
+
+ if(++m > 11) {
+ m = 0;
+ y++;
+ }
+ }
+
+ epoch += (t->tm_mday - 1) * DAY;
+ epoch += t->tm_hour * HOUR + t->tm_min * MINUTE + t->tm_sec;
+
+ if((u = localtime(&epoch)) != NULL) {
+ t->tm_sec = u->tm_sec;
+ t->tm_min = u->tm_min;
+ t->tm_hour = u->tm_hour;
+ t->tm_mday = u->tm_mday;
+ t->tm_mon = u->tm_mon;
+ t->tm_year = u->tm_year;
+ t->tm_wday = u->tm_wday;
+ t->tm_yday = u->tm_yday;
+ t->tm_isdst = u->tm_isdst;
+ }
+
+ return(epoch);
+}
+#endif /* !HAVE_MKTIME */
+
+
+
+#ifndef HAVE_RENAME
+/* Rename a file. (from libiberty in GNU binutils) */
+ int rename(const char *zfrom, const char *zto)
+{
+ if (link (zfrom, zto) < 0)
+ {
+ if (errno != EEXIST)
+ return -1;
+ if (unlink (zto) < 0
+ || link (zfrom, zto) < 0)
+ return -1;
+ }
+ return unlink (zfrom);
+}
+#endif /* HAVE_RENAME */
+
+
+#ifndef HAVE_INNETGR
+#if defined(HAVE_SETNETGRENT) && defined(HAVE_GETNETGRENT) && defined(HAVE_ENDNETGRENT)
+/*
+ * Search for a match in a netgroup. This replaces it on broken systems.
+ */
+ int innetgr(const char *group,const char *host,const char *user,const char *dom)
+{
+ char *hst, *usr, *dm;
+
+ setnetgrent(group);
+ while (getnetgrent(&hst, &usr, &dm)) {
+ if (((host == 0) || (hst == 0) || !strcmp(host, hst)) &&
+ ((user == 0) || (usr == 0) || !strcmp(user, usr)) &&
+ ((dom == 0) || (dm == 0) || !strcmp(dom, dm))) {
+ endnetgrent();
+ return (1);
+ }
+ }
+ endnetgrent();
+ return (0);
+}
+#endif /* HAVE_SETNETGRENT HAVE_GETNETGRENT HAVE_ENDNETGRENT */
+#endif /* HAVE_INNETGR */
+
+
+
+#ifndef HAVE_INITGROUPS
+/****************************************************************************
+ some systems don't have an initgroups call
+****************************************************************************/
+ int initgroups(char *name,gid_t id)
+{
+#ifndef HAVE_SETGROUPS
+ static int done;
+ if (!done) {
+ DEBUG(1,("WARNING: running without setgroups\n"));
+ done=1;
+ }
+ /* yikes! no SETGROUPS or INITGROUPS? how can this work? */
+ return(0);
+#else /* HAVE_SETGROUPS */
+ gid_t *grouplst = NULL;
+ int max_gr = groups_max();
+ int ret;
+ int i,j;
+ struct group *g;
+ char *gr;
+
+ if((grouplst = (gid_t *)malloc(sizeof(gid_t) * max_gr)) == NULL) {
+ DEBUG(0,("initgroups: malloc fail !\n"));
+ return -1;
+ }
+
+ grouplst[0] = id;
+ i = 1;
+ while (i < max_gr && ((g = (struct group *)getgrent()) != (struct group *)NULL)) {
+ if (g->gr_gid == id)
+ continue;
+ j = 0;
+ gr = g->gr_mem[0];
+ while (gr && (*gr != (char)NULL)) {
+ if (strcmp(name,gr) == 0) {
+ grouplst[i] = g->gr_gid;
+ i++;
+ gr = (char *)NULL;
+ break;
+ }
+ gr = g->gr_mem[++j];
+ }
+ }
+ endgrent();
+ ret = sys_setgroups(i,grouplst);
+ SAFE_FREE(grouplst);
+ return ret;
+#endif /* HAVE_SETGROUPS */
+}
+#endif /* HAVE_INITGROUPS */
+
+
+#if (defined(SecureWare) && defined(SCO))
+/* This is needed due to needing the nap() function but we don't want
+ to include the Xenix libraries since that will break other things...
+ BTW: system call # 0x0c28 is the same as calling nap() */
+ long nap(long milliseconds) {
+ return syscall(0x0c28, milliseconds);
+ }
+#endif
+
+
+#ifndef HAVE_MEMMOVE
+/*******************************************************************
+safely copies memory, ensuring no overlap problems.
+this is only used if the machine does not have it's own memmove().
+this is not the fastest algorithm in town, but it will do for our
+needs.
+********************************************************************/
+ void *memmove(void *dest,const void *src,int size)
+{
+ unsigned long d,s;
+ int i;
+ if (dest==src || !size) return(dest);
+
+ d = (unsigned long)dest;
+ s = (unsigned long)src;
+
+ if ((d >= (s+size)) || (s >= (d+size))) {
+ /* no overlap */
+ memcpy(dest,src,size);
+ return(dest);
+ }
+
+ if (d < s) {
+ /* we can forward copy */
+ if (s-d >= sizeof(int) &&
+ !(s%sizeof(int)) &&
+ !(d%sizeof(int)) &&
+ !(size%sizeof(int))) {
+ /* do it all as words */
+ int *idest = (int *)dest;
+ int *isrc = (int *)src;
+ size /= sizeof(int);
+ for (i=0;i<size;i++) idest[i] = isrc[i];
+ } else {
+ /* simplest */
+ char *cdest = (char *)dest;
+ char *csrc = (char *)src;
+ for (i=0;i<size;i++) cdest[i] = csrc[i];
+ }
+ } else {
+ /* must backward copy */
+ if (d-s >= sizeof(int) &&
+ !(s%sizeof(int)) &&
+ !(d%sizeof(int)) &&
+ !(size%sizeof(int))) {
+ /* do it all as words */
+ int *idest = (int *)dest;
+ int *isrc = (int *)src;
+ size /= sizeof(int);
+ for (i=size-1;i>=0;i--) idest[i] = isrc[i];
+ } else {
+ /* simplest */
+ char *cdest = (char *)dest;
+ char *csrc = (char *)src;
+ for (i=size-1;i>=0;i--) cdest[i] = csrc[i];
+ }
+ }
+ return(dest);
+}
+#endif /* HAVE_MEMMOVE */
+
+#ifndef HAVE_STRDUP
+/****************************************************************************
+duplicate a string
+****************************************************************************/
+ char *strdup(const char *s)
+{
+ size_t len;
+ char *ret;
+
+ if (!s) return(NULL);
+
+ len = strlen(s)+1;
+ ret = (char *)malloc(len);
+ if (!ret) return(NULL);
+ memcpy(ret,s,len);
+ return(ret);
+}
+#endif /* HAVE_STRDUP */
+
+#ifdef REPLACE_INET_NTOA
+char *rep_inet_ntoa(struct in_addr ip)
+{
+ unsigned char *p = (unsigned char *)&ip.s_addr;
+ static char buf[18];
+ slprintf(buf, 17, "%d.%d.%d.%d",
+ (int)p[0], (int)p[1], (int)p[2], (int)p[3]);
+ return buf;
+}
+#endif /* REPLACE_INET_NTOA */
+
+#ifndef HAVE_STRTOUL
+#ifndef ULONG_MAX
+#define ULONG_MAX ((unsigned long)(~0L)) /* 0xFFFFFFFF */
+#endif
+
+/*
+ * Convert a string to an unsigned long integer.
+ * Taken from libg++ - libiberty code.
+ *
+ * Ignores `locale' stuff. Assumes that the upper and lower case
+ * alphabets and digits are each contiguous.
+ */
+ unsigned long strtoul(const char *nptr, char **endptr, int base)
+{
+ const char *s = nptr;
+ unsigned long acc;
+ int c;
+ unsigned long cutoff;
+ int neg = 0, any, cutlim;
+
+ /*
+ * See strtol for comments as to the logic used.
+ */
+ do {
+ c = *s++;
+ } while (isspace(c));
+ if (c == '-') {
+ neg = 1;
+ c = *s++;
+ } else if (c == '+')
+ c = *s++;
+ if ((base == 0 || base == 16) &&
+ c == '0' && (*s == 'x' || *s == 'X')) {
+ c = s[1];
+ s += 2;
+ base = 16;
+ }
+ if (base == 0)
+ base = c == '0' ? 8 : 10;
+ cutoff = (unsigned long)ULONG_MAX / (unsigned long)base;
+ cutlim = (int)((unsigned long)ULONG_MAX % (unsigned long)base);
+ for (acc = 0, any = 0;; c = *s++) {
+ if (isdigit(c))
+ c -= '0';
+ else if (isalpha(c))
+ c -= isupper(c) ? 'A' - 10 : 'a' - 10;
+ else
+ break;
+ if (c >= base)
+ break;
+ if (any < 0 || acc > cutoff || acc == cutoff && c > cutlim)
+ any = -1;
+ else {
+ any = 1;
+ acc *= base;
+ acc += c;
+ }
+ }
+ if (any < 0) {
+ acc = ULONG_MAX;
+ errno = ERANGE;
+ } else if (neg)
+ acc = -acc;
+ if (endptr != 0)
+ *endptr = (char *) (any ? s - 1 : nptr);
+ return (acc);
+}
+#endif /* HAVE_STRTOUL */
+
+#ifndef HAVE_SETLINEBUF
+ int setlinebuf(FILE *stream)
+{
+ return setvbuf(stream, (char *)NULL, _IOLBF, 0);
+}
+#endif /* HAVE_SETLINEBUF */
+
+#ifndef HAVE_VSYSLOG
+#ifdef HAVE_SYSLOG
+ void vsyslog (int facility_priority, char *format, va_list arglist)
+{
+ char *msg = NULL;
+ vasprintf(&msg, format, arglist);
+ if (!msg)
+ return;
+ syslog(facility_priority, "%s", msg);
+ SAFE_FREE(msg);
+}
+#endif /* HAVE_SYSLOG */
+#endif /* HAVE_VSYSLOG */
+
+
+#ifndef HAVE_TIMEGM
+/*
+ yes, I know this looks insane, but its really needed. The function in the
+ Linux timegm() manpage does not work on solaris.
+*/
+ time_t timegm(struct tm *tm)
+{
+ struct tm tm2, tm3;
+ time_t t;
+
+ tm2 = *tm;
+
+ t = mktime(&tm2);
+ tm3 = *localtime(&t);
+ tm2 = *tm;
+ tm2.tm_isdst = tm3.tm_isdst;
+ t = mktime(&tm2);
+ t -= TimeDiff(t);
+
+ return t;
+}
+#endif
diff --git a/source/lib/replace1.c b/source/lib/replace1.c
new file mode 100644
index 00000000000..e1be56eb128
--- /dev/null
+++ b/source/lib/replace1.c
@@ -0,0 +1,42 @@
+/*
+ Unix SMB/CIFS implementation.
+ replacement routines for broken systems
+ 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 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"
+
+ void replace1_dummy(void);
+ void replace1_dummy(void) {}
+
+#ifndef HAVE_SETENV
+ int setenv(const char *name, const char *value, int overwrite)
+{
+ char *p = NULL;
+ int ret = -1;
+
+ asprintf(&p, "%s=%s", name, value);
+
+ if (overwrite || getenv(name)) {
+ if (p) ret = putenv(p);
+ } else {
+ ret = 0;
+ }
+
+ return ret;
+}
+#endif
diff --git a/source/lib/secace.c b/source/lib/secace.c
new file mode 100644
index 00000000000..8c54c970433
--- /dev/null
+++ b/source/lib/secace.c
@@ -0,0 +1,285 @@
+/*
+ * Unix SMB/Netbios implementation.
+ * SEC_ACE handling functions
+ * Copyright (C) Andrew Tridgell 1992-1998,
+ * Copyright (C) Jeremy R. Allison 1995-2003.
+ * Copyright (C) Luke Kenneth Casson Leighton 1996-1998,
+ * Copyright (C) Paul Ashton 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 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"
+
+/*******************************************************************
+ Check if ACE has OBJECT type.
+********************************************************************/
+
+BOOL sec_ace_object(uint8 type)
+{
+ if (type == SEC_ACE_TYPE_ACCESS_ALLOWED_OBJECT ||
+ type == SEC_ACE_TYPE_ACCESS_DENIED_OBJECT ||
+ type == SEC_ACE_TYPE_SYSTEM_AUDIT_OBJECT ||
+ type == SEC_ACE_TYPE_SYSTEM_ALARM_OBJECT) {
+ return True;
+ }
+ return False;
+}
+
+/*******************************************************************
+ copy a SEC_ACE structure.
+********************************************************************/
+void sec_ace_copy(SEC_ACE *ace_dest, SEC_ACE *ace_src)
+{
+ ace_dest->type = ace_src->type;
+ ace_dest->flags = ace_src->flags;
+ ace_dest->size = ace_src->size;
+ ace_dest->info.mask = ace_src->info.mask;
+ ace_dest->obj_flags = ace_src->obj_flags;
+ memcpy(&ace_dest->obj_guid, &ace_src->obj_guid, sizeof(struct uuid));
+ memcpy(&ace_dest->inh_guid, &ace_src->inh_guid, sizeof(struct uuid));
+ sid_copy(&ace_dest->trustee, &ace_src->trustee);
+}
+
+/*******************************************************************
+ Sets up a SEC_ACE structure.
+********************************************************************/
+
+void init_sec_ace(SEC_ACE *t, DOM_SID *sid, uint8 type, SEC_ACCESS mask, uint8 flag)
+{
+ t->type = type;
+ t->flags = flag;
+ t->size = sid_size(sid) + 8;
+ t->info = mask;
+
+ ZERO_STRUCTP(&t->trustee);
+ sid_copy(&t->trustee, sid);
+}
+
+/*******************************************************************
+ adds new SID with its permissions to ACE list
+********************************************************************/
+
+NTSTATUS sec_ace_add_sid(TALLOC_CTX *ctx, SEC_ACE **new, SEC_ACE *old, unsigned *num, DOM_SID *sid, uint32 mask)
+{
+ unsigned int i = 0;
+
+ if (!ctx || !new || !old || !sid || !num) return NT_STATUS_INVALID_PARAMETER;
+
+ *num += 1;
+
+ if((new[0] = (SEC_ACE *) talloc_zero(ctx, (*num) * sizeof(SEC_ACE))) == 0)
+ return NT_STATUS_NO_MEMORY;
+
+ for (i = 0; i < *num - 1; i ++)
+ sec_ace_copy(&(*new)[i], &old[i]);
+
+ (*new)[i].type = 0;
+ (*new)[i].flags = 0;
+ (*new)[i].size = SEC_ACE_HEADER_SIZE + sid_size(sid);
+ (*new)[i].info.mask = mask;
+ sid_copy(&(*new)[i].trustee, sid);
+ return NT_STATUS_OK;
+}
+
+/*******************************************************************
+ modify SID's permissions at ACL
+********************************************************************/
+
+NTSTATUS sec_ace_mod_sid(SEC_ACE *ace, size_t num, DOM_SID *sid, uint32 mask)
+{
+ unsigned int i = 0;
+
+ if (!ace || !sid) return NT_STATUS_INVALID_PARAMETER;
+
+ for (i = 0; i < num; i ++) {
+ if (sid_compare(&ace[i].trustee, sid) == 0) {
+ ace[i].info.mask = mask;
+ return NT_STATUS_OK;
+ }
+ }
+ return NT_STATUS_NOT_FOUND;
+}
+
+/*******************************************************************
+ delete SID from ACL
+********************************************************************/
+
+NTSTATUS sec_ace_del_sid(TALLOC_CTX *ctx, SEC_ACE **new, SEC_ACE *old, uint32 *num, DOM_SID *sid)
+{
+ unsigned int i = 0;
+ unsigned int n_del = 0;
+
+ if (!ctx || !new || !old || !sid || !num) return NT_STATUS_INVALID_PARAMETER;
+
+ if((new[0] = (SEC_ACE *) talloc_zero(ctx, (*num) * sizeof(SEC_ACE))) == 0)
+ return NT_STATUS_NO_MEMORY;
+
+ for (i = 0; i < *num; i ++) {
+ if (sid_compare(&old[i].trustee, sid) != 0)
+ sec_ace_copy(&(*new)[i], &old[i]);
+ else
+ n_del ++;
+ }
+ if (n_del == 0)
+ return NT_STATUS_NOT_FOUND;
+ else {
+ *num -= n_del;
+ return NT_STATUS_OK;
+ }
+}
+
+/*******************************************************************
+ Compares two SEC_ACE structures
+********************************************************************/
+
+BOOL sec_ace_equal(SEC_ACE *s1, SEC_ACE *s2)
+{
+ /* Trivial case */
+
+ if (!s1 && !s2) return True;
+
+ /* Check top level stuff */
+
+ if (s1->type != s2->type || s1->flags != s2->flags ||
+ s1->info.mask != s2->info.mask) {
+ return False;
+ }
+
+ /* Check SID */
+
+ if (!sid_equal(&s1->trustee, &s2->trustee)) {
+ return False;
+ }
+
+ return True;
+}
+
+int nt_ace_inherit_comp( SEC_ACE *a1, SEC_ACE *a2)
+{
+ int a1_inh = a1->flags & SEC_ACE_FLAG_INHERITED_ACE;
+ int a2_inh = a2->flags & SEC_ACE_FLAG_INHERITED_ACE;
+
+ if (a1_inh == a2_inh)
+ return 0;
+
+ if (!a1_inh && a2_inh)
+ return -1;
+ return 1;
+}
+
+/*******************************************************************
+ Comparison function to apply the order explained below in a group.
+*******************************************************************/
+
+int nt_ace_canon_comp( SEC_ACE *a1, SEC_ACE *a2)
+{
+ if ((a1->type == SEC_ACE_TYPE_ACCESS_DENIED) &&
+ (a2->type != SEC_ACE_TYPE_ACCESS_DENIED))
+ return -1;
+
+ if ((a2->type == SEC_ACE_TYPE_ACCESS_DENIED) &&
+ (a1->type != SEC_ACE_TYPE_ACCESS_DENIED))
+ return 1;
+
+ /* Both access denied or access allowed. */
+
+ /* 1. ACEs that apply to the object itself */
+
+ if (!(a1->flags & SEC_ACE_FLAG_INHERIT_ONLY) &&
+ (a2->flags & SEC_ACE_FLAG_INHERIT_ONLY))
+ return -1;
+ else if (!(a2->flags & SEC_ACE_FLAG_INHERIT_ONLY) &&
+ (a1->flags & SEC_ACE_FLAG_INHERIT_ONLY))
+ return 1;
+
+ /* 2. ACEs that apply to a subobject of the object, such as
+ * a property set or property. */
+
+ if (a1->flags & (SEC_ACE_FLAG_CONTAINER_INHERIT|SEC_ACE_FLAG_OBJECT_INHERIT) &&
+ !(a2->flags & (SEC_ACE_FLAG_CONTAINER_INHERIT|SEC_ACE_FLAG_OBJECT_INHERIT)))
+ return -1;
+ else if (a2->flags & (SEC_ACE_FLAG_CONTAINER_INHERIT|SEC_ACE_FLAG_OBJECT_INHERIT) &&
+ !(a1->flags & (SEC_ACE_FLAG_CONTAINER_INHERIT|SEC_ACE_FLAG_OBJECT_INHERIT)))
+ return 1;
+
+ return 0;
+}
+
+/*******************************************************************
+ Functions to convert a SEC_DESC ACE DACL list into canonical order.
+ JRA.
+
+--- from http://msdn.microsoft.com/library/default.asp?url=/library/en-us/security/security/order_of_aces_in_a_dacl.asp
+
+The following describes the preferred order:
+
+ To ensure that noninherited ACEs have precedence over inherited ACEs,
+ place all noninherited ACEs in a group before any inherited ACEs.
+ This ordering ensures, for example, that a noninherited access-denied ACE
+ is enforced regardless of any inherited ACE that allows access.
+
+ Within the groups of noninherited ACEs and inherited ACEs, order ACEs according to ACE type, as the following shows:
+ 1. Access-denied ACEs that apply to the object itself
+ 2. Access-denied ACEs that apply to a subobject of the object, such as a property set or property
+ 3. Access-allowed ACEs that apply to the object itself
+ 4. Access-allowed ACEs that apply to a subobject of the object"
+
+********************************************************************/
+
+void dacl_sort_into_canonical_order(SEC_ACE *srclist, unsigned int num_aces)
+{
+ unsigned int i;
+
+ if (!srclist || num_aces == 0)
+ return;
+
+ /* Sort so that non-inherited ACE's come first. */
+ qsort( srclist, num_aces, sizeof(srclist[0]), QSORT_CAST nt_ace_inherit_comp);
+
+ /* Find the boundary between non-inherited ACEs. */
+ for (i = 0; i < num_aces; i++ ) {
+ SEC_ACE *curr_ace = &srclist[i];
+
+ if (curr_ace->flags & SEC_ACE_FLAG_INHERITED_ACE)
+ break;
+ }
+
+ /* i now points at entry number of the first inherited ACE. */
+
+ /* Sort the non-inherited ACEs. */
+ if (i)
+ qsort( srclist, i, sizeof(srclist[0]), QSORT_CAST nt_ace_canon_comp);
+
+ /* Now sort the inherited ACEs. */
+ if (num_aces - i)
+ qsort( &srclist[i], num_aces - i, sizeof(srclist[0]), QSORT_CAST nt_ace_canon_comp);
+}
+
+/*******************************************************************
+ Check if this ACE has a SID in common with the token.
+********************************************************************/
+
+BOOL token_sid_in_ace(const NT_USER_TOKEN *token, const SEC_ACE *ace)
+{
+ size_t i;
+
+ for (i = 0; i < token->num_sids; i++) {
+ if (sid_equal(&ace->trustee, &token->user_sids[i]))
+ return True;
+ }
+
+ return False;
+}
diff --git a/source/lib/secacl.c b/source/lib/secacl.c
new file mode 100644
index 00000000000..756685a8216
--- /dev/null
+++ b/source/lib/secacl.c
@@ -0,0 +1,118 @@
+/*
+ * Unix SMB/Netbios implementation.
+ * SEC_ACL handling routines
+ * Copyright (C) Andrew Tridgell 1992-1998,
+ * Copyright (C) Jeremy R. Allison 1995-2003.
+ * Copyright (C) Luke Kenneth Casson Leighton 1996-1998,
+ * Copyright (C) Paul Ashton 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 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"
+
+/*******************************************************************
+ Create a SEC_ACL structure.
+********************************************************************/
+
+SEC_ACL *make_sec_acl(TALLOC_CTX *ctx, uint16 revision, int num_aces, SEC_ACE *ace_list)
+{
+ SEC_ACL *dst;
+ int i;
+
+ if((dst = (SEC_ACL *)talloc_zero(ctx,sizeof(SEC_ACL))) == NULL)
+ return NULL;
+
+ dst->revision = revision;
+ dst->num_aces = num_aces;
+ dst->size = SEC_ACL_HEADER_SIZE;
+
+ /* Now we need to return a non-NULL address for the ace list even
+ if the number of aces required is zero. This is because there
+ is a distinct difference between a NULL ace and an ace with zero
+ entries in it. This is achieved by checking that num_aces is a
+ positive number. */
+
+ if ((num_aces) &&
+ ((dst->ace = (SEC_ACE *)talloc(ctx, sizeof(SEC_ACE) * num_aces))
+ == NULL)) {
+ return NULL;
+ }
+
+ for (i = 0; i < num_aces; i++) {
+ dst->ace[i] = ace_list[i]; /* Structure copy. */
+ dst->size += ace_list[i].size;
+ }
+
+ return dst;
+}
+
+/*******************************************************************
+ Duplicate a SEC_ACL structure.
+********************************************************************/
+
+SEC_ACL *dup_sec_acl(TALLOC_CTX *ctx, SEC_ACL *src)
+{
+ if(src == NULL)
+ return NULL;
+
+ return make_sec_acl(ctx, src->revision, src->num_aces, src->ace);
+}
+
+/*******************************************************************
+ Compares two SEC_ACL structures
+********************************************************************/
+
+BOOL sec_acl_equal(SEC_ACL *s1, SEC_ACL *s2)
+{
+ unsigned int i, j;
+
+ /* Trivial cases */
+
+ if (!s1 && !s2) return True;
+ if (!s1 || !s2) return False;
+
+ /* Check top level stuff */
+
+ if (s1->revision != s2->revision) {
+ DEBUG(10, ("sec_acl_equal(): revision differs (%d != %d)\n",
+ s1->revision, s2->revision));
+ return False;
+ }
+
+ if (s1->num_aces != s2->num_aces) {
+ DEBUG(10, ("sec_acl_equal(): num_aces differs (%d != %d)\n",
+ s1->revision, s2->revision));
+ return False;
+ }
+
+ /* The ACEs could be in any order so check each ACE in s1 against
+ each ACE in s2. */
+
+ for (i = 0; i < s1->num_aces; i++) {
+ BOOL found = False;
+
+ for (j = 0; j < s2->num_aces; j++) {
+ if (sec_ace_equal(&s1->ace[i], &s2->ace[j])) {
+ found = True;
+ break;
+ }
+ }
+
+ if (!found) return False;
+ }
+
+ return True;
+}
diff --git a/source/lib/secdesc.c b/source/lib/secdesc.c
new file mode 100644
index 00000000000..411185dbfa6
--- /dev/null
+++ b/source/lib/secdesc.c
@@ -0,0 +1,522 @@
+/*
+ * Unix SMB/Netbios implementation.
+ * SEC_DESC handling functions
+ * Copyright (C) Andrew Tridgell 1992-1998,
+ * Copyright (C) Jeremy R. Allison 1995-2003.
+ * Copyright (C) Luke Kenneth Casson Leighton 1996-1998,
+ * Copyright (C) Paul Ashton 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 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"
+
+/*******************************************************************
+ Works out the linearization size of a SEC_DESC.
+********************************************************************/
+
+size_t sec_desc_size(SEC_DESC *psd)
+{
+ size_t offset;
+
+ if (!psd) return 0;
+
+ offset = SEC_DESC_HEADER_SIZE;
+
+ /* don't align */
+
+ if (psd->owner_sid != NULL)
+ offset += sid_size(psd->owner_sid);
+
+ if (psd->grp_sid != NULL)
+ offset += sid_size(psd->grp_sid);
+
+ if (psd->sacl != NULL)
+ offset += psd->sacl->size;
+
+ if (psd->dacl != NULL)
+ offset += psd->dacl->size;
+
+ return offset;
+}
+
+/*******************************************************************
+ Compares two SEC_DESC structures
+********************************************************************/
+
+BOOL sec_desc_equal(SEC_DESC *s1, SEC_DESC *s2)
+{
+ /* Trivial case */
+
+ if (!s1 && !s2) {
+ goto done;
+ }
+
+ /* Check top level stuff */
+
+ if (s1->revision != s2->revision) {
+ DEBUG(10, ("sec_desc_equal(): revision differs (%d != %d)\n",
+ s1->revision, s2->revision));
+ return False;
+ }
+
+ if (s1->type!= s2->type) {
+ DEBUG(10, ("sec_desc_equal(): type differs (%d != %d)\n",
+ s1->type, s2->type));
+ return False;
+ }
+
+ /* Check owner and group */
+
+ if (!sid_equal(s1->owner_sid, s2->owner_sid)) {
+ fstring str1, str2;
+
+ sid_to_string(str1, s1->owner_sid);
+ sid_to_string(str2, s2->owner_sid);
+
+ DEBUG(10, ("sec_desc_equal(): owner differs (%s != %s)\n",
+ str1, str2));
+ return False;
+ }
+
+ if (!sid_equal(s1->grp_sid, s2->grp_sid)) {
+ fstring str1, str2;
+
+ sid_to_string(str1, s1->grp_sid);
+ sid_to_string(str2, s2->grp_sid);
+
+ DEBUG(10, ("sec_desc_equal(): group differs (%s != %s)\n",
+ str1, str2));
+ return False;
+ }
+
+ /* Check ACLs present in one but not the other */
+
+ if ((s1->dacl && !s2->dacl) || (!s1->dacl && s2->dacl) ||
+ (s1->sacl && !s2->sacl) || (!s1->sacl && s2->sacl)) {
+ DEBUG(10, ("sec_desc_equal(): dacl or sacl not present\n"));
+ return False;
+ }
+
+ /* Sigh - we have to do it the hard way by iterating over all
+ the ACEs in the ACLs */
+
+ if (!sec_acl_equal(s1->dacl, s2->dacl) ||
+ !sec_acl_equal(s1->sacl, s2->sacl)) {
+ DEBUG(10, ("sec_desc_equal(): dacl/sacl list not equal\n"));
+ return False;
+ }
+
+ done:
+ DEBUG(10, ("sec_desc_equal(): secdescs are identical\n"));
+ return True;
+}
+
+/*******************************************************************
+ Merge part of security descriptor old_sec in to the empty sections of
+ security descriptor new_sec.
+********************************************************************/
+
+SEC_DESC_BUF *sec_desc_merge(TALLOC_CTX *ctx, SEC_DESC_BUF *new_sdb, SEC_DESC_BUF *old_sdb)
+{
+ DOM_SID *owner_sid, *group_sid;
+ SEC_DESC_BUF *return_sdb;
+ SEC_ACL *dacl, *sacl;
+ SEC_DESC *psd = NULL;
+ uint16 secdesc_type;
+ size_t secdesc_size;
+
+ /* Copy over owner and group sids. There seems to be no flag for
+ this so just check the pointer values. */
+
+ owner_sid = new_sdb->sec->owner_sid ? new_sdb->sec->owner_sid :
+ old_sdb->sec->owner_sid;
+
+ group_sid = new_sdb->sec->grp_sid ? new_sdb->sec->grp_sid :
+ old_sdb->sec->grp_sid;
+
+ secdesc_type = new_sdb->sec->type;
+
+ /* Ignore changes to the system ACL. This has the effect of making
+ changes through the security tab audit button not sticking.
+ Perhaps in future Samba could implement these settings somehow. */
+
+ sacl = NULL;
+ secdesc_type &= ~SEC_DESC_SACL_PRESENT;
+
+ /* Copy across discretionary ACL */
+
+ if (secdesc_type & SEC_DESC_DACL_PRESENT) {
+ dacl = new_sdb->sec->dacl;
+ } else {
+ dacl = old_sdb->sec->dacl;
+ }
+
+ /* Create new security descriptor from bits */
+
+ psd = make_sec_desc(ctx, new_sdb->sec->revision, secdesc_type,
+ owner_sid, group_sid, sacl, dacl, &secdesc_size);
+
+ return_sdb = make_sec_desc_buf(ctx, secdesc_size, psd);
+
+ return(return_sdb);
+}
+
+/*******************************************************************
+ Creates a SEC_DESC structure
+********************************************************************/
+
+SEC_DESC *make_sec_desc(TALLOC_CTX *ctx, uint16 revision, uint16 type,
+ DOM_SID *owner_sid, DOM_SID *grp_sid,
+ SEC_ACL *sacl, SEC_ACL *dacl, size_t *sd_size)
+{
+ SEC_DESC *dst;
+ uint32 offset = 0;
+
+ *sd_size = 0;
+
+ if(( dst = (SEC_DESC *)talloc_zero(ctx, sizeof(SEC_DESC))) == NULL)
+ return NULL;
+
+ dst->revision = revision;
+ dst->type = type;
+
+ if (sacl)
+ dst->type |= SEC_DESC_SACL_PRESENT;
+ if (dacl)
+ dst->type |= SEC_DESC_DACL_PRESENT;
+
+ dst->off_owner_sid = 0;
+ dst->off_grp_sid = 0;
+ dst->off_sacl = 0;
+ dst->off_dacl = 0;
+
+ if(owner_sid && ((dst->owner_sid = sid_dup_talloc(ctx,owner_sid)) == NULL))
+ goto error_exit;
+
+ if(grp_sid && ((dst->grp_sid = sid_dup_talloc(ctx,grp_sid)) == NULL))
+ goto error_exit;
+
+ if(sacl && ((dst->sacl = dup_sec_acl(ctx, sacl)) == NULL))
+ goto error_exit;
+
+ if(dacl && ((dst->dacl = dup_sec_acl(ctx, dacl)) == NULL))
+ goto error_exit;
+
+ offset = SEC_DESC_HEADER_SIZE;
+
+ /*
+ * Work out the linearization sizes.
+ */
+
+ if (dst->sacl != NULL) {
+ dst->off_sacl = offset;
+ offset += dst->sacl->size;
+ }
+ if (dst->dacl != NULL) {
+ dst->off_dacl = offset;
+ offset += dst->dacl->size;
+ }
+
+ if (dst->owner_sid != NULL) {
+ dst->off_owner_sid = offset;
+ offset += sid_size(dst->owner_sid);
+ }
+
+ if (dst->grp_sid != NULL) {
+ dst->off_grp_sid = offset;
+ offset += sid_size(dst->grp_sid);
+ }
+
+ *sd_size = (size_t)offset;
+ return dst;
+
+error_exit:
+
+ *sd_size = 0;
+ return NULL;
+}
+
+/*******************************************************************
+ Duplicate a SEC_DESC structure.
+********************************************************************/
+
+SEC_DESC *dup_sec_desc(TALLOC_CTX *ctx, const SEC_DESC *src)
+{
+ size_t dummy;
+
+ if(src == NULL)
+ return NULL;
+
+ return make_sec_desc( ctx, src->revision, src->type,
+ src->owner_sid, src->grp_sid, src->sacl,
+ src->dacl, &dummy);
+}
+
+/*******************************************************************
+ Creates a SEC_DESC structure with typical defaults.
+********************************************************************/
+
+SEC_DESC *make_standard_sec_desc(TALLOC_CTX *ctx, DOM_SID *owner_sid, DOM_SID *grp_sid,
+ SEC_ACL *dacl, size_t *sd_size)
+{
+ return make_sec_desc(ctx, SEC_DESC_REVISION, SEC_DESC_SELF_RELATIVE,
+ owner_sid, grp_sid, NULL, dacl, sd_size);
+}
+
+/*******************************************************************
+ Creates a SEC_DESC_BUF structure.
+********************************************************************/
+
+SEC_DESC_BUF *make_sec_desc_buf(TALLOC_CTX *ctx, size_t len, SEC_DESC *sec_desc)
+{
+ SEC_DESC_BUF *dst;
+
+ if((dst = (SEC_DESC_BUF *)talloc_zero(ctx, sizeof(SEC_DESC_BUF))) == NULL)
+ return NULL;
+
+ /* max buffer size (allocated size) */
+ dst->max_len = (uint32)len;
+ dst->len = (uint32)len;
+
+ if(sec_desc && ((dst->sec = dup_sec_desc(ctx, sec_desc)) == NULL)) {
+ return NULL;
+ }
+
+ dst->ptr = 0x1;
+
+ return dst;
+}
+
+/*******************************************************************
+ Duplicates a SEC_DESC_BUF structure.
+********************************************************************/
+
+SEC_DESC_BUF *dup_sec_desc_buf(TALLOC_CTX *ctx, SEC_DESC_BUF *src)
+{
+ if(src == NULL)
+ return NULL;
+
+ return make_sec_desc_buf( ctx, src->len, src->sec);
+}
+
+/*******************************************************************
+ Add a new SID with its permissions to SEC_DESC.
+********************************************************************/
+
+NTSTATUS sec_desc_add_sid(TALLOC_CTX *ctx, SEC_DESC **psd, DOM_SID *sid, uint32 mask, size_t *sd_size)
+{
+ SEC_DESC *sd = 0;
+ SEC_ACL *dacl = 0;
+ SEC_ACE *ace = 0;
+ NTSTATUS status;
+
+ *sd_size = 0;
+
+ if (!ctx || !psd || !sid || !sd_size)
+ return NT_STATUS_INVALID_PARAMETER;
+
+ status = sec_ace_add_sid(ctx, &ace, psd[0]->dacl->ace, &psd[0]->dacl->num_aces, sid, mask);
+
+ if (!NT_STATUS_IS_OK(status))
+ return status;
+
+ if (!(dacl = make_sec_acl(ctx, psd[0]->dacl->revision, psd[0]->dacl->num_aces, ace)))
+ return NT_STATUS_UNSUCCESSFUL;
+
+ if (!(sd = make_sec_desc(ctx, psd[0]->revision, psd[0]->type, psd[0]->owner_sid,
+ psd[0]->grp_sid, psd[0]->sacl, dacl, sd_size)))
+ return NT_STATUS_UNSUCCESSFUL;
+
+ *psd = sd;
+ sd = 0;
+ return NT_STATUS_OK;
+}
+
+/*******************************************************************
+ Modify a SID's permissions in a SEC_DESC.
+********************************************************************/
+
+NTSTATUS sec_desc_mod_sid(SEC_DESC *sd, DOM_SID *sid, uint32 mask)
+{
+ NTSTATUS status;
+
+ if (!sd || !sid)
+ return NT_STATUS_INVALID_PARAMETER;
+
+ status = sec_ace_mod_sid(sd->dacl->ace, sd->dacl->num_aces, sid, mask);
+
+ if (!NT_STATUS_IS_OK(status))
+ return status;
+
+ return NT_STATUS_OK;
+}
+
+/*******************************************************************
+ Delete a SID from a SEC_DESC.
+********************************************************************/
+
+NTSTATUS sec_desc_del_sid(TALLOC_CTX *ctx, SEC_DESC **psd, DOM_SID *sid, size_t *sd_size)
+{
+ SEC_DESC *sd = 0;
+ SEC_ACL *dacl = 0;
+ SEC_ACE *ace = 0;
+ NTSTATUS status;
+
+ *sd_size = 0;
+
+ if (!ctx || !psd[0] || !sid || !sd_size)
+ return NT_STATUS_INVALID_PARAMETER;
+
+ status = sec_ace_del_sid(ctx, &ace, psd[0]->dacl->ace, &psd[0]->dacl->num_aces, sid);
+
+ if (!NT_STATUS_IS_OK(status))
+ return status;
+
+ if (!(dacl = make_sec_acl(ctx, psd[0]->dacl->revision, psd[0]->dacl->num_aces, ace)))
+ return NT_STATUS_UNSUCCESSFUL;
+
+ if (!(sd = make_sec_desc(ctx, psd[0]->revision, psd[0]->type, psd[0]->owner_sid,
+ psd[0]->grp_sid, psd[0]->sacl, dacl, sd_size)))
+ return NT_STATUS_UNSUCCESSFUL;
+
+ *psd = sd;
+ sd = 0;
+ return NT_STATUS_OK;
+}
+
+/* Create a child security descriptor using another security descriptor as
+ the parent container. This child object can either be a container or
+ non-container object. */
+
+SEC_DESC_BUF *se_create_child_secdesc(TALLOC_CTX *ctx, SEC_DESC *parent_ctr,
+ BOOL child_container)
+{
+ SEC_DESC_BUF *sdb;
+ SEC_DESC *sd;
+ SEC_ACL *new_dacl, *the_acl;
+ SEC_ACE *new_ace_list = NULL;
+ unsigned int new_ace_list_ndx = 0, i;
+ size_t size;
+
+ /* Currently we only process the dacl when creating the child. The
+ sacl should also be processed but this is left out as sacls are
+ not implemented in Samba at the moment.*/
+
+ the_acl = parent_ctr->dacl;
+
+ if (!(new_ace_list = talloc(ctx, sizeof(SEC_ACE) * the_acl->num_aces)))
+ return NULL;
+
+ for (i = 0; the_acl && i < the_acl->num_aces; i++) {
+ SEC_ACE *ace = &the_acl->ace[i];
+ SEC_ACE *new_ace = &new_ace_list[new_ace_list_ndx];
+ uint8 new_flags = 0;
+ BOOL inherit = False;
+ fstring sid_str;
+
+ /* The OBJECT_INHERIT_ACE flag causes the ACE to be
+ inherited by non-container children objects. Container
+ children objects will inherit it as an INHERIT_ONLY
+ ACE. */
+
+ if (ace->flags & SEC_ACE_FLAG_OBJECT_INHERIT) {
+
+ if (!child_container) {
+ new_flags |= SEC_ACE_FLAG_OBJECT_INHERIT;
+ } else {
+ new_flags |= SEC_ACE_FLAG_INHERIT_ONLY;
+ }
+
+ inherit = True;
+ }
+
+ /* The CONAINER_INHERIT_ACE flag means all child container
+ objects will inherit and use the ACE. */
+
+ if (ace->flags & SEC_ACE_FLAG_CONTAINER_INHERIT) {
+ if (!child_container) {
+ inherit = False;
+ } else {
+ new_flags |= SEC_ACE_FLAG_CONTAINER_INHERIT;
+ }
+ }
+
+ /* The INHERIT_ONLY_ACE is not used by the se_access_check()
+ function for the parent container, but is inherited by
+ all child objects as a normal ACE. */
+
+ if (ace->flags & SEC_ACE_FLAG_INHERIT_ONLY) {
+ /* Move along, nothing to see here */
+ }
+
+ /* The SEC_ACE_FLAG_NO_PROPAGATE_INHERIT flag means the ACE
+ is inherited by child objects but not grandchildren
+ objects. We clear the object inherit and container
+ inherit flags in the inherited ACE. */
+
+ if (ace->flags & SEC_ACE_FLAG_NO_PROPAGATE_INHERIT) {
+ new_flags &= ~(SEC_ACE_FLAG_OBJECT_INHERIT |
+ SEC_ACE_FLAG_CONTAINER_INHERIT);
+ }
+
+ /* Add ACE to ACE list */
+
+ if (!inherit)
+ continue;
+
+ init_sec_access(&new_ace->info, ace->info.mask);
+ init_sec_ace(new_ace, &ace->trustee, ace->type,
+ new_ace->info, new_flags);
+
+ sid_to_string(sid_str, &ace->trustee);
+
+ DEBUG(5, ("se_create_child_secdesc(): %s:%d/0x%02x/0x%08x "
+ " inherited as %s:%d/0x%02x/0x%08x\n", sid_str,
+ ace->type, ace->flags, ace->info.mask,
+ sid_str, new_ace->type, new_ace->flags,
+ new_ace->info.mask));
+
+ new_ace_list_ndx++;
+ }
+
+ /* Create child security descriptor to return */
+
+ new_dacl = make_sec_acl(ctx, ACL_REVISION, new_ace_list_ndx, new_ace_list);
+
+ /* Use the existing user and group sids. I don't think this is
+ correct. Perhaps the user and group should be passed in as
+ parameters by the caller? */
+
+ sd = make_sec_desc(ctx, SEC_DESC_REVISION, SEC_DESC_SELF_RELATIVE,
+ parent_ctr->owner_sid,
+ parent_ctr->grp_sid,
+ parent_ctr->sacl,
+ new_dacl, &size);
+
+ sdb = make_sec_desc_buf(ctx, size, sd);
+
+ return sdb;
+}
+
+/*******************************************************************
+ Sets up a SEC_ACCESS structure.
+********************************************************************/
+
+void init_sec_access(SEC_ACCESS *t, uint32 mask)
+{
+ t->mask = mask;
+}
+
diff --git a/source/lib/select.c b/source/lib/select.c
new file mode 100644
index 00000000000..f88ad52de65
--- /dev/null
+++ b/source/lib/select.c
@@ -0,0 +1,159 @@
+/*
+ Unix SMB/Netbios implementation.
+ Version 3.0
+ Samba select/poll implementation
+ 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 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"
+
+/* This is here because it allows us to avoid a nasty race in signal handling.
+ We need to guarantee that when we get a signal we get out of a select immediately
+ but doing that involves a race condition. We can avoid the race by getting the
+ signal handler to write to a pipe that is in the select/poll list
+
+ This means all Samba signal handlers should call sys_select_signal().
+*/
+
+static pid_t initialised;
+static int select_pipe[2];
+static VOLATILE unsigned pipe_written, pipe_read;
+
+/*******************************************************************
+ Call this from all Samba signal handlers if you want to avoid a
+ nasty signal race condition.
+********************************************************************/
+
+void sys_select_signal(void)
+{
+ char c = 1;
+ if (!initialised) return;
+
+ if (pipe_written > pipe_read+256) return;
+
+ if (write(select_pipe[1], &c, 1) == 1) pipe_written++;
+}
+
+/*******************************************************************
+ Like select() but avoids the signal race using a pipe
+ it also guuarantees that fds on return only ever contains bits set
+ for file descriptors that were readable.
+********************************************************************/
+
+int sys_select(int maxfd, fd_set *readfds, fd_set *writefds, fd_set *errorfds, struct timeval *tval)
+{
+ int ret, saved_errno;
+ fd_set *readfds2, readfds_buf;
+
+ if (initialised != sys_getpid()) {
+ pipe(select_pipe);
+
+ /*
+ * These next two lines seem to fix a bug with the Linux
+ * 2.0.x kernel (and probably other UNIXes as well) where
+ * the one byte read below can block even though the
+ * select returned that there is data in the pipe and
+ * the pipe_written variable was incremented. Thanks to
+ * HP for finding this one. JRA.
+ */
+
+ if(set_blocking(select_pipe[0],0)==-1)
+ smb_panic("select_pipe[0]: O_NONBLOCK failed.\n");
+ if(set_blocking(select_pipe[1],0)==-1)
+ smb_panic("select_pipe[1]: O_NONBLOCK failed.\n");
+
+ initialised = sys_getpid();
+ }
+
+ maxfd = MAX(select_pipe[0]+1, maxfd);
+
+ /* If readfds is NULL we need to provide our own set. */
+ if (readfds) {
+ readfds2 = readfds;
+ } else {
+ readfds2 = &readfds_buf;
+ FD_ZERO(readfds2);
+ }
+ FD_SET(select_pipe[0], readfds2);
+
+ errno = 0;
+ ret = select(maxfd,readfds2,writefds,errorfds,tval);
+
+ if (ret <= 0) {
+ FD_ZERO(readfds2);
+ if (writefds)
+ FD_ZERO(writefds);
+ if (errorfds)
+ FD_ZERO(errorfds);
+ }
+
+ if (FD_ISSET(select_pipe[0], readfds2)) {
+ char c;
+ saved_errno = errno;
+ if (read(select_pipe[0], &c, 1) == 1) {
+ pipe_read++;
+ }
+ errno = saved_errno;
+ FD_CLR(select_pipe[0], readfds2);
+ ret--;
+ if (ret == 0) {
+ ret = -1;
+ errno = EINTR;
+ }
+ }
+
+ return ret;
+}
+
+/*******************************************************************
+ Similar to sys_select() but catch EINTR and continue.
+ This is what sys_select() used to do in Samba.
+********************************************************************/
+
+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;
+
+ ret = sys_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;
+}
diff --git a/source/lib/sendfile.c b/source/lib/sendfile.c
new file mode 100644
index 00000000000..4aa76a0c74a
--- /dev/null
+++ b/source/lib/sendfile.c
@@ -0,0 +1,382 @@
+/*
+ Unix SMB/Netbios implementation.
+ Version 2.2.x / 3.0.x
+ sendfile implementations.
+ Copyright (C) Jeremy Allison 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 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.
+*/
+
+/*
+ * This file handles the OS dependent sendfile implementations.
+ * The API is such that it returns -1 on error, else returns the
+ * number of bytes written.
+ */
+
+#include "includes.h"
+
+#if defined(LINUX_SENDFILE_API)
+
+#include <sys/sendfile.h>
+
+#ifndef MSG_MORE
+#define MSG_MORE 0x8000
+#endif
+
+ssize_t sys_sendfile(int tofd, int fromfd, const DATA_BLOB *header, SMB_OFF_T offset, size_t count)
+{
+ size_t total=0;
+ ssize_t ret;
+ size_t hdr_len = 0;
+
+ /*
+ * Send the header first.
+ * Use MSG_MORE to cork the TCP output until sendfile is called.
+ */
+
+ if (header) {
+ hdr_len = header->length;
+ while (total < hdr_len) {
+ ret = sys_send(tofd, header->data + total,hdr_len - total, MSG_MORE);
+ if (ret == -1)
+ return -1;
+ total += ret;
+ }
+ }
+
+ total = count;
+ while (total) {
+ ssize_t nwritten;
+ do {
+#if defined(HAVE_EXPLICIT_LARGEFILE_SUPPORT) && defined(HAVE_OFF64_T) && defined(HAVE_SENDFILE64)
+ nwritten = sendfile64(tofd, fromfd, &offset, total);
+#else
+ nwritten = sendfile(tofd, fromfd, &offset, total);
+#endif
+ } while (nwritten == -1 && errno == EINTR);
+ if (nwritten == -1)
+ return -1;
+ if (nwritten == 0)
+ return -1; /* I think we're at EOF here... */
+ total -= nwritten;
+ }
+ return count + hdr_len;
+}
+
+#elif defined(LINUX_BROKEN_SENDFILE_API)
+
+/*
+ * We must use explicit 32 bit types here. This code path means Linux
+ * won't do proper 64-bit sendfile. JRA.
+ */
+
+extern int32 sendfile (int out_fd, int in_fd, int32 *offset, uint32 count);
+
+
+#ifndef MSG_MORE
+#define MSG_MORE 0x8000
+#endif
+
+ssize_t sys_sendfile(int tofd, int fromfd, const DATA_BLOB *header, SMB_OFF_T offset, size_t count)
+{
+ size_t total=0;
+ ssize_t ret;
+ ssize_t hdr_len = 0;
+ uint32 small_total = 0;
+ int32 small_offset;
+
+ /*
+ * Fix for broken Linux 2.4 systems with no working sendfile64().
+ * If the offset+count > 2 GB then pretend we don't have the
+ * system call sendfile at all. The upper layer catches this
+ * and uses a normal read. JRA.
+ */
+
+ if ((sizeof(SMB_OFF_T) >= 8) && (offset + count > (SMB_OFF_T)0x7FFFFFFF)) {
+ errno = ENOSYS;
+ return -1;
+ }
+
+ /*
+ * Send the header first.
+ * Use MSG_MORE to cork the TCP output until sendfile is called.
+ */
+
+ if (header) {
+ hdr_len = header->length;
+ while (total < hdr_len) {
+ ret = sys_send(tofd, header->data + total,hdr_len - total, MSG_MORE);
+ if (ret == -1)
+ return -1;
+ total += ret;
+ }
+ }
+
+ small_total = (uint32)count;
+ small_offset = (int32)offset;
+
+ while (small_total) {
+ int32 nwritten;
+ do {
+ nwritten = sendfile(tofd, fromfd, &small_offset, small_total);
+ } while (nwritten == -1 && errno == EINTR);
+ if (nwritten == -1)
+ return -1;
+ if (nwritten == 0)
+ return -1; /* I think we're at EOF here... */
+ small_total -= nwritten;
+ }
+ return count + hdr_len;
+}
+
+
+#elif defined(SOLARIS_SENDFILE_API)
+
+/*
+ * Solaris sendfile code written by Pierre Belanger <belanger@pobox.com>.
+ */
+
+#include <sys/sendfile.h>
+
+ssize_t sys_sendfile(int tofd, int fromfd, const DATA_BLOB *header, SMB_OFF_T offset, size_t count)
+{
+ int sfvcnt;
+ size_t total, xferred;
+ struct sendfilevec vec[2];
+ ssize_t hdr_len = 0;
+
+ if (header) {
+ sfvcnt = 2;
+
+ vec[0].sfv_fd = SFV_FD_SELF;
+ vec[0].sfv_flag = 0;
+ vec[0].sfv_off = (off_t)header->data;
+ vec[0].sfv_len = hdr_len = header->length;
+
+ vec[1].sfv_fd = fromfd;
+ vec[1].sfv_flag = 0;
+ vec[1].sfv_off = offset;
+ vec[1].sfv_len = count;
+
+ } else {
+ sfvcnt = 1;
+
+ vec[0].sfv_fd = fromfd;
+ vec[0].sfv_flag = 0;
+ vec[0].sfv_off = offset;
+ vec[0].sfv_len = count;
+ }
+
+ total = count + hdr_len;
+
+ while (total) {
+ ssize_t nwritten;
+
+ /*
+ * Although not listed in the API error returns, this is almost certainly
+ * a slow system call and will be interrupted by a signal with EINTR. JRA.
+ */
+
+ xferred = 0;
+
+#if defined(HAVE_EXPLICIT_LARGEFILE_SUPPORT) && defined(HAVE_OFF64_T) && defined(HAVE_SENDFILEV64)
+ nwritten = sendfilev64(tofd, vec, sfvcnt, &xferred);
+#else
+ nwritten = sendfilev(tofd, vec, sfvcnt, &xferred);
+#endif
+ if (nwritten == -1 && errno == EINTR) {
+ if (xferred == 0)
+ continue; /* Nothing written yet. */
+ else
+ nwritten = xferred;
+ }
+
+ if (nwritten == -1)
+ return -1;
+ if (nwritten == 0)
+ return -1; /* I think we're at EOF here... */
+
+ /*
+ * If this was a short (signal interrupted) write we may need
+ * to subtract it from the header data, or null out the header
+ * data altogether if we wrote more than vec[0].sfv_len bytes.
+ * We move vec[1].* to vec[0].* and set sfvcnt to 1
+ */
+
+ if (sfvcnt == 2 && nwritten >= vec[0].sfv_len) {
+ vec[1].sfv_off += nwritten - vec[0].sfv_len;
+ vec[1].sfv_len -= nwritten - vec[0].sfv_len;
+
+ /* Move vec[1].* to vec[0].* and set sfvcnt to 1 */
+ vec[0] = vec[1];
+ sfvcnt = 1;
+ } else {
+ vec[0].sfv_off += nwritten;
+ vec[0].sfv_len -= nwritten;
+ }
+ total -= nwritten;
+ }
+ return count + hdr_len;
+}
+
+#elif defined(HPUX_SENDFILE_API)
+
+#include <sys/socket.h>
+#include <sys/uio.h>
+
+ssize_t sys_sendfile(int tofd, int fromfd, const DATA_BLOB *header, SMB_OFF_T offset, size_t count)
+{
+ size_t total=0;
+ struct iovec hdtrl[2];
+ size_t hdr_len = 0;
+
+ if (header) {
+ /* Set up the header/trailer iovec. */
+ hdtrl[0].iov_base = header->data;
+ hdtrl[0].iov_len = hdr_len = header->length;
+ } else {
+ hdtrl[0].iov_base = NULL;
+ hdtrl[0].iov_len = hdr_len = 0;
+ }
+ hdtrl[1].iov_base = NULL;
+ hdtrl[1].iov_base = 0;
+
+ total = count;
+ while (total + hdtrl[0].iov_len) {
+ ssize_t nwritten;
+
+ /*
+ * HPUX guarantees that if any data was written before
+ * a signal interrupt then sendfile returns the number of
+ * bytes written (which may be less than requested) not -1.
+ * nwritten includes the header data sent.
+ */
+
+ do {
+#if defined(HAVE_EXPLICIT_LARGEFILE_SUPPORT) && defined(HAVE_OFF64_T) && defined(HAVE_SENDFILE64)
+ nwritten = sendfile64(tofd, fromfd, offset, total, &hdtrl[0], 0);
+#else
+ nwritten = sendfile(tofd, fromfd, offset, total, &hdtrl[0], 0);
+#endif
+ } while (nwritten == -1 && errno == EINTR);
+ if (nwritten == -1)
+ return -1;
+ if (nwritten == 0)
+ return -1; /* I think we're at EOF here... */
+
+ /*
+ * If this was a short (signal interrupted) write we may need
+ * to subtract it from the header data, or null out the header
+ * data altogether if we wrote more than hdtrl[0].iov_len bytes.
+ * We change nwritten to be the number of file bytes written.
+ */
+
+ if (hdtrl[0].iov_base && hdtrl[0].iov_len) {
+ if (nwritten >= hdtrl[0].iov_len) {
+ nwritten -= hdtrl[0].iov_len;
+ hdtrl[0].iov_base = NULL;
+ hdtrl[0].iov_len = 0;
+ } else {
+ /* iov_base is defined as a void *... */
+ hdtrl[0].iov_base = ((char *)hdtrl[0].iov_base) + nwritten;
+ hdtrl[0].iov_len -= nwritten;
+ nwritten = 0;
+ }
+ }
+ total -= nwritten;
+ offset += nwritten;
+ }
+ return count + hdr_len;
+}
+
+#elif defined(FREEBSD_SENDFILE_API)
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/uio.h>
+
+ssize_t sys_sendfile(int tofd, int fromfd, const DATA_BLOB *header, SMB_OFF_T offset, size_t count)
+{
+ size_t total=0;
+ struct sf_hdtr hdr;
+ struct iovec hdtrl;
+ size_t hdr_len = 0;
+
+ hdr.headers = &hdtrl;
+ hdr.hdr_cnt = 1;
+ hdr.trailers = NULL;
+ hdr.trl_cnt = 0;
+
+ /* Set up the header iovec. */
+ if (header) {
+ hdtrl.iov_base = header->data;
+ hdtrl.iov_len = hdr_len = header->length;
+ } else {
+ hdtrl.iov_base = NULL;
+ hdtrl.iov_len = 0;
+ }
+
+ total = count;
+ while (total + hdtrl.iov_len) {
+ SMB_OFF_T nwritten;
+ int ret;
+
+ /*
+ * FreeBSD sendfile returns 0 on success, -1 on error.
+ * Remember, the tofd and fromfd are reversed..... :-).
+ * nwritten includes the header data sent.
+ */
+
+ do {
+ ret = sendfile(fromfd, tofd, offset, total, &hdr, &nwritten, 0);
+ } while (ret == -1 && errno == EINTR);
+ if (ret == -1)
+ return -1;
+
+ if (nwritten == 0)
+ return -1; /* I think we're at EOF here... */
+
+ /*
+ * If this was a short (signal interrupted) write we may need
+ * to subtract it from the header data, or null out the header
+ * data altogether if we wrote more than hdtrl.iov_len bytes.
+ * We change nwritten to be the number of file bytes written.
+ */
+
+ if (hdtrl.iov_base && hdtrl.iov_len) {
+ if (nwritten >= hdtrl.iov_len) {
+ nwritten -= hdtrl.iov_len;
+ hdtrl.iov_base = NULL;
+ hdtrl.iov_len = 0;
+ } else {
+ hdtrl.iov_base += nwritten;
+ hdtrl.iov_len -= nwritten;
+ nwritten = 0;
+ }
+ }
+ total -= nwritten;
+ offset += nwritten;
+ }
+ return count + hdr_len;
+}
+
+#else /* No sendfile implementation. Return error. */
+
+ssize_t sys_sendfile(int tofd, int fromfd, const DATA_BLOB *header, SMB_OFF_T offset, size_t count)
+{
+ /* No sendfile syscall. */
+ errno = ENOSYS;
+ return -1;
+}
+#endif
diff --git a/source/lib/server_mutex.c b/source/lib/server_mutex.c
new file mode 100644
index 00000000000..3e5512c7342
--- /dev/null
+++ b/source/lib/server_mutex.c
@@ -0,0 +1,56 @@
+/*
+ Unix SMB/CIFS implementation.
+ Authenticate against a remote domain
+ Copyright (C) Andrew Tridgell 1992-2002
+ 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 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"
+
+/* For reasons known only to MS, many of their NT/Win2k versions
+ need serialised access only. Two connections at the same time
+ may (in certain situations) cause connections to be reset,
+ or access to be denied.
+
+ This locking allows smbd's mutlithread architecture to look
+ like the single-connection that NT makes. */
+
+static char *mutex_server_name;
+
+BOOL grab_server_mutex(const char *name)
+{
+ mutex_server_name = strdup(name);
+ if (!mutex_server_name) {
+ DEBUG(0,("grab_server_mutex: malloc failed for %s\n", name));
+ return False;
+ }
+ if (!secrets_named_mutex(mutex_server_name, 10)) {
+ DEBUG(10,("grab_server_mutex: failed for %s\n", name));
+ SAFE_FREE(mutex_server_name);
+ return False;
+ }
+
+ return True;
+}
+
+void release_server_mutex(void)
+{
+ if (mutex_server_name) {
+ secrets_named_mutex_release(mutex_server_name);
+ SAFE_FREE(mutex_server_name);
+ }
+}
diff --git a/source/lib/signal.c b/source/lib/signal.c
new file mode 100644
index 00000000000..bff4b91c1a0
--- /dev/null
+++ b/source/lib/signal.c
@@ -0,0 +1,139 @@
+/*
+ Unix SMB/CIFS implementation.
+ signal handling functions
+
+ 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 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"
+
+/****************************************************************************
+ Catch child exits and reap the child zombie status.
+****************************************************************************/
+
+static void sig_cld(int signum)
+{
+ while (sys_waitpid((pid_t)-1,(int *)NULL, WNOHANG) > 0)
+ ;
+
+ /*
+ * Turns out it's *really* important not to
+ * restore the signal handler here if we have real POSIX
+ * signal handling. If we do, then we get the signal re-delivered
+ * immediately - hey presto - instant loop ! JRA.
+ */
+
+#if !defined(HAVE_SIGACTION)
+ CatchSignal(SIGCLD, sig_cld);
+#endif
+}
+
+/****************************************************************************
+catch child exits - leave status;
+****************************************************************************/
+
+static void sig_cld_leave_status(int signum)
+{
+ /*
+ * Turns out it's *really* important not to
+ * restore the signal handler here if we have real POSIX
+ * signal handling. If we do, then we get the signal re-delivered
+ * immediately - hey presto - instant loop ! JRA.
+ */
+
+#if !defined(HAVE_SIGACTION)
+ CatchSignal(SIGCLD, sig_cld_leave_status);
+#endif
+}
+
+/*******************************************************************
+ Block sigs.
+********************************************************************/
+
+void BlockSignals(BOOL block,int signum)
+{
+#ifdef HAVE_SIGPROCMASK
+ sigset_t set;
+ sigemptyset(&set);
+ sigaddset(&set,signum);
+ sigprocmask(block?SIG_BLOCK:SIG_UNBLOCK,&set,NULL);
+#elif defined(HAVE_SIGBLOCK)
+ if (block) {
+ sigblock(sigmask(signum));
+ } else {
+ sigsetmask(siggetmask() & ~sigmask(signum));
+ }
+#else
+ /* yikes! This platform can't block signals? */
+ static int done;
+ if (!done) {
+ DEBUG(0,("WARNING: No signal blocking available\n"));
+ done=1;
+ }
+#endif
+}
+
+/*******************************************************************
+ Catch a signal. This should implement the following semantics:
+
+ 1) The handler remains installed after being called.
+ 2) The signal should be blocked during handler execution.
+********************************************************************/
+
+void (*CatchSignal(int signum,void (*handler)(int )))(int)
+{
+#ifdef HAVE_SIGACTION
+ struct sigaction act;
+ struct sigaction oldact;
+
+ ZERO_STRUCT(act);
+
+ act.sa_handler = handler;
+#ifdef SA_RESTART
+ /*
+ * We *want* SIGALRM to interrupt a system call.
+ */
+ if(signum != SIGALRM)
+ act.sa_flags = SA_RESTART;
+#endif
+ sigemptyset(&act.sa_mask);
+ sigaddset(&act.sa_mask,signum);
+ sigaction(signum,&act,&oldact);
+ return oldact.sa_handler;
+#else /* !HAVE_SIGACTION */
+ /* FIXME: need to handle sigvec and systems with broken signal() */
+ return signal(signum, handler);
+#endif
+}
+
+/*******************************************************************
+ Ignore SIGCLD via whatever means is necessary for this OS.
+********************************************************************/
+
+void CatchChild(void)
+{
+ CatchSignal(SIGCLD, sig_cld);
+}
+
+/*******************************************************************
+ Catch SIGCLD but leave the child around so it's status can be reaped.
+********************************************************************/
+
+void CatchChildLeaveStatus(void)
+{
+ CatchSignal(SIGCLD, sig_cld_leave_status);
+}
diff --git a/source/lib/smbldap.c b/source/lib/smbldap.c
new file mode 100644
index 00000000000..14a46fc5fb0
--- /dev/null
+++ b/source/lib/smbldap.c
@@ -0,0 +1,1234 @@
+/*
+ Unix SMB/CIFS mplementation.
+ LDAP protocol helper functions for SAMBA
+ Copyright (C) Jean François Micouleau 1998
+ Copyright (C) Gerald Carter 2001-2003
+ Copyright (C) Shahms King 2001
+ Copyright (C) Andrew Bartlett 2002-2003
+ Copyright (C) Stefan (metze) Metzmacher 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 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 "smbldap.h"
+
+#ifndef LDAP_OPT_SUCCESS
+#define LDAP_OPT_SUCCESS 0
+#endif
+
+/* Try not to hit the up or down server forever */
+
+#define SMBLDAP_DONT_PING_TIME 10 /* ping only all 10 seconds */
+#define SMBLDAP_NUM_RETRIES 8 /* retry only 8 times */
+
+#define SMBLDAP_IDLE_TIME 150 /* After 2.5 minutes disconnect */
+
+
+/* attributes used by Samba 2.2 */
+
+ATTRIB_MAP_ENTRY attrib_map_v22[] = {
+ { LDAP_ATTR_UID, "uid" },
+ { LDAP_ATTR_UIDNUMBER, LDAP_ATTRIBUTE_UIDNUMBER},
+ { LDAP_ATTR_GIDNUMBER, LDAP_ATTRIBUTE_GIDNUMBER},
+ { LDAP_ATTR_UNIX_HOME, "homeDirectory" },
+ { LDAP_ATTR_PWD_LAST_SET, "pwdLastSet" },
+ { LDAP_ATTR_PWD_CAN_CHANGE, "pwdCanChange" },
+ { LDAP_ATTR_PWD_MUST_CHANGE, "pwdMustChange" },
+ { LDAP_ATTR_LOGON_TIME, "logonTime" },
+ { LDAP_ATTR_LOGOFF_TIME, "logoffTime" },
+ { LDAP_ATTR_KICKOFF_TIME, "kickoffTime" },
+ { LDAP_ATTR_CN, "cn" },
+ { LDAP_ATTR_DISPLAY_NAME, "displayName" },
+ { LDAP_ATTR_HOME_PATH, "smbHome" },
+ { LDAP_ATTR_HOME_DRIVE, "homeDrives" },
+ { LDAP_ATTR_LOGON_SCRIPT, "scriptPath" },
+ { LDAP_ATTR_PROFILE_PATH, "profilePath" },
+ { LDAP_ATTR_DESC, "description" },
+ { LDAP_ATTR_USER_WKS, "userWorkstations"},
+ { LDAP_ATTR_USER_RID, "rid" },
+ { LDAP_ATTR_PRIMARY_GROUP_RID, "primaryGroupID"},
+ { LDAP_ATTR_LMPW, "lmPassword" },
+ { LDAP_ATTR_NTPW, "ntPassword" },
+ { LDAP_ATTR_DOMAIN, "domain" },
+ { LDAP_ATTR_OBJCLASS, "objectClass" },
+ { LDAP_ATTR_ACB_INFO, "acctFlags" },
+ { LDAP_ATTR_LIST_END, NULL }
+};
+
+/* attributes used by Samba 3.0's sambaSamAccount */
+
+ATTRIB_MAP_ENTRY attrib_map_v30[] = {
+ { LDAP_ATTR_UID, "uid" },
+ { LDAP_ATTR_UIDNUMBER, LDAP_ATTRIBUTE_UIDNUMBER},
+ { LDAP_ATTR_GIDNUMBER, LDAP_ATTRIBUTE_GIDNUMBER},
+ { LDAP_ATTR_UNIX_HOME, "homeDirectory" },
+ { LDAP_ATTR_PWD_LAST_SET, "sambaPwdLastSet" },
+ { LDAP_ATTR_PWD_CAN_CHANGE, "sambaPwdCanChange" },
+ { LDAP_ATTR_PWD_MUST_CHANGE, "sambaPwdMustChange" },
+ { LDAP_ATTR_LOGON_TIME, "sambaLogonTime" },
+ { LDAP_ATTR_LOGOFF_TIME, "sambaLogoffTime" },
+ { LDAP_ATTR_KICKOFF_TIME, "sambaKickoffTime" },
+ { LDAP_ATTR_CN, "cn" },
+ { LDAP_ATTR_DISPLAY_NAME, "displayName" },
+ { LDAP_ATTR_HOME_DRIVE, "sambaHomeDrive" },
+ { LDAP_ATTR_HOME_PATH, "sambaHomePath" },
+ { LDAP_ATTR_LOGON_SCRIPT, "sambaLogonScript" },
+ { LDAP_ATTR_PROFILE_PATH, "sambaProfilePath" },
+ { LDAP_ATTR_DESC, "description" },
+ { LDAP_ATTR_USER_WKS, "sambaUserWorkstations" },
+ { LDAP_ATTR_USER_SID, LDAP_ATTRIBUTE_SID },
+ { LDAP_ATTR_PRIMARY_GROUP_SID, "sambaPrimaryGroupSID" },
+ { LDAP_ATTR_LMPW, "sambaLMPassword" },
+ { LDAP_ATTR_NTPW, "sambaNTPassword" },
+ { LDAP_ATTR_DOMAIN, "sambaDomainName" },
+ { LDAP_ATTR_OBJCLASS, "objectClass" },
+ { LDAP_ATTR_ACB_INFO, "sambaAcctFlags" },
+ { LDAP_ATTR_MUNGED_DIAL, "sambaMungedDial" },
+ { LDAP_ATTR_BAD_PASSWORD_COUNT, "sambaBadPasswordCount" },
+ { LDAP_ATTR_BAD_PASSWORD_TIME, "sambaBadPasswordTime" },
+ { LDAP_ATTR_MOD_TIMESTAMP, "modifyTimestamp" },
+ { LDAP_ATTR_LIST_END, NULL }
+};
+
+/* attributes used for allocating RIDs */
+
+ATTRIB_MAP_ENTRY dominfo_attr_list[] = {
+ { LDAP_ATTR_DOMAIN, "sambaDomainName" },
+ { LDAP_ATTR_NEXT_RID, "sambaNextRid" },
+ { LDAP_ATTR_NEXT_USERRID, "sambaNextUserRid" },
+ { LDAP_ATTR_NEXT_GROUPRID, "sambaNextGroupRid" },
+ { LDAP_ATTR_DOM_SID, LDAP_ATTRIBUTE_SID },
+ { LDAP_ATTR_ALGORITHMIC_RID_BASE,"sambaAlgorithmicRidBase"},
+ { LDAP_ATTR_OBJCLASS, "objectClass" },
+ { LDAP_ATTR_LIST_END, NULL },
+};
+
+/* Samba 3.0 group mapping attributes */
+
+ATTRIB_MAP_ENTRY groupmap_attr_list[] = {
+ { LDAP_ATTR_GIDNUMBER, LDAP_ATTRIBUTE_GIDNUMBER},
+ { LDAP_ATTR_GROUP_SID, LDAP_ATTRIBUTE_SID },
+ { LDAP_ATTR_GROUP_TYPE, "sambaGroupType" },
+ { LDAP_ATTR_SID_LIST, "sambaSIDList" },
+ { LDAP_ATTR_DESC, "description" },
+ { LDAP_ATTR_DISPLAY_NAME, "displayName" },
+ { LDAP_ATTR_CN, "cn" },
+ { LDAP_ATTR_OBJCLASS, "objectClass" },
+ { LDAP_ATTR_LIST_END, NULL }
+};
+
+ATTRIB_MAP_ENTRY groupmap_attr_list_to_delete[] = {
+ { LDAP_ATTR_GROUP_SID, LDAP_ATTRIBUTE_SID },
+ { LDAP_ATTR_GROUP_TYPE, "sambaGroupType" },
+ { LDAP_ATTR_DESC, "description" },
+ { LDAP_ATTR_DISPLAY_NAME, "displayName" },
+ { LDAP_ATTR_SID_LIST, "sambaSIDList" },
+ { LDAP_ATTR_LIST_END, NULL }
+};
+
+/* idmap_ldap sambaUnixIdPool */
+
+ATTRIB_MAP_ENTRY idpool_attr_list[] = {
+ { LDAP_ATTR_UIDNUMBER, LDAP_ATTRIBUTE_UIDNUMBER},
+ { LDAP_ATTR_GIDNUMBER, LDAP_ATTRIBUTE_GIDNUMBER},
+ { LDAP_ATTR_OBJCLASS, "objectClass" },
+ { LDAP_ATTR_LIST_END, NULL }
+};
+
+ATTRIB_MAP_ENTRY sidmap_attr_list[] = {
+ { LDAP_ATTR_SID, LDAP_ATTRIBUTE_SID },
+ { LDAP_ATTR_UIDNUMBER, LDAP_ATTRIBUTE_UIDNUMBER},
+ { LDAP_ATTR_GIDNUMBER, LDAP_ATTRIBUTE_GIDNUMBER},
+ { LDAP_ATTR_OBJCLASS, "objectClass" },
+ { LDAP_ATTR_LIST_END, NULL }
+};
+
+/* privileges */
+
+ATTRIB_MAP_ENTRY privilege_attr_list[] = {
+ { LDAP_ATTR_CN, "sambaPrivName" },
+ { LDAP_ATTR_SID_LIST, LDAP_ATTRIBUTE_SID_LIST },
+ { LDAP_ATTR_DESC, "description" },
+ { LDAP_ATTR_OBJCLASS, "objectClass" },
+ { LDAP_ATTR_LIST_END, NULL }
+};
+
+/**********************************************************************
+ perform a simple table lookup and return the attribute name
+ **********************************************************************/
+
+ const char* get_attr_key2string( ATTRIB_MAP_ENTRY table[], int key )
+{
+ int i = 0;
+
+ while ( table[i].attrib != LDAP_ATTR_LIST_END ) {
+ if ( table[i].attrib == key )
+ return table[i].name;
+ i++;
+ }
+
+ return NULL;
+}
+
+
+/**********************************************************************
+ Return the list of attribute names from a mapping table
+ **********************************************************************/
+
+ char** get_attr_list( ATTRIB_MAP_ENTRY table[] )
+{
+ char **names;
+ int i = 0;
+
+ while ( table[i].attrib != LDAP_ATTR_LIST_END )
+ i++;
+ i++;
+
+ names = (char**)malloc( sizeof(char*)*i );
+ if ( !names ) {
+ DEBUG(0,("get_attr_list: out of memory\n"));
+ return NULL;
+ }
+
+ i = 0;
+ while ( table[i].attrib != LDAP_ATTR_LIST_END ) {
+ names[i] = strdup( table[i].name );
+ i++;
+ }
+ names[i] = NULL;
+
+ return names;
+}
+
+/*********************************************************************
+ Cleanup
+ ********************************************************************/
+
+ void free_attr_list( char **list )
+{
+ int i = 0;
+
+ if ( !list )
+ return;
+
+ while ( list[i] ) {
+ SAFE_FREE( list[i] );
+ i+=1;
+ }
+
+ SAFE_FREE( list );
+}
+
+/*******************************************************************
+ find the ldap password
+******************************************************************/
+static BOOL fetch_ldap_pw(char **dn, char** pw)
+{
+ char *key = NULL;
+ size_t size;
+
+ *dn = smb_xstrdup(lp_ldap_admin_dn());
+
+ if (asprintf(&key, "%s/%s", SECRETS_LDAP_BIND_PW, *dn) < 0) {
+ SAFE_FREE(*dn);
+ DEBUG(0, ("fetch_ldap_pw: asprintf failed!\n"));
+ }
+
+ *pw=secrets_fetch(key, &size);
+ SAFE_FREE(key);
+
+ if (!size) {
+ /* Upgrade 2.2 style entry */
+ char *p;
+ char* old_style_key = strdup(*dn);
+ char *data;
+ fstring old_style_pw;
+
+ if (!old_style_key) {
+ DEBUG(0, ("fetch_ldap_pw: strdup failed!\n"));
+ return False;
+ }
+
+ for (p=old_style_key; *p; p++)
+ if (*p == ',') *p = '/';
+
+ data=secrets_fetch(old_style_key, &size);
+ if (!size && size < sizeof(old_style_pw)) {
+ DEBUG(0,("fetch_ldap_pw: neither ldap secret retrieved!\n"));
+ SAFE_FREE(old_style_key);
+ SAFE_FREE(*dn);
+ return False;
+ }
+
+ size = MIN(size, sizeof(fstring)-1);
+ strncpy(old_style_pw, data, size);
+ old_style_pw[size] = 0;
+
+ SAFE_FREE(data);
+
+ if (!secrets_store_ldap_pw(*dn, old_style_pw)) {
+ DEBUG(0,("fetch_ldap_pw: ldap secret could not be upgraded!\n"));
+ SAFE_FREE(old_style_key);
+ SAFE_FREE(*dn);
+ return False;
+ }
+ if (!secrets_delete(old_style_key)) {
+ DEBUG(0,("fetch_ldap_pw: old ldap secret could not be deleted!\n"));
+ }
+
+ SAFE_FREE(old_style_key);
+
+ *pw = smb_xstrdup(old_style_pw);
+ }
+
+ return True;
+}
+
+/*******************************************************************
+ Search an attribute and return the first value found.
+******************************************************************/
+
+ BOOL smbldap_get_single_attribute (LDAP * ldap_struct, LDAPMessage * entry,
+ const char *attribute, char *value,
+ int max_len)
+{
+ char **values;
+
+ if ( !attribute )
+ return False;
+
+ value[0] = '\0';
+
+ if ((values = ldap_get_values (ldap_struct, entry, attribute)) == NULL) {
+ DEBUG (10, ("smbldap_get_single_attribute: [%s] = [<does not exist>]\n", attribute));
+
+ return False;
+ }
+
+ if (convert_string(CH_UTF8, CH_UNIX,values[0], -1, value, max_len, False) == (size_t)-1) {
+ DEBUG(1, ("smbldap_get_single_attribute: string conversion of [%s] = [%s] failed!\n",
+ attribute, values[0]));
+ ldap_value_free(values);
+ return False;
+ }
+
+ ldap_value_free(values);
+#ifdef DEBUG_PASSWORDS
+ DEBUG (100, ("smbldap_get_single_attribute: [%s] = [%s]\n", attribute, value));
+#endif
+ return True;
+}
+
+ BOOL smbldap_get_single_pstring (LDAP * ldap_struct, LDAPMessage * entry,
+ const char *attribute, pstring value)
+{
+ return smbldap_get_single_attribute(ldap_struct, entry,
+ attribute, value,
+ sizeof(pstring));
+}
+
+/************************************************************************
+ Routine to manage the LDAPMod structure array
+ manage memory used by the array, by each struct, and values
+ ***********************************************************************/
+
+ void smbldap_set_mod (LDAPMod *** modlist, int modop, const char *attribute, const char *value)
+{
+ LDAPMod **mods;
+ int i;
+ int j;
+
+ mods = *modlist;
+
+ /* sanity checks on the mod values */
+
+ if (attribute == NULL || *attribute == '\0')
+ return;
+#if 0 /* commented out after discussion with abartlet. Do not reenable.
+ left here so other so re-add similar code --jerry */
+ if (value == NULL || *value == '\0')
+ return;
+#endif
+
+ if (mods == NULL)
+ {
+ mods = (LDAPMod **) malloc(sizeof(LDAPMod *));
+ if (mods == NULL)
+ {
+ DEBUG(0, ("make_a_mod: out of memory!\n"));
+ return;
+ }
+ mods[0] = NULL;
+ }
+
+ for (i = 0; mods[i] != NULL; ++i) {
+ if (mods[i]->mod_op == modop && strequal(mods[i]->mod_type, attribute))
+ break;
+ }
+
+ if (mods[i] == NULL)
+ {
+ mods = (LDAPMod **) Realloc (mods, (i + 2) * sizeof (LDAPMod *));
+ if (mods == NULL)
+ {
+ DEBUG(0, ("make_a_mod: out of memory!\n"));
+ return;
+ }
+ mods[i] = (LDAPMod *) malloc(sizeof(LDAPMod));
+ if (mods[i] == NULL)
+ {
+ DEBUG(0, ("make_a_mod: out of memory!\n"));
+ return;
+ }
+ mods[i]->mod_op = modop;
+ mods[i]->mod_values = NULL;
+ mods[i]->mod_type = strdup(attribute);
+ mods[i + 1] = NULL;
+ }
+
+ if (value != NULL)
+ {
+ char *utf8_value = NULL;
+
+ j = 0;
+ if (mods[i]->mod_values != NULL) {
+ for (; mods[i]->mod_values[j] != NULL; j++);
+ }
+ mods[i]->mod_values = (char **)Realloc(mods[i]->mod_values,
+ (j + 2) * sizeof (char *));
+
+ if (mods[i]->mod_values == NULL) {
+ DEBUG (0, ("make_a_mod: Memory allocation failure!\n"));
+ return;
+ }
+
+ if (push_utf8_allocate(&utf8_value, value) == (size_t)-1) {
+ DEBUG (0, ("make_a_mod: String conversion failure!\n"));
+ return;
+ }
+
+ mods[i]->mod_values[j] = utf8_value;
+
+ mods[i]->mod_values[j + 1] = NULL;
+ }
+ *modlist = mods;
+}
+
+/**********************************************************************
+ Set attribute to newval in LDAP, regardless of what value the
+ attribute had in LDAP before.
+*********************************************************************/
+
+ void smbldap_make_mod(LDAP *ldap_struct, LDAPMessage *existing,
+ LDAPMod ***mods,
+ const char *attribute, const char *newval)
+{
+ char oldval[2048]; /* current largest allowed value is mungeddial */
+ BOOL existed;
+
+ if (existing != NULL) {
+ existed = smbldap_get_single_attribute(ldap_struct, existing, attribute, oldval, sizeof(oldval));
+ } else {
+ existed = False;
+ *oldval = '\0';
+ }
+
+ /* all of our string attributes are case insensitive */
+
+ if (existed && newval && (StrCaseCmp(oldval, newval) == 0)) {
+
+ /* Believe it or not, but LDAP will deny a delete and
+ an add at the same time if the values are the
+ same... */
+ return;
+ }
+
+ if (existed) {
+ /* There has been no value before, so don't delete it.
+ * Here's a possible race: We might end up with
+ * duplicate attributes */
+ /* By deleting exactly the value we found in the entry this
+ * should be race-free in the sense that the LDAP-Server will
+ * deny the complete operation if somebody changed the
+ * attribute behind our back. */
+ /* This will also allow modifying single valued attributes
+ * in Novell NDS. In NDS you have to first remove attribute and then
+ * you could add new value */
+
+ smbldap_set_mod(mods, LDAP_MOD_DELETE, attribute, oldval);
+ }
+
+ /* Regardless of the real operation (add or modify)
+ we add the new value here. We rely on deleting
+ the old value, should it exist. */
+
+ if ((newval != NULL) && (strlen(newval) > 0)) {
+ smbldap_set_mod(mods, LDAP_MOD_ADD, attribute, newval);
+ }
+}
+
+/**********************************************************************
+ Some varients of the LDAP rebind code do not pass in the third 'arg'
+ pointer to a void*, so we try and work around it by assuming that the
+ value of the 'LDAP *' pointer is the same as the one we had passed in
+ **********************************************************************/
+
+struct smbldap_state_lookup {
+ LDAP *ld;
+ struct smbldap_state *smbldap_state;
+ struct smbldap_state_lookup *prev, *next;
+};
+
+static struct smbldap_state_lookup *smbldap_state_lookup_list;
+
+static struct smbldap_state *smbldap_find_state(LDAP *ld)
+{
+ struct smbldap_state_lookup *t;
+
+ for (t = smbldap_state_lookup_list; t; t = t->next) {
+ if (t->ld == ld) {
+ return t->smbldap_state;
+ }
+ }
+ return NULL;
+}
+
+static void smbldap_delete_state(struct smbldap_state *smbldap_state)
+{
+ struct smbldap_state_lookup *t;
+
+ for (t = smbldap_state_lookup_list; t; t = t->next) {
+ if (t->smbldap_state == smbldap_state) {
+ DLIST_REMOVE(smbldap_state_lookup_list, t);
+ SAFE_FREE(t);
+ return;
+ }
+ }
+}
+
+static void smbldap_store_state(LDAP *ld, struct smbldap_state *smbldap_state)
+{
+ struct smbldap_state *tmp_ldap_state;
+ struct smbldap_state_lookup *t;
+ struct smbldap_state_lookup *tmp;
+
+ if ((tmp_ldap_state = smbldap_find_state(ld))) {
+ SMB_ASSERT(tmp_ldap_state == smbldap_state);
+ return;
+ }
+
+ t = smb_xmalloc(sizeof(*t));
+ ZERO_STRUCTP(t);
+
+ DLIST_ADD_END(smbldap_state_lookup_list, t, tmp);
+ t->ld = ld;
+ t->smbldap_state = smbldap_state;
+}
+
+/*******************************************************************
+ open a connection to the ldap server.
+******************************************************************/
+static int smbldap_open_connection (struct smbldap_state *ldap_state)
+
+{
+ int rc = LDAP_SUCCESS;
+ int version;
+ BOOL ldap_v3 = False;
+ LDAP **ldap_struct = &ldap_state->ldap_struct;
+
+#ifdef HAVE_LDAP_INITIALIZE
+ DEBUG(10, ("smbldap_open_connection: %s\n", ldap_state->uri));
+
+ if ((rc = ldap_initialize(ldap_struct, ldap_state->uri)) != LDAP_SUCCESS) {
+ DEBUG(0, ("ldap_initialize: %s\n", ldap_err2string(rc)));
+ return rc;
+ }
+#else
+
+ /* Parse the string manually */
+
+ {
+ int port = 0;
+ fstring protocol;
+ fstring host;
+ const char *p = ldap_state->uri;
+ SMB_ASSERT(sizeof(protocol)>10 && sizeof(host)>254);
+
+ /* skip leading "URL:" (if any) */
+ if ( strnequal( p, "URL:", 4 ) ) {
+ p += 4;
+ }
+
+ sscanf(p, "%10[^:]://%254[^:/]:%d", protocol, host, &port);
+
+ if (port == 0) {
+ if (strequal(protocol, "ldap")) {
+ port = LDAP_PORT;
+ } else if (strequal(protocol, "ldaps")) {
+ port = LDAPS_PORT;
+ } else {
+ DEBUG(0, ("unrecognised protocol (%s)!\n", protocol));
+ }
+ }
+
+ if ((*ldap_struct = ldap_init(host, port)) == NULL) {
+ DEBUG(0, ("ldap_init failed !\n"));
+ return LDAP_OPERATIONS_ERROR;
+ }
+
+ if (strequal(protocol, "ldaps")) {
+#ifdef LDAP_OPT_X_TLS
+ int tls = LDAP_OPT_X_TLS_HARD;
+ if (ldap_set_option (*ldap_struct, LDAP_OPT_X_TLS, &tls) != LDAP_SUCCESS)
+ {
+ DEBUG(0, ("Failed to setup a TLS session\n"));
+ }
+
+ DEBUG(3,("LDAPS option set...!\n"));
+#else
+ DEBUG(0,("smbldap_open_connection: Secure connection not supported by LDAP client libraries!\n"));
+ return LDAP_OPERATIONS_ERROR;
+#endif
+ }
+ }
+#endif
+
+ /* Store the LDAP pointer in a lookup list */
+
+ smbldap_store_state(*ldap_struct, ldap_state);
+
+ /* Upgrade to LDAPv3 if possible */
+
+ if (ldap_get_option(*ldap_struct, LDAP_OPT_PROTOCOL_VERSION, &version) == LDAP_OPT_SUCCESS)
+ {
+ if (version != LDAP_VERSION3)
+ {
+ version = LDAP_VERSION3;
+ if (ldap_set_option (*ldap_struct, LDAP_OPT_PROTOCOL_VERSION, &version) == LDAP_OPT_SUCCESS) {
+ ldap_v3 = True;
+ }
+ } else {
+ ldap_v3 = True;
+ }
+ }
+
+ if (lp_ldap_ssl() == LDAP_SSL_START_TLS) {
+#ifdef LDAP_OPT_X_TLS
+ if (ldap_v3) {
+ if ((rc = ldap_start_tls_s (*ldap_struct, NULL, NULL)) != LDAP_SUCCESS)
+ {
+ DEBUG(0,("Failed to issue the StartTLS instruction: %s\n",
+ ldap_err2string(rc)));
+ return rc;
+ }
+ DEBUG (3, ("StartTLS issued: using a TLS connection\n"));
+ } else {
+
+ DEBUG(0, ("Need LDAPv3 for Start TLS\n"));
+ return LDAP_OPERATIONS_ERROR;
+ }
+#else
+ DEBUG(0,("smbldap_open_connection: StartTLS not supported by LDAP client libraries!\n"));
+ return LDAP_OPERATIONS_ERROR;
+#endif
+ }
+
+ DEBUG(2, ("smbldap_open_connection: connection opened\n"));
+ return rc;
+}
+
+
+/*******************************************************************
+ a rebind function for authenticated referrals
+ This version takes a void* that we can shove useful stuff in :-)
+******************************************************************/
+#if defined(LDAP_API_FEATURE_X_OPENLDAP) && (LDAP_API_VERSION > 2000)
+#else
+static int rebindproc_with_state (LDAP * ld, char **whop, char **credp,
+ int *methodp, int freeit, void *arg)
+{
+ struct smbldap_state *ldap_state = arg;
+
+ /** @TODO Should we be doing something to check what servers we rebind to?
+ Could we get a referral to a machine that we don't want to give our
+ username and password to? */
+
+ if (freeit) {
+ SAFE_FREE(*whop);
+ memset(*credp, '\0', strlen(*credp));
+ SAFE_FREE(*credp);
+ } else {
+ DEBUG(5,("rebind_proc_with_state: Rebinding as \"%s\"\n",
+ ldap_state->bind_dn));
+
+ *whop = strdup(ldap_state->bind_dn);
+ if (!*whop) {
+ return LDAP_NO_MEMORY;
+ }
+ *credp = strdup(ldap_state->bind_secret);
+ if (!*credp) {
+ SAFE_FREE(*whop);
+ return LDAP_NO_MEMORY;
+ }
+ *methodp = LDAP_AUTH_SIMPLE;
+ }
+
+ gettimeofday(&(ldap_state->last_rebind),NULL);
+
+ return 0;
+}
+#endif /*defined(LDAP_API_FEATURE_X_OPENLDAP) && (LDAP_API_VERSION > 2000)*/
+
+/*******************************************************************
+ a rebind function for authenticated referrals
+ This version takes a void* that we can shove useful stuff in :-)
+ and actually does the connection.
+******************************************************************/
+#if defined(LDAP_API_FEATURE_X_OPENLDAP) && (LDAP_API_VERSION > 2000)
+static int rebindproc_connect_with_state (LDAP *ldap_struct,
+ LDAP_CONST char *url,
+ ber_tag_t request,
+ ber_int_t msgid, void *arg)
+{
+ struct smbldap_state *ldap_state = arg;
+ int rc;
+ DEBUG(5,("rebindproc_connect_with_state: Rebinding as \"%s\"\n",
+ ldap_state->bind_dn));
+
+ /** @TODO Should we be doing something to check what servers we rebind to?
+ Could we get a referral to a machine that we don't want to give our
+ username and password to? */
+
+ rc = ldap_simple_bind_s(ldap_struct, ldap_state->bind_dn, ldap_state->bind_secret);
+
+ gettimeofday(&(ldap_state->last_rebind),NULL);
+
+ return rc;
+}
+#endif /*defined(LDAP_API_FEATURE_X_OPENLDAP) && (LDAP_API_VERSION > 2000)*/
+
+/*******************************************************************
+ Add a rebind function for authenticated referrals
+******************************************************************/
+#if defined(LDAP_API_FEATURE_X_OPENLDAP) && (LDAP_API_VERSION > 2000)
+#else
+# if LDAP_SET_REBIND_PROC_ARGS == 2
+static int rebindproc (LDAP *ldap_struct, char **whop, char **credp,
+ int *method, int freeit )
+{
+ struct smbldap_state *ldap_state = smbldap_find_state(ldap_struct);
+
+ return rebindproc_with_state(ldap_struct, whop, credp,
+ method, freeit, ldap_state);
+
+}
+# endif /*LDAP_SET_REBIND_PROC_ARGS == 2*/
+#endif /*defined(LDAP_API_FEATURE_X_OPENLDAP) && (LDAP_API_VERSION > 2000)*/
+
+/*******************************************************************
+ a rebind function for authenticated referrals
+ this also does the connection, but no void*.
+******************************************************************/
+#if defined(LDAP_API_FEATURE_X_OPENLDAP) && (LDAP_API_VERSION > 2000)
+# if LDAP_SET_REBIND_PROC_ARGS == 2
+static int rebindproc_connect (LDAP * ld, LDAP_CONST char *url, int request,
+ ber_int_t msgid)
+{
+ struct smbldap_state *ldap_state = smbldap_find_state(ld);
+
+ return rebindproc_connect_with_state(ld, url, (ber_tag_t)request, msgid,
+ ldap_state);
+}
+# endif /*LDAP_SET_REBIND_PROC_ARGS == 2*/
+#endif /*defined(LDAP_API_FEATURE_X_OPENLDAP) && (LDAP_API_VERSION > 2000)*/
+
+/*******************************************************************
+ connect to the ldap server under system privilege.
+******************************************************************/
+static int smbldap_connect_system(struct smbldap_state *ldap_state, LDAP * ldap_struct)
+{
+ int rc;
+ char *ldap_dn;
+ char *ldap_secret;
+
+ /* get the password */
+ if (!fetch_ldap_pw(&ldap_dn, &ldap_secret))
+ {
+ DEBUG(0, ("ldap_connect_system: Failed to retrieve password from secrets.tdb\n"));
+ return LDAP_INVALID_CREDENTIALS;
+ }
+
+ ldap_state->bind_dn = ldap_dn;
+ ldap_state->bind_secret = ldap_secret;
+
+ /* removed the sasl_bind_s "EXTERNAL" stuff, as my testsuite
+ (OpenLDAP) doesnt' seem to support it */
+
+ DEBUG(10,("ldap_connect_system: Binding to ldap server %s as \"%s\"\n",
+ ldap_state->uri, ldap_dn));
+
+#if defined(LDAP_API_FEATURE_X_OPENLDAP) && (LDAP_API_VERSION > 2000)
+# if LDAP_SET_REBIND_PROC_ARGS == 2
+ ldap_set_rebind_proc(ldap_struct, &rebindproc_connect);
+# endif
+# if LDAP_SET_REBIND_PROC_ARGS == 3
+ ldap_set_rebind_proc(ldap_struct, &rebindproc_connect_with_state, (void *)ldap_state);
+# endif
+#else /*defined(LDAP_API_FEATURE_X_OPENLDAP) && (LDAP_API_VERSION > 2000)*/
+# if LDAP_SET_REBIND_PROC_ARGS == 2
+ ldap_set_rebind_proc(ldap_struct, &rebindproc);
+# endif
+# if LDAP_SET_REBIND_PROC_ARGS == 3
+ ldap_set_rebind_proc(ldap_struct, &rebindproc_with_state, (void *)ldap_state);
+# endif
+#endif /*defined(LDAP_API_FEATURE_X_OPENLDAP) && (LDAP_API_VERSION > 2000)*/
+
+ rc = ldap_simple_bind_s(ldap_struct, ldap_dn, ldap_secret);
+
+ if (rc != LDAP_SUCCESS) {
+ char *ld_error = NULL;
+ ldap_get_option(ldap_state->ldap_struct, LDAP_OPT_ERROR_STRING,
+ &ld_error);
+ DEBUG(ldap_state->num_failures ? 2 : 0,
+ ("failed to bind to server with dn= %s Error: %s\n\t%s\n",
+ ldap_dn ? ldap_dn : "(unknown)", ldap_err2string(rc),
+ ld_error ? ld_error : "(unknown)"));
+ SAFE_FREE(ld_error);
+ ldap_state->num_failures++;
+ return rc;
+ }
+
+ ldap_state->num_failures = 0;
+
+ DEBUG(3, ("ldap_connect_system: succesful connection to the LDAP server\n"));
+ return rc;
+}
+
+/**********************************************************************
+Connect to LDAP server (called before every ldap operation)
+*********************************************************************/
+static int smbldap_open(struct smbldap_state *ldap_state)
+{
+ int rc;
+ SMB_ASSERT(ldap_state);
+
+#ifndef NO_LDAP_SECURITY
+ if (geteuid() != 0) {
+ DEBUG(0, ("smbldap_open: cannot access LDAP when not root..\n"));
+ return LDAP_INSUFFICIENT_ACCESS;
+ }
+#endif
+
+ if ((ldap_state->ldap_struct != NULL) && ((ldap_state->last_ping + SMBLDAP_DONT_PING_TIME) < time(NULL))) {
+ struct sockaddr_un addr;
+ socklen_t len = sizeof(addr);
+ int sd;
+ if (ldap_get_option(ldap_state->ldap_struct, LDAP_OPT_DESC, &sd) == 0 &&
+ getpeername(sd, (struct sockaddr *) &addr, &len) < 0) {
+ /* the other end has died. reopen. */
+ ldap_unbind_ext(ldap_state->ldap_struct, NULL, NULL);
+ ldap_state->ldap_struct = NULL;
+ ldap_state->last_ping = (time_t)0;
+ } else {
+ ldap_state->last_ping = time(NULL);
+ }
+ }
+
+ if (ldap_state->ldap_struct != NULL) {
+ DEBUG(11,("smbldap_open: already connected to the LDAP server\n"));
+ return LDAP_SUCCESS;
+ }
+
+ if ((rc = smbldap_open_connection(ldap_state))) {
+ return rc;
+ }
+
+ if ((rc = smbldap_connect_system(ldap_state, ldap_state->ldap_struct))) {
+ ldap_unbind_ext(ldap_state->ldap_struct, NULL, NULL);
+ ldap_state->ldap_struct = NULL;
+ return rc;
+ }
+
+
+ ldap_state->last_ping = time(NULL);
+ DEBUG(4,("The LDAP server is succesful connected\n"));
+
+ return LDAP_SUCCESS;
+}
+
+/**********************************************************************
+Disconnect from LDAP server
+*********************************************************************/
+static NTSTATUS smbldap_close(struct smbldap_state *ldap_state)
+{
+ if (!ldap_state)
+ return NT_STATUS_INVALID_PARAMETER;
+
+ if (ldap_state->ldap_struct != NULL) {
+ ldap_unbind_ext(ldap_state->ldap_struct, NULL, NULL);
+ ldap_state->ldap_struct = NULL;
+ }
+
+ smbldap_delete_state(ldap_state);
+
+ DEBUG(5,("The connection to the LDAP server was closed\n"));
+ /* maybe free the results here --metze */
+
+
+
+ return NT_STATUS_OK;
+}
+
+int smbldap_retry_open(struct smbldap_state *ldap_state, int *attempts)
+{
+ int rc;
+
+ SMB_ASSERT(ldap_state && attempts);
+
+ if (*attempts != 0) {
+ unsigned int sleep_time;
+ uint8 rand_byte;
+
+ /* Sleep for a random timeout */
+ rand_byte = (char)(sys_random());
+
+ sleep_time = (((*attempts)*(*attempts))/2)*rand_byte*2;
+ /* we retry after (0.5, 1, 2, 3, 4.5, 6) seconds
+ on average.
+ */
+ DEBUG(3, ("Sleeping for %u milliseconds before reconnecting\n",
+ sleep_time));
+ smb_msleep(sleep_time);
+ }
+ (*attempts)++;
+
+ if ((rc = smbldap_open(ldap_state))) {
+ DEBUG(1,("Connection to LDAP Server failed for the %d try!\n",*attempts));
+ return rc;
+ }
+
+ return LDAP_SUCCESS;
+}
+
+
+/*********************************************************************
+ ********************************************************************/
+
+int smbldap_search(struct smbldap_state *ldap_state,
+ const char *base, int scope, const char *filter,
+ char *attrs[], int attrsonly,
+ LDAPMessage **res)
+{
+ int rc = LDAP_SERVER_DOWN;
+ int attempts = 0;
+ char *utf8_filter;
+
+ SMB_ASSERT(ldap_state);
+
+ DEBUG(5,("smbldap_search: base => [%s], filter => [%s], scope => [%d]\n",
+ base, filter, scope));
+
+ if (ldap_state->last_rebind.tv_sec > 0) {
+ struct timeval tval;
+ int tdiff = 0;
+ int sleep_time = 0;
+
+ ZERO_STRUCT(tval);
+
+ gettimeofday(&tval,NULL);
+
+ tdiff = 1000000 *(tval.tv_sec - ldap_state->last_rebind.tv_sec) +
+ (tval.tv_usec - ldap_state->last_rebind.tv_usec);
+
+ sleep_time = ((1000*lp_ldap_replication_sleep())-tdiff)/1000;
+
+ if (sleep_time > 0) {
+ /* we wait for the LDAP replication */
+ DEBUG(5,("smbldap_search: waiting %d milliseconds for LDAP replication.\n",sleep_time));
+ smb_msleep(sleep_time);
+ DEBUG(5,("smbldap_search: go on!\n"));
+ ZERO_STRUCT(ldap_state->last_rebind);
+ }
+ }
+
+ if (push_utf8_allocate(&utf8_filter, filter) == (size_t)-1) {
+ return LDAP_NO_MEMORY;
+ }
+
+ while ((rc == LDAP_SERVER_DOWN) && (attempts < SMBLDAP_NUM_RETRIES)) {
+
+ if ((rc = smbldap_retry_open(ldap_state,&attempts)) != LDAP_SUCCESS)
+ continue;
+
+ rc = ldap_search_s(ldap_state->ldap_struct, base, scope,
+ utf8_filter, attrs, attrsonly, res);
+ }
+
+ if (rc == LDAP_SERVER_DOWN) {
+ DEBUG(0,("%s: LDAP server is down!\n",FUNCTION_MACRO));
+ smbldap_close(ldap_state);
+ }
+
+ ldap_state->last_use = time(NULL);
+
+ SAFE_FREE(utf8_filter);
+ return rc;
+}
+
+int smbldap_modify(struct smbldap_state *ldap_state, const char *dn, LDAPMod *attrs[])
+{
+ int rc = LDAP_SERVER_DOWN;
+ int attempts = 0;
+ char *utf8_dn;
+
+ SMB_ASSERT(ldap_state);
+
+ DEBUG(5,("smbldap_modify: dn => [%s]\n", dn ));
+
+ if (push_utf8_allocate(&utf8_dn, dn) == (size_t)-1) {
+ return LDAP_NO_MEMORY;
+ }
+
+ while ((rc == LDAP_SERVER_DOWN) && (attempts < SMBLDAP_NUM_RETRIES)) {
+
+ if ((rc = smbldap_retry_open(ldap_state,&attempts)) != LDAP_SUCCESS)
+ continue;
+
+ rc = ldap_modify_s(ldap_state->ldap_struct, utf8_dn, attrs);
+ }
+
+ if (rc == LDAP_SERVER_DOWN) {
+ DEBUG(0,("%s: LDAP server is down!\n",FUNCTION_MACRO));
+ smbldap_close(ldap_state);
+ }
+
+ ldap_state->last_use = time(NULL);
+
+ SAFE_FREE(utf8_dn);
+ return rc;
+}
+
+int smbldap_add(struct smbldap_state *ldap_state, const char *dn, LDAPMod *attrs[])
+{
+ int rc = LDAP_SERVER_DOWN;
+ int attempts = 0;
+ char *utf8_dn;
+
+ SMB_ASSERT(ldap_state);
+
+ DEBUG(5,("smbldap_add: dn => [%s]\n", dn ));
+
+ if (push_utf8_allocate(&utf8_dn, dn) == (size_t)-1) {
+ return LDAP_NO_MEMORY;
+ }
+
+ while ((rc == LDAP_SERVER_DOWN) && (attempts < SMBLDAP_NUM_RETRIES)) {
+
+ if ((rc = smbldap_retry_open(ldap_state,&attempts)) != LDAP_SUCCESS)
+ continue;
+
+ rc = ldap_add_s(ldap_state->ldap_struct, utf8_dn, attrs);
+ }
+
+ if (rc == LDAP_SERVER_DOWN) {
+ DEBUG(0,("%s: LDAP server is down!\n",FUNCTION_MACRO));
+ smbldap_close(ldap_state);
+ }
+
+ ldap_state->last_use = time(NULL);
+
+ SAFE_FREE(utf8_dn);
+ return rc;
+}
+
+int smbldap_delete(struct smbldap_state *ldap_state, const char *dn)
+{
+ int rc = LDAP_SERVER_DOWN;
+ int attempts = 0;
+ char *utf8_dn;
+
+ SMB_ASSERT(ldap_state);
+
+ DEBUG(5,("smbldap_delete: dn => [%s]\n", dn ));
+
+ if (push_utf8_allocate(&utf8_dn, dn) == (size_t)-1) {
+ return LDAP_NO_MEMORY;
+ }
+
+ while ((rc == LDAP_SERVER_DOWN) && (attempts < SMBLDAP_NUM_RETRIES)) {
+
+ if ((rc = smbldap_retry_open(ldap_state,&attempts)) != LDAP_SUCCESS)
+ continue;
+
+ rc = ldap_delete_s(ldap_state->ldap_struct, utf8_dn);
+ }
+
+ if (rc == LDAP_SERVER_DOWN) {
+ DEBUG(0,("%s: LDAP server is down!\n",FUNCTION_MACRO));
+ smbldap_close(ldap_state);
+ }
+
+ ldap_state->last_use = time(NULL);
+
+ SAFE_FREE(utf8_dn);
+ return rc;
+}
+
+int smbldap_extended_operation(struct smbldap_state *ldap_state,
+ LDAP_CONST char *reqoid, struct berval *reqdata,
+ LDAPControl **serverctrls, LDAPControl **clientctrls,
+ char **retoidp, struct berval **retdatap)
+{
+ int rc = LDAP_SERVER_DOWN;
+ int attempts = 0;
+
+ if (!ldap_state)
+ return (-1);
+
+ while ((rc == LDAP_SERVER_DOWN) && (attempts < SMBLDAP_NUM_RETRIES)) {
+
+ if ((rc = smbldap_retry_open(ldap_state,&attempts)) != LDAP_SUCCESS)
+ continue;
+
+ rc = ldap_extended_operation_s(ldap_state->ldap_struct, reqoid, reqdata,
+ serverctrls, clientctrls, retoidp, retdatap);
+ }
+
+ if (rc == LDAP_SERVER_DOWN) {
+ DEBUG(0,("%s: LDAP server is down!\n",FUNCTION_MACRO));
+ smbldap_close(ldap_state);
+ }
+
+ ldap_state->last_use = time(NULL);
+
+ return rc;
+}
+
+/*******************************************************************
+ run the search by name.
+******************************************************************/
+int smbldap_search_suffix (struct smbldap_state *ldap_state, const char *filter,
+ char **search_attr, LDAPMessage ** result)
+{
+ int scope = LDAP_SCOPE_SUBTREE;
+ int rc;
+
+ rc = smbldap_search(ldap_state, lp_ldap_suffix(), scope, filter, search_attr, 0, result);
+
+ if (rc != LDAP_SUCCESS) {
+ char *ld_error = NULL;
+ ldap_get_option(ldap_state->ldap_struct, LDAP_OPT_ERROR_STRING,
+ &ld_error);
+ DEBUG(0,("smbldap_search_suffix: Problem during the LDAP search: %s (%s)\n",
+ ld_error?ld_error:"(unknown)", ldap_err2string (rc)));
+ SAFE_FREE(ld_error);
+ }
+
+ return rc;
+}
+
+static void smbldap_idle_fn(void **data, time_t *interval, time_t now)
+{
+ struct smbldap_state *state = (struct smbldap_state *)(*data);
+
+ if (state->ldap_struct == NULL) {
+ DEBUG(10,("ldap connection not connected...\n"));
+ return;
+ }
+
+ if ((state->last_use+SMBLDAP_IDLE_TIME) > now) {
+ DEBUG(10,("ldap connection not idle...\n"));
+ return;
+ }
+
+ DEBUG(7,("ldap connection idle...closing connection\n"));
+ smbldap_close(state);
+}
+
+/**********************************************************************
+ Housekeeping
+ *********************************************************************/
+
+void smbldap_free_struct(struct smbldap_state **ldap_state)
+{
+ smbldap_close(*ldap_state);
+
+ if ((*ldap_state)->bind_secret) {
+ memset((*ldap_state)->bind_secret, '\0', strlen((*ldap_state)->bind_secret));
+ }
+
+ SAFE_FREE((*ldap_state)->bind_dn);
+ SAFE_FREE((*ldap_state)->bind_secret);
+
+ smb_unregister_idle_event((*ldap_state)->event_id);
+
+ *ldap_state = NULL;
+
+ /* No need to free any further, as it is talloc()ed */
+}
+
+
+/**********************************************************************
+ Intitalise the 'general' ldap structures, on which ldap operations may be conducted
+ *********************************************************************/
+
+NTSTATUS smbldap_init(TALLOC_CTX *mem_ctx, const char *location, struct smbldap_state **smbldap_state)
+{
+ *smbldap_state = talloc_zero(mem_ctx, sizeof(**smbldap_state));
+ if (!*smbldap_state) {
+ DEBUG(0, ("talloc() failed for ldapsam private_data!\n"));
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ if (location) {
+ (*smbldap_state)->uri = talloc_strdup(mem_ctx, location);
+ } else {
+ (*smbldap_state)->uri = "ldap://localhost";
+ }
+
+ (*smbldap_state)->event_id =
+ smb_register_idle_event(smbldap_idle_fn, (void *)(*smbldap_state),
+ SMBLDAP_IDLE_TIME);
+
+ if ((*smbldap_state)->event_id == SMB_EVENT_ID_INVALID) {
+ DEBUG(0,("Failed to register LDAP idle event!\n"));
+ return NT_STATUS_INVALID_HANDLE;
+ }
+
+ return NT_STATUS_OK;
+}
+
+/*******************************************************************
+ Return a copy of the DN for a LDAPMessage. Convert from utf8 to CH_UNIX.
+********************************************************************/
+
+char *smbldap_get_dn(LDAP *ld, LDAPMessage *entry)
+{
+ char *utf8_dn, *unix_dn;
+
+ utf8_dn = ldap_get_dn(ld, entry);
+ if (!utf8_dn) {
+ DEBUG (5, ("smbldap_get_dn: ldap_get_dn failed\n"));
+ return NULL;
+ }
+ if (pull_utf8_allocate(&unix_dn, utf8_dn) == (size_t)-1) {
+ DEBUG (0, ("smbldap_get_dn: String conversion failure utf8 [%s]\n", utf8_dn));
+ return NULL;
+ }
+ ldap_memfree(utf8_dn);
+ return unix_dn;
+}
diff --git a/source/lib/smbldap_util.c b/source/lib/smbldap_util.c
new file mode 100644
index 00000000000..f6097599bc5
--- /dev/null
+++ b/source/lib/smbldap_util.c
@@ -0,0 +1,203 @@
+/*
+ Unix SMB/CIFS mplementation.
+ LDAP protocol helper functions for SAMBA
+ Copyright (C) Jean François Micouleau 1998
+ Copyright (C) Gerald Carter 2001-2003
+ Copyright (C) Shahms King 2001
+ Copyright (C) Andrew Bartlett 2002-2003
+ Copyright (C) Stefan (metze) Metzmacher 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 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 "smbldap.h"
+
+/**********************************************************************
+ Add the sambaDomain to LDAP, so we don't have to search for this stuff
+ again. This is a once-add operation for now.
+
+ TODO: Add other attributes, and allow modification.
+*********************************************************************/
+static NTSTATUS add_new_domain_info(struct smbldap_state *ldap_state,
+ const char *domain_name)
+{
+ fstring sid_string;
+ fstring algorithmic_rid_base_string;
+ pstring filter, dn;
+ LDAPMod **mods = NULL;
+ int rc;
+ int ldap_op;
+ LDAPMessage *result = NULL;
+ int num_result;
+ char **attr_list;
+ uid_t u_low, u_high;
+ gid_t g_low, g_high;
+ uint32 rid_low, rid_high;
+
+ slprintf (filter, sizeof (filter) - 1, "(&(%s=%s)(objectclass=%s))",
+ get_attr_key2string(dominfo_attr_list, LDAP_ATTR_DOMAIN),
+ domain_name, LDAP_OBJ_DOMINFO);
+
+ attr_list = get_attr_list( dominfo_attr_list );
+ rc = smbldap_search_suffix(ldap_state, filter, attr_list, &result);
+ free_attr_list( attr_list );
+
+ if (rc != LDAP_SUCCESS) {
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ num_result = ldap_count_entries(ldap_state->ldap_struct, result);
+
+ if (num_result > 1) {
+ DEBUG (0, ("More than domain with that name exists: bailing out!\n"));
+ ldap_msgfree(result);
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ /* Check if we need to add an entry */
+ DEBUG(3,("Adding new domain\n"));
+ ldap_op = LDAP_MOD_ADD;
+
+ pstr_sprintf(dn, "%s=%s,%s", get_attr_key2string(dominfo_attr_list, LDAP_ATTR_DOMAIN),
+ domain_name, lp_ldap_suffix());
+
+ /* Free original search */
+ ldap_msgfree(result);
+
+ /* make the changes - the entry *must* not already have samba attributes */
+ smbldap_set_mod(&mods, LDAP_MOD_ADD, get_attr_key2string(dominfo_attr_list, LDAP_ATTR_DOMAIN),
+ domain_name);
+
+ /* If we don't have an entry, then ask secrets.tdb for what it thinks.
+ It may choose to make it up */
+
+ sid_to_string(sid_string, get_global_sam_sid());
+ smbldap_set_mod(&mods, LDAP_MOD_ADD, get_attr_key2string(dominfo_attr_list, LDAP_ATTR_DOM_SID), sid_string);
+
+ slprintf(algorithmic_rid_base_string, sizeof(algorithmic_rid_base_string) - 1, "%i", algorithmic_rid_base());
+ smbldap_set_mod(&mods, LDAP_MOD_ADD, get_attr_key2string(dominfo_attr_list, LDAP_ATTR_ALGORITHMIC_RID_BASE),
+ algorithmic_rid_base_string);
+ smbldap_set_mod(&mods, LDAP_MOD_ADD, "objectclass", LDAP_OBJ_DOMINFO);
+
+ /* add the sambaNext[User|Group]Rid attributes if the idmap ranges are set.
+ TODO: fix all the places where the line between idmap and normal operations
+ needed by smbd gets fuzzy --jerry 2003-08-11 */
+
+ if ( lp_idmap_uid(&u_low, &u_high) && lp_idmap_gid(&g_low, &g_high)
+ && get_free_rid_range(&rid_low, &rid_high) )
+ {
+ fstring rid_str;
+
+ fstr_sprintf( rid_str, "%i", rid_high|USER_RID_TYPE );
+ DEBUG(10,("setting next available user rid [%s]\n", rid_str));
+ smbldap_set_mod(&mods, LDAP_MOD_ADD,
+ get_attr_key2string(dominfo_attr_list, LDAP_ATTR_NEXT_USERRID),
+ rid_str);
+
+ fstr_sprintf( rid_str, "%i", rid_high|GROUP_RID_TYPE );
+ DEBUG(10,("setting next available group rid [%s]\n", rid_str));
+ smbldap_set_mod(&mods, LDAP_MOD_ADD,
+ get_attr_key2string(dominfo_attr_list, LDAP_ATTR_NEXT_GROUPRID),
+ rid_str);
+
+ }
+
+
+ switch(ldap_op)
+ {
+ case LDAP_MOD_ADD:
+ rc = smbldap_add(ldap_state, dn, mods);
+ break;
+ case LDAP_MOD_REPLACE:
+ rc = smbldap_modify(ldap_state, dn, mods);
+ break;
+ default:
+ DEBUG(0,("Wrong LDAP operation type: %d!\n", ldap_op));
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ if (rc!=LDAP_SUCCESS) {
+ char *ld_error = NULL;
+ ldap_get_option(ldap_state->ldap_struct, LDAP_OPT_ERROR_STRING, &ld_error);
+ DEBUG(1,("failed to %s domain dn= %s with: %s\n\t%s\n",
+ ldap_op == LDAP_MOD_ADD ? "add" : "modify",
+ dn, ldap_err2string(rc),
+ ld_error?ld_error:"unknown"));
+ SAFE_FREE(ld_error);
+
+ ldap_mods_free(mods, True);
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ DEBUG(2,("added: domain = %s in the LDAP database\n", domain_name));
+ ldap_mods_free(mods, True);
+ return NT_STATUS_OK;
+}
+
+/**********************************************************************
+Search for the domain info entry
+*********************************************************************/
+NTSTATUS smbldap_search_domain_info(struct smbldap_state *ldap_state,
+ LDAPMessage ** result, const char *domain_name,
+ BOOL try_add)
+{
+ NTSTATUS ret = NT_STATUS_UNSUCCESSFUL;
+ pstring filter;
+ int rc;
+ char **attr_list;
+ int count;
+
+ pstr_sprintf(filter, "(&(objectClass=%s)(%s=%s))",
+ LDAP_OBJ_DOMINFO,
+ get_attr_key2string(dominfo_attr_list, LDAP_ATTR_DOMAIN),
+ domain_name);
+
+ DEBUG(2, ("Searching for:[%s]\n", filter));
+
+
+ attr_list = get_attr_list( dominfo_attr_list );
+ rc = smbldap_search_suffix(ldap_state, filter, attr_list , result);
+ free_attr_list( attr_list );
+
+ if (rc != LDAP_SUCCESS) {
+ DEBUG(2,("Problem during LDAPsearch: %s\n", ldap_err2string (rc)));
+ DEBUG(2,("Query was: %s, %s\n", lp_ldap_suffix(), filter));
+ } else if (ldap_count_entries(ldap_state->ldap_struct, *result) < 1) {
+ DEBUG(3, ("Got no domain info entries for domain\n"));
+ ldap_msgfree(*result);
+ *result = NULL;
+ if (try_add && NT_STATUS_IS_OK(ret = add_new_domain_info(ldap_state, domain_name))) {
+ return smbldap_search_domain_info(ldap_state, result, domain_name, False);
+ }
+ else {
+ DEBUG(0, ("Adding domain info for %s failed with %s\n",
+ domain_name, nt_errstr(ret)));
+ return ret;
+ }
+ } else if ((count = ldap_count_entries(ldap_state->ldap_struct, *result)) > 1) {
+ DEBUG(0, ("Got too many (%d) domain info entries for domain %s\n",
+ count, domain_name));
+ ldap_msgfree(*result);
+ *result = NULL;
+ return ret;
+ } else {
+ return NT_STATUS_OK;
+ }
+
+ return ret;
+}
+
diff --git a/source/lib/smbrun.c b/source/lib/smbrun.c
new file mode 100644
index 00000000000..592543bc43b
--- /dev/null
+++ b/source/lib/smbrun.c
@@ -0,0 +1,180 @@
+/*
+ Unix SMB/CIFS implementation.
+ run a command as a specified user
+ 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 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"
+
+/* need to move this from here!! need some sleep ... */
+struct current_user current_user;
+
+/****************************************************************************
+This is a utility function of smbrun().
+****************************************************************************/
+
+static int setup_out_fd(void)
+{
+ int fd;
+ pstring path;
+
+ slprintf(path, sizeof(path)-1, "%s/smb.XXXXXX", tmpdir());
+
+ /* now create the file */
+ fd = smb_mkstemp(path);
+
+ if (fd == -1) {
+ DEBUG(0,("setup_out_fd: Failed to create file %s. (%s)\n",
+ path, strerror(errno) ));
+ return -1;
+ }
+
+ DEBUG(10,("setup_out_fd: Created tmp file %s\n", path ));
+
+ /* Ensure file only kept around by open fd. */
+ unlink(path);
+ return fd;
+}
+
+/****************************************************************************
+run a command being careful about uid/gid handling and putting the output in
+outfd (or discard it if outfd is NULL).
+****************************************************************************/
+
+int smbrun(char *cmd, int *outfd)
+{
+ pid_t pid;
+ uid_t uid = current_user.uid;
+ gid_t gid = current_user.gid;
+
+ /*
+ * Lose any kernel oplock capabilities we may have.
+ */
+ oplock_set_capability(False, False);
+
+ /* point our stdout at the file we want output to go into */
+
+ if (outfd && ((*outfd = setup_out_fd()) == -1)) {
+ return -1;
+ }
+
+ /* in this method we will exec /bin/sh with the correct
+ arguments, after first setting stdout to point at the file */
+
+ /*
+ * We need to temporarily stop CatchChild from eating
+ * SIGCLD signals as it also eats the exit status code. JRA.
+ */
+
+ CatchChildLeaveStatus();
+
+ if ((pid=sys_fork()) < 0) {
+ DEBUG(0,("smbrun: fork failed with error %s\n", strerror(errno) ));
+ CatchChild();
+ if (outfd) {
+ close(*outfd);
+ *outfd = -1;
+ }
+ return errno;
+ }
+
+ if (pid) {
+ /*
+ * Parent.
+ */
+ int status=0;
+ pid_t wpid;
+
+
+ /* the parent just waits for the child to exit */
+ while((wpid = sys_waitpid(pid,&status,0)) < 0) {
+ if(errno == EINTR) {
+ errno = 0;
+ continue;
+ }
+ break;
+ }
+
+ CatchChild();
+
+ if (wpid != pid) {
+ DEBUG(2,("waitpid(%d) : %s\n",(int)pid,strerror(errno)));
+ if (outfd) {
+ close(*outfd);
+ *outfd = -1;
+ }
+ return -1;
+ }
+
+ /* Reset the seek pointer. */
+ if (outfd) {
+ sys_lseek(*outfd, 0, SEEK_SET);
+ }
+
+#if defined(WIFEXITED) && defined(WEXITSTATUS)
+ if (WIFEXITED(status)) {
+ return WEXITSTATUS(status);
+ }
+#endif
+
+ return status;
+ }
+
+ CatchChild();
+
+ /* we are in the child. we exec /bin/sh to do the work for us. we
+ don't directly exec the command we want because it may be a
+ pipeline or anything else the config file specifies */
+
+ /* point our stdout at the file we want output to go into */
+ if (outfd) {
+ close(1);
+ if (sys_dup2(*outfd,1) != 1) {
+ DEBUG(2,("Failed to create stdout file descriptor\n"));
+ close(*outfd);
+ exit(80);
+ }
+ }
+
+ /* now completely lose our privileges. This is a fairly paranoid
+ way of doing it, but it does work on all systems that I know of */
+
+ become_user_permanently(uid, gid);
+
+ if (getuid() != uid || geteuid() != uid ||
+ getgid() != gid || getegid() != gid) {
+ /* we failed to lose our privileges - do not execute
+ the command */
+ exit(81); /* we can't print stuff at this stage,
+ instead use exit codes for debugging */
+ }
+
+#ifndef __INSURE__
+ /* close all other file descriptors, leaving only 0, 1 and 2. 0 and
+ 2 point to /dev/null from the startup code */
+ {
+ int fd;
+ for (fd=3;fd<256;fd++) close(fd);
+ }
+#endif
+
+ execl("/bin/sh","sh","-c",cmd,NULL);
+
+ /* not reached */
+ exit(82);
+ return 1;
+}
diff --git a/source/lib/snprintf.c b/source/lib/snprintf.c
new file mode 100644
index 00000000000..5b0cfa1ab33
--- /dev/null
+++ b/source/lib/snprintf.c
@@ -0,0 +1,1021 @@
+/*
+ * NOTE: If you change this file, please merge it into rsync, samba, etc.
+ */
+
+/*
+ * Copyright Patrick Powell 1995
+ * This code is based on code written by Patrick Powell (papowell@astart.com)
+ * It may be used for any purpose as long as this notice remains intact
+ * on all source code distributions
+ */
+
+/**************************************************************
+ * Original:
+ * Patrick Powell Tue Apr 11 09:48:21 PDT 1995
+ * A bombproof version of doprnt (dopr) included.
+ * Sigh. This sort of thing is always nasty do deal with. Note that
+ * the version here does not include floating point...
+ *
+ * snprintf() is used instead of sprintf() as it does limit checks
+ * for string length. This covers a nasty loophole.
+ *
+ * The other functions are there to prevent NULL pointers from
+ * causing nast effects.
+ *
+ * More Recently:
+ * Brandon Long <blong@fiction.net> 9/15/96 for mutt 0.43
+ * This was ugly. It is still ugly. I opted out of floating point
+ * numbers, but the formatter understands just about everything
+ * from the normal C string format, at least as far as I can tell from
+ * the Solaris 2.5 printf(3S) man page.
+ *
+ * Brandon Long <blong@fiction.net> 10/22/97 for mutt 0.87.1
+ * Ok, added some minimal floating point support, which means this
+ * probably requires libm on most operating systems. Don't yet
+ * support the exponent (e,E) and sigfig (g,G). Also, fmtint()
+ * was pretty badly broken, it just wasn't being exercised in ways
+ * which showed it, so that's been fixed. Also, formated the code
+ * to mutt conventions, and removed dead code left over from the
+ * original. Also, there is now a builtin-test, just compile with:
+ * gcc -DTEST_SNPRINTF -o snprintf snprintf.c -lm
+ * and run snprintf for results.
+ *
+ * Thomas Roessler <roessler@guug.de> 01/27/98 for mutt 0.89i
+ * The PGP code was using unsigned hexadecimal formats.
+ * Unfortunately, unsigned formats simply didn't work.
+ *
+ * Michael Elkins <me@cs.hmc.edu> 03/05/98 for mutt 0.90.8
+ * The original code assumed that both snprintf() and vsnprintf() were
+ * missing. Some systems only have snprintf() but not vsnprintf(), so
+ * the code is now broken down under HAVE_SNPRINTF and HAVE_VSNPRINTF.
+ *
+ * Andrew Tridgell (tridge@samba.org) Oct 1998
+ * fixed handling of %.0f
+ * added test for HAVE_LONG_DOUBLE
+ *
+ * tridge@samba.org, idra@samba.org, April 2001
+ * got rid of fcvt code (twas buggy and made testing harder)
+ * added C99 semantics
+ *
+ * date: 2002/12/19 19:56:31; author: herb; state: Exp; lines: +2 -0
+ * actually print args for %g and %e
+ *
+ * date: 2002/06/03 13:37:52; author: jmcd; state: Exp; lines: +8 -0
+ * Since includes.h isn't included here, VA_COPY has to be defined here. I don't
+ * see any include file that is guaranteed to be here, so I'm defining it
+ * locally. Fixes AIX and Solaris builds.
+ *
+ * date: 2002/06/03 03:07:24; author: tridge; state: Exp; lines: +5 -13
+ * put the ifdef for HAVE_VA_COPY in one place rather than in lots of
+ * functions
+ *
+ * date: 2002/05/17 14:51:22; author: jmcd; state: Exp; lines: +21 -4
+ * Fix usage of va_list passed as an arg. Use __va_copy before using it
+ * when it exists.
+ *
+ * date: 2002/04/16 22:38:04; author: idra; state: Exp; lines: +20 -14
+ * Fix incorrect zpadlen handling in fmtfp.
+ * Thanks to Ollie Oldham <ollie.oldham@metro-optix.com> for spotting it.
+ * few mods to make it easier to compile the tests.
+ * addedd the "Ollie" test to the floating point ones.
+ *
+ * Martin Pool (mbp@samba.org) April 2003
+ * Remove NO_CONFIG_H so that the test case can be built within a source
+ * tree with less trouble.
+ * Remove unnecessary SAFE_FREE() definition.
+ *
+ * Martin Pool (mbp@samba.org) May 2003
+ * Put in a prototype for dummy_snprintf() to quiet compiler warnings.
+ *
+ * Move #endif to make sure VA_COPY, LDOUBLE, etc are defined even
+ * if the C library has some snprintf functions already.
+ **************************************************************/
+
+#ifndef NO_CONFIG_H
+#include "config.h"
+#else
+#define NULL 0
+#endif
+
+#ifdef TEST_SNPRINTF /* need math library headers for testing */
+
+/* In test mode, we pretend that this system doesn't have any snprintf
+ * functions, regardless of what config.h says. */
+# undef HAVE_SNPRINTF
+# undef HAVE_VSNPRINTF
+# undef HAVE_C99_VSNPRINTF
+# undef HAVE_ASPRINTF
+# undef HAVE_VASPRINTF
+# include <math.h>
+#endif /* TEST_SNPRINTF */
+
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+
+#ifdef HAVE_STRINGS_H
+#include <strings.h>
+#endif
+#ifdef HAVE_CTYPE_H
+#include <ctype.h>
+#endif
+#include <sys/types.h>
+#include <stdarg.h>
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+
+#if defined(HAVE_SNPRINTF) && defined(HAVE_VSNPRINTF) && defined(HAVE_C99_VSNPRINTF)
+/* only include stdio.h if we are not re-defining snprintf or vsnprintf */
+#include <stdio.h>
+ /* make the compiler happy with an empty file */
+ void dummy_snprintf(void);
+ void dummy_snprintf(void) {}
+#endif /* HAVE_SNPRINTF, etc */
+
+#ifdef HAVE_LONG_DOUBLE
+#define LDOUBLE long double
+#else
+#define LDOUBLE double
+#endif
+
+#ifdef HAVE_LONG_LONG
+#define LLONG long long
+#else
+#define LLONG long
+#endif
+
+#ifndef VA_COPY
+#ifdef HAVE_VA_COPY
+#define VA_COPY(dest, src) va_copy(dest, src)
+#else
+#ifdef HAVE___VA_COPY
+#define VA_COPY(dest, src) __va_copy(dest, src)
+#else
+#define VA_COPY(dest, src) (dest) = (src)
+#endif
+#endif
+
+/*
+ * dopr(): poor man's version of doprintf
+ */
+
+/* format read states */
+#define DP_S_DEFAULT 0
+#define DP_S_FLAGS 1
+#define DP_S_MIN 2
+#define DP_S_DOT 3
+#define DP_S_MAX 4
+#define DP_S_MOD 5
+#define DP_S_CONV 6
+#define DP_S_DONE 7
+
+/* format flags - Bits */
+#define DP_F_MINUS (1 << 0)
+#define DP_F_PLUS (1 << 1)
+#define DP_F_SPACE (1 << 2)
+#define DP_F_NUM (1 << 3)
+#define DP_F_ZERO (1 << 4)
+#define DP_F_UP (1 << 5)
+#define DP_F_UNSIGNED (1 << 6)
+
+/* Conversion Flags */
+#define DP_C_SHORT 1
+#define DP_C_LONG 2
+#define DP_C_LDOUBLE 3
+#define DP_C_LLONG 4
+
+#define char_to_int(p) ((p)- '0')
+#ifndef MAX
+#define MAX(p,q) (((p) >= (q)) ? (p) : (q))
+#endif
+
+/* yes this really must be a ||. Don't muck with this (tridge) */
+#if !defined(HAVE_VSNPRINTF) || !defined(HAVE_C99_VSNPRINTF)
+
+static size_t dopr(char *buffer, size_t maxlen, const char *format,
+ va_list args_in);
+static void fmtstr(char *buffer, size_t *currlen, size_t maxlen,
+ char *value, int flags, int min, int max);
+static void fmtint(char *buffer, size_t *currlen, size_t maxlen,
+ long value, int base, int min, int max, int flags);
+static void fmtfp(char *buffer, size_t *currlen, size_t maxlen,
+ LDOUBLE fvalue, int min, int max, int flags);
+static void dopr_outch(char *buffer, size_t *currlen, size_t maxlen, char c);
+
+static size_t dopr(char *buffer, size_t maxlen, const char *format, va_list args_in)
+{
+ char ch;
+ LLONG value;
+ LDOUBLE fvalue;
+ char *strvalue;
+ int min;
+ int max;
+ int state;
+ int flags;
+ int cflags;
+ size_t currlen;
+ va_list args;
+
+ VA_COPY(args, args_in);
+
+ state = DP_S_DEFAULT;
+ currlen = flags = cflags = min = 0;
+ max = -1;
+ ch = *format++;
+
+ while (state != DP_S_DONE) {
+ if (ch == '\0')
+ state = DP_S_DONE;
+
+ switch(state) {
+ case DP_S_DEFAULT:
+ if (ch == '%')
+ state = DP_S_FLAGS;
+ else
+ dopr_outch (buffer, &currlen, maxlen, ch);
+ ch = *format++;
+ break;
+ case DP_S_FLAGS:
+ switch (ch) {
+ case '-':
+ flags |= DP_F_MINUS;
+ ch = *format++;
+ break;
+ case '+':
+ flags |= DP_F_PLUS;
+ ch = *format++;
+ break;
+ case ' ':
+ flags |= DP_F_SPACE;
+ ch = *format++;
+ break;
+ case '#':
+ flags |= DP_F_NUM;
+ ch = *format++;
+ break;
+ case '0':
+ flags |= DP_F_ZERO;
+ ch = *format++;
+ break;
+ default:
+ state = DP_S_MIN;
+ break;
+ }
+ break;
+ case DP_S_MIN:
+ if (isdigit((unsigned char)ch)) {
+ min = 10*min + char_to_int (ch);
+ ch = *format++;
+ } else if (ch == '*') {
+ min = va_arg (args, int);
+ ch = *format++;
+ state = DP_S_DOT;
+ } else {
+ state = DP_S_DOT;
+ }
+ break;
+ case DP_S_DOT:
+ if (ch == '.') {
+ state = DP_S_MAX;
+ ch = *format++;
+ } else {
+ state = DP_S_MOD;
+ }
+ break;
+ case DP_S_MAX:
+ if (isdigit((unsigned char)ch)) {
+ if (max < 0)
+ max = 0;
+ max = 10*max + char_to_int (ch);
+ ch = *format++;
+ } else if (ch == '*') {
+ max = va_arg (args, int);
+ ch = *format++;
+ state = DP_S_MOD;
+ } else {
+ state = DP_S_MOD;
+ }
+ break;
+ case DP_S_MOD:
+ switch (ch) {
+ case 'h':
+ cflags = DP_C_SHORT;
+ ch = *format++;
+ break;
+ case 'l':
+ cflags = DP_C_LONG;
+ ch = *format++;
+ if (ch == 'l') { /* It's a long long */
+ cflags = DP_C_LLONG;
+ ch = *format++;
+ }
+ break;
+ case 'L':
+ cflags = DP_C_LDOUBLE;
+ ch = *format++;
+ break;
+ default:
+ break;
+ }
+ state = DP_S_CONV;
+ break;
+ case DP_S_CONV:
+ switch (ch) {
+ case 'd':
+ case 'i':
+ if (cflags == DP_C_SHORT)
+ value = va_arg (args, int);
+ else if (cflags == DP_C_LONG)
+ value = va_arg (args, long int);
+ else if (cflags == DP_C_LLONG)
+ value = va_arg (args, LLONG);
+ else
+ value = va_arg (args, int);
+ fmtint (buffer, &currlen, maxlen, value, 10, min, max, flags);
+ break;
+ case 'o':
+ flags |= DP_F_UNSIGNED;
+ if (cflags == DP_C_SHORT)
+ value = va_arg (args, unsigned int);
+ else if (cflags == DP_C_LONG)
+ value = (long)va_arg (args, unsigned long int);
+ else if (cflags == DP_C_LLONG)
+ value = (long)va_arg (args, unsigned LLONG);
+ else
+ value = (long)va_arg (args, unsigned int);
+ fmtint (buffer, &currlen, maxlen, value, 8, min, max, flags);
+ break;
+ case 'u':
+ flags |= DP_F_UNSIGNED;
+ if (cflags == DP_C_SHORT)
+ value = va_arg (args, unsigned int);
+ else if (cflags == DP_C_LONG)
+ value = (long)va_arg (args, unsigned long int);
+ else if (cflags == DP_C_LLONG)
+ value = (LLONG)va_arg (args, unsigned LLONG);
+ else
+ value = (long)va_arg (args, unsigned int);
+ fmtint (buffer, &currlen, maxlen, value, 10, min, max, flags);
+ break;
+ case 'X':
+ flags |= DP_F_UP;
+ case 'x':
+ flags |= DP_F_UNSIGNED;
+ if (cflags == DP_C_SHORT)
+ value = va_arg (args, unsigned int);
+ else if (cflags == DP_C_LONG)
+ value = (long)va_arg (args, unsigned long int);
+ else if (cflags == DP_C_LLONG)
+ value = (LLONG)va_arg (args, unsigned LLONG);
+ else
+ value = (long)va_arg (args, unsigned int);
+ fmtint (buffer, &currlen, maxlen, value, 16, min, max, flags);
+ break;
+ case 'f':
+ if (cflags == DP_C_LDOUBLE)
+ fvalue = va_arg (args, LDOUBLE);
+ else
+ fvalue = va_arg (args, double);
+ /* um, floating point? */
+ fmtfp (buffer, &currlen, maxlen, fvalue, min, max, flags);
+ break;
+ case 'E':
+ flags |= DP_F_UP;
+ case 'e':
+ if (cflags == DP_C_LDOUBLE)
+ fvalue = va_arg (args, LDOUBLE);
+ else
+ fvalue = va_arg (args, double);
+ fmtfp (buffer, &currlen, maxlen, fvalue, min, max, flags);
+ break;
+ case 'G':
+ flags |= DP_F_UP;
+ case 'g':
+ if (cflags == DP_C_LDOUBLE)
+ fvalue = va_arg (args, LDOUBLE);
+ else
+ fvalue = va_arg (args, double);
+ fmtfp (buffer, &currlen, maxlen, fvalue, min, max, flags);
+ break;
+ case 'c':
+ dopr_outch (buffer, &currlen, maxlen, va_arg (args, int));
+ break;
+ case 's':
+ strvalue = va_arg (args, char *);
+ if (!strvalue) strvalue = "(NULL)";
+ if (max == -1) {
+ max = strlen(strvalue);
+ }
+ if (min > 0 && max >= 0 && min > max) max = min;
+ fmtstr (buffer, &currlen, maxlen, strvalue, flags, min, max);
+ break;
+ case 'p':
+ strvalue = va_arg (args, void *);
+ fmtint (buffer, &currlen, maxlen, (long) strvalue, 16, min, max, flags);
+ break;
+ case 'n':
+ if (cflags == DP_C_SHORT) {
+ short int *num;
+ num = va_arg (args, short int *);
+ *num = currlen;
+ } else if (cflags == DP_C_LONG) {
+ long int *num;
+ num = va_arg (args, long int *);
+ *num = (long int)currlen;
+ } else if (cflags == DP_C_LLONG) {
+ LLONG *num;
+ num = va_arg (args, LLONG *);
+ *num = (LLONG)currlen;
+ } else {
+ int *num;
+ num = va_arg (args, int *);
+ *num = currlen;
+ }
+ break;
+ case '%':
+ dopr_outch (buffer, &currlen, maxlen, ch);
+ break;
+ case 'w':
+ /* not supported yet, treat as next char */
+ ch = *format++;
+ break;
+ default:
+ /* Unknown, skip */
+ break;
+ }
+ ch = *format++;
+ state = DP_S_DEFAULT;
+ flags = cflags = min = 0;
+ max = -1;
+ break;
+ case DP_S_DONE:
+ break;
+ default:
+ /* hmm? */
+ break; /* some picky compilers need this */
+ }
+ }
+ if (maxlen != 0) {
+ if (currlen < maxlen - 1)
+ buffer[currlen] = '\0';
+ else if (maxlen > 0)
+ buffer[maxlen - 1] = '\0';
+ }
+
+ return currlen;
+}
+
+static void fmtstr(char *buffer, size_t *currlen, size_t maxlen,
+ char *value, int flags, int min, int max)
+{
+ int padlen, strln; /* amount to pad */
+ int cnt = 0;
+
+#ifdef DEBUG_SNPRINTF
+ printf("fmtstr min=%d max=%d s=[%s]\n", min, max, value);
+#endif
+ if (value == 0) {
+ value = "<NULL>";
+ }
+
+ for (strln = 0; value[strln]; ++strln); /* strlen */
+ padlen = min - strln;
+ if (padlen < 0)
+ padlen = 0;
+ if (flags & DP_F_MINUS)
+ padlen = -padlen; /* Left Justify */
+
+ while ((padlen > 0) && (cnt < max)) {
+ dopr_outch (buffer, currlen, maxlen, ' ');
+ --padlen;
+ ++cnt;
+ }
+ while (*value && (cnt < max)) {
+ dopr_outch (buffer, currlen, maxlen, *value++);
+ ++cnt;
+ }
+ while ((padlen < 0) && (cnt < max)) {
+ dopr_outch (buffer, currlen, maxlen, ' ');
+ ++padlen;
+ ++cnt;
+ }
+}
+
+/* Have to handle DP_F_NUM (ie 0x and 0 alternates) */
+
+static void fmtint(char *buffer, size_t *currlen, size_t maxlen,
+ long value, int base, int min, int max, int flags)
+{
+ int signvalue = 0;
+ unsigned long uvalue;
+ char convert[20];
+ int place = 0;
+ int spadlen = 0; /* amount to space pad */
+ int zpadlen = 0; /* amount to zero pad */
+ int caps = 0;
+
+ if (max < 0)
+ max = 0;
+
+ uvalue = value;
+
+ if(!(flags & DP_F_UNSIGNED)) {
+ if( value < 0 ) {
+ signvalue = '-';
+ uvalue = -value;
+ } else {
+ if (flags & DP_F_PLUS) /* Do a sign (+/i) */
+ signvalue = '+';
+ else if (flags & DP_F_SPACE)
+ signvalue = ' ';
+ }
+ }
+
+ if (flags & DP_F_UP) caps = 1; /* Should characters be upper case? */
+
+ do {
+ convert[place++] =
+ (caps? "0123456789ABCDEF":"0123456789abcdef")
+ [uvalue % (unsigned)base ];
+ uvalue = (uvalue / (unsigned)base );
+ } while(uvalue && (place < 20));
+ if (place == 20) place--;
+ convert[place] = 0;
+
+ zpadlen = max - place;
+ spadlen = min - MAX (max, place) - (signvalue ? 1 : 0);
+ if (zpadlen < 0) zpadlen = 0;
+ if (spadlen < 0) spadlen = 0;
+ if (flags & DP_F_ZERO) {
+ zpadlen = MAX(zpadlen, spadlen);
+ spadlen = 0;
+ }
+ if (flags & DP_F_MINUS)
+ spadlen = -spadlen; /* Left Justifty */
+
+#ifdef DEBUG_SNPRINTF
+ printf("zpad: %d, spad: %d, min: %d, max: %d, place: %d\n",
+ zpadlen, spadlen, min, max, place);
+#endif
+
+ /* Spaces */
+ while (spadlen > 0) {
+ dopr_outch (buffer, currlen, maxlen, ' ');
+ --spadlen;
+ }
+
+ /* Sign */
+ if (signvalue)
+ dopr_outch (buffer, currlen, maxlen, signvalue);
+
+ /* Zeros */
+ if (zpadlen > 0) {
+ while (zpadlen > 0) {
+ dopr_outch (buffer, currlen, maxlen, '0');
+ --zpadlen;
+ }
+ }
+
+ /* Digits */
+ while (place > 0)
+ dopr_outch (buffer, currlen, maxlen, convert[--place]);
+
+ /* Left Justified spaces */
+ while (spadlen < 0) {
+ dopr_outch (buffer, currlen, maxlen, ' ');
+ ++spadlen;
+ }
+}
+
+static LDOUBLE abs_val(LDOUBLE value)
+{
+ LDOUBLE result = value;
+
+ if (value < 0)
+ result = -value;
+
+ return result;
+}
+
+static LDOUBLE POW10(int exp)
+{
+ LDOUBLE result = 1;
+
+ while (exp) {
+ result *= 10;
+ exp--;
+ }
+
+ return result;
+}
+
+static LLONG ROUND(LDOUBLE value)
+{
+ LLONG intpart;
+
+ intpart = (LLONG)value;
+ value = value - intpart;
+ if (value >= 0.5) intpart++;
+
+ return intpart;
+}
+
+/* a replacement for modf that doesn't need the math library. Should
+ be portable, but slow */
+static double my_modf(double x0, double *iptr)
+{
+ int i;
+ long l;
+ double x = x0;
+ double f = 1.0;
+
+ for (i=0;i<100;i++) {
+ l = (long)x;
+ if (l <= (x+1) && l >= (x-1)) break;
+ x *= 0.1;
+ f *= 10.0;
+ }
+
+ if (i == 100) {
+ /* yikes! the number is beyond what we can handle. What do we do? */
+ (*iptr) = 0;
+ return 0;
+ }
+
+ if (i != 0) {
+ double i2;
+ double ret;
+
+ ret = my_modf(x0-l*f, &i2);
+ (*iptr) = l*f + i2;
+ return ret;
+ }
+
+ (*iptr) = l;
+ return x - (*iptr);
+}
+
+
+static void fmtfp (char *buffer, size_t *currlen, size_t maxlen,
+ LDOUBLE fvalue, int min, int max, int flags)
+{
+ int signvalue = 0;
+ double ufvalue;
+ char iconvert[311];
+ char fconvert[311];
+ int iplace = 0;
+ int fplace = 0;
+ int padlen = 0; /* amount to pad */
+ int zpadlen = 0;
+ int caps = 0;
+ int idx;
+ double intpart;
+ double fracpart;
+ double temp;
+
+ /*
+ * AIX manpage says the default is 0, but Solaris says the default
+ * is 6, and sprintf on AIX defaults to 6
+ */
+ if (max < 0)
+ max = 6;
+
+ ufvalue = abs_val (fvalue);
+
+ if (fvalue < 0) {
+ signvalue = '-';
+ } else {
+ if (flags & DP_F_PLUS) { /* Do a sign (+/i) */
+ signvalue = '+';
+ } else {
+ if (flags & DP_F_SPACE)
+ signvalue = ' ';
+ }
+ }
+
+#if 0
+ if (flags & DP_F_UP) caps = 1; /* Should characters be upper case? */
+#endif
+
+#if 0
+ if (max == 0) ufvalue += 0.5; /* if max = 0 we must round */
+#endif
+
+ /*
+ * Sorry, we only support 16 digits past the decimal because of our
+ * conversion method
+ */
+ if (max > 16)
+ max = 16;
+
+ /* We "cheat" by converting the fractional part to integer by
+ * multiplying by a factor of 10
+ */
+
+ temp = ufvalue;
+ my_modf(temp, &intpart);
+
+ fracpart = ROUND((POW10(max)) * (ufvalue - intpart));
+
+ if (fracpart >= POW10(max)) {
+ intpart++;
+ fracpart -= POW10(max);
+ }
+
+
+ /* Convert integer part */
+ do {
+ temp = intpart*0.1;
+ my_modf(temp, &intpart);
+ idx = (int) ((temp -intpart +0.05)* 10.0);
+ /* idx = (int) (((double)(temp*0.1) -intpart +0.05) *10.0); */
+ /* printf ("%llf, %f, %x\n", temp, intpart, idx); */
+ iconvert[iplace++] =
+ (caps? "0123456789ABCDEF":"0123456789abcdef")[idx];
+ } while (intpart && (iplace < 311));
+ if (iplace == 311) iplace--;
+ iconvert[iplace] = 0;
+
+ /* Convert fractional part */
+ if (fracpart)
+ {
+ do {
+ temp = fracpart*0.1;
+ my_modf(temp, &fracpart);
+ idx = (int) ((temp -fracpart +0.05)* 10.0);
+ /* idx = (int) ((((temp/10) -fracpart) +0.05) *10); */
+ /* printf ("%lf, %lf, %ld\n", temp, fracpart, idx ); */
+ fconvert[fplace++] =
+ (caps? "0123456789ABCDEF":"0123456789abcdef")[idx];
+ } while(fracpart && (fplace < 311));
+ if (fplace == 311) fplace--;
+ }
+ fconvert[fplace] = 0;
+
+ /* -1 for decimal point, another -1 if we are printing a sign */
+ padlen = min - iplace - max - 1 - ((signvalue) ? 1 : 0);
+ zpadlen = max - fplace;
+ if (zpadlen < 0) zpadlen = 0;
+ if (padlen < 0)
+ padlen = 0;
+ if (flags & DP_F_MINUS)
+ padlen = -padlen; /* Left Justifty */
+
+ if ((flags & DP_F_ZERO) && (padlen > 0)) {
+ if (signvalue) {
+ dopr_outch (buffer, currlen, maxlen, signvalue);
+ --padlen;
+ signvalue = 0;
+ }
+ while (padlen > 0) {
+ dopr_outch (buffer, currlen, maxlen, '0');
+ --padlen;
+ }
+ }
+ while (padlen > 0) {
+ dopr_outch (buffer, currlen, maxlen, ' ');
+ --padlen;
+ }
+ if (signvalue)
+ dopr_outch (buffer, currlen, maxlen, signvalue);
+
+ while (iplace > 0)
+ dopr_outch (buffer, currlen, maxlen, iconvert[--iplace]);
+
+#ifdef DEBUG_SNPRINTF
+ printf("fmtfp: fplace=%d zpadlen=%d\n", fplace, zpadlen);
+#endif
+
+ /*
+ * Decimal point. This should probably use locale to find the correct
+ * char to print out.
+ */
+ if (max > 0) {
+ dopr_outch (buffer, currlen, maxlen, '.');
+
+ while (zpadlen > 0) {
+ dopr_outch (buffer, currlen, maxlen, '0');
+ --zpadlen;
+ }
+
+ while (fplace > 0)
+ dopr_outch (buffer, currlen, maxlen, fconvert[--fplace]);
+ }
+
+ while (padlen < 0) {
+ dopr_outch (buffer, currlen, maxlen, ' ');
+ ++padlen;
+ }
+}
+
+static void dopr_outch(char *buffer, size_t *currlen, size_t maxlen, char c)
+{
+ if (*currlen < maxlen) {
+ buffer[(*currlen)] = c;
+ }
+ (*currlen)++;
+}
+
+ int vsnprintf (char *str, size_t count, const char *fmt, va_list args)
+{
+ return dopr(str, count, fmt, args);
+}
+#endif
+
+/* yes this really must be a ||. Don't muck with this (tridge)
+ *
+ * The logic for these two is that we need our own definition if the
+ * OS *either* has no definition of *sprintf, or if it does have one
+ * that doesn't work properly according to the autoconf test.
+ */
+#if !defined(HAVE_SNPRINTF) || !defined(HAVE_C99_VSNPRINTF)
+int smb_snprintf(char *str,size_t count,const char *fmt,...)
+{
+ size_t ret;
+ va_list ap;
+
+ va_start(ap, fmt);
+ ret = vsnprintf(str, count, fmt, ap);
+ va_end(ap);
+ return ret;
+}
+#endif
+
+#endif
+
+#ifndef HAVE_VASPRINTF
+ int vasprintf(char **ptr, const char *format, va_list ap)
+{
+ int ret;
+ va_list ap2;
+
+ VA_COPY(ap2, ap);
+
+ ret = vsnprintf(NULL, 0, format, ap2);
+ if (ret <= 0) return ret;
+
+ (*ptr) = (char *)malloc(ret+1);
+ if (!*ptr) return -1;
+
+ VA_COPY(ap2, ap);
+
+ ret = vsnprintf(*ptr, ret+1, format, ap2);
+
+ return ret;
+}
+#endif
+
+
+#ifndef HAVE_ASPRINTF
+ int asprintf(char **ptr, const char *format, ...)
+{
+ va_list ap;
+ int ret;
+
+ *ptr = NULL;
+ va_start(ap, format);
+ ret = vasprintf(ptr, format, ap);
+ va_end(ap);
+
+ return ret;
+}
+#endif
+
+#ifdef TEST_SNPRINTF
+
+ int sprintf(char *str,const char *fmt,...);
+
+ int main (void)
+{
+ char buf1[1024];
+ char buf2[1024];
+ char *fp_fmt[] = {
+ "%1.1f",
+ "%-1.5f",
+ "%1.5f",
+ "%123.9f",
+ "%10.5f",
+ "% 10.5f",
+ "%+22.9f",
+ "%+4.9f",
+ "%01.3f",
+ "%4f",
+ "%3.1f",
+ "%3.2f",
+ "%.0f",
+ "%f",
+ "-16.16f",
+ NULL
+ };
+ double fp_nums[] = { 6442452944.1234, -1.5, 134.21, 91340.2, 341.1234, 203.9, 0.96, 0.996,
+ 0.9996, 1.996, 4.136, 5.030201, 0.00205,
+ /* END LIST */ 0};
+ char *int_fmt[] = {
+ "%-1.5d",
+ "%1.5d",
+ "%123.9d",
+ "%5.5d",
+ "%10.5d",
+ "% 10.5d",
+ "%+22.33d",
+ "%01.3d",
+ "%4d",
+ "%d",
+ NULL
+ };
+ long int_nums[] = { -1, 134, 91340, 341, 0203, 0};
+ char *str_fmt[] = {
+ "10.5s",
+ "5.10s",
+ "10.1s",
+ "0.10s",
+ "10.0s",
+ "1.10s",
+ "%s",
+ "%.1s",
+ "%.10s",
+ "%10s",
+ NULL
+ };
+ char *str_vals[] = {"hello", "a", "", "a longer string", NULL};
+ int x, y;
+ int fail = 0;
+ int num = 0;
+
+ printf ("Testing snprintf format codes against system sprintf...\n");
+
+ for (x = 0; fp_fmt[x] ; x++) {
+ for (y = 0; fp_nums[y] != 0 ; y++) {
+ int l1 = snprintf(NULL, 0, fp_fmt[x], fp_nums[y]);
+ int l2 = snprintf(buf1, sizeof(buf1), fp_fmt[x], fp_nums[y]);
+ sprintf (buf2, fp_fmt[x], fp_nums[y]);
+ if (strcmp (buf1, buf2)) {
+ printf("snprintf doesn't match Format: %s\n\tsnprintf = [%s]\n\t sprintf = [%s]\n",
+ fp_fmt[x], buf1, buf2);
+ fail++;
+ }
+ if (l1 != l2) {
+ printf("snprintf l1 != l2 (%d %d) %s\n", l1, l2, fp_fmt[x]);
+ fail++;
+ }
+ num++;
+ }
+ }
+
+ for (x = 0; int_fmt[x] ; x++) {
+ for (y = 0; int_nums[y] != 0 ; y++) {
+ int l1 = snprintf(NULL, 0, int_fmt[x], int_nums[y]);
+ int l2 = snprintf(buf1, sizeof(buf1), int_fmt[x], int_nums[y]);
+ sprintf (buf2, int_fmt[x], int_nums[y]);
+ if (strcmp (buf1, buf2)) {
+ printf("snprintf doesn't match Format: %s\n\tsnprintf = [%s]\n\t sprintf = [%s]\n",
+ int_fmt[x], buf1, buf2);
+ fail++;
+ }
+ if (l1 != l2) {
+ printf("snprintf l1 != l2 (%d %d) %s\n", l1, l2, int_fmt[x]);
+ fail++;
+ }
+ num++;
+ }
+ }
+
+ for (x = 0; str_fmt[x] ; x++) {
+ for (y = 0; str_vals[y] != 0 ; y++) {
+ int l1 = snprintf(NULL, 0, str_fmt[x], str_vals[y]);
+ int l2 = snprintf(buf1, sizeof(buf1), str_fmt[x], str_vals[y]);
+ sprintf (buf2, str_fmt[x], str_vals[y]);
+ if (strcmp (buf1, buf2)) {
+ printf("snprintf doesn't match Format: %s\n\tsnprintf = [%s]\n\t sprintf = [%s]\n",
+ str_fmt[x], buf1, buf2);
+ fail++;
+ }
+ if (l1 != l2) {
+ printf("snprintf l1 != l2 (%d %d) %s\n", l1, l2, str_fmt[x]);
+ fail++;
+ }
+ num++;
+ }
+ }
+
+ printf ("%d tests failed out of %d.\n", fail, num);
+
+ printf("seeing how many digits we support\n");
+ {
+ double v0 = 0.12345678901234567890123456789012345678901;
+ for (x=0; x<100; x++) {
+ double p = pow(10, x);
+ double r = v0*p;
+ snprintf(buf1, sizeof(buf1), "%1.1f", r);
+ sprintf(buf2, "%1.1f", r);
+ if (strcmp(buf1, buf2)) {
+ printf("we seem to support %d digits\n", x-1);
+ break;
+ }
+ }
+ }
+
+ return 0;
+}
+#endif /* TEST_SNPRINTF */
diff --git a/source/lib/sock_exec.c b/source/lib/sock_exec.c
new file mode 100644
index 00000000000..52c5a8ce52c
--- /dev/null
+++ b/source/lib/sock_exec.c
@@ -0,0 +1,115 @@
+/*
+ Unix SMB/CIFS implementation.
+ Samba utility functions
+ Copyright (C) Andrew Tridgell 1992-1998
+ Copyright (C) Tim Potter 2000-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 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"
+
+/*******************************************************************
+this is like socketpair but uses tcp. It is used by the Samba
+regression test code
+The function guarantees that nobody else can attach to the socket,
+or if they do that this function fails and the socket gets closed
+returns 0 on success, -1 on failure
+the resulting file descriptors are symmetrical
+ ******************************************************************/
+static int socketpair_tcp(int fd[2])
+{
+ int listener;
+ struct sockaddr_in sock;
+ struct sockaddr_in sock2;
+ socklen_t socklen = sizeof(sock);
+ int connect_done = 0;
+
+ fd[0] = fd[1] = listener = -1;
+
+ memset(&sock, 0, sizeof(sock));
+
+ if ((listener = socket(PF_INET, SOCK_STREAM, 0)) == -1) goto failed;
+
+ memset(&sock2, 0, sizeof(sock2));
+#ifdef HAVE_SOCK_SIN_LEN
+ sock2.sin_len = sizeof(sock2);
+#endif
+ sock2.sin_family = PF_INET;
+
+ bind(listener, (struct sockaddr *)&sock2, sizeof(sock2));
+
+ if (listen(listener, 1) != 0) goto failed;
+
+ if (getsockname(listener, (struct sockaddr *)&sock, &socklen) != 0) goto failed;
+
+ if ((fd[1] = socket(PF_INET, SOCK_STREAM, 0)) == -1) goto failed;
+
+ set_blocking(fd[1], 0);
+
+ sock.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
+
+ if (connect(fd[1],(struct sockaddr *)&sock,sizeof(sock)) == -1) {
+ if (errno != EINPROGRESS) goto failed;
+ } else {
+ connect_done = 1;
+ }
+
+ if ((fd[0] = accept(listener, (struct sockaddr *)&sock, &socklen)) == -1) goto failed;
+
+ close(listener);
+ if (connect_done == 0) {
+ if (connect(fd[1],(struct sockaddr *)&sock,sizeof(sock)) != 0
+ && errno != EISCONN) goto failed;
+ }
+
+ set_blocking(fd[1], 1);
+
+ /* all OK! */
+ return 0;
+
+ failed:
+ if (fd[0] != -1) close(fd[0]);
+ if (fd[1] != -1) close(fd[1]);
+ if (listener != -1) close(listener);
+ return -1;
+}
+
+
+/*******************************************************************
+run a program on a local tcp socket, this is used to launch smbd
+when regression testing
+the return value is a socket which is attached to a subprocess
+running "prog". stdin and stdout are attached. stderr is left
+attached to the original stderr
+ ******************************************************************/
+int sock_exec(const char *prog)
+{
+ int fd[2];
+ if (socketpair_tcp(fd) != 0) {
+ DEBUG(0,("socketpair_tcp failed (%s)\n", strerror(errno)));
+ return -1;
+ }
+ if (fork() == 0) {
+ close(fd[0]);
+ close(0);
+ close(1);
+ dup(fd[1]);
+ dup(fd[1]);
+ exit(system(prog));
+ }
+ close(fd[1]);
+ return fd[0];
+}
diff --git a/source/lib/substitute.c b/source/lib/substitute.c
new file mode 100644
index 00000000000..fed11c22982
--- /dev/null
+++ b/source/lib/substitute.c
@@ -0,0 +1,802 @@
+/*
+ Unix SMB/CIFS implementation.
+ string substitution functions
+ Copyright (C) Andrew Tridgell 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 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"
+
+fstring local_machine="";
+fstring remote_arch="UNKNOWN";
+userdom_struct current_user_info;
+fstring remote_proto="UNKNOWN";
+
+static fstring remote_machine;
+static fstring smb_user_name;
+
+/**
+ * Set the 'local' machine name
+ * @param local_name the name we are being called
+ * @param if this is the 'final' name for us, not be be changed again
+ */
+
+void set_local_machine_name(const char* local_name, BOOL perm)
+{
+ static BOOL already_perm = False;
+ fstring tmp_local_machine;
+
+ /*
+ * Windows NT/2k uses "*SMBSERVER" and XP uses "*SMBSERV"
+ * arrggg!!!
+ */
+
+ if (strequal(local_name, "*SMBSERVER"))
+ return;
+
+ if (strequal(local_name, "*SMBSERV"))
+ return;
+
+ if (already_perm)
+ return;
+
+ already_perm = perm;
+
+ fstrcpy(tmp_local_machine,local_name);
+ trim_char(tmp_local_machine,' ',' ');
+ alpha_strcpy(local_machine,tmp_local_machine,SAFE_NETBIOS_CHARS,sizeof(local_machine)-1);
+ strlower_m(local_machine);
+}
+
+/**
+ * Set the 'remote' machine name
+ * @param remote_name the name our client wants to be called by
+ * @param if this is the 'final' name for them, not be be changed again
+ */
+
+void set_remote_machine_name(const char* remote_name, BOOL perm)
+{
+ static BOOL already_perm = False;
+ fstring tmp_remote_machine;
+
+ if (already_perm)
+ return;
+
+ already_perm = perm;
+
+ fstrcpy(tmp_remote_machine,remote_name);
+ trim_char(tmp_remote_machine,' ',' ');
+ alpha_strcpy(remote_machine,tmp_remote_machine,SAFE_NETBIOS_CHARS,sizeof(remote_machine)-1);
+ strlower_m(remote_machine);
+}
+
+const char* get_remote_machine_name(void)
+{
+ return remote_machine;
+}
+
+const char* get_local_machine_name(void)
+{
+ if (!*local_machine) {
+ return global_myname();
+ }
+
+ return local_machine;
+}
+
+/*******************************************************************
+ Setup the string used by %U substitution.
+********************************************************************/
+
+void sub_set_smb_name(const char *name)
+{
+ fstring tmp;
+
+ /* don't let anonymous logins override the name */
+ if (! *name)
+ return;
+
+ fstrcpy(tmp,name);
+ trim_char(tmp,' ',' ');
+ strlower_m(tmp);
+ alpha_strcpy(smb_user_name,tmp,SAFE_NETBIOS_CHARS,sizeof(smb_user_name)-1);
+}
+
+char* sub_get_smb_name( void )
+{
+ return smb_user_name;
+}
+
+/*******************************************************************
+ Setup the strings used by substitutions. Called per packet. Ensure
+ %U name is set correctly also.
+********************************************************************/
+
+void set_current_user_info(const userdom_struct *pcui)
+{
+ current_user_info = *pcui;
+ /* The following is safe as current_user_info.smb_name
+ * has already been sanitised in register_vuid. */
+ fstrcpy(smb_user_name, current_user_info.smb_name);
+}
+
+/*******************************************************************
+ Given a pointer to a %$(NAME) expand it as an environment variable.
+ Return the number of characters by which the pointer should be advanced.
+ Based on code by Branko Cibej <branko.cibej@hermes.si>
+ When this is called p points at the '%' character.
+********************************************************************/
+
+static size_t expand_env_var(char *p, int len)
+{
+ fstring envname;
+ char *envval;
+ char *q, *r;
+ int copylen;
+
+ if (p[1] != '$')
+ return 1;
+
+ if (p[2] != '(')
+ return 2;
+
+ /*
+ * Look for the terminating ')'.
+ */
+
+ if ((q = strchr_m(p,')')) == NULL) {
+ DEBUG(0,("expand_env_var: Unterminated environment variable [%s]\n", p));
+ return 2;
+ }
+
+ /*
+ * Extract the name from within the %$(NAME) string.
+ */
+
+ r = p+3;
+ copylen = MIN((q-r),(sizeof(envname)-1));
+ strncpy(envname,r,copylen);
+ envname[copylen] = '\0';
+
+ if ((envval = getenv(envname)) == NULL) {
+ DEBUG(0,("expand_env_var: Environment variable [%s] not set\n", envname));
+ return 2;
+ }
+
+ /*
+ * Copy the full %$(NAME) into envname so it
+ * can be replaced.
+ */
+
+ copylen = MIN((q+1-p),(sizeof(envname)-1));
+ strncpy(envname,p,copylen);
+ envname[copylen] = '\0';
+ string_sub(p,envname,envval,len);
+ return 0; /* Allow the environment contents to be parsed. */
+}
+
+/*******************************************************************
+ Given a pointer to a %$(NAME) in p and the whole string in str
+ expand it as an environment variable.
+ Return a new allocated and expanded string.
+ Based on code by Branko Cibej <branko.cibej@hermes.si>
+ When this is called p points at the '%' character.
+ May substitute multiple occurrencies of the same env var.
+********************************************************************/
+
+
+static char * realloc_expand_env_var(char *str, char *p)
+{
+ char *envname;
+ char *envval;
+ char *q, *r;
+ int copylen;
+
+ if (p[0] != '%' || p[1] != '$' || p[2] != '(')
+ return str;
+
+ /*
+ * Look for the terminating ')'.
+ */
+
+ if ((q = strchr_m(p,')')) == NULL) {
+ DEBUG(0,("expand_env_var: Unterminated environment variable [%s]\n", p));
+ return str;
+ }
+
+ /*
+ * Extract the name from within the %$(NAME) string.
+ */
+
+ r = p + 3;
+ copylen = q - r;
+ envname = (char *)malloc(copylen + 1 + 4); /* reserve space for use later add %$() chars */
+ if (envname == NULL) return NULL;
+ strncpy(envname,r,copylen);
+ envname[copylen] = '\0';
+
+ if ((envval = getenv(envname)) == NULL) {
+ DEBUG(0,("expand_env_var: Environment variable [%s] not set\n", envname));
+ SAFE_FREE(envname);
+ return str;
+ }
+
+ /*
+ * Copy the full %$(NAME) into envname so it
+ * can be replaced.
+ */
+
+ copylen = q + 1 - p;
+ strncpy(envname,p,copylen);
+ envname[copylen] = '\0';
+ r = realloc_string_sub(str, envname, envval);
+ SAFE_FREE(envname);
+ if (r == NULL) return NULL;
+ return r;
+}
+
+/*******************************************************************
+ Patch from jkf@soton.ac.uk
+ Added this to implement %p (NIS auto-map version of %H)
+*******************************************************************/
+
+static char *automount_path(const char *user_name)
+{
+ static pstring server_path;
+
+ /* use the passwd entry as the default */
+ /* this will be the default if WITH_AUTOMOUNT is not used or fails */
+
+ pstrcpy(server_path, get_user_home_dir(user_name));
+
+#if (defined(HAVE_NETGROUP) && defined (WITH_AUTOMOUNT))
+
+ if (lp_nis_home_map()) {
+ char *home_path_start;
+ char *automount_value = automount_lookup(user_name);
+
+ if(strlen(automount_value) > 0) {
+ home_path_start = strchr_m(automount_value,':');
+ if (home_path_start != NULL) {
+ DEBUG(5, ("NIS lookup succeeded. Home path is: %s\n",
+ home_path_start?(home_path_start+1):""));
+ pstrcpy(server_path, home_path_start+1);
+ }
+ } else {
+ /* NIS key lookup failed: default to user home directory from password file */
+ DEBUG(5, ("NIS lookup failed. Using Home path from passwd file. Home path is: %s\n", server_path ));
+ }
+ }
+#endif
+
+ DEBUG(4,("Home server path: %s\n", server_path));
+
+ return server_path;
+}
+
+/*******************************************************************
+ Patch from jkf@soton.ac.uk
+ This is Luke's original function with the NIS lookup code
+ moved out to a separate function.
+*******************************************************************/
+
+static const char *automount_server(const char *user_name)
+{
+ static pstring server_name;
+ const char *local_machine_name = get_local_machine_name();
+
+ /* use the local machine name as the default */
+ /* this will be the default if WITH_AUTOMOUNT is not used or fails */
+ if (local_machine_name && *local_machine_name)
+ pstrcpy(server_name, local_machine_name);
+ else
+ pstrcpy(server_name, global_myname());
+
+#if (defined(HAVE_NETGROUP) && defined (WITH_AUTOMOUNT))
+
+ if (lp_nis_home_map()) {
+ int home_server_len;
+ char *automount_value = automount_lookup(user_name);
+ home_server_len = strcspn(automount_value,":");
+ DEBUG(5, ("NIS lookup succeeded. Home server length: %d\n",home_server_len));
+ if (home_server_len > sizeof(pstring))
+ home_server_len = sizeof(pstring);
+ strncpy(server_name, automount_value, home_server_len);
+ server_name[home_server_len] = '\0';
+ }
+#endif
+
+ DEBUG(4,("Home server: %s\n", server_name));
+
+ return server_name;
+}
+
+/****************************************************************************
+ Do some standard substitutions in a string.
+ len is the length in bytes of the space allowed in string str. If zero means
+ don't allow expansions.
+****************************************************************************/
+
+void standard_sub_basic(const char *smb_name, char *str,size_t len)
+{
+ char *p, *s;
+ fstring pidstr;
+ struct passwd *pass;
+ const char *local_machine_name = get_local_machine_name();
+
+ for (s=str; (p=strchr_m(s, '%'));s=p) {
+ fstring tmp_str;
+
+ int l = (int)len - (int)(p-str);
+
+ if (l < 0)
+ l = 0;
+
+ switch (*(p+1)) {
+ case 'U' :
+ fstrcpy(tmp_str, smb_name);
+ strlower_m(tmp_str);
+ string_sub(p,"%U",tmp_str,l);
+ break;
+ case 'G' :
+ fstrcpy(tmp_str, smb_name);
+ if ((pass = Get_Pwnam(tmp_str))!=NULL) {
+ string_sub(p,"%G",gidtoname(pass->pw_gid),l);
+ } else {
+ p += 2;
+ }
+ break;
+ case 'D' :
+ fstrcpy(tmp_str, current_user_info.domain);
+ strupper_m(tmp_str);
+ string_sub(p,"%D", tmp_str,l);
+ break;
+ case 'I' :
+ string_sub(p,"%I", client_addr(),l);
+ break;
+ case 'i' :
+ string_sub(p,"%i", client_socket_addr(),l);
+ break;
+ case 'L' :
+ if (local_machine_name && *local_machine_name)
+ string_sub(p,"%L", local_machine_name,l);
+ else {
+ pstring temp_name;
+
+ pstrcpy(temp_name, global_myname());
+ strlower_m(temp_name);
+ string_sub(p,"%L", temp_name,l);
+ }
+ break;
+ case 'M' :
+ string_sub(p,"%M", client_name(),l);
+ break;
+ case 'R' :
+ string_sub(p,"%R", remote_proto,l);
+ break;
+ case 'T' :
+ string_sub(p,"%T", timestring(False),l);
+ break;
+ case 'a' :
+ string_sub(p,"%a", remote_arch,l);
+ break;
+ case 'd' :
+ slprintf(pidstr,sizeof(pidstr)-1, "%d",(int)sys_getpid());
+ string_sub(p,"%d", pidstr,l);
+ break;
+ case 'h' :
+ string_sub(p,"%h", myhostname(),l);
+ break;
+ case 'm' :
+ string_sub(p,"%m", get_remote_machine_name(),l);
+ break;
+ case 'v' :
+ string_sub(p,"%v", SAMBA_VERSION_STRING,l);
+ break;
+ case '$' :
+ p += expand_env_var(p,l);
+ break; /* Expand environment variables */
+ case '\0':
+ p++;
+ break; /* don't run off the end of the string */
+
+ default: p+=2;
+ break;
+ }
+ }
+}
+
+static void standard_sub_advanced(int snum, const char *user,
+ const char *connectpath, gid_t gid,
+ const char *smb_name, char *str, size_t len)
+{
+ char *p, *s, *home;
+
+ for (s=str; (p=strchr_m(s, '%'));s=p) {
+ int l = (int)len - (int)(p-str);
+
+ if (l < 0)
+ l = 0;
+
+ switch (*(p+1)) {
+ case 'N' :
+ string_sub(p,"%N", automount_server(user),l);
+ break;
+ case 'H':
+ if ((home = get_user_home_dir(user)))
+ string_sub(p,"%H",home, l);
+ else
+ p += 2;
+ break;
+ case 'P':
+ string_sub(p,"%P", connectpath, l);
+ break;
+ case 'S':
+ string_sub(p,"%S", lp_servicename(snum), l);
+ break;
+ case 'g':
+ string_sub(p,"%g", gidtoname(gid), l);
+ break;
+ case 'u':
+ string_sub(p,"%u", user, l);
+ break;
+
+ /* Patch from jkf@soton.ac.uk Left the %N (NIS
+ * server name) in standard_sub_basic as it is
+ * a feature for logon servers, hence uses the
+ * username. The %p (NIS server path) code is
+ * here as it is used instead of the default
+ * "path =" string in [homes] and so needs the
+ * service name, not the username. */
+ case 'p':
+ string_sub(p,"%p", automount_path(lp_servicename(snum)), l);
+ break;
+ case '\0':
+ p++;
+ break; /* don't run off the end of the string */
+
+ default: p+=2;
+ break;
+ }
+ }
+
+ standard_sub_basic(smb_name, str, len);
+}
+
+/****************************************************************************
+ Do some standard substitutions in a string.
+ This function will return an allocated string that have to be freed.
+****************************************************************************/
+
+char *talloc_sub_basic(TALLOC_CTX *mem_ctx, const char *smb_name, const char *str)
+{
+ char *a, *t;
+ a = alloc_sub_basic(smb_name, str);
+ if (!a) return NULL;
+ t = talloc_strdup(mem_ctx, a);
+ SAFE_FREE(a);
+ return t;
+}
+
+char *alloc_sub_basic(const char *smb_name, const char *str)
+{
+ char *b, *p, *s, *t, *r, *a_string;
+ fstring pidstr;
+ struct passwd *pass;
+ const char *local_machine_name = get_local_machine_name();
+
+ /* workaround to prevent a crash while lookinf at bug #687 */
+
+ if ( !str ) {
+ DEBUG(0,("alloc_sub_basic: NULL source string! This should not happen\n"));
+ return NULL;
+ }
+
+ a_string = strdup(str);
+ if (a_string == NULL) {
+ DEBUG(0, ("alloc_sub_specified: Out of memory!\n"));
+ return NULL;
+ }
+
+ for (b = s = a_string; (p = strchr_m(s, '%')); s = a_string + (p - b)) {
+
+ r = NULL;
+ b = t = a_string;
+
+ switch (*(p+1)) {
+ case 'U' :
+ r = strdup_lower(smb_name);
+ if (r == NULL) goto error;
+ t = realloc_string_sub(t, "%U", r);
+ break;
+ case 'G' :
+ r = strdup(smb_name);
+ if (r == NULL) goto error;
+ if ((pass = Get_Pwnam(r))!=NULL) {
+ t = realloc_string_sub(t, "%G", gidtoname(pass->pw_gid));
+ }
+ break;
+ case 'D' :
+ r = strdup_upper(current_user_info.domain);
+ if (r == NULL) goto error;
+ t = realloc_string_sub(t, "%D", r);
+ break;
+ case 'I' :
+ t = realloc_string_sub(t, "%I", client_addr());
+ break;
+ case 'L' :
+ if (local_machine_name && *local_machine_name)
+ t = realloc_string_sub(t, "%L", local_machine_name);
+ else
+ t = realloc_string_sub(t, "%L", global_myname());
+ break;
+ case 'N':
+ t = realloc_string_sub(t, "%N", automount_server(smb_name));
+ break;
+ case 'M' :
+ t = realloc_string_sub(t, "%M", client_name());
+ break;
+ case 'R' :
+ t = realloc_string_sub(t, "%R", remote_proto);
+ break;
+ case 'T' :
+ t = realloc_string_sub(t, "%T", timestring(False));
+ break;
+ case 'a' :
+ t = realloc_string_sub(t, "%a", remote_arch);
+ break;
+ case 'd' :
+ slprintf(pidstr,sizeof(pidstr)-1, "%d",(int)sys_getpid());
+ t = realloc_string_sub(t, "%d", pidstr);
+ break;
+ case 'h' :
+ t = realloc_string_sub(t, "%h", myhostname());
+ break;
+ case 'm' :
+ t = realloc_string_sub(t, "%m", remote_machine);
+ break;
+ case 'v' :
+ t = realloc_string_sub(t, "%v", SAMBA_VERSION_STRING);
+ break;
+ case '$' :
+ t = realloc_expand_env_var(t, p); /* Expand environment variables */
+ break;
+
+ default:
+ break;
+ }
+
+ p++;
+ SAFE_FREE(r);
+ if (t == NULL) goto error;
+ a_string = t;
+ }
+
+ return a_string;
+error:
+ SAFE_FREE(a_string);
+ return NULL;
+}
+
+/****************************************************************************
+ Do some specific substitutions in a string.
+ This function will return an allocated string that have to be freed.
+****************************************************************************/
+
+char *talloc_sub_specified(TALLOC_CTX *mem_ctx,
+ const char *input_string,
+ const char *username,
+ const char *domain,
+ uid_t uid,
+ gid_t gid)
+{
+ char *a, *t;
+ a = alloc_sub_specified(input_string, username, domain, uid, gid);
+ if (!a) return NULL;
+ t = talloc_strdup(mem_ctx, a);
+ SAFE_FREE(a);
+ return t;
+}
+
+char *alloc_sub_specified(const char *input_string,
+ const char *username,
+ const char *domain,
+ uid_t uid,
+ gid_t gid)
+{
+ char *a_string, *ret_string;
+ char *b, *p, *s, *t;
+
+ a_string = strdup(input_string);
+ if (a_string == NULL) {
+ DEBUG(0, ("alloc_sub_specified: Out of memory!\n"));
+ return NULL;
+ }
+
+ for (b = s = a_string; (p = strchr_m(s, '%')); s = a_string + (p - b)) {
+
+ b = t = a_string;
+
+ switch (*(p+1)) {
+ case 'U' :
+ t = realloc_string_sub(t, "%U", username);
+ break;
+ case 'u' :
+ t = realloc_string_sub(t, "%u", username);
+ break;
+ case 'G' :
+ if (gid != -1) {
+ t = realloc_string_sub(t, "%G", gidtoname(gid));
+ } else {
+ t = realloc_string_sub(t, "%G", "NO_GROUP");
+ }
+ break;
+ case 'g' :
+ if (gid != -1) {
+ t = realloc_string_sub(t, "%g", gidtoname(gid));
+ } else {
+ t = realloc_string_sub(t, "%g", "NO_GROUP");
+ }
+ break;
+ case 'D' :
+ t = realloc_string_sub(t, "%D", domain);
+ break;
+ case 'N' :
+ t = realloc_string_sub(t, "%N", automount_server(username));
+ break;
+ default:
+ break;
+ }
+
+ p++;
+ if (t == NULL) {
+ SAFE_FREE(a_string);
+ return NULL;
+ }
+ a_string = t;
+ }
+
+ ret_string = alloc_sub_basic(username, a_string);
+ SAFE_FREE(a_string);
+ return ret_string;
+}
+
+char *talloc_sub_advanced(TALLOC_CTX *mem_ctx,
+ int snum,
+ const char *user,
+ const char *connectpath,
+ gid_t gid,
+ const char *smb_name,
+ const char *str)
+{
+ char *a, *t;
+ a = alloc_sub_advanced(snum, user, connectpath, gid, smb_name, str);
+ if (!a) return NULL;
+ t = talloc_strdup(mem_ctx, a);
+ SAFE_FREE(a);
+ return t;
+}
+
+char *alloc_sub_advanced(int snum, const char *user,
+ const char *connectpath, gid_t gid,
+ const char *smb_name, const char *str)
+{
+ char *a_string, *ret_string;
+ char *b, *p, *s, *t, *h;
+
+ a_string = strdup(str);
+ if (a_string == NULL) {
+ DEBUG(0, ("alloc_sub_specified: Out of memory!\n"));
+ return NULL;
+ }
+
+ for (b = s = a_string; (p = strchr_m(s, '%')); s = a_string + (p - b)) {
+
+ b = t = a_string;
+
+ switch (*(p+1)) {
+ case 'N' :
+ t = realloc_string_sub(t, "%N", automount_server(user));
+ break;
+ case 'H':
+ if ((h = get_user_home_dir(user)))
+ t = realloc_string_sub(t, "%H", h);
+ break;
+ case 'P':
+ t = realloc_string_sub(t, "%P", connectpath);
+ break;
+ case 'S':
+ t = realloc_string_sub(t, "%S", lp_servicename(snum));
+ break;
+ case 'g':
+ t = realloc_string_sub(t, "%g", gidtoname(gid));
+ break;
+ case 'u':
+ t = realloc_string_sub(t, "%u", user);
+ break;
+
+ /* Patch from jkf@soton.ac.uk Left the %N (NIS
+ * server name) in standard_sub_basic as it is
+ * a feature for logon servers, hence uses the
+ * username. The %p (NIS server path) code is
+ * here as it is used instead of the default
+ * "path =" string in [homes] and so needs the
+ * service name, not the username. */
+ case 'p':
+ t = realloc_string_sub(t, "%p", automount_path(lp_servicename(snum)));
+ break;
+
+ default:
+ break;
+ }
+
+ p++;
+ if (t == NULL) {
+ SAFE_FREE(a_string);
+ return NULL;
+ }
+ a_string = t;
+ }
+
+ ret_string = alloc_sub_basic(smb_name, a_string);
+ SAFE_FREE(a_string);
+ return ret_string;
+}
+
+/****************************************************************************
+ Do some standard substitutions in a string.
+****************************************************************************/
+
+void standard_sub_conn(connection_struct *conn, char *str, size_t len)
+{
+ standard_sub_advanced(SNUM(conn), conn->user, conn->connectpath,
+ conn->gid, smb_user_name, str, len);
+}
+
+char *talloc_sub_conn(TALLOC_CTX *mem_ctx, connection_struct *conn, const char *str)
+{
+ return talloc_sub_advanced(mem_ctx, SNUM(conn), conn->user,
+ conn->connectpath, conn->gid,
+ smb_user_name, str);
+}
+
+char *alloc_sub_conn(connection_struct *conn, const char *str)
+{
+ return alloc_sub_advanced(SNUM(conn), conn->user, conn->connectpath,
+ conn->gid, smb_user_name, str);
+}
+
+/****************************************************************************
+ Like standard_sub but by snum.
+****************************************************************************/
+
+void standard_sub_snum(int snum, char *str, size_t len)
+{
+ extern struct current_user current_user;
+ static uid_t cached_uid = -1;
+ static fstring cached_user;
+ /* calling uidtoname() on every substitute would be too expensive, so
+ we cache the result here as nearly every call is for the same uid */
+
+ if (cached_uid != current_user.uid) {
+ fstrcpy(cached_user, uidtoname(current_user.uid));
+ cached_uid = current_user.uid;
+ }
+
+ standard_sub_advanced(snum, cached_user, "", -1,
+ smb_user_name, str, len);
+}
diff --git a/source/lib/sysacls.c b/source/lib/sysacls.c
new file mode 100644
index 00000000000..00d06e4a5ae
--- /dev/null
+++ b/source/lib/sysacls.c
@@ -0,0 +1,3198 @@
+/*
+ Unix SMB/CIFS implementation.
+ Samba system utilities for ACL support.
+ Copyright (C) Jeremy Allison 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 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"
+
+/*
+ This file wraps all differing system ACL interfaces into a consistent
+ one based on the POSIX interface. It also returns the correct errors
+ for older UNIX systems that don't support ACLs.
+
+ The interfaces that each ACL implementation must support are as follows :
+
+ int sys_acl_get_entry( SMB_ACL_T theacl, int entry_id, SMB_ACL_ENTRY_T *entry_p)
+ int sys_acl_get_tag_type( SMB_ACL_ENTRY_T entry_d, SMB_ACL_TAG_T *tag_type_p)
+ int sys_acl_get_permset( SMB_ACL_ENTRY_T entry_d, SMB_ACL_PERMSET_T *permset_p
+ void *sys_acl_get_qualifier( SMB_ACL_ENTRY_T entry_d)
+ SMB_ACL_T sys_acl_get_file( const char *path_p, SMB_ACL_TYPE_T type)
+ SMB_ACL_T sys_acl_get_fd(int fd)
+ int sys_acl_clear_perms(SMB_ACL_PERMSET_T permset);
+ int sys_acl_add_perm( SMB_ACL_PERMSET_T permset, SMB_ACL_PERM_T perm);
+ char *sys_acl_to_text( SMB_ACL_T theacl, ssize_t *plen)
+ SMB_ACL_T sys_acl_init( int count)
+ int sys_acl_create_entry( SMB_ACL_T *pacl, SMB_ACL_ENTRY_T *pentry)
+ int sys_acl_set_tag_type( SMB_ACL_ENTRY_T entry, SMB_ACL_TAG_T tagtype)
+ int sys_acl_set_qualifier( SMB_ACL_ENTRY_T entry, void *qual)
+ int sys_acl_set_permset( SMB_ACL_ENTRY_T entry, SMB_ACL_PERMSET_T permset)
+ int sys_acl_valid( SMB_ACL_T theacl )
+ int sys_acl_set_file( const char *name, SMB_ACL_TYPE_T acltype, SMB_ACL_T theacl)
+ int sys_acl_set_fd( int fd, SMB_ACL_T theacl)
+ int sys_acl_delete_def_file(const char *path)
+
+ This next one is not POSIX complient - but we *have* to have it !
+ More POSIX braindamage.
+
+ int sys_acl_get_perm( SMB_ACL_PERMSET_T permset, SMB_ACL_PERM_T perm)
+
+ The generic POSIX free is the following call. We split this into
+ several different free functions as we may need to add tag info
+ to structures when emulating the POSIX interface.
+
+ int sys_acl_free( void *obj_p)
+
+ The calls we actually use are :
+
+ int sys_acl_free_text(char *text) - free acl_to_text
+ int sys_acl_free_acl(SMB_ACL_T posix_acl)
+ int sys_acl_free_qualifier(void *qualifier, SMB_ACL_TAG_T tagtype)
+
+*/
+
+#if defined(HAVE_POSIX_ACLS)
+
+/* Identity mapping - easy. */
+
+int sys_acl_get_entry( SMB_ACL_T the_acl, int entry_id, SMB_ACL_ENTRY_T *entry_p)
+{
+ return acl_get_entry( the_acl, entry_id, entry_p);
+}
+
+int sys_acl_get_tag_type( SMB_ACL_ENTRY_T entry_d, SMB_ACL_TAG_T *tag_type_p)
+{
+ return acl_get_tag_type( entry_d, tag_type_p);
+}
+
+int sys_acl_get_permset( SMB_ACL_ENTRY_T entry_d, SMB_ACL_PERMSET_T *permset_p)
+{
+ return acl_get_permset( entry_d, permset_p);
+}
+
+void *sys_acl_get_qualifier( SMB_ACL_ENTRY_T entry_d)
+{
+ return acl_get_qualifier( entry_d);
+}
+
+SMB_ACL_T sys_acl_get_file( const char *path_p, SMB_ACL_TYPE_T type)
+{
+ return acl_get_file( path_p, type);
+}
+
+SMB_ACL_T sys_acl_get_fd(int fd)
+{
+ return acl_get_fd(fd);
+}
+
+int sys_acl_clear_perms(SMB_ACL_PERMSET_T permset)
+{
+ return acl_clear_perms(permset);
+}
+
+int sys_acl_add_perm( SMB_ACL_PERMSET_T permset, SMB_ACL_PERM_T perm)
+{
+ return acl_add_perm(permset, perm);
+}
+
+int sys_acl_get_perm( SMB_ACL_PERMSET_T permset, SMB_ACL_PERM_T perm)
+{
+#if defined(HAVE_ACL_GET_PERM_NP)
+ /*
+ * Required for TrustedBSD-based ACL implementations where
+ * non-POSIX.1e functions are denoted by a _np (non-portable)
+ * suffix.
+ */
+ return acl_get_perm_np(permset, perm);
+#else
+ return acl_get_perm(permset, perm);
+#endif
+}
+
+char *sys_acl_to_text( SMB_ACL_T the_acl, ssize_t *plen)
+{
+ return acl_to_text( the_acl, plen);
+}
+
+SMB_ACL_T sys_acl_init( int count)
+{
+ return acl_init(count);
+}
+
+int sys_acl_create_entry( SMB_ACL_T *pacl, SMB_ACL_ENTRY_T *pentry)
+{
+ return acl_create_entry(pacl, pentry);
+}
+
+int sys_acl_set_tag_type( SMB_ACL_ENTRY_T entry, SMB_ACL_TAG_T tagtype)
+{
+ return acl_set_tag_type(entry, tagtype);
+}
+
+int sys_acl_set_qualifier( SMB_ACL_ENTRY_T entry, void *qual)
+{
+ return acl_set_qualifier(entry, qual);
+}
+
+int sys_acl_set_permset( SMB_ACL_ENTRY_T entry, SMB_ACL_PERMSET_T permset)
+{
+ return acl_set_permset(entry, permset);
+}
+
+int sys_acl_valid( SMB_ACL_T theacl )
+{
+ return acl_valid(theacl);
+}
+
+int sys_acl_set_file(const char *name, SMB_ACL_TYPE_T acltype, SMB_ACL_T theacl)
+{
+ return acl_set_file(name, acltype, theacl);
+}
+
+int sys_acl_set_fd( int fd, SMB_ACL_T theacl)
+{
+ return acl_set_fd(fd, theacl);
+}
+
+int sys_acl_delete_def_file(const char *name)
+{
+ return acl_delete_def_file(name);
+}
+
+int sys_acl_free_text(char *text)
+{
+ return acl_free(text);
+}
+
+int sys_acl_free_acl(SMB_ACL_T the_acl)
+{
+ return acl_free(the_acl);
+}
+
+int sys_acl_free_qualifier(void *qual, SMB_ACL_TAG_T tagtype)
+{
+ return acl_free(qual);
+}
+
+#elif defined(HAVE_TRU64_ACLS)
+/*
+ * The interface to DEC/Compaq Tru64 UNIX ACLs
+ * is based on Draft 13 of the POSIX spec which is
+ * slightly different from the Draft 16 interface.
+ *
+ * Also, some of the permset manipulation functions
+ * such as acl_clear_perm() and acl_add_perm() appear
+ * to be broken on Tru64 so we have to manipulate
+ * the permission bits in the permset directly.
+ */
+int sys_acl_get_entry( SMB_ACL_T the_acl, int entry_id, SMB_ACL_ENTRY_T *entry_p)
+{
+ SMB_ACL_ENTRY_T entry;
+
+ if (entry_id == SMB_ACL_FIRST_ENTRY && acl_first_entry(the_acl) != 0) {
+ return -1;
+ }
+
+ errno = 0;
+ if ((entry = acl_get_entry(the_acl)) != NULL) {
+ *entry_p = entry;
+ return 1;
+ }
+
+ return errno ? -1 : 0;
+}
+
+int sys_acl_get_tag_type( SMB_ACL_ENTRY_T entry_d, SMB_ACL_TAG_T *tag_type_p)
+{
+ return acl_get_tag_type( entry_d, tag_type_p);
+}
+
+int sys_acl_get_permset( SMB_ACL_ENTRY_T entry_d, SMB_ACL_PERMSET_T *permset_p)
+{
+ return acl_get_permset( entry_d, permset_p);
+}
+
+void *sys_acl_get_qualifier( SMB_ACL_ENTRY_T entry_d)
+{
+ return acl_get_qualifier( entry_d);
+}
+
+SMB_ACL_T sys_acl_get_file( const char *path_p, SMB_ACL_TYPE_T type)
+{
+ return acl_get_file((char *)path_p, type);
+}
+
+SMB_ACL_T sys_acl_get_fd(int fd)
+{
+ return acl_get_fd(fd, ACL_TYPE_ACCESS);
+}
+
+int sys_acl_clear_perms(SMB_ACL_PERMSET_T permset)
+{
+ *permset = 0; /* acl_clear_perm() is broken on Tru64 */
+
+ return 0;
+}
+
+int sys_acl_add_perm( SMB_ACL_PERMSET_T permset, SMB_ACL_PERM_T perm)
+{
+ if (perm & ~(SMB_ACL_READ | SMB_ACL_WRITE | SMB_ACL_EXECUTE)) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ *permset |= perm; /* acl_add_perm() is broken on Tru64 */
+
+ return 0;
+}
+
+int sys_acl_get_perm( SMB_ACL_PERMSET_T permset, SMB_ACL_PERM_T perm)
+{
+ return *permset & perm; /* Tru64 doesn't have acl_get_perm() */
+}
+
+char *sys_acl_to_text( SMB_ACL_T the_acl, ssize_t *plen)
+{
+ return acl_to_text( the_acl, plen);
+}
+
+SMB_ACL_T sys_acl_init( int count)
+{
+ return acl_init(count);
+}
+
+int sys_acl_create_entry( SMB_ACL_T *pacl, SMB_ACL_ENTRY_T *pentry)
+{
+ SMB_ACL_ENTRY_T entry;
+
+ if ((entry = acl_create_entry(pacl)) == NULL) {
+ return -1;
+ }
+
+ *pentry = entry;
+ return 0;
+}
+
+int sys_acl_set_tag_type( SMB_ACL_ENTRY_T entry, SMB_ACL_TAG_T tagtype)
+{
+ return acl_set_tag_type(entry, tagtype);
+}
+
+int sys_acl_set_qualifier( SMB_ACL_ENTRY_T entry, void *qual)
+{
+ return acl_set_qualifier(entry, qual);
+}
+
+int sys_acl_set_permset( SMB_ACL_ENTRY_T entry, SMB_ACL_PERMSET_T permset)
+{
+ return acl_set_permset(entry, permset);
+}
+
+int sys_acl_valid( SMB_ACL_T theacl )
+{
+ acl_entry_t entry;
+
+ return acl_valid(theacl, &entry);
+}
+
+int sys_acl_set_file( const char *name, SMB_ACL_TYPE_T acltype, SMB_ACL_T theacl)
+{
+ return acl_set_file((char *)name, acltype, theacl);
+}
+
+int sys_acl_set_fd( int fd, SMB_ACL_T theacl)
+{
+ return acl_set_fd(fd, ACL_TYPE_ACCESS, theacl);
+}
+
+int sys_acl_delete_def_file(const char *name)
+{
+ return acl_delete_def_file((char *)name);
+}
+
+int sys_acl_free_text(char *text)
+{
+ /*
+ * (void) cast and explicit return 0 are for DEC UNIX
+ * which just #defines acl_free_text() to be free()
+ */
+ (void) acl_free_text(text);
+ return 0;
+}
+
+int sys_acl_free_acl(SMB_ACL_T the_acl)
+{
+ return acl_free(the_acl);
+}
+
+int sys_acl_free_qualifier(void *qual, SMB_ACL_TAG_T tagtype)
+{
+ return acl_free_qualifier(qual, tagtype);
+}
+
+#elif defined(HAVE_UNIXWARE_ACLS) || defined(HAVE_SOLARIS_ACLS)
+
+/*
+ * Donated by Michael Davidson <md@sco.COM> for UnixWare / OpenUNIX.
+ * Modified by Toomas Soome <tsoome@ut.ee> for Solaris.
+ */
+
+/*
+ * Note that while this code implements sufficient functionality
+ * to support the sys_acl_* interfaces it does not provide all
+ * of the semantics of the POSIX ACL interfaces.
+ *
+ * In particular, an ACL entry descriptor (SMB_ACL_ENTRY_T) returned
+ * from a call to sys_acl_get_entry() should not be assumed to be
+ * valid after calling any of the following functions, which may
+ * reorder the entries in the ACL.
+ *
+ * sys_acl_valid()
+ * sys_acl_set_file()
+ * sys_acl_set_fd()
+ */
+
+/*
+ * The only difference between Solaris and UnixWare / OpenUNIX is
+ * that the #defines for the ACL operations have different names
+ */
+#if defined(HAVE_UNIXWARE_ACLS)
+
+#define SETACL ACL_SET
+#define GETACL ACL_GET
+#define GETACLCNT ACL_CNT
+
+#endif
+
+
+int sys_acl_get_entry(SMB_ACL_T acl_d, int entry_id, SMB_ACL_ENTRY_T *entry_p)
+{
+ if (entry_id != SMB_ACL_FIRST_ENTRY && entry_id != SMB_ACL_NEXT_ENTRY) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ if (entry_p == NULL) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ if (entry_id == SMB_ACL_FIRST_ENTRY) {
+ acl_d->next = 0;
+ }
+
+ if (acl_d->next < 0) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ if (acl_d->next >= acl_d->count) {
+ return 0;
+ }
+
+ *entry_p = &acl_d->acl[acl_d->next++];
+
+ return 1;
+}
+
+int sys_acl_get_tag_type(SMB_ACL_ENTRY_T entry_d, SMB_ACL_TAG_T *type_p)
+{
+ *type_p = entry_d->a_type;
+
+ return 0;
+}
+
+int sys_acl_get_permset(SMB_ACL_ENTRY_T entry_d, SMB_ACL_PERMSET_T *permset_p)
+{
+ *permset_p = &entry_d->a_perm;
+
+ return 0;
+}
+
+void *sys_acl_get_qualifier(SMB_ACL_ENTRY_T entry_d)
+{
+ if (entry_d->a_type != SMB_ACL_USER
+ && entry_d->a_type != SMB_ACL_GROUP) {
+ errno = EINVAL;
+ return NULL;
+ }
+
+ return &entry_d->a_id;
+}
+
+/*
+ * There is no way of knowing what size the ACL returned by
+ * GETACL will be unless you first call GETACLCNT which means
+ * making an additional system call.
+ *
+ * In the hope of avoiding the cost of the additional system
+ * call in most cases, we initially allocate enough space for
+ * an ACL with INITIAL_ACL_SIZE entries. If this turns out to
+ * be too small then we use GETACLCNT to find out the actual
+ * size, reallocate the ACL buffer, and then call GETACL again.
+ */
+
+#define INITIAL_ACL_SIZE 16
+
+SMB_ACL_T sys_acl_get_file(const char *path_p, SMB_ACL_TYPE_T type)
+{
+ SMB_ACL_T acl_d;
+ int count; /* # of ACL entries allocated */
+ int naccess; /* # of access ACL entries */
+ int ndefault; /* # of default ACL entries */
+
+ if (type != SMB_ACL_TYPE_ACCESS && type != SMB_ACL_TYPE_DEFAULT) {
+ errno = EINVAL;
+ return NULL;
+ }
+
+ count = INITIAL_ACL_SIZE;
+ if ((acl_d = sys_acl_init(count)) == NULL) {
+ return NULL;
+ }
+
+ /*
+ * If there isn't enough space for the ACL entries we use
+ * GETACLCNT to determine the actual number of ACL entries
+ * reallocate and try again. This is in a loop because it
+ * is possible that someone else could modify the ACL and
+ * increase the number of entries between the call to
+ * GETACLCNT and the call to GETACL.
+ */
+ while ((count = acl(path_p, GETACL, count, &acl_d->acl[0])) < 0
+ && errno == ENOSPC) {
+
+ sys_acl_free_acl(acl_d);
+
+ if ((count = acl(path_p, GETACLCNT, 0, NULL)) < 0) {
+ return NULL;
+ }
+
+ if ((acl_d = sys_acl_init(count)) == NULL) {
+ return NULL;
+ }
+ }
+
+ if (count < 0) {
+ sys_acl_free_acl(acl_d);
+ return NULL;
+ }
+
+ /*
+ * calculate the number of access and default ACL entries
+ *
+ * Note: we assume that the acl() system call returned a
+ * well formed ACL which is sorted so that all of the
+ * access ACL entries preceed any default ACL entries
+ */
+ for (naccess = 0; naccess < count; naccess++) {
+ if (acl_d->acl[naccess].a_type & ACL_DEFAULT)
+ break;
+ }
+ ndefault = count - naccess;
+
+ /*
+ * if the caller wants the default ACL we have to copy
+ * the entries down to the start of the acl[] buffer
+ * and mask out the ACL_DEFAULT flag from the type field
+ */
+ if (type == SMB_ACL_TYPE_DEFAULT) {
+ int i, j;
+
+ for (i = 0, j = naccess; i < ndefault; i++, j++) {
+ acl_d->acl[i] = acl_d->acl[j];
+ acl_d->acl[i].a_type &= ~ACL_DEFAULT;
+ }
+
+ acl_d->count = ndefault;
+ } else {
+ acl_d->count = naccess;
+ }
+
+ return acl_d;
+}
+
+SMB_ACL_T sys_acl_get_fd(int fd)
+{
+ SMB_ACL_T acl_d;
+ int count; /* # of ACL entries allocated */
+ int naccess; /* # of access ACL entries */
+
+ count = INITIAL_ACL_SIZE;
+ if ((acl_d = sys_acl_init(count)) == NULL) {
+ return NULL;
+ }
+
+ while ((count = facl(fd, GETACL, count, &acl_d->acl[0])) < 0
+ && errno == ENOSPC) {
+
+ sys_acl_free_acl(acl_d);
+
+ if ((count = facl(fd, GETACLCNT, 0, NULL)) < 0) {
+ return NULL;
+ }
+
+ if ((acl_d = sys_acl_init(count)) == NULL) {
+ return NULL;
+ }
+ }
+
+ if (count < 0) {
+ sys_acl_free_acl(acl_d);
+ return NULL;
+ }
+
+ /*
+ * calculate the number of access ACL entries
+ */
+ for (naccess = 0; naccess < count; naccess++) {
+ if (acl_d->acl[naccess].a_type & ACL_DEFAULT)
+ break;
+ }
+
+ acl_d->count = naccess;
+
+ return acl_d;
+}
+
+int sys_acl_clear_perms(SMB_ACL_PERMSET_T permset_d)
+{
+ *permset_d = 0;
+
+ return 0;
+}
+
+int sys_acl_add_perm(SMB_ACL_PERMSET_T permset_d, SMB_ACL_PERM_T perm)
+{
+ if (perm != SMB_ACL_READ && perm != SMB_ACL_WRITE
+ && perm != SMB_ACL_EXECUTE) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ if (permset_d == NULL) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ *permset_d |= perm;
+
+ return 0;
+}
+
+int sys_acl_get_perm(SMB_ACL_PERMSET_T permset_d, SMB_ACL_PERM_T perm)
+{
+ return *permset_d & perm;
+}
+
+char *sys_acl_to_text(SMB_ACL_T acl_d, ssize_t *len_p)
+{
+ int i;
+ int len, maxlen;
+ char *text;
+
+ /*
+ * use an initial estimate of 20 bytes per ACL entry
+ * when allocating memory for the text representation
+ * of the ACL
+ */
+ len = 0;
+ maxlen = 20 * acl_d->count;
+ if ((text = malloc(maxlen)) == NULL) {
+ errno = ENOMEM;
+ return NULL;
+ }
+
+ for (i = 0; i < acl_d->count; i++) {
+ struct acl *ap = &acl_d->acl[i];
+ struct passwd *pw;
+ struct group *gr;
+ char tagbuf[12];
+ char idbuf[12];
+ char *tag;
+ char *id = "";
+ char perms[4];
+ int nbytes;
+
+ switch (ap->a_type) {
+ /*
+ * for debugging purposes it's probably more
+ * useful to dump unknown tag types rather
+ * than just returning an error
+ */
+ default:
+ slprintf(tagbuf, sizeof(tagbuf)-1, "0x%x",
+ ap->a_type);
+ tag = tagbuf;
+ slprintf(idbuf, sizeof(idbuf)-1, "%ld",
+ (long)ap->a_id);
+ id = idbuf;
+ break;
+
+ case SMB_ACL_USER:
+ id = uidtoname(ap->a_id);
+ case SMB_ACL_USER_OBJ:
+ tag = "user";
+ break;
+
+ case SMB_ACL_GROUP:
+ if ((gr = getgrgid(ap->a_id)) == NULL) {
+ slprintf(idbuf, sizeof(idbuf)-1, "%ld",
+ (long)ap->a_id);
+ id = idbuf;
+ } else {
+ id = gr->gr_name;
+ }
+ case SMB_ACL_GROUP_OBJ:
+ tag = "group";
+ break;
+
+ case SMB_ACL_OTHER:
+ tag = "other";
+ break;
+
+ case SMB_ACL_MASK:
+ tag = "mask";
+ break;
+
+ }
+
+ perms[0] = (ap->a_perm & SMB_ACL_READ) ? 'r' : '-';
+ perms[1] = (ap->a_perm & SMB_ACL_WRITE) ? 'w' : '-';
+ perms[2] = (ap->a_perm & SMB_ACL_EXECUTE) ? 'x' : '-';
+ perms[3] = '\0';
+
+ /* <tag> : <qualifier> : rwx \n \0 */
+ nbytes = strlen(tag) + 1 + strlen(id) + 1 + 3 + 1 + 1;
+
+ /*
+ * If this entry would overflow the buffer
+ * allocate enough additional memory for this
+ * entry and an estimate of another 20 bytes
+ * for each entry still to be processed
+ */
+ if ((len + nbytes) > maxlen) {
+ char *oldtext = text;
+
+ maxlen += nbytes + 20 * (acl_d->count - i);
+
+ if ((text = Realloc(oldtext, maxlen)) == NULL) {
+ SAFE_FREE(oldtext);
+ errno = ENOMEM;
+ return NULL;
+ }
+ }
+
+ slprintf(&text[len], nbytes-1, "%s:%s:%s\n", tag, id, perms);
+ len += nbytes - 1;
+ }
+
+ if (len_p)
+ *len_p = len;
+
+ return text;
+}
+
+SMB_ACL_T sys_acl_init(int count)
+{
+ SMB_ACL_T a;
+
+ if (count < 0) {
+ errno = EINVAL;
+ return NULL;
+ }
+
+ /*
+ * note that since the definition of the structure pointed
+ * to by the SMB_ACL_T includes the first element of the
+ * acl[] array, this actually allocates an ACL with room
+ * for (count+1) entries
+ */
+ if ((a = malloc(sizeof(*a) + count * sizeof(struct acl))) == NULL) {
+ errno = ENOMEM;
+ return NULL;
+ }
+
+ a->size = count + 1;
+ a->count = 0;
+ a->next = -1;
+
+ return a;
+}
+
+
+int sys_acl_create_entry(SMB_ACL_T *acl_p, SMB_ACL_ENTRY_T *entry_p)
+{
+ SMB_ACL_T acl_d;
+ SMB_ACL_ENTRY_T entry_d;
+
+ if (acl_p == NULL || entry_p == NULL || (acl_d = *acl_p) == NULL) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ if (acl_d->count >= acl_d->size) {
+ errno = ENOSPC;
+ return -1;
+ }
+
+ entry_d = &acl_d->acl[acl_d->count++];
+ entry_d->a_type = 0;
+ entry_d->a_id = -1;
+ entry_d->a_perm = 0;
+ *entry_p = entry_d;
+
+ return 0;
+}
+
+int sys_acl_set_tag_type(SMB_ACL_ENTRY_T entry_d, SMB_ACL_TAG_T tag_type)
+{
+ switch (tag_type) {
+ case SMB_ACL_USER:
+ case SMB_ACL_USER_OBJ:
+ case SMB_ACL_GROUP:
+ case SMB_ACL_GROUP_OBJ:
+ case SMB_ACL_OTHER:
+ case SMB_ACL_MASK:
+ entry_d->a_type = tag_type;
+ break;
+ default:
+ errno = EINVAL;
+ return -1;
+ }
+
+ return 0;
+}
+
+int sys_acl_set_qualifier(SMB_ACL_ENTRY_T entry_d, void *qual_p)
+{
+ if (entry_d->a_type != SMB_ACL_GROUP
+ && entry_d->a_type != SMB_ACL_USER) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ entry_d->a_id = *((id_t *)qual_p);
+
+ return 0;
+}
+
+int sys_acl_set_permset(SMB_ACL_ENTRY_T entry_d, SMB_ACL_PERMSET_T permset_d)
+{
+ if (*permset_d & ~(SMB_ACL_READ|SMB_ACL_WRITE|SMB_ACL_EXECUTE)) {
+ return EINVAL;
+ }
+
+ entry_d->a_perm = *permset_d;
+
+ return 0;
+}
+
+/*
+ * sort the ACL and check it for validity
+ *
+ * if it's a minimal ACL with only 4 entries then we
+ * need to recalculate the mask permissions to make
+ * sure that they are the same as the GROUP_OBJ
+ * permissions as required by the UnixWare acl() system call.
+ *
+ * (note: since POSIX allows minimal ACLs which only contain
+ * 3 entries - ie there is no mask entry - we should, in theory,
+ * check for this and add a mask entry if necessary - however
+ * we "know" that the caller of this interface always specifies
+ * a mask so, in practice "this never happens" (tm) - if it *does*
+ * happen aclsort() will fail and return an error and someone will
+ * have to fix it ...)
+ */
+
+static int acl_sort(SMB_ACL_T acl_d)
+{
+ int fixmask = (acl_d->count <= 4);
+
+ if (aclsort(acl_d->count, fixmask, acl_d->acl) != 0) {
+ errno = EINVAL;
+ return -1;
+ }
+ return 0;
+}
+
+int sys_acl_valid(SMB_ACL_T acl_d)
+{
+ return acl_sort(acl_d);
+}
+
+int sys_acl_set_file(const char *name, SMB_ACL_TYPE_T type, SMB_ACL_T acl_d)
+{
+ struct stat s;
+ struct acl *acl_p;
+ int acl_count;
+ struct acl *acl_buf = NULL;
+ int ret;
+
+ if (type != SMB_ACL_TYPE_ACCESS && type != SMB_ACL_TYPE_DEFAULT) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ if (acl_sort(acl_d) != 0) {
+ return -1;
+ }
+
+ acl_p = &acl_d->acl[0];
+ acl_count = acl_d->count;
+
+ /*
+ * if it's a directory there is extra work to do
+ * since the acl() system call will replace both
+ * the access ACLs and the default ACLs (if any)
+ */
+ if (stat(name, &s) != 0) {
+ return -1;
+ }
+ if (S_ISDIR(s.st_mode)) {
+ SMB_ACL_T acc_acl;
+ SMB_ACL_T def_acl;
+ SMB_ACL_T tmp_acl;
+ int i;
+
+ if (type == SMB_ACL_TYPE_ACCESS) {
+ acc_acl = acl_d;
+ def_acl = tmp_acl = sys_acl_get_file(name, SMB_ACL_TYPE_DEFAULT);
+
+ } else {
+ def_acl = acl_d;
+ acc_acl = tmp_acl = sys_acl_get_file(name, SMB_ACL_TYPE_ACCESS);
+ }
+
+ if (tmp_acl == NULL) {
+ return -1;
+ }
+
+ /*
+ * allocate a temporary buffer for the complete ACL
+ */
+ acl_count = acc_acl->count + def_acl->count;
+ acl_p = acl_buf = malloc(acl_count * sizeof(acl_buf[0]));
+
+ if (acl_buf == NULL) {
+ sys_acl_free_acl(tmp_acl);
+ errno = ENOMEM;
+ return -1;
+ }
+
+ /*
+ * copy the access control and default entries into the buffer
+ */
+ memcpy(&acl_buf[0], &acc_acl->acl[0],
+ acc_acl->count * sizeof(acl_buf[0]));
+
+ memcpy(&acl_buf[acc_acl->count], &def_acl->acl[0],
+ def_acl->count * sizeof(acl_buf[0]));
+
+ /*
+ * set the ACL_DEFAULT flag on the default entries
+ */
+ for (i = acc_acl->count; i < acl_count; i++) {
+ acl_buf[i].a_type |= ACL_DEFAULT;
+ }
+
+ sys_acl_free_acl(tmp_acl);
+
+ } else if (type != SMB_ACL_TYPE_ACCESS) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ ret = acl(name, SETACL, acl_count, acl_p);
+
+ SAFE_FREE(acl_buf);
+
+ return ret;
+}
+
+int sys_acl_set_fd(int fd, SMB_ACL_T acl_d)
+{
+ if (acl_sort(acl_d) != 0) {
+ return -1;
+ }
+
+ return facl(fd, SETACL, acl_d->count, &acl_d->acl[0]);
+}
+
+int sys_acl_delete_def_file(const char *path)
+{
+ SMB_ACL_T acl_d;
+ int ret;
+
+ /*
+ * fetching the access ACL and rewriting it has
+ * the effect of deleting the default ACL
+ */
+ if ((acl_d = sys_acl_get_file(path, SMB_ACL_TYPE_ACCESS)) == NULL) {
+ return -1;
+ }
+
+ ret = acl(path, SETACL, acl_d->count, acl_d->acl);
+
+ sys_acl_free_acl(acl_d);
+
+ return ret;
+}
+
+int sys_acl_free_text(char *text)
+{
+ SAFE_FREE(text);
+ return 0;
+}
+
+int sys_acl_free_acl(SMB_ACL_T acl_d)
+{
+ SAFE_FREE(acl_d);
+ return 0;
+}
+
+int sys_acl_free_qualifier(void *qual, SMB_ACL_TAG_T tagtype)
+{
+ return 0;
+}
+
+#elif defined(HAVE_HPUX_ACLS)
+#include <dl.h>
+
+/*
+ * Based on the Solaris/SCO code - with modifications.
+ */
+
+/*
+ * Note that while this code implements sufficient functionality
+ * to support the sys_acl_* interfaces it does not provide all
+ * of the semantics of the POSIX ACL interfaces.
+ *
+ * In particular, an ACL entry descriptor (SMB_ACL_ENTRY_T) returned
+ * from a call to sys_acl_get_entry() should not be assumed to be
+ * valid after calling any of the following functions, which may
+ * reorder the entries in the ACL.
+ *
+ * sys_acl_valid()
+ * sys_acl_set_file()
+ * sys_acl_set_fd()
+ */
+
+/* This checks if the POSIX ACL system call is defined */
+/* which basically corresponds to whether JFS 3.3 or */
+/* higher is installed. If acl() was called when it */
+/* isn't defined, it causes the process to core dump */
+/* so it is important to check this and avoid acl() */
+/* calls if it isn't there. */
+
+static BOOL hpux_acl_call_presence(void)
+{
+
+ shl_t handle = NULL;
+ void *value;
+ int ret_val=0;
+ static BOOL already_checked=0;
+
+ if(already_checked)
+ return True;
+
+
+ ret_val = shl_findsym(&handle, "acl", TYPE_PROCEDURE, &value);
+
+ if(ret_val != 0) {
+ DEBUG(5, ("hpux_acl_call_presence: shl_findsym() returned %d, errno = %d, error %s\n",
+ ret_val, errno, strerror(errno)));
+ DEBUG(5,("hpux_acl_call_presence: acl() system call is not present. Check if you have JFS 3.3 and above?\n"));
+ return False;
+ }
+
+ DEBUG(10,("hpux_acl_call_presence: acl() system call is present. We have JFS 3.3 or above \n"));
+
+ already_checked = True;
+ return True;
+}
+
+int sys_acl_get_entry(SMB_ACL_T acl_d, int entry_id, SMB_ACL_ENTRY_T *entry_p)
+{
+ if (entry_id != SMB_ACL_FIRST_ENTRY && entry_id != SMB_ACL_NEXT_ENTRY) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ if (entry_p == NULL) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ if (entry_id == SMB_ACL_FIRST_ENTRY) {
+ acl_d->next = 0;
+ }
+
+ if (acl_d->next < 0) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ if (acl_d->next >= acl_d->count) {
+ return 0;
+ }
+
+ *entry_p = &acl_d->acl[acl_d->next++];
+
+ return 1;
+}
+
+int sys_acl_get_tag_type(SMB_ACL_ENTRY_T entry_d, SMB_ACL_TAG_T *type_p)
+{
+ *type_p = entry_d->a_type;
+
+ return 0;
+}
+
+int sys_acl_get_permset(SMB_ACL_ENTRY_T entry_d, SMB_ACL_PERMSET_T *permset_p)
+{
+ *permset_p = &entry_d->a_perm;
+
+ return 0;
+}
+
+void *sys_acl_get_qualifier(SMB_ACL_ENTRY_T entry_d)
+{
+ if (entry_d->a_type != SMB_ACL_USER
+ && entry_d->a_type != SMB_ACL_GROUP) {
+ errno = EINVAL;
+ return NULL;
+ }
+
+ return &entry_d->a_id;
+}
+
+/*
+ * There is no way of knowing what size the ACL returned by
+ * ACL_GET will be unless you first call ACL_CNT which means
+ * making an additional system call.
+ *
+ * In the hope of avoiding the cost of the additional system
+ * call in most cases, we initially allocate enough space for
+ * an ACL with INITIAL_ACL_SIZE entries. If this turns out to
+ * be too small then we use ACL_CNT to find out the actual
+ * size, reallocate the ACL buffer, and then call ACL_GET again.
+ */
+
+#define INITIAL_ACL_SIZE 16
+
+SMB_ACL_T sys_acl_get_file(const char *path_p, SMB_ACL_TYPE_T type)
+{
+ SMB_ACL_T acl_d;
+ int count; /* # of ACL entries allocated */
+ int naccess; /* # of access ACL entries */
+ int ndefault; /* # of default ACL entries */
+
+ if(hpux_acl_call_presence() == False) {
+ /* Looks like we don't have the acl() system call on HPUX.
+ * May be the system doesn't have the latest version of JFS.
+ */
+ return NULL;
+ }
+
+ if (type != SMB_ACL_TYPE_ACCESS && type != SMB_ACL_TYPE_DEFAULT) {
+ errno = EINVAL;
+ return NULL;
+ }
+
+ count = INITIAL_ACL_SIZE;
+ if ((acl_d = sys_acl_init(count)) == NULL) {
+ return NULL;
+ }
+
+ /*
+ * If there isn't enough space for the ACL entries we use
+ * ACL_CNT to determine the actual number of ACL entries
+ * reallocate and try again. This is in a loop because it
+ * is possible that someone else could modify the ACL and
+ * increase the number of entries between the call to
+ * ACL_CNT and the call to ACL_GET.
+ */
+ while ((count = acl(path_p, ACL_GET, count, &acl_d->acl[0])) < 0 && errno == ENOSPC) {
+
+ sys_acl_free_acl(acl_d);
+
+ if ((count = acl(path_p, ACL_CNT, 0, NULL)) < 0) {
+ return NULL;
+ }
+
+ if ((acl_d = sys_acl_init(count)) == NULL) {
+ return NULL;
+ }
+ }
+
+ if (count < 0) {
+ sys_acl_free_acl(acl_d);
+ return NULL;
+ }
+
+ /*
+ * calculate the number of access and default ACL entries
+ *
+ * Note: we assume that the acl() system call returned a
+ * well formed ACL which is sorted so that all of the
+ * access ACL entries preceed any default ACL entries
+ */
+ for (naccess = 0; naccess < count; naccess++) {
+ if (acl_d->acl[naccess].a_type & ACL_DEFAULT)
+ break;
+ }
+ ndefault = count - naccess;
+
+ /*
+ * if the caller wants the default ACL we have to copy
+ * the entries down to the start of the acl[] buffer
+ * and mask out the ACL_DEFAULT flag from the type field
+ */
+ if (type == SMB_ACL_TYPE_DEFAULT) {
+ int i, j;
+
+ for (i = 0, j = naccess; i < ndefault; i++, j++) {
+ acl_d->acl[i] = acl_d->acl[j];
+ acl_d->acl[i].a_type &= ~ACL_DEFAULT;
+ }
+
+ acl_d->count = ndefault;
+ } else {
+ acl_d->count = naccess;
+ }
+
+ return acl_d;
+}
+
+SMB_ACL_T sys_acl_get_fd(int fd)
+{
+ /*
+ * HPUX doesn't have the facl call. Fake it using the path.... JRA.
+ */
+
+ files_struct *fsp = file_find_fd(fd);
+
+ if (fsp == NULL) {
+ errno = EBADF;
+ return NULL;
+ }
+
+ /*
+ * We know we're in the same conn context. So we
+ * can use the relative path.
+ */
+
+ return sys_acl_get_file(fsp->fsp_name, SMB_ACL_TYPE_ACCESS);
+}
+
+int sys_acl_clear_perms(SMB_ACL_PERMSET_T permset_d)
+{
+ *permset_d = 0;
+
+ return 0;
+}
+
+int sys_acl_add_perm(SMB_ACL_PERMSET_T permset_d, SMB_ACL_PERM_T perm)
+{
+ if (perm != SMB_ACL_READ && perm != SMB_ACL_WRITE
+ && perm != SMB_ACL_EXECUTE) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ if (permset_d == NULL) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ *permset_d |= perm;
+
+ return 0;
+}
+
+int sys_acl_get_perm(SMB_ACL_PERMSET_T permset_d, SMB_ACL_PERM_T perm)
+{
+ return *permset_d & perm;
+}
+
+char *sys_acl_to_text(SMB_ACL_T acl_d, ssize_t *len_p)
+{
+ int i;
+ int len, maxlen;
+ char *text;
+
+ /*
+ * use an initial estimate of 20 bytes per ACL entry
+ * when allocating memory for the text representation
+ * of the ACL
+ */
+ len = 0;
+ maxlen = 20 * acl_d->count;
+ if ((text = malloc(maxlen)) == NULL) {
+ errno = ENOMEM;
+ return NULL;
+ }
+
+ for (i = 0; i < acl_d->count; i++) {
+ struct acl *ap = &acl_d->acl[i];
+ struct passwd *pw;
+ struct group *gr;
+ char tagbuf[12];
+ char idbuf[12];
+ char *tag;
+ char *id = "";
+ char perms[4];
+ int nbytes;
+
+ switch (ap->a_type) {
+ /*
+ * for debugging purposes it's probably more
+ * useful to dump unknown tag types rather
+ * than just returning an error
+ */
+ default:
+ slprintf(tagbuf, sizeof(tagbuf)-1, "0x%x",
+ ap->a_type);
+ tag = tagbuf;
+ slprintf(idbuf, sizeof(idbuf)-1, "%ld",
+ (long)ap->a_id);
+ id = idbuf;
+ break;
+
+ case SMB_ACL_USER:
+ id = uidtoname(ap->a_id);
+ case SMB_ACL_USER_OBJ:
+ tag = "user";
+ break;
+
+ case SMB_ACL_GROUP:
+ if ((gr = getgrgid(ap->a_id)) == NULL) {
+ slprintf(idbuf, sizeof(idbuf)-1, "%ld",
+ (long)ap->a_id);
+ id = idbuf;
+ } else {
+ id = gr->gr_name;
+ }
+ case SMB_ACL_GROUP_OBJ:
+ tag = "group";
+ break;
+
+ case SMB_ACL_OTHER:
+ tag = "other";
+ break;
+
+ case SMB_ACL_MASK:
+ tag = "mask";
+ break;
+
+ }
+
+ perms[0] = (ap->a_perm & SMB_ACL_READ) ? 'r' : '-';
+ perms[1] = (ap->a_perm & SMB_ACL_WRITE) ? 'w' : '-';
+ perms[2] = (ap->a_perm & SMB_ACL_EXECUTE) ? 'x' : '-';
+ perms[3] = '\0';
+
+ /* <tag> : <qualifier> : rwx \n \0 */
+ nbytes = strlen(tag) + 1 + strlen(id) + 1 + 3 + 1 + 1;
+
+ /*
+ * If this entry would overflow the buffer
+ * allocate enough additional memory for this
+ * entry and an estimate of another 20 bytes
+ * for each entry still to be processed
+ */
+ if ((len + nbytes) > maxlen) {
+ char *oldtext = text;
+
+ maxlen += nbytes + 20 * (acl_d->count - i);
+
+ if ((text = Realloc(oldtext, maxlen)) == NULL) {
+ free(oldtext);
+ errno = ENOMEM;
+ return NULL;
+ }
+ }
+
+ slprintf(&text[len], nbytes-1, "%s:%s:%s\n", tag, id, perms);
+ len += nbytes - 1;
+ }
+
+ if (len_p)
+ *len_p = len;
+
+ return text;
+}
+
+SMB_ACL_T sys_acl_init(int count)
+{
+ SMB_ACL_T a;
+
+ if (count < 0) {
+ errno = EINVAL;
+ return NULL;
+ }
+
+ /*
+ * note that since the definition of the structure pointed
+ * to by the SMB_ACL_T includes the first element of the
+ * acl[] array, this actually allocates an ACL with room
+ * for (count+1) entries
+ */
+ if ((a = malloc(sizeof(*a) + count * sizeof(struct acl))) == NULL) {
+ errno = ENOMEM;
+ return NULL;
+ }
+
+ a->size = count + 1;
+ a->count = 0;
+ a->next = -1;
+
+ return a;
+}
+
+
+int sys_acl_create_entry(SMB_ACL_T *acl_p, SMB_ACL_ENTRY_T *entry_p)
+{
+ SMB_ACL_T acl_d;
+ SMB_ACL_ENTRY_T entry_d;
+
+ if (acl_p == NULL || entry_p == NULL || (acl_d = *acl_p) == NULL) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ if (acl_d->count >= acl_d->size) {
+ errno = ENOSPC;
+ return -1;
+ }
+
+ entry_d = &acl_d->acl[acl_d->count++];
+ entry_d->a_type = 0;
+ entry_d->a_id = -1;
+ entry_d->a_perm = 0;
+ *entry_p = entry_d;
+
+ return 0;
+}
+
+int sys_acl_set_tag_type(SMB_ACL_ENTRY_T entry_d, SMB_ACL_TAG_T tag_type)
+{
+ switch (tag_type) {
+ case SMB_ACL_USER:
+ case SMB_ACL_USER_OBJ:
+ case SMB_ACL_GROUP:
+ case SMB_ACL_GROUP_OBJ:
+ case SMB_ACL_OTHER:
+ case SMB_ACL_MASK:
+ entry_d->a_type = tag_type;
+ break;
+ default:
+ errno = EINVAL;
+ return -1;
+ }
+
+ return 0;
+}
+
+int sys_acl_set_qualifier(SMB_ACL_ENTRY_T entry_d, void *qual_p)
+{
+ if (entry_d->a_type != SMB_ACL_GROUP
+ && entry_d->a_type != SMB_ACL_USER) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ entry_d->a_id = *((id_t *)qual_p);
+
+ return 0;
+}
+
+int sys_acl_set_permset(SMB_ACL_ENTRY_T entry_d, SMB_ACL_PERMSET_T permset_d)
+{
+ if (*permset_d & ~(SMB_ACL_READ|SMB_ACL_WRITE|SMB_ACL_EXECUTE)) {
+ return EINVAL;
+ }
+
+ entry_d->a_perm = *permset_d;
+
+ return 0;
+}
+
+/* Structure to capture the count for each type of ACE. */
+
+struct hpux_acl_types {
+ int n_user;
+ int n_def_user;
+ int n_user_obj;
+ int n_def_user_obj;
+
+ int n_group;
+ int n_def_group;
+ int n_group_obj;
+ int n_def_group_obj;
+
+ int n_other;
+ int n_other_obj;
+ int n_def_other_obj;
+
+ int n_class_obj;
+ int n_def_class_obj;
+
+ int n_illegal_obj;
+};
+
+/* count_obj:
+ * Counts the different number of objects in a given array of ACL
+ * structures.
+ * Inputs:
+ *
+ * acl_count - Count of ACLs in the array of ACL strucutres.
+ * aclp - Array of ACL structures.
+ * acl_type_count - Pointer to acl_types structure. Should already be
+ * allocated.
+ * Output:
+ *
+ * acl_type_count - This structure is filled up with counts of various
+ * acl types.
+ */
+
+static int hpux_count_obj(int acl_count, struct acl *aclp, struct hpux_acl_types *acl_type_count)
+{
+ int i;
+
+ memset(acl_type_count, 0, sizeof(struct hpux_acl_types));
+
+ for(i=0;i<acl_count;i++) {
+ switch(aclp[i].a_type) {
+ case USER:
+ acl_type_count->n_user++;
+ break;
+ case USER_OBJ:
+ acl_type_count->n_user_obj++;
+ break;
+ case DEF_USER_OBJ:
+ acl_type_count->n_def_user_obj++;
+ break;
+ case GROUP:
+ acl_type_count->n_group++;
+ break;
+ case GROUP_OBJ:
+ acl_type_count->n_group_obj++;
+ break;
+ case DEF_GROUP_OBJ:
+ acl_type_count->n_def_group_obj++;
+ break;
+ case OTHER_OBJ:
+ acl_type_count->n_other_obj++;
+ break;
+ case DEF_OTHER_OBJ:
+ acl_type_count->n_def_other_obj++;
+ break;
+ case CLASS_OBJ:
+ acl_type_count->n_class_obj++;
+ break;
+ case DEF_CLASS_OBJ:
+ acl_type_count->n_def_class_obj++;
+ break;
+ case DEF_USER:
+ acl_type_count->n_def_user++;
+ break;
+ case DEF_GROUP:
+ acl_type_count->n_def_group++;
+ break;
+ default:
+ acl_type_count->n_illegal_obj++;
+ break;
+ }
+ }
+}
+
+/* swap_acl_entries: Swaps two ACL entries.
+ *
+ * Inputs: aclp0, aclp1 - ACL entries to be swapped.
+ */
+
+static void hpux_swap_acl_entries(struct acl *aclp0, struct acl *aclp1)
+{
+ struct acl temp_acl;
+
+ temp_acl.a_type = aclp0->a_type;
+ temp_acl.a_id = aclp0->a_id;
+ temp_acl.a_perm = aclp0->a_perm;
+
+ aclp0->a_type = aclp1->a_type;
+ aclp0->a_id = aclp1->a_id;
+ aclp0->a_perm = aclp1->a_perm;
+
+ aclp1->a_type = temp_acl.a_type;
+ aclp1->a_id = temp_acl.a_id;
+ aclp1->a_perm = temp_acl.a_perm;
+}
+
+/* prohibited_duplicate_type
+ * Identifies if given ACL type can have duplicate entries or
+ * not.
+ *
+ * Inputs: acl_type - ACL Type.
+ *
+ * Outputs:
+ *
+ * Return..
+ *
+ * True - If the ACL type matches any of the prohibited types.
+ * False - If the ACL type doesn't match any of the prohibited types.
+ */
+
+static BOOL hpux_prohibited_duplicate_type(int acl_type)
+{
+ switch(acl_type) {
+ case USER:
+ case GROUP:
+ case DEF_USER:
+ case DEF_GROUP:
+ return True;
+ default:
+ return False;
+ }
+}
+
+/* get_needed_class_perm
+ * Returns the permissions of a ACL structure only if the ACL
+ * type matches one of the pre-determined types for computing
+ * CLASS_OBJ permissions.
+ *
+ * Inputs: aclp - Pointer to ACL structure.
+ */
+
+static int hpux_get_needed_class_perm(struct acl *aclp)
+{
+ switch(aclp->a_type) {
+ case USER:
+ case GROUP_OBJ:
+ case GROUP:
+ case DEF_USER_OBJ:
+ case DEF_USER:
+ case DEF_GROUP_OBJ:
+ case DEF_GROUP:
+ case DEF_CLASS_OBJ:
+ case DEF_OTHER_OBJ:
+ return aclp->a_perm;
+ default:
+ return 0;
+ }
+}
+
+/* acl_sort for HPUX.
+ * Sorts the array of ACL structures as per the description in
+ * aclsort man page. Refer to aclsort man page for more details
+ *
+ * Inputs:
+ *
+ * acl_count - Count of ACLs in the array of ACL structures.
+ * calclass - If this is not zero, then we compute the CLASS_OBJ
+ * permissions.
+ * aclp - Array of ACL structures.
+ *
+ * Outputs:
+ *
+ * aclp - Sorted array of ACL structures.
+ *
+ * Outputs:
+ *
+ * Returns 0 for success -1 for failure. Prints a message to the Samba
+ * debug log in case of failure.
+ */
+
+static int hpux_acl_sort(int acl_count, int calclass, struct acl *aclp)
+{
+#if !defined(HAVE_HPUX_ACLSORT)
+ /*
+ * The aclsort() system call is availabe on the latest HPUX General
+ * Patch Bundles. So for HPUX, we developed our version of acl_sort
+ * function. Because, we don't want to update to a new
+ * HPUX GR bundle just for aclsort() call.
+ */
+
+ struct hpux_acl_types acl_obj_count;
+ int n_class_obj_perm = 0;
+ int i, j;
+
+ if(!acl_count) {
+ DEBUG(10,("Zero acl count passed. Returning Success\n"));
+ return 0;
+ }
+
+ if(aclp == NULL) {
+ DEBUG(0,("Null ACL pointer in hpux_acl_sort. Returning Failure. \n"));
+ return -1;
+ }
+
+ /* Count different types of ACLs in the ACLs array */
+
+ hpux_count_obj(acl_count, aclp, &acl_obj_count);
+
+ /* There should be only one entry each of type USER_OBJ, GROUP_OBJ,
+ * CLASS_OBJ and OTHER_OBJ
+ */
+
+ if( (acl_obj_count.n_user_obj != 1) ||
+ (acl_obj_count.n_group_obj != 1) ||
+ (acl_obj_count.n_class_obj != 1) ||
+ (acl_obj_count.n_other_obj != 1)
+ ) {
+ DEBUG(0,("hpux_acl_sort: More than one entry or no entries for \
+USER OBJ or GROUP_OBJ or OTHER_OBJ or CLASS_OBJ\n"));
+ return -1;
+ }
+
+ /* If any of the default objects are present, there should be only
+ * one of them each.
+ */
+
+ if( (acl_obj_count.n_def_user_obj > 1) || (acl_obj_count.n_def_group_obj > 1) ||
+ (acl_obj_count.n_def_other_obj > 1) || (acl_obj_count.n_def_class_obj > 1) ) {
+ DEBUG(0,("hpux_acl_sort: More than one entry for DEF_CLASS_OBJ \
+or DEF_USER_OBJ or DEF_GROUP_OBJ or DEF_OTHER_OBJ\n"));
+ return -1;
+ }
+
+ /* We now have proper number of OBJ and DEF_OBJ entries. Now sort the acl
+ * structures.
+ *
+ * Sorting crieteria - First sort by ACL type. If there are multiple entries of
+ * same ACL type, sort by ACL id.
+ *
+ * I am using the trival kind of sorting method here because, performance isn't
+ * really effected by the ACLs feature. More over there aren't going to be more
+ * than 17 entries on HPUX.
+ */
+
+ for(i=0; i<acl_count;i++) {
+ for (j=i+1; j<acl_count; j++) {
+ if( aclp[i].a_type > aclp[j].a_type ) {
+ /* ACL entries out of order, swap them */
+
+ hpux_swap_acl_entries((aclp+i), (aclp+j));
+
+ } else if ( aclp[i].a_type == aclp[j].a_type ) {
+
+ /* ACL entries of same type, sort by id */
+
+ if(aclp[i].a_id > aclp[j].a_id) {
+ hpux_swap_acl_entries((aclp+i), (aclp+j));
+ } else if (aclp[i].a_id == aclp[j].a_id) {
+ /* We have a duplicate entry. */
+ if(hpux_prohibited_duplicate_type(aclp[i].a_type)) {
+ DEBUG(0, ("hpux_acl_sort: Duplicate entry: Type(hex): %x Id: %d\n",
+ aclp[i].a_type, aclp[i].a_id));
+ return -1;
+ }
+ }
+
+ }
+ }
+ }
+
+ /* set the class obj permissions to the computed one. */
+ if(calclass) {
+ int n_class_obj_index = -1;
+
+ for(i=0;i<acl_count;i++) {
+ n_class_obj_perm |= hpux_get_needed_class_perm((aclp+i));
+
+ if(aclp[i].a_type == CLASS_OBJ)
+ n_class_obj_index = i;
+ }
+ aclp[n_class_obj_index].a_perm = n_class_obj_perm;
+ }
+
+ return 0;
+#else
+ return aclsort(acl_count, calclass, aclp);
+#endif
+}
+
+/*
+ * sort the ACL and check it for validity
+ *
+ * if it's a minimal ACL with only 4 entries then we
+ * need to recalculate the mask permissions to make
+ * sure that they are the same as the GROUP_OBJ
+ * permissions as required by the UnixWare acl() system call.
+ *
+ * (note: since POSIX allows minimal ACLs which only contain
+ * 3 entries - ie there is no mask entry - we should, in theory,
+ * check for this and add a mask entry if necessary - however
+ * we "know" that the caller of this interface always specifies
+ * a mask so, in practice "this never happens" (tm) - if it *does*
+ * happen aclsort() will fail and return an error and someone will
+ * have to fix it ...)
+ */
+
+static int acl_sort(SMB_ACL_T acl_d)
+{
+ int fixmask = (acl_d->count <= 4);
+
+ if (hpux_acl_sort(acl_d->count, fixmask, acl_d->acl) != 0) {
+ errno = EINVAL;
+ return -1;
+ }
+ return 0;
+}
+
+int sys_acl_valid(SMB_ACL_T acl_d)
+{
+ return acl_sort(acl_d);
+}
+
+int sys_acl_set_file(const char *name, SMB_ACL_TYPE_T type, SMB_ACL_T acl_d)
+{
+ struct stat s;
+ struct acl *acl_p;
+ int acl_count;
+ struct acl *acl_buf = NULL;
+ int ret;
+
+ if(hpux_acl_call_presence() == False) {
+ /* Looks like we don't have the acl() system call on HPUX.
+ * May be the system doesn't have the latest version of JFS.
+ */
+ errno=ENOSYS;
+ return -1;
+ }
+
+ if (type != SMB_ACL_TYPE_ACCESS && type != SMB_ACL_TYPE_DEFAULT) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ if (acl_sort(acl_d) != 0) {
+ return -1;
+ }
+
+ acl_p = &acl_d->acl[0];
+ acl_count = acl_d->count;
+
+ /*
+ * if it's a directory there is extra work to do
+ * since the acl() system call will replace both
+ * the access ACLs and the default ACLs (if any)
+ */
+ if (stat(name, &s) != 0) {
+ return -1;
+ }
+ if (S_ISDIR(s.st_mode)) {
+ SMB_ACL_T acc_acl;
+ SMB_ACL_T def_acl;
+ SMB_ACL_T tmp_acl;
+ int i;
+
+ if (type == SMB_ACL_TYPE_ACCESS) {
+ acc_acl = acl_d;
+ def_acl = tmp_acl = sys_acl_get_file(name, SMB_ACL_TYPE_DEFAULT);
+
+ } else {
+ def_acl = acl_d;
+ acc_acl = tmp_acl = sys_acl_get_file(name, SMB_ACL_TYPE_ACCESS);
+ }
+
+ if (tmp_acl == NULL) {
+ return -1;
+ }
+
+ /*
+ * allocate a temporary buffer for the complete ACL
+ */
+ acl_count = acc_acl->count + def_acl->count;
+ acl_p = acl_buf = malloc(acl_count * sizeof(acl_buf[0]));
+
+ if (acl_buf == NULL) {
+ sys_acl_free_acl(tmp_acl);
+ errno = ENOMEM;
+ return -1;
+ }
+
+ /*
+ * copy the access control and default entries into the buffer
+ */
+ memcpy(&acl_buf[0], &acc_acl->acl[0],
+ acc_acl->count * sizeof(acl_buf[0]));
+
+ memcpy(&acl_buf[acc_acl->count], &def_acl->acl[0],
+ def_acl->count * sizeof(acl_buf[0]));
+
+ /*
+ * set the ACL_DEFAULT flag on the default entries
+ */
+ for (i = acc_acl->count; i < acl_count; i++) {
+ acl_buf[i].a_type |= ACL_DEFAULT;
+ }
+
+ sys_acl_free_acl(tmp_acl);
+
+ } else if (type != SMB_ACL_TYPE_ACCESS) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ ret = acl(name, ACL_SET, acl_count, acl_p);
+
+ if (acl_buf) {
+ free(acl_buf);
+ }
+
+ return ret;
+}
+
+int sys_acl_set_fd(int fd, SMB_ACL_T acl_d)
+{
+ /*
+ * HPUX doesn't have the facl call. Fake it using the path.... JRA.
+ */
+
+ files_struct *fsp = file_find_fd(fd);
+
+ if (fsp == NULL) {
+ errno = EBADF;
+ return NULL;
+ }
+
+ if (acl_sort(acl_d) != 0) {
+ return -1;
+ }
+
+ /*
+ * We know we're in the same conn context. So we
+ * can use the relative path.
+ */
+
+ return sys_acl_set_file(fsp->fsp_name, SMB_ACL_TYPE_ACCESS, acl_d);
+}
+
+int sys_acl_delete_def_file(const char *path)
+{
+ SMB_ACL_T acl_d;
+ int ret;
+
+ /*
+ * fetching the access ACL and rewriting it has
+ * the effect of deleting the default ACL
+ */
+ if ((acl_d = sys_acl_get_file(path, SMB_ACL_TYPE_ACCESS)) == NULL) {
+ return -1;
+ }
+
+ ret = acl(path, ACL_SET, acl_d->count, acl_d->acl);
+
+ sys_acl_free_acl(acl_d);
+
+ return ret;
+}
+
+int sys_acl_free_text(char *text)
+{
+ free(text);
+ return 0;
+}
+
+int sys_acl_free_acl(SMB_ACL_T acl_d)
+{
+ free(acl_d);
+ return 0;
+}
+
+int sys_acl_free_qualifier(void *qual, SMB_ACL_TAG_T tagtype)
+{
+ return 0;
+}
+
+#elif defined(HAVE_IRIX_ACLS)
+
+int sys_acl_get_entry(SMB_ACL_T acl_d, int entry_id, SMB_ACL_ENTRY_T *entry_p)
+{
+ if (entry_id != SMB_ACL_FIRST_ENTRY && entry_id != SMB_ACL_NEXT_ENTRY) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ if (entry_p == NULL) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ if (entry_id == SMB_ACL_FIRST_ENTRY) {
+ acl_d->next = 0;
+ }
+
+ if (acl_d->next < 0) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ if (acl_d->next >= acl_d->aclp->acl_cnt) {
+ return 0;
+ }
+
+ *entry_p = &acl_d->aclp->acl_entry[acl_d->next++];
+
+ return 1;
+}
+
+int sys_acl_get_tag_type(SMB_ACL_ENTRY_T entry_d, SMB_ACL_TAG_T *type_p)
+{
+ *type_p = entry_d->ae_tag;
+
+ return 0;
+}
+
+int sys_acl_get_permset(SMB_ACL_ENTRY_T entry_d, SMB_ACL_PERMSET_T *permset_p)
+{
+ *permset_p = entry_d;
+
+ return 0;
+}
+
+void *sys_acl_get_qualifier(SMB_ACL_ENTRY_T entry_d)
+{
+ if (entry_d->ae_tag != SMB_ACL_USER
+ && entry_d->ae_tag != SMB_ACL_GROUP) {
+ errno = EINVAL;
+ return NULL;
+ }
+
+ return &entry_d->ae_id;
+}
+
+SMB_ACL_T sys_acl_get_file(const char *path_p, SMB_ACL_TYPE_T type)
+{
+ SMB_ACL_T a;
+
+ if ((a = malloc(sizeof(*a))) == NULL) {
+ errno = ENOMEM;
+ return NULL;
+ }
+ if ((a->aclp = acl_get_file(path_p, type)) == NULL) {
+ SAFE_FREE(a);
+ return NULL;
+ }
+ a->next = -1;
+ a->freeaclp = True;
+ return a;
+}
+
+SMB_ACL_T sys_acl_get_fd(int fd)
+{
+ SMB_ACL_T a;
+
+ if ((a = malloc(sizeof(*a))) == NULL) {
+ errno = ENOMEM;
+ return NULL;
+ }
+ if ((a->aclp = acl_get_fd(fd)) == NULL) {
+ SAFE_FREE(a);
+ return NULL;
+ }
+ a->next = -1;
+ a->freeaclp = True;
+ return a;
+}
+
+int sys_acl_clear_perms(SMB_ACL_PERMSET_T permset_d)
+{
+ permset_d->ae_perm = 0;
+
+ return 0;
+}
+
+int sys_acl_add_perm(SMB_ACL_PERMSET_T permset_d, SMB_ACL_PERM_T perm)
+{
+ if (perm != SMB_ACL_READ && perm != SMB_ACL_WRITE
+ && perm != SMB_ACL_EXECUTE) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ if (permset_d == NULL) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ permset_d->ae_perm |= perm;
+
+ return 0;
+}
+
+int sys_acl_get_perm(SMB_ACL_PERMSET_T permset_d, SMB_ACL_PERM_T perm)
+{
+ return permset_d->ae_perm & perm;
+}
+
+char *sys_acl_to_text(SMB_ACL_T acl_d, ssize_t *len_p)
+{
+ return acl_to_text(acl_d->aclp, len_p);
+}
+
+SMB_ACL_T sys_acl_init(int count)
+{
+ SMB_ACL_T a;
+
+ if (count < 0) {
+ errno = EINVAL;
+ return NULL;
+ }
+
+ if ((a = malloc(sizeof(*a) + sizeof(struct acl))) == NULL) {
+ errno = ENOMEM;
+ return NULL;
+ }
+
+ a->next = -1;
+ a->freeaclp = False;
+ a->aclp = (struct acl *)(&a->aclp + sizeof(struct acl *));
+ a->aclp->acl_cnt = 0;
+
+ return a;
+}
+
+
+int sys_acl_create_entry(SMB_ACL_T *acl_p, SMB_ACL_ENTRY_T *entry_p)
+{
+ SMB_ACL_T acl_d;
+ SMB_ACL_ENTRY_T entry_d;
+
+ if (acl_p == NULL || entry_p == NULL || (acl_d = *acl_p) == NULL) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ if (acl_d->aclp->acl_cnt >= ACL_MAX_ENTRIES) {
+ errno = ENOSPC;
+ return -1;
+ }
+
+ entry_d = &acl_d->aclp->acl_entry[acl_d->aclp->acl_cnt++];
+ entry_d->ae_tag = 0;
+ entry_d->ae_id = 0;
+ entry_d->ae_perm = 0;
+ *entry_p = entry_d;
+
+ return 0;
+}
+
+int sys_acl_set_tag_type(SMB_ACL_ENTRY_T entry_d, SMB_ACL_TAG_T tag_type)
+{
+ switch (tag_type) {
+ case SMB_ACL_USER:
+ case SMB_ACL_USER_OBJ:
+ case SMB_ACL_GROUP:
+ case SMB_ACL_GROUP_OBJ:
+ case SMB_ACL_OTHER:
+ case SMB_ACL_MASK:
+ entry_d->ae_tag = tag_type;
+ break;
+ default:
+ errno = EINVAL;
+ return -1;
+ }
+
+ return 0;
+}
+
+int sys_acl_set_qualifier(SMB_ACL_ENTRY_T entry_d, void *qual_p)
+{
+ if (entry_d->ae_tag != SMB_ACL_GROUP
+ && entry_d->ae_tag != SMB_ACL_USER) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ entry_d->ae_id = *((id_t *)qual_p);
+
+ return 0;
+}
+
+int sys_acl_set_permset(SMB_ACL_ENTRY_T entry_d, SMB_ACL_PERMSET_T permset_d)
+{
+ if (permset_d->ae_perm & ~(SMB_ACL_READ|SMB_ACL_WRITE|SMB_ACL_EXECUTE)) {
+ return EINVAL;
+ }
+
+ entry_d->ae_perm = permset_d->ae_perm;
+
+ return 0;
+}
+
+int sys_acl_valid(SMB_ACL_T acl_d)
+{
+ return acl_valid(acl_d->aclp);
+}
+
+int sys_acl_set_file(const char *name, SMB_ACL_TYPE_T type, SMB_ACL_T acl_d)
+{
+ return acl_set_file(name, type, acl_d->aclp);
+}
+
+int sys_acl_set_fd(int fd, SMB_ACL_T acl_d)
+{
+ return acl_set_fd(fd, acl_d->aclp);
+}
+
+int sys_acl_delete_def_file(const char *name)
+{
+ return acl_delete_def_file(name);
+}
+
+int sys_acl_free_text(char *text)
+{
+ return acl_free(text);
+}
+
+int sys_acl_free_acl(SMB_ACL_T acl_d)
+{
+ if (acl_d->freeaclp) {
+ acl_free(acl_d->aclp);
+ }
+ acl_free(acl_d);
+ return 0;
+}
+
+int sys_acl_free_qualifier(void *qual, SMB_ACL_TAG_T tagtype)
+{
+ return 0;
+}
+
+#elif defined(HAVE_AIX_ACLS)
+
+/* Donated by Medha Date, mdate@austin.ibm.com, for IBM */
+
+int sys_acl_get_entry( SMB_ACL_T theacl, int entry_id, SMB_ACL_ENTRY_T *entry_p)
+{
+ struct acl_entry_link *link;
+ struct new_acl_entry *entry;
+ int keep_going;
+
+ DEBUG(10,("This is the count: %d\n",theacl->count));
+
+ /* Check if count was previously set to -1. *
+ * If it was, that means we reached the end *
+ * of the acl last time. */
+ if(theacl->count == -1)
+ return(0);
+
+ link = theacl;
+ /* To get to the next acl, traverse linked list until index *
+ * of acl matches the count we are keeping. This count is *
+ * incremented each time we return an acl entry. */
+
+ for(keep_going = 0; keep_going < theacl->count; keep_going++)
+ link = link->nextp;
+
+ entry = *entry_p = link->entryp;
+
+ DEBUG(10,("*entry_p is %d\n",entry_p));
+ DEBUG(10,("*entry_p->ace_access is %d\n",entry->ace_access));
+
+ /* Increment count */
+ theacl->count++;
+ if(link->nextp == NULL)
+ theacl->count = -1;
+
+ return(1);
+}
+
+int sys_acl_get_tag_type( SMB_ACL_ENTRY_T entry_d, SMB_ACL_TAG_T *tag_type_p)
+{
+ /* Initialize tag type */
+
+ *tag_type_p = -1;
+ DEBUG(10,("the tagtype is %d\n",entry_d->ace_id->id_type));
+
+ /* Depending on what type of entry we have, *
+ * return tag type. */
+ switch(entry_d->ace_id->id_type) {
+ case ACEID_USER:
+ *tag_type_p = SMB_ACL_USER;
+ break;
+ case ACEID_GROUP:
+ *tag_type_p = SMB_ACL_GROUP;
+ break;
+
+ case SMB_ACL_USER_OBJ:
+ case SMB_ACL_GROUP_OBJ:
+ case SMB_ACL_OTHER:
+ *tag_type_p = entry_d->ace_id->id_type;
+ break;
+
+ default:
+ return(-1);
+ }
+
+ return(0);
+}
+
+int sys_acl_get_permset( SMB_ACL_ENTRY_T entry_d, SMB_ACL_PERMSET_T *permset_p)
+{
+ DEBUG(10,("Starting AIX sys_acl_get_permset\n"));
+ *permset_p = &entry_d->ace_access;
+ DEBUG(10,("**permset_p is %d\n",**permset_p));
+ if(!(**permset_p & S_IXUSR) &&
+ !(**permset_p & S_IWUSR) &&
+ !(**permset_p & S_IRUSR) &&
+ (**permset_p != 0))
+ return(-1);
+
+ DEBUG(10,("Ending AIX sys_acl_get_permset\n"));
+ return(0);
+}
+
+void *sys_acl_get_qualifier( SMB_ACL_ENTRY_T entry_d)
+{
+ return(entry_d->ace_id->id_data);
+}
+
+SMB_ACL_T sys_acl_get_file( const char *path_p, SMB_ACL_TYPE_T type)
+{
+ struct acl *file_acl = (struct acl *)NULL;
+ struct acl_entry *acl_entry;
+ struct new_acl_entry *new_acl_entry;
+ struct ace_id *idp;
+ struct acl_entry_link *acl_entry_link;
+ struct acl_entry_link *acl_entry_link_head;
+ int i;
+ int rc = 0;
+ uid_t user_id;
+
+ /* Get the acl using statacl */
+
+ DEBUG(10,("Entering sys_acl_get_file\n"));
+ DEBUG(10,("path_p is %s\n",path_p));
+
+ file_acl = (struct acl *)malloc(BUFSIZ);
+
+ if(file_acl == NULL) {
+ errno=ENOMEM;
+ DEBUG(0,("Error in AIX sys_acl_get_file: %d\n",errno));
+ return(NULL);
+ }
+
+ memset(file_acl,0,BUFSIZ);
+
+ rc = statacl((char *)path_p,0,file_acl,BUFSIZ);
+ if(rc == -1) {
+ DEBUG(0,("statacl returned %d with errno %d\n",rc,errno));
+ SAFE_FREE(file_acl);
+ return(NULL);
+ }
+
+ DEBUG(10,("Got facl and returned it\n"));
+
+ /* Point to the first acl entry in the acl */
+ acl_entry = file_acl->acl_ext;
+
+ /* Begin setting up the head of the linked list *
+ * that will be used for the storing the acl *
+ * in a way that is useful for the posix_acls.c *
+ * code. */
+
+ acl_entry_link_head = acl_entry_link = sys_acl_init(0);
+ if(acl_entry_link_head == NULL)
+ return(NULL);
+
+ acl_entry_link->entryp = (struct new_acl_entry *)malloc(sizeof(struct new_acl_entry));
+ if(acl_entry_link->entryp == NULL) {
+ SAFE_FREE(file_acl);
+ errno = ENOMEM;
+ DEBUG(0,("Error in AIX sys_acl_get_file is %d\n",errno));
+ return(NULL);
+ }
+
+ DEBUG(10,("acl_entry is %d\n",acl_entry));
+ DEBUG(10,("acl_last(file_acl) id %d\n",acl_last(file_acl)));
+
+ /* Check if the extended acl bit is on. *
+ * If it isn't, do not show the *
+ * contents of the acl since AIX intends *
+ * the extended info to remain unused */
+
+ if(file_acl->acl_mode & S_IXACL){
+ /* while we are not pointing to the very end */
+ while(acl_entry < acl_last(file_acl)) {
+ /* before we malloc anything, make sure this is */
+ /* a valid acl entry and one that we want to map */
+ idp = id_nxt(acl_entry->ace_id);
+ if((acl_entry->ace_type == ACC_SPECIFY ||
+ (acl_entry->ace_type == ACC_PERMIT)) && (idp != id_last(acl_entry))) {
+ acl_entry = acl_nxt(acl_entry);
+ continue;
+ }
+
+ idp = acl_entry->ace_id;
+
+ /* Check if this is the first entry in the linked list. *
+ * The first entry needs to keep prevp pointing to NULL *
+ * and already has entryp allocated. */
+
+ if(acl_entry_link_head->count != 0) {
+ acl_entry_link->nextp = (struct acl_entry_link *)
+ malloc(sizeof(struct acl_entry_link));
+
+ if(acl_entry_link->nextp == NULL) {
+ SAFE_FREE(file_acl);
+ errno = ENOMEM;
+ DEBUG(0,("Error in AIX sys_acl_get_file is %d\n",errno));
+ return(NULL);
+ }
+
+ acl_entry_link->nextp->prevp = acl_entry_link;
+ acl_entry_link = acl_entry_link->nextp;
+ acl_entry_link->entryp = (struct new_acl_entry *)malloc(sizeof(struct new_acl_entry));
+ if(acl_entry_link->entryp == NULL) {
+ SAFE_FREE(file_acl);
+ errno = ENOMEM;
+ DEBUG(0,("Error in AIX sys_acl_get_file is %d\n",errno));
+ return(NULL);
+ }
+ acl_entry_link->nextp = NULL;
+ }
+
+ acl_entry_link->entryp->ace_len = acl_entry->ace_len;
+
+ /* Don't really need this since all types are going *
+ * to be specified but, it's better than leaving it 0 */
+
+ acl_entry_link->entryp->ace_type = acl_entry->ace_type;
+
+ acl_entry_link->entryp->ace_access = acl_entry->ace_access;
+
+ memcpy(acl_entry_link->entryp->ace_id,idp,sizeof(struct ace_id));
+
+ /* The access in the acl entries must be left shifted by *
+ * three bites, because they will ultimately be compared *
+ * to S_IRUSR, S_IWUSR, and S_IXUSR. */
+
+ switch(acl_entry->ace_type){
+ case ACC_PERMIT:
+ case ACC_SPECIFY:
+ acl_entry_link->entryp->ace_access = acl_entry->ace_access;
+ acl_entry_link->entryp->ace_access <<= 6;
+ acl_entry_link_head->count++;
+ break;
+ case ACC_DENY:
+ /* Since there is no way to return a DENY acl entry *
+ * change to PERMIT and then shift. */
+ DEBUG(10,("acl_entry->ace_access is %d\n",acl_entry->ace_access));
+ acl_entry_link->entryp->ace_access = ~acl_entry->ace_access & 7;
+ DEBUG(10,("acl_entry_link->entryp->ace_access is %d\n",acl_entry_link->entryp->ace_access));
+ acl_entry_link->entryp->ace_access <<= 6;
+ acl_entry_link_head->count++;
+ break;
+ default:
+ return(0);
+ }
+
+ DEBUG(10,("acl_entry = %d\n",acl_entry));
+ DEBUG(10,("The ace_type is %d\n",acl_entry->ace_type));
+
+ acl_entry = acl_nxt(acl_entry);
+ }
+ } /* end of if enabled */
+
+ /* Since owner, group, other acl entries are not *
+ * part of the acl entries in an acl, they must *
+ * be dummied up to become part of the list. */
+
+ for( i = 1; i < 4; i++) {
+ DEBUG(10,("i is %d\n",i));
+ if(acl_entry_link_head->count != 0) {
+ acl_entry_link->nextp = (struct acl_entry_link *)malloc(sizeof(struct acl_entry_link));
+ if(acl_entry_link->nextp == NULL) {
+ SAFE_FREE(file_acl);
+ errno = ENOMEM;
+ DEBUG(0,("Error in AIX sys_acl_get_file is %d\n",errno));
+ return(NULL);
+ }
+
+ acl_entry_link->nextp->prevp = acl_entry_link;
+ acl_entry_link = acl_entry_link->nextp;
+ acl_entry_link->entryp = (struct new_acl_entry *)malloc(sizeof(struct new_acl_entry));
+ if(acl_entry_link->entryp == NULL) {
+ SAFE_FREE(file_acl);
+ errno = ENOMEM;
+ DEBUG(0,("Error in AIX sys_acl_get_file is %d\n",errno));
+ return(NULL);
+ }
+ }
+
+ acl_entry_link->nextp = NULL;
+
+ new_acl_entry = acl_entry_link->entryp;
+ idp = new_acl_entry->ace_id;
+
+ new_acl_entry->ace_len = sizeof(struct acl_entry);
+ new_acl_entry->ace_type = ACC_PERMIT;
+ idp->id_len = sizeof(struct ace_id);
+ DEBUG(10,("idp->id_len = %d\n",idp->id_len));
+ memset(idp->id_data,0,sizeof(uid_t));
+
+ switch(i) {
+ case 2:
+ new_acl_entry->ace_access = file_acl->g_access << 6;
+ idp->id_type = SMB_ACL_GROUP_OBJ;
+ break;
+
+ case 3:
+ new_acl_entry->ace_access = file_acl->o_access << 6;
+ idp->id_type = SMB_ACL_OTHER;
+ break;
+
+ case 1:
+ new_acl_entry->ace_access = file_acl->u_access << 6;
+ idp->id_type = SMB_ACL_USER_OBJ;
+ break;
+
+ default:
+ return(NULL);
+
+ }
+
+ acl_entry_link_head->count++;
+ DEBUG(10,("new_acl_entry->ace_access = %d\n",new_acl_entry->ace_access));
+ }
+
+ acl_entry_link_head->count = 0;
+ SAFE_FREE(file_acl);
+
+ return(acl_entry_link_head);
+}
+
+SMB_ACL_T sys_acl_get_fd(int fd)
+{
+ struct acl *file_acl = (struct acl *)NULL;
+ struct acl_entry *acl_entry;
+ struct new_acl_entry *new_acl_entry;
+ struct ace_id *idp;
+ struct acl_entry_link *acl_entry_link;
+ struct acl_entry_link *acl_entry_link_head;
+ int i;
+ int rc = 0;
+ uid_t user_id;
+
+ /* Get the acl using fstatacl */
+
+ DEBUG(10,("Entering sys_acl_get_fd\n"));
+ DEBUG(10,("fd is %d\n",fd));
+ file_acl = (struct acl *)malloc(BUFSIZ);
+
+ if(file_acl == NULL) {
+ errno=ENOMEM;
+ DEBUG(0,("Error in sys_acl_get_fd is %d\n",errno));
+ return(NULL);
+ }
+
+ memset(file_acl,0,BUFSIZ);
+
+ rc = fstatacl(fd,0,file_acl,BUFSIZ);
+ if(rc == -1) {
+ DEBUG(0,("The fstatacl call returned %d with errno %d\n",rc,errno));
+ SAFE_FREE(file_acl);
+ return(NULL);
+ }
+
+ DEBUG(10,("Got facl and returned it\n"));
+
+ /* Point to the first acl entry in the acl */
+
+ acl_entry = file_acl->acl_ext;
+ /* Begin setting up the head of the linked list *
+ * that will be used for the storing the acl *
+ * in a way that is useful for the posix_acls.c *
+ * code. */
+
+ acl_entry_link_head = acl_entry_link = sys_acl_init(0);
+ if(acl_entry_link_head == NULL){
+ SAFE_FREE(file_acl);
+ return(NULL);
+ }
+
+ acl_entry_link->entryp = (struct new_acl_entry *)malloc(sizeof(struct new_acl_entry));
+
+ if(acl_entry_link->entryp == NULL) {
+ errno = ENOMEM;
+ DEBUG(0,("Error in sys_acl_get_fd is %d\n",errno));
+ SAFE_FREE(file_acl);
+ return(NULL);
+ }
+
+ DEBUG(10,("acl_entry is %d\n",acl_entry));
+ DEBUG(10,("acl_last(file_acl) id %d\n",acl_last(file_acl)));
+
+ /* Check if the extended acl bit is on. *
+ * If it isn't, do not show the *
+ * contents of the acl since AIX intends *
+ * the extended info to remain unused */
+
+ if(file_acl->acl_mode & S_IXACL){
+ /* while we are not pointing to the very end */
+ while(acl_entry < acl_last(file_acl)) {
+ /* before we malloc anything, make sure this is */
+ /* a valid acl entry and one that we want to map */
+
+ idp = id_nxt(acl_entry->ace_id);
+ if((acl_entry->ace_type == ACC_SPECIFY ||
+ (acl_entry->ace_type == ACC_PERMIT)) && (idp != id_last(acl_entry))) {
+ acl_entry = acl_nxt(acl_entry);
+ continue;
+ }
+
+ idp = acl_entry->ace_id;
+
+ /* Check if this is the first entry in the linked list. *
+ * The first entry needs to keep prevp pointing to NULL *
+ * and already has entryp allocated. */
+
+ if(acl_entry_link_head->count != 0) {
+ acl_entry_link->nextp = (struct acl_entry_link *)malloc(sizeof(struct acl_entry_link));
+ if(acl_entry_link->nextp == NULL) {
+ errno = ENOMEM;
+ DEBUG(0,("Error in sys_acl_get_fd is %d\n",errno));
+ SAFE_FREE(file_acl);
+ return(NULL);
+ }
+ acl_entry_link->nextp->prevp = acl_entry_link;
+ acl_entry_link = acl_entry_link->nextp;
+ acl_entry_link->entryp = (struct new_acl_entry *)malloc(sizeof(struct new_acl_entry));
+ if(acl_entry_link->entryp == NULL) {
+ errno = ENOMEM;
+ DEBUG(0,("Error in sys_acl_get_fd is %d\n",errno));
+ SAFE_FREE(file_acl);
+ return(NULL);
+ }
+
+ acl_entry_link->nextp = NULL;
+ }
+
+ acl_entry_link->entryp->ace_len = acl_entry->ace_len;
+
+ /* Don't really need this since all types are going *
+ * to be specified but, it's better than leaving it 0 */
+
+ acl_entry_link->entryp->ace_type = acl_entry->ace_type;
+ acl_entry_link->entryp->ace_access = acl_entry->ace_access;
+
+ memcpy(acl_entry_link->entryp->ace_id, idp, sizeof(struct ace_id));
+
+ /* The access in the acl entries must be left shifted by *
+ * three bites, because they will ultimately be compared *
+ * to S_IRUSR, S_IWUSR, and S_IXUSR. */
+
+ switch(acl_entry->ace_type){
+ case ACC_PERMIT:
+ case ACC_SPECIFY:
+ acl_entry_link->entryp->ace_access = acl_entry->ace_access;
+ acl_entry_link->entryp->ace_access <<= 6;
+ acl_entry_link_head->count++;
+ break;
+ case ACC_DENY:
+ /* Since there is no way to return a DENY acl entry *
+ * change to PERMIT and then shift. */
+ DEBUG(10,("acl_entry->ace_access is %d\n",acl_entry->ace_access));
+ acl_entry_link->entryp->ace_access = ~acl_entry->ace_access & 7;
+ DEBUG(10,("acl_entry_link->entryp->ace_access is %d\n",acl_entry_link->entryp->ace_access));
+ acl_entry_link->entryp->ace_access <<= 6;
+ acl_entry_link_head->count++;
+ break;
+ default:
+ return(0);
+ }
+
+ DEBUG(10,("acl_entry = %d\n",acl_entry));
+ DEBUG(10,("The ace_type is %d\n",acl_entry->ace_type));
+
+ acl_entry = acl_nxt(acl_entry);
+ }
+ } /* end of if enabled */
+
+ /* Since owner, group, other acl entries are not *
+ * part of the acl entries in an acl, they must *
+ * be dummied up to become part of the list. */
+
+ for( i = 1; i < 4; i++) {
+ DEBUG(10,("i is %d\n",i));
+ if(acl_entry_link_head->count != 0){
+ acl_entry_link->nextp = (struct acl_entry_link *)malloc(sizeof(struct acl_entry_link));
+ if(acl_entry_link->nextp == NULL) {
+ errno = ENOMEM;
+ DEBUG(0,("Error in sys_acl_get_fd is %d\n",errno));
+ SAFE_FREE(file_acl);
+ return(NULL);
+ }
+
+ acl_entry_link->nextp->prevp = acl_entry_link;
+ acl_entry_link = acl_entry_link->nextp;
+ acl_entry_link->entryp = (struct new_acl_entry *)malloc(sizeof(struct new_acl_entry));
+
+ if(acl_entry_link->entryp == NULL) {
+ SAFE_FREE(file_acl);
+ errno = ENOMEM;
+ DEBUG(0,("Error in sys_acl_get_fd is %d\n",errno));
+ return(NULL);
+ }
+ }
+
+ acl_entry_link->nextp = NULL;
+
+ new_acl_entry = acl_entry_link->entryp;
+ idp = new_acl_entry->ace_id;
+
+ new_acl_entry->ace_len = sizeof(struct acl_entry);
+ new_acl_entry->ace_type = ACC_PERMIT;
+ idp->id_len = sizeof(struct ace_id);
+ DEBUG(10,("idp->id_len = %d\n",idp->id_len));
+ memset(idp->id_data,0,sizeof(uid_t));
+
+ switch(i) {
+ case 2:
+ new_acl_entry->ace_access = file_acl->g_access << 6;
+ idp->id_type = SMB_ACL_GROUP_OBJ;
+ break;
+
+ case 3:
+ new_acl_entry->ace_access = file_acl->o_access << 6;
+ idp->id_type = SMB_ACL_OTHER;
+ break;
+
+ case 1:
+ new_acl_entry->ace_access = file_acl->u_access << 6;
+ idp->id_type = SMB_ACL_USER_OBJ;
+ break;
+
+ default:
+ return(NULL);
+ }
+
+ acl_entry_link_head->count++;
+ DEBUG(10,("new_acl_entry->ace_access = %d\n",new_acl_entry->ace_access));
+ }
+
+ acl_entry_link_head->count = 0;
+ SAFE_FREE(file_acl);
+
+ return(acl_entry_link_head);
+}
+
+int sys_acl_clear_perms(SMB_ACL_PERMSET_T permset)
+{
+ *permset = *permset & ~0777;
+ return(0);
+}
+
+int sys_acl_add_perm( SMB_ACL_PERMSET_T permset, SMB_ACL_PERM_T perm)
+{
+ if((perm != 0) &&
+ (perm & (S_IXUSR | S_IWUSR | S_IRUSR)) == 0)
+ return(-1);
+
+ *permset |= perm;
+ DEBUG(10,("This is the permset now: %d\n",*permset));
+ return(0);
+}
+
+char *sys_acl_to_text( SMB_ACL_T theacl, ssize_t *plen)
+{
+ return(NULL);
+}
+
+SMB_ACL_T sys_acl_init( int count)
+{
+ struct acl_entry_link *theacl = NULL;
+
+ DEBUG(10,("Entering sys_acl_init\n"));
+
+ theacl = (struct acl_entry_link *)malloc(sizeof(struct acl_entry_link));
+ if(theacl == NULL) {
+ errno = ENOMEM;
+ DEBUG(0,("Error in sys_acl_init is %d\n",errno));
+ return(NULL);
+ }
+
+ theacl->count = 0;
+ theacl->nextp = NULL;
+ theacl->prevp = NULL;
+ theacl->entryp = NULL;
+ DEBUG(10,("Exiting sys_acl_init\n"));
+ return(theacl);
+}
+
+int sys_acl_create_entry( SMB_ACL_T *pacl, SMB_ACL_ENTRY_T *pentry)
+{
+ struct acl_entry_link *theacl;
+ struct acl_entry_link *acl_entryp;
+ struct acl_entry_link *temp_entry;
+ int counting;
+
+ DEBUG(10,("Entering the sys_acl_create_entry\n"));
+
+ theacl = acl_entryp = *pacl;
+
+ /* Get to the end of the acl before adding entry */
+
+ for(counting=0; counting < theacl->count; counting++){
+ DEBUG(10,("The acl_entryp is %d\n",acl_entryp));
+ temp_entry = acl_entryp;
+ acl_entryp = acl_entryp->nextp;
+ }
+
+ if(theacl->count != 0){
+ temp_entry->nextp = acl_entryp = (struct acl_entry_link *)malloc(sizeof(struct acl_entry_link));
+ if(acl_entryp == NULL) {
+ errno = ENOMEM;
+ DEBUG(0,("Error in sys_acl_create_entry is %d\n",errno));
+ return(-1);
+ }
+
+ DEBUG(10,("The acl_entryp is %d\n",acl_entryp));
+ acl_entryp->prevp = temp_entry;
+ DEBUG(10,("The acl_entryp->prevp is %d\n",acl_entryp->prevp));
+ }
+
+ *pentry = acl_entryp->entryp = (struct new_acl_entry *)malloc(sizeof(struct new_acl_entry));
+ if(*pentry == NULL) {
+ errno = ENOMEM;
+ DEBUG(0,("Error in sys_acl_create_entry is %d\n",errno));
+ return(-1);
+ }
+
+ memset(*pentry,0,sizeof(struct new_acl_entry));
+ acl_entryp->entryp->ace_len = sizeof(struct acl_entry);
+ acl_entryp->entryp->ace_type = ACC_PERMIT;
+ acl_entryp->entryp->ace_id->id_len = sizeof(struct ace_id);
+ acl_entryp->nextp = NULL;
+ theacl->count++;
+ DEBUG(10,("Exiting sys_acl_create_entry\n"));
+ return(0);
+}
+
+int sys_acl_set_tag_type( SMB_ACL_ENTRY_T entry, SMB_ACL_TAG_T tagtype)
+{
+ DEBUG(10,("Starting AIX sys_acl_set_tag_type\n"));
+ entry->ace_id->id_type = tagtype;
+ DEBUG(10,("The tag type is %d\n",entry->ace_id->id_type));
+ DEBUG(10,("Ending AIX sys_acl_set_tag_type\n"));
+}
+
+int sys_acl_set_qualifier( SMB_ACL_ENTRY_T entry, void *qual)
+{
+ DEBUG(10,("Starting AIX sys_acl_set_qualifier\n"));
+ memcpy(entry->ace_id->id_data,qual,sizeof(uid_t));
+ DEBUG(10,("Ending AIX sys_acl_set_qualifier\n"));
+ return(0);
+}
+
+int sys_acl_set_permset( SMB_ACL_ENTRY_T entry, SMB_ACL_PERMSET_T permset)
+{
+ DEBUG(10,("Starting AIX sys_acl_set_permset\n"));
+ if(!(*permset & S_IXUSR) &&
+ !(*permset & S_IWUSR) &&
+ !(*permset & S_IRUSR) &&
+ (*permset != 0))
+ return(-1);
+
+ entry->ace_access = *permset;
+ DEBUG(10,("entry->ace_access = %d\n",entry->ace_access));
+ DEBUG(10,("Ending AIX sys_acl_set_permset\n"));
+ return(0);
+}
+
+int sys_acl_valid( SMB_ACL_T theacl )
+{
+ int user_obj = 0;
+ int group_obj = 0;
+ int other_obj = 0;
+ struct acl_entry_link *acl_entry;
+
+ for(acl_entry=theacl; acl_entry != NULL; acl_entry = acl_entry->nextp) {
+ user_obj += (acl_entry->entryp->ace_id->id_type == SMB_ACL_USER_OBJ);
+ group_obj += (acl_entry->entryp->ace_id->id_type == SMB_ACL_GROUP_OBJ);
+ other_obj += (acl_entry->entryp->ace_id->id_type == SMB_ACL_OTHER);
+ }
+
+ DEBUG(10,("user_obj=%d, group_obj=%d, other_obj=%d\n",user_obj,group_obj,other_obj));
+
+ if(user_obj != 1 || group_obj != 1 || other_obj != 1)
+ return(-1);
+
+ return(0);
+}
+
+int sys_acl_set_file( const char *name, SMB_ACL_TYPE_T acltype, SMB_ACL_T theacl)
+{
+ struct acl_entry_link *acl_entry_link = NULL;
+ struct acl *file_acl = NULL;
+ struct acl *file_acl_temp = NULL;
+ struct acl_entry *acl_entry = NULL;
+ struct ace_id *ace_id = NULL;
+ uint id_type;
+ uint ace_access;
+ uint user_id;
+ uint acl_length;
+ uint rc;
+
+ DEBUG(10,("Entering sys_acl_set_file\n"));
+ DEBUG(10,("File name is %s\n",name));
+
+ /* AIX has no default ACL */
+ if(acltype == SMB_ACL_TYPE_DEFAULT)
+ return(0);
+
+ acl_length = BUFSIZ;
+ file_acl = (struct acl *)malloc(BUFSIZ);
+
+ if(file_acl == NULL) {
+ errno = ENOMEM;
+ DEBUG(0,("Error in sys_acl_set_file is %d\n",errno));
+ return(-1);
+ }
+
+ memset(file_acl,0,BUFSIZ);
+
+ file_acl->acl_len = ACL_SIZ;
+ file_acl->acl_mode = S_IXACL;
+
+ for(acl_entry_link=theacl; acl_entry_link != NULL; acl_entry_link = acl_entry_link->nextp) {
+ acl_entry_link->entryp->ace_access >>= 6;
+ id_type = acl_entry_link->entryp->ace_id->id_type;
+
+ switch(id_type) {
+ case SMB_ACL_USER_OBJ:
+ file_acl->u_access = acl_entry_link->entryp->ace_access;
+ continue;
+ case SMB_ACL_GROUP_OBJ:
+ file_acl->g_access = acl_entry_link->entryp->ace_access;
+ continue;
+ case SMB_ACL_OTHER:
+ file_acl->o_access = acl_entry_link->entryp->ace_access;
+ continue;
+ case SMB_ACL_MASK:
+ continue;
+ }
+
+ if((file_acl->acl_len + sizeof(struct acl_entry)) > acl_length) {
+ acl_length += sizeof(struct acl_entry);
+ file_acl_temp = (struct acl *)malloc(acl_length);
+ if(file_acl_temp == NULL) {
+ SAFE_FREE(file_acl);
+ errno = ENOMEM;
+ DEBUG(0,("Error in sys_acl_set_file is %d\n",errno));
+ return(-1);
+ }
+
+ memcpy(file_acl_temp,file_acl,file_acl->acl_len);
+ SAFE_FREE(file_acl);
+ file_acl = file_acl_temp;
+ }
+
+ acl_entry = (struct acl_entry *)((char *)file_acl + file_acl->acl_len);
+ file_acl->acl_len += sizeof(struct acl_entry);
+ acl_entry->ace_len = acl_entry_link->entryp->ace_len;
+ acl_entry->ace_access = acl_entry_link->entryp->ace_access;
+
+ /* In order to use this, we'll need to wait until we can get denies */
+ /* if(!acl_entry->ace_access && acl_entry->ace_type == ACC_PERMIT)
+ acl_entry->ace_type = ACC_SPECIFY; */
+
+ acl_entry->ace_type = ACC_SPECIFY;
+
+ ace_id = acl_entry->ace_id;
+
+ ace_id->id_type = acl_entry_link->entryp->ace_id->id_type;
+ DEBUG(10,("The id type is %d\n",ace_id->id_type));
+ ace_id->id_len = acl_entry_link->entryp->ace_id->id_len;
+ memcpy(&user_id, acl_entry_link->entryp->ace_id->id_data, sizeof(uid_t));
+ memcpy(acl_entry->ace_id->id_data, &user_id, sizeof(uid_t));
+ }
+
+ rc = chacl(name,file_acl,file_acl->acl_len);
+ DEBUG(10,("errno is %d\n",errno));
+ DEBUG(10,("return code is %d\n",rc));
+ SAFE_FREE(file_acl);
+ DEBUG(10,("Exiting the sys_acl_set_file\n"));
+ return(rc);
+}
+
+int sys_acl_set_fd( int fd, SMB_ACL_T theacl)
+{
+ struct acl_entry_link *acl_entry_link = NULL;
+ struct acl *file_acl = NULL;
+ struct acl *file_acl_temp = NULL;
+ struct acl_entry *acl_entry = NULL;
+ struct ace_id *ace_id = NULL;
+ uint id_type;
+ uint user_id;
+ uint acl_length;
+ uint rc;
+
+ DEBUG(10,("Entering sys_acl_set_fd\n"));
+ acl_length = BUFSIZ;
+ file_acl = (struct acl *)malloc(BUFSIZ);
+
+ if(file_acl == NULL) {
+ errno = ENOMEM;
+ DEBUG(0,("Error in sys_acl_set_fd is %d\n",errno));
+ return(-1);
+ }
+
+ memset(file_acl,0,BUFSIZ);
+
+ file_acl->acl_len = ACL_SIZ;
+ file_acl->acl_mode = S_IXACL;
+
+ for(acl_entry_link=theacl; acl_entry_link != NULL; acl_entry_link = acl_entry_link->nextp) {
+ acl_entry_link->entryp->ace_access >>= 6;
+ id_type = acl_entry_link->entryp->ace_id->id_type;
+ DEBUG(10,("The id_type is %d\n",id_type));
+
+ switch(id_type) {
+ case SMB_ACL_USER_OBJ:
+ file_acl->u_access = acl_entry_link->entryp->ace_access;
+ continue;
+ case SMB_ACL_GROUP_OBJ:
+ file_acl->g_access = acl_entry_link->entryp->ace_access;
+ continue;
+ case SMB_ACL_OTHER:
+ file_acl->o_access = acl_entry_link->entryp->ace_access;
+ continue;
+ case SMB_ACL_MASK:
+ continue;
+ }
+
+ if((file_acl->acl_len + sizeof(struct acl_entry)) > acl_length) {
+ acl_length += sizeof(struct acl_entry);
+ file_acl_temp = (struct acl *)malloc(acl_length);
+ if(file_acl_temp == NULL) {
+ SAFE_FREE(file_acl);
+ errno = ENOMEM;
+ DEBUG(0,("Error in sys_acl_set_fd is %d\n",errno));
+ return(-1);
+ }
+
+ memcpy(file_acl_temp,file_acl,file_acl->acl_len);
+ SAFE_FREE(file_acl);
+ file_acl = file_acl_temp;
+ }
+
+ acl_entry = (struct acl_entry *)((char *)file_acl + file_acl->acl_len);
+ file_acl->acl_len += sizeof(struct acl_entry);
+ acl_entry->ace_len = acl_entry_link->entryp->ace_len;
+ acl_entry->ace_access = acl_entry_link->entryp->ace_access;
+
+ /* In order to use this, we'll need to wait until we can get denies */
+ /* if(!acl_entry->ace_access && acl_entry->ace_type == ACC_PERMIT)
+ acl_entry->ace_type = ACC_SPECIFY; */
+
+ acl_entry->ace_type = ACC_SPECIFY;
+
+ ace_id = acl_entry->ace_id;
+
+ ace_id->id_type = acl_entry_link->entryp->ace_id->id_type;
+ DEBUG(10,("The id type is %d\n",ace_id->id_type));
+ ace_id->id_len = acl_entry_link->entryp->ace_id->id_len;
+ memcpy(&user_id, acl_entry_link->entryp->ace_id->id_data, sizeof(uid_t));
+ memcpy(ace_id->id_data, &user_id, sizeof(uid_t));
+ }
+
+ rc = fchacl(fd,file_acl,file_acl->acl_len);
+ DEBUG(10,("errno is %d\n",errno));
+ DEBUG(10,("return code is %d\n",rc));
+ SAFE_FREE(file_acl);
+ DEBUG(10,("Exiting sys_acl_set_fd\n"));
+ return(rc);
+}
+
+int sys_acl_delete_def_file(const char *name)
+{
+ /* AIX has no default ACL */
+ return 0;
+}
+
+int sys_acl_get_perm( SMB_ACL_PERMSET_T permset, SMB_ACL_PERM_T perm)
+{
+ return(*permset & perm);
+}
+
+int sys_acl_free_text(char *text)
+{
+ return(0);
+}
+
+int sys_acl_free_acl(SMB_ACL_T posix_acl)
+{
+ struct acl_entry_link *acl_entry_link;
+
+ for(acl_entry_link = posix_acl->nextp; acl_entry_link->nextp != NULL; acl_entry_link = acl_entry_link->nextp) {
+ SAFE_FREE(acl_entry_link->prevp->entryp);
+ SAFE_FREE(acl_entry_link->prevp);
+ }
+
+ SAFE_FREE(acl_entry_link->prevp->entryp);
+ SAFE_FREE(acl_entry_link->prevp);
+ SAFE_FREE(acl_entry_link->entryp);
+ SAFE_FREE(acl_entry_link);
+
+ return(0);
+}
+
+int sys_acl_free_qualifier(void *qual, SMB_ACL_TAG_T tagtype)
+{
+ return(0);
+}
+
+#else /* No ACLs. */
+
+int sys_acl_get_entry( SMB_ACL_T the_acl, int entry_id, SMB_ACL_ENTRY_T *entry_p)
+{
+ errno = ENOSYS;
+ return -1;
+}
+
+int sys_acl_get_tag_type( SMB_ACL_ENTRY_T entry_d, SMB_ACL_TAG_T *tag_type_p)
+{
+ errno = ENOSYS;
+ return -1;
+}
+
+int sys_acl_get_permset( SMB_ACL_ENTRY_T entry_d, SMB_ACL_PERMSET_T *permset_p)
+{
+ errno = ENOSYS;
+ return -1;
+}
+
+void *sys_acl_get_qualifier( SMB_ACL_ENTRY_T entry_d)
+{
+ errno = ENOSYS;
+ return NULL;
+}
+
+SMB_ACL_T sys_acl_get_file( const char *path_p, SMB_ACL_TYPE_T type)
+{
+ errno = ENOSYS;
+ return (SMB_ACL_T)NULL;
+}
+
+SMB_ACL_T sys_acl_get_fd(int fd)
+{
+ errno = ENOSYS;
+ return (SMB_ACL_T)NULL;
+}
+
+int sys_acl_clear_perms(SMB_ACL_PERMSET_T permset)
+{
+ errno = ENOSYS;
+ return -1;
+}
+
+int sys_acl_add_perm( SMB_ACL_PERMSET_T permset, SMB_ACL_PERM_T perm)
+{
+ errno = ENOSYS;
+ return -1;
+}
+
+int sys_acl_get_perm( SMB_ACL_PERMSET_T permset, SMB_ACL_PERM_T perm)
+{
+ errno = ENOSYS;
+ return (permset & perm) ? 1 : 0;
+}
+
+char *sys_acl_to_text( SMB_ACL_T the_acl, ssize_t *plen)
+{
+ errno = ENOSYS;
+ return NULL;
+}
+
+int sys_acl_free_text(char *text)
+{
+ errno = ENOSYS;
+ return -1;
+}
+
+SMB_ACL_T sys_acl_init( int count)
+{
+ errno = ENOSYS;
+ return NULL;
+}
+
+int sys_acl_create_entry( SMB_ACL_T *pacl, SMB_ACL_ENTRY_T *pentry)
+{
+ errno = ENOSYS;
+ return -1;
+}
+
+int sys_acl_set_tag_type( SMB_ACL_ENTRY_T entry, SMB_ACL_TAG_T tagtype)
+{
+ errno = ENOSYS;
+ return -1;
+}
+
+int sys_acl_set_qualifier( SMB_ACL_ENTRY_T entry, void *qual)
+{
+ errno = ENOSYS;
+ return -1;
+}
+
+int sys_acl_set_permset( SMB_ACL_ENTRY_T entry, SMB_ACL_PERMSET_T permset)
+{
+ errno = ENOSYS;
+ return -1;
+}
+
+int sys_acl_valid( SMB_ACL_T theacl )
+{
+ errno = ENOSYS;
+ return -1;
+}
+
+int sys_acl_set_file( const char *name, SMB_ACL_TYPE_T acltype, SMB_ACL_T theacl)
+{
+ errno = ENOSYS;
+ return -1;
+}
+
+int sys_acl_set_fd( int fd, SMB_ACL_T theacl)
+{
+ errno = ENOSYS;
+ return -1;
+}
+
+int sys_acl_delete_def_file(const char *name)
+{
+ errno = ENOSYS;
+ return -1;
+}
+
+int sys_acl_free_acl(SMB_ACL_T the_acl)
+{
+ errno = ENOSYS;
+ return -1;
+}
+
+int sys_acl_free_qualifier(void *qual, SMB_ACL_TAG_T tagtype)
+{
+ errno = ENOSYS;
+ return -1;
+}
+
+#endif /* No ACLs. */
diff --git a/source/lib/sysquotas.c b/source/lib/sysquotas.c
new file mode 100644
index 00000000000..1c5c7e8bd4f
--- /dev/null
+++ b/source/lib/sysquotas.c
@@ -0,0 +1,505 @@
+/*
+ Unix SMB/CIFS implementation.
+ System QUOTA function wrappers
+ 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 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"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_QUOTA
+
+#ifdef HAVE_SYS_QUOTAS
+
+#if defined(HAVE_QUOTACTL_4A)
+
+/*#endif HAVE_QUOTACTL_4A */
+#elif defined(HAVE_QUOTACTL_4B)
+
+#error HAVE_QUOTACTL_4B not implemeted
+
+/*#endif HAVE_QUOTACTL_4B */
+#elif defined(HAVE_QUOTACTL_3)
+
+#error HAVE_QUOTACTL_3 not implemented
+
+/* #endif HAVE_QUOTACTL_3 */
+#else /* NO_QUOTACTL_USED */
+
+#endif /* NO_QUOTACTL_USED */
+
+#ifdef HAVE_MNTENT
+static int sys_path_to_bdev(const char *path, char **mntpath, char **bdev, char **fs)
+{
+ int ret = -1;
+ SMB_STRUCT_STAT S;
+ FILE *fp;
+ struct mntent *mnt;
+ SMB_DEV_T devno;
+
+ /* find the block device file */
+
+ if (!path||!mntpath||!bdev||!fs)
+ smb_panic("sys_path_to_bdev: called with NULL pointer");
+
+ (*mntpath) = NULL;
+ (*bdev) = NULL;
+ (*fs) = NULL;
+
+ if ( sys_stat(path, &S) == -1 )
+ return (-1);
+
+ devno = S.st_dev ;
+
+ fp = setmntent(MOUNTED,"r");
+
+ while ((mnt = getmntent(fp))) {
+ if ( sys_stat(mnt->mnt_dir,&S) == -1 )
+ continue ;
+
+ if (S.st_dev == devno) {
+ (*mntpath) = strdup(mnt->mnt_dir);
+ (*bdev) = strdup(mnt->mnt_fsname);
+ (*fs) = strdup(mnt->mnt_type);
+ if ((*mntpath)&&(*bdev)&&(*fs)) {
+ ret = 0;
+ } else {
+ SAFE_FREE(*mntpath);
+ SAFE_FREE(*bdev);
+ SAFE_FREE(*fs);
+ ret = -1;
+ }
+
+ break;
+ }
+ }
+
+ endmntent(fp) ;
+
+ return ret;
+}
+/* #endif HAVE_MNTENT */
+#elif defined(HAVE_DEVNM)
+
+/* we have this on HPUX, ... */
+static int sys_path_to_bdev(const char *path, char **mntpath, char **bdev, char **fs)
+{
+ int ret = -1;
+ char dev_disk[256];
+ SMB_STRUCT_STAT S;
+
+ if (!path||!mntpath||!bdev||!fs)
+ smb_panic("sys_path_to_bdev: called with NULL pointer");
+
+ (*mntpath) = NULL;
+ (*bdev) = NULL;
+ (*fs) = NULL;
+
+ /* find the block device file */
+
+ if ((ret=sys_stat(path, &S))!=0) {
+ return ret;
+ }
+
+ if ((ret=devnm(S_IFBLK, S.st_dev, dev_disk, 256, 1))!=0) {
+ return ret;
+ }
+
+ /* we should get the mntpath right...
+ * but I don't know how
+ * --metze
+ */
+ (*mntpath) = strdup(path);
+ (*bdev) = strdup(dev_disk);
+ if ((*mntpath)&&(*bdev)) {
+ ret = 0;
+ } else {
+ SAFE_FREE(*mntpath);
+ SAFE_FREE(*bdev);
+ ret = -1;
+ }
+
+
+ return ret;
+}
+
+/* #endif HAVE_DEVNM */
+#else
+/* we should fake this up...*/
+static int sys_path_to_bdev(const char *path, char **mntpath, char **bdev, char **fs)
+{
+ int ret = -1;
+
+ if (!path||!mntpath||!bdev||!fs)
+ smb_panic("sys_path_to_bdev: called with NULL pointer");
+
+ (*mntpath) = NULL;
+ (*bdev) = NULL;
+ (*fs) = NULL;
+
+ (*mntpath) = strdup(path);
+ if (*mntpath) {
+ ret = 0;
+ } else {
+ SAFE_FREE(*mntpath);
+ ret = -1;
+ }
+
+ return ret;
+}
+#endif
+
+/*********************************************************************
+ Now the list of all filesystem specific quota systems we have found
+**********************************************************************/
+static struct {
+ const char *name;
+ int (*get_quota)(const char *path, const char *bdev, enum SMB_QUOTA_TYPE qtype, unid_t id, SMB_DISK_QUOTA *dp);
+ int (*set_quota)(const char *path, const char *bdev, enum SMB_QUOTA_TYPE qtype, unid_t id, SMB_DISK_QUOTA *dp);
+} sys_quota_backends[] = {
+#ifdef HAVE_XFS_QUOTAS
+ {"xfs", sys_get_xfs_quota, sys_set_xfs_quota},
+#endif /* HAVE_XFS_QUOTAS */
+ {NULL, NULL, NULL}
+};
+
+static int command_get_quota(const char *path, enum SMB_QUOTA_TYPE qtype, unid_t id, SMB_DISK_QUOTA *dp)
+{
+ const char *get_quota_command;
+
+ get_quota_command = lp_get_quota_command();
+ if (get_quota_command && *get_quota_command) {
+ const char *p;
+ char *p2;
+ char **lines;
+ pstring syscmd;
+ int _id = -1;
+
+ switch(qtype) {
+ case SMB_USER_QUOTA_TYPE:
+ case SMB_USER_FS_QUOTA_TYPE:
+ _id = id.uid;
+ break;
+ case SMB_GROUP_QUOTA_TYPE:
+ case SMB_GROUP_FS_QUOTA_TYPE:
+ _id = id.gid;
+ break;
+ default:
+ DEBUG(0,("invalid quota type.\n"));
+ return -1;
+ }
+
+ slprintf(syscmd, sizeof(syscmd)-1,
+ "%s \"%s\" %d %d",
+ get_quota_command, path, qtype, _id);
+
+ DEBUG (3, ("get_quota: Running command %s\n", syscmd));
+
+ lines = file_lines_pload(syscmd, NULL);
+ if (lines) {
+ char *line = lines[0];
+
+ DEBUG (3, ("Read output from get_quota, \"%s\"\n", line));
+
+ /* we need to deal with long long unsigned here, if supported */
+
+ dp->qflags = (enum SMB_QUOTA_TYPE)strtoul(line, &p2, 10);
+ p = p2;
+ while (p && *p && isspace(*p))
+ p++;
+ if (p && *p)
+ dp->curblocks = STR_TO_SMB_BIG_UINT(p, &p);
+ else
+ goto invalid_param;
+ while (p && *p && isspace(*p))
+ p++;
+ if (p && *p)
+ dp->softlimit = STR_TO_SMB_BIG_UINT(p, &p);
+ else
+ goto invalid_param;
+ while (p && *p && isspace(*p))
+ p++;
+ if (p && *p)
+ dp->hardlimit = STR_TO_SMB_BIG_UINT(p, &p);
+ else
+ goto invalid_param;
+ while (p && *p && isspace(*p))
+ p++;
+ if (p && *p)
+ dp->curinodes = STR_TO_SMB_BIG_UINT(p, &p);
+ else
+ goto invalid_param;
+ while (p && *p && isspace(*p))
+ p++;
+ if (p && *p)
+ dp->isoftlimit = STR_TO_SMB_BIG_UINT(p, &p);
+ else
+ goto invalid_param;
+ while (p && *p && isspace(*p))
+ p++;
+ if (p && *p)
+ dp->ihardlimit = STR_TO_SMB_BIG_UINT(p, &p);
+ else
+ goto invalid_param;
+ while (p && *p && isspace(*p))
+ p++;
+ if (p && *p)
+ dp->bsize = STR_TO_SMB_BIG_UINT(p, NULL);
+ else
+ dp->bsize = 1024;
+ file_lines_free(lines);
+ DEBUG (3, ("Parsed output of get_quota, ...\n"));
+
+#ifdef LARGE_SMB_OFF_T
+ DEBUGADD (5,(
+ "qflags:%u curblocks:%llu softlimit:%llu hardlimit:%llu\n"
+ "curinodes:%llu isoftlimit:%llu ihardlimit:%llu bsize:%llu\n",
+ dp->qflags,(long long unsigned)dp->curblocks,
+ (long long unsigned)dp->softlimit,(long long unsigned)dp->hardlimit,
+ (long long unsigned)dp->curinodes,
+ (long long unsigned)dp->isoftlimit,(long long unsigned)dp->ihardlimit,
+ (long long unsigned)dp->bsize));
+#else /* LARGE_SMB_OFF_T */
+ DEBUGADD (5,(
+ "qflags:%u curblocks:%lu softlimit:%lu hardlimit:%lu\n"
+ "curinodes:%lu isoftlimit:%lu ihardlimit:%lu bsize:%lu\n",
+ dp->qflags,(long unsigned)dp->curblocks,
+ (long unsigned)dp->softlimit,(long unsigned)dp->hardlimit,
+ (long unsigned)dp->curinodes,
+ (long unsigned)dp->isoftlimit,(long unsigned)dp->ihardlimit,
+ (long unsigned)dp->bsize));
+#endif /* LARGE_SMB_OFF_T */
+ return 0;
+ }
+
+ DEBUG (0, ("get_quota_command failed!\n"));
+ return -1;
+ }
+
+ errno = ENOSYS;
+ return -1;
+
+invalid_param:
+ DEBUG(0,("The output of get_quota_command is invalid!\n"));
+ return -1;
+}
+
+static int command_set_quota(const char *path, enum SMB_QUOTA_TYPE qtype, unid_t id, SMB_DISK_QUOTA *dp)
+{
+ const char *set_quota_command;
+
+ set_quota_command = lp_set_quota_command();
+ if (set_quota_command && *set_quota_command) {
+ char **lines;
+ pstring syscmd;
+ int _id = -1;
+
+ switch(qtype) {
+ case SMB_USER_QUOTA_TYPE:
+ case SMB_USER_FS_QUOTA_TYPE:
+ _id = id.uid;
+ break;
+ case SMB_GROUP_QUOTA_TYPE:
+ case SMB_GROUP_FS_QUOTA_TYPE:
+ _id = id.gid;
+ break;
+ default:
+ return -1;
+ }
+
+#ifdef LARGE_SMB_OFF_T
+ slprintf(syscmd, sizeof(syscmd)-1,
+ "%s \"%s\" %d %d "
+ "%u %llu %llu "
+ "%llu %llu %llu ",
+ set_quota_command, path, qtype, _id, dp->qflags,
+ (long long unsigned)dp->softlimit,(long long unsigned)dp->hardlimit,
+ (long long unsigned)dp->isoftlimit,(long long unsigned)dp->ihardlimit,
+ (long long unsigned)dp->bsize);
+#else /* LARGE_SMB_OFF_T */
+ slprintf(syscmd, sizeof(syscmd)-1,
+ "%s \"%s\" %d %d "
+ "%u %lu %lu "
+ "%lu %lu %lu ",
+ set_quota_command, path, qtype, _id, dp->qflags,
+ (long unsigned)dp->softlimit,(long unsigned)dp->hardlimit,
+ (long unsigned)dp->isoftlimit,(long unsigned)dp->ihardlimit,
+ (long unsigned)dp->bsize);
+#endif /* LARGE_SMB_OFF_T */
+
+
+
+ DEBUG (3, ("get_quota: Running command %s\n", syscmd));
+
+ lines = file_lines_pload(syscmd, NULL);
+ if (lines) {
+ char *line = lines[0];
+
+ DEBUG (3, ("Read output from set_quota, \"%s\"\n", line));
+
+ file_lines_free(lines);
+
+ return 0;
+ }
+ DEBUG (0, ("set_quota_command failed!\n"));
+ return -1;
+ }
+
+ errno = ENOSYS;
+ return -1;
+}
+
+int sys_get_quota(const char *path, enum SMB_QUOTA_TYPE qtype, unid_t id, SMB_DISK_QUOTA *dp)
+{
+ int ret = -1;
+ int i;
+ BOOL ready = False;
+ char *mntpath = NULL;
+ char *bdev = NULL;
+ char *fs = NULL;
+
+ if (!path||!dp)
+ smb_panic("sys_get_quota: called with NULL pointer");
+
+ if (command_get_quota(path, qtype, id, dp)==0) {
+ return 0;
+ } else if (errno != ENOSYS) {
+ return -1;
+ }
+
+ if ((ret=sys_path_to_bdev(path,&mntpath,&bdev,&fs))!=0) {
+ DEBUG(0,("sys_path_to_bdev() failed for path [%s]!\n",path));
+ return ret;
+ }
+
+ errno = 0;
+ DEBUG(10,("sys_get_quota() uid(%u, %u)\n", (unsigned)getuid(), (unsigned)geteuid()));
+
+ for (i=0;(fs && sys_quota_backends[i].name && sys_quota_backends[i].get_quota);i++) {
+ if (strcmp(fs,sys_quota_backends[i].name)==0) {
+ ret = sys_quota_backends[i].get_quota(mntpath, bdev, qtype, id, dp);
+ if (ret!=0) {
+ DEBUG(3,("sys_get_%s_quota() failed for mntpath[%s] bdev[%s] qtype[%d] id[%d]: %s.\n",
+ fs,mntpath,bdev,qtype,(qtype==SMB_GROUP_QUOTA_TYPE?id.gid:id.uid),strerror(errno)));
+ } else {
+ DEBUG(10,("sys_get_%s_quota() called for mntpath[%s] bdev[%s] qtype[%d] id[%d].\n",
+ fs,mntpath,bdev,qtype,(qtype==SMB_GROUP_QUOTA_TYPE?id.gid:id.uid)));
+ }
+ ready = True;
+ break;
+ }
+ }
+
+ if (!ready) {
+ /* use the default vfs quota functions */
+ ret=sys_get_vfs_quota(mntpath, bdev, qtype, id, dp);
+ if (ret!=0) {
+ DEBUG(3,("sys_get_%s_quota() failed for mntpath[%s] bdev[%s] qtype[%d] id[%d]: %s\n",
+ "vfs",mntpath,bdev,qtype,(qtype==SMB_GROUP_QUOTA_TYPE?id.gid:id.uid),strerror(errno)));
+ } else {
+ DEBUG(10,("sys_get_%s_quota() called for mntpath[%s] bdev[%s] qtype[%d] id[%d].\n",
+ "vfs",mntpath,bdev,qtype,(qtype==SMB_GROUP_QUOTA_TYPE?id.gid:id.uid)));
+ }
+ }
+
+ SAFE_FREE(mntpath);
+ SAFE_FREE(bdev);
+ SAFE_FREE(fs);
+
+ if ((ret!=0)&& (errno == EDQUOT)) {
+ DEBUG(10,("sys_get_quota() warning over quota!\n"));
+ return 0;
+ }
+
+ return ret;
+}
+
+int sys_set_quota(const char *path, enum SMB_QUOTA_TYPE qtype, unid_t id, SMB_DISK_QUOTA *dp)
+{
+ int ret = -1;
+ int i;
+ BOOL ready = False;
+ char *mntpath = NULL;
+ char *bdev = NULL;
+ char *fs = NULL;
+
+ /* find the block device file */
+
+ if (!path||!dp)
+ smb_panic("get_smb_quota: called with NULL pointer");
+
+ if (command_set_quota(path, qtype, id, dp)==0) {
+ return 0;
+ } else if (errno != ENOSYS) {
+ return -1;
+ }
+
+ if ((ret=sys_path_to_bdev(path,&mntpath,&bdev,&fs))!=0) {
+ DEBUG(0,("sys_path_to_bdev() failed for path [%s]!\n",path));
+ return ret;
+ }
+
+ errno = 0;
+ DEBUG(10,("sys_set_quota() uid(%u, %u)\n", (unsigned)getuid(), (unsigned)geteuid()));
+
+ for (i=0;(fs && sys_quota_backends[i].name && sys_quota_backends[i].set_quota);i++) {
+ if (strcmp(fs,sys_quota_backends[i].name)==0) {
+ ret = sys_quota_backends[i].set_quota(mntpath, bdev, qtype, id, dp);
+ if (ret!=0) {
+ DEBUG(3,("sys_set_%s_quota() failed for mntpath[%s] bdev[%s] qtype[%d] id[%d]: %s.\n",
+ fs,mntpath,bdev,qtype,(qtype==SMB_GROUP_QUOTA_TYPE?id.gid:id.uid),strerror(errno)));
+ } else {
+ DEBUG(10,("sys_set_%s_quota() called for mntpath[%s] bdev[%s] qtype[%d] id[%d].\n",
+ fs,mntpath,bdev,qtype,(qtype==SMB_GROUP_QUOTA_TYPE?id.gid:id.uid)));
+ }
+ ready = True;
+ break;
+ }
+ }
+
+ if (!ready) {
+ /* use the default vfs quota functions */
+ ret=sys_set_vfs_quota(mntpath, bdev, qtype, id, dp);
+ if (ret!=0) {
+ DEBUG(3,("sys_set_%s_quota() failed for mntpath[%s] bdev[%s] qtype[%d] id[%d]: %s.\n",
+ "vfs",mntpath,bdev,qtype,(qtype==SMB_GROUP_QUOTA_TYPE?id.gid:id.uid),strerror(errno)));
+ } else {
+ DEBUG(10,("sys_set_%s_quota() called for mntpath[%s] bdev[%s] qtype[%d] id[%d].\n",
+ "vfs",mntpath,bdev,qtype,(qtype==SMB_GROUP_QUOTA_TYPE?id.gid:id.uid)));
+ }
+ }
+
+ SAFE_FREE(mntpath);
+ SAFE_FREE(bdev);
+ SAFE_FREE(fs);
+
+ if ((ret!=0)&& (errno == EDQUOT)) {
+ DEBUG(10,("sys_set_quota() warning over quota!\n"));
+ return 0;
+ }
+
+ return ret;
+}
+
+#else /* HAVE_SYS_QUOTAS */
+ void dummy_sysquotas_c(void)
+{
+ return;
+}
+#endif /* HAVE_SYS_QUOTAS */
+
diff --git a/source/lib/sysquotas_4A.c b/source/lib/sysquotas_4A.c
new file mode 100644
index 00000000000..ffb4123799d
--- /dev/null
+++ b/source/lib/sysquotas_4A.c
@@ -0,0 +1,339 @@
+/*
+ Unix SMB/CIFS implementation.
+ System QUOTA function wrappers for QUOTACTL_4A
+ 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 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"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_QUOTA
+
+#ifdef HAVE_QUOTACTL_4A
+/* long quotactl(int cmd, char *special, qid_t id, caddr_t addr) */
+/* this is used by: HPUX,IRIX */
+
+#ifdef HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+
+#ifdef HAVE_ASM_TYPES_H
+#include <asm/types.h>
+#endif
+
+#ifdef HAVE_SYS_QUOTA_H
+#include <sys/quota.h>
+#endif
+
+#ifndef Q_SETQLIM
+#define Q_SETQLIM Q_SETQUOTA
+#endif
+
+#ifndef QCMD
+#define QCMD(x,y) x
+#endif
+
+#ifndef QCMD
+#define QCMD(x,y) x
+#endif
+
+#ifdef GRPQUOTA
+#define HAVE_GROUP_QUOTA
+#endif
+
+#ifndef QUOTABLOCK_SIZE
+#define QUOTABLOCK_SIZE DEV_BSIZE
+#endif
+
+#ifdef HAVE_DQB_FSOFTLIMIT
+#define dqb_isoftlimit dqb_fsoftlimit
+#define dqb_ihardlimit dqb_fhardlimit
+#define dqb_curinodes dqb_curfiles
+#endif
+
+#ifdef INITQFNAMES
+#define USERQUOTAFILE_EXTENSION ".user"
+#else
+#define USERQUOTAFILE_EXTENSION ""
+#endif
+
+#if !defined(QUOTAFILENAME) && defined(QFILENAME)
+#define QUOTAFILENAME QFILENAME
+#endif
+
+/****************************************************************************
+ Abstract out the quotactl_4A get calls.
+****************************************************************************/
+int sys_get_vfs_quota(const char *path, const char *bdev, enum SMB_QUOTA_TYPE qtype, unid_t id, SMB_DISK_QUOTA *dp)
+{
+ int ret = -1;
+ uint32 qflags = 0;
+ struct dqblk D;
+ SMB_BIG_UINT bsize = (SMB_BIG_UINT)QUOTABLOCK_SIZE;
+
+ ZERO_STRUCT(D);
+ ZERO_STRUCT(*dp);
+ dp->qtype = qtype;
+
+ switch (qtype) {
+ case SMB_USER_QUOTA_TYPE:
+ DEBUG(10,("sys_get_vfs_quota: path[%s] bdev[%s] SMB_USER_QUOTA_TYPE uid[%u]\n",
+ path, bdev, (unsigned)id.uid));
+
+ if ((ret = quotactl(QCMD(Q_GETQUOTA,USRQUOTA), bdev, id.uid, (void *)&D))&&errno != EDQUOT) {
+ return ret;
+ }
+
+ if ((D.dqb_curblocks==0)&&
+ (D.dqb_bsoftlimit==0)&&
+ (D.dqb_bhardlimit==0)) {
+ /* the upper layer functions don't want empty quota records...*/
+ return -1;
+ }
+
+ break;
+#ifdef HAVE_GROUP_QUOTA
+ case SMB_GROUP_QUOTA_TYPE:
+ DEBUG(10,("sys_get_vfs_quota: path[%s] bdev[%s] SMB_GROUP_QUOTA_TYPE gid[%u]\n",
+ path, bdev, (unsigned)id.gid));
+
+ if ((ret = quotactl(QCMD(Q_GETQUOTA,GRPQUOTA), bdev, id.gid, (void *)&D))&&errno != EDQUOT) {
+ return ret;
+ }
+
+ if ((D.dqb_curblocks==0)&&
+ (D.dqb_bsoftlimit==0)&&
+ (D.dqb_bhardlimit==0)) {
+ /* the upper layer functions don't want empty quota records...*/
+ return -1;
+ }
+
+ break;
+#endif /* HAVE_GROUP_QUOTA */
+ case SMB_USER_FS_QUOTA_TYPE:
+ id.uid = getuid();
+
+ DEBUG(10,("sys_get_vfs_quota: path[%s] bdev[%s] SMB_USER_FS_QUOTA_TYPE (uid[%u])\n",
+ path, bdev, (unsigned)id.uid));
+
+ if ((ret = quotactl(QCMD(Q_GETQUOTA,USRQUOTA), bdev, id.uid, (void *)&D))==0) {
+ qflags |= QUOTAS_DENY_DISK;
+ }
+
+ ret = 0;
+ break;
+#ifdef HAVE_GROUP_QUOTA
+ case SMB_GROUP_FS_QUOTA_TYPE:
+ id.gid = getgid();
+
+ DEBUG(10,("sys_get_vfs_quota: path[%s] bdev[%s] SMB_GROUP_FS_QUOTA_TYPE (gid[%u])\n",
+ path, bdev, (unsigned)id.gid));
+
+ if ((ret = quotactl(QCMD(Q_GETQUOTA,GRPQUOTA), bdev, id.gid, (void *)&D))==0) {
+ qflags |= QUOTAS_DENY_DISK;
+ }
+
+ ret = 0;
+ break;
+#endif /* HAVE_GROUP_QUOTA */
+ default:
+ errno = ENOSYS;
+ return -1;
+ }
+
+ dp->bsize = bsize;
+ dp->softlimit = (SMB_BIG_UINT)D.dqb_bsoftlimit;
+ dp->hardlimit = (SMB_BIG_UINT)D.dqb_bhardlimit;
+ dp->ihardlimit = (SMB_BIG_UINT)D.dqb_ihardlimit;
+ dp->isoftlimit = (SMB_BIG_UINT)D.dqb_isoftlimit;
+ dp->curinodes = (SMB_BIG_UINT)D.dqb_curinodes;
+ dp->curblocks = (SMB_BIG_UINT)D.dqb_curblocks;
+
+
+ dp->qflags = qflags;
+
+ return ret;
+}
+
+/****************************************************************************
+ Abstract out the quotactl_4A set calls.
+****************************************************************************/
+int sys_set_vfs_quota(const char *path, const char *bdev, enum SMB_QUOTA_TYPE qtype, unid_t id, SMB_DISK_QUOTA *dp)
+{
+ int ret = -1;
+ uint32 qflags = 0;
+ uint32 oldqflags = 0;
+ struct dqblk D;
+ SMB_BIG_UINT bsize = (SMB_BIG_UINT)QUOTABLOCK_SIZE;
+
+ ZERO_STRUCT(D);
+
+ if (bsize == dp->bsize) {
+ D.dqb_bsoftlimit = dp->softlimit;
+ D.dqb_bhardlimit = dp->hardlimit;
+ D.dqb_ihardlimit = dp->ihardlimit;
+ D.dqb_isoftlimit = dp->isoftlimit;
+ } else {
+ D.dqb_bsoftlimit = (dp->softlimit*dp->bsize)/bsize;
+ D.dqb_bhardlimit = (dp->hardlimit*dp->bsize)/bsize;
+ D.dqb_ihardlimit = (dp->ihardlimit*dp->bsize)/bsize;
+ D.dqb_isoftlimit = (dp->isoftlimit*dp->bsize)/bsize;
+ }
+
+ qflags = dp->qflags;
+
+ switch (qtype) {
+ case SMB_USER_QUOTA_TYPE:
+ DEBUG(10,("sys_set_vfs_quota: path[%s] bdev[%s] SMB_USER_QUOTA_TYPE uid[%u]\n",
+ path, bdev, (unsigned)id.uid));
+
+ ret = quotactl(QCMD(Q_SETQLIM,USRQUOTA), bdev, id.uid, (void *)&D);
+ break;
+#ifdef HAVE_GROUP_QUOTA
+ case SMB_GROUP_QUOTA_TYPE:
+ DEBUG(10,("sys_set_vfs_quota: path[%s] bdev[%s] SMB_GROUP_QUOTA_TYPE gid[%u]\n",
+ path, bdev, (unsigned)id.gid));
+
+ ret = quotactl(QCMD(Q_SETQLIM,GRPQUOTA), bdev, id.gid, (void *)&D);
+ break;
+#endif /* HAVE_GROUP_QUOTA */
+ case SMB_USER_FS_QUOTA_TYPE:
+ /* this stuff didn't work as it should:
+ * switching on/off quota via quotactl()
+ * didn't work!
+ * So we just return 0
+ * --metze
+ *
+ * On HPUX we didn't have the mount path,
+ * we need to fix sys_path_to_bdev()
+ *
+ */
+ id.uid = getuid();
+ DEBUG(10,("sys_set_vfs_quota: path[%s] bdev[%s] SMB_USER_FS_QUOTA_TYPE (uid[%u])\n",
+ path, bdev, (unsigned)id.uid));
+
+#if 0
+ ret = quotactl(QCMD(Q_GETQUOTA,USRQUOTA), bdev, id.uid, (void *)&D);
+
+ if ((qflags&QUOTAS_DENY_DISK)||(qflags&QUOTAS_ENABLED)) {
+ if (ret == 0) {
+ char *quota_file = NULL;
+
+ asprintf(&quota_file,"/%s/%s%s",path, QUOTAFILENAME,USERQUOTAFILE_EXTENSION);
+ if (quota_file == NULL) {
+ DEBUG(0,("asprintf() failed!\n"));
+ errno = ENOMEM;
+ return -1;
+ }
+
+ ret = quotactl(QCMD(Q_QUOTAON,USRQUOTA), bdev, -1,(void *)quota_file);
+ } else {
+ ret = 0;
+ }
+ } else {
+ if (ret != 0) {
+ /* turn off */
+ ret = quotactl(QCMD(Q_QUOTAOFF,USRQUOTA), bdev, -1, (void *)0);
+ } else {
+ ret = 0;
+ }
+ }
+
+ DEBUG(0,("sys_set_vfs_quota: ret(%d) errno(%d)[%s] uid(%d) bdev[%s]\n",
+ ret,errno,strerror(errno),id.uid,bdev));
+#else
+ if ((ret = quotactl(QCMD(Q_GETQUOTA,USRQUOTA), bdev, id.uid, (void *)&D))==0) {
+ oldqflags |= QUOTAS_DENY_DISK;
+ }
+
+ if (oldqflags == qflags) {
+ ret = 0;
+ } else {
+ ret = -1;
+ }
+#endif
+ break;
+#ifdef HAVE_GROUP_QUOTA
+ case SMB_GROUP_FS_QUOTA_TYPE:
+ /* this stuff didn't work as it should:
+ * switching on/off quota via quotactl()
+ * didn't work!
+ * So we just return 0
+ * --metze
+ *
+ * On HPUX we didn't have the mount path,
+ * we need to fix sys_path_to_bdev()
+ *
+ */
+ id.gid = getgid();
+ DEBUG(10,("sys_set_vfs_quota: path[%s] bdev[%s] SMB_GROUP_FS_QUOTA_TYPE (gid[%u])\n",
+ path, bdev, (unsigned)id.gid));
+
+#if 0
+ ret = quotactl(QCMD(Q_GETQUOTA,GRPQUOTA), bdev, id, (void *)&D);
+
+ if ((qflags&QUOTAS_DENY_DISK)||(qflags&QUOTAS_ENABLED)) {
+ if (ret == 0) {
+ char *quota_file = NULL;
+
+ asprintf(&quota_file,"/%s/%s%s",path, QUOTAFILENAME,GROUPQUOTAFILE_EXTENSION);
+ if (quota_file == NULL) {
+ DEBUG(0,("asprintf() failed!\n"));
+ errno = ENOMEM;
+ return -1;
+ }
+
+ ret = quotactl(QCMD(Q_QUOTAON,GRPQUOTA), bdev, -1,(void *)quota_file);
+ } else {
+ ret = 0;
+ }
+ } else {
+ if (ret != 0) {
+ /* turn off */
+ ret = quotactl(QCMD(Q_QUOTAOFF,GRPQUOTA), bdev, -1, (void *)0);
+ } else {
+ ret = 0;
+ }
+ }
+
+ DEBUG(0,("sys_set_vfs_quota: ret(%d) errno(%d)[%s] uid(%d) bdev[%s]\n",
+ ret,errno,strerror(errno),id.gid,bdev));
+#else
+ if ((ret = quotactl(QCMD(Q_GETQUOTA,GRPQUOTA), bdev, id.gid, (void *)&D))==0) {
+ oldqflags |= QUOTAS_DENY_DISK;
+ }
+
+ if (oldqflags == qflags) {
+ ret = 0;
+ } else {
+ ret = -1;
+ }
+#endif
+ break;
+#endif /* HAVE_GROUP_QUOTA */
+ default:
+ errno = ENOSYS;
+ return -1;
+ }
+
+ return ret;
+}
+
+#else /* HAVE_QUOTACTL_4A */
+ void dummy_sysquotas_4A(void){}
+#endif /* HAVE_QUOTACTL_4A */
diff --git a/source/lib/sysquotas_linux.c b/source/lib/sysquotas_linux.c
new file mode 100644
index 00000000000..3867c1b0f9b
--- /dev/null
+++ b/source/lib/sysquotas_linux.c
@@ -0,0 +1,560 @@
+/*
+ Unix SMB/CIFS implementation.
+ System QUOTA function wrappers for LINUX
+ 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 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"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_QUOTA
+
+#ifdef HAVE_QUOTACTL_LINUX
+
+#include "samba_linux_quota.h"
+
+/****************************************************************************
+ Abstract out the v1 Linux quota get calls.
+****************************************************************************/
+static int sys_get_linux_v1_quota(const char *path, const char *bdev, enum SMB_QUOTA_TYPE qtype, unid_t id, SMB_DISK_QUOTA *dp)
+{
+ int ret = -1;
+ uint32 qflags = 0;
+ struct v1_kern_dqblk D;
+ SMB_BIG_UINT bsize = (SMB_BIG_UINT)QUOTABLOCK_SIZE;
+
+ ZERO_STRUCT(D);
+
+ switch (qtype) {
+ case SMB_USER_QUOTA_TYPE:
+ DEBUG(10,("sys_get_linux_v1_quota: path[%s] bdev[%s] SMB_USER_QUOTA_TYPE uid[%u]\n",
+ path, bdev, (unsigned)id.uid));
+
+ if ((ret = quotactl(QCMD(Q_V1_GETQUOTA,USRQUOTA), bdev, id.uid, (caddr_t)&D))&&errno != EDQUOT) {
+ return ret;
+ }
+
+ break;
+ case SMB_GROUP_QUOTA_TYPE:
+ DEBUG(10,("sys_get_linux_v1_quota: path[%s] bdev[%s] SMB_GROUP_QUOTA_TYPE gid[%u]\n",
+ path, bdev, (unsigned)id.gid));
+
+ if ((ret = quotactl(QCMD(Q_V1_GETQUOTA,GRPQUOTA), bdev, id.gid, (caddr_t)&D))&&errno != EDQUOT) {
+ return ret;
+ }
+
+ break;
+ case SMB_USER_FS_QUOTA_TYPE:
+ DEBUG(10,("sys_get_linux_v1_quota: path[%s] bdev[%s] SMB_USER_FS_QUOTA_TYPE (uid[%u])\n",
+ path, bdev, (unsigned)id.uid));
+
+ if ((ret = quotactl(QCMD(Q_V1_GETQUOTA,USRQUOTA), bdev, id.uid, (caddr_t)&D))==0) {
+ qflags |= QUOTAS_DENY_DISK;
+ }
+
+ break;
+ case SMB_GROUP_FS_QUOTA_TYPE:
+ DEBUG(10,("sys_get_linux_v1_quota: path[%s] bdev[%s] SMB_GROUP_FS_QUOTA_TYPE (gid[%u])\n",
+ path, bdev, (unsigned)id.gid));
+
+ if ((ret = quotactl(QCMD(Q_V1_GETQUOTA,GRPQUOTA), bdev, id.gid, (caddr_t)&D))==0) {
+ qflags |= QUOTAS_DENY_DISK;
+ }
+
+ break;
+ default:
+ errno = ENOSYS;
+ return -1;
+ }
+
+ dp->bsize = bsize;
+ dp->softlimit = (SMB_BIG_UINT)D.dqb_bsoftlimit;
+ dp->hardlimit = (SMB_BIG_UINT)D.dqb_bhardlimit;
+ dp->ihardlimit = (SMB_BIG_UINT)D.dqb_ihardlimit;
+ dp->isoftlimit = (SMB_BIG_UINT)D.dqb_isoftlimit;
+ dp->curinodes = (SMB_BIG_UINT)D.dqb_curinodes;
+ dp->curblocks = (SMB_BIG_UINT)D.dqb_curblocks;
+
+
+ dp->qflags = qflags;
+
+ return ret;
+}
+
+/****************************************************************************
+ Abstract out the v1 Linux quota set calls.
+****************************************************************************/
+static int sys_set_linux_v1_quota(const char *path, const char *bdev, enum SMB_QUOTA_TYPE qtype, unid_t id, SMB_DISK_QUOTA *dp)
+{
+ int ret = -1;
+ uint32 qflags = 0;
+ uint32 oldqflags = 0;
+ struct v1_kern_dqblk D;
+ SMB_BIG_UINT bsize = (SMB_BIG_UINT)QUOTABLOCK_SIZE;
+
+ ZERO_STRUCT(D);
+
+ if (bsize == dp->bsize) {
+ D.dqb_bsoftlimit = dp->softlimit;
+ D.dqb_bhardlimit = dp->hardlimit;
+ D.dqb_ihardlimit = dp->ihardlimit;
+ D.dqb_isoftlimit = dp->isoftlimit;
+ } else {
+ D.dqb_bsoftlimit = (dp->softlimit*dp->bsize)/bsize;
+ D.dqb_bhardlimit = (dp->hardlimit*dp->bsize)/bsize;
+ D.dqb_ihardlimit = (dp->ihardlimit*dp->bsize)/bsize;
+ D.dqb_isoftlimit = (dp->isoftlimit*dp->bsize)/bsize;
+ }
+
+ qflags = dp->qflags;
+
+ switch (qtype) {
+ case SMB_USER_QUOTA_TYPE:
+ DEBUG(10,("sys_set_linux_v1_quota: path[%s] bdev[%s] SMB_USER_QUOTA_TYPE uid[%u]\n",
+ path, bdev, (unsigned)id.uid));
+
+ ret = quotactl(QCMD(Q_V1_SETQUOTA,USRQUOTA), bdev, id.uid, (caddr_t)&D);
+ break;
+ case SMB_GROUP_QUOTA_TYPE:
+ DEBUG(10,("sys_set_linux_v1_quota: path[%s] bdev[%s] SMB_GROUP_QUOTA_TYPE gid[%u]\n",
+ path, bdev, (unsigned)id.gid));
+
+ ret = quotactl(QCMD(Q_V1_SETQUOTA,GRPQUOTA), bdev, id.gid, (caddr_t)&D);
+ break;
+ case SMB_USER_FS_QUOTA_TYPE:
+ DEBUG(10,("sys_set_linux_v1_quota: path[%s] bdev[%s] SMB_USER_FS_QUOTA_TYPE (uid[%u])\n",
+ path, bdev, (unsigned)id.uid));
+
+ if ((ret = quotactl(QCMD(Q_V1_GETQUOTA,USRQUOTA), bdev, id.uid, (caddr_t)&D))==0) {
+ oldqflags |= QUOTAS_DENY_DISK;
+ }
+
+ break;
+ case SMB_GROUP_FS_QUOTA_TYPE:
+ DEBUG(10,("sys_set_linux_v1_quota: path[%s] bdev[%s] SMB_GROUP_FS_QUOTA_TYPE (gid[%u])\n",
+ path, bdev, (unsigned)id.gid));
+
+ if ((ret = quotactl(QCMD(Q_V1_GETQUOTA,GRPQUOTA), bdev, id.gid, (caddr_t)&D))==0) {
+ oldqflags |= QUOTAS_DENY_DISK;
+ }
+
+ break;
+ default:
+ errno = ENOSYS;
+ return -1;
+ }
+
+ return ret;
+}
+
+/****************************************************************************
+ Abstract out the v2 Linux quota get calls.
+****************************************************************************/
+static int sys_get_linux_v2_quota(const char *path, const char *bdev, enum SMB_QUOTA_TYPE qtype, unid_t id, SMB_DISK_QUOTA *dp)
+{
+ int ret = -1;
+ uint32 qflags = 0;
+ struct v2_kern_dqblk D;
+ SMB_BIG_UINT bsize = (SMB_BIG_UINT)QUOTABLOCK_SIZE;
+
+ ZERO_STRUCT(D);
+
+ switch (qtype) {
+ case SMB_USER_QUOTA_TYPE:
+ DEBUG(10,("sys_get_linux_v2_quota: path[%s] bdev[%s] SMB_USER_QUOTA_TYPE uid[%u]\n",
+ path, bdev, (unsigned)id.uid));
+
+ if ((ret = quotactl(QCMD(Q_V2_GETQUOTA,USRQUOTA), bdev, id.uid, (caddr_t)&D))&&errno != EDQUOT) {
+ return ret;
+ }
+
+ break;
+ case SMB_GROUP_QUOTA_TYPE:
+ DEBUG(10,("sys_get_linux_v2_quota: path[%s] bdev[%s] SMB_GROUP_QUOTA_TYPE gid[%u]\n",
+ path, bdev, (unsigned)id.gid));
+
+ if ((ret = quotactl(QCMD(Q_V2_GETQUOTA,GRPQUOTA), bdev, id.gid, (caddr_t)&D))&&errno != EDQUOT) {
+ return ret;
+ }
+
+ break;
+ case SMB_USER_FS_QUOTA_TYPE:
+ DEBUG(10,("sys_get_linux_v2_quota: path[%s] bdev[%s] SMB_USER_FS_QUOTA_TYPE (uid[%u])\n",
+ path, bdev, (unsigned)id.uid));
+
+ if ((ret = quotactl(QCMD(Q_V2_GETQUOTA,USRQUOTA), bdev, id.uid, (caddr_t)&D))==0) {
+ qflags |= QUOTAS_DENY_DISK;
+ }
+
+ break;
+ case SMB_GROUP_FS_QUOTA_TYPE:
+ DEBUG(10,("sys_get_linux_v2_quota: path[%s] bdev[%s] SMB_GROUP_FS_QUOTA_TYPE (gid[%u])\n",
+ path, bdev, (unsigned)id.gid));
+
+ if ((ret = quotactl(QCMD(Q_V2_GETQUOTA,GRPQUOTA), bdev, id.gid, (caddr_t)&D))==0) {
+ qflags |= QUOTAS_DENY_DISK;
+ }
+
+ break;
+ default:
+ errno = ENOSYS;
+ return -1;
+ }
+
+ dp->bsize = bsize;
+ dp->softlimit = (SMB_BIG_UINT)D.dqb_bsoftlimit;
+ dp->hardlimit = (SMB_BIG_UINT)D.dqb_bhardlimit;
+ dp->ihardlimit = (SMB_BIG_UINT)D.dqb_ihardlimit;
+ dp->isoftlimit = (SMB_BIG_UINT)D.dqb_isoftlimit;
+ dp->curinodes = (SMB_BIG_UINT)D.dqb_curinodes;
+ dp->curblocks = (SMB_BIG_UINT)D.dqb_curspace/bsize;
+
+
+ dp->qflags = qflags;
+
+ return ret;
+}
+
+/****************************************************************************
+ Abstract out the v2 Linux quota set calls.
+****************************************************************************/
+static int sys_set_linux_v2_quota(const char *path, const char *bdev, enum SMB_QUOTA_TYPE qtype, unid_t id, SMB_DISK_QUOTA *dp)
+{
+ int ret = -1;
+ uint32 qflags = 0;
+ uint32 oldqflags = 0;
+ struct v2_kern_dqblk D;
+ SMB_BIG_UINT bsize = (SMB_BIG_UINT)QUOTABLOCK_SIZE;
+
+ ZERO_STRUCT(D);
+
+ if (bsize == dp->bsize) {
+ D.dqb_bsoftlimit = dp->softlimit;
+ D.dqb_bhardlimit = dp->hardlimit;
+ D.dqb_ihardlimit = dp->ihardlimit;
+ D.dqb_isoftlimit = dp->isoftlimit;
+ } else {
+ D.dqb_bsoftlimit = (dp->softlimit*dp->bsize)/bsize;
+ D.dqb_bhardlimit = (dp->hardlimit*dp->bsize)/bsize;
+ D.dqb_ihardlimit = (dp->ihardlimit*dp->bsize)/bsize;
+ D.dqb_isoftlimit = (dp->isoftlimit*dp->bsize)/bsize;
+ }
+
+ qflags = dp->qflags;
+
+ switch (qtype) {
+ case SMB_USER_QUOTA_TYPE:
+ DEBUG(10,("sys_set_linux_v2_quota: path[%s] bdev[%s] SMB_USER_QUOTA_TYPE uid[%u]\n",
+ path, bdev, (unsigned)id.uid));
+
+ ret = quotactl(QCMD(Q_V2_SETQUOTA,USRQUOTA), bdev, id.uid, (caddr_t)&D);
+ break;
+ case SMB_GROUP_QUOTA_TYPE:
+ DEBUG(10,("sys_set_linux_v2_quota: path[%s] bdev[%s] SMB_GROUP_QUOTA_TYPE gid[%u]\n",
+ path, bdev, (unsigned)id.gid));
+
+ ret = quotactl(QCMD(Q_V2_SETQUOTA,GRPQUOTA), bdev, id.gid, (caddr_t)&D);
+ break;
+ case SMB_USER_FS_QUOTA_TYPE:
+ DEBUG(10,("sys_set_linux_v2_quota: path[%s] bdev[%s] SMB_USER_FS_QUOTA_TYPE (uid[%u])\n",
+ path, bdev, (unsigned)id.uid));
+
+ if ((ret = quotactl(QCMD(Q_V2_GETQUOTA,USRQUOTA), bdev, id.uid, (caddr_t)&D))==0) {
+ oldqflags |= QUOTAS_DENY_DISK;
+ }
+
+ break;
+ case SMB_GROUP_FS_QUOTA_TYPE:
+ DEBUG(10,("sys_set_linux_v2_quota: path[%s] bdev[%s] SMB_GROUP_FS_QUOTA_TYPE (gid[%u])\n",
+ path, bdev, (unsigned)id.gid));
+
+ if ((ret = quotactl(QCMD(Q_V2_GETQUOTA,GRPQUOTA), bdev, id.gid, (caddr_t)&D))==0) {
+ oldqflags |= QUOTAS_DENY_DISK;
+ }
+
+ break;
+ default:
+ errno = ENOSYS;
+ return -1;
+ }
+
+ return ret;
+}
+
+/****************************************************************************
+ Abstract out the generic Linux quota get calls.
+****************************************************************************/
+static int sys_get_linux_gen_quota(const char *path, const char *bdev, enum SMB_QUOTA_TYPE qtype, unid_t id, SMB_DISK_QUOTA *dp)
+{
+ int ret = -1;
+ uint32 qflags = 0;
+ struct if_dqblk D;
+ SMB_BIG_UINT bsize = (SMB_BIG_UINT)QUOTABLOCK_SIZE;
+
+ ZERO_STRUCT(D);
+
+ switch (qtype) {
+ case SMB_USER_QUOTA_TYPE:
+ DEBUG(10,("sys_get_linux_gen_quota: path[%s] bdev[%s] SMB_USER_QUOTA_TYPE uid[%u]\n",
+ path, bdev, (unsigned)id.uid));
+
+ if ((ret = quotactl(QCMD(Q_GETQUOTA,USRQUOTA), bdev, id.uid, (caddr_t)&D))&&errno != EDQUOT) {
+ return ret;
+ }
+
+ break;
+ case SMB_GROUP_QUOTA_TYPE:
+ DEBUG(10,("sys_get_linux_gen_quota: path[%s] bdev[%s] SMB_GROUP_QUOTA_TYPE gid[%u]\n",
+ path, bdev, (unsigned)id.gid));
+
+ if ((ret = quotactl(QCMD(Q_GETQUOTA,GRPQUOTA), bdev, id.gid, (caddr_t)&D))&&errno != EDQUOT) {
+ return ret;
+ }
+
+ break;
+ case SMB_USER_FS_QUOTA_TYPE:
+ DEBUG(10,("sys_get_linux_gen_quota: path[%s] bdev[%s] SMB_USER_FS_QUOTA_TYPE (uid[%u])\n",
+ path, bdev, (unsigned)id.uid));
+
+ if ((ret = quotactl(QCMD(Q_GETQUOTA,USRQUOTA), bdev, id.uid, (caddr_t)&D))==0) {
+ qflags |= QUOTAS_DENY_DISK;
+ }
+
+ break;
+ case SMB_GROUP_FS_QUOTA_TYPE:
+ DEBUG(10,("sys_get_linux_gen_quota: path[%s] bdev[%s] SMB_GROUP_FS_QUOTA_TYPE (gid[%u])\n",
+ path, bdev, (unsigned)id.gid));
+
+ if ((ret = quotactl(QCMD(Q_GETQUOTA,GRPQUOTA), bdev, id.gid, (caddr_t)&D))==0) {
+ qflags |= QUOTAS_DENY_DISK;
+ }
+
+ break;
+ default:
+ errno = ENOSYS;
+ return -1;
+ }
+
+ dp->bsize = bsize;
+ dp->softlimit = (SMB_BIG_UINT)D.dqb_bsoftlimit;
+ dp->hardlimit = (SMB_BIG_UINT)D.dqb_bhardlimit;
+ dp->ihardlimit = (SMB_BIG_UINT)D.dqb_ihardlimit;
+ dp->isoftlimit = (SMB_BIG_UINT)D.dqb_isoftlimit;
+ dp->curinodes = (SMB_BIG_UINT)D.dqb_curinodes;
+ dp->curblocks = (SMB_BIG_UINT)D.dqb_curspace/bsize;
+
+
+ dp->qflags = qflags;
+
+ return ret;
+}
+
+/****************************************************************************
+ Abstract out the gen Linux quota set calls.
+****************************************************************************/
+static int sys_set_linux_gen_quota(const char *path, const char *bdev, enum SMB_QUOTA_TYPE qtype, unid_t id, SMB_DISK_QUOTA *dp)
+{
+ int ret = -1;
+ uint32 qflags = 0;
+ uint32 oldqflags = 0;
+ struct if_dqblk D;
+ SMB_BIG_UINT bsize = (SMB_BIG_UINT)QUOTABLOCK_SIZE;
+
+ ZERO_STRUCT(D);
+
+ if (bsize == dp->bsize) {
+ D.dqb_bsoftlimit = dp->softlimit;
+ D.dqb_bhardlimit = dp->hardlimit;
+ D.dqb_ihardlimit = dp->ihardlimit;
+ D.dqb_isoftlimit = dp->isoftlimit;
+ } else {
+ D.dqb_bsoftlimit = (dp->softlimit*dp->bsize)/bsize;
+ D.dqb_bhardlimit = (dp->hardlimit*dp->bsize)/bsize;
+ D.dqb_ihardlimit = (dp->ihardlimit*dp->bsize)/bsize;
+ D.dqb_isoftlimit = (dp->isoftlimit*dp->bsize)/bsize;
+ }
+
+ qflags = dp->qflags;
+
+ switch (qtype) {
+ case SMB_USER_QUOTA_TYPE:
+ DEBUG(10,("sys_set_linux_gen_quota: path[%s] bdev[%s] SMB_USER_QUOTA_TYPE uid[%u]\n",
+ path, bdev, (unsigned)id.uid));
+
+ ret = quotactl(QCMD(Q_SETQUOTA,USRQUOTA), bdev, id.uid, (caddr_t)&D);
+ break;
+ case SMB_GROUP_QUOTA_TYPE:
+ DEBUG(10,("sys_set_linux_gen_quota: path[%s] bdev[%s] SMB_GROUP_QUOTA_TYPE gid[%u]\n",
+ path, bdev, (unsigned)id.gid));
+
+ ret = quotactl(QCMD(Q_SETQUOTA,GRPQUOTA), bdev, id.gid, (caddr_t)&D);
+ break;
+ case SMB_USER_FS_QUOTA_TYPE:
+ DEBUG(10,("sys_set_linux_gen_quota: path[%s] bdev[%s] SMB_USER_FS_QUOTA_TYPE (uid[%u])\n",
+ path, bdev, (unsigned)id.uid));
+
+ if ((ret = quotactl(QCMD(Q_GETQUOTA,USRQUOTA), bdev, id.uid, (caddr_t)&D))==0) {
+ oldqflags |= QUOTAS_DENY_DISK;
+ }
+
+ break;
+ case SMB_GROUP_FS_QUOTA_TYPE:
+ DEBUG(10,("sys_set_linux_gen_quota: path[%s] bdev[%s] SMB_GROUP_FS_QUOTA_TYPE (gid[%u])\n",
+ path, bdev, (unsigned)id.gid));
+
+ if ((ret = quotactl(QCMD(Q_GETQUOTA,GRPQUOTA), bdev, id.gid, (caddr_t)&D))==0) {
+ oldqflags |= QUOTAS_DENY_DISK;
+ }
+
+ break;
+ default:
+ errno = ENOSYS;
+ return -1;
+ }
+
+ return ret;
+}
+
+/****************************************************************************
+ Abstract out the Linux quota get calls.
+****************************************************************************/
+int sys_get_vfs_quota(const char *path, const char *bdev, enum SMB_QUOTA_TYPE qtype, unid_t id, SMB_DISK_QUOTA *dp)
+{
+ int ret = -1;
+
+ if (!path||!bdev||!dp)
+ smb_panic("sys_set_vfs_quota: called with NULL pointer");
+
+ ZERO_STRUCT(*dp);
+ dp->qtype = qtype;
+
+ switch (qtype) {
+ case SMB_USER_QUOTA_TYPE:
+ case SMB_GROUP_QUOTA_TYPE:
+ if ((ret=sys_get_linux_gen_quota(path, bdev, qtype, id, dp))&&errno != EDQUOT) {
+ if ((ret=sys_get_linux_v2_quota(path, bdev, qtype, id, dp))&&errno != EDQUOT) {
+ if ((ret=sys_get_linux_v1_quota(path, bdev, qtype, id, dp))&&errno != EDQUOT) {
+ return ret;
+ }
+ }
+ }
+
+ if ((dp->curblocks==0)&&
+ (dp->softlimit==0)&&
+ (dp->hardlimit==0)) {
+ /* the upper layer functions don't want empty quota records...*/
+ return -1;
+ }
+
+ break;
+ case SMB_USER_FS_QUOTA_TYPE:
+ id.uid = getuid();
+
+ if ((ret=sys_get_linux_gen_quota(path, bdev, qtype, id, dp))&&errno != EDQUOT) {
+ if ((ret=sys_get_linux_v2_quota(path, bdev, qtype, id, dp))&&errno != EDQUOT) {
+ ret=sys_get_linux_v1_quota(path, bdev, qtype, id, dp);
+ }
+ }
+
+ ret = 0;
+ break;
+ case SMB_GROUP_FS_QUOTA_TYPE:
+ id.gid = getgid();
+
+ if ((ret=sys_get_linux_gen_quota(path, bdev, qtype, id, dp))&&errno != EDQUOT) {
+ if ((ret=sys_get_linux_v2_quota(path, bdev, qtype, id, dp))&&errno != EDQUOT) {
+ ret=sys_get_linux_v1_quota(path, bdev, qtype, id, dp);
+ }
+ }
+
+ ret = 0;
+ break;
+ default:
+ errno = ENOSYS;
+ return -1;
+ }
+
+ return ret;
+}
+
+/****************************************************************************
+ Abstract out the Linux quota set calls.
+****************************************************************************/
+int sys_set_vfs_quota(const char *path, const char *bdev, enum SMB_QUOTA_TYPE qtype, unid_t id, SMB_DISK_QUOTA *dp)
+{
+ int ret = -1;
+ uint32 oldqflags = 0;
+
+ if (!path||!bdev||!dp)
+ smb_panic("sys_set_vfs_quota: called with NULL pointer");
+
+ oldqflags = dp->qflags;
+
+ switch (qtype) {
+ case SMB_USER_QUOTA_TYPE:
+ case SMB_GROUP_QUOTA_TYPE:
+ if ((ret=sys_set_linux_gen_quota(path, bdev, qtype, id, dp))) {
+ if ((ret=sys_set_linux_v2_quota(path, bdev, qtype, id, dp))) {
+ if ((ret=sys_set_linux_v1_quota(path, bdev, qtype, id, dp))) {
+ return ret;
+ }
+ }
+ }
+ break;
+ case SMB_USER_FS_QUOTA_TYPE:
+ id.uid = getuid();
+
+ if ((ret=sys_get_linux_gen_quota(path, bdev, qtype, id, dp))) {
+ if ((ret=sys_get_linux_v2_quota(path, bdev, qtype, id, dp))) {
+ ret=sys_get_linux_v1_quota(path, bdev, qtype, id, dp);
+ }
+ }
+
+ if (oldqflags == dp->qflags) {
+ ret = 0;
+ } else {
+ ret = -1;
+ }
+ break;
+ case SMB_GROUP_FS_QUOTA_TYPE:
+ id.gid = getgid();
+
+ if ((ret=sys_get_linux_gen_quota(path, bdev, qtype, id, dp))) {
+ if ((ret=sys_get_linux_v2_quota(path, bdev, qtype, id, dp))) {
+ ret=sys_get_linux_v1_quota(path, bdev, qtype, id, dp);
+ }
+ }
+
+ if (oldqflags == dp->qflags) {
+ ret = 0;
+ } else {
+ ret = -1;
+ }
+
+ break;
+ default:
+ errno = ENOSYS;
+ return -1;
+ }
+
+ return ret;
+}
+
+#else /* HAVE_QUOTACTL_LINUX */
+ void dummy_sysquotas_linux(void){}
+#endif /* HAVE_QUOTACTL_LINUX */
diff --git a/source/lib/sysquotas_xfs.c b/source/lib/sysquotas_xfs.c
new file mode 100644
index 00000000000..9fe4ec0d992
--- /dev/null
+++ b/source/lib/sysquotas_xfs.c
@@ -0,0 +1,333 @@
+/*
+ Unix SMB/CIFS implementation.
+ System QUOTA function wrappers for XFS
+ 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 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"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_QUOTA
+
+#ifndef HAVE_SYS_QUOTAS
+#ifdef HAVE_XFS_QUOTAS
+#undef HAVE_XFS_QUOTAS
+#endif
+#endif
+
+#ifdef HAVE_XFS_QUOTAS
+
+#ifdef HAVE_LINUX_XFS_QUOTAS
+#include "samba_linux_quota.h"
+#include "samba_xfs_quota.h"
+#define HAVE_GROUP_QUOTA
+#else /* IRIX */
+#include <sys/quota.h>
+#endif
+
+/* on IRIX */
+#ifndef Q_XQUOTAON
+#define Q_XQUOTAON Q_QUOTAON
+#endif /* Q_XQUOTAON */
+#ifndef Q_XQUOTAOFF
+#define Q_XQUOTAOFF Q_QUOTAOFF
+#endif /* Q_XQUOTAOFF */
+#ifndef Q_XGETQSTAT
+#define Q_XGETQSTAT Q_GETQSTAT
+#endif /* Q_XGETQSTAT */
+
+/* currently doesn't support Group and Project quotas on IRIX
+ */
+
+#ifndef QCMD
+#define QCMD(x,y) x
+#endif
+
+/*
+ * IRIX has BBSIZE in <sys/param.h>
+ */
+#ifndef BBSHIFT
+#define BBSHIFT 9
+#endif /* BBSHIFT */
+#ifndef BBSIZE
+#define BBSIZE (1<<BBSHIFT)
+#endif /* BBSIZE */
+
+/****************************************************************************
+ Abstract out the XFS Quota Manager quota get call.
+****************************************************************************/
+int sys_get_xfs_quota(const char *path, const char *bdev, enum SMB_QUOTA_TYPE qtype, unid_t id, SMB_DISK_QUOTA *dp)
+{
+ int ret = -1;
+ uint32 qflags = 0;
+ SMB_BIG_UINT bsize = (SMB_BIG_UINT)BBSIZE;
+ struct fs_disk_quota D;
+ struct fs_quota_stat F;
+ ZERO_STRUCT(D);
+ ZERO_STRUCT(F);
+
+ if (!bdev||!dp)
+ smb_panic("sys_get_xfs_quota: called with NULL pointer");
+
+ ZERO_STRUCT(*dp);
+ dp->qtype = qtype;
+
+ switch (qtype) {
+ case SMB_USER_QUOTA_TYPE:
+ DEBUG(10,("sys_get_xfs_quota: path[%s] bdev[%s] SMB_USER_QUOTA_TYPE uid[%u]\n",
+ path, bdev, (unsigned)id.uid));
+
+ if ((ret=quotactl(QCMD(Q_XGETQUOTA,USRQUOTA), bdev, id.uid, (caddr_t)&D)))
+ return ret;
+ break;
+#ifdef HAVE_GROUP_QUOTA
+ case SMB_GROUP_QUOTA_TYPE:
+ DEBUG(10,("sys_get_xfs_quota: path[%s] bdev[%s] SMB_GROUP_QUOTA_TYPE gid[%u]\n",
+ path, bdev, (unsigned)id.gid));
+
+ if ((ret=quotactl(QCMD(Q_XGETQUOTA,GRPQUOTA), bdev, id.gid, (caddr_t)&D)))
+ return ret;
+ break;
+#endif /* HAVE_GROUP_QUOTA */
+ case SMB_USER_FS_QUOTA_TYPE:
+ DEBUG(10,("sys_get_xfs_quota: path[%s] bdev[%s] SMB_USER_FS_QUOTA_TYPE (uid[%u])\n",
+ path, bdev, (unsigned)id.uid));
+
+ quotactl(QCMD(Q_XGETQSTAT,USRQUOTA), bdev, -1, (caddr_t)&F);
+
+ if (F.qs_flags & XFS_QUOTA_UDQ_ENFD) {
+ qflags |= QUOTAS_DENY_DISK;
+ }
+ else if (F.qs_flags & XFS_QUOTA_UDQ_ACCT) {
+ qflags |= QUOTAS_ENABLED;
+ }
+
+ ret = 0;
+
+ break;
+#ifdef HAVE_GROUP_QUOTA
+ case SMB_GROUP_FS_QUOTA_TYPE:
+ DEBUG(10,("sys_get_xfs_quota: path[%s] bdev[%s] SMB_GROUP_FS_QUOTA_TYPE (gid[%u])\n",
+ path, bdev, (unsigned)id.gid));
+
+ quotactl(QCMD(Q_XGETQSTAT,GRPQUOTA), bdev, -1, (caddr_t)&F);
+
+ if (F.qs_flags & XFS_QUOTA_GDQ_ENFD) {
+ qflags |= QUOTAS_DENY_DISK;
+ }
+ else if (F.qs_flags & XFS_QUOTA_GDQ_ACCT) {
+ qflags |= QUOTAS_ENABLED;
+ }
+
+ ret = 0;
+
+ break;
+#endif /* HAVE_GROUP_QUOTA */
+ default:
+ errno = ENOSYS;
+ return -1;
+ }
+
+ dp->bsize = bsize;
+ dp->softlimit = (SMB_BIG_UINT)D.d_blk_softlimit;
+ dp->hardlimit = (SMB_BIG_UINT)D.d_blk_hardlimit;
+ dp->ihardlimit = (SMB_BIG_UINT)D.d_ino_hardlimit;
+ dp->isoftlimit = (SMB_BIG_UINT)D.d_ino_softlimit;
+ dp->curinodes = (SMB_BIG_UINT)D.d_icount;
+ dp->curblocks = (SMB_BIG_UINT)D.d_bcount;
+ dp->qflags = qflags;
+
+ return ret;
+}
+
+/****************************************************************************
+ Abstract out the XFS Quota Manager quota set call.
+****************************************************************************/
+int sys_set_xfs_quota(const char *path, const char *bdev, enum SMB_QUOTA_TYPE qtype, unid_t id, SMB_DISK_QUOTA *dp)
+{
+ int ret = -1;
+ uint32 qflags = 0;
+ SMB_BIG_UINT bsize = (SMB_BIG_UINT)BBSIZE;
+ struct fs_disk_quota D;
+ struct fs_quota_stat F;
+ int q_on = 0;
+ int q_off = 0;
+ ZERO_STRUCT(D);
+ ZERO_STRUCT(F);
+
+ if (!bdev||!dp)
+ smb_panic("sys_set_xfs_quota: called with NULL pointer");
+
+ if (bsize == dp->bsize) {
+ D.d_blk_softlimit = dp->softlimit;
+ D.d_blk_hardlimit = dp->hardlimit;
+ D.d_ino_hardlimit = dp->ihardlimit;
+ D.d_ino_softlimit = dp->isoftlimit;
+ } else {
+ D.d_blk_softlimit = (dp->softlimit*dp->bsize)/bsize;
+ D.d_blk_hardlimit = (dp->hardlimit*dp->bsize)/bsize;
+ D.d_ino_hardlimit = (dp->ihardlimit*dp->bsize)/bsize;
+ D.d_ino_softlimit = (dp->isoftlimit*dp->bsize)/bsize;
+ }
+
+ qflags = dp->qflags;
+
+ switch (qtype) {
+ case SMB_USER_QUOTA_TYPE:
+ DEBUG(10,("sys_set_xfs_quota: path[%s] bdev[%s] SMB_USER_QUOTA_TYPE uid[%u]\n",
+ path, bdev, (unsigned)id.uid));
+
+ D.d_fieldmask |= FS_DQ_LIMIT_MASK;
+ ret = quotactl(QCMD(Q_XSETQLIM,USRQUOTA), bdev, id.uid, (caddr_t)&D);
+ break;
+#ifdef HAVE_GROUP_QUOTA
+ case SMB_GROUP_QUOTA_TYPE:
+ DEBUG(10,("sys_set_xfs_quota: path[%s] bdev[%s] SMB_GROUP_QUOTA_TYPE gid[%u]\n",
+ path, bdev, (unsigned)id.gid));
+
+ D.d_fieldmask |= FS_DQ_LIMIT_MASK;
+ ret = quotactl(QCMD(Q_XSETQLIM,GRPQUOTA), bdev, id.gid, (caddr_t)&D);
+ break;
+#endif /* HAVE_GROUP_QUOTA */
+ case SMB_USER_FS_QUOTA_TYPE:
+ DEBUG(10,("sys_set_xfs_quota: path[%s] bdev[%s] SMB_USER_FS_QUOTA_TYPE (uid[%u])\n",
+ path, bdev, (unsigned)id.uid));
+
+ quotactl(QCMD(Q_XGETQSTAT,USRQUOTA), bdev, -1, (caddr_t)&F);
+
+ if (qflags & QUOTAS_DENY_DISK) {
+ if (!(F.qs_flags & XFS_QUOTA_UDQ_ENFD))
+ q_on |= XFS_QUOTA_UDQ_ENFD;
+ if (!(F.qs_flags & XFS_QUOTA_UDQ_ACCT))
+ q_on |= XFS_QUOTA_UDQ_ACCT;
+
+ if (q_on != 0) {
+ ret = quotactl(QCMD(Q_XQUOTAON,USRQUOTA),bdev, -1, (caddr_t)&q_on);
+ } else {
+ ret = 0;
+ }
+
+ } else if (qflags & QUOTAS_ENABLED) {
+ if (F.qs_flags & XFS_QUOTA_UDQ_ENFD)
+ q_off |= XFS_QUOTA_UDQ_ENFD;
+
+ if (q_off != 0) {
+ ret = quotactl(QCMD(Q_XQUOTAOFF,USRQUOTA),bdev, -1, (caddr_t)&q_off);
+ } else {
+ ret = 0;
+ }
+
+ if (!(F.qs_flags & XFS_QUOTA_UDQ_ACCT))
+ q_on |= XFS_QUOTA_UDQ_ACCT;
+
+ if (q_on != 0) {
+ ret = quotactl(QCMD(Q_XQUOTAON,USRQUOTA),bdev, -1, (caddr_t)&q_on);
+ } else {
+ ret = 0;
+ }
+ } else {
+#if 0
+ /* Switch on XFS_QUOTA_UDQ_ACCT didn't work!
+ * only swittching off XFS_QUOTA_UDQ_ACCT work
+ */
+ if (F.qs_flags & XFS_QUOTA_UDQ_ENFD)
+ q_off |= XFS_QUOTA_UDQ_ENFD;
+ if (F.qs_flags & XFS_QUOTA_UDQ_ACCT)
+ q_off |= XFS_QUOTA_UDQ_ACCT;
+
+ if (q_off !=0) {
+ ret = quotactl(QCMD(Q_XQUOTAOFF,USRQUOTA),bdev, -1, (caddr_t)&q_off);
+ } else {
+ ret = 0;
+ }
+#else
+ ret = -1;
+#endif
+ }
+
+ break;
+#ifdef HAVE_GROUP_QUOTA
+ case SMB_GROUP_FS_QUOTA_TYPE:
+ DEBUG(10,("sys_set_xfs_quota: path[%s] bdev[%s] SMB_GROUP_FS_QUOTA_TYPE (gid[%u])\n",
+ path, bdev, (unsigned)id.gid));
+
+ quotactl(QCMD(Q_XGETQSTAT,GRPQUOTA), bdev, -1, (caddr_t)&F);
+
+ if (qflags & QUOTAS_DENY_DISK) {
+ if (!(F.qs_flags & XFS_QUOTA_GDQ_ENFD))
+ q_on |= XFS_QUOTA_GDQ_ENFD;
+ if (!(F.qs_flags & XFS_QUOTA_GDQ_ACCT))
+ q_on |= XFS_QUOTA_GDQ_ACCT;
+
+ if (q_on != 0) {
+ ret = quotactl(QCMD(Q_XQUOTAON,GRPQUOTA),bdev, -1, (caddr_t)&q_on);
+ } else {
+ ret = 0;
+ }
+
+ } else if (qflags & QUOTAS_ENABLED) {
+ if (F.qs_flags & XFS_QUOTA_GDQ_ENFD)
+ q_off |= XFS_QUOTA_GDQ_ENFD;
+
+ if (q_off != 0) {
+ ret = quotactl(QCMD(Q_XQUOTAOFF,GRPQUOTA),bdev, -1, (caddr_t)&q_off);
+ } else {
+ ret = 0;
+ }
+
+ if (!(F.qs_flags & XFS_QUOTA_GDQ_ACCT))
+ q_on |= XFS_QUOTA_GDQ_ACCT;
+
+ if (q_on != 0) {
+ ret = quotactl(QCMD(Q_XQUOTAON,GRPQUOTA),bdev, -1, (caddr_t)&q_on);
+ } else {
+ ret = 0;
+ }
+ } else {
+#if 0
+ /* Switch on XFS_QUOTA_UDQ_ACCT didn't work!
+ * only swittching off XFS_QUOTA_UDQ_ACCT work
+ */
+ if (F.qs_flags & XFS_QUOTA_GDQ_ENFD)
+ q_off |= XFS_QUOTA_GDQ_ENFD;
+ if (F.qs_flags & XFS_QUOTA_GDQ_ACCT)
+ q_off |= XFS_QUOTA_GDQ_ACCT;
+
+ if (q_off !=0) {
+ ret = quotactl(QCMD(Q_XQUOTAOFF,GRPQUOTA),bdev, -1, (caddr_t)&q_off);
+ } else {
+ ret = 0;
+ }
+#else
+ ret = -1;
+#endif
+ }
+
+ break;
+#endif /* HAVE_GROUP_QUOTA */
+ default:
+ errno = ENOSYS;
+ return -1;
+ }
+
+ return ret;
+}
+
+#else /* HAVE_XFS_QUOTAS */
+ void dummy_sysquotas_xfs(void){}
+#endif /* HAVE_XFS_QUOTAS */
diff --git a/source/lib/system.c b/source/lib/system.c
new file mode 100644
index 00000000000..a0007ec83cd
--- /dev/null
+++ b/source/lib/system.c
@@ -0,0 +1,1582 @@
+/*
+ Unix SMB/CIFS implementation.
+ Samba system utilities
+ Copyright (C) Andrew Tridgell 1992-1998
+ Copyright (C) Jeremy Allison 1998-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 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"
+
+/*
+ The idea is that this file will eventually have wrappers around all
+ important system calls in samba. The aims are:
+
+ - to enable easier porting by putting OS dependent stuff in here
+
+ - to allow for hooks into other "pseudo-filesystems"
+
+ - to allow easier integration of things like the japanese extensions
+
+ - to support the philosophy of Samba to expose the features of
+ the OS within the SMB model. In general whatever file/printer/variable
+ expansions/etc make sense to the OS should be acceptable to Samba.
+*/
+
+
+
+/*******************************************************************
+ A wrapper for usleep in case we don't have one.
+********************************************************************/
+
+int sys_usleep(long usecs)
+{
+#ifndef HAVE_USLEEP
+ struct timeval tval;
+#endif
+
+ /*
+ * We need this braindamage as the glibc usleep
+ * is not SPEC1170 complient... grumble... JRA.
+ */
+
+ if(usecs < 0 || usecs > 1000000) {
+ errno = EINVAL;
+ return -1;
+ }
+
+#if HAVE_USLEEP
+ usleep(usecs);
+ return 0;
+#else /* HAVE_USLEEP */
+ /*
+ * Fake it with select...
+ */
+ tval.tv_sec = 0;
+ tval.tv_usec = usecs/1000;
+ select(0,NULL,NULL,NULL,&tval);
+ return 0;
+#endif /* HAVE_USLEEP */
+}
+
+/*******************************************************************
+A read wrapper that will deal with EINTR.
+********************************************************************/
+
+ssize_t sys_read(int fd, void *buf, size_t count)
+{
+ ssize_t ret;
+
+ do {
+ ret = read(fd, buf, count);
+ } while (ret == -1 && errno == EINTR);
+ return ret;
+}
+
+/*******************************************************************
+A write wrapper that will deal with EINTR.
+********************************************************************/
+
+ssize_t sys_write(int fd, const void *buf, size_t count)
+{
+ ssize_t ret;
+
+ do {
+ ret = write(fd, buf, count);
+ } while (ret == -1 && errno == EINTR);
+ return ret;
+}
+
+
+/*******************************************************************
+A pread wrapper that will deal with EINTR and 64-bit file offsets.
+********************************************************************/
+
+#if defined(HAVE_PREAD) || defined(HAVE_PREAD64)
+ssize_t sys_pread(int fd, void *buf, size_t count, SMB_OFF_T off)
+{
+ ssize_t ret;
+
+ do {
+#if defined(HAVE_EXPLICIT_LARGEFILE_SUPPORT) && defined(HAVE_OFF64_T) && defined(HAVE_PREAD64)
+ ret = pread64(fd, buf, count, off);
+#else
+ ret = pread(fd, buf, count, off);
+#endif
+ } while (ret == -1 && errno == EINTR);
+ return ret;
+}
+#endif
+
+/*******************************************************************
+A write wrapper that will deal with EINTR and 64-bit file offsets.
+********************************************************************/
+
+#if defined(HAVE_PWRITE) || defined(HAVE_PWRITE64)
+ssize_t sys_pwrite(int fd, const void *buf, size_t count, SMB_OFF_T off)
+{
+ ssize_t ret;
+
+ do {
+#if defined(HAVE_EXPLICIT_LARGEFILE_SUPPORT) && defined(HAVE_OFF64_T) && defined(HAVE_PWRITE64)
+ ret = pwrite64(fd, buf, count, off);
+#else
+ ret = pwrite(fd, buf, count, off);
+#endif
+ } while (ret == -1 && errno == EINTR);
+ return ret;
+}
+#endif
+
+/*******************************************************************
+A send wrapper that will deal with EINTR.
+********************************************************************/
+
+ssize_t sys_send(int s, const void *msg, size_t len, int flags)
+{
+ ssize_t ret;
+
+ do {
+ ret = send(s, msg, len, flags);
+ } while (ret == -1 && errno == EINTR);
+ return ret;
+}
+
+/*******************************************************************
+A sendto wrapper that will deal with EINTR.
+********************************************************************/
+
+ssize_t sys_sendto(int s, const void *msg, size_t len, int flags, const struct sockaddr *to, socklen_t tolen)
+{
+ ssize_t ret;
+
+ do {
+ ret = sendto(s, msg, len, flags, to, tolen);
+ } while (ret == -1 && errno == EINTR);
+ return ret;
+}
+
+/*******************************************************************
+A recvfrom wrapper that will deal with EINTR.
+********************************************************************/
+
+ssize_t sys_recvfrom(int s, void *buf, size_t len, int flags, struct sockaddr *from, socklen_t *fromlen)
+{
+ ssize_t ret;
+
+ do {
+ ret = recvfrom(s, buf, len, flags, from, fromlen);
+ } while (ret == -1 && errno == EINTR);
+ return ret;
+}
+
+/*******************************************************************
+A fcntl wrapper that will deal with EINTR.
+********************************************************************/
+
+int sys_fcntl_ptr(int fd, int cmd, void *arg)
+{
+ int ret;
+
+ do {
+ ret = fcntl(fd, cmd, arg);
+ } while (ret == -1 && errno == EINTR);
+ return ret;
+}
+
+/*******************************************************************
+A fcntl wrapper that will deal with EINTR.
+********************************************************************/
+
+int sys_fcntl_long(int fd, int cmd, long arg)
+{
+ int ret;
+
+ do {
+ ret = fcntl(fd, cmd, arg);
+ } while (ret == -1 && errno == EINTR);
+ return ret;
+}
+
+/*******************************************************************
+A stat() wrapper that will deal with 64 bit filesizes.
+********************************************************************/
+
+int sys_stat(const char *fname,SMB_STRUCT_STAT *sbuf)
+{
+ int ret;
+#if defined(HAVE_EXPLICIT_LARGEFILE_SUPPORT) && defined(HAVE_OFF64_T) && defined(HAVE_STAT64)
+ ret = stat64(fname, sbuf);
+#else
+ ret = stat(fname, sbuf);
+#endif
+ /* we always want directories to appear zero size */
+ if (ret == 0 && S_ISDIR(sbuf->st_mode)) sbuf->st_size = 0;
+ return ret;
+}
+
+/*******************************************************************
+ An fstat() wrapper that will deal with 64 bit filesizes.
+********************************************************************/
+
+int sys_fstat(int fd,SMB_STRUCT_STAT *sbuf)
+{
+ int ret;
+#if defined(HAVE_EXPLICIT_LARGEFILE_SUPPORT) && defined(HAVE_OFF64_T) && defined(HAVE_FSTAT64)
+ ret = fstat64(fd, sbuf);
+#else
+ ret = fstat(fd, sbuf);
+#endif
+ /* we always want directories to appear zero size */
+ if (ret == 0 && S_ISDIR(sbuf->st_mode)) sbuf->st_size = 0;
+ return ret;
+}
+
+/*******************************************************************
+ An lstat() wrapper that will deal with 64 bit filesizes.
+********************************************************************/
+
+int sys_lstat(const char *fname,SMB_STRUCT_STAT *sbuf)
+{
+ int ret;
+#if defined(HAVE_EXPLICIT_LARGEFILE_SUPPORT) && defined(HAVE_OFF64_T) && defined(HAVE_LSTAT64)
+ ret = lstat64(fname, sbuf);
+#else
+ ret = lstat(fname, sbuf);
+#endif
+ /* we always want directories to appear zero size */
+ if (ret == 0 && S_ISDIR(sbuf->st_mode)) sbuf->st_size = 0;
+ return ret;
+}
+
+/*******************************************************************
+ An ftruncate() wrapper that will deal with 64 bit filesizes.
+********************************************************************/
+
+int sys_ftruncate(int fd, SMB_OFF_T offset)
+{
+#if defined(HAVE_EXPLICIT_LARGEFILE_SUPPORT) && defined(HAVE_OFF64_T) && defined(HAVE_FTRUNCATE64)
+ return ftruncate64(fd, offset);
+#else
+ return ftruncate(fd, offset);
+#endif
+}
+
+/*******************************************************************
+ An lseek() wrapper that will deal with 64 bit filesizes.
+********************************************************************/
+
+SMB_OFF_T sys_lseek(int fd, SMB_OFF_T offset, int whence)
+{
+#if defined(HAVE_EXPLICIT_LARGEFILE_SUPPORT) && defined(HAVE_OFF64_T) && defined(HAVE_LSEEK64)
+ return lseek64(fd, offset, whence);
+#else
+ return lseek(fd, offset, whence);
+#endif
+}
+
+/*******************************************************************
+ An fseek() wrapper that will deal with 64 bit filesizes.
+********************************************************************/
+
+int sys_fseek(FILE *fp, SMB_OFF_T offset, int whence)
+{
+#if defined(HAVE_EXPLICIT_LARGEFILE_SUPPORT) && defined(LARGE_SMB_OFF_T) && defined(HAVE_FSEEK64)
+ return fseek64(fp, offset, whence);
+#elif defined(HAVE_EXPLICIT_LARGEFILE_SUPPORT) && defined(LARGE_SMB_OFF_T) && defined(HAVE_FSEEKO64)
+ return fseeko64(fp, offset, whence);
+#else
+ return fseek(fp, offset, whence);
+#endif
+}
+
+/*******************************************************************
+ An ftell() wrapper that will deal with 64 bit filesizes.
+********************************************************************/
+
+SMB_OFF_T sys_ftell(FILE *fp)
+{
+#if defined(HAVE_EXPLICIT_LARGEFILE_SUPPORT) && defined(LARGE_SMB_OFF_T) && defined(HAVE_FTELL64)
+ return (SMB_OFF_T)ftell64(fp);
+#elif defined(HAVE_EXPLICIT_LARGEFILE_SUPPORT) && defined(LARGE_SMB_OFF_T) && defined(HAVE_FTELLO64)
+ return (SMB_OFF_T)ftello64(fp);
+#else
+ return (SMB_OFF_T)ftell(fp);
+#endif
+}
+
+/*******************************************************************
+ A creat() wrapper that will deal with 64 bit filesizes.
+********************************************************************/
+
+int sys_creat(const char *path, mode_t mode)
+{
+#if defined(HAVE_EXPLICIT_LARGEFILE_SUPPORT) && defined(HAVE_CREAT64)
+ return creat64(path, mode);
+#else
+ /*
+ * If creat64 isn't defined then ensure we call a potential open64.
+ * JRA.
+ */
+ return sys_open(path, O_WRONLY | O_CREAT | O_TRUNC, mode);
+#endif
+}
+
+/*******************************************************************
+ An open() wrapper that will deal with 64 bit filesizes.
+********************************************************************/
+
+int sys_open(const char *path, int oflag, mode_t mode)
+{
+#if defined(HAVE_EXPLICIT_LARGEFILE_SUPPORT) && defined(HAVE_OPEN64)
+ return open64(path, oflag, mode);
+#else
+ return open(path, oflag, mode);
+#endif
+}
+
+/*******************************************************************
+ An fopen() wrapper that will deal with 64 bit filesizes.
+********************************************************************/
+
+FILE *sys_fopen(const char *path, const char *type)
+{
+#if defined(HAVE_EXPLICIT_LARGEFILE_SUPPORT) && defined(HAVE_FOPEN64)
+ return fopen64(path, type);
+#else
+ return fopen(path, type);
+#endif
+}
+
+/*******************************************************************
+ A readdir wrapper that will deal with 64 bit filesizes.
+********************************************************************/
+
+SMB_STRUCT_DIRENT *sys_readdir(DIR *dirp)
+{
+#if defined(HAVE_EXPLICIT_LARGEFILE_SUPPORT) && defined(HAVE_READDIR64)
+ return readdir64(dirp);
+#else
+ return readdir(dirp);
+#endif
+}
+
+/*******************************************************************
+ An mknod() wrapper that will deal with 64 bit filesizes.
+********************************************************************/
+
+int sys_mknod(const char *path, mode_t mode, SMB_DEV_T dev)
+{
+#if defined(HAVE_MKNOD) || defined(HAVE_MKNOD64)
+#if defined(HAVE_EXPLICIT_LARGEFILE_SUPPORT) && defined(HAVE_MKNOD64) && defined(HAVE_DEV64_T)
+ return mknod64(path, mode, dev);
+#else
+ return mknod(path, mode, dev);
+#endif
+#else
+ /* No mknod system call. */
+ errno = ENOSYS;
+ return -1;
+#endif
+}
+
+/*******************************************************************
+ Wrapper for realpath.
+********************************************************************/
+
+char *sys_realpath(const char *path, char *resolved_path)
+{
+#if defined(HAVE_REALPATH)
+ return realpath(path, resolved_path);
+#else
+ /* As realpath is not a system call we can't return ENOSYS. */
+ errno = EINVAL;
+ return NULL;
+#endif
+}
+
+/*******************************************************************
+The wait() calls vary between systems
+********************************************************************/
+
+int sys_waitpid(pid_t pid,int *status,int options)
+{
+#ifdef HAVE_WAITPID
+ return waitpid(pid,status,options);
+#else /* HAVE_WAITPID */
+ return wait4(pid, status, options, NULL);
+#endif /* HAVE_WAITPID */
+}
+
+/*******************************************************************
+ System wrapper for getwd
+********************************************************************/
+
+char *sys_getwd(char *s)
+{
+ char *wd;
+#ifdef HAVE_GETCWD
+ wd = (char *)getcwd(s, sizeof (pstring));
+#else
+ wd = (char *)getwd(s);
+#endif
+ return wd;
+}
+
+/*******************************************************************
+system wrapper for symlink
+********************************************************************/
+
+int sys_symlink(const char *oldpath, const char *newpath)
+{
+#ifndef HAVE_SYMLINK
+ errno = ENOSYS;
+ return -1;
+#else
+ return symlink(oldpath, newpath);
+#endif
+}
+
+/*******************************************************************
+system wrapper for readlink
+********************************************************************/
+
+int sys_readlink(const char *path, char *buf, size_t bufsiz)
+{
+#ifndef HAVE_READLINK
+ errno = ENOSYS;
+ return -1;
+#else
+ return readlink(path, buf, bufsiz);
+#endif
+}
+
+/*******************************************************************
+system wrapper for link
+********************************************************************/
+
+int sys_link(const char *oldpath, const char *newpath)
+{
+#ifndef HAVE_LINK
+ errno = ENOSYS;
+ return -1;
+#else
+ return link(oldpath, newpath);
+#endif
+}
+
+/*******************************************************************
+chown isn't used much but OS/2 doesn't have it
+********************************************************************/
+
+int sys_chown(const char *fname,uid_t uid,gid_t gid)
+{
+#ifndef HAVE_CHOWN
+ static int done;
+ if (!done) {
+ DEBUG(1,("WARNING: no chown!\n"));
+ done=1;
+ }
+ errno = ENOSYS;
+ return -1;
+#else
+ return(chown(fname,uid,gid));
+#endif
+}
+
+/*******************************************************************
+os/2 also doesn't have chroot
+********************************************************************/
+int sys_chroot(const char *dname)
+{
+#ifndef HAVE_CHROOT
+ static int done;
+ if (!done) {
+ DEBUG(1,("WARNING: no chroot!\n"));
+ done=1;
+ }
+ errno = ENOSYS;
+ return -1;
+#else
+ return(chroot(dname));
+#endif
+}
+
+/**************************************************************************
+A wrapper for gethostbyname() that tries avoids looking up hostnames
+in the root domain, which can cause dial-on-demand links to come up for no
+apparent reason.
+****************************************************************************/
+
+struct hostent *sys_gethostbyname(const char *name)
+{
+#ifdef REDUCE_ROOT_DNS_LOOKUPS
+ char query[256], hostname[256];
+ char *domain;
+
+ /* Does this name have any dots in it? If so, make no change */
+
+ if (strchr_m(name, '.'))
+ return(gethostbyname(name));
+
+ /* Get my hostname, which should have domain name
+ attached. If not, just do the gethostname on the
+ original string.
+ */
+
+ gethostname(hostname, sizeof(hostname) - 1);
+ hostname[sizeof(hostname) - 1] = 0;
+ if ((domain = strchr_m(hostname, '.')) == NULL)
+ return(gethostbyname(name));
+
+ /* Attach domain name to query and do modified query.
+ If names too large, just do gethostname on the
+ original string.
+ */
+
+ if((strlen(name) + strlen(domain)) >= sizeof(query))
+ return(gethostbyname(name));
+
+ slprintf(query, sizeof(query)-1, "%s%s", name, domain);
+ return(gethostbyname(query));
+#else /* REDUCE_ROOT_DNS_LOOKUPS */
+ return(gethostbyname(name));
+#endif /* REDUCE_ROOT_DNS_LOOKUPS */
+}
+
+
+#if defined(HAVE_IRIX_SPECIFIC_CAPABILITIES)
+/**************************************************************************
+ Try and abstract process capabilities (for systems that have them).
+****************************************************************************/
+static BOOL set_process_capability( uint32 cap_flag, BOOL enable )
+{
+ if(cap_flag == KERNEL_OPLOCK_CAPABILITY) {
+ cap_t cap = cap_get_proc();
+
+ if (cap == NULL) {
+ DEBUG(0,("set_process_capability: cap_get_proc failed. Error was %s\n",
+ strerror(errno)));
+ return False;
+ }
+
+ if(enable)
+ cap->cap_effective |= CAP_NETWORK_MGT;
+ else
+ cap->cap_effective &= ~CAP_NETWORK_MGT;
+
+ if (cap_set_proc(cap) == -1) {
+ DEBUG(0,("set_process_capability: cap_set_proc failed. Error was %s\n",
+ strerror(errno)));
+ cap_free(cap);
+ return False;
+ }
+
+ cap_free(cap);
+
+ DEBUG(10,("set_process_capability: Set KERNEL_OPLOCK_CAPABILITY.\n"));
+ }
+ return True;
+}
+
+/**************************************************************************
+ Try and abstract inherited process capabilities (for systems that have them).
+****************************************************************************/
+
+static BOOL set_inherited_process_capability( uint32 cap_flag, BOOL enable )
+{
+ if(cap_flag == KERNEL_OPLOCK_CAPABILITY) {
+ cap_t cap = cap_get_proc();
+
+ if (cap == NULL) {
+ DEBUG(0,("set_inherited_process_capability: cap_get_proc failed. Error was %s\n",
+ strerror(errno)));
+ return False;
+ }
+
+ if(enable)
+ cap->cap_inheritable |= CAP_NETWORK_MGT;
+ else
+ cap->cap_inheritable &= ~CAP_NETWORK_MGT;
+
+ if (cap_set_proc(cap) == -1) {
+ DEBUG(0,("set_inherited_process_capability: cap_set_proc failed. Error was %s\n",
+ strerror(errno)));
+ cap_free(cap);
+ return False;
+ }
+
+ cap_free(cap);
+
+ DEBUG(10,("set_inherited_process_capability: Set KERNEL_OPLOCK_CAPABILITY.\n"));
+ }
+ return True;
+}
+#endif
+
+/****************************************************************************
+ Gain the oplock capability from the kernel if possible.
+****************************************************************************/
+
+void oplock_set_capability(BOOL this_process, BOOL inherit)
+{
+#if HAVE_KERNEL_OPLOCKS_IRIX
+ set_process_capability(KERNEL_OPLOCK_CAPABILITY,this_process);
+ set_inherited_process_capability(KERNEL_OPLOCK_CAPABILITY,inherit);
+#endif
+}
+
+/**************************************************************************
+ Wrapper for random().
+****************************************************************************/
+
+long sys_random(void)
+{
+#if defined(HAVE_RANDOM)
+ return (long)random();
+#elif defined(HAVE_RAND)
+ return (long)rand();
+#else
+ DEBUG(0,("Error - no random function available !\n"));
+ exit(1);
+#endif
+}
+
+/**************************************************************************
+ Wrapper for srandom().
+****************************************************************************/
+
+void sys_srandom(unsigned int seed)
+{
+#if defined(HAVE_SRANDOM)
+ srandom(seed);
+#elif defined(HAVE_SRAND)
+ srand(seed);
+#else
+ DEBUG(0,("Error - no srandom function available !\n"));
+ exit(1);
+#endif
+}
+
+/**************************************************************************
+ Returns equivalent to NGROUPS_MAX - using sysconf if needed.
+****************************************************************************/
+
+int groups_max(void)
+{
+#if defined(SYSCONF_SC_NGROUPS_MAX)
+ int ret = sysconf(_SC_NGROUPS_MAX);
+ return (ret == -1) ? NGROUPS_MAX : ret;
+#else
+ return NGROUPS_MAX;
+#endif
+}
+
+/**************************************************************************
+ Wrapper for getgroups. Deals with broken (int) case.
+****************************************************************************/
+
+int sys_getgroups(int setlen, gid_t *gidset)
+{
+#if !defined(HAVE_BROKEN_GETGROUPS)
+ return getgroups(setlen, gidset);
+#else
+
+ GID_T gid;
+ GID_T *group_list;
+ int i, ngroups;
+
+ if(setlen == 0) {
+ return getgroups(setlen, &gid);
+ }
+
+ /*
+ * Broken case. We need to allocate a
+ * GID_T array of size setlen.
+ */
+
+ if(setlen < 0) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ if (setlen == 0)
+ setlen = groups_max();
+
+ if((group_list = (GID_T *)malloc(setlen * sizeof(GID_T))) == NULL) {
+ DEBUG(0,("sys_getgroups: Malloc fail.\n"));
+ return -1;
+ }
+
+ if((ngroups = getgroups(setlen, group_list)) < 0) {
+ int saved_errno = errno;
+ SAFE_FREE(group_list);
+ errno = saved_errno;
+ return -1;
+ }
+
+ for(i = 0; i < ngroups; i++)
+ gidset[i] = (gid_t)group_list[i];
+
+ SAFE_FREE(group_list);
+ return ngroups;
+#endif /* HAVE_BROKEN_GETGROUPS */
+}
+
+
+/**************************************************************************
+ Wrapper for setgroups. Deals with broken (int) case. Automatically used
+ if we have broken getgroups.
+****************************************************************************/
+
+int sys_setgroups(int setlen, gid_t *gidset)
+{
+#if !defined(HAVE_SETGROUPS)
+ errno = ENOSYS;
+ return -1;
+#endif /* HAVE_SETGROUPS */
+
+#if !defined(HAVE_BROKEN_GETGROUPS)
+ return setgroups(setlen, gidset);
+#else
+
+ GID_T *group_list;
+ int i ;
+
+ if (setlen == 0)
+ return 0 ;
+
+ if (setlen < 0 || setlen > groups_max()) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ /*
+ * Broken case. We need to allocate a
+ * GID_T array of size setlen.
+ */
+
+ if((group_list = (GID_T *)malloc(setlen * sizeof(GID_T))) == NULL) {
+ DEBUG(0,("sys_setgroups: Malloc fail.\n"));
+ return -1;
+ }
+
+ for(i = 0; i < setlen; i++)
+ group_list[i] = (GID_T) gidset[i];
+
+ if(setgroups(setlen, group_list) != 0) {
+ int saved_errno = errno;
+ SAFE_FREE(group_list);
+ errno = saved_errno;
+ return -1;
+ }
+
+ SAFE_FREE(group_list);
+ return 0 ;
+#endif /* HAVE_BROKEN_GETGROUPS */
+}
+
+/**************************************************************************
+ Wrappers for setpwent(), getpwent() and endpwent()
+****************************************************************************/
+
+void sys_setpwent(void)
+{
+ setpwent();
+}
+
+struct passwd *sys_getpwent(void)
+{
+ return getpwent();
+}
+
+void sys_endpwent(void)
+{
+ endpwent();
+}
+
+/**************************************************************************
+ Wrappers for getpwnam(), getpwuid(), getgrnam(), getgrgid()
+****************************************************************************/
+
+struct passwd *sys_getpwnam(const char *name)
+{
+ return getpwnam(name);
+}
+
+struct passwd *sys_getpwuid(uid_t uid)
+{
+ return getpwuid(uid);
+}
+
+struct group *sys_getgrnam(const char *name)
+{
+ return getgrnam(name);
+}
+
+struct group *sys_getgrgid(gid_t gid)
+{
+ return getgrgid(gid);
+}
+
+#if 0 /* NOT CURRENTLY USED - JRA */
+/**************************************************************************
+ The following are the UNICODE versions of *all* system interface functions
+ called within Samba. Ok, ok, the exceptions are the gethostbyXX calls,
+ which currently are left as ascii as they are not used other than in name
+ resolution.
+****************************************************************************/
+
+/**************************************************************************
+ Wide stat. Just narrow and call sys_xxx.
+****************************************************************************/
+
+int wsys_stat(const smb_ucs2_t *wfname,SMB_STRUCT_STAT *sbuf)
+{
+ pstring fname;
+ return sys_stat(unicode_to_unix(fname,wfname,sizeof(fname)), sbuf);
+}
+
+/**************************************************************************
+ Wide lstat. Just narrow and call sys_xxx.
+****************************************************************************/
+
+int wsys_lstat(const smb_ucs2_t *wfname,SMB_STRUCT_STAT *sbuf)
+{
+ pstring fname;
+ return sys_lstat(unicode_to_unix(fname,wfname,sizeof(fname)), sbuf);
+}
+
+/**************************************************************************
+ Wide creat. Just narrow and call sys_xxx.
+****************************************************************************/
+
+int wsys_creat(const smb_ucs2_t *wfname, mode_t mode)
+{
+ pstring fname;
+ return sys_creat(unicode_to_unix(fname,wfname,sizeof(fname)), mode);
+}
+
+/**************************************************************************
+ Wide open. Just narrow and call sys_xxx.
+****************************************************************************/
+
+int wsys_open(const smb_ucs2_t *wfname, int oflag, mode_t mode)
+{
+ pstring fname;
+ return sys_open(unicode_to_unix(fname,wfname,sizeof(fname)), oflag, mode);
+}
+
+/**************************************************************************
+ Wide fopen. Just narrow and call sys_xxx.
+****************************************************************************/
+
+FILE *wsys_fopen(const smb_ucs2_t *wfname, const char *type)
+{
+ pstring fname;
+ return sys_fopen(unicode_to_unix(fname,wfname,sizeof(fname)), type);
+}
+
+/**************************************************************************
+ Wide opendir. Just narrow and call sys_xxx.
+****************************************************************************/
+
+DIR *wsys_opendir(const smb_ucs2_t *wfname)
+{
+ pstring fname;
+ return opendir(unicode_to_unix(fname,wfname,sizeof(fname)));
+}
+
+/**************************************************************************
+ Wide readdir. Return a structure pointer containing a wide filename.
+****************************************************************************/
+
+SMB_STRUCT_WDIRENT *wsys_readdir(DIR *dirp)
+{
+ static SMB_STRUCT_WDIRENT retval;
+ SMB_STRUCT_DIRENT *dirval = sys_readdir(dirp);
+
+ if(!dirval)
+ return NULL;
+
+ /*
+ * The only POSIX defined member of this struct is d_name.
+ */
+
+ unix_to_unicode(retval.d_name,dirval->d_name,sizeof(retval.d_name));
+
+ return &retval;
+}
+
+/**************************************************************************
+ Wide getwd. Call sys_xxx and widen. Assumes s points to a wpstring.
+****************************************************************************/
+
+smb_ucs2_t *wsys_getwd(smb_ucs2_t *s)
+{
+ pstring fname;
+ char *p = sys_getwd(fname);
+
+ if(!p)
+ return NULL;
+
+ return unix_to_unicode(s, p, sizeof(wpstring));
+}
+
+/**************************************************************************
+ Wide chown. Just narrow and call sys_xxx.
+****************************************************************************/
+
+int wsys_chown(const smb_ucs2_t *wfname, uid_t uid, gid_t gid)
+{
+ pstring fname;
+ return chown(unicode_to_unix(fname,wfname,sizeof(fname)), uid, gid);
+}
+
+/**************************************************************************
+ Wide chroot. Just narrow and call sys_xxx.
+****************************************************************************/
+
+int wsys_chroot(const smb_ucs2_t *wfname)
+{
+ pstring fname;
+ return chroot(unicode_to_unix(fname,wfname,sizeof(fname)));
+}
+
+/**************************************************************************
+ Wide getpwnam. Return a structure pointer containing wide names.
+****************************************************************************/
+
+SMB_STRUCT_WPASSWD *wsys_getpwnam(const smb_ucs2_t *wname)
+{
+ static SMB_STRUCT_WPASSWD retval;
+ fstring name;
+ struct passwd *pwret = sys_getpwnam(unicode_to_unix(name,wname,sizeof(name)));
+
+ if(!pwret)
+ return NULL;
+
+ unix_to_unicode(retval.pw_name, pwret->pw_name, sizeof(retval.pw_name));
+ retval.pw_passwd = pwret->pw_passwd;
+ retval.pw_uid = pwret->pw_uid;
+ retval.pw_gid = pwret->pw_gid;
+ unix_to_unicode(retval.pw_gecos, pwret->pw_gecos, sizeof(retval.pw_gecos));
+ unix_to_unicode(retval.pw_dir, pwret->pw_dir, sizeof(retval.pw_dir));
+ unix_to_unicode(retval.pw_shell, pwret->pw_shell, sizeof(retval.pw_shell));
+
+ return &retval;
+}
+
+/**************************************************************************
+ Wide getpwuid. Return a structure pointer containing wide names.
+****************************************************************************/
+
+SMB_STRUCT_WPASSWD *wsys_getpwuid(uid_t uid)
+{
+ static SMB_STRUCT_WPASSWD retval;
+ struct passwd *pwret = sys_getpwuid(uid);
+
+ if(!pwret)
+ return NULL;
+
+ unix_to_unicode(retval.pw_name, pwret->pw_name, sizeof(retval.pw_name));
+ retval.pw_passwd = pwret->pw_passwd;
+ retval.pw_uid = pwret->pw_uid;
+ retval.pw_gid = pwret->pw_gid;
+ unix_to_unicode(retval.pw_gecos, pwret->pw_gecos, sizeof(retval.pw_gecos));
+ unix_to_unicode(retval.pw_dir, pwret->pw_dir, sizeof(retval.pw_dir));
+ unix_to_unicode(retval.pw_shell, pwret->pw_shell, sizeof(retval.pw_shell));
+
+ return &retval;
+}
+#endif /* NOT CURRENTLY USED - JRA */
+
+/**************************************************************************
+ Extract a command into an arg list. Uses a static pstring for storage.
+ Caller frees returned arg list (which contains pointers into the static pstring).
+****************************************************************************/
+
+static char **extract_args(const char *command)
+{
+ static pstring trunc_cmd;
+ char *ptr;
+ int argcl;
+ char **argl = NULL;
+ int i;
+
+ pstrcpy(trunc_cmd, command);
+
+ if(!(ptr = strtok(trunc_cmd, " \t"))) {
+ errno = EINVAL;
+ return NULL;
+ }
+
+ /*
+ * Count the args.
+ */
+
+ for( argcl = 1; ptr; ptr = strtok(NULL, " \t"))
+ argcl++;
+
+ if((argl = (char **)malloc((argcl + 1) * sizeof(char *))) == NULL)
+ return NULL;
+
+ /*
+ * Now do the extraction.
+ */
+
+ pstrcpy(trunc_cmd, command);
+
+ ptr = strtok(trunc_cmd, " \t");
+ i = 0;
+ argl[i++] = ptr;
+
+ while((ptr = strtok(NULL, " \t")) != NULL)
+ argl[i++] = ptr;
+
+ argl[i++] = NULL;
+ return argl;
+}
+
+/**************************************************************************
+ Wrapper for fork. Ensures that mypid is reset. Used so we can write
+ a sys_getpid() that only does a system call *once*.
+****************************************************************************/
+
+static pid_t mypid = (pid_t)-1;
+
+pid_t sys_fork(void)
+{
+ pid_t forkret = fork();
+
+ if (forkret == (pid_t)0) /* Child - reset mypid so sys_getpid does a system call. */
+ mypid = (pid_t) -1;
+
+ return forkret;
+}
+
+/**************************************************************************
+ Wrapper for getpid. Ensures we only do a system call *once*.
+****************************************************************************/
+
+pid_t sys_getpid(void)
+{
+ if (mypid == (pid_t)-1)
+ mypid = getpid();
+
+ return mypid;
+}
+
+/**************************************************************************
+ Wrapper for popen. Safer as it doesn't search a path.
+ Modified from the glibc sources.
+ modified by tridge to return a file descriptor. We must kick our FILE* habit
+****************************************************************************/
+
+typedef struct _popen_list
+{
+ int fd;
+ pid_t child_pid;
+ struct _popen_list *next;
+} popen_list;
+
+static popen_list *popen_chain;
+
+int sys_popen(const char *command)
+{
+ int parent_end, child_end;
+ int pipe_fds[2];
+ popen_list *entry = NULL;
+ char **argl = NULL;
+
+ if (pipe(pipe_fds) < 0)
+ return -1;
+
+ parent_end = pipe_fds[0];
+ child_end = pipe_fds[1];
+
+ if (!*command) {
+ errno = EINVAL;
+ goto err_exit;
+ }
+
+ if((entry = (popen_list *)malloc(sizeof(popen_list))) == NULL)
+ goto err_exit;
+
+ ZERO_STRUCTP(entry);
+
+ /*
+ * Extract the command and args into a NULL terminated array.
+ */
+
+ if(!(argl = extract_args(command)))
+ goto err_exit;
+
+ entry->child_pid = sys_fork();
+
+ if (entry->child_pid == -1) {
+ goto err_exit;
+ }
+
+ if (entry->child_pid == 0) {
+
+ /*
+ * Child !
+ */
+
+ int child_std_end = STDOUT_FILENO;
+ popen_list *p;
+
+ close(parent_end);
+ if (child_end != child_std_end) {
+ dup2 (child_end, child_std_end);
+ close (child_end);
+ }
+
+ /*
+ * POSIX.2: "popen() shall ensure that any streams from previous
+ * popen() calls that remain open in the parent process are closed
+ * in the new child process."
+ */
+
+ for (p = popen_chain; p; p = p->next)
+ close(p->fd);
+
+ execv(argl[0], argl);
+ _exit (127);
+ }
+
+ /*
+ * Parent.
+ */
+
+ close (child_end);
+ SAFE_FREE(argl);
+
+ /* Link into popen_chain. */
+ entry->next = popen_chain;
+ popen_chain = entry;
+ entry->fd = parent_end;
+
+ return entry->fd;
+
+err_exit:
+
+ SAFE_FREE(entry);
+ SAFE_FREE(argl);
+ close(pipe_fds[0]);
+ close(pipe_fds[1]);
+ return -1;
+}
+
+/**************************************************************************
+ Wrapper for pclose. Modified from the glibc sources.
+****************************************************************************/
+
+int sys_pclose(int fd)
+{
+ int wstatus;
+ popen_list **ptr = &popen_chain;
+ popen_list *entry = NULL;
+ pid_t wait_pid;
+ int status = -1;
+
+ /* Unlink from popen_chain. */
+ for ( ; *ptr != NULL; ptr = &(*ptr)->next) {
+ if ((*ptr)->fd == fd) {
+ entry = *ptr;
+ *ptr = (*ptr)->next;
+ status = 0;
+ break;
+ }
+ }
+
+ if (status < 0 || close(entry->fd) < 0)
+ return -1;
+
+ /*
+ * As Samba is catching and eating child process
+ * exits we don't really care about the child exit
+ * code, a -1 with errno = ECHILD will do fine for us.
+ */
+
+ do {
+ wait_pid = sys_waitpid (entry->child_pid, &wstatus, 0);
+ } while (wait_pid == -1 && errno == EINTR);
+
+ SAFE_FREE(entry);
+
+ if (wait_pid == -1)
+ return -1;
+ return wstatus;
+}
+
+/**************************************************************************
+ Wrappers for dlopen, dlsym, dlclose.
+****************************************************************************/
+
+void *sys_dlopen(const char *name, int flags)
+{
+#if defined(HAVE_DLOPEN)
+ return dlopen(name, flags);
+#else
+ return NULL;
+#endif
+}
+
+void *sys_dlsym(void *handle, const char *symbol)
+{
+#if defined(HAVE_DLSYM)
+ return dlsym(handle, symbol);
+#else
+ return NULL;
+#endif
+}
+
+int sys_dlclose (void *handle)
+{
+#if defined(HAVE_DLCLOSE)
+ return dlclose(handle);
+#else
+ return 0;
+#endif
+}
+
+const char *sys_dlerror(void)
+{
+#if defined(HAVE_DLERROR)
+ return dlerror();
+#else
+ return NULL;
+#endif
+}
+
+int sys_dup2(int oldfd, int newfd)
+{
+#if defined(HAVE_DUP2)
+ return dup2(oldfd, newfd);
+#else
+ errno = ENOSYS;
+ return -1;
+#endif
+}
+
+/**************************************************************************
+ Wrapper for Admin Logs.
+****************************************************************************/
+
+ void sys_adminlog(int priority, const char *format_str, ...)
+{
+ va_list ap;
+ int ret;
+ char *msgbuf = NULL;
+
+ va_start( ap, format_str );
+ ret = vasprintf( &msgbuf, format_str, ap );
+ va_end( ap );
+
+ if (ret == -1)
+ return;
+
+#if defined(HAVE_SYSLOG)
+ syslog( priority, "%s", msgbuf );
+#else
+ DEBUG(0,("%s", msgbuf ));
+#endif
+ SAFE_FREE(msgbuf);
+}
+
+/**************************************************************************
+ Wrappers for extented attribute calls. Based on the Linux package with
+ support for IRIX also. Expand as other systems have them.
+****************************************************************************/
+
+ssize_t sys_getxattr (const char *path, const char *name, void *value, size_t size)
+{
+#if defined(HAVE_GETXATTR)
+ return getxattr(path, name, value, size);
+#elif defined(HAVE_ATTR_GET)
+ int retval, flags = 0;
+ int valuelength = (int)size;
+ char *attrname = strchr(name,'.') +1;
+
+ if (strncmp(name, "system", 6) == 0) flags |= ATTR_ROOT;
+
+ retval = attr_get(path, attrname, (char *)value, &valuelength, flags);
+
+ return retval ? retval : valuelength;
+#else
+ errno = ENOSYS;
+ return -1;
+#endif
+}
+
+ssize_t sys_lgetxattr (const char *path, const char *name, void *value, size_t size)
+{
+#if defined(HAVE_LGETXATTR)
+ return lgetxattr(path, name, value, size);
+#elif defined(HAVE_ATTR_GET)
+ int retval, flags = ATTR_DONTFOLLOW;
+ int valuelength = (int)size;
+ char *attrname = strchr(name,'.') +1;
+
+ if (strncmp(name, "system", 6) == 0) flags |= ATTR_ROOT;
+
+ retval = attr_get(path, attrname, (char *)value, &valuelength, flags);
+
+ return retval ? retval : valuelength;
+#else
+ errno = ENOSYS;
+ return -1;
+#endif
+}
+
+ssize_t sys_fgetxattr (int filedes, const char *name, void *value, size_t size)
+{
+#if defined(HAVE_FGETXATTR)
+ return fgetxattr(filedes, name, value, size);
+#elif defined(HAVE_ATTR_GETF)
+ int retval, flags = 0;
+ int valuelength = (int)size;
+ char *attrname = strchr(name,'.') +1;
+
+ if (strncmp(name, "system", 6) == 0) flags |= ATTR_ROOT;
+
+ retval = attr_getf(filedes, attrname, (char *)value, &valuelength, flags);
+
+ return retval ? retval : valuelength;
+#else
+ errno = ENOSYS;
+ return -1;
+#endif
+}
+
+#if defined(HAVE_ATTR_LIST) && defined(HAVE_SYS_ATTRIBUTES_H)
+static char attr_buffer[ATTR_MAX_VALUELEN];
+
+static ssize_t irix_attr_list(const char *path, int filedes, char *list, size_t size, int flags)
+{
+ int retval = 0, index;
+ attrlist_cursor_t *cursor = 0;
+ int total_size = 0;
+ attrlist_t * al = (attrlist_t *)attr_buffer;
+ attrlist_ent_t *ae;
+ size_t ent_size, left = size;
+ char *bp = list;
+
+ while (True) {
+ if (filedes)
+ retval = attr_listf(filedes, attr_buffer, ATTR_MAX_VALUELEN, flags, cursor);
+ else
+ retval = attr_list(path, attr_buffer, ATTR_MAX_VALUELEN, flags, cursor);
+ if (retval) break;
+ for (index = 0; index < al->al_count; index++) {
+ ae = ATTR_ENTRY(attr_buffer, index);
+ ent_size = strlen(ae->a_name) + sizeof("user.");
+ if (left >= ent_size) {
+ strncpy(bp, "user.", sizeof("user."));
+ strncat(bp, ae->a_name, ent_size - sizeof("user."));
+ bp += ent_size;
+ left -= ent_size;
+ } else if (size) {
+ errno = ERANGE;
+ retval = -1;
+ break;
+ }
+ total_size += ent_size;
+ }
+ if (al->al_more == 0) break;
+ }
+ if (retval == 0) {
+ flags |= ATTR_ROOT;
+ cursor = 0;
+ while (True) {
+ if (filedes)
+ retval = attr_listf(filedes, attr_buffer, ATTR_MAX_VALUELEN, flags, cursor);
+ else
+ retval = attr_list(path, attr_buffer, ATTR_MAX_VALUELEN, flags, cursor);
+ if (retval) break;
+ for (index = 0; index < al->al_count; index++) {
+ ae = ATTR_ENTRY(attr_buffer, index);
+ ent_size = strlen(ae->a_name) + sizeof("system.");
+ if (left >= ent_size) {
+ strncpy(bp, "system.", sizeof("system."));
+ strncat(bp, ae->a_name, ent_size - sizeof("system."));
+ bp += ent_size;
+ left -= ent_size;
+ } else if (size) {
+ errno = ERANGE;
+ retval = -1;
+ break;
+ }
+ total_size += ent_size;
+ }
+ if (al->al_more == 0) break;
+ }
+ }
+ return (ssize_t)(retval ? retval : total_size);
+}
+
+#endif
+
+ssize_t sys_listxattr (const char *path, char *list, size_t size)
+{
+#if defined(HAVE_LISTXATTR)
+ return listxattr(path, list, size);
+#elif defined(HAVE_ATTR_LIST) && defined(HAVE_SYS_ATTRIBUTES_H)
+ return irix_attr_list(path, 0, list, size, 0);
+#else
+ errno = ENOSYS;
+ return -1;
+#endif
+}
+
+ssize_t sys_llistxattr (const char *path, char *list, size_t size)
+{
+#if defined(HAVE_LLISTXATTR)
+ return llistxattr(path, list, size);
+#elif defined(HAVE_ATTR_LIST) && defined(HAVE_SYS_ATTRIBUTES_H)
+ return irix_attr_list(path, 0, list, size, ATTR_DONTFOLLOW);
+#else
+ errno = ENOSYS;
+ return -1;
+#endif
+}
+
+ssize_t sys_flistxattr (int filedes, char *list, size_t size)
+{
+#if defined(HAVE_FLISTXATTR)
+ return flistxattr(filedes, list, size);
+#elif defined(HAVE_ATTR_LISTF)
+ return irix_attr_list(NULL, filedes, list, size, 0);
+#else
+ errno = ENOSYS;
+ return -1;
+#endif
+}
+
+int sys_removexattr (const char *path, const char *name)
+{
+#if defined(HAVE_REMOVEXATTR)
+ return removexattr(path, name);
+#elif defined(HAVE_ATTR_REMOVE)
+ int flags = 0;
+ char *attrname = strchr(name,'.') +1;
+
+ if (strncmp(name, "system", 6) == 0) flags |= ATTR_ROOT;
+
+ return attr_remove(path, attrname, flags);
+#else
+ errno = ENOSYS;
+ return -1;
+#endif
+}
+
+int sys_lremovexattr (const char *path, const char *name)
+{
+#if defined(HAVE_LREMOVEXATTR)
+ return lremovexattr(path, name);
+#elif defined(HAVE_ATTR_REMOVE)
+ int flags = ATTR_DONTFOLLOW;
+ char *attrname = strchr(name,'.') +1;
+
+ if (strncmp(name, "system", 6) == 0) flags |= ATTR_ROOT;
+
+ return attr_remove(path, attrname, flags);
+#else
+ errno = ENOSYS;
+ return -1;
+#endif
+}
+
+int sys_fremovexattr (int filedes, const char *name)
+{
+#if defined(HAVE_FREMOVEXATTR)
+ return fremovexattr(filedes, name);
+#elif defined(HAVE_ATTR_REMOVEF)
+ int flags = 0;
+ char *attrname = strchr(name,'.') +1;
+
+ if (strncmp(name, "system", 6) == 0) flags |= ATTR_ROOT;
+
+ return attr_removef(filedes, attrname, flags);
+#else
+ errno = ENOSYS;
+ return -1;
+#endif
+}
+
+#if !defined(HAVE_SETXATTR)
+#define XATTR_CREATE 0x1 /* set value, fail if attr already exists */
+#define XATTR_REPLACE 0x2 /* set value, fail if attr does not exist */
+#endif
+
+int sys_setxattr (const char *path, const char *name, const void *value, size_t size, int flags)
+{
+#if defined(HAVE_SETXATTR)
+ return setxattr(path, name, value, size, flags);
+#elif defined(HAVE_ATTR_SET)
+ int myflags = 0;
+ char *attrname = strchr(name,'.') +1;
+
+ if (strncmp(name, "system", 6) == 0) myflags |= ATTR_ROOT;
+ if (flags & XATTR_CREATE) myflags |= ATTR_CREATE;
+ if (flags & XATTR_REPLACE) myflags |= ATTR_REPLACE;
+
+ return attr_set(path, attrname, (const char *)value, size, myflags);
+#else
+ errno = ENOSYS;
+ return -1;
+#endif
+}
+
+int sys_lsetxattr (const char *path, const char *name, const void *value, size_t size, int flags)
+{
+#if defined(HAVE_LSETXATTR)
+ return lsetxattr(path, name, value, size, flags);
+#elif defined(HAVE_ATTR_SET)
+ int myflags = ATTR_DONTFOLLOW;
+ char *attrname = strchr(name,'.') +1;
+
+ if (strncmp(name, "system", 6) == 0) myflags |= ATTR_ROOT;
+ if (flags & XATTR_CREATE) myflags |= ATTR_CREATE;
+ if (flags & XATTR_REPLACE) myflags |= ATTR_REPLACE;
+
+ return attr_set(path, attrname, (const char *)value, size, myflags);
+#else
+ errno = ENOSYS;
+ return -1;
+#endif
+}
+
+int sys_fsetxattr (int filedes, const char *name, const void *value, size_t size, int flags)
+{
+#if defined(HAVE_FSETXATTR)
+ return fsetxattr(filedes, name, value, size, flags);
+#elif defined(HAVE_ATTR_SETF)
+ int myflags = 0;
+ char *attrname = strchr(name,'.') +1;
+
+ if (strncmp(name, "system", 6) == 0) myflags |= ATTR_ROOT;
+ if (flags & XATTR_CREATE) myflags |= ATTR_CREATE;
+ if (flags & XATTR_REPLACE) myflags |= ATTR_REPLACE;
+
+ return attr_setf(filedes, attrname, (const char *)value, size, myflags);
+#else
+ errno = ENOSYS;
+ return -1;
+#endif
+}
diff --git a/source/lib/system_smbd.c b/source/lib/system_smbd.c
new file mode 100644
index 00000000000..73c910e631d
--- /dev/null
+++ b/source/lib/system_smbd.c
@@ -0,0 +1,137 @@
+/*
+ Unix SMB/CIFS implementation.
+ system call wrapper interface.
+ Copyright (C) Andrew Tridgell 2002
+ Copyright (C) Andrew Barteltt 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 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.
+*/
+
+/*
+ This file may assume linkage with smbd - for things like become_root()
+ etc.
+*/
+
+#include "includes.h"
+
+#ifndef HAVE_GETGROUPLIST
+/*
+ This is a *much* faster way of getting the list of groups for a user
+ without changing the current supplemenrary group list. The old
+ method used getgrent() which could take 20 minutes on a really big
+ network with hundeds of thousands of groups and users. The new method
+ takes a couple of seconds.
+
+ NOTE!! this function only works if it is called as root!
+ */
+static int getgrouplist_internals(const char *user, gid_t gid, gid_t *groups, int *grpcnt)
+{
+ gid_t *gids_saved;
+ int ret, ngrp_saved, num_gids;
+
+ if (non_root_mode()) {
+ *grpcnt = 0;
+ return 0;
+ }
+
+ /* work out how many groups we need to save */
+ ngrp_saved = getgroups(0, NULL);
+ if (ngrp_saved == -1) {
+ /* this shouldn't happen */
+ return -1;
+ }
+
+ gids_saved = (gid_t *)malloc(sizeof(gid_t) * (ngrp_saved+1));
+ if (!gids_saved) {
+ errno = ENOMEM;
+ return -1;
+ }
+
+ ngrp_saved = getgroups(ngrp_saved, gids_saved);
+ if (ngrp_saved == -1) {
+ SAFE_FREE(gids_saved);
+ /* very strange! */
+ return -1;
+ }
+
+ if (initgroups(user, gid) != 0) {
+ DEBUG(0, ("getgrouplist_internals: initgroups() failed!\n"));
+ SAFE_FREE(gids_saved);
+ return -1;
+ }
+
+ /* this must be done to cope with systems that put the current egid in the
+ return from getgroups() */
+ save_re_gid();
+ set_effective_gid(gid);
+ setgid(gid);
+
+ num_gids = getgroups(0, NULL);
+ if (num_gids + 1 > *grpcnt) {
+ *grpcnt = num_gids + 1;
+ ret = -1;
+ } else {
+ ret = getgroups(*grpcnt - 1, &groups[1]);
+ if (ret >= 0) {
+ groups[0] = gid;
+ *grpcnt = ret + 1;
+ }
+ }
+
+ restore_re_gid();
+
+ if (sys_setgroups(ngrp_saved, gids_saved) != 0) {
+ /* yikes! */
+ DEBUG(0,("ERROR: getgrouplist: failed to reset group list!\n"));
+ smb_panic("getgrouplist: failed to reset group list!\n");
+ free(gids_saved);
+ return -1;
+ }
+
+ free(gids_saved);
+ return ret;
+}
+#endif
+
+int sys_getgrouplist(const char *user, gid_t gid, gid_t *groups, int *grpcnt)
+{
+ char *p;
+ int retval;
+
+ DEBUG(10,("sys_getgrouplist: user [%s]\n", user));
+
+ /* see if we should disable winbindd lookups for local users */
+ if ( (p = strchr(user, *lp_winbind_separator())) == NULL ) {
+ if ( !winbind_off() )
+ DEBUG(0,("sys_getgroup_list: Insufficient environment space for %s\n",
+ WINBINDD_DONT_ENV));
+ else
+ DEBUG(10,("sys_getgrouplist(): disabled winbindd for group lookup [user == %s]\n",
+ user));
+ }
+
+#ifdef HAVE_GETGROUPLIST
+ retval = getgrouplist(user, gid, groups, grpcnt);
+#else
+ become_root();
+ retval = getgrouplist_internals(user, gid, groups, grpcnt);
+ unbecome_root();
+#endif
+
+ /* allow winbindd lookups */
+ winbind_on();
+
+ return retval;
+}
diff --git a/source/lib/talloc.c b/source/lib/talloc.c
new file mode 100644
index 00000000000..485dc28f31d
--- /dev/null
+++ b/source/lib/talloc.c
@@ -0,0 +1,449 @@
+/*
+ Samba Unix SMB/CIFS implementation.
+ Samba temporary memory allocation functions
+ Copyright (C) Andrew Tridgell 2000
+ Copyright (C) 2001, 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 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.
+*/
+
+/**
+ @defgroup talloc Simple memory allocator
+ @{
+
+ This is a very simple temporary memory allocator. To use it do the following:
+
+ 1) when you first want to allocate a pool of meomry use
+ talloc_init() and save the resulting context pointer somewhere
+
+ 2) to allocate memory use talloc()
+
+ 3) when _all_ of the memory allocated using this context is no longer needed
+ use talloc_destroy()
+
+ talloc does not zero the memory. It guarantees memory of a
+ TALLOC_ALIGN alignment
+
+ @sa talloc.h
+*/
+
+/**
+ * @todo We could allocate both the talloc_chunk structure, and the
+ * memory it contains all in one allocation, which might be a bit
+ * faster and perhaps use less memory overhead.
+ *
+ * That smells like a premature optimization, though. -- mbp
+ **/
+
+/**
+ * If you want testing for memory corruption, link with dmalloc or use
+ * Insure++. It doesn't seem useful to duplicate them here.
+ **/
+
+#include "includes.h"
+
+
+/**
+ * Start of linked list of all talloc pools.
+ *
+ * @todo We should turn the global list off when using Insure++,
+ * otherwise all the memory will be seen as still reachable.
+ **/
+static TALLOC_CTX *list_head = NULL;
+
+
+/**
+ * Add to the global list
+ **/
+static void talloc_enroll(TALLOC_CTX *t)
+{
+ t->next_ctx = list_head;
+ list_head = t;
+}
+
+
+static void talloc_disenroll(TALLOC_CTX *t)
+{
+ TALLOC_CTX **ttmp;
+
+ /* Use a double-* so that no special case is required for the
+ * list head. */
+ for (ttmp = &list_head; *ttmp; ttmp = &((*ttmp)->next_ctx))
+ if (*ttmp == t) {
+ /* ttmp is the link that points to t, either
+ * list_head or the next_ctx link in its
+ * predecessor */
+ *ttmp = t->next_ctx;
+ t->next_ctx = NULL; /* clobber */
+ return;
+ }
+ abort(); /* oops, this talloc was already
+ * clobbered or something else went
+ * wrong. */
+}
+
+
+/** Create a new talloc context. **/
+static TALLOC_CTX *talloc_init_internal(void)
+{
+ TALLOC_CTX *t;
+
+ t = (TALLOC_CTX *)malloc(sizeof(TALLOC_CTX));
+ if (t) {
+ t->list = NULL;
+ t->total_alloc_size = 0;
+ t->name = NULL;
+ talloc_enroll(t);
+ }
+
+ return t;
+}
+
+
+
+/**
+ * Create a new talloc context, with a name specifying its purpose.
+ **/
+
+ TALLOC_CTX *talloc_init(char const *fmt, ...)
+{
+ TALLOC_CTX *t;
+ va_list ap;
+
+ t = talloc_init_internal();
+ if (t && fmt) {
+ /*
+ * t->name must not be talloced.
+ * as destroying the pool would destroy it. JRA.
+ */
+ t->name = NULL;
+ va_start(ap, fmt);
+ vasprintf(&t->name, fmt, ap);
+ va_end(ap);
+ if (!t->name) {
+ talloc_destroy(t);
+ t = NULL;
+ }
+ }
+
+ return t;
+}
+
+
+/** Allocate a bit of memory from the specified pool **/
+void *talloc(TALLOC_CTX *t, size_t size)
+{
+ void *p;
+ struct talloc_chunk *tc;
+
+ if (!t || size == 0) return NULL;
+
+ p = malloc(size);
+ if (p) {
+ tc = malloc(sizeof(*tc));
+ if (tc) {
+ tc->ptr = p;
+ tc->size = size;
+ tc->next = t->list;
+ t->list = tc;
+ t->total_alloc_size += size;
+ }
+ else {
+ SAFE_FREE(p);
+ }
+ }
+ return p;
+}
+
+/** A talloc version of realloc */
+void *talloc_realloc(TALLOC_CTX *t, void *ptr, size_t size)
+{
+ struct talloc_chunk *tc;
+ void *new_ptr;
+
+ /* size zero is equivalent to free() */
+ if (!t || size == 0)
+ return NULL;
+
+ /* realloc(NULL) is equavalent to malloc() */
+ if (ptr == NULL)
+ return talloc(t, size);
+
+ for (tc=t->list; tc; tc=tc->next) {
+ if (tc->ptr == ptr) {
+ new_ptr = Realloc(ptr, size);
+ if (new_ptr) {
+ t->total_alloc_size += (size - tc->size);
+ tc->size = size;
+ tc->ptr = new_ptr;
+ }
+ return new_ptr;
+ }
+ }
+ return NULL;
+}
+
+/** Destroy all the memory allocated inside @p t, but not @p t
+ * itself. */
+void talloc_destroy_pool(TALLOC_CTX *t)
+{
+ struct talloc_chunk *c;
+
+ if (!t)
+ return;
+
+ while (t->list) {
+ c = t->list->next;
+ SAFE_FREE(t->list->ptr);
+ SAFE_FREE(t->list);
+ t->list = c;
+ }
+
+ t->total_alloc_size = 0;
+}
+
+/** Destroy a whole pool including the context */
+void talloc_destroy(TALLOC_CTX *t)
+{
+ if (!t)
+ return;
+
+ talloc_destroy_pool(t);
+ talloc_disenroll(t);
+ SAFE_FREE(t->name);
+ memset(t, 0, sizeof(TALLOC_CTX));
+ SAFE_FREE(t);
+}
+
+/** Return the current total size of the pool. */
+size_t talloc_pool_size(TALLOC_CTX *t)
+{
+ if (t)
+ return t->total_alloc_size;
+ else
+ return 0;
+}
+
+const char * talloc_pool_name(TALLOC_CTX const *t)
+{
+ if (t)
+ return t->name;
+ else
+ return NULL;
+}
+
+
+/** talloc and zero memory. */
+void *talloc_zero(TALLOC_CTX *t, size_t size)
+{
+ void *p = talloc(t, size);
+
+ if (p)
+ memset(p, '\0', size);
+
+ return p;
+}
+
+/** memdup with a talloc. */
+void *talloc_memdup(TALLOC_CTX *t, const void *p, size_t size)
+{
+ void *newp = talloc(t,size);
+
+ if (newp)
+ memcpy(newp, p, size);
+
+ return newp;
+}
+
+/** strdup with a talloc */
+char *talloc_strdup(TALLOC_CTX *t, const char *p)
+{
+ if (p)
+ return talloc_memdup(t, p, strlen(p) + 1);
+ else
+ return NULL;
+}
+
+/** strdup_w with a talloc */
+smb_ucs2_t *talloc_strdup_w(TALLOC_CTX *t, const smb_ucs2_t *p)
+{
+ if (p)
+ return talloc_memdup(t, p, (strlen_w(p) + 1) * sizeof(smb_ucs2_t));
+ else
+ return NULL;
+}
+
+/**
+ * Perform string formatting, and return a pointer to newly allocated
+ * memory holding the result, inside a memory pool.
+ **/
+ char *talloc_asprintf(TALLOC_CTX *t, const char *fmt, ...)
+{
+ va_list ap;
+ char *ret;
+
+ va_start(ap, fmt);
+ ret = talloc_vasprintf(t, fmt, ap);
+ va_end(ap);
+ return ret;
+}
+
+
+ char *talloc_vasprintf(TALLOC_CTX *t, const char *fmt, va_list ap)
+{
+ int len;
+ char *ret;
+ va_list ap2;
+
+ VA_COPY(ap2, ap);
+
+ len = vsnprintf(NULL, 0, fmt, ap2);
+
+ ret = talloc(t, len+1);
+ if (ret) {
+ VA_COPY(ap2, ap);
+ vsnprintf(ret, len+1, fmt, ap2);
+ }
+
+ return ret;
+}
+
+
+/**
+ * Realloc @p s to append the formatted result of @p fmt and return @p
+ * s, which may have moved. Good for gradually accumulating output
+ * into a string buffer.
+ **/
+ char *talloc_asprintf_append(TALLOC_CTX *t, char *s,
+ const char *fmt, ...)
+{
+ va_list ap;
+
+ va_start(ap, fmt);
+ s = talloc_vasprintf_append(t, s, fmt, ap);
+ va_end(ap);
+ return s;
+}
+
+
+
+/**
+ * Realloc @p s to append the formatted result of @p fmt and @p ap,
+ * and return @p s, which may have moved. Good for gradually
+ * accumulating output into a string buffer.
+ **/
+ char *talloc_vasprintf_append(TALLOC_CTX *t, char *s,
+ const char *fmt, va_list ap)
+{
+ int len, s_len;
+ va_list ap2;
+
+ VA_COPY(ap2, ap);
+
+ s_len = strlen(s);
+ len = vsnprintf(NULL, 0, fmt, ap2);
+
+ s = talloc_realloc(t, s, s_len + len+1);
+ if (!s) return NULL;
+
+ VA_COPY(ap2, ap);
+
+ vsnprintf(s+s_len, len+1, fmt, ap2);
+
+ return s;
+}
+
+
+/**
+ * Return a human-readable description of all talloc memory usage.
+ * The result is allocated from @p t.
+ **/
+char *talloc_describe_all(TALLOC_CTX *rt)
+{
+ int n_pools = 0, total_chunks = 0;
+ size_t total_bytes = 0;
+ TALLOC_CTX *it;
+ char *s;
+
+ if (!rt) return NULL;
+
+ s = talloc_asprintf(rt, "global talloc allocations in pid: %u\n",
+ (unsigned) sys_getpid());
+ s = talloc_asprintf_append(rt, s, "%-40s %8s %8s\n",
+ "name", "chunks", "bytes");
+ s = talloc_asprintf_append(rt, s, "%-40s %8s %8s\n",
+ "----------------------------------------",
+ "--------",
+ "--------");
+
+ for (it = list_head; it; it = it->next_ctx) {
+ size_t bytes;
+ int n_chunks;
+ fstring what;
+
+ n_pools++;
+
+ talloc_get_allocation(it, &bytes, &n_chunks);
+
+ if (it->name)
+ fstrcpy(what, it->name);
+ else
+ slprintf(what, sizeof(what), "@%p", it);
+
+ s = talloc_asprintf_append(rt, s, "%-40s %8u %8u\n",
+ what,
+ (unsigned) n_chunks,
+ (unsigned) bytes);
+ total_bytes += bytes;
+ total_chunks += n_chunks;
+ }
+
+ s = talloc_asprintf_append(rt, s, "%-40s %8s %8s\n",
+ "----------------------------------------",
+ "--------",
+ "--------");
+
+ s = talloc_asprintf_append(rt, s, "%-40s %8u %8u\n",
+ "TOTAL",
+ (unsigned) total_chunks, (unsigned) total_bytes);
+
+ return s;
+}
+
+
+
+/**
+ * Return an estimated memory usage for the specified pool. This does
+ * not include memory used by the underlying malloc implementation.
+ **/
+void talloc_get_allocation(TALLOC_CTX *t,
+ size_t *total_bytes,
+ int *n_chunks)
+{
+ struct talloc_chunk *chunk;
+
+ if (t) {
+ *total_bytes = 0;
+ *n_chunks = 0;
+
+ for (chunk = t->list; chunk; chunk = chunk->next) {
+ n_chunks[0]++;
+ *total_bytes += chunk->size;
+ }
+ }
+}
+
+
+/** @} */
diff --git a/source/lib/tallocmsg.c b/source/lib/tallocmsg.c
new file mode 100644
index 00000000000..bbe1ee60a46
--- /dev/null
+++ b/source/lib/tallocmsg.c
@@ -0,0 +1,58 @@
+/*
+ samba -- Unix SMB/CIFS implementation.
+ 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 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"
+
+/**
+ * @file tallocmsg.c
+ *
+ * Glue code between talloc profiling and the Samba messaging system.
+ **/
+
+
+/**
+ * Respond to a POOL_USAGE message by sending back string form of memory
+ * usage stats.
+ **/
+void msg_pool_usage(int msg_type, pid_t src_pid,
+ void *UNUSED(buf), size_t UNUSED(len))
+{
+ char *reply;
+ TALLOC_CTX *reply_pool = talloc_init("msg_pool_usage");
+
+ SMB_ASSERT(msg_type == MSG_REQ_POOL_USAGE);
+
+ DEBUG(2,("Got POOL_USAGE\n"));
+
+ reply = talloc_describe_all(reply_pool);
+
+ message_send_pid(src_pid, MSG_POOL_USAGE,
+ reply, strlen(reply)+1, True);
+
+ talloc_destroy(reply_pool);
+}
+
+/**
+ * Register handler for MSG_REQ_POOL_USAGE
+ **/
+void register_msg_pool_usage(void)
+{
+ message_register(MSG_REQ_POOL_USAGE, msg_pool_usage);
+ DEBUG(2, ("Registered MSG_REQ_POOL_USAGE\n"));
+}
diff --git a/source/lib/talloctort.c b/source/lib/talloctort.c
new file mode 100644
index 00000000000..0cdf693bb91
--- /dev/null
+++ b/source/lib/talloctort.c
@@ -0,0 +1,65 @@
+/*
+ Unix SMB/CIFS implementation.
+ Samba temporary memory allocation functions -- torturer
+ Copyright (C) 2001 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 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"
+
+#define NCTX 10
+#define NOBJ 20
+
+int main(void)
+{
+ int i;
+ TALLOC_CTX *ctx[NCTX];
+
+ for (i = 0; i < NCTX; i++) {
+ ctx[i] = talloc_init("torture(%d)", i);
+ }
+
+ for (i = 0; i < NCTX; i++) {
+ int j;
+ for (j = 0; j < NOBJ; j++) {
+ char *p;
+ size_t size = 1<<(i/3+j);
+
+ p = talloc(ctx[i], size);
+ if (!p) {
+ fprintf(stderr,
+ "failed to talloc %.0f bytes\n",
+ (double) size);
+ exit(1);
+ }
+
+ memset(p, 'A' + j, size);
+ }
+ }
+
+ for (i = 0; i < NCTX; i++) {
+ printf("talloc@%p %-40s %ldkB\n", ctx[i],
+ talloc_pool_name(ctx[i]),
+ (unsigned long)talloc_pool_size(ctx[i]) >> 10);
+ }
+
+ printf("%s", talloc_describe_all(ctx[0]));
+
+ for (i = NCTX - 1; i >= 0; i--)
+ talloc_destroy(ctx[i]);
+
+ return 0;
+}
diff --git a/source/lib/time.c b/source/lib/time.c
new file mode 100644
index 00000000000..faca2cba879
--- /dev/null
+++ b/source/lib/time.c
@@ -0,0 +1,756 @@
+/*
+ Unix SMB/CIFS implementation.
+ time handling functions
+ Copyright (C) Andrew Tridgell 1992-1998
+ Copyright (C) Stefan (metze) Metzmacher 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 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"
+
+/*
+ This stuff was largely rewritten by Paul Eggert <eggert@twinsun.com>
+ in May 1996
+ */
+
+
+int extra_time_offset = 0;
+
+#ifndef CHAR_BIT
+#define CHAR_BIT 8
+#endif
+
+#ifndef TIME_T_MIN
+#define TIME_T_MIN ((time_t)0 < (time_t) -1 ? (time_t) 0 \
+ : ~ (time_t) 0 << (sizeof (time_t) * CHAR_BIT - 1))
+#endif
+#ifndef TIME_T_MAX
+#define TIME_T_MAX (~ (time_t) 0 - TIME_T_MIN)
+#endif
+
+void get_nttime_max(NTTIME *t)
+{
+ /* FIXME: This is incorrect */
+ unix_to_nt_time(t, get_time_t_max());
+}
+
+/*******************************************************************
+ External access to time_t_min and time_t_max.
+********************************************************************/
+
+time_t get_time_t_max(void)
+{
+ return TIME_T_MAX;
+}
+
+/*******************************************************************
+a gettimeofday wrapper
+********************************************************************/
+void GetTimeOfDay(struct timeval *tval)
+{
+#ifdef HAVE_GETTIMEOFDAY_TZ
+ gettimeofday(tval,NULL);
+#else
+ gettimeofday(tval);
+#endif
+}
+
+#define TM_YEAR_BASE 1900
+
+/*******************************************************************
+yield the difference between *A and *B, in seconds, ignoring leap seconds
+********************************************************************/
+static int tm_diff(struct tm *a, struct tm *b)
+{
+ int ay = a->tm_year + (TM_YEAR_BASE - 1);
+ int by = b->tm_year + (TM_YEAR_BASE - 1);
+ int intervening_leap_days =
+ (ay/4 - by/4) - (ay/100 - by/100) + (ay/400 - by/400);
+ int years = ay - by;
+ int days = 365*years + intervening_leap_days + (a->tm_yday - b->tm_yday);
+ int hours = 24*days + (a->tm_hour - b->tm_hour);
+ int minutes = 60*hours + (a->tm_min - b->tm_min);
+ int seconds = 60*minutes + (a->tm_sec - b->tm_sec);
+
+ return seconds;
+}
+
+/*******************************************************************
+ return the UTC offset in seconds west of UTC, or 0 if it cannot be determined
+ ******************************************************************/
+static int TimeZone(time_t t)
+{
+ struct tm *tm = gmtime(&t);
+ struct tm tm_utc;
+ if (!tm)
+ return 0;
+ tm_utc = *tm;
+ tm = localtime(&t);
+ if (!tm)
+ return 0;
+ return tm_diff(&tm_utc,tm);
+
+}
+
+static BOOL done_serverzone_init;
+
+/* Return the smb serverzone value */
+
+static int get_serverzone(void)
+{
+ static int serverzone;
+
+ if (!done_serverzone_init) {
+ serverzone = TimeZone(time(NULL));
+
+ if ((serverzone % 60) != 0) {
+ DEBUG(1,("WARNING: Your timezone is not a multiple of 1 minute.\n"));
+ }
+
+ DEBUG(4,("Serverzone is %d\n",serverzone));
+
+ done_serverzone_init = True;
+ }
+
+ return serverzone;
+}
+
+/* Re-read the smb serverzone value */
+
+static struct timeval start_time_hires;
+
+void TimeInit(void)
+{
+ done_serverzone_init = False;
+ get_serverzone();
+ /* Save the start time of this process. */
+ if (start_time_hires.tv_sec == 0 && start_time_hires.tv_usec == 0)
+ GetTimeOfDay(&start_time_hires);
+}
+
+/**********************************************************************
+ Return a timeval struct of the uptime of this process. As TimeInit is
+ done before a daemon fork then this is the start time from the parent
+ daemon start. JRA.
+***********************************************************************/
+
+void get_process_uptime(struct timeval *ret_time)
+{
+ struct timeval time_now_hires;
+
+ GetTimeOfDay(&time_now_hires);
+ ret_time->tv_sec = time_now_hires.tv_sec - start_time_hires.tv_sec;
+ ret_time->tv_usec = time_now_hires.tv_usec - start_time_hires.tv_usec;
+ if (time_now_hires.tv_usec < start_time_hires.tv_usec) {
+ ret_time->tv_sec -= 1;
+ ret_time->tv_usec = 1000000 + (time_now_hires.tv_usec - start_time_hires.tv_usec);
+ } else
+ ret_time->tv_usec = time_now_hires.tv_usec - start_time_hires.tv_usec;
+}
+
+/*******************************************************************
+return the same value as TimeZone, but it should be more efficient.
+
+We keep a table of DST offsets to prevent calling localtime() on each
+call of this function. This saves a LOT of time on many unixes.
+
+Updated by Paul Eggert <eggert@twinsun.com>
+********************************************************************/
+static int TimeZoneFaster(time_t t)
+{
+ static struct dst_table {time_t start,end; int zone;} *tdt, *dst_table = NULL;
+ static int table_size = 0;
+ int i;
+ int zone = 0;
+
+ if (t == 0) t = time(NULL);
+
+ /* Tunis has a 8 day DST region, we need to be careful ... */
+#define MAX_DST_WIDTH (365*24*60*60)
+#define MAX_DST_SKIP (7*24*60*60)
+
+ for (i=0;i<table_size;i++)
+ if (t >= dst_table[i].start && t <= dst_table[i].end) break;
+
+ if (i<table_size) {
+ zone = dst_table[i].zone;
+ } else {
+ time_t low,high;
+
+ zone = TimeZone(t);
+ tdt = (struct dst_table *)Realloc(dst_table,
+ sizeof(dst_table[0])*(i+1));
+ if (!tdt) {
+ DEBUG(0,("TimeZoneFaster: out of memory!\n"));
+ SAFE_FREE(dst_table);
+ table_size = 0;
+ } else {
+ dst_table = tdt;
+ table_size++;
+
+ dst_table[i].zone = zone;
+ dst_table[i].start = dst_table[i].end = t;
+
+ /* no entry will cover more than 6 months */
+ low = t - MAX_DST_WIDTH/2;
+ if (t < low)
+ low = TIME_T_MIN;
+
+ high = t + MAX_DST_WIDTH/2;
+ if (high < t)
+ high = TIME_T_MAX;
+
+ /* widen the new entry using two bisection searches */
+ while (low+60*60 < dst_table[i].start) {
+ if (dst_table[i].start - low > MAX_DST_SKIP*2)
+ t = dst_table[i].start - MAX_DST_SKIP;
+ else
+ t = low + (dst_table[i].start-low)/2;
+ if (TimeZone(t) == zone)
+ dst_table[i].start = t;
+ else
+ low = t;
+ }
+
+ while (high-60*60 > dst_table[i].end) {
+ if (high - dst_table[i].end > MAX_DST_SKIP*2)
+ t = dst_table[i].end + MAX_DST_SKIP;
+ else
+ t = high - (high-dst_table[i].end)/2;
+ if (TimeZone(t) == zone)
+ dst_table[i].end = t;
+ else
+ high = t;
+ }
+#if 0
+ DEBUG(1,("Added DST entry from %s ",
+ asctime(localtime(&dst_table[i].start))));
+ DEBUG(1,("to %s (%d)\n",asctime(localtime(&dst_table[i].end)),
+ dst_table[i].zone));
+#endif
+ }
+ }
+ return zone;
+}
+
+/****************************************************************************
+ return the UTC offset in seconds west of UTC, adjusted for extra time offset
+ **************************************************************************/
+int TimeDiff(time_t t)
+{
+ return TimeZoneFaster(t) + 60*extra_time_offset;
+}
+
+
+/****************************************************************************
+ return the UTC offset in seconds west of UTC, adjusted for extra time
+ offset, for a local time value. If ut = lt + LocTimeDiff(lt), then
+ lt = ut - TimeDiff(ut), but the converse does not necessarily hold near
+ daylight savings transitions because some local times are ambiguous.
+ LocTimeDiff(t) equals TimeDiff(t) except near daylight savings transitions.
+ +**************************************************************************/
+static int LocTimeDiff(time_t lte)
+{
+ time_t lt = lte - 60*extra_time_offset;
+ int d = TimeZoneFaster(lt);
+ time_t t = lt + d;
+
+ /* if overflow occurred, ignore all the adjustments so far */
+ if (((lte < lt) ^ (extra_time_offset < 0)) | ((t < lt) ^ (d < 0)))
+ t = lte;
+
+ /* now t should be close enough to the true UTC to yield the right answer */
+ return TimeDiff(t);
+}
+
+
+/****************************************************************************
+try to optimise the localtime call, it can be quite expensive on some machines
+****************************************************************************/
+struct tm *LocalTime(time_t *t)
+{
+ time_t t2 = *t;
+
+ t2 -= TimeDiff(t2);
+
+ return(gmtime(&t2));
+}
+
+#define TIME_FIXUP_CONSTANT (369.0*365.25*24*60*60-(3.0*24*60*60+6.0*60*60))
+
+/****************************************************************************
+interpret an 8 byte "filetime" structure to a time_t
+It's originally in "100ns units since jan 1st 1601"
+
+It appears to be kludge-GMT (at least for file listings). This means
+its the GMT you get by taking a localtime and adding the
+serverzone. This is NOT the same as GMT in some cases. This routine
+converts this to real GMT.
+****************************************************************************/
+time_t nt_time_to_unix(NTTIME *nt)
+{
+ double d;
+ time_t ret;
+ /* The next two lines are a fix needed for the
+ broken SCO compiler. JRA. */
+ time_t l_time_min = TIME_T_MIN;
+ time_t l_time_max = TIME_T_MAX;
+
+ if (nt->high == 0 || (nt->high == 0xffffffff && nt->low == 0xffffffff))
+ return(0);
+
+ d = ((double)nt->high)*4.0*(double)(1<<30);
+ d += (nt->low&0xFFF00000);
+ d *= 1.0e-7;
+
+ /* now adjust by 369 years to make the secs since 1970 */
+ d -= TIME_FIXUP_CONSTANT;
+
+ if (d <= l_time_min)
+ return (l_time_min);
+
+ if (d >= l_time_max)
+ return (l_time_max);
+
+ ret = (time_t)(d+0.5);
+
+ /* this takes us from kludge-GMT to real GMT */
+ ret -= get_serverzone();
+ ret += LocTimeDiff(ret);
+
+ return(ret);
+}
+
+/****************************************************************************
+ Convert a NTTIME structure to a time_t.
+ It's originally in "100ns units".
+
+ This is an absolute version of the one above.
+ By absolute I mean, it doesn't adjust from 1/1/1601 to 1/1/1970
+ if the NTTIME was 5 seconds, the time_t is 5 seconds. JFM
+****************************************************************************/
+
+time_t nt_time_to_unix_abs(NTTIME *nt)
+{
+ double d;
+ time_t ret;
+ /* The next two lines are a fix needed for the
+ broken SCO compiler. JRA. */
+ time_t l_time_min = TIME_T_MIN;
+ time_t l_time_max = TIME_T_MAX;
+
+ if (nt->high == 0)
+ return(0);
+
+ if (nt->high==0x80000000 && nt->low==0)
+ return -1;
+
+ /* reverse the time */
+ /* it's a negative value, turn it to positive */
+ nt->high=~nt->high;
+ nt->low=~nt->low;
+
+ d = ((double)nt->high)*4.0*(double)(1<<30);
+ d += (nt->low&0xFFF00000);
+ d *= 1.0e-7;
+
+ if (!(l_time_min <= d && d <= l_time_max))
+ return(0);
+
+ ret = (time_t)(d+0.5);
+
+ return(ret);
+}
+
+/****************************************************************************
+interprets an nt time into a unix time_t
+****************************************************************************/
+time_t interpret_long_date(char *p)
+{
+ NTTIME nt;
+ nt.low = IVAL(p,0);
+ nt.high = IVAL(p,4);
+ return nt_time_to_unix(&nt);
+}
+
+/****************************************************************************
+put a 8 byte filetime from a time_t
+This takes real GMT as input and converts to kludge-GMT
+****************************************************************************/
+void unix_to_nt_time(NTTIME *nt, time_t t)
+{
+ double d;
+
+ if (t==0)
+ {
+ nt->low = 0;
+ nt->high = 0;
+ return;
+ }
+ if (t == TIME_T_MAX)
+ {
+ nt->low = 0xffffffff;
+ nt->high = 0x7fffffff;
+ return;
+ }
+ if (t == -1)
+ {
+ nt->low = 0xffffffff;
+ nt->high = 0xffffffff;
+ return;
+ }
+
+ /* this converts GMT to kludge-GMT */
+ t -= TimeDiff(t) - get_serverzone();
+
+ d = (double)(t);
+ d += TIME_FIXUP_CONSTANT;
+ d *= 1.0e7;
+
+ nt->high = (uint32)(d * (1.0/(4.0*(double)(1<<30))));
+ nt->low = (uint32)(d - ((double)nt->high)*4.0*(double)(1<<30));
+}
+
+/****************************************************************************
+ Convert a time_t to a NTTIME structure
+
+ This is an absolute version of the one above.
+ By absolute I mean, it doesn't adjust from 1/1/1970 to 1/1/1601
+ If the nttime_t was 5 seconds, the NTTIME is 5 seconds. JFM
+****************************************************************************/
+
+void unix_to_nt_time_abs(NTTIME *nt, time_t t)
+{
+ double d;
+
+ if (t==0) {
+ nt->low = 0;
+ nt->high = 0;
+ return;
+ }
+
+ if (t == TIME_T_MAX) {
+ nt->low = 0xffffffff;
+ nt->high = 0x7fffffff;
+ return;
+ }
+
+ if (t == -1) {
+ /* that's what NT uses for infinite */
+ nt->low = 0x0;
+ nt->high = 0x80000000;
+ return;
+ }
+
+ d = (double)(t);
+ d *= 1.0e7;
+
+ nt->high = (uint32)(d * (1.0/(4.0*(double)(1<<30))));
+ nt->low = (uint32)(d - ((double)nt->high)*4.0*(double)(1<<30));
+
+ /* convert to a negative value */
+ nt->high=~nt->high;
+ nt->low=~nt->low;
+}
+
+/****************************************************************************
+take a Unix time and convert to an NTTIME structure and place in buffer
+pointed to by p.
+****************************************************************************/
+void put_long_date(char *p,time_t t)
+{
+ NTTIME nt;
+ unix_to_nt_time(&nt, t);
+ SIVAL(p, 0, nt.low);
+ SIVAL(p, 4, nt.high);
+}
+
+/****************************************************************************
+check if it's a null mtime
+****************************************************************************/
+BOOL null_mtime(time_t mtime)
+{
+ if (mtime == 0 || mtime == (time_t)0xFFFFFFFF || mtime == (time_t)-1)
+ return(True);
+ return(False);
+}
+
+/*******************************************************************
+ create a 16 bit dos packed date
+********************************************************************/
+static uint16 make_dos_date1(struct tm *t)
+{
+ uint16 ret=0;
+ ret = (((unsigned)(t->tm_mon+1)) >> 3) | ((t->tm_year-80) << 1);
+ ret = ((ret&0xFF)<<8) | (t->tm_mday | (((t->tm_mon+1) & 0x7) << 5));
+ return(ret);
+}
+
+/*******************************************************************
+ create a 16 bit dos packed time
+********************************************************************/
+static uint16 make_dos_time1(struct tm *t)
+{
+ uint16 ret=0;
+ ret = ((((unsigned)t->tm_min >> 3)&0x7) | (((unsigned)t->tm_hour) << 3));
+ ret = ((ret&0xFF)<<8) | ((t->tm_sec/2) | ((t->tm_min & 0x7) << 5));
+ return(ret);
+}
+
+/*******************************************************************
+ create a 32 bit dos packed date/time from some parameters
+ This takes a GMT time and returns a packed localtime structure
+********************************************************************/
+static uint32 make_dos_date(time_t unixdate)
+{
+ struct tm *t;
+ uint32 ret=0;
+
+ t = LocalTime(&unixdate);
+ if (!t)
+ return 0xFFFFFFFF;
+
+ ret = make_dos_date1(t);
+ ret = ((ret&0xFFFF)<<16) | make_dos_time1(t);
+
+ return(ret);
+}
+
+/*******************************************************************
+put a dos date into a buffer (time/date format)
+This takes GMT time and puts local time in the buffer
+********************************************************************/
+void put_dos_date(char *buf,int offset,time_t unixdate)
+{
+ uint32 x = make_dos_date(unixdate);
+ SIVAL(buf,offset,x);
+}
+
+/*******************************************************************
+put a dos date into a buffer (date/time format)
+This takes GMT time and puts local time in the buffer
+********************************************************************/
+void put_dos_date2(char *buf,int offset,time_t unixdate)
+{
+ uint32 x = make_dos_date(unixdate);
+ x = ((x&0xFFFF)<<16) | ((x&0xFFFF0000)>>16);
+ SIVAL(buf,offset,x);
+}
+
+/*******************************************************************
+put a dos 32 bit "unix like" date into a buffer. This routine takes
+GMT and converts it to LOCAL time before putting it (most SMBs assume
+localtime for this sort of date)
+********************************************************************/
+void put_dos_date3(char *buf,int offset,time_t unixdate)
+{
+ if (!null_mtime(unixdate))
+ unixdate -= TimeDiff(unixdate);
+ SIVAL(buf,offset,unixdate);
+}
+
+/*******************************************************************
+ interpret a 32 bit dos packed date/time to some parameters
+********************************************************************/
+static void interpret_dos_date(uint32 date,int *year,int *month,int *day,int *hour,int *minute,int *second)
+{
+ uint32 p0,p1,p2,p3;
+
+ p0=date&0xFF; p1=((date&0xFF00)>>8)&0xFF;
+ p2=((date&0xFF0000)>>16)&0xFF; p3=((date&0xFF000000)>>24)&0xFF;
+
+ *second = 2*(p0 & 0x1F);
+ *minute = ((p0>>5)&0xFF) + ((p1&0x7)<<3);
+ *hour = (p1>>3)&0xFF;
+ *day = (p2&0x1F);
+ *month = ((p2>>5)&0xFF) + ((p3&0x1)<<3) - 1;
+ *year = ((p3>>1)&0xFF) + 80;
+}
+
+/*******************************************************************
+ create a unix date (int GMT) from a dos date (which is actually in
+ localtime)
+********************************************************************/
+time_t make_unix_date(void *date_ptr)
+{
+ uint32 dos_date=0;
+ struct tm t;
+ time_t ret;
+
+ dos_date = IVAL(date_ptr,0);
+
+ if (dos_date == 0) return(0);
+
+ interpret_dos_date(dos_date,&t.tm_year,&t.tm_mon,
+ &t.tm_mday,&t.tm_hour,&t.tm_min,&t.tm_sec);
+ t.tm_isdst = -1;
+
+ /* mktime() also does the local to GMT time conversion for us */
+ ret = mktime(&t);
+
+ return(ret);
+}
+
+/*******************************************************************
+like make_unix_date() but the words are reversed
+********************************************************************/
+time_t make_unix_date2(void *date_ptr)
+{
+ uint32 x,x2;
+
+ x = IVAL(date_ptr,0);
+ x2 = ((x&0xFFFF)<<16) | ((x&0xFFFF0000)>>16);
+ SIVAL(&x,0,x2);
+
+ return(make_unix_date((void *)&x));
+}
+
+/*******************************************************************
+ create a unix GMT date from a dos date in 32 bit "unix like" format
+ these generally arrive as localtimes, with corresponding DST
+ ******************************************************************/
+time_t make_unix_date3(void *date_ptr)
+{
+ time_t t = (time_t)IVAL(date_ptr,0);
+ if (!null_mtime(t))
+ t += LocTimeDiff(t);
+ return(t);
+}
+
+
+/***************************************************************************
+return a HTTP/1.0 time string
+ ***************************************************************************/
+char *http_timestring(time_t t)
+{
+ static fstring buf;
+ struct tm *tm = LocalTime(&t);
+
+ if (!tm)
+ slprintf(buf,sizeof(buf)-1,"%ld seconds since the Epoch",(long)t);
+ else
+#ifndef HAVE_STRFTIME
+ fstrcpy(buf, asctime(tm));
+ if(buf[strlen(buf)-1] == '\n')
+ buf[strlen(buf)-1] = 0;
+#else /* !HAVE_STRFTIME */
+ strftime(buf, sizeof(buf)-1, "%a, %d %b %Y %H:%M:%S %Z", tm);
+#endif /* !HAVE_STRFTIME */
+ return buf;
+}
+
+
+
+/****************************************************************************
+ Return the date and time as a string
+****************************************************************************/
+
+char *timestring(BOOL hires)
+{
+ static fstring TimeBuf;
+ struct timeval tp;
+ time_t t;
+ struct tm *tm;
+
+ if (hires) {
+ GetTimeOfDay(&tp);
+ t = (time_t)tp.tv_sec;
+ } else {
+ t = time(NULL);
+ }
+ tm = LocalTime(&t);
+ if (!tm) {
+ if (hires) {
+ slprintf(TimeBuf,
+ sizeof(TimeBuf)-1,
+ "%ld.%06ld seconds since the Epoch",
+ (long)tp.tv_sec,
+ (long)tp.tv_usec);
+ } else {
+ slprintf(TimeBuf,
+ sizeof(TimeBuf)-1,
+ "%ld seconds since the Epoch",
+ (long)t);
+ }
+ } else {
+#ifdef HAVE_STRFTIME
+ if (hires) {
+ strftime(TimeBuf,sizeof(TimeBuf)-1,"%Y/%m/%d %H:%M:%S",tm);
+ slprintf(TimeBuf+strlen(TimeBuf),
+ sizeof(TimeBuf)-1 - strlen(TimeBuf),
+ ".%06ld",
+ (long)tp.tv_usec);
+ } else {
+ strftime(TimeBuf,sizeof(TimeBuf)-1,"%Y/%m/%d %H:%M:%S",tm);
+ }
+#else
+ if (hires) {
+ slprintf(TimeBuf,
+ sizeof(TimeBuf)-1,
+ "%s.%06ld",
+ asctime(tm),
+ (long)tp.tv_usec);
+ } else {
+ fstrcpy(TimeBuf, asctime(tm));
+ }
+#endif
+ }
+ return(TimeBuf);
+}
+
+/****************************************************************************
+ return the best approximation to a 'create time' under UNIX from a stat
+ structure.
+****************************************************************************/
+
+time_t get_create_time(SMB_STRUCT_STAT *st,BOOL fake_dirs)
+{
+ time_t ret, ret1;
+
+ if(S_ISDIR(st->st_mode) && fake_dirs)
+ return (time_t)315493200L; /* 1/1/1980 */
+
+ ret = MIN(st->st_ctime, st->st_mtime);
+ ret1 = MIN(ret, st->st_atime);
+
+ if(ret1 != (time_t)0)
+ return ret1;
+
+ /*
+ * One of ctime, mtime or atime was zero (probably atime).
+ * Just return MIN(ctime, mtime).
+ */
+ return ret;
+}
+
+/****************************************************************************
+initialise an NTTIME to -1, which means "unknown" or "don't expire"
+****************************************************************************/
+
+void init_nt_time(NTTIME *nt)
+{
+ nt->high = 0x7FFFFFFF;
+ nt->low = 0xFFFFFFFF;
+}
+
+/****************************************************************************
+check if NTTIME is 0
+****************************************************************************/
+BOOL nt_time_is_zero(NTTIME *nt)
+{
+ if(nt->high==0)
+ return True;
+ return False;
+}
diff --git a/source/lib/ufc.c b/source/lib/ufc.c
new file mode 100644
index 00000000000..ecc04d9e97c
--- /dev/null
+++ b/source/lib/ufc.c
@@ -0,0 +1,771 @@
+/*
+ This bit of code was derived from the UFC-crypt package which
+ carries the following copyright
+
+ Modified for use by Samba by Andrew Tridgell, October 1994
+
+ Note that this routine is only faster on some machines. Under Linux 1.1.51
+ libc 4.5.26 I actually found this routine to be slightly slower.
+
+ Under SunOS I found a huge speedup by using these routines
+ (a factor of 20 or so)
+
+ Warning: I've had a report from Steve Kennedy <steve@gbnet.org>
+ that this crypt routine may sometimes get the wrong answer. Only
+ use UFC_CRYT if you really need it.
+
+*/
+
+#include "includes.h"
+
+#ifndef HAVE_CRYPT
+
+/*
+ * UFC-crypt: ultra fast crypt(3) implementation
+ *
+ * Copyright (C) 1991-1998, Free Software Foundation, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 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 Library General Public
+ * License along with this library; if not, write to the Free
+ * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * @(#)crypt_util.c 2.31 02/08/92
+ *
+ * Support routines
+ *
+ */
+
+
+#ifndef long32
+#define long32 int32
+#endif
+
+#ifndef long64
+#define long64 int64
+#endif
+
+#ifndef ufc_long
+#define ufc_long unsigned
+#endif
+
+#ifndef _UFC_64_
+#define _UFC_32_
+#endif
+
+/*
+ * Permutation done once on the 56 bit
+ * key derived from the original 8 byte ASCII key.
+ */
+static int pc1[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
+};
+
+/*
+ * How much to rotate each 28 bit half of the pc1 permutated
+ * 56 bit key before using pc2 to give the i' key
+ */
+static int rots[16] = {
+ 1, 1, 2, 2, 2, 2, 2, 2, 1, 2, 2, 2, 2, 2, 2, 1
+};
+
+/*
+ * Permutation giving the key
+ * of the i' DES round
+ */
+static int pc2[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
+};
+
+/*
+ * The E expansion table which selects
+ * bits from the 32 bit intermediate result.
+ */
+static int esel[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 int e_inverse[64];
+
+/*
+ * Permutation done on the
+ * result of sbox lookups
+ */
+static int perm32[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
+};
+
+/*
+ * The sboxes
+ */
+static int 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 }
+ }
+};
+
+/*
+ * This is the final
+ * permutation matrix
+ */
+static int final_perm[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
+};
+
+/*
+ * The 16 DES keys in BITMASK format
+ */
+#ifdef _UFC_32_
+long32 _ufc_keytab[16][2];
+#endif
+
+#ifdef _UFC_64_
+long64 _ufc_keytab[16];
+#endif
+
+
+#define ascii_to_bin(c) ((c)>='a'?(c-59):(c)>='A'?((c)-53):(c)-'.')
+#define bin_to_ascii(c) ((c)>=38?((c)-38+'a'):(c)>=12?((c)-12+'A'):(c)+'.')
+
+/* Macro to set a bit (0..23) */
+#define BITMASK(i) ( (1<<(11-(i)%12+3)) << ((i)<12?16:0) )
+
+/*
+ * sb arrays:
+ *
+ * Workhorses of the inner loop of the DES implementation.
+ * They do sbox lookup, shifting of this value, 32 bit
+ * permutation and E permutation for the next round.
+ *
+ * Kept in 'BITMASK' format.
+ */
+
+#ifdef _UFC_32_
+long32 _ufc_sb0[8192], _ufc_sb1[8192], _ufc_sb2[8192], _ufc_sb3[8192];
+static long32 *sb[4] = {_ufc_sb0, _ufc_sb1, _ufc_sb2, _ufc_sb3};
+#endif
+
+#ifdef _UFC_64_
+long64 _ufc_sb0[4096], _ufc_sb1[4096], _ufc_sb2[4096], _ufc_sb3[4096];
+static long64 *sb[4] = {_ufc_sb0, _ufc_sb1, _ufc_sb2, _ufc_sb3};
+#endif
+
+/*
+ * eperm32tab: do 32 bit permutation and E selection
+ *
+ * The first index is the byte number in the 32 bit value to be permuted
+ * - second - is the value of this byte
+ * - third - selects the two 32 bit values
+ *
+ * The table is used and generated internally in init_des to speed it up
+ */
+static ufc_long eperm32tab[4][256][2];
+
+/*
+ * do_pc1: permform pc1 permutation in the key schedule generation.
+ *
+ * The first index is the byte number in the 8 byte ASCII key
+ * - second - - the two 28 bits halfs of the result
+ * - third - selects the 7 bits actually used of each byte
+ *
+ * The result is kept with 28 bit per 32 bit with the 4 most significant
+ * bits zero.
+ */
+static ufc_long do_pc1[8][2][128];
+
+/*
+ * do_pc2: permform pc2 permutation in the key schedule generation.
+ *
+ * The first index is the septet number in the two 28 bit intermediate values
+ * - second - - - septet values
+ *
+ * Knowledge of the structure of the pc2 permutation is used.
+ *
+ * The result is kept with 28 bit per 32 bit with the 4 most significant
+ * bits zero.
+ */
+static ufc_long do_pc2[8][128];
+
+/*
+ * efp: undo an extra e selection and do final
+ * permutation giving the DES result.
+ *
+ * Invoked 6 bit a time on two 48 bit values
+ * giving two 32 bit longs.
+ */
+static ufc_long efp[16][64][2];
+
+static unsigned char bytemask[8] = {
+ 0x80, 0x40, 0x20, 0x10, 0x08, 0x04, 0x02, 0x01
+};
+
+static ufc_long longmask[32] = {
+ 0x80000000, 0x40000000, 0x20000000, 0x10000000,
+ 0x08000000, 0x04000000, 0x02000000, 0x01000000,
+ 0x00800000, 0x00400000, 0x00200000, 0x00100000,
+ 0x00080000, 0x00040000, 0x00020000, 0x00010000,
+ 0x00008000, 0x00004000, 0x00002000, 0x00001000,
+ 0x00000800, 0x00000400, 0x00000200, 0x00000100,
+ 0x00000080, 0x00000040, 0x00000020, 0x00000010,
+ 0x00000008, 0x00000004, 0x00000002, 0x00000001
+};
+
+
+/*
+ * Silly rewrite of 'bzero'. I do so
+ * because some machines don't have
+ * bzero and some don't have memset.
+ */
+
+static void clearmem(char *start, int cnt)
+ { while(cnt--)
+ *start++ = '\0';
+ }
+
+static int initialized = 0;
+
+/* lookup a 6 bit value in sbox */
+
+#define s_lookup(i,s) sbox[(i)][(((s)>>4) & 0x2)|((s) & 0x1)][((s)>>1) & 0xf];
+
+/*
+ * Initialize unit - may be invoked directly
+ * by fcrypt users.
+ */
+
+static void ufc_init_des(void)
+ { int comes_from_bit;
+ int bit, sg;
+ ufc_long j;
+ ufc_long mask1, mask2;
+
+ /*
+ * Create the do_pc1 table used
+ * to affect pc1 permutation
+ * when generating keys
+ */
+ for(bit = 0; bit < 56; bit++) {
+ comes_from_bit = pc1[bit] - 1;
+ mask1 = bytemask[comes_from_bit % 8 + 1];
+ mask2 = longmask[bit % 28 + 4];
+ for(j = 0; j < 128; j++) {
+ if(j & mask1)
+ do_pc1[comes_from_bit / 8][bit / 28][j] |= mask2;
+ }
+ }
+
+ /*
+ * Create the do_pc2 table used
+ * to affect pc2 permutation when
+ * generating keys
+ */
+ for(bit = 0; bit < 48; bit++) {
+ comes_from_bit = pc2[bit] - 1;
+ mask1 = bytemask[comes_from_bit % 7 + 1];
+ mask2 = BITMASK(bit % 24);
+ for(j = 0; j < 128; j++) {
+ if(j & mask1)
+ do_pc2[comes_from_bit / 7][j] |= mask2;
+ }
+ }
+
+ /*
+ * Now generate the table used to do combined
+ * 32 bit permutation and e expansion
+ *
+ * We use it because we have to permute 16384 32 bit
+ * longs into 48 bit in order to initialize sb.
+ *
+ * Looping 48 rounds per permutation becomes
+ * just too slow...
+ *
+ */
+
+ clearmem((char*)eperm32tab, sizeof(eperm32tab));
+
+ for(bit = 0; bit < 48; bit++) {
+ ufc_long inner_mask1,comes_from;
+
+ comes_from = perm32[esel[bit]-1]-1;
+ inner_mask1 = bytemask[comes_from % 8];
+
+ for(j = 256; j--;) {
+ if(j & inner_mask1)
+ eperm32tab[comes_from / 8][j][bit / 24] |= BITMASK(bit % 24);
+ }
+ }
+
+ /*
+ * Create the sb tables:
+ *
+ * For each 12 bit segment of an 48 bit intermediate
+ * result, the sb table precomputes the two 4 bit
+ * values of the sbox lookups done with the two 6
+ * bit halves, shifts them to their proper place,
+ * sends them through perm32 and finally E expands
+ * them so that they are ready for the next
+ * DES round.
+ *
+ */
+ for(sg = 0; sg < 4; sg++) {
+ int j1, j2;
+ int s1, s2;
+
+ for(j1 = 0; j1 < 64; j1++) {
+ s1 = s_lookup(2 * sg, j1);
+ for(j2 = 0; j2 < 64; j2++) {
+ ufc_long to_permute, inx;
+
+ s2 = s_lookup(2 * sg + 1, j2);
+ to_permute = ((s1 << 4) | s2) << (24 - 8 * sg);
+
+#ifdef _UFC_32_
+ inx = ((j1 << 6) | j2) << 1;
+ sb[sg][inx ] = eperm32tab[0][(to_permute >> 24) & 0xff][0];
+ sb[sg][inx+1] = eperm32tab[0][(to_permute >> 24) & 0xff][1];
+ sb[sg][inx ] |= eperm32tab[1][(to_permute >> 16) & 0xff][0];
+ sb[sg][inx+1] |= eperm32tab[1][(to_permute >> 16) & 0xff][1];
+ sb[sg][inx ] |= eperm32tab[2][(to_permute >> 8) & 0xff][0];
+ sb[sg][inx+1] |= eperm32tab[2][(to_permute >> 8) & 0xff][1];
+ sb[sg][inx ] |= eperm32tab[3][(to_permute) & 0xff][0];
+ sb[sg][inx+1] |= eperm32tab[3][(to_permute) & 0xff][1];
+#endif
+#ifdef _UFC_64_
+ inx = ((j1 << 6) | j2);
+ sb[sg][inx] =
+ ((long64)eperm32tab[0][(to_permute >> 24) & 0xff][0] << 32) |
+ (long64)eperm32tab[0][(to_permute >> 24) & 0xff][1];
+ sb[sg][inx] |=
+ ((long64)eperm32tab[1][(to_permute >> 16) & 0xff][0] << 32) |
+ (long64)eperm32tab[1][(to_permute >> 16) & 0xff][1];
+ sb[sg][inx] |=
+ ((long64)eperm32tab[2][(to_permute >> 8) & 0xff][0] << 32) |
+ (long64)eperm32tab[2][(to_permute >> 8) & 0xff][1];
+ sb[sg][inx] |=
+ ((long64)eperm32tab[3][(to_permute) & 0xff][0] << 32) |
+ (long64)eperm32tab[3][(to_permute) & 0xff][1];
+#endif
+ }
+ }
+ }
+
+ /*
+ * Create an inverse matrix for esel telling
+ * where to plug out bits if undoing it
+ */
+ for(bit=48; bit--;) {
+ e_inverse[esel[bit] - 1 ] = bit;
+ e_inverse[esel[bit] - 1 + 32] = bit + 48;
+ }
+
+ /*
+ * create efp: the matrix used to
+ * undo the E expansion and effect final permutation
+ */
+ clearmem((char*)efp, sizeof efp);
+ for(bit = 0; bit < 64; bit++) {
+ int o_bit, o_long;
+ ufc_long word_value, inner_mask1, inner_mask2;
+ int comes_from_f_bit, comes_from_e_bit;
+ int comes_from_word, bit_within_word;
+
+ /* See where bit i belongs in the two 32 bit long's */
+ o_long = bit / 32; /* 0..1 */
+ o_bit = bit % 32; /* 0..31 */
+
+ /*
+ * And find a bit in the e permutated value setting this bit.
+ *
+ * Note: the e selection may have selected the same bit several
+ * times. By the initialization of e_inverse, we only look
+ * for one specific instance.
+ */
+ comes_from_f_bit = final_perm[bit] - 1; /* 0..63 */
+ comes_from_e_bit = e_inverse[comes_from_f_bit]; /* 0..95 */
+ comes_from_word = comes_from_e_bit / 6; /* 0..15 */
+ bit_within_word = comes_from_e_bit % 6; /* 0..5 */
+
+ inner_mask1 = longmask[bit_within_word + 26];
+ inner_mask2 = longmask[o_bit];
+
+ for(word_value = 64; word_value--;) {
+ if(word_value & inner_mask1)
+ efp[comes_from_word][word_value][o_long] |= inner_mask2;
+ }
+ }
+ initialized++;
+ }
+
+/*
+ * Process the elements of the sb table permuting the
+ * bits swapped in the expansion by the current salt.
+ */
+
+#ifdef _UFC_32_
+static void shuffle_sb(long32 *k, ufc_long saltbits)
+ { ufc_long j;
+ long32 x;
+ for(j=4096; j--;) {
+ x = (k[0] ^ k[1]) & (long32)saltbits;
+ *k++ ^= x;
+ *k++ ^= x;
+ }
+ }
+#endif
+
+#ifdef _UFC_64_
+static void shuffle_sb(long64 *k, ufc_long saltbits)
+ { ufc_long j;
+ long64 x;
+ for(j=4096; j--;) {
+ x = ((*k >> 32) ^ *k) & (long64)saltbits;
+ *k++ ^= (x << 32) | x;
+ }
+ }
+#endif
+
+/*
+ * Setup the unit for a new salt
+ * Hopefully we'll not see a new salt in each crypt call.
+ */
+
+static unsigned char current_salt[3] = "&&"; /* invalid value */
+static ufc_long current_saltbits = 0;
+static int direction = 0;
+
+static void setup_salt(const char *s1)
+ { ufc_long i, j, saltbits;
+ const unsigned char *s2 = (const unsigned char *)s1;
+
+ if(!initialized)
+ ufc_init_des();
+
+ if(s2[0] == current_salt[0] && s2[1] == current_salt[1])
+ return;
+ current_salt[0] = s2[0]; current_salt[1] = s2[1];
+
+ /*
+ * This is the only crypt change to DES:
+ * entries are swapped in the expansion table
+ * according to the bits set in the salt.
+ */
+ saltbits = 0;
+ for(i = 0; i < 2; i++) {
+ long c=ascii_to_bin(s2[i]);
+ if(c < 0 || c > 63)
+ c = 0;
+ for(j = 0; j < 6; j++) {
+ if((c >> j) & 0x1)
+ saltbits |= BITMASK(6 * i + j);
+ }
+ }
+
+ /*
+ * Permute the sb table values
+ * to reflect the changed e
+ * selection table
+ */
+ shuffle_sb(_ufc_sb0, current_saltbits ^ saltbits);
+ shuffle_sb(_ufc_sb1, current_saltbits ^ saltbits);
+ shuffle_sb(_ufc_sb2, current_saltbits ^ saltbits);
+ shuffle_sb(_ufc_sb3, current_saltbits ^ saltbits);
+
+ current_saltbits = saltbits;
+ }
+
+static void ufc_mk_keytab(char *key)
+ { ufc_long v1, v2, *k1;
+ int i;
+#ifdef _UFC_32_
+ long32 v, *k2 = &_ufc_keytab[0][0];
+#endif
+#ifdef _UFC_64_
+ long64 v, *k2 = &_ufc_keytab[0];
+#endif
+
+ v1 = v2 = 0; k1 = &do_pc1[0][0][0];
+ for(i = 8; i--;) {
+ v1 |= k1[*key & 0x7f]; k1 += 128;
+ v2 |= k1[*key++ & 0x7f]; k1 += 128;
+ }
+
+ for(i = 0; i < 16; i++) {
+ k1 = &do_pc2[0][0];
+
+ v1 = (v1 << rots[i]) | (v1 >> (28 - rots[i]));
+ v = k1[(v1 >> 21) & 0x7f]; k1 += 128;
+ v |= k1[(v1 >> 14) & 0x7f]; k1 += 128;
+ v |= k1[(v1 >> 7) & 0x7f]; k1 += 128;
+ v |= k1[(v1 ) & 0x7f]; k1 += 128;
+
+#ifdef _UFC_32_
+ *k2++ = v;
+ v = 0;
+#endif
+#ifdef _UFC_64_
+ v <<= 32;
+#endif
+
+ v2 = (v2 << rots[i]) | (v2 >> (28 - rots[i]));
+ v |= k1[(v2 >> 21) & 0x7f]; k1 += 128;
+ v |= k1[(v2 >> 14) & 0x7f]; k1 += 128;
+ v |= k1[(v2 >> 7) & 0x7f]; k1 += 128;
+ v |= k1[(v2 ) & 0x7f];
+
+ *k2++ = v;
+ }
+
+ direction = 0;
+ }
+
+/*
+ * Undo an extra E selection and do final permutations
+ */
+
+ufc_long *_ufc_dofinalperm(ufc_long l1, ufc_long l2, ufc_long r1, ufc_long r2)
+ { ufc_long v1, v2, x;
+ static ufc_long ary[2];
+
+ x = (l1 ^ l2) & current_saltbits; l1 ^= x; l2 ^= x;
+ x = (r1 ^ r2) & current_saltbits; r1 ^= x; r2 ^= x;
+
+ v1=v2=0; l1 >>= 3; l2 >>= 3; r1 >>= 3; r2 >>= 3;
+
+ v1 |= efp[15][ r2 & 0x3f][0]; v2 |= efp[15][ r2 & 0x3f][1];
+ v1 |= efp[14][(r2 >>= 6) & 0x3f][0]; v2 |= efp[14][ r2 & 0x3f][1];
+ v1 |= efp[13][(r2 >>= 10) & 0x3f][0]; v2 |= efp[13][ r2 & 0x3f][1];
+ v1 |= efp[12][(r2 >>= 6) & 0x3f][0]; v2 |= efp[12][ r2 & 0x3f][1];
+
+ v1 |= efp[11][ r1 & 0x3f][0]; v2 |= efp[11][ r1 & 0x3f][1];
+ v1 |= efp[10][(r1 >>= 6) & 0x3f][0]; v2 |= efp[10][ r1 & 0x3f][1];
+ v1 |= efp[ 9][(r1 >>= 10) & 0x3f][0]; v2 |= efp[ 9][ r1 & 0x3f][1];
+ v1 |= efp[ 8][(r1 >>= 6) & 0x3f][0]; v2 |= efp[ 8][ r1 & 0x3f][1];
+
+ v1 |= efp[ 7][ l2 & 0x3f][0]; v2 |= efp[ 7][ l2 & 0x3f][1];
+ v1 |= efp[ 6][(l2 >>= 6) & 0x3f][0]; v2 |= efp[ 6][ l2 & 0x3f][1];
+ v1 |= efp[ 5][(l2 >>= 10) & 0x3f][0]; v2 |= efp[ 5][ l2 & 0x3f][1];
+ v1 |= efp[ 4][(l2 >>= 6) & 0x3f][0]; v2 |= efp[ 4][ l2 & 0x3f][1];
+
+ v1 |= efp[ 3][ l1 & 0x3f][0]; v2 |= efp[ 3][ l1 & 0x3f][1];
+ v1 |= efp[ 2][(l1 >>= 6) & 0x3f][0]; v2 |= efp[ 2][ l1 & 0x3f][1];
+ v1 |= efp[ 1][(l1 >>= 10) & 0x3f][0]; v2 |= efp[ 1][ l1 & 0x3f][1];
+ v1 |= efp[ 0][(l1 >>= 6) & 0x3f][0]; v2 |= efp[ 0][ l1 & 0x3f][1];
+
+ ary[0] = v1; ary[1] = v2;
+ return ary;
+ }
+
+/*
+ * crypt only: convert from 64 bit to 11 bit ASCII
+ * prefixing with the salt
+ */
+
+static char *output_conversion(ufc_long v1, ufc_long v2, const char *salt)
+ { static char outbuf[14];
+ int i, s;
+
+ outbuf[0] = salt[0];
+ outbuf[1] = salt[1] ? salt[1] : salt[0];
+
+ for(i = 0; i < 5; i++)
+ outbuf[i + 2] = bin_to_ascii((v1 >> (26 - 6 * i)) & 0x3f);
+
+ s = (v2 & 0xf) << 2;
+ v2 = (v2 >> 2) | ((v1 & 0x3) << 30);
+
+ for(i = 5; i < 10; i++)
+ outbuf[i + 2] = bin_to_ascii((v2 >> (56 - 6 * i)) & 0x3f);
+
+ outbuf[12] = bin_to_ascii(s);
+ outbuf[13] = 0;
+
+ return outbuf;
+ }
+
+/*
+ * UNIX crypt function
+ */
+
+static ufc_long *_ufc_doit(ufc_long , ufc_long, ufc_long, ufc_long, ufc_long);
+
+char *ufc_crypt(const char *key,const char *salt)
+ { ufc_long *s;
+ char ktab[9];
+
+ /*
+ * Hack DES tables according to salt
+ */
+ setup_salt(salt);
+
+ /*
+ * Setup key schedule
+ */
+ clearmem(ktab, sizeof ktab);
+ StrnCpy(ktab, key, 8);
+ ufc_mk_keytab(ktab);
+
+ /*
+ * Go for the 25 DES encryptions
+ */
+ s = _ufc_doit((ufc_long)0, (ufc_long)0,
+ (ufc_long)0, (ufc_long)0, (ufc_long)25);
+
+ /*
+ * And convert back to 6 bit ASCII
+ */
+ return output_conversion(s[0], s[1], salt);
+ }
+
+
+#ifdef _UFC_32_
+
+/*
+ * 32 bit version
+ */
+
+extern long32 _ufc_keytab[16][2];
+extern long32 _ufc_sb0[], _ufc_sb1[], _ufc_sb2[], _ufc_sb3[];
+
+#define SBA(sb, v) (*(long32*)((char*)(sb)+(v)))
+
+static ufc_long *_ufc_doit(ufc_long l1, ufc_long l2, ufc_long r1, ufc_long r2, ufc_long itr)
+ { int i;
+ long32 s, *k;
+
+ while(itr--) {
+ k = &_ufc_keytab[0][0];
+ for(i=8; i--; ) {
+ s = *k++ ^ r1;
+ l1 ^= SBA(_ufc_sb1, s & 0xffff); l2 ^= SBA(_ufc_sb1, (s & 0xffff)+4);
+ l1 ^= SBA(_ufc_sb0, s >>= 16); l2 ^= SBA(_ufc_sb0, (s) +4);
+ s = *k++ ^ r2;
+ l1 ^= SBA(_ufc_sb3, s & 0xffff); l2 ^= SBA(_ufc_sb3, (s & 0xffff)+4);
+ l1 ^= SBA(_ufc_sb2, s >>= 16); l2 ^= SBA(_ufc_sb2, (s) +4);
+
+ s = *k++ ^ l1;
+ r1 ^= SBA(_ufc_sb1, s & 0xffff); r2 ^= SBA(_ufc_sb1, (s & 0xffff)+4);
+ r1 ^= SBA(_ufc_sb0, s >>= 16); r2 ^= SBA(_ufc_sb0, (s) +4);
+ s = *k++ ^ l2;
+ r1 ^= SBA(_ufc_sb3, s & 0xffff); r2 ^= SBA(_ufc_sb3, (s & 0xffff)+4);
+ r1 ^= SBA(_ufc_sb2, s >>= 16); r2 ^= SBA(_ufc_sb2, (s) +4);
+ }
+ s=l1; l1=r1; r1=s; s=l2; l2=r2; r2=s;
+ }
+ return _ufc_dofinalperm(l1, l2, r1, r2);
+ }
+
+#endif
+
+#ifdef _UFC_64_
+
+/*
+ * 64 bit version
+ */
+
+extern long64 _ufc_keytab[16];
+extern long64 _ufc_sb0[], _ufc_sb1[], _ufc_sb2[], _ufc_sb3[];
+
+#define SBA(sb, v) (*(long64*)((char*)(sb)+(v)))
+
+static ufc_long *_ufc_doit(ufc_long l1, ufc_long l2, ufc_long r1, ufc_long r2, ufc_long itr)
+ { int i;
+ long64 l, r, s, *k;
+
+ l = (((long64)l1) << 32) | ((long64)l2);
+ r = (((long64)r1) << 32) | ((long64)r2);
+
+ while(itr--) {
+ k = &_ufc_keytab[0];
+ for(i=8; i--; ) {
+ s = *k++ ^ r;
+ l ^= SBA(_ufc_sb3, (s >> 0) & 0xffff);
+ l ^= SBA(_ufc_sb2, (s >> 16) & 0xffff);
+ l ^= SBA(_ufc_sb1, (s >> 32) & 0xffff);
+ l ^= SBA(_ufc_sb0, (s >> 48) & 0xffff);
+
+ s = *k++ ^ l;
+ r ^= SBA(_ufc_sb3, (s >> 0) & 0xffff);
+ r ^= SBA(_ufc_sb2, (s >> 16) & 0xffff);
+ r ^= SBA(_ufc_sb1, (s >> 32) & 0xffff);
+ r ^= SBA(_ufc_sb0, (s >> 48) & 0xffff);
+ }
+ s=l; l=r; r=s;
+ }
+
+ l1 = l >> 32; l2 = l & 0xffffffff;
+ r1 = r >> 32; r2 = r & 0xffffffff;
+ return _ufc_dofinalperm(l1, l2, r1, r2);
+ }
+
+#endif
+
+
+#else
+ int ufc_dummy_procedure(void);
+ int ufc_dummy_procedure(void) {return 0;}
+#endif
diff --git a/source/lib/username.c b/source/lib/username.c
new file mode 100644
index 00000000000..ac5530b5c71
--- /dev/null
+++ b/source/lib/username.c
@@ -0,0 +1,690 @@
+/*
+ Unix SMB/CIFS implementation.
+ Username handling
+ Copyright (C) Andrew Tridgell 1992-1998
+ Copyright (C) Jeremy Allison 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 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"
+
+/* internal functions */
+static struct passwd *uname_string_combinations(char *s, struct passwd * (*fn) (const char *), int N);
+static struct passwd *uname_string_combinations2(char *s, int offset, struct passwd * (*fn) (const char *), int N);
+
+/*****************************************************************
+ Check if a user or group name is local (this is a *local* name for
+ *local* people, there's nothing for you here...).
+*****************************************************************/
+
+static BOOL name_is_local(const char *name)
+{
+ return !(strchr_m(name, *lp_winbind_separator()));
+}
+
+/*****************************************************************
+ Splits passed user or group name to domain and user/group name parts
+ Returns True if name was splitted and False otherwise.
+*****************************************************************/
+
+BOOL split_domain_and_name(const char *name, char *domain, char* username)
+{
+ char *p = strchr(name,*lp_winbind_separator());
+
+
+ /* Parse a string of the form DOMAIN/user into a domain and a user */
+ DEBUG(10,("split_domain_and_name: checking whether name |%s| local or not\n", name));
+
+ if (p) {
+ fstrcpy(username, p+1);
+ fstrcpy(domain, name);
+ domain[PTR_DIFF(p, name)] = 0;
+ } else if (lp_winbind_use_default_domain()) {
+ fstrcpy(username, name);
+ fstrcpy(domain, lp_workgroup());
+ } else {
+ return False;
+ }
+
+ DEBUG(10,("split_domain_and_name: all is fine, domain is |%s| and name is |%s|\n", domain, username));
+ return True;
+}
+
+/****************************************************************************
+ Get a users home directory.
+****************************************************************************/
+
+char *get_user_home_dir(const char *user)
+{
+ static struct passwd *pass;
+
+ /* Ensure the user exists. */
+
+ pass = Get_Pwnam(user);
+
+ if (!pass)
+ return(NULL);
+ /* Return home directory from struct passwd. */
+
+ return(pass->pw_dir);
+}
+
+/*******************************************************************
+ Map a username from a dos name to a unix name by looking in the username
+ map. Note that this modifies the name in place.
+ This is the main function that should be called *once* on
+ any incoming or new username - in order to canonicalize the name.
+ This is being done to de-couple the case conversions from the user mapping
+ function. Previously, the map_username was being called
+ every time Get_Pwnam was called.
+ Returns True if username was changed, false otherwise.
+********************************************************************/
+
+BOOL map_username(char *user)
+{
+ static BOOL initialised=False;
+ static fstring last_from,last_to;
+ XFILE *f;
+ char *mapfile = lp_username_map();
+ char *s;
+ pstring buf;
+ BOOL mapped_user = False;
+
+ if (!*user)
+ return False;
+
+ if (!*mapfile)
+ return False;
+
+ if (!initialised) {
+ *last_from = *last_to = 0;
+ initialised = True;
+ }
+
+ if (strequal(user,last_to))
+ return False;
+
+ if (strequal(user,last_from)) {
+ DEBUG(3,("Mapped user %s to %s\n",user,last_to));
+ fstrcpy(user,last_to);
+ return True;
+ }
+
+ f = x_fopen(mapfile,O_RDONLY, 0);
+ if (!f) {
+ DEBUG(0,("can't open username map %s. Error %s\n",mapfile, strerror(errno) ));
+ return False;
+ }
+
+ DEBUG(4,("Scanning username map %s\n",mapfile));
+
+ while((s=fgets_slash(buf,sizeof(buf),f))!=NULL) {
+ char *unixname = s;
+ char *dosname = strchr_m(unixname,'=');
+ char **dosuserlist;
+ BOOL return_if_mapped = False;
+
+ if (!dosname)
+ continue;
+
+ *dosname++ = 0;
+
+ while (isspace((int)*unixname))
+ unixname++;
+
+ if ('!' == *unixname) {
+ return_if_mapped = True;
+ unixname++;
+ while (*unixname && isspace((int)*unixname))
+ unixname++;
+ }
+
+ if (!*unixname || strchr_m("#;",*unixname))
+ continue;
+
+ {
+ int l = strlen(unixname);
+ while (l && isspace((int)unixname[l-1])) {
+ unixname[l-1] = 0;
+ l--;
+ }
+ }
+
+ dosuserlist = str_list_make(dosname, NULL);
+ if (!dosuserlist) {
+ DEBUG(0,("Unable to build user list\n"));
+ return False;
+ }
+
+ if (strchr_m(dosname,'*') || user_in_list(user, (const char **)dosuserlist, NULL, 0)) {
+ DEBUG(3,("Mapped user %s to %s\n",user,unixname));
+ mapped_user = True;
+ fstrcpy(last_from,user);
+ sscanf(unixname,"%s",user);
+ fstrcpy(last_to,user);
+ if(return_if_mapped) {
+ str_list_free (&dosuserlist);
+ x_fclose(f);
+ return True;
+ }
+ }
+
+ str_list_free (&dosuserlist);
+ }
+
+ x_fclose(f);
+
+ /*
+ * Setup the last_from and last_to as an optimization so
+ * that we don't scan the file again for the same user.
+ */
+ fstrcpy(last_from,user);
+ fstrcpy(last_to,user);
+
+ return mapped_user;
+}
+
+/****************************************************************************
+ * A wrapper for sys_getpwnam(). The following variations are tried:
+ * - as transmitted
+ * - in all lower case if this differs from transmitted
+ * - in all upper case if this differs from transmitted
+ * - using lp_usernamelevel() for permutations.
+****************************************************************************/
+
+static struct passwd *Get_Pwnam_ret = NULL;
+
+static struct passwd *Get_Pwnam_internals(const char *user, char *user2)
+{
+ struct passwd *ret = NULL;
+
+ if (!user2 || !(*user2))
+ return(NULL);
+
+ if (!user || !(*user))
+ return(NULL);
+
+ /* Try in all lower case first as this is the most
+ common case on UNIX systems */
+ strlower_m(user2);
+ DEBUG(5,("Trying _Get_Pwnam(), username as lowercase is %s\n",user2));
+ ret = getpwnam_alloc(user2);
+ if(ret)
+ goto done;
+
+ /* Try as given, if username wasn't originally lowercase */
+ if(strcmp(user, user2) != 0) {
+ DEBUG(5,("Trying _Get_Pwnam(), username as given is %s\n", user));
+ ret = getpwnam_alloc(user);
+ if(ret)
+ goto done;
+ }
+
+ /* Try as uppercase, if username wasn't originally uppercase */
+ strupper_m(user2);
+ if(strcmp(user, user2) != 0) {
+ DEBUG(5,("Trying _Get_Pwnam(), username as uppercase is %s\n", user2));
+ ret = getpwnam_alloc(user2);
+ if(ret)
+ goto done;
+ }
+
+ /* Try all combinations up to usernamelevel */
+ strlower_m(user2);
+ DEBUG(5,("Checking combinations of %d uppercase letters in %s\n", lp_usernamelevel(), user2));
+ ret = uname_string_combinations(user2, getpwnam_alloc, lp_usernamelevel());
+
+done:
+ DEBUG(5,("Get_Pwnam_internals %s find user [%s]!\n",ret ? "did":"didn't", user));
+
+ /* This call used to just return the 'passwd' static buffer.
+ This could then have accidental reuse implications, so
+ we now malloc a copy, and free it in the next use.
+
+ This should cause the (ab)user to segfault if it
+ uses an old struct.
+
+ This is better than useing the wrong data in security
+ critical operations.
+
+ The real fix is to make the callers free the returned
+ malloc'ed data.
+ */
+
+ if (Get_Pwnam_ret) {
+ passwd_free(&Get_Pwnam_ret);
+ }
+
+ Get_Pwnam_ret = ret;
+
+ return ret;
+}
+
+/****************************************************************************
+ Get_Pwnam wrapper without modification.
+ NOTE: This with NOT modify 'user'!
+****************************************************************************/
+
+struct passwd *Get_Pwnam(const char *user)
+{
+ fstring user2;
+ struct passwd *ret;
+
+ if ( *user == '\0' ) {
+ DEBUG(10,("Get_Pwnam: empty username!\n"));
+ return NULL;
+ }
+
+ fstrcpy(user2, user);
+
+ DEBUG(5,("Finding user %s\n", user));
+
+ ret = Get_Pwnam_internals(user, user2);
+
+ return ret;
+}
+
+/****************************************************************************
+ Check if a user is in a netgroup user list. If at first we don't succeed,
+ try lower case.
+****************************************************************************/
+
+static BOOL user_in_netgroup_list(const char *user, const char *ngname)
+{
+#ifdef HAVE_NETGROUP
+ static char *mydomain = NULL;
+ fstring lowercase_user, lowercase_ngname;
+
+ if (mydomain == NULL)
+ yp_get_default_domain(&mydomain);
+
+ if(mydomain == NULL) {
+ DEBUG(5,("Unable to get default yp domain\n"));
+ return False;
+ }
+
+ DEBUG(5,("looking for user %s of domain %s in netgroup %s\n",
+ user, mydomain, ngname));
+ DEBUG(5,("innetgr is %s\n", innetgr(ngname, NULL, user, mydomain)
+ ? "TRUE" : "FALSE"));
+
+ if (innetgr(ngname, NULL, user, mydomain))
+ return (True);
+
+ /*
+ * Ok, innetgr is case sensitive. Try once more with lowercase
+ * just in case. Attempt to fix #703. JRA.
+ */
+
+ fstrcpy(lowercase_user, user);
+ strlower_m(lowercase_user);
+ fstrcpy(lowercase_ngname, ngname);
+ strlower_m(lowercase_ngname);
+
+ if (innetgr(lowercase_ngname, NULL, lowercase_user, mydomain))
+ return (True);
+
+#endif /* HAVE_NETGROUP */
+ return False;
+}
+
+/****************************************************************************
+ Check if a user is in a winbind group.
+****************************************************************************/
+
+static BOOL user_in_winbind_group_list(const char *user, const char *gname, BOOL *winbind_answered)
+{
+ int i;
+ gid_t gid, gid_low, gid_high;
+ BOOL ret = False;
+ static gid_t *groups = NULL;
+ static int num_groups = 0;
+ static fstring last_user = "";
+
+ *winbind_answered = False;
+
+ if ((gid = nametogid(gname)) == (gid_t)-1) {
+ DEBUG(0,("user_in_winbind_group_list: nametogid for group %s failed.\n",
+ gname ));
+ goto err;
+ }
+
+ if (!lp_idmap_gid(&gid_low, &gid_high)) {
+ DEBUG(4, ("winbind gid range not configured, therefore %s cannot be a winbind group\n", gname));
+ goto err;
+ }
+
+ if (gid < gid_low || gid > gid_high) {
+ DEBUG(4, ("group %s is not a winbind group\n", gname));
+ goto err;
+ }
+
+ /* try to user the last user we looked up */
+ /* otherwise fall back to lookups */
+
+ if ( !strequal( last_user, user ) || !groups )
+ {
+ /* clear any cached information */
+
+ SAFE_FREE(groups);
+ fstrcpy( last_user, "" );
+
+ /*
+ * Get the gid's that this user belongs to.
+ */
+
+ if ((num_groups = winbind_getgroups(user, &groups)) == -1)
+ return False;
+
+ if ( num_groups == -1 )
+ return False;
+
+ if ( num_groups == 0 ) {
+ *winbind_answered = True;
+ return False;
+ }
+
+ /* save the last username */
+
+ fstrcpy( last_user, user );
+
+ }
+ else
+ DEBUG(10,("user_in_winbind_group_list: using cached user groups for [%s]\n", user));
+
+ if ( DEBUGLEVEL >= 10 ) {
+ DEBUG(10,("user_in_winbind_group_list: using groups -- "));
+ for ( i=0; i<num_groups; i++ )
+ DEBUGADD(10,("%lu ", (unsigned long)groups[i]));
+ DEBUGADD(10,("\n"));
+ }
+
+ /*
+ * Now we have the gid list for this user - convert the gname
+ * to a gid_t via either winbind or the local UNIX lookup and do the comparison.
+ */
+
+ for (i = 0; i < num_groups; i++) {
+ if (gid == groups[i]) {
+ ret = True;
+ break;
+ }
+ }
+
+ *winbind_answered = True;
+ SAFE_FREE(groups);
+ return ret;
+
+ err:
+
+ *winbind_answered = False;
+ SAFE_FREE(groups);
+ return False;
+}
+
+/****************************************************************************
+ Check if a user is in a UNIX group.
+****************************************************************************/
+
+BOOL user_in_unix_group_list(const char *user,const char *gname)
+{
+ struct passwd *pass = Get_Pwnam(user);
+ struct sys_userlist *user_list;
+ struct sys_userlist *member;
+
+ DEBUG(10,("user_in_unix_group_list: checking user %s in group %s\n", user, gname));
+
+ /*
+ * We need to check the users primary group as this
+ * group is implicit and often not listed in the group database.
+ */
+
+ if (pass) {
+ if (strequal(gname,gidtoname(pass->pw_gid))) {
+ DEBUG(10,("user_in_unix_group_list: group %s is primary group.\n", gname ));
+ return True;
+ }
+ }
+
+ user_list = get_users_in_group(gname);
+ if (user_list == NULL) {
+ DEBUG(10,("user_in_unix_group_list: no such group %s\n", gname ));
+ return False;
+ }
+
+ for (member = user_list; member; member = member->next) {
+ DEBUG(10,("user_in_unix_group_list: checking user %s against member %s\n",
+ user, member->unix_name ));
+ if (strequal(member->unix_name,user)) {
+ free_userlist(user_list);
+ return(True);
+ }
+ }
+
+ free_userlist(user_list);
+ return False;
+}
+
+/****************************************************************************
+ Check if a user is in a group list. Ask winbind first, then use UNIX.
+****************************************************************************/
+
+BOOL user_in_group_list(const char *user, const char *gname, gid_t *groups, size_t n_groups)
+{
+ BOOL winbind_answered = False;
+ BOOL ret;
+ gid_t gid;
+ unsigned i;
+
+ gid = nametogid(gname);
+ if (gid == (gid_t)-1)
+ return False;
+
+ if (groups && n_groups > 0) {
+ for (i=0; i < n_groups; i++) {
+ if (groups[i] == gid) {
+ return True;
+ }
+ }
+ return False;
+ }
+
+ /* fallback if we don't yet have the group list */
+
+ ret = user_in_winbind_group_list(user, gname, &winbind_answered);
+ if (!winbind_answered)
+ ret = user_in_unix_group_list(user, gname);
+
+ if (ret)
+ DEBUG(10,("user_in_group_list: user |%s| is in group |%s|\n", user, gname));
+ return ret;
+}
+
+/****************************************************************************
+ Check if a user is in a user list - can check combinations of UNIX
+ and netgroup lists.
+****************************************************************************/
+
+BOOL user_in_list(const char *user,const char **list, gid_t *groups, size_t n_groups)
+{
+ if (!list || !*list)
+ return False;
+
+ DEBUG(10,("user_in_list: checking user %s in list\n", user));
+
+ while (*list) {
+
+ DEBUG(10,("user_in_list: checking user |%s| against |%s|\n", user, *list));
+
+ /*
+ * Check raw username.
+ */
+ if (strequal(user, *list))
+ return(True);
+
+ /*
+ * Now check to see if any combination
+ * of UNIX and netgroups has been specified.
+ */
+
+ if(**list == '@') {
+ /*
+ * Old behaviour. Check netgroup list
+ * followed by UNIX list.
+ */
+ if(user_in_netgroup_list(user, *list +1))
+ return True;
+ if(user_in_group_list(user, *list +1, groups, n_groups))
+ return True;
+ } else if (**list == '+') {
+
+ if((*(*list +1)) == '&') {
+ /*
+ * Search UNIX list followed by netgroup.
+ */
+ if(user_in_group_list(user, *list +2, groups, n_groups))
+ return True;
+ if(user_in_netgroup_list(user, *list +2))
+ return True;
+
+ } else {
+
+ /*
+ * Just search UNIX list.
+ */
+
+ if(user_in_group_list(user, *list +1, groups, n_groups))
+ return True;
+ }
+
+ } else if (**list == '&') {
+
+ if(*(*list +1) == '+') {
+ /*
+ * Search netgroup list followed by UNIX list.
+ */
+ if(user_in_netgroup_list(user, *list +2))
+ return True;
+ if(user_in_group_list(user, *list +2, groups, n_groups))
+ return True;
+ } else {
+ /*
+ * Just search netgroup list.
+ */
+ if(user_in_netgroup_list(user, *list +1))
+ return True;
+ }
+ } else if (!name_is_local(*list)) {
+ /*
+ * If user name did not match and token is not
+ * a unix group and the token has a winbind separator in the
+ * name then see if it is a Windows group.
+ */
+
+ DOM_SID g_sid;
+ enum SID_NAME_USE name_type;
+ BOOL winbind_answered = False;
+ BOOL ret;
+ fstring groupname, domain;
+
+ /* Parse a string of the form DOMAIN/user into a domain and a user */
+
+ char *p = strchr(*list,*lp_winbind_separator());
+
+ DEBUG(10,("user_in_list: checking if user |%s| is in winbind group |%s|\n", user, *list));
+
+ if (p) {
+ fstrcpy(groupname, p+1);
+ fstrcpy(domain, *list);
+ domain[PTR_DIFF(p, *list)] = 0;
+
+ /* Check to see if name is a Windows group; Win2k native mode DCs
+ will return domain local groups; while NT4 or mixed mode 2k DCs
+ will not */
+
+ if ( winbind_lookup_name(domain, groupname, &g_sid, &name_type)
+ && ( name_type==SID_NAME_DOM_GRP ||
+ (strequal(lp_workgroup(), domain) && name_type==SID_NAME_ALIAS) ) )
+ {
+
+ /* Check if user name is in the Windows group */
+ ret = user_in_winbind_group_list(user, *list, &winbind_answered);
+
+ if (winbind_answered && ret == True) {
+ DEBUG(10,("user_in_list: user |%s| is in winbind group |%s|\n", user, *list));
+ return ret;
+ }
+ }
+ }
+ }
+
+ list++;
+ }
+ return(False);
+}
+
+/* The functions below have been taken from password.c and slightly modified */
+/****************************************************************************
+ Apply a function to upper/lower case combinations
+ of a string and return true if one of them returns true.
+ Try all combinations with N uppercase letters.
+ offset is the first char to try and change (start with 0)
+ it assumes the string starts lowercased
+****************************************************************************/
+
+static struct passwd *uname_string_combinations2(char *s,int offset,struct passwd *(*fn)(const char *),int N)
+{
+ ssize_t len = (ssize_t)strlen(s);
+ int i;
+ struct passwd *ret;
+
+ if (N <= 0 || offset >= len)
+ return(fn(s));
+
+ for (i=offset;i<(len-(N-1));i++) {
+ char c = s[i];
+ if (!islower((int)c))
+ continue;
+ s[i] = toupper(c);
+ ret = uname_string_combinations2(s,i+1,fn,N-1);
+ if(ret)
+ return(ret);
+ s[i] = c;
+ }
+ return(NULL);
+}
+
+/****************************************************************************
+ Apply a function to upper/lower case combinations
+ of a string and return true if one of them returns true.
+ Try all combinations with up to N uppercase letters.
+ offset is the first char to try and change (start with 0)
+ it assumes the string starts lowercased
+****************************************************************************/
+
+static struct passwd * uname_string_combinations(char *s,struct passwd * (*fn)(const char *),int N)
+{
+ int n;
+ struct passwd *ret;
+
+ for (n=1;n<=N;n++) {
+ ret = uname_string_combinations2(s,0,fn,n);
+ if(ret)
+ return(ret);
+ }
+ return(NULL);
+}
+
diff --git a/source/lib/util.c b/source/lib/util.c
new file mode 100644
index 00000000000..3f57048a00b
--- /dev/null
+++ b/source/lib/util.c
@@ -0,0 +1,2501 @@
+/*
+ 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
+
+ This program is free software; you can 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"
+
+#if (defined(HAVE_NETGROUP) && defined (WITH_AUTOMOUNT))
+#ifdef WITH_NISPLUS_HOME
+#ifdef BROKEN_NISPLUS_INCLUDE_FILES
+/*
+ * The following lines are needed due to buggy include files
+ * in Solaris 2.6 which define GROUP in both /usr/include/sys/acl.h and
+ * also in /usr/include/rpcsvc/nis.h. The definitions conflict. JRA.
+ * Also GROUP_OBJ is defined as 0x4 in /usr/include/sys/acl.h and as
+ * an enum in /usr/include/rpcsvc/nis.h.
+ */
+
+#if defined(GROUP)
+#undef GROUP
+#endif
+
+#if defined(GROUP_OBJ)
+#undef GROUP_OBJ
+#endif
+
+#endif /* BROKEN_NISPLUS_INCLUDE_FILES */
+
+#include <rpcsvc/nis.h>
+
+#else /* !WITH_NISPLUS_HOME */
+
+#include "rpcsvc/ypclnt.h"
+
+#endif /* WITH_NISPLUS_HOME */
+#endif /* HAVE_NETGROUP && WITH_AUTOMOUNT */
+
+int Protocol = PROTOCOL_COREPLUS;
+
+/* a default finfo structure to ensure all fields are sensible */
+file_info def_finfo = {-1,0,0,0,0,0,0,"",""};
+
+/* this is used by the chaining code */
+int chain_size = 0;
+
+int trans_num = 0;
+
+/*
+ case handling on filenames
+*/
+int case_default = CASE_LOWER;
+
+/* the following control case operations - they are put here so the
+ client can link easily */
+BOOL case_sensitive;
+BOOL case_preserve;
+BOOL use_mangled_map = False;
+BOOL short_case_preserve;
+BOOL case_mangle;
+
+static enum remote_arch_types ra_type = RA_UNKNOWN;
+pstring user_socket_options=DEFAULT_SOCKET_OPTIONS;
+
+/***********************************************************************
+ Definitions for all names.
+***********************************************************************/
+
+static char *smb_myname;
+static char *smb_myworkgroup;
+static char *smb_scope;
+static int smb_num_netbios_names;
+static char **smb_my_netbios_names;
+
+/***********************************************************************
+ Allocate and set myname. Ensure upper case.
+***********************************************************************/
+
+BOOL set_global_myname(const char *myname)
+{
+ SAFE_FREE(smb_myname);
+ smb_myname = strdup(myname);
+ if (!smb_myname)
+ return False;
+ strupper_m(smb_myname);
+ return True;
+}
+
+const char *global_myname(void)
+{
+ return smb_myname;
+}
+
+/***********************************************************************
+ Allocate and set myworkgroup. Ensure upper case.
+***********************************************************************/
+
+BOOL set_global_myworkgroup(const char *myworkgroup)
+{
+ SAFE_FREE(smb_myworkgroup);
+ smb_myworkgroup = strdup(myworkgroup);
+ if (!smb_myworkgroup)
+ return False;
+ strupper_m(smb_myworkgroup);
+ return True;
+}
+
+const char *lp_workgroup(void)
+{
+ return smb_myworkgroup;
+}
+
+/***********************************************************************
+ Allocate and set scope. Ensure upper case.
+***********************************************************************/
+
+BOOL set_global_scope(const char *scope)
+{
+ SAFE_FREE(smb_scope);
+ smb_scope = strdup(scope);
+ if (!smb_scope)
+ return False;
+ strupper_m(smb_scope);
+ return True;
+}
+
+/*********************************************************************
+ Ensure scope is never null string.
+*********************************************************************/
+
+const char *global_scope(void)
+{
+ if (!smb_scope)
+ set_global_scope("");
+ return smb_scope;
+}
+
+static void free_netbios_names_array(void)
+{
+ int i;
+
+ for (i = 0; i < smb_num_netbios_names; i++)
+ SAFE_FREE(smb_my_netbios_names[i]);
+
+ SAFE_FREE(smb_my_netbios_names);
+ smb_num_netbios_names = 0;
+}
+
+static BOOL allocate_my_netbios_names_array(size_t number)
+{
+ free_netbios_names_array();
+
+ smb_num_netbios_names = number + 1;
+ smb_my_netbios_names = (char **)malloc( sizeof(char *) * smb_num_netbios_names );
+
+ if (!smb_my_netbios_names)
+ return False;
+
+ memset(smb_my_netbios_names, '\0', sizeof(char *) * smb_num_netbios_names);
+ return True;
+}
+
+static BOOL set_my_netbios_names(const char *name, int i)
+{
+ SAFE_FREE(smb_my_netbios_names[i]);
+
+ smb_my_netbios_names[i] = strdup(name);
+ if (!smb_my_netbios_names[i])
+ return False;
+ strupper_m(smb_my_netbios_names[i]);
+ return True;
+}
+
+const char *my_netbios_names(int i)
+{
+ return smb_my_netbios_names[i];
+}
+
+BOOL set_netbios_aliases(const char **str_array)
+{
+ size_t namecount;
+
+ /* Work out the max number of netbios aliases that we have */
+ for( namecount=0; str_array && (str_array[namecount] != NULL); namecount++ )
+ ;
+
+ if ( global_myname() && *global_myname())
+ namecount++;
+
+ /* Allocate space for the netbios aliases */
+ if (!allocate_my_netbios_names_array(namecount))
+ return False;
+
+ /* Use the global_myname string first */
+ namecount=0;
+ if ( global_myname() && *global_myname()) {
+ set_my_netbios_names( global_myname(), namecount );
+ namecount++;
+ }
+
+ if (str_array) {
+ size_t i;
+ for ( i = 0; str_array[i] != NULL; i++) {
+ size_t n;
+ BOOL duplicate = False;
+
+ /* Look for duplicates */
+ for( n=0; n<namecount; n++ ) {
+ if( strequal( str_array[i], my_netbios_names(n) ) ) {
+ duplicate = True;
+ break;
+ }
+ }
+ if (!duplicate) {
+ if (!set_my_netbios_names(str_array[i], namecount))
+ return False;
+ namecount++;
+ }
+ }
+ }
+ return True;
+}
+
+/****************************************************************************
+ Common name initialization code.
+****************************************************************************/
+
+BOOL init_names(void)
+{
+ extern fstring local_machine;
+ char *p;
+ int n;
+
+ if (global_myname() == NULL || *global_myname() == '\0') {
+ if (!set_global_myname(myhostname())) {
+ DEBUG( 0, ( "init_structs: malloc fail.\n" ) );
+ return False;
+ }
+ }
+
+ if (!set_netbios_aliases(lp_netbios_aliases())) {
+ DEBUG( 0, ( "init_structs: malloc fail.\n" ) );
+ return False;
+ }
+
+ fstrcpy( local_machine, global_myname() );
+ trim_char( local_machine, ' ', ' ' );
+ p = strchr( local_machine, ' ' );
+ if (p)
+ *p = 0;
+ strlower_m( local_machine );
+
+ DEBUG( 5, ("Netbios name list:-\n") );
+ for( n=0; my_netbios_names(n); n++ )
+ DEBUGADD( 5, ( "my_netbios_names[%d]=\"%s\"\n", n, my_netbios_names(n) ) );
+
+ return( True );
+}
+
+/**************************************************************************n
+ Find a suitable temporary directory. The result should be copied immediately
+ as it may be overwritten by a subsequent call.
+****************************************************************************/
+
+const char *tmpdir(void)
+{
+ char *p;
+ if ((p = getenv("TMPDIR")))
+ return p;
+ return "/tmp";
+}
+
+/****************************************************************************
+ Determine whether we are in the specified group.
+****************************************************************************/
+
+BOOL in_group(gid_t group, gid_t current_gid, int ngroups, const gid_t *groups)
+{
+ int i;
+
+ if (group == current_gid)
+ return(True);
+
+ for (i=0;i<ngroups;i++)
+ if (group == groups[i])
+ return(True);
+
+ return(False);
+}
+
+/****************************************************************************
+ Like atoi but gets the value up to the separator character.
+****************************************************************************/
+
+static const char *Atoic(const char *p, int *n, const char *c)
+{
+ if (!isdigit((int)*p)) {
+ DEBUG(5, ("Atoic: malformed number\n"));
+ return NULL;
+ }
+
+ (*n) = atoi(p);
+
+ while ((*p) && isdigit((int)*p))
+ p++;
+
+ if (strchr_m(c, *p) == NULL) {
+ DEBUG(5, ("Atoic: no separator characters (%s) not found\n", c));
+ return NULL;
+ }
+
+ return p;
+}
+
+/*************************************************************************
+ Reads a list of numbers.
+ *************************************************************************/
+
+const char *get_numlist(const char *p, uint32 **num, int *count)
+{
+ int val;
+
+ if (num == NULL || count == NULL)
+ return NULL;
+
+ (*count) = 0;
+ (*num ) = NULL;
+
+ while ((p = Atoic(p, &val, ":,")) != NULL && (*p) != ':') {
+ uint32 *tn;
+
+ tn = Realloc((*num), ((*count)+1) * sizeof(uint32));
+ if (tn == NULL) {
+ SAFE_FREE(*num);
+ return NULL;
+ } else
+ (*num) = tn;
+ (*num)[(*count)] = val;
+ (*count)++;
+ p++;
+ }
+
+ return p;
+}
+
+/*******************************************************************
+ Check if a file exists - call vfs_file_exist for samba files.
+********************************************************************/
+
+BOOL file_exist(const char *fname,SMB_STRUCT_STAT *sbuf)
+{
+ SMB_STRUCT_STAT st;
+ if (!sbuf)
+ sbuf = &st;
+
+ if (sys_stat(fname,sbuf) != 0)
+ return(False);
+
+ return((S_ISREG(sbuf->st_mode)) || (S_ISFIFO(sbuf->st_mode)));
+}
+
+/*******************************************************************
+ Check a files mod time.
+********************************************************************/
+
+time_t file_modtime(const char *fname)
+{
+ SMB_STRUCT_STAT st;
+
+ if (sys_stat(fname,&st) != 0)
+ return(0);
+
+ return(st.st_mtime);
+}
+
+/*******************************************************************
+ Check if a directory exists.
+********************************************************************/
+
+BOOL directory_exist(char *dname,SMB_STRUCT_STAT *st)
+{
+ SMB_STRUCT_STAT st2;
+ BOOL ret;
+
+ if (!st)
+ st = &st2;
+
+ if (sys_stat(dname,st) != 0)
+ return(False);
+
+ ret = S_ISDIR(st->st_mode);
+ if(!ret)
+ errno = ENOTDIR;
+ return ret;
+}
+
+/*******************************************************************
+ Returns the size in bytes of the named file.
+********************************************************************/
+
+SMB_OFF_T get_file_size(char *file_name)
+{
+ SMB_STRUCT_STAT buf;
+ buf.st_size = 0;
+ if(sys_stat(file_name,&buf) != 0)
+ return (SMB_OFF_T)-1;
+ return(buf.st_size);
+}
+
+/*******************************************************************
+ Return a string representing an attribute for a file.
+********************************************************************/
+
+char *attrib_string(uint16 mode)
+{
+ static fstring attrstr;
+
+ attrstr[0] = 0;
+
+ if (mode & aVOLID) fstrcat(attrstr,"V");
+ if (mode & aDIR) fstrcat(attrstr,"D");
+ if (mode & aARCH) fstrcat(attrstr,"A");
+ if (mode & aHIDDEN) fstrcat(attrstr,"H");
+ if (mode & aSYSTEM) fstrcat(attrstr,"S");
+ if (mode & aRONLY) fstrcat(attrstr,"R");
+
+ return(attrstr);
+}
+
+/*******************************************************************
+ Show a smb message structure.
+********************************************************************/
+
+void show_msg(char *buf)
+{
+ int i;
+ int bcc=0;
+
+ if (!DEBUGLVL(5))
+ return;
+
+ DEBUG(5,("size=%d\nsmb_com=0x%x\nsmb_rcls=%d\nsmb_reh=%d\nsmb_err=%d\nsmb_flg=%d\nsmb_flg2=%d\n",
+ smb_len(buf),
+ (int)CVAL(buf,smb_com),
+ (int)CVAL(buf,smb_rcls),
+ (int)CVAL(buf,smb_reh),
+ (int)SVAL(buf,smb_err),
+ (int)CVAL(buf,smb_flg),
+ (int)SVAL(buf,smb_flg2)));
+ DEBUGADD(5,("smb_tid=%d\nsmb_pid=%d\nsmb_uid=%d\nsmb_mid=%d\n",
+ (int)SVAL(buf,smb_tid),
+ (int)SVAL(buf,smb_pid),
+ (int)SVAL(buf,smb_uid),
+ (int)SVAL(buf,smb_mid)));
+ DEBUGADD(5,("smt_wct=%d\n",(int)CVAL(buf,smb_wct)));
+
+ for (i=0;i<(int)CVAL(buf,smb_wct);i++)
+ DEBUGADD(5,("smb_vwv[%2d]=%5d (0x%X)\n",i,
+ SVAL(buf,smb_vwv+2*i),SVAL(buf,smb_vwv+2*i)));
+
+ bcc = (int)SVAL(buf,smb_vwv+2*(CVAL(buf,smb_wct)));
+
+ DEBUGADD(5,("smb_bcc=%d\n",bcc));
+
+ if (DEBUGLEVEL < 10)
+ return;
+
+ if (DEBUGLEVEL < 50)
+ bcc = MIN(bcc, 512);
+
+ dump_data(10, smb_buf(buf), bcc);
+}
+
+/*******************************************************************
+ Set the length and marker of an smb packet.
+********************************************************************/
+
+void smb_setlen(char *buf,int len)
+{
+ _smb_setlen(buf,len);
+
+ SCVAL(buf,4,0xFF);
+ SCVAL(buf,5,'S');
+ SCVAL(buf,6,'M');
+ SCVAL(buf,7,'B');
+}
+
+/*******************************************************************
+ Setup the word count and byte count for a smb message.
+********************************************************************/
+
+int set_message(char *buf,int num_words,int num_bytes,BOOL zero)
+{
+ if (zero)
+ memset(buf + smb_size,'\0',num_words*2 + num_bytes);
+ SCVAL(buf,smb_wct,num_words);
+ SSVAL(buf,smb_vwv + num_words*SIZEOFWORD,num_bytes);
+ smb_setlen(buf,smb_size + num_words*2 + num_bytes - 4);
+ return (smb_size + num_words*2 + num_bytes);
+}
+
+/*******************************************************************
+ Setup only the byte count for a smb message.
+********************************************************************/
+
+int set_message_bcc(char *buf,int num_bytes)
+{
+ int num_words = CVAL(buf,smb_wct);
+ SSVAL(buf,smb_vwv + num_words*SIZEOFWORD,num_bytes);
+ smb_setlen(buf,smb_size + num_words*2 + num_bytes - 4);
+ return (smb_size + num_words*2 + num_bytes);
+}
+
+/*******************************************************************
+ Setup only the byte count for a smb message, using the end of the
+ message as a marker.
+********************************************************************/
+
+int set_message_end(void *outbuf,void *end_ptr)
+{
+ return set_message_bcc((char *)outbuf,PTR_DIFF(end_ptr,smb_buf((char *)outbuf)));
+}
+
+/*******************************************************************
+ Reduce a file name, removing .. elements.
+********************************************************************/
+
+void dos_clean_name(char *s)
+{
+ char *p=NULL;
+
+ DEBUG(3,("dos_clean_name [%s]\n",s));
+
+ /* remove any double slashes */
+ all_string_sub(s, "\\\\", "\\", 0);
+
+ while ((p = strstr_m(s,"\\..\\")) != NULL) {
+ pstring s1;
+
+ *p = 0;
+ pstrcpy(s1,p+3);
+
+ if ((p=strrchr_m(s,'\\')) != NULL)
+ *p = 0;
+ else
+ *s = 0;
+ pstrcat(s,s1);
+ }
+
+ trim_string(s,NULL,"\\..");
+
+ all_string_sub(s, "\\.\\", "\\", 0);
+}
+
+/*******************************************************************
+ Reduce a file name, removing .. elements.
+********************************************************************/
+
+void unix_clean_name(char *s)
+{
+ char *p=NULL;
+
+ DEBUG(3,("unix_clean_name [%s]\n",s));
+
+ /* remove any double slashes */
+ all_string_sub(s, "//","/", 0);
+
+ /* Remove leading ./ characters */
+ if(strncmp(s, "./", 2) == 0) {
+ trim_string(s, "./", NULL);
+ if(*s == 0)
+ pstrcpy(s,"./");
+ }
+
+ while ((p = strstr_m(s,"/../")) != NULL) {
+ pstring s1;
+
+ *p = 0;
+ pstrcpy(s1,p+3);
+
+ if ((p=strrchr_m(s,'/')) != NULL)
+ *p = 0;
+ else
+ *s = 0;
+ pstrcat(s,s1);
+ }
+
+ trim_string(s,NULL,"/..");
+}
+
+/****************************************************************************
+ Make a dir struct.
+****************************************************************************/
+
+void make_dir_struct(char *buf, const char *mask, const char *fname,SMB_OFF_T size,int mode,time_t date)
+{
+ char *p;
+ pstring mask2;
+
+ pstrcpy(mask2,mask);
+
+ if ((mode & aDIR) != 0)
+ size = 0;
+
+ memset(buf+1,' ',11);
+ if ((p = strchr_m(mask2,'.')) != NULL) {
+ *p = 0;
+ push_ascii(buf+1,mask2,8, 0);
+ push_ascii(buf+9,p+1,3, 0);
+ *p = '.';
+ } else
+ push_ascii(buf+1,mask2,11, 0);
+
+ memset(buf+21,'\0',DIR_STRUCT_SIZE-21);
+ SCVAL(buf,21,mode);
+ put_dos_date(buf,22,date);
+ SSVAL(buf,26,size & 0xFFFF);
+ SSVAL(buf,28,(size >> 16)&0xFFFF);
+ push_ascii(buf+30,fname,12, case_sensitive ? 0 : STR_UPPER);
+ DEBUG(8,("put name [%s] from [%s] into dir struct\n",buf+30, fname));
+}
+
+/*******************************************************************
+ Close the low 3 fd's and open dev/null in their place.
+********************************************************************/
+
+void close_low_fds(BOOL stderr_too)
+{
+#ifndef VALGRIND
+ int fd;
+ int i;
+
+ close(0);
+ close(1);
+
+ if (stderr_too)
+ close(2);
+
+ /* try and use up these file descriptors, so silly
+ library routines writing to stdout etc won't cause havoc */
+ for (i=0;i<3;i++) {
+ if (i == 2 && !stderr_too)
+ continue;
+
+ fd = sys_open("/dev/null",O_RDWR,0);
+ if (fd < 0)
+ fd = sys_open("/dev/null",O_WRONLY,0);
+ if (fd < 0) {
+ DEBUG(0,("Can't open /dev/null\n"));
+ return;
+ }
+ if (fd != i) {
+ DEBUG(0,("Didn't get file descriptor %d\n",i));
+ return;
+ }
+ }
+#endif
+}
+
+/****************************************************************************
+ Set a fd into blocking/nonblocking mode. Uses POSIX O_NONBLOCK if available,
+ else
+ if SYSV use O_NDELAY
+ if BSD use FNDELAY
+****************************************************************************/
+
+int set_blocking(int fd, BOOL set)
+{
+ int val;
+#ifdef O_NONBLOCK
+#define FLAG_TO_SET O_NONBLOCK
+#else
+#ifdef SYSV
+#define FLAG_TO_SET O_NDELAY
+#else /* BSD */
+#define FLAG_TO_SET FNDELAY
+#endif
+#endif
+
+ if((val = sys_fcntl_long(fd, F_GETFL, 0)) == -1)
+ return -1;
+ if(set) /* Turn blocking on - ie. clear nonblock flag */
+ val &= ~FLAG_TO_SET;
+ else
+ val |= FLAG_TO_SET;
+ return sys_fcntl_long( fd, F_SETFL, val);
+#undef FLAG_TO_SET
+}
+
+/****************************************************************************
+ Transfer some data between two fd's.
+****************************************************************************/
+
+#ifndef TRANSFER_BUF_SIZE
+#define TRANSFER_BUF_SIZE 65536
+#endif
+
+ssize_t transfer_file_internal(int infd, int outfd, size_t n, ssize_t (*read_fn)(int, void *, size_t),
+ ssize_t (*write_fn)(int, const void *, size_t))
+{
+ char *buf;
+ size_t total = 0;
+ ssize_t read_ret;
+ ssize_t write_ret;
+ size_t num_to_read_thistime;
+ size_t num_written = 0;
+
+ if ((buf = malloc(TRANSFER_BUF_SIZE)) == NULL)
+ return -1;
+
+ while (total < n) {
+ num_to_read_thistime = MIN((n - total), TRANSFER_BUF_SIZE);
+
+ read_ret = (*read_fn)(infd, buf, num_to_read_thistime);
+ if (read_ret == -1) {
+ DEBUG(0,("transfer_file_internal: read failure. Error = %s\n", strerror(errno) ));
+ SAFE_FREE(buf);
+ return -1;
+ }
+ if (read_ret == 0)
+ break;
+
+ num_written = 0;
+
+ while (num_written < read_ret) {
+ write_ret = (*write_fn)(outfd,buf + num_written, read_ret - num_written);
+
+ if (write_ret == -1) {
+ DEBUG(0,("transfer_file_internal: write failure. Error = %s\n", strerror(errno) ));
+ SAFE_FREE(buf);
+ return -1;
+ }
+ if (write_ret == 0)
+ return (ssize_t)total;
+
+ num_written += (size_t)write_ret;
+ }
+
+ total += (size_t)read_ret;
+ }
+
+ SAFE_FREE(buf);
+ return (ssize_t)total;
+}
+
+SMB_OFF_T transfer_file(int infd,int outfd,SMB_OFF_T n)
+{
+ return (SMB_OFF_T)transfer_file_internal(infd, outfd, (size_t)n, sys_read, sys_write);
+}
+
+/*******************************************************************
+ Sleep for a specified number of milliseconds.
+********************************************************************/
+
+void smb_msleep(unsigned int t)
+{
+ unsigned int tdiff=0;
+ struct timeval tval,t1,t2;
+ fd_set fds;
+
+ GetTimeOfDay(&t1);
+ GetTimeOfDay(&t2);
+
+ while (tdiff < t) {
+ tval.tv_sec = (t-tdiff)/1000;
+ tval.tv_usec = 1000*((t-tdiff)%1000);
+
+ /* Never wait for more than 1 sec. */
+ if (tval.tv_sec > 1) {
+ tval.tv_sec = 1;
+ tval.tv_usec = 0;
+ }
+
+ FD_ZERO(&fds);
+ errno = 0;
+ sys_select_intr(0,&fds,NULL,NULL,&tval);
+
+ GetTimeOfDay(&t2);
+ if (t2.tv_sec < t1.tv_sec) {
+ /* Someone adjusted time... */
+ t1 = t2;
+ }
+
+ tdiff = TvalDiff(&t1,&t2);
+ }
+}
+
+/****************************************************************************
+ Become a daemon, discarding the controlling terminal.
+****************************************************************************/
+
+void become_daemon(BOOL Fork)
+{
+ if (Fork) {
+ if (sys_fork()) {
+ _exit(0);
+ }
+ }
+
+ /* detach from the terminal */
+#ifdef HAVE_SETSID
+ setsid();
+#elif defined(TIOCNOTTY)
+ {
+ int i = sys_open("/dev/tty", O_RDWR, 0);
+ if (i != -1) {
+ ioctl(i, (int) TIOCNOTTY, (char *)0);
+ close(i);
+ }
+ }
+#endif /* HAVE_SETSID */
+
+ /* Close fd's 0,1,2. Needed if started by rsh */
+ close_low_fds(False); /* Don't close stderr, let the debug system
+ attach it to the logfile */
+}
+
+/****************************************************************************
+ Put up a yes/no prompt.
+****************************************************************************/
+
+BOOL yesno(char *p)
+{
+ pstring ans;
+ printf("%s",p);
+
+ if (!fgets(ans,sizeof(ans)-1,stdin))
+ return(False);
+
+ if (*ans == 'y' || *ans == 'Y')
+ return(True);
+
+ return(False);
+}
+
+/****************************************************************************
+ Expand a pointer to be a particular size.
+****************************************************************************/
+
+void *Realloc(void *p,size_t size)
+{
+ void *ret=NULL;
+
+ if (size == 0) {
+ SAFE_FREE(p);
+ DEBUG(5,("Realloc asked for 0 bytes\n"));
+ return NULL;
+ }
+
+ if (!p)
+ ret = (void *)malloc(size);
+ else
+ ret = (void *)realloc(p,size);
+
+ if (!ret)
+ DEBUG(0,("Memory allocation error: failed to expand to %d bytes\n",(int)size));
+
+ return(ret);
+}
+
+void *Realloc_zero(void *ptr, size_t size)
+{
+ void *tptr = NULL;
+
+ tptr = Realloc(ptr, size);
+ if(tptr == NULL)
+ return NULL;
+
+ memset((char *)tptr,'\0',size);
+
+ return tptr;
+}
+
+/****************************************************************************
+ Free memory, checks for NULL.
+ Use directly SAFE_FREE()
+ Exists only because we need to pass a function pointer somewhere --SSS
+****************************************************************************/
+
+void safe_free(void *p)
+{
+ SAFE_FREE(p);
+}
+
+/****************************************************************************
+ Get my own name and IP.
+****************************************************************************/
+
+BOOL get_myname(char *my_name)
+{
+ pstring hostname;
+
+ *hostname = 0;
+
+ /* get my host name */
+ if (gethostname(hostname, sizeof(hostname)) == -1) {
+ DEBUG(0,("gethostname failed\n"));
+ return False;
+ }
+
+ /* Ensure null termination. */
+ hostname[sizeof(hostname)-1] = '\0';
+
+ if (my_name) {
+ /* split off any parts after an initial . */
+ char *p = strchr_m(hostname,'.');
+
+ if (p)
+ *p = 0;
+
+ fstrcpy(my_name,hostname);
+ }
+
+ return(True);
+}
+
+/****************************************************************************
+ Get my own canonical name, including domain.
+****************************************************************************/
+
+BOOL get_mydnsfullname(fstring my_dnsname)
+{
+ static fstring dnshostname;
+ struct hostent *hp;
+
+ if (!*dnshostname) {
+ /* get my host name */
+ if (gethostname(dnshostname, sizeof(dnshostname)) == -1) {
+ *dnshostname = '\0';
+ DEBUG(0,("gethostname failed\n"));
+ return False;
+ }
+
+ /* Ensure null termination. */
+ dnshostname[sizeof(dnshostname)-1] = '\0';
+
+ /* Ensure we get the cannonical name. */
+ if (!(hp = sys_gethostbyname(dnshostname))) {
+ *dnshostname = '\0';
+ return False;
+ }
+ fstrcpy(dnshostname, hp->h_name);
+ }
+ fstrcpy(my_dnsname, dnshostname);
+ return True;
+}
+
+/****************************************************************************
+ Get my own domain name.
+****************************************************************************/
+
+BOOL get_mydnsdomname(fstring my_domname)
+{
+ fstring domname;
+ char *p;
+
+ *my_domname = '\0';
+ if (!get_mydnsfullname(domname)) {
+ return False;
+ }
+ p = strchr_m(domname, '.');
+ if (p) {
+ p++;
+ fstrcpy(my_domname, p);
+ }
+
+ return False;
+}
+
+/****************************************************************************
+ Interpret a protocol description string, with a default.
+****************************************************************************/
+
+int interpret_protocol(const char *str,int def)
+{
+ if (strequal(str,"NT1"))
+ return(PROTOCOL_NT1);
+ if (strequal(str,"LANMAN2"))
+ return(PROTOCOL_LANMAN2);
+ if (strequal(str,"LANMAN1"))
+ return(PROTOCOL_LANMAN1);
+ if (strequal(str,"CORE"))
+ return(PROTOCOL_CORE);
+ if (strequal(str,"COREPLUS"))
+ return(PROTOCOL_COREPLUS);
+ if (strequal(str,"CORE+"))
+ return(PROTOCOL_COREPLUS);
+
+ DEBUG(0,("Unrecognised protocol level %s\n",str));
+
+ return(def);
+}
+
+/****************************************************************************
+ Return true if a string could be a pure IP address.
+****************************************************************************/
+
+BOOL is_ipaddress(const char *str)
+{
+ BOOL pure_address = True;
+ int i;
+
+ for (i=0; pure_address && str[i]; i++)
+ if (!(isdigit((int)str[i]) || str[i] == '.'))
+ pure_address = False;
+
+ /* Check that a pure number is not misinterpreted as an IP */
+ pure_address = pure_address && (strchr_m(str, '.') != NULL);
+
+ return pure_address;
+}
+
+/****************************************************************************
+ Interpret an internet address or name into an IP address in 4 byte form.
+****************************************************************************/
+
+uint32 interpret_addr(const char *str)
+{
+ struct hostent *hp;
+ uint32 res;
+
+ if (strcmp(str,"0.0.0.0") == 0)
+ return(0);
+ if (strcmp(str,"255.255.255.255") == 0)
+ return(0xFFFFFFFF);
+
+ /* if it's in the form of an IP address then get the lib to interpret it */
+ if (is_ipaddress(str)) {
+ res = inet_addr(str);
+ } else {
+ /* otherwise assume it's a network name of some sort and use
+ sys_gethostbyname */
+ if ((hp = sys_gethostbyname(str)) == 0) {
+ DEBUG(3,("sys_gethostbyname: Unknown host. %s\n",str));
+ return 0;
+ }
+
+ if(hp->h_addr == NULL) {
+ DEBUG(3,("sys_gethostbyname: host address is invalid for host %s\n",str));
+ return 0;
+ }
+ putip((char *)&res,(char *)hp->h_addr);
+ }
+
+ if (res == (uint32)-1)
+ return(0);
+
+ return(res);
+}
+
+/*******************************************************************
+ A convenient addition to interpret_addr().
+******************************************************************/
+
+struct in_addr *interpret_addr2(const char *str)
+{
+ static struct in_addr ret;
+ uint32 a = interpret_addr(str);
+ ret.s_addr = a;
+ return(&ret);
+}
+
+/*******************************************************************
+ Check if an IP is the 0.0.0.0.
+******************************************************************/
+
+BOOL is_zero_ip(struct in_addr ip)
+{
+ uint32 a;
+ putip((char *)&a,(char *)&ip);
+ return(a == 0);
+}
+
+/*******************************************************************
+ Set an IP to 0.0.0.0.
+******************************************************************/
+
+void zero_ip(struct in_addr *ip)
+{
+ static BOOL init;
+ static struct in_addr ipzero;
+
+ if (!init) {
+ ipzero = *interpret_addr2("0.0.0.0");
+ init = True;
+ }
+
+ *ip = ipzero;
+}
+
+#if (defined(HAVE_NETGROUP) && defined(WITH_AUTOMOUNT))
+/******************************************************************
+ Remove any mount options such as -rsize=2048,wsize=2048 etc.
+ Based on a fix from <Thomas.Hepper@icem.de>.
+*******************************************************************/
+
+static void strip_mount_options( pstring *str)
+{
+ if (**str == '-') {
+ char *p = *str;
+ while(*p && !isspace(*p))
+ p++;
+ while(*p && isspace(*p))
+ p++;
+ if(*p) {
+ pstring tmp_str;
+
+ pstrcpy(tmp_str, p);
+ pstrcpy(*str, tmp_str);
+ }
+ }
+}
+
+/*******************************************************************
+ Patch from jkf@soton.ac.uk
+ Split Luke's automount_server into YP lookup and string splitter
+ so can easily implement automount_path().
+ As we may end up doing both, cache the last YP result.
+*******************************************************************/
+
+#ifdef WITH_NISPLUS_HOME
+char *automount_lookup(const char *user_name)
+{
+ static fstring last_key = "";
+ static pstring last_value = "";
+
+ char *nis_map = (char *)lp_nis_home_map_name();
+
+ char buffer[NIS_MAXATTRVAL + 1];
+ nis_result *result;
+ nis_object *object;
+ entry_obj *entry;
+
+ if (strcmp(user_name, last_key)) {
+ slprintf(buffer, sizeof(buffer)-1, "[key=%s],%s", user_name, nis_map);
+ DEBUG(5, ("NIS+ querystring: %s\n", buffer));
+
+ if (result = nis_list(buffer, FOLLOW_PATH|EXPAND_NAME|HARD_LOOKUP, NULL, NULL)) {
+ if (result->status != NIS_SUCCESS) {
+ DEBUG(3, ("NIS+ query failed: %s\n", nis_sperrno(result->status)));
+ fstrcpy(last_key, ""); pstrcpy(last_value, "");
+ } else {
+ object = result->objects.objects_val;
+ if (object->zo_data.zo_type == ENTRY_OBJ) {
+ entry = &object->zo_data.objdata_u.en_data;
+ DEBUG(5, ("NIS+ entry type: %s\n", entry->en_type));
+ DEBUG(3, ("NIS+ result: %s\n", entry->en_cols.en_cols_val[1].ec_value.ec_value_val));
+
+ pstrcpy(last_value, entry->en_cols.en_cols_val[1].ec_value.ec_value_val);
+ pstring_sub(last_value, "&", user_name);
+ fstrcpy(last_key, user_name);
+ }
+ }
+ }
+ nis_freeresult(result);
+ }
+
+ strip_mount_options(&last_value);
+
+ DEBUG(4, ("NIS+ Lookup: %s resulted in %s\n", user_name, last_value));
+ return last_value;
+}
+#else /* WITH_NISPLUS_HOME */
+
+char *automount_lookup(const char *user_name)
+{
+ static fstring last_key = "";
+ static pstring last_value = "";
+
+ int nis_error; /* returned by yp all functions */
+ char *nis_result; /* yp_match inits this */
+ int nis_result_len; /* and set this */
+ char *nis_domain; /* yp_get_default_domain inits this */
+ char *nis_map = (char *)lp_nis_home_map_name();
+
+ if ((nis_error = yp_get_default_domain(&nis_domain)) != 0) {
+ DEBUG(3, ("YP Error: %s\n", yperr_string(nis_error)));
+ return last_value;
+ }
+
+ DEBUG(5, ("NIS Domain: %s\n", nis_domain));
+
+ if (!strcmp(user_name, last_key)) {
+ nis_result = last_value;
+ nis_result_len = strlen(last_value);
+ nis_error = 0;
+ } else {
+ if ((nis_error = yp_match(nis_domain, nis_map, user_name, strlen(user_name),
+ &nis_result, &nis_result_len)) == 0) {
+ if (!nis_error && nis_result_len >= sizeof(pstring)) {
+ nis_result_len = sizeof(pstring)-1;
+ }
+ fstrcpy(last_key, user_name);
+ strncpy(last_value, nis_result, nis_result_len);
+ last_value[nis_result_len] = '\0';
+ strip_mount_options(&last_value);
+
+ } else if(nis_error == YPERR_KEY) {
+
+ /* If Key lookup fails user home server is not in nis_map
+ use default information for server, and home directory */
+ last_value[0] = 0;
+ DEBUG(3, ("YP Key not found: while looking up \"%s\" in map \"%s\"\n",
+ user_name, nis_map));
+ DEBUG(3, ("using defaults for server and home directory\n"));
+ } else {
+ DEBUG(3, ("YP Error: \"%s\" while looking up \"%s\" in map \"%s\"\n",
+ yperr_string(nis_error), user_name, nis_map));
+ }
+ }
+
+ DEBUG(4, ("YP Lookup: %s resulted in %s\n", user_name, last_value));
+ return last_value;
+}
+#endif /* WITH_NISPLUS_HOME */
+#endif
+
+/*******************************************************************
+ Are two IPs on the same subnet?
+********************************************************************/
+
+BOOL same_net(struct in_addr ip1,struct in_addr ip2,struct in_addr mask)
+{
+ uint32 net1,net2,nmask;
+
+ nmask = ntohl(mask.s_addr);
+ net1 = ntohl(ip1.s_addr);
+ net2 = ntohl(ip2.s_addr);
+
+ return((net1 & nmask) == (net2 & nmask));
+}
+
+
+/****************************************************************************
+ Check if a process exists. Does this work on all unixes?
+****************************************************************************/
+
+BOOL process_exists(pid_t pid)
+{
+ /* Doing kill with a non-positive pid causes messages to be
+ * sent to places we don't want. */
+ SMB_ASSERT(pid > 0);
+ return(kill(pid,0) == 0 || errno != ESRCH);
+}
+
+/*******************************************************************
+ Convert a uid into a user name.
+********************************************************************/
+
+const char *uidtoname(uid_t uid)
+{
+ static fstring name;
+ struct passwd *pass;
+
+ pass = getpwuid_alloc(uid);
+ if (pass) {
+ fstrcpy(name, pass->pw_name);
+ passwd_free(&pass);
+ } else {
+ slprintf(name, sizeof(name) - 1, "%ld",(long int)uid);
+ }
+ return name;
+}
+
+
+/*******************************************************************
+ Convert a gid into a group name.
+********************************************************************/
+
+char *gidtoname(gid_t gid)
+{
+ static fstring name;
+ struct group *grp;
+
+ grp = getgrgid(gid);
+ if (grp)
+ return(grp->gr_name);
+ slprintf(name,sizeof(name) - 1, "%d",(int)gid);
+ return(name);
+}
+
+/*******************************************************************
+ Convert a user name into a uid.
+********************************************************************/
+
+uid_t nametouid(const char *name)
+{
+ struct passwd *pass;
+ char *p;
+ uid_t u;
+
+ pass = getpwnam_alloc(name);
+ if (pass) {
+ u = pass->pw_uid;
+ passwd_free(&pass);
+ return u;
+ }
+
+ u = (uid_t)strtol(name, &p, 0);
+ if ((p != name) && (*p == '\0'))
+ return u;
+
+ return (uid_t)-1;
+}
+
+/*******************************************************************
+ Convert a name to a gid_t if possible. Return -1 if not a group.
+********************************************************************/
+
+gid_t nametogid(const char *name)
+{
+ struct group *grp;
+ char *p;
+ gid_t g;
+
+ g = (gid_t)strtol(name, &p, 0);
+ if ((p != name) && (*p == '\0'))
+ return g;
+
+ grp = sys_getgrnam(name);
+ if (grp)
+ return(grp->gr_gid);
+ return (gid_t)-1;
+}
+
+/*******************************************************************
+ legacy wrapper for smb_panic2()
+********************************************************************/
+void smb_panic( const char *why )
+{
+ smb_panic2( why, True );
+}
+
+/*******************************************************************
+ Something really nasty happened - panic !
+********************************************************************/
+
+#ifdef HAVE_LIBEXC_H
+#include <libexc.h>
+#endif
+
+void smb_panic2(const char *why, BOOL decrement_pid_count )
+{
+ char *cmd;
+ int result;
+#ifdef HAVE_BACKTRACE_SYMBOLS
+ void *backtrace_stack[BACKTRACE_STACK_SIZE];
+ size_t backtrace_size;
+ char **backtrace_strings;
+#endif
+
+#ifdef DEVELOPER
+ {
+ extern char *global_clobber_region_function;
+ extern unsigned int global_clobber_region_line;
+
+ if (global_clobber_region_function) {
+ DEBUG(0,("smb_panic: clobber_region() last called from [%s(%u)]\n",
+ global_clobber_region_function,
+ global_clobber_region_line));
+ }
+ }
+#endif
+
+ /* only smbd needs to decrement the smbd counter in connections.tdb */
+ if ( decrement_pid_count )
+ decrement_smbd_process_count();
+
+ cmd = lp_panic_action();
+ if (cmd && *cmd) {
+ DEBUG(0, ("smb_panic(): calling panic action [%s]\n", cmd));
+ result = system(cmd);
+
+ if (result == -1)
+ DEBUG(0, ("smb_panic(): fork failed in panic action: %s\n",
+ strerror(errno)));
+ else
+ DEBUG(0, ("smb_panic(): action returned status %d\n",
+ WEXITSTATUS(result)));
+ }
+ DEBUG(0,("PANIC: %s\n", why));
+
+#ifdef HAVE_BACKTRACE_SYMBOLS
+ /* get the backtrace (stack frames) */
+ backtrace_size = backtrace(backtrace_stack,BACKTRACE_STACK_SIZE);
+ backtrace_strings = backtrace_symbols(backtrace_stack, backtrace_size);
+
+ DEBUG(0, ("BACKTRACE: %lu stack frames:\n",
+ (unsigned long)backtrace_size));
+
+ if (backtrace_strings) {
+ int i;
+
+ for (i = 0; i < backtrace_size; i++)
+ DEBUGADD(0, (" #%u %s\n", i, backtrace_strings[i]));
+
+ /* Leak the backtrace_strings, rather than risk what free() might do */
+ }
+
+#elif HAVE_LIBEXC
+
+#define NAMESIZE 32 /* Arbitrary */
+
+ /* The IRIX libexc library provides an API for unwinding the stack. See
+ * libexc(3) for details. Apparantly trace_back_stack leaks memory, but
+ * since we are about to abort anyway, it hardly matters.
+ *
+ * Note that if we paniced due to a SIGSEGV or SIGBUS (or similar) this
+ * will fail with a nasty message upon failing to open the /proc entry.
+ */
+ {
+ __uint64_t addrs[BACKTRACE_STACK_SIZE];
+ char * names[BACKTRACE_STACK_SIZE];
+ char namebuf[BACKTRACE_STACK_SIZE * NAMESIZE];
+
+ int i;
+ int levels;
+
+ ZERO_ARRAY(addrs);
+ ZERO_ARRAY(names);
+ ZERO_ARRAY(namebuf);
+
+ for (i = 0; i < BACKTRACE_STACK_SIZE; i++) {
+ names[i] = namebuf + (i * NAMESIZE);
+ }
+
+ levels = trace_back_stack(0, addrs, names,
+ BACKTRACE_STACK_SIZE, NAMESIZE);
+
+ DEBUG(0, ("BACKTRACE: %d stack frames:\n", levels));
+ for (i = 0; i < levels; i++) {
+ DEBUGADD(0, (" #%d 0x%llx %s\n", i, addrs[i], names[i]));
+ }
+ }
+#undef NAMESIZE
+#endif
+
+ dbgflush();
+ abort();
+}
+
+/*******************************************************************
+ A readdir wrapper which just returns the file name.
+ ********************************************************************/
+
+const char *readdirname(DIR *p)
+{
+ SMB_STRUCT_DIRENT *ptr;
+ char *dname;
+
+ if (!p)
+ return(NULL);
+
+ ptr = (SMB_STRUCT_DIRENT *)sys_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 pstring buf;
+ int len = NAMLEN(ptr);
+ memcpy(buf, dname, len);
+ buf[len] = 0;
+ dname = buf;
+ }
+
+ return(dname);
+}
+
+/*******************************************************************
+ Utility function used to decide if the last component
+ of a path matches a (possibly wildcarded) entry in a namelist.
+********************************************************************/
+
+BOOL is_in_path(const char *name, name_compare_entry *namelist)
+{
+ pstring last_component;
+ char *p;
+
+ DEBUG(8, ("is_in_path: %s\n", name));
+
+ /* if we have no list it's obviously not in the path */
+ if((namelist == NULL ) || ((namelist != NULL) && (namelist[0].name == NULL))) {
+ DEBUG(8,("is_in_path: no name list.\n"));
+ return False;
+ }
+
+ /* Get the last component of the unix name. */
+ p = strrchr_m(name, '/');
+ strncpy(last_component, p ? ++p : name, sizeof(last_component)-1);
+ last_component[sizeof(last_component)-1] = '\0';
+
+ for(; namelist->name != NULL; namelist++) {
+ if(namelist->is_wild) {
+ if (mask_match(last_component, namelist->name, case_sensitive)) {
+ DEBUG(8,("is_in_path: mask match succeeded\n"));
+ return True;
+ }
+ } else {
+ if((case_sensitive && (strcmp(last_component, namelist->name) == 0))||
+ (!case_sensitive && (StrCaseCmp(last_component, namelist->name) == 0))) {
+ DEBUG(8,("is_in_path: match succeeded\n"));
+ return True;
+ }
+ }
+ }
+ DEBUG(8,("is_in_path: match not found\n"));
+
+ return False;
+}
+
+/*******************************************************************
+ Strip a '/' separated list into an array of
+ name_compare_enties structures suitable for
+ passing to is_in_path(). We do this for
+ speed so we can pre-parse all the names in the list
+ and don't do it for each call to is_in_path().
+ namelist is modified here and is assumed to be
+ a copy owned by the caller.
+ We also check if the entry contains a wildcard to
+ remove a potentially expensive call to mask_match
+ if possible.
+********************************************************************/
+
+void set_namearray(name_compare_entry **ppname_array, char *namelist)
+{
+ char *name_end;
+ char *nameptr = namelist;
+ int num_entries = 0;
+ int i;
+
+ (*ppname_array) = NULL;
+
+ if((nameptr == NULL ) || ((nameptr != NULL) && (*nameptr == '\0')))
+ return;
+
+ /* We need to make two passes over the string. The
+ first to count the number of elements, the second
+ to split it.
+ */
+
+ while(*nameptr) {
+ if ( *nameptr == '/' ) {
+ /* cope with multiple (useless) /s) */
+ nameptr++;
+ continue;
+ }
+ /* find the next / */
+ name_end = strchr_m(nameptr, '/');
+
+ /* oops - the last check for a / didn't find one. */
+ if (name_end == NULL)
+ break;
+
+ /* next segment please */
+ nameptr = name_end + 1;
+ num_entries++;
+ }
+
+ if(num_entries == 0)
+ return;
+
+ if(( (*ppname_array) = (name_compare_entry *)malloc(
+ (num_entries + 1) * sizeof(name_compare_entry))) == NULL) {
+ DEBUG(0,("set_namearray: malloc fail\n"));
+ return;
+ }
+
+ /* Now copy out the names */
+ nameptr = namelist;
+ i = 0;
+ while(*nameptr) {
+ if ( *nameptr == '/' ) {
+ /* cope with multiple (useless) /s) */
+ nameptr++;
+ continue;
+ }
+ /* find the next / */
+ if ((name_end = strchr_m(nameptr, '/')) != NULL)
+ *name_end = 0;
+
+ /* oops - the last check for a / didn't find one. */
+ if(name_end == NULL)
+ break;
+
+ (*ppname_array)[i].is_wild = ms_has_wild(nameptr);
+ if(((*ppname_array)[i].name = strdup(nameptr)) == NULL) {
+ DEBUG(0,("set_namearray: malloc fail (1)\n"));
+ return;
+ }
+
+ /* next segment please */
+ nameptr = name_end + 1;
+ i++;
+ }
+
+ (*ppname_array)[i].name = NULL;
+
+ return;
+}
+
+/****************************************************************************
+ Routine to free a namearray.
+****************************************************************************/
+
+void free_namearray(name_compare_entry *name_array)
+{
+ int i;
+
+ if(name_array == NULL)
+ return;
+
+ for(i=0; name_array[i].name!=NULL; i++)
+ SAFE_FREE(name_array[i].name);
+ SAFE_FREE(name_array);
+}
+
+/****************************************************************************
+ Simple routine to do POSIX file locking. Cruft in NFS and 64->32 bit mapping
+ is dealt with in posix.c
+****************************************************************************/
+
+BOOL fcntl_lock(int fd, int op, SMB_OFF_T offset, SMB_OFF_T count, int type)
+{
+ SMB_STRUCT_FLOCK lock;
+ int ret;
+
+ DEBUG(8,("fcntl_lock %d %d %.0f %.0f %d\n",fd,op,(double)offset,(double)count,type));
+
+ lock.l_type = type;
+ lock.l_whence = SEEK_SET;
+ lock.l_start = offset;
+ lock.l_len = count;
+ lock.l_pid = 0;
+
+ ret = sys_fcntl_ptr(fd,op,&lock);
+
+ if (ret == -1 && errno != 0)
+ DEBUG(3,("fcntl_lock: fcntl lock gave errno %d (%s)\n",errno,strerror(errno)));
+
+ /* a lock query */
+ if (op == SMB_F_GETLK) {
+ if ((ret != -1) &&
+ (lock.l_type != F_UNLCK) &&
+ (lock.l_pid != 0) &&
+ (lock.l_pid != sys_getpid())) {
+ DEBUG(3,("fcntl_lock: fd %d is locked by pid %d\n",fd,(int)lock.l_pid));
+ return(True);
+ }
+
+ /* it must be not locked or locked by me */
+ return(False);
+ }
+
+ /* a lock set or unset */
+ if (ret == -1) {
+ DEBUG(3,("fcntl_lock: lock failed at offset %.0f count %.0f op %d type %d (%s)\n",
+ (double)offset,(double)count,op,type,strerror(errno)));
+ return(False);
+ }
+
+ /* everything went OK */
+ DEBUG(8,("fcntl_lock: Lock call successful\n"));
+
+ return(True);
+}
+
+/*******************************************************************
+ Is the name specified one of my netbios names.
+ Returns true if it is equal, false otherwise.
+********************************************************************/
+
+BOOL is_myname(const char *s)
+{
+ int n;
+ BOOL ret = False;
+
+ for (n=0; my_netbios_names(n); n++) {
+ if (strequal(my_netbios_names(n), s)) {
+ ret=True;
+ break;
+ }
+ }
+ DEBUG(8, ("is_myname(\"%s\") returns %d\n", s, ret));
+ return(ret);
+}
+
+/********************************************************************
+ Return only the first IP address of our configured interfaces
+ as a string
+ *******************************************************************/
+
+const char* get_my_primary_ip (void)
+{
+ static fstring ip_string;
+ int n;
+ struct iface_struct nics[MAX_INTERFACES];
+
+ if ((n=get_interfaces(nics, MAX_INTERFACES)) <= 0)
+ return NULL;
+
+ fstrcpy(ip_string, inet_ntoa(nics[0].ip));
+ return ip_string;
+}
+
+BOOL is_myname_or_ipaddr(const char *s)
+{
+ /* optimize for the common case */
+ if (strequal(s, global_myname()))
+ return True;
+
+ /* maybe its an IP address? */
+ if (is_ipaddress(s)) {
+ struct iface_struct nics[MAX_INTERFACES];
+ int i, n;
+ uint32 ip;
+
+ ip = interpret_addr(s);
+ if ((ip==0) || (ip==0xffffffff))
+ return False;
+
+ n = get_interfaces(nics, MAX_INTERFACES);
+ for (i=0; i<n; i++) {
+ if (ip == nics[i].ip.s_addr)
+ return True;
+ }
+ }
+
+ /* check for an alias */
+ if (is_myname(s))
+ return True;
+
+ /* no match */
+ return False;
+}
+
+/*******************************************************************
+ Is the name specified our workgroup/domain.
+ Returns true if it is equal, false otherwise.
+********************************************************************/
+
+BOOL is_myworkgroup(const char *s)
+{
+ BOOL ret = False;
+
+ if (strequal(s, lp_workgroup())) {
+ ret=True;
+ }
+
+ DEBUG(8, ("is_myworkgroup(\"%s\") returns %d\n", s, ret));
+ return(ret);
+}
+
+/*******************************************************************
+ we distinguish between 2K and XP by the "Native Lan Manager" string
+ WinXP => "Windows 2002 5.1"
+ Win2k => "Windows 2000 5.0"
+ NT4 => "Windows NT 4.0"
+ Win9x => "Windows 4.0"
+ Windows 2003 doesn't set the native lan manager string but
+ they do set the domain to "Windows 2003 5.2" (probably a bug).
+********************************************************************/
+
+void ra_lanman_string( const char *native_lanman )
+{
+ if ( strcmp( native_lanman, "Windows 2002 5.1" ) == 0 )
+ set_remote_arch( RA_WINXP );
+ else if ( strcmp( native_lanman, "Windows Server 2003 5.2" ) == 0 )
+ set_remote_arch( RA_WIN2K3 );
+}
+
+/*******************************************************************
+ Set the horrid remote_arch string based on an enum.
+********************************************************************/
+
+void set_remote_arch(enum remote_arch_types type)
+{
+ extern fstring remote_arch;
+ ra_type = type;
+ switch( type ) {
+ case RA_WFWG:
+ fstrcpy(remote_arch, "WfWg");
+ break;
+ case RA_OS2:
+ fstrcpy(remote_arch, "OS2");
+ break;
+ case RA_WIN95:
+ fstrcpy(remote_arch, "Win95");
+ break;
+ case RA_WINNT:
+ fstrcpy(remote_arch, "WinNT");
+ break;
+ case RA_WIN2K:
+ fstrcpy(remote_arch, "Win2K");
+ break;
+ case RA_WINXP:
+ fstrcpy(remote_arch, "WinXP");
+ break;
+ case RA_WIN2K3:
+ fstrcpy(remote_arch, "Win2K3");
+ break;
+ case RA_SAMBA:
+ fstrcpy(remote_arch,"Samba");
+ break;
+ default:
+ ra_type = RA_UNKNOWN;
+ fstrcpy(remote_arch, "UNKNOWN");
+ break;
+ }
+
+ DEBUG(10,("set_remote_arch: Client arch is \'%s\'\n", remote_arch));
+}
+
+/*******************************************************************
+ Get the remote_arch type.
+********************************************************************/
+
+enum remote_arch_types get_remote_arch(void)
+{
+ return ra_type;
+}
+
+void print_asc(int level, const unsigned char *buf,int len)
+{
+ int i;
+ for (i=0;i<len;i++)
+ DEBUG(level,("%c", isprint(buf[i])?buf[i]:'.'));
+}
+
+void dump_data(int level, const char *buf1,int len)
+{
+ const unsigned char *buf = (const unsigned char *)buf1;
+ int i=0;
+ if (len<=0) return;
+
+ if (!DEBUGLVL(level)) return;
+
+ DEBUGADD(level,("[%03X] ",i));
+ for (i=0;i<len;) {
+ DEBUGADD(level,("%02X ",(int)buf[i]));
+ i++;
+ if (i%8 == 0) DEBUGADD(level,(" "));
+ if (i%16 == 0) {
+ print_asc(level,&buf[i-16],8); DEBUGADD(level,(" "));
+ print_asc(level,&buf[i-8],8); DEBUGADD(level,("\n"));
+ if (i<len) DEBUGADD(level,("[%03X] ",i));
+ }
+ }
+ if (i%16) {
+ int n;
+ n = 16 - (i%16);
+ DEBUGADD(level,(" "));
+ if (n>8) DEBUGADD(level,(" "));
+ while (n--) DEBUGADD(level,(" "));
+ n = MIN(8,i%16);
+ print_asc(level,&buf[i-(i%16)],n); DEBUGADD(level,( " " ));
+ n = (i%16) - n;
+ if (n>0) print_asc(level,&buf[i-n],n);
+ DEBUGADD(level,("\n"));
+ }
+}
+
+void dump_data_pw(const char *msg, const uchar * data, size_t len)
+{
+#ifdef DEBUG_PASSWORD
+ DEBUG(11, ("%s", msg));
+ if (data != NULL && len > 0)
+ {
+ dump_data(11, data, len);
+ }
+#endif
+}
+
+char *tab_depth(int depth)
+{
+ static pstring spaces;
+ memset(spaces, ' ', depth * 4);
+ spaces[depth * 4] = 0;
+ return spaces;
+}
+
+/*****************************************************************************
+ Provide a checksum on a string
+
+ Input: s - the null-terminated character string for which the checksum
+ will be calculated.
+
+ Output: The checksum value calculated for s.
+*****************************************************************************/
+
+int str_checksum(const char *s)
+{
+ int res = 0;
+ int c;
+ int i=0;
+
+ while(*s) {
+ c = *s;
+ res ^= (c << (i % 15)) ^ (c >> (15-(i%15)));
+ s++;
+ i++;
+ }
+ return(res);
+}
+
+/*****************************************************************
+ Zero a memory area then free it. Used to catch bugs faster.
+*****************************************************************/
+
+void zero_free(void *p, size_t size)
+{
+ memset(p, 0, size);
+ SAFE_FREE(p);
+}
+
+/*****************************************************************
+ Set our open file limit to a requested max and return the limit.
+*****************************************************************/
+
+int set_maxfiles(int requested_max)
+{
+#if (defined(HAVE_GETRLIMIT) && defined(RLIMIT_NOFILE))
+ struct rlimit rlp;
+ int saved_current_limit;
+
+ if(getrlimit(RLIMIT_NOFILE, &rlp)) {
+ DEBUG(0,("set_maxfiles: getrlimit (1) for RLIMIT_NOFILE failed with error %s\n",
+ strerror(errno) ));
+ /* just guess... */
+ return requested_max;
+ }
+
+ /*
+ * Set the fd limit to be real_max_open_files + MAX_OPEN_FUDGEFACTOR to
+ * account for the extra fd we need
+ * as well as the log files and standard
+ * handles etc. Save the limit we want to set in case
+ * we are running on an OS that doesn't support this limit (AIX)
+ * which always returns RLIM_INFINITY for rlp.rlim_max.
+ */
+
+ /* Try raising the hard (max) limit to the requested amount. */
+
+#if defined(RLIM_INFINITY)
+ if (rlp.rlim_max != RLIM_INFINITY) {
+ int orig_max = rlp.rlim_max;
+
+ if ( rlp.rlim_max < requested_max )
+ rlp.rlim_max = requested_max;
+
+ /* This failing is not an error - many systems (Linux) don't
+ support our default request of 10,000 open files. JRA. */
+
+ if(setrlimit(RLIMIT_NOFILE, &rlp)) {
+ DEBUG(3,("set_maxfiles: setrlimit for RLIMIT_NOFILE for %d max files failed with error %s\n",
+ (int)rlp.rlim_max, strerror(errno) ));
+
+ /* Set failed - restore original value from get. */
+ rlp.rlim_max = orig_max;
+ }
+ }
+#endif
+
+ /* Now try setting the soft (current) limit. */
+
+ saved_current_limit = rlp.rlim_cur = MIN(requested_max,rlp.rlim_max);
+
+ if(setrlimit(RLIMIT_NOFILE, &rlp)) {
+ DEBUG(0,("set_maxfiles: setrlimit for RLIMIT_NOFILE for %d files failed with error %s\n",
+ (int)rlp.rlim_cur, strerror(errno) ));
+ /* just guess... */
+ return saved_current_limit;
+ }
+
+ if(getrlimit(RLIMIT_NOFILE, &rlp)) {
+ DEBUG(0,("set_maxfiles: getrlimit (2) for RLIMIT_NOFILE failed with error %s\n",
+ strerror(errno) ));
+ /* just guess... */
+ return saved_current_limit;
+ }
+
+#if defined(RLIM_INFINITY)
+ if(rlp.rlim_cur == RLIM_INFINITY)
+ return saved_current_limit;
+#endif
+
+ if((int)rlp.rlim_cur > saved_current_limit)
+ return saved_current_limit;
+
+ return rlp.rlim_cur;
+#else /* !defined(HAVE_GETRLIMIT) || !defined(RLIMIT_NOFILE) */
+ /*
+ * No way to know - just guess...
+ */
+ return requested_max;
+#endif
+}
+
+/*****************************************************************
+ Splits out the start of the key (HKLM or HKU) and the rest of the key.
+*****************************************************************/
+
+BOOL reg_split_key(const char *full_keyname, uint32 *reg_type, char *key_name)
+{
+ pstring tmp;
+
+ if (!next_token(&full_keyname, tmp, "\\", sizeof(tmp)))
+ return False;
+
+ (*reg_type) = 0;
+
+ DEBUG(10, ("reg_split_key: hive %s\n", tmp));
+
+ if (strequal(tmp, "HKLM") || strequal(tmp, "HKEY_LOCAL_MACHINE"))
+ (*reg_type) = HKEY_LOCAL_MACHINE;
+ else if (strequal(tmp, "HKU") || strequal(tmp, "HKEY_USERS"))
+ (*reg_type) = HKEY_USERS;
+ else {
+ DEBUG(10,("reg_split_key: unrecognised hive key %s\n", tmp));
+ return False;
+ }
+
+ if (next_token(&full_keyname, tmp, "\n\r", sizeof(tmp)))
+ fstrcpy(key_name, tmp);
+ else
+ key_name[0] = 0;
+
+ DEBUG(10, ("reg_split_key: name %s\n", key_name));
+
+ return True;
+}
+
+/*****************************************************************
+ Possibly replace mkstemp if it is broken.
+*****************************************************************/
+
+int smb_mkstemp(char *template)
+{
+#if HAVE_SECURE_MKSTEMP
+ return mkstemp(template);
+#else
+ /* have a reasonable go at emulating it. Hope that
+ the system mktemp() isn't completly hopeless */
+ char *p = mktemp(template);
+ if (!p)
+ return -1;
+ return open(p, O_CREAT|O_EXCL|O_RDWR, 0600);
+#endif
+}
+
+/*****************************************************************
+ malloc that aborts with smb_panic on fail or zero size.
+ *****************************************************************/
+
+void *smb_xmalloc(size_t size)
+{
+ void *p;
+ if (size == 0)
+ smb_panic("smb_xmalloc: called with zero size.\n");
+ if ((p = malloc(size)) == NULL) {
+ DEBUG(0, ("smb_xmalloc() failed to allocate %lu bytes\n", (unsigned long)size));
+ smb_panic("smb_xmalloc: malloc fail.\n");
+ }
+ return p;
+}
+
+/**
+ Memdup with smb_panic on fail.
+**/
+
+void *smb_xmemdup(const void *p, size_t size)
+{
+ void *p2;
+ p2 = smb_xmalloc(size);
+ memcpy(p2, p, size);
+ return p2;
+}
+
+/**
+ strdup that aborts on malloc fail.
+**/
+
+char *smb_xstrdup(const char *s)
+{
+ char *s1 = strdup(s);
+ if (!s1)
+ smb_panic("smb_xstrdup: malloc fail\n");
+ return s1;
+}
+
+/**
+ strndup that aborts on malloc fail.
+**/
+
+char *smb_xstrndup(const char *s, size_t n)
+{
+ char *s1 = strndup(s, n);
+ if (!s1)
+ smb_panic("smb_xstrndup: malloc fail\n");
+ return s1;
+}
+
+/*
+ vasprintf that aborts on malloc fail
+*/
+
+ int smb_xvasprintf(char **ptr, const char *format, va_list ap)
+{
+ int n;
+ va_list ap2;
+
+ VA_COPY(ap2, ap);
+
+ n = vasprintf(ptr, format, ap2);
+ if (n == -1 || ! *ptr)
+ smb_panic("smb_xvasprintf: out of memory");
+ return n;
+}
+
+/*****************************************************************
+ Like strdup but for memory.
+*****************************************************************/
+
+void *memdup(const void *p, size_t size)
+{
+ void *p2;
+ if (size == 0)
+ return NULL;
+ p2 = malloc(size);
+ if (!p2)
+ return NULL;
+ memcpy(p2, p, size);
+ return p2;
+}
+
+/*****************************************************************
+ Get local hostname and cache result.
+*****************************************************************/
+
+char *myhostname(void)
+{
+ static pstring ret;
+ if (ret[0] == 0)
+ get_myname(ret);
+ return ret;
+}
+
+/*****************************************************************
+ A useful function for returning a path in the Samba lock directory.
+*****************************************************************/
+
+char *lock_path(const char *name)
+{
+ static pstring fname;
+
+ pstrcpy(fname,lp_lockdir());
+ trim_char(fname,'\0','/');
+
+ if (!directory_exist(fname,NULL))
+ mkdir(fname,0755);
+
+ pstrcat(fname,"/");
+ pstrcat(fname,name);
+
+ return fname;
+}
+
+/*****************************************************************
+ A useful function for returning a path in the Samba pid directory.
+*****************************************************************/
+
+char *pid_path(const char *name)
+{
+ static pstring fname;
+
+ pstrcpy(fname,lp_piddir());
+ trim_char(fname,'\0','/');
+
+ if (!directory_exist(fname,NULL))
+ mkdir(fname,0755);
+
+ pstrcat(fname,"/");
+ pstrcat(fname,name);
+
+ return fname;
+}
+
+/**
+ * @brief Returns an absolute path to a file in the Samba lib directory.
+ *
+ * @param name File to find, relative to LIBDIR.
+ *
+ * @retval Pointer to a static #pstring containing the full path.
+ **/
+
+char *lib_path(const char *name)
+{
+ static pstring fname;
+ fstr_sprintf(fname, "%s/%s", dyn_LIBDIR, name);
+ return fname;
+}
+
+/**
+ * @brief Returns the platform specific shared library extension.
+ *
+ * @retval Pointer to a static #fstring containing the extension.
+ **/
+
+const char *shlib_ext(void)
+{
+ return dyn_SHLIBEXT;
+}
+
+/*******************************************************************
+ Given a filename - get its directory name
+ NB: Returned in static storage. Caveats:
+ o Not safe in thread environment.
+ o Caller must not free.
+ o If caller wishes to preserve, they should copy.
+********************************************************************/
+
+char *parent_dirname(const char *path)
+{
+ static pstring dirpath;
+ char *p;
+
+ if (!path)
+ return(NULL);
+
+ pstrcpy(dirpath, path);
+ p = strrchr_m(dirpath, '/'); /* Find final '/', if any */
+ if (!p) {
+ pstrcpy(dirpath, "."); /* No final "/", so dir is "." */
+ } else {
+ if (p == dirpath)
+ ++p; /* For root "/", leave "/" in place */
+ *p = '\0';
+ }
+ return dirpath;
+}
+
+
+/*******************************************************************
+ Determine if a pattern contains any Microsoft wildcard characters.
+*******************************************************************/
+
+BOOL ms_has_wild(const char *s)
+{
+ char c;
+ while ((c = *s++)) {
+ switch (c) {
+ case '*':
+ case '?':
+ case '<':
+ case '>':
+ case '"':
+ return True;
+ }
+ }
+ return False;
+}
+
+BOOL ms_has_wild_w(const smb_ucs2_t *s)
+{
+ smb_ucs2_t c;
+ if (!s) return False;
+ while ((c = *s++)) {
+ switch (c) {
+ case UCS2_CHAR('*'):
+ case UCS2_CHAR('?'):
+ case UCS2_CHAR('<'):
+ case UCS2_CHAR('>'):
+ case UCS2_CHAR('"'):
+ return True;
+ }
+ }
+ return False;
+}
+
+/*******************************************************************
+ A wrapper that handles case sensitivity and the special handling
+ of the ".." name.
+*******************************************************************/
+
+BOOL mask_match(const char *string, char *pattern, BOOL is_case_sensitive)
+{
+ if (strcmp(string,"..") == 0)
+ string = ".";
+ if (strcmp(pattern,".") == 0)
+ return False;
+
+ return ms_fnmatch(pattern, string, Protocol, is_case_sensitive) == 0;
+}
+
+/*******************************************************************
+ A wrapper that handles a list of patters and calls mask_match()
+ on each. Returns True if any of the patterns match.
+*******************************************************************/
+
+BOOL mask_match_list(const char *string, char **list, int listLen, BOOL is_case_sensitive)
+{
+ while (listLen-- > 0) {
+ if (mask_match(string, *list++, is_case_sensitive))
+ return True;
+ }
+ return False;
+}
+
+/*********************************************************
+ Recursive routine that is called by unix_wild_match.
+*********************************************************/
+
+static BOOL unix_do_match(const char *regexp, const char *str)
+{
+ const char *p;
+
+ for( p = regexp; *p && *str; ) {
+
+ switch(*p) {
+ case '?':
+ str++;
+ p++;
+ break;
+
+ case '*':
+
+ /*
+ * Look for a character matching
+ * the one after the '*'.
+ */
+ p++;
+ if(!*p)
+ return True; /* Automatic match */
+ while(*str) {
+
+ while(*str && (*p != *str))
+ str++;
+
+ /*
+ * Patch from weidel@multichart.de. In the case of the regexp
+ * '*XX*' we want to ensure there are at least 2 'X' characters
+ * in the string after the '*' for a match to be made.
+ */
+
+ {
+ int matchcount=0;
+
+ /*
+ * Eat all the characters that match, but count how many there were.
+ */
+
+ while(*str && (*p == *str)) {
+ str++;
+ matchcount++;
+ }
+
+ /*
+ * Now check that if the regexp had n identical characters that
+ * matchcount had at least that many matches.
+ */
+
+ while ( *(p+1) && (*(p+1) == *p)) {
+ p++;
+ matchcount--;
+ }
+
+ if ( matchcount <= 0 )
+ return False;
+ }
+
+ str--; /* We've eaten the match char after the '*' */
+
+ if(unix_do_match(p, str))
+ return True;
+
+ if(!*str)
+ return False;
+ else
+ str++;
+ }
+ return False;
+
+ default:
+ if(*str != *p)
+ return False;
+ str++;
+ p++;
+ break;
+ }
+ }
+
+ if(!*p && !*str)
+ return True;
+
+ if (!*p && str[0] == '.' && str[1] == 0)
+ return(True);
+
+ if (!*str && *p == '?') {
+ while (*p == '?')
+ p++;
+ return(!*p);
+ }
+
+ if(!*str && (*p == '*' && p[1] == '\0'))
+ return True;
+
+ return False;
+}
+
+/*******************************************************************
+ Simple case insensitive interface to a UNIX wildcard matcher.
+*******************************************************************/
+
+BOOL unix_wild_match(const char *pattern, const char *string)
+{
+ pstring p2, s2;
+ char *p;
+
+ pstrcpy(p2, pattern);
+ pstrcpy(s2, string);
+ strlower_m(p2);
+ strlower_m(s2);
+
+ /* Remove any *? and ** from the pattern as they are meaningless */
+ for(p = p2; *p; p++)
+ while( *p == '*' && (p[1] == '?' ||p[1] == '*'))
+ pstrcpy( &p[1], &p[2]);
+
+ if (strequal(p2,"*"))
+ return True;
+
+ return unix_do_match(p2, s2) == 0;
+}
+
+
+#ifdef __INSURE__
+
+/*******************************************************************
+This routine is a trick to immediately catch errors when debugging
+with insure. A xterm with a gdb is popped up when insure catches
+a error. It is Linux specific.
+********************************************************************/
+
+int _Insure_trap_error(int a1, int a2, int a3, int a4, int a5, int a6)
+{
+ static int (*fn)();
+ int ret;
+ char pidstr[10];
+ /* you can get /usr/bin/backtrace from
+ http://samba.org/ftp/unpacked/junkcode/backtrace */
+ pstring cmd = "/usr/bin/backtrace %d";
+
+ slprintf(pidstr, sizeof(pidstr)-1, "%d", sys_getpid());
+ pstring_sub(cmd, "%d", pidstr);
+
+ if (!fn) {
+ static void *h;
+ h = dlopen("/usr/local/parasoft/insure++lite/lib.linux2/libinsure.so", RTLD_LAZY);
+ fn = dlsym(h, "_Insure_trap_error");
+
+ if (!h || h == _Insure_trap_error) {
+ h = dlopen("/usr/local/parasoft/lib.linux2/libinsure.so", RTLD_LAZY);
+ fn = dlsym(h, "_Insure_trap_error");
+ }
+ }
+
+ ret = fn(a1, a2, a3, a4, a5, a6);
+
+ system(cmd);
+
+ return ret;
+}
+#endif
diff --git a/source/lib/util_file.c b/source/lib/util_file.c
new file mode 100644
index 00000000000..bd505ac921c
--- /dev/null
+++ b/source/lib/util_file.c
@@ -0,0 +1,606 @@
+/*
+ * Unix SMB/CIFS implementation.
+ * SMB parameters and setup
+ * Copyright (C) Andrew Tridgell 1992-1998 Modified by Jeremy Allison 1995.
+ *
+ * This program is free software; you can 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"
+
+#ifndef MAP_FAILED
+#define MAP_FAILED ((void *)-1)
+#endif
+
+
+static int gotalarm;
+
+/***************************************************************
+ Signal function to tell us we timed out.
+****************************************************************/
+
+static void gotalarm_sig(void)
+{
+ gotalarm = 1;
+}
+
+/***************************************************************
+ Lock or unlock a fd for a known lock type. Abandon after waitsecs
+ seconds.
+****************************************************************/
+
+BOOL do_file_lock(int fd, int waitsecs, int type)
+{
+ SMB_STRUCT_FLOCK lock;
+ int ret;
+ void (*oldsig_handler)(int);
+
+ gotalarm = 0;
+ oldsig_handler = CatchSignal(SIGALRM, SIGNAL_CAST gotalarm_sig);
+
+ lock.l_type = type;
+ lock.l_whence = SEEK_SET;
+ lock.l_start = 0;
+ lock.l_len = 1;
+ lock.l_pid = 0;
+
+ alarm(waitsecs);
+ /* Note we must *NOT* use sys_fcntl here ! JRA */
+ ret = fcntl(fd, SMB_F_SETLKW, &lock);
+ alarm(0);
+ CatchSignal(SIGALRM, SIGNAL_CAST oldsig_handler);
+
+ if (gotalarm) {
+ DEBUG(0, ("do_file_lock: failed to %s file.\n",
+ type == F_UNLCK ? "unlock" : "lock"));
+ return False;
+ }
+
+ return (ret == 0);
+}
+
+
+/***************************************************************
+ Lock an fd. Abandon after waitsecs seconds.
+****************************************************************/
+
+BOOL file_lock(int fd, int type, int secs, int *plock_depth)
+{
+ if (fd < 0)
+ return False;
+
+ (*plock_depth)++;
+
+ if ((*plock_depth) == 0)
+ {
+ if (!do_file_lock(fd, secs, type)) {
+ DEBUG(10,("file_lock: locking file failed, error = %s.\n",
+ strerror(errno)));
+ return False;
+ }
+ }
+
+ return True;
+}
+
+/***************************************************************
+ Unlock an fd. Abandon after waitsecs seconds.
+****************************************************************/
+
+BOOL file_unlock(int fd, int *plock_depth)
+{
+ BOOL ret=True;
+
+ if(*plock_depth == 1)
+ ret = do_file_lock(fd, 5, F_UNLCK);
+
+ (*plock_depth)--;
+
+ if(!ret)
+ DEBUG(10,("file_unlock: unlocking file failed, error = %s.\n",
+ strerror(errno)));
+ return ret;
+}
+
+/***************************************************************
+ locks a file for enumeration / modification.
+ update to be set = True if modification is required.
+****************************************************************/
+
+void *startfilepwent(char *pfile, char *s_readbuf, int bufsize,
+ int *file_lock_depth, BOOL update)
+{
+ FILE *fp = NULL;
+
+ if (!*pfile)
+ {
+ DEBUG(0, ("startfilepwent: No file set\n"));
+ return (NULL);
+ }
+ DEBUG(10, ("startfilepwent: opening file %s\n", pfile));
+
+ fp = sys_fopen(pfile, update ? "r+b" : "rb");
+
+ if (fp == NULL) {
+ DEBUG(0, ("startfilepwent: unable to open file %s\n", pfile));
+ return NULL;
+ }
+
+ /* Set a buffer to do more efficient reads */
+ setvbuf(fp, s_readbuf, _IOFBF, bufsize);
+
+ if (!file_lock(fileno(fp), (update ? F_WRLCK : F_RDLCK), 5, file_lock_depth))
+ {
+ DEBUG(0, ("startfilepwent: unable to lock file %s\n", pfile));
+ fclose(fp);
+ return NULL;
+ }
+
+ /* Make sure it is only rw by the owner */
+ chmod(pfile, 0600);
+
+ /* We have a lock on the file. */
+ return (void *)fp;
+}
+
+/***************************************************************
+ End enumeration of the file.
+****************************************************************/
+void endfilepwent(void *vp, int *file_lock_depth)
+{
+ FILE *fp = (FILE *)vp;
+
+ file_unlock(fileno(fp), file_lock_depth);
+ fclose(fp);
+ DEBUG(7, ("endfilepwent: closed file.\n"));
+}
+
+/*************************************************************************
+ Return the current position in the file list as an SMB_BIG_UINT.
+ This must be treated as an opaque token.
+*************************************************************************/
+SMB_BIG_UINT getfilepwpos(void *vp)
+{
+ return (SMB_BIG_UINT)sys_ftell((FILE *)vp);
+}
+
+/*************************************************************************
+ Set the current position in the file list from an SMB_BIG_UINT.
+ This must be treated as an opaque token.
+*************************************************************************/
+BOOL setfilepwpos(void *vp, SMB_BIG_UINT tok)
+{
+ return !sys_fseek((FILE *)vp, (SMB_OFF_T)tok, SEEK_SET);
+}
+
+/*************************************************************************
+ gets a line out of a file.
+ line is of format "xxxx:xxxxxx:xxxxx:".
+ lines with "#" at the front are ignored.
+*************************************************************************/
+int getfileline(void *vp, char *linebuf, int linebuf_size)
+{
+ /* Static buffers we will return. */
+ FILE *fp = (FILE *)vp;
+ unsigned char c;
+ unsigned char *p;
+ size_t linebuf_len;
+
+ if (fp == NULL)
+ {
+ DEBUG(0,("getfileline: Bad file pointer.\n"));
+ return -1;
+ }
+
+ /*
+ * Scan the file, a line at a time.
+ */
+ while (!feof(fp))
+ {
+ linebuf[0] = '\0';
+
+ fgets(linebuf, linebuf_size, fp);
+ if (ferror(fp))
+ {
+ return -1;
+ }
+
+ /*
+ * Check if the string is terminated with a newline - if not
+ * then we must keep reading and discard until we get one.
+ */
+
+ linebuf_len = strlen(linebuf);
+ if (linebuf_len == 0)
+ {
+ linebuf[0] = '\0';
+ return 0;
+ }
+
+ if (linebuf[linebuf_len - 1] != '\n')
+ {
+ c = '\0';
+ while (!ferror(fp) && !feof(fp))
+ {
+ c = fgetc(fp);
+ if (c == '\n')
+ {
+ break;
+ }
+ }
+ }
+ else
+ {
+ linebuf[linebuf_len - 1] = '\0';
+ }
+
+#ifdef DEBUG_PASSWORD
+ DEBUG(100, ("getfileline: got line |%s|\n", linebuf));
+#endif
+ if ((linebuf[0] == 0) && feof(fp))
+ {
+ DEBUG(4, ("getfileline: end of file reached\n"));
+ return 0;
+ }
+
+ if (linebuf[0] == '#' || linebuf[0] == '\0')
+ {
+ DEBUG(6, ("getfileline: skipping comment or blank line\n"));
+ continue;
+ }
+
+ p = (unsigned char *) strchr_m(linebuf, ':');
+ if (p == NULL)
+ {
+ DEBUG(0, ("getfileline: malformed line entry (no :)\n"));
+ continue;
+ }
+ return linebuf_len;
+ }
+ return -1;
+}
+
+
+/****************************************************************************
+read a line from a file with possible \ continuation chars.
+Blanks at the start or end of a line are stripped.
+The string will be allocated if s2 is NULL
+****************************************************************************/
+char *fgets_slash(char *s2,int maxlen,XFILE *f)
+{
+ char *s=s2;
+ int len = 0;
+ int c;
+ BOOL start_of_line = True;
+
+ if (x_feof(f))
+ return(NULL);
+
+ if (maxlen <2) return(NULL);
+
+ if (!s2)
+ {
+ maxlen = MIN(maxlen,8);
+ s = (char *)malloc(maxlen);
+ }
+
+ if (!s) return(NULL);
+
+ *s = 0;
+
+ while (len < maxlen-1)
+ {
+ c = x_getc(f);
+ switch (c)
+ {
+ case '\r':
+ break;
+ case '\n':
+ while (len > 0 && s[len-1] == ' ')
+ {
+ s[--len] = 0;
+ }
+ if (len > 0 && s[len-1] == '\\')
+ {
+ s[--len] = 0;
+ start_of_line = True;
+ break;
+ }
+ return(s);
+ case EOF:
+ if (len <= 0 && !s2)
+ SAFE_FREE(s);
+ return(len>0?s:NULL);
+ case ' ':
+ if (start_of_line)
+ break;
+ default:
+ start_of_line = False;
+ s[len++] = c;
+ s[len] = 0;
+ }
+ if (!s2 && len > maxlen-3)
+ {
+ char *t;
+
+ maxlen *= 2;
+ t = (char *)Realloc(s,maxlen);
+ if (!t) {
+ DEBUG(0,("fgets_slash: failed to expand buffer!\n"));
+ SAFE_FREE(s);
+ return(NULL);
+ } else s = t;
+ }
+ }
+ return(s);
+}
+
+
+/****************************************************************************
+load from a pipe into memory
+****************************************************************************/
+char *file_pload(char *syscmd, size_t *size)
+{
+ int fd, n;
+ char *p, *tp;
+ pstring buf;
+ size_t total;
+
+ fd = sys_popen(syscmd);
+ if (fd == -1) return NULL;
+
+ p = NULL;
+ total = 0;
+
+ while ((n = read(fd, buf, sizeof(buf))) > 0) {
+ tp = Realloc(p, total + n + 1);
+ if (!tp) {
+ DEBUG(0,("file_pload: failed to expand buffer!\n"));
+ close(fd);
+ SAFE_FREE(p);
+ return NULL;
+ } else p = tp;
+ memcpy(p+total, buf, n);
+ total += n;
+ }
+ if (p) p[total] = 0;
+
+ /* FIXME: Perhaps ought to check that the command completed
+ * successfully (returned 0); if not the data may be
+ * truncated. */
+ sys_pclose(fd);
+
+ if (size) *size = total;
+
+ return p;
+}
+
+/****************************************************************************
+load a file into memory from a fd.
+****************************************************************************/
+
+char *fd_load(int fd, size_t *size)
+{
+ SMB_STRUCT_STAT sbuf;
+ char *p;
+
+ if (sys_fstat(fd, &sbuf) != 0) return NULL;
+
+ p = (char *)malloc(sbuf.st_size+1);
+ if (!p) return NULL;
+
+ if (read(fd, p, sbuf.st_size) != sbuf.st_size) {
+ SAFE_FREE(p);
+ return NULL;
+ }
+ p[sbuf.st_size] = 0;
+
+ if (size) *size = sbuf.st_size;
+
+ return p;
+}
+
+/****************************************************************************
+load a file into memory
+****************************************************************************/
+char *file_load(const char *fname, size_t *size)
+{
+ int fd;
+ char *p;
+
+ if (!fname || !*fname) return NULL;
+
+ fd = open(fname,O_RDONLY);
+ if (fd == -1) return NULL;
+
+ p = fd_load(fd, size);
+
+ close(fd);
+
+ return p;
+}
+
+
+/*******************************************************************
+mmap (if possible) or read a file
+********************************************************************/
+void *map_file(char *fname, size_t size)
+{
+ size_t s2 = 0;
+ void *p = NULL;
+#ifdef HAVE_MMAP
+ if (lp_use_mmap()) {
+ int fd;
+ fd = open(fname, O_RDONLY, 0);
+ if (fd == -1) {
+ DEBUG(2,("Failed to load %s - %s\n", fname, strerror(errno)));
+ return NULL;
+ }
+ p = mmap(NULL, size, PROT_READ, MAP_SHARED|MAP_FILE, fd, 0);
+ close(fd);
+ if (p == MAP_FAILED) {
+ DEBUG(1,("Failed to mmap %s - %s\n", fname, strerror(errno)));
+ return NULL;
+ }
+ }
+#endif
+ if (!p) {
+ p = file_load(fname, &s2);
+ if (!p) return NULL;
+ if (s2 != size) {
+ DEBUG(1,("incorrect size for %s - got %lu expected %lu\n",
+ fname, (unsigned long)s2, (unsigned long)size));
+ if (p) free(p);
+ return NULL;
+ }
+ }
+
+ return p;
+}
+
+
+/****************************************************************************
+parse a buffer into lines
+****************************************************************************/
+static char **file_lines_parse(char *p, size_t size, int *numlines)
+{
+ int i;
+ char *s, **ret;
+
+ if (!p) return NULL;
+
+ for (s = p, i=0; s < p+size; s++) {
+ if (s[0] == '\n') i++;
+ }
+
+ ret = (char **)malloc(sizeof(ret[0])*(i+2));
+ if (!ret) {
+ SAFE_FREE(p);
+ return NULL;
+ }
+ memset(ret, 0, sizeof(ret[0])*(i+2));
+ if (numlines) *numlines = i;
+
+ ret[0] = p;
+ for (s = p, i=0; s < p+size; s++) {
+ if (s[0] == '\n') {
+ s[0] = 0;
+ i++;
+ ret[i] = s+1;
+ }
+ if (s[0] == '\r') s[0] = 0;
+ }
+
+ return ret;
+}
+
+
+/****************************************************************************
+load a file into memory and return an array of pointers to lines in the file
+must be freed with file_lines_free().
+****************************************************************************/
+char **file_lines_load(const char *fname, int *numlines)
+{
+ char *p;
+ size_t size;
+
+ p = file_load(fname, &size);
+ if (!p) return NULL;
+
+ return file_lines_parse(p, size, numlines);
+}
+
+/****************************************************************************
+load a fd into memory and return an array of pointers to lines in the file
+must be freed with file_lines_free(). If convert is true calls unix_to_dos on
+the list.
+****************************************************************************/
+char **fd_lines_load(int fd, int *numlines)
+{
+ char *p;
+ size_t size;
+
+ p = fd_load(fd, &size);
+ if (!p) return NULL;
+
+ return file_lines_parse(p, size, numlines);
+}
+
+
+/****************************************************************************
+load a pipe into memory and return an array of pointers to lines in the data
+must be freed with file_lines_free().
+****************************************************************************/
+char **file_lines_pload(char *syscmd, int *numlines)
+{
+ char *p;
+ size_t size;
+
+ p = file_pload(syscmd, &size);
+ if (!p) return NULL;
+
+ return file_lines_parse(p, size, numlines);
+}
+
+/****************************************************************************
+free lines loaded with file_lines_load
+****************************************************************************/
+void file_lines_free(char **lines)
+{
+ if (!lines) return;
+ SAFE_FREE(lines[0]);
+ SAFE_FREE(lines);
+}
+
+
+/****************************************************************************
+take a lislist of lines and modify them to produce a list where \ continues
+a line
+****************************************************************************/
+void file_lines_slashcont(char **lines)
+{
+ int i, j;
+
+ for (i=0; lines[i];) {
+ int len = strlen(lines[i]);
+ if (lines[i][len-1] == '\\') {
+ lines[i][len-1] = ' ';
+ if (lines[i+1]) {
+ char *p = &lines[i][len];
+ while (p < lines[i+1]) *p++ = ' ';
+ for (j = i+1; lines[j]; j++) lines[j] = lines[j+1];
+ }
+ } else {
+ i++;
+ }
+ }
+}
+
+/*
+ save a lump of data into a file. Mostly used for debugging
+*/
+BOOL file_save(const char *fname, void *packet, size_t length)
+{
+ int fd;
+ fd = open(fname, O_WRONLY|O_CREAT|O_TRUNC, 0644);
+ if (fd == -1) {
+ return False;
+ }
+ if (write(fd, packet, length) != (size_t)length) {
+ return False;
+ }
+ close(fd);
+ return True;
+}
diff --git a/source/lib/util_getent.c b/source/lib/util_getent.c
new file mode 100644
index 00000000000..3544c1678cc
--- /dev/null
+++ b/source/lib/util_getent.c
@@ -0,0 +1,306 @@
+/*
+ Unix SMB/CIFS implementation.
+ Samba utility functions
+ Copyright (C) Simo Sorce 2001
+ Copyright (C) Jeremy Allison 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 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"
+
+
+/****************************************************************
+ Returns a single linked list of group entries.
+ Use grent_free() to free it after use.
+****************************************************************/
+
+struct sys_grent * getgrent_list(void)
+{
+ struct sys_grent *glist;
+ struct sys_grent *gent;
+ struct group *grp;
+
+ gent = (struct sys_grent *) malloc(sizeof(struct sys_grent));
+ if (gent == NULL) {
+ DEBUG (0, ("Out of memory in getgrent_list!\n"));
+ return NULL;
+ }
+ memset(gent, '\0', sizeof(struct sys_grent));
+ glist = gent;
+
+ setgrent();
+ grp = getgrent();
+ if (grp == NULL) {
+ endgrent();
+ SAFE_FREE(glist);
+ return NULL;
+ }
+
+ while (grp != NULL) {
+ int i,num;
+
+ if (grp->gr_name) {
+ if ((gent->gr_name = strdup(grp->gr_name)) == NULL)
+ goto err;
+ }
+ if (grp->gr_passwd) {
+ if ((gent->gr_passwd = strdup(grp->gr_passwd)) == NULL)
+ goto err;
+ }
+ gent->gr_gid = grp->gr_gid;
+
+ /* number of strings in gr_mem */
+ for (num = 0; grp->gr_mem[num]; num++)
+ ;
+
+ /* alloc space for gr_mem string pointers */
+ if ((gent->gr_mem = (char **) malloc((num+1) * sizeof(char *))) == NULL)
+ goto err;
+
+ memset(gent->gr_mem, '\0', (num+1) * sizeof(char *));
+
+ for (i=0; i < num; i++) {
+ if ((gent->gr_mem[i] = strdup(grp->gr_mem[i])) == NULL)
+ goto err;
+ }
+ gent->gr_mem[num] = NULL;
+
+ grp = getgrent();
+ if (grp) {
+ gent->next = (struct sys_grent *) malloc(sizeof(struct sys_grent));
+ if (gent->next == NULL)
+ goto err;
+ gent = gent->next;
+ memset(gent, '\0', sizeof(struct sys_grent));
+ }
+ }
+
+ endgrent();
+ return glist;
+
+ err:
+
+ endgrent();
+ DEBUG(0, ("Out of memory in getgrent_list!\n"));
+ grent_free(glist);
+ return NULL;
+}
+
+/****************************************************************
+ Free the single linked list of group entries made by
+ getgrent_list()
+****************************************************************/
+
+void grent_free (struct sys_grent *glist)
+{
+ while (glist) {
+ struct sys_grent *prev;
+
+ SAFE_FREE(glist->gr_name);
+ SAFE_FREE(glist->gr_passwd);
+ if (glist->gr_mem) {
+ int i;
+ for (i = 0; glist->gr_mem[i]; i++)
+ SAFE_FREE(glist->gr_mem[i]);
+ SAFE_FREE(glist->gr_mem);
+ }
+ prev = glist;
+ glist = glist->next;
+ SAFE_FREE(prev);
+ }
+}
+
+/****************************************************************
+ Returns a single linked list of passwd entries.
+ Use pwent_free() to free it after use.
+****************************************************************/
+
+struct sys_pwent * getpwent_list(void)
+{
+ struct sys_pwent *plist;
+ struct sys_pwent *pent;
+ struct passwd *pwd;
+
+ pent = (struct sys_pwent *) malloc(sizeof(struct sys_pwent));
+ if (pent == NULL) {
+ DEBUG (0, ("Out of memory in getpwent_list!\n"));
+ return NULL;
+ }
+ plist = pent;
+
+ setpwent();
+ pwd = getpwent();
+ while (pwd != NULL) {
+ memset(pent, '\0', sizeof(struct sys_pwent));
+ if (pwd->pw_name) {
+ if ((pent->pw_name = strdup(pwd->pw_name)) == NULL)
+ goto err;
+ }
+ if (pwd->pw_passwd) {
+ if ((pent->pw_passwd = strdup(pwd->pw_passwd)) == NULL)
+ goto err;
+ }
+ pent->pw_uid = pwd->pw_uid;
+ pent->pw_gid = pwd->pw_gid;
+ if (pwd->pw_gecos) {
+ if ((pent->pw_gecos = strdup(pwd->pw_gecos)) == NULL)
+ goto err;
+ }
+ if (pwd->pw_dir) {
+ if ((pent->pw_dir = strdup(pwd->pw_dir)) == NULL)
+ goto err;
+ }
+ if (pwd->pw_shell) {
+ if ((pent->pw_shell = strdup(pwd->pw_shell)) == NULL)
+ goto err;
+ }
+
+ pwd = getpwent();
+ if (pwd) {
+ pent->next = (struct sys_pwent *) malloc(sizeof(struct sys_pwent));
+ if (pent->next == NULL)
+ goto err;
+ pent = pent->next;
+ }
+ }
+
+ endpwent();
+ return plist;
+
+ err:
+
+ endpwent();
+ DEBUG(0, ("Out of memory in getpwent_list!\n"));
+ pwent_free(plist);
+ return NULL;
+}
+
+/****************************************************************
+ Free the single linked list of passwd entries made by
+ getpwent_list()
+****************************************************************/
+
+void pwent_free (struct sys_pwent *plist)
+{
+ while (plist) {
+ struct sys_pwent *prev;
+
+ SAFE_FREE(plist->pw_name);
+ SAFE_FREE(plist->pw_passwd);
+ SAFE_FREE(plist->pw_gecos);
+ SAFE_FREE(plist->pw_dir);
+ SAFE_FREE(plist->pw_shell);
+
+ prev = plist;
+ plist = plist->next;
+ SAFE_FREE(prev);
+ }
+}
+
+/****************************************************************
+ Add the individual group users onto the list.
+****************************************************************/
+
+static struct sys_userlist *add_members_to_userlist(struct sys_userlist *list_head, const struct group *grp)
+{
+ size_t num_users, i;
+
+ /* Count the number of users. */
+ for (num_users = 0; grp->gr_mem[num_users]; num_users++)
+ ;
+
+ for (i = 0; i < num_users; i++) {
+ struct sys_userlist *entry = (struct sys_userlist *)malloc(sizeof(*entry));
+ if (entry == NULL) {
+ free_userlist(list_head);
+ return NULL;
+ }
+ entry->unix_name = (char *)strdup(grp->gr_mem[i]);
+ if (entry->unix_name == NULL) {
+ SAFE_FREE(entry);
+ free_userlist(list_head);
+ return NULL;
+ }
+ DLIST_ADD(list_head, entry);
+ }
+ return list_head;
+}
+
+/****************************************************************
+ Get the list of UNIX users in a group.
+ We have to enumerate the /etc/group file as some UNIX getgrnam()
+ calls won't do that for us (notably Tru64 UNIX).
+****************************************************************/
+
+struct sys_userlist *get_users_in_group(const char *gname)
+{
+ struct sys_userlist *list_head = NULL;
+ struct group *gptr;
+ fstring domain;
+ fstring groupname;
+ DOM_SID sid;
+ enum SID_NAME_USE name_type;
+
+ /* No point using winbind if we can't split it in the
+ first place */
+ if (split_domain_and_name(gname, domain, groupname)) {
+
+ /*
+ * If we're doing this via winbindd, don't do the
+ * entire group list enumeration as we know this is
+ * pointless (and slow).
+ */
+
+ if (winbind_lookup_name(domain, groupname, &sid, &name_type)
+ && name_type == SID_NAME_DOM_GRP) {
+ if ((gptr = (struct group *)getgrnam(gname)) == NULL)
+ return NULL;
+ return add_members_to_userlist(list_head, gptr);
+ }
+ }
+
+#if !defined(BROKEN_GETGRNAM)
+ if ((gptr = (struct group *)getgrnam(gname)) == NULL)
+ return NULL;
+ return add_members_to_userlist(list_head, gptr);
+#else
+ /* BROKEN_GETGRNAM - True64 */
+ setgrent();
+ while((gptr = getgrent()) != NULL) {
+ if (strequal(gname, gptr->gr_name)) {
+ list_head = add_members_to_userlist(list_head, gptr);
+ if (list_head == NULL)
+ return NULL;
+ }
+ }
+ endgrent();
+ return list_head;
+#endif
+}
+
+/****************************************************************
+ Free list allocated above.
+****************************************************************/
+
+void free_userlist(struct sys_userlist *list_head)
+{
+ while (list_head) {
+ struct sys_userlist *old_head = list_head;
+ DLIST_REMOVE(list_head, list_head);
+ SAFE_FREE(old_head->unix_name);
+ SAFE_FREE(old_head);
+ }
+}
diff --git a/source/lib/util_pw.c b/source/lib/util_pw.c
new file mode 100644
index 00000000000..9d075a05e88
--- /dev/null
+++ b/source/lib/util_pw.c
@@ -0,0 +1,89 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ Safe versions of getpw* calls
+
+ 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 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"
+
+static struct passwd *alloc_copy_passwd(const struct passwd *from)
+{
+ struct passwd *ret = smb_xmalloc(sizeof(struct passwd));
+ ZERO_STRUCTP(ret);
+ ret->pw_name = smb_xstrdup(from->pw_name);
+ ret->pw_passwd = smb_xstrdup(from->pw_passwd);
+ ret->pw_uid = from->pw_uid;
+ ret->pw_gid = from->pw_gid;
+ ret->pw_gecos = smb_xstrdup(from->pw_gecos);
+ ret->pw_dir = smb_xstrdup(from->pw_dir);
+ ret->pw_shell = smb_xstrdup(from->pw_shell);
+ return ret;
+}
+
+void passwd_free (struct passwd **buf)
+{
+ if (!*buf) {
+ DEBUG(0, ("attempted double-free of allocated passwd\n"));
+ return;
+ }
+
+ SAFE_FREE((*buf)->pw_name);
+ SAFE_FREE((*buf)->pw_passwd);
+ SAFE_FREE((*buf)->pw_gecos);
+ SAFE_FREE((*buf)->pw_dir);
+ SAFE_FREE((*buf)->pw_shell);
+
+ SAFE_FREE(*buf);
+}
+
+struct passwd *getpwnam_alloc(const char *name)
+{
+ struct passwd *temp;
+
+ temp = sys_getpwnam(name);
+
+ if (!temp) {
+#if 0
+ if (errno == ENOMEM) {
+ /* what now? */
+ }
+#endif
+ return NULL;
+ }
+
+ return alloc_copy_passwd(temp);
+}
+
+struct passwd *getpwuid_alloc(uid_t uid)
+{
+ struct passwd *temp;
+
+ temp = sys_getpwuid(uid);
+
+ if (!temp) {
+#if 0
+ if (errno == ENOMEM) {
+ /* what now? */
+ }
+#endif
+ return NULL;
+ }
+
+ return alloc_copy_passwd(temp);
+}
diff --git a/source/lib/util_seaccess.c b/source/lib/util_seaccess.c
new file mode 100644
index 00000000000..cb0f46e2f9d
--- /dev/null
+++ b/source/lib/util_seaccess.c
@@ -0,0 +1,357 @@
+/*
+ Unix SMB/CIFS implementation.
+ Copyright (C) Luke Kenneth Casson Leighton 1996-2000.
+ Copyright (C) Tim Potter 2000.
+ Copyright (C) Re-written by Jeremy Allison 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 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"
+
+extern DOM_SID global_sid_Builtin;
+
+/*********************************************************************************
+ Check an ACE against a SID. We return the remaining needed permission
+ bits not yet granted. Zero means permission allowed (no more needed bits).
+**********************************************************************************/
+
+static uint32 check_ace(SEC_ACE *ace, const NT_USER_TOKEN *token, uint32 acc_desired,
+ NTSTATUS *status)
+{
+ uint32 mask = ace->info.mask;
+
+ /*
+ * Inherit only is ignored.
+ */
+
+ if (ace->flags & SEC_ACE_FLAG_INHERIT_ONLY) {
+ return acc_desired;
+ }
+
+ /*
+ * If this ACE has no SID in common with the token,
+ * ignore it as it cannot be used to make an access
+ * determination.
+ */
+
+ if (!token_sid_in_ace( token, ace))
+ return acc_desired;
+
+ switch (ace->type) {
+ case SEC_ACE_TYPE_ACCESS_ALLOWED:
+ /*
+ * This is explicitly allowed.
+ * Remove the bits from the remaining
+ * access required. Return the remaining
+ * bits needed.
+ */
+ acc_desired &= ~mask;
+ break;
+ case SEC_ACE_TYPE_ACCESS_DENIED:
+ /*
+ * This is explicitly denied.
+ * If any bits match terminate here,
+ * we are denied.
+ */
+ if (acc_desired & mask) {
+ *status = NT_STATUS_ACCESS_DENIED;
+ return 0xFFFFFFFF;
+ }
+ break;
+ case SEC_ACE_TYPE_SYSTEM_ALARM:
+ case SEC_ACE_TYPE_SYSTEM_AUDIT:
+ *status = NT_STATUS_NOT_IMPLEMENTED;
+ return 0xFFFFFFFF;
+ default:
+ *status = NT_STATUS_INVALID_PARAMETER;
+ return 0xFFFFFFFF;
+ }
+
+ return acc_desired;
+}
+
+/*********************************************************************************
+ Maximum access was requested. Calculate the max possible. Fail if it doesn't
+ include other bits requested.
+**********************************************************************************/
+
+static BOOL get_max_access( SEC_ACL *the_acl, const NT_USER_TOKEN *token, uint32 *granted,
+ uint32 desired,
+ NTSTATUS *status)
+{
+ uint32 acc_denied = 0;
+ uint32 acc_granted = 0;
+ size_t i;
+
+ for ( i = 0 ; i < the_acl->num_aces; i++) {
+ SEC_ACE *ace = &the_acl->ace[i];
+ uint32 mask = ace->info.mask;
+
+ if (!token_sid_in_ace( token, ace))
+ continue;
+
+ switch (ace->type) {
+ case SEC_ACE_TYPE_ACCESS_ALLOWED:
+ acc_granted |= (mask & ~acc_denied);
+ break;
+ case SEC_ACE_TYPE_ACCESS_DENIED:
+ acc_denied |= (mask & ~acc_granted);
+ break;
+ case SEC_ACE_TYPE_SYSTEM_ALARM:
+ case SEC_ACE_TYPE_SYSTEM_AUDIT:
+ *status = NT_STATUS_NOT_IMPLEMENTED;
+ *granted = 0;
+ return False;
+ default:
+ *status = NT_STATUS_INVALID_PARAMETER;
+ *granted = 0;
+ return False;
+ }
+ }
+
+ /*
+ * If we were granted no access, or we desired bits that we
+ * didn't get, then deny.
+ */
+
+ if ((acc_granted == 0) || ((acc_granted & desired) != desired)) {
+ *status = NT_STATUS_ACCESS_DENIED;
+ *granted = 0;
+ return False;
+ }
+
+ /*
+ * Return the access we did get.
+ */
+
+ *granted = acc_granted;
+ *status = NT_STATUS_OK;
+ return True;
+}
+
+/* Map generic access rights to object specific rights. This technique is
+ used to give meaning to assigning read, write, execute and all access to
+ objects. Each type of object has its own mapping of generic to object
+ specific access rights. */
+
+void se_map_generic(uint32 *access_mask, struct generic_mapping *mapping)
+{
+ uint32 old_mask = *access_mask;
+
+ if (*access_mask & GENERIC_READ_ACCESS) {
+ *access_mask &= ~GENERIC_READ_ACCESS;
+ *access_mask |= mapping->generic_read;
+ }
+
+ if (*access_mask & GENERIC_WRITE_ACCESS) {
+ *access_mask &= ~GENERIC_WRITE_ACCESS;
+ *access_mask |= mapping->generic_write;
+ }
+
+ if (*access_mask & GENERIC_EXECUTE_ACCESS) {
+ *access_mask &= ~GENERIC_EXECUTE_ACCESS;
+ *access_mask |= mapping->generic_execute;
+ }
+
+ if (*access_mask & GENERIC_ALL_ACCESS) {
+ *access_mask &= ~GENERIC_ALL_ACCESS;
+ *access_mask |= mapping->generic_all;
+ }
+
+ if (old_mask != *access_mask) {
+ DEBUG(10, ("se_map_generic(): mapped mask 0x%08x to 0x%08x\n",
+ old_mask, *access_mask));
+ }
+}
+
+/* Map standard access rights to object specific rights. This technique is
+ used to give meaning to assigning read, write, execute and all access to
+ objects. Each type of object has its own mapping of standard to object
+ specific access rights. */
+
+void se_map_standard(uint32 *access_mask, struct standard_mapping *mapping)
+{
+ uint32 old_mask = *access_mask;
+
+ if (*access_mask & READ_CONTROL_ACCESS) {
+ *access_mask &= ~READ_CONTROL_ACCESS;
+ *access_mask |= mapping->std_read;
+ }
+
+ if (*access_mask & (DELETE_ACCESS|WRITE_DAC_ACCESS|WRITE_OWNER_ACCESS|SYNCHRONIZE_ACCESS)) {
+ *access_mask &= ~(DELETE_ACCESS|WRITE_DAC_ACCESS|WRITE_OWNER_ACCESS|SYNCHRONIZE_ACCESS);
+ *access_mask |= mapping->std_all;
+ }
+
+ if (old_mask != *access_mask) {
+ DEBUG(10, ("se_map_standard(): mapped mask 0x%08x to 0x%08x\n",
+ old_mask, *access_mask));
+ }
+}
+
+/*****************************************************************************
+ Check access rights of a user against a security descriptor. Look at
+ each ACE in the security descriptor until an access denied ACE denies
+ any of the desired rights to the user or any of the users groups, or one
+ or more ACEs explicitly grant all requested access rights. See
+ "Access-Checking" document in MSDN.
+*****************************************************************************/
+
+BOOL se_access_check(const SEC_DESC *sd, const NT_USER_TOKEN *token,
+ uint32 acc_desired, uint32 *acc_granted,
+ NTSTATUS *status)
+{
+ extern NT_USER_TOKEN anonymous_token;
+ size_t i;
+ SEC_ACL *the_acl;
+ fstring sid_str;
+ uint32 tmp_acc_desired = acc_desired;
+
+ if (!status || !acc_granted)
+ return False;
+
+ if (!token)
+ token = &anonymous_token;
+
+ *status = NT_STATUS_OK;
+ *acc_granted = 0;
+
+ DEBUG(10,("se_access_check: requested access 0x%08x, for NT token with %u entries and first sid %s.\n",
+ (unsigned int)acc_desired, (unsigned int)token->num_sids,
+ sid_to_string(sid_str, &token->user_sids[0])));
+
+ /*
+ * No security descriptor or security descriptor with no DACL
+ * present allows all access.
+ */
+
+ /* ACL must have something in it */
+
+ if (!sd || (sd && (!(sd->type & SEC_DESC_DACL_PRESENT) || sd->dacl == NULL))) {
+ *status = NT_STATUS_OK;
+ *acc_granted = acc_desired;
+ DEBUG(5, ("se_access_check: no sd or blank DACL, access allowed\n"));
+ return True;
+ }
+
+ /* The user sid is the first in the token */
+ if (DEBUGLVL(3)) {
+ DEBUG(3, ("se_access_check: user sid is %s\n", sid_to_string(sid_str, &token->user_sids[PRIMARY_USER_SID_INDEX]) ));
+
+ for (i = 1; i < token->num_sids; i++) {
+ DEBUGADD(3, ("se_access_check: also %s\n",
+ sid_to_string(sid_str, &token->user_sids[i])));
+ }
+ }
+
+ /* Is the token the owner of the SID ? */
+
+ if (sd->owner_sid) {
+ for (i = 0; i < token->num_sids; i++) {
+ if (sid_equal(&token->user_sids[i], sd->owner_sid)) {
+ /*
+ * The owner always has SEC_RIGHTS_WRITE_DAC & READ_CONTROL.
+ */
+ if (tmp_acc_desired & WRITE_DAC_ACCESS)
+ tmp_acc_desired &= ~WRITE_DAC_ACCESS;
+ if (tmp_acc_desired & READ_CONTROL_ACCESS)
+ tmp_acc_desired &= ~READ_CONTROL_ACCESS;
+ }
+ }
+ }
+
+ the_acl = sd->dacl;
+
+ if (tmp_acc_desired & MAXIMUM_ALLOWED_ACCESS) {
+ tmp_acc_desired &= ~MAXIMUM_ALLOWED_ACCESS;
+ return get_max_access( the_acl, token, acc_granted, tmp_acc_desired,
+ status);
+ }
+
+ for ( i = 0 ; i < the_acl->num_aces && tmp_acc_desired != 0; i++) {
+ SEC_ACE *ace = &the_acl->ace[i];
+
+ DEBUGADD(10,("se_access_check: ACE %u: type %d, flags = 0x%02x, SID = %s mask = %x, current desired = %x\n",
+ (unsigned int)i, ace->type, ace->flags,
+ sid_to_string(sid_str, &ace->trustee),
+ (unsigned int) ace->info.mask,
+ (unsigned int)tmp_acc_desired ));
+
+ tmp_acc_desired = check_ace( ace, token, tmp_acc_desired, status);
+ if (NT_STATUS_V(*status)) {
+ *acc_granted = 0;
+ DEBUG(5,("se_access_check: ACE %u denied with status %s.\n", (unsigned int)i, nt_errstr(*status)));
+ return False;
+ }
+ }
+
+ /*
+ * If there are no more desired permissions left then
+ * access was allowed.
+ */
+
+ if (tmp_acc_desired == 0) {
+ *acc_granted = acc_desired;
+ *status = NT_STATUS_OK;
+ DEBUG(5,("se_access_check: access (%x) granted.\n", (unsigned int)acc_desired ));
+ return True;
+ }
+
+ *acc_granted = 0;
+ *status = NT_STATUS_ACCESS_DENIED;
+ DEBUG(5,("se_access_check: access (%x) denied.\n", (unsigned int)acc_desired ));
+ return False;
+}
+
+
+/*******************************************************************
+ samr_make_sam_obj_sd
+ ********************************************************************/
+
+NTSTATUS samr_make_sam_obj_sd(TALLOC_CTX *ctx, SEC_DESC **psd, size_t *sd_size)
+{
+ extern DOM_SID global_sid_World;
+ DOM_SID adm_sid;
+ DOM_SID act_sid;
+
+ SEC_ACE ace[3];
+ SEC_ACCESS mask;
+
+ SEC_ACL *psa = NULL;
+
+ sid_copy(&adm_sid, &global_sid_Builtin);
+ sid_append_rid(&adm_sid, BUILTIN_ALIAS_RID_ADMINS);
+
+ sid_copy(&act_sid, &global_sid_Builtin);
+ sid_append_rid(&act_sid, BUILTIN_ALIAS_RID_ACCOUNT_OPS);
+
+ /*basic access for every one*/
+ init_sec_access(&mask, GENERIC_RIGHTS_SAM_EXECUTE | GENERIC_RIGHTS_SAM_READ);
+ init_sec_ace(&ace[0], &global_sid_World, SEC_ACE_TYPE_ACCESS_ALLOWED, mask, 0);
+
+ /*full access for builtin aliases Administrators and Account Operators*/
+ init_sec_access(&mask, GENERIC_RIGHTS_SAM_ALL_ACCESS);
+ init_sec_ace(&ace[1], &adm_sid, SEC_ACE_TYPE_ACCESS_ALLOWED, mask, 0);
+ init_sec_ace(&ace[2], &act_sid, SEC_ACE_TYPE_ACCESS_ALLOWED, mask, 0);
+
+ if ((psa = make_sec_acl(ctx, NT4_ACL_REVISION, 3, ace)) == NULL)
+ return NT_STATUS_NO_MEMORY;
+
+ if ((*psd = make_sec_desc(ctx, SEC_DESC_REVISION, SEC_DESC_SELF_RELATIVE, NULL, NULL, NULL, psa, sd_size)) == NULL)
+ return NT_STATUS_NO_MEMORY;
+
+ return NT_STATUS_OK;
+}
diff --git a/source/lib/util_sec.c b/source/lib/util_sec.c
new file mode 100644
index 00000000000..26be27ea515
--- /dev/null
+++ b/source/lib/util_sec.c
@@ -0,0 +1,467 @@
+/*
+ Unix SMB/CIFS implementation.
+ Copyright (C) Jeremy Allison 1998.
+ rewritten for version 2.0.6 by Tridge
+
+ This program is free software; you can 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 AUTOCONF_TEST
+#include "includes.h"
+#else
+/* we are running this code in autoconf test mode to see which type of setuid
+ function works */
+#if defined(HAVE_UNISTD_H)
+#include <unistd.h>
+#endif
+#include <stdlib.h>
+#include <stdio.h>
+#include <sys/types.h>
+#include <errno.h>
+
+#ifdef HAVE_SYS_PRIV_H
+#include <sys/priv.h>
+#endif
+#ifdef HAVE_SYS_ID_H
+#include <sys/id.h>
+#endif
+
+#define DEBUG(x, y) printf y
+#define smb_panic(x) exit(1)
+#define BOOL int
+#endif
+
+/* are we running as non-root? This is used by the regresison test code,
+ and potentially also for sites that want non-root smbd */
+static uid_t initial_uid;
+static gid_t initial_gid;
+
+/****************************************************************************
+remember what uid we got started as - this allows us to run correctly
+as non-root while catching trapdoor systems
+****************************************************************************/
+void sec_init(void)
+{
+ initial_uid = geteuid();
+ initial_gid = getegid();
+}
+
+/****************************************************************************
+some code (eg. winbindd) needs to know what uid we started as
+****************************************************************************/
+uid_t sec_initial_uid(void)
+{
+ return initial_uid;
+}
+
+/****************************************************************************
+some code (eg. winbindd, profiling shm) needs to know what gid we started as
+****************************************************************************/
+gid_t sec_initial_gid(void)
+{
+ return initial_gid;
+}
+
+/****************************************************************************
+are we running in non-root mode?
+****************************************************************************/
+BOOL non_root_mode(void)
+{
+ return (initial_uid != (uid_t)0);
+}
+
+/****************************************************************************
+abort if we haven't set the uid correctly
+****************************************************************************/
+static void assert_uid(uid_t ruid, uid_t euid)
+{
+ if ((euid != (uid_t)-1 && geteuid() != euid) ||
+ (ruid != (uid_t)-1 && getuid() != ruid)) {
+ if (!non_root_mode()) {
+ DEBUG(0,("Failed to set uid privileges to (%d,%d) now set to (%d,%d)\n",
+ (int)ruid, (int)euid,
+ (int)getuid(), (int)geteuid()));
+ smb_panic("failed to set uid\n");
+ exit(1);
+ }
+ }
+}
+
+/****************************************************************************
+abort if we haven't set the gid correctly
+****************************************************************************/
+static void assert_gid(gid_t rgid, gid_t egid)
+{
+ if ((egid != (gid_t)-1 && getegid() != egid) ||
+ (rgid != (gid_t)-1 && getgid() != rgid)) {
+ if (!non_root_mode()) {
+ DEBUG(0,("Failed to set gid privileges to (%d,%d) now set to (%d,%d) uid=(%d,%d)\n",
+ (int)rgid, (int)egid,
+ (int)getgid(), (int)getegid(),
+ (int)getuid(), (int)geteuid()));
+ smb_panic("failed to set gid\n");
+ exit(1);
+ }
+ }
+}
+
+/****************************************************************************
+ Gain root privilege before doing something.
+ We want to end up with ruid==euid==0
+****************************************************************************/
+void gain_root_privilege(void)
+{
+#if USE_SETRESUID
+ setresuid(0,0,0);
+#endif
+
+#if USE_SETEUID
+ seteuid(0);
+#endif
+
+#if USE_SETREUID
+ setreuid(0, 0);
+#endif
+
+#if USE_SETUIDX
+ setuidx(ID_EFFECTIVE, 0);
+ setuidx(ID_REAL, 0);
+#endif
+
+ /* this is needed on some systems */
+ setuid(0);
+
+ assert_uid(0, 0);
+}
+
+
+/****************************************************************************
+ Ensure our real and effective groups are zero.
+ we want to end up with rgid==egid==0
+****************************************************************************/
+void gain_root_group_privilege(void)
+{
+#if USE_SETRESUID
+ setresgid(0,0,0);
+#endif
+
+#if USE_SETREUID
+ setregid(0,0);
+#endif
+
+#if USE_SETEUID
+ setegid(0);
+#endif
+
+#if USE_SETUIDX
+ setgidx(ID_EFFECTIVE, 0);
+ setgidx(ID_REAL, 0);
+#endif
+
+ setgid(0);
+
+ assert_gid(0, 0);
+}
+
+
+/****************************************************************************
+ Set effective uid, and possibly the real uid too.
+ We want to end up with either:
+
+ ruid==uid and euid==uid
+
+ or
+
+ ruid==0 and euid==uid
+
+ depending on what the local OS will allow us to regain root from.
+****************************************************************************/
+void set_effective_uid(uid_t uid)
+{
+#if USE_SETRESUID
+ /* Set the effective as well as the real uid. */
+ setresuid(uid,uid,-1);
+#endif
+
+#if USE_SETREUID
+ setreuid(-1,uid);
+#endif
+
+#if USE_SETEUID
+ seteuid(uid);
+#endif
+
+#if USE_SETUIDX
+ setuidx(ID_EFFECTIVE, uid);
+#endif
+
+ assert_uid(-1, uid);
+}
+
+/****************************************************************************
+ Set *only* the effective gid.
+ we want to end up with rgid==0 and egid==gid
+****************************************************************************/
+void set_effective_gid(gid_t gid)
+{
+#if USE_SETRESUID
+ setresgid(-1,gid,-1);
+#endif
+
+#if USE_SETREUID
+ setregid(-1,gid);
+#endif
+
+#if USE_SETEUID
+ setegid(gid);
+#endif
+
+#if USE_SETUIDX
+ setgidx(ID_EFFECTIVE, gid);
+#endif
+
+ assert_gid(-1, gid);
+}
+
+static uid_t saved_euid, saved_ruid;
+static gid_t saved_egid, saved_rgid;
+
+/****************************************************************************
+ save the real and effective uid for later restoration. Used by the quotas
+ code
+****************************************************************************/
+void save_re_uid(void)
+{
+ saved_ruid = getuid();
+ saved_euid = geteuid();
+}
+
+
+/****************************************************************************
+ and restore them!
+****************************************************************************/
+void restore_re_uid(void)
+{
+ set_effective_uid(0);
+
+#if USE_SETRESUID
+ setresuid(saved_ruid, saved_euid, -1);
+#elif USE_SETREUID
+ setreuid(saved_ruid, -1);
+ setreuid(-1,saved_euid);
+#elif USE_SETUIDX
+ setuidx(ID_REAL, saved_ruid);
+ setuidx(ID_EFFECTIVE, saved_euid);
+#else
+ set_effective_uid(saved_euid);
+ if (getuid() != saved_ruid)
+ setuid(saved_ruid);
+ set_effective_uid(saved_euid);
+#endif
+
+ assert_uid(saved_ruid, saved_euid);
+}
+
+
+/****************************************************************************
+ save the real and effective gid for later restoration. Used by the
+ getgroups code
+****************************************************************************/
+void save_re_gid(void)
+{
+ saved_rgid = getgid();
+ saved_egid = getegid();
+}
+
+/****************************************************************************
+ and restore them!
+****************************************************************************/
+void restore_re_gid(void)
+{
+#if USE_SETRESUID
+ setresgid(saved_rgid, saved_egid, -1);
+#elif USE_SETREUID
+ setregid(saved_rgid, -1);
+ setregid(-1,saved_egid);
+#elif USE_SETUIDX
+ setgidx(ID_REAL, saved_rgid);
+ setgidx(ID_EFFECTIVE, saved_egid);
+#else
+ set_effective_gid(saved_egid);
+ if (getgid() != saved_rgid)
+ setgid(saved_rgid);
+ set_effective_gid(saved_egid);
+#endif
+
+ assert_gid(saved_rgid, saved_egid);
+}
+
+
+/****************************************************************************
+ set the real AND effective uid to the current effective uid in a way that
+ allows root to be regained.
+ This is only possible on some platforms.
+****************************************************************************/
+int set_re_uid(void)
+{
+ uid_t uid = geteuid();
+
+#if USE_SETRESUID
+ setresuid(geteuid(), -1, -1);
+#endif
+
+#if USE_SETREUID
+ setreuid(0, 0);
+ setreuid(uid, -1);
+ setreuid(-1, uid);
+#endif
+
+#if USE_SETEUID
+ /* can't be done */
+ return -1;
+#endif
+
+#if USE_SETUIDX
+ /* can't be done */
+ return -1;
+#endif
+
+ assert_uid(uid, uid);
+ return 0;
+}
+
+
+/****************************************************************************
+ Become the specified uid and gid - permanently !
+ there should be no way back if possible
+****************************************************************************/
+void become_user_permanently(uid_t uid, gid_t gid)
+{
+ /*
+ * First - gain root privilege. We do this to ensure
+ * we can lose it again.
+ */
+
+ gain_root_privilege();
+ gain_root_group_privilege();
+
+#if USE_SETRESUID
+ setresgid(gid,gid,gid);
+ setgid(gid);
+ setresuid(uid,uid,uid);
+ setuid(uid);
+#endif
+
+#if USE_SETREUID
+ setregid(gid,gid);
+ setgid(gid);
+ setreuid(uid,uid);
+ setuid(uid);
+#endif
+
+#if USE_SETEUID
+ setegid(gid);
+ setgid(gid);
+ setuid(uid);
+ seteuid(uid);
+ setuid(uid);
+#endif
+
+#if USE_SETUIDX
+ setgidx(ID_REAL, gid);
+ setgidx(ID_EFFECTIVE, gid);
+ setgid(gid);
+ setuidx(ID_REAL, uid);
+ setuidx(ID_EFFECTIVE, uid);
+ setuid(uid);
+#endif
+
+ assert_uid(uid, uid);
+ assert_gid(gid, gid);
+}
+
+#ifdef AUTOCONF_TEST
+
+/****************************************************************************
+this function just checks that we don't get ENOSYS back
+****************************************************************************/
+static int have_syscall(void)
+{
+ errno = 0;
+
+#if USE_SETRESUID
+ setresuid(-1,-1,-1);
+#endif
+
+#if USE_SETREUID
+ setreuid(-1,-1);
+#endif
+
+#if USE_SETEUID
+ seteuid(-1);
+#endif
+
+#if USE_SETUIDX
+ setuidx(ID_EFFECTIVE, -1);
+#endif
+
+ if (errno == ENOSYS) return -1;
+
+ return 0;
+}
+
+main()
+{
+ if (getuid() != 0) {
+#if (defined(AIX) && defined(USE_SETREUID))
+ /* setreuid is badly broken on AIX 4.1, we avoid it completely */
+ fprintf(stderr,"avoiding possibly broken setreuid\n");
+ exit(1);
+#endif
+
+ /* if not running as root then at least check to see if we get ENOSYS - this
+ handles Linux 2.0.x with glibc 2.1 */
+ fprintf(stderr,"not running as root: checking for ENOSYS\n");
+ exit(have_syscall());
+ }
+
+ gain_root_privilege();
+ gain_root_group_privilege();
+ set_effective_gid(1);
+ set_effective_uid(1);
+ save_re_uid();
+ restore_re_uid();
+ gain_root_privilege();
+ gain_root_group_privilege();
+ become_user_permanently(1, 1);
+ setuid(0);
+ if (getuid() == 0) {
+ fprintf(stderr,"uid not set permanently\n");
+ exit(1);
+ }
+
+ printf("OK\n");
+
+ exit(0);
+}
+#endif
+
+/****************************************************************************
+Check if we are setuid root. Used in libsmb and smbpasswd paranoia checks.
+****************************************************************************/
+BOOL is_setuid_root(void)
+{
+ return (geteuid() == (uid_t)0) && (getuid() != (uid_t)0);
+}
diff --git a/source/lib/util_sid.c b/source/lib/util_sid.c
new file mode 100644
index 00000000000..2c0bd797859
--- /dev/null
+++ b/source/lib/util_sid.c
@@ -0,0 +1,636 @@
+/*
+ Unix SMB/CIFS implementation.
+ Samba utility functions
+ Copyright (C) Andrew Tridgell 1992-1998
+ Copyright (C) Luke Kenneth Caseson Leighton 1998-1999
+ Copyright (C) Jeremy Allison 1999
+ Copyright (C) Stefan (metze) Metzmacher 2002
+ Copyright (C) Simo Sorce 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 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"
+
+/*
+ * Some useful sids
+ */
+
+DOM_SID global_sid_World_Domain; /* Everyone domain */
+DOM_SID global_sid_World; /* Everyone */
+DOM_SID global_sid_Creator_Owner_Domain; /* Creator Owner domain */
+DOM_SID global_sid_NT_Authority; /* NT Authority */
+DOM_SID global_sid_System; /* System */
+DOM_SID global_sid_NULL; /* NULL sid */
+DOM_SID global_sid_Authenticated_Users; /* All authenticated rids */
+DOM_SID global_sid_Network; /* Network rids */
+
+DOM_SID global_sid_Creator_Owner; /* Creator Owner */
+DOM_SID global_sid_Creator_Group; /* Creator Group */
+DOM_SID global_sid_Anonymous; /* Anonymous login */
+
+DOM_SID global_sid_Builtin; /* Local well-known domain */
+DOM_SID global_sid_Builtin_Administrators; /* Builtin administrators */
+DOM_SID global_sid_Builtin_Users; /* Builtin users */
+DOM_SID global_sid_Builtin_Guests; /* Builtin guest users */
+DOM_SID global_sid_Builtin_Power_Users; /* Builtin power users */
+DOM_SID global_sid_Builtin_Account_Operators; /* Builtin account operators */
+DOM_SID global_sid_Builtin_Server_Operators; /* Builtin server operators */
+DOM_SID global_sid_Builtin_Print_Operators; /* Builtin print operators */
+DOM_SID global_sid_Builtin_Backup_Operators; /* Builtin backup operators */
+DOM_SID global_sid_Builtin_Replicator; /* Builtin replicator */
+
+#define SECURITY_NULL_SID_AUTHORITY 0
+#define SECURITY_WORLD_SID_AUTHORITY 1
+#define SECURITY_LOCAL_SID_AUTHORITY 2
+#define SECURITY_CREATOR_SID_AUTHORITY 3
+#define SECURITY_NT_AUTHORITY 5
+
+/*
+ * An NT compatible anonymous token.
+ */
+
+static DOM_SID anon_sid_array[3];
+
+NT_USER_TOKEN anonymous_token = {
+ 3,
+ anon_sid_array
+};
+
+static DOM_SID system_sid_array[4];
+NT_USER_TOKEN system_token = {
+ 1,
+ system_sid_array
+};
+
+/****************************************************************************
+ Lookup string names for SID types.
+****************************************************************************/
+
+static const struct {
+ enum SID_NAME_USE sid_type;
+ const char *string;
+} sid_name_type[] = {
+ {SID_NAME_USER, "User"},
+ {SID_NAME_DOM_GRP, "Domain Group"},
+ {SID_NAME_DOMAIN, "Domain"},
+ {SID_NAME_ALIAS, "Local Group"},
+ {SID_NAME_WKN_GRP, "Well-known Group"},
+ {SID_NAME_DELETED, "Deleted Account"},
+ {SID_NAME_INVALID, "Invalid Account"},
+ {SID_NAME_UNKNOWN, "UNKNOWN"},
+ {SID_NAME_COMPUTER, "Computer"},
+
+ {(enum SID_NAME_USE)0, NULL}
+};
+
+const char *sid_type_lookup(uint32 sid_type)
+{
+ int i = 0;
+
+ /* Look through list */
+ while(sid_name_type[i].sid_type != 0) {
+ if (sid_name_type[i].sid_type == sid_type)
+ return sid_name_type[i].string;
+ i++;
+ }
+
+ /* Default return */
+ return "SID *TYPE* is INVALID";
+}
+
+/****************************************************************************
+ Creates some useful well known sids
+****************************************************************************/
+
+void generate_wellknown_sids(void)
+{
+ static BOOL initialised = False;
+
+ if (initialised)
+ return;
+
+ /* SECURITY_NULL_SID_AUTHORITY */
+ string_to_sid(&global_sid_NULL, "S-1-0-0");
+
+ /* SECURITY_WORLD_SID_AUTHORITY */
+ string_to_sid(&global_sid_World_Domain, "S-1-1");
+ string_to_sid(&global_sid_World, "S-1-1-0");
+
+ /* SECURITY_CREATOR_SID_AUTHORITY */
+ string_to_sid(&global_sid_Creator_Owner_Domain, "S-1-3");
+ string_to_sid(&global_sid_Creator_Owner, "S-1-3-0");
+ string_to_sid(&global_sid_Creator_Group, "S-1-3-1");
+
+ /* SECURITY_NT_AUTHORITY */
+ string_to_sid(&global_sid_NT_Authority, "S-1-5");
+ string_to_sid(&global_sid_Network, "S-1-5-2");
+ string_to_sid(&global_sid_Anonymous, "S-1-5-7");
+ string_to_sid(&global_sid_Authenticated_Users, "S-1-5-11");
+ string_to_sid(&global_sid_System, "S-1-5-18");
+
+ /* SECURITY_BUILTIN_DOMAIN_RID */
+ string_to_sid(&global_sid_Builtin, "S-1-5-32");
+ string_to_sid(&global_sid_Builtin_Administrators, "S-1-5-32-544");
+ string_to_sid(&global_sid_Builtin_Users, "S-1-5-32-545");
+ string_to_sid(&global_sid_Builtin_Guests, "S-1-5-32-546");
+ string_to_sid(&global_sid_Builtin_Power_Users, "S-1-5-32-547");
+ string_to_sid(&global_sid_Builtin_Account_Operators, "S-1-5-32-548");
+ string_to_sid(&global_sid_Builtin_Server_Operators, "S-1-5-32-549");
+ string_to_sid(&global_sid_Builtin_Print_Operators, "S-1-5-32-550");
+ string_to_sid(&global_sid_Builtin_Backup_Operators, "S-1-5-32-551");
+ string_to_sid(&global_sid_Builtin_Replicator, "S-1-5-32-552");
+
+ /* Create the anon token. */
+ sid_copy( &anonymous_token.user_sids[0], &global_sid_World);
+ sid_copy( &anonymous_token.user_sids[1], &global_sid_Network);
+ sid_copy( &anonymous_token.user_sids[2], &global_sid_Anonymous);
+
+ /* Create the system token. */
+ sid_copy( &system_token.user_sids[0], &global_sid_System);
+
+ initialised = True;
+}
+
+/**************************************************************************
+ Create the SYSTEM token.
+***************************************************************************/
+
+NT_USER_TOKEN *get_system_token(void)
+{
+ generate_wellknown_sids(); /* The token is initialised here */
+ return &system_token;
+}
+
+/**************************************************************************
+ Splits a name of format \DOMAIN\name or name into its two components.
+ Sets the DOMAIN name to global_myname() if it has not been specified.
+***************************************************************************/
+
+void split_domain_name(const char *fullname, char *domain, char *name)
+{
+ pstring full_name;
+ const char *sep;
+ char *p;
+
+ sep = lp_winbind_separator();
+
+ *domain = *name = '\0';
+
+ if (fullname[0] == sep[0] || fullname[0] == '\\')
+ fullname++;
+
+ pstrcpy(full_name, fullname);
+ p = strchr_m(full_name+1, '\\');
+ if (!p) p = strchr_m(full_name+1, sep[0]);
+
+ if (p != NULL) {
+ *p = 0;
+ fstrcpy(domain, full_name);
+ fstrcpy(name, p+1);
+ } else {
+ fstrcpy(domain, global_myname());
+ fstrcpy(name, full_name);
+ }
+
+ DEBUG(10,("split_domain_name:name '%s' split into domain :'%s' and user :'%s'\n",
+ fullname, domain, name));
+}
+
+/****************************************************************************
+ Test if a SID is wellknown and resolvable.
+****************************************************************************/
+
+BOOL resolvable_wellknown_sid(DOM_SID *sid)
+{
+ uint32 ia = (sid->id_auth[5]) +
+ (sid->id_auth[4] << 8 ) +
+ (sid->id_auth[3] << 16) +
+ (sid->id_auth[2] << 24);
+
+ if (sid->sid_rev_num != SEC_DESC_REVISION || sid->num_auths < 1)
+ return False;
+
+ return (ia == SECURITY_WORLD_SID_AUTHORITY ||
+ ia == SECURITY_CREATOR_SID_AUTHORITY);
+}
+
+/*****************************************************************
+ Convert a SID to an ascii string.
+*****************************************************************/
+
+char *sid_to_string(fstring sidstr_out, const DOM_SID *sid)
+{
+ char subauth[16];
+ int i;
+ uint32 ia;
+
+ if (!sid) {
+ fstrcpy(sidstr_out, "(NULL SID)");
+ return sidstr_out;
+ }
+
+ /*
+ * BIG NOTE: this function only does SIDS where the identauth is not >= 2^32
+ * in a range of 2^48.
+ */
+ ia = (sid->id_auth[5]) +
+ (sid->id_auth[4] << 8 ) +
+ (sid->id_auth[3] << 16) +
+ (sid->id_auth[2] << 24);
+
+ slprintf(sidstr_out, sizeof(fstring) - 1, "S-%u-%lu", (unsigned int)sid->sid_rev_num, (unsigned long)ia);
+
+ for (i = 0; i < sid->num_auths; i++) {
+ slprintf(subauth, sizeof(subauth)-1, "-%lu", (unsigned long)sid->sub_auths[i]);
+ fstrcat(sidstr_out, subauth);
+ }
+
+ return sidstr_out;
+}
+
+/*****************************************************************
+ Useful function for debug lines.
+*****************************************************************/
+
+const char *sid_string_static(const DOM_SID *sid)
+{
+ static fstring sid_str;
+ sid_to_string(sid_str, sid);
+ return sid_str;
+}
+
+/*****************************************************************
+ Convert a string to a SID. Returns True on success, False on fail.
+*****************************************************************/
+
+BOOL string_to_sid(DOM_SID *sidout, const char *sidstr)
+{
+ pstring tok;
+ char *q;
+ const char *p;
+ /* BIG NOTE: this function only does SIDS where the identauth is not >= 2^32 */
+ uint32 ia;
+
+ if (StrnCaseCmp( sidstr, "S-", 2)) {
+ DEBUG(0,("string_to_sid: Sid %s does not start with 'S-'.\n", sidstr));
+ return False;
+ }
+
+ memset((char *)sidout, '\0', sizeof(DOM_SID));
+
+ p = q = strdup(sidstr + 2);
+ if (p == NULL) {
+ DEBUG(0, ("string_to_sid: out of memory!\n"));
+ return False;
+ }
+
+ if (!next_token(&p, tok, "-", sizeof(tok))) {
+ DEBUG(0,("string_to_sid: Sid %s is not in a valid format.\n", sidstr));
+ SAFE_FREE(q);
+ return False;
+ }
+
+ /* Get the revision number. */
+ sidout->sid_rev_num = (uint8)strtoul(tok, NULL, 10);
+
+ if (!next_token(&p, tok, "-", sizeof(tok))) {
+ DEBUG(0,("string_to_sid: Sid %s is not in a valid format.\n", sidstr));
+ SAFE_FREE(q);
+ return False;
+ }
+
+ /* identauth in decimal should be < 2^32 */
+ ia = (uint32)strtoul(tok, NULL, 10);
+
+ /* NOTE - the ia value is in big-endian format. */
+ sidout->id_auth[0] = 0;
+ sidout->id_auth[1] = 0;
+ sidout->id_auth[2] = (ia & 0xff000000) >> 24;
+ sidout->id_auth[3] = (ia & 0x00ff0000) >> 16;
+ sidout->id_auth[4] = (ia & 0x0000ff00) >> 8;
+ sidout->id_auth[5] = (ia & 0x000000ff);
+
+ sidout->num_auths = 0;
+
+ while(next_token(&p, tok, "-", sizeof(tok)) &&
+ sidout->num_auths < MAXSUBAUTHS) {
+ /*
+ * NOTE - the subauths are in native machine-endian format. They
+ * are converted to little-endian when linearized onto the wire.
+ */
+ sid_append_rid(sidout, (uint32)strtoul(tok, NULL, 10));
+ }
+
+ SAFE_FREE(q);
+ return True;
+}
+
+/*****************************************************************
+ Add a rid to the end of a sid
+*****************************************************************/
+
+BOOL sid_append_rid(DOM_SID *sid, uint32 rid)
+{
+ if (sid->num_auths < MAXSUBAUTHS) {
+ sid->sub_auths[sid->num_auths++] = rid;
+ return True;
+ }
+ return False;
+}
+
+/*****************************************************************
+ Removes the last rid from the end of a sid
+*****************************************************************/
+
+BOOL sid_split_rid(DOM_SID *sid, uint32 *rid)
+{
+ if (sid->num_auths > 0) {
+ sid->num_auths--;
+ *rid = sid->sub_auths[sid->num_auths];
+ return True;
+ }
+ return False;
+}
+
+/*****************************************************************
+ Return the last rid from the end of a sid
+*****************************************************************/
+
+BOOL sid_peek_rid(const DOM_SID *sid, uint32 *rid)
+{
+ if (!sid || !rid)
+ return False;
+
+ if (sid->num_auths > 0) {
+ *rid = sid->sub_auths[sid->num_auths - 1];
+ return True;
+ }
+ return False;
+}
+
+/*****************************************************************
+ Return the last rid from the end of a sid
+ and check the sid against the exp_dom_sid
+*****************************************************************/
+
+BOOL sid_peek_check_rid(const DOM_SID *exp_dom_sid, const DOM_SID *sid, uint32 *rid)
+{
+ if (!exp_dom_sid || !sid || !rid)
+ return False;
+
+ if (sid->num_auths != (exp_dom_sid->num_auths+1)) {
+ return False;
+ }
+
+ if (sid_compare_domain(exp_dom_sid, sid)!=0){
+ *rid=(-1);
+ return False;
+ }
+
+ return sid_peek_rid(sid, rid);
+}
+
+/*****************************************************************
+ Copies a sid
+*****************************************************************/
+
+void sid_copy(DOM_SID *dst, const DOM_SID *src)
+{
+ int i;
+
+ ZERO_STRUCTP(dst);
+
+ dst->sid_rev_num = src->sid_rev_num;
+ dst->num_auths = src->num_auths;
+
+ memcpy(&dst->id_auth[0], &src->id_auth[0], sizeof(src->id_auth));
+
+ for (i = 0; i < src->num_auths; i++)
+ dst->sub_auths[i] = src->sub_auths[i];
+}
+
+/*****************************************************************
+ Write a sid out into on-the-wire format.
+*****************************************************************/
+
+BOOL sid_linearize(char *outbuf, size_t len, const DOM_SID *sid)
+{
+ size_t i;
+
+ if (len < sid_size(sid))
+ return False;
+
+ SCVAL(outbuf,0,sid->sid_rev_num);
+ SCVAL(outbuf,1,sid->num_auths);
+ memcpy(&outbuf[2], sid->id_auth, 6);
+ for(i = 0; i < sid->num_auths; i++)
+ SIVAL(outbuf, 8 + (i*4), sid->sub_auths[i]);
+
+ return True;
+}
+
+/*****************************************************************
+ Parse a on-the-wire SID to a DOM_SID.
+*****************************************************************/
+
+BOOL sid_parse(const char *inbuf, size_t len, DOM_SID *sid)
+{
+ int i;
+ if (len < 8)
+ return False;
+
+ ZERO_STRUCTP(sid);
+
+ sid->sid_rev_num = CVAL(inbuf, 0);
+ sid->num_auths = CVAL(inbuf, 1);
+ memcpy(sid->id_auth, inbuf+2, 6);
+ if (len < 8 + sid->num_auths*4)
+ return False;
+ for (i=0;i<sid->num_auths;i++)
+ sid->sub_auths[i] = IVAL(inbuf, 8+i*4);
+ return True;
+}
+
+/*****************************************************************
+ Compare the auth portion of two sids.
+*****************************************************************/
+
+static int sid_compare_auth(const DOM_SID *sid1, const DOM_SID *sid2)
+{
+ int i;
+
+ if (sid1 == sid2)
+ return 0;
+ if (!sid1)
+ return -1;
+ if (!sid2)
+ return 1;
+
+ if (sid1->sid_rev_num != sid2->sid_rev_num)
+ return sid1->sid_rev_num - sid2->sid_rev_num;
+
+ for (i = 0; i < 6; i++)
+ if (sid1->id_auth[i] != sid2->id_auth[i])
+ return sid1->id_auth[i] - sid2->id_auth[i];
+
+ return 0;
+}
+
+/*****************************************************************
+ Compare two sids.
+*****************************************************************/
+
+int sid_compare(const DOM_SID *sid1, const DOM_SID *sid2)
+{
+ int i;
+
+ if (sid1 == sid2)
+ return 0;
+ if (!sid1)
+ return -1;
+ if (!sid2)
+ return 1;
+
+ /* Compare most likely different rids, first: i.e start at end */
+ if (sid1->num_auths != sid2->num_auths)
+ return sid1->num_auths - sid2->num_auths;
+
+ for (i = sid1->num_auths-1; i >= 0; --i)
+ if (sid1->sub_auths[i] != sid2->sub_auths[i])
+ return sid1->sub_auths[i] - sid2->sub_auths[i];
+
+ return sid_compare_auth(sid1, sid2);
+}
+
+/*****************************************************************
+ See if 2 SIDs are in the same domain
+ this just compares the leading sub-auths
+*****************************************************************/
+
+int sid_compare_domain(const DOM_SID *sid1, const DOM_SID *sid2)
+{
+ int n, i;
+
+ n = MIN(sid1->num_auths, sid2->num_auths);
+
+ for (i = n-1; i >= 0; --i)
+ if (sid1->sub_auths[i] != sid2->sub_auths[i])
+ return sid1->sub_auths[i] - sid2->sub_auths[i];
+
+ return sid_compare_auth(sid1, sid2);
+}
+
+/*****************************************************************
+ Compare two sids.
+*****************************************************************/
+
+BOOL sid_equal(const DOM_SID *sid1, const DOM_SID *sid2)
+{
+ return sid_compare(sid1, sid2) == 0;
+}
+
+/*****************************************************************
+ Check if the SID is the builtin SID (S-1-5-32).
+*****************************************************************/
+
+BOOL sid_check_is_builtin(const DOM_SID *sid)
+{
+ return sid_equal(sid, &global_sid_Builtin);
+}
+
+/*****************************************************************
+ Check if the SID is one of the builtin SIDs (S-1-5-32-a).
+*****************************************************************/
+
+BOOL sid_check_is_in_builtin(const DOM_SID *sid)
+{
+ DOM_SID dom_sid;
+ uint32 rid;
+
+ sid_copy(&dom_sid, sid);
+ sid_split_rid(&dom_sid, &rid);
+
+ return sid_equal(&dom_sid, &global_sid_Builtin);
+}
+
+/*****************************************************************
+ Calculates size of a sid.
+*****************************************************************/
+
+size_t sid_size(const DOM_SID *sid)
+{
+ if (sid == NULL)
+ return 0;
+
+ return sid->num_auths * sizeof(uint32) + 8;
+}
+
+/*****************************************************************
+ Returns true if SID is internal (and non-mappable).
+*****************************************************************/
+
+BOOL non_mappable_sid(DOM_SID *sid)
+{
+ DOM_SID dom;
+ uint32 rid;
+
+ sid_copy(&dom, sid);
+ sid_split_rid(&dom, &rid);
+
+ if (sid_equal(&dom, &global_sid_Builtin))
+ return True;
+
+ if (sid_equal(&dom, &global_sid_NT_Authority))
+ return True;
+
+ return False;
+}
+
+/*****************************************************************
+ Return the binary string representation of a DOM_SID.
+ Caller must free.
+*****************************************************************/
+
+char *sid_binstring(const DOM_SID *sid)
+{
+ char *buf, *s;
+ int len = sid_size(sid);
+ buf = malloc(len);
+ if (!buf)
+ return NULL;
+ sid_linearize(buf, len, sid);
+ s = binary_string(buf, len);
+ free(buf);
+ return s;
+}
+
+/*******************************************************************
+ Tallocs a duplicate SID.
+********************************************************************/
+
+DOM_SID *sid_dup_talloc(TALLOC_CTX *ctx, const DOM_SID *src)
+{
+ DOM_SID *dst;
+
+ if(!src)
+ return NULL;
+
+ if((dst = talloc_zero(ctx, sizeof(DOM_SID))) != NULL) {
+ sid_copy( dst, src);
+ }
+
+ return dst;
+}
diff --git a/source/lib/util_smbd.c b/source/lib/util_smbd.c
new file mode 100644
index 00000000000..071f20b4162
--- /dev/null
+++ b/source/lib/util_smbd.c
@@ -0,0 +1,65 @@
+/*
+ Unix SMB/CIFS implementation.
+ Samba utility functions, used in smbd only
+ 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 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"
+
+/*
+ This function requires sys_getgrouplist - which is only
+ available in smbd due to it's use of become_root() in a
+ legacy systems hack.
+*/
+
+/*
+ return a full list of groups for a user
+
+ returns the number of groups the user is a member of. The return will include the
+ users primary group.
+
+ remember to free the resulting gid_t array
+
+ NOTE! uses become_root() to gain correct priviages on systems
+ that lack a native getgroups() call (uses initgroups and getgroups)
+*/
+int getgroups_user(const char *user, gid_t **groups)
+{
+ struct passwd *pwd;
+ int ngrp, max_grp;
+
+ pwd = getpwnam_alloc(user);
+ if (!pwd) return -1;
+
+ max_grp = groups_max();
+ (*groups) = (gid_t *)malloc(sizeof(gid_t) * max_grp);
+ if (! *groups) {
+ passwd_free(&pwd);
+ errno = ENOMEM;
+ return -1;
+ }
+
+ ngrp = sys_getgrouplist(user, pwd->pw_gid, *groups, &max_grp);
+ if (ngrp <= 0) {
+ passwd_free(&pwd);
+ free(*groups);
+ return ngrp;
+ }
+
+ passwd_free(&pwd);
+ return ngrp;
+}
diff --git a/source/lib/util_sock.c b/source/lib/util_sock.c
new file mode 100644
index 00000000000..845aaa4b13a
--- /dev/null
+++ b/source/lib/util_sock.c
@@ -0,0 +1,1078 @@
+/*
+ Unix SMB/CIFS implementation.
+ Samba utility functions
+ Copyright (C) Andrew Tridgell 1992-1998
+ Copyright (C) Tim Potter 2000-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 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"
+
+/* the last IP received from */
+struct in_addr lastip;
+
+/* the last port received from */
+int lastport=0;
+
+int smb_read_error = 0;
+
+static char *get_socket_addr(int fd)
+{
+ struct sockaddr sa;
+ struct sockaddr_in *sockin = (struct sockaddr_in *) (&sa);
+ int length = sizeof(sa);
+ static fstring addr_buf;
+
+ fstrcpy(addr_buf,"0.0.0.0");
+
+ if (fd == -1) {
+ return addr_buf;
+ }
+
+ if (getsockname(fd, &sa, &length) < 0) {
+ DEBUG(0,("getpeername failed. Error was %s\n", strerror(errno) ));
+ return addr_buf;
+ }
+
+ fstrcpy(addr_buf,(char *)inet_ntoa(sockin->sin_addr));
+
+ return addr_buf;
+}
+
+/****************************************************************************
+ Determine if a file descriptor is in fact a socket.
+****************************************************************************/
+
+BOOL is_a_socket(int fd)
+{
+ int v,l;
+ l = sizeof(int);
+ return(getsockopt(fd, SOL_SOCKET, SO_TYPE, (char *)&v, &l) == 0);
+}
+
+enum SOCK_OPT_TYPES {OPT_BOOL,OPT_INT,OPT_ON};
+
+typedef struct smb_socket_option {
+ const char *name;
+ int level;
+ int option;
+ int value;
+ int opttype;
+} smb_socket_option;
+
+static const smb_socket_option 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}};
+
+/****************************************************************************
+ Print socket options.
+****************************************************************************/
+
+static void print_socket_options(int s)
+{
+ int value, vlen = 4;
+ const smb_socket_option *p = &socket_options[0];
+
+ /* wrapped in if statement to prevent streams leak in SCO Openserver 5.0 */
+ /* reported on samba-technical --jerry */
+ if ( DEBUGLEVEL >= 5 ) {
+ for (; p->name != NULL; p++) {
+ if (getsockopt(s, p->level, p->option, (void *)&value, &vlen) == -1) {
+ DEBUG(5,("Could not test socket option %s.\n", p->name));
+ } else {
+ DEBUG(5,("socket option %s = %d\n",p->name,value));
+ }
+ }
+ }
+ }
+
+/****************************************************************************
+ Set user socket options.
+****************************************************************************/
+
+void set_socket_options(int fd, const char *options)
+{
+ fstring tok;
+
+ while (next_token(&options,tok," \t,", sizeof(tok))) {
+ int ret=0,i;
+ int value = 1;
+ char *p;
+ BOOL got_value = False;
+
+ if ((p = strchr_m(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) ));
+ }
+
+ print_socket_options(fd);
+}
+
+/****************************************************************************
+ Read from a socket.
+****************************************************************************/
+
+ssize_t read_udp_socket(int fd,char *buf,size_t len)
+{
+ ssize_t ret;
+ struct sockaddr_in sock;
+ socklen_t socklen = sizeof(sock);
+
+ memset((char *)&sock,'\0',socklen);
+ memset((char *)&lastip,'\0',sizeof(lastip));
+ ret = (ssize_t)sys_recvfrom(fd,buf,len,0,(struct sockaddr *)&sock,&socklen);
+ if (ret <= 0) {
+ DEBUG(2,("read socket failed. ERRNO=%s\n",strerror(errno)));
+ return(0);
+ }
+
+ lastip = sock.sin_addr;
+ lastport = ntohs(sock.sin_port);
+
+ DEBUG(10,("read_udp_socket: lastip %s lastport %d read: %lu\n",
+ inet_ntoa(lastip), lastport, (unsigned long)ret));
+
+ return(ret);
+}
+
+/****************************************************************************
+ Read data from a socket with a timout in msec.
+ mincount = if timeout, minimum to read before returning
+ maxcount = number to be read.
+ time_out = timeout in milliseconds
+****************************************************************************/
+
+ssize_t read_socket_with_timeout(int fd,char *buf,size_t mincnt,size_t maxcnt,unsigned int time_out)
+{
+ fd_set fds;
+ int selrtn;
+ ssize_t readret;
+ size_t nread = 0;
+ struct timeval timeout;
+
+ /* just checking .... */
+ if (maxcnt <= 0)
+ return(0);
+
+ smb_read_error = 0;
+
+ /* Blocking read */
+ if (time_out <= 0) {
+ if (mincnt == 0) mincnt = maxcnt;
+
+ while (nread < mincnt) {
+ readret = sys_read(fd, buf + nread, maxcnt - nread);
+
+ if (readret == 0) {
+ DEBUG(5,("read_socket_with_timeout: blocking read. EOF from client.\n"));
+ smb_read_error = READ_EOF;
+ return -1;
+ }
+
+ if (readret == -1) {
+ DEBUG(0,("read_socket_with_timeout: read error = %s.\n", strerror(errno) ));
+ smb_read_error = READ_ERROR;
+ return -1;
+ }
+ nread += readret;
+ }
+ return((ssize_t)nread);
+ }
+
+ /* Most difficult - timeout read */
+ /* If this is ever called on a disk file and
+ mincnt is greater then the filesize then
+ system performance will suffer severely as
+ select always returns true on disk files */
+
+ /* Set initial timeout */
+ timeout.tv_sec = (time_t)(time_out / 1000);
+ timeout.tv_usec = (long)(1000 * (time_out % 1000));
+
+ for (nread=0; nread < mincnt; ) {
+ FD_ZERO(&fds);
+ FD_SET(fd,&fds);
+
+ selrtn = sys_select_intr(fd+1,&fds,NULL,NULL,&timeout);
+
+ /* Check if error */
+ if (selrtn == -1) {
+ /* something is wrong. Maybe the socket is dead? */
+ DEBUG(0,("read_socket_with_timeout: timeout read. select error = %s.\n", strerror(errno) ));
+ smb_read_error = READ_ERROR;
+ return -1;
+ }
+
+ /* Did we timeout ? */
+ if (selrtn == 0) {
+ DEBUG(10,("read_socket_with_timeout: timeout read. select timed out.\n"));
+ smb_read_error = READ_TIMEOUT;
+ return -1;
+ }
+
+ readret = sys_read(fd, buf+nread, maxcnt-nread);
+
+ if (readret == 0) {
+ /* we got EOF on the file descriptor */
+ DEBUG(5,("read_socket_with_timeout: timeout read. EOF from client.\n"));
+ smb_read_error = READ_EOF;
+ return -1;
+ }
+
+ if (readret == -1) {
+ /* the descriptor is probably dead */
+ DEBUG(0,("read_socket_with_timeout: timeout read. read error = %s.\n", strerror(errno) ));
+ smb_read_error = READ_ERROR;
+ return -1;
+ }
+
+ nread += readret;
+ }
+
+ /* Return the number we got */
+ return (ssize_t)nread;
+}
+
+/****************************************************************************
+ Read data from the client, reading exactly N bytes.
+****************************************************************************/
+
+ssize_t read_data(int fd,char *buffer,size_t N)
+{
+ ssize_t ret;
+ size_t total=0;
+
+ smb_read_error = 0;
+
+ while (total < N) {
+ ret = sys_read(fd,buffer + total,N - total);
+
+ if (ret == 0) {
+ DEBUG(10,("read_data: read of %d returned 0. Error = %s\n", (int)(N - total), strerror(errno) ));
+ smb_read_error = READ_EOF;
+ return 0;
+ }
+
+ if (ret == -1) {
+ DEBUG(0,("read_data: read failure for %d. Error = %s\n", (int)(N - total), strerror(errno) ));
+ smb_read_error = READ_ERROR;
+ return -1;
+ }
+ total += ret;
+ }
+ return (ssize_t)total;
+}
+
+/****************************************************************************
+ Read data from a socket, reading exactly N bytes.
+****************************************************************************/
+
+static ssize_t read_socket_data(int fd,char *buffer,size_t N)
+{
+ ssize_t ret;
+ size_t total=0;
+
+ smb_read_error = 0;
+
+ while (total < N) {
+ ret = sys_read(fd,buffer + total,N - total);
+
+ if (ret == 0) {
+ DEBUG(10,("read_socket_data: recv of %d returned 0. Error = %s\n", (int)(N - total), strerror(errno) ));
+ smb_read_error = READ_EOF;
+ return 0;
+ }
+
+ if (ret == -1) {
+ DEBUG(0,("read_socket_data: recv failure for %d. Error = %s\n", (int)(N - total), strerror(errno) ));
+ smb_read_error = READ_ERROR;
+ return -1;
+ }
+ total += ret;
+ }
+ return (ssize_t)total;
+}
+
+/****************************************************************************
+ Write data to a fd.
+****************************************************************************/
+
+ssize_t write_data(int fd,char *buffer,size_t N)
+{
+ size_t total=0;
+ ssize_t ret;
+
+ while (total < N) {
+ ret = sys_write(fd,buffer + total,N - total);
+
+ if (ret == -1) {
+ DEBUG(0,("write_data: write failure. Error = %s\n", strerror(errno) ));
+ return -1;
+ }
+ if (ret == 0)
+ return total;
+
+ total += ret;
+ }
+ return (ssize_t)total;
+}
+
+/****************************************************************************
+ Write data to a socket - use send rather than write.
+****************************************************************************/
+
+static ssize_t write_socket_data(int fd,char *buffer,size_t N)
+{
+ size_t total=0;
+ ssize_t ret;
+
+ while (total < N) {
+ ret = sys_send(fd,buffer + total,N - total,0);
+
+ if (ret == -1) {
+ DEBUG(0,("write_socket_data: write failure. Error = %s\n", strerror(errno) ));
+ return -1;
+ }
+ if (ret == 0)
+ return total;
+
+ total += ret;
+ }
+ return (ssize_t)total;
+}
+
+/****************************************************************************
+ Write to a socket.
+****************************************************************************/
+
+ssize_t write_socket(int fd,char *buf,size_t len)
+{
+ ssize_t ret=0;
+
+ DEBUG(6,("write_socket(%d,%d)\n",fd,(int)len));
+ ret = write_socket_data(fd,buf,len);
+
+ DEBUG(6,("write_socket(%d,%d) wrote %d\n",fd,(int)len,(int)ret));
+ if(ret <= 0)
+ DEBUG(0,("write_socket: Error writing %d bytes to socket %d: ERRNO = %s\n",
+ (int)len, fd, strerror(errno) ));
+
+ return(ret);
+}
+
+/****************************************************************************
+ Send a keepalive packet (rfc1002).
+****************************************************************************/
+
+BOOL send_keepalive(int client)
+{
+ unsigned char buf[4];
+
+ buf[0] = SMBkeepalive;
+ buf[1] = buf[2] = buf[3] = 0;
+
+ return(write_socket_data(client,(char *)buf,4) == 4);
+}
+
+
+/****************************************************************************
+ Read 4 bytes of a smb packet and return the smb length of the packet.
+ Store the result in the buffer.
+ This version of the function will return a length of zero on receiving
+ a keepalive packet.
+ Timeout is in milliseconds.
+****************************************************************************/
+
+static ssize_t read_smb_length_return_keepalive(int fd,char *inbuf,unsigned int timeout)
+{
+ ssize_t len=0;
+ int msg_type;
+ BOOL ok = False;
+
+ while (!ok) {
+ if (timeout > 0)
+ ok = (read_socket_with_timeout(fd,inbuf,4,4,timeout) == 4);
+ else
+ ok = (read_socket_data(fd,inbuf,4) == 4);
+
+ if (!ok)
+ return(-1);
+
+ len = smb_len(inbuf);
+ msg_type = CVAL(inbuf,0);
+
+ if (msg_type == SMBkeepalive)
+ DEBUG(5,("Got keepalive packet\n"));
+ }
+
+ DEBUG(10,("got smb length of %lu\n",(unsigned long)len));
+
+ return(len);
+}
+
+/****************************************************************************
+ Read 4 bytes of a smb packet and return the smb length of the packet.
+ Store the result in the buffer. This version of the function will
+ never return a session keepalive (length of zero).
+ Timeout is in milliseconds.
+****************************************************************************/
+
+ssize_t read_smb_length(int fd,char *inbuf,unsigned int timeout)
+{
+ ssize_t len;
+
+ for(;;) {
+ len = read_smb_length_return_keepalive(fd, inbuf, timeout);
+
+ if(len < 0)
+ return len;
+
+ /* Ignore session keepalives. */
+ if(CVAL(inbuf,0) != SMBkeepalive)
+ break;
+ }
+
+ DEBUG(10,("read_smb_length: got smb length of %lu\n",
+ (unsigned long)len));
+
+ return len;
+}
+
+/****************************************************************************
+ Read an smb from a fd. Note that the buffer *MUST* be of size
+ BUFFER_SIZE+SAFETY_MARGIN.
+ The timeout is in milliseconds.
+ This function will return on receipt of a session keepalive packet.
+ Doesn't check the MAC on signed packets.
+****************************************************************************/
+
+BOOL receive_smb_raw(int fd,char *buffer, unsigned int timeout)
+{
+ ssize_t len,ret;
+
+ smb_read_error = 0;
+
+ memset(buffer,'\0',smb_size + 100);
+
+ len = read_smb_length_return_keepalive(fd,buffer,timeout);
+ if (len < 0) {
+ DEBUG(10,("receive_smb_raw: length < 0!\n"));
+
+ /*
+ * Correct fix. smb_read_error may have already been
+ * set. Only set it here if not already set. Global
+ * variables still suck :-). JRA.
+ */
+
+ if (smb_read_error == 0)
+ smb_read_error = READ_ERROR;
+ return False;
+ }
+
+ /*
+ * A WRITEX with CAP_LARGE_WRITEX can be 64k worth of data plus 65 bytes
+ * of header. Don't print the error if this fits.... JRA.
+ */
+
+ if (len > (BUFFER_SIZE + LARGE_WRITEX_HDR_SIZE)) {
+ DEBUG(0,("Invalid packet length! (%lu bytes).\n",(unsigned long)len));
+ if (len > BUFFER_SIZE + (SAFETY_MARGIN/2)) {
+
+ /*
+ * Correct fix. smb_read_error may have already been
+ * set. Only set it here if not already set. Global
+ * variables still suck :-). JRA.
+ */
+
+ if (smb_read_error == 0)
+ smb_read_error = READ_ERROR;
+ return False;
+ }
+ }
+
+ if(len > 0) {
+ ret = read_socket_data(fd,buffer+4,len);
+ if (ret != len) {
+ if (smb_read_error == 0)
+ smb_read_error = READ_ERROR;
+ return False;
+ }
+
+ /* not all of samba3 properly checks for packet-termination of strings. This
+ ensures that we don't run off into empty space. */
+ SSVAL(buffer+4,len, 0);
+ }
+
+ return True;
+}
+
+/****************************************************************************
+ Wrapper for receive_smb_raw().
+ Checks the MAC on signed packets.
+****************************************************************************/
+
+BOOL receive_smb(int fd,char *buffer, unsigned int timeout)
+{
+ if (!receive_smb_raw(fd, buffer, timeout)) {
+ return False;
+ }
+
+ /* Check the incoming SMB signature. */
+ if (!srv_check_sign_mac(buffer, True)) {
+ DEBUG(0, ("receive_smb: SMB Signature verification failed on incoming packet!\n"));
+ if (smb_read_error == 0)
+ smb_read_error = READ_BAD_SIG;
+ return False;
+ };
+
+ return(True);
+}
+
+/****************************************************************************
+ Send an smb to a fd.
+****************************************************************************/
+
+BOOL send_smb(int fd,char *buffer)
+{
+ size_t len;
+ size_t nwritten=0;
+ ssize_t ret;
+
+ /* Sign the outgoing packet if required. */
+ srv_calculate_sign_mac(buffer);
+
+ len = smb_len(buffer) + 4;
+
+ while (nwritten < len) {
+ ret = write_socket(fd,buffer+nwritten,len - nwritten);
+ if (ret <= 0) {
+ DEBUG(0,("Error writing %d bytes to client. %d. (%s)\n",
+ (int)len,(int)ret, strerror(errno) ));
+ return False;
+ }
+ nwritten += ret;
+ }
+
+ return True;
+}
+
+/****************************************************************************
+ Open a socket of the specified type, port, and address for incoming data.
+****************************************************************************/
+
+int open_socket_in( int type, int port, int dlevel, uint32 socket_addr, BOOL rebind )
+{
+ struct sockaddr_in sock;
+ int res;
+
+ memset( (char *)&sock, '\0', sizeof(sock) );
+
+#ifdef HAVE_SOCK_SIN_LEN
+ sock.sin_len = sizeof(sock);
+#endif
+ sock.sin_port = htons( port );
+ sock.sin_family = AF_INET;
+ sock.sin_addr.s_addr = socket_addr;
+
+ res = socket( AF_INET, type, 0 );
+ if( res == -1 ) {
+ if( DEBUGLVL(0) ) {
+ dbgtext( "open_socket_in(): socket() call failed: " );
+ dbgtext( "%s\n", strerror( errno ) );
+ }
+ return -1;
+ }
+
+ /* This block sets/clears the SO_REUSEADDR and possibly SO_REUSEPORT. */
+ {
+ int val = rebind ? 1 : 0;
+ if( setsockopt(res,SOL_SOCKET,SO_REUSEADDR,(char *)&val,sizeof(val)) == -1 ) {
+ if( DEBUGLVL( dlevel ) ) {
+ dbgtext( "open_socket_in(): setsockopt: " );
+ dbgtext( "SO_REUSEADDR = %s ", val?"True":"False" );
+ dbgtext( "on port %d failed ", port );
+ dbgtext( "with error = %s\n", strerror(errno) );
+ }
+ }
+#ifdef SO_REUSEPORT
+ if( setsockopt(res,SOL_SOCKET,SO_REUSEPORT,(char *)&val,sizeof(val)) == -1 ) {
+ if( DEBUGLVL( dlevel ) ) {
+ dbgtext( "open_socket_in(): setsockopt: ");
+ dbgtext( "SO_REUSEPORT = %s ", val?"True":"False" );
+ dbgtext( "on port %d failed ", port );
+ dbgtext( "with error = %s\n", strerror(errno) );
+ }
+ }
+#endif /* SO_REUSEPORT */
+ }
+
+ /* now we've got a socket - we need to bind it */
+ if( bind( res, (struct sockaddr *)&sock, sizeof(sock) ) == -1 ) {
+ if( DEBUGLVL(dlevel) && (port == SMB_PORT1 || port == SMB_PORT2 || port == NMB_PORT) ) {
+ dbgtext( "bind failed on port %d ", port );
+ dbgtext( "socket_addr = %s.\n", inet_ntoa( sock.sin_addr ) );
+ dbgtext( "Error = %s\n", strerror(errno) );
+ }
+ close( res );
+ return( -1 );
+ }
+
+ DEBUG( 10, ( "bind succeeded on port %d\n", port ) );
+
+ return( res );
+ }
+
+/****************************************************************************
+ Create an outgoing socket. timeout is in milliseconds.
+**************************************************************************/
+
+int open_socket_out(int type, struct in_addr *addr, int port ,int timeout)
+{
+ struct sockaddr_in sock_out;
+ int res,ret;
+ int connect_loop = 10;
+ int increment = 10;
+
+ /* create a socket to write to */
+ res = socket(PF_INET, type, 0);
+ if (res == -1) {
+ DEBUG(0,("socket error (%s)\n", strerror(errno)));
+ return -1;
+ }
+
+ if (type != SOCK_STREAM)
+ return(res);
+
+ memset((char *)&sock_out,'\0',sizeof(sock_out));
+ putip((char *)&sock_out.sin_addr,(char *)addr);
+
+ sock_out.sin_port = htons( port );
+ sock_out.sin_family = PF_INET;
+
+ /* set it non-blocking */
+ set_blocking(res,False);
+
+ DEBUG(3,("Connecting to %s at port %d\n",inet_ntoa(*addr),port));
+
+ /* and connect it to the destination */
+ connect_again:
+
+ ret = connect(res,(struct sockaddr *)&sock_out,sizeof(sock_out));
+
+ /* Some systems return EAGAIN when they mean EINPROGRESS */
+ if (ret < 0 && (errno == EINPROGRESS || errno == EALREADY ||
+ errno == EAGAIN) && (connect_loop < timeout) ) {
+ smb_msleep(connect_loop);
+ connect_loop += increment;
+ if (increment < 250) {
+ /* After 8 rounds we end up at a max of 255 msec */
+ increment *= 1.5;
+ }
+ goto connect_again;
+ }
+
+ if (ret < 0 && (errno == EINPROGRESS || errno == EALREADY ||
+ errno == EAGAIN)) {
+ DEBUG(1,("timeout connecting to %s:%d\n",inet_ntoa(*addr),port));
+ close(res);
+ return -1;
+ }
+
+#ifdef EISCONN
+
+ if (ret < 0 && errno == EISCONN) {
+ errno = 0;
+ ret = 0;
+ }
+#endif
+
+ if (ret < 0) {
+ DEBUG(2,("error connecting to %s:%d (%s)\n",
+ inet_ntoa(*addr),port,strerror(errno)));
+ close(res);
+ return -1;
+ }
+
+ /* set it blocking again */
+ set_blocking(res,True);
+
+ return res;
+}
+
+/****************************************************************************
+ Open a connected UDP socket to host on port
+**************************************************************************/
+
+int open_udp_socket(const char *host, int port)
+{
+ int type = SOCK_DGRAM;
+ struct sockaddr_in sock_out;
+ int res;
+ struct in_addr *addr;
+
+ addr = interpret_addr2(host);
+
+ res = socket(PF_INET, type, 0);
+ if (res == -1) {
+ return -1;
+ }
+
+ memset((char *)&sock_out,'\0',sizeof(sock_out));
+ putip((char *)&sock_out.sin_addr,(char *)addr);
+ sock_out.sin_port = htons(port);
+ sock_out.sin_family = PF_INET;
+
+ if (connect(res,(struct sockaddr *)&sock_out,sizeof(sock_out))) {
+ close(res);
+ return -1;
+ }
+
+ return res;
+}
+
+
+/* the following 3 client_*() functions are nasty ways of allowing
+ some generic functions to get info that really should be hidden in
+ particular modules */
+static int client_fd = -1;
+
+void client_setfd(int fd)
+{
+ client_fd = fd;
+}
+
+char *client_name(void)
+{
+ return get_peer_name(client_fd,False);
+}
+
+char *client_addr(void)
+{
+ return get_peer_addr(client_fd);
+}
+
+char *client_socket_addr(void)
+{
+ return get_socket_addr(client_fd);
+}
+
+struct in_addr *client_inaddr(struct sockaddr *sa)
+{
+ struct sockaddr_in *sockin = (struct sockaddr_in *) (sa);
+ int length = sizeof(*sa);
+
+ if (getpeername(client_fd, sa, &length) < 0) {
+ DEBUG(0,("getpeername failed. Error was %s\n", strerror(errno) ));
+ return NULL;
+ }
+
+ return &sockin->sin_addr;
+}
+
+/*******************************************************************
+ Matchname - determine if host name matches IP address. Used to
+ confirm a hostname lookup to prevent spoof attacks.
+******************************************************************/
+
+static BOOL matchname(char *remotehost,struct in_addr addr)
+{
+ struct hostent *hp;
+ int i;
+
+ if ((hp = sys_gethostbyname(remotehost)) == 0) {
+ DEBUG(0,("sys_gethostbyname(%s): lookup failure.\n", remotehost));
+ return False;
+ }
+
+ /*
+ * Make sure that gethostbyname() returns the "correct" host name.
+ * Unfortunately, gethostbyname("localhost") sometimes yields
+ * "localhost.domain". Since the latter host name comes from the
+ * local DNS, we just have to trust it (all bets are off if the local
+ * DNS is perverted). We always check the address list, though.
+ */
+
+ if (!strequal(remotehost, hp->h_name)
+ && !strequal(remotehost, "localhost")) {
+ DEBUG(0,("host name/name mismatch: %s != %s\n",
+ remotehost, hp->h_name));
+ return False;
+ }
+
+ /* Look up the host address in the address list we just got. */
+ for (i = 0; hp->h_addr_list[i]; i++) {
+ if (memcmp(hp->h_addr_list[i], (char *) & addr, sizeof(addr)) == 0)
+ return True;
+ }
+
+ /*
+ * The host name does not map to the original host address. Perhaps
+ * someone has compromised a name server. More likely someone botched
+ * it, but that could be dangerous, too.
+ */
+
+ DEBUG(0,("host name/address mismatch: %s != %s\n",
+ inet_ntoa(addr), hp->h_name));
+ return False;
+}
+
+/*******************************************************************
+ Return the DNS name of the remote end of a socket.
+******************************************************************/
+
+char *get_peer_name(int fd, BOOL force_lookup)
+{
+ static pstring name_buf;
+ pstring tmp_name;
+ static fstring addr_buf;
+ struct hostent *hp;
+ struct in_addr addr;
+ char *p;
+
+ /* reverse lookups can be *very* expensive, and in many
+ situations won't work because many networks don't link dhcp
+ with dns. To avoid the delay we avoid the lookup if
+ possible */
+ if (!lp_hostname_lookups() && (force_lookup == False)) {
+ return get_peer_addr(fd);
+ }
+
+ p = get_peer_addr(fd);
+
+ /* it might be the same as the last one - save some DNS work */
+ if (strcmp(p, addr_buf) == 0)
+ return name_buf;
+
+ pstrcpy(name_buf,"UNKNOWN");
+ if (fd == -1)
+ return name_buf;
+
+ fstrcpy(addr_buf, p);
+
+ addr = *interpret_addr2(p);
+
+ /* Look up the remote host name. */
+ if ((hp = gethostbyaddr((char *)&addr.s_addr, sizeof(addr.s_addr), AF_INET)) == 0) {
+ DEBUG(1,("Gethostbyaddr failed for %s\n",p));
+ pstrcpy(name_buf, p);
+ } else {
+ pstrcpy(name_buf,(char *)hp->h_name);
+ if (!matchname(name_buf, addr)) {
+ DEBUG(0,("Matchname failed on %s %s\n",name_buf,p));
+ pstrcpy(name_buf,"UNKNOWN");
+ }
+ }
+
+ /* can't pass the same source and dest strings in when you
+ use --enable-developer or the clobber_region() call will
+ get you */
+
+ pstrcpy( tmp_name, name_buf );
+ alpha_strcpy(name_buf, tmp_name, "_-.", sizeof(name_buf));
+ if (strstr(name_buf,"..")) {
+ pstrcpy(name_buf, "UNKNOWN");
+ }
+
+ return name_buf;
+}
+
+/*******************************************************************
+ Return the IP addr of the remote end of a socket as a string.
+ ******************************************************************/
+
+char *get_peer_addr(int fd)
+{
+ struct sockaddr sa;
+ struct sockaddr_in *sockin = (struct sockaddr_in *) (&sa);
+ int length = sizeof(sa);
+ static fstring addr_buf;
+
+ fstrcpy(addr_buf,"0.0.0.0");
+
+ if (fd == -1) {
+ return addr_buf;
+ }
+
+ if (getpeername(fd, &sa, &length) < 0) {
+ DEBUG(0,("getpeername failed. Error was %s\n", strerror(errno) ));
+ return addr_buf;
+ }
+
+ fstrcpy(addr_buf,(char *)inet_ntoa(sockin->sin_addr));
+
+ return addr_buf;
+}
+
+/*******************************************************************
+ Create protected unix domain socket.
+
+ Some unixes cannot set permissions on a ux-dom-sock, so we
+ have to make sure that the directory contains the protection
+ permissions instead.
+ ******************************************************************/
+
+int create_pipe_sock(const char *socket_dir,
+ const char *socket_name,
+ mode_t dir_perms)
+{
+#ifdef HAVE_UNIXSOCKET
+ struct sockaddr_un sunaddr;
+ struct stat st;
+ int sock;
+ mode_t old_umask;
+ pstring path;
+
+ old_umask = umask(0);
+
+ /* Create the socket directory or reuse the existing one */
+
+ if (lstat(socket_dir, &st) == -1) {
+ if (errno == ENOENT) {
+ /* Create directory */
+ if (mkdir(socket_dir, dir_perms) == -1) {
+ DEBUG(0, ("error creating socket directory "
+ "%s: %s\n", socket_dir,
+ strerror(errno)));
+ goto out_umask;
+ }
+ } else {
+ DEBUG(0, ("lstat failed on socket directory %s: %s\n",
+ socket_dir, strerror(errno)));
+ goto out_umask;
+ }
+ } else {
+ /* Check ownership and permission on existing directory */
+ if (!S_ISDIR(st.st_mode)) {
+ DEBUG(0, ("socket directory %s isn't a directory\n",
+ socket_dir));
+ goto out_umask;
+ }
+ if ((st.st_uid != sec_initial_uid()) ||
+ ((st.st_mode & 0777) != dir_perms)) {
+ DEBUG(0, ("invalid permissions on socket directory "
+ "%s\n", socket_dir));
+ goto out_umask;
+ }
+ }
+
+ /* Create the socket file */
+
+ sock = socket(AF_UNIX, SOCK_STREAM, 0);
+
+ if (sock == -1) {
+ perror("socket");
+ goto out_umask;
+ }
+
+ pstr_sprintf(path, "%s/%s", socket_dir, socket_name);
+
+ unlink(path);
+ memset(&sunaddr, 0, sizeof(sunaddr));
+ sunaddr.sun_family = AF_UNIX;
+ safe_strcpy(sunaddr.sun_path, path, sizeof(sunaddr.sun_path)-1);
+
+ if (bind(sock, (struct sockaddr *)&sunaddr, sizeof(sunaddr)) == -1) {
+ DEBUG(0, ("bind failed on pipe socket %s: %s\n", path,
+ strerror(errno)));
+ goto out_close;
+ }
+
+ if (listen(sock, 5) == -1) {
+ DEBUG(0, ("listen failed on pipe socket %s: %s\n", path,
+ strerror(errno)));
+ goto out_close;
+ }
+
+ umask(old_umask);
+ return sock;
+
+out_close:
+ close(sock);
+
+out_umask:
+ umask(old_umask);
+ return -1;
+
+#else
+ DEBUG(0, ("create_pipe_sock: No Unix sockets on this system\n"));
+ return -1;
+#endif /* HAVE_UNIXSOCKET */
+}
diff --git a/source/lib/util_str.c b/source/lib/util_str.c
new file mode 100644
index 00000000000..be1e2ffeb1b
--- /dev/null
+++ b/source/lib/util_str.c
@@ -0,0 +1,2047 @@
+/*
+ Unix SMB/CIFS implementation.
+ Samba utility functions
+
+ Copyright (C) Andrew Tridgell 1992-2001
+ Copyright (C) Simo Sorce 2001-2002
+ Copyright (C) Martin Pool 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 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"
+
+/**
+ * @file
+ * @brief String utilities.
+ **/
+
+/**
+ * Get the next token from a string, return False if none found.
+ * Handles double-quotes.
+ *
+ * Based on a routine by GJC@VILLAGE.COM.
+ * Extensively modified by Andrew.Tridgell@anu.edu.au
+ **/
+BOOL next_token(const char **ptr,char *buff, const char *sep, size_t bufsize)
+{
+ char *s;
+ char *pbuf;
+ BOOL quoted;
+ size_t len=1;
+
+ if (!ptr)
+ return(False);
+
+ s = (char *)*ptr;
+
+ /* default to simple separators */
+ if (!sep)
+ sep = " \t\n\r";
+
+ /* find the first non sep char */
+ while (*s && strchr_m(sep,*s))
+ s++;
+
+ /* nothing left? */
+ if (! *s)
+ return(False);
+
+ /* copy over the token */
+ pbuf = buff;
+ for (quoted = False; len < bufsize && *s && (quoted || !strchr_m(sep,*s)); s++) {
+ if (*s == '\"' || *s == '\'') {
+ quoted = !quoted;
+ } else {
+ len++;
+ *pbuf++ = *s;
+ }
+ }
+
+ *ptr = (*s) ? s+1 : s;
+ *pbuf = 0;
+
+ return(True);
+}
+
+/**
+This is like next_token but is not re-entrant and "remembers" the first
+parameter so you can pass NULL. This is useful for user interface code
+but beware the fact that it is not re-entrant!
+**/
+
+static const char *last_ptr=NULL;
+
+BOOL next_token_nr(const char **ptr,char *buff, const char *sep, size_t bufsize)
+{
+ BOOL ret;
+ if (!ptr)
+ ptr = &last_ptr;
+
+ ret = next_token(ptr, buff, sep, bufsize);
+ last_ptr = *ptr;
+ return ret;
+}
+
+static uint16 tmpbuf[sizeof(pstring)];
+
+void set_first_token(char *ptr)
+{
+ last_ptr = ptr;
+}
+
+/**
+ Convert list of tokens to array; dependent on above routine.
+ Uses last_ptr from above - bit of a hack.
+**/
+
+char **toktocliplist(int *ctok, const char *sep)
+{
+ char *s=(char *)last_ptr;
+ int ictok=0;
+ char **ret, **iret;
+
+ if (!sep)
+ sep = " \t\n\r";
+
+ while(*s && strchr_m(sep,*s))
+ s++;
+
+ /* nothing left? */
+ if (!*s)
+ return(NULL);
+
+ do {
+ ictok++;
+ while(*s && (!strchr_m(sep,*s)))
+ s++;
+ while(*s && strchr_m(sep,*s))
+ *s++=0;
+ } while(*s);
+
+ *ctok=ictok;
+ s=(char *)last_ptr;
+
+ if (!(ret=iret=malloc(ictok*sizeof(char *))))
+ return NULL;
+
+ while(ictok--) {
+ *iret++=s;
+ while(*s++)
+ ;
+ while(!*s)
+ s++;
+ }
+
+ return ret;
+}
+
+/**
+ * Case insensitive string compararison.
+ *
+ * iconv does not directly give us a way to compare strings in
+ * arbitrary unix character sets -- all we can is convert and then
+ * compare. This is expensive.
+ *
+ * As an optimization, we do a first pass that considers only the
+ * prefix of the strings that is entirely 7-bit. Within this, we
+ * check whether they have the same value.
+ *
+ * Hopefully this will often give the answer without needing to copy.
+ * In particular it should speed comparisons to literal ascii strings
+ * or comparisons of strings that are "obviously" different.
+ *
+ * If we find a non-ascii character we fall back to converting via
+ * iconv.
+ *
+ * This should never be slower than convering the whole thing, and
+ * often faster.
+ *
+ * A different optimization would be to compare for bitwise equality
+ * in the binary encoding. (It would be possible thought hairy to do
+ * both simultaneously.) But in that case if they turn out to be
+ * different, we'd need to restart the whole thing.
+ *
+ * Even better is to implement strcasecmp for each encoding and use a
+ * function pointer.
+ **/
+int StrCaseCmp(const char *s, const char *t)
+{
+
+ const char * ps, * pt;
+ size_t size;
+ smb_ucs2_t *buffer_s, *buffer_t;
+ int ret;
+
+ for (ps = s, pt = t; ; ps++, pt++) {
+ char us, ut;
+
+ if (!*ps && !*pt)
+ return 0; /* both ended */
+ else if (!*ps)
+ return -1; /* s is a prefix */
+ else if (!*pt)
+ return +1; /* t is a prefix */
+ else if ((*ps & 0x80) || (*pt & 0x80))
+ /* not ascii anymore, do it the hard way from here on in */
+ break;
+
+ us = toupper(*ps);
+ ut = toupper(*pt);
+ if (us == ut)
+ continue;
+ else if (us < ut)
+ return -1;
+ else if (us > ut)
+ return +1;
+ }
+
+ size = push_ucs2_allocate(&buffer_s, s);
+ if (size == (size_t)-1) {
+ return strcmp(s, t);
+ /* Not quite the right answer, but finding the right one
+ under this failure case is expensive, and it's pretty close */
+ }
+
+ size = push_ucs2_allocate(&buffer_t, t);
+ if (size == (size_t)-1) {
+ SAFE_FREE(buffer_s);
+ return strcmp(s, t);
+ /* Not quite the right answer, but finding the right one
+ under this failure case is expensive, and it's pretty close */
+ }
+
+ ret = strcasecmp_w(buffer_s, buffer_t);
+ SAFE_FREE(buffer_s);
+ SAFE_FREE(buffer_t);
+ return ret;
+}
+
+
+/**
+ Case insensitive string compararison, length limited.
+**/
+int StrnCaseCmp(const char *s, const char *t, size_t n)
+{
+ pstring buf1, buf2;
+ unix_strupper(s, strlen(s)+1, buf1, sizeof(buf1));
+ unix_strupper(t, strlen(t)+1, buf2, sizeof(buf2));
+ return strncmp(buf1,buf2,n);
+}
+
+/**
+ * Compare 2 strings.
+ *
+ * @note The comparison is case-insensitive.
+ **/
+BOOL strequal(const char *s1, const char *s2)
+{
+ if (s1 == s2)
+ return(True);
+ if (!s1 || !s2)
+ return(False);
+
+ return(StrCaseCmp(s1,s2)==0);
+}
+
+/**
+ * Compare 2 strings up to and including the nth char.
+ *
+ * @note The comparison is case-insensitive.
+ **/
+BOOL strnequal(const char *s1,const char *s2,size_t n)
+{
+ if (s1 == s2)
+ return(True);
+ if (!s1 || !s2 || !n)
+ return(False);
+
+ return(StrnCaseCmp(s1,s2,n)==0);
+}
+
+/**
+ Compare 2 strings (case sensitive).
+**/
+
+BOOL strcsequal(const char *s1,const char *s2)
+{
+ if (s1 == s2)
+ return(True);
+ if (!s1 || !s2)
+ return(False);
+
+ return(strcmp(s1,s2)==0);
+}
+
+/**
+Do a case-insensitive, whitespace-ignoring string compare.
+**/
+
+int strwicmp(const char *psz1, const char *psz2)
+{
+ /* if BOTH strings are NULL, return TRUE, if ONE is NULL return */
+ /* appropriate value. */
+ if (psz1 == psz2)
+ return (0);
+ else if (psz1 == NULL)
+ return (-1);
+ else if (psz2 == NULL)
+ return (1);
+
+ /* sync the strings on first non-whitespace */
+ while (1) {
+ while (isspace((int)*psz1))
+ psz1++;
+ while (isspace((int)*psz2))
+ psz2++;
+ if (toupper(*psz1) != toupper(*psz2) || *psz1 == '\0'
+ || *psz2 == '\0')
+ break;
+ psz1++;
+ psz2++;
+ }
+ return (*psz1 - *psz2);
+}
+
+
+/**
+ Convert a string to upper case, but don't modify it.
+**/
+
+char *strupper_static(const char *s)
+{
+ static pstring str;
+
+ pstrcpy(str, s);
+ strupper_m(str);
+
+ return str;
+}
+
+/**
+ Convert a string to "normal" form.
+**/
+
+void strnorm(char *s)
+{
+ extern int case_default;
+ if (case_default == CASE_UPPER)
+ strupper_m(s);
+ else
+ strlower_m(s);
+}
+
+/**
+ Check if a string is in "normal" case.
+**/
+
+BOOL strisnormal(const char *s)
+{
+ extern int case_default;
+ if (case_default == CASE_UPPER)
+ return(!strhaslower(s));
+
+ return(!strhasupper(s));
+}
+
+
+/**
+ String replace.
+ NOTE: oldc and newc must be 7 bit characters
+**/
+
+void string_replace(pstring s,char oldc,char newc)
+{
+ unsigned char *p;
+
+ /* this is quite a common operation, so we want it to be
+ fast. We optimise for the ascii case, knowing that all our
+ supported multi-byte character sets are ascii-compatible
+ (ie. they match for the first 128 chars) */
+
+ for (p = (unsigned char *)s; *p; p++) {
+ if (*p & 0x80) /* mb string - slow path. */
+ break;
+ if (*p == oldc)
+ *p = newc;
+ }
+
+ if (!*p)
+ return;
+
+ /* Slow (mb) path. */
+#ifdef BROKEN_UNICODE_COMPOSE_CHARACTERS
+ /* With compose characters we must restart from the beginning. JRA. */
+ p = s;
+#endif
+ push_ucs2(NULL, tmpbuf, p, sizeof(tmpbuf), STR_TERMINATE);
+ string_replace_w(tmpbuf, UCS2_CHAR(oldc), UCS2_CHAR(newc));
+ pull_ucs2(NULL, p, tmpbuf, -1, sizeof(tmpbuf), STR_TERMINATE);
+}
+
+/**
+ Skip past some strings in a buffer.
+**/
+
+char *skip_string(char *buf,size_t n)
+{
+ while (n--)
+ buf += strlen(buf) + 1;
+ return(buf);
+}
+
+/**
+ Count the number of characters in a string. Normally this will
+ be the same as the number of bytes in a string for single byte strings,
+ but will be different for multibyte.
+**/
+
+size_t str_charnum(const char *s)
+{
+ uint16 tmpbuf2[sizeof(pstring)];
+ push_ucs2(NULL, tmpbuf2,s, sizeof(tmpbuf2), STR_TERMINATE);
+ return strlen_w(tmpbuf2);
+}
+
+/**
+ Count the number of characters in a string. Normally this will
+ be the same as the number of bytes in a string for single byte strings,
+ but will be different for multibyte.
+**/
+
+size_t str_ascii_charnum(const char *s)
+{
+ pstring tmpbuf2;
+ push_ascii(tmpbuf2, s, sizeof(tmpbuf2), STR_TERMINATE);
+ return strlen(tmpbuf2);
+}
+
+BOOL trim_char(char *s,char cfront,char cback)
+{
+ BOOL ret = False;
+ char *ep;
+ char *fp = s;
+
+ /* Ignore null or empty strings. */
+ if (!s || (s[0] == '\0'))
+ return False;
+
+ if (cfront) {
+ while (*fp && *fp == cfront)
+ fp++;
+ if (!*fp) {
+ /* We ate the string. */
+ s[0] = '\0';
+ return True;
+ }
+ if (fp != s)
+ ret = True;
+ }
+
+ ep = fp + strlen(fp) - 1;
+ if (cback) {
+ /* Attempt ascii only. Bail for mb strings. */
+ while ((ep >= fp) && (*ep == cback)) {
+ ret = True;
+ if ((ep > fp) && (((unsigned char)ep[-1]) & 0x80)) {
+ /* Could be mb... bail back to tim_string. */
+ char fs[2], bs[2];
+ if (cfront) {
+ fs[0] = cfront;
+ fs[1] = '\0';
+ }
+ bs[0] = cback;
+ bs[1] = '\0';
+ return trim_string(s, cfront ? fs : NULL, bs);
+ } else {
+ ep--;
+ }
+ }
+ if (ep < fp) {
+ /* We ate the string. */
+ s[0] = '\0';
+ return True;
+ }
+ }
+
+ ep[1] = '\0';
+ memmove(s, fp, ep-fp+2);
+ return ret;
+}
+
+/**
+ Trim the specified elements off the front and back of a string.
+**/
+
+BOOL trim_string(char *s,const char *front,const char *back)
+{
+ BOOL ret = False;
+ size_t front_len;
+ size_t back_len;
+ size_t len;
+
+ /* Ignore null or empty strings. */
+ if (!s || (s[0] == '\0'))
+ return False;
+
+ front_len = front? strlen(front) : 0;
+ back_len = back? strlen(back) : 0;
+
+ len = strlen(s);
+
+ if (front_len) {
+ while (len && strncmp(s, front, front_len)==0) {
+ /* Must use memmove here as src & dest can
+ * easily overlap. Found by valgrind. JRA. */
+ memmove(s, s+front_len, (len-front_len)+1);
+ len -= front_len;
+ ret=True;
+ }
+ }
+
+ if (back_len) {
+ while ((len >= back_len) && strncmp(s+len-back_len,back,back_len)==0) {
+ s[len-back_len]='\0';
+ len -= back_len;
+ ret=True;
+ }
+ }
+ return ret;
+}
+
+/**
+ Does a string have any uppercase chars in it?
+**/
+
+BOOL strhasupper(const char *s)
+{
+ smb_ucs2_t *ptr;
+ push_ucs2(NULL, tmpbuf,s, sizeof(tmpbuf), STR_TERMINATE);
+ for(ptr=tmpbuf;*ptr;ptr++)
+ if(isupper_w(*ptr))
+ return True;
+ return(False);
+}
+
+/**
+ Does a string have any lowercase chars in it?
+**/
+
+BOOL strhaslower(const char *s)
+{
+ smb_ucs2_t *ptr;
+ push_ucs2(NULL, tmpbuf,s, sizeof(tmpbuf), STR_TERMINATE);
+ for(ptr=tmpbuf;*ptr;ptr++)
+ if(islower_w(*ptr))
+ return True;
+ return(False);
+}
+
+/**
+ Find the number of 'c' chars in a string
+**/
+
+size_t count_chars(const char *s,char c)
+{
+ smb_ucs2_t *ptr;
+ int count;
+ smb_ucs2_t *alloc_tmpbuf = NULL;
+
+ if (push_ucs2_allocate(&alloc_tmpbuf, s) == (size_t)-1) {
+ return 0;
+ }
+
+ for(count=0,ptr=alloc_tmpbuf;*ptr;ptr++)
+ if(*ptr==UCS2_CHAR(c))
+ count++;
+
+ SAFE_FREE(alloc_tmpbuf);
+ return(count);
+}
+
+/**
+ Safe string copy into a known length string. maxlength does not
+ include the terminating zero.
+**/
+
+char *safe_strcpy_fn(const char *fn, int line, char *dest,const char *src, size_t maxlength)
+{
+ size_t len;
+
+ if (!dest) {
+ DEBUG(0,("ERROR: NULL dest in safe_strcpy, called from [%s][%d]\n", fn, line));
+ return NULL;
+ }
+
+#ifdef DEVELOPER
+ clobber_region(fn,line,dest, maxlength+1);
+#endif
+
+ if (!src) {
+ *dest = 0;
+ return dest;
+ }
+
+ len = strnlen(src, maxlength+1);
+
+ if (len > maxlength) {
+ DEBUG(0,("ERROR: string overflow by %lu (%lu - %lu) in safe_strcpy [%.50s]\n",
+ (unsigned long)(len-maxlength), (unsigned long)len,
+ (unsigned long)maxlength, src));
+ len = maxlength;
+ }
+
+ memmove(dest, src, len);
+ dest[len] = 0;
+ return dest;
+}
+
+/**
+ Safe string cat into a string. maxlength does not
+ include the terminating zero.
+**/
+char *safe_strcat_fn(const char *fn, int line, char *dest, const char *src, size_t maxlength)
+{
+ size_t src_len, dest_len;
+
+ if (!dest) {
+ DEBUG(0,("ERROR: NULL dest in safe_strcat, called from [%s][%d]\n", fn, line));
+ return NULL;
+ }
+
+ if (!src)
+ return dest;
+
+ src_len = strnlen(src, maxlength + 1);
+ dest_len = strnlen(dest, maxlength + 1);
+
+#ifdef DEVELOPER
+ clobber_region(fn, line, dest + dest_len, maxlength + 1 - dest_len);
+#endif
+
+ if (src_len + dest_len > maxlength) {
+ DEBUG(0,("ERROR: string overflow by %d in safe_strcat [%.50s]\n",
+ (int)(src_len + dest_len - maxlength), src));
+ if (maxlength > dest_len) {
+ memcpy(&dest[dest_len], src, maxlength - dest_len);
+ }
+ dest[maxlength] = 0;
+ return NULL;
+ }
+
+ memcpy(&dest[dest_len], src, src_len);
+ dest[dest_len + src_len] = 0;
+ return dest;
+}
+
+/**
+ Paranoid strcpy into a buffer of given length (includes terminating
+ zero. Strips out all but 'a-Z0-9' and the character in other_safe_chars
+ and replaces with '_'. Deliberately does *NOT* check for multibyte
+ characters. Don't change it !
+**/
+char *alpha_strcpy_fn(const char *fn, int line, char *dest, const char *src, const char *other_safe_chars, size_t maxlength)
+{
+ size_t len, i;
+
+#ifdef DEVELOPER
+ clobber_region(fn, line, dest, maxlength);
+#endif
+
+ if (!dest) {
+ DEBUG(0,("ERROR: NULL dest in alpha_strcpy, called from [%s][%d]\n", fn, line));
+ return NULL;
+ }
+
+ if (!src) {
+ *dest = 0;
+ return dest;
+ }
+
+ len = strlen(src);
+ if (len >= maxlength)
+ len = maxlength - 1;
+
+ if (!other_safe_chars)
+ other_safe_chars = "";
+
+ for(i = 0; i < len; i++) {
+ int val = (src[i] & 0xff);
+ if (isupper(val) || islower(val) || isdigit(val) || strchr_m(other_safe_chars, val))
+ dest[i] = src[i];
+ else
+ dest[i] = '_';
+ }
+
+ dest[i] = '\0';
+
+ return dest;
+}
+
+/**
+ Like strncpy but always null terminates. Make sure there is room!
+ The variable n should always be one less than the available size.
+**/
+char *StrnCpy_fn(const char *fn, int line,char *dest,const char *src,size_t n)
+{
+ char *d = dest;
+
+#ifdef DEVELOPER
+ clobber_region(fn, line, dest, n+1);
+#endif
+
+ if (!dest) {
+ DEBUG(0,("ERROR: NULL dest in StrnCpy, called from [%s][%d]\n", fn, line));
+ return(NULL);
+ }
+
+ if (!src) {
+ *dest = 0;
+ return(dest);
+ }
+
+ while (n-- && (*d = *src)) {
+ d++;
+ src++;
+ }
+
+ *d = 0;
+ return(dest);
+}
+
+#if 0
+/**
+ Like strncpy but copies up to the character marker. always null terminates.
+ returns a pointer to the character marker in the source string (src).
+**/
+
+static char *strncpyn(char *dest, const char *src, size_t n, char c)
+{
+ char *p;
+ size_t str_len;
+
+#ifdef DEVELOPER
+ clobber_region(dest, n+1);
+#endif
+ p = strchr_m(src, c);
+ if (p == NULL) {
+ DEBUG(5, ("strncpyn: separator character (%c) not found\n", c));
+ return NULL;
+ }
+
+ str_len = PTR_DIFF(p, src);
+ strncpy(dest, src, MIN(n, str_len));
+ dest[str_len] = '\0';
+
+ return p;
+}
+#endif
+
+/**
+ Routine to get hex characters and turn them into a 16 byte array.
+ the array can be variable length, and any non-hex-numeric
+ characters are skipped. "0xnn" or "0Xnn" is specially catered
+ for.
+
+ valid examples: "0A5D15"; "0x15, 0x49, 0xa2"; "59\ta9\te3\n"
+
+**/
+
+size_t strhex_to_str(char *p, size_t len, const char *strhex)
+{
+ size_t i;
+ size_t num_chars = 0;
+ unsigned char lonybble, hinybble;
+ const char *hexchars = "0123456789ABCDEF";
+ char *p1 = NULL, *p2 = NULL;
+
+ for (i = 0; i < len && strhex[i] != 0; i++) {
+ if (strnequal(hexchars, "0x", 2)) {
+ i++; /* skip two chars */
+ continue;
+ }
+
+ if (!(p1 = strchr_m(hexchars, toupper(strhex[i]))))
+ break;
+
+ i++; /* next hex digit */
+
+ if (!(p2 = strchr_m(hexchars, toupper(strhex[i]))))
+ break;
+
+ /* get the two nybbles */
+ hinybble = PTR_DIFF(p1, hexchars);
+ lonybble = PTR_DIFF(p2, hexchars);
+
+ p[num_chars] = (hinybble << 4) | lonybble;
+ num_chars++;
+
+ p1 = NULL;
+ p2 = NULL;
+ }
+ return num_chars;
+}
+
+/**
+ * Routine to print a buffer as HEX digits, into an allocated string.
+ */
+
+void hex_encode(const unsigned char *buff_in, size_t len, char **out_hex_buffer)
+{
+ int i;
+ char *hex_buffer;
+
+ *out_hex_buffer = smb_xmalloc((len*2)+1);
+ hex_buffer = *out_hex_buffer;
+
+ for (i = 0; i < len; i++)
+ slprintf(&hex_buffer[i*2], 3, "%02X", buff_in[i]);
+}
+
+/**
+ Check if a string is part of a list.
+**/
+
+BOOL in_list(char *s,char *list,BOOL casesensitive)
+{
+ pstring tok;
+ const char *p=list;
+
+ if (!list)
+ return(False);
+
+ while (next_token(&p,tok,LIST_SEP,sizeof(tok))) {
+ if (casesensitive) {
+ if (strcmp(tok,s) == 0)
+ return(True);
+ } else {
+ if (StrCaseCmp(tok,s) == 0)
+ return(True);
+ }
+ }
+ return(False);
+}
+
+/* this is used to prevent lots of mallocs of size 1 */
+static char *null_string = NULL;
+
+/**
+ Set a string value, allocing the space for the string
+**/
+
+static BOOL string_init(char **dest,const char *src)
+{
+ size_t l;
+ if (!src)
+ src = "";
+
+ l = strlen(src);
+
+ if (l == 0) {
+ if (!null_string) {
+ if((null_string = (char *)malloc(1)) == NULL) {
+ DEBUG(0,("string_init: malloc fail for null_string.\n"));
+ return False;
+ }
+ *null_string = 0;
+ }
+ *dest = null_string;
+ } else {
+ (*dest) = strdup(src);
+ if ((*dest) == NULL) {
+ DEBUG(0,("Out of memory in string_init\n"));
+ return False;
+ }
+ }
+ return(True);
+}
+
+/**
+ Free a string value.
+**/
+
+void string_free(char **s)
+{
+ if (!s || !(*s))
+ return;
+ if (*s == null_string)
+ *s = NULL;
+ SAFE_FREE(*s);
+}
+
+/**
+ Set a string value, deallocating any existing space, and allocing the space
+ for the string
+**/
+
+BOOL string_set(char **dest,const char *src)
+{
+ string_free(dest);
+ return(string_init(dest,src));
+}
+
+/**
+ Substitute a string for a pattern in another string. Make sure there is
+ enough room!
+
+ This routine looks for pattern in s and replaces it with
+ insert. It may do multiple replacements.
+
+ Any of " ; ' $ or ` in the insert string are replaced with _
+ if len==0 then the string cannot be extended. This is different from the old
+ use of len==0 which was for no length checks to be done.
+**/
+
+void string_sub(char *s,const char *pattern, const char *insert, size_t len)
+{
+ char *p;
+ ssize_t ls,lp,li, i;
+
+ if (!insert || !pattern || !*pattern || !s)
+ return;
+
+ ls = (ssize_t)strlen(s);
+ lp = (ssize_t)strlen(pattern);
+ li = (ssize_t)strlen(insert);
+
+ if (len == 0)
+ len = ls + 1; /* len is number of *bytes* */
+
+ while (lp <= ls && (p = strstr_m(s,pattern))) {
+ if (ls + (li-lp) >= len) {
+ DEBUG(0,("ERROR: string overflow by %d in string_sub(%.50s, %d)\n",
+ (int)(ls + (li-lp) - len),
+ pattern, (int)len));
+ break;
+ }
+ if (li != lp) {
+ memmove(p+li,p+lp,strlen(p+lp)+1);
+ }
+ for (i=0;i<li;i++) {
+ switch (insert[i]) {
+ case '`':
+ case '"':
+ case '\'':
+ case ';':
+ case '$':
+ case '%':
+ case '\r':
+ case '\n':
+ p[i] = '_';
+ break;
+ default:
+ p[i] = insert[i];
+ }
+ }
+ s = p + li;
+ ls += (li-lp);
+ }
+}
+
+void fstring_sub(char *s,const char *pattern,const char *insert)
+{
+ string_sub(s, pattern, insert, sizeof(fstring));
+}
+
+void pstring_sub(char *s,const char *pattern,const char *insert)
+{
+ string_sub(s, pattern, insert, sizeof(pstring));
+}
+
+/**
+ Similar to string_sub, but it will accept only allocated strings
+ and may realloc them so pay attention at what you pass on no
+ pointers inside strings, no pstrings or const may be passed
+ as string.
+**/
+
+char *realloc_string_sub(char *string, const char *pattern, const char *insert)
+{
+ char *p, *in;
+ char *s;
+ ssize_t ls,lp,li,ld, i;
+
+ if (!insert || !pattern || !*pattern || !string || !*string)
+ return NULL;
+
+ s = string;
+
+ in = strdup(insert);
+ if (!in) {
+ DEBUG(0, ("realloc_string_sub: out of memory!\n"));
+ return NULL;
+ }
+ ls = (ssize_t)strlen(s);
+ lp = (ssize_t)strlen(pattern);
+ li = (ssize_t)strlen(insert);
+ ld = li - lp;
+ for (i=0;i<li;i++) {
+ switch (in[i]) {
+ case '`':
+ case '"':
+ case '\'':
+ case ';':
+ case '$':
+ case '%':
+ case '\r':
+ case '\n':
+ in[i] = '_';
+ default:
+ /* ok */
+ break;
+ }
+ }
+
+ while ((p = strstr_m(s,pattern))) {
+ if (ld > 0) {
+ int offset = PTR_DIFF(s,string);
+ char *t = Realloc(string, ls + ld + 1);
+ if (!t) {
+ DEBUG(0, ("realloc_string_sub: out of memory!\n"));
+ SAFE_FREE(in);
+ return NULL;
+ }
+ string = t;
+ p = t + offset + (p - s);
+ }
+ if (li != lp) {
+ memmove(p+li,p+lp,strlen(p+lp)+1);
+ }
+ memcpy(p, in, li);
+ s = p + li;
+ ls += ld;
+ }
+ SAFE_FREE(in);
+ return string;
+}
+
+/**
+ Similar to string_sub() but allows for any character to be substituted.
+ Use with caution!
+ if len==0 then the string cannot be extended. This is different from the old
+ use of len==0 which was for no length checks to be done.
+**/
+
+void all_string_sub(char *s,const char *pattern,const char *insert, size_t len)
+{
+ char *p;
+ ssize_t ls,lp,li;
+
+ if (!insert || !pattern || !s)
+ return;
+
+ ls = (ssize_t)strlen(s);
+ lp = (ssize_t)strlen(pattern);
+ li = (ssize_t)strlen(insert);
+
+ if (!*pattern)
+ return;
+
+ if (len == 0)
+ len = ls + 1; /* len is number of *bytes* */
+
+ while (lp <= ls && (p = strstr_m(s,pattern))) {
+ if (ls + (li-lp) >= len) {
+ DEBUG(0,("ERROR: string overflow by %d in all_string_sub(%.50s, %d)\n",
+ (int)(ls + (li-lp) - len),
+ pattern, (int)len));
+ break;
+ }
+ if (li != lp) {
+ memmove(p+li,p+lp,strlen(p+lp)+1);
+ }
+ memcpy(p, insert, li);
+ s = p + li;
+ ls += (li-lp);
+ }
+}
+
+/**
+ Similar to all_string_sub but for unicode strings.
+ Return a new allocated unicode string.
+ similar to string_sub() but allows for any character to be substituted.
+ Use with caution!
+**/
+
+static smb_ucs2_t *all_string_sub_w(const smb_ucs2_t *s, const smb_ucs2_t *pattern,
+ const smb_ucs2_t *insert)
+{
+ smb_ucs2_t *r, *rp;
+ const smb_ucs2_t *sp;
+ size_t lr, lp, li, lt;
+
+ if (!insert || !pattern || !*pattern || !s)
+ return NULL;
+
+ lt = (size_t)strlen_w(s);
+ lp = (size_t)strlen_w(pattern);
+ li = (size_t)strlen_w(insert);
+
+ if (li > lp) {
+ const smb_ucs2_t *st = s;
+ int ld = li - lp;
+ while ((sp = strstr_w(st, pattern))) {
+ st = sp + lp;
+ lt += ld;
+ }
+ }
+
+ r = rp = (smb_ucs2_t *)malloc((lt + 1)*(sizeof(smb_ucs2_t)));
+ if (!r) {
+ DEBUG(0, ("all_string_sub_w: out of memory!\n"));
+ return NULL;
+ }
+
+ while ((sp = strstr_w(s, pattern))) {
+ memcpy(rp, s, (sp - s));
+ rp += ((sp - s) / sizeof(smb_ucs2_t));
+ memcpy(rp, insert, (li * sizeof(smb_ucs2_t)));
+ s = sp + lp;
+ rp += li;
+ }
+ lr = ((rp - r) / sizeof(smb_ucs2_t));
+ if (lr < lt) {
+ memcpy(rp, s, ((lt - lr) * sizeof(smb_ucs2_t)));
+ rp += (lt - lr);
+ }
+ *rp = 0;
+
+ return r;
+}
+
+smb_ucs2_t *all_string_sub_wa(smb_ucs2_t *s, const char *pattern,
+ const char *insert)
+{
+ wpstring p, i;
+
+ if (!insert || !pattern || !s)
+ return NULL;
+ push_ucs2(NULL, p, pattern, sizeof(wpstring) - 1, STR_TERMINATE);
+ push_ucs2(NULL, i, insert, sizeof(wpstring) - 1, STR_TERMINATE);
+ return all_string_sub_w(s, p, i);
+}
+
+#if 0
+/**
+ Splits out the front and back at a separator.
+**/
+
+static void split_at_last_component(char *path, char *front, char sep, char *back)
+{
+ char *p = strrchr_m(path, sep);
+
+ if (p != NULL)
+ *p = 0;
+
+ if (front != NULL)
+ pstrcpy(front, path);
+
+ if (p != NULL) {
+ if (back != NULL)
+ pstrcpy(back, p+1);
+ *p = '\\';
+ } else {
+ if (back != NULL)
+ back[0] = 0;
+ }
+}
+#endif
+
+/**
+ Write an octal as a string.
+**/
+
+const char *octal_string(int i)
+{
+ static char ret[64];
+ if (i == -1)
+ return "-1";
+ slprintf(ret, sizeof(ret)-1, "0%o", i);
+ return ret;
+}
+
+
+/**
+ Truncate a string at a specified length.
+**/
+
+char *string_truncate(char *s, unsigned int length)
+{
+ if (s && strlen(s) > length)
+ s[length] = 0;
+ return s;
+}
+
+/**
+ Strchr and strrchr_m are very hard to do on general multi-byte strings.
+ We convert via ucs2 for now.
+**/
+
+char *strchr_m(const char *src, char c)
+{
+ wpstring ws;
+ pstring s2;
+ smb_ucs2_t *p;
+ const char *s;
+
+ /* this is quite a common operation, so we want it to be
+ fast. We optimise for the ascii case, knowing that all our
+ supported multi-byte character sets are ascii-compatible
+ (ie. they match for the first 128 chars) */
+
+ for (s = src; *s && !(((unsigned char)s[0]) & 0x80); s++) {
+ if (*s == c)
+ return (char *)s;
+ }
+
+ if (!*s)
+ return NULL;
+
+#ifdef BROKEN_UNICODE_COMPOSE_CHARACTERS
+ /* With compose characters we must restart from the beginning. JRA. */
+ s = src;
+#endif
+
+ push_ucs2(NULL, ws, s, sizeof(ws), STR_TERMINATE);
+ p = strchr_w(ws, UCS2_CHAR(c));
+ if (!p)
+ return NULL;
+ *p = 0;
+ pull_ucs2_pstring(s2, ws);
+ return (char *)(s+strlen(s2));
+}
+
+char *strrchr_m(const char *s, char c)
+{
+ /* this is quite a common operation, so we want it to be
+ fast. We optimise for the ascii case, knowing that all our
+ supported multi-byte character sets are ascii-compatible
+ (ie. they match for the first 128 chars). Also, in Samba
+ we only search for ascii characters in 'c' and that
+ in all mb character sets with a compound character
+ containing c, if 'c' is not a match at position
+ p, then p[-1] > 0x7f. JRA. */
+
+ {
+ size_t len = strlen(s);
+ const char *cp = s;
+ BOOL got_mb = False;
+
+ if (len == 0)
+ return NULL;
+ cp += (len - 1);
+ do {
+ if (c == *cp) {
+ /* Could be a match. Part of a multibyte ? */
+ if ((cp > s) && (((unsigned char)cp[-1]) & 0x80)) {
+ /* Yep - go slow :-( */
+ got_mb = True;
+ break;
+ }
+ /* No - we have a match ! */
+ return (char *)cp;
+ }
+ } while (cp-- != s);
+ if (!got_mb)
+ return NULL;
+ }
+
+ /* String contained a non-ascii char. Slow path. */
+ {
+ wpstring ws;
+ pstring s2;
+ smb_ucs2_t *p;
+
+ push_ucs2(NULL, ws, s, sizeof(ws), STR_TERMINATE);
+ p = strrchr_w(ws, UCS2_CHAR(c));
+ if (!p)
+ return NULL;
+ *p = 0;
+ pull_ucs2_pstring(s2, ws);
+ return (char *)(s+strlen(s2));
+ }
+}
+
+/***********************************************************************
+ Return the equivalent of doing strrchr 'n' times - always going
+ backwards.
+***********************************************************************/
+
+char *strnrchr_m(const char *s, char c, unsigned int n)
+{
+ wpstring ws;
+ pstring s2;
+ smb_ucs2_t *p;
+
+ push_ucs2(NULL, ws, s, sizeof(ws), STR_TERMINATE);
+ p = strnrchr_w(ws, UCS2_CHAR(c), n);
+ if (!p)
+ return NULL;
+ *p = 0;
+ pull_ucs2_pstring(s2, ws);
+ return (char *)(s+strlen(s2));
+}
+
+/***********************************************************************
+ strstr_m - We convert via ucs2 for now.
+***********************************************************************/
+
+char *strstr_m(const char *src, const char *findstr)
+{
+ smb_ucs2_t *p;
+ smb_ucs2_t *src_w, *find_w;
+ const char *s;
+ char *s2;
+ char *retp;
+
+ size_t findstr_len = 0;
+
+ /* for correctness */
+ if (!findstr[0]) {
+ return src;
+ }
+
+ /* Samba does single character findstr calls a *lot*. */
+ if (findstr[1] == '\0')
+ return strchr_m(src, *findstr);
+
+ /* We optimise for the ascii case, knowing that all our
+ supported multi-byte character sets are ascii-compatible
+ (ie. they match for the first 128 chars) */
+
+ for (s = src; *s && !(((unsigned char)s[0]) & 0x80); s++) {
+ if (*s == *findstr) {
+ if (!findstr_len)
+ findstr_len = strlen(findstr);
+
+ if (strncmp(s, findstr, findstr_len) == 0) {
+ return (char *)s;
+ }
+ }
+ }
+
+ if (!*s)
+ return NULL;
+
+#if 1 /* def BROKEN_UNICODE_COMPOSE_CHARACTERS */
+ /* 'make check' fails unless we do this */
+
+ /* With compose characters we must restart from the beginning. JRA. */
+ s = src;
+#endif
+
+ if (push_ucs2_allocate(&src_w, src) == (size_t)-1) {
+ DEBUG(0,("strstr_m: src malloc fail\n"));
+ return NULL;
+ }
+
+ if (push_ucs2_allocate(&find_w, findstr) == (size_t)-1) {
+ SAFE_FREE(src_w);
+ DEBUG(0,("strstr_m: find malloc fail\n"));
+ return NULL;
+ }
+
+ p = strstr_w(src_w, find_w);
+
+ if (!p) {
+ SAFE_FREE(src_w);
+ SAFE_FREE(find_w);
+ return NULL;
+ }
+
+ *p = 0;
+ if (pull_ucs2_allocate(&s2, src_w) == (size_t)-1) {
+ SAFE_FREE(src_w);
+ SAFE_FREE(find_w);
+ DEBUG(0,("strstr_m: dest malloc fail\n"));
+ return NULL;
+ }
+ retp = (char *)(s+strlen(s2));
+ SAFE_FREE(src_w);
+ SAFE_FREE(find_w);
+ SAFE_FREE(s2);
+ return retp;
+}
+
+/**
+ Convert a string to lower case.
+**/
+
+void strlower_m(char *s)
+{
+ size_t len;
+
+ /* this is quite a common operation, so we want it to be
+ fast. We optimise for the ascii case, knowing that all our
+ supported multi-byte character sets are ascii-compatible
+ (ie. they match for the first 128 chars) */
+
+ while (*s && !(((unsigned char)s[0]) & 0x80)) {
+ *s = tolower((unsigned char)*s);
+ s++;
+ }
+
+ if (!*s)
+ return;
+
+ /* I assume that lowercased string takes the same number of bytes
+ * as source string even in UTF-8 encoding. (VIV) */
+ len = strlen(s) + 1;
+ errno = 0;
+ unix_strlower(s,len,s,len);
+ /* Catch mb conversion errors that may not terminate. */
+ if (errno)
+ s[len-1] = '\0';
+}
+
+/**
+ Convert a string to upper case.
+**/
+
+void strupper_m(char *s)
+{
+ size_t len;
+
+ /* this is quite a common operation, so we want it to be
+ fast. We optimise for the ascii case, knowing that all our
+ supported multi-byte character sets are ascii-compatible
+ (ie. they match for the first 128 chars) */
+
+ while (*s && !(((unsigned char)s[0]) & 0x80)) {
+ *s = toupper((unsigned char)*s);
+ s++;
+ }
+
+ if (!*s)
+ return;
+
+ /* I assume that lowercased string takes the same number of bytes
+ * as source string even in multibyte encoding. (VIV) */
+ len = strlen(s) + 1;
+ errno = 0;
+ unix_strupper(s,len,s,len);
+ /* Catch mb conversion errors that may not terminate. */
+ if (errno)
+ s[len-1] = '\0';
+}
+
+/**
+ Return a RFC2254 binary string representation of a buffer.
+ Used in LDAP filters.
+ Caller must free.
+**/
+
+char *binary_string(char *buf, int len)
+{
+ char *s;
+ int i, j;
+ const char *hex = "0123456789ABCDEF";
+ s = malloc(len * 3 + 1);
+ if (!s)
+ return NULL;
+ for (j=i=0;i<len;i++) {
+ s[j] = '\\';
+ s[j+1] = hex[((unsigned char)buf[i]) >> 4];
+ s[j+2] = hex[((unsigned char)buf[i]) & 0xF];
+ j += 3;
+ }
+ s[j] = 0;
+ return s;
+}
+
+/**
+ Just a typesafety wrapper for snprintf into a pstring.
+**/
+
+ int pstr_sprintf(pstring s, const char *fmt, ...)
+{
+ va_list ap;
+ int ret;
+
+ va_start(ap, fmt);
+ ret = vsnprintf(s, PSTRING_LEN, fmt, ap);
+ va_end(ap);
+ return ret;
+}
+
+
+/**
+ Just a typesafety wrapper for snprintf into a fstring.
+**/
+
+int fstr_sprintf(fstring s, const char *fmt, ...)
+{
+ va_list ap;
+ int ret;
+
+ va_start(ap, fmt);
+ ret = vsnprintf(s, FSTRING_LEN, fmt, ap);
+ va_end(ap);
+ return ret;
+}
+
+
+#ifndef HAVE_STRNDUP
+/**
+ Some platforms don't have strndup.
+**/
+
+ char *strndup(const char *s, size_t n)
+{
+ char *ret;
+
+ n = strnlen(s, n);
+ ret = malloc(n+1);
+ if (!ret)
+ return NULL;
+ memcpy(ret, s, n);
+ ret[n] = 0;
+
+ return ret;
+}
+#endif
+
+#ifndef HAVE_STRNLEN
+/**
+ Some platforms don't have strnlen
+**/
+
+ size_t strnlen(const char *s, size_t n)
+{
+ int i;
+ for (i=0; s[i] && i<n; i++)
+ /* noop */ ;
+ return i;
+}
+#endif
+
+/**
+ List of Strings manipulation functions
+**/
+
+#define S_LIST_ABS 16 /* List Allocation Block Size */
+
+char **str_list_make(const char *string, const char *sep)
+{
+ char **list, **rlist;
+ const char *str;
+ char *s;
+ int num, lsize;
+ pstring tok;
+
+ if (!string || !*string)
+ return NULL;
+ s = strdup(string);
+ if (!s) {
+ DEBUG(0,("str_list_make: Unable to allocate memory"));
+ return NULL;
+ }
+ if (!sep) sep = LIST_SEP;
+
+ num = lsize = 0;
+ list = NULL;
+
+ str = s;
+ while (next_token(&str, tok, sep, sizeof(tok))) {
+ if (num == lsize) {
+ lsize += S_LIST_ABS;
+ rlist = (char **)Realloc(list, ((sizeof(char **)) * (lsize +1)));
+ if (!rlist) {
+ DEBUG(0,("str_list_make: Unable to allocate memory"));
+ str_list_free(&list);
+ SAFE_FREE(s);
+ return NULL;
+ } else
+ list = rlist;
+ memset (&list[num], 0, ((sizeof(char**)) * (S_LIST_ABS +1)));
+ }
+
+ list[num] = strdup(tok);
+ if (!list[num]) {
+ DEBUG(0,("str_list_make: Unable to allocate memory"));
+ str_list_free(&list);
+ SAFE_FREE(s);
+ return NULL;
+ }
+
+ num++;
+ }
+
+ SAFE_FREE(s);
+ return list;
+}
+
+BOOL str_list_copy(char ***dest, const char **src)
+{
+ char **list, **rlist;
+ int num, lsize;
+
+ *dest = NULL;
+ if (!src)
+ return False;
+
+ num = lsize = 0;
+ list = NULL;
+
+ while (src[num]) {
+ if (num == lsize) {
+ lsize += S_LIST_ABS;
+ rlist = (char **)Realloc(list, ((sizeof(char **)) * (lsize +1)));
+ if (!rlist) {
+ DEBUG(0,("str_list_copy: Unable to re-allocate memory"));
+ str_list_free(&list);
+ return False;
+ } else
+ list = rlist;
+ memset (&list[num], 0, ((sizeof(char **)) * (S_LIST_ABS +1)));
+ }
+
+ list[num] = strdup(src[num]);
+ if (!list[num]) {
+ DEBUG(0,("str_list_copy: Unable to allocate memory"));
+ str_list_free(&list);
+ return False;
+ }
+
+ num++;
+ }
+
+ *dest = list;
+ return True;
+}
+
+/**
+ * Return true if all the elements of the list match exactly.
+ **/
+BOOL str_list_compare(char **list1, char **list2)
+{
+ int num;
+
+ if (!list1 || !list2)
+ return (list1 == list2);
+
+ for (num = 0; list1[num]; num++) {
+ if (!list2[num])
+ return False;
+ if (!strcsequal(list1[num], list2[num]))
+ return False;
+ }
+ if (list2[num])
+ return False; /* if list2 has more elements than list1 fail */
+
+ return True;
+}
+
+void str_list_free(char ***list)
+{
+ char **tlist;
+
+ if (!list || !*list)
+ return;
+ tlist = *list;
+ for(; *tlist; tlist++)
+ SAFE_FREE(*tlist);
+ SAFE_FREE(*list);
+}
+
+/******************************************************************************
+ version of standard_sub_basic() for string lists; uses alloc_sub_basic()
+ for the work
+ *****************************************************************************/
+
+BOOL str_list_sub_basic( char **list, const char *smb_name )
+{
+ char *s, *tmpstr;
+
+ while ( *list ) {
+ s = *list;
+ tmpstr = alloc_sub_basic(smb_name, s);
+ if ( !tmpstr ) {
+ DEBUG(0,("str_list_sub_basic: alloc_sub_basic() return NULL!\n"));
+ return False;
+ }
+
+ *list = tmpstr;
+
+ list++;
+ }
+
+ return True;
+}
+
+/******************************************************************************
+ substritute a specific pattern in a string list
+ *****************************************************************************/
+
+BOOL str_list_substitute(char **list, const char *pattern, const char *insert)
+{
+ char *p, *s, *t;
+ ssize_t ls, lp, li, ld, i, d;
+
+ if (!list)
+ return False;
+ if (!pattern)
+ return False;
+ if (!insert)
+ return False;
+
+ lp = (ssize_t)strlen(pattern);
+ li = (ssize_t)strlen(insert);
+ ld = li -lp;
+
+ while (*list) {
+ s = *list;
+ ls = (ssize_t)strlen(s);
+
+ while ((p = strstr_m(s, pattern))) {
+ t = *list;
+ d = p -t;
+ if (ld) {
+ t = (char *) malloc(ls +ld +1);
+ if (!t) {
+ DEBUG(0,("str_list_substitute: Unable to allocate memory"));
+ return False;
+ }
+ memcpy(t, *list, d);
+ memcpy(t +d +li, p +lp, ls -d -lp +1);
+ SAFE_FREE(*list);
+ *list = t;
+ ls += ld;
+ s = t +d +li;
+ }
+
+ for (i = 0; i < li; i++) {
+ switch (insert[i]) {
+ case '`':
+ case '"':
+ case '\'':
+ case ';':
+ case '$':
+ case '%':
+ case '\r':
+ case '\n':
+ t[d +i] = '_';
+ break;
+ default:
+ t[d +i] = insert[i];
+ }
+ }
+ }
+
+
+ list++;
+ }
+
+ return True;
+}
+
+
+#define IPSTR_LIST_SEP ","
+#define IPSTR_LIST_CHAR ','
+
+/**
+ * Add ip string representation to ipstr list. Used also
+ * as part of @function ipstr_list_make
+ *
+ * @param ipstr_list pointer to string containing ip list;
+ * MUST BE already allocated and IS reallocated if necessary
+ * @param ipstr_size pointer to current size of ipstr_list (might be changed
+ * as a result of reallocation)
+ * @param ip IP address which is to be added to list
+ * @return pointer to string appended with new ip and possibly
+ * reallocated to new length
+ **/
+
+char* ipstr_list_add(char** ipstr_list, const struct ip_service *service)
+{
+ char* new_ipstr = NULL;
+
+ /* arguments checking */
+ if (!ipstr_list || !service) return NULL;
+
+ /* attempt to convert ip to a string and append colon separator to it */
+ if (*ipstr_list) {
+ asprintf(&new_ipstr, "%s%s%s:%d", *ipstr_list, IPSTR_LIST_SEP,
+ inet_ntoa(service->ip), service->port);
+ SAFE_FREE(*ipstr_list);
+ } else {
+ asprintf(&new_ipstr, "%s:%d", inet_ntoa(service->ip), service->port);
+ }
+ *ipstr_list = new_ipstr;
+ return *ipstr_list;
+}
+
+
+/**
+ * Allocate and initialise an ipstr list using ip adresses
+ * passed as arguments.
+ *
+ * @param ipstr_list pointer to string meant to be allocated and set
+ * @param ip_list array of ip addresses to place in the list
+ * @param ip_count number of addresses stored in ip_list
+ * @return pointer to allocated ip string
+ **/
+
+char* ipstr_list_make(char** ipstr_list, const struct ip_service* ip_list, int ip_count)
+{
+ int i;
+
+ /* arguments checking */
+ if (!ip_list && !ipstr_list) return 0;
+
+ *ipstr_list = NULL;
+
+ /* process ip addresses given as arguments */
+ for (i = 0; i < ip_count; i++)
+ *ipstr_list = ipstr_list_add(ipstr_list, &ip_list[i]);
+
+ return (*ipstr_list);
+}
+
+
+/**
+ * Parse given ip string list into array of ip addresses
+ * (as ip_service structures)
+ * e.g. 192.168.1.100:389,192.168.1.78, ...
+ *
+ * @param ipstr ip string list to be parsed
+ * @param ip_list pointer to array of ip addresses which is
+ * allocated by this function and must be freed by caller
+ * @return number of succesfully parsed addresses
+ **/
+
+int ipstr_list_parse(const char* ipstr_list, struct ip_service **ip_list)
+{
+ fstring token_str;
+ size_t count;
+ int i;
+
+ if (!ipstr_list || !ip_list)
+ return 0;
+
+ count = count_chars(ipstr_list, IPSTR_LIST_CHAR) + 1;
+ if ( (*ip_list = (struct ip_service*)malloc(count * sizeof(struct ip_service))) == NULL ) {
+ DEBUG(0,("ipstr_list_parse: malloc failed for %lu entries\n", (unsigned long)count));
+ return 0;
+ }
+
+ for ( i=0;
+ next_token(&ipstr_list, token_str, IPSTR_LIST_SEP, FSTRING_LEN) && i<count;
+ i++ )
+ {
+ struct in_addr addr;
+ unsigned port = 0;
+ char *p = strchr(token_str, ':');
+
+ if (p) {
+ *p = 0;
+ port = atoi(p+1);
+ }
+
+ /* convert single token to ip address */
+ if ( (addr.s_addr = inet_addr(token_str)) == INADDR_NONE )
+ break;
+
+ (*ip_list)[i].ip = addr;
+ (*ip_list)[i].port = port;
+ }
+
+ return count;
+}
+
+
+/**
+ * Safely free ip string list
+ *
+ * @param ipstr_list ip string list to be freed
+ **/
+
+void ipstr_list_free(char* ipstr_list)
+{
+ SAFE_FREE(ipstr_list);
+}
+
+
+/**
+ Unescape a URL encoded string, in place.
+**/
+
+void rfc1738_unescape(char *buf)
+{
+ char *p=buf;
+
+ while (p && *p && (p=strchr_m(p,'%'))) {
+ int c1 = p[1];
+ int c2 = p[2];
+
+ if (c1 >= '0' && c1 <= '9')
+ c1 = c1 - '0';
+ else if (c1 >= 'A' && c1 <= 'F')
+ c1 = 10 + c1 - 'A';
+ else if (c1 >= 'a' && c1 <= 'f')
+ c1 = 10 + c1 - 'a';
+ else {p++; continue;}
+
+ if (c2 >= '0' && c2 <= '9')
+ c2 = c2 - '0';
+ else if (c2 >= 'A' && c2 <= 'F')
+ c2 = 10 + c2 - 'A';
+ else if (c2 >= 'a' && c2 <= 'f')
+ c2 = 10 + c2 - 'a';
+ else {p++; continue;}
+
+ *p = (c1<<4) | c2;
+
+ memmove(p+1, p+3, strlen(p+3)+1);
+ p++;
+ }
+}
+
+static const char *b64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+
+/**
+ * Decode a base64 string into a DATA_BLOB - simple and slow algorithm
+ **/
+DATA_BLOB base64_decode_data_blob(const char *s)
+{
+ int bit_offset, byte_offset, idx, i, n;
+ DATA_BLOB decoded = data_blob(s, strlen(s)+1);
+ unsigned char *d = decoded.data;
+ char *p;
+
+ n=i=0;
+
+ while (*s && (p=strchr_m(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 (*s == '=') n -= 1;
+
+ /* fix up length */
+ decoded.length = n;
+ return decoded;
+}
+
+/**
+ * Decode a base64 string in-place - wrapper for the above
+ **/
+void base64_decode_inplace(char *s)
+{
+ DATA_BLOB decoded = base64_decode_data_blob(s);
+ memcpy(s, decoded.data, decoded.length);
+ /* null terminate */
+ s[decoded.length] = '\0';
+
+ data_blob_free(&decoded);
+}
+
+/**
+ * Encode a base64 string into a malloc()ed string caller to free.
+ *
+ *From SQUID: adopted from http://ftp.sunet.se/pub2/gnu/vm/base64-encode.c with adjustments
+ **/
+char * base64_encode_data_blob(DATA_BLOB data)
+{
+ int bits = 0;
+ int char_count = 0;
+ size_t out_cnt = 0;
+ size_t len = data.length;
+ size_t output_len = data.length * 2;
+ char *result = malloc(output_len); /* get us plenty of space */
+
+ while (len-- && out_cnt < (data.length * 2) - 5) {
+ int c = (unsigned char) *(data.data++);
+ bits += c;
+ char_count++;
+ if (char_count == 3) {
+ result[out_cnt++] = b64[bits >> 18];
+ result[out_cnt++] = b64[(bits >> 12) & 0x3f];
+ result[out_cnt++] = b64[(bits >> 6) & 0x3f];
+ result[out_cnt++] = b64[bits & 0x3f];
+ bits = 0;
+ char_count = 0;
+ } else {
+ bits <<= 8;
+ }
+ }
+ if (char_count != 0) {
+ bits <<= 16 - (8 * char_count);
+ result[out_cnt++] = b64[bits >> 18];
+ result[out_cnt++] = b64[(bits >> 12) & 0x3f];
+ if (char_count == 1) {
+ result[out_cnt++] = '=';
+ result[out_cnt++] = '=';
+ } else {
+ result[out_cnt++] = b64[(bits >> 6) & 0x3f];
+ result[out_cnt++] = '=';
+ }
+ }
+ result[out_cnt] = '\0'; /* terminate */
+ return result;
+}
+
+/* read a SMB_BIG_UINT from a string */
+SMB_BIG_UINT STR_TO_SMB_BIG_UINT(const char *nptr, const char **entptr)
+{
+
+ SMB_BIG_UINT val = -1;
+ const char *p = nptr;
+
+ while (p && *p && isspace(*p))
+ p++;
+#ifdef LARGE_SMB_OFF_T
+ sscanf(p,"%llu",&val);
+#else /* LARGE_SMB_OFF_T */
+ sscanf(p,"%lu",&val);
+#endif /* LARGE_SMB_OFF_T */
+ if (entptr) {
+ while (p && *p && isdigit(*p))
+ p++;
+ *entptr = p;
+ }
+
+ return val;
+}
+
+void string_append(char **left, const char *right)
+{
+ int new_len = strlen(right) + 1;
+
+ if (*left == NULL) {
+ *left = malloc(new_len);
+ *left[0] = '\0';
+ } else {
+ new_len += strlen(*left);
+ *left = Realloc(*left, new_len);
+ }
+
+ if (*left == NULL)
+ return;
+
+ safe_strcat(*left, right, new_len-1);
+}
diff --git a/source/lib/util_unistr.c b/source/lib/util_unistr.c
new file mode 100644
index 00000000000..005f10a4c0a
--- /dev/null
+++ b/source/lib/util_unistr.c
@@ -0,0 +1,847 @@
+/*
+ Unix SMB/CIFS implementation.
+ Samba utility functions
+ Copyright (C) Andrew Tridgell 1992-2001
+ Copyright (C) Simo Sorce 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 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"
+
+#ifndef MAXUNI
+#define MAXUNI 1024
+#endif
+
+/* these 3 tables define the unicode case handling. They are loaded
+ at startup either via mmap() or read() from the lib directory */
+static smb_ucs2_t *upcase_table;
+static smb_ucs2_t *lowcase_table;
+static uint8 *valid_table;
+
+/**
+ * This table says which Unicode characters are valid dos
+ * characters.
+ *
+ * Each value is just a single bit.
+ **/
+static uint8 doschar_table[8192]; /* 65536 characters / 8 bits/byte */
+
+
+/**
+ * Load or generate the case handling tables.
+ *
+ * The case tables are defined in UCS2 and don't depend on any
+ * configured parameters, so they never need to be reloaded.
+ **/
+void load_case_tables(void)
+{
+ static int initialised;
+ int i;
+
+ if (initialised) return;
+ initialised = 1;
+
+ upcase_table = map_file(lib_path("upcase.dat"), 0x20000);
+ lowcase_table = map_file(lib_path("lowcase.dat"), 0x20000);
+
+ /* we would like Samba to limp along even if these tables are
+ not available */
+ if (!upcase_table) {
+ DEBUG(1,("creating lame upcase table\n"));
+ upcase_table = malloc(0x20000);
+ for (i=0;i<0x10000;i++) {
+ smb_ucs2_t v;
+ SSVAL(&v, 0, i);
+ upcase_table[v] = i;
+ }
+ for (i=0;i<256;i++) {
+ smb_ucs2_t v;
+ SSVAL(&v, 0, UCS2_CHAR(i));
+ upcase_table[v] = UCS2_CHAR(islower(i)?toupper(i):i);
+ }
+ }
+
+ if (!lowcase_table) {
+ DEBUG(1,("creating lame lowcase table\n"));
+ lowcase_table = malloc(0x20000);
+ for (i=0;i<0x10000;i++) {
+ smb_ucs2_t v;
+ SSVAL(&v, 0, i);
+ lowcase_table[v] = i;
+ }
+ for (i=0;i<256;i++) {
+ smb_ucs2_t v;
+ SSVAL(&v, 0, UCS2_CHAR(i));
+ lowcase_table[v] = UCS2_CHAR(isupper(i)?tolower(i):i);
+ }
+ }
+}
+
+/*
+ see if a ucs2 character can be mapped correctly to a dos character
+ and mapped back to the same character in ucs2
+*/
+int check_dos_char(smb_ucs2_t c)
+{
+ lazy_initialize_conv();
+
+ /* Find the right byte, and right bit within the byte; return
+ * 1 or 0 */
+ return (doschar_table[(c & 0xffff) / 8] & (1 << (c & 7))) != 0;
+}
+
+
+static int check_dos_char_slowly(smb_ucs2_t c)
+{
+ char buf[10];
+ smb_ucs2_t c2 = 0;
+ int len1, len2;
+ len1 = convert_string(CH_UCS2, CH_DOS, &c, 2, buf, sizeof(buf),False);
+ if (len1 == 0) return 0;
+ len2 = convert_string(CH_DOS, CH_UCS2, buf, len1, &c2, 2,False);
+ if (len2 != 2) return 0;
+ return (c == c2);
+}
+
+
+/**
+ * Fill out doschar table the hard way, by examining each character
+ **/
+void init_doschar_table(void)
+{
+ int i, j, byteval;
+
+ /* For each byte of packed table */
+
+ for (i = 0; i <= 0xffff; i += 8) {
+ byteval = 0;
+ for (j = 0; j <= 7; j++) {
+ smb_ucs2_t c;
+
+ c = i + j;
+
+ if (check_dos_char_slowly(c))
+ byteval |= 1 << j;
+ }
+ doschar_table[i/8] = byteval;
+ }
+}
+
+
+/**
+ * Load the valid character map table from <tt>valid.dat</tt> or
+ * create from the configured codepage.
+ *
+ * This function is called whenever the configuration is reloaded.
+ * However, the valid character table is not changed if it's loaded
+ * from a file, because we can't unmap files.
+ **/
+void init_valid_table(void)
+{
+ static int mapped_file;
+ int i;
+ const char *allowed = ".!#$%&'()_-@^`~";
+ uint8 *valid_file;
+
+ if (mapped_file) {
+ /* Can't unmap files, so stick with what we have */
+ return;
+ }
+
+ valid_file = map_file(lib_path("valid.dat"), 0x10000);
+ if (valid_file) {
+ valid_table = valid_file;
+ mapped_file = 1;
+ return;
+ }
+
+ /* Otherwise, we're using a dynamically created valid_table.
+ * It might need to be regenerated if the code page changed.
+ * We know that we're not using a mapped file, so we can
+ * free() the old one. */
+ if (valid_table) free(valid_table);
+
+ DEBUG(2,("creating default valid table\n"));
+ valid_table = malloc(0x10000);
+ for (i=0;i<128;i++)
+ valid_table[i] = isalnum(i) || strchr(allowed,i);
+
+ for (;i<0x10000;i++) {
+ smb_ucs2_t c;
+ SSVAL(&c, 0, i);
+ valid_table[i] = check_dos_char(c);
+ }
+}
+
+
+
+/*******************************************************************
+ Write a string in (little-endian) unicode format. src is in
+ the current DOS codepage. len is the length in bytes of the
+ string pointed to by dst.
+
+ if null_terminate is True then null terminate the packet (adds 2 bytes)
+
+ the return value is the length in bytes consumed by the string, including the
+ null termination if applied
+********************************************************************/
+
+size_t dos_PutUniCode(char *dst,const char *src, ssize_t len, BOOL null_terminate)
+{
+ return push_ucs2(NULL, dst, src, len,
+ STR_UNICODE|STR_NOALIGN | (null_terminate?STR_TERMINATE:0));
+}
+
+
+/*******************************************************************
+ Skip past a unicode string, but not more than len. Always move
+ past a terminating zero if found.
+********************************************************************/
+
+char *skip_unibuf(char *src, size_t len)
+{
+ char *srcend = src + len;
+
+ while (src < srcend && SVAL(src,0))
+ src += 2;
+
+ if(!SVAL(src,0))
+ src += 2;
+
+ return src;
+}
+
+/* Copy a string from little-endian or big-endian unicode source (depending
+ * on flags) to internal samba format destination
+ */
+int rpcstr_pull(char* dest, void *src, int dest_len, int src_len, int flags)
+{
+ if (!src) {
+ dest[0] = 0;
+ return 0;
+ }
+ if(dest_len==-1) dest_len=MAXUNI-3;
+ return pull_ucs2(NULL, dest, src, dest_len, src_len, flags|STR_UNICODE|STR_NOALIGN);
+}
+
+/* Copy a string from a unistr2 source to internal samba format
+ destination. Use this instead of direct calls to rpcstr_pull() to avoid
+ having to determine whether the source string is null terminated. */
+
+int rpcstr_pull_unistr2_fstring(char *dest, UNISTR2 *src)
+{
+ return pull_ucs2(NULL, dest, src->buffer, sizeof(fstring),
+ src->uni_str_len * 2, 0);
+}
+
+/* Converts a string from internal samba format to unicode
+ */
+int rpcstr_push(void* dest, const char *src, int dest_len, int flags)
+{
+ return push_ucs2(NULL, dest, src, dest_len, flags|STR_UNICODE|STR_NOALIGN);
+}
+
+/*******************************************************************
+ Return a DOS codepage version of a little-endian unicode string.
+ len is the filename length (ignoring any terminating zero) in uin16
+ units. Always null terminates.
+ Hack alert: uses fixed buffer(s).
+********************************************************************/
+char *dos_unistrn2(const uint16 *src, int len)
+{
+ static char lbufs[8][MAXUNI];
+ static int nexti;
+ char *lbuf = lbufs[nexti];
+ nexti = (nexti+1)%8;
+ pull_ucs2(NULL, lbuf, src, MAXUNI-3, len*2, STR_NOALIGN);
+ return lbuf;
+}
+
+/*******************************************************************
+ Convert a (little-endian) UNISTR2 structure to an ASCII string
+********************************************************************/
+void unistr2_to_ascii(char *dest, const UNISTR2 *str, size_t maxlen)
+{
+ if (str == NULL) {
+ *dest='\0';
+ return;
+ }
+ pull_ucs2(NULL, dest, str->buffer, maxlen, str->uni_str_len*2, STR_NOALIGN);
+}
+
+/*******************************************************************
+give a static string for displaying a UNISTR2
+********************************************************************/
+const char *unistr2_static(const UNISTR2 *str)
+{
+ static pstring ret;
+ unistr2_to_ascii(ret, str, sizeof(ret));
+ return ret;
+}
+
+
+/*******************************************************************
+ duplicate a UNISTR2 string into a null terminated char*
+ using a talloc context
+********************************************************************/
+char *unistr2_tdup(TALLOC_CTX *ctx, const UNISTR2 *str)
+{
+ char *s;
+ int maxlen = (str->uni_str_len+1)*4;
+ if (!str->buffer) return NULL;
+ s = (char *)talloc(ctx, maxlen); /* convervative */
+ if (!s) return NULL;
+ pull_ucs2(NULL, s, str->buffer, maxlen, str->uni_str_len*2,
+ STR_NOALIGN);
+ return s;
+}
+
+
+/*******************************************************************
+Return a number stored in a buffer
+********************************************************************/
+
+uint32 buffer2_to_uint32(BUFFER2 *str)
+{
+ if (str->buf_len == 4)
+ return IVAL(str->buffer, 0);
+ else
+ return 0;
+}
+
+/*******************************************************************
+ Convert a wchar to upper case.
+********************************************************************/
+
+smb_ucs2_t toupper_w(smb_ucs2_t val)
+{
+ return upcase_table[SVAL(&val,0)];
+}
+
+/*******************************************************************
+ Convert a wchar to lower case.
+********************************************************************/
+
+smb_ucs2_t tolower_w( smb_ucs2_t val )
+{
+ return lowcase_table[SVAL(&val,0)];
+
+}
+
+/*******************************************************************
+determine if a character is lowercase
+********************************************************************/
+BOOL islower_w(smb_ucs2_t c)
+{
+ return upcase_table[SVAL(&c,0)] != c;
+}
+
+/*******************************************************************
+determine if a character is uppercase
+********************************************************************/
+BOOL isupper_w(smb_ucs2_t c)
+{
+ return lowcase_table[SVAL(&c,0)] != c;
+}
+
+
+/*******************************************************************
+determine if a character is valid in a 8.3 name
+********************************************************************/
+BOOL isvalid83_w(smb_ucs2_t c)
+{
+ return valid_table[SVAL(&c,0)] != 0;
+}
+
+/*******************************************************************
+ Count the number of characters in a smb_ucs2_t string.
+********************************************************************/
+size_t strlen_w(const smb_ucs2_t *src)
+{
+ size_t len;
+
+ for(len = 0; *src++; len++) ;
+
+ return len;
+}
+
+/*******************************************************************
+ Count up to max number of characters in a smb_ucs2_t string.
+********************************************************************/
+size_t strnlen_w(const smb_ucs2_t *src, size_t max)
+{
+ size_t len;
+
+ for(len = 0; *src++ && (len < max); len++) ;
+
+ return len;
+}
+
+/*******************************************************************
+ Wide strchr().
+********************************************************************/
+
+smb_ucs2_t *strchr_w(const smb_ucs2_t *s, smb_ucs2_t c)
+{
+ while (*s != 0) {
+ if (c == *s) return (smb_ucs2_t *)s;
+ s++;
+ }
+ if (c == *s) return (smb_ucs2_t *)s;
+
+ return NULL;
+}
+
+smb_ucs2_t *strchr_wa(const smb_ucs2_t *s, char c)
+{
+ return strchr_w(s, UCS2_CHAR(c));
+}
+
+/*******************************************************************
+ Wide strrchr().
+********************************************************************/
+
+smb_ucs2_t *strrchr_w(const smb_ucs2_t *s, smb_ucs2_t c)
+{
+ const smb_ucs2_t *p = s;
+ int len = strlen_w(s);
+ if (len == 0) return NULL;
+ p += (len - 1);
+ do {
+ if (c == *p) return (smb_ucs2_t *)p;
+ } while (p-- != s);
+ return NULL;
+}
+
+/*******************************************************************
+ Wide version of strrchr that returns after doing strrchr 'n' times.
+********************************************************************/
+
+smb_ucs2_t *strnrchr_w(const smb_ucs2_t *s, smb_ucs2_t c, unsigned int n)
+{
+ const smb_ucs2_t *p = s;
+ int len = strlen_w(s);
+ if (len == 0 || !n)
+ return NULL;
+ p += (len - 1);
+ do {
+ if (c == *p)
+ n--;
+
+ if (!n)
+ return (smb_ucs2_t *)p;
+ } while (p-- != s);
+ return NULL;
+}
+
+/*******************************************************************
+ Wide strstr().
+********************************************************************/
+
+smb_ucs2_t *strstr_w(const smb_ucs2_t *s, const smb_ucs2_t *ins)
+{
+ smb_ucs2_t *r;
+ size_t slen, inslen;
+
+ if (!s || !*s || !ins || !*ins) return NULL;
+ slen = strlen_w(s);
+ inslen = strlen_w(ins);
+ r = (smb_ucs2_t *)s;
+ while ((r = strchr_w(r, *ins))) {
+ if (strncmp_w(r, ins, inslen) == 0) return r;
+ r++;
+ }
+ return NULL;
+}
+
+/*******************************************************************
+ Convert a string to lower case.
+ return True if any char is converted
+********************************************************************/
+BOOL strlower_w(smb_ucs2_t *s)
+{
+ BOOL ret = False;
+ while (*s) {
+ smb_ucs2_t v = tolower_w(*s);
+ if (v != *s) {
+ *s = v;
+ ret = True;
+ }
+ s++;
+ }
+ return ret;
+}
+
+/*******************************************************************
+ Convert a string to upper case.
+ return True if any char is converted
+********************************************************************/
+BOOL strupper_w(smb_ucs2_t *s)
+{
+ BOOL ret = False;
+ while (*s) {
+ smb_ucs2_t v = toupper_w(*s);
+ if (v != *s) {
+ *s = v;
+ ret = True;
+ }
+ s++;
+ }
+ return ret;
+}
+
+/*******************************************************************
+ convert a string to "normal" form
+********************************************************************/
+void strnorm_w(smb_ucs2_t *s)
+{
+ extern int case_default;
+ if (case_default == CASE_UPPER)
+ strupper_w(s);
+ else
+ strlower_w(s);
+}
+
+int strcmp_w(const smb_ucs2_t *a, const smb_ucs2_t *b)
+{
+ while (*b && *a == *b) { a++; b++; }
+ return (*a - *b);
+ /* warning: if *a != *b and both are not 0 we retrun a random
+ greater or lesser than 0 number not realted to which
+ string is longer */
+}
+
+int strncmp_w(const smb_ucs2_t *a, const smb_ucs2_t *b, size_t len)
+{
+ size_t n = 0;
+ while ((n < len) && *b && *a == *b) { a++; b++; n++;}
+ return (len - n)?(*a - *b):0;
+}
+
+/*******************************************************************
+case insensitive string comparison
+********************************************************************/
+int strcasecmp_w(const smb_ucs2_t *a, const smb_ucs2_t *b)
+{
+ while (*b && toupper_w(*a) == toupper_w(*b)) { a++; b++; }
+ return (tolower_w(*a) - tolower_w(*b));
+}
+
+/*******************************************************************
+case insensitive string comparison, lenght limited
+********************************************************************/
+int strncasecmp_w(const smb_ucs2_t *a, const smb_ucs2_t *b, size_t len)
+{
+ size_t n = 0;
+ while ((n < len) && *b && (toupper_w(*a) == toupper_w(*b))) { a++; b++; n++; }
+ return (len - n)?(tolower_w(*a) - tolower_w(*b)):0;
+}
+
+/*******************************************************************
+ compare 2 strings
+********************************************************************/
+BOOL strequal_w(const smb_ucs2_t *s1, const smb_ucs2_t *s2)
+{
+ if (s1 == s2) return(True);
+ if (!s1 || !s2) return(False);
+
+ return(strcasecmp_w(s1,s2)==0);
+}
+
+/*******************************************************************
+ compare 2 strings up to and including the nth char.
+ ******************************************************************/
+BOOL strnequal_w(const smb_ucs2_t *s1,const smb_ucs2_t *s2,size_t n)
+{
+ if (s1 == s2) return(True);
+ if (!s1 || !s2 || !n) return(False);
+
+ return(strncasecmp_w(s1,s2,n)==0);
+}
+
+/*******************************************************************
+duplicate string
+********************************************************************/
+smb_ucs2_t *strdup_w(const smb_ucs2_t *src)
+{
+ return strndup_w(src, 0);
+}
+
+/* if len == 0 then duplicate the whole string */
+smb_ucs2_t *strndup_w(const smb_ucs2_t *src, size_t len)
+{
+ smb_ucs2_t *dest;
+
+ if (!len) len = strlen_w(src);
+ dest = (smb_ucs2_t *)malloc((len + 1) * sizeof(smb_ucs2_t));
+ if (!dest) {
+ DEBUG(0,("strdup_w: out of memory!\n"));
+ return NULL;
+ }
+
+ memcpy(dest, src, len * sizeof(smb_ucs2_t));
+ dest[len] = 0;
+
+ return dest;
+}
+
+/*******************************************************************
+copy a string with max len
+********************************************************************/
+
+smb_ucs2_t *strncpy_w(smb_ucs2_t *dest, const smb_ucs2_t *src, const size_t max)
+{
+ size_t len;
+
+ if (!dest || !src) return NULL;
+
+ for (len = 0; (src[len] != 0) && (len < max); len++)
+ dest[len] = src[len];
+ while (len < max)
+ dest[len++] = 0;
+
+ return dest;
+}
+
+
+/*******************************************************************
+append a string of len bytes and add a terminator
+********************************************************************/
+
+smb_ucs2_t *strncat_w(smb_ucs2_t *dest, const smb_ucs2_t *src, const size_t max)
+{
+ size_t start;
+ size_t len;
+
+ if (!dest || !src) return NULL;
+
+ start = strlen_w(dest);
+ len = strnlen_w(src, max);
+
+ memcpy(&dest[start], src, len*sizeof(smb_ucs2_t));
+ dest[start+len] = 0;
+
+ return dest;
+}
+
+smb_ucs2_t *strcat_w(smb_ucs2_t *dest, const smb_ucs2_t *src)
+{
+ size_t start;
+ size_t len;
+
+ if (!dest || !src) return NULL;
+
+ start = strlen_w(dest);
+ len = strlen_w(src);
+
+ memcpy(&dest[start], src, len*sizeof(smb_ucs2_t));
+ dest[start+len] = 0;
+
+ return dest;
+}
+
+
+/*******************************************************************
+replace any occurence of oldc with newc in unicode string
+********************************************************************/
+
+void string_replace_w(smb_ucs2_t *s, smb_ucs2_t oldc, smb_ucs2_t newc)
+{
+ for(;*s;s++) {
+ if(*s==oldc) *s=newc;
+ }
+}
+
+/*******************************************************************
+trim unicode string
+********************************************************************/
+
+BOOL trim_string_w(smb_ucs2_t *s, const smb_ucs2_t *front,
+ const smb_ucs2_t *back)
+{
+ BOOL ret = False;
+ size_t len, front_len, back_len;
+
+ if (!s || !*s) return False;
+
+ len = strlen_w(s);
+
+ if (front && *front) {
+ front_len = strlen_w(front);
+ while (len && strncmp_w(s, front, front_len) == 0) {
+ memmove(s, (s + front_len), (len - front_len + 1) * sizeof(smb_ucs2_t));
+ len -= front_len;
+ ret = True;
+ }
+ }
+
+ if (back && *back) {
+ back_len = strlen_w(back);
+ while (len && strncmp_w((s + (len - back_len)), back, back_len) == 0) {
+ s[len - back_len] = 0;
+ len -= back_len;
+ ret = True;
+ }
+ }
+
+ return ret;
+}
+
+/*
+ The *_wa() functions take a combination of 7 bit ascii
+ and wide characters They are used so that you can use string
+ functions combining C string constants with ucs2 strings
+
+ The char* arguments must NOT be multibyte - to be completely sure
+ of this only pass string constants */
+
+
+void pstrcpy_wa(smb_ucs2_t *dest, const char *src)
+{
+ int i;
+ for (i=0;i<PSTRING_LEN;i++) {
+ dest[i] = UCS2_CHAR(src[i]);
+ if (src[i] == 0) return;
+ }
+}
+
+int strcmp_wa(const smb_ucs2_t *a, const char *b)
+{
+ while (*b && *a == UCS2_CHAR(*b)) { a++; b++; }
+ return (*a - UCS2_CHAR(*b));
+}
+
+int strncmp_wa(const smb_ucs2_t *a, const char *b, size_t len)
+{
+ size_t n = 0;
+ while ((n < len) && *b && *a == UCS2_CHAR(*b)) { a++; b++; n++;}
+ return (len - n)?(*a - UCS2_CHAR(*b)):0;
+}
+
+smb_ucs2_t *strpbrk_wa(const smb_ucs2_t *s, const char *p)
+{
+ while (*s != 0) {
+ int i;
+ for (i=0; p[i] && *s != UCS2_CHAR(p[i]); i++)
+ ;
+ if (p[i]) return (smb_ucs2_t *)s;
+ s++;
+ }
+ return NULL;
+}
+
+smb_ucs2_t *strstr_wa(const smb_ucs2_t *s, const char *ins)
+{
+ smb_ucs2_t *r;
+ size_t slen, inslen;
+
+ if (!s || !*s || !ins || !*ins) return NULL;
+ slen = strlen_w(s);
+ inslen = strlen(ins);
+ r = (smb_ucs2_t *)s;
+ while ((r = strchr_w(r, UCS2_CHAR(*ins)))) {
+ if (strncmp_wa(r, ins, inslen) == 0) return r;
+ r++;
+ }
+ return NULL;
+}
+
+BOOL trim_string_wa(smb_ucs2_t *s, const char *front,
+ const char *back)
+{
+ wpstring f, b;
+
+ if (front) push_ucs2(NULL, f, front, sizeof(wpstring) - 1, STR_TERMINATE);
+ else *f = 0;
+ if (back) push_ucs2(NULL, b, back, sizeof(wpstring) - 1, STR_TERMINATE);
+ else *b = 0;
+ return trim_string_w(s, f, b);
+}
+
+/*******************************************************************
+ returns the length in number of wide characters
+ ******************************************************************/
+int unistrlen(uint16 *s)
+{
+ int len;
+
+ if (!s)
+ return -1;
+
+ for (len=0; *s; s++,len++);
+
+ return len;
+}
+
+/*******************************************************************
+ Strcpy for unicode strings. returns length (in num of wide chars)
+********************************************************************/
+
+int unistrcpy(uint16 *dst, uint16 *src)
+{
+ int num_wchars = 0;
+
+ while (*src) {
+ *dst++ = *src++;
+ num_wchars++;
+ }
+ *dst = 0;
+
+ return num_wchars;
+}
+
+/**
+ * Samba ucs2 type to UNISTR2 conversion
+ *
+ * @param ctx Talloc context to create the dst strcture (if null) and the
+ * contents of the unicode string.
+ * @param dst UNISTR2 destination. If equals null, then it's allocated.
+ * @param src smb_ucs2_t source.
+ * @param max_len maximum number of unicode characters to copy. If equals
+ * null, then null-termination of src is taken
+ *
+ * @return copied UNISTR2 destination
+ **/
+UNISTR2* ucs2_to_unistr2(TALLOC_CTX *ctx, UNISTR2* dst, smb_ucs2_t* src)
+{
+ size_t len;
+
+ if (!src)
+ return NULL;
+ len = strlen_w(src);
+
+ /* allocate UNISTR2 destination if not given */
+ if (!dst) {
+ dst = (UNISTR2*) talloc(ctx, sizeof(UNISTR2));
+ if (!dst)
+ return NULL;
+ }
+ if (!dst->buffer) {
+ dst->buffer = (uint16*) talloc(ctx, sizeof(uint16) * (len + 1));
+ if (!dst->buffer)
+ return NULL;
+ }
+
+ /* set UNISTR2 parameters */
+ dst->uni_max_len = len + 1;
+ dst->offset = 0;
+ dst->uni_str_len = len;
+
+ /* copy the actual unicode string */
+ strncpy_w(dst->buffer, src, dst->uni_max_len);
+
+ return dst;
+}
diff --git a/source/lib/util_uuid.c b/source/lib/util_uuid.c
new file mode 100644
index 00000000000..4c35236c902
--- /dev/null
+++ b/source/lib/util_uuid.c
@@ -0,0 +1,174 @@
+/*
+ * Unix SMB/CIFS implementation.
+ * UUID server routines
+ * Copyright (C) Theodore Ts'o 1996, 1997,
+ * Copyright (C) Jim McDonough <jmcd@us.ibm.com> 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 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"
+
+/*
+ * Offset between 15-Oct-1582 and 1-Jan-70
+ */
+#define TIME_OFFSET_HIGH 0x01B21DD2
+#define TIME_OFFSET_LOW 0x13814000
+
+void smb_uuid_pack(const struct uuid uu, UUID_FLAT *ptr)
+{
+ SIVAL(ptr, 0, uu.time_low);
+ SSVAL(ptr, 4, uu.time_mid);
+ SSVAL(ptr, 6, uu.time_hi_and_version);
+ memcpy(ptr+8, uu.clock_seq, 2);
+ memcpy(ptr+10, uu.node, 6);
+}
+
+void smb_uuid_unpack(const UUID_FLAT in, struct uuid *uu)
+{
+ uu->time_low = IVAL(in.info, 0);
+ uu->time_mid = SVAL(in.info, 4);
+ uu->time_hi_and_version = SVAL(in.info, 6);
+ memcpy(uu->clock_seq, in.info+8, 2);
+ memcpy(uu->node, in.info+10, 6);
+}
+
+const struct uuid smb_uuid_unpack_static(const UUID_FLAT in)
+{
+ static struct uuid uu;
+
+ smb_uuid_unpack(in, &uu);
+ return uu;
+}
+
+void smb_uuid_generate_random(struct uuid *uu)
+{
+ UUID_FLAT tmp;
+
+ generate_random_buffer(tmp.info, sizeof(tmp.info), True);
+ smb_uuid_unpack(tmp, uu);
+
+ uu->clock_seq[0] = (uu->clock_seq[0] & 0x3F) | 0x80;
+ uu->time_hi_and_version = (uu->time_hi_and_version & 0x0FFF) | 0x4000;
+}
+
+char *smb_uuid_to_string(const struct uuid uu)
+{
+ char *out;
+
+ asprintf(&out, "%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x",
+ uu.time_low, uu.time_mid, uu.time_hi_and_version,
+ uu.clock_seq[0], uu.clock_seq[1],
+ uu.node[0], uu.node[1], uu.node[2],
+ uu.node[3], uu.node[4], uu.node[5]);
+
+ return out;
+}
+
+const char *smb_uuid_string_static(const struct uuid uu)
+{
+ static char out[37];
+
+ slprintf(out, sizeof(out),
+ "%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x",
+ uu.time_low, uu.time_mid, uu.time_hi_and_version,
+ uu.clock_seq[0], uu.clock_seq[1],
+ uu.node[0], uu.node[1], uu.node[2],
+ uu.node[3], uu.node[4], uu.node[5]);
+ return out;
+}
+
+BOOL smb_string_to_uuid(const char *in, struct uuid* uu)
+{
+ BOOL ret = False;
+ const char *ptr = in;
+ char *end = (char *)in;
+ int i;
+
+ if (!in || !uu) goto out;
+
+ uu->time_low = strtoul(ptr, &end, 16);
+ if ((end - ptr) != 8 || *end != '-') goto out;
+ ptr = (end + 1);
+
+ uu->time_mid = strtoul(ptr, &end, 16);
+ if ((end - ptr) != 4 || *end != '-') goto out;
+ ptr = (end + 1);
+
+ uu->time_hi_and_version = strtoul(ptr, &end, 16);
+ if ((end - ptr) != 4 || *end != '-') goto out;
+ ptr = (end + 1);
+
+ for (i = 0; i < 2; i++) {
+ int adj = 0;
+ if (*ptr >= '0' && *ptr <= '9') {
+ adj = '0';
+ } else if (*ptr >= 'a' && *ptr <= 'f') {
+ adj = 'a';
+ } else if (*ptr >= 'A' && *ptr <= 'F') {
+ adj = 'A';
+ } else {
+ goto out;
+ }
+ uu->clock_seq[i] = (*ptr - adj) << 4;
+ ptr++;
+
+ if (*ptr >= '0' && *ptr <= '9') {
+ adj = '0';
+ } else if (*ptr >= 'a' && *ptr <= 'f') {
+ adj = 'a';
+ } else if (*ptr >= 'A' && *ptr <= 'F') {
+ adj = 'A';
+ } else {
+ goto out;
+ }
+ uu->clock_seq[i] |= (*ptr - adj);
+ ptr++;
+ }
+
+ if (*ptr != '-') goto out;
+ ptr++;
+
+ for (i = 0; i < 6; i++) {
+ int adj = 0;
+ if (*ptr >= '0' && *ptr <= '9') {
+ adj = '0';
+ } else if (*ptr >= 'a' && *ptr <= 'f') {
+ adj = 'a';
+ } else if (*ptr >= 'A' && *ptr <= 'F') {
+ adj = 'A';
+ } else {
+ goto out;
+ }
+ uu->node[i] = (*ptr - adj) << 4;
+ ptr++;
+
+ if (*ptr >= '0' && *ptr <= '9') {
+ adj = '0';
+ } else if (*ptr >= 'a' && *ptr <= 'f') {
+ adj = 'a';
+ } else if (*ptr >= 'A' && *ptr <= 'F') {
+ adj = 'A';
+ } else {
+ goto out;
+ }
+ uu->node[i] |= (*ptr - adj);
+ ptr++;
+ }
+
+ ret = True;
+out:
+ return ret;
+}
diff --git a/source/lib/version.c b/source/lib/version.c
new file mode 100644
index 00000000000..99f836c2d5b
--- /dev/null
+++ b/source/lib/version.c
@@ -0,0 +1,42 @@
+/*
+ Unix SMB/CIFS implementation.
+ Samba Version functions
+
+ 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 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"
+
+const char *samba_version_string(void)
+{
+#ifndef SAMBA_VERSION_VENDOR_SUFFIX
+ return SAMBA_VERSION_OFFICIAL_STRING;
+#else
+ static fstring samba_version;
+ static BOOL init_samba_version;
+
+ if (init_samba_version)
+ return samba_version;
+
+ snprintf(samba_version,sizeof(samba_version),"%s-%s",
+ SAMBA_VERSION_OFFICIAL_STRING,
+ SAMBA_VERSION_VENDOR_SUFFIX);
+
+ init_samba_version = True;
+ return samba_version;
+#endif
+}
diff --git a/source/lib/wins_srv.c b/source/lib/wins_srv.c
new file mode 100644
index 00000000000..4a54762fde7
--- /dev/null
+++ b/source/lib/wins_srv.c
@@ -0,0 +1,356 @@
+/*
+ Unix SMB/CIFS implementation.
+ Samba wins server helper functions
+ Copyright (C) Andrew Tridgell 1992-2002
+ Copyright (C) Christopher R. Hertel 2000
+ 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 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"
+
+/*
+ This is pretty much a complete rewrite of the earlier code. The main
+ aim of the rewrite is to add support for having multiple wins server
+ lists, so Samba can register with multiple groups of wins servers
+ and each group has a failover list of wins servers.
+
+ Central to the way it all works is the idea of a wins server
+ 'tag'. A wins tag is a label for a group of wins servers. For
+ example if you use
+
+ wins server = fred:192.168.2.10 mary:192.168.3.199 fred:192.168.2.61
+
+ then you would have two groups of wins servers, one tagged with the
+ name 'fred' and the other with the name 'mary'. I would usually
+ recommend using interface names instead of 'fred' and 'mary' but
+ they can be any alpha string.
+
+ Now, how does it all work. Well, nmbd needs to register each of its
+ IPs with each of its names once with each group of wins servers. So
+ it tries registering with the first one mentioned in the list, then
+ if that fails it marks that WINS server dead and moves onto the next
+ one.
+
+ In the client code things are a bit different. As each of the groups
+ of wins servers is a separate name space we need to try each of the
+ groups until we either succeed or we run out of wins servers to
+ try. If we get a negative response from a wins server then that
+ means the name doesn't exist in that group, so we give up on that
+ group and move to the next group. If we don't get a response at all
+ then maybe the wins server is down, in which case we need to
+ failover to the next one for that group.
+
+ confused yet? (tridge)
+*/
+
+/* how long a server is marked dead for */
+#define DEATH_TIME 600
+
+/* The list of dead wins servers is stored in gencache.tdb. Each server is
+ marked dead from the point of view of a given source address. We keep a
+ separate dead list for each src address to cope with multiple interfaces
+ that are not routable to each other.
+ */
+
+#define WINS_SRV_FMT "WINS_SRV_DEAD/%s,%s" /* wins_ip,src_ip */
+
+static char *wins_srv_keystr(struct in_addr wins_ip, struct in_addr src_ip)
+{
+ char *keystr = NULL, *wins_ip_addr = NULL, *src_ip_addr = NULL;
+
+ wins_ip_addr = strdup(inet_ntoa(wins_ip));
+ src_ip_addr = strdup(inet_ntoa(src_ip));
+
+ if ( !wins_ip_addr || !src_ip_addr ) {
+ DEBUG(0,("wins_srv_keystr: malloc error\n"));
+ goto done;
+ }
+
+ if (asprintf(&keystr, WINS_SRV_FMT, wins_ip_addr, src_ip_addr) == -1) {
+ DEBUG(0, (": ns_srv_keystr: malloc error for key string\n"));
+ }
+
+done:
+ SAFE_FREE(wins_ip_addr);
+ SAFE_FREE(src_ip_addr);
+
+ return keystr;
+}
+
+/*
+ see if an ip is on the dead list
+*/
+
+BOOL wins_srv_is_dead(struct in_addr wins_ip, struct in_addr src_ip)
+{
+ char *keystr = wins_srv_keystr(wins_ip, src_ip);
+ BOOL result;
+
+ /* If the key exists then the WINS server has been marked as dead */
+
+ result = gencache_get(keystr, NULL, NULL);
+ SAFE_FREE(keystr);
+
+ DEBUG(4, ("wins_srv_is_dead: %s is %s\n", inet_ntoa(wins_ip),
+ result ? "dead" : "alive"));
+
+ return result;
+}
+
+
+/*
+ mark a wins server as being alive (for the moment)
+*/
+void wins_srv_alive(struct in_addr wins_ip, struct in_addr src_ip)
+{
+ char *keystr = wins_srv_keystr(wins_ip, src_ip);
+
+ gencache_del(keystr);
+ SAFE_FREE(keystr);
+
+ DEBUG(4, ("wins_srv_alive: marking wins server %s alive\n",
+ inet_ntoa(wins_ip)));
+}
+
+/*
+ mark a wins server as temporarily dead
+*/
+void wins_srv_died(struct in_addr wins_ip, struct in_addr src_ip)
+{
+ char *keystr;
+
+ if (is_zero_ip(wins_ip) || wins_srv_is_dead(wins_ip, src_ip))
+ return;
+
+ keystr = wins_srv_keystr(wins_ip, src_ip);
+
+ gencache_set(keystr, "DOWN", time(NULL) + DEATH_TIME);
+
+ SAFE_FREE(keystr);
+
+ DEBUG(4,("Marking wins server %s dead for %u seconds from source %s\n",
+ inet_ntoa(wins_ip), DEATH_TIME, inet_ntoa(src_ip)));
+}
+
+/*
+ return the total number of wins servers, dead or not
+*/
+unsigned wins_srv_count(void)
+{
+ const char **list;
+ int count = 0;
+
+ if (lp_wins_support()) {
+ /* simple - just talk to ourselves */
+ return 1;
+ }
+
+ list = lp_wins_server_list();
+ for (count=0; list && list[count]; count++)
+ /* nop */ ;
+
+ return count;
+}
+
+/* an internal convenience structure for an IP with a short string tag
+ attached */
+struct tagged_ip {
+ fstring tag;
+ struct in_addr ip;
+};
+
+/*
+ parse an IP string that might be in tagged format
+ the result is a tagged_ip structure containing the tag
+ and the ip in in_addr format. If there is no tag then
+ use the tag '*'
+*/
+static void parse_ip(struct tagged_ip *ip, const char *str)
+{
+ char *s = strchr(str, ':');
+ if (!s) {
+ fstrcpy(ip->tag, "*");
+ ip->ip = *interpret_addr2(str);
+ return;
+ }
+
+ ip->ip = *interpret_addr2(s+1);
+ fstrcpy(ip->tag, str);
+ s = strchr(ip->tag, ':');
+ if (s) *s = 0;
+}
+
+
+
+/*
+ return the list of wins server tags. A 'tag' is used to distinguish
+ wins server as either belonging to the same name space or a separate
+ name space. Usually you would setup your 'wins server' option to
+ list one or more wins server per interface and use the interface
+ name as your tag, but you are free to use any tag you like.
+*/
+char **wins_srv_tags(void)
+{
+ char **ret = NULL;
+ int count=0, i, j;
+ const char **list;
+
+ if (lp_wins_support()) {
+ /* give the caller something to chew on. This makes
+ the rest of the logic simpler (ie. less special cases) */
+ ret = (char **)malloc(sizeof(char *)*2);
+ if (!ret) return NULL;
+ ret[0] = strdup("*");
+ ret[1] = NULL;
+ return ret;
+ }
+
+ list = lp_wins_server_list();
+ if (!list)
+ return NULL;
+
+ /* yes, this is O(n^2) but n is very small */
+ for (i=0;list[i];i++) {
+ struct tagged_ip t_ip;
+
+ parse_ip(&t_ip, list[i]);
+
+ /* see if we already have it */
+ for (j=0;j<count;j++) {
+ if (strcmp(ret[j], t_ip.tag) == 0) {
+ break;
+ }
+ }
+
+ if (j != count) {
+ /* we already have it. Move along */
+ continue;
+ }
+
+ /* add it to the list */
+ ret = (char **)Realloc(ret, (count+2) * sizeof(char *));
+ ret[count] = strdup(t_ip.tag);
+ if (!ret[count]) break;
+ count++;
+ }
+
+ if (count) {
+ /* make sure we null terminate */
+ ret[count] = NULL;
+ }
+
+ return ret;
+}
+
+/* free a list of wins server tags given by wins_srv_tags */
+void wins_srv_tags_free(char **list)
+{
+ int i;
+ if (!list) return;
+ for (i=0; list[i]; i++) {
+ free(list[i]);
+ }
+ free(list);
+}
+
+
+/*
+ return the IP of the currently active wins server for the given tag,
+ or the zero IP otherwise
+*/
+struct in_addr wins_srv_ip_tag(const char *tag, struct in_addr src_ip)
+{
+ const char **list;
+ int i;
+ struct tagged_ip t_ip;
+
+ /* if we are a wins server then we always just talk to ourselves */
+ if (lp_wins_support()) {
+ extern struct in_addr loopback_ip;
+ return loopback_ip;
+ }
+
+ list = lp_wins_server_list();
+ if (!list || !list[0]) {
+ struct in_addr ip;
+ zero_ip(&ip);
+ return ip;
+ }
+
+ /* find the first live one for this tag */
+ for (i=0; list[i]; i++) {
+ parse_ip(&t_ip, list[i]);
+ if (strcmp(tag, t_ip.tag) != 0) {
+ /* not for the right tag. Move along */
+ continue;
+ }
+ if (!wins_srv_is_dead(t_ip.ip, src_ip)) {
+ fstring src_name;
+ fstrcpy(src_name, inet_ntoa(src_ip));
+ DEBUG(6,("Current wins server for tag '%s' with source %s is %s\n",
+ tag,
+ src_name,
+ inet_ntoa(t_ip.ip)));
+ return t_ip.ip;
+ }
+ }
+
+ /* they're all dead - try the first one until they revive */
+ for (i=0; list[i]; i++) {
+ parse_ip(&t_ip, list[i]);
+ if (strcmp(tag, t_ip.tag) != 0) {
+ continue;
+ }
+ return t_ip.ip;
+ }
+
+ /* this can't happen?? */
+ zero_ip(&t_ip.ip);
+ return t_ip.ip;
+}
+
+
+/*
+ return a count of the number of IPs for a particular tag, including
+ dead ones
+*/
+unsigned wins_srv_count_tag(const char *tag)
+{
+ const char **list;
+ int i, count=0;
+
+ /* if we are a wins server then we always just talk to ourselves */
+ if (lp_wins_support()) {
+ return 1;
+ }
+
+ list = lp_wins_server_list();
+ if (!list || !list[0]) {
+ return 0;
+ }
+
+ /* find the first live one for this tag */
+ for (i=0; list[i]; i++) {
+ struct tagged_ip t_ip;
+ parse_ip(&t_ip, list[i]);
+ if (strcmp(tag, t_ip.tag) == 0) {
+ count++;
+ }
+ }
+
+ return count;
+}
diff --git a/source/lib/xfile.c b/source/lib/xfile.c
new file mode 100644
index 00000000000..1534dd855e9
--- /dev/null
+++ b/source/lib/xfile.c
@@ -0,0 +1,378 @@
+/*
+ Unix SMB/CIFS implementation.
+ stdio replacement
+ 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 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.
+*/
+
+/*
+ stdio is very convenient, but on some systems the file descriptor
+ in FILE* is 8 bits, so it fails when more than 255 files are open.
+
+ XFILE replaces stdio. It is less efficient, but at least it works
+ when you have lots of files open
+
+ The main restriction on XFILE is that it doesn't support seeking,
+ and doesn't support O_RDWR. That keeps the code simple.
+*/
+
+#include "includes.h"
+
+#define XBUFSIZE BUFSIZ
+
+static XFILE _x_stdin = { 0, NULL, NULL, XBUFSIZE, 0, O_RDONLY, X_IOFBF, 0 };
+static XFILE _x_stdout = { 1, NULL, NULL, XBUFSIZE, 0, O_WRONLY, X_IOLBF, 0 };
+static XFILE _x_stderr = { 2, NULL, NULL, 0, 0, O_WRONLY, X_IONBF, 0 };
+
+XFILE *x_stdin = &_x_stdin;
+XFILE *x_stdout = &_x_stdout;
+XFILE *x_stderr = &_x_stderr;
+
+#define X_FLAG_EOF 1
+#define X_FLAG_ERROR 2
+#define X_FLAG_EINVAL 3
+
+/* simulate setvbuf() */
+int x_setvbuf(XFILE *f, char *buf, int mode, size_t size)
+{
+ x_fflush(f);
+ if (f->bufused) return -1;
+
+ /* on files being read full buffering is the only option */
+ if ((f->open_flags & O_ACCMODE) == O_RDONLY) {
+ mode = X_IOFBF;
+ }
+
+ /* destroy any earlier buffer */
+ SAFE_FREE(f->buf);
+ f->buf = 0;
+ f->bufsize = 0;
+ f->next = NULL;
+ f->bufused = 0;
+ f->buftype = mode;
+
+ if (f->buftype == X_IONBF) return 0;
+
+ /* if buffering then we need some size */
+ if (size == 0) size = XBUFSIZE;
+
+ f->bufsize = size;
+ f->bufused = 0;
+
+ return 0;
+}
+
+/* allocate the buffer */
+static int x_allocate_buffer(XFILE *f)
+{
+ if (f->buf) return 1;
+ if (f->bufsize == 0) return 0;
+ f->buf = malloc(f->bufsize);
+ if (!f->buf) return 0;
+ f->next = f->buf;
+ return 1;
+}
+
+
+/* this looks more like open() than fopen(), but that is quite deliberate.
+ I want programmers to *think* about O_EXCL, O_CREAT etc not just
+ get them magically added
+*/
+XFILE *x_fopen(const char *fname, int flags, mode_t mode)
+{
+ XFILE *ret;
+
+ ret = (XFILE *)malloc(sizeof(XFILE));
+ if (!ret) return NULL;
+
+ memset(ret, 0, sizeof(XFILE));
+
+ if ((flags & O_ACCMODE) == O_RDWR) {
+ /* we don't support RDWR in XFILE - use file
+ descriptors instead */
+ errno = EINVAL;
+ return NULL;
+ }
+
+ ret->open_flags = flags;
+
+ ret->fd = sys_open(fname, flags, mode);
+ if (ret->fd == -1) {
+ SAFE_FREE(ret);
+ return NULL;
+ }
+
+ x_setvbuf(ret, NULL, X_IOFBF, XBUFSIZE);
+
+ return ret;
+}
+
+/* simulate fclose() */
+int x_fclose(XFILE *f)
+{
+ int ret;
+
+ /* make sure we flush any buffered data */
+ x_fflush(f);
+
+ ret = close(f->fd);
+ f->fd = -1;
+ if (f->buf) {
+ /* make sure data can't leak into a later malloc */
+ memset(f->buf, 0, f->bufsize);
+ SAFE_FREE(f->buf);
+ }
+ SAFE_FREE(f);
+ return ret;
+}
+
+/* simulate fwrite() */
+size_t x_fwrite(const void *p, size_t size, size_t nmemb, XFILE *f)
+{
+ ssize_t ret;
+ size_t total=0;
+
+ /* we might be writing unbuffered */
+ if (f->buftype == X_IONBF ||
+ (!f->buf && !x_allocate_buffer(f))) {
+ ret = write(f->fd, p, size*nmemb);
+ if (ret == -1) return -1;
+ return ret/size;
+ }
+
+
+ while (total < size*nmemb) {
+ size_t n = f->bufsize - f->bufused;
+ n = MIN(n, (size*nmemb)-total);
+
+ if (n == 0) {
+ /* it's full, flush it */
+ x_fflush(f);
+ continue;
+ }
+
+ memcpy(f->buf + f->bufused, total+(const char *)p, n);
+ f->bufused += n;
+ total += n;
+ }
+
+ /* when line buffered we need to flush at the last linefeed. This can
+ flush a bit more than necessary, but that is harmless */
+ if (f->buftype == X_IOLBF && f->bufused) {
+ int i;
+ for (i=(size*nmemb)-1; i>=0; i--) {
+ if (*(i+(const char *)p) == '\n') {
+ x_fflush(f);
+ break;
+ }
+ }
+ }
+
+ return total/size;
+}
+
+/* thank goodness for asprintf() */
+ int x_vfprintf(XFILE *f, const char *format, va_list ap)
+{
+ char *p;
+ int len, ret;
+ va_list ap2;
+
+ VA_COPY(ap2, ap);
+
+ len = vasprintf(&p, format, ap2);
+ if (len <= 0) return len;
+ ret = x_fwrite(p, 1, len, f);
+ SAFE_FREE(p);
+ return ret;
+}
+
+ int x_fprintf(XFILE *f, const char *format, ...)
+{
+ va_list ap;
+ int ret;
+
+ va_start(ap, format);
+ ret = x_vfprintf(f, format, ap);
+ va_end(ap);
+ return ret;
+}
+
+/* at least fileno() is simple! */
+int x_fileno(XFILE *f)
+{
+ return f->fd;
+}
+
+/* simulate fflush() */
+int x_fflush(XFILE *f)
+{
+ int ret;
+
+ if (f->flags & X_FLAG_ERROR) return -1;
+
+ if ((f->open_flags & O_ACCMODE) != O_WRONLY) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ if (f->bufused == 0) return 0;
+
+ ret = write(f->fd, f->buf, f->bufused);
+ if (ret == -1) return -1;
+
+ f->bufused -= ret;
+ if (f->bufused > 0) {
+ f->flags |= X_FLAG_ERROR;
+ memmove(f->buf, ret + (char *)f->buf, f->bufused);
+ return -1;
+ }
+
+ return 0;
+}
+
+/* simulate setbuffer() */
+void x_setbuffer(XFILE *f, char *buf, size_t size)
+{
+ x_setvbuf(f, buf, buf?X_IOFBF:X_IONBF, size);
+}
+
+/* simulate setbuf() */
+void x_setbuf(XFILE *f, char *buf)
+{
+ x_setvbuf(f, buf, buf?X_IOFBF:X_IONBF, XBUFSIZE);
+}
+
+/* simulate setlinebuf() */
+void x_setlinebuf(XFILE *f)
+{
+ x_setvbuf(f, NULL, X_IOLBF, 0);
+}
+
+
+/* simulate feof() */
+int x_feof(XFILE *f)
+{
+ if (f->flags & X_FLAG_EOF) return 1;
+ return 0;
+}
+
+/* simulate ferror() */
+int x_ferror(XFILE *f)
+{
+ if (f->flags & X_FLAG_ERROR) return 1;
+ return 0;
+}
+
+/* fill the read buffer */
+static void x_fillbuf(XFILE *f)
+{
+ int n;
+
+ if (f->bufused) return;
+
+ if (!f->buf && !x_allocate_buffer(f)) return;
+
+ n = read(f->fd, f->buf, f->bufsize);
+ if (n <= 0) return;
+ f->bufused = n;
+ f->next = f->buf;
+}
+
+/* simulate fgetc() */
+int x_fgetc(XFILE *f)
+{
+ int ret;
+
+ if (f->flags & (X_FLAG_EOF | X_FLAG_ERROR)) return EOF;
+
+ if (f->bufused == 0) x_fillbuf(f);
+
+ if (f->bufused == 0) {
+ f->flags |= X_FLAG_EOF;
+ return EOF;
+ }
+
+ ret = *(unsigned char *)(f->next);
+ f->next++;
+ f->bufused--;
+ return ret;
+}
+
+/* simulate fread */
+size_t x_fread(void *p, size_t size, size_t nmemb, XFILE *f)
+{
+ size_t total = 0;
+ while (total < size*nmemb) {
+ int c = x_fgetc(f);
+ if (c == EOF) break;
+ (total+(char *)p)[0] = (char)c;
+ total++;
+ }
+ return total/size;
+}
+
+/* simulate fgets() */
+char *x_fgets(char *s, int size, XFILE *stream)
+{
+ char *s0 = s;
+ int l = size;
+ while (l>1) {
+ int c = x_fgetc(stream);
+ if (c == EOF) break;
+ *s++ = (char)c;
+ l--;
+ if (c == '\n') break;
+ }
+ if (l==size || x_ferror(stream)) {
+ return 0;
+ }
+ *s = 0;
+ return s0;
+}
+
+/* trivial seek, works only for SEEK_SET and SEEK_END if SEEK_CUR is
+ * set then an error is returned */
+off_t x_tseek(XFILE *f, off_t offset, int whence)
+{
+ if (f->flags & X_FLAG_ERROR)
+ return -1;
+
+ /* only SEEK_SET and SEEK_END are supported */
+ /* SEEK_CUR needs internal offset counter */
+ if (whence != SEEK_SET && whence != SEEK_END) {
+ f->flags |= X_FLAG_EINVAL;
+ errno = EINVAL;
+ return -1;
+ }
+
+ /* empty the buffer */
+ switch (f->open_flags & O_ACCMODE) {
+ case O_RDONLY:
+ f->bufused = 0;
+ break;
+ case O_WRONLY:
+ if (x_fflush(f) != 0)
+ return -1;
+ break;
+ default:
+ errno = EINVAL;
+ return -1;
+ }
+
+ f->flags &= ~X_FLAG_EOF;
+ return (off_t)sys_lseek(f->fd, offset, whence);
+}
diff --git a/source/libads/.cvsignore b/source/libads/.cvsignore
new file mode 100644
index 00000000000..5f2a5c4cf75
--- /dev/null
+++ b/source/libads/.cvsignore
@@ -0,0 +1,2 @@
+*.po
+*.po32
diff --git a/source/libads/ads_ldap.c b/source/libads/ads_ldap.c
new file mode 100644
index 00000000000..944cb1599cc
--- /dev/null
+++ b/source/libads/ads_ldap.c
@@ -0,0 +1,234 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ Winbind ADS backend functions
+
+ Copyright (C) Andrew Tridgell 2001
+ 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 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"
+#ifdef HAVE_LDAP
+
+/* convert a single name to a sid in a domain */
+NTSTATUS ads_name_to_sid(ADS_STRUCT *ads,
+ const char *name,
+ DOM_SID *sid,
+ enum SID_NAME_USE *type)
+{
+ const char *attrs[] = {"objectSid", "sAMAccountType", NULL};
+ int count;
+ ADS_STATUS rc;
+ void *res = NULL;
+ char *ldap_exp;
+ uint32 t;
+ NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
+ char *escaped_name = escape_ldap_string_alloc(name);
+ char *escaped_realm = escape_ldap_string_alloc(ads->config.realm);
+
+ if (!escaped_name || !escaped_realm) {
+ status = NT_STATUS_NO_MEMORY;
+ goto done;
+ }
+
+ if (asprintf(&ldap_exp, "(|(sAMAccountName=%s)(userPrincipalName=%s@%s))",
+ escaped_name, escaped_name, escaped_realm) == -1) {
+ DEBUG(1,("ads_name_to_sid: asprintf failed!\n"));
+ status = NT_STATUS_NO_MEMORY;
+ goto done;
+ }
+
+ rc = ads_search_retry(ads, &res, ldap_exp, attrs);
+ free(ldap_exp);
+ if (!ADS_ERR_OK(rc)) {
+ DEBUG(1,("name_to_sid ads_search: %s\n", ads_errstr(rc)));
+ goto done;
+ }
+
+ count = ads_count_replies(ads, res);
+ if (count != 1) {
+ DEBUG(1,("name_to_sid: %s not found\n", name));
+ goto done;
+ }
+
+ if (!ads_pull_sid(ads, res, "objectSid", sid)) {
+ DEBUG(1,("No sid for %s !?\n", name));
+ goto done;
+ }
+
+ if (!ads_pull_uint32(ads, res, "sAMAccountType", &t)) {
+ DEBUG(1,("No sAMAccountType for %s !?\n", name));
+ goto done;
+ }
+
+ *type = ads_atype_map(t);
+
+ status = NT_STATUS_OK;
+
+ DEBUG(3,("ads name_to_sid mapped %s\n", name));
+
+done:
+ if (res) ads_msgfree(ads, res);
+
+ SAFE_FREE(escaped_name);
+ SAFE_FREE(escaped_realm);
+
+ return status;
+}
+
+/* convert a sid to a user or group name */
+NTSTATUS ads_sid_to_name(ADS_STRUCT *ads,
+ TALLOC_CTX *mem_ctx,
+ const DOM_SID *sid,
+ char **name,
+ enum SID_NAME_USE *type)
+{
+ const char *attrs[] = {"userPrincipalName",
+ "sAMAccountName",
+ "sAMAccountType", NULL};
+ ADS_STATUS rc;
+ void *msg = NULL;
+ char *ldap_exp = NULL;
+ char *sidstr = NULL;
+ uint32 atype;
+ NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
+
+ if (!(sidstr = sid_binstring(sid))) {
+ DEBUG(1,("ads_sid_to_name: sid_binstring failed!\n"));
+ status = NT_STATUS_NO_MEMORY;
+ goto done;
+ }
+
+ if (asprintf(&ldap_exp, "(objectSid=%s)", sidstr) == -1) {
+ DEBUG(1,("ads_sid_to_name: asprintf failed!\n"));
+ status = NT_STATUS_NO_MEMORY;
+ goto done;
+ }
+
+ rc = ads_search_retry(ads, &msg, ldap_exp, attrs);
+ if (!ADS_ERR_OK(rc)) {
+ status = ads_ntstatus(rc);
+ DEBUG(1,("ads_sid_to_name ads_search: %s\n", ads_errstr(rc)));
+ goto done;
+ }
+
+ if (!ads_pull_uint32(ads, msg, "sAMAccountType", &atype)) {
+ goto done;
+ }
+
+ *name = ads_pull_username(ads, mem_ctx, msg);
+ if (!*name) {
+ DEBUG(1,("ads_sid_to_name: ads_pull_username retuned NULL!\n"));
+ status = NT_STATUS_NO_MEMORY;
+ goto done;
+ }
+
+ *type = ads_atype_map(atype);
+
+ status = NT_STATUS_OK;
+
+ DEBUG(3,("ads sid_to_name mapped %s\n", *name));
+
+done:
+ if (msg) ads_msgfree(ads, msg);
+
+ SAFE_FREE(ldap_exp);
+ SAFE_FREE(sidstr);
+
+ return status;
+}
+
+
+/* convert a sid to a DN */
+
+ADS_STATUS ads_sid_to_dn(ADS_STRUCT *ads,
+ TALLOC_CTX *mem_ctx,
+ const DOM_SID *sid,
+ char **dn)
+{
+ ADS_STATUS rc;
+ LDAPMessage *msg = NULL;
+ LDAPMessage *entry = NULL;
+ char *ldap_exp;
+ char *sidstr = NULL;
+ int count;
+ char *dn2 = NULL;
+
+ const char *attr[] = {
+ "dn",
+ NULL
+ };
+
+ if (!(sidstr = sid_binstring(sid))) {
+ DEBUG(1,("ads_sid_to_dn: sid_binstring failed!\n"));
+ rc = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
+ goto done;
+ }
+
+ if(!(ldap_exp = talloc_asprintf(mem_ctx, "(objectSid=%s)", sidstr))) {
+ DEBUG(1,("ads_sid_to_dn: talloc_asprintf failed!\n"));
+ rc = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
+ goto done;
+ }
+
+ rc = ads_search_retry(ads, (void **)&msg, ldap_exp, attr);
+
+ if (!ADS_ERR_OK(rc)) {
+ DEBUG(1,("ads_sid_to_dn ads_search: %s\n", ads_errstr(rc)));
+ goto done;
+ }
+
+ if ((count = ads_count_replies(ads, msg)) != 1) {
+ fstring sid_string;
+ DEBUG(1,("ads_sid_to_dn (sid=%s): Not found (count=%d)\n",
+ sid_to_string(sid_string, sid), count));
+ rc = ADS_ERROR_NT(NT_STATUS_UNSUCCESSFUL);
+ goto done;
+ }
+
+ entry = ads_first_entry(ads, msg);
+
+ dn2 = ads_get_dn(ads, entry);
+
+ if (!dn2) {
+ rc = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
+ goto done;
+ }
+
+ *dn = talloc_strdup(mem_ctx, dn2);
+
+ if (!*dn) {
+ ads_memfree(ads, dn2);
+ rc = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
+ goto done;
+ }
+
+ rc = ADS_ERROR_NT(NT_STATUS_OK);
+
+ DEBUG(3,("ads sid_to_dn mapped %s\n", dn2));
+
+ SAFE_FREE(dn2);
+done:
+ if (msg) ads_msgfree(ads, msg);
+ if (dn2) ads_memfree(ads, dn2);
+
+ SAFE_FREE(sidstr);
+
+ return rc;
+}
+
+#endif
diff --git a/source/libads/ads_status.c b/source/libads/ads_status.c
new file mode 100644
index 00000000000..463f647f9c7
--- /dev/null
+++ b/source/libads/ads_status.c
@@ -0,0 +1,142 @@
+/*
+ Unix SMB/CIFS implementation.
+ ads (active directory) utility library
+ Copyright (C) Andrew Tridgell 2001
+ Copyright (C) Remus Koos 2001
+ Copyright (C) Andrew Bartlett 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 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"
+
+/*
+ build a ADS_STATUS structure
+*/
+ADS_STATUS ads_build_error(enum ads_error_type etype,
+ int rc, int minor_status)
+{
+ ADS_STATUS ret;
+
+ if (etype == ENUM_ADS_ERROR_NT) {
+ DEBUG(0,("don't use ads_build_error with ENUM_ADS_ERROR_NT!\n"));
+ ret.err.rc = -1;
+ ret.error_type = ENUM_ADS_ERROR_SYSTEM;
+ ret.minor_status = 0;
+ return ret;
+ }
+
+ ret.err.rc = rc;
+ ret.error_type = etype;
+ ret.minor_status = minor_status;
+ return ret;
+}
+
+ADS_STATUS ads_build_nt_error(enum ads_error_type etype,
+ NTSTATUS nt_status)
+{
+ ADS_STATUS ret;
+
+ if (etype != ENUM_ADS_ERROR_NT) {
+ DEBUG(0,("don't use ads_build_nt_error without ENUM_ADS_ERROR_NT!\n"));
+ ret.err.rc = -1;
+ ret.error_type = ENUM_ADS_ERROR_SYSTEM;
+ ret.minor_status = 0;
+ return ret;
+ }
+ ret.err.nt_status = nt_status;
+ ret.error_type = etype;
+ ret.minor_status = 0;
+ return ret;
+}
+
+/*
+ do a rough conversion between ads error codes and NT status codes
+ we'll need to fill this in more
+*/
+NTSTATUS ads_ntstatus(ADS_STATUS status)
+{
+ if (status.error_type == ENUM_ADS_ERROR_NT){
+ return status.err.nt_status;
+ }
+#ifdef HAVE_LDAP
+ if ((status.error_type == ENUM_ADS_ERROR_LDAP)
+ && (status.err.rc == LDAP_NO_MEMORY)) {
+ return NT_STATUS_NO_MEMORY;
+ }
+#endif
+#ifdef HAVE_KRB5
+ if (status.error_type == ENUM_ADS_ERROR_KRB5) {
+ if (status.err.rc == KRB5KDC_ERR_PREAUTH_FAILED) {
+ return NT_STATUS_LOGON_FAILURE;
+ } else if (status.err.rc == KRB5_KDC_UNREACH) {
+ return NT_STATUS_NO_LOGON_SERVERS;
+ }
+ }
+#endif
+ if (ADS_ERR_OK(status)) return NT_STATUS_OK;
+ return NT_STATUS_UNSUCCESSFUL;
+}
+
+/*
+ return a string for an error from a ads routine
+*/
+const char *ads_errstr(ADS_STATUS status)
+{
+ uint32 msg_ctx;
+ static char *ret;
+
+ SAFE_FREE(ret);
+ msg_ctx = 0;
+
+ switch (status.error_type) {
+ case ENUM_ADS_ERROR_SYSTEM:
+ return strerror(status.err.rc);
+#ifdef HAVE_LDAP
+ case ENUM_ADS_ERROR_LDAP:
+ return ldap_err2string(status.err.rc);
+#endif
+#ifdef HAVE_KRB5
+ case ENUM_ADS_ERROR_KRB5:
+ return error_message(status.err.rc);
+#endif
+#ifdef HAVE_GSSAPI
+ case ENUM_ADS_ERROR_GSS:
+ {
+ uint32 minor;
+
+ gss_buffer_desc msg1, msg2;
+ msg1.value = NULL;
+ msg2.value = NULL;
+ gss_display_status(&minor, status.err.rc, GSS_C_GSS_CODE,
+ GSS_C_NULL_OID, &msg_ctx, &msg1);
+ gss_display_status(&minor, status.minor_status, GSS_C_MECH_CODE,
+ GSS_C_NULL_OID, &msg_ctx, &msg2);
+ asprintf(&ret, "%s : %s", (char *)msg1.value, (char *)msg2.value);
+ gss_release_buffer(&minor, &msg1);
+ gss_release_buffer(&minor, &msg2);
+ return ret;
+ }
+#endif
+ case ENUM_ADS_ERROR_NT:
+ return get_friendly_nt_error_msg(ads_ntstatus(status));
+ default:
+ return "Unknown ADS error type!? (not compiled in?)";
+ }
+
+}
+
+
diff --git a/source/libads/ads_struct.c b/source/libads/ads_struct.c
new file mode 100644
index 00000000000..92f37093f46
--- /dev/null
+++ b/source/libads/ads_struct.c
@@ -0,0 +1,143 @@
+/*
+ Unix SMB/CIFS implementation.
+ ads (active directory) utility library
+ Copyright (C) Andrew Tridgell 2001
+ Copyright (C) Andrew Bartlett 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 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"
+
+/* return a ldap dn path from a string, given separators and field name
+ caller must free
+*/
+char *ads_build_path(const char *realm, const char *sep, const char *field, int reverse)
+{
+ char *p, *r;
+ int numbits = 0;
+ char *ret;
+ int len;
+
+ r = strdup(realm);
+
+ if (!r || !*r)
+ return r;
+
+ for (p=r; *p; p++)
+ if (strchr(sep, *p))
+ numbits++;
+
+ len = (numbits+1)*(strlen(field)+1) + strlen(r) + 1;
+
+ ret = malloc(len);
+ if (!ret)
+ return NULL;
+
+ strlcpy(ret,field, len);
+ p=strtok(r,sep);
+ strlcat(ret, p, len);
+
+ while ((p=strtok(NULL,sep))) {
+ char *s;
+ if (reverse)
+ asprintf(&s, "%s%s,%s", field, p, ret);
+ else
+ asprintf(&s, "%s,%s%s", ret, field, p);
+ free(ret);
+ ret = s;
+ }
+
+ free(r);
+ return ret;
+}
+
+/* return a dn of the form "dc=AA,dc=BB,dc=CC" from a
+ realm of the form AA.BB.CC
+ caller must free
+*/
+char *ads_build_dn(const char *realm)
+{
+ return ads_build_path(realm, ".", "dc=", 0);
+}
+
+
+#ifndef LDAP_PORT
+#define LDAP_PORT 389
+#endif
+
+/*
+ initialise a ADS_STRUCT, ready for some ads_ ops
+*/
+ADS_STRUCT *ads_init(const char *realm,
+ const char *workgroup,
+ const char *ldap_server)
+{
+ ADS_STRUCT *ads;
+
+ ads = (ADS_STRUCT *)smb_xmalloc(sizeof(*ads));
+ ZERO_STRUCTP(ads);
+
+ ads->server.realm = realm? strdup(realm) : NULL;
+ ads->server.workgroup = workgroup ? strdup(workgroup) : NULL;
+ ads->server.ldap_server = ldap_server? strdup(ldap_server) : NULL;
+
+ /* we need to know if this is a foreign realm */
+ if (realm && *realm && !strequal(lp_realm(), realm)) {
+ ads->server.foreign = 1;
+ }
+ if (workgroup && *workgroup && !strequal(lp_workgroup(), workgroup)) {
+ ads->server.foreign = 1;
+ }
+
+ /* the caller will own the memory by default */
+ ads->is_mine = 1;
+
+ return ads;
+}
+
+/*
+ free the memory used by the ADS structure initialized with 'ads_init(...)'
+*/
+void ads_destroy(ADS_STRUCT **ads)
+{
+ if (ads && *ads) {
+ BOOL is_mine;
+
+ is_mine = (*ads)->is_mine;
+#if HAVE_LDAP
+ if ((*ads)->ld) ldap_unbind((*ads)->ld);
+#endif
+ SAFE_FREE((*ads)->server.realm);
+ SAFE_FREE((*ads)->server.workgroup);
+ SAFE_FREE((*ads)->server.ldap_server);
+ SAFE_FREE((*ads)->server.ldap_uri);
+
+ SAFE_FREE((*ads)->auth.realm);
+ SAFE_FREE((*ads)->auth.password);
+ SAFE_FREE((*ads)->auth.user_name);
+ SAFE_FREE((*ads)->auth.kdc_server);
+
+ SAFE_FREE((*ads)->config.realm);
+ SAFE_FREE((*ads)->config.bind_path);
+ SAFE_FREE((*ads)->config.ldap_server_name);
+
+
+ ZERO_STRUCTP(*ads);
+
+ if ( is_mine )
+ SAFE_FREE(*ads);
+ }
+}
diff --git a/source/libads/ads_utils.c b/source/libads/ads_utils.c
new file mode 100644
index 00000000000..1aad0bed547
--- /dev/null
+++ b/source/libads/ads_utils.c
@@ -0,0 +1,137 @@
+/*
+ Unix SMB/CIFS implementation.
+ ads (active directory) utility library
+
+ Copyright (C) Stefan (metze) Metzmacher 2002
+ 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 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"
+
+/*
+translated the ACB_CTRL Flags to UserFlags (userAccountControl)
+*/
+uint32 ads_acb2uf(uint16 acb)
+{
+ uint32 uf = 0x00000000;
+
+ if (acb & ACB_DISABLED) uf |= UF_ACCOUNTDISABLE;
+ if (acb & ACB_HOMDIRREQ) uf |= UF_HOMEDIR_REQUIRED;
+ if (acb & ACB_PWNOTREQ) uf |= UF_PASSWD_NOTREQD;
+ if (acb & ACB_TEMPDUP) uf |= UF_TEMP_DUPLICATE_ACCOUNT;
+ if (acb & ACB_NORMAL) uf |= UF_NORMAL_ACCOUNT;
+ if (acb & ACB_MNS) uf |= UF_MNS_LOGON_ACCOUNT;
+ if (acb & ACB_DOMTRUST) uf |= UF_INTERDOMAIN_TRUST_ACCOUNT;
+ if (acb & ACB_WSTRUST) uf |= UF_WORKSTATION_TRUST_ACCOUNT;
+ if (acb & ACB_SVRTRUST) uf |= UF_SERVER_TRUST_ACCOUNT;
+ if (acb & ACB_PWNOEXP) uf |= UF_DONT_EXPIRE_PASSWD;
+ if (acb & ACB_AUTOLOCK) uf |= UF_LOCKOUT;
+
+ return uf;
+}
+
+/*
+translated the UserFlags (userAccountControl) to ACB_CTRL Flags
+*/
+uint16 ads_uf2acb(uint32 uf)
+{
+ uint16 acb = 0x0000;
+
+ if (uf & UF_ACCOUNTDISABLE) acb |= ACB_DISABLED;
+ if (uf & UF_HOMEDIR_REQUIRED) acb |= ACB_HOMDIRREQ;
+ if (uf & UF_PASSWD_NOTREQD) acb |= ACB_PWNOTREQ;
+ if (uf & UF_MNS_LOGON_ACCOUNT) acb |= ACB_MNS;
+ if (uf & UF_DONT_EXPIRE_PASSWD) acb |= ACB_PWNOEXP;
+ if (uf & UF_LOCKOUT) acb |= ACB_AUTOLOCK;
+
+ switch (uf & UF_ACCOUNT_TYPE_MASK)
+ {
+ case UF_TEMP_DUPLICATE_ACCOUNT: acb |= ACB_TEMPDUP;break;
+ case UF_NORMAL_ACCOUNT: acb |= ACB_NORMAL;break;
+ case UF_INTERDOMAIN_TRUST_ACCOUNT: acb |= ACB_DOMTRUST;break;
+ case UF_WORKSTATION_TRUST_ACCOUNT: acb |= ACB_WSTRUST;break;
+ case UF_SERVER_TRUST_ACCOUNT: acb |= ACB_SVRTRUST;break;
+ /*Fix Me: what should we do here? */
+ default: acb |= ACB_NORMAL;break;
+ }
+
+ return acb;
+}
+
+/*
+get the accountType from the UserFlags
+*/
+uint32 ads_uf2atype(uint32 uf)
+{
+ uint32 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 ads_gtype2atype(uint32 gtype)
+{
+ uint32 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 SID_NAME_USE ads_atype_map(uint32 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/source/libads/authdata.c b/source/libads/authdata.c
new file mode 100644
index 00000000000..29170af377e
--- /dev/null
+++ b/source/libads/authdata.c
@@ -0,0 +1,617 @@
+/*
+ Unix SMB/CIFS implementation.
+ kerberos authorization data (PAC) utility library
+ 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 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"
+
+#ifdef HAVE_KRB5
+
+static DATA_BLOB unwrap_pac(DATA_BLOB *auth_data)
+{
+ DATA_BLOB pac_contents;
+ ASN1_DATA data;
+ int data_type;
+
+ asn1_load(&data, *auth_data);
+ asn1_start_tag(&data, ASN1_SEQUENCE(0));
+ asn1_start_tag(&data, ASN1_SEQUENCE(0));
+ asn1_start_tag(&data, ASN1_CONTEXT(0));
+ asn1_read_Integer(&data, &data_type);
+ asn1_end_tag(&data);
+ asn1_start_tag(&data, ASN1_CONTEXT(1));
+ asn1_read_OctetString(&data, &pac_contents);
+ asn1_end_tag(&data);
+ asn1_end_tag(&data);
+ asn1_end_tag(&data);
+ asn1_free(&data);
+ return pac_contents;
+}
+
+static BOOL pac_io_unknown_type_10(const char *desc, UNKNOWN_TYPE_10 *type_10,
+ prs_struct *ps, int depth)
+{
+ if (NULL == type_10)
+ return False;
+
+ prs_debug(ps, depth, desc, "pac_io_unknown_type_10");
+ depth++;
+
+ if (!smb_io_time("unknown_time", &type_10->unknown_time, ps, depth))
+ return False;
+
+ if (!prs_uint16("len", ps, depth, &type_10->len))
+ return False;
+
+ if (UNMARSHALLING(ps) && type_10->len) {
+ type_10->username = (uint16 *) prs_alloc_mem(ps, type_10->len);
+ if (!type_10->username) {
+ DEBUG(3, ("No memory available\n"));
+ return False;
+ }
+ }
+
+ if (!prs_uint16s(True, "name", ps, depth, type_10->username,
+ (type_10->len / sizeof(uint16))))
+ return False;
+
+ return True;
+
+}
+
+
+static BOOL pac_io_krb_sids(const char *desc, KRB_SID_AND_ATTRS *sid_and_attr,
+ prs_struct *ps, int depth)
+{
+ if (NULL == sid_and_attr)
+ return False;
+
+ prs_debug(ps, depth, desc, "pac_io_krb_sids");
+ depth++;
+
+ if (UNMARSHALLING(ps)) {
+ sid_and_attr->sid =
+ (DOM_SID2 * ) prs_alloc_mem(ps, sizeof(DOM_SID2));
+ if (!sid_and_attr->sid) {
+ DEBUG(3, ("No memory available\n"));
+ return False;
+ }
+ }
+
+ if(!smb_io_dom_sid2("sid", sid_and_attr->sid, ps, depth))
+ return False;
+
+ return True;
+}
+
+
+static BOOL pac_io_krb_attrs(const char *desc, KRB_SID_AND_ATTRS *sid_and_attr,
+ prs_struct *ps, int depth)
+{
+ if (NULL == sid_and_attr)
+ return False;
+
+ prs_debug(ps, depth, desc, "pac_io_krb_attrs");
+ depth++;
+
+ if (!prs_uint32("sid_ptr", ps, depth, &sid_and_attr->sid_ptr))
+ return False;
+ if (!prs_uint32("attrs", ps, depth, &sid_and_attr->attrs))
+ return False;
+
+ return True;
+}
+
+static BOOL pac_io_krb_sid_and_attr_array(const char *desc,
+ KRB_SID_AND_ATTR_ARRAY *array,
+ uint32 num,
+ prs_struct *ps, int depth)
+{
+ int i;
+
+ if (NULL == array)
+ return False;
+
+ prs_debug(ps, depth, desc, "pac_io_krb_sid_and_attr_array");
+ depth++;
+
+
+ if (!prs_uint32("count", ps, depth, &array->count))
+ return False;
+
+ if (UNMARSHALLING(ps)) {
+ array->krb_sid_and_attrs = (KRB_SID_AND_ATTRS *)
+ prs_alloc_mem(ps, sizeof(KRB_SID_AND_ATTRS) * num);
+ if (!array->krb_sid_and_attrs) {
+ DEBUG(3, ("No memory available\n"));
+ return False;
+ }
+ }
+
+ for (i=0; i<num; i++) {
+ if (!pac_io_krb_attrs(desc,
+ &array->krb_sid_and_attrs[i],
+ ps, depth))
+ return False;
+
+ }
+ for (i=0; i<num; i++) {
+ if (!pac_io_krb_sids(desc,
+ &array->krb_sid_and_attrs[i],
+ ps, depth))
+ return False;
+
+ }
+
+ return True;
+
+}
+
+static BOOL pac_io_group_membership(const char *desc,
+ GROUP_MEMBERSHIP *membership,
+ prs_struct *ps, int depth)
+{
+ if (NULL == membership)
+ return False;
+
+ prs_debug(ps, depth, desc, "pac_io_group_membership");
+ depth++;
+
+ if (!prs_uint32("rid", ps, depth, &membership->rid))
+ return False;
+ if (!prs_uint32("attrs", ps, depth, &membership->attrs))
+ return False;
+
+ return True;
+}
+
+
+static BOOL pac_io_group_membership_array(const char *desc,
+ GROUP_MEMBERSHIP_ARRAY *array,
+ uint32 num,
+ prs_struct *ps, int depth)
+{
+ int i;
+
+ if (NULL == array)
+ return False;
+
+ prs_debug(ps, depth, desc, "pac_io_group_membership_array");
+ depth++;
+
+
+ if (!prs_uint32("count", ps, depth, &array->count))
+ return False;
+
+ if (UNMARSHALLING(ps)) {
+ array->group_membership = (GROUP_MEMBERSHIP *)
+ prs_alloc_mem(ps, sizeof(GROUP_MEMBERSHIP) * num);
+ if (!array->group_membership) {
+ DEBUG(3, ("No memory available\n"));
+ return False;
+ }
+ }
+
+ for (i=0; i<num; i++) {
+ if (!pac_io_group_membership(desc,
+ &array->group_membership[i],
+ ps, depth))
+ return False;
+
+ }
+
+ return True;
+
+}
+
+static BOOL pac_io_pac_logon_info(const char *desc, PAC_LOGON_INFO *info,
+ prs_struct *ps, int depth)
+{
+ uint32 garbage;
+ if (NULL == info)
+ return False;
+
+ prs_debug(ps, depth, desc, "pac_io_pac_logon_info");
+ depth++;
+
+ if (!prs_uint32("unknown", ps, depth, &garbage))
+ return False;
+ if (!prs_uint32("unknown", ps, depth, &garbage))
+ return False;
+ if (!prs_uint32("bufferlen", ps, depth, &garbage))
+ return False;
+ if (!prs_uint32("bufferlenhi", ps, depth, &garbage))
+ return False;
+ if (!prs_uint32("pointer", ps, depth, &garbage))
+ return False;
+
+ if (!smb_io_time("logon_time", &info->logon_time, ps, depth))
+ return False;
+ if (!smb_io_time("logoff_time", &info->logoff_time, ps, depth))
+ return False;
+ if (!smb_io_time("kickoff_time", &info->kickoff_time, ps, depth))
+ return False;
+ if (!smb_io_time("pass_last_set_time", &info->pass_last_set_time,
+ ps, depth))
+ return False;
+ if (!smb_io_time("pass_can_change_time", &info->pass_can_change_time,
+ ps, depth))
+ return False;
+ if (!smb_io_time("pass_must_change_time", &info->pass_must_change_time,
+ ps, depth))
+ return False;
+
+ if (!smb_io_unihdr("hdr_user_name", &info->hdr_user_name, ps, depth))
+ return False;
+ if (!smb_io_unihdr("hdr_full_name", &info->hdr_full_name, ps, depth))
+ return False;
+ if (!smb_io_unihdr("hdr_logon_script", &info->hdr_logon_script,
+ ps, depth))
+ return False;
+ if (!smb_io_unihdr("hdr_profile_path", &info->hdr_profile_path,
+ ps, depth))
+ return False;
+ if (!smb_io_unihdr("hdr_home_dir", &info->hdr_home_dir, ps, depth))
+ return False;
+ if (!smb_io_unihdr("hdr_dir_drive", &info->hdr_dir_drive, ps, depth))
+ return False;
+
+ if (!prs_uint16("logon_count", ps, depth, &info->logon_count))
+ return False;
+ if (!prs_uint16("reserved12", ps, depth, &info->reserved12))
+ return False;
+ if (!prs_uint32("user_rid", ps, depth, &info->user_rid))
+ return False;
+ if (!prs_uint32("group_rid", ps, depth, &info->group_rid))
+ return False;
+ if (!prs_uint32("group_count", ps, depth, &info->group_count))
+ return False;
+ /* I haven't seen this contain anything yet, but when it does
+ we will have to make sure we decode the contents in the middle
+ all the unistr2s ... */
+ if (!prs_uint32("group_mem_ptr", ps, depth,
+ &info->group_membership_ptr))
+ return False;
+ if (!prs_uint32("user_flags", ps, depth, &info->user_flags))
+ return False;
+
+ if (!prs_uint32("reserved13.0", ps, depth, &info->reserved13[0]))
+ return False;
+ if (!prs_uint32("reserved13.1", ps, depth, &info->reserved13[1]))
+ return False;
+ if (!prs_uint32("reserved13.2", ps, depth, &info->reserved13[2]))
+ return False;
+ if (!prs_uint32("reserved13.3", ps, depth, &info->reserved13[3]))
+ return False;
+
+ if (!smb_io_unihdr("hdr_dom_controller",
+ &info->hdr_dom_controller, ps, depth))
+ return False;
+ if (!smb_io_unihdr("hdr_dom_name", &info->hdr_dom_name, ps, depth))
+ return False;
+
+ /* this should be followed, but just get ptr for now */
+ if (!prs_uint32("ptr_dom_sid", ps, depth, &info->ptr_dom_sid))
+ return False;
+
+ if (!prs_uint32("reserved16.0", ps, depth, &info->reserved16[0]))
+ return False;
+ if (!prs_uint32("reserved16.1", ps, depth, &info->reserved16[1]))
+ return False;
+
+ /* might be acb_info */
+ if (!prs_uint32("reserved17", ps, depth, &info->reserved17))
+ return False;
+
+
+ if (!prs_uint32("reserved18.0", ps, depth, &info->reserved18[0]))
+ return False;
+ if (!prs_uint32("reserved18.1", ps, depth, &info->reserved18[1]))
+ return False;
+ if (!prs_uint32("reserved18.2", ps, depth, &info->reserved18[2]))
+ return False;
+ if (!prs_uint32("reserved18.3", ps, depth, &info->reserved18[3]))
+ return False;
+ if (!prs_uint32("reserved18.4", ps, depth, &info->reserved18[4]))
+ return False;
+ if (!prs_uint32("reserved18.5", ps, depth, &info->reserved18[5]))
+ return False;
+ if (!prs_uint32("reserved18.6", ps, depth, &info->reserved18[6]))
+ return False;
+
+ if (!prs_uint32("sid_count", ps, depth, &info->sid_count))
+ return False;
+ if (!prs_uint32("ptr_extra_sids", ps, depth, &info->ptr_extra_sids))
+ return False;
+ if (!prs_uint32("ptr_res_group_dom_sid", ps, depth,
+ &info->ptr_res_group_dom_sid))
+ return False;
+ if (!prs_uint32("res_group_count", ps, depth, &info->res_group_count))
+ return False;
+ if (!prs_uint32("ptr_res_groups", ps, depth, &info->ptr_res_groups))
+ return False;
+
+ if(!smb_io_unistr2("uni_user_name", &info->uni_user_name,
+ info->hdr_user_name.buffer, ps, depth))
+ return False;
+ if(!smb_io_unistr2("uni_full_name", &info->uni_full_name,
+ info->hdr_full_name.buffer, ps, depth))
+ return False;
+ if(!smb_io_unistr2("uni_logon_script", &info->uni_logon_script,
+ info->hdr_logon_script.buffer, ps, depth))
+ return False;
+ if(!smb_io_unistr2("uni_profile_path", &info->uni_profile_path,
+ info->hdr_profile_path.buffer, ps, depth))
+ return False;
+ if(!smb_io_unistr2("uni_home_dir", &info->uni_home_dir,
+ info->hdr_home_dir.buffer, ps, depth))
+ return False;
+ if(!smb_io_unistr2("uni_dir_drive", &info->uni_dir_drive,
+ info->hdr_dir_drive.buffer, ps, depth))
+ return False;
+
+ if (info->group_membership_ptr) {
+ if (!pac_io_group_membership_array("group membership",
+ &info->groups,
+ info->group_count,
+ ps, depth))
+ return False;
+ }
+
+
+ if(!smb_io_unistr2("uni_dom_controller", &info->uni_dom_controller,
+ info->hdr_dom_controller.buffer, ps, depth))
+ return False;
+ if(!smb_io_unistr2("uni_dom_name", &info->uni_dom_name,
+ info->hdr_dom_name.buffer, ps, depth))
+ return False;
+
+ if(info->ptr_dom_sid)
+ if(!smb_io_dom_sid2("dom_sid", &info->dom_sid, ps, depth))
+ return False;
+
+
+ if (info->sid_count && info->ptr_extra_sids)
+ if (!pac_io_krb_sid_and_attr_array("extra_sids",
+ &info->extra_sids,
+ info->sid_count,
+ ps, depth))
+ return False;
+
+ if (info->ptr_res_group_dom_sid)
+ if (!smb_io_dom_sid2("res_group_dom_sid",
+ &info->res_group_dom_sid, ps, depth))
+ return False;
+
+ if (info->ptr_res_groups)
+ if (!pac_io_group_membership_array("res group membership",
+ &info->res_groups,
+ info->res_group_count,
+ ps, depth))
+ return False;
+
+ return True;
+}
+
+
+static BOOL pac_io_pac_signature_data(const char *desc,
+ PAC_SIGNATURE_DATA *data, uint32 length,
+ prs_struct *ps, int depth)
+{
+ uint32 siglen = length - sizeof(uint32);
+ if (NULL == data)
+ return False;
+
+ prs_debug(ps, depth, desc, "pac_io_pac_signature_data");
+ depth++;
+
+ if (!prs_uint32("type", ps, depth, &data->type))
+ return False;
+ if (UNMARSHALLING(ps)) {
+ data->signature = (unsigned char *)prs_alloc_mem(ps, siglen);
+ if (!data->signature) {
+ DEBUG(3, ("No memory available\n"));
+ return False;
+ }
+ }
+ if (!prs_uint8s(False, "signature", ps, depth, data->signature,siglen))
+ return False;
+
+ return True;
+}
+
+static BOOL pac_io_pac_info_hdr_ctr(const char *desc, PAC_INFO_HDR *hdr,
+ prs_struct *ps, int depth)
+{
+ if (NULL == hdr)
+ return False;
+
+ prs_debug(ps, depth, desc, "pac_io_pac_info_hdr_ctr");
+ depth++;
+
+ if (!prs_align(ps))
+ return False;
+
+ if (hdr->offset != prs_offset(ps)) {
+ DEBUG(5, ("offset in header(x%x) and data(x%x) do not match\n",
+ hdr->offset, prs_offset(ps)));
+ prs_set_offset(ps, hdr->offset);
+ }
+
+ if (UNMARSHALLING(ps) && hdr->size > 0) {
+ hdr->ctr = (PAC_INFO_CTR *)
+ prs_alloc_mem(ps, sizeof(PAC_INFO_CTR));
+ if (!hdr->ctr) {
+ DEBUG(3, ("No memory available\n"));
+ return False;
+ }
+ }
+
+ switch(hdr->type) {
+ case PAC_TYPE_LOGON_INFO:
+ DEBUG(5, ("PAC_TYPE_LOGON_INFO\n"));
+ if (UNMARSHALLING(ps))
+ hdr->ctr->pac.logon_info = (PAC_LOGON_INFO *)
+ prs_alloc_mem(ps, sizeof(PAC_LOGON_INFO));
+ if (!hdr->ctr->pac.logon_info) {
+ DEBUG(3, ("No memory available\n"));
+ return False;
+ }
+ if (!pac_io_pac_logon_info(desc, hdr->ctr->pac.logon_info,
+ ps, depth))
+ return False;
+ break;
+
+ case PAC_TYPE_SERVER_CHECKSUM:
+ DEBUG(5, ("PAC_TYPE_SERVER_CHECKSUM\n"));
+ if (UNMARSHALLING(ps))
+ hdr->ctr->pac.srv_cksum = (PAC_SIGNATURE_DATA *)
+ prs_alloc_mem(ps, sizeof(PAC_SIGNATURE_DATA));
+ if (!hdr->ctr->pac.srv_cksum) {
+ DEBUG(3, ("No memory available\n"));
+ return False;
+ }
+ if (!pac_io_pac_signature_data(desc, hdr->ctr->pac.srv_cksum,
+ hdr->size, ps, depth))
+ return False;
+ break;
+
+ case PAC_TYPE_PRIVSVR_CHECKSUM:
+ DEBUG(5, ("PAC_TYPE_PRIVSVR_CHECKSUM\n"));
+ if (UNMARSHALLING(ps))
+ hdr->ctr->pac.privsrv_cksum = (PAC_SIGNATURE_DATA *)
+ prs_alloc_mem(ps, sizeof(PAC_SIGNATURE_DATA));
+ if (!hdr->ctr->pac.privsrv_cksum) {
+ DEBUG(3, ("No memory available\n"));
+ return False;
+ }
+ if (!pac_io_pac_signature_data(desc,
+ hdr->ctr->pac.privsrv_cksum,
+ hdr->size, ps, depth))
+ return False;
+ break;
+
+ case PAC_TYPE_UNKNOWN_10:
+ DEBUG(5, ("PAC_TYPE_UNKNOWN_10\n"));
+ if (UNMARSHALLING(ps))
+ hdr->ctr->pac.type_10 = (UNKNOWN_TYPE_10 *)
+ prs_alloc_mem(ps, sizeof(UNKNOWN_TYPE_10));
+ if (!hdr->ctr->pac.type_10) {
+ DEBUG(3, ("No memory available\n"));
+ return False;
+ }
+ if (!pac_io_unknown_type_10(desc, hdr->ctr->pac.type_10,
+ ps, depth))
+ return False;
+ break;
+
+ default:
+ /* dont' know, so we need to skip it */
+ DEBUG(3, ("unknown PAC type %d\n", hdr->type));
+ prs_set_offset(ps, prs_offset(ps) + hdr->size);
+ }
+
+ return True;
+}
+
+static BOOL pac_io_pac_info_hdr(const char *desc, PAC_INFO_HDR *hdr,
+ prs_struct *ps, int depth)
+{
+ if (NULL == hdr)
+ return False;
+
+ prs_debug(ps, depth, desc, "pac_io_pac_info_hdr");
+ depth++;
+
+ if (!prs_align(ps))
+ return False;
+ if (!prs_uint32("type", ps, depth, &hdr->type))
+ return False;
+ if (!prs_uint32("size", ps, depth, &hdr->size))
+ return False;
+ if (!prs_uint32("offset", ps, depth, &hdr->offset))
+ return False;
+ if (!prs_uint32("offsethi", ps, depth, &hdr->offsethi))
+ return False;
+
+ return True;
+}
+
+static BOOL pac_io_pac_data(const char *desc, PAC_DATA *data,
+ prs_struct *ps, int depth)
+{
+ int i;
+
+ if (NULL == data)
+ return False;
+
+ prs_debug(ps, depth, desc, "pac_io_pac_data");
+ depth++;
+
+ if (!prs_align(ps))
+ return False;
+ if (!prs_uint32("num_buffers", ps, depth, &data->num_buffers))
+ return False;
+ if (!prs_uint32("version", ps, depth, &data->version))
+ return False;
+
+ if (UNMARSHALLING(ps) && data->num_buffers > 0) {
+ if ((data->pac_info_hdr_ptr = (PAC_INFO_HDR *)
+ prs_alloc_mem(ps, sizeof(PAC_INFO_HDR) *
+ data->num_buffers)) == NULL) {
+ return False;
+ }
+ }
+
+ for (i=0; i<data->num_buffers; i++) {
+ if (!pac_io_pac_info_hdr(desc, &data->pac_info_hdr_ptr[i], ps,
+ depth))
+ return False;
+ }
+
+ for (i=0; i<data->num_buffers; i++) {
+ if (!pac_io_pac_info_hdr_ctr(desc, &data->pac_info_hdr_ptr[i],
+ ps, depth))
+ return False;
+ }
+
+ return True;
+}
+
+PAC_DATA *decode_pac_data(DATA_BLOB *auth_data, TALLOC_CTX *ctx)
+{
+ DATA_BLOB pac_data_blob = unwrap_pac(auth_data);
+ prs_struct ps;
+ PAC_DATA *pac_data;
+
+ DEBUG(5,("dump_pac_data\n"));
+ prs_init(&ps, pac_data_blob.length, ctx, UNMARSHALL);
+ prs_copy_data_in(&ps, (char *)pac_data_blob.data, pac_data_blob.length);
+ prs_set_offset(&ps, 0);
+
+ data_blob_free(&pac_data_blob);
+
+ pac_data = (PAC_DATA *) talloc_zero(ctx, sizeof(PAC_DATA));
+ pac_io_pac_data("pac data", pac_data, &ps, 0);
+
+ prs_mem_free(&ps);
+
+ return pac_data;
+}
+
+#endif
diff --git a/source/libads/disp_sec.c b/source/libads/disp_sec.c
new file mode 100644
index 00000000000..c9de447e698
--- /dev/null
+++ b/source/libads/disp_sec.c
@@ -0,0 +1,159 @@
+/*
+ Unix SMB/CIFS implementation.
+ Samba utility functions. ADS stuff
+ Copyright (C) Alexey Kotovich 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 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"
+
+static struct perm_mask_str {
+ uint32 mask;
+ const char *str;
+} perms[] = {
+ {SEC_RIGHTS_FULL_CTRL, "[Full Control]"},
+
+ {SEC_RIGHTS_LIST_CONTENTS, "[List Contents]"},
+ {SEC_RIGHTS_LIST_OBJECT, "[List Object]"},
+
+ {SEC_RIGHTS_READ_ALL_PROP, "[Read All Properties]"},
+ {SEC_RIGHTS_READ_PERMS, "[Read Permissions]"},
+
+ {SEC_RIGHTS_WRITE_ALL_VALID, "[All validate writes]"},
+ {SEC_RIGHTS_WRITE_ALL_PROP, "[Write All Properties]"},
+
+ {SEC_RIGHTS_MODIFY_PERMS, "[Modify Permissions]"},
+ {SEC_RIGHTS_MODIFY_OWNER, "[Modify Owner]"},
+
+ {SEC_RIGHTS_CREATE_CHILD, "[Create All Child Objects]"},
+
+ {SEC_RIGHTS_DELETE, "[Delete]"},
+ {SEC_RIGHTS_DELETE_SUBTREE, "[Delete Subtree]"},
+ {SEC_RIGHTS_DELETE_CHILD, "[Delete All Child Objects]"},
+
+ {SEC_RIGHTS_CHANGE_PASSWD, "[Change Password]"},
+ {SEC_RIGHTS_RESET_PASSWD, "[Reset Password]"},
+ {0, 0}
+};
+
+/* convert a security permissions into a string */
+static void ads_disp_perms(uint32 type)
+{
+ int i = 0;
+ int j = 0;
+
+ printf("Permissions: ");
+
+ if (type == SEC_RIGHTS_FULL_CTRL) {
+ printf("%s\n", perms[j].str);
+ return;
+ }
+
+ for (i = 0; i < 32; i++) {
+ if (type & (1 << i)) {
+ for (j = 1; perms[j].str; j ++) {
+ if (perms[j].mask == (((unsigned) 1) << i)) {
+ printf("\n\t%s", perms[j].str);
+ }
+ }
+ type &= ~(1 << i);
+ }
+ }
+
+ /* remaining bits get added on as-is */
+ if (type != 0) {
+ printf("[%08x]", type);
+ }
+ puts("");
+}
+
+/* display ACE */
+static void ads_disp_ace(SEC_ACE *sec_ace)
+{
+ const char *access_type = "UNKNOWN";
+
+ if (!sec_ace_object(sec_ace->type)) {
+ printf("------- ACE (type: 0x%02x, flags: 0x%02x, size: 0x%02x, mask: 0x%x)\n",
+ sec_ace->type,
+ sec_ace->flags,
+ sec_ace->size,
+ sec_ace->info.mask);
+ } else {
+ printf("------- ACE (type: 0x%02x, flags: 0x%02x, size: 0x%02x, mask: 0x%x, object flags: 0x%x)\n",
+ sec_ace->type,
+ sec_ace->flags,
+ sec_ace->size,
+ sec_ace->info.mask,
+ sec_ace->obj_flags);
+ }
+
+ if (sec_ace->type == SEC_ACE_TYPE_ACCESS_ALLOWED) {
+ access_type = "ALLOWED";
+ } else if (sec_ace->type == SEC_ACE_TYPE_ACCESS_DENIED) {
+ access_type = "DENIED";
+ } else if (sec_ace->type == SEC_ACE_TYPE_SYSTEM_AUDIT) {
+ access_type = "SYSTEM AUDIT";
+ } else if (sec_ace->type == SEC_ACE_TYPE_ACCESS_ALLOWED_OBJECT) {
+ access_type = "ALLOWED OBJECT";
+ } else if (sec_ace->type == SEC_ACE_TYPE_ACCESS_DENIED_OBJECT) {
+ access_type = "DENIED OBJECT";
+ } else if (sec_ace->type == SEC_ACE_TYPE_SYSTEM_AUDIT_OBJECT) {
+ access_type = "AUDIT OBJECT";
+ }
+
+ printf("access SID: %s\naccess type: %s\n",
+ sid_string_static(&sec_ace->trustee), access_type);
+
+ ads_disp_perms(sec_ace->info.mask);
+}
+
+/* display ACL */
+static void ads_disp_acl(SEC_ACL *sec_acl, const char *type)
+{
+ if (!sec_acl)
+ printf("------- (%s) ACL not present\n", type);
+ else {
+ printf("------- (%s) ACL (revision: %d, size: %d, number of ACEs: %d)\n",
+ type,
+ sec_acl->revision,
+ sec_acl->size,
+ sec_acl->num_aces);
+ }
+}
+
+/* display SD */
+void ads_disp_sd(SEC_DESC *sd)
+{
+ int i;
+
+ printf("-------------- Security Descriptor (revision: %d, type: 0x%02x)\n",
+ sd->revision,
+ sd->type);
+ printf("owner SID: %s\n", sid_string_static(sd->owner_sid));
+ printf("group SID: %s\n", sid_string_static(sd->grp_sid));
+
+ ads_disp_acl(sd->sacl, "system");
+ for (i = 0; i < sd->sacl->num_aces; i ++)
+ ads_disp_ace(&sd->sacl->ace[i]);
+
+ ads_disp_acl(sd->dacl, "user");
+ for (i = 0; i < sd->dacl->num_aces; i ++)
+ ads_disp_ace(&sd->dacl->ace[i]);
+
+ printf("-------------- End Of Security Descriptor\n");
+}
+
+
diff --git a/source/libads/kerberos.c b/source/libads/kerberos.c
new file mode 100644
index 00000000000..70f6f3386c7
--- /dev/null
+++ b/source/libads/kerberos.c
@@ -0,0 +1,175 @@
+/*
+ Unix SMB/CIFS implementation.
+ kerberos utility library
+ Copyright (C) Andrew Tridgell 2001
+ Copyright (C) Remus Koos 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 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"
+
+#ifdef HAVE_KRB5
+
+/*
+ we use a prompter to avoid a crash bug in the kerberos libs when
+ dealing with empty passwords
+ this prompter is just a string copy ...
+*/
+static krb5_error_code
+kerb_prompter(krb5_context ctx, void *data,
+ const char *name,
+ const char *banner,
+ int num_prompts,
+ krb5_prompt prompts[])
+{
+ if (num_prompts == 0) return 0;
+
+ memset(prompts[0].reply->data, 0, prompts[0].reply->length);
+ if (prompts[0].reply->length > 0) {
+ if (data) {
+ strncpy(prompts[0].reply->data, data, prompts[0].reply->length-1);
+ prompts[0].reply->length = strlen(prompts[0].reply->data);
+ } else {
+ prompts[0].reply->length = 0;
+ }
+ }
+ return 0;
+}
+
+/*
+ simulate a kinit, putting the tgt in the default cache location
+ remus@snapserver.com
+*/
+int kerberos_kinit_password(const char *principal, const char *password, int time_offset, time_t *expire_time)
+{
+ krb5_context ctx;
+ krb5_error_code code = 0;
+ krb5_ccache cc;
+ krb5_principal me;
+ krb5_creds my_creds;
+
+ if ((code = krb5_init_context(&ctx)))
+ return code;
+
+ if (time_offset != 0) {
+ krb5_set_real_time(ctx, time(NULL) + time_offset, 0);
+ }
+
+ if ((code = krb5_cc_default(ctx, &cc))) {
+ krb5_free_context(ctx);
+ return code;
+ }
+
+ if ((code = krb5_parse_name(ctx, principal, &me))) {
+ krb5_free_context(ctx);
+ return code;
+ }
+
+ if ((code = krb5_get_init_creds_password(ctx, &my_creds, me, NULL,
+ kerb_prompter,
+ password, 0, NULL, NULL))) {
+ krb5_free_principal(ctx, me);
+ krb5_free_context(ctx);
+ return code;
+ }
+
+ if ((code = krb5_cc_initialize(ctx, cc, me))) {
+ krb5_free_cred_contents(ctx, &my_creds);
+ krb5_free_principal(ctx, me);
+ krb5_free_context(ctx);
+ return code;
+ }
+
+ if ((code = krb5_cc_store_cred(ctx, cc, &my_creds))) {
+ krb5_cc_close(ctx, cc);
+ krb5_free_cred_contents(ctx, &my_creds);
+ krb5_free_principal(ctx, me);
+ krb5_free_context(ctx);
+ return code;
+ }
+
+ if (expire_time)
+ *expire_time = (time_t) my_creds.times.endtime;
+
+ krb5_cc_close(ctx, cc);
+ krb5_free_cred_contents(ctx, &my_creds);
+ krb5_free_principal(ctx, me);
+ krb5_free_context(ctx);
+
+ return 0;
+}
+
+
+
+/* run kinit to setup our ccache */
+int ads_kinit_password(ADS_STRUCT *ads)
+{
+ char *s;
+ int ret;
+
+ if (asprintf(&s, "%s@%s", ads->auth.user_name, ads->auth.realm) == -1) {
+ return KRB5_CC_NOMEM;
+ }
+
+ if (!ads->auth.password) {
+ return KRB5_LIBOS_CANTREADPWD;
+ }
+
+ ret = kerberos_kinit_password(s, ads->auth.password, ads->auth.time_offset, &ads->auth.expire);
+
+ if (ret) {
+ DEBUG(0,("kerberos_kinit_password %s failed: %s\n",
+ s, error_message(ret)));
+ }
+ free(s);
+ return ret;
+}
+
+int ads_kdestroy(const char *cc_name)
+{
+ krb5_error_code code;
+ krb5_context ctx;
+ krb5_ccache cc;
+
+ if ((code = krb5_init_context (&ctx))) {
+ DEBUG(3, ("ads_kdestroy: kdb5_init_context rc=%d\n", code));
+ return code;
+ }
+
+ if (!cc_name) {
+ if ((code = krb5_cc_default(ctx, &cc))) {
+ krb5_free_context(ctx);
+ return code;
+ }
+ } else {
+ if ((code = krb5_cc_resolve(ctx, cc_name, &cc))) {
+ DEBUG(3, ("ads_kdestroy: krb5_cc_resolve rc=%d\n",
+ code));
+ krb5_free_context(ctx);
+ return code;
+ }
+ }
+
+ if ((code = krb5_cc_destroy (ctx, cc))) {
+ DEBUG(3, ("ads_kdestroy: krb5_cc_destroy rc=%d\n", code));
+ }
+
+ krb5_free_context (ctx);
+ return code;
+}
+
+#endif
diff --git a/source/libads/kerberos_verify.c b/source/libads/kerberos_verify.c
new file mode 100644
index 00000000000..47559c1abb7
--- /dev/null
+++ b/source/libads/kerberos_verify.c
@@ -0,0 +1,271 @@
+/*
+ Unix SMB/CIFS implementation.
+ kerberos utility library
+ Copyright (C) Andrew Tridgell 2001
+ Copyright (C) Remus Koos 2001
+ Copyright (C) Luke Howard 2003
+ Copyright (C) Guenther Deschner 2003
+ 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 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"
+
+#ifdef HAVE_KRB5
+
+/*
+ verify an incoming ticket and parse out the principal name and
+ authorization_data if available
+*/
+NTSTATUS ads_verify_ticket(const char *realm, const DATA_BLOB *ticket,
+ char **principal, DATA_BLOB *auth_data,
+ DATA_BLOB *ap_rep,
+ DATA_BLOB *session_key)
+{
+ NTSTATUS sret = NT_STATUS_LOGON_FAILURE;
+ krb5_context context = NULL;
+ krb5_auth_context auth_context = NULL;
+ krb5_data packet;
+ krb5_ticket *tkt = NULL;
+ krb5_rcache rcache = NULL;
+ int ret, i;
+ krb5_keyblock *key = NULL;
+
+ krb5_principal host_princ;
+ char *host_princ_s = NULL;
+ BOOL free_host_princ = False;
+ BOOL got_replay_mutex = False;
+
+ fstring myname;
+ char *password_s = NULL;
+ krb5_data password;
+ krb5_enctype *enctypes = NULL;
+#if 0
+ krb5_address local_addr;
+ krb5_address remote_addr;
+#endif
+ BOOL auth_ok = False;
+
+ ZERO_STRUCT(packet);
+ ZERO_STRUCT(password);
+ ZERO_STRUCTP(auth_data);
+ ZERO_STRUCTP(ap_rep);
+
+ if (!secrets_init()) {
+ DEBUG(1,("ads_verify_ticket: secrets_init failed\n"));
+ return NT_STATUS_LOGON_FAILURE;
+ }
+
+ password_s = secrets_fetch_machine_password(lp_workgroup(), NULL, NULL);
+ if (!password_s) {
+ DEBUG(1,("ads_verify_ticket: failed to fetch machine password\n"));
+ return NT_STATUS_LOGON_FAILURE;
+ }
+
+ password.data = password_s;
+ password.length = strlen(password_s);
+
+ ret = krb5_init_context(&context);
+ if (ret) {
+ DEBUG(1,("ads_verify_ticket: krb5_init_context failed (%s)\n", error_message(ret)));
+ return NT_STATUS_LOGON_FAILURE;
+ }
+
+ ret = krb5_set_default_realm(context, realm);
+ if (ret) {
+ DEBUG(1,("ads_verify_ticket: krb5_set_default_realm failed (%s)\n", error_message(ret)));
+ sret = NT_STATUS_LOGON_FAILURE;
+ goto out;
+ }
+
+ /* This whole process is far more complex than I would
+ like. We have to go through all this to allow us to store
+ the secret internally, instead of using /etc/krb5.keytab */
+
+ ret = krb5_auth_con_init(context, &auth_context);
+ if (ret) {
+ DEBUG(1,("ads_verify_ticket: krb5_auth_con_init failed (%s)\n", error_message(ret)));
+ sret = NT_STATUS_LOGON_FAILURE;
+ goto out;
+ }
+
+ fstrcpy(myname, global_myname());
+ strlower_m(myname);
+ asprintf(&host_princ_s, "HOST/%s@%s", myname, lp_realm());
+ ret = krb5_parse_name(context, host_princ_s, &host_princ);
+ if (ret) {
+ DEBUG(1,("ads_verify_ticket: krb5_parse_name(%s) failed (%s)\n",
+ host_princ_s, error_message(ret)));
+ sret = NT_STATUS_LOGON_FAILURE;
+ goto out;
+ }
+
+ free_host_princ = True;
+
+ /*
+ * JRA. We must set the rcache here. This will prevent replay attacks.
+ */
+
+ ret = krb5_get_server_rcache(context, krb5_princ_component(context, host_princ, 0), &rcache);
+ if (ret) {
+ DEBUG(1,("ads_verify_ticket: krb5_get_server_rcache failed (%s)\n", error_message(ret)));
+ sret = NT_STATUS_LOGON_FAILURE;
+ goto out;
+ }
+
+ ret = krb5_auth_con_setrcache(context, auth_context, rcache);
+ if (ret) {
+ DEBUG(1,("ads_verify_ticket: krb5_auth_con_setrcache failed (%s)\n", error_message(ret)));
+ sret = NT_STATUS_LOGON_FAILURE;
+ goto out;
+ }
+
+ /* CIFS doesn't use addresses in tickets. This would breat NAT. JRA */
+
+ if ((ret = get_kerberos_allowed_etypes(context, &enctypes))) {
+ DEBUG(1,("ads_verify_ticket: krb5_get_permitted_enctypes failed (%s)\n",
+ error_message(ret)));
+ sret = NT_STATUS_LOGON_FAILURE;
+ goto out;
+ }
+
+ /* Lock a mutex surrounding the replay as there is no locking in the MIT krb5
+ * code surrounding the replay cache... */
+
+ if (!grab_server_mutex("replay cache mutex")) {
+ DEBUG(1,("ads_verify_ticket: unable to protect replay cache with mutex.\n"));
+ sret = NT_STATUS_LOGON_FAILURE;
+ goto out;
+ }
+
+ got_replay_mutex = True;
+
+ /* We need to setup a auth context with each possible encoding type in turn. */
+ for (i=0;enctypes[i];i++) {
+ if (!(key = (krb5_keyblock *)malloc(sizeof(*key)))) {
+ sret = NT_STATUS_NO_MEMORY;
+ goto out;
+ }
+
+ if (create_kerberos_key_from_string(context, host_princ, &password, key, enctypes[i])) {
+ continue;
+ }
+
+ krb5_auth_con_setuseruserkey(context, auth_context, key);
+
+ krb5_free_keyblock(context, key);
+
+ packet.length = ticket->length;
+ packet.data = (krb5_pointer)ticket->data;
+
+ if (!(ret = krb5_rd_req(context, &auth_context, &packet,
+ NULL,
+ NULL, NULL, &tkt))) {
+ DEBUG(10,("ads_verify_ticket: enc type [%u] decrypted message !\n",
+ (unsigned int)enctypes[i] ));
+ auth_ok = True;
+ break;
+ }
+
+ DEBUG((ret != KRB5_BAD_ENCTYPE) ? 3 : 10,
+ ("ads_verify_ticket: enc type [%u] failed to decrypt with error %s\n",
+ (unsigned int)enctypes[i], error_message(ret)));
+ }
+
+ release_server_mutex();
+ got_replay_mutex = False;
+
+ if (!auth_ok) {
+ DEBUG(3,("ads_verify_ticket: krb5_rd_req with auth failed (%s)\n",
+ error_message(ret)));
+ sret = NT_STATUS_LOGON_FAILURE;
+ goto out;
+ }
+
+ ret = krb5_mk_rep(context, auth_context, &packet);
+ if (ret) {
+ DEBUG(3,("ads_verify_ticket: Failed to generate mutual authentication reply (%s)\n",
+ error_message(ret)));
+ sret = NT_STATUS_LOGON_FAILURE;
+ goto out;
+ }
+
+ *ap_rep = data_blob(packet.data, packet.length);
+ free(packet.data);
+
+ get_krb5_smb_session_key(context, auth_context, session_key, True);
+ dump_data_pw("SMB session key (from ticket)\n", session_key->data, session_key->length);
+
+#if 0
+ file_save("/tmp/ticket.dat", ticket->data, ticket->length);
+#endif
+
+ get_auth_data_from_tkt(auth_data, tkt);
+
+ {
+ TALLOC_CTX *ctx = talloc_init("pac data");
+ decode_pac_data(auth_data, ctx);
+ talloc_destroy(ctx);
+ }
+
+#if 0
+ if (tkt->enc_part2) {
+ file_save("/tmp/authdata.dat",
+ tkt->enc_part2->authorization_data[0]->contents,
+ tkt->enc_part2->authorization_data[0]->length);
+ }
+#endif
+
+ if ((ret = krb5_unparse_name(context, get_principal_from_tkt(tkt),
+ principal))) {
+ DEBUG(3,("ads_verify_ticket: krb5_unparse_name failed (%s)\n",
+ error_message(ret)));
+ sret = NT_STATUS_LOGON_FAILURE;
+ goto out;
+ }
+
+ sret = NT_STATUS_OK;
+
+ out:
+
+ if (got_replay_mutex)
+ release_server_mutex();
+
+ if (!NT_STATUS_IS_OK(sret))
+ data_blob_free(auth_data);
+
+ if (!NT_STATUS_IS_OK(sret))
+ data_blob_free(ap_rep);
+
+ if (free_host_princ)
+ krb5_free_principal(context, host_princ);
+
+ if (tkt != NULL)
+ krb5_free_ticket(context, tkt);
+ free_kerberos_etypes(context, enctypes);
+ SAFE_FREE(password_s);
+ SAFE_FREE(host_princ_s);
+
+ if (auth_context)
+ krb5_auth_con_free(context, auth_context);
+
+ if (context)
+ krb5_free_context(context);
+
+ return sret;
+}
+
+#endif /* HAVE_KRB5 */
diff --git a/source/libads/krb5_setpw.c b/source/libads/krb5_setpw.c
new file mode 100644
index 00000000000..16d3df83e93
--- /dev/null
+++ b/source/libads/krb5_setpw.c
@@ -0,0 +1,690 @@
+/*
+ Unix SMB/CIFS implementation.
+ krb5 set password implementation
+ Copyright (C) Andrew Tridgell 2001
+ Copyright (C) Remus Koos 2001 (remuskoos@yahoo.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 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"
+
+#ifdef HAVE_KRB5
+
+#define DEFAULT_KPASSWD_PORT 464
+#define KRB5_KPASSWD_VERS_CHANGEPW 1
+#define KRB5_KPASSWD_VERS_SETPW 2
+#define KRB5_KPASSWD_VERS_SETPW_MS 0xff80
+#define KRB5_KPASSWD_ACCESSDENIED 5
+#define KRB5_KPASSWD_BAD_VERSION 6
+#define KRB5_KPASSWD_INITIAL_FLAG_NEEDED 7
+
+/* Those are defined by kerberos-set-passwd-02.txt and are probably
+ * not supported by M$ implementation */
+#define KRB5_KPASSWD_POLICY_REJECT 8
+#define KRB5_KPASSWD_BAD_PRINCIPAL 9
+#define KRB5_KPASSWD_ETYPE_NOSUPP 10
+
+/* This implements kerberos password change protocol as specified in
+ * kerb-chg-password-02.txt and kerberos-set-passwd-02.txt
+ * as well as microsoft version of the protocol
+ * as specified in kerberos-set-passwd-00.txt
+ */
+static DATA_BLOB encode_krb5_setpw(const char *principal, const char *password)
+{
+ char* princ_part1 = NULL;
+ char* princ_part2 = NULL;
+ char* realm = NULL;
+ char* c;
+ char* princ;
+
+ ASN1_DATA req;
+ DATA_BLOB ret;
+
+
+ princ = strdup(principal);
+
+ if ((c = strchr(princ, '/')) == NULL) {
+ c = princ;
+ } else {
+ *c = '\0';
+ c++;
+ princ_part1 = princ;
+ }
+
+ princ_part2 = c;
+
+ if ((c = strchr(c, '@')) != NULL) {
+ *c = '\0';
+ c++;
+ realm = c;
+ }
+
+ memset(&req, 0, sizeof(req));
+
+ asn1_push_tag(&req, ASN1_SEQUENCE(0));
+ asn1_push_tag(&req, ASN1_CONTEXT(0));
+ asn1_write_OctetString(&req, password, strlen(password));
+ asn1_pop_tag(&req);
+
+ asn1_push_tag(&req, ASN1_CONTEXT(1));
+ asn1_push_tag(&req, ASN1_SEQUENCE(0));
+
+ asn1_push_tag(&req, ASN1_CONTEXT(0));
+ asn1_write_Integer(&req, 1);
+ asn1_pop_tag(&req);
+
+ asn1_push_tag(&req, ASN1_CONTEXT(1));
+ asn1_push_tag(&req, ASN1_SEQUENCE(0));
+
+ if (princ_part1)
+ asn1_write_GeneralString(&req, princ_part1);
+
+ asn1_write_GeneralString(&req, princ_part2);
+ asn1_pop_tag(&req);
+ asn1_pop_tag(&req);
+ asn1_pop_tag(&req);
+ asn1_pop_tag(&req);
+
+ asn1_push_tag(&req, ASN1_CONTEXT(2));
+ asn1_write_GeneralString(&req, realm);
+ asn1_pop_tag(&req);
+ asn1_pop_tag(&req);
+
+ ret = data_blob(req.data, req.length);
+ asn1_free(&req);
+
+ free(princ);
+
+ return ret;
+}
+
+static krb5_error_code build_kpasswd_request(uint16 pversion,
+ krb5_context context,
+ krb5_auth_context auth_context,
+ krb5_data *ap_req,
+ const char *princ,
+ const char *passwd,
+ krb5_data *packet)
+{
+ krb5_error_code ret;
+ krb5_data cipherpw;
+ krb5_data encoded_setpw;
+ krb5_replay_data replay;
+ char *p;
+ DATA_BLOB setpw;
+
+ ret = krb5_auth_con_setflags(context,
+ auth_context,KRB5_AUTH_CONTEXT_DO_SEQUENCE);
+ if (ret) {
+ DEBUG(1,("krb5_auth_con_setflags failed (%s)\n",
+ error_message(ret)));
+ return ret;
+ }
+
+ /* handle protocol differences in chpw and setpw */
+ if (pversion == KRB5_KPASSWD_VERS_CHANGEPW)
+ setpw = data_blob(passwd, strlen(passwd));
+ else if (pversion == KRB5_KPASSWD_VERS_SETPW ||
+ pversion == KRB5_KPASSWD_VERS_SETPW_MS)
+ setpw = encode_krb5_setpw(princ, passwd);
+ else
+ return EINVAL;
+
+ encoded_setpw.data = (char *)setpw.data;
+ encoded_setpw.length = setpw.length;
+
+ ret = krb5_mk_priv(context, auth_context,
+ &encoded_setpw, &cipherpw, &replay);
+
+ data_blob_free(&setpw); /*from 'encode_krb5_setpw(...)' */
+
+ if (ret) {
+ DEBUG(1,("krb5_mk_priv failed (%s)\n", error_message(ret)));
+ return ret;
+ }
+
+ packet->data = (char *)malloc(ap_req->length + cipherpw.length + 6);
+ if (!packet->data)
+ return -1;
+
+ /* see the RFC for details */
+ p = ((char *)packet->data) + 2;
+ RSSVAL(p, 0, pversion);
+ p += 2;
+ RSSVAL(p, 0, ap_req->length);
+ p += 2;
+ memcpy(p, ap_req->data, ap_req->length);
+ p += ap_req->length;
+ memcpy(p, cipherpw.data, cipherpw.length);
+ p += cipherpw.length;
+ packet->length = PTR_DIFF(p,packet->data);
+ RSSVAL(packet->data, 0, packet->length);
+
+ free(cipherpw.data); /* from 'krb5_mk_priv(...)' */
+
+ return 0;
+}
+
+static const struct kpasswd_errors {
+ int result_code;
+ const char *error_string;
+} kpasswd_errors[] = {
+ {KRB5_KPASSWD_MALFORMED, "Malformed request error"},
+ {KRB5_KPASSWD_HARDERROR, "Server error"},
+ {KRB5_KPASSWD_AUTHERROR, "Authentication error"},
+ {KRB5_KPASSWD_SOFTERROR, "Password change rejected"},
+ {KRB5_KPASSWD_ACCESSDENIED, "Client does not have proper authorization"},
+ {KRB5_KPASSWD_BAD_VERSION, "Protocol version not supported"},
+ {KRB5_KPASSWD_INITIAL_FLAG_NEEDED, "Authorization ticket must have initial flag set"},
+ {KRB5_KPASSWD_POLICY_REJECT, "Password rejected due to policy requirements"},
+ {KRB5_KPASSWD_BAD_PRINCIPAL, "Target principal does not exist"},
+ {KRB5_KPASSWD_ETYPE_NOSUPP, "Unsupported encryption type"},
+ {0, NULL}
+};
+
+static krb5_error_code setpw_result_code_string(krb5_context context,
+ int result_code,
+ const char **code_string)
+{
+ unsigned int idx = 0;
+
+ while (kpasswd_errors[idx].error_string != NULL) {
+ if (kpasswd_errors[idx].result_code ==
+ result_code) {
+ *code_string = kpasswd_errors[idx].error_string;
+ return 0;
+ }
+ idx++;
+ }
+ *code_string = "Password change failed";
+ return (0);
+}
+
+static krb5_error_code parse_setpw_reply(krb5_context context,
+ krb5_auth_context auth_context,
+ krb5_data *packet)
+{
+ krb5_data ap_rep;
+ char *p;
+ int vnum, ret, res_code;
+ krb5_data cipherresult;
+ krb5_data clearresult;
+ krb5_ap_rep_enc_part *ap_rep_enc;
+ krb5_replay_data replay;
+
+ if (packet->length < 4) {
+ return KRB5KRB_AP_ERR_MODIFIED;
+ }
+
+ p = packet->data;
+
+ if (((char *)packet->data)[0] == 0x7e || ((char *)packet->data)[0] == 0x5e) {
+ /* it's an error packet. We should parse it ... */
+ DEBUG(1,("Got error packet 0x%x from kpasswd server\n",
+ ((char *)packet->data)[0]));
+ return KRB5KRB_AP_ERR_MODIFIED;
+ }
+
+ if (RSVAL(p, 0) != packet->length) {
+ DEBUG(1,("Bad packet length (%d/%d) from kpasswd server\n",
+ RSVAL(p, 0), packet->length));
+ return KRB5KRB_AP_ERR_MODIFIED;
+ }
+
+ p += 2;
+
+ vnum = RSVAL(p, 0); p += 2;
+
+ /* FIXME: According to standard there is only one type of reply */
+ if (vnum != KRB5_KPASSWD_VERS_SETPW &&
+ vnum != KRB5_KPASSWD_VERS_SETPW_MS &&
+ vnum != KRB5_KPASSWD_VERS_CHANGEPW) {
+ DEBUG(1,("Bad vnum (%d) from kpasswd server\n", vnum));
+ return KRB5KDC_ERR_BAD_PVNO;
+ }
+
+ ap_rep.length = RSVAL(p, 0); p += 2;
+
+ if (p + ap_rep.length >= (char *)packet->data + packet->length) {
+ DEBUG(1,("ptr beyond end of packet from kpasswd server\n"));
+ return KRB5KRB_AP_ERR_MODIFIED;
+ }
+
+ if (ap_rep.length == 0) {
+ DEBUG(1,("got unencrypted setpw result?!\n"));
+ return KRB5KRB_AP_ERR_MODIFIED;
+ }
+
+ /* verify ap_rep */
+ ap_rep.data = p;
+ p += ap_rep.length;
+
+ ret = krb5_rd_rep(context, auth_context, &ap_rep, &ap_rep_enc);
+ if (ret) {
+ DEBUG(1,("failed to rd setpw reply (%s)\n", error_message(ret)));
+ return KRB5KRB_AP_ERR_MODIFIED;
+ }
+
+ krb5_free_ap_rep_enc_part(context, ap_rep_enc);
+
+ cipherresult.data = p;
+ cipherresult.length = ((char *)packet->data + packet->length) - p;
+
+ ret = krb5_rd_priv(context, auth_context, &cipherresult, &clearresult,
+ &replay);
+ if (ret) {
+ DEBUG(1,("failed to decrypt setpw reply (%s)\n", error_message(ret)));
+ return KRB5KRB_AP_ERR_MODIFIED;
+ }
+
+ if (clearresult.length < 2) {
+ free(clearresult.data);
+ ret = KRB5KRB_AP_ERR_MODIFIED;
+ return KRB5KRB_AP_ERR_MODIFIED;
+ }
+
+ p = clearresult.data;
+
+ res_code = RSVAL(p, 0);
+
+ free(clearresult.data);
+
+ if ((res_code < KRB5_KPASSWD_SUCCESS) ||
+ (res_code > KRB5_KPASSWD_ETYPE_NOSUPP)) {
+ return KRB5KRB_AP_ERR_MODIFIED;
+ }
+
+ if(res_code == KRB5_KPASSWD_SUCCESS)
+ return 0;
+ else {
+ const char *errstr;
+ setpw_result_code_string(context, res_code, &errstr);
+ DEBUG(1, ("Error changing password: %s\n", errstr));
+
+ switch(res_code) {
+ case KRB5_KPASSWD_ACCESSDENIED:
+ return KRB5KDC_ERR_BADOPTION;
+ break;
+ case KRB5_KPASSWD_INITIAL_FLAG_NEEDED:
+ return KRB5KDC_ERR_BADOPTION;
+ /* return KV5M_ALT_METHOD; MIT-only define */
+ break;
+ case KRB5_KPASSWD_ETYPE_NOSUPP:
+ return KRB5KDC_ERR_ETYPE_NOSUPP;
+ break;
+ case KRB5_KPASSWD_BAD_PRINCIPAL:
+ return KRB5KDC_ERR_C_PRINCIPAL_UNKNOWN;
+ break;
+ case KRB5_KPASSWD_POLICY_REJECT:
+ return KRB5KDC_ERR_POLICY;
+ break;
+ default:
+ return KRB5KRB_ERR_GENERIC;
+ break;
+ }
+ }
+}
+
+static ADS_STATUS do_krb5_kpasswd_request(krb5_context context,
+ const char *kdc_host,
+ uint16 pversion,
+ krb5_creds *credsp,
+ const char *princ,
+ const char *newpw)
+{
+ krb5_auth_context auth_context = NULL;
+ krb5_data ap_req, chpw_req, chpw_rep;
+ int ret, sock, addr_len;
+ struct sockaddr remote_addr, local_addr;
+ krb5_address local_kaddr, remote_kaddr;
+
+ ret = krb5_mk_req_extended(context, &auth_context, AP_OPTS_USE_SUBKEY,
+ NULL, credsp, &ap_req);
+ if (ret) {
+ DEBUG(1,("krb5_mk_req_extended failed (%s)\n", error_message(ret)));
+ return ADS_ERROR_KRB5(ret);
+ }
+
+ sock = open_udp_socket(kdc_host, DEFAULT_KPASSWD_PORT);
+ if (sock == -1) {
+ int rc = errno;
+ free(ap_req.data);
+ krb5_auth_con_free(context, auth_context);
+ DEBUG(1,("failed to open kpasswd socket to %s (%s)\n",
+ kdc_host, strerror(errno)));
+ return ADS_ERROR_SYSTEM(rc);
+ }
+
+ addr_len = sizeof(remote_addr);
+ getpeername(sock, &remote_addr, &addr_len);
+ addr_len = sizeof(local_addr);
+ getsockname(sock, &local_addr, &addr_len);
+
+ setup_kaddr(&remote_kaddr, &remote_addr);
+ setup_kaddr(&local_kaddr, &local_addr);
+
+ ret = krb5_auth_con_setaddrs(context, auth_context, &local_kaddr, NULL);
+ if (ret) {
+ close(sock);
+ free(ap_req.data);
+ krb5_auth_con_free(context, auth_context);
+ DEBUG(1,("krb5_auth_con_setaddrs failed (%s)\n", error_message(ret)));
+ return ADS_ERROR_KRB5(ret);
+ }
+
+ ret = build_kpasswd_request(pversion, context, auth_context, &ap_req,
+ princ, newpw, &chpw_req);
+ if (ret) {
+ close(sock);
+ free(ap_req.data);
+ krb5_auth_con_free(context, auth_context);
+ DEBUG(1,("build_setpw_request failed (%s)\n", error_message(ret)));
+ return ADS_ERROR_KRB5(ret);
+ }
+
+ if (write(sock, chpw_req.data, chpw_req.length) != chpw_req.length) {
+ close(sock);
+ free(chpw_req.data);
+ free(ap_req.data);
+ krb5_auth_con_free(context, auth_context);
+ DEBUG(1,("send of chpw failed (%s)\n", strerror(errno)));
+ return ADS_ERROR_SYSTEM(errno);
+ }
+
+ free(chpw_req.data);
+
+ chpw_rep.length = 1500;
+ chpw_rep.data = (char *) malloc(chpw_rep.length);
+ if (!chpw_rep.data) {
+ close(sock);
+ free(ap_req.data);
+ krb5_auth_con_free(context, auth_context);
+ DEBUG(1,("send of chpw failed (%s)\n", strerror(errno)));
+ errno = ENOMEM;
+ return ADS_ERROR_SYSTEM(errno);
+ }
+
+ ret = read(sock, chpw_rep.data, chpw_rep.length);
+ if (ret < 0) {
+ close(sock);
+ free(chpw_rep.data);
+ free(ap_req.data);
+ krb5_auth_con_free(context, auth_context);
+ DEBUG(1,("recv of chpw reply failed (%s)\n", strerror(errno)));
+ return ADS_ERROR_SYSTEM(errno);
+ }
+
+ close(sock);
+ chpw_rep.length = ret;
+
+ ret = krb5_auth_con_setaddrs(context, auth_context, NULL,&remote_kaddr);
+ if (ret) {
+ free(chpw_rep.data);
+ free(ap_req.data);
+ krb5_auth_con_free(context, auth_context);
+ DEBUG(1,("krb5_auth_con_setaddrs on reply failed (%s)\n",
+ error_message(ret)));
+ return ADS_ERROR_KRB5(ret);
+ }
+
+ ret = parse_setpw_reply(context, auth_context, &chpw_rep);
+ free(chpw_rep.data);
+
+ if (ret) {
+ free(ap_req.data);
+ krb5_auth_con_free(context, auth_context);
+ DEBUG(1,("parse_setpw_reply failed (%s)\n",
+ error_message(ret)));
+ return ADS_ERROR_KRB5(ret);
+ }
+
+ free(ap_req.data);
+ krb5_auth_con_free(context, auth_context);
+
+ return ADS_SUCCESS;
+}
+
+ADS_STATUS ads_krb5_set_password(const char *kdc_host, const char *princ,
+ const char *newpw, int time_offset)
+{
+
+ ADS_STATUS aret;
+ krb5_error_code ret;
+ krb5_context context;
+ krb5_principal principal;
+ char *princ_name;
+ char *realm;
+ krb5_creds creds, *credsp;
+ krb5_ccache ccache;
+
+ ret = krb5_init_context(&context);
+ if (ret) {
+ DEBUG(1,("Failed to init krb5 context (%s)\n", error_message(ret)));
+ return ADS_ERROR_KRB5(ret);
+ }
+
+ if (time_offset != 0) {
+ krb5_set_real_time(context, time(NULL) + time_offset, 0);
+ }
+
+ ret = krb5_cc_default(context, &ccache);
+ if (ret) {
+ krb5_free_context(context);
+ DEBUG(1,("Failed to get default creds (%s)\n", error_message(ret)));
+ return ADS_ERROR_KRB5(ret);
+ }
+
+ ZERO_STRUCT(creds);
+
+ realm = strchr(princ, '@');
+ realm++;
+
+ asprintf(&princ_name, "kadmin/changepw@%s", realm);
+ ret = krb5_parse_name(context, princ_name, &creds.server);
+ if (ret) {
+ krb5_free_context(context);
+ DEBUG(1,("Failed to parse kadmin/changepw (%s)\n", error_message(ret)));
+ return ADS_ERROR_KRB5(ret);
+ }
+ free(princ_name);
+
+ /* parse the principal we got as a function argument */
+ ret = krb5_parse_name(context, princ, &principal);
+ if (ret) {
+ krb5_free_context(context);
+ DEBUG(1,("Failed to parse %s (%s)\n", princ_name, error_message(ret)));
+ return ADS_ERROR_KRB5(ret);
+ }
+
+ krb5_princ_set_realm(context, creds.server,
+ krb5_princ_realm(context, principal));
+
+ ret = krb5_cc_get_principal(context, ccache, &creds.client);
+ if (ret) {
+ krb5_free_principal(context, principal);
+ krb5_free_context(context);
+ DEBUG(1,("Failed to get principal from ccache (%s)\n",
+ error_message(ret)));
+ return ADS_ERROR_KRB5(ret);
+ }
+
+ ret = krb5_get_credentials(context, 0, ccache, &creds, &credsp);
+ if (ret) {
+ krb5_free_principal(context, creds.client);
+ krb5_free_principal(context, principal);
+ krb5_free_context(context);
+ DEBUG(1,("krb5_get_credentials failed (%s)\n", error_message(ret)));
+ return ADS_ERROR_KRB5(ret);
+ }
+
+ /* we might have to call krb5_free_creds(...) from now on ... */
+
+ aret = do_krb5_kpasswd_request(context, kdc_host,
+ KRB5_KPASSWD_VERS_SETPW_MS,
+ credsp, princ, newpw);
+
+ krb5_free_creds(context, credsp);
+ krb5_free_principal(context, creds.client);
+ krb5_free_principal(context, principal);
+ krb5_free_context(context);
+
+ return aret;
+}
+
+/*
+ we use a prompter to avoid a crash bug in the kerberos libs when
+ dealing with empty passwords
+ this prompter is just a string copy ...
+*/
+static krb5_error_code
+kerb_prompter(krb5_context ctx, void *data,
+ const char *name,
+ const char *banner,
+ int num_prompts,
+ krb5_prompt prompts[])
+{
+ if (num_prompts == 0) return 0;
+
+ memset(prompts[0].reply->data, 0, prompts[0].reply->length);
+ if (prompts[0].reply->length > 0) {
+ if (data) {
+ strncpy(prompts[0].reply->data, data, prompts[0].reply->length-1);
+ prompts[0].reply->length = strlen(prompts[0].reply->data);
+ } else {
+ prompts[0].reply->length = 0;
+ }
+ }
+ return 0;
+}
+
+static ADS_STATUS ads_krb5_chg_password(const char *kdc_host,
+ const char *principal,
+ const char *oldpw,
+ const char *newpw,
+ int time_offset)
+{
+ ADS_STATUS aret;
+ krb5_error_code ret;
+ krb5_context context;
+ krb5_principal princ;
+ krb5_get_init_creds_opt opts;
+ krb5_creds creds;
+ char *chpw_princ = NULL, *password;
+
+ ret = krb5_init_context(&context);
+ if (ret) {
+ DEBUG(1,("Failed to init krb5 context (%s)\n", error_message(ret)));
+ return ADS_ERROR_KRB5(ret);
+ }
+
+ if ((ret = krb5_parse_name(context, principal,
+ &princ))) {
+ krb5_free_context(context);
+ DEBUG(1,("Failed to parse %s (%s)\n", principal, error_message(ret)));
+ return ADS_ERROR_KRB5(ret);
+ }
+
+ krb5_get_init_creds_opt_init(&opts);
+ krb5_get_init_creds_opt_set_tkt_life(&opts, 5*60);
+ krb5_get_init_creds_opt_set_renew_life(&opts, 0);
+ krb5_get_init_creds_opt_set_forwardable(&opts, 0);
+ krb5_get_init_creds_opt_set_proxiable(&opts, 0);
+
+ /* We have to obtain an INITIAL changepw ticket for changing password */
+ asprintf(&chpw_princ, "kadmin/changepw@%s",
+ (char *) krb5_princ_realm(context, princ));
+ password = strdup(oldpw);
+ ret = krb5_get_init_creds_password(context, &creds, princ, password,
+ kerb_prompter, NULL,
+ 0, chpw_princ, &opts);
+ SAFE_FREE(chpw_princ);
+ SAFE_FREE(password);
+
+ if (ret) {
+ if (ret == KRB5KRB_AP_ERR_BAD_INTEGRITY)
+ DEBUG(1,("Password incorrect while getting initial ticket"));
+ else
+ DEBUG(1,("krb5_get_init_creds_password failed (%s)\n", error_message(ret)));
+
+ krb5_free_principal(context, princ);
+ krb5_free_context(context);
+ return ADS_ERROR_KRB5(ret);
+ }
+
+ aret = do_krb5_kpasswd_request(context, kdc_host,
+ KRB5_KPASSWD_VERS_CHANGEPW,
+ &creds, principal, newpw);
+
+ krb5_free_principal(context, princ);
+ krb5_free_context(context);
+
+ return aret;
+}
+
+
+ADS_STATUS kerberos_set_password(const char *kpasswd_server,
+ const char *auth_principal, const char *auth_password,
+ const char *target_principal, const char *new_password,
+ int time_offset)
+{
+ int ret;
+
+ if ((ret = kerberos_kinit_password(auth_principal, auth_password, time_offset, NULL))) {
+ DEBUG(1,("Failed kinit for principal %s (%s)\n", auth_principal, error_message(ret)));
+ return ADS_ERROR_KRB5(ret);
+ }
+
+ if (!strcmp(auth_principal, target_principal))
+ return ads_krb5_chg_password(kpasswd_server, target_principal,
+ auth_password, new_password, time_offset);
+ else
+ return ads_krb5_set_password(kpasswd_server, target_principal,
+ new_password, time_offset);
+}
+
+
+/**
+ * Set the machine account password
+ * @param ads connection to ads server
+ * @param hostname machine whose password is being set
+ * @param password new password
+ * @return status of password change
+ **/
+ADS_STATUS ads_set_machine_password(ADS_STRUCT *ads,
+ const char *machine_account,
+ const char *password)
+{
+ ADS_STATUS status;
+ char *principal = NULL;
+
+ /*
+ we need to use the '$' form of the name here (the machine account name),
+ as otherwise the server might end up setting the password for a user
+ instead
+ */
+ asprintf(&principal, "%s@%s", machine_account, ads->config.realm);
+
+ status = ads_krb5_set_password(ads->auth.kdc_server, principal,
+ password, ads->auth.time_offset);
+
+ free(principal);
+
+ return status;
+}
+
+
+
+#endif
diff --git a/source/libads/ldap.c b/source/libads/ldap.c
new file mode 100644
index 00000000000..20a36dfdf5c
--- /dev/null
+++ b/source/libads/ldap.c
@@ -0,0 +1,2135 @@
+/*
+ Unix SMB/CIFS implementation.
+ ads (active directory) utility library
+ Copyright (C) Andrew Tridgell 2001
+ Copyright (C) Remus Koos 2001
+ Copyright (C) Jim McDonough <jmcd@us.ibm.com> 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 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"
+
+#ifdef HAVE_LDAP
+
+/**
+ * @file ldap.c
+ * @brief basic ldap client-side routines for ads server communications
+ *
+ * The routines contained here should do the necessary ldap calls for
+ * ads setups.
+ *
+ * Important note: attribute names passed into ads_ routines must
+ * already be in UTF-8 format. We do not convert them because in almost
+ * all cases, they are just ascii (which is represented with the same
+ * codepoints in UTF-8). This may have to change at some point
+ **/
+
+
+/*
+ try a connection to a given ldap server, returning True and setting the servers IP
+ in the ads struct if successful
+
+ TODO : add a negative connection cache in here leveraged off of the one
+ found in the rpc code. --jerry
+ */
+static BOOL ads_try_connect(ADS_STRUCT *ads, const char *server, unsigned port)
+{
+ char *srv;
+
+ if (!server || !*server) {
+ return False;
+ }
+
+ DEBUG(5,("ads_try_connect: trying ldap server '%s' port %u\n", server, port));
+
+ /* this copes with inet_ntoa brokenness */
+ srv = strdup(server);
+
+ ads->ld = ldap_open(srv, port);
+ if (!ads->ld) {
+ free(srv);
+ return False;
+ }
+ ads->ldap_port = port;
+ ads->ldap_ip = *interpret_addr2(srv);
+ free(srv);
+
+ return True;
+}
+
+/*
+ try a connection to a given ldap server, based on URL, returning True if successful
+ */
+static BOOL ads_try_connect_uri(ADS_STRUCT *ads)
+{
+#if defined(LDAP_API_FEATURE_X_OPENLDAP) && (LDAP_API_VERSION > 2000)
+ DEBUG(5,("ads_try_connect: trying ldap server at URI '%s'\n",
+ ads->server.ldap_uri));
+
+
+ if (ldap_initialize((LDAP**)&(ads->ld), ads->server.ldap_uri) == LDAP_SUCCESS) {
+ return True;
+ }
+ DEBUG(0, ("ldap_initialize: %s\n", strerror(errno)));
+
+#else
+
+ DEBUG(1, ("no URL support in LDAP libs!\n"));
+#endif
+
+ return False;
+}
+
+/**********************************************************************
+ Try to find an AD dc using our internal name resolution routines
+ Try the realm first and then then workgroup name if netbios is not
+ disabled
+**********************************************************************/
+
+static BOOL ads_find_dc(ADS_STRUCT *ads)
+{
+ const char *c_realm;
+ int count, i=0;
+ struct ip_service *ip_list;
+ pstring realm;
+ BOOL got_realm = False;
+ BOOL use_own_domain = False;
+
+ /* if the realm and workgroup are both empty, assume they are ours */
+
+ /* realm */
+ c_realm = ads->server.realm;
+
+ if ( !c_realm || !*c_realm ) {
+ /* special case where no realm and no workgroup means our own */
+ if ( !ads->server.workgroup || !*ads->server.workgroup ) {
+ use_own_domain = True;
+ c_realm = lp_realm();
+ }
+ }
+
+ if (c_realm && *c_realm)
+ got_realm = True;
+
+again:
+ /* we need to try once with the realm name and fallback to the
+ netbios domain name if we fail (if netbios has not been disabled */
+
+ if ( !got_realm && !lp_disable_netbios() ) {
+ c_realm = ads->server.workgroup;
+ if (!c_realm || !*c_realm) {
+ if ( use_own_domain )
+ c_realm = lp_workgroup();
+ }
+
+ if ( !c_realm || !*c_realm ) {
+ DEBUG(0,("ads_find_dc: no realm or workgroup! Don't know what to do\n"));
+ return False;
+ }
+ }
+
+ pstrcpy( realm, c_realm );
+
+ DEBUG(6,("ads_find_dc: looking for %s '%s'\n",
+ (got_realm ? "realm" : "domain"), realm));
+
+ if ( !get_sorted_dc_list(realm, &ip_list, &count, got_realm) ) {
+ /* fall back to netbios if we can */
+ if ( got_realm && !lp_disable_netbios() ) {
+ got_realm = False;
+ goto again;
+ }
+
+ return False;
+ }
+
+ /* if we fail this loop, then giveup since all the IP addresses returned were dead */
+ for ( i=0; i<count; i++ ) {
+ /* since this is an ads conection request, default to LDAP_PORT is not set */
+ int port = (ip_list[i].port!=PORT_NONE) ? ip_list[i].port : LDAP_PORT;
+ fstring server;
+
+ fstrcpy( server, inet_ntoa(ip_list[i].ip) );
+
+ if ( !NT_STATUS_IS_OK(check_negative_conn_cache(realm, server)) )
+ continue;
+
+ if ( ads_try_connect(ads, server, port) ) {
+ SAFE_FREE(ip_list);
+ return True;
+ }
+
+ /* keep track of failures */
+ add_failed_connection_entry( realm, server, NT_STATUS_UNSUCCESSFUL );
+ }
+
+ SAFE_FREE(ip_list);
+
+ return False;
+}
+
+
+/**
+ * Connect to the LDAP server
+ * @param ads Pointer to an existing ADS_STRUCT
+ * @return status of connection
+ **/
+ADS_STATUS ads_connect(ADS_STRUCT *ads)
+{
+ int version = LDAP_VERSION3;
+ ADS_STATUS status;
+
+ ads->last_attempt = time(NULL);
+ ads->ld = NULL;
+
+ /* try with a URL based server */
+
+ if (ads->server.ldap_uri &&
+ ads_try_connect_uri(ads)) {
+ goto got_connection;
+ }
+
+ /* try with a user specified server */
+ if (ads->server.ldap_server &&
+ ads_try_connect(ads, ads->server.ldap_server, LDAP_PORT)) {
+ goto got_connection;
+ }
+
+ if (ads_find_dc(ads)) {
+ goto got_connection;
+ }
+
+ return ADS_ERROR_SYSTEM(errno?errno:ENOENT);
+
+got_connection:
+ DEBUG(3,("Connected to LDAP server %s\n", inet_ntoa(ads->ldap_ip)));
+
+ status = ads_server_info(ads);
+ if (!ADS_ERR_OK(status)) {
+ DEBUG(1,("Failed to get ldap server info\n"));
+ return status;
+ }
+
+ ldap_set_option(ads->ld, LDAP_OPT_PROTOCOL_VERSION, &version);
+
+ if (!ads->auth.user_name) {
+ /* by default use the machine account */
+ fstring myname;
+ fstrcpy(myname, global_myname());
+ strlower_m(myname);
+ asprintf(&ads->auth.user_name, "HOST/%s", myname);
+ }
+
+ if (!ads->auth.realm) {
+ ads->auth.realm = strdup(ads->config.realm);
+ }
+
+ if (!ads->auth.kdc_server) {
+ ads->auth.kdc_server = strdup(inet_ntoa(ads->ldap_ip));
+ }
+
+#if KRB5_DNS_HACK
+ /* this is a really nasty hack to avoid ADS DNS problems. It needs a patch
+ to MIT kerberos to work (tridge) */
+ {
+ char *env;
+ asprintf(&env, "KRB5_KDC_ADDRESS_%s", ads->config.realm);
+ setenv(env, ads->auth.kdc_server, 1);
+ free(env);
+ }
+#endif
+
+ if (ads->auth.flags & ADS_AUTH_NO_BIND) {
+ return ADS_SUCCESS;
+ }
+
+ if (ads->auth.flags & ADS_AUTH_ANON_BIND) {
+ return ADS_ERROR(ldap_simple_bind_s( ads->ld, NULL, NULL));
+ }
+
+ if (ads->auth.flags & ADS_AUTH_SIMPLE_BIND) {
+ return ADS_ERROR(ldap_simple_bind_s( ads->ld, ads->auth.user_name, ads->auth.password));
+ }
+
+ return ads_sasl_bind(ads);
+}
+
+/*
+ Duplicate a struct berval into talloc'ed memory
+ */
+static struct berval *dup_berval(TALLOC_CTX *ctx, const struct berval *in_val)
+{
+ struct berval *value;
+
+ if (!in_val) return NULL;
+
+ value = talloc_zero(ctx, sizeof(struct berval));
+ if (value == NULL)
+ return NULL;
+ if (in_val->bv_len == 0) return value;
+
+ value->bv_len = in_val->bv_len;
+ value->bv_val = talloc_memdup(ctx, in_val->bv_val, in_val->bv_len);
+ return value;
+}
+
+/*
+ Make a values list out of an array of (struct berval *)
+ */
+static struct berval **ads_dup_values(TALLOC_CTX *ctx,
+ const struct berval **in_vals)
+{
+ struct berval **values;
+ int i;
+
+ if (!in_vals) return NULL;
+ for (i=0; in_vals[i]; i++); /* count values */
+ values = (struct berval **) talloc_zero(ctx,
+ (i+1)*sizeof(struct berval *));
+ if (!values) return NULL;
+
+ for (i=0; in_vals[i]; i++) {
+ values[i] = dup_berval(ctx, in_vals[i]);
+ }
+ return values;
+}
+
+/*
+ UTF8-encode a values list out of an array of (char *)
+ */
+static char **ads_push_strvals(TALLOC_CTX *ctx, const char **in_vals)
+{
+ char **values;
+ int i;
+
+ if (!in_vals) return NULL;
+ for (i=0; in_vals[i]; i++); /* count values */
+ values = (char ** ) talloc_zero(ctx, (i+1)*sizeof(char *));
+ if (!values) return NULL;
+
+ for (i=0; in_vals[i]; i++) {
+ push_utf8_talloc(ctx, &values[i], in_vals[i]);
+ }
+ return values;
+}
+
+/*
+ Pull a (char *) array out of a UTF8-encoded values list
+ */
+static char **ads_pull_strvals(TALLOC_CTX *ctx, const char **in_vals)
+{
+ char **values;
+ int i;
+
+ if (!in_vals) return NULL;
+ for (i=0; in_vals[i]; i++); /* count values */
+ values = (char **) talloc_zero(ctx, (i+1)*sizeof(char *));
+ if (!values) return NULL;
+
+ for (i=0; in_vals[i]; i++) {
+ pull_utf8_talloc(ctx, &values[i], in_vals[i]);
+ }
+ return values;
+}
+
+/**
+ * Do a search with paged results. cookie must be null on the first
+ * call, and then returned on each subsequent call. It will be null
+ * again when the entire search is complete
+ * @param ads connection to ads server
+ * @param bind_path Base dn for the search
+ * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
+ * @param expr Search expression - specified in local charset
+ * @param attrs Attributes to retrieve - specified in utf8 or ascii
+ * @param res ** which will contain results - free res* with ads_msgfree()
+ * @param count Number of entries retrieved on this page
+ * @param cookie The paged results cookie to be returned on subsequent calls
+ * @return status of search
+ **/
+ADS_STATUS ads_do_paged_search(ADS_STRUCT *ads, const char *bind_path,
+ int scope, const char *expr,
+ const char **attrs, void **res,
+ int *count, void **cookie)
+{
+ int rc, i, version;
+ char *utf8_expr, *utf8_path, **search_attrs;
+ LDAPControl PagedResults, NoReferrals, *controls[3], **rcontrols;
+ BerElement *cookie_be = NULL;
+ struct berval *cookie_bv= NULL;
+ TALLOC_CTX *ctx;
+
+ *res = NULL;
+
+ if (!(ctx = talloc_init("ads_do_paged_search")))
+ return ADS_ERROR(LDAP_NO_MEMORY);
+
+ /* 0 means the conversion worked but the result was empty
+ so we only fail if it's -1. In any case, it always
+ at least nulls out the dest */
+ if ((push_utf8_talloc(ctx, &utf8_expr, expr) == (size_t)-1) ||
+ (push_utf8_talloc(ctx, &utf8_path, bind_path) == (size_t)-1)) {
+ rc = LDAP_NO_MEMORY;
+ goto done;
+ }
+
+ if (!attrs || !(*attrs))
+ search_attrs = NULL;
+ else {
+ /* This would be the utf8-encoded version...*/
+ /* if (!(search_attrs = ads_push_strvals(ctx, attrs))) */
+ if (!(str_list_copy(&search_attrs, attrs))) {
+ rc = LDAP_NO_MEMORY;
+ goto done;
+ }
+ }
+
+
+ /* Paged results only available on ldap v3 or later */
+ ldap_get_option(ads->ld, LDAP_OPT_PROTOCOL_VERSION, &version);
+ if (version < LDAP_VERSION3) {
+ rc = LDAP_NOT_SUPPORTED;
+ goto done;
+ }
+
+ cookie_be = ber_alloc_t(LBER_USE_DER);
+ if (cookie && *cookie) {
+ ber_printf(cookie_be, "{iO}", (ber_int_t) 1000, *cookie);
+ ber_bvfree(*cookie); /* don't need it from last time */
+ *cookie = NULL;
+ } else {
+ ber_printf(cookie_be, "{io}", (ber_int_t) 1000, "", 0);
+ }
+ ber_flatten(cookie_be, &cookie_bv);
+ PagedResults.ldctl_oid = ADS_PAGE_CTL_OID;
+ PagedResults.ldctl_iscritical = (char) 1;
+ PagedResults.ldctl_value.bv_len = cookie_bv->bv_len;
+ PagedResults.ldctl_value.bv_val = cookie_bv->bv_val;
+
+ NoReferrals.ldctl_oid = ADS_NO_REFERRALS_OID;
+ NoReferrals.ldctl_iscritical = (char) 0;
+ NoReferrals.ldctl_value.bv_len = 0;
+ NoReferrals.ldctl_value.bv_val = "";
+
+
+ controls[0] = &NoReferrals;
+ controls[1] = &PagedResults;
+ controls[2] = NULL;
+
+ *res = NULL;
+
+ /* we need to disable referrals as the openldap libs don't
+ handle them and paged results at the same time. Using them
+ together results in the result record containing the server
+ page control being removed from the result list (tridge/jmcd)
+
+ leaving this in despite the control that says don't generate
+ referrals, in case the server doesn't support it (jmcd)
+ */
+ ldap_set_option(ads->ld, LDAP_OPT_REFERRALS, LDAP_OPT_OFF);
+
+ rc = ldap_search_ext_s(ads->ld, utf8_path, scope, utf8_expr,
+ search_attrs, 0, controls,
+ NULL, NULL, LDAP_NO_LIMIT, (LDAPMessage **)res);
+
+ ber_free(cookie_be, 1);
+ ber_bvfree(cookie_bv);
+
+ if (rc) {
+ DEBUG(3,("ldap_search_ext_s(%s) -> %s\n", expr, ldap_err2string(rc)));
+ goto done;
+ }
+
+ rc = ldap_parse_result(ads->ld, *res, NULL, NULL, NULL,
+ NULL, &rcontrols, 0);
+
+ if (!rcontrols) {
+ goto done;
+ }
+
+ for (i=0; rcontrols[i]; i++) {
+ if (strcmp(ADS_PAGE_CTL_OID, rcontrols[i]->ldctl_oid) == 0) {
+ cookie_be = ber_init(&rcontrols[i]->ldctl_value);
+ ber_scanf(cookie_be,"{iO}", (ber_int_t *) count,
+ &cookie_bv);
+ /* the berval is the cookie, but must be freed when
+ it is all done */
+ if (cookie_bv->bv_len) /* still more to do */
+ *cookie=ber_bvdup(cookie_bv);
+ else
+ *cookie=NULL;
+ ber_bvfree(cookie_bv);
+ ber_free(cookie_be, 1);
+ break;
+ }
+ }
+ ldap_controls_free(rcontrols);
+
+done:
+ talloc_destroy(ctx);
+ /* if/when we decide to utf8-encode attrs, take out this next line */
+ str_list_free(&search_attrs);
+
+ return ADS_ERROR(rc);
+}
+
+
+/**
+ * Get all results for a search. This uses ads_do_paged_search() to return
+ * all entries in a large search.
+ * @param ads connection to ads server
+ * @param bind_path Base dn for the search
+ * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
+ * @param expr Search expression
+ * @param attrs Attributes to retrieve
+ * @param res ** which will contain results - free res* with ads_msgfree()
+ * @return status of search
+ **/
+ADS_STATUS ads_do_search_all(ADS_STRUCT *ads, const char *bind_path,
+ int scope, const char *expr,
+ const char **attrs, void **res)
+{
+ void *cookie = NULL;
+ int count = 0;
+ ADS_STATUS status;
+
+ status = ads_do_paged_search(ads, bind_path, scope, expr, attrs, res,
+ &count, &cookie);
+
+ if (!ADS_ERR_OK(status)) return status;
+
+ while (cookie) {
+ void *res2 = NULL;
+ ADS_STATUS status2;
+ LDAPMessage *msg, *next;
+
+ status2 = ads_do_paged_search(ads, bind_path, scope, expr,
+ attrs, &res2, &count, &cookie);
+
+ if (!ADS_ERR_OK(status2)) break;
+
+ /* this relies on the way that ldap_add_result_entry() works internally. I hope
+ that this works on all ldap libs, but I have only tested with openldap */
+ for (msg = ads_first_entry(ads, res2); msg; msg = next) {
+ next = ads_next_entry(ads, msg);
+ ldap_add_result_entry((LDAPMessage **)res, msg);
+ }
+ /* note that we do not free res2, as the memory is now
+ part of the main returned list */
+ }
+
+ return status;
+}
+
+/**
+ * Run a function on all results for a search. Uses ads_do_paged_search() and
+ * runs the function as each page is returned, using ads_process_results()
+ * @param ads connection to ads server
+ * @param bind_path Base dn for the search
+ * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
+ * @param expr Search expression - specified in local charset
+ * @param attrs Attributes to retrieve - specified in UTF-8 or ascii
+ * @param fn Function which takes attr name, values list, and data_area
+ * @param data_area Pointer which is passed to function on each call
+ * @return status of search
+ **/
+ADS_STATUS ads_do_search_all_fn(ADS_STRUCT *ads, const char *bind_path,
+ int scope, const char *expr, const char **attrs,
+ BOOL(*fn)(char *, void **, void *),
+ void *data_area)
+{
+ void *cookie = NULL;
+ int count = 0;
+ ADS_STATUS status;
+ void *res;
+
+ status = ads_do_paged_search(ads, bind_path, scope, expr, attrs, &res,
+ &count, &cookie);
+
+ if (!ADS_ERR_OK(status)) return status;
+
+ ads_process_results(ads, res, fn, data_area);
+ ads_msgfree(ads, res);
+
+ while (cookie) {
+ status = ads_do_paged_search(ads, bind_path, scope, expr, attrs,
+ &res, &count, &cookie);
+
+ if (!ADS_ERR_OK(status)) break;
+
+ ads_process_results(ads, res, fn, data_area);
+ ads_msgfree(ads, res);
+ }
+
+ return status;
+}
+
+/**
+ * Do a search with a timeout.
+ * @param ads connection to ads server
+ * @param bind_path Base dn for the search
+ * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
+ * @param expr Search expression
+ * @param attrs Attributes to retrieve
+ * @param res ** which will contain results - free res* with ads_msgfree()
+ * @return status of search
+ **/
+ADS_STATUS ads_do_search(ADS_STRUCT *ads, const char *bind_path, int scope,
+ const char *expr,
+ const char **attrs, void **res)
+{
+ struct timeval timeout;
+ int rc;
+ char *utf8_expr, *utf8_path, **search_attrs = NULL;
+ TALLOC_CTX *ctx;
+
+ if (!(ctx = talloc_init("ads_do_search"))) {
+ DEBUG(1,("ads_do_search: talloc_init() failed!"));
+ return ADS_ERROR(LDAP_NO_MEMORY);
+ }
+
+ /* 0 means the conversion worked but the result was empty
+ so we only fail if it's negative. In any case, it always
+ at least nulls out the dest */
+ if ((push_utf8_talloc(ctx, &utf8_expr, expr) == (size_t)-1) ||
+ (push_utf8_talloc(ctx, &utf8_path, bind_path) == (size_t)-1)) {
+ DEBUG(1,("ads_do_search: push_utf8_talloc() failed!"));
+ rc = LDAP_NO_MEMORY;
+ goto done;
+ }
+
+ if (!attrs || !(*attrs))
+ search_attrs = NULL;
+ else {
+ /* This would be the utf8-encoded version...*/
+ /* if (!(search_attrs = ads_push_strvals(ctx, attrs))) */
+ if (!(str_list_copy(&search_attrs, attrs)))
+ {
+ DEBUG(1,("ads_do_search: str_list_copy() failed!"));
+ rc = LDAP_NO_MEMORY;
+ goto done;
+ }
+ }
+
+ timeout.tv_sec = ADS_SEARCH_TIMEOUT;
+ timeout.tv_usec = 0;
+ *res = NULL;
+
+ /* see the note in ads_do_paged_search - we *must* disable referrals */
+ ldap_set_option(ads->ld, LDAP_OPT_REFERRALS, LDAP_OPT_OFF);
+
+ rc = ldap_search_ext_s(ads->ld, utf8_path, scope, utf8_expr,
+ search_attrs, 0, NULL, NULL,
+ &timeout, LDAP_NO_LIMIT, (LDAPMessage **)res);
+
+ if (rc == LDAP_SIZELIMIT_EXCEEDED) {
+ DEBUG(3,("Warning! sizelimit exceeded in ldap. Truncating.\n"));
+ rc = 0;
+ }
+
+ done:
+ talloc_destroy(ctx);
+ /* if/when we decide to utf8-encode attrs, take out this next line */
+ str_list_free(&search_attrs);
+ return ADS_ERROR(rc);
+}
+/**
+ * Do a general ADS search
+ * @param ads connection to ads server
+ * @param res ** which will contain results - free res* with ads_msgfree()
+ * @param expr Search expression
+ * @param attrs Attributes to retrieve
+ * @return status of search
+ **/
+ADS_STATUS ads_search(ADS_STRUCT *ads, void **res,
+ const char *expr,
+ const char **attrs)
+{
+ return ads_do_search(ads, ads->config.bind_path, LDAP_SCOPE_SUBTREE,
+ expr, attrs, res);
+}
+
+/**
+ * Do a search on a specific DistinguishedName
+ * @param ads connection to ads server
+ * @param res ** which will contain results - free res* with ads_msgfree()
+ * @param dn DistinguishName to search
+ * @param attrs Attributes to retrieve
+ * @return status of search
+ **/
+ADS_STATUS ads_search_dn(ADS_STRUCT *ads, void **res,
+ const char *dn,
+ const char **attrs)
+{
+ return ads_do_search(ads, dn, LDAP_SCOPE_BASE, "(objectclass=*)", attrs, res);
+}
+
+/**
+ * Free up memory from a ads_search
+ * @param ads connection to ads server
+ * @param msg Search results to free
+ **/
+void ads_msgfree(ADS_STRUCT *ads, void *msg)
+{
+ if (!msg) return;
+ ldap_msgfree(msg);
+}
+
+/**
+ * Free up memory from various ads requests
+ * @param ads connection to ads server
+ * @param mem Area to free
+ **/
+void ads_memfree(ADS_STRUCT *ads, void *mem)
+{
+ SAFE_FREE(mem);
+}
+
+/**
+ * Get a dn from search results
+ * @param ads connection to ads server
+ * @param msg Search result
+ * @return dn string
+ **/
+char *ads_get_dn(ADS_STRUCT *ads, void *msg)
+{
+ char *utf8_dn, *unix_dn;
+
+ utf8_dn = ldap_get_dn(ads->ld, msg);
+
+ if (!utf8_dn) {
+ DEBUG (5, ("ads_get_dn: ldap_get_dn failed\n"));
+ return NULL;
+ }
+
+ if (pull_utf8_allocate(&unix_dn, utf8_dn) == (size_t)-1) {
+ DEBUG(0,("ads_get_dn: string conversion failure utf8 [%s]\n",
+ utf8_dn ));
+ return NULL;
+ }
+ ldap_memfree(utf8_dn);
+ return unix_dn;
+}
+
+/**
+ * Find a machine account given a hostname
+ * @param ads connection to ads server
+ * @param res ** which will contain results - free res* with ads_msgfree()
+ * @param host Hostname to search for
+ * @return status of search
+ **/
+ADS_STATUS ads_find_machine_acct(ADS_STRUCT *ads, void **res, const char *host)
+{
+ ADS_STATUS status;
+ char *expr;
+ const char *attrs[] = {"*", "nTSecurityDescriptor", NULL};
+
+ /* the easiest way to find a machine account anywhere in the tree
+ is to look for hostname$ */
+ if (asprintf(&expr, "(samAccountName=%s$)", host) == -1) {
+ DEBUG(1, ("asprintf failed!\n"));
+ return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
+ }
+
+ status = ads_search(ads, res, expr, attrs);
+ free(expr);
+ return status;
+}
+
+/**
+ * Initialize a list of mods to be used in a modify request
+ * @param ctx An initialized TALLOC_CTX
+ * @return allocated ADS_MODLIST
+ **/
+ADS_MODLIST ads_init_mods(TALLOC_CTX *ctx)
+{
+#define ADS_MODLIST_ALLOC_SIZE 10
+ LDAPMod **mods;
+
+ if ((mods = (LDAPMod **) talloc_zero(ctx, sizeof(LDAPMod *) *
+ (ADS_MODLIST_ALLOC_SIZE + 1))))
+ /* -1 is safety to make sure we don't go over the end.
+ need to reset it to NULL before doing ldap modify */
+ mods[ADS_MODLIST_ALLOC_SIZE] = (LDAPMod *) -1;
+
+ return mods;
+}
+
+
+/*
+ add an attribute to the list, with values list already constructed
+*/
+static ADS_STATUS ads_modlist_add(TALLOC_CTX *ctx, ADS_MODLIST *mods,
+ int mod_op, const char *name,
+ const void **invals)
+{
+ int curmod;
+ LDAPMod **modlist = (LDAPMod **) *mods;
+ struct berval **ber_values = NULL;
+ char **char_values = NULL;
+
+ if (!invals) {
+ mod_op = LDAP_MOD_DELETE;
+ } else {
+ if (mod_op & LDAP_MOD_BVALUES)
+ ber_values = ads_dup_values(ctx,
+ (const struct berval **)invals);
+ else
+ char_values = ads_push_strvals(ctx,
+ (const char **) invals);
+ }
+
+ /* find the first empty slot */
+ for (curmod=0; modlist[curmod] && modlist[curmod] != (LDAPMod *) -1;
+ curmod++);
+ if (modlist[curmod] == (LDAPMod *) -1) {
+ if (!(modlist = talloc_realloc(ctx, modlist,
+ (curmod+ADS_MODLIST_ALLOC_SIZE+1)*sizeof(LDAPMod *))))
+ return ADS_ERROR(LDAP_NO_MEMORY);
+ memset(&modlist[curmod], 0,
+ ADS_MODLIST_ALLOC_SIZE*sizeof(LDAPMod *));
+ modlist[curmod+ADS_MODLIST_ALLOC_SIZE] = (LDAPMod *) -1;
+ *mods = modlist;
+ }
+
+ if (!(modlist[curmod] = talloc_zero(ctx, sizeof(LDAPMod))))
+ return ADS_ERROR(LDAP_NO_MEMORY);
+ modlist[curmod]->mod_type = talloc_strdup(ctx, name);
+ if (mod_op & LDAP_MOD_BVALUES) {
+ modlist[curmod]->mod_bvalues = ber_values;
+ } else if (mod_op & LDAP_MOD_DELETE) {
+ modlist[curmod]->mod_values = NULL;
+ } else {
+ modlist[curmod]->mod_values = char_values;
+ }
+
+ modlist[curmod]->mod_op = mod_op;
+ return ADS_ERROR(LDAP_SUCCESS);
+}
+
+/**
+ * Add a single string value to a mod list
+ * @param ctx An initialized TALLOC_CTX
+ * @param mods An initialized ADS_MODLIST
+ * @param name The attribute name to add
+ * @param val The value to add - NULL means DELETE
+ * @return ADS STATUS indicating success of add
+ **/
+ADS_STATUS ads_mod_str(TALLOC_CTX *ctx, ADS_MODLIST *mods,
+ const char *name, const char *val)
+{
+ const char *values[2];
+
+ values[0] = val;
+ values[1] = NULL;
+
+ if (!val)
+ return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
+ return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE, name,
+ (const void **) values);
+}
+
+/**
+ * Add an array of string values to a mod list
+ * @param ctx An initialized TALLOC_CTX
+ * @param mods An initialized ADS_MODLIST
+ * @param name The attribute name to add
+ * @param vals The array of string values to add - NULL means DELETE
+ * @return ADS STATUS indicating success of add
+ **/
+ADS_STATUS ads_mod_strlist(TALLOC_CTX *ctx, ADS_MODLIST *mods,
+ const char *name, const char **vals)
+{
+ if (!vals)
+ return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
+ return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE,
+ name, (const void **) vals);
+}
+
+/**
+ * Add a single ber-encoded value to a mod list
+ * @param ctx An initialized TALLOC_CTX
+ * @param mods An initialized ADS_MODLIST
+ * @param name The attribute name to add
+ * @param val The value to add - NULL means DELETE
+ * @return ADS STATUS indicating success of add
+ **/
+static ADS_STATUS ads_mod_ber(TALLOC_CTX *ctx, ADS_MODLIST *mods,
+ const char *name, const struct berval *val)
+{
+ const struct berval *values[2];
+
+ values[0] = val;
+ values[1] = NULL;
+ if (!val)
+ return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
+ return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE|LDAP_MOD_BVALUES,
+ name, (const void **) values);
+}
+
+/**
+ * Perform an ldap modify
+ * @param ads connection to ads server
+ * @param mod_dn DistinguishedName to modify
+ * @param mods list of modifications to perform
+ * @return status of modify
+ **/
+ADS_STATUS ads_gen_mod(ADS_STRUCT *ads, const char *mod_dn, ADS_MODLIST mods)
+{
+ int ret,i;
+ char *utf8_dn = NULL;
+ /*
+ this control is needed to modify that contains a currently
+ non-existent attribute (but allowable for the object) to run
+ */
+ LDAPControl PermitModify = {
+ ADS_PERMIT_MODIFY_OID,
+ {0, NULL},
+ (char) 1};
+ LDAPControl *controls[2];
+
+ controls[0] = &PermitModify;
+ controls[1] = NULL;
+
+ if (push_utf8_allocate(&utf8_dn, mod_dn) == -1) {
+ return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
+ }
+
+ /* find the end of the list, marked by NULL or -1 */
+ for(i=0;(mods[i]!=0)&&(mods[i]!=(LDAPMod *) -1);i++);
+ /* make sure the end of the list is NULL */
+ mods[i] = NULL;
+ ret = ldap_modify_ext_s(ads->ld, utf8_dn,
+ (LDAPMod **) mods, controls, NULL);
+ SAFE_FREE(utf8_dn);
+ return ADS_ERROR(ret);
+}
+
+/**
+ * Perform an ldap add
+ * @param ads connection to ads server
+ * @param new_dn DistinguishedName to add
+ * @param mods list of attributes and values for DN
+ * @return status of add
+ **/
+ADS_STATUS ads_gen_add(ADS_STRUCT *ads, const char *new_dn, ADS_MODLIST mods)
+{
+ int ret, i;
+ char *utf8_dn = NULL;
+
+ if (push_utf8_allocate(&utf8_dn, new_dn) == -1) {
+ DEBUG(1, ("ads_gen_add: push_utf8_allocate failed!"));
+ return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
+ }
+
+ /* find the end of the list, marked by NULL or -1 */
+ for(i=0;(mods[i]!=0)&&(mods[i]!=(LDAPMod *) -1);i++);
+ /* make sure the end of the list is NULL */
+ mods[i] = NULL;
+
+ ret = ldap_add_s(ads->ld, utf8_dn, mods);
+ SAFE_FREE(utf8_dn);
+ return ADS_ERROR(ret);
+}
+
+/**
+ * Delete a DistinguishedName
+ * @param ads connection to ads server
+ * @param new_dn DistinguishedName to delete
+ * @return status of delete
+ **/
+ADS_STATUS ads_del_dn(ADS_STRUCT *ads, char *del_dn)
+{
+ int ret;
+ char *utf8_dn = NULL;
+ if (push_utf8_allocate(&utf8_dn, del_dn) == -1) {
+ DEBUG(1, ("ads_del_dn: push_utf8_allocate failed!"));
+ return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
+ }
+
+ ret = ldap_delete_s(ads->ld, utf8_dn);
+ return ADS_ERROR(ret);
+}
+
+/**
+ * Build an org unit string
+ * if org unit is Computers or blank then assume a container, otherwise
+ * assume a \ separated list of organisational units
+ * @param org_unit Organizational unit
+ * @return org unit string - caller must free
+ **/
+char *ads_ou_string(const char *org_unit)
+{
+ if (!org_unit || !*org_unit || strequal(org_unit, "Computers")) {
+ return strdup("cn=Computers");
+ }
+
+ return ads_build_path(org_unit, "\\/", "ou=", 1);
+}
+
+
+
+/*
+ add a machine account to the ADS server
+*/
+static ADS_STATUS ads_add_machine_acct(ADS_STRUCT *ads, const char *hostname,
+ uint32 account_type,
+ const char *org_unit)
+{
+ ADS_STATUS ret, status;
+ char *host_spn, *host_upn, *new_dn, *samAccountName, *controlstr;
+ char *ou_str;
+ TALLOC_CTX *ctx;
+ ADS_MODLIST mods;
+ const char *objectClass[] = {"top", "person", "organizationalPerson",
+ "user", "computer", NULL};
+ const char *servicePrincipalName[5] = {NULL, NULL, NULL, NULL, NULL};
+ char *psp, *psp2;
+ unsigned acct_control;
+ unsigned exists=0;
+ LDAPMessage *res;
+
+ status = ads_find_machine_acct(ads, (void **)&res, hostname);
+ if (ADS_ERR_OK(status) && ads_count_replies(ads, res) == 1) {
+ DEBUG(0, ("Host account for %s already exists - modifying old account\n", hostname));
+ exists=1;
+ }
+
+ if (!(ctx = talloc_init("machine_account")))
+ return ADS_ERROR(LDAP_NO_MEMORY);
+
+ ret = ADS_ERROR(LDAP_NO_MEMORY);
+
+ if (!(host_spn = talloc_asprintf(ctx, "HOST/%s", hostname)))
+ goto done;
+ if (!(host_upn = talloc_asprintf(ctx, "%s@%s", host_spn, ads->config.realm)))
+ goto done;
+ ou_str = ads_ou_string(org_unit);
+ if (!ou_str) {
+ DEBUG(1, ("ads_ou_string returned NULL (malloc failure?)\n"));
+ goto done;
+ }
+ new_dn = talloc_asprintf(ctx, "cn=%s,%s,%s", hostname, ou_str,
+ ads->config.bind_path);
+ servicePrincipalName[0] = talloc_asprintf(ctx, "HOST/%s", hostname);
+ psp = talloc_asprintf(ctx, "HOST/%s.%s",
+ hostname,
+ ads->config.realm);
+ strlower_m(&psp[5]);
+ servicePrincipalName[1] = psp;
+ servicePrincipalName[2] = talloc_asprintf(ctx, "CIFS/%s", hostname);
+ psp2 = talloc_asprintf(ctx, "CIFS/%s.%s",
+ hostname,
+ ads->config.realm);
+ strlower_m(&psp2[5]);
+ servicePrincipalName[3] = psp2;
+
+ free(ou_str);
+ if (!new_dn)
+ goto done;
+
+ if (!(samAccountName = talloc_asprintf(ctx, "%s$", hostname)))
+ goto done;
+
+ acct_control = account_type | UF_DONT_EXPIRE_PASSWD;
+#ifndef ENCTYPE_ARCFOUR_HMAC
+ acct_control |= UF_USE_DES_KEY_ONLY;
+#endif
+
+ if (!(controlstr = talloc_asprintf(ctx, "%u", acct_control)))
+ goto done;
+
+ if (!(mods = ads_init_mods(ctx)))
+ goto done;
+
+ if (!exists) {
+ ads_mod_str(ctx, &mods, "cn", hostname);
+ ads_mod_str(ctx, &mods, "sAMAccountName", samAccountName);
+ ads_mod_str(ctx, &mods, "userAccountControl", controlstr);
+ ads_mod_strlist(ctx, &mods, "objectClass", objectClass);
+ }
+ ads_mod_str(ctx, &mods, "dNSHostName", hostname);
+ ads_mod_str(ctx, &mods, "userPrincipalName", host_upn);
+ ads_mod_strlist(ctx, &mods, "servicePrincipalName", servicePrincipalName);
+ ads_mod_str(ctx, &mods, "operatingSystem", "Samba");
+ ads_mod_str(ctx, &mods, "operatingSystemVersion", SAMBA_VERSION_STRING);
+
+ if (!exists)
+ ret = ads_gen_add(ads, new_dn, mods);
+ else
+ ret = ads_gen_mod(ads, new_dn, mods);
+
+ if (!ADS_ERR_OK(ret))
+ goto done;
+
+ /* Do not fail if we can't set security descriptor
+ * it shouldn't be mandatory and probably we just
+ * don't have enough rights to do it.
+ */
+ if (!exists) {
+ status = ads_set_machine_sd(ads, hostname, new_dn);
+
+ if (!ADS_ERR_OK(status)) {
+ DEBUG(0, ("Warning: ads_set_machine_sd: %s\n",
+ ads_errstr(status)));
+ }
+ }
+done:
+ talloc_destroy(ctx);
+ return ret;
+}
+
+/*
+ dump a binary result from ldap
+*/
+static void dump_binary(const char *field, struct berval **values)
+{
+ int i, j;
+ for (i=0; values[i]; i++) {
+ printf("%s: ", field);
+ for (j=0; j<values[i]->bv_len; j++) {
+ printf("%02X", (unsigned char)values[i]->bv_val[j]);
+ }
+ printf("\n");
+ }
+}
+
+static void dump_guid(const char *field, struct berval **values)
+{
+ int i;
+ UUID_FLAT guid;
+ for (i=0; values[i]; i++) {
+ memcpy(guid.info, values[i]->bv_val, sizeof(guid.info));
+ printf("%s: %s\n", field,
+ smb_uuid_string_static(smb_uuid_unpack_static(guid)));
+ }
+}
+
+/*
+ dump a sid result from ldap
+*/
+static void dump_sid(const char *field, struct berval **values)
+{
+ int i;
+ for (i=0; values[i]; i++) {
+ DOM_SID sid;
+ sid_parse(values[i]->bv_val, values[i]->bv_len, &sid);
+ printf("%s: %s\n", field, sid_string_static(&sid));
+ }
+}
+
+/*
+ dump ntSecurityDescriptor
+*/
+static void dump_sd(const char *filed, struct berval **values)
+{
+ prs_struct ps;
+
+ SEC_DESC *psd = 0;
+ TALLOC_CTX *ctx = 0;
+
+ if (!(ctx = talloc_init("sec_io_desc")))
+ return;
+
+ /* prepare data */
+ prs_init(&ps, values[0]->bv_len, ctx, UNMARSHALL);
+ prs_copy_data_in(&ps, values[0]->bv_val, values[0]->bv_len);
+ prs_set_offset(&ps,0);
+
+ /* parse secdesc */
+ if (!sec_io_desc("sd", &psd, &ps, 1)) {
+ prs_mem_free(&ps);
+ talloc_destroy(ctx);
+ return;
+ }
+ if (psd) ads_disp_sd(psd);
+
+ prs_mem_free(&ps);
+ talloc_destroy(ctx);
+}
+
+/*
+ dump a string result from ldap
+*/
+static void dump_string(const char *field, char **values)
+{
+ int i;
+ for (i=0; values[i]; i++) {
+ printf("%s: %s\n", field, values[i]);
+ }
+}
+
+/*
+ dump a field from LDAP on stdout
+ used for debugging
+*/
+
+static BOOL ads_dump_field(char *field, void **values, void *data_area)
+{
+ const struct {
+ const char *name;
+ BOOL string;
+ void (*handler)(const char *, struct berval **);
+ } handlers[] = {
+ {"objectGUID", False, dump_guid},
+ {"nTSecurityDescriptor", False, dump_sd},
+ {"dnsRecord", False, dump_binary},
+ {"objectSid", False, dump_sid},
+ {"tokenGroups", False, dump_sid},
+ {NULL, True, NULL}
+ };
+ int i;
+
+ if (!field) { /* must be end of an entry */
+ printf("\n");
+ return False;
+ }
+
+ for (i=0; handlers[i].name; i++) {
+ if (StrCaseCmp(handlers[i].name, field) == 0) {
+ if (!values) /* first time, indicate string or not */
+ return handlers[i].string;
+ handlers[i].handler(field, (struct berval **) values);
+ break;
+ }
+ }
+ if (!handlers[i].name) {
+ if (!values) /* first time, indicate string conversion */
+ return True;
+ dump_string(field, (char **)values);
+ }
+ return False;
+}
+
+/**
+ * Dump a result from LDAP on stdout
+ * used for debugging
+ * @param ads connection to ads server
+ * @param res Results to dump
+ **/
+
+void ads_dump(ADS_STRUCT *ads, void *res)
+{
+ ads_process_results(ads, res, ads_dump_field, NULL);
+}
+
+/**
+ * Walk through results, calling a function for each entry found.
+ * The function receives a field name, a berval * array of values,
+ * and a data area passed through from the start. The function is
+ * called once with null for field and values at the end of each
+ * entry.
+ * @param ads connection to ads server
+ * @param res Results to process
+ * @param fn Function for processing each result
+ * @param data_area user-defined area to pass to function
+ **/
+void ads_process_results(ADS_STRUCT *ads, void *res,
+ BOOL(*fn)(char *, void **, void *),
+ void *data_area)
+{
+ void *msg;
+ TALLOC_CTX *ctx;
+
+ if (!(ctx = talloc_init("ads_process_results")))
+ return;
+
+ for (msg = ads_first_entry(ads, res); msg;
+ msg = ads_next_entry(ads, msg)) {
+ char *utf8_field;
+ BerElement *b;
+
+ for (utf8_field=ldap_first_attribute(ads->ld,
+ (LDAPMessage *)msg,&b);
+ utf8_field;
+ utf8_field=ldap_next_attribute(ads->ld,
+ (LDAPMessage *)msg,b)) {
+ struct berval **ber_vals;
+ char **str_vals, **utf8_vals;
+ char *field;
+ BOOL string;
+
+ pull_utf8_talloc(ctx, &field, utf8_field);
+ string = fn(field, NULL, data_area);
+
+ if (string) {
+ utf8_vals = ldap_get_values(ads->ld,
+ (LDAPMessage *)msg, field);
+ str_vals = ads_pull_strvals(ctx,
+ (const char **) utf8_vals);
+ fn(field, (void **) str_vals, data_area);
+ ldap_value_free(utf8_vals);
+ } else {
+ ber_vals = ldap_get_values_len(ads->ld,
+ (LDAPMessage *)msg, field);
+ fn(field, (void **) ber_vals, data_area);
+
+ ldap_value_free_len(ber_vals);
+ }
+ ldap_memfree(utf8_field);
+ }
+ ber_free(b, 0);
+ talloc_destroy_pool(ctx);
+ fn(NULL, NULL, data_area); /* completed an entry */
+
+ }
+ talloc_destroy(ctx);
+}
+
+/**
+ * count how many replies are in a LDAPMessage
+ * @param ads connection to ads server
+ * @param res Results to count
+ * @return number of replies
+ **/
+int ads_count_replies(ADS_STRUCT *ads, void *res)
+{
+ return ldap_count_entries(ads->ld, (LDAPMessage *)res);
+}
+
+/**
+ * Join a machine to a realm
+ * Creates the machine account and sets the machine password
+ * @param ads connection to ads server
+ * @param hostname name of host to add
+ * @param org_unit Organizational unit to place machine in
+ * @return status of join
+ **/
+ADS_STATUS ads_join_realm(ADS_STRUCT *ads, const char *hostname,
+ uint32 account_type, const char *org_unit)
+{
+ ADS_STATUS status;
+ LDAPMessage *res;
+ char *host;
+
+ /* hostname must be lowercase */
+ host = strdup(hostname);
+ strlower_m(host);
+
+ /*
+ status = ads_find_machine_acct(ads, (void **)&res, host);
+ if (ADS_ERR_OK(status) && ads_count_replies(ads, res) == 1) {
+ DEBUG(0, ("Host account for %s already exists - deleting old account\n", host));
+ status = ads_leave_realm(ads, host);
+ if (!ADS_ERR_OK(status)) {
+ DEBUG(0, ("Failed to delete host '%s' from the '%s' realm.\n",
+ host, ads->config.realm));
+ return status;
+ }
+ }
+ */
+
+ status = ads_add_machine_acct(ads, host, account_type, org_unit);
+ if (!ADS_ERR_OK(status)) {
+ DEBUG(0, ("ads_add_machine_acct: %s\n", ads_errstr(status)));
+ return status;
+ }
+
+ status = ads_find_machine_acct(ads, (void **)&res, host);
+ if (!ADS_ERR_OK(status)) {
+ DEBUG(0, ("Host account test failed\n"));
+ return status;
+ }
+
+ free(host);
+
+ return status;
+}
+
+/**
+ * Delete a machine from the realm
+ * @param ads connection to ads server
+ * @param hostname Machine to remove
+ * @return status of delete
+ **/
+ADS_STATUS ads_leave_realm(ADS_STRUCT *ads, const char *hostname)
+{
+ ADS_STATUS status;
+ void *res, *msg;
+ char *hostnameDN, *host;
+ int rc;
+
+ /* hostname must be lowercase */
+ host = strdup(hostname);
+ strlower_m(host);
+
+ status = ads_find_machine_acct(ads, &res, host);
+ if (!ADS_ERR_OK(status)) {
+ DEBUG(0, ("Host account for %s does not exist.\n", host));
+ return status;
+ }
+
+ msg = ads_first_entry(ads, res);
+ if (!msg) {
+ return ADS_ERROR_SYSTEM(ENOENT);
+ }
+
+ hostnameDN = ads_get_dn(ads, (LDAPMessage *)msg);
+ rc = ldap_delete_s(ads->ld, hostnameDN);
+ ads_memfree(ads, hostnameDN);
+ if (rc != LDAP_SUCCESS) {
+ return ADS_ERROR(rc);
+ }
+
+ status = ads_find_machine_acct(ads, &res, host);
+ if (ADS_ERR_OK(status) && ads_count_replies(ads, res) == 1) {
+ DEBUG(0, ("Failed to remove host account.\n"));
+ return status;
+ }
+
+ free(host);
+
+ return status;
+}
+
+/**
+ * add machine account to existing security descriptor
+ * @param ads connection to ads server
+ * @param hostname machine to add
+ * @param dn DN of security descriptor
+ * @return status
+ **/
+ADS_STATUS ads_set_machine_sd(ADS_STRUCT *ads, const char *hostname, char *dn)
+{
+ const char *attrs[] = {"nTSecurityDescriptor", "objectSid", 0};
+ char *expr = 0;
+ size_t sd_size = 0;
+ struct berval bval = {0, NULL};
+ prs_struct ps_wire;
+ char *escaped_hostname = escape_ldap_string_alloc(hostname);
+
+ LDAPMessage *res = 0;
+ LDAPMessage *msg = 0;
+ ADS_MODLIST mods = 0;
+
+ NTSTATUS status;
+ ADS_STATUS ret;
+ DOM_SID sid;
+ SEC_DESC *psd = NULL;
+ TALLOC_CTX *ctx = NULL;
+
+ /* Avoid segmentation fault in prs_mem_free if
+ * we have to bail out before prs_init */
+ ps_wire.is_dynamic = False;
+
+ if (!ads) return ADS_ERROR(LDAP_SERVER_DOWN);
+
+ ret = ADS_ERROR(LDAP_SUCCESS);
+
+ if (!escaped_hostname) {
+ return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
+ }
+
+ if (asprintf(&expr, "(samAccountName=%s$)", escaped_hostname) == -1) {
+ DEBUG(1, ("ads_set_machine_sd: asprintf failed!\n"));
+ SAFE_FREE(escaped_hostname);
+ return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
+ }
+
+ SAFE_FREE(escaped_hostname);
+
+ ret = ads_search(ads, (void *) &res, expr, attrs);
+
+ if (!ADS_ERR_OK(ret)) return ret;
+
+ if ( !(msg = ads_first_entry(ads, res) )) {
+ ret = ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
+ goto ads_set_sd_error;
+ }
+
+ if (!ads_pull_sid(ads, msg, attrs[1], &sid)) {
+ ret = ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
+ goto ads_set_sd_error;
+ }
+
+ if (!(ctx = talloc_init("sec_io_desc"))) {
+ ret = ADS_ERROR(LDAP_NO_MEMORY);
+ goto ads_set_sd_error;
+ }
+
+ if (!ads_pull_sd(ads, ctx, msg, attrs[0], &psd)) {
+ ret = ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
+ goto ads_set_sd_error;
+ }
+
+ status = sec_desc_add_sid(ctx, &psd, &sid, SEC_RIGHTS_FULL_CTRL, &sd_size);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ ret = ADS_ERROR_NT(status);
+ goto ads_set_sd_error;
+ }
+
+ if (!prs_init(&ps_wire, sd_size, ctx, MARSHALL)) {
+ ret = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
+ }
+
+ if (!sec_io_desc("sd_wire", &psd, &ps_wire, 1)) {
+ ret = ADS_ERROR(LDAP_NO_MEMORY);
+ goto ads_set_sd_error;
+ }
+
+#if 0
+ file_save("/tmp/sec_desc.new", ps_wire.data_p, sd_size);
+#endif
+ if (!(mods = ads_init_mods(ctx))) return ADS_ERROR(LDAP_NO_MEMORY);
+
+ bval.bv_len = prs_offset(&ps_wire);
+ bval.bv_val = talloc(ctx, bval.bv_len);
+ if (!bval.bv_val) {
+ ret = ADS_ERROR(LDAP_NO_MEMORY);
+ goto ads_set_sd_error;
+ }
+
+ prs_set_offset(&ps_wire, 0);
+
+ if (!prs_copy_data_out(bval.bv_val, &ps_wire, bval.bv_len)) {
+ ret = ADS_ERROR(LDAP_NO_MEMORY);
+ goto ads_set_sd_error;
+ }
+
+ ret = ads_mod_ber(ctx, &mods, attrs[0], &bval);
+ if (ADS_ERR_OK(ret)) {
+ ret = ads_gen_mod(ads, dn, mods);
+ }
+
+ads_set_sd_error:
+ ads_msgfree(ads, res);
+ prs_mem_free(&ps_wire);
+ talloc_destroy(ctx);
+ return ret;
+}
+
+/**
+ * pull the first entry from a ADS result
+ * @param ads connection to ads server
+ * @param res Results of search
+ * @return first entry from result
+ **/
+void *ads_first_entry(ADS_STRUCT *ads, void *res)
+{
+ return (void *)ldap_first_entry(ads->ld, (LDAPMessage *)res);
+}
+
+/**
+ * pull the next entry from a ADS result
+ * @param ads connection to ads server
+ * @param res Results of search
+ * @return next entry from result
+ **/
+void *ads_next_entry(ADS_STRUCT *ads, void *res)
+{
+ return (void *)ldap_next_entry(ads->ld, (LDAPMessage *)res);
+}
+
+/**
+ * pull a single string from a ADS result
+ * @param ads connection to ads server
+ * @param mem_ctx TALLOC_CTX to use for allocating result string
+ * @param msg Results of search
+ * @param field Attribute to retrieve
+ * @return Result string in talloc context
+ **/
+char *ads_pull_string(ADS_STRUCT *ads,
+ TALLOC_CTX *mem_ctx, void *msg, const char *field)
+{
+ char **values;
+ char *ret = NULL;
+ char *ux_string;
+ size_t rc;
+
+ values = ldap_get_values(ads->ld, msg, field);
+ if (!values)
+ return NULL;
+
+ if (values[0]) {
+ rc = pull_utf8_talloc(mem_ctx, &ux_string,
+ values[0]);
+ if (rc != (size_t)-1)
+ ret = ux_string;
+
+ }
+ ldap_value_free(values);
+ return ret;
+}
+
+/**
+ * pull an array of strings from a ADS result
+ * @param ads connection to ads server
+ * @param mem_ctx TALLOC_CTX to use for allocating result string
+ * @param msg Results of search
+ * @param field Attribute to retrieve
+ * @return Result strings in talloc context
+ **/
+char **ads_pull_strings(ADS_STRUCT *ads,
+ TALLOC_CTX *mem_ctx, void *msg, const char *field,
+ size_t *num_values)
+{
+ char **values;
+ char **ret = NULL;
+ int i;
+
+ values = ldap_get_values(ads->ld, msg, field);
+ if (!values)
+ return NULL;
+
+ *num_values = ldap_count_values(values);
+
+ ret = talloc(mem_ctx, sizeof(char *) * (*num_values+1));
+ if (!ret) {
+ ldap_value_free(values);
+ return NULL;
+ }
+
+ for (i=0;i<*num_values;i++) {
+ if (pull_utf8_talloc(mem_ctx, &ret[i], values[i]) == -1) {
+ ldap_value_free(values);
+ return NULL;
+ }
+ }
+ ret[i] = NULL;
+
+ ldap_value_free(values);
+ return ret;
+}
+
+/**
+ * pull an array of strings from a ADS result
+ * (handle large multivalue attributes with range retrieval)
+ * @param ads connection to ads server
+ * @param mem_ctx TALLOC_CTX to use for allocating result string
+ * @param msg Results of search
+ * @param field Attribute to retrieve
+ * @param current_strings strings returned by a previous call to this function
+ * @param next_attribute The next query should ask for this attribute
+ * @param num_values How many values did we get this time?
+ * @param more_values Are there more values to get?
+ * @return Result strings in talloc context
+ **/
+char **ads_pull_strings_range(ADS_STRUCT *ads,
+ TALLOC_CTX *mem_ctx,
+ void *msg, const char *field,
+ char **current_strings,
+ const char **next_attribute,
+ size_t *num_strings,
+ BOOL *more_strings)
+{
+ char *attr;
+ char *expected_range_attrib, *range_attr;
+ BerElement *ptr = NULL;
+ char **strings;
+ char **new_strings;
+ size_t num_new_strings;
+ unsigned long int range_start;
+ unsigned long int range_end;
+
+ /* we might have been given the whole lot anyway */
+ if ((strings = ads_pull_strings(ads, mem_ctx, msg, field, num_strings))) {
+ *more_strings = False;
+ return strings;
+ }
+
+ expected_range_attrib = talloc_asprintf(mem_ctx, "%s;Range=", field);
+
+ /* look for Range result */
+ for (attr = ldap_first_attribute(ads->ld, (LDAPMessage *)msg, &ptr);
+ attr;
+ attr = ldap_next_attribute(ads->ld, (LDAPMessage *)msg, ptr)) {
+ /* we ignore the fact that this is utf8, as all attributes are ascii... */
+ if (strnequal(attr, expected_range_attrib, strlen(expected_range_attrib))) {
+ range_attr = attr;
+ break;
+ }
+ ldap_memfree(attr);
+ }
+ if (!attr) {
+ ber_free(ptr, 0);
+ /* nothing here - this field is just empty */
+ *more_strings = False;
+ return NULL;
+ }
+
+ if (sscanf(&range_attr[strlen(expected_range_attrib)], "%lu-%lu",
+ &range_start, &range_end) == 2) {
+ *more_strings = True;
+ } else {
+ if (sscanf(&range_attr[strlen(expected_range_attrib)], "%lu-*",
+ &range_start) == 1) {
+ *more_strings = False;
+ } else {
+ DEBUG(1, ("ads_pull_strings_range: Cannot parse Range attriubte (%s)\n",
+ range_attr));
+ ldap_memfree(range_attr);
+ *more_strings = False;
+ return NULL;
+ }
+ }
+
+ if ((*num_strings) != range_start) {
+ DEBUG(1, ("ads_pull_strings_range: Range attribute (%s) doesn't start at %u, but at %lu"
+ " - aborting range retreival\n",
+ range_attr, *num_strings + 1, range_start));
+ ldap_memfree(range_attr);
+ *more_strings = False;
+ return NULL;
+ }
+
+ new_strings = ads_pull_strings(ads, mem_ctx, msg, range_attr, &num_new_strings);
+
+ if (*more_strings && ((*num_strings + num_new_strings) != (range_end + 1))) {
+ DEBUG(1, ("ads_pull_strings_range: Range attribute (%s) tells us we have %lu "
+ "strings in this bunch, but we only got %lu - aborting range retreival\n",
+ range_attr, (unsigned long int)range_end - range_start + 1,
+ (unsigned long int)num_new_strings));
+ ldap_memfree(range_attr);
+ *more_strings = False;
+ return NULL;
+ }
+
+ strings = talloc_realloc(mem_ctx, current_strings,
+ sizeof(*current_strings) *
+ (*num_strings + num_new_strings));
+
+ if (strings == NULL) {
+ ldap_memfree(range_attr);
+ *more_strings = False;
+ return NULL;
+ }
+
+ memcpy(&strings[*num_strings], new_strings,
+ sizeof(*new_strings) * num_new_strings);
+
+ (*num_strings) += num_new_strings;
+
+ if (*more_strings) {
+ *next_attribute = talloc_asprintf(mem_ctx,
+ "%s;range=%d-*",
+ field,
+ *num_strings);
+
+ if (!*next_attribute) {
+ DEBUG(1, ("talloc_asprintf for next attribute failed!\n"));
+ ldap_memfree(range_attr);
+ *more_strings = False;
+ return NULL;
+ }
+ }
+
+ ldap_memfree(range_attr);
+
+ return strings;
+}
+
+/**
+ * pull a single uint32 from a ADS result
+ * @param ads connection to ads server
+ * @param msg Results of search
+ * @param field Attribute to retrieve
+ * @param v Pointer to int to store result
+ * @return boolean inidicating success
+*/
+BOOL ads_pull_uint32(ADS_STRUCT *ads,
+ void *msg, const char *field, uint32 *v)
+{
+ char **values;
+
+ values = ldap_get_values(ads->ld, msg, field);
+ if (!values)
+ return False;
+ if (!values[0]) {
+ ldap_value_free(values);
+ return False;
+ }
+
+ *v = atoi(values[0]);
+ ldap_value_free(values);
+ return True;
+}
+
+/**
+ * pull a single objectGUID from an ADS result
+ * @param ads connection to ADS server
+ * @param msg results of search
+ * @param guid 37-byte area to receive text guid
+ * @return boolean indicating success
+ **/
+BOOL ads_pull_guid(ADS_STRUCT *ads,
+ void *msg, struct uuid *guid)
+{
+ char **values;
+ UUID_FLAT flat_guid;
+
+ values = ldap_get_values(ads->ld, msg, "objectGUID");
+ if (!values)
+ return False;
+
+ if (values[0]) {
+ memcpy(&flat_guid.info, values[0], sizeof(UUID_FLAT));
+ smb_uuid_unpack(flat_guid, guid);
+ ldap_value_free(values);
+ return True;
+ }
+ ldap_value_free(values);
+ return False;
+
+}
+
+
+/**
+ * pull a single DOM_SID from a ADS result
+ * @param ads connection to ads server
+ * @param msg Results of search
+ * @param field Attribute to retrieve
+ * @param sid Pointer to sid to store result
+ * @return boolean inidicating success
+*/
+BOOL ads_pull_sid(ADS_STRUCT *ads,
+ void *msg, const char *field, DOM_SID *sid)
+{
+ struct berval **values;
+ BOOL ret = False;
+
+ values = ldap_get_values_len(ads->ld, msg, field);
+
+ if (!values)
+ return False;
+
+ if (values[0])
+ ret = sid_parse(values[0]->bv_val, values[0]->bv_len, sid);
+
+ ldap_value_free_len(values);
+ return ret;
+}
+
+/**
+ * pull an array of DOM_SIDs from a ADS result
+ * @param ads connection to ads server
+ * @param mem_ctx TALLOC_CTX for allocating sid array
+ * @param msg Results of search
+ * @param field Attribute to retrieve
+ * @param sids pointer to sid array to allocate
+ * @return the count of SIDs pulled
+ **/
+int ads_pull_sids(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
+ void *msg, const char *field, DOM_SID **sids)
+{
+ struct berval **values;
+ BOOL ret;
+ int count, i;
+
+ values = ldap_get_values_len(ads->ld, msg, field);
+
+ if (!values)
+ return 0;
+
+ for (i=0; values[i]; i++)
+ /* nop */ ;
+
+ (*sids) = talloc(mem_ctx, sizeof(DOM_SID) * i);
+ if (!(*sids)) {
+ ldap_value_free_len(values);
+ return 0;
+ }
+
+ count = 0;
+ for (i=0; values[i]; i++) {
+ ret = sid_parse(values[i]->bv_val, values[i]->bv_len, &(*sids)[count]);
+ if (ret) {
+ fstring sid;
+ DEBUG(10, ("pulling SID: %s\n", sid_to_string(sid, &(*sids)[count])));
+ count++;
+ }
+ }
+
+ ldap_value_free_len(values);
+ return count;
+}
+
+/**
+ * pull a SEC_DESC from a ADS result
+ * @param ads connection to ads server
+ * @param mem_ctx TALLOC_CTX for allocating sid array
+ * @param msg Results of search
+ * @param field Attribute to retrieve
+ * @param sd Pointer to *SEC_DESC to store result (talloc()ed)
+ * @return boolean inidicating success
+*/
+BOOL ads_pull_sd(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
+ void *msg, const char *field, SEC_DESC **sd)
+{
+ struct berval **values;
+ prs_struct ps;
+ BOOL ret = False;
+
+ values = ldap_get_values_len(ads->ld, msg, field);
+
+ if (!values) return False;
+
+ if (values[0]) {
+ prs_init(&ps, values[0]->bv_len, mem_ctx, UNMARSHALL);
+ prs_copy_data_in(&ps, values[0]->bv_val, values[0]->bv_len);
+ prs_set_offset(&ps,0);
+
+ ret = sec_io_desc("sd", sd, &ps, 1);
+ }
+
+ ldap_value_free_len(values);
+ return ret;
+}
+
+/*
+ * in order to support usernames longer than 21 characters we need to
+ * use both the sAMAccountName and the userPrincipalName attributes
+ * It seems that not all users have the userPrincipalName attribute set
+ *
+ * @param ads connection to ads server
+ * @param mem_ctx TALLOC_CTX for allocating sid array
+ * @param msg Results of search
+ * @return the username
+ */
+char *ads_pull_username(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, void *msg)
+{
+ char *ret, *p;
+
+ ret = ads_pull_string(ads, mem_ctx, msg, "userPrincipalName");
+ if (ret && (p = strchr(ret, '@'))) {
+ *p = 0;
+ return ret;
+ }
+ return ads_pull_string(ads, mem_ctx, msg, "sAMAccountName");
+}
+
+
+/**
+ * find the update serial number - this is the core of the ldap cache
+ * @param ads connection to ads server
+ * @param ads connection to ADS server
+ * @param usn Pointer to retrieved update serial number
+ * @return status of search
+ **/
+ADS_STATUS ads_USN(ADS_STRUCT *ads, uint32 *usn)
+{
+ const char *attrs[] = {"highestCommittedUSN", NULL};
+ ADS_STATUS status;
+ void *res;
+
+ status = ads_do_search_retry(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
+ if (!ADS_ERR_OK(status))
+ return status;
+
+ if (ads_count_replies(ads, res) != 1) {
+ return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
+ }
+
+ ads_pull_uint32(ads, res, "highestCommittedUSN", usn);
+ ads_msgfree(ads, res);
+ return ADS_SUCCESS;
+}
+
+/* parse a ADS timestring - typical string is
+ '20020917091222.0Z0' which means 09:12.22 17th September
+ 2002, timezone 0 */
+static time_t ads_parse_time(const char *str)
+{
+ struct tm tm;
+
+ ZERO_STRUCT(tm);
+
+ if (sscanf(str, "%4d%2d%2d%2d%2d%2d",
+ &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);
+}
+
+
+/**
+ * Find the servers name and realm - this can be done before authentication
+ * The ldapServiceName field on w2k looks like this:
+ * vnet3.home.samba.org:win2000-vnet3$@VNET3.HOME.SAMBA.ORG
+ * @param ads connection to ads server
+ * @return status of search
+ **/
+ADS_STATUS ads_server_info(ADS_STRUCT *ads)
+{
+ const char *attrs[] = {"ldapServiceName", "currentTime", NULL};
+ ADS_STATUS status;
+ void *res;
+ char *value;
+ char *p;
+ char *timestr;
+ TALLOC_CTX *ctx;
+
+ if (!(ctx = talloc_init("ads_server_info"))) {
+ return ADS_ERROR(LDAP_NO_MEMORY);
+ }
+
+ status = ads_do_search(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
+ if (!ADS_ERR_OK(status)) return status;
+
+ value = ads_pull_string(ads, ctx, res, "ldapServiceName");
+ if (!value) {
+ return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
+ }
+
+ timestr = ads_pull_string(ads, ctx, res, "currentTime");
+ if (!timestr) {
+ return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
+ }
+
+ ldap_msgfree(res);
+
+ p = strchr(value, ':');
+ if (!p) {
+ talloc_destroy(ctx);
+ DEBUG(1, ("ads_server_info: returned ldap server name did not contain a ':' "
+ "so was deemed invalid\n"));
+ return ADS_ERROR(LDAP_DECODING_ERROR);
+ }
+
+ SAFE_FREE(ads->config.ldap_server_name);
+
+ ads->config.ldap_server_name = strdup(p+1);
+ p = strchr(ads->config.ldap_server_name, '$');
+ if (!p || p[1] != '@') {
+ talloc_destroy(ctx);
+ DEBUG(1, ("ads_server_info: returned ldap server name (%s) does not contain '$@'"
+ " so was deemed invalid\n", ads->config.ldap_server_name));
+ SAFE_FREE(ads->config.ldap_server_name);
+ return ADS_ERROR(LDAP_DECODING_ERROR);
+ }
+
+ *p = 0;
+
+ SAFE_FREE(ads->config.realm);
+ SAFE_FREE(ads->config.bind_path);
+
+ ads->config.realm = strdup(p+2);
+ ads->config.bind_path = ads_build_dn(ads->config.realm);
+
+ DEBUG(3,("got ldap server name %s@%s, using bind path: %s\n",
+ ads->config.ldap_server_name, ads->config.realm,
+ ads->config.bind_path));
+
+ ads->config.current_time = ads_parse_time(timestr);
+
+ if (ads->config.current_time != 0) {
+ ads->auth.time_offset = ads->config.current_time - time(NULL);
+ DEBUG(4,("time offset is %d seconds\n", ads->auth.time_offset));
+ }
+
+ talloc_destroy(ctx);
+
+ return ADS_SUCCESS;
+}
+
+/**
+ * find the domain sid for our domain
+ * @param ads connection to ads server
+ * @param sid Pointer to domain sid
+ * @return status of search
+ **/
+ADS_STATUS ads_domain_sid(ADS_STRUCT *ads, DOM_SID *sid)
+{
+ const char *attrs[] = {"objectSid", NULL};
+ void *res;
+ ADS_STATUS rc;
+
+ rc = ads_do_search_retry(ads, ads->config.bind_path, LDAP_SCOPE_BASE, "(objectclass=*)",
+ attrs, &res);
+ if (!ADS_ERR_OK(rc)) return rc;
+ if (!ads_pull_sid(ads, res, "objectSid", sid)) {
+ return ADS_ERROR_SYSTEM(ENOENT);
+ }
+ ads_msgfree(ads, res);
+
+ return ADS_SUCCESS;
+}
+
+/* this is rather complex - we need to find the allternate (netbios) name
+ for the domain, but there isn't a simple query to do this. Instead
+ we look for the principle names on the DCs account and find one that has
+ the right form, then extract the netbios name of the domain from that
+
+ NOTE! better method is this:
+
+bin/net -Uadministrator%XXXXX ads search '(&(objectclass=crossref)(dnsroot=VNET3.HOME.SAMBA.ORG))' nETBIOSName
+
+but you need to force the bind path to match the configurationNamingContext from the rootDSE
+
+*/
+ADS_STATUS ads_workgroup_name(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, const char **workgroup)
+{
+ char *expr;
+ ADS_STATUS rc;
+ char **principles;
+ char *prefix;
+ int prefix_length;
+ int i;
+ void *res;
+ const char *attrs[] = {"servicePrincipalName", NULL};
+ int num_principals;
+
+ (*workgroup) = NULL;
+
+ asprintf(&expr, "(&(objectclass=computer)(dnshostname=%s.%s))",
+ ads->config.ldap_server_name, ads->config.realm);
+ rc = ads_search(ads, &res, expr, attrs);
+ free(expr);
+
+ if (!ADS_ERR_OK(rc)) {
+ return rc;
+ }
+
+ principles = ads_pull_strings(ads, mem_ctx, res,
+ "servicePrincipalName", &num_principals);
+
+ ads_msgfree(ads, res);
+
+ if (!principles) {
+ return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
+ }
+
+ asprintf(&prefix, "HOST/%s.%s/",
+ ads->config.ldap_server_name,
+ ads->config.realm);
+
+ prefix_length = strlen(prefix);
+
+ for (i=0;principles[i]; i++) {
+ if (strnequal(principles[i], prefix, prefix_length) &&
+ !strequal(ads->config.realm, principles[i]+prefix_length) &&
+ !strchr(principles[i]+prefix_length, '.')) {
+ /* found an alternate (short) name for the domain. */
+ DEBUG(3,("Found alternate name '%s' for realm '%s'\n",
+ principles[i]+prefix_length,
+ ads->config.realm));
+ (*workgroup) = talloc_strdup(mem_ctx, principles[i]+prefix_length);
+ break;
+ }
+ }
+ free(prefix);
+
+ if (!*workgroup) {
+ return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
+ }
+
+ return ADS_SUCCESS;
+}
+
+#endif
diff --git a/source/libads/ldap_printer.c b/source/libads/ldap_printer.c
new file mode 100644
index 00000000000..1448074ea02
--- /dev/null
+++ b/source/libads/ldap_printer.c
@@ -0,0 +1,354 @@
+/*
+ Unix SMB/CIFS implementation.
+ ads (active directory) printer utility library
+ Copyright (C) Jim McDonough <jmcd@us.ibm.com> 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 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"
+
+#ifdef HAVE_ADS
+
+/*
+ find a printer given the name and the hostname
+ Note that results "res" may be allocated on return so that the
+ results can be used. It should be freed using ads_msgfree.
+*/
+ADS_STATUS ads_find_printer_on_server(ADS_STRUCT *ads, void **res,
+ const char *printer, const char *servername)
+{
+ ADS_STATUS status;
+ char *srv_dn, **srv_cn, *s;
+ const char *attrs[] = {"*", "nTSecurityDescriptor", NULL};
+
+ status = ads_find_machine_acct(ads, res, servername);
+ if (!ADS_ERR_OK(status)) {
+ DEBUG(1, ("ads_add_printer: cannot find host %s in ads\n",
+ servername));
+ return status;
+ }
+ srv_dn = ldap_get_dn(ads->ld, *res);
+ srv_cn = ldap_explode_dn(srv_dn, 1);
+ ads_msgfree(ads, *res);
+
+ asprintf(&s, "(cn=%s-%s)", srv_cn[0], printer);
+ status = ads_search(ads, res, s, attrs);
+
+ ldap_memfree(srv_dn);
+ ldap_value_free(srv_cn);
+ free(s);
+ return status;
+}
+
+ADS_STATUS ads_find_printers(ADS_STRUCT *ads, void **res)
+{
+ char *ldap_expr;
+ const char *attrs[] = { "objectClass", "printerName", "location", "driverName",
+ "serverName", "description", NULL };
+
+ /* For the moment only display all printers */
+
+ ldap_expr = "(&(!(showInAdvancedViewOnly=TRUE))(uncName=*)"
+ "(objectCategory=printQueue))";
+
+ return ads_search(ads, res, ldap_expr, attrs);
+}
+
+/*
+ modify a printer entry in the directory
+*/
+ADS_STATUS ads_mod_printer_entry(ADS_STRUCT *ads, char *prt_dn,
+ TALLOC_CTX *ctx, const ADS_MODLIST *mods)
+{
+ return ads_gen_mod(ads, prt_dn, *mods);
+}
+
+/*
+ add a printer to the directory
+*/
+ADS_STATUS ads_add_printer_entry(ADS_STRUCT *ads, char *prt_dn,
+ TALLOC_CTX *ctx, ADS_MODLIST *mods)
+{
+ ads_mod_str(ctx, mods, "objectClass", "printQueue");
+ return ads_gen_add(ads, prt_dn, *mods);
+}
+
+/*
+ map a REG_SZ to an ldap mod
+*/
+static BOOL map_sz(TALLOC_CTX *ctx, ADS_MODLIST *mods,
+ const REGISTRY_VALUE *value)
+{
+ char *str_value = NULL;
+ ADS_STATUS status;
+
+ if (value->type != REG_SZ)
+ return False;
+
+ if (value->size && *((smb_ucs2_t *) value->data_p)) {
+ pull_ucs2_talloc(ctx, &str_value, (const smb_ucs2_t *) value->data_p);
+ status = ads_mod_str(ctx, mods, value->valuename, str_value);
+ return ADS_ERR_OK(status);
+ }
+ return True;
+
+}
+
+/*
+ map a REG_DWORD to an ldap mod
+*/
+static BOOL map_dword(TALLOC_CTX *ctx, ADS_MODLIST *mods,
+ const REGISTRY_VALUE *value)
+{
+ char *str_value = NULL;
+ ADS_STATUS status;
+
+ if (value->type != REG_DWORD)
+ return False;
+ str_value = talloc_asprintf(ctx, "%d", *((uint32 *) value->data_p));
+ status = ads_mod_str(ctx, mods, value->valuename, str_value);
+ return ADS_ERR_OK(status);
+}
+
+/*
+ map a boolean REG_BINARY to an ldap mod
+*/
+static BOOL map_bool(TALLOC_CTX *ctx, ADS_MODLIST *mods,
+ const REGISTRY_VALUE *value)
+{
+ char *str_value;
+ ADS_STATUS status;
+
+ if ((value->type != REG_BINARY) || (value->size != 1))
+ return False;
+ str_value = talloc_asprintf(ctx, "%s",
+ *(value->data_p) ? "TRUE" : "FALSE");
+ status = ads_mod_str(ctx, mods, value->valuename, str_value);
+ return ADS_ERR_OK(status);
+}
+
+/*
+ map a REG_MULTI_SZ to an ldap mod
+*/
+static BOOL map_multi_sz(TALLOC_CTX *ctx, ADS_MODLIST *mods,
+ const REGISTRY_VALUE *value)
+{
+ char **str_values = NULL;
+ smb_ucs2_t *cur_str = (smb_ucs2_t *) value->data_p;
+ uint32 size = 0, num_vals = 0, i=0;
+ ADS_STATUS status;
+
+ if (value->type != REG_MULTI_SZ)
+ return False;
+
+ while(cur_str && *cur_str && (size < value->size)) {
+ size += 2 * (strlen_w(cur_str) + 1);
+ cur_str += strlen_w(cur_str) + 1;
+ num_vals++;
+ };
+
+ if (num_vals) {
+ str_values = talloc(ctx,
+ (num_vals + 1) * sizeof(smb_ucs2_t *));
+ memset(str_values, '\0',
+ (num_vals + 1) * sizeof(smb_ucs2_t *));
+
+ cur_str = (smb_ucs2_t *) value->data_p;
+ for (i=0; i < num_vals; i++)
+ cur_str += pull_ucs2_talloc(ctx, &str_values[i],
+ cur_str);
+
+ status = ads_mod_strlist(ctx, mods, value->valuename,
+ (const char **) str_values);
+ return ADS_ERR_OK(status);
+ }
+ return True;
+}
+
+struct valmap_to_ads {
+ const char *valname;
+ BOOL (*fn)(TALLOC_CTX *, ADS_MODLIST *, const REGISTRY_VALUE *);
+};
+
+/*
+ map a REG_SZ to an ldap mod
+*/
+static void map_regval_to_ads(TALLOC_CTX *ctx, ADS_MODLIST *mods,
+ REGISTRY_VALUE *value)
+{
+ const struct valmap_to_ads map[] = {
+ {SPOOL_REG_ASSETNUMBER, map_sz},
+ {SPOOL_REG_BYTESPERMINUTE, map_dword},
+ {SPOOL_REG_DEFAULTPRIORITY, map_dword},
+ {SPOOL_REG_DESCRIPTION, map_sz},
+ {SPOOL_REG_DRIVERNAME, map_sz},
+ {SPOOL_REG_DRIVERVERSION, map_dword},
+ {SPOOL_REG_FLAGS, map_dword},
+ {SPOOL_REG_LOCATION, map_sz},
+ {SPOOL_REG_OPERATINGSYSTEM, map_sz},
+ {SPOOL_REG_OPERATINGSYSTEMHOTFIX, map_sz},
+ {SPOOL_REG_OPERATINGSYSTEMSERVICEPACK, map_sz},
+ {SPOOL_REG_OPERATINGSYSTEMVERSION, map_sz},
+ {SPOOL_REG_PORTNAME, map_multi_sz},
+ {SPOOL_REG_PRINTATTRIBUTES, map_dword},
+ {SPOOL_REG_PRINTBINNAMES, map_multi_sz},
+ {SPOOL_REG_PRINTCOLLATE, map_bool},
+ {SPOOL_REG_PRINTCOLOR, map_bool},
+ {SPOOL_REG_PRINTDUPLEXSUPPORTED, map_bool},
+ {SPOOL_REG_PRINTENDTIME, map_dword},
+ {SPOOL_REG_PRINTFORMNAME, map_sz},
+ {SPOOL_REG_PRINTKEEPPRINTEDJOBS, map_bool},
+ {SPOOL_REG_PRINTLANGUAGE, map_multi_sz},
+ {SPOOL_REG_PRINTMACADDRESS, map_sz},
+ {SPOOL_REG_PRINTMAXCOPIES, map_sz},
+ {SPOOL_REG_PRINTMAXRESOLUTIONSUPPORTED, map_dword},
+ {SPOOL_REG_PRINTMAXXEXTENT, map_dword},
+ {SPOOL_REG_PRINTMAXYEXTENT, map_dword},
+ {SPOOL_REG_PRINTMEDIAREADY, map_multi_sz},
+ {SPOOL_REG_PRINTMEDIASUPPORTED, map_multi_sz},
+ {SPOOL_REG_PRINTMEMORY, map_dword},
+ {SPOOL_REG_PRINTMINXEXTENT, map_dword},
+ {SPOOL_REG_PRINTMINYEXTENT, map_dword},
+ {SPOOL_REG_PRINTNETWORKADDRESS, map_sz},
+ {SPOOL_REG_PRINTNOTIFY, map_sz},
+ {SPOOL_REG_PRINTNUMBERUP, map_dword},
+ {SPOOL_REG_PRINTORIENTATIONSSUPPORTED, map_multi_sz},
+ {SPOOL_REG_PRINTOWNER, map_sz},
+ {SPOOL_REG_PRINTPAGESPERMINUTE, map_dword},
+ {SPOOL_REG_PRINTRATE, map_dword},
+ {SPOOL_REG_PRINTRATEUNIT, map_sz},
+ {SPOOL_REG_PRINTSEPARATORFILE, map_sz},
+ {SPOOL_REG_PRINTSHARENAME, map_sz},
+ {SPOOL_REG_PRINTSPOOLING, map_sz},
+ {SPOOL_REG_PRINTSTAPLINGSUPPORTED, map_bool},
+ {SPOOL_REG_PRINTSTARTTIME, map_dword},
+ {SPOOL_REG_PRINTSTATUS, map_sz},
+ {SPOOL_REG_PRIORITY, map_dword},
+ {SPOOL_REG_SERVERNAME, map_sz},
+ {SPOOL_REG_SHORTSERVERNAME, map_sz},
+ {SPOOL_REG_UNCNAME, map_sz},
+ {SPOOL_REG_URL, map_sz},
+ {SPOOL_REG_VERSIONNUMBER, map_dword},
+ {NULL, NULL}
+ };
+ int i;
+
+ for (i=0; map[i].valname; i++) {
+ if (StrCaseCmp(map[i].valname, value->valuename) == 0) {
+ if (!map[i].fn(ctx, mods, value)) {
+ DEBUG(5, ("Add of value %s to modlist failed\n", value->valuename));
+ } else {
+ DEBUG(7, ("Mapped value %s\n", value->valuename));
+ }
+
+ }
+ }
+}
+
+
+WERROR get_remote_printer_publishing_data(struct cli_state *cli,
+ TALLOC_CTX *mem_ctx,
+ ADS_MODLIST *mods,
+ const char *printer)
+{
+ WERROR result;
+ char *printername, *servername;
+ REGVAL_CTR dsdriver_ctr, dsspooler_ctr;
+ BOOL got_dsdriver = False, got_dsspooler = False;
+ uint32 needed, i;
+ POLICY_HND pol;
+
+ asprintf(&servername, "\\\\%s", cli->desthost);
+ asprintf(&printername, "%s\\%s", servername, printer);
+ if (!servername || !printername) {
+ DEBUG(3, ("Insufficient memory\n"));
+ return WERR_NOMEM;
+ }
+
+ result = cli_spoolss_open_printer_ex(cli, mem_ctx, printername,
+ "", MAXIMUM_ALLOWED_ACCESS,
+ servername, cli->user_name, &pol);
+ if (!W_ERROR_IS_OK(result)) {
+ DEBUG(3, ("Unable to open printer %s, error is %s.\n",
+ printername, dos_errstr(result)));
+ return result;
+ }
+
+ result = cli_spoolss_enumprinterdataex(cli, mem_ctx, 0, &needed,
+ &pol, SPOOL_DSDRIVER_KEY, NULL);
+
+ if (W_ERROR_V(result) == ERRmoredata)
+ result = cli_spoolss_enumprinterdataex(cli, mem_ctx, needed,
+ NULL, &pol,
+ SPOOL_DSDRIVER_KEY,
+ &dsdriver_ctr);
+
+ if (!W_ERROR_IS_OK(result)) {
+ DEBUG(3, ("Unable to do enumdataex on %s, error is %s.\n",
+ printername, dos_errstr(result)));
+ } else {
+
+ /* Have the data we need now, so start building */
+ got_dsdriver = True;
+ for (i=0; i < dsdriver_ctr.num_values; i++)
+ map_regval_to_ads(mem_ctx, mods,
+ dsdriver_ctr.values[i]);
+ }
+
+ result = cli_spoolss_enumprinterdataex(cli, mem_ctx, 0, &needed,
+ &pol, SPOOL_DSSPOOLER_KEY,
+ NULL);
+
+ if (W_ERROR_V(result) == ERRmoredata)
+ result = cli_spoolss_enumprinterdataex(cli, mem_ctx, needed,
+ NULL, &pol,
+ SPOOL_DSSPOOLER_KEY,
+ &dsspooler_ctr);
+
+ if (!W_ERROR_IS_OK(result)) {
+ DEBUG(3, ("Unable to do enumdataex on %s, error is %s.\n",
+ printername, dos_errstr(result)));
+ } else {
+ got_dsspooler = True;
+ for (i=0; i < dsspooler_ctr.num_values; i++)
+ map_regval_to_ads(mem_ctx, mods,
+ dsspooler_ctr.values[i]);
+ }
+
+ ads_mod_str(mem_ctx, mods, SPOOL_REG_PRINTERNAME, printer);
+
+ if (got_dsdriver) regval_ctr_destroy(&dsdriver_ctr);
+ if (got_dsspooler) regval_ctr_destroy(&dsspooler_ctr);
+ cli_spoolss_close_printer(cli, mem_ctx, &pol);
+
+ return result;
+}
+
+BOOL get_local_printer_publishing_data(TALLOC_CTX *mem_ctx,
+ ADS_MODLIST *mods,
+ NT_PRINTER_DATA *data)
+{
+ uint32 key,val;
+
+ for (key=0; key < data->num_keys; key++) {
+ REGVAL_CTR ctr = data->keys[key].values;
+ for (val=0; val < ctr.num_values; val++)
+ map_regval_to_ads(mem_ctx, mods, ctr.values[val]);
+ }
+ return True;
+}
+
+#endif
diff --git a/source/libads/ldap_user.c b/source/libads/ldap_user.c
new file mode 100644
index 00000000000..56a0d8013b2
--- /dev/null
+++ b/source/libads/ldap_user.c
@@ -0,0 +1,119 @@
+/*
+ Unix SMB/CIFS implementation.
+ ads (active directory) utility library
+ Copyright (C) Jim McDonough <jmcd@us.ibm.com> 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 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"
+
+#ifdef HAVE_ADS
+
+/*
+ find a user account
+*/
+ADS_STATUS ads_find_user_acct(ADS_STRUCT *ads, void **res, const char *user)
+{
+ ADS_STATUS status;
+ char *ldap_exp;
+ const char *attrs[] = {"*", NULL};
+ char *escaped_user = escape_ldap_string_alloc(user);
+ if (!escaped_user) {
+ return ADS_ERROR(LDAP_NO_MEMORY);
+ }
+
+ asprintf(&ldap_exp, "(samAccountName=%s)", escaped_user);
+ status = ads_search(ads, res, ldap_exp, attrs);
+ SAFE_FREE(ldap_exp);
+ SAFE_FREE(escaped_user);
+ return status;
+}
+
+ADS_STATUS ads_add_user_acct(ADS_STRUCT *ads, const char *user,
+ const char *container, const char *fullname)
+{
+ TALLOC_CTX *ctx;
+ ADS_MODLIST mods;
+ ADS_STATUS status;
+ const char *upn, *new_dn, *name, *controlstr;
+ const char *objectClass[] = {"top", "person", "organizationalPerson",
+ "user", NULL};
+
+ if (fullname && *fullname) name = fullname;
+ else name = user;
+
+ if (!(ctx = talloc_init("ads_add_user_acct")))
+ return ADS_ERROR(LDAP_NO_MEMORY);
+
+ status = ADS_ERROR(LDAP_NO_MEMORY);
+
+ if (!(upn = talloc_asprintf(ctx, "%s@%s", user, ads->config.realm)))
+ goto done;
+ if (!(new_dn = talloc_asprintf(ctx, "cn=%s,%s,%s", name, container,
+ ads->config.bind_path)))
+ goto done;
+ if (!(controlstr = talloc_asprintf(ctx, "%u", UF_NORMAL_ACCOUNT)))
+ goto done;
+ if (!(mods = ads_init_mods(ctx)))
+ goto done;
+
+ ads_mod_str(ctx, &mods, "cn", name);
+ ads_mod_strlist(ctx, &mods, "objectClass", objectClass);
+ ads_mod_str(ctx, &mods, "userPrincipalName", upn);
+ ads_mod_str(ctx, &mods, "name", name);
+ ads_mod_str(ctx, &mods, "displayName", name);
+ ads_mod_str(ctx, &mods, "sAMAccountName", user);
+ ads_mod_str(ctx, &mods, "userAccountControl", controlstr);
+ status = ads_gen_add(ads, new_dn, mods);
+
+ done:
+ talloc_destroy(ctx);
+ return status;
+}
+
+ADS_STATUS ads_add_group_acct(ADS_STRUCT *ads, const char *group,
+ const char *container, const char *comment)
+{
+ TALLOC_CTX *ctx;
+ ADS_MODLIST mods;
+ ADS_STATUS status;
+ char *new_dn;
+ const char *objectClass[] = {"top", "group", NULL};
+
+ if (!(ctx = talloc_init("ads_add_group_acct")))
+ return ADS_ERROR(LDAP_NO_MEMORY);
+
+ status = ADS_ERROR(LDAP_NO_MEMORY);
+
+ if (!(new_dn = talloc_asprintf(ctx, "cn=%s,%s,%s", group, container,
+ ads->config.bind_path)))
+ goto done;
+ if (!(mods = ads_init_mods(ctx)))
+ goto done;
+
+ ads_mod_str(ctx, &mods, "cn", group);
+ ads_mod_strlist(ctx, &mods, "objectClass",objectClass);
+ ads_mod_str(ctx, &mods, "name", group);
+ if (comment && *comment)
+ ads_mod_str(ctx, &mods, "description", comment);
+ ads_mod_str(ctx, &mods, "sAMAccountName", group);
+ status = ads_gen_add(ads, new_dn, mods);
+
+ done:
+ talloc_destroy(ctx);
+ return status;
+}
+#endif
diff --git a/source/libads/ldap_utils.c b/source/libads/ldap_utils.c
new file mode 100644
index 00000000000..991f16c8454
--- /dev/null
+++ b/source/libads/ldap_utils.c
@@ -0,0 +1,108 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ Some Helpful wrappers on LDAP
+
+ 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 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"
+
+#ifdef HAVE_LDAP
+/*
+ a wrapper around ldap_search_s that retries depending on the error code
+ this is supposed to catch dropped connections and auto-reconnect
+*/
+ADS_STATUS ads_do_search_retry(ADS_STRUCT *ads, const char *bind_path, int scope,
+ const char *expr,
+ const char **attrs, void **res)
+{
+ ADS_STATUS status;
+ int count = 3;
+ char *bp;
+
+ *res = NULL;
+
+ if (!ads->ld &&
+ time(NULL) - ads->last_attempt < ADS_RECONNECT_TIME) {
+ return ADS_ERROR(LDAP_SERVER_DOWN);
+ }
+
+ bp = strdup(bind_path);
+
+ if (!bp) {
+ return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
+ }
+
+ while (count--) {
+ *res = NULL;
+ status = ads_do_search_all(ads, bp, scope, expr, attrs, res);
+ if (ADS_ERR_OK(status)) {
+ DEBUG(5,("Search for %s gave %d replies\n",
+ expr, ads_count_replies(ads, *res)));
+ SAFE_FREE(bp);
+ return status;
+ }
+
+ if (*res)
+ ads_msgfree(ads, *res);
+ *res = NULL;
+
+ DEBUG(3,("Reopening ads connection to realm '%s' after error %s\n",
+ ads->config.realm, ads_errstr(status)));
+
+ if (ads->ld) {
+ ldap_unbind(ads->ld);
+ }
+
+ ads->ld = NULL;
+ status = ads_connect(ads);
+
+ if (!ADS_ERR_OK(status)) {
+ DEBUG(1,("ads_search_retry: failed to reconnect (%s)\n",
+ ads_errstr(status)));
+ ads_destroy(&ads);
+ SAFE_FREE(bp);
+ return status;
+ }
+ }
+ SAFE_FREE(bp);
+
+ if (!ADS_ERR_OK(status))
+ DEBUG(1,("ads reopen failed after error %s\n",
+ ads_errstr(status)));
+
+ return status;
+}
+
+
+ADS_STATUS ads_search_retry(ADS_STRUCT *ads, void **res,
+ const char *expr,
+ const char **attrs)
+{
+ return ads_do_search_retry(ads, ads->config.bind_path, LDAP_SCOPE_SUBTREE,
+ expr, attrs, res);
+}
+
+ADS_STATUS ads_search_retry_dn(ADS_STRUCT *ads, void **res,
+ const char *dn,
+ const char **attrs)
+{
+ return ads_do_search_retry(ads, dn, LDAP_SCOPE_BASE,
+ "(objectclass=*)", attrs, res);
+}
+#endif
diff --git a/source/libads/sasl.c b/source/libads/sasl.c
new file mode 100644
index 00000000000..1ab71c6ee51
--- /dev/null
+++ b/source/libads/sasl.c
@@ -0,0 +1,438 @@
+/*
+ Unix SMB/CIFS implementation.
+ ads sasl code
+ 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 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"
+
+#ifdef HAVE_LDAP
+
+/*
+ perform a LDAP/SASL/SPNEGO/NTLMSSP bind (just how many layers can
+ we fit on one socket??)
+*/
+static ADS_STATUS ads_sasl_spnego_ntlmssp_bind(ADS_STRUCT *ads)
+{
+ const char *mechs[] = {OID_NTLMSSP, NULL};
+ DATA_BLOB msg1;
+ DATA_BLOB blob, chal1, chal2, auth;
+ uint8 challenge[8];
+ uint8 nthash[24], lmhash[24], sess_key[16];
+ uint32 neg_flags;
+ struct berval cred, *scred;
+ ADS_STATUS status;
+ int rc;
+
+ if (!ads->auth.password) {
+ /* No password, don't segfault below... */
+ return ADS_ERROR_NT(NT_STATUS_LOGON_FAILURE);
+ }
+
+ neg_flags = NTLMSSP_NEGOTIATE_UNICODE |
+ NTLMSSP_NEGOTIATE_128 |
+ NTLMSSP_NEGOTIATE_NTLM;
+
+ memset(sess_key, 0, 16);
+
+ /* generate the ntlmssp negotiate packet */
+ msrpc_gen(&blob, "CddB",
+ "NTLMSSP",
+ NTLMSSP_NEGOTIATE,
+ neg_flags,
+ sess_key, 16);
+
+ /* and wrap it in a SPNEGO wrapper */
+ msg1 = gen_negTokenTarg(mechs, blob);
+ data_blob_free(&blob);
+
+ cred.bv_val = (char *)msg1.data;
+ cred.bv_len = msg1.length;
+
+ rc = ldap_sasl_bind_s(ads->ld, NULL, "GSS-SPNEGO", &cred, NULL, NULL, &scred);
+ if (rc != LDAP_SASL_BIND_IN_PROGRESS) {
+ status = ADS_ERROR(rc);
+ goto failed;
+ }
+
+ blob = data_blob(scred->bv_val, scred->bv_len);
+
+ /* the server gives us back two challenges */
+ if (!spnego_parse_challenge(blob, &chal1, &chal2)) {
+ DEBUG(3,("Failed to parse challenges\n"));
+ status = ADS_ERROR(LDAP_OPERATIONS_ERROR);
+ goto failed;
+ }
+
+ data_blob_free(&blob);
+
+ /* encrypt the password with the challenge */
+ memcpy(challenge, chal1.data + 24, 8);
+ SMBencrypt(ads->auth.password, challenge,lmhash);
+ SMBNTencrypt(ads->auth.password, challenge,nthash);
+
+ data_blob_free(&chal1);
+ data_blob_free(&chal2);
+
+ /* this generates the actual auth packet */
+ msrpc_gen(&blob, "CdBBUUUBd",
+ "NTLMSSP",
+ NTLMSSP_AUTH,
+ lmhash, 24,
+ nthash, 24,
+ lp_workgroup(),
+ ads->auth.user_name,
+ global_myname(),
+ sess_key, 16,
+ neg_flags);
+
+ /* wrap it in SPNEGO */
+ auth = spnego_gen_auth(blob);
+
+ data_blob_free(&blob);
+
+ /* now send the auth packet and we should be done */
+ cred.bv_val = (char *)auth.data;
+ cred.bv_len = auth.length;
+
+ rc = ldap_sasl_bind_s(ads->ld, NULL, "GSS-SPNEGO", &cred, NULL, NULL, &scred);
+
+ return ADS_ERROR(rc);
+
+failed:
+ return status;
+}
+
+/*
+ perform a LDAP/SASL/SPNEGO/KRB5 bind
+*/
+static ADS_STATUS ads_sasl_spnego_krb5_bind(ADS_STRUCT *ads, const char *principal)
+{
+ DATA_BLOB blob;
+ struct berval cred, *scred;
+ DATA_BLOB session_key;
+ int rc;
+
+ rc = spnego_gen_negTokenTarg(principal, ads->auth.time_offset, &blob, &session_key);
+
+ if (rc) {
+ return ADS_ERROR_KRB5(rc);
+ }
+
+ /* now send the auth packet and we should be done */
+ cred.bv_val = (char *)blob.data;
+ cred.bv_len = blob.length;
+
+ rc = ldap_sasl_bind_s(ads->ld, NULL, "GSS-SPNEGO", &cred, NULL, NULL, &scred);
+
+ data_blob_free(&blob);
+ data_blob_free(&session_key);
+
+ return ADS_ERROR(rc);
+}
+
+/*
+ this performs a SASL/SPNEGO bind
+*/
+static ADS_STATUS ads_sasl_spnego_bind(ADS_STRUCT *ads)
+{
+ struct berval *scred=NULL;
+ int rc, i;
+ ADS_STATUS status;
+ DATA_BLOB blob;
+ char *principal;
+ char *OIDs[ASN1_MAX_OIDS];
+ BOOL got_kerberos_mechanism = False;
+
+ rc = ldap_sasl_bind_s(ads->ld, NULL, "GSS-SPNEGO", NULL, NULL, NULL, &scred);
+
+ if (rc != LDAP_SASL_BIND_IN_PROGRESS) {
+ status = ADS_ERROR(rc);
+ goto failed;
+ }
+
+ blob = data_blob(scred->bv_val, scred->bv_len);
+
+ ber_bvfree(scred);
+
+#if 0
+ file_save("sasl_spnego.dat", blob.data, blob.length);
+#endif
+
+ /* the server sent us the first part of the SPNEGO exchange in the negprot
+ reply */
+ if (!spnego_parse_negTokenInit(blob, OIDs, &principal)) {
+ data_blob_free(&blob);
+ status = ADS_ERROR(LDAP_OPERATIONS_ERROR);
+ goto failed;
+ }
+ data_blob_free(&blob);
+
+ /* make sure the server understands kerberos */
+ for (i=0;OIDs[i];i++) {
+ DEBUG(3,("got OID=%s\n", OIDs[i]));
+ if (strcmp(OIDs[i], OID_KERBEROS5_OLD) == 0 ||
+ strcmp(OIDs[i], OID_KERBEROS5) == 0) {
+ got_kerberos_mechanism = True;
+ }
+ free(OIDs[i]);
+ }
+ DEBUG(3,("got principal=%s\n", principal));
+
+#ifdef HAVE_KRB5
+ if (!(ads->auth.flags & ADS_AUTH_DISABLE_KERBEROS) &&
+ got_kerberos_mechanism) {
+ status = ads_sasl_spnego_krb5_bind(ads, principal);
+ if (ADS_ERR_OK(status))
+ return status;
+
+ status = ADS_ERROR_KRB5(ads_kinit_password(ads));
+
+ if (ADS_ERR_OK(status)) {
+ status = ads_sasl_spnego_krb5_bind(ads, principal);
+ }
+
+ /* only fallback to NTLMSSP if allowed */
+ if (ADS_ERR_OK(status) ||
+ !(ads->auth.flags & ADS_AUTH_ALLOW_NTLMSSP)) {
+ return status;
+ }
+ }
+#endif
+
+ /* lets do NTLMSSP ... this has the big advantage that we don't need
+ to sync clocks, and we don't rely on special versions of the krb5
+ library for HMAC_MD4 encryption */
+ return ads_sasl_spnego_ntlmssp_bind(ads);
+
+failed:
+ return status;
+}
+
+#ifdef HAVE_GSSAPI
+#define MAX_GSS_PASSES 3
+
+/* this performs a SASL/gssapi bind
+ we avoid using cyrus-sasl to make Samba more robust. cyrus-sasl
+ is very dependent on correctly configured DNS whereas
+ this routine is much less fragile
+ see RFC2078 and RFC2222 for details
+*/
+static ADS_STATUS ads_sasl_gssapi_bind(ADS_STRUCT *ads)
+{
+ uint32 minor_status;
+ gss_name_t serv_name;
+ gss_buffer_desc input_name;
+ gss_ctx_id_t context_handle;
+ gss_OID mech_type = GSS_C_NULL_OID;
+ gss_buffer_desc output_token, input_token;
+ uint32 ret_flags, conf_state;
+ struct berval cred;
+ struct berval *scred;
+ int i=0;
+ int gss_rc, rc;
+ uint8 *p;
+ uint32 max_msg_size;
+ char *sname;
+ unsigned sec_layer;
+ ADS_STATUS status;
+ krb5_principal principal;
+ krb5_context ctx;
+ krb5_enctype enc_types[] = {
+#ifdef ENCTYPE_ARCFOUR_HMAC
+ ENCTYPE_ARCFOUR_HMAC,
+#endif
+ ENCTYPE_DES_CBC_MD5,
+ ENCTYPE_NULL};
+ gss_OID_desc nt_principal =
+ {10, "\052\206\110\206\367\022\001\002\002\002"};
+
+ /* we need to fetch a service ticket as the ldap user in the
+ servers realm, regardless of our realm */
+ asprintf(&sname, "ldap/%s@%s", ads->config.ldap_server_name, ads->config.realm);
+ krb5_init_context(&ctx);
+ krb5_set_default_tgs_ktypes(ctx, enc_types);
+ krb5_parse_name(ctx, sname, &principal);
+ free(sname);
+ krb5_free_context(ctx);
+
+ input_name.value = &principal;
+ input_name.length = sizeof(principal);
+
+ gss_rc = gss_import_name(&minor_status,&input_name,&nt_principal, &serv_name);
+ if (gss_rc) {
+ return ADS_ERROR_GSS(gss_rc, minor_status);
+ }
+
+ context_handle = GSS_C_NO_CONTEXT;
+
+ input_token.value = NULL;
+ input_token.length = 0;
+
+ for (i=0; i < MAX_GSS_PASSES; i++) {
+ gss_rc = gss_init_sec_context(&minor_status,
+ GSS_C_NO_CREDENTIAL,
+ &context_handle,
+ serv_name,
+ mech_type,
+ GSS_C_MUTUAL_FLAG | GSS_C_REPLAY_FLAG,
+ 0,
+ NULL,
+ &input_token,
+ NULL,
+ &output_token,
+ &ret_flags,
+ NULL);
+
+ if (input_token.value) {
+ gss_release_buffer(&minor_status, &input_token);
+ }
+
+ if (gss_rc && gss_rc != GSS_S_CONTINUE_NEEDED) {
+ status = ADS_ERROR_GSS(gss_rc, minor_status);
+ goto failed;
+ }
+
+ cred.bv_val = output_token.value;
+ cred.bv_len = output_token.length;
+
+ rc = ldap_sasl_bind_s(ads->ld, NULL, "GSSAPI", &cred, NULL, NULL,
+ &scred);
+ if (rc != LDAP_SASL_BIND_IN_PROGRESS) {
+ status = ADS_ERROR(rc);
+ goto failed;
+ }
+
+ if (output_token.value) {
+ gss_release_buffer(&minor_status, &output_token);
+ }
+
+ if (scred) {
+ input_token.value = scred->bv_val;
+ input_token.length = scred->bv_len;
+ } else {
+ input_token.value = NULL;
+ input_token.length = 0;
+ }
+
+ if (gss_rc == 0) break;
+ }
+
+ gss_release_name(&minor_status, &serv_name);
+
+ gss_rc = gss_unwrap(&minor_status,context_handle,&input_token,&output_token,
+ (int *)&conf_state,NULL);
+ if (gss_rc) {
+ status = ADS_ERROR_GSS(gss_rc, minor_status);
+ goto failed;
+ }
+
+ gss_release_buffer(&minor_status, &input_token);
+
+ p = (uint8 *)output_token.value;
+
+ file_save("sasl_gssapi.dat", output_token.value, output_token.length);
+
+ max_msg_size = (p[1]<<16) | (p[2]<<8) | p[3];
+ sec_layer = *p;
+
+ gss_release_buffer(&minor_status, &output_token);
+
+ output_token.value = malloc(strlen(ads->config.bind_path) + 8);
+ p = output_token.value;
+
+ *p++ = 1; /* no sign & seal selection */
+ /* choose the same size as the server gave us */
+ *p++ = max_msg_size>>16;
+ *p++ = max_msg_size>>8;
+ *p++ = max_msg_size;
+ snprintf((char *)p, strlen(ads->config.bind_path)+4, "dn:%s", ads->config.bind_path);
+ p += strlen((const char *)p);
+
+ output_token.length = PTR_DIFF(p, output_token.value);
+
+ gss_rc = gss_wrap(&minor_status, context_handle,0,GSS_C_QOP_DEFAULT,
+ &output_token, (int *)&conf_state,
+ &input_token);
+ if (gss_rc) {
+ status = ADS_ERROR_GSS(gss_rc, minor_status);
+ goto failed;
+ }
+
+ free(output_token.value);
+
+ cred.bv_val = input_token.value;
+ cred.bv_len = input_token.length;
+
+ rc = ldap_sasl_bind_s(ads->ld, NULL, "GSSAPI", &cred, NULL, NULL,
+ &scred);
+ status = ADS_ERROR(rc);
+
+ gss_release_buffer(&minor_status, &input_token);
+
+failed:
+ return status;
+}
+#endif
+
+/* mapping between SASL mechanisms and functions */
+static struct {
+ const char *name;
+ ADS_STATUS (*fn)(ADS_STRUCT *);
+} sasl_mechanisms[] = {
+ {"GSS-SPNEGO", ads_sasl_spnego_bind},
+#ifdef HAVE_GSSAPI
+ {"GSSAPI", ads_sasl_gssapi_bind}, /* doesn't work with .NET RC1. No idea why */
+#endif
+ {NULL, NULL}
+};
+
+ADS_STATUS ads_sasl_bind(ADS_STRUCT *ads)
+{
+ const char *attrs[] = {"supportedSASLMechanisms", NULL};
+ char **values;
+ ADS_STATUS status;
+ int i, j;
+ void *res;
+
+ /* get a list of supported SASL mechanisms */
+ status = ads_do_search(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
+ if (!ADS_ERR_OK(status)) return status;
+
+ values = ldap_get_values(ads->ld, res, "supportedSASLMechanisms");
+
+ /* try our supported mechanisms in order */
+ for (i=0;sasl_mechanisms[i].name;i++) {
+ /* see if the server supports it */
+ for (j=0;values && values[j];j++) {
+ if (strcmp(values[j], sasl_mechanisms[i].name) == 0) {
+ DEBUG(4,("Found SASL mechanism %s\n", values[j]));
+ status = sasl_mechanisms[i].fn(ads);
+ ldap_value_free(values);
+ ldap_msgfree(res);
+ return status;
+ }
+ }
+ }
+
+ ldap_value_free(values);
+ ldap_msgfree(res);
+ return ADS_ERROR(LDAP_AUTH_METHOD_NOT_SUPPORTED);
+}
+
+#endif
+
diff --git a/source/libads/util.c b/source/libads/util.c
new file mode 100644
index 00000000000..9912a7ba831
--- /dev/null
+++ b/source/libads/util.c
@@ -0,0 +1,62 @@
+/*
+ Unix SMB/CIFS implementation.
+ krb5 set password implementation
+ Copyright (C) Remus Koos 2001 (remuskoos@yahoo.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 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"
+
+#ifdef HAVE_KRB5
+
+ADS_STATUS ads_change_trust_account_password(ADS_STRUCT *ads, char *host_principal)
+{
+ char *tmp_password;
+ char *password;
+ char *new_password;
+ char *service_principal;
+ ADS_STATUS ret;
+ uint32 sec_channel_type;
+
+ if ((password = secrets_fetch_machine_password(lp_workgroup(), NULL, &sec_channel_type)) == NULL) {
+ DEBUG(1,("Failed to retrieve password for principal %s\n", host_principal));
+ return ADS_ERROR_SYSTEM(ENOENT);
+ }
+
+ tmp_password = generate_random_str(DEFAULT_TRUST_ACCOUNT_PASSWORD_LENGTH);
+ new_password = strdup(tmp_password);
+
+ asprintf(&service_principal, "HOST/%s", host_principal);
+
+ ret = kerberos_set_password(ads->auth.kdc_server, service_principal, password, service_principal, new_password, ads->auth.time_offset);
+
+ if (!ADS_ERR_OK(ret)) goto failed;
+
+ if (!secrets_store_machine_password(new_password, lp_workgroup(), sec_channel_type)) {
+ DEBUG(1,("Failed to save machine password\n"));
+ return ADS_ERROR_SYSTEM(EACCES);
+ }
+
+failed:
+ SAFE_FREE(service_principal);
+ SAFE_FREE(new_password);
+
+ return ret;
+}
+
+
+
+#endif
diff --git a/source/libsmb/.cvsignore b/source/libsmb/.cvsignore
new file mode 100644
index 00000000000..07da2225c72
--- /dev/null
+++ b/source/libsmb/.cvsignore
@@ -0,0 +1,3 @@
+*.po
+*.po32
+
diff --git a/source/libsmb/asn1.c b/source/libsmb/asn1.c
new file mode 100644
index 00000000000..ecc5e3dee64
--- /dev/null
+++ b/source/libsmb/asn1.c
@@ -0,0 +1,441 @@
+/*
+ Unix SMB/CIFS implementation.
+ simple SPNEGO 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 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"
+
+/* free an asn1 structure */
+void asn1_free(ASN1_DATA *data)
+{
+ SAFE_FREE(data->data);
+}
+
+/* write to the ASN1 buffer, advancing the buffer pointer */
+BOOL asn1_write(ASN1_DATA *data, const void *p, int len)
+{
+ if (data->has_error) return False;
+ if (data->length < data->ofs+len) {
+ uint8 *newp;
+ newp = Realloc(data->data, data->ofs+len);
+ if (!newp) {
+ SAFE_FREE(data->data);
+ data->has_error = True;
+ return False;
+ }
+ data->data = newp;
+ data->length = data->ofs+len;
+ }
+ memcpy(data->data + data->ofs, p, len);
+ data->ofs += len;
+ return True;
+}
+
+/* useful fn for writing a uint8 */
+BOOL asn1_write_uint8(ASN1_DATA *data, uint8 v)
+{
+ return asn1_write(data, &v, 1);
+}
+
+/* push a tag onto the asn1 data buffer. Used for nested structures */
+BOOL asn1_push_tag(ASN1_DATA *data, uint8 tag)
+{
+ struct nesting *nesting;
+
+ asn1_write_uint8(data, tag);
+ nesting = (struct nesting *)malloc(sizeof(struct nesting));
+ if (!nesting) {
+ data->has_error = True;
+ return False;
+ }
+
+ nesting->start = data->ofs;
+ nesting->next = data->nesting;
+ data->nesting = nesting;
+ return asn1_write_uint8(data, 0xff);
+}
+
+/* pop a tag */
+BOOL asn1_pop_tag(ASN1_DATA *data)
+{
+ struct nesting *nesting;
+ size_t len;
+
+ nesting = data->nesting;
+
+ if (!nesting) {
+ data->has_error = True;
+ return False;
+ }
+ len = data->ofs - (nesting->start+1);
+ /* yes, this is ugly. We don't know in advance how many bytes the length
+ of a tag will take, so we assumed 1 byte. If we were wrong then we
+ need to correct our mistake */
+ if (len > 255) {
+ data->data[nesting->start] = 0x82;
+ if (!asn1_write_uint8(data, 0)) return False;
+ if (!asn1_write_uint8(data, 0)) return False;
+ memmove(data->data+nesting->start+3, data->data+nesting->start+1, len);
+ data->data[nesting->start+1] = len>>8;
+ data->data[nesting->start+2] = len&0xff;
+ } else if (len > 127) {
+ data->data[nesting->start] = 0x81;
+ if (!asn1_write_uint8(data, 0)) return False;
+ memmove(data->data+nesting->start+2, data->data+nesting->start+1, len);
+ data->data[nesting->start+1] = len;
+ } else {
+ data->data[nesting->start] = len;
+ }
+
+ data->nesting = nesting->next;
+ free(nesting);
+ return True;
+}
+
+
+/* write an integer */
+BOOL asn1_write_Integer(ASN1_DATA *data, int i)
+{
+ if (!asn1_push_tag(data, ASN1_INTEGER)) return False;
+ do {
+ asn1_write_uint8(data, i);
+ i = i >> 8;
+ } while (i);
+ return asn1_pop_tag(data);
+}
+
+/* write an object ID to a ASN1 buffer */
+BOOL asn1_write_OID(ASN1_DATA *data, const char *OID)
+{
+ unsigned v, v2;
+ const char *p = (const char *)OID;
+ char *newp;
+
+ if (!asn1_push_tag(data, ASN1_OID))
+ return False;
+ v = strtol(p, &newp, 10);
+ p = newp;
+ v2 = strtol(p, &newp, 10);
+ p = newp;
+ if (!asn1_write_uint8(data, 40*v + v2))
+ return False;
+
+ while (*p) {
+ v = strtol(p, &newp, 10);
+ p = newp;
+ if (v >= (1<<28)) asn1_write_uint8(data, 0x80 | ((v>>28)&0xff));
+ if (v >= (1<<21)) asn1_write_uint8(data, 0x80 | ((v>>21)&0xff));
+ if (v >= (1<<14)) asn1_write_uint8(data, 0x80 | ((v>>14)&0xff));
+ if (v >= (1<<7)) asn1_write_uint8(data, 0x80 | ((v>>7)&0xff));
+ if (!asn1_write_uint8(data, v&0x7f))
+ return False;
+ }
+ return asn1_pop_tag(data);
+}
+
+/* write an octet string */
+BOOL asn1_write_OctetString(ASN1_DATA *data, const void *p, size_t length)
+{
+ asn1_push_tag(data, ASN1_OCTET_STRING);
+ asn1_write(data, p, length);
+ asn1_pop_tag(data);
+ return !data->has_error;
+}
+
+/* write a general string */
+BOOL asn1_write_GeneralString(ASN1_DATA *data, const char *s)
+{
+ asn1_push_tag(data, ASN1_GENERAL_STRING);
+ asn1_write(data, s, strlen(s));
+ asn1_pop_tag(data);
+ return !data->has_error;
+}
+
+/* write a BOOLEAN */
+BOOL asn1_write_BOOLEAN(ASN1_DATA *data, BOOL v)
+{
+ asn1_write_uint8(data, ASN1_BOOLEAN);
+ asn1_write_uint8(data, v);
+ return !data->has_error;
+}
+
+/* write a BOOLEAN - hmm, I suspect this one is the correct one, and the
+ above boolean is bogus. Need to check */
+BOOL asn1_write_BOOLEAN2(ASN1_DATA *data, BOOL v)
+{
+ asn1_push_tag(data, ASN1_BOOLEAN);
+ asn1_write_uint8(data, v);
+ asn1_pop_tag(data);
+ return !data->has_error;
+}
+
+/* check a BOOLEAN */
+BOOL asn1_check_BOOLEAN(ASN1_DATA *data, BOOL v)
+{
+ uint8 b = 0;
+
+ asn1_read_uint8(data, &b);
+ if (b != ASN1_BOOLEAN) {
+ data->has_error = True;
+ return False;
+ }
+ asn1_read_uint8(data, &b);
+ if (b != v) {
+ data->has_error = True;
+ return False;
+ }
+ return !data->has_error;
+}
+
+
+/* load a ASN1_DATA structure with a lump of data, ready to be parsed */
+BOOL asn1_load(ASN1_DATA *data, DATA_BLOB blob)
+{
+ ZERO_STRUCTP(data);
+ data->data = memdup(blob.data, blob.length);
+ if (!data->data) {
+ data->has_error = True;
+ return False;
+ }
+ data->length = blob.length;
+ return True;
+}
+
+/* read from a ASN1 buffer, advancing the buffer pointer */
+BOOL asn1_read(ASN1_DATA *data, void *p, int len)
+{
+ if (len < 0 || data->ofs + len < data->ofs || data->ofs + len < len) {
+ data->has_error = True;
+ return False;
+ }
+
+ if (data->ofs + len > data->length) {
+ data->has_error = True;
+ return False;
+ }
+ memcpy(p, data->data + data->ofs, len);
+ data->ofs += len;
+ return True;
+}
+
+/* read a uint8 from a ASN1 buffer */
+BOOL asn1_read_uint8(ASN1_DATA *data, uint8 *v)
+{
+ return asn1_read(data, v, 1);
+}
+
+/* start reading a nested asn1 structure */
+BOOL asn1_start_tag(ASN1_DATA *data, uint8 tag)
+{
+ uint8 b;
+ struct nesting *nesting;
+
+ if (!asn1_read_uint8(data, &b))
+ return False;
+
+ if (b != tag) {
+ data->has_error = True;
+ return False;
+ }
+ nesting = (struct nesting *)malloc(sizeof(struct nesting));
+ if (!nesting) {
+ data->has_error = True;
+ return False;
+ }
+
+ if (!asn1_read_uint8(data, &b)) {
+ return False;
+ }
+
+ if (b & 0x80) {
+ int n = b & 0x7f;
+ if (!asn1_read_uint8(data, &b))
+ return False;
+ nesting->taglen = b;
+ while (n > 1) {
+ if (!asn1_read_uint8(data, &b))
+ return False;
+ nesting->taglen = (nesting->taglen << 8) | b;
+ n--;
+ }
+ } else {
+ nesting->taglen = b;
+ }
+ nesting->start = data->ofs;
+ nesting->next = data->nesting;
+ data->nesting = nesting;
+ return !data->has_error;
+}
+
+
+/* stop reading a tag */
+BOOL asn1_end_tag(ASN1_DATA *data)
+{
+ struct nesting *nesting;
+
+ /* make sure we read it all */
+ if (asn1_tag_remaining(data) != 0) {
+ data->has_error = True;
+ return False;
+ }
+
+ nesting = data->nesting;
+
+ if (!nesting) {
+ data->has_error = True;
+ return False;
+ }
+
+ data->nesting = nesting->next;
+ free(nesting);
+ return True;
+}
+
+/* work out how many bytes are left in this nested tag */
+int asn1_tag_remaining(ASN1_DATA *data)
+{
+ if (!data->nesting) {
+ data->has_error = True;
+ return -1;
+ }
+ return data->nesting->taglen - (data->ofs - data->nesting->start);
+}
+
+/* read an object ID from a ASN1 buffer */
+BOOL asn1_read_OID(ASN1_DATA *data, char **OID)
+{
+ uint8 b;
+ pstring oid;
+ fstring el;
+
+ if (!asn1_start_tag(data, ASN1_OID)) return False;
+ asn1_read_uint8(data, &b);
+
+ oid[0] = 0;
+ fstr_sprintf(el, "%u", b/40);
+ pstrcat(oid, el);
+ fstr_sprintf(el, " %u", b%40);
+ pstrcat(oid, el);
+
+ while (asn1_tag_remaining(data) > 0) {
+ unsigned v = 0;
+ do {
+ asn1_read_uint8(data, &b);
+ v = (v<<7) | (b&0x7f);
+ } while (!data->has_error && b & 0x80);
+ fstr_sprintf(el, " %u", v);
+ pstrcat(oid, el);
+ }
+
+ asn1_end_tag(data);
+
+ *OID = strdup(oid);
+
+ return !data->has_error;
+}
+
+/* check that the next object ID is correct */
+BOOL asn1_check_OID(ASN1_DATA *data, const char *OID)
+{
+ char *id;
+
+ if (!asn1_read_OID(data, &id)) return False;
+
+ if (strcmp(id, OID) != 0) {
+ data->has_error = True;
+ return False;
+ }
+ free(id);
+ return True;
+}
+
+/* read a GeneralString from a ASN1 buffer */
+BOOL asn1_read_GeneralString(ASN1_DATA *data, char **s)
+{
+ int len;
+ if (!asn1_start_tag(data, ASN1_GENERAL_STRING)) return False;
+ len = asn1_tag_remaining(data);
+ if (len < 0) {
+ data->has_error = True;
+ return False;
+ }
+ *s = malloc(len+1);
+ if (! *s) {
+ data->has_error = True;
+ return False;
+ }
+ asn1_read(data, *s, len);
+ (*s)[len] = 0;
+ asn1_end_tag(data);
+ return !data->has_error;
+}
+
+/* read a octet string blob */
+BOOL asn1_read_OctetString(ASN1_DATA *data, DATA_BLOB *blob)
+{
+ int len;
+ ZERO_STRUCTP(blob);
+ if (!asn1_start_tag(data, ASN1_OCTET_STRING)) return False;
+ len = asn1_tag_remaining(data);
+ if (len < 0) {
+ data->has_error = True;
+ return False;
+ }
+ *blob = data_blob(NULL, len);
+ asn1_read(data, blob->data, len);
+ asn1_end_tag(data);
+ return !data->has_error;
+}
+
+/* read an interger */
+BOOL asn1_read_Integer(ASN1_DATA *data, int *i)
+{
+ uint8 b;
+ *i = 0;
+
+ if (!asn1_start_tag(data, ASN1_INTEGER)) return False;
+ while (asn1_tag_remaining(data)>0) {
+ asn1_read_uint8(data, &b);
+ *i = (*i << 8) + b;
+ }
+ return asn1_end_tag(data);
+
+}
+
+/* check a enumarted value is correct */
+BOOL asn1_check_enumerated(ASN1_DATA *data, int v)
+{
+ uint8 b;
+ if (!asn1_start_tag(data, ASN1_ENUMERATED)) return False;
+ asn1_read_uint8(data, &b);
+ asn1_end_tag(data);
+
+ if (v != b)
+ data->has_error = False;
+
+ return !data->has_error;
+}
+
+/* write an enumarted value to the stream */
+BOOL asn1_write_enumerated(ASN1_DATA *data, uint8 v)
+{
+ if (!asn1_push_tag(data, ASN1_ENUMERATED)) return False;
+ asn1_write_uint8(data, v);
+ asn1_pop_tag(data);
+ return !data->has_error;
+}
diff --git a/source/libsmb/cliconnect.c b/source/libsmb/cliconnect.c
new file mode 100644
index 00000000000..c39044e10af
--- /dev/null
+++ b/source/libsmb/cliconnect.c
@@ -0,0 +1,1683 @@
+/*
+ Unix SMB/CIFS implementation.
+ client connect/disconnect routines
+ Copyright (C) Andrew Tridgell 1994-1998
+ Copyright (C) Andrew Bartlett 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 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.
+*/
+
+#define NO_SYSLOG
+
+#include "includes.h"
+
+
+static const struct {
+ int 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_LANMAN2,"LM1.2X002"},
+ {PROTOCOL_LANMAN2,"DOS LANMAN2.1"},
+ {PROTOCOL_LANMAN2,"Samba"},
+ {PROTOCOL_NT1,"NT LANMAN 1.0"},
+ {PROTOCOL_NT1,"NT LM 0.12"},
+ {-1,NULL}
+};
+
+/****************************************************************************
+ Do an old lanman2 style session setup.
+****************************************************************************/
+
+static BOOL cli_session_setup_lanman2(struct cli_state *cli, const char *user,
+ const char *pass, size_t passlen, const char *workgroup)
+{
+ fstring pword;
+ char *p;
+
+ if (passlen > sizeof(pword)-1)
+ return False;
+
+ /* LANMAN servers predate NT status codes and Unicode and ignore those
+ smb flags so we must disable the corresponding default capabilities
+ that would otherwise cause the Unicode and NT Status flags to be
+ set (and even returned by the server) */
+
+ cli->capabilities &= ~(CAP_UNICODE | CAP_STATUS32);
+
+ /* if in share level security then don't send a password now */
+ if (!(cli->sec_mode & NEGOTIATE_SECURITY_USER_LEVEL))
+ passlen = 0;
+
+ if (passlen > 0 && (cli->sec_mode & NEGOTIATE_SECURITY_CHALLENGE_RESPONSE) && passlen != 24) {
+ /* Encrypted mode needed, and non encrypted password supplied. */
+ passlen = 24;
+ SMBencrypt(pass,cli->secblob.data,(uchar *)pword);
+ } else if ((cli->sec_mode & NEGOTIATE_SECURITY_CHALLENGE_RESPONSE) && passlen == 24) {
+ /* Encrypted mode needed, and encrypted password supplied. */
+ memcpy(pword, pass, passlen);
+ } else if (passlen > 0) {
+ /* Plaintext mode needed, assume plaintext supplied. */
+ passlen = clistr_push(cli, pword, pass, sizeof(pword), STR_TERMINATE);
+ }
+
+ /* send a session setup command */
+ memset(cli->outbuf,'\0',smb_size);
+ set_message(cli->outbuf,10, 0, True);
+ SCVAL(cli->outbuf,smb_com,SMBsesssetupX);
+ cli_setup_packet(cli);
+
+ SCVAL(cli->outbuf,smb_vwv0,0xFF);
+ SSVAL(cli->outbuf,smb_vwv2,cli->max_xmit);
+ SSVAL(cli->outbuf,smb_vwv3,2);
+ SSVAL(cli->outbuf,smb_vwv4,1);
+ SIVAL(cli->outbuf,smb_vwv5,cli->sesskey);
+ SSVAL(cli->outbuf,smb_vwv7,passlen);
+
+ p = smb_buf(cli->outbuf);
+ memcpy(p,pword,passlen);
+ p += passlen;
+ p += clistr_push(cli, p, user, -1, STR_TERMINATE|STR_UPPER);
+ p += clistr_push(cli, p, workgroup, -1, STR_TERMINATE|STR_UPPER);
+ p += clistr_push(cli, p, "Unix", -1, STR_TERMINATE);
+ p += clistr_push(cli, p, "Samba", -1, STR_TERMINATE);
+ cli_setup_bcc(cli, p);
+
+ cli_send_smb(cli);
+ if (!cli_receive_smb(cli))
+ return False;
+
+ show_msg(cli->inbuf);
+
+ if (cli_is_error(cli))
+ return False;
+
+ /* use the returned vuid from now on */
+ cli->vuid = SVAL(cli->inbuf,smb_uid);
+ fstrcpy(cli->user_name, user);
+
+ return True;
+}
+
+/****************************************************************************
+ Work out suitable capabilities to offer the server.
+****************************************************************************/
+
+static uint32 cli_session_setup_capabilities(struct cli_state *cli)
+{
+ uint32 capabilities = CAP_NT_SMBS;
+
+ if (!cli->force_dos_errors)
+ capabilities |= CAP_STATUS32;
+
+ if (cli->use_level_II_oplocks)
+ capabilities |= CAP_LEVEL_II_OPLOCKS;
+
+ if (cli->capabilities & CAP_UNICODE)
+ capabilities |= CAP_UNICODE;
+
+ if (cli->capabilities & CAP_LARGE_FILES)
+ capabilities |= CAP_LARGE_FILES;
+
+ return capabilities;
+}
+
+/****************************************************************************
+ Do a NT1 guest session setup.
+****************************************************************************/
+
+static BOOL cli_session_setup_guest(struct cli_state *cli)
+{
+ char *p;
+ uint32 capabilities = cli_session_setup_capabilities(cli);
+
+ set_message(cli->outbuf,13,0,True);
+ SCVAL(cli->outbuf,smb_com,SMBsesssetupX);
+ cli_setup_packet(cli);
+
+ SCVAL(cli->outbuf,smb_vwv0,0xFF);
+ SSVAL(cli->outbuf,smb_vwv2,CLI_BUFFER_SIZE);
+ SSVAL(cli->outbuf,smb_vwv3,2);
+ SSVAL(cli->outbuf,smb_vwv4,cli->pid);
+ SIVAL(cli->outbuf,smb_vwv5,cli->sesskey);
+ SSVAL(cli->outbuf,smb_vwv7,0);
+ SSVAL(cli->outbuf,smb_vwv8,0);
+ SIVAL(cli->outbuf,smb_vwv11,capabilities);
+ p = smb_buf(cli->outbuf);
+ p += clistr_push(cli, p, "", -1, STR_TERMINATE); /* username */
+ p += clistr_push(cli, p, "", -1, STR_TERMINATE); /* workgroup */
+ p += clistr_push(cli, p, "Unix", -1, STR_TERMINATE);
+ p += clistr_push(cli, p, "Samba", -1, STR_TERMINATE);
+ cli_setup_bcc(cli, p);
+
+ cli_send_smb(cli);
+ if (!cli_receive_smb(cli))
+ return False;
+
+ show_msg(cli->inbuf);
+
+ if (cli_is_error(cli))
+ return False;
+
+ cli->vuid = SVAL(cli->inbuf,smb_uid);
+
+ p = smb_buf(cli->inbuf);
+ p += clistr_pull(cli, cli->server_os, p, sizeof(fstring), -1, STR_TERMINATE);
+ p += clistr_pull(cli, cli->server_type, p, sizeof(fstring), -1, STR_TERMINATE);
+ p += clistr_pull(cli, cli->server_domain, p, sizeof(fstring), -1, STR_TERMINATE);
+
+ fstrcpy(cli->user_name, "");
+
+ return True;
+}
+
+/****************************************************************************
+ Do a NT1 plaintext session setup.
+****************************************************************************/
+
+static BOOL cli_session_setup_plaintext(struct cli_state *cli, const char *user,
+ const char *pass, const char *workgroup)
+{
+ uint32 capabilities = cli_session_setup_capabilities(cli);
+ char *p;
+ fstring lanman;
+
+ fstr_sprintf( lanman, "Samba %s", SAMBA_VERSION_STRING);
+
+ set_message(cli->outbuf,13,0,True);
+ SCVAL(cli->outbuf,smb_com,SMBsesssetupX);
+ cli_setup_packet(cli);
+
+ SCVAL(cli->outbuf,smb_vwv0,0xFF);
+ SSVAL(cli->outbuf,smb_vwv2,CLI_BUFFER_SIZE);
+ SSVAL(cli->outbuf,smb_vwv3,2);
+ SSVAL(cli->outbuf,smb_vwv4,cli->pid);
+ SIVAL(cli->outbuf,smb_vwv5,cli->sesskey);
+ SSVAL(cli->outbuf,smb_vwv8,0);
+ SIVAL(cli->outbuf,smb_vwv11,capabilities);
+ p = smb_buf(cli->outbuf);
+
+ /* check wether to send the ASCII or UNICODE version of the password */
+
+ if ( (capabilities & CAP_UNICODE) == 0 ) {
+ p += clistr_push(cli, p, pass, -1, STR_TERMINATE); /* password */
+ SSVAL(cli->outbuf,smb_vwv7,PTR_DIFF(p, smb_buf(cli->outbuf)));
+ }
+ else {
+ p += clistr_push(cli, p, pass, -1, STR_UNICODE|STR_TERMINATE); /* unicode password */
+ SSVAL(cli->outbuf,smb_vwv8,PTR_DIFF(p, smb_buf(cli->outbuf)));
+ }
+
+ p += clistr_push(cli, p, user, -1, STR_TERMINATE); /* username */
+ p += clistr_push(cli, p, workgroup, -1, STR_TERMINATE); /* workgroup */
+ p += clistr_push(cli, p, "Unix", -1, STR_TERMINATE);
+ p += clistr_push(cli, p, lanman, -1, STR_TERMINATE);
+ cli_setup_bcc(cli, p);
+
+ cli_send_smb(cli);
+ if (!cli_receive_smb(cli))
+ return False;
+
+ show_msg(cli->inbuf);
+
+ if (cli_is_error(cli))
+ return False;
+
+ cli->vuid = SVAL(cli->inbuf,smb_uid);
+ p = smb_buf(cli->inbuf);
+ p += clistr_pull(cli, cli->server_os, p, sizeof(fstring), -1, STR_TERMINATE);
+ p += clistr_pull(cli, cli->server_type, p, sizeof(fstring), -1, STR_TERMINATE);
+ p += clistr_pull(cli, cli->server_domain, p, sizeof(fstring), -1, STR_TERMINATE);
+ fstrcpy(cli->user_name, user);
+
+ return True;
+}
+
+/**
+ * Set the user session key for a connection
+ * @param cli The cli structure to add it too
+ * @param session_key The session key used. (A copy of this is taken for the cli struct)
+ *
+ */
+
+static void cli_set_session_key (struct cli_state *cli, const DATA_BLOB session_key)
+{
+ cli->user_session_key = data_blob(session_key.data, session_key.length);
+}
+
+/****************************************************************************
+ do a NT1 NTLM/LM encrypted session setup - for when extended security
+ is not negotiated.
+ @param cli client state to create do session setup on
+ @param user username
+ @param pass *either* cleartext password (passlen !=24) or LM response.
+ @param ntpass NT response, implies ntpasslen >=24, implies pass is not clear
+ @param workgroup The user's domain.
+****************************************************************************/
+
+static BOOL cli_session_setup_nt1(struct cli_state *cli, const char *user,
+ const char *pass, size_t passlen,
+ const char *ntpass, size_t ntpasslen,
+ const char *workgroup)
+{
+ uint32 capabilities = cli_session_setup_capabilities(cli);
+ DATA_BLOB lm_response = data_blob(NULL, 0);
+ DATA_BLOB nt_response = data_blob(NULL, 0);
+ DATA_BLOB session_key = data_blob(NULL, 0);
+ BOOL ret = False;
+ char *p;
+
+ if (passlen == 0) {
+ /* do nothing - guest login */
+ } else if (passlen != 24) {
+ if (lp_client_ntlmv2_auth()) {
+ DATA_BLOB server_chal;
+ DATA_BLOB names_blob;
+ server_chal = data_blob(cli->secblob.data, MIN(cli->secblob.length, 8));
+
+ /* note that the 'workgroup' here is a best guess - we don't know
+ the server's domain at this point. The 'server name' is also
+ dodgy...
+ */
+ names_blob = NTLMv2_generate_names_blob(cli->called.name, workgroup);
+
+ if (!SMBNTLMv2encrypt(user, workgroup, pass, &server_chal,
+ &names_blob,
+ &lm_response, &nt_response, &session_key)) {
+ data_blob_free(&names_blob);
+ data_blob_free(&server_chal);
+ return False;
+ }
+ data_blob_free(&names_blob);
+ data_blob_free(&server_chal);
+
+ } else {
+ uchar nt_hash[16];
+ E_md4hash(pass, nt_hash);
+
+ nt_response = data_blob(NULL, 24);
+ SMBNTencrypt(pass,cli->secblob.data,nt_response.data);
+
+ /* non encrypted password supplied. Ignore ntpass. */
+ if (lp_client_lanman_auth()) {
+ lm_response = data_blob(NULL, 24);
+ SMBencrypt(pass,cli->secblob.data, lm_response.data);
+ } else {
+ /* LM disabled, place NT# in LM field instead */
+ lm_response = data_blob(nt_response.data, nt_response.length);
+ }
+
+ session_key = data_blob(NULL, 16);
+ SMBsesskeygen_ntv1(nt_hash, NULL, session_key.data);
+ }
+ cli_simple_set_signing(cli, session_key, nt_response);
+ } else {
+ /* pre-encrypted password supplied. Only used for
+ security=server, can't do
+ signing because we don't have original key */
+
+ lm_response = data_blob(pass, passlen);
+ nt_response = data_blob(ntpass, ntpasslen);
+ }
+
+ /* send a session setup command */
+ memset(cli->outbuf,'\0',smb_size);
+
+ set_message(cli->outbuf,13,0,True);
+ SCVAL(cli->outbuf,smb_com,SMBsesssetupX);
+ cli_setup_packet(cli);
+
+ SCVAL(cli->outbuf,smb_vwv0,0xFF);
+ SSVAL(cli->outbuf,smb_vwv2,CLI_BUFFER_SIZE);
+ SSVAL(cli->outbuf,smb_vwv3,2);
+ SSVAL(cli->outbuf,smb_vwv4,cli->pid);
+ SIVAL(cli->outbuf,smb_vwv5,cli->sesskey);
+ SSVAL(cli->outbuf,smb_vwv7,lm_response.length);
+ SSVAL(cli->outbuf,smb_vwv8,nt_response.length);
+ SIVAL(cli->outbuf,smb_vwv11,capabilities);
+ p = smb_buf(cli->outbuf);
+ if (lm_response.length) {
+ memcpy(p,lm_response.data, lm_response.length); p += lm_response.length;
+ }
+ if (nt_response.length) {
+ memcpy(p,nt_response.data, nt_response.length); p += nt_response.length;
+ }
+ p += clistr_push(cli, p, user, -1, STR_TERMINATE);
+ p += clistr_push(cli, p, workgroup, -1, STR_TERMINATE);
+ p += clistr_push(cli, p, "Unix", -1, STR_TERMINATE);
+ p += clistr_push(cli, p, "Samba", -1, STR_TERMINATE);
+ cli_setup_bcc(cli, p);
+
+ if (!cli_send_smb(cli) || !cli_receive_smb(cli)) {
+ ret = False;
+ goto end;
+ }
+
+ /* show_msg(cli->inbuf); */
+
+ if (cli_is_error(cli)) {
+ ret = False;
+ goto end;
+ }
+
+ /* use the returned vuid from now on */
+ cli->vuid = SVAL(cli->inbuf,smb_uid);
+
+ p = smb_buf(cli->inbuf);
+ p += clistr_pull(cli, cli->server_os, p, sizeof(fstring), -1, STR_TERMINATE);
+ p += clistr_pull(cli, cli->server_type, p, sizeof(fstring), -1, STR_TERMINATE);
+ p += clistr_pull(cli, cli->server_domain, p, sizeof(fstring), -1, STR_TERMINATE);
+
+ fstrcpy(cli->user_name, user);
+
+ if (session_key.data) {
+ /* Have plaintext orginal */
+ cli_set_session_key(cli, session_key);
+ }
+
+ ret = True;
+end:
+ data_blob_free(&lm_response);
+ data_blob_free(&nt_response);
+
+ if (!ret)
+ data_blob_free(&session_key);
+ return ret;
+}
+
+/****************************************************************************
+ Send a extended security session setup blob
+****************************************************************************/
+
+static BOOL cli_session_setup_blob_send(struct cli_state *cli, DATA_BLOB blob)
+{
+ uint32 capabilities = cli_session_setup_capabilities(cli);
+ char *p;
+
+ capabilities |= CAP_EXTENDED_SECURITY;
+
+ /* send a session setup command */
+ memset(cli->outbuf,'\0',smb_size);
+
+ set_message(cli->outbuf,12,0,True);
+ SCVAL(cli->outbuf,smb_com,SMBsesssetupX);
+
+ cli_setup_packet(cli);
+
+ SCVAL(cli->outbuf,smb_vwv0,0xFF);
+ SSVAL(cli->outbuf,smb_vwv2,CLI_BUFFER_SIZE);
+ SSVAL(cli->outbuf,smb_vwv3,2);
+ SSVAL(cli->outbuf,smb_vwv4,1);
+ SIVAL(cli->outbuf,smb_vwv5,0);
+ SSVAL(cli->outbuf,smb_vwv7,blob.length);
+ SIVAL(cli->outbuf,smb_vwv10,capabilities);
+ p = smb_buf(cli->outbuf);
+ memcpy(p, blob.data, blob.length);
+ p += blob.length;
+ p += clistr_push(cli, p, "Unix", -1, STR_TERMINATE);
+ p += clistr_push(cli, p, "Samba", -1, STR_TERMINATE);
+ cli_setup_bcc(cli, p);
+ return cli_send_smb(cli);
+}
+
+/****************************************************************************
+ Send a extended security session setup blob, returning a reply blob.
+****************************************************************************/
+
+static DATA_BLOB cli_session_setup_blob_receive(struct cli_state *cli)
+{
+ DATA_BLOB blob2 = data_blob(NULL, 0);
+ char *p;
+ size_t len;
+
+ if (!cli_receive_smb(cli))
+ return blob2;
+
+ show_msg(cli->inbuf);
+
+ if (cli_is_error(cli) && !NT_STATUS_EQUAL(cli_nt_error(cli),
+ NT_STATUS_MORE_PROCESSING_REQUIRED)) {
+ return blob2;
+ }
+
+ /* use the returned vuid from now on */
+ cli->vuid = SVAL(cli->inbuf,smb_uid);
+
+ p = smb_buf(cli->inbuf);
+
+ blob2 = data_blob(p, SVAL(cli->inbuf, smb_vwv3));
+
+ p += blob2.length;
+ p += clistr_pull(cli, cli->server_os, p, sizeof(fstring), -1, STR_TERMINATE);
+
+ /* w2k with kerberos doesn't properly null terminate this field */
+ len = smb_buflen(cli->inbuf) - PTR_DIFF(p, smb_buf(cli->inbuf));
+ p += clistr_pull(cli, cli->server_type, p, sizeof(fstring), len, 0);
+
+ return blob2;
+}
+
+#ifdef HAVE_KRB5
+
+/****************************************************************************
+ Send a extended security session setup blob, returning a reply blob.
+****************************************************************************/
+
+static DATA_BLOB cli_session_setup_blob(struct cli_state *cli, DATA_BLOB blob)
+{
+ DATA_BLOB blob2 = data_blob(NULL, 0);
+ if (!cli_session_setup_blob_send(cli, blob)) {
+ return blob2;
+ }
+
+ return cli_session_setup_blob_receive(cli);
+}
+
+/****************************************************************************
+ Use in-memory credentials cache
+****************************************************************************/
+
+static void use_in_memory_ccache(void) {
+ setenv(KRB5_ENV_CCNAME, "MEMORY:cliconnect", 1);
+}
+
+/****************************************************************************
+ Do a spnego/kerberos encrypted session setup.
+****************************************************************************/
+
+static ADS_STATUS cli_session_setup_kerberos(struct cli_state *cli, const char *principal, const char *workgroup)
+{
+ DATA_BLOB blob2, negTokenTarg;
+ DATA_BLOB session_key_krb5;
+ DATA_BLOB null_blob = data_blob(NULL, 0);
+ int rc;
+
+ DEBUG(2,("Doing kerberos session setup\n"));
+
+ /* generate the encapsulated kerberos5 ticket */
+ rc = spnego_gen_negTokenTarg(principal, 0, &negTokenTarg, &session_key_krb5);
+
+ if (rc) {
+ DEBUG(1, ("spnego_gen_negTokenTarg failed: %s\n", error_message(rc)));
+ return ADS_ERROR_KRB5(rc);
+ }
+
+#if 0
+ file_save("negTokenTarg.dat", negTokenTarg.data, negTokenTarg.length);
+#endif
+
+ cli_simple_set_signing(cli, session_key_krb5, null_blob);
+
+ blob2 = cli_session_setup_blob(cli, negTokenTarg);
+
+ /* we don't need this blob for kerberos */
+ data_blob_free(&blob2);
+
+ cli_set_session_key(cli, session_key_krb5);
+
+ data_blob_free(&negTokenTarg);
+
+ if (cli_is_error(cli)) {
+ if (NT_STATUS_IS_OK(cli_nt_error(cli))) {
+ return ADS_ERROR_NT(NT_STATUS_UNSUCCESSFUL);
+ }
+ }
+ return ADS_ERROR_NT(cli_nt_error(cli));
+}
+#endif /* HAVE_KRB5 */
+
+
+/****************************************************************************
+ Do a spnego/NTLMSSP encrypted session setup.
+****************************************************************************/
+
+static NTSTATUS cli_session_setup_ntlmssp(struct cli_state *cli, const char *user,
+ const char *pass, const char *domain)
+{
+ struct ntlmssp_state *ntlmssp_state;
+ NTSTATUS nt_status;
+ int turn = 1;
+ DATA_BLOB msg1;
+ DATA_BLOB blob;
+ DATA_BLOB blob_in = data_blob(NULL, 0);
+ DATA_BLOB blob_out;
+
+ cli_temp_set_signing(cli);
+
+ if (!NT_STATUS_IS_OK(nt_status = ntlmssp_client_start(&ntlmssp_state))) {
+ return nt_status;
+ }
+
+ if (!NT_STATUS_IS_OK(nt_status = ntlmssp_set_username(ntlmssp_state, user))) {
+ return nt_status;
+ }
+ if (!NT_STATUS_IS_OK(nt_status = ntlmssp_set_domain(ntlmssp_state, domain))) {
+ return nt_status;
+ }
+ if (!NT_STATUS_IS_OK(nt_status = ntlmssp_set_password(ntlmssp_state, pass))) {
+ return nt_status;
+ }
+
+ do {
+ nt_status = ntlmssp_update(ntlmssp_state,
+ blob_in, &blob_out);
+ data_blob_free(&blob_in);
+ if (NT_STATUS_EQUAL(nt_status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
+ if (turn == 1) {
+ /* and wrap it in a SPNEGO wrapper */
+ msg1 = gen_negTokenInit(OID_NTLMSSP, blob_out);
+ } else {
+ /* wrap it in SPNEGO */
+ msg1 = spnego_gen_auth(blob_out);
+ }
+
+ /* now send that blob on its way */
+ if (!cli_session_setup_blob_send(cli, msg1)) {
+ DEBUG(3, ("Failed to send NTLMSSP/SPNEGO blob to server!\n"));
+ nt_status = NT_STATUS_UNSUCCESSFUL;
+ } else {
+ data_blob_free(&msg1);
+
+ blob = cli_session_setup_blob_receive(cli);
+
+ nt_status = cli_nt_error(cli);
+ if (cli_is_error(cli) && NT_STATUS_IS_OK(nt_status)) {
+ if (cli->smb_rw_error == READ_BAD_SIG) {
+ nt_status = NT_STATUS_ACCESS_DENIED;
+ } else {
+ nt_status = NT_STATUS_UNSUCCESSFUL;
+ }
+ }
+ }
+ }
+
+ if (!blob.length) {
+ if (NT_STATUS_IS_OK(nt_status)) {
+ nt_status = NT_STATUS_UNSUCCESSFUL;
+ }
+ } else if ((turn == 1) &&
+ NT_STATUS_EQUAL(nt_status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
+ DATA_BLOB tmp_blob = data_blob(NULL, 0);
+ /* the server might give us back two challenges */
+ if (!spnego_parse_challenge(blob, &blob_in,
+ &tmp_blob)) {
+ DEBUG(3,("Failed to parse challenges\n"));
+ nt_status = NT_STATUS_INVALID_PARAMETER;
+ }
+ data_blob_free(&tmp_blob);
+ } else {
+ if (!spnego_parse_auth_response(blob, nt_status,
+ &blob_in)) {
+ DEBUG(3,("Failed to parse auth response\n"));
+ if (NT_STATUS_IS_OK(nt_status)
+ || NT_STATUS_EQUAL(nt_status, NT_STATUS_MORE_PROCESSING_REQUIRED))
+ nt_status = NT_STATUS_INVALID_PARAMETER;
+ }
+ }
+ data_blob_free(&blob);
+ data_blob_free(&blob_out);
+ turn++;
+ } while (NT_STATUS_EQUAL(nt_status, NT_STATUS_MORE_PROCESSING_REQUIRED));
+
+ if (NT_STATUS_IS_OK(nt_status)) {
+
+ DATA_BLOB key = data_blob(ntlmssp_state->session_key.data,
+ ntlmssp_state->session_key.length);
+ DATA_BLOB null_blob = data_blob(NULL, 0);
+
+ fstrcpy(cli->server_domain, ntlmssp_state->server_domain);
+ cli_set_session_key(cli, ntlmssp_state->session_key);
+
+ if (cli_simple_set_signing(cli, key, null_blob)) {
+
+ /* 'resign' the last message, so we get the right sequence numbers
+ for checking the first reply from the server */
+ cli_calculate_sign_mac(cli);
+
+ if (!cli_check_sign_mac(cli, True)) {
+ nt_status = NT_STATUS_ACCESS_DENIED;
+ }
+ }
+ }
+
+ /* we have a reference conter on ntlmssp_state, if we are signing
+ then the state will be kept by the signing engine */
+
+ ntlmssp_end(&ntlmssp_state);
+
+ return nt_status;
+}
+
+/****************************************************************************
+ Do a spnego encrypted session setup.
+****************************************************************************/
+
+ADS_STATUS cli_session_setup_spnego(struct cli_state *cli, const char *user,
+ const char *pass, const char *domain)
+{
+ char *principal;
+ char *OIDs[ASN1_MAX_OIDS];
+ int i;
+ BOOL got_kerberos_mechanism = False;
+ DATA_BLOB blob;
+
+ DEBUG(3,("Doing spnego session setup (blob length=%lu)\n", (unsigned long)cli->secblob.length));
+
+ /* the server might not even do spnego */
+ if (cli->secblob.length <= 16) {
+ DEBUG(3,("server didn't supply a full spnego negprot\n"));
+ goto ntlmssp;
+ }
+
+#if 0
+ file_save("negprot.dat", cli->secblob.data, cli->secblob.length);
+#endif
+
+ /* there is 16 bytes of GUID before the real spnego packet starts */
+ blob = data_blob(cli->secblob.data+16, cli->secblob.length-16);
+
+ /* the server sent us the first part of the SPNEGO exchange in the negprot
+ reply */
+ if (!spnego_parse_negTokenInit(blob, OIDs, &principal)) {
+ data_blob_free(&blob);
+ return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
+ }
+ data_blob_free(&blob);
+
+ /* make sure the server understands kerberos */
+ for (i=0;OIDs[i];i++) {
+ DEBUG(3,("got OID=%s\n", OIDs[i]));
+ if (strcmp(OIDs[i], OID_KERBEROS5_OLD) == 0 ||
+ strcmp(OIDs[i], OID_KERBEROS5) == 0) {
+ got_kerberos_mechanism = True;
+ }
+ free(OIDs[i]);
+ }
+ DEBUG(3,("got principal=%s\n", principal));
+
+ fstrcpy(cli->user_name, user);
+
+#ifdef HAVE_KRB5
+ /* If password is set we reauthenticate to kerberos server
+ * and do not store results */
+
+ if (got_kerberos_mechanism && cli->use_kerberos) {
+ if (pass && *pass) {
+ int ret;
+
+ use_in_memory_ccache();
+ ret = kerberos_kinit_password(user, pass, 0 /* no time correction for now */, NULL);
+
+ if (ret){
+ DEBUG(0, ("Kinit failed: %s\n", error_message(ret)));
+ return ADS_ERROR_KRB5(ret);
+ }
+ }
+
+ return cli_session_setup_kerberos(cli, principal, domain);
+ }
+#endif
+
+ free(principal);
+
+ntlmssp:
+
+ return ADS_ERROR_NT(cli_session_setup_ntlmssp(cli, user, pass, domain));
+}
+
+/****************************************************************************
+ Send a session setup. The username and workgroup is in UNIX character
+ format and must be converted to DOS codepage format before sending. If the
+ password is in plaintext, the same should be done.
+****************************************************************************/
+
+BOOL cli_session_setup(struct cli_state *cli,
+ const char *user,
+ const char *pass, int passlen,
+ const char *ntpass, int ntpasslen,
+ const char *workgroup)
+{
+ char *p;
+ fstring user2;
+
+ /* allow for workgroups as part of the username */
+ fstrcpy(user2, user);
+ if ((p=strchr_m(user2,'\\')) || (p=strchr_m(user2,'/')) ||
+ (p=strchr_m(user2,*lp_winbind_separator()))) {
+ *p = 0;
+ user = p+1;
+ workgroup = user2;
+ }
+
+ if (cli->protocol < PROTOCOL_LANMAN1)
+ return True;
+
+ /* now work out what sort of session setup we are going to
+ do. I have split this into separate functions to make the
+ flow a bit easier to understand (tridge) */
+
+ /* if its an older server then we have to use the older request format */
+
+ if (cli->protocol < PROTOCOL_NT1) {
+ if (!lp_client_lanman_auth() && passlen != 24 && (*pass)) {
+ DEBUG(1, ("Server requested LM password but 'client lanman auth'"
+ " is disabled\n"));
+ return False;
+ }
+
+ if ((cli->sec_mode & NEGOTIATE_SECURITY_CHALLENGE_RESPONSE) == 0 &&
+ !lp_client_plaintext_auth() && (*pass)) {
+ DEBUG(1, ("Server requested plaintext password but 'client use plaintext auth'"
+ " is disabled\n"));
+ return False;
+ }
+
+ return cli_session_setup_lanman2(cli, user, pass, passlen, workgroup);
+ }
+
+ /* if no user is supplied then we have to do an anonymous connection.
+ passwords are ignored */
+
+ if (!user || !*user)
+ return cli_session_setup_guest(cli);
+
+ /* if the server is share level then send a plaintext null
+ password at this point. The password is sent in the tree
+ connect */
+
+ if ((cli->sec_mode & NEGOTIATE_SECURITY_USER_LEVEL) == 0)
+ return cli_session_setup_plaintext(cli, user, "", workgroup);
+
+ /* if the server doesn't support encryption then we have to use
+ plaintext. The second password is ignored */
+
+ if ((cli->sec_mode & NEGOTIATE_SECURITY_CHALLENGE_RESPONSE) == 0) {
+ if (!lp_client_plaintext_auth() && (*pass)) {
+ DEBUG(1, ("Server requested plaintext password but 'client use plaintext auth'"
+ " is disabled\n"));
+ return False;
+ }
+ return cli_session_setup_plaintext(cli, user, pass, workgroup);
+ }
+
+ /* if the server supports extended security then use SPNEGO */
+
+ if (cli->capabilities & CAP_EXTENDED_SECURITY) {
+ ADS_STATUS status = cli_session_setup_spnego(cli, user, pass, workgroup);
+ if (!ADS_ERR_OK(status)) {
+ DEBUG(3, ("SPNEGO login failed: %s\n", ads_errstr(status)));
+ return False;
+ }
+ return True;
+ }
+
+ /* otherwise do a NT1 style session setup */
+
+ return cli_session_setup_nt1(cli, user,
+ pass, passlen, ntpass, ntpasslen,
+ workgroup);
+}
+
+/****************************************************************************
+ Send a uloggoff.
+*****************************************************************************/
+
+BOOL cli_ulogoff(struct cli_state *cli)
+{
+ memset(cli->outbuf,'\0',smb_size);
+ set_message(cli->outbuf,2,0,True);
+ SCVAL(cli->outbuf,smb_com,SMBulogoffX);
+ cli_setup_packet(cli);
+ SSVAL(cli->outbuf,smb_vwv0,0xFF);
+ SSVAL(cli->outbuf,smb_vwv2,0); /* no additional info */
+
+ cli_send_smb(cli);
+ if (!cli_receive_smb(cli))
+ return False;
+
+ return !cli_is_error(cli);
+}
+
+/****************************************************************************
+ Send a tconX.
+****************************************************************************/
+BOOL cli_send_tconX(struct cli_state *cli,
+ const char *share, const char *dev, const char *pass, int passlen)
+{
+ fstring fullshare, pword;
+ char *p;
+ memset(cli->outbuf,'\0',smb_size);
+ memset(cli->inbuf,'\0',smb_size);
+
+ fstrcpy(cli->share, share);
+
+ /* in user level security don't send a password now */
+ if (cli->sec_mode & NEGOTIATE_SECURITY_USER_LEVEL) {
+ passlen = 1;
+ pass = "";
+ }
+
+ if ((cli->sec_mode & NEGOTIATE_SECURITY_CHALLENGE_RESPONSE) && *pass && passlen != 24) {
+ if (!lp_client_lanman_auth()) {
+ DEBUG(1, ("Server requested LANMAN password but 'client use lanman auth'"
+ " is disabled\n"));
+ return False;
+ }
+
+ /*
+ * Non-encrypted passwords - convert to DOS codepage before encryption.
+ */
+ passlen = 24;
+ SMBencrypt(pass,cli->secblob.data,(uchar *)pword);
+ } else {
+ if((cli->sec_mode & (NEGOTIATE_SECURITY_USER_LEVEL|NEGOTIATE_SECURITY_CHALLENGE_RESPONSE)) == 0) {
+ if (!lp_client_plaintext_auth() && (*pass)) {
+ DEBUG(1, ("Server requested plaintext password but 'client use plaintext auth'"
+ " is disabled\n"));
+ return False;
+ }
+
+ /*
+ * Non-encrypted passwords - convert to DOS codepage before using.
+ */
+ passlen = clistr_push(cli, pword, pass, sizeof(pword), STR_TERMINATE);
+
+ } else {
+ memcpy(pword, pass, passlen);
+ }
+ }
+
+ slprintf(fullshare, sizeof(fullshare)-1,
+ "\\\\%s\\%s", cli->desthost, share);
+
+ set_message(cli->outbuf,4, 0, True);
+ SCVAL(cli->outbuf,smb_com,SMBtconX);
+ cli_setup_packet(cli);
+
+ SSVAL(cli->outbuf,smb_vwv0,0xFF);
+ SSVAL(cli->outbuf,smb_vwv3,passlen);
+
+ p = smb_buf(cli->outbuf);
+ memcpy(p,pword,passlen);
+ p += passlen;
+ p += clistr_push(cli, p, fullshare, -1, STR_TERMINATE |STR_UPPER);
+ p += clistr_push(cli, p, dev, -1, STR_TERMINATE |STR_UPPER | STR_ASCII);
+
+ cli_setup_bcc(cli, p);
+
+ cli_send_smb(cli);
+ if (!cli_receive_smb(cli))
+ return False;
+
+ if (cli_is_error(cli))
+ return False;
+
+ clistr_pull(cli, cli->dev, smb_buf(cli->inbuf), sizeof(fstring), -1, STR_TERMINATE|STR_ASCII);
+
+ if (cli->protocol >= PROTOCOL_NT1 &&
+ smb_buflen(cli->inbuf) == 3) {
+ /* almost certainly win95 - enable bug fixes */
+ cli->win95 = True;
+ }
+
+ cli->cnum = SVAL(cli->inbuf,smb_tid);
+ return True;
+}
+
+/****************************************************************************
+ Send a tree disconnect.
+****************************************************************************/
+
+BOOL cli_tdis(struct cli_state *cli)
+{
+ memset(cli->outbuf,'\0',smb_size);
+ set_message(cli->outbuf,0,0,True);
+ SCVAL(cli->outbuf,smb_com,SMBtdis);
+ SSVAL(cli->outbuf,smb_tid,cli->cnum);
+ cli_setup_packet(cli);
+
+ cli_send_smb(cli);
+ if (!cli_receive_smb(cli))
+ return False;
+
+ return !cli_is_error(cli);
+}
+
+/****************************************************************************
+ Send a negprot command.
+****************************************************************************/
+
+void cli_negprot_send(struct cli_state *cli)
+{
+ char *p;
+ int numprots;
+
+ if (cli->protocol < PROTOCOL_NT1)
+ cli->use_spnego = False;
+
+ memset(cli->outbuf,'\0',smb_size);
+
+ /* setup the protocol strings */
+ set_message(cli->outbuf,0,0,True);
+
+ p = smb_buf(cli->outbuf);
+ for (numprots=0;
+ prots[numprots].name && prots[numprots].prot<=cli->protocol;
+ numprots++) {
+ *p++ = 2;
+ p += clistr_push(cli, p, prots[numprots].name, -1, STR_TERMINATE);
+ }
+
+ SCVAL(cli->outbuf,smb_com,SMBnegprot);
+ cli_setup_bcc(cli, p);
+ cli_setup_packet(cli);
+
+ SCVAL(smb_buf(cli->outbuf),0,2);
+
+ cli_send_smb(cli);
+}
+
+/****************************************************************************
+ Send a negprot command.
+****************************************************************************/
+
+BOOL cli_negprot(struct cli_state *cli)
+{
+ char *p;
+ int numprots;
+ int plength;
+
+ if (cli->protocol < PROTOCOL_NT1)
+ cli->use_spnego = False;
+
+ memset(cli->outbuf,'\0',smb_size);
+
+ /* setup the protocol strings */
+ for (plength=0,numprots=0;
+ prots[numprots].name && prots[numprots].prot<=cli->protocol;
+ numprots++)
+ plength += strlen(prots[numprots].name)+2;
+
+ set_message(cli->outbuf,0,plength,True);
+
+ p = smb_buf(cli->outbuf);
+ for (numprots=0;
+ prots[numprots].name && prots[numprots].prot<=cli->protocol;
+ numprots++) {
+ *p++ = 2;
+ p += clistr_push(cli, p, prots[numprots].name, -1, STR_TERMINATE);
+ }
+
+ SCVAL(cli->outbuf,smb_com,SMBnegprot);
+ cli_setup_packet(cli);
+
+ SCVAL(smb_buf(cli->outbuf),0,2);
+
+ cli_send_smb(cli);
+ if (!cli_receive_smb(cli))
+ return False;
+
+ show_msg(cli->inbuf);
+
+ if (cli_is_error(cli) ||
+ ((int)SVAL(cli->inbuf,smb_vwv0) >= numprots)) {
+ return(False);
+ }
+
+ cli->protocol = prots[SVAL(cli->inbuf,smb_vwv0)].prot;
+
+ if ((cli->protocol < PROTOCOL_NT1) && cli->sign_info.mandatory_signing) {
+ DEBUG(0,("cli_negprot: SMB signing is mandatory and the selected protocol level doesn't support it.\n"));
+ return False;
+ }
+
+ if (cli->protocol >= PROTOCOL_NT1) {
+ /* NT protocol */
+ cli->sec_mode = CVAL(cli->inbuf,smb_vwv1);
+ cli->max_mux = SVAL(cli->inbuf, smb_vwv1+1);
+ cli->max_xmit = IVAL(cli->inbuf,smb_vwv3+1);
+ cli->sesskey = IVAL(cli->inbuf,smb_vwv7+1);
+ cli->serverzone = SVALS(cli->inbuf,smb_vwv15+1);
+ cli->serverzone *= 60;
+ /* this time arrives in real GMT */
+ cli->servertime = interpret_long_date(cli->inbuf+smb_vwv11+1);
+ cli->secblob = data_blob(smb_buf(cli->inbuf),smb_buflen(cli->inbuf));
+ cli->capabilities = IVAL(cli->inbuf,smb_vwv9+1);
+ if (cli->capabilities & CAP_RAW_MODE) {
+ cli->readbraw_supported = True;
+ cli->writebraw_supported = True;
+ }
+ /* work out if they sent us a workgroup */
+ if (!(cli->capabilities & CAP_EXTENDED_SECURITY) &&
+ smb_buflen(cli->inbuf) > 8) {
+ clistr_pull(cli, cli->server_domain,
+ smb_buf(cli->inbuf)+8, sizeof(cli->server_domain),
+ smb_buflen(cli->inbuf)-8, STR_UNICODE|STR_NOALIGN);
+ }
+
+ /*
+ * As signing is slow we only turn it on if either the client or
+ * the server require it. JRA.
+ */
+
+ if (cli->sec_mode & NEGOTIATE_SECURITY_SIGNATURES_REQUIRED) {
+ /* Fail if server says signing is mandatory and we don't want to support it. */
+ if (!cli->sign_info.allow_smb_signing) {
+ DEBUG(0,("cli_negprot: SMB signing is mandatory and we have disabled it.\n"));
+ return False;
+ }
+ cli->sign_info.negotiated_smb_signing = True;
+ cli->sign_info.mandatory_signing = True;
+ } else if (cli->sign_info.mandatory_signing && cli->sign_info.allow_smb_signing) {
+ /* Fail if client says signing is mandatory and the server doesn't support it. */
+ if (!(cli->sec_mode & NEGOTIATE_SECURITY_SIGNATURES_ENABLED)) {
+ DEBUG(1,("cli_negprot: SMB signing is mandatory and the server doesn't support it.\n"));
+ return False;
+ }
+ cli->sign_info.negotiated_smb_signing = True;
+ cli->sign_info.mandatory_signing = True;
+ } else if (cli->sign_info.allow_smb_signing && cli->sec_mode & NEGOTIATE_SECURITY_SIGNATURES_ENABLED) {
+ cli->sign_info.negotiated_smb_signing = True;
+ }
+
+ } else if (cli->protocol >= PROTOCOL_LANMAN1) {
+ cli->use_spnego = False;
+ cli->sec_mode = SVAL(cli->inbuf,smb_vwv1);
+ cli->max_xmit = SVAL(cli->inbuf,smb_vwv2);
+ cli->sesskey = IVAL(cli->inbuf,smb_vwv6);
+ cli->serverzone = SVALS(cli->inbuf,smb_vwv10);
+ cli->serverzone *= 60;
+ /* this time is converted to GMT by make_unix_date */
+ cli->servertime = make_unix_date(cli->inbuf+smb_vwv8);
+ cli->readbraw_supported = ((SVAL(cli->inbuf,smb_vwv5) & 0x1) != 0);
+ cli->writebraw_supported = ((SVAL(cli->inbuf,smb_vwv5) & 0x2) != 0);
+ cli->secblob = data_blob(smb_buf(cli->inbuf),smb_buflen(cli->inbuf));
+ } else {
+ /* the old core protocol */
+ cli->use_spnego = False;
+ cli->sec_mode = 0;
+ cli->serverzone = TimeDiff(time(NULL));
+ }
+
+ cli->max_xmit = MIN(cli->max_xmit, CLI_BUFFER_SIZE);
+
+ /* a way to force ascii SMB */
+ if (getenv("CLI_FORCE_ASCII"))
+ cli->capabilities &= ~CAP_UNICODE;
+
+ return True;
+}
+
+/****************************************************************************
+ Send a session request. See rfc1002.txt 4.3 and 4.3.2.
+****************************************************************************/
+
+BOOL cli_session_request(struct cli_state *cli,
+ struct nmb_name *calling, struct nmb_name *called)
+{
+ char *p;
+ int len = 4;
+ extern pstring user_socket_options;
+
+ memcpy(&(cli->calling), calling, sizeof(*calling));
+ memcpy(&(cli->called ), called , sizeof(*called ));
+
+ /* put in the destination name */
+ p = cli->outbuf+len;
+ name_mangle(cli->called .name, p, cli->called .name_type);
+ len += name_len(p);
+
+ /* and my name */
+ p = cli->outbuf+len;
+ name_mangle(cli->calling.name, p, cli->calling.name_type);
+ len += name_len(p);
+
+ /* 445 doesn't have session request */
+ if (cli->port == 445)
+ return True;
+
+ /* send a session request (RFC 1002) */
+ /* setup the packet length
+ * Remove four bytes from the length count, since the length
+ * field in the NBT Session Service header counts the number
+ * of bytes which follow. The cli_send_smb() function knows
+ * about this and accounts for those four bytes.
+ * CRH.
+ */
+ len -= 4;
+ _smb_setlen(cli->outbuf,len);
+ SCVAL(cli->outbuf,0,0x81);
+
+ cli_send_smb(cli);
+ DEBUG(5,("Sent session request\n"));
+
+ if (!cli_receive_smb(cli))
+ return False;
+
+ if (CVAL(cli->inbuf,0) == 0x84) {
+ /* C. Hoch 9/14/95 Start */
+ /* For information, here is the response structure.
+ * We do the byte-twiddling to for portability.
+ struct RetargetResponse{
+ unsigned char type;
+ unsigned char flags;
+ int16 length;
+ int32 ip_addr;
+ int16 port;
+ };
+ */
+ int port = (CVAL(cli->inbuf,8)<<8)+CVAL(cli->inbuf,9);
+ /* SESSION RETARGET */
+ putip((char *)&cli->dest_ip,cli->inbuf+4);
+
+ cli->fd = open_socket_out(SOCK_STREAM, &cli->dest_ip, port, LONG_CONNECT_TIMEOUT);
+ if (cli->fd == -1)
+ return False;
+
+ DEBUG(3,("Retargeted\n"));
+
+ set_socket_options(cli->fd,user_socket_options);
+
+ /* Try again */
+ {
+ static int depth;
+ BOOL ret;
+ if (depth > 4) {
+ DEBUG(0,("Retarget recursion - failing\n"));
+ return False;
+ }
+ depth++;
+ ret = cli_session_request(cli, calling, called);
+ depth--;
+ return ret;
+ }
+ } /* C. Hoch 9/14/95 End */
+
+ if (CVAL(cli->inbuf,0) != 0x82) {
+ /* This is the wrong place to put the error... JRA. */
+ cli->rap_error = CVAL(cli->inbuf,4);
+ return False;
+ }
+ return(True);
+}
+
+/****************************************************************************
+ Open the client sockets.
+****************************************************************************/
+
+BOOL cli_connect(struct cli_state *cli, const char *host, struct in_addr *ip)
+{
+ extern pstring user_socket_options;
+ int name_type = 0x20;
+ char *p;
+
+ /* reasonable default hostname */
+ if (!host) host = "*SMBSERVER";
+
+ fstrcpy(cli->desthost, host);
+
+ /* allow hostnames of the form NAME#xx and do a netbios lookup */
+ if ((p = strchr(cli->desthost, '#'))) {
+ name_type = strtol(p+1, NULL, 16);
+ *p = 0;
+ }
+
+ if (!ip || is_zero_ip(*ip)) {
+ if (!resolve_name(cli->desthost, &cli->dest_ip, name_type)) {
+ return False;
+ }
+ if (ip) *ip = cli->dest_ip;
+ } else {
+ cli->dest_ip = *ip;
+ }
+
+ if (getenv("LIBSMB_PROG")) {
+ cli->fd = sock_exec(getenv("LIBSMB_PROG"));
+ } else {
+ /* try 445 first, then 139 */
+ int port = cli->port?cli->port:445;
+ cli->fd = open_socket_out(SOCK_STREAM, &cli->dest_ip,
+ port, cli->timeout);
+ if (cli->fd == -1 && cli->port == 0) {
+ port = 139;
+ cli->fd = open_socket_out(SOCK_STREAM, &cli->dest_ip,
+ port, cli->timeout);
+ }
+ if (cli->fd != -1)
+ cli->port = port;
+ }
+ if (cli->fd == -1) {
+ DEBUG(1,("Error connecting to %s (%s)\n",
+ ip?inet_ntoa(*ip):host,strerror(errno)));
+ return False;
+ }
+
+ set_socket_options(cli->fd,user_socket_options);
+
+ return True;
+}
+
+/****************************************************************************
+ Initialise client credentials for authenticated pipe access.
+****************************************************************************/
+
+void init_creds(struct ntuser_creds *creds, const char* username,
+ const char* domain, const char* password)
+{
+ ZERO_STRUCTP(creds);
+
+ pwd_set_cleartext(&creds->pwd, password);
+
+ fstrcpy(creds->user_name, username);
+ fstrcpy(creds->domain, domain);
+
+ if (!*username) {
+ creds->pwd.null_pwd = True;
+ }
+}
+
+/**
+ establishes a connection to after the negprot.
+ @param output_cli A fully initialised cli structure, non-null only on success
+ @param dest_host The netbios name of the remote host
+ @param dest_ip (optional) The the destination IP, NULL for name based lookup
+ @param port (optional) The destination port (0 for default)
+ @param retry BOOL. Did this connection fail with a retryable error ?
+
+*/
+NTSTATUS cli_start_connection(struct cli_state **output_cli,
+ const char *my_name,
+ const char *dest_host,
+ struct in_addr *dest_ip, int port,
+ int signing_state, int flags,
+ BOOL *retry)
+{
+ NTSTATUS nt_status;
+ struct nmb_name calling;
+ struct nmb_name called;
+ struct cli_state *cli;
+ struct in_addr ip;
+
+ if (retry)
+ *retry = False;
+
+ if (!my_name)
+ my_name = global_myname();
+
+ if (!(cli = cli_initialise(NULL)))
+ return NT_STATUS_NO_MEMORY;
+
+ make_nmb_name(&calling, my_name, 0x0);
+ make_nmb_name(&called , dest_host, 0x20);
+
+ if (cli_set_port(cli, port) != port) {
+ cli_shutdown(cli);
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ cli_set_timeout(cli, 10000); /* 10 seconds. */
+
+ if (dest_ip)
+ ip = *dest_ip;
+ else
+ ZERO_STRUCT(ip);
+
+again:
+
+ DEBUG(3,("Connecting to host=%s\n", dest_host));
+
+ if (!cli_connect(cli, dest_host, &ip)) {
+ DEBUG(1,("cli_full_connection: failed to connect to %s (%s)\n",
+ nmb_namestr(&called), inet_ntoa(ip)));
+ cli_shutdown(cli);
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ if (retry)
+ *retry = True;
+
+ if (!cli_session_request(cli, &calling, &called)) {
+ char *p;
+ DEBUG(1,("session request to %s failed (%s)\n",
+ called.name, cli_errstr(cli)));
+ if ((p=strchr(called.name, '.')) && !is_ipaddress(called.name)) {
+ *p = 0;
+ goto again;
+ }
+ if (strcmp(called.name, "*SMBSERVER")) {
+ make_nmb_name(&called , "*SMBSERVER", 0x20);
+ goto again;
+ }
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ cli_setup_signing_state(cli, signing_state);
+
+ if (flags & CLI_FULL_CONNECTION_DONT_SPNEGO)
+ cli->use_spnego = False;
+ else if (flags & CLI_FULL_CONNECTION_USE_KERBEROS)
+ cli->use_kerberos = True;
+
+ if (!cli_negprot(cli)) {
+ DEBUG(1,("failed negprot\n"));
+ nt_status = NT_STATUS_UNSUCCESSFUL;
+ cli_shutdown(cli);
+ return nt_status;
+ }
+
+ *output_cli = cli;
+ return NT_STATUS_OK;
+}
+
+
+/**
+ establishes a connection right up to doing tconX, password specified.
+ @param output_cli A fully initialised cli structure, non-null only on success
+ @param dest_host The netbios name of the remote host
+ @param dest_ip (optional) The the destination IP, NULL for name based lookup
+ @param port (optional) The destination port (0 for default)
+ @param service (optional) The share to make the connection to. Should be 'unqualified' in any way.
+ @param service_type The 'type' of serivice.
+ @param user Username, unix string
+ @param domain User's domain
+ @param password User's password, unencrypted unix string.
+ @param retry BOOL. Did this connection fail with a retryable error ?
+*/
+
+NTSTATUS cli_full_connection(struct cli_state **output_cli,
+ const char *my_name,
+ const char *dest_host,
+ struct in_addr *dest_ip, int port,
+ const char *service, const char *service_type,
+ const char *user, const char *domain,
+ const char *password, int flags,
+ int signing_state,
+ BOOL *retry)
+{
+ struct ntuser_creds creds;
+ NTSTATUS nt_status;
+ struct cli_state *cli = NULL;
+
+ nt_status = cli_start_connection(&cli, my_name, dest_host,
+ dest_ip, port, signing_state, flags, retry);
+
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ return nt_status;
+ }
+
+ if (!cli_session_setup(cli, user, password, strlen(password)+1,
+ password, strlen(password)+1,
+ domain)) {
+ if ((flags & CLI_FULL_CONNECTION_ANNONYMOUS_FALLBACK)
+ && cli_session_setup(cli, "", "", 0, "", 0, domain)) {
+ } else {
+ nt_status = cli_nt_error(cli);
+ DEBUG(1,("failed session setup with %s\n", nt_errstr(nt_status)));
+ cli_shutdown(cli);
+ if (NT_STATUS_IS_OK(nt_status))
+ nt_status = NT_STATUS_UNSUCCESSFUL;
+ return nt_status;
+ }
+ }
+
+ if (service) {
+ if (!cli_send_tconX(cli, service, service_type,
+ password, strlen(password)+1)) {
+ nt_status = cli_nt_error(cli);
+ DEBUG(1,("failed tcon_X with %s\n", nt_errstr(nt_status)));
+ cli_shutdown(cli);
+ if (NT_STATUS_IS_OK(nt_status)) {
+ nt_status = NT_STATUS_UNSUCCESSFUL;
+ }
+ return nt_status;
+ }
+ }
+
+ init_creds(&creds, user, domain, password);
+ cli_init_creds(cli, &creds);
+
+ *output_cli = cli;
+ return NT_STATUS_OK;
+}
+
+/****************************************************************************
+ Attempt a NetBIOS session request, falling back to *SMBSERVER if needed.
+****************************************************************************/
+
+BOOL attempt_netbios_session_request(struct cli_state *cli, const char *srchost, const char *desthost,
+ struct in_addr *pdest_ip)
+{
+ struct nmb_name calling, called;
+
+ make_nmb_name(&calling, srchost, 0x0);
+
+ /*
+ * If the called name is an IP address
+ * then use *SMBSERVER immediately.
+ */
+
+ if(is_ipaddress(desthost))
+ make_nmb_name(&called, "*SMBSERVER", 0x20);
+ else
+ make_nmb_name(&called, desthost, 0x20);
+
+ if (!cli_session_request(cli, &calling, &called)) {
+ struct nmb_name smbservername;
+
+ make_nmb_name(&smbservername , "*SMBSERVER", 0x20);
+
+ /*
+ * If the name wasn't *SMBSERVER then
+ * try with *SMBSERVER if the first name fails.
+ */
+
+ if (nmb_name_equal(&called, &smbservername)) {
+
+ /*
+ * The name used was *SMBSERVER, don't bother with another name.
+ */
+
+ DEBUG(0,("attempt_netbios_session_request: %s rejected the session for name *SMBSERVER \
+with error %s.\n", desthost, cli_errstr(cli) ));
+ return False;
+ }
+
+ /*
+ * We need to close the connection here but can't call cli_shutdown as
+ * will free an allocated cli struct. cli_close_connection was invented
+ * for this purpose. JRA. Based on work by "Kim R. Pedersen" <krp@filanet.dk>.
+ */
+
+ cli_close_connection(cli);
+
+ if (!cli_initialise(cli) ||
+ !cli_connect(cli, desthost, pdest_ip) ||
+ !cli_session_request(cli, &calling, &smbservername)) {
+ DEBUG(0,("attempt_netbios_session_request: %s rejected the session for \
+name *SMBSERVER with error %s\n", desthost, cli_errstr(cli) ));
+ return False;
+ }
+ }
+
+ return True;
+}
+
+
+
+
+
+/****************************************************************************
+ Send an old style tcon.
+****************************************************************************/
+NTSTATUS cli_raw_tcon(struct cli_state *cli,
+ const char *service, const char *pass, const char *dev,
+ uint16 *max_xmit, uint16 *tid)
+{
+ char *p;
+
+ if (!lp_client_plaintext_auth() && (*pass)) {
+ DEBUG(1, ("Server requested plaintext password but 'client use plaintext auth'"
+ " is disabled\n"));
+ return NT_STATUS_ACCESS_DENIED;
+ }
+
+ memset(cli->outbuf,'\0',smb_size);
+ memset(cli->inbuf,'\0',smb_size);
+
+ set_message(cli->outbuf, 0, 0, True);
+ SCVAL(cli->outbuf,smb_com,SMBtcon);
+ cli_setup_packet(cli);
+
+ p = smb_buf(cli->outbuf);
+ *p++ = 4; p += clistr_push(cli, p, service, -1, STR_TERMINATE | STR_NOALIGN);
+ *p++ = 4; p += clistr_push(cli, p, pass, -1, STR_TERMINATE | STR_NOALIGN);
+ *p++ = 4; p += clistr_push(cli, p, dev, -1, STR_TERMINATE | STR_NOALIGN);
+
+ cli_setup_bcc(cli, p);
+
+ cli_send_smb(cli);
+ if (!cli_receive_smb(cli)) {
+ return NT_STATUS_UNEXPECTED_NETWORK_ERROR;
+ }
+
+ if (cli_is_error(cli)) {
+ return cli_nt_error(cli);
+ }
+
+ *max_xmit = SVAL(cli->inbuf, smb_vwv0);
+ *tid = SVAL(cli->inbuf, smb_vwv1);
+
+ return NT_STATUS_OK;
+}
+
+/* Return a cli_state pointing at the IPC$ share for the given server */
+
+struct cli_state *get_ipc_connect(char *server, struct in_addr *server_ip,
+ struct user_auth_info *user_info)
+{
+ struct cli_state *cli;
+ pstring myname;
+ NTSTATUS nt_status;
+
+ get_myname(myname);
+
+ nt_status = cli_full_connection(&cli, myname, server, server_ip, 0, "IPC$", "IPC",
+ user_info->username, lp_workgroup(), user_info->password,
+ CLI_FULL_CONNECTION_ANNONYMOUS_FALLBACK, Undefined, NULL);
+
+ if (NT_STATUS_IS_OK(nt_status)) {
+ return cli;
+ } else if (is_ipaddress(server)) {
+ /* windows 9* needs a correct NMB name for connections */
+ fstring remote_name;
+
+ if (name_status_find("*", 0, 0, *server_ip, remote_name)) {
+ cli = get_ipc_connect(remote_name, server_ip, user_info);
+ if (cli)
+ return cli;
+ }
+ }
+ return NULL;
+}
+
+/*
+ * Given the IP address of a master browser on the network, return its
+ * workgroup and connect to it.
+ *
+ * This function is provided to allow additional processing beyond what
+ * get_ipc_connect_master_ip_bcast() does, e.g. to retrieve the list of master
+ * browsers and obtain each master browsers' list of domains (in case the
+ * first master browser is recently on the network and has not yet
+ * synchronized with other master browsers and therefore does not yet have the
+ * entire network browse list)
+ */
+
+struct cli_state *get_ipc_connect_master_ip(struct ip_service * mb_ip, pstring workgroup, struct user_auth_info *user_info)
+{
+ static fstring name;
+ struct cli_state *cli;
+ struct in_addr server_ip;
+
+ DEBUG(99, ("Looking up name of master browser %s\n",
+ inet_ntoa(mb_ip->ip)));
+
+ /*
+ * Do a name status query to find out the name of the master browser.
+ * We use <01><02>__MSBROWSE__<02>#01 if *#00 fails because a domain
+ * master browser will not respond to a wildcard query (or, at least,
+ * an NT4 server acting as the domain master browser will not).
+ *
+ * We might be able to use ONLY the query on MSBROWSE, but that's not
+ * yet been tested with all Windows versions, so until it is, leave
+ * the original wildcard query as the first choice and fall back to
+ * MSBROWSE if the wildcard query fails.
+ */
+ if (!name_status_find("*", 0, 0x1d, mb_ip->ip, name) &&
+ !name_status_find(MSBROWSE, 1, 0x1d, mb_ip->ip, name)) {
+
+ DEBUG(99, ("Could not retrieve name status for %s\n",
+ inet_ntoa(mb_ip->ip)));
+ return NULL;
+ }
+
+ if (!find_master_ip(name, &server_ip)) {
+ DEBUG(99, ("Could not find master ip for %s\n", name));
+ return NULL;
+ }
+
+ pstrcpy(workgroup, name);
+
+ DEBUG(4, ("found master browser %s, %s\n",
+ name, inet_ntoa(mb_ip->ip)));
+
+ cli = get_ipc_connect(inet_ntoa(server_ip), &server_ip, user_info);
+
+ return cli;
+
+}
+
+/*
+ * Return the IP address and workgroup of a master browser on the network, and
+ * connect to it.
+ */
+
+struct cli_state *get_ipc_connect_master_ip_bcast(pstring workgroup, struct user_auth_info *user_info)
+{
+ struct ip_service *ip_list;
+ struct cli_state *cli;
+ int i, count;
+
+ DEBUG(99, ("Do broadcast lookup for workgroups on local network\n"));
+
+ /* Go looking for workgroups by broadcasting on the local network */
+
+ if (!name_resolve_bcast(MSBROWSE, 1, &ip_list, &count)) {
+ DEBUG(99, ("No master browsers responded\n"));
+ return False;
+ }
+
+ for (i = 0; i < count; i++) {
+ DEBUG(99, ("Found master browser %s\n", inet_ntoa(ip_list[i].ip)));
+
+ cli = get_ipc_connect_master_ip(&ip_list[i], workgroup, user_info);
+ if (cli)
+ return(cli);
+ }
+
+ return NULL;
+}
diff --git a/source/libsmb/clidgram.c b/source/libsmb/clidgram.c
new file mode 100644
index 00000000000..c4675f1938a
--- /dev/null
+++ b/source/libsmb/clidgram.c
@@ -0,0 +1,264 @@
+/*
+ Unix SMB/CIFS implementation.
+ client dgram calls
+ Copyright (C) Andrew Tridgell 1994-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 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"
+
+/*
+ * cli_send_mailslot, send a mailslot for client code ...
+ */
+
+int cli_send_mailslot(int dgram_sock, BOOL unique, const char *mailslot,
+ char *buf, int len,
+ const char *srcname, int src_type,
+ const char *dstname, int dest_type,
+ struct in_addr dest_ip, struct in_addr src_ip,
+ int dest_port, int src_port)
+{
+ struct packet_struct p;
+ struct dgram_packet *dgram = &p.packet.dgram;
+ char *ptr, *p2;
+ char tmp[4];
+
+ memset((char *)&p, '\0', sizeof(p));
+
+ /*
+ * Next, build the DGRAM ...
+ */
+
+ /* DIRECT GROUP or UNIQUE datagram. */
+ dgram->header.msg_type = unique ? 0x10 : 0x11;
+ dgram->header.flags.node_type = M_NODE;
+ dgram->header.flags.first = True;
+ dgram->header.flags.more = False;
+ dgram->header.dgm_id = ((unsigned)time(NULL)%(unsigned)0x7FFF) + ((unsigned)sys_getpid()%(unsigned)100);
+ dgram->header.source_ip.s_addr = src_ip.s_addr;
+ dgram->header.source_port = ntohs(src_port);
+ dgram->header.dgm_length = 0; /* Let build_dgram() handle this. */
+ dgram->header.packet_offset = 0;
+
+ make_nmb_name(&dgram->source_name,srcname,src_type);
+ make_nmb_name(&dgram->dest_name,dstname,dest_type);
+
+ ptr = &dgram->data[0];
+
+ /* Setup the smb part. */
+ ptr -= 4; /* XXX Ugliness because of handling of tcp SMB length. */
+ memcpy(tmp,ptr,4);
+ set_message(ptr,17,17 + len,True);
+ memcpy(ptr,tmp,4);
+
+ SCVAL(ptr,smb_com,SMBtrans);
+ SSVAL(ptr,smb_vwv1,len);
+ SSVAL(ptr,smb_vwv11,len);
+ SSVAL(ptr,smb_vwv12,70 + strlen(mailslot));
+ SSVAL(ptr,smb_vwv13,3);
+ SSVAL(ptr,smb_vwv14,1);
+ SSVAL(ptr,smb_vwv15,1);
+ SSVAL(ptr,smb_vwv16,2);
+ p2 = smb_buf(ptr);
+ fstrcpy(p2,mailslot);
+ p2 = skip_string(p2,1);
+
+ memcpy(p2,buf,len);
+ p2 += len;
+
+ dgram->datasize = PTR_DIFF(p2,ptr+4); /* +4 for tcp length. */
+
+ p.ip = dest_ip;
+ p.port = dest_port;
+ p.fd = dgram_sock;
+ p.timestamp = time(NULL);
+ p.packet_type = DGRAM_PACKET;
+
+ DEBUG(4,("send_mailslot: Sending to mailslot %s from %s IP %s ", mailslot,
+ nmb_namestr(&dgram->source_name), inet_ntoa(src_ip)));
+ DEBUG(4,("to %s IP %s\n", nmb_namestr(&dgram->dest_name), inet_ntoa(dest_ip)));
+
+ return send_packet(&p);
+
+}
+
+/*
+ * cli_get_response: Get a response ...
+ */
+int cli_get_response(int dgram_sock, BOOL unique, const char *mailslot, char *buf, int bufsiz)
+{
+ struct packet_struct *packet;
+
+ packet = receive_dgram_packet(dgram_sock, 5, mailslot);
+
+ if (packet) { /* We got one, pull what we want out of the SMB data ... */
+
+ struct dgram_packet *dgram = &packet->packet.dgram;
+
+ /*
+ * We should probably parse the SMB, but for now, we will pull what
+ * from fixed, known locations ...
+ */
+
+ /* Copy the data to buffer, respecting sizes ... */
+
+ memcpy(buf, &dgram->data[92], MIN(bufsiz, (dgram->datasize - 92)));
+
+ }
+ else
+ return -1;
+
+ return 0;
+
+}
+
+/*
+ * cli_get_backup_list: Send a get backup list request ...
+ */
+
+static char cli_backup_list[1024];
+
+int cli_get_backup_list(const char *myname, const char *send_to_name)
+{
+ pstring outbuf;
+ char *p;
+ struct in_addr sendto_ip, my_ip;
+ int dgram_sock;
+ struct sockaddr_in sock_out;
+ socklen_t name_size;
+
+ if (!resolve_name(send_to_name, &sendto_ip, 0x1d)) {
+
+ DEBUG(0, ("Could not resolve name: %s<1D>\n", send_to_name));
+ return False;
+
+ }
+
+ my_ip.s_addr = inet_addr("0.0.0.0");
+
+ if (!resolve_name(myname, &my_ip, 0x00)) { /* FIXME: Call others here */
+
+ DEBUG(0, ("Could not resolve name: %s<00>\n", myname));
+
+ }
+
+ if ((dgram_sock = open_socket_out(SOCK_DGRAM, &sendto_ip, 138, LONG_CONNECT_TIMEOUT)) < 0) {
+
+ DEBUG(4, ("open_sock_out failed ..."));
+ return False;
+
+ }
+
+ /* Make it a broadcast socket ... */
+
+ set_socket_options(dgram_sock, "SO_BROADCAST");
+
+ /* Make it non-blocking??? */
+
+ if (fcntl(dgram_sock, F_SETFL, O_NONBLOCK) < 0) {
+
+ DEBUG(0, ("Unable to set non blocking on dgram sock\n"));
+
+ }
+
+ /* Now, bind a local addr to it ... Try port 138 first ... */
+
+ memset((char *)&sock_out, '\0', sizeof(sock_out));
+ sock_out.sin_addr.s_addr = INADDR_ANY;
+ sock_out.sin_port = htons(138);
+ sock_out.sin_family = AF_INET;
+
+ if (bind(dgram_sock, (struct sockaddr *)&sock_out, sizeof(sock_out)) < 0) {
+
+ /* Try again on any port ... */
+
+ sock_out.sin_port = INADDR_ANY;
+
+ if (bind(dgram_sock, (struct sockaddr *)&sock_out, sizeof(sock_out)) < 0) {
+
+ DEBUG(4, ("failed to bind socket to address ...\n"));
+ return False;
+
+ }
+
+ }
+
+ /* Now, figure out what socket name we were bound to. We want the port */
+
+ name_size = sizeof(sock_out);
+
+ getsockname(dgram_sock, (struct sockaddr *)&sock_out, &name_size);
+
+ DEBUG(5, ("Socket bound to IP:%s, port: %d\n", inet_ntoa(sock_out.sin_addr), ntohs(sock_out.sin_port)));
+
+ /* Now, build the request */
+
+ memset(cli_backup_list, '\0', sizeof(cli_backup_list));
+ memset(outbuf, '\0', sizeof(outbuf));
+
+ p = outbuf;
+
+ SCVAL(p, 0, ANN_GetBackupListReq);
+ p++;
+
+ SCVAL(p, 0, 1); /* Count pointer ... */
+ p++;
+
+ SIVAL(p, 0, 1); /* The sender's token ... */
+ p += 4;
+
+ cli_send_mailslot(dgram_sock, True, "\\MAILSLOT\\BROWSE", outbuf,
+ PTR_DIFF(p, outbuf), myname, 0, send_to_name,
+ 0x1d, sendto_ip, my_ip, 138, sock_out.sin_port);
+
+ /* We should check the error and return if we got one */
+
+ /* Now, get the response ... */
+
+ cli_get_response(dgram_sock, True, "\\MAILSLOT\\BROWSE", cli_backup_list, sizeof(cli_backup_list));
+
+ /* Should check the response here ... FIXME */
+
+ close(dgram_sock);
+
+ return True;
+
+}
+
+/*
+ * cli_get_backup_server: Get the backup list and retrieve a server from it
+ */
+
+int cli_get_backup_server(char *my_name, char *target, char *servername, int namesize)
+{
+
+ /* Get the backup list first. We could pull this from the cache later */
+
+ cli_get_backup_list(my_name, target); /* FIXME: Check the response */
+
+ if (!cli_backup_list[0]) { /* Empty list ... try again */
+
+ cli_get_backup_list(my_name, target);
+
+ }
+
+ strncpy(servername, cli_backup_list, MIN(16, namesize));
+
+ return True;
+
+}
diff --git a/source/libsmb/clientgen.c b/source/libsmb/clientgen.c
new file mode 100644
index 00000000000..66edc3ce38b
--- /dev/null
+++ b/source/libsmb/clientgen.c
@@ -0,0 +1,455 @@
+/*
+ Unix SMB/CIFS implementation.
+ SMB client generic functions
+ Copyright (C) Andrew Tridgell 1994-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 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.
+*/
+
+#define NO_SYSLOG
+
+#include "includes.h"
+
+/****************************************************************************
+ Change the timeout (in milliseconds).
+****************************************************************************/
+
+unsigned int cli_set_timeout(struct cli_state *cli, unsigned int timeout)
+{
+ unsigned int old_timeout = cli->timeout;
+ cli->timeout = timeout;
+ return old_timeout;
+}
+
+/****************************************************************************
+ Change the port number used to call on.
+****************************************************************************/
+
+int cli_set_port(struct cli_state *cli, int port)
+{
+ cli->port = port;
+ return port;
+}
+
+/****************************************************************************
+ Read an smb from a fd ignoring all keepalive packets. Note that the buffer
+ *MUST* be of size BUFFER_SIZE+SAFETY_MARGIN.
+ The timeout is in milliseconds
+
+ This is exactly the same as receive_smb except that it never returns
+ a session keepalive packet (just as receive_smb used to do).
+ receive_smb was changed to return keepalives as the oplock processing means this call
+ should never go into a blocking read.
+****************************************************************************/
+
+static BOOL client_receive_smb(int fd,char *buffer, unsigned int timeout)
+{
+ BOOL ret;
+
+ for(;;) {
+ ret = receive_smb_raw(fd, buffer, timeout);
+
+ if (!ret) {
+ DEBUG(10,("client_receive_smb failed\n"));
+ show_msg(buffer);
+ return ret;
+ }
+
+ /* Ignore session keepalive packets. */
+ if(CVAL(buffer,0) != SMBkeepalive)
+ break;
+ }
+ show_msg(buffer);
+ return ret;
+}
+
+/****************************************************************************
+ Recv an smb.
+****************************************************************************/
+
+BOOL cli_receive_smb(struct cli_state *cli)
+{
+ extern int smb_read_error;
+ BOOL ret;
+
+ /* fd == -1 causes segfaults -- Tom (tom@ninja.nl) */
+ if (cli->fd == -1)
+ return False;
+
+ again:
+ ret = client_receive_smb(cli->fd,cli->inbuf,cli->timeout);
+
+ if (ret) {
+ /* it might be an oplock break request */
+ if (!(CVAL(cli->inbuf, smb_flg) & FLAG_REPLY) &&
+ CVAL(cli->inbuf,smb_com) == SMBlockingX &&
+ SVAL(cli->inbuf,smb_vwv6) == 0 &&
+ SVAL(cli->inbuf,smb_vwv7) == 0) {
+ if (cli->oplock_handler) {
+ int fnum = SVAL(cli->inbuf,smb_vwv2);
+ unsigned char level = CVAL(cli->inbuf,smb_vwv3+1);
+ if (!cli->oplock_handler(cli, fnum, level)) return False;
+ }
+ /* try to prevent loops */
+ SCVAL(cli->inbuf,smb_com,0xFF);
+ goto again;
+ }
+ }
+
+ /* If the server is not responding, note that now */
+
+ if (!ret) {
+ cli->smb_rw_error = smb_read_error;
+ close(cli->fd);
+ cli->fd = -1;
+ return ret;
+ }
+
+ if (!cli_check_sign_mac(cli, True)) {
+ DEBUG(0, ("SMB Signature verification failed on incoming packet!\n"));
+ cli->smb_rw_error = READ_BAD_SIG;
+ close(cli->fd);
+ cli->fd = -1;
+ return False;
+ };
+ return True;
+}
+
+/****************************************************************************
+ Send an smb to a fd.
+****************************************************************************/
+
+BOOL cli_send_smb(struct cli_state *cli)
+{
+ size_t len;
+ size_t nwritten=0;
+ ssize_t ret;
+
+ /* fd == -1 causes segfaults -- Tom (tom@ninja.nl) */
+ if (cli->fd == -1)
+ return False;
+
+ cli_calculate_sign_mac(cli);
+
+ len = smb_len(cli->outbuf) + 4;
+
+ while (nwritten < len) {
+ ret = write_socket(cli->fd,cli->outbuf+nwritten,len - nwritten);
+ if (ret <= 0) {
+ close(cli->fd);
+ cli->fd = -1;
+ cli->smb_rw_error = WRITE_ERROR;
+ DEBUG(0,("Error writing %d bytes to client. %d (%s)\n",
+ (int)len,(int)ret, strerror(errno) ));
+ return False;
+ }
+ nwritten += ret;
+ }
+ /* Increment the mid so we can tell between responses. */
+ cli->mid++;
+ if (!cli->mid)
+ cli->mid++;
+ return True;
+}
+
+/****************************************************************************
+ Setup basics in a outgoing packet.
+****************************************************************************/
+
+void cli_setup_packet(struct cli_state *cli)
+{
+ cli->rap_error = 0;
+ SSVAL(cli->outbuf,smb_pid,cli->pid);
+ SSVAL(cli->outbuf,smb_uid,cli->vuid);
+ SSVAL(cli->outbuf,smb_mid,cli->mid);
+ if (cli->protocol > PROTOCOL_CORE) {
+ uint16 flags2;
+ SCVAL(cli->outbuf,smb_flg,0x8);
+ flags2 = FLAGS2_LONG_PATH_COMPONENTS;
+ if (cli->capabilities & CAP_UNICODE)
+ flags2 |= FLAGS2_UNICODE_STRINGS;
+ if (cli->capabilities & CAP_STATUS32)
+ flags2 |= FLAGS2_32_BIT_ERROR_CODES;
+ if (cli->use_spnego)
+ flags2 |= FLAGS2_EXTENDED_SECURITY;
+ SSVAL(cli->outbuf,smb_flg2, flags2);
+ }
+}
+
+/****************************************************************************
+ Setup the bcc length of the packet from a pointer to the end of the data.
+****************************************************************************/
+
+void cli_setup_bcc(struct cli_state *cli, void *p)
+{
+ set_message_bcc(cli->outbuf, PTR_DIFF(p, smb_buf(cli->outbuf)));
+}
+
+/****************************************************************************
+ Initialise credentials of a client structure.
+****************************************************************************/
+
+void cli_init_creds(struct cli_state *cli, const struct ntuser_creds *usr)
+{
+ /* copy_nt_creds(&cli->usr, usr); */
+ fstrcpy(cli->domain , usr->domain);
+ fstrcpy(cli->user_name, usr->user_name);
+ memcpy(&cli->pwd, &usr->pwd, sizeof(usr->pwd));
+
+ DEBUG(10,("cli_init_creds: user %s domain %s\n",
+ cli->user_name, cli->domain));
+}
+
+/****************************************************************************
+ Set the signing state (used from the command line).
+****************************************************************************/
+
+void cli_setup_signing_state(struct cli_state *cli, int signing_state)
+{
+ if (signing_state == Undefined)
+ return;
+
+ if (signing_state == False) {
+ cli->sign_info.allow_smb_signing = False;
+ cli->sign_info.mandatory_signing = False;
+ return;
+ }
+
+ cli->sign_info.allow_smb_signing = True;
+
+ if (signing_state == Required)
+ cli->sign_info.mandatory_signing = True;
+}
+
+/****************************************************************************
+ Initialise a client structure.
+****************************************************************************/
+
+struct cli_state *cli_initialise(struct cli_state *cli)
+{
+ BOOL alloced_cli = False;
+
+ /* Check the effective uid - make sure we are not setuid */
+ if (is_setuid_root()) {
+ DEBUG(0,("libsmb based programs must *NOT* be setuid root.\n"));
+ return NULL;
+ }
+
+ if (!cli) {
+ cli = (struct cli_state *)malloc(sizeof(*cli));
+ if (!cli)
+ return NULL;
+ ZERO_STRUCTP(cli);
+ alloced_cli = True;
+ }
+
+ if (cli->initialised)
+ cli_close_connection(cli);
+
+ ZERO_STRUCTP(cli);
+
+ cli->port = 0;
+ cli->fd = -1;
+ cli->cnum = -1;
+ cli->pid = (uint16)sys_getpid();
+ cli->mid = 1;
+ cli->vuid = UID_FIELD_INVALID;
+ cli->protocol = PROTOCOL_NT1;
+ cli->timeout = 20000; /* Timeout is in milliseconds. */
+ cli->bufsize = CLI_BUFFER_SIZE+4;
+ cli->max_xmit = cli->bufsize;
+ cli->outbuf = (char *)malloc(cli->bufsize+SAFETY_MARGIN);
+ cli->inbuf = (char *)malloc(cli->bufsize+SAFETY_MARGIN);
+ cli->oplock_handler = cli_oplock_ack;
+
+ cli->use_spnego = lp_client_use_spnego();
+
+ cli->capabilities = CAP_UNICODE | CAP_STATUS32;
+
+ /* Set the CLI_FORCE_DOSERR environment variable to test
+ client routines using DOS errors instead of STATUS32
+ ones. This intended only as a temporary hack. */
+ if (getenv("CLI_FORCE_DOSERR"))
+ cli->force_dos_errors = True;
+
+ if (lp_client_signing())
+ cli->sign_info.allow_smb_signing = True;
+
+ if (lp_client_signing() == Required)
+ cli->sign_info.mandatory_signing = True;
+
+ if (!cli->outbuf || !cli->inbuf)
+ goto error;
+
+ if ((cli->mem_ctx = talloc_init("cli based talloc")) == NULL)
+ goto error;
+
+ memset(cli->outbuf, 0, cli->bufsize);
+ memset(cli->inbuf, 0, cli->bufsize);
+
+
+#if defined(DEVELOPER)
+ /* just because we over-allocate, doesn't mean it's right to use it */
+ clobber_region(FUNCTION_MACRO, __LINE__, cli->outbuf+cli->bufsize, SAFETY_MARGIN);
+ clobber_region(FUNCTION_MACRO, __LINE__, cli->inbuf+cli->bufsize, SAFETY_MARGIN);
+#endif
+
+ /* initialise signing */
+ cli_null_set_signing(cli);
+
+ cli->nt_pipe_fnum = 0;
+ cli->saved_netlogon_pipe_fnum = 0;
+
+ cli->initialised = 1;
+ cli->allocated = alloced_cli;
+
+ cli->pipe_idx = -1;
+
+ return cli;
+
+ /* Clean up after malloc() error */
+
+ error:
+
+ SAFE_FREE(cli->inbuf);
+ SAFE_FREE(cli->outbuf);
+
+ if (alloced_cli)
+ SAFE_FREE(cli);
+
+ return NULL;
+}
+
+/****************************************************************************
+close the session
+****************************************************************************/
+
+void cli_nt_session_close(struct cli_state *cli)
+{
+ if (cli->ntlmssp_pipe_state) {
+ ntlmssp_end(&cli->ntlmssp_pipe_state);
+ }
+
+ if (cli->nt_pipe_fnum != 0)
+ cli_close(cli, cli->nt_pipe_fnum);
+
+ cli->nt_pipe_fnum = 0;
+ cli->pipe_idx = -1;
+}
+
+/****************************************************************************
+close the NETLOGON session holding the session key for NETSEC
+****************************************************************************/
+
+void cli_nt_netlogon_netsec_session_close(struct cli_state *cli)
+{
+ if (cli->saved_netlogon_pipe_fnum != 0) {
+ cli_close(cli, cli->saved_netlogon_pipe_fnum);
+ cli->saved_netlogon_pipe_fnum = 0;
+ }
+}
+
+/****************************************************************************
+ Close a client connection and free the memory without destroying cli itself.
+****************************************************************************/
+
+void cli_close_connection(struct cli_state *cli)
+{
+ /*
+ * tell our peer to free his resources. Wihtout this, when an
+ * application attempts to do a graceful shutdown and calls
+ * smbc_free_context() to clean up all connections, some connections
+ * can remain active on the peer end, until some (long) timeout period
+ * later. This tree disconnect forces the peer to clean up, since the
+ * connection will be going away.
+ */
+ if ( cli->cnum != (uint16)-1 )
+ cli_tdis(cli);
+
+ cli_nt_session_close(cli);
+ cli_nt_netlogon_netsec_session_close(cli);
+
+ SAFE_FREE(cli->outbuf);
+ SAFE_FREE(cli->inbuf);
+
+ cli_free_signing_context(cli);
+ data_blob_free(&cli->secblob);
+ data_blob_free(&cli->user_session_key);
+
+ if (cli->ntlmssp_pipe_state)
+ ntlmssp_end(&cli->ntlmssp_pipe_state);
+
+ if (cli->mem_ctx) {
+ talloc_destroy(cli->mem_ctx);
+ cli->mem_ctx = NULL;
+ }
+
+ if (cli->fd != -1)
+ close(cli->fd);
+ cli->fd = -1;
+ cli->smb_rw_error = 0;
+
+}
+
+/****************************************************************************
+ Shutdown a client structure.
+****************************************************************************/
+
+void cli_shutdown(struct cli_state *cli)
+{
+ BOOL allocated = cli->allocated;
+ cli_close_connection(cli);
+ ZERO_STRUCTP(cli);
+ if (allocated)
+ free(cli);
+}
+
+/****************************************************************************
+ Set socket options on a open connection.
+****************************************************************************/
+
+void cli_sockopt(struct cli_state *cli, const char *options)
+{
+ set_socket_options(cli->fd, options);
+}
+
+/****************************************************************************
+ Set the PID to use for smb messages. Return the old pid.
+****************************************************************************/
+
+uint16 cli_setpid(struct cli_state *cli, uint16 pid)
+{
+ uint16 ret = cli->pid;
+ cli->pid = pid;
+ return ret;
+}
+
+/****************************************************************************
+Send a keepalive packet to the server
+****************************************************************************/
+BOOL cli_send_keepalive(struct cli_state *cli)
+{
+ if (cli->fd == -1) {
+ DEBUG(3, ("cli_send_keepalive: fd == -1\n"));
+ return False;
+ }
+ if (!send_keepalive(cli->fd)) {
+ close(cli->fd);
+ cli->fd = -1;
+ DEBUG(0,("Error sending keepalive packet to client.\n"));
+ return False;
+ }
+ return True;
+}
diff --git a/source/libsmb/clierror.c b/source/libsmb/clierror.c
new file mode 100644
index 00000000000..c27e1955e20
--- /dev/null
+++ b/source/libsmb/clierror.c
@@ -0,0 +1,405 @@
+/*
+ Unix SMB/CIFS implementation.
+ client error handling routines
+ Copyright (C) Andrew Tridgell 1994-1998
+ 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 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.
+*/
+
+#define NO_SYSLOG
+
+#include "includes.h"
+
+/*****************************************************
+ RAP error codes - a small start but will be extended.
+
+ XXX: Perhaps these should move into a common function because they're
+ duplicated in clirap2.c
+
+*******************************************************/
+
+static const struct
+{
+ int err;
+ const char *message;
+} rap_errmap[] =
+{
+ {5, "RAP5: User has insufficient privilege" },
+ {50, "RAP50: Not supported by server" },
+ {65, "RAP65: Access denied" },
+ {86, "RAP86: The specified password is invalid" },
+ {2220, "RAP2220: Group does not exist" },
+ {2221, "RAP2221: User does not exist" },
+ {2226, "RAP2226: Operation only permitted on a Primary Domain Controller" },
+ {2237, "RAP2237: User is not in group" },
+ {2242, "RAP2242: The password of this user has expired." },
+ {2243, "RAP2243: The password of this user cannot change." },
+ {2244, "RAP2244: This password cannot be used now (password history conflict)." },
+ {2245, "RAP2245: The password is shorter than required." },
+ {2246, "RAP2246: The password of this user is too recent to change."},
+
+ /* these really shouldn't be here ... */
+ {0x80, "Not listening on called name"},
+ {0x81, "Not listening for calling name"},
+ {0x82, "Called name not present"},
+ {0x83, "Called name present, but insufficient resources"},
+
+ {0, NULL}
+};
+
+/****************************************************************************
+ return a description of an SMB error
+****************************************************************************/
+static const char *cli_smb_errstr(struct cli_state *cli)
+{
+ return smb_dos_errstr(cli->inbuf);
+}
+
+/***************************************************************************
+ Return an error message - either an NT error, SMB error or a RAP error.
+ Note some of the NT errors are actually warnings or "informational" errors
+ in which case they can be safely ignored.
+****************************************************************************/
+
+const char *cli_errstr(struct cli_state *cli)
+{
+ static fstring cli_error_message;
+ uint32 flgs2 = SVAL(cli->inbuf,smb_flg2), errnum;
+ uint8 errclass;
+ int i;
+
+ if (!cli->initialised) {
+ fstrcpy(cli_error_message, "[Programmer's error] cli_errstr called on unitialized cli_stat struct!\n");
+ return cli_error_message;
+ }
+
+ /* Was it server socket error ? */
+ if (cli->fd == -1 && cli->smb_rw_error) {
+ switch(cli->smb_rw_error) {
+ case READ_TIMEOUT:
+ slprintf(cli_error_message, sizeof(cli_error_message) - 1,
+ "Call timed out: server did not respond after %d milliseconds",
+ cli->timeout);
+ break;
+ case READ_EOF:
+ slprintf(cli_error_message, sizeof(cli_error_message) - 1,
+ "Call returned zero bytes (EOF)" );
+ break;
+ case READ_ERROR:
+ slprintf(cli_error_message, sizeof(cli_error_message) - 1,
+ "Read error: %s", strerror(errno) );
+ break;
+ case WRITE_ERROR:
+ slprintf(cli_error_message, sizeof(cli_error_message) - 1,
+ "Write error: %s", strerror(errno) );
+ break;
+ case READ_BAD_SIG:
+ slprintf(cli_error_message, sizeof(cli_error_message) - 1,
+ "Server packet had invalid SMB signature!");
+ break;
+ default:
+ slprintf(cli_error_message, sizeof(cli_error_message) - 1,
+ "Unknown error code %d\n", cli->smb_rw_error );
+ break;
+ }
+ return cli_error_message;
+ }
+
+ /* Case #1: RAP error */
+ if (cli->rap_error) {
+ for (i = 0; rap_errmap[i].message != NULL; i++) {
+ if (rap_errmap[i].err == cli->rap_error) {
+ return rap_errmap[i].message;
+ }
+ }
+
+ slprintf(cli_error_message, sizeof(cli_error_message) - 1, "RAP code %d",
+ cli->rap_error);
+
+ return cli_error_message;
+ }
+
+ /* Case #2: 32-bit NT errors */
+ if (flgs2 & FLAGS2_32_BIT_ERROR_CODES) {
+ NTSTATUS status = NT_STATUS(IVAL(cli->inbuf,smb_rcls));
+
+ return nt_errstr(status);
+ }
+
+ cli_dos_error(cli, &errclass, &errnum);
+
+ /* Case #3: SMB error */
+
+ return cli_smb_errstr(cli);
+}
+
+
+/* Return the 32-bit NT status code from the last packet */
+NTSTATUS cli_nt_error(struct cli_state *cli)
+{
+ int flgs2 = SVAL(cli->inbuf,smb_flg2);
+
+ if (!(flgs2 & FLAGS2_32_BIT_ERROR_CODES)) {
+ int class = CVAL(cli->inbuf,smb_rcls);
+ int code = SVAL(cli->inbuf,smb_err);
+ return dos_to_ntstatus(class, code);
+ }
+
+ return NT_STATUS(IVAL(cli->inbuf,smb_rcls));
+}
+
+
+/* Return the DOS error from the last packet - an error class and an error
+ code. */
+void cli_dos_error(struct cli_state *cli, uint8 *eclass, uint32 *ecode)
+{
+ int flgs2;
+ char rcls;
+ int code;
+
+ if(!cli->initialised) return;
+
+ flgs2 = SVAL(cli->inbuf,smb_flg2);
+
+ if (flgs2 & FLAGS2_32_BIT_ERROR_CODES) {
+ NTSTATUS ntstatus = NT_STATUS(IVAL(cli->inbuf, smb_rcls));
+ ntstatus_to_dos(ntstatus, eclass, ecode);
+ return;
+ }
+
+ rcls = CVAL(cli->inbuf,smb_rcls);
+ code = SVAL(cli->inbuf,smb_err);
+
+ if (eclass) *eclass = rcls;
+ if (ecode) *ecode = code;
+}
+
+/* Return a UNIX errno from a dos error class, error number tuple */
+
+static int cli_errno_from_dos(uint8 eclass, uint32 num)
+{
+ if (eclass == ERRDOS) {
+ switch (num) {
+ case ERRbadfile: return ENOENT;
+ case ERRbadpath: return ENOTDIR;
+ case ERRnoaccess: return EACCES;
+ case ERRfilexists: return EEXIST;
+ case ERRrename: return EEXIST;
+ case ERRbadshare: return EBUSY;
+ case ERRlock: return EBUSY;
+ case ERRinvalidname: return ENOENT;
+ case ERRnosuchshare: return ENODEV;
+ }
+ }
+
+ if (eclass == ERRSRV) {
+ switch (num) {
+ case ERRbadpw: return EPERM;
+ case ERRaccess: return EACCES;
+ case ERRnoresource: return ENOMEM;
+ case ERRinvdevice: return ENODEV;
+ case ERRinvnetname: return ENODEV;
+ }
+ }
+
+ /* for other cases */
+ return EINVAL;
+}
+
+/* Return a UNIX errno from a NT status code */
+static struct {
+ NTSTATUS status;
+ int error;
+} nt_errno_map[] = {
+ {NT_STATUS_ACCESS_VIOLATION, EACCES},
+ {NT_STATUS_INVALID_HANDLE, EBADF},
+ {NT_STATUS_ACCESS_DENIED, EACCES},
+ {NT_STATUS_OBJECT_NAME_NOT_FOUND, ENOENT},
+ {NT_STATUS_SHARING_VIOLATION, EBUSY},
+ {NT_STATUS_OBJECT_PATH_INVALID, ENOTDIR},
+ {NT_STATUS_OBJECT_NAME_COLLISION, EEXIST},
+ {NT_STATUS_PATH_NOT_COVERED, ENOENT},
+ {NT_STATUS_UNSUCCESSFUL, EINVAL},
+ {NT_STATUS_NOT_IMPLEMENTED, ENOSYS},
+ {NT_STATUS_IN_PAGE_ERROR, EFAULT},
+ {NT_STATUS_BAD_NETWORK_NAME, ENOENT},
+#ifdef EDQUOT
+ {NT_STATUS_PAGEFILE_QUOTA, EDQUOT},
+ {NT_STATUS_QUOTA_EXCEEDED, EDQUOT},
+ {NT_STATUS_REGISTRY_QUOTA_LIMIT, EDQUOT},
+ {NT_STATUS_LICENSE_QUOTA_EXCEEDED, EDQUOT},
+#endif
+#ifdef ETIME
+ {NT_STATUS_TIMER_NOT_CANCELED, ETIME},
+#endif
+ {NT_STATUS_INVALID_PARAMETER, EINVAL},
+ {NT_STATUS_NO_SUCH_DEVICE, ENODEV},
+ {NT_STATUS_NO_SUCH_FILE, ENOENT},
+#ifdef ENODATA
+ {NT_STATUS_END_OF_FILE, ENODATA},
+#endif
+#ifdef ENOMEDIUM
+ {NT_STATUS_NO_MEDIA_IN_DEVICE, ENOMEDIUM},
+ {NT_STATUS_NO_MEDIA, ENOMEDIUM},
+#endif
+ {NT_STATUS_NONEXISTENT_SECTOR, ESPIPE},
+ {NT_STATUS_NO_MEMORY, ENOMEM},
+ {NT_STATUS_CONFLICTING_ADDRESSES, EADDRINUSE},
+ {NT_STATUS_NOT_MAPPED_VIEW, EINVAL},
+ {NT_STATUS_UNABLE_TO_FREE_VM, EADDRINUSE},
+ {NT_STATUS_ACCESS_DENIED, EACCES},
+ {NT_STATUS_BUFFER_TOO_SMALL, ENOBUFS},
+ {NT_STATUS_WRONG_PASSWORD, EACCES},
+ {NT_STATUS_LOGON_FAILURE, EACCES},
+ {NT_STATUS_INVALID_WORKSTATION, EACCES},
+ {NT_STATUS_INVALID_LOGON_HOURS, EACCES},
+ {NT_STATUS_PASSWORD_EXPIRED, EACCES},
+ {NT_STATUS_ACCOUNT_DISABLED, EACCES},
+ {NT_STATUS_DISK_FULL, ENOSPC},
+ {NT_STATUS_INVALID_PIPE_STATE, EPIPE},
+ {NT_STATUS_PIPE_BUSY, EPIPE},
+ {NT_STATUS_PIPE_DISCONNECTED, EPIPE},
+ {NT_STATUS_PIPE_NOT_AVAILABLE, ENOSYS},
+ {NT_STATUS_FILE_IS_A_DIRECTORY, EISDIR},
+ {NT_STATUS_NOT_SUPPORTED, ENOSYS},
+ {NT_STATUS_NOT_A_DIRECTORY, ENOTDIR},
+ {NT_STATUS_DIRECTORY_NOT_EMPTY, ENOTEMPTY},
+ {NT_STATUS_NETWORK_UNREACHABLE, ENETUNREACH},
+ {NT_STATUS_HOST_UNREACHABLE, EHOSTUNREACH},
+ {NT_STATUS_CONNECTION_ABORTED, ECONNABORTED},
+ {NT_STATUS_CONNECTION_REFUSED, ECONNREFUSED},
+ {NT_STATUS_TOO_MANY_LINKS, EMLINK},
+ {NT_STATUS_NETWORK_BUSY, EBUSY},
+ {NT_STATUS_DEVICE_DOES_NOT_EXIST, ENODEV},
+#ifdef ELIBACC
+ {NT_STATUS_DLL_NOT_FOUND, ELIBACC},
+#endif
+ {NT_STATUS_PIPE_BROKEN, EPIPE},
+ {NT_STATUS_REMOTE_NOT_LISTENING, ECONNREFUSED},
+ {NT_STATUS_NETWORK_ACCESS_DENIED, EACCES},
+ {NT_STATUS_TOO_MANY_OPENED_FILES, EMFILE},
+#ifdef EPROTO
+ {NT_STATUS_DEVICE_PROTOCOL_ERROR, EPROTO},
+#endif
+ {NT_STATUS_FLOAT_OVERFLOW, ERANGE},
+ {NT_STATUS_FLOAT_UNDERFLOW, ERANGE},
+ {NT_STATUS_INTEGER_OVERFLOW, ERANGE},
+ {NT_STATUS_MEDIA_WRITE_PROTECTED, EROFS},
+ {NT_STATUS_PIPE_CONNECTED, EISCONN},
+ {NT_STATUS_MEMORY_NOT_ALLOCATED, EFAULT},
+ {NT_STATUS_FLOAT_INEXACT_RESULT, ERANGE},
+ {NT_STATUS_ILL_FORMED_PASSWORD, EACCES},
+ {NT_STATUS_PASSWORD_RESTRICTION, EACCES},
+ {NT_STATUS_ACCOUNT_RESTRICTION, EACCES},
+ {NT_STATUS_PORT_CONNECTION_REFUSED, ECONNREFUSED},
+ {NT_STATUS_NAME_TOO_LONG, ENAMETOOLONG},
+ {NT_STATUS_REMOTE_DISCONNECT, ESHUTDOWN},
+ {NT_STATUS_CONNECTION_DISCONNECTED, ECONNABORTED},
+ {NT_STATUS_CONNECTION_RESET, ENETRESET},
+#ifdef ENOTUNIQ
+ {NT_STATUS_IP_ADDRESS_CONFLICT1, ENOTUNIQ},
+ {NT_STATUS_IP_ADDRESS_CONFLICT2, ENOTUNIQ},
+#endif
+ {NT_STATUS_PORT_MESSAGE_TOO_LONG, EMSGSIZE},
+ {NT_STATUS_PROTOCOL_UNREACHABLE, ENOPROTOOPT},
+ {NT_STATUS_ADDRESS_ALREADY_EXISTS, EADDRINUSE},
+ {NT_STATUS_PORT_UNREACHABLE, EHOSTUNREACH},
+ {NT_STATUS_IO_TIMEOUT, ETIMEDOUT},
+ {NT_STATUS_RETRY, EAGAIN},
+#ifdef ECOMM
+ {NT_STATUS_NET_WRITE_FAULT, ECOMM},
+#endif
+
+ {NT_STATUS(0), 0}
+};
+
+static int cli_errno_from_nt(NTSTATUS status)
+{
+ int i;
+ DEBUG(10,("cli_errno_from_nt: 32 bit codes: code=%08x\n", NT_STATUS_V(status)));
+
+ /* Status codes without this bit set are not errors */
+
+ if (!(NT_STATUS_V(status) & 0xc0000000))
+ return 0;
+
+ for (i=0;nt_errno_map[i].error;i++) {
+ if (NT_STATUS_V(nt_errno_map[i].status) ==
+ NT_STATUS_V(status)) return nt_errno_map[i].error;
+ }
+
+ /* for all other cases - a default code */
+ return EINVAL;
+}
+
+/* Return a UNIX errno appropriate for the error received in the last
+ packet. */
+
+int cli_errno(struct cli_state *cli)
+{
+ NTSTATUS status;
+
+ if (cli_is_dos_error(cli)) {
+ uint8 eclass;
+ uint32 ecode;
+
+ cli_dos_error(cli, &eclass, &ecode);
+ return cli_errno_from_dos(eclass, ecode);
+ }
+
+ status = cli_nt_error(cli);
+
+ return cli_errno_from_nt(status);
+}
+
+/* Return true if the last packet was in error */
+
+BOOL cli_is_error(struct cli_state *cli)
+{
+ uint32 flgs2 = SVAL(cli->inbuf,smb_flg2), rcls = 0;
+
+ if (cli->fd == -1 && cli->smb_rw_error != 0)
+ return True;
+
+ if (flgs2 & FLAGS2_32_BIT_ERROR_CODES) {
+ /* Return error is error bits are set */
+ rcls = IVAL(cli->inbuf, smb_rcls);
+ return (rcls & 0xF0000000) == 0xC0000000;
+ }
+
+ /* Return error if error class in non-zero */
+
+ rcls = CVAL(cli->inbuf, smb_rcls);
+ return rcls != 0;
+}
+
+/* Return true if the last error was an NT error */
+
+BOOL cli_is_nt_error(struct cli_state *cli)
+{
+ uint32 flgs2 = SVAL(cli->inbuf,smb_flg2);
+
+ return cli_is_error(cli) && (flgs2 & FLAGS2_32_BIT_ERROR_CODES);
+}
+
+/* Return true if the last error was a DOS error */
+
+BOOL cli_is_dos_error(struct cli_state *cli)
+{
+ uint32 flgs2 = SVAL(cli->inbuf,smb_flg2);
+
+ return cli_is_error(cli) && !(flgs2 & FLAGS2_32_BIT_ERROR_CODES);
+}
diff --git a/source/libsmb/clifile.c b/source/libsmb/clifile.c
new file mode 100644
index 00000000000..bf7923ec788
--- /dev/null
+++ b/source/libsmb/clifile.c
@@ -0,0 +1,1417 @@
+/*
+ Unix SMB/CIFS implementation.
+ client file operations
+ Copyright (C) Andrew Tridgell 1994-1998
+ Copyright (C) Jeremy Allison 2001-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 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.
+*/
+
+#define NO_SYSLOG
+
+#include "includes.h"
+
+/****************************************************************************
+ Hard/Symlink a file (UNIX extensions).
+****************************************************************************/
+
+static BOOL cli_link_internal(struct cli_state *cli, const char *fname_src, const char *fname_dst, BOOL hard_link)
+{
+ unsigned int data_len = 0;
+ unsigned int param_len = 0;
+ uint16 setup = TRANSACT2_SETPATHINFO;
+ char param[sizeof(pstring)+6];
+ pstring data;
+ char *rparam=NULL, *rdata=NULL;
+ char *p;
+ size_t srclen = 2*(strlen(fname_src)+1);
+ size_t destlen = 2*(strlen(fname_dst) + 1);
+
+ memset(param, 0, sizeof(param));
+ SSVAL(param,0,hard_link ? SMB_SET_FILE_UNIX_HLINK : SMB_SET_FILE_UNIX_LINK);
+ p = &param[6];
+
+ p += clistr_push(cli, p, fname_src, MIN(srclen, sizeof(param)-6), STR_TERMINATE);
+ param_len = PTR_DIFF(p, param);
+
+ p = data;
+ p += clistr_push(cli, p, fname_dst, MIN(destlen,sizeof(data)), STR_TERMINATE);
+ data_len = PTR_DIFF(p, data);
+
+ if (!cli_send_trans(cli, SMBtrans2,
+ NULL, /* name */
+ -1, 0, /* fid, flags */
+ &setup, 1, 0, /* setup, length, max */
+ param, param_len, 2, /* param, length, max */
+ (char *)&data, data_len, cli->max_xmit /* data, length, max */
+ )) {
+ return False;
+ }
+
+ if (!cli_receive_trans(cli, SMBtrans2,
+ &rparam, &param_len,
+ &rdata, &data_len)) {
+ return False;
+ }
+
+ SAFE_FREE(rdata);
+ SAFE_FREE(rparam);
+
+ return True;
+}
+
+/****************************************************************************
+ Map standard UNIX permissions onto wire representations.
+****************************************************************************/
+
+uint32 unix_perms_to_wire(mode_t perms)
+{
+ unsigned int 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).
+****************************************************************************/
+
+BOOL cli_unix_symlink(struct cli_state *cli, const char *fname_src, const char *fname_dst)
+{
+ return cli_link_internal(cli, fname_src, fname_dst, False);
+}
+
+/****************************************************************************
+ Hard a file (UNIX extensions).
+****************************************************************************/
+
+BOOL cli_unix_hardlink(struct cli_state *cli, const char *fname_src, const char *fname_dst)
+{
+ return cli_link_internal(cli, fname_src, fname_dst, True);
+}
+
+/****************************************************************************
+ Chmod or chown a file internal (UNIX extensions).
+****************************************************************************/
+
+static BOOL cli_unix_chmod_chown_internal(struct cli_state *cli, const char *fname, uint32 mode, uint32 uid, uint32 gid)
+{
+ unsigned int data_len = 0;
+ unsigned int param_len = 0;
+ uint16 setup = TRANSACT2_SETPATHINFO;
+ char param[sizeof(pstring)+6];
+ char data[100];
+ char *rparam=NULL, *rdata=NULL;
+ char *p;
+
+ memset(param, 0, sizeof(param));
+ memset(data, 0, sizeof(data));
+ SSVAL(param,0,SMB_SET_FILE_UNIX_BASIC);
+ p = &param[6];
+
+ p += clistr_push(cli, p, fname, -1, STR_TERMINATE);
+ param_len = PTR_DIFF(p, param);
+
+ SIVAL(data,40,uid);
+ SIVAL(data,48,gid);
+ SIVAL(data,84,mode);
+
+ data_len = 100;
+
+ if (!cli_send_trans(cli, SMBtrans2,
+ NULL, /* name */
+ -1, 0, /* fid, flags */
+ &setup, 1, 0, /* setup, length, max */
+ param, param_len, 2, /* param, length, max */
+ (char *)&data, data_len, cli->max_xmit /* data, length, max */
+ )) {
+ return False;
+ }
+
+ if (!cli_receive_trans(cli, SMBtrans2,
+ &rparam, &param_len,
+ &rdata, &data_len)) {
+ return False;
+ }
+
+ SAFE_FREE(rdata);
+ SAFE_FREE(rparam);
+
+ return True;
+}
+
+/****************************************************************************
+ chmod a file (UNIX extensions).
+****************************************************************************/
+
+BOOL cli_unix_chmod(struct cli_state *cli, const char *fname, mode_t mode)
+{
+ return cli_unix_chmod_chown_internal(cli, fname,
+ unix_perms_to_wire(mode), SMB_UID_NO_CHANGE, SMB_GID_NO_CHANGE);
+}
+
+/****************************************************************************
+ chown a file (UNIX extensions).
+****************************************************************************/
+
+BOOL cli_unix_chown(struct cli_state *cli, const char *fname, uid_t uid, gid_t gid)
+{
+ return cli_unix_chmod_chown_internal(cli, fname, SMB_MODE_NO_CHANGE, (uint32)uid, (uint32)gid);
+}
+
+/****************************************************************************
+ Rename a file.
+****************************************************************************/
+
+BOOL cli_rename(struct cli_state *cli, const char *fname_src, const char *fname_dst)
+{
+ char *p;
+
+ memset(cli->outbuf,'\0',smb_size);
+ memset(cli->inbuf,'\0',smb_size);
+
+ set_message(cli->outbuf,1, 0, True);
+
+ SCVAL(cli->outbuf,smb_com,SMBmv);
+ SSVAL(cli->outbuf,smb_tid,cli->cnum);
+ cli_setup_packet(cli);
+
+ SSVAL(cli->outbuf,smb_vwv0,aSYSTEM | aHIDDEN | aDIR);
+
+ p = smb_buf(cli->outbuf);
+ *p++ = 4;
+ p += clistr_push(cli, p, fname_src, -1, STR_TERMINATE);
+ *p++ = 4;
+ p += clistr_push(cli, p, fname_dst, -1, STR_TERMINATE);
+
+ cli_setup_bcc(cli, p);
+
+ cli_send_smb(cli);
+ if (!cli_receive_smb(cli))
+ return False;
+
+ if (cli_is_error(cli))
+ return False;
+
+ return True;
+}
+
+/****************************************************************************
+ NT Rename a file.
+****************************************************************************/
+
+BOOL cli_ntrename(struct cli_state *cli, const char *fname_src, const char *fname_dst)
+{
+ char *p;
+
+ memset(cli->outbuf,'\0',smb_size);
+ memset(cli->inbuf,'\0',smb_size);
+
+ set_message(cli->outbuf, 4, 0, True);
+
+ SCVAL(cli->outbuf,smb_com,SMBntrename);
+ SSVAL(cli->outbuf,smb_tid,cli->cnum);
+ cli_setup_packet(cli);
+
+ SSVAL(cli->outbuf,smb_vwv0,aSYSTEM | aHIDDEN | aDIR);
+ SSVAL(cli->outbuf,smb_vwv1, RENAME_FLAG_RENAME);
+
+ p = smb_buf(cli->outbuf);
+ *p++ = 4;
+ p += clistr_push(cli, p, fname_src, -1, STR_TERMINATE);
+ *p++ = 4;
+ p += clistr_push(cli, p, fname_dst, -1, STR_TERMINATE);
+
+ cli_setup_bcc(cli, p);
+
+ cli_send_smb(cli);
+ if (!cli_receive_smb(cli))
+ return False;
+
+ if (cli_is_error(cli))
+ return False;
+
+ return True;
+}
+
+/****************************************************************************
+ NT hardlink a file.
+****************************************************************************/
+
+BOOL cli_nt_hardlink(struct cli_state *cli, const char *fname_src, const char *fname_dst)
+{
+ char *p;
+
+ memset(cli->outbuf,'\0',smb_size);
+ memset(cli->inbuf,'\0',smb_size);
+
+ set_message(cli->outbuf, 4, 0, True);
+
+ SCVAL(cli->outbuf,smb_com,SMBntrename);
+ SSVAL(cli->outbuf,smb_tid,cli->cnum);
+ cli_setup_packet(cli);
+
+ SSVAL(cli->outbuf,smb_vwv0,aSYSTEM | aHIDDEN | aDIR);
+ SSVAL(cli->outbuf,smb_vwv1, RENAME_FLAG_HARD_LINK);
+
+ p = smb_buf(cli->outbuf);
+ *p++ = 4;
+ p += clistr_push(cli, p, fname_src, -1, STR_TERMINATE);
+ *p++ = 4;
+ p += clistr_push(cli, p, fname_dst, -1, STR_TERMINATE);
+
+ cli_setup_bcc(cli, p);
+
+ cli_send_smb(cli);
+ if (!cli_receive_smb(cli))
+ return False;
+
+ if (cli_is_error(cli))
+ return False;
+
+ return True;
+}
+
+/****************************************************************************
+ Delete a file.
+****************************************************************************/
+
+BOOL cli_unlink(struct cli_state *cli, const char *fname)
+{
+ char *p;
+
+ memset(cli->outbuf,'\0',smb_size);
+ memset(cli->inbuf,'\0',smb_size);
+
+ set_message(cli->outbuf,1, 0,True);
+
+ SCVAL(cli->outbuf,smb_com,SMBunlink);
+ SSVAL(cli->outbuf,smb_tid,cli->cnum);
+ cli_setup_packet(cli);
+
+ SSVAL(cli->outbuf,smb_vwv0,aSYSTEM | aHIDDEN);
+
+ p = smb_buf(cli->outbuf);
+ *p++ = 4;
+ p += clistr_push(cli, p, fname, -1, STR_TERMINATE);
+
+ cli_setup_bcc(cli, p);
+ cli_send_smb(cli);
+ if (!cli_receive_smb(cli)) {
+ return False;
+ }
+
+ if (cli_is_error(cli)) {
+ return False;
+ }
+
+ return True;
+}
+
+/****************************************************************************
+ Create a directory.
+****************************************************************************/
+
+BOOL cli_mkdir(struct cli_state *cli, const char *dname)
+{
+ char *p;
+
+ memset(cli->outbuf,'\0',smb_size);
+ memset(cli->inbuf,'\0',smb_size);
+
+ set_message(cli->outbuf,0, 0,True);
+
+ SCVAL(cli->outbuf,smb_com,SMBmkdir);
+ SSVAL(cli->outbuf,smb_tid,cli->cnum);
+ cli_setup_packet(cli);
+
+ p = smb_buf(cli->outbuf);
+ *p++ = 4;
+ p += clistr_push(cli, p, dname, -1, STR_TERMINATE);
+
+ cli_setup_bcc(cli, p);
+
+ cli_send_smb(cli);
+ if (!cli_receive_smb(cli)) {
+ return False;
+ }
+
+ if (cli_is_error(cli)) {
+ return False;
+ }
+
+ return True;
+}
+
+/****************************************************************************
+ Remove a directory.
+****************************************************************************/
+
+BOOL cli_rmdir(struct cli_state *cli, const char *dname)
+{
+ char *p;
+
+ memset(cli->outbuf,'\0',smb_size);
+ memset(cli->inbuf,'\0',smb_size);
+
+ set_message(cli->outbuf,0, 0, True);
+
+ SCVAL(cli->outbuf,smb_com,SMBrmdir);
+ SSVAL(cli->outbuf,smb_tid,cli->cnum);
+ cli_setup_packet(cli);
+
+ p = smb_buf(cli->outbuf);
+ *p++ = 4;
+ p += clistr_push(cli, p, dname, -1, STR_TERMINATE);
+
+ cli_setup_bcc(cli, p);
+
+ cli_send_smb(cli);
+ if (!cli_receive_smb(cli)) {
+ return False;
+ }
+
+ if (cli_is_error(cli)) {
+ return False;
+ }
+
+ return True;
+}
+
+/****************************************************************************
+ Set or clear the delete on close flag.
+****************************************************************************/
+
+int cli_nt_delete_on_close(struct cli_state *cli, int fnum, BOOL flag)
+{
+ unsigned int data_len = 1;
+ unsigned int param_len = 6;
+ uint16 setup = TRANSACT2_SETFILEINFO;
+ pstring param;
+ unsigned char data;
+ char *rparam=NULL, *rdata=NULL;
+
+ memset(param, 0, param_len);
+ SSVAL(param,0,fnum);
+ SSVAL(param,2,SMB_SET_FILE_DISPOSITION_INFO);
+
+ data = flag ? 1 : 0;
+
+ if (!cli_send_trans(cli, SMBtrans2,
+ NULL, /* name */
+ -1, 0, /* fid, flags */
+ &setup, 1, 0, /* setup, length, max */
+ param, param_len, 2, /* param, length, max */
+ (char *)&data, data_len, cli->max_xmit /* data, length, max */
+ )) {
+ return False;
+ }
+
+ if (!cli_receive_trans(cli, SMBtrans2,
+ &rparam, &param_len,
+ &rdata, &data_len)) {
+ return False;
+ }
+
+ SAFE_FREE(rdata);
+ SAFE_FREE(rparam);
+
+ return True;
+}
+
+/****************************************************************************
+ Open a file - exposing the full horror of the NT API :-).
+ Used in smbtorture.
+****************************************************************************/
+
+int cli_nt_create_full(struct cli_state *cli, const char *fname,
+ uint32 CreatFlags, uint32 DesiredAccess,
+ uint32 FileAttributes, uint32 ShareAccess,
+ uint32 CreateDisposition, uint32 CreateOptions,
+ uint8 SecuityFlags)
+{
+ char *p;
+ int len;
+
+ memset(cli->outbuf,'\0',smb_size);
+ memset(cli->inbuf,'\0',smb_size);
+
+ set_message(cli->outbuf,24,0,True);
+
+ SCVAL(cli->outbuf,smb_com,SMBntcreateX);
+ SSVAL(cli->outbuf,smb_tid,cli->cnum);
+ cli_setup_packet(cli);
+
+ SSVAL(cli->outbuf,smb_vwv0,0xFF);
+ if (cli->use_oplocks)
+ CreatFlags |= (REQUEST_OPLOCK|REQUEST_BATCH_OPLOCK);
+
+ SIVAL(cli->outbuf,smb_ntcreate_Flags, CreatFlags);
+ SIVAL(cli->outbuf,smb_ntcreate_RootDirectoryFid, 0x0);
+ SIVAL(cli->outbuf,smb_ntcreate_DesiredAccess, DesiredAccess);
+ SIVAL(cli->outbuf,smb_ntcreate_FileAttributes, FileAttributes);
+ SIVAL(cli->outbuf,smb_ntcreate_ShareAccess, ShareAccess);
+ SIVAL(cli->outbuf,smb_ntcreate_CreateDisposition, CreateDisposition);
+ SIVAL(cli->outbuf,smb_ntcreate_CreateOptions, CreateOptions);
+ SIVAL(cli->outbuf,smb_ntcreate_ImpersonationLevel, 0x02);
+ SCVAL(cli->outbuf,smb_ntcreate_SecurityFlags, SecuityFlags);
+
+ p = smb_buf(cli->outbuf);
+ /* this alignment and termination is critical for netapp filers. Don't change */
+ p += clistr_align_out(cli, p, 0);
+ len = clistr_push(cli, p, fname, -1, 0);
+ p += len;
+ SSVAL(cli->outbuf,smb_ntcreate_NameLength, len);
+ /* sigh. this copes with broken netapp filer behaviour */
+ p += clistr_push(cli, p, "", -1, STR_TERMINATE);
+
+ cli_setup_bcc(cli, p);
+
+ cli_send_smb(cli);
+ if (!cli_receive_smb(cli)) {
+ return -1;
+ }
+
+ if (cli_is_error(cli)) {
+ return -1;
+ }
+
+ return SVAL(cli->inbuf,smb_vwv2 + 1);
+}
+
+/****************************************************************************
+ Open a file.
+****************************************************************************/
+
+int cli_nt_create(struct cli_state *cli, const char *fname, uint32 DesiredAccess)
+{
+ return cli_nt_create_full(cli, fname, 0, DesiredAccess, 0,
+ FILE_SHARE_READ|FILE_SHARE_WRITE, FILE_EXISTS_OPEN, 0x0, 0x0);
+}
+
+/****************************************************************************
+ Open a file
+ WARNING: if you open with O_WRONLY then getattrE won't work!
+****************************************************************************/
+
+int cli_open(struct cli_state *cli, const char *fname, int flags, int share_mode)
+{
+ char *p;
+ unsigned openfn=0;
+ unsigned accessmode=0;
+
+ if (flags & O_CREAT)
+ openfn |= (1<<4);
+ if (!(flags & O_EXCL)) {
+ if (flags & O_TRUNC)
+ openfn |= (1<<1);
+ else
+ openfn |= (1<<0);
+ }
+
+ accessmode = (share_mode<<4);
+
+ if ((flags & O_ACCMODE) == O_RDWR) {
+ accessmode |= 2;
+ } else if ((flags & O_ACCMODE) == O_WRONLY) {
+ accessmode |= 1;
+ }
+
+#if defined(O_SYNC)
+ if ((flags & O_SYNC) == O_SYNC) {
+ accessmode |= (1<<14);
+ }
+#endif /* O_SYNC */
+
+ if (share_mode == DENY_FCB) {
+ accessmode = 0xFF;
+ }
+
+ memset(cli->outbuf,'\0',smb_size);
+ memset(cli->inbuf,'\0',smb_size);
+
+ set_message(cli->outbuf,15,0,True);
+
+ SCVAL(cli->outbuf,smb_com,SMBopenX);
+ SSVAL(cli->outbuf,smb_tid,cli->cnum);
+ cli_setup_packet(cli);
+
+ SSVAL(cli->outbuf,smb_vwv0,0xFF);
+ SSVAL(cli->outbuf,smb_vwv2,0); /* no additional info */
+ SSVAL(cli->outbuf,smb_vwv3,accessmode);
+ SSVAL(cli->outbuf,smb_vwv4,aSYSTEM | aHIDDEN);
+ SSVAL(cli->outbuf,smb_vwv5,0);
+ SSVAL(cli->outbuf,smb_vwv8,openfn);
+
+ if (cli->use_oplocks) {
+ /* if using oplocks then ask for a batch oplock via
+ core and extended methods */
+ SCVAL(cli->outbuf,smb_flg, CVAL(cli->outbuf,smb_flg)|
+ FLAG_REQUEST_OPLOCK|FLAG_REQUEST_BATCH_OPLOCK);
+ SSVAL(cli->outbuf,smb_vwv2,SVAL(cli->outbuf,smb_vwv2) | 6);
+ }
+
+ p = smb_buf(cli->outbuf);
+ p += clistr_push(cli, p, fname, -1, STR_TERMINATE);
+
+ cli_setup_bcc(cli, p);
+
+ cli_send_smb(cli);
+ if (!cli_receive_smb(cli)) {
+ return -1;
+ }
+
+ if (cli_is_error(cli)) {
+ return -1;
+ }
+
+ return SVAL(cli->inbuf,smb_vwv2);
+}
+
+/****************************************************************************
+ Close a file.
+****************************************************************************/
+
+BOOL cli_close(struct cli_state *cli, int fnum)
+{
+ memset(cli->outbuf,'\0',smb_size);
+ memset(cli->inbuf,'\0',smb_size);
+
+ set_message(cli->outbuf,3,0,True);
+
+ SCVAL(cli->outbuf,smb_com,SMBclose);
+ SSVAL(cli->outbuf,smb_tid,cli->cnum);
+ cli_setup_packet(cli);
+
+ SSVAL(cli->outbuf,smb_vwv0,fnum);
+ SIVALS(cli->outbuf,smb_vwv1,-1);
+
+ cli_send_smb(cli);
+ if (!cli_receive_smb(cli)) {
+ return False;
+ }
+
+ return !cli_is_error(cli);
+}
+
+
+/****************************************************************************
+ send a lock with a specified locktype
+ this is used for testing LOCKING_ANDX_CANCEL_LOCK
+****************************************************************************/
+NTSTATUS cli_locktype(struct cli_state *cli, int fnum,
+ uint32 offset, uint32 len, int timeout, unsigned char locktype)
+{
+ char *p;
+ int saved_timeout = cli->timeout;
+
+ memset(cli->outbuf,'\0',smb_size);
+ memset(cli->inbuf,'\0', smb_size);
+
+ set_message(cli->outbuf,8,0,True);
+
+ SCVAL(cli->outbuf,smb_com,SMBlockingX);
+ SSVAL(cli->outbuf,smb_tid,cli->cnum);
+ cli_setup_packet(cli);
+
+ SCVAL(cli->outbuf,smb_vwv0,0xFF);
+ SSVAL(cli->outbuf,smb_vwv2,fnum);
+ SCVAL(cli->outbuf,smb_vwv3,locktype);
+ SIVALS(cli->outbuf, smb_vwv4, timeout);
+ SSVAL(cli->outbuf,smb_vwv6,0);
+ SSVAL(cli->outbuf,smb_vwv7,1);
+
+ p = smb_buf(cli->outbuf);
+ SSVAL(p, 0, cli->pid);
+ SIVAL(p, 2, offset);
+ SIVAL(p, 6, len);
+
+ p += 10;
+
+ cli_setup_bcc(cli, p);
+
+ cli_send_smb(cli);
+
+ if (timeout != 0) {
+ cli->timeout = (timeout == -1) ? 0x7FFFFFFF : (timeout + 2*1000);
+ }
+
+ if (!cli_receive_smb(cli)) {
+ cli->timeout = saved_timeout;
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ cli->timeout = saved_timeout;
+
+ return cli_nt_error(cli);
+}
+
+
+/****************************************************************************
+ Lock a file.
+ note that timeout is in units of 2 milliseconds
+****************************************************************************/
+BOOL cli_lock(struct cli_state *cli, int fnum,
+ uint32 offset, uint32 len, int timeout, enum brl_type lock_type)
+{
+ char *p;
+ int saved_timeout = cli->timeout;
+
+ memset(cli->outbuf,'\0',smb_size);
+ memset(cli->inbuf,'\0', smb_size);
+
+ set_message(cli->outbuf,8,0,True);
+
+ SCVAL(cli->outbuf,smb_com,SMBlockingX);
+ SSVAL(cli->outbuf,smb_tid,cli->cnum);
+ cli_setup_packet(cli);
+
+ SCVAL(cli->outbuf,smb_vwv0,0xFF);
+ SSVAL(cli->outbuf,smb_vwv2,fnum);
+ SCVAL(cli->outbuf,smb_vwv3,(lock_type == READ_LOCK? 1 : 0));
+ SIVALS(cli->outbuf, smb_vwv4, timeout);
+ SSVAL(cli->outbuf,smb_vwv6,0);
+ SSVAL(cli->outbuf,smb_vwv7,1);
+
+ p = smb_buf(cli->outbuf);
+ SSVAL(p, 0, cli->pid);
+ SIVAL(p, 2, offset);
+ SIVAL(p, 6, len);
+
+ p += 10;
+
+ cli_setup_bcc(cli, p);
+
+ cli_send_smb(cli);
+
+ if (timeout != 0) {
+ cli->timeout = (timeout == -1) ? 0x7FFFFFFF : (timeout*2 + 5*1000);
+ }
+
+ if (!cli_receive_smb(cli)) {
+ cli->timeout = saved_timeout;
+ return False;
+ }
+
+ cli->timeout = saved_timeout;
+
+ if (cli_is_error(cli)) {
+ return False;
+ }
+
+ return True;
+}
+
+/****************************************************************************
+ Unlock a file.
+****************************************************************************/
+
+BOOL cli_unlock(struct cli_state *cli, int fnum, uint32 offset, uint32 len)
+{
+ char *p;
+
+ memset(cli->outbuf,'\0',smb_size);
+ memset(cli->inbuf,'\0',smb_size);
+
+ set_message(cli->outbuf,8,0,True);
+
+ SCVAL(cli->outbuf,smb_com,SMBlockingX);
+ SSVAL(cli->outbuf,smb_tid,cli->cnum);
+ cli_setup_packet(cli);
+
+ SCVAL(cli->outbuf,smb_vwv0,0xFF);
+ SSVAL(cli->outbuf,smb_vwv2,fnum);
+ SCVAL(cli->outbuf,smb_vwv3,0);
+ SIVALS(cli->outbuf, smb_vwv4, 0);
+ SSVAL(cli->outbuf,smb_vwv6,1);
+ SSVAL(cli->outbuf,smb_vwv7,0);
+
+ p = smb_buf(cli->outbuf);
+ SSVAL(p, 0, cli->pid);
+ SIVAL(p, 2, offset);
+ SIVAL(p, 6, len);
+ p += 10;
+ cli_setup_bcc(cli, p);
+ cli_send_smb(cli);
+ if (!cli_receive_smb(cli)) {
+ return False;
+ }
+
+ if (cli_is_error(cli)) {
+ return False;
+ }
+
+ return True;
+}
+
+/****************************************************************************
+ Lock a file with 64 bit offsets.
+****************************************************************************/
+
+BOOL cli_lock64(struct cli_state *cli, int fnum,
+ SMB_BIG_UINT offset, SMB_BIG_UINT len, int timeout, enum brl_type lock_type)
+{
+ char *p;
+ int saved_timeout = cli->timeout;
+ int ltype;
+
+ if (! (cli->capabilities & CAP_LARGE_FILES)) {
+ return cli_lock(cli, fnum, offset, len, timeout, lock_type);
+ }
+
+ ltype = (lock_type == READ_LOCK? 1 : 0);
+ ltype |= LOCKING_ANDX_LARGE_FILES;
+
+ memset(cli->outbuf,'\0',smb_size);
+ memset(cli->inbuf,'\0', smb_size);
+
+ set_message(cli->outbuf,8,0,True);
+
+ SCVAL(cli->outbuf,smb_com,SMBlockingX);
+ SSVAL(cli->outbuf,smb_tid,cli->cnum);
+ cli_setup_packet(cli);
+
+ SCVAL(cli->outbuf,smb_vwv0,0xFF);
+ SSVAL(cli->outbuf,smb_vwv2,fnum);
+ SCVAL(cli->outbuf,smb_vwv3,ltype);
+ SIVALS(cli->outbuf, smb_vwv4, timeout);
+ SSVAL(cli->outbuf,smb_vwv6,0);
+ SSVAL(cli->outbuf,smb_vwv7,1);
+
+ p = smb_buf(cli->outbuf);
+ SIVAL(p, 0, cli->pid);
+ SOFF_T_R(p, 4, offset);
+ SOFF_T_R(p, 12, len);
+ p += 20;
+
+ cli_setup_bcc(cli, p);
+ cli_send_smb(cli);
+
+ if (timeout != 0) {
+ cli->timeout = (timeout == -1) ? 0x7FFFFFFF : (timeout + 5*1000);
+ }
+
+ if (!cli_receive_smb(cli)) {
+ cli->timeout = saved_timeout;
+ return False;
+ }
+
+ cli->timeout = saved_timeout;
+
+ if (cli_is_error(cli)) {
+ return False;
+ }
+
+ return True;
+}
+
+/****************************************************************************
+ Unlock a file with 64 bit offsets.
+****************************************************************************/
+
+BOOL cli_unlock64(struct cli_state *cli, int fnum, SMB_BIG_UINT offset, SMB_BIG_UINT len)
+{
+ char *p;
+
+ if (! (cli->capabilities & CAP_LARGE_FILES)) {
+ return cli_unlock(cli, fnum, offset, len);
+ }
+
+ memset(cli->outbuf,'\0',smb_size);
+ memset(cli->inbuf,'\0',smb_size);
+
+ set_message(cli->outbuf,8,0,True);
+
+ SCVAL(cli->outbuf,smb_com,SMBlockingX);
+ SSVAL(cli->outbuf,smb_tid,cli->cnum);
+ cli_setup_packet(cli);
+
+ SCVAL(cli->outbuf,smb_vwv0,0xFF);
+ SSVAL(cli->outbuf,smb_vwv2,fnum);
+ SCVAL(cli->outbuf,smb_vwv3,LOCKING_ANDX_LARGE_FILES);
+ SIVALS(cli->outbuf, smb_vwv4, 0);
+ SSVAL(cli->outbuf,smb_vwv6,1);
+ SSVAL(cli->outbuf,smb_vwv7,0);
+
+ p = smb_buf(cli->outbuf);
+ SIVAL(p, 0, cli->pid);
+ SOFF_T_R(p, 4, offset);
+ SOFF_T_R(p, 12, len);
+ p += 20;
+ cli_setup_bcc(cli, p);
+ cli_send_smb(cli);
+ if (!cli_receive_smb(cli)) {
+ return False;
+ }
+
+ if (cli_is_error(cli)) {
+ return False;
+ }
+
+ return True;
+}
+
+
+/****************************************************************************
+ Do a SMBgetattrE call.
+****************************************************************************/
+
+BOOL cli_getattrE(struct cli_state *cli, int fd,
+ uint16 *attr, SMB_BIG_UINT *size,
+ time_t *c_time, time_t *a_time, time_t *m_time)
+{
+ memset(cli->outbuf,'\0',smb_size);
+ memset(cli->inbuf,'\0',smb_size);
+
+ set_message(cli->outbuf,1,0,True);
+
+ SCVAL(cli->outbuf,smb_com,SMBgetattrE);
+ SSVAL(cli->outbuf,smb_tid,cli->cnum);
+ cli_setup_packet(cli);
+
+ SSVAL(cli->outbuf,smb_vwv0,fd);
+
+ cli_send_smb(cli);
+ if (!cli_receive_smb(cli)) {
+ return False;
+ }
+
+ if (cli_is_error(cli)) {
+ return False;
+ }
+
+ if (size) {
+ *size = IVAL(cli->inbuf, smb_vwv6);
+ }
+
+ if (attr) {
+ *attr = SVAL(cli->inbuf,smb_vwv10);
+ }
+
+ if (c_time) {
+ *c_time = make_unix_date3(cli->inbuf+smb_vwv0);
+ }
+
+ if (a_time) {
+ *a_time = make_unix_date3(cli->inbuf+smb_vwv2);
+ }
+
+ if (m_time) {
+ *m_time = make_unix_date3(cli->inbuf+smb_vwv4);
+ }
+
+ return True;
+}
+
+/****************************************************************************
+ Do a SMBgetatr call
+****************************************************************************/
+
+BOOL cli_getatr(struct cli_state *cli, const char *fname,
+ uint16 *attr, size_t *size, time_t *t)
+{
+ char *p;
+
+ memset(cli->outbuf,'\0',smb_size);
+ memset(cli->inbuf,'\0',smb_size);
+
+ set_message(cli->outbuf,0,0,True);
+
+ SCVAL(cli->outbuf,smb_com,SMBgetatr);
+ SSVAL(cli->outbuf,smb_tid,cli->cnum);
+ cli_setup_packet(cli);
+
+ p = smb_buf(cli->outbuf);
+ *p++ = 4;
+ p += clistr_push(cli, p, fname, -1, STR_TERMINATE);
+
+ cli_setup_bcc(cli, p);
+
+ cli_send_smb(cli);
+ if (!cli_receive_smb(cli)) {
+ return False;
+ }
+
+ if (cli_is_error(cli)) {
+ return False;
+ }
+
+ if (size) {
+ *size = IVAL(cli->inbuf, smb_vwv3);
+ }
+
+ if (t) {
+ *t = make_unix_date3(cli->inbuf+smb_vwv1);
+ }
+
+ if (attr) {
+ *attr = SVAL(cli->inbuf,smb_vwv0);
+ }
+
+
+ return True;
+}
+
+/****************************************************************************
+ Do a SMBsetatr call.
+****************************************************************************/
+
+BOOL cli_setatr(struct cli_state *cli, const char *fname, uint16 attr, time_t t)
+{
+ char *p;
+
+ memset(cli->outbuf,'\0',smb_size);
+ memset(cli->inbuf,'\0',smb_size);
+
+ set_message(cli->outbuf,8,0,True);
+
+ SCVAL(cli->outbuf,smb_com,SMBsetatr);
+ SSVAL(cli->outbuf,smb_tid,cli->cnum);
+ cli_setup_packet(cli);
+
+ SSVAL(cli->outbuf,smb_vwv0, attr);
+ put_dos_date3(cli->outbuf,smb_vwv1, t);
+
+ p = smb_buf(cli->outbuf);
+ *p++ = 4;
+ p += clistr_push(cli, p, fname, -1, STR_TERMINATE);
+ *p++ = 4;
+
+ cli_setup_bcc(cli, p);
+
+ cli_send_smb(cli);
+ if (!cli_receive_smb(cli)) {
+ return False;
+ }
+
+ if (cli_is_error(cli)) {
+ return False;
+ }
+
+ return True;
+}
+
+/****************************************************************************
+ Check for existance of a dir.
+****************************************************************************/
+BOOL cli_chkpath(struct cli_state *cli, const char *path)
+{
+ pstring path2;
+ char *p;
+
+ pstrcpy(path2,path);
+ trim_char(path2,'\0','\\');
+ if (!*path2)
+ *path2 = '\\';
+
+ memset(cli->outbuf,'\0',smb_size);
+ set_message(cli->outbuf,0,0,True);
+ SCVAL(cli->outbuf,smb_com,SMBchkpth);
+ SSVAL(cli->outbuf,smb_tid,cli->cnum);
+ cli_setup_packet(cli);
+ p = smb_buf(cli->outbuf);
+ *p++ = 4;
+ p += clistr_push(cli, p, path2, -1, STR_TERMINATE);
+
+ cli_setup_bcc(cli, p);
+
+ cli_send_smb(cli);
+ if (!cli_receive_smb(cli)) {
+ return False;
+ }
+
+ if (cli_is_error(cli)) return False;
+
+ return True;
+}
+
+/****************************************************************************
+ Query disk space.
+****************************************************************************/
+
+BOOL cli_dskattr(struct cli_state *cli, int *bsize, int *total, int *avail)
+{
+ memset(cli->outbuf,'\0',smb_size);
+ set_message(cli->outbuf,0,0,True);
+ SCVAL(cli->outbuf,smb_com,SMBdskattr);
+ SSVAL(cli->outbuf,smb_tid,cli->cnum);
+ cli_setup_packet(cli);
+
+ cli_send_smb(cli);
+ if (!cli_receive_smb(cli)) {
+ return False;
+ }
+
+ *bsize = SVAL(cli->inbuf,smb_vwv1)*SVAL(cli->inbuf,smb_vwv2);
+ *total = SVAL(cli->inbuf,smb_vwv0);
+ *avail = SVAL(cli->inbuf,smb_vwv3);
+
+ return True;
+}
+
+/****************************************************************************
+ Create and open a temporary file.
+****************************************************************************/
+
+int cli_ctemp(struct cli_state *cli, const char *path, char **tmp_path)
+{
+ int len;
+ char *p;
+
+ memset(cli->outbuf,'\0',smb_size);
+ memset(cli->inbuf,'\0',smb_size);
+
+ set_message(cli->outbuf,3,0,True);
+
+ SCVAL(cli->outbuf,smb_com,SMBctemp);
+ SSVAL(cli->outbuf,smb_tid,cli->cnum);
+ cli_setup_packet(cli);
+
+ SSVAL(cli->outbuf,smb_vwv0,0);
+ SIVALS(cli->outbuf,smb_vwv1,-1);
+
+ p = smb_buf(cli->outbuf);
+ *p++ = 4;
+ p += clistr_push(cli, p, path, -1, STR_TERMINATE);
+
+ cli_setup_bcc(cli, p);
+
+ cli_send_smb(cli);
+ if (!cli_receive_smb(cli)) {
+ return -1;
+ }
+
+ if (cli_is_error(cli)) {
+ return -1;
+ }
+
+ /* despite the spec, the result has a -1, followed by
+ length, followed by name */
+ p = smb_buf(cli->inbuf);
+ p += 4;
+ len = smb_buflen(cli->inbuf) - 4;
+ if (len <= 0) return -1;
+
+ if (tmp_path) {
+ pstring path2;
+ clistr_pull(cli, path2, p,
+ sizeof(path2), len, STR_ASCII);
+ *tmp_path = strdup(path2);
+ }
+
+ return SVAL(cli->inbuf,smb_vwv0);
+}
+
+
+/*
+ send a raw ioctl - used by the torture code
+*/
+NTSTATUS cli_raw_ioctl(struct cli_state *cli, int fnum, uint32 code, DATA_BLOB *blob)
+{
+ memset(cli->outbuf,'\0',smb_size);
+ memset(cli->inbuf,'\0',smb_size);
+
+ set_message(cli->outbuf, 3, 0, True);
+ SCVAL(cli->outbuf,smb_com,SMBioctl);
+ cli_setup_packet(cli);
+
+ SSVAL(cli->outbuf, smb_vwv0, fnum);
+ SSVAL(cli->outbuf, smb_vwv1, code>>16);
+ SSVAL(cli->outbuf, smb_vwv2, (code&0xFFFF));
+
+ cli_send_smb(cli);
+ if (!cli_receive_smb(cli)) {
+ return NT_STATUS_UNEXPECTED_NETWORK_ERROR;
+ }
+
+ if (cli_is_error(cli)) {
+ return cli_nt_error(cli);
+ }
+
+ *blob = data_blob(NULL, 0);
+
+ return NT_STATUS_OK;
+}
+
+/*********************************************************
+ Set an extended attribute utility fn.
+*********************************************************/
+
+static BOOL cli_set_ea(struct cli_state *cli, uint16 setup, char *param, unsigned int param_len,
+ const char *ea_name, const char *ea_val, size_t ea_len)
+{
+ unsigned int data_len = 0;
+ char *data = NULL;
+ char *rparam=NULL, *rdata=NULL;
+ char *p;
+ size_t ea_namelen = strlen(ea_name);
+
+ data_len = 4 + 4 + ea_namelen + 1 + ea_len;
+ data = malloc(data_len);
+ if (!data) {
+ return False;
+ }
+ p = data;
+ SIVAL(p,0,data_len);
+ p += 4;
+ SCVAL(p, 0, 0); /* EA flags. */
+ SCVAL(p, 1, ea_namelen);
+ SSVAL(p, 2, ea_len);
+ memcpy(p+4, ea_name, ea_namelen+1); /* Copy in the name. */
+ memcpy(p+4+ea_namelen+1, ea_val, ea_len);
+
+ if (!cli_send_trans(cli, SMBtrans2,
+ NULL, /* name */
+ -1, 0, /* fid, flags */
+ &setup, 1, 0, /* setup, length, max */
+ param, param_len, 2, /* param, length, max */
+ data, data_len, cli->max_xmit /* data, length, max */
+ )) {
+ return False;
+ }
+
+ if (!cli_receive_trans(cli, SMBtrans2,
+ &rparam, &param_len,
+ &rdata, &data_len)) {
+ return False;
+ }
+
+ SAFE_FREE(data);
+ SAFE_FREE(rdata);
+ SAFE_FREE(rparam);
+
+ return True;
+}
+
+/*********************************************************
+ Set an extended attribute on a pathname.
+*********************************************************/
+
+BOOL cli_set_ea_path(struct cli_state *cli, const char *path, const char *ea_name, const char *ea_val, size_t ea_len)
+{
+ uint16 setup = TRANSACT2_SETPATHINFO;
+ unsigned int param_len = 0;
+ char param[sizeof(pstring)+6];
+ size_t srclen = 2*(strlen(path)+1);
+ char *p;
+
+ memset(param, 0, sizeof(param));
+ SSVAL(param,0,SMB_INFO_SET_EA);
+ p = &param[6];
+
+ p += clistr_push(cli, p, path, MIN(srclen, sizeof(param)-6), STR_TERMINATE);
+ param_len = PTR_DIFF(p, param);
+
+ return cli_set_ea(cli, setup, param, param_len, ea_name, ea_val, ea_len);
+}
+
+/*********************************************************
+ Set an extended attribute on an fnum.
+*********************************************************/
+
+BOOL cli_set_ea_fnum(struct cli_state *cli, int fnum, const char *ea_name, const char *ea_val, size_t ea_len)
+{
+ char param[6];
+ uint16 setup = TRANSACT2_SETFILEINFO;
+
+ memset(param, 0, 6);
+ SSVAL(param,0,fnum);
+ SSVAL(param,2,SMB_INFO_SET_EA);
+
+ return cli_set_ea(cli, setup, param, 6, ea_name, ea_val, ea_len);
+}
+
+/*********************************************************
+ Get an extended attribute list tility fn.
+*********************************************************/
+
+static BOOL cli_get_ea_list(struct cli_state *cli,
+ uint16 setup, char *param, unsigned int param_len,
+ TALLOC_CTX *ctx,
+ size_t *pnum_eas,
+ struct ea_struct **pea_list)
+{
+ unsigned int data_len = 0;
+ unsigned int rparam_len, rdata_len;
+ char *rparam=NULL, *rdata=NULL;
+ char *p;
+ size_t ea_size;
+ size_t num_eas;
+ BOOL ret = False;
+ struct ea_struct *ea_list;
+
+ *pnum_eas = 0;
+ *pea_list = NULL;
+
+ if (!cli_send_trans(cli, SMBtrans2,
+ NULL, /* Name */
+ -1, 0, /* fid, flags */
+ &setup, 1, 0, /* setup, length, max */
+ param, param_len, 10, /* param, length, max */
+ NULL, data_len, cli->max_xmit /* data, length, max */
+ )) {
+ return False;
+ }
+
+ if (!cli_receive_trans(cli, SMBtrans2,
+ &rparam, &rparam_len,
+ &rdata, &rdata_len)) {
+ return False;
+ }
+
+ if (!rdata || rdata_len < 4) {
+ goto out;
+ }
+
+ ea_size = (size_t)IVAL(rdata,0);
+ if (ea_size > rdata_len) {
+ goto out;
+ }
+
+ if (ea_size == 0) {
+ /* No EA's present. */
+ ret = True;
+ goto out;
+ }
+
+ p = rdata + 4;
+ ea_size -= 4;
+
+ /* Validate the EA list and count it. */
+ for (num_eas = 0; ea_size >= 4; num_eas++) {
+ unsigned int ea_namelen = CVAL(p,1);
+ unsigned int ea_valuelen = SVAL(p,2);
+ if (ea_namelen == 0) {
+ goto out;
+ }
+ if (4 + ea_namelen + 1 + ea_valuelen > ea_size) {
+ goto out;
+ }
+ ea_size -= 4 + ea_namelen + 1 + ea_valuelen;
+ p += 4 + ea_namelen + 1 + ea_valuelen;
+ }
+
+ if (num_eas == 0) {
+ ret = True;
+ goto out;
+ }
+
+ *pnum_eas = num_eas;
+ if (!pea_list) {
+ /* Caller only wants number of EA's. */
+ ret = True;
+ goto out;
+ }
+
+ ea_list = (struct ea_struct *)talloc(ctx, num_eas*sizeof(struct ea_struct));
+ if (!ea_list) {
+ goto out;
+ }
+
+ ea_size = (size_t)IVAL(rdata,0);
+ p = rdata + 4;
+
+ for (num_eas = 0; num_eas < *pnum_eas; num_eas++ ) {
+ struct ea_struct *ea = &ea_list[num_eas];
+ fstring unix_ea_name;
+ unsigned int ea_namelen = CVAL(p,1);
+ unsigned int ea_valuelen = SVAL(p,2);
+
+ ea->flags = CVAL(p,0);
+ unix_ea_name[0] = '\0';
+ pull_ascii_fstring(unix_ea_name, p + 4);
+ ea->name = talloc_strdup(ctx, unix_ea_name);
+ /* Ensure the value is null terminated (in case it's a string). */
+ ea->value = data_blob_talloc(ctx, NULL, ea_valuelen + 1);
+ if (!ea->value.data) {
+ goto out;
+ }
+ if (ea_valuelen) {
+ memcpy(ea->value.data, p+4+ea_namelen+1, ea_valuelen);
+ }
+ ea->value.data[ea_valuelen] = 0;
+ ea->value.length--;
+ p += 4 + ea_namelen + 1 + ea_valuelen;
+ }
+
+ *pea_list = ea_list;
+ ret = True;
+
+ out :
+
+ SAFE_FREE(rdata);
+ SAFE_FREE(rparam);
+ return ret;
+}
+
+/*********************************************************
+ Get an extended attribute list from a pathname.
+*********************************************************/
+
+BOOL cli_get_ea_list_path(struct cli_state *cli, const char *path,
+ TALLOC_CTX *ctx,
+ size_t *pnum_eas,
+ struct ea_struct **pea_list)
+{
+ uint16 setup = TRANSACT2_QPATHINFO;
+ unsigned int param_len = 0;
+ char param[sizeof(pstring)+6];
+ char *p;
+
+ p = param;
+ memset(p, 0, 6);
+ SSVAL(p, 0, SMB_INFO_QUERY_ALL_EAS);
+ p += 6;
+ p += clistr_push(cli, p, path, sizeof(pstring)-6, STR_TERMINATE);
+ param_len = PTR_DIFF(p, param);
+
+ return cli_get_ea_list(cli, setup, param, param_len, ctx, pnum_eas, pea_list);
+}
+
+/*********************************************************
+ Get an extended attribute list from an fnum.
+*********************************************************/
+
+BOOL cli_get_ea_list_fnum(struct cli_state *cli, int fnum,
+ TALLOC_CTX *ctx,
+ size_t *pnum_eas,
+ struct ea_struct **pea_list)
+{
+ uint16 setup = TRANSACT2_QFILEINFO;
+ char param[6];
+
+ memset(param, 0, 6);
+ SSVAL(param,0,fnum);
+ SSVAL(param,2,SMB_INFO_SET_EA);
+
+ return cli_get_ea_list(cli, setup, param, 6, ctx, pnum_eas, pea_list);
+}
diff --git a/source/libsmb/clifsinfo.c b/source/libsmb/clifsinfo.c
new file mode 100644
index 00000000000..00fe189e9a9
--- /dev/null
+++ b/source/libsmb/clifsinfo.c
@@ -0,0 +1,76 @@
+/*
+ Unix SMB/CIFS implementation.
+ FS info functions
+ 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 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"
+
+
+BOOL cli_get_fs_attr_info(struct cli_state *cli, uint32 *fs_attr)
+{
+ BOOL ret = False;
+ uint16 setup;
+ char param[2];
+ char *rparam=NULL, *rdata=NULL;
+ unsigned int rparam_count=0, rdata_count=0;
+
+ if (!cli||!fs_attr)
+ smb_panic("cli_get_fs_attr_info() called with NULL Pionter!");
+
+ setup = TRANSACT2_QFSINFO;
+
+ SSVAL(param,0,SMB_QUERY_FS_ATTRIBUTE_INFO);
+
+ if (!cli_send_trans(cli, SMBtrans2,
+ NULL,
+ 0, 0,
+ &setup, 1, 0,
+ param, 2, 0,
+ NULL, 0, 560)) {
+ goto cleanup;
+ }
+
+ if (!cli_receive_trans(cli, SMBtrans2,
+ &rparam, &rparam_count,
+ &rdata, &rdata_count)) {
+ goto cleanup;
+ }
+
+ if (cli_is_error(cli)) {
+ ret = False;
+ goto cleanup;
+ } else {
+ ret = True;
+ }
+
+ if (rdata_count < 12) {
+ goto cleanup;
+ }
+
+ *fs_attr = IVAL(rdata,0);
+
+ /* todo: but not yet needed
+ * return the other stuff
+ */
+
+cleanup:
+ SAFE_FREE(rparam);
+ SAFE_FREE(rdata);
+
+ return ret;
+}
diff --git a/source/libsmb/clikrb5.c b/source/libsmb/clikrb5.c
new file mode 100644
index 00000000000..15b244a83dc
--- /dev/null
+++ b/source/libsmb/clikrb5.c
@@ -0,0 +1,421 @@
+/*
+ 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 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"
+
+#ifdef HAVE_KRB5
+
+#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
+/*
+ * This function is not in the Heimdal mainline.
+ */
+ krb5_error_code krb5_set_real_time(krb5_context context, int32_t seconds, int32_t microseconds)
+{
+ krb5_error_code ret;
+ int32_t sec, usec;
+
+ ret = krb5_us_timeofday(context, &sec, &usec);
+ if (ret)
+ return ret;
+
+ context->kdc_sec_offset = seconds - sec;
+ context->kdc_usec_offset = microseconds - usec;
+
+ return 0;
+}
+#endif
+
+#if defined(HAVE_KRB5_SET_DEFAULT_IN_TKT_ETYPES) && !defined(HAVE_KRB5_SET_DEFAULT_TGS_KTYPES)
+ krb5_error_code krb5_set_default_tgs_ktypes(krb5_context ctx, const krb5_enctype *enc)
+{
+ return krb5_set_default_in_tkt_etypes(ctx, enc);
+}
+#endif
+
+#if defined(HAVE_ADDR_TYPE_IN_KRB5_ADDRESS)
+/* HEIMDAL */
+ void setup_kaddr( krb5_address *pkaddr, struct sockaddr *paddr)
+{
+ pkaddr->addr_type = KRB5_ADDRESS_INET;
+ pkaddr->address.length = sizeof(((struct sockaddr_in *)paddr)->sin_addr);
+ pkaddr->address.data = (char *)&(((struct sockaddr_in *)paddr)->sin_addr);
+}
+#elif defined(HAVE_ADDRTYPE_IN_KRB5_ADDRESS)
+/* MIT */
+ void setup_kaddr( krb5_address *pkaddr, struct sockaddr *paddr)
+{
+ pkaddr->addrtype = ADDRTYPE_INET;
+ pkaddr->length = sizeof(((struct sockaddr_in *)paddr)->sin_addr);
+ pkaddr->contents = (krb5_octet *)&(((struct sockaddr_in *)paddr)->sin_addr);
+}
+#else
+ __ERROR__XX__UNKNOWN_ADDRTYPE
+#endif
+
+#if defined(HAVE_KRB5_PRINCIPAL2SALT) && defined(HAVE_KRB5_USE_ENCTYPE) && defined(HAVE_KRB5_STRING_TO_KEY)
+ 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;
+ }
+ return krb5_string_to_key_salt(context, enctype, password->data,
+ salt, key);
+}
+#else
+ __ERROR_XX_UNKNOWN_CREATE_KEY_FUNCTIONS
+#endif
+
+#if defined(HAVE_KRB5_GET_PERMITTED_ENCTYPES)
+krb5_error_code get_kerberos_allowed_etypes(krb5_context context,
+ krb5_enctype **enctypes)
+{
+ return krb5_get_permitted_enctypes(context, enctypes);
+}
+#elif defined(HAVE_KRB5_GET_DEFAULT_IN_TKT_ETYPES)
+krb5_error_code get_kerberos_allowed_etypes(krb5_context context,
+ krb5_enctype **enctypes)
+{
+ return krb5_get_default_in_tkt_etypes(context, enctypes);
+}
+#else
+#error UNKNOWN_GET_ENCTYPES_FUNCTIONS
+#endif
+
+ void free_kerberos_etypes(krb5_context context,
+ krb5_enctype *enctypes)
+{
+#if defined(HAVE_KRB5_FREE_KTYPES)
+ krb5_free_ktypes(context, enctypes);
+ return;
+#else
+ SAFE_FREE(enctypes);
+ return;
+#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)
+{
+ return krb5_auth_con_setkey(context, auth_context, keyblock);
+}
+#endif
+
+ void get_auth_data_from_tkt(DATA_BLOB *auth_data, krb5_ticket *tkt)
+{
+#if defined(HAVE_KRB5_TKT_ENC_PART2)
+ if (tkt->enc_part2)
+ *auth_data = data_blob(tkt->enc_part2->authorization_data[0]->contents,
+ tkt->enc_part2->authorization_data[0]->length);
+#else
+ if (tkt->ticket.authorization_data && tkt->ticket.authorization_data->len)
+ *auth_data = data_blob(tkt->ticket.authorization_data->val->ad_data.data,
+ tkt->ticket.authorization_data->val->ad_data.length);
+#endif
+}
+
+ krb5_const_principal get_principal_from_tkt(krb5_ticket *tkt)
+{
+#if defined(HAVE_KRB5_TKT_ENC_PART2)
+ return tkt->enc_part2->client;
+#else
+ return tkt->client;
+#endif
+}
+
+#if !defined(HAVE_KRB5_LOCATE_KDC)
+ krb5_error_code krb5_locate_kdc(krb5_context ctx, const krb5_data *realm, struct sockaddr **addr_pp, int *naddrs, int get_masters)
+{
+ krb5_krbhst_handle hnd;
+ krb5_krbhst_info *hinfo;
+ krb5_error_code rc;
+ int num_kdcs, i;
+ struct sockaddr *sa;
+
+ *addr_pp = NULL;
+ *naddrs = 0;
+
+ rc = krb5_krbhst_init(ctx, realm->data, KRB5_KRBHST_KDC, &hnd);
+ if (rc) {
+ DEBUG(0, ("krb5_locate_kdc: krb5_krbhst_init failed (%s)\n", error_message(rc)));
+ return rc;
+ }
+
+ for ( num_kdcs = 0; (rc = krb5_krbhst_next(ctx, hnd, &hinfo) == 0); num_kdcs++)
+ ;
+
+ krb5_krbhst_reset(ctx, hnd);
+
+ if (!num_kdcs) {
+ DEBUG(0, ("krb5_locate_kdc: zero kdcs found !\n"));
+ krb5_krbhst_free(ctx, hnd);
+ return -1;
+ }
+
+ sa = malloc( sizeof(struct sockaddr) * num_kdcs );
+ if (!sa) {
+ DEBUG(0, ("krb5_locate_kdc: malloc failed\n"));
+ krb5_krbhst_free(ctx, hnd);
+ naddrs = 0;
+ return -1;
+ }
+
+ memset(*addr_pp, '\0', sizeof(struct sockaddr) * num_kdcs );
+
+ for (i = 0; i < num_kdcs && (rc = krb5_krbhst_next(ctx, hnd, &hinfo) == 0); i++) {
+ if (hinfo->ai->ai_family == AF_INET)
+ memcpy(&sa[i], hinfo->ai->ai_addr, sizeof(struct sockaddr));
+ }
+
+ krb5_krbhst_free(ctx, hnd);
+
+ *naddrs = num_kdcs;
+ *addr_pp = sa;
+ return 0;
+}
+#endif
+
+/*
+ we can't use krb5_mk_req because w2k wants the service to be in a particular format
+*/
+static 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)
+{
+ krb5_error_code retval;
+ krb5_principal server;
+ krb5_creds * credsp;
+ krb5_creds creds;
+ krb5_data in_data;
+
+ retval = krb5_parse_name(context, principal, &server);
+ if (retval) {
+ DEBUG(1,("Failed to parse principal %s\n", principal));
+ return retval;
+ }
+
+ /* obtain ticket & session key */
+ ZERO_STRUCT(creds);
+ if ((retval = krb5_copy_principal(context, server, &creds.server))) {
+ DEBUG(1,("krb5_copy_principal failed (%s)\n",
+ error_message(retval)));
+ goto cleanup_princ;
+ }
+
+ if ((retval = krb5_cc_get_principal(context, ccache, &creds.client))) {
+ DEBUG(1,("krb5_cc_get_principal failed (%s)\n",
+ error_message(retval)));
+ goto cleanup_creds;
+ }
+
+ if ((retval = krb5_get_credentials(context, 0,
+ ccache, &creds, &credsp))) {
+ DEBUG(1,("krb5_get_credentials failed for %s (%s)\n",
+ principal, error_message(retval)));
+ goto cleanup_creds;
+ }
+
+ /* cope with the ticket being in the future due to clock skew */
+ if ((unsigned)credsp->times.starttime > time(NULL)) {
+ time_t t = time(NULL);
+ int time_offset = (unsigned)credsp->times.starttime - t;
+ DEBUG(4,("Advancing clock by %d seconds to cope with clock skew\n", time_offset));
+ krb5_set_real_time(context, t + time_offset + 1, 0);
+ }
+
+ in_data.length = 0;
+ retval = krb5_mk_req_extended(context, auth_context, ap_req_options,
+ &in_data, credsp, outbuf);
+ if (retval) {
+ DEBUG(1,("krb5_mk_req_extended failed (%s)\n",
+ error_message(retval)));
+ }
+
+ krb5_free_creds(context, credsp);
+
+cleanup_creds:
+ krb5_free_cred_contents(context, &creds);
+
+cleanup_princ:
+ krb5_free_principal(context, server);
+
+ return retval;
+}
+
+/*
+ get a kerberos5 ticket for the given service
+*/
+int cli_krb5_get_ticket(const char *principal, time_t time_offset,
+ DATA_BLOB *ticket, DATA_BLOB *session_key_krb5)
+{
+ krb5_error_code retval;
+ krb5_data packet;
+ krb5_ccache ccdef;
+ krb5_context context;
+ krb5_auth_context auth_context = NULL;
+ krb5_enctype enc_types[] = {
+#ifdef ENCTYPE_ARCFOUR_HMAC
+ ENCTYPE_ARCFOUR_HMAC,
+#endif
+ ENCTYPE_DES_CBC_MD5,
+ ENCTYPE_DES_CBC_CRC,
+ ENCTYPE_NULL};
+
+ retval = krb5_init_context(&context);
+ if (retval) {
+ DEBUG(1,("krb5_init_context failed (%s)\n",
+ error_message(retval)));
+ goto failed;
+ }
+
+ if (time_offset != 0) {
+ krb5_set_real_time(context, time(NULL) + time_offset, 0);
+ }
+
+ if ((retval = krb5_cc_default(context, &ccdef))) {
+ DEBUG(1,("krb5_cc_default failed (%s)\n",
+ error_message(retval)));
+ goto failed;
+ }
+
+ if ((retval = krb5_set_default_tgs_ktypes(context, enc_types))) {
+ DEBUG(1,("krb5_set_default_tgs_ktypes failed (%s)\n",
+ error_message(retval)));
+ goto failed;
+ }
+
+ if ((retval = ads_krb5_mk_req(context,
+ &auth_context,
+ AP_OPTS_USE_SUBKEY,
+ principal,
+ ccdef, &packet))) {
+ goto failed;
+ }
+
+ get_krb5_smb_session_key(context, auth_context, session_key_krb5, False);
+
+ *ticket = data_blob(packet.data, packet.length);
+
+/* Hmm, heimdal dooesn't have this - what's the correct call? */
+#ifdef HAVE_KRB5_FREE_DATA_CONTENTS
+ krb5_free_data_contents(context, &packet);
+#endif
+
+failed:
+ if ( context )
+ krb5_free_context(context);
+
+ return retval;
+}
+
+ BOOL get_krb5_smb_session_key(krb5_context context, krb5_auth_context auth_context, DATA_BLOB *session_key, BOOL remote)
+ {
+ krb5_keyblock *skey;
+ krb5_error_code err;
+ BOOL ret = False;
+
+ memset(session_key, 0, 16);
+
+ if (remote)
+ err = krb5_auth_con_getremotesubkey(context, auth_context, &skey);
+ else
+ err = krb5_auth_con_getlocalsubkey(context, auth_context, &skey);
+ if (err == 0 && skey != NULL) {
+ DEBUG(10, ("Got KRB5 session key of length %d\n", KRB5_KEY_LENGTH(skey)));
+ *session_key = data_blob(KRB5_KEY_DATA(skey), KRB5_KEY_LENGTH(skey));
+ dump_data_pw("KRB5 Session Key:\n", session_key->data, session_key->length);
+
+ ret = True;
+
+ krb5_free_keyblock(context, skey);
+ } else {
+ DEBUG(10, ("KRB5 error getting session key %d\n", err));
+ }
+
+ return ret;
+ }
+
+
+#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 )
+{
+ static krb5_data kdata;
+
+ kdata.data = krb5_principal_get_comp_string(context, principal, i);
+ kdata.length = strlen(kdata.data);
+ return &kdata;
+}
+#endif
+
+#else /* HAVE_KRB5 */
+ /* this saves a few linking headaches */
+int cli_krb5_get_ticket(const char *principal, time_t time_offset,
+ DATA_BLOB *ticket, DATA_BLOB *session_key_krb5)
+{
+ DEBUG(0,("NO KERBEROS SUPPORT\n"));
+ return 1;
+}
+
+#endif
diff --git a/source/libsmb/clilist.c b/source/libsmb/clilist.c
new file mode 100644
index 00000000000..ab157d48e96
--- /dev/null
+++ b/source/libsmb/clilist.c
@@ -0,0 +1,487 @@
+/*
+ Unix SMB/CIFS implementation.
+ client directory list routines
+ Copyright (C) Andrew Tridgell 1994-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 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.
+*/
+
+#define NO_SYSLOG
+
+#include "includes.h"
+
+/****************************************************************************
+ Interpret a long filename structure - this is mostly guesses at the moment.
+ The length of the structure is returned
+ The structure of a long filename depends on the info level. 260 is used
+ by NT and 2 is used by OS/2
+****************************************************************************/
+
+static int interpret_long_filename(struct cli_state *cli,
+ int level,char *p,file_info *finfo)
+{
+ extern file_info def_finfo;
+ file_info finfo2;
+ int len;
+ char *base = p;
+
+ if (!finfo) finfo = &finfo2;
+
+ memcpy(finfo,&def_finfo,sizeof(*finfo));
+
+ switch (level) {
+ case 1: /* OS/2 understands this */
+ /* these dates are converted to GMT by
+ make_unix_date */
+ finfo->ctime = make_unix_date2(p+4);
+ finfo->atime = make_unix_date2(p+8);
+ finfo->mtime = make_unix_date2(p+12);
+ finfo->size = IVAL(p,16);
+ finfo->mode = CVAL(p,24);
+ len = CVAL(p, 26);
+ p += 27;
+ p += clistr_align_in(cli, p, 0);
+ /* the len+2 below looks strange but it is
+ important to cope with the differences
+ between win2000 and win9x for this call
+ (tridge) */
+ p += clistr_pull(cli, finfo->name, p,
+ sizeof(finfo->name),
+ len+2,
+ STR_TERMINATE);
+ return PTR_DIFF(p, base);
+
+ case 2: /* this is what OS/2 uses mostly */
+ /* these dates are converted to GMT by
+ make_unix_date */
+ finfo->ctime = make_unix_date2(p+4);
+ finfo->atime = make_unix_date2(p+8);
+ finfo->mtime = make_unix_date2(p+12);
+ finfo->size = IVAL(p,16);
+ finfo->mode = CVAL(p,24);
+ len = CVAL(p, 30);
+ p += 31;
+ /* check for unisys! */
+ p += clistr_pull(cli, finfo->name, p,
+ sizeof(finfo->name),
+ len,
+ STR_NOALIGN);
+ return PTR_DIFF(p, base) + 1;
+
+ case 260: /* NT uses this, but also accepts 2 */
+ {
+ size_t namelen, slen;
+ p += 4; /* next entry offset */
+ p += 4; /* fileindex */
+
+ /* these dates appear to arrive in a
+ weird way. It seems to be localtime
+ plus the serverzone given in the
+ initial connect. This is GMT when
+ DST is not in effect and one hour
+ from GMT otherwise. Can this really
+ be right??
+
+ I suppose this could be called
+ kludge-GMT. Is is the GMT you get
+ by using the current DST setting on
+ a different localtime. It will be
+ cheap to calculate, I suppose, as
+ no DST tables will be needed */
+
+ finfo->ctime = interpret_long_date(p);
+ p += 8;
+ finfo->atime = interpret_long_date(p);
+ p += 8;
+ finfo->mtime = interpret_long_date(p);
+ p += 8;
+ p += 8;
+ finfo->size = IVAL2_TO_SMB_BIG_UINT(p,0);
+ p += 8;
+ p += 8; /* alloc size */
+ finfo->mode = CVAL(p,0);
+ p += 4;
+ namelen = IVAL(p,0);
+ p += 4;
+ p += 4; /* EA size */
+ slen = SVAL(p, 0);
+ p += 2;
+ {
+ /* stupid NT bugs. grr */
+ int flags = 0;
+ if (p[1] == 0 && namelen > 1) flags |= STR_UNICODE;
+ clistr_pull(cli, finfo->short_name, p,
+ sizeof(finfo->short_name),
+ slen, flags);
+ }
+ p += 24; /* short name? */
+ clistr_pull(cli, finfo->name, p,
+ sizeof(finfo->name),
+ namelen, 0);
+ return SVAL(base, 0);
+ }
+ }
+
+ DEBUG(1,("Unknown long filename format %d\n",level));
+ return(SVAL(p,0));
+}
+
+/****************************************************************************
+ Do a directory listing, calling fn on each file found.
+****************************************************************************/
+
+int cli_list_new(struct cli_state *cli,const char *Mask,uint16 attribute,
+ void (*fn)(file_info *, const char *, void *), void *state)
+{
+#if 0
+ int max_matches = 1366; /* Match W2k - was 512. */
+#else
+ int max_matches = 512;
+#endif
+ int info_level;
+ char *p, *p2;
+ pstring mask;
+ file_info finfo;
+ int i;
+ char *tdl, *dirlist = NULL;
+ int dirlist_len = 0;
+ int total_received = -1;
+ BOOL First = True;
+ int ff_searchcount=0;
+ int ff_eos=0;
+ int ff_lastname=0;
+ int ff_dir_handle=0;
+ int loop_count = 0;
+ char *rparam=NULL, *rdata=NULL;
+ unsigned int param_len, data_len;
+ uint16 setup;
+ pstring param;
+
+ /* NT uses 260, OS/2 uses 2. Both accept 1. */
+ info_level = (cli->capabilities&CAP_NT_SMBS)?260:1;
+
+ pstrcpy(mask,Mask);
+
+ while (ff_eos == 0) {
+ loop_count++;
+ if (loop_count > 200) {
+ DEBUG(0,("Error: Looping in FIND_NEXT??\n"));
+ break;
+ }
+
+ if (First) {
+ setup = TRANSACT2_FINDFIRST;
+ SSVAL(param,0,attribute); /* attribute */
+ SSVAL(param,2,max_matches); /* max count */
+ SSVAL(param,4,4+2); /* resume required + close on end */
+ SSVAL(param,6,info_level);
+ SIVAL(param,8,0);
+ p = param+12;
+ p += clistr_push(cli, param+12, mask, sizeof(param)-12,
+ STR_TERMINATE);
+ } else {
+ setup = TRANSACT2_FINDNEXT;
+ SSVAL(param,0,ff_dir_handle);
+ SSVAL(param,2,max_matches); /* max count */
+ SSVAL(param,4,info_level);
+ SIVAL(param,6,0); /* ff_resume_key */
+ SSVAL(param,10,8+4+2); /* continue + resume required + close on end */
+ p = param+12;
+ p += clistr_push(cli, param+12, mask, sizeof(param)-12,
+ STR_TERMINATE);
+ }
+
+ param_len = PTR_DIFF(p, param);
+
+ if (!cli_send_trans(cli, SMBtrans2,
+ NULL, /* Name */
+ -1, 0, /* fid, flags */
+ &setup, 1, 0, /* setup, length, max */
+ param, param_len, 10, /* param, length, max */
+ NULL, 0,
+#if 0
+ /* w2k value. */
+ MIN(16384,cli->max_xmit) /* data, length, max. */
+#else
+ cli->max_xmit /* data, length, max. */
+#endif
+ )) {
+ break;
+ }
+
+ if (!cli_receive_trans(cli, SMBtrans2,
+ &rparam, &param_len,
+ &rdata, &data_len) &&
+ cli_is_dos_error(cli)) {
+ /* we need to work around a Win95 bug - sometimes
+ it gives ERRSRV/ERRerror temprarily */
+ uint8 eclass;
+ uint32 ecode;
+ cli_dos_error(cli, &eclass, &ecode);
+ if (eclass != ERRSRV || ecode != ERRerror)
+ break;
+ smb_msleep(100);
+ continue;
+ }
+
+ if (cli_is_error(cli) || !rdata || !rparam)
+ break;
+
+ if (total_received == -1)
+ total_received = 0;
+
+ /* parse out some important return info */
+ p = rparam;
+ if (First) {
+ ff_dir_handle = SVAL(p,0);
+ ff_searchcount = SVAL(p,2);
+ ff_eos = SVAL(p,4);
+ ff_lastname = SVAL(p,8);
+ } else {
+ ff_searchcount = SVAL(p,0);
+ ff_eos = SVAL(p,2);
+ ff_lastname = SVAL(p,6);
+ }
+
+ if (ff_searchcount == 0)
+ break;
+
+ /* point to the data bytes */
+ p = rdata;
+
+ /* we might need the lastname for continuations */
+ if (ff_lastname > 0) {
+ switch(info_level) {
+ case 260:
+ clistr_pull(cli, mask, p+ff_lastname,
+ sizeof(mask),
+ data_len-ff_lastname,
+ STR_TERMINATE);
+ break;
+ case 1:
+ clistr_pull(cli, mask, p+ff_lastname+1,
+ sizeof(mask),
+ -1,
+ STR_TERMINATE);
+ break;
+ }
+ } else {
+ pstrcpy(mask,"");
+ }
+
+ /* and add them to the dirlist pool */
+ tdl = Realloc(dirlist,dirlist_len + data_len);
+
+ if (!tdl) {
+ DEBUG(0,("cli_list_new: Failed to expand dirlist\n"));
+ break;
+ } else {
+ dirlist = tdl;
+ }
+
+ /* put in a length for the last entry, to ensure we can chain entries
+ into the next packet */
+ for (p2=p,i=0;i<(ff_searchcount-1);i++)
+ p2 += interpret_long_filename(cli,info_level,p2,NULL);
+ SSVAL(p2,0,data_len - PTR_DIFF(p2,p));
+
+ /* grab the data for later use */
+ memcpy(dirlist+dirlist_len,p,data_len);
+ dirlist_len += data_len;
+
+ total_received += ff_searchcount;
+
+ SAFE_FREE(rdata);
+ SAFE_FREE(rparam);
+
+ DEBUG(3,("received %d entries (eos=%d)\n",
+ ff_searchcount,ff_eos));
+
+ if (ff_searchcount > 0)
+ loop_count = 0;
+
+ First = False;
+ }
+
+ for (p=dirlist,i=0;i<total_received;i++) {
+ p += interpret_long_filename(cli,info_level,p,&finfo);
+ fn(&finfo, Mask, state);
+ }
+
+ /* free up the dirlist buffer */
+ SAFE_FREE(dirlist);
+ return(total_received);
+}
+
+/****************************************************************************
+ Interpret a short filename structure.
+ The length of the structure is returned.
+****************************************************************************/
+
+static int interpret_short_filename(struct cli_state *cli, char *p,file_info *finfo)
+{
+ extern file_info def_finfo;
+
+ *finfo = def_finfo;
+
+ finfo->mode = CVAL(p,21);
+
+ /* this date is converted to GMT by make_unix_date */
+ finfo->ctime = make_unix_date(p+22);
+ finfo->mtime = finfo->atime = finfo->ctime;
+ finfo->size = IVAL(p,26);
+ clistr_pull(cli, finfo->name, p+30, sizeof(finfo->name), 12, STR_ASCII);
+ if (strcmp(finfo->name, "..") && strcmp(finfo->name, ".")) {
+ strncpy(finfo->short_name,finfo->name, sizeof(finfo->short_name)-1);
+ finfo->short_name[sizeof(finfo->short_name)-1] = '\0';
+ }
+
+ return(DIR_STRUCT_SIZE);
+}
+
+
+/****************************************************************************
+ Do a directory listing, calling fn on each file found.
+ this uses the old SMBsearch interface. It is needed for testing Samba,
+ but should otherwise not be used.
+****************************************************************************/
+
+int cli_list_old(struct cli_state *cli,const char *Mask,uint16 attribute,
+ void (*fn)(file_info *, const char *, void *), void *state)
+{
+ char *p;
+ int received = 0;
+ BOOL first = True;
+ char status[21];
+ int num_asked = (cli->max_xmit - 100)/DIR_STRUCT_SIZE;
+ int num_received = 0;
+ int i;
+ char *tdl, *dirlist = NULL;
+ pstring mask;
+
+ ZERO_ARRAY(status);
+
+ pstrcpy(mask,Mask);
+
+ while (1) {
+ memset(cli->outbuf,'\0',smb_size);
+ memset(cli->inbuf,'\0',smb_size);
+
+ set_message(cli->outbuf,2,0,True);
+
+ SCVAL(cli->outbuf,smb_com,SMBsearch);
+
+ SSVAL(cli->outbuf,smb_tid,cli->cnum);
+ cli_setup_packet(cli);
+
+ SSVAL(cli->outbuf,smb_vwv0,num_asked);
+ SSVAL(cli->outbuf,smb_vwv1,attribute);
+
+ p = smb_buf(cli->outbuf);
+ *p++ = 4;
+
+ p += clistr_push(cli, p, first?mask:"", -1, STR_TERMINATE);
+ *p++ = 5;
+ if (first) {
+ SSVAL(p,0,0);
+ p += 2;
+ } else {
+ SSVAL(p,0,21);
+ p += 2;
+ memcpy(p,status,21);
+ p += 21;
+ }
+
+ cli_setup_bcc(cli, p);
+ cli_send_smb(cli);
+ if (!cli_receive_smb(cli)) break;
+
+ received = SVAL(cli->inbuf,smb_vwv0);
+ if (received <= 0) break;
+
+ first = False;
+
+ tdl = Realloc(dirlist,(num_received + received)*DIR_STRUCT_SIZE);
+
+ if (!tdl) {
+ DEBUG(0,("cli_list_old: failed to expand dirlist"));
+ SAFE_FREE(dirlist);
+ return 0;
+ }
+ else dirlist = tdl;
+
+ p = smb_buf(cli->inbuf) + 3;
+
+ memcpy(dirlist+num_received*DIR_STRUCT_SIZE,
+ p,received*DIR_STRUCT_SIZE);
+
+ memcpy(status,p + ((received-1)*DIR_STRUCT_SIZE),21);
+
+ num_received += received;
+
+ if (cli_is_error(cli)) break;
+ }
+
+ if (!first) {
+ memset(cli->outbuf,'\0',smb_size);
+ memset(cli->inbuf,'\0',smb_size);
+
+ set_message(cli->outbuf,2,0,True);
+ SCVAL(cli->outbuf,smb_com,SMBfclose);
+ SSVAL(cli->outbuf,smb_tid,cli->cnum);
+ cli_setup_packet(cli);
+
+ SSVAL(cli->outbuf, smb_vwv0, 0); /* find count? */
+ SSVAL(cli->outbuf, smb_vwv1, attribute);
+
+ p = smb_buf(cli->outbuf);
+ *p++ = 4;
+ fstrcpy(p, "");
+ p += strlen(p) + 1;
+ *p++ = 5;
+ SSVAL(p, 0, 21);
+ p += 2;
+ memcpy(p,status,21);
+ p += 21;
+
+ cli_setup_bcc(cli, p);
+ cli_send_smb(cli);
+ if (!cli_receive_smb(cli)) {
+ DEBUG(0,("Error closing search: %s\n",cli_errstr(cli)));
+ }
+ }
+
+ for (p=dirlist,i=0;i<num_received;i++) {
+ file_info finfo;
+ p += interpret_short_filename(cli, p,&finfo);
+ fn(&finfo, Mask, state);
+ }
+
+ SAFE_FREE(dirlist);
+ return(num_received);
+}
+
+/****************************************************************************
+ Do a directory listing, calling fn on each file found.
+ This auto-switches between old and new style.
+****************************************************************************/
+
+int cli_list(struct cli_state *cli,const char *Mask,uint16 attribute,
+ void (*fn)(file_info *, const char *, void *), void *state)
+{
+ if (cli->protocol <= PROTOCOL_LANMAN1)
+ return cli_list_old(cli, Mask, attribute, fn, state);
+ return cli_list_new(cli, Mask, attribute, fn, state);
+}
diff --git a/source/libsmb/climessage.c b/source/libsmb/climessage.c
new file mode 100644
index 00000000000..8429ca4f413
--- /dev/null
+++ b/source/libsmb/climessage.c
@@ -0,0 +1,156 @@
+/*
+ Unix SMB/CIFS implementation.
+ client message handling routines
+ Copyright (C) Andrew Tridgell 1994-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 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.
+*/
+
+#define NO_SYSLOG
+
+#include "includes.h"
+
+
+/****************************************************************************
+start a message sequence
+****************************************************************************/
+int cli_message_start_build(struct cli_state *cli, char *host, char *username)
+{
+ char *p;
+
+ /* construct a SMBsendstrt command */
+ memset(cli->outbuf,'\0',smb_size);
+ set_message(cli->outbuf,0,0,True);
+ SCVAL(cli->outbuf,smb_com,SMBsendstrt);
+ SSVAL(cli->outbuf,smb_tid,cli->cnum);
+ cli_setup_packet(cli);
+
+ p = smb_buf(cli->outbuf);
+ *p++ = 4;
+ p += clistr_push(cli, p, username, -1, STR_ASCII|STR_TERMINATE);
+ *p++ = 4;
+ p += clistr_push(cli, p, host, -1, STR_ASCII|STR_TERMINATE);
+
+ cli_setup_bcc(cli, p);
+
+ return(PTR_DIFF(p, cli->outbuf));
+}
+
+BOOL cli_message_start(struct cli_state *cli, char *host, char *username,
+ int *grp)
+{
+ cli_message_start_build(cli, host, username);
+
+ cli_send_smb(cli);
+
+ if (!cli_receive_smb(cli)) {
+ return False;
+ }
+
+ if (cli_is_error(cli)) return False;
+
+ *grp = SVAL(cli->inbuf,smb_vwv0);
+
+ return True;
+}
+
+
+/****************************************************************************
+send a message
+****************************************************************************/
+int cli_message_text_build(struct cli_state *cli, char *msg, int len, int grp)
+{
+ char *msgdos;
+ int lendos;
+ char *p;
+
+ memset(cli->outbuf,'\0',smb_size);
+ set_message(cli->outbuf,1,0,True);
+ SCVAL(cli->outbuf,smb_com,SMBsendtxt);
+ SSVAL(cli->outbuf,smb_tid,cli->cnum);
+ cli_setup_packet(cli);
+
+ SSVAL(cli->outbuf,smb_vwv0,grp);
+
+ p = smb_buf(cli->outbuf);
+ *p++ = 1;
+
+ if ((lendos = (int)convert_string_allocate(NULL,CH_UNIX, CH_DOS, msg,len, (void **) &msgdos, True)) < 0 || !msgdos) {
+ DEBUG(3,("Conversion failed, sending message in UNIX charset\n"));
+ SSVAL(p, 0, len); p += 2;
+ memcpy(p, msg, len);
+ p += len;
+ } else {
+ SSVAL(p, 0, lendos); p += 2;
+ memcpy(p, msgdos, lendos);
+ p += lendos;
+ SAFE_FREE(msgdos);
+ }
+
+ cli_setup_bcc(cli, p);
+
+ return(PTR_DIFF(p, cli->outbuf));
+}
+
+BOOL cli_message_text(struct cli_state *cli, char *msg, int len, int grp)
+{
+ cli_message_text_build(cli, msg, len, grp);
+
+ cli_send_smb(cli);
+
+ if (!cli_receive_smb(cli)) {
+ return False;
+ }
+
+ if (cli_is_error(cli)) return False;
+
+ return True;
+}
+
+/****************************************************************************
+end a message
+****************************************************************************/
+int cli_message_end_build(struct cli_state *cli, int grp)
+{
+ char *p;
+
+ memset(cli->outbuf,'\0',smb_size);
+ set_message(cli->outbuf,1,0,True);
+ SCVAL(cli->outbuf,smb_com,SMBsendend);
+ SSVAL(cli->outbuf,smb_tid,cli->cnum);
+
+ SSVAL(cli->outbuf,smb_vwv0,grp);
+
+ cli_setup_packet(cli);
+
+ p = smb_buf(cli->outbuf);
+
+ return(PTR_DIFF(p, cli->outbuf));
+}
+
+BOOL cli_message_end(struct cli_state *cli, int grp)
+{
+ cli_message_end_build(cli, grp);
+
+ cli_send_smb(cli);
+
+ if (!cli_receive_smb(cli)) {
+ return False;
+ }
+
+ if (cli_is_error(cli)) return False;
+
+ return True;
+}
diff --git a/source/libsmb/clioplock.c b/source/libsmb/clioplock.c
new file mode 100644
index 00000000000..0ffeb1926b0
--- /dev/null
+++ b/source/libsmb/clioplock.c
@@ -0,0 +1,68 @@
+/*
+ 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 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.
+*/
+
+#define NO_SYSLOG
+
+#include "includes.h"
+
+/****************************************************************************
+send an ack for an oplock break request
+****************************************************************************/
+BOOL cli_oplock_ack(struct cli_state *cli, int fnum, unsigned char level)
+{
+ char *oldbuf = cli->outbuf;
+ pstring buf;
+ BOOL ret;
+
+ cli->outbuf = buf;
+
+ memset(buf,'\0',smb_size);
+ set_message(buf,8,0,True);
+
+ SCVAL(buf,smb_com,SMBlockingX);
+ SSVAL(buf,smb_tid, cli->cnum);
+ cli_setup_packet(cli);
+ SSVAL(buf,smb_vwv0,0xFF);
+ SSVAL(buf,smb_vwv1,0);
+ SSVAL(buf,smb_vwv2,fnum);
+ if (level == 1)
+ SSVAL(buf,smb_vwv3,0x102); /* levelII oplock break ack */
+ else
+ SSVAL(buf,smb_vwv3,2); /* exclusive oplock break ack */
+ SIVAL(buf,smb_vwv4,0); /* timoeut */
+ SSVAL(buf,smb_vwv6,0); /* unlockcount */
+ SSVAL(buf,smb_vwv7,0); /* lockcount */
+
+ ret = cli_send_smb(cli);
+
+ cli->outbuf = oldbuf;
+
+ return ret;
+}
+
+
+/****************************************************************************
+set the oplock handler for a connection
+****************************************************************************/
+void cli_oplock_handler(struct cli_state *cli,
+ BOOL (*handler)(struct cli_state *, int, unsigned char))
+{
+ cli->oplock_handler = handler;
+}
diff --git a/source/libsmb/cliprint.c b/source/libsmb/cliprint.c
new file mode 100644
index 00000000000..2fb0e59acac
--- /dev/null
+++ b/source/libsmb/cliprint.c
@@ -0,0 +1,158 @@
+/*
+ Unix SMB/CIFS implementation.
+ client print routines
+ Copyright (C) Andrew Tridgell 1994-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 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.
+*/
+
+#define NO_SYSLOG
+
+#include "includes.h"
+
+/*****************************************************************************
+ Convert a character pointer in a cli_call_api() response to a form we can use.
+ This function contains code to prevent core dumps if the server returns
+ invalid data.
+*****************************************************************************/
+static const char *fix_char_ptr(unsigned int datap, unsigned int converter,
+ char *rdata, int rdrcnt)
+{
+ if (datap == 0) { /* turn NULL pointers into zero length strings */
+ return "";
+ } else {
+ unsigned int offset = datap - converter;
+
+ if (offset >= rdrcnt) {
+ DEBUG(1,("bad char ptr: datap=%u, converter=%u rdrcnt=%d>",
+ datap, converter, rdrcnt));
+ return "<ERROR>";
+ } else {
+ return &rdata[offset];
+ }
+ }
+}
+
+
+/****************************************************************************
+call fn() on each entry in a print queue
+****************************************************************************/
+int cli_print_queue(struct cli_state *cli,
+ void (*fn)(struct print_job_info *))
+{
+ char *rparam = NULL;
+ char *rdata = NULL;
+ char *p;
+ unsigned int rdrcnt, rprcnt;
+ pstring param;
+ int result_code=0;
+ int i = -1;
+
+ memset(param,'\0',sizeof(param));
+
+ p = param;
+ SSVAL(p,0,76); /* API function number 76 (DosPrintJobEnum) */
+ p += 2;
+ pstrcpy_base(p,"zWrLeh", param); /* parameter description? */
+ p = skip_string(p,1);
+ pstrcpy_base(p,"WWzWWDDzz", param); /* returned data format */
+ p = skip_string(p,1);
+ pstrcpy_base(p,cli->share, param); /* name of queue */
+ p = skip_string(p,1);
+ SSVAL(p,0,2); /* API function level 2, PRJINFO_2 data structure */
+ SSVAL(p,2,1000); /* size of bytes of returned data buffer */
+ p += 4;
+ pstrcpy_base(p,"", param); /* subformat */
+ p = skip_string(p,1);
+
+ DEBUG(4,("doing cli_print_queue for %s\n", cli->share));
+
+ if (cli_api(cli,
+ param, PTR_DIFF(p,param), 1024, /* Param, length, maxlen */
+ NULL, 0, CLI_BUFFER_SIZE, /* data, length, maxlen */
+ &rparam, &rprcnt, /* return params, length */
+ &rdata, &rdrcnt)) { /* return data, length */
+ int converter;
+ result_code = SVAL(rparam,0);
+ converter = SVAL(rparam,2); /* conversion factor */
+
+ if (result_code == 0) {
+ struct print_job_info job;
+
+ p = rdata;
+
+ for (i = 0; i < SVAL(rparam,4); ++i) {
+ job.id = SVAL(p,0);
+ job.priority = SVAL(p,2);
+ fstrcpy(job.user,
+ fix_char_ptr(SVAL(p,4), converter,
+ rdata, rdrcnt));
+ job.t = make_unix_date3(p + 12);
+ job.size = IVAL(p,16);
+ fstrcpy(job.name,fix_char_ptr(SVAL(p,24),
+ converter,
+ rdata, rdrcnt));
+ fn(&job);
+ p += 28;
+ }
+ }
+ }
+
+ /* If any parameters or data were returned, free the storage. */
+ SAFE_FREE(rparam);
+ SAFE_FREE(rdata);
+
+ return i;
+}
+
+/****************************************************************************
+ cancel a print job
+ ****************************************************************************/
+int cli_printjob_del(struct cli_state *cli, int job)
+{
+ char *rparam = NULL;
+ char *rdata = NULL;
+ char *p;
+ unsigned int rdrcnt,rprcnt;
+ int ret = -1;
+ pstring param;
+
+ memset(param,'\0',sizeof(param));
+
+ p = param;
+ SSVAL(p,0,81); /* DosPrintJobDel() */
+ p += 2;
+ pstrcpy_base(p,"W", param);
+ p = skip_string(p,1);
+ pstrcpy_base(p,"", param);
+ p = skip_string(p,1);
+ SSVAL(p,0,job);
+ p += 2;
+
+ if (cli_api(cli,
+ param, PTR_DIFF(p,param), 1024, /* Param, length, maxlen */
+ NULL, 0, CLI_BUFFER_SIZE, /* data, length, maxlen */
+ &rparam, &rprcnt, /* return params, length */
+ &rdata, &rdrcnt)) { /* return data, length */
+ ret = SVAL(rparam,0);
+ }
+
+ SAFE_FREE(rparam);
+ SAFE_FREE(rdata);
+
+ return ret;
+}
+
+
diff --git a/source/libsmb/cliquota.c b/source/libsmb/cliquota.c
new file mode 100644
index 00000000000..ed808aa1f5c
--- /dev/null
+++ b/source/libsmb/cliquota.c
@@ -0,0 +1,633 @@
+/*
+ Unix SMB/CIFS implementation.
+ client quota functions
+ 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 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"
+
+BOOL cli_get_quota_handle(struct cli_state *cli, int *quota_fnum)
+{
+ *quota_fnum = cli_nt_create_full(cli, FAKE_FILE_NAME_QUOTA,
+ 0x00000016, DESIRED_ACCESS_PIPE,
+ 0x00000000, FILE_SHARE_READ|FILE_SHARE_WRITE,
+ FILE_OPEN, 0x00000000, 0x03);
+
+ if (*quota_fnum == (-1)) {
+ return False;
+ }
+
+ return True;
+}
+
+void free_ntquota_list(SMB_NTQUOTA_LIST **qt_list)
+{
+ if (!qt_list)
+ return;
+
+ if ((*qt_list)->mem_ctx)
+ talloc_destroy((*qt_list)->mem_ctx);
+
+ (*qt_list) = NULL;
+
+ return;
+}
+
+static BOOL parse_user_quota_record(const char *rdata, unsigned int rdata_count, unsigned int *offset, SMB_NTQUOTA_STRUCT *pqt)
+{
+ int sid_len;
+ SMB_NTQUOTA_STRUCT qt;
+
+ ZERO_STRUCT(qt);
+
+ if (!rdata||!offset||!pqt)
+ smb_panic("parse_quota_record: called with NULL POINTER!\n");
+
+ if (rdata_count < 40) {
+ return False;
+ }
+
+ /* offset to next quota record.
+ * 4 bytes IVAL(rdata,0)
+ * unused here...
+ */
+ *offset = IVAL(rdata,0);
+
+ /* sid len */
+ sid_len = IVAL(rdata,4);
+
+ if (rdata_count < 40+sid_len) {
+ return False;
+ }
+
+ /* unknown 8 bytes in pdata
+ * maybe its the change time in NTTIME
+ */
+
+ /* the used space 8 bytes (SMB_BIG_UINT)*/
+ qt.usedspace = (SMB_BIG_UINT)IVAL(rdata,16);
+#ifdef LARGE_SMB_OFF_T
+ qt.usedspace |= (((SMB_BIG_UINT)IVAL(rdata,20)) << 32);
+#else /* LARGE_SMB_OFF_T */
+ if ((IVAL(rdata,20) != 0)&&
+ ((qt.usedspace != 0xFFFFFFFF)||
+ (IVAL(rdata,20)!=0xFFFFFFFF))) {
+ /* more than 32 bits? */
+ return False;
+ }
+#endif /* LARGE_SMB_OFF_T */
+
+ /* the soft quotas 8 bytes (SMB_BIG_UINT)*/
+ qt.softlim = (SMB_BIG_UINT)IVAL(rdata,24);
+#ifdef LARGE_SMB_OFF_T
+ qt.softlim |= (((SMB_BIG_UINT)IVAL(rdata,28)) << 32);
+#else /* LARGE_SMB_OFF_T */
+ if ((IVAL(rdata,28) != 0)&&
+ ((qt.softlim != 0xFFFFFFFF)||
+ (IVAL(rdata,28)!=0xFFFFFFFF))) {
+ /* more than 32 bits? */
+ return False;
+ }
+#endif /* LARGE_SMB_OFF_T */
+
+ /* the hard quotas 8 bytes (SMB_BIG_UINT)*/
+ qt.hardlim = (SMB_BIG_UINT)IVAL(rdata,32);
+#ifdef LARGE_SMB_OFF_T
+ qt.hardlim |= (((SMB_BIG_UINT)IVAL(rdata,36)) << 32);
+#else /* LARGE_SMB_OFF_T */
+ if ((IVAL(rdata,36) != 0)&&
+ ((qt.hardlim != 0xFFFFFFFF)||
+ (IVAL(rdata,36)!=0xFFFFFFFF))) {
+ /* more than 32 bits? */
+ return False;
+ }
+#endif /* LARGE_SMB_OFF_T */
+
+ sid_parse(rdata+40,sid_len,&qt.sid);
+
+ qt.qtype = SMB_USER_QUOTA_TYPE;
+
+ *pqt = qt;
+
+ return True;
+}
+
+BOOL cli_get_user_quota(struct cli_state *cli, int quota_fnum, SMB_NTQUOTA_STRUCT *pqt)
+{
+ BOOL ret = False;
+ uint16 setup;
+ char params[16];
+ unsigned int data_len;
+ char data[SID_MAX_SIZE+8];
+ char *rparam=NULL, *rdata=NULL;
+ unsigned int rparam_count=0, rdata_count=0;
+ unsigned int sid_len;
+ unsigned int offset;
+
+ if (!cli||!pqt)
+ smb_panic("cli_get_user_quota() called with NULL Pointer!");
+
+ setup = NT_TRANSACT_GET_USER_QUOTA;
+
+ SSVAL(params, 0,quota_fnum);
+ SSVAL(params, 2,TRANSACT_GET_USER_QUOTA_FOR_SID);
+ SIVAL(params, 4,0x00000024);
+ SIVAL(params, 8,0x00000000);
+ SIVAL(params,12,0x00000024);
+
+ sid_len = sid_size(&pqt->sid);
+ data_len = sid_len+8;
+ SIVAL(data, 0, 0x00000000);
+ SIVAL(data, 4, sid_len);
+ sid_linearize(data+8, sid_len, &pqt->sid);
+
+ if (!cli_send_nt_trans(cli,
+ NT_TRANSACT_GET_USER_QUOTA,
+ 0,
+ &setup, 1, 0,
+ params, 16, 4,
+ data, data_len, 112)) {
+ DEBUG(1,("Failed to send NT_TRANSACT_GET_USER_QUOTA\n"));
+ goto cleanup;
+ }
+
+
+ if (!cli_receive_nt_trans(cli,
+ &rparam, &rparam_count,
+ &rdata, &rdata_count)) {
+ DEBUG(1,("Failed to recv NT_TRANSACT_GET_USER_QUOTA\n"));
+ goto cleanup;
+ }
+
+ if (cli_is_error(cli)) {
+ ret = False;
+ goto cleanup;
+ } else {
+ ret = True;
+ }
+
+ if ((rparam&&rdata)&&(rparam_count>=4&&rdata_count>=8)) {
+ ret = parse_user_quota_record(rdata, rdata_count, &offset, pqt);
+ } else {
+ DEBUG(0,("Got INVALID NT_TRANSACT_GET_USER_QUOTA reply.\n"));
+ ret = False;
+ }
+
+ cleanup:
+ SAFE_FREE(rparam);
+ SAFE_FREE(rdata);
+ return ret;
+}
+
+BOOL cli_set_user_quota(struct cli_state *cli, int quota_fnum, SMB_NTQUOTA_STRUCT *pqt)
+{
+ BOOL ret = False;
+ uint16 setup;
+ char params[2];
+ char data[112];
+ char *rparam=NULL, *rdata=NULL;
+ unsigned int rparam_count=0, rdata_count=0;
+ unsigned int sid_len;
+ memset(data,'\0',112);
+
+ if (!cli||!pqt)
+ smb_panic("cli_set_user_quota() called with NULL Pointer!");
+
+ setup = NT_TRANSACT_SET_USER_QUOTA;
+
+ SSVAL(params,0,quota_fnum);
+
+ sid_len = sid_size(&pqt->sid);
+ SIVAL(data,0,0);
+ SIVAL(data,4,sid_len);
+ SBIG_UINT(data, 8,(SMB_BIG_UINT)0);
+ SBIG_UINT(data,16,pqt->usedspace);
+ SBIG_UINT(data,24,pqt->softlim);
+ SBIG_UINT(data,32,pqt->hardlim);
+ sid_linearize(data+40, sid_len, &pqt->sid);
+
+ if (!cli_send_nt_trans(cli,
+ NT_TRANSACT_SET_USER_QUOTA,
+ 0,
+ &setup, 1, 0,
+ params, 2, 0,
+ data, 112, 0)) {
+ DEBUG(1,("Failed to send NT_TRANSACT_SET_USER_QUOTA\n"));
+ goto cleanup;
+ }
+
+
+ if (!cli_receive_nt_trans(cli,
+ &rparam, &rparam_count,
+ &rdata, &rdata_count)) {
+ DEBUG(1,("NT_TRANSACT_SET_USER_QUOTA failed\n"));
+ goto cleanup;
+ }
+
+ if (cli_is_error(cli)) {
+ ret = False;
+ goto cleanup;
+ } else {
+ ret = True;
+ }
+
+ cleanup:
+ SAFE_FREE(rparam);
+ SAFE_FREE(rdata);
+ return ret;
+}
+
+BOOL cli_list_user_quota(struct cli_state *cli, int quota_fnum, SMB_NTQUOTA_LIST **pqt_list)
+{
+ BOOL ret = False;
+ uint16 setup;
+ char params[16];
+ char *rparam=NULL, *rdata=NULL;
+ unsigned int rparam_count=0, rdata_count=0;
+ unsigned int offset;
+ const char *curdata = NULL;
+ unsigned int curdata_count = 0;
+ TALLOC_CTX *mem_ctx = NULL;
+ SMB_NTQUOTA_STRUCT qt;
+ SMB_NTQUOTA_LIST *tmp_list_ent;
+
+ if (!cli||!pqt_list)
+ smb_panic("cli_list_user_quota() called with NULL Pointer!");
+
+ setup = NT_TRANSACT_GET_USER_QUOTA;
+
+ SSVAL(params, 0,quota_fnum);
+ SSVAL(params, 2,TRANSACT_GET_USER_QUOTA_LIST_START);
+ SIVAL(params, 4,0x00000000);
+ SIVAL(params, 8,0x00000000);
+ SIVAL(params,12,0x00000000);
+
+ if (!cli_send_nt_trans(cli,
+ NT_TRANSACT_GET_USER_QUOTA,
+ 0,
+ &setup, 1, 0,
+ params, 16, 4,
+ NULL, 0, 2048)) {
+ DEBUG(1,("Failed to send NT_TRANSACT_GET_USER_QUOTA\n"));
+ goto cleanup;
+ }
+
+
+ if (!cli_receive_nt_trans(cli,
+ &rparam, &rparam_count,
+ &rdata, &rdata_count)) {
+ DEBUG(1,("Failed to recv NT_TRANSACT_GET_USER_QUOTA\n"));
+ goto cleanup;
+ }
+
+ if (cli_is_error(cli)) {
+ ret = False;
+ goto cleanup;
+ } else {
+ ret = True;
+ }
+
+ if (rdata_count == 0) {
+ *pqt_list = NULL;
+ return True;
+ }
+
+ if ((mem_ctx=talloc_init("SMB_USER_QUOTA_LIST"))==NULL) {
+ DEBUG(0,("talloc_init() failed\n"));
+ return (-1);
+ }
+
+ offset = 1;
+ for (curdata=rdata,curdata_count=rdata_count;
+ ((curdata)&&(curdata_count>=8)&&(offset>0));
+ curdata +=offset,curdata_count -= offset) {
+ ZERO_STRUCT(qt);
+ if (!parse_user_quota_record(curdata, curdata_count, &offset, &qt)) {
+ DEBUG(1,("Failed to parse the quota record\n"));
+ goto cleanup;
+ }
+
+ if ((tmp_list_ent=(SMB_NTQUOTA_LIST *)talloc_zero(mem_ctx,sizeof(SMB_NTQUOTA_LIST)))==NULL) {
+ DEBUG(0,("talloc_zero() failed\n"));
+ return (-1);
+ }
+
+ if ((tmp_list_ent->quotas=(SMB_NTQUOTA_STRUCT *)talloc_zero(mem_ctx,sizeof(SMB_NTQUOTA_STRUCT)))==NULL) {
+ DEBUG(0,("talloc_zero() failed\n"));
+ return (-1);
+ }
+
+ memcpy(tmp_list_ent->quotas,&qt,sizeof(qt));
+ tmp_list_ent->mem_ctx = mem_ctx;
+
+ DLIST_ADD((*pqt_list),tmp_list_ent);
+ }
+
+ SSVAL(params, 2,TRANSACT_GET_USER_QUOTA_LIST_CONTINUE);
+ while(1) {
+ if (!cli_send_nt_trans(cli,
+ NT_TRANSACT_GET_USER_QUOTA,
+ 0,
+ &setup, 1, 0,
+ params, 16, 4,
+ NULL, 0, 2048)) {
+ DEBUG(1,("Failed to send NT_TRANSACT_GET_USER_QUOTA\n"));
+ goto cleanup;
+ }
+
+ SAFE_FREE(rparam);
+ SAFE_FREE(rdata);
+ if (!cli_receive_nt_trans(cli,
+ &rparam, &rparam_count,
+ &rdata, &rdata_count)) {
+ DEBUG(1,("Failed to recv NT_TRANSACT_GET_USER_QUOTA\n"));
+ goto cleanup;
+ }
+
+ if (cli_is_error(cli)) {
+ ret = False;
+ goto cleanup;
+ } else {
+ ret = True;
+ }
+
+ if (rdata_count == 0) {
+ break;
+ }
+
+ offset = 1;
+ for (curdata=rdata,curdata_count=rdata_count;
+ ((curdata)&&(curdata_count>=8)&&(offset>0));
+ curdata +=offset,curdata_count -= offset) {
+ ZERO_STRUCT(qt);
+ if (!parse_user_quota_record(curdata, curdata_count, &offset, &qt)) {
+ DEBUG(1,("Failed to parse the quota record\n"));
+ goto cleanup;
+ }
+
+ if ((tmp_list_ent=(SMB_NTQUOTA_LIST *)talloc_zero(mem_ctx,sizeof(SMB_NTQUOTA_LIST)))==NULL) {
+ DEBUG(0,("talloc_zero() failed\n"));
+ talloc_destroy(mem_ctx);
+ goto cleanup;
+ }
+
+ if ((tmp_list_ent->quotas=(SMB_NTQUOTA_STRUCT *)talloc_zero(mem_ctx,sizeof(SMB_NTQUOTA_STRUCT)))==NULL) {
+ DEBUG(0,("talloc_zero() failed\n"));
+ talloc_destroy(mem_ctx);
+ goto cleanup;
+ }
+
+ memcpy(tmp_list_ent->quotas,&qt,sizeof(qt));
+ tmp_list_ent->mem_ctx = mem_ctx;
+
+ DLIST_ADD((*pqt_list),tmp_list_ent);
+ }
+ }
+
+
+ ret = True;
+ cleanup:
+ SAFE_FREE(rparam);
+ SAFE_FREE(rdata);
+
+ return ret;
+}
+
+BOOL cli_get_fs_quota_info(struct cli_state *cli, int quota_fnum, SMB_NTQUOTA_STRUCT *pqt)
+{
+ BOOL ret = False;
+ uint16 setup;
+ char param[2];
+ char *rparam=NULL, *rdata=NULL;
+ unsigned int rparam_count=0, rdata_count=0;
+ SMB_NTQUOTA_STRUCT qt;
+ ZERO_STRUCT(qt);
+
+ if (!cli||!pqt)
+ smb_panic("cli_get_fs_quota_info() called with NULL Pointer!");
+
+ setup = TRANSACT2_QFSINFO;
+
+ SSVAL(param,0,SMB_FS_QUOTA_INFORMATION);
+
+ if (!cli_send_trans(cli, SMBtrans2,
+ NULL,
+ 0, 0,
+ &setup, 1, 0,
+ param, 2, 0,
+ NULL, 0, 560)) {
+ goto cleanup;
+ }
+
+ if (!cli_receive_trans(cli, SMBtrans2,
+ &rparam, &rparam_count,
+ &rdata, &rdata_count)) {
+ goto cleanup;
+ }
+
+ if (cli_is_error(cli)) {
+ ret = False;
+ goto cleanup;
+ } else {
+ ret = True;
+ }
+
+ if (rdata_count < 48) {
+ goto cleanup;
+ }
+
+ /* unknown_1 24 NULL bytes in pdata*/
+
+ /* the soft quotas 8 bytes (SMB_BIG_UINT)*/
+ qt.softlim = (SMB_BIG_UINT)IVAL(rdata,24);
+#ifdef LARGE_SMB_OFF_T
+ qt.softlim |= (((SMB_BIG_UINT)IVAL(rdata,28)) << 32);
+#else /* LARGE_SMB_OFF_T */
+ if ((IVAL(rdata,28) != 0)&&
+ ((qt.softlim != 0xFFFFFFFF)||
+ (IVAL(rdata,28)!=0xFFFFFFFF))) {
+ /* more than 32 bits? */
+ goto cleanup;
+ }
+#endif /* LARGE_SMB_OFF_T */
+
+ /* the hard quotas 8 bytes (SMB_BIG_UINT)*/
+ qt.hardlim = (SMB_BIG_UINT)IVAL(rdata,32);
+#ifdef LARGE_SMB_OFF_T
+ qt.hardlim |= (((SMB_BIG_UINT)IVAL(rdata,36)) << 32);
+#else /* LARGE_SMB_OFF_T */
+ if ((IVAL(rdata,36) != 0)&&
+ ((qt.hardlim != 0xFFFFFFFF)||
+ (IVAL(rdata,36)!=0xFFFFFFFF))) {
+ /* more than 32 bits? */
+ goto cleanup;
+ }
+#endif /* LARGE_SMB_OFF_T */
+
+ /* quota_flags 2 bytes **/
+ qt.qflags = SVAL(rdata,40);
+
+ qt.qtype = SMB_USER_FS_QUOTA_TYPE;
+
+ *pqt = qt;
+
+ ret = True;
+cleanup:
+ SAFE_FREE(rparam);
+ SAFE_FREE(rdata);
+
+ return ret;
+}
+
+BOOL cli_set_fs_quota_info(struct cli_state *cli, int quota_fnum, SMB_NTQUOTA_STRUCT *pqt)
+{
+ BOOL ret = False;
+ uint16 setup;
+ char param[4];
+ char data[48];
+ char *rparam=NULL, *rdata=NULL;
+ unsigned int rparam_count=0, rdata_count=0;
+ SMB_NTQUOTA_STRUCT qt;
+ ZERO_STRUCT(qt);
+ memset(data,'\0',48);
+
+ if (!cli||!pqt)
+ smb_panic("cli_set_fs_quota_info() called with NULL Pointer!");
+
+ setup = TRANSACT2_SETFSINFO;
+
+ SSVAL(param,0,quota_fnum);
+ SSVAL(param,2,SMB_FS_QUOTA_INFORMATION);
+
+ /* Unknown1 24 NULL bytes*/
+
+ /* Default Soft Quota 8 bytes */
+ SBIG_UINT(data,24,pqt->softlim);
+
+ /* Default Hard Quota 8 bytes */
+ SBIG_UINT(data,32,pqt->hardlim);
+
+ /* Quota flag 2 bytes */
+ SSVAL(data,40,pqt->qflags);
+
+ /* Unknown3 6 NULL bytes */
+
+ if (!cli_send_trans(cli, SMBtrans2,
+ NULL,
+ 0, 0,
+ &setup, 1, 0,
+ param, 4, 0,
+ data, 48, 0)) {
+ goto cleanup;
+ }
+
+ if (!cli_receive_trans(cli, SMBtrans2,
+ &rparam, &rparam_count,
+ &rdata, &rdata_count)) {
+ goto cleanup;
+ }
+
+ if (cli_is_error(cli)) {
+ ret = False;
+ goto cleanup;
+ } else {
+ ret = True;
+ }
+
+cleanup:
+ SAFE_FREE(rparam);
+ SAFE_FREE(rdata);
+
+ return ret;
+}
+
+static char *quota_str_static(SMB_BIG_UINT val, BOOL special, BOOL _numeric)
+{
+ static fstring buffer;
+
+ memset(buffer,'\0',sizeof(buffer));
+
+ if (!_numeric&&special&&(val == SMB_NTQUOTAS_NO_LIMIT)) {
+ fstr_sprintf(buffer,"NO LIMIT");
+ return buffer;
+ }
+#if defined(HAVE_LONGLONG)
+ fstr_sprintf(buffer,"%llu",val);
+#else
+ fstr_sprintf(buffer,"%lu",val);
+#endif
+ return buffer;
+}
+
+void dump_ntquota(SMB_NTQUOTA_STRUCT *qt, BOOL _verbose, BOOL _numeric, void (*_sidtostring)(fstring str, DOM_SID *sid, BOOL _numeric))
+{
+ if (!qt)
+ smb_panic("dump_ntquota() called with NULL pointer");
+
+ switch (qt->qtype) {
+ case SMB_USER_FS_QUOTA_TYPE:
+ {
+ d_printf("File System QUOTAS:\n");
+ d_printf("Limits:\n");
+ d_printf(" Default Soft Limit: %15s\n",quota_str_static(qt->softlim,True,_numeric));
+ d_printf(" Default Hard Limit: %15s\n",quota_str_static(qt->hardlim,True,_numeric));
+ d_printf("Quota Flags:\n");
+ d_printf(" Quotas Enabled: %s\n",
+ ((qt->qflags&QUOTAS_ENABLED)||(qt->qflags&QUOTAS_DENY_DISK))?"On":"Off");
+ d_printf(" Deny Disk: %s\n",(qt->qflags&QUOTAS_DENY_DISK)?"On":"Off");
+ d_printf(" Log Soft Limit: %s\n",(qt->qflags&QUOTAS_LOG_THRESHOLD)?"On":"Off");
+ d_printf(" Log Hard Limit: %s\n",(qt->qflags&QUOTAS_LOG_LIMIT)?"On":"Off");
+ }
+ break;
+ case SMB_USER_QUOTA_TYPE:
+ {
+ fstring username_str = {0};
+
+ if (_sidtostring) {
+ _sidtostring(username_str,&qt->sid,_numeric);
+ } else {
+ fstrcpy(username_str,sid_string_static(&qt->sid));
+ }
+
+ if (_verbose) {
+ d_printf("Quotas for User: %s\n",username_str);
+ d_printf("Used Space: %15s\n",quota_str_static(qt->usedspace,False,_numeric));
+ d_printf("Soft Limit: %15s\n",quota_str_static(qt->softlim,True,_numeric));
+ d_printf("Hard Limit: %15s\n",quota_str_static(qt->hardlim,True,_numeric));
+ } else {
+ d_printf("%-30s: ",username_str);
+ d_printf("%15s/",quota_str_static(qt->usedspace,False,_numeric));
+ d_printf("%15s/",quota_str_static(qt->softlim,True,_numeric));
+ d_printf("%15s\n",quota_str_static(qt->hardlim,True,_numeric));
+ }
+ }
+ break;
+ default:
+ d_printf("dump_ntquota() invalid qtype(%d)\n",qt->qtype);
+ return;
+ }
+}
+
+void dump_ntquota_list(SMB_NTQUOTA_LIST **qtl, BOOL _verbose, BOOL _numeric, void (*_sidtostring)(fstring str, DOM_SID *sid, BOOL _numeric))
+{
+ SMB_NTQUOTA_LIST *cur;
+
+ for (cur = *qtl;cur;cur = cur->next) {
+ if (cur->quotas)
+ dump_ntquota(cur->quotas,_verbose,_numeric,_sidtostring);
+ }
+}
diff --git a/source/libsmb/clirap.c b/source/libsmb/clirap.c
new file mode 100644
index 00000000000..f8204e05d68
--- /dev/null
+++ b/source/libsmb/clirap.c
@@ -0,0 +1,743 @@
+/*
+ Unix SMB/CIFS implementation.
+ client RAP calls
+ Copyright (C) Andrew Tridgell 1994-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 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.
+*/
+
+#define NO_SYSLOG
+
+#include "includes.h"
+
+
+/****************************************************************************
+Call a remote api on an arbitrary pipe. takes param, data and setup buffers.
+****************************************************************************/
+BOOL cli_api_pipe(struct cli_state *cli, const char *pipe_name,
+ uint16 *setup, uint32 setup_count, uint32 max_setup_count,
+ char *params, uint32 param_count, uint32 max_param_count,
+ char *data, uint32 data_count, uint32 max_data_count,
+ char **rparam, uint32 *rparam_count,
+ char **rdata, uint32 *rdata_count)
+{
+ cli_send_trans(cli, SMBtrans,
+ pipe_name,
+ 0,0, /* fid, flags */
+ setup, setup_count, max_setup_count,
+ params, param_count, max_param_count,
+ data, data_count, max_data_count);
+
+ return (cli_receive_trans(cli, SMBtrans,
+ rparam, (unsigned int *)rparam_count,
+ rdata, (unsigned int *)rdata_count));
+}
+
+/****************************************************************************
+call a remote api
+****************************************************************************/
+BOOL cli_api(struct cli_state *cli,
+ char *param, int prcnt, int mprcnt,
+ char *data, int drcnt, int mdrcnt,
+ char **rparam, unsigned int *rprcnt,
+ char **rdata, unsigned int *rdrcnt)
+{
+ cli_send_trans(cli,SMBtrans,
+ PIPE_LANMAN, /* Name */
+ 0,0, /* fid, flags */
+ NULL,0,0, /* Setup, length, max */
+ param, prcnt, mprcnt, /* Params, length, max */
+ data, drcnt, mdrcnt /* Data, length, max */
+ );
+
+ return (cli_receive_trans(cli,SMBtrans,
+ rparam, rprcnt,
+ rdata, rdrcnt));
+}
+
+
+/****************************************************************************
+perform a NetWkstaUserLogon
+****************************************************************************/
+BOOL cli_NetWkstaUserLogon(struct cli_state *cli,char *user, char *workstation)
+{
+ char *rparam = NULL;
+ char *rdata = NULL;
+ char *p;
+ unsigned int rdrcnt,rprcnt;
+ pstring param;
+
+ memset(param, 0, sizeof(param));
+
+ /* send a SMBtrans command with api NetWkstaUserLogon */
+ p = param;
+ SSVAL(p,0,132); /* api number */
+ p += 2;
+ pstrcpy_base(p,"OOWb54WrLh",param);
+ p = skip_string(p,1);
+ pstrcpy_base(p,"WB21BWDWWDDDDDDDzzzD",param);
+ p = skip_string(p,1);
+ SSVAL(p,0,1);
+ p += 2;
+ pstrcpy_base(p,user,param);
+ strupper_m(p);
+ p += 21;
+ p++;
+ p += 15;
+ p++;
+ pstrcpy_base(p, workstation, param);
+ strupper_m(p);
+ p += 16;
+ SSVAL(p, 0, CLI_BUFFER_SIZE);
+ p += 2;
+ SSVAL(p, 0, CLI_BUFFER_SIZE);
+ p += 2;
+
+ if (cli_api(cli,
+ param, PTR_DIFF(p,param),1024, /* param, length, max */
+ NULL, 0, CLI_BUFFER_SIZE, /* data, length, max */
+ &rparam, &rprcnt, /* return params, return size */
+ &rdata, &rdrcnt /* return data, return size */
+ )) {
+ cli->rap_error = rparam? SVAL(rparam,0) : -1;
+ p = rdata;
+
+ if (cli->rap_error == 0) {
+ DEBUG(4,("NetWkstaUserLogon success\n"));
+ cli->privileges = SVAL(p, 24);
+ /* The cli->eff_name field used to be set here
+ but it wasn't used anywhere else. */
+ } else {
+ DEBUG(1,("NetwkstaUserLogon gave error %d\n", cli->rap_error));
+ }
+ }
+
+ SAFE_FREE(rparam);
+ SAFE_FREE(rdata);
+ return (cli->rap_error == 0);
+}
+
+/****************************************************************************
+call a NetShareEnum - try and browse available connections on a host
+****************************************************************************/
+int cli_RNetShareEnum(struct cli_state *cli, void (*fn)(const char *, uint32, const char *, void *), void *state)
+{
+ char *rparam = NULL;
+ char *rdata = NULL;
+ char *p;
+ unsigned int rdrcnt,rprcnt;
+ pstring param;
+ int count = -1;
+
+ /* now send a SMBtrans command with api RNetShareEnum */
+ p = param;
+ SSVAL(p,0,0); /* api number */
+ p += 2;
+ pstrcpy_base(p,"WrLeh",param);
+ p = skip_string(p,1);
+ pstrcpy_base(p,"B13BWz",param);
+ p = skip_string(p,1);
+ SSVAL(p,0,1);
+ /*
+ * Win2k needs a *smaller* buffer than 0xFFFF here -
+ * it returns "out of server memory" with 0xFFFF !!! JRA.
+ */
+ SSVAL(p,2,0xFFE0);
+ p += 4;
+
+ if (cli_api(cli,
+ param, PTR_DIFF(p,param), 1024, /* Param, length, maxlen */
+ NULL, 0, 0xFFE0, /* data, length, maxlen - Win2k needs a small buffer here too ! */
+ &rparam, &rprcnt, /* return params, length */
+ &rdata, &rdrcnt)) /* return data, length */
+ {
+ int res = rparam? SVAL(rparam,0) : -1;
+
+ if (res == 0 || res == ERRmoredata) {
+ int converter=SVAL(rparam,2);
+ int i;
+
+ count=SVAL(rparam,4);
+ p = rdata;
+
+ for (i=0;i<count;i++,p+=20) {
+ char *sname = p;
+ int type = SVAL(p,14);
+ int comment_offset = IVAL(p,16) & 0xFFFF;
+ const char *cmnt = comment_offset?(rdata+comment_offset-converter):"";
+ pstring s1, s2;
+
+ pull_ascii_pstring(s1, sname);
+ pull_ascii_pstring(s2, cmnt);
+
+ fn(s1, type, s2, state);
+ }
+ } else {
+ DEBUG(4,("NetShareEnum res=%d\n", res));
+ }
+ } else {
+ DEBUG(4,("NetShareEnum failed\n"));
+ }
+
+ SAFE_FREE(rparam);
+ SAFE_FREE(rdata);
+
+ return count;
+}
+
+
+/****************************************************************************
+call a NetServerEnum for the specified workgroup and servertype mask. This
+function then calls the specified callback function for each name returned.
+
+The callback function takes 4 arguments: the machine name, the server type,
+the comment and a state pointer.
+****************************************************************************/
+BOOL cli_NetServerEnum(struct cli_state *cli, char *workgroup, uint32 stype,
+ void (*fn)(const char *, uint32, const char *, void *),
+ void *state)
+{
+ char *rparam = NULL;
+ char *rdata = NULL;
+ unsigned int rdrcnt,rprcnt;
+ char *p;
+ pstring param;
+ int uLevel = 1;
+ int count = -1;
+
+ /* send a SMBtrans command with api NetServerEnum */
+ p = param;
+ SSVAL(p,0,0x68); /* api number */
+ p += 2;
+ pstrcpy_base(p,"WrLehDz", param);
+ p = skip_string(p,1);
+
+ pstrcpy_base(p,"B16BBDz", param);
+
+ p = skip_string(p,1);
+ SSVAL(p,0,uLevel);
+ SSVAL(p,2,CLI_BUFFER_SIZE);
+ p += 4;
+ SIVAL(p,0,stype);
+ p += 4;
+
+ p += push_ascii(p, workgroup, sizeof(pstring)-PTR_DIFF(p,param)-1, STR_TERMINATE|STR_UPPER);
+
+ if (cli_api(cli,
+ param, PTR_DIFF(p,param), 8, /* params, length, max */
+ NULL, 0, CLI_BUFFER_SIZE, /* data, length, max */
+ &rparam, &rprcnt, /* return params, return size */
+ &rdata, &rdrcnt /* return data, return size */
+ )) {
+ int res = rparam? SVAL(rparam,0) : -1;
+
+ if (res == 0 || res == ERRmoredata) {
+ int i;
+ int converter=SVAL(rparam,2);
+
+ count=SVAL(rparam,4);
+ p = rdata;
+
+ for (i = 0;i < count;i++, p += 26) {
+ char *sname = p;
+ int comment_offset = (IVAL(p,22) & 0xFFFF)-converter;
+ const char *cmnt = comment_offset?(rdata+comment_offset):"";
+ pstring s1, s2;
+
+ if (comment_offset < 0 || comment_offset > (int)rdrcnt) continue;
+
+ stype = IVAL(p,18) & ~SV_TYPE_LOCAL_LIST_ONLY;
+
+ pull_ascii_pstring(s1, sname);
+ pull_ascii_pstring(s2, cmnt);
+ fn(s1, stype, s2, state);
+ }
+ }
+ }
+
+ SAFE_FREE(rparam);
+ SAFE_FREE(rdata);
+
+ return(count > 0);
+}
+
+
+
+/****************************************************************************
+Send a SamOEMChangePassword command
+****************************************************************************/
+BOOL cli_oem_change_password(struct cli_state *cli, const char *user, const char *new_password,
+ const char *old_password)
+{
+ pstring param;
+ char data[532];
+ char *p = param;
+ unsigned char old_pw_hash[16];
+ unsigned char new_pw_hash[16];
+ unsigned int data_len;
+ unsigned int param_len = 0;
+ char *rparam = NULL;
+ char *rdata = NULL;
+ unsigned int rprcnt, rdrcnt;
+
+ if (strlen(user) >= sizeof(fstring)-1) {
+ DEBUG(0,("cli_oem_change_password: user name %s is too long.\n", user));
+ return False;
+ }
+
+ SSVAL(p,0,214); /* SamOEMChangePassword command. */
+ p += 2;
+ pstrcpy_base(p, "zsT", param);
+ p = skip_string(p,1);
+ pstrcpy_base(p, "B516B16", param);
+ p = skip_string(p,1);
+ pstrcpy_base(p,user, param);
+ p = skip_string(p,1);
+ SSVAL(p,0,532);
+ p += 2;
+
+ param_len = PTR_DIFF(p,param);
+
+ /*
+ * Get the Lanman hash of the old password, we
+ * use this as the key to make_oem_passwd_hash().
+ */
+ E_deshash(old_password, old_pw_hash);
+
+ encode_pw_buffer(data, new_password, STR_ASCII);
+
+#ifdef DEBUG_PASSWORD
+ DEBUG(100,("make_oem_passwd_hash\n"));
+ dump_data(100, data, 516);
+#endif
+ SamOEMhash( (unsigned char *)data, (unsigned char *)old_pw_hash, 516);
+
+ /*
+ * Now place the old password hash in the data.
+ */
+ E_deshash(new_password, new_pw_hash);
+
+ E_old_pw_hash( new_pw_hash, old_pw_hash, (uchar *)&data[516]);
+
+ data_len = 532;
+
+ if (cli_send_trans(cli,SMBtrans,
+ PIPE_LANMAN, /* name */
+ 0,0, /* fid, flags */
+ NULL,0,0, /* setup, length, max */
+ param,param_len,2, /* param, length, max */
+ data,data_len,0 /* data, length, max */
+ ) == False) {
+ DEBUG(0,("cli_oem_change_password: Failed to send password change for user %s\n",
+ user ));
+ return False;
+ }
+
+ if (!cli_receive_trans(cli,SMBtrans,
+ &rparam, &rprcnt,
+ &rdata, &rdrcnt)) {
+ DEBUG(0,("cli_oem_change_password: Failed to recieve reply to password change for user %s\n",
+ user ));
+ return False;
+ }
+
+ if (rparam)
+ cli->rap_error = SVAL(rparam,0);
+
+ SAFE_FREE(rparam);
+ SAFE_FREE(rdata);
+
+ return (cli->rap_error == 0);
+}
+
+
+/****************************************************************************
+send a qpathinfo call
+****************************************************************************/
+BOOL cli_qpathinfo(struct cli_state *cli, const char *fname,
+ time_t *c_time, time_t *a_time, time_t *m_time,
+ size_t *size, uint16 *mode)
+{
+ unsigned int data_len = 0;
+ unsigned int param_len = 0;
+ unsigned int rparam_len, rdata_len;
+ uint16 setup = TRANSACT2_QPATHINFO;
+ pstring param;
+ char *rparam=NULL, *rdata=NULL;
+ int count=8;
+ BOOL ret;
+ time_t (*date_fn)(void *);
+ char *p;
+
+ p = param;
+ memset(p, 0, 6);
+ SSVAL(p, 0, SMB_INFO_STANDARD);
+ p += 6;
+ p += clistr_push(cli, p, fname, sizeof(pstring)-6, STR_TERMINATE);
+
+ param_len = PTR_DIFF(p, param);
+
+ do {
+ ret = (cli_send_trans(cli, SMBtrans2,
+ NULL, /* Name */
+ -1, 0, /* fid, flags */
+ &setup, 1, 0, /* setup, length, max */
+ param, param_len, 10, /* param, length, max */
+ NULL, data_len, cli->max_xmit /* data, length, max */
+ ) &&
+ cli_receive_trans(cli, SMBtrans2,
+ &rparam, &rparam_len,
+ &rdata, &rdata_len));
+ if (!cli_is_dos_error(cli)) break;
+ if (!ret) {
+ /* we need to work around a Win95 bug - sometimes
+ it gives ERRSRV/ERRerror temprarily */
+ uint8 eclass;
+ uint32 ecode;
+ cli_dos_error(cli, &eclass, &ecode);
+ if (eclass != ERRSRV || ecode != ERRerror) break;
+ smb_msleep(100);
+ }
+ } while (count-- && ret==False);
+
+ if (!ret || !rdata || rdata_len < 22) {
+ return False;
+ }
+
+ if (cli->win95) {
+ date_fn = make_unix_date;
+ } else {
+ date_fn = make_unix_date2;
+ }
+
+ if (c_time) {
+ *c_time = date_fn(rdata+0);
+ }
+ if (a_time) {
+ *a_time = date_fn(rdata+4);
+ }
+ if (m_time) {
+ *m_time = date_fn(rdata+8);
+ }
+ if (size) {
+ *size = IVAL(rdata, 12);
+ }
+ if (mode) {
+ *mode = SVAL(rdata,l1_attrFile);
+ }
+
+ SAFE_FREE(rdata);
+ SAFE_FREE(rparam);
+ return True;
+}
+
+/****************************************************************************
+send a qpathinfo call with the SMB_QUERY_FILE_ALL_INFO info level
+****************************************************************************/
+BOOL cli_qpathinfo2(struct cli_state *cli, const char *fname,
+ time_t *c_time, time_t *a_time, time_t *m_time,
+ time_t *w_time, size_t *size, uint16 *mode,
+ SMB_INO_T *ino)
+{
+ unsigned int data_len = 0;
+ unsigned int param_len = 0;
+ uint16 setup = TRANSACT2_QPATHINFO;
+ pstring param;
+ char *rparam=NULL, *rdata=NULL;
+ char *p;
+
+ p = param;
+ memset(p, 0, 6);
+ SSVAL(p, 0, SMB_QUERY_FILE_ALL_INFO);
+ p += 6;
+ p += clistr_push(cli, p, fname, sizeof(pstring)-6, STR_TERMINATE);
+
+ param_len = PTR_DIFF(p, param);
+
+ if (!cli_send_trans(cli, SMBtrans2,
+ NULL, /* name */
+ -1, 0, /* fid, flags */
+ &setup, 1, 0, /* setup, length, max */
+ param, param_len, 10, /* param, length, max */
+ NULL, data_len, cli->max_xmit /* data, length, max */
+ )) {
+ return False;
+ }
+
+ if (!cli_receive_trans(cli, SMBtrans2,
+ &rparam, &param_len,
+ &rdata, &data_len)) {
+ return False;
+ }
+
+ if (!rdata || data_len < 22) {
+ return False;
+ }
+
+ if (c_time) {
+ *c_time = interpret_long_date(rdata+0) - cli->serverzone;
+ }
+ if (a_time) {
+ *a_time = interpret_long_date(rdata+8) - cli->serverzone;
+ }
+ if (m_time) {
+ *m_time = interpret_long_date(rdata+16) - cli->serverzone;
+ }
+ if (w_time) {
+ *w_time = interpret_long_date(rdata+24) - cli->serverzone;
+ }
+ if (mode) {
+ *mode = SVAL(rdata, 32);
+ }
+ if (size) {
+ *size = IVAL(rdata, 48);
+ }
+ if (ino) {
+ *ino = IVAL(rdata, 64);
+ }
+
+ SAFE_FREE(rdata);
+ SAFE_FREE(rparam);
+ return True;
+}
+
+
+/****************************************************************************
+send a qfileinfo QUERY_FILE_NAME_INFO call
+****************************************************************************/
+BOOL cli_qfilename(struct cli_state *cli, int fnum,
+ pstring name)
+{
+ unsigned int data_len = 0;
+ unsigned int param_len = 0;
+ uint16 setup = TRANSACT2_QFILEINFO;
+ pstring param;
+ char *rparam=NULL, *rdata=NULL;
+
+ param_len = 4;
+ memset(param, 0, param_len);
+ SSVAL(param, 0, fnum);
+ SSVAL(param, 2, SMB_QUERY_FILE_NAME_INFO);
+
+ if (!cli_send_trans(cli, SMBtrans2,
+ NULL, /* name */
+ -1, 0, /* fid, flags */
+ &setup, 1, 0, /* setup, length, max */
+ param, param_len, 2, /* param, length, max */
+ NULL, data_len, cli->max_xmit /* data, length, max */
+ )) {
+ return False;
+ }
+
+ if (!cli_receive_trans(cli, SMBtrans2,
+ &rparam, &param_len,
+ &rdata, &data_len)) {
+ return False;
+ }
+
+ if (!rdata || data_len < 4) {
+ return False;
+ }
+
+ clistr_pull(cli, name, rdata+4, sizeof(pstring), IVAL(rdata, 0), STR_UNICODE);
+
+ return True;
+}
+
+
+/****************************************************************************
+send a qfileinfo call
+****************************************************************************/
+BOOL cli_qfileinfo(struct cli_state *cli, int fnum,
+ uint16 *mode, size_t *size,
+ time_t *c_time, time_t *a_time, time_t *m_time,
+ time_t *w_time, SMB_INO_T *ino)
+{
+ unsigned int data_len = 0;
+ unsigned int param_len = 0;
+ uint16 setup = TRANSACT2_QFILEINFO;
+ pstring param;
+ char *rparam=NULL, *rdata=NULL;
+
+ /* if its a win95 server then fail this - win95 totally screws it
+ up */
+ if (cli->win95) return False;
+
+ param_len = 4;
+
+ memset(param, 0, param_len);
+ SSVAL(param, 0, fnum);
+ SSVAL(param, 2, SMB_QUERY_FILE_ALL_INFO);
+
+ if (!cli_send_trans(cli, SMBtrans2,
+ NULL, /* name */
+ -1, 0, /* fid, flags */
+ &setup, 1, 0, /* setup, length, max */
+ param, param_len, 2, /* param, length, max */
+ NULL, data_len, cli->max_xmit /* data, length, max */
+ )) {
+ return False;
+ }
+
+ if (!cli_receive_trans(cli, SMBtrans2,
+ &rparam, &param_len,
+ &rdata, &data_len)) {
+ return False;
+ }
+
+ if (!rdata || data_len < 68) {
+ return False;
+ }
+
+ if (c_time) {
+ *c_time = interpret_long_date(rdata+0) - cli->serverzone;
+ }
+ if (a_time) {
+ *a_time = interpret_long_date(rdata+8) - cli->serverzone;
+ }
+ if (m_time) {
+ *m_time = interpret_long_date(rdata+16) - cli->serverzone;
+ }
+ if (w_time) {
+ *w_time = interpret_long_date(rdata+24) - cli->serverzone;
+ }
+ if (mode) {
+ *mode = SVAL(rdata, 32);
+ }
+ if (size) {
+ *size = IVAL(rdata, 48);
+ }
+ if (ino) {
+ *ino = IVAL(rdata, 64);
+ }
+
+ SAFE_FREE(rdata);
+ SAFE_FREE(rparam);
+ return True;
+}
+
+/****************************************************************************
+send a qfileinfo call
+****************************************************************************/
+BOOL cli_qfileinfo_test(struct cli_state *cli, int fnum, int level, char **poutdata, uint32 *poutlen)
+{
+ unsigned int data_len = 0;
+ unsigned int param_len = 0;
+ uint16 setup = TRANSACT2_QFILEINFO;
+ pstring param;
+ char *rparam=NULL, *rdata=NULL;
+
+ *poutdata = NULL;
+ *poutlen = 0;
+
+ /* if its a win95 server then fail this - win95 totally screws it
+ up */
+ if (cli->win95)
+ return False;
+
+ param_len = 4;
+
+ memset(param, 0, param_len);
+ SSVAL(param, 0, fnum);
+ SSVAL(param, 2, level);
+
+ if (!cli_send_trans(cli, SMBtrans2,
+ NULL, /* name */
+ -1, 0, /* fid, flags */
+ &setup, 1, 0, /* setup, length, max */
+ param, param_len, 2, /* param, length, max */
+ NULL, data_len, cli->max_xmit /* data, length, max */
+ )) {
+ return False;
+ }
+
+ if (!cli_receive_trans(cli, SMBtrans2,
+ &rparam, &param_len,
+ &rdata, &data_len)) {
+ return False;
+ }
+
+ *poutdata = memdup(rdata, data_len);
+ *poutlen = data_len;
+
+ SAFE_FREE(rdata);
+ SAFE_FREE(rparam);
+ return True;
+}
+
+
+
+/****************************************************************************
+send a qpathinfo SMB_QUERY_FILE_ALT_NAME_INFO call
+****************************************************************************/
+NTSTATUS cli_qpathinfo_alt_name(struct cli_state *cli, const char *fname, fstring alt_name)
+{
+ unsigned int data_len = 0;
+ unsigned int param_len = 0;
+ uint16 setup = TRANSACT2_QPATHINFO;
+ pstring param;
+ char *rparam=NULL, *rdata=NULL;
+ int count=8;
+ char *p;
+ BOOL ret;
+ unsigned int len;
+
+ p = param;
+ memset(p, 0, 6);
+ SSVAL(p, 0, SMB_QUERY_FILE_ALT_NAME_INFO);
+ p += 6;
+ p += clistr_push(cli, p, fname, sizeof(pstring)-6, STR_TERMINATE);
+
+ param_len = PTR_DIFF(p, param);
+
+ do {
+ ret = (cli_send_trans(cli, SMBtrans2,
+ NULL, /* Name */
+ -1, 0, /* fid, flags */
+ &setup, 1, 0, /* setup, length, max */
+ param, param_len, 10, /* param, length, max */
+ NULL, data_len, cli->max_xmit /* data, length, max */
+ ) &&
+ cli_receive_trans(cli, SMBtrans2,
+ &rparam, &param_len,
+ &rdata, &data_len));
+ if (!ret && cli_is_dos_error(cli)) {
+ /* we need to work around a Win95 bug - sometimes
+ it gives ERRSRV/ERRerror temprarily */
+ uint8 eclass;
+ uint32 ecode;
+ cli_dos_error(cli, &eclass, &ecode);
+ if (eclass != ERRSRV || ecode != ERRerror) break;
+ smb_msleep(100);
+ }
+ } while (count-- && ret==False);
+
+ if (!ret || !rdata || data_len < 4) {
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ len = IVAL(rdata, 0);
+
+ if (len > data_len - 4) {
+ return NT_STATUS_INVALID_NETWORK_RESPONSE;
+ }
+
+ clistr_pull(cli, alt_name, rdata+4, sizeof(fstring), len, STR_UNICODE);
+
+ SAFE_FREE(rdata);
+ SAFE_FREE(rparam);
+
+ return NT_STATUS_OK;
+}
diff --git a/source/libsmb/clirap2.c b/source/libsmb/clirap2.c
new file mode 100644
index 00000000000..12a3d63aff3
--- /dev/null
+++ b/source/libsmb/clirap2.c
@@ -0,0 +1,1970 @@
+/*
+ Samba Unix/Linux SMB client library
+ More client RAP (SMB Remote Procedure Calls) functions
+ Copyright (C) 2001 Steve French (sfrench@us.ibm.com)
+ Copyright (C) 2001 Jim McDonough (jmcd@us.ibm.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 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.
+*/
+
+/*****************************************************/
+/* */
+/* Additional RAP functionality */
+/* */
+/* RAP is the original SMB RPC, documented */
+/* by Microsoft and X/Open in the 1990s and */
+/* supported by most SMB/CIFS servers although */
+/* it is unlikely that any one implementation */
+/* supports all RAP command codes since some */
+/* are quite obsolete and a few are specific */
+/* to a particular network operating system */
+/* */
+/* Although it has largely been replaced */
+/* for complex remote admistration and management */
+/* (of servers) by the relatively newer */
+/* DCE/RPC based remote API (which better handles */
+/* large >64K data structures), there are many */
+/* important administrative and resource location */
+/* tasks and user tasks (e.g. password change) */
+/* that are performed via RAP. */
+/* */
+/* Although a few of the RAP calls are implemented */
+/* in the Samba client library already (clirap.c) */
+/* the new ones are in clirap2.c for easy patching */
+/* and integration and a corresponding header */
+/* file, rap.h, has been created. */
+/* */
+/* This is based on data from the CIFS spec */
+/* and the LAN Server and LAN Manager */
+/* Programming Reference books and published */
+/* RAP document and CIFS forum postings and */
+/* lots of trial and error */
+/* */
+/* Function names changed from API_ (as they are */
+/* in the CIFS specification) to RAP_ in order */
+/* to avoid confusion with other API calls */
+/* sent via DCE RPC */
+/* */
+/*****************************************************/
+
+/*****************************************************/
+/* */
+/* cifsrap.c already includes support for: */
+/* */
+/* WshareEnum ( API number 0, level 1) */
+/* NetServerEnum2 (API num 104, level 1) */
+/* WWkstaUserLogon (132) */
+/* SamOEMchgPasswordUser2_P (214) */
+/* */
+/* cifsprint.c already includes support for: */
+/* */
+/* WPrintJobEnum (API num 76, level 2) */
+/* WPrintJobDel (API num 81) */
+/* */
+/*****************************************************/
+
+#define NO_SYSLOG
+
+#include "includes.h"
+
+#define WORDSIZE 2
+#define DWORDSIZE 4
+
+#define PUTBYTE(p,b) do {SCVAL(p,0,b); p++;} while(0)
+#define GETBYTE(p,b) do {b = CVAL(p,0); p++;} while(0)
+#define PUTWORD(p,w) do {SSVAL(p,0,w); p += WORDSIZE;} while(0)
+#define GETWORD(p,w) do {w = SVAL(p,0); p += WORDSIZE;} while(0)
+#define PUTDWORD(p,d) do {SIVAL(p,0,d); p += DWORDSIZE;} while(0)
+#define GETDWORD(p,d) do {d = IVAL(p,0); p += DWORDSIZE;} while(0)
+#define GETRES(p) p ? SVAL(p,0) : -1
+/* put string s at p with max len n and increment p past string */
+#define PUTSTRING(p,s,n) do {\
+ push_ascii(p,s?s:"",n?n:256,STR_TERMINATE);\
+ p = skip_string(p,1);\
+ } while(0)
+/* put string s and p, using fixed len l, and increment p by l */
+#define PUTSTRINGF(p,s,l) do {\
+ push_ascii(p,s?s:"",l,STR_TERMINATE);\
+ p += l;\
+ } while (0)
+/* put string pointer at p, supplying offset o from rdata r, store */
+/* dword offset at p, increment p by 4 and o by length of s. This */
+/* means on the first call, you must calc the offset yourself! */
+#define PUTSTRINGP(p,s,r,o) do {\
+ if (s) {\
+ push_ascii(r+o,s,strlen(s)+1,STR_TERMINATE);\
+ PUTDWORD(p,o);\
+ o += strlen(s) + 1;\
+ } else PUTDWORD(p,0);\
+ }while(0);
+/* get asciiz string s from p, increment p past string */
+#define GETSTRING(p,s) do {\
+ pull_ascii_pstring(s,p);\
+ p = skip_string(p,1);\
+ } while(0)
+/* get fixed length l string s from p, increment p by l */
+#define GETSTRINGF(p,s,l) do {\
+ pull_ascii_pstring(s,p);\
+ p += l;\
+ } while(0)
+/* get string s from offset (obtained at p) from rdata r - converter c */
+#define GETSTRINGP(p,s,r,c) do {\
+ uint32 off;\
+ GETDWORD(p,off);\
+ off &= 0x0000FFFF; /* mask the obsolete segment number from the offset */ \
+ pull_ascii_pstring(s, off?(r+off-c):"");\
+ } while(0)
+
+static char *make_header(char *param, uint16 apinum, const char *reqfmt, const char *datafmt)
+{
+ PUTWORD(param,apinum);
+ if (reqfmt)
+ PUTSTRING(param,reqfmt,0);
+ else
+ *param++ = (char) 0;
+
+ if (datafmt)
+ PUTSTRING(param,datafmt,0);
+ else
+ *param++ = (char) 0;
+
+ return param;
+}
+
+
+/****************************************************************************
+ call a NetGroupDelete - delete user group from remote server
+****************************************************************************/
+int cli_NetGroupDelete(struct cli_state *cli, const char *group_name )
+{
+ char *rparam = NULL;
+ char *rdata = NULL;
+ char *p;
+ unsigned int rdrcnt,rprcnt;
+ int res;
+ char param[WORDSIZE /* api number */
+ +sizeof(RAP_NetGroupDel_REQ) /* parm string */
+ +1 /* no ret string */
+ +RAP_GROUPNAME_LEN /* group to del */
+ +WORDSIZE]; /* reserved word */
+
+ /* now send a SMBtrans command with api GroupDel */
+ p = make_header(param, RAP_WGroupDel, RAP_NetGroupDel_REQ, NULL);
+ PUTSTRING(p, group_name, RAP_GROUPNAME_LEN);
+ PUTWORD(p,0); /* reserved word MBZ on input */
+
+ if (cli_api(cli,
+ param, PTR_DIFF(p,param), 1024, /* Param, length, maxlen */
+ NULL, 0, 200, /* data, length, maxlen */
+ &rparam, &rprcnt, /* return params, length */
+ &rdata, &rdrcnt)) /* return data, length */
+ {
+ res = GETRES(rparam);
+
+ if (res == 0) {
+ /* nothing to do */
+ }
+ else if ((res == 5) || (res == 65)) {
+ DEBUG(1, ("Access Denied\n"));
+ }
+ else if (res == 2220) {
+ DEBUG (1, ("Group does not exist\n"));
+ }
+ else {
+ DEBUG(4,("NetGroupDelete res=%d\n", res));
+ }
+ } else {
+ res = -1;
+ DEBUG(4,("NetGroupDelete failed\n"));
+ }
+
+ SAFE_FREE(rparam);
+ SAFE_FREE(rdata);
+
+ return res;
+}
+
+/****************************************************************************
+ call a NetGroupAdd - add user group to remote server
+****************************************************************************/
+int cli_NetGroupAdd(struct cli_state *cli, RAP_GROUP_INFO_1 * grinfo )
+{
+ char *rparam = NULL;
+ char *rdata = NULL;
+ char *p;
+ unsigned int rdrcnt,rprcnt;
+ int res;
+ char param[WORDSIZE /* api number */
+ +sizeof(RAP_NetGroupAdd_REQ) /* req string */
+ +sizeof(RAP_GROUP_INFO_L1) /* return string */
+ +WORDSIZE /* info level */
+ +WORDSIZE]; /* reserved word */
+
+ char data[1024];
+
+ /* offset into data of free format strings. Will be updated */
+ /* by PUTSTRINGP macro and end up with total data length. */
+ int soffset = RAP_GROUPNAME_LEN + 1 + DWORDSIZE;
+
+ /* now send a SMBtrans command with api WGroupAdd */
+
+ p = make_header(param, RAP_WGroupAdd,
+ RAP_NetGroupAdd_REQ, RAP_GROUP_INFO_L1);
+ PUTWORD(p, 1); /* info level */
+ PUTWORD(p, 0); /* reserved word 0 */
+
+ p = data;
+ PUTSTRINGF(p, grinfo->group_name, RAP_GROUPNAME_LEN);
+ PUTBYTE(p, 0); /* pad byte 0 */
+ PUTSTRINGP(p, grinfo->comment, data, soffset);
+
+ if (cli_api(cli,
+ param, sizeof(param), 1024, /* Param, length, maxlen */
+ data, soffset, sizeof(data), /* data, length, maxlen */
+ &rparam, &rprcnt, /* return params, length */
+ &rdata, &rdrcnt)) /* return data, length */
+ {
+ res = GETRES(rparam);
+
+ if (res == 0) {
+ /* nothing to do */
+ } else if ((res == 5) || (res == 65)) {
+ DEBUG(1, ("Access Denied\n"));
+ }
+ else if (res == 2223) {
+ DEBUG (1, ("Group already exists\n"));
+ }
+ else {
+ DEBUG(4,("NetGroupAdd res=%d\n", res));
+ }
+ } else {
+ res = -1;
+ DEBUG(4,("NetGroupAdd failed\n"));
+ }
+
+ SAFE_FREE(rparam);
+ SAFE_FREE(rdata);
+
+ return res;
+}
+
+/****************************************************************************
+call a NetGroupEnum - try and list user groups on a different host
+****************************************************************************/
+int cli_RNetGroupEnum(struct cli_state *cli, void (*fn)(const char *, const char *, void *), void *state)
+{
+ char param[WORDSIZE /* api number */
+ +sizeof(RAP_NetGroupEnum_REQ) /* parm string */
+ +sizeof(RAP_GROUP_INFO_L1) /* return string */
+ +WORDSIZE /* info level */
+ +WORDSIZE]; /* buffer size */
+ char *p;
+ char *rparam = NULL;
+ char *rdata = NULL;
+ unsigned int rprcnt, rdrcnt;
+ int res = -1;
+
+
+ memset(param, '\0', sizeof(param));
+ p = make_header(param, RAP_WGroupEnum,
+ RAP_NetGroupEnum_REQ, RAP_GROUP_INFO_L1);
+ PUTWORD(p,1); /* Info level 1 */ /* add level 0 */
+ PUTWORD(p,0xFFE0); /* Return buffer size */
+
+ if (cli_api(cli,
+ param, PTR_DIFF(p,param),8,
+ NULL, 0, 0xFFE0 /* data area size */,
+ &rparam, &rprcnt,
+ &rdata, &rdrcnt)) {
+ res = GETRES(rparam);
+ cli->rap_error = res;
+ if(cli->rap_error == 234)
+ DEBUG(1,("Not all group names were returned (such as those longer than 21 characters)\n"));
+ else if (cli->rap_error != 0) {
+ DEBUG(1,("NetGroupEnum gave error %d\n", cli->rap_error));
+ }
+ }
+
+ if (rdata) {
+ if (res == 0 || res == ERRmoredata) {
+ int i, converter, count;
+
+ p = rparam + WORDSIZE; /* skip result */
+ GETWORD(p, converter);
+ GETWORD(p, count);
+
+ for (i=0,p=rdata;i<count;i++) {
+ pstring comment;
+ char groupname[RAP_GROUPNAME_LEN];
+
+ GETSTRINGF(p, groupname, RAP_GROUPNAME_LEN);
+ p++; /* pad byte */
+ GETSTRINGP(p, comment, rdata, converter);
+
+ fn(groupname, comment, cli);
+ }
+ } else {
+ DEBUG(4,("NetGroupEnum res=%d\n", res));
+ }
+ } else {
+ DEBUG(4,("NetGroupEnum no data returned\n"));
+ }
+
+ SAFE_FREE(rparam);
+ SAFE_FREE(rdata);
+
+ return res;
+}
+
+int cli_NetGroupDelUser(struct cli_state * cli, const char *group_name, const char *user_name)
+{
+ char *rparam = NULL;
+ char *rdata = NULL;
+ char *p;
+ unsigned int rdrcnt,rprcnt;
+ int res;
+ char param[WORDSIZE /* api number */
+ +sizeof(RAP_NetGroupDelUser_REQ) /* parm string */
+ +1 /* no ret string */
+ +RAP_GROUPNAME_LEN /* group name */
+ +RAP_USERNAME_LEN]; /* user to del */
+
+ /* now send a SMBtrans command with api GroupMemberAdd */
+ p = make_header(param, RAP_WGroupDelUser, RAP_NetGroupDelUser_REQ, NULL);
+ PUTSTRING(p,group_name,RAP_GROUPNAME_LEN);
+ PUTSTRING(p,user_name,RAP_USERNAME_LEN);
+
+ if (cli_api(cli,
+ param, PTR_DIFF(p,param), 1024, /* Param, length, maxlen */
+ NULL, 0, 200, /* data, length, maxlen */
+ &rparam, &rprcnt, /* return params, length */
+ &rdata, &rdrcnt)) /* return data, length */
+ {
+ res = GETRES(rparam);
+
+ switch(res) {
+ case 0:
+ break;
+ case 5:
+ case 65:
+ DEBUG(1, ("Access Denied\n"));
+ break;
+ case 50:
+ DEBUG(1, ("Not supported by server\n"));
+ break;
+ case 2220:
+ DEBUG(1, ("Group does not exist\n"));
+ break;
+ case 2221:
+ DEBUG(1, ("User does not exist\n"));
+ break;
+ case 2237:
+ DEBUG(1, ("User is not in group\n"));
+ break;
+ default:
+ DEBUG(4,("NetGroupDelUser res=%d\n", res));
+ }
+ } else {
+ res = -1;
+ DEBUG(4,("NetGroupDelUser failed\n"));
+ }
+
+ SAFE_FREE(rparam);
+ SAFE_FREE(rdata);
+
+ return res;
+}
+
+int cli_NetGroupAddUser(struct cli_state * cli, const char *group_name, const char *user_name)
+{
+ char *rparam = NULL;
+ char *rdata = NULL;
+ char *p;
+ unsigned int rdrcnt,rprcnt;
+ int res;
+ char param[WORDSIZE /* api number */
+ +sizeof(RAP_NetGroupAddUser_REQ) /* parm string */
+ +1 /* no ret string */
+ +RAP_GROUPNAME_LEN /* group name */
+ +RAP_USERNAME_LEN]; /* user to add */
+
+ /* now send a SMBtrans command with api GroupMemberAdd */
+ p = make_header(param, RAP_WGroupAddUser, RAP_NetGroupAddUser_REQ, NULL);
+ PUTSTRING(p,group_name,RAP_GROUPNAME_LEN);
+ PUTSTRING(p,user_name,RAP_USERNAME_LEN);
+
+ if (cli_api(cli,
+ param, PTR_DIFF(p,param), 1024, /* Param, length, maxlen */
+ NULL, 0, 200, /* data, length, maxlen */
+ &rparam, &rprcnt, /* return params, length */
+ &rdata, &rdrcnt)) /* return data, length */
+ {
+ res = GETRES(rparam);
+
+ switch(res) {
+ case 0:
+ break;
+ case 5:
+ case 65:
+ DEBUG(1, ("Access Denied\n"));
+ break;
+ case 50:
+ DEBUG(1, ("Not supported by server\n"));
+ break;
+ case 2220:
+ DEBUG(1, ("Group does not exist\n"));
+ break;
+ case 2221:
+ DEBUG(1, ("User does not exist\n"));
+ break;
+ default:
+ DEBUG(4,("NetGroupAddUser res=%d\n", res));
+ }
+ } else {
+ res = -1;
+ DEBUG(4,("NetGroupAddUser failed\n"));
+ }
+
+ SAFE_FREE(rparam);
+ SAFE_FREE(rdata);
+
+ return res;
+}
+
+
+int cli_NetGroupGetUsers(struct cli_state * cli, const char *group_name, void (*fn)(const char *, void *), void *state )
+{
+ char *rparam = NULL;
+ char *rdata = NULL;
+ char *p;
+ unsigned int rdrcnt,rprcnt;
+ int res = -1;
+ char param[WORDSIZE /* api number */
+ +sizeof(RAP_NetGroupGetUsers_REQ)/* parm string */
+ +sizeof(RAP_GROUP_USERS_INFO_0) /* return string */
+ +RAP_GROUPNAME_LEN /* group name */
+ +WORDSIZE /* info level */
+ +WORDSIZE]; /* buffer size */
+
+ /* now send a SMBtrans command with api GroupGetUsers */
+ p = make_header(param, RAP_WGroupGetUsers,
+ RAP_NetGroupGetUsers_REQ, RAP_GROUP_USERS_INFO_0);
+ PUTSTRING(p,group_name,RAP_GROUPNAME_LEN-1);
+ PUTWORD(p,0); /* info level 0 */
+ PUTWORD(p,0xFFE0); /* return buffer size */
+
+ if (cli_api(cli,
+ param, PTR_DIFF(p,param),PTR_DIFF(p,param),
+ NULL, 0, CLI_BUFFER_SIZE,
+ &rparam, &rprcnt,
+ &rdata, &rdrcnt)) {
+ res = GETRES(rparam);
+ cli->rap_error = res;
+ if (res != 0) {
+ DEBUG(1,("NetGroupGetUsers gave error %d\n", res));
+ }
+ }
+ if (rdata) {
+ if (res == 0 || res == ERRmoredata) {
+ int i, converter, count;
+ fstring username;
+ p = rparam +WORDSIZE;
+ GETWORD(p, converter);
+ GETWORD(p, count);
+
+ for (i=0,p=rdata; i<count; i++) {
+ GETSTRINGF(p, username, RAP_USERNAME_LEN);
+ fn(username, state);
+ }
+ } else {
+ DEBUG(4,("NetGroupGetUsers res=%d\n", res));
+ }
+ } else {
+ DEBUG(4,("NetGroupGetUsers no data returned\n"));
+ }
+ SAFE_FREE(rdata);
+ SAFE_FREE(rparam);
+ return res;
+}
+
+int cli_NetUserGetGroups(struct cli_state * cli, const char *user_name, void (*fn)(const char *, void *), void *state )
+{
+ char *rparam = NULL;
+ char *rdata = NULL;
+ char *p;
+ unsigned int rdrcnt,rprcnt;
+ int res = -1;
+ char param[WORDSIZE /* api number */
+ +sizeof(RAP_NetUserGetGroups_REQ)/* parm string */
+ +sizeof(RAP_GROUP_USERS_INFO_0) /* return string */
+ +RAP_USERNAME_LEN /* user name */
+ +WORDSIZE /* info level */
+ +WORDSIZE]; /* buffer size */
+
+ /* now send a SMBtrans command with api GroupGetUsers */
+ p = make_header(param, RAP_WUserGetGroups,
+ RAP_NetUserGetGroups_REQ, RAP_GROUP_USERS_INFO_0);
+ PUTSTRING(p,user_name,RAP_USERNAME_LEN-1);
+ PUTWORD(p,0); /* info level 0 */
+ PUTWORD(p,0xFFE0); /* return buffer size */
+
+ if (cli_api(cli,
+ param, PTR_DIFF(p,param),PTR_DIFF(p,param),
+ NULL, 0, CLI_BUFFER_SIZE,
+ &rparam, &rprcnt,
+ &rdata, &rdrcnt)) {
+ res = GETRES(rparam);
+ cli->rap_error = res;
+ if (res != 0) {
+ DEBUG(1,("NetUserGetGroups gave error %d\n", res));
+ }
+ }
+ if (rdata) {
+ if (res == 0 || res == ERRmoredata) {
+ int i, converter, count;
+ fstring groupname;
+ p = rparam +WORDSIZE;
+ GETWORD(p, converter);
+ GETWORD(p, count);
+
+ for (i=0,p=rdata; i<count; i++) {
+ GETSTRINGF(p, groupname, RAP_USERNAME_LEN);
+ fn(groupname, state);
+ }
+ } else {
+ DEBUG(4,("NetUserGetGroups res=%d\n", res));
+ }
+ } else {
+ DEBUG(4,("NetUserGetGroups no data returned\n"));
+ }
+ SAFE_FREE(rdata);
+ SAFE_FREE(rparam);
+ return res;
+}
+
+
+/****************************************************************************
+ call a NetUserDelete - delete user from remote server
+****************************************************************************/
+int cli_NetUserDelete(struct cli_state *cli, const char * user_name )
+{
+ char *rparam = NULL;
+ char *rdata = NULL;
+ char *p;
+ unsigned int rdrcnt,rprcnt;
+ int res;
+ char param[WORDSIZE /* api number */
+ +sizeof(RAP_NetGroupDel_REQ) /* parm string */
+ +1 /* no ret string */
+ +RAP_USERNAME_LEN /* user to del */
+ +WORDSIZE]; /* reserved word */
+
+ /* now send a SMBtrans command with api UserDel */
+ p = make_header(param, RAP_WUserDel, RAP_NetGroupDel_REQ, NULL);
+ PUTSTRING(p, user_name, RAP_USERNAME_LEN);
+ PUTWORD(p,0); /* reserved word MBZ on input */
+
+ if (cli_api(cli,
+ param, PTR_DIFF(p,param), 1024, /* Param, length, maxlen */
+ NULL, 0, 200, /* data, length, maxlen */
+ &rparam, &rprcnt, /* return params, length */
+ &rdata, &rdrcnt)) /* return data, length */
+ {
+ res = GETRES(rparam);
+
+ if (res == 0) {
+ /* nothing to do */
+ }
+ else if ((res == 5) || (res == 65)) {
+ DEBUG(1, ("Access Denied\n"));
+ }
+ else if (res == 2221) {
+ DEBUG (1, ("User does not exist\n"));
+ }
+ else {
+ DEBUG(4,("NetUserDelete res=%d\n", res));
+ }
+ } else {
+ res = -1;
+ DEBUG(4,("NetUserDelete failed\n"));
+ }
+
+ SAFE_FREE(rparam);
+ SAFE_FREE(rdata);
+
+ return res;
+}
+
+/****************************************************************************
+ call a NetUserAdd - add user to remote server
+****************************************************************************/
+int cli_NetUserAdd(struct cli_state *cli, RAP_USER_INFO_1 * userinfo )
+{
+
+
+
+ char *rparam = NULL;
+ char *rdata = NULL;
+ char *p;
+ unsigned int rdrcnt,rprcnt;
+ int res;
+ char param[WORDSIZE /* api number */
+ +sizeof(RAP_NetUserAdd2_REQ) /* req string */
+ +sizeof(RAP_USER_INFO_L1) /* data string */
+ +WORDSIZE /* info level */
+ +WORDSIZE /* buffer length */
+ +WORDSIZE]; /* reserved */
+
+ char data[1024];
+ /* offset into data of free format strings. Will be updated */
+ /* by PUTSTRINGP macro and end up with total data length. */
+ int soffset=RAP_USERNAME_LEN+1 /* user name + pad */
+ + RAP_UPASSWD_LEN /* password */
+ + DWORDSIZE /* password age */
+ + WORDSIZE /* privilege */
+ + DWORDSIZE /* home dir ptr */
+ + DWORDSIZE /* comment ptr */
+ + WORDSIZE /* flags */
+ + DWORDSIZE; /* login script ptr*/
+
+ /* now send a SMBtrans command with api NetUserAdd */
+ p = make_header(param, RAP_WUserAdd2,
+ RAP_NetUserAdd2_REQ, RAP_USER_INFO_L1);
+ PUTWORD(p, 1); /* info level */
+
+ PUTWORD(p, 0); /* pwencrypt */
+ if(userinfo->passwrd)
+ PUTWORD(p,MIN(strlen(userinfo->passwrd), RAP_UPASSWD_LEN));
+ else
+ PUTWORD(p, 0); /* password length */
+
+ p = data;
+ memset(data, '\0', soffset);
+
+ PUTSTRINGF(p, userinfo->user_name, RAP_USERNAME_LEN);
+ PUTBYTE(p, 0); /* pad byte 0 */
+ PUTSTRINGF(p, userinfo->passwrd, RAP_UPASSWD_LEN);
+ PUTDWORD(p, 0); /* pw age - n.a. on user add */
+ PUTWORD(p, userinfo->priv);
+ PUTSTRINGP(p, userinfo->home_dir, data, soffset);
+ PUTSTRINGP(p, userinfo->comment, data, soffset);
+ PUTWORD(p, userinfo->userflags);
+ PUTSTRINGP(p, userinfo->logon_script, data, soffset);
+
+ if (cli_api(cli,
+ param, sizeof(param), 1024, /* Param, length, maxlen */
+ data, soffset, sizeof(data), /* data, length, maxlen */
+ &rparam, &rprcnt, /* return params, length */
+ &rdata, &rdrcnt)) /* return data, length */
+ {
+ res = GETRES(rparam);
+
+ if (res == 0) {
+ /* nothing to do */
+ }
+ else if ((res == 5) || (res == 65)) {
+ DEBUG(1, ("Access Denied\n"));
+ }
+ else if (res == 2224) {
+ DEBUG (1, ("User already exists\n"));
+ }
+ else {
+ DEBUG(4,("NetUserAdd res=%d\n", res));
+ }
+ } else {
+ res = -1;
+ DEBUG(4,("NetUserAdd failed\n"));
+ }
+
+ SAFE_FREE(rparam);
+ SAFE_FREE(rdata);
+
+ return res;
+}
+
+/****************************************************************************
+call a NetUserEnum - try and list users on a different host
+****************************************************************************/
+int cli_RNetUserEnum(struct cli_state *cli, void (*fn)(const char *, const char *, const char *, const char *, void *), void *state)
+{
+ char param[WORDSIZE /* api number */
+ +sizeof(RAP_NetUserEnum_REQ) /* parm string */
+ +sizeof(RAP_USER_INFO_L1) /* return string */
+ +WORDSIZE /* info level */
+ +WORDSIZE]; /* buffer size */
+ char *p;
+ char *rparam = NULL;
+ char *rdata = NULL;
+ unsigned int rprcnt, rdrcnt;
+ int res = -1;
+
+
+ memset(param, '\0', sizeof(param));
+ p = make_header(param, RAP_WUserEnum,
+ RAP_NetUserEnum_REQ, RAP_USER_INFO_L1);
+ PUTWORD(p,1); /* Info level 1 */
+ PUTWORD(p,0xFF00); /* Return buffer size */
+
+/* BB Fix handling of large numbers of users to be returned */
+ if (cli_api(cli,
+ param, PTR_DIFF(p,param),8,
+ NULL, 0, CLI_BUFFER_SIZE,
+ &rparam, &rprcnt,
+ &rdata, &rdrcnt)) {
+ res = GETRES(rparam);
+ cli->rap_error = res;
+ if (cli->rap_error != 0) {
+ DEBUG(1,("NetUserEnum gave error %d\n", cli->rap_error));
+ }
+ }
+ if (rdata) {
+ if (res == 0 || res == ERRmoredata) {
+ int i, converter, count;
+ char username[RAP_USERNAME_LEN];
+ char userpw[RAP_UPASSWD_LEN];
+ pstring comment, homedir, logonscript;
+ int pwage, priv, flags;
+
+ p = rparam + WORDSIZE; /* skip result */
+ GETWORD(p, converter);
+ GETWORD(p, count);
+
+ for (i=0,p=rdata;i<count;i++) {
+ GETSTRINGF(p, username, RAP_USERNAME_LEN);
+ p++; /* pad byte */
+ GETSTRINGF(p, userpw, RAP_UPASSWD_LEN);
+ GETDWORD(p, pwage); /* password age */
+ GETWORD(p, priv); /* 0=guest, 1=user, 2=admin */
+ GETSTRINGP(p, homedir, rdata, converter);
+ GETSTRINGP(p, comment, rdata, converter);
+ GETWORD(p, flags);
+ GETSTRINGP(p, logonscript, rdata, converter);
+
+ fn(username, comment, homedir, logonscript, cli);
+ }
+ } else {
+ DEBUG(4,("NetUserEnum res=%d\n", res));
+ }
+ } else {
+ DEBUG(4,("NetUserEnum no data returned\n"));
+ }
+
+ SAFE_FREE(rparam);
+ SAFE_FREE(rdata);
+
+ return res;
+}
+
+/****************************************************************************
+ call a NetFileClose2 - close open file on another session to server
+****************************************************************************/
+int cli_NetFileClose(struct cli_state *cli, uint32 file_id )
+{
+ char *rparam = NULL;
+ char *rdata = NULL;
+ char *p;
+ unsigned int rdrcnt,rprcnt;
+ char param[WORDSIZE /* api number */
+ +sizeof(RAP_WFileClose2_REQ) /* req string */
+ +1 /* no ret string */
+ +DWORDSIZE]; /* file ID */
+ int res = -1;
+
+ /* now send a SMBtrans command with api RNetShareEnum */
+ p = make_header(param, RAP_WFileClose2, RAP_WFileClose2_REQ, NULL);
+ PUTDWORD(p, file_id);
+
+ if (cli_api(cli,
+ param, PTR_DIFF(p,param), 1024, /* Param, length, maxlen */
+ NULL, 0, 200, /* data, length, maxlen */
+ &rparam, &rprcnt, /* return params, length */
+ &rdata, &rdrcnt)) /* return data, length */
+ {
+ res = GETRES(rparam);
+
+ if (res == 0) {
+ /* nothing to do */
+ } else if (res == 2314){
+ DEBUG(1, ("NetFileClose2 - attempt to close non-existant file open instance\n"));
+ } else {
+ DEBUG(4,("NetFileClose2 res=%d\n", res));
+ }
+ } else {
+ res = -1;
+ DEBUG(4,("NetFileClose2 failed\n"));
+ }
+
+ SAFE_FREE(rparam);
+ SAFE_FREE(rdata);
+
+ return res;
+}
+
+/****************************************************************************
+call a NetFileGetInfo - get information about server file opened from other
+ workstation
+****************************************************************************/
+int cli_NetFileGetInfo(struct cli_state *cli, uint32 file_id, void (*fn)(const char *, const char *, uint16, uint16, uint32))
+{
+ char *rparam = NULL;
+ char *rdata = NULL;
+ char *p;
+ unsigned int rdrcnt,rprcnt;
+ int res;
+ char param[WORDSIZE /* api number */
+ +sizeof(RAP_WFileGetInfo2_REQ) /* req string */
+ +sizeof(RAP_FILE_INFO_L3) /* return string */
+ +DWORDSIZE /* file ID */
+ +WORDSIZE /* info level */
+ +WORDSIZE]; /* buffer size */
+
+ /* now send a SMBtrans command with api RNetShareEnum */
+ p = make_header(param, RAP_WFileGetInfo2,
+ RAP_WFileGetInfo2_REQ, RAP_FILE_INFO_L3);
+ PUTDWORD(p, file_id);
+ PUTWORD(p, 3); /* info level */
+ PUTWORD(p, 0x1000); /* buffer size */
+ if (cli_api(cli,
+ param, PTR_DIFF(p,param), 1024, /* Param, length, maxlen */
+ NULL, 0, 0x1000, /* data, length, maxlen */
+ &rparam, &rprcnt, /* return params, length */
+ &rdata, &rdrcnt)) /* return data, length */
+ {
+ res = GETRES(rparam);
+ if (res == 0 || res == ERRmoredata) {
+ int converter,id, perms, locks;
+ pstring fpath, fuser;
+
+ p = rparam + WORDSIZE; /* skip result */
+ GETWORD(p, converter);
+
+ p = rdata;
+ GETDWORD(p, id);
+ GETWORD(p, perms);
+ GETWORD(p, locks);
+ GETSTRINGP(p, fpath, rdata, converter);
+ GETSTRINGP(p, fuser, rdata, converter);
+
+ fn(fpath, fuser, perms, locks, id);
+ } else {
+ DEBUG(4,("NetFileGetInfo2 res=%d\n", res));
+ }
+ } else {
+ res = -1;
+ DEBUG(4,("NetFileGetInfo2 failed\n"));
+ }
+
+ SAFE_FREE(rparam);
+ SAFE_FREE(rdata);
+
+ return res;
+}
+
+/****************************************************************************
+* Call a NetFileEnum2 - list open files on an SMB server
+*
+* PURPOSE: Remotes a NetFileEnum API call to the current server or target
+* server listing the files open via the network (and their
+* corresponding open instance ids)
+*
+* Dependencies: none
+*
+* Parameters:
+* cli - pointer to cli_state structure
+* user - if present, return only files opened by this remote user
+* base_path - if present, return only files opened below this
+* base path
+* fn - display function to invoke for each entry in the result
+*
+*
+* Returns:
+* True - success
+* False - failure
+*
+****************************************************************************/
+int cli_NetFileEnum(struct cli_state *cli, char * user, char * base_path, void (*fn)(const char *, const char *, uint16, uint16, uint32))
+{
+ char *rparam = NULL;
+ char *rdata = NULL;
+ char *p;
+ unsigned int rdrcnt,rprcnt;
+ char param[WORDSIZE /* api number */
+ +sizeof(RAP_WFileEnum2_REQ) /* req string */
+ +sizeof(RAP_FILE_INFO_L3) /* return string */
+ +256 /* base path (opt) */
+ +RAP_USERNAME_LEN /* user name (opt) */
+ +WORDSIZE /* info level */
+ +WORDSIZE /* buffer size */
+ +DWORDSIZE /* resume key ? */
+ +DWORDSIZE]; /* resume key ? */
+ int count = -1;
+
+ /* now send a SMBtrans command with api RNetShareEnum */
+ p = make_header(param, RAP_WFileEnum2,
+ RAP_WFileEnum2_REQ, RAP_FILE_INFO_L3);
+
+ PUTSTRING(p, base_path, 256);
+ PUTSTRING(p, user, RAP_USERNAME_LEN);
+ PUTWORD(p, 3); /* info level */
+ PUTWORD(p, 0xFF00); /* buffer size */
+ PUTDWORD(p, 0); /* zero out the resume key */
+ PUTDWORD(p, 0); /* or is this one the resume key? */
+
+ if (cli_api(cli,
+ param, PTR_DIFF(p,param), 1024, /* Param, length, maxlen */
+ NULL, 0, 0xFF00, /* data, length, maxlen */
+ &rparam, &rprcnt, /* return params, length */
+ &rdata, &rdrcnt)) /* return data, length */
+ {
+ int res = GETRES(rparam);
+
+ if (res == 0 || res == ERRmoredata) {
+ int converter, i;
+
+ p = rparam + WORDSIZE; /* skip result */
+ GETWORD(p, converter);
+ GETWORD(p, count);
+
+ p = rdata;
+ for (i=0; i<count; i++) {
+ int id, perms, locks;
+ pstring fpath, fuser;
+
+ GETDWORD(p, id);
+ GETWORD(p, perms);
+ GETWORD(p, locks);
+ GETSTRINGP(p, fpath, rdata, converter);
+ GETSTRINGP(p, fuser, rdata, converter);
+
+ fn(fpath, fuser, perms, locks, id);
+ } /* BB fix ERRmoredata case to send resume request */
+ } else {
+ DEBUG(4,("NetFileEnum2 res=%d\n", res));
+ }
+ } else {
+ DEBUG(4,("NetFileEnum2 failed\n"));
+ }
+
+ SAFE_FREE(rparam);
+ SAFE_FREE(rdata);
+
+ return count;
+}
+
+/****************************************************************************
+ call a NetShareAdd - share/export directory on remote server
+****************************************************************************/
+int cli_NetShareAdd(struct cli_state *cli, RAP_SHARE_INFO_2 * sinfo )
+{
+ char *rparam = NULL;
+ char *rdata = NULL;
+ char *p;
+ unsigned int rdrcnt,rprcnt;
+ int res;
+ char param[WORDSIZE /* api number */
+ +sizeof(RAP_WShareAdd_REQ) /* req string */
+ +sizeof(RAP_SHARE_INFO_L2) /* return string */
+ +WORDSIZE /* info level */
+ +WORDSIZE]; /* reserved word */
+ char data[1024];
+ /* offset to free format string section following fixed length data. */
+ /* will be updated by PUTSTRINGP macro and will end up with total len */
+ int soffset = RAP_SHARENAME_LEN + 1 /* share name + pad */
+ + WORDSIZE /* share type */
+ + DWORDSIZE /* comment pointer */
+ + WORDSIZE /* permissions */
+ + WORDSIZE /* max users */
+ + WORDSIZE /* active users */
+ + DWORDSIZE /* share path */
+ + RAP_SPASSWD_LEN + 1; /* share password + pad */
+
+ memset(param,'\0',sizeof(param));
+ /* now send a SMBtrans command with api RNetShareAdd */
+ p = make_header(param, RAP_WshareAdd,
+ RAP_WShareAdd_REQ, RAP_SHARE_INFO_L2);
+ PUTWORD(p, 2); /* info level */
+ PUTWORD(p, 0); /* reserved word 0 */
+
+ p = data;
+ PUTSTRINGF(p, sinfo->share_name, RAP_SHARENAME_LEN);
+ PUTBYTE(p, 0); /* pad byte 0 */
+
+ PUTWORD(p, sinfo->share_type);
+ PUTSTRINGP(p, sinfo->comment, data, soffset);
+ PUTWORD(p, sinfo->perms);
+ PUTWORD(p, sinfo->maximum_users);
+ PUTWORD(p, sinfo->active_users);
+ PUTSTRINGP(p, sinfo->path, data, soffset);
+ PUTSTRINGF(p, sinfo->password, RAP_SPASSWD_LEN);
+ SCVAL(p,-1,0x0A); /* required 0x0A at end of password */
+
+ if (cli_api(cli,
+ param, sizeof(param), 1024, /* Param, length, maxlen */
+ data, soffset, sizeof(data), /* data, length, maxlen */
+ &rparam, &rprcnt, /* return params, length */
+ &rdata, &rdrcnt)) /* return data, length */
+ {
+ res = rparam? SVAL(rparam,0) : -1;
+
+ if (res == 0) {
+ /* nothing to do */
+ }
+ else {
+ DEBUG(4,("NetShareAdd res=%d\n", res));
+ }
+ } else {
+ res = -1;
+ DEBUG(4,("NetShareAdd failed\n"));
+ }
+
+ SAFE_FREE(rparam);
+ SAFE_FREE(rdata);
+
+ return res;
+}
+/****************************************************************************
+ call a NetShareDelete - unshare exported directory on remote server
+****************************************************************************/
+int cli_NetShareDelete(struct cli_state *cli, const char * share_name )
+{
+ char *rparam = NULL;
+ char *rdata = NULL;
+ char *p;
+ unsigned int rdrcnt,rprcnt;
+ int res;
+ char param[WORDSIZE /* api number */
+ +sizeof(RAP_WShareDel_REQ) /* req string */
+ +1 /* no ret string */
+ +RAP_SHARENAME_LEN /* share to del */
+ +WORDSIZE]; /* reserved word */
+
+
+ /* now send a SMBtrans command with api RNetShareDelete */
+ p = make_header(param, RAP_WshareDel, RAP_WShareDel_REQ, NULL);
+ PUTSTRING(p,share_name,RAP_SHARENAME_LEN);
+ PUTWORD(p,0); /* reserved word MBZ on input */
+
+ if (cli_api(cli,
+ param, PTR_DIFF(p,param), 1024, /* Param, length, maxlen */
+ NULL, 0, 200, /* data, length, maxlen */
+ &rparam, &rprcnt, /* return params, length */
+ &rdata, &rdrcnt)) /* return data, length */
+ {
+ res = GETRES(rparam);
+
+ if (res == 0) {
+ /* nothing to do */
+ }
+ else {
+ DEBUG(4,("NetShareDelete res=%d\n", res));
+ }
+ } else {
+ res = -1;
+ DEBUG(4,("NetShareDelete failed\n"));
+ }
+
+ SAFE_FREE(rparam);
+ SAFE_FREE(rdata);
+
+ return res;
+}
+/*************************************************************************
+*
+* Function Name: cli_get_pdc_name
+*
+* PURPOSE: Remotes a NetServerEnum API call to the current server
+* requesting the name of a server matching the server
+* type of SV_TYPE_DOMAIN_CTRL (PDC).
+*
+* Dependencies: none
+*
+* Parameters:
+* cli - pointer to cli_state structure
+* workgroup - pointer to string containing name of domain
+* pdc_name - pointer to string that will contain PDC name
+* on successful return
+*
+* Returns:
+* True - success
+* False - failure
+*
+************************************************************************/
+BOOL cli_get_pdc_name(struct cli_state *cli, char *workgroup, char *pdc_name)
+{
+ char *rparam = NULL;
+ char *rdata = NULL;
+ unsigned int rdrcnt,rprcnt;
+ char *p;
+ char param[WORDSIZE /* api number */
+ +sizeof(RAP_NetServerEnum2_REQ) /* req string */
+ +sizeof(RAP_SERVER_INFO_L1) /* return string */
+ +WORDSIZE /* info level */
+ +WORDSIZE /* buffer size */
+ +DWORDSIZE /* server type */
+ +RAP_MACHNAME_LEN]; /* workgroup */
+ int count = -1;
+
+ *pdc_name = '\0';
+
+ /* send a SMBtrans command with api NetServerEnum */
+ p = make_header(param, RAP_NetServerEnum2,
+ RAP_NetServerEnum2_REQ, RAP_SERVER_INFO_L1);
+ PUTWORD(p, 1); /* info level */
+ PUTWORD(p, CLI_BUFFER_SIZE);
+ PUTDWORD(p, SV_TYPE_DOMAIN_CTRL);
+ PUTSTRING(p, workgroup, RAP_MACHNAME_LEN);
+
+ if (cli_api(cli,
+ param, PTR_DIFF(p,param), 8, /* params, length, max */
+ NULL, 0, CLI_BUFFER_SIZE, /* data, length, max */
+ &rparam, &rprcnt, /* return params, return size */
+ &rdata, &rdrcnt /* return data, return size */
+ )) {
+ cli->rap_error = GETRES(rparam);
+
+ /*
+ * We only really care to copy a name if the
+ * API succeeded and we got back a name.
+ */
+ if (cli->rap_error == 0) {
+ p = rparam + WORDSIZE + WORDSIZE; /* skip result and converter */
+ GETWORD(p, count);
+ p = rdata;
+
+ if (count > 0)
+ GETSTRING(p, pdc_name);
+ }
+ else {
+ DEBUG(4,("cli_get_pdc_name: machine %s failed the NetServerEnum call. "
+ "Error was : %s.\n", cli->desthost, cli_errstr(cli) ));
+ }
+ }
+
+ SAFE_FREE(rparam);
+ SAFE_FREE(rdata);
+
+ return(count > 0);
+}
+
+
+/*************************************************************************
+*
+* Function Name: cli_get_server_domain
+*
+* PURPOSE: Remotes a NetWkstaGetInfo API call to the current server
+* requesting wksta_info_10 level information to determine
+* the domain the server belongs to. On success, this
+* routine sets the server_domain field in the cli_state structure
+* to the server's domain name.
+*
+* Dependencies: none
+*
+* Parameters:
+* cli - pointer to cli_state structure
+*
+* Returns:
+* True - success
+* False - failure
+*
+* Origins: samba 2.0.6 source/libsmb/clientgen.c cli_NetServerEnum()
+*
+************************************************************************/
+BOOL cli_get_server_domain(struct cli_state *cli)
+{
+ char *rparam = NULL;
+ char *rdata = NULL;
+ unsigned int rdrcnt,rprcnt;
+ char *p;
+ char param[WORDSIZE /* api number */
+ +sizeof(RAP_WWkstaGetInfo_REQ) /* req string */
+ +sizeof(RAP_WKSTA_INFO_L10) /* return string */
+ +WORDSIZE /* info level */
+ +WORDSIZE]; /* buffer size */
+ int res = -1;
+
+ /* send a SMBtrans command with api NetWkstaGetInfo */
+ p = make_header(param, RAP_WWkstaGetInfo,
+ RAP_WWkstaGetInfo_REQ, RAP_WKSTA_INFO_L10);
+ PUTWORD(p, 10); /* info level */
+ PUTWORD(p, CLI_BUFFER_SIZE);
+
+ if (cli_api(cli, param, PTR_DIFF(p,param), 8, /* params, length, max */
+ NULL, 0, CLI_BUFFER_SIZE, /* data, length, max */
+ &rparam, &rprcnt, /* return params, return size */
+ &rdata, &rdrcnt)) { /* return data, return size */
+ res = GETRES(rparam);
+ p = rdata;
+
+ if (res == 0) {
+ int converter;
+
+ p = rparam + WORDSIZE;
+ GETWORD(p, converter);
+
+ p = rdata + DWORDSIZE + DWORDSIZE; /* skip computer & user names */
+ GETSTRINGP(p, cli->server_domain, rdata, converter);
+ }
+ }
+
+ SAFE_FREE(rparam);
+ SAFE_FREE(rdata);
+
+ return(res == 0);
+}
+
+
+/*************************************************************************
+*
+* Function Name: cli_get_server_type
+*
+* PURPOSE: Remotes a NetServerGetInfo API call to the current server
+* requesting server_info_1 level information to retrieve
+* the server type.
+*
+* Dependencies: none
+*
+* Parameters:
+* cli - pointer to cli_state structure
+* pstype - pointer to uint32 to contain returned server type
+*
+* Returns:
+* True - success
+* False - failure
+*
+* Origins: samba 2.0.6 source/libsmb/clientgen.c cli_NetServerEnum()
+*
+************************************************************************/
+BOOL cli_get_server_type(struct cli_state *cli, uint32 *pstype)
+{
+ char *rparam = NULL;
+ char *rdata = NULL;
+ unsigned int rdrcnt,rprcnt;
+ char *p;
+ char param[WORDSIZE /* api number */
+ +sizeof(RAP_WserverGetInfo_REQ) /* req string */
+ +sizeof(RAP_SERVER_INFO_L1) /* return string */
+ +WORDSIZE /* info level */
+ +WORDSIZE]; /* buffer size */
+ int res = -1;
+
+ /* send a SMBtrans command with api NetServerGetInfo */
+ p = make_header(param, RAP_WserverGetInfo,
+ RAP_WserverGetInfo_REQ, RAP_SERVER_INFO_L1);
+ PUTWORD(p, 1); /* info level */
+ PUTWORD(p, CLI_BUFFER_SIZE);
+
+ if (cli_api(cli,
+ param, PTR_DIFF(p,param), 8, /* params, length, max */
+ NULL, 0, CLI_BUFFER_SIZE, /* data, length, max */
+ &rparam, &rprcnt, /* return params, return size */
+ &rdata, &rdrcnt /* return data, return size */
+ )) {
+
+ res = GETRES(rparam);
+
+ if (res == 0 || res == ERRmoredata) {
+ p = rdata;
+ *pstype = IVAL(p,18) & ~SV_TYPE_LOCAL_LIST_ONLY;
+ }
+ }
+
+ SAFE_FREE(rparam);
+ SAFE_FREE(rdata);
+
+ return(res == 0 || res == ERRmoredata);
+}
+
+
+/*************************************************************************
+*
+* Function Name: cli_ns_check_server_type
+*
+* PURPOSE: Remotes a NetServerEnum2 API call to the current server
+* requesting server_info_0 level information of machines
+* matching the given server type. If the returned server
+* list contains the machine name contained in cli->desthost
+* then we conclude the server type checks out. This routine
+* is useful to retrieve list of server's of a certain
+* type when all you have is a null session connection and
+* can't remote API calls such as NetWkstaGetInfo or
+* NetServerGetInfo.
+*
+* Dependencies: none
+*
+* Parameters:
+* cli - pointer to cli_state structure
+* workgroup - pointer to string containing domain
+* stype - server type
+*
+* Returns:
+* True - success
+* False - failure
+*
+************************************************************************/
+BOOL cli_ns_check_server_type(struct cli_state *cli, char *workgroup, uint32 stype)
+{
+ char *rparam = NULL;
+ char *rdata = NULL;
+ unsigned int rdrcnt,rprcnt;
+ char *p;
+ char param[WORDSIZE /* api number */
+ +sizeof(RAP_NetServerEnum2_REQ) /* req string */
+ +sizeof(RAP_SERVER_INFO_L0) /* return string */
+ +WORDSIZE /* info level */
+ +WORDSIZE /* buffer size */
+ +DWORDSIZE /* server type */
+ +RAP_MACHNAME_LEN]; /* workgroup */
+ BOOL found_server = False;
+ int res = -1;
+
+ /* send a SMBtrans command with api NetServerEnum */
+ p = make_header(param, RAP_NetServerEnum2,
+ RAP_NetServerEnum2_REQ, RAP_SERVER_INFO_L0);
+ PUTWORD(p, 0); /* info level 0 */
+ PUTWORD(p, CLI_BUFFER_SIZE);
+ PUTDWORD(p, stype);
+ PUTSTRING(p, workgroup, RAP_MACHNAME_LEN);
+
+ if (cli_api(cli,
+ param, PTR_DIFF(p,param), 8, /* params, length, max */
+ NULL, 0, CLI_BUFFER_SIZE, /* data, length, max */
+ &rparam, &rprcnt, /* return params, return size */
+ &rdata, &rdrcnt /* return data, return size */
+ )) {
+
+ res = GETRES(rparam);
+ cli->rap_error = res;
+
+ if (res == 0 || res == ERRmoredata) {
+ int i, converter, count;
+
+ p = rparam + WORDSIZE;
+ GETWORD(p, converter);
+ GETWORD(p, count);
+
+ p = rdata;
+ for (i = 0;i < count;i++, p += 16) {
+ char ret_server[RAP_MACHNAME_LEN];
+
+ GETSTRINGF(p, ret_server, RAP_MACHNAME_LEN);
+ if (strequal(ret_server, cli->desthost)) {
+ found_server = True;
+ break;
+ }
+ }
+ }
+ else {
+ DEBUG(4,("cli_ns_check_server_type: machine %s failed the NetServerEnum call. "
+ "Error was : %s.\n", cli->desthost, cli_errstr(cli) ));
+ }
+ }
+
+ SAFE_FREE(rparam);
+ SAFE_FREE(rdata);
+
+ return found_server;
+ }
+
+
+/****************************************************************************
+ perform a NetWkstaUserLogoff
+****************************************************************************/
+BOOL cli_NetWkstaUserLogoff(struct cli_state *cli,char *user, char *workstation)
+{
+ char *rparam = NULL;
+ char *rdata = NULL;
+ char *p;
+ unsigned int rdrcnt,rprcnt;
+ char param[WORDSIZE /* api number */
+ +sizeof(RAP_NetWkstaUserLogoff_REQ) /* req string */
+ +sizeof(RAP_USER_LOGOFF_INFO_L1) /* return string */
+ +RAP_USERNAME_LEN+1 /* user name+pad */
+ +RAP_MACHNAME_LEN /* wksta name */
+ +WORDSIZE /* buffer size */
+ +WORDSIZE]; /* buffer size? */
+ fstring upperbuf;
+
+ memset(param, 0, sizeof(param));
+
+ /* send a SMBtrans command with api NetWkstaUserLogoff */
+ p = make_header(param, RAP_WWkstaUserLogoff,
+ RAP_NetWkstaUserLogoff_REQ, RAP_USER_LOGOFF_INFO_L1);
+ PUTDWORD(p, 0); /* Null pointer */
+ PUTDWORD(p, 0); /* Null pointer */
+ fstrcpy(upperbuf, user);
+ strupper_m(upperbuf);
+ PUTSTRINGF(p, upperbuf, RAP_USERNAME_LEN);
+ p++; /* strange format, but ok */
+ fstrcpy(upperbuf, workstation);
+ strupper_m(upperbuf);
+ PUTSTRINGF(p, upperbuf, RAP_MACHNAME_LEN);
+ PUTWORD(p, CLI_BUFFER_SIZE);
+ PUTWORD(p, CLI_BUFFER_SIZE);
+
+ if (cli_api(cli,
+ param, PTR_DIFF(p,param),1024, /* param, length, max */
+ NULL, 0, CLI_BUFFER_SIZE, /* data, length, max */
+ &rparam, &rprcnt, /* return params, return size */
+ &rdata, &rdrcnt /* return data, return size */
+ )) {
+ cli->rap_error = GETRES(rparam);
+
+ if (cli->rap_error != 0) {
+ DEBUG(4,("NetwkstaUserLogoff gave error %d\n", cli->rap_error));
+ }
+ }
+
+ SAFE_FREE(rparam);
+ SAFE_FREE(rdata);
+ return (cli->rap_error == 0);
+}
+
+int cli_NetPrintQEnum(struct cli_state *cli,
+ void (*qfn)(const char*,uint16,uint16,uint16,const char*,const char*,const char*,const char*,const char*,uint16,uint16),
+ void (*jfn)(uint16,const char*,const char*,const char*,const char*,uint16,uint16,const char*,uint,uint,const char*))
+{
+ char param[WORDSIZE /* api number */
+ +sizeof(RAP_NetPrintQEnum_REQ) /* req string */
+ +sizeof(RAP_PRINTQ_INFO_L2) /* return string */
+ +WORDSIZE /* info level */
+ +WORDSIZE /* buffer size */
+ +sizeof(RAP_SMB_PRINT_JOB_L1)]; /* more ret data */
+ char *p;
+ char *rparam = NULL;
+ char *rdata = NULL;
+ unsigned int rprcnt, rdrcnt;
+ int res = -1;
+
+
+ memset(param, '\0',sizeof(param));
+ p = make_header(param, RAP_WPrintQEnum,
+ RAP_NetPrintQEnum_REQ, RAP_PRINTQ_INFO_L2);
+ PUTWORD(p,2); /* Info level 2 */
+ PUTWORD(p,0xFFE0); /* Return buffer size */
+ PUTSTRING(p, RAP_SMB_PRINT_JOB_L1, 0);
+
+ if (cli_api(cli,
+ param, PTR_DIFF(p,param),1024,
+ NULL, 0, CLI_BUFFER_SIZE,
+ &rparam, &rprcnt,
+ &rdata, &rdrcnt)) {
+ res = GETRES(rparam);
+ cli->rap_error = res;
+ if (res != 0) {
+ DEBUG(1,("NetPrintQEnum gave error %d\n", res));
+ }
+ }
+
+ if (rdata) {
+ if (res == 0 || res == ERRmoredata) {
+ int i, converter, count;
+
+ p = rparam + WORDSIZE;
+ GETWORD(p, converter);
+ GETWORD(p, count);
+
+ p = rdata;
+ for (i=0;i<count;i++) {
+ pstring qname, sep_file, print_proc, dest, parms, comment;
+ uint16 jobcount, priority, start_time, until_time, status;
+
+ GETSTRINGF(p, qname, RAP_SHARENAME_LEN);
+ p++; /* pad */
+ GETWORD(p, priority);
+ GETWORD(p, start_time);
+ GETWORD(p, until_time);
+ GETSTRINGP(p, sep_file, rdata, converter);
+ GETSTRINGP(p, print_proc, rdata, converter);
+ GETSTRINGP(p, dest, rdata, converter);
+ GETSTRINGP(p, parms, rdata, converter);
+ GETSTRINGP(p, parms, comment, converter);
+ GETWORD(p, status);
+ GETWORD(p, jobcount);
+
+ qfn(qname, priority, start_time, until_time, sep_file, print_proc,
+ dest, parms, comment, status, jobcount);
+
+ if (jobcount) {
+ int j;
+ for (j=0;j<jobcount;j++) {
+ uint16 jid, pos, fsstatus;
+ pstring ownername, notifyname, datatype, jparms, jstatus, jcomment;
+ unsigned int submitted, jsize;
+
+ GETWORD(p, jid);
+ GETSTRINGF(p, ownername, RAP_USERNAME_LEN);
+ p++; /* pad byte */
+ GETSTRINGF(p, notifyname, RAP_MACHNAME_LEN);
+ GETSTRINGF(p, datatype, RAP_DATATYPE_LEN);
+ GETSTRINGP(p, jparms, rdata, converter);
+ GETWORD(p, pos);
+ GETWORD(p, fsstatus);
+ GETSTRINGP(p, jstatus, rdata, converter);
+ GETDWORD(p, submitted);
+ GETDWORD(p, jsize);
+ GETSTRINGP(p, jcomment, rdata, converter);
+
+ jfn(jid, ownername, notifyname, datatype, jparms, pos, fsstatus,
+ jstatus, submitted, jsize, jcomment);
+ }
+ }
+ }
+ } else {
+ DEBUG(4,("NetPrintQEnum res=%d\n", res));
+ }
+ } else {
+ DEBUG(4,("NetPrintQEnum no data returned\n"));
+ }
+
+ SAFE_FREE(rparam);
+ SAFE_FREE(rdata);
+
+ return res;
+}
+
+int cli_NetPrintQGetInfo(struct cli_state *cli, const char *printer,
+ void (*qfn)(const char*,uint16,uint16,uint16,const char*,const char*,const char*,const char*,const char*,uint16,uint16),
+ void (*jfn)(uint16,const char*,const char*,const char*,const char*,uint16,uint16,const char*,uint,uint,const char*))
+{
+ char param[WORDSIZE /* api number */
+ +sizeof(RAP_NetPrintQGetInfo_REQ) /* req string */
+ +sizeof(RAP_PRINTQ_INFO_L2) /* return string */
+ +RAP_SHARENAME_LEN /* printer name */
+ +WORDSIZE /* info level */
+ +WORDSIZE /* buffer size */
+ +sizeof(RAP_SMB_PRINT_JOB_L1)]; /* more ret data */
+ char *p;
+ char *rparam = NULL;
+ char *rdata = NULL;
+ unsigned int rprcnt, rdrcnt;
+ int res = -1;
+
+
+ memset(param, '\0',sizeof(param));
+ p = make_header(param, RAP_WPrintQGetInfo,
+ RAP_NetPrintQGetInfo_REQ, RAP_PRINTQ_INFO_L2);
+ PUTSTRING(p, printer, RAP_SHARENAME_LEN-1);
+ PUTWORD(p, 2); /* Info level 2 */
+ PUTWORD(p,0xFFE0); /* Return buffer size */
+ PUTSTRING(p, RAP_SMB_PRINT_JOB_L1, 0);
+
+ if (cli_api(cli,
+ param, PTR_DIFF(p,param),1024,
+ NULL, 0, CLI_BUFFER_SIZE,
+ &rparam, &rprcnt,
+ &rdata, &rdrcnt)) {
+ res = GETRES(rparam);
+ cli->rap_error = res;
+ if (res != 0) {
+ DEBUG(1,("NetPrintQGetInfo gave error %d\n", res));
+ }
+ }
+
+ if (rdata) {
+ if (res == 0 || res == ERRmoredata) {
+ int rsize, converter;
+ pstring qname, sep_file, print_proc, dest, parms, comment;
+ uint16 jobcount, priority, start_time, until_time, status;
+
+ p = rparam + WORDSIZE;
+ GETWORD(p, converter);
+ GETWORD(p, rsize);
+
+ p = rdata;
+ GETSTRINGF(p, qname, RAP_SHARENAME_LEN);
+ p++; /* pad */
+ GETWORD(p, priority);
+ GETWORD(p, start_time);
+ GETWORD(p, until_time);
+ GETSTRINGP(p, sep_file, rdata, converter);
+ GETSTRINGP(p, print_proc, rdata, converter);
+ GETSTRINGP(p, dest, rdata, converter);
+ GETSTRINGP(p, parms, rdata, converter);
+ GETSTRINGP(p, comment, rdata, converter);
+ GETWORD(p, status);
+ GETWORD(p, jobcount);
+ qfn(qname, priority, start_time, until_time, sep_file, print_proc,
+ dest, parms, comment, status, jobcount);
+ if (jobcount) {
+ int j;
+ for (j=0;(j<jobcount)&&(PTR_DIFF(p,rdata)< rsize);j++) {
+ uint16 jid, pos, fsstatus;
+ pstring ownername, notifyname, datatype, jparms, jstatus, jcomment;
+ unsigned int submitted, jsize;
+
+ GETWORD(p, jid);
+ GETSTRINGF(p, ownername, RAP_USERNAME_LEN);
+ p++; /* pad byte */
+ GETSTRINGF(p, notifyname, RAP_MACHNAME_LEN);
+ GETSTRINGF(p, datatype, RAP_DATATYPE_LEN);
+ GETSTRINGP(p, jparms, rdata, converter);
+ GETWORD(p, pos);
+ GETWORD(p, fsstatus);
+ GETSTRINGP(p, jstatus, rdata, converter);
+ GETDWORD(p, submitted);
+ GETDWORD(p, jsize);
+ GETSTRINGP(p, jcomment, rdata, converter);
+
+ jfn(jid, ownername, notifyname, datatype, jparms, pos, fsstatus,
+ jstatus, submitted, jsize, jcomment);
+ }
+ }
+ } else {
+ DEBUG(4,("NetPrintQGetInfo res=%d\n", res));
+ }
+ } else {
+ DEBUG(4,("NetPrintQGetInfo no data returned\n"));
+ }
+
+ SAFE_FREE(rparam);
+ SAFE_FREE(rdata);
+
+ return res;
+}
+
+/****************************************************************************
+call a NetServiceEnum - list running services on a different host
+****************************************************************************/
+int cli_RNetServiceEnum(struct cli_state *cli, void (*fn)(const char *, const char *, void *), void *state)
+{
+ char param[WORDSIZE /* api number */
+ +sizeof(RAP_NetServiceEnum_REQ) /* parm string */
+ +sizeof(RAP_SERVICE_INFO_L2) /* return string */
+ +WORDSIZE /* info level */
+ +WORDSIZE]; /* buffer size */
+ char *p;
+ char *rparam = NULL;
+ char *rdata = NULL;
+ unsigned int rprcnt, rdrcnt;
+ int res = -1;
+
+
+ memset(param, '\0', sizeof(param));
+ p = make_header(param, RAP_WServiceEnum,
+ RAP_NetServiceEnum_REQ, RAP_SERVICE_INFO_L2);
+ PUTWORD(p,2); /* Info level 2 */
+ PUTWORD(p,0xFFE0); /* Return buffer size */
+
+ if (cli_api(cli,
+ param, PTR_DIFF(p,param),8,
+ NULL, 0, 0xFFE0 /* data area size */,
+ &rparam, &rprcnt,
+ &rdata, &rdrcnt)) {
+ res = GETRES(rparam);
+ cli->rap_error = res;
+ if(cli->rap_error == 234)
+ DEBUG(1,("Not all service names were returned (such as those longer than 15 characters)\n"));
+ else if (cli->rap_error != 0) {
+ DEBUG(1,("NetServiceEnum gave error %d\n", cli->rap_error));
+ }
+ }
+
+ if (rdata) {
+ if (res == 0 || res == ERRmoredata) {
+ int i, converter, count;
+
+ p = rparam + WORDSIZE; /* skip result */
+ GETWORD(p, converter);
+ GETWORD(p, count);
+
+ for (i=0,p=rdata;i<count;i++) {
+ pstring comment;
+ char servicename[RAP_SRVCNAME_LEN];
+
+ GETSTRINGF(p, servicename, RAP_SRVCNAME_LEN);
+ p+=8; /* pass status words */
+ GETSTRINGF(p, comment, RAP_SRVCCMNT_LEN);
+
+ fn(servicename, comment, cli); /* BB add status too */
+ }
+ } else {
+ DEBUG(4,("NetServiceEnum res=%d\n", res));
+ }
+ } else {
+ DEBUG(4,("NetServiceEnum no data returned\n"));
+ }
+
+ SAFE_FREE(rparam);
+ SAFE_FREE(rdata);
+
+ return res;
+}
+
+
+/****************************************************************************
+call a NetSessionEnum - list workstations with sessions to an SMB server
+****************************************************************************/
+int cli_NetSessionEnum(struct cli_state *cli, void (*fn)(char *, char *, uint16, uint16, uint16, uint, uint, uint, char *))
+{
+ char param[WORDSIZE /* api number */
+ +sizeof(RAP_NetSessionEnum_REQ) /* parm string */
+ +sizeof(RAP_SESSION_INFO_L2) /* return string */
+ +WORDSIZE /* info level */
+ +WORDSIZE]; /* buffer size */
+ char *p;
+ char *rparam = NULL;
+ char *rdata = NULL;
+ unsigned int rprcnt, rdrcnt;
+ int res = -1;
+
+ memset(param, '\0', sizeof(param));
+ p = make_header(param, RAP_WsessionEnum,
+ RAP_NetSessionEnum_REQ, RAP_SESSION_INFO_L2);
+ PUTWORD(p,2); /* Info level 2 */
+ PUTWORD(p,0xFF); /* Return buffer size */
+
+ if (cli_api(cli,
+ param, PTR_DIFF(p,param),8,
+ NULL, 0, CLI_BUFFER_SIZE,
+ &rparam, &rprcnt,
+ &rdata, &rdrcnt)) {
+ res = GETRES(rparam);
+ cli->rap_error = res;
+ if (res != 0) {
+ DEBUG(1,("NetSessionEnum gave error %d\n", res));
+ }
+ }
+
+ if (rdata) {
+ if (res == 0 || res == ERRmoredata) {
+ int i, converter, count;
+
+ p = rparam + WORDSIZE;
+ GETWORD(p, converter);
+ GETWORD(p, count);
+
+ for (i=0,p=rdata;i<count;i++) {
+ pstring wsname, username, clitype_name;
+ uint16 num_conns, num_opens, num_users;
+ unsigned int sess_time, idle_time, user_flags;
+
+ GETSTRINGP(p, wsname, rdata, converter);
+ GETSTRINGP(p, username, rdata, converter);
+ GETWORD(p, num_conns);
+ GETWORD(p, num_opens);
+ GETWORD(p, num_users);
+ GETDWORD(p, sess_time);
+ GETDWORD(p, idle_time);
+ GETDWORD(p, user_flags);
+ GETSTRINGP(p, clitype_name, rdata, converter);
+
+ fn(wsname, username, num_conns, num_opens, num_users, sess_time,
+ idle_time, user_flags, clitype_name);
+ }
+
+ } else {
+ DEBUG(4,("NetSessionEnum res=%d\n", res));
+ }
+ } else {
+ DEBUG(4,("NetSesssionEnum no data returned\n"));
+ }
+
+ SAFE_FREE(rparam);
+ SAFE_FREE(rdata);
+
+ return res;
+}
+
+/****************************************************************************
+ Call a NetSessionGetInfo - get information about other session to an SMB server.
+****************************************************************************/
+
+int cli_NetSessionGetInfo(struct cli_state *cli, const char *workstation, void (*fn)(const char *, const char *, uint16, uint16, uint16, uint, uint, uint, const char *))
+{
+ char param[WORDSIZE /* api number */
+ +sizeof(RAP_NetSessionGetInfo_REQ) /* req string */
+ +sizeof(RAP_SESSION_INFO_L2) /* return string */
+ +RAP_MACHNAME_LEN /* wksta name */
+ +WORDSIZE /* info level */
+ +WORDSIZE]; /* buffer size */
+ char *p;
+ char *rparam = NULL;
+ char *rdata = NULL;
+ unsigned int rprcnt, rdrcnt;
+ int res = -1;
+
+
+ memset(param, '\0', sizeof(param));
+ p = make_header(param, RAP_WsessionGetInfo,
+ RAP_NetSessionGetInfo_REQ, RAP_SESSION_INFO_L2);
+ PUTSTRING(p, workstation, RAP_MACHNAME_LEN-1);
+ PUTWORD(p,2); /* Info level 2 */
+ PUTWORD(p,0xFF); /* Return buffer size */
+
+ if (cli_api(cli,
+ param, PTR_DIFF(p,param),PTR_DIFF(p,param),
+ NULL, 0, CLI_BUFFER_SIZE,
+ &rparam, &rprcnt,
+ &rdata, &rdrcnt)) {
+ cli->rap_error = SVAL(rparam,0);
+ if (cli->rap_error != 0) {
+ DEBUG(1,("NetSessionGetInfo gave error %d\n", cli->rap_error));
+ }
+ }
+
+ if (rdata) {
+ res = GETRES(rparam);
+
+ if (res == 0 || res == ERRmoredata) {
+ int rsize, converter;
+ pstring wsname, username, clitype_name;
+ uint16 num_conns, num_opens, num_users;
+ unsigned int sess_time, idle_time, user_flags;
+
+ p = rparam + WORDSIZE;
+ GETWORD(p, converter);
+ GETWORD(p, rsize);
+
+ p = rdata;
+ GETSTRINGP(p, wsname, rdata, converter);
+ GETSTRINGP(p, username, rdata, converter);
+ GETWORD(p, num_conns);
+ GETWORD(p, num_opens);
+ GETWORD(p, num_users);
+ GETDWORD(p, sess_time);
+ GETDWORD(p, idle_time);
+ GETDWORD(p, user_flags);
+ GETSTRINGP(p, clitype_name, rdata, converter);
+
+ fn(wsname, username, num_conns, num_opens, num_users, sess_time,
+ idle_time, user_flags, clitype_name);
+ } else {
+ DEBUG(4,("NetSessionGetInfo res=%d\n", res));
+ }
+ } else {
+ DEBUG(4,("NetSessionGetInfo no data returned\n"));
+ }
+
+ SAFE_FREE(rparam);
+ SAFE_FREE(rdata);
+
+ return res;
+}
+
+/****************************************************************************
+call a NetSessionDel - close a session to an SMB server
+****************************************************************************/
+int cli_NetSessionDel(struct cli_state *cli, const char *workstation)
+{
+ char param[WORDSIZE /* api number */
+ +sizeof(RAP_NetSessionDel_REQ) /* req string */
+ +1 /* no return string */
+ +RAP_MACHNAME_LEN /* workstation name */
+ +WORDSIZE]; /* reserved (0) */
+ char *p;
+ char *rparam = NULL;
+ char *rdata = NULL;
+ unsigned int rprcnt, rdrcnt;
+ int res;
+
+ memset(param, '\0', sizeof(param));
+ p = make_header(param, RAP_WsessionDel, RAP_NetSessionDel_REQ, NULL);
+ PUTSTRING(p, workstation, RAP_MACHNAME_LEN-1);
+ PUTWORD(p,0); /* reserved word of 0 */
+ if (cli_api(cli,
+ param, PTR_DIFF(p,param), 1024, /* Param, length, maxlen */
+ NULL, 0, 200, /* data, length, maxlen */
+ &rparam, &rprcnt, /* return params, length */
+ &rdata, &rdrcnt)) /* return data, length */
+ {
+ res = GETRES(rparam);
+ cli->rap_error = res;
+
+ if (res == 0) {
+ /* nothing to do */
+ }
+ else {
+ DEBUG(4,("NetFileClose2 res=%d\n", res));
+ }
+ } else {
+ res = -1;
+ DEBUG(4,("NetFileClose2 failed\n"));
+ }
+
+ SAFE_FREE(rparam);
+ SAFE_FREE(rdata);
+
+ return res;
+}
+
+
+int cli_NetConnectionEnum(struct cli_state *cli, const char *qualifier, void (*fn)(uint16 conid, uint16 contype, uint16 numopens, uint16 numusers, uint32 contime, const char *username, const char *netname))
+{
+ char param[WORDSIZE /* api number */
+ +sizeof(RAP_NetConnectionEnum_REQ) /* req string */
+ +sizeof(RAP_CONNECTION_INFO_L1) /* return string */
+ +RAP_MACHNAME_LEN /* wksta name */
+ +WORDSIZE /* info level */
+ +WORDSIZE]; /* buffer size */
+ char *p;
+ char *rparam = NULL;
+ char *rdata = NULL;
+ unsigned int rprcnt, rdrcnt;
+ int res = -1;
+
+ memset(param, '\0', sizeof(param));
+ p = make_header(param, RAP_WconnectionEnum,
+ RAP_NetConnectionEnum_REQ, RAP_CONNECTION_INFO_L1);
+ PUTSTRING(p, qualifier, RAP_MACHNAME_LEN-1);/* Workstation name */
+ PUTWORD(p,1); /* Info level 1 */
+ PUTWORD(p,0xFFE0); /* Return buffer size */
+
+ if (cli_api(cli,
+ param, PTR_DIFF(p,param),PTR_DIFF(p,param),
+ NULL, 0, CLI_BUFFER_SIZE,
+ &rparam, &rprcnt,
+ &rdata, &rdrcnt)) {
+ res = GETRES(rparam);
+ cli->rap_error = res;
+ if (res != 0) {
+ DEBUG(1,("NetConnectionEnum gave error %d\n", res));
+ }
+ }
+ if (rdata) {
+ if (res == 0 || res == ERRmoredata) {
+ int i, converter, count;
+
+ p = rparam + WORDSIZE;
+ GETWORD(p, converter);
+ GETWORD(p, count);
+
+ for (i=0,p=rdata;i<count;i++) {
+ pstring netname, username;
+ uint16 conn_id, conn_type, num_opens, num_users;
+ unsigned int conn_time;
+
+ GETWORD(p,conn_id);
+ GETWORD(p,conn_type);
+ GETWORD(p,num_opens);
+ GETWORD(p,num_users);
+ GETDWORD(p,conn_time);
+ GETSTRINGP(p, username, rdata, converter);
+ GETSTRINGP(p, netname, rdata, converter);
+
+ fn(conn_id, conn_type, num_opens, num_users, conn_time,
+ username, netname);
+ }
+
+ } else {
+ DEBUG(4,("NetConnectionEnum res=%d\n", res));
+ }
+ } else {
+ DEBUG(4,("NetConnectionEnum no data returned\n"));
+ }
+ SAFE_FREE(rdata);
+ SAFE_FREE(rparam);
+ return res;
+}
diff --git a/source/libsmb/clireadwrite.c b/source/libsmb/clireadwrite.c
new file mode 100644
index 00000000000..8eac7d07d8b
--- /dev/null
+++ b/source/libsmb/clireadwrite.c
@@ -0,0 +1,418 @@
+/*
+ Unix SMB/CIFS implementation.
+ client file read/write routines
+ Copyright (C) Andrew Tridgell 1994-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 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.
+*/
+
+#define NO_SYSLOG
+
+#include "includes.h"
+
+/****************************************************************************
+Issue a single SMBread and don't wait for a reply.
+****************************************************************************/
+
+static BOOL cli_issue_read(struct cli_state *cli, int fnum, off_t offset,
+ size_t size, int i)
+{
+ BOOL bigoffset = False;
+
+ memset(cli->outbuf,'\0',smb_size);
+ memset(cli->inbuf,'\0',smb_size);
+
+ if ((SMB_BIG_UINT)offset >> 32)
+ bigoffset = True;
+
+ set_message(cli->outbuf,bigoffset ? 12 : 10,0,True);
+
+ SCVAL(cli->outbuf,smb_com,SMBreadX);
+ SSVAL(cli->outbuf,smb_tid,cli->cnum);
+ cli_setup_packet(cli);
+
+ SCVAL(cli->outbuf,smb_vwv0,0xFF);
+ SSVAL(cli->outbuf,smb_vwv2,fnum);
+ SIVAL(cli->outbuf,smb_vwv3,offset);
+ SSVAL(cli->outbuf,smb_vwv5,size);
+ SSVAL(cli->outbuf,smb_vwv6,size);
+ SSVAL(cli->outbuf,smb_mid,cli->mid + i);
+
+ if (bigoffset)
+ SIVAL(cli->outbuf,smb_vwv10,(offset>>32) & 0xffffffff);
+
+ return cli_send_smb(cli);
+}
+
+/****************************************************************************
+ Read size bytes at offset offset using SMBreadX.
+****************************************************************************/
+
+ssize_t cli_read(struct cli_state *cli, int fnum, char *buf, off_t offset, size_t size)
+{
+ char *p;
+ int size2;
+ int readsize;
+ ssize_t total = 0;
+
+ if (size == 0)
+ return 0;
+
+ /*
+ * Set readsize to the maximum size we can handle in one readX,
+ * rounded down to a multiple of 1024.
+ */
+
+ readsize = (cli->max_xmit - (smb_size+32)) & ~1023;
+
+ while (total < size) {
+ readsize = MIN(readsize, size-total);
+
+ /* Issue a read and receive a reply */
+
+ if (!cli_issue_read(cli, fnum, offset, readsize, 0))
+ return -1;
+
+ if (!cli_receive_smb(cli))
+ return -1;
+
+ /* Check for error. Make sure to check for DOS and NT
+ errors. */
+
+ if (cli_is_error(cli)) {
+ BOOL recoverable_error = False;
+ NTSTATUS status = NT_STATUS_OK;
+ uint8 eclass = 0;
+ uint32 ecode = 0;
+
+ if (cli_is_nt_error(cli))
+ status = cli_nt_error(cli);
+ else
+ cli_dos_error(cli, &eclass, &ecode);
+
+ /*
+ * ERRDOS ERRmoredata or STATUS_MORE_ENRTIES is a
+ * recoverable error, plus we have valid data in the
+ * packet so don't error out here.
+ */
+
+ if ((eclass == ERRDOS && ecode == ERRmoredata) ||
+ NT_STATUS_V(status) == NT_STATUS_V(STATUS_MORE_ENTRIES))
+ recoverable_error = True;
+
+ if (!recoverable_error)
+ return -1;
+ }
+
+ size2 = SVAL(cli->inbuf, smb_vwv5);
+
+ if (size2 > readsize) {
+ DEBUG(5,("server returned more than we wanted!\n"));
+ return -1;
+ } else if (size2 < 0) {
+ DEBUG(5,("read return < 0!\n"));
+ return -1;
+ }
+
+ /* Copy data into buffer */
+
+ p = smb_base(cli->inbuf) + SVAL(cli->inbuf,smb_vwv6);
+ memcpy(buf + total, p, size2);
+
+ total += size2;
+ offset += size2;
+
+ /*
+ * If the server returned less than we asked for we're at EOF.
+ */
+
+ if (size2 < readsize)
+ break;
+ }
+
+ return total;
+}
+
+#if 0 /* relies on client_receive_smb(), now a static in libsmb/clientgen.c */
+
+/* This call is INCOMPATIBLE with SMB signing. If you remove the #if 0
+ you must fix ensure you don't attempt to sign the packets - data
+ *will* be currupted */
+
+/****************************************************************************
+Issue a single SMBreadraw and don't wait for a reply.
+****************************************************************************/
+
+static BOOL cli_issue_readraw(struct cli_state *cli, int fnum, off_t offset,
+ size_t size, int i)
+{
+
+ if (!cli->sign_info.use_smb_signing) {
+ DEBUG(0, ("Cannot use readraw and SMB Signing\n"));
+ return False;
+ }
+
+ memset(cli->outbuf,'\0',smb_size);
+ memset(cli->inbuf,'\0',smb_size);
+
+ set_message(cli->outbuf,10,0,True);
+
+ SCVAL(cli->outbuf,smb_com,SMBreadbraw);
+ SSVAL(cli->outbuf,smb_tid,cli->cnum);
+ cli_setup_packet(cli);
+
+ SSVAL(cli->outbuf,smb_vwv0,fnum);
+ SIVAL(cli->outbuf,smb_vwv1,offset);
+ SSVAL(cli->outbuf,smb_vwv2,size);
+ SSVAL(cli->outbuf,smb_vwv3,size);
+ SSVAL(cli->outbuf,smb_mid,cli->mid + i);
+
+ return cli_send_smb(cli);
+}
+
+/****************************************************************************
+ Tester for the readraw call.
+****************************************************************************/
+
+ssize_t cli_readraw(struct cli_state *cli, int fnum, char *buf, off_t offset, size_t size)
+{
+ char *p;
+ int size2;
+ size_t readsize;
+ ssize_t total = 0;
+
+ if (size == 0)
+ return 0;
+
+ /*
+ * Set readsize to the maximum size we can handle in one readraw.
+ */
+
+ readsize = 0xFFFF;
+
+ while (total < size) {
+ readsize = MIN(readsize, size-total);
+
+ /* Issue a read and receive a reply */
+
+ if (!cli_issue_readraw(cli, fnum, offset, readsize, 0))
+ return -1;
+
+ if (!client_receive_smb(cli->fd, cli->inbuf, cli->timeout))
+ return -1;
+
+ size2 = smb_len(cli->inbuf);
+
+ if (size2 > readsize) {
+ DEBUG(5,("server returned more than we wanted!\n"));
+ return -1;
+ } else if (size2 < 0) {
+ DEBUG(5,("read return < 0!\n"));
+ return -1;
+ }
+
+ /* Copy data into buffer */
+
+ if (size2) {
+ p = cli->inbuf + 4;
+ memcpy(buf + total, p, size2);
+ }
+
+ total += size2;
+ offset += size2;
+
+ /*
+ * If the server returned less than we asked for we're at EOF.
+ */
+
+ if (size2 < readsize)
+ break;
+ }
+
+ return total;
+}
+#endif
+/****************************************************************************
+issue a single SMBwrite and don't wait for a reply
+****************************************************************************/
+
+static BOOL cli_issue_write(struct cli_state *cli, int fnum, off_t offset,
+ uint16 mode, const char *buf,
+ size_t size, int i)
+{
+ char *p;
+ BOOL bigoffset = False;
+
+ if (size > cli->bufsize) {
+ cli->outbuf = realloc(cli->outbuf, size + 1024);
+ cli->inbuf = realloc(cli->inbuf, size + 1024);
+ if (cli->outbuf == NULL || cli->inbuf == NULL)
+ return False;
+ cli->bufsize = size + 1024;
+ }
+
+ memset(cli->outbuf,'\0',smb_size);
+ memset(cli->inbuf,'\0',smb_size);
+
+ if ((SMB_BIG_UINT)offset >> 32)
+ bigoffset = True;
+
+ if (bigoffset)
+ set_message(cli->outbuf,14,0,True);
+ else
+ set_message(cli->outbuf,12,0,True);
+
+ SCVAL(cli->outbuf,smb_com,SMBwriteX);
+ SSVAL(cli->outbuf,smb_tid,cli->cnum);
+ cli_setup_packet(cli);
+
+ SCVAL(cli->outbuf,smb_vwv0,0xFF);
+ SSVAL(cli->outbuf,smb_vwv2,fnum);
+
+ SIVAL(cli->outbuf,smb_vwv3,offset);
+ SIVAL(cli->outbuf,smb_vwv5,0);
+ SSVAL(cli->outbuf,smb_vwv7,mode);
+
+ SSVAL(cli->outbuf,smb_vwv8,(mode & 0x0008) ? size : 0);
+ /*
+ * According to CIFS-TR-1p00, this following field should only
+ * be set if CAP_LARGE_WRITEX is set. We should check this
+ * locally. However, this check might already have been
+ * done by our callers.
+ */
+ SSVAL(cli->outbuf,smb_vwv9,((size>>16)&1));
+ SSVAL(cli->outbuf,smb_vwv10,size);
+ SSVAL(cli->outbuf,smb_vwv11,
+ smb_buf(cli->outbuf) - smb_base(cli->outbuf));
+
+ if (bigoffset)
+ SIVAL(cli->outbuf,smb_vwv12,(offset>>32) & 0xffffffff);
+
+ p = smb_base(cli->outbuf) + SVAL(cli->outbuf,smb_vwv11);
+ memcpy(p, buf, size);
+ cli_setup_bcc(cli, p+size);
+
+ SSVAL(cli->outbuf,smb_mid,cli->mid + i);
+
+ show_msg(cli->outbuf);
+ return cli_send_smb(cli);
+}
+
+/****************************************************************************
+ 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 cli_write(struct cli_state *cli,
+ int fnum, uint16 write_mode,
+ const char *buf, off_t offset, size_t size)
+{
+ int bwritten = 0;
+ int issued = 0;
+ int received = 0;
+ int mpx = MAX(cli->max_mux-1, 1);
+ int block = cli->max_xmit - (smb_size+32);
+ int blocks = (size + (block-1)) / block;
+
+ while (received < blocks) {
+
+ while ((issued - received < mpx) && (issued < blocks)) {
+ int bsent = issued * block;
+ int size1 = MIN(block, size - bsent);
+
+ if (!cli_issue_write(cli, fnum, offset + bsent,
+ write_mode,
+ buf + bsent,
+ size1, issued))
+ return -1;
+ issued++;
+ }
+
+ if (!cli_receive_smb(cli))
+ return bwritten;
+
+ received++;
+
+ if (cli_is_error(cli))
+ break;
+
+ bwritten += SVAL(cli->inbuf, smb_vwv2);
+ bwritten += (((int)(SVAL(cli->inbuf, smb_vwv4)))>>16);
+ }
+
+ while (received < issued && cli_receive_smb(cli))
+ received++;
+
+ return bwritten;
+}
+
+/****************************************************************************
+ write to a file using a SMBwrite and not bypassing 0 byte writes
+****************************************************************************/
+
+ssize_t cli_smbwrite(struct cli_state *cli,
+ int fnum, char *buf, off_t offset, size_t size1)
+{
+ char *p;
+ ssize_t total = 0;
+
+ do {
+ size_t size = MIN(size1, cli->max_xmit - 48);
+
+ memset(cli->outbuf,'\0',smb_size);
+ memset(cli->inbuf,'\0',smb_size);
+
+ set_message(cli->outbuf,5, 0,True);
+
+ SCVAL(cli->outbuf,smb_com,SMBwrite);
+ SSVAL(cli->outbuf,smb_tid,cli->cnum);
+ cli_setup_packet(cli);
+
+ SSVAL(cli->outbuf,smb_vwv0,fnum);
+ SSVAL(cli->outbuf,smb_vwv1,size);
+ SIVAL(cli->outbuf,smb_vwv2,offset);
+ SSVAL(cli->outbuf,smb_vwv4,0);
+
+ p = smb_buf(cli->outbuf);
+ *p++ = 1;
+ SSVAL(p, 0, size); p += 2;
+ memcpy(p, buf, size); p += size;
+
+ cli_setup_bcc(cli, p);
+
+ if (!cli_send_smb(cli))
+ return -1;
+
+ if (!cli_receive_smb(cli))
+ return -1;
+
+ if (cli_is_error(cli))
+ return -1;
+
+ size = SVAL(cli->inbuf,smb_vwv0);
+ if (size == 0)
+ break;
+
+ size1 -= size;
+ total += size;
+ offset += size;
+
+ } while (size1);
+
+ return total;
+}
diff --git a/source/libsmb/clisecdesc.c b/source/libsmb/clisecdesc.c
new file mode 100644
index 00000000000..2989966f4de
--- /dev/null
+++ b/source/libsmb/clisecdesc.c
@@ -0,0 +1,139 @@
+/*
+ Unix SMB/CIFS implementation.
+ client security descriptor functions
+ 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 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"
+
+/****************************************************************************
+ query the security descriptor for a open file
+ ****************************************************************************/
+SEC_DESC *cli_query_secdesc(struct cli_state *cli, int fnum,
+ TALLOC_CTX *mem_ctx)
+{
+ char param[8];
+ char *rparam=NULL, *rdata=NULL;
+ unsigned int rparam_count=0, rdata_count=0;
+ prs_struct pd;
+ SEC_DESC *psd = NULL;
+
+ SIVAL(param, 0, fnum);
+ SIVAL(param, 4, 0x7);
+
+ if (!cli_send_nt_trans(cli,
+ NT_TRANSACT_QUERY_SECURITY_DESC,
+ 0,
+ NULL, 0, 0,
+ param, 8, 4,
+ NULL, 0, 0x10000)) {
+ DEBUG(1,("Failed to send NT_TRANSACT_QUERY_SECURITY_DESC\n"));
+ goto cleanup;
+ }
+
+
+ if (!cli_receive_nt_trans(cli,
+ &rparam, &rparam_count,
+ &rdata, &rdata_count)) {
+ DEBUG(1,("Failed to recv NT_TRANSACT_QUERY_SECURITY_DESC\n"));
+ goto cleanup;
+ }
+
+ prs_init(&pd, rdata_count, mem_ctx, UNMARSHALL);
+ prs_copy_data_in(&pd, rdata, rdata_count);
+ prs_set_offset(&pd,0);
+
+ if (!sec_io_desc("sd data", &psd, &pd, 1)) {
+ DEBUG(1,("Failed to parse secdesc\n"));
+ goto cleanup;
+ }
+
+ cleanup:
+
+ SAFE_FREE(rparam);
+ SAFE_FREE(rdata);
+
+ prs_mem_free(&pd);
+ return psd;
+}
+
+/****************************************************************************
+ set the security descriptor for a open file
+ ****************************************************************************/
+BOOL cli_set_secdesc(struct cli_state *cli, int fnum, SEC_DESC *sd)
+{
+ char param[8];
+ char *rparam=NULL, *rdata=NULL;
+ unsigned int rparam_count=0, rdata_count=0;
+ uint32 sec_info = 0;
+ TALLOC_CTX *mem_ctx;
+ prs_struct pd;
+ BOOL ret = False;
+
+ if ((mem_ctx = talloc_init("cli_set_secdesc")) == NULL) {
+ DEBUG(0,("talloc_init failed.\n"));
+ goto cleanup;
+ }
+
+ prs_init(&pd, 0, mem_ctx, MARSHALL);
+ prs_give_memory(&pd, NULL, 0, True);
+
+ if (!sec_io_desc("sd data", &sd, &pd, 1)) {
+ DEBUG(1,("Failed to marshall secdesc\n"));
+ goto cleanup;
+ }
+
+ SIVAL(param, 0, fnum);
+
+ if (sd->off_dacl)
+ sec_info |= DACL_SECURITY_INFORMATION;
+ if (sd->off_owner_sid)
+ sec_info |= OWNER_SECURITY_INFORMATION;
+ if (sd->off_grp_sid)
+ sec_info |= GROUP_SECURITY_INFORMATION;
+ SSVAL(param, 4, sec_info);
+
+ if (!cli_send_nt_trans(cli,
+ NT_TRANSACT_SET_SECURITY_DESC,
+ 0,
+ NULL, 0, 0,
+ param, 8, 0,
+ prs_data_p(&pd), prs_offset(&pd), 0)) {
+ DEBUG(1,("Failed to send NT_TRANSACT_SET_SECURITY_DESC\n"));
+ goto cleanup;
+ }
+
+
+ if (!cli_receive_nt_trans(cli,
+ &rparam, &rparam_count,
+ &rdata, &rdata_count)) {
+ DEBUG(1,("NT_TRANSACT_SET_SECURITY_DESC failed\n"));
+ goto cleanup;
+ }
+
+ ret = True;
+
+ cleanup:
+
+ SAFE_FREE(rparam);
+ SAFE_FREE(rdata);
+
+ talloc_destroy(mem_ctx);
+
+ prs_mem_free(&pd);
+ return ret;
+}
diff --git a/source/libsmb/clispnego.c b/source/libsmb/clispnego.c
new file mode 100644
index 00000000000..e6cadc466c1
--- /dev/null
+++ b/source/libsmb/clispnego.c
@@ -0,0 +1,539 @@
+/*
+ Unix SMB/CIFS implementation.
+ simple kerberos5/SPNEGO routines
+ 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 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"
+
+/*
+ generate a negTokenInit packet given a GUID, a list of supported
+ OIDs (the mechanisms) and a principal name string
+*/
+DATA_BLOB spnego_gen_negTokenInit(uint8 guid[16],
+ const char *OIDs[],
+ const char *principal)
+{
+ int i;
+ ASN1_DATA data;
+ DATA_BLOB ret;
+
+ memset(&data, 0, sizeof(data));
+
+ asn1_write(&data, guid, 16);
+ asn1_push_tag(&data,ASN1_APPLICATION(0));
+ asn1_write_OID(&data,OID_SPNEGO);
+ asn1_push_tag(&data,ASN1_CONTEXT(0));
+ asn1_push_tag(&data,ASN1_SEQUENCE(0));
+
+ asn1_push_tag(&data,ASN1_CONTEXT(0));
+ asn1_push_tag(&data,ASN1_SEQUENCE(0));
+ for (i=0; OIDs[i]; i++) {
+ asn1_write_OID(&data,OIDs[i]);
+ }
+ asn1_pop_tag(&data);
+ asn1_pop_tag(&data);
+
+ asn1_push_tag(&data, ASN1_CONTEXT(3));
+ asn1_push_tag(&data, ASN1_SEQUENCE(0));
+ asn1_push_tag(&data, ASN1_CONTEXT(0));
+ asn1_write_GeneralString(&data,principal);
+ asn1_pop_tag(&data);
+ asn1_pop_tag(&data);
+ asn1_pop_tag(&data);
+
+ asn1_pop_tag(&data);
+ asn1_pop_tag(&data);
+
+ asn1_pop_tag(&data);
+
+ if (data.has_error) {
+ DEBUG(1,("Failed to build negTokenInit at offset %d\n", (int)data.ofs));
+ asn1_free(&data);
+ }
+
+ ret = data_blob(data.data, data.length);
+ asn1_free(&data);
+
+ return ret;
+}
+
+/*
+ Generate a negTokenInit as used by the client side ... It has a mechType
+ (OID), and a mechToken (a security blob) ...
+
+ Really, we need to break out the NTLMSSP stuff as well, because it could be
+ raw in the packets!
+*/
+DATA_BLOB gen_negTokenInit(const char *OID, DATA_BLOB blob)
+{
+ ASN1_DATA data;
+ DATA_BLOB ret;
+
+ memset(&data, 0, sizeof(data));
+
+ asn1_push_tag(&data, ASN1_APPLICATION(0));
+ asn1_write_OID(&data,OID_SPNEGO);
+ asn1_push_tag(&data, ASN1_CONTEXT(0));
+ asn1_push_tag(&data, ASN1_SEQUENCE(0));
+
+ asn1_push_tag(&data, ASN1_CONTEXT(0));
+ asn1_push_tag(&data, ASN1_SEQUENCE(0));
+ asn1_write_OID(&data, OID);
+ asn1_pop_tag(&data);
+ asn1_pop_tag(&data);
+
+ asn1_push_tag(&data, ASN1_CONTEXT(2));
+ asn1_write_OctetString(&data,blob.data,blob.length);
+ asn1_pop_tag(&data);
+
+ asn1_pop_tag(&data);
+ asn1_pop_tag(&data);
+
+ asn1_pop_tag(&data);
+
+ if (data.has_error) {
+ DEBUG(1,("Failed to build negTokenInit at offset %d\n", (int)data.ofs));
+ asn1_free(&data);
+ }
+
+ ret = data_blob(data.data, data.length);
+ asn1_free(&data);
+
+ return ret;
+}
+
+/*
+ parse a negTokenInit packet giving a GUID, a list of supported
+ OIDs (the mechanisms) and a principal name string
+*/
+BOOL spnego_parse_negTokenInit(DATA_BLOB blob,
+ char *OIDs[ASN1_MAX_OIDS],
+ char **principal)
+{
+ int i;
+ BOOL ret;
+ ASN1_DATA data;
+
+ asn1_load(&data, blob);
+
+ asn1_start_tag(&data,ASN1_APPLICATION(0));
+ asn1_check_OID(&data,OID_SPNEGO);
+ asn1_start_tag(&data,ASN1_CONTEXT(0));
+ asn1_start_tag(&data,ASN1_SEQUENCE(0));
+
+ asn1_start_tag(&data,ASN1_CONTEXT(0));
+ asn1_start_tag(&data,ASN1_SEQUENCE(0));
+ for (i=0; asn1_tag_remaining(&data) > 0 && i < ASN1_MAX_OIDS; i++) {
+ char *oid = NULL;
+ asn1_read_OID(&data,&oid);
+ OIDs[i] = oid;
+ }
+ OIDs[i] = NULL;
+ asn1_end_tag(&data);
+ asn1_end_tag(&data);
+
+ asn1_start_tag(&data, ASN1_CONTEXT(3));
+ asn1_start_tag(&data, ASN1_SEQUENCE(0));
+ asn1_start_tag(&data, ASN1_CONTEXT(0));
+ asn1_read_GeneralString(&data,principal);
+ asn1_end_tag(&data);
+ asn1_end_tag(&data);
+ asn1_end_tag(&data);
+
+ asn1_end_tag(&data);
+ asn1_end_tag(&data);
+
+ asn1_end_tag(&data);
+
+ ret = !data.has_error;
+ asn1_free(&data);
+ return ret;
+}
+
+
+/*
+ generate a negTokenTarg packet given a list of OIDs and a security blob
+*/
+DATA_BLOB gen_negTokenTarg(const char *OIDs[], DATA_BLOB blob)
+{
+ int i;
+ ASN1_DATA data;
+ DATA_BLOB ret;
+
+ memset(&data, 0, sizeof(data));
+
+ asn1_push_tag(&data, ASN1_APPLICATION(0));
+ asn1_write_OID(&data,OID_SPNEGO);
+ asn1_push_tag(&data, ASN1_CONTEXT(0));
+ asn1_push_tag(&data, ASN1_SEQUENCE(0));
+
+ asn1_push_tag(&data, ASN1_CONTEXT(0));
+ asn1_push_tag(&data, ASN1_SEQUENCE(0));
+ for (i=0; OIDs[i]; i++) {
+ asn1_write_OID(&data,OIDs[i]);
+ }
+ asn1_pop_tag(&data);
+ asn1_pop_tag(&data);
+
+ asn1_push_tag(&data, ASN1_CONTEXT(2));
+ asn1_write_OctetString(&data,blob.data,blob.length);
+ asn1_pop_tag(&data);
+
+ asn1_pop_tag(&data);
+ asn1_pop_tag(&data);
+
+ asn1_pop_tag(&data);
+
+ if (data.has_error) {
+ DEBUG(1,("Failed to build negTokenTarg at offset %d\n", (int)data.ofs));
+ asn1_free(&data);
+ }
+
+ ret = data_blob(data.data, data.length);
+ asn1_free(&data);
+
+ return ret;
+}
+
+
+/*
+ parse a negTokenTarg packet giving a list of OIDs and a security blob
+*/
+BOOL parse_negTokenTarg(DATA_BLOB blob, char *OIDs[ASN1_MAX_OIDS], DATA_BLOB *secblob)
+{
+ int i;
+ ASN1_DATA data;
+
+ asn1_load(&data, blob);
+ asn1_start_tag(&data, ASN1_APPLICATION(0));
+ asn1_check_OID(&data,OID_SPNEGO);
+ asn1_start_tag(&data, ASN1_CONTEXT(0));
+ asn1_start_tag(&data, ASN1_SEQUENCE(0));
+
+ asn1_start_tag(&data, ASN1_CONTEXT(0));
+ asn1_start_tag(&data, ASN1_SEQUENCE(0));
+ for (i=0; asn1_tag_remaining(&data) > 0 && i < ASN1_MAX_OIDS; i++) {
+ char *oid = NULL;
+ asn1_read_OID(&data,&oid);
+ OIDs[i] = oid;
+ }
+ OIDs[i] = NULL;
+ asn1_end_tag(&data);
+ asn1_end_tag(&data);
+
+ asn1_start_tag(&data, ASN1_CONTEXT(2));
+ asn1_read_OctetString(&data,secblob);
+ asn1_end_tag(&data);
+
+ asn1_end_tag(&data);
+ asn1_end_tag(&data);
+
+ asn1_end_tag(&data);
+
+ if (data.has_error) {
+ DEBUG(1,("Failed to parse negTokenTarg at offset %d\n", (int)data.ofs));
+ asn1_free(&data);
+ return False;
+ }
+
+ asn1_free(&data);
+ return True;
+}
+
+/*
+ generate a krb5 GSS-API wrapper packet given a ticket
+*/
+DATA_BLOB spnego_gen_krb5_wrap(DATA_BLOB ticket, const uint8 tok_id[2])
+{
+ ASN1_DATA data;
+ DATA_BLOB ret;
+
+ memset(&data, 0, sizeof(data));
+
+ asn1_push_tag(&data, ASN1_APPLICATION(0));
+ asn1_write_OID(&data, 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);
+ }
+
+ ret = data_blob(data.data, data.length);
+ asn1_free(&data);
+
+ return ret;
+}
+
+/*
+ parse a krb5 GSS-API wrapper packet giving a ticket
+*/
+BOOL spnego_parse_krb5_wrap(DATA_BLOB blob, DATA_BLOB *ticket, uint8 tok_id[2])
+{
+ BOOL ret;
+ ASN1_DATA data;
+ int data_remaining;
+
+ asn1_load(&data, blob);
+ asn1_start_tag(&data, ASN1_APPLICATION(0));
+ asn1_check_OID(&data, 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(NULL, data_remaining);
+ asn1_read(&data, ticket->data, ticket->length);
+ }
+
+ asn1_end_tag(&data);
+
+ ret = !data.has_error;
+
+ asn1_free(&data);
+
+ return ret;
+}
+
+
+/*
+ generate a SPNEGO negTokenTarg packet, ready for a EXTENDED_SECURITY
+ kerberos session setup
+*/
+int spnego_gen_negTokenTarg(const char *principal, int time_offset,
+ DATA_BLOB *targ,
+ DATA_BLOB *session_key_krb5)
+{
+ int retval;
+ DATA_BLOB tkt, tkt_wrapped;
+ const char *krb_mechs[] = {OID_KERBEROS5_OLD, OID_NTLMSSP, NULL};
+
+ /* get a kerberos ticket for the service and extract the session key */
+ retval = cli_krb5_get_ticket(principal, time_offset, &tkt, session_key_krb5);
+
+ if (retval)
+ return retval;
+
+ /* wrap that up in a nice GSS-API wrapping */
+ tkt_wrapped = spnego_gen_krb5_wrap(tkt, TOK_ID_KRB_AP_REQ);
+
+ /* and wrap that in a shiny SPNEGO wrapper */
+ *targ = gen_negTokenTarg(krb_mechs, tkt_wrapped);
+
+ data_blob_free(&tkt_wrapped);
+ data_blob_free(&tkt);
+
+ return retval;
+}
+
+
+/*
+ parse a spnego NTLMSSP challenge packet giving two security blobs
+*/
+BOOL spnego_parse_challenge(const DATA_BLOB blob,
+ DATA_BLOB *chal1, DATA_BLOB *chal2)
+{
+ BOOL ret;
+ ASN1_DATA data;
+
+ ZERO_STRUCTP(chal1);
+ ZERO_STRUCTP(chal2);
+
+ asn1_load(&data, blob);
+ asn1_start_tag(&data,ASN1_CONTEXT(1));
+ asn1_start_tag(&data,ASN1_SEQUENCE(0));
+
+ asn1_start_tag(&data,ASN1_CONTEXT(0));
+ asn1_check_enumerated(&data,1);
+ asn1_end_tag(&data);
+
+ asn1_start_tag(&data,ASN1_CONTEXT(1));
+ asn1_check_OID(&data, OID_NTLMSSP);
+ asn1_end_tag(&data);
+
+ asn1_start_tag(&data,ASN1_CONTEXT(2));
+ asn1_read_OctetString(&data, chal1);
+ asn1_end_tag(&data);
+
+ /* the second challenge is optional (XP doesn't send it) */
+ if (asn1_tag_remaining(&data)) {
+ asn1_start_tag(&data,ASN1_CONTEXT(3));
+ asn1_read_OctetString(&data, chal2);
+ asn1_end_tag(&data);
+ }
+
+ asn1_end_tag(&data);
+ asn1_end_tag(&data);
+
+ ret = !data.has_error;
+ asn1_free(&data);
+ return ret;
+}
+
+
+/*
+ generate a SPNEGO auth packet. This will contain the encrypted passwords
+*/
+DATA_BLOB spnego_gen_auth(DATA_BLOB blob)
+{
+ ASN1_DATA data;
+ DATA_BLOB ret;
+
+ memset(&data, 0, sizeof(data));
+
+ asn1_push_tag(&data, ASN1_CONTEXT(1));
+ asn1_push_tag(&data, ASN1_SEQUENCE(0));
+ asn1_push_tag(&data, ASN1_CONTEXT(2));
+ asn1_write_OctetString(&data,blob.data,blob.length);
+ asn1_pop_tag(&data);
+ asn1_pop_tag(&data);
+ asn1_pop_tag(&data);
+
+ ret = data_blob(data.data, data.length);
+
+ asn1_free(&data);
+
+ return ret;
+}
+
+/*
+ parse a SPNEGO auth packet. This contains the encrypted passwords
+*/
+BOOL spnego_parse_auth(DATA_BLOB blob, DATA_BLOB *auth)
+{
+ ASN1_DATA data;
+
+ asn1_load(&data, blob);
+ asn1_start_tag(&data, ASN1_CONTEXT(1));
+ asn1_start_tag(&data, ASN1_SEQUENCE(0));
+ asn1_start_tag(&data, ASN1_CONTEXT(2));
+ asn1_read_OctetString(&data,auth);
+ asn1_end_tag(&data);
+ asn1_end_tag(&data);
+ asn1_end_tag(&data);
+
+ if (data.has_error) {
+ DEBUG(3,("spnego_parse_auth failed at %d\n", (int)data.ofs));
+ asn1_free(&data);
+ return False;
+ }
+
+ asn1_free(&data);
+ return True;
+}
+
+/*
+ generate a minimal SPNEGO response packet. Doesn't contain much.
+*/
+DATA_BLOB spnego_gen_auth_response(DATA_BLOB *reply, NTSTATUS nt_status,
+ const char *mechOID)
+{
+ ASN1_DATA data;
+ DATA_BLOB ret;
+ uint8 negResult;
+
+ if (NT_STATUS_IS_OK(nt_status)) {
+ negResult = SPNEGO_NEG_RESULT_ACCEPT;
+ } else if (NT_STATUS_EQUAL(nt_status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
+ negResult = SPNEGO_NEG_RESULT_INCOMPLETE;
+ } else {
+ negResult = SPNEGO_NEG_RESULT_REJECT;
+ }
+
+ ZERO_STRUCT(data);
+
+ asn1_push_tag(&data, ASN1_CONTEXT(1));
+ asn1_push_tag(&data, ASN1_SEQUENCE(0));
+ asn1_push_tag(&data, ASN1_CONTEXT(0));
+ asn1_write_enumerated(&data, negResult);
+ asn1_pop_tag(&data);
+
+ if (reply->data != NULL) {
+ asn1_push_tag(&data,ASN1_CONTEXT(1));
+ asn1_write_OID(&data, mechOID);
+ asn1_pop_tag(&data);
+
+ asn1_push_tag(&data,ASN1_CONTEXT(2));
+ asn1_write_OctetString(&data, reply->data, reply->length);
+ asn1_pop_tag(&data);
+ }
+
+ asn1_pop_tag(&data);
+ asn1_pop_tag(&data);
+
+ ret = data_blob(data.data, data.length);
+ asn1_free(&data);
+ return ret;
+}
+
+/*
+ parse a SPNEGO NTLMSSP auth packet. This contains the encrypted passwords
+*/
+BOOL spnego_parse_auth_response(DATA_BLOB blob, NTSTATUS nt_status,
+ DATA_BLOB *auth)
+{
+ ASN1_DATA data;
+ uint8 negResult;
+
+ if (NT_STATUS_IS_OK(nt_status)) {
+ negResult = SPNEGO_NEG_RESULT_ACCEPT;
+ } else if (NT_STATUS_EQUAL(nt_status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
+ negResult = SPNEGO_NEG_RESULT_INCOMPLETE;
+ } else {
+ negResult = SPNEGO_NEG_RESULT_REJECT;
+ }
+
+ asn1_load(&data, blob);
+ asn1_start_tag(&data, ASN1_CONTEXT(1));
+ asn1_start_tag(&data, ASN1_SEQUENCE(0));
+ asn1_start_tag(&data, ASN1_CONTEXT(0));
+ asn1_check_enumerated(&data, negResult);
+ asn1_end_tag(&data);
+
+ if (negResult == SPNEGO_NEG_RESULT_INCOMPLETE) {
+ asn1_start_tag(&data,ASN1_CONTEXT(1));
+ asn1_check_OID(&data, OID_NTLMSSP);
+ asn1_end_tag(&data);
+
+ asn1_start_tag(&data,ASN1_CONTEXT(2));
+ asn1_read_OctetString(&data, auth);
+ asn1_end_tag(&data);
+ }
+
+ asn1_end_tag(&data);
+ asn1_end_tag(&data);
+
+ if (data.has_error) {
+ DEBUG(3,("spnego_parse_auth_response failed at %d\n", (int)data.ofs));
+ asn1_free(&data);
+ data_blob_free(auth);
+ return False;
+ }
+
+ asn1_free(&data);
+ return True;
+}
+
diff --git a/source/libsmb/clistr.c b/source/libsmb/clistr.c
new file mode 100644
index 00000000000..c61445c0735
--- /dev/null
+++ b/source/libsmb/clistr.c
@@ -0,0 +1,58 @@
+/*
+ Unix SMB/CIFS implementation.
+ client string routines
+ Copyright (C) Andrew Tridgell 2001
+ Copyright (C) Andrew Bartlett <abartlet@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 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"
+
+size_t clistr_push_fn(const char *function, unsigned int line,
+ struct cli_state *cli, void *dest,
+ const char *src, int dest_len, int flags)
+{
+ size_t buf_used = PTR_DIFF(dest, cli->outbuf);
+ if (dest_len == -1) {
+ if (((ptrdiff_t)dest < (ptrdiff_t)cli->outbuf) || (buf_used > cli->bufsize)) {
+ DEBUG(0, ("Pushing string of 'unlimited' length into non-SMB buffer!\n"));
+ return push_string_fn(function, line, cli->outbuf, dest, src, -1, flags);
+ }
+ return push_string_fn(function, line, cli->outbuf, dest, src, cli->bufsize - buf_used, flags);
+ }
+
+ /* 'normal' push into size-specified buffer */
+ return push_string_fn(function, line, cli->outbuf, dest, src, dest_len, flags);
+}
+
+size_t clistr_pull_fn(const char *function, unsigned int line,
+ struct cli_state *cli, char *dest, const void *src,
+ int dest_len, int src_len,
+ int flags)
+{
+ return pull_string_fn(function, line, cli->inbuf, dest, src, dest_len, src_len, flags);
+}
+
+
+size_t clistr_align_out(struct cli_state *cli, const void *p, int flags)
+{
+ return align_string(cli->outbuf, p, flags);
+}
+
+size_t clistr_align_in(struct cli_state *cli, const void *p, int flags)
+{
+ return align_string(cli->inbuf, p, flags);
+}
diff --git a/source/libsmb/clitrans.c b/source/libsmb/clitrans.c
new file mode 100644
index 00000000000..ae44ca1a779
--- /dev/null
+++ b/source/libsmb/clitrans.c
@@ -0,0 +1,658 @@
+/*
+ Unix SMB/CIFS implementation.
+ client transaction calls
+ Copyright (C) Andrew Tridgell 1994-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 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.
+*/
+
+#define NO_SYSLOG
+
+#include "includes.h"
+
+
+/****************************************************************************
+ Send a SMB trans or trans2 request.
+****************************************************************************/
+
+BOOL cli_send_trans(struct cli_state *cli, int trans,
+ const char *pipe_name,
+ int fid, int flags,
+ uint16 *setup, unsigned int lsetup, unsigned int msetup,
+ const char *param, unsigned int lparam, unsigned int mparam,
+ const char *data, unsigned int ldata, unsigned int mdata)
+{
+ unsigned int i;
+ unsigned int this_ldata,this_lparam;
+ unsigned int tot_data=0,tot_param=0;
+ char *outdata,*outparam;
+ char *p;
+ int pipe_name_len=0;
+ uint16 mid;
+
+ this_lparam = MIN(lparam,cli->max_xmit - (500+lsetup*2)); /* hack */
+ this_ldata = MIN(ldata,cli->max_xmit - (500+lsetup*2+this_lparam));
+
+ memset(cli->outbuf,'\0',smb_size);
+ set_message(cli->outbuf,14+lsetup,0,True);
+ SCVAL(cli->outbuf,smb_com,trans);
+ SSVAL(cli->outbuf,smb_tid, cli->cnum);
+ cli_setup_packet(cli);
+
+ /*
+ * Save the mid we're using. We need this for finding
+ * signing replies.
+ */
+
+ mid = cli->mid;
+
+ if (pipe_name) {
+ pipe_name_len = clistr_push(cli, smb_buf(cli->outbuf), pipe_name, -1, STR_TERMINATE);
+ }
+
+ outparam = smb_buf(cli->outbuf)+(trans==SMBtrans ? pipe_name_len : 3);
+ outdata = outparam+this_lparam;
+
+ /* primary request */
+ SSVAL(cli->outbuf,smb_tpscnt,lparam); /* tpscnt */
+ SSVAL(cli->outbuf,smb_tdscnt,ldata); /* tdscnt */
+ SSVAL(cli->outbuf,smb_mprcnt,mparam); /* mprcnt */
+ SSVAL(cli->outbuf,smb_mdrcnt,mdata); /* mdrcnt */
+ SCVAL(cli->outbuf,smb_msrcnt,msetup); /* msrcnt */
+ SSVAL(cli->outbuf,smb_flags,flags); /* flags */
+ SIVAL(cli->outbuf,smb_timeout,0); /* timeout */
+ SSVAL(cli->outbuf,smb_pscnt,this_lparam); /* pscnt */
+ SSVAL(cli->outbuf,smb_psoff,smb_offset(outparam,cli->outbuf)); /* psoff */
+ SSVAL(cli->outbuf,smb_dscnt,this_ldata); /* dscnt */
+ SSVAL(cli->outbuf,smb_dsoff,smb_offset(outdata,cli->outbuf)); /* dsoff */
+ SCVAL(cli->outbuf,smb_suwcnt,lsetup); /* suwcnt */
+ for (i=0;i<lsetup;i++) /* setup[] */
+ SSVAL(cli->outbuf,smb_setup+i*2,setup[i]);
+ p = smb_buf(cli->outbuf);
+ if (trans != SMBtrans) {
+ *p++ = 0; /* put in a null smb_name */
+ *p++ = 'D'; *p++ = ' '; /* observed in OS/2 */
+ }
+ if (this_lparam) /* param[] */
+ memcpy(outparam,param,this_lparam);
+ if (this_ldata) /* data[] */
+ memcpy(outdata,data,this_ldata);
+ cli_setup_bcc(cli, outdata+this_ldata);
+
+ show_msg(cli->outbuf);
+
+ if (!cli_send_smb(cli)) {
+ return False;
+ }
+
+ if (this_ldata < ldata || this_lparam < lparam) {
+ /* receive interim response */
+ if (!cli_receive_smb(cli) || cli_is_error(cli)) {
+ return(False);
+ }
+
+ tot_data = this_ldata;
+ tot_param = this_lparam;
+
+ while (tot_data < ldata || tot_param < lparam) {
+ this_lparam = MIN(lparam-tot_param,cli->max_xmit - 500); /* hack */
+ this_ldata = MIN(ldata-tot_data,cli->max_xmit - (500+this_lparam));
+
+ set_message(cli->outbuf,trans==SMBtrans?8:9,0,True);
+ SCVAL(cli->outbuf,smb_com,(trans==SMBtrans ? SMBtranss : SMBtranss2));
+
+ outparam = smb_buf(cli->outbuf);
+ outdata = outparam+this_lparam;
+
+ /* secondary request */
+ SSVAL(cli->outbuf,smb_tpscnt,lparam); /* tpscnt */
+ SSVAL(cli->outbuf,smb_tdscnt,ldata); /* tdscnt */
+ SSVAL(cli->outbuf,smb_spscnt,this_lparam); /* pscnt */
+ SSVAL(cli->outbuf,smb_spsoff,smb_offset(outparam,cli->outbuf)); /* psoff */
+ SSVAL(cli->outbuf,smb_spsdisp,tot_param); /* psdisp */
+ SSVAL(cli->outbuf,smb_sdscnt,this_ldata); /* dscnt */
+ SSVAL(cli->outbuf,smb_sdsoff,smb_offset(outdata,cli->outbuf)); /* dsoff */
+ SSVAL(cli->outbuf,smb_sdsdisp,tot_data); /* dsdisp */
+ if (trans==SMBtrans2)
+ SSVALS(cli->outbuf,smb_sfid,fid); /* fid */
+ if (this_lparam) /* param[] */
+ memcpy(outparam,param+tot_param,this_lparam);
+ if (this_ldata) /* data[] */
+ memcpy(outdata,data+tot_data,this_ldata);
+ cli_setup_bcc(cli, outdata+this_ldata);
+
+ /*
+ * Save the mid we're using. We need this for finding
+ * signing replies.
+ */
+ mid = cli->mid;
+
+ show_msg(cli->outbuf);
+ if (!cli_send_smb(cli)) {
+ return False;
+ }
+
+ /* Ensure we use the same mid for the secondaries. */
+ cli->mid = mid;
+
+ tot_data += this_ldata;
+ tot_param += this_lparam;
+ }
+ }
+
+ /* Note we're in a trans state. Save the sequence
+ * numbers for replies. */
+
+ cli_signing_trans_start(cli, mid);
+ return(True);
+}
+
+/****************************************************************************
+ Receive a SMB trans or trans2 response allocating the necessary memory.
+****************************************************************************/
+
+BOOL cli_receive_trans(struct cli_state *cli,int trans,
+ char **param, unsigned int *param_len,
+ char **data, unsigned int *data_len)
+{
+ unsigned int total_data=0;
+ unsigned int total_param=0;
+ unsigned int this_data,this_param;
+ NTSTATUS status;
+ char *tdata;
+ char *tparam;
+
+ *data_len = *param_len = 0;
+
+ if (!cli_receive_smb(cli)) {
+ cli_signing_trans_stop(cli);
+ return False;
+ }
+
+ show_msg(cli->inbuf);
+
+ /* sanity check */
+ if (CVAL(cli->inbuf,smb_com) != trans) {
+ DEBUG(0,("Expected %s response, got command 0x%02x\n",
+ trans==SMBtrans?"SMBtrans":"SMBtrans2",
+ CVAL(cli->inbuf,smb_com)));
+ cli_signing_trans_stop(cli);
+ return(False);
+ }
+
+ /*
+ * 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.
+ */
+ status = cli_nt_error(cli);
+
+ if (NT_STATUS_IS_ERR(status)) {
+ cli_signing_trans_stop(cli);
+ return False;
+ }
+
+ /* parse out the lengths */
+ total_data = SVAL(cli->inbuf,smb_tdrcnt);
+ total_param = SVAL(cli->inbuf,smb_tprcnt);
+
+ /* allocate it */
+ if (total_data!=0) {
+ tdata = Realloc(*data,total_data);
+ if (!tdata) {
+ DEBUG(0,("cli_receive_trans: failed to enlarge data buffer\n"));
+ cli_signing_trans_stop(cli);
+ return False;
+ }
+ else
+ *data = tdata;
+ }
+
+ if (total_param!=0) {
+ tparam = Realloc(*param,total_param);
+ if (!tparam) {
+ DEBUG(0,("cli_receive_trans: failed to enlarge param buffer\n"));
+ cli_signing_trans_stop(cli);
+ return False;
+ }
+ else
+ *param = tparam;
+ }
+
+ for (;;) {
+ this_data = SVAL(cli->inbuf,smb_drcnt);
+ this_param = SVAL(cli->inbuf,smb_prcnt);
+
+ if (this_data + *data_len > total_data ||
+ this_param + *param_len > total_param) {
+ DEBUG(1,("Data overflow in cli_receive_trans\n"));
+ cli_signing_trans_stop(cli);
+ return False;
+ }
+
+ if (this_data + *data_len < this_data ||
+ this_data + *data_len < *data_len ||
+ this_param + *param_len < this_param ||
+ this_param + *param_len < *param_len) {
+ DEBUG(1,("Data overflow in cli_receive_trans\n"));
+ cli_signing_trans_stop(cli);
+ return False;
+ }
+
+ if (this_data) {
+ unsigned int data_offset_out = SVAL(cli->inbuf,smb_drdisp);
+ unsigned int data_offset_in = SVAL(cli->inbuf,smb_droff);
+
+ if (data_offset_out > total_data ||
+ data_offset_out + this_data > total_data ||
+ data_offset_out + this_data < data_offset_out ||
+ data_offset_out + this_data < this_data) {
+ DEBUG(1,("Data overflow in cli_receive_trans\n"));
+ cli_signing_trans_stop(cli);
+ return False;
+ }
+ if (data_offset_in > cli->bufsize ||
+ data_offset_in + this_data > cli->bufsize ||
+ data_offset_in + this_data < data_offset_in ||
+ data_offset_in + this_data < this_data) {
+ DEBUG(1,("Data overflow in cli_receive_trans\n"));
+ cli_signing_trans_stop(cli);
+ return False;
+ }
+
+ memcpy(*data + data_offset_out, smb_base(cli->inbuf) + data_offset_in, this_data);
+ }
+ if (this_param) {
+ unsigned int param_offset_out = SVAL(cli->inbuf,smb_prdisp);
+ unsigned int param_offset_in = SVAL(cli->inbuf,smb_proff);
+
+ if (param_offset_out > total_param ||
+ param_offset_out + this_param > total_param ||
+ param_offset_out + this_param < param_offset_out ||
+ param_offset_out + this_param < this_param) {
+ DEBUG(1,("Param overflow in cli_receive_trans\n"));
+ cli_signing_trans_stop(cli);
+ return False;
+ }
+ if (param_offset_in > cli->bufsize ||
+ param_offset_in + this_param > cli->bufsize ||
+ param_offset_in + this_param < param_offset_in ||
+ param_offset_in + this_param < this_param) {
+ DEBUG(1,("Param overflow in cli_receive_trans\n"));
+ cli_signing_trans_stop(cli);
+ return False;
+ }
+
+ memcpy(*param + param_offset_out, smb_base(cli->inbuf) + param_offset_in, this_param);
+ }
+ *data_len += this_data;
+ *param_len += this_param;
+
+ if (total_data <= *data_len && total_param <= *param_len)
+ break;
+
+ if (!cli_receive_smb(cli)) {
+ cli_signing_trans_stop(cli);
+ return False;
+ }
+
+ show_msg(cli->inbuf);
+
+ /* sanity check */
+ if (CVAL(cli->inbuf,smb_com) != trans) {
+ DEBUG(0,("Expected %s response, got command 0x%02x\n",
+ trans==SMBtrans?"SMBtrans":"SMBtrans2",
+ CVAL(cli->inbuf,smb_com)));
+ cli_signing_trans_stop(cli);
+ return(False);
+ }
+ if (NT_STATUS_IS_ERR(cli_nt_error(cli))) {
+ cli_signing_trans_stop(cli);
+ return(False);
+ }
+
+ /* parse out the total lengths again - they can shrink! */
+ if (SVAL(cli->inbuf,smb_tdrcnt) < total_data)
+ total_data = SVAL(cli->inbuf,smb_tdrcnt);
+ if (SVAL(cli->inbuf,smb_tprcnt) < total_param)
+ total_param = SVAL(cli->inbuf,smb_tprcnt);
+
+ if (total_data <= *data_len && total_param <= *param_len)
+ break;
+
+ }
+
+ cli_signing_trans_stop(cli);
+ return(True);
+}
+
+/****************************************************************************
+ Send a SMB nttrans request.
+****************************************************************************/
+
+BOOL cli_send_nt_trans(struct cli_state *cli,
+ int function,
+ int flags,
+ uint16 *setup, unsigned int lsetup, unsigned int msetup,
+ char *param, unsigned int lparam, unsigned int mparam,
+ char *data, unsigned int ldata, unsigned int mdata)
+{
+ unsigned int i;
+ unsigned int this_ldata,this_lparam;
+ unsigned int tot_data=0,tot_param=0;
+ uint16 mid;
+ char *outdata,*outparam;
+
+ this_lparam = MIN(lparam,cli->max_xmit - (500+lsetup*2)); /* hack */
+ this_ldata = MIN(ldata,cli->max_xmit - (500+lsetup*2+this_lparam));
+
+ memset(cli->outbuf,'\0',smb_size);
+ set_message(cli->outbuf,19+lsetup,0,True);
+ SCVAL(cli->outbuf,smb_com,SMBnttrans);
+ SSVAL(cli->outbuf,smb_tid, cli->cnum);
+ cli_setup_packet(cli);
+
+ /*
+ * Save the mid we're using. We need this for finding
+ * signing replies.
+ */
+
+ mid = cli->mid;
+
+ outparam = smb_buf(cli->outbuf)+3;
+ outdata = outparam+this_lparam;
+
+ /* primary request */
+ SCVAL(cli->outbuf,smb_nt_MaxSetupCount,msetup);
+ SCVAL(cli->outbuf,smb_nt_Flags,flags);
+ SIVAL(cli->outbuf,smb_nt_TotalParameterCount, lparam);
+ SIVAL(cli->outbuf,smb_nt_TotalDataCount, ldata);
+ SIVAL(cli->outbuf,smb_nt_MaxParameterCount, mparam);
+ SIVAL(cli->outbuf,smb_nt_MaxDataCount, mdata);
+ SIVAL(cli->outbuf,smb_nt_ParameterCount, this_lparam);
+ SIVAL(cli->outbuf,smb_nt_ParameterOffset, smb_offset(outparam,cli->outbuf));
+ SIVAL(cli->outbuf,smb_nt_DataCount, this_ldata);
+ SIVAL(cli->outbuf,smb_nt_DataOffset, smb_offset(outdata,cli->outbuf));
+ SIVAL(cli->outbuf,smb_nt_SetupCount, lsetup);
+ SIVAL(cli->outbuf,smb_nt_Function, function);
+ for (i=0;i<lsetup;i++) /* setup[] */
+ SSVAL(cli->outbuf,smb_nt_SetupStart+i*2,setup[i]);
+
+ if (this_lparam) /* param[] */
+ memcpy(outparam,param,this_lparam);
+ if (this_ldata) /* data[] */
+ memcpy(outdata,data,this_ldata);
+
+ cli_setup_bcc(cli, outdata+this_ldata);
+
+ show_msg(cli->outbuf);
+ if (!cli_send_smb(cli)) {
+ return False;
+ }
+
+ if (this_ldata < ldata || this_lparam < lparam) {
+ /* receive interim response */
+ if (!cli_receive_smb(cli) || cli_is_error(cli)) {
+ return(False);
+ }
+
+ tot_data = this_ldata;
+ tot_param = this_lparam;
+
+ while (tot_data < ldata || tot_param < lparam) {
+ this_lparam = MIN(lparam-tot_param,cli->max_xmit - 500); /* hack */
+ this_ldata = MIN(ldata-tot_data,cli->max_xmit - (500+this_lparam));
+
+ set_message(cli->outbuf,18,0,True);
+ SCVAL(cli->outbuf,smb_com,SMBnttranss);
+
+ /* XXX - these should probably be aligned */
+ outparam = smb_buf(cli->outbuf);
+ outdata = outparam+this_lparam;
+
+ /* secondary request */
+ SIVAL(cli->outbuf,smb_nts_TotalParameterCount,lparam);
+ SIVAL(cli->outbuf,smb_nts_TotalDataCount,ldata);
+ SIVAL(cli->outbuf,smb_nts_ParameterCount,this_lparam);
+ SIVAL(cli->outbuf,smb_nts_ParameterOffset,smb_offset(outparam,cli->outbuf));
+ SIVAL(cli->outbuf,smb_nts_ParameterDisplacement,tot_param);
+ SIVAL(cli->outbuf,smb_nts_DataCount,this_ldata);
+ SIVAL(cli->outbuf,smb_nts_DataOffset,smb_offset(outdata,cli->outbuf));
+ SIVAL(cli->outbuf,smb_nts_DataDisplacement,tot_data);
+ if (this_lparam) /* param[] */
+ memcpy(outparam,param+tot_param,this_lparam);
+ if (this_ldata) /* data[] */
+ memcpy(outdata,data+tot_data,this_ldata);
+ cli_setup_bcc(cli, outdata+this_ldata);
+
+ /*
+ * Save the mid we're using. We need this for finding
+ * signing replies.
+ */
+ mid = cli->mid;
+
+ show_msg(cli->outbuf);
+
+ if (!cli_send_smb(cli)) {
+ return False;
+ }
+
+ /* Ensure we use the same mid for the secondaries. */
+ cli->mid = mid;
+
+ tot_data += this_ldata;
+ tot_param += this_lparam;
+ }
+ }
+
+ /* Note we're in a trans state. Save the sequence
+ * numbers for replies. */
+
+ cli_signing_trans_start(cli, mid);
+ return(True);
+}
+
+/****************************************************************************
+ receive a SMB nttrans response allocating the necessary memory
+ ****************************************************************************/
+
+BOOL cli_receive_nt_trans(struct cli_state *cli,
+ char **param, unsigned int *param_len,
+ char **data, unsigned int *data_len)
+{
+ unsigned int total_data=0;
+ unsigned int total_param=0;
+ unsigned int this_data,this_param;
+ uint8 eclass;
+ uint32 ecode;
+ char *tdata;
+ char *tparam;
+
+ *data_len = *param_len = 0;
+
+ if (!cli_receive_smb(cli)) {
+ cli_signing_trans_stop(cli);
+ return False;
+ }
+
+ show_msg(cli->inbuf);
+
+ /* sanity check */
+ if (CVAL(cli->inbuf,smb_com) != SMBnttrans) {
+ DEBUG(0,("Expected SMBnttrans response, got command 0x%02x\n",
+ CVAL(cli->inbuf,smb_com)));
+ cli_signing_trans_stop(cli);
+ return(False);
+ }
+
+ /*
+ * 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 (cli_is_dos_error(cli)) {
+ cli_dos_error(cli, &eclass, &ecode);
+ if (cli->nt_pipe_fnum == 0 || !(eclass == ERRDOS && ecode == ERRmoredata)) {
+ cli_signing_trans_stop(cli);
+ return(False);
+ }
+ }
+
+ /*
+ * Likewise for NT_STATUS_BUFFER_TOO_SMALL
+ */
+ if (cli_is_nt_error(cli)) {
+ if (!NT_STATUS_EQUAL(cli_nt_error(cli),
+ NT_STATUS_BUFFER_TOO_SMALL)) {
+ cli_signing_trans_stop(cli);
+ return(False);
+ }
+ }
+
+ /* parse out the lengths */
+ total_data = SVAL(cli->inbuf,smb_ntr_TotalDataCount);
+ total_param = SVAL(cli->inbuf,smb_ntr_TotalParameterCount);
+
+ /* allocate it */
+ if (total_data) {
+ tdata = Realloc(*data,total_data);
+ if (!tdata) {
+ DEBUG(0,("cli_receive_nt_trans: failed to enlarge data buffer to %d\n",total_data));
+ cli_signing_trans_stop(cli);
+ return False;
+ } else {
+ *data = tdata;
+ }
+ }
+
+ if (total_param) {
+ tparam = Realloc(*param,total_param);
+ if (!tparam) {
+ DEBUG(0,("cli_receive_nt_trans: failed to enlarge param buffer to %d\n", total_param));
+ cli_signing_trans_stop(cli);
+ return False;
+ } else {
+ *param = tparam;
+ }
+ }
+
+ while (1) {
+ this_data = SVAL(cli->inbuf,smb_ntr_DataCount);
+ this_param = SVAL(cli->inbuf,smb_ntr_ParameterCount);
+
+ if (this_data + *data_len > total_data ||
+ this_param + *param_len > total_param) {
+ DEBUG(1,("Data overflow in cli_receive_nt_trans\n"));
+ cli_signing_trans_stop(cli);
+ return False;
+ }
+
+ if (this_data + *data_len < this_data ||
+ this_data + *data_len < *data_len ||
+ this_param + *param_len < this_param ||
+ this_param + *param_len < *param_len) {
+ DEBUG(1,("Data overflow in cli_receive_nt_trans\n"));
+ cli_signing_trans_stop(cli);
+ return False;
+ }
+
+ if (this_data) {
+ unsigned int data_offset_out = SVAL(cli->inbuf,smb_ntr_DataDisplacement);
+ unsigned int data_offset_in = SVAL(cli->inbuf,smb_ntr_DataOffset);
+
+ if (data_offset_out > total_data ||
+ data_offset_out + this_data > total_data ||
+ data_offset_out + this_data < data_offset_out ||
+ data_offset_out + this_data < this_data) {
+ DEBUG(1,("Data overflow in cli_receive_nt_trans\n"));
+ cli_signing_trans_stop(cli);
+ return False;
+ }
+ if (data_offset_in > cli->bufsize ||
+ data_offset_in + this_data > cli->bufsize ||
+ data_offset_in + this_data < data_offset_in ||
+ data_offset_in + this_data < this_data) {
+ DEBUG(1,("Data overflow in cli_receive_nt_trans\n"));
+ cli_signing_trans_stop(cli);
+ return False;
+ }
+
+ memcpy(*data + data_offset_out, smb_base(cli->inbuf) + data_offset_in, this_data);
+ }
+
+ if (this_param) {
+ unsigned int param_offset_out = SVAL(cli->inbuf,smb_ntr_ParameterDisplacement);
+ unsigned int param_offset_in = SVAL(cli->inbuf,smb_ntr_ParameterOffset);
+
+ if (param_offset_out > total_param ||
+ param_offset_out + this_param > total_param ||
+ param_offset_out + this_param < param_offset_out ||
+ param_offset_out + this_param < this_param) {
+ DEBUG(1,("Param overflow in cli_receive_nt_trans\n"));
+ cli_signing_trans_stop(cli);
+ return False;
+ }
+ if (param_offset_in > cli->bufsize ||
+ param_offset_in + this_param > cli->bufsize ||
+ param_offset_in + this_param < param_offset_in ||
+ param_offset_in + this_param < this_param) {
+ DEBUG(1,("Param overflow in cli_receive_nt_trans\n"));
+ cli_signing_trans_stop(cli);
+ return False;
+ }
+
+ memcpy(*param + param_offset_out, smb_base(cli->inbuf) + param_offset_in, this_param);
+ }
+
+ *data_len += this_data;
+ *param_len += this_param;
+
+ if (total_data <= *data_len && total_param <= *param_len)
+ break;
+
+ if (!cli_receive_smb(cli)) {
+ cli_signing_trans_stop(cli);
+ return False;
+ }
+
+ show_msg(cli->inbuf);
+
+ /* sanity check */
+ if (CVAL(cli->inbuf,smb_com) != SMBnttrans) {
+ DEBUG(0,("Expected SMBnttrans response, got command 0x%02x\n",
+ CVAL(cli->inbuf,smb_com)));
+ cli_signing_trans_stop(cli);
+ return(False);
+ }
+ if (cli_is_dos_error(cli)) {
+ cli_dos_error(cli, &eclass, &ecode);
+ if(cli->nt_pipe_fnum == 0 || !(eclass == ERRDOS && ecode == ERRmoredata)) {
+ cli_signing_trans_stop(cli);
+ return(False);
+ }
+ }
+ /* parse out the total lengths again - they can shrink! */
+ if (SVAL(cli->inbuf,smb_ntr_TotalDataCount) < total_data)
+ total_data = SVAL(cli->inbuf,smb_ntr_TotalDataCount);
+ if (SVAL(cli->inbuf,smb_ntr_TotalParameterCount) < total_param)
+ total_param = SVAL(cli->inbuf,smb_ntr_TotalParameterCount);
+
+ if (total_data <= *data_len && total_param <= *param_len)
+ break;
+ }
+
+ cli_signing_trans_stop(cli);
+ return(True);
+}
diff --git a/source/libsmb/conncache.c b/source/libsmb/conncache.c
new file mode 100644
index 00000000000..e6604617d68
--- /dev/null
+++ b/source/libsmb/conncache.c
@@ -0,0 +1,158 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ Winbind daemon connection manager
+
+ Copyright (C) Tim Potter 2001
+ Copyright (C) Andrew Bartlett 2002
+ Copyright (C) Gerald (Jerry) Carter 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 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"
+
+#define FAILED_CONNECTION_CACHE_TIMEOUT 30 /* Seconds between attempts */
+
+#define CONNCACHE_ADDR 1
+#define CONNCACHE_NAME 2
+
+/* cache entry contains either a server name **or** and IP address as
+ the key. This means that a server could have two entries (one for each key) */
+
+struct failed_connection_cache {
+ fstring domain_name;
+ fstring controller;
+ time_t lookup_time;
+ NTSTATUS nt_status;
+ struct failed_connection_cache *prev, *next;
+};
+
+static struct failed_connection_cache *failed_connection_cache;
+
+/**********************************************************************
+ Check for a previously failed connection
+**********************************************************************/
+
+NTSTATUS check_negative_conn_cache( const char *domain, const char *server )
+{
+ struct failed_connection_cache *fcc;
+ NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+
+ /* can't check if we don't have strings */
+
+ if ( !domain || !server )
+ return NT_STATUS_OK;
+
+ for (fcc = failed_connection_cache; fcc; fcc = fcc->next) {
+
+ if ( !(strequal(domain, fcc->domain_name) && strequal(server, fcc->controller)) )
+ continue; /* no match; check the next entry */
+
+ /* we have a match so see if it is still current */
+
+ if ((time(NULL) - fcc->lookup_time) > FAILED_CONNECTION_CACHE_TIMEOUT)
+ {
+ /* Cache entry has expired, delete it */
+
+ DEBUG(10, ("check_negative_conn_cache: cache entry expired for %s, %s\n",
+ domain, server ));
+
+ DLIST_REMOVE(failed_connection_cache, fcc);
+ SAFE_FREE(fcc);
+
+ return NT_STATUS_OK;
+ }
+
+ /* The timeout hasn't expired yet so return false */
+
+ DEBUG(10, ("check_negative_conn_cache: returning negative entry for %s, %s\n",
+ domain, server ));
+
+ result = fcc->nt_status;
+ return result;
+ }
+
+ /* end of function means no cache entry */
+ return NT_STATUS_OK;
+}
+
+/**********************************************************************
+ Add an entry to the failed conneciton cache (aither a name of dotted
+ decimal IP
+**********************************************************************/
+
+void add_failed_connection_entry(const char *domain, const char *server, NTSTATUS result)
+{
+ struct failed_connection_cache *fcc;
+
+ SMB_ASSERT(!NT_STATUS_IS_OK(result));
+
+ /* Check we already aren't in the cache. We always have to have
+ a domain, but maybe not a specific DC name. */
+
+ for (fcc = failed_connection_cache; fcc; fcc = fcc->next) {
+ if ( strequal(fcc->domain_name, domain) && strequal(fcc->controller, server) )
+ {
+ DEBUG(10, ("add_failed_connection_entry: domain %s (%s) already tried and failed\n",
+ domain, server ));
+ return;
+ }
+ }
+
+ /* Create negative lookup cache entry for this domain and controller */
+
+ if ( !(fcc = (struct failed_connection_cache *)malloc(sizeof(struct failed_connection_cache))) )
+ {
+ DEBUG(0, ("malloc failed in add_failed_connection_entry!\n"));
+ return;
+ }
+
+ ZERO_STRUCTP(fcc);
+
+ fstrcpy( fcc->domain_name, domain );
+ fstrcpy( fcc->controller, server );
+ fcc->lookup_time = time(NULL);
+ fcc->nt_status = result;
+
+ DEBUG(10,("add_failed_connection_entry: added domain %s (%s) to failed conn cache\n",
+ domain, server ));
+
+ DLIST_ADD(failed_connection_cache, fcc);
+}
+
+/****************************************************************************
+****************************************************************************/
+
+void flush_negative_conn_cache( void )
+{
+ struct failed_connection_cache *fcc;
+
+ fcc = failed_connection_cache;
+
+ while (fcc) {
+ struct failed_connection_cache *fcc_next;
+
+ fcc_next = fcc->next;
+ DLIST_REMOVE(failed_connection_cache, fcc);
+ free(fcc);
+
+ fcc = fcc_next;
+ }
+
+}
+
+
diff --git a/source/libsmb/credentials.c b/source/libsmb/credentials.c
new file mode 100644
index 00000000000..0d521bae8ac
--- /dev/null
+++ b/source/libsmb/credentials.c
@@ -0,0 +1,215 @@
+/*
+ Unix SMB/CIFS implementation.
+ code to manipulate domain credentials
+ 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 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"
+
+/****************************************************************************
+represent a credential as a string
+****************************************************************************/
+char *credstr(const uchar *cred)
+{
+ static fstring buf;
+ slprintf(buf, sizeof(buf) - 1, "%02X%02X%02X%02X%02X%02X%02X%02X",
+ cred[0], cred[1], cred[2], cred[3],
+ cred[4], cred[5], cred[6], cred[7]);
+ return buf;
+}
+
+
+/****************************************************************************
+ setup the session key.
+Input: 8 byte challenge block
+ 8 byte server challenge block
+ 16 byte md4 encrypted password
+Output:
+ 8 byte session key
+****************************************************************************/
+void cred_session_key(const DOM_CHAL *clnt_chal, const DOM_CHAL *srv_chal, const uchar *pass,
+ uchar session_key[8])
+{
+ uint32 sum[2];
+ unsigned char sum2[8];
+
+ sum[0] = IVAL(clnt_chal->data, 0) + IVAL(srv_chal->data, 0);
+ sum[1] = IVAL(clnt_chal->data, 4) + IVAL(srv_chal->data, 4);
+
+ SIVAL(sum2,0,sum[0]);
+ SIVAL(sum2,4,sum[1]);
+
+ cred_hash1(session_key, sum2, pass);
+
+ /* debug output */
+ DEBUG(4,("cred_session_key\n"));
+
+ DEBUG(5,(" clnt_chal: %s\n", credstr(clnt_chal->data)));
+ DEBUG(5,(" srv_chal : %s\n", credstr(srv_chal->data)));
+ DEBUG(5,(" clnt+srv : %s\n", credstr(sum2)));
+ DEBUG(5,(" sess_key : %s\n", credstr(session_key)));
+}
+
+
+/****************************************************************************
+create a credential
+
+Input:
+ 8 byte sesssion key
+ 8 byte stored credential
+ 4 byte timestamp
+
+Output:
+ 8 byte credential
+****************************************************************************/
+void cred_create(uchar session_key[8], DOM_CHAL *stor_cred, UTIME timestamp,
+ DOM_CHAL *cred)
+{
+ DOM_CHAL time_cred;
+
+ SIVAL(time_cred.data, 0, IVAL(stor_cred->data, 0) + timestamp.time);
+ SIVAL(time_cred.data, 4, IVAL(stor_cred->data, 4));
+
+ cred_hash2(cred->data, time_cred.data, session_key);
+
+ /* debug output*/
+ DEBUG(4,("cred_create\n"));
+
+ DEBUG(5,(" sess_key : %s\n", credstr(session_key)));
+ DEBUG(5,(" stor_cred: %s\n", credstr(stor_cred->data)));
+ DEBUG(5,(" timestamp: %x\n" , timestamp.time));
+ DEBUG(5,(" timecred : %s\n", credstr(time_cred.data)));
+ DEBUG(5,(" calc_cred: %s\n", credstr(cred->data)));
+}
+
+
+/****************************************************************************
+ check a supplied credential
+
+Input:
+ 8 byte received credential
+ 8 byte sesssion key
+ 8 byte stored credential
+ 4 byte timestamp
+
+Output:
+ returns 1 if computed credential matches received credential
+ returns 0 otherwise
+****************************************************************************/
+int cred_assert(DOM_CHAL *cred, uchar session_key[8], DOM_CHAL *stored_cred,
+ UTIME timestamp)
+{
+ DOM_CHAL cred2;
+
+ cred_create(session_key, stored_cred, timestamp, &cred2);
+
+ /* debug output*/
+ DEBUG(4,("cred_assert\n"));
+
+ DEBUG(5,(" challenge : %s\n", credstr(cred->data)));
+ DEBUG(5,(" calculated: %s\n", credstr(cred2.data)));
+
+ if (memcmp(cred->data, cred2.data, 8) == 0)
+ {
+ DEBUG(5, ("credentials check ok\n"));
+ return True;
+ }
+ else
+ {
+ DEBUG(5, ("credentials check wrong\n"));
+ return False;
+ }
+}
+
+
+/****************************************************************************
+ checks credentials; generates next step in the credential chain
+****************************************************************************/
+BOOL clnt_deal_with_creds(uchar sess_key[8],
+ DOM_CRED *sto_clnt_cred, DOM_CRED *rcv_srv_cred)
+{
+ UTIME new_clnt_time;
+ uint32 new_cred;
+
+ DEBUG(5,("clnt_deal_with_creds: %d\n", __LINE__));
+
+ /* increment client time by one second */
+ new_clnt_time.time = sto_clnt_cred->timestamp.time + 1;
+
+ /* check that the received server credentials are valid */
+ if (!cred_assert(&rcv_srv_cred->challenge, sess_key,
+ &sto_clnt_cred->challenge, new_clnt_time))
+ {
+ return False;
+ }
+
+ /* first 4 bytes of the new seed is old client 4 bytes + clnt time + 1 */
+ new_cred = IVAL(sto_clnt_cred->challenge.data, 0);
+ new_cred += new_clnt_time.time;
+
+ /* store new seed in client credentials */
+ SIVAL(sto_clnt_cred->challenge.data, 0, new_cred);
+
+ DEBUG(5,(" new clnt cred: %s\n", credstr(sto_clnt_cred->challenge.data)));
+ return True;
+}
+
+
+/****************************************************************************
+ checks credentials; generates next step in the credential chain
+****************************************************************************/
+BOOL deal_with_creds(uchar sess_key[8],
+ DOM_CRED *sto_clnt_cred,
+ DOM_CRED *rcv_clnt_cred, DOM_CRED *rtn_srv_cred)
+{
+ UTIME new_clnt_time;
+ uint32 new_cred;
+
+ DEBUG(5,("deal_with_creds: %d\n", __LINE__));
+
+ /* check that the received client credentials are valid */
+ if (!cred_assert(&rcv_clnt_cred->challenge, sess_key,
+ &sto_clnt_cred->challenge, rcv_clnt_cred->timestamp))
+ {
+ return False;
+ }
+
+ /* increment client time by one second */
+ new_clnt_time.time = rcv_clnt_cred->timestamp.time + 1;
+
+ /* first 4 bytes of the new seed is old client 4 bytes + clnt time + 1 */
+ new_cred = IVAL(sto_clnt_cred->challenge.data, 0);
+ new_cred += new_clnt_time.time;
+
+ DEBUG(5,("deal_with_creds: new_cred[0]=%x\n", new_cred));
+
+ /* doesn't matter that server time is 0 */
+ rtn_srv_cred->timestamp.time = 0;
+
+ DEBUG(5,("deal_with_creds: new_clnt_time=%x\n", new_clnt_time.time));
+
+ /* create return credentials for inclusion in the reply */
+ cred_create(sess_key, &sto_clnt_cred->challenge, new_clnt_time,
+ &rtn_srv_cred->challenge);
+
+ DEBUG(5,("deal_with_creds: clnt_cred=%s\n", credstr(sto_clnt_cred->challenge.data)));
+
+ /* store new seed in client credentials */
+ SIVAL(sto_clnt_cred->challenge.data, 0, new_cred);
+
+ return True;
+}
diff --git a/source/libsmb/doserr.c b/source/libsmb/doserr.c
new file mode 100644
index 00000000000..c6348568cf9
--- /dev/null
+++ b/source/libsmb/doserr.c
@@ -0,0 +1,92 @@
+/*
+ * Unix SMB/CIFS implementation.
+ * DOS error routines
+ * Copyright (C) Tim Potter 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 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.
+ */
+
+/* DOS error codes. please read doserr.h */
+
+#include "includes.h"
+
+typedef const struct
+{
+ const char *dos_errstr;
+ WERROR werror;
+} werror_code_struct;
+
+werror_code_struct dos_errs[] =
+{
+ { "WERR_OK", WERR_OK },
+ { "WERR_BADFILE", WERR_BADFILE },
+ { "WERR_ACCESS_DENIED", WERR_ACCESS_DENIED },
+ { "WERR_BADFID", WERR_BADFID },
+ { "WERR_BADFUNC", WERR_BADFUNC },
+ { "WERR_INSUFFICIENT_BUFFER", WERR_INSUFFICIENT_BUFFER },
+ { "WERR_NO_SUCH_SHARE", WERR_NO_SUCH_SHARE },
+ { "WERR_ALREADY_EXISTS", WERR_ALREADY_EXISTS },
+ { "WERR_INVALID_PARAM", WERR_INVALID_PARAM },
+ { "WERR_NOT_SUPPORTED", WERR_NOT_SUPPORTED },
+ { "WERR_BAD_PASSWORD", WERR_BAD_PASSWORD },
+ { "WERR_NOMEM", WERR_NOMEM },
+ { "WERR_INVALID_NAME", WERR_INVALID_NAME },
+ { "WERR_UNKNOWN_LEVEL", WERR_UNKNOWN_LEVEL },
+ { "WERR_OBJECT_PATH_INVALID", WERR_OBJECT_PATH_INVALID },
+ { "WERR_NO_MORE_ITEMS", WERR_NO_MORE_ITEMS },
+ { "WERR_MORE_DATA", WERR_MORE_DATA },
+ { "WERR_UNKNOWN_PRINTER_DRIVER", WERR_UNKNOWN_PRINTER_DRIVER },
+ { "WERR_INVALID_PRINTER_NAME", WERR_INVALID_PRINTER_NAME },
+ { "WERR_PRINTER_ALREADY_EXISTS", WERR_PRINTER_ALREADY_EXISTS },
+ { "WERR_INVALID_DATATYPE", WERR_INVALID_DATATYPE },
+ { "WERR_INVALID_ENVIRONMENT", WERR_INVALID_ENVIRONMENT },
+ { "WERR_INVALID_FORM_NAME", WERR_INVALID_FORM_NAME },
+ { "WERR_INVALID_FORM_SIZE", WERR_INVALID_FORM_SIZE },
+ { "WERR_BUF_TOO_SMALL", WERR_BUF_TOO_SMALL },
+ { "WERR_JOB_NOT_FOUND", WERR_JOB_NOT_FOUND },
+ { "WERR_DEST_NOT_FOUND", WERR_DEST_NOT_FOUND },
+ { "WERR_NOT_LOCAL_DOMAIN", WERR_NOT_LOCAL_DOMAIN },
+ { "WERR_PRINTER_DRIVER_IN_USE", WERR_PRINTER_DRIVER_IN_USE },
+ { "WERR_STATUS_MORE_ENTRIES ", WERR_STATUS_MORE_ENTRIES },
+ { "WERR_DFS_NO_SUCH_VOL", WERR_DFS_NO_SUCH_VOL },
+ { "WERR_DFS_NO_SUCH_SHARE", WERR_DFS_NO_SUCH_SHARE },
+ { "WERR_DFS_NO_SUCH_SERVER", WERR_DFS_NO_SUCH_SERVER },
+ { "WERR_DFS_INTERNAL_ERROR", WERR_DFS_INTERNAL_ERROR },
+ { "WERR_DFS_CANT_CREATE_JUNCT", WERR_DFS_CANT_CREATE_JUNCT },
+ { "WERR_INVALID_SECURITY_DESCRIPTOR", WERR_INVALID_SECURITY_DESCRIPTOR },
+ { "WERR_INVALID_OWNER", WERR_INVALID_OWNER },
+ { "WERR_SERVER_UNAVAILABLE", WERR_SERVER_UNAVAILABLE },
+ { NULL, W_ERROR(0) }
+};
+
+/*****************************************************************************
+ returns a DOS error message. not amazingly helpful, but better than a number.
+ *****************************************************************************/
+const char *dos_errstr(WERROR werror)
+{
+ static pstring msg;
+ int idx = 0;
+
+ slprintf(msg, sizeof(msg), "DOS code 0x%08x", W_ERROR_V(werror));
+
+ while (dos_errs[idx].dos_errstr != NULL) {
+ if (W_ERROR_V(dos_errs[idx].werror) ==
+ W_ERROR_V(werror))
+ return dos_errs[idx].dos_errstr;
+ idx++;
+ }
+
+ return msg;
+}
diff --git a/source/libsmb/errormap.c b/source/libsmb/errormap.c
new file mode 100644
index 00000000000..aeb68b6596c
--- /dev/null
+++ b/source/libsmb/errormap.c
@@ -0,0 +1,1548 @@
+/*
+ * 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 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"
+
+/* 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 dos_class;
+ uint32 dos_code;
+ NTSTATUS ntstatus;
+} ntstatus_to_dos_map[] = {
+ {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},
+/*
+ * Not an official error, as only bit 0x80000000, not bits 0xC0000000 are set.
+ */
+ {ERRDOS, ERRmoredata, STATUS_BUFFER_OVERFLOW},
+ {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, 183, 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)},
+};
+
+
+/* dos -> nt status error map */
+static const struct {
+ uint8 dos_class;
+ uint32 dos_code;
+ NTSTATUS ntstatus;
+} dos_to_ntstatus_map[] = {
+ {ERRDOS, ERRbadfunc, NT_STATUS_NOT_IMPLEMENTED},
+ {ERRDOS, ERRbadfile, NT_STATUS_NO_SUCH_FILE},
+ {ERRDOS, ERRbadpath, NT_STATUS_OBJECT_PATH_NOT_FOUND},
+ {ERRDOS, ERRnofids, NT_STATUS_TOO_MANY_OPENED_FILES},
+ {ERRDOS, ERRnoaccess, NT_STATUS_ACCESS_DENIED},
+ {ERRDOS, ERRbadfid, NT_STATUS_INVALID_HANDLE},
+ {ERRDOS, ERRnomem, NT_STATUS_INSUFFICIENT_RESOURCES},
+ {ERRDOS, ERRbadaccess, NT_STATUS_INVALID_LOCK_SEQUENCE},
+ {ERRDOS, ERRbaddata, NT_STATUS_DATA_ERROR},
+ {ERRDOS, 14, NT_STATUS_SECTION_NOT_EXTENDED},
+ {ERRDOS, ERRremcd, NT_STATUS_DIRECTORY_NOT_EMPTY},
+ {ERRDOS, ERRdiffdevice, NT_STATUS_NOT_SAME_DEVICE},
+ {ERRDOS, ERRnofiles, NT_STATUS(0x80000006)},
+ {ERRDOS, 19, NT_STATUS_MEDIA_WRITE_PROTECTED},
+ {ERRDOS, 21, NT_STATUS_NO_MEDIA_IN_DEVICE},
+ {ERRDOS, 22, NT_STATUS_INVALID_DEVICE_STATE},
+ {ERRDOS, 23, NT_STATUS_DATA_ERROR},
+ {ERRDOS, 24, NT_STATUS_DATA_ERROR},
+ {ERRDOS, 26, NT_STATUS_DISK_CORRUPT_ERROR},
+ {ERRDOS, 27, NT_STATUS_NONEXISTENT_SECTOR},
+ {ERRDOS, 28, NT_STATUS(0x8000000e)},
+ {ERRDOS, 31, NT_STATUS_UNSUCCESSFUL},
+ {ERRDOS, ERRbadshare, NT_STATUS_SHARING_VIOLATION},
+ {ERRDOS, ERRlock, NT_STATUS_FILE_LOCK_CONFLICT},
+ {ERRDOS, 34, NT_STATUS_WRONG_VOLUME},
+ {ERRDOS, 38, NT_STATUS_END_OF_FILE},
+ {ERRDOS, ERRunsup, NT_STATUS_CTL_FILE_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_GUIDS_REQUESTED},
+ {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, ERRfilexists, NT_STATUS_OBJECT_NAME_COLLISION},
+ {ERRDOS, 86, NT_STATUS_WRONG_PASSWORD},
+ {ERRDOS, 87, NT_STATUS_INVALID_INFO_CLASS},
+ {ERRDOS, 88, NT_STATUS_NET_WRITE_FAULT},
+ {ERRDOS, 109, NT_STATUS_PIPE_BROKEN},
+ {ERRDOS, 111, STATUS_MORE_ENTRIES},
+ {ERRDOS, 112, NT_STATUS_DISK_FULL},
+ {ERRDOS, 121, NT_STATUS_IO_TIMEOUT},
+ {ERRDOS, 122, NT_STATUS_BUFFER_TOO_SMALL},
+ {ERRDOS, ERRinvalidname, NT_STATUS_OBJECT_NAME_INVALID},
+ {ERRDOS, 124, NT_STATUS_INVALID_LEVEL},
+ {ERRDOS, 126, NT_STATUS_DLL_NOT_FOUND},
+ {ERRDOS, 127, NT_STATUS_PROCEDURE_NOT_FOUND},
+ {ERRDOS, 145, NT_STATUS_DIRECTORY_NOT_EMPTY},
+ {ERRDOS, 154, NT_STATUS_INVALID_VOLUME_LABEL},
+ {ERRDOS, 156, NT_STATUS_SUSPEND_COUNT_EXCEEDED},
+ {ERRDOS, 158, NT_STATUS_NOT_LOCKED},
+ {ERRDOS, 161, NT_STATUS_OBJECT_PATH_INVALID},
+ {ERRDOS, 170, NT_STATUS(0x80000011)},
+ {ERRDOS, 182, NT_STATUS_ORDINAL_NOT_FOUND},
+ {ERRDOS, 183, NT_STATUS_OBJECT_NAME_COLLISION},
+ {ERRDOS, 193, NT_STATUS_BAD_INITIAL_PC},
+ {ERRDOS, 203, NT_STATUS(0xc0000100)},
+ {ERRDOS, 206, NT_STATUS_NAME_TOO_LONG},
+ {ERRDOS, ERRbadpipe, NT_STATUS_INVALID_INFO_CLASS},
+ {ERRDOS, ERRpipebusy, NT_STATUS_INSTANCE_NOT_AVAILABLE},
+ {ERRDOS, ERRpipeclosing, NT_STATUS_PIPE_CLOSING},
+ {ERRDOS, ERRnotconnected, NT_STATUS_PIPE_DISCONNECTED},
+ {ERRDOS, ERRmoredata, NT_STATUS_MORE_PROCESSING_REQUIRED},
+ {ERRDOS, 240, NT_STATUS_VIRTUAL_CIRCUIT_CLOSED},
+ {ERRDOS, 254, NT_STATUS(0x80000013)},
+ {ERRDOS, 255, NT_STATUS_EA_TOO_LARGE},
+ {ERRDOS, 259, NT_STATUS_GUIDS_EXHAUSTED},
+ {ERRDOS, 267, NT_STATUS_NOT_A_DIRECTORY},
+ {ERRDOS, 275, NT_STATUS_EA_TOO_LARGE},
+ {ERRDOS, 276, NT_STATUS_NONEXISTENT_EA_ENTRY},
+ {ERRDOS, 277, NT_STATUS_NONEXISTENT_EA_ENTRY},
+ {ERRDOS, 278, NT_STATUS_NONEXISTENT_EA_ENTRY},
+ {ERRDOS, 282, NT_STATUS_EAS_NOT_SUPPORTED},
+ {ERRDOS, 288, NT_STATUS_MUTANT_NOT_OWNED},
+ {ERRDOS, 298, NT_STATUS_SEMAPHORE_LIMIT_EXCEEDED},
+ {ERRDOS, 299, NT_STATUS(0x8000000d)},
+ {ERRDOS, 300, NT_STATUS_OPLOCK_NOT_GRANTED},
+ {ERRDOS, 301, NT_STATUS_INVALID_OPLOCK_PROTOCOL},
+ {ERRDOS, 487, NT_STATUS_CONFLICTING_ADDRESSES},
+ {ERRDOS, 534, NT_STATUS_INTEGER_OVERFLOW},
+ {ERRDOS, 535, NT_STATUS_PIPE_CONNECTED},
+ {ERRDOS, 536, NT_STATUS_PIPE_LISTENING},
+ {ERRDOS, 995, NT_STATUS_CANCELLED},
+ {ERRDOS, 997, NT_STATUS(0x00000103)},
+ {ERRDOS, 998, NT_STATUS_ACCESS_VIOLATION},
+ {ERRDOS, 999, NT_STATUS_IN_PAGE_ERROR},
+ {ERRDOS, 1001, NT_STATUS_BAD_INITIAL_STACK},
+ {ERRDOS, 1005, NT_STATUS_UNRECOGNIZED_VOLUME},
+ {ERRDOS, 1006, NT_STATUS_FILE_INVALID},
+ {ERRDOS, 1007, NT_STATUS_FULLSCREEN_MODE},
+ {ERRDOS, 1008, NT_STATUS_NO_TOKEN},
+ {ERRDOS, 1009, NT_STATUS_REGISTRY_CORRUPT},
+ {ERRDOS, 1016, NT_STATUS_REGISTRY_IO_FAILED},
+ {ERRDOS, 1017, NT_STATUS_NOT_REGISTRY_FILE},
+ {ERRDOS, 1018, NT_STATUS_KEY_DELETED},
+ {ERRDOS, 1019, NT_STATUS_NO_LOG_SPACE},
+ {ERRDOS, 1020, NT_STATUS_KEY_HAS_CHILDREN},
+ {ERRDOS, 1021, NT_STATUS_CHILD_MUST_BE_VOLATILE},
+ {ERRDOS, 1022, NT_STATUS(0x0000010c)},
+ {ERRSRV, ERRbadpw, NT_STATUS_WRONG_PASSWORD},
+ {ERRSRV, ERRbadtype, NT_STATUS_BAD_DEVICE_TYPE},
+ {ERRSRV, ERRaccess, NT_STATUS_NETWORK_ACCESS_DENIED},
+ {ERRSRV, ERRinvnid, NT_STATUS_NETWORK_NAME_DELETED},
+ {ERRSRV, ERRinvnetname, NT_STATUS_BAD_NETWORK_NAME},
+ {ERRSRV, ERRinvdevice, NT_STATUS_BAD_DEVICE_TYPE},
+ {ERRSRV, ERRqfull, NT_STATUS_PRINT_QUEUE_FULL},
+ {ERRSRV, ERRqtoobig, NT_STATUS_NO_SPOOL_SPACE},
+ {ERRSRV, ERRinvpfid, NT_STATUS_PRINT_CANCELLED},
+ {ERRSRV, ERRsmbcmd, NT_STATUS_NOT_IMPLEMENTED},
+ {ERRSRV, ERRbadpermits, NT_STATUS_NETWORK_ACCESS_DENIED},
+ {ERRSRV, ERRpaused, NT_STATUS_SHARING_PAUSED},
+ {ERRSRV, ERRmsgoff, NT_STATUS_REQUEST_NOT_ACCEPTED},
+ {ERRSRV, ERRnoroom, NT_STATUS_DISK_FULL},
+ {ERRSRV, ERRnoresource, NT_STATUS_REQUEST_NOT_ACCEPTED},
+ {ERRSRV, ERRtoomanyuids, NT_STATUS_TOO_MANY_SESSIONS},
+ {ERRSRV, 123, NT_STATUS_OBJECT_NAME_INVALID},
+ {ERRSRV, 206, NT_STATUS_OBJECT_NAME_INVALID},
+ {ERRHRD, 1, NT_STATUS_NOT_IMPLEMENTED},
+ {ERRHRD, 2, NT_STATUS_NO_SUCH_DEVICE},
+ {ERRHRD, 3, NT_STATUS_OBJECT_PATH_NOT_FOUND},
+ {ERRHRD, 4, NT_STATUS_TOO_MANY_OPENED_FILES},
+ {ERRHRD, 5, NT_STATUS_INVALID_LOCK_SEQUENCE},
+ {ERRHRD, 6, NT_STATUS_INVALID_HANDLE},
+ {ERRHRD, 8, NT_STATUS_INSUFFICIENT_RESOURCES},
+ {ERRHRD, 12, NT_STATUS_INVALID_LOCK_SEQUENCE},
+ {ERRHRD, 13, NT_STATUS_DATA_ERROR},
+ {ERRHRD, 14, NT_STATUS_SECTION_NOT_EXTENDED},
+ {ERRHRD, 16, NT_STATUS_DIRECTORY_NOT_EMPTY},
+ {ERRHRD, 17, NT_STATUS_NOT_SAME_DEVICE},
+ {ERRHRD, 18, NT_STATUS(0x80000006)},
+ {ERRHRD, ERRnowrite, NT_STATUS_MEDIA_WRITE_PROTECTED},
+ {ERRHRD, ERRnotready, NT_STATUS_NO_MEDIA_IN_DEVICE},
+ {ERRHRD, ERRbadcmd, NT_STATUS_INVALID_DEVICE_STATE},
+ {ERRHRD, ERRdata, NT_STATUS_DATA_ERROR},
+ {ERRHRD, ERRbadreq, NT_STATUS_DATA_ERROR},
+ {ERRHRD, ERRbadmedia, NT_STATUS_DISK_CORRUPT_ERROR},
+ {ERRHRD, ERRbadsector, NT_STATUS_NONEXISTENT_SECTOR},
+ {ERRHRD, ERRnopaper, NT_STATUS(0x8000000e)},
+ {ERRHRD, ERRgeneral, NT_STATUS_UNSUCCESSFUL},
+ {ERRHRD, ERRbadshare, NT_STATUS_SHARING_VIOLATION},
+ {ERRHRD, ERRlock, NT_STATUS_FILE_LOCK_CONFLICT},
+ {ERRHRD, ERRwrongdisk, NT_STATUS_WRONG_VOLUME},
+ {ERRHRD, 38, NT_STATUS_END_OF_FILE},
+ {ERRHRD, ERRdiskfull, NT_STATUS_DISK_FULL},
+ {ERRHRD, 50, NT_STATUS_CTL_FILE_NOT_SUPPORTED},
+ {ERRHRD, 51, NT_STATUS_REMOTE_NOT_LISTENING},
+ {ERRHRD, 52, NT_STATUS_DUPLICATE_NAME},
+ {ERRHRD, 53, NT_STATUS_BAD_NETWORK_PATH},
+ {ERRHRD, 54, NT_STATUS_NETWORK_BUSY},
+ {ERRHRD, 55, NT_STATUS_DEVICE_DOES_NOT_EXIST},
+ {ERRHRD, 56, NT_STATUS_TOO_MANY_COMMANDS},
+ {ERRHRD, 57, NT_STATUS_ADAPTER_HARDWARE_ERROR},
+ {ERRHRD, 58, NT_STATUS_INVALID_NETWORK_RESPONSE},
+ {ERRHRD, 59, NT_STATUS_UNEXPECTED_NETWORK_ERROR},
+ {ERRHRD, 60, NT_STATUS_BAD_REMOTE_ADAPTER},
+ {ERRHRD, 61, NT_STATUS_PRINT_QUEUE_FULL},
+ {ERRHRD, 62, NT_STATUS_NO_SPOOL_SPACE},
+ {ERRHRD, 63, NT_STATUS_PRINT_CANCELLED},
+ {ERRHRD, 64, NT_STATUS_NETWORK_NAME_DELETED},
+ {ERRHRD, 65, NT_STATUS_NETWORK_ACCESS_DENIED},
+ {ERRHRD, 66, NT_STATUS_BAD_DEVICE_TYPE},
+ {ERRHRD, 67, NT_STATUS_BAD_NETWORK_NAME},
+ {ERRHRD, 68, NT_STATUS_TOO_MANY_GUIDS_REQUESTED},
+ {ERRHRD, 69, NT_STATUS_TOO_MANY_SESSIONS},
+ {ERRHRD, 70, NT_STATUS_SHARING_PAUSED},
+ {ERRHRD, 71, NT_STATUS_REQUEST_NOT_ACCEPTED},
+ {ERRHRD, 72, NT_STATUS_REDIRECTOR_PAUSED},
+ {ERRHRD, 80, NT_STATUS_OBJECT_NAME_COLLISION},
+ {ERRHRD, 86, NT_STATUS_WRONG_PASSWORD},
+ {ERRHRD, 87, NT_STATUS_INVALID_INFO_CLASS},
+ {ERRHRD, 88, NT_STATUS_NET_WRITE_FAULT},
+ {ERRHRD, 109, NT_STATUS_PIPE_BROKEN},
+ {ERRHRD, 111, STATUS_MORE_ENTRIES},
+ {ERRHRD, 112, NT_STATUS_DISK_FULL},
+ {ERRHRD, 121, NT_STATUS_IO_TIMEOUT},
+ {ERRHRD, 122, NT_STATUS_BUFFER_TOO_SMALL},
+ {ERRHRD, 123, NT_STATUS_OBJECT_NAME_INVALID},
+ {ERRHRD, 124, NT_STATUS_INVALID_LEVEL},
+ {ERRHRD, 126, NT_STATUS_DLL_NOT_FOUND},
+ {ERRHRD, 127, NT_STATUS_PROCEDURE_NOT_FOUND},
+ {ERRHRD, 145, NT_STATUS_DIRECTORY_NOT_EMPTY},
+ {ERRHRD, 154, NT_STATUS_INVALID_VOLUME_LABEL},
+ {ERRHRD, 156, NT_STATUS_SUSPEND_COUNT_EXCEEDED},
+ {ERRHRD, 158, NT_STATUS_NOT_LOCKED},
+ {ERRHRD, 161, NT_STATUS_OBJECT_PATH_INVALID},
+ {ERRHRD, 170, NT_STATUS(0x80000011)},
+ {ERRHRD, 182, NT_STATUS_ORDINAL_NOT_FOUND},
+ {ERRHRD, 183, NT_STATUS_OBJECT_NAME_COLLISION},
+ {ERRHRD, 193, NT_STATUS_BAD_INITIAL_PC},
+ {ERRHRD, 203, NT_STATUS(0xc0000100)},
+ {ERRHRD, 206, NT_STATUS_NAME_TOO_LONG},
+ {ERRHRD, 230, NT_STATUS_INVALID_INFO_CLASS},
+ {ERRHRD, 231, NT_STATUS_INSTANCE_NOT_AVAILABLE},
+ {ERRHRD, 232, NT_STATUS_PIPE_CLOSING},
+ {ERRHRD, 233, NT_STATUS_PIPE_DISCONNECTED},
+ {ERRHRD, 234, STATUS_MORE_ENTRIES},
+ {ERRHRD, 240, NT_STATUS_VIRTUAL_CIRCUIT_CLOSED},
+ {ERRHRD, 254, NT_STATUS(0x80000013)},
+ {ERRHRD, 255, NT_STATUS_EA_TOO_LARGE},
+ {ERRHRD, 259, NT_STATUS_GUIDS_EXHAUSTED},
+ {ERRHRD, 267, NT_STATUS_NOT_A_DIRECTORY},
+ {ERRHRD, 275, NT_STATUS_EA_TOO_LARGE},
+ {ERRHRD, 276, NT_STATUS_NONEXISTENT_EA_ENTRY},
+ {ERRHRD, 277, NT_STATUS_NONEXISTENT_EA_ENTRY},
+ {ERRHRD, 278, NT_STATUS_NONEXISTENT_EA_ENTRY},
+ {ERRHRD, 282, NT_STATUS_EAS_NOT_SUPPORTED},
+ {ERRHRD, 288, NT_STATUS_MUTANT_NOT_OWNED},
+ {ERRHRD, 298, NT_STATUS_SEMAPHORE_LIMIT_EXCEEDED},
+ {ERRHRD, 299, NT_STATUS(0x8000000d)},
+ {ERRHRD, 300, NT_STATUS_OPLOCK_NOT_GRANTED},
+ {ERRHRD, 301, NT_STATUS_INVALID_OPLOCK_PROTOCOL},
+ {ERRHRD, 487, NT_STATUS_CONFLICTING_ADDRESSES},
+ {ERRHRD, 534, NT_STATUS_INTEGER_OVERFLOW},
+ {ERRHRD, 535, NT_STATUS_PIPE_CONNECTED},
+ {ERRHRD, 536, NT_STATUS_PIPE_LISTENING},
+ {ERRHRD, 995, NT_STATUS_CANCELLED},
+ {ERRHRD, 997, NT_STATUS(0x00000103)},
+ {ERRHRD, 998, NT_STATUS_ACCESS_VIOLATION},
+ {ERRHRD, 999, NT_STATUS_IN_PAGE_ERROR},
+ {ERRHRD, 1001, NT_STATUS_BAD_INITIAL_STACK},
+ {ERRHRD, 1005, NT_STATUS_UNRECOGNIZED_VOLUME},
+ {ERRHRD, 1006, NT_STATUS_FILE_INVALID},
+ {ERRHRD, 1007, NT_STATUS_FULLSCREEN_MODE},
+ {ERRHRD, 1008, NT_STATUS_NO_TOKEN},
+ {ERRHRD, 1009, NT_STATUS_REGISTRY_CORRUPT},
+ {ERRHRD, 1016, NT_STATUS_REGISTRY_IO_FAILED},
+ {ERRHRD, 1017, NT_STATUS_NOT_REGISTRY_FILE},
+ {ERRHRD, 1018, NT_STATUS_KEY_DELETED},
+ {ERRHRD, 1019, NT_STATUS_NO_LOG_SPACE},
+ {ERRHRD, 1020, NT_STATUS_KEY_HAS_CHILDREN},
+ {ERRHRD, 1021, NT_STATUS_CHILD_MUST_BE_VOLATILE},
+ {ERRHRD, 1022, NT_STATUS(0x0000010c)},
+};
+
+/* errmap NTSTATUS->Win32 */
+static const struct {
+ NTSTATUS ntstatus;
+ WERROR werror;
+} ntstatus_to_werror_map[] = {
+ {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}};
+
+
+/*****************************************************************************
+convert a dos eclas/ecode to a NT status32 code
+ *****************************************************************************/
+NTSTATUS dos_to_ntstatus(uint8 eclass, uint32 ecode)
+{
+ int i;
+ if (eclass == 0 && ecode == 0) return NT_STATUS_OK;
+ for (i=0; NT_STATUS_V(dos_to_ntstatus_map[i].ntstatus); i++) {
+ if (eclass == dos_to_ntstatus_map[i].dos_class &&
+ ecode == dos_to_ntstatus_map[i].dos_code) {
+ return dos_to_ntstatus_map[i].ntstatus;
+ }
+ }
+ return NT_STATUS_UNSUCCESSFUL;
+}
+
+
+/*****************************************************************************
+convert a NT status code to a dos class/code
+ *****************************************************************************/
+void ntstatus_to_dos(NTSTATUS ntstatus, uint8 *eclass, uint32 *ecode)
+{
+ int i;
+ if (NT_STATUS_IS_OK(ntstatus)) {
+ *eclass = 0;
+ *ecode = 0;
+ 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 */
+
+const struct unix_error_map unix_dos_nt_errmap[] = {
+ { EPERM, ERRDOS, ERRnoaccess, NT_STATUS_ACCESS_DENIED },
+ { EACCES, ERRDOS, ERRnoaccess, NT_STATUS_ACCESS_DENIED },
+ { ENOENT, ERRDOS, ERRbadfile, NT_STATUS_OBJECT_NAME_NOT_FOUND },
+ { ENOTDIR, ERRDOS, ERRbadpath, NT_STATUS_NOT_A_DIRECTORY },
+ { EIO, ERRHRD, ERRgeneral, NT_STATUS_IO_DEVICE_ERROR },
+ { EBADF, ERRSRV, ERRsrverror, NT_STATUS_INVALID_HANDLE },
+ { EINVAL, ERRSRV, ERRsrverror, NT_STATUS_INVALID_HANDLE },
+ { EEXIST, ERRDOS, ERRfilexists, NT_STATUS_OBJECT_NAME_COLLISION},
+ { ENFILE, ERRDOS, ERRnofids, NT_STATUS_TOO_MANY_OPENED_FILES },
+ { EMFILE, ERRDOS, ERRnofids, NT_STATUS_TOO_MANY_OPENED_FILES },
+ { ENOSPC, ERRHRD, ERRdiskfull, NT_STATUS_DISK_FULL },
+ { ENOMEM, ERRDOS, ERRnomem, NT_STATUS_NO_MEMORY },
+ { EISDIR, ERRDOS, ERRnoaccess, NT_STATUS_FILE_IS_A_DIRECTORY},
+#ifdef EDQUOT
+ { EDQUOT, ERRHRD, ERRdiskfull, NT_STATUS_DISK_FULL },
+#endif
+#ifdef ENOTEMPTY
+ { ENOTEMPTY, ERRDOS, ERRnoaccess, NT_STATUS_DIRECTORY_NOT_EMPTY },
+#endif
+#ifdef EXDEV
+ { EXDEV, ERRDOS, ERRdiffdevice, NT_STATUS_NOT_SAME_DEVICE },
+#endif
+#ifdef EROFS
+ { EROFS, ERRHRD, ERRnowrite, NT_STATUS_ACCESS_DENIED },
+#endif
+#ifdef ENAMETOOLONG
+ { ENAMETOOLONG, ERRDOS, 206, NT_STATUS_OBJECT_NAME_INVALID },
+#endif
+#ifdef EFBIG
+ { EFBIG, ERRHRD, ERRdiskfull, NT_STATUS_DISK_FULL },
+#endif
+ { 0, 0, 0, NT_STATUS_OK }
+};
+
+/*********************************************************************
+ Map an NT error code from a Unix error code.
+*********************************************************************/
+
+NTSTATUS map_nt_error_from_unix(int unix_error)
+{
+ int i = 0;
+
+ if (unix_error == 0)
+ return NT_STATUS_OK;
+
+ /* Look through list */
+ while(unix_dos_nt_errmap[i].unix_error != 0) {
+ if (unix_dos_nt_errmap[i].unix_error == unix_error)
+ return unix_dos_nt_errmap[i].nt_error;
+ i++;
+ }
+
+ /* Default return */
+ return NT_STATUS_ACCESS_DENIED;
+}
diff --git a/source/libsmb/libsmb_cache.c b/source/libsmb/libsmb_cache.c
new file mode 100644
index 00000000000..cb40b4aaa6b
--- /dev/null
+++ b/source/libsmb/libsmb_cache.c
@@ -0,0 +1,196 @@
+/*
+ Unix SMB/CIFS implementation.
+ SMB client library implementation (server cache)
+ Copyright (C) Andrew Tridgell 1998
+ Copyright (C) Richard Sharpe 2000
+ Copyright (C) John Terpstra 2000
+ Copyright (C) Tom Jansen (Ninja ISD) 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 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"
+
+/*
+ * Define this to get the real SMBCFILE and SMBCSRV structures
+ */
+#define _SMBC_INTERNAL
+#include "../include/libsmbclient.h"
+
+/*
+ * Structure we use if internal caching mechanism is used
+ * nothing fancy here.
+ */
+struct smbc_server_cache {
+ char *server_name;
+ char *share_name;
+ char *workgroup;
+ char *username;
+ SMBCSRV *server;
+
+ struct smbc_server_cache *next, *prev;
+};
+
+
+
+/*
+ * Add a new connection to the server cache.
+ * This function is only used if the external cache is not enabled
+ */
+static int smbc_add_cached_server(SMBCCTX * context, SMBCSRV * new,
+ const char * server, const char * share,
+ const char * workgroup, const char * username)
+{
+ struct smbc_server_cache * srvcache = NULL;
+
+ if (!(srvcache = malloc(sizeof(*srvcache)))) {
+ errno = ENOMEM;
+ DEBUG(3, ("Not enough space for server cache allocation\n"));
+ return 1;
+ }
+
+ ZERO_STRUCTP(srvcache);
+
+ srvcache->server = new;
+
+ srvcache->server_name = strdup(server);
+ if (!srvcache->server_name) {
+ errno = ENOMEM;
+ goto failed;
+ }
+
+ srvcache->share_name = strdup(share);
+ if (!srvcache->share_name) {
+ errno = ENOMEM;
+ goto failed;
+ }
+
+ srvcache->workgroup = strdup(workgroup);
+ if (!srvcache->workgroup) {
+ errno = ENOMEM;
+ goto failed;
+ }
+
+ srvcache->username = strdup(username);
+ if (!srvcache->username) {
+ errno = ENOMEM;
+ goto failed;
+ }
+
+ DLIST_ADD((context->server_cache), srvcache);
+ return 0;
+
+ failed:
+ SAFE_FREE(srvcache->server_name);
+ SAFE_FREE(srvcache->share_name);
+ SAFE_FREE(srvcache->workgroup);
+ SAFE_FREE(srvcache->username);
+
+ return 1;
+}
+
+
+
+/*
+ * Search the server cache for a server
+ * returns server_fd on success, -1 on error (not found)
+ * This function is only used if the external cache is not enabled
+ */
+static SMBCSRV * smbc_get_cached_server(SMBCCTX * context, const char * server,
+ const char * share, const char * workgroup, const char * user)
+{
+ struct smbc_server_cache * srv = NULL;
+
+ /* Search the cache lines */
+ for (srv=((struct smbc_server_cache *)context->server_cache);srv;srv=srv->next) {
+ if (strcmp(server,srv->server_name) == 0 &&
+ strcmp(share,srv->share_name) == 0 &&
+ strcmp(workgroup,srv->workgroup) == 0 &&
+ strcmp(user, srv->username) == 0)
+ return srv->server;
+ }
+
+ return NULL;
+}
+
+
+/*
+ * Search the server cache for a server and remove it
+ * returns 0 on success
+ * This function is only used if the external cache is not enabled
+ */
+static int smbc_remove_cached_server(SMBCCTX * context, SMBCSRV * server)
+{
+ struct smbc_server_cache * srv = NULL;
+
+ for (srv=((struct smbc_server_cache *)context->server_cache);srv;srv=srv->next) {
+ if (server == srv->server) {
+
+ /* remove this sucker */
+ DLIST_REMOVE(context->server_cache, srv);
+ SAFE_FREE(srv->server_name);
+ SAFE_FREE(srv->share_name);
+ SAFE_FREE(srv->workgroup);
+ SAFE_FREE(srv->username);
+ SAFE_FREE(srv);
+ return 0;
+ }
+ }
+ /* server not found */
+ return 1;
+}
+
+
+/*
+ * Try to remove all the servers in cache
+ * returns 1 on failure and 0 if all servers could be removed.
+ */
+static int smbc_purge_cached(SMBCCTX * context)
+{
+ struct smbc_server_cache * srv;
+ struct smbc_server_cache * next;
+ int could_not_purge_all = 0;
+
+ for (srv = ((struct smbc_server_cache *) context->server_cache),
+ next = (srv ? srv->next :NULL);
+ srv;
+ srv = next, next = (srv ? srv->next : NULL)) {
+
+ if (smbc_remove_unused_server(context, srv->server)) {
+ /* could not be removed */
+ could_not_purge_all = 1;
+ }
+ }
+ return could_not_purge_all;
+}
+
+
+
+/*
+ * This functions initializes all server-cache related functions
+ * to the default (internal) system.
+ *
+ * We use this to make the rest of the cache system static.
+ */
+
+int smbc_default_cache_functions(SMBCCTX * context)
+{
+ context->callbacks.add_cached_srv_fn = smbc_add_cached_server;
+ context->callbacks.get_cached_srv_fn = smbc_get_cached_server;
+ context->callbacks.remove_cached_srv_fn = smbc_remove_cached_server;
+ context->callbacks.purge_cached_fn = smbc_purge_cached;
+
+ return 0;
+}
diff --git a/source/libsmb/libsmb_compat.c b/source/libsmb/libsmb_compat.c
new file mode 100644
index 00000000000..cc23835ae3d
--- /dev/null
+++ b/source/libsmb/libsmb_compat.c
@@ -0,0 +1,434 @@
+/*
+ Unix SMB/CIFS implementation.
+ SMB client library implementation (Old interface compatibility)
+ Copyright (C) Andrew Tridgell 1998
+ Copyright (C) Richard Sharpe 2000
+ Copyright (C) John Terpstra 2000
+ Copyright (C) Tom Jansen (Ninja ISD) 2002
+ Copyright (C) Derrell Lipman 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 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 "../include/libsmb_internal.h"
+
+struct smbc_compat_fdlist {
+ SMBCFILE * file;
+ int fd;
+ struct smbc_compat_fdlist *next, *prev;
+};
+
+static SMBCCTX * statcont = NULL;
+static int smbc_compat_initialized = 0;
+static int smbc_compat_nextfd = 0;
+static struct smbc_compat_fdlist * smbc_compat_fd_in_use = NULL;
+static struct smbc_compat_fdlist * smbc_compat_fd_avail = NULL;
+
+/* Find an fd and return the SMBCFILE * or NULL on failure */
+static SMBCFILE * find_fd(int fd)
+{
+ struct smbc_compat_fdlist * f = smbc_compat_fd_in_use;
+ while (f) {
+ if (f->fd == fd)
+ return f->file;
+ f = f->next;
+ }
+ return NULL;
+}
+
+/* Add an fd, returns 0 on success, -1 on error with errno set */
+static int add_fd(SMBCFILE * file)
+{
+ struct smbc_compat_fdlist * f = smbc_compat_fd_avail;
+
+ if (f) {
+ /* We found one that's available */
+ DLIST_REMOVE(smbc_compat_fd_avail, f);
+
+ } else {
+ /*
+ * None were available, so allocate one. Keep the number of
+ * file descriptors determinate. This allows the application
+ * to allocate bitmaps or mapping of file descriptors based on
+ * a known maximum number of file descriptors that will ever
+ * be returned.
+ */
+ if (smbc_compat_nextfd >= FD_SETSIZE) {
+ errno = EMFILE;
+ return -1;
+ }
+
+ f = malloc(sizeof(struct smbc_compat_fdlist));
+ if (!f) {
+ errno = ENOMEM;
+ return -1;
+ }
+
+ f->fd = SMBC_BASE_FD + smbc_compat_nextfd++;
+ }
+
+ f->file = file;
+ DLIST_ADD(smbc_compat_fd_in_use, f);
+
+ return f->fd;
+}
+
+
+
+/* Delete an fd, returns 0 on success */
+static int del_fd(int fd)
+{
+ struct smbc_compat_fdlist * f = smbc_compat_fd_in_use;
+
+ while (f) {
+ if (f->fd == fd)
+ break;
+ f = f->next;
+ }
+
+ if (f) {
+ /* found */
+ DLIST_REMOVE(smbc_compat_fd_in_use, f);
+ f->file = NULL;
+ DLIST_ADD(smbc_compat_fd_avail, f);
+ return 0;
+ }
+ return 1;
+}
+
+
+
+int smbc_init(smbc_get_auth_data_fn fn, int debug)
+{
+ if (!smbc_compat_initialized) {
+ statcont = smbc_new_context();
+ if (!statcont)
+ return -1;
+
+ statcont->debug = debug;
+ statcont->callbacks.auth_fn = fn;
+
+ if (!smbc_init_context(statcont)) {
+ smbc_free_context(statcont, False);
+ return -1;
+ }
+
+ smbc_compat_initialized = 1;
+
+ return 0;
+ }
+ return 0;
+}
+
+
+SMBCCTX *smbc_set_context(SMBCCTX * context)
+{
+ SMBCCTX *old_context = statcont;
+
+ if (context) {
+ /* Save provided context. It must have been initialized! */
+ statcont = context;
+
+ /* You'd better know what you're doing. We won't help you. */
+ smbc_compat_initialized = 1;
+ }
+
+ return old_context;
+}
+
+
+int smbc_open(const char *furl, int flags, mode_t mode)
+{
+ SMBCFILE * file;
+ int fd;
+
+ file = statcont->open(statcont, furl, flags, mode);
+ if (!file)
+ return -1;
+
+ fd = add_fd(file);
+ if (fd == -1)
+ statcont->close(statcont, file);
+ return fd;
+}
+
+
+int smbc_creat(const char *furl, mode_t mode)
+{
+ SMBCFILE * file;
+ int fd;
+
+ file = statcont->creat(statcont, furl, mode);
+ if (!file)
+ return -1;
+
+ fd = add_fd(file);
+ if (fd == -1) {
+ /* Hmm... should we delete the file too ? I guess we could try */
+ statcont->close(statcont, file);
+ statcont->unlink(statcont, furl);
+ }
+ return fd;
+}
+
+
+ssize_t smbc_read(int fd, void *buf, size_t bufsize)
+{
+ SMBCFILE * file = find_fd(fd);
+ return statcont->read(statcont, file, buf, bufsize);
+}
+
+ssize_t smbc_write(int fd, void *buf, size_t bufsize)
+{
+ SMBCFILE * file = find_fd(fd);
+ return statcont->write(statcont, file, buf, bufsize);
+}
+
+off_t smbc_lseek(int fd, off_t offset, int whence)
+{
+ SMBCFILE * file = find_fd(fd);
+ return statcont->lseek(statcont, file, offset, whence);
+}
+
+int smbc_close(int fd)
+{
+ SMBCFILE * file = find_fd(fd);
+ del_fd(fd);
+ return statcont->close(statcont, file);
+}
+
+int smbc_unlink(const char *fname)
+{
+ return statcont->unlink(statcont, fname);
+}
+
+int smbc_rename(const char *ourl, const char *nurl)
+{
+ return statcont->rename(statcont, ourl, statcont, nurl);
+}
+
+int smbc_opendir(const char *durl)
+{
+ SMBCFILE * file;
+ int fd;
+
+ file = statcont->opendir(statcont, durl);
+ if (!file)
+ return -1;
+
+ fd = add_fd(file);
+ if (fd == -1)
+ statcont->closedir(statcont, file);
+
+ return fd;
+}
+
+int smbc_closedir(int dh)
+{
+ SMBCFILE * file = find_fd(dh);
+ del_fd(dh);
+ return statcont->closedir(statcont, file);
+}
+
+int smbc_getdents(unsigned int dh, struct smbc_dirent *dirp, int count)
+{
+ SMBCFILE * file = find_fd(dh);
+ return statcont->getdents(statcont, file,dirp, count);
+}
+
+struct smbc_dirent* smbc_readdir(unsigned int dh)
+{
+ SMBCFILE * file = find_fd(dh);
+ return statcont->readdir(statcont, file);
+}
+
+off_t smbc_telldir(int dh)
+{
+ SMBCFILE * file = find_fd(dh);
+ return statcont->telldir(statcont, file);
+}
+
+int smbc_lseekdir(int fd, off_t offset)
+{
+ SMBCFILE * file = find_fd(fd);
+ return statcont->lseekdir(statcont, file, offset);
+}
+
+int smbc_mkdir(const char *durl, mode_t mode)
+{
+ return statcont->mkdir(statcont, durl, mode);
+}
+
+int smbc_rmdir(const char *durl)
+{
+ return statcont->rmdir(statcont, durl);
+}
+
+int smbc_stat(const char *url, struct stat *st)
+{
+ return statcont->stat(statcont, url, st);
+}
+
+int smbc_fstat(int fd, struct stat *st)
+{
+ SMBCFILE * file = find_fd(fd);
+ return statcont->fstat(statcont, file, st);
+}
+
+int smbc_chmod(const char *url, mode_t mode)
+{
+ return statcont->chmod(statcont, url, mode);
+}
+
+int smbc_utimes(const char *fname, struct timeval *tbuf)
+{
+ return statcont->utimes(statcont, fname, tbuf);
+}
+
+#ifdef HAVE_UTIME_H
+int smbc_utime(const char *fname, struct utimbuf *utbuf)
+{
+ struct timeval tv;
+
+ if (utbuf == NULL)
+ return statcont->utimes(statcont, fname, NULL);
+
+ tv.tv_sec = utbuf->modtime;
+ tv.tv_usec = 0;
+ return statcont->utimes(statcont, fname, &tv);
+}
+#endif
+
+int smbc_setxattr(const char *fname,
+ const char *name,
+ const void *value,
+ size_t size,
+ int flags)
+{
+ return statcont->setxattr(statcont, fname, name, value, size, flags);
+}
+
+int smbc_lsetxattr(const char *fname,
+ const char *name,
+ const void *value,
+ size_t size,
+ int flags)
+{
+ return statcont->setxattr(statcont, fname, name, value, size, flags);
+}
+
+int smbc_fsetxattr(int fd,
+ const char *name,
+ const void *value,
+ size_t size,
+ int flags)
+{
+ SMBCFILE * file = find_fd(fd);
+ return statcont->setxattr(statcont, file->fname,
+ name, value, size, flags);
+}
+
+int smbc_getxattr(const char *fname,
+ const char *name,
+ const void *value,
+ size_t size)
+{
+ return statcont->getxattr(statcont, fname, name, value, size);
+}
+
+int smbc_lgetxattr(const char *fname,
+ const char *name,
+ const void *value,
+ size_t size)
+{
+ return statcont->getxattr(statcont, fname, name, value, size);
+}
+
+int smbc_fgetxattr(int fd,
+ const char *name,
+ const void *value,
+ size_t size)
+{
+ SMBCFILE * file = find_fd(fd);
+ return statcont->getxattr(statcont, file->fname, name, value, size);
+}
+
+int smbc_removexattr(const char *fname,
+ const char *name)
+{
+ return statcont->removexattr(statcont, fname, name);
+}
+
+int smbc_lremovexattr(const char *fname,
+ const char *name)
+{
+ return statcont->removexattr(statcont, fname, name);
+}
+
+int smbc_fremovexattr(int fd,
+ const char *name)
+{
+ SMBCFILE * file = find_fd(fd);
+ return statcont->removexattr(statcont, file->fname, name);
+}
+
+int smbc_listxattr(const char *fname,
+ char *list,
+ size_t size)
+{
+ return statcont->listxattr(statcont, fname, list, size);
+}
+
+int smbc_llistxattr(const char *fname,
+ char *list,
+ size_t size)
+{
+ return statcont->listxattr(statcont, fname, list, size);
+}
+
+int smbc_flistxattr(int fd,
+ char *list,
+ size_t size)
+{
+ SMBCFILE * file = find_fd(fd);
+ return statcont->listxattr(statcont, file->fname, list, size);
+}
+
+int smbc_print_file(const char *fname, const char *printq)
+{
+ return statcont->print_file(statcont, fname, statcont, printq);
+}
+
+int smbc_open_print_job(const char *fname)
+{
+ SMBCFILE * file = statcont->open_print_job(statcont, fname);
+ if (!file) return -1;
+ return (int) file;
+}
+
+int smbc_list_print_jobs(const char *purl, smbc_list_print_job_fn fn)
+{
+ return statcont->list_print_jobs(statcont, purl, fn);
+}
+
+int smbc_unlink_print_job(const char *purl, int id)
+{
+ return statcont->unlink_print_job(statcont, purl, id);
+}
+
+
diff --git a/source/libsmb/libsmbclient.c b/source/libsmb/libsmbclient.c
new file mode 100644
index 00000000000..68bb6661ebd
--- /dev/null
+++ b/source/libsmb/libsmbclient.c
@@ -0,0 +1,4534 @@
+/*
+ Unix SMB/Netbios implementation.
+ SMB client library implementation
+ Copyright (C) Andrew Tridgell 1998
+ Copyright (C) Richard Sharpe 2000, 2002
+ Copyright (C) John Terpstra 2000
+ Copyright (C) Tom Jansen (Ninja ISD) 2002
+ Copyright (C) Derrell Lipman 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 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 "../include/libsmb_internal.h"
+
+/*
+ * Internal flags for extended attributes
+ */
+
+/* internal mode values */
+#define SMBC_XATTR_MODE_ADD 1
+#define SMBC_XATTR_MODE_REMOVE 2
+#define SMBC_XATTR_MODE_REMOVE_ALL 3
+#define SMBC_XATTR_MODE_SET 4
+#define SMBC_XATTR_MODE_CHOWN 5
+#define SMBC_XATTR_MODE_CHGRP 6
+
+#define CREATE_ACCESS_READ READ_CONTROL_ACCESS
+
+/*We should test for this in configure ... */
+#ifndef ENOTSUP
+#define ENOTSUP EOPNOTSUPP
+#endif
+
+/*
+ * Functions exported by libsmb_cache.c that we need here
+ */
+int smbc_default_cache_functions(SMBCCTX *context);
+
+/*
+ * check if an element is part of the list.
+ * FIXME: Does not belong here !
+ * Can anyone put this in a macro in dlinklist.h ?
+ * -- Tom
+ */
+static int DLIST_CONTAINS(SMBCFILE * list, SMBCFILE *p) {
+ if (!p || !list) return False;
+ do {
+ if (p == list) return True;
+ list = list->next;
+ } while (list);
+ return False;
+}
+
+extern BOOL in_client;
+
+/*
+ * Is the logging working / configfile read ?
+ */
+static int smbc_initialized = 0;
+
+static int
+hex2int( unsigned int _char )
+{
+ if ( _char >= 'A' && _char <='F')
+ return _char - 'A' + 10;
+ if ( _char >= 'a' && _char <='f')
+ return _char - 'a' + 10;
+ if ( _char >= '0' && _char <='9')
+ return _char - '0';
+ return -1;
+}
+
+static void
+decode_urlpart(char *segment, size_t sizeof_segment)
+{
+ int old_length = strlen(segment);
+ int new_length = 0;
+ int new_length2 = 0;
+ int i = 0;
+ pstring new_segment;
+ char *new_usegment = 0;
+
+ if ( !old_length ) {
+ return;
+ }
+
+ /* make a copy of the old one */
+ new_usegment = (char*)malloc( old_length * 3 + 1 );
+
+ while( i < old_length ) {
+ int bReencode = False;
+ unsigned char character = segment[ i++ ];
+ if ((character <= ' ') || (character > 127))
+ bReencode = True;
+
+ new_usegment [ new_length2++ ] = character;
+ if (character == '%' ) {
+ int a = i+1 < old_length ? hex2int( segment[i] ) : -1;
+ int b = i+1 < old_length ? hex2int( segment[i+1] ) : -1;
+ if ((a == -1) || (b == -1)) { /* Only replace if sequence is valid */
+ /* Contains stray %, make sure to re-encode! */
+ bReencode = True;
+ } else {
+ /* Valid %xx sequence */
+ character = a * 16 + b; /* Replace with value of %dd */
+ if (!character)
+ break; /* Stop at %00 */
+
+ new_usegment [ new_length2++ ] = (unsigned char) segment[i++];
+ new_usegment [ new_length2++ ] = (unsigned char) segment[i++];
+ }
+ }
+ if (bReencode) {
+ unsigned int c = character / 16;
+ new_length2--;
+ new_usegment [ new_length2++ ] = '%';
+
+ c += (c > 9) ? ('A' - 10) : '0';
+ new_usegment[ new_length2++ ] = c;
+
+ c = character % 16;
+ c += (c > 9) ? ('A' - 10) : '0';
+ new_usegment[ new_length2++ ] = c;
+ }
+
+ new_segment [ new_length++ ] = character;
+ }
+ new_segment [ new_length ] = 0;
+
+ free(new_usegment);
+
+ /* realloc it with unix charset */
+ pull_utf8_allocate(&new_usegment, new_segment);
+
+ /* this assumes (very safely) that removing %aa sequences
+ only shortens the string */
+ strncpy(segment, new_usegment, sizeof_segment);
+
+ free(new_usegment);
+}
+
+/*
+ * Function to parse a path and turn it into components
+ *
+ * The general format of an SMB URI is explain in Christopher Hertel's CIFS
+ * book, at http://ubiqx.org/cifs/Appendix-D.html. We accept a subset of the
+ * general format ("smb:" only; we do not look for "cifs:"), and expand on
+ * what he calls "context", herein called "options" to avoid conflict with the
+ * SMBCCTX context used throughout this library. We add the "mb" keyword
+ * which applies as follows:
+ *
+ *
+ * We accept:
+ * smb://[[[domain;]user[:password@]]server[/share[/path[/file]]]][?options]
+ *
+ * Meaning of URLs:
+ *
+ * smb:// show all workgroups known by the first master browser found
+ * smb://?mb=.any same as smb:// (i.e. without any options)
+ *
+ * smb://?mb=.all show all workgroups known by every master browser found.
+ * Why might you want this? In an "appliance" application
+ * where the workgroup/domain being used on the local network
+ * is not known ahead of time, but where one wanted to
+ * provide network services via samba, a unique workgroup
+ * could be used. However, when the appliance is first
+ * started, the local samba instance's master browser has not
+ * synchronized with the other master browser(s) on the
+ * network (and might not synchronize for 12 minutes) and
+ * therefore is not aware of the workgroup/ domain names
+ * available on the network. This option may be used to
+ * overcome the problem of a libsmbclient application
+ * arbitrarily selecting the local (still ignorant) master
+ * browser to obtain its list of workgroups/domains and
+ * getting back a practically emmpty list. By requesting
+ * the list of workgroups/domains from each found master
+ * browser on the local network, a complete list of
+ * workgroups/domains can be built.
+ *
+ * smb://?mb=name NOT YET IMPLEMENTED -- show all workgroups known by the
+ * master browser whose name is "name"
+ *
+ * smb://name/ if name<1D> or name<1B> exists, list servers in
+ * workgroup, else, if name<20> exists, list all shares
+ * for server ...
+ *
+ * If "options" are provided, this function returns the entire option list as
+ * a string, for later parsing by the caller.
+ */
+
+static const char *smbc_prefix = "smb:";
+
+static int
+smbc_parse_path(SMBCCTX *context,
+ const char *fname,
+ char *server, int server_len,
+ char *share, int share_len,
+ char *path, int path_len,
+ char *user, int user_len,
+ char *password, int password_len,
+ char *options, int options_len)
+{
+ static pstring s;
+ pstring userinfo;
+ const char *p;
+ char *q, *r;
+ int len;
+
+ server[0] = share[0] = path[0] = user[0] = password[0] = (char)0;
+ if (options != NULL && options_len > 0) {
+ options[0] = (char)0;
+ }
+ pstrcpy(s, fname);
+
+ /* see if it has the right prefix */
+ len = strlen(smbc_prefix);
+ if (strncmp(s,smbc_prefix,len) || (s[len] != '/' && s[len] != 0)) {
+ return -1; /* What about no smb: ? */
+ }
+
+ p = s + len;
+
+ /* Watch the test below, we are testing to see if we should exit */
+
+ if (strncmp(p, "//", 2) && strncmp(p, "\\\\", 2)) {
+
+ DEBUG(1, ("Invalid path (does not begin with smb://"));
+ return -1;
+
+ }
+
+ p += 2; /* Skip the double slash */
+
+ /* See if any options were specified */
+ if (q = strrchr(p, '?')) {
+ /* There are options. Null terminate here and point to them */
+ *q++ = '\0';
+
+ DEBUG(4, ("Found options '%s'", q));
+
+ /* Copy the options */
+ if (options != NULL && options_len > 0) {
+ safe_strcpy(options, q, options_len - 1);
+ }
+ }
+
+ if (*p == (char)0)
+ goto decoding;
+
+ if (*p == '/') {
+
+ strncpy(server, context->workgroup,
+ (strlen(context->workgroup) < 16)?strlen(context->workgroup):16);
+ return 0;
+
+ }
+
+ /*
+ * ok, its for us. Now parse out the server, share etc.
+ *
+ * However, we want to parse out [[domain;]user[:password]@] if it
+ * exists ...
+ */
+
+ /* check that '@' occurs before '/', if '/' exists at all */
+ q = strchr_m(p, '@');
+ r = strchr_m(p, '/');
+ if (q && (!r || q < r)) {
+ pstring username, passwd, domain;
+ const char *u = userinfo;
+
+ next_token(&p, userinfo, "@", sizeof(fstring));
+
+ username[0] = passwd[0] = domain[0] = 0;
+
+ if (strchr_m(u, ';')) {
+
+ next_token(&u, domain, ";", sizeof(fstring));
+
+ }
+
+ if (strchr_m(u, ':')) {
+
+ next_token(&u, username, ":", sizeof(fstring));
+
+ pstrcpy(passwd, u);
+
+ }
+ else {
+
+ pstrcpy(username, u);
+
+ }
+
+ if (username[0])
+ strncpy(user, username, user_len); /* FIXME, domain */
+
+ if (passwd[0])
+ strncpy(password, passwd, password_len);
+
+ }
+
+ if (!next_token(&p, server, "/", sizeof(fstring))) {
+
+ return -1;
+
+ }
+
+ if (*p == (char)0) goto decoding; /* That's it ... */
+
+ if (!next_token(&p, share, "/", sizeof(fstring))) {
+
+ return -1;
+
+ }
+
+ safe_strcpy(path, p, path_len - 1);
+
+ all_string_sub(path, "/", "\\", 0);
+
+ decoding:
+ decode_urlpart(path, path_len);
+ decode_urlpart(server, server_len);
+ decode_urlpart(share, share_len);
+ decode_urlpart(user, user_len);
+ decode_urlpart(password, password_len);
+
+ return 0;
+}
+
+/*
+ * Verify that the options specified in a URL are valid
+ */
+static int smbc_check_options(char *server, char *share, char *path, char *options)
+{
+ DEBUG(4, ("smbc_check_options(): server='%s' share='%s' path='%s' options='%s'\n", server, share, path, options));
+
+ /* No options at all is always ok */
+ if (! *options) return 0;
+
+ /*
+ * For right now, we only support a very few options possibilities.
+ * No options are supported if server, share, or path are not empty.
+ * If all are empty, then we support the following two choices right
+ * now:
+ *
+ * mb=.any
+ * mb=.all
+ */
+ if ((*server || *share || *path) && *options) {
+ /* Invalid: options provided with server, share, or path */
+ DEBUG(1, ("Found unsupported options (%s) with non-empty server, share, or path\n", options));
+ return -1;
+ }
+
+ if (strcmp(options, "mb=.any") != 0 &&
+ strcmp(options, "mb=.all") != 0) {
+ DEBUG(1, ("Found unsupported options (%s)\n", options));
+ return -1;
+ }
+
+ return 0;
+}
+
+/*
+ * Convert an SMB error into a UNIX error ...
+ */
+static int smbc_errno(SMBCCTX *context, struct cli_state *c)
+{
+ int ret = cli_errno(c);
+
+ if (cli_is_dos_error(c)) {
+ uint8 eclass;
+ uint32 ecode;
+
+ cli_dos_error(c, &eclass, &ecode);
+
+ DEBUG(3,("smbc_error %d %d (0x%x) -> %d\n",
+ (int)eclass, (int)ecode, (int)ecode, ret));
+ } else {
+ NTSTATUS status;
+
+ status = cli_nt_error(c);
+
+ DEBUG(3,("smbc errno %s -> %d\n",
+ nt_errstr(status), ret));
+ }
+
+ return ret;
+}
+
+/*
+ * Check a server_fd.
+ * returns 0 if the server is in shape. Returns 1 on error
+ *
+ * Also useable outside libsmbclient to enable external cache
+ * to do some checks too.
+ */
+int smbc_check_server(SMBCCTX * context, SMBCSRV * server)
+{
+ if ( send_keepalive(server->cli.fd) == False )
+ return 1;
+
+ /* connection is ok */
+ return 0;
+}
+
+/*
+ * Remove a server from the cached server list it's unused.
+ * On success, 0 is returned. 1 is returned if the server could not be removed.
+ *
+ * Also useable outside libsmbclient
+ */
+int smbc_remove_unused_server(SMBCCTX * context, SMBCSRV * srv)
+{
+ SMBCFILE * file;
+
+ /* are we being fooled ? */
+ if (!context || !context->internal ||
+ !context->internal->_initialized || !srv) return 1;
+
+
+ /* Check all open files/directories for a relation with this server */
+ for (file = context->internal->_files; file; file=file->next) {
+ if (file->srv == srv) {
+ /* Still used */
+ DEBUG(3, ("smbc_remove_usused_server: %p still used by %p.\n",
+ srv, file));
+ return 1;
+ }
+ }
+
+ DLIST_REMOVE(context->internal->_servers, srv);
+
+ cli_shutdown(&srv->cli);
+
+ DEBUG(3, ("smbc_remove_usused_server: %p removed.\n", srv));
+
+ context->callbacks.remove_cached_srv_fn(context, srv);
+
+ SAFE_FREE(srv);
+
+ return 0;
+}
+
+SMBCSRV *find_server(SMBCCTX *context,
+ const char *server,
+ const char *share,
+ fstring workgroup,
+ fstring username,
+ fstring password)
+{
+ SMBCSRV *srv;
+ int auth_called = 0;
+
+ check_server_cache:
+
+ srv = context->callbacks.get_cached_srv_fn(context, server, share,
+ workgroup, username);
+
+ if (!auth_called && !srv && (!username[0] || !password[0])) {
+ context->callbacks.auth_fn(server, share,
+ workgroup, sizeof(fstring),
+ username, sizeof(fstring),
+ password, sizeof(fstring));
+ /*
+ * However, smbc_auth_fn may have picked up info relating to
+ * an existing connection, so try for an existing connection
+ * again ...
+ */
+ auth_called = 1;
+ goto check_server_cache;
+
+ }
+
+ if (srv) {
+ if (context->callbacks.check_server_fn(context, srv)) {
+ /*
+ * This server is no good anymore
+ * Try to remove it and check for more possible
+ * servers in the cache
+ */
+ if (context->callbacks.remove_unused_server_fn(context,
+ srv)) {
+ /*
+ * We could not remove the server completely,
+ * remove it from the cache so we will not get
+ * it again. It will be removed when the last
+ * file/dir is closed.
+ */
+ context->callbacks.remove_cached_srv_fn(context,
+ srv);
+ }
+
+ /*
+ * Maybe there are more cached connections to this
+ * server
+ */
+ goto check_server_cache;
+ }
+ return srv;
+ }
+
+ return NULL;
+}
+
+/*
+ * Connect to a server, possibly on an existing connection
+ *
+ * Here, what we want to do is: If the server and username
+ * match an existing connection, reuse that, otherwise, establish a
+ * new connection.
+ *
+ * If we have to create a new connection, call the auth_fn to get the
+ * info we need, unless the username and password were passed in.
+ */
+
+SMBCSRV *smbc_server(SMBCCTX *context,
+ const char *server, const char *share,
+ fstring workgroup, fstring username,
+ fstring password)
+{
+ SMBCSRV *srv=NULL;
+ struct cli_state c;
+ struct nmb_name called, calling;
+ char *p;
+ const char *server_n = server;
+ fstring group;
+ pstring ipenv;
+ struct in_addr ip;
+ int tried_reverse = 0;
+
+ zero_ip(&ip);
+ ZERO_STRUCT(c);
+
+ if (server[0] == 0) {
+ errno = EPERM;
+ return NULL;
+ }
+
+ srv = find_server(context, server, share,
+ workgroup, username, password);
+ if (srv)
+ return srv;
+
+ make_nmb_name(&calling, context->netbios_name, 0x0);
+ make_nmb_name(&called , server, 0x20);
+
+ DEBUG(4,("smbc_server: server_n=[%s] server=[%s]\n", server_n, server));
+
+#if 0 /* djl: obsolete code? neither group nor p is used beyond here */
+ if ((p=strchr_m(server_n,'#')) &&
+ (strcmp(p+1,"1D")==0 || strcmp(p+1,"01")==0)) {
+
+ fstrcpy(group, server_n);
+ p = strchr_m(group,'#');
+ *p = 0;
+
+ }
+#endif
+
+ DEBUG(4,(" -> server_n=[%s] server=[%s]\n", server_n, server));
+
+ again:
+ slprintf(ipenv,sizeof(ipenv)-1,"HOST_%s", server_n);
+
+ zero_ip(&ip);
+
+ /* have to open a new connection */
+ if (!cli_initialise(&c)) {
+ errno = ENOMEM;
+ return NULL;
+ }
+
+ c.timeout = context->timeout;
+
+ /* Force use of port 139 for first try, so browse lists can work */
+ c.port = 139;
+
+ if (!cli_connect(&c, server_n, &ip)) {
+ /*
+ * Port 139 connection failed. Try port 445 to handle
+ * connections to newer (e.g. XP) hosts with NetBIOS disabled.
+ */
+ c.port = 445;
+ if (!cli_connect(&c, server_n, &ip)) {
+ errno = ENETUNREACH;
+ return NULL;
+ }
+ }
+
+ if (!cli_session_request(&c, &calling, &called)) {
+ cli_shutdown(&c);
+ if (strcmp(called.name, "*SMBSERVER")) {
+ make_nmb_name(&called , "*SMBSERVER", 0x20);
+ goto again;
+ }
+ else { /* Try one more time, but ensure we don't loop */
+
+ /* Only try this if server is an IP address ... */
+
+ if (is_ipaddress(server) && !tried_reverse) {
+ fstring remote_name;
+ struct in_addr rem_ip;
+
+ if ((rem_ip.s_addr=inet_addr(server)) == INADDR_NONE) {
+ DEBUG(4, ("Could not convert IP address %s to struct in_addr\n", server));
+ errno = ENOENT;
+ return NULL;
+ }
+
+ tried_reverse++; /* Yuck */
+
+ if (name_status_find("*", 0, 0, rem_ip, remote_name)) {
+ make_nmb_name(&called, remote_name, 0x20);
+ goto again;
+ }
+
+
+ }
+ }
+ errno = ENOENT;
+ return NULL;
+ }
+
+ DEBUG(4,(" session request ok\n"));
+
+ if (!cli_negprot(&c)) {
+ cli_shutdown(&c);
+ errno = ENOENT;
+ return NULL;
+ }
+
+ if (!cli_session_setup(&c, username,
+ password, strlen(password),
+ password, strlen(password),
+ workgroup) &&
+ /* try an anonymous login if it failed */
+ !cli_session_setup(&c, "", "", 1,"", 0, workgroup)) {
+ cli_shutdown(&c);
+ errno = EPERM;
+ return NULL;
+ }
+
+ DEBUG(4,(" session setup ok\n"));
+
+ if (!cli_send_tconX(&c, share, "?????",
+ password, strlen(password)+1)) {
+ errno = smbc_errno(context, &c);
+ cli_shutdown(&c);
+ return NULL;
+ }
+
+ DEBUG(4,(" tconx ok\n"));
+
+ /*
+ * Ok, we have got a nice connection
+ * Let's find a free server_fd
+ */
+
+ srv = (SMBCSRV *)malloc(sizeof(*srv));
+ if (!srv) {
+ errno = ENOMEM;
+ goto failed;
+ }
+
+ ZERO_STRUCTP(srv);
+ srv->cli = c;
+ srv->dev = (dev_t)(str_checksum(server) ^ str_checksum(share));
+
+ /* now add it to the cache (internal or external) */
+ if (context->callbacks.add_cached_srv_fn(context, srv, server, share, workgroup, username)) {
+ DEBUG(3, (" Failed to add server to cache\n"));
+ goto failed;
+ }
+
+
+ DEBUG(2, ("Server connect ok: //%s/%s: %p\n",
+ server, share, srv));
+
+ DLIST_ADD(context->internal->_servers, srv);
+ return srv;
+
+ failed:
+ cli_shutdown(&c);
+ if (!srv) return NULL;
+
+ SAFE_FREE(srv);
+ return NULL;
+}
+
+/*
+ * Connect to a server for getting/setting attributes, possibly on an existing
+ * connection. This works similarly to smbc_server().
+ */
+SMBCSRV *smbc_attr_server(SMBCCTX *context,
+ const char *server, const char *share,
+ fstring workgroup,
+ fstring username, fstring password,
+ POLICY_HND *pol)
+{
+ struct in_addr ip;
+ struct cli_state *ipc_cli;
+ NTSTATUS nt_status;
+ SMBCSRV *ipc_srv=NULL;
+
+ /*
+ * See if we've already created this special connection. Reference
+ * our "special" share name 'IPC$$'.
+ */
+ ipc_srv = find_server(context, server, "IPC$$",
+ workgroup, username, password);
+ if (!ipc_srv) {
+
+ /* We didn't find a cached connection. Get the password */
+ if (*password == '\0') {
+ /* ... then retrieve it now. */
+ context->callbacks.auth_fn(server, share,
+ workgroup, sizeof(fstring),
+ username, sizeof(fstring),
+ password, sizeof(fstring));
+ }
+
+ zero_ip(&ip);
+ nt_status = cli_full_connection(&ipc_cli,
+ global_myname(), server,
+ &ip, 0, "IPC$", "?????",
+ username, workgroup,
+ password, 0,
+ Undefined, NULL);
+ if (! NT_STATUS_IS_OK(nt_status)) {
+ DEBUG(1,("cli_full_connection failed! (%s)\n",
+ nt_errstr(nt_status)));
+ errno = ENOTSUP;
+ return NULL;
+ }
+
+ if (!cli_nt_session_open(ipc_cli, PI_LSARPC)) {
+ DEBUG(1, ("cli_nt_session_open fail!\n"));
+ errno = ENOTSUP;
+ cli_shutdown(ipc_cli);
+ return NULL;
+ }
+
+ /* Some systems don't support SEC_RIGHTS_MAXIMUM_ALLOWED,
+ but NT sends 0x2000000 so we might as well do it too. */
+
+ nt_status = cli_lsa_open_policy(ipc_cli,
+ ipc_cli->mem_ctx,
+ True,
+ GENERIC_EXECUTE_ACCESS,
+ pol);
+
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ errno = smbc_errno(context, ipc_cli);
+ cli_shutdown(ipc_cli);
+ return NULL;
+ }
+
+ ipc_srv = (SMBCSRV *)malloc(sizeof(*ipc_srv));
+ if (!ipc_srv) {
+ errno = ENOMEM;
+ cli_shutdown(ipc_cli);
+ return NULL;
+ }
+
+ ZERO_STRUCTP(ipc_srv);
+ ipc_srv->cli = *ipc_cli;
+
+ free(ipc_cli);
+
+ /* now add it to the cache (internal or external) */
+
+ errno = 0; /* let cache function set errno if it likes */
+ if (context->callbacks.add_cached_srv_fn(context, ipc_srv,
+ server,
+ "IPC$$",
+ workgroup,
+ username)) {
+ DEBUG(3, (" Failed to add server to cache\n"));
+ if (errno == 0) {
+ errno = ENOMEM;
+ }
+ cli_shutdown(&ipc_srv->cli);
+ free(ipc_srv);
+ return NULL;
+ }
+
+ DLIST_ADD(context->internal->_servers, ipc_srv);
+ }
+
+ return ipc_srv;
+}
+
+/*
+ * Routine to open() a file ...
+ */
+
+static SMBCFILE *smbc_open_ctx(SMBCCTX *context, const char *fname, int flags, mode_t mode)
+{
+ fstring server, share, user, password, workgroup;
+ pstring path;
+ SMBCSRV *srv = NULL;
+ SMBCFILE *file = NULL;
+ int fd;
+
+ if (!context || !context->internal ||
+ !context->internal->_initialized) {
+
+ errno = EINVAL; /* Best I can think of ... */
+ return NULL;
+
+ }
+
+ if (!fname) {
+
+ errno = EINVAL;
+ return NULL;
+
+ }
+
+ if (smbc_parse_path(context, fname,
+ server, sizeof(server),
+ share, sizeof(share),
+ path, sizeof(path),
+ user, sizeof(user),
+ password, sizeof(password),
+ NULL, 0)) {
+ errno = EINVAL;
+ return NULL;
+ }
+
+ if (user[0] == (char)0) fstrcpy(user, context->user);
+
+ fstrcpy(workgroup, context->workgroup);
+
+ srv = smbc_server(context, server, share, workgroup, user, password);
+
+ if (!srv) {
+
+ if (errno == EPERM) errno = EACCES;
+ return NULL; /* smbc_server sets errno */
+
+ }
+
+ /* Hmmm, the test for a directory is suspect here ... FIXME */
+
+ if (strlen(path) > 0 && path[strlen(path) - 1] == '\\') {
+
+ fd = -1;
+
+ }
+ else {
+
+ file = malloc(sizeof(SMBCFILE));
+
+ if (!file) {
+
+ errno = ENOMEM;
+ return NULL;
+
+ }
+
+ ZERO_STRUCTP(file);
+
+ if ((fd = cli_open(&srv->cli, path, flags, DENY_NONE)) < 0) {
+
+ /* Handle the error ... */
+
+ SAFE_FREE(file);
+ errno = smbc_errno(context, &srv->cli);
+ return NULL;
+
+ }
+
+ /* Fill in file struct */
+
+ file->cli_fd = fd;
+ file->fname = strdup(fname);
+ file->srv = srv;
+ file->offset = 0;
+ file->file = True;
+
+ DLIST_ADD(context->internal->_files, file);
+ return file;
+
+ }
+
+ /* Check if opendir needed ... */
+
+ if (fd == -1) {
+ int eno = 0;
+
+ eno = smbc_errno(context, &srv->cli);
+ file = context->opendir(context, fname);
+ if (!file) errno = eno;
+ return file;
+
+ }
+
+ errno = EINVAL; /* FIXME, correct errno ? */
+ return NULL;
+
+}
+
+/*
+ * Routine to create a file
+ */
+
+static int creat_bits = O_WRONLY | O_CREAT | O_TRUNC; /* FIXME: Do we need this */
+
+static SMBCFILE *smbc_creat_ctx(SMBCCTX *context, const char *path, mode_t mode)
+{
+
+ if (!context || !context->internal ||
+ !context->internal->_initialized) {
+
+ errno = EINVAL;
+ return NULL;
+
+ }
+
+ return smbc_open_ctx(context, path, creat_bits, mode);
+}
+
+/*
+ * Routine to read() a file ...
+ */
+
+static ssize_t smbc_read_ctx(SMBCCTX *context, SMBCFILE *file, void *buf, size_t count)
+{
+ int ret;
+
+ if (!context || !context->internal ||
+ !context->internal->_initialized) {
+
+ errno = EINVAL;
+ return -1;
+
+ }
+
+ DEBUG(4, ("smbc_read(%p, %d)\n", file, (int)count));
+
+ if (!file || !DLIST_CONTAINS(context->internal->_files, file)) {
+
+ errno = EBADF;
+ return -1;
+
+ }
+
+ /* Check that the buffer exists ... */
+
+ if (buf == NULL) {
+
+ errno = EINVAL;
+ return -1;
+
+ }
+
+ ret = cli_read(&file->srv->cli, file->cli_fd, buf, file->offset, count);
+
+ if (ret < 0) {
+
+ errno = smbc_errno(context, &file->srv->cli);
+ return -1;
+
+ }
+
+ file->offset += ret;
+
+ DEBUG(4, (" --> %d\n", ret));
+
+ return ret; /* Success, ret bytes of data ... */
+
+}
+
+/*
+ * Routine to write() a file ...
+ */
+
+static ssize_t smbc_write_ctx(SMBCCTX *context, SMBCFILE *file, void *buf, size_t count)
+{
+ int ret;
+
+ if (!context || !context->internal ||
+ !context->internal->_initialized) {
+
+ errno = EINVAL;
+ return -1;
+
+ }
+
+ if (!file || !DLIST_CONTAINS(context->internal->_files, file)) {
+
+ errno = EBADF;
+ return -1;
+
+ }
+
+ /* Check that the buffer exists ... */
+
+ if (buf == NULL) {
+
+ errno = EINVAL;
+ return -1;
+
+ }
+
+ ret = cli_write(&file->srv->cli, file->cli_fd, 0, buf, file->offset, count);
+
+ if (ret <= 0) {
+
+ errno = smbc_errno(context, &file->srv->cli);
+ return -1;
+
+ }
+
+ file->offset += ret;
+
+ return ret; /* Success, 0 bytes of data ... */
+}
+
+/*
+ * Routine to close() a file ...
+ */
+
+static int smbc_close_ctx(SMBCCTX *context, SMBCFILE *file)
+{
+ SMBCSRV *srv;
+
+ if (!context || !context->internal ||
+ !context->internal->_initialized) {
+
+ errno = EINVAL;
+ return -1;
+
+ }
+
+ if (!file || !DLIST_CONTAINS(context->internal->_files, file)) {
+
+ errno = EBADF;
+ return -1;
+
+ }
+
+ /* IS a dir ... */
+ if (!file->file) {
+
+ return context->closedir(context, file);
+
+ }
+
+ if (!cli_close(&file->srv->cli, file->cli_fd)) {
+
+ DEBUG(3, ("cli_close failed on %s. purging server.\n",
+ file->fname));
+ /* Deallocate slot and remove the server
+ * from the server cache if unused */
+ errno = smbc_errno(context, &file->srv->cli);
+ srv = file->srv;
+ DLIST_REMOVE(context->internal->_files, file);
+ SAFE_FREE(file->fname);
+ SAFE_FREE(file);
+ context->callbacks.remove_unused_server_fn(context, srv);
+
+ return -1;
+
+ }
+
+ DLIST_REMOVE(context->internal->_files, file);
+ SAFE_FREE(file->fname);
+ SAFE_FREE(file);
+
+ return 0;
+}
+
+/*
+ * Get info from an SMB server on a file. Use a qpathinfo call first
+ * and if that fails, use getatr, as Win95 sometimes refuses qpathinfo
+ */
+static BOOL smbc_getatr(SMBCCTX * context, SMBCSRV *srv, char *path,
+ uint16 *mode, size_t *size,
+ time_t *c_time, time_t *a_time, time_t *m_time,
+ SMB_INO_T *ino)
+{
+
+ if (!context || !context->internal ||
+ !context->internal->_initialized) {
+
+ errno = EINVAL;
+ return -1;
+
+ }
+
+ DEBUG(4,("smbc_getatr: sending qpathinfo\n"));
+
+ if (!srv->no_pathinfo2 &&
+ cli_qpathinfo2(&srv->cli, path, c_time, a_time, m_time, NULL,
+ size, mode, ino)) return True;
+
+ /* if this is NT then don't bother with the getatr */
+ if (srv->cli.capabilities & CAP_NT_SMBS) {
+ errno = EPERM;
+ return False;
+ }
+
+ if (cli_getatr(&srv->cli, path, mode, size, m_time)) {
+ a_time = c_time = m_time;
+ srv->no_pathinfo2 = True;
+ return True;
+ }
+
+ errno = EPERM;
+ return False;
+
+}
+
+/*
+ * Routine to unlink() a file
+ */
+
+static int smbc_unlink_ctx(SMBCCTX *context, const char *fname)
+{
+ fstring server, share, user, password, workgroup;
+ pstring path;
+ SMBCSRV *srv = NULL;
+
+ if (!context || !context->internal ||
+ !context->internal->_initialized) {
+
+ errno = EINVAL; /* Best I can think of ... */
+ return -1;
+
+ }
+
+ if (!fname) {
+
+ errno = EINVAL;
+ return -1;
+
+ }
+
+ if (smbc_parse_path(context, fname,
+ server, sizeof(server),
+ share, sizeof(share),
+ path, sizeof(path),
+ user, sizeof(user),
+ password, sizeof(password),
+ NULL, 0)) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ if (user[0] == (char)0) fstrcpy(user, context->user);
+
+ fstrcpy(workgroup, context->workgroup);
+
+ srv = smbc_server(context, server, share, workgroup, user, password);
+
+ if (!srv) {
+
+ return -1; /* smbc_server sets errno */
+
+ }
+
+ /* if (strncmp(srv->cli.dev, "LPT", 3) == 0) {
+
+ int job = smbc_stat_printjob(srv, path, NULL, NULL);
+ if (job == -1) {
+
+ return -1;
+
+ }
+ if ((err = cli_printjob_del(&srv->cli, job)) != 0) {
+
+
+ return -1;
+
+ }
+ } else */
+
+ if (!cli_unlink(&srv->cli, path)) {
+
+ errno = smbc_errno(context, &srv->cli);
+
+ if (errno == EACCES) { /* Check if the file is a directory */
+
+ int saverr = errno;
+ size_t size = 0;
+ uint16 mode = 0;
+ time_t m_time = 0, a_time = 0, c_time = 0;
+ SMB_INO_T ino = 0;
+
+ if (!smbc_getatr(context, srv, path, &mode, &size,
+ &c_time, &a_time, &m_time, &ino)) {
+
+ /* Hmmm, bad error ... What? */
+
+ errno = smbc_errno(context, &srv->cli);
+ return -1;
+
+ }
+ else {
+
+ if (IS_DOS_DIR(mode))
+ errno = EISDIR;
+ else
+ errno = saverr; /* Restore this */
+
+ }
+ }
+
+ return -1;
+
+ }
+
+ return 0; /* Success ... */
+
+}
+
+/*
+ * Routine to rename() a file
+ */
+
+static int smbc_rename_ctx(SMBCCTX *ocontext, const char *oname,
+ SMBCCTX *ncontext, const char *nname)
+{
+ fstring server1, share1, server2, share2, user1, user2, password1, password2, workgroup;
+ pstring path1, path2;
+ SMBCSRV *srv = NULL;
+
+ if (!ocontext || !ncontext ||
+ !ocontext->internal || !ncontext->internal ||
+ !ocontext->internal->_initialized ||
+ !ncontext->internal->_initialized) {
+
+ errno = EINVAL; /* Best I can think of ... */
+ return -1;
+
+ }
+
+ if (!oname || !nname) {
+
+ errno = EINVAL;
+ return -1;
+
+ }
+
+ DEBUG(4, ("smbc_rename(%s,%s)\n", oname, nname));
+
+ smbc_parse_path(ocontext, oname,
+ server1, sizeof(server1),
+ share1, sizeof(share1),
+ path1, sizeof(path1),
+ user1, sizeof(user1),
+ password1, sizeof(password1),
+ NULL, 0);
+
+ if (user1[0] == (char)0) fstrcpy(user1, ocontext->user);
+
+ smbc_parse_path(ncontext, nname,
+ server2, sizeof(server2),
+ share2, sizeof(share2),
+ path2, sizeof(path2),
+ user2, sizeof(user2),
+ password2, sizeof(password2),
+ NULL, 0);
+
+ if (user2[0] == (char)0) fstrcpy(user2, ncontext->user);
+
+ if (strcmp(server1, server2) || strcmp(share1, share2) ||
+ strcmp(user1, user2)) {
+
+ /* Can't rename across file systems, or users?? */
+
+ errno = EXDEV;
+ return -1;
+
+ }
+
+ fstrcpy(workgroup, ocontext->workgroup);
+ /* HELP !!! Which workgroup should I use ? Or are they always the same -- Tom */
+ srv = smbc_server(ocontext, server1, share1, workgroup, user1, password1);
+ if (!srv) {
+
+ return -1;
+
+ }
+
+ if (!cli_rename(&srv->cli, path1, path2)) {
+ int eno = smbc_errno(ocontext, &srv->cli);
+
+ if (eno != EEXIST ||
+ !cli_unlink(&srv->cli, path2) ||
+ !cli_rename(&srv->cli, path1, path2)) {
+
+ errno = eno;
+ return -1;
+
+ }
+ }
+
+ return 0; /* Success */
+
+}
+
+/*
+ * A routine to lseek() a file
+ */
+
+static off_t smbc_lseek_ctx(SMBCCTX *context, SMBCFILE *file, off_t offset, int whence)
+{
+ size_t size;
+
+ if (!context || !context->internal ||
+ !context->internal->_initialized) {
+
+ errno = EINVAL;
+ return -1;
+
+ }
+
+ if (!file || !DLIST_CONTAINS(context->internal->_files, file)) {
+
+ errno = EBADF;
+ return -1;
+
+ }
+
+ if (!file->file) {
+
+ errno = EINVAL;
+ return -1; /* Can't lseek a dir ... */
+
+ }
+
+ switch (whence) {
+ case SEEK_SET:
+ file->offset = offset;
+ break;
+
+ case SEEK_CUR:
+ file->offset += offset;
+ break;
+
+ case SEEK_END:
+ if (!cli_qfileinfo(&file->srv->cli, file->cli_fd, NULL, &size, NULL, NULL,
+ NULL, NULL, NULL))
+ {
+ SMB_BIG_UINT b_size = size;
+ if (!cli_getattrE(&file->srv->cli, file->cli_fd, NULL, &b_size, NULL, NULL,
+ NULL))
+ {
+ errno = EINVAL;
+ return -1;
+ } else
+ size = b_size;
+ }
+ file->offset = size + offset;
+ break;
+
+ default:
+ errno = EINVAL;
+ break;
+
+ }
+
+ return file->offset;
+
+}
+
+/*
+ * Generate an inode number from file name for those things that need it
+ */
+
+static
+ino_t smbc_inode(SMBCCTX *context, const char *name)
+{
+
+ if (!context || !context->internal ||
+ !context->internal->_initialized) {
+
+ errno = EINVAL;
+ return -1;
+
+ }
+
+ if (!*name) return 2; /* FIXME, why 2 ??? */
+ return (ino_t)str_checksum(name);
+
+}
+
+/*
+ * Routine to put basic stat info into a stat structure ... Used by stat and
+ * fstat below.
+ */
+
+static
+int smbc_setup_stat(SMBCCTX *context, struct stat *st, char *fname, size_t size, int mode)
+{
+
+ st->st_mode = 0;
+
+ if (IS_DOS_DIR(mode)) {
+ st->st_mode = SMBC_DIR_MODE;
+ } else {
+ st->st_mode = SMBC_FILE_MODE;
+ }
+
+ if (IS_DOS_ARCHIVE(mode)) st->st_mode |= S_IXUSR;
+ if (IS_DOS_SYSTEM(mode)) st->st_mode |= S_IXGRP;
+ if (IS_DOS_HIDDEN(mode)) st->st_mode |= S_IXOTH;
+ if (!IS_DOS_READONLY(mode)) st->st_mode |= S_IWUSR;
+
+ st->st_size = size;
+#ifdef HAVE_STAT_ST_BLKSIZE
+ st->st_blksize = 512;
+#endif
+#ifdef HAVE_STAT_ST_BLOCKS
+ st->st_blocks = (size+511)/512;
+#endif
+ st->st_uid = getuid();
+ st->st_gid = getgid();
+
+ if (IS_DOS_DIR(mode)) {
+ st->st_nlink = 2;
+ } else {
+ st->st_nlink = 1;
+ }
+
+ if (st->st_ino == 0) {
+ st->st_ino = smbc_inode(context, fname);
+ }
+
+ return True; /* FIXME: Is this needed ? */
+
+}
+
+/*
+ * Routine to stat a file given a name
+ */
+
+static int smbc_stat_ctx(SMBCCTX *context, const char *fname, struct stat *st)
+{
+ SMBCSRV *srv;
+ fstring server, share, user, password, workgroup;
+ pstring path;
+ time_t m_time = 0, a_time = 0, c_time = 0;
+ size_t size = 0;
+ uint16 mode = 0;
+ SMB_INO_T ino = 0;
+
+ if (!context || !context->internal ||
+ !context->internal->_initialized) {
+
+ errno = EINVAL; /* Best I can think of ... */
+ return -1;
+
+ }
+
+ if (!fname) {
+
+ errno = EINVAL;
+ return -1;
+
+ }
+
+ DEBUG(4, ("smbc_stat(%s)\n", fname));
+
+ if (smbc_parse_path(context, fname,
+ server, sizeof(server),
+ share, sizeof(share),
+ path, sizeof(path),
+ user, sizeof(user),
+ password, sizeof(password),
+ NULL, 0)) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ if (user[0] == (char)0) fstrcpy(user, context->user);
+
+ fstrcpy(workgroup, context->workgroup);
+
+ srv = smbc_server(context, server, share, workgroup, user, password);
+
+ if (!srv) {
+ return -1; /* errno set by smbc_server */
+ }
+
+ if (!smbc_getatr(context, srv, path, &mode, &size,
+ &c_time, &a_time, &m_time, &ino)) {
+
+ errno = smbc_errno(context, &srv->cli);
+ return -1;
+
+ }
+
+ st->st_ino = ino;
+
+ smbc_setup_stat(context, st, path, size, mode);
+
+ st->st_atime = a_time;
+ st->st_ctime = c_time;
+ st->st_mtime = m_time;
+ st->st_dev = srv->dev;
+
+ return 0;
+
+}
+
+/*
+ * Routine to stat a file given an fd
+ */
+
+static int smbc_fstat_ctx(SMBCCTX *context, SMBCFILE *file, struct stat *st)
+{
+ time_t c_time, a_time, m_time;
+ size_t size;
+ uint16 mode;
+ SMB_INO_T ino = 0;
+
+ if (!context || !context->internal ||
+ !context->internal->_initialized) {
+
+ errno = EINVAL;
+ return -1;
+
+ }
+
+ if (!file || !DLIST_CONTAINS(context->internal->_files, file)) {
+
+ errno = EBADF;
+ return -1;
+
+ }
+
+ if (!file->file) {
+
+ return context->fstatdir(context, file, st);
+
+ }
+
+ if (!cli_qfileinfo(&file->srv->cli, file->cli_fd,
+ &mode, &size, &c_time, &a_time, &m_time, NULL, &ino)) {
+ SMB_BIG_UINT b_size = size;
+ if (!cli_getattrE(&file->srv->cli, file->cli_fd,
+ &mode, &b_size, &c_time, &a_time, &m_time)) {
+
+ errno = EINVAL;
+ return -1;
+ } else
+ size = b_size;
+
+ }
+
+ st->st_ino = ino;
+
+ smbc_setup_stat(context, st, file->fname, size, mode);
+
+ st->st_atime = a_time;
+ st->st_ctime = c_time;
+ st->st_mtime = m_time;
+ st->st_dev = file->srv->dev;
+
+ return 0;
+
+}
+
+/*
+ * Routine to open a directory
+ * We accept the URL syntax explained in smbc_parse_path(), above.
+ */
+
+static void smbc_remove_dir(SMBCFILE *dir)
+{
+ struct smbc_dir_list *d,*f;
+
+ d = dir->dir_list;
+ while (d) {
+
+ f = d; d = d->next;
+
+ SAFE_FREE(f->dirent);
+ SAFE_FREE(f);
+
+ }
+
+ dir->dir_list = dir->dir_end = dir->dir_next = NULL;
+
+}
+
+static int add_dirent(SMBCFILE *dir, const char *name, const char *comment, uint32 type)
+{
+ struct smbc_dirent *dirent;
+ int size;
+ char *u_name = NULL, *u_comment = NULL;
+ size_t u_name_len = 0, u_comment_len = 0;
+
+ if (name)
+ u_name_len = push_utf8_allocate(&u_name, name);
+ if (comment)
+ u_comment_len = push_utf8_allocate(&u_comment, comment);
+
+ /*
+ * Allocate space for the dirent, which must be increased by the
+ * size of the name and the comment and 1 for the null on the comment.
+ * The null on the name is already accounted for.
+ */
+
+ size = sizeof(struct smbc_dirent) + u_name_len + u_comment_len + 1;
+
+ dirent = malloc(size);
+
+ if (!dirent) {
+
+ dir->dir_error = ENOMEM;
+ return -1;
+
+ }
+
+ ZERO_STRUCTP(dirent);
+
+ if (dir->dir_list == NULL) {
+
+ dir->dir_list = malloc(sizeof(struct smbc_dir_list));
+ if (!dir->dir_list) {
+
+ SAFE_FREE(dirent);
+ dir->dir_error = ENOMEM;
+ return -1;
+
+ }
+ ZERO_STRUCTP(dir->dir_list);
+
+ dir->dir_end = dir->dir_next = dir->dir_list;
+ }
+ else {
+
+ dir->dir_end->next = malloc(sizeof(struct smbc_dir_list));
+
+ if (!dir->dir_end->next) {
+
+ SAFE_FREE(dirent);
+ dir->dir_error = ENOMEM;
+ return -1;
+
+ }
+ ZERO_STRUCTP(dir->dir_end->next);
+
+ dir->dir_end = dir->dir_end->next;
+ }
+
+ dir->dir_end->next = NULL;
+ dir->dir_end->dirent = dirent;
+
+ dirent->smbc_type = type;
+ dirent->namelen = u_name_len;
+ dirent->commentlen = u_comment_len;
+ dirent->dirlen = size;
+
+ strncpy(dirent->name, (u_name?u_name:""), dirent->namelen + 1);
+
+ dirent->comment = (char *)(&dirent->name + dirent->namelen + 1);
+ strncpy(dirent->comment, (u_comment?u_comment:""), dirent->commentlen + 1);
+
+ SAFE_FREE(u_comment);
+ SAFE_FREE(u_name);
+
+ return 0;
+
+}
+
+static void
+list_unique_wg_fn(const char *name, uint32 type, const char *comment, void *state)
+{
+ SMBCFILE *dir = (SMBCFILE *)state;
+ struct smbc_dir_list *dir_list;
+ struct smbc_dirent *dirent;
+ int dirent_type;
+ int remove = 0;
+
+ dirent_type = dir->dir_type;
+
+ if (add_dirent(dir, name, comment, dirent_type) < 0) {
+
+ /* An error occurred, what do we do? */
+ /* FIXME: Add some code here */
+ }
+
+ /* Point to the one just added */
+ dirent = dir->dir_end->dirent;
+
+ /* See if this was a duplicate */
+ for (dir_list = dir->dir_list;
+ dir_list != dir->dir_end;
+ dir_list = dir_list->next) {
+ if (! remove &&
+ strcmp(dir_list->dirent->name, dirent->name) == 0) {
+ /* Duplicate. End end of list need to be removed. */
+ remove = 1;
+ }
+
+ if (remove && dir_list->next == dir->dir_end) {
+ /* Found the end of the list. Remove it. */
+ dir->dir_end = dir_list;
+ free(dir_list->next);
+ dir_list->next = NULL;
+ break;
+ }
+ }
+}
+
+static void
+list_fn(const char *name, uint32 type, const char *comment, void *state)
+{
+ SMBCFILE *dir = (SMBCFILE *)state;
+ int dirent_type;
+
+ /* We need to process the type a little ... */
+
+ if (dir->dir_type == SMBC_FILE_SHARE) {
+
+ switch (type) {
+ case 0: /* Directory tree */
+ dirent_type = SMBC_FILE_SHARE;
+ break;
+
+ case 1:
+ dirent_type = SMBC_PRINTER_SHARE;
+ break;
+
+ case 2:
+ dirent_type = SMBC_COMMS_SHARE;
+ break;
+
+ case 3:
+ dirent_type = SMBC_IPC_SHARE;
+ break;
+
+ default:
+ dirent_type = SMBC_FILE_SHARE; /* FIXME, error? */
+ break;
+ }
+ }
+ else dirent_type = dir->dir_type;
+
+ if (add_dirent(dir, name, comment, dirent_type) < 0) {
+
+ /* An error occurred, what do we do? */
+ /* FIXME: Add some code here */
+
+ }
+}
+
+static void
+dir_list_fn(file_info *finfo, const char *mask, void *state)
+{
+
+ if (add_dirent((SMBCFILE *)state, finfo->name, "",
+ (finfo->mode&aDIR?SMBC_DIR:SMBC_FILE)) < 0) {
+
+ /* Handle an error ... */
+
+ /* FIXME: Add some code ... */
+
+ }
+
+}
+
+static SMBCFILE *smbc_opendir_ctx(SMBCCTX *context, const char *fname)
+{
+ fstring server, share, user, password, options;
+ pstring workgroup;
+ pstring path;
+ SMBCSRV *srv = NULL;
+ SMBCFILE *dir = NULL;
+ struct in_addr rem_ip;
+
+ if (!context || !context->internal ||
+ !context->internal->_initialized) {
+ DEBUG(4, ("no valid context\n"));
+ errno = EINVAL;
+ return NULL;
+
+ }
+
+ if (!fname) {
+ DEBUG(4, ("no valid fname\n"));
+ errno = EINVAL;
+ return NULL;
+ }
+
+ if (smbc_parse_path(context, fname,
+ server, sizeof(server),
+ share, sizeof(share),
+ path, sizeof(path),
+ user, sizeof(path),
+ password, sizeof(password),
+ options, sizeof(options))) {
+ DEBUG(4, ("no valid path\n"));
+ errno = EINVAL;
+ return NULL;
+ }
+
+ DEBUG(4, ("parsed path: fname='%s' server='%s' share='%s' path='%s' options='%s'\n", fname, server, share, path, options));
+
+ /* Ensure the options are valid */
+ if (smbc_check_options(server, share, path, options)) {
+ DEBUG(4, ("unacceptable options (%s)\n", options));
+ errno = EINVAL;
+ return NULL;
+ }
+
+ if (user[0] == (char)0) fstrcpy(user, context->user);
+
+ pstrcpy(workgroup, context->workgroup);
+
+ dir = malloc(sizeof(*dir));
+
+ if (!dir) {
+
+ errno = ENOMEM;
+ return NULL;
+
+ }
+
+ ZERO_STRUCTP(dir);
+
+ dir->cli_fd = 0;
+ dir->fname = strdup(fname);
+ dir->srv = NULL;
+ dir->offset = 0;
+ dir->file = False;
+ dir->dir_list = dir->dir_next = dir->dir_end = NULL;
+
+ if (server[0] == (char)0 &&
+ (! *options || strcmp(options, "mb=.any") == 0)) {
+ struct in_addr server_ip;
+ if (share[0] != (char)0 || path[0] != (char)0) {
+
+ errno = EINVAL;
+ if (dir) {
+ SAFE_FREE(dir->fname);
+ SAFE_FREE(dir);
+ }
+ return NULL;
+ }
+
+ /*
+ * We have server and share and path empty ... so list the
+ * workgroups first try to get the LMB for our workgroup, and
+ * if that fails, try the DMB
+ */
+
+ pstrcpy(workgroup, lp_workgroup());
+
+ if (!find_master_ip(workgroup, &server_ip)) {
+ struct user_auth_info u_info;
+ struct cli_state *cli;
+
+ DEBUG(4, ("Unable to find master browser for workgroup %s\n",
+ workgroup));
+
+ /* find the name of the server ... */
+ pstrcpy(u_info.username, user);
+ pstrcpy(u_info.password, password);
+
+ if (!(cli = get_ipc_connect_master_ip_bcast(workgroup, &u_info))) {
+ DEBUG(4, ("Unable to find master browser by "
+ "broadcast\n"));
+ errno = ENOENT;
+ return NULL;
+ }
+
+ fstrcpy(server, cli->desthost);
+
+ cli_shutdown(cli);
+ } else {
+ /*
+ * Do a name status query to find out the name of the
+ * master browser. We use <01><02>__MSBROWSE__<02>#01 if
+ * *#00 fails because a domain master browser will not
+ * respond to a wildcard query (or, at least, an NT4
+ * server acting as the domain master browser will not).
+ *
+ * We might be able to use ONLY the query on MSBROWSE, but
+ * that's not yet been tested with all Windows versions,
+ * so until it is, leave the original wildcard query as
+ * the first choice and fall back to MSBROWSE if the
+ * wildcard query fails.
+ */
+ if (!name_status_find("*", 0, 0x1d, server_ip, server) &&
+ !name_status_find(MSBROWSE, 1, 0x1d, server_ip, server)) {
+ errno = ENOENT;
+ return NULL;
+ }
+ }
+
+ DEBUG(4, ("using workgroup %s %s\n", workgroup, server));
+
+ /*
+ * Get a connection to IPC$ on the server if we do not already
+ * have one
+ */
+
+ srv = smbc_server(context, server, "IPC$", workgroup, user, password);
+ if (!srv) {
+
+ if (dir) {
+ SAFE_FREE(dir->fname);
+ SAFE_FREE(dir);
+ }
+ return NULL;
+ }
+
+ dir->srv = srv;
+ dir->dir_type = SMBC_WORKGROUP;
+
+ /* Now, list the stuff ... */
+
+ if (!cli_NetServerEnum(&srv->cli, workgroup, SV_TYPE_DOMAIN_ENUM, list_fn,
+ (void *)dir)) {
+
+ DEBUG(1, ("Could not enumerate domains using '%s'\n", workgroup));
+ if (dir) {
+ SAFE_FREE(dir->fname);
+ SAFE_FREE(dir);
+ }
+ errno = cli_errno(&srv->cli);
+
+ return NULL;
+
+ }
+ } else if (server[0] == (char)0 &&
+ (! *options || strcmp(options, "mb=.all") == 0)) {
+
+ int i;
+ int count;
+ struct ip_service *ip_list;
+ struct ip_service server_addr;
+ struct user_auth_info u_info;
+ struct cli_state *cli;
+
+ if (share[0] != (char)0 || path[0] != (char)0) {
+
+ errno = EINVAL;
+ if (dir) {
+ SAFE_FREE(dir->fname);
+ SAFE_FREE(dir);
+ }
+ return NULL;
+ }
+
+ pstrcpy(u_info.username, user);
+ pstrcpy(u_info.password, password);
+
+ /*
+ * We have server and share and path empty but options
+ * requesting that we scan all master browsers for their list
+ * of workgroups/domains. This implies that we must first try
+ * broadcast queries to find all master browsers, and if that
+ * doesn't work, then try our other methods which return only
+ * a single master browser.
+ */
+
+ if (!name_resolve_bcast(MSBROWSE, 1, &ip_list, &count)) {
+ if (!find_master_ip(workgroup, &server_addr.ip)) {
+
+ errno = ENOENT;
+ return NULL;
+ }
+
+ ip_list = &server_addr;
+ count = 1;
+ }
+
+ for (i = 0; i < count; i++) {
+ DEBUG(99, ("Found master browser %s\n", inet_ntoa(ip_list[i].ip)));
+
+ cli = get_ipc_connect_master_ip(&ip_list[i], workgroup, &u_info);
+ fstrcpy(server, cli->desthost);
+ cli_shutdown(cli);
+
+ DEBUG(4, ("using workgroup %s %s\n", workgroup, server));
+
+ /*
+ * For each returned master browser IP address, get a
+ * connection to IPC$ on the server if we do not
+ * already have one, and determine the
+ * workgroups/domains that it knows about.
+ */
+
+ srv = smbc_server(context, server,
+ "IPC$", workgroup, user, password);
+ if (!srv) {
+
+ if (dir) {
+ SAFE_FREE(dir->fname);
+ SAFE_FREE(dir);
+ }
+ return NULL;
+ }
+
+ dir->srv = srv;
+ dir->dir_type = SMBC_WORKGROUP;
+
+ /* Now, list the stuff ... */
+
+ if (!cli_NetServerEnum(&srv->cli, workgroup, SV_TYPE_DOMAIN_ENUM, list_unique_wg_fn,
+ (void *)dir)) {
+
+ if (dir) {
+ SAFE_FREE(dir->fname);
+ SAFE_FREE(dir);
+ }
+ errno = cli_errno(&srv->cli);
+
+ return NULL;
+
+ }
+ }
+ } else {
+ /*
+ * Server not an empty string ... Check the rest and see what
+ * gives
+ */
+ if (share[0] == (char)0) {
+
+ if (path[0] != (char)0) { /* Should not have empty share with path */
+
+ errno = EINVAL;
+ if (dir) {
+ SAFE_FREE(dir->fname);
+ SAFE_FREE(dir);
+ }
+ return NULL;
+
+ }
+
+ /* Check to see if <server><1D>, <server><1B>, or <server><20> translates */
+ /* However, we check to see if <server> is an IP address first */
+
+ if (!is_ipaddress(server) && /* Not an IP addr so check next */
+ (resolve_name(server, &rem_ip, 0x1d) || /* Found LMB */
+ resolve_name(server, &rem_ip, 0x1b) )) { /* Found DMB */
+ fstring buserver;
+
+ dir->dir_type = SMBC_SERVER;
+
+ /*
+ * Get the backup list ...
+ */
+
+
+ if (!name_status_find(server, 0, 0, rem_ip, buserver)) {
+
+ DEBUG(0, ("Could not get name of local/domain master browser for server %s\n", server));
+ errno = EPERM; /* FIXME, is this correct */
+ return NULL;
+
+ }
+
+ /*
+ * Get a connection to IPC$ on the server if we do not already have one
+ */
+
+ srv = smbc_server(context, buserver, "IPC$", workgroup, user, password);
+
+ if (!srv) {
+ DEBUG(0, ("got no contact to IPC$\n"));
+ if (dir) {
+ SAFE_FREE(dir->fname);
+ SAFE_FREE(dir);
+ }
+ return NULL;
+
+ }
+
+ dir->srv = srv;
+
+ /* Now, list the servers ... */
+
+ if (!cli_NetServerEnum(&srv->cli, server, 0x0000FFFE, list_fn,
+ (void *)dir)) {
+
+ if (dir) {
+ SAFE_FREE(dir->fname);
+ SAFE_FREE(dir);
+ }
+ errno = cli_errno(&srv->cli);
+ return NULL;
+
+ }
+ }
+ else {
+
+ if (resolve_name(server, &rem_ip, 0x20)) {
+
+ /* Now, list the shares ... */
+
+ dir->dir_type = SMBC_FILE_SHARE;
+
+ srv = smbc_server(context, server, "IPC$", workgroup, user, password);
+
+ if (!srv) {
+
+ if (dir) {
+ SAFE_FREE(dir->fname);
+ SAFE_FREE(dir);
+ }
+ return NULL;
+
+ }
+
+ dir->srv = srv;
+
+ /* Now, list the servers ... */
+
+ if (cli_RNetShareEnum(&srv->cli, list_fn,
+ (void *)dir) < 0) {
+
+ errno = cli_errno(&srv->cli);
+ if (dir) {
+ SAFE_FREE(dir->fname);
+ SAFE_FREE(dir);
+ }
+ return NULL;
+
+ }
+
+ }
+ else {
+
+ errno = ENODEV; /* Neither the workgroup nor server exists */
+ if (dir) {
+ SAFE_FREE(dir->fname);
+ SAFE_FREE(dir);
+ }
+ return NULL;
+
+ }
+
+ }
+
+ }
+ else { /* The server and share are specified ... work from there ... */
+
+ /* Well, we connect to the server and list the directory */
+
+ dir->dir_type = SMBC_FILE_SHARE;
+
+ srv = smbc_server(context, server, share, workgroup, user, password);
+
+ if (!srv) {
+
+ if (dir) {
+ SAFE_FREE(dir->fname);
+ SAFE_FREE(dir);
+ }
+ return NULL;
+
+ }
+
+ dir->srv = srv;
+
+ /* Now, list the files ... */
+
+ pstrcat(path, "\\*");
+
+ if (cli_list(&srv->cli, path, aDIR | aSYSTEM | aHIDDEN, dir_list_fn,
+ (void *)dir) < 0) {
+
+ if (dir) {
+ SAFE_FREE(dir->fname);
+ SAFE_FREE(dir);
+ }
+ errno = smbc_errno(context, &srv->cli);
+ return NULL;
+
+ }
+ }
+
+ }
+
+ DLIST_ADD(context->internal->_files, dir);
+ return dir;
+
+}
+
+/*
+ * Routine to close a directory
+ */
+
+static int smbc_closedir_ctx(SMBCCTX *context, SMBCFILE *dir)
+{
+
+ if (!context || !context->internal ||
+ !context->internal->_initialized) {
+
+ errno = EINVAL;
+ return -1;
+
+ }
+
+ if (!dir || !DLIST_CONTAINS(context->internal->_files, dir)) {
+
+ errno = EBADF;
+ return -1;
+
+ }
+
+ smbc_remove_dir(dir); /* Clean it up */
+
+ DLIST_REMOVE(context->internal->_files, dir);
+
+ if (dir) {
+
+ SAFE_FREE(dir->fname);
+ SAFE_FREE(dir); /* Free the space too */
+ }
+
+ return 0;
+
+}
+
+/*
+ * Routine to get a directory entry
+ */
+
+struct smbc_dirent *smbc_readdir_ctx(SMBCCTX *context, SMBCFILE *dir)
+{
+ struct smbc_dirent *dirp, *dirent;
+
+ /* Check that all is ok first ... */
+
+ if (!context || !context->internal ||
+ !context->internal->_initialized) {
+
+ errno = EINVAL;
+ DEBUG(0, ("Invalid context in smbc_readdir_ctx()\n"));
+ return NULL;
+
+ }
+
+ if (!dir || !DLIST_CONTAINS(context->internal->_files, dir)) {
+
+ errno = EBADF;
+ DEBUG(0, ("Invalid dir in smbc_readdir_ctx()\n"));
+ return NULL;
+
+ }
+
+ if (dir->file != False) { /* FIXME, should be dir, perhaps */
+
+ errno = ENOTDIR;
+ DEBUG(0, ("Found file vs directory in smbc_readdir_ctx()\n"));
+ return NULL;
+
+ }
+
+ if (!dir->dir_next) {
+ return NULL;
+ }
+ else {
+
+ dirent = dir->dir_next->dirent;
+ if (!dirent) {
+
+ errno = ENOENT;
+ return NULL;
+
+ }
+
+ /* Hmmm, do I even need to copy it? */
+
+ memcpy(context->internal->_dirent, dirent, dirent->dirlen); /* Copy the dirent */
+ dirp = (struct smbc_dirent *)context->internal->_dirent;
+ dirp->comment = (char *)(&dirp->name + dirent->namelen + 1);
+ dir->dir_next = dir->dir_next->next;
+
+ return (struct smbc_dirent *)context->internal->_dirent;
+ }
+
+}
+
+/*
+ * Routine to get directory entries
+ */
+
+static int smbc_getdents_ctx(SMBCCTX *context, SMBCFILE *dir, struct smbc_dirent *dirp, int count)
+{
+ struct smbc_dir_list *dirlist;
+ int rem = count, reqd;
+ char *ndir = (char *)dirp;
+
+ /* Check that all is ok first ... */
+
+ if (!context || !context->internal ||
+ !context->internal->_initialized) {
+
+ errno = EINVAL;
+ return -1;
+
+ }
+
+ if (!dir || !DLIST_CONTAINS(context->internal->_files, dir)) {
+
+ errno = EBADF;
+ return -1;
+
+ }
+
+ if (dir->file != False) { /* FIXME, should be dir, perhaps */
+
+ errno = ENOTDIR;
+ return -1;
+
+ }
+
+ /*
+ * Now, retrieve the number of entries that will fit in what was passed
+ * We have to figure out if the info is in the list, or we need to
+ * send a request to the server to get the info.
+ */
+
+ while ((dirlist = dir->dir_next)) {
+ struct smbc_dirent *dirent;
+
+ if (!dirlist->dirent) {
+
+ errno = ENOENT; /* Bad error */
+ return -1;
+
+ }
+
+ if (rem < (reqd = (sizeof(struct smbc_dirent) + dirlist->dirent->namelen +
+ dirlist->dirent->commentlen + 1))) {
+
+ if (rem < count) { /* We managed to copy something */
+
+ errno = 0;
+ return count - rem;
+
+ }
+ else { /* Nothing copied ... */
+
+ errno = EINVAL; /* Not enough space ... */
+ return -1;
+
+ }
+
+ }
+
+ dirent = dirlist->dirent;
+
+ memcpy(ndir, dirent, reqd); /* Copy the data in ... */
+
+ ((struct smbc_dirent *)ndir)->comment =
+ (char *)(&((struct smbc_dirent *)ndir)->name + dirent->namelen + 1);
+
+ ndir += reqd;
+
+ rem -= reqd;
+
+ dir->dir_next = dirlist = dirlist -> next;
+ }
+
+ if (rem == count)
+ return 0;
+ else
+ return count - rem;
+
+}
+
+/*
+ * Routine to create a directory ...
+ */
+
+static int smbc_mkdir_ctx(SMBCCTX *context, const char *fname, mode_t mode)
+{
+ SMBCSRV *srv;
+ fstring server, share, user, password, workgroup;
+ pstring path;
+
+ if (!context || !context->internal ||
+ !context->internal->_initialized) {
+
+ errno = EINVAL;
+ return -1;
+
+ }
+
+ if (!fname) {
+
+ errno = EINVAL;
+ return -1;
+
+ }
+
+ DEBUG(4, ("smbc_mkdir(%s)\n", fname));
+
+ if (smbc_parse_path(context, fname,
+ server, sizeof(server),
+ share, sizeof(share),
+ path, sizeof(path),
+ user, sizeof(user),
+ password, sizeof(password),
+ NULL, 0)) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ if (user[0] == (char)0) fstrcpy(user, context->user);
+
+ fstrcpy(workgroup, context->workgroup);
+
+ srv = smbc_server(context, server, share, workgroup, user, password);
+
+ if (!srv) {
+
+ return -1; /* errno set by smbc_server */
+
+ }
+
+ /* if (strncmp(srv->cli.dev, "IPC", 3) == 0) {
+
+ mode = aDIR | aRONLY;
+
+ }
+ else if (strncmp(srv->cli.dev, "LPT", 3) == 0) {
+
+ if (strcmp(path, "\\") == 0) {
+
+ mode = aDIR | aRONLY;
+
+ }
+ else {
+
+ mode = aRONLY;
+ smbc_stat_printjob(srv, path, &size, &m_time);
+ c_time = a_time = m_time;
+
+ }
+ else { */
+
+ if (!cli_mkdir(&srv->cli, path)) {
+
+ errno = smbc_errno(context, &srv->cli);
+ return -1;
+
+ }
+
+ return 0;
+
+}
+
+/*
+ * Our list function simply checks to see if a directory is not empty
+ */
+
+static int smbc_rmdir_dirempty = True;
+
+static void rmdir_list_fn(file_info *finfo, const char *mask, void *state)
+{
+
+ if (strncmp(finfo->name, ".", 1) != 0 && strncmp(finfo->name, "..", 2) != 0)
+ smbc_rmdir_dirempty = False;
+
+}
+
+/*
+ * Routine to remove a directory
+ */
+
+static int smbc_rmdir_ctx(SMBCCTX *context, const char *fname)
+{
+ SMBCSRV *srv;
+ fstring server, share, user, password, workgroup;
+ pstring path;
+
+ if (!context || !context->internal ||
+ !context->internal->_initialized) {
+
+ errno = EINVAL;
+ return -1;
+
+ }
+
+ if (!fname) {
+
+ errno = EINVAL;
+ return -1;
+
+ }
+
+ DEBUG(4, ("smbc_rmdir(%s)\n", fname));
+
+ if (smbc_parse_path(context, fname,
+ server, sizeof(server),
+ share, sizeof(share),
+ path, sizeof(path),
+ user, sizeof(user),
+ password, sizeof(password),
+ NULL, 0))
+ {
+ errno = EINVAL;
+ return -1;
+ }
+
+ if (user[0] == (char)0) fstrcpy(user, context->user);
+
+ fstrcpy(workgroup, context->workgroup);
+
+ srv = smbc_server(context, server, share, workgroup, user, password);
+
+ if (!srv) {
+
+ return -1; /* errno set by smbc_server */
+
+ }
+
+ /* if (strncmp(srv->cli.dev, "IPC", 3) == 0) {
+
+ mode = aDIR | aRONLY;
+
+ }
+ else if (strncmp(srv->cli.dev, "LPT", 3) == 0) {
+
+ if (strcmp(path, "\\") == 0) {
+
+ mode = aDIR | aRONLY;
+
+ }
+ else {
+
+ mode = aRONLY;
+ smbc_stat_printjob(srv, path, &size, &m_time);
+ c_time = a_time = m_time;
+
+ }
+ else { */
+
+ if (!cli_rmdir(&srv->cli, path)) {
+
+ errno = smbc_errno(context, &srv->cli);
+
+ if (errno == EACCES) { /* Check if the dir empty or not */
+
+ pstring lpath; /* Local storage to avoid buffer overflows */
+
+ smbc_rmdir_dirempty = True; /* Make this so ... */
+
+ pstrcpy(lpath, path);
+ pstrcat(lpath, "\\*");
+
+ if (cli_list(&srv->cli, lpath, aDIR | aSYSTEM | aHIDDEN, rmdir_list_fn,
+ NULL) < 0) {
+
+ /* Fix errno to ignore latest error ... */
+
+ DEBUG(5, ("smbc_rmdir: cli_list returned an error: %d\n",
+ smbc_errno(context, &srv->cli)));
+ errno = EACCES;
+
+ }
+
+ if (smbc_rmdir_dirempty)
+ errno = EACCES;
+ else
+ errno = ENOTEMPTY;
+
+ }
+
+ return -1;
+
+ }
+
+ return 0;
+
+}
+
+/*
+ * Routine to return the current directory position
+ */
+
+static off_t smbc_telldir_ctx(SMBCCTX *context, SMBCFILE *dir)
+{
+ off_t ret_val; /* Squash warnings about cast */
+
+ if (!context || !context->internal ||
+ !context->internal->_initialized) {
+
+ errno = EINVAL;
+ return -1;
+
+ }
+
+ if (!dir || !DLIST_CONTAINS(context->internal->_files, dir)) {
+
+ errno = EBADF;
+ return -1;
+
+ }
+
+ if (dir->file != False) { /* FIXME, should be dir, perhaps */
+
+ errno = ENOTDIR;
+ return -1;
+
+ }
+
+ /*
+ * We return the pointer here as the offset
+ */
+ ret_val = (int)dir->dir_next;
+ return ret_val;
+
+}
+
+/*
+ * A routine to run down the list and see if the entry is OK
+ */
+
+struct smbc_dir_list *smbc_check_dir_ent(struct smbc_dir_list *list,
+ struct smbc_dirent *dirent)
+{
+
+ /* Run down the list looking for what we want */
+
+ if (dirent) {
+
+ struct smbc_dir_list *tmp = list;
+
+ while (tmp) {
+
+ if (tmp->dirent == dirent)
+ return tmp;
+
+ tmp = tmp->next;
+
+ }
+
+ }
+
+ return NULL; /* Not found, or an error */
+
+}
+
+
+/*
+ * Routine to seek on a directory
+ */
+
+static int smbc_lseekdir_ctx(SMBCCTX *context, SMBCFILE *dir, off_t offset)
+{
+ long int l_offset = offset; /* Handle problems of size */
+ struct smbc_dirent *dirent = (struct smbc_dirent *)l_offset;
+ struct smbc_dir_list *list_ent = (struct smbc_dir_list *)NULL;
+
+ if (!context || !context->internal ||
+ !context->internal->_initialized) {
+
+ errno = EINVAL;
+ return -1;
+
+ }
+
+ if (dir->file != False) { /* FIXME, should be dir, perhaps */
+
+ errno = ENOTDIR;
+ return -1;
+
+ }
+
+ /* Now, check what we were passed and see if it is OK ... */
+
+ if (dirent == NULL) { /* Seek to the begining of the list */
+
+ dir->dir_next = dir->dir_list;
+ return 0;
+
+ }
+
+ /* Now, run down the list and make sure that the entry is OK */
+ /* This may need to be changed if we change the format of the list */
+
+ if ((list_ent = smbc_check_dir_ent(dir->dir_list, dirent)) == NULL) {
+
+ errno = EINVAL; /* Bad entry */
+ return -1;
+
+ }
+
+ dir->dir_next = list_ent;
+
+ return 0;
+
+}
+
+/*
+ * Routine to fstat a dir
+ */
+
+static int smbc_fstatdir_ctx(SMBCCTX *context, SMBCFILE *dir, struct stat *st)
+{
+
+ if (!context || !context->internal ||
+ !context->internal->_initialized) {
+
+ errno = EINVAL;
+ return -1;
+
+ }
+
+ /* No code yet ... */
+
+ return 0;
+
+}
+
+int smbc_chmod_ctx(SMBCCTX *context, const char *fname, mode_t newmode)
+{
+ SMBCSRV *srv;
+ fstring server, share, user, password, workgroup;
+ pstring path;
+ uint16 mode;
+
+ if (!context || !context->internal ||
+ !context->internal->_initialized) {
+
+ errno = EINVAL; /* Best I can think of ... */
+ return -1;
+
+ }
+
+ if (!fname) {
+
+ errno = EINVAL;
+ return -1;
+
+ }
+
+ DEBUG(4, ("smbc_chmod(%s, 0%3o)\n", fname, newmode));
+
+ if (smbc_parse_path(context, fname,
+ server, sizeof(server),
+ share, sizeof(share),
+ path, sizeof(path),
+ user, sizeof(user),
+ password, sizeof(password),
+ NULL, 0)) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ if (user[0] == (char)0) fstrcpy(user, context->user);
+
+ fstrcpy(workgroup, context->workgroup);
+
+ srv = smbc_server(context, server, share, workgroup, user, password);
+
+ if (!srv) {
+ return -1; /* errno set by smbc_server */
+ }
+
+ mode = 0;
+
+ if (!(newmode & (S_IWUSR | S_IWGRP | S_IWOTH))) mode |= aRONLY;
+ if ((newmode & S_IXUSR) && lp_map_archive(-1)) mode |= aARCH;
+ if ((newmode & S_IXGRP) && lp_map_system(-1)) mode |= aSYSTEM;
+ if ((newmode & S_IXOTH) && lp_map_hidden(-1)) mode |= aHIDDEN;
+
+ if (!cli_setatr(&srv->cli, path, mode, 0)) {
+ errno = smbc_errno(context, &srv->cli);
+ return -1;
+ }
+
+ return 0;
+}
+
+int smbc_utimes_ctx(SMBCCTX *context, const char *fname, struct timeval *tbuf)
+{
+ SMBCSRV *srv;
+ fstring server, share, user, password, workgroup;
+ pstring path;
+ uint16 mode;
+ time_t t = (tbuf == NULL ? time(NULL) : tbuf->tv_sec);
+
+ if (!context || !context->internal ||
+ !context->internal->_initialized) {
+
+ errno = EINVAL; /* Best I can think of ... */
+ return -1;
+
+ }
+
+ if (!fname) {
+
+ errno = EINVAL;
+ return -1;
+
+ }
+
+ DEBUG(4, ("smbc_utimes(%s, [%s])\n", fname, ctime(&t)));
+
+ if (smbc_parse_path(context, fname,
+ server, sizeof(server),
+ share, sizeof(share),
+ path, sizeof(path),
+ user, sizeof(user),
+ password, sizeof(password),
+ NULL, 0)) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ if (user[0] == (char)0) fstrcpy(user, context->user);
+
+ fstrcpy(workgroup, context->workgroup);
+
+ srv = smbc_server(context, server, share, workgroup, user, password);
+
+ if (!srv) {
+ return -1; /* errno set by smbc_server */
+ }
+
+ if (!smbc_getatr(context, srv, path,
+ &mode, NULL,
+ NULL, NULL, NULL,
+ NULL)) {
+ return -1;
+ }
+
+ if (!cli_setatr(&srv->cli, path, mode, t)) {
+ /* some servers always refuse directory changes */
+ if (!(mode & aDIR)) {
+ errno = smbc_errno(context, &srv->cli);
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+
+/* The MSDN is contradictory over the ordering of ACE entries in an ACL.
+ However NT4 gives a "The information may have been modified by a
+ computer running Windows NT 5.0" if denied ACEs do not appear before
+ allowed ACEs. */
+
+static int ace_compare(SEC_ACE *ace1, SEC_ACE *ace2)
+{
+ if (sec_ace_equal(ace1, ace2))
+ return 0;
+
+ if (ace1->type != ace2->type)
+ return ace2->type - ace1->type;
+
+ if (sid_compare(&ace1->trustee, &ace2->trustee))
+ return sid_compare(&ace1->trustee, &ace2->trustee);
+
+ if (ace1->flags != ace2->flags)
+ return ace1->flags - ace2->flags;
+
+ if (ace1->info.mask != ace2->info.mask)
+ return ace1->info.mask - ace2->info.mask;
+
+ if (ace1->size != ace2->size)
+ return ace1->size - ace2->size;
+
+ return memcmp(ace1, ace2, sizeof(SEC_ACE));
+}
+
+
+static void sort_acl(SEC_ACL *the_acl)
+{
+ uint32 i;
+ if (!the_acl) return;
+
+ qsort(the_acl->ace, the_acl->num_aces, sizeof(the_acl->ace[0]), QSORT_CAST ace_compare);
+
+ for (i=1;i<the_acl->num_aces;) {
+ if (sec_ace_equal(&the_acl->ace[i-1], &the_acl->ace[i])) {
+ int j;
+ for (j=i; j<the_acl->num_aces-1; j++) {
+ the_acl->ace[j] = the_acl->ace[j+1];
+ }
+ the_acl->num_aces--;
+ } else {
+ i++;
+ }
+ }
+}
+
+/* convert a SID to a string, either numeric or username/group */
+static void convert_sid_to_string(struct cli_state *ipc_cli,
+ POLICY_HND *pol,
+ fstring str,
+ BOOL numeric,
+ DOM_SID *sid)
+{
+ char **domains = NULL;
+ char **names = NULL;
+ uint32 *types = NULL;
+
+ sid_to_string(str, sid);
+
+ if (numeric) return; /* no lookup desired */
+
+ /* Ask LSA to convert the sid to a name */
+
+ if (!NT_STATUS_IS_OK(cli_lsa_lookup_sids(ipc_cli, ipc_cli->mem_ctx,
+ pol, 1, sid, &domains,
+ &names, &types)) ||
+ !domains || !domains[0] || !names || !names[0]) {
+ return;
+ }
+
+ /* Converted OK */
+
+ slprintf(str, sizeof(fstring) - 1, "%s%s%s",
+ domains[0], lp_winbind_separator(),
+ names[0]);
+}
+
+/* convert a string to a SID, either numeric or username/group */
+static BOOL convert_string_to_sid(struct cli_state *ipc_cli,
+ POLICY_HND *pol,
+ BOOL numeric,
+ DOM_SID *sid,
+ const char *str)
+{
+ uint32 *types = NULL;
+ DOM_SID *sids = NULL;
+ BOOL result = True;
+
+ if (numeric) {
+ if (strncmp(str, "S-", 2) == 0) {
+ return string_to_sid(sid, str);
+ }
+
+ result = False;
+ goto done;
+ }
+
+ if (!NT_STATUS_IS_OK(cli_lsa_lookup_names(ipc_cli, ipc_cli->mem_ctx,
+ pol, 1, &str, &sids,
+ &types))) {
+ result = False;
+ goto done;
+ }
+
+ sid_copy(sid, &sids[0]);
+ done:
+
+ return result;
+}
+
+
+/* parse an ACE in the same format as print_ace() */
+static BOOL parse_ace(struct cli_state *ipc_cli,
+ POLICY_HND *pol,
+ SEC_ACE *ace,
+ BOOL numeric,
+ char *str)
+{
+ char *p;
+ const char *cp;
+ fstring tok;
+ unsigned atype, aflags, amask;
+ DOM_SID sid;
+ SEC_ACCESS mask;
+ const struct perm_value *v;
+ struct perm_value {
+ const char *perm;
+ uint32 mask;
+ };
+
+ /* These values discovered by inspection */
+ static const struct perm_value special_values[] = {
+ { "R", 0x00120089 },
+ { "W", 0x00120116 },
+ { "X", 0x001200a0 },
+ { "D", 0x00010000 },
+ { "P", 0x00040000 },
+ { "O", 0x00080000 },
+ { NULL, 0 },
+ };
+
+ static const struct perm_value standard_values[] = {
+ { "READ", 0x001200a9 },
+ { "CHANGE", 0x001301bf },
+ { "FULL", 0x001f01ff },
+ { NULL, 0 },
+ };
+
+
+ ZERO_STRUCTP(ace);
+ p = strchr_m(str,':');
+ if (!p) return False;
+ *p = '\0';
+ p++;
+ /* Try to parse numeric form */
+
+ if (sscanf(p, "%i/%i/%i", &atype, &aflags, &amask) == 3 &&
+ convert_string_to_sid(ipc_cli, pol, numeric, &sid, str)) {
+ goto done;
+ }
+
+ /* Try to parse text form */
+
+ if (!convert_string_to_sid(ipc_cli, pol, numeric, &sid, str)) {
+ return False;
+ }
+
+ cp = p;
+ if (!next_token(&cp, tok, "/", sizeof(fstring))) {
+ return False;
+ }
+
+ if (StrnCaseCmp(tok, "ALLOWED", strlen("ALLOWED")) == 0) {
+ atype = SEC_ACE_TYPE_ACCESS_ALLOWED;
+ } else if (StrnCaseCmp(tok, "DENIED", strlen("DENIED")) == 0) {
+ atype = SEC_ACE_TYPE_ACCESS_DENIED;
+ } else {
+ return False;
+ }
+
+ /* Only numeric form accepted for flags at present */
+
+ if (!(next_token(&cp, tok, "/", sizeof(fstring)) &&
+ sscanf(tok, "%i", &aflags))) {
+ return False;
+ }
+
+ if (!next_token(&cp, tok, "/", sizeof(fstring))) {
+ return False;
+ }
+
+ if (strncmp(tok, "0x", 2) == 0) {
+ if (sscanf(tok, "%i", &amask) != 1) {
+ return False;
+ }
+ goto done;
+ }
+
+ for (v = standard_values; v->perm; v++) {
+ if (strcmp(tok, v->perm) == 0) {
+ amask = v->mask;
+ goto done;
+ }
+ }
+
+ p = tok;
+
+ while(*p) {
+ BOOL found = False;
+
+ for (v = special_values; v->perm; v++) {
+ if (v->perm[0] == *p) {
+ amask |= v->mask;
+ found = True;
+ }
+ }
+
+ if (!found) return False;
+ p++;
+ }
+
+ if (*p) {
+ return False;
+ }
+
+ done:
+ mask.mask = amask;
+ init_sec_ace(ace, &sid, atype, mask, aflags);
+ return True;
+}
+
+/* add an ACE to a list of ACEs in a SEC_ACL */
+static BOOL add_ace(SEC_ACL **the_acl, SEC_ACE *ace, TALLOC_CTX *ctx)
+{
+ SEC_ACL *new;
+ SEC_ACE *aces;
+ if (! *the_acl) {
+ (*the_acl) = make_sec_acl(ctx, 3, 1, ace);
+ return True;
+ }
+
+ aces = calloc(1+(*the_acl)->num_aces,sizeof(SEC_ACE));
+ memcpy(aces, (*the_acl)->ace, (*the_acl)->num_aces * sizeof(SEC_ACE));
+ memcpy(aces+(*the_acl)->num_aces, ace, sizeof(SEC_ACE));
+ new = make_sec_acl(ctx,(*the_acl)->revision,1+(*the_acl)->num_aces, aces);
+ SAFE_FREE(aces);
+ (*the_acl) = new;
+ return True;
+}
+
+
+/* parse a ascii version of a security descriptor */
+static SEC_DESC *sec_desc_parse(TALLOC_CTX *ctx,
+ struct cli_state *ipc_cli,
+ POLICY_HND *pol,
+ BOOL numeric,
+ char *str)
+{
+ const char *p = str;
+ fstring tok;
+ SEC_DESC *ret;
+ size_t sd_size;
+ DOM_SID *grp_sid=NULL, *owner_sid=NULL;
+ SEC_ACL *dacl=NULL;
+ int revision=1;
+
+ while (next_token(&p, tok, "\t,\r\n", sizeof(tok))) {
+
+ if (StrnCaseCmp(tok,"REVISION:", 9) == 0) {
+ revision = strtol(tok+9, NULL, 16);
+ continue;
+ }
+
+ if (StrnCaseCmp(tok,"OWNER:", 6) == 0) {
+ owner_sid = (DOM_SID *)calloc(1, sizeof(DOM_SID));
+ if (!owner_sid ||
+ !convert_string_to_sid(ipc_cli, pol,
+ numeric,
+ owner_sid, tok+6)) {
+ DEBUG(5, ("Failed to parse owner sid\n"));
+ return NULL;
+ }
+ continue;
+ }
+
+ if (StrnCaseCmp(tok,"OWNER+:", 7) == 0) {
+ owner_sid = (DOM_SID *)calloc(1, sizeof(DOM_SID));
+ if (!owner_sid ||
+ !convert_string_to_sid(ipc_cli, pol,
+ False,
+ owner_sid, tok+7)) {
+ DEBUG(5, ("Failed to parse owner sid\n"));
+ return NULL;
+ }
+ continue;
+ }
+
+ if (StrnCaseCmp(tok,"GROUP:", 6) == 0) {
+ grp_sid = (DOM_SID *)calloc(1, sizeof(DOM_SID));
+ if (!grp_sid ||
+ !convert_string_to_sid(ipc_cli, pol,
+ numeric,
+ grp_sid, tok+6)) {
+ DEBUG(5, ("Failed to parse group sid\n"));
+ return NULL;
+ }
+ continue;
+ }
+
+ if (StrnCaseCmp(tok,"GROUP+:", 7) == 0) {
+ grp_sid = (DOM_SID *)calloc(1, sizeof(DOM_SID));
+ if (!grp_sid ||
+ !convert_string_to_sid(ipc_cli, pol,
+ False,
+ grp_sid, tok+6)) {
+ DEBUG(5, ("Failed to parse group sid\n"));
+ return NULL;
+ }
+ continue;
+ }
+
+ if (StrnCaseCmp(tok,"ACL:", 4) == 0) {
+ SEC_ACE ace;
+ if (!parse_ace(ipc_cli, pol, &ace, numeric, tok+4)) {
+ DEBUG(5, ("Failed to parse ACL %s\n", tok));
+ return NULL;
+ }
+ if(!add_ace(&dacl, &ace, ctx)) {
+ DEBUG(5, ("Failed to add ACL %s\n", tok));
+ return NULL;
+ }
+ continue;
+ }
+
+ if (StrnCaseCmp(tok,"ACL+:", 5) == 0) {
+ SEC_ACE ace;
+ if (!parse_ace(ipc_cli, pol, &ace, False, tok+5)) {
+ DEBUG(5, ("Failed to parse ACL %s\n", tok));
+ return NULL;
+ }
+ if(!add_ace(&dacl, &ace, ctx)) {
+ DEBUG(5, ("Failed to add ACL %s\n", tok));
+ return NULL;
+ }
+ continue;
+ }
+
+ DEBUG(5, ("Failed to parse security descriptor\n"));
+ return NULL;
+ }
+
+ ret = make_sec_desc(ctx, revision, SEC_DESC_SELF_RELATIVE,
+ owner_sid, grp_sid, NULL, dacl, &sd_size);
+
+ SAFE_FREE(grp_sid);
+ SAFE_FREE(owner_sid);
+
+ return ret;
+}
+
+
+/*****************************************************
+retrieve the acls for a file
+*******************************************************/
+static int cacl_get(TALLOC_CTX *ctx, struct cli_state *cli,
+ struct cli_state *ipc_cli, POLICY_HND *pol,
+ char *filename, char *name, char *buf, int bufsize)
+{
+ uint32 i;
+ int n = 0;
+ int n_used;
+ BOOL all;
+ BOOL numeric = True;
+ BOOL determine_size = (bufsize == 0);
+ int fnum = -1;
+ SEC_DESC *sd;
+ fstring sidstr;
+ char *p;
+
+ fnum = cli_nt_create(cli, filename, CREATE_ACCESS_READ);
+
+ if (fnum == -1) {
+ DEBUG(5, ("cacl_get failed to open %s: %s\n",
+ filename, cli_errstr(cli)));
+ errno = 0;
+ return -1;
+ }
+
+ sd = cli_query_secdesc(cli, fnum, ctx);
+
+ if (!sd) {
+ DEBUG(5, ("cacl_get Failed to query old descriptor\n"));
+ errno = 0;
+ return -1;
+ }
+
+ cli_close(cli, fnum);
+
+ all = (*name == '*');
+ numeric = (* (name + strlen(name) - 1) != '+');
+
+ n_used = 0;
+
+ if (all) {
+ if (determine_size) {
+ p = talloc_asprintf(ctx,
+ "REVISION:%d", sd->revision);
+ if (!p) {
+ errno = ENOMEM;
+ return -1;
+ }
+ n = strlen(p);
+ } else {
+ n = snprintf(buf, bufsize,
+ "REVISION:%d", sd->revision);
+ }
+ } else if (StrCaseCmp(name, "revision") == 0) {
+ if (determine_size) {
+ p = talloc_asprintf(ctx, "%d", sd->revision);
+ if (!p) {
+ errno = ENOMEM;
+ return -1;
+ }
+ n = strlen(p);
+ } else {
+ n = snprintf(buf, bufsize, "%d", sd->revision);
+ }
+ }
+
+ if (!determine_size && n > bufsize) {
+ errno = ERANGE;
+ return -1;
+ }
+ buf += n;
+ n_used += n;
+ bufsize -= n;
+
+ /* Get owner and group sid */
+
+ if (sd->owner_sid) {
+ convert_sid_to_string(ipc_cli, pol,
+ sidstr, numeric, sd->owner_sid);
+ } else {
+ fstrcpy(sidstr, "");
+ }
+
+ if (all) {
+ if (determine_size) {
+ p = talloc_asprintf(ctx, ",OWNER:%s", sidstr);
+ if (!p) {
+ errno = ENOMEM;
+ return -1;
+ }
+ n = strlen(p);
+ } else {
+ n = snprintf(buf, bufsize, ",OWNER:%s", sidstr);
+ }
+ } else if (StrnCaseCmp(name, "owner", 5) == 0) {
+ if (determine_size) {
+ p = talloc_asprintf(ctx, "%s", sidstr);
+ if (!p) {
+ errno = ENOMEM;
+ return -1;
+ }
+ n = strlen(p);
+ } else {
+ n = snprintf(buf, bufsize, "%s", sidstr);
+ }
+ }
+
+ if (!determine_size && n > bufsize) {
+ errno = ERANGE;
+ return -1;
+ }
+ buf += n;
+ n_used += n;
+ bufsize -= n;
+
+ if (sd->grp_sid) {
+ convert_sid_to_string(ipc_cli, pol,
+ sidstr, numeric, sd->grp_sid);
+ } else {
+ fstrcpy(sidstr, "");
+ }
+
+ if (all) {
+ if (determine_size) {
+ p = talloc_asprintf(ctx, ",GROUP:%s", sidstr);
+ if (!p) {
+ errno = ENOMEM;
+ return -1;
+ }
+ n = strlen(p);
+ } else {
+ n = snprintf(buf, bufsize, ",GROUP:%s", sidstr);
+ }
+ } else if (StrnCaseCmp(name, "group", 5) == 0) {
+ if (determine_size) {
+ p = talloc_asprintf(ctx, "%s", sidstr);
+ if (!p) {
+ errno = ENOMEM;
+ return -1;
+ }
+ n = strlen(p);
+ } else {
+ n = snprintf(buf, bufsize, "%s", sidstr);
+ }
+ }
+
+ if (!determine_size && n > bufsize) {
+ errno = ERANGE;
+ return -1;
+ }
+ buf += n;
+ n_used += n;
+ bufsize -= n;
+
+ /* Add aces to value buffer */
+ for (i = 0; sd->dacl && i < sd->dacl->num_aces; i++) {
+
+ SEC_ACE *ace = &sd->dacl->ace[i];
+ convert_sid_to_string(ipc_cli, pol,
+ sidstr, numeric, &ace->trustee);
+
+ if (all) {
+ if (determine_size) {
+ p = talloc_asprintf(ctx,
+ ",ACL:%s:%d/%d/0x%08x",
+ sidstr,
+ ace->type,
+ ace->flags,
+ ace->info.mask);
+ if (!p) {
+ errno = ENOMEM;
+ return -1;
+ }
+ n = strlen(p);
+ } else {
+ n = snprintf(buf, bufsize,
+ ",ACL:%s:%d/%d/0x%08x",
+ sidstr,
+ ace->type,
+ ace->flags,
+ ace->info.mask);
+ }
+ } else if ((StrnCaseCmp(name, "acl", 3) == 0 &&
+ StrCaseCmp(name + 3, sidstr) == 0) ||
+ (StrnCaseCmp(name, "acl+", 4) == 0 &&
+ StrCaseCmp(name + 4, sidstr) == 0)) {
+ if (determine_size) {
+ p = talloc_asprintf(ctx,
+ "%d/%d/0x%08x",
+ ace->type,
+ ace->flags,
+ ace->info.mask);
+ if (!p) {
+ errno = ENOMEM;
+ return -1;
+ }
+ n = strlen(p);
+ } else {
+ n = snprintf(buf, bufsize,
+ "%d/%d/0x%08x",
+ ace->type, ace->flags, ace->info.mask);
+ }
+ }
+ if (n > bufsize) {
+ errno = ERANGE;
+ return -1;
+ }
+ buf += n;
+ n_used += n;
+ bufsize -= n;
+ }
+
+ if (n_used == 0) {
+ errno = ENOATTR;
+ return -1;
+ }
+ return n_used;
+}
+
+
+/*****************************************************
+set the ACLs on a file given an ascii description
+*******************************************************/
+static int cacl_set(TALLOC_CTX *ctx, struct cli_state *cli,
+ struct cli_state *ipc_cli, POLICY_HND *pol,
+ const char *filename, const char *the_acl,
+ int mode, int flags)
+{
+ int fnum;
+ int err = 0;
+ SEC_DESC *sd = NULL, *old;
+ SEC_ACL *dacl = NULL;
+ DOM_SID *owner_sid = NULL;
+ DOM_SID *grp_sid = NULL;
+ uint32 i, j;
+ size_t sd_size;
+ int ret = 0;
+ char *p;
+ BOOL numeric = True;
+
+ /* the_acl will be null for REMOVE_ALL operations */
+ if (the_acl) {
+ numeric = ((p = strchr(the_acl, ':')) != NULL &&
+ p > the_acl &&
+ p[-1] != '+');
+
+ /* if this is to set the entire ACL... */
+ if (*the_acl == '*') {
+ /* ... then increment past the first colon */
+ the_acl = p + 1;
+ }
+
+ sd = sec_desc_parse(ctx, ipc_cli, pol,
+ numeric, (char *) the_acl);
+
+ if (!sd) {
+ errno = EINVAL;
+ return -1;
+ }
+ }
+
+ /* The desired access below is the only one I could find that works
+ with NT4, W2KP and Samba */
+
+ fnum = cli_nt_create(cli, filename, CREATE_ACCESS_READ);
+
+ if (fnum == -1) {
+ DEBUG(5, ("cacl_set failed to open %s: %s\n",
+ filename, cli_errstr(cli)));
+ errno = 0;
+ return -1;
+ }
+
+ old = cli_query_secdesc(cli, fnum, ctx);
+
+ if (!old) {
+ DEBUG(5, ("cacl_set Failed to query old descriptor\n"));
+ errno = 0;
+ return -1;
+ }
+
+ cli_close(cli, fnum);
+
+ switch (mode) {
+ case SMBC_XATTR_MODE_REMOVE_ALL:
+ old->dacl->num_aces = 0;
+ SAFE_FREE(old->dacl->ace);
+ SAFE_FREE(old->dacl);
+ old->off_dacl = 0;
+ dacl = old->dacl;
+ break;
+
+ case SMBC_XATTR_MODE_REMOVE:
+ for (i=0;sd->dacl && i<sd->dacl->num_aces;i++) {
+ BOOL found = False;
+
+ for (j=0;old->dacl && j<old->dacl->num_aces;j++) {
+ if (sec_ace_equal(&sd->dacl->ace[i],
+ &old->dacl->ace[j])) {
+ uint32 k;
+ for (k=j; k<old->dacl->num_aces-1;k++) {
+ old->dacl->ace[k] = old->dacl->ace[k+1];
+ }
+ old->dacl->num_aces--;
+ if (old->dacl->num_aces == 0) {
+ SAFE_FREE(old->dacl->ace);
+ SAFE_FREE(old->dacl);
+ old->off_dacl = 0;
+ }
+ found = True;
+ dacl = old->dacl;
+ break;
+ }
+ }
+
+ if (!found) {
+ err = ENOATTR;
+ ret = -1;
+ goto failed;
+ }
+ }
+ break;
+
+ case SMBC_XATTR_MODE_ADD:
+ for (i=0;sd->dacl && i<sd->dacl->num_aces;i++) {
+ BOOL found = False;
+
+ for (j=0;old->dacl && j<old->dacl->num_aces;j++) {
+ if (sid_equal(&sd->dacl->ace[i].trustee,
+ &old->dacl->ace[j].trustee)) {
+ if (!(flags & SMBC_XATTR_FLAG_CREATE)) {
+ err = EEXIST;
+ ret = -1;
+ goto failed;
+ }
+ old->dacl->ace[j] = sd->dacl->ace[i];
+ ret = -1;
+ found = True;
+ }
+ }
+
+ if (!found && (flags & SMBC_XATTR_FLAG_REPLACE)) {
+ err = ENOATTR;
+ ret = -1;
+ goto failed;
+ }
+
+ for (i=0;sd->dacl && i<sd->dacl->num_aces;i++) {
+ add_ace(&old->dacl, &sd->dacl->ace[i], ctx);
+ }
+ }
+ dacl = old->dacl;
+ break;
+
+ case SMBC_XATTR_MODE_SET:
+ old = sd;
+ owner_sid = old->owner_sid;
+ grp_sid = old->grp_sid;
+ dacl = old->dacl;
+ break;
+
+ case SMBC_XATTR_MODE_CHOWN:
+ owner_sid = sd->owner_sid;
+ break;
+
+ case SMBC_XATTR_MODE_CHGRP:
+ grp_sid = sd->grp_sid;
+ break;
+ }
+
+ /* Denied ACE entries must come before allowed ones */
+ sort_acl(old->dacl);
+
+ /* Create new security descriptor and set it */
+ sd = make_sec_desc(ctx, old->revision, SEC_DESC_SELF_RELATIVE,
+ owner_sid, grp_sid, NULL, dacl, &sd_size);
+
+ fnum = cli_nt_create(cli, filename,
+ WRITE_DAC_ACCESS | WRITE_OWNER_ACCESS);
+
+ if (fnum == -1) {
+ DEBUG(5, ("cacl_set failed to open %s: %s\n",
+ filename, cli_errstr(cli)));
+ errno = 0;
+ return -1;
+ }
+
+ if (!cli_set_secdesc(cli, fnum, sd)) {
+ DEBUG(5, ("ERROR: secdesc set failed: %s\n", cli_errstr(cli)));
+ ret = -1;
+ }
+
+ /* Clean up */
+
+ failed:
+ cli_close(cli, fnum);
+
+ if (err != 0) {
+ errno = err;
+ }
+
+ return ret;
+}
+
+
+int smbc_setxattr_ctx(SMBCCTX *context,
+ const char *fname,
+ const char *name,
+ const void *value,
+ size_t size,
+ int flags)
+{
+ int ret;
+ SMBCSRV *srv;
+ SMBCSRV *ipc_srv;
+ fstring server, share, user, password, workgroup;
+ pstring path;
+ TALLOC_CTX *ctx;
+ POLICY_HND pol;
+
+ if (!context || !context->internal ||
+ !context->internal->_initialized) {
+
+ errno = EINVAL; /* Best I can think of ... */
+ return -1;
+
+ }
+
+ if (!fname) {
+
+ errno = EINVAL;
+ return -1;
+
+ }
+
+ DEBUG(4, ("smbc_setxattr(%s, %s, %.*s)\n",
+ fname, name, (int) size, (char *) value));
+
+ if (smbc_parse_path(context, fname,
+ server, sizeof(server),
+ share, sizeof(share),
+ path, sizeof(path),
+ user, sizeof(user),
+ password, sizeof(password),
+ NULL, 0)) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ if (user[0] == (char)0) fstrcpy(user, context->user);
+
+ fstrcpy(workgroup, context->workgroup);
+
+ srv = smbc_server(context, server, share, workgroup, user, password);
+ if (!srv) {
+ return -1; /* errno set by smbc_server */
+ }
+
+ ipc_srv = smbc_attr_server(context, server, share,
+ workgroup, user, password,
+ &pol);
+ if (!ipc_srv) {
+ return -1;
+ }
+
+ ctx = talloc_init("smbc_setxattr");
+ if (!ctx) {
+ errno = ENOMEM;
+ return -1;
+ }
+
+ /*
+ * Are they asking to set an access control element or to set
+ * the entire access control list?
+ */
+ if (StrCaseCmp(name, "system.nt_sec_desc.*") == 0 ||
+ StrCaseCmp(name, "system.nt_sec_desc.*+") == 0 ||
+ StrCaseCmp(name, "system.nt_sec_desc.revision") == 0 ||
+ StrnCaseCmp(name, "system.nt_sec_desc.acl", 22) == 0 ||
+ StrnCaseCmp(name, "system.nt_sec_desc.acl+", 23) == 0) {
+
+ /* Yup. */
+ char *namevalue =
+ talloc_asprintf(ctx, "%s:%s", name+19, (char *) value);
+ if (! namevalue) {
+ errno = ENOMEM;
+ ret = -1;
+ } else {
+ ret = cacl_set(ctx, &srv->cli,
+ &ipc_srv->cli, &pol, path,
+ namevalue,
+ (*namevalue == '*'
+ ? SMBC_XATTR_MODE_SET
+ : SMBC_XATTR_MODE_ADD),
+ flags);
+ }
+ talloc_destroy(ctx);
+ return ret;
+ }
+
+ /*
+ * Are they asking to set the owner?
+ */
+ if (StrCaseCmp(name, "system.nt_sec_desc.owner") == 0 ||
+ StrCaseCmp(name, "system.nt_sec_desc.owner+") == 0) {
+
+ /* Yup. */
+ char *namevalue =
+ talloc_asprintf(ctx, "%s:%s", name+19, (char *) value);
+ if (! namevalue) {
+ errno = ENOMEM;
+ ret = -1;
+ } else {
+ ret = cacl_set(ctx, &srv->cli,
+ &ipc_srv->cli, &pol, path,
+ namevalue, SMBC_XATTR_MODE_CHOWN, 0);
+ }
+ talloc_destroy(ctx);
+ return ret;
+ }
+
+ /*
+ * Are they asking to set the group?
+ */
+ if (StrCaseCmp(name, "system.nt_sec_desc.group") == 0 ||
+ StrCaseCmp(name, "system.nt_sec_desc.group+") == 0) {
+
+ /* Yup. */
+ char *namevalue =
+ talloc_asprintf(ctx, "%s:%s", name+19, (char *) value);
+ if (! namevalue) {
+ errno = ENOMEM;
+ ret = -1;
+ } else {
+ ret = cacl_set(ctx, &srv->cli,
+ &ipc_srv->cli, &pol, path,
+ namevalue, SMBC_XATTR_MODE_CHOWN, 0);
+ }
+ talloc_destroy(ctx);
+ return ret;
+ }
+
+ /* Unsupported attribute name */
+ talloc_destroy(ctx);
+ errno = EINVAL;
+ return -1;
+}
+
+int smbc_getxattr_ctx(SMBCCTX *context,
+ const char *fname,
+ const char *name,
+ const void *value,
+ size_t size)
+{
+ int ret;
+ SMBCSRV *srv;
+ SMBCSRV *ipc_srv;
+ fstring server, share, user, password, workgroup;
+ pstring path;
+ TALLOC_CTX *ctx;
+ POLICY_HND pol;
+
+ if (!context || !context->internal ||
+ !context->internal->_initialized) {
+
+ errno = EINVAL; /* Best I can think of ... */
+ return -1;
+
+ }
+
+ if (!fname) {
+
+ errno = EINVAL;
+ return -1;
+
+ }
+
+ DEBUG(4, ("smbc_getxattr(%s, %s)\n", fname, name));
+
+ if (smbc_parse_path(context, fname,
+ server, sizeof(server),
+ share, sizeof(share),
+ path, sizeof(path),
+ user, sizeof(user),
+ password, sizeof(password),
+ NULL, 0)) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ if (user[0] == (char)0) fstrcpy(user, context->user);
+
+ fstrcpy(workgroup, context->workgroup);
+
+ srv = smbc_server(context, server, share, workgroup, user, password);
+ if (!srv) {
+ return -1; /* errno set by smbc_server */
+ }
+
+ ipc_srv = smbc_attr_server(context, server, share,
+ workgroup, user, password,
+ &pol);
+ if (!ipc_srv) {
+ return -1;
+ }
+
+ ctx = talloc_init("smbc:getxattr");
+ if (!ctx) {
+ errno = ENOMEM;
+ return -1;
+ }
+
+ /* Are they requesting a supported attribute? */
+ if (StrCaseCmp(name, "system.nt_sec_desc.*") == 0 ||
+ StrCaseCmp(name, "system.nt_sec_desc.*+") == 0 ||
+ StrCaseCmp(name, "system.nt_sec_desc.revision") == 0 ||
+ StrCaseCmp(name, "system.nt_sec_desc.owner") == 0 ||
+ StrCaseCmp(name, "system.nt_sec_desc.owner+") == 0 ||
+ StrCaseCmp(name, "system.nt_sec_desc.group") == 0 ||
+ StrCaseCmp(name, "system.nt_sec_desc.group+") == 0 ||
+ StrnCaseCmp(name, "system.nt_sec_desc.acl", 22) == 0 ||
+ StrnCaseCmp(name, "system.nt_sec_desc.acl+", 23) == 0) {
+
+ /* Yup. */
+ ret = cacl_get(ctx, &srv->cli,
+ &ipc_srv->cli, &pol,
+ (char *) path, (char *) name + 19,
+ (char *) value, size);
+ if (ret < 0 && errno == 0) {
+ errno = smbc_errno(context, &srv->cli);
+ }
+ talloc_destroy(ctx);
+ return ret;
+ }
+
+ /* Unsupported attribute name */
+ talloc_destroy(ctx);
+ errno = EINVAL;
+ return -1;
+}
+
+
+int smbc_removexattr_ctx(SMBCCTX *context,
+ const char *fname,
+ const char *name)
+{
+ int ret;
+ SMBCSRV *srv;
+ SMBCSRV *ipc_srv;
+ fstring server, share, user, password, workgroup;
+ pstring path;
+ TALLOC_CTX *ctx;
+ POLICY_HND pol;
+
+ if (!context || !context->internal ||
+ !context->internal->_initialized) {
+
+ errno = EINVAL; /* Best I can think of ... */
+ return -1;
+
+ }
+
+ if (!fname) {
+
+ errno = EINVAL;
+ return -1;
+
+ }
+
+ DEBUG(4, ("smbc_removexattr(%s, %s)\n", fname, name));
+
+ if (smbc_parse_path(context, fname,
+ server, sizeof(server),
+ share, sizeof(share),
+ path, sizeof(path),
+ user, sizeof(user),
+ password, sizeof(password),
+ NULL, 0)) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ if (user[0] == (char)0) fstrcpy(user, context->user);
+
+ fstrcpy(workgroup, context->workgroup);
+
+ srv = smbc_server(context, server, share, workgroup, user, password);
+ if (!srv) {
+ return -1; /* errno set by smbc_server */
+ }
+
+ ipc_srv = smbc_attr_server(context, server, share,
+ workgroup, user, password,
+ &pol);
+ if (!ipc_srv) {
+ return -1;
+ }
+
+ ipc_srv = smbc_attr_server(context, server, share,
+ workgroup, user, password,
+ &pol);
+ if (!ipc_srv) {
+ return -1;
+ }
+
+ ctx = talloc_init("smbc_removexattr");
+ if (!ctx) {
+ errno = ENOMEM;
+ return -1;
+ }
+
+ /* Are they asking to set the entire ACL? */
+ if (StrCaseCmp(name, "system.nt_sec_desc.*") == 0 ||
+ StrCaseCmp(name, "system.nt_sec_desc.*+") == 0) {
+
+ /* Yup. */
+ ret = cacl_set(ctx, &srv->cli,
+ &ipc_srv->cli, &pol, path,
+ NULL, SMBC_XATTR_MODE_REMOVE_ALL, 0);
+ talloc_destroy(ctx);
+ return ret;
+ }
+
+ /*
+ * Are they asking to remove one or more spceific security descriptor
+ * attributes?
+ */
+ if (StrCaseCmp(name, "system.nt_sec_desc.revision") == 0 ||
+ StrCaseCmp(name, "system.nt_sec_desc.owner") == 0 ||
+ StrCaseCmp(name, "system.nt_sec_desc.owner+") == 0 ||
+ StrCaseCmp(name, "system.nt_sec_desc.group") == 0 ||
+ StrCaseCmp(name, "system.nt_sec_desc.group+") == 0 ||
+ StrnCaseCmp(name, "system.nt_sec_desc.acl", 22) == 0 ||
+ StrnCaseCmp(name, "system.nt_sec_desc.acl+", 23) == 0) {
+
+ /* Yup. */
+ ret = cacl_set(ctx, &srv->cli,
+ &ipc_srv->cli, &pol, path,
+ name + 19, SMBC_XATTR_MODE_REMOVE, 0);
+ talloc_destroy(ctx);
+ return ret;
+ }
+
+ /* Unsupported attribute name */
+ talloc_destroy(ctx);
+ errno = EINVAL;
+ return -1;
+}
+
+int smbc_listxattr_ctx(SMBCCTX *context,
+ const char *fname,
+ char *list,
+ size_t size)
+{
+ /*
+ * This isn't quite what listxattr() is supposed to do. This returns
+ * the complete set of attributes, always, rather than only those
+ * attribute names which actually exist for a file. Hmmm...
+ */
+ const char supported[] =
+ "system.nt_sec_desc.revision\0"
+ "system.nt_sec_desc.owner\0"
+ "system.nt_sec_desc.owner+\0"
+ "system.nt_sec_desc.group\0"
+ "system.nt_sec_desc.group+\0"
+ "system.nt_sec_desc.acl\0"
+ "system.nt_sec_desc.acl+\0"
+ "system.nt_sec_desc.*\0"
+ "system.nt_sec_desc.*+\0"
+ ;
+
+ if (size == 0) {
+ return sizeof(supported);
+ }
+
+ if (sizeof(supported) > size) {
+ errno = ERANGE;
+ return -1;
+ }
+
+ /* this can't be strcpy() because there are embedded null characters */
+ memcpy(list, supported, sizeof(supported));
+ return sizeof(supported);
+}
+
+
+/*
+ * Open a print file to be written to by other calls
+ */
+
+static SMBCFILE *smbc_open_print_job_ctx(SMBCCTX *context, const char *fname)
+{
+ fstring server, share, user, password;
+ pstring path;
+
+ if (!context || !context->internal ||
+ !context->internal->_initialized) {
+
+ errno = EINVAL;
+ return NULL;
+
+ }
+
+ if (!fname) {
+
+ errno = EINVAL;
+ return NULL;
+
+ }
+
+ DEBUG(4, ("smbc_open_print_job_ctx(%s)\n", fname));
+
+ if (smbc_parse_path(context, fname,
+ server, sizeof(server),
+ share, sizeof(share),
+ path, sizeof(path),
+ user, sizeof(user),
+ password, sizeof(password),
+ NULL, 0)) {
+ errno = EINVAL;
+ return NULL;
+ }
+
+ /* What if the path is empty, or the file exists? */
+
+ return context->open(context, fname, O_WRONLY, 666);
+
+}
+
+/*
+ * Routine to print a file on a remote server ...
+ *
+ * We open the file, which we assume to be on a remote server, and then
+ * copy it to a print file on the share specified by printq.
+ */
+
+static int smbc_print_file_ctx(SMBCCTX *c_file, const char *fname, SMBCCTX *c_print, const char *printq)
+{
+ SMBCFILE *fid1, *fid2;
+ int bytes, saverr, tot_bytes = 0;
+ char buf[4096];
+
+ if (!c_file || !c_file->internal->_initialized || !c_print ||
+ !c_print->internal->_initialized) {
+
+ errno = EINVAL;
+ return -1;
+
+ }
+
+ if (!fname && !printq) {
+
+ errno = EINVAL;
+ return -1;
+
+ }
+
+ /* Try to open the file for reading ... */
+
+ if ((int)(fid1 = c_file->open(c_file, fname, O_RDONLY, 0666)) < 0) {
+
+ DEBUG(3, ("Error, fname=%s, errno=%i\n", fname, errno));
+ return -1; /* smbc_open sets errno */
+
+ }
+
+ /* Now, try to open the printer file for writing */
+
+ if ((int)(fid2 = c_print->open_print_job(c_print, printq)) < 0) {
+
+ saverr = errno; /* Save errno */
+ c_file->close(c_file, fid1);
+ errno = saverr;
+ return -1;
+
+ }
+
+ while ((bytes = c_file->read(c_file, fid1, buf, sizeof(buf))) > 0) {
+
+ tot_bytes += bytes;
+
+ if ((c_print->write(c_print, fid2, buf, bytes)) < 0) {
+
+ saverr = errno;
+ c_file->close(c_file, fid1);
+ c_print->close(c_print, fid2);
+ errno = saverr;
+
+ }
+
+ }
+
+ saverr = errno;
+
+ c_file->close(c_file, fid1); /* We have to close these anyway */
+ c_print->close(c_print, fid2);
+
+ if (bytes < 0) {
+
+ errno = saverr;
+ return -1;
+
+ }
+
+ return tot_bytes;
+
+}
+
+/*
+ * Routine to list print jobs on a printer share ...
+ */
+
+static int smbc_list_print_jobs_ctx(SMBCCTX *context, const char *fname, smbc_list_print_job_fn fn)
+{
+ SMBCSRV *srv;
+ fstring server, share, user, password, workgroup;
+ pstring path;
+
+ if (!context || !context->internal ||
+ !context->internal->_initialized) {
+
+ errno = EINVAL;
+ return -1;
+
+ }
+
+ if (!fname) {
+
+ errno = EINVAL;
+ return -1;
+
+ }
+
+ DEBUG(4, ("smbc_list_print_jobs(%s)\n", fname));
+
+ if (smbc_parse_path(context, fname,
+ server, sizeof(server),
+ share, sizeof(share),
+ path, sizeof(path),
+ user, sizeof(user),
+ password, sizeof(password),
+ NULL, 0)) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ if (user[0] == (char)0) fstrcpy(user, context->user);
+
+ fstrcpy(workgroup, context->workgroup);
+
+ srv = smbc_server(context, server, share, workgroup, user, password);
+
+ if (!srv) {
+
+ return -1; /* errno set by smbc_server */
+
+ }
+
+ if (cli_print_queue(&srv->cli, (void (*)(struct print_job_info *))fn) < 0) {
+
+ errno = smbc_errno(context, &srv->cli);
+ return -1;
+
+ }
+
+ return 0;
+
+}
+
+/*
+ * Delete a print job from a remote printer share
+ */
+
+static int smbc_unlink_print_job_ctx(SMBCCTX *context, const char *fname, int id)
+{
+ SMBCSRV *srv;
+ fstring server, share, user, password, workgroup;
+ pstring path;
+ int err;
+
+ if (!context || !context->internal ||
+ !context->internal->_initialized) {
+
+ errno = EINVAL;
+ return -1;
+
+ }
+
+ if (!fname) {
+
+ errno = EINVAL;
+ return -1;
+
+ }
+
+ DEBUG(4, ("smbc_unlink_print_job(%s)\n", fname));
+
+ if (smbc_parse_path(context, fname,
+ server, sizeof(server),
+ share, sizeof(share),
+ path, sizeof(path),
+ user, sizeof(user),
+ password, sizeof(password),
+ NULL, 0)) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ if (user[0] == (char)0) fstrcpy(user, context->user);
+
+ fstrcpy(workgroup, context->workgroup);
+
+ srv = smbc_server(context, server, share, workgroup, user, password);
+
+ if (!srv) {
+
+ return -1; /* errno set by smbc_server */
+
+ }
+
+ if ((err = cli_printjob_del(&srv->cli, id)) != 0) {
+
+ if (err < 0)
+ errno = smbc_errno(context, &srv->cli);
+ else if (err == ERRnosuchprintjob)
+ errno = EINVAL;
+ return -1;
+
+ }
+
+ return 0;
+
+}
+
+/*
+ * Get a new empty handle to fill in with your own info
+ */
+SMBCCTX * smbc_new_context(void)
+{
+ SMBCCTX * context;
+
+ context = malloc(sizeof(SMBCCTX));
+ if (!context) {
+ errno = ENOMEM;
+ return NULL;
+ }
+
+ ZERO_STRUCTP(context);
+
+ context->internal = malloc(sizeof(struct smbc_internal_data));
+ if (!context->internal) {
+ errno = ENOMEM;
+ return NULL;
+ }
+
+ ZERO_STRUCTP(context->internal);
+
+
+ /* ADD REASONABLE DEFAULTS */
+ context->debug = 0;
+ context->timeout = 20000; /* 20 seconds */
+
+ context->open = smbc_open_ctx;
+ context->creat = smbc_creat_ctx;
+ context->read = smbc_read_ctx;
+ context->write = smbc_write_ctx;
+ context->close = smbc_close_ctx;
+ context->unlink = smbc_unlink_ctx;
+ context->rename = smbc_rename_ctx;
+ context->lseek = smbc_lseek_ctx;
+ context->stat = smbc_stat_ctx;
+ context->fstat = smbc_fstat_ctx;
+ context->opendir = smbc_opendir_ctx;
+ context->closedir = smbc_closedir_ctx;
+ context->readdir = smbc_readdir_ctx;
+ context->getdents = smbc_getdents_ctx;
+ context->mkdir = smbc_mkdir_ctx;
+ context->rmdir = smbc_rmdir_ctx;
+ context->telldir = smbc_telldir_ctx;
+ context->lseekdir = smbc_lseekdir_ctx;
+ context->fstatdir = smbc_fstatdir_ctx;
+ context->chmod = smbc_chmod_ctx;
+ context->utimes = smbc_utimes_ctx;
+ context->setxattr = smbc_setxattr_ctx;
+ context->getxattr = smbc_getxattr_ctx;
+ context->removexattr = smbc_removexattr_ctx;
+ context->listxattr = smbc_listxattr_ctx;
+ context->open_print_job = smbc_open_print_job_ctx;
+ context->print_file = smbc_print_file_ctx;
+ context->list_print_jobs = smbc_list_print_jobs_ctx;
+ context->unlink_print_job = smbc_unlink_print_job_ctx;
+
+ context->callbacks.check_server_fn = smbc_check_server;
+ context->callbacks.remove_unused_server_fn = smbc_remove_unused_server;
+
+ smbc_default_cache_functions(context);
+
+ return context;
+}
+
+/*
+ * Free a context
+ *
+ * Returns 0 on success. Otherwise returns 1, the SMBCCTX is _not_ freed
+ * and thus you'll be leaking memory if not handled properly.
+ *
+ */
+int smbc_free_context(SMBCCTX * context, int shutdown_ctx)
+{
+ if (!context) {
+ errno = EBADF;
+ return 1;
+ }
+
+ if (shutdown_ctx) {
+ SMBCFILE * f;
+ DEBUG(1,("Performing aggressive shutdown.\n"));
+
+ f = context->internal->_files;
+ while (f) {
+ context->close(context, f);
+ f = f->next;
+ }
+ context->internal->_files = NULL;
+
+ /* First try to remove the servers the nice way. */
+ if (context->callbacks.purge_cached_fn(context)) {
+ SMBCSRV * s;
+ SMBCSRV * next;
+ DEBUG(1, ("Could not purge all servers, Nice way shutdown failed.\n"));
+ s = context->internal->_servers;
+ while (s) {
+ DEBUG(1, ("Forced shutdown: %p (fd=%d)\n", s, s->cli.fd));
+ cli_shutdown(&s->cli);
+ context->callbacks.remove_cached_srv_fn(context, s);
+ next = s->next;
+ DLIST_REMOVE(context->internal->_servers, s);
+ SAFE_FREE(s);
+ s = next;
+ }
+ context->internal->_servers = NULL;
+ }
+ }
+ else {
+ /* This is the polite way */
+ if (context->callbacks.purge_cached_fn(context)) {
+ DEBUG(1, ("Could not purge all servers, free_context failed.\n"));
+ errno = EBUSY;
+ return 1;
+ }
+ if (context->internal->_servers) {
+ DEBUG(1, ("Active servers in context, free_context failed.\n"));
+ errno = EBUSY;
+ return 1;
+ }
+ if (context->internal->_files) {
+ DEBUG(1, ("Active files in context, free_context failed.\n"));
+ errno = EBUSY;
+ return 1;
+ }
+ }
+
+ /* Things we have to clean up */
+ SAFE_FREE(context->workgroup);
+ SAFE_FREE(context->netbios_name);
+ SAFE_FREE(context->user);
+
+ DEBUG(3, ("Context %p succesfully freed\n", context));
+ SAFE_FREE(context->internal);
+ SAFE_FREE(context);
+ return 0;
+}
+
+
+/*
+ * Initialise the library etc
+ *
+ * We accept a struct containing handle information.
+ * valid values for info->debug from 0 to 100,
+ * and insist that info->fn must be non-null.
+ */
+SMBCCTX * smbc_init_context(SMBCCTX * context)
+{
+ pstring conf;
+ int pid;
+ char *user = NULL, *home = NULL;
+
+ if (!context || !context->internal) {
+ errno = EBADF;
+ return NULL;
+ }
+
+ /* Do not initialise the same client twice */
+ if (context->internal->_initialized) {
+ return 0;
+ }
+
+ if (!context->callbacks.auth_fn || context->debug < 0 || context->debug > 100) {
+
+ errno = EINVAL;
+ return NULL;
+
+ }
+
+ if (!smbc_initialized) {
+ /* Do some library wide intialisations the first time we get called */
+
+ /* Set this to what the user wants */
+ DEBUGLEVEL = context->debug;
+
+ setup_logging( "libsmbclient", True);
+
+ /* Here we would open the smb.conf file if needed ... */
+
+ home = getenv("HOME");
+
+ slprintf(conf, sizeof(conf), "%s/.smb/smb.conf", home);
+
+ load_interfaces(); /* Load the list of interfaces ... */
+
+ in_client = True; /* FIXME, make a param */
+
+ if (!lp_load(conf, True, False, False)) {
+
+ /*
+ * Well, if that failed, try the dyn_CONFIGFILE
+ * Which points to the standard locn, and if that
+ * fails, silently ignore it and use the internal
+ * defaults ...
+ */
+
+ if (!lp_load(dyn_CONFIGFILE, True, False, False)) {
+ DEBUG(5, ("Could not load either config file: %s or %s\n",
+ conf, dyn_CONFIGFILE));
+ }
+ }
+
+ reopen_logs(); /* Get logging working ... */
+
+ /*
+ * Block SIGPIPE (from lib/util_sock.c: write())
+ * It is not needed and should not stop execution
+ */
+ BlockSignals(True, SIGPIPE);
+
+ /* Done with one-time initialisation */
+ smbc_initialized = 1;
+
+ }
+
+ if (!context->user) {
+ /*
+ * FIXME: Is this the best way to get the user info?
+ */
+ user = getenv("USER");
+ /* walk around as "guest" if no username can be found */
+ if (!user) context->user = strdup("guest");
+ else context->user = strdup(user);
+ }
+
+ if (!context->netbios_name) {
+ /*
+ * We try to get our netbios name from the config. If that fails we fall
+ * back on constructing our netbios name from our hostname etc
+ */
+ if (global_myname()) {
+ context->netbios_name = strdup(global_myname());
+ }
+ else {
+ /*
+ * Hmmm, I want to get hostname as well, but I am too lazy for the moment
+ */
+ pid = sys_getpid();
+ context->netbios_name = malloc(17);
+ if (!context->netbios_name) {
+ errno = ENOMEM;
+ return NULL;
+ }
+ slprintf(context->netbios_name, 16, "smbc%s%d", context->user, pid);
+ }
+ }
+
+ DEBUG(1, ("Using netbios name %s.\n", context->netbios_name));
+
+ if (!context->workgroup) {
+ if (lp_workgroup()) {
+ context->workgroup = strdup(lp_workgroup());
+ }
+ else {
+ /* TODO: Think about a decent default workgroup */
+ context->workgroup = strdup("samba");
+ }
+ }
+
+ DEBUG(1, ("Using workgroup %s.\n", context->workgroup));
+
+ /* shortest timeout is 1 second */
+ if (context->timeout > 0 && context->timeout < 1000)
+ context->timeout = 1000;
+
+ /*
+ * FIXME: Should we check the function pointers here?
+ */
+
+ context->internal->_initialized = 1;
+
+ return context;
+}
diff --git a/source/libsmb/namecache.c b/source/libsmb/namecache.c
new file mode 100644
index 00000000000..e3e7ac4e3c2
--- /dev/null
+++ b/source/libsmb/namecache.c
@@ -0,0 +1,321 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ NetBIOS name cache module on top of gencache mechanism.
+
+ Copyright (C) Tim Potter 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 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"
+
+#define NBTKEY_FMT "NBT/%s#%02X"
+
+
+/**
+ * Initialise namecache system. Function calls gencache
+ * initialisation function to perform necessary actions
+ *
+ * @return true upon successful initialisation of the cache or
+ * false on failure
+ **/
+
+BOOL namecache_enable(void)
+{
+ /*
+ * Check if name caching disabled by setting the name cache
+ * timeout to zero.
+ */
+
+ if (lp_name_cache_timeout() == 0) {
+ DEBUG(5, ("namecache_enable: disabling netbios name cache\n"));
+ return False;
+ }
+
+ /* Init namecache by calling gencache initialisation */
+
+ if (!gencache_init()) {
+ DEBUG(2, ("namecache_enable: Couldn't initialise namecache on top of gencache.\n"));
+ return False;
+ }
+
+ /* I leave it for now, though I don't think we really need this (mimir, 27.09.2002) */
+ DEBUG(5, ("namecache_enable: enabling netbios namecache, timeout %d "
+ "seconds\n", lp_name_cache_timeout()));
+
+ return True;
+}
+
+
+/**
+ * Shutdown namecache. Routine calls gencache close function
+ * to safely close gencache file.
+ *
+ * @return true upon successful shutdown of the cache or
+ * false on failure
+ **/
+
+BOOL namecache_shutdown(void)
+{
+ if (!gencache_shutdown()) {
+ DEBUG(2, ("namecache_shutdown: Couldn't close namecache on top of gencache.\n"));
+ return False;
+ }
+
+ DEBUG(5, ("namecache_shutdown: netbios namecache closed successfully.\n"));
+ return True;
+}
+
+
+/**
+ * Generates a key for netbios name lookups on basis of
+ * netbios name and type.
+ * The caller must free returned key string when finished.
+ *
+ * @param name netbios name string (case insensitive)
+ * @param name_type netbios type of the name being looked up
+ *
+ * @return string consisted of uppercased name and appended
+ * type number
+ */
+
+static char* namecache_key(const char *name, int name_type)
+{
+ char *keystr;
+ asprintf(&keystr, NBTKEY_FMT, strupper_static(name), name_type);
+
+ return keystr;
+}
+
+
+/**
+ * Store a name(s) in the name cache
+ *
+ * @param name netbios names array
+ * @param name_type integer netbios name type
+ * @param num_names number of names being stored
+ * @param ip_list array of in_addr structures containing
+ * ip addresses being stored
+ **/
+
+BOOL namecache_store(const char *name, int name_type,
+ int num_names, struct ip_service *ip_list)
+{
+ time_t expiry;
+ char *key, *value_string;
+ int i;
+ BOOL ret;
+
+ /*
+ * we use gecache call to avoid annoying debug messages about
+ * initialised namecache again and again...
+ */
+ if (!gencache_init()) return False;
+
+ if ( DEBUGLEVEL >= 5 ) {
+ DEBUG(5, ("namecache_store: storing %d address%s for %s#%02x: ",
+ num_names, num_names == 1 ? "": "es", name, name_type));
+
+ for (i = 0; i < num_names; i++)
+ DEBUGADD(5, ("%s:%d%s", inet_ntoa(ip_list[i].ip),
+ ip_list[i].port, (i == (num_names - 1) ? "" : ",")));
+
+ DEBUGADD(5, ("\n"));
+ }
+
+ key = namecache_key(name, name_type);
+ expiry = time(NULL) + lp_name_cache_timeout();
+
+ /*
+ * Generate string representation of ip addresses list
+ * First, store the number of ip addresses and then
+ * place each single ip
+ */
+ if (!ipstr_list_make(&value_string, ip_list, num_names)) {
+ SAFE_FREE(key);
+ SAFE_FREE(value_string);
+ return False;
+ }
+
+ /* set the entry */
+ ret = gencache_set(key, value_string, expiry);
+ SAFE_FREE(key);
+ SAFE_FREE(value_string);
+ return ret;
+}
+
+
+/**
+ * Look up a name in the cache.
+ *
+ * @param name netbios name to look up for
+ * @param name_type netbios name type of @param name
+ * @param ip_list mallocated list of IP addresses if found in the cache,
+ * NULL otherwise
+ * @param num_names number of entries found
+ *
+ * @return true upon successful fetch or
+ * false if name isn't found in the cache or has expired
+ **/
+
+BOOL namecache_fetch(const char *name, int name_type, struct ip_service **ip_list,
+ int *num_names)
+{
+ char *key, *value;
+ time_t timeout;
+
+ *num_names = 0;
+
+ /* exit now if null pointers were passed as they're required further */
+ if (!ip_list || !num_names) return False;
+
+ if (!gencache_init())
+ return False;
+
+ /*
+ * Use gencache interface - lookup the key
+ */
+ key = namecache_key(name, name_type);
+
+ if (!gencache_get(key, &value, &timeout)) {
+ DEBUG(5, ("no entry for %s#%02X found.\n", name, name_type));
+ gencache_del(key);
+ SAFE_FREE(key);
+ SAFE_FREE(value);
+ return False;
+ } else {
+ DEBUG(5, ("name %s#%02X found.\n", name, name_type));
+ }
+
+ /*
+ * Split up the stored value into the list of IP adresses
+ */
+ *num_names = ipstr_list_parse(value, ip_list);
+
+ SAFE_FREE(key);
+ SAFE_FREE(value);
+
+ return *num_names > 0; /* true only if some ip has been fetched */
+}
+
+
+/**
+ * Delete single namecache entry. Look at the
+ * gencache_iterate definition.
+ *
+ **/
+
+static void flush_netbios_name(const char* key, const char *value, time_t timeout, void* dptr)
+{
+ gencache_del(key);
+ DEBUG(5, ("Deleting entry %s\n", key));
+}
+
+
+/**
+ * Flush all names from the name cache.
+ * It's done by gencache_iterate()
+ *
+ * @return True upon successful deletion or
+ * False in case of an error
+ **/
+
+void namecache_flush(void)
+{
+ if (!gencache_init())
+ return;
+
+ /*
+ * iterate through each NBT cache's entry and flush it
+ * by flush_netbios_name function
+ */
+ gencache_iterate(flush_netbios_name, NULL, "NBT/*");
+ DEBUG(5, ("Namecache flushed\n"));
+}
+
+/* Construct a name status record key. */
+
+static char *namecache_status_record_key(const char *name, int name_type1,
+ int name_type2, struct in_addr keyip)
+{
+ char *keystr;
+
+ asprintf(&keystr, "NBT/%s#%02X.%02X.%s",
+ strupper_static(name), name_type1, name_type2, inet_ntoa(keyip));
+ return keystr;
+}
+
+/* Store a name status record. */
+
+BOOL namecache_status_store(const char *keyname, int keyname_type,
+ int name_type, struct in_addr keyip,
+ const char *srvname)
+{
+ char *key;
+ time_t expiry;
+ BOOL ret;
+
+ if (!gencache_init())
+ return False;
+
+ key = namecache_status_record_key(keyname, keyname_type, name_type, keyip);
+ if (!key)
+ return False;
+
+ expiry = time(NULL) + lp_name_cache_timeout();
+ ret = gencache_set(key, srvname, expiry);
+
+ if (ret)
+ DEBUG(5, ("namecache_status_store: entry %s -> %s\n", key, srvname ));
+ else
+ DEBUG(5, ("namecache_status_store: entry %s store failed.\n", key ));
+
+ SAFE_FREE(key);
+ return ret;
+}
+
+/* Fetch a name status record. */
+
+BOOL namecache_status_fetch(const char *keyname, int keyname_type,
+ int name_type, struct in_addr keyip, char *srvname_out)
+{
+ char *key = NULL;
+ char *value = NULL;
+ time_t timeout;
+
+ if (!gencache_init())
+ return False;
+
+ key = namecache_status_record_key(keyname, keyname_type, name_type, keyip);
+ if (!key)
+ return False;
+
+ if (!gencache_get(key, &value, &timeout)) {
+ DEBUG(5, ("namecache_status_fetch: no entry for %s found.\n", key));
+ gencache_del(key);
+ SAFE_FREE(key);
+ SAFE_FREE(value);
+ return False;
+ } else {
+ DEBUG(5, ("namecache_status_fetch: key %s -> %s\n", key, value ));
+ }
+
+ strlcpy(srvname_out, value, 16);
+ SAFE_FREE(key);
+ SAFE_FREE(value);
+ return True;
+}
diff --git a/source/libsmb/namequery.c b/source/libsmb/namequery.c
new file mode 100644
index 00000000000..b9bc4e11664
--- /dev/null
+++ b/source/libsmb/namequery.c
@@ -0,0 +1,1406 @@
+/*
+ Unix SMB/CIFS implementation.
+ name query routines
+ Copyright (C) Andrew Tridgell 1994-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 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"
+
+/* nmbd.c sets this to True. */
+BOOL global_in_nmbd = False;
+
+/****************************************************************************
+ Generate a random trn_id.
+****************************************************************************/
+
+static int generate_trn_id(void)
+{
+ static int trn_id;
+
+ if (trn_id == 0) {
+ sys_srandom(sys_getpid());
+ }
+
+ trn_id = sys_random();
+
+ return trn_id % (unsigned)0x7FFF;
+}
+
+/****************************************************************************
+ Parse a node status response into an array of structures.
+****************************************************************************/
+
+static struct node_status *parse_node_status(char *p, int *num_names)
+{
+ struct node_status *ret;
+ int i;
+
+ *num_names = CVAL(p,0);
+
+ if (*num_names == 0)
+ return NULL;
+
+ ret = (struct node_status *)malloc(sizeof(struct node_status)* (*num_names));
+ if (!ret) return NULL;
+
+ p++;
+ for (i=0;i< *num_names;i++) {
+ StrnCpy(ret[i].name,p,15);
+ trim_char(ret[i].name,'\0',' ');
+ ret[i].type = CVAL(p,15);
+ ret[i].flags = p[16];
+ p += 18;
+ DEBUG(10, ("%s#%02x: flags = 0x%02x\n", ret[i].name,
+ ret[i].type, ret[i].flags));
+ }
+ return ret;
+}
+
+
+/****************************************************************************
+ Do a NBT node status query on an open socket and return an array of
+ structures holding the returned names or NULL if the query failed.
+**************************************************************************/
+
+struct node_status *node_status_query(int fd,struct nmb_name *name,
+ struct in_addr to_ip, int *num_names)
+{
+ BOOL found=False;
+ int retries = 2;
+ int retry_time = 2000;
+ struct timeval tval;
+ struct packet_struct p;
+ struct packet_struct *p2;
+ struct nmb_packet *nmb = &p.packet.nmb;
+ struct node_status *ret;
+
+ ZERO_STRUCT(p);
+
+ nmb->header.name_trn_id = generate_trn_id();
+ nmb->header.opcode = 0;
+ nmb->header.response = False;
+ nmb->header.nm_flags.bcast = False;
+ nmb->header.nm_flags.recursion_available = False;
+ nmb->header.nm_flags.recursion_desired = False;
+ nmb->header.nm_flags.trunc = False;
+ nmb->header.nm_flags.authoritative = False;
+ nmb->header.rcode = 0;
+ nmb->header.qdcount = 1;
+ nmb->header.ancount = 0;
+ nmb->header.nscount = 0;
+ nmb->header.arcount = 0;
+ nmb->question.question_name = *name;
+ nmb->question.question_type = 0x21;
+ nmb->question.question_class = 0x1;
+
+ p.ip = to_ip;
+ p.port = NMB_PORT;
+ p.fd = fd;
+ p.timestamp = time(NULL);
+ p.packet_type = NMB_PACKET;
+
+ GetTimeOfDay(&tval);
+
+ if (!send_packet(&p))
+ return NULL;
+
+ retries--;
+
+ while (1) {
+ struct timeval tval2;
+ GetTimeOfDay(&tval2);
+ if (TvalDiff(&tval,&tval2) > retry_time) {
+ if (!retries)
+ break;
+ if (!found && !send_packet(&p))
+ return NULL;
+ GetTimeOfDay(&tval);
+ retries--;
+ }
+
+ if ((p2=receive_nmb_packet(fd,90,nmb->header.name_trn_id))) {
+ struct nmb_packet *nmb2 = &p2->packet.nmb;
+ debug_nmb_packet(p2);
+
+ if (nmb2->header.opcode != 0 ||
+ nmb2->header.nm_flags.bcast ||
+ nmb2->header.rcode ||
+ !nmb2->header.ancount ||
+ nmb2->answers->rr_type != 0x21) {
+ /* XXXX what do we do with this? could be a
+ redirect, but we'll discard it for the
+ moment */
+ free_packet(p2);
+ continue;
+ }
+
+ ret = parse_node_status(&nmb2->answers->rdata[0], num_names);
+ free_packet(p2);
+ return ret;
+ }
+ }
+
+ return NULL;
+}
+
+/****************************************************************************
+ Find the first type XX name in a node status reply - used for finding
+ a servers name given its IP. Return the matched name in *name.
+**************************************************************************/
+
+BOOL name_status_find(const char *q_name, int q_type, int type, struct in_addr to_ip, fstring name)
+{
+ struct node_status *status = NULL;
+ struct nmb_name nname;
+ int count, i;
+ int sock;
+ BOOL result = False;
+
+ if (lp_disable_netbios()) {
+ DEBUG(5,("name_status_find(%s#%02x): netbios is disabled\n", q_name, q_type));
+ return False;
+ }
+
+ DEBUG(10, ("name_status_find: looking up %s#%02x at %s\n", q_name,
+ q_type, inet_ntoa(to_ip)));
+
+ /* Check the cache first. */
+
+ if (namecache_status_fetch(q_name, q_type, type, to_ip, name))
+ return True;
+
+ sock = open_socket_in(SOCK_DGRAM, 0, 3, interpret_addr(lp_socket_address()), True);
+ if (sock == -1)
+ goto done;
+
+ /* W2K PDC's seem not to respond to '*'#0. JRA */
+ make_nmb_name(&nname, q_name, q_type);
+ status = node_status_query(sock, &nname, to_ip, &count);
+ close(sock);
+ if (!status)
+ goto done;
+
+ for (i=0;i<count;i++) {
+ if (status[i].type == type)
+ break;
+ }
+ if (i == count)
+ goto done;
+
+ pull_ascii_nstring(name, sizeof(fstring), status[i].name);
+
+ /* Store the result in the cache. */
+ /* but don't store an entry for 0x1c names here. Here we have
+ a single host and DOMAIN<0x1c> names should be a list of hosts */
+
+ if ( q_type != 0x1c )
+ namecache_status_store(q_name, q_type, type, to_ip, name);
+
+ result = True;
+
+ done:
+ SAFE_FREE(status);
+
+ DEBUG(10, ("name_status_find: name %sfound", result ? "" : "not "));
+
+ if (result)
+ DEBUGADD(10, (", name %s ip address is %s", name, inet_ntoa(to_ip)));
+
+ DEBUG(10, ("\n"));
+
+ return result;
+}
+
+/*
+ comparison function used by sort_ip_list
+*/
+
+static int ip_compare(struct in_addr *ip1, struct in_addr *ip2)
+{
+ int max_bits1=0, max_bits2=0;
+ int num_interfaces = iface_count();
+ int i;
+
+ for (i=0;i<num_interfaces;i++) {
+ struct in_addr ip;
+ int bits1, bits2;
+ ip = *iface_n_bcast(i);
+ bits1 = matching_quad_bits((uchar *)&ip1->s_addr, (uchar *)&ip.s_addr);
+ bits2 = matching_quad_bits((uchar *)&ip2->s_addr, (uchar *)&ip.s_addr);
+ max_bits1 = MAX(bits1, max_bits1);
+ max_bits2 = MAX(bits2, max_bits2);
+ }
+
+ /* bias towards directly reachable IPs */
+ if (iface_local(*ip1)) {
+ max_bits1 += 32;
+ }
+ if (iface_local(*ip2)) {
+ max_bits2 += 32;
+ }
+
+ return max_bits2 - max_bits1;
+}
+
+/*******************************************************************
+ compare 2 ldap IPs by nearness to our interfaces - used in qsort
+*******************************************************************/
+
+static int ip_service_compare(struct ip_service *ip1, struct ip_service *ip2)
+{
+ int result;
+
+ if ( (result = ip_compare(&ip1->ip, &ip2->ip)) != 0 )
+ return result;
+
+ if ( ip1->port > ip2->port )
+ return 1;
+
+ if ( ip1->port < ip2->port )
+ return -1;
+
+ return 0;
+}
+
+/*
+ sort an IP list so that names that are close to one of our interfaces
+ are at the top. This prevents the problem where a WINS server returns an IP that
+ is not reachable from our subnet as the first match
+*/
+
+static void sort_ip_list(struct in_addr *iplist, int count)
+{
+ if (count <= 1) {
+ return;
+ }
+
+ qsort(iplist, count, sizeof(struct in_addr), QSORT_CAST ip_compare);
+}
+
+static void sort_ip_list2(struct ip_service *iplist, int count)
+{
+ if (count <= 1) {
+ return;
+ }
+
+ qsort(iplist, count, sizeof(struct ip_service), QSORT_CAST ip_service_compare);
+}
+
+/**********************************************************************
+ Remove any duplicate address/port pairs in the list
+ *********************************************************************/
+
+static int remove_duplicate_addrs2( struct ip_service *iplist, int count )
+{
+ int i, j;
+
+ DEBUG(10,("remove_duplicate_addrs2: looking for duplicate address/port pairs\n"));
+
+ /* one loop to remove duplicates */
+ for ( i=0; i<count; i++ ) {
+ if ( is_zero_ip(iplist[i].ip) )
+ continue;
+
+ for ( j=i+1; j<count; j++ ) {
+ if ( ip_service_equal(iplist[i], iplist[j]) )
+ zero_ip(&iplist[j].ip);
+ }
+ }
+
+ /* one loop to clean up any holes we left */
+ /* first ip should never be a zero_ip() */
+ for (i = 0; i<count; ) {
+ if ( is_zero_ip(iplist[i].ip) ) {
+ if (i != count-1 )
+ memmove(&iplist[i], &iplist[i+1], (count - i - 1)*sizeof(iplist[i]));
+ count--;
+ continue;
+ }
+ i++;
+ }
+
+ return count;
+}
+
+/****************************************************************************
+ Do a netbios name query to find someones IP.
+ Returns an array of IP addresses or NULL if none.
+ *count will be set to the number of addresses returned.
+ *timed_out is set if we failed by timing out
+****************************************************************************/
+
+struct in_addr *name_query(int fd,const char *name,int name_type,
+ BOOL bcast,BOOL recurse,
+ struct in_addr to_ip, int *count, int *flags,
+ BOOL *timed_out)
+{
+ BOOL found=False;
+ int i, retries = 3;
+ int retry_time = bcast?250:2000;
+ struct timeval tval;
+ struct packet_struct p;
+ struct packet_struct *p2;
+ struct nmb_packet *nmb = &p.packet.nmb;
+ struct in_addr *ip_list = NULL;
+
+ if (lp_disable_netbios()) {
+ DEBUG(5,("name_query(%s#%02x): netbios is disabled\n", name, name_type));
+ return NULL;
+ }
+
+ if (timed_out) {
+ *timed_out = False;
+ }
+
+ memset((char *)&p,'\0',sizeof(p));
+ (*count) = 0;
+ (*flags) = 0;
+
+ nmb->header.name_trn_id = generate_trn_id();
+ nmb->header.opcode = 0;
+ nmb->header.response = False;
+ nmb->header.nm_flags.bcast = bcast;
+ nmb->header.nm_flags.recursion_available = False;
+ nmb->header.nm_flags.recursion_desired = recurse;
+ nmb->header.nm_flags.trunc = False;
+ nmb->header.nm_flags.authoritative = False;
+ nmb->header.rcode = 0;
+ nmb->header.qdcount = 1;
+ nmb->header.ancount = 0;
+ nmb->header.nscount = 0;
+ nmb->header.arcount = 0;
+
+ make_nmb_name(&nmb->question.question_name,name,name_type);
+
+ nmb->question.question_type = 0x20;
+ nmb->question.question_class = 0x1;
+
+ p.ip = to_ip;
+ p.port = NMB_PORT;
+ p.fd = fd;
+ p.timestamp = time(NULL);
+ p.packet_type = NMB_PACKET;
+
+ GetTimeOfDay(&tval);
+
+ if (!send_packet(&p))
+ return NULL;
+
+ retries--;
+
+ while (1) {
+ struct timeval tval2;
+ struct in_addr *tmp_ip_list;
+
+ GetTimeOfDay(&tval2);
+ if (TvalDiff(&tval,&tval2) > retry_time) {
+ if (!retries)
+ break;
+ if (!found && !send_packet(&p))
+ return NULL;
+ GetTimeOfDay(&tval);
+ retries--;
+ }
+
+ if ((p2=receive_nmb_packet(fd,90,nmb->header.name_trn_id))) {
+ struct nmb_packet *nmb2 = &p2->packet.nmb;
+ debug_nmb_packet(p2);
+
+ /* If we get a Negative Name Query Response from a WINS
+ * server, we should report it and give up.
+ */
+ if( 0 == nmb2->header.opcode /* A query response */
+ && !(bcast) /* from a WINS server */
+ && nmb2->header.rcode /* Error returned */
+ ) {
+
+ if( DEBUGLVL( 3 ) ) {
+ /* Only executed if DEBUGLEVEL >= 3 */
+ dbgtext( "Negative name query response, rcode 0x%02x: ", nmb2->header.rcode );
+ switch( nmb2->header.rcode ) {
+ case 0x01:
+ dbgtext( "Request was invalidly formatted.\n" );
+ break;
+ case 0x02:
+ dbgtext( "Problem with NBNS, cannot process name.\n");
+ break;
+ case 0x03:
+ dbgtext( "The name requested does not exist.\n" );
+ break;
+ case 0x04:
+ dbgtext( "Unsupported request error.\n" );
+ break;
+ case 0x05:
+ dbgtext( "Query refused error.\n" );
+ break;
+ default:
+ dbgtext( "Unrecognized error code.\n" );
+ break;
+ }
+ }
+ free_packet(p2);
+ return( NULL );
+ }
+
+ if (nmb2->header.opcode != 0 ||
+ nmb2->header.nm_flags.bcast ||
+ nmb2->header.rcode ||
+ !nmb2->header.ancount) {
+ /*
+ * XXXX what do we do with this? Could be a
+ * redirect, but we'll discard it for the
+ * moment.
+ */
+ free_packet(p2);
+ continue;
+ }
+
+ tmp_ip_list = (struct in_addr *)Realloc( ip_list, sizeof( ip_list[0] )
+ * ( (*count) + nmb2->answers->rdlength/6 ) );
+
+ if (!tmp_ip_list) {
+ DEBUG(0,("name_query: Realloc failed.\n"));
+ SAFE_FREE(ip_list);
+ }
+
+ ip_list = tmp_ip_list;
+
+ if (ip_list) {
+ DEBUG(2,("Got a positive name query response from %s ( ", inet_ntoa(p2->ip)));
+ for (i=0;i<nmb2->answers->rdlength/6;i++) {
+ putip((char *)&ip_list[(*count)],&nmb2->answers->rdata[2+i*6]);
+ DEBUGADD(2,("%s ",inet_ntoa(ip_list[(*count)])));
+ (*count)++;
+ }
+ DEBUGADD(2,(")\n"));
+ }
+
+ found=True;
+ retries=0;
+ /* We add the flags back ... */
+ if (nmb2->header.response)
+ (*flags) |= NM_FLAGS_RS;
+ if (nmb2->header.nm_flags.authoritative)
+ (*flags) |= NM_FLAGS_AA;
+ if (nmb2->header.nm_flags.trunc)
+ (*flags) |= NM_FLAGS_TC;
+ if (nmb2->header.nm_flags.recursion_desired)
+ (*flags) |= NM_FLAGS_RD;
+ if (nmb2->header.nm_flags.recursion_available)
+ (*flags) |= NM_FLAGS_RA;
+ if (nmb2->header.nm_flags.bcast)
+ (*flags) |= NM_FLAGS_B;
+ free_packet(p2);
+ /*
+ * If we're doing a unicast lookup we only
+ * expect one reply. Don't wait the full 2
+ * seconds if we got one. JRA.
+ */
+ if(!bcast && found)
+ break;
+ }
+ }
+
+ /* only set timed_out if we didn't fund what we where looking for*/
+
+ if ( !found && timed_out ) {
+ *timed_out = True;
+ }
+
+ /* sort the ip list so we choose close servers first if possible */
+ sort_ip_list(ip_list, *count);
+
+ return ip_list;
+}
+
+/********************************************************
+ Start parsing the lmhosts file.
+*********************************************************/
+
+XFILE *startlmhosts(char *fname)
+{
+ XFILE *fp = x_fopen(fname,O_RDONLY, 0);
+ if (!fp) {
+ DEBUG(4,("startlmhosts: Can't open lmhosts file %s. Error was %s\n",
+ fname, strerror(errno)));
+ return NULL;
+ }
+ return fp;
+}
+
+/********************************************************
+ Parse the next line in the lmhosts file.
+*********************************************************/
+
+BOOL getlmhostsent( XFILE *fp, pstring name, int *name_type, struct in_addr *ipaddr)
+{
+ pstring line;
+
+ while(!x_feof(fp) && !x_ferror(fp)) {
+ pstring ip,flags,extra;
+ const char *ptr;
+ char *ptr1;
+ int count = 0;
+
+ *name_type = -1;
+
+ if (!fgets_slash(line,sizeof(pstring),fp))
+ continue;
+
+ if (*line == '#')
+ continue;
+
+ pstrcpy(ip,"");
+ pstrcpy(name,"");
+ pstrcpy(flags,"");
+
+ ptr = line;
+
+ if (next_token(&ptr,ip ,NULL,sizeof(ip)))
+ ++count;
+ if (next_token(&ptr,name ,NULL, sizeof(pstring)))
+ ++count;
+ if (next_token(&ptr,flags,NULL, sizeof(flags)))
+ ++count;
+ if (next_token(&ptr,extra,NULL, sizeof(extra)))
+ ++count;
+
+ if (count <= 0)
+ continue;
+
+ if (count > 0 && count < 2)
+ {
+ DEBUG(0,("getlmhostsent: Ill formed hosts line [%s]\n",line));
+ continue;
+ }
+
+ if (count >= 4)
+ {
+ DEBUG(0,("getlmhostsent: too many columns in lmhosts file (obsolete syntax)\n"));
+ continue;
+ }
+
+ DEBUG(4, ("getlmhostsent: lmhost entry: %s %s %s\n", ip, name, flags));
+
+ if (strchr_m(flags,'G') || strchr_m(flags,'S'))
+ {
+ DEBUG(0,("getlmhostsent: group flag in lmhosts ignored (obsolete)\n"));
+ continue;
+ }
+
+ *ipaddr = *interpret_addr2(ip);
+
+ /* Extra feature. If the name ends in '#XX', where XX is a hex number,
+ then only add that name type. */
+ if((ptr1 = strchr_m(name, '#')) != NULL)
+ {
+ char *endptr;
+
+ ptr1++;
+ *name_type = (int)strtol(ptr1, &endptr, 16);
+
+ if(!*ptr1 || (endptr == ptr1))
+ {
+ DEBUG(0,("getlmhostsent: invalid name %s containing '#'.\n", name));
+ continue;
+ }
+
+ *(--ptr1) = '\0'; /* Truncate at the '#' */
+ }
+
+ return True;
+ }
+
+ return False;
+}
+
+/********************************************************
+ Finish parsing the lmhosts file.
+*********************************************************/
+
+void endlmhosts(XFILE *fp)
+{
+ x_fclose(fp);
+}
+
+/********************************************************
+ convert an array if struct in_addrs to struct ip_service
+ return False on failure. Port is set to PORT_NONE;
+*********************************************************/
+
+static BOOL convert_ip2service( struct ip_service **return_iplist, struct in_addr *ip_list, int count )
+{
+ int i;
+
+ if ( count==0 || !ip_list )
+ return False;
+
+ /* copy the ip address; port will be PORT_NONE */
+ if ( (*return_iplist = (struct ip_service*)malloc(count*sizeof(struct ip_service))) == NULL ) {
+ DEBUG(0,("convert_ip2service: malloc failed for %d enetries!\n", count ));
+ return False;
+ }
+
+ for ( i=0; i<count; i++ ) {
+ (*return_iplist)[i].ip = ip_list[i];
+ (*return_iplist)[i].port = PORT_NONE;
+ }
+
+ return True;
+}
+/********************************************************
+ Resolve via "bcast" method.
+*********************************************************/
+
+BOOL name_resolve_bcast(const char *name, int name_type,
+ struct ip_service **return_iplist, int *return_count)
+{
+ int sock, i;
+ int num_interfaces = iface_count();
+ struct in_addr *ip_list;
+ BOOL ret;
+
+ if (lp_disable_netbios()) {
+ DEBUG(5,("name_resolve_bcast(%s#%02x): netbios is disabled\n", name, name_type));
+ return False;
+ }
+
+ *return_iplist = NULL;
+ *return_count = 0;
+
+ /*
+ * "bcast" means do a broadcast lookup on all the local interfaces.
+ */
+
+ DEBUG(3,("name_resolve_bcast: Attempting broadcast lookup for name %s<0x%x>\n", name, name_type));
+
+ sock = open_socket_in( SOCK_DGRAM, 0, 3,
+ interpret_addr(lp_socket_address()), True );
+
+ if (sock == -1) return False;
+
+ set_socket_options(sock,"SO_BROADCAST");
+ /*
+ * Lookup the name on all the interfaces, return on
+ * the first successful match.
+ */
+ for( i = num_interfaces-1; i >= 0; i--) {
+ struct in_addr sendto_ip;
+ int flags;
+ /* Done this way to fix compiler error on IRIX 5.x */
+ sendto_ip = *iface_n_bcast(i);
+ ip_list = name_query(sock, name, name_type, True,
+ True, sendto_ip, return_count, &flags, NULL);
+ if( ip_list )
+ goto success;
+ }
+
+ /* failed - no response */
+
+ close(sock);
+ return False;
+
+success:
+ ret = True;
+ if ( !convert_ip2service(return_iplist, ip_list, *return_count) )
+ ret = False;
+
+ SAFE_FREE( ip_list );
+ close(sock);
+ return ret;
+}
+
+/********************************************************
+ Resolve via "wins" method.
+*********************************************************/
+
+BOOL resolve_wins(const char *name, int name_type,
+ struct ip_service **return_iplist, int *return_count)
+{
+ int sock, t, i;
+ char **wins_tags;
+ struct in_addr src_ip, *ip_list = NULL;
+ BOOL ret;
+
+ if (lp_disable_netbios()) {
+ DEBUG(5,("resolve_wins(%s#%02x): netbios is disabled\n", name, name_type));
+ return False;
+ }
+
+ *return_iplist = NULL;
+ *return_count = 0;
+
+ DEBUG(3,("resolve_wins: Attempting wins lookup for name %s<0x%x>\n", name, name_type));
+
+ if (wins_srv_count() < 1) {
+ DEBUG(3,("resolve_wins: WINS server resolution selected and no WINS servers listed.\n"));
+ return False;
+ }
+
+ /* we try a lookup on each of the WINS tags in turn */
+ wins_tags = wins_srv_tags();
+
+ if (!wins_tags) {
+ /* huh? no tags?? give up in disgust */
+ return False;
+ }
+
+ /* the address we will be sending from */
+ src_ip = *interpret_addr2(lp_socket_address());
+
+ /* in the worst case we will try every wins server with every
+ tag! */
+ for (t=0; wins_tags && wins_tags[t]; t++) {
+ int srv_count = wins_srv_count_tag(wins_tags[t]);
+ for (i=0; i<srv_count; i++) {
+ struct in_addr wins_ip;
+ int flags;
+ BOOL timed_out;
+
+ wins_ip = wins_srv_ip_tag(wins_tags[t], src_ip);
+
+ if (global_in_nmbd && ismyip(wins_ip)) {
+ /* yikes! we'll loop forever */
+ continue;
+ }
+
+ /* skip any that have been unresponsive lately */
+ if (wins_srv_is_dead(wins_ip, src_ip)) {
+ continue;
+ }
+
+ DEBUG(3,("resolve_wins: using WINS server %s and tag '%s'\n", inet_ntoa(wins_ip), wins_tags[t]));
+
+ sock = open_socket_in(SOCK_DGRAM, 0, 3, src_ip.s_addr, True);
+ if (sock == -1) {
+ continue;
+ }
+
+ ip_list = name_query(sock,name,name_type, False,
+ True, wins_ip, return_count, &flags,
+ &timed_out);
+
+ /* exit loop if we got a list of addresses */
+
+ if ( ip_list )
+ goto success;
+
+ close(sock);
+
+ if (timed_out) {
+ /* Timed out wating for WINS server to respond. Mark it dead. */
+ wins_srv_died(wins_ip, src_ip);
+ } else {
+ /* The name definately isn't in this
+ group of WINS servers. goto the next group */
+ break;
+ }
+ }
+ }
+
+ wins_srv_tags_free(wins_tags);
+ return False;
+
+success:
+ ret = True;
+ if ( !convert_ip2service( return_iplist, ip_list, *return_count ) )
+ ret = False;
+
+ SAFE_FREE( ip_list );
+ wins_srv_tags_free(wins_tags);
+ close(sock);
+
+ return ret;
+}
+
+/********************************************************
+ Resolve via "lmhosts" method.
+*********************************************************/
+
+static BOOL resolve_lmhosts(const char *name, int name_type,
+ struct ip_service **return_iplist, int *return_count)
+{
+ /*
+ * "lmhosts" means parse the local lmhosts file.
+ */
+
+ XFILE *fp;
+ pstring lmhost_name;
+ int name_type2;
+ struct in_addr return_ip;
+
+ *return_iplist = NULL;
+ *return_count = 0;
+
+ DEBUG(3,("resolve_lmhosts: Attempting lmhosts lookup for name %s<0x%x>\n", name, name_type));
+
+ fp = startlmhosts(dyn_LMHOSTSFILE);
+ if(fp) {
+ while (getlmhostsent(fp, lmhost_name, &name_type2, &return_ip)) {
+ if (strequal(name, lmhost_name) &&
+ ((name_type2 == -1) || (name_type == name_type2))
+ ) {
+ endlmhosts(fp);
+ if ( (*return_iplist = (struct ip_service *)malloc(sizeof(struct ip_service))) == NULL ) {
+ DEBUG(3,("resolve_lmhosts: malloc fail !\n"));
+ return False;
+ }
+ (*return_iplist)[0].ip = return_ip;
+ (*return_iplist)[0].port = PORT_NONE;
+ *return_count = 1;
+ return True;
+ }
+ }
+ endlmhosts(fp);
+ }
+ return False;
+}
+
+
+/********************************************************
+ Resolve via "hosts" method.
+*********************************************************/
+
+static BOOL resolve_hosts(const char *name, int name_type,
+ struct ip_service **return_iplist, int *return_count)
+{
+ /*
+ * "host" means do a localhost, or dns lookup.
+ */
+ struct hostent *hp;
+
+ if ( name_type != 0x20 && name_type != 0x0) {
+ DEBUG(5, ("resolve_hosts: not appropriate for name type <0x%x>\n", name_type));
+ return False;
+ }
+
+ *return_iplist = NULL;
+ *return_count = 0;
+
+ DEBUG(3,("resolve_hosts: Attempting host lookup for name %s<0x%x>\n", name, name_type));
+
+ if (((hp = sys_gethostbyname(name)) != NULL) && (hp->h_addr != NULL)) {
+ struct in_addr return_ip;
+ putip((char *)&return_ip,(char *)hp->h_addr);
+ *return_iplist = (struct ip_service *)malloc(sizeof(struct ip_service));
+ if(*return_iplist == NULL) {
+ DEBUG(3,("resolve_hosts: malloc fail !\n"));
+ return False;
+ }
+ (*return_iplist)->ip = return_ip;
+ (*return_iplist)->port = PORT_NONE;
+ *return_count = 1;
+ return True;
+ }
+ return False;
+}
+
+/********************************************************
+ Resolve via "ADS" method.
+*********************************************************/
+
+static BOOL resolve_ads(const char *name, int name_type,
+ struct ip_service **return_iplist, int *return_count)
+{
+
+#ifdef HAVE_ADS
+ if ( name_type == 0x1c ) {
+ int count, i = 0;
+ char *list = NULL;
+ const char *ptr;
+ pstring tok;
+
+ /* try to lookup the _ldap._tcp.<domain> if we are using ADS */
+ if ( lp_security() != SEC_ADS )
+ return False;
+
+ DEBUG(5,("resolve_hosts: Attempting to resolve DC's for %s using DNS\n",
+ name));
+
+ if (ldap_domain2hostlist(name, &list) != LDAP_SUCCESS)
+ return False;
+
+ count = count_chars(list, ' ') + 1;
+ if ( (*return_iplist = malloc(count * sizeof(struct ip_service))) == NULL ) {
+ DEBUG(0,("resolve_hosts: malloc failed for %d entries\n", count ));
+ return False;
+ }
+
+ ptr = list;
+ while (next_token(&ptr, tok, " ", sizeof(tok))) {
+ unsigned port = LDAP_PORT;
+ char *p = strchr(tok, ':');
+ if (p) {
+ *p = 0;
+ port = atoi(p+1);
+ }
+ (*return_iplist)[i].ip = *interpret_addr2(tok);
+ (*return_iplist)[i].port = port;
+
+ /* make sure it is a valid IP. I considered checking the negative
+ connection cache, but this is the wrong place for it. Maybe only
+ as a hac. After think about it, if all of the IP addresses retuend
+ from DNS are dead, what hope does a netbios name lookup have?
+ The standard reason for falling back to netbios lookups is that
+ our DNS server doesn't know anything about the DC's -- jerry */
+
+ if ( is_zero_ip((*return_iplist)[i].ip) )
+ continue;
+
+ i++;
+ }
+ SAFE_FREE(list);
+
+ *return_count = i;
+
+ return True;
+ } else
+#endif /* HAVE_ADS */
+ {
+ return False;
+ }
+}
+
+/*******************************************************************
+ Internal interface to resolve a name into an IP address.
+ Use this function if the string is either an IP address, DNS
+ or host name or NetBIOS name. This uses the name switch in the
+ smb.conf to determine the order of name resolution.
+
+ Added support for ip addr/port to support ADS ldap servers.
+ the only place we currently care about the port is in the
+ resolve_hosts() when looking up DC's via SRV RR entries in DNS
+**********************************************************************/
+
+static BOOL internal_resolve_name(const char *name, int name_type,
+ struct ip_service **return_iplist,
+ int *return_count, const char *resolve_order)
+{
+ pstring name_resolve_list;
+ fstring tok;
+ const char *ptr;
+ BOOL allones = (strcmp(name,"255.255.255.255") == 0);
+ BOOL allzeros = (strcmp(name,"0.0.0.0") == 0);
+ BOOL is_address = is_ipaddress(name);
+ BOOL result = False;
+ int i;
+
+ *return_iplist = NULL;
+ *return_count = 0;
+
+ DEBUG(10, ("internal_resolve_name: looking up %s#%x\n", name, name_type));
+
+ if (allzeros || allones || is_address) {
+
+ if ( (*return_iplist = (struct ip_service *)malloc(sizeof(struct ip_service))) == NULL ) {
+ DEBUG(0,("internal_resolve_name: malloc fail !\n"));
+ return False;
+ }
+
+ if(is_address) {
+ /* ignore the port here */
+ (*return_iplist)->port = PORT_NONE;
+
+ /* if it's in the form of an IP address then get the lib to interpret it */
+ if (((*return_iplist)->ip.s_addr = inet_addr(name)) == 0xFFFFFFFF ){
+ DEBUG(1,("internal_resolve_name: inet_addr failed on %s\n", name));
+ return False;
+ }
+ } else {
+ (*return_iplist)->ip.s_addr = allones ? 0xFFFFFFFF : 0;
+ }
+ *return_count = 1;
+ return True;
+ }
+
+ /* Check name cache */
+
+ if (namecache_fetch(name, name_type, return_iplist, return_count)) {
+ /* This could be a negative response */
+ return (*return_count > 0);
+ }
+
+ /* set the name resolution order */
+
+ if ( strcmp( resolve_order, "NULL") == 0 ) {
+ DEBUG(8,("internal_resolve_name: all lookups disabled\n"));
+ return False;
+ }
+
+ if ( !resolve_order )
+ pstrcpy(name_resolve_list, lp_name_resolve_order());
+ else
+ pstrcpy(name_resolve_list, resolve_order);
+
+ if ( !name_resolve_list[0] )
+ ptr = "host";
+ else
+ ptr = name_resolve_list;
+
+ /* iterate through the name resolution backends */
+
+ while (next_token(&ptr, tok, LIST_SEP, sizeof(tok))) {
+ if((strequal(tok, "host") || strequal(tok, "hosts"))) {
+ if (resolve_hosts(name, name_type, return_iplist, return_count)) {
+ result = True;
+ goto done;
+ }
+ } else if(strequal( tok, "ads")) {
+ /* deal with 0x1c names here. This will result in a
+ SRV record lookup for _ldap._tcp.<domain> if we
+ are using 'security = ads' */
+ if (resolve_ads(name, name_type, return_iplist, return_count)) {
+ result = True;
+ goto done;
+ }
+ } else if(strequal( tok, "lmhosts")) {
+ if (resolve_lmhosts(name, name_type, return_iplist, return_count)) {
+ result = True;
+ goto done;
+ }
+ } else if(strequal( tok, "wins")) {
+ /* don't resolve 1D via WINS */
+ if (name_type != 0x1D &&
+ resolve_wins(name, name_type, return_iplist, return_count)) {
+ result = True;
+ goto done;
+ }
+ } else if(strequal( tok, "bcast")) {
+ if (name_resolve_bcast(name, name_type, return_iplist, return_count)) {
+ result = True;
+ goto done;
+ }
+ } else {
+ DEBUG(0,("resolve_name: unknown name switch type %s\n", tok));
+ }
+ }
+
+ /* All of the resolve_* functions above have returned false. */
+
+ SAFE_FREE(*return_iplist);
+ *return_count = 0;
+
+ return False;
+
+ done:
+
+ /* Remove duplicate entries. Some queries, notably #1c (domain
+ controllers) return the PDC in iplist[0] and then all domain
+ controllers including the PDC in iplist[1..n]. Iterating over
+ the iplist when the PDC is down will cause two sets of timeouts. */
+
+ if ( *return_count ) {
+ *return_count = remove_duplicate_addrs2( *return_iplist, *return_count );
+ }
+
+ /* Save in name cache */
+ if ( DEBUGLEVEL >= 100 ) {
+ for (i = 0; i < *return_count && DEBUGLEVEL == 100; i++)
+ DEBUG(100, ("Storing name %s of type %d (%s:%d)\n", name,
+ name_type, inet_ntoa((*return_iplist)[i].ip), (*return_iplist)[i].port));
+ }
+
+ namecache_store(name, name_type, *return_count, *return_iplist);
+
+ /* Display some debugging info */
+
+ if ( DEBUGLEVEL >= 10 ) {
+ DEBUG(10, ("internal_resolve_name: returning %d addresses: ",
+ *return_count));
+
+ for (i = 0; i < *return_count; i++)
+ DEBUGADD(10, ("%s:%d ", inet_ntoa((*return_iplist)[i].ip), (*return_iplist)[i].port));
+
+ DEBUG(10, ("\n"));
+ }
+
+ return result;
+}
+
+/********************************************************
+ Internal interface to resolve a name into one IP address.
+ Use this function if the string is either an IP address, DNS
+ or host name or NetBIOS name. This uses the name switch in the
+ smb.conf to determine the order of name resolution.
+*********************************************************/
+
+BOOL resolve_name(const char *name, struct in_addr *return_ip, int name_type)
+{
+ struct ip_service *ip_list = NULL;
+ int count = 0;
+
+ if (is_ipaddress(name)) {
+ *return_ip = *interpret_addr2(name);
+ return True;
+ }
+
+ if (internal_resolve_name(name, name_type, &ip_list, &count, lp_name_resolve_order())) {
+ int i;
+
+ /* only return valid addresses for TCP connections */
+ for (i=0; i<count; i++) {
+ char *ip_str = inet_ntoa(ip_list[i].ip);
+ if (ip_str &&
+ strcmp(ip_str, "255.255.255.255") != 0 &&
+ strcmp(ip_str, "0.0.0.0") != 0)
+ {
+ *return_ip = ip_list[i].ip;
+ SAFE_FREE(ip_list);
+ return True;
+ }
+ }
+ }
+
+ SAFE_FREE(ip_list);
+ return False;
+}
+
+/********************************************************
+ Find the IP address of the master browser or DMB for a workgroup.
+*********************************************************/
+
+BOOL find_master_ip(const char *group, struct in_addr *master_ip)
+{
+ struct ip_service *ip_list = NULL;
+ int count = 0;
+
+ if (lp_disable_netbios()) {
+ DEBUG(5,("find_master_ip(%s): netbios is disabled\n", group));
+ return False;
+ }
+
+ if (internal_resolve_name(group, 0x1D, &ip_list, &count, lp_name_resolve_order())) {
+ *master_ip = ip_list[0].ip;
+ SAFE_FREE(ip_list);
+ return True;
+ }
+ if(internal_resolve_name(group, 0x1B, &ip_list, &count, lp_name_resolve_order())) {
+ *master_ip = ip_list[0].ip;
+ SAFE_FREE(ip_list);
+ return True;
+ }
+
+ SAFE_FREE(ip_list);
+ return False;
+}
+
+/********************************************************
+ Get the IP address list of the primary domain controller
+ for a domain.
+*********************************************************/
+
+BOOL get_pdc_ip(const char *domain, struct in_addr *ip)
+{
+ struct ip_service *ip_list;
+ int count;
+
+ /* Look up #1B name */
+
+ if (!internal_resolve_name(domain, 0x1b, &ip_list, &count, lp_name_resolve_order()))
+ return False;
+
+ /* if we get more than 1 IP back we have to assume it is a
+ multi-homed PDC and not a mess up */
+
+ if ( count > 1 ) {
+ DEBUG(6,("get_pdc_ip: PDC has %d IP addresses!\n", count));
+ sort_ip_list2( ip_list, count );
+ }
+
+ *ip = ip_list[0].ip;
+
+ SAFE_FREE(ip_list);
+
+ return True;
+}
+
+/********************************************************
+ Get the IP address list of the domain controllers for
+ a domain.
+*********************************************************/
+
+static BOOL get_dc_list(const char *domain, struct ip_service **ip_list,
+ int *count, BOOL ads_only, int *ordered)
+{
+ fstring resolve_order;
+
+ /* if we are restricted to solely using DNS for looking
+ up a domain controller, make sure that host lookups
+ are enabled for the 'name resolve order'. If host lookups
+ are disabled and ads_only is True, then set the string to
+ NULL. */
+
+ fstrcpy( resolve_order, lp_name_resolve_order() );
+ strlower_m( resolve_order );
+ if ( ads_only ) {
+ if ( strstr( resolve_order, "host" ) )
+ fstrcpy( resolve_order, "ads" );
+ else
+ fstrcpy( resolve_order, "NULL" );
+ }
+
+
+ *ordered = False;
+
+ /* If it's our domain then use the 'password server' parameter. */
+
+ if ( strequal(domain, lp_workgroup()) || strequal(domain, lp_realm()) ) {
+ const char *p;
+ char *pserver = lp_passwordserver(); /* UNIX charset. */
+ char *port_str;
+ int port;
+ fstring name;
+ int num_addresses = 0;
+ int local_count, i, j;
+ struct ip_service *return_iplist = NULL;
+ struct ip_service *auto_ip_list = NULL;
+ BOOL done_auto_lookup = False;
+ int auto_count = 0;
+
+
+ if (!*pserver)
+ return internal_resolve_name(domain, 0x1C, ip_list, count, resolve_order);
+
+ p = pserver;
+
+ /*
+ * if '*' appears in the "password server" list then add
+ * an auto lookup to the list of manually configured
+ * DC's. If any DC is listed by name, then the list should be
+ * considered to be ordered
+ */
+
+ while (next_token(&p,name,LIST_SEP,sizeof(name))) {
+ if (strequal(name, "*")) {
+ if ( internal_resolve_name(domain, 0x1C, &auto_ip_list, &auto_count, resolve_order) )
+ num_addresses += auto_count;
+ done_auto_lookup = True;
+ DEBUG(8,("Adding %d DC's from auto lookup\n", auto_count));
+ }
+ else
+ num_addresses++;
+ }
+
+ /* if we have no addresses and haven't done the auto lookup, then
+ just return the list of DC's */
+
+ if ( (num_addresses == 0) && !done_auto_lookup )
+ return internal_resolve_name(domain, 0x1C, ip_list, count, resolve_order);
+
+ /* maybe we just failed? */
+
+ if ( num_addresses == 0 ) {
+ DEBUG(4,("get_dc_list: no servers found\n"));
+ return False;
+ }
+
+ if ( (return_iplist = (struct ip_service *)
+ malloc(num_addresses * sizeof(struct ip_service))) == NULL )
+ {
+ DEBUG(3,("get_dc_list: malloc fail !\n"));
+ return False;
+ }
+
+ p = pserver;
+ local_count = 0;
+
+ /* fill in the return list now with real IP's */
+
+ while ( (local_count<num_addresses) && next_token(&p,name,LIST_SEP,sizeof(name)) ) {
+ struct in_addr name_ip;
+
+ /* copy any addersses from the auto lookup */
+
+ if ( strequal(name, "*") ) {
+ for ( j=0; j<auto_count; j++ ) {
+ return_iplist[local_count].ip = auto_ip_list[j].ip;
+ return_iplist[local_count].port = auto_ip_list[j].port;
+ local_count++;
+ }
+ continue;
+ }
+
+
+ /* added support for address:port syntax for ads (not that I think
+ anyone will ever run the LDAP server in an AD domain on something
+ other than port 389 */
+
+ port = (lp_security() == SEC_ADS) ? LDAP_PORT : PORT_NONE;
+ if ( (port_str=strchr(name, ':')) != NULL ) {
+ *port_str = '\0';
+ port_str++;
+ port = atoi( port_str );
+ }
+
+ /* explicit lookup; resolve_name() will handle names & IP addresses */
+ if ( resolve_name( name, &name_ip, 0x20 ) ) {
+ return_iplist[local_count].ip = name_ip;
+ return_iplist[local_count].port = port;
+ local_count++;
+ *ordered = True;
+ }
+ }
+
+ SAFE_FREE(auto_ip_list);
+
+ /* need to remove duplicates in the list if we have any
+ explicit password servers */
+
+ if ( local_count )
+ local_count = remove_duplicate_addrs2( return_iplist, local_count );
+
+ if ( DEBUGLEVEL >= 4 ) {
+ DEBUG(4,("get_dc_list: returning %d ip addresses in an %sordered list\n", local_count,
+ *ordered ? "":"un"));
+ DEBUG(4,("get_dc_list: "));
+ for ( i=0; i<local_count; i++ )
+ DEBUGADD(4,("%s:%d ", inet_ntoa(return_iplist[i].ip), return_iplist[i].port ));
+ DEBUGADD(4,("\n"));
+ }
+
+ *ip_list = return_iplist;
+ *count = local_count;
+
+ return (*count != 0);
+ }
+
+ DEBUG(10,("get_dc_list: defaulting to internal auto lookup for domain %s\n", domain));
+
+ return internal_resolve_name(domain, 0x1C, ip_list, count, resolve_order);
+}
+
+/*********************************************************************
+ small wrapper function to get the DC list and sort it if neccessary
+*********************************************************************/
+BOOL get_sorted_dc_list( const char *domain, struct ip_service **ip_list, int *count, BOOL ads_only )
+{
+ BOOL ordered;
+
+ DEBUG(8,("get_sorted_dc_list: attempting lookup using [%s]\n",
+ (ads_only ? "ads" : lp_name_resolve_order())));
+
+ if ( !get_dc_list(domain, ip_list, count, ads_only, &ordered) )
+ return False;
+
+ /* only sort if we don't already have an ordered list */
+ if ( !ordered )
+ sort_ip_list2( *ip_list, *count );
+
+ return True;
+}
+
diff --git a/source/libsmb/namequery_dc.c b/source/libsmb/namequery_dc.c
new file mode 100644
index 00000000000..31d759e0d2c
--- /dev/null
+++ b/source/libsmb/namequery_dc.c
@@ -0,0 +1,185 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ Winbind daemon connection manager
+
+ Copyright (C) Tim Potter 2001
+ Copyright (C) Andrew Bartlett 2002
+ Copyright (C) Gerald Carter 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 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"
+
+/**************************************************************************
+ Find the name and IP address for a server in he realm/domain
+ *************************************************************************/
+
+static BOOL ads_dc_name(const char *domain, const char *realm, struct in_addr *dc_ip, fstring srv_name)
+{
+ ADS_STRUCT *ads;
+
+ if (!realm && strequal(domain, lp_workgroup()))
+ realm = lp_realm();
+
+ ads = ads_init(realm, domain, NULL);
+ if (!ads)
+ return False;
+
+ DEBUG(4,("ads_dc_name: domain=%s\n", domain));
+
+#ifdef HAVE_ADS
+ /* we don't need to bind, just connect */
+ ads->auth.flags |= ADS_AUTH_NO_BIND;
+
+ ads_connect(ads);
+#endif
+
+ if (!ads->config.realm)
+ return False;
+
+ fstrcpy(srv_name, ads->config.ldap_server_name);
+ strupper_m(srv_name);
+ *dc_ip = ads->ldap_ip;
+ ads_destroy(&ads);
+
+ DEBUG(4,("ads_dc_name: using server='%s' IP=%s\n",
+ srv_name, inet_ntoa(*dc_ip)));
+
+ return True;
+}
+
+/****************************************************************************
+ Utility function to return the name of a DC. The name is guaranteed to be
+ valid since we have already done a name_status_find on it
+ ***************************************************************************/
+
+static BOOL rpc_dc_name(const char *domain, fstring srv_name, struct in_addr *ip_out)
+{
+ struct ip_service *ip_list = NULL;
+ struct in_addr dc_ip, exclude_ip;
+ int count, i;
+ BOOL use_pdc_only;
+ NTSTATUS result;
+
+ zero_ip(&exclude_ip);
+
+ use_pdc_only = must_use_pdc(domain);
+
+ /* Lookup domain controller name */
+
+ if ( use_pdc_only && get_pdc_ip(domain, &dc_ip) )
+ {
+ DEBUG(10,("rpc_dc_name: Atempting to lookup PDC to avoid sam sync delays\n"));
+
+ /* check the connection cache and perform the node status
+ lookup only if the IP is not found to be bad */
+
+ if (name_status_find(domain, 0x1b, 0x20, dc_ip, srv_name) ) {
+ result = check_negative_conn_cache( domain, srv_name );
+ if ( NT_STATUS_IS_OK(result) )
+ goto done;
+ }
+ /* Didn't get name, remember not to talk to this DC. */
+ exclude_ip = dc_ip;
+ }
+
+ /* get a list of all domain controllers */
+
+ if ( !get_sorted_dc_list(domain, &ip_list, &count, False) ) {
+ DEBUG(3, ("Could not look up dc's for domain %s\n", domain));
+ return False;
+ }
+
+ /* Remove the entry we've already failed with (should be the PDC). */
+
+ if ( use_pdc_only ) {
+ for (i = 0; i < count; i++) {
+ if (ip_equal( exclude_ip, ip_list[i].ip))
+ zero_ip(&ip_list[i].ip);
+ }
+ }
+
+ for (i = 0; i < count; i++) {
+ if (is_zero_ip(ip_list[i].ip))
+ continue;
+
+ if (name_status_find(domain, 0x1c, 0x20, ip_list[i].ip, srv_name)) {
+ result = check_negative_conn_cache( domain, srv_name );
+ if ( NT_STATUS_IS_OK(result) ) {
+ dc_ip = ip_list[i].ip;
+ goto done;
+ }
+ }
+ }
+
+
+ SAFE_FREE(ip_list);
+
+ /* No-one to talk to )-: */
+ return False; /* Boo-hoo */
+
+ done:
+ /* We have the netbios name and IP address of a domain controller.
+ Ideally we should sent a SAMLOGON request to determine whether
+ the DC is alive and kicking. If we can catch a dead DC before
+ performing a cli_connect() we can avoid a 30-second timeout. */
+
+ DEBUG(3, ("rpc_dc_name: Returning DC %s (%s) for domain %s\n", srv_name,
+ inet_ntoa(dc_ip), domain));
+
+ *ip_out = dc_ip;
+
+ SAFE_FREE(ip_list);
+
+ return True;
+}
+
+/**********************************************************************
+ wrapper around ads and rpc methods of finds DC's
+**********************************************************************/
+
+BOOL get_dc_name(const char *domain, const char *realm, fstring srv_name, struct in_addr *ip_out)
+{
+ struct in_addr dc_ip;
+ BOOL ret;
+ BOOL our_domain = False;
+
+ zero_ip(&dc_ip);
+
+ ret = False;
+
+ if ( strequal(lp_workgroup(), domain) || strequal(lp_realm(), realm) )
+ our_domain = True;
+
+ /* always try to obey what the admin specified in smb.conf
+ (for the local domain) */
+
+ if ( (our_domain && lp_security()==SEC_ADS) || realm ) {
+ ret = ads_dc_name(domain, realm, &dc_ip, srv_name);
+ }
+
+ if (!ret) {
+ /* fall back on rpc methods if the ADS methods fail */
+ ret = rpc_dc_name(domain, srv_name, &dc_ip);
+ }
+
+ *ip_out = dc_ip;
+
+ return ret;
+}
+
diff --git a/source/libsmb/nmblib.c b/source/libsmb/nmblib.c
new file mode 100644
index 00000000000..3c25eba744f
--- /dev/null
+++ b/source/libsmb/nmblib.c
@@ -0,0 +1,1343 @@
+/*
+ Unix SMB/CIFS implementation.
+ NBT netbios library routines
+ Copyright (C) Andrew Tridgell 1994-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 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"
+
+int num_good_sends = 0;
+int num_good_receives = 0;
+
+static const struct opcode_names {
+ const char *nmb_opcode_name;
+ int opcode;
+} nmb_header_opcode_names[] = {
+ {"Query", 0 },
+ {"Registration", 5 },
+ {"Release", 6 },
+ {"WACK", 7 },
+ {"Refresh", 8 },
+ {"Refresh(altcode)", 9 },
+ {"Multi-homed Registration", 15 },
+ {0, -1 }
+};
+
+/****************************************************************************
+ Lookup a nmb opcode name.
+****************************************************************************/
+
+static const char *lookup_opcode_name( int opcode )
+{
+ const struct opcode_names *op_namep;
+ int i;
+
+ for(i = 0; nmb_header_opcode_names[i].nmb_opcode_name != 0; i++) {
+ op_namep = &nmb_header_opcode_names[i];
+ if(opcode == op_namep->opcode)
+ return op_namep->nmb_opcode_name;
+ }
+ return "<unknown opcode>";
+}
+
+/****************************************************************************
+ Print out a res_rec structure.
+****************************************************************************/
+
+static void debug_nmb_res_rec(struct res_rec *res, const char *hdr)
+{
+ int i, j;
+
+ DEBUGADD( 4, ( " %s: nmb_name=%s rr_type=%d rr_class=%d ttl=%d\n",
+ hdr,
+ nmb_namestr(&res->rr_name),
+ res->rr_type,
+ res->rr_class,
+ res->ttl ) );
+
+ if( res->rdlength == 0 || res->rdata == NULL )
+ return;
+
+ for (i = 0; i < res->rdlength; i+= MAX_NETBIOSNAME_LEN) {
+ DEBUGADD(4, (" %s %3x char ", hdr, i));
+
+ for (j = 0; j < MAX_NETBIOSNAME_LEN; j++) {
+ unsigned char x = res->rdata[i+j];
+ if (x < 32 || x > 127)
+ x = '.';
+
+ if (i+j >= res->rdlength)
+ break;
+ DEBUGADD(4, ("%c", x));
+ }
+
+ DEBUGADD(4, (" hex "));
+
+ for (j = 0; j < MAX_NETBIOSNAME_LEN; j++) {
+ if (i+j >= res->rdlength)
+ break;
+ DEBUGADD(4, ("%02X", (unsigned char)res->rdata[i+j]));
+ }
+
+ DEBUGADD(4, ("\n"));
+ }
+}
+
+/****************************************************************************
+ Process a nmb packet.
+****************************************************************************/
+
+void debug_nmb_packet(struct packet_struct *p)
+{
+ struct nmb_packet *nmb = &p->packet.nmb;
+
+ if( DEBUGLVL( 4 ) ) {
+ dbgtext( "nmb packet from %s(%d) header: id=%d opcode=%s(%d) response=%s\n",
+ inet_ntoa(p->ip), p->port,
+ nmb->header.name_trn_id,
+ lookup_opcode_name(nmb->header.opcode),
+ nmb->header.opcode,
+ BOOLSTR(nmb->header.response) );
+ dbgtext( " header: flags: bcast=%s rec_avail=%s rec_des=%s trunc=%s auth=%s\n",
+ BOOLSTR(nmb->header.nm_flags.bcast),
+ BOOLSTR(nmb->header.nm_flags.recursion_available),
+ BOOLSTR(nmb->header.nm_flags.recursion_desired),
+ BOOLSTR(nmb->header.nm_flags.trunc),
+ BOOLSTR(nmb->header.nm_flags.authoritative) );
+ dbgtext( " header: rcode=%d qdcount=%d ancount=%d nscount=%d arcount=%d\n",
+ nmb->header.rcode,
+ nmb->header.qdcount,
+ nmb->header.ancount,
+ nmb->header.nscount,
+ nmb->header.arcount );
+ }
+
+ if (nmb->header.qdcount) {
+ DEBUGADD( 4, ( " question: q_name=%s q_type=%d q_class=%d\n",
+ nmb_namestr(&nmb->question.question_name),
+ nmb->question.question_type,
+ nmb->question.question_class) );
+ }
+
+ if (nmb->answers && nmb->header.ancount) {
+ debug_nmb_res_rec(nmb->answers,"answers");
+ }
+ if (nmb->nsrecs && nmb->header.nscount) {
+ debug_nmb_res_rec(nmb->nsrecs,"nsrecs");
+ }
+ if (nmb->additional && nmb->header.arcount) {
+ debug_nmb_res_rec(nmb->additional,"additional");
+ }
+}
+
+/*******************************************************************
+ Handle "compressed" name pointers.
+******************************************************************/
+
+static BOOL handle_name_ptrs(unsigned char *ubuf,int *offset,int length,
+ BOOL *got_pointer,int *ret)
+{
+ int loop_count=0;
+
+ while ((ubuf[*offset] & 0xC0) == 0xC0) {
+ if (!*got_pointer)
+ (*ret) += 2;
+ (*got_pointer)=True;
+ (*offset) = ((ubuf[*offset] & ~0xC0)<<8) | ubuf[(*offset)+1];
+ if (loop_count++ == 10 || (*offset) < 0 || (*offset)>(length-2)) {
+ return(False);
+ }
+ }
+ return(True);
+}
+
+/*******************************************************************
+ Parse a nmb name from "compressed" format to something readable
+ return the space taken by the name, or 0 if the name is invalid
+******************************************************************/
+
+static int parse_nmb_name(char *inbuf,int ofs,int length, struct nmb_name *name)
+{
+ int m,n=0;
+ unsigned char *ubuf = (unsigned char *)inbuf;
+ int ret = 0;
+ BOOL got_pointer=False;
+ int loop_count=0;
+ int offset = ofs;
+
+ if (length - offset < 2)
+ return(0);
+
+ /* handle initial name pointers */
+ if (!handle_name_ptrs(ubuf,&offset,length,&got_pointer,&ret))
+ return(0);
+
+ m = ubuf[offset];
+
+ if (!m)
+ return(0);
+ if ((m & 0xC0) || offset+m+2 > length)
+ return(0);
+
+ memset((char *)name,'\0',sizeof(*name));
+
+ /* the "compressed" part */
+ if (!got_pointer)
+ ret += m + 2;
+ offset++;
+ while (m > 0) {
+ unsigned char c1,c2;
+ c1 = ubuf[offset++]-'A';
+ c2 = ubuf[offset++]-'A';
+ if ((c1 & 0xF0) || (c2 & 0xF0) || (n > sizeof(name->name)-1))
+ return(0);
+ name->name[n++] = (c1<<4) | c2;
+ m -= 2;
+ }
+ name->name[n] = 0;
+
+ if (n==MAX_NETBIOSNAME_LEN) {
+ /* parse out the name type, its always in the 16th byte of the name */
+ name->name_type = ((unsigned char)name->name[15]) & 0xff;
+
+ /* remove trailing spaces */
+ name->name[15] = 0;
+ n = 14;
+ while (n && name->name[n]==' ')
+ name->name[n--] = 0;
+ }
+
+ /* now the domain parts (if any) */
+ n = 0;
+ while (ubuf[offset]) {
+ /* we can have pointers within the domain part as well */
+ if (!handle_name_ptrs(ubuf,&offset,length,&got_pointer,&ret))
+ return(0);
+
+ m = ubuf[offset];
+ /*
+ * Don't allow null domain parts.
+ */
+ if (!m)
+ return(0);
+ if (!got_pointer)
+ ret += m+1;
+ if (n)
+ name->scope[n++] = '.';
+ if (m+2+offset>length || n+m+1>sizeof(name->scope))
+ return(0);
+ offset++;
+ while (m--)
+ name->scope[n++] = (char)ubuf[offset++];
+
+ /*
+ * Watch for malicious loops.
+ */
+ if (loop_count++ == 10)
+ return 0;
+ }
+ name->scope[n++] = 0;
+
+ return(ret);
+}
+
+/****************************************************************************
+ Put a netbios name, padding(s) and a name type into a 16 character buffer.
+ name is already in DOS charset.
+ [15 bytes name + padding][1 byte name type].
+****************************************************************************/
+
+static void put_name(char *dest, const char *name, int pad, unsigned int name_type)
+{
+ size_t len = strlen(name);
+
+ memcpy(dest, name, (len < MAX_NETBIOSNAME_LEN) ? len : MAX_NETBIOSNAME_LEN - 1);
+ if (len < MAX_NETBIOSNAME_LEN - 1) {
+ memset(dest + len, pad, MAX_NETBIOSNAME_LEN - 1 - len);
+ }
+ dest[MAX_NETBIOSNAME_LEN - 1] = name_type;
+}
+
+/*******************************************************************
+ Put a compressed nmb name into a buffer. Return the length of the
+ compressed name.
+
+ Compressed names are really weird. The "compression" doubles the
+ size. The idea is that it also means that compressed names conform
+ to the doman name system. See RFC1002.
+******************************************************************/
+
+static int put_nmb_name(char *buf,int offset,struct nmb_name *name)
+{
+ int ret,m;
+ nstring buf1;
+ char *p;
+
+ if (strcmp(name->name,"*") == 0) {
+ /* special case for wildcard name */
+ put_name(buf1, "*", '\0', name->name_type);
+ } else {
+ put_name(buf1, name->name, ' ', name->name_type);
+ }
+
+ buf[offset] = 0x20;
+
+ ret = 34;
+
+ for (m=0;m<MAX_NETBIOSNAME_LEN;m++) {
+ buf[offset+1+2*m] = 'A' + ((buf1[m]>>4)&0xF);
+ buf[offset+2+2*m] = 'A' + (buf1[m]&0xF);
+ }
+ offset += 33;
+
+ buf[offset] = 0;
+
+ if (name->scope[0]) {
+ /* XXXX this scope handling needs testing */
+ ret += strlen(name->scope) + 1;
+ safe_strcpy(&buf[offset+1],name->scope,sizeof(name->scope));
+
+ p = &buf[offset+1];
+ while ((p = strchr_m(p,'.'))) {
+ buf[offset] = PTR_DIFF(p,&buf[offset+1]);
+ offset += (buf[offset] + 1);
+ p = &buf[offset+1];
+ }
+ buf[offset] = strlen(&buf[offset+1]);
+ }
+
+ return(ret);
+}
+
+/*******************************************************************
+ Useful for debugging messages.
+******************************************************************/
+
+char *nmb_namestr(struct nmb_name *n)
+{
+ static int i=0;
+ static fstring ret[4];
+ fstring name;
+ char *p = ret[i];
+
+ pull_ascii_fstring(name, n->name);
+ if (!n->scope[0])
+ slprintf(p,sizeof(fstring)-1, "%s<%02x>",name,n->name_type);
+ else
+ slprintf(p,sizeof(fstring)-1, "%s<%02x>.%s",name,n->name_type,n->scope);
+
+ i = (i+1)%4;
+ return(p);
+}
+
+/*******************************************************************
+ Allocate and parse some resource records.
+******************************************************************/
+
+static BOOL parse_alloc_res_rec(char *inbuf,int *offset,int length,
+ struct res_rec **recs, int count)
+{
+ int i;
+
+ *recs = (struct res_rec *)malloc(sizeof(**recs)*count);
+ if (!*recs)
+ return(False);
+
+ memset((char *)*recs,'\0',sizeof(**recs)*count);
+
+ for (i=0;i<count;i++) {
+ int l = parse_nmb_name(inbuf,*offset,length,&(*recs)[i].rr_name);
+ (*offset) += l;
+ if (!l || (*offset)+10 > length) {
+ SAFE_FREE(*recs);
+ return(False);
+ }
+ (*recs)[i].rr_type = RSVAL(inbuf,(*offset));
+ (*recs)[i].rr_class = RSVAL(inbuf,(*offset)+2);
+ (*recs)[i].ttl = RIVAL(inbuf,(*offset)+4);
+ (*recs)[i].rdlength = RSVAL(inbuf,(*offset)+8);
+ (*offset) += 10;
+ if ((*recs)[i].rdlength>sizeof((*recs)[i].rdata) ||
+ (*offset)+(*recs)[i].rdlength > length) {
+ SAFE_FREE(*recs);
+ return(False);
+ }
+ memcpy((*recs)[i].rdata,inbuf+(*offset),(*recs)[i].rdlength);
+ (*offset) += (*recs)[i].rdlength;
+ }
+ return(True);
+}
+
+/*******************************************************************
+ Put a resource record into a packet.
+******************************************************************/
+
+static int put_res_rec(char *buf,int offset,struct res_rec *recs,int count)
+{
+ int ret=0;
+ int i;
+
+ for (i=0;i<count;i++) {
+ int l = put_nmb_name(buf,offset,&recs[i].rr_name);
+ offset += l;
+ ret += l;
+ RSSVAL(buf,offset,recs[i].rr_type);
+ RSSVAL(buf,offset+2,recs[i].rr_class);
+ RSIVAL(buf,offset+4,recs[i].ttl);
+ RSSVAL(buf,offset+8,recs[i].rdlength);
+ memcpy(buf+offset+10,recs[i].rdata,recs[i].rdlength);
+ offset += 10+recs[i].rdlength;
+ ret += 10+recs[i].rdlength;
+ }
+
+ return(ret);
+}
+
+/*******************************************************************
+ Put a compressed name pointer record into a packet.
+******************************************************************/
+
+static int put_compressed_name_ptr(unsigned char *buf,int offset,struct res_rec *rec,int ptr_offset)
+{
+ int ret=0;
+ buf[offset] = (0xC0 | ((ptr_offset >> 8) & 0xFF));
+ buf[offset+1] = (ptr_offset & 0xFF);
+ offset += 2;
+ ret += 2;
+ RSSVAL(buf,offset,rec->rr_type);
+ RSSVAL(buf,offset+2,rec->rr_class);
+ RSIVAL(buf,offset+4,rec->ttl);
+ RSSVAL(buf,offset+8,rec->rdlength);
+ memcpy(buf+offset+10,rec->rdata,rec->rdlength);
+ offset += 10+rec->rdlength;
+ ret += 10+rec->rdlength;
+
+ return(ret);
+}
+
+/*******************************************************************
+ Parse a dgram packet. Return False if the packet can't be parsed
+ or is invalid for some reason, True otherwise.
+
+ This is documented in section 4.4.1 of RFC1002.
+******************************************************************/
+
+static BOOL parse_dgram(char *inbuf,int length,struct dgram_packet *dgram)
+{
+ int offset;
+ int flags;
+
+ memset((char *)dgram,'\0',sizeof(*dgram));
+
+ if (length < 14)
+ return(False);
+
+ dgram->header.msg_type = CVAL(inbuf,0);
+ flags = CVAL(inbuf,1);
+ dgram->header.flags.node_type = (enum node_type)((flags>>2)&3);
+ if (flags & 1)
+ dgram->header.flags.more = True;
+ if (flags & 2)
+ dgram->header.flags.first = True;
+ dgram->header.dgm_id = RSVAL(inbuf,2);
+ putip((char *)&dgram->header.source_ip,inbuf+4);
+ dgram->header.source_port = RSVAL(inbuf,8);
+ dgram->header.dgm_length = RSVAL(inbuf,10);
+ dgram->header.packet_offset = RSVAL(inbuf,12);
+
+ offset = 14;
+
+ if (dgram->header.msg_type == 0x10 ||
+ dgram->header.msg_type == 0x11 ||
+ dgram->header.msg_type == 0x12) {
+ offset += parse_nmb_name(inbuf,offset,length,&dgram->source_name);
+ offset += parse_nmb_name(inbuf,offset,length,&dgram->dest_name);
+ }
+
+ if (offset >= length || (length-offset > sizeof(dgram->data)))
+ return(False);
+
+ dgram->datasize = length-offset;
+ memcpy(dgram->data,inbuf+offset,dgram->datasize);
+
+ return(True);
+}
+
+/*******************************************************************
+ Parse a nmb packet. Return False if the packet can't be parsed
+ or is invalid for some reason, True otherwise.
+******************************************************************/
+
+static BOOL parse_nmb(char *inbuf,int length,struct nmb_packet *nmb)
+{
+ int nm_flags,offset;
+
+ memset((char *)nmb,'\0',sizeof(*nmb));
+
+ if (length < 12)
+ return(False);
+
+ /* parse the header */
+ nmb->header.name_trn_id = RSVAL(inbuf,0);
+
+ DEBUG(10,("parse_nmb: packet id = %d\n", nmb->header.name_trn_id));
+
+ nmb->header.opcode = (CVAL(inbuf,2) >> 3) & 0xF;
+ nmb->header.response = ((CVAL(inbuf,2)>>7)&1)?True:False;
+ nm_flags = ((CVAL(inbuf,2) & 0x7) << 4) + (CVAL(inbuf,3)>>4);
+ nmb->header.nm_flags.bcast = (nm_flags&1)?True:False;
+ nmb->header.nm_flags.recursion_available = (nm_flags&8)?True:False;
+ nmb->header.nm_flags.recursion_desired = (nm_flags&0x10)?True:False;
+ nmb->header.nm_flags.trunc = (nm_flags&0x20)?True:False;
+ nmb->header.nm_flags.authoritative = (nm_flags&0x40)?True:False;
+ nmb->header.rcode = CVAL(inbuf,3) & 0xF;
+ nmb->header.qdcount = RSVAL(inbuf,4);
+ nmb->header.ancount = RSVAL(inbuf,6);
+ nmb->header.nscount = RSVAL(inbuf,8);
+ nmb->header.arcount = RSVAL(inbuf,10);
+
+ if (nmb->header.qdcount) {
+ offset = parse_nmb_name(inbuf,12,length,&nmb->question.question_name);
+ if (!offset)
+ return(False);
+
+ if (length - (12+offset) < 4)
+ return(False);
+ nmb->question.question_type = RSVAL(inbuf,12+offset);
+ nmb->question.question_class = RSVAL(inbuf,12+offset+2);
+
+ offset += 12+4;
+ } else {
+ offset = 12;
+ }
+
+ /* and any resource records */
+ if (nmb->header.ancount && !parse_alloc_res_rec(inbuf,&offset,length,&nmb->answers,
+ nmb->header.ancount))
+ return(False);
+
+ if (nmb->header.nscount && !parse_alloc_res_rec(inbuf,&offset,length,&nmb->nsrecs,
+ nmb->header.nscount))
+ return(False);
+
+ if (nmb->header.arcount && !parse_alloc_res_rec(inbuf,&offset,length,&nmb->additional,
+ nmb->header.arcount))
+ return(False);
+
+ return(True);
+}
+
+/*******************************************************************
+ 'Copy constructor' for an nmb packet.
+******************************************************************/
+
+static struct packet_struct *copy_nmb_packet(struct packet_struct *packet)
+{
+ struct nmb_packet *nmb;
+ struct nmb_packet *copy_nmb;
+ struct packet_struct *pkt_copy;
+
+ if(( pkt_copy = (struct packet_struct *)malloc(sizeof(*packet))) == NULL) {
+ DEBUG(0,("copy_nmb_packet: malloc fail.\n"));
+ return NULL;
+ }
+
+ /* Structure copy of entire thing. */
+
+ *pkt_copy = *packet;
+
+ /* Ensure this copy is not locked. */
+ pkt_copy->locked = False;
+
+ /* Ensure this copy has no resource records. */
+ nmb = &packet->packet.nmb;
+ copy_nmb = &pkt_copy->packet.nmb;
+
+ copy_nmb->answers = NULL;
+ copy_nmb->nsrecs = NULL;
+ copy_nmb->additional = NULL;
+
+ /* Now copy any resource records. */
+
+ if (nmb->answers) {
+ if((copy_nmb->answers = (struct res_rec *)
+ malloc(nmb->header.ancount * sizeof(struct res_rec))) == NULL)
+ goto free_and_exit;
+ memcpy((char *)copy_nmb->answers, (char *)nmb->answers,
+ nmb->header.ancount * sizeof(struct res_rec));
+ }
+ if (nmb->nsrecs) {
+ if((copy_nmb->nsrecs = (struct res_rec *)
+ malloc(nmb->header.nscount * sizeof(struct res_rec))) == NULL)
+ goto free_and_exit;
+ memcpy((char *)copy_nmb->nsrecs, (char *)nmb->nsrecs,
+ nmb->header.nscount * sizeof(struct res_rec));
+ }
+ if (nmb->additional) {
+ if((copy_nmb->additional = (struct res_rec *)
+ malloc(nmb->header.arcount * sizeof(struct res_rec))) == NULL)
+ goto free_and_exit;
+ memcpy((char *)copy_nmb->additional, (char *)nmb->additional,
+ nmb->header.arcount * sizeof(struct res_rec));
+ }
+
+ return pkt_copy;
+
+ free_and_exit:
+
+ SAFE_FREE(copy_nmb->answers);
+ SAFE_FREE(copy_nmb->nsrecs);
+ SAFE_FREE(copy_nmb->additional);
+ SAFE_FREE(pkt_copy);
+
+ DEBUG(0,("copy_nmb_packet: malloc fail in resource records.\n"));
+ return NULL;
+}
+
+/*******************************************************************
+ 'Copy constructor' for a dgram packet.
+******************************************************************/
+
+static struct packet_struct *copy_dgram_packet(struct packet_struct *packet)
+{
+ struct packet_struct *pkt_copy;
+
+ if(( pkt_copy = (struct packet_struct *)malloc(sizeof(*packet))) == NULL) {
+ DEBUG(0,("copy_dgram_packet: malloc fail.\n"));
+ return NULL;
+ }
+
+ /* Structure copy of entire thing. */
+
+ *pkt_copy = *packet;
+
+ /* Ensure this copy is not locked. */
+ pkt_copy->locked = False;
+
+ /* There are no additional pointers in a dgram packet,
+ we are finished. */
+ return pkt_copy;
+}
+
+/*******************************************************************
+ 'Copy constructor' for a generic packet.
+******************************************************************/
+
+struct packet_struct *copy_packet(struct packet_struct *packet)
+{
+ if(packet->packet_type == NMB_PACKET)
+ return copy_nmb_packet(packet);
+ else if (packet->packet_type == DGRAM_PACKET)
+ return copy_dgram_packet(packet);
+ return NULL;
+}
+
+/*******************************************************************
+ Free up any resources associated with an nmb packet.
+******************************************************************/
+
+static void free_nmb_packet(struct nmb_packet *nmb)
+{
+ SAFE_FREE(nmb->answers);
+ SAFE_FREE(nmb->nsrecs);
+ SAFE_FREE(nmb->additional);
+}
+
+/*******************************************************************
+ Free up any resources associated with a dgram packet.
+******************************************************************/
+
+static void free_dgram_packet(struct dgram_packet *nmb)
+{
+ /* We have nothing to do for a dgram packet. */
+}
+
+/*******************************************************************
+ Free up any resources associated with a packet.
+******************************************************************/
+
+void free_packet(struct packet_struct *packet)
+{
+ if (packet->locked)
+ return;
+ if (packet->packet_type == NMB_PACKET)
+ free_nmb_packet(&packet->packet.nmb);
+ else if (packet->packet_type == DGRAM_PACKET)
+ free_dgram_packet(&packet->packet.dgram);
+ ZERO_STRUCTPN(packet);
+ SAFE_FREE(packet);
+}
+
+/*******************************************************************
+ Parse a packet buffer into a packet structure.
+******************************************************************/
+
+struct packet_struct *parse_packet(char *buf,int length,
+ enum packet_type packet_type)
+{
+ extern struct in_addr lastip;
+ extern int lastport;
+ struct packet_struct *p;
+ BOOL ok=False;
+
+ p = (struct packet_struct *)malloc(sizeof(*p));
+ if (!p)
+ return(NULL);
+
+ p->next = NULL;
+ p->prev = NULL;
+ p->ip = lastip;
+ p->port = lastport;
+ p->locked = False;
+ p->timestamp = time(NULL);
+ p->packet_type = packet_type;
+
+ switch (packet_type) {
+ case NMB_PACKET:
+ ok = parse_nmb(buf,length,&p->packet.nmb);
+ break;
+
+ case DGRAM_PACKET:
+ ok = parse_dgram(buf,length,&p->packet.dgram);
+ break;
+ }
+
+ if (!ok) {
+ free_packet(p);
+ return NULL;
+ }
+
+ return p;
+}
+
+/*******************************************************************
+ Read a packet from a socket and parse it, returning a packet ready
+ to be used or put on the queue. This assumes a UDP socket.
+******************************************************************/
+
+struct packet_struct *read_packet(int fd,enum packet_type packet_type)
+{
+ struct packet_struct *packet;
+ char buf[MAX_DGRAM_SIZE];
+ int length;
+
+ length = read_udp_socket(fd,buf,sizeof(buf));
+ if (length < MIN_DGRAM_SIZE)
+ return(NULL);
+
+ packet = parse_packet(buf, length, packet_type);
+ if (!packet)
+ return NULL;
+
+ packet->fd = fd;
+
+ num_good_receives++;
+
+ DEBUG(5,("Received a packet of len %d from (%s) port %d\n",
+ length, inet_ntoa(packet->ip), packet->port ) );
+
+ return(packet);
+}
+
+/*******************************************************************
+ Send a udp packet on a already open socket.
+******************************************************************/
+
+static BOOL send_udp(int fd,char *buf,int len,struct in_addr ip,int port)
+{
+ BOOL ret = False;
+ int i;
+ struct sockaddr_in sock_out;
+
+ /* set the address and port */
+ memset((char *)&sock_out,'\0',sizeof(sock_out));
+ putip((char *)&sock_out.sin_addr,(char *)&ip);
+ sock_out.sin_port = htons( port );
+ sock_out.sin_family = AF_INET;
+
+ DEBUG( 5, ( "Sending a packet of len %d to (%s) on port %d\n",
+ len, inet_ntoa(ip), port ) );
+
+ /*
+ * Patch to fix asynch error notifications from Linux kernel.
+ */
+
+ for (i = 0; i < 5; i++) {
+ ret = (sendto(fd,buf,len,0,(struct sockaddr *)&sock_out, sizeof(sock_out)) >= 0);
+ if (ret || errno != ECONNREFUSED)
+ break;
+ }
+
+ if (!ret)
+ DEBUG(0,("Packet send failed to %s(%d) ERRNO=%s\n",
+ inet_ntoa(ip),port,strerror(errno)));
+
+ if (ret)
+ num_good_sends++;
+
+ return(ret);
+}
+
+/*******************************************************************
+ Build a dgram packet ready for sending.
+
+ XXXX This currently doesn't handle packets too big for one
+ datagram. It should split them and use the packet_offset, more and
+ first flags to handle the fragmentation. Yuck.
+
+ [...but it isn't clear that we would ever need to send a
+ a fragmented NBT Datagram. The IP layer does its own
+ fragmentation to ensure that messages can fit into the path
+ MTU. It *is* important to be able to receive and rebuild
+ fragmented NBT datagrams, just in case someone out there
+ really has implemented this 'feature'. crh -)------ ]
+
+******************************************************************/
+
+static int build_dgram(char *buf,struct packet_struct *p)
+{
+ struct dgram_packet *dgram = &p->packet.dgram;
+ unsigned char *ubuf = (unsigned char *)buf;
+ int offset=0;
+
+ /* put in the header */
+ ubuf[0] = dgram->header.msg_type;
+ ubuf[1] = (((int)dgram->header.flags.node_type)<<2);
+ if (dgram->header.flags.more)
+ ubuf[1] |= 1;
+ if (dgram->header.flags.first)
+ ubuf[1] |= 2;
+ RSSVAL(ubuf,2,dgram->header.dgm_id);
+ putip(ubuf+4,(char *)&dgram->header.source_ip);
+ RSSVAL(ubuf,8,dgram->header.source_port);
+ RSSVAL(ubuf,12,dgram->header.packet_offset);
+
+ offset = 14;
+
+ if (dgram->header.msg_type == 0x10 ||
+ dgram->header.msg_type == 0x11 ||
+ dgram->header.msg_type == 0x12) {
+ offset += put_nmb_name((char *)ubuf,offset,&dgram->source_name);
+ offset += put_nmb_name((char *)ubuf,offset,&dgram->dest_name);
+ }
+
+ memcpy(ubuf+offset,dgram->data,dgram->datasize);
+ offset += dgram->datasize;
+
+ /* automatically set the dgm_length
+ * NOTE: RFC1002 says the dgm_length does *not*
+ * include the fourteen-byte header. crh
+ */
+ dgram->header.dgm_length = (offset - 14);
+ RSSVAL(ubuf,10,dgram->header.dgm_length);
+
+ return(offset);
+}
+
+/*******************************************************************
+ Build a nmb name
+*******************************************************************/
+
+void make_nmb_name( struct nmb_name *n, const char *name, int type)
+{
+ fstring unix_name;
+ memset( (char *)n, '\0', sizeof(struct nmb_name) );
+ fstrcpy(unix_name, name);
+ strupper_m(unix_name);
+ push_ascii(n->name, unix_name, sizeof(n->name), STR_TERMINATE);
+ n->name_type = (unsigned int)type & 0xFF;
+ push_ascii(n->scope, global_scope(), 64, STR_TERMINATE);
+}
+
+/*******************************************************************
+ Compare two nmb names
+******************************************************************/
+
+BOOL nmb_name_equal(struct nmb_name *n1, struct nmb_name *n2)
+{
+ return ((n1->name_type == n2->name_type) &&
+ strequal(n1->name ,n2->name ) &&
+ strequal(n1->scope,n2->scope));
+}
+
+/*******************************************************************
+ Build a nmb packet ready for sending.
+
+ XXXX this currently relies on not being passed something that expands
+ to a packet too big for the buffer. Eventually this should be
+ changed to set the trunc bit so the receiver can request the rest
+ via tcp (when that becomes supported)
+******************************************************************/
+
+static int build_nmb(char *buf,struct packet_struct *p)
+{
+ struct nmb_packet *nmb = &p->packet.nmb;
+ unsigned char *ubuf = (unsigned char *)buf;
+ int offset=0;
+
+ /* put in the header */
+ RSSVAL(ubuf,offset,nmb->header.name_trn_id);
+ ubuf[offset+2] = (nmb->header.opcode & 0xF) << 3;
+ if (nmb->header.response)
+ ubuf[offset+2] |= (1<<7);
+ if (nmb->header.nm_flags.authoritative &&
+ nmb->header.response)
+ ubuf[offset+2] |= 0x4;
+ if (nmb->header.nm_flags.trunc)
+ ubuf[offset+2] |= 0x2;
+ if (nmb->header.nm_flags.recursion_desired)
+ ubuf[offset+2] |= 0x1;
+ if (nmb->header.nm_flags.recursion_available &&
+ nmb->header.response)
+ ubuf[offset+3] |= 0x80;
+ if (nmb->header.nm_flags.bcast)
+ ubuf[offset+3] |= 0x10;
+ ubuf[offset+3] |= (nmb->header.rcode & 0xF);
+
+ RSSVAL(ubuf,offset+4,nmb->header.qdcount);
+ RSSVAL(ubuf,offset+6,nmb->header.ancount);
+ RSSVAL(ubuf,offset+8,nmb->header.nscount);
+ RSSVAL(ubuf,offset+10,nmb->header.arcount);
+
+ offset += 12;
+ if (nmb->header.qdcount) {
+ /* XXXX this doesn't handle a qdcount of > 1 */
+ offset += put_nmb_name((char *)ubuf,offset,&nmb->question.question_name);
+ RSSVAL(ubuf,offset,nmb->question.question_type);
+ RSSVAL(ubuf,offset+2,nmb->question.question_class);
+ offset += 4;
+ }
+
+ if (nmb->header.ancount)
+ offset += put_res_rec((char *)ubuf,offset,nmb->answers,
+ nmb->header.ancount);
+
+ if (nmb->header.nscount)
+ offset += put_res_rec((char *)ubuf,offset,nmb->nsrecs,
+ nmb->header.nscount);
+
+ /*
+ * The spec says we must put compressed name pointers
+ * in the following outgoing packets :
+ * NAME_REGISTRATION_REQUEST, NAME_REFRESH_REQUEST,
+ * NAME_RELEASE_REQUEST.
+ */
+
+ if((nmb->header.response == False) &&
+ ((nmb->header.opcode == NMB_NAME_REG_OPCODE) ||
+ (nmb->header.opcode == NMB_NAME_RELEASE_OPCODE) ||
+ (nmb->header.opcode == NMB_NAME_REFRESH_OPCODE_8) ||
+ (nmb->header.opcode == NMB_NAME_REFRESH_OPCODE_9) ||
+ (nmb->header.opcode == NMB_NAME_MULTIHOMED_REG_OPCODE)) &&
+ (nmb->header.arcount == 1)) {
+
+ offset += put_compressed_name_ptr(ubuf,offset,nmb->additional,12);
+
+ } else if (nmb->header.arcount) {
+ offset += put_res_rec((char *)ubuf,offset,nmb->additional,
+ nmb->header.arcount);
+ }
+ return(offset);
+}
+
+/*******************************************************************
+ Linearise a packet.
+******************************************************************/
+
+int build_packet(char *buf, struct packet_struct *p)
+{
+ int len = 0;
+
+ switch (p->packet_type) {
+ case NMB_PACKET:
+ len = build_nmb(buf,p);
+ break;
+
+ case DGRAM_PACKET:
+ len = build_dgram(buf,p);
+ break;
+ }
+
+ return len;
+}
+
+/*******************************************************************
+ Send a packet_struct.
+******************************************************************/
+
+BOOL send_packet(struct packet_struct *p)
+{
+ char buf[1024];
+ int len=0;
+
+ memset(buf,'\0',sizeof(buf));
+
+ len = build_packet(buf, p);
+
+ if (!len)
+ return(False);
+
+ return(send_udp(p->fd,buf,len,p->ip,p->port));
+}
+
+/****************************************************************************
+ Receive a packet with timeout on a open UDP filedescriptor.
+ The timeout is in milliseconds
+***************************************************************************/
+
+struct packet_struct *receive_packet(int fd,enum packet_type type,int t)
+{
+ fd_set fds;
+ struct timeval timeout;
+ int ret;
+
+ FD_ZERO(&fds);
+ FD_SET(fd,&fds);
+ timeout.tv_sec = t/1000;
+ timeout.tv_usec = 1000*(t%1000);
+
+ if ((ret = sys_select_intr(fd+1,&fds,NULL,NULL,&timeout)) == -1) {
+ /* errno should be EBADF or EINVAL. */
+ DEBUG(0,("select returned -1, errno = %s (%d)\n", strerror(errno), errno));
+ return NULL;
+ }
+
+ if (ret == 0) /* timeout */
+ return NULL;
+
+ if (FD_ISSET(fd,&fds))
+ return(read_packet(fd,type));
+
+ return(NULL);
+}
+
+/****************************************************************************
+ Receive a UDP/137 packet either via UDP or from the unexpected packet
+ queue. The packet must be a reply packet and have the specified trn_id.
+ The timeout is in milliseconds.
+***************************************************************************/
+
+struct packet_struct *receive_nmb_packet(int fd, int t, int trn_id)
+{
+ struct packet_struct *p;
+
+ p = receive_packet(fd, NMB_PACKET, t);
+
+ if (p && p->packet.nmb.header.response &&
+ p->packet.nmb.header.name_trn_id == trn_id) {
+ return p;
+ }
+ if (p)
+ free_packet(p);
+
+ /* try the unexpected packet queue */
+ return receive_unexpected(NMB_PACKET, trn_id, NULL);
+}
+
+/****************************************************************************
+ Receive a UDP/138 packet either via UDP or from the unexpected packet
+ queue. The packet must be a reply packet and have the specified mailslot name
+ The timeout is in milliseconds.
+***************************************************************************/
+
+struct packet_struct *receive_dgram_packet(int fd, int t, const char *mailslot_name)
+{
+ struct packet_struct *p;
+
+ p = receive_packet(fd, DGRAM_PACKET, t);
+
+ if (p && match_mailslot_name(p, mailslot_name)) {
+ return p;
+ }
+ if (p)
+ free_packet(p);
+
+ /* try the unexpected packet queue */
+ return receive_unexpected(DGRAM_PACKET, 0, mailslot_name);
+}
+
+/****************************************************************************
+ See if a datagram has the right mailslot name.
+***************************************************************************/
+
+BOOL match_mailslot_name(struct packet_struct *p, const char *mailslot_name)
+{
+ struct dgram_packet *dgram = &p->packet.dgram;
+ char *buf;
+
+ buf = &dgram->data[0];
+ buf -= 4;
+
+ buf = smb_buf(buf);
+
+ if (memcmp(buf, mailslot_name, strlen(mailslot_name)+1) == 0) {
+ return True;
+ }
+
+ return False;
+}
+
+/****************************************************************************
+ Return the number of bits that match between two 4 character buffers
+***************************************************************************/
+
+int matching_quad_bits(unsigned char *p1, unsigned char *p2)
+{
+ int i, j, ret = 0;
+ for (i=0; i<4; i++) {
+ if (p1[i] != p2[i])
+ break;
+ ret += 8;
+ }
+
+ if (i==4)
+ return ret;
+
+ for (j=0; j<8; j++) {
+ if ((p1[i] & (1<<(7-j))) != (p2[i] & (1<<(7-j))))
+ break;
+ ret++;
+ }
+
+ return ret;
+}
+
+static unsigned char sort_ip[4];
+
+/****************************************************************************
+ Compare two query reply records.
+***************************************************************************/
+
+static int name_query_comp(unsigned char *p1, unsigned char *p2)
+{
+ return matching_quad_bits(p2+2, sort_ip) - matching_quad_bits(p1+2, sort_ip);
+}
+
+/****************************************************************************
+ Sort a set of 6 byte name query response records so that the IPs that
+ have the most leading bits in common with the specified address come first.
+***************************************************************************/
+
+void sort_query_replies(char *data, int n, struct in_addr ip)
+{
+ if (n <= 1)
+ return;
+
+ putip(sort_ip, (char *)&ip);
+
+ qsort(data, n, 6, QSORT_CAST name_query_comp);
+}
+
+#define TRUNCATE_NETBIOS_NAME 1
+
+/*******************************************************************
+ Convert, possibly using a stupid microsoft-ism which has destroyed
+ the transport independence of netbios (for CIFS vendors that usually
+ use the Win95-type methods, not for NT to NT communication, which uses
+ DCE/RPC and therefore full-length unicode strings...) a dns name into
+ a netbios name.
+
+ The netbios name (NOT necessarily null-terminated) is truncated to 15
+ characters.
+
+ ******************************************************************/
+
+char *dns_to_netbios_name(const char *dns_name)
+{
+ static nstring netbios_name;
+ int i;
+ StrnCpy(netbios_name, dns_name, MAX_NETBIOSNAME_LEN-1);
+ netbios_name[15] = 0;
+
+#ifdef TRUNCATE_NETBIOS_NAME
+ /* ok. this is because of a stupid microsoft-ism. if the called host
+ name contains a '.', microsoft clients expect you to truncate the
+ netbios name up to and including the '.' this even applies, by
+ mistake, to workgroup (domain) names, which is _really_ daft.
+ */
+ for (i = 0; i >= 15; i--) {
+ if (netbios_name[i] == '.') {
+ netbios_name[i] = 0;
+ break;
+ }
+ }
+#endif /* TRUNCATE_NETBIOS_NAME */
+
+ return netbios_name;
+}
+
+/****************************************************************************
+ Interpret the weird netbios "name" into a unix fstring. Return the name type.
+****************************************************************************/
+
+static int name_interpret(char *in, fstring name)
+{
+ int ret;
+ int len = (*in++) / 2;
+ fstring out_string;
+ char *out = out_string;
+
+ *out=0;
+
+ if (len > 30 || len<1)
+ return(0);
+
+ while (len--) {
+ if (in[0] < 'A' || in[0] > 'P' || in[1] < 'A' || in[1] > 'P') {
+ *out = 0;
+ return(0);
+ }
+ *out = ((in[0]-'A')<<4) + (in[1]-'A');
+ in += 2;
+ out++;
+ }
+ ret = out[-1];
+ out[-1] = 0;
+
+#ifdef NETBIOS_SCOPE
+ /* Handle any scope names */
+ while(*in) {
+ *out++ = '.'; /* Scope names are separated by periods */
+ len = *(unsigned char *)in++;
+ StrnCpy(out, in, len);
+ out += len;
+ *out=0;
+ in += len;
+ }
+#endif
+ pull_ascii_fstring(name, out_string);
+
+ return(ret);
+}
+
+/****************************************************************************
+ Mangle a name into netbios format.
+ Note: <Out> must be (33 + strlen(scope) + 2) bytes long, at minimum.
+****************************************************************************/
+
+int name_mangle( char *In, char *Out, char name_type )
+{
+ int i;
+ int len;
+ nstring buf;
+ char *p = Out;
+
+ /* Safely copy the input string, In, into buf[]. */
+ if (strcmp(In,"*") == 0)
+ put_name(buf, "*", '\0', 0x00);
+ else {
+ /* We use an fstring here as mb dos names can expend x3 when
+ going to utf8. */
+ fstring buf_unix;
+ nstring buf_dos;
+
+ pull_ascii_fstring(buf_unix, In);
+ strupper_m(buf_unix);
+
+ push_ascii_nstring(buf_dos, buf_unix);
+ put_name(buf, buf_dos, ' ', name_type);
+ }
+
+ /* Place the length of the first field into the output buffer. */
+ p[0] = 32;
+ p++;
+
+ /* Now convert the name to the rfc1001/1002 format. */
+ for( i = 0; i < MAX_NETBIOSNAME_LEN; i++ ) {
+ p[i*2] = ( (buf[i] >> 4) & 0x000F ) + 'A';
+ p[(i*2)+1] = (buf[i] & 0x000F) + 'A';
+ }
+ p += 32;
+ p[0] = '\0';
+
+ /* Add the scope string. */
+ for( i = 0, len = 0; *(global_scope()) != '\0'; i++, len++ ) {
+ switch( (global_scope())[i] ) {
+ case '\0':
+ p[0] = len;
+ if( len > 0 )
+ p[len+1] = 0;
+ return( name_len(Out) );
+ case '.':
+ p[0] = len;
+ p += (len + 1);
+ len = -1;
+ break;
+ default:
+ p[len+1] = (global_scope())[i];
+ break;
+ }
+ }
+
+ return( name_len(Out) );
+}
+
+/****************************************************************************
+ Find a pointer to a netbios name.
+****************************************************************************/
+
+static char *name_ptr(char *buf,int ofs)
+{
+ unsigned char c = *(unsigned char *)(buf+ofs);
+
+ if ((c & 0xC0) == 0xC0) {
+ uint16 l = RSVAL(buf, ofs) & 0x3FFF;
+ DEBUG(5,("name ptr to pos %d from %d is %s\n",l,ofs,buf+l));
+ return(buf + l);
+ } else {
+ return(buf+ofs);
+ }
+}
+
+/****************************************************************************
+ Extract a netbios name from a buf (into a unix string) return name type.
+****************************************************************************/
+
+int name_extract(char *buf,int ofs, fstring name)
+{
+ char *p = name_ptr(buf,ofs);
+ int d = PTR_DIFF(p,buf+ofs);
+
+ name[0] = '\0';
+ if (d < -50 || d > 50)
+ return(0);
+ return(name_interpret(p,name));
+}
+
+/****************************************************************************
+ Return the total storage length of a mangled name.
+****************************************************************************/
+
+int name_len(char *s1)
+{
+ /* NOTE: this argument _must_ be unsigned */
+ unsigned char *s = (unsigned char *)s1;
+ int len;
+
+ /* If the two high bits of the byte are set, return 2. */
+ if (0xC0 == (*s & 0xC0))
+ return(2);
+
+ /* Add up the length bytes. */
+ for (len = 1; (*s); s += (*s) + 1) {
+ len += *s + 1;
+ SMB_ASSERT(len < 80);
+ }
+
+ return(len);
+}
diff --git a/source/libsmb/nterr.c b/source/libsmb/nterr.c
new file mode 100644
index 00000000000..166229ec6c4
--- /dev/null
+++ b/source/libsmb/nterr.c
@@ -0,0 +1,719 @@
+/*
+ * 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 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.
+ */
+
+/* NT error codes. please read nterr.h */
+
+#include "includes.h"
+
+typedef const struct
+{
+ const char *nt_errstr;
+ NTSTATUS nt_errcode;
+} nt_err_code_struct;
+
+static nt_err_code_struct nt_errs[] =
+{
+ { "NT_STATUS_OK", NT_STATUS_OK },
+ { "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_NOT_A_REPARSE_POINT", NT_STATUS_NOT_A_REPARSE_POINT },
+ { "NT_STATUS_NO_MORE_ENTRIES", NT_STATUS_NO_MORE_ENTRIES },
+ { "STATUS_MORE_ENTRIES", STATUS_MORE_ENTRIES },
+ { "STATUS_SOME_UNMAPPED", STATUS_SOME_UNMAPPED },
+ { NULL, NT_STATUS(0) }
+};
+
+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 pstring msg;
+ int idx = 0;
+
+ slprintf(msg, sizeof(msg), "NT code 0x%08x", NT_STATUS_V(nt_code));
+
+ 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++;
+ }
+
+ 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 pstring out;
+ 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(char *nt_status_str)
+{
+ int idx = 0;
+
+ while (nt_errs[idx].nt_errstr != NULL) {
+ if (strcmp(nt_errs[idx].nt_errstr, nt_status_str) == 0) {
+ return nt_errs[idx].nt_errcode;
+ }
+ idx++;
+ }
+ return NT_STATUS_UNSUCCESSFUL;
+}
diff --git a/source/libsmb/ntlm_check.c b/source/libsmb/ntlm_check.c
new file mode 100644
index 00000000000..362b640f913
--- /dev/null
+++ b/source/libsmb/ntlm_check.c
@@ -0,0 +1,377 @@
+/*
+ Unix SMB/CIFS implementation.
+ Password and authentication handling
+ Copyright (C) Andrew Tridgell 1992-2000
+ Copyright (C) Luke Kenneth Casson Leighton 1996-2000
+ Copyright (C) Andrew Bartlett 2001-2003
+ Copyright (C) Gerald Carter 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 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"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_AUTH
+
+/****************************************************************************
+ Core of smb password checking routine.
+****************************************************************************/
+
+static BOOL smb_pwd_check_ntlmv1(const DATA_BLOB *nt_response,
+ const uchar *part_passwd,
+ const DATA_BLOB *sec_blob,
+ DATA_BLOB *user_sess_key)
+{
+ /* Finish the encryption of part_passwd. */
+ uchar 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 (user_sess_key != NULL) {
+ *user_sess_key = data_blob(NULL, 16);
+ SMBsesskeygen_ntv1(part_passwd, NULL, user_sess_key->data);
+ }
+
+
+#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
+ return (memcmp(p24, nt_response->data, 24) == 0);
+}
+
+/****************************************************************************
+ Core of smb password checking routine. (NTLMv2, LMv2)
+ Note: The same code works with both NTLMv2 and LMv2.
+****************************************************************************/
+
+static BOOL smb_pwd_check_ntlmv2(const DATA_BLOB *ntv2_response,
+ const uchar *part_passwd,
+ const DATA_BLOB *sec_blob,
+ const char *user, const char *domain,
+ DATA_BLOB *user_sess_key)
+{
+ /* Finish the encryption of part_passwd. */
+ uchar kr[16];
+ uchar value_from_encryption[16];
+ uchar client_response[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(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.
+ */
+
+ memcpy(client_response, ntv2_response->data, sizeof(client_response));
+
+ if (!ntv2_owf_gen(part_passwd, user, domain, kr)) {
+ return False;
+ }
+
+ SMBOWFencrypt_ntv2(kr, sec_blob, &client_key_data, value_from_encryption);
+ if (user_sess_key != NULL) {
+ *user_sess_key = data_blob(NULL, 16);
+ SMBsesskeygen_ntv2(kr, value_from_encryption, user_sess_key->data);
+ }
+
+#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);
+ return (memcmp(value_from_encryption, client_response, 16) == 0);
+}
+
+/**
+ * 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 nt_pw MD4 unicode password from our passdb or similar
+ * @param lm_pw LANMAN ASCII 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,
+ 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 uint8 *lm_pw, const uint8 *nt_pw,
+ DATA_BLOB *user_sess_key,
+ DATA_BLOB *lm_sess_key)
+{
+ static const unsigned char zeros[8];
+ if (nt_pw == NULL) {
+ DEBUG(3,("ntlm_password_check: NO NT password stored for user %s.\n",
+ username));
+ }
+
+ /* Check for cleartext netlogon. Used by Exchange 5.5. */
+ if (challenge->length == sizeof(zeros) &&
+ (memcmp(challenge->data, zeros, challenge->length) == 0 )) {
+
+ DEBUG(4,("ntlm_password_check: checking plaintext passwords for user %s\n",
+ username));
+ if (nt_pw && nt_response->length) {
+ unsigned char pwhash[16];
+ mdfour(pwhash, nt_response->data, nt_response->length);
+ if (memcmp(pwhash, nt_pw, sizeof(pwhash)) == 0) {
+ return NT_STATUS_OK;
+ } else {
+ DEBUG(3,("ntlm_password_check: NT (Unicode) plaintext password check failed for user %s\n",
+ username));
+ return NT_STATUS_WRONG_PASSWORD;
+ }
+
+ } else if (!lp_lanman_auth()) {
+ DEBUG(3,("ntlm_password_check: (plaintext password check) LANMAN passwords NOT PERMITTED for user %s\n",
+ username));
+
+ } else if (lm_pw && lm_response->length) {
+ uchar dospwd[14];
+ uchar p16[16];
+ ZERO_STRUCT(dospwd);
+
+ memcpy(dospwd, lm_response->data, MIN(lm_response->length, sizeof(dospwd)));
+ /* Only the fisrt 14 chars are considered, password need not be null terminated. */
+
+ /* we *might* need to upper-case the string here */
+ E_P16((const unsigned char *)dospwd, p16);
+
+ if (memcmp(p16, lm_pw, sizeof(p16)) == 0) {
+ return NT_STATUS_OK;
+ } else {
+ DEBUG(3,("ntlm_password_check: LANMAN (ASCII) plaintext password check failed for user %s\n",
+ username));
+ return NT_STATUS_WRONG_PASSWORD;
+ }
+ } else {
+ DEBUG(3, ("Plaintext authentication for user %s attempted, but neither NT nor LM passwords available\n", username));
+ return NT_STATUS_WRONG_PASSWORD;
+ }
+ }
+
+ 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 && nt_pw) {
+ if (nt_response->length > 24) {
+ /* 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 NTLMv2 password with domain [%s]\n", client_domain));
+ if (smb_pwd_check_ntlmv2( nt_response,
+ nt_pw, challenge,
+ client_username,
+ client_domain,
+ user_sess_key)) {
+ return NT_STATUS_OK;
+ }
+
+ DEBUG(4,("ntlm_password_check: Checking NTLMv2 password without a domain\n"));
+ if (smb_pwd_check_ntlmv2( nt_response,
+ nt_pw, challenge,
+ client_username,
+ "",
+ user_sess_key)) {
+ return NT_STATUS_OK;
+ } else {
+ DEBUG(3,("ntlm_password_check: NTLMv2 password check failed\n"));
+ return NT_STATUS_WRONG_PASSWORD;
+ }
+ }
+
+ if (lp_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(nt_response,
+ nt_pw, 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 (lp_lanman_auth() && lm_pw) {
+ uint8 first_8_lm_hash[16];
+ memcpy(first_8_lm_hash, lm_pw, 8);
+ memset(first_8_lm_hash + 8, '\0', 8);
+ *lm_sess_key = data_blob(first_8_lm_hash, 16);
+ }
+ 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 (!lp_lanman_auth()) {
+ DEBUG(3,("ntlm_password_check: Lanman passwords NOT PERMITTED for user %s\n",
+ username));
+ } else if (!lm_pw) {
+ DEBUG(3,("ntlm_password_check: NO LanMan password set for user %s (and no NT password supplied)\n",
+ username));
+ } else {
+ DEBUG(4,("ntlm_password_check: Checking LM password\n"));
+ if (smb_pwd_check_ntlmv1(lm_response,
+ lm_pw, challenge,
+ NULL)) {
+ uint8 first_8_lm_hash[16];
+ memcpy(first_8_lm_hash, lm_pw, 8);
+ memset(first_8_lm_hash + 8, '\0', 8);
+ *user_sess_key = data_blob(first_8_lm_hash, 16);
+ *lm_sess_key = data_blob(first_8_lm_hash, 16);
+ return NT_STATUS_OK;
+ }
+ }
+
+ if (!nt_pw) {
+ 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( lm_response,
+ nt_pw, challenge,
+ client_username,
+ client_domain,
+ NULL)) {
+ return NT_STATUS_OK;
+ }
+
+ DEBUG(4,("ntlm_password_check: Checking LMv2 password without a domain\n"));
+ if (smb_pwd_check_ntlmv2( lm_response,
+ nt_pw, challenge,
+ client_username,
+ "",
+ NULL)) {
+ 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 (lp_ntlm_auth()) {
+ if (smb_pwd_check_ntlmv1(lm_response,
+ nt_pw, 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 (lp_lanman_auth() && lm_pw) {
+ uint8 first_8_lm_hash[16];
+ memcpy(first_8_lm_hash, lm_pw, 8);
+ memset(first_8_lm_hash + 8, '\0', 8);
+ *user_sess_key = data_blob(first_8_lm_hash, 16);
+ *lm_sess_key = data_blob(first_8_lm_hash, 16);
+ }
+ 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));
+ }
+ return NT_STATUS_WRONG_PASSWORD;
+}
+
diff --git a/source/libsmb/ntlmssp.c b/source/libsmb/ntlmssp.c
new file mode 100644
index 00000000000..60523ddf9d0
--- /dev/null
+++ b/source/libsmb/ntlmssp.c
@@ -0,0 +1,1112 @@
+/*
+ Unix SMB/Netbios implementation.
+ Version 3.0
+ handle NLTMSSP, server side
+
+ Copyright (C) Andrew Tridgell 2001
+ Copyright (C) Andrew Bartlett 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 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"
+
+static NTSTATUS ntlmssp_client_initial(struct ntlmssp_state *ntlmssp_state,
+ DATA_BLOB reply, DATA_BLOB *next_request);
+static NTSTATUS ntlmssp_server_negotiate(struct ntlmssp_state *ntlmssp_state,
+ const DATA_BLOB in, DATA_BLOB *out);
+static NTSTATUS ntlmssp_client_challenge(struct ntlmssp_state *ntlmssp_state,
+ const DATA_BLOB reply, DATA_BLOB *next_request);
+static NTSTATUS ntlmssp_server_auth(struct ntlmssp_state *ntlmssp_state,
+ const DATA_BLOB request, DATA_BLOB *reply);
+
+/**
+ * Callbacks for NTLMSSP - for both client and server operating modes
+ *
+ */
+
+static const struct ntlmssp_callbacks {
+ enum NTLMSSP_ROLE role;
+ enum NTLM_MESSAGE_TYPE ntlmssp_command;
+ NTSTATUS (*fn)(struct ntlmssp_state *ntlmssp_state,
+ DATA_BLOB in, DATA_BLOB *out);
+} ntlmssp_callbacks[] = {
+ {NTLMSSP_CLIENT, NTLMSSP_INITIAL, ntlmssp_client_initial},
+ {NTLMSSP_SERVER, NTLMSSP_NEGOTIATE, ntlmssp_server_negotiate},
+ {NTLMSSP_CLIENT, NTLMSSP_CHALLENGE, ntlmssp_client_challenge},
+ {NTLMSSP_SERVER, NTLMSSP_AUTH, ntlmssp_server_auth},
+ {NTLMSSP_CLIENT, NTLMSSP_UNKNOWN, NULL},
+ {NTLMSSP_SERVER, NTLMSSP_UNKNOWN, NULL}
+};
+
+
+/**
+ * Print out the NTLMSSP flags for debugging
+ * @param neg_flags The flags from the packet
+ */
+
+void debug_ntlmssp_flags(uint32 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_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_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"));
+}
+
+/**
+ * Default challenge generation code.
+ *
+ */
+
+static const uint8 *get_challenge(const struct ntlmssp_state *ntlmssp_state)
+{
+ static uchar chal[8];
+ generate_random_buffer(chal, sizeof(chal), False);
+
+ return chal;
+}
+
+/**
+ * Default 'we can set the challenge to anything we like' implementation
+ *
+ */
+
+static BOOL may_set_challenge(const struct ntlmssp_state *ntlmssp_state)
+{
+ return True;
+}
+
+/**
+ * Default 'we can set the challenge to anything we like' implementation
+ *
+ * Does not actually do anything, as the value is always in the structure anyway.
+ *
+ */
+
+static NTSTATUS set_challenge(struct ntlmssp_state *ntlmssp_state, DATA_BLOB *challenge)
+{
+ SMB_ASSERT(challenge->length == 8);
+ return NT_STATUS_OK;
+}
+
+/**
+ * Set a username on an NTLMSSP context - ensures it is talloc()ed
+ *
+ */
+
+NTSTATUS ntlmssp_set_username(NTLMSSP_STATE *ntlmssp_state, const char *user)
+{
+ ntlmssp_state->user = talloc_strdup(ntlmssp_state->mem_ctx, user);
+ if (!ntlmssp_state->user) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ return NT_STATUS_OK;
+}
+
+/**
+ * Set a password on an NTLMSSP context - ensures it is talloc()ed
+ *
+ */
+NTSTATUS ntlmssp_set_password(NTLMSSP_STATE *ntlmssp_state, const char *password)
+{
+ if (!password) {
+ ntlmssp_state->password = NULL;
+ } else {
+ ntlmssp_state->password = talloc_strdup(ntlmssp_state->mem_ctx, password);
+ if (!ntlmssp_state->password) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ }
+ return NT_STATUS_OK;
+}
+
+/**
+ * Set a domain on an NTLMSSP context - ensures it is talloc()ed
+ *
+ */
+NTSTATUS ntlmssp_set_domain(NTLMSSP_STATE *ntlmssp_state, const char *domain)
+{
+ ntlmssp_state->domain = talloc_strdup(ntlmssp_state->mem_ctx, domain);
+ if (!ntlmssp_state->domain) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ return NT_STATUS_OK;
+}
+
+/**
+ * Set a workstation on an NTLMSSP context - ensures it is talloc()ed
+ *
+ */
+NTSTATUS ntlmssp_set_workstation(NTLMSSP_STATE *ntlmssp_state, const char *workstation)
+{
+ ntlmssp_state->workstation = talloc_strdup(ntlmssp_state->mem_ctx, workstation);
+ if (!ntlmssp_state->domain) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ return NT_STATUS_OK;
+}
+
+/**
+ * Store a DATA_BLOB containing an NTLMSSP response, for use later.
+ * This copies the data blob
+ */
+
+NTSTATUS ntlmssp_store_response(NTLMSSP_STATE *ntlmssp_state,
+ DATA_BLOB response)
+{
+ ntlmssp_state->stored_response = data_blob_talloc(ntlmssp_state->mem_ctx,
+ response.data, response.length);
+ return NT_STATUS_OK;
+}
+
+/**
+ * Next state function for the NTLMSSP state machine
+ *
+ * @param ntlmssp_state NTLMSSP State
+ * @param in The packet in from the NTLMSSP partner, as a DATA_BLOB
+ * @param out The reply, as an allocated DATA_BLOB, caller to free.
+ * @return Errors, NT_STATUS_MORE_PROCESSING_REQUIRED or NT_STATUS_OK.
+ */
+
+NTSTATUS ntlmssp_update(NTLMSSP_STATE *ntlmssp_state,
+ const DATA_BLOB in, DATA_BLOB *out)
+{
+ DATA_BLOB input;
+ uint32 ntlmssp_command;
+ int i;
+
+ *out = data_blob(NULL, 0);
+
+ if (!in.length && ntlmssp_state->stored_response.length) {
+ input = ntlmssp_state->stored_response;
+
+ /* we only want to read the stored response once - overwrite it */
+ ntlmssp_state->stored_response = data_blob(NULL, 0);
+ } else {
+ input = in;
+ }
+
+ if (!input.length) {
+ switch (ntlmssp_state->role) {
+ case NTLMSSP_CLIENT:
+ ntlmssp_command = NTLMSSP_INITIAL;
+ break;
+ case NTLMSSP_SERVER:
+ /* 'datagram' mode - no neg packet */
+ ntlmssp_command = NTLMSSP_NEGOTIATE;
+ break;
+ }
+ } else {
+ if (!msrpc_parse(&input, "Cd",
+ "NTLMSSP",
+ &ntlmssp_command)) {
+ DEBUG(1, ("Failed to parse NTLMSSP packet, could not extract NTLMSSP command\n"));
+ dump_data(2, (const char *)input.data, input.length);
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+ }
+
+ if (ntlmssp_command != ntlmssp_state->expected_state) {
+ DEBUG(1, ("got NTLMSSP command %u, expected %u\n", ntlmssp_command, ntlmssp_state->expected_state));
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ for (i=0; ntlmssp_callbacks[i].fn; i++) {
+ if (ntlmssp_callbacks[i].role == ntlmssp_state->role
+ && ntlmssp_callbacks[i].ntlmssp_command == ntlmssp_command) {
+ return ntlmssp_callbacks[i].fn(ntlmssp_state, input, out);
+ }
+ }
+
+ DEBUG(1, ("failed to find NTLMSSP callback for NTLMSSP mode %u, command %u\n",
+ ntlmssp_state->role, ntlmssp_command));
+
+ return NT_STATUS_INVALID_PARAMETER;
+}
+
+/**
+ * End an NTLMSSP state machine
+ *
+ * @param ntlmssp_state NTLMSSP State, free()ed by this function
+ */
+
+void ntlmssp_end(NTLMSSP_STATE **ntlmssp_state)
+{
+ TALLOC_CTX *mem_ctx = (*ntlmssp_state)->mem_ctx;
+
+ (*ntlmssp_state)->ref_count--;
+
+ if ((*ntlmssp_state)->ref_count == 0) {
+ data_blob_free(&(*ntlmssp_state)->chal);
+ data_blob_free(&(*ntlmssp_state)->lm_resp);
+ data_blob_free(&(*ntlmssp_state)->nt_resp);
+
+ talloc_destroy(mem_ctx);
+ }
+
+ *ntlmssp_state = NULL;
+ return;
+}
+
+/**
+ * Determine correct target name flags for reply, given server role
+ * and negotiated flags
+ *
+ * @param 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 ntlmssp_state *ntlmssp_state,
+ uint32 neg_flags, uint32 *chal_flags)
+{
+ if (neg_flags & NTLMSSP_REQUEST_TARGET) {
+ *chal_flags |= NTLMSSP_CHAL_TARGET_INFO;
+ *chal_flags |= NTLMSSP_REQUEST_TARGET;
+ if (ntlmssp_state->server_role == ROLE_STANDALONE) {
+ *chal_flags |= NTLMSSP_TARGET_TYPE_SERVER;
+ return ntlmssp_state->get_global_myname();
+ } else {
+ *chal_flags |= NTLMSSP_TARGET_TYPE_DOMAIN;
+ return ntlmssp_state->get_domain();
+ };
+ } else {
+ return "";
+ }
+}
+
+static void ntlmssp_handle_neg_flags(struct ntlmssp_state *ntlmssp_state,
+ uint32 neg_flags, BOOL allow_lm) {
+ if (neg_flags & NTLMSSP_NEGOTIATE_UNICODE) {
+ ntlmssp_state->neg_flags |= NTLMSSP_NEGOTIATE_UNICODE;
+ ntlmssp_state->neg_flags &= ~NTLMSSP_NEGOTIATE_OEM;
+ ntlmssp_state->unicode = True;
+ } else {
+ ntlmssp_state->neg_flags &= ~NTLMSSP_NEGOTIATE_UNICODE;
+ ntlmssp_state->neg_flags |= NTLMSSP_NEGOTIATE_OEM;
+ ntlmssp_state->unicode = False;
+ }
+
+ if ((neg_flags & NTLMSSP_NEGOTIATE_LM_KEY) && allow_lm) {
+ /* other end forcing us to use LM */
+ ntlmssp_state->neg_flags |= NTLMSSP_NEGOTIATE_LM_KEY;
+ ntlmssp_state->use_ntlmv2 = False;
+ } else {
+ ntlmssp_state->neg_flags &= ~NTLMSSP_NEGOTIATE_LM_KEY;
+ }
+
+ if (neg_flags & NTLMSSP_NEGOTIATE_ALWAYS_SIGN) {
+ ntlmssp_state->neg_flags |= NTLMSSP_NEGOTIATE_ALWAYS_SIGN;
+ }
+
+ if (!(neg_flags & NTLMSSP_NEGOTIATE_NTLM2)) {
+ ntlmssp_state->neg_flags &= ~NTLMSSP_NEGOTIATE_NTLM2;
+ }
+
+ if (!(neg_flags & NTLMSSP_NEGOTIATE_128)) {
+ ntlmssp_state->neg_flags &= ~NTLMSSP_NEGOTIATE_128;
+ }
+
+ if (!(neg_flags & NTLMSSP_NEGOTIATE_KEY_EXCH)) {
+ ntlmssp_state->neg_flags &= ~NTLMSSP_NEGOTIATE_KEY_EXCH;
+ }
+
+ if ((neg_flags & NTLMSSP_REQUEST_TARGET)) {
+ ntlmssp_state->neg_flags |= NTLMSSP_REQUEST_TARGET;
+ }
+
+}
+
+
+/**
+ * Next state function for the Negotiate packet
+ *
+ * @param ntlmssp_state NTLMSSP State
+ * @param request The request, as a DATA_BLOB
+ * @param request The reply, as an allocated DATA_BLOB, caller to free.
+ * @return Errors or MORE_PROCESSING_REQUIRED if a reply is sent.
+ */
+
+static NTSTATUS ntlmssp_server_negotiate(struct ntlmssp_state *ntlmssp_state,
+ const DATA_BLOB request, DATA_BLOB *reply)
+{
+ DATA_BLOB struct_blob;
+ fstring dnsname, dnsdomname;
+ uint32 neg_flags = 0;
+ uint32 ntlmssp_command, chal_flags;
+ char *cliname=NULL, *domname=NULL;
+ const uint8 *cryptkey;
+ const char *target_name;
+
+ /* parse the NTLMSSP packet */
+#if 0
+ file_save("ntlmssp_negotiate.dat", request.data, request.length);
+#endif
+
+ if (request.length) {
+ if (!msrpc_parse(&request, "CddAA",
+ "NTLMSSP",
+ &ntlmssp_command,
+ &neg_flags,
+ &cliname,
+ &domname)) {
+ DEBUG(1, ("ntlmssp_server_negotiate: failed to parse NTLMSSP:\n"));
+ dump_data(2, (const char *)request.data, request.length);
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ SAFE_FREE(cliname);
+ SAFE_FREE(domname);
+
+ debug_ntlmssp_flags(neg_flags);
+ }
+
+ ntlmssp_handle_neg_flags(ntlmssp_state, neg_flags, lp_lanman_auth());
+
+ /* Ask our caller what challenge they would like in the packet */
+ cryptkey = ntlmssp_state->get_challenge(ntlmssp_state);
+
+ /* Check if we may set the challenge */
+ if (!ntlmssp_state->may_set_challenge(ntlmssp_state)) {
+ 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 = ntlmssp_state->neg_flags;
+
+ /* get the right name to fill in as 'target' */
+ target_name = ntlmssp_target_name(ntlmssp_state,
+ neg_flags, &chal_flags);
+ if (target_name == NULL)
+ return NT_STATUS_INVALID_PARAMETER;
+
+ ntlmssp_state->chal = data_blob_talloc(ntlmssp_state->mem_ctx, cryptkey, 8);
+ ntlmssp_state->internal_chal = data_blob_talloc(ntlmssp_state->mem_ctx, cryptkey, 8);
+
+
+ /* This should be a 'netbios domain -> DNS domain' mapping */
+ dnsdomname[0] = '\0';
+ get_mydnsdomname(dnsdomname);
+ strlower_m(dnsdomname);
+
+ dnsname[0] = '\0';
+ get_mydnsfullname(dnsname);
+
+ /* This creates the 'blob' of names that appears at the end of the packet */
+ if (chal_flags & NTLMSSP_CHAL_TARGET_INFO)
+ {
+ const char *target_name_dns = "";
+ 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(&struct_blob, "aaaaa",
+ NTLMSSP_NAME_TYPE_DOMAIN, target_name,
+ NTLMSSP_NAME_TYPE_SERVER, ntlmssp_state->get_global_myname(),
+ NTLMSSP_NAME_TYPE_DOMAIN_DNS, dnsdomname,
+ NTLMSSP_NAME_TYPE_SERVER_DNS, dnsname,
+ 0, "");
+ } else {
+ struct_blob = data_blob(NULL, 0);
+ }
+
+ {
+ /* Marshel the packet in the right format, be it unicode or ASCII */
+ const char *gen_string;
+ if (ntlmssp_state->unicode) {
+ gen_string = "CdUdbddB";
+ } else {
+ gen_string = "CdAdbddB";
+ }
+
+ msrpc_gen(reply, gen_string,
+ "NTLMSSP",
+ NTLMSSP_CHALLENGE,
+ target_name,
+ chal_flags,
+ cryptkey, 8,
+ 0, 0,
+ struct_blob.data, struct_blob.length);
+ }
+
+ data_blob_free(&struct_blob);
+
+ ntlmssp_state->expected_state = NTLMSSP_AUTH;
+
+ return NT_STATUS_MORE_PROCESSING_REQUIRED;
+}
+
+/**
+ * Next state function for the Authenticate packet
+ *
+ * @param ntlmssp_state NTLMSSP State
+ * @param request The request, as a DATA_BLOB
+ * @param request The reply, as an allocated DATA_BLOB, caller to free.
+ * @return Errors or NT_STATUS_OK.
+ */
+
+static NTSTATUS ntlmssp_server_auth(struct ntlmssp_state *ntlmssp_state,
+ const DATA_BLOB request, DATA_BLOB *reply)
+{
+ DATA_BLOB encrypted_session_key = data_blob(NULL, 0);
+ DATA_BLOB nt_session_key = data_blob(NULL, 0);
+ DATA_BLOB lm_session_key = data_blob(NULL, 0);
+ DATA_BLOB session_key = data_blob(NULL, 0);
+ uint32 ntlmssp_command, auth_flags;
+ NTSTATUS nt_status;
+
+ /* used by NTLM2 */
+ BOOL doing_ntlm2 = False;
+
+ uchar session_nonce[16];
+ uchar session_nonce_hash[16];
+
+ const char *parse_string;
+ char *domain = NULL;
+ char *user = NULL;
+ char *workstation = NULL;
+
+ /* parse the NTLMSSP packet */
+ *reply = data_blob(NULL, 0);
+
+#if 0
+ file_save("ntlmssp_auth.dat", request.data, request.length);
+#endif
+
+ if (ntlmssp_state->unicode) {
+ parse_string = "CdBBUUUBd";
+ } else {
+ parse_string = "CdBBAAABd";
+ }
+
+ data_blob_free(&ntlmssp_state->lm_resp);
+ data_blob_free(&ntlmssp_state->nt_resp);
+
+ ntlmssp_state->user = NULL;
+ ntlmssp_state->domain = NULL;
+ ntlmssp_state->workstation = NULL;
+
+ /* now the NTLMSSP encoded auth hashes */
+ if (!msrpc_parse(&request, parse_string,
+ "NTLMSSP",
+ &ntlmssp_command,
+ &ntlmssp_state->lm_resp,
+ &ntlmssp_state->nt_resp,
+ &domain,
+ &user,
+ &workstation,
+ &encrypted_session_key,
+ &auth_flags)) {
+ DEBUG(1, ("ntlmssp_server_auth: failed to parse NTLMSSP:\n"));
+ dump_data(2, (const char *)request.data, request.length);
+ SAFE_FREE(domain);
+ SAFE_FREE(user);
+ SAFE_FREE(workstation);
+ data_blob_free(&encrypted_session_key);
+ auth_flags = 0;
+
+ /* Try again with a shorter string (Win9X truncates this packet) */
+ if (ntlmssp_state->unicode) {
+ parse_string = "CdBBUUU";
+ } else {
+ parse_string = "CdBBAAA";
+ }
+
+ /* now the NTLMSSP encoded auth hashes */
+ if (!msrpc_parse(&request, parse_string,
+ "NTLMSSP",
+ &ntlmssp_command,
+ &ntlmssp_state->lm_resp,
+ &ntlmssp_state->nt_resp,
+ &domain,
+ &user,
+ &workstation)) {
+ DEBUG(1, ("ntlmssp_server_auth: failed to parse NTLMSSP:\n"));
+ dump_data(2, (const char *)request.data, request.length);
+ SAFE_FREE(domain);
+ SAFE_FREE(user);
+ SAFE_FREE(workstation);
+
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+ }
+
+ if (auth_flags)
+ ntlmssp_handle_neg_flags(ntlmssp_state, auth_flags, lp_lanman_auth());
+
+ if (!NT_STATUS_IS_OK(nt_status = ntlmssp_set_domain(ntlmssp_state, domain))) {
+ SAFE_FREE(domain);
+ SAFE_FREE(user);
+ SAFE_FREE(workstation);
+ data_blob_free(&encrypted_session_key);
+ return nt_status;
+ }
+
+ if (!NT_STATUS_IS_OK(nt_status = ntlmssp_set_username(ntlmssp_state, user))) {
+ SAFE_FREE(domain);
+ SAFE_FREE(user);
+ SAFE_FREE(workstation);
+ data_blob_free(&encrypted_session_key);
+ return nt_status;
+ }
+
+ if (!NT_STATUS_IS_OK(nt_status = ntlmssp_set_workstation(ntlmssp_state, workstation))) {
+ SAFE_FREE(domain);
+ SAFE_FREE(user);
+ SAFE_FREE(workstation);
+ data_blob_free(&encrypted_session_key);
+ return nt_status;
+ }
+
+ SAFE_FREE(domain);
+ SAFE_FREE(user);
+ SAFE_FREE(workstation);
+
+ DEBUG(3,("Got user=[%s] domain=[%s] workstation=[%s] len1=%lu len2=%lu\n",
+ ntlmssp_state->user, ntlmssp_state->domain, ntlmssp_state->workstation, (unsigned long)ntlmssp_state->lm_resp.length, (unsigned long)ntlmssp_state->nt_resp.length));
+
+#if 0
+ file_save("nthash1.dat", &ntlmssp_state->nt_resp.data, &ntlmssp_state->nt_resp.length);
+ file_save("lmhash1.dat", &ntlmssp_state->lm_resp.data, &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 (ntlmssp_state->neg_flags & NTLMSSP_NEGOTIATE_NTLM2) {
+ if (ntlmssp_state->nt_resp.length == 24 && ntlmssp_state->lm_resp.length == 24) {
+ struct MD5Context md5_session_nonce_ctx;
+ SMB_ASSERT(ntlmssp_state->internal_chal.data && ntlmssp_state->internal_chal.length == 8);
+
+ doing_ntlm2 = True;
+
+ memcpy(session_nonce, ntlmssp_state->internal_chal.data, 8);
+ memcpy(&session_nonce[8], ntlmssp_state->lm_resp.data, 8);
+
+ MD5Init(&md5_session_nonce_ctx);
+ MD5Update(&md5_session_nonce_ctx, session_nonce, 16);
+ MD5Final(session_nonce_hash, &md5_session_nonce_ctx);
+
+ ntlmssp_state->chal = data_blob_talloc(ntlmssp_state->mem_ctx, session_nonce_hash, 8);
+
+ /* LM response is no longer useful */
+ data_blob_free(&ntlmssp_state->lm_resp);
+
+ /* We changed the effective challenge - set it */
+ if (!NT_STATUS_IS_OK(nt_status = ntlmssp_state->set_challenge(ntlmssp_state, &ntlmssp_state->chal))) {
+ data_blob_free(&encrypted_session_key);
+ 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 = ntlmssp_state->check_password(ntlmssp_state,
+ &nt_session_key, &lm_session_key))) {
+ data_blob_free(&encrypted_session_key);
+ return nt_status;
+ }
+
+ dump_data_pw("NT session key:\n", nt_session_key.data, nt_session_key.length);
+ dump_data_pw("LM first-8:\n", lm_session_key.data, lm_session_key.length);
+
+ /* Handle the different session key derivation for NTLM2 */
+ if (doing_ntlm2) {
+ if (nt_session_key.data && nt_session_key.length == 16) {
+ session_key = data_blob_talloc(ntlmssp_state->mem_ctx, NULL, 16);
+ hmac_md5(nt_session_key.data, session_nonce,
+ sizeof(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 (ntlmssp_state->neg_flags & NTLMSSP_NEGOTIATE_LM_KEY) {
+ if (lm_session_key.data && lm_session_key.length >= 8) {
+ if (ntlmssp_state->lm_resp.data && ntlmssp_state->lm_resp.length == 24) {
+ session_key = data_blob_talloc(ntlmssp_state->mem_ctx, NULL, 16);
+ SMBsesskeygen_lm_sess_key(lm_session_key.data, 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 {
+ /* use the key unmodified - it's
+ * probably a NULL key from the guest
+ * login */
+ session_key = lm_session_key;
+ }
+ } else {
+ DEBUG(10,("ntlmssp_server_auth: Failed to create NTLM session key.\n"));
+ session_key = data_blob(NULL, 0);
+ }
+ } else if (nt_session_key.data) {
+ session_key = nt_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);
+ } else if (lm_session_key.data) {
+ 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);
+ } else {
+ DEBUG(10,("ntlmssp_server_auth: Failed to create unmodified session key.\n"));
+ session_key = data_blob(NULL, 0);
+ }
+
+ /* With KEY_EXCH, the client supplies the proposed session key,
+ but encrypts it with the long-term key */
+ if (ntlmssp_state->neg_flags & NTLMSSP_NEGOTIATE_KEY_EXCH) {
+ if (!encrypted_session_key.data || encrypted_session_key.length != 16) {
+ data_blob_free(&encrypted_session_key);
+ DEBUG(1, ("Client-supplied KEY_EXCH session key was of invalid length (%u)!\n",
+ 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",
+ session_key.length));
+ ntlmssp_state->session_key = session_key;
+ } else {
+ dump_data_pw("KEY_EXCH session key (enc):\n", encrypted_session_key.data, encrypted_session_key.length);
+ SamOEMhash(encrypted_session_key.data,
+ session_key.data,
+ encrypted_session_key.length);
+ ntlmssp_state->session_key = data_blob_talloc(ntlmssp_state->mem_ctx,
+ encrypted_session_key.data,
+ encrypted_session_key.length);
+ dump_data_pw("KEY_EXCH session key:\n", encrypted_session_key.data,
+ encrypted_session_key.length);
+ }
+ } else {
+ ntlmssp_state->session_key = session_key;
+ }
+
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ ntlmssp_state->session_key = data_blob(NULL, 0);
+ } else if (ntlmssp_state->session_key.length) {
+ nt_status = ntlmssp_sign_init(ntlmssp_state);
+ }
+
+ data_blob_free(&encrypted_session_key);
+
+ /* allow arbitarily many authentications */
+ ntlmssp_state->expected_state = NTLMSSP_AUTH;
+
+ return nt_status;
+}
+
+/**
+ * Create an NTLMSSP state machine
+ *
+ * @param ntlmssp_state NTLMSSP State, allocated by this function
+ */
+
+NTSTATUS ntlmssp_server_start(NTLMSSP_STATE **ntlmssp_state)
+{
+ TALLOC_CTX *mem_ctx;
+
+ mem_ctx = talloc_init("NTLMSSP context");
+
+ *ntlmssp_state = talloc_zero(mem_ctx, sizeof(**ntlmssp_state));
+ if (!*ntlmssp_state) {
+ DEBUG(0,("ntlmssp_server_start: talloc failed!\n"));
+ talloc_destroy(mem_ctx);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ (*ntlmssp_state)->role = NTLMSSP_SERVER;
+
+ (*ntlmssp_state)->mem_ctx = mem_ctx;
+ (*ntlmssp_state)->get_challenge = get_challenge;
+ (*ntlmssp_state)->set_challenge = set_challenge;
+ (*ntlmssp_state)->may_set_challenge = may_set_challenge;
+
+ (*ntlmssp_state)->get_global_myname = global_myname;
+ (*ntlmssp_state)->get_domain = lp_workgroup;
+ (*ntlmssp_state)->server_role = ROLE_DOMAIN_MEMBER; /* a good default */
+
+ (*ntlmssp_state)->expected_state = NTLMSSP_NEGOTIATE;
+
+ (*ntlmssp_state)->ref_count = 1;
+
+ (*ntlmssp_state)->neg_flags =
+ NTLMSSP_NEGOTIATE_128 |
+ NTLMSSP_NEGOTIATE_NTLM |
+ NTLMSSP_NEGOTIATE_NTLM2 |
+ NTLMSSP_NEGOTIATE_KEY_EXCH |
+ NTLMSSP_NEGOTIATE_SIGN;
+
+ return NT_STATUS_OK;
+}
+
+/*********************************************************************
+ Client side NTLMSSP
+*********************************************************************/
+
+/**
+ * Next state function for the Initial packet
+ *
+ * @param ntlmssp_state NTLMSSP State
+ * @param request The request, as a DATA_BLOB. reply.data must be NULL
+ * @param request The reply, as an allocated DATA_BLOB, caller to free.
+ * @return Errors or NT_STATUS_OK.
+ */
+
+static NTSTATUS ntlmssp_client_initial(struct ntlmssp_state *ntlmssp_state,
+ DATA_BLOB reply, DATA_BLOB *next_request)
+{
+ if (ntlmssp_state->unicode) {
+ ntlmssp_state->neg_flags |= NTLMSSP_NEGOTIATE_UNICODE;
+ } else {
+ ntlmssp_state->neg_flags |= NTLMSSP_NEGOTIATE_OEM;
+ }
+
+ if (ntlmssp_state->use_ntlmv2) {
+ ntlmssp_state->neg_flags |= NTLMSSP_NEGOTIATE_NTLM2;
+ }
+
+ /* generate the ntlmssp negotiate packet */
+ msrpc_gen(next_request, "CddAA",
+ "NTLMSSP",
+ NTLMSSP_NEGOTIATE,
+ ntlmssp_state->neg_flags,
+ ntlmssp_state->get_domain(),
+ ntlmssp_state->get_global_myname());
+
+ ntlmssp_state->expected_state = NTLMSSP_CHALLENGE;
+
+ return NT_STATUS_MORE_PROCESSING_REQUIRED;
+}
+
+/**
+ * Next state function for the Challenge Packet. Generate an auth packet.
+ *
+ * @param ntlmssp_state NTLMSSP State
+ * @param request The request, as a DATA_BLOB. reply.data must be NULL
+ * @param request The reply, as an allocated DATA_BLOB, caller to free.
+ * @return Errors or NT_STATUS_OK.
+ */
+
+static NTSTATUS ntlmssp_client_challenge(struct ntlmssp_state *ntlmssp_state,
+ const DATA_BLOB reply, DATA_BLOB *next_request)
+{
+ uint32 chal_flags, ntlmssp_command, unkn1, unkn2;
+ DATA_BLOB server_domain_blob;
+ DATA_BLOB challenge_blob;
+ DATA_BLOB struct_blob = 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 encrypted_session_key = data_blob(NULL, 0);
+ NTSTATUS nt_status;
+
+ if (!msrpc_parse(&reply, "CdBd",
+ "NTLMSSP",
+ &ntlmssp_command,
+ &server_domain_blob,
+ &chal_flags)) {
+ DEBUG(1, ("Failed to parse the NTLMSSP Challenge: (#1)\n"));
+ dump_data(2, (const char *)reply.data, reply.length);
+
+ 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(ntlmssp_state, chal_flags, lp_client_lanman_auth());
+
+ if (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";
+ }
+
+ DEBUG(3, ("NTLMSSP: Set final flags:\n"));
+ debug_ntlmssp_flags(ntlmssp_state->neg_flags);
+
+ if (!msrpc_parse(&reply, chal_parse_string,
+ "NTLMSSP",
+ &ntlmssp_command,
+ &server_domain,
+ &chal_flags,
+ &challenge_blob, 8,
+ &unkn1, &unkn2,
+ &struct_blob)) {
+ DEBUG(1, ("Failed to parse the NTLMSSP Challenge: (#2)\n"));
+ dump_data(2, (const char *)reply.data, reply.length);
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ ntlmssp_state->server_domain = talloc_strdup(ntlmssp_state->mem_ctx,
+ server_domain);
+
+ SAFE_FREE(server_domain);
+ if (challenge_blob.length != 8) {
+ data_blob_free(&struct_blob);
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ if (!ntlmssp_state->password) {
+ static const uchar zeros[16];
+ /* do nothing - blobs are zero length */
+
+ /* session key is all zeros */
+ session_key = data_blob_talloc(ntlmssp_state->mem_ctx, zeros, 16);
+
+ /* not doing NLTM2 without a password */
+ ntlmssp_state->neg_flags &= ~NTLMSSP_NEGOTIATE_NTLM2;
+ } else if (ntlmssp_state->use_ntlmv2) {
+
+ if (!struct_blob.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(ntlmssp_state->user,
+ ntlmssp_state->domain,
+ ntlmssp_state->password, &challenge_blob,
+ &struct_blob,
+ &lm_response, &nt_response, &session_key)) {
+ data_blob_free(&challenge_blob);
+ data_blob_free(&struct_blob);
+ return NT_STATUS_NO_MEMORY;
+ }
+ } else if (ntlmssp_state->neg_flags & NTLMSSP_NEGOTIATE_NTLM2) {
+ struct MD5Context md5_session_nonce_ctx;
+ uchar nt_hash[16];
+ uchar session_nonce[16];
+ uchar session_nonce_hash[16];
+ uchar nt_session_key[16];
+ E_md4hash(ntlmssp_state->password, nt_hash);
+
+ lm_response = data_blob_talloc(ntlmssp_state->mem_ctx, NULL, 24);
+ generate_random_buffer(lm_response.data, 8, False);
+ memset(lm_response.data+8, 0, 16);
+
+ memcpy(session_nonce, challenge_blob.data, 8);
+ memcpy(&session_nonce[8], lm_response.data, 8);
+
+ MD5Init(&md5_session_nonce_ctx);
+ MD5Update(&md5_session_nonce_ctx, challenge_blob.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, (const char *)session_nonce_hash, 8);
+
+ nt_response = data_blob_talloc(ntlmssp_state->mem_ctx, NULL, 24);
+ SMBNTencrypt(ntlmssp_state->password,
+ session_nonce_hash,
+ nt_response.data);
+
+ session_key = data_blob_talloc(ntlmssp_state->mem_ctx, NULL, 16);
+
+ SMBsesskeygen_ntv1(nt_hash, NULL, nt_session_key);
+ hmac_md5(nt_session_key, session_nonce, sizeof(session_nonce), session_key.data);
+ dump_data_pw("NTLM2 session key:\n", session_key.data, session_key.length);
+ } else {
+
+
+ uchar lm_hash[16];
+ uchar nt_hash[16];
+ E_deshash(ntlmssp_state->password, lm_hash);
+ E_md4hash(ntlmssp_state->password, nt_hash);
+
+ /* lanman auth is insecure, it may be disabled */
+ if (lp_client_lanman_auth()) {
+ lm_response = data_blob_talloc(ntlmssp_state->mem_ctx, NULL, 24);
+ SMBencrypt(ntlmssp_state->password,challenge_blob.data,
+ lm_response.data);
+ }
+
+ nt_response = data_blob_talloc(ntlmssp_state->mem_ctx, NULL, 24);
+ SMBNTencrypt(ntlmssp_state->password,challenge_blob.data,
+ nt_response.data);
+
+ session_key = data_blob_talloc(ntlmssp_state->mem_ctx, NULL, 16);
+ if ((ntlmssp_state->neg_flags & NTLMSSP_NEGOTIATE_LM_KEY)
+ && lp_client_lanman_auth()) {
+ SMBsesskeygen_lmv1(lm_hash, lm_response.data,
+ session_key.data);
+ dump_data_pw("LM session key\n", session_key.data, session_key.length);
+ } else {
+ SMBsesskeygen_ntv1(nt_hash, NULL, session_key.data);
+ dump_data_pw("NT session key:\n", session_key.data, session_key.length);
+ }
+ }
+ data_blob_free(&struct_blob);
+
+ /* Key exchange encryptes a new client-generated session key with
+ the password-derived key */
+ if (ntlmssp_state->neg_flags & NTLMSSP_NEGOTIATE_KEY_EXCH) {
+ uint8 client_session_key[16];
+
+ generate_random_buffer(client_session_key, sizeof(client_session_key), False);
+ encrypted_session_key = data_blob(client_session_key, sizeof(client_session_key));
+ dump_data_pw("KEY_EXCH session key:\n", encrypted_session_key.data, encrypted_session_key.length);
+
+ SamOEMhash(encrypted_session_key.data, session_key.data, encrypted_session_key.length);
+ data_blob_free(&session_key);
+ session_key = data_blob_talloc(ntlmssp_state->mem_ctx, client_session_key, sizeof(client_session_key));
+ dump_data_pw("KEY_EXCH session key (enc):\n", encrypted_session_key.data, encrypted_session_key.length);
+ }
+
+ /* this generates the actual auth packet */
+ if (!msrpc_gen(next_request, auth_gen_string,
+ "NTLMSSP",
+ NTLMSSP_AUTH,
+ lm_response.data, lm_response.length,
+ nt_response.data, nt_response.length,
+ ntlmssp_state->domain,
+ ntlmssp_state->user,
+ ntlmssp_state->get_global_myname(),
+ encrypted_session_key.data, encrypted_session_key.length,
+ ntlmssp_state->neg_flags)) {
+
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ data_blob_free(&encrypted_session_key);
+
+ data_blob_free(&ntlmssp_state->chal);
+
+ ntlmssp_state->chal = challenge_blob;
+ ntlmssp_state->lm_resp = lm_response;
+ ntlmssp_state->nt_resp = nt_response;
+ ntlmssp_state->session_key = session_key;
+
+ ntlmssp_state->expected_state = NTLMSSP_UNKNOWN;
+
+ if (!NT_STATUS_IS_OK(nt_status = ntlmssp_sign_init(ntlmssp_state))) {
+ DEBUG(1, ("Could not setup NTLMSSP signing/sealing system (error was: %s)\n", nt_errstr(nt_status)));
+ return nt_status;
+ }
+
+ return NT_STATUS_MORE_PROCESSING_REQUIRED;
+}
+
+NTSTATUS ntlmssp_client_start(NTLMSSP_STATE **ntlmssp_state)
+{
+ TALLOC_CTX *mem_ctx;
+
+ mem_ctx = talloc_init("NTLMSSP Client context");
+
+ *ntlmssp_state = talloc_zero(mem_ctx, sizeof(**ntlmssp_state));
+ if (!*ntlmssp_state) {
+ DEBUG(0,("ntlmssp_client_start: talloc failed!\n"));
+ talloc_destroy(mem_ctx);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ (*ntlmssp_state)->role = NTLMSSP_CLIENT;
+
+ (*ntlmssp_state)->mem_ctx = mem_ctx;
+
+ (*ntlmssp_state)->get_global_myname = global_myname;
+ (*ntlmssp_state)->get_domain = lp_workgroup;
+
+ (*ntlmssp_state)->unicode = True;
+
+ (*ntlmssp_state)->use_ntlmv2 = lp_client_ntlmv2_auth();
+
+ (*ntlmssp_state)->expected_state = NTLMSSP_INITIAL;
+
+ (*ntlmssp_state)->ref_count = 1;
+
+ (*ntlmssp_state)->neg_flags =
+ NTLMSSP_NEGOTIATE_128 |
+ NTLMSSP_NEGOTIATE_NTLM |
+ NTLMSSP_NEGOTIATE_NTLM2 |
+ NTLMSSP_NEGOTIATE_KEY_EXCH |
+ /*
+ * 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.
+ * */
+ NTLMSSP_NEGOTIATE_SIGN |
+ NTLMSSP_REQUEST_TARGET;
+
+ return NT_STATUS_OK;
+}
+
diff --git a/source/libsmb/ntlmssp_parse.c b/source/libsmb/ntlmssp_parse.c
new file mode 100644
index 00000000000..4b3043aec80
--- /dev/null
+++ b/source/libsmb/ntlmssp_parse.c
@@ -0,0 +1,315 @@
+/*
+ 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 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"
+
+/*
+ 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(DATA_BLOB *blob,
+ const char *format, ...)
+{
+ int i, n;
+ va_list ap;
+ char *s;
+ uint8 *b;
+ int head_size=0, data_size=0;
+ int head_ofs, data_ofs;
+
+ /* 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;
+ data_size += str_charnum(s) * 2;
+ break;
+ case 'A':
+ s = va_arg(ap, char *);
+ head_size += 8;
+ data_size += str_ascii_charnum(s);
+ break;
+ case 'a':
+ n = va_arg(ap, int);
+ s = va_arg(ap, char *);
+ data_size += (str_charnum(s) * 2) + 4;
+ break;
+ case 'B':
+ b = va_arg(ap, uint8 *);
+ head_size += 8;
+ data_size += va_arg(ap, int);
+ break;
+ case 'b':
+ b = va_arg(ap, uint8 *);
+ head_size += va_arg(ap, int);
+ break;
+ case 'd':
+ n = va_arg(ap, int);
+ head_size += 4;
+ break;
+ case 'C':
+ s = va_arg(ap, char *);
+ head_size += str_charnum(s) + 1;
+ break;
+ }
+ }
+ va_end(ap);
+
+ /* allocate the space, then scan the format again to fill in the values */
+ *blob = data_blob(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':
+ s = va_arg(ap, char *);
+ n = str_charnum(s);
+ SSVAL(blob->data, head_ofs, n*2); head_ofs += 2;
+ SSVAL(blob->data, head_ofs, n*2); head_ofs += 2;
+ SIVAL(blob->data, head_ofs, data_ofs); head_ofs += 4;
+ push_string(NULL, blob->data+data_ofs, s, n*2, STR_UNICODE|STR_NOALIGN);
+ data_ofs += n*2;
+ break;
+ case 'A':
+ s = va_arg(ap, char *);
+ n = str_ascii_charnum(s);
+ 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;
+ push_string(NULL, blob->data+data_ofs, s, n, STR_ASCII|STR_NOALIGN);
+ data_ofs += n;
+ break;
+ case 'a':
+ n = va_arg(ap, int);
+ SSVAL(blob->data, data_ofs, n); data_ofs += 2;
+ s = va_arg(ap, char *);
+ n = str_charnum(s);
+ SSVAL(blob->data, data_ofs, n*2); data_ofs += 2;
+ if (0 < n) {
+ push_string(NULL, blob->data+data_ofs, s, n*2,
+ STR_UNICODE|STR_NOALIGN);
+ }
+ data_ofs += n*2;
+ break;
+
+ case 'B':
+ b = va_arg(ap, uint8 *);
+ n = va_arg(ap, int);
+ 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 (n && b) /* don't follow null pointers... */
+ memcpy(blob->data+data_ofs, b, n);
+ data_ofs += n;
+ break;
+ case 'd':
+ n = va_arg(ap, int);
+ SIVAL(blob->data, head_ofs, n); head_ofs += 4;
+ break;
+ case 'b':
+ b = va_arg(ap, uint8 *);
+ n = va_arg(ap, int);
+ memcpy(blob->data + head_ofs, b, n);
+ head_ofs += n;
+ break;
+ case 'C':
+ s = va_arg(ap, char *);
+ head_ofs += push_string(NULL, blob->data+head_ofs, s, -1,
+ STR_ASCII|STR_TERMINATE);
+ break;
+ }
+ }
+ va_end(ap);
+
+ 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(const DATA_BLOB *blob,
+ const char *format, ...)
+{
+ int i;
+ va_list ap;
+ char **ps, *s;
+ DATA_BLOB *b;
+ size_t head_ofs = 0;
+ uint16 len1, len2;
+ uint32 ptr;
+ uint32 *v;
+ pstring p;
+
+ 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 = va_arg(ap, char **);
+ if (len1 == 0 && len2 == 0) {
+ *ps = smb_xstrdup("");
+ } else {
+ /* make sure its in the right format - be strict */
+ if ((len1 != len2) || (ptr + len1 < ptr) || (ptr + len1 < len1) || (ptr + len1 > blob->length)) {
+ return False;
+ }
+ if (len1 & 1) {
+ /* if odd length and unicode */
+ return False;
+ }
+ if (blob->data + ptr < (uint8 *)ptr || blob->data + ptr < blob->data)
+ return False;
+
+ if (0 < len1) {
+ pull_string(NULL, p, blob->data + ptr, sizeof(p),
+ len1,
+ STR_UNICODE|STR_NOALIGN);
+ (*ps) = smb_xstrdup(p);
+ } else {
+ (*ps) = smb_xstrdup("");
+ }
+ }
+ 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 = va_arg(ap, char **);
+ /* make sure its in the right format - be strict */
+ if (len1 == 0 && len2 == 0) {
+ *ps = smb_xstrdup("");
+ } else {
+ if ((len1 != len2) || (ptr + len1 < ptr) || (ptr + len1 < len1) || (ptr + len1 > blob->length)) {
+ return False;
+ }
+
+ if (blob->data + ptr < (uint8 *)ptr || blob->data + ptr < blob->data)
+ return False;
+
+ if (0 < len1) {
+ pull_string(NULL, p, blob->data + ptr, sizeof(p),
+ len1,
+ STR_ASCII|STR_NOALIGN);
+ (*ps) = smb_xstrdup(p);
+ } else {
+ (*ps) = smb_xstrdup("");
+ }
+ }
+ 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(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)) {
+ return False;
+ }
+
+ if (blob->data + ptr < (uint8 *)ptr || blob->data + ptr < blob->data)
+ return False;
+
+ *b = data_blob(blob->data + ptr, len1);
+ }
+ break;
+ case 'b':
+ b = (DATA_BLOB *)va_arg(ap, void *);
+ len1 = va_arg(ap, unsigned);
+ /* make sure its in the right format - be strict */
+ NEED_DATA(len1);
+ if (blob->data + head_ofs < (uint8 *)head_ofs || blob->data + head_ofs < blob->data)
+ return False;
+
+ *b = data_blob(blob->data + head_ofs, len1);
+ head_ofs += len1;
+ break;
+ case 'd':
+ v = va_arg(ap, uint32 *);
+ 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 *)head_ofs || blob->data + head_ofs < blob->data)
+ return False;
+
+ head_ofs += pull_string(NULL, p, blob->data+head_ofs, sizeof(p),
+ blob->length - head_ofs,
+ STR_ASCII|STR_TERMINATE);
+ if (strcmp(s, p) != 0) {
+ return False;
+ }
+ break;
+ }
+ }
+ va_end(ap);
+
+ return True;
+}
diff --git a/source/libsmb/ntlmssp_sign.c b/source/libsmb/ntlmssp_sign.c
new file mode 100644
index 00000000000..2347619e57d
--- /dev/null
+++ b/source/libsmb/ntlmssp_sign.c
@@ -0,0 +1,407 @@
+/*
+ * Unix SMB/CIFS implementation.
+ * Version 3.0
+ * NTLMSSP Signing routines
+ * Copyright (C) Luke Kenneth Casson Leighton 1996-2001
+ * Copyright (C) Andrew Bartlett 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 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.
+ */
+
+#include "includes.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"
+
+static void NTLMSSPcalc_ap( unsigned char *hash, unsigned char *data, int len)
+{
+ unsigned char index_i = hash[256];
+ unsigned char index_j = hash[257];
+ int ind;
+
+ for (ind = 0; ind < len; ind++)
+ {
+ unsigned char tc;
+ unsigned char t;
+
+ index_i++;
+ index_j += hash[index_i];
+
+ tc = hash[index_i];
+ hash[index_i] = hash[index_j];
+ hash[index_j] = tc;
+
+ t = hash[index_i] + hash[index_j];
+ data[ind] = data[ind] ^ hash[t];
+ }
+
+ hash[256] = index_i;
+ hash[257] = index_j;
+}
+
+static void calc_hash(unsigned char hash[258], const char *k2, int k2l)
+{
+ unsigned char j = 0;
+ int ind;
+
+ for (ind = 0; ind < 256; ind++)
+ {
+ hash[ind] = (unsigned char)ind;
+ }
+
+ for (ind = 0; ind < 256; ind++)
+ {
+ unsigned char tc;
+
+ j += (hash[ind] + k2[ind%k2l]);
+
+ tc = hash[ind];
+ hash[ind] = hash[j];
+ hash[j] = tc;
+ }
+
+ hash[256] = 0;
+ hash[257] = 0;
+}
+
+static void calc_ntlmv2_hash(unsigned char hash[258], unsigned char digest[16],
+ DATA_BLOB session_key,
+ const char *constant)
+{
+ struct MD5Context ctx3;
+
+ /* NOTE: This code is currently complate fantasy - it's
+ got more in common with reality than the previous code
+ (the LM session key is not the right thing to use) but
+ it still needs work */
+
+ MD5Init(&ctx3);
+ MD5Update(&ctx3, session_key.data, session_key.length);
+ MD5Update(&ctx3, (const unsigned char *)constant, strlen(constant)+1);
+ MD5Final(digest, &ctx3);
+
+ calc_hash(hash, digest, 16);
+}
+
+enum ntlmssp_direction {
+ NTLMSSP_SEND,
+ NTLMSSP_RECEIVE
+};
+
+static NTSTATUS ntlmssp_make_packet_signature(NTLMSSP_STATE *ntlmssp_state,
+ const uchar *data, size_t length,
+ enum ntlmssp_direction direction,
+ DATA_BLOB *sig)
+{
+ if (ntlmssp_state->neg_flags & NTLMSSP_NEGOTIATE_NTLM2) {
+ HMACMD5Context ctx;
+ uchar seq_num[4];
+ uchar digest[16];
+ SIVAL(seq_num, 0, ntlmssp_state->ntlmssp_seq_num);
+
+ hmac_md5_init_limK_to_64((const unsigned char *)(ntlmssp_state->send_sign_const), 16, &ctx);
+ hmac_md5_update(seq_num, 4, &ctx);
+ hmac_md5_update(data, length, &ctx);
+ hmac_md5_final(digest, &ctx);
+
+ if (!msrpc_gen(sig, "dBd", NTLMSSP_SIGN_VERSION, digest, 8 /* only copy first 8 bytes */
+ , ntlmssp_state->ntlmssp_seq_num)) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ if (ntlmssp_state->neg_flags & NTLMSSP_NEGOTIATE_KEY_EXCH) {
+ switch (direction) {
+ case NTLMSSP_SEND:
+ NTLMSSPcalc_ap(ntlmssp_state->send_sign_hash, sig->data+4, sig->length-4);
+ break;
+ case NTLMSSP_RECEIVE:
+ NTLMSSPcalc_ap(ntlmssp_state->recv_sign_hash, sig->data+4, sig->length-4);
+ break;
+ }
+ }
+ } else {
+ uint32 crc;
+ crc = crc32_calc_buffer((const char *)data, length);
+ if (!msrpc_gen(sig, "dddd", NTLMSSP_SIGN_VERSION, 0, crc, ntlmssp_state->ntlmssp_seq_num)) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ dump_data_pw("ntlmssp hash:\n", ntlmssp_state->ntlmssp_hash,
+ sizeof(ntlmssp_state->ntlmssp_hash));
+ NTLMSSPcalc_ap(ntlmssp_state->ntlmssp_hash, sig->data+4, sig->length-4);
+ }
+ return NT_STATUS_OK;
+}
+
+NTSTATUS ntlmssp_sign_packet(NTLMSSP_STATE *ntlmssp_state,
+ const uchar *data, size_t length,
+ DATA_BLOB *sig)
+{
+ NTSTATUS nt_status;
+ if (!ntlmssp_state->session_key.length) {
+ DEBUG(3, ("NO session key, cannot check sign packet\n"));
+ return NT_STATUS_NO_USER_SESSION_KEY;
+ }
+
+ nt_status = ntlmssp_make_packet_signature(ntlmssp_state, data, length, NTLMSSP_SEND, sig);
+
+ /* increment counter on send */
+ ntlmssp_state->ntlmssp_seq_num++;
+ return nt_status;
+}
+
+/**
+ * Check the signature of an incoming packet
+ * @note caller *must* check that the signature is the size it expects
+ *
+ */
+
+NTSTATUS ntlmssp_check_packet(NTLMSSP_STATE *ntlmssp_state,
+ const uchar *data, size_t length,
+ const DATA_BLOB *sig)
+{
+ DATA_BLOB local_sig;
+ NTSTATUS nt_status;
+
+ if (!ntlmssp_state->session_key.length) {
+ DEBUG(3, ("NO session key, cannot check packet signature\n"));
+ return NT_STATUS_NO_USER_SESSION_KEY;
+ }
+
+ if (sig->length < 8) {
+ DEBUG(0, ("NTLMSSP packet check failed due to short signature (%lu bytes)!\n",
+ (unsigned long)sig->length));
+ }
+
+ nt_status = ntlmssp_make_packet_signature(ntlmssp_state, data,
+ length, NTLMSSP_RECEIVE, &local_sig);
+
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ DEBUG(0, ("NTLMSSP packet check failed with %s\n", nt_errstr(nt_status)));
+ return nt_status;
+ }
+
+ if (memcmp(sig->data+sig->length - 8, local_sig.data+local_sig.length - 8, 8) != 0) {
+ DEBUG(5, ("BAD SIG: wanted signature of\n"));
+ dump_data(5, (const char *)local_sig.data, local_sig.length);
+
+ DEBUG(5, ("BAD SIG: got signature of\n"));
+ dump_data(5, (const char *)(sig->data), sig->length);
+
+ DEBUG(0, ("NTLMSSP packet check failed due to invalid signature!\n"));
+ return NT_STATUS_ACCESS_DENIED;
+ }
+
+ /* increment counter on recieive */
+ ntlmssp_state->ntlmssp_seq_num++;
+
+ return NT_STATUS_OK;
+}
+
+
+/**
+ * Seal data with the NTLMSSP algorithm
+ *
+ */
+
+NTSTATUS ntlmssp_seal_packet(NTLMSSP_STATE *ntlmssp_state,
+ uchar *data, size_t length,
+ DATA_BLOB *sig)
+{
+ if (!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 (ntlmssp_state->neg_flags & NTLMSSP_NEGOTIATE_NTLM2) {
+ HMACMD5Context ctx;
+ char seq_num[4];
+ uchar digest[16];
+ SIVAL(seq_num, 0, ntlmssp_state->ntlmssp_seq_num);
+
+ hmac_md5_init_limK_to_64((const unsigned char *)(ntlmssp_state->send_sign_const), 16, &ctx);
+ hmac_md5_update((const unsigned char *)seq_num, 4, &ctx);
+ hmac_md5_update(data, length, &ctx);
+ hmac_md5_final(digest, &ctx);
+
+ if (!msrpc_gen(sig, "dBd", NTLMSSP_SIGN_VERSION, digest, 8 /* only copy first 8 bytes */
+ , ntlmssp_state->ntlmssp_seq_num)) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ dump_data_pw("ntlmssp client sealing hash:\n",
+ ntlmssp_state->send_seal_hash,
+ sizeof(ntlmssp_state->send_seal_hash));
+ NTLMSSPcalc_ap(ntlmssp_state->send_seal_hash, data, length);
+ dump_data_pw("ntlmssp client signing hash:\n",
+ ntlmssp_state->send_sign_hash,
+ sizeof(ntlmssp_state->send_sign_hash));
+ NTLMSSPcalc_ap(ntlmssp_state->send_sign_hash, sig->data+4, sig->length-4);
+ } else {
+ uint32 crc;
+ crc = crc32_calc_buffer((const char *)data, length);
+ if (!msrpc_gen(sig, "dddd", NTLMSSP_SIGN_VERSION, 0, crc, ntlmssp_state->ntlmssp_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 */
+
+ dump_data_pw("ntlmssp hash:\n", ntlmssp_state->ntlmssp_hash,
+ sizeof(ntlmssp_state->ntlmssp_hash));
+ NTLMSSPcalc_ap(ntlmssp_state->ntlmssp_hash, data, length);
+
+ dump_data_pw("ntlmssp hash:\n", ntlmssp_state->ntlmssp_hash,
+ sizeof(ntlmssp_state->ntlmssp_hash));
+ NTLMSSPcalc_ap(ntlmssp_state->ntlmssp_hash, sig->data+4, sig->length-4);
+ }
+ dump_data_pw("ntlmssp sealed data\n", data, length);
+
+ /* increment counter on send */
+ ntlmssp_state->ntlmssp_seq_num++;
+
+ return NT_STATUS_OK;
+}
+
+/**
+ * Unseal data with the NTLMSSP algorithm
+ *
+ */
+
+NTSTATUS ntlmssp_unseal_packet(NTLMSSP_STATE *ntlmssp_state,
+ uchar *data, size_t length,
+ DATA_BLOB *sig)
+{
+ if (!ntlmssp_state->session_key.length) {
+ DEBUG(3, ("NO session key, cannot unseal packet\n"));
+ return NT_STATUS_NO_USER_SESSION_KEY;
+ }
+
+ DEBUG(10,("ntlmssp__unseal_data: seal\n"));
+ dump_data_pw("ntlmssp sealed data\n", data, length);
+ if (ntlmssp_state->neg_flags & NTLMSSP_NEGOTIATE_NTLM2) {
+ NTLMSSPcalc_ap(ntlmssp_state->recv_seal_hash, data, length);
+ } else {
+ dump_data_pw("ntlmssp hash:\n", ntlmssp_state->ntlmssp_hash,
+ sizeof(ntlmssp_state->ntlmssp_hash));
+ NTLMSSPcalc_ap(ntlmssp_state->ntlmssp_hash, data, length);
+ }
+ dump_data_pw("ntlmssp clear data\n", data, length);
+
+ return ntlmssp_check_packet(ntlmssp_state, data, length, sig);
+}
+
+/**
+ Initialise the state for NTLMSSP signing.
+*/
+NTSTATUS ntlmssp_sign_init(NTLMSSP_STATE *ntlmssp_state)
+{
+ unsigned char p24[24];
+ ZERO_STRUCT(p24);
+
+ DEBUG(3, ("NTLMSSP Sign/Seal - Initialising with flags:\n"));
+ debug_ntlmssp_flags(ntlmssp_state->neg_flags);
+
+ if (!ntlmssp_state->session_key.length) {
+ DEBUG(3, ("NO session key, cannot intialise signing\n"));
+ return NT_STATUS_NO_USER_SESSION_KEY;
+ }
+
+ if (ntlmssp_state->neg_flags & NTLMSSP_NEGOTIATE_NTLM2)
+ {
+ const char *send_sign_const;
+ const char *send_seal_const;
+ const char *recv_sign_const;
+ const char *recv_seal_const;
+
+ switch (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;
+ }
+
+ calc_ntlmv2_hash(ntlmssp_state->send_sign_hash,
+ ntlmssp_state->send_sign_const,
+ ntlmssp_state->session_key, send_sign_const);
+ dump_data_pw("NTLMSSP send sign hash:\n",
+ ntlmssp_state->send_sign_hash,
+ sizeof(ntlmssp_state->send_sign_hash));
+
+ calc_ntlmv2_hash(ntlmssp_state->send_seal_hash,
+ ntlmssp_state->send_seal_const,
+ ntlmssp_state->session_key, send_seal_const);
+ dump_data_pw("NTLMSSP send sesl hash:\n",
+ ntlmssp_state->send_seal_hash,
+ sizeof(ntlmssp_state->send_seal_hash));
+
+ calc_ntlmv2_hash(ntlmssp_state->recv_sign_hash,
+ ntlmssp_state->recv_sign_const,
+ ntlmssp_state->session_key, recv_sign_const);
+ dump_data_pw("NTLMSSP receive sign hash:\n",
+ ntlmssp_state->recv_sign_hash,
+ sizeof(ntlmssp_state->recv_sign_hash));
+
+ calc_ntlmv2_hash(ntlmssp_state->recv_seal_hash,
+ ntlmssp_state->recv_seal_const,
+ ntlmssp_state->session_key, recv_seal_const);
+ dump_data_pw("NTLMSSP receive seal hash:\n",
+ ntlmssp_state->recv_sign_hash,
+ sizeof(ntlmssp_state->recv_sign_hash));
+
+ }
+ else if (ntlmssp_state->neg_flags & NTLMSSP_NEGOTIATE_LM_KEY) {
+ if (!ntlmssp_state->session_key.data || ntlmssp_state->session_key.length < 8) {
+ /* can't sign or check signatures yet */
+ DEBUG(5, ("NTLMSSP Sign/Seal - cannot use LM KEY yet\n"));
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ DEBUG(5, ("NTLMSSP Sign/Seal - using LM KEY\n"));
+
+ calc_hash(ntlmssp_state->ntlmssp_hash, (const char *)(ntlmssp_state->session_key.data), 8);
+ dump_data_pw("NTLMSSP hash:\n", ntlmssp_state->ntlmssp_hash,
+ sizeof(ntlmssp_state->ntlmssp_hash));
+ } else {
+ if (!ntlmssp_state->session_key.data || ntlmssp_state->session_key.length < 16) {
+ /* can't sign or check signatures yet */
+ DEBUG(5, ("NTLMSSP Sign/Seal - cannot use NT KEY yet\n"));
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ DEBUG(5, ("NTLMSSP Sign/Seal - using NT KEY\n"));
+
+ calc_hash(ntlmssp_state->ntlmssp_hash, (const char *)(ntlmssp_state->session_key.data), 16);
+ dump_data_pw("NTLMSSP hash:\n", ntlmssp_state->ntlmssp_hash,
+ sizeof(ntlmssp_state->ntlmssp_hash));
+ }
+
+ ntlmssp_state->ntlmssp_seq_num = 0;
+
+ return NT_STATUS_OK;
+}
diff --git a/source/libsmb/passchange.c b/source/libsmb/passchange.c
new file mode 100644
index 00000000000..dc0cbbcb7cc
--- /dev/null
+++ b/source/libsmb/passchange.c
@@ -0,0 +1,152 @@
+/*
+ Unix SMB/CIFS implementation.
+ SMB client password change routine
+ Copyright (C) Andrew Tridgell 1994-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 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"
+
+/*************************************************************
+change a password on a remote machine using IPC calls
+*************************************************************/
+BOOL remote_password_change(const char *remote_machine, const char *user_name,
+ const char *old_passwd, const char *new_passwd,
+ char *err_str, size_t err_str_len)
+{
+ struct nmb_name calling, called;
+ struct cli_state cli;
+ struct in_addr ip;
+ struct ntuser_creds creds;
+
+ NTSTATUS result;
+
+ *err_str = '\0';
+
+ if(!resolve_name( remote_machine, &ip, 0x20)) {
+ slprintf(err_str, err_str_len-1, "unable to find an IP address for machine %s.\n",
+ remote_machine );
+ return False;
+ }
+
+ ZERO_STRUCT(cli);
+
+ if (!cli_initialise(&cli) || !cli_connect(&cli, remote_machine, &ip)) {
+ slprintf(err_str, err_str_len-1, "unable to connect to SMB server on machine %s. Error was : %s.\n",
+ remote_machine, cli_errstr(&cli) );
+ return False;
+ }
+
+ make_nmb_name(&calling, global_myname() , 0x0);
+ make_nmb_name(&called , remote_machine, 0x20);
+
+ if (!cli_session_request(&cli, &calling, &called)) {
+ slprintf(err_str, err_str_len-1, "machine %s rejected the session setup. Error was : %s.\n",
+ remote_machine, cli_errstr(&cli) );
+ cli_shutdown(&cli);
+ return False;
+ }
+
+ cli.protocol = PROTOCOL_NT1;
+
+ if (!cli_negprot(&cli)) {
+ slprintf(err_str, err_str_len-1, "machine %s rejected the negotiate protocol. Error was : %s.\n",
+ remote_machine, cli_errstr(&cli) );
+ cli_shutdown(&cli);
+ return False;
+ }
+
+ /* Given things like SMB signing, restrict anonymous and the like,
+ try an authenticated connection first */
+ if (!cli_session_setup(&cli, user_name, old_passwd, strlen(old_passwd)+1, old_passwd, strlen(old_passwd)+1, "")) {
+ /*
+ * We should connect as the anonymous user here, in case
+ * the server has "must change password" checked...
+ * Thanks to <Nicholas.S.Jenkins@cdc.com> for this fix.
+ */
+
+ if (!cli_session_setup(&cli, "", "", 0, "", 0, "")) {
+ slprintf(err_str, err_str_len-1, "machine %s rejected the session setup. Error was : %s.\n",
+ remote_machine, cli_errstr(&cli) );
+ cli_shutdown(&cli);
+ return False;
+ }
+
+ init_creds(&creds, "", "", NULL);
+ cli_init_creds(&cli, &creds);
+ } else {
+ init_creds(&creds, user_name, "", old_passwd);
+ cli_init_creds(&cli, &creds);
+ }
+
+ if (!cli_send_tconX(&cli, "IPC$", "IPC", "", 1)) {
+ slprintf(err_str, err_str_len-1, "machine %s rejected the tconX on the IPC$ share. Error was : %s.\n",
+ remote_machine, cli_errstr(&cli) );
+ cli_shutdown(&cli);
+ return False;
+ }
+
+ /* Try not to give the password away to easily */
+
+ cli.pipe_auth_flags = AUTH_PIPE_NTLMSSP;
+ cli.pipe_auth_flags |= AUTH_PIPE_SIGN;
+ cli.pipe_auth_flags |= AUTH_PIPE_SEAL;
+
+ if ( !cli_nt_session_open( &cli, PI_SAMR ) ) {
+ if (lp_client_lanman_auth()) {
+ if (!cli_oem_change_password(&cli, user_name, new_passwd, old_passwd)) {
+ slprintf(err_str, err_str_len-1, "machine %s rejected the password change: Error was : %s.\n",
+ remote_machine, cli_errstr(&cli) );
+ cli_shutdown(&cli);
+ return False;
+ }
+ } else {
+ slprintf(err_str, err_str_len-1, "machine %s does not support SAMR connections, but LANMAN password changed are disabled\n",
+ remote_machine);
+ cli_shutdown(&cli);
+ return False;
+ }
+ }
+
+ if (!NT_STATUS_IS_OK(result = cli_samr_chgpasswd_user(&cli, cli.mem_ctx, user_name,
+ new_passwd, old_passwd))) {
+
+ if (NT_STATUS_EQUAL(result, NT_STATUS_ACCESS_DENIED)
+ || NT_STATUS_EQUAL(result, NT_STATUS_UNSUCCESSFUL)) {
+ /* try the old Lanman method */
+ if (lp_client_lanman_auth()) {
+ if (!cli_oem_change_password(&cli, user_name, new_passwd, old_passwd)) {
+ slprintf(err_str, err_str_len-1, "machine %s rejected the password change: Error was : %s.\n",
+ remote_machine, cli_errstr(&cli) );
+ cli_shutdown(&cli);
+ return False;
+ }
+ } else {
+ slprintf(err_str, err_str_len-1, "machine %s does not support SAMR connections, but LANMAN password changed are disabled\n",
+ remote_machine);
+ cli_shutdown(&cli);
+ return False;
+ }
+ } else {
+ slprintf(err_str, err_str_len-1, "machine %s rejected the password change: Error was : %s.\n",
+ remote_machine, get_friendly_nt_error_msg(result));
+ cli_shutdown(&cli);
+ return False;
+ }
+ }
+ cli_shutdown(&cli);
+ return True;
+}
diff --git a/source/libsmb/pwd_cache.c b/source/libsmb/pwd_cache.c
new file mode 100644
index 00000000000..e010f226a02
--- /dev/null
+++ b/source/libsmb/pwd_cache.c
@@ -0,0 +1,63 @@
+/*
+ Unix SMB/CIFS implementation.
+ Password cacheing. obfuscation is planned
+ Copyright (C) Luke Kenneth Casson Leighton 1996-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 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"
+
+/****************************************************************************
+ Initialises a password structure.
+****************************************************************************/
+
+static void pwd_init(struct pwd_info *pwd)
+{
+ memset((char *)pwd->password , '\0', sizeof(pwd->password ));
+
+ pwd->null_pwd = True; /* safest option... */
+}
+
+/****************************************************************************
+ Stores a cleartext password.
+****************************************************************************/
+
+void pwd_set_cleartext(struct pwd_info *pwd, const char *clr)
+{
+ pwd_init(pwd);
+ if (clr) {
+ fstrcpy(pwd->password, clr);
+ pwd->null_pwd = False;
+ } else {
+ pwd->null_pwd = True;
+ }
+
+ pwd->cleartext = True;
+}
+
+/****************************************************************************
+ Gets a cleartext password.
+****************************************************************************/
+
+void pwd_get_cleartext(struct pwd_info *pwd, fstring clr)
+{
+ if (pwd->cleartext)
+ fstrcpy(clr, pwd->password);
+ else
+ clr[0] = 0;
+
+}
+
diff --git a/source/libsmb/samlogon_cache.c b/source/libsmb/samlogon_cache.c
new file mode 100644
index 00000000000..4cd642c4e35
--- /dev/null
+++ b/source/libsmb/samlogon_cache.c
@@ -0,0 +1,238 @@
+/*
+ Unix SMB/CIFS implementation.
+ Net_sam_logon info3 helpers
+ Copyright (C) Alexander Bokovoy 2002.
+ Copyright (C) Andrew Bartlett 2002.
+ Copyright (C) Gerald Carter 2003.
+ 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 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"
+
+#define NETSAMLOGON_TDB "netsamlogon_cache.tdb"
+
+static TDB_CONTEXT *netsamlogon_tdb = NULL;
+
+/***********************************************************************
+ open the tdb
+ ***********************************************************************/
+
+BOOL netsamlogon_cache_init(void)
+{
+ if (!netsamlogon_tdb) {
+ netsamlogon_tdb = tdb_open_log(lock_path(NETSAMLOGON_TDB), 0,
+ TDB_DEFAULT, O_RDWR | O_CREAT, 0600);
+ }
+
+ return (netsamlogon_tdb != NULL);
+}
+
+
+/***********************************************************************
+ Shutdown samlogon_cache database
+***********************************************************************/
+
+BOOL netsamlogon_cache_shutdown(void)
+{
+ if(netsamlogon_tdb)
+ return (tdb_close(netsamlogon_tdb) == 0);
+
+ return True;
+}
+
+/***********************************************************************
+ Clear cache getpwnam and getgroups entries from the winbindd cache
+***********************************************************************/
+void netsamlogon_clear_cached_user(TDB_CONTEXT *tdb, NET_USER_INFO_3 *user)
+{
+ fstring domain;
+ TDB_DATA key;
+ BOOL got_tdb = False;
+
+ /* We may need to call this function from smbd which will not have
+ winbindd_cache.tdb open. Open the tdb if a NULL is passed. */
+
+ if (!tdb) {
+ tdb = tdb_open_log(lock_path("winbindd_cache.tdb"), 5000,
+ TDB_DEFAULT, O_RDWR, 0600);
+ if (!tdb) {
+ DEBUG(5, ("netsamlogon_clear_cached_user: failed to open cache\n"));
+ return;
+ }
+ got_tdb = True;
+ }
+
+ unistr2_to_ascii(domain, &user->uni_logon_dom, sizeof(domain) - 1);
+
+ /* Clear U/DOMAIN/RID cache entry */
+
+ asprintf(&key.dptr, "U/%s/%d", domain, user->user_rid);
+ key.dsize = strlen(key.dptr) - 1; /* keys are not NULL terminated */
+
+ DEBUG(10, ("netsamlogon_clear_cached_user: clearing %s\n", key.dptr));
+
+ tdb_delete(tdb, key);
+
+ SAFE_FREE(key.dptr);
+
+ /* Clear UG/DOMAIN/RID cache entry */
+
+ asprintf(&key.dptr, "UG/%s/%d", domain, user->user_rid);
+ key.dsize = strlen(key.dptr) - 1; /* keys are not NULL terminated */
+
+ DEBUG(10, ("netsamlogon_clear_cached_user: clearing %s\n", key.dptr));
+
+ tdb_delete(tdb, key);
+
+ SAFE_FREE(key.dptr);
+
+ if (got_tdb)
+ tdb_close(tdb);
+}
+
+/***********************************************************************
+ Store a NET_USER_INFO_3 structure in a tdb for later user
+***********************************************************************/
+
+BOOL netsamlogon_cache_store(TALLOC_CTX *mem_ctx, NET_USER_INFO_3 *user)
+{
+ TDB_DATA data;
+ fstring keystr;
+ prs_struct ps;
+ BOOL result = False;
+ DOM_SID user_sid;
+ time_t t = time(NULL);
+
+
+ if (!netsamlogon_cache_init()) {
+ DEBUG(0,("netsamlogon_cache_store: cannot open %s for write!\n", NETSAMLOGON_TDB));
+ return False;
+ }
+
+ sid_copy( &user_sid, &user->dom_sid.sid );
+ sid_append_rid( &user_sid, user->user_rid );
+
+ /* Prepare key as DOMAIN-SID/USER-RID string */
+ slprintf(keystr, sizeof(keystr), "%s", sid_string_static(&user_sid));
+
+ DEBUG(10,("netsamlogon_cache_store: SID [%s]\n", keystr));
+
+ /* Prepare data */
+
+ prs_init( &ps,MAX_PDU_FRAG_LEN , mem_ctx, MARSHALL);
+
+ if ( !prs_uint32( "timestamp", &ps, 0, (uint32*)&t ) )
+ return False;
+
+ if ( net_io_user_info3("", user, &ps, 0, 3) )
+ {
+ data.dsize = prs_offset( &ps );
+ data.dptr = prs_data_p( &ps );
+
+ if (tdb_store_bystring(netsamlogon_tdb, keystr, data, TDB_REPLACE) != -1)
+ result = True;
+
+ prs_mem_free( &ps );
+ }
+
+ return result;
+}
+
+/***********************************************************************
+ Retrieves a NET_USER_INFO_3 structure from a tdb. Caller must
+ free the user_info struct (malloc()'d memory)
+***********************************************************************/
+
+NET_USER_INFO_3* netsamlogon_cache_get( TALLOC_CTX *mem_ctx, const DOM_SID *user_sid)
+{
+ NET_USER_INFO_3 *user = NULL;
+ TDB_DATA data, key;
+ prs_struct ps;
+ fstring keystr;
+ uint32 t;
+
+ if (!netsamlogon_cache_init()) {
+ DEBUG(0,("netsamlogon_cache_store: cannot open %s for write!\n", NETSAMLOGON_TDB));
+ return False;
+ }
+
+ /* Prepare key as DOMAIN-SID/USER-RID string */
+ slprintf(keystr, sizeof(keystr), "%s", sid_string_static(user_sid));
+ DEBUG(10,("netsamlogon_cache_get: SID [%s]\n", keystr));
+ key.dptr = keystr;
+ key.dsize = strlen(keystr)+1;
+ data = tdb_fetch( netsamlogon_tdb, key );
+
+ if ( data.dptr ) {
+
+ if ( (user = (NET_USER_INFO_3*)malloc(sizeof(NET_USER_INFO_3))) == NULL )
+ return NULL;
+
+ prs_init( &ps, 0, mem_ctx, UNMARSHALL );
+ prs_give_memory( &ps, data.dptr, data.dsize, True );
+
+ if ( !prs_uint32( "timestamp", &ps, 0, &t ) ) {
+ prs_mem_free( &ps );
+ return False;
+ }
+
+ if ( !net_io_user_info3("", user, &ps, 0, 3) ) {
+ SAFE_FREE( user );
+ }
+
+ prs_mem_free( &ps );
+
+#if 0 /* The netsamlogon cache needs to hang around. Something about
+ this feels wrong, but it is the only way we can get all of the
+ groups. The old universal groups cache didn't expire either.
+ --jerry */
+ {
+ time_t now = time(NULL);
+ uint32 time_diff;
+
+ /* is the entry expired? */
+ time_diff = now - t;
+
+ if ( (time_diff < 0 ) || (time_diff > lp_winbind_cache_time()) ) {
+ DEBUG(10,("netsamlogon_cache_get: cache entry expired \n"));
+ tdb_delete( netsamlogon_tdb, key );
+ SAFE_FREE( user );
+ }
+#endif
+ }
+
+ return user;
+}
+
+BOOL netsamlogon_cache_have(const DOM_SID *user_sid)
+{
+ TALLOC_CTX *mem_ctx = talloc_init("netsamlogon_cache_have");
+ NET_USER_INFO_3 *user = NULL;
+ BOOL result;
+
+ if (!mem_ctx)
+ return False;
+
+ user = netsamlogon_cache_get(mem_ctx, user_sid);
+
+ result = (user != NULL);
+
+ talloc_destroy(mem_ctx);
+ SAFE_FREE(user);
+
+ return result;
+}
diff --git a/source/libsmb/smb_signing.c b/source/libsmb/smb_signing.c
new file mode 100644
index 00000000000..28ff0e0c2e9
--- /dev/null
+++ b/source/libsmb/smb_signing.c
@@ -0,0 +1,1052 @@
+/*
+ Unix SMB/CIFS implementation.
+ SMB Signing Code
+ Copyright (C) Jeremy Allison 2003.
+ 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 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"
+
+/* Lookup a packet's MID (multiplex id) and figure out it's sequence number */
+struct outstanding_packet_lookup {
+ uint16 mid;
+ uint32 reply_seq_num;
+ struct outstanding_packet_lookup *prev, *next;
+};
+
+/* Store the data for an ongoing trans/trans2/nttrans operation. */
+struct trans_info_context {
+ uint16 mid;
+ uint32 send_seq_num;
+ uint32 reply_seq_num;
+};
+
+struct smb_basic_signing_context {
+ DATA_BLOB mac_key;
+ uint32 send_seq_num;
+ struct trans_info_context *trans_info;
+ struct outstanding_packet_lookup *outstanding_packet_list;
+};
+
+static void store_sequence_for_reply(struct outstanding_packet_lookup **list,
+ uint16 mid, uint32 reply_seq_num)
+{
+ struct outstanding_packet_lookup *t;
+
+ t = smb_xmalloc(sizeof(*t));
+ ZERO_STRUCTP(t);
+
+ t->mid = mid;
+ t->reply_seq_num = reply_seq_num;
+
+ /*
+ * Add to the *start* of the list not the end of the list.
+ * This ensures that the *last* send sequence with this mid
+ * is returned by preference.
+ * This can happen if the mid wraps and one of the early
+ * mid numbers didn't get a reply and is still lurking on
+ * the list. JRA. Found by Fran Fabrizio <fran@cis.uab.edu>.
+ */
+
+ DLIST_ADD(*list, t);
+ DEBUG(10,("store_sequence_for_reply: stored seq = %u mid = %u\n",
+ (unsigned int)reply_seq_num, (unsigned int)mid ));
+}
+
+static BOOL get_sequence_for_reply(struct outstanding_packet_lookup **list,
+ uint16 mid, uint32 *reply_seq_num)
+{
+ struct outstanding_packet_lookup *t;
+
+ for (t = *list; t; t = t->next) {
+ if (t->mid == mid) {
+ *reply_seq_num = t->reply_seq_num;
+ DEBUG(10,("get_sequence_for_reply: found seq = %u mid = %u\n",
+ (unsigned int)t->reply_seq_num, (unsigned int)t->mid ));
+ DLIST_REMOVE(*list, t);
+ SAFE_FREE(t);
+ return True;
+ }
+ }
+ return False;
+}
+
+/***********************************************************
+ SMB signing - Common code before we set a new signing implementation
+************************************************************/
+
+static BOOL cli_set_smb_signing_common(struct cli_state *cli)
+{
+ if (!cli->sign_info.negotiated_smb_signing
+ && !cli->sign_info.mandatory_signing) {
+ return False;
+ }
+
+ if (cli->sign_info.doing_signing) {
+ return False;
+ }
+
+ if (cli->sign_info.free_signing_context)
+ cli->sign_info.free_signing_context(&cli->sign_info);
+
+ /* These calls are INCOMPATIBLE with SMB signing */
+ cli->readbraw_supported = False;
+ cli->writebraw_supported = False;
+
+ return True;
+}
+
+/***********************************************************
+ SMB signing - Common code for 'real' implementations
+************************************************************/
+
+static BOOL set_smb_signing_real_common(struct smb_sign_info *si)
+{
+ if (si->mandatory_signing) {
+ DEBUG(5, ("Mandatory SMB signing enabled!\n"));
+ }
+
+ si->doing_signing = True;
+ DEBUG(5, ("SMB signing enabled!\n"));
+
+ return True;
+}
+
+static void mark_packet_signed(char *outbuf)
+{
+ uint16 flags2;
+ flags2 = SVAL(outbuf,smb_flg2);
+ flags2 |= FLAGS2_SMB_SECURITY_SIGNATURES;
+ SSVAL(outbuf,smb_flg2, flags2);
+}
+
+/***********************************************************
+ SMB signing - NULL implementation - calculate a MAC to send.
+************************************************************/
+
+static void null_sign_outgoing_message(char *outbuf, struct smb_sign_info *si)
+{
+ /* we can't zero out the sig, as we might be trying to send a
+ session request - which is NBT-level, not SMB level and doesn't
+ have the field */
+ return;
+}
+
+/***********************************************************
+ SMB signing - NULL implementation - check a MAC sent by server.
+************************************************************/
+
+static BOOL null_check_incoming_message(char *inbuf, struct smb_sign_info *si, BOOL expected_ok)
+{
+ return True;
+}
+
+/***********************************************************
+ SMB signing - NULL implementation - free signing context
+************************************************************/
+
+static void null_free_signing_context(struct smb_sign_info *si)
+{
+ return;
+}
+
+/**
+ SMB signing - NULL implementation - setup the MAC key.
+
+ @note Used as an initialisation only - it will not correctly
+ shut down a real signing mechanism
+*/
+
+static BOOL null_set_signing(struct smb_sign_info *si)
+{
+ si->signing_context = NULL;
+
+ si->sign_outgoing_message = null_sign_outgoing_message;
+ si->check_incoming_message = null_check_incoming_message;
+ si->free_signing_context = null_free_signing_context;
+
+ return True;
+}
+
+/**
+ * Free the signing context
+ */
+
+static void free_signing_context(struct smb_sign_info *si)
+{
+ if (si->free_signing_context) {
+ si->free_signing_context(si);
+ si->signing_context = NULL;
+ }
+
+ null_set_signing(si);
+}
+
+
+static BOOL signing_good(char *inbuf, struct smb_sign_info *si, BOOL good, uint32 seq, BOOL expected_ok)
+{
+ if (good) {
+
+ if (!si->doing_signing) {
+ si->doing_signing = True;
+ }
+
+ if (!si->seen_valid) {
+ si->seen_valid = True;
+ }
+
+ } else {
+ if (!si->mandatory_signing && !si->seen_valid) {
+
+ if (!expected_ok) {
+ return True;
+ }
+ /* Non-mandatory signing - just turn off if this is the first bad packet.. */
+ DEBUG(5, ("signing_good: signing negotiated but not required and the other side \
+isn't sending correct signatures. Turning signatures off.\n"));
+ si->negotiated_smb_signing = False;
+ si->allow_smb_signing = False;
+ si->doing_signing = False;
+ free_signing_context(si);
+ return True;
+ } else if (!expected_ok) {
+ /* This packet is known to be unsigned */
+ return True;
+ } else {
+ /* Mandatory signing or bad packet after signing started - fail and disconnect. */
+ if (seq)
+ DEBUG(0, ("signing_good: BAD SIG: seq %u\n", (unsigned int)seq));
+ return False;
+ }
+ }
+ return True;
+}
+
+/***********************************************************
+ SMB signing - Simple implementation - calculate a MAC on the packet
+************************************************************/
+
+static void simple_packet_signature(struct smb_basic_signing_context *data,
+ const uchar *buf, uint32 seq_number,
+ unsigned char calc_md5_mac[16])
+{
+ const size_t offset_end_of_sig = (smb_ss_field + 8);
+ unsigned char sequence_buf[8];
+ struct MD5Context md5_ctx;
+
+ /*
+ * Firstly put the sequence number into the first 4 bytes.
+ * and zero out the next 4 bytes.
+ *
+ * We do this here, to avoid modifying the packet.
+ */
+
+ DEBUG(10,("simple_packet_signature: sequence number %u\n", seq_number ));
+
+ SIVAL(sequence_buf, 0, seq_number);
+ SIVAL(sequence_buf, 4, 0);
+
+ /* Calculate the 16 byte MAC - but don't alter the data in the
+ incoming packet.
+
+ This makes for a bit of fussing about, but it's not too bad.
+ */
+ MD5Init(&md5_ctx);
+
+ /* intialise with the key */
+ MD5Update(&md5_ctx, data->mac_key.data,
+ data->mac_key.length);
+
+ /* copy in the first bit of the SMB header */
+ MD5Update(&md5_ctx, buf + 4, smb_ss_field - 4);
+
+ /* copy in the sequence number, instead of the signature */
+ MD5Update(&md5_ctx, sequence_buf, sizeof(sequence_buf));
+
+ /* copy in the rest of the packet in, skipping the signature */
+ MD5Update(&md5_ctx, buf + offset_end_of_sig,
+ smb_len(buf) - (offset_end_of_sig - 4));
+
+ /* calculate the MD5 sig */
+ MD5Final(calc_md5_mac, &md5_ctx);
+}
+
+
+/***********************************************************
+ SMB signing - Client implementation - send the MAC.
+************************************************************/
+
+static void client_sign_outgoing_message(char *outbuf, struct smb_sign_info *si)
+{
+ unsigned char calc_md5_mac[16];
+ struct smb_basic_signing_context *data = si->signing_context;
+ uint32 send_seq_num;
+
+ if (!si->doing_signing)
+ return;
+
+ /* JRA Paranioa test - we should be able to get rid of this... */
+ if (smb_len(outbuf) < (smb_ss_field + 8 - 4)) {
+ DEBUG(1, ("client_sign_outgoing_message: Logic error. Can't check signature on short packet! smb_len = %u\n",
+ smb_len(outbuf) ));
+ abort();
+ }
+
+ /* mark the packet as signed - BEFORE we sign it...*/
+ mark_packet_signed(outbuf);
+
+ if (data->trans_info)
+ send_seq_num = data->trans_info->send_seq_num;
+ else
+ send_seq_num = data->send_seq_num;
+
+ simple_packet_signature(data, (const unsigned char *)outbuf, send_seq_num, calc_md5_mac);
+
+ DEBUG(10, ("client_sign_outgoing_message: sent SMB signature of\n"));
+ dump_data(10, (const char *)calc_md5_mac, 8);
+
+ memcpy(&outbuf[smb_ss_field], calc_md5_mac, 8);
+
+/* cli->outbuf[smb_ss_field+2]=0;
+ Uncomment this to test if the remote server actually verifies signatures...*/
+
+ if (data->trans_info)
+ return;
+
+ data->send_seq_num++;
+ store_sequence_for_reply(&data->outstanding_packet_list,
+ SVAL(outbuf,smb_mid), data->send_seq_num);
+ data->send_seq_num++;
+}
+
+/***********************************************************
+ SMB signing - Client implementation - check a MAC sent by server.
+************************************************************/
+
+static BOOL client_check_incoming_message(char *inbuf, struct smb_sign_info *si, BOOL expected_ok)
+{
+ BOOL good;
+ uint32 reply_seq_number;
+ uint32 saved_seq;
+ unsigned char calc_md5_mac[16];
+ unsigned char *server_sent_mac;
+
+ struct smb_basic_signing_context *data = si->signing_context;
+
+ if (!si->doing_signing)
+ return True;
+
+ if (smb_len(inbuf) < (smb_ss_field + 8 - 4)) {
+ DEBUG(1, ("client_check_incoming_message: Can't check signature on short packet! smb_len = %u\n", smb_len(inbuf)));
+ return False;
+ }
+
+ if (data->trans_info) {
+ reply_seq_number = data->trans_info->reply_seq_num;
+ } else if (!get_sequence_for_reply(&data->outstanding_packet_list,
+ SVAL(inbuf, smb_mid), &reply_seq_number)) {
+ DEBUG(1, ("client_check_incoming_message: failed to get sequence number %u for reply.\n",
+ (unsigned int) SVAL(inbuf, smb_mid) ));
+ return False;
+ }
+
+ saved_seq = reply_seq_number;
+ simple_packet_signature(data, (const unsigned char *)inbuf, reply_seq_number, calc_md5_mac);
+
+ server_sent_mac = (unsigned char *)&inbuf[smb_ss_field];
+ good = (memcmp(server_sent_mac, calc_md5_mac, 8) == 0);
+
+ if (!good) {
+ DEBUG(5, ("client_check_incoming_message: BAD SIG: wanted SMB signature of\n"));
+ dump_data(5, (const char *)calc_md5_mac, 8);
+
+ DEBUG(5, ("client_check_incoming_message: BAD SIG: got SMB signature of\n"));
+ dump_data(5, (const char *)server_sent_mac, 8);
+#if 1 /* JRATEST */
+ {
+ int i;
+ reply_seq_number -= 5;
+ for (i = 0; i < 10; i++, reply_seq_number++) {
+ simple_packet_signature(data, (const unsigned char *)inbuf, reply_seq_number, calc_md5_mac);
+ if (memcmp(server_sent_mac, calc_md5_mac, 8) == 0) {
+ DEBUG(0,("client_check_incoming_message: out of seq. seq num %u matches. \
+We were expecting seq %u\n", reply_seq_number, saved_seq ));
+ break;
+ }
+ }
+ }
+#endif /* JRATEST */
+
+ } else {
+ DEBUG(10, ("client_check_incoming_message: seq %u: got good SMB signature of\n", (unsigned int)reply_seq_number));
+ dump_data(10, (const char *)server_sent_mac, 8);
+ }
+ return signing_good(inbuf, si, good, saved_seq, expected_ok);
+}
+
+/***********************************************************
+ SMB signing - Simple implementation - free signing context
+************************************************************/
+
+static void simple_free_signing_context(struct smb_sign_info *si)
+{
+ struct smb_basic_signing_context *data = si->signing_context;
+ struct outstanding_packet_lookup *list = data->outstanding_packet_list;
+
+ while (list) {
+ struct outstanding_packet_lookup *old_head = list;
+ DLIST_REMOVE(list, list);
+ SAFE_FREE(old_head);
+ }
+
+ data_blob_free(&data->mac_key);
+
+ if (data->trans_info)
+ SAFE_FREE(data->trans_info);
+
+ SAFE_FREE(si->signing_context);
+
+ return;
+}
+
+/***********************************************************
+ SMB signing - Simple implementation - setup the MAC key.
+************************************************************/
+
+BOOL cli_simple_set_signing(struct cli_state *cli,
+ const DATA_BLOB user_session_key,
+ const DATA_BLOB response)
+{
+ struct smb_basic_signing_context *data;
+
+ if (!user_session_key.length)
+ return False;
+
+ if (!cli_set_smb_signing_common(cli)) {
+ return False;
+ }
+
+ if (!set_smb_signing_real_common(&cli->sign_info)) {
+ return False;
+ }
+
+ data = smb_xmalloc(sizeof(*data));
+ memset(data, '\0', sizeof(*data));
+
+ cli->sign_info.signing_context = data;
+
+ data->mac_key = data_blob(NULL, response.length + user_session_key.length);
+
+ memcpy(&data->mac_key.data[0], user_session_key.data, user_session_key.length);
+
+ DEBUG(10, ("cli_simple_set_signing: user_session_key\n"));
+ dump_data(10, (const char *)user_session_key.data, user_session_key.length);
+
+ if (response.length) {
+ memcpy(&data->mac_key.data[user_session_key.length],response.data, response.length);
+ DEBUG(10, ("cli_simple_set_signing: response_data\n"));
+ dump_data(10, (const char *)response.data, response.length);
+ } else {
+ DEBUG(10, ("cli_simple_set_signing: NULL response_data\n"));
+ }
+
+ dump_data_pw("MAC ssession key is:\n", data->mac_key.data, data->mac_key.length);
+
+ /* Initialise the sequence number */
+ data->send_seq_num = 0;
+
+ /* Initialise the list of outstanding packets */
+ data->outstanding_packet_list = NULL;
+
+ cli->sign_info.sign_outgoing_message = client_sign_outgoing_message;
+ cli->sign_info.check_incoming_message = client_check_incoming_message;
+ cli->sign_info.free_signing_context = simple_free_signing_context;
+
+ return True;
+}
+
+/***********************************************************
+ Tell client code we are in a multiple trans reply state.
+ We call this after the last outgoing trans2 packet (which
+ has incremented the sequence numbers), so we must save the
+ current mid and sequence number -2.
+************************************************************/
+
+void cli_signing_trans_start(struct cli_state *cli, uint16 mid)
+{
+ struct smb_basic_signing_context *data = cli->sign_info.signing_context;
+
+ if (!cli->sign_info.doing_signing || !data)
+ return;
+
+ data->trans_info = smb_xmalloc(sizeof(struct trans_info_context));
+ ZERO_STRUCTP(data->trans_info);
+
+ data->trans_info->send_seq_num = data->send_seq_num-2;
+ data->trans_info->mid = mid;
+ data->trans_info->reply_seq_num = data->send_seq_num-1;
+
+ DEBUG(10,("cli_signing_trans_start: storing mid = %u, reply_seq_num = %u, send_seq_num = %u \
+data->send_seq_num = %u\n",
+ (unsigned int)data->trans_info->mid,
+ (unsigned int)data->trans_info->reply_seq_num,
+ (unsigned int)data->trans_info->send_seq_num,
+ (unsigned int)data->send_seq_num ));
+}
+
+/***********************************************************
+ Tell client code we are out of a multiple trans reply state.
+************************************************************/
+
+void cli_signing_trans_stop(struct cli_state *cli)
+{
+ struct smb_basic_signing_context *data = cli->sign_info.signing_context;
+
+ if (!cli->sign_info.doing_signing || !data)
+ return;
+
+ DEBUG(10,("cli_signing_trans_stop: freeing mid = %u, reply_seq_num = %u, send_seq_num = %u \
+data->send_seq_num = %u\n",
+ (unsigned int)data->trans_info->mid,
+ (unsigned int)data->trans_info->reply_seq_num,
+ (unsigned int)data->trans_info->send_seq_num,
+ (unsigned int)data->send_seq_num ));
+
+ SAFE_FREE(data->trans_info);
+ data->trans_info = NULL;
+}
+
+/***********************************************************
+ SMB signing - TEMP implementation - calculate a MAC to send.
+************************************************************/
+
+static void temp_sign_outgoing_message(char *outbuf, struct smb_sign_info *si)
+{
+ /* mark the packet as signed - BEFORE we sign it...*/
+ mark_packet_signed(outbuf);
+
+ /* I wonder what BSRSPYL stands for - but this is what MS
+ actually sends! */
+ memcpy(&outbuf[smb_ss_field], "BSRSPYL ", 8);
+ return;
+}
+
+/***********************************************************
+ SMB signing - TEMP implementation - check a MAC sent by server.
+************************************************************/
+
+static BOOL temp_check_incoming_message(char *inbuf, struct smb_sign_info *si, BOOL expected_ok)
+{
+ return True;
+}
+
+/***********************************************************
+ SMB signing - TEMP implementation - free signing context
+************************************************************/
+
+static void temp_free_signing_context(struct smb_sign_info *si)
+{
+ return;
+}
+
+/***********************************************************
+ SMB signing - NULL implementation - setup the MAC key.
+************************************************************/
+
+BOOL cli_null_set_signing(struct cli_state *cli)
+{
+ return null_set_signing(&cli->sign_info);
+}
+
+/***********************************************************
+ SMB signing - temp implementation - setup the MAC key.
+************************************************************/
+
+BOOL cli_temp_set_signing(struct cli_state *cli)
+{
+ if (!cli_set_smb_signing_common(cli)) {
+ return False;
+ }
+
+ cli->sign_info.signing_context = NULL;
+
+ cli->sign_info.sign_outgoing_message = temp_sign_outgoing_message;
+ cli->sign_info.check_incoming_message = temp_check_incoming_message;
+ cli->sign_info.free_signing_context = temp_free_signing_context;
+
+ return True;
+}
+
+void cli_free_signing_context(struct cli_state *cli)
+{
+ free_signing_context(&cli->sign_info);
+}
+
+/**
+ * Sign a packet with the current mechanism
+ */
+
+void cli_calculate_sign_mac(struct cli_state *cli)
+{
+ cli->sign_info.sign_outgoing_message(cli->outbuf, &cli->sign_info);
+}
+
+/**
+ * Check a packet with the current mechanism
+ * @return False if we had an established signing connection
+ * which had a bad checksum, True otherwise.
+ */
+
+BOOL cli_check_sign_mac(struct cli_state *cli, BOOL expected_ok)
+{
+ if (!cli->sign_info.check_incoming_message(cli->inbuf, &cli->sign_info, expected_ok)) {
+ free_signing_context(&cli->sign_info);
+ return False;
+ }
+ return True;
+}
+
+/***********************************************************
+ SMB signing - Server implementation - send the MAC.
+************************************************************/
+
+static void srv_sign_outgoing_message(char *outbuf, struct smb_sign_info *si)
+{
+ unsigned char calc_md5_mac[16];
+ struct smb_basic_signing_context *data = si->signing_context;
+ uint32 send_seq_number = data->send_seq_num;
+ BOOL was_deferred_packet = False;
+ uint16 mid;
+
+ if (!si->doing_signing) {
+ return;
+ }
+
+ /* JRA Paranioa test - we should be able to get rid of this... */
+ if (smb_len(outbuf) < (smb_ss_field + 8 - 4)) {
+ DEBUG(1, ("srv_sign_outgoing_message: Logic error. Can't send signature on short packet! smb_len = %u\n",
+ smb_len(outbuf) ));
+ abort();
+ }
+
+ /* mark the packet as signed - BEFORE we sign it...*/
+ mark_packet_signed(outbuf);
+
+ mid = SVAL(outbuf, smb_mid);
+
+ /* See if this is a reply for a deferred packet. */
+ was_deferred_packet = get_sequence_for_reply(&data->outstanding_packet_list, mid, &send_seq_number);
+
+ if (data->trans_info && (data->trans_info->mid == mid)) {
+ /* This is a reply in a trans stream. Use the sequence
+ * number associated with the stream mid. */
+ send_seq_number = data->trans_info->send_seq_num;
+ }
+
+ simple_packet_signature(data, (const unsigned char *)outbuf, send_seq_number, calc_md5_mac);
+
+ DEBUG(10, ("srv_sign_outgoing_message: seq %u: sent SMB signature of\n", (unsigned int)send_seq_number));
+ dump_data(10, (const char *)calc_md5_mac, 8);
+
+ memcpy(&outbuf[smb_ss_field], calc_md5_mac, 8);
+
+/* cli->outbuf[smb_ss_field+2]=0;
+ Uncomment this to test if the remote client actually verifies signatures...*/
+
+ /* Don't mess with the sequence number for a deferred packet. */
+ if (was_deferred_packet) {
+ return;
+ }
+
+ if (!data->trans_info) {
+ /* Always increment if not in a trans stream. */
+ data->send_seq_num++;
+ } else if ((data->trans_info->send_seq_num == data->send_seq_num) || (data->trans_info->mid != mid)) {
+ /* Increment if this is the first reply in a trans stream or a
+ * packet that doesn't belong to this stream (different mid). */
+ data->send_seq_num++;
+ }
+}
+
+/***********************************************************
+ Is an incoming packet an oplock break reply ?
+************************************************************/
+
+static BOOL is_oplock_break(char *inbuf)
+{
+ if (CVAL(inbuf,smb_com) != SMBlockingX)
+ return False;
+
+ if (!(CVAL(inbuf,smb_vwv3) & LOCKING_ANDX_OPLOCK_RELEASE))
+ return False;
+
+ DEBUG(10,("is_oplock_break: Packet is oplock break\n"));
+ return True;
+}
+
+/***********************************************************
+ SMB signing - Server implementation - check a MAC sent by server.
+************************************************************/
+
+static BOOL srv_check_incoming_message(char *inbuf, struct smb_sign_info *si, BOOL expected_ok)
+{
+ BOOL good;
+ struct smb_basic_signing_context *data = si->signing_context;
+ uint32 reply_seq_number = data->send_seq_num;
+ uint32 saved_seq;
+ unsigned char calc_md5_mac[16];
+ unsigned char *server_sent_mac;
+ uint mid;
+
+ if (!si->doing_signing)
+ return True;
+
+ if (smb_len(inbuf) < (smb_ss_field + 8 - 4)) {
+ DEBUG(1, ("srv_check_incoming_message: Can't check signature on short packet! smb_len = %u\n", smb_len(inbuf)));
+ return False;
+ }
+
+ mid = SVAL(inbuf, smb_mid);
+
+ /* Is this part of a trans stream ? */
+ if (data->trans_info && (data->trans_info->mid == mid)) {
+ /* If so we don't increment the sequence. */
+ reply_seq_number = data->trans_info->reply_seq_num;
+ } else {
+ /* We always increment the sequence number. */
+ data->send_seq_num++;
+
+ /* If we get an asynchronous oplock break reply and there
+ * isn't a reply pending we need to re-sync the sequence
+ * number.
+ */
+ if (is_oplock_break(inbuf)) {
+ DEBUG(10,("srv_check_incoming_message: oplock break at seq num %u\n", data->send_seq_num));
+ data->send_seq_num++;
+ }
+ }
+
+ saved_seq = reply_seq_number;
+ simple_packet_signature(data, (const unsigned char *)inbuf, reply_seq_number, calc_md5_mac);
+
+ server_sent_mac = (unsigned char *)&inbuf[smb_ss_field];
+ good = (memcmp(server_sent_mac, calc_md5_mac, 8) == 0);
+
+ if (!good) {
+
+ DEBUG(5, ("srv_check_incoming_message: BAD SIG: seq %u wanted SMB signature of\n",
+ (unsigned int)saved_seq));
+ dump_data(5, (const char *)calc_md5_mac, 8);
+
+ DEBUG(5, ("srv_check_incoming_message: BAD SIG: seq %u got SMB signature of\n",
+ (unsigned int)saved_seq));
+ dump_data(5, (const char *)server_sent_mac, 8);
+
+#if 1 /* JRATEST */
+ {
+ int i;
+ reply_seq_number -= 5;
+ for (i = 0; i < 10; i++, reply_seq_number++) {
+ simple_packet_signature(data, (const unsigned char *)inbuf, reply_seq_number, calc_md5_mac);
+ if (memcmp(server_sent_mac, calc_md5_mac, 8) == 0) {
+ DEBUG(0,("srv_check_incoming_message: out of seq. seq num %u matches. \
+We were expecting seq %u\n", reply_seq_number, saved_seq ));
+ break;
+ }
+ }
+ }
+#endif /* JRATEST */
+
+ } else {
+ DEBUG(10, ("srv_check_incoming_message: seq %u: (current is %u) got good SMB signature of\n", (unsigned int)reply_seq_number, (unsigned int)data->send_seq_num));
+ dump_data(10, (const char *)server_sent_mac, 8);
+ }
+
+ return (signing_good(inbuf, si, good, saved_seq, expected_ok));
+}
+
+/***********************************************************
+ SMB signing - server API's.
+************************************************************/
+
+static struct smb_sign_info srv_sign_info = {
+ null_sign_outgoing_message,
+ null_check_incoming_message,
+ null_free_signing_context,
+ NULL,
+ False,
+ False,
+ False,
+ False
+};
+
+/***********************************************************
+ Turn signing off or on for oplock break code.
+************************************************************/
+
+BOOL srv_oplock_set_signing(BOOL onoff)
+{
+ BOOL ret = srv_sign_info.doing_signing;
+ srv_sign_info.doing_signing = onoff;
+ return ret;
+}
+
+/***********************************************************
+ Called to validate an incoming packet from the client.
+************************************************************/
+
+BOOL srv_check_sign_mac(char *inbuf, BOOL expected_ok)
+{
+ /* Check if it's a session keepalive. */
+ if(CVAL(inbuf,0) == SMBkeepalive)
+ return True;
+
+ return srv_sign_info.check_incoming_message(inbuf, &srv_sign_info, expected_ok);
+}
+
+/***********************************************************
+ Called to sign an outgoing packet to the client.
+************************************************************/
+
+void srv_calculate_sign_mac(char *outbuf)
+{
+ /* Check if it's a session keepalive. */
+ /* JRA Paranioa test - do we ever generate these in the server ? */
+ if(CVAL(outbuf,0) == SMBkeepalive)
+ return;
+
+ srv_sign_info.sign_outgoing_message(outbuf, &srv_sign_info);
+}
+
+/***********************************************************
+ Called by server to defer an outgoing packet.
+************************************************************/
+
+void srv_defer_sign_response(uint16 mid)
+{
+ struct smb_basic_signing_context *data;
+
+ if (!srv_sign_info.doing_signing)
+ return;
+
+ data = (struct smb_basic_signing_context *)srv_sign_info.signing_context;
+
+ if (!data)
+ return;
+
+ store_sequence_for_reply(&data->outstanding_packet_list,
+ mid, data->send_seq_num);
+ data->send_seq_num++;
+}
+
+/***********************************************************
+ Called to remove sequence records when a deferred packet is
+ cancelled by mid. This should never find one....
+************************************************************/
+
+void srv_cancel_sign_response(uint16 mid)
+{
+ struct smb_basic_signing_context *data;
+ uint32 dummy_seq;
+
+ if (!srv_sign_info.doing_signing)
+ return;
+
+ data = (struct smb_basic_signing_context *)srv_sign_info.signing_context;
+
+ if (!data)
+ return;
+
+ DEBUG(10,("srv_cancel_sign_response: for mid %u\n", (unsigned int)mid ));
+
+ while (get_sequence_for_reply(&data->outstanding_packet_list, mid, &dummy_seq))
+ ;
+}
+
+/***********************************************************
+ Called by server negprot when signing has been negotiated.
+************************************************************/
+
+void srv_set_signing_negotiated(void)
+{
+ srv_sign_info.allow_smb_signing = True;
+ srv_sign_info.negotiated_smb_signing = True;
+ if (lp_server_signing() == Required)
+ srv_sign_info.mandatory_signing = True;
+
+ srv_sign_info.sign_outgoing_message = temp_sign_outgoing_message;
+ srv_sign_info.check_incoming_message = temp_check_incoming_message;
+ srv_sign_info.free_signing_context = temp_free_signing_context;
+}
+
+/***********************************************************
+ Returns whether signing is active. We can't use sendfile or raw
+ reads/writes if it is.
+************************************************************/
+
+BOOL srv_is_signing_active(void)
+{
+ return srv_sign_info.doing_signing;
+}
+
+
+/***********************************************************
+ Returns whether signing is negotiated. We can't use it unless it was
+ in the negprot.
+************************************************************/
+
+BOOL srv_is_signing_negotiated(void)
+{
+ return srv_sign_info.negotiated_smb_signing;
+}
+
+/***********************************************************
+ Returns whether signing is negotiated. We can't use it unless it was
+ in the negprot.
+************************************************************/
+
+BOOL srv_signing_started(void)
+{
+ struct smb_basic_signing_context *data;
+
+ if (!srv_sign_info.doing_signing) {
+ return False;
+ }
+
+ data = (struct smb_basic_signing_context *)srv_sign_info.signing_context;
+ if (!data)
+ return False;
+
+ if (data->send_seq_num == 0) {
+ return False;
+ }
+
+ return True;
+}
+
+
+/***********************************************************
+ Tell server code we are in a multiple trans reply state.
+************************************************************/
+
+void srv_signing_trans_start(uint16 mid)
+{
+ struct smb_basic_signing_context *data;
+
+ if (!srv_sign_info.doing_signing)
+ return;
+
+ data = (struct smb_basic_signing_context *)srv_sign_info.signing_context;
+ if (!data)
+ return;
+
+ data->trans_info = smb_xmalloc(sizeof(struct trans_info_context));
+ ZERO_STRUCTP(data->trans_info);
+
+ data->trans_info->reply_seq_num = data->send_seq_num-1;
+ data->trans_info->mid = mid;
+ data->trans_info->send_seq_num = data->send_seq_num;
+
+ DEBUG(10,("srv_signing_trans_start: storing mid = %u, reply_seq_num = %u, send_seq_num = %u \
+data->send_seq_num = %u\n",
+ (unsigned int)mid,
+ (unsigned int)data->trans_info->reply_seq_num,
+ (unsigned int)data->trans_info->send_seq_num,
+ (unsigned int)data->send_seq_num ));
+}
+
+/***********************************************************
+ Tell server code we are out of a multiple trans reply state.
+************************************************************/
+
+void srv_signing_trans_stop(void)
+{
+ struct smb_basic_signing_context *data;
+
+ if (!srv_sign_info.doing_signing)
+ return;
+
+ data = (struct smb_basic_signing_context *)srv_sign_info.signing_context;
+ if (!data || !data->trans_info)
+ return;
+
+ DEBUG(10,("srv_signing_trans_stop: removing mid = %u, reply_seq_num = %u, send_seq_num = %u \
+data->send_seq_num = %u\n",
+ (unsigned int)data->trans_info->mid,
+ (unsigned int)data->trans_info->reply_seq_num,
+ (unsigned int)data->trans_info->send_seq_num,
+ (unsigned int)data->send_seq_num ));
+
+ SAFE_FREE(data->trans_info);
+ data->trans_info = NULL;
+}
+
+/***********************************************************
+ Turn on signing from this packet onwards.
+************************************************************/
+
+void srv_set_signing(const DATA_BLOB user_session_key, const DATA_BLOB response)
+{
+ struct smb_basic_signing_context *data;
+
+ if (!user_session_key.length)
+ return;
+
+ if (!srv_sign_info.negotiated_smb_signing && !srv_sign_info.mandatory_signing) {
+ DEBUG(5,("srv_set_signing: signing negotiated = %u, mandatory_signing = %u. Not allowing smb signing.\n",
+ (unsigned int)srv_sign_info.negotiated_smb_signing,
+ (unsigned int)srv_sign_info.mandatory_signing ));
+ return;
+ }
+
+ /* Once we've turned on, ignore any more sessionsetups. */
+ if (srv_sign_info.doing_signing) {
+ return;
+ }
+
+ if (srv_sign_info.free_signing_context)
+ srv_sign_info.free_signing_context(&srv_sign_info);
+
+ srv_sign_info.doing_signing = True;
+
+ data = smb_xmalloc(sizeof(*data));
+ memset(data, '\0', sizeof(*data));
+
+ srv_sign_info.signing_context = data;
+
+ data->mac_key = data_blob(NULL, response.length + user_session_key.length);
+
+ memcpy(&data->mac_key.data[0], user_session_key.data, user_session_key.length);
+ if (response.length)
+ memcpy(&data->mac_key.data[user_session_key.length],response.data, response.length);
+
+ dump_data_pw("MAC ssession key is:\n", data->mac_key.data, data->mac_key.length);
+
+ DEBUG(3,("srv_set_signing: turning on SMB signing: signing negotiated = %s, mandatory_signing = %s.\n",
+ BOOLSTR(srv_sign_info.negotiated_smb_signing),
+ BOOLSTR(srv_sign_info.mandatory_signing) ));
+
+ /* Initialise the sequence number */
+ data->send_seq_num = 0;
+
+ /* Initialise the list of outstanding packets */
+ data->outstanding_packet_list = NULL;
+
+ srv_sign_info.sign_outgoing_message = srv_sign_outgoing_message;
+ srv_sign_info.check_incoming_message = srv_check_incoming_message;
+ srv_sign_info.free_signing_context = simple_free_signing_context;
+}
diff --git a/source/libsmb/smbdes.c b/source/libsmb/smbdes.c
new file mode 100644
index 00000000000..ae946b4a660
--- /dev/null
+++ b/source/libsmb/smbdes.c
@@ -0,0 +1,455 @@
+/*
+ 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 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"
+
+/* 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)
+*/
+
+
+#define uchar unsigned char
+
+static const uchar 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 uchar 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 uchar 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 uchar 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 uchar 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 uchar 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 uchar sc[16] = {1, 1, 2, 2, 2, 2, 2, 2, 1, 2, 2, 2, 2, 2, 2, 1};
+
+static const uchar 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 uchar *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 unsigned char *str,unsigned char *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);
+ }
+}
+
+
+static void smbhash(unsigned char *out, const unsigned char *in, const unsigned char *key, int forw)
+{
+ int i;
+ char outb[64];
+ char inb[64];
+ char keyb[64];
+ unsigned char 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 unsigned char *p14,unsigned char *p16)
+{
+ unsigned char sp8[8] = {0x4b, 0x47, 0x53, 0x21, 0x40, 0x23, 0x24, 0x25};
+ smbhash(p16, sp8, p14, 1);
+ smbhash(p16+8, sp8, p14+7, 1);
+}
+
+void E_P24(const unsigned char *p21, const unsigned char *c8, unsigned char *p24)
+{
+ smbhash(p24, c8, p21, 1);
+ smbhash(p24+8, c8, p21+7, 1);
+ smbhash(p24+16, c8, p21+14, 1);
+}
+
+void D_P16(const unsigned char *p14, const unsigned char *in, unsigned char *out)
+{
+ smbhash(out, in, p14, 0);
+ smbhash(out+8, in+8, p14+7, 0);
+}
+
+void E_old_pw_hash( unsigned char *p14, const unsigned char *in, unsigned char *out)
+{
+ smbhash(out, in, p14, 1);
+ smbhash(out+8, in+8, p14+7, 1);
+}
+
+void cred_hash1(unsigned char *out, const unsigned char *in, const unsigned char *key)
+{
+ unsigned char buf[8];
+
+ smbhash(buf, in, key, 1);
+ smbhash(out, buf, key+9, 1);
+}
+
+void cred_hash2(unsigned char *out, const unsigned char *in, const unsigned char *key)
+{
+ unsigned char buf[8];
+ static unsigned char key2[8];
+
+ smbhash(buf, in, key, 1);
+ key2[0] = key[7];
+ smbhash(out, buf, key2, 1);
+}
+
+void cred_hash3(unsigned char *out, unsigned char *in, const unsigned char *key, int forw)
+{
+ static unsigned char key2[8];
+
+ smbhash(out, in, key, forw);
+ key2[0] = key[7];
+ smbhash(out + 8, in + 8, key2, forw);
+}
+
+void SamOEMhash( unsigned char *data, const unsigned char *key, int val)
+{
+ unsigned char s_box[256];
+ unsigned char index_i = 0;
+ unsigned char index_j = 0;
+ unsigned char j = 0;
+ int ind;
+
+ for (ind = 0; ind < 256; ind++)
+ {
+ s_box[ind] = (unsigned char)ind;
+ }
+
+ for( ind = 0; ind < 256; ind++)
+ {
+ unsigned char tc;
+
+ j += (s_box[ind] + key[ind%16]);
+
+ tc = s_box[ind];
+ s_box[ind] = s_box[j];
+ s_box[j] = tc;
+ }
+ for( ind = 0; ind < val; ind++)
+ {
+ unsigned char tc;
+ unsigned char t;
+
+ index_i++;
+ index_j += s_box[index_i];
+
+ tc = s_box[index_i];
+ s_box[index_i] = s_box[index_j];
+ s_box[index_j] = tc;
+
+ t = s_box[index_i] + s_box[index_j];
+ data[ind] = data[ind] ^ s_box[t];
+ }
+}
+
+void SamOEMhashBlob( unsigned char *data, int len, DATA_BLOB *key)
+{
+ unsigned char s_box[256];
+ unsigned char index_i = 0;
+ unsigned char index_j = 0;
+ unsigned char j = 0;
+ int ind;
+
+ for (ind = 0; ind < 256; ind++)
+ {
+ s_box[ind] = (unsigned char)ind;
+ }
+
+ for( ind = 0; ind < 256; ind++)
+ {
+ unsigned char tc;
+
+ j += (s_box[ind] + key->data[ind%key->length]);
+
+ tc = s_box[ind];
+ s_box[ind] = s_box[j];
+ s_box[j] = tc;
+ }
+ for( ind = 0; ind < len; ind++)
+ {
+ unsigned char tc;
+ unsigned char t;
+
+ index_i++;
+ index_j += s_box[index_i];
+
+ tc = s_box[index_i];
+ s_box[index_i] = s_box[index_j];
+ s_box[index_j] = tc;
+
+ t = s_box[index_i] + s_box[index_j];
+ data[ind] = data[ind] ^ s_box[t];
+ }
+}
+
+/* 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_pwd_hash(unsigned int rid, const uchar *in, uchar *out, int forw)
+{
+ uchar s[14];
+
+ s[0] = s[4] = s[8] = s[12] = (uchar)(rid & 0xFF);
+ s[1] = s[5] = s[9] = s[13] = (uchar)((rid >> 8) & 0xFF);
+ s[2] = s[6] = s[10] = (uchar)((rid >> 16) & 0xFF);
+ s[3] = s[7] = s[11] = (uchar)((rid >> 24) & 0xFF);
+
+ smbhash(out, in, s, forw);
+ smbhash(out+8, in+8, s+7, forw);
+}
diff --git a/source/libsmb/smbencrypt.c b/source/libsmb/smbencrypt.c
new file mode 100644
index 00000000000..c5acedae51c
--- /dev/null
+++ b/source/libsmb/smbencrypt.c
@@ -0,0 +1,526 @@
+/*
+ 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 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 "byteorder.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 */
+void SMBencrypt(const char *passwd, const uchar *c8, uchar p24[24])
+{
+ uchar p21[21];
+
+ memset(p21,'\0',21);
+ E_deshash(passwd, p21);
+
+ SMBOWFencrypt(p21, c8, p24);
+
+#ifdef DEBUG_PASSWORD
+ DEBUG(100,("SMBencrypt: lm#, challenge, response\n"));
+ dump_data(100, (char *)p21, 16);
+ dump_data(100, (const char *)c8, 8);
+ dump_data(100, (char *)p24, 24);
+#endif
+}
+
+/**
+ * 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
+ */
+
+void E_md4hash(const char *passwd, uchar p16[16])
+{
+ int len;
+ smb_ucs2_t wpwd[129];
+
+ /* Password must be converted to NT unicode - null terminated. */
+ push_ucs2(NULL, wpwd, (const char *)passwd, 256, STR_UNICODE|STR_NOALIGN|STR_TERMINATE);
+ /* Calculate length in bytes */
+ len = strlen_w(wpwd) * sizeof(int16);
+
+ mdfour(p16, (unsigned char *)wpwd, len);
+ ZERO_STRUCT(wpwd);
+}
+
+/**
+ * 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, uchar p16[16])
+{
+ BOOL ret = True;
+ fstring dospwd;
+ ZERO_STRUCT(dospwd);
+
+ /* Password must be converted to DOS charset - null terminated, uppercase. */
+ push_ascii(dospwd, passwd, sizeof(dospwd), STR_UPPER|STR_TERMINATE);
+
+ /* Only the fisrt 14 chars are considered, password need not be null terminated. */
+ E_P16((const unsigned char *)dospwd, p16);
+
+ if (strlen(dospwd) > 14) {
+ ret = False;
+ }
+
+ ZERO_STRUCT(dospwd);
+
+ return ret;
+}
+
+/**
+ * Creates the MD4 and DES (LM) Hash of the users password.
+ * MD4 is of the NT Unicode, DES is of the DOS UPPERCASE password.
+ * @param passwd password in 'unix' charset.
+ * @param nt_p16 return password hashed with md4, caller allocated 16 byte buffer
+ * @param p16 return password hashed with des, caller allocated 16 byte buffer
+ */
+
+/* Does both the NT and LM owfs of a user's password */
+void nt_lm_owf_gen(const char *pwd, uchar nt_p16[16], uchar p16[16])
+{
+ /* Calculate the MD4 hash (NT compatible) of the password */
+ memset(nt_p16, '\0', 16);
+ E_md4hash(pwd, nt_p16);
+
+#ifdef DEBUG_PASSWORD
+ DEBUG(100,("nt_lm_owf_gen: pwd, nt#\n"));
+ dump_data(120, pwd, strlen(pwd));
+ dump_data(100, (char *)nt_p16, 16);
+#endif
+
+ E_deshash(pwd, (uchar *)p16);
+
+#ifdef DEBUG_PASSWORD
+ DEBUG(100,("nt_lm_owf_gen: pwd, lm#\n"));
+ dump_data(120, pwd, strlen(pwd));
+ dump_data(100, (char *)p16, 16);
+#endif
+}
+
+/* Does both the NTLMv2 owfs of a user's password */
+BOOL ntv2_owf_gen(const uchar owf[16],
+ const char *user_in, const char *domain_in, uchar kr_buf[16])
+{
+ smb_ucs2_t *user;
+ smb_ucs2_t *domain;
+
+ size_t user_byte_len;
+ size_t domain_byte_len;
+
+ HMACMD5Context ctx;
+
+ user_byte_len = push_ucs2_allocate(&user, user_in);
+ if (user_byte_len == (size_t)-1) {
+ DEBUG(0, ("push_uss2_allocate() for user returned -1 (probably malloc() failure)\n"));
+ return False;
+ }
+
+ domain_byte_len = push_ucs2_allocate(&domain, domain_in);
+ if (domain_byte_len == (size_t)-1) {
+ DEBUG(0, ("push_uss2_allocate() for domain returned -1 (probably malloc() failure)\n"));
+ return False;
+ }
+
+ strupper_w(user);
+ strupper_w(domain);
+
+ 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 unsigned char *)user, user_byte_len, &ctx);
+ hmac_md5_update((const unsigned char *)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 char *)user, user_byte_len);
+ dump_data(100, (const char *)domain, domain_byte_len);
+ dump_data(100, owf, 16);
+ dump_data(100, kr_buf, 16);
+#endif
+
+ SAFE_FREE(user);
+ SAFE_FREE(domain);
+ return True;
+}
+
+/* Does the des encryption from the NT or LM MD4 hash. */
+void SMBOWFencrypt(const uchar passwd[16], const uchar *c8, uchar p24[24])
+{
+ uchar p21[21];
+
+ ZERO_STRUCT(p21);
+
+ memcpy(p21, passwd, 16);
+ E_P24(p21, c8, p24);
+}
+
+/* Does the des encryption from the FIRST 8 BYTES of the NT or LM MD4 hash. */
+void NTLMSSPOWFencrypt(const uchar passwd[8], const uchar *ntlmchalresp, uchar p24[24])
+{
+ uchar p21[21];
+
+ memset(p21,'\0',21);
+ memcpy(p21, passwd, 8);
+ memset(p21 + 8, 0xbd, 8);
+
+ E_P24(p21, ntlmchalresp, p24);
+#ifdef DEBUG_PASSWORD
+ DEBUG(100,("NTLMSSPOWFencrypt: p21, c8, p24\n"));
+ dump_data(100, (char *)p21, 21);
+ dump_data(100, (const char *)ntlmchalresp, 8);
+ dump_data(100, (char *)p24, 24);
+#endif
+}
+
+
+/* Does the NT MD4 hash then des encryption. */
+
+void SMBNTencrypt(const char *passwd, uchar *c8, uchar *p24)
+{
+ uchar 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, (char *)p21, 16);
+ dump_data(100, (char *)c8, 8);
+ dump_data(100, (char *)p24, 24);
+#endif
+}
+
+/* Does the md5 encryption from the Key Response for NTLMv2. */
+void SMBOWFencrypt_ntv2(const uchar kr[16],
+ const DATA_BLOB *srv_chal,
+ const DATA_BLOB *cli_chal,
+ uchar 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(cli_chal->data, cli_chal->length, &ctx);
+ hmac_md5_final(resp_buf, &ctx);
+
+#ifdef DEBUG_PASSWORD
+ DEBUG(100, ("SMBOWFencrypt_ntv2: srv_chal, cli_chal, resp_buf\n"));
+ dump_data(100, srv_chal->data, srv_chal->length);
+ dump_data(100, cli_chal->data, cli_chal->length);
+ dump_data(100, resp_buf, 16);
+#endif
+}
+
+void SMBsesskeygen_ntv2(const uchar kr[16],
+ const uchar * nt_resp, uint8 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((unsigned char *)sess_key, &ctx);
+
+#ifdef DEBUG_PASSWORD
+ DEBUG(100, ("SMBsesskeygen_ntv2:\n"));
+ dump_data(100, sess_key, 16);
+#endif
+}
+
+void SMBsesskeygen_ntv1(const uchar kr[16],
+ const uchar * nt_resp, uint8 sess_key[16])
+{
+ /* yes, this session key does not change - yes, this
+ is a problem - but it is 128 bits */
+
+ mdfour((unsigned char *)sess_key, kr, 16);
+
+#ifdef DEBUG_PASSWORD
+ DEBUG(100, ("SMBsesskeygen_ntv1:\n"));
+ dump_data(100, sess_key, 16);
+#endif
+}
+
+void SMBsesskeygen_lmv1(const uchar lm_hash[16],
+ const uchar lm_resp[24], /* only uses 8 */
+ uint8 sess_key[16])
+{
+ /* Calculate the LM session key (effective length 40 bits,
+ but changes with each session) */
+
+ uchar p24[24];
+ uchar partial_lm_hash[16];
+
+ memcpy(partial_lm_hash, lm_hash, 8);
+ memset(partial_lm_hash + 8, 0xbd, 8);
+
+ SMBOWFencrypt(lm_hash, lm_resp, p24);
+
+ memcpy(sess_key, p24, 16);
+ sess_key[5] = 0xe5;
+ sess_key[6] = 0x38;
+ sess_key[7] = 0xb0;
+
+#ifdef DEBUG_PASSWORD
+ DEBUG(100, ("SMBsesskeygen_lmv1:\n"));
+ dump_data(100, sess_key, 16);
+#endif
+}
+
+void SMBsesskeygen_lm_sess_key(const uchar lm_hash[16],
+ const uchar lm_resp[24], /* only uses 8 */
+ uint8 sess_key[16])
+{
+ uchar p24[24];
+ uchar partial_lm_hash[16];
+
+ memcpy(partial_lm_hash, lm_hash, 8);
+ memset(partial_lm_hash + 8, 0xbd, 8);
+
+ SMBOWFencrypt(partial_lm_hash, lm_resp, p24);
+
+ memcpy(sess_key, p24, 16);
+
+#ifdef DEBUG_PASSWORD
+ DEBUG(100, ("SMBsesskeygen_lmv1_jerry:\n"));
+ dump_data(100, sess_key, 16);
+#endif
+}
+
+DATA_BLOB NTLMv2_generate_names_blob(const char *hostname,
+ const char *domain)
+{
+ DATA_BLOB names_blob = data_blob(NULL, 0);
+
+ msrpc_gen(&names_blob, "aaa",
+ NTLMSSP_NAME_TYPE_DOMAIN, domain,
+ NTLMSSP_NAME_TYPE_SERVER, hostname,
+ 0, "");
+ return names_blob;
+}
+
+static DATA_BLOB NTLMv2_generate_client_data(const DATA_BLOB *names_blob)
+{
+ uchar client_chal[8];
+ DATA_BLOB response = data_blob(NULL, 0);
+ char long_date[8];
+
+ generate_random_buffer(client_chal, sizeof(client_chal), False);
+
+ put_long_date(long_date, time(NULL));
+
+ /* See http://www.ubiqx.org/cifs/SMB.html#SMB.8.5 */
+
+ msrpc_gen(&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(const uchar ntlm_v2_hash[16],
+ const DATA_BLOB *server_chal,
+ const DATA_BLOB *names_blob)
+{
+ uchar ntlmv2_response[16];
+ DATA_BLOB ntlmv2_client_data;
+ DATA_BLOB final_response;
+
+ /* 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(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(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);
+
+ data_blob_free(&ntlmv2_client_data);
+
+ return final_response;
+}
+
+static DATA_BLOB LMv2_generate_response(const uchar ntlm_v2_hash[16],
+ const DATA_BLOB *server_chal)
+{
+ uchar lmv2_response[16];
+ DATA_BLOB lmv2_client_data = data_blob(NULL, 8);
+ DATA_BLOB final_response = data_blob(NULL, 24);
+
+ /* LMv2 */
+ /* client-supplied random data */
+ generate_random_buffer(lmv2_client_data.data, lmv2_client_data.length, False);
+
+ /* 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(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 *nt_session_key)
+{
+ uchar nt_hash[16];
+ uchar ntlm_v2_hash[16];
+ E_md4hash(password, nt_hash);
+
+ /* 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, ntlm_v2_hash)) {
+ return False;
+ }
+
+ if (nt_response) {
+ *nt_response = NTLMv2_generate_response(ntlm_v2_hash, server_chal,
+ names_blob);
+ if (nt_session_key) {
+ *nt_session_key = data_blob(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, nt_session_key->data);
+ }
+ }
+
+ /* LMv2 */
+
+ if (lm_response) {
+ *lm_response = LMv2_generate_response(ntlm_v2_hash, server_chal);
+ }
+
+ return True;
+}
+
+/***********************************************************
+ 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(char buffer[516], const char *password, int string_flags)
+{
+ uchar new_pw[512];
+ size_t new_pw_len;
+
+ new_pw_len = push_string(NULL, new_pw,
+ password,
+ sizeof(new_pw), string_flags);
+
+ memcpy(&buffer[512 - new_pw_len], new_pw, new_pw_len);
+
+ generate_random_buffer((unsigned char *)buffer, 512 - new_pw_len, True);
+
+ /*
+ * 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(char in_buffer[516], char *new_pwrd,
+ int new_pwrd_size, uint32 *new_pw_len,
+ int string_flags)
+{
+ int byte_len=0;
+
+ /*
+ 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)) {
+ DEBUG(0, ("decode_pw_buffer: incorrect password length (%d).\n", byte_len));
+ DEBUG(0, ("decode_pw_buffer: check that 'encrypt passwords = yes'\n"));
+ return False;
+ }
+
+ /* decode into the return buffer. Buffer length supplied */
+ *new_pw_len = pull_string(NULL, new_pwrd, &in_buffer[512 - byte_len], new_pwrd_size,
+ byte_len, string_flags);
+
+#ifdef DEBUG_PASSWORD
+ DEBUG(100,("decode_pw_buffer: new_pwrd: "));
+ dump_data(100, (char *)new_pwrd, *new_pw_len);
+ DEBUG(100,("multibyte len:%d\n", *new_pw_len));
+ DEBUG(100,("original char len:%d\n", byte_len/2));
+#endif
+
+ return True;
+}
diff --git a/source/libsmb/smberr.c b/source/libsmb/smberr.c
new file mode 100644
index 00000000000..82efbdb6898
--- /dev/null
+++ b/source/libsmb/smberr.c
@@ -0,0 +1,256 @@
+/*
+ Unix SMB/CIFS implementation.
+ 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 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.
+*/
+
+#define NO_SYSLOG
+
+#include "includes.h"
+
+/* error code stuff - put together by Merik Karman
+ merik@blackadder.dsh.oz.au */
+
+
+/* There is a big list of error codes and their meanings at:
+
+ http://msdn.microsoft.com/library/default.asp?url=/library/en-us/debug/errlist_7oz7.asp
+
+ and if you don't like MSDN try:
+
+ http://www.siris.gr/computers/library/error.htm
+
+*/
+
+typedef const struct
+{
+ const char *name;
+ int code;
+ const char *message;
+} err_code_struct;
+
+/* Dos Error Messages */
+err_code_struct dos_msgs[] = {
+ {"ERRbadfunc",ERRbadfunc,"Invalid function."},
+ {"ERRbadfile",ERRbadfile,"File not found."},
+ {"ERRbadpath",ERRbadpath,"Directory invalid."},
+ {"ERRnofids",ERRnofids,"No file descriptors available"},
+ {"ERRnoaccess",ERRnoaccess,"Access denied."},
+ {"ERRbadfid",ERRbadfid,"Invalid file handle."},
+ {"ERRbadmcb",ERRbadmcb,"Memory control blocks destroyed."},
+ {"ERRnomem",ERRnomem,"Insufficient server memory to perform the requested function."},
+ {"ERRbadmem",ERRbadmem,"Invalid memory block address."},
+ {"ERRbadenv",ERRbadenv,"Invalid environment."},
+ {"ERRbadformat",11,"Invalid format."},
+ {"ERRbadaccess",ERRbadaccess,"Invalid open mode."},
+ {"ERRbaddata",ERRbaddata,"Invalid data."},
+ {"ERRres",ERRres,"reserved."},
+ {"ERRbaddrive",ERRbaddrive,"Invalid drive specified."},
+ {"ERRremcd",ERRremcd,"A Delete Directory request attempted to remove the server's current directory."},
+ {"ERRdiffdevice",ERRdiffdevice,"Not same device."},
+ {"ERRnofiles",ERRnofiles,"A File Search command can find no more files matching the specified criteria."},
+ {"ERRbadshare",ERRbadshare,"The sharing mode specified for an Open conflicts with existing FIDs on the file."},
+ {"ERRlock",ERRlock,"A Lock request conflicted with an existing lock or specified an invalid mode, or an Unlock requested attempted to remove a lock held by another process."},
+ {"ERRunsup", ERRunsup, "The operation is unsupported"},
+ {"ERRnosuchshare", ERRnosuchshare, "You specified an invalid share name"},
+ {"ERRfilexists",ERRfilexists,"The file named in a Create Directory, Make New File or Link request already exists."},
+ {"ERRinvalidname",ERRinvalidname, "Invalid name"},
+ {"ERRbadpipe",ERRbadpipe,"Pipe invalid."},
+ {"ERRpipebusy",ERRpipebusy,"All instances of the requested pipe are busy."},
+ {"ERRpipeclosing",ERRpipeclosing,"Pipe close in progress."},
+ {"ERRnotconnected",ERRnotconnected,"No process on other end of pipe."},
+ {"ERRmoredata",ERRmoredata,"There is more data to be returned."},
+ {"ERRinvgroup",ERRinvgroup,"Invalid workgroup (try the -W option)"},
+ {"ERRlogonfailure",ERRlogonfailure,"Logon failure"},
+ {"ERRdiskfull",ERRdiskfull,"Disk full"},
+ {"ERRgeneral",ERRgeneral, "General failure"},
+ {"ERRunknownlevel",ERRunknownlevel, "Unknown info level"},
+ {NULL,-1,NULL}};
+
+/* Server Error Messages */
+err_code_struct server_msgs[] = {
+ {"ERRerror",1,"Non-specific error code."},
+ {"ERRbadpw",2,"Bad password - name/password pair in a Tree Connect or Session Setup are invalid."},
+ {"ERRbadtype",3,"reserved."},
+ {"ERRaccess",4,"The requester does not have the necessary access rights within the specified context for the requested function. The context is defined by the TID or the UID."},
+ {"ERRinvnid",5,"The tree ID (TID) specified in a command was invalid."},
+ {"ERRinvnetname",6,"Invalid network name in tree connect."},
+ {"ERRinvdevice",7,"Invalid device - printer request made to non-printer connection or non-printer request made to printer connection."},
+ {"ERRqfull",49,"Print queue full (files) -- returned by open print file."},
+ {"ERRqtoobig",50,"Print queue full -- no space."},
+ {"ERRqeof",51,"EOF on print queue dump."},
+ {"ERRinvpfid",52,"Invalid print file FID."},
+ {"ERRsmbcmd",64,"The server did not recognize the command received."},
+ {"ERRsrverror",65,"The server encountered an internal error, e.g., system file unavailable."},
+ {"ERRfilespecs",67,"The file handle (FID) and pathname parameters contained an invalid combination of values."},
+ {"ERRreserved",68,"reserved."},
+ {"ERRbadpermits",69,"The access permissions specified for a file or directory are not a valid combination. The server cannot set the requested attribute."},
+ {"ERRreserved",70,"reserved."},
+ {"ERRsetattrmode",71,"The attribute mode in the Set File Attribute request is invalid."},
+ {"ERRpaused",81,"Server is paused."},
+ {"ERRmsgoff",82,"Not receiving messages."},
+ {"ERRnoroom",83,"No room to buffer message."},
+ {"ERRrmuns",87,"Too many remote user names."},
+ {"ERRtimeout",88,"Operation timed out."},
+ {"ERRnoresource",89,"No resources currently available for request."},
+ {"ERRtoomanyuids",90,"Too many UIDs active on this session."},
+ {"ERRbaduid",91,"The UID is not known as a valid ID on this session."},
+ {"ERRusempx",250,"Temp unable to support Raw, use MPX mode."},
+ {"ERRusestd",251,"Temp unable to support Raw, use standard read/write."},
+ {"ERRcontmpx",252,"Continue in MPX mode."},
+ {"ERRreserved",253,"reserved."},
+ {"ERRreserved",254,"reserved."},
+ {"ERRnosupport",0xFFFF,"Function not supported."},
+ {NULL,-1,NULL}};
+
+/* Hard Error Messages */
+err_code_struct hard_msgs[] = {
+ {"ERRnowrite",19,"Attempt to write on write-protected diskette."},
+ {"ERRbadunit",20,"Unknown unit."},
+ {"ERRnotready",21,"Drive not ready."},
+ {"ERRbadcmd",22,"Unknown command."},
+ {"ERRdata",23,"Data error (CRC)."},
+ {"ERRbadreq",24,"Bad request structure length."},
+ {"ERRseek",25 ,"Seek error."},
+ {"ERRbadmedia",26,"Unknown media type."},
+ {"ERRbadsector",27,"Sector not found."},
+ {"ERRnopaper",28,"Printer out of paper."},
+ {"ERRwrite",29,"Write fault."},
+ {"ERRread",30,"Read fault."},
+ {"ERRgeneral",31,"General failure."},
+ {"ERRbadshare",32,"An open conflicts with an existing open."},
+ {"ERRlock",33,"A Lock request conflicted with an existing lock or specified an invalid mode, or an Unlock requested attempted to remove a lock held by another process."},
+ {"ERRwrongdisk",34,"The wrong disk was found in a drive."},
+ {"ERRFCBUnavail",35,"No FCBs are available to process request."},
+ {"ERRsharebufexc",36,"A sharing buffer has been exceeded."},
+ {NULL,-1,NULL}};
+
+
+const struct
+{
+ int code;
+ const char *class;
+ err_code_struct *err_msgs;
+} err_classes[] = {
+ {0,"SUCCESS",NULL},
+ {0x01,"ERRDOS",dos_msgs},
+ {0x02,"ERRSRV",server_msgs},
+ {0x03,"ERRHRD",hard_msgs},
+ {0x04,"ERRXOS",NULL},
+ {0xE1,"ERRRMX1",NULL},
+ {0xE2,"ERRRMX2",NULL},
+ {0xE3,"ERRRMX3",NULL},
+ {0xFF,"ERRCMD",NULL},
+ {-1,NULL,NULL}};
+
+
+/****************************************************************************
+return a SMB error name from a class and code
+****************************************************************************/
+const char *smb_dos_err_name(uint8 class, uint16 num)
+{
+ static pstring ret;
+ int i,j;
+
+ for (i=0;err_classes[i].class;i++)
+ if (err_classes[i].code == class) {
+ if (err_classes[i].err_msgs) {
+ err_code_struct *err = err_classes[i].err_msgs;
+ for (j=0;err[j].name;j++)
+ if (num == err[j].code) {
+ return err[j].name;
+ }
+ }
+ slprintf(ret, sizeof(ret) - 1, "%d",num);
+ return ret;
+ }
+
+ slprintf(ret, sizeof(ret) - 1, "Error: Unknown error class (%d,%d)",class,num);
+ return(ret);
+}
+
+/* Return a string for a DOS error */
+
+const char *get_dos_error_msg(WERROR result)
+{
+ uint16 errnum;
+
+ errnum = W_ERROR_V(result);
+
+ return smb_dos_err_name(ERRDOS, errnum);
+}
+
+/****************************************************************************
+return a SMB error class name as a string.
+****************************************************************************/
+const char *smb_dos_err_class(uint8 class)
+{
+ static pstring ret;
+ int i;
+
+ for (i=0;err_classes[i].class;i++) {
+ if (err_classes[i].code == class) {
+ return err_classes[i].class;
+ }
+ }
+
+ slprintf(ret, sizeof(ret) - 1, "Error: Unknown class (%d)",class);
+ return(ret);
+}
+
+/****************************************************************************
+return a SMB string from an SMB buffer
+****************************************************************************/
+char *smb_dos_errstr(char *inbuf)
+{
+ static pstring ret;
+ int class = CVAL(inbuf,smb_rcls);
+ int num = SVAL(inbuf,smb_err);
+ int i,j;
+
+ for (i=0;err_classes[i].class;i++)
+ if (err_classes[i].code == class) {
+ if (err_classes[i].err_msgs) {
+ err_code_struct *err = err_classes[i].err_msgs;
+ for (j=0;err[j].name;j++)
+ if (num == err[j].code) {
+ if (DEBUGLEVEL > 0)
+ slprintf(ret, sizeof(ret) - 1, "%s - %s (%s)",
+ err_classes[i].class,
+ err[j].name,err[j].message);
+ else
+ slprintf(ret, sizeof(ret) - 1, "%s - %s",
+ err_classes[i].class,err[j].name);
+ return ret;
+ }
+ }
+
+ slprintf(ret, sizeof(ret) - 1, "%s - %d",err_classes[i].class,num);
+ return ret;
+ }
+
+ slprintf(ret, sizeof(ret) - 1, "Error: Unknown error (%d,%d)",class,num);
+ return(ret);
+}
+
+/*****************************************************************************
+map a unix errno to a win32 error
+ *****************************************************************************/
+WERROR map_werror_from_unix(int error)
+{
+ NTSTATUS status = map_nt_error_from_unix(error);
+ return ntstatus_to_werror(status);
+}
diff --git a/source/libsmb/spnego.c b/source/libsmb/spnego.c
new file mode 100644
index 00000000000..50caf7b4c0e
--- /dev/null
+++ b/source/libsmb/spnego.c
@@ -0,0 +1,343 @@
+/*
+ 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 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"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_AUTH
+
+static BOOL read_negTokenInit(ASN1_DATA *asn1, negTokenInit_t *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;
+
+ switch (asn1->data[asn1->ofs]) {
+ /* Read mechTypes */
+ case ASN1_CONTEXT(0):
+ asn1_start_tag(asn1, ASN1_CONTEXT(0));
+ asn1_start_tag(asn1, ASN1_SEQUENCE(0));
+
+ token->mechTypes = malloc(sizeof(*token->mechTypes));
+ for (i = 0; !asn1->has_error &&
+ 0 < asn1_tag_remaining(asn1); i++) {
+ token->mechTypes =
+ realloc(token->mechTypes, (i + 2) *
+ sizeof(*token->mechTypes));
+ asn1_read_OID(asn1, 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, &token->mechToken);
+ asn1_end_tag(asn1);
+ break;
+ /* Read mecListMIC */
+ case ASN1_CONTEXT(3):
+ asn1_start_tag(asn1, ASN1_CONTEXT(3));
+ if (asn1->data[asn1->ofs] == ASN1_OCTET_STRING) {
+ asn1_read_OctetString(asn1,
+ &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, &mechListMIC);
+ asn1_pop_tag(asn1);
+ asn1_pop_tag(asn1);
+
+ token->mechListMIC =
+ data_blob(mechListMIC, strlen(mechListMIC));
+ SAFE_FREE(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(ASN1_DATA *asn1, negTokenInit_t *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(ASN1_DATA *asn1, negTokenTarg_t *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)) {
+ switch (asn1->data[asn1->ofs]) {
+ 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, &token->supportedMech);
+ asn1_end_tag(asn1);
+ break;
+ case ASN1_CONTEXT(2):
+ asn1_start_tag(asn1, ASN1_CONTEXT(2));
+ asn1_read_OctetString(asn1, &token->responseToken);
+ asn1_end_tag(asn1);
+ break;
+ case ASN1_CONTEXT(3):
+ asn1_start_tag(asn1, ASN1_CONTEXT(3));
+ asn1_read_OctetString(asn1, &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(ASN1_DATA *asn1, negTokenTarg_t *token)
+{
+ asn1_push_tag(asn1, ASN1_CONTEXT(1));
+ asn1_push_tag(asn1, ASN1_SEQUENCE(0));
+
+ 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 read_spnego_data(DATA_BLOB data, SPNEGO_DATA *token)
+{
+ ASN1_DATA asn1;
+ ssize_t ret = -1;
+
+ ZERO_STRUCTP(token);
+ ZERO_STRUCT(asn1);
+ asn1_load(&asn1, data);
+
+ switch (asn1.data[asn1.ofs]) {
+ case ASN1_APPLICATION(0):
+ asn1_start_tag(&asn1, ASN1_APPLICATION(0));
+ asn1_check_OID(&asn1, OID_SPNEGO);
+ if (read_negTokenInit(&asn1, &token->negTokenInit)) {
+ token->type = SPNEGO_NEG_TOKEN_INIT;
+ }
+ asn1_end_tag(&asn1);
+ break;
+ case ASN1_CONTEXT(1):
+ if (read_negTokenTarg(&asn1, &token->negTokenTarg)) {
+ token->type = SPNEGO_NEG_TOKEN_TARG;
+ }
+ break;
+ default:
+ break;
+ }
+
+ if (!asn1.has_error) ret = asn1.ofs;
+ asn1_free(&asn1);
+
+ return ret;
+}
+
+ssize_t write_spnego_data(DATA_BLOB *blob, SPNEGO_DATA *spnego)
+{
+ ASN1_DATA asn1;
+ ssize_t ret = -1;
+
+ ZERO_STRUCT(asn1);
+
+ switch (spnego->type) {
+ case SPNEGO_NEG_TOKEN_INIT:
+ asn1_push_tag(&asn1, ASN1_APPLICATION(0));
+ asn1_write_OID(&asn1, 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(asn1.data, asn1.length);
+ ret = asn1.ofs;
+ }
+ asn1_free(&asn1);
+
+ return ret;
+}
+
+BOOL free_spnego_data(SPNEGO_DATA *spnego)
+{
+ BOOL ret = True;
+
+ if (!spnego) goto out;
+
+ switch(spnego->type) {
+ case SPNEGO_NEG_TOKEN_INIT:
+ if (spnego->negTokenInit.mechTypes) {
+ int i;
+ for (i = 0; spnego->negTokenInit.mechTypes[i]; i++) {
+ free(spnego->negTokenInit.mechTypes[i]);
+ }
+ free(spnego->negTokenInit.mechTypes);
+ }
+ data_blob_free(&spnego->negTokenInit.mechToken);
+ data_blob_free(&spnego->negTokenInit.mechListMIC);
+ break;
+ case SPNEGO_NEG_TOKEN_TARG:
+ if (spnego->negTokenTarg.supportedMech) {
+ free(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;
+}
+
diff --git a/source/libsmb/trustdom_cache.c b/source/libsmb/trustdom_cache.c
new file mode 100644
index 00000000000..0128d080062
--- /dev/null
+++ b/source/libsmb/trustdom_cache.c
@@ -0,0 +1,342 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ Trusted domain names cache on top of gencache.
+
+ 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 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"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_ALL /* there's no proper class yet */
+
+#define TDOMKEY_FMT "TDOM/%s"
+#define TDOMTSKEY "TDOMCACHE/TIMESTAMP"
+
+
+/**
+ * @file trustdom_cache.c
+ *
+ * Implementation of trusted domain names cache useful when
+ * samba acts as domain member server. In such case, caching
+ * domain names currently trusted gives a performance gain
+ * because there's no need to query PDC each time we need
+ * list of trusted domains
+ **/
+
+
+/**
+ * Initialise trustdom name caching system. Call gencache
+ * initialisation routine to perform necessary activities.
+ *
+ * @return true upon successful cache initialisation or
+ * false if cache init failed
+ **/
+
+BOOL trustdom_cache_enable(void)
+{
+ /* Init trustdom cache by calling gencache initialisation */
+ if (!gencache_init()) {
+ DEBUG(2, ("trustdomcache_enable: Couldn't initialise trustdom cache on top of gencache.\n"));
+ return False;
+ }
+
+ return True;
+}
+
+
+/**
+ * Shutdown trustdom name caching system. Calls gencache
+ * shutdown function.
+ *
+ * @return true upon successful cache close or
+ * false if it failed
+ **/
+
+BOOL trustdom_cache_shutdown(void)
+{
+ /* Close trustdom cache by calling gencache shutdown */
+ if (!gencache_shutdown()) {
+ DEBUG(2, ("trustdomcache_shutdown: Couldn't shutdown trustdom cache on top of gencache.\n"));
+ return False;
+ }
+
+ return True;
+}
+
+
+/**
+ * Form up trustdom name key. It is based only
+ * on domain name now.
+ *
+ * @param name trusted domain name
+ * @return cache key for use in gencache mechanism
+ **/
+
+static char* trustdom_cache_key(const char* name)
+{
+ char* keystr = NULL;
+ asprintf(&keystr, TDOMKEY_FMT, strupper_static(name));
+
+ return keystr;
+}
+
+
+/**
+ * Store trusted domain in gencache as the domain name (key)
+ * and ip address of domain controller (value)
+ *
+ * @param name trusted domain name
+ * @param alt_name alternative trusted domain name (used in ADS domains)
+ * @param sid trusted domain's SID
+ * @param timeout cache entry expiration time
+ * @return true upon successful value storing or
+ * false if store attempt failed
+ **/
+
+BOOL trustdom_cache_store(char* name, char* alt_name, const DOM_SID *sid,
+ time_t timeout)
+{
+ char *key, *alt_key;
+ fstring sid_string;
+
+ /*
+ * we use gecache call to avoid annoying debug messages
+ * about initialised trustdom
+ */
+ if (!gencache_init()) return False;
+
+ DEBUG(5, ("trustdom_store: storing SID %s of domain %s\n",
+ sid_string_static(sid), name));
+
+ key = trustdom_cache_key(name);
+ alt_key = alt_name ? trustdom_cache_key(alt_name) : NULL;
+
+ /* Generate string representation domain SID */
+ sid_to_string(sid_string, sid);
+
+ /*
+ * try to put the names in the cache
+ */
+ if (alt_key) {
+ return (gencache_set(alt_key, sid_string, timeout)
+ && gencache_set(key, sid_string, timeout));
+ }
+
+ return gencache_set(key, sid_string, timeout);
+}
+
+
+/**
+ * Fetch trusted domain's dc from the gencache.
+ * This routine can also be used to check whether given
+ * domain is currently trusted one.
+ *
+ * @param name trusted domain name
+ * @param sid trusted domain's SID to be returned
+ * @return true if entry is found or
+ * false if has expired/doesn't exist
+ **/
+
+BOOL trustdom_cache_fetch(const char* name, DOM_SID* sid)
+{
+ char *key, *value;
+ time_t timeout;
+
+ /* init the cache */
+ if (!gencache_init()) return False;
+
+ /* exit now if null pointers were passed as they're required further */
+ if (!sid) return False;
+
+ /* prepare a key and get the value */
+ key = trustdom_cache_key(name);
+ if (!key) return False;
+
+ if (!gencache_get(key, &value, &timeout)) {
+ DEBUG(5, ("no entry for trusted domain %s found.\n", name));
+ SAFE_FREE(key);
+ return False;
+ } else {
+ SAFE_FREE(key);
+ DEBUG(5, ("trusted domain %s found (%s)\n", name, value));
+ }
+
+ /* convert ip string representation into in_addr structure */
+ if(! string_to_sid(sid, value)) {
+ sid = NULL;
+ return False;
+ }
+
+ return True;
+}
+
+
+/*******************************************************************
+ fetch the timestamp from the last update
+*******************************************************************/
+
+uint32 trustdom_cache_fetch_timestamp( void )
+{
+ char *value;
+ time_t timeout;
+ uint32 timestamp;
+
+ /* init the cache */
+ if (!gencache_init())
+ return False;
+
+ if (!gencache_get(TDOMTSKEY, &value, &timeout)) {
+ DEBUG(5, ("no timestamp for trusted domain cache located.\n"));
+ return 0;
+ }
+
+ timestamp = atoi(value);
+
+ return timestamp;
+}
+
+/*******************************************************************
+ store the timestamp from the last update
+*******************************************************************/
+
+BOOL trustdom_cache_store_timestamp( uint32 t, time_t timeout )
+{
+ fstring value;
+
+ /* init the cache */
+ if (!gencache_init())
+ return False;
+
+ fstr_sprintf(value, "%d", t );
+
+ if (!gencache_set(TDOMTSKEY, value, timeout)) {
+ DEBUG(5, ("failed to set timestamp for trustdom_cache\n"));
+ return False;
+ }
+
+ return True;
+}
+
+
+/*******************************************************************
+ lock the timestamp entry in the trustdom_cache
+*******************************************************************/
+
+BOOL trustdom_cache_lock_timestamp( void )
+{
+ return gencache_lock_entry( TDOMTSKEY ) != -1;
+}
+
+/*******************************************************************
+ unlock the timestamp entry in the trustdom_cache
+*******************************************************************/
+
+void trustdom_cache_unlock_timestamp( void )
+{
+ gencache_unlock_entry( TDOMTSKEY );
+}
+
+/**
+ * Delete single trustdom entry. Look at the
+ * gencache_iterate definition.
+ *
+ **/
+
+static void flush_trustdom_name(const char* key, const char *value, time_t timeout, void* dptr)
+{
+ gencache_del(key);
+ DEBUG(5, ("Deleting entry %s\n", key));
+}
+
+
+/**
+ * Flush all the trusted domains entries from the cache.
+ **/
+
+void trustdom_cache_flush(void)
+{
+ if (!gencache_init())
+ return;
+
+ /*
+ * iterate through each TDOM cache's entry and flush it
+ * by flush_trustdom_name function
+ */
+ gencache_iterate(flush_trustdom_name, NULL, trustdom_cache_key("*"));
+ DEBUG(5, ("Trusted domains cache flushed\n"));
+}
+
+/********************************************************************
+ update the trustdom_cache if needed
+********************************************************************/
+#define TRUSTDOM_UPDATE_INTERVAL 600
+
+void update_trustdom_cache( void )
+{
+ char **domain_names;
+ DOM_SID *dom_sids;
+ uint32 num_domains;
+ uint32 last_check;
+ int time_diff;
+ TALLOC_CTX *mem_ctx = NULL;
+ time_t now = time(NULL);
+ int i;
+
+ /* get the timestamp. We have to initialise it if the last timestamp == 0 */
+
+ if ( (last_check = trustdom_cache_fetch_timestamp()) == 0 )
+ trustdom_cache_store_timestamp(0, now+TRUSTDOM_UPDATE_INTERVAL);
+
+ time_diff = now - last_check;
+
+ if ( (time_diff > 0) && (time_diff < TRUSTDOM_UPDATE_INTERVAL) ) {
+ DEBUG(10,("update_trustdom_cache: not time to update trustdom_cache yet\n"));
+ return;
+ }
+
+ /* lock the timestamp */
+ if ( !trustdom_cache_lock_timestamp() )
+ return;
+
+ if ( !(mem_ctx = talloc_init("update_trustdom_cache")) ) {
+ DEBUG(0,("update_trustdom_cache: talloc_init() failed!\n"));
+ goto done;
+ }
+
+ /* get the domains and store them */
+
+ if ( enumerate_domain_trusts(mem_ctx, lp_workgroup(), &domain_names,
+ &num_domains, &dom_sids) )
+ {
+ for ( i=0; i<num_domains; i++ ) {
+ trustdom_cache_store( domain_names[i], NULL, &dom_sids[i],
+ now+TRUSTDOM_UPDATE_INTERVAL);
+ }
+
+ trustdom_cache_store_timestamp( now, now+TRUSTDOM_UPDATE_INTERVAL );
+ }
+
+done:
+ /* unlock and we're done */
+ trustdom_cache_unlock_timestamp();
+
+ talloc_destroy( mem_ctx );
+
+ return;
+}
diff --git a/source/libsmb/trusts_util.c b/source/libsmb/trusts_util.c
new file mode 100644
index 00000000000..b420e4fa081
--- /dev/null
+++ b/source/libsmb/trusts_util.c
@@ -0,0 +1,190 @@
+/*
+ * Unix SMB/CIFS implementation.
+ * Routines to operate on various trust relationships
+ * Copyright (C) Andrew Bartlett 2001
+ * Copyright (C) Rafal Szczesniak 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 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"
+
+/*********************************************************
+ Change the domain password on the PDC.
+
+ Just changes the password betwen the two values specified.
+
+ Caller must have the cli connected to the netlogon pipe
+ already.
+**********************************************************/
+static NTSTATUS just_change_the_password(struct cli_state *cli, TALLOC_CTX *mem_ctx,
+ unsigned char orig_trust_passwd_hash[16],
+ unsigned char new_trust_passwd_hash[16],
+ uint32 sec_channel_type)
+{
+ NTSTATUS result;
+
+ /* ensure that schannel uses the right domain */
+ fstrcpy(cli->domain, lp_workgroup());
+ if (! NT_STATUS_IS_OK(result = cli_nt_establish_netlogon(cli, sec_channel_type, orig_trust_passwd_hash))) {
+ DEBUG(3,("just_change_the_password: unable to setup creds (%s)!\n",
+ nt_errstr(result)));
+ return result;
+ }
+
+ result = cli_net_srv_pwset(cli, mem_ctx, global_myname(), new_trust_passwd_hash);
+
+ if (!NT_STATUS_IS_OK(result)) {
+ DEBUG(0,("just_change_the_password: unable to change password (%s)!\n",
+ nt_errstr(result)));
+ }
+ return result;
+}
+
+/*********************************************************
+ Change the domain password on the PDC.
+ Store the password ourselves, but use the supplied password
+ Caller must have already setup the connection to the NETLOGON pipe
+**********************************************************/
+
+NTSTATUS trust_pw_change_and_store_it(struct cli_state *cli, TALLOC_CTX *mem_ctx,
+ const char *domain,
+ unsigned char orig_trust_passwd_hash[16],
+ uint32 sec_channel_type)
+{
+ unsigned char new_trust_passwd_hash[16];
+ char *new_trust_passwd;
+ char *str;
+ NTSTATUS nt_status;
+
+ /* Create a random machine account password */
+ str = generate_random_str(DEFAULT_TRUST_ACCOUNT_PASSWORD_LENGTH);
+ new_trust_passwd = talloc_strdup(mem_ctx, str);
+
+ E_md4hash(new_trust_passwd, new_trust_passwd_hash);
+
+ nt_status = just_change_the_password(cli, mem_ctx, orig_trust_passwd_hash,
+ new_trust_passwd_hash, sec_channel_type);
+
+ if (NT_STATUS_IS_OK(nt_status)) {
+ DEBUG(3,("%s : trust_pw_change_and_store_it: Changed password.\n",
+ timestring(False)));
+ /*
+ * Return the result of trying to write the new password
+ * back into the trust account file.
+ */
+ if (!secrets_store_machine_password(new_trust_passwd, domain, sec_channel_type)) {
+ nt_status = NT_STATUS_UNSUCCESSFUL;
+ }
+ }
+
+ return nt_status;
+}
+
+/*********************************************************
+ Change the domain password on the PDC.
+ Do most of the legwork ourselfs. Caller must have
+ already setup the connection to the NETLOGON pipe
+**********************************************************/
+
+NTSTATUS trust_pw_find_change_and_store_it(struct cli_state *cli,
+ TALLOC_CTX *mem_ctx,
+ const char *domain)
+{
+ unsigned char old_trust_passwd_hash[16];
+ char *up_domain;
+ uint32 sec_channel_type = 0;
+
+ up_domain = talloc_strdup(mem_ctx, domain);
+
+ if (!secrets_fetch_trust_account_password(domain,
+ old_trust_passwd_hash,
+ NULL, &sec_channel_type)) {
+ DEBUG(0, ("could not fetch domain secrets for domain %s!\n", domain));
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ return trust_pw_change_and_store_it(cli, mem_ctx, domain,
+ old_trust_passwd_hash,
+ sec_channel_type);
+
+}
+
+/*********************************************************************
+ Enumerate the list of trusted domains from a DC
+*********************************************************************/
+
+BOOL enumerate_domain_trusts( TALLOC_CTX *mem_ctx, const char *domain,
+ char ***domain_names, uint32 *num_domains,
+ DOM_SID **sids )
+{
+ POLICY_HND pol;
+ NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+ fstring dc_name;
+ struct in_addr dc_ip;
+ uint32 enum_ctx = 0;
+ struct cli_state *cli = NULL;
+ BOOL retry;
+
+ *domain_names = NULL;
+ *num_domains = 0;
+ *sids = NULL;
+
+ /* lookup a DC first */
+
+ if ( !get_dc_name(domain, NULL, dc_name, &dc_ip) ) {
+ DEBUG(3,("enumerate_domain_trusts: can't locate a DC for domain %s\n",
+ domain));
+ return False;
+ }
+
+ /* setup the anonymous connection */
+
+ result = cli_full_connection( &cli, global_myname(), dc_name, &dc_ip, 0, "IPC$", "IPC",
+ "", "", "", 0, Undefined, &retry);
+ if ( !NT_STATUS_IS_OK(result) )
+ goto done;
+
+ /* open the LSARPC_PIPE */
+
+ if ( !cli_nt_session_open( cli, PI_LSARPC ) ) {
+ result = NT_STATUS_UNSUCCESSFUL;
+ goto done;
+ }
+
+ /* get a handle */
+
+ result = cli_lsa_open_policy(cli, mem_ctx, True,
+ POLICY_VIEW_LOCAL_INFORMATION, &pol);
+ if ( !NT_STATUS_IS_OK(result) )
+ goto done;
+
+ /* Lookup list of trusted domains */
+
+ result = cli_lsa_enum_trust_dom(cli, mem_ctx, &pol, &enum_ctx,
+ num_domains, domain_names, sids);
+ if ( !NT_STATUS_IS_OK(result) )
+ goto done;
+
+done:
+ /* cleanup */
+ if (cli) {
+ DEBUG(10,("enumerate_domain_trusts: shutting down connection...\n"));
+ cli_shutdown( cli );
+ }
+
+ return NT_STATUS_IS_OK(result);
+}
+
diff --git a/source/libsmb/unexpected.c b/source/libsmb/unexpected.c
new file mode 100644
index 00000000000..97d6071e714
--- /dev/null
+++ b/source/libsmb/unexpected.c
@@ -0,0 +1,164 @@
+/*
+ Unix SMB/CIFS implementation.
+ handle unexpected packets
+ 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 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"
+
+static TDB_CONTEXT *tdbd = NULL;
+
+/* the key type used in the unexpeceted packet database */
+struct unexpected_key {
+ enum packet_type packet_type;
+ time_t timestamp;
+ int count;
+};
+
+
+
+/****************************************************************************
+ all unexpected packets are passed in here, to be stored in a unexpected
+ packet database. This allows nmblookup and other tools to receive packets
+ erroneoously sent to the wrong port by broken MS systems
+ **************************************************************************/
+void unexpected_packet(struct packet_struct *p)
+{
+ static int count;
+ TDB_DATA kbuf, dbuf;
+ struct unexpected_key key;
+ char buf[1024];
+ int len=0;
+
+ if (!tdbd) {
+ tdbd = tdb_open_log(lock_path("unexpected.tdb"), 0,
+ TDB_CLEAR_IF_FIRST|TDB_DEFAULT,
+ O_RDWR | O_CREAT, 0644);
+ if (!tdbd) {
+ DEBUG(0,("Failed to open unexpected.tdb\n"));
+ return;
+ }
+ }
+
+ memset(buf,'\0',sizeof(buf));
+
+ len = build_packet(buf, p);
+
+ key.packet_type = p->packet_type;
+ key.timestamp = p->timestamp;
+ key.count = count++;
+
+ kbuf.dptr = (char *)&key;
+ kbuf.dsize = sizeof(key);
+ dbuf.dptr = buf;
+ dbuf.dsize = len;
+
+ tdb_store(tdbd, kbuf, dbuf, TDB_REPLACE);
+}
+
+
+static time_t lastt;
+
+/****************************************************************************
+delete the record if it is too old
+ **************************************************************************/
+static int traverse_fn(TDB_CONTEXT *ttdb, TDB_DATA kbuf, TDB_DATA dbuf, void *state)
+{
+ struct unexpected_key key;
+
+ memcpy(&key, kbuf.dptr, sizeof(key));
+
+ if (lastt - key.timestamp > NMBD_UNEXPECTED_TIMEOUT) {
+ tdb_delete(ttdb, kbuf);
+ }
+
+ return 0;
+}
+
+
+/****************************************************************************
+delete all old unexpected packets
+ **************************************************************************/
+void clear_unexpected(time_t t)
+{
+ if (!tdbd) return;
+
+ if ((lastt != 0) && (t < lastt + NMBD_UNEXPECTED_TIMEOUT))
+ return;
+
+ lastt = t;
+
+ tdb_traverse(tdbd, traverse_fn, NULL);
+}
+
+
+static struct packet_struct *matched_packet;
+static int match_id;
+static enum packet_type match_type;
+static const char *match_name;
+
+/****************************************************************************
+tdb traversal fn to find a matching 137 packet
+ **************************************************************************/
+static int traverse_match(TDB_CONTEXT *ttdb, TDB_DATA kbuf, TDB_DATA dbuf, void *state)
+{
+ struct unexpected_key key;
+ struct packet_struct *p;
+
+ memcpy(&key, kbuf.dptr, sizeof(key));
+
+ if (key.packet_type != match_type) return 0;
+
+ p = parse_packet(dbuf.dptr, dbuf.dsize, match_type);
+
+ if ((match_type == NMB_PACKET &&
+ p->packet.nmb.header.name_trn_id == match_id) ||
+ (match_type == DGRAM_PACKET &&
+ match_mailslot_name(p, match_name))) {
+ matched_packet = p;
+ return -1;
+ }
+
+ free_packet(p);
+
+ return 0;
+}
+
+
+/****************************************************************************
+check for a particular packet in the unexpected packet queue
+ **************************************************************************/
+struct packet_struct *receive_unexpected(enum packet_type packet_type, int id,
+ const char *mailslot_name)
+{
+ TDB_CONTEXT *tdb2;
+
+ tdb2 = tdb_open_log(lock_path("unexpected.tdb"), 0, 0, O_RDONLY, 0);
+ if (!tdb2) return NULL;
+
+ matched_packet = NULL;
+ match_id = id;
+ match_type = packet_type;
+ match_name = mailslot_name;
+
+ tdb_traverse(tdb2, traverse_match, NULL);
+
+ tdb_close(tdb2);
+
+ return matched_packet;
+}
diff --git a/source/locking/brlock.c b/source/locking/brlock.c
new file mode 100644
index 00000000000..47001c8b89c
--- /dev/null
+++ b/source/locking/brlock.c
@@ -0,0 +1,714 @@
+/*
+ Unix SMB/CIFS implementation.
+ byte range locking code
+ Updated to handle range splits/merges.
+
+ Copyright (C) Andrew Tridgell 1992-2000
+ 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 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.
+*/
+
+/* 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"
+
+#define ZERO_ZERO 0
+
+/* This contains elements that differentiate locks. The smbpid is a
+ client supplied pid, and is essentially the locking context for
+ this client */
+
+struct lock_context {
+ uint16 smbpid;
+ uint16 tid;
+ pid_t pid;
+};
+
+/* 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;
+ br_off start;
+ br_off size;
+ int fnum;
+ enum brl_type lock_type;
+};
+
+/* The key used in the brlock database. */
+
+struct lock_key {
+ SMB_DEV_T device;
+ SMB_INO_T inode;
+};
+
+/* The open brlock.tdb database. */
+
+static TDB_CONTEXT *tdb;
+
+/****************************************************************************
+ Create a locking key - ensuring zero filled for pad purposes.
+****************************************************************************/
+
+static TDB_DATA locking_key(SMB_DEV_T dev, SMB_INO_T inode)
+{
+ static struct lock_key key;
+ TDB_DATA kbuf;
+
+ memset(&key, '\0', sizeof(key));
+ key.device = dev;
+ key.inode = inode;
+ kbuf.dptr = (char *)&key;
+ kbuf.dsize = sizeof(key);
+ return kbuf;
+}
+
+/****************************************************************************
+ See if two locking contexts are equal.
+****************************************************************************/
+
+static BOOL brl_same_context(struct lock_context *ctx1,
+ struct lock_context *ctx2)
+{
+ return (ctx1->pid == ctx2->pid) &&
+ (ctx1->smbpid == ctx2->smbpid) &&
+ (ctx1->tid == ctx2->tid);
+}
+
+/****************************************************************************
+ See if lock2 can be added when lock1 is in place.
+****************************************************************************/
+
+static BOOL brl_conflict(struct lock_struct *lck1,
+ struct lock_struct *lck2)
+{
+ if (lck1->lock_type == PENDING_LOCK || lck2->lock_type == PENDING_LOCK )
+ return False;
+
+ if (lck1->lock_type == READ_LOCK && lck2->lock_type == READ_LOCK) {
+ return False;
+ }
+
+ if (brl_same_context(&lck1->context, &lck2->context) &&
+ lck2->lock_type == READ_LOCK && lck1->fnum == lck2->fnum) {
+ return False;
+ }
+
+ if (lck1->start >= (lck2->start + lck2->size) ||
+ lck2->start >= (lck1->start + lck1->size)) {
+ return False;
+ }
+
+ return True;
+}
+
+#if ZERO_ZERO
+static BOOL brl_conflict1(struct lock_struct *lck1,
+ struct lock_struct *lck2)
+{
+ if (lck1->lock_type == PENDING_LOCK || lck2->lock_type == PENDING_LOCK )
+ return False;
+
+ if (lck1->lock_type == READ_LOCK && lck2->lock_type == READ_LOCK) {
+ return False;
+ }
+
+ if (brl_same_context(&lck1->context, &lck2->context) &&
+ lck2->lock_type == READ_LOCK && lck1->fnum == lck2->fnum) {
+ return False;
+ }
+
+ if (lck2->start == 0 && lck2->size == 0 && lck1->size != 0) {
+ return True;
+ }
+
+ if (lck1->start >= (lck2->start + lck2->size) ||
+ lck2->start >= (lck1->start + lck1->size)) {
+ return False;
+ }
+
+ return True;
+}
+#endif
+
+/****************************************************************************
+ Check to see if this lock conflicts, but ignore our own locks on the
+ same fnum only.
+****************************************************************************/
+
+static BOOL brl_conflict_other(struct lock_struct *lck1, struct lock_struct *lck2)
+{
+ if (lck1->lock_type == PENDING_LOCK || lck2->lock_type == PENDING_LOCK )
+ return False;
+
+ if (lck1->lock_type == READ_LOCK && lck2->lock_type == READ_LOCK)
+ return False;
+
+ /*
+ * Incoming WRITE locks conflict with existing READ locks even
+ * if the context is the same. JRA. See LOCKTEST7 in smbtorture.
+ */
+
+ if (!(lck2->lock_type == WRITE_LOCK && lck1->lock_type == READ_LOCK)) {
+ if (brl_same_context(&lck1->context, &lck2->context) &&
+ lck1->fnum == lck2->fnum)
+ return False;
+ }
+
+ if (lck1->start >= (lck2->start + lck2->size) ||
+ lck2->start >= (lck1->start + lck1->size))
+ return False;
+
+ return True;
+}
+
+
+#if DONT_DO_THIS
+ /* doing this traversal could kill solaris machines under high load (tridge) */
+ /* delete any dead locks */
+
+/****************************************************************************
+ Delete a record if it is for a dead process, if check_self is true, then
+ delete any records belonging to this pid also (there shouldn't be any).
+****************************************************************************/
+
+static int delete_fn(TDB_CONTEXT *ttdb, TDB_DATA kbuf, TDB_DATA dbuf, void *state)
+{
+ struct lock_struct *locks;
+ int count, i;
+ BOOL check_self = *(BOOL *)state;
+ pid_t mypid = sys_getpid();
+
+ tdb_chainlock(tdb, kbuf);
+
+ locks = (struct lock_struct *)dbuf.dptr;
+
+ count = dbuf.dsize / sizeof(*locks);
+ for (i=0; i<count; i++) {
+ struct lock_struct *lock = &locks[i];
+
+ /* If check_self is true we want to remove our own records. */
+ if (check_self && (mypid == lock->context.pid)) {
+
+ DEBUG(0,("brlock : delete_fn. LOGIC ERROR ! Shutting down and a record for my pid (%u) exists !\n",
+ (unsigned int)lock->context.pid ));
+
+ } else if (process_exists(lock->context.pid)) {
+
+ DEBUG(10,("brlock : delete_fn. pid %u exists.\n", (unsigned int)lock->context.pid ));
+ continue;
+ }
+
+ DEBUG(10,("brlock : delete_fn. Deleting record for process %u\n",
+ (unsigned int)lock->context.pid ));
+
+ if (count > 1 && i < count-1) {
+ memmove(&locks[i], &locks[i+1],
+ sizeof(*locks)*((count-1) - i));
+ }
+ count--;
+ i--;
+ }
+
+ if (count == 0) {
+ tdb_delete(tdb, kbuf);
+ } else if (count < (dbuf.dsize / sizeof(*locks))) {
+ dbuf.dsize = count * sizeof(*locks);
+ tdb_store(tdb, kbuf, dbuf, TDB_REPLACE);
+ }
+
+ tdb_chainunlock(tdb, kbuf);
+ return 0;
+}
+#endif
+
+/****************************************************************************
+ Open up the brlock.tdb database.
+****************************************************************************/
+
+void brl_init(int read_only)
+{
+ if (tdb)
+ return;
+ tdb = tdb_open_ex(lock_path("brlock.tdb"), 0, TDB_DEFAULT|(read_only?0x0:TDB_CLEAR_IF_FIRST),
+ read_only?O_RDONLY:(O_RDWR|O_CREAT), 0644, smbd_tdb_log);
+ if (!tdb) {
+ DEBUG(0,("Failed to open byte range locking database\n"));
+ return;
+ }
+
+#if DONT_DO_THIS
+ /* doing this traversal could kill solaris machines under high load (tridge) */
+ /* delete any dead locks */
+ if (!read_only) {
+ BOOL check_self = False;
+ tdb_traverse(tdb, delete_fn, &check_self);
+ }
+#endif
+}
+
+/****************************************************************************
+ Close down the brlock.tdb database.
+****************************************************************************/
+
+void brl_shutdown(int read_only)
+{
+ if (!tdb)
+ return;
+
+#if DONT_DO_THIS
+ /* doing this traversal could kill solaris machines under high load (tridge) */
+ /* delete any dead locks */
+ if (!read_only) {
+ BOOL check_self = True;
+ tdb_traverse(tdb, delete_fn, &check_self);
+ }
+#endif
+
+ tdb_close(tdb);
+}
+
+#if ZERO_ZERO
+/****************************************************************************
+compare two locks for sorting
+****************************************************************************/
+static int lock_compare(struct lock_struct *lck1,
+ struct lock_struct *lck2)
+{
+ if (lck1->start != lck2->start) return (lck1->start - lck2->start);
+ if (lck2->size != lck1->size) {
+ return ((int)lck1->size - (int)lck2->size);
+ }
+ return 0;
+}
+#endif
+
+/****************************************************************************
+ Lock a range of bytes.
+****************************************************************************/
+
+NTSTATUS brl_lock(SMB_DEV_T dev, SMB_INO_T ino, int fnum,
+ uint16 smbpid, pid_t pid, uint16 tid,
+ br_off start, br_off size,
+ enum brl_type lock_type, BOOL *my_lock_ctx)
+{
+ TDB_DATA kbuf, dbuf;
+ int count, i;
+ struct lock_struct lock, *locks;
+ char *tp;
+ NTSTATUS status = NT_STATUS_OK;
+ static int last_failed = -1;
+ static br_off last_failed_start;
+
+ *my_lock_ctx = False;
+ kbuf = locking_key(dev,ino);
+
+ dbuf.dptr = NULL;
+
+#if !ZERO_ZERO
+ if (start == 0 && size == 0) {
+ DEBUG(0,("client sent 0/0 lock - please report this\n"));
+ }
+#endif
+
+ tdb_chainlock(tdb, kbuf);
+ dbuf = tdb_fetch(tdb, kbuf);
+
+ lock.context.smbpid = smbpid;
+ lock.context.pid = pid;
+ lock.context.tid = tid;
+ lock.start = start;
+ lock.size = size;
+ lock.fnum = fnum;
+ lock.lock_type = lock_type;
+
+ 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_conflict(&locks[i], &lock)) {
+ status = NT_STATUS_LOCK_NOT_GRANTED;
+ /* Did we block ourselves ? */
+ if (brl_same_context(&locks[i].context, &lock.context))
+ *my_lock_ctx = True;
+ goto fail;
+ }
+#if ZERO_ZERO
+ if (lock.start == 0 && lock.size == 0 &&
+ locks[i].size == 0) {
+ break;
+ }
+#endif
+ }
+ }
+
+ /* no conflicts - add it to the list of locks */
+ tp = Realloc(dbuf.dptr, dbuf.dsize + sizeof(*locks));
+ if (!tp) {
+ status = NT_STATUS_NO_MEMORY;
+ goto fail;
+ } else {
+ dbuf.dptr = tp;
+ }
+ memcpy(dbuf.dptr + dbuf.dsize, &lock, sizeof(lock));
+ dbuf.dsize += sizeof(lock);
+
+#if ZERO_ZERO
+ /* sort the lock list */
+ qsort(dbuf.dptr, dbuf.dsize/sizeof(lock), sizeof(lock), lock_compare);
+#endif
+
+ tdb_store(tdb, kbuf, dbuf, TDB_REPLACE);
+
+ SAFE_FREE(dbuf.dptr);
+ tdb_chainunlock(tdb, kbuf);
+ return NT_STATUS_OK;
+
+ fail:
+ /* this is a nasty hack to try to simulate the lock result cache code in w2k.
+ It isn't completely accurate as I haven't yet worked out the correct
+ semantics (tridge)
+ */
+ if (last_failed == fnum &&
+ last_failed_start == start &&
+ NT_STATUS_EQUAL(status, NT_STATUS_LOCK_NOT_GRANTED)) {
+ status = NT_STATUS_FILE_LOCK_CONFLICT;
+ }
+ last_failed = fnum;
+ last_failed_start = start;
+
+ SAFE_FREE(dbuf.dptr);
+ tdb_chainunlock(tdb, kbuf);
+ return status;
+}
+
+/****************************************************************************
+ Check if an unlock overlaps a pending lock.
+****************************************************************************/
+
+static BOOL brl_pending_overlap(struct lock_struct *lock, struct lock_struct *pend_lock)
+{
+ if ((lock->start <= pend_lock->start) && (lock->start + lock->size > pend_lock->start))
+ return True;
+ if ((lock->start >= pend_lock->start) && (lock->start <= pend_lock->start + pend_lock->size))
+ return True;
+ return False;
+}
+
+/****************************************************************************
+ Unlock a range of bytes.
+****************************************************************************/
+
+BOOL brl_unlock(SMB_DEV_T dev, SMB_INO_T ino, int fnum,
+ uint16 smbpid, pid_t pid, uint16 tid,
+ br_off start, br_off size,
+ BOOL remove_pending_locks_only,
+ void (*pre_unlock_fn)(void *),
+ void *pre_unlock_data)
+{
+ TDB_DATA kbuf, dbuf;
+ int count, i, j;
+ struct lock_struct *locks;
+ struct lock_context context;
+
+ kbuf = locking_key(dev,ino);
+
+ dbuf.dptr = NULL;
+
+ tdb_chainlock(tdb, kbuf);
+ dbuf = tdb_fetch(tdb, kbuf);
+
+ if (!dbuf.dptr) {
+ DEBUG(10,("brl_unlock: tdb_fetch failed !\n"));
+ goto fail;
+ }
+
+ context.smbpid = smbpid;
+ context.pid = pid;
+ context.tid = tid;
+
+ /* there are existing locks - find a match */
+ locks = (struct lock_struct *)dbuf.dptr;
+ count = dbuf.dsize / sizeof(*locks);
+
+#if ZERO_ZERO
+ for (i=0; i<count; i++) {
+ struct lock_struct *lock = &locks[i];
+
+ if (lock->lock_type == WRITE_LOCK &&
+ brl_same_context(&lock->context, &context) &&
+ lock->fnum == fnum &&
+ lock->start == start &&
+ lock->size == size) {
+
+ if (pre_unlock_fn)
+ (*pre_unlock_fn)(pre_unlock_data);
+
+ /* found it - delete it */
+ if (count == 1) {
+ tdb_delete(tdb, kbuf);
+ } else {
+ if (i < count-1) {
+ memmove(&locks[i], &locks[i+1],
+ sizeof(*locks)*((count-1) - i));
+ }
+ dbuf.dsize -= sizeof(*locks);
+ tdb_store(tdb, kbuf, dbuf, TDB_REPLACE);
+ }
+
+ SAFE_FREE(dbuf.dptr);
+ tdb_chainunlock(tdb, kbuf);
+ return True;
+ }
+ }
+#endif
+
+ locks = (struct lock_struct *)dbuf.dptr;
+ count = dbuf.dsize / sizeof(*locks);
+ for (i=0; i<count; i++) {
+ struct lock_struct *lock = &locks[i];
+
+ if (brl_same_context(&lock->context, &context) &&
+ lock->fnum == fnum &&
+ lock->start == start &&
+ lock->size == size) {
+
+ if (remove_pending_locks_only && lock->lock_type != PENDING_LOCK)
+ continue;
+
+ if (lock->lock_type != PENDING_LOCK) {
+
+ /* Do any POSIX unlocks needed. */
+ if (pre_unlock_fn)
+ (*pre_unlock_fn)(pre_unlock_data);
+
+ /* Send unlock messages to any pending waiters that overlap. */
+ for (j=0; j<count; j++) {
+ struct lock_struct *pend_lock = &locks[j];
+
+ /* Ignore non-pending locks. */
+ if (pend_lock->lock_type != PENDING_LOCK)
+ continue;
+
+ /* We could send specific lock info here... */
+ if (brl_pending_overlap(lock, pend_lock)) {
+ DEBUG(10,("brl_unlock: sending unlock message to pid %u\n",
+ (unsigned int)pend_lock->context.pid ));
+
+ message_send_pid(pend_lock->context.pid,
+ MSG_SMB_UNLOCK,
+ NULL, 0, True);
+ }
+ }
+ }
+
+ /* found it - delete it */
+ if (count == 1) {
+ tdb_delete(tdb, kbuf);
+ } else {
+ if (i < count-1) {
+ memmove(&locks[i], &locks[i+1],
+ sizeof(*locks)*((count-1) - i));
+ }
+ dbuf.dsize -= sizeof(*locks);
+ tdb_store(tdb, kbuf, dbuf, TDB_REPLACE);
+ }
+
+ SAFE_FREE(dbuf.dptr);
+ tdb_chainunlock(tdb, kbuf);
+ return True;
+ }
+ }
+
+ /* we didn't find it */
+
+ fail:
+ SAFE_FREE(dbuf.dptr);
+ tdb_chainunlock(tdb, kbuf);
+ return False;
+}
+
+
+/****************************************************************************
+ Test if we could add a lock if we wanted to.
+****************************************************************************/
+
+BOOL brl_locktest(SMB_DEV_T dev, SMB_INO_T ino, int fnum,
+ uint16 smbpid, pid_t pid, uint16 tid,
+ br_off start, br_off size,
+ enum brl_type lock_type, int check_self)
+{
+ TDB_DATA kbuf, dbuf;
+ int count, i;
+ struct lock_struct lock, *locks;
+
+ kbuf = locking_key(dev,ino);
+
+ dbuf.dptr = NULL;
+
+ tdb_chainlock(tdb, kbuf);
+ dbuf = tdb_fetch(tdb, kbuf);
+
+ lock.context.smbpid = smbpid;
+ lock.context.pid = pid;
+ lock.context.tid = tid;
+ lock.start = start;
+ lock.size = size;
+ lock.fnum = fnum;
+ lock.lock_type = lock_type;
+
+ 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 (check_self) {
+ if (brl_conflict(&locks[i], &lock))
+ goto fail;
+ } else {
+ /*
+ * Our own locks don't conflict.
+ */
+ if (brl_conflict_other(&locks[i], &lock))
+ goto fail;
+ }
+ }
+ }
+
+ /* no conflicts - we could have added it */
+ SAFE_FREE(dbuf.dptr);
+ tdb_chainunlock(tdb, kbuf);
+ return True;
+
+ fail:
+ SAFE_FREE(dbuf.dptr);
+ tdb_chainunlock(tdb, kbuf);
+ return False;
+}
+
+/****************************************************************************
+ Remove any locks associated with a open file.
+****************************************************************************/
+
+void brl_close(SMB_DEV_T dev, SMB_INO_T ino, pid_t pid, int tid, int fnum)
+{
+ TDB_DATA kbuf, dbuf;
+ int count, i, j, dcount=0;
+ struct lock_struct *locks;
+
+ kbuf = locking_key(dev,ino);
+
+ dbuf.dptr = NULL;
+
+ tdb_chainlock(tdb, kbuf);
+ dbuf = tdb_fetch(tdb, kbuf);
+
+ if (!dbuf.dptr) goto fail;
+
+ /* 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.tid == tid &&
+ lock->context.pid == pid &&
+ lock->fnum == fnum) {
+
+ /* Send unlock messages to any pending waiters that overlap. */
+ for (j=0; j<count; j++) {
+ struct lock_struct *pend_lock = &locks[j];
+
+ /* Ignore our own or non-pending locks. */
+ if (pend_lock->lock_type != PENDING_LOCK)
+ continue;
+
+ if (pend_lock->context.tid == tid &&
+ pend_lock->context.pid == pid &&
+ pend_lock->fnum == fnum)
+ continue;
+
+ /* We could send specific lock info here... */
+ if (brl_pending_overlap(lock, pend_lock))
+ message_send_pid(pend_lock->context.pid,
+ MSG_SMB_UNLOCK,
+ NULL, 0, True);
+ }
+
+ /* found it - delete it */
+ if (count > 1 && i < count-1) {
+ memmove(&locks[i], &locks[i+1],
+ sizeof(*locks)*((count-1) - i));
+ }
+ count--;
+ i--;
+ dcount++;
+ }
+ }
+
+ if (count == 0) {
+ tdb_delete(tdb, kbuf);
+ } else if (count < (dbuf.dsize / sizeof(*locks))) {
+ dbuf.dsize -= dcount * sizeof(*locks);
+ tdb_store(tdb, kbuf, dbuf, TDB_REPLACE);
+ }
+
+ /* we didn't find it */
+ fail:
+ SAFE_FREE(dbuf.dptr);
+ tdb_chainunlock(tdb, kbuf);
+}
+
+/****************************************************************************
+ Traverse the whole database with this function, calling traverse_callback
+ on each lock.
+****************************************************************************/
+
+static int traverse_fn(TDB_CONTEXT *ttdb, TDB_DATA kbuf, TDB_DATA dbuf, void *state)
+{
+ struct lock_struct *locks;
+ struct lock_key *key;
+ int i;
+
+ BRLOCK_FN(traverse_callback) = (BRLOCK_FN_CAST())state;
+
+ locks = (struct lock_struct *)dbuf.dptr;
+ key = (struct lock_key *)kbuf.dptr;
+
+ for (i=0;i<dbuf.dsize/sizeof(*locks);i++) {
+ traverse_callback(key->device, key->inode,
+ locks[i].context.pid,
+ locks[i].lock_type,
+ locks[i].start,
+ locks[i].size);
+ }
+ return 0;
+}
+
+/*******************************************************************
+ Call the specified function on each lock in the database.
+********************************************************************/
+
+int brl_forall(BRLOCK_FN(fn))
+{
+ if (!tdb) return 0;
+ return tdb_traverse(tdb, traverse_fn, (void *)fn);
+}
diff --git a/source/locking/locking.c b/source/locking/locking.c
new file mode 100644
index 00000000000..42036cc70cf
--- /dev/null
+++ b/source/locking/locking.c
@@ -0,0 +1,881 @@
+/*
+ Unix SMB/CIFS implementation.
+ Locking functions
+ Copyright (C) Andrew Tridgell 1992-2000
+ 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 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.
+
+ Revision History:
+
+ 12 aug 96: Erik.Devriendt@te6.siemens.be
+ added support for shared memory implementation of share mode locking
+
+ May 1997. Jeremy Allison (jallison@whistle.com). Modified share mode
+ locking to deal with multiple share modes per open file.
+
+ September 1997. Jeremy Allison (jallison@whistle.com). Added oplock
+ support.
+
+ rewrtten completely to use new tdb code. Tridge, Dec '99
+
+ Added POSIX locking support. Jeremy Allison (jeremy@valinux.com), Apr. 2000.
+*/
+
+#include "includes.h"
+uint16 global_smbpid;
+
+/* the locking database handle */
+static TDB_CONTEXT *tdb;
+
+/****************************************************************************
+ Debugging aid :-).
+****************************************************************************/
+
+static const char *lock_type_name(enum brl_type lock_type)
+{
+ return (lock_type == READ_LOCK) ? "READ" : "WRITE";
+}
+
+/****************************************************************************
+ Utility function called to see if a file region is locked.
+ If check_self is True, then checks on our own fd with the same locking context
+ are still made. If check_self is False, then checks are not made on our own fd
+ with the same locking context are not made.
+****************************************************************************/
+
+BOOL is_locked(files_struct *fsp,connection_struct *conn,
+ SMB_BIG_UINT count,SMB_BIG_UINT offset,
+ enum brl_type lock_type, BOOL check_self)
+{
+ int snum = SNUM(conn);
+ BOOL ret;
+
+ if (count == 0)
+ return(False);
+
+ if (!lp_locking(snum) || !lp_strict_locking(snum))
+ return(False);
+
+ ret = !brl_locktest(fsp->dev, fsp->inode, fsp->fnum,
+ global_smbpid, sys_getpid(), conn->cnum,
+ offset, count, lock_type, check_self);
+
+ DEBUG(10,("is_locked: brl start=%.0f len=%.0f %s for file %s\n",
+ (double)offset, (double)count, ret ? "locked" : "unlocked",
+ fsp->fsp_name ));
+
+ /*
+ * There is no lock held by an SMB daemon, check to
+ * see if there is a POSIX lock from a UNIX or NFS process.
+ */
+
+ if(!ret && lp_posix_locking(snum)) {
+ ret = is_posix_locked(fsp, offset, count, lock_type);
+
+ DEBUG(10,("is_locked: posix start=%.0f len=%.0f %s for file %s\n",
+ (double)offset, (double)count, ret ? "locked" : "unlocked",
+ fsp->fsp_name ));
+ }
+
+ return ret;
+}
+
+/****************************************************************************
+ Utility function called by locking requests.
+****************************************************************************/
+
+static NTSTATUS do_lock(files_struct *fsp,connection_struct *conn, uint16 lock_pid,
+ SMB_BIG_UINT count,SMB_BIG_UINT offset,enum brl_type lock_type, BOOL *my_lock_ctx)
+{
+ NTSTATUS status = NT_STATUS_LOCK_NOT_GRANTED;
+
+ if (!lp_locking(SNUM(conn)))
+ return NT_STATUS_OK;
+
+ /* NOTE! 0 byte long ranges ARE allowed and should be stored */
+
+ DEBUG(10,("do_lock: lock type %s start=%.0f len=%.0f requested for file %s\n",
+ lock_type_name(lock_type), (double)offset, (double)count, fsp->fsp_name ));
+
+ if (OPEN_FSP(fsp) && fsp->can_lock && (fsp->conn == conn)) {
+ status = brl_lock(fsp->dev, fsp->inode, fsp->fnum,
+ lock_pid, sys_getpid(), conn->cnum,
+ offset, count,
+ lock_type, my_lock_ctx);
+
+ if (NT_STATUS_IS_OK(status) && lp_posix_locking(SNUM(conn))) {
+
+ /*
+ * Try and get a POSIX lock on this range.
+ * Note that this is ok if it is a read lock
+ * overlapping on a different fd. JRA.
+ */
+
+ if (!set_posix_lock(fsp, offset, count, lock_type)) {
+ if (errno == EACCES || errno == EAGAIN)
+ status = NT_STATUS_FILE_LOCK_CONFLICT;
+ else
+ status = map_nt_error_from_unix(errno);
+
+ /*
+ * We failed to map - we must now remove the brl
+ * lock entry.
+ */
+ (void)brl_unlock(fsp->dev, fsp->inode, fsp->fnum,
+ lock_pid, sys_getpid(), conn->cnum,
+ offset, count, False,
+ NULL, NULL);
+ }
+ }
+ }
+
+ return status;
+}
+
+/****************************************************************************
+ Utility function called by locking requests. This is *DISGUSTING*. It also
+ appears to be "What Windows Does" (tm). Andrew, ever wonder why Windows 2000
+ is so slow on the locking tests...... ? This is the reason. Much though I hate
+ it, we need this. JRA.
+****************************************************************************/
+
+NTSTATUS do_lock_spin(files_struct *fsp,connection_struct *conn, uint16 lock_pid,
+ SMB_BIG_UINT count,SMB_BIG_UINT offset,enum brl_type lock_type, BOOL *my_lock_ctx)
+{
+ int j, maxj = lp_lock_spin_count();
+ int sleeptime = lp_lock_sleep_time();
+ NTSTATUS status, ret;
+
+ if (maxj <= 0)
+ maxj = 1;
+
+ ret = NT_STATUS_OK; /* to keep dumb compilers happy */
+
+ for (j = 0; j < maxj; j++) {
+ status = do_lock(fsp, conn, lock_pid, count, offset, lock_type, my_lock_ctx);
+ if (!NT_STATUS_EQUAL(status, NT_STATUS_LOCK_NOT_GRANTED) &&
+ !NT_STATUS_EQUAL(status, NT_STATUS_FILE_LOCK_CONFLICT)) {
+ return status;
+ }
+ /* if we do fail then return the first error code we got */
+ if (j == 0) {
+ ret = status;
+ /* Don't spin if we blocked ourselves. */
+ if (*my_lock_ctx)
+ return ret;
+ }
+ if (sleeptime)
+ sys_usleep(sleeptime);
+ }
+ return ret;
+}
+
+/* Struct passed to brl_unlock. */
+struct posix_unlock_data_struct {
+ files_struct *fsp;
+ SMB_BIG_UINT offset;
+ SMB_BIG_UINT count;
+};
+
+/****************************************************************************
+ Function passed to brl_unlock to allow POSIX unlock to be done first.
+****************************************************************************/
+
+static void posix_unlock(void *pre_data)
+{
+ struct posix_unlock_data_struct *pdata = (struct posix_unlock_data_struct *)pre_data;
+
+ if (lp_posix_locking(SNUM(pdata->fsp->conn)))
+ release_posix_lock(pdata->fsp, pdata->offset, pdata->count);
+}
+
+/****************************************************************************
+ Utility function called by unlocking requests.
+****************************************************************************/
+
+NTSTATUS do_unlock(files_struct *fsp,connection_struct *conn, uint16 lock_pid,
+ SMB_BIG_UINT count,SMB_BIG_UINT offset)
+{
+ BOOL ok = False;
+ struct posix_unlock_data_struct posix_data;
+
+ if (!lp_locking(SNUM(conn)))
+ return NT_STATUS_OK;
+
+ if (!OPEN_FSP(fsp) || !fsp->can_lock || (fsp->conn != conn)) {
+ return NT_STATUS_INVALID_HANDLE;
+ }
+
+ DEBUG(10,("do_unlock: unlock start=%.0f len=%.0f requested for file %s\n",
+ (double)offset, (double)count, fsp->fsp_name ));
+
+ /*
+ * Remove the existing lock record from the tdb lockdb
+ * before looking at POSIX locks. If this record doesn't
+ * match then don't bother looking to remove POSIX locks.
+ */
+
+ posix_data.fsp = fsp;
+ posix_data.offset = offset;
+ posix_data.count = count;
+
+ ok = brl_unlock(fsp->dev, fsp->inode, fsp->fnum,
+ lock_pid, sys_getpid(), conn->cnum, offset, count,
+ False, posix_unlock, (void *)&posix_data);
+
+ if (!ok) {
+ DEBUG(10,("do_unlock: returning ERRlock.\n" ));
+ return NT_STATUS_RANGE_NOT_LOCKED;
+ }
+ return NT_STATUS_OK;
+}
+
+/****************************************************************************
+ Remove any locks on this fd. Called from file_close().
+****************************************************************************/
+
+void locking_close_file(files_struct *fsp)
+{
+ pid_t pid = sys_getpid();
+
+ if (!lp_locking(SNUM(fsp->conn)))
+ return;
+
+ /*
+ * Just release all the brl locks, no need to release individually.
+ */
+
+ brl_close(fsp->dev, fsp->inode, pid, fsp->conn->cnum, fsp->fnum);
+
+ if(lp_posix_locking(SNUM(fsp->conn))) {
+
+ /*
+ * Release all the POSIX locks.
+ */
+ posix_locking_close_file(fsp);
+
+ }
+}
+
+/****************************************************************************
+ Initialise the locking functions.
+****************************************************************************/
+
+static int open_read_only;
+
+BOOL locking_init(int read_only)
+{
+ brl_init(read_only);
+
+ if (tdb)
+ return True;
+
+ tdb = tdb_open_ex(lock_path("locking.tdb"),
+ 0, TDB_DEFAULT|(read_only?0x0:TDB_CLEAR_IF_FIRST),
+ read_only?O_RDONLY:O_RDWR|O_CREAT,
+ 0644, smbd_tdb_log);
+
+ if (!tdb) {
+ DEBUG(0,("ERROR: Failed to initialise locking database\n"));
+ return False;
+ }
+
+ if (!posix_locking_init(read_only))
+ return False;
+
+ open_read_only = read_only;
+
+ return True;
+}
+
+/*******************************************************************
+ Deinitialize the share_mode management.
+******************************************************************/
+
+BOOL locking_end(void)
+{
+
+ brl_shutdown(open_read_only);
+ if (tdb) {
+
+ if (tdb_close(tdb) != 0)
+ return False;
+ }
+
+ return True;
+}
+
+/*******************************************************************
+ Form a static locking key for a dev/inode pair.
+******************************************************************/
+
+static TDB_DATA locking_key(SMB_DEV_T dev, SMB_INO_T inode)
+{
+ static struct locking_key key;
+ TDB_DATA kbuf;
+
+ memset(&key, '\0', sizeof(key));
+ key.dev = dev;
+ key.inode = inode;
+ kbuf.dptr = (char *)&key;
+ kbuf.dsize = sizeof(key);
+ return kbuf;
+}
+
+static TDB_DATA locking_key_fsp(files_struct *fsp)
+{
+ return locking_key(fsp->dev, fsp->inode);
+}
+
+/*******************************************************************
+ Lock a hash bucket entry.
+******************************************************************/
+
+BOOL lock_share_entry(connection_struct *conn,
+ SMB_DEV_T dev, SMB_INO_T inode)
+{
+ return tdb_chainlock(tdb, locking_key(dev, inode)) == 0;
+}
+
+/*******************************************************************
+ Unlock a hash bucket entry.
+******************************************************************/
+
+void unlock_share_entry(connection_struct *conn,
+ SMB_DEV_T dev, SMB_INO_T inode)
+{
+ tdb_chainunlock(tdb, locking_key(dev, inode));
+}
+
+/*******************************************************************
+ Lock a hash bucket entry. use a fsp for convenience
+******************************************************************/
+
+BOOL lock_share_entry_fsp(files_struct *fsp)
+{
+ return tdb_chainlock(tdb, locking_key(fsp->dev, fsp->inode)) == 0;
+}
+
+/*******************************************************************
+ Unlock a hash bucket entry.
+******************************************************************/
+
+void unlock_share_entry_fsp(files_struct *fsp)
+{
+ tdb_chainunlock(tdb, locking_key(fsp->dev, fsp->inode));
+}
+
+/*******************************************************************
+ Print out a share mode.
+********************************************************************/
+
+char *share_mode_str(int num, share_mode_entry *e)
+{
+ static pstring share_str;
+
+ slprintf(share_str, sizeof(share_str)-1, "share_mode_entry[%d]: \
+pid = %lu, share_mode = 0x%x, desired_access = 0x%x, port = 0x%x, type= 0x%x, file_id = %lu, dev = 0x%x, inode = %.0f",
+ num, (unsigned long)e->pid, e->share_mode, (unsigned int)e->desired_access, e->op_port, e->op_type, e->share_file_id,
+ (unsigned int)e->dev, (double)e->inode );
+
+ return share_str;
+}
+
+/*******************************************************************
+ Print out a share mode table.
+********************************************************************/
+
+static void print_share_mode_table(struct locking_data *data)
+{
+ int num_share_modes = data->u.num_share_mode_entries;
+ share_mode_entry *shares = (share_mode_entry *)(data + 1);
+ int i;
+
+ for (i = 0; i < num_share_modes; i++) {
+ share_mode_entry *entry_p = &shares[i];
+ DEBUG(10,("print_share_mode_table: %s\n", share_mode_str(i, entry_p) ));
+ }
+}
+
+/*******************************************************************
+ Get all share mode entries for a dev/inode pair.
+********************************************************************/
+
+int get_share_modes(connection_struct *conn,
+ SMB_DEV_T dev, SMB_INO_T inode,
+ share_mode_entry **pp_shares)
+{
+ TDB_DATA dbuf;
+ struct locking_data *data;
+ int num_share_modes;
+ share_mode_entry *shares = NULL;
+ TDB_DATA key = locking_key(dev, inode);
+ *pp_shares = NULL;
+
+ dbuf = tdb_fetch(tdb, key);
+ if (!dbuf.dptr)
+ return 0;
+
+ data = (struct locking_data *)dbuf.dptr;
+ num_share_modes = data->u.num_share_mode_entries;
+ if(num_share_modes) {
+ int i;
+ int del_count = 0;
+
+ shares = (share_mode_entry *)memdup(dbuf.dptr + sizeof(*data),
+ num_share_modes * sizeof(share_mode_entry));
+
+ if (!shares) {
+ SAFE_FREE(dbuf.dptr);
+ return 0;
+ }
+
+ /*
+ * Ensure that each entry has a real process attached.
+ */
+
+ for (i = 0; i < num_share_modes; ) {
+ share_mode_entry *entry_p = &shares[i];
+ if (process_exists(entry_p->pid)) {
+ DEBUG(10,("get_share_modes: %s\n", share_mode_str(i, entry_p) ));
+ i++;
+ } else {
+ DEBUG(10,("get_share_modes: deleted %s\n", share_mode_str(i, entry_p) ));
+ memcpy( &shares[i], &shares[i+1],
+ sizeof(share_mode_entry) * (num_share_modes - i - 1));
+ num_share_modes--;
+ del_count++;
+ }
+ }
+
+ /* Did we delete any ? If so, re-store in tdb. */
+ if (del_count) {
+ data->u.num_share_mode_entries = num_share_modes;
+
+ if (num_share_modes)
+ memcpy(dbuf.dptr + sizeof(*data), shares,
+ num_share_modes * sizeof(share_mode_entry));
+
+ /* The record has shrunk a bit */
+ dbuf.dsize -= del_count * sizeof(share_mode_entry);
+
+ if (tdb_store(tdb, key, dbuf, TDB_REPLACE) == -1) {
+ SAFE_FREE(shares);
+ SAFE_FREE(dbuf.dptr);
+ return 0;
+ }
+ }
+ }
+
+ SAFE_FREE(dbuf.dptr);
+ *pp_shares = shares;
+ return num_share_modes;
+}
+
+/*******************************************************************
+ Fill a share mode entry.
+********************************************************************/
+
+static void fill_share_mode(char *p, files_struct *fsp, uint16 port, uint16 op_type)
+{
+ share_mode_entry *e = (share_mode_entry *)p;
+ void *x = &e->time; /* Needed to force alignment. p may not be aligned.... */
+
+ memset(e, '\0', sizeof(share_mode_entry));
+ e->pid = sys_getpid();
+ e->share_mode = fsp->share_mode;
+ e->desired_access = fsp->desired_access;
+ e->op_port = port;
+ e->op_type = op_type;
+ memcpy(x, &fsp->open_time, sizeof(struct timeval));
+ e->share_file_id = fsp->file_id;
+ e->dev = fsp->dev;
+ e->inode = fsp->inode;
+}
+
+/*******************************************************************
+ Check if two share mode entries are identical, ignoring oplock
+ and port info and desired_access.
+********************************************************************/
+
+BOOL share_modes_identical( share_mode_entry *e1, share_mode_entry *e2)
+{
+#if 1 /* JRA PARANOIA TEST - REMOVE LATER */
+ if (e1->pid == e2->pid &&
+ e1->share_file_id == e2->share_file_id &&
+ e1->dev == e2->dev &&
+ e1->inode == e2->inode &&
+ (e1->share_mode & ~DELETE_ON_CLOSE_FLAG) != (e2->share_mode & ~DELETE_ON_CLOSE_FLAG)) {
+ DEBUG(0,("PANIC: share_modes_identical: share_mode missmatch (e1 = %u, e2 = %u). Logic error.\n",
+ (unsigned int)(e1->share_mode & ~DELETE_ON_CLOSE_FLAG),
+ (unsigned int)(e2->share_mode & ~DELETE_ON_CLOSE_FLAG) ));
+ smb_panic("PANIC: share_modes_identical logic error.\n");
+ }
+#endif
+
+ return (e1->pid == e2->pid &&
+ (e1->share_mode & ~DELETE_ON_CLOSE_FLAG) == (e2->share_mode & ~DELETE_ON_CLOSE_FLAG) &&
+ e1->dev == e2->dev &&
+ e1->inode == e2->inode &&
+ e1->share_file_id == e2->share_file_id );
+}
+
+/*******************************************************************
+ Delete a specific share mode. Return the number
+ of entries left, and a memdup'ed copy of the entry deleted (if required).
+ Ignore if no entry deleted.
+********************************************************************/
+
+ssize_t del_share_entry( SMB_DEV_T dev, SMB_INO_T inode,
+ share_mode_entry *entry, share_mode_entry **ppse)
+{
+ TDB_DATA dbuf;
+ struct locking_data *data;
+ int i, del_count=0;
+ share_mode_entry *shares;
+ ssize_t count = 0;
+ TDB_DATA key = locking_key(dev, inode);
+
+ if (ppse)
+ *ppse = NULL;
+
+ /* read in the existing share modes */
+ dbuf = tdb_fetch(tdb, key);
+ if (!dbuf.dptr)
+ return -1;
+
+ data = (struct locking_data *)dbuf.dptr;
+ shares = (share_mode_entry *)(dbuf.dptr + sizeof(*data));
+
+ /*
+ * Find any with this pid and delete it
+ * by overwriting with the rest of the data
+ * from the record.
+ */
+
+ DEBUG(10,("del_share_entry: num_share_modes = %d\n", data->u.num_share_mode_entries ));
+
+ for (i=0;i<data->u.num_share_mode_entries;) {
+ if (share_modes_identical(&shares[i], entry)) {
+ DEBUG(10,("del_share_entry: deleted %s\n",
+ share_mode_str(i, &shares[i]) ));
+ if (ppse)
+ *ppse = memdup(&shares[i], sizeof(*shares));
+ data->u.num_share_mode_entries--;
+ memmove(&shares[i], &shares[i+1],
+ dbuf.dsize - (sizeof(*data) + (i+1)*sizeof(*shares)));
+ del_count++;
+
+ DEBUG(10,("del_share_entry: deleting entry %d\n", i ));
+
+ } else {
+ i++;
+ }
+ }
+
+ if (del_count) {
+ /* the record may have shrunk a bit */
+ dbuf.dsize -= del_count * sizeof(*shares);
+
+ count = (ssize_t)data->u.num_share_mode_entries;
+
+ /* store it back in the database */
+ if (data->u.num_share_mode_entries == 0) {
+ if (tdb_delete(tdb, key) == -1)
+ count = -1;
+ } else {
+ if (tdb_store(tdb, key, dbuf, TDB_REPLACE) == -1)
+ count = -1;
+ }
+ }
+ DEBUG(10,("del_share_entry: Remaining table.\n"));
+ print_share_mode_table((struct locking_data *)dbuf.dptr);
+ SAFE_FREE(dbuf.dptr);
+ return count;
+}
+
+/*******************************************************************
+ Del the share mode of a file for this process. Return the number
+ of entries left, and a memdup'ed copy of the entry deleted.
+********************************************************************/
+
+ssize_t del_share_mode(files_struct *fsp, share_mode_entry **ppse)
+{
+ share_mode_entry entry;
+
+ /*
+ * Fake up a share_mode_entry for comparisons.
+ */
+
+ fill_share_mode((char *)&entry, fsp, 0, 0);
+ return del_share_entry(fsp->dev, fsp->inode, &entry, ppse);
+}
+
+/*******************************************************************
+ Set the share mode of a file. Return False on fail, True on success.
+********************************************************************/
+
+BOOL set_share_mode(files_struct *fsp, uint16 port, uint16 op_type)
+{
+ TDB_DATA dbuf;
+ struct locking_data *data;
+ char *p=NULL;
+ int size;
+ TDB_DATA key = locking_key_fsp(fsp);
+ BOOL ret = True;
+
+ /* read in the existing share modes if any */
+ dbuf = tdb_fetch(tdb, key);
+ if (!dbuf.dptr) {
+ size_t offset;
+ /* we'll need to create a new record */
+ pstring fname;
+
+ pstrcpy(fname, fsp->conn->connectpath);
+ pstrcat(fname, "/");
+ pstrcat(fname, fsp->fsp_name);
+
+ size = sizeof(*data) + sizeof(share_mode_entry) + strlen(fname) + 1;
+ p = (char *)malloc(size);
+ if (!p)
+ return False;
+ data = (struct locking_data *)p;
+ data->u.num_share_mode_entries = 1;
+
+ DEBUG(10,("set_share_mode: creating entry for file %s. num_share_modes = 1\n",
+ fsp->fsp_name ));
+
+ offset = sizeof(*data) + sizeof(share_mode_entry);
+ safe_strcpy(p + offset, fname, size - offset - 1);
+ fill_share_mode(p + sizeof(*data), fsp, port, op_type);
+ dbuf.dptr = p;
+ dbuf.dsize = size;
+ if (tdb_store(tdb, key, dbuf, TDB_REPLACE) == -1)
+ ret = False;
+
+ print_share_mode_table((struct locking_data *)p);
+
+ SAFE_FREE(p);
+ return ret;
+ }
+
+ /* we're adding to an existing entry - this is a bit fiddly */
+ data = (struct locking_data *)dbuf.dptr;
+
+ data->u.num_share_mode_entries++;
+
+ DEBUG(10,("set_share_mode: adding entry for file %s. new num_share_modes = %d\n",
+ fsp->fsp_name, data->u.num_share_mode_entries ));
+
+ size = dbuf.dsize + sizeof(share_mode_entry);
+ p = malloc(size);
+ if (!p) {
+ SAFE_FREE(dbuf.dptr);
+ return False;
+ }
+ memcpy(p, dbuf.dptr, sizeof(*data));
+ fill_share_mode(p + sizeof(*data), fsp, port, op_type);
+ memcpy(p + sizeof(*data) + sizeof(share_mode_entry), dbuf.dptr + sizeof(*data),
+ dbuf.dsize - sizeof(*data));
+ SAFE_FREE(dbuf.dptr);
+ dbuf.dptr = p;
+ dbuf.dsize = size;
+ if (tdb_store(tdb, key, dbuf, TDB_REPLACE) == -1)
+ ret = False;
+ print_share_mode_table((struct locking_data *)p);
+ SAFE_FREE(p);
+ return ret;
+}
+
+/*******************************************************************
+ A generic in-place modification call for share mode entries.
+********************************************************************/
+
+static BOOL mod_share_mode( SMB_DEV_T dev, SMB_INO_T inode, share_mode_entry *entry,
+ void (*mod_fn)(share_mode_entry *, SMB_DEV_T, SMB_INO_T, void *),
+ void *param)
+{
+ TDB_DATA dbuf;
+ struct locking_data *data;
+ int i;
+ share_mode_entry *shares;
+ BOOL need_store=False;
+ BOOL ret = True;
+ TDB_DATA key = locking_key(dev, inode);
+
+ /* read in the existing share modes */
+ dbuf = tdb_fetch(tdb, key);
+ if (!dbuf.dptr)
+ return False;
+
+ data = (struct locking_data *)dbuf.dptr;
+ shares = (share_mode_entry *)(dbuf.dptr + sizeof(*data));
+
+ /* find any with our pid and call the supplied function */
+ for (i=0;i<data->u.num_share_mode_entries;i++) {
+ if (share_modes_identical(entry, &shares[i])) {
+ mod_fn(&shares[i], dev, inode, param);
+ need_store=True;
+ }
+ }
+
+ /* if the mod fn was called then store it back */
+ if (need_store) {
+ if (data->u.num_share_mode_entries == 0) {
+ if (tdb_delete(tdb, key) == -1)
+ ret = False;
+ } else {
+ if (tdb_store(tdb, key, dbuf, TDB_REPLACE) == -1)
+ ret = False;
+ }
+ }
+
+ SAFE_FREE(dbuf.dptr);
+ return ret;
+}
+
+/*******************************************************************
+ Static function that actually does the work for the generic function
+ below.
+********************************************************************/
+
+static void remove_share_oplock_fn(share_mode_entry *entry, SMB_DEV_T dev, SMB_INO_T inode,
+ void *param)
+{
+ DEBUG(10,("remove_share_oplock_fn: removing oplock info for entry dev=%x ino=%.0f\n",
+ (unsigned int)dev, (double)inode ));
+ /* Delete the oplock info. */
+ entry->op_port = 0;
+ entry->op_type = NO_OPLOCK;
+}
+
+/*******************************************************************
+ Remove an oplock port and mode entry from a share mode.
+********************************************************************/
+
+BOOL remove_share_oplock(files_struct *fsp)
+{
+ share_mode_entry entry;
+ /*
+ * Fake up an entry for comparisons...
+ */
+ fill_share_mode((char *)&entry, fsp, 0, 0);
+ return mod_share_mode(fsp->dev, fsp->inode, &entry, remove_share_oplock_fn, NULL);
+}
+
+/*******************************************************************
+ Static function that actually does the work for the generic function
+ below.
+********************************************************************/
+
+static void downgrade_share_oplock_fn(share_mode_entry *entry, SMB_DEV_T dev, SMB_INO_T inode,
+ void *param)
+{
+ DEBUG(10,("downgrade_share_oplock_fn: downgrading oplock info for entry dev=%x ino=%.0f\n",
+ (unsigned int)dev, (double)inode ));
+ entry->op_type = LEVEL_II_OPLOCK;
+}
+
+/*******************************************************************
+ Downgrade a oplock type from exclusive to level II.
+********************************************************************/
+
+BOOL downgrade_share_oplock(files_struct *fsp)
+{
+ share_mode_entry entry;
+ /*
+ * Fake up an entry for comparisons...
+ */
+ fill_share_mode((char *)&entry, fsp, 0, 0);
+ return mod_share_mode(fsp->dev, fsp->inode, &entry, downgrade_share_oplock_fn, NULL);
+}
+
+/*******************************************************************
+ Get/Set the delete on close flag in a set of share modes.
+ Return False on fail, True on success.
+********************************************************************/
+
+BOOL modify_delete_flag( SMB_DEV_T dev, SMB_INO_T inode, BOOL delete_on_close)
+{
+ TDB_DATA dbuf;
+ struct locking_data *data;
+ int i;
+ share_mode_entry *shares;
+ TDB_DATA key = locking_key(dev, inode);
+
+ /* read in the existing share modes */
+ dbuf = tdb_fetch(tdb, key);
+ if (!dbuf.dptr)
+ return False;
+
+ data = (struct locking_data *)dbuf.dptr;
+ shares = (share_mode_entry *)(dbuf.dptr + sizeof(*data));
+
+ /* Set/Unset the delete on close element. */
+ for (i=0;i<data->u.num_share_mode_entries;i++,shares++) {
+ shares->share_mode = (delete_on_close ?
+ (shares->share_mode | DELETE_ON_CLOSE_FLAG) :
+ (shares->share_mode & ~DELETE_ON_CLOSE_FLAG) );
+ }
+
+ /* store it back */
+ if (data->u.num_share_mode_entries) {
+ if (tdb_store(tdb, key, dbuf, TDB_REPLACE)==-1) {
+ SAFE_FREE(dbuf.dptr);
+ return False;
+ }
+ }
+
+ SAFE_FREE(dbuf.dptr);
+ return True;
+}
+
+/****************************************************************************
+ Traverse the whole database with this function, calling traverse_callback
+ on each share mode
+****************************************************************************/
+
+static int traverse_fn(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf,
+ void* state)
+{
+ struct locking_data *data;
+ share_mode_entry *shares;
+ char *name;
+ int i;
+
+ SHAREMODE_FN(traverse_callback) = (SHAREMODE_FN_CAST())state;
+
+ data = (struct locking_data *)dbuf.dptr;
+ shares = (share_mode_entry *)(dbuf.dptr + sizeof(*data));
+ name = dbuf.dptr + sizeof(*data) + data->u.num_share_mode_entries*sizeof(*shares);
+
+ for (i=0;i<data->u.num_share_mode_entries;i++) {
+ traverse_callback(&shares[i], name);
+ }
+ return 0;
+}
+
+/*******************************************************************
+ Call the specified function on each entry under management by the
+ share mode system.
+********************************************************************/
+
+int share_mode_forall(SHAREMODE_FN(fn))
+{
+ if (!tdb)
+ return 0;
+ return tdb_traverse(tdb, traverse_fn, (void*)fn);
+}
diff --git a/source/locking/posix.c b/source/locking/posix.c
new file mode 100644
index 00000000000..6173c80b2fd
--- /dev/null
+++ b/source/locking/posix.c
@@ -0,0 +1,1312 @@
+/*
+ Unix SMB/CIFS implementation.
+ Locking functions
+ 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 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.
+
+ Revision History:
+
+ POSIX locking support. Jeremy Allison (jeremy@valinux.com), Apr. 2000.
+*/
+
+#include "includes.h"
+
+/*
+ * The POSIX locking database handle.
+ */
+
+static TDB_CONTEXT *posix_lock_tdb;
+
+/*
+ * The pending close database handle.
+ */
+
+static TDB_CONTEXT *posix_pending_close_tdb;
+
+/*
+ * The data in POSIX lock 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 posix_lock {
+ int fd;
+ SMB_OFF_T start;
+ SMB_OFF_T size;
+ int lock_type;
+};
+
+/*
+ * The data in POSIX pending close records is an unsorted linear array of int
+ * records. It is unnecessary to store the count as tdb provides the
+ * size of the record.
+ */
+
+/* The key used in both the POSIX databases. */
+
+struct posix_lock_key {
+ SMB_DEV_T device;
+ SMB_INO_T inode;
+};
+
+/*******************************************************************
+ Form a static locking key for a dev/inode pair.
+******************************************************************/
+
+static TDB_DATA locking_key(SMB_DEV_T dev, SMB_INO_T inode)
+{
+ static struct posix_lock_key key;
+ TDB_DATA kbuf;
+
+ memset(&key, '\0', sizeof(key));
+ key.device = dev;
+ key.inode = inode;
+ kbuf.dptr = (char *)&key;
+ kbuf.dsize = sizeof(key);
+ return kbuf;
+}
+
+/*******************************************************************
+ Convenience function to get a key from an fsp.
+******************************************************************/
+
+static TDB_DATA locking_key_fsp(files_struct *fsp)
+{
+ return locking_key(fsp->dev, fsp->inode);
+}
+
+/****************************************************************************
+ Add an fd to the pending close tdb.
+****************************************************************************/
+
+static BOOL add_fd_to_close_entry(files_struct *fsp)
+{
+ TDB_DATA kbuf = locking_key_fsp(fsp);
+ TDB_DATA dbuf;
+ char *tp;
+
+ dbuf.dptr = NULL;
+
+ dbuf = tdb_fetch(posix_pending_close_tdb, kbuf);
+
+ tp = Realloc(dbuf.dptr, dbuf.dsize + sizeof(int));
+ if (!tp) {
+ DEBUG(0,("add_fd_to_close_entry: Realloc fail !\n"));
+ SAFE_FREE(dbuf.dptr);
+ return False;
+ } else
+ dbuf.dptr = tp;
+
+ memcpy(dbuf.dptr + dbuf.dsize, &fsp->fd, sizeof(int));
+ dbuf.dsize += sizeof(int);
+
+ if (tdb_store(posix_pending_close_tdb, kbuf, dbuf, TDB_REPLACE) == -1) {
+ DEBUG(0,("add_fd_to_close_entry: tdb_store fail !\n"));
+ }
+
+ SAFE_FREE(dbuf.dptr);
+ return True;
+}
+
+/****************************************************************************
+ Remove all fd entries for a specific dev/inode pair from the tdb.
+****************************************************************************/
+
+static void delete_close_entries(files_struct *fsp)
+{
+ TDB_DATA kbuf = locking_key_fsp(fsp);
+
+ if (tdb_delete(posix_pending_close_tdb, kbuf) == -1)
+ DEBUG(0,("delete_close_entries: tdb_delete fail !\n"));
+}
+
+/****************************************************************************
+ Get the array of POSIX pending close records for an open fsp. Caller must
+ free. Returns number of entries.
+****************************************************************************/
+
+static size_t get_posix_pending_close_entries(files_struct *fsp, int **entries)
+{
+ TDB_DATA kbuf = locking_key_fsp(fsp);
+ TDB_DATA dbuf;
+ size_t count = 0;
+
+ *entries = NULL;
+ dbuf.dptr = NULL;
+
+ dbuf = tdb_fetch(posix_pending_close_tdb, kbuf);
+
+ if (!dbuf.dptr) {
+ return 0;
+ }
+
+ *entries = (int *)dbuf.dptr;
+ count = (size_t)(dbuf.dsize / sizeof(int));
+
+ return count;
+}
+
+/****************************************************************************
+ Get the array of POSIX locks for an fsp. Caller must free. Returns
+ number of entries.
+****************************************************************************/
+
+static size_t get_posix_lock_entries(files_struct *fsp, struct posix_lock **entries)
+{
+ TDB_DATA kbuf = locking_key_fsp(fsp);
+ TDB_DATA dbuf;
+ size_t count = 0;
+
+ *entries = NULL;
+
+ dbuf.dptr = NULL;
+
+ dbuf = tdb_fetch(posix_lock_tdb, kbuf);
+
+ if (!dbuf.dptr) {
+ return 0;
+ }
+
+ *entries = (struct posix_lock *)dbuf.dptr;
+ count = (size_t)(dbuf.dsize / sizeof(struct posix_lock));
+
+ return count;
+}
+
+/****************************************************************************
+ Deal with pending closes needed by POSIX locking support.
+ Note that posix_locking_close_file() is expected to have been called
+ to delete all locks on this fsp before this function is called.
+****************************************************************************/
+
+int fd_close_posix(struct connection_struct *conn, files_struct *fsp)
+{
+ int saved_errno = 0;
+ int ret;
+ size_t count, i;
+ struct posix_lock *entries = NULL;
+ int *fd_array = NULL;
+ BOOL locks_on_other_fds = False;
+
+ if (!lp_posix_locking(SNUM(conn))) {
+ /*
+ * No POSIX to worry about, just close.
+ */
+ ret = SMB_VFS_CLOSE(fsp,fsp->fd);
+ fsp->fd = -1;
+ return ret;
+ }
+
+ /*
+ * Get the number of outstanding POSIX locks on this dev/inode pair.
+ */
+
+ count = get_posix_lock_entries(fsp, &entries);
+
+ /*
+ * Check if there are any outstanding locks belonging to
+ * other fd's. This should never be the case if posix_locking_close_file()
+ * has been called first, but it never hurts to be *sure*.
+ */
+
+ for (i = 0; i < count; i++) {
+ if (entries[i].fd != fsp->fd) {
+ locks_on_other_fds = True;
+ break;
+ }
+ }
+
+ if (locks_on_other_fds) {
+
+ /*
+ * There are outstanding locks on this dev/inode pair on other fds.
+ * Add our fd to the pending close tdb and set fsp->fd to -1.
+ */
+
+ if (!add_fd_to_close_entry(fsp)) {
+ SAFE_FREE(entries);
+ return False;
+ }
+
+ SAFE_FREE(entries);
+ fsp->fd = -1;
+ return 0;
+ }
+
+ SAFE_FREE(entries);
+
+ /*
+ * No outstanding POSIX locks. Get the pending close fd's
+ * from the tdb and close them all.
+ */
+
+ count = get_posix_pending_close_entries(fsp, &fd_array);
+
+ if (count) {
+ DEBUG(10,("fd_close_posix: doing close on %u fd's.\n", (unsigned int)count ));
+
+ for(i = 0; i < count; i++) {
+ if (SMB_VFS_CLOSE(fsp,fd_array[i]) == -1) {
+ saved_errno = errno;
+ }
+ }
+
+ /*
+ * Delete all fd's stored in the tdb
+ * for this dev/inode pair.
+ */
+
+ delete_close_entries(fsp);
+ }
+
+ SAFE_FREE(fd_array);
+
+ /*
+ * Finally close the fd associated with this fsp.
+ */
+
+ ret = SMB_VFS_CLOSE(fsp,fsp->fd);
+
+ if (saved_errno != 0) {
+ errno = saved_errno;
+ ret = -1;
+ }
+
+ fsp->fd = -1;
+
+ return ret;
+}
+
+/****************************************************************************
+ Debugging aid :-).
+****************************************************************************/
+
+static const char *posix_lock_type_name(int lock_type)
+{
+ return (lock_type == F_RDLCK) ? "READ" : "WRITE";
+}
+
+/****************************************************************************
+ Delete a POSIX lock entry by index number. Used if the tdb add succeeds, but
+ then the POSIX fcntl lock fails.
+****************************************************************************/
+
+static BOOL delete_posix_lock_entry_by_index(files_struct *fsp, size_t entry)
+{
+ TDB_DATA kbuf = locking_key_fsp(fsp);
+ TDB_DATA dbuf;
+ struct posix_lock *locks;
+ size_t count;
+
+ dbuf.dptr = NULL;
+
+ dbuf = tdb_fetch(posix_lock_tdb, kbuf);
+
+ if (!dbuf.dptr) {
+ DEBUG(10,("delete_posix_lock_entry_by_index: tdb_fetch failed !\n"));
+ goto fail;
+ }
+
+ count = (size_t)(dbuf.dsize / sizeof(struct posix_lock));
+ locks = (struct posix_lock *)dbuf.dptr;
+
+ if (count == 1) {
+ tdb_delete(posix_lock_tdb, kbuf);
+ } else {
+ if (entry < count-1) {
+ memmove(&locks[entry], &locks[entry+1], sizeof(*locks)*((count-1) - entry));
+ }
+ dbuf.dsize -= sizeof(*locks);
+ tdb_store(posix_lock_tdb, kbuf, dbuf, TDB_REPLACE);
+ }
+
+ SAFE_FREE(dbuf.dptr);
+
+ return True;
+
+ fail:
+
+ SAFE_FREE(dbuf.dptr);
+ return False;
+}
+
+/****************************************************************************
+ Add an entry into the POSIX locking tdb. We return the index number of the
+ added lock (used in case we need to delete *exactly* this entry). Returns
+ False on fail, True on success.
+****************************************************************************/
+
+static BOOL add_posix_lock_entry(files_struct *fsp, SMB_OFF_T start, SMB_OFF_T size, int lock_type, size_t *pentry_num)
+{
+ TDB_DATA kbuf = locking_key_fsp(fsp);
+ TDB_DATA dbuf;
+ struct posix_lock pl;
+ char *tp;
+
+ dbuf.dptr = NULL;
+
+ dbuf = tdb_fetch(posix_lock_tdb, kbuf);
+
+ *pentry_num = (size_t)(dbuf.dsize / sizeof(pl));
+
+ /*
+ * Add new record.
+ */
+
+ pl.fd = fsp->fd;
+ pl.start = start;
+ pl.size = size;
+ pl.lock_type = lock_type;
+
+ tp = Realloc(dbuf.dptr, dbuf.dsize + sizeof(pl));
+ if (!tp) {
+ DEBUG(0,("add_posix_lock_entry: Realloc fail !\n"));
+ goto fail;
+ } else
+ dbuf.dptr = tp;
+
+ memcpy(dbuf.dptr + dbuf.dsize, &pl, sizeof(pl));
+ dbuf.dsize += sizeof(pl);
+
+ if (tdb_store(posix_lock_tdb, kbuf, dbuf, TDB_REPLACE) == -1) {
+ DEBUG(0,("add_posix_lock: Failed to add lock entry on file %s\n", fsp->fsp_name));
+ goto fail;
+ }
+
+ SAFE_FREE(dbuf.dptr);
+
+ DEBUG(10,("add_posix_lock: File %s: type = %s: start=%.0f size=%.0f: dev=%.0f inode=%.0f\n",
+ fsp->fsp_name, posix_lock_type_name(lock_type), (double)start, (double)size,
+ (double)fsp->dev, (double)fsp->inode ));
+
+ return True;
+
+ fail:
+
+ SAFE_FREE(dbuf.dptr);
+ return False;
+}
+
+/****************************************************************************
+ Calculate if locks have any overlap at all.
+****************************************************************************/
+
+static BOOL does_lock_overlap(SMB_OFF_T start1, SMB_OFF_T size1, SMB_OFF_T start2, SMB_OFF_T size2)
+{
+ if (start1 >= start2 && start1 <= start2 + size2)
+ return True;
+
+ if (start1 < start2 && start1 + size1 > start2)
+ return True;
+
+ return False;
+}
+
+/****************************************************************************
+ Delete an entry from the POSIX locking tdb. Returns a copy of the entry being
+ deleted and the number of records that are overlapped by this one, or -1 on error.
+****************************************************************************/
+
+static int delete_posix_lock_entry(files_struct *fsp, SMB_OFF_T start, SMB_OFF_T size, struct posix_lock *pl)
+{
+ TDB_DATA kbuf = locking_key_fsp(fsp);
+ TDB_DATA dbuf;
+ struct posix_lock *locks;
+ size_t i, count;
+ BOOL found = False;
+ int num_overlapping_records = 0;
+
+ dbuf.dptr = NULL;
+
+ dbuf = tdb_fetch(posix_lock_tdb, kbuf);
+
+ if (!dbuf.dptr) {
+ DEBUG(10,("delete_posix_lock_entry: tdb_fetch failed !\n"));
+ goto fail;
+ }
+
+ /* There are existing locks - find a match. */
+ locks = (struct posix_lock *)dbuf.dptr;
+ count = (size_t)(dbuf.dsize / sizeof(*locks));
+
+ /*
+ * Search for and delete the first record that matches the
+ * unlock criteria.
+ */
+
+ for (i=0; i<count; i++) {
+ struct posix_lock *entry = &locks[i];
+
+ if (entry->fd == fsp->fd &&
+ entry->start == start &&
+ entry->size == size) {
+
+ /* Make a copy if requested. */
+ if (pl)
+ *pl = *entry;
+
+ /* Found it - delete it. */
+ if (count == 1) {
+ tdb_delete(posix_lock_tdb, kbuf);
+ } else {
+ if (i < count-1) {
+ memmove(&locks[i], &locks[i+1], sizeof(*locks)*((count-1) - i));
+ }
+ dbuf.dsize -= sizeof(*locks);
+ tdb_store(posix_lock_tdb, kbuf, dbuf, TDB_REPLACE);
+ }
+ count--;
+ found = True;
+ break;
+ }
+ }
+
+ if (!found)
+ goto fail;
+
+ /*
+ * Count the number of entries that are
+ * overlapped by this unlock request.
+ */
+
+ for (i = 0; i < count; i++) {
+ struct posix_lock *entry = &locks[i];
+
+ if (fsp->fd == entry->fd &&
+ does_lock_overlap( start, size, entry->start, entry->size))
+ num_overlapping_records++;
+ }
+
+ DEBUG(10,("delete_posix_lock_entry: type = %s: start=%.0f size=%.0f, num_records = %d\n",
+ posix_lock_type_name(pl->lock_type), (double)pl->start, (double)pl->size,
+ (unsigned int)num_overlapping_records ));
+
+ SAFE_FREE(dbuf.dptr);
+
+ return num_overlapping_records;
+
+ fail:
+
+ SAFE_FREE(dbuf.dptr);
+ return -1;
+}
+
+/****************************************************************************
+ Utility function to map a lock type correctly depending on the open
+ mode of a file.
+****************************************************************************/
+
+static int map_posix_lock_type( files_struct *fsp, enum brl_type lock_type)
+{
+ if((lock_type == WRITE_LOCK) && !fsp->can_write) {
+ /*
+ * Many UNIX's cannot get a write lock on a file opened read-only.
+ * Win32 locking semantics allow this.
+ * Do the best we can and attempt a read-only lock.
+ */
+ DEBUG(10,("map_posix_lock_type: Downgrading write lock to read due to read-only file.\n"));
+ return F_RDLCK;
+ } else if((lock_type == READ_LOCK) && !fsp->can_read) {
+ /*
+ * Ditto for read locks on write only files.
+ */
+ DEBUG(10,("map_posix_lock_type: Changing read lock to write due to write-only file.\n"));
+ return F_WRLCK;
+ }
+
+ /*
+ * This return should be the most normal, as we attempt
+ * to always open files read/write.
+ */
+
+ return (lock_type == READ_LOCK) ? F_RDLCK : F_WRLCK;
+}
+
+/****************************************************************************
+ Check to see if the given unsigned lock range is within the possible POSIX
+ range. Modifies the given args to be in range if possible, just returns
+ False if not.
+****************************************************************************/
+
+static BOOL posix_lock_in_range(SMB_OFF_T *offset_out, SMB_OFF_T *count_out,
+ SMB_BIG_UINT u_offset, SMB_BIG_UINT u_count)
+{
+ SMB_OFF_T offset = (SMB_OFF_T)u_offset;
+ SMB_OFF_T count = (SMB_OFF_T)u_count;
+
+ /*
+ * For the type of system we are, attempt to
+ * find the maximum positive lock offset as an SMB_OFF_T.
+ */
+
+#if defined(MAX_POSITIVE_LOCK_OFFSET) /* Some systems have arbitrary limits. */
+
+ SMB_OFF_T max_positive_lock_offset = (MAX_POSITIVE_LOCK_OFFSET);
+
+#elif defined(LARGE_SMB_OFF_T) && !defined(HAVE_BROKEN_FCNTL64_LOCKS)
+
+ /*
+ * In this case SMB_OFF_T is 64 bits,
+ * and the underlying system can handle 64 bit signed locks.
+ */
+
+ SMB_OFF_T mask2 = ((SMB_OFF_T)0x4) << (SMB_OFF_T_BITS-4);
+ SMB_OFF_T mask = (mask2<<1);
+ SMB_OFF_T max_positive_lock_offset = ~mask;
+
+#else /* !LARGE_SMB_OFF_T || HAVE_BROKEN_FCNTL64_LOCKS */
+
+ /*
+ * In this case either SMB_OFF_T is 32 bits,
+ * or the underlying system cannot handle 64 bit signed locks.
+ * All offsets & counts must be 2^31 or less.
+ */
+
+ SMB_OFF_T max_positive_lock_offset = 0x7FFFFFFF;
+
+#endif /* !LARGE_SMB_OFF_T || HAVE_BROKEN_FCNTL64_LOCKS */
+
+ /*
+ * POSIX locks of length zero mean lock to end-of-file.
+ * Win32 locks of length zero are point probes. Ignore
+ * any Win32 locks of length zero. JRA.
+ */
+
+ if (count == (SMB_OFF_T)0) {
+ DEBUG(10,("posix_lock_in_range: count = 0, ignoring.\n"));
+ return False;
+ }
+
+ /*
+ * If the given offset was > max_positive_lock_offset then we cannot map this at all
+ * ignore this lock.
+ */
+
+ if (u_offset & ~((SMB_BIG_UINT)max_positive_lock_offset)) {
+ DEBUG(10,("posix_lock_in_range: (offset = %.0f) offset > %.0f and we cannot handle this. Ignoring lock.\n",
+ (double)u_offset, (double)((SMB_BIG_UINT)max_positive_lock_offset) ));
+ return False;
+ }
+
+ /*
+ * We must truncate the count to less than max_positive_lock_offset.
+ */
+
+ if (u_count & ~((SMB_BIG_UINT)max_positive_lock_offset))
+ count = max_positive_lock_offset;
+
+ /*
+ * Truncate count to end at max lock offset.
+ */
+
+ if (offset + count < 0 || offset + count > max_positive_lock_offset)
+ count = max_positive_lock_offset - offset;
+
+ /*
+ * If we ate all the count, ignore this lock.
+ */
+
+ if (count == 0) {
+ DEBUG(10,("posix_lock_in_range: Count = 0. Ignoring lock u_offset = %.0f, u_count = %.0f\n",
+ (double)u_offset, (double)u_count ));
+ return False;
+ }
+
+ /*
+ * The mapping was successful.
+ */
+
+ DEBUG(10,("posix_lock_in_range: offset_out = %.0f, count_out = %.0f\n",
+ (double)offset, (double)count ));
+
+ *offset_out = offset;
+ *count_out = count;
+
+ return True;
+}
+
+/****************************************************************************
+ Actual function that does POSIX locks. Copes with 64 -> 32 bit cruft and
+ broken NFS implementations.
+****************************************************************************/
+
+static BOOL posix_fcntl_lock(files_struct *fsp, int op, SMB_OFF_T offset, SMB_OFF_T count, int type)
+{
+ int ret;
+
+ DEBUG(8,("posix_fcntl_lock %d %d %.0f %.0f %d\n",fsp->fd,op,(double)offset,(double)count,type));
+
+ ret = SMB_VFS_LOCK(fsp,fsp->fd,op,offset,count,type);
+
+ if (!ret && ((errno == EFBIG) || (errno == ENOLCK) || (errno == EINVAL))) {
+
+ DEBUG(0,("posix_fcntl_lock: WARNING: lock request at offset %.0f, length %.0f returned\n",
+ (double)offset,(double)count));
+ DEBUG(0,("an %s error. This can happen when using 64 bit lock offsets\n", strerror(errno)));
+ DEBUG(0,("on 32 bit NFS mounted file systems.\n"));
+
+ /*
+ * If the offset is > 0x7FFFFFFF then this will cause problems on
+ * 32 bit NFS mounted filesystems. Just ignore it.
+ */
+
+ if (offset & ~((SMB_OFF_T)0x7fffffff)) {
+ DEBUG(0,("Offset greater than 31 bits. Returning success.\n"));
+ return True;
+ }
+
+ if (count & ~((SMB_OFF_T)0x7fffffff)) {
+ /* 32 bit NFS file system, retry with smaller offset */
+ DEBUG(0,("Count greater than 31 bits - retrying with 31 bit truncated length.\n"));
+ errno = 0;
+ count &= 0x7fffffff;
+ ret = SMB_VFS_LOCK(fsp,fsp->fd,op,offset,count,type);
+ }
+ }
+
+ DEBUG(8,("posix_fcntl_lock: Lock call %s\n", ret ? "successful" : "failed"));
+
+ return ret;
+}
+
+/****************************************************************************
+ POSIX function to see if a file region is locked. Returns True if the
+ region is locked, False otherwise.
+****************************************************************************/
+
+BOOL is_posix_locked(files_struct *fsp, SMB_BIG_UINT u_offset, SMB_BIG_UINT u_count, enum brl_type lock_type)
+{
+ SMB_OFF_T offset;
+ SMB_OFF_T count;
+ int posix_lock_type = map_posix_lock_type(fsp,lock_type);
+
+ DEBUG(10,("is_posix_locked: File %s, offset = %.0f, count = %.0f, type = %s\n",
+ fsp->fsp_name, (double)u_offset, (double)u_count, posix_lock_type_name(lock_type) ));
+
+ /*
+ * If the requested lock won't fit in the POSIX range, we will
+ * never set it, so presume it is not locked.
+ */
+
+ if(!posix_lock_in_range(&offset, &count, u_offset, u_count))
+ return False;
+
+ /*
+ * Note that most UNIX's can *test* for a write lock on
+ * a read-only fd, just not *set* a write lock on a read-only
+ * fd. So we don't need to use map_lock_type here.
+ */
+
+ return posix_fcntl_lock(fsp,SMB_F_GETLK,offset,count,posix_lock_type);
+}
+
+/*
+ * Structure used when splitting a lock range
+ * into a POSIX lock range. Doubly linked list.
+ */
+
+struct lock_list {
+ struct lock_list *next;
+ struct lock_list *prev;
+ SMB_OFF_T start;
+ SMB_OFF_T size;
+};
+
+/****************************************************************************
+ Create a list of lock ranges that don't overlap a given range. Used in calculating
+ POSIX locks and unlocks. This is a difficult function that requires ASCII art to
+ understand it :-).
+****************************************************************************/
+
+static struct lock_list *posix_lock_list(TALLOC_CTX *ctx, struct lock_list *lhead, files_struct *fsp)
+{
+ TDB_DATA kbuf = locking_key_fsp(fsp);
+ TDB_DATA dbuf;
+ struct posix_lock *locks;
+ size_t num_locks, i;
+
+ dbuf.dptr = NULL;
+
+ dbuf = tdb_fetch(posix_lock_tdb, kbuf);
+
+ if (!dbuf.dptr)
+ return lhead;
+
+ locks = (struct posix_lock *)dbuf.dptr;
+ num_locks = (size_t)(dbuf.dsize / sizeof(*locks));
+
+ /*
+ * Check the current lock list on this dev/inode pair.
+ * Quit if the list is deleted.
+ */
+
+ DEBUG(10,("posix_lock_list: curr: start=%.0f,size=%.0f\n",
+ (double)lhead->start, (double)lhead->size ));
+
+ for (i=0; i<num_locks && lhead; i++) {
+
+ struct posix_lock *lock = &locks[i];
+ struct lock_list *l_curr;
+
+ /*
+ * Walk the lock list, checking for overlaps. Note that
+ * the lock list can expand within this loop if the current
+ * range being examined needs to be split.
+ */
+
+ for (l_curr = lhead; l_curr;) {
+
+ DEBUG(10,("posix_lock_list: lock: fd=%d: start=%.0f,size=%.0f:type=%s", lock->fd,
+ (double)lock->start, (double)lock->size, posix_lock_type_name(lock->lock_type) ));
+
+ if ( (l_curr->start >= (lock->start + lock->size)) ||
+ (lock->start >= (l_curr->start + l_curr->size))) {
+
+ /* No overlap with this lock - leave this range alone. */
+/*********************************************
+ +---------+
+ | l_curr |
+ +---------+
+ +-------+
+ | lock |
+ +-------+
+OR....
+ +---------+
+ | l_curr |
+ +---------+
+**********************************************/
+
+ DEBUG(10,("no overlap case.\n" ));
+
+ l_curr = l_curr->next;
+
+ } else if ( (l_curr->start >= lock->start) &&
+ (l_curr->start + l_curr->size <= lock->start + lock->size) ) {
+
+ /*
+ * This unlock is completely overlapped by this existing lock range
+ * and thus should have no effect (not be unlocked). Delete it from the list.
+ */
+/*********************************************
+ +---------+
+ | l_curr |
+ +---------+
+ +---------------------------+
+ | lock |
+ +---------------------------+
+**********************************************/
+ /* Save the next pointer */
+ struct lock_list *ul_next = l_curr->next;
+
+ DEBUG(10,("delete case.\n" ));
+
+ DLIST_REMOVE(lhead, l_curr);
+ if(lhead == NULL)
+ break; /* No more list... */
+
+ l_curr = ul_next;
+
+ } else if ( (l_curr->start >= lock->start) &&
+ (l_curr->start < lock->start + lock->size) &&
+ (l_curr->start + l_curr->size > lock->start + lock->size) ) {
+
+ /*
+ * This unlock overlaps the existing lock range at the high end.
+ * Truncate by moving start to existing range end and reducing size.
+ */
+/*********************************************
+ +---------------+
+ | l_curr |
+ +---------------+
+ +---------------+
+ | lock |
+ +---------------+
+BECOMES....
+ +-------+
+ | l_curr|
+ +-------+
+**********************************************/
+
+ l_curr->size = (l_curr->start + l_curr->size) - (lock->start + lock->size);
+ l_curr->start = lock->start + lock->size;
+
+ DEBUG(10,("truncate high case: start=%.0f,size=%.0f\n",
+ (double)l_curr->start, (double)l_curr->size ));
+
+ l_curr = l_curr->next;
+
+ } else if ( (l_curr->start < lock->start) &&
+ (l_curr->start + l_curr->size > lock->start) &&
+ (l_curr->start + l_curr->size <= lock->start + lock->size) ) {
+
+ /*
+ * This unlock overlaps the existing lock range at the low end.
+ * Truncate by reducing size.
+ */
+/*********************************************
+ +---------------+
+ | l_curr |
+ +---------------+
+ +---------------+
+ | lock |
+ +---------------+
+BECOMES....
+ +-------+
+ | l_curr|
+ +-------+
+**********************************************/
+
+ l_curr->size = lock->start - l_curr->start;
+
+ DEBUG(10,("truncate low case: start=%.0f,size=%.0f\n",
+ (double)l_curr->start, (double)l_curr->size ));
+
+ l_curr = l_curr->next;
+
+ } else if ( (l_curr->start < lock->start) &&
+ (l_curr->start + l_curr->size > lock->start + lock->size) ) {
+ /*
+ * Worst case scenario. Unlock request completely overlaps an existing
+ * lock range. Split the request into two, push the new (upper) request
+ * into the dlink list, and continue with the entry after ul_new (as we
+ * know that ul_new will not overlap with this lock).
+ */
+/*********************************************
+ +---------------------------+
+ | l_curr |
+ +---------------------------+
+ +---------+
+ | lock |
+ +---------+
+BECOMES.....
+ +-------+ +---------+
+ | l_curr| | l_new |
+ +-------+ +---------+
+**********************************************/
+ struct lock_list *l_new = (struct lock_list *)talloc(ctx,
+ sizeof(struct lock_list));
+
+ if(l_new == NULL) {
+ DEBUG(0,("posix_lock_list: talloc fail.\n"));
+ return NULL; /* The talloc_destroy takes care of cleanup. */
+ }
+
+ ZERO_STRUCTP(l_new);
+ l_new->start = lock->start + lock->size;
+ l_new->size = l_curr->start + l_curr->size - l_new->start;
+
+ /* Truncate the l_curr. */
+ l_curr->size = lock->start - l_curr->start;
+
+ DEBUG(10,("split case: curr: start=%.0f,size=%.0f \
+new: start=%.0f,size=%.0f\n", (double)l_curr->start, (double)l_curr->size,
+ (double)l_new->start, (double)l_new->size ));
+
+ /*
+ * Add into the dlink list after the l_curr point - NOT at lhead.
+ * Note we can't use DLINK_ADD here as this inserts at the head of the given list.
+ */
+
+ l_new->prev = l_curr;
+ l_new->next = l_curr->next;
+ l_curr->next = l_new;
+
+ /* And move after the link we added. */
+ l_curr = l_new->next;
+
+ } else {
+
+ /*
+ * This logic case should never happen. Ensure this is the
+ * case by forcing an abort.... Remove in production.
+ */
+ pstring msg;
+
+ slprintf(msg, sizeof(msg)-1, "logic flaw in cases: l_curr: start = %.0f, size = %.0f : \
+lock: start = %.0f, size = %.0f\n", (double)l_curr->start, (double)l_curr->size, (double)lock->start, (double)lock->size );
+
+ smb_panic(msg);
+ }
+ } /* end for ( l_curr = lhead; l_curr;) */
+ } /* end for (i=0; i<num_locks && ul_head; i++) */
+
+ SAFE_FREE(dbuf.dptr);
+
+ return lhead;
+}
+
+/****************************************************************************
+ POSIX function to acquire a lock. Returns True if the
+ lock could be granted, False if not.
+****************************************************************************/
+
+BOOL set_posix_lock(files_struct *fsp, SMB_BIG_UINT u_offset, SMB_BIG_UINT u_count, enum brl_type lock_type)
+{
+ SMB_OFF_T offset;
+ SMB_OFF_T count;
+ BOOL ret = True;
+ size_t entry_num = 0;
+ size_t lock_count;
+ TALLOC_CTX *l_ctx = NULL;
+ struct lock_list *llist = NULL;
+ struct lock_list *ll = NULL;
+ int posix_lock_type = map_posix_lock_type(fsp,lock_type);
+
+ DEBUG(5,("set_posix_lock: File %s, offset = %.0f, count = %.0f, type = %s\n",
+ fsp->fsp_name, (double)u_offset, (double)u_count, posix_lock_type_name(lock_type) ));
+
+ /*
+ * If the requested lock won't fit in the POSIX range, we will
+ * pretend it was successful.
+ */
+
+ if(!posix_lock_in_range(&offset, &count, u_offset, u_count))
+ return True;
+
+ /*
+ * Windows is very strange. It allows read locks to be overlayed
+ * (even over a write lock), but leaves the write lock in force until the first
+ * unlock. It also reference counts the locks. This means the following sequence :
+ *
+ * process1 process2
+ * ------------------------------------------------------------------------
+ * WRITE LOCK : start = 2, len = 10
+ * READ LOCK: start =0, len = 10 - FAIL
+ * READ LOCK : start = 0, len = 14
+ * READ LOCK: start =0, len = 10 - FAIL
+ * UNLOCK : start = 2, len = 10
+ * READ LOCK: start =0, len = 10 - OK
+ *
+ * Under POSIX, the same sequence in steps 1 and 2 would not be reference counted, but
+ * would leave a single read lock over the 0-14 region. In order to
+ * re-create Windows semantics mapped to POSIX locks, we create multiple TDB
+ * entries, one for each overlayed lock request. We are guarenteed by the brlock
+ * semantics that if a write lock is added, then it will be first in the array.
+ */
+
+ if ((l_ctx = talloc_init("set_posix_lock")) == NULL) {
+ DEBUG(0,("set_posix_lock: unable to init talloc context.\n"));
+ return True; /* Not a fatal error. */
+ }
+
+ if ((ll = (struct lock_list *)talloc(l_ctx, sizeof(struct lock_list))) == NULL) {
+ DEBUG(0,("set_posix_lock: unable to talloc unlock list.\n"));
+ talloc_destroy(l_ctx);
+ return True; /* Not a fatal error. */
+ }
+
+ /*
+ * Create the initial list entry containing the
+ * lock we want to add.
+ */
+
+ ZERO_STRUCTP(ll);
+ ll->start = offset;
+ ll->size = count;
+
+ DLIST_ADD(llist, ll);
+
+ /*
+ * The following call calculates if there are any
+ * overlapping locks held by this process on
+ * fd's open on the same file and splits this list
+ * into a list of lock ranges that do not overlap with existing
+ * POSIX locks.
+ */
+
+ llist = posix_lock_list(l_ctx, llist, fsp);
+
+ /*
+ * Now we have the list of ranges to lock it is safe to add the
+ * entry into the POSIX lock tdb. We take note of the entry we
+ * added here in case we have to remove it on POSIX lock fail.
+ */
+
+ if (!add_posix_lock_entry(fsp,offset,count,posix_lock_type,&entry_num)) {
+ DEBUG(0,("set_posix_lock: Unable to create posix lock entry !\n"));
+ talloc_destroy(l_ctx);
+ return False;
+ }
+
+ /*
+ * Add the POSIX locks on the list of ranges returned.
+ * As the lock is supposed to be added atomically, we need to
+ * back out all the locks if any one of these calls fail.
+ */
+
+ for (lock_count = 0, ll = llist; ll; ll = ll->next, lock_count++) {
+ offset = ll->start;
+ count = ll->size;
+
+ DEBUG(5,("set_posix_lock: Real lock: Type = %s: offset = %.0f, count = %.0f\n",
+ posix_lock_type_name(posix_lock_type), (double)offset, (double)count ));
+
+ if (!posix_fcntl_lock(fsp,SMB_F_SETLK,offset,count,posix_lock_type)) {
+ DEBUG(5,("set_posix_lock: Lock fail !: Type = %s: offset = %.0f, count = %.0f. Errno = %s\n",
+ posix_lock_type_name(posix_lock_type), (double)offset, (double)count, strerror(errno) ));
+ ret = False;
+ break;
+ }
+ }
+
+ if (!ret) {
+
+ /*
+ * Back out all the POSIX locks we have on fail.
+ */
+
+ for (ll = llist; lock_count; ll = ll->next, lock_count--) {
+ offset = ll->start;
+ count = ll->size;
+
+ DEBUG(5,("set_posix_lock: Backing out locks: Type = %s: offset = %.0f, count = %.0f\n",
+ posix_lock_type_name(posix_lock_type), (double)offset, (double)count ));
+
+ posix_fcntl_lock(fsp,SMB_F_SETLK,offset,count,F_UNLCK);
+ }
+
+ /*
+ * Remove the tdb entry for this lock.
+ */
+
+ delete_posix_lock_entry_by_index(fsp,entry_num);
+ }
+
+ talloc_destroy(l_ctx);
+ return ret;
+}
+
+/****************************************************************************
+ POSIX function to release a lock. Returns True if the
+ lock could be released, False if not.
+****************************************************************************/
+
+BOOL release_posix_lock(files_struct *fsp, SMB_BIG_UINT u_offset, SMB_BIG_UINT u_count)
+{
+ SMB_OFF_T offset;
+ SMB_OFF_T count;
+ BOOL ret = True;
+ TALLOC_CTX *ul_ctx = NULL;
+ struct lock_list *ulist = NULL;
+ struct lock_list *ul = NULL;
+ struct posix_lock deleted_lock;
+ int num_overlapped_entries;
+
+ DEBUG(5,("release_posix_lock: File %s, offset = %.0f, count = %.0f\n",
+ fsp->fsp_name, (double)u_offset, (double)u_count ));
+
+ /*
+ * If the requested lock won't fit in the POSIX range, we will
+ * pretend it was successful.
+ */
+
+ if(!posix_lock_in_range(&offset, &count, u_offset, u_count))
+ return True;
+
+ /*
+ * We treat this as one unlock request for POSIX accounting purposes even
+ * if it may later be split into multiple smaller POSIX unlock ranges.
+ * num_overlapped_entries is the number of existing locks that have any
+ * overlap with this unlock request.
+ */
+
+ num_overlapped_entries = delete_posix_lock_entry(fsp, offset, count, &deleted_lock);
+
+ if (num_overlapped_entries == -1) {
+ smb_panic("release_posix_lock: unable find entry to delete !\n");
+ }
+
+ /*
+ * If num_overlapped_entries is > 0, and the lock_type we just deleted from the tdb was
+ * a POSIX write lock, then before doing the unlock we need to downgrade
+ * the POSIX lock to a read lock. This allows any overlapping read locks
+ * to be atomically maintained.
+ */
+
+ if (num_overlapped_entries > 0 && deleted_lock.lock_type == F_WRLCK) {
+ if (!posix_fcntl_lock(fsp,SMB_F_SETLK,offset,count,F_RDLCK)) {
+ DEBUG(0,("release_posix_lock: downgrade of lock failed with error %s !\n", strerror(errno) ));
+ return False;
+ }
+ }
+
+ if ((ul_ctx = talloc_init("release_posix_lock")) == NULL) {
+ DEBUG(0,("release_posix_lock: unable to init talloc context.\n"));
+ return True; /* Not a fatal error. */
+ }
+
+ if ((ul = (struct lock_list *)talloc(ul_ctx, sizeof(struct lock_list))) == NULL) {
+ DEBUG(0,("release_posix_lock: unable to talloc unlock list.\n"));
+ talloc_destroy(ul_ctx);
+ return True; /* Not a fatal error. */
+ }
+
+ /*
+ * Create the initial list entry containing the
+ * lock we want to remove.
+ */
+
+ ZERO_STRUCTP(ul);
+ ul->start = offset;
+ ul->size = count;
+
+ DLIST_ADD(ulist, ul);
+
+ /*
+ * The following call calculates if there are any
+ * overlapping locks held by this process on
+ * fd's open on the same file and creates a
+ * list of unlock ranges that will allow
+ * POSIX lock ranges to remain on the file whilst the
+ * unlocks are performed.
+ */
+
+ ulist = posix_lock_list(ul_ctx, ulist, fsp);
+
+ /*
+ * Release the POSIX locks on the list of ranges returned.
+ */
+
+ for(; ulist; ulist = ulist->next) {
+ offset = ulist->start;
+ count = ulist->size;
+
+ DEBUG(5,("release_posix_lock: Real unlock: offset = %.0f, count = %.0f\n",
+ (double)offset, (double)count ));
+
+ if (!posix_fcntl_lock(fsp,SMB_F_SETLK,offset,count,F_UNLCK))
+ ret = False;
+ }
+
+ talloc_destroy(ul_ctx);
+
+ return ret;
+}
+
+/****************************************************************************
+ Remove all lock entries for a specific dev/inode pair from the tdb.
+****************************************************************************/
+
+static void delete_posix_lock_entries(files_struct *fsp)
+{
+ TDB_DATA kbuf = locking_key_fsp(fsp);
+
+ if (tdb_delete(posix_lock_tdb, kbuf) == -1)
+ DEBUG(0,("delete_close_entries: tdb_delete fail !\n"));
+}
+
+/****************************************************************************
+ Debug function.
+****************************************************************************/
+
+static void dump_entry(struct posix_lock *pl)
+{
+ DEBUG(10,("entry: start=%.0f, size=%.0f, type=%d, fd=%i\n",
+ (double)pl->start, (double)pl->size, (int)pl->lock_type, pl->fd ));
+}
+
+/****************************************************************************
+ Remove any locks on this fd. Called from file_close().
+****************************************************************************/
+
+void posix_locking_close_file(files_struct *fsp)
+{
+ struct posix_lock *entries = NULL;
+ size_t count, i;
+
+ /*
+ * Optimization for the common case where we are the only
+ * opener of a file. If all fd entries are our own, we don't
+ * need to explicitly release all the locks via the POSIX functions,
+ * we can just remove all the entries in the tdb and allow the
+ * close to remove the real locks.
+ */
+
+ count = get_posix_lock_entries(fsp, &entries);
+
+ if (count == 0) {
+ DEBUG(10,("posix_locking_close_file: file %s has no outstanding locks.\n", fsp->fsp_name ));
+ return;
+ }
+
+ for (i = 0; i < count; i++) {
+ if (entries[i].fd != fsp->fd )
+ break;
+
+ dump_entry(&entries[i]);
+ }
+
+ if (i == count) {
+ /* All locks are ours. */
+ DEBUG(10,("posix_locking_close_file: file %s has %u outstanding locks, but all on one fd.\n",
+ fsp->fsp_name, (unsigned int)count ));
+ SAFE_FREE(entries);
+ delete_posix_lock_entries(fsp);
+ return;
+ }
+
+ /*
+ * Difficult case. We need to delete all our locks, whilst leaving
+ * all other POSIX locks in place.
+ */
+
+ for (i = 0; i < count; i++) {
+ struct posix_lock *pl = &entries[i];
+ if (pl->fd == fsp->fd)
+ release_posix_lock(fsp, (SMB_BIG_UINT)pl->start, (SMB_BIG_UINT)pl->size );
+ }
+ SAFE_FREE(entries);
+}
+
+/*******************************************************************
+ Create the in-memory POSIX lock databases.
+********************************************************************/
+
+BOOL posix_locking_init(int read_only)
+{
+ if (posix_lock_tdb && posix_pending_close_tdb)
+ return True;
+
+ if (!posix_lock_tdb)
+ posix_lock_tdb = tdb_open_log(NULL, 0, TDB_INTERNAL,
+ read_only?O_RDONLY:(O_RDWR|O_CREAT), 0644);
+ if (!posix_lock_tdb) {
+ DEBUG(0,("Failed to open POSIX byte range locking database.\n"));
+ return False;
+ }
+ if (!posix_pending_close_tdb)
+ posix_pending_close_tdb = tdb_open_log(NULL, 0, TDB_INTERNAL,
+ read_only?O_RDONLY:(O_RDWR|O_CREAT), 0644);
+ if (!posix_pending_close_tdb) {
+ DEBUG(0,("Failed to open POSIX pending close database.\n"));
+ return False;
+ }
+
+ return True;
+}
+
+/*******************************************************************
+ Delete the in-memory POSIX lock databases.
+********************************************************************/
+
+BOOL posix_locking_end(void)
+{
+ if (posix_lock_tdb && tdb_close(posix_lock_tdb) != 0)
+ return False;
+ if (posix_pending_close_tdb && tdb_close(posix_pending_close_tdb) != 0)
+ return False;
+ return True;
+}
diff --git a/source/mainpage.dox b/source/mainpage.dox
new file mode 100644
index 00000000000..8b72f804627
--- /dev/null
+++ b/source/mainpage.dox
@@ -0,0 +1,7 @@
+/**
+
+@mainpage
+
+@li \ref CodingSuggestions
+
+**/
diff --git a/source/modules/.cvsignore b/source/modules/.cvsignore
new file mode 100644
index 00000000000..6d609cec52b
--- /dev/null
+++ b/source/modules/.cvsignore
@@ -0,0 +1 @@
+*.po
diff --git a/source/modules/CP437.c b/source/modules/CP437.c
new file mode 100644
index 00000000000..620f6cd9b69
--- /dev/null
+++ b/source/modules/CP437.c
@@ -0,0 +1,136 @@
+/*
+ * Conversion table for CP437 charset also known as IBM437
+ *
+ * Copyright (C) Alexander Bokovoy 2003
+ *
+ * Conversion tables are generated using GNU libc 2.2.5's
+ * localedata/charmaps/IBM437 table and source/script/gen-8bit-gap.sh script
+ *
+ * This program is free software; you can 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"
+
+static const uint16 to_ucs2[256] = {
+ 0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007,
+ 0x0008, 0x0009, 0x000A, 0x000B, 0x000C, 0x000D, 0x000E, 0x000F,
+ 0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x0015, 0x0016, 0x0017,
+ 0x0018, 0x0019, 0x001A, 0x001B, 0x001C, 0x001D, 0x001E, 0x001F,
+ 0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027,
+ 0x0028, 0x0029, 0x002A, 0x002B, 0x002C, 0x002D, 0x002E, 0x002F,
+ 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037,
+ 0x0038, 0x0039, 0x003A, 0x003B, 0x003C, 0x003D, 0x003E, 0x003F,
+ 0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047,
+ 0x0048, 0x0049, 0x004A, 0x004B, 0x004C, 0x004D, 0x004E, 0x004F,
+ 0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057,
+ 0x0058, 0x0059, 0x005A, 0x005B, 0x005C, 0x005D, 0x005E, 0x005F,
+ 0x0060, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067,
+ 0x0068, 0x0069, 0x006A, 0x006B, 0x006C, 0x006D, 0x006E, 0x006F,
+ 0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077,
+ 0x0078, 0x0079, 0x007A, 0x007B, 0x007C, 0x007D, 0x007E, 0x007F,
+ 0x00C7, 0x00FC, 0x00E9, 0x00E2, 0x00E4, 0x00E0, 0x00E5, 0x00E7,
+ 0x00EA, 0x00EB, 0x00E8, 0x00EF, 0x00EE, 0x00EC, 0x00C4, 0x00C5,
+ 0x00C9, 0x00E6, 0x00C6, 0x00F4, 0x00F6, 0x00F2, 0x00FB, 0x00F9,
+ 0x00FF, 0x00D6, 0x00DC, 0x00A2, 0x00A3, 0x00A5, 0x20A7, 0x0192,
+ 0x00E1, 0x00ED, 0x00F3, 0x00FA, 0x00F1, 0x00D1, 0x00AA, 0x00BA,
+ 0x00BF, 0x2310, 0x00AC, 0x00BD, 0x00BC, 0x00A1, 0x00AB, 0x00BB,
+ 0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x2561, 0x2562, 0x2556,
+ 0x2555, 0x2563, 0x2551, 0x2557, 0x255D, 0x255C, 0x255B, 0x2510,
+ 0x2514, 0x2534, 0x252C, 0x251C, 0x2500, 0x253C, 0x255E, 0x255F,
+ 0x255A, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256C, 0x2567,
+ 0x2568, 0x2564, 0x2565, 0x2559, 0x2558, 0x2552, 0x2553, 0x256B,
+ 0x256A, 0x2518, 0x250C, 0x2588, 0x2584, 0x258C, 0x2590, 0x2580,
+ 0x03B1, 0x00DF, 0x0393, 0x03C0, 0x03A3, 0x03C3, 0x00B5, 0x03C4,
+ 0x03A6, 0x0398, 0x03A9, 0x03B4, 0x221E, 0x03C6, 0x03B5, 0x2229,
+ 0x2261, 0x00B1, 0x2265, 0x2264, 0x2320, 0x2321, 0x00F7, 0x2248,
+ 0x00B0, 0x2219, 0x00B7, 0x221A, 0x207F, 0x00B2, 0x25A0, 0x00A0,
+};
+
+static const struct charset_gap_table from_idx[] = {
+ { 0x0000, 0x007f, 0 },
+ { 0x00a0, 0x00c9, -32 },
+ { 0x00d1, 0x00ff, -39 },
+ { 0x0192, 0x0192, -185 },
+ { 0x0393, 0x0398, -697 },
+ { 0x03a3, 0x03a9, -707 },
+ { 0x03b1, 0x03b5, -714 },
+ { 0x03c0, 0x03c6, -724 },
+ { 0x207f, 0x207f, -8076 },
+ { 0x20a7, 0x20a7, -8115 },
+ { 0x2219, 0x221e, -8484 },
+ { 0x2229, 0x2229, -8494 },
+ { 0x2248, 0x2248, -8524 },
+ { 0x2261, 0x2265, -8548 },
+ { 0x2310, 0x2310, -8718 },
+ { 0x2320, 0x2321, -8733 },
+ { 0x2500, 0x2502, -9211 },
+ { 0x250c, 0x251c, -9220 },
+ { 0x2524, 0x2524, -9227 },
+ { 0x252c, 0x252c, -9234 },
+ { 0x2534, 0x2534, -9241 },
+ { 0x253c, 0x253c, -9248 },
+ { 0x2550, 0x256c, -9267 },
+ { 0x2580, 0x2593, -9286 },
+ { 0x25a0, 0x25a0, -9298 },
+ { 0xffff, 0xffff, 0 }
+};
+
+static const unsigned char from_ucs2[] = {
+
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+ 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
+ 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
+ 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27,
+ 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f,
+ 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37,
+ 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f,
+ 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47,
+ 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f,
+ 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57,
+ 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f,
+ 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67,
+ 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f,
+ 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77,
+ 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f,
+ 0xff, 0xad, 0x9b, 0x9c, 0x00, 0x9d, 0x00, 0x00,
+ 0x00, 0x00, 0xa6, 0xae, 0xaa, 0x00, 0x00, 0x00,
+ 0xf8, 0xf1, 0xfd, 0x00, 0x00, 0xe6, 0x00, 0xfa,
+ 0x00, 0x00, 0xa7, 0xaf, 0xac, 0xab, 0x00, 0xa8,
+ 0x00, 0x00, 0x00, 0x00, 0x8e, 0x8f, 0x92, 0x80,
+ 0x00, 0x90, 0xa5, 0x00, 0x00, 0x00, 0x00, 0x99,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x9a, 0x00, 0x00,
+ 0xe1, 0x85, 0xa0, 0x83, 0x00, 0x84, 0x86, 0x91,
+ 0x87, 0x8a, 0x82, 0x88, 0x89, 0x8d, 0xa1, 0x8c,
+ 0x8b, 0x00, 0xa4, 0x95, 0xa2, 0x93, 0x00, 0x94,
+ 0xf6, 0x00, 0x97, 0xa3, 0x96, 0x81, 0x00, 0x00,
+ 0x98, 0x9f, 0xe2, 0x00, 0x00, 0x00, 0x00, 0xe9,
+ 0xe4, 0x00, 0x00, 0xe8, 0x00, 0x00, 0xea, 0xe0,
+ 0x00, 0x00, 0xeb, 0xee, 0xe3, 0x00, 0x00, 0xe5,
+ 0xe7, 0x00, 0xed, 0xfc, 0x9e, 0xf9, 0xfb, 0x00,
+ 0x00, 0x00, 0xec, 0xef, 0xf7, 0xf0, 0x00, 0x00,
+ 0xf3, 0xf2, 0xa9, 0xf4, 0xf5, 0xc4, 0x00, 0xb3,
+ 0xda, 0x00, 0x00, 0x00, 0xbf, 0x00, 0x00, 0x00,
+ 0xc0, 0x00, 0x00, 0x00, 0xd9, 0x00, 0x00, 0x00,
+ 0xc3, 0xb4, 0xc2, 0xc1, 0xc5, 0xcd, 0xba, 0xd5,
+ 0xd6, 0xc9, 0xb8, 0xb7, 0xbb, 0xd4, 0xd3, 0xc8,
+ 0xbe, 0xbd, 0xbc, 0xc6, 0xc7, 0xcc, 0xb5, 0xb6,
+ 0xb9, 0xd1, 0xd2, 0xcb, 0xcf, 0xd0, 0xca, 0xd8,
+ 0xd7, 0xce, 0xdf, 0x00, 0x00, 0x00, 0xdc, 0x00,
+ 0x00, 0x00, 0xdb, 0x00, 0x00, 0x00, 0xdd, 0x00,
+ 0x00, 0x00, 0xde, 0xb0, 0xb1, 0xb2, 0xfe,
+};
+
+SMB_GENERATE_CHARSET_MODULE_8_BIT_GAP(CP437)
diff --git a/source/modules/CP850.c b/source/modules/CP850.c
new file mode 100644
index 00000000000..e6f70aa17af
--- /dev/null
+++ b/source/modules/CP850.c
@@ -0,0 +1,122 @@
+/*
+ * Conversion table for CP850 charset also known as IBM850.
+ *
+ * Copyright (C) Alexander Bokovoy 2003
+ *
+ * Conversion tables are generated using GNU libc 2.2.5's
+ * localedata/charmaps/IBM850 table and source/script/gen-8bit-gap.sh script
+ *
+ * This program is free software; you can 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"
+
+static const uint16 to_ucs2[256] = {
+ 0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007,
+ 0x0008, 0x0009, 0x000A, 0x000B, 0x000C, 0x000D, 0x000E, 0x000F,
+ 0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x0015, 0x0016, 0x0017,
+ 0x0018, 0x0019, 0x001A, 0x001B, 0x001C, 0x001D, 0x001E, 0x001F,
+ 0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027,
+ 0x0028, 0x0029, 0x002A, 0x002B, 0x002C, 0x002D, 0x002E, 0x002F,
+ 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037,
+ 0x0038, 0x0039, 0x003A, 0x003B, 0x003C, 0x003D, 0x003E, 0x003F,
+ 0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047,
+ 0x0048, 0x0049, 0x004A, 0x004B, 0x004C, 0x004D, 0x004E, 0x004F,
+ 0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057,
+ 0x0058, 0x0059, 0x005A, 0x005B, 0x005C, 0x005D, 0x005E, 0x005F,
+ 0x0060, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067,
+ 0x0068, 0x0069, 0x006A, 0x006B, 0x006C, 0x006D, 0x006E, 0x006F,
+ 0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077,
+ 0x0078, 0x0079, 0x007A, 0x007B, 0x007C, 0x007D, 0x007E, 0x007F,
+ 0x00C7, 0x00FC, 0x00E9, 0x00E2, 0x00E4, 0x00E0, 0x00E5, 0x00E7,
+ 0x00EA, 0x00EB, 0x00E8, 0x00EF, 0x00EE, 0x00EC, 0x00C4, 0x00C5,
+ 0x00C9, 0x00E6, 0x00C6, 0x00F4, 0x00F6, 0x00F2, 0x00FB, 0x00F9,
+ 0x00FF, 0x00D6, 0x00DC, 0x00F8, 0x00A3, 0x00D8, 0x00D7, 0x0192,
+ 0x00E1, 0x00ED, 0x00F3, 0x00FA, 0x00F1, 0x00D1, 0x00AA, 0x00BA,
+ 0x00BF, 0x00AE, 0x00AC, 0x00BD, 0x00BC, 0x00A1, 0x00AB, 0x00BB,
+ 0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x00C1, 0x00C2, 0x00C0,
+ 0x00A9, 0x2563, 0x2551, 0x2557, 0x255D, 0x00A2, 0x00A5, 0x2510,
+ 0x2514, 0x2534, 0x252C, 0x251C, 0x2500, 0x253C, 0x00E3, 0x00C3,
+ 0x255A, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256C, 0x00A4,
+ 0x00F0, 0x00D0, 0x00CA, 0x00CB, 0x00C8, 0x0131, 0x00CD, 0x00CE,
+ 0x00CF, 0x2518, 0x250C, 0x2588, 0x2584, 0x00A6, 0x00CC, 0x2580,
+ 0x00D3, 0x00DF, 0x00D4, 0x00D2, 0x00F5, 0x00D5, 0x00B5, 0x00FE,
+ 0x00DE, 0x00DA, 0x00DB, 0x00D9, 0x00FD, 0x00DD, 0x00AF, 0x00B4,
+ 0x00AD, 0x00B1, 0x2017, 0x00BE, 0x00B6, 0x00A7, 0x00F7, 0x00B8,
+ 0x00B0, 0x00A8, 0x00B7, 0x00B9, 0x00B3, 0x00B2, 0x25A0, 0x00A0,
+};
+
+static const struct charset_gap_table from_idx[] = {
+ /* start, end, idx */
+ { 0x0000, 0x007f, 0 },
+ { 0x00a0, 0x00ff, -32 },
+ { 0x0131, 0x0131, -81 },
+ { 0x0192, 0x0192, -177 },
+ { 0x2017, 0x2017, -7989 },
+ { 0x2500, 0x2502, -9245 },
+ { 0x250c, 0x251c, -9254 },
+ { 0x2524, 0x2524, -9261 },
+ { 0x252c, 0x252c, -9268 },
+ { 0x2534, 0x2534, -9275 },
+ { 0x253c, 0x253c, -9282 },
+ { 0x2550, 0x256c, -9301 },
+ { 0x2580, 0x2588, -9320 },
+ { 0x2591, 0x2593, -9328 },
+ { 0x25a0, 0x25a0, -9340 },
+ { 0xffff, 0xffff, 0 }
+};
+static const unsigned char from_ucs2[] = {
+
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+ 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
+ 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
+ 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27,
+ 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f,
+ 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37,
+ 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f,
+ 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47,
+ 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f,
+ 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57,
+ 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f,
+ 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67,
+ 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f,
+ 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77,
+ 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f,
+ 0xff, 0xad, 0xbd, 0x9c, 0xcf, 0xbe, 0xdd, 0xf5,
+ 0xf9, 0xb8, 0xa6, 0xae, 0xaa, 0xf0, 0xa9, 0xee,
+ 0xf8, 0xf1, 0xfd, 0xfc, 0xef, 0xe6, 0xf4, 0xfa,
+ 0xf7, 0xfb, 0xa7, 0xaf, 0xac, 0xab, 0xf3, 0xa8,
+ 0xb7, 0xb5, 0xb6, 0xc7, 0x8e, 0x8f, 0x92, 0x80,
+ 0xd4, 0x90, 0xd2, 0xd3, 0xde, 0xd6, 0xd7, 0xd8,
+ 0xd1, 0xa5, 0xe3, 0xe0, 0xe2, 0xe5, 0x99, 0x9e,
+ 0x9d, 0xeb, 0xe9, 0xea, 0x9a, 0xed, 0xe8, 0xe1,
+ 0x85, 0xa0, 0x83, 0xc6, 0x84, 0x86, 0x91, 0x87,
+ 0x8a, 0x82, 0x88, 0x89, 0x8d, 0xa1, 0x8c, 0x8b,
+ 0xd0, 0xa4, 0x95, 0xa2, 0x93, 0xe4, 0x94, 0xf6,
+ 0x9b, 0x97, 0xa3, 0x96, 0x81, 0xec, 0xe7, 0x98,
+ 0xd5, 0x9f, 0xf2, 0xc4, 0x00, 0xb3, 0xda, 0x00,
+ 0x00, 0x00, 0xbf, 0x00, 0x00, 0x00, 0xc0, 0x00,
+ 0x00, 0x00, 0xd9, 0x00, 0x00, 0x00, 0xc3, 0xb4,
+ 0xc2, 0xc1, 0xc5, 0xcd, 0xba, 0x00, 0x00, 0xc9,
+ 0x00, 0x00, 0xbb, 0x00, 0x00, 0xc8, 0x00, 0x00,
+ 0xbc, 0x00, 0x00, 0xcc, 0x00, 0x00, 0xb9, 0x00,
+ 0x00, 0xcb, 0x00, 0x00, 0xca, 0x00, 0x00, 0xce,
+ 0xdf, 0x00, 0x00, 0x00, 0xdc, 0x00, 0x00, 0x00,
+ 0xdb, 0xb0, 0xb1, 0xb2, 0xfe,
+};
+
+SMB_GENERATE_CHARSET_MODULE_8_BIT_GAP(CP850)
+
diff --git a/source/modules/charset_macosxfs.c b/source/modules/charset_macosxfs.c
new file mode 100644
index 00000000000..6f50d879ba4
--- /dev/null
+++ b/source/modules/charset_macosxfs.c
@@ -0,0 +1,602 @@
+/*
+ Unix SMB/CIFS implementation.
+ Samba charset module for Mac OS X/Darwin
+ Copyright (C) Benjamin Riefenstahl 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 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.
+*/
+
+/*
+ * modules/charset_macosxfs.c
+ *
+ * A Samba charset module to use on Mac OS X/Darwin as the filesystem
+ * and display encoding.
+ *
+ * Actually two implementations are provided here. The default
+ * implementation is based on the official CFString API. The other is
+ * based on internal CFString APIs as defined in the OpenDarwin
+ * source.
+ */
+
+#include "includes.h"
+
+/*
+ * Include OS frameworks. These are only needed in this module.
+ */
+#include <CoreFoundation/CFString.h>
+
+/*
+ * See if autoconf has found us the internal headers in some form.
+ */
+#if HAVE_COREFOUNDATION_CFSTRINGENCODINGCONVERTER_H
+# include <Corefoundation/CFStringEncodingConverter.h>
+# include <Corefoundation/CFUnicodePrecomposition.h>
+# define USE_INTERNAL_API 1
+#elif HAVE_CFSTRINGENCODINGCONVERTER_H
+# include <CFStringEncodingConverter.h>
+# include <CFUnicodePrecomposition.h>
+# define USE_INTERNAL_API 1
+#endif
+
+/*
+ * Compile time configuration: Do we want debug output?
+ */
+/* #define DEBUG_STRINGS 1 */
+
+/*
+ * A simple, but efficient memory provider for our buffers.
+ */
+static inline void *resize_buffer (void *buffer, size_t *size, size_t newsize)
+{
+ if (newsize > *size) {
+ *size = newsize + 128;
+ buffer = realloc(buffer, *size);
+ }
+ return buffer;
+}
+
+/*
+ * While there is a version of OpenDarwin for intel, the usual case is
+ * big-endian PPC. So we need byte swapping to handle the
+ * little-endian byte order of the network protocol. We also need an
+ * additional dynamic buffer to do this work for incoming data blocks,
+ * because we have to consider the original data as constant.
+ *
+ * We abstract the differences away by providing a simple facade with
+ * these functions/macros:
+ *
+ * le_to_native(dst,src,len)
+ * native_to_le(cp,len)
+ * set_ucbuffer_with_le(buffer,bufsize,data,size)
+ * set_ucbuffer_with_le_copy(buffer,bufsize,data,size,reserve)
+ */
+#ifdef WORDS_BIGENDIAN
+
+static inline void swap_bytes (char * dst, const char * src, size_t len)
+{
+ const char *srcend = src + len;
+ while (src < srcend) {
+ dst[0] = src[1];
+ dst[1] = src[0];
+ dst += 2;
+ src += 2;
+ }
+}
+static inline void swap_bytes_inplace (char * cp, size_t len)
+{
+ char temp;
+ char *end = cp + len;
+ while (cp < end) {
+ temp = cp[1];
+ cp[1] = cp[0];
+ cp[0] = temp;
+ cp += 2;
+ }
+}
+
+#define le_to_native(dst,src,len) swap_bytes(dst,src,len)
+#define native_to_le(cp,len) swap_bytes_inplace(cp,len)
+#define set_ucbuffer_with_le(buffer,bufsize,data,size) \
+ set_ucbuffer_with_le_copy(buffer,bufsize,data,size,0)
+
+#else /* ! WORDS_BIGENDIAN */
+
+#define le_to_native(dst,src,len) memcpy(dst,src,len)
+#define native_to_le(cp,len) /* nothing */
+#define set_ucbuffer_with_le(buffer,bufsize,data,size) \
+ (((void)(bufsize)),(UniChar*)(data))
+
+#endif
+
+static inline UniChar *set_ucbuffer_with_le_copy (
+ UniChar *buffer, size_t *bufsize,
+ const void *data, size_t size, size_t reserve)
+{
+ buffer = resize_buffer(buffer, bufsize, size+reserve);
+ le_to_native((char*)buffer,data,size);
+ return buffer;
+}
+
+
+/*
+ * A simple hexdump function for debugging error conditions.
+ */
+#define debug_out(s) DEBUG(0,(s))
+
+#ifdef DEBUG_STRINGS
+
+static void hexdump( const char * label, const char * s, size_t len )
+{
+ size_t restlen = len;
+ debug_out("<<<<<<<\n");
+ debug_out(label);
+ debug_out("\n");
+ while (restlen > 0) {
+ char line[100];
+ size_t i, j;
+ char * d = line;
+#undef sprintf
+ d += sprintf(d, "%04X ", (unsigned)(len-restlen));
+ *d++ = ' ';
+ for( i = 0; i<restlen && i<8; ++i ) {
+ d += sprintf(d, "%02X ", ((unsigned)s[i]) & 0xFF);
+ }
+ for( j = i; j<8; ++j ) {
+ d += sprintf(d, " ");
+ }
+ *d++ = ' ';
+ for( i = 8; i<restlen && i<16; ++i ) {
+ d += sprintf(d, "%02X ", ((unsigned)s[i]) & 0xFF);
+ }
+ for( j = i; j<16; ++j ) {
+ d += sprintf(d, " ");
+ }
+ *d++ = ' ';
+ for( i = 0; i<restlen && i<16; ++i ) {
+ if(s[i] < ' ' || s[i] >= 0x7F || !isprint(s[i]))
+ *d++ = '.';
+ else
+ *d++ = s[i];
+ }
+ *d++ = '\n';
+ *d = 0;
+ restlen -= i;
+ s += i;
+ debug_out(line);
+ }
+ debug_out(">>>>>>>\n");
+}
+
+#else /* !DEBUG_STRINGS */
+
+#define hexdump(label,s,len) /* nothing */
+
+#endif
+
+
+#if !USE_INTERNAL_API
+
+/*
+ * An implementation based on documented Mac OS X APIs.
+ *
+ * This does a certain amount of memory management, creating and
+ * manipulating CFString objects. We try to minimize the impact by
+ * keeping those objects around and re-using them. We also use
+ * external backing store for the CFStrings where this is possible and
+ * benficial.
+ *
+ * The Unicode normalizations forms available at this level are
+ * generic, not specifically for the file system. So they may not be
+ * perfect fits.
+ */
+static size_t macosxfs_encoding_pull(
+ void *cd, /* Encoder handle */
+ char **inbuf, size_t *inbytesleft, /* Script string */
+ char **outbuf, size_t *outbytesleft) /* UTF-16-LE string */
+{
+ static const int script_code = kCFStringEncodingUTF8;
+ static CFMutableStringRef cfstring = NULL;
+ size_t outsize;
+ CFRange range;
+
+ (void) cd; /* UNUSED */
+
+ if (0 == *inbytesleft) {
+ return 0;
+ }
+
+ if (NULL == cfstring) {
+ /*
+ * A version with an external backing store as in the
+ * push function should have been more efficient, but
+ * testing shows, that it is actually slower (!).
+ * Maybe kCFAllocatorDefault gets shortcut evaluation
+ * internally, while kCFAllocatorNull doesn't.
+ */
+ cfstring = CFStringCreateMutable(kCFAllocatorDefault,0);
+ }
+
+ /*
+ * Three methods of appending to a CFString, choose the most
+ * efficient.
+ */
+ if (0 == (*inbuf)[*inbytesleft-1]) {
+ CFStringAppendCString(cfstring, *inbuf, script_code);
+ } else if (*inbytesleft <= 255) {
+ Str255 buffer;
+ buffer[0] = *inbytesleft;
+ memcpy(buffer+1, *inbuf, buffer[0]);
+ CFStringAppendPascalString(cfstring, buffer, script_code);
+ } else {
+ /*
+ * We would like to use a fixed buffer and a loop
+ * here, but than we can't garantee that the input is
+ * well-formed UTF-8, as we are supposed to do.
+ */
+ static char *buffer = NULL;
+ static size_t buflen = 0;
+ buffer = resize_buffer(buffer, &buflen, *inbytesleft+1);
+ memcpy(buffer, *inbuf, *inbytesleft);
+ buffer[*inbytesleft] = 0;
+ CFStringAppendCString(cfstring, *inbuf, script_code);
+ }
+
+ /*
+ * Compose characters, using the non-canonical composition
+ * form.
+ */
+ CFStringNormalize(cfstring, kCFStringNormalizationFormC);
+
+ outsize = CFStringGetLength(cfstring);
+ range = CFRangeMake(0,outsize);
+
+ if (outsize == 0) {
+ /*
+ * HACK: smbd/mangle_hash2.c:is_legal_name() expects
+ * errors here. That function will always pass 2
+ * characters. smbd/open.c:check_for_pipe() cuts a
+ * patchname to 10 characters blindly. Suppress the
+ * debug output in those cases.
+ */
+ if(2 != *inbytesleft && 10 != *inbytesleft) {
+ debug_out("String conversion: "
+ "An unknown error occurred\n");
+ hexdump("UTF8->UTF16LE (old) input",
+ *inbuf, *inbytesleft);
+ }
+ errno = EILSEQ; /* Not sure, but this is what we have
+ * actually seen. */
+ return -1;
+ }
+ if (outsize*2 > *outbytesleft) {
+ CFStringDelete(cfstring, range);
+ debug_out("String conversion: "
+ "Output buffer too small\n");
+ hexdump("UTF8->UTF16LE (old) input",
+ *inbuf, *inbytesleft);
+ errno = E2BIG;
+ return -1;
+ }
+
+ CFStringGetCharacters(cfstring, range, (UniChar*)*outbuf);
+ CFStringDelete(cfstring, range);
+
+ native_to_le(*outbuf, outsize*2);
+
+ /*
+ * Add a converted null byte, if the CFString conversions
+ * prevented that until now.
+ */
+ if (0 == (*inbuf)[*inbytesleft-1] &&
+ (0 != (*outbuf)[outsize*2-1] || 0 != (*outbuf)[outsize*2-2])) {
+
+ if ((outsize*2+2) > *outbytesleft) {
+ debug_out("String conversion: "
+ "Output buffer too small\n");
+ hexdump("UTF8->UTF16LE (old) input",
+ *inbuf, *inbytesleft);
+ errno = E2BIG;
+ return -1;
+ }
+
+ (*outbuf)[outsize*2] = (*outbuf)[outsize*2+1] = 0;
+ outsize += 2;
+ }
+
+ *inbuf += *inbytesleft;
+ *inbytesleft = 0;
+ *outbuf += outsize*2;
+ *outbytesleft -= outsize*2;
+
+ return 0;
+}
+
+static size_t macosxfs_encoding_push(
+ void *cd, /* Encoder handle */
+ char **inbuf, size_t *inbytesleft, /* UTF-16-LE string */
+ char **outbuf, size_t *outbytesleft) /* Script string */
+{
+ static const int script_code = kCFStringEncodingUTF8;
+ static CFMutableStringRef cfstring = NULL;
+ static UniChar *buffer = NULL;
+ static size_t buflen = 0;
+ CFIndex outsize, cfsize, charsconverted;
+
+ (void) cd; /* UNUSED */
+
+ if (0 == *inbytesleft) {
+ return 0;
+ }
+
+ /*
+ * We need a buffer that can hold 4 times the original data,
+ * because that is the theoretical maximum that decomposition
+ * can create currently (in Unicode 4.0).
+ */
+ buffer = set_ucbuffer_with_le_copy(
+ buffer, &buflen, *inbuf, *inbytesleft, 3 * *inbytesleft);
+
+ if (NULL == cfstring) {
+ cfstring = CFStringCreateMutableWithExternalCharactersNoCopy(
+ kCFAllocatorDefault,
+ buffer, *inbytesleft/2, buflen/2,
+ kCFAllocatorNull);
+ } else {
+ CFStringSetExternalCharactersNoCopy(
+ cfstring,
+ buffer, *inbytesleft/2, buflen/2);
+ }
+
+ /*
+ * Decompose characters, using the non-canonical decomposition
+ * form.
+ *
+ * NB: This isn't exactly what HFS+ wants (see note on
+ * kCFStringEncodingUseHFSPlusCanonical in
+ * CFStringEncodingConverter.h), but AFAIK it's the best that
+ * the official API can do.
+ */
+ CFStringNormalize(cfstring, kCFStringNormalizationFormD);
+
+ cfsize = CFStringGetLength(cfstring);
+ charsconverted = CFStringGetBytes(
+ cfstring, CFRangeMake(0,cfsize),
+ script_code, 0, False,
+ *outbuf, *outbytesleft, &outsize);
+
+ if (0 == charsconverted) {
+ debug_out("String conversion: "
+ "Buffer too small or not convertable\n");
+ hexdump("UTF16LE->UTF8 (old) input",
+ *inbuf, *inbytesleft);
+ errno = EILSEQ; /* Probably more likely. */
+ return -1;
+ }
+
+ /*
+ * Add a converted null byte, if the CFString conversions
+ * prevented that until now.
+ */
+ if (0 == (*inbuf)[*inbytesleft-1] && 0 == (*inbuf)[*inbytesleft-2] &&
+ (0 != (*outbuf)[outsize-1])) {
+
+ if (((size_t)outsize+1) > *outbytesleft) {
+ debug_out("String conversion: "
+ "Output buffer too small\n");
+ hexdump("UTF16LE->UTF8 (old) input",
+ *inbuf, *inbytesleft);
+ errno = E2BIG;
+ return -1;
+ }
+
+ (*outbuf)[outsize] = 0;
+ ++outsize;
+ }
+
+ *inbuf += *inbytesleft;
+ *inbytesleft = 0;
+ *outbuf += outsize;
+ *outbytesleft -= outsize;
+
+ return 0;
+}
+
+#else /* USE_INTERNAL_API */
+
+/*
+ * An implementation based on internal code as known from the
+ * OpenDarwin CVS.
+ *
+ * This code doesn't need much memory management because it uses
+ * functions that operate on the raw memory directly.
+ *
+ * The push routine here is faster and more compatible with HFS+ than
+ * the other implementation above. The pull routine is only faster
+ * for some strings, slightly slower for others. The pull routine
+ * looses because it has to iterate over the data twice, once to
+ * decode UTF-8 and than to do the character composition required by
+ * Windows.
+ */
+static size_t macosxfs_encoding_pull(
+ void *cd, /* Encoder handle */
+ char **inbuf, size_t *inbytesleft, /* Script string */
+ char **outbuf, size_t *outbytesleft) /* UTF-16-LE string */
+{
+ static const int script_code = kCFStringEncodingUTF8;
+ UInt32 srcCharsUsed = 0;
+ UInt32 dstCharsUsed = 0;
+ UInt32 result;
+ uint32_t dstDecomposedUsed = 0;
+ uint32_t dstPrecomposedUsed = 0;
+
+ (void) cd; /* UNUSED */
+
+ if (0 == *inbytesleft) {
+ return 0;
+ }
+
+ result = CFStringEncodingBytesToUnicode(
+ script_code, kCFStringEncodingComposeCombinings,
+ *inbuf, *inbytesleft, &srcCharsUsed,
+ (UniChar*)*outbuf, *outbytesleft, &dstCharsUsed);
+
+ switch(result) {
+ case kCFStringEncodingConversionSuccess:
+ if (*inbytesleft == srcCharsUsed)
+ break;
+ else
+ ; /*fall through*/
+ case kCFStringEncodingInsufficientOutputBufferLength:
+ debug_out("String conversion: "
+ "Output buffer too small\n");
+ hexdump("UTF8->UTF16LE (new) input",
+ *inbuf, *inbytesleft);
+ errno = E2BIG;
+ return -1;
+ case kCFStringEncodingInvalidInputStream:
+ /*
+ * HACK: smbd/mangle_hash2.c:is_legal_name() expects
+ * errors here. That function will always pass 2
+ * characters. smbd/open.c:check_for_pipe() cuts a
+ * patchname to 10 characters blindly. Suppress the
+ * debug output in those cases.
+ */
+ if(2 != *inbytesleft && 10 != *inbytesleft) {
+ debug_out("String conversion: "
+ "Invalid input sequence\n");
+ hexdump("UTF8->UTF16LE (new) input",
+ *inbuf, *inbytesleft);
+ }
+ errno = EILSEQ;
+ return -1;
+ case kCFStringEncodingConverterUnavailable:
+ debug_out("String conversion: "
+ "Unknown encoding\n");
+ hexdump("UTF8->UTF16LE (new) input",
+ *inbuf, *inbytesleft);
+ errno = EINVAL;
+ return -1;
+ }
+
+ /*
+ * It doesn't look like CFStringEncodingBytesToUnicode() can
+ * produce precomposed characters (flags=ComposeCombinings
+ * doesn't do it), so we need another pass over the data here.
+ * We can do this in-place, as the string can only get
+ * shorter.
+ *
+ * (Actually in theory there should be an internal
+ * decomposition and reordering before the actual composition
+ * step. But we should be able to rely on that we always get
+ * fully decomposed strings for input, so this can't create
+ * problems in reality.)
+ */
+ CFUniCharPrecompose(
+ (const UTF16Char *)*outbuf, dstCharsUsed, &dstDecomposedUsed,
+ (UTF16Char *)*outbuf, dstCharsUsed, &dstPrecomposedUsed);
+
+ native_to_le(*outbuf, dstPrecomposedUsed*2);
+
+ *inbuf += srcCharsUsed;
+ *inbytesleft -= srcCharsUsed;
+ *outbuf += dstPrecomposedUsed*2;
+ *outbytesleft -= dstPrecomposedUsed*2;
+
+ return 0;
+}
+
+static size_t macosxfs_encoding_push(
+ void *cd, /* Encoder handle */
+ char **inbuf, size_t *inbytesleft, /* UTF-16-LE string */
+ char **outbuf, size_t *outbytesleft) /* Script string */
+{
+ static const int script_code = kCFStringEncodingUTF8;
+ static UniChar *buffer = NULL;
+ static size_t buflen = 0;
+ UInt32 srcCharsUsed=0, dstCharsUsed=0, result;
+
+ (void) cd; /* UNUSED */
+
+ if (0 == *inbytesleft) {
+ return 0;
+ }
+
+ buffer = set_ucbuffer_with_le(
+ buffer, &buflen, *inbuf, *inbytesleft);
+
+ result = CFStringEncodingUnicodeToBytes(
+ script_code, kCFStringEncodingUseHFSPlusCanonical,
+ buffer, *inbytesleft/2, &srcCharsUsed,
+ *outbuf, *outbytesleft, &dstCharsUsed);
+
+ switch(result) {
+ case kCFStringEncodingConversionSuccess:
+ if (*inbytesleft/2 == srcCharsUsed)
+ break;
+ else
+ ; /*fall through*/
+ case kCFStringEncodingInsufficientOutputBufferLength:
+ debug_out("String conversion: "
+ "Output buffer too small\n");
+ hexdump("UTF16LE->UTF8 (new) input",
+ *inbuf, *inbytesleft);
+ errno = E2BIG;
+ return -1;
+ case kCFStringEncodingInvalidInputStream:
+ /*
+ * HACK: smbd/open.c:check_for_pipe():is_legal_name()
+ * cuts a pathname to 10 characters blindly. Suppress
+ * the debug output in those cases.
+ */
+ if(10 != *inbytesleft) {
+ debug_out("String conversion: "
+ "Invalid input sequence\n");
+ hexdump("UTF16LE->UTF8 (new) input",
+ *inbuf, *inbytesleft);
+ }
+ errno = EILSEQ;
+ return -1;
+ case kCFStringEncodingConverterUnavailable:
+ debug_out("String conversion: "
+ "Unknown encoding\n");
+ hexdump("UTF16LE->UTF8 (new) input",
+ *inbuf, *inbytesleft);
+ errno = EINVAL;
+ return -1;
+ }
+
+ *inbuf += srcCharsUsed*2;
+ *inbytesleft -= srcCharsUsed*2;
+ *outbuf += dstCharsUsed;
+ *outbytesleft -= dstCharsUsed;
+
+ return 0;
+}
+
+#endif /* USE_INTERNAL_API */
+
+/*
+ * For initialization, actually install the encoding as "macosxfs".
+ */
+static struct charset_functions macosxfs_encoding_functions = {
+ "MACOSXFS", macosxfs_encoding_pull, macosxfs_encoding_push
+};
+
+NTSTATUS init_module(void)
+{
+ return smb_register_charset(&macosxfs_encoding_functions);
+}
+
+/* eof */
diff --git a/source/modules/developer.c b/source/modules/developer.c
new file mode 100644
index 00000000000..7ffc3ff50d2
--- /dev/null
+++ b/source/modules/developer.c
@@ -0,0 +1,132 @@
+/*
+ Unix SMB/CIFS implementation.
+ Samba module with developer tools
+ Copyright (C) Andrew Tridgell 2001
+ Copyright (C) Jelmer Vernooij 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 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"
+
+static struct {
+ char from;
+ char *to;
+ int len;
+} weird_table[] = {
+ {'q', "^q^", 3},
+ {'Q', "^Q^", 3},
+ {0, NULL}
+};
+
+static size_t weird_pull(void *cd, char **inbuf, size_t *inbytesleft,
+ char **outbuf, size_t *outbytesleft)
+{
+ while (*inbytesleft >= 1 && *outbytesleft >= 2) {
+ int i;
+ int done = 0;
+ for (i=0;weird_table[i].from;i++) {
+ if (strncmp((*inbuf),
+ weird_table[i].to,
+ weird_table[i].len) == 0) {
+ if (*inbytesleft < weird_table[i].len) {
+ DEBUG(0,("ERROR: truncated weird string\n"));
+ /* smb_panic("weird_pull"); */
+
+ } else {
+ (*outbuf)[0] = weird_table[i].from;
+ (*outbuf)[1] = 0;
+ (*inbytesleft) -= weird_table[i].len;
+ (*outbytesleft) -= 2;
+ (*inbuf) += weird_table[i].len;
+ (*outbuf) += 2;
+ done = 1;
+ break;
+ }
+ }
+ }
+ if (done) continue;
+ (*outbuf)[0] = (*inbuf)[0];
+ (*outbuf)[1] = 0;
+ (*inbytesleft) -= 1;
+ (*outbytesleft) -= 2;
+ (*inbuf) += 1;
+ (*outbuf) += 2;
+ }
+
+ if (*inbytesleft > 0) {
+ errno = E2BIG;
+ return -1;
+ }
+
+ return 0;
+}
+
+static size_t weird_push(void *cd, char **inbuf, size_t *inbytesleft,
+ char **outbuf, size_t *outbytesleft)
+{
+ int ir_count=0;
+
+ while (*inbytesleft >= 2 && *outbytesleft >= 1) {
+ int i;
+ int done=0;
+ for (i=0;weird_table[i].from;i++) {
+ if ((*inbuf)[0] == weird_table[i].from &&
+ (*inbuf)[1] == 0) {
+ if (*outbytesleft < weird_table[i].len) {
+ DEBUG(0,("No room for weird character\n"));
+ /* smb_panic("weird_push"); */
+ } else {
+ memcpy(*outbuf, weird_table[i].to,
+ weird_table[i].len);
+ (*inbytesleft) -= 2;
+ (*outbytesleft) -= weird_table[i].len;
+ (*inbuf) += 2;
+ (*outbuf) += weird_table[i].len;
+ done = 1;
+ break;
+ }
+ }
+ }
+ if (done) continue;
+
+ (*outbuf)[0] = (*inbuf)[0];
+ if ((*inbuf)[1]) ir_count++;
+ (*inbytesleft) -= 2;
+ (*outbytesleft) -= 1;
+ (*inbuf) += 2;
+ (*outbuf) += 1;
+ }
+
+ if (*inbytesleft == 1) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ if (*inbytesleft > 1) {
+ errno = E2BIG;
+ return -1;
+ }
+
+ return ir_count;
+}
+
+struct charset_functions weird_functions = {"WEIRD", weird_pull, weird_push};
+
+int charset_weird_init(void)
+{
+ smb_register_charset(&weird_functions);
+ return True;
+}
diff --git a/source/modules/getdate.c b/source/modules/getdate.c
new file mode 100644
index 00000000000..491c51294e9
--- /dev/null
+++ b/source/modules/getdate.c
@@ -0,0 +1,2460 @@
+/* A Bison parser, made by GNU Bison 1.875a. */
+
+/* Skeleton parser for Yacc-like parsing with Bison,
+ Copyright (C) 1984, 1989, 1990, 2000, 2001, 2002, 2003 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., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA. */
+
+/* As a special exception, when this file is copied by Bison into a
+ Bison output file, you may use that output file without restriction.
+ This special exception was added by the Free Software Foundation
+ in version 1.24 of Bison. */
+
+/* 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
+
+/* Skeleton name. */
+#define YYSKELETON_NAME "yacc.c"
+
+/* Pure parsers. */
+#define YYPURE 1
+
+/* 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 {
+ tAGO = 258,
+ tDST = 259,
+ tDAY = 260,
+ tDAY_UNIT = 261,
+ tDAYZONE = 262,
+ tHOUR_UNIT = 263,
+ tLOCAL_ZONE = 264,
+ tMERIDIAN = 265,
+ tMINUTE_UNIT = 266,
+ tMONTH = 267,
+ tMONTH_UNIT = 268,
+ tSEC_UNIT = 269,
+ tYEAR_UNIT = 270,
+ tZONE = 271,
+ tSNUMBER = 272,
+ tUNUMBER = 273
+ };
+#endif
+#define tAGO 258
+#define tDST 259
+#define tDAY 260
+#define tDAY_UNIT 261
+#define tDAYZONE 262
+#define tHOUR_UNIT 263
+#define tLOCAL_ZONE 264
+#define tMERIDIAN 265
+#define tMINUTE_UNIT 266
+#define tMONTH 267
+#define tMONTH_UNIT 268
+#define tSEC_UNIT 269
+#define tYEAR_UNIT 270
+#define tZONE 271
+#define tSNUMBER 272
+#define tUNUMBER 273
+
+
+
+
+/* Copy the first part of user declarations. */
+#line 1 "getdate.y"
+
+/* Parse a string into an internal time stamp.
+ Copyright (C) 1999, 2000, 2002 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
+
+/* Originally written by Steven M. Bellovin <smb@research.att.com> while
+ at the University of North Carolina at Chapel Hill. Later tweaked by
+ a couple of people on Usenet. Completely overhauled by Rich $alz
+ <rsalz@bbn.com> and Jim Berets <jberets@bbn.com> in August, 1990.
+
+ Modified by Paul Eggert <eggert@twinsun.com> in August 1999 to do
+ the right thing about local DST. Unlike previous versions, this
+ version is reentrant. */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+# ifdef HAVE_ALLOCA_H
+# include <alloca.h>
+# endif
+#endif
+
+/* Since the code of getdate.y is not included in the Emacs executable
+ itself, there is no need to #define static in this file. Even if
+ the code were included in the Emacs executable, it probably
+ wouldn't do any harm to #undef it here; this will only cause
+ problems if we try to write to a static variable, which I don't
+ think this code needs to do. */
+#ifdef emacs
+# undef static
+#endif
+
+#include <ctype.h>
+
+#if HAVE_STDLIB_H
+# include <stdlib.h> /* for `free'; used by Bison 1.27 */
+#endif
+
+#if STDC_HEADERS || (! defined isascii && ! HAVE_ISASCII)
+# define IN_CTYPE_DOMAIN(c) 1
+#else
+# define IN_CTYPE_DOMAIN(c) isascii (c)
+#endif
+
+#define ISSPACE(c) (IN_CTYPE_DOMAIN (c) && isspace (c))
+#define ISALPHA(c) (IN_CTYPE_DOMAIN (c) && isalpha (c))
+#define ISLOWER(c) (IN_CTYPE_DOMAIN (c) && islower (c))
+#define ISDIGIT_LOCALE(c) (IN_CTYPE_DOMAIN (c) && isdigit (c))
+
+/* ISDIGIT differs from ISDIGIT_LOCALE, as follows:
+ - Its arg may be any int or unsigned int; it need not be an unsigned char.
+ - It's guaranteed to evaluate its argument exactly once.
+ - It's typically faster.
+ POSIX says that only '0' through '9' are digits. Prefer ISDIGIT to
+ ISDIGIT_LOCALE unless it's important to use the locale's definition
+ of `digit' even when the host does not conform to POSIX. */
+#define ISDIGIT(c) ((unsigned) (c) - '0' <= 9)
+
+#if STDC_HEADERS || HAVE_STRING_H
+# include <string.h>
+#endif
+
+#if __GNUC__ < 2 || (__GNUC__ == 2 && __GNUC_MINOR__ < 8) || __STRICT_ANSI__
+# define __attribute__(x)
+#endif
+
+#ifndef ATTRIBUTE_UNUSED
+# define ATTRIBUTE_UNUSED __attribute__ ((__unused__))
+#endif
+
+#define EPOCH_YEAR 1970
+#define TM_YEAR_BASE 1900
+
+#define HOUR(x) ((x) * 60)
+
+/* An integer value, and the number of digits in its textual
+ representation. */
+typedef struct
+{
+ int value;
+ int digits;
+} textint;
+
+/* An entry in the lexical lookup table. */
+typedef struct
+{
+ char const *name;
+ int type;
+ int value;
+} table;
+
+/* Meridian: am, pm, or 24-hour style. */
+enum { MERam, MERpm, MER24 };
+
+/* Information passed to and from the parser. */
+typedef struct
+{
+ /* The input string remaining to be parsed. */
+ const char *input;
+
+ /* N, if this is the Nth Tuesday. */
+ int day_ordinal;
+
+ /* Day of week; Sunday is 0. */
+ int day_number;
+
+ /* tm_isdst flag for the local zone. */
+ int local_isdst;
+
+ /* Time zone, in minutes east of UTC. */
+ int time_zone;
+
+ /* Style used for time. */
+ int meridian;
+
+ /* Gregorian year, month, day, hour, minutes, and seconds. */
+ textint year;
+ int month;
+ int day;
+ int hour;
+ int minutes;
+ int seconds;
+
+ /* Relative year, month, day, hour, minutes, and seconds. */
+ int rel_year;
+ int rel_month;
+ int rel_day;
+ int rel_hour;
+ int rel_minutes;
+ int rel_seconds;
+
+ /* Counts of nonterminals of various flavors parsed so far. */
+ int dates_seen;
+ int days_seen;
+ int local_zones_seen;
+ int rels_seen;
+ int times_seen;
+ int zones_seen;
+
+ /* Table of local time zone abbrevations, terminated by a null entry. */
+ table local_time_zone_table[3];
+} parser_control;
+
+#define PC (* (parser_control *) parm)
+#define YYLEX_PARAM parm
+#define YYPARSE_PARAM parm
+
+static int yyerror ();
+static int yylex ();
+
+
+
+/* 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
+
+#if ! defined (YYSTYPE) && ! defined (YYSTYPE_IS_DECLARED)
+#line 172 "getdate.y"
+typedef union YYSTYPE {
+ int intval;
+ textint textintval;
+} YYSTYPE;
+/* Line 191 of yacc.c. */
+#line 281 "getdate.c"
+# 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 214 of yacc.c. */
+#line 293 "getdate.c"
+
+#if ! defined (yyoverflow) || YYERROR_VERBOSE
+
+/* The parser invokes alloca or malloc; define the necessary symbols. */
+
+# if YYSTACK_USE_ALLOCA
+# define YYSTACK_ALLOC alloca
+# else
+# ifndef YYSTACK_USE_ALLOCA
+# if defined (alloca) || defined (_ALLOCA_H)
+# define YYSTACK_ALLOC alloca
+# else
+# ifdef __GNUC__
+# define YYSTACK_ALLOC __builtin_alloca
+# endif
+# endif
+# endif
+# endif
+
+# ifdef YYSTACK_ALLOC
+ /* Pacify GCC's `empty if-body' warning. */
+# define YYSTACK_FREE(Ptr) do { /* empty */; } while (0)
+# else
+# if defined (__STDC__) || defined (__cplusplus)
+# include <stdlib.h> /* INFRINGES ON USER NAME SPACE */
+# define YYSIZE_T size_t
+# endif
+# define YYSTACK_ALLOC malloc
+# define YYSTACK_FREE free
+# endif
+#endif /* ! defined (yyoverflow) || YYERROR_VERBOSE */
+
+
+#if (! defined (yyoverflow) \
+ && (! defined (__cplusplus) \
+ || (YYSTYPE_IS_TRIVIAL)))
+
+/* A type that is properly aligned for any stack member. */
+union yyalloc
+{
+ short 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 (short) + sizeof (YYSTYPE)) \
+ + YYSTACK_GAP_MAXIMUM)
+
+/* Copy COUNT objects from FROM to TO. The source and destination do
+ not overlap. */
+# ifndef YYCOPY
+# if 1 < __GNUC__
+# define YYCOPY(To, From, Count) \
+ __builtin_memcpy (To, From, (Count) * sizeof (*(From)))
+# else
+# define YYCOPY(To, From, Count) \
+ do \
+ { \
+ register YYSIZE_T yyi; \
+ for (yyi = 0; yyi < (Count); yyi++) \
+ (To)[yyi] = (From)[yyi]; \
+ } \
+ while (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 (0)
+
+#endif
+
+#if defined (__STDC__) || defined (__cplusplus)
+ typedef signed char yysigned_char;
+#else
+ typedef short yysigned_char;
+#endif
+
+/* YYFINAL -- State number of the termination state. */
+#define YYFINAL 2
+/* YYLAST -- Last index in YYTABLE. */
+#define YYLAST 52
+
+/* YYNTOKENS -- Number of terminals. */
+#define YYNTOKENS 22
+/* YYNNTS -- Number of nonterminals. */
+#define YYNNTS 12
+/* YYNRULES -- Number of rules. */
+#define YYNRULES 54
+/* YYNRULES -- Number of states. */
+#define YYNSTATES 64
+
+/* YYTRANSLATE(YYLEX) -- Bison symbol number corresponding to YYLEX. */
+#define YYUNDEFTOK 2
+#define YYMAXUTOK 273
+
+#define YYTRANSLATE(YYX) \
+ ((unsigned int) (YYX) <= YYMAXUTOK ? yytranslate[YYX] : YYUNDEFTOK)
+
+/* YYTRANSLATE[YYLEX] -- Bison symbol number corresponding to YYLEX. */
+static const unsigned char 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, 20, 2, 2, 21, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 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, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 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
+};
+
+#if YYDEBUG
+/* YYPRHS[YYN] -- Index of the first RHS symbol of rule number YYN in
+ YYRHS. */
+static const unsigned char yyprhs[] =
+{
+ 0, 0, 3, 4, 7, 9, 11, 13, 15, 17,
+ 19, 21, 24, 29, 34, 41, 48, 50, 53, 55,
+ 57, 60, 62, 65, 68, 72, 78, 82, 86, 89,
+ 94, 97, 101, 104, 106, 109, 112, 114, 117, 120,
+ 122, 125, 128, 130, 133, 136, 138, 141, 144, 146,
+ 149, 152, 154, 156, 157
+};
+
+/* YYRHS -- A `-1'-separated list of the rules' RHS. */
+static const yysigned_char yyrhs[] =
+{
+ 23, 0, -1, -1, 23, 24, -1, 25, -1, 26,
+ -1, 27, -1, 29, -1, 28, -1, 30, -1, 32,
+ -1, 18, 10, -1, 18, 19, 18, 33, -1, 18,
+ 19, 18, 17, -1, 18, 19, 18, 19, 18, 33,
+ -1, 18, 19, 18, 19, 18, 17, -1, 9, -1,
+ 9, 4, -1, 16, -1, 7, -1, 16, 4, -1,
+ 5, -1, 5, 20, -1, 18, 5, -1, 18, 21,
+ 18, -1, 18, 21, 18, 21, 18, -1, 18, 17,
+ 17, -1, 18, 12, 17, -1, 12, 18, -1, 12,
+ 18, 20, 18, -1, 18, 12, -1, 18, 12, 18,
+ -1, 31, 3, -1, 31, -1, 18, 15, -1, 17,
+ 15, -1, 15, -1, 18, 13, -1, 17, 13, -1,
+ 13, -1, 18, 6, -1, 17, 6, -1, 6, -1,
+ 18, 8, -1, 17, 8, -1, 8, -1, 18, 11,
+ -1, 17, 11, -1, 11, -1, 18, 14, -1, 17,
+ 14, -1, 14, -1, 18, -1, -1, 10, -1
+};
+
+/* YYRLINE[YYN] -- source line where rule number YYN was defined. */
+static const unsigned short yyrline[] =
+{
+ 0, 188, 188, 190, 194, 196, 198, 200, 202, 204,
+ 206, 210, 217, 224, 232, 239, 251, 253, 258, 260,
+ 262, 267, 272, 277, 285, 290, 310, 317, 325, 330,
+ 336, 341, 350, 359, 363, 365, 367, 369, 371, 373,
+ 375, 377, 379, 381, 383, 385, 387, 389, 391, 393,
+ 395, 397, 402, 439, 440
+};
+#endif
+
+#if YYDEBUG || YYERROR_VERBOSE
+/* YYTNME[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", "tAGO", "tDST", "tDAY", "tDAY_UNIT",
+ "tDAYZONE", "tHOUR_UNIT", "tLOCAL_ZONE", "tMERIDIAN", "tMINUTE_UNIT",
+ "tMONTH", "tMONTH_UNIT", "tSEC_UNIT", "tYEAR_UNIT", "tZONE", "tSNUMBER",
+ "tUNUMBER", "':'", "','", "'/'", "$accept", "spec", "item", "time",
+ "local_zone", "zone", "day", "date", "rel", "relunit", "number",
+ "o_merid", 0
+};
+#endif
+
+# ifdef YYPRINT
+/* YYTOKNUM[YYLEX-NUM] -- Internal token number corresponding to
+ token YYLEX-NUM. */
+static const unsigned short yytoknum[] =
+{
+ 0, 256, 257, 258, 259, 260, 261, 262, 263, 264,
+ 265, 266, 267, 268, 269, 270, 271, 272, 273, 58,
+ 44, 47
+};
+# endif
+
+/* YYR1[YYN] -- Symbol number of symbol that rule YYN derives. */
+static const unsigned char yyr1[] =
+{
+ 0, 22, 23, 23, 24, 24, 24, 24, 24, 24,
+ 24, 25, 25, 25, 25, 25, 26, 26, 27, 27,
+ 27, 28, 28, 28, 29, 29, 29, 29, 29, 29,
+ 29, 29, 30, 30, 31, 31, 31, 31, 31, 31,
+ 31, 31, 31, 31, 31, 31, 31, 31, 31, 31,
+ 31, 31, 32, 33, 33
+};
+
+/* YYR2[YYN] -- Number of symbols composing right hand side of rule YYN. */
+static const unsigned char yyr2[] =
+{
+ 0, 2, 0, 2, 1, 1, 1, 1, 1, 1,
+ 1, 2, 4, 4, 6, 6, 1, 2, 1, 1,
+ 2, 1, 2, 2, 3, 5, 3, 3, 2, 4,
+ 2, 3, 2, 1, 2, 2, 1, 2, 2, 1,
+ 2, 2, 1, 2, 2, 1, 2, 2, 1, 2,
+ 2, 1, 1, 0, 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 unsigned char yydefact[] =
+{
+ 2, 0, 1, 21, 42, 19, 45, 16, 48, 0,
+ 39, 51, 36, 18, 0, 52, 3, 4, 5, 6,
+ 8, 7, 9, 33, 10, 22, 17, 28, 20, 41,
+ 44, 47, 38, 50, 35, 23, 40, 43, 11, 46,
+ 30, 37, 49, 34, 0, 0, 0, 32, 0, 27,
+ 31, 26, 53, 24, 29, 54, 13, 0, 12, 0,
+ 53, 25, 15, 14
+};
+
+/* YYDEFGOTO[NTERM-NUM]. */
+static const yysigned_char yydefgoto[] =
+{
+ -1, 1, 16, 17, 18, 19, 20, 21, 22, 23,
+ 24, 58
+};
+
+/* YYPACT[STATE-NUM] -- Index in YYTABLE of the portion describing
+ STATE-NUM. */
+#define YYPACT_NINF -17
+static const yysigned_char yypact[] =
+{
+ -17, 0, -17, 1, -17, -17, -17, 19, -17, -14,
+ -17, -17, -17, 32, 26, 14, -17, -17, -17, -17,
+ -17, -17, -17, 27, -17, -17, -17, 22, -17, -17,
+ -17, -17, -17, -17, -17, -17, -17, -17, -17, -17,
+ -16, -17, -17, -17, 29, 25, 30, -17, 31, -17,
+ -17, -17, 28, 23, -17, -17, -17, 33, -17, 34,
+ -7, -17, -17, -17
+};
+
+/* YYPGOTO[NTERM-NUM]. */
+static const yysigned_char yypgoto[] =
+{
+ -17, -17, -17, -17, -17, -17, -17, -17, -17, -17,
+ -17, -10
+};
+
+/* 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 unsigned char yytable[] =
+{
+ 2, 49, 50, 55, 27, 3, 4, 5, 6, 7,
+ 62, 8, 9, 10, 11, 12, 13, 14, 15, 35,
+ 36, 25, 37, 26, 38, 39, 40, 41, 42, 43,
+ 47, 44, 29, 45, 30, 46, 28, 31, 55, 32,
+ 33, 34, 48, 52, 59, 56, 51, 57, 53, 54,
+ 63, 60, 61
+};
+
+static const unsigned char yycheck[] =
+{
+ 0, 17, 18, 10, 18, 5, 6, 7, 8, 9,
+ 17, 11, 12, 13, 14, 15, 16, 17, 18, 5,
+ 6, 20, 8, 4, 10, 11, 12, 13, 14, 15,
+ 3, 17, 6, 19, 8, 21, 4, 11, 10, 13,
+ 14, 15, 20, 18, 21, 17, 17, 19, 18, 18,
+ 60, 18, 18
+};
+
+/* YYSTOS[STATE-NUM] -- The (internal number of the) accessing
+ symbol of state STATE-NUM. */
+static const unsigned char yystos[] =
+{
+ 0, 23, 0, 5, 6, 7, 8, 9, 11, 12,
+ 13, 14, 15, 16, 17, 18, 24, 25, 26, 27,
+ 28, 29, 30, 31, 32, 20, 4, 18, 4, 6,
+ 8, 11, 13, 14, 15, 5, 6, 8, 10, 11,
+ 12, 13, 14, 15, 17, 19, 21, 3, 20, 17,
+ 18, 17, 18, 18, 18, 10, 17, 19, 33, 21,
+ 18, 18, 17, 33
+};
+
+#if ! defined (YYSIZE_T) && defined (__SIZE_TYPE__)
+# define YYSIZE_T __SIZE_TYPE__
+#endif
+#if ! defined (YYSIZE_T) && defined (size_t)
+# define YYSIZE_T size_t
+#endif
+#if ! defined (YYSIZE_T)
+# if defined (__STDC__) || defined (__cplusplus)
+# include <stddef.h> /* INFRINGES ON USER NAME SPACE */
+# define YYSIZE_T size_t
+# endif
+#endif
+#if ! defined (YYSIZE_T)
+# define YYSIZE_T unsigned int
+#endif
+
+#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 yyerrlab1
+
+
+/* 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; \
+ goto yybackup; \
+ } \
+ else \
+ { \
+ yyerror ("syntax error: cannot back up");\
+ YYERROR; \
+ } \
+while (0)
+
+#define YYTERROR 1
+#define YYERRCODE 256
+
+/* YYLLOC_DEFAULT -- Compute the default location (before the actions
+ are run). */
+
+#ifndef YYLLOC_DEFAULT
+# define YYLLOC_DEFAULT(Current, Rhs, N) \
+ Current.first_line = Rhs[1].first_line; \
+ Current.first_column = Rhs[1].first_column; \
+ Current.last_line = Rhs[N].last_line; \
+ Current.last_column = Rhs[N].last_column;
+#endif
+
+/* YYLEX -- calling `yylex' with the right arguments. */
+
+#ifdef YYLEX_PARAM
+# define YYLEX yylex (&yylval, YYLEX_PARAM)
+#else
+# define YYLEX yylex (&yylval)
+#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 (0)
+
+# define YYDSYMPRINT(Args) \
+do { \
+ if (yydebug) \
+ yysymprint Args; \
+} while (0)
+
+# define YYDSYMPRINTF(Title, Token, Value, Location) \
+do { \
+ if (yydebug) \
+ { \
+ YYFPRINTF (stderr, "%s ", Title); \
+ yysymprint (stderr, \
+ Token, Value); \
+ YYFPRINTF (stderr, "\n"); \
+ } \
+} while (0)
+
+/*------------------------------------------------------------------.
+| yy_stack_print -- Print the state stack from its BOTTOM up to its |
+| TOP (cinluded). |
+`------------------------------------------------------------------*/
+
+#if defined (__STDC__) || defined (__cplusplus)
+static void
+yy_stack_print (short *bottom, short *top)
+#else
+static void
+yy_stack_print (bottom, top)
+ short *bottom;
+ short *top;
+#endif
+{
+ YYFPRINTF (stderr, "Stack now");
+ for (/* Nothing. */; 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 (0)
+
+
+/*------------------------------------------------.
+| Report that the YYRULE is going to be reduced. |
+`------------------------------------------------*/
+
+#if defined (__STDC__) || defined (__cplusplus)
+static void
+yy_reduce_print (int yyrule)
+#else
+static void
+yy_reduce_print (yyrule)
+ int yyrule;
+#endif
+{
+ int yyi;
+ unsigned int yylineno = yyrline[yyrule];
+ YYFPRINTF (stderr, "Reducing stack by rule %d (line %u), ",
+ yyrule - 1, yylineno);
+ /* Print the symbols being reduced, and their result. */
+ for (yyi = yyprhs[yyrule]; 0 <= yyrhs[yyi]; yyi++)
+ YYFPRINTF (stderr, "%s ", yytname [yyrhs[yyi]]);
+ YYFPRINTF (stderr, "-> %s\n", yytname [yyr1[yyrule]]);
+}
+
+# define YY_REDUCE_PRINT(Rule) \
+do { \
+ if (yydebug) \
+ yy_reduce_print (Rule); \
+} while (0)
+
+/* Nonzero means print parse trace. It is left uninitialized so that
+ multiple parsers can coexist. */
+int yydebug;
+#else /* !YYDEBUG */
+# define YYDPRINTF(Args)
+# define YYDSYMPRINT(Args)
+# define YYDSYMPRINTF(Title, Token, 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
+ SIZE_MAX < YYSTACK_BYTES (YYMAXDEPTH)
+ evaluated with infinite-precision integer arithmetic. */
+
+#if YYMAXDEPTH == 0
+# undef YYMAXDEPTH
+#endif
+
+#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. */
+static YYSIZE_T
+# if defined (__STDC__) || defined (__cplusplus)
+yystrlen (const char *yystr)
+# else
+yystrlen (yystr)
+ const char *yystr;
+# endif
+{
+ register const char *yys = yystr;
+
+ while (*yys++ != '\0')
+ continue;
+
+ return yys - yystr - 1;
+}
+# 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. */
+static char *
+# if defined (__STDC__) || defined (__cplusplus)
+yystpcpy (char *yydest, const char *yysrc)
+# else
+yystpcpy (yydest, yysrc)
+ char *yydest;
+ const char *yysrc;
+# endif
+{
+ register char *yyd = yydest;
+ register const char *yys = yysrc;
+
+ while ((*yyd++ = *yys++) != '\0')
+ continue;
+
+ return yyd - 1;
+}
+# endif
+# endif
+
+#endif /* !YYERROR_VERBOSE */
+
+
+
+#if YYDEBUG
+/*--------------------------------.
+| Print this symbol on YYOUTPUT. |
+`--------------------------------*/
+
+#if defined (__STDC__) || defined (__cplusplus)
+static void
+yysymprint (FILE *yyoutput, int yytype, YYSTYPE *yyvaluep)
+#else
+static void
+yysymprint (yyoutput, yytype, yyvaluep)
+ FILE *yyoutput;
+ int yytype;
+ YYSTYPE *yyvaluep;
+#endif
+{
+ /* Pacify ``unused variable'' warnings. */
+ (void) yyvaluep;
+
+ if (yytype < YYNTOKENS)
+ {
+ YYFPRINTF (yyoutput, "token %s (", yytname[yytype]);
+# ifdef YYPRINT
+ YYPRINT (yyoutput, yytoknum[yytype], *yyvaluep);
+# endif
+ }
+ else
+ YYFPRINTF (yyoutput, "nterm %s (", yytname[yytype]);
+
+ switch (yytype)
+ {
+ default:
+ break;
+ }
+ YYFPRINTF (yyoutput, ")");
+}
+
+#endif /* ! YYDEBUG */
+/*-----------------------------------------------.
+| Release the memory associated to this symbol. |
+`-----------------------------------------------*/
+
+#if defined (__STDC__) || defined (__cplusplus)
+static void
+yydestruct (int yytype, YYSTYPE *yyvaluep)
+#else
+static void
+yydestruct (yytype, yyvaluep)
+ int yytype;
+ YYSTYPE *yyvaluep;
+#endif
+{
+ /* Pacify ``unused variable'' warnings. */
+ (void) yyvaluep;
+
+ 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 */
+
+
+
+
+
+
+/*----------.
+| yyparse. |
+`----------*/
+
+#ifdef YYPARSE_PARAM
+# if defined (__STDC__) || defined (__cplusplus)
+int yyparse (void *YYPARSE_PARAM)
+# else
+int yyparse (YYPARSE_PARAM)
+ void *YYPARSE_PARAM;
+# endif
+#else /* ! YYPARSE_PARAM */
+#if defined (__STDC__) || defined (__cplusplus)
+int
+yyparse (void)
+#else
+int
+yyparse ()
+
+#endif
+#endif
+{
+ /* The lookahead symbol. */
+int yychar;
+
+/* The semantic value of the lookahead symbol. */
+YYSTYPE yylval;
+
+/* Number of syntax errors so far. */
+int yynerrs;
+
+ register int yystate;
+ register int yyn;
+ int yyresult;
+ /* Number of tokens to shift before error messages enabled. */
+ int yyerrstatus;
+ /* Lookahead token as an internal (translated) token number. */
+ int yytoken = 0;
+
+ /* 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. */
+ short yyssa[YYINITDEPTH];
+ short *yyss = yyssa;
+ register short *yyssp;
+
+ /* The semantic value stack. */
+ YYSTYPE yyvsa[YYINITDEPTH];
+ YYSTYPE *yyvs = yyvsa;
+ register YYSTYPE *yyvsp;
+
+
+
+#define YYPOPSTACK (yyvsp--, yyssp--)
+
+ YYSIZE_T yystacksize = YYINITDEPTH;
+
+ /* The variables used to return semantic value and location from the
+ action routines. */
+ YYSTYPE yyval;
+
+
+ /* When reducing, the number of symbols on the RHS of the reduced
+ rule. */
+ int yylen;
+
+ 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;
+ short *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 ("parser stack overflow",
+ &yyss1, yysize * sizeof (*yyssp),
+ &yyvs1, yysize * sizeof (*yyvsp),
+
+ &yystacksize);
+
+ yyss = yyss1;
+ yyvs = yyvs1;
+ }
+#else /* no yyoverflow */
+# ifndef YYSTACK_RELOCATE
+ goto yyoverflowlab;
+# else
+ /* Extend the stack our own way. */
+ if (YYMAXDEPTH <= yystacksize)
+ goto yyoverflowlab;
+ yystacksize *= 2;
+ if (YYMAXDEPTH < yystacksize)
+ yystacksize = YYMAXDEPTH;
+
+ {
+ short *yyss1 = yyss;
+ union yyalloc *yyptr =
+ (union yyalloc *) YYSTACK_ALLOC (YYSTACK_BYTES (yystacksize));
+ if (! yyptr)
+ goto yyoverflowlab;
+ 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 lookahead token if we need one and don't already have one. */
+/* yyresume: */
+
+ /* First try to decide what to do without reference to lookahead token. */
+
+ yyn = yypact[yystate];
+ if (yyn == YYPACT_NINF)
+ goto yydefault;
+
+ /* Not known => get a lookahead token if don't already have one. */
+
+ /* YYCHAR is either YYEMPTY or YYEOF or a valid lookahead 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);
+ YYDSYMPRINTF ("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;
+
+ /* Shift the lookahead token. */
+ YYDPRINTF ((stderr, "Shifting token %s, ", yytname[yytoken]));
+
+ /* Discard the token being shifted unless it is eof. */
+ if (yychar != YYEOF)
+ yychar = YYEMPTY;
+
+ *++yyvsp = yylval;
+
+
+ /* Count tokens shifted since error; after three, turn off error
+ status. */
+ if (yyerrstatus)
+ yyerrstatus--;
+
+ yystate = yyn;
+ 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 4:
+#line 195 "getdate.y"
+ { PC.times_seen++; }
+ break;
+
+ case 5:
+#line 197 "getdate.y"
+ { PC.local_zones_seen++; }
+ break;
+
+ case 6:
+#line 199 "getdate.y"
+ { PC.zones_seen++; }
+ break;
+
+ case 7:
+#line 201 "getdate.y"
+ { PC.dates_seen++; }
+ break;
+
+ case 8:
+#line 203 "getdate.y"
+ { PC.days_seen++; }
+ break;
+
+ case 9:
+#line 205 "getdate.y"
+ { PC.rels_seen++; }
+ break;
+
+ case 11:
+#line 211 "getdate.y"
+ {
+ PC.hour = yyvsp[-1].textintval.value;
+ PC.minutes = 0;
+ PC.seconds = 0;
+ PC.meridian = yyvsp[0].intval;
+ }
+ break;
+
+ case 12:
+#line 218 "getdate.y"
+ {
+ PC.hour = yyvsp[-3].textintval.value;
+ PC.minutes = yyvsp[-1].textintval.value;
+ PC.seconds = 0;
+ PC.meridian = yyvsp[0].intval;
+ }
+ break;
+
+ case 13:
+#line 225 "getdate.y"
+ {
+ PC.hour = yyvsp[-3].textintval.value;
+ PC.minutes = yyvsp[-1].textintval.value;
+ PC.meridian = MER24;
+ PC.zones_seen++;
+ PC.time_zone = yyvsp[0].textintval.value % 100 + (yyvsp[0].textintval.value / 100) * 60;
+ }
+ break;
+
+ case 14:
+#line 233 "getdate.y"
+ {
+ PC.hour = yyvsp[-5].textintval.value;
+ PC.minutes = yyvsp[-3].textintval.value;
+ PC.seconds = yyvsp[-1].textintval.value;
+ PC.meridian = yyvsp[0].intval;
+ }
+ break;
+
+ case 15:
+#line 240 "getdate.y"
+ {
+ PC.hour = yyvsp[-5].textintval.value;
+ PC.minutes = yyvsp[-3].textintval.value;
+ PC.seconds = yyvsp[-1].textintval.value;
+ PC.meridian = MER24;
+ PC.zones_seen++;
+ PC.time_zone = yyvsp[0].textintval.value % 100 + (yyvsp[0].textintval.value / 100) * 60;
+ }
+ break;
+
+ case 16:
+#line 252 "getdate.y"
+ { PC.local_isdst = yyvsp[0].intval; }
+ break;
+
+ case 17:
+#line 254 "getdate.y"
+ { PC.local_isdst = yyvsp[-1].intval < 0 ? 1 : yyvsp[-1].intval + 1; }
+ break;
+
+ case 18:
+#line 259 "getdate.y"
+ { PC.time_zone = yyvsp[0].intval; }
+ break;
+
+ case 19:
+#line 261 "getdate.y"
+ { PC.time_zone = yyvsp[0].intval + 60; }
+ break;
+
+ case 20:
+#line 263 "getdate.y"
+ { PC.time_zone = yyvsp[-1].intval + 60; }
+ break;
+
+ case 21:
+#line 268 "getdate.y"
+ {
+ PC.day_ordinal = 1;
+ PC.day_number = yyvsp[0].intval;
+ }
+ break;
+
+ case 22:
+#line 273 "getdate.y"
+ {
+ PC.day_ordinal = 1;
+ PC.day_number = yyvsp[-1].intval;
+ }
+ break;
+
+ case 23:
+#line 278 "getdate.y"
+ {
+ PC.day_ordinal = yyvsp[-1].textintval.value;
+ PC.day_number = yyvsp[0].intval;
+ }
+ break;
+
+ case 24:
+#line 286 "getdate.y"
+ {
+ PC.month = yyvsp[-2].textintval.value;
+ PC.day = yyvsp[0].textintval.value;
+ }
+ break;
+
+ case 25:
+#line 291 "getdate.y"
+ {
+ /* Interpret as YYYY/MM/DD if the first value has 4 or more digits,
+ otherwise as MM/DD/YY.
+ The goal in recognizing YYYY/MM/DD is solely to support legacy
+ machine-generated dates like those in an RCS log listing. If
+ you want portability, use the ISO 8601 format. */
+ if (4 <= yyvsp[-4].textintval.digits)
+ {
+ PC.year = yyvsp[-4].textintval;
+ PC.month = yyvsp[-2].textintval.value;
+ PC.day = yyvsp[0].textintval.value;
+ }
+ else
+ {
+ PC.month = yyvsp[-4].textintval.value;
+ PC.day = yyvsp[-2].textintval.value;
+ PC.year = yyvsp[0].textintval;
+ }
+ }
+ break;
+
+ case 26:
+#line 311 "getdate.y"
+ {
+ /* ISO 8601 format. YYYY-MM-DD. */
+ PC.year = yyvsp[-2].textintval;
+ PC.month = -yyvsp[-1].textintval.value;
+ PC.day = -yyvsp[0].textintval.value;
+ }
+ break;
+
+ case 27:
+#line 318 "getdate.y"
+ {
+ /* e.g. 17-JUN-1992. */
+ PC.day = yyvsp[-2].textintval.value;
+ PC.month = yyvsp[-1].intval;
+ PC.year.value = -yyvsp[0].textintval.value;
+ PC.year.digits = yyvsp[0].textintval.digits;
+ }
+ break;
+
+ case 28:
+#line 326 "getdate.y"
+ {
+ PC.month = yyvsp[-1].intval;
+ PC.day = yyvsp[0].textintval.value;
+ }
+ break;
+
+ case 29:
+#line 331 "getdate.y"
+ {
+ PC.month = yyvsp[-3].intval;
+ PC.day = yyvsp[-2].textintval.value;
+ PC.year = yyvsp[0].textintval;
+ }
+ break;
+
+ case 30:
+#line 337 "getdate.y"
+ {
+ PC.day = yyvsp[-1].textintval.value;
+ PC.month = yyvsp[0].intval;
+ }
+ break;
+
+ case 31:
+#line 342 "getdate.y"
+ {
+ PC.day = yyvsp[-2].textintval.value;
+ PC.month = yyvsp[-1].intval;
+ PC.year = yyvsp[0].textintval;
+ }
+ break;
+
+ case 32:
+#line 351 "getdate.y"
+ {
+ PC.rel_seconds = -PC.rel_seconds;
+ PC.rel_minutes = -PC.rel_minutes;
+ PC.rel_hour = -PC.rel_hour;
+ PC.rel_day = -PC.rel_day;
+ PC.rel_month = -PC.rel_month;
+ PC.rel_year = -PC.rel_year;
+ }
+ break;
+
+ case 34:
+#line 364 "getdate.y"
+ { PC.rel_year += yyvsp[-1].textintval.value * yyvsp[0].intval; }
+ break;
+
+ case 35:
+#line 366 "getdate.y"
+ { PC.rel_year += yyvsp[-1].textintval.value * yyvsp[0].intval; }
+ break;
+
+ case 36:
+#line 368 "getdate.y"
+ { PC.rel_year += yyvsp[0].intval; }
+ break;
+
+ case 37:
+#line 370 "getdate.y"
+ { PC.rel_month += yyvsp[-1].textintval.value * yyvsp[0].intval; }
+ break;
+
+ case 38:
+#line 372 "getdate.y"
+ { PC.rel_month += yyvsp[-1].textintval.value * yyvsp[0].intval; }
+ break;
+
+ case 39:
+#line 374 "getdate.y"
+ { PC.rel_month += yyvsp[0].intval; }
+ break;
+
+ case 40:
+#line 376 "getdate.y"
+ { PC.rel_day += yyvsp[-1].textintval.value * yyvsp[0].intval; }
+ break;
+
+ case 41:
+#line 378 "getdate.y"
+ { PC.rel_day += yyvsp[-1].textintval.value * yyvsp[0].intval; }
+ break;
+
+ case 42:
+#line 380 "getdate.y"
+ { PC.rel_day += yyvsp[0].intval; }
+ break;
+
+ case 43:
+#line 382 "getdate.y"
+ { PC.rel_hour += yyvsp[-1].textintval.value * yyvsp[0].intval; }
+ break;
+
+ case 44:
+#line 384 "getdate.y"
+ { PC.rel_hour += yyvsp[-1].textintval.value * yyvsp[0].intval; }
+ break;
+
+ case 45:
+#line 386 "getdate.y"
+ { PC.rel_hour += yyvsp[0].intval; }
+ break;
+
+ case 46:
+#line 388 "getdate.y"
+ { PC.rel_minutes += yyvsp[-1].textintval.value * yyvsp[0].intval; }
+ break;
+
+ case 47:
+#line 390 "getdate.y"
+ { PC.rel_minutes += yyvsp[-1].textintval.value * yyvsp[0].intval; }
+ break;
+
+ case 48:
+#line 392 "getdate.y"
+ { PC.rel_minutes += yyvsp[0].intval; }
+ break;
+
+ case 49:
+#line 394 "getdate.y"
+ { PC.rel_seconds += yyvsp[-1].textintval.value * yyvsp[0].intval; }
+ break;
+
+ case 50:
+#line 396 "getdate.y"
+ { PC.rel_seconds += yyvsp[-1].textintval.value * yyvsp[0].intval; }
+ break;
+
+ case 51:
+#line 398 "getdate.y"
+ { PC.rel_seconds += yyvsp[0].intval; }
+ break;
+
+ case 52:
+#line 403 "getdate.y"
+ {
+ if (PC.dates_seen
+ && ! PC.rels_seen && (PC.times_seen || 2 < yyvsp[0].textintval.digits))
+ PC.year = yyvsp[0].textintval;
+ else
+ {
+ if (4 < yyvsp[0].textintval.digits)
+ {
+ PC.dates_seen++;
+ PC.day = yyvsp[0].textintval.value % 100;
+ PC.month = (yyvsp[0].textintval.value / 100) % 100;
+ PC.year.value = yyvsp[0].textintval.value / 10000;
+ PC.year.digits = yyvsp[0].textintval.digits - 4;
+ }
+ else
+ {
+ PC.times_seen++;
+ if (yyvsp[0].textintval.digits <= 2)
+ {
+ PC.hour = yyvsp[0].textintval.value;
+ PC.minutes = 0;
+ }
+ else
+ {
+ PC.hour = yyvsp[0].textintval.value / 100;
+ PC.minutes = yyvsp[0].textintval.value % 100;
+ }
+ PC.seconds = 0;
+ PC.meridian = MER24;
+ }
+ }
+ }
+ break;
+
+ case 53:
+#line 439 "getdate.y"
+ { yyval.intval = MER24; }
+ break;
+
+ case 54:
+#line 441 "getdate.y"
+ { yyval.intval = yyvsp[0].intval; }
+ break;
+
+
+ }
+
+/* Line 999 of yacc.c. */
+#line 1593 "getdate.c"
+
+ yyvsp -= yylen;
+ yyssp -= yylen;
+
+
+ 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
+ yyn = yypact[yystate];
+
+ if (YYPACT_NINF < yyn && yyn < YYLAST)
+ {
+ YYSIZE_T yysize = 0;
+ int yytype = YYTRANSLATE (yychar);
+ char *yymsg;
+ int yyx, yycount;
+
+ yycount = 0;
+ /* Start YYX at -YYN if negative to avoid negative indexes in
+ YYCHECK. */
+ for (yyx = yyn < 0 ? -yyn : 0;
+ yyx < (int) (sizeof (yytname) / sizeof (char *)); yyx++)
+ if (yycheck[yyx + yyn] == yyx && yyx != YYTERROR)
+ yysize += yystrlen (yytname[yyx]) + 15, yycount++;
+ yysize += yystrlen ("syntax error, unexpected ") + 1;
+ yysize += yystrlen (yytname[yytype]);
+ yymsg = (char *) YYSTACK_ALLOC (yysize);
+ if (yymsg != 0)
+ {
+ char *yyp = yystpcpy (yymsg, "syntax error, unexpected ");
+ yyp = yystpcpy (yyp, yytname[yytype]);
+
+ if (yycount < 5)
+ {
+ yycount = 0;
+ for (yyx = yyn < 0 ? -yyn : 0;
+ yyx < (int) (sizeof (yytname) / sizeof (char *));
+ yyx++)
+ if (yycheck[yyx + yyn] == yyx && yyx != YYTERROR)
+ {
+ const char *yyq = ! yycount ? ", expecting " : " or ";
+ yyp = yystpcpy (yyp, yyq);
+ yyp = yystpcpy (yyp, yytname[yyx]);
+ yycount++;
+ }
+ }
+ yyerror (yymsg);
+ YYSTACK_FREE (yymsg);
+ }
+ else
+ yyerror ("syntax error; also virtual memory exhausted");
+ }
+ else
+#endif /* YYERROR_VERBOSE */
+ yyerror ("syntax error");
+ }
+
+
+
+ if (yyerrstatus == 3)
+ {
+ /* If just tried and failed to reuse lookahead token after an
+ error, discard it. */
+
+ /* Return failure if at end of input. */
+ if (yychar == YYEOF)
+ {
+ /* Pop the error token. */
+ YYPOPSTACK;
+ /* Pop the rest of the stack. */
+ while (yyss < yyssp)
+ {
+ YYDSYMPRINTF ("Error: popping", yystos[*yyssp], yyvsp, yylsp);
+ yydestruct (yystos[*yyssp], yyvsp);
+ YYPOPSTACK;
+ }
+ YYABORT;
+ }
+
+ YYDSYMPRINTF ("Error: discarding", yytoken, &yylval, &yylloc);
+ yydestruct (yytoken, &yylval);
+ yychar = YYEMPTY;
+
+ }
+
+ /* Else will try to reuse lookahead token after shifting the error
+ token. */
+ goto yyerrlab1;
+
+
+/*----------------------------------------------------.
+| yyerrlab1 -- error raised explicitly by an action. |
+`----------------------------------------------------*/
+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;
+
+ YYDSYMPRINTF ("Error: popping", yystos[*yyssp], yyvsp, yylsp);
+ yydestruct (yystos[yystate], yyvsp);
+ yyvsp--;
+ yystate = *--yyssp;
+
+ YY_STACK_PRINT (yyss, yyssp);
+ }
+
+ if (yyn == YYFINAL)
+ YYACCEPT;
+
+ YYDPRINTF ((stderr, "Shifting error token, "));
+
+ *++yyvsp = yylval;
+
+
+ yystate = yyn;
+ goto yynewstate;
+
+
+/*-------------------------------------.
+| yyacceptlab -- YYACCEPT comes here. |
+`-------------------------------------*/
+yyacceptlab:
+ yyresult = 0;
+ goto yyreturn;
+
+/*-----------------------------------.
+| yyabortlab -- YYABORT comes here. |
+`-----------------------------------*/
+yyabortlab:
+ yyresult = 1;
+ goto yyreturn;
+
+#ifndef yyoverflow
+/*----------------------------------------------.
+| yyoverflowlab -- parser overflow comes here. |
+`----------------------------------------------*/
+yyoverflowlab:
+ yyerror ("parser stack overflow");
+ yyresult = 2;
+ /* Fall through. */
+#endif
+
+yyreturn:
+#ifndef yyoverflow
+ if (yyss != yyssa)
+ YYSTACK_FREE (yyss);
+#endif
+ return yyresult;
+}
+
+
+#line 444 "getdate.y"
+
+
+/* Include this file down here because bison inserts code above which
+ may define-away `const'. We want the prototype for get_date to have
+ the same signature as the function definition. */
+#include "modules/getdate.h"
+
+#ifndef gmtime
+struct tm *gmtime ();
+#endif
+#ifndef localtime
+struct tm *localtime ();
+#endif
+#ifndef mktime
+time_t mktime ();
+#endif
+
+static table const meridian_table[] =
+{
+ { "AM", tMERIDIAN, MERam },
+ { "A.M.", tMERIDIAN, MERam },
+ { "PM", tMERIDIAN, MERpm },
+ { "P.M.", tMERIDIAN, MERpm },
+ { 0, 0, 0 }
+};
+
+static table const dst_table[] =
+{
+ { "DST", tDST, 0 }
+};
+
+static table const month_and_day_table[] =
+{
+ { "JANUARY", tMONTH, 1 },
+ { "FEBRUARY", tMONTH, 2 },
+ { "MARCH", tMONTH, 3 },
+ { "APRIL", tMONTH, 4 },
+ { "MAY", tMONTH, 5 },
+ { "JUNE", tMONTH, 6 },
+ { "JULY", tMONTH, 7 },
+ { "AUGUST", tMONTH, 8 },
+ { "SEPTEMBER",tMONTH, 9 },
+ { "SEPT", tMONTH, 9 },
+ { "OCTOBER", tMONTH, 10 },
+ { "NOVEMBER", tMONTH, 11 },
+ { "DECEMBER", tMONTH, 12 },
+ { "SUNDAY", tDAY, 0 },
+ { "MONDAY", tDAY, 1 },
+ { "TUESDAY", tDAY, 2 },
+ { "TUES", tDAY, 2 },
+ { "WEDNESDAY",tDAY, 3 },
+ { "WEDNES", tDAY, 3 },
+ { "THURSDAY", tDAY, 4 },
+ { "THUR", tDAY, 4 },
+ { "THURS", tDAY, 4 },
+ { "FRIDAY", tDAY, 5 },
+ { "SATURDAY", tDAY, 6 },
+ { 0, 0, 0 }
+};
+
+static table const time_units_table[] =
+{
+ { "YEAR", tYEAR_UNIT, 1 },
+ { "MONTH", tMONTH_UNIT, 1 },
+ { "FORTNIGHT",tDAY_UNIT, 14 },
+ { "WEEK", tDAY_UNIT, 7 },
+ { "DAY", tDAY_UNIT, 1 },
+ { "HOUR", tHOUR_UNIT, 1 },
+ { "MINUTE", tMINUTE_UNIT, 1 },
+ { "MIN", tMINUTE_UNIT, 1 },
+ { "SECOND", tSEC_UNIT, 1 },
+ { "SEC", tSEC_UNIT, 1 },
+ { 0, 0, 0 }
+};
+
+/* Assorted relative-time words. */
+static table const relative_time_table[] =
+{
+ { "TOMORROW", tMINUTE_UNIT, 24 * 60 },
+ { "YESTERDAY",tMINUTE_UNIT, - (24 * 60) },
+ { "TODAY", tMINUTE_UNIT, 0 },
+ { "NOW", tMINUTE_UNIT, 0 },
+ { "LAST", tUNUMBER, -1 },
+ { "THIS", tUNUMBER, 0 },
+ { "NEXT", tUNUMBER, 1 },
+ { "FIRST", tUNUMBER, 1 },
+/*{ "SECOND", tUNUMBER, 2 }, */
+ { "THIRD", tUNUMBER, 3 },
+ { "FOURTH", tUNUMBER, 4 },
+ { "FIFTH", tUNUMBER, 5 },
+ { "SIXTH", tUNUMBER, 6 },
+ { "SEVENTH", tUNUMBER, 7 },
+ { "EIGHTH", tUNUMBER, 8 },
+ { "NINTH", tUNUMBER, 9 },
+ { "TENTH", tUNUMBER, 10 },
+ { "ELEVENTH", tUNUMBER, 11 },
+ { "TWELFTH", tUNUMBER, 12 },
+ { "AGO", tAGO, 1 },
+ { 0, 0, 0 }
+};
+
+/* The time zone table. This table is necessarily incomplete, as time
+ zone abbreviations are ambiguous; e.g. Australians interpret "EST"
+ as Eastern time in Australia, not as US Eastern Standard Time.
+ You cannot rely on getdate to handle arbitrary time zone
+ abbreviations; use numeric abbreviations like `-0500' instead. */
+static table const time_zone_table[] =
+{
+ { "GMT", tZONE, HOUR ( 0) }, /* Greenwich Mean */
+ { "UT", tZONE, HOUR ( 0) }, /* Universal (Coordinated) */
+ { "UTC", tZONE, HOUR ( 0) },
+ { "WET", tZONE, HOUR ( 0) }, /* Western European */
+ { "WEST", tDAYZONE, HOUR ( 0) }, /* Western European Summer */
+ { "BST", tDAYZONE, HOUR ( 0) }, /* British Summer */
+ { "ART", tZONE, -HOUR ( 3) }, /* Argentina */
+ { "BRT", tZONE, -HOUR ( 3) }, /* Brazil */
+ { "BRST", tDAYZONE, -HOUR ( 3) }, /* Brazil Summer */
+ { "NST", tZONE, -(HOUR ( 3) + 30) }, /* Newfoundland Standard */
+ { "NDT", tDAYZONE,-(HOUR ( 3) + 30) }, /* Newfoundland Daylight */
+ { "AST", tZONE, -HOUR ( 4) }, /* Atlantic Standard */
+ { "ADT", tDAYZONE, -HOUR ( 4) }, /* Atlantic Daylight */
+ { "CLT", tZONE, -HOUR ( 4) }, /* Chile */
+ { "CLST", tDAYZONE, -HOUR ( 4) }, /* Chile Summer */
+ { "EST", tZONE, -HOUR ( 5) }, /* Eastern Standard */
+ { "EDT", tDAYZONE, -HOUR ( 5) }, /* Eastern Daylight */
+ { "CST", tZONE, -HOUR ( 6) }, /* Central Standard */
+ { "CDT", tDAYZONE, -HOUR ( 6) }, /* Central Daylight */
+ { "MST", tZONE, -HOUR ( 7) }, /* Mountain Standard */
+ { "MDT", tDAYZONE, -HOUR ( 7) }, /* Mountain Daylight */
+ { "PST", tZONE, -HOUR ( 8) }, /* Pacific Standard */
+ { "PDT", tDAYZONE, -HOUR ( 8) }, /* Pacific Daylight */
+ { "AKST", tZONE, -HOUR ( 9) }, /* Alaska Standard */
+ { "AKDT", tDAYZONE, -HOUR ( 9) }, /* Alaska Daylight */
+ { "HST", tZONE, -HOUR (10) }, /* Hawaii Standard */
+ { "HAST", tZONE, -HOUR (10) }, /* Hawaii-Aleutian Standard */
+ { "HADT", tDAYZONE, -HOUR (10) }, /* Hawaii-Aleutian Daylight */
+ { "SST", tZONE, -HOUR (12) }, /* Samoa Standard */
+ { "WAT", tZONE, HOUR ( 1) }, /* West Africa */
+ { "CET", tZONE, HOUR ( 1) }, /* Central European */
+ { "CEST", tDAYZONE, HOUR ( 1) }, /* Central European Summer */
+ { "MET", tZONE, HOUR ( 1) }, /* Middle European */
+ { "MEZ", tZONE, HOUR ( 1) }, /* Middle European */
+ { "MEST", tDAYZONE, HOUR ( 1) }, /* Middle European Summer */
+ { "MESZ", tDAYZONE, HOUR ( 1) }, /* Middle European Summer */
+ { "EET", tZONE, HOUR ( 2) }, /* Eastern European */
+ { "EEST", tDAYZONE, HOUR ( 2) }, /* Eastern European Summer */
+ { "CAT", tZONE, HOUR ( 2) }, /* Central Africa */
+ { "SAST", tZONE, HOUR ( 2) }, /* South Africa Standard */
+ { "EAT", tZONE, HOUR ( 3) }, /* East Africa */
+ { "MSK", tZONE, HOUR ( 3) }, /* Moscow */
+ { "MSD", tDAYZONE, HOUR ( 3) }, /* Moscow Daylight */
+ { "IST", tZONE, (HOUR ( 5) + 30) }, /* India Standard */
+ { "SGT", tZONE, HOUR ( 8) }, /* Singapore */
+ { "KST", tZONE, HOUR ( 9) }, /* Korea Standard */
+ { "JST", tZONE, HOUR ( 9) }, /* Japan Standard */
+ { "GST", tZONE, HOUR (10) }, /* Guam Standard */
+ { "NZST", tZONE, HOUR (12) }, /* New Zealand Standard */
+ { "NZDT", tDAYZONE, HOUR (12) }, /* New Zealand Daylight */
+ { 0, 0, 0 }
+};
+
+/* Military time zone table. */
+static table const military_table[] =
+{
+ { "A", tZONE, -HOUR ( 1) },
+ { "B", tZONE, -HOUR ( 2) },
+ { "C", tZONE, -HOUR ( 3) },
+ { "D", tZONE, -HOUR ( 4) },
+ { "E", tZONE, -HOUR ( 5) },
+ { "F", tZONE, -HOUR ( 6) },
+ { "G", tZONE, -HOUR ( 7) },
+ { "H", tZONE, -HOUR ( 8) },
+ { "I", tZONE, -HOUR ( 9) },
+ { "K", tZONE, -HOUR (10) },
+ { "L", tZONE, -HOUR (11) },
+ { "M", tZONE, -HOUR (12) },
+ { "N", tZONE, HOUR ( 1) },
+ { "O", tZONE, HOUR ( 2) },
+ { "P", tZONE, HOUR ( 3) },
+ { "Q", tZONE, HOUR ( 4) },
+ { "R", tZONE, HOUR ( 5) },
+ { "S", tZONE, HOUR ( 6) },
+ { "T", tZONE, HOUR ( 7) },
+ { "U", tZONE, HOUR ( 8) },
+ { "V", tZONE, HOUR ( 9) },
+ { "W", tZONE, HOUR (10) },
+ { "X", tZONE, HOUR (11) },
+ { "Y", tZONE, HOUR (12) },
+ { "Z", tZONE, HOUR ( 0) },
+ { 0, 0, 0 }
+};
+
+
+
+static int
+to_hour (int hours, int meridian)
+{
+ switch (meridian)
+ {
+ case MER24:
+ return 0 <= hours && hours < 24 ? hours : -1;
+ case MERam:
+ return 0 < hours && hours < 12 ? hours : hours == 12 ? 0 : -1;
+ case MERpm:
+ return 0 < hours && hours < 12 ? hours + 12 : hours == 12 ? 12 : -1;
+ default:
+ abort ();
+ }
+ /* NOTREACHED */
+ return 0;
+}
+
+static int
+to_year (textint textyear)
+{
+ int year = textyear.value;
+
+ if (year < 0)
+ year = -year;
+
+ /* XPG4 suggests that years 00-68 map to 2000-2068, and
+ years 69-99 map to 1969-1999. */
+ if (textyear.digits == 2)
+ year += year < 69 ? 2000 : 1900;
+
+ return year;
+}
+
+static table const *
+lookup_zone (parser_control const *pc, char const *name)
+{
+ table const *tp;
+
+ /* Try local zone abbreviations first; they're more likely to be right. */
+ for (tp = pc->local_time_zone_table; tp->name; tp++)
+ if (strcmp (name, tp->name) == 0)
+ return tp;
+
+ for (tp = time_zone_table; tp->name; tp++)
+ if (strcmp (name, tp->name) == 0)
+ return tp;
+
+ return 0;
+}
+
+#if ! HAVE_TM_GMTOFF
+/* Yield the difference between *A and *B,
+ measured in seconds, ignoring leap seconds.
+ The body of this function is taken directly from the GNU C Library;
+ see src/strftime.c. */
+static int
+tm_diff (struct tm const *a, struct tm const *b)
+{
+ /* Compute intervening leap days correctly even if year is negative.
+ Take care to avoid int overflow in leap day calculations,
+ but it's OK to assume that A and B are close to each other. */
+ int a4 = (a->tm_year >> 2) + (TM_YEAR_BASE >> 2) - ! (a->tm_year & 3);
+ int b4 = (b->tm_year >> 2) + (TM_YEAR_BASE >> 2) - ! (b->tm_year & 3);
+ int a100 = a4 / 25 - (a4 % 25 < 0);
+ int b100 = b4 / 25 - (b4 % 25 < 0);
+ int a400 = a100 >> 2;
+ int b400 = b100 >> 2;
+ int intervening_leap_days = (a4 - b4) - (a100 - b100) + (a400 - b400);
+ int years = a->tm_year - b->tm_year;
+ int days = (365 * years + intervening_leap_days
+ + (a->tm_yday - b->tm_yday));
+ return (60 * (60 * (24 * days + (a->tm_hour - b->tm_hour))
+ + (a->tm_min - b->tm_min))
+ + (a->tm_sec - b->tm_sec));
+}
+#endif /* ! HAVE_TM_GMTOFF */
+
+static table const *
+lookup_word (parser_control const *pc, char *word)
+{
+ char *p;
+ char *q;
+ size_t wordlen;
+ table const *tp;
+ int i;
+ int abbrev;
+
+ /* Make it uppercase. */
+ for (p = word; *p; p++)
+ if (ISLOWER ((unsigned char) *p))
+ *p = toupper ((unsigned char) *p);
+
+ for (tp = meridian_table; tp->name; tp++)
+ if (strcmp (word, tp->name) == 0)
+ return tp;
+
+ /* See if we have an abbreviation for a month. */
+ wordlen = strlen (word);
+ abbrev = wordlen == 3 || (wordlen == 4 && word[3] == '.');
+
+ for (tp = month_and_day_table; tp->name; tp++)
+ if ((abbrev ? strncmp (word, tp->name, 3) : strcmp (word, tp->name)) == 0)
+ return tp;
+
+ if ((tp = lookup_zone (pc, word)))
+ return tp;
+
+ if (strcmp (word, dst_table[0].name) == 0)
+ return dst_table;
+
+ for (tp = time_units_table; tp->name; tp++)
+ if (strcmp (word, tp->name) == 0)
+ return tp;
+
+ /* Strip off any plural and try the units table again. */
+ if (word[wordlen - 1] == 'S')
+ {
+ word[wordlen - 1] = '\0';
+ for (tp = time_units_table; tp->name; tp++)
+ if (strcmp (word, tp->name) == 0)
+ return tp;
+ word[wordlen - 1] = 'S'; /* For "this" in relative_time_table. */
+ }
+
+ for (tp = relative_time_table; tp->name; tp++)
+ if (strcmp (word, tp->name) == 0)
+ return tp;
+
+ /* Military time zones. */
+ if (wordlen == 1)
+ for (tp = military_table; tp->name; tp++)
+ if (word[0] == tp->name[0])
+ return tp;
+
+ /* Drop out any periods and try the time zone table again. */
+ for (i = 0, p = q = word; (*p = *q); q++)
+ if (*q == '.')
+ i = 1;
+ else
+ p++;
+ if (i && (tp = lookup_zone (pc, word)))
+ return tp;
+
+ return 0;
+}
+
+static int
+yylex (YYSTYPE *lvalp, parser_control *pc)
+{
+ unsigned char c;
+ int count;
+
+ for (;;)
+ {
+ while (c = *pc->input, ISSPACE (c))
+ pc->input++;
+
+ if (ISDIGIT (c) || c == '-' || c == '+')
+ {
+ char const *p;
+ int sign;
+ int value;
+ if (c == '-' || c == '+')
+ {
+ sign = c == '-' ? -1 : 1;
+ c = *++pc->input;
+ if (! ISDIGIT (c))
+ /* skip the '-' sign */
+ continue;
+ }
+ else
+ sign = 0;
+ p = pc->input;
+ value = 0;
+ do
+ {
+ value = 10 * value + c - '0';
+ c = *++p;
+ }
+ while (ISDIGIT (c));
+ lvalp->textintval.value = sign < 0 ? -value : value;
+ lvalp->textintval.digits = p - pc->input;
+ pc->input = p;
+ return sign ? tSNUMBER : tUNUMBER;
+ }
+
+ if (ISALPHA (c))
+ {
+ char buff[20];
+ char *p = buff;
+ table const *tp;
+
+ do
+ {
+ if (p < buff + sizeof buff - 1)
+ *p++ = c;
+ c = *++pc->input;
+ }
+ while (ISALPHA (c) || c == '.');
+
+ *p = '\0';
+ tp = lookup_word (pc, buff);
+ if (! tp)
+ return '?';
+ lvalp->intval = tp->value;
+ return tp->type;
+ }
+
+ if (c != '(')
+ return *pc->input++;
+ count = 0;
+ do
+ {
+ c = *pc->input++;
+ if (c == '\0')
+ return c;
+ if (c == '(')
+ count++;
+ else if (c == ')')
+ count--;
+ }
+ while (count > 0);
+ }
+}
+
+/* Do nothing if the parser reports an error. */
+static int
+yyerror (char *s ATTRIBUTE_UNUSED)
+{
+ return 0;
+}
+
+/* Parse a date/time string P. Return the corresponding time_t value,
+ or (time_t) -1 if there is an error. P can be an incomplete or
+ relative time specification; if so, use *NOW as the basis for the
+ returned time. */
+time_t
+get_date (const char *p, const time_t *now)
+{
+ time_t Start = now ? *now : time (0);
+ struct tm *tmp = localtime (&Start);
+ struct tm tm;
+ struct tm tm0;
+ parser_control pc;
+
+ if (! tmp)
+ return -1;
+
+ pc.input = p;
+ pc.year.value = tmp->tm_year + TM_YEAR_BASE;
+ pc.year.digits = 4;
+ pc.month = tmp->tm_mon + 1;
+ pc.day = tmp->tm_mday;
+ pc.hour = tmp->tm_hour;
+ pc.minutes = tmp->tm_min;
+ pc.seconds = tmp->tm_sec;
+ tm.tm_isdst = tmp->tm_isdst;
+
+ pc.meridian = MER24;
+ pc.rel_seconds = 0;
+ pc.rel_minutes = 0;
+ pc.rel_hour = 0;
+ pc.rel_day = 0;
+ pc.rel_month = 0;
+ pc.rel_year = 0;
+ pc.dates_seen = 0;
+ pc.days_seen = 0;
+ pc.rels_seen = 0;
+ pc.times_seen = 0;
+ pc.local_zones_seen = 0;
+ pc.zones_seen = 0;
+
+#if HAVE_STRUCT_TM_TM_ZONE
+ pc.local_time_zone_table[0].name = tmp->tm_zone;
+ pc.local_time_zone_table[0].type = tLOCAL_ZONE;
+ pc.local_time_zone_table[0].value = tmp->tm_isdst;
+ pc.local_time_zone_table[1].name = 0;
+
+ /* Probe the names used in the next three calendar quarters, looking
+ for a tm_isdst different from the one we already have. */
+ {
+ int quarter;
+ for (quarter = 1; quarter <= 3; quarter++)
+ {
+ time_t probe = Start + quarter * (90 * 24 * 60 * 60);
+ struct tm *probe_tm = localtime (&probe);
+ if (probe_tm && probe_tm->tm_zone
+ && probe_tm->tm_isdst != pc.local_time_zone_table[0].value)
+ {
+ {
+ pc.local_time_zone_table[1].name = probe_tm->tm_zone;
+ pc.local_time_zone_table[1].type = tLOCAL_ZONE;
+ pc.local_time_zone_table[1].value = probe_tm->tm_isdst;
+ pc.local_time_zone_table[2].name = 0;
+ }
+ break;
+ }
+ }
+ }
+#else
+#if HAVE_TZNAME
+ {
+# ifndef tzname
+ extern char *tzname[];
+# endif
+ int i;
+ for (i = 0; i < 2; i++)
+ {
+ pc.local_time_zone_table[i].name = tzname[i];
+ pc.local_time_zone_table[i].type = tLOCAL_ZONE;
+ pc.local_time_zone_table[i].value = i;
+ }
+ pc.local_time_zone_table[i].name = 0;
+ }
+#else
+ pc.local_time_zone_table[0].name = 0;
+#endif
+#endif
+
+ if (pc.local_time_zone_table[0].name && pc.local_time_zone_table[1].name
+ && ! strcmp (pc.local_time_zone_table[0].name,
+ pc.local_time_zone_table[1].name))
+ {
+ /* This locale uses the same abbrevation for standard and
+ daylight times. So if we see that abbreviation, we don't
+ know whether it's daylight time. */
+ pc.local_time_zone_table[0].value = -1;
+ pc.local_time_zone_table[1].name = 0;
+ }
+
+ if (yyparse (&pc) != 0
+ || 1 < pc.times_seen || 1 < pc.dates_seen || 1 < pc.days_seen
+ || 1 < (pc.local_zones_seen + pc.zones_seen)
+ || (pc.local_zones_seen && 1 < pc.local_isdst))
+ return -1;
+
+ tm.tm_year = to_year (pc.year) - TM_YEAR_BASE + pc.rel_year;
+ tm.tm_mon = pc.month - 1 + pc.rel_month;
+ tm.tm_mday = pc.day + pc.rel_day;
+ if (pc.times_seen || (pc.rels_seen && ! pc.dates_seen && ! pc.days_seen))
+ {
+ tm.tm_hour = to_hour (pc.hour, pc.meridian);
+ if (tm.tm_hour < 0)
+ return -1;
+ tm.tm_min = pc.minutes;
+ tm.tm_sec = pc.seconds;
+ }
+ else
+ {
+ tm.tm_hour = tm.tm_min = tm.tm_sec = 0;
+ }
+
+ /* Let mktime deduce tm_isdst if we have an absolute time stamp,
+ or if the relative time stamp mentions days, months, or years. */
+ if (pc.dates_seen | pc.days_seen | pc.times_seen | pc.rel_day
+ | pc.rel_month | pc.rel_year)
+ tm.tm_isdst = -1;
+
+ /* But if the input explicitly specifies local time with or without
+ DST, give mktime that information. */
+ if (pc.local_zones_seen)
+ tm.tm_isdst = pc.local_isdst;
+
+ tm0 = tm;
+
+ Start = mktime (&tm);
+
+ if (Start == (time_t) -1)
+ {
+
+ /* Guard against falsely reporting errors near the time_t boundaries
+ when parsing times in other time zones. For example, if the min
+ time_t value is 1970-01-01 00:00:00 UTC and we are 8 hours ahead
+ of UTC, then the min localtime value is 1970-01-01 08:00:00; if
+ we apply mktime to 1970-01-01 00:00:00 we will get an error, so
+ we apply mktime to 1970-01-02 08:00:00 instead and adjust the time
+ zone by 24 hours to compensate. This algorithm assumes that
+ there is no DST transition within a day of the time_t boundaries. */
+ if (pc.zones_seen)
+ {
+ tm = tm0;
+ if (tm.tm_year <= EPOCH_YEAR - TM_YEAR_BASE)
+ {
+ tm.tm_mday++;
+ pc.time_zone += 24 * 60;
+ }
+ else
+ {
+ tm.tm_mday--;
+ pc.time_zone -= 24 * 60;
+ }
+ Start = mktime (&tm);
+ }
+
+ if (Start == (time_t) -1)
+ return Start;
+ }
+
+ if (pc.days_seen && ! pc.dates_seen)
+ {
+ tm.tm_mday += ((pc.day_number - tm.tm_wday + 7) % 7
+ + 7 * (pc.day_ordinal - (0 < pc.day_ordinal)));
+ tm.tm_isdst = -1;
+ Start = mktime (&tm);
+ if (Start == (time_t) -1)
+ return Start;
+ }
+
+ if (pc.zones_seen)
+ {
+ int delta = pc.time_zone * 60;
+#ifdef HAVE_TM_GMTOFF
+ delta -= tm.tm_gmtoff;
+#else
+ struct tm *gmt = gmtime (&Start);
+ if (! gmt)
+ return -1;
+ delta -= tm_diff (&tm, gmt);
+#endif
+ if ((Start < Start - delta) != (delta < 0))
+ return -1; /* time_t overflow */
+ Start -= delta;
+ }
+
+ /* Add relative hours, minutes, and seconds. Ignore leap seconds;
+ i.e. "+ 10 minutes" means 600 seconds, even if one of them is a
+ leap second. Typically this is not what the user wants, but it's
+ too hard to do it the other way, because the time zone indicator
+ must be applied before relative times, and if mktime is applied
+ again the time zone will be lost. */
+ {
+ time_t t0 = Start;
+ long d1 = 60 * 60 * (long) pc.rel_hour;
+ time_t t1 = t0 + d1;
+ long d2 = 60 * (long) pc.rel_minutes;
+ time_t t2 = t1 + d2;
+ int d3 = pc.rel_seconds;
+ time_t t3 = t2 + d3;
+ if ((d1 / (60 * 60) ^ pc.rel_hour)
+ | (d2 / 60 ^ pc.rel_minutes)
+ | ((t0 + d1 < t0) ^ (d1 < 0))
+ | ((t1 + d2 < t1) ^ (d2 < 0))
+ | ((t2 + d3 < t2) ^ (d3 < 0)))
+ return -1;
+ Start = t3;
+ }
+
+ return Start;
+}
+
+#if TEST
+
+#include <stdio.h>
+
+int
+main (int ac, char **av)
+{
+ char buff[BUFSIZ];
+ time_t d;
+
+ printf ("Enter date, or blank line to exit.\n\t> ");
+ fflush (stdout);
+
+ buff[BUFSIZ - 1] = 0;
+ while (fgets (buff, BUFSIZ - 1, stdin) && buff[0])
+ {
+ d = get_date (buff, 0);
+ if (d == (time_t) -1)
+ printf ("Bad format - couldn't convert.\n");
+ else
+ printf ("%s", ctime (&d));
+ printf ("\t> ");
+ fflush (stdout);
+ }
+ return 0;
+}
+#endif /* defined TEST */
+
+
diff --git a/source/modules/getdate.h b/source/modules/getdate.h
new file mode 100644
index 00000000000..674c474f115
--- /dev/null
+++ b/source/modules/getdate.h
@@ -0,0 +1,46 @@
+/* Copyright (C) 1995, 1997, 1998 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
+
+#if HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#ifndef PARAMS
+# if defined PROTOTYPES || (defined __STDC__ && __STDC__)
+# define PARAMS(Args) Args
+# else
+# define PARAMS(Args) ()
+# endif
+#endif
+
+#ifdef vms
+# include <types.h>
+# include <time.h>
+#else
+# include <sys/types.h>
+# if TIME_WITH_SYS_TIME
+# include <sys/time.h>
+# include <time.h>
+# else
+# if HAVE_SYS_TIME_H
+# include <sys/time.h>
+# else
+# include <time.h>
+# endif
+# endif
+#endif /* defined (vms) */
+
+time_t get_date PARAMS ((const char *p, const time_t *now));
diff --git a/source/modules/getdate.y b/source/modules/getdate.y
new file mode 100644
index 00000000000..aab37f4d235
--- /dev/null
+++ b/source/modules/getdate.y
@@ -0,0 +1,1115 @@
+%{
+/* Parse a string into an internal time stamp.
+ Copyright (C) 1999, 2000, 2002 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
+
+/* Originally written by Steven M. Bellovin <smb@research.att.com> while
+ at the University of North Carolina at Chapel Hill. Later tweaked by
+ a couple of people on Usenet. Completely overhauled by Rich $alz
+ <rsalz@bbn.com> and Jim Berets <jberets@bbn.com> in August, 1990.
+
+ Modified by Paul Eggert <eggert@twinsun.com> in August 1999 to do
+ the right thing about local DST. Unlike previous versions, this
+ version is reentrant. */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+# ifdef HAVE_ALLOCA_H
+# include <alloca.h>
+# endif
+#endif
+
+/* Since the code of getdate.y is not included in the Emacs executable
+ itself, there is no need to #define static in this file. Even if
+ the code were included in the Emacs executable, it probably
+ wouldn't do any harm to #undef it here; this will only cause
+ problems if we try to write to a static variable, which I don't
+ think this code needs to do. */
+#ifdef emacs
+# undef static
+#endif
+
+#include <ctype.h>
+
+#if HAVE_STDLIB_H
+# include <stdlib.h> /* for `free'; used by Bison 1.27 */
+#endif
+
+#if STDC_HEADERS || (! defined isascii && ! HAVE_ISASCII)
+# define IN_CTYPE_DOMAIN(c) 1
+#else
+# define IN_CTYPE_DOMAIN(c) isascii (c)
+#endif
+
+#define ISSPACE(c) (IN_CTYPE_DOMAIN (c) && isspace (c))
+#define ISALPHA(c) (IN_CTYPE_DOMAIN (c) && isalpha (c))
+#define ISLOWER(c) (IN_CTYPE_DOMAIN (c) && islower (c))
+#define ISDIGIT_LOCALE(c) (IN_CTYPE_DOMAIN (c) && isdigit (c))
+
+/* ISDIGIT differs from ISDIGIT_LOCALE, as follows:
+ - Its arg may be any int or unsigned int; it need not be an unsigned char.
+ - It's guaranteed to evaluate its argument exactly once.
+ - It's typically faster.
+ POSIX says that only '0' through '9' are digits. Prefer ISDIGIT to
+ ISDIGIT_LOCALE unless it's important to use the locale's definition
+ of `digit' even when the host does not conform to POSIX. */
+#define ISDIGIT(c) ((unsigned) (c) - '0' <= 9)
+
+#if STDC_HEADERS || HAVE_STRING_H
+# include <string.h>
+#endif
+
+#if __GNUC__ < 2 || (__GNUC__ == 2 && __GNUC_MINOR__ < 8) || __STRICT_ANSI__
+# define __attribute__(x)
+#endif
+
+#ifndef ATTRIBUTE_UNUSED
+# define ATTRIBUTE_UNUSED __attribute__ ((__unused__))
+#endif
+
+#define EPOCH_YEAR 1970
+#define TM_YEAR_BASE 1900
+
+#define HOUR(x) ((x) * 60)
+
+/* An integer value, and the number of digits in its textual
+ representation. */
+typedef struct
+{
+ int value;
+ int digits;
+} textint;
+
+/* An entry in the lexical lookup table. */
+typedef struct
+{
+ char const *name;
+ int type;
+ int value;
+} table;
+
+/* Meridian: am, pm, or 24-hour style. */
+enum { MERam, MERpm, MER24 };
+
+/* Information passed to and from the parser. */
+typedef struct
+{
+ /* The input string remaining to be parsed. */
+ const char *input;
+
+ /* N, if this is the Nth Tuesday. */
+ int day_ordinal;
+
+ /* Day of week; Sunday is 0. */
+ int day_number;
+
+ /* tm_isdst flag for the local zone. */
+ int local_isdst;
+
+ /* Time zone, in minutes east of UTC. */
+ int time_zone;
+
+ /* Style used for time. */
+ int meridian;
+
+ /* Gregorian year, month, day, hour, minutes, and seconds. */
+ textint year;
+ int month;
+ int day;
+ int hour;
+ int minutes;
+ int seconds;
+
+ /* Relative year, month, day, hour, minutes, and seconds. */
+ int rel_year;
+ int rel_month;
+ int rel_day;
+ int rel_hour;
+ int rel_minutes;
+ int rel_seconds;
+
+ /* Counts of nonterminals of various flavors parsed so far. */
+ int dates_seen;
+ int days_seen;
+ int local_zones_seen;
+ int rels_seen;
+ int times_seen;
+ int zones_seen;
+
+ /* Table of local time zone abbrevations, terminated by a null entry. */
+ table local_time_zone_table[3];
+} parser_control;
+
+#define PC (* (parser_control *) parm)
+#define YYLEX_PARAM parm
+#define YYPARSE_PARAM parm
+
+static int yyerror ();
+static int yylex ();
+
+%}
+
+/* We want a reentrant parser. */
+%pure_parser
+
+/* This grammar has 13 shift/reduce conflicts. */
+%expect 13
+
+%union
+{
+ int intval;
+ textint textintval;
+}
+
+%token tAGO tDST
+
+%token <intval> tDAY tDAY_UNIT tDAYZONE tHOUR_UNIT tLOCAL_ZONE tMERIDIAN
+%token <intval> tMINUTE_UNIT tMONTH tMONTH_UNIT tSEC_UNIT tYEAR_UNIT tZONE
+
+%token <textintval> tSNUMBER tUNUMBER
+
+%type <intval> o_merid
+
+%%
+
+spec:
+ /* empty */
+ | spec item
+ ;
+
+item:
+ time
+ { PC.times_seen++; }
+ | local_zone
+ { PC.local_zones_seen++; }
+ | zone
+ { PC.zones_seen++; }
+ | date
+ { PC.dates_seen++; }
+ | day
+ { PC.days_seen++; }
+ | rel
+ { PC.rels_seen++; }
+ | number
+ ;
+
+time:
+ tUNUMBER tMERIDIAN
+ {
+ PC.hour = $1.value;
+ PC.minutes = 0;
+ PC.seconds = 0;
+ PC.meridian = $2;
+ }
+ | tUNUMBER ':' tUNUMBER o_merid
+ {
+ PC.hour = $1.value;
+ PC.minutes = $3.value;
+ PC.seconds = 0;
+ PC.meridian = $4;
+ }
+ | tUNUMBER ':' tUNUMBER tSNUMBER
+ {
+ PC.hour = $1.value;
+ PC.minutes = $3.value;
+ PC.meridian = MER24;
+ PC.zones_seen++;
+ PC.time_zone = $4.value % 100 + ($4.value / 100) * 60;
+ }
+ | tUNUMBER ':' tUNUMBER ':' tUNUMBER o_merid
+ {
+ PC.hour = $1.value;
+ PC.minutes = $3.value;
+ PC.seconds = $5.value;
+ PC.meridian = $6;
+ }
+ | tUNUMBER ':' tUNUMBER ':' tUNUMBER tSNUMBER
+ {
+ PC.hour = $1.value;
+ PC.minutes = $3.value;
+ PC.seconds = $5.value;
+ PC.meridian = MER24;
+ PC.zones_seen++;
+ PC.time_zone = $6.value % 100 + ($6.value / 100) * 60;
+ }
+ ;
+
+local_zone:
+ tLOCAL_ZONE
+ { PC.local_isdst = $1; }
+ | tLOCAL_ZONE tDST
+ { PC.local_isdst = $1 < 0 ? 1 : $1 + 1; }
+ ;
+
+zone:
+ tZONE
+ { PC.time_zone = $1; }
+ | tDAYZONE
+ { PC.time_zone = $1 + 60; }
+ | tZONE tDST
+ { PC.time_zone = $1 + 60; }
+ ;
+
+day:
+ tDAY
+ {
+ PC.day_ordinal = 1;
+ PC.day_number = $1;
+ }
+ | tDAY ','
+ {
+ PC.day_ordinal = 1;
+ PC.day_number = $1;
+ }
+ | tUNUMBER tDAY
+ {
+ PC.day_ordinal = $1.value;
+ PC.day_number = $2;
+ }
+ ;
+
+date:
+ tUNUMBER '/' tUNUMBER
+ {
+ PC.month = $1.value;
+ PC.day = $3.value;
+ }
+ | tUNUMBER '/' tUNUMBER '/' tUNUMBER
+ {
+ /* Interpret as YYYY/MM/DD if the first value has 4 or more digits,
+ otherwise as MM/DD/YY.
+ The goal in recognizing YYYY/MM/DD is solely to support legacy
+ machine-generated dates like those in an RCS log listing. If
+ you want portability, use the ISO 8601 format. */
+ if (4 <= $1.digits)
+ {
+ PC.year = $1;
+ PC.month = $3.value;
+ PC.day = $5.value;
+ }
+ else
+ {
+ PC.month = $1.value;
+ PC.day = $3.value;
+ PC.year = $5;
+ }
+ }
+ | tUNUMBER tSNUMBER tSNUMBER
+ {
+ /* ISO 8601 format. YYYY-MM-DD. */
+ PC.year = $1;
+ PC.month = -$2.value;
+ PC.day = -$3.value;
+ }
+ | tUNUMBER tMONTH tSNUMBER
+ {
+ /* e.g. 17-JUN-1992. */
+ PC.day = $1.value;
+ PC.month = $2;
+ PC.year.value = -$3.value;
+ PC.year.digits = $3.digits;
+ }
+ | tMONTH tUNUMBER
+ {
+ PC.month = $1;
+ PC.day = $2.value;
+ }
+ | tMONTH tUNUMBER ',' tUNUMBER
+ {
+ PC.month = $1;
+ PC.day = $2.value;
+ PC.year = $4;
+ }
+ | tUNUMBER tMONTH
+ {
+ PC.day = $1.value;
+ PC.month = $2;
+ }
+ | tUNUMBER tMONTH tUNUMBER
+ {
+ PC.day = $1.value;
+ PC.month = $2;
+ PC.year = $3;
+ }
+ ;
+
+rel:
+ relunit tAGO
+ {
+ PC.rel_seconds = -PC.rel_seconds;
+ PC.rel_minutes = -PC.rel_minutes;
+ PC.rel_hour = -PC.rel_hour;
+ PC.rel_day = -PC.rel_day;
+ PC.rel_month = -PC.rel_month;
+ PC.rel_year = -PC.rel_year;
+ }
+ | relunit
+ ;
+
+relunit:
+ tUNUMBER tYEAR_UNIT
+ { PC.rel_year += $1.value * $2; }
+ | tSNUMBER tYEAR_UNIT
+ { PC.rel_year += $1.value * $2; }
+ | tYEAR_UNIT
+ { PC.rel_year += $1; }
+ | tUNUMBER tMONTH_UNIT
+ { PC.rel_month += $1.value * $2; }
+ | tSNUMBER tMONTH_UNIT
+ { PC.rel_month += $1.value * $2; }
+ | tMONTH_UNIT
+ { PC.rel_month += $1; }
+ | tUNUMBER tDAY_UNIT
+ { PC.rel_day += $1.value * $2; }
+ | tSNUMBER tDAY_UNIT
+ { PC.rel_day += $1.value * $2; }
+ | tDAY_UNIT
+ { PC.rel_day += $1; }
+ | tUNUMBER tHOUR_UNIT
+ { PC.rel_hour += $1.value * $2; }
+ | tSNUMBER tHOUR_UNIT
+ { PC.rel_hour += $1.value * $2; }
+ | tHOUR_UNIT
+ { PC.rel_hour += $1; }
+ | tUNUMBER tMINUTE_UNIT
+ { PC.rel_minutes += $1.value * $2; }
+ | tSNUMBER tMINUTE_UNIT
+ { PC.rel_minutes += $1.value * $2; }
+ | tMINUTE_UNIT
+ { PC.rel_minutes += $1; }
+ | tUNUMBER tSEC_UNIT
+ { PC.rel_seconds += $1.value * $2; }
+ | tSNUMBER tSEC_UNIT
+ { PC.rel_seconds += $1.value * $2; }
+ | tSEC_UNIT
+ { PC.rel_seconds += $1; }
+ ;
+
+number:
+ tUNUMBER
+ {
+ if (PC.dates_seen
+ && ! PC.rels_seen && (PC.times_seen || 2 < $1.digits))
+ PC.year = $1;
+ else
+ {
+ if (4 < $1.digits)
+ {
+ PC.dates_seen++;
+ PC.day = $1.value % 100;
+ PC.month = ($1.value / 100) % 100;
+ PC.year.value = $1.value / 10000;
+ PC.year.digits = $1.digits - 4;
+ }
+ else
+ {
+ PC.times_seen++;
+ if ($1.digits <= 2)
+ {
+ PC.hour = $1.value;
+ PC.minutes = 0;
+ }
+ else
+ {
+ PC.hour = $1.value / 100;
+ PC.minutes = $1.value % 100;
+ }
+ PC.seconds = 0;
+ PC.meridian = MER24;
+ }
+ }
+ }
+ ;
+
+o_merid:
+ /* empty */
+ { $$ = MER24; }
+ | tMERIDIAN
+ { $$ = $1; }
+ ;
+
+%%
+
+/* Include this file down here because bison inserts code above which
+ may define-away `const'. We want the prototype for get_date to have
+ the same signature as the function definition. */
+#include "modules/getdate.h"
+
+#ifndef gmtime
+struct tm *gmtime ();
+#endif
+#ifndef localtime
+struct tm *localtime ();
+#endif
+#ifndef mktime
+time_t mktime ();
+#endif
+
+static table const meridian_table[] =
+{
+ { "AM", tMERIDIAN, MERam },
+ { "A.M.", tMERIDIAN, MERam },
+ { "PM", tMERIDIAN, MERpm },
+ { "P.M.", tMERIDIAN, MERpm },
+ { 0, 0, 0 }
+};
+
+static table const dst_table[] =
+{
+ { "DST", tDST, 0 }
+};
+
+static table const month_and_day_table[] =
+{
+ { "JANUARY", tMONTH, 1 },
+ { "FEBRUARY", tMONTH, 2 },
+ { "MARCH", tMONTH, 3 },
+ { "APRIL", tMONTH, 4 },
+ { "MAY", tMONTH, 5 },
+ { "JUNE", tMONTH, 6 },
+ { "JULY", tMONTH, 7 },
+ { "AUGUST", tMONTH, 8 },
+ { "SEPTEMBER",tMONTH, 9 },
+ { "SEPT", tMONTH, 9 },
+ { "OCTOBER", tMONTH, 10 },
+ { "NOVEMBER", tMONTH, 11 },
+ { "DECEMBER", tMONTH, 12 },
+ { "SUNDAY", tDAY, 0 },
+ { "MONDAY", tDAY, 1 },
+ { "TUESDAY", tDAY, 2 },
+ { "TUES", tDAY, 2 },
+ { "WEDNESDAY",tDAY, 3 },
+ { "WEDNES", tDAY, 3 },
+ { "THURSDAY", tDAY, 4 },
+ { "THUR", tDAY, 4 },
+ { "THURS", tDAY, 4 },
+ { "FRIDAY", tDAY, 5 },
+ { "SATURDAY", tDAY, 6 },
+ { 0, 0, 0 }
+};
+
+static table const time_units_table[] =
+{
+ { "YEAR", tYEAR_UNIT, 1 },
+ { "MONTH", tMONTH_UNIT, 1 },
+ { "FORTNIGHT",tDAY_UNIT, 14 },
+ { "WEEK", tDAY_UNIT, 7 },
+ { "DAY", tDAY_UNIT, 1 },
+ { "HOUR", tHOUR_UNIT, 1 },
+ { "MINUTE", tMINUTE_UNIT, 1 },
+ { "MIN", tMINUTE_UNIT, 1 },
+ { "SECOND", tSEC_UNIT, 1 },
+ { "SEC", tSEC_UNIT, 1 },
+ { 0, 0, 0 }
+};
+
+/* Assorted relative-time words. */
+static table const relative_time_table[] =
+{
+ { "TOMORROW", tMINUTE_UNIT, 24 * 60 },
+ { "YESTERDAY",tMINUTE_UNIT, - (24 * 60) },
+ { "TODAY", tMINUTE_UNIT, 0 },
+ { "NOW", tMINUTE_UNIT, 0 },
+ { "LAST", tUNUMBER, -1 },
+ { "THIS", tUNUMBER, 0 },
+ { "NEXT", tUNUMBER, 1 },
+ { "FIRST", tUNUMBER, 1 },
+/*{ "SECOND", tUNUMBER, 2 }, */
+ { "THIRD", tUNUMBER, 3 },
+ { "FOURTH", tUNUMBER, 4 },
+ { "FIFTH", tUNUMBER, 5 },
+ { "SIXTH", tUNUMBER, 6 },
+ { "SEVENTH", tUNUMBER, 7 },
+ { "EIGHTH", tUNUMBER, 8 },
+ { "NINTH", tUNUMBER, 9 },
+ { "TENTH", tUNUMBER, 10 },
+ { "ELEVENTH", tUNUMBER, 11 },
+ { "TWELFTH", tUNUMBER, 12 },
+ { "AGO", tAGO, 1 },
+ { 0, 0, 0 }
+};
+
+/* The time zone table. This table is necessarily incomplete, as time
+ zone abbreviations are ambiguous; e.g. Australians interpret "EST"
+ as Eastern time in Australia, not as US Eastern Standard Time.
+ You cannot rely on getdate to handle arbitrary time zone
+ abbreviations; use numeric abbreviations like `-0500' instead. */
+static table const time_zone_table[] =
+{
+ { "GMT", tZONE, HOUR ( 0) }, /* Greenwich Mean */
+ { "UT", tZONE, HOUR ( 0) }, /* Universal (Coordinated) */
+ { "UTC", tZONE, HOUR ( 0) },
+ { "WET", tZONE, HOUR ( 0) }, /* Western European */
+ { "WEST", tDAYZONE, HOUR ( 0) }, /* Western European Summer */
+ { "BST", tDAYZONE, HOUR ( 0) }, /* British Summer */
+ { "ART", tZONE, -HOUR ( 3) }, /* Argentina */
+ { "BRT", tZONE, -HOUR ( 3) }, /* Brazil */
+ { "BRST", tDAYZONE, -HOUR ( 3) }, /* Brazil Summer */
+ { "NST", tZONE, -(HOUR ( 3) + 30) }, /* Newfoundland Standard */
+ { "NDT", tDAYZONE,-(HOUR ( 3) + 30) }, /* Newfoundland Daylight */
+ { "AST", tZONE, -HOUR ( 4) }, /* Atlantic Standard */
+ { "ADT", tDAYZONE, -HOUR ( 4) }, /* Atlantic Daylight */
+ { "CLT", tZONE, -HOUR ( 4) }, /* Chile */
+ { "CLST", tDAYZONE, -HOUR ( 4) }, /* Chile Summer */
+ { "EST", tZONE, -HOUR ( 5) }, /* Eastern Standard */
+ { "EDT", tDAYZONE, -HOUR ( 5) }, /* Eastern Daylight */
+ { "CST", tZONE, -HOUR ( 6) }, /* Central Standard */
+ { "CDT", tDAYZONE, -HOUR ( 6) }, /* Central Daylight */
+ { "MST", tZONE, -HOUR ( 7) }, /* Mountain Standard */
+ { "MDT", tDAYZONE, -HOUR ( 7) }, /* Mountain Daylight */
+ { "PST", tZONE, -HOUR ( 8) }, /* Pacific Standard */
+ { "PDT", tDAYZONE, -HOUR ( 8) }, /* Pacific Daylight */
+ { "AKST", tZONE, -HOUR ( 9) }, /* Alaska Standard */
+ { "AKDT", tDAYZONE, -HOUR ( 9) }, /* Alaska Daylight */
+ { "HST", tZONE, -HOUR (10) }, /* Hawaii Standard */
+ { "HAST", tZONE, -HOUR (10) }, /* Hawaii-Aleutian Standard */
+ { "HADT", tDAYZONE, -HOUR (10) }, /* Hawaii-Aleutian Daylight */
+ { "SST", tZONE, -HOUR (12) }, /* Samoa Standard */
+ { "WAT", tZONE, HOUR ( 1) }, /* West Africa */
+ { "CET", tZONE, HOUR ( 1) }, /* Central European */
+ { "CEST", tDAYZONE, HOUR ( 1) }, /* Central European Summer */
+ { "MET", tZONE, HOUR ( 1) }, /* Middle European */
+ { "MEZ", tZONE, HOUR ( 1) }, /* Middle European */
+ { "MEST", tDAYZONE, HOUR ( 1) }, /* Middle European Summer */
+ { "MESZ", tDAYZONE, HOUR ( 1) }, /* Middle European Summer */
+ { "EET", tZONE, HOUR ( 2) }, /* Eastern European */
+ { "EEST", tDAYZONE, HOUR ( 2) }, /* Eastern European Summer */
+ { "CAT", tZONE, HOUR ( 2) }, /* Central Africa */
+ { "SAST", tZONE, HOUR ( 2) }, /* South Africa Standard */
+ { "EAT", tZONE, HOUR ( 3) }, /* East Africa */
+ { "MSK", tZONE, HOUR ( 3) }, /* Moscow */
+ { "MSD", tDAYZONE, HOUR ( 3) }, /* Moscow Daylight */
+ { "IST", tZONE, (HOUR ( 5) + 30) }, /* India Standard */
+ { "SGT", tZONE, HOUR ( 8) }, /* Singapore */
+ { "KST", tZONE, HOUR ( 9) }, /* Korea Standard */
+ { "JST", tZONE, HOUR ( 9) }, /* Japan Standard */
+ { "GST", tZONE, HOUR (10) }, /* Guam Standard */
+ { "NZST", tZONE, HOUR (12) }, /* New Zealand Standard */
+ { "NZDT", tDAYZONE, HOUR (12) }, /* New Zealand Daylight */
+ { 0, 0, 0 }
+};
+
+/* Military time zone table. */
+static table const military_table[] =
+{
+ { "A", tZONE, -HOUR ( 1) },
+ { "B", tZONE, -HOUR ( 2) },
+ { "C", tZONE, -HOUR ( 3) },
+ { "D", tZONE, -HOUR ( 4) },
+ { "E", tZONE, -HOUR ( 5) },
+ { "F", tZONE, -HOUR ( 6) },
+ { "G", tZONE, -HOUR ( 7) },
+ { "H", tZONE, -HOUR ( 8) },
+ { "I", tZONE, -HOUR ( 9) },
+ { "K", tZONE, -HOUR (10) },
+ { "L", tZONE, -HOUR (11) },
+ { "M", tZONE, -HOUR (12) },
+ { "N", tZONE, HOUR ( 1) },
+ { "O", tZONE, HOUR ( 2) },
+ { "P", tZONE, HOUR ( 3) },
+ { "Q", tZONE, HOUR ( 4) },
+ { "R", tZONE, HOUR ( 5) },
+ { "S", tZONE, HOUR ( 6) },
+ { "T", tZONE, HOUR ( 7) },
+ { "U", tZONE, HOUR ( 8) },
+ { "V", tZONE, HOUR ( 9) },
+ { "W", tZONE, HOUR (10) },
+ { "X", tZONE, HOUR (11) },
+ { "Y", tZONE, HOUR (12) },
+ { "Z", tZONE, HOUR ( 0) },
+ { 0, 0, 0 }
+};
+
+
+
+static int
+to_hour (int hours, int meridian)
+{
+ switch (meridian)
+ {
+ case MER24:
+ return 0 <= hours && hours < 24 ? hours : -1;
+ case MERam:
+ return 0 < hours && hours < 12 ? hours : hours == 12 ? 0 : -1;
+ case MERpm:
+ return 0 < hours && hours < 12 ? hours + 12 : hours == 12 ? 12 : -1;
+ default:
+ abort ();
+ }
+ /* NOTREACHED */
+ return 0;
+}
+
+static int
+to_year (textint textyear)
+{
+ int year = textyear.value;
+
+ if (year < 0)
+ year = -year;
+
+ /* XPG4 suggests that years 00-68 map to 2000-2068, and
+ years 69-99 map to 1969-1999. */
+ if (textyear.digits == 2)
+ year += year < 69 ? 2000 : 1900;
+
+ return year;
+}
+
+static table const *
+lookup_zone (parser_control const *pc, char const *name)
+{
+ table const *tp;
+
+ /* Try local zone abbreviations first; they're more likely to be right. */
+ for (tp = pc->local_time_zone_table; tp->name; tp++)
+ if (strcmp (name, tp->name) == 0)
+ return tp;
+
+ for (tp = time_zone_table; tp->name; tp++)
+ if (strcmp (name, tp->name) == 0)
+ return tp;
+
+ return 0;
+}
+
+#if ! HAVE_TM_GMTOFF
+/* Yield the difference between *A and *B,
+ measured in seconds, ignoring leap seconds.
+ The body of this function is taken directly from the GNU C Library;
+ see src/strftime.c. */
+static int
+tm_diff (struct tm const *a, struct tm const *b)
+{
+ /* Compute intervening leap days correctly even if year is negative.
+ Take care to avoid int overflow in leap day calculations,
+ but it's OK to assume that A and B are close to each other. */
+ int a4 = (a->tm_year >> 2) + (TM_YEAR_BASE >> 2) - ! (a->tm_year & 3);
+ int b4 = (b->tm_year >> 2) + (TM_YEAR_BASE >> 2) - ! (b->tm_year & 3);
+ int a100 = a4 / 25 - (a4 % 25 < 0);
+ int b100 = b4 / 25 - (b4 % 25 < 0);
+ int a400 = a100 >> 2;
+ int b400 = b100 >> 2;
+ int intervening_leap_days = (a4 - b4) - (a100 - b100) + (a400 - b400);
+ int years = a->tm_year - b->tm_year;
+ int days = (365 * years + intervening_leap_days
+ + (a->tm_yday - b->tm_yday));
+ return (60 * (60 * (24 * days + (a->tm_hour - b->tm_hour))
+ + (a->tm_min - b->tm_min))
+ + (a->tm_sec - b->tm_sec));
+}
+#endif /* ! HAVE_TM_GMTOFF */
+
+static table const *
+lookup_word (parser_control const *pc, char *word)
+{
+ char *p;
+ char *q;
+ size_t wordlen;
+ table const *tp;
+ int i;
+ int abbrev;
+
+ /* Make it uppercase. */
+ for (p = word; *p; p++)
+ if (ISLOWER ((unsigned char) *p))
+ *p = toupper ((unsigned char) *p);
+
+ for (tp = meridian_table; tp->name; tp++)
+ if (strcmp (word, tp->name) == 0)
+ return tp;
+
+ /* See if we have an abbreviation for a month. */
+ wordlen = strlen (word);
+ abbrev = wordlen == 3 || (wordlen == 4 && word[3] == '.');
+
+ for (tp = month_and_day_table; tp->name; tp++)
+ if ((abbrev ? strncmp (word, tp->name, 3) : strcmp (word, tp->name)) == 0)
+ return tp;
+
+ if ((tp = lookup_zone (pc, word)))
+ return tp;
+
+ if (strcmp (word, dst_table[0].name) == 0)
+ return dst_table;
+
+ for (tp = time_units_table; tp->name; tp++)
+ if (strcmp (word, tp->name) == 0)
+ return tp;
+
+ /* Strip off any plural and try the units table again. */
+ if (word[wordlen - 1] == 'S')
+ {
+ word[wordlen - 1] = '\0';
+ for (tp = time_units_table; tp->name; tp++)
+ if (strcmp (word, tp->name) == 0)
+ return tp;
+ word[wordlen - 1] = 'S'; /* For "this" in relative_time_table. */
+ }
+
+ for (tp = relative_time_table; tp->name; tp++)
+ if (strcmp (word, tp->name) == 0)
+ return tp;
+
+ /* Military time zones. */
+ if (wordlen == 1)
+ for (tp = military_table; tp->name; tp++)
+ if (word[0] == tp->name[0])
+ return tp;
+
+ /* Drop out any periods and try the time zone table again. */
+ for (i = 0, p = q = word; (*p = *q); q++)
+ if (*q == '.')
+ i = 1;
+ else
+ p++;
+ if (i && (tp = lookup_zone (pc, word)))
+ return tp;
+
+ return 0;
+}
+
+static int
+yylex (YYSTYPE *lvalp, parser_control *pc)
+{
+ unsigned char c;
+ int count;
+
+ for (;;)
+ {
+ while (c = *pc->input, ISSPACE (c))
+ pc->input++;
+
+ if (ISDIGIT (c) || c == '-' || c == '+')
+ {
+ char const *p;
+ int sign;
+ int value;
+ if (c == '-' || c == '+')
+ {
+ sign = c == '-' ? -1 : 1;
+ c = *++pc->input;
+ if (! ISDIGIT (c))
+ /* skip the '-' sign */
+ continue;
+ }
+ else
+ sign = 0;
+ p = pc->input;
+ value = 0;
+ do
+ {
+ value = 10 * value + c - '0';
+ c = *++p;
+ }
+ while (ISDIGIT (c));
+ lvalp->textintval.value = sign < 0 ? -value : value;
+ lvalp->textintval.digits = p - pc->input;
+ pc->input = p;
+ return sign ? tSNUMBER : tUNUMBER;
+ }
+
+ if (ISALPHA (c))
+ {
+ char buff[20];
+ char *p = buff;
+ table const *tp;
+
+ do
+ {
+ if (p < buff + sizeof buff - 1)
+ *p++ = c;
+ c = *++pc->input;
+ }
+ while (ISALPHA (c) || c == '.');
+
+ *p = '\0';
+ tp = lookup_word (pc, buff);
+ if (! tp)
+ return '?';
+ lvalp->intval = tp->value;
+ return tp->type;
+ }
+
+ if (c != '(')
+ return *pc->input++;
+ count = 0;
+ do
+ {
+ c = *pc->input++;
+ if (c == '\0')
+ return c;
+ if (c == '(')
+ count++;
+ else if (c == ')')
+ count--;
+ }
+ while (count > 0);
+ }
+}
+
+/* Do nothing if the parser reports an error. */
+static int
+yyerror (char *s ATTRIBUTE_UNUSED)
+{
+ return 0;
+}
+
+/* Parse a date/time string P. Return the corresponding time_t value,
+ or (time_t) -1 if there is an error. P can be an incomplete or
+ relative time specification; if so, use *NOW as the basis for the
+ returned time. */
+time_t
+get_date (const char *p, const time_t *now)
+{
+ time_t Start = now ? *now : time (0);
+ struct tm *tmp = localtime (&Start);
+ struct tm tm;
+ struct tm tm0;
+ parser_control pc;
+
+ if (! tmp)
+ return -1;
+
+ pc.input = p;
+ pc.year.value = tmp->tm_year + TM_YEAR_BASE;
+ pc.year.digits = 4;
+ pc.month = tmp->tm_mon + 1;
+ pc.day = tmp->tm_mday;
+ pc.hour = tmp->tm_hour;
+ pc.minutes = tmp->tm_min;
+ pc.seconds = tmp->tm_sec;
+ tm.tm_isdst = tmp->tm_isdst;
+
+ pc.meridian = MER24;
+ pc.rel_seconds = 0;
+ pc.rel_minutes = 0;
+ pc.rel_hour = 0;
+ pc.rel_day = 0;
+ pc.rel_month = 0;
+ pc.rel_year = 0;
+ pc.dates_seen = 0;
+ pc.days_seen = 0;
+ pc.rels_seen = 0;
+ pc.times_seen = 0;
+ pc.local_zones_seen = 0;
+ pc.zones_seen = 0;
+
+#if HAVE_STRUCT_TM_TM_ZONE
+ pc.local_time_zone_table[0].name = tmp->tm_zone;
+ pc.local_time_zone_table[0].type = tLOCAL_ZONE;
+ pc.local_time_zone_table[0].value = tmp->tm_isdst;
+ pc.local_time_zone_table[1].name = 0;
+
+ /* Probe the names used in the next three calendar quarters, looking
+ for a tm_isdst different from the one we already have. */
+ {
+ int quarter;
+ for (quarter = 1; quarter <= 3; quarter++)
+ {
+ time_t probe = Start + quarter * (90 * 24 * 60 * 60);
+ struct tm *probe_tm = localtime (&probe);
+ if (probe_tm && probe_tm->tm_zone
+ && probe_tm->tm_isdst != pc.local_time_zone_table[0].value)
+ {
+ {
+ pc.local_time_zone_table[1].name = probe_tm->tm_zone;
+ pc.local_time_zone_table[1].type = tLOCAL_ZONE;
+ pc.local_time_zone_table[1].value = probe_tm->tm_isdst;
+ pc.local_time_zone_table[2].name = 0;
+ }
+ break;
+ }
+ }
+ }
+#else
+#if HAVE_TZNAME
+ {
+# ifndef tzname
+ extern char *tzname[];
+# endif
+ int i;
+ for (i = 0; i < 2; i++)
+ {
+ pc.local_time_zone_table[i].name = tzname[i];
+ pc.local_time_zone_table[i].type = tLOCAL_ZONE;
+ pc.local_time_zone_table[i].value = i;
+ }
+ pc.local_time_zone_table[i].name = 0;
+ }
+#else
+ pc.local_time_zone_table[0].name = 0;
+#endif
+#endif
+
+ if (pc.local_time_zone_table[0].name && pc.local_time_zone_table[1].name
+ && ! strcmp (pc.local_time_zone_table[0].name,
+ pc.local_time_zone_table[1].name))
+ {
+ /* This locale uses the same abbrevation for standard and
+ daylight times. So if we see that abbreviation, we don't
+ know whether it's daylight time. */
+ pc.local_time_zone_table[0].value = -1;
+ pc.local_time_zone_table[1].name = 0;
+ }
+
+ if (yyparse (&pc) != 0
+ || 1 < pc.times_seen || 1 < pc.dates_seen || 1 < pc.days_seen
+ || 1 < (pc.local_zones_seen + pc.zones_seen)
+ || (pc.local_zones_seen && 1 < pc.local_isdst))
+ return -1;
+
+ tm.tm_year = to_year (pc.year) - TM_YEAR_BASE + pc.rel_year;
+ tm.tm_mon = pc.month - 1 + pc.rel_month;
+ tm.tm_mday = pc.day + pc.rel_day;
+ if (pc.times_seen || (pc.rels_seen && ! pc.dates_seen && ! pc.days_seen))
+ {
+ tm.tm_hour = to_hour (pc.hour, pc.meridian);
+ if (tm.tm_hour < 0)
+ return -1;
+ tm.tm_min = pc.minutes;
+ tm.tm_sec = pc.seconds;
+ }
+ else
+ {
+ tm.tm_hour = tm.tm_min = tm.tm_sec = 0;
+ }
+
+ /* Let mktime deduce tm_isdst if we have an absolute time stamp,
+ or if the relative time stamp mentions days, months, or years. */
+ if (pc.dates_seen | pc.days_seen | pc.times_seen | pc.rel_day
+ | pc.rel_month | pc.rel_year)
+ tm.tm_isdst = -1;
+
+ /* But if the input explicitly specifies local time with or without
+ DST, give mktime that information. */
+ if (pc.local_zones_seen)
+ tm.tm_isdst = pc.local_isdst;
+
+ tm0 = tm;
+
+ Start = mktime (&tm);
+
+ if (Start == (time_t) -1)
+ {
+
+ /* Guard against falsely reporting errors near the time_t boundaries
+ when parsing times in other time zones. For example, if the min
+ time_t value is 1970-01-01 00:00:00 UTC and we are 8 hours ahead
+ of UTC, then the min localtime value is 1970-01-01 08:00:00; if
+ we apply mktime to 1970-01-01 00:00:00 we will get an error, so
+ we apply mktime to 1970-01-02 08:00:00 instead and adjust the time
+ zone by 24 hours to compensate. This algorithm assumes that
+ there is no DST transition within a day of the time_t boundaries. */
+ if (pc.zones_seen)
+ {
+ tm = tm0;
+ if (tm.tm_year <= EPOCH_YEAR - TM_YEAR_BASE)
+ {
+ tm.tm_mday++;
+ pc.time_zone += 24 * 60;
+ }
+ else
+ {
+ tm.tm_mday--;
+ pc.time_zone -= 24 * 60;
+ }
+ Start = mktime (&tm);
+ }
+
+ if (Start == (time_t) -1)
+ return Start;
+ }
+
+ if (pc.days_seen && ! pc.dates_seen)
+ {
+ tm.tm_mday += ((pc.day_number - tm.tm_wday + 7) % 7
+ + 7 * (pc.day_ordinal - (0 < pc.day_ordinal)));
+ tm.tm_isdst = -1;
+ Start = mktime (&tm);
+ if (Start == (time_t) -1)
+ return Start;
+ }
+
+ if (pc.zones_seen)
+ {
+ int delta = pc.time_zone * 60;
+#ifdef HAVE_TM_GMTOFF
+ delta -= tm.tm_gmtoff;
+#else
+ struct tm *gmt = gmtime (&Start);
+ if (! gmt)
+ return -1;
+ delta -= tm_diff (&tm, gmt);
+#endif
+ if ((Start < Start - delta) != (delta < 0))
+ return -1; /* time_t overflow */
+ Start -= delta;
+ }
+
+ /* Add relative hours, minutes, and seconds. Ignore leap seconds;
+ i.e. "+ 10 minutes" means 600 seconds, even if one of them is a
+ leap second. Typically this is not what the user wants, but it's
+ too hard to do it the other way, because the time zone indicator
+ must be applied before relative times, and if mktime is applied
+ again the time zone will be lost. */
+ {
+ time_t t0 = Start;
+ long d1 = 60 * 60 * (long) pc.rel_hour;
+ time_t t1 = t0 + d1;
+ long d2 = 60 * (long) pc.rel_minutes;
+ time_t t2 = t1 + d2;
+ int d3 = pc.rel_seconds;
+ time_t t3 = t2 + d3;
+ if ((d1 / (60 * 60) ^ pc.rel_hour)
+ | (d2 / 60 ^ pc.rel_minutes)
+ | ((t0 + d1 < t0) ^ (d1 < 0))
+ | ((t1 + d2 < t1) ^ (d2 < 0))
+ | ((t2 + d3 < t2) ^ (d3 < 0)))
+ return -1;
+ Start = t3;
+ }
+
+ return Start;
+}
+
+#if TEST
+
+#include <stdio.h>
+
+int
+main (int ac, char **av)
+{
+ char buff[BUFSIZ];
+ time_t d;
+
+ printf ("Enter date, or blank line to exit.\n\t> ");
+ fflush (stdout);
+
+ buff[BUFSIZ - 1] = 0;
+ while (fgets (buff, BUFSIZ - 1, stdin) && buff[0])
+ {
+ d = get_date (buff, 0);
+ if (d == (time_t) -1)
+ printf ("Bad format - couldn't convert.\n");
+ else
+ printf ("%s", ctime (&d));
+ printf ("\t> ");
+ fflush (stdout);
+ }
+ return 0;
+}
+#endif /* defined TEST */
diff --git a/source/modules/vfs_audit.c b/source/modules/vfs_audit.c
new file mode 100644
index 00000000000..550d918b43c
--- /dev/null
+++ b/source/modules/vfs_audit.c
@@ -0,0 +1,280 @@
+/*
+ * Auditing VFS module for samba. Log selected file operations to syslog
+ * facility.
+ *
+ * Copyright (C) Tim Potter 1999-2000
+ * Copyright (C) Alexander Bokovoy 2002
+ * Copyright (C) Stefan (metze) Metzmacher 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 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"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_VFS
+
+/* Function prototypes */
+
+static int audit_connect(vfs_handle_struct *handle, connection_struct *conn, const char *svc, const char *user);
+static void audit_disconnect(vfs_handle_struct *handle, connection_struct *conn);
+static DIR *audit_opendir(vfs_handle_struct *handle, connection_struct *conn, const char *fname);
+static int audit_mkdir(vfs_handle_struct *handle, connection_struct *conn, const char *path, mode_t mode);
+static int audit_rmdir(vfs_handle_struct *handle, connection_struct *conn, const char *path);
+static int audit_open(vfs_handle_struct *handle, connection_struct *conn, const char *fname, int flags, mode_t mode);
+static int audit_close(vfs_handle_struct *handle, files_struct *fsp, int fd);
+static int audit_rename(vfs_handle_struct *handle, connection_struct *conn, const char *old, const char *new);
+static int audit_unlink(vfs_handle_struct *handle, connection_struct *conn, const char *path);
+static int audit_chmod(vfs_handle_struct *handle, connection_struct *conn, const char *path, mode_t mode);
+static int audit_chmod_acl(vfs_handle_struct *handle, connection_struct *conn, const char *name, mode_t mode);
+static int audit_fchmod(vfs_handle_struct *handle, files_struct *fsp, int fd, mode_t mode);
+static int audit_fchmod_acl(vfs_handle_struct *handle, files_struct *fsp, int fd, mode_t mode);
+
+/* VFS operations */
+
+static vfs_op_tuple audit_op_tuples[] = {
+
+ /* Disk operations */
+
+ {SMB_VFS_OP(audit_connect), SMB_VFS_OP_CONNECT, SMB_VFS_LAYER_LOGGER},
+ {SMB_VFS_OP(audit_disconnect), SMB_VFS_OP_DISCONNECT, SMB_VFS_LAYER_LOGGER},
+
+ /* Directory operations */
+
+ {SMB_VFS_OP(audit_opendir), SMB_VFS_OP_OPENDIR, SMB_VFS_LAYER_LOGGER},
+ {SMB_VFS_OP(audit_mkdir), SMB_VFS_OP_MKDIR, SMB_VFS_LAYER_LOGGER},
+ {SMB_VFS_OP(audit_rmdir), SMB_VFS_OP_RMDIR, SMB_VFS_LAYER_LOGGER},
+
+ /* File operations */
+
+ {SMB_VFS_OP(audit_open), SMB_VFS_OP_OPEN, SMB_VFS_LAYER_LOGGER},
+ {SMB_VFS_OP(audit_close), SMB_VFS_OP_CLOSE, SMB_VFS_LAYER_LOGGER},
+ {SMB_VFS_OP(audit_rename), SMB_VFS_OP_RENAME, SMB_VFS_LAYER_LOGGER},
+ {SMB_VFS_OP(audit_unlink), SMB_VFS_OP_UNLINK, SMB_VFS_LAYER_LOGGER},
+ {SMB_VFS_OP(audit_chmod), SMB_VFS_OP_CHMOD, SMB_VFS_LAYER_LOGGER},
+ {SMB_VFS_OP(audit_fchmod), SMB_VFS_OP_FCHMOD, SMB_VFS_LAYER_LOGGER},
+ {SMB_VFS_OP(audit_chmod_acl), SMB_VFS_OP_CHMOD_ACL, SMB_VFS_LAYER_LOGGER},
+ {SMB_VFS_OP(audit_fchmod_acl), SMB_VFS_OP_FCHMOD_ACL, SMB_VFS_LAYER_LOGGER},
+
+ /* Finish VFS operations definition */
+
+ {SMB_VFS_OP(NULL), SMB_VFS_OP_NOOP, SMB_VFS_LAYER_NOOP}
+};
+
+
+static int audit_syslog_facility(vfs_handle_struct *handle)
+{
+ /* fix me: let this be configurable by:
+ * lp_param_enum(SNUM(handle->conn),(handle->param?handle->param:"audit"),"syslog facility",
+ * audit_enum_facility,LOG_USER);
+ */
+ return LOG_USER;
+}
+
+
+static int audit_syslog_priority(vfs_handle_struct *handle)
+{
+ /* fix me: let this be configurable by:
+ * lp_param_enum(SNUM(handle->conn),(handle->param?handle->param:"audit"),"syslog priority",
+ * audit_enum_priority,LOG_NOTICE);
+ */
+ return LOG_NOTICE;
+}
+
+/* Implementation of vfs_ops. Pass everything on to the default
+ operation but log event first. */
+
+static int audit_connect(vfs_handle_struct *handle, connection_struct *conn, const char *svc, const char *user)
+{
+ int result;
+
+ openlog("smbd_audit", LOG_PID, audit_syslog_facility(handle));
+
+ syslog(audit_syslog_priority(handle), "connect to service %s by user %s\n",
+ svc, user);
+
+ result = SMB_VFS_NEXT_CONNECT(handle, conn, svc, user);
+
+ return result;
+}
+
+static void audit_disconnect(vfs_handle_struct *handle, connection_struct *conn)
+{
+ syslog(audit_syslog_priority(handle), "disconnected\n");
+ SMB_VFS_NEXT_DISCONNECT(handle, conn);
+
+ return;
+}
+
+static DIR *audit_opendir(vfs_handle_struct *handle, connection_struct *conn, const char *fname)
+{
+ DIR *result;
+
+ result = SMB_VFS_NEXT_OPENDIR(handle, conn, fname);
+
+ syslog(audit_syslog_priority(handle), "opendir %s %s%s\n",
+ fname,
+ (result == NULL) ? "failed: " : "",
+ (result == NULL) ? strerror(errno) : "");
+
+ return result;
+}
+
+static int audit_mkdir(vfs_handle_struct *handle, connection_struct *conn, const char *path, mode_t mode)
+{
+ int result;
+
+ result = SMB_VFS_NEXT_MKDIR(handle, conn, path, mode);
+
+ syslog(audit_syslog_priority(handle), "mkdir %s %s%s\n",
+ path,
+ (result < 0) ? "failed: " : "",
+ (result < 0) ? strerror(errno) : "");
+
+ return result;
+}
+
+static int audit_rmdir(vfs_handle_struct *handle, connection_struct *conn, const char *path)
+{
+ int result;
+
+ result = SMB_VFS_NEXT_RMDIR(handle, conn, path);
+
+ syslog(audit_syslog_priority(handle), "rmdir %s %s%s\n",
+ path,
+ (result < 0) ? "failed: " : "",
+ (result < 0) ? strerror(errno) : "");
+
+ return result;
+}
+
+static int audit_open(vfs_handle_struct *handle, connection_struct *conn, const char *fname, int flags, mode_t mode)
+{
+ int result;
+
+ result = SMB_VFS_NEXT_OPEN(handle, conn, fname, flags, mode);
+
+ syslog(audit_syslog_priority(handle), "open %s (fd %d) %s%s%s\n",
+ fname, result,
+ ((flags & O_WRONLY) || (flags & O_RDWR)) ? "for writing " : "",
+ (result < 0) ? "failed: " : "",
+ (result < 0) ? strerror(errno) : "");
+
+ return result;
+}
+
+static int audit_close(vfs_handle_struct *handle, files_struct *fsp, int fd)
+{
+ int result;
+
+ result = SMB_VFS_NEXT_CLOSE(handle, fsp, fd);
+
+ syslog(audit_syslog_priority(handle), "close fd %d %s%s\n",
+ fd,
+ (result < 0) ? "failed: " : "",
+ (result < 0) ? strerror(errno) : "");
+
+ return result;
+}
+
+static int audit_rename(vfs_handle_struct *handle, connection_struct *conn, const char *old, const char *new)
+{
+ int result;
+
+ result = SMB_VFS_NEXT_RENAME(handle, conn, old, new);
+
+ syslog(audit_syslog_priority(handle), "rename %s -> %s %s%s\n",
+ old, new,
+ (result < 0) ? "failed: " : "",
+ (result < 0) ? strerror(errno) : "");
+
+ return result;
+}
+
+static int audit_unlink(vfs_handle_struct *handle, connection_struct *conn, const char *path)
+{
+ int result;
+
+ result = SMB_VFS_NEXT_UNLINK(handle, conn, path);
+
+ syslog(audit_syslog_priority(handle), "unlink %s %s%s\n",
+ path,
+ (result < 0) ? "failed: " : "",
+ (result < 0) ? strerror(errno) : "");
+
+ return result;
+}
+
+static int audit_chmod(vfs_handle_struct *handle, connection_struct *conn, const char *path, mode_t mode)
+{
+ int result;
+
+ result = SMB_VFS_NEXT_CHMOD(handle, conn, path, mode);
+
+ syslog(audit_syslog_priority(handle), "chmod %s mode 0x%x %s%s\n",
+ path, mode,
+ (result < 0) ? "failed: " : "",
+ (result < 0) ? strerror(errno) : "");
+
+ return result;
+}
+
+static int audit_chmod_acl(vfs_handle_struct *handle, connection_struct *conn, const char *path, mode_t mode)
+{
+ int result;
+
+ result = SMB_VFS_NEXT_CHMOD_ACL(handle, conn, path, mode);
+
+ syslog(audit_syslog_priority(handle), "chmod_acl %s mode 0x%x %s%s\n",
+ path, mode,
+ (result < 0) ? "failed: " : "",
+ (result < 0) ? strerror(errno) : "");
+
+ return result;
+}
+
+static int audit_fchmod(vfs_handle_struct *handle, files_struct *fsp, int fd, mode_t mode)
+{
+ int result;
+
+ result = SMB_VFS_NEXT_FCHMOD(handle, fsp, fd, mode);
+
+ syslog(audit_syslog_priority(handle), "fchmod %s mode 0x%x %s%s\n",
+ fsp->fsp_name, mode,
+ (result < 0) ? "failed: " : "",
+ (result < 0) ? strerror(errno) : "");
+
+ return result;
+}
+
+static int audit_fchmod_acl(vfs_handle_struct *handle, files_struct *fsp, int fd, mode_t mode)
+{
+ int result;
+
+ result = SMB_VFS_NEXT_FCHMOD_ACL(handle, fsp, fd, mode);
+
+ syslog(audit_syslog_priority(handle), "fchmod_acl %s mode 0x%x %s%s\n",
+ fsp->fsp_name, mode,
+ (result < 0) ? "failed: " : "",
+ (result < 0) ? strerror(errno) : "");
+
+ return result;
+}
+
+NTSTATUS vfs_audit_init(void)
+{
+ return smb_register_vfs(SMB_VFS_INTERFACE_VERSION, "audit", audit_op_tuples);
+}
diff --git a/source/modules/vfs_cap.c b/source/modules/vfs_cap.c
new file mode 100644
index 00000000000..0526276acb8
--- /dev/null
+++ b/source/modules/vfs_cap.c
@@ -0,0 +1,448 @@
+/*
+ * CAP VFS module for Samba 3.x Version 0.3
+ *
+ * Copyright (C) Tim Potter, 1999-2000
+ * Copyright (C) Alexander Bokovoy, 2002-2003
+ * Copyright (C) Stefan (metze) Metzmacher, 2003
+ * Copyright (C) TAKAHASHI Motonobu (monyo), 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 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"
+
+/* cap functions */
+static char *capencode(char *to, const char *from);
+static char *capdecode(char *to, const char *from);
+
+static SMB_BIG_UINT cap_disk_free(vfs_handle_struct *handle, connection_struct *conn, const char *path,
+ BOOL small_query, SMB_BIG_UINT *bsize,
+ SMB_BIG_UINT *dfree, SMB_BIG_UINT *dsize)
+{
+ pstring cappath;
+ capencode(cappath, path);
+ return SMB_VFS_NEXT_DISK_FREE(handle, conn, cappath, small_query, bsize,
+ dfree, dsize);
+}
+
+static DIR *cap_opendir(vfs_handle_struct *handle, connection_struct *conn, const char *fname)
+{
+ pstring capname;
+ capencode(capname, fname);
+ return SMB_VFS_NEXT_OPENDIR(handle, conn, capname);
+}
+
+static struct dirent *cap_readdir(vfs_handle_struct *handle, connection_struct *conn, DIR *dirp)
+{
+ struct dirent *result;
+ DEBUG(3,("cap: cap_readdir\n"));
+ result = SMB_VFS_NEXT_READDIR(handle, conn, dirp);
+ if (result) {
+ DEBUG(3,("cap: cap_readdir: %s\n", result->d_name));
+ capdecode(result->d_name, result->d_name);
+ }
+ return result;
+}
+
+static int cap_mkdir(vfs_handle_struct *handle, connection_struct *conn, const char *path, mode_t mode)
+{
+ pstring cappath;
+ capencode(cappath, path);
+ return SMB_VFS_NEXT_MKDIR(handle, conn, cappath, mode);
+}
+
+static int cap_rmdir(vfs_handle_struct *handle, connection_struct *conn, const char *path)
+{
+ pstring cappath;
+ capencode(cappath, path);
+ return SMB_VFS_NEXT_RMDIR(handle, conn, cappath);
+}
+
+static int cap_open(vfs_handle_struct *handle, connection_struct *conn, const char *fname, int flags, mode_t mode)
+{
+ pstring capname;
+ DEBUG(3,("cap: cap_open for %s\n", fname));
+ capencode(capname, fname);
+ return SMB_VFS_NEXT_OPEN(handle, conn, capname, flags, mode);
+}
+
+static int cap_rename(vfs_handle_struct *handle, connection_struct *conn, const char *old, const char *new)
+{
+ pstring capold, capnew;
+ capencode(capold, old);
+ capencode(capnew, new);
+
+ return SMB_VFS_NEXT_RENAME(handle, conn, capold, capnew);
+}
+
+static int cap_stat(vfs_handle_struct *handle, connection_struct *conn, const char *fname, SMB_STRUCT_STAT *sbuf)
+{
+ pstring capname;
+ capencode(capname, fname);
+ return SMB_VFS_NEXT_STAT(handle, conn, capname, sbuf);
+}
+
+static int cap_lstat(vfs_handle_struct *handle, connection_struct *conn, const char *path, SMB_STRUCT_STAT *sbuf)
+{
+ pstring cappath;
+ capencode(cappath, path);
+ return SMB_VFS_NEXT_LSTAT(handle, conn, cappath, sbuf);
+}
+
+static int cap_unlink(vfs_handle_struct *handle, connection_struct *conn, const char *path)
+{
+ pstring cappath;
+ capencode(cappath, path);
+ return SMB_VFS_NEXT_UNLINK(handle, conn, cappath);
+}
+
+static int cap_chmod(vfs_handle_struct *handle, connection_struct *conn, const char *path, mode_t mode)
+{
+ pstring cappath;
+ capencode(cappath, path);
+ return SMB_VFS_NEXT_CHMOD(handle, conn, cappath, mode);
+}
+
+static int cap_chown(vfs_handle_struct *handle, connection_struct *conn, const char *path, uid_t uid, gid_t gid)
+{
+ pstring cappath;
+ capencode(cappath, path);
+ return SMB_VFS_NEXT_CHOWN(handle, conn, cappath, uid, gid);
+}
+
+static int cap_chdir(vfs_handle_struct *handle, connection_struct *conn, const char *path)
+{
+ pstring cappath;
+ DEBUG(3,("cap: cap_chdir for %s\n", path));
+ capencode(cappath, path);
+ return SMB_VFS_NEXT_CHDIR(handle, conn, cappath);
+}
+
+static int cap_utime(vfs_handle_struct *handle, connection_struct *conn, const char *path, struct utimbuf *times)
+{
+ pstring cappath;
+ capencode(cappath, path);
+ return SMB_VFS_NEXT_UTIME(handle, conn, cappath, times);
+}
+
+
+static BOOL cap_symlink(vfs_handle_struct *handle, connection_struct *conn, const char *oldpath, const char *newpath)
+{
+ pstring capoldpath, capnewpath;
+ capencode(capoldpath, oldpath);
+ capencode(capnewpath, newpath);
+ return SMB_VFS_NEXT_SYMLINK(handle, conn, capoldpath, capnewpath);
+}
+
+static BOOL cap_readlink(vfs_handle_struct *handle, connection_struct *conn, const char *path, char *buf, size_t bufsiz)
+{
+ pstring cappath;
+ capencode(cappath, path);
+ return SMB_VFS_NEXT_READLINK(handle, conn, cappath, buf, bufsiz);
+}
+
+static int cap_link(vfs_handle_struct *handle, connection_struct *conn, const char *oldpath, const char *newpath)
+{
+ pstring capoldpath, capnewpath;
+ capencode(capoldpath, oldpath);
+ capencode(capnewpath, newpath);
+ return SMB_VFS_NEXT_LINK(handle, conn, capoldpath, capnewpath);
+}
+
+static int cap_mknod(vfs_handle_struct *handle, connection_struct *conn, const char *path, mode_t mode, SMB_DEV_T dev)
+{
+ pstring cappath;
+ capencode(cappath, path);
+ return SMB_VFS_NEXT_MKNOD(handle, conn, cappath, mode, dev);
+}
+
+static char *cap_realpath(vfs_handle_struct *handle, connection_struct *conn, const char *path, char *resolved_path)
+{
+ /* monyo need capencode'ed and capdecode'ed? */
+ pstring cappath;
+ capencode(cappath, path);
+ return SMB_VFS_NEXT_REALPATH(handle, conn, path, resolved_path);
+}
+
+static BOOL cap_set_nt_acl(vfs_handle_struct *handle, files_struct *fsp, const char *name, uint32 security_info_sent, struct security_descriptor_info *psd)
+{
+ pstring capname;
+ capencode(capname, name);
+ return SMB_VFS_NEXT_SET_NT_ACL(handle, fsp, capname, security_info_sent, psd);
+}
+
+static int cap_chmod_acl(vfs_handle_struct *handle, connection_struct *conn, const char *name, mode_t mode)
+{
+ pstring capname;
+ capencode(capname, name);
+
+ /* If the underlying VFS doesn't have ACL support... */
+ if (!handle->vfs_next.ops.chmod_acl) {
+ errno = ENOSYS;
+ return -1;
+ }
+ return SMB_VFS_NEXT_CHMOD_ACL(handle, conn, capname, mode);
+}
+
+static SMB_ACL_T cap_sys_acl_get_file(vfs_handle_struct *handle, connection_struct *conn, const char *path_p, SMB_ACL_TYPE_T type)
+{
+ pstring cappath_p;
+ capencode(cappath_p, path_p);
+ return SMB_VFS_NEXT_SYS_ACL_GET_FILE(handle, conn, cappath_p, type);
+}
+
+static int cap_sys_acl_set_file(vfs_handle_struct *handle, connection_struct *conn, const char *name, SMB_ACL_TYPE_T acltype, SMB_ACL_T theacl)
+{
+ pstring capname;
+ capencode(capname, name);
+ return SMB_VFS_NEXT_SYS_ACL_SET_FILE(handle, conn, capname, acltype, theacl);
+}
+
+static int cap_sys_acl_delete_def_file(vfs_handle_struct *handle, connection_struct *conn, const char *path)
+{
+ pstring cappath;
+ capencode(cappath, path);
+ return SMB_VFS_NEXT_SYS_ACL_DELETE_DEF_FILE(handle, conn, cappath);
+}
+
+static ssize_t cap_getxattr(vfs_handle_struct *handle, struct connection_struct *conn,const char *path, const char *name, void *value, size_t size)
+{
+ pstring cappath, capname;
+ capencode(cappath, path);
+ capencode(capname, name);
+ return SMB_VFS_NEXT_GETXATTR(handle, conn, cappath, capname, value, size);
+}
+
+static ssize_t cap_lgetxattr(vfs_handle_struct *handle, struct connection_struct *conn,const char *path, const char *name, void *value, size_t
+size)
+{
+ pstring cappath, capname;
+ capencode(cappath, path);
+ capencode(capname, name);
+ return SMB_VFS_NEXT_LGETXATTR(handle, conn, cappath, capname, value, size);
+}
+
+static ssize_t cap_fgetxattr(vfs_handle_struct *handle, struct files_struct *fsp,int fd, const char *name, void *value, size_t size)
+{
+ pstring capname;
+ capencode(capname, name);
+ return SMB_VFS_NEXT_FGETXATTR(handle, fsp, fd, capname, value, size);
+}
+
+static ssize_t cap_listxattr(vfs_handle_struct *handle, struct connection_struct *conn,const char *path, char *list, size_t size)
+{
+ pstring cappath;
+ capencode(cappath, path);
+ return SMB_VFS_NEXT_LISTXATTR(handle, conn, cappath, list, size);
+}
+
+static ssize_t cap_llistxattr(vfs_handle_struct *handle, struct connection_struct *conn,const char *path, char *list, size_t size)
+{
+ pstring cappath;
+ capencode(cappath, path);
+ return SMB_VFS_NEXT_LLISTXATTR(handle, conn, cappath, list, size);
+}
+
+static int cap_removexattr(vfs_handle_struct *handle, struct connection_struct *conn,const char *path, const char *name)
+{
+ pstring cappath, capname;
+ capencode(cappath, path);
+ capencode(capname, name);
+ return SMB_VFS_NEXT_REMOVEXATTR(handle, conn, cappath, capname);
+}
+
+static int cap_lremovexattr(vfs_handle_struct *handle, struct connection_struct *conn,const char *path, const char *name)
+{
+ pstring cappath, capname;
+ capencode(cappath, path);
+ capencode(capname, name);
+ return SMB_VFS_NEXT_LREMOVEXATTR(handle, conn, cappath, capname);
+}
+
+static int cap_fremovexattr(vfs_handle_struct *handle, struct files_struct *fsp,int fd, const char *name)
+{
+ pstring capname;
+ capencode(capname, name);
+ return SMB_VFS_NEXT_FREMOVEXATTR(handle, fsp, fd, capname);
+}
+
+static int cap_setxattr(vfs_handle_struct *handle, struct connection_struct *conn,const char *path, const char *name, const void *value, size_t size, int flags)
+{
+ pstring cappath, capname;
+ capencode(cappath, path);
+ capencode(capname, name);
+ return SMB_VFS_NEXT_SETXATTR(handle, conn, cappath, capname, value, size, flags);
+}
+
+static int cap_lsetxattr(vfs_handle_struct *handle, struct connection_struct *conn,const char *path, const char *name, const void *value, size_t size, int flags)
+{
+ pstring cappath, capname;
+ capencode(cappath, path);
+ capencode(capname, name);
+ return SMB_VFS_NEXT_LSETXATTR(handle, conn, cappath, capname, value, size, flags);
+}
+
+static int cap_fsetxattr(vfs_handle_struct *handle, struct files_struct *fsp,int fd, const char *name, const void *value, size_t size, int flags)
+{
+ pstring capname;
+ capencode(capname, name);
+ return SMB_VFS_NEXT_FSETXATTR(handle, fsp, fd, capname, value, size, flags);
+}
+
+/* VFS operations structure */
+
+static vfs_op_tuple cap_op_tuples[] = {
+
+ /* Disk operations */
+
+ {SMB_VFS_OP(cap_disk_free), SMB_VFS_OP_DISK_FREE, SMB_VFS_LAYER_TRANSPARENT},
+
+ /* Directory operations */
+
+ {SMB_VFS_OP(cap_opendir), SMB_VFS_OP_OPENDIR, SMB_VFS_LAYER_TRANSPARENT},
+ {SMB_VFS_OP(cap_readdir), SMB_VFS_OP_READDIR, SMB_VFS_LAYER_TRANSPARENT},
+ {SMB_VFS_OP(cap_mkdir), SMB_VFS_OP_MKDIR, SMB_VFS_LAYER_TRANSPARENT},
+ {SMB_VFS_OP(cap_rmdir), SMB_VFS_OP_RMDIR, SMB_VFS_LAYER_TRANSPARENT},
+
+ /* File operations */
+
+ {SMB_VFS_OP(cap_open), SMB_VFS_OP_OPEN, SMB_VFS_LAYER_TRANSPARENT},
+ {SMB_VFS_OP(cap_rename), SMB_VFS_OP_RENAME, SMB_VFS_LAYER_TRANSPARENT},
+ {SMB_VFS_OP(cap_stat), SMB_VFS_OP_STAT, SMB_VFS_LAYER_TRANSPARENT},
+ {SMB_VFS_OP(cap_lstat), SMB_VFS_OP_LSTAT, SMB_VFS_LAYER_TRANSPARENT},
+ {SMB_VFS_OP(cap_unlink), SMB_VFS_OP_UNLINK, SMB_VFS_LAYER_TRANSPARENT},
+ {SMB_VFS_OP(cap_chmod), SMB_VFS_OP_CHMOD, SMB_VFS_LAYER_TRANSPARENT},
+ {SMB_VFS_OP(cap_chown), SMB_VFS_OP_CHOWN, SMB_VFS_LAYER_TRANSPARENT},
+ {SMB_VFS_OP(cap_chdir), SMB_VFS_OP_CHDIR, SMB_VFS_LAYER_TRANSPARENT},
+ {SMB_VFS_OP(cap_utime), SMB_VFS_OP_UTIME, SMB_VFS_LAYER_TRANSPARENT},
+ {SMB_VFS_OP(cap_symlink), SMB_VFS_OP_SYMLINK, SMB_VFS_LAYER_TRANSPARENT},
+ {SMB_VFS_OP(cap_readlink), SMB_VFS_OP_READLINK, SMB_VFS_LAYER_TRANSPARENT},
+ {SMB_VFS_OP(cap_link), SMB_VFS_OP_LINK, SMB_VFS_LAYER_TRANSPARENT},
+ {SMB_VFS_OP(cap_mknod), SMB_VFS_OP_MKNOD, SMB_VFS_LAYER_TRANSPARENT},
+ {SMB_VFS_OP(cap_realpath), SMB_VFS_OP_REALPATH, SMB_VFS_LAYER_TRANSPARENT},
+
+ /* NT File ACL operations */
+
+ {SMB_VFS_OP(cap_set_nt_acl), SMB_VFS_OP_SET_NT_ACL, SMB_VFS_LAYER_TRANSPARENT},
+
+ /* POSIX ACL operations */
+
+ {SMB_VFS_OP(cap_chmod_acl), SMB_VFS_OP_CHMOD_ACL, SMB_VFS_LAYER_TRANSPARENT},
+
+ {SMB_VFS_OP(cap_sys_acl_get_file), SMB_VFS_OP_SYS_ACL_GET_FILE, SMB_VFS_LAYER_TRANSPARENT},
+ {SMB_VFS_OP(cap_sys_acl_set_file), SMB_VFS_OP_SYS_ACL_SET_FILE, SMB_VFS_LAYER_TRANSPARENT},
+ {SMB_VFS_OP(cap_sys_acl_delete_def_file), SMB_VFS_OP_SYS_ACL_DELETE_DEF_FILE, SMB_VFS_LAYER_TRANSPARENT},
+
+ /* EA operations. */
+ {SMB_VFS_OP(cap_getxattr), SMB_VFS_OP_GETXATTR, SMB_VFS_LAYER_TRANSPARENT},
+ {SMB_VFS_OP(cap_lgetxattr), SMB_VFS_OP_LGETXATTR, SMB_VFS_LAYER_TRANSPARENT},
+ {SMB_VFS_OP(cap_fgetxattr), SMB_VFS_OP_FGETXATTR, SMB_VFS_LAYER_TRANSPARENT},
+ {SMB_VFS_OP(cap_listxattr), SMB_VFS_OP_LISTXATTR, SMB_VFS_LAYER_TRANSPARENT},
+ {SMB_VFS_OP(cap_llistxattr), SMB_VFS_OP_LLISTXATTR, SMB_VFS_LAYER_TRANSPARENT},
+ {SMB_VFS_OP(cap_removexattr), SMB_VFS_OP_REMOVEXATTR, SMB_VFS_LAYER_TRANSPARENT},
+ {SMB_VFS_OP(cap_lremovexattr), SMB_VFS_OP_LREMOVEXATTR, SMB_VFS_LAYER_TRANSPARENT},
+ {SMB_VFS_OP(cap_fremovexattr), SMB_VFS_OP_FREMOVEXATTR, SMB_VFS_LAYER_TRANSPARENT},
+ {SMB_VFS_OP(cap_setxattr), SMB_VFS_OP_SETXATTR, SMB_VFS_LAYER_TRANSPARENT},
+ {SMB_VFS_OP(cap_lsetxattr), SMB_VFS_OP_LSETXATTR, SMB_VFS_LAYER_TRANSPARENT},
+ {SMB_VFS_OP(cap_fsetxattr), SMB_VFS_OP_FSETXATTR, SMB_VFS_LAYER_TRANSPARENT},
+
+ {NULL, SMB_VFS_OP_NOOP, SMB_VFS_LAYER_NOOP}
+};
+
+NTSTATUS vfs_cap_init(void)
+{
+ return smb_register_vfs(SMB_VFS_INTERFACE_VERSION, "cap", cap_op_tuples);
+}
+
+/* For CAP functions */
+#define hex_tag ':'
+#define hex2bin(c) hex2bin_table[(unsigned char)(c)]
+#define bin2hex(c) bin2hex_table[(unsigned char)(c)]
+#define is_hex(s) ((s)[0] == hex_tag)
+
+static unsigned char hex2bin_table[256] = {
+0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x00 */
+0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x10 */
+0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x20 */
+0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 0, 0, 0, 0, 0, /* 0x30 */
+0000, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0000, /* 0x40 */
+0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000,
+0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x50 */
+0000, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0000, /* 0x60 */
+0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000,
+0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x70 */
+0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x80 */
+0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x90 */
+0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0xa0 */
+0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0xb0 */
+0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0xc0 */
+0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0xd0 */
+0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0xe0 */
+0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 /* 0xf0 */
+};
+static unsigned char bin2hex_table[256] = "0123456789abcdef";
+
+/*******************************************************************
+ original code -> ":xx" - CAP format
+********************************************************************/
+static char *capencode(char *to, const char *from)
+{
+ pstring cvtbuf;
+ char *out;
+
+ if (to == from) {
+ from = pstrcpy ((char *) cvtbuf, from);
+ }
+
+ for (out = to; *from && (out - to < sizeof(pstring)-7);) {
+ /* buffer husoku error */
+ if ((unsigned char)*from >= 0x80) {
+ *out++ = hex_tag;
+ *out++ = bin2hex (((*from)>>4)&0x0f);
+ *out++ = bin2hex ((*from)&0x0f);
+ from++;
+ }
+ else {
+ *out++ = *from++;
+ }
+ }
+ *out = '\0';
+ return to;
+}
+
+/*******************************************************************
+ CAP -> original code
+********************************************************************/
+/* ":xx" -> a byte */
+static char *capdecode(char *to, const char *from)
+{
+ pstring cvtbuf;
+ char *out;
+
+ if (to == from) {
+ from = pstrcpy ((char *) cvtbuf, from);
+ }
+ for (out = to; *from && (out - to < sizeof(pstring)-3);) {
+ if (is_hex(from)) {
+ *out++ = (hex2bin (from[1])<<4) | (hex2bin (from[2]));
+ from += 3;
+ } else {
+ *out++ = *from++;
+ }
+ }
+ *out = '\0';
+ return to;
+}
diff --git a/source/modules/vfs_default_quota.c b/source/modules/vfs_default_quota.c
new file mode 100644
index 00000000000..1294a515333
--- /dev/null
+++ b/source/modules/vfs_default_quota.c
@@ -0,0 +1,180 @@
+/*
+ * Store default Quotas in a specified quota record
+ *
+ * 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 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"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_VFS
+
+#define DEFAULT_QUOTA_NAME "default_quota"
+
+#define DEFAULT_QUOTA_UID_DEFAULT 0
+#define DEFAULT_QUOTA_UID_NOLIMIT_DEFAULT True
+#define DEFAULT_QUOTA_GID_DEFAULT 0
+#define DEFAULT_QUOTA_GID_NOLIMIT_DEFAULT True
+
+#define DEFAULT_QUOTA_UID(handle) \
+ (uid_t)lp_parm_int(SNUM((handle)->conn),DEFAULT_QUOTA_NAME,"uid",DEFAULT_QUOTA_UID_DEFAULT)
+
+#define DEFAULT_QUOTA_UID_NOLIMIT(handle) \
+ lp_parm_bool(SNUM((handle)->conn),DEFAULT_QUOTA_NAME,"uid nolimit",DEFAULT_QUOTA_UID_NOLIMIT_DEFAULT)
+
+#define DEFAULT_QUOTA_GID(handle) \
+ (gid_t)lp_parm_int(SNUM((handle)->conn),DEFAULT_QUOTA_NAME,"gid",DEFAULT_QUOTA_GID_DEFAULT)
+
+#define DEFAULT_QUOTA_GID_NOLIMIT(handle) \
+ lp_parm_bool(SNUM((handle)->conn),DEFAULT_QUOTA_NAME,"gid nolimit",DEFAULT_QUOTA_GID_NOLIMIT_DEFAULT)
+
+static int default_quota_get_quota(vfs_handle_struct *handle, connection_struct *conn, enum SMB_QUOTA_TYPE qtype, unid_t id, SMB_DISK_QUOTA *dq)
+{
+ int ret = -1;
+
+ if ((ret=SMB_VFS_NEXT_GET_QUOTA(handle, conn, qtype, id, dq))!=0) {
+ return ret;
+ }
+
+ switch (qtype) {
+ case SMB_USER_QUOTA_TYPE:
+ /* we use id.uid == 0 for default quotas */
+ if ((id.uid==DEFAULT_QUOTA_UID(handle)) &&
+ DEFAULT_QUOTA_UID_NOLIMIT(handle)) {
+ SMB_QUOTAS_SET_NO_LIMIT(dq);
+ }
+ break;
+#ifdef HAVE_GROUP_QUOTA
+ case SMB_GROUP_QUOTA_TYPE:
+ /* we use id.gid == 0 for default quotas */
+ if ((id.gid==DEFAULT_QUOTA_GID(handle)) &&
+ DEFAULT_QUOTA_GID_NOLIMIT(handle)) {
+ SMB_QUOTAS_SET_NO_LIMIT(dq);
+ }
+ break;
+#endif /* HAVE_GROUP_QUOTA */
+ case SMB_USER_FS_QUOTA_TYPE:
+ {
+ unid_t qid;
+ uint32 qflags = dq->qflags;
+ qid.uid = DEFAULT_QUOTA_UID(handle);
+ SMB_VFS_NEXT_GET_QUOTA(handle, conn, SMB_USER_QUOTA_TYPE, qid, dq);
+ dq->qflags = qflags;
+ }
+ break;
+#ifdef HAVE_GROUP_QUOTA
+ case SMB_GROUP_FS_QUOTA_TYPE:
+ {
+ unid_t qid;
+ uint32 qflags = dq->qflags;
+ qid.gid = DEFAULT_QUOTA_GID(handle);
+ SMB_VFS_NEXT_GET_QUOTA(handle, conn, SMB_GROUP_QUOTA_TYPE, qid, dq);
+ dq->qflags = qflags;
+ }
+ break;
+#endif /* HAVE_GROUP_QUOTA */
+ default:
+ errno = ENOSYS;
+ return -1;
+ break;
+ }
+
+ return ret;
+}
+
+static int default_quota_set_quota(vfs_handle_struct *handle, connection_struct *conn, enum SMB_QUOTA_TYPE qtype, unid_t id, SMB_DISK_QUOTA *dq)
+{
+ int ret = -1;
+
+ switch (qtype) {
+ case SMB_USER_QUOTA_TYPE:
+ /* we use id.uid == 0 for default quotas */
+ if ((id.uid==DEFAULT_QUOTA_UID(handle)) &&
+ DEFAULT_QUOTA_UID_NOLIMIT(handle)) {
+ return -1;
+ }
+ break;
+#ifdef HAVE_GROUP_QUOTA
+ case SMB_GROUP_QUOTA_TYPE:
+ /* we use id.gid == 0 for default quotas */
+ if ((id.gid==DEFAULT_QUOTA_GID(handle)) &&
+ DEFAULT_QUOTA_GID_NOLIMIT(handle)) {
+ return -1;
+ }
+ break;
+#endif /* HAVE_GROUP_QUOTA */
+ case SMB_USER_FS_QUOTA_TYPE:
+ break;
+#ifdef HAVE_GROUP_QUOTA
+ case SMB_GROUP_FS_QUOTA_TYPE:
+ break;
+#endif /* HAVE_GROUP_QUOTA */
+ default:
+ errno = ENOSYS;
+ return -1;
+ break;
+ }
+
+ if ((ret=SMB_VFS_NEXT_SET_QUOTA(handle, conn, qtype, id, dq))!=0) {
+ return ret;
+ }
+
+ switch (qtype) {
+ case SMB_USER_QUOTA_TYPE:
+ break;
+#ifdef HAVE_GROUP_QUOTA
+ case SMB_GROUP_QUOTA_TYPE:
+ break;
+#endif /* HAVE_GROUP_QUOTA */
+ case SMB_USER_FS_QUOTA_TYPE:
+ {
+ unid_t qid;
+ qid.uid = DEFAULT_QUOTA_UID(handle);
+ ret = SMB_VFS_NEXT_SET_QUOTA(handle, conn, SMB_USER_QUOTA_TYPE, qid, dq);
+ }
+ break;
+#ifdef HAVE_GROUP_QUOTA
+ case SMB_GROUP_FS_QUOTA_TYPE:
+ {
+ unid_t qid;
+ qid.gid = DEFAULT_QUOTA_GID(handle);
+ ret = SMB_VFS_NEXT_SET_QUOTA(handle, conn, SMB_GROUP_QUOTA_TYPE, qid, dq);
+ }
+ break;
+#endif /* HAVE_GROUP_QUOTA */
+ default:
+ errno = ENOSYS;
+ return -1;
+ break;
+ }
+
+ return ret;
+}
+
+/* VFS operations structure */
+
+static vfs_op_tuple default_quota_ops[] = {
+ {SMB_VFS_OP(default_quota_get_quota), SMB_VFS_OP_GET_QUOTA, SMB_VFS_LAYER_TRANSPARENT},
+ {SMB_VFS_OP(default_quota_set_quota), SMB_VFS_OP_SET_QUOTA, SMB_VFS_LAYER_TRANSPARENT},
+
+ {SMB_VFS_OP(NULL), SMB_VFS_OP_NOOP, SMB_VFS_LAYER_NOOP}
+};
+
+NTSTATUS vfs_default_quota_init(void)
+{
+ return smb_register_vfs(SMB_VFS_INTERFACE_VERSION, DEFAULT_QUOTA_NAME, default_quota_ops);
+}
diff --git a/source/modules/vfs_expand_msdfs.c b/source/modules/vfs_expand_msdfs.c
new file mode 100644
index 00000000000..07fbe59825e
--- /dev/null
+++ b/source/modules/vfs_expand_msdfs.c
@@ -0,0 +1,191 @@
+/*
+ * Expand msdfs targets based on client IP
+ *
+ * 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 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"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_VFS
+
+/**********************************************************
+ Under mapfile we expect a table of the following format:
+
+ IP-Prefix whitespace expansion
+
+ For example:
+ 192.168.234 local.samba.org
+ 192.168 remote.samba.org
+ default.samba.org
+
+ This is to redirect a DFS client to a host close to it.
+***********************************************************/
+
+static BOOL read_target_host(const char *mapfile, pstring targethost)
+{
+ XFILE *f;
+ pstring buf;
+ char *s, *space = buf;
+ BOOL found = False;
+
+ f = x_fopen(mapfile, O_RDONLY, 0);
+
+ if (f == NULL) {
+ DEBUG(0,("can't open IP map %s. Error %s\n",
+ mapfile, strerror(errno) ));
+ return False;
+ }
+
+ DEBUG(10, ("Scanning mapfile [%s]\n", mapfile));
+
+ while ((s=x_fgets(buf, sizeof(buf), f)) != NULL) {
+
+ if ((strlen(buf) > 0) && (buf[strlen(buf)-1] == '\n'))
+ buf[strlen(buf)-1] = '\0';
+
+ DEBUG(10, ("Scanning line [%s]\n", buf));
+
+ space = strchr_m(buf, ' ');
+
+ if (space == NULL) {
+ DEBUG(0, ("Ignoring invalid line %s\n", buf));
+ continue;
+ }
+
+ *space = '\0';
+
+ if (strncmp(client_addr(), buf, strlen(buf)) == 0) {
+ found = True;
+ break;
+ }
+ }
+
+ x_fclose(f);
+
+ if (!found)
+ return False;
+
+ space += 1;
+
+ while (isspace(*space))
+ space += 1;
+
+ pstrcpy(targethost, space);
+ return True;
+}
+
+/**********************************************************
+
+ Expand the msdfs target host using read_target_host
+ explained above. The syntax used in the msdfs link is
+
+ msdfs:@table-filename@/share
+
+ Everything between and including the two @-signs is
+ replaced by the substitution string found in the table
+ described above.
+
+***********************************************************/
+
+static BOOL expand_msdfs_target(connection_struct* conn, pstring target)
+{
+ pstring mapfilename;
+ char *filename_start = strchr_m(target, '@');
+ char *filename_end;
+ int filename_len;
+ pstring targethost;
+ pstring new_target;
+
+ if (filename_start == NULL) {
+ DEBUG(10, ("No filename start in %s\n", target));
+ return False;
+ }
+
+ filename_end = strchr_m(filename_start+1, '@');
+
+ if (filename_end == NULL) {
+ DEBUG(10, ("No filename end in %s\n", target));
+ return False;
+ }
+
+ filename_len = PTR_DIFF(filename_end, filename_start+1);
+ pstrcpy(mapfilename, filename_start+1);
+ mapfilename[filename_len] = '\0';
+
+ DEBUG(10, ("Expanding from table [%s]\n", mapfilename));
+
+ if (!read_target_host(mapfilename, targethost)) {
+ DEBUG(1, ("Could not expand target host from file %s\n",
+ mapfilename));
+ return False;
+ }
+
+ standard_sub_conn(conn, mapfilename, sizeof(mapfilename));
+
+ DEBUG(10, ("Expanded targethost to %s\n", targethost));
+
+ *filename_start = '\0';
+ pstrcpy(new_target, target);
+ pstrcat(new_target, targethost);
+ pstrcat(new_target, filename_end+1);
+
+ DEBUG(10, ("New DFS target: %s\n", new_target));
+ pstrcpy(target, new_target);
+ return True;
+}
+
+static int expand_msdfs_readlink(struct vfs_handle_struct *handle,
+ struct connection_struct *conn,
+ const char *path, char *buf, size_t bufsiz)
+{
+ pstring target;
+ int result;
+
+ result = SMB_VFS_NEXT_READLINK(handle, conn, path, target,
+ sizeof(target));
+
+ if (result < 0)
+ return result;
+
+ target[result] = '\0';
+
+ if ((strncmp(target, "msdfs:", strlen("msdfs:")) == 0) &&
+ (strchr_m(target, '@') != NULL)) {
+ if (!expand_msdfs_target(conn, target)) {
+ errno = ENOENT;
+ return -1;
+ }
+ }
+
+ safe_strcpy(buf, target, bufsiz-1);
+ return strlen(buf);
+}
+
+/* VFS operations structure */
+
+static vfs_op_tuple expand_msdfs_ops[] = {
+ {SMB_VFS_OP(expand_msdfs_readlink), SMB_VFS_OP_READLINK,
+ SMB_VFS_LAYER_TRANSPARENT},
+ {SMB_VFS_OP(NULL), SMB_VFS_OP_NOOP, SMB_VFS_LAYER_NOOP}
+};
+
+NTSTATUS vfs_expand_msdfs_init(void)
+{
+ return smb_register_vfs(SMB_VFS_INTERFACE_VERSION, "expand_msdfs",
+ expand_msdfs_ops);
+}
diff --git a/source/modules/vfs_extd_audit.c b/source/modules/vfs_extd_audit.c
new file mode 100644
index 00000000000..06cddc78e43
--- /dev/null
+++ b/source/modules/vfs_extd_audit.c
@@ -0,0 +1,343 @@
+/*
+ * Auditing VFS module for samba. Log selected file operations to syslog
+ * facility.
+ *
+ * Copyright (C) Tim Potter, 1999-2000
+ * Copyright (C) Alexander Bokovoy, 2002
+ * Copyright (C) John H Terpstra, 2003
+ * 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 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"
+
+static int vfs_extd_audit_debug_level = DBGC_VFS;
+
+#undef DBGC_CLASS
+#define DBGC_CLASS vfs_extd_audit_debug_level
+
+/* Function prototypes */
+
+static int audit_connect(vfs_handle_struct *handle, connection_struct *conn, const char *svc, const char *user);
+static void audit_disconnect(vfs_handle_struct *handle, connection_struct *conn);
+static DIR *audit_opendir(vfs_handle_struct *handle, connection_struct *conn, const char *fname);
+static int audit_mkdir(vfs_handle_struct *handle, connection_struct *conn, const char *path, mode_t mode);
+static int audit_rmdir(vfs_handle_struct *handle, connection_struct *conn, const char *path);
+static int audit_open(vfs_handle_struct *handle, connection_struct *conn, const char *fname, int flags, mode_t mode);
+static int audit_close(vfs_handle_struct *handle, files_struct *fsp, int fd);
+static int audit_rename(vfs_handle_struct *handle, connection_struct *conn, const char *old, const char *new);
+static int audit_unlink(vfs_handle_struct *handle, connection_struct *conn, const char *path);
+static int audit_chmod(vfs_handle_struct *handle, connection_struct *conn, const char *path, mode_t mode);
+static int audit_chmod_acl(vfs_handle_struct *handle, connection_struct *conn, const char *name, mode_t mode);
+static int audit_fchmod(vfs_handle_struct *handle, files_struct *fsp, int fd, mode_t mode);
+static int audit_fchmod_acl(vfs_handle_struct *handle, files_struct *fsp, int fd, mode_t mode);
+
+/* VFS operations */
+
+static vfs_op_tuple audit_op_tuples[] = {
+
+ /* Disk operations */
+
+ {SMB_VFS_OP(audit_connect), SMB_VFS_OP_CONNECT, SMB_VFS_LAYER_LOGGER},
+ {SMB_VFS_OP(audit_disconnect), SMB_VFS_OP_DISCONNECT, SMB_VFS_LAYER_LOGGER},
+
+ /* Directory operations */
+
+ {SMB_VFS_OP(audit_opendir), SMB_VFS_OP_OPENDIR, SMB_VFS_LAYER_LOGGER},
+ {SMB_VFS_OP(audit_mkdir), SMB_VFS_OP_MKDIR, SMB_VFS_LAYER_LOGGER},
+ {SMB_VFS_OP(audit_rmdir), SMB_VFS_OP_RMDIR, SMB_VFS_LAYER_LOGGER},
+
+ /* File operations */
+
+ {SMB_VFS_OP(audit_open), SMB_VFS_OP_OPEN, SMB_VFS_LAYER_LOGGER},
+ {SMB_VFS_OP(audit_close), SMB_VFS_OP_CLOSE, SMB_VFS_LAYER_LOGGER},
+ {SMB_VFS_OP(audit_rename), SMB_VFS_OP_RENAME, SMB_VFS_LAYER_LOGGER},
+ {SMB_VFS_OP(audit_unlink), SMB_VFS_OP_UNLINK, SMB_VFS_LAYER_LOGGER},
+ {SMB_VFS_OP(audit_chmod), SMB_VFS_OP_CHMOD, SMB_VFS_LAYER_LOGGER},
+ {SMB_VFS_OP(audit_fchmod), SMB_VFS_OP_FCHMOD, SMB_VFS_LAYER_LOGGER},
+ {SMB_VFS_OP(audit_chmod_acl), SMB_VFS_OP_CHMOD_ACL, SMB_VFS_LAYER_LOGGER},
+ {SMB_VFS_OP(audit_fchmod_acl), SMB_VFS_OP_FCHMOD_ACL, SMB_VFS_LAYER_LOGGER},
+
+ /* Finish VFS operations definition */
+
+ {SMB_VFS_OP(NULL), SMB_VFS_OP_NOOP, SMB_VFS_LAYER_NOOP}
+};
+
+
+static int audit_syslog_facility(vfs_handle_struct *handle)
+{
+ /* fix me: let this be configurable by:
+ * lp_param_enum(SNUM(handle->conn),(handle->param?handle->param:"extd_audit"),"syslog facility",
+ * audit_enum_facility,LOG_USER);
+ */
+ return LOG_USER;
+}
+
+
+static int audit_syslog_priority(vfs_handle_struct *handle)
+{
+ /* fix me: let this be configurable by:
+ * lp_param_enum(SNUM(handle->conn),(handle->param?handle->param:"extd_audit"),"syslog priority",
+ * audit_enum_priority,LOG_NOTICE);
+ */
+ return LOG_NOTICE;
+}
+
+/* Implementation of vfs_ops. Pass everything on to the default
+ operation but log event first. */
+
+static int audit_connect(vfs_handle_struct *handle, connection_struct *conn, const char *svc, const char *user)
+{
+ int result;
+
+ openlog("smbd_audit", LOG_PID, audit_syslog_facility(handle));
+
+ syslog(audit_syslog_priority(handle), "connect to service %s by user %s\n",
+ svc, user);
+ DEBUG(10, ("Connected to service %s as user %s\n",
+ svc, user));
+
+ result = SMB_VFS_NEXT_CONNECT(handle, conn, svc, user);
+
+ return result;
+}
+
+static void audit_disconnect(vfs_handle_struct *handle, connection_struct *conn)
+{
+ syslog(audit_syslog_priority(handle), "disconnected\n");
+ DEBUG(10, ("Disconnected from VFS module extd_audit\n"));
+ SMB_VFS_NEXT_DISCONNECT(handle, conn);
+
+ return;
+}
+
+static DIR *audit_opendir(vfs_handle_struct *handle, connection_struct *conn, const char *fname)
+{
+ DIR *result;
+
+ result = SMB_VFS_NEXT_OPENDIR(handle, conn, fname);
+
+ syslog(audit_syslog_priority(handle), "opendir %s %s%s\n",
+ fname,
+ (result == NULL) ? "failed: " : "",
+ (result == NULL) ? strerror(errno) : "");
+ DEBUG(1, ("vfs_extd_audit: opendir %s %s %s\n",
+ fname,
+ (result == NULL) ? "failed: " : "",
+ (result == NULL) ? strerror(errno) : ""));
+
+ return result;
+}
+
+static int audit_mkdir(vfs_handle_struct *handle, connection_struct *conn, const char *path, mode_t mode)
+{
+ int result;
+
+ result = SMB_VFS_NEXT_MKDIR(handle, conn, path, mode);
+
+ syslog(audit_syslog_priority(handle), "mkdir %s %s%s\n",
+ path,
+ (result < 0) ? "failed: " : "",
+ (result < 0) ? strerror(errno) : "");
+ DEBUG(0, ("vfs_extd_audit: mkdir %s %s %s\n",
+ path,
+ (result < 0) ? "failed: " : "",
+ (result < 0) ? strerror(errno) : ""));
+
+ return result;
+}
+
+static int audit_rmdir(vfs_handle_struct *handle, connection_struct *conn, const char *path)
+{
+ int result;
+
+ result = SMB_VFS_NEXT_RMDIR(handle, conn, path);
+
+ syslog(audit_syslog_priority(handle), "rmdir %s %s%s\n",
+ path,
+ (result < 0) ? "failed: " : "",
+ (result < 0) ? strerror(errno) : "");
+ DEBUG(0, ("vfs_extd_audit: rmdir %s %s %s\n",
+ path,
+ (result < 0) ? "failed: " : "",
+ (result < 0) ? strerror(errno) : ""));
+
+ return result;
+}
+
+static int audit_open(vfs_handle_struct *handle, connection_struct *conn, const char *fname, int flags, mode_t mode)
+{
+ int result;
+
+ result = SMB_VFS_NEXT_OPEN(handle, conn, fname, flags, mode);
+
+ syslog(audit_syslog_priority(handle), "open %s (fd %d) %s%s%s\n",
+ fname, result,
+ ((flags & O_WRONLY) || (flags & O_RDWR)) ? "for writing " : "",
+ (result < 0) ? "failed: " : "",
+ (result < 0) ? strerror(errno) : "");
+ DEBUG(2, ("vfs_extd_audit: open %s %s %s\n",
+ fname,
+ (result < 0) ? "failed: " : "",
+ (result < 0) ? strerror(errno) : ""));
+
+ return result;
+}
+
+static int audit_close(vfs_handle_struct *handle, files_struct *fsp, int fd)
+{
+ int result;
+
+ result = SMB_VFS_NEXT_CLOSE(handle, fsp, fd);
+
+ syslog(audit_syslog_priority(handle), "close fd %d %s%s\n",
+ fd,
+ (result < 0) ? "failed: " : "",
+ (result < 0) ? strerror(errno) : "");
+ DEBUG(2, ("vfs_extd_audit: close fd %d %s %s\n",
+ fd,
+ (result < 0) ? "failed: " : "",
+ (result < 0) ? strerror(errno) : ""));
+
+ return result;
+}
+
+static int audit_rename(vfs_handle_struct *handle, connection_struct *conn, const char *old, const char *new)
+{
+ int result;
+
+ result = SMB_VFS_NEXT_RENAME(handle, conn, old, new);
+
+ syslog(audit_syslog_priority(handle), "rename %s -> %s %s%s\n",
+ old, new,
+ (result < 0) ? "failed: " : "",
+ (result < 0) ? strerror(errno) : "");
+ DEBUG(1, ("vfs_extd_audit: rename old: %s new: %s %s %s\n",
+ old, new,
+ (result < 0) ? "failed: " : "",
+ (result < 0) ? strerror(errno) : ""));
+
+ return result;
+}
+
+static int audit_unlink(vfs_handle_struct *handle, connection_struct *conn, const char *path)
+{
+ int result;
+
+ result = SMB_VFS_NEXT_UNLINK(handle, conn, path);
+
+ syslog(audit_syslog_priority(handle), "unlink %s %s%s\n",
+ path,
+ (result < 0) ? "failed: " : "",
+ (result < 0) ? strerror(errno) : "");
+ DEBUG(0, ("vfs_extd_audit: unlink %s %s %s\n",
+ path,
+ (result < 0) ? "failed: " : "",
+ (result < 0) ? strerror(errno) : ""));
+
+ return result;
+}
+
+static int audit_chmod(vfs_handle_struct *handle, connection_struct *conn, const char *path, mode_t mode)
+{
+ int result;
+
+ result = SMB_VFS_NEXT_CHMOD(handle, conn, path, mode);
+
+ syslog(audit_syslog_priority(handle), "chmod %s mode 0x%x %s%s\n",
+ path, mode,
+ (result < 0) ? "failed: " : "",
+ (result < 0) ? strerror(errno) : "");
+ DEBUG(1, ("vfs_extd_audit: chmod %s mode 0x%x %s %s\n",
+ path, mode,
+ (result < 0) ? "failed: " : "",
+ (result < 0) ? strerror(errno) : ""));
+
+ return result;
+}
+
+static int audit_chmod_acl(vfs_handle_struct *handle, connection_struct *conn, const char *path, mode_t mode)
+{
+ int result;
+
+ result = SMB_VFS_NEXT_CHMOD_ACL(handle, conn, path, mode);
+
+ syslog(audit_syslog_priority(handle), "chmod_acl %s mode 0x%x %s%s\n",
+ path, mode,
+ (result < 0) ? "failed: " : "",
+ (result < 0) ? strerror(errno) : "");
+ DEBUG(1, ("vfs_extd_audit: chmod_acl %s mode 0x%x %s %s\n",
+ path, mode,
+ (result < 0) ? "failed: " : "",
+ (result < 0) ? strerror(errno) : ""));
+
+ return result;
+}
+
+static int audit_fchmod(vfs_handle_struct *handle, files_struct *fsp, int fd, mode_t mode)
+{
+ int result;
+
+ result = SMB_VFS_NEXT_FCHMOD(handle, fsp, fd, mode);
+
+ syslog(audit_syslog_priority(handle), "fchmod %s mode 0x%x %s%s\n",
+ fsp->fsp_name, mode,
+ (result < 0) ? "failed: " : "",
+ (result < 0) ? strerror(errno) : "");
+ DEBUG(1, ("vfs_extd_audit: fchmod %s mode 0x%x %s %s",
+ fsp->fsp_name, mode,
+ (result < 0) ? "failed: " : "",
+ (result < 0) ? strerror(errno) : ""));
+
+ return result;
+}
+
+static int audit_fchmod_acl(vfs_handle_struct *handle, files_struct *fsp, int fd, mode_t mode)
+{
+ int result;
+
+ result = SMB_VFS_NEXT_FCHMOD_ACL(handle, fsp, fd, mode);
+
+ syslog(audit_syslog_priority(handle), "fchmod_acl %s mode 0x%x %s%s\n",
+ fsp->fsp_name, mode,
+ (result < 0) ? "failed: " : "",
+ (result < 0) ? strerror(errno) : "");
+ DEBUG(1, ("vfs_extd_audit: fchmod_acl %s mode 0x%x %s %s",
+ fsp->fsp_name, mode,
+ (result < 0) ? "failed: " : "",
+ (result < 0) ? strerror(errno) : ""));
+
+ return result;
+}
+
+NTSTATUS vfs_extd_audit_init(void)
+{
+ NTSTATUS ret = smb_register_vfs(SMB_VFS_INTERFACE_VERSION, "extd_audit", audit_op_tuples);
+
+ if (!NT_STATUS_IS_OK(ret))
+ return ret;
+
+ vfs_extd_audit_debug_level = debug_add_class("extd_audit");
+ if (vfs_extd_audit_debug_level == -1) {
+ vfs_extd_audit_debug_level = DBGC_VFS;
+ DEBUG(0, ("vfs_extd_audit: Couldn't register custom debugging class!\n"));
+ } else {
+ DEBUG(10, ("vfs_extd_audit: Debug class number of 'extd_audit': %d\n", vfs_extd_audit_debug_level));
+ }
+
+ return ret;
+}
diff --git a/source/modules/vfs_fake_perms.c b/source/modules/vfs_fake_perms.c
new file mode 100644
index 00000000000..740218dcd41
--- /dev/null
+++ b/source/modules/vfs_fake_perms.c
@@ -0,0 +1,81 @@
+/*
+ * Fake Perms VFS module. Implements passthrough operation of all VFS
+ * calls to disk functions, except for file permissions, which are now
+ * mode 0700 for the current uid/gid.
+ *
+ * Copyright (C) Tim Potter, 1999-2000
+ * Copyright (C) Alexander Bokovoy, 2002
+ * 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 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"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_VFS
+
+static int fake_perms_stat(vfs_handle_struct *handle, connection_struct *conn, const char *fname, SMB_STRUCT_STAT *sbuf)
+{
+ int ret = -1;
+
+ ret = SMB_VFS_NEXT_STAT(handle, conn, fname, sbuf);
+ if (ret == 0) {
+ extern struct current_user current_user;
+
+ if (S_ISDIR(sbuf->st_mode)) {
+ sbuf->st_mode = S_IFDIR | S_IRWXU;
+ } else {
+ sbuf->st_mode = S_IRWXU;
+ }
+ sbuf->st_uid = current_user.uid;
+ sbuf->st_gid = current_user.gid;
+ }
+
+ return ret;
+}
+
+static int fake_perms_fstat(vfs_handle_struct *handle, files_struct *fsp, int fd, SMB_STRUCT_STAT *sbuf)
+{
+ int ret = -1;
+
+ ret = SMB_VFS_NEXT_FSTAT(handle, fsp, fd, sbuf);
+ if (ret == 0) {
+ extern struct current_user current_user;
+
+ if (S_ISDIR(sbuf->st_mode)) {
+ sbuf->st_mode = S_IFDIR | S_IRWXU;
+ } else {
+ sbuf->st_mode = S_IRWXU;
+ }
+ sbuf->st_uid = current_user.uid;
+ sbuf->st_gid = current_user.gid;
+ }
+ return ret;
+}
+
+/* VFS operations structure */
+
+static vfs_op_tuple fake_perms_ops[] = {
+ {SMB_VFS_OP(fake_perms_stat), SMB_VFS_OP_STAT, SMB_VFS_LAYER_TRANSPARENT},
+ {SMB_VFS_OP(fake_perms_fstat), SMB_VFS_OP_FSTAT, SMB_VFS_LAYER_TRANSPARENT},
+
+ {SMB_VFS_OP(NULL), SMB_VFS_OP_NOOP, SMB_VFS_LAYER_NOOP}
+};
+
+NTSTATUS vfs_fake_perms_init(void)
+{
+ return smb_register_vfs(SMB_VFS_INTERFACE_VERSION, "fake_perms", fake_perms_ops);
+}
diff --git a/source/modules/vfs_netatalk.c b/source/modules/vfs_netatalk.c
new file mode 100644
index 00000000000..ae6286e292d
--- /dev/null
+++ b/source/modules/vfs_netatalk.c
@@ -0,0 +1,401 @@
+/*
+ * AppleTalk VFS module for Samba-3.x
+ *
+ * Copyright (C) Alexei Kotovich, 2002
+ * 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 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"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_VFS
+
+#define APPLEDOUBLE ".AppleDouble"
+#define ADOUBLEMODE 0777
+
+/* atalk functions */
+
+static int atalk_build_paths(TALLOC_CTX *ctx, const char *path,
+ const char *fname, char **adbl_path, char **orig_path,
+ SMB_STRUCT_STAT *adbl_info, SMB_STRUCT_STAT *orig_info);
+
+static int atalk_unlink_file(const char *path);
+
+static int atalk_get_path_ptr(char *path)
+{
+ int i = 0;
+ int ptr = 0;
+
+ for (i = 0; path[i]; i ++) {
+ if (path[i] == '/')
+ ptr = i;
+ /* get out some 'spam';) from win32's file name */
+ else if (path[i] == ':') {
+ path[i] = '\0';
+ break;
+ }
+ }
+
+ return ptr;
+}
+
+static int atalk_build_paths(TALLOC_CTX *ctx, const char *path, const char *fname,
+ char **adbl_path, char **orig_path,
+ SMB_STRUCT_STAT *adbl_info, SMB_STRUCT_STAT *orig_info)
+{
+ int ptr0 = 0;
+ int ptr1 = 0;
+ char *dname = 0;
+ char *name = 0;
+
+ if (!ctx || !path || !fname || !adbl_path || !orig_path ||
+ !adbl_info || !orig_info)
+ return -1;
+#if 0
+ DEBUG(3, ("ATALK: PATH: %s[%s]\n", path, fname));
+#endif
+ if (strstr(path, APPLEDOUBLE) || strstr(fname, APPLEDOUBLE)) {
+ DEBUG(3, ("ATALK: path %s[%s] already contains %s\n", path, fname, APPLEDOUBLE));
+ return -1;
+ }
+
+ if (fname[0] == '.') ptr0 ++;
+ if (fname[1] == '/') ptr0 ++;
+
+ *orig_path = talloc_asprintf(ctx, "%s/%s", path, &fname[ptr0]);
+
+ /* get pointer to last '/' */
+ ptr1 = atalk_get_path_ptr(*orig_path);
+
+ sys_lstat(*orig_path, orig_info);
+
+ if (S_ISDIR(orig_info->st_mode)) {
+ *adbl_path = talloc_asprintf(ctx, "%s/%s/%s/",
+ path, &fname[ptr0], APPLEDOUBLE);
+ } else {
+ dname = talloc_strdup(ctx, *orig_path);
+ dname[ptr1] = '\0';
+ name = *orig_path;
+ *adbl_path = talloc_asprintf(ctx, "%s/%s/%s",
+ dname, APPLEDOUBLE, &name[ptr1 + 1]);
+ }
+#if 0
+ DEBUG(3, ("ATALK: DEBUG:\n%s\n%s\n", *orig_path, *adbl_path));
+#endif
+ sys_lstat(*adbl_path, adbl_info);
+ return 0;
+}
+
+static int atalk_unlink_file(const char *path)
+{
+ int ret = 0;
+
+ become_root();
+ ret = unlink(path);
+ unbecome_root();
+
+ return ret;
+}
+
+static void atalk_add_to_list(name_compare_entry **list)
+{
+ int i, count = 0;
+ name_compare_entry *new_list = 0;
+ name_compare_entry *cur_list = 0;
+
+ cur_list = *list;
+
+ if (cur_list) {
+ for (i = 0, count = 0; cur_list[i].name; i ++, count ++) {
+ if (strstr(cur_list[i].name, APPLEDOUBLE))
+ return;
+ }
+ }
+
+ if (!(new_list = calloc(1,
+ (count == 0 ? 1 : count + 1) * sizeof(name_compare_entry))))
+ return;
+
+ for (i = 0; i < count; i ++) {
+ new_list[i].name = strdup(cur_list[i].name);
+ new_list[i].is_wild = cur_list[i].is_wild;
+ }
+
+ new_list[i].name = strdup(APPLEDOUBLE);
+ new_list[i].is_wild = False;
+
+ free_namearray(*list);
+
+ *list = new_list;
+ new_list = 0;
+ cur_list = 0;
+}
+
+static void atalk_rrmdir(TALLOC_CTX *ctx, char *path)
+{
+ char *dpath;
+ struct dirent *dent = 0;
+ DIR *dir;
+
+ if (!path) return;
+
+ dir = opendir(path);
+ if (!dir) return;
+
+ while (NULL != (dent = readdir(dir))) {
+ if (strcmp(dent->d_name, ".") == 0 ||
+ strcmp(dent->d_name, "..") == 0)
+ continue;
+ if (!(dpath = talloc_asprintf(ctx, "%s/%s",
+ path, dent->d_name)))
+ continue;
+ atalk_unlink_file(dpath);
+ }
+
+ closedir(dir);
+}
+
+/* Disk operations */
+
+/* Directory operations */
+
+DIR *atalk_opendir(struct vfs_handle_struct *handle, struct connection_struct *conn, const char *fname)
+{
+ DIR *ret = 0;
+
+ ret = SMB_VFS_NEXT_OPENDIR(handle, conn, fname);
+
+ /*
+ * when we try to perform delete operation upon file which has fork
+ * in ./.AppleDouble and this directory wasn't hidden by Samba,
+ * MS Windows explorer causes the error: "Cannot find the specified file"
+ * There is some workaround to avoid this situation, i.e. if
+ * connection has not .AppleDouble entry in either veto or hide
+ * list then it would be nice to add one.
+ */
+
+ atalk_add_to_list(&conn->hide_list);
+ atalk_add_to_list(&conn->veto_list);
+
+ return ret;
+}
+
+static int atalk_rmdir(struct vfs_handle_struct *handle, struct connection_struct *conn, const char *path)
+{
+ BOOL add = False;
+ TALLOC_CTX *ctx = 0;
+ char *dpath;
+
+ if (!conn || !conn->origpath || !path) goto exit_rmdir;
+
+ /* due to there is no way to change bDeleteVetoFiles variable
+ * from this module, gotta use talloc stuff..
+ */
+
+ strstr(path, APPLEDOUBLE) ? (add = False) : (add = True);
+
+ if (!(ctx = talloc_init("remove_directory")))
+ goto exit_rmdir;
+
+ if (!(dpath = talloc_asprintf(ctx, "%s/%s%s",
+ conn->origpath, path, add ? "/"APPLEDOUBLE : "")))
+ goto exit_rmdir;
+
+ atalk_rrmdir(ctx, dpath);
+
+exit_rmdir:
+ talloc_destroy(ctx);
+ return SMB_VFS_NEXT_RMDIR(handle, conn, path);
+}
+
+/* File operations */
+
+static int atalk_rename(struct vfs_handle_struct *handle, struct connection_struct *conn, const char *old, const char *new)
+{
+ int ret = 0;
+ char *adbl_path = 0;
+ char *orig_path = 0;
+ SMB_STRUCT_STAT adbl_info;
+ SMB_STRUCT_STAT orig_info;
+ TALLOC_CTX *ctx;
+
+ ret = SMB_VFS_NEXT_RENAME(handle, conn, old, new);
+
+ if (!conn || !old) return ret;
+
+ if (!(ctx = talloc_init("rename_file")))
+ return ret;
+
+ if (atalk_build_paths(ctx, conn->origpath, old, &adbl_path, &orig_path,
+ &adbl_info, &orig_info) != 0)
+ return ret;
+
+ if (S_ISDIR(orig_info.st_mode) || S_ISREG(orig_info.st_mode)) {
+ DEBUG(3, ("ATALK: %s has passed..\n", adbl_path));
+ goto exit_rename;
+ }
+
+ atalk_unlink_file(adbl_path);
+
+exit_rename:
+ talloc_destroy(ctx);
+ return ret;
+}
+
+static int atalk_unlink(struct vfs_handle_struct *handle, struct connection_struct *conn, const char *path)
+{
+ int ret = 0, i;
+ char *adbl_path = 0;
+ char *orig_path = 0;
+ SMB_STRUCT_STAT adbl_info;
+ SMB_STRUCT_STAT orig_info;
+ TALLOC_CTX *ctx;
+
+ ret = SMB_VFS_NEXT_UNLINK(handle, conn, path);
+
+ if (!conn || !path) return ret;
+
+ /* no .AppleDouble sync if veto or hide list is empty,
+ * otherwise "Cannot find the specified file" error will be caused
+ */
+
+ if (!conn->veto_list) return ret;
+ if (!conn->hide_list) return ret;
+
+ for (i = 0; conn->veto_list[i].name; i ++) {
+ if (strstr(conn->veto_list[i].name, APPLEDOUBLE))
+ break;
+ }
+
+ if (!conn->veto_list[i].name) {
+ for (i = 0; conn->hide_list[i].name; i ++) {
+ if (strstr(conn->hide_list[i].name, APPLEDOUBLE))
+ break;
+ else {
+ DEBUG(3, ("ATALK: %s is not hidden, skipped..\n",
+ APPLEDOUBLE));
+ return ret;
+ }
+ }
+ }
+
+ if (!(ctx = talloc_init("unlink_file")))
+ return ret;
+
+ if (atalk_build_paths(ctx, conn->origpath, path, &adbl_path, &orig_path,
+ &adbl_info, &orig_info) != 0)
+ return ret;
+
+ if (S_ISDIR(orig_info.st_mode) || S_ISREG(orig_info.st_mode)) {
+ DEBUG(3, ("ATALK: %s has passed..\n", adbl_path));
+ goto exit_unlink;
+ }
+
+ atalk_unlink_file(adbl_path);
+
+exit_unlink:
+ talloc_destroy(ctx);
+ return ret;
+}
+
+static int atalk_chmod(struct vfs_handle_struct *handle, struct connection_struct *conn, const char *path, mode_t mode)
+{
+ int ret = 0;
+ char *adbl_path = 0;
+ char *orig_path = 0;
+ SMB_STRUCT_STAT adbl_info;
+ SMB_STRUCT_STAT orig_info;
+ TALLOC_CTX *ctx;
+
+ ret = SMB_VFS_NEXT_CHMOD(handle, conn, path, mode);
+
+ if (!conn || !path) return ret;
+
+ if (!(ctx = talloc_init("chmod_file")))
+ return ret;
+
+ if (atalk_build_paths(ctx, conn->origpath, path, &adbl_path, &orig_path,
+ &adbl_info, &orig_info) != 0)
+ return ret;
+
+ if (!S_ISDIR(orig_info.st_mode) && !S_ISREG(orig_info.st_mode)) {
+ DEBUG(3, ("ATALK: %s has passed..\n", orig_path));
+ goto exit_chmod;
+ }
+
+ chmod(adbl_path, ADOUBLEMODE);
+
+exit_chmod:
+ talloc_destroy(ctx);
+ return ret;
+}
+
+static int atalk_chown(struct vfs_handle_struct *handle, struct connection_struct *conn, const char *path, uid_t uid, gid_t gid)
+{
+ int ret = 0;
+ char *adbl_path = 0;
+ char *orig_path = 0;
+ SMB_STRUCT_STAT adbl_info;
+ SMB_STRUCT_STAT orig_info;
+ TALLOC_CTX *ctx;
+
+ ret = SMB_VFS_NEXT_CHOWN(handle, conn, path, uid, gid);
+
+ if (!conn || !path) return ret;
+
+ if (!(ctx = talloc_init("chown_file")))
+ return ret;
+
+ if (atalk_build_paths(ctx, conn->origpath, path, &adbl_path, &orig_path,
+ &adbl_info, &orig_info) != 0)
+ return ret;
+
+ if (!S_ISDIR(orig_info.st_mode) && !S_ISREG(orig_info.st_mode)) {
+ DEBUG(3, ("ATALK: %s has passed..\n", orig_path));
+ goto exit_chown;
+ }
+
+ chown(adbl_path, uid, gid);
+
+exit_chown:
+ talloc_destroy(ctx);
+ return ret;
+}
+
+static vfs_op_tuple atalk_ops[] = {
+
+ /* Directory operations */
+
+ {SMB_VFS_OP(atalk_opendir), SMB_VFS_OP_OPENDIR, SMB_VFS_LAYER_TRANSPARENT},
+ {SMB_VFS_OP(atalk_rmdir), SMB_VFS_OP_RMDIR, SMB_VFS_LAYER_TRANSPARENT},
+
+ /* File operations */
+
+ {SMB_VFS_OP(atalk_rename), SMB_VFS_OP_RENAME, SMB_VFS_LAYER_TRANSPARENT},
+ {SMB_VFS_OP(atalk_unlink), SMB_VFS_OP_UNLINK, SMB_VFS_LAYER_TRANSPARENT},
+ {SMB_VFS_OP(atalk_chmod), SMB_VFS_OP_CHMOD, SMB_VFS_LAYER_TRANSPARENT},
+ {SMB_VFS_OP(atalk_chown), SMB_VFS_OP_CHOWN, SMB_VFS_LAYER_TRANSPARENT},
+
+ /* Finish VFS operations definition */
+
+ {SMB_VFS_OP(NULL), SMB_VFS_OP_NOOP, SMB_VFS_LAYER_NOOP}
+};
+
+NTSTATUS vfs_netatalk_init(void)
+{
+ return smb_register_vfs(SMB_VFS_INTERFACE_VERSION, "netatalk", atalk_ops);
+}
diff --git a/source/modules/vfs_readonly.c b/source/modules/vfs_readonly.c
new file mode 100644
index 00000000000..ee9e40c2fca
--- /dev/null
+++ b/source/modules/vfs_readonly.c
@@ -0,0 +1,98 @@
+/*
+ Unix SMB/Netbios implementation.
+ Version 1.9.
+ VFS module to perform read-only limitation based on a time period
+ Copyright (C) Alexander Bokovoy 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 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.
+
+ This work was sponsored by Optifacio Software Services, Inc.
+*/
+
+#include "includes.h"
+#include "getdate.h"
+
+/*
+ This module performs a read-only limitation for specified share
+ (or all of them if it is loaded in a [global] section) based on period
+ definition in smb.conf. You can stack this module multiple times under
+ different names to get multiple limit intervals.
+
+ The module uses get_date() function from coreutils' date utility to parse
+ specified dates according to date(1) rules. Look into info page for date(1)
+ to understand the syntax.
+
+ The module accepts one parameter:
+
+ readonly: period = "begin date","end date"
+
+ where "begin date" and "end date" are mandatory and should comply with date(1)
+ syntax for date strings.
+
+ Example:
+
+ readonly: period = "today 14:00","today 15:00"
+
+ Default:
+
+ readonly: period = "today 0:0:0","tomorrow 0:0:0"
+
+ The default covers whole day thus making the share readonly
+
+ */
+
+#define MODULE_NAME "readonly"
+static int readonly_connect(vfs_handle_struct *handle,
+ connection_struct *conn,
+ const char *service,
+ const char *user)
+{
+ const char *period_def[] = {"today 0:0:0", "tomorrow 0:0:0"};
+
+ const char **period = lp_parm_string_list(SNUM(handle->conn),
+ (handle->param ? handle->param : MODULE_NAME),
+ "period", period_def);
+
+ if (period && period[0] && period[1]) {
+ time_t current_time = time(NULL);
+ time_t begin_period = get_date(period[0], &current_time);
+ time_t end_period = get_date(period[1], &current_time);
+
+ if ((current_time >= begin_period) && (current_time <= end_period)) {
+ conn->read_only = True;
+ }
+
+ return SMB_VFS_NEXT_CONNECT(handle, conn, service, user);
+
+ } else {
+
+ return 1;
+
+ }
+}
+
+
+/* VFS operations structure */
+
+static vfs_op_tuple readonly_op_tuples[] = {
+ /* Disk operations */
+ {SMB_VFS_OP(readonly_connect), SMB_VFS_OP_CONNECT, SMB_VFS_LAYER_TRANSPARENT},
+ {SMB_VFS_OP(NULL), SMB_VFS_OP_NOOP, SMB_VFS_LAYER_NOOP}
+};
+
+NTSTATUS vfs_readonly_init(void)
+{
+ return smb_register_vfs(SMB_VFS_INTERFACE_VERSION, MODULE_NAME, readonly_op_tuples);
+}
diff --git a/source/modules/vfs_recycle.c b/source/modules/vfs_recycle.c
new file mode 100644
index 00000000000..1cb1cb327b4
--- /dev/null
+++ b/source/modules/vfs_recycle.c
@@ -0,0 +1,498 @@
+/*
+ * Recycle bin VFS module for Samba.
+ *
+ * Copyright (C) 2001, Brandon Stone, Amherst College, <bbstone@amherst.edu>.
+ * Copyright (C) 2002, Jeremy Allison - modified to make a VFS module.
+ * Copyright (C) 2002, Alexander Bokovoy - cascaded VFS adoption,
+ * Copyright (C) 2002, Juergen Hasch - added some options.
+ * Copyright (C) 2002, Simo Sorce
+ * Copyright (C) 2002, Stefan (metze) Metzmacher
+ *
+ * This program is free software; you can 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"
+
+#define ALLOC_CHECK(ptr, label) do { if ((ptr) == NULL) { DEBUG(0, ("recycle.bin: out of memory!\n")); errno = ENOMEM; goto label; } } while(0)
+
+static int vfs_recycle_debug_level = DBGC_VFS;
+
+#undef DBGC_CLASS
+#define DBGC_CLASS vfs_recycle_debug_level
+
+static int recycle_connect(vfs_handle_struct *handle, connection_struct *conn, const char *service, const char *user);
+static void recycle_disconnect(vfs_handle_struct *handle, connection_struct *conn);
+static int recycle_unlink(vfs_handle_struct *handle, connection_struct *conn, const char *name);
+
+static vfs_op_tuple recycle_ops[] = {
+
+ /* Disk operations */
+ {SMB_VFS_OP(recycle_connect), SMB_VFS_OP_CONNECT, SMB_VFS_LAYER_TRANSPARENT},
+ {SMB_VFS_OP(recycle_disconnect), SMB_VFS_OP_DISCONNECT, SMB_VFS_LAYER_TRANSPARENT},
+
+ /* File operations */
+ {SMB_VFS_OP(recycle_unlink), SMB_VFS_OP_UNLINK, SMB_VFS_LAYER_TRANSPARENT},
+
+ {SMB_VFS_OP(NULL), SMB_VFS_OP_NOOP, SMB_VFS_LAYER_NOOP}
+};
+
+static int recycle_connect(vfs_handle_struct *handle, connection_struct *conn, const char *service, const char *user)
+{
+ DEBUG(10,("recycle_connect() connect to service[%s] as user[%s].\n",
+ service,user));
+
+ return SMB_VFS_NEXT_CONNECT(handle, conn, service, user);
+}
+
+static void recycle_disconnect(vfs_handle_struct *handle, connection_struct *conn)
+{
+ DEBUG(10,("recycle_disconnect() connect to service[%s].\n",
+ lp_servicename(SNUM(conn))));
+
+ SMB_VFS_NEXT_DISCONNECT(handle, conn);
+}
+
+static const char *recycle_repository(vfs_handle_struct *handle)
+{
+ const char *tmp_str = NULL;
+
+
+ tmp_str = lp_parm_const_string(SNUM(handle->conn), "recycle", "repository",".recycle");
+
+ DEBUG(10, ("recycle: repository = %s\n", tmp_str));
+
+ return tmp_str;
+}
+
+static BOOL recycle_keep_dir_tree(vfs_handle_struct *handle)
+{
+ BOOL ret;
+
+ ret = lp_parm_bool(SNUM(handle->conn), "recycle", "keeptree", False);
+
+ DEBUG(10, ("recycle_bin: keeptree = %s\n", ret?"True":"False"));
+
+ return ret;
+}
+
+static BOOL recycle_versions(vfs_handle_struct *handle)
+{
+ BOOL ret;
+
+ ret = lp_parm_bool(SNUM(handle->conn), "recycle", "versions", False);
+
+ DEBUG(10, ("recycle: versions = %s\n", ret?"True":"False"));
+
+ return ret;
+}
+
+static BOOL recycle_touch(vfs_handle_struct *handle)
+{
+ BOOL ret;
+
+ ret = lp_parm_bool(SNUM(handle->conn), "recycle", "touch", False);
+
+ DEBUG(10, ("recycle: touch = %s\n", ret?"True":"False"));
+
+ return ret;
+}
+
+static const char **recycle_exclude(vfs_handle_struct *handle)
+{
+ const char **tmp_lp;
+
+ tmp_lp = lp_parm_string_list(SNUM(handle->conn), "recycle", "exclude", NULL);
+
+ DEBUG(10, ("recycle: exclude = %s ...\n", tmp_lp?*tmp_lp:""));
+
+ return tmp_lp;
+}
+
+static const char **recycle_exclude_dir(vfs_handle_struct *handle)
+{
+ const char **tmp_lp;
+
+ tmp_lp = lp_parm_string_list(SNUM(handle->conn), "recycle", "exclude_dir", NULL);
+
+ DEBUG(10, ("recycle: exclude_dir = %s ...\n", tmp_lp?*tmp_lp:""));
+
+ return tmp_lp;
+}
+
+static const char **recycle_noversions(vfs_handle_struct *handle)
+{
+ const char **tmp_lp;
+
+ tmp_lp = lp_parm_string_list(SNUM(handle->conn), "recycle", "noversions", NULL);
+
+ DEBUG(10, ("recycle: noversions = %s\n", tmp_lp?*tmp_lp:""));
+
+ return tmp_lp;
+}
+
+static int recycle_maxsize(vfs_handle_struct *handle)
+{
+ int maxsize;
+
+ maxsize = lp_parm_int(SNUM(handle->conn), "recycle", "maxsize", -1);
+
+ DEBUG(10, ("recycle: maxsize = %d\n", maxsize));
+
+ return maxsize;
+}
+
+static BOOL recycle_directory_exist(vfs_handle_struct *handle, const char *dname)
+{
+ SMB_STRUCT_STAT st;
+
+ if (SMB_VFS_NEXT_STAT(handle, handle->conn, dname, &st) == 0) {
+ if (S_ISDIR(st.st_mode)) {
+ return True;
+ }
+ }
+
+ return False;
+}
+
+static BOOL recycle_file_exist(vfs_handle_struct *handle, const char *fname)
+{
+ SMB_STRUCT_STAT st;
+
+ if (SMB_VFS_NEXT_STAT(handle, handle->conn, fname, &st) == 0) {
+ if (S_ISREG(st.st_mode)) {
+ return True;
+ }
+ }
+
+ return False;
+}
+
+/**
+ * Return file size
+ * @param conn connection
+ * @param fname file name
+ * @return size in bytes
+ **/
+static SMB_OFF_T recycle_get_file_size(vfs_handle_struct *handle, const char *fname)
+{
+ SMB_STRUCT_STAT st;
+
+ if (SMB_VFS_NEXT_STAT(handle, handle->conn, fname, &st) != 0) {
+ DEBUG(0,("recycle: stat for %s returned %s\n", fname, strerror(errno)));
+ return (SMB_OFF_T)0;
+ }
+
+ return(st.st_size);
+}
+
+/**
+ * Create directory tree
+ * @param conn connection
+ * @param dname Directory tree to be created
+ * @return Returns True for success
+ **/
+static BOOL recycle_create_dir(vfs_handle_struct *handle, const char *dname)
+{
+ int len;
+ mode_t mode;
+ char *new_dir = NULL;
+ char *tmp_str = NULL;
+ char *token;
+ char *tok_str;
+ BOOL ret = False;
+
+ mode = S_IRUSR | S_IWUSR | S_IXUSR;
+
+ tmp_str = strdup(dname);
+ ALLOC_CHECK(tmp_str, done);
+ tok_str = tmp_str;
+
+ len = strlen(dname)+1;
+ new_dir = (char *)malloc(len + 1);
+ ALLOC_CHECK(new_dir, done);
+ *new_dir = '\0';
+
+ /* Create directory tree if neccessary */
+ for(token = strtok(tok_str, "/"); token; token = strtok(NULL, "/")) {
+ safe_strcat(new_dir, token, len);
+ if (recycle_directory_exist(handle, new_dir))
+ DEBUG(10, ("recycle: dir %s already exists\n", new_dir));
+ else {
+ DEBUG(5, ("recycle: creating new dir %s\n", new_dir));
+ if (SMB_VFS_NEXT_MKDIR(handle, handle->conn, new_dir, mode) != 0) {
+ DEBUG(1,("recycle: mkdir failed for %s with error: %s\n", new_dir, strerror(errno)));
+ ret = False;
+ goto done;
+ }
+ }
+ safe_strcat(new_dir, "/", len);
+ }
+
+ ret = True;
+done:
+ SAFE_FREE(tmp_str);
+ SAFE_FREE(new_dir);
+ return ret;
+}
+
+/**
+ * Check if needle is contained exactly in haystack
+ * @param haystack list of parameters separated by delimimiter character
+ * @param needle string to be matched exactly to haystack
+ * @return True if found
+ **/
+static BOOL checkparam(const char **haystack_list, const char *needle)
+{
+ int i;
+
+ if (haystack_list == NULL || haystack_list[0] == NULL ||
+ *haystack_list[0] == '\0' || needle == NULL || *needle == '\0') {
+ return False;
+ }
+
+ for(i=0; haystack_list[i] ; i++) {
+ if(strequal(haystack_list[i], needle)) {
+ return True;
+ }
+ }
+
+ return False;
+}
+
+/**
+ * Check if needle is contained in haystack, * and ? patterns are resolved
+ * @param haystack list of parameters separated by delimimiter character
+ * @param needle string to be matched exectly to haystack including pattern matching
+ * @return True if found
+ **/
+static BOOL matchparam(const char **haystack_list, const char *needle)
+{
+ int i;
+
+ if (haystack_list == NULL || haystack_list[0] == NULL ||
+ *haystack_list[0] == '\0' || needle == NULL || *needle == '\0') {
+ return False;
+ }
+
+ for(i=0; haystack_list[i] ; i++) {
+ if(!unix_wild_match(haystack_list[i], needle)) {
+ return True;
+ }
+ }
+
+ return False;
+}
+
+/**
+ * Touch access date
+ **/
+static void recycle_do_touch(vfs_handle_struct *handle, const char *fname)
+{
+ SMB_STRUCT_STAT st;
+ struct utimbuf tb;
+ time_t currtime;
+
+ if (SMB_VFS_NEXT_STAT(handle, handle->conn, fname, &st) != 0) {
+ DEBUG(0,("recycle: stat for %s returned %s\n", fname, strerror(errno)));
+ return;
+ }
+ currtime = time(&currtime);
+ tb.actime = currtime;
+ tb.modtime = st.st_mtime;
+
+ if (SMB_VFS_NEXT_UTIME(handle, handle->conn, fname, &tb) == -1 ) {
+ DEBUG(0, ("recycle: touching %s failed, reason = %s\n", fname, strerror(errno)));
+ }
+}
+
+/**
+ * Check if file should be recycled
+ **/
+static int recycle_unlink(vfs_handle_struct *handle, connection_struct *conn, const char *file_name)
+{
+ char *path_name = NULL;
+ char *temp_name = NULL;
+ char *final_name = NULL;
+ const char *base;
+ char *repository = NULL;
+ int i = 1;
+ int maxsize;
+ SMB_OFF_T file_size; /* space_avail; */
+ BOOL exist;
+ int rc = -1;
+
+ repository = alloc_sub_conn(conn, recycle_repository(handle));
+ ALLOC_CHECK(repository, done);
+ /* shouldn't we allow absolute path names here? --metze */
+ trim_char(repository, '/', '/');
+
+ if(!repository || *(repository) == '\0') {
+ DEBUG(3, ("recycle: repository path not set, purging %s...\n", file_name));
+ rc = SMB_VFS_NEXT_UNLINK(handle, conn, file_name);
+ goto done;
+ }
+
+ /* we don't recycle the recycle bin... */
+ if (strncmp(file_name, repository, strlen(repository)) == 0) {
+ DEBUG(3, ("recycle: File is within recycling bin, unlinking ...\n"));
+ rc = SMB_VFS_NEXT_UNLINK(handle, conn, file_name);
+ goto done;
+ }
+
+ file_size = recycle_get_file_size(handle, file_name);
+ /* it is wrong to purge filenames only because they are empty imho
+ * --- simo
+ *
+ if(fsize == 0) {
+ DEBUG(3, ("recycle: File %s is empty, purging...\n", file_name));
+ rc = SMB_VFS_NEXT_UNLINK(handle,conn,file_name);
+ goto done;
+ }
+ */
+
+ /* FIXME: this is wrong, we should check the hole size of the recycle bin is
+ * not greater then maxsize, not the size of the single file, also it is better
+ * to remove older files
+ */
+ maxsize = recycle_maxsize(handle);
+ if(maxsize > 0 && file_size > maxsize) {
+ DEBUG(3, ("recycle: File %s exceeds maximum recycle size, purging... \n", file_name));
+ rc = SMB_VFS_NEXT_UNLINK(handle, conn, file_name);
+ goto done;
+ }
+
+ /* FIXME: this is wrong: moving files with rename does not change the disk space
+ * allocation
+ *
+ space_avail = SMB_VFS_NEXT_DISK_FREE(handle, conn, ".", True, &bsize, &dfree, &dsize) * 1024L;
+ DEBUG(5, ("space_avail = %Lu, file_size = %Lu\n", space_avail, file_size));
+ if(space_avail < file_size) {
+ DEBUG(3, ("recycle: Not enough diskspace, purging file %s\n", file_name));
+ rc = SMB_VFS_NEXT_UNLINK(handle, conn, file_name);
+ goto done;
+ }
+ */
+
+ /* extract filename and path */
+ base = strrchr(file_name, '/');
+ if (base == NULL) {
+ base = file_name;
+ path_name = strdup("/");
+ ALLOC_CHECK(path_name, done);
+ }
+ else {
+ path_name = strdup(file_name);
+ ALLOC_CHECK(path_name, done);
+ path_name[base - file_name] = '\0';
+ base++;
+ }
+
+ DEBUG(10, ("recycle: fname = %s\n", file_name)); /* original filename with path */
+ DEBUG(10, ("recycle: fpath = %s\n", path_name)); /* original path */
+ DEBUG(10, ("recycle: base = %s\n", base)); /* filename without path */
+
+ if (matchparam(recycle_exclude(handle), base)) {
+ DEBUG(3, ("recycle: file %s is excluded \n", base));
+ rc = SMB_VFS_NEXT_UNLINK(handle, conn, file_name);
+ goto done;
+ }
+
+ /* FIXME: this check will fail if we have more than one level of directories,
+ * we shoud check for every level 1, 1/2, 1/2/3, 1/2/3/4 ....
+ * ---simo
+ */
+ if (checkparam(recycle_exclude_dir(handle), path_name)) {
+ DEBUG(3, ("recycle: directory %s is excluded \n", path_name));
+ rc = SMB_VFS_NEXT_UNLINK(handle, conn, file_name);
+ goto done;
+ }
+
+ if (recycle_keep_dir_tree(handle) == True) {
+ asprintf(&temp_name, "%s/%s", repository, path_name);
+ } else {
+ temp_name = strdup(repository);
+ }
+ ALLOC_CHECK(temp_name, done);
+
+ exist = recycle_directory_exist(handle, temp_name);
+ if (exist) {
+ DEBUG(10, ("recycle: Directory already exists\n"));
+ } else {
+ DEBUG(10, ("recycle: Creating directory %s\n", temp_name));
+ if (recycle_create_dir(handle, temp_name) == False) {
+ DEBUG(3, ("recycle: Could not create directory, purging %s...\n", file_name));
+ rc = SMB_VFS_NEXT_UNLINK(handle, conn, file_name);
+ goto done;
+ }
+ }
+
+ asprintf(&final_name, "%s/%s", temp_name, base);
+ ALLOC_CHECK(final_name, done);
+ DEBUG(10, ("recycle: recycled file name: %s\n", final_name)); /* new filename with path */
+
+ /* check if we should delete file from recycle bin */
+ if (recycle_file_exist(handle, final_name)) {
+ if (recycle_versions(handle) == False || matchparam(recycle_noversions(handle), base) == True) {
+ DEBUG(3, ("recycle: Removing old file %s from recycle bin\n", final_name));
+ if (SMB_VFS_NEXT_UNLINK(handle, conn, final_name) != 0) {
+ DEBUG(1, ("recycle: Error deleting old file: %s\n", strerror(errno)));
+ }
+ }
+ }
+
+ /* rename file we move to recycle bin */
+ i = 1;
+ while (recycle_file_exist(handle, final_name)) {
+ SAFE_FREE(final_name);
+ asprintf(&final_name, "%s/Copy #%d of %s", temp_name, i++, base);
+ }
+
+ DEBUG(10, ("recycle: Moving %s to %s\n", file_name, final_name));
+ rc = SMB_VFS_NEXT_RENAME(handle, conn, file_name, final_name);
+ if (rc != 0) {
+ DEBUG(3, ("recycle: Move error %d (%s), purging file %s (%s)\n", errno, strerror(errno), file_name, final_name));
+ rc = SMB_VFS_NEXT_UNLINK(handle, conn, file_name);
+ goto done;
+ }
+
+ /* touch access date of moved file */
+ if (recycle_touch(handle) == True )
+ recycle_do_touch(handle, final_name);
+
+done:
+ SAFE_FREE(path_name);
+ SAFE_FREE(temp_name);
+ SAFE_FREE(final_name);
+ SAFE_FREE(repository);
+ return rc;
+}
+
+NTSTATUS vfs_recycle_init(void)
+{
+ NTSTATUS ret = smb_register_vfs(SMB_VFS_INTERFACE_VERSION, "recycle", recycle_ops);
+
+ if (!NT_STATUS_IS_OK(ret))
+ return ret;
+
+ vfs_recycle_debug_level = debug_add_class("recycle");
+ if (vfs_recycle_debug_level == -1) {
+ vfs_recycle_debug_level = DBGC_VFS;
+ DEBUG(0, ("vfs_recycle: Couldn't register custom debugging class!\n"));
+ } else {
+ DEBUG(10, ("vfs_recycle: Debug class number of 'recycle': %d\n", vfs_recycle_debug_level));
+ }
+
+ return ret;
+}
diff --git a/source/modules/weird.c b/source/modules/weird.c
new file mode 100644
index 00000000000..444853f3831
--- /dev/null
+++ b/source/modules/weird.c
@@ -0,0 +1,131 @@
+/*
+ Unix SMB/CIFS implementation.
+ Samba module with developer tools
+ Copyright (C) Andrew Tridgell 2001
+ Copyright (C) Jelmer Vernooij 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 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"
+
+static struct {
+ char from;
+ char *to;
+ int len;
+} weird_table[] = {
+ {'q', "^q^", 3},
+ {'Q', "^Q^", 3},
+ {0, NULL}
+};
+
+static size_t weird_pull(void *cd, char **inbuf, size_t *inbytesleft,
+ char **outbuf, size_t *outbytesleft)
+{
+ while (*inbytesleft >= 1 && *outbytesleft >= 2) {
+ int i;
+ int done = 0;
+ for (i=0;weird_table[i].from;i++) {
+ if (strncmp((*inbuf),
+ weird_table[i].to,
+ weird_table[i].len) == 0) {
+ if (*inbytesleft < weird_table[i].len) {
+ DEBUG(0,("ERROR: truncated weird string\n"));
+ /* smb_panic("weird_pull"); */
+
+ } else {
+ (*outbuf)[0] = weird_table[i].from;
+ (*outbuf)[1] = 0;
+ (*inbytesleft) -= weird_table[i].len;
+ (*outbytesleft) -= 2;
+ (*inbuf) += weird_table[i].len;
+ (*outbuf) += 2;
+ done = 1;
+ break;
+ }
+ }
+ }
+ if (done) continue;
+ (*outbuf)[0] = (*inbuf)[0];
+ (*outbuf)[1] = 0;
+ (*inbytesleft) -= 1;
+ (*outbytesleft) -= 2;
+ (*inbuf) += 1;
+ (*outbuf) += 2;
+ }
+
+ if (*inbytesleft > 0) {
+ errno = E2BIG;
+ return -1;
+ }
+
+ return 0;
+}
+
+static size_t weird_push(void *cd, char **inbuf, size_t *inbytesleft,
+ char **outbuf, size_t *outbytesleft)
+{
+ int ir_count=0;
+
+ while (*inbytesleft >= 2 && *outbytesleft >= 1) {
+ int i;
+ int done=0;
+ for (i=0;weird_table[i].from;i++) {
+ if ((*inbuf)[0] == weird_table[i].from &&
+ (*inbuf)[1] == 0) {
+ if (*outbytesleft < weird_table[i].len) {
+ DEBUG(0,("No room for weird character\n"));
+ /* smb_panic("weird_push"); */
+ } else {
+ memcpy(*outbuf, weird_table[i].to,
+ weird_table[i].len);
+ (*inbytesleft) -= 2;
+ (*outbytesleft) -= weird_table[i].len;
+ (*inbuf) += 2;
+ (*outbuf) += weird_table[i].len;
+ done = 1;
+ break;
+ }
+ }
+ }
+ if (done) continue;
+
+ (*outbuf)[0] = (*inbuf)[0];
+ if ((*inbuf)[1]) ir_count++;
+ (*inbytesleft) -= 2;
+ (*outbytesleft) -= 1;
+ (*inbuf) += 2;
+ (*outbuf) += 1;
+ }
+
+ if (*inbytesleft == 1) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ if (*inbytesleft > 1) {
+ errno = E2BIG;
+ return -1;
+ }
+
+ return ir_count;
+}
+
+struct charset_functions weird_functions = {"WEIRD", weird_pull, weird_push};
+
+NTSTATUS charset_weird_init(void)
+{
+ return smb_register_charset(&weird_functions);
+}
diff --git a/source/msdfs/msdfs.c b/source/msdfs/msdfs.c
new file mode 100644
index 00000000000..2ac7bda1754
--- /dev/null
+++ b/source/msdfs/msdfs.c
@@ -0,0 +1,1017 @@
+/*
+ Unix SMB/Netbios implementation.
+ Version 3.0
+ MSDfs services for Samba
+ Copyright (C) Shirish Kalele 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 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"
+
+extern fstring local_machine;
+extern uint32 global_client_caps;
+
+/**********************************************************************
+ Parse the pathname of the form \hostname\service\reqpath
+ into the dfs_path structure
+ **********************************************************************/
+
+static BOOL parse_dfs_path(char* pathname, struct dfs_path* pdp)
+{
+ pstring pathname_local;
+ char* p,*temp;
+
+ pstrcpy(pathname_local,pathname);
+ p = temp = pathname_local;
+
+ ZERO_STRUCTP(pdp);
+
+ trim_char(temp,'\\','\\');
+ DEBUG(10,("temp in parse_dfs_path: .%s. after trimming \\'s\n",temp));
+
+ /* now tokenize */
+ /* parse out hostname */
+ p = strchr_m(temp,'\\');
+ if(p == NULL)
+ return False;
+ *p = '\0';
+ pstrcpy(pdp->hostname,temp);
+ DEBUG(10,("parse_dfs_path: hostname: %s\n",pdp->hostname));
+
+ /* parse out servicename */
+ temp = p+1;
+ p = strchr_m(temp,'\\');
+ if(p == NULL) {
+ pstrcpy(pdp->servicename,temp);
+ pdp->reqpath[0] = '\0';
+ return True;
+ }
+ *p = '\0';
+ pstrcpy(pdp->servicename,temp);
+ DEBUG(10,("parse_dfs_path: servicename: %s\n",pdp->servicename));
+
+ /* rest is reqpath */
+ check_path_syntax(pdp->reqpath, p+1);
+
+ DEBUG(10,("parse_dfs_path: rest of the path: %s\n",pdp->reqpath));
+ return True;
+}
+
+/**********************************************************************
+ Parse the pathname of the form /hostname/service/reqpath
+ into the dfs_path structure
+ **********************************************************************/
+
+static BOOL parse_processed_dfs_path(char* pathname, struct dfs_path* pdp)
+{
+ pstring pathname_local;
+ char* p,*temp;
+
+ pstrcpy(pathname_local,pathname);
+ p = temp = pathname_local;
+
+ ZERO_STRUCTP(pdp);
+
+ trim_char(temp,'/','/');
+ DEBUG(10,("temp in parse_processed_dfs_path: .%s. after trimming \\'s\n",temp));
+
+ /* now tokenize */
+ /* parse out hostname */
+ p = strchr_m(temp,'/');
+ if(p == NULL)
+ return False;
+ *p = '\0';
+ pstrcpy(pdp->hostname,temp);
+ DEBUG(10,("parse_processed_dfs_path: hostname: %s\n",pdp->hostname));
+
+ /* parse out servicename */
+ temp = p+1;
+ p = strchr_m(temp,'/');
+ if(p == NULL) {
+ pstrcpy(pdp->servicename,temp);
+ pdp->reqpath[0] = '\0';
+ return True;
+ }
+ *p = '\0';
+ pstrcpy(pdp->servicename,temp);
+ DEBUG(10,("parse_processed_dfs_path: servicename: %s\n",pdp->servicename));
+
+ /* rest is reqpath */
+ check_path_syntax(pdp->reqpath, p+1);
+
+ DEBUG(10,("parse_processed_dfs_path: rest of the path: %s\n",pdp->reqpath));
+ return True;
+}
+
+/********************************************************
+ Fake up a connection struct for the VFS layer.
+ Note this CHANGES CWD !!!! JRA.
+*********************************************************/
+
+static BOOL create_conn_struct( connection_struct *conn, int snum, char *path)
+{
+ ZERO_STRUCTP(conn);
+ conn->service = snum;
+ conn->connectpath = path;
+ pstring_sub(conn->connectpath , "%S", lp_servicename(snum));
+
+ /* needed for smbd_vfs_init() */
+
+ if ( (conn->mem_ctx=talloc_init("connection_struct")) == NULL ) {
+ DEBUG(0,("talloc_init(connection_struct) failed!\n"));
+ return False;
+ }
+
+ if (!smbd_vfs_init(conn)) {
+ DEBUG(0,("create_conn_struct: smbd_vfs_init failed.\n"));
+ talloc_destroy( conn->mem_ctx );
+ return False;
+ }
+
+ /*
+ * Windows seems to insist on doing trans2getdfsreferral() calls on the IPC$
+ * share as the anonymous user. If we try to chdir as that user we will
+ * fail.... WTF ? JRA.
+ */
+
+ if (vfs_ChDir(conn,conn->connectpath) != 0) {
+ DEBUG(3,("create_conn_struct: Can't ChDir to new conn path %s. Error was %s\n",
+ conn->connectpath, strerror(errno) ));
+ talloc_destroy( conn->mem_ctx );
+ return False;
+ }
+ return True;
+}
+
+
+/**********************************************************************
+ Parse the contents of a symlink to verify if it is an msdfs referral
+ A valid referral is of the form: msdfs:server1\share1,server2\share2
+ **********************************************************************/
+
+static BOOL parse_symlink(char* buf,struct referral** preflist,
+ int* refcount)
+{
+ pstring temp;
+ char* prot;
+ char* alt_path[MAX_REFERRAL_COUNT];
+ int count=0, i;
+ struct referral* reflist;
+
+ pstrcpy(temp,buf);
+
+ prot = strtok(temp,":");
+
+ if (!strequal(prot, "msdfs"))
+ return False;
+
+ /* No referral list requested. Just yes/no. */
+ if (!preflist)
+ return True;
+
+ /* parse out the alternate paths */
+ while(((alt_path[count] = strtok(NULL,",")) != NULL) && count<MAX_REFERRAL_COUNT)
+ count++;
+
+ DEBUG(10,("parse_symlink: count=%d\n", count));
+
+ reflist = *preflist = (struct referral*) malloc(count * sizeof(struct referral));
+ if(reflist == NULL) {
+ DEBUG(0,("parse_symlink: Malloc failed!\n"));
+ return False;
+ }
+
+ for(i=0;i<count;i++) {
+ char *p;
+
+ /* replace all /'s in the alternate path by a \ */
+ for(p = alt_path[i]; *p && ((p = strchr_m(p,'/'))!=NULL); p++) {
+ *p = '\\';
+ }
+
+ /* Remove leading '\\'s */
+ p = alt_path[i];
+ while (*p && (*p == '\\')) {
+ p++;
+ }
+
+ pstrcpy(reflist[i].alternate_path, "\\");
+ pstrcat(reflist[i].alternate_path, p);
+ reflist[i].proximity = 0;
+ reflist[i].ttl = REFERRAL_TTL;
+ DEBUG(10, ("parse_symlink: Created alt path: %s\n", reflist[i].alternate_path));
+ }
+
+ if(refcount)
+ *refcount = count;
+
+ return True;
+}
+
+/**********************************************************************
+ Returns true if the unix path is a valid msdfs symlink
+ **********************************************************************/
+
+BOOL is_msdfs_link(connection_struct* conn, char * path,
+ struct referral** reflistp, int* refcnt,
+ SMB_STRUCT_STAT *sbufp)
+{
+ SMB_STRUCT_STAT st;
+ pstring referral;
+ int referral_len = 0;
+
+ if (!path || !conn)
+ return False;
+
+ if (sbufp == NULL)
+ sbufp = &st;
+
+ if (SMB_VFS_LSTAT(conn, path, sbufp) != 0) {
+ DEBUG(5,("is_msdfs_link: %s does not exist.\n",path));
+ return False;
+ }
+
+ if (S_ISLNK(sbufp->st_mode)) {
+ /* open the link and read it */
+ referral_len = SMB_VFS_READLINK(conn, path, referral,
+ sizeof(pstring));
+ if (referral_len == -1) {
+ DEBUG(0,("is_msdfs_link: Error reading msdfs link %s: %s\n", path, strerror(errno)));
+ return False;
+ }
+
+ referral[referral_len] = '\0';
+ DEBUG(5,("is_msdfs_link: %s -> %s\n",path,referral));
+ if (parse_symlink(referral, reflistp, refcnt))
+ return True;
+ }
+ return False;
+}
+
+/*****************************************************************
+ Used by other functions to decide if a dfs path is remote,
+and to get the list of referred locations for that remote path.
+
+findfirst_flag: For findfirsts, dfs links themselves are not
+redirected, but paths beyond the links are. For normal smb calls,
+even dfs links need to be redirected.
+
+self_referralp: clients expect a dfs referral for the same share when
+they request referrals for dfs roots on a server.
+
+consumedcntp: how much of the dfs path is being redirected. the client
+should try the remaining path on the redirected server.
+
+*****************************************************************/
+
+static BOOL resolve_dfs_path(pstring dfspath, struct dfs_path* dp,
+ connection_struct* conn,
+ BOOL findfirst_flag,
+ struct referral** reflistpp, int* refcntp,
+ BOOL* self_referralp, int* consumedcntp)
+{
+ pstring localpath;
+ int consumed_level = 1;
+ char *p;
+ BOOL bad_path = False;
+ SMB_STRUCT_STAT sbuf;
+ pstring reqpath;
+
+ if (!dp || !conn) {
+ DEBUG(1,("resolve_dfs_path: NULL dfs_path* or NULL connection_struct*!\n"));
+ return False;
+ }
+
+ if (dp->reqpath[0] == '\0') {
+ if (self_referralp) {
+ DEBUG(6,("resolve_dfs_path: self-referral. returning False\n"));
+ *self_referralp = True;
+ }
+ return False;
+ }
+
+ DEBUG(10,("resolve_dfs_path: Conn path = %s req_path = %s\n", conn->connectpath, dp->reqpath));
+
+ unix_convert(dp->reqpath,conn,0,&bad_path,&sbuf);
+ /* JRA... should we strlower the last component here.... ? */
+ pstrcpy(localpath, dp->reqpath);
+
+ /* check if need to redirect */
+ if (is_msdfs_link(conn, localpath, reflistpp, refcntp, NULL)) {
+ if (findfirst_flag) {
+ DEBUG(6,("resolve_dfs_path (FindFirst) No redirection "
+ "for dfs link %s.\n", dfspath));
+ return False;
+ } else {
+ DEBUG(6,("resolve_dfs_path: %s resolves to a valid Dfs link.\n",
+ dfspath));
+ if (consumedcntp)
+ *consumedcntp = strlen(dfspath);
+ return True;
+ }
+ }
+
+ /* redirect if any component in the path is a link */
+ pstrcpy(reqpath, dp->reqpath);
+ p = strrchr_m(reqpath, '/');
+ while (p) {
+ *p = '\0';
+ pstrcpy(localpath, reqpath);
+ if (is_msdfs_link(conn, localpath, reflistpp, refcntp, NULL)) {
+ DEBUG(4, ("resolve_dfs_path: Redirecting %s because parent %s is dfs link\n", dfspath, localpath));
+
+ /* To find the path consumed, we truncate the original
+ DFS pathname passed to use to remove the last
+ component. The length of the resulting string is
+ the path consumed
+ */
+ if (consumedcntp) {
+ char *q;
+ pstring buf;
+ pstrcpy(buf, dfspath);
+ trim_char(buf, '\0', '\\');
+ for (; consumed_level; consumed_level--) {
+ q = strrchr_m(buf, '\\');
+ if (q)
+ *q = 0;
+ }
+ *consumedcntp = strlen(buf);
+ DEBUG(10, ("resolve_dfs_path: Path consumed: %s (%d)\n", buf, *consumedcntp));
+ }
+
+ return True;
+ }
+ p = strrchr_m(reqpath, '/');
+ consumed_level++;
+ }
+
+ return False;
+}
+
+/*****************************************************************
+ Decides if a dfs pathname should be redirected or not.
+ If not, the pathname is converted to a tcon-relative local unix path
+*****************************************************************/
+
+BOOL dfs_redirect(pstring pathname, connection_struct* conn,
+ BOOL findfirst_flag)
+{
+ struct dfs_path dp;
+
+ if (!conn || !pathname)
+ return False;
+
+ parse_processed_dfs_path(pathname, &dp);
+
+ /* if dfs pathname for a non-dfs share, convert to tcon-relative
+ path and return false */
+ if (!lp_msdfs_root(SNUM(conn))) {
+ pstrcpy(pathname, dp.reqpath);
+ return False;
+ }
+
+ if (!strequal(dp.servicename, lp_servicename(SNUM(conn)) ))
+ return False;
+
+ if (resolve_dfs_path(pathname, &dp, conn, findfirst_flag,
+ NULL, NULL, NULL, NULL)) {
+ DEBUG(3,("dfs_redirect: Redirecting %s\n", pathname));
+ return True;
+ } else {
+ DEBUG(3,("dfs_redirect: Not redirecting %s.\n", pathname));
+
+ /* Form non-dfs tcon-relative path */
+ pstrcpy(pathname, dp.reqpath);
+ DEBUG(3,("dfs_redirect: Path converted to non-dfs path %s\n",
+ pathname));
+ return False;
+ }
+
+ /* never reached */
+}
+
+/**********************************************************************
+ Return a self referral.
+**********************************************************************/
+
+static BOOL self_ref(char *pathname, struct junction_map *jucn,
+ int *consumedcntp, BOOL *self_referralp)
+{
+ struct referral *ref;
+
+ if (self_referralp != NULL)
+ *self_referralp = True;
+
+ jucn->referral_count = 1;
+ if((ref = (struct referral*) malloc(sizeof(struct referral))) == NULL) {
+ DEBUG(0,("self_ref: malloc failed for referral\n"));
+ return False;
+ }
+
+ pstrcpy(ref->alternate_path,pathname);
+ ref->proximity = 0;
+ ref->ttl = REFERRAL_TTL;
+ jucn->referral_list = ref;
+ if (consumedcntp)
+ *consumedcntp = strlen(pathname);
+
+ return True;
+}
+
+/**********************************************************************
+ Gets valid referrals for a dfs path and fills up the
+ junction_map structure
+**********************************************************************/
+
+BOOL get_referred_path(char *pathname, struct junction_map *jucn,
+ int *consumedcntp, BOOL *self_referralp)
+{
+ struct dfs_path dp;
+
+ struct connection_struct conns;
+ struct connection_struct* conn = &conns;
+ pstring conn_path;
+ int snum;
+ BOOL ret = False;
+ BOOL self_referral = False;
+
+ if (!pathname || !jucn)
+ return False;
+
+ ZERO_STRUCT(conns);
+
+ if (self_referralp)
+ *self_referralp = False;
+ else
+ self_referralp = &self_referral;
+
+ parse_dfs_path(pathname, &dp);
+
+ /* Verify hostname in path */
+ if (local_machine && (!strequal(local_machine, dp.hostname))) {
+ /* Hostname mismatch, check if one of our IP addresses */
+ if (!ismyip(*interpret_addr2(dp.hostname))) {
+ DEBUG(3, ("get_referred_path: Invalid hostname %s in path %s\n",
+ dp.hostname, pathname));
+ return False;
+ }
+ }
+
+ pstrcpy(jucn->service_name, dp.servicename);
+ pstrcpy(jucn->volume_name, dp.reqpath);
+
+ /* Verify the share is a dfs root */
+ snum = lp_servicenumber(jucn->service_name);
+ if(snum < 0) {
+ if ((snum = find_service(jucn->service_name)) < 0)
+ return False;
+ }
+
+ if (!lp_msdfs_root(snum)) {
+ DEBUG(3,("get_referred_path: .%s. in dfs path %s is not a dfs root.\n",
+ dp.servicename, pathname));
+ goto out;
+ }
+
+ /*
+ * Self referrals are tested with a anonymous IPC connection and
+ * a GET_DFS_REFERRAL call to \\server\share. (which means dp.reqpath[0] points
+ * to an empty string). create_conn_struct cd's into the directory and will
+ * fail if it cannot (as the anonymous user). Cope with this.
+ */
+
+ if (dp.reqpath[0] == '\0') {
+ return self_ref(pathname, jucn, consumedcntp, self_referralp);
+ }
+
+ pstrcpy(conn_path, lp_pathname(snum));
+ if (!create_conn_struct(conn, snum, conn_path))
+ return False;
+
+ if (*lp_msdfs_proxy(snum) != '\0') {
+ struct referral* ref;
+ jucn->referral_count = 1;
+ if ((ref = (struct referral*) malloc(sizeof(struct referral))) == NULL) {
+ DEBUG(0, ("malloc failed for referral\n"));
+ goto out;
+ }
+
+ pstrcpy(ref->alternate_path, lp_msdfs_proxy(snum));
+ if (dp.reqpath[0] != '\0')
+ pstrcat(ref->alternate_path, dp.reqpath);
+ ref->proximity = 0;
+ ref->ttl = REFERRAL_TTL;
+ jucn->referral_list = ref;
+ if (consumedcntp)
+ *consumedcntp = strlen(pathname);
+ ret = True;
+ goto out;
+ }
+
+ /* If not remote & not a self referral, return False */
+ if (!resolve_dfs_path(pathname, &dp, conn, False,
+ &jucn->referral_list, &jucn->referral_count,
+ self_referralp, consumedcntp)) {
+ if (!*self_referralp) {
+ DEBUG(3,("get_referred_path: No valid referrals for path %s\n", pathname));
+ goto out;
+ }
+ }
+
+ /* if self_referral, fill up the junction map */
+ if (*self_referralp) {
+ if (self_ref(pathname, jucn, consumedcntp, self_referralp) == False) {
+ goto out;
+ }
+ }
+
+ ret = True;
+
+out:
+ if (conn->mem_ctx)
+ talloc_destroy( conn->mem_ctx );
+
+ return ret;
+}
+
+static int setup_ver2_dfs_referral(char* pathname, char** ppdata,
+ struct junction_map* junction,
+ int consumedcnt,
+ BOOL self_referral)
+{
+ char* pdata = *ppdata;
+
+ unsigned char uni_requestedpath[1024];
+ int uni_reqpathoffset1,uni_reqpathoffset2;
+ int uni_curroffset;
+ int requestedpathlen=0;
+ int offset;
+ int reply_size = 0;
+ int i=0;
+
+ DEBUG(10,("setting up version2 referral\nRequested path:\n"));
+
+ requestedpathlen = rpcstr_push(uni_requestedpath, pathname, -1,
+ STR_TERMINATE);
+
+ dump_data(10, (const char *) uni_requestedpath,requestedpathlen);
+
+ DEBUG(10,("ref count = %u\n",junction->referral_count));
+
+ uni_reqpathoffset1 = REFERRAL_HEADER_SIZE +
+ VERSION2_REFERRAL_SIZE * junction->referral_count;
+
+ uni_reqpathoffset2 = uni_reqpathoffset1 + requestedpathlen;
+
+ uni_curroffset = uni_reqpathoffset2 + requestedpathlen;
+
+ reply_size = REFERRAL_HEADER_SIZE + VERSION2_REFERRAL_SIZE*junction->referral_count +
+ 2 * requestedpathlen;
+ DEBUG(10,("reply_size: %u\n",reply_size));
+
+ /* add up the unicode lengths of all the referral paths */
+ for(i=0;i<junction->referral_count;i++) {
+ DEBUG(10,("referral %u : %s\n",i,junction->referral_list[i].alternate_path));
+ reply_size += (strlen(junction->referral_list[i].alternate_path)+1)*2;
+ }
+
+ DEBUG(10,("reply_size = %u\n",reply_size));
+ /* add the unexplained 0x16 bytes */
+ reply_size += 0x16;
+
+ pdata = Realloc(pdata,reply_size);
+ if(pdata == NULL) {
+ DEBUG(0,("malloc failed for Realloc!\n"));
+ return -1;
+ } else
+ *ppdata = pdata;
+
+ /* copy in the dfs requested paths.. required for offset calculations */
+ memcpy(pdata+uni_reqpathoffset1,uni_requestedpath,requestedpathlen);
+ memcpy(pdata+uni_reqpathoffset2,uni_requestedpath,requestedpathlen);
+
+ /* create the header */
+ SSVAL(pdata,0,consumedcnt * 2); /* path consumed */
+ SSVAL(pdata,2,junction->referral_count); /* number of referral in this pkt */
+ if(self_referral)
+ SIVAL(pdata,4,DFSREF_REFERRAL_SERVER | DFSREF_STORAGE_SERVER);
+ else
+ SIVAL(pdata,4,DFSREF_STORAGE_SERVER);
+
+ offset = 8;
+ /* add the referral elements */
+ for(i=0;i<junction->referral_count;i++) {
+ struct referral* ref = &junction->referral_list[i];
+ int unilen;
+
+ SSVAL(pdata,offset,2); /* version 2 */
+ SSVAL(pdata,offset+2,VERSION2_REFERRAL_SIZE);
+ if(self_referral)
+ SSVAL(pdata,offset+4,1);
+ else
+ SSVAL(pdata,offset+4,0);
+ SSVAL(pdata,offset+6,0); /* ref_flags :use path_consumed bytes? */
+ SIVAL(pdata,offset+8,ref->proximity);
+ SIVAL(pdata,offset+12,ref->ttl);
+
+ SSVAL(pdata,offset+16,uni_reqpathoffset1-offset);
+ SSVAL(pdata,offset+18,uni_reqpathoffset2-offset);
+ /* copy referred path into current offset */
+ unilen = rpcstr_push(pdata+uni_curroffset, ref->alternate_path,
+ -1, STR_UNICODE);
+
+ SSVAL(pdata,offset+20,uni_curroffset-offset);
+
+ uni_curroffset += unilen;
+ offset += VERSION2_REFERRAL_SIZE;
+ }
+ /* add in the unexplained 22 (0x16) bytes at the end */
+ memset(pdata+uni_curroffset,'\0',0x16);
+ return reply_size;
+}
+
+static int setup_ver3_dfs_referral(char* pathname, char** ppdata,
+ struct junction_map* junction,
+ int consumedcnt,
+ BOOL self_referral)
+{
+ char* pdata = *ppdata;
+
+ unsigned char uni_reqpath[1024];
+ int uni_reqpathoffset1, uni_reqpathoffset2;
+ int uni_curroffset;
+ int reply_size = 0;
+
+ int reqpathlen = 0;
+ int offset,i=0;
+
+ DEBUG(10,("setting up version3 referral\n"));
+
+ reqpathlen = rpcstr_push(uni_reqpath, pathname, -1, STR_TERMINATE);
+
+ dump_data(10, (char *) uni_reqpath,reqpathlen);
+
+ uni_reqpathoffset1 = REFERRAL_HEADER_SIZE + VERSION3_REFERRAL_SIZE * junction->referral_count;
+ uni_reqpathoffset2 = uni_reqpathoffset1 + reqpathlen;
+ reply_size = uni_curroffset = uni_reqpathoffset2 + reqpathlen;
+
+ for(i=0;i<junction->referral_count;i++) {
+ DEBUG(10,("referral %u : %s\n",i,junction->referral_list[i].alternate_path));
+ reply_size += (strlen(junction->referral_list[i].alternate_path)+1)*2;
+ }
+
+ pdata = Realloc(pdata,reply_size);
+ if(pdata == NULL) {
+ DEBUG(0,("version3 referral setup: malloc failed for Realloc!\n"));
+ return -1;
+ } else
+ *ppdata = pdata;
+
+ /* create the header */
+ SSVAL(pdata,0,consumedcnt * 2); /* path consumed */
+ SSVAL(pdata,2,junction->referral_count); /* number of referral */
+ if(self_referral)
+ SIVAL(pdata,4,DFSREF_REFERRAL_SERVER | DFSREF_STORAGE_SERVER);
+ else
+ SIVAL(pdata,4,DFSREF_STORAGE_SERVER);
+
+ /* copy in the reqpaths */
+ memcpy(pdata+uni_reqpathoffset1,uni_reqpath,reqpathlen);
+ memcpy(pdata+uni_reqpathoffset2,uni_reqpath,reqpathlen);
+
+ offset = 8;
+ for(i=0;i<junction->referral_count;i++) {
+ struct referral* ref = &(junction->referral_list[i]);
+ int unilen;
+
+ SSVAL(pdata,offset,3); /* version 3 */
+ SSVAL(pdata,offset+2,VERSION3_REFERRAL_SIZE);
+ if(self_referral)
+ SSVAL(pdata,offset+4,1);
+ else
+ SSVAL(pdata,offset+4,0);
+
+ SSVAL(pdata,offset+6,0); /* ref_flags :use path_consumed bytes? */
+ SIVAL(pdata,offset+8,ref->ttl);
+
+ SSVAL(pdata,offset+12,uni_reqpathoffset1-offset);
+ SSVAL(pdata,offset+14,uni_reqpathoffset2-offset);
+ /* copy referred path into current offset */
+ unilen = rpcstr_push(pdata+uni_curroffset,ref->alternate_path,
+ -1, STR_UNICODE | STR_TERMINATE);
+ SSVAL(pdata,offset+16,uni_curroffset-offset);
+ /* copy 0x10 bytes of 00's in the ServiceSite GUID */
+ memset(pdata+offset+18,'\0',16);
+
+ uni_curroffset += unilen;
+ offset += VERSION3_REFERRAL_SIZE;
+ }
+ return reply_size;
+}
+
+/******************************************************************
+ * Set up the Dfs referral for the dfs pathname
+ ******************************************************************/
+
+int setup_dfs_referral(connection_struct *orig_conn, char *pathname, int max_referral_level, char** ppdata)
+{
+ struct junction_map junction;
+ int consumedcnt;
+ BOOL self_referral = False;
+ pstring buf;
+ int reply_size = 0;
+ char *pathnamep = pathname;
+
+ ZERO_STRUCT(junction);
+
+ /* get the junction entry */
+ if (!pathnamep)
+ return -1;
+
+ /* Trim pathname sent by client so it begins with only one backslash.
+ Two backslashes confuse some dfs clients
+ */
+ while (pathnamep[0] == '\\' && pathnamep[1] == '\\')
+ pathnamep++;
+
+ pstrcpy(buf, pathnamep);
+ /* The following call can change cwd. */
+ if (!get_referred_path(buf, &junction, &consumedcnt, &self_referral)) {
+ vfs_ChDir(orig_conn,orig_conn->connectpath);
+ return -1;
+ }
+ vfs_ChDir(orig_conn,orig_conn->connectpath);
+
+ if (!self_referral) {
+ pathnamep[consumedcnt] = '\0';
+
+ if( DEBUGLVL( 3 ) ) {
+ int i=0;
+ dbgtext("setup_dfs_referral: Path %s to alternate path(s):",pathnamep);
+ for(i=0;i<junction.referral_count;i++)
+ dbgtext(" %s",junction.referral_list[i].alternate_path);
+ dbgtext(".\n");
+ }
+ }
+
+ /* create the referral depeding on version */
+ DEBUG(10,("max_referral_level :%d\n",max_referral_level));
+ if(max_referral_level<2 || max_referral_level>3)
+ max_referral_level = 2;
+
+ switch(max_referral_level) {
+ case 2:
+ reply_size = setup_ver2_dfs_referral(pathnamep, ppdata, &junction,
+ consumedcnt, self_referral);
+ SAFE_FREE(junction.referral_list);
+ break;
+ case 3:
+ reply_size = setup_ver3_dfs_referral(pathnamep, ppdata, &junction,
+ consumedcnt, self_referral);
+ SAFE_FREE(junction.referral_list);
+ break;
+ default:
+ DEBUG(0,("setup_dfs_referral: Invalid dfs referral version: %d\n", max_referral_level));
+ return -1;
+ }
+
+ DEBUG(10,("DFS Referral pdata:\n"));
+ dump_data(10,*ppdata,reply_size);
+ return reply_size;
+}
+
+/**********************************************************************
+ The following functions are called by the NETDFS RPC pipe functions
+ **********************************************************************/
+
+/**********************************************************************
+ Creates a junction structure from a Dfs pathname
+ **********************************************************************/
+BOOL create_junction(char* pathname, struct junction_map* jucn)
+{
+ struct dfs_path dp;
+
+ parse_dfs_path(pathname,&dp);
+
+ /* check if path is dfs : validate first token */
+ if (local_machine && (!strequal(local_machine,dp.hostname))) {
+
+ /* Hostname mismatch, check if one of our IP addresses */
+ if (!ismyip(*interpret_addr2(dp.hostname))) {
+ DEBUG(4,("create_junction: Invalid hostname %s in dfs path %s\n",
+ dp.hostname, pathname));
+ return False;
+ }
+ }
+
+ /* Check for a non-DFS share */
+ if(!lp_msdfs_root(lp_servicenumber(dp.servicename))) {
+ DEBUG(4,("create_junction: %s is not an msdfs root.\n",
+ dp.servicename));
+ return False;
+ }
+
+ pstrcpy(jucn->service_name,dp.servicename);
+ pstrcpy(jucn->volume_name,dp.reqpath);
+ return True;
+}
+
+/**********************************************************************
+ Forms a valid Unix pathname from the junction
+ **********************************************************************/
+
+static BOOL junction_to_local_path(struct junction_map* jucn, char* path,
+ int max_pathlen, connection_struct *conn)
+{
+ int snum;
+ pstring conn_path;
+
+ if(!path || !jucn)
+ return False;
+
+ snum = lp_servicenumber(jucn->service_name);
+ if(snum < 0)
+ return False;
+
+ safe_strcpy(path, lp_pathname(snum), max_pathlen-1);
+ safe_strcat(path, "/", max_pathlen-1);
+ safe_strcat(path, jucn->volume_name, max_pathlen-1);
+
+ pstrcpy(conn_path, lp_pathname(snum));
+ if (!create_conn_struct(conn, snum, conn_path))
+ return False;
+
+ return True;
+}
+
+BOOL create_msdfs_link(struct junction_map *jucn, BOOL exists)
+{
+ pstring path;
+ pstring msdfs_link;
+ connection_struct conns;
+ connection_struct *conn = &conns;
+ int i=0;
+ BOOL insert_comma = False;
+ BOOL ret = False;
+
+ if(!junction_to_local_path(jucn, path, sizeof(path), conn))
+ return False;
+
+ /* form the msdfs_link contents */
+ pstrcpy(msdfs_link, "msdfs:");
+ for(i=0; i<jucn->referral_count; i++) {
+ char* refpath = jucn->referral_list[i].alternate_path;
+
+ trim_char(refpath, '\\', '\\');
+ if(*refpath == '\0') {
+ if (i == 0)
+ insert_comma = False;
+ continue;
+ }
+ if (i > 0 && insert_comma)
+ pstrcat(msdfs_link, ",");
+
+ pstrcat(msdfs_link, refpath);
+ if (!insert_comma)
+ insert_comma = True;
+
+ }
+
+ DEBUG(5,("create_msdfs_link: Creating new msdfs link: %s -> %s\n", path, msdfs_link));
+
+ if(exists)
+ if(SMB_VFS_UNLINK(conn,path)!=0)
+ goto out;
+
+ if(SMB_VFS_SYMLINK(conn, msdfs_link, path) < 0) {
+ DEBUG(1,("create_msdfs_link: symlink failed %s -> %s\nError: %s\n",
+ path, msdfs_link, strerror(errno)));
+ goto out;
+ }
+
+
+ ret = True;
+
+out:
+ talloc_destroy( conn->mem_ctx );
+ return ret;
+}
+
+BOOL remove_msdfs_link(struct junction_map* jucn)
+{
+ pstring path;
+ connection_struct conns;
+ connection_struct *conn = &conns;
+ BOOL ret = False;
+
+ if( junction_to_local_path(jucn, path, sizeof(path), conn) ) {
+ if( SMB_VFS_UNLINK(conn, path) == 0 )
+ ret = True;
+
+ talloc_destroy( conn->mem_ctx );
+ }
+
+ return ret;
+}
+
+static BOOL form_junctions(int snum, struct junction_map* jucn, int* jn_count)
+{
+ int cnt = *jn_count;
+ DIR *dirp;
+ char* dname;
+ pstring connect_path;
+ char* service_name = lp_servicename(snum);
+ connection_struct conns;
+ connection_struct *conn = &conns;
+ struct referral *ref = NULL;
+ BOOL ret = False;
+
+ pstrcpy(connect_path,lp_pathname(snum));
+
+ if(*connect_path == '\0')
+ return False;
+
+ /*
+ * Fake up a connection struct for the VFS layer.
+ */
+
+ if (!create_conn_struct(conn, snum, connect_path))
+ return False;
+
+ /* form a junction for the msdfs root - convention
+ DO NOT REMOVE THIS: NT clients will not work with us
+ if this is not present
+ */
+ pstrcpy(jucn[cnt].service_name, service_name);
+ jucn[cnt].volume_name[0] = '\0';
+ jucn[cnt].referral_count = 1;
+
+ ref = jucn[cnt].referral_list
+ = (struct referral*) malloc(sizeof(struct referral));
+ if (jucn[cnt].referral_list == NULL) {
+ DEBUG(0, ("Malloc failed!\n"));
+ goto out;
+ }
+
+ ref->proximity = 0;
+ ref->ttl = REFERRAL_TTL;
+ if (*lp_msdfs_proxy(snum) != '\0') {
+ pstrcpy(ref->alternate_path, lp_msdfs_proxy(snum));
+ *jn_count = ++cnt;
+ ret = True;
+ goto out;
+ }
+
+ slprintf(ref->alternate_path, sizeof(pstring)-1,
+ "\\\\%s\\%s", local_machine, service_name);
+ cnt++;
+
+ /* Now enumerate all dfs links */
+ dirp = SMB_VFS_OPENDIR(conn, ".");
+ if(!dirp)
+ goto out;
+
+ while((dname = vfs_readdirname(conn, dirp)) != NULL) {
+ if (is_msdfs_link(conn, dname, &(jucn[cnt].referral_list),
+ &(jucn[cnt].referral_count), NULL)) {
+ pstrcpy(jucn[cnt].service_name, service_name);
+ pstrcpy(jucn[cnt].volume_name, dname);
+ cnt++;
+ }
+ }
+
+ SMB_VFS_CLOSEDIR(conn,dirp);
+ *jn_count = cnt;
+out:
+ talloc_destroy(conn->mem_ctx);
+ return ret;
+}
+
+int enum_msdfs_links(struct junction_map* jucn)
+{
+ int i=0;
+ int jn_count = 0;
+
+ if(!lp_host_msdfs())
+ return 0;
+
+ for(i=0;i < lp_numservices();i++) {
+ if(lp_msdfs_root(i))
+ form_junctions(i,jucn,&jn_count);
+ }
+ return jn_count;
+}
diff --git a/source/nmbd/.cvsignore b/source/nmbd/.cvsignore
new file mode 100644
index 00000000000..e69de29bb2d
--- /dev/null
+++ b/source/nmbd/.cvsignore
diff --git a/source/nmbd/asyncdns.c b/source/nmbd/asyncdns.c
new file mode 100644
index 00000000000..653cb97fbb0
--- /dev/null
+++ b/source/nmbd/asyncdns.c
@@ -0,0 +1,353 @@
+/*
+ Unix SMB/CIFS implementation.
+ a async DNS handler
+ 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 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"
+
+/***************************************************************************
+ Add a DNS result to the name cache.
+****************************************************************************/
+
+static struct name_record *add_dns_result(struct nmb_name *question, struct in_addr addr)
+{
+ int name_type = question->name_type;
+ unstring qname;
+
+ pull_ascii_nstring(qname, sizeof(qname), question->name);
+
+ if (!addr.s_addr) {
+ /* add the fail to WINS cache of names. give it 1 hour in the cache */
+ DEBUG(3,("add_dns_result: Negative DNS answer for %s\n", qname));
+ (void)add_name_to_subnet( wins_server_subnet, qname, name_type,
+ NB_ACTIVE, 60*60, DNSFAIL_NAME, 1, &addr );
+ return( NULL );
+ }
+
+ /* add it to our WINS cache of names. give it 2 hours in the cache */
+ DEBUG(3,("add_dns_result: DNS gave answer for %s of %s\n", qname, inet_ntoa(addr)));
+
+ return( add_name_to_subnet( wins_server_subnet, qname, name_type,
+ NB_ACTIVE, 2*60*60, DNS_NAME, 1, &addr ) );
+}
+
+#ifndef SYNC_DNS
+
+static int fd_in = -1, fd_out = -1;
+static pid_t child_pid = -1;
+static int in_dns;
+
+/* this is the structure that is passed between the parent and child */
+struct query_record {
+ struct nmb_name name;
+ struct in_addr result;
+};
+
+/* a queue of pending requests waiting to be sent to the DNS child */
+static struct packet_struct *dns_queue;
+
+/* the packet currently being processed by the dns child */
+static struct packet_struct *dns_current;
+
+
+/***************************************************************************
+ return the fd used to gather async dns replies. This is added to the select
+ loop
+ ****************************************************************************/
+
+int asyncdns_fd(void)
+{
+ return fd_in;
+}
+
+/***************************************************************************
+ handle DNS queries arriving from the parent
+ ****************************************************************************/
+static void asyncdns_process(void)
+{
+ struct query_record r;
+ unstring qname;
+
+ DEBUGLEVEL = -1;
+
+ while (1) {
+ if (read_data(fd_in, (char *)&r, sizeof(r)) != sizeof(r))
+ break;
+
+ pull_ascii_nstring( qname, sizeof(qname), r.name.name);
+ r.result.s_addr = interpret_addr(qname);
+
+ if (write_data(fd_out, (char *)&r, sizeof(r)) != sizeof(r))
+ break;
+ }
+
+ _exit(0);
+}
+
+/**************************************************************************** **
+ catch a sigterm (in the child process - the parent has a different handler
+ see nmbd.c for details).
+ We need a separate term handler here so we don't release any
+ names that our parent is going to release, or overwrite a
+ WINS db that our parent is going to write.
+ **************************************************************************** */
+
+static void sig_term(int sig)
+{
+ _exit(0);
+}
+
+/***************************************************************************
+ Called by the parent process when it receives a SIGTERM - also kills the
+ child so we don't get child async dns processes lying around, causing trouble.
+ ****************************************************************************/
+
+void kill_async_dns_child(void)
+{
+ if (child_pid > 0) {
+ kill(child_pid, SIGTERM);
+ child_pid = -1;
+ }
+}
+
+/***************************************************************************
+ create a child process to handle DNS lookups
+ ****************************************************************************/
+void start_async_dns(void)
+{
+ int fd1[2], fd2[2];
+
+ CatchChild();
+
+ if (pipe(fd1) || pipe(fd2)) {
+ DEBUG(0,("can't create asyncdns pipes\n"));
+ return;
+ }
+
+ child_pid = sys_fork();
+
+ if (child_pid) {
+ fd_in = fd1[0];
+ fd_out = fd2[1];
+ close(fd1[1]);
+ close(fd2[0]);
+ DEBUG(0,("started asyncdns process %d\n", (int)child_pid));
+ return;
+ }
+
+ fd_in = fd2[0];
+ fd_out = fd1[1];
+
+ CatchSignal(SIGUSR2, SIG_IGN);
+ CatchSignal(SIGUSR1, SIG_IGN);
+ CatchSignal(SIGHUP, SIG_IGN);
+ CatchSignal(SIGTERM, SIGNAL_CAST sig_term );
+
+ asyncdns_process();
+}
+
+
+/***************************************************************************
+check if a particular name is already being queried
+ ****************************************************************************/
+static BOOL query_current(struct query_record *r)
+{
+ return dns_current &&
+ nmb_name_equal(&r->name,
+ &dns_current->packet.nmb.question.question_name);
+}
+
+
+/***************************************************************************
+ write a query to the child process
+ ****************************************************************************/
+static BOOL write_child(struct packet_struct *p)
+{
+ struct query_record r;
+
+ r.name = p->packet.nmb.question.question_name;
+
+ return write_data(fd_out, (char *)&r, sizeof(r)) == sizeof(r);
+}
+
+/***************************************************************************
+ check the DNS queue
+ ****************************************************************************/
+void run_dns_queue(void)
+{
+ struct query_record r;
+ struct packet_struct *p, *p2;
+ struct name_record *namerec;
+ int size;
+
+ if (fd_in == -1)
+ return;
+
+ /* Allow SIGTERM to kill us. */
+ BlockSignals(False, SIGTERM);
+
+ if (!process_exists(child_pid)) {
+ close(fd_in);
+ start_async_dns();
+ }
+
+ if ((size=read_data(fd_in, (char *)&r, sizeof(r))) != sizeof(r)) {
+ if (size) {
+ DEBUG(0,("Incomplete DNS answer from child!\n"));
+ fd_in = -1;
+ }
+ BlockSignals(True, SIGTERM);
+ return;
+ }
+
+ BlockSignals(True, SIGTERM);
+
+ namerec = add_dns_result(&r.name, r.result);
+
+ if (dns_current) {
+ if (query_current(&r)) {
+ DEBUG(3,("DNS calling send_wins_name_query_response\n"));
+ in_dns = 1;
+ if(namerec == NULL)
+ send_wins_name_query_response(NAM_ERR, dns_current, NULL);
+ else
+ send_wins_name_query_response(0,dns_current,namerec);
+ in_dns = 0;
+ }
+
+ dns_current->locked = False;
+ free_packet(dns_current);
+ dns_current = NULL;
+ }
+
+ /* loop over the whole dns queue looking for entries that
+ match the result we just got */
+ for (p = dns_queue; p;) {
+ struct nmb_packet *nmb = &p->packet.nmb;
+ struct nmb_name *question = &nmb->question.question_name;
+
+ if (nmb_name_equal(question, &r.name)) {
+ DEBUG(3,("DNS calling send_wins_name_query_response\n"));
+ in_dns = 1;
+ if(namerec == NULL)
+ send_wins_name_query_response(NAM_ERR, p, NULL);
+ else
+ send_wins_name_query_response(0,p,namerec);
+ in_dns = 0;
+ p->locked = False;
+
+ if (p->prev)
+ p->prev->next = p->next;
+ else
+ dns_queue = p->next;
+ if (p->next)
+ p->next->prev = p->prev;
+ p2 = p->next;
+ free_packet(p);
+ p = p2;
+ } else {
+ p = p->next;
+ }
+ }
+
+ if (dns_queue) {
+ dns_current = dns_queue;
+ dns_queue = dns_queue->next;
+ if (dns_queue)
+ dns_queue->prev = NULL;
+ dns_current->next = NULL;
+
+ if (!write_child(dns_current)) {
+ DEBUG(3,("failed to send DNS query to child!\n"));
+ return;
+ }
+ }
+}
+
+/***************************************************************************
+queue a DNS query
+ ****************************************************************************/
+
+BOOL queue_dns_query(struct packet_struct *p,struct nmb_name *question,
+ struct name_record **n)
+{
+ if (in_dns || fd_in == -1)
+ return False;
+
+ if (!dns_current) {
+ if (!write_child(p)) {
+ DEBUG(3,("failed to send DNS query to child!\n"));
+ return False;
+ }
+ dns_current = p;
+ p->locked = True;
+ } else {
+ p->locked = True;
+ p->next = dns_queue;
+ p->prev = NULL;
+ if (p->next)
+ p->next->prev = p;
+ dns_queue = p;
+ }
+
+ DEBUG(3,("added DNS query for %s\n", nmb_namestr(question)));
+ return True;
+}
+
+#else
+
+
+/***************************************************************************
+ we use this when we can't do async DNS lookups
+ ****************************************************************************/
+
+BOOL queue_dns_query(struct packet_struct *p,struct nmb_name *question,
+ struct name_record **n)
+{
+ struct in_addr dns_ip;
+ unstring qname;
+
+ pull_ascii_nstring(qname, question->name);
+
+ DEBUG(3,("DNS search for %s - ", nmb_namestr(question)));
+
+ /* Unblock TERM signal so we can be killed in DNS lookup. */
+ BlockSignals(False, SIGTERM);
+
+ dns_ip.s_addr = interpret_addr(qname);
+
+ /* Re-block TERM signal. */
+ BlockSignals(True, SIGTERM);
+
+ *n = add_dns_result(question, dns_ip);
+ if(*n == NULL)
+ send_wins_name_query_response(NAM_ERR, p, NULL);
+ else
+ send_wins_name_query_response(0, p, *n);
+ return False;
+}
+
+/***************************************************************************
+ With sync dns there is no child to kill on SIGTERM.
+ ****************************************************************************/
+
+void kill_async_dns_child(void)
+{
+ return;
+}
+#endif
diff --git a/source/nmbd/nmbd.c b/source/nmbd/nmbd.c
new file mode 100644
index 00000000000..880de7f91bf
--- /dev/null
+++ b/source/nmbd/nmbd.c
@@ -0,0 +1,771 @@
+/*
+ Unix SMB/CIFS implementation.
+ NBT netbios routines and daemon - version 2
+ Copyright (C) Andrew Tridgell 1994-1998
+ Copyright (C) Jeremy Allison 1997-2002
+ Copyright (C) Jelmer Vernooij 2002,2003 (Conversion to popt)
+
+ This program is free software; you can 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"
+
+int ClientNMB = -1;
+int ClientDGRAM = -1;
+int global_nmb_port = -1;
+
+extern BOOL global_in_nmbd;
+
+/* are we running as a daemon ? */
+static BOOL is_daemon;
+
+/* fork or run in foreground ? */
+static BOOL Fork = True;
+
+/* log to standard output ? */
+static BOOL log_stdout;
+
+/* have we found LanMan clients yet? */
+BOOL found_lm_clients = False;
+
+/* what server type are we currently */
+
+time_t StartupTime = 0;
+
+/**************************************************************************** **
+ Handle a SIGTERM in band.
+ **************************************************************************** */
+
+static void terminate(void)
+{
+ DEBUG(0,("Got SIGTERM: going down...\n"));
+
+ /* Write out wins.dat file if samba is a WINS server */
+ wins_write_database(False);
+
+ /* Remove all SELF registered names from WINS */
+ release_wins_names();
+
+ /* Announce all server entries as 0 time-to-live, 0 type. */
+ announce_my_servers_removed();
+
+ /* If there was an async dns child - kill it. */
+ kill_async_dns_child();
+
+ exit(0);
+}
+
+/**************************************************************************** **
+ Handle a SHUTDOWN message from smbcontrol.
+ **************************************************************************** */
+
+static void nmbd_terminate(int msg_type, pid_t src, void *buf, size_t len)
+{
+ terminate();
+}
+
+/**************************************************************************** **
+ Catch a SIGTERM signal.
+ **************************************************************************** */
+
+static SIG_ATOMIC_T got_sig_term;
+
+static void sig_term(int sig)
+{
+ got_sig_term = 1;
+ sys_select_signal();
+}
+
+/**************************************************************************** **
+ Catch a SIGHUP signal.
+ **************************************************************************** */
+
+static SIG_ATOMIC_T reload_after_sighup;
+
+static void sig_hup(int sig)
+{
+ reload_after_sighup = 1;
+ sys_select_signal();
+}
+
+#if DUMP_CORE
+/**************************************************************************** **
+ Prepare to dump a core file - carefully!
+ **************************************************************************** */
+
+static BOOL dump_core(void)
+{
+ char *p;
+ pstring dname;
+ pstrcpy( dname, lp_logfile() );
+ if ((p=strrchr_m(dname,'/')))
+ *p=0;
+ pstrcat( dname, "/corefiles" );
+ mkdir( dname, 0700 );
+ sys_chown( dname, getuid(), getgid() );
+ chmod( dname, 0700 );
+ if ( chdir(dname) )
+ return( False );
+ umask( ~(0700) );
+
+#ifdef HAVE_GETRLIMIT
+#ifdef RLIMIT_CORE
+ {
+ struct rlimit rlp;
+ getrlimit( RLIMIT_CORE, &rlp );
+ rlp.rlim_cur = MAX( 4*1024*1024, rlp.rlim_cur );
+ setrlimit( RLIMIT_CORE, &rlp );
+ getrlimit( RLIMIT_CORE, &rlp );
+ DEBUG( 3, ( "Core limits now %d %d\n", (int)rlp.rlim_cur, (int)rlp.rlim_max ) );
+ }
+#endif
+#endif
+
+
+ DEBUG(0,("Dumping core in %s\n",dname));
+ abort();
+ return( True );
+}
+#endif
+
+/**************************************************************************** **
+ Possibly continue after a fault.
+ **************************************************************************** */
+
+static void fault_continue(void)
+{
+#if DUMP_CORE
+ dump_core();
+#endif
+}
+
+/**************************************************************************** **
+ Expire old names from the namelist and server list.
+ **************************************************************************** */
+
+static void expire_names_and_servers(time_t t)
+{
+ static time_t lastrun = 0;
+
+ if ( !lastrun )
+ lastrun = t;
+ if ( t < (lastrun + 5) )
+ return;
+ lastrun = t;
+
+ /*
+ * Expire any timed out names on all the broadcast
+ * subnets and those registered with the WINS server.
+ * (nmbd_namelistdb.c)
+ */
+
+ expire_names(t);
+
+ /*
+ * Go through all the broadcast subnets and for each
+ * workgroup known on that subnet remove any expired
+ * server names. If a workgroup has an empty serverlist
+ * and has itself timed out then remove the workgroup.
+ * (nmbd_workgroupdb.c)
+ */
+
+ expire_workgroups_and_servers(t);
+}
+
+/************************************************************************** **
+ Reload the list of network interfaces.
+ ************************************************************************** */
+
+static BOOL reload_interfaces(time_t t)
+{
+ static time_t lastt;
+ int n;
+ struct subnet_record *subrec;
+ extern BOOL rescan_listen_set;
+ extern struct in_addr loopback_ip;
+
+ if (t && ((t - lastt) < NMBD_INTERFACES_RELOAD)) return False;
+ lastt = t;
+
+ if (!interfaces_changed()) return False;
+
+ /* the list of probed interfaces has changed, we may need to add/remove
+ some subnets */
+ load_interfaces();
+
+ /* find any interfaces that need adding */
+ for (n=iface_count() - 1; n >= 0; n--) {
+ struct interface *iface = get_interface(n);
+
+ /*
+ * We don't want to add a loopback interface, in case
+ * someone has added 127.0.0.1 for smbd, nmbd needs to
+ * ignore it here. JRA.
+ */
+
+ if (ip_equal(iface->ip, loopback_ip)) {
+ DEBUG(2,("reload_interfaces: Ignoring loopback interface %s\n", inet_ntoa(iface->ip)));
+ continue;
+ }
+
+ for (subrec=subnetlist; subrec; subrec=subrec->next) {
+ if (ip_equal(iface->ip, subrec->myip) &&
+ ip_equal(iface->nmask, subrec->mask_ip)) break;
+ }
+
+ if (!subrec) {
+ /* it wasn't found! add it */
+ DEBUG(2,("Found new interface %s\n",
+ inet_ntoa(iface->ip)));
+ subrec = make_normal_subnet(iface);
+ if (subrec)
+ register_my_workgroup_one_subnet(subrec);
+ }
+ }
+
+ /* find any interfaces that need deleting */
+ for (subrec=subnetlist; subrec; subrec=subrec->next) {
+ for (n=iface_count() - 1; n >= 0; n--) {
+ struct interface *iface = get_interface(n);
+ if (ip_equal(iface->ip, subrec->myip) &&
+ ip_equal(iface->nmask, subrec->mask_ip)) break;
+ }
+ if (n == -1) {
+ /* oops, an interface has disapeared. This is
+ tricky, we don't dare actually free the
+ interface as it could be being used, so
+ instead we just wear the memory leak and
+ remove it from the list of interfaces without
+ freeing it */
+ DEBUG(2,("Deleting dead interface %s\n",
+ inet_ntoa(subrec->myip)));
+ close_subnet(subrec);
+ }
+ }
+
+ rescan_listen_set = True;
+
+ /* We need to shutdown if there are no subnets... */
+ if (FIRST_SUBNET == NULL) {
+ DEBUG(0,("reload_interfaces: No subnets to listen to. Shutting down...\n"));
+ return True;
+ }
+ return False;
+}
+
+/**************************************************************************** **
+ Reload the services file.
+ **************************************************************************** */
+
+static BOOL reload_nmbd_services(BOOL test)
+{
+ BOOL ret;
+
+ set_remote_machine_name("nmbd", False);
+
+ if ( lp_loaded() ) {
+ pstring fname;
+ pstrcpy( fname,lp_configfile());
+ if (file_exist(fname,NULL) && !strcsequal(fname,dyn_CONFIGFILE)) {
+ pstrcpy(dyn_CONFIGFILE,fname);
+ test = False;
+ }
+ }
+
+ if ( test && !lp_file_list_changed() )
+ return(True);
+
+ ret = lp_load( dyn_CONFIGFILE, True , False, False);
+
+ /* perhaps the config filename is now set */
+ if ( !test ) {
+ DEBUG( 3, ( "services not loaded\n" ) );
+ reload_nmbd_services( True );
+ }
+
+ return(ret);
+}
+
+/**************************************************************************** **
+ * React on 'smbcontrol nmbd reload-config' in the same way as to SIGHUP
+ * We use buf here to return BOOL result to process() when reload_interfaces()
+ * detects that there are no subnets.
+ **************************************************************************** */
+
+static void msg_reload_nmbd_services(int msg_type, pid_t src, void *buf, size_t len)
+{
+ write_browse_list( 0, True );
+ dump_all_namelists();
+ reload_nmbd_services( True );
+ reopen_logs();
+
+ if(buf) {
+ /* We were called from process() */
+ /* If reload_interfaces() returned True */
+ /* we need to shutdown if there are no subnets... */
+ /* pass this info back to process() */
+ *((BOOL*)buf) = reload_interfaces(0);
+ }
+}
+
+
+/**************************************************************************** **
+ The main select loop.
+ **************************************************************************** */
+
+static void process(void)
+{
+ BOOL run_election;
+ BOOL no_subnets;
+
+ while( True ) {
+ time_t t = time(NULL);
+
+ /* Check for internal messages */
+
+ message_dispatch();
+
+ /*
+ * Check all broadcast subnets to see if
+ * we need to run an election on any of them.
+ * (nmbd_elections.c)
+ */
+
+ run_election = check_elections();
+
+ /*
+ * Read incoming UDP packets.
+ * (nmbd_packets.c)
+ */
+
+ if(listen_for_packets(run_election))
+ return;
+
+ /*
+ * Handle termination inband.
+ */
+
+ if (got_sig_term) {
+ got_sig_term = 0;
+ terminate();
+ }
+
+ /*
+ * Process all incoming packets
+ * read above. This calls the success and
+ * failure functions registered when response
+ * packets arrrive, and also deals with request
+ * packets from other sources.
+ * (nmbd_packets.c)
+ */
+
+ run_packet_queue();
+
+ /*
+ * Run any elections - initiate becoming
+ * a local master browser if we have won.
+ * (nmbd_elections.c)
+ */
+
+ run_elections(t);
+
+ /*
+ * Send out any broadcast announcements
+ * of our server names. This also announces
+ * the workgroup name if we are a local
+ * master browser.
+ * (nmbd_sendannounce.c)
+ */
+
+ announce_my_server_names(t);
+
+ /*
+ * Send out any LanMan broadcast announcements
+ * of our server names.
+ * (nmbd_sendannounce.c)
+ */
+
+ announce_my_lm_server_names(t);
+
+ /*
+ * If we are a local master browser, periodically
+ * announce ourselves to the domain master browser.
+ * This also deals with syncronising the domain master
+ * browser server lists with ourselves as a local
+ * master browser.
+ * (nmbd_sendannounce.c)
+ */
+
+ announce_myself_to_domain_master_browser(t);
+
+ /*
+ * Fullfill any remote announce requests.
+ * (nmbd_sendannounce.c)
+ */
+
+ announce_remote(t);
+
+ /*
+ * Fullfill any remote browse sync announce requests.
+ * (nmbd_sendannounce.c)
+ */
+
+ browse_sync_remote(t);
+
+ /*
+ * Scan the broadcast subnets, and WINS client
+ * namelists and refresh any that need refreshing.
+ * (nmbd_mynames.c)
+ */
+
+ refresh_my_names(t);
+
+ /*
+ * Scan the subnet namelists and server lists and
+ * expire thos that have timed out.
+ * (nmbd.c)
+ */
+
+ expire_names_and_servers(t);
+
+ /*
+ * Write out a snapshot of our current browse list into
+ * the browse.dat file. This is used by smbd to service
+ * incoming NetServerEnum calls - used to synchronise
+ * browse lists over subnets.
+ * (nmbd_serverlistdb.c)
+ */
+
+ write_browse_list(t, False);
+
+ /*
+ * If we are a domain master browser, we have a list of
+ * local master browsers we should synchronise browse
+ * lists with (these are added by an incoming local
+ * master browser announcement packet). Expire any of
+ * these that are no longer current, and pull the server
+ * lists from each of these known local master browsers.
+ * (nmbd_browsesync.c)
+ */
+
+ dmb_expire_and_sync_browser_lists(t);
+
+ /*
+ * Check that there is a local master browser for our
+ * workgroup for all our broadcast subnets. If one
+ * is not found, start an election (which we ourselves
+ * may or may not participate in, depending on the
+ * setting of the 'local master' parameter.
+ * (nmbd_elections.c)
+ */
+
+ check_master_browser_exists(t);
+
+ /*
+ * If we are configured as a logon server, attempt to
+ * register the special NetBIOS names to become such
+ * (WORKGROUP<1c> name) on all broadcast subnets and
+ * with the WINS server (if used). If we are configured
+ * to become a domain master browser, attempt to register
+ * the special NetBIOS name (WORKGROUP<1b> name) to
+ * become such.
+ * (nmbd_become_dmb.c)
+ */
+
+ add_domain_names(t);
+
+ /*
+ * If we are a WINS server, do any timer dependent
+ * processing required.
+ * (nmbd_winsserver.c)
+ */
+
+ initiate_wins_processing(t);
+
+ /*
+ * If we are a domain master browser, attempt to contact the
+ * WINS server to get a list of all known WORKGROUPS/DOMAINS.
+ * This will only work to a Samba WINS server.
+ * (nmbd_browsesync.c)
+ */
+
+ if (lp_enhanced_browsing())
+ collect_all_workgroup_names_from_wins_server(t);
+
+ /*
+ * Go through the response record queue and time out or re-transmit
+ * and expired entries.
+ * (nmbd_packets.c)
+ */
+
+ retransmit_or_expire_response_records(t);
+
+ /*
+ * check to see if any remote browse sync child processes have completed
+ */
+
+ sync_check_completion();
+
+ /*
+ * regularly sync with any other DMBs we know about
+ */
+
+ if (lp_enhanced_browsing())
+ sync_all_dmbs(t);
+
+ /*
+ * clear the unexpected packet queue
+ */
+
+ clear_unexpected(t);
+
+ /*
+ * Reload the services file if we got a sighup.
+ */
+
+ if(reload_after_sighup) {
+ DEBUG( 0, ( "Got SIGHUP dumping debug info.\n" ) );
+ msg_reload_nmbd_services(MSG_SMB_CONF_UPDATED, (pid_t) 0, (void*) &no_subnets, 0);
+ if(no_subnets)
+ return;
+ reload_after_sighup = 0;
+ }
+
+ /* check for new network interfaces */
+
+ if(reload_interfaces(t))
+ return;
+
+ /* free up temp memory */
+ lp_talloc_free();
+ }
+}
+
+/**************************************************************************** **
+ Open the socket communication.
+ **************************************************************************** */
+
+static BOOL open_sockets(BOOL isdaemon, int port)
+{
+ /*
+ * The sockets opened here will be used to receive broadcast
+ * packets *only*. Interface specific sockets are opened in
+ * make_subnet() in namedbsubnet.c. Thus we bind to the
+ * address "0.0.0.0". The parameter 'socket address' is
+ * now deprecated.
+ */
+
+ if ( isdaemon )
+ ClientNMB = open_socket_in(SOCK_DGRAM, port,
+ 0, interpret_addr(lp_socket_address()),
+ True);
+ else
+ ClientNMB = 0;
+
+ ClientDGRAM = open_socket_in(SOCK_DGRAM,DGRAM_PORT,3,0,True);
+
+ if ( ClientNMB == -1 )
+ return( False );
+
+ /* we are never interested in SIGPIPE */
+ BlockSignals(True,SIGPIPE);
+
+ set_socket_options( ClientNMB, "SO_BROADCAST" );
+ set_socket_options( ClientDGRAM, "SO_BROADCAST" );
+
+ DEBUG( 3, ( "open_sockets: Broadcast sockets opened.\n" ) );
+ return( True );
+}
+
+/**************************************************************************** **
+ main program
+ **************************************************************************** */
+ int main(int argc, const char *argv[])
+{
+ pstring logfile;
+ static BOOL opt_interactive;
+ poptContext pc;
+ int opt;
+ struct poptOption long_options[] = {
+ POPT_AUTOHELP
+ {"daemon", 'D', POPT_ARG_VAL, &is_daemon, True, "Become a daemon(default)" },
+ {"interactive", 'i', POPT_ARG_VAL, &opt_interactive, True, "Run interactive (not a daemon)" },
+ {"foreground", 'F', POPT_ARG_VAL, &Fork, False, "Run daemon in foreground (for daemontools & etc)" },
+ {"log-stdout", 'S', POPT_ARG_VAL, &log_stdout, True, "Log to stdout" },
+ {"hosts", 'H', POPT_ARG_STRING, dyn_LMHOSTSFILE, 'H', "Load a netbios hosts file"},
+ {"port", 'p', POPT_ARG_INT, &global_nmb_port, NMB_PORT, "Listen on the specified port" },
+ POPT_COMMON_SAMBA
+ { NULL }
+ };
+
+ global_nmb_port = NMB_PORT;
+
+ pc = poptGetContext("nmbd", argc, argv, long_options, 0);
+ while ((opt = poptGetNextOpt(pc)) != -1) ;
+ poptFreeContext(pc);
+
+ global_in_nmbd = True;
+
+ StartupTime = time(NULL);
+
+ sys_srandom(time(NULL) ^ sys_getpid());
+
+ slprintf(logfile, sizeof(logfile)-1, "%s/log.nmbd", dyn_LOGFILEBASE);
+ lp_set_logfile(logfile);
+
+ fault_setup((void (*)(void *))fault_continue );
+
+ /* POSIX demands that signals are inherited. If the invoking process has
+ * these signals masked, we will have problems, as we won't receive them. */
+ BlockSignals(False, SIGHUP);
+ BlockSignals(False, SIGUSR1);
+ BlockSignals(False, SIGTERM);
+
+ CatchSignal( SIGHUP, SIGNAL_CAST sig_hup );
+ CatchSignal( SIGTERM, SIGNAL_CAST sig_term );
+
+#if defined(SIGFPE)
+ /* we are never interested in SIGFPE */
+ BlockSignals(True,SIGFPE);
+#endif
+
+ /* We no longer use USR2... */
+#if defined(SIGUSR2)
+ BlockSignals(True, SIGUSR2);
+#endif
+
+ if ( opt_interactive ) {
+ Fork = False;
+ log_stdout = True;
+ }
+
+ if ( log_stdout && Fork ) {
+ DEBUG(0,("ERROR: Can't log to stdout (-S) unless daemon is in foreground (-F) or interactive (-i)\n"));
+ exit(1);
+ }
+
+ setup_logging( argv[0], log_stdout );
+
+ reopen_logs();
+
+ DEBUG( 0, ( "Netbios nameserver version %s started.\n", SAMBA_VERSION_STRING) );
+ DEBUGADD( 0, ( "Copyright Andrew Tridgell and the Samba Team 1994-2004\n" ) );
+
+ if ( !reload_nmbd_services(False) )
+ return(-1);
+
+ if(!init_names())
+ return -1;
+
+ reload_nmbd_services( True );
+
+ if (strequal(lp_workgroup(),"*")) {
+ DEBUG(0,("ERROR: a workgroup name of * is no longer supported\n"));
+ exit(1);
+ }
+
+ set_samba_nb_type();
+
+ if (!is_daemon && !is_a_socket(0)) {
+ DEBUG(0,("standard input is not a socket, assuming -D option\n"));
+ is_daemon = True;
+ }
+
+ if (is_daemon && !opt_interactive) {
+ DEBUG( 2, ( "Becoming a daemon.\n" ) );
+ become_daemon(Fork);
+ }
+
+#if HAVE_SETPGID
+ /*
+ * If we're interactive we want to set our own process group for
+ * signal management.
+ */
+ if (opt_interactive)
+ setpgid( (pid_t)0, (pid_t)0 );
+#endif
+
+#ifndef SYNC_DNS
+ /* Setup the async dns. We do it here so it doesn't have all the other
+ stuff initialised and thus chewing memory and sockets */
+ if(lp_we_are_a_wins_server() && lp_dns_proxy()) {
+ start_async_dns();
+ }
+#endif
+
+ if (!directory_exist(lp_lockdir(), NULL)) {
+ mkdir(lp_lockdir(), 0755);
+ }
+
+ pidfile_create("nmbd");
+ message_init();
+ message_register(MSG_FORCE_ELECTION, nmbd_message_election);
+ message_register(MSG_WINS_NEW_ENTRY, nmbd_wins_new_entry);
+ message_register(MSG_SHUTDOWN, nmbd_terminate);
+ message_register(MSG_SMB_CONF_UPDATED, msg_reload_nmbd_services);
+
+ DEBUG( 3, ( "Opening sockets %d\n", global_nmb_port ) );
+
+ if ( !open_sockets( is_daemon, global_nmb_port ) ) {
+ kill_async_dns_child();
+ return 1;
+ }
+
+ /* Determine all the IP addresses we have. */
+ load_interfaces();
+
+ /* Create an nmbd subnet record for each of the above. */
+ if( False == create_subnets() ) {
+ DEBUG(0,("ERROR: Failed when creating subnet lists. Exiting.\n"));
+ kill_async_dns_child();
+ exit(1);
+ }
+
+ /* Load in any static local names. */
+ load_lmhosts_file(dyn_LMHOSTSFILE);
+ DEBUG(3,("Loaded hosts file %s\n", dyn_LMHOSTSFILE));
+
+ /* If we are acting as a WINS server, initialise data structures. */
+ if( !initialise_wins() ) {
+ DEBUG( 0, ( "nmbd: Failed when initialising WINS server.\n" ) );
+ kill_async_dns_child();
+ exit(1);
+ }
+
+ /*
+ * Register nmbd primary workgroup and nmbd names on all
+ * the broadcast subnets, and on the WINS server (if specified).
+ * Also initiate the startup of our primary workgroup (start
+ * elections if we are setup as being able to be a local
+ * master browser.
+ */
+
+ if( False == register_my_workgroup_and_names() ) {
+ DEBUG(0,("ERROR: Failed when creating my my workgroup. Exiting.\n"));
+ kill_async_dns_child();
+ exit(1);
+ }
+
+ /* We can only take signals in the select. */
+ BlockSignals( True, SIGTERM );
+
+ process();
+
+ if (dbf)
+ x_fclose(dbf);
+ kill_async_dns_child();
+ return(0);
+}
diff --git a/source/nmbd/nmbd_become_dmb.c b/source/nmbd/nmbd_become_dmb.c
new file mode 100644
index 00000000000..fb1fb33a818
--- /dev/null
+++ b/source/nmbd/nmbd_become_dmb.c
@@ -0,0 +1,386 @@
+/*
+ Unix SMB/CIFS implementation.
+ NBT netbios routines and daemon - version 2
+ Copyright (C) Andrew Tridgell 1994-1998
+ Copyright (C) Luke Kenneth Casson Leighton 1994-1998
+ Copyright (C) Jeremy Allison 1994-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 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"
+
+extern struct in_addr allones_ip;
+
+extern uint16 samba_nb_type; /* Samba's NetBIOS type. */
+
+static void become_domain_master_browser_bcast(const char *);
+
+/****************************************************************************
+ Fail to become a Domain Master Browser on a subnet.
+ ****************************************************************************/
+
+static void become_domain_master_fail(struct subnet_record *subrec,
+ struct response_record *rrec,
+ struct nmb_name *fail_name)
+{
+ unstring failname;
+ struct work_record *work;
+ struct server_record *servrec;
+
+ pull_ascii_nstring(failname, sizeof(failname), fail_name->name);
+ work = find_workgroup_on_subnet(subrec, failname);
+ if(!work) {
+ DEBUG(0,("become_domain_master_fail: Error - cannot find \
+workgroup %s on subnet %s\n", failname, subrec->subnet_name));
+ return;
+ }
+
+ /* Set the state back to DOMAIN_NONE. */
+ work->dom_state = DOMAIN_NONE;
+
+ if((servrec = find_server_in_workgroup( work, global_myname())) == NULL) {
+ DEBUG(0,("become_domain_master_fail: Error - cannot find server %s \
+in workgroup %s on subnet %s\n",
+ global_myname(), work->work_group, subrec->subnet_name));
+ return;
+ }
+
+ /* Update our server status. */
+ servrec->serv.type &= ~SV_TYPE_DOMAIN_MASTER;
+
+ /* Tell the namelist writer to write out a change. */
+ subrec->work_changed = True;
+
+ DEBUG(0,("become_domain_master_fail: Failed to become a domain master browser for \
+workgroup %s on subnet %s. Couldn't register name %s.\n",
+ work->work_group, subrec->subnet_name, nmb_namestr(fail_name)));
+}
+
+/****************************************************************************
+ Become a Domain Master Browser on a subnet.
+ ****************************************************************************/
+
+static void become_domain_master_stage2(struct subnet_record *subrec,
+ struct userdata_struct *userdata,
+ struct nmb_name *registered_name,
+ uint16 nb_flags,
+ int ttl, struct in_addr registered_ip)
+{
+ unstring regname;
+ struct work_record *work;
+ struct server_record *servrec;
+
+ pull_ascii_nstring(regname, sizeof(regname), registered_name->name);
+ work = find_workgroup_on_subnet( subrec, regname);
+
+ if(!work) {
+ DEBUG(0,("become_domain_master_stage2: Error - cannot find \
+workgroup %s on subnet %s\n", regname, subrec->subnet_name));
+ return;
+ }
+
+ if((servrec = find_server_in_workgroup( work, global_myname())) == NULL) {
+ DEBUG(0,("become_domain_master_stage2: Error - cannot find server %s \
+in workgroup %s on subnet %s\n",
+ global_myname(), regname, subrec->subnet_name));
+ work->dom_state = DOMAIN_NONE;
+ return;
+ }
+
+ /* Set the state in the workgroup structure. */
+ work->dom_state = DOMAIN_MST; /* Become domain master. */
+
+ /* Update our server status. */
+ servrec->serv.type |= (SV_TYPE_NT|SV_TYPE_DOMAIN_MASTER);
+
+ /* Tell the namelist writer to write out a change. */
+ subrec->work_changed = True;
+
+ if( DEBUGLVL( 0 ) ) {
+ dbgtext( "*****\n\nSamba server %s ", global_myname() );
+ dbgtext( "is now a domain master browser for " );
+ dbgtext( "workgroup %s ", work->work_group );
+ dbgtext( "on subnet %s\n\n*****\n", subrec->subnet_name );
+ }
+
+ if( subrec == unicast_subnet ) {
+ struct nmb_name nmbname;
+ struct in_addr my_first_ip;
+
+ /* Put our name and first IP address into the
+ workgroup struct as domain master browser. This
+ will stop us syncing with ourself if we are also
+ a local master browser. */
+
+ make_nmb_name(&nmbname, global_myname(), 0x20);
+
+ work->dmb_name = nmbname;
+ /* Pick the first interface ip address as the domain master browser ip. */
+ my_first_ip = *iface_n_ip(0);
+
+ putip((char *)&work->dmb_addr, &my_first_ip);
+
+ /* We successfully registered by unicast with the
+ WINS server. We now expect to become the domain
+ master on the local subnets. If this fails, it's
+ probably a 1.9.16p2 to 1.9.16p11 server's fault.
+
+ This is a configuration issue that should be addressed
+ by the network administrator - you shouldn't have
+ several machines configured as a domain master browser
+ for the same WINS scope (except if they are 1.9.17 or
+ greater, and you know what you're doing.
+
+ see docs/DOMAIN.txt.
+
+ */
+ become_domain_master_browser_bcast(work->work_group);
+ } else {
+ /*
+ * Now we are a domain master on a broadcast subnet, we need to add
+ * the WORKGROUP<1b> name to the unicast subnet so that we can answer
+ * unicast requests sent to this name. This bug wasn't found for a while
+ * as it is strange to have a DMB without using WINS. JRA.
+ */
+ insert_permanent_name_into_unicast(subrec, registered_name, nb_flags);
+ }
+}
+
+/****************************************************************************
+ Start the name registration process when becoming a Domain Master Browser
+ on a subnet.
+****************************************************************************/
+
+static void become_domain_master_stage1(struct subnet_record *subrec, const char *wg_name)
+{
+ struct work_record *work;
+
+ DEBUG(2,("become_domain_master_stage1: Becoming domain master browser for \
+workgroup %s on subnet %s\n", wg_name, subrec->subnet_name));
+
+ /* First, find the workgroup on the subnet. */
+ if((work = find_workgroup_on_subnet( subrec, wg_name )) == NULL) {
+ DEBUG(0,("become_domain_master_stage1: Error - unable to find workgroup %s on subnet %s.\n",
+ wg_name, subrec->subnet_name));
+ return;
+ }
+
+ DEBUG(3,("become_domain_master_stage1: go to first stage: register <1b> name\n"));
+ work->dom_state = DOMAIN_WAIT;
+
+ /* WORKGROUP<1b> is the domain master browser name. */
+ register_name(subrec, work->work_group,0x1b,samba_nb_type,
+ become_domain_master_stage2,
+ become_domain_master_fail, NULL);
+}
+
+/****************************************************************************
+ Function called when a query for a WORKGROUP<1b> name succeeds.
+ This is normally a fail condition as it means there is already
+ a domain master browser for a workgroup and we were trying to
+ become one.
+****************************************************************************/
+
+static void become_domain_master_query_success(struct subnet_record *subrec,
+ struct userdata_struct *userdata,
+ struct nmb_name *nmbname, struct in_addr ip,
+ struct res_rec *rrec)
+{
+ unstring name;
+ pull_ascii_nstring(name, sizeof(name), nmbname->name);
+
+ /* If the given ip is not ours, then we can't become a domain
+ controler as the name is already registered.
+ */
+
+ /* BUG note. Samba 1.9.16p11 servers seem to return the broadcast
+ address or zero ip for this query. Pretend this is ok. */
+
+ if(ismyip(ip) || ip_equal(allones_ip, ip) || is_zero_ip(ip)) {
+ if( DEBUGLVL( 3 ) ) {
+ dbgtext( "become_domain_master_query_success():\n" );
+ dbgtext( "Our address (%s) ", inet_ntoa(ip) );
+ dbgtext( "returned in query for name %s ", nmb_namestr(nmbname) );
+ dbgtext( "(domain master browser name) " );
+ dbgtext( "on subnet %s.\n", subrec->subnet_name );
+ dbgtext( "Continuing with domain master code.\n" );
+ }
+
+ become_domain_master_stage1(subrec, name);
+ } else {
+ if( DEBUGLVL( 0 ) ) {
+ dbgtext( "become_domain_master_query_success:\n" );
+ dbgtext( "There is already a domain master browser at " );
+ dbgtext( "IP %s for workgroup %s ", inet_ntoa(ip), name );
+ dbgtext( "registered on subnet %s.\n", subrec->subnet_name );
+ }
+ }
+}
+
+/****************************************************************************
+ Function called when a query for a WORKGROUP<1b> name fails.
+ This is normally a success condition as it then allows us to register
+ our own Domain Master Browser name.
+ ****************************************************************************/
+
+static void become_domain_master_query_fail(struct subnet_record *subrec,
+ struct response_record *rrec,
+ struct nmb_name *question_name, int fail_code)
+{
+ unstring name;
+
+ /* If the query was unicast, and the error is not NAM_ERR (name didn't exist),
+ then this is a failure. Otherwise, not finding the name is what we want. */
+
+ if((subrec == unicast_subnet) && (fail_code != NAM_ERR)) {
+ DEBUG(0,("become_domain_master_query_fail: Error %d returned when \
+querying WINS server for name %s.\n",
+ fail_code, nmb_namestr(question_name)));
+ return;
+ }
+
+ /* Otherwise - not having the name allows us to register it. */
+ pull_ascii_nstring(name, sizeof(name), question_name->name);
+ become_domain_master_stage1(subrec, name);
+}
+
+/****************************************************************************
+ Attempt to become a domain master browser on all broadcast subnets.
+ ****************************************************************************/
+
+static void become_domain_master_browser_bcast(const char *workgroup_name)
+{
+ struct subnet_record *subrec;
+
+ for (subrec = FIRST_SUBNET; subrec; subrec = NEXT_SUBNET_EXCLUDING_UNICAST(subrec)) {
+ struct work_record *work = find_workgroup_on_subnet(subrec, workgroup_name);
+
+ if (work && (work->dom_state == DOMAIN_NONE)) {
+ struct nmb_name nmbname;
+ make_nmb_name(&nmbname,workgroup_name,0x1b);
+
+ /*
+ * Check for our name on the given broadcast subnet first, only initiate
+ * further processing if we cannot find it.
+ */
+
+ if (find_name_on_subnet(subrec, &nmbname, FIND_SELF_NAME) == NULL) {
+ if( DEBUGLVL( 0 ) ) {
+ dbgtext( "become_domain_master_browser_bcast:\n" );
+ dbgtext( "Attempting to become domain master browser on " );
+ dbgtext( "workgroup %s on subnet %s\n",
+ workgroup_name, subrec->subnet_name );
+ }
+
+ /* Send out a query to establish whether there's a
+ domain controller on the local subnet. If not,
+ we can become a domain controller.
+ */
+
+ DEBUG(0,("become_domain_master_browser_bcast: querying subnet %s \
+for domain master browser on workgroup %s\n", subrec->subnet_name, workgroup_name));
+
+ query_name(subrec, workgroup_name, nmbname.name_type,
+ become_domain_master_query_success,
+ become_domain_master_query_fail,
+ NULL);
+ }
+ }
+ }
+}
+
+/****************************************************************************
+ Attempt to become a domain master browser by registering with WINS.
+ ****************************************************************************/
+
+static void become_domain_master_browser_wins(const char *workgroup_name)
+{
+ struct work_record *work;
+
+ work = find_workgroup_on_subnet(unicast_subnet, workgroup_name);
+
+ if (work && (work->dom_state == DOMAIN_NONE)) {
+ struct nmb_name nmbname;
+
+ make_nmb_name(&nmbname,workgroup_name,0x1b);
+
+ /*
+ * Check for our name on the unicast subnet first, only initiate
+ * further processing if we cannot find it.
+ */
+
+ if (find_name_on_subnet(unicast_subnet, &nmbname, FIND_SELF_NAME) == NULL) {
+ if( DEBUGLVL( 0 ) ) {
+ dbgtext( "become_domain_master_browser_wins:\n" );
+ dbgtext( "Attempting to become domain master browser " );
+ dbgtext( "on workgroup %s, subnet %s.\n",
+ workgroup_name, unicast_subnet->subnet_name );
+ }
+
+ /* Send out a query to establish whether there's a
+ domain master broswer registered with WINS. If not,
+ we can become a domain master browser.
+ */
+
+ DEBUG(0,("become_domain_master_browser_wins: querying WINS server from IP %s \
+for domain master browser name %s on workgroup %s\n",
+ inet_ntoa(unicast_subnet->myip), nmb_namestr(&nmbname), workgroup_name));
+
+ query_name(unicast_subnet, workgroup_name, nmbname.name_type,
+ become_domain_master_query_success,
+ become_domain_master_query_fail,
+ NULL);
+ }
+ }
+}
+
+/****************************************************************************
+ Add the domain logon server and domain master browser names
+ if we are set up to do so.
+ **************************************************************************/
+
+void add_domain_names(time_t t)
+{
+ static time_t lastrun = 0;
+
+ if ((lastrun != 0) && (t < lastrun + (CHECK_TIME_ADD_DOM_NAMES * 60)))
+ return;
+
+ lastrun = t;
+
+ /* Do the "internet group" - <1c> names. */
+ if (lp_domain_logons())
+ add_logon_names();
+
+ /* Do the domain master names. */
+ if(lp_domain_master()) {
+ if(we_are_a_wins_client()) {
+ /* We register the WORKGROUP<1b> name with the WINS
+ server first, and call add_domain_master_bcast()
+ only if this is successful.
+
+ This results in domain logon services being gracefully provided,
+ as opposed to the aggressive nature of 1.9.16p2 to 1.9.16p11.
+ 1.9.16p2 to 1.9.16p11 - due to a bug in namelogon.c,
+ cannot provide domain master / domain logon services.
+ */
+ become_domain_master_browser_wins(lp_workgroup());
+ } else {
+ become_domain_master_browser_bcast(lp_workgroup());
+ }
+ }
+}
diff --git a/source/nmbd/nmbd_become_lmb.c b/source/nmbd/nmbd_become_lmb.c
new file mode 100644
index 00000000000..c536deb6f45
--- /dev/null
+++ b/source/nmbd/nmbd_become_lmb.c
@@ -0,0 +1,594 @@
+/*
+ Unix SMB/CIFS implementation.
+ NBT netbios routines and daemon - version 2
+ Copyright (C) Andrew Tridgell 1994-1998
+ Copyright (C) Luke Kenneth Casson Leighton 1994-1998
+ Copyright (C) Jeremy Allison 1994-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 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"
+
+extern uint16 samba_nb_type; /* Samba's NetBIOS name type. */
+
+/*******************************************************************
+ Utility function to add a name to the unicast subnet, or add in
+ our IP address if it already exists.
+******************************************************************/
+
+void insert_permanent_name_into_unicast( struct subnet_record *subrec,
+ struct nmb_name *nmbname, uint16 nb_type )
+{
+ unstring name;
+ struct name_record *namerec;
+
+ if((namerec = find_name_on_subnet(unicast_subnet, nmbname, FIND_SELF_NAME)) == NULL) {
+ pull_ascii_nstring(name, sizeof(name), nmbname->name);
+ /* The name needs to be created on the unicast subnet. */
+ (void)add_name_to_subnet( unicast_subnet, name,
+ nmbname->name_type, nb_type,
+ PERMANENT_TTL, PERMANENT_NAME, 1, &subrec->myip);
+ } else {
+ /* The name already exists on the unicast subnet. Add our local
+ IP for the given broadcast subnet to the name. */
+ add_ip_to_name_record( namerec, subrec->myip);
+ }
+}
+
+/*******************************************************************
+ Utility function to remove a name from the unicast subnet.
+******************************************************************/
+
+static void remove_permanent_name_from_unicast( struct subnet_record *subrec,
+ struct nmb_name *nmbname )
+{
+ struct name_record *namerec;
+
+ if((namerec = find_name_on_subnet(unicast_subnet, nmbname, FIND_SELF_NAME)) != NULL) {
+ /* Remove this broadcast subnet IP address from the name. */
+ remove_ip_from_name_record( namerec, subrec->myip);
+ if(namerec->data.num_ips == 0)
+ remove_name_from_namelist( unicast_subnet, namerec);
+ }
+}
+
+/*******************************************************************
+ Utility function always called to set our workgroup and server
+ state back to potential browser, or none.
+******************************************************************/
+
+static void reset_workgroup_state( struct subnet_record *subrec, const char *workgroup_name,
+ BOOL force_new_election )
+{
+ struct work_record *work;
+ struct server_record *servrec;
+ struct nmb_name nmbname;
+
+ if((work = find_workgroup_on_subnet( subrec, workgroup_name)) == NULL) {
+ DEBUG(0,("reset_workgroup_state: Error - cannot find workgroup %s on \
+subnet %s.\n", workgroup_name, subrec->subnet_name ));
+ return;
+ }
+
+ if((servrec = find_server_in_workgroup( work, global_myname())) == NULL) {
+ DEBUG(0,("reset_workgroup_state: Error - cannot find server %s \
+in workgroup %s on subnet %s\n",
+ global_myname(), work->work_group, subrec->subnet_name));
+ work->mst_state = lp_local_master() ? MST_POTENTIAL : MST_NONE;
+ return;
+ }
+
+ /* Update our server status - remove any master flag and replace
+ it with the potential browser flag. */
+ servrec->serv.type &= ~SV_TYPE_MASTER_BROWSER;
+ servrec->serv.type |= (lp_local_master() ? SV_TYPE_POTENTIAL_BROWSER : 0);
+
+ /* Tell the namelist writer to write out a change. */
+ subrec->work_changed = True;
+
+ /* Reset our election flags. */
+ work->ElectionCriterion &= ~0x4;
+
+ work->mst_state = lp_local_master() ? MST_POTENTIAL : MST_NONE;
+
+ /* Forget who the local master browser was for
+ this workgroup. */
+
+ set_workgroup_local_master_browser_name( work, "");
+
+ /*
+ * Ensure the IP address of this subnet is not registered as one
+ * of the IP addresses of the WORKGROUP<1d> name on the unicast
+ * subnet. This undoes what we did below when we became a local
+ * master browser.
+ */
+
+ make_nmb_name(&nmbname, work->work_group, 0x1d);
+
+ remove_permanent_name_from_unicast( subrec, &nmbname);
+
+ if(force_new_election)
+ work->needelection = True;
+}
+
+/*******************************************************************
+ Unbecome the local master browser name release success function.
+******************************************************************/
+
+static void unbecome_local_master_success(struct subnet_record *subrec,
+ struct userdata_struct *userdata,
+ struct nmb_name *released_name,
+ struct in_addr released_ip)
+{
+ BOOL force_new_election = False;
+ unstring relname;
+
+ memcpy((char *)&force_new_election, userdata->data, sizeof(BOOL));
+
+ DEBUG(3,("unbecome_local_master_success: released name %s.\n",
+ nmb_namestr(released_name)));
+
+ /* Now reset the workgroup and server state. */
+ pull_ascii_nstring(relname, sizeof(relname), released_name->name);
+ reset_workgroup_state( subrec, relname, force_new_election );
+
+ if( DEBUGLVL( 0 ) ) {
+ dbgtext( "*****\n\n" );
+ dbgtext( "Samba name server %s ", global_myname() );
+ dbgtext( "has stopped being a local master browser " );
+ dbgtext( "for workgroup %s ", relname );
+ dbgtext( "on subnet %s\n\n*****\n", subrec->subnet_name );
+ }
+
+}
+
+/*******************************************************************
+ Unbecome the local master browser name release fail function.
+******************************************************************/
+
+static void unbecome_local_master_fail(struct subnet_record *subrec, struct response_record *rrec,
+ struct nmb_name *fail_name)
+{
+ struct name_record *namerec;
+ struct userdata_struct *userdata = rrec->userdata;
+ BOOL force_new_election = False;
+ unstring failname;
+
+ memcpy((char *)&force_new_election, userdata->data, sizeof(BOOL));
+
+ DEBUG(0,("unbecome_local_master_fail: failed to release name %s. \
+Removing from namelist anyway.\n", nmb_namestr(fail_name)));
+
+ /* Do it anyway. */
+ namerec = find_name_on_subnet(subrec, fail_name, FIND_SELF_NAME);
+ if(namerec)
+ remove_name_from_namelist(subrec, namerec);
+
+ /* Now reset the workgroup and server state. */
+ pull_ascii_nstring(failname, sizeof(failname), fail_name->name);
+ reset_workgroup_state( subrec, failname, force_new_election );
+
+ if( DEBUGLVL( 0 ) ) {
+ dbgtext( "*****\n\n" );
+ dbgtext( "Samba name server %s ", global_myname() );
+ dbgtext( "has stopped being a local master browser " );
+ dbgtext( "for workgroup %s ", failname );
+ dbgtext( "on subnet %s\n\n*****\n", subrec->subnet_name );
+ }
+}
+
+/*******************************************************************
+ Utility function to remove the WORKGROUP<1d> name.
+******************************************************************/
+
+static void release_1d_name( struct subnet_record *subrec, const char *workgroup_name,
+ BOOL force_new_election)
+{
+ struct nmb_name nmbname;
+ struct name_record *namerec;
+
+ make_nmb_name(&nmbname, workgroup_name, 0x1d);
+ if((namerec = find_name_on_subnet( subrec, &nmbname, FIND_SELF_NAME))!=NULL) {
+ struct userdata_struct *userdata;
+ size_t size = sizeof(struct userdata_struct) + sizeof(BOOL);
+
+ if((userdata = (struct userdata_struct *)malloc(size)) == NULL) {
+ DEBUG(0,("release_1d_name: malloc fail.\n"));
+ return;
+ }
+
+ userdata->copy_fn = NULL;
+ userdata->free_fn = NULL;
+ userdata->userdata_len = sizeof(BOOL);
+ memcpy((char *)userdata->data, &force_new_election, sizeof(BOOL));
+
+ release_name(subrec, namerec,
+ unbecome_local_master_success,
+ unbecome_local_master_fail,
+ userdata);
+
+ zero_free(userdata, size);
+ }
+}
+
+/*******************************************************************
+ Unbecome the local master browser MSBROWSE name release success function.
+******************************************************************/
+
+static void release_msbrowse_name_success(struct subnet_record *subrec,
+ struct userdata_struct *userdata,
+ struct nmb_name *released_name,
+ struct in_addr released_ip)
+{
+ DEBUG(4,("release_msbrowse_name_success: Released name %s on subnet %s\n.",
+ nmb_namestr(released_name), subrec->subnet_name ));
+
+ /* Remove the permanent MSBROWSE name added into the unicast subnet. */
+ remove_permanent_name_from_unicast( subrec, released_name);
+}
+
+/*******************************************************************
+ Unbecome the local master browser MSBROWSE name release fail function.
+******************************************************************/
+
+static void release_msbrowse_name_fail( struct subnet_record *subrec,
+ struct response_record *rrec,
+ struct nmb_name *fail_name)
+{
+ struct name_record *namerec;
+
+ DEBUG(4,("release_msbrowse_name_fail: Failed to release name %s on subnet %s\n.",
+ nmb_namestr(fail_name), subrec->subnet_name ));
+
+ /* Release the name anyway. */
+ namerec = find_name_on_subnet(subrec, fail_name, FIND_SELF_NAME);
+ if(namerec)
+ remove_name_from_namelist(subrec, namerec);
+
+ /* Remove the permanent MSBROWSE name added into the unicast subnet. */
+ remove_permanent_name_from_unicast( subrec, fail_name);
+}
+
+/*******************************************************************
+ Unbecome the local master browser. If force_new_election is true, restart
+ the election process after we've unbecome the local master.
+******************************************************************/
+
+void unbecome_local_master_browser(struct subnet_record *subrec, struct work_record *work,
+ BOOL force_new_election)
+{
+ struct name_record *namerec;
+ struct nmb_name nmbname;
+
+ /* Sanity check. */
+
+ DEBUG(2,("unbecome_local_master_browser: unbecoming local master for workgroup %s \
+on subnet %s\n",work->work_group, subrec->subnet_name));
+
+ if(find_server_in_workgroup( work, global_myname()) == NULL) {
+ DEBUG(0,("unbecome_local_master_browser: Error - cannot find server %s \
+in workgroup %s on subnet %s\n",
+ global_myname(), work->work_group, subrec->subnet_name));
+ work->mst_state = lp_local_master() ? MST_POTENTIAL : MST_NONE;
+ return;
+ }
+
+ /* Set the state to unbecoming. */
+ work->mst_state = MST_UNBECOMING_MASTER;
+
+ /*
+ * Release the WORKGROUP<1d> name asap to allow another machine to
+ * claim it.
+ */
+
+ release_1d_name( subrec, work->work_group, force_new_election);
+
+ /* Deregister any browser names we may have. */
+ make_nmb_name(&nmbname, MSBROWSE, 0x1);
+ if((namerec = find_name_on_subnet( subrec, &nmbname, FIND_SELF_NAME))!=NULL) {
+ release_name(subrec, namerec,
+ release_msbrowse_name_success,
+ release_msbrowse_name_fail,
+ NULL);
+ }
+
+ /*
+ * Ensure we have sent and processed these release packets
+ * before returning - we don't want to process any election
+ * packets before dealing with the 1d release.
+ */
+
+ retransmit_or_expire_response_records(time(NULL));
+}
+
+/****************************************************************************
+ Success in registering the WORKGROUP<1d> name.
+ We are now *really* a local master browser.
+ ****************************************************************************/
+
+static void become_local_master_stage2(struct subnet_record *subrec,
+ struct userdata_struct *userdata,
+ struct nmb_name *registered_name,
+ uint16 nb_flags,
+ int ttl, struct in_addr registered_ip)
+{
+ int i = 0;
+ struct server_record *sl;
+ struct work_record *work;
+ struct server_record *servrec;
+ unstring regname;
+
+ pull_ascii_nstring(regname, sizeof(regname), registered_name->name);
+ work = find_workgroup_on_subnet( subrec, regname);
+
+ if(!work) {
+ DEBUG(0,("become_local_master_stage2: Error - cannot find \
+workgroup %s on subnet %s\n", regname, subrec->subnet_name));
+ return;
+ }
+
+ if((servrec = find_server_in_workgroup( work, global_myname())) == NULL) {
+ DEBUG(0,("become_local_master_stage2: Error - cannot find server %s \
+in workgroup %s on subnet %s\n",
+ global_myname(), regname, subrec->subnet_name));
+ work->mst_state = lp_local_master() ? MST_POTENTIAL : MST_NONE;
+ return;
+ }
+
+ DEBUG(3,("become_local_master_stage2: registered as master browser for workgroup %s \
+on subnet %s\n", work->work_group, subrec->subnet_name));
+
+ work->mst_state = MST_BROWSER; /* registering WORKGROUP(1d) succeeded */
+
+ /* update our server status */
+ servrec->serv.type |= SV_TYPE_MASTER_BROWSER;
+ servrec->serv.type &= ~SV_TYPE_POTENTIAL_BROWSER;
+
+ /* Tell the namelist writer to write out a change. */
+ subrec->work_changed = True;
+
+ /* Add this name to the workgroup as local master browser. */
+ set_workgroup_local_master_browser_name( work, global_myname());
+
+ /* Count the number of servers we have on our list. If it's
+ less than 10 (just a heuristic) request the servers
+ to announce themselves.
+ */
+ for( sl = work->serverlist; sl != NULL; sl = sl->next)
+ i++;
+
+ if (i < 10) {
+ /* Ask all servers on our local net to announce to us. */
+ broadcast_announce_request(subrec, work);
+ }
+
+ /*
+ * Now we are a local master on a broadcast subnet, we need to add
+ * the WORKGROUP<1d> name to the unicast subnet so that we can answer
+ * unicast requests sent to this name. We can create this name directly on
+ * the unicast subnet as a WINS server always returns true when registering
+ * this name, and discards the registration. We use the number of IP
+ * addresses registered to this name as a reference count, as we
+ * remove this broadcast subnet IP address from it when we stop becoming a local
+ * master browser for this broadcast subnet.
+ */
+
+ insert_permanent_name_into_unicast( subrec, registered_name, nb_flags);
+
+ /* Reset the announce master browser timer so that we try and tell a domain
+ master browser as soon as possible that we are a local master browser. */
+ reset_announce_timer();
+
+ if( DEBUGLVL( 0 ) ) {
+ dbgtext( "*****\n\n" );
+ dbgtext( "Samba name server %s ", global_myname() );
+ dbgtext( "is now a local master browser " );
+ dbgtext( "for workgroup %s ", work->work_group );
+ dbgtext( "on subnet %s\n\n*****\n", subrec->subnet_name );
+ }
+}
+
+/****************************************************************************
+ Failed to register the WORKGROUP<1d> name.
+ ****************************************************************************/
+
+static void become_local_master_fail2(struct subnet_record *subrec,
+ struct response_record *rrec,
+ struct nmb_name *fail_name)
+{
+ unstring failname;
+ struct work_record *work;
+
+ DEBUG(0,("become_local_master_fail2: failed to register name %s on subnet %s. \
+Failed to become a local master browser.\n", nmb_namestr(fail_name), subrec->subnet_name));
+
+ pull_ascii_nstring(failname, sizeof(failname), fail_name->name);
+ work = find_workgroup_on_subnet( subrec, failname);
+
+ if(!work) {
+ DEBUG(0,("become_local_master_fail2: Error - cannot find \
+workgroup %s on subnet %s\n", failname, subrec->subnet_name));
+ return;
+ }
+
+ /* Roll back all the way by calling unbecome_local_master_browser(). */
+ unbecome_local_master_browser(subrec, work, False);
+}
+
+/****************************************************************************
+ Success in registering the MSBROWSE name.
+ ****************************************************************************/
+
+static void become_local_master_stage1(struct subnet_record *subrec,
+ struct userdata_struct *userdata,
+ struct nmb_name *registered_name,
+ uint16 nb_flags,
+ int ttl, struct in_addr registered_ip)
+{
+ char *work_name = userdata->data;
+ struct work_record *work = find_workgroup_on_subnet( subrec, work_name);
+
+ if(!work) {
+ DEBUG(0,("become_local_master_stage1: Error - cannot find \
+ %s on subnet %s\n", work_name, subrec->subnet_name));
+ return;
+ }
+
+ DEBUG(3,("become_local_master_stage1: go to stage 2: register the %s<1d> name.\n",
+ work->work_group));
+
+ work->mst_state = MST_MSB; /* Registering MSBROWSE was successful. */
+
+ /*
+ * We registered the MSBROWSE name on a broadcast subnet, now need to add
+ * the MSBROWSE name to the unicast subnet so that we can answer
+ * unicast requests sent to this name. We create this name directly on
+ * the unicast subnet.
+ */
+
+ insert_permanent_name_into_unicast( subrec, registered_name, nb_flags);
+
+ /* Attempt to register the WORKGROUP<1d> name. */
+ register_name(subrec, work->work_group,0x1d,samba_nb_type,
+ become_local_master_stage2,
+ become_local_master_fail2,
+ NULL);
+}
+
+/****************************************************************************
+ Failed to register the MSBROWSE name.
+ ****************************************************************************/
+
+static void become_local_master_fail1(struct subnet_record *subrec,
+ struct response_record *rrec,
+ struct nmb_name *fail_name)
+{
+ char *work_name = rrec->userdata->data;
+ struct work_record *work = find_workgroup_on_subnet(subrec, work_name);
+
+ if(!work) {
+ DEBUG(0,("become_local_master_fail1: Error - cannot find \
+workgroup %s on subnet %s\n", work_name, subrec->subnet_name));
+ return;
+ }
+
+ if(find_server_in_workgroup(work, global_myname()) == NULL) {
+ DEBUG(0,("become_local_master_fail1: Error - cannot find server %s \
+in workgroup %s on subnet %s\n",
+ global_myname(), work->work_group, subrec->subnet_name));
+ return;
+ }
+
+ reset_workgroup_state( subrec, work->work_group, False );
+
+ DEBUG(0,("become_local_master_fail1: Failed to become a local master browser for \
+workgroup %s on subnet %s. Couldn't register name %s.\n",
+ work->work_group, subrec->subnet_name, nmb_namestr(fail_name)));
+}
+
+/******************************************************************
+ Become the local master browser on a subnet.
+ This gets called if we win an election on this subnet.
+
+ Stage 1: mst_state was MST_POTENTIAL - go to MST_BACK register ^1^2__MSBROWSE__^2^1.
+ Stage 2: mst_state was MST_BACKUP - go to MST_MSB and register WORKGROUP<1d>.
+ Stage 3: mst_state was MST_MSB - go to MST_BROWSER.
+******************************************************************/
+
+void become_local_master_browser(struct subnet_record *subrec, struct work_record *work)
+{
+ struct userdata_struct *userdata;
+ size_t size = sizeof(struct userdata_struct) + sizeof(fstring) + 1;
+
+ /* Sanity check. */
+ if (!lp_local_master()) {
+ DEBUG(0,("become_local_master_browser: Samba not configured as a local master browser.\n"));
+ return;
+ }
+
+ if(!AM_POTENTIAL_MASTER_BROWSER(work)) {
+ DEBUG(2,("become_local_master_browser: Awaiting potential browser state. Current state is %d\n",
+ work->mst_state ));
+ return;
+ }
+
+ if(find_server_in_workgroup( work, global_myname()) == NULL) {
+ DEBUG(0,("become_local_master_browser: Error - cannot find server %s \
+in workgroup %s on subnet %s\n",
+ global_myname(), work->work_group, subrec->subnet_name));
+ return;
+ }
+
+ DEBUG(2,("become_local_master_browser: Starting to become a master browser for workgroup \
+%s on subnet %s\n", work->work_group, subrec->subnet_name));
+
+ DEBUG(3,("become_local_master_browser: first stage - attempt to register ^1^2__MSBROWSE__^2^1\n"));
+ work->mst_state = MST_BACKUP; /* an election win was successful */
+
+ work->ElectionCriterion |= 0x5;
+
+ /* Tell the namelist writer to write out a change. */
+ subrec->work_changed = True;
+
+ /* Setup the userdata_struct. */
+ if((userdata = (struct userdata_struct *)malloc(size)) == NULL) {
+ DEBUG(0,("become_local_master_browser: malloc fail.\n"));
+ return;
+ }
+
+ userdata->copy_fn = NULL;
+ userdata->free_fn = NULL;
+ userdata->userdata_len = strlen(work->work_group)+1;
+ overmalloc_safe_strcpy(userdata->data, work->work_group, size - sizeof(*userdata) - 1);
+
+ /* Register the special browser group name. */
+ register_name(subrec, MSBROWSE, 0x01, samba_nb_type|NB_GROUP,
+ become_local_master_stage1,
+ become_local_master_fail1,
+ userdata);
+
+ zero_free(userdata, size);
+}
+
+/***************************************************************
+ Utility function to set the local master browser name. Does
+ some sanity checking as old versions of Samba seem to sometimes
+ say that the master browser name for a workgroup is the same
+ as the workgroup name.
+****************************************************************/
+
+void set_workgroup_local_master_browser_name( struct work_record *work, const char *newname)
+{
+ DEBUG(5,("set_workgroup_local_master_browser_name: setting local master name to '%s' \
+for workgroup %s.\n", newname, work->work_group ));
+
+#if 0
+ /*
+ * Apparently some sites use the workgroup name as the local
+ * master browser name. Arrrrggghhhhh ! (JRA).
+ */
+ if(strequal( work->work_group, newname))
+ {
+ DEBUG(5, ("set_workgroup_local_master_browser_name: Refusing to set \
+local_master_browser_name for workgroup %s to workgroup name.\n",
+ work->work_group ));
+ return;
+ }
+#endif
+
+ unstrcpy(work->local_master_browser_name, newname);
+}
diff --git a/source/nmbd/nmbd_browserdb.c b/source/nmbd/nmbd_browserdb.c
new file mode 100644
index 00000000000..c92513fae81
--- /dev/null
+++ b/source/nmbd/nmbd_browserdb.c
@@ -0,0 +1,175 @@
+/*
+ Unix SMB/CIFS implementation.
+ NBT netbios routines and daemon - version 2
+ Copyright (C) Andrew Tridgell 1994-1998
+ Copyright (C) Luke Kenneth Casson Leighton 1994-1998
+ Copyright (C) Jeremy Allison 1994-1998
+ Copyright (C) Christopher R. Hertel 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 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.
+
+*/
+/* -------------------------------------------------------------------------- **
+ * Modified July 1998 by CRH.
+ * I converted this module to use the canned doubly-linked lists. I also
+ * added comments above the functions where possible.
+ */
+
+#include "includes.h"
+
+/* -------------------------------------------------------------------------- **
+ * Variables...
+ *
+ * lmb_browserlist - This is our local master browser list.
+ */
+
+ubi_dlNewList( lmb_browserlist );
+
+/* -------------------------------------------------------------------------- **
+ * Functions...
+ */
+
+/* ************************************************************************** **
+ * Remove and free a browser list entry.
+ *
+ * Input: browc - A pointer to the entry to be removed from the list and
+ * freed.
+ * Output: none.
+ *
+ * ************************************************************************** **
+ */
+static void remove_lmb_browser_entry( struct browse_cache_record *browc )
+{
+ safe_free( ubi_dlRemThis( lmb_browserlist, browc ) );
+}
+
+/* ************************************************************************** **
+ * Update a browser death time.
+ *
+ * Input: browc - Pointer to the entry to be updated.
+ * Output: none.
+ *
+ * ************************************************************************** **
+ */
+void update_browser_death_time( struct browse_cache_record *browc )
+{
+ /* Allow the new lmb to miss an announce period before we remove it. */
+ browc->death_time = time(NULL) + ( (CHECK_TIME_MST_ANNOUNCE + 2) * 60 );
+}
+
+/* ************************************************************************** **
+ * Create a browser entry and add it to the local master browser list.
+ *
+ * Input: work_name
+ * browser_name
+ * ip
+ *
+ * Output: Pointer to the new entry, or NULL if malloc() failed.
+ *
+ * ************************************************************************** **
+ */
+struct browse_cache_record *create_browser_in_lmb_cache( const char *work_name,
+ const char *browser_name,
+ struct in_addr ip )
+{
+ struct browse_cache_record *browc;
+ time_t now = time( NULL );
+
+ browc = (struct browse_cache_record *)malloc( sizeof( *browc ) );
+
+ if( NULL == browc ) {
+ DEBUG( 0, ("create_browser_in_lmb_cache: malloc fail !\n") );
+ return( NULL );
+ }
+
+ memset( (char *)browc, '\0', sizeof( *browc ) );
+
+ /* For a new lmb entry we want to sync with it after one minute. This
+ will allow it time to send out a local announce and build its
+ browse list.
+ */
+
+ browc->sync_time = now + 60;
+
+ /* Allow the new lmb to miss an announce period before we remove it. */
+ browc->death_time = now + ( (CHECK_TIME_MST_ANNOUNCE + 2) * 60 );
+
+ unstrcpy( browc->lmb_name, browser_name);
+ unstrcpy( browc->work_group, work_name);
+ strupper_m( browc->lmb_name );
+ strupper_m( browc->work_group );
+
+ browc->ip = ip;
+
+ (void)ubi_dlAddTail( lmb_browserlist, browc );
+
+ if( DEBUGLVL( 3 ) ) {
+ Debug1( "nmbd_browserdb:create_browser_in_lmb_cache()\n" );
+ Debug1( " Added lmb cache entry for workgroup %s ", browc->work_group );
+ Debug1( "name %s IP %s ", browc->lmb_name, inet_ntoa(ip) );
+ Debug1( "ttl %d\n", (int)browc->death_time );
+ }
+
+ return( browc );
+}
+
+/* ************************************************************************** **
+ * Find a browser entry in the local master browser list.
+ *
+ * Input: browser_name - The name for which to search.
+ *
+ * Output: A pointer to the matching entry, or NULL if no match was found.
+ *
+ * ************************************************************************** **
+ */
+struct browse_cache_record *find_browser_in_lmb_cache( const char *browser_name )
+{
+ struct browse_cache_record *browc;
+
+ for( browc = (struct browse_cache_record *)ubi_dlFirst( lmb_browserlist );
+ browc; browc = (struct browse_cache_record *)ubi_dlNext( browc ) )
+ if( strequal( browser_name, browc->lmb_name ) )
+ break;
+
+ return( browc );
+}
+
+/* ************************************************************************** **
+ * Expire timed out browsers in the browserlist.
+ *
+ * Input: t - Expiration time. Entries with death times less than this
+ * value will be removed from the list.
+ * Output: none.
+ *
+ * ************************************************************************** **
+ */
+void expire_lmb_browsers( time_t t )
+{
+ struct browse_cache_record *browc;
+ struct browse_cache_record *nextbrowc;
+
+ for( browc = (struct browse_cache_record *)ubi_dlFirst( lmb_browserlist );
+ browc; browc = nextbrowc ) {
+ nextbrowc = (struct browse_cache_record *)ubi_dlNext( browc );
+
+ if( browc->death_time < t ) {
+ if( DEBUGLVL( 3 ) ) {
+ Debug1( "nmbd_browserdb:expire_lmb_browsers()\n" );
+ Debug1( " Removing timed out lmb entry %s\n", browc->lmb_name );
+ }
+ remove_lmb_browser_entry( browc );
+ }
+ }
+}
diff --git a/source/nmbd/nmbd_browsesync.c b/source/nmbd/nmbd_browsesync.c
new file mode 100644
index 00000000000..15827e21bae
--- /dev/null
+++ b/source/nmbd/nmbd_browsesync.c
@@ -0,0 +1,676 @@
+/*
+ Unix SMB/CIFS implementation.
+ NBT netbios routines and daemon - version 2
+ Copyright (C) Andrew Tridgell 1994-1998
+ Copyright (C) Luke Kenneth Casson Leighton 1994-1998
+ Copyright (C) Jeremy Allison 1994-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 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"
+
+/* This is our local master browser list database. */
+extern ubi_dlList lmb_browserlist[];
+
+/****************************************************************************
+As a domain master browser, do a sync with a local master browser.
+**************************************************************************/
+
+static void sync_with_lmb(struct browse_cache_record *browc)
+{
+ struct work_record *work;
+
+ if( !(work = find_workgroup_on_subnet(unicast_subnet, browc->work_group)) ) {
+ if( DEBUGLVL( 0 ) ) {
+ dbgtext( "sync_with_lmb:\n" );
+ dbgtext( "Failed to get a workgroup for a local master browser " );
+ dbgtext( "cache entry workgroup " );
+ dbgtext( "%s, server %s\n", browc->work_group, browc->lmb_name );
+ }
+ return;
+ }
+
+ /* We should only be doing this if we are a domain master browser for
+ the given workgroup. Ensure this is so. */
+
+ if(!AM_DOMAIN_MASTER_BROWSER(work)) {
+ if( DEBUGLVL( 0 ) ) {
+ dbgtext( "sync_with_lmb:\n" );
+ dbgtext( "We are trying to sync with a local master browser " );
+ dbgtext( "%s for workgroup %s\n", browc->lmb_name, browc->work_group );
+ dbgtext( "and we are not a domain master browser on this workgroup.\n" );
+ dbgtext( "Error!\n" );
+ }
+ return;
+ }
+
+ if( DEBUGLVL( 2 ) ) {
+ dbgtext( "sync_with_lmb:\n" );
+ dbgtext( "Initiating sync with local master browser " );
+ dbgtext( "%s<0x20> at IP %s ", browc->lmb_name, inet_ntoa(browc->ip) );
+ dbgtext( "for workgroup %s\n", browc->work_group );
+ }
+
+ sync_browse_lists(work, browc->lmb_name, 0x20, browc->ip, True, True);
+
+ browc->sync_time += (CHECK_TIME_DMB_TO_LMB_SYNC * 60);
+}
+
+/****************************************************************************
+Sync or expire any local master browsers.
+**************************************************************************/
+
+void dmb_expire_and_sync_browser_lists(time_t t)
+{
+ static time_t last_run = 0;
+ struct browse_cache_record *browc;
+
+ /* Only do this every 20 seconds. */
+ if (t - last_run < 20)
+ return;
+
+ last_run = t;
+
+ expire_lmb_browsers(t);
+
+ for( browc = (struct browse_cache_record *)ubi_dlFirst( lmb_browserlist );
+ browc;
+ browc = (struct browse_cache_record *)ubi_dlNext( browc ) ) {
+ if (browc->sync_time < t)
+ sync_with_lmb(browc);
+ }
+}
+
+/****************************************************************************
+As a local master browser, send an announce packet to the domain master browser.
+**************************************************************************/
+
+static void announce_local_master_browser_to_domain_master_browser( struct work_record *work)
+{
+ pstring outbuf;
+ unstring myname;
+ char *p;
+
+ if(ismyip(work->dmb_addr)) {
+ if( DEBUGLVL( 2 ) ) {
+ dbgtext( "announce_local_master_browser_to_domain_master_browser:\n" );
+ dbgtext( "We are both a domain and a local master browser for " );
+ dbgtext( "workgroup %s. ", work->work_group );
+ dbgtext( "Do not announce to ourselves.\n" );
+ }
+ return;
+ }
+
+ memset(outbuf,'\0',sizeof(outbuf));
+ p = outbuf;
+ SCVAL(p,0,ANN_MasterAnnouncement);
+ p++;
+
+ unstrcpy(myname, global_myname());
+ strupper_m(myname);
+ myname[15]='\0';
+ /* The call below does CH_UNIX -> CH_DOS conversion. JRA */
+ push_pstring_base(p, myname, outbuf);
+
+ p = skip_string(p,1);
+
+ if( DEBUGLVL( 4 ) ) {
+ dbgtext( "announce_local_master_browser_to_domain_master_browser:\n" );
+ dbgtext( "Sending local master announce to " );
+ dbgtext( "%s for workgroup %s.\n", nmb_namestr(&work->dmb_name),
+ work->work_group );
+ }
+
+ send_mailslot(True, BROWSE_MAILSLOT, outbuf,PTR_DIFF(p,outbuf),
+ global_myname(), 0x0, work->dmb_name.name, 0x0,
+ work->dmb_addr, FIRST_SUBNET->myip, DGRAM_PORT);
+}
+
+/****************************************************************************
+As a local master browser, do a sync with a domain master browser.
+**************************************************************************/
+
+static void sync_with_dmb(struct work_record *work)
+{
+ unstring dmb_name;
+
+ if( DEBUGLVL( 2 ) ) {
+ dbgtext( "sync_with_dmb:\n" );
+ dbgtext( "Initiating sync with domain master browser " );
+ dbgtext( "%s ", nmb_namestr(&work->dmb_name) );
+ dbgtext( "at IP %s ", inet_ntoa(work->dmb_addr) );
+ dbgtext( "for workgroup %s\n", work->work_group );
+ }
+
+ pull_ascii_nstring(dmb_name, sizeof(dmb_name), work->dmb_name.name);
+ sync_browse_lists(work, dmb_name, work->dmb_name.name_type,
+ work->dmb_addr, False, True);
+}
+
+/****************************************************************************
+ Function called when a node status query to a domain master browser IP succeeds.
+****************************************************************************/
+
+static void domain_master_node_status_success(struct subnet_record *subrec,
+ struct userdata_struct *userdata,
+ struct res_rec *answers,
+ struct in_addr from_ip)
+{
+ struct work_record *work = find_workgroup_on_subnet( subrec, userdata->data);
+
+ if( work == NULL ) {
+ if( DEBUGLVL( 0 ) ) {
+ dbgtext( "domain_master_node_status_success:\n" );
+ dbgtext( "Unable to find workgroup " );
+ dbgtext( "%s on subnet %s.\n", userdata->data, subrec->subnet_name );
+ }
+ return;
+ }
+
+ if( DEBUGLVL( 3 ) ) {
+ dbgtext( "domain_master_node_status_success:\n" );
+ dbgtext( "Success in node status for workgroup " );
+ dbgtext( "%s from ip %s\n", work->work_group, inet_ntoa(from_ip) );
+ }
+
+ /* Go through the list of names found at answers->rdata and look for
+ the first SERVER<0x20> name. */
+
+ if(answers->rdata != NULL) {
+ char *p = answers->rdata;
+ int numnames = CVAL(p, 0);
+
+ p += 1;
+
+ while (numnames--) {
+ unstring qname;
+ uint16 nb_flags;
+ int name_type;
+
+ pull_ascii_nstring(qname, sizeof(qname), p);
+ name_type = CVAL(p,15);
+ nb_flags = get_nb_flags(&p[16]);
+ trim_char(qname,'\0',' ');
+
+ p += 18;
+
+ if(!(nb_flags & NB_GROUP) && (name_type == 0x20)) {
+ struct nmb_name nmbname;
+
+ make_nmb_name(&nmbname, qname, name_type);
+
+ /* Copy the dmb name and IP address
+ into the workgroup struct. */
+
+ work->dmb_name = nmbname;
+ putip((char *)&work->dmb_addr, &from_ip);
+
+ /* Do the local master browser announcement to the domain
+ master browser name and IP. */
+ announce_local_master_browser_to_domain_master_browser( work );
+
+ /* Now synchronise lists with the domain master browser. */
+ sync_with_dmb(work);
+ break;
+ }
+ }
+ } else if( DEBUGLVL( 0 ) ) {
+ dbgtext( "domain_master_node_status_success:\n" );
+ dbgtext( "Failed to find a SERVER<0x20> name in reply from IP " );
+ dbgtext( "%s.\n", inet_ntoa(from_ip) );
+ }
+}
+
+/****************************************************************************
+ Function called when a node status query to a domain master browser IP fails.
+****************************************************************************/
+
+static void domain_master_node_status_fail(struct subnet_record *subrec,
+ struct response_record *rrec)
+{
+ struct userdata_struct *userdata = rrec->userdata;
+
+ if( DEBUGLVL( 0 ) ) {
+ dbgtext( "domain_master_node_status_fail:\n" );
+ dbgtext( "Doing a node status request to the domain master browser\n" );
+ dbgtext( "for workgroup %s ", userdata ? userdata->data : "NULL" );
+ dbgtext( "at IP %s failed.\n", inet_ntoa(rrec->packet->ip) );
+ dbgtext( "Cannot sync browser lists.\n" );
+ }
+}
+
+/****************************************************************************
+ Function called when a query for a WORKGROUP<1b> name succeeds.
+****************************************************************************/
+
+static void find_domain_master_name_query_success(struct subnet_record *subrec,
+ struct userdata_struct *userdata_in,
+ struct nmb_name *q_name, struct in_addr answer_ip, struct res_rec *rrec)
+{
+ /*
+ * Unfortunately, finding the IP address of the Domain Master Browser,
+ * as we have here, is not enough. We need to now do a sync to the
+ * SERVERNAME<0x20> NetBIOS name, as only recent NT servers will
+ * respond to the SMBSERVER name. To get this name from IP
+ * address we do a Node status request, and look for the first
+ * NAME<0x20> in the response, and take that as the server name.
+ * We also keep a cache of the Domain Master Browser name for this
+ * workgroup in the Workgroup struct, so that if the same IP addess
+ * is returned every time, we don't need to do the node status
+ * request.
+ */
+
+ struct work_record *work;
+ struct nmb_name nmbname;
+ struct userdata_struct *userdata;
+ size_t size = sizeof(struct userdata_struct) + sizeof(fstring)+1;
+ unstring qname;
+
+ pull_ascii_nstring(qname, sizeof(qname), q_name->name);
+ if( !(work = find_workgroup_on_subnet(subrec, qname)) ) {
+ if( DEBUGLVL( 0 ) ) {
+ dbgtext( "find_domain_master_name_query_success:\n" );
+ dbgtext( "Failed to find workgroup %s\n", qname);
+ }
+ return;
+ }
+
+ /* First check if we already have a dmb for this workgroup. */
+
+ if(!is_zero_ip(work->dmb_addr) && ip_equal(work->dmb_addr, answer_ip)) {
+ /* Do the local master browser announcement to the domain
+ master browser name and IP. */
+ announce_local_master_browser_to_domain_master_browser( work );
+
+ /* Now synchronise lists with the domain master browser. */
+ sync_with_dmb(work);
+ return;
+ } else {
+ zero_ip(&work->dmb_addr);
+ }
+
+ /* Now initiate the node status request. */
+
+ /* We used to use the name "*",0x0 here, but some Windows
+ * servers don't answer that name. However we *know* they
+ * have the name workgroup#1b (as we just looked it up).
+ * So do the node status request on this name instead.
+ * Found at LBL labs. JRA.
+ */
+
+ make_nmb_name(&nmbname,work->work_group,0x1b);
+
+ /* Put the workgroup name into the userdata so we know
+ what workgroup we're talking to when the reply comes
+ back. */
+
+ /* Setup the userdata_struct - this is copied so we can use
+ a stack variable for this. */
+
+ if((userdata = (struct userdata_struct *)malloc(size)) == NULL) {
+ DEBUG(0, ("find_domain_master_name_query_success: malloc fail.\n"));
+ return;
+ }
+
+ userdata->copy_fn = NULL;
+ userdata->free_fn = NULL;
+ userdata->userdata_len = strlen(work->work_group)+1;
+ overmalloc_safe_strcpy(userdata->data, work->work_group, size - sizeof(*userdata) - 1);
+
+ node_status( subrec, &nmbname, answer_ip,
+ domain_master_node_status_success,
+ domain_master_node_status_fail,
+ userdata);
+
+ zero_free(userdata, size);
+}
+
+/****************************************************************************
+ Function called when a query for a WORKGROUP<1b> name fails.
+ ****************************************************************************/
+
+static void find_domain_master_name_query_fail(struct subnet_record *subrec,
+ struct response_record *rrec,
+ struct nmb_name *question_name, int fail_code)
+{
+ if( DEBUGLVL( 0 ) ) {
+ dbgtext( "find_domain_master_name_query_fail:\n" );
+ dbgtext( "Unable to find the Domain Master Browser name " );
+ dbgtext( "%s for the workgroup %s.\n",
+ nmb_namestr(question_name), question_name->name );
+ dbgtext( "Unable to sync browse lists in this workgroup.\n" );
+ }
+}
+
+/****************************************************************************
+As a local master browser for a workgroup find the domain master browser
+name, announce ourselves as local master browser to it and then pull the
+full domain browse lists from it onto the given subnet.
+**************************************************************************/
+
+void announce_and_sync_with_domain_master_browser( struct subnet_record *subrec,
+ struct work_record *work)
+{
+ /* Only do this if we are using a WINS server. */
+ if(we_are_a_wins_client() == False) {
+ if( DEBUGLVL( 10 ) ) {
+ dbgtext( "announce_and_sync_with_domain_master_browser:\n" );
+ dbgtext( "Ignoring, as we are not a WINS client.\n" );
+ }
+ return;
+ }
+
+ /* First, query for the WORKGROUP<1b> name from the WINS server. */
+ query_name(unicast_subnet, work->work_group, 0x1b,
+ find_domain_master_name_query_success,
+ find_domain_master_name_query_fail,
+ NULL);
+}
+
+/****************************************************************************
+ Function called when a node status query to a domain master browser IP succeeds.
+ This function is only called on query to a Samba 1.9.18 or above WINS server.
+
+ Note that adding the workgroup name is enough for this workgroup to be
+ browsable by clients, as clients query the WINS server or broadcast
+ nets for the WORKGROUP<1b> name when they want to browse a workgroup
+ they are not in. We do not need to do a sync with this Domain Master
+ Browser in order for our browse clients to see machines in this workgroup.
+ JRA.
+****************************************************************************/
+
+static void get_domain_master_name_node_status_success(struct subnet_record *subrec,
+ struct userdata_struct *userdata,
+ struct res_rec *answers,
+ struct in_addr from_ip)
+{
+ struct work_record *work;
+ unstring server_name;
+
+ server_name[0] = 0;
+
+ if( DEBUGLVL( 3 ) ) {
+ dbgtext( "get_domain_master_name_node_status_success:\n" );
+ dbgtext( "Success in node status from ip %s\n", inet_ntoa(from_ip) );
+ }
+
+ /*
+ * Go through the list of names found at answers->rdata and look for
+ * the first WORKGROUP<0x1b> name.
+ */
+
+ if(answers->rdata != NULL) {
+ char *p = answers->rdata;
+ int numnames = CVAL(p, 0);
+
+ p += 1;
+
+ while (numnames--) {
+ unstring qname;
+ uint16 nb_flags;
+ int name_type;
+
+ pull_ascii_nstring(qname, sizeof(qname), p);
+ name_type = CVAL(p,15);
+ nb_flags = get_nb_flags(&p[16]);
+ trim_char(qname,'\0',' ');
+
+ p += 18;
+
+ if(!(nb_flags & NB_GROUP) && (name_type == 0x00) &&
+ server_name[0] == 0) {
+ /* this is almost certainly the server netbios name */
+ unstrcpy(server_name, qname);
+ continue;
+ }
+
+ if(!(nb_flags & NB_GROUP) && (name_type == 0x1b)) {
+ if( DEBUGLVL( 5 ) ) {
+ dbgtext( "get_domain_master_name_node_status_success:\n" );
+ dbgtext( "%s(%s) ", server_name, inet_ntoa(from_ip) );
+ dbgtext( "is a domain master browser for workgroup " );
+ dbgtext( "%s. Adding this name.\n", qname );
+ }
+
+ /*
+ * If we don't already know about this workgroup, add it
+ * to the workgroup list on the unicast_subnet.
+ */
+
+ if((work = find_workgroup_on_subnet( subrec, qname)) == NULL) {
+ struct nmb_name nmbname;
+ /*
+ * Add it - with an hour in the cache.
+ */
+ if(!(work= create_workgroup_on_subnet(subrec, qname, 60*60)))
+ return;
+
+ /* remember who the master is */
+ unstrcpy(work->local_master_browser_name, server_name);
+ make_nmb_name(&nmbname, server_name, 0x20);
+ work->dmb_name = nmbname;
+ work->dmb_addr = from_ip;
+ }
+ break;
+ }
+ }
+ } else if( DEBUGLVL( 0 ) ) {
+ dbgtext( "get_domain_master_name_node_status_success:\n" );
+ dbgtext( "Failed to find a WORKGROUP<0x1b> name in reply from IP " );
+ dbgtext( "%s.\n", inet_ntoa(from_ip) );
+ }
+}
+
+/****************************************************************************
+ Function called when a node status query to a domain master browser IP fails.
+****************************************************************************/
+
+static void get_domain_master_name_node_status_fail(struct subnet_record *subrec,
+ struct response_record *rrec)
+{
+ if( DEBUGLVL( 0 ) ) {
+ dbgtext( "get_domain_master_name_node_status_fail:\n" );
+ dbgtext( "Doing a node status request to the domain master browser " );
+ dbgtext( "at IP %s failed.\n", inet_ntoa(rrec->packet->ip) );
+ dbgtext( "Cannot get workgroup name.\n" );
+ }
+}
+
+/****************************************************************************
+ Function called when a query for *<1b> name succeeds.
+****************************************************************************/
+
+static void find_all_domain_master_names_query_success(struct subnet_record *subrec,
+ struct userdata_struct *userdata_in,
+ struct nmb_name *q_name, struct in_addr answer_ip, struct res_rec *rrec)
+{
+ /*
+ * We now have a list of all the domain master browsers for all workgroups
+ * that have registered with the WINS server. Now do a node status request
+ * to each one and look for the first 1b name in the reply. This will be
+ * the workgroup name that we will add to the unicast subnet as a 'non-local'
+ * workgroup.
+ */
+
+ struct nmb_name nmbname;
+ struct in_addr send_ip;
+ int i;
+
+ if( DEBUGLVL( 5 ) ) {
+ dbgtext( "find_all_domain_master_names_query_succes:\n" );
+ dbgtext( "Got answer from WINS server of %d ", (rrec->rdlength / 6) );
+ dbgtext( "IP addresses for Domain Master Browsers.\n" );
+ }
+
+ for(i = 0; i < rrec->rdlength / 6; i++) {
+ /* Initiate the node status requests. */
+ make_nmb_name(&nmbname, "*", 0);
+
+ putip((char *)&send_ip, (char *)&rrec->rdata[(i*6) + 2]);
+
+ /*
+ * Don't send node status requests to ourself.
+ */
+
+ if(ismyip( send_ip )) {
+ if( DEBUGLVL( 5 ) ) {
+ dbgtext( "find_all_domain_master_names_query_succes:\n" );
+ dbgtext( "Not sending node status to our own IP " );
+ dbgtext( "%s.\n", inet_ntoa(send_ip) );
+ }
+ continue;
+ }
+
+ if( DEBUGLVL( 5 ) ) {
+ dbgtext( "find_all_domain_master_names_query_success:\n" );
+ dbgtext( "Sending node status request to IP %s.\n", inet_ntoa(send_ip) );
+ }
+
+ node_status( subrec, &nmbname, send_ip,
+ get_domain_master_name_node_status_success,
+ get_domain_master_name_node_status_fail,
+ NULL);
+ }
+}
+
+/****************************************************************************
+ Function called when a query for *<1b> name fails.
+ ****************************************************************************/
+static void find_all_domain_master_names_query_fail(struct subnet_record *subrec,
+ struct response_record *rrec,
+ struct nmb_name *question_name, int fail_code)
+{
+ if( DEBUGLVL( 10 ) ) {
+ dbgtext( "find_domain_master_name_query_fail:\n" );
+ dbgtext( "WINS server did not reply to a query for name " );
+ dbgtext( "%s.\nThis means it ", nmb_namestr(question_name) );
+ dbgtext( "is probably not a Samba 1.9.18 or above WINS server.\n" );
+ }
+}
+
+/****************************************************************************
+ If we are a domain master browser on the unicast subnet, do a query to the
+ WINS server for the *<1b> name. This will only work to a Samba WINS server,
+ so ignore it if we fail. If we succeed, contact each of the IP addresses in
+ turn and do a node status request to them. If this succeeds then look for a
+ <1b> name in the reply - this is the workgroup name. Add this to the unicast
+ subnet. This is expensive, so we only do this every 15 minutes.
+**************************************************************************/
+
+void collect_all_workgroup_names_from_wins_server(time_t t)
+{
+ static time_t lastrun = 0;
+ struct work_record *work;
+
+ /* Only do this if we are using a WINS server. */
+ if(we_are_a_wins_client() == False)
+ return;
+
+ /* Check to see if we are a domain master browser on the unicast subnet. */
+ if((work = find_workgroup_on_subnet( unicast_subnet, lp_workgroup())) == NULL) {
+ if( DEBUGLVL( 0 ) ) {
+ dbgtext( "collect_all_workgroup_names_from_wins_server:\n" );
+ dbgtext( "Cannot find my workgroup %s ", lp_workgroup() );
+ dbgtext( "on subnet %s.\n", unicast_subnet->subnet_name );
+ }
+ return;
+ }
+
+ if(!AM_DOMAIN_MASTER_BROWSER(work))
+ return;
+
+ if ((lastrun != 0) && (t < lastrun + (15 * 60)))
+ return;
+
+ lastrun = t;
+
+ /* First, query for the *<1b> name from the WINS server. */
+ query_name(unicast_subnet, "*", 0x1b,
+ find_all_domain_master_names_query_success,
+ find_all_domain_master_names_query_fail,
+ NULL);
+}
+
+
+/****************************************************************************
+ If we are a domain master browser on the unicast subnet, do a regular sync
+ with all other DMBs that we know of on that subnet.
+
+To prevent exponential network traffic with large numbers of workgroups
+we use a randomised system where sync probability is inversely proportional
+to the number of known workgroups
+**************************************************************************/
+
+void sync_all_dmbs(time_t t)
+{
+ static time_t lastrun = 0;
+ struct work_record *work;
+ int count=0;
+
+ /* Only do this if we are using a WINS server. */
+ if(we_are_a_wins_client() == False)
+ return;
+
+ /* Check to see if we are a domain master browser on the
+ unicast subnet. */
+ work = find_workgroup_on_subnet(unicast_subnet, lp_workgroup());
+ if (!work)
+ return;
+
+ if (!AM_DOMAIN_MASTER_BROWSER(work))
+ return;
+
+ if ((lastrun != 0) && (t < lastrun + (5 * 60)))
+ return;
+
+ /* count how many syncs we might need to do */
+ for (work=unicast_subnet->workgrouplist; work; work = work->next) {
+ if (strcmp(lp_workgroup(), work->work_group)) {
+ count++;
+ }
+ }
+
+ /* sync with a probability of 1/count */
+ for (work=unicast_subnet->workgrouplist; work; work = work->next) {
+ if (strcmp(lp_workgroup(), work->work_group)) {
+ unstring dmb_name;
+
+ if (((unsigned)sys_random()) % count != 0)
+ continue;
+
+ lastrun = t;
+
+ if (!work->dmb_name.name[0]) {
+ /* we don't know the DMB - assume it is
+ the same as the unicast local master */
+ make_nmb_name(&work->dmb_name,
+ work->local_master_browser_name,
+ 0x20);
+ }
+
+ pull_ascii_nstring(dmb_name, sizeof(dmb_name), work->dmb_name.name);
+
+ DEBUG(3,("Initiating DMB<->DMB sync with %s(%s)\n",
+ dmb_name, inet_ntoa(work->dmb_addr)));
+
+ sync_browse_lists(work,
+ dmb_name,
+ work->dmb_name.name_type,
+ work->dmb_addr, False, False);
+ }
+ }
+}
diff --git a/source/nmbd/nmbd_elections.c b/source/nmbd/nmbd_elections.c
new file mode 100644
index 00000000000..470cf4277b5
--- /dev/null
+++ b/source/nmbd/nmbd_elections.c
@@ -0,0 +1,391 @@
+/*
+ Unix SMB/CIFS implementation.
+ NBT netbios routines and daemon - version 2
+ Copyright (C) Andrew Tridgell 1994-1998
+ Copyright (C) Luke Kenneth Casson Leighton 1994-1998
+ Copyright (C) Jeremy Allison 1994-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 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"
+
+/* Election parameters. */
+extern time_t StartupTime;
+
+/****************************************************************************
+ Send an election datagram packet.
+**************************************************************************/
+
+static void send_election_dgram(struct subnet_record *subrec, const char *workgroup_name,
+ uint32 criterion, int timeup,const char *server_name)
+{
+ pstring outbuf;
+ unstring srv_name;
+ char *p;
+
+ DEBUG(2,("send_election_dgram: Sending election packet for workgroup %s on subnet %s\n",
+ workgroup_name, subrec->subnet_name ));
+
+ memset(outbuf,'\0',sizeof(outbuf));
+ p = outbuf;
+ SCVAL(p,0,ANN_Election); /* Election opcode. */
+ p++;
+
+ SCVAL(p,0,((criterion == 0 && timeup == 0) ? 0 : ELECTION_VERSION));
+ SIVAL(p,1,criterion);
+ SIVAL(p,5,timeup*1000); /* ms - Despite what the spec says. */
+ p += 13;
+ unstrcpy(srv_name, server_name);
+ strupper_m(srv_name);
+ /* The following call does UNIX -> DOS charset conversion. */
+ pstrcpy_base(p, srv_name, outbuf);
+ p = skip_string(p,1);
+
+ send_mailslot(False, BROWSE_MAILSLOT, outbuf, PTR_DIFF(p,outbuf),
+ global_myname(), 0,
+ workgroup_name, 0x1e,
+ subrec->bcast_ip, subrec->myip, DGRAM_PORT);
+}
+
+/*******************************************************************
+ We found a current master browser on one of our broadcast interfaces.
+******************************************************************/
+
+static void check_for_master_browser_success(struct subnet_record *subrec,
+ struct userdata_struct *userdata,
+ struct nmb_name *answer_name,
+ struct in_addr answer_ip, struct res_rec *rrec)
+{
+ unstring aname;
+ pull_ascii_nstring(aname, sizeof(aname), answer_name->name);
+ DEBUG(3,("check_for_master_browser_success: Local master browser for workgroup %s exists at \
+IP %s (just checking).\n", aname, inet_ntoa(answer_ip) ));
+}
+
+/*******************************************************************
+ We failed to find a current master browser on one of our broadcast interfaces.
+******************************************************************/
+
+static void check_for_master_browser_fail( struct subnet_record *subrec,
+ struct response_record *rrec,
+ struct nmb_name *question_name,
+ int fail_code)
+{
+ unstring workgroup_name;
+ struct work_record *work;
+
+ pull_ascii_nstring(workgroup_name,sizeof(workgroup_name),question_name->name);
+
+ work = find_workgroup_on_subnet(subrec, workgroup_name);
+ if(work == NULL) {
+ DEBUG(0,("check_for_master_browser_fail: Unable to find workgroup %s on subnet %s.=\n",
+ workgroup_name, subrec->subnet_name ));
+ return;
+ }
+
+ if (strequal(work->work_group, lp_workgroup())) {
+
+ if (lp_local_master()) {
+ /* We have discovered that there is no local master
+ browser, and we are configured to initiate
+ an election that we will participate in.
+ */
+ DEBUG(2,("check_for_master_browser_fail: Forcing election on workgroup %s subnet %s\n",
+ work->work_group, subrec->subnet_name ));
+
+ /* Setting this means we will participate when the
+ election is run in run_elections(). */
+ work->needelection = True;
+ } else {
+ /* We need to force an election, because we are configured
+ not to become the local master, but we still need one,
+ having detected that one doesn't exist.
+ */
+ send_election_dgram(subrec, work->work_group, 0, 0, "");
+ }
+ }
+}
+
+/*******************************************************************
+ Ensure there is a local master browser for a workgroup on our
+ broadcast interfaces.
+******************************************************************/
+
+void check_master_browser_exists(time_t t)
+{
+ static time_t lastrun=0;
+ struct subnet_record *subrec;
+ const char *workgroup_name = lp_workgroup();
+
+ if (!lastrun)
+ lastrun = t;
+
+ if (t < (lastrun + (CHECK_TIME_MST_BROWSE * 60)))
+ return;
+
+ lastrun = t;
+
+ dump_workgroups(False);
+
+ for (subrec = FIRST_SUBNET; subrec; subrec = NEXT_SUBNET_EXCLUDING_UNICAST(subrec)) {
+ struct work_record *work;
+
+ for (work = subrec->workgrouplist; work; work = work->next) {
+ if (strequal(work->work_group, workgroup_name) && !AM_LOCAL_MASTER_BROWSER(work)) {
+ /* Do a name query for the local master browser on this net. */
+ query_name( subrec, work->work_group, 0x1d,
+ check_for_master_browser_success,
+ check_for_master_browser_fail,
+ NULL);
+ }
+ }
+ }
+}
+
+/*******************************************************************
+ Run an election.
+******************************************************************/
+
+void run_elections(time_t t)
+{
+ static time_t lastime = 0;
+
+ struct subnet_record *subrec;
+
+ /* Send election packets once every 2 seconds - note */
+ if (lastime && (t - lastime < 2))
+ return;
+
+ lastime = t;
+
+ START_PROFILE(run_elections);
+ for (subrec = FIRST_SUBNET; subrec; subrec = NEXT_SUBNET_EXCLUDING_UNICAST(subrec)) {
+ struct work_record *work;
+
+ for (work = subrec->workgrouplist; work; work = work->next) {
+ if (work->RunningElection) {
+ /*
+ * We can only run an election for a workgroup if we have
+ * registered the WORKGROUP<1e> name, as that's the name
+ * we must listen to.
+ */
+ struct nmb_name nmbname;
+
+ make_nmb_name(&nmbname, work->work_group, 0x1e);
+ if(find_name_on_subnet( subrec, &nmbname, FIND_SELF_NAME)==NULL) {
+ DEBUG(8,("run_elections: Cannot send election packet yet as name %s not \
+yet registered on subnet %s\n", nmb_namestr(&nmbname), subrec->subnet_name ));
+ continue;
+ }
+
+ send_election_dgram(subrec, work->work_group, work->ElectionCriterion,
+ t - StartupTime, global_myname());
+
+ if (work->ElectionCount++ >= 4) {
+ /* Won election (4 packets were sent out uncontested. */
+ DEBUG(2,("run_elections: >>> Won election for workgroup %s on subnet %s <<<\n",
+ work->work_group, subrec->subnet_name ));
+
+ work->RunningElection = False;
+
+ become_local_master_browser(subrec, work);
+ }
+ }
+ }
+ }
+ END_PROFILE(run_elections);
+}
+
+/*******************************************************************
+ Determine if I win an election.
+******************************************************************/
+
+static BOOL win_election(struct work_record *work, int version,
+ uint32 criterion, int timeup, const char *server_name)
+{
+ int mytimeup = time(NULL) - StartupTime;
+ uint32 mycriterion = work->ElectionCriterion;
+
+ /* If local master is false then never win in election broadcasts. */
+ if(!lp_local_master()) {
+ DEBUG(3,("win_election: Losing election as local master == False\n"));
+ return False;
+ }
+
+ DEBUG(4,("win_election: election comparison: %x:%x %x:%x %d:%d %s:%s\n",
+ version, ELECTION_VERSION,
+ criterion, mycriterion,
+ timeup, mytimeup,
+ server_name, global_myname()));
+
+ if (version > ELECTION_VERSION)
+ return(False);
+ if (version < ELECTION_VERSION)
+ return(True);
+
+ if (criterion > mycriterion)
+ return(False);
+ if (criterion < mycriterion)
+ return(True);
+
+ if (timeup > mytimeup)
+ return(False);
+ if (timeup < mytimeup)
+ return(True);
+
+ if (StrCaseCmp(global_myname(), server_name) > 0)
+ return(False);
+
+ return(True);
+}
+
+/*******************************************************************
+ Process an incoming election datagram packet.
+******************************************************************/
+
+void process_election(struct subnet_record *subrec, struct packet_struct *p, char *buf)
+{
+ struct dgram_packet *dgram = &p->packet.dgram;
+ int version = CVAL(buf,0);
+ uint32 criterion = IVAL(buf,1);
+ int timeup = IVAL(buf,5)/1000;
+ unstring server_name;
+ struct work_record *work;
+ unstring workgroup_name;
+
+ pull_ascii_nstring(server_name, sizeof(server_name), buf+13);
+ pull_ascii_nstring(workgroup_name, sizeof(workgroup_name), dgram->dest_name.name);
+
+ START_PROFILE(election);
+ server_name[15] = 0;
+
+ DEBUG(3,("process_election: Election request from %s at IP %s on subnet %s for workgroup %s.\n",
+ server_name,inet_ntoa(p->ip), subrec->subnet_name, workgroup_name ));
+
+ DEBUG(5,("process_election: vers=%d criterion=%08x timeup=%d\n", version,criterion,timeup));
+
+ if(( work = find_workgroup_on_subnet(subrec, workgroup_name)) == NULL) {
+ DEBUG(0,("process_election: Cannot find workgroup %s on subnet %s.\n",
+ workgroup_name, subrec->subnet_name ));
+ goto done;
+ }
+
+ if (!strequal(work->work_group, lp_workgroup())) {
+ DEBUG(3,("process_election: ignoring election request for workgroup %s on subnet %s as this \
+is not my workgroup.\n", work->work_group, subrec->subnet_name ));
+ goto done;
+ }
+
+ if (win_election(work, version,criterion,timeup,server_name)) {
+ /* We take precedence over the requesting server. */
+ if (!work->RunningElection) {
+ /* We weren't running an election - start running one. */
+
+ work->needelection = True;
+ work->ElectionCount=0;
+ }
+
+ /* Note that if we were running an election for this workgroup on this
+ subnet already, we just ignore the server we take precedence over. */
+ } else {
+ /* We lost. Stop participating. */
+ work->needelection = False;
+
+ if (work->RunningElection || AM_LOCAL_MASTER_BROWSER(work)) {
+ work->RunningElection = False;
+ DEBUG(3,("process_election: >>> Lost election for workgroup %s on subnet %s <<<\n",
+ work->work_group, subrec->subnet_name ));
+ if (AM_LOCAL_MASTER_BROWSER(work))
+ unbecome_local_master_browser(subrec, work, False);
+ }
+ }
+done:
+
+ END_PROFILE(election);
+}
+
+/****************************************************************************
+ This function looks over all the workgroups known on all the broadcast
+ subnets and decides if a browser election is to be run on that workgroup.
+ It returns True if any election packets need to be sent (this will then
+ be done by run_elections().
+***************************************************************************/
+
+BOOL check_elections(void)
+{
+ struct subnet_record *subrec;
+ BOOL run_any_election = False;
+
+ for (subrec = FIRST_SUBNET; subrec; subrec = NEXT_SUBNET_EXCLUDING_UNICAST(subrec)) {
+ struct work_record *work;
+ for (work = subrec->workgrouplist; work; work = work->next) {
+ run_any_election |= work->RunningElection;
+
+ /*
+ * Start an election if we have any chance of winning.
+ * Note this is a change to the previous code, that would
+ * only run an election if nmbd was in the potential browser
+ * state. We need to run elections in any state if we're told
+ * to. JRA.
+ */
+
+ if (work->needelection && !work->RunningElection && lp_local_master()) {
+ /*
+ * We can only run an election for a workgroup if we have
+ * registered the WORKGROUP<1e> name, as that's the name
+ * we must listen to.
+ */
+ struct nmb_name nmbname;
+
+ make_nmb_name(&nmbname, work->work_group, 0x1e);
+ if(find_name_on_subnet( subrec, &nmbname, FIND_SELF_NAME)==NULL) {
+ DEBUG(8,("check_elections: Cannot send election packet yet as name %s not \
+yet registered on subnet %s\n", nmb_namestr(&nmbname), subrec->subnet_name ));
+ continue;
+ }
+
+ DEBUG(3,("check_elections: >>> Starting election for workgroup %s on subnet %s <<<\n",
+ work->work_group, subrec->subnet_name ));
+
+ work->ElectionCount = 0;
+ work->RunningElection = True;
+ work->needelection = False;
+ }
+ }
+ }
+ return run_any_election;
+}
+
+/****************************************************************************
+ Process a internal Samba message forcing an election.
+***************************************************************************/
+
+void nmbd_message_election(int msg_type, pid_t src, void *buf, size_t len)
+{
+ struct subnet_record *subrec;
+
+ for (subrec = FIRST_SUBNET; subrec; subrec = NEXT_SUBNET_EXCLUDING_UNICAST(subrec)) {
+ struct work_record *work;
+ for (work = subrec->workgrouplist; work; work = work->next) {
+ if (strequal(work->work_group, lp_workgroup())) {
+ work->needelection = True;
+ work->ElectionCount=0;
+ work->mst_state = lp_local_master() ? MST_POTENTIAL : MST_NONE;
+ }
+ }
+ }
+}
diff --git a/source/nmbd/nmbd_incomingdgrams.c b/source/nmbd/nmbd_incomingdgrams.c
new file mode 100644
index 00000000000..53b19471572
--- /dev/null
+++ b/source/nmbd/nmbd_incomingdgrams.c
@@ -0,0 +1,844 @@
+/*
+ Unix SMB/CIFS implementation.
+ NBT netbios routines and daemon - version 2
+ Copyright (C) Andrew Tridgell 1994-1998
+ Copyright (C) Luke Kenneth Casson Leighton 1994-1998
+ Copyright (C) Jeremy Allison 1994-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 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"
+
+extern BOOL found_lm_clients;
+
+#if 0
+
+/* XXXX note: This function is currently unsuitable for use, as it
+ does not properly check that a server is in a fit state to become
+ a backup browser before asking it to be one.
+ The code is left here to be worked on at a later date.
+*/
+
+/****************************************************************************
+Tell a server to become a backup browser
+**************************************************************************/
+
+void tell_become_backup(void)
+{
+ struct subnet_record *subrec;
+ for (subrec = FIRST_SUBNET; subrec; subrec = NEXT_SUBNET_EXCLUDING_UNICAST(subrec))
+ {
+ struct work_record *work;
+ for (work = subrec->workgrouplist; work; work = work->next)
+ {
+ struct server_record *servrec;
+ int num_servers = 0;
+ int num_backups = 0;
+
+ for (servrec = work->serverlist; servrec; servrec = servrec->next)
+ {
+ num_servers++;
+
+ if (is_myname(servrec->serv.name))
+ continue;
+
+ if (servrec->serv.type & SV_TYPE_BACKUP_BROWSER)
+ {
+ num_backups++;
+ continue;
+ }
+
+ if (servrec->serv.type & SV_TYPE_MASTER_BROWSER)
+ continue;
+
+ if (!(servrec->serv.type & SV_TYPE_POTENTIAL_BROWSER))
+ continue;
+
+ DEBUG(3,("num servers: %d num backups: %d\n",
+ num_servers, num_backups));
+
+ /* make first server a backup server. thereafter make every
+ tenth server a backup server */
+ if (num_backups != 0 && (num_servers+9) / num_backups > 10)
+ continue;
+
+ DEBUG(2,("sending become backup to %s %s for %s\n",
+ servrec->serv.name, inet_ntoa(subrec->bcast_ip),
+ work->work_group));
+
+ /* type 11 request from MYNAME(20) to WG(1e) for SERVER */
+ do_announce_request(servrec->serv.name, work->work_group,
+ ANN_BecomeBackup, 0x20, 0x1e, subrec->bcast_ip);
+ }
+ }
+ }
+}
+#endif
+
+/*******************************************************************
+ Process an incoming host announcement packet.
+*******************************************************************/
+
+void process_host_announce(struct subnet_record *subrec, struct packet_struct *p, char *buf)
+{
+ struct dgram_packet *dgram = &p->packet.dgram;
+ int ttl = IVAL(buf,1)/1000;
+ unstring announce_name;
+ uint32 servertype = IVAL(buf,23);
+ fstring comment;
+ struct work_record *work;
+ struct server_record *servrec;
+ unstring work_name;
+ unstring source_name;
+
+ START_PROFILE(host_announce);
+
+ pull_ascii_fstring(comment, buf+31);
+ comment[42] = 0;
+
+ pull_ascii_nstring(announce_name, sizeof(announce_name), buf+5);
+ pull_ascii_nstring(source_name, sizeof(source_name), dgram->source_name.name);
+
+ DEBUG(3,("process_host_announce: from %s<%02x> IP %s to \
+%s for server %s.\n", source_name, source_name[15], inet_ntoa(p->ip),
+ nmb_namestr(&dgram->dest_name),announce_name));
+
+ DEBUG(5,("process_host_announce: ttl=%d server type=%08x comment=%s\n",
+ ttl, servertype,comment));
+
+ /* Filter servertype to remove impossible bits. */
+ servertype &= ~(SV_TYPE_LOCAL_LIST_ONLY|SV_TYPE_DOMAIN_ENUM);
+
+ /* A host announcement must be sent to the name WORKGROUP<1d>. */
+ if(dgram->dest_name.name_type != 0x1d) {
+ DEBUG(2,("process_host_announce: incorrect name type for destination from IP %s \
+(was %02x) should be 0x1d. Allowing packet anyway.\n",
+ inet_ntoa(p->ip), dgram->dest_name.name_type));
+ /* Change it so it was. */
+ dgram->dest_name.name_type = 0x1d;
+ }
+
+ /* For a host announce the workgroup name is the destination name. */
+ pull_ascii_nstring(work_name, sizeof(work_name), dgram->dest_name.name);
+
+ /*
+ * Syntax servers version 5.1 send HostAnnounce packets to
+ * *THE WRONG NAME*. They send to LOCAL_MASTER_BROWSER_NAME<00>
+ * instead of WORKGROUP<1d> name. So to fix this we check if
+ * the workgroup name is our own name, and if so change it
+ * to be our primary workgroup name.
+ */
+
+ if(strequal(work_name, global_myname()))
+ unstrcpy(work_name,lp_workgroup());
+
+ /*
+ * We are being very agressive here in adding a workgroup
+ * name on the basis of a host announcing itself as being
+ * in that workgroup. Maybe we should wait for the workgroup
+ * announce instead ? JRA.
+ */
+
+ work = find_workgroup_on_subnet(subrec, work_name);
+
+ if(servertype != 0) {
+ if (work ==NULL ) {
+ /* We have no record of this workgroup. Add it. */
+ if((work = create_workgroup_on_subnet(subrec, work_name, ttl))==NULL)
+ goto done;
+ }
+
+ if((servrec = find_server_in_workgroup( work, announce_name))==NULL) {
+ /* If this server is not already in the workgroup, add it. */
+ create_server_on_workgroup(work, announce_name,
+ servertype|SV_TYPE_LOCAL_LIST_ONLY,
+ ttl, comment);
+ } else {
+ /* Update the record. */
+ servrec->serv.type = servertype|SV_TYPE_LOCAL_LIST_ONLY;
+ update_server_ttl( servrec, ttl);
+ fstrcpy(servrec->serv.comment,comment);
+ }
+ } else {
+ /*
+ * This server is announcing it is going down. Remove it from the
+ * workgroup.
+ */
+ if(!is_myname(announce_name) && (work != NULL) &&
+ ((servrec = find_server_in_workgroup( work, announce_name))!=NULL)) {
+ remove_server_from_workgroup( work, servrec);
+ }
+ }
+
+ subrec->work_changed = True;
+done:
+
+ END_PROFILE(host_announce);
+}
+
+/*******************************************************************
+ Process an incoming WORKGROUP announcement packet.
+*******************************************************************/
+
+void process_workgroup_announce(struct subnet_record *subrec, struct packet_struct *p, char *buf)
+{
+ struct dgram_packet *dgram = &p->packet.dgram;
+ int ttl = IVAL(buf,1)/1000;
+ unstring workgroup_announce_name;
+ unstring master_name;
+ uint32 servertype = IVAL(buf,23);
+ struct work_record *work;
+ unstring source_name;
+ unstring dest_name;
+
+ START_PROFILE(workgroup_announce);
+
+ pull_ascii_nstring(workgroup_announce_name,sizeof(workgroup_announce_name),buf+5);
+ pull_ascii_nstring(master_name,sizeof(master_name),buf+31);
+ pull_ascii_nstring(source_name,sizeof(source_name),dgram->source_name.name);
+ pull_ascii_nstring(dest_name,sizeof(dest_name),dgram->dest_name.name);
+
+ DEBUG(3,("process_workgroup_announce: from %s<%02x> IP %s to \
+%s for workgroup %s.\n", source_name, source_name[15], inet_ntoa(p->ip),
+ nmb_namestr(&dgram->dest_name),workgroup_announce_name));
+
+ DEBUG(5,("process_workgroup_announce: ttl=%d server type=%08x master browser=%s\n",
+ ttl, servertype, master_name));
+
+ /* Workgroup announcements must only go to the MSBROWSE name. */
+ if (!strequal(dest_name, MSBROWSE) || (dgram->dest_name.name_type != 0x1)) {
+ DEBUG(0,("process_workgroup_announce: from IP %s should be to __MSBROWSE__<0x01> not %s\n",
+ inet_ntoa(p->ip), nmb_namestr(&dgram->dest_name)));
+ goto done;
+ }
+
+ if ((work = find_workgroup_on_subnet(subrec, workgroup_announce_name))==NULL) {
+ /* We have no record of this workgroup. Add it. */
+ if((work = create_workgroup_on_subnet(subrec, workgroup_announce_name, ttl))==NULL)
+ goto done;
+ } else {
+ /* Update the workgroup death_time. */
+ update_workgroup_ttl(work, ttl);
+ }
+
+ if(*work->local_master_browser_name == '\0') {
+ /* Set the master browser name. */
+ set_workgroup_local_master_browser_name( work, master_name );
+ }
+
+ subrec->work_changed = True;
+
+done:
+
+ END_PROFILE(workgroup_announce);
+}
+
+/*******************************************************************
+ Process an incoming local master browser announcement packet.
+*******************************************************************/
+
+void process_local_master_announce(struct subnet_record *subrec, struct packet_struct *p, char *buf)
+{
+ struct dgram_packet *dgram = &p->packet.dgram;
+ int ttl = IVAL(buf,1)/1000;
+ unstring server_name;
+ uint32 servertype = IVAL(buf,23);
+ fstring comment;
+ unstring work_name;
+ struct work_record *work;
+ struct server_record *servrec;
+ unstring source_name;
+
+ START_PROFILE(local_master_announce);
+
+ pull_ascii_nstring(server_name,sizeof(server_name),buf+5);
+ pull_ascii_fstring(comment, buf+31);
+ comment[42] = 0;
+ pull_ascii_nstring(source_name, sizeof(source_name), dgram->source_name.name);
+ pull_ascii_nstring(work_name, sizeof(work_name), dgram->dest_name.name);
+
+ DEBUG(3,("process_local_master_announce: from %s<%02x> IP %s to \
+%s for server %s.\n", source_name, source_name[15], inet_ntoa(p->ip),
+ nmb_namestr(&dgram->dest_name),server_name));
+
+ DEBUG(5,("process_local_master_announce: ttl=%d server type=%08x comment=%s\n",
+ ttl, servertype, comment));
+
+ /* A local master announcement must be sent to the name WORKGROUP<1e>. */
+ if(dgram->dest_name.name_type != 0x1e) {
+ DEBUG(0,("process_local_master_announce: incorrect name type for destination from IP %s \
+(was %02x) should be 0x1e. Ignoring packet.\n",
+ inet_ntoa(p->ip), dgram->dest_name.name_type));
+ goto done;
+ }
+
+ /* Filter servertype to remove impossible bits. */
+ servertype &= ~(SV_TYPE_LOCAL_LIST_ONLY|SV_TYPE_DOMAIN_ENUM);
+
+ /* For a local master announce the workgroup name is the destination name. */
+
+ if ((work = find_workgroup_on_subnet(subrec, work_name))==NULL) {
+ /* Don't bother adding if it's a local master release announce. */
+ if(servertype == 0)
+ goto done;
+
+ /* We have no record of this workgroup. Add it. */
+ if((work = create_workgroup_on_subnet(subrec, work_name, ttl))==NULL)
+ goto done;
+ }
+
+ /* If we think we're the local master browser for this workgroup,
+ we should never have got this packet. We don't see our own
+ packets.
+ */
+ if(AM_LOCAL_MASTER_BROWSER(work)) {
+ DEBUG(0,("process_local_master_announce: Server %s at IP %s is announcing itself as \
+a local master browser for workgroup %s and we think we are master. Forcing election.\n",
+ server_name, inet_ntoa(p->ip), work_name));
+
+ /* Samba nmbd versions 1.9.17 to 1.9.17p4 have a bug in that when
+ they have become a local master browser once, they will never
+ stop sending local master announcements. To fix this we send
+ them a reset browser packet, with level 0x2 on the __SAMBA__
+ name that only they should be listening to. */
+
+ send_browser_reset( 0x2, "__SAMBA__" , 0x20, p->ip);
+
+ /* We should demote ourself and force an election. */
+
+ unbecome_local_master_browser( subrec, work, True);
+
+ /* The actual election requests are handled in nmbd_election.c */
+ goto done;
+ }
+
+ /* Find the server record on this workgroup. If it doesn't exist, add it. */
+
+ if(servertype != 0) {
+ if((servrec = find_server_in_workgroup( work, server_name))==NULL) {
+ /* If this server is not already in the workgroup, add it. */
+ create_server_on_workgroup(work, server_name,
+ servertype|SV_TYPE_LOCAL_LIST_ONLY,
+ ttl, comment);
+ } else {
+ /* Update the record. */
+ servrec->serv.type = servertype|SV_TYPE_LOCAL_LIST_ONLY;
+ update_server_ttl(servrec, ttl);
+ fstrcpy(servrec->serv.comment,comment);
+ }
+
+ set_workgroup_local_master_browser_name( work, server_name );
+ } else {
+ /*
+ * This server is announcing it is going down. Remove it from the
+ * workgroup.
+ */
+ if(!is_myname(server_name) && (work != NULL) &&
+ ((servrec = find_server_in_workgroup( work, server_name))!=NULL)) {
+ remove_server_from_workgroup( work, servrec);
+ }
+ }
+
+ subrec->work_changed = True;
+done:
+
+ END_PROFILE(local_master_announce);
+}
+
+/*******************************************************************
+ Process a domain master announcement frame.
+ Domain master browsers receive these from local masters. The Domain
+ master should then issue a sync with the local master, asking for
+ that machines local server list.
+******************************************************************/
+
+void process_master_browser_announce(struct subnet_record *subrec,
+ struct packet_struct *p,char *buf)
+{
+ unstring local_master_name;
+ struct work_record *work;
+ struct browse_cache_record *browrec;
+
+ START_PROFILE(master_browser_announce);
+
+ pull_ascii_nstring(local_master_name,sizeof(local_master_name),buf);
+
+ DEBUG(3,("process_master_browser_announce: Local master announce from %s IP %s.\n",
+ local_master_name, inet_ntoa(p->ip)));
+
+ if (!lp_domain_master()) {
+ DEBUG(0,("process_master_browser_announce: Not configured as domain \
+master - ignoring master announce.\n"));
+ goto done;
+ }
+
+ if((work = find_workgroup_on_subnet(subrec, lp_workgroup())) == NULL) {
+ DEBUG(0,("process_master_browser_announce: Cannot find workgroup %s on subnet %s\n",
+ lp_workgroup(), subrec->subnet_name));
+ goto done;
+ }
+
+ if(!AM_DOMAIN_MASTER_BROWSER(work)) {
+ DEBUG(0,("process_master_browser_announce: Local master announce made to us from \
+%s IP %s and we are not a domain master browser.\n", local_master_name, inet_ntoa(p->ip)));
+ goto done;
+ }
+
+ /* Add this host as a local master browser entry on the browse lists.
+ This causes a sync request to be made to it at a later date.
+ */
+
+ if((browrec = find_browser_in_lmb_cache( local_master_name )) == NULL) {
+ /* Add it. */
+ create_browser_in_lmb_cache( work->work_group, local_master_name, p->ip);
+ } else {
+ update_browser_death_time(browrec);
+ }
+
+done:
+
+ END_PROFILE(master_browser_announce);
+}
+
+/*******************************************************************
+ Process an incoming LanMan host announcement packet.
+*******************************************************************/
+
+void process_lm_host_announce(struct subnet_record *subrec, struct packet_struct *p, char *buf)
+{
+ struct dgram_packet *dgram = &p->packet.dgram;
+ uint32 servertype = IVAL(buf,1);
+ int osmajor=CVAL(buf,5); /* major version of node software */
+ int osminor=CVAL(buf,6); /* minor version of node software */
+ int ttl = SVAL(buf,7);
+ unstring announce_name;
+ struct work_record *work;
+ struct server_record *servrec;
+ unstring work_name;
+ unstring source_name;
+ fstring comment;
+ char *s = buf+9;
+
+ START_PROFILE(lm_host_announce);
+ s = skip_string(s,1);
+ pull_ascii(comment, s, sizeof(fstring), 43, STR_TERMINATE);
+
+ pull_ascii_nstring(announce_name,sizeof(announce_name),buf+9);
+ pull_ascii_nstring(source_name,sizeof(source_name),dgram->source_name.name);
+ /* For a LanMan host announce the workgroup name is the destination name. */
+ pull_ascii_nstring(work_name,sizeof(work_name),dgram->dest_name.name);
+
+ DEBUG(3,("process_lm_host_announce: LM Announcement from %s IP %s to \
+%s for server %s.\n", nmb_namestr(&dgram->source_name), inet_ntoa(p->ip),
+ nmb_namestr(&dgram->dest_name),announce_name));
+
+ DEBUG(5,("process_lm_host_announce: os=(%d,%d) ttl=%d server type=%08x comment=%s\n",
+ osmajor, osminor, ttl, servertype,comment));
+
+ if ((osmajor < 36) || (osmajor > 38) || (osminor !=0)) {
+ DEBUG(5,("process_lm_host_announce: LM Announcement packet does not \
+originate from OS/2 Warp client. Ignoring packet.\n"));
+ /* Could have been from a Windows machine (with its LM Announce enabled),
+ or a Samba server. Then don't disrupt the current browse list. */
+ goto done;
+ }
+
+ /* Filter servertype to remove impossible bits. */
+ servertype &= ~(SV_TYPE_LOCAL_LIST_ONLY|SV_TYPE_DOMAIN_ENUM);
+
+ /* A LanMan host announcement must be sent to the name WORKGROUP<00>. */
+ if(dgram->dest_name.name_type != 0x00) {
+ DEBUG(2,("process_lm_host_announce: incorrect name type for destination from IP %s \
+(was %02x) should be 0x00. Allowing packet anyway.\n",
+ inet_ntoa(p->ip), dgram->dest_name.name_type));
+ /* Change it so it was. */
+ dgram->dest_name.name_type = 0x00;
+ }
+
+ /*
+ * Syntax servers version 5.1 send HostAnnounce packets to
+ * *THE WRONG NAME*. They send to LOCAL_MASTER_BROWSER_NAME<00>
+ * instead of WORKGROUP<1d> name. So to fix this we check if
+ * the workgroup name is our own name, and if so change it
+ * to be our primary workgroup name. This code is probably
+ * not needed in the LanMan announce code, but it won't hurt.
+ */
+
+ if(strequal(work_name, global_myname()))
+ unstrcpy(work_name,lp_workgroup());
+
+ /*
+ * We are being very agressive here in adding a workgroup
+ * name on the basis of a host announcing itself as being
+ * in that workgroup. Maybe we should wait for the workgroup
+ * announce instead ? JRA.
+ */
+
+ work = find_workgroup_on_subnet(subrec, work_name);
+
+ if(servertype != 0) {
+ if (work == NULL) {
+ /* We have no record of this workgroup. Add it. */
+ if((work = create_workgroup_on_subnet(subrec, work_name, ttl))==NULL)
+ goto done;
+ }
+
+ if((servrec = find_server_in_workgroup( work, announce_name))==NULL) {
+ /* If this server is not already in the workgroup, add it. */
+ create_server_on_workgroup(work, announce_name,
+ servertype|SV_TYPE_LOCAL_LIST_ONLY,
+ ttl, comment);
+ } else {
+ /* Update the record. */
+ servrec->serv.type = servertype|SV_TYPE_LOCAL_LIST_ONLY;
+ update_server_ttl( servrec, ttl);
+ fstrcpy(servrec->serv.comment,comment);
+ }
+ } else {
+ /*
+ * This server is announcing it is going down. Remove it from the
+ * workgroup.
+ */
+ if(!is_myname(announce_name) && (work != NULL) &&
+ ((servrec = find_server_in_workgroup( work, announce_name))!=NULL)) {
+ remove_server_from_workgroup( work, servrec);
+ }
+ }
+
+ subrec->work_changed = True;
+ found_lm_clients = True;
+
+done:
+
+ END_PROFILE(lm_host_announce);
+}
+
+/****************************************************************************
+ Send a backup list response.
+*****************************************************************************/
+
+static void send_backup_list_response(struct subnet_record *subrec,
+ struct work_record *work,
+ struct nmb_name *send_to_name,
+ unsigned char max_number_requested,
+ uint32 token, struct in_addr sendto_ip,
+ int port)
+{
+ char outbuf[1024];
+ char *p, *countptr;
+ unsigned int count = 0;
+ unstring send_to_namestr;
+#if 0
+ struct server_record *servrec;
+#endif
+ unstring myname;
+
+ memset(outbuf,'\0',sizeof(outbuf));
+
+ DEBUG(3,("send_backup_list_response: sending backup list for workgroup %s to %s IP %s\n",
+ work->work_group, nmb_namestr(send_to_name), inet_ntoa(sendto_ip)));
+
+ p = outbuf;
+
+ SCVAL(p,0,ANN_GetBackupListResp); /* Backup list response opcode. */
+ p++;
+
+ countptr = p;
+ p++;
+
+ SIVAL(p,0,token); /* The sender's unique info. */
+ p += 4;
+
+ /* We always return at least one name - our own. */
+ count = 1;
+ unstrcpy(myname, global_myname());
+ strupper_m(myname);
+ myname[15]='\0';
+ push_pstring_base(p, myname, outbuf);
+
+ p = skip_string(p,1);
+
+ /* Look for backup browsers in this workgroup. */
+
+#if 0
+ /* we don't currently send become_backup requests so we should never
+ send any other servers names out as backups for our
+ workgroup. That's why this is commented out (tridge) */
+
+ /*
+ * NB. Note that the struct work_record here is not neccessarily
+ * attached to the subnet *subrec.
+ */
+
+ for (servrec = work->serverlist; servrec; servrec = servrec->next)
+ {
+ int len = PTR_DIFF(p, outbuf);
+ if((sizeof(outbuf) - len) < 16)
+ break;
+
+ if(count >= (unsigned int)max_number_requested)
+ break;
+
+ if(strnequal(servrec->serv.name, global_myname(),15))
+ continue;
+
+ if(!(servrec->serv.type & SV_TYPE_BACKUP_BROWSER))
+ continue;
+
+ StrnCpy(p, servrec->serv.name, 15);
+ strupper_m(p);
+ count++;
+
+ DEBUG(5,("send_backup_list_response: Adding server %s number %d\n",
+ p, count));
+
+ p = skip_string(p,1);
+ }
+#endif
+
+ SCVAL(countptr, 0, count);
+
+ pull_ascii_nstring(send_to_namestr, sizeof(send_to_namestr), send_to_name->name);
+
+ DEBUG(4,("send_backup_list_response: sending response to %s<00> IP %s with %d servers.\n",
+ send_to_namestr, inet_ntoa(sendto_ip), count));
+
+ send_mailslot(True, BROWSE_MAILSLOT,
+ outbuf,PTR_DIFF(p,outbuf),
+ global_myname(), 0,
+ send_to_namestr,0,
+ sendto_ip, subrec->myip, port);
+}
+
+/*******************************************************************
+ Process a send backup list request packet.
+
+ A client sends a backup list request to ask for a list of servers on
+ the net that maintain server lists for a domain. A server is then
+ chosen from this list to send NetServerEnum commands to to list
+ available servers.
+
+********************************************************************/
+
+void process_get_backup_list_request(struct subnet_record *subrec,
+ struct packet_struct *p,char *buf)
+{
+ struct dgram_packet *dgram = &p->packet.dgram;
+ struct work_record *work;
+ unsigned char max_number_requested = CVAL(buf,0);
+ uint32 token = IVAL(buf,1); /* Sender's key index for the workgroup. */
+ int name_type = dgram->dest_name.name_type;
+ unstring workgroup_name;
+ struct subnet_record *search_subrec = subrec;
+
+ START_PROFILE(get_backup_list);
+ pull_ascii_nstring(workgroup_name, sizeof(workgroup_name), dgram->dest_name.name);
+
+ DEBUG(3,("process_get_backup_list_request: request from %s IP %s to %s.\n",
+ nmb_namestr(&dgram->source_name), inet_ntoa(p->ip),
+ nmb_namestr(&dgram->dest_name)));
+
+ /* We have to be a master browser, or a domain master browser
+ for the requested workgroup. That means it must be our
+ workgroup. */
+
+ if(strequal(workgroup_name, lp_workgroup()) == False) {
+ DEBUG(7,("process_get_backup_list_request: Ignoring announce request for workgroup %s.\n",
+ workgroup_name));
+ goto done;
+ }
+
+ if((work = find_workgroup_on_subnet(search_subrec, workgroup_name)) == NULL) {
+ DEBUG(0,("process_get_backup_list_request: Cannot find workgroup %s on \
+subnet %s.\n", workgroup_name, search_subrec->subnet_name));
+ goto done;
+ }
+
+ /*
+ * If the packet was sent to WORKGROUP<1b> instead
+ * of WORKGROUP<1d> then it was unicast to us a domain master
+ * browser. Change search subrec to unicast.
+ */
+
+ if(name_type == 0x1b) {
+ /* We must be a domain master browser in order to
+ process this packet. */
+
+ if(!AM_DOMAIN_MASTER_BROWSER(work)) {
+ DEBUG(0,("process_get_backup_list_request: domain list requested for workgroup %s \
+and I am not a domain master browser.\n", workgroup_name));
+ goto done;
+ }
+
+ search_subrec = unicast_subnet;
+ } else if (name_type == 0x1d) {
+ /* We must be a local master browser in order to process this packet. */
+
+ if(!AM_LOCAL_MASTER_BROWSER(work)) {
+ DEBUG(0,("process_get_backup_list_request: domain list requested for workgroup %s \
+and I am not a local master browser.\n", workgroup_name));
+ goto done;
+ }
+ } else {
+ DEBUG(0,("process_get_backup_list_request: Invalid name type %x - should be 0x1b or 0x1d.\n",
+ name_type));
+ goto done;
+ }
+
+ send_backup_list_response(subrec, work, &dgram->source_name,
+ max_number_requested, token, p->ip, p->port);
+
+done:
+
+ END_PROFILE(get_backup_list);
+}
+
+/*******************************************************************
+ Process a reset browser state packet.
+
+ Diagnostic packet:
+ 0x1 - Stop being a master browser and become a backup browser.
+ 0x2 - Discard browse lists, stop being a master browser, try again.
+ 0x4 - Stop being a master browser forever.
+
+******************************************************************/
+
+void process_reset_browser(struct subnet_record *subrec,
+ struct packet_struct *p,char *buf)
+{
+ struct dgram_packet *dgram = &p->packet.dgram;
+ int state = CVAL(buf,0);
+ struct subnet_record *sr;
+
+ START_PROFILE(reset_browser);
+
+ DEBUG(1,("process_reset_browser: received diagnostic browser reset \
+request from %s IP %s state=0x%X\n",
+ nmb_namestr(&dgram->source_name), inet_ntoa(p->ip), state));
+
+ /* Stop being a local master browser on all our broadcast subnets. */
+ if (state & 0x1) {
+ for (sr = FIRST_SUBNET; sr; sr = NEXT_SUBNET_EXCLUDING_UNICAST(sr)) {
+ struct work_record *work;
+ for (work = sr->workgrouplist; work; work = work->next) {
+ if (AM_LOCAL_MASTER_BROWSER(work))
+ unbecome_local_master_browser(sr, work, True);
+ }
+ }
+ }
+
+ /* Discard our browse lists. */
+ if (state & 0x2) {
+ /*
+ * Calling expire_workgroups_and_servers with a -1
+ * time causes all servers not marked with a PERMANENT_TTL
+ * on the workgroup lists to be discarded, and all
+ * workgroups with empty server lists to be discarded.
+ * This means we keep our own server names and workgroup
+ * as these have a PERMANENT_TTL.
+ */
+
+ expire_workgroups_and_servers(-1);
+ }
+
+ /* Request to stop browsing altogether. */
+ if (state & 0x4)
+ DEBUG(1,("process_reset_browser: ignoring request to stop being a browser.\n"));
+
+ END_PROFILE(reset_browser);
+}
+
+/*******************************************************************
+ Process an announcement request packet.
+ We don't respond immediately, we just check it's a request for
+ our workgroup and then set the flag telling the announce code
+ in nmbd_sendannounce.c:announce_my_server_names that an
+ announcement is needed soon.
+******************************************************************/
+
+void process_announce_request(struct subnet_record *subrec, struct packet_struct *p, char *buf)
+{
+ struct dgram_packet *dgram = &p->packet.dgram;
+ struct work_record *work;
+ unstring workgroup_name;
+
+ START_PROFILE(announce_request);
+
+ pull_ascii_nstring(workgroup_name, sizeof(workgroup_name), dgram->dest_name.name);
+ DEBUG(3,("process_announce_request: Announce request from %s IP %s to %s.\n",
+ nmb_namestr(&dgram->source_name), inet_ntoa(p->ip),
+ nmb_namestr(&dgram->dest_name)));
+
+ /* We only send announcement requests on our workgroup. */
+ if(strequal(workgroup_name, lp_workgroup()) == False) {
+ DEBUG(7,("process_announce_request: Ignoring announce request for workgroup %s.\n",
+ workgroup_name));
+ goto done;
+ }
+
+ if((work = find_workgroup_on_subnet(subrec, workgroup_name)) == NULL) {
+ DEBUG(0,("process_announce_request: Unable to find workgroup %s on subnet !\n",
+ workgroup_name));
+ goto done;
+ }
+
+ work->needannounce = True;
+done:
+
+ END_PROFILE(lm_host_announce);
+}
+
+/*******************************************************************
+ Process a LanMan announcement request packet.
+ We don't respond immediately, we just check it's a request for
+ our workgroup and then set the flag telling that we have found
+ a LanMan client (DOS or OS/2) and that we will have to start
+ sending LanMan announcements (unless specifically disabled
+ through the "lm announce" parameter in smb.conf)
+******************************************************************/
+
+void process_lm_announce_request(struct subnet_record *subrec, struct packet_struct *p, char *buf)
+{
+ struct dgram_packet *dgram = &p->packet.dgram;
+ unstring workgroup_name;
+
+ START_PROFILE(lm_announce_request);
+
+ pull_ascii_nstring(workgroup_name, sizeof(workgroup_name), dgram->dest_name.name);
+ DEBUG(3,("process_lm_announce_request: Announce request from %s IP %s to %s.\n",
+ nmb_namestr(&dgram->source_name), inet_ntoa(p->ip),
+ nmb_namestr(&dgram->dest_name)));
+
+ /* We only send announcement requests on our workgroup. */
+ if(strequal(workgroup_name, lp_workgroup()) == False) {
+ DEBUG(7,("process_lm_announce_request: Ignoring announce request for workgroup %s.\n",
+ workgroup_name));
+ goto done;
+ }
+
+ if(find_workgroup_on_subnet(subrec, workgroup_name) == NULL) {
+ DEBUG(0,("process_announce_request: Unable to find workgroup %s on subnet !\n",
+ workgroup_name));
+ goto done;
+ }
+
+ found_lm_clients = True;
+
+done:
+
+ END_PROFILE(lm_host_announce);
+}
diff --git a/source/nmbd/nmbd_incomingrequests.c b/source/nmbd/nmbd_incomingrequests.c
new file mode 100644
index 00000000000..9214594096c
--- /dev/null
+++ b/source/nmbd/nmbd_incomingrequests.c
@@ -0,0 +1,587 @@
+/*
+ Unix SMB/CIFS implementation.
+ NBT netbios routines and daemon - version 2
+ Copyright (C) Andrew Tridgell 1994-1998
+ Copyright (C) Luke Kenneth Casson Leighton 1994-1998
+ Copyright (C) Jeremy Allison 1994-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 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.
+
+ This file contains all the code to process NetBIOS requests coming
+ in on port 137. It does not deal with the code needed to service
+ WINS server requests, but only broadcast and unicast requests.
+
+*/
+
+#include "includes.h"
+
+/****************************************************************************
+Send a name release response.
+**************************************************************************/
+
+static void send_name_release_response(int rcode, struct packet_struct *p)
+{
+ struct nmb_packet *nmb = &p->packet.nmb;
+ char rdata[6];
+
+ memcpy(&rdata[0], &nmb->additional->rdata[0], 6);
+
+ reply_netbios_packet(p, /* Packet to reply to. */
+ rcode, /* Result code. */
+ NMB_REL, /* nmbd type code. */
+ NMB_NAME_RELEASE_OPCODE, /* opcode. */
+ 0, /* ttl. */
+ rdata, /* data to send. */
+ 6); /* data length. */
+}
+
+/****************************************************************************
+Process a name release packet on a broadcast subnet.
+Ignore it if it's not one of our names.
+****************************************************************************/
+
+void process_name_release_request(struct subnet_record *subrec,
+ struct packet_struct *p)
+{
+ struct nmb_packet *nmb = &p->packet.nmb;
+ struct in_addr owner_ip;
+ struct nmb_name *question = &nmb->question.question_name;
+ unstring qname;
+ BOOL bcast = nmb->header.nm_flags.bcast;
+ uint16 nb_flags = get_nb_flags(nmb->additional->rdata);
+ BOOL group = (nb_flags & NB_GROUP) ? True : False;
+ struct name_record *namerec;
+ int rcode = 0;
+
+ putip((char *)&owner_ip,&nmb->additional->rdata[2]);
+
+ if(!bcast) {
+ /* We should only get broadcast name release packets here.
+ Anyone trying to release unicast should be going to a WINS
+ server. If the code gets here, then either we are not a wins
+ server and they sent it anyway, or we are a WINS server and
+ the request was malformed. Either way, log an error here.
+ and send an error reply back.
+ */
+ DEBUG(0,("process_name_release_request: unicast name release request \
+received for name %s from IP %s on subnet %s. Error - should be sent to WINS server\n",
+ nmb_namestr(question), inet_ntoa(owner_ip), subrec->subnet_name));
+
+ send_name_release_response(FMT_ERR, p);
+ return;
+ }
+
+ DEBUG(3,("process_name_release_request: Name release on name %s, \
+subnet %s from owner IP %s\n",
+ nmb_namestr(&nmb->question.question_name),
+ subrec->subnet_name, inet_ntoa(owner_ip)));
+
+ /* If someone is releasing a broadcast group name, just ignore it. */
+ if( group && !ismyip(owner_ip) )
+ return;
+
+ /*
+ * Code to work around a bug in FTP OnNet software NBT implementation.
+ * They do a broadcast name release for WORKGROUP<0> and WORKGROUP<1e>
+ * names and *don't set the group bit* !!!!!
+ */
+
+ pull_ascii_nstring(qname, sizeof(qname), question->name);
+ if( !group && !ismyip(owner_ip) && strequal(qname, lp_workgroup()) &&
+ ((question->name_type == 0x0) || (question->name_type == 0x1e))) {
+ DEBUG(6,("process_name_release_request: FTP OnNet bug workaround. Ignoring \
+group release name %s from IP %s on subnet %s with no group bit set.\n",
+ nmb_namestr(question), inet_ntoa(owner_ip), subrec->subnet_name ));
+ return;
+ }
+
+ namerec = find_name_on_subnet(subrec, question, FIND_ANY_NAME);
+
+ /* We only care about someone trying to release one of our names. */
+ if( namerec && ( (namerec->data.source == SELF_NAME)
+ || (namerec->data.source == PERMANENT_NAME) ) ) {
+ rcode = ACT_ERR;
+ DEBUG(0, ("process_name_release_request: Attempt to release name %s from IP %s \
+on subnet %s being rejected as it is one of our names.\n",
+ nmb_namestr(&nmb->question.question_name), inet_ntoa(owner_ip), subrec->subnet_name));
+ }
+
+ if(rcode == 0)
+ return;
+
+ /* Send a NAME RELEASE RESPONSE (pos/neg) see rfc1002.txt 4.2.10-11 */
+ send_name_release_response(rcode, p);
+}
+
+/****************************************************************************
+Send a name registration response.
+**************************************************************************/
+
+static void send_name_registration_response(int rcode, int ttl, struct packet_struct *p)
+{
+ struct nmb_packet *nmb = &p->packet.nmb;
+ char rdata[6];
+
+ memcpy(&rdata[0], &nmb->additional->rdata[0], 6);
+
+ reply_netbios_packet(p, /* Packet to reply to. */
+ rcode, /* Result code. */
+ NMB_REG, /* nmbd type code. */
+ NMB_NAME_REG_OPCODE, /* opcode. */
+ ttl, /* ttl. */
+ rdata, /* data to send. */
+ 6); /* data length. */
+}
+
+/****************************************************************************
+Process a name refresh request on a broadcast subnet.
+**************************************************************************/
+
+void process_name_refresh_request(struct subnet_record *subrec,
+ struct packet_struct *p)
+{
+ struct nmb_packet *nmb = &p->packet.nmb;
+ struct nmb_name *question = &nmb->question.question_name;
+ BOOL bcast = nmb->header.nm_flags.bcast;
+ struct in_addr from_ip;
+
+ putip((char *)&from_ip,&nmb->additional->rdata[2]);
+
+ if(!bcast) {
+ /* We should only get broadcast name refresh packets here.
+ Anyone trying to refresh unicast should be going to a WINS
+ server. If the code gets here, then either we are not a wins
+ server and they sent it anyway, or we are a WINS server and
+ the request was malformed. Either way, log an error here.
+ and send an error reply back.
+ */
+ DEBUG(0,("process_name_refresh_request: unicast name registration request \
+received for name %s from IP %s on subnet %s.\n",
+ nmb_namestr(question), inet_ntoa(from_ip), subrec->subnet_name));
+ DEBUG(0,("Error - should be sent to WINS server\n"));
+
+ send_name_registration_response(FMT_ERR, 0, p);
+ return;
+ }
+
+ /* Just log a message. We really don't care about broadcast name refreshes. */
+
+ DEBUG(3,("process_name_refresh_request: Name refresh for name %s \
+IP %s on subnet %s\n", nmb_namestr(question), inet_ntoa(from_ip), subrec->subnet_name));
+}
+
+/****************************************************************************
+Process a name registration request on a broadcast subnet.
+**************************************************************************/
+
+void process_name_registration_request(struct subnet_record *subrec,
+ struct packet_struct *p)
+{
+ struct nmb_packet *nmb = &p->packet.nmb;
+ struct nmb_name *question = &nmb->question.question_name;
+ BOOL bcast = nmb->header.nm_flags.bcast;
+ uint16 nb_flags = get_nb_flags(nmb->additional->rdata);
+ BOOL group = (nb_flags & NB_GROUP) ? True : False;
+ struct name_record *namerec = NULL;
+ int ttl = nmb->additional->ttl;
+ struct in_addr from_ip;
+
+ putip((char *)&from_ip,&nmb->additional->rdata[2]);
+
+ if(!bcast) {
+ /* We should only get broadcast name registration packets here.
+ Anyone trying to register unicast should be going to a WINS
+ server. If the code gets here, then either we are not a wins
+ server and they sent it anyway, or we are a WINS server and
+ the request was malformed. Either way, log an error here.
+ and send an error reply back.
+ */
+ DEBUG(0,("process_name_registration_request: unicast name registration request \
+received for name %s from IP %s on subnet %s. Error - should be sent to WINS server\n",
+ nmb_namestr(question), inet_ntoa(from_ip), subrec->subnet_name));
+
+ send_name_registration_response(FMT_ERR, 0, p);
+ return;
+ }
+
+ DEBUG(3,("process_name_registration_request: Name registration for name %s \
+IP %s on subnet %s\n", nmb_namestr(question), inet_ntoa(from_ip), subrec->subnet_name));
+
+ /* See if the name already exists. */
+ namerec = find_name_on_subnet(subrec, question, FIND_ANY_NAME);
+
+ /*
+ * If the name being registered exists and is a WINS_PROXY_NAME
+ * then delete the WINS proxy name entry so we don't reply erroneously
+ * later to queries.
+ */
+
+ if((namerec != NULL) && (namerec->data.source == WINS_PROXY_NAME)) {
+ remove_name_from_namelist( subrec, namerec );
+ namerec = NULL;
+ }
+
+ if (!group) {
+ /* Unique name. */
+
+ if( (namerec != NULL)
+ && ( (namerec->data.source == SELF_NAME)
+ || (namerec->data.source == PERMANENT_NAME)
+ || NAME_GROUP(namerec) ) ) {
+ /* No-one can register one of Samba's names, nor can they
+ register a name that's a group name as a unique name */
+
+ send_name_registration_response(ACT_ERR, 0, p);
+ return;
+ } else if(namerec != NULL) {
+ /* Update the namelist record with the new information. */
+ namerec->data.ip[0] = from_ip;
+ update_name_ttl(namerec, ttl);
+
+ DEBUG(3,("process_name_registration_request: Updated name record %s \
+with IP %s on subnet %s\n",nmb_namestr(&namerec->name),inet_ntoa(from_ip), subrec->subnet_name));
+ return;
+ }
+ } else {
+ /* Group name. */
+
+ if( (namerec != NULL)
+ && !NAME_GROUP(namerec)
+ && ( (namerec->data.source == SELF_NAME)
+ || (namerec->data.source == PERMANENT_NAME) ) ) {
+ /* Disallow group names when we have a unique name. */
+ send_name_registration_response(ACT_ERR, 0, p);
+ return;
+ }
+ }
+}
+
+/****************************************************************************
+This is used to sort names for a name status into a sensible order.
+We put our own names first, then in alphabetical order.
+**************************************************************************/
+
+static int status_compare(char *n1,char *n2)
+{
+ unstring name1, name2;
+ int l1,l2,l3;
+
+ memset(name1, '\0', sizeof(name1));
+ memset(name2, '\0', sizeof(name2));
+ pull_ascii_nstring(name1, sizeof(name1), n1);
+ pull_ascii_nstring(name2, sizeof(name2), n2);
+ n1 = name1;
+ n2 = name2;
+
+ /* It's a bit tricky because the names are space padded */
+ for (l1=0;l1<15 && n1[l1] && n1[l1] != ' ';l1++)
+ ;
+ for (l2=0;l2<15 && n2[l2] && n2[l2] != ' ';l2++)
+ ;
+ l3 = strlen(global_myname());
+
+ if ((l1==l3) && strncmp(n1,global_myname(),l3) == 0 &&
+ (l2!=l3 || strncmp(n2,global_myname(),l3) != 0))
+ return -1;
+
+ if ((l2==l3) && strncmp(n2,global_myname(),l3) == 0 &&
+ (l1!=l3 || strncmp(n1,global_myname(),l3) != 0))
+ return 1;
+
+ return memcmp(n1,n2,sizeof(name1));
+}
+
+/****************************************************************************
+ Process a node status query
+ ****************************************************************************/
+
+void process_node_status_request(struct subnet_record *subrec, struct packet_struct *p)
+{
+ struct nmb_packet *nmb = &p->packet.nmb;
+ unstring qname;
+ int ques_type = nmb->question.question_name.name_type;
+ char rdata[MAX_DGRAM_SIZE];
+ char *countptr, *buf, *bufend, *buf0;
+ int names_added,i;
+ struct name_record *namerec;
+
+ pull_ascii_nstring(qname, sizeof(qname), nmb->question.question_name.name);
+
+ DEBUG(3,("process_node_status_request: status request for name %s from IP %s on \
+subnet %s.\n", nmb_namestr(&nmb->question.question_name), inet_ntoa(p->ip), subrec->subnet_name));
+
+ if((namerec = find_name_on_subnet(subrec, &nmb->question.question_name, FIND_SELF_NAME)) == 0) {
+ DEBUG(1,("process_node_status_request: status request for name %s from IP %s on \
+subnet %s - name not found.\n", nmb_namestr(&nmb->question.question_name),
+ inet_ntoa(p->ip), subrec->subnet_name));
+
+ return;
+ }
+
+ /* this is not an exact calculation. the 46 is for the stats buffer
+ and the 60 is to leave room for the header etc */
+ bufend = &rdata[MAX_DGRAM_SIZE] - (18 + 46 + 60);
+ countptr = buf = rdata;
+ buf += 1;
+ buf0 = buf;
+
+ names_added = 0;
+
+ namerec = (struct name_record *)ubi_trFirst( subrec->namelist );
+
+ while (buf < bufend) {
+ if( (namerec->data.source == SELF_NAME) || (namerec->data.source == PERMANENT_NAME) ) {
+ int name_type = namerec->name.name_type;
+ unstring name;
+
+ pull_ascii_nstring(name, sizeof(name), namerec->name.name);
+ strupper_m(name);
+ if (!strequal(name,"*") &&
+ !strequal(name,"__SAMBA__") &&
+ (name_type < 0x1b || name_type >= 0x20 ||
+ ques_type < 0x1b || ques_type >= 0x20 ||
+ strequal(qname, name))) {
+ /* Start with the name. */
+ size_t len;
+ push_ascii_nstring(buf, name);
+ len = strlen(buf);
+ memset(buf + len, ' ', MAX_NETBIOSNAME_LEN - len - 1);
+ buf[MAX_NETBIOSNAME_LEN - 1] = '\0';
+
+ /* Put the name type and netbios flags in the buffer. */
+
+ buf[15] = name_type;
+ set_nb_flags( &buf[16],namerec->data.nb_flags );
+ buf[16] |= NB_ACTIVE; /* all our names are active */
+
+ buf += 18;
+
+ names_added++;
+ }
+ }
+
+ /* Remove duplicate names. */
+ if (names_added > 1) {
+ qsort( buf0, names_added, 18, QSORT_CAST status_compare );
+ }
+
+ for( i=1; i < names_added ; i++ ) {
+ if (memcmp(buf0 + 18*i,buf0 + 18*(i-1),16) == 0) {
+ names_added--;
+ if (names_added == i)
+ break;
+ memmove(buf0 + 18*i,buf0 + 18*(i+1),18*(names_added-i));
+ i--;
+ }
+ }
+
+ buf = buf0 + 18*names_added;
+
+ namerec = (struct name_record *)ubi_trNext( namerec );
+
+ if (!namerec) {
+ /* End of the subnet specific name list. Now
+ add the names on the unicast subnet . */
+ struct subnet_record *uni_subrec = unicast_subnet;
+
+ if (uni_subrec != subrec) {
+ subrec = uni_subrec;
+ namerec = (struct name_record *)ubi_trFirst( subrec->namelist );
+ }
+ }
+ if (!namerec)
+ break;
+
+ }
+
+ SCVAL(countptr,0,names_added);
+
+ /* We don't send any stats as they could be used to attack
+ the protocol. */
+ memset(buf,'\0',46);
+
+ buf += 46;
+
+ /* Send a NODE STATUS RESPONSE */
+ reply_netbios_packet(p, /* Packet to reply to. */
+ 0, /* Result code. */
+ NMB_STATUS, /* nmbd type code. */
+ NMB_NAME_QUERY_OPCODE, /* opcode. */
+ 0, /* ttl. */
+ rdata, /* data to send. */
+ PTR_DIFF(buf,rdata)); /* data length. */
+}
+
+
+/***************************************************************************
+Process a name query.
+
+For broadcast name queries:
+
+ - Only reply if the query is for one of YOUR names.
+ - NEVER send a negative response to a broadcast query.
+
+****************************************************************************/
+
+void process_name_query_request(struct subnet_record *subrec, struct packet_struct *p)
+{
+ struct nmb_packet *nmb = &p->packet.nmb;
+ struct nmb_name *question = &nmb->question.question_name;
+ int name_type = question->name_type;
+ BOOL bcast = nmb->header.nm_flags.bcast;
+ int ttl=0;
+ int rcode = 0;
+ char *prdata = NULL;
+ char rdata[6];
+ BOOL success = False;
+ struct name_record *namerec = NULL;
+ int reply_data_len = 0;
+ int i;
+
+ DEBUG(3,("process_name_query_request: Name query from %s on subnet %s for name %s\n",
+ inet_ntoa(p->ip), subrec->subnet_name, nmb_namestr(question)));
+
+ /* Look up the name in the cache - if the request is a broadcast request that
+ came from a subnet we don't know about then search all the broadcast subnets
+ for a match (as we don't know what interface the request came in on). */
+
+ if(subrec == remote_broadcast_subnet)
+ namerec = find_name_for_remote_broadcast_subnet( question, FIND_ANY_NAME);
+ else
+ namerec = find_name_on_subnet(subrec, question, FIND_ANY_NAME);
+
+ /* Check if it is a name that expired */
+ if (namerec &&
+ ((namerec->data.death_time != PERMANENT_TTL) &&
+ (namerec->data.death_time < p->timestamp))) {
+ DEBUG(5,("process_name_query_request: expired name %s\n", nmb_namestr(&namerec->name)));
+ namerec = NULL;
+ }
+
+ if (namerec) {
+ /*
+ * Always respond to unicast queries.
+ * Don't respond to broadcast queries unless the query is for
+ * a name we own, a Primary Domain Controller name, or a WINS_PROXY
+ * name with type 0 or 0x20. WINS_PROXY names are only ever added
+ * into the namelist if we were configured as a WINS proxy.
+ */
+
+ if (!bcast ||
+ (bcast && ((name_type == 0x1b) ||
+ (namerec->data.source == SELF_NAME) ||
+ (namerec->data.source == PERMANENT_NAME) ||
+ ((namerec->data.source == WINS_PROXY_NAME) &&
+ ((name_type == 0) || (name_type == 0x20)))))) {
+ /* The requested name is a directed query, or it's SELF or PERMANENT or WINS_PROXY,
+ or it's a Domain Master type. */
+
+ /*
+ * If this is a WINS_PROXY_NAME, then ceck that none of the IP
+ * addresses we are returning is on the same broadcast subnet
+ * as the requesting packet. If it is then don't reply as the
+ * actual machine will be replying also and we don't want two
+ * replies to a broadcast query.
+ */
+
+ if (namerec->data.source == WINS_PROXY_NAME) {
+ for( i = 0; i < namerec->data.num_ips; i++) {
+ if (same_net(namerec->data.ip[i], subrec->myip, subrec->mask_ip)) {
+ DEBUG(5,("process_name_query_request: name %s is a WINS proxy name and is also on the same subnet (%s) as the requestor. Not replying.\n",
+ nmb_namestr(&namerec->name), subrec->subnet_name ));
+ return;
+ }
+ }
+ }
+
+ ttl = (namerec->data.death_time != PERMANENT_TTL) ?
+ namerec->data.death_time - p->timestamp : lp_max_ttl();
+
+ /* Copy all known ip addresses into the return data. */
+ /* Optimise for the common case of one IP address so
+ we don't need a malloc. */
+
+ if (namerec->data.num_ips == 1) {
+ prdata = rdata;
+ } else {
+ if ((prdata = (char *)malloc( namerec->data.num_ips * 6 )) == NULL) {
+ DEBUG(0,("process_name_query_request: malloc fail !\n"));
+ return;
+ }
+ }
+
+ for (i = 0; i < namerec->data.num_ips; i++) {
+ set_nb_flags(&prdata[i*6],namerec->data.nb_flags);
+ putip((char *)&prdata[2+(i*6)], &namerec->data.ip[i]);
+ }
+
+ sort_query_replies(prdata, i, p->ip);
+
+ reply_data_len = namerec->data.num_ips * 6;
+ success = True;
+ }
+ }
+
+ /*
+ * If a machine is broadcasting a name lookup request and we have lp_wins_proxy()
+ * set we should initiate a WINS query here. On success we add the resolved name
+ * into our namelist with a type of WINS_PROXY_NAME and then reply to the query.
+ */
+
+ if(!success && (namerec == NULL) && we_are_a_wins_client() && lp_wins_proxy() &&
+ bcast && (subrec != remote_broadcast_subnet)) {
+ make_wins_proxy_name_query_request( subrec, p, question );
+ return;
+ }
+
+ if (!success && bcast) {
+ if(prdata != rdata)
+ SAFE_FREE(prdata);
+ return; /* Never reply with a negative response to broadcasts. */
+ }
+
+ /*
+ * Final check. From observation, if a unicast packet is sent
+ * to a non-WINS server with the recursion desired bit set
+ * then never send a negative response.
+ */
+
+ if(!success && !bcast && nmb->header.nm_flags.recursion_desired) {
+ if(prdata != rdata)
+ SAFE_FREE(prdata);
+ return;
+ }
+
+ if (success) {
+ rcode = 0;
+ DEBUG(3,("OK\n"));
+ } else {
+ rcode = NAM_ERR;
+ DEBUG(3,("UNKNOWN\n"));
+ }
+
+ /* See rfc1002.txt 4.2.13. */
+
+ reply_netbios_packet(p, /* Packet to reply to. */
+ rcode, /* Result code. */
+ NMB_QUERY, /* nmbd type code. */
+ NMB_NAME_QUERY_OPCODE, /* opcode. */
+ ttl, /* ttl. */
+ prdata, /* data to send. */
+ reply_data_len); /* data length. */
+
+ if(prdata != rdata)
+ SAFE_FREE(prdata);
+}
diff --git a/source/nmbd/nmbd_lmhosts.c b/source/nmbd/nmbd_lmhosts.c
new file mode 100644
index 00000000000..b14e13f3a47
--- /dev/null
+++ b/source/nmbd/nmbd_lmhosts.c
@@ -0,0 +1,93 @@
+/*
+ Unix SMB/CIFS implementation.
+ NBT netbios routines and daemon - version 2
+ Copyright (C) Jeremy Allison 1994-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 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.
+
+ Revision History:
+
+ Handle lmhosts file reading.
+
+*/
+
+#include "includes.h"
+
+/****************************************************************************
+Load a lmhosts file.
+****************************************************************************/
+
+void load_lmhosts_file(char *fname)
+{
+ pstring name;
+ int name_type;
+ struct in_addr ipaddr;
+ XFILE *fp = startlmhosts( fname );
+
+ if (!fp) {
+ DEBUG(2,("load_lmhosts_file: Can't open lmhosts file %s. Error was %s\n",
+ fname, strerror(errno)));
+ return;
+ }
+
+ while (getlmhostsent(fp, name, &name_type, &ipaddr) ) {
+ struct subnet_record *subrec = NULL;
+ enum name_source source = LMHOSTS_NAME;
+
+ /* We find a relevent subnet to put this entry on, then add it. */
+ /* Go through all the broadcast subnets and see if the mask matches. */
+ for (subrec = FIRST_SUBNET; subrec ; subrec = NEXT_SUBNET_EXCLUDING_UNICAST(subrec)) {
+ if(same_net(ipaddr, subrec->bcast_ip, subrec->mask_ip))
+ break;
+ }
+
+ /* If none match add the name to the remote_broadcast_subnet. */
+ if(subrec == NULL)
+ subrec = remote_broadcast_subnet;
+
+ if(name_type == -1) {
+ /* Add the (0) and (0x20) names directly into the namelist for this subnet. */
+ (void)add_name_to_subnet(subrec,name,0x00,(uint16)NB_ACTIVE,PERMANENT_TTL,source,1,&ipaddr);
+ (void)add_name_to_subnet(subrec,name,0x20,(uint16)NB_ACTIVE,PERMANENT_TTL,source,1,&ipaddr);
+ } else {
+ /* Add the given name type to the subnet namelist. */
+ (void)add_name_to_subnet(subrec,name,name_type,(uint16)NB_ACTIVE,PERMANENT_TTL,source,1,&ipaddr);
+ }
+ }
+
+ endlmhosts(fp);
+}
+
+/****************************************************************************
+ Find a name read from the lmhosts file. We secretly check the names on
+ the remote_broadcast_subnet as if the name was added to a regular broadcast
+ subnet it will be found by normal name query processing.
+****************************************************************************/
+
+BOOL find_name_in_lmhosts(struct nmb_name *nmbname, struct name_record **namerecp)
+{
+ struct name_record *namerec;
+
+ *namerecp = NULL;
+
+ if((namerec = find_name_on_subnet(remote_broadcast_subnet, nmbname, FIND_ANY_NAME))==NULL)
+ return False;
+
+ if(!NAME_IS_ACTIVE(namerec) || (namerec->data.source != LMHOSTS_NAME))
+ return False;
+
+ *namerecp = namerec;
+ return True;
+}
diff --git a/source/nmbd/nmbd_logonnames.c b/source/nmbd/nmbd_logonnames.c
new file mode 100644
index 00000000000..e4264305911
--- /dev/null
+++ b/source/nmbd/nmbd_logonnames.c
@@ -0,0 +1,173 @@
+/*
+ Unix SMB/CIFS implementation.
+ NBT netbios routines and daemon - version 2
+ Copyright (C) Andrew Tridgell 1994-1998
+ Copyright (C) Luke Kenneth Casson Leighton 1994-1998
+ Copyright (C) Jeremy Allison 1994-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 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"
+
+extern struct in_addr allones_ip;
+
+extern uint16 samba_nb_type; /* Samba's NetBIOS type. */
+
+/****************************************************************************
+ Fail to become a Logon server on a subnet.
+****************************************************************************/
+
+static void become_logon_server_fail(struct subnet_record *subrec,
+ struct response_record *rrec,
+ struct nmb_name *fail_name)
+{
+ unstring failname;
+ struct work_record *work;
+ struct server_record *servrec;
+
+ pull_ascii_nstring(failname, sizeof(failname), fail_name->name);
+ work = find_workgroup_on_subnet(subrec, failname);
+ if(!work) {
+ DEBUG(0,("become_logon_server_fail: Error - cannot find \
+workgroup %s on subnet %s\n", failname, subrec->subnet_name));
+ return;
+ }
+
+ if((servrec = find_server_in_workgroup( work, global_myname())) == NULL) {
+ DEBUG(0,("become_logon_server_fail: Error - cannot find server %s \
+in workgroup %s on subnet %s\n",
+ global_myname(), failname, subrec->subnet_name));
+ work->log_state = LOGON_NONE;
+ return;
+ }
+
+ /* Set the state back to LOGON_NONE. */
+ work->log_state = LOGON_NONE;
+
+ servrec->serv.type &= ~SV_TYPE_DOMAIN_CTRL;
+
+ DEBUG(0,("become_logon_server_fail: Failed to become a domain master for \
+workgroup %s on subnet %s. Couldn't register name %s.\n",
+ work->work_group, subrec->subnet_name, nmb_namestr(fail_name)));
+
+}
+
+/****************************************************************************
+ Become a Logon server on a subnet.
+ ****************************************************************************/
+
+static void become_logon_server_success(struct subnet_record *subrec,
+ struct userdata_struct *userdata,
+ struct nmb_name *registered_name,
+ uint16 nb_flags,
+ int ttl, struct in_addr registered_ip)
+{
+ unstring reg_name;
+ struct work_record *work;
+ struct server_record *servrec;
+
+ pull_ascii_nstring(reg_name, sizeof(reg_name), registered_name->name);
+ work = find_workgroup_on_subnet( subrec, reg_name);
+ if(!work) {
+ DEBUG(0,("become_logon_server_success: Error - cannot find \
+workgroup %s on subnet %s\n", reg_name, subrec->subnet_name));
+ return;
+ }
+
+ if((servrec = find_server_in_workgroup( work, global_myname())) == NULL) {
+ DEBUG(0,("become_logon_server_success: Error - cannot find server %s \
+in workgroup %s on subnet %s\n",
+ global_myname(), reg_name, subrec->subnet_name));
+ work->log_state = LOGON_NONE;
+ return;
+ }
+
+ /* Set the state in the workgroup structure. */
+ work->log_state = LOGON_SRV; /* Become domain master. */
+
+ /* Update our server status. */
+ servrec->serv.type |= (SV_TYPE_NT|SV_TYPE_DOMAIN_MEMBER);
+ /* To allow Win95 policies to load we need to set type domain
+ controller.
+ */
+ servrec->serv.type |= SV_TYPE_DOMAIN_CTRL;
+
+ /* Tell the namelist writer to write out a change. */
+ subrec->work_changed = True;
+
+ /*
+ * Add the WORKGROUP<1C> name to the UNICAST subnet with the IP address
+ * for this subnet so we will respond to queries on this name.
+ */
+
+ {
+ struct nmb_name nmbname;
+ make_nmb_name(&nmbname,lp_workgroup(),0x1c);
+ insert_permanent_name_into_unicast(subrec, &nmbname, 0x1c);
+ }
+
+ DEBUG(0,("become_logon_server_success: Samba is now a logon server \
+for workgroup %s on subnet %s\n", work->work_group, subrec->subnet_name));
+}
+
+/*******************************************************************
+ Become a logon server by attempting to register the WORKGROUP<1c>
+ group name.
+******************************************************************/
+
+static void become_logon_server(struct subnet_record *subrec,
+ struct work_record *work)
+{
+ DEBUG(2,("become_logon_server: Atempting to become logon server for workgroup %s \
+on subnet %s\n", work->work_group,subrec->subnet_name));
+
+ DEBUG(3,("become_logon_server: go to first stage: register %s<1c> name\n",
+ work->work_group));
+ work->log_state = LOGON_WAIT;
+
+ register_name(subrec, work->work_group,0x1c,samba_nb_type|NB_GROUP,
+ become_logon_server_success,
+ become_logon_server_fail, NULL);
+}
+
+/*****************************************************************************
+ Add the internet group <1c> logon names by unicast and broadcast.
+ ****************************************************************************/
+
+void add_logon_names(void)
+{
+ struct subnet_record *subrec;
+
+ for (subrec = FIRST_SUBNET; subrec; subrec = NEXT_SUBNET_INCLUDING_UNICAST(subrec)) {
+ struct work_record *work = find_workgroup_on_subnet(subrec, lp_workgroup());
+
+ if (work && (work->log_state == LOGON_NONE)) {
+ struct nmb_name nmbname;
+ make_nmb_name(&nmbname,lp_workgroup(),0x1c);
+
+ if (find_name_on_subnet(subrec, &nmbname, FIND_SELF_NAME) == NULL) {
+ if( DEBUGLVL( 0 ) ) {
+ dbgtext( "add_domain_logon_names:\n" );
+ dbgtext( "Attempting to become logon server " );
+ dbgtext( "for workgroup %s ", lp_workgroup() );
+ dbgtext( "on subnet %s\n", subrec->subnet_name );
+ }
+ become_logon_server(subrec, work);
+ }
+ }
+ }
+}
diff --git a/source/nmbd/nmbd_mynames.c b/source/nmbd/nmbd_mynames.c
new file mode 100644
index 00000000000..07247d5495e
--- /dev/null
+++ b/source/nmbd/nmbd_mynames.c
@@ -0,0 +1,224 @@
+/*
+ Unix SMB/CIFS implementation.
+ NBT netbios routines and daemon - version 2
+ Copyright (C) Andrew Tridgell 1994-1998
+ Copyright (C) Luke Kenneth Casson Leighton 1994-1998
+ Copyright (C) Jeremy Allison 1994-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 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"
+
+extern uint16 samba_nb_type; /* Samba's NetBIOS type. */
+
+/****************************************************************************
+ Fail funtion when registering my netbios names.
+**************************************************************************/
+
+static void my_name_register_failed(struct subnet_record *subrec,
+ struct response_record *rrec, struct nmb_name *nmbname)
+{
+ DEBUG(0,("my_name_register_failed: Failed to register my name %s on subnet %s.\n",
+ nmb_namestr(nmbname), subrec->subnet_name));
+}
+
+
+/****************************************************************************
+ Add my workgroup and my given names to one subnet
+ Also add the magic Samba names.
+**************************************************************************/
+
+void register_my_workgroup_one_subnet(struct subnet_record *subrec)
+{
+ int i;
+
+ struct work_record *work;
+
+ /* Create the workgroup on the subnet. */
+ if((work = create_workgroup_on_subnet(subrec, lp_workgroup(),
+ PERMANENT_TTL)) == NULL) {
+ DEBUG(0,("register_my_workgroup_and_names: Failed to create my workgroup %s on subnet %s. \
+Exiting.\n", lp_workgroup(), subrec->subnet_name));
+ return;
+ }
+
+ /* Each subnet entry, except for the wins_server_subnet has
+ the magic Samba names. */
+ add_samba_names_to_subnet(subrec);
+
+ /* Register all our names including aliases. */
+ for (i=0; my_netbios_names(i); i++) {
+ register_name(subrec, my_netbios_names(i),0x20,samba_nb_type,
+ NULL,
+ my_name_register_failed, NULL);
+ register_name(subrec, my_netbios_names(i),0x03,samba_nb_type,
+ NULL,
+ my_name_register_failed, NULL);
+ register_name(subrec, my_netbios_names(i),0x00,samba_nb_type,
+ NULL,
+ my_name_register_failed, NULL);
+ }
+
+ /* Initiate election processing, register the workgroup names etc. */
+ initiate_myworkgroup_startup(subrec, work);
+}
+
+/*******************************************************************
+ Utility function to add a name to the unicast subnet, or add in
+ our IP address if it already exists.
+******************************************************************/
+
+static void insert_refresh_name_into_unicast( struct subnet_record *subrec,
+ struct nmb_name *nmbname, uint16 nb_type )
+{
+ struct name_record *namerec;
+
+ if (!we_are_a_wins_client()) {
+ insert_permanent_name_into_unicast(subrec, nmbname, nb_type);
+ return;
+ }
+
+ if((namerec = find_name_on_subnet(unicast_subnet, nmbname, FIND_SELF_NAME)) == NULL) {
+ unstring name;
+ pull_ascii_nstring(name, sizeof(name), nmbname->name);
+ /* The name needs to be created on the unicast subnet. */
+ (void)add_name_to_subnet( unicast_subnet, name,
+ nmbname->name_type, nb_type,
+ MIN(lp_max_ttl(), MAX_REFRESH_TIME), SELF_NAME, 1, &subrec->myip);
+ } else {
+ /* The name already exists on the unicast subnet. Add our local
+ IP for the given broadcast subnet to the name. */
+ add_ip_to_name_record( namerec, subrec->myip);
+ }
+}
+
+/****************************************************************************
+ Add my workgroup and my given names to the subnet lists.
+ Also add the magic Samba names.
+**************************************************************************/
+
+BOOL register_my_workgroup_and_names(void)
+{
+ struct subnet_record *subrec;
+ int i;
+
+ for(subrec = FIRST_SUBNET; subrec; subrec = NEXT_SUBNET_INCLUDING_UNICAST(subrec)) {
+ register_my_workgroup_one_subnet(subrec);
+ }
+
+ /* We still need to add the magic Samba
+ names and the netbios names to the unicast subnet directly. This is
+ to allow unicast node status requests and queries to still work
+ in a broadcast only environment. */
+
+ add_samba_names_to_subnet(unicast_subnet);
+
+ for (i=0; my_netbios_names(i); i++) {
+ for(subrec = FIRST_SUBNET; subrec; subrec = NEXT_SUBNET_EXCLUDING_UNICAST(subrec)) {
+ /*
+ * Ensure all the IP addresses are added if we are multihomed.
+ */
+ struct nmb_name nmbname;
+
+ make_nmb_name(&nmbname, my_netbios_names(i),0x20);
+ insert_refresh_name_into_unicast(subrec, &nmbname, samba_nb_type);
+
+ make_nmb_name(&nmbname, my_netbios_names(i),0x3);
+ insert_refresh_name_into_unicast(subrec, &nmbname, samba_nb_type);
+
+ make_nmb_name(&nmbname, my_netbios_names(i),0x0);
+ insert_refresh_name_into_unicast(subrec, &nmbname, samba_nb_type);
+ }
+ }
+
+ /*
+ * Add the WORKGROUP<0> and WORKGROUP<1e> group names to the unicast subnet
+ * also for the same reasons.
+ */
+
+ for(subrec = FIRST_SUBNET; subrec; subrec = NEXT_SUBNET_EXCLUDING_UNICAST(subrec)) {
+ /*
+ * Ensure all the IP addresses are added if we are multihomed.
+ */
+ struct nmb_name nmbname;
+
+ make_nmb_name(&nmbname, lp_workgroup(), 0x0);
+ insert_refresh_name_into_unicast(subrec, &nmbname, samba_nb_type|NB_GROUP);
+
+ make_nmb_name(&nmbname, lp_workgroup(), 0x1e);
+ insert_refresh_name_into_unicast(subrec, &nmbname, samba_nb_type|NB_GROUP);
+ }
+
+ /*
+ * We need to add the Samba names to the remote broadcast subnet,
+ * as NT 4.x does directed broadcast requests to the *<0x0> name.
+ */
+
+ add_samba_names_to_subnet(remote_broadcast_subnet);
+
+ return True;
+}
+
+/****************************************************************************
+ Remove all the names we registered.
+**************************************************************************/
+
+void release_wins_names(void)
+{
+ struct subnet_record *subrec = unicast_subnet;
+ struct name_record *namerec, *nextnamerec;
+
+ for (namerec = (struct name_record *)ubi_trFirst( subrec->namelist ); namerec; namerec = nextnamerec) {
+ nextnamerec = (struct name_record *)ubi_trNext( namerec );
+ if( (namerec->data.source == SELF_NAME)
+ && !NAME_IS_DEREGISTERING(namerec) )
+ release_name( subrec, namerec, standard_success_release,
+ NULL, NULL);
+ }
+}
+
+/*******************************************************************
+ Refresh our registered names with WINS
+******************************************************************/
+
+void refresh_my_names(time_t t)
+{
+ struct name_record *namerec;
+
+ if (wins_srv_count() < 1)
+ return;
+
+ for (namerec = (struct name_record *)ubi_trFirst(unicast_subnet->namelist);
+ namerec;
+ namerec = (struct name_record *)ubi_trNext(namerec)) {
+ /* Each SELF name has an individual time to be refreshed. */
+ if ((namerec->data.source == SELF_NAME) &&
+ (namerec->data.refresh_time < t) &&
+ (namerec->data.death_time != PERMANENT_TTL)) {
+ /* We cheat here and pretend the refresh is going to be
+ successful & update the refresh times. This stops
+ multiple refresh calls being done. We actually
+ deal with refresh failure in the fail_fn.
+ */
+ if (!is_refresh_already_queued(unicast_subnet, namerec)) {
+ wins_refresh_name(namerec);
+ }
+ namerec->data.death_time = t + lp_max_ttl();
+ namerec->data.refresh_time = t + MIN(lp_max_ttl()/2, MAX_REFRESH_TIME);
+ }
+ }
+}
diff --git a/source/nmbd/nmbd_namelistdb.c b/source/nmbd/nmbd_namelistdb.c
new file mode 100644
index 00000000000..bb14ff7641a
--- /dev/null
+++ b/source/nmbd/nmbd_namelistdb.c
@@ -0,0 +1,584 @@
+/*
+ Unix SMB/CIFS implementation.
+ NBT netbios routines and daemon - version 2
+ Copyright (C) Andrew Tridgell 1994-1998
+ Copyright (C) Luke Kenneth Casson Leighton 1994-1998
+ Copyright (C) Jeremy Allison 1994-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 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"
+
+uint16 samba_nb_type = 0; /* samba's NetBIOS name type */
+
+
+/**************************************************************************
+ Set Samba's NetBIOS name type.
+***************************************************************************/
+
+void set_samba_nb_type(void)
+{
+ if( lp_wins_support() || wins_srv_count() )
+ samba_nb_type = NB_HFLAG; /* samba is a 'hybrid' node type. */
+ else
+ samba_nb_type = NB_BFLAG; /* samba is broadcast-only node type. */
+}
+
+/***************************************************************************
+ Convert a NetBIOS name to upper case.
+***************************************************************************/
+
+static void upcase_name( struct nmb_name *target, struct nmb_name *source )
+{
+ int i;
+ unstring targ;
+ fstring scope;
+
+ if( NULL != source )
+ memcpy( target, source, sizeof( struct nmb_name ) );
+
+ pull_ascii_nstring(targ, sizeof(targ), target->name);
+ strupper_m( targ );
+ push_ascii_nstring( target->name, targ);
+
+ pull_ascii(scope, target->scope, 64, -1, STR_TERMINATE);
+ strupper_m( scope );
+ push_ascii(target->scope, scope, 64, STR_TERMINATE);
+
+ /* fudge... We're using a byte-by-byte compare, so we must be sure that
+ * unused space doesn't have garbage in it.
+ */
+
+ for( i = strlen( target->name ); i < sizeof( target->name ); i++ )
+ target->name[i] = '\0';
+ for( i = strlen( target->scope ); i < sizeof( target->scope ); i++ )
+ target->scope[i] = '\0';
+}
+
+/**************************************************************************
+ Add a new or overwrite an existing namelist entry.
+***************************************************************************/
+
+static void update_name_in_namelist( struct subnet_record *subrec,
+ struct name_record *namerec )
+{
+ struct name_record *oldrec = NULL;
+
+ ubi_trInsert( subrec->namelist, namerec, &(namerec->name), &oldrec );
+ if( oldrec ) {
+ SAFE_FREE( oldrec->data.ip );
+ SAFE_FREE( oldrec );
+ }
+}
+
+/**************************************************************************
+ Remove a name from the namelist.
+***************************************************************************/
+
+void remove_name_from_namelist( struct subnet_record *subrec,
+ struct name_record *namerec )
+{
+ ubi_trRemove( subrec->namelist, namerec );
+ SAFE_FREE(namerec->data.ip);
+ ZERO_STRUCTP(namerec);
+ SAFE_FREE(namerec);
+ subrec->namelist_changed = True;
+}
+
+/**************************************************************************
+ Find a name in a subnet.
+**************************************************************************/
+
+struct name_record *find_name_on_subnet( struct subnet_record *subrec,
+ struct nmb_name *nmbname,
+ BOOL self_only )
+{
+ struct nmb_name uc_name[1];
+ struct name_record *name_ret;
+
+ upcase_name( uc_name, nmbname );
+ name_ret = (struct name_record *)ubi_trFind( subrec->namelist, uc_name );
+ if( name_ret ) {
+ /* Self names only - these include permanent names. */
+ if( self_only && (name_ret->data.source != SELF_NAME) && (name_ret->data.source != PERMANENT_NAME) ) {
+ DEBUG( 9, ( "find_name_on_subnet: on subnet %s - self name %s NOT FOUND\n",
+ subrec->subnet_name, nmb_namestr(nmbname) ) );
+ return( NULL );
+ }
+
+ DEBUG( 9, ("find_name_on_subnet: on subnet %s - found name %s source=%d\n",
+ subrec->subnet_name, nmb_namestr(nmbname), name_ret->data.source) );
+ return( name_ret );
+ }
+
+ DEBUG( 9, ( "find_name_on_subnet: on subnet %s - name %s NOT FOUND\n",
+ subrec->subnet_name, nmb_namestr(nmbname) ) );
+ return( NULL );
+}
+
+/**************************************************************************
+ Find a name over all known broadcast subnets.
+************************************************************************/
+
+struct name_record *find_name_for_remote_broadcast_subnet(
+ struct nmb_name *nmbname,
+ BOOL self_only )
+{
+ struct subnet_record *subrec;
+ struct name_record *namerec = NULL;
+
+ for( subrec = FIRST_SUBNET; subrec; subrec = NEXT_SUBNET_EXCLUDING_UNICAST(subrec) ) {
+ if( NULL != (namerec = find_name_on_subnet(subrec, nmbname, self_only)) )
+ break;
+ }
+
+ return( namerec );
+}
+
+/**************************************************************************
+ Update the ttl of an entry in a subnet name list.
+***************************************************************************/
+
+void update_name_ttl( struct name_record *namerec, int ttl )
+{
+ time_t time_now = time(NULL);
+
+ if( namerec->data.death_time != PERMANENT_TTL )
+ namerec->data.death_time = time_now + ttl;
+
+ namerec->data.refresh_time = time_now + MIN((ttl/2), MAX_REFRESH_TIME);
+
+ namerec->subnet->namelist_changed = True;
+}
+
+/**************************************************************************
+ Add an entry to a subnet name list.
+***********************************************************************/
+
+struct name_record *add_name_to_subnet( struct subnet_record *subrec,
+ const char *name,
+ int type,
+ uint16 nb_flags,
+ int ttl,
+ enum name_source source,
+ int num_ips,
+ struct in_addr *iplist)
+{
+ struct name_record *namerec;
+ time_t time_now = time(NULL);
+
+ namerec = (struct name_record *)malloc( sizeof(*namerec) );
+ if( NULL == namerec ) {
+ DEBUG( 0, ( "add_name_to_subnet: malloc fail.\n" ) );
+ return( NULL );
+ }
+
+ memset( (char *)namerec, '\0', sizeof(*namerec) );
+ namerec->data.ip = (struct in_addr *)malloc( sizeof(struct in_addr) * num_ips );
+ if( NULL == namerec->data.ip ) {
+ DEBUG( 0, ( "add_name_to_subnet: malloc fail when creating ip_flgs.\n" ) );
+ ZERO_STRUCTP(namerec);
+ SAFE_FREE(namerec);
+ return NULL;
+ }
+
+ namerec->subnet = subrec;
+
+ make_nmb_name(&namerec->name, name, type);
+ upcase_name(&namerec->name, NULL );
+
+ /* Enter the name as active. */
+ namerec->data.nb_flags = nb_flags | NB_ACTIVE;
+ namerec->data.wins_flags = WINS_ACTIVE;
+
+ /* If it's our primary name, flag it as so. */
+ if( strequal( my_netbios_names(0), name ) )
+ namerec->data.nb_flags |= NB_PERM;
+
+ /* Copy the IPs. */
+ namerec->data.num_ips = num_ips;
+ memcpy( (namerec->data.ip), iplist, num_ips * sizeof(struct in_addr) );
+
+ /* Data source. */
+ namerec->data.source = source;
+
+ /* Setup the death_time and refresh_time. */
+ if( ttl == PERMANENT_TTL )
+ namerec->data.death_time = PERMANENT_TTL;
+ else
+ namerec->data.death_time = time_now + ttl;
+
+ namerec->data.refresh_time = time_now + MIN((ttl/2), MAX_REFRESH_TIME);
+
+ /* Now add the record to the name list. */
+ update_name_in_namelist( subrec, namerec );
+
+ DEBUG( 3, ( "add_name_to_subnet: Added netbios name %s with first IP %s \
+ttl=%d nb_flags=%2x to subnet %s\n",
+ nmb_namestr( &namerec->name ),
+ inet_ntoa( *iplist ),
+ ttl,
+ (unsigned int)nb_flags,
+ subrec->subnet_name ) );
+
+ subrec->namelist_changed = True;
+
+ return(namerec);
+}
+
+/*******************************************************************
+ Utility function automatically called when a name refresh or register
+ succeeds. By definition this is a SELF_NAME (or we wouldn't be registering
+ it).
+ ******************************************************************/
+
+void standard_success_register(struct subnet_record *subrec,
+ struct userdata_struct *userdata,
+ struct nmb_name *nmbname, uint16 nb_flags, int ttl,
+ struct in_addr registered_ip)
+{
+ struct name_record *namerec;
+
+ namerec = find_name_on_subnet( subrec, nmbname, FIND_SELF_NAME );
+ if( NULL == namerec ) {
+ unstring name;
+ pull_ascii_nstring(name, sizeof(name), nmbname->name);
+ add_name_to_subnet( subrec, name, nmbname->name_type,
+ nb_flags, ttl, SELF_NAME, 1, &registered_ip );
+ } else {
+ update_name_ttl( namerec, ttl );
+ }
+}
+
+/*******************************************************************
+ Utility function automatically called when a name refresh or register
+ fails. Note that this is only ever called on a broadcast subnet with
+ one IP address per name. This is why it can just delete the name
+ without enumerating the IP adresses. JRA.
+ ******************************************************************/
+
+void standard_fail_register( struct subnet_record *subrec,
+ struct response_record *rrec,
+ struct nmb_name *nmbname )
+{
+ struct name_record *namerec;
+
+ namerec = find_name_on_subnet( subrec, nmbname, FIND_SELF_NAME );
+
+ DEBUG( 0, ( "standard_fail_register: Failed to register/refresh name %s \
+on subnet %s\n", nmb_namestr(nmbname), subrec->subnet_name) );
+
+ /* Remove the name from the subnet. */
+ if( namerec )
+ remove_name_from_namelist(subrec, namerec);
+}
+
+/*******************************************************************
+ Utility function to remove an IP address from a name record.
+ ******************************************************************/
+
+static void remove_nth_ip_in_record( struct name_record *namerec, int ind)
+{
+ if( ind != namerec->data.num_ips )
+ memmove( (char *)(&namerec->data.ip[ind]),
+ (char *)(&namerec->data.ip[ind+1]),
+ ( namerec->data.num_ips - ind - 1) * sizeof(struct in_addr) );
+
+ namerec->data.num_ips--;
+ namerec->subnet->namelist_changed = True;
+}
+
+/*******************************************************************
+ Utility function to check if an IP address exists in a name record.
+ ******************************************************************/
+
+BOOL find_ip_in_name_record( struct name_record *namerec, struct in_addr ip )
+{
+ int i;
+
+ for(i = 0; i < namerec->data.num_ips; i++)
+ if(ip_equal( namerec->data.ip[i], ip))
+ return True;
+
+ return False;
+}
+
+/*******************************************************************
+ Utility function to add an IP address to a name record.
+ ******************************************************************/
+
+void add_ip_to_name_record( struct name_record *namerec, struct in_addr new_ip )
+{
+ struct in_addr *new_list;
+
+ /* Don't add one we already have. */
+ if( find_ip_in_name_record( namerec, new_ip ) )
+ return;
+
+ new_list = (struct in_addr *)malloc( (namerec->data.num_ips + 1) * sizeof(struct in_addr) );
+ if( NULL == new_list ) {
+ DEBUG(0,("add_ip_to_name_record: Malloc fail !\n"));
+ return;
+ }
+
+ memcpy( (char *)new_list, (char *)namerec->data.ip, namerec->data.num_ips * sizeof(struct in_addr) );
+ new_list[namerec->data.num_ips] = new_ip;
+
+ SAFE_FREE(namerec->data.ip);
+ namerec->data.ip = new_list;
+ namerec->data.num_ips += 1;
+
+ namerec->subnet->namelist_changed = True;
+}
+
+/*******************************************************************
+ Utility function to remove an IP address from a name record.
+ ******************************************************************/
+
+void remove_ip_from_name_record( struct name_record *namerec,
+ struct in_addr remove_ip )
+{
+ /* Try and find the requested ip address - remove it. */
+ int i;
+ int orig_num = namerec->data.num_ips;
+
+ for(i = 0; i < orig_num; i++) {
+ if( ip_equal( remove_ip, namerec->data.ip[i]) ) {
+ remove_nth_ip_in_record( namerec, i);
+ break;
+ }
+ }
+}
+
+/*******************************************************************
+ Utility function that release_name callers can plug into as the
+ success function when a name release is successful. Used to save
+ duplication of success_function code.
+ ******************************************************************/
+
+void standard_success_release( struct subnet_record *subrec,
+ struct userdata_struct *userdata,
+ struct nmb_name *nmbname,
+ struct in_addr released_ip )
+{
+ struct name_record *namerec;
+
+ namerec = find_name_on_subnet( subrec, nmbname, FIND_ANY_NAME );
+ if( namerec == NULL ) {
+ DEBUG( 0, ( "standard_success_release: Name release for name %s IP %s \
+on subnet %s. Name was not found on subnet.\n", nmb_namestr(nmbname), inet_ntoa(released_ip),
+ subrec->subnet_name) );
+ return;
+ } else {
+ int orig_num = namerec->data.num_ips;
+
+ remove_ip_from_name_record( namerec, released_ip );
+
+ if( namerec->data.num_ips == orig_num )
+ DEBUG( 0, ( "standard_success_release: Name release for name %s IP %s \
+on subnet %s. This ip is not known for this name.\n", nmb_namestr(nmbname), inet_ntoa(released_ip), subrec->subnet_name ) );
+ }
+
+ if( namerec->data.num_ips == 0 )
+ remove_name_from_namelist( subrec, namerec );
+}
+
+/*******************************************************************
+ Expires old names in a subnet namelist.
+******************************************************************/
+
+void expire_names_on_subnet(struct subnet_record *subrec, time_t t)
+{
+ struct name_record *namerec;
+ struct name_record *next_namerec;
+
+ for( namerec = (struct name_record *)ubi_trFirst( subrec->namelist ); namerec; namerec = next_namerec ) {
+ next_namerec = (struct name_record *)ubi_trNext( namerec );
+ if( (namerec->data.death_time != PERMANENT_TTL) && (namerec->data.death_time < t) ) {
+ if( namerec->data.source == SELF_NAME ) {
+ DEBUG( 3, ( "expire_names_on_subnet: Subnet %s not expiring SELF \
+name %s\n", subrec->subnet_name, nmb_namestr(&namerec->name) ) );
+ namerec->data.death_time += 300;
+ namerec->subnet->namelist_changed = True;
+ continue;
+ }
+
+ DEBUG(3,("expire_names_on_subnet: Subnet %s - removing expired name %s\n",
+ subrec->subnet_name, nmb_namestr(&namerec->name)));
+
+ remove_name_from_namelist( subrec, namerec );
+ }
+ }
+}
+
+/*******************************************************************
+ Expires old names in all subnet namelists.
+******************************************************************/
+
+void expire_names(time_t t)
+{
+ struct subnet_record *subrec;
+
+ for( subrec = FIRST_SUBNET; subrec; subrec = NEXT_SUBNET_INCLUDING_UNICAST(subrec) ) {
+ expire_names_on_subnet( subrec, t );
+ }
+}
+
+/****************************************************************************
+ Add the magic samba names, useful for finding samba servers.
+ These go directly into the name list for a particular subnet,
+ without going through the normal registration process.
+ When adding them to the unicast subnet, add them as a list of
+ all broadcast subnet IP addresses.
+**************************************************************************/
+
+void add_samba_names_to_subnet( struct subnet_record *subrec )
+{
+ struct in_addr *iplist = &subrec->myip;
+ int num_ips = 1;
+
+ /* These names are added permanently (ttl of zero) and will NOT be refreshed. */
+
+ if( (subrec == unicast_subnet) || (subrec == wins_server_subnet) || (subrec == remote_broadcast_subnet) ) {
+ struct subnet_record *bcast_subrecs;
+ int i;
+
+ /* Create an IP list containing all our known subnets. */
+
+ num_ips = iface_count();
+ iplist = (struct in_addr *)malloc( num_ips * sizeof(struct in_addr) );
+ if( NULL == iplist ) {
+ DEBUG(0,("add_samba_names_to_subnet: Malloc fail !\n"));
+ return;
+ }
+
+ for( bcast_subrecs = FIRST_SUBNET, i = 0; bcast_subrecs; bcast_subrecs = NEXT_SUBNET_EXCLUDING_UNICAST(bcast_subrecs), i++ )
+ iplist[i] = bcast_subrecs->myip;
+ }
+
+ add_name_to_subnet(subrec,"*",0x0,samba_nb_type, PERMANENT_TTL,
+ PERMANENT_NAME, num_ips, iplist);
+ add_name_to_subnet(subrec,"*",0x20,samba_nb_type,PERMANENT_TTL,
+ PERMANENT_NAME, num_ips, iplist);
+ add_name_to_subnet(subrec,"__SAMBA__",0x20,samba_nb_type,PERMANENT_TTL,
+ PERMANENT_NAME, num_ips, iplist);
+ add_name_to_subnet(subrec,"__SAMBA__",0x00,samba_nb_type,PERMANENT_TTL,
+ PERMANENT_NAME, num_ips, iplist);
+
+ if(iplist != &subrec->myip)
+ SAFE_FREE(iplist);
+}
+
+/****************************************************************************
+ Dump the contents of the namelists on all the subnets (including unicast)
+ into a file. Initiated by SIGHUP - used to debug the state of the namelists.
+**************************************************************************/
+
+static void dump_subnet_namelist( struct subnet_record *subrec, XFILE *fp)
+{
+ struct name_record *namerec;
+ const char *src_type;
+ struct tm *tm;
+ int i;
+
+ x_fprintf(fp, "Subnet %s\n----------------------\n", subrec->subnet_name);
+ for( namerec = (struct name_record *)ubi_trFirst( subrec->namelist ); namerec;
+ namerec = (struct name_record *)ubi_trNext( namerec ) ) {
+
+ x_fprintf(fp,"\tName = %s\t", nmb_namestr(&namerec->name));
+ switch(namerec->data.source) {
+ case LMHOSTS_NAME:
+ src_type = "LMHOSTS_NAME";
+ break;
+ case WINS_PROXY_NAME:
+ src_type = "WINS_PROXY_NAME";
+ break;
+ case REGISTER_NAME:
+ src_type = "REGISTER_NAME";
+ break;
+ case SELF_NAME:
+ src_type = "SELF_NAME";
+ break;
+ case DNS_NAME:
+ src_type = "DNS_NAME";
+ break;
+ case DNSFAIL_NAME:
+ src_type = "DNSFAIL_NAME";
+ break;
+ case PERMANENT_NAME:
+ src_type = "PERMANENT_NAME";
+ break;
+ default:
+ src_type = "unknown!";
+ break;
+ }
+
+ x_fprintf(fp,"Source = %s\nb_flags = %x\t", src_type, namerec->data.nb_flags);
+
+ if(namerec->data.death_time != PERMANENT_TTL) {
+ tm = LocalTime(&namerec->data.death_time);
+ x_fprintf(fp, "death_time = %s\t", asctime(tm));
+ } else {
+ x_fprintf(fp, "death_time = PERMANENT\t");
+ }
+
+ if(namerec->data.refresh_time != PERMANENT_TTL) {
+ tm = LocalTime(&namerec->data.refresh_time);
+ x_fprintf(fp, "refresh_time = %s\n", asctime(tm));
+ } else {
+ x_fprintf(fp, "refresh_time = PERMANENT\n");
+ }
+
+ x_fprintf(fp, "\t\tnumber of IPS = %d", namerec->data.num_ips);
+ for(i = 0; i < namerec->data.num_ips; i++)
+ x_fprintf(fp, "\t%s", inet_ntoa(namerec->data.ip[i]));
+
+ x_fprintf(fp, "\n\n");
+ }
+}
+
+/****************************************************************************
+ Dump the contents of the namelists on all the subnets (including unicast)
+ into a file. Initiated by SIGHUP - used to debug the state of the namelists.
+**************************************************************************/
+
+void dump_all_namelists(void)
+{
+ XFILE *fp;
+ struct subnet_record *subrec;
+
+ fp = x_fopen(lock_path("namelist.debug"),O_WRONLY|O_CREAT|O_TRUNC, 0644);
+
+ if (!fp) {
+ DEBUG(0,("dump_all_namelists: Can't open file %s. Error was %s\n",
+ "namelist.debug",strerror(errno)));
+ return;
+ }
+
+ for( subrec = FIRST_SUBNET; subrec; subrec = NEXT_SUBNET_INCLUDING_UNICAST(subrec) )
+ dump_subnet_namelist( subrec, fp );
+
+ if( !we_are_a_wins_client() )
+ dump_subnet_namelist( unicast_subnet, fp );
+
+ if( remote_broadcast_subnet->namelist != NULL )
+ dump_subnet_namelist( remote_broadcast_subnet, fp );
+
+ if( wins_server_subnet != NULL )
+ dump_subnet_namelist( wins_server_subnet, fp );
+ x_fclose( fp );
+}
diff --git a/source/nmbd/nmbd_namequery.c b/source/nmbd/nmbd_namequery.c
new file mode 100644
index 00000000000..1b07852f111
--- /dev/null
+++ b/source/nmbd/nmbd_namequery.c
@@ -0,0 +1,268 @@
+/*
+ Unix SMB/CIFS implementation.
+ NBT netbios routines and daemon - version 2
+ Copyright (C) Andrew Tridgell 1994-1998
+ Copyright (C) Luke Kenneth Casson Leighton 1994-1998
+ Copyright (C) Jeremy Allison 1994-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 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"
+
+/****************************************************************************
+ Deal with a response packet when querying a name.
+****************************************************************************/
+
+static void query_name_response( struct subnet_record *subrec,
+ struct response_record *rrec,
+ struct packet_struct *p)
+{
+ struct nmb_packet *nmb = &p->packet.nmb;
+ BOOL success = False;
+ struct nmb_name *question_name = &rrec->packet->packet.nmb.question.question_name;
+ struct in_addr answer_ip;
+
+ zero_ip(&answer_ip);
+
+ /* Ensure we don't retry the query but leave the response record cleanup
+ to the timeout code. We may get more answer responses in which case
+ we should mark the name in conflict.. */
+ rrec->repeat_count = 0;
+
+ if(rrec->num_msgs == 1) {
+ /* This is the first response. */
+
+ if(nmb->header.opcode == NMB_WACK_OPCODE) {
+ /* WINS server is telling us to wait. Pretend we didn't get
+ the response but don't send out any more query requests. */
+
+ if( DEBUGLVL( 5 ) ) {
+ dbgtext( "query_name_response: " );
+ dbgtext( "WACK from WINS server %s ", inet_ntoa(p->ip) );
+ dbgtext( "in querying name %s ", nmb_namestr(question_name) );
+ dbgtext( "on subnet %s.\n", subrec->subnet_name );
+ }
+
+ rrec->repeat_count = 0;
+ /* How long we should wait for. */
+ rrec->repeat_time = p->timestamp + nmb->answers->ttl;
+ rrec->num_msgs--;
+ return;
+ } else if(nmb->header.rcode != 0) {
+
+ success = False;
+
+ if( DEBUGLVL( 5 ) ) {
+ dbgtext( "query_name_response: On subnet %s ", subrec->subnet_name );
+ dbgtext( "- negative response from IP %s ", inet_ntoa(p->ip) );
+ dbgtext( "for name %s. ", nmb_namestr(question_name) );
+ dbgtext( "Error code was %d.\n", nmb->header.rcode );
+ }
+ } else {
+ if (!nmb->answers) {
+ dbgtext( "query_name_response: On subnet %s ", subrec->subnet_name );
+ dbgtext( "IP %s ", inet_ntoa(p->ip) );
+ dbgtext( "returned a success response with no answer\n" );
+ return;
+ }
+
+ success = True;
+
+ putip((char *)&answer_ip,&nmb->answers->rdata[2]);
+
+ if( DEBUGLVL( 5 ) ) {
+ dbgtext( "query_name_response: On subnet %s ", subrec->subnet_name );
+ dbgtext( "- positive response from IP %s ", inet_ntoa(p->ip) );
+ dbgtext( "for name %s. ", nmb_namestr(question_name) );
+ dbgtext( "IP of that name is %s\n", inet_ntoa(answer_ip) );
+ }
+
+ /* Interestingly, we could add these names to our namelists, and
+ change nmbd to a model that checked its own name cache first,
+ before sending out a query. This is a task for another day, though.
+ */
+ }
+ } else if( rrec->num_msgs > 1) {
+
+ if( DEBUGLVL( 0 ) ) {
+ if (nmb->answers)
+ putip( (char *)&answer_ip, &nmb->answers->rdata[2] );
+ dbgtext( "query_name_response: " );
+ dbgtext( "Multiple (%d) responses ", rrec->num_msgs );
+ dbgtext( "received for a query on subnet %s ", subrec->subnet_name );
+ dbgtext( "for name %s.\nThis response ", nmb_namestr(question_name) );
+ dbgtext( "was from IP %s, reporting ", inet_ntoa(p->ip) );
+ dbgtext( "an IP address of %s.\n", inet_ntoa(answer_ip) );
+ }
+
+ /* We have already called the success or fail function, so we
+ don't call again here. Leave the response record around in
+ case we get more responses. */
+
+ return;
+ }
+
+ if(success && rrec->success_fn)
+ (*(query_name_success_function)rrec->success_fn)(subrec, rrec->userdata, question_name, answer_ip, nmb->answers);
+ else if( rrec->fail_fn)
+ (*(query_name_fail_function)rrec->fail_fn)(subrec, rrec, question_name, nmb->header.rcode);
+
+}
+
+/****************************************************************************
+ Deal with a timeout when querying a name.
+****************************************************************************/
+
+static void query_name_timeout_response(struct subnet_record *subrec,
+ struct response_record *rrec)
+{
+ struct nmb_packet *sent_nmb = &rrec->packet->packet.nmb;
+ /* We can only fail here, never succeed. */
+ BOOL failed = True;
+ struct nmb_name *question_name = &sent_nmb->question.question_name;
+
+ if(rrec->num_msgs != 0) {
+ /* We got at least one response, and have called the success/fail
+ function already. */
+
+ failed = False;
+ }
+
+ if(failed) {
+ if( DEBUGLVL( 5 ) ) {
+ dbgtext( "query_name_timeout_response: No response to " );
+ dbgtext( "query for name %s ", nmb_namestr(question_name) );
+ dbgtext( "on subnet %s.\n", subrec->subnet_name );
+ }
+
+ if(rrec->fail_fn)
+ (*(query_name_fail_function)rrec->fail_fn)(subrec, rrec, question_name, 0);
+ }
+
+ remove_response_record(subrec, rrec);
+}
+
+/****************************************************************************
+ Lookup a name on our local namelists. We check the lmhosts file first. If the
+ name is not there we look for the name on the given subnet.
+****************************************************************************/
+
+static BOOL query_local_namelists(struct subnet_record *subrec, struct nmb_name *nmbname,
+ struct name_record **namerecp)
+{
+ struct name_record *namerec;
+
+ *namerecp = NULL;
+
+ if(find_name_in_lmhosts(nmbname, namerecp))
+ return True;
+
+ if((namerec = find_name_on_subnet(subrec, nmbname, FIND_ANY_NAME))==NULL)
+ return False;
+
+ if( NAME_IS_ACTIVE(namerec) && ( (namerec->data.source == SELF_NAME) || (namerec->data.source == LMHOSTS_NAME) ) ) {
+ *namerecp = namerec;
+ return True;
+ }
+ return False;
+}
+
+/****************************************************************************
+ Try and query for a name.
+****************************************************************************/
+
+BOOL query_name(struct subnet_record *subrec, const char *name, int type,
+ query_name_success_function success_fn,
+ query_name_fail_function fail_fn,
+ struct userdata_struct *userdata)
+{
+ struct nmb_name nmbname;
+ struct name_record *namerec;
+
+ make_nmb_name(&nmbname, name, type);
+
+ /*
+ * We need to check our local namelists first.
+ * It may be an magic name, lmhosts name or just
+ * a name we have registered.
+ */
+
+ if(query_local_namelists(subrec, &nmbname, &namerec) == True) {
+ struct res_rec rrec;
+ int i;
+
+ memset((char *)&rrec, '\0', sizeof(struct res_rec));
+
+ /* Fake up the needed res_rec just in case it's used. */
+ rrec.rr_name = nmbname;
+ rrec.rr_type = RR_TYPE_NB;
+ rrec.rr_class = RR_CLASS_IN;
+ rrec.ttl = PERMANENT_TTL;
+ rrec.rdlength = namerec->data.num_ips * 6;
+ if(rrec.rdlength > MAX_DGRAM_SIZE) {
+ if( DEBUGLVL( 0 ) ) {
+ dbgtext( "query_name: nmbd internal error - " );
+ dbgtext( "there are %d ip addresses ", namerec->data.num_ips );
+ dbgtext( "for name %s.\n", nmb_namestr(&nmbname) );
+ }
+ return False;
+ }
+
+ for( i = 0; i < namerec->data.num_ips; i++) {
+ set_nb_flags( &rrec.rdata[i*6], namerec->data.nb_flags );
+ putip( &rrec.rdata[(i*6) + 2], (char *)&namerec->data.ip[i]);
+ }
+
+ /* Call the success function directly. */
+ if(success_fn)
+ (*(query_name_success_function)success_fn)(subrec, userdata, &nmbname, namerec->data.ip[0], &rrec);
+ return False;
+ }
+
+ if(queue_query_name( subrec, query_name_response, query_name_timeout_response, success_fn, fail_fn, userdata, &nmbname) == NULL) {
+ if( DEBUGLVL( 0 ) ) {
+ dbgtext( "query_name: Failed to send packet " );
+ dbgtext( "trying to query name %s\n", nmb_namestr(&nmbname) );
+ }
+ return True;
+ }
+ return False;
+}
+
+/****************************************************************************
+ Try and query for a name from nmbd acting as a WINS server.
+****************************************************************************/
+
+BOOL query_name_from_wins_server(struct in_addr ip_to,
+ const char *name, int type,
+ query_name_success_function success_fn,
+ query_name_fail_function fail_fn,
+ struct userdata_struct *userdata)
+{
+ struct nmb_name nmbname;
+
+ make_nmb_name(&nmbname, name, type);
+
+ if(queue_query_name_from_wins_server( ip_to, query_name_response, query_name_timeout_response, success_fn, fail_fn, userdata, &nmbname) == NULL) {
+ if( DEBUGLVL( 0 ) ) {
+ dbgtext( "query_name_from_wins_server: Failed to send packet " );
+ dbgtext( "trying to query name %s\n", nmb_namestr(&nmbname) );
+ }
+ return True;
+ }
+ return False;
+}
diff --git a/source/nmbd/nmbd_nameregister.c b/source/nmbd/nmbd_nameregister.c
new file mode 100644
index 00000000000..4e11881f063
--- /dev/null
+++ b/source/nmbd/nmbd_nameregister.c
@@ -0,0 +1,537 @@
+/*
+ Unix SMB/CIFS implementation.
+ NBT netbios routines and daemon - version 2
+ Copyright (C) Andrew Tridgell 1994-1998
+ Copyright (C) Luke Kenneth Casson Leighton 1994-1998
+ Copyright (C) Jeremy Allison 1994-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 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"
+
+/* forward declarations */
+static void wins_next_registration(struct response_record *rrec);
+
+
+/****************************************************************************
+ Deal with a response packet when registering one of our names.
+****************************************************************************/
+
+static void register_name_response(struct subnet_record *subrec,
+ struct response_record *rrec, struct packet_struct *p)
+{
+ /*
+ * If we are registering broadcast, then getting a response is an
+ * error - we do not have the name. If we are registering unicast,
+ * then we expect to get a response.
+ */
+
+ struct nmb_packet *nmb = &p->packet.nmb;
+ BOOL bcast = nmb->header.nm_flags.bcast;
+ BOOL success = True;
+ struct nmb_name *question_name = &rrec->packet->packet.nmb.question.question_name;
+ struct nmb_name *answer_name = &nmb->answers->rr_name;
+ struct nmb_packet *sent_nmb = &rrec->packet->packet.nmb;
+ int ttl = 0;
+ uint16 nb_flags = 0;
+ struct in_addr register_ip;
+ fstring reg_name;
+
+ putip(&register_ip,&sent_nmb->additional->rdata[2]);
+ fstrcpy(reg_name, inet_ntoa(register_ip));
+
+ if (subrec == unicast_subnet) {
+ /* we know that this wins server is definately alive - for the moment! */
+ wins_srv_alive(rrec->packet->ip, register_ip);
+ }
+
+ /* Sanity check. Ensure that the answer name in the incoming packet is the
+ same as the requested name in the outgoing packet. */
+
+ if(!question_name || !answer_name) {
+ DEBUG(0,("register_name_response: malformed response (%s is NULL).\n",
+ question_name ? "question_name" : "answer_name" ));
+ return;
+ }
+
+ if(!nmb_name_equal(question_name, answer_name)) {
+ DEBUG(0,("register_name_response: Answer name %s differs from question name %s.\n",
+ nmb_namestr(answer_name), nmb_namestr(question_name)));
+ return;
+ }
+
+ if(bcast) {
+ /*
+ * Special hack to cope with old Samba nmbd's.
+ * Earlier versions of Samba (up to 1.9.16p11) respond
+ * to a broadcast name registration of WORKGROUP<1b> when
+ * they should not. Hence, until these versions are gone,
+ * we should treat such errors as success for this particular
+ * case only. jallison@whistle.com.
+ */
+
+#if 1 /* OLD_SAMBA_SERVER_HACK */
+ unstring ans_name;
+ pull_ascii_nstring(ans_name, sizeof(ans_name), answer_name->name);
+ if((nmb->header.rcode == ACT_ERR) && strequal(lp_workgroup(), ans_name) &&
+ (answer_name->name_type == 0x1b)) {
+ /* Pretend we did not get this. */
+ rrec->num_msgs--;
+
+ DEBUG(5,("register_name_response: Ignoring broadcast response to registration of name %s due to old Samba server bug.\n",
+ nmb_namestr(answer_name)));
+ return;
+ }
+#endif /* OLD_SAMBA_SERVER_HACK */
+
+ /* Someone else has the name. Log the problem. */
+ DEBUG(1,("register_name_response: Failed to register name %s IP %s on subnet %s via broadcast. Error code was %d. Reject came from IP %s\n",
+ nmb_namestr(answer_name),
+ reg_name,
+ subrec->subnet_name, nmb->header.rcode, inet_ntoa(p->ip)));
+ success = False;
+ } else {
+ /* Unicast - check to see if the response allows us to have the name. */
+ if (nmb->header.opcode == NMB_WACK_OPCODE) {
+ /* WINS server is telling us to wait. Pretend we didn't get
+ the response but don't send out any more register requests. */
+
+ DEBUG(5,("register_name_response: WACK from WINS server %s in registering name %s IP %s\n",
+ inet_ntoa(p->ip), nmb_namestr(answer_name), reg_name));
+
+ rrec->repeat_count = 0;
+ /* How long we should wait for. */
+ rrec->repeat_time = p->timestamp + nmb->answers->ttl;
+ rrec->num_msgs--;
+ return;
+ } else if (nmb->header.rcode != 0) {
+ /* Error code - we didn't get the name. */
+ success = False;
+
+ DEBUG(0,("register_name_response: %sserver at IP %s rejected our name registration of %s IP %s with error code %d.\n",
+ subrec==unicast_subnet?"WINS ":"",
+ inet_ntoa(p->ip),
+ nmb_namestr(answer_name),
+ reg_name,
+ nmb->header.rcode));
+ } else {
+ success = True;
+ /* Get the data we need to pass to the success function. */
+ nb_flags = get_nb_flags(nmb->answers->rdata);
+ ttl = nmb->answers->ttl;
+
+ /* send off a registration for the next IP, if any */
+ wins_next_registration(rrec);
+ }
+ }
+
+ DEBUG(5,("register_name_response: %s in registering %sname %s IP %s with %s.\n",
+ success ? "success" : "failure",
+ subrec==unicast_subnet?"WINS ":"",
+ nmb_namestr(answer_name),
+ reg_name,
+ inet_ntoa(rrec->packet->ip)));
+
+ if(success) {
+ /* Enter the registered name into the subnet name database before calling
+ the success function. */
+ standard_success_register(subrec, rrec->userdata, answer_name, nb_flags, ttl, register_ip);
+ if( rrec->success_fn)
+ (*(register_name_success_function)rrec->success_fn)(subrec, rrec->userdata, answer_name, nb_flags, ttl, register_ip);
+ } else {
+ if( rrec->fail_fn)
+ (*(register_name_fail_function)rrec->fail_fn)(subrec, rrec, question_name);
+ /* Remove the name. */
+ standard_fail_register( subrec, rrec, question_name);
+ }
+
+ /* Ensure we don't retry. */
+ remove_response_record(subrec, rrec);
+}
+
+/****************************************************************************
+ Deal with a timeout of a WINS registration request
+****************************************************************************/
+
+static void wins_registration_timeout(struct subnet_record *subrec,
+ struct response_record *rrec)
+{
+ struct userdata_struct *userdata = rrec->userdata;
+ struct nmb_packet *sent_nmb = &rrec->packet->packet.nmb;
+ struct nmb_name *nmbname = &sent_nmb->question.question_name;
+ struct in_addr register_ip;
+ fstring src_addr;
+
+ putip(&register_ip,&sent_nmb->additional->rdata[2]);
+
+ fstrcpy(src_addr, inet_ntoa(register_ip));
+
+ DEBUG(2,("wins_registration_timeout: WINS server %s timed out registering IP %s\n",
+ inet_ntoa(rrec->packet->ip), src_addr));
+
+ /* mark it temporarily dead for this source address */
+ wins_srv_died(rrec->packet->ip, register_ip);
+
+ /* if we have some userdata then use that to work out what
+ wins server to try next */
+ if (userdata) {
+ const char *tag = (const char *)userdata->data;
+
+ /* try the next wins server in our failover list for
+ this tag */
+ rrec->packet->ip = wins_srv_ip_tag(tag, register_ip);
+ }
+
+ /* if we have run out of wins servers for this tag then they
+ must all have timed out. We treat this as *success*, not
+ failure, and go into our standard name refresh mode. This
+ copes with all the wins servers being down */
+ if (wins_srv_is_dead(rrec->packet->ip, register_ip)) {
+ uint16 nb_flags = get_nb_flags(sent_nmb->additional->rdata);
+ int ttl = sent_nmb->additional->ttl;
+
+ standard_success_register(subrec, userdata, nmbname, nb_flags, ttl, register_ip);
+ if(rrec->success_fn) {
+ (*(register_name_success_function)rrec->success_fn)(subrec,
+ rrec->userdata,
+ nmbname,
+ nb_flags,
+ ttl,
+ register_ip);
+ }
+
+ /* send off a registration for the next IP, if any */
+ wins_next_registration(rrec);
+
+ /* don't need to send this packet any more */
+ remove_response_record(subrec, rrec);
+ return;
+ }
+
+ /* we will be moving to the next WINS server for this group,
+ send it immediately */
+ rrec->repeat_count = 2;
+ rrec->repeat_time = time(NULL) + 1;
+ rrec->in_expiration_processing = False;
+
+ DEBUG(6,("Retrying register of name %s IP %s with WINS server %s\n",
+ nmb_namestr(nmbname), src_addr, inet_ntoa(rrec->packet->ip)));
+
+ /* notice that we don't remove the response record. This keeps
+ us trying to register with each of our failover wins servers */
+}
+
+/****************************************************************************
+ Deal with a timeout when registering one of our names.
+****************************************************************************/
+
+static void register_name_timeout_response(struct subnet_record *subrec,
+ struct response_record *rrec)
+{
+ /*
+ * If we are registering unicast, then NOT getting a response is an
+ * error - we do not have the name. If we are registering broadcast,
+ * then we don't expect to get a response.
+ */
+
+ struct nmb_packet *sent_nmb = &rrec->packet->packet.nmb;
+ BOOL bcast = sent_nmb->header.nm_flags.bcast;
+ BOOL success = False;
+ struct nmb_name *question_name = &sent_nmb->question.question_name;
+ uint16 nb_flags = 0;
+ int ttl = 0;
+ struct in_addr registered_ip;
+
+ if (bcast) {
+ if(rrec->num_msgs == 0) {
+ /* Not receiving a message is success for broadcast registration. */
+ success = True;
+
+ /* Pull the success values from the original request packet. */
+ nb_flags = get_nb_flags(sent_nmb->additional->rdata);
+ ttl = sent_nmb->additional->ttl;
+ putip(&registered_ip,&sent_nmb->additional->rdata[2]);
+ }
+ } else {
+ /* wins timeouts are special */
+ wins_registration_timeout(subrec, rrec);
+ return;
+ }
+
+ DEBUG(5,("register_name_timeout_response: %s in registering name %s on subnet %s.\n",
+ success ? "success" : "failure", nmb_namestr(question_name), subrec->subnet_name));
+ if(success) {
+ /* Enter the registered name into the subnet name database before calling
+ the success function. */
+ standard_success_register(subrec, rrec->userdata, question_name, nb_flags, ttl, registered_ip);
+ if( rrec->success_fn)
+ (*(register_name_success_function)rrec->success_fn)(subrec, rrec->userdata, question_name, nb_flags, ttl, registered_ip);
+ } else {
+ if( rrec->fail_fn)
+ (*(register_name_fail_function)rrec->fail_fn)(subrec, rrec, question_name);
+ /* Remove the name. */
+ standard_fail_register( subrec, rrec, question_name);
+ }
+
+ /* Ensure we don't retry. */
+ remove_response_record(subrec, rrec);
+}
+
+/****************************************************************************
+ Initiate one multi-homed name registration packet.
+****************************************************************************/
+
+static void multihomed_register_one(struct nmb_name *nmbname,
+ uint16 nb_flags,
+ register_name_success_function success_fn,
+ register_name_fail_function fail_fn,
+ struct in_addr ip,
+ const char *tag)
+{
+ struct userdata_struct *userdata;
+ struct in_addr wins_ip = wins_srv_ip_tag(tag, ip);
+ fstring ip_str;
+
+ userdata = (struct userdata_struct *)malloc(sizeof(*userdata) + strlen(tag) + 1);
+ if (!userdata) {
+ DEBUG(0,("Failed to allocate userdata structure!\n"));
+ return;
+ }
+ ZERO_STRUCTP(userdata);
+ userdata->userdata_len = strlen(tag) + 1;
+ strlcpy(userdata->data, tag, userdata->userdata_len);
+
+ fstrcpy(ip_str, inet_ntoa(ip));
+
+ DEBUG(6,("Registering name %s IP %s with WINS server %s using tag '%s'\n",
+ nmb_namestr(nmbname), ip_str, inet_ntoa(wins_ip), tag));
+
+ if (queue_register_multihomed_name(unicast_subnet,
+ register_name_response,
+ register_name_timeout_response,
+ success_fn,
+ fail_fn,
+ userdata,
+ nmbname,
+ nb_flags,
+ ip,
+ wins_ip) == NULL) {
+ DEBUG(0,("multihomed_register_one: Failed to send packet trying to register name %s IP %s\n",
+ nmb_namestr(nmbname), inet_ntoa(ip)));
+ }
+
+ free(userdata);
+}
+
+/****************************************************************************
+ We have finished the registration of one IP and need to see if we have
+ any more IPs left to register with this group of wins server for this name.
+****************************************************************************/
+
+static void wins_next_registration(struct response_record *rrec)
+{
+ struct nmb_packet *sent_nmb = &rrec->packet->packet.nmb;
+ struct nmb_name *nmbname = &sent_nmb->question.question_name;
+ uint16 nb_flags = get_nb_flags(sent_nmb->additional->rdata);
+ struct userdata_struct *userdata = rrec->userdata;
+ const char *tag;
+ struct in_addr last_ip;
+ struct subnet_record *subrec;
+
+ putip(&last_ip,&sent_nmb->additional->rdata[2]);
+
+ if (!userdata) {
+ /* it wasn't multi-homed */
+ return;
+ }
+
+ tag = (const char *)userdata->data;
+
+ for (subrec = FIRST_SUBNET; subrec; subrec = NEXT_SUBNET_EXCLUDING_UNICAST(subrec)) {
+ if (ip_equal(last_ip, subrec->myip)) {
+ subrec = NEXT_SUBNET_EXCLUDING_UNICAST(subrec);
+ break;
+ }
+ }
+
+ if (!subrec) {
+ /* no more to do! */
+ return;
+ }
+
+ switch (sent_nmb->header.opcode) {
+ case NMB_NAME_MULTIHOMED_REG_OPCODE:
+ multihomed_register_one(nmbname, nb_flags, NULL, NULL, subrec->myip, tag);
+ break;
+ case NMB_NAME_REFRESH_OPCODE_8:
+ queue_wins_refresh(nmbname,
+ register_name_response,
+ register_name_timeout_response,
+ nb_flags, subrec->myip, tag);
+ break;
+ }
+}
+
+/****************************************************************************
+ Try and register one of our names on the unicast subnet - multihomed.
+****************************************************************************/
+
+static void multihomed_register_name(struct nmb_name *nmbname, uint16 nb_flags,
+ register_name_success_function success_fn,
+ register_name_fail_function fail_fn)
+{
+ /*
+ If we are adding a group name, we just send multiple
+ register name packets to the WINS server (this is an
+ internet group name.
+
+ If we are adding a unique name, We need first to add
+ our names to the unicast subnet namelist. This is
+ because when a WINS server receives a multihomed
+ registration request, the first thing it does is to
+ send a name query to the registering machine, to see
+ if it has put the name in it's local namelist.
+ We need the name there so the query response code in
+ nmbd_incomingrequests.c will find it.
+
+ We are adding this name prematurely (we don't really
+ have it yet), but as this is on the unicast subnet
+ only we will get away with this (only the WINS server
+ will ever query names from us on this subnet).
+ */
+ int num_ips=0;
+ int i, t;
+ struct subnet_record *subrec;
+ char **wins_tags;
+ struct in_addr *ip_list;
+ unstring name;
+
+ for(subrec = FIRST_SUBNET; subrec; subrec = NEXT_SUBNET_EXCLUDING_UNICAST(subrec) )
+ num_ips++;
+
+ if((ip_list = (struct in_addr *)malloc(num_ips * sizeof(struct in_addr)))==NULL) {
+ DEBUG(0,("multihomed_register_name: malloc fail !\n"));
+ return;
+ }
+
+ for (subrec = FIRST_SUBNET, i = 0;
+ subrec;
+ subrec = NEXT_SUBNET_EXCLUDING_UNICAST(subrec), i++ ) {
+ ip_list[i] = subrec->myip;
+ }
+
+ pull_ascii_nstring(name, sizeof(name), nmbname->name);
+ add_name_to_subnet(unicast_subnet, name, nmbname->name_type,
+ nb_flags, lp_max_ttl(), SELF_NAME,
+ num_ips, ip_list);
+
+ /* get the list of wins tags - we try to register for each of them */
+ wins_tags = wins_srv_tags();
+
+ /* Now try and register the name for each wins tag. Note that
+ at this point we only register our first IP with each wins
+ group. We will register the rest from
+ wins_next_registration() when we get the reply for this
+ one. That follows the way W2K does things (tridge)
+ */
+ for (t=0; wins_tags && wins_tags[t]; t++) {
+ multihomed_register_one(nmbname, nb_flags,
+ success_fn, fail_fn,
+ ip_list[0],
+ wins_tags[t]);
+ }
+
+ wins_srv_tags_free(wins_tags);
+
+ SAFE_FREE(ip_list);
+}
+
+/****************************************************************************
+ Try and register one of our names.
+****************************************************************************/
+
+void register_name(struct subnet_record *subrec,
+ const char *name, int type, uint16 nb_flags,
+ register_name_success_function success_fn,
+ register_name_fail_function fail_fn,
+ struct userdata_struct *userdata)
+{
+ struct nmb_name nmbname;
+ nstring nname;
+
+ errno = 0;
+ push_ascii_nstring(nname, name);
+ if (errno == E2BIG) {
+ unstring tname;
+ pull_ascii_nstring(tname, sizeof(tname), nname);
+ DEBUG(0,("register_name: NetBIOS name %s is too long. Truncating to %s\n",
+ name, tname));
+ make_nmb_name(&nmbname, tname, type);
+ } else {
+ make_nmb_name(&nmbname, name, type);
+ }
+
+ /* Always set the NB_ACTIVE flag on the name we are
+ registering. Doesn't make sense without it.
+ */
+
+ nb_flags |= NB_ACTIVE;
+
+ if (subrec == unicast_subnet) {
+ /* we now always do multi-homed registration if we are
+ registering to a WINS server. This copes much
+ better with complex WINS setups */
+ multihomed_register_name(&nmbname, nb_flags,
+ success_fn, fail_fn);
+ return;
+ }
+
+ if (queue_register_name(subrec,
+ register_name_response,
+ register_name_timeout_response,
+ success_fn,
+ fail_fn,
+ userdata,
+ &nmbname,
+ nb_flags) == NULL) {
+ DEBUG(0,("register_name: Failed to send packet trying to register name %s\n",
+ nmb_namestr(&nmbname)));
+ }
+}
+
+/****************************************************************************
+ Try and refresh one of our names. This is *only* called for WINS refresh
+****************************************************************************/
+
+void wins_refresh_name(struct name_record *namerec)
+{
+ int t;
+ char **wins_tags;
+
+ /* get the list of wins tags - we try to refresh for each of them */
+ wins_tags = wins_srv_tags();
+
+ for (t=0; wins_tags && wins_tags[t]; t++) {
+ queue_wins_refresh(&namerec->name,
+ register_name_response,
+ register_name_timeout_response,
+ namerec->data.nb_flags,
+ namerec->data.ip[0], wins_tags[t]);
+ }
+
+ wins_srv_tags_free(wins_tags);
+}
diff --git a/source/nmbd/nmbd_namerelease.c b/source/nmbd/nmbd_namerelease.c
new file mode 100644
index 00000000000..0611ca93234
--- /dev/null
+++ b/source/nmbd/nmbd_namerelease.c
@@ -0,0 +1,222 @@
+/*
+ Unix SMB/CIFS implementation.
+ NBT netbios routines and daemon - version 2
+ Copyright (C) Andrew Tridgell 1994-1998
+ Copyright (C) Luke Kenneth Casson Leighton 1994-1998
+ Copyright (C) Jeremy Allison 1994-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 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"
+
+/****************************************************************************
+ Deal with a response packet when releasing one of our names.
+****************************************************************************/
+
+static void release_name_response(struct subnet_record *subrec,
+ struct response_record *rrec, struct packet_struct *p)
+{
+ /*
+ * If we are releasing broadcast, then getting a response is an
+ * error. If we are releasing unicast, then we expect to get a response.
+ */
+ struct nmb_packet *nmb = &p->packet.nmb;
+ BOOL bcast = nmb->header.nm_flags.bcast;
+ BOOL success = True;
+ struct nmb_name *question_name = &rrec->packet->packet.nmb.question.question_name;
+ struct nmb_name *answer_name = &nmb->answers->rr_name;
+ struct in_addr released_ip;
+
+ /* Sanity check. Ensure that the answer name in the incoming packet is the
+ same as the requested name in the outgoing packet. */
+ if (!nmb_name_equal(question_name, answer_name)) {
+ DEBUG(0,("release_name_response: Answer name %s differs from question name %s.\n",
+ nmb_namestr(answer_name), nmb_namestr(question_name)));
+ return;
+ }
+
+ if (bcast) {
+ /* Someone sent a response to a bcast release? ignore it. */
+ return;
+ }
+
+ /* Unicast - check to see if the response allows us to release the name. */
+ if (nmb->header.rcode != 0) {
+ /* Error code - we were told not to release the name ! What now ! */
+ success = False;
+
+ DEBUG(0,("release_name_response: WINS server at IP %s rejected our \
+name release of name %s with error code %d.\n",
+ inet_ntoa(p->ip),
+ nmb_namestr(answer_name), nmb->header.rcode));
+ } else if (nmb->header.opcode == NMB_WACK_OPCODE) {
+ /* WINS server is telling us to wait. Pretend we didn't get
+ the response but don't send out any more release requests. */
+
+ DEBUG(5,("release_name_response: WACK from WINS server %s in releasing \
+name %s on subnet %s.\n",
+ inet_ntoa(p->ip), nmb_namestr(answer_name), subrec->subnet_name));
+
+ rrec->repeat_count = 0;
+ /* How long we should wait for. */
+ rrec->repeat_time = p->timestamp + nmb->answers->ttl;
+ rrec->num_msgs--;
+ return;
+ }
+
+ DEBUG(5,("release_name_response: %s in releasing name %s on subnet %s.\n",
+ success ? "success" : "failure", nmb_namestr(answer_name), subrec->subnet_name));
+ if (success) {
+ putip((char*)&released_ip ,&nmb->answers->rdata[2]);
+
+ if(rrec->success_fn)
+ (*(release_name_success_function)rrec->success_fn)(subrec, rrec->userdata, answer_name, released_ip);
+ standard_success_release( subrec, rrec->userdata, answer_name, released_ip);
+ } else {
+ /* We have no standard_fail_release - maybe we should add one ? */
+ if (rrec->fail_fn) {
+ (*(release_name_fail_function)rrec->fail_fn)(subrec, rrec, answer_name);
+ }
+ }
+
+ remove_response_record(subrec, rrec);
+}
+
+/****************************************************************************
+ Deal with a timeout when releasing one of our names.
+****************************************************************************/
+
+static void release_name_timeout_response(struct subnet_record *subrec,
+ struct response_record *rrec)
+{
+ /* a release is *always* considered to be successful when it
+ times out. This doesn't cause problems as if a WINS server
+ doesn't respond and someone else wants the name then the
+ normal WACK/name query from the WINS server will cope */
+ struct nmb_packet *sent_nmb = &rrec->packet->packet.nmb;
+ BOOL bcast = sent_nmb->header.nm_flags.bcast;
+ struct nmb_name *question_name = &sent_nmb->question.question_name;
+ struct in_addr released_ip;
+
+ /* Get the ip address we were trying to release. */
+ putip((char*)&released_ip ,&sent_nmb->additional->rdata[2]);
+
+ if (!bcast) {
+ /* mark the WINS server temporarily dead */
+ wins_srv_died(rrec->packet->ip, released_ip);
+ }
+
+ DEBUG(5,("release_name_timeout_response: success in releasing name %s on subnet %s.\n",
+ nmb_namestr(question_name), subrec->subnet_name));
+
+ if (rrec->success_fn) {
+ (*(release_name_success_function)rrec->success_fn)(subrec, rrec->userdata, question_name, released_ip);
+ }
+
+ standard_success_release( subrec, rrec->userdata, question_name, released_ip);
+ remove_response_record(subrec, rrec);
+}
+
+
+/*
+ when releasing a name with WINS we need to send the release to each of
+ the WINS groups
+*/
+static void wins_release_name(struct name_record *namerec,
+ release_name_success_function success_fn,
+ release_name_fail_function fail_fn,
+ struct userdata_struct *userdata)
+{
+ int t, i;
+ char **wins_tags;
+
+ /* get the list of wins tags - we try to release for each of them */
+ wins_tags = wins_srv_tags();
+
+ for (t=0;wins_tags && wins_tags[t]; t++) {
+ for (i = 0; i < namerec->data.num_ips; i++) {
+ struct in_addr wins_ip = wins_srv_ip_tag(wins_tags[t], namerec->data.ip[i]);
+
+ BOOL last_one = ((i==namerec->data.num_ips - 1) && !wins_tags[t+1]);
+ if (queue_release_name(unicast_subnet,
+ release_name_response,
+ release_name_timeout_response,
+ last_one?success_fn : NULL,
+ last_one? fail_fn : NULL,
+ last_one? userdata : NULL,
+ &namerec->name,
+ namerec->data.nb_flags,
+ namerec->data.ip[i],
+ wins_ip) == NULL) {
+ DEBUG(0,("release_name: Failed to send packet trying to release name %s IP %s\n",
+ nmb_namestr(&namerec->name), inet_ntoa(namerec->data.ip[i]) ));
+ }
+ }
+ }
+
+ wins_srv_tags_free(wins_tags);
+}
+
+
+/****************************************************************************
+ Try and release one of our names.
+****************************************************************************/
+
+void release_name(struct subnet_record *subrec, struct name_record *namerec,
+ release_name_success_function success_fn,
+ release_name_fail_function fail_fn,
+ struct userdata_struct *userdata)
+{
+ int i;
+
+ /* Ensure it's a SELF name, and in the ACTIVE state. */
+ if ((namerec->data.source != SELF_NAME) || !NAME_IS_ACTIVE(namerec)) {
+ DEBUG(0,("release_name: Cannot release name %s from subnet %s. Source was %d \n",
+ nmb_namestr(&namerec->name), subrec->subnet_name, namerec->data.source));
+ return;
+ }
+
+ /* Set the name into the deregistering state. */
+ namerec->data.nb_flags |= NB_DEREG;
+
+ /* wins releases are a bit different */
+ if (subrec == unicast_subnet) {
+ wins_release_name(namerec, success_fn, fail_fn, userdata);
+ return;
+ }
+
+ /*
+ * Go through and release the name for all known ip addresses.
+ * Only call the success/fail function on the last one (it should
+ * only be done once).
+ */
+ for (i = 0; i < namerec->data.num_ips; i++) {
+ if (queue_release_name(subrec,
+ release_name_response,
+ release_name_timeout_response,
+ (i == (namerec->data.num_ips - 1)) ? success_fn : NULL,
+ (i == (namerec->data.num_ips - 1)) ? fail_fn : NULL,
+ (i == (namerec->data.num_ips - 1)) ? userdata : NULL,
+ &namerec->name,
+ namerec->data.nb_flags,
+ namerec->data.ip[i],
+ subrec->bcast_ip) == NULL) {
+ DEBUG(0,("release_name: Failed to send packet trying to release name %s IP %s\n",
+ nmb_namestr(&namerec->name), inet_ntoa(namerec->data.ip[i]) ));
+ }
+ }
+}
diff --git a/source/nmbd/nmbd_nodestatus.c b/source/nmbd/nmbd_nodestatus.c
new file mode 100644
index 00000000000..0ea5d6a8182
--- /dev/null
+++ b/source/nmbd/nmbd_nodestatus.c
@@ -0,0 +1,92 @@
+/*
+ Unix SMB/CIFS implementation.
+ NBT netbios routines and daemon - version 2
+ Copyright (C) Andrew Tridgell 1994-1998
+ Copyright (C) Luke Kenneth Casson Leighton 1994-1998
+ Copyright (C) Jeremy Allison 1994-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 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"
+
+/****************************************************************************
+ Deal with a successful node status response.
+****************************************************************************/
+
+static void node_status_response(struct subnet_record *subrec,
+ struct response_record *rrec, struct packet_struct *p)
+{
+ struct nmb_packet *nmb = &p->packet.nmb;
+ struct nmb_name *question_name = &rrec->packet->packet.nmb.question.question_name;
+ struct nmb_name *answer_name = &nmb->answers->rr_name;
+
+ /* Sanity check. Ensure that the answer name in the incoming packet is the
+ same as the requested name in the outgoing packet. */
+
+ if(!nmb_name_equal(question_name, answer_name)) {
+ DEBUG(0,("node_status_response: Answer name %s differs from question \
+name %s.\n", nmb_namestr(answer_name), nmb_namestr(question_name)));
+ return;
+ }
+
+ DEBUG(5,("node_status_response: response from name %s on subnet %s.\n",
+ nmb_namestr(answer_name), subrec->subnet_name));
+
+ /* Just send the whole answer resource record for the success function to parse. */
+ if(rrec->success_fn)
+ (*(node_status_success_function)rrec->success_fn)(subrec, rrec->userdata, nmb->answers, p->ip);
+
+ /* Ensure we don't retry. */
+ remove_response_record(subrec, rrec);
+}
+
+/****************************************************************************
+ Deal with a timeout when requesting a node status.
+****************************************************************************/
+
+static void node_status_timeout_response(struct subnet_record *subrec,
+ struct response_record *rrec)
+{
+ struct nmb_packet *sent_nmb = &rrec->packet->packet.nmb;
+ struct nmb_name *question_name = &sent_nmb->question.question_name;
+
+ DEBUG(5,("node_status_timeout_response: failed to get node status from name %s on subnet %s\n",
+ nmb_namestr(question_name), subrec->subnet_name));
+
+ if( rrec->fail_fn)
+ (*rrec->fail_fn)(subrec, rrec);
+
+ /* Ensure we don't retry. */
+ remove_response_record(subrec, rrec);
+}
+
+/****************************************************************************
+ Try and do a node status to a name - given the name & IP address.
+****************************************************************************/
+
+BOOL node_status(struct subnet_record *subrec, struct nmb_name *nmbname,
+ struct in_addr send_ip, node_status_success_function success_fn,
+ node_status_fail_function fail_fn, struct userdata_struct *userdata)
+{
+ if(queue_node_status( subrec, node_status_response, node_status_timeout_response,
+ success_fn, fail_fn, userdata, nmbname, send_ip)==NULL) {
+ DEBUG(0,("node_status: Failed to send packet trying to get node status for \
+name %s, IP address %s\n", nmb_namestr(nmbname), inet_ntoa(send_ip)));
+ return True;
+ }
+ return False;
+}
diff --git a/source/nmbd/nmbd_packets.c b/source/nmbd/nmbd_packets.c
new file mode 100644
index 00000000000..d8b50a1b2e9
--- /dev/null
+++ b/source/nmbd/nmbd_packets.c
@@ -0,0 +1,1885 @@
+/*
+ Unix SMB/CIFS implementation.
+ NBT netbios routines and daemon - version 2
+ Copyright (C) Andrew Tridgell 1994-1998
+ Copyright (C) Luke Kenneth Casson Leighton 1994-1998
+ Copyright (C) Jeremy Allison 1994-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 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"
+
+extern int ClientNMB;
+extern int ClientDGRAM;
+extern int global_nmb_port;
+
+extern int num_response_packets;
+
+extern struct in_addr loopback_ip;
+
+static void queue_packet(struct packet_struct *packet);
+
+BOOL rescan_listen_set = False;
+
+
+/*******************************************************************
+ The global packet linked-list. Incoming entries are
+ added to the end of this list. It is supposed to remain fairly
+ short so we won't bother with an end pointer.
+******************************************************************/
+
+static struct packet_struct *packet_queue = NULL;
+
+/***************************************************************************
+Utility function to find the specific fd to send a packet out on.
+**************************************************************************/
+
+static int find_subnet_fd_for_address( struct in_addr local_ip )
+{
+ struct subnet_record *subrec;
+
+ for( subrec = FIRST_SUBNET; subrec; subrec = NEXT_SUBNET_EXCLUDING_UNICAST(subrec))
+ if(ip_equal(local_ip, subrec->myip))
+ return subrec->nmb_sock;
+
+ return ClientNMB;
+}
+
+/***************************************************************************
+Utility function to find the specific fd to send a mailslot packet out on.
+**************************************************************************/
+
+static int find_subnet_mailslot_fd_for_address( struct in_addr local_ip )
+{
+ struct subnet_record *subrec;
+
+ for( subrec = FIRST_SUBNET; subrec; subrec = NEXT_SUBNET_EXCLUDING_UNICAST(subrec))
+ if(ip_equal(local_ip, subrec->myip))
+ return subrec->dgram_sock;
+
+ return ClientDGRAM;
+}
+
+/***************************************************************************
+Get/Set problematic nb_flags as network byte order 16 bit int.
+**************************************************************************/
+
+uint16 get_nb_flags(char *buf)
+{
+ return ((((uint16)*buf)&0xFFFF) & NB_FLGMSK);
+}
+
+void set_nb_flags(char *buf, uint16 nb_flags)
+{
+ *buf++ = ((nb_flags & NB_FLGMSK) & 0xFF);
+ *buf = '\0';
+}
+
+/***************************************************************************
+Dumps out the browse packet data.
+**************************************************************************/
+
+static void debug_browse_data(char *outbuf, int len)
+{
+ int i,j;
+
+ DEBUG( 4, ( "debug_browse_data():\n" ) );
+ for (i = 0; i < len; i+= 16) {
+ DEBUGADD( 4, ( "%3x char ", i ) );
+
+ for (j = 0; j < 16; j++) {
+ unsigned char x;
+ if (i+j >= len)
+ break;
+
+ x = outbuf[i+j];
+ if (x < 32 || x > 127)
+ x = '.';
+
+ DEBUGADD( 4, ( "%c", x ) );
+ }
+
+ DEBUGADD( 4, ( "%*s hex", 16-j, "" ) );
+
+ for (j = 0; j < 16; j++) {
+ if (i+j >= len)
+ break;
+ DEBUGADD( 4, ( " %02x", (unsigned char)outbuf[i+j] ) );
+ }
+
+ DEBUGADD( 4, ("\n") );
+ }
+}
+
+/***************************************************************************
+ Generates the unique transaction identifier
+**************************************************************************/
+
+static uint16 name_trn_id=0;
+
+static uint16 generate_name_trn_id(void)
+{
+ if (!name_trn_id) {
+ name_trn_id = ((unsigned)time(NULL)%(unsigned)0x7FFF) + ((unsigned)sys_getpid()%(unsigned)100);
+ }
+ name_trn_id = (name_trn_id+1) % (unsigned)0x7FFF;
+ return name_trn_id;
+}
+
+/***************************************************************************
+ Either loops back or sends out a completed NetBIOS packet.
+**************************************************************************/
+
+static BOOL send_netbios_packet(struct packet_struct *p)
+{
+ BOOL loopback_this_packet = False;
+
+ /* Check if we are sending to or from ourselves as a WINS server. */
+ if(ismyip(p->ip) && (p->port == global_nmb_port))
+ loopback_this_packet = True;
+
+ if(loopback_this_packet) {
+ struct packet_struct *lo_packet = NULL;
+ DEBUG(5,("send_netbios_packet: sending packet to ourselves.\n"));
+ if((lo_packet = copy_packet(p)) == NULL)
+ return False;
+ queue_packet(lo_packet);
+ } else if (!send_packet(p)) {
+ DEBUG(0,("send_netbios_packet: send_packet() to IP %s port %d failed\n",
+ inet_ntoa(p->ip),p->port));
+ return False;
+ }
+
+ return True;
+}
+
+/***************************************************************************
+ Sets up the common elements of an outgoing NetBIOS packet.
+
+ Note: do not attempt to rationalise whether rec_des should be set or not
+ in a particular situation. Just follow rfc_1002 or look at examples from WinXX.
+ It does NOT follow the rule that requests to the wins server always have
+ rec_des true. See for example name releases and refreshes
+**************************************************************************/
+
+static struct packet_struct *create_and_init_netbios_packet(struct nmb_name *nmbname,
+ BOOL bcast, BOOL rec_des,
+ struct in_addr to_ip)
+{
+ struct packet_struct *packet = NULL;
+ struct nmb_packet *nmb = NULL;
+
+ /* Allocate the packet_struct we will return. */
+ if((packet = (struct packet_struct *)malloc(sizeof(*packet))) == NULL) {
+ DEBUG(0,("create_and_init_netbios_packet: malloc fail (1) for packet struct.\n"));
+ return NULL;
+ }
+
+ memset((char *)packet,'\0',sizeof(*packet));
+
+ nmb = &packet->packet.nmb;
+
+ nmb->header.name_trn_id = generate_name_trn_id();
+ nmb->header.response = False;
+ nmb->header.nm_flags.recursion_desired = rec_des;
+ nmb->header.nm_flags.recursion_available = False;
+ nmb->header.nm_flags.trunc = False;
+ nmb->header.nm_flags.authoritative = False;
+ nmb->header.nm_flags.bcast = bcast;
+
+ nmb->header.rcode = 0;
+ nmb->header.qdcount = 1;
+ nmb->header.ancount = 0;
+ nmb->header.nscount = 0;
+
+ nmb->question.question_name = *nmbname;
+ nmb->question.question_type = QUESTION_TYPE_NB_QUERY;
+ nmb->question.question_class = QUESTION_CLASS_IN;
+
+ packet->ip = to_ip;
+ packet->port = NMB_PORT;
+ packet->fd = ClientNMB;
+ packet->timestamp = time(NULL);
+ packet->packet_type = NMB_PACKET;
+ packet->locked = False;
+
+ return packet; /* Caller must free. */
+}
+
+/***************************************************************************
+ Sets up the common elements of register, refresh or release packet.
+**************************************************************************/
+
+static BOOL create_and_init_additional_record(struct packet_struct *packet,
+ uint16 nb_flags,
+ struct in_addr *register_ip)
+{
+ struct nmb_packet *nmb = &packet->packet.nmb;
+
+ if((nmb->additional = (struct res_rec *)malloc(sizeof(struct res_rec))) == NULL) {
+ DEBUG(0,("initiate_name_register_packet: malloc fail for additional record.\n"));
+ return False;
+ }
+
+ memset((char *)nmb->additional,'\0',sizeof(struct res_rec));
+
+ nmb->additional->rr_name = nmb->question.question_name;
+ nmb->additional->rr_type = RR_TYPE_NB;
+ nmb->additional->rr_class = RR_CLASS_IN;
+
+ /* See RFC 1002, sections 5.1.1.1, 5.1.1.2 and 5.1.1.3 */
+ if (nmb->header.nm_flags.bcast)
+ nmb->additional->ttl = PERMANENT_TTL;
+ else
+ nmb->additional->ttl = lp_max_ttl();
+
+ nmb->additional->rdlength = 6;
+
+ set_nb_flags(nmb->additional->rdata,nb_flags);
+
+ /* Set the address for the name we are registering. */
+ putip(&nmb->additional->rdata[2], register_ip);
+
+ /*
+ it turns out that Jeremys code was correct, we are supposed
+ to send registrations from the IP we are registering. The
+ trick is what to do on timeouts! When we send on a
+ non-routable IP then the reply will timeout, and we should
+ treat this as success, not failure. That means we go into
+ our standard refresh cycle for that name which copes nicely
+ with disconnected networks.
+ */
+ packet->fd = find_subnet_fd_for_address(*register_ip);
+
+ return True;
+}
+
+/***************************************************************************
+ Sends out a name query.
+**************************************************************************/
+
+static BOOL initiate_name_query_packet( struct packet_struct *packet)
+{
+ struct nmb_packet *nmb = NULL;
+
+ nmb = &packet->packet.nmb;
+
+ nmb->header.opcode = NMB_NAME_QUERY_OPCODE;
+ nmb->header.arcount = 0;
+
+ nmb->header.nm_flags.recursion_desired = True;
+
+ DEBUG(4,("initiate_name_query_packet: sending query for name %s (bcast=%s) to IP %s\n",
+ nmb_namestr(&nmb->question.question_name),
+ BOOLSTR(nmb->header.nm_flags.bcast), inet_ntoa(packet->ip)));
+
+ return send_netbios_packet( packet );
+}
+
+/***************************************************************************
+ Sends out a name query - from a WINS server.
+**************************************************************************/
+
+static BOOL initiate_name_query_packet_from_wins_server( struct packet_struct *packet)
+{
+ struct nmb_packet *nmb = NULL;
+
+ nmb = &packet->packet.nmb;
+
+ nmb->header.opcode = NMB_NAME_QUERY_OPCODE;
+ nmb->header.arcount = 0;
+
+ nmb->header.nm_flags.recursion_desired = False;
+
+ DEBUG(4,("initiate_name_query_packet_from_wins_server: sending query for name %s (bcast=%s) to IP %s\n",
+ nmb_namestr(&nmb->question.question_name),
+ BOOLSTR(nmb->header.nm_flags.bcast), inet_ntoa(packet->ip)));
+
+ return send_netbios_packet( packet );
+}
+
+/***************************************************************************
+ Sends out a name register.
+**************************************************************************/
+
+static BOOL initiate_name_register_packet( struct packet_struct *packet,
+ uint16 nb_flags, struct in_addr *register_ip)
+{
+ struct nmb_packet *nmb = &packet->packet.nmb;
+
+ nmb->header.opcode = NMB_NAME_REG_OPCODE;
+ nmb->header.arcount = 1;
+
+ nmb->header.nm_flags.recursion_desired = True;
+
+ if(create_and_init_additional_record(packet, nb_flags, register_ip) == False)
+ return False;
+
+ DEBUG(4,("initiate_name_register_packet: sending registration for name %s (bcast=%s) to IP %s\n",
+ nmb_namestr(&nmb->additional->rr_name),
+ BOOLSTR(nmb->header.nm_flags.bcast), inet_ntoa(packet->ip)));
+
+ return send_netbios_packet( packet );
+}
+
+/***************************************************************************
+ Sends out a multihomed name register.
+**************************************************************************/
+
+static BOOL initiate_multihomed_name_register_packet(struct packet_struct *packet,
+ uint16 nb_flags, struct in_addr *register_ip)
+{
+ struct nmb_packet *nmb = &packet->packet.nmb;
+ fstring second_ip_buf;
+
+ fstrcpy(second_ip_buf, inet_ntoa(packet->ip));
+
+ nmb->header.opcode = NMB_NAME_MULTIHOMED_REG_OPCODE;
+ nmb->header.arcount = 1;
+
+ nmb->header.nm_flags.recursion_desired = True;
+
+ if(create_and_init_additional_record(packet, nb_flags, register_ip) == False)
+ return False;
+
+ DEBUG(4,("initiate_multihomed_name_register_packet: sending registration \
+for name %s IP %s (bcast=%s) to IP %s\n",
+ nmb_namestr(&nmb->additional->rr_name), inet_ntoa(*register_ip),
+ BOOLSTR(nmb->header.nm_flags.bcast), second_ip_buf ));
+
+ return send_netbios_packet( packet );
+}
+
+/***************************************************************************
+ Sends out a name refresh.
+**************************************************************************/
+
+static BOOL initiate_name_refresh_packet( struct packet_struct *packet,
+ uint16 nb_flags, struct in_addr *refresh_ip)
+{
+ struct nmb_packet *nmb = &packet->packet.nmb;
+
+ nmb->header.opcode = NMB_NAME_REFRESH_OPCODE_8;
+ nmb->header.arcount = 1;
+
+ nmb->header.nm_flags.recursion_desired = False;
+
+ if(create_and_init_additional_record(packet, nb_flags, refresh_ip) == False)
+ return False;
+
+ DEBUG(4,("initiate_name_refresh_packet: sending refresh for name %s (bcast=%s) to IP %s\n",
+ nmb_namestr(&nmb->additional->rr_name),
+ BOOLSTR(nmb->header.nm_flags.bcast), inet_ntoa(packet->ip)));
+
+ return send_netbios_packet( packet );
+}
+
+/***************************************************************************
+ Sends out a name release.
+**************************************************************************/
+
+static BOOL initiate_name_release_packet( struct packet_struct *packet,
+ uint16 nb_flags, struct in_addr *release_ip)
+{
+ struct nmb_packet *nmb = &packet->packet.nmb;
+
+ nmb->header.opcode = NMB_NAME_RELEASE_OPCODE;
+ nmb->header.arcount = 1;
+
+ nmb->header.nm_flags.recursion_desired = False;
+
+ if(create_and_init_additional_record(packet, nb_flags, release_ip) == False)
+ return False;
+
+ DEBUG(4,("initiate_name_release_packet: sending release for name %s (bcast=%s) to IP %s\n",
+ nmb_namestr(&nmb->additional->rr_name),
+ BOOLSTR(nmb->header.nm_flags.bcast), inet_ntoa(packet->ip)));
+
+ return send_netbios_packet( packet );
+}
+
+/***************************************************************************
+ Sends out a node status.
+**************************************************************************/
+
+static BOOL initiate_node_status_packet( struct packet_struct *packet )
+{
+ struct nmb_packet *nmb = &packet->packet.nmb;
+
+ nmb->header.opcode = NMB_NAME_QUERY_OPCODE;
+ nmb->header.arcount = 0;
+
+ nmb->header.nm_flags.recursion_desired = False;
+
+ nmb->question.question_type = QUESTION_TYPE_NB_STATUS;
+
+ DEBUG(4,("initiate_node_status_packet: sending node status request for name %s to IP %s\n",
+ nmb_namestr(&nmb->question.question_name),
+ inet_ntoa(packet->ip)));
+
+ return send_netbios_packet( packet );
+}
+
+/****************************************************************************
+ Simplification functions for queuing standard packets.
+ These should be the only publicly callable functions for sending
+ out packets.
+****************************************************************************/
+
+/****************************************************************************
+ Assertion - we should never be sending nmbd packets on the remote
+ broadcast subnet.
+****************************************************************************/
+
+static BOOL assert_check_subnet(struct subnet_record *subrec)
+{
+ if( subrec == remote_broadcast_subnet) {
+ DEBUG(0,("assert_check_subnet: Attempt to send packet on remote broadcast subnet. \
+This is a bug.\n"));
+ return True;
+ }
+ return False;
+}
+
+/****************************************************************************
+ Queue a register name packet to the broadcast address of a subnet.
+****************************************************************************/
+
+struct response_record *queue_register_name( struct subnet_record *subrec,
+ response_function resp_fn,
+ timeout_response_function timeout_fn,
+ register_name_success_function success_fn,
+ register_name_fail_function fail_fn,
+ struct userdata_struct *userdata,
+ struct nmb_name *nmbname,
+ uint16 nb_flags)
+{
+ struct packet_struct *p;
+ struct response_record *rrec;
+
+ if(assert_check_subnet(subrec))
+ return NULL;
+
+ /* note that all name registration requests have RD set (rfc1002 - section 4.2.2 */
+ if ((p = create_and_init_netbios_packet(nmbname, (subrec != unicast_subnet), True,
+ subrec->bcast_ip)) == NULL)
+ return NULL;
+
+ if(initiate_name_register_packet( p, nb_flags, iface_ip(subrec->bcast_ip)) == False) {
+ p->locked = False;
+ free_packet(p);
+ return NULL;
+ }
+
+ if((rrec = make_response_record(subrec, /* subnet record. */
+ p, /* packet we sent. */
+ resp_fn, /* function to call on response. */
+ timeout_fn, /* function to call on timeout. */
+ (success_function)success_fn, /* function to call on operation success. */
+ (fail_function)fail_fn, /* function to call on operation fail. */
+ userdata)) == NULL) {
+ p->locked = False;
+ free_packet(p);
+ return NULL;
+ }
+
+ return rrec;
+}
+
+/****************************************************************************
+ Queue a refresh name packet to the broadcast address of a subnet.
+****************************************************************************/
+
+void queue_wins_refresh(struct nmb_name *nmbname,
+ response_function resp_fn,
+ timeout_response_function timeout_fn,
+ uint16 nb_flags,
+ struct in_addr refresh_ip,
+ const char *tag)
+{
+ struct packet_struct *p;
+ struct response_record *rrec;
+ struct in_addr wins_ip;
+ struct userdata_struct *userdata;
+ fstring ip_str;
+
+ wins_ip = wins_srv_ip_tag(tag, refresh_ip);
+
+ if ((p = create_and_init_netbios_packet(nmbname, False, False, wins_ip)) == NULL) {
+ return;
+ }
+
+ if (!initiate_name_refresh_packet(p, nb_flags, &refresh_ip)) {
+ p->locked = False;
+ free_packet(p);
+ return;
+ }
+
+ fstrcpy(ip_str, inet_ntoa(refresh_ip));
+
+ DEBUG(6,("Refreshing name %s IP %s with WINS server %s using tag '%s'\n",
+ nmb_namestr(nmbname), ip_str, inet_ntoa(wins_ip), tag));
+
+ userdata = (struct userdata_struct *)malloc(sizeof(*userdata) + strlen(tag) + 1);
+ if (!userdata) {
+ DEBUG(0,("Failed to allocate userdata structure!\n"));
+ return;
+ }
+ ZERO_STRUCTP(userdata);
+ userdata->userdata_len = strlen(tag) + 1;
+ strlcpy(userdata->data, tag, userdata->userdata_len);
+
+ if ((rrec = make_response_record(unicast_subnet,
+ p,
+ resp_fn, timeout_fn,
+ NULL,
+ NULL,
+ userdata)) == NULL) {
+ p->locked = False;
+ free_packet(p);
+ return;
+ }
+
+ free(userdata);
+
+ /* we don't want to repeat refresh packets */
+ rrec->repeat_count = 0;
+}
+
+
+/****************************************************************************
+ Queue a multihomed register name packet to a given WINS server IP
+****************************************************************************/
+
+struct response_record *queue_register_multihomed_name( struct subnet_record *subrec,
+ response_function resp_fn,
+ timeout_response_function timeout_fn,
+ register_name_success_function success_fn,
+ register_name_fail_function fail_fn,
+ struct userdata_struct *userdata,
+ struct nmb_name *nmbname,
+ uint16 nb_flags,
+ struct in_addr register_ip,
+ struct in_addr wins_ip)
+{
+ struct packet_struct *p;
+ struct response_record *rrec;
+ BOOL ret;
+
+ /* Sanity check. */
+ if(subrec != unicast_subnet) {
+ DEBUG(0,("queue_register_multihomed_name: should only be done on \
+unicast subnet. subnet is %s\n.", subrec->subnet_name ));
+ return NULL;
+ }
+
+ if(assert_check_subnet(subrec))
+ return NULL;
+
+ if ((p = create_and_init_netbios_packet(nmbname, False, True, wins_ip)) == NULL)
+ return NULL;
+
+ if (nb_flags & NB_GROUP)
+ ret = initiate_name_register_packet( p, nb_flags, &register_ip);
+ else
+ ret = initiate_multihomed_name_register_packet(p, nb_flags, &register_ip);
+
+ if (ret == False) {
+ p->locked = False;
+ free_packet(p);
+ return NULL;
+ }
+
+ if ((rrec = make_response_record(subrec, /* subnet record. */
+ p, /* packet we sent. */
+ resp_fn, /* function to call on response. */
+ timeout_fn, /* function to call on timeout. */
+ (success_function)success_fn, /* function to call on operation success. */
+ (fail_function)fail_fn, /* function to call on operation fail. */
+ userdata)) == NULL) {
+ p->locked = False;
+ free_packet(p);
+ return NULL;
+ }
+
+ return rrec;
+}
+
+/****************************************************************************
+ Queue a release name packet to the broadcast address of a subnet.
+****************************************************************************/
+
+struct response_record *queue_release_name( struct subnet_record *subrec,
+ response_function resp_fn,
+ timeout_response_function timeout_fn,
+ release_name_success_function success_fn,
+ release_name_fail_function fail_fn,
+ struct userdata_struct *userdata,
+ struct nmb_name *nmbname,
+ uint16 nb_flags,
+ struct in_addr release_ip,
+ struct in_addr dest_ip)
+{
+ struct packet_struct *p;
+ struct response_record *rrec;
+
+ if(assert_check_subnet(subrec))
+ return NULL;
+
+ if ((p = create_and_init_netbios_packet(nmbname, (subrec != unicast_subnet), False, dest_ip)) == NULL)
+ return NULL;
+
+ if(initiate_name_release_packet( p, nb_flags, &release_ip) == False) {
+ p->locked = False;
+ free_packet(p);
+ return NULL;
+ }
+
+ if((rrec = make_response_record(subrec, /* subnet record. */
+ p, /* packet we sent. */
+ resp_fn, /* function to call on response. */
+ timeout_fn, /* function to call on timeout. */
+ (success_function)success_fn, /* function to call on operation success. */
+ (fail_function)fail_fn, /* function to call on operation fail. */
+ userdata)) == NULL) {
+ p->locked = False;
+ free_packet(p);
+ return NULL;
+ }
+
+ /*
+ * For a broadcast release packet, only send once.
+ * This will cause us to remove the name asap. JRA.
+ */
+
+ if (subrec != unicast_subnet) {
+ rrec->repeat_count = 0;
+ rrec->repeat_time = 0;
+ }
+
+ return rrec;
+}
+
+/****************************************************************************
+ Queue a query name packet to the broadcast address of a subnet.
+****************************************************************************/
+
+struct response_record *queue_query_name( struct subnet_record *subrec,
+ response_function resp_fn,
+ timeout_response_function timeout_fn,
+ query_name_success_function success_fn,
+ query_name_fail_function fail_fn,
+ struct userdata_struct *userdata,
+ struct nmb_name *nmbname)
+{
+ struct packet_struct *p;
+ struct response_record *rrec;
+ struct in_addr to_ip;
+
+ if(assert_check_subnet(subrec))
+ return NULL;
+
+ to_ip = subrec->bcast_ip;
+
+ /* queries to the WINS server turn up here as queries to IP 0.0.0.0
+ These need to be handled a bit differently */
+ if (subrec->type == UNICAST_SUBNET && is_zero_ip(to_ip)) {
+ /* What we really need to do is loop over each of our wins
+ * servers and wins server tags here, but that just doesn't
+ * fit our architecture at the moment (userdata may already
+ * be used when we get here). For now we just query the first
+ * active wins server on the first tag.
+ */
+ char **tags = wins_srv_tags();
+ if (!tags) {
+ return NULL;
+ }
+ to_ip = wins_srv_ip_tag(tags[0], to_ip);
+ wins_srv_tags_free(tags);
+ }
+
+ if(( p = create_and_init_netbios_packet(nmbname,
+ (subrec != unicast_subnet),
+ (subrec == unicast_subnet),
+ to_ip)) == NULL)
+ return NULL;
+
+ if(lp_bind_interfaces_only()) {
+ int i;
+
+ DEBUG(10,("queue_query_name: bind_interfaces_only is set, looking for suitable source IP\n"));
+ for(i = 0; i < iface_count(); i++) {
+ struct in_addr *ifip = iface_n_ip(i);
+
+ if(ifip == NULL) {
+ DEBUG(0,("queue_query_name: interface %d has NULL IP address !\n", i));
+ continue;
+ }
+
+ if (ip_equal(*ifip,loopback_ip)) {
+ DEBUG(5,("queue_query_name: ignoring loopback interface (%d)\n", i));
+ continue;
+ }
+
+ DEBUG(10,("queue_query_name: using source IP %s\n",inet_ntoa(*ifip)));
+ p->fd = find_subnet_fd_for_address( *ifip );
+ break;
+ }
+ }
+
+ if(initiate_name_query_packet( p ) == False) {
+ p->locked = False;
+ free_packet(p);
+ return NULL;
+ }
+
+ if((rrec = make_response_record(subrec, /* subnet record. */
+ p, /* packet we sent. */
+ resp_fn, /* function to call on response. */
+ timeout_fn, /* function to call on timeout. */
+ (success_function)success_fn, /* function to call on operation success. */
+ (fail_function)fail_fn, /* function to call on operation fail. */
+ userdata)) == NULL) {
+ p->locked = False;
+ free_packet(p);
+ return NULL;
+ }
+
+ return rrec;
+}
+
+/****************************************************************************
+ Queue a query name packet to a given address from the WINS subnet.
+****************************************************************************/
+
+struct response_record *queue_query_name_from_wins_server( struct in_addr to_ip,
+ response_function resp_fn,
+ timeout_response_function timeout_fn,
+ query_name_success_function success_fn,
+ query_name_fail_function fail_fn,
+ struct userdata_struct *userdata,
+ struct nmb_name *nmbname)
+{
+ struct packet_struct *p;
+ struct response_record *rrec;
+
+ if ((p = create_and_init_netbios_packet(nmbname, False, False, to_ip)) == NULL)
+ return NULL;
+
+ if(initiate_name_query_packet_from_wins_server( p ) == False) {
+ p->locked = False;
+ free_packet(p);
+ return NULL;
+ }
+
+ if((rrec = make_response_record(wins_server_subnet, /* subnet record. */
+ p, /* packet we sent. */
+ resp_fn, /* function to call on response. */
+ timeout_fn, /* function to call on timeout. */
+ (success_function)success_fn, /* function to call on operation success. */
+ (fail_function)fail_fn, /* function to call on operation fail. */
+ userdata)) == NULL) {
+ p->locked = False;
+ free_packet(p);
+ return NULL;
+ }
+
+ return rrec;
+}
+
+/****************************************************************************
+ Queue a node status packet to a given name and address.
+****************************************************************************/
+
+struct response_record *queue_node_status( struct subnet_record *subrec,
+ response_function resp_fn,
+ timeout_response_function timeout_fn,
+ node_status_success_function success_fn,
+ node_status_fail_function fail_fn,
+ struct userdata_struct *userdata,
+ struct nmb_name *nmbname,
+ struct in_addr send_ip)
+{
+ struct packet_struct *p;
+ struct response_record *rrec;
+
+ /* Sanity check. */
+ if(subrec != unicast_subnet) {
+ DEBUG(0,("queue_register_multihomed_name: should only be done on \
+unicast subnet. subnet is %s\n.", subrec->subnet_name ));
+ return NULL;
+ }
+
+ if(assert_check_subnet(subrec))
+ return NULL;
+
+ if(( p = create_and_init_netbios_packet(nmbname, False, False, send_ip)) == NULL)
+ return NULL;
+
+ if(initiate_node_status_packet(p) == False) {
+ p->locked = False;
+ free_packet(p);
+ return NULL;
+ }
+
+ if((rrec = make_response_record(subrec, /* subnet record. */
+ p, /* packet we sent. */
+ resp_fn, /* function to call on response. */
+ timeout_fn, /* function to call on timeout. */
+ (success_function)success_fn, /* function to call on operation success. */
+ (fail_function)fail_fn, /* function to call on operation fail. */
+ userdata)) == NULL) {
+ p->locked = False;
+ free_packet(p);
+ return NULL;
+ }
+
+ return rrec;
+}
+
+/****************************************************************************
+ Reply to a netbios name packet. see rfc1002.txt
+****************************************************************************/
+
+void reply_netbios_packet(struct packet_struct *orig_packet,
+ int rcode, enum netbios_reply_type_code rcv_code, int opcode,
+ int ttl, char *data,int len)
+{
+ struct packet_struct packet;
+ struct nmb_packet *nmb = NULL;
+ struct res_rec answers;
+ struct nmb_packet *orig_nmb = &orig_packet->packet.nmb;
+ BOOL loopback_this_packet = False;
+ const char *packet_type = "unknown";
+
+ /* Check if we are sending to or from ourselves. */
+ if(ismyip(orig_packet->ip) && (orig_packet->port == global_nmb_port))
+ loopback_this_packet = True;
+
+ nmb = &packet.packet.nmb;
+
+ /* Do a partial copy of the packet. We clear the locked flag and
+ the resource record pointers. */
+ packet = *orig_packet; /* Full structure copy. */
+ packet.locked = False;
+ nmb->answers = NULL;
+ nmb->nsrecs = NULL;
+ nmb->additional = NULL;
+
+ switch (rcv_code) {
+ case NMB_STATUS:
+ packet_type = "nmb_status";
+ nmb->header.nm_flags.recursion_desired = False;
+ nmb->header.nm_flags.recursion_available = False;
+ break;
+ case NMB_QUERY:
+ packet_type = "nmb_query";
+ nmb->header.nm_flags.recursion_desired = True;
+ nmb->header.nm_flags.recursion_available = True;
+ break;
+ case NMB_REG:
+ case NMB_REG_REFRESH:
+ packet_type = "nmb_reg";
+ nmb->header.nm_flags.recursion_desired = True;
+ nmb->header.nm_flags.recursion_available = True;
+ break;
+ case NMB_REL:
+ packet_type = "nmb_rel";
+ nmb->header.nm_flags.recursion_desired = False;
+ nmb->header.nm_flags.recursion_available = False;
+ break;
+ case NMB_WAIT_ACK:
+ packet_type = "nmb_wack";
+ nmb->header.nm_flags.recursion_desired = False;
+ nmb->header.nm_flags.recursion_available = False;
+ break;
+ case WINS_REG:
+ packet_type = "wins_reg";
+ nmb->header.nm_flags.recursion_desired = True;
+ nmb->header.nm_flags.recursion_available = True;
+ break;
+ case WINS_QUERY:
+ packet_type = "wins_query";
+ nmb->header.nm_flags.recursion_desired = True;
+ nmb->header.nm_flags.recursion_available = True;
+ break;
+ default:
+ DEBUG(0,("reply_netbios_packet: Unknown packet type: %s %s to ip %s\n",
+ packet_type, nmb_namestr(&orig_nmb->question.question_name),
+ inet_ntoa(packet.ip)));
+ return;
+ }
+
+ DEBUG(4,("reply_netbios_packet: sending a reply of packet type: %s %s to ip %s \
+for id %hu\n", packet_type, nmb_namestr(&orig_nmb->question.question_name),
+ inet_ntoa(packet.ip), orig_nmb->header.name_trn_id));
+
+ nmb->header.name_trn_id = orig_nmb->header.name_trn_id;
+ nmb->header.opcode = opcode;
+ nmb->header.response = True;
+ nmb->header.nm_flags.bcast = False;
+ nmb->header.nm_flags.trunc = False;
+ nmb->header.nm_flags.authoritative = True;
+
+ nmb->header.rcode = rcode;
+ nmb->header.qdcount = 0;
+ nmb->header.ancount = 1;
+ nmb->header.nscount = 0;
+ nmb->header.arcount = 0;
+
+ memset((char*)&nmb->question,'\0',sizeof(nmb->question));
+
+ nmb->answers = &answers;
+ memset((char*)nmb->answers,'\0',sizeof(*nmb->answers));
+
+ nmb->answers->rr_name = orig_nmb->question.question_name;
+ nmb->answers->rr_type = orig_nmb->question.question_type;
+ nmb->answers->rr_class = orig_nmb->question.question_class;
+ nmb->answers->ttl = ttl;
+
+ if (data && len) {
+ nmb->answers->rdlength = len;
+ memcpy(nmb->answers->rdata, data, len);
+ }
+
+ packet.packet_type = NMB_PACKET;
+ /* Ensure we send out on the same fd that the original
+ packet came in on to give the correct source IP address. */
+ packet.fd = orig_packet->fd;
+ packet.timestamp = time(NULL);
+
+ debug_nmb_packet(&packet);
+
+ if(loopback_this_packet) {
+ struct packet_struct *lo_packet;
+ DEBUG(5,("reply_netbios_packet: sending packet to ourselves.\n"));
+ if((lo_packet = copy_packet(&packet)) == NULL)
+ return;
+ queue_packet(lo_packet);
+ } else if (!send_packet(&packet)) {
+ DEBUG(0,("reply_netbios_packet: send_packet to IP %s port %d failed\n",
+ inet_ntoa(packet.ip),packet.port));
+ }
+}
+
+/*******************************************************************
+ Queue a packet into a packet queue
+******************************************************************/
+
+static void queue_packet(struct packet_struct *packet)
+{
+ struct packet_struct *p;
+
+ if (!packet_queue) {
+ packet->prev = NULL;
+ packet->next = NULL;
+ packet_queue = packet;
+ return;
+ }
+
+ /* find the bottom */
+ for (p=packet_queue;p->next;p=p->next)
+ ;
+
+ p->next = packet;
+ packet->next = NULL;
+ packet->prev = p;
+}
+
+/****************************************************************************
+ Try and find a matching subnet record for a datagram port 138 packet.
+****************************************************************************/
+
+static struct subnet_record *find_subnet_for_dgram_browse_packet(struct packet_struct *p)
+{
+ struct subnet_record *subrec;
+
+ /* Go through all the broadcast subnets and see if the mask matches. */
+ for (subrec = FIRST_SUBNET; subrec ; subrec = NEXT_SUBNET_EXCLUDING_UNICAST(subrec)) {
+ if(same_net(p->ip, subrec->bcast_ip, subrec->mask_ip))
+ return subrec;
+ }
+
+ /* If the subnet record is the remote announce broadcast subnet,
+ hack it here to be the first subnet. This is really gross and
+ is needed due to people turning on port 137/138 broadcast
+ forwarding on their routers. May fire and brimstone rain
+ down upon them...
+ */
+
+ return FIRST_SUBNET;
+}
+
+/****************************************************************************
+Dispatch a browse frame from port 138 to the correct processing function.
+****************************************************************************/
+
+static void process_browse_packet(struct packet_struct *p, char *buf,int len)
+{
+ struct dgram_packet *dgram = &p->packet.dgram;
+ int command = CVAL(buf,0);
+ struct subnet_record *subrec = find_subnet_for_dgram_browse_packet(p);
+ char scope[64];
+ unstring src_name;
+
+ /* Drop the packet if it's a different NetBIOS scope, or the source is from one of our names. */
+ pull_ascii(scope, dgram->dest_name.scope, 64, 64, STR_TERMINATE);
+ if (!strequal(scope, global_scope())) {
+ DEBUG(7,("process_browse_packet: Discarding datagram from IP %s. Scope (%s) \
+mismatch with our scope (%s).\n", inet_ntoa(p->ip), scope, global_scope()));
+ return;
+ }
+
+ pull_ascii_nstring(src_name, sizeof(src_name), dgram->source_name.name);
+ if (is_myname(src_name)) {
+ DEBUG(0,("process_browse_packet: Discarding datagram from IP %s. Source name \
+%s is one of our names !\n", inet_ntoa(p->ip), nmb_namestr(&dgram->source_name)));
+ return;
+ }
+
+ switch (command) {
+ case ANN_HostAnnouncement:
+ debug_browse_data(buf, len);
+ process_host_announce(subrec, p, buf+1);
+ break;
+ case ANN_DomainAnnouncement:
+ debug_browse_data(buf, len);
+ process_workgroup_announce(subrec, p, buf+1);
+ break;
+ case ANN_LocalMasterAnnouncement:
+ debug_browse_data(buf, len);
+ process_local_master_announce(subrec, p, buf+1);
+ break;
+ case ANN_AnnouncementRequest:
+ debug_browse_data(buf, len);
+ process_announce_request(subrec, p, buf+1);
+ break;
+ case ANN_Election:
+ debug_browse_data(buf, len);
+ process_election(subrec, p, buf+1);
+ break;
+ case ANN_GetBackupListReq:
+ debug_browse_data(buf, len);
+ process_get_backup_list_request(subrec, p, buf+1);
+ break;
+ case ANN_GetBackupListResp:
+ debug_browse_data(buf, len);
+ /* We never send ANN_GetBackupListReq so we should never get these. */
+ DEBUG(0,("process_browse_packet: Discarding GetBackupListResponse \
+packet from %s IP %s\n", nmb_namestr(&dgram->source_name), inet_ntoa(p->ip)));
+ break;
+ case ANN_ResetBrowserState:
+ debug_browse_data(buf, len);
+ process_reset_browser(subrec, p, buf+1);
+ break;
+ case ANN_MasterAnnouncement:
+ /* Master browser datagrams must be processed on the unicast subnet. */
+ subrec = unicast_subnet;
+
+ debug_browse_data(buf, len);
+ process_master_browser_announce(subrec, p, buf+1);
+ break;
+ case ANN_BecomeBackup:
+ /*
+ * We don't currently implement this. Log it just in case.
+ */
+ debug_browse_data(buf, len);
+ DEBUG(10,("process_browse_packet: On subnet %s ignoring browse packet \
+command ANN_BecomeBackup from %s IP %s to %s\n", subrec->subnet_name, nmb_namestr(&dgram->source_name),
+ inet_ntoa(p->ip), nmb_namestr(&dgram->dest_name)));
+ break;
+ default:
+ debug_browse_data(buf, len);
+ DEBUG(0,("process_browse_packet: On subnet %s ignoring browse packet \
+command code %d from %s IP %s to %s\n", subrec->subnet_name, command, nmb_namestr(&dgram->source_name),
+ inet_ntoa(p->ip), nmb_namestr(&dgram->dest_name)));
+ break;
+ }
+}
+
+/****************************************************************************
+ Dispatch a LanMan browse frame from port 138 to the correct processing function.
+****************************************************************************/
+
+static void process_lanman_packet(struct packet_struct *p, char *buf,int len)
+{
+ struct dgram_packet *dgram = &p->packet.dgram;
+ int command = SVAL(buf,0);
+ struct subnet_record *subrec = find_subnet_for_dgram_browse_packet(p);
+ char scope[64];
+ unstring src_name;
+
+ /* Drop the packet if it's a different NetBIOS scope, or the source is from one of our names. */
+
+ pull_ascii(scope, dgram->dest_name.scope, 64, 64, STR_TERMINATE);
+ if (!strequal(scope, global_scope())) {
+ DEBUG(7,("process_lanman_packet: Discarding datagram from IP %s. Scope (%s) \
+mismatch with our scope (%s).\n", inet_ntoa(p->ip), scope, global_scope()));
+ return;
+ }
+
+ pull_ascii_nstring(src_name, sizeof(src_name), dgram->source_name.name);
+ if (is_myname(src_name)) {
+ DEBUG(0,("process_lanman_packet: Discarding datagram from IP %s. Source name \
+%s is one of our names !\n", inet_ntoa(p->ip), nmb_namestr(&dgram->source_name)));
+ return;
+ }
+
+ switch (command) {
+ case ANN_HostAnnouncement:
+ debug_browse_data(buf, len);
+ process_lm_host_announce(subrec, p, buf+1);
+ break;
+ case ANN_AnnouncementRequest:
+ process_lm_announce_request(subrec, p, buf+1);
+ break;
+ default:
+ DEBUG(0,("process_lanman_packet: On subnet %s ignoring browse packet \
+command code %d from %s IP %s to %s\n", subrec->subnet_name, command, nmb_namestr(&dgram->source_name),
+ inet_ntoa(p->ip), nmb_namestr(&dgram->dest_name)));
+ break;
+ }
+}
+
+/****************************************************************************
+ Determine if a packet is for us on port 138. Note that to have any chance of
+ being efficient we need to drop as many packets as possible at this
+ stage as subsequent processing is expensive.
+****************************************************************************/
+
+static BOOL listening(struct packet_struct *p,struct nmb_name *nbname)
+{
+ struct subnet_record *subrec = NULL;
+
+ for (subrec = FIRST_SUBNET; subrec ; subrec = NEXT_SUBNET_EXCLUDING_UNICAST(subrec)) {
+ if(same_net(p->ip, subrec->bcast_ip, subrec->mask_ip))
+ break;
+ }
+
+ if(subrec == NULL)
+ subrec = unicast_subnet;
+
+ return (find_name_on_subnet(subrec, nbname, FIND_SELF_NAME) != NULL);
+}
+
+/****************************************************************************
+ Process udp 138 datagrams
+****************************************************************************/
+
+static void process_dgram(struct packet_struct *p)
+{
+ char *buf;
+ char *buf2;
+ int len;
+ struct dgram_packet *dgram = &p->packet.dgram;
+
+ /* If we aren't listening to the destination name then ignore the packet */
+ if (!listening(p,&dgram->dest_name)) {
+ unexpected_packet(p);
+ DEBUG(5,("process_dgram: ignoring dgram packet sent to name %s from %s\n",
+ nmb_namestr(&dgram->dest_name), inet_ntoa(p->ip)));
+ return;
+ }
+
+ if (dgram->header.msg_type != 0x10 && dgram->header.msg_type != 0x11 && dgram->header.msg_type != 0x12) {
+ unexpected_packet(p);
+ /* Don't process error packets etc yet */
+ DEBUG(5,("process_dgram: ignoring dgram packet sent to name %s from IP %s as it is \
+an error packet of type %x\n", nmb_namestr(&dgram->dest_name), inet_ntoa(p->ip), dgram->header.msg_type));
+ return;
+ }
+
+ buf = &dgram->data[0];
+ buf -= 4; /* XXXX for the pseudo tcp length - someday I need to get rid of this */
+
+ if (CVAL(buf,smb_com) != SMBtrans)
+ return;
+
+ len = SVAL(buf,smb_vwv11);
+ buf2 = smb_base(buf) + SVAL(buf,smb_vwv12);
+
+ if (len <= 0)
+ return;
+
+ if (buf2 + len > buf + sizeof(dgram->data)) {
+ DEBUG(2,("process_dgram: datagram from %s to %s IP %s for %s len=%d too long.\n",
+ nmb_namestr(&dgram->source_name),nmb_namestr(&dgram->dest_name),
+ inet_ntoa(p->ip), smb_buf(buf),len));
+ len = (buf + sizeof(dgram->data)) - buf;
+ }
+
+ DEBUG(4,("process_dgram: datagram from %s to %s IP %s for %s of type %d len=%d\n",
+ nmb_namestr(&dgram->source_name),nmb_namestr(&dgram->dest_name),
+ inet_ntoa(p->ip), smb_buf(buf),CVAL(buf2,0),len));
+
+ /* Datagram packet received for the browser mailslot */
+ if (strequal(smb_buf(buf),BROWSE_MAILSLOT)) {
+ process_browse_packet(p,buf2,len);
+ return;
+ }
+
+ /* Datagram packet received for the LAN Manager mailslot */
+ if (strequal(smb_buf(buf),LANMAN_MAILSLOT)) {
+ process_lanman_packet(p,buf2,len);
+ return;
+ }
+
+ /* Datagram packet received for the domain logon mailslot */
+ if (strequal(smb_buf(buf),NET_LOGON_MAILSLOT)) {
+ process_logon_packet(p,buf2,len,NET_LOGON_MAILSLOT);
+ return;
+ }
+
+ /* Datagram packet received for the NT domain logon mailslot */
+ if (strequal(smb_buf(buf),NT_LOGON_MAILSLOT)) {
+ process_logon_packet(p,buf2,len,NT_LOGON_MAILSLOT);
+ return;
+ }
+
+ unexpected_packet(p);
+}
+
+/****************************************************************************
+ Validate a response nmb packet.
+****************************************************************************/
+
+static BOOL validate_nmb_response_packet( struct nmb_packet *nmb )
+{
+ BOOL ignore = False;
+
+ switch (nmb->header.opcode) {
+ case NMB_NAME_REG_OPCODE:
+ case NMB_NAME_REFRESH_OPCODE_8: /* ambiguity in rfc1002 about which is correct. */
+ case NMB_NAME_REFRESH_OPCODE_9: /* WinNT uses 8 by default. */
+ if (nmb->header.ancount == 0) {
+ DEBUG(0,("validate_nmb_response_packet: Bad REG/REFRESH Packet. "));
+ ignore = True;
+ }
+ break;
+
+ case NMB_NAME_QUERY_OPCODE:
+ if ((nmb->header.ancount != 0) && (nmb->header.ancount != 1)) {
+ DEBUG(0,("validate_nmb_response_packet: Bad QUERY Packet. "));
+ ignore = True;
+ }
+ break;
+
+ case NMB_NAME_RELEASE_OPCODE:
+ if (nmb->header.ancount == 0) {
+ DEBUG(0,("validate_nmb_response_packet: Bad RELEASE Packet. "));
+ ignore = True;
+ }
+ break;
+
+ case NMB_WACK_OPCODE:
+ /* Check WACK response here. */
+ if (nmb->header.ancount != 1) {
+ DEBUG(0,("validate_nmb_response_packet: Bad WACK Packet. "));
+ ignore = True;
+ }
+ break;
+ default:
+ DEBUG(0,("validate_nmb_response_packet: Ignoring packet with unknown opcode %d.\n",
+ nmb->header.opcode));
+ return True;
+ }
+
+ if(ignore)
+ DEBUG(0,("Ignoring response packet with opcode %d.\n", nmb->header.opcode));
+
+ return ignore;
+}
+
+/****************************************************************************
+ Validate a request nmb packet.
+****************************************************************************/
+
+static BOOL validate_nmb_packet( struct nmb_packet *nmb )
+{
+ BOOL ignore = False;
+
+ switch (nmb->header.opcode) {
+ case NMB_NAME_REG_OPCODE:
+ case NMB_NAME_REFRESH_OPCODE_8: /* ambiguity in rfc1002 about which is correct. */
+ case NMB_NAME_REFRESH_OPCODE_9: /* WinNT uses 8 by default. */
+ case NMB_NAME_MULTIHOMED_REG_OPCODE:
+ if (nmb->header.qdcount==0 || nmb->header.arcount==0) {
+ DEBUG(0,("validate_nmb_packet: Bad REG/REFRESH Packet. "));
+ ignore = True;
+ }
+ break;
+
+ case NMB_NAME_QUERY_OPCODE:
+ if ((nmb->header.qdcount == 0) || ((nmb->question.question_type != QUESTION_TYPE_NB_QUERY) &&
+ (nmb->question.question_type != QUESTION_TYPE_NB_STATUS))) {
+ DEBUG(0,("validate_nmb_packet: Bad QUERY Packet. "));
+ ignore = True;
+ }
+ break;
+
+ case NMB_NAME_RELEASE_OPCODE:
+ if (nmb->header.qdcount==0 || nmb->header.arcount==0) {
+ DEBUG(0,("validate_nmb_packet: Bad RELEASE Packet. "));
+ ignore = True;
+ }
+ break;
+ default:
+ DEBUG(0,("validate_nmb_packet: Ignoring packet with unknown opcode %d.\n",
+ nmb->header.opcode));
+ return True;
+ }
+
+ if(ignore)
+ DEBUG(0,("validate_nmb_packet: Ignoring request packet with opcode %d.\n", nmb->header.opcode));
+
+ return ignore;
+}
+
+/****************************************************************************
+ Find a subnet (and potentially a response record) for a packet.
+****************************************************************************/
+
+static struct subnet_record *find_subnet_for_nmb_packet( struct packet_struct *p,
+ struct response_record **pprrec)
+{
+ struct nmb_packet *nmb = &p->packet.nmb;
+ struct response_record *rrec = NULL;
+ struct subnet_record *subrec = NULL;
+
+ if(pprrec != NULL)
+ *pprrec = NULL;
+
+ if(nmb->header.response) {
+ /* It's a response packet. Find a record for it or it's an error. */
+
+ rrec = find_response_record( &subrec, nmb->header.name_trn_id);
+ if(rrec == NULL) {
+ DEBUG(3,("find_subnet_for_nmb_packet: response record not found for response id %hu\n",
+ nmb->header.name_trn_id));
+ unexpected_packet(p);
+ return NULL;
+ }
+
+ if(subrec == NULL) {
+ DEBUG(0,("find_subnet_for_nmb_packet: subnet record not found for response id %hu\n",
+ nmb->header.name_trn_id));
+ return NULL;
+ }
+
+ if(pprrec != NULL)
+ *pprrec = rrec;
+ return subrec;
+ }
+
+ /* Try and see what subnet this packet belongs to. */
+
+ /* WINS server ? */
+ if(packet_is_for_wins_server(p))
+ return wins_server_subnet;
+
+ /* If it wasn't a broadcast packet then send to the UNICAST subnet. */
+ if(nmb->header.nm_flags.bcast == False)
+ return unicast_subnet;
+
+ /* Go through all the broadcast subnets and see if the mask matches. */
+ for (subrec = FIRST_SUBNET; subrec ; subrec = NEXT_SUBNET_EXCLUDING_UNICAST(subrec)) {
+ if(same_net(p->ip, subrec->bcast_ip, subrec->mask_ip))
+ return subrec;
+ }
+
+ /* If none match it must have been a directed broadcast - assign the remote_broadcast_subnet. */
+ return remote_broadcast_subnet;
+}
+
+/****************************************************************************
+ Process a nmb request packet - validate the packet and route it.
+****************************************************************************/
+
+static void process_nmb_request(struct packet_struct *p)
+{
+ struct nmb_packet *nmb = &p->packet.nmb;
+ struct subnet_record *subrec = NULL;
+
+ debug_nmb_packet(p);
+
+ /* Ensure we have a good packet. */
+ if(validate_nmb_packet(nmb))
+ return;
+
+ /* Allocate a subnet to this packet - if we cannot - fail. */
+ if((subrec = find_subnet_for_nmb_packet(p, NULL))==NULL)
+ return;
+
+ switch (nmb->header.opcode) {
+ case NMB_NAME_REG_OPCODE:
+ if(subrec == wins_server_subnet)
+ wins_process_name_registration_request(subrec, p);
+ else
+ process_name_registration_request(subrec, p);
+ break;
+
+ case NMB_NAME_REFRESH_OPCODE_8: /* ambiguity in rfc1002 about which is correct. */
+ case NMB_NAME_REFRESH_OPCODE_9:
+ if(subrec == wins_server_subnet)
+ wins_process_name_refresh_request(subrec, p);
+ else
+ process_name_refresh_request(subrec, p);
+ break;
+
+ case NMB_NAME_MULTIHOMED_REG_OPCODE:
+ if(subrec == wins_server_subnet) {
+ wins_process_multihomed_name_registration_request(subrec, p);
+ } else {
+ DEBUG(0,("process_nmb_request: Multihomed registration request must be \
+directed at a WINS server.\n"));
+ }
+ break;
+
+ case NMB_NAME_QUERY_OPCODE:
+ switch (nmb->question.question_type) {
+ case QUESTION_TYPE_NB_QUERY:
+ if(subrec == wins_server_subnet)
+ wins_process_name_query_request(subrec, p);
+ else
+ process_name_query_request(subrec, p);
+ break;
+ case QUESTION_TYPE_NB_STATUS:
+ if(subrec == wins_server_subnet) {
+ DEBUG(0,("process_nmb_request: NB_STATUS request directed at WINS server is \
+not allowed.\n"));
+ break;
+ } else {
+ process_node_status_request(subrec, p);
+ }
+ break;
+ }
+ break;
+
+ case NMB_NAME_RELEASE_OPCODE:
+ if(subrec == wins_server_subnet)
+ wins_process_name_release_request(subrec, p);
+ else
+ process_name_release_request(subrec, p);
+ break;
+ }
+}
+
+/****************************************************************************
+ Process a nmb response packet - validate the packet and route it.
+ to either the WINS server or a normal response.
+****************************************************************************/
+
+static void process_nmb_response(struct packet_struct *p)
+{
+ struct nmb_packet *nmb = &p->packet.nmb;
+ struct subnet_record *subrec = NULL;
+ struct response_record *rrec = NULL;
+
+ debug_nmb_packet(p);
+
+ if(validate_nmb_response_packet(nmb))
+ return;
+
+ if((subrec = find_subnet_for_nmb_packet(p, &rrec))==NULL)
+ return;
+
+ if(rrec == NULL) {
+ DEBUG(0,("process_nmb_response: response packet received but no response record \
+found for id = %hu. Ignoring packet.\n", nmb->header.name_trn_id));
+ return;
+ }
+
+ /* Increment the number of responses received for this record. */
+ rrec->num_msgs++;
+ /* Ensure we don't re-send the request. */
+ rrec->repeat_count = 0;
+
+ /* Call the response received function for this packet. */
+ (*rrec->resp_fn)(subrec, rrec, p);
+}
+
+/*******************************************************************
+ Run elements off the packet queue till its empty
+******************************************************************/
+
+void run_packet_queue(void)
+{
+ struct packet_struct *p;
+
+ while ((p = packet_queue)) {
+ packet_queue = p->next;
+ if (packet_queue)
+ packet_queue->prev = NULL;
+ p->next = p->prev = NULL;
+
+ switch (p->packet_type) {
+ case NMB_PACKET:
+ if(p->packet.nmb.header.response)
+ process_nmb_response(p);
+ else
+ process_nmb_request(p);
+ break;
+
+ case DGRAM_PACKET:
+ process_dgram(p);
+ break;
+ }
+ free_packet(p);
+ }
+}
+
+/*******************************************************************
+ Retransmit or timeout elements from all the outgoing subnet response
+ record queues. NOTE that this code must also check the WINS server
+ subnet for response records to timeout as the WINS server code
+ can send requests to check if a client still owns a name.
+ (Patch from Andrey Alekseyev <fetch@muffin.arcadia.spb.ru>).
+******************************************************************/
+
+void retransmit_or_expire_response_records(time_t t)
+{
+ struct subnet_record *subrec;
+
+ for (subrec = FIRST_SUBNET; subrec; subrec = get_next_subnet_maybe_unicast_or_wins_server(subrec)) {
+ struct response_record *rrec, *nextrrec;
+
+ for (rrec = subrec->responselist; rrec; rrec = nextrrec) {
+ nextrrec = rrec->next;
+
+ if (rrec->repeat_time <= t) {
+ if (rrec->repeat_count > 0) {
+ /* Resend while we have a non-zero repeat_count. */
+ if(!send_packet(rrec->packet)) {
+ DEBUG(0,("retransmit_or_expire_response_records: Failed to resend packet id %hu \
+to IP %s on subnet %s\n", rrec->response_id, inet_ntoa(rrec->packet->ip), subrec->subnet_name));
+ }
+ rrec->repeat_time = t + rrec->repeat_interval;
+ rrec->repeat_count--;
+ } else {
+ DEBUG(4,("retransmit_or_expire_response_records: timeout for packet id %hu to IP %s \
+on subnet %s\n", rrec->response_id, inet_ntoa(rrec->packet->ip), subrec->subnet_name));
+
+ /*
+ * Check the flag in this record to prevent recursion if we end
+ * up in this function again via the timeout function call.
+ */
+
+ if(!rrec->in_expiration_processing) {
+
+ /*
+ * Set the recursion protection flag in this record.
+ */
+
+ rrec->in_expiration_processing = True;
+
+ /* Call the timeout function. This will deal with removing the
+ timed out packet. */
+ if(rrec->timeout_fn) {
+ (*rrec->timeout_fn)(subrec, rrec);
+ } else {
+ /* We must remove the record ourself if there is
+ no timeout function. */
+ remove_response_record(subrec, rrec);
+ }
+ } /* !rrec->in_expitation_processing */
+ } /* rrec->repeat_count > 0 */
+ } /* rrec->repeat_time <= t */
+ } /* end for rrec */
+ } /* end for subnet */
+}
+
+/****************************************************************************
+ Create an fd_set containing all the sockets in the subnet structures,
+ plus the broadcast sockets.
+***************************************************************************/
+
+static BOOL create_listen_fdset(fd_set **ppset, int **psock_array, int *listen_number)
+{
+ int *sock_array = NULL;
+ struct subnet_record *subrec = NULL;
+ int count = 0;
+ int num = 0;
+ fd_set *pset = (fd_set *)malloc(sizeof(fd_set));
+
+ if(pset == NULL) {
+ DEBUG(0,("create_listen_fdset: malloc fail !\n"));
+ return True;
+ }
+
+ /* Check that we can add all the fd's we need. */
+ for (subrec = FIRST_SUBNET; subrec; subrec = NEXT_SUBNET_EXCLUDING_UNICAST(subrec))
+ count++;
+
+ if((count*2) + 2 > FD_SETSIZE) {
+ DEBUG(0,("create_listen_fdset: Too many file descriptors needed (%d). We can \
+only use %d.\n", (count*2) + 2, FD_SETSIZE));
+ return True;
+ }
+
+ if((sock_array = (int *)malloc(((count*2) + 2)*sizeof(int))) == NULL) {
+ DEBUG(0,("create_listen_fdset: malloc fail for socket array.\n"));
+ return True;
+ }
+
+ FD_ZERO(pset);
+
+ /* Add in the broadcast socket on 137. */
+ FD_SET(ClientNMB,pset);
+ sock_array[num++] = ClientNMB;
+
+ /* Add in the 137 sockets on all the interfaces. */
+ for (subrec = FIRST_SUBNET; subrec; subrec = NEXT_SUBNET_EXCLUDING_UNICAST(subrec)) {
+ FD_SET(subrec->nmb_sock,pset);
+ sock_array[num++] = subrec->nmb_sock;
+ }
+
+ /* Add in the broadcast socket on 138. */
+ FD_SET(ClientDGRAM,pset);
+ sock_array[num++] = ClientDGRAM;
+
+ /* Add in the 138 sockets on all the interfaces. */
+ for (subrec = FIRST_SUBNET; subrec; subrec = NEXT_SUBNET_EXCLUDING_UNICAST(subrec)) {
+ FD_SET(subrec->dgram_sock,pset);
+ sock_array[num++] = subrec->dgram_sock;
+ }
+
+ *listen_number = (count*2) + 2;
+
+ SAFE_FREE(*ppset);
+ SAFE_FREE(*psock_array);
+
+ *ppset = pset;
+ *psock_array = sock_array;
+
+ return False;
+}
+
+/****************************************************************************
+ Listens for NMB or DGRAM packets, and queues them.
+ return True if the socket is dead
+***************************************************************************/
+
+BOOL listen_for_packets(BOOL run_election)
+{
+ static fd_set *listen_set = NULL;
+ static int listen_number = 0;
+ static int *sock_array = NULL;
+ int i;
+
+ fd_set fds;
+ int selrtn;
+ struct timeval timeout;
+#ifndef SYNC_DNS
+ int dns_fd;
+#endif
+
+ if(listen_set == NULL || rescan_listen_set) {
+ if(create_listen_fdset(&listen_set, &sock_array, &listen_number)) {
+ DEBUG(0,("listen_for_packets: Fatal error. unable to create listen set. Exiting.\n"));
+ return True;
+ }
+ rescan_listen_set = False;
+ }
+
+ memcpy((char *)&fds, (char *)listen_set, sizeof(fd_set));
+
+#ifndef SYNC_DNS
+ dns_fd = asyncdns_fd();
+ if (dns_fd != -1) {
+ FD_SET(dns_fd, &fds);
+ }
+#endif
+
+ /*
+ * During elections and when expecting a netbios response packet we
+ * need to send election packets at tighter intervals.
+ * Ideally it needs to be the interval (in ms) between time now and
+ * the time we are expecting the next netbios packet.
+ */
+
+ timeout.tv_sec = (run_election||num_response_packets) ? 1 : NMBD_SELECT_LOOP;
+ timeout.tv_usec = 0;
+
+ /* Prepare for the select - allow certain signals. */
+
+ BlockSignals(False, SIGTERM);
+
+ selrtn = sys_select(FD_SETSIZE,&fds,NULL,NULL,&timeout);
+
+ /* We can only take signals when we are in the select - block them again here. */
+
+ BlockSignals(True, SIGTERM);
+
+ if(selrtn == -1) {
+ return False;
+ }
+
+#ifndef SYNC_DNS
+ if (dns_fd != -1 && FD_ISSET(dns_fd,&fds)) {
+ run_dns_queue();
+ }
+#endif
+
+ for(i = 0; i < listen_number; i++) {
+ if (i < (listen_number/2)) {
+ /* Processing a 137 socket. */
+ if (FD_ISSET(sock_array[i],&fds)) {
+ struct packet_struct *packet = read_packet(sock_array[i], NMB_PACKET);
+ if (packet) {
+ /*
+ * If we got a packet on the broadcast socket and interfaces
+ * only is set then check it came from one of our local nets.
+ */
+ if(lp_bind_interfaces_only() && (sock_array[i] == ClientNMB) &&
+ (!is_local_net(packet->ip))) {
+ DEBUG(7,("discarding nmb packet sent to broadcast socket from %s:%d\n",
+ inet_ntoa(packet->ip),packet->port));
+ free_packet(packet);
+ } else if ((ip_equal(loopback_ip, packet->ip) ||
+ ismyip(packet->ip)) && packet->port == global_nmb_port &&
+ packet->packet.nmb.header.nm_flags.bcast) {
+ DEBUG(7,("discarding own bcast packet from %s:%d\n",
+ inet_ntoa(packet->ip),packet->port));
+ free_packet(packet);
+ } else {
+ /* Save the file descriptor this packet came in on. */
+ packet->fd = sock_array[i];
+ queue_packet(packet);
+ }
+ }
+ }
+ } else {
+ /* Processing a 138 socket. */
+ if (FD_ISSET(sock_array[i],&fds)) {
+ struct packet_struct *packet = read_packet(sock_array[i], DGRAM_PACKET);
+ if (packet) {
+ /*
+ * If we got a packet on the broadcast socket and interfaces
+ * only is set then check it came from one of our local nets.
+ */
+ if(lp_bind_interfaces_only() && (sock_array[i] == ClientDGRAM) &&
+ (!is_local_net(packet->ip))) {
+ DEBUG(7,("discarding dgram packet sent to broadcast socket from %s:%d\n",
+ inet_ntoa(packet->ip),packet->port));
+ free_packet(packet);
+ } else if ((ip_equal(loopback_ip, packet->ip) ||
+ ismyip(packet->ip)) && packet->port == DGRAM_PORT) {
+ DEBUG(7,("discarding own dgram packet from %s:%d\n",
+ inet_ntoa(packet->ip),packet->port));
+ free_packet(packet);
+ } else {
+ /* Save the file descriptor this packet came in on. */
+ packet->fd = sock_array[i];
+ queue_packet(packet);
+ }
+ }
+ }
+ } /* end processing 138 socket. */
+ } /* end for */
+ return False;
+}
+
+/****************************************************************************
+ Construct and send a netbios DGRAM.
+**************************************************************************/
+
+BOOL send_mailslot(BOOL unique, const char *mailslot,char *buf, size_t len,
+ const char *srcname, int src_type,
+ const char *dstname, int dest_type,
+ struct in_addr dest_ip,struct in_addr src_ip,
+ int dest_port)
+{
+ BOOL loopback_this_packet = False;
+ struct packet_struct p;
+ struct dgram_packet *dgram = &p.packet.dgram;
+ char *ptr,*p2;
+ char tmp[4];
+
+ memset((char *)&p,'\0',sizeof(p));
+
+ if(ismyip(dest_ip) && (dest_port == DGRAM_PORT)) /* Only if to DGRAM_PORT */
+ loopback_this_packet = True;
+
+ /* generate_name_trn_id(); */ /* Not used, so gone, RJS */
+
+ /* DIRECT GROUP or UNIQUE datagram. */
+ dgram->header.msg_type = unique ? 0x10 : 0x11;
+ dgram->header.flags.node_type = M_NODE;
+ dgram->header.flags.first = True;
+ dgram->header.flags.more = False;
+ dgram->header.dgm_id = generate_name_trn_id();
+ dgram->header.source_ip = src_ip;
+ dgram->header.source_port = DGRAM_PORT;
+ dgram->header.dgm_length = 0; /* Let build_dgram() handle this. */
+ dgram->header.packet_offset = 0;
+
+ make_nmb_name(&dgram->source_name,srcname,src_type);
+ make_nmb_name(&dgram->dest_name,dstname,dest_type);
+
+ ptr = &dgram->data[0];
+
+ /* Setup the smb part. */
+ ptr -= 4; /* XXX Ugliness because of handling of tcp SMB length. */
+ memcpy(tmp,ptr,4);
+ set_message(ptr,17,strlen(mailslot) + 1 + len,True);
+ memcpy(ptr,tmp,4);
+
+ SCVAL(ptr,smb_com,SMBtrans);
+ SSVAL(ptr,smb_vwv1,len);
+ SSVAL(ptr,smb_vwv11,len);
+ SSVAL(ptr,smb_vwv12,70 + strlen(mailslot));
+ SSVAL(ptr,smb_vwv13,3);
+ SSVAL(ptr,smb_vwv14,1);
+ SSVAL(ptr,smb_vwv15,1);
+ SSVAL(ptr,smb_vwv16,2);
+ p2 = smb_buf(ptr);
+ safe_strcpy_base(p2, mailslot, dgram->data, sizeof(dgram->data));
+ p2 = skip_string(p2,1);
+
+ if (((p2+len) > dgram->data+sizeof(dgram->data)) || ((p2+len) < p2)) {
+ DEBUG(0, ("send_mailslot: Cannot write beyond end of packet\n"));
+ return False;
+ } else {
+ memcpy(p2,buf,len);
+ p2 += len;
+ }
+
+ dgram->datasize = PTR_DIFF(p2,ptr+4); /* +4 for tcp length. */
+
+ p.ip = dest_ip;
+ p.port = dest_port;
+ p.fd = find_subnet_mailslot_fd_for_address( src_ip );
+ p.timestamp = time(NULL);
+ p.packet_type = DGRAM_PACKET;
+
+ DEBUG(4,("send_mailslot: Sending to mailslot %s from %s IP %s ", mailslot,
+ nmb_namestr(&dgram->source_name), inet_ntoa(src_ip)));
+ DEBUG(4,("to %s IP %s\n", nmb_namestr(&dgram->dest_name), inet_ntoa(dest_ip)));
+
+ debug_browse_data(buf, len);
+
+ if(loopback_this_packet) {
+ struct packet_struct *lo_packet = NULL;
+ DEBUG(5,("send_mailslot: sending packet to ourselves.\n"));
+ if((lo_packet = copy_packet(&p)) == NULL)
+ return False;
+ queue_packet(lo_packet);
+ return True;
+ } else {
+ return(send_packet(&p));
+ }
+}
diff --git a/source/nmbd/nmbd_processlogon.c b/source/nmbd/nmbd_processlogon.c
new file mode 100644
index 00000000000..da93224043c
--- /dev/null
+++ b/source/nmbd/nmbd_processlogon.c
@@ -0,0 +1,522 @@
+/*
+ Unix SMB/CIFS implementation.
+ NBT netbios routines and daemon - version 2
+ Copyright (C) Andrew Tridgell 1994-1998
+ Copyright (C) Luke Kenneth Casson Leighton 1994-1998
+ Copyright (C) Jeremy Allison 1994-2003
+ Copyright (C) Jim McDonough <jmcd@us.ibm.com> 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 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.
+
+ Revision History:
+
+*/
+
+#include "includes.h"
+
+struct sam_database_info {
+ uint32 index;
+ uint32 serial_lo, serial_hi;
+ uint32 date_lo, date_hi;
+};
+
+/****************************************************************************
+Send a message to smbd to do a sam delta sync
+**************************************************************************/
+
+static void send_repl_message(uint32 low_serial)
+{
+ TDB_CONTEXT *tdb;
+
+ tdb = tdb_open_log(lock_path("connections.tdb"), 0,
+ TDB_DEFAULT, O_RDONLY, 0);
+
+ if (!tdb) {
+ DEBUG(3, ("send_repl_message(): failed to open connections "
+ "database\n"));
+ return;
+ }
+
+ DEBUG(3, ("sending replication message, serial = 0x%04x\n",
+ low_serial));
+
+ message_send_all(tdb, MSG_SMB_SAM_REPL, &low_serial,
+ sizeof(low_serial), False, NULL);
+
+ tdb_close(tdb);
+}
+
+/****************************************************************************
+Process a domain logon packet
+**************************************************************************/
+
+void process_logon_packet(struct packet_struct *p, char *buf,int len,
+ const char *mailslot)
+{
+ struct dgram_packet *dgram = &p->packet.dgram;
+ pstring my_name;
+ fstring reply_name;
+ pstring outbuf;
+ int code;
+ uint16 token = 0;
+ uint32 ntversion = 0;
+ uint16 lmnttoken = 0;
+ uint16 lm20token = 0;
+ uint32 domainsidsize;
+ BOOL short_request = False;
+ char *getdc;
+ char *uniuser; /* Unicode user name. */
+ pstring ascuser;
+ char *unicomp; /* Unicode computer name. */
+
+ memset(outbuf, 0, sizeof(outbuf));
+
+ if (!lp_domain_logons()) {
+ DEBUG(5,("process_logon_packet: Logon packet received from IP %s and domain \
+logons are not enabled.\n", inet_ntoa(p->ip) ));
+ return;
+ }
+
+ pstrcpy(my_name, global_myname());
+
+ code = SVAL(buf,0);
+ DEBUG(4,("process_logon_packet: Logon from %s: code = 0x%x\n", inet_ntoa(p->ip), code));
+
+ switch (code) {
+ case 0:
+ {
+ fstring mach_str, user_str, getdc_str;
+ char *q = buf + 2;
+ char *machine = q;
+ char *user = skip_string(machine,1);
+
+ getdc = skip_string(user,1);
+ q = skip_string(getdc,1);
+ token = SVAL(q,3);
+
+ fstrcpy(reply_name,my_name);
+
+ pull_ascii_fstring(mach_str, machine);
+ pull_ascii_fstring(user_str, user);
+ pull_ascii_fstring(getdc_str, getdc);
+
+ DEBUG(5,("process_logon_packet: Domain login request from %s at IP %s user=%s token=%x\n",
+ mach_str,inet_ntoa(p->ip),user_str,token));
+
+ q = outbuf;
+ SSVAL(q, 0, 6);
+ q += 2;
+
+ fstrcpy(reply_name, "\\\\");
+ fstrcat(reply_name, my_name);
+ push_ascii_fstring(q, reply_name);
+ q = skip_string(q, 1); /* PDC name */
+
+ SSVAL(q, 0, token);
+ q += 2;
+
+ dump_data(4, outbuf, PTR_DIFF(q, outbuf));
+
+ send_mailslot(True, getdc_str,
+ outbuf,PTR_DIFF(q,outbuf),
+ global_myname(), 0x0,
+ mach_str,
+ dgram->source_name.name_type,
+ p->ip, *iface_ip(p->ip), p->port);
+ break;
+ }
+
+ case QUERYFORPDC:
+ {
+ fstring mach_str, getdc_str;
+ fstring source_name;
+ char *q = buf + 2;
+ char *machine = q;
+
+ if (!lp_domain_master()) {
+ /* We're not Primary Domain Controller -- ignore this */
+ return;
+ }
+
+ getdc = skip_string(machine,1);
+ q = skip_string(getdc,1);
+ q = ALIGN2(q, buf);
+
+ /* At this point we can work out if this is a W9X or NT style
+ request. Experiments show that the difference is wether the
+ packet ends here. For a W9X request we now end with a pair of
+ bytes (usually 0xFE 0xFF) whereas with NT we have two further
+ strings - the following is a simple way of detecting this */
+
+ if (len - PTR_DIFF(q, buf) <= 3) {
+ short_request = True;
+ } else {
+ unicomp = q;
+
+ /* A full length (NT style) request */
+ q = skip_unibuf(unicomp, PTR_DIFF(buf + len, unicomp));
+
+ if (len - PTR_DIFF(q, buf) > 8) {
+ /* with NT5 clients we can sometimes
+ get additional data - a length specificed string
+ containing the domain name, then 16 bytes of
+ data (no idea what it is) */
+ int dom_len = CVAL(q, 0);
+ q++;
+ if (dom_len != 0) {
+ q += dom_len + 1;
+ }
+ q += 16;
+ }
+ ntversion = IVAL(q, 0);
+ lmnttoken = SVAL(q, 4);
+ lm20token = SVAL(q, 6);
+ }
+
+ /* Construct reply. */
+ q = outbuf;
+ SSVAL(q, 0, QUERYFORPDC_R);
+ q += 2;
+
+ fstrcpy(reply_name,my_name);
+ push_ascii_fstring(q, reply_name);
+ q = skip_string(q, 1); /* PDC name */
+
+ /* PDC and domain name */
+ if (!short_request) {
+ /* Make a full reply */
+ q = ALIGN2(q, outbuf);
+
+ q += dos_PutUniCode(q, my_name, sizeof(pstring), True); /* PDC name */
+ q += dos_PutUniCode(q, lp_workgroup(),sizeof(pstring), True); /* Domain name*/
+ SIVAL(q, 0, 1); /* our nt version */
+ SSVAL(q, 4, 0xffff); /* our lmnttoken */
+ SSVAL(q, 6, 0xffff); /* our lm20token */
+ q += 8;
+ }
+
+ /* RJS, 21-Feb-2000, we send a short reply if the request was short */
+
+ pull_ascii_fstring(mach_str, machine);
+
+ DEBUG(5,("process_logon_packet: GETDC request from %s at IP %s, \
+reporting %s domain %s 0x%x ntversion=%x lm_nt token=%x lm_20 token=%x\n",
+ mach_str,inet_ntoa(p->ip), reply_name, lp_workgroup(),
+ QUERYFORPDC_R, (uint32)ntversion, (uint32)lmnttoken,
+ (uint32)lm20token ));
+
+ dump_data(4, outbuf, PTR_DIFF(q, outbuf));
+
+ pull_ascii_fstring(getdc_str, getdc);
+ pull_ascii_nstring(source_name, sizeof(source_name), dgram->source_name.name);
+
+ send_mailslot(True, getdc_str,
+ outbuf,PTR_DIFF(q,outbuf),
+ global_myname(), 0x0,
+ source_name,
+ dgram->source_name.name_type,
+ p->ip, *iface_ip(p->ip), p->port);
+ return;
+ }
+
+ case SAMLOGON:
+
+ {
+ fstring getdc_str;
+ fstring source_name;
+ char *q = buf + 2;
+ fstring asccomp;
+
+ q += 2;
+ unicomp = q;
+ uniuser = skip_unibuf(unicomp, PTR_DIFF(buf+len, unicomp));
+ getdc = skip_unibuf(uniuser,PTR_DIFF(buf+len, uniuser));
+ q = skip_string(getdc,1);
+ q += 4; /* Account Control Bits - indicating username type */
+ domainsidsize = IVAL(q, 0);
+ q += 4;
+
+ DEBUG(5,("process_logon_packet: SAMLOGON sidsize %d, len = %d\n", domainsidsize, len));
+
+ if (domainsidsize < (len - PTR_DIFF(q, buf)) && (domainsidsize != 0)) {
+ q += domainsidsize;
+ q = ALIGN4(q, buf);
+ }
+
+ DEBUG(5,("process_logon_packet: len = %d PTR_DIFF(q, buf) = %ld\n", len, (unsigned long)PTR_DIFF(q, buf) ));
+
+ if (len - PTR_DIFF(q, buf) > 8) {
+ /* with NT5 clients we can sometimes
+ get additional data - a length specificed string
+ containing the domain name, then 16 bytes of
+ data (no idea what it is) */
+ int dom_len = CVAL(q, 0);
+ q++;
+ if (dom_len < (len - PTR_DIFF(q, buf)) && (dom_len != 0)) {
+ q += dom_len + 1;
+ }
+ q += 16;
+ }
+
+ ntversion = IVAL(q, 0);
+ lmnttoken = SVAL(q, 4);
+ lm20token = SVAL(q, 6);
+ q += 8;
+
+ DEBUG(3,("process_logon_packet: SAMLOGON sidsize %d ntv %d\n", domainsidsize, ntversion));
+
+ /*
+ * we respond regadless of whether the machine is in our password
+ * database. If it isn't then we let smbd send an appropriate error.
+ * Let's ignore the SID.
+ */
+ pull_ucs2_pstring(ascuser, uniuser);
+ pull_ucs2_fstring(asccomp, unicomp);
+ DEBUG(5,("process_logon_packet: SAMLOGON user %s\n", ascuser));
+
+ fstrcpy(reply_name, "\\\\"); /* Here it wants \\LOGONSERVER. */
+ fstrcat(reply_name, my_name);
+
+ DEBUG(5,("process_logon_packet: SAMLOGON request from %s(%s) for %s, returning logon svr %s domain %s code %x token=%x\n",
+ asccomp,inet_ntoa(p->ip), ascuser, reply_name, lp_workgroup(),
+ SAMLOGON_R ,lmnttoken));
+
+ /* Construct reply. */
+
+ q = outbuf;
+ /* we want the simple version unless we are an ADS PDC..which means */
+ /* never, at least for now */
+ if ((ntversion < 11) || (SEC_ADS != lp_security()) || (ROLE_DOMAIN_PDC != lp_server_role())) {
+ if (SVAL(uniuser, 0) == 0) {
+ SSVAL(q, 0, SAMLOGON_UNK_R); /* user unknown */
+ } else {
+ SSVAL(q, 0, SAMLOGON_R);
+ }
+
+ q += 2;
+
+ q += dos_PutUniCode(q, reply_name,sizeof(pstring), True);
+ q += dos_PutUniCode(q, ascuser, sizeof(pstring), True);
+ q += dos_PutUniCode(q, lp_workgroup(),sizeof(pstring), True);
+ }
+#ifdef HAVE_ADS
+ else {
+ struct uuid domain_guid;
+ UUID_FLAT flat_guid;
+ pstring domain;
+ pstring hostname;
+ char *component, *dc, *q1;
+ uint8 size;
+ char *q_orig = q;
+ int str_offset;
+
+ get_mydnsdomname(domain);
+ get_myname(hostname);
+
+ if (SVAL(uniuser, 0) == 0) {
+ SIVAL(q, 0, SAMLOGON_AD_UNK_R); /* user unknown */
+ } else {
+ SIVAL(q, 0, SAMLOGON_AD_R);
+ }
+ q += 4;
+
+ SIVAL(q, 0, ADS_PDC|ADS_GC|ADS_LDAP|ADS_DS|
+ ADS_KDC|ADS_TIMESERV|ADS_CLOSEST|ADS_WRITABLE);
+ q += 4;
+
+ /* Push Domain GUID */
+ if (False == secrets_fetch_domain_guid(domain, &domain_guid)) {
+ DEBUG(2, ("Could not fetch DomainGUID for %s\n", domain));
+ return;
+ }
+
+ smb_uuid_pack(domain_guid, &flat_guid);
+ memcpy(q, &flat_guid.info, UUID_FLAT_SIZE);
+ q += UUID_FLAT_SIZE;
+
+ /* Forest */
+ str_offset = q - q_orig;
+ dc = domain;
+ q1 = q;
+ while ((component = strtok(dc, "."))) {
+ dc = NULL;
+ size = push_ascii(&q[1], component, -1, 0);
+ SCVAL(q, 0, size);
+ q += (size + 1);
+ }
+
+ /* Unk0 */
+ SCVAL(q, 0, 0);
+ q++;
+
+ /* Domain */
+ SCVAL(q, 0, 0xc0 | ((str_offset >> 8) & 0x3F));
+ SCVAL(q, 1, str_offset & 0xFF);
+ q += 2;
+
+ /* Hostname */
+ size = push_ascii(&q[1], hostname, -1, 0);
+ SCVAL(q, 0, size);
+ q += (size + 1);
+ SCVAL(q, 0, 0xc0 | ((str_offset >> 8) & 0x3F));
+ SCVAL(q, 1, str_offset & 0xFF);
+ q += 2;
+
+ /* NETBIOS of domain */
+ size = push_ascii(&q[1], lp_workgroup(), -1, STR_UPPER);
+ SCVAL(q, 0, size);
+ q += (size + 1);
+
+ /* Unk1 */
+ SCVAL(q, 0, 0);
+ q++;
+
+ /* NETBIOS of hostname */
+ size = push_ascii(&q[1], my_name, -1, 0);
+ SCVAL(q, 0, size);
+ q += (size + 1);
+
+ /* Unk2 */
+ SCVAL(q, 0, 0);
+ q++;
+
+ /* User name */
+ if (SVAL(uniuser, 0) != 0) {
+ size = push_ascii(&q[1], ascuser, -1, 0);
+ SCVAL(q, 0, size);
+ q += (size + 1);
+ }
+
+ q_orig = q;
+ /* Site name */
+ size = push_ascii(&q[1], "Default-First-Site-Name", -1, 0);
+ SCVAL(q, 0, size);
+ q += (size + 1);
+
+ /* Site name (2) */
+ str_offset = q - q_orig;
+ SCVAL(q, 0, 0xc0 | ((str_offset >> 8) & 0x3F));
+ SCVAL(q, 1, str_offset & 0xFF);
+ q += 2;
+
+ SCVAL(q, 0, PTR_DIFF(q,q1));
+ SCVAL(q, 1, 0x10); /* unknown */
+
+ SIVAL(q, 0, 0x00000002);
+ q += 4; /* unknown */
+ SIVAL(q, 0, (iface_ip(p->ip))->s_addr);
+ q += 4;
+ SIVAL(q, 0, 0x00000000);
+ q += 4; /* unknown */
+ SIVAL(q, 0, 0x00000000);
+ q += 4; /* unknown */
+ }
+#endif
+
+ /* tell the client what version we are */
+ SIVAL(q, 0, ((ntversion < 11) || (SEC_ADS != lp_security())) ? 1 : 13);
+ /* our ntversion */
+ SSVAL(q, 4, 0xffff); /* our lmnttoken */
+ SSVAL(q, 6, 0xffff); /* our lm20token */
+ q += 8;
+
+ dump_data(4, outbuf, PTR_DIFF(q, outbuf));
+
+ pull_ascii_fstring(getdc_str, getdc);
+ pull_ascii_nstring(source_name, sizeof(source_name), dgram->source_name.name);
+
+ send_mailslot(True, getdc,
+ outbuf,PTR_DIFF(q,outbuf),
+ global_myname(), 0x0,
+ source_name,
+ dgram->source_name.name_type,
+ p->ip, *iface_ip(p->ip), p->port);
+ break;
+ }
+
+ /* Announce change to UAS or SAM. Send by the domain controller when a
+ replication event is required. */
+
+ case SAM_UAS_CHANGE:
+ {
+ struct sam_database_info *db_info;
+ char *q = buf + 2;
+ int i, db_count;
+ uint32 low_serial;
+
+ /* Header */
+
+ low_serial = IVAL(q, 0); q += 4; /* Low serial number */
+
+ q += 4; /* Date/time */
+ q += 4; /* Pulse */
+ q += 4; /* Random */
+
+ /* Domain info */
+
+ q = skip_string(q, 1); /* PDC name */
+ q = skip_string(q, 1); /* Domain name */
+ q = skip_unibuf(q, PTR_DIFF(buf + len, q)); /* Unicode PDC name */
+ q = skip_unibuf(q, PTR_DIFF(buf + len, q)); /* Unicode domain name */
+
+ /* Database info */
+
+ db_count = SVAL(q, 0); q += 2;
+
+ db_info = (struct sam_database_info *)
+ malloc(sizeof(struct sam_database_info) * db_count);
+
+ if (db_info == NULL) {
+ DEBUG(3, ("out of memory allocating info for %d databases\n", db_count));
+ return;
+ }
+
+ for (i = 0; i < db_count; i++) {
+ db_info[i].index = IVAL(q, 0);
+ db_info[i].serial_lo = IVAL(q, 4);
+ db_info[i].serial_hi = IVAL(q, 8);
+ db_info[i].date_lo = IVAL(q, 12);
+ db_info[i].date_hi = IVAL(q, 16);
+ q += 20;
+ }
+
+ /* Domain SID */
+
+#if 0
+ /* We must range check this. */
+ q += IVAL(q, 0) + 4; /* 4 byte length plus data */
+
+ q += 2; /* Alignment? */
+
+ /* Misc other info */
+
+ q += 4; /* NT version (0x1) */
+ q += 2; /* LMNT token (0xff) */
+ q += 2; /* LM20 token (0xff) */
+#endif
+
+ SAFE_FREE(db_info); /* Not sure whether we need to do anything useful with these */
+
+ /* Send message to smbd */
+
+ send_repl_message(low_serial);
+ break;
+ }
+
+ default:
+ DEBUG(3,("process_logon_packet: Unknown domain request %d\n",code));
+ return;
+ }
+}
diff --git a/source/nmbd/nmbd_responserecordsdb.c b/source/nmbd/nmbd_responserecordsdb.c
new file mode 100644
index 00000000000..30c0c129508
--- /dev/null
+++ b/source/nmbd/nmbd_responserecordsdb.c
@@ -0,0 +1,248 @@
+/*
+ Unix SMB/CIFS implementation.
+ NBT netbios library routines
+ Copyright (C) Andrew Tridgell 1994-1998
+ Copyright (C) Luke Kenneth Casson Leighton 1994-1998
+ Copyright (C) Jeremy Allison 1994-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 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"
+
+extern int ClientNMB;
+
+int num_response_packets = 0;
+
+/***************************************************************************
+ Add an expected response record into the list
+ **************************************************************************/
+
+static void add_response_record(struct subnet_record *subrec,
+ struct response_record *rrec)
+{
+ struct response_record *rrec2;
+
+ num_response_packets++; /* count of total number of packets still around */
+
+ DEBUG(4,("add_response_record: adding response record id:%hu to subnet %s. num_records:%d\n",
+ rrec->response_id, subrec->subnet_name, num_response_packets));
+
+ if (!subrec->responselist) {
+ subrec->responselist = rrec;
+ rrec->prev = NULL;
+ rrec->next = NULL;
+ return;
+ }
+
+ for (rrec2 = subrec->responselist; rrec2->next; rrec2 = rrec2->next)
+ ;
+
+ rrec2->next = rrec;
+ rrec->next = NULL;
+ rrec->prev = rrec2;
+}
+
+/***************************************************************************
+ Remove an expected response record from the list
+ **************************************************************************/
+
+void remove_response_record(struct subnet_record *subrec,
+ struct response_record *rrec)
+{
+ if (rrec->prev)
+ rrec->prev->next = rrec->next;
+ if (rrec->next)
+ rrec->next->prev = rrec->prev;
+
+ if (subrec->responselist == rrec)
+ subrec->responselist = rrec->next;
+
+ if(rrec->userdata) {
+ if(rrec->userdata->free_fn) {
+ (*rrec->userdata->free_fn)(rrec->userdata);
+ } else {
+ ZERO_STRUCTP(rrec->userdata);
+ SAFE_FREE(rrec->userdata);
+ }
+ }
+
+ /* Ensure we can delete. */
+ rrec->packet->locked = False;
+ free_packet(rrec->packet);
+
+ ZERO_STRUCTP(rrec);
+ SAFE_FREE(rrec);
+
+ num_response_packets--; /* count of total number of packets still around */
+}
+
+/****************************************************************************
+ Create a response record for an outgoing packet.
+ **************************************************************************/
+
+struct response_record *make_response_record( struct subnet_record *subrec,
+ struct packet_struct *p,
+ response_function resp_fn,
+ timeout_response_function timeout_fn,
+ success_function success_fn,
+ fail_function fail_fn,
+ struct userdata_struct *userdata)
+{
+ struct response_record *rrec;
+ struct nmb_packet *nmb = &p->packet.nmb;
+
+ if (!(rrec = (struct response_record *)malloc(sizeof(*rrec)))) {
+ DEBUG(0,("make_response_queue_record: malloc fail for response_record.\n"));
+ return NULL;
+ }
+
+ memset((char *)rrec, '\0', sizeof(*rrec));
+
+ rrec->response_id = nmb->header.name_trn_id;
+
+ rrec->resp_fn = resp_fn;
+ rrec->timeout_fn = timeout_fn;
+ rrec->success_fn = success_fn;
+ rrec->fail_fn = fail_fn;
+
+ rrec->packet = p;
+
+ if(userdata) {
+ /* Intelligent userdata. */
+ if(userdata->copy_fn) {
+ if((rrec->userdata = (*userdata->copy_fn)(userdata)) == NULL) {
+ DEBUG(0,("make_response_queue_record: copy fail for userdata.\n"));
+ ZERO_STRUCTP(rrec);
+ SAFE_FREE(rrec);
+ return NULL;
+ }
+ } else {
+ /* Primitive userdata, do a memcpy. */
+ if((rrec->userdata = (struct userdata_struct *)
+ malloc(sizeof(struct userdata_struct)+userdata->userdata_len)) == NULL) {
+ DEBUG(0,("make_response_queue_record: malloc fail for userdata.\n"));
+ ZERO_STRUCTP(rrec);
+ SAFE_FREE(rrec);
+ return NULL;
+ }
+ rrec->userdata->copy_fn = userdata->copy_fn;
+ rrec->userdata->free_fn = userdata->free_fn;
+ rrec->userdata->userdata_len = userdata->userdata_len;
+ memcpy(rrec->userdata->data, userdata->data, userdata->userdata_len);
+ }
+ } else {
+ rrec->userdata = NULL;
+ }
+
+ rrec->num_msgs = 0;
+
+ if(!nmb->header.nm_flags.bcast)
+ rrec->repeat_interval = 5; /* 5 seconds for unicast packets. */
+ else
+ rrec->repeat_interval = 1; /* XXXX should be in ms */
+ rrec->repeat_count = 3; /* 3 retries */
+ rrec->repeat_time = time(NULL) + rrec->repeat_interval; /* initial retry time */
+
+ /* This packet is not being processed. */
+ rrec->in_expiration_processing = False;
+
+ /* Lock the packet so we won't lose it while it's on the list. */
+ p->locked = True;
+
+ add_response_record(subrec, rrec);
+
+ return rrec;
+}
+
+/****************************************************************************
+ Find a response in a subnet's name query response list.
+ **************************************************************************/
+
+static struct response_record *find_response_record_on_subnet(
+ struct subnet_record *subrec, uint16 id)
+{
+ struct response_record *rrec = NULL;
+
+ for (rrec = subrec->responselist; rrec; rrec = rrec->next) {
+ if (rrec->response_id == id) {
+ DEBUG(4, ("find_response_record: found response record id = %hu on subnet %s\n",
+ id, subrec->subnet_name));
+ break;
+ }
+ }
+ return rrec;
+}
+
+/****************************************************************************
+ Find a response in any subnet's name query response list.
+ **************************************************************************/
+
+struct response_record *find_response_record(struct subnet_record **ppsubrec,
+ uint16 id)
+{
+ struct response_record *rrec = NULL;
+
+ for ((*ppsubrec) = FIRST_SUBNET; (*ppsubrec);
+ (*ppsubrec) = NEXT_SUBNET_INCLUDING_UNICAST(*ppsubrec)) {
+ if((rrec = find_response_record_on_subnet(*ppsubrec, id)) != NULL)
+ return rrec;
+ }
+
+ /* There should never be response records on the remote_broadcast subnet.
+ Sanity check to ensure this is so. */
+ if(remote_broadcast_subnet->responselist != NULL) {
+ DEBUG(0,("find_response_record: response record found on subnet %s. This should \
+never happen !\n", remote_broadcast_subnet->subnet_name));
+ }
+
+ /* Now check the WINS server subnet if it exists. */
+ if(wins_server_subnet != NULL) {
+ *ppsubrec = wins_server_subnet;
+ if((rrec = find_response_record_on_subnet(*ppsubrec, id))!= NULL)
+ return rrec;
+ }
+
+ DEBUG(0,("find_response_record: response packet id %hu received with no \
+matching record.\n", id));
+
+ *ppsubrec = NULL;
+
+ return NULL;
+}
+
+/****************************************************************************
+ Check if a refresh is queued for a particular name on a particular subnet.
+ **************************************************************************/
+
+BOOL is_refresh_already_queued(struct subnet_record *subrec, struct name_record *namerec)
+{
+ struct response_record *rrec = NULL;
+
+ for (rrec = subrec->responselist; rrec; rrec = rrec->next) {
+ struct packet_struct *p = rrec->packet;
+ struct nmb_packet *nmb = &p->packet.nmb;
+
+ if((nmb->header.opcode == NMB_NAME_REFRESH_OPCODE_8) ||
+ (nmb->header.opcode == NMB_NAME_REFRESH_OPCODE_9)) {
+ /* Yes it's a queued refresh - check if the name is correct. */
+ if(nmb_name_equal(&nmb->question.question_name, &namerec->name))
+ return True;
+ }
+ }
+
+ return False;
+}
diff --git a/source/nmbd/nmbd_sendannounce.c b/source/nmbd/nmbd_sendannounce.c
new file mode 100644
index 00000000000..a74dd99196f
--- /dev/null
+++ b/source/nmbd/nmbd_sendannounce.c
@@ -0,0 +1,581 @@
+/*
+ Unix SMB/CIFS implementation.
+ NBT netbios routines and daemon - version 2
+ Copyright (C) Andrew Tridgell 1994-1998
+ Copyright (C) Luke Kenneth Casson Leighton 1994-1998
+ Copyright (C) Jeremy Allison 1994-1998
+
+ SMB Version handling
+ Copyright (C) John H Terpstra 1995-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 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"
+
+extern int updatecount;
+extern BOOL found_lm_clients;
+
+/****************************************************************************
+ Send a browser reset packet.
+**************************************************************************/
+
+void send_browser_reset(int reset_type, const char *to_name, int to_type, struct in_addr to_ip)
+{
+ pstring outbuf;
+ char *p;
+
+ DEBUG(3,("send_browser_reset: sending reset request type %d to %s<%02x> IP %s.\n",
+ reset_type, to_name, to_type, inet_ntoa(to_ip) ));
+
+ memset(outbuf,'\0',sizeof(outbuf));
+ p = outbuf;
+ SCVAL(p,0,ANN_ResetBrowserState);
+ p++;
+ SCVAL(p,0,reset_type);
+ p++;
+
+ send_mailslot(True, BROWSE_MAILSLOT, outbuf,PTR_DIFF(p,outbuf),
+ global_myname(), 0x0, to_name, to_type, to_ip,
+ FIRST_SUBNET->myip, DGRAM_PORT);
+}
+
+/****************************************************************************
+ Broadcast a packet to the local net requesting that all servers in this
+ workgroup announce themselves to us.
+ **************************************************************************/
+
+void broadcast_announce_request(struct subnet_record *subrec, struct work_record *work)
+{
+ pstring outbuf;
+ char *p;
+
+ work->needannounce = True;
+
+ DEBUG(3,("broadcast_announce_request: sending announce request for workgroup %s \
+to subnet %s\n", work->work_group, subrec->subnet_name));
+
+ memset(outbuf,'\0',sizeof(outbuf));
+ p = outbuf;
+ SCVAL(p,0,ANN_AnnouncementRequest);
+ p++;
+
+ SCVAL(p,0,work->token); /* (local) Unique workgroup token id. */
+ p++;
+ p += push_string(NULL, p+1, global_myname(), 15, STR_ASCII|STR_UPPER|STR_TERMINATE);
+
+ send_mailslot(False, BROWSE_MAILSLOT, outbuf,PTR_DIFF(p,outbuf),
+ global_myname(), 0x0, work->work_group,0x1e, subrec->bcast_ip,
+ subrec->myip, DGRAM_PORT);
+}
+
+/****************************************************************************
+ Broadcast an announcement.
+ **************************************************************************/
+
+static void send_announcement(struct subnet_record *subrec, int announce_type,
+ const char *from_name, const char *to_name, int to_type, struct in_addr to_ip,
+ time_t announce_interval,
+ const char *server_name, int server_type, const char *server_comment)
+{
+ pstring outbuf;
+ unstring upper_server_name;
+ char *p;
+
+ memset(outbuf,'\0',sizeof(outbuf));
+ p = outbuf+1;
+
+ SCVAL(outbuf,0,announce_type);
+
+ /* Announcement parameters. */
+ SCVAL(p,0,updatecount);
+ SIVAL(p,1,announce_interval*1000); /* Milliseconds - despite the spec. */
+
+ safe_strcpy(upper_server_name, server_name, sizeof(upper_server_name)-1);
+ strupper_m(upper_server_name);
+ push_string(NULL, p+5, upper_server_name, 16, STR_ASCII|STR_TERMINATE);
+
+ SCVAL(p,21,lp_major_announce_version()); /* Major version. */
+ SCVAL(p,22,lp_minor_announce_version()); /* Minor version. */
+
+ SIVAL(p,23,server_type & ~SV_TYPE_LOCAL_LIST_ONLY);
+ /* Browse version: got from NT/AS 4.00 - Value defined in smb.h (JHT). */
+ SSVAL(p,27,BROWSER_ELECTION_VERSION);
+ SSVAL(p,29,BROWSER_CONSTANT); /* Browse signature. */
+
+ p += 31 + push_string(NULL, p+31, server_comment, -1, STR_ASCII|STR_TERMINATE);
+
+ send_mailslot(False,BROWSE_MAILSLOT, outbuf, PTR_DIFF(p,outbuf),
+ from_name, 0x0, to_name, to_type, to_ip, subrec->myip,
+ DGRAM_PORT);
+}
+
+/****************************************************************************
+ Broadcast a LanMan announcement.
+**************************************************************************/
+
+static void send_lm_announcement(struct subnet_record *subrec, int announce_type,
+ char *from_name, char *to_name, int to_type, struct in_addr to_ip,
+ time_t announce_interval,
+ char *server_name, int server_type, char *server_comment)
+{
+ pstring outbuf;
+ char *p=outbuf;
+
+ memset(outbuf,'\0',sizeof(outbuf));
+
+ SSVAL(p,0,announce_type);
+ SIVAL(p,2,server_type & ~SV_TYPE_LOCAL_LIST_ONLY);
+ SCVAL(p,6,lp_major_announce_version()); /* Major version. */
+ SCVAL(p,7,lp_minor_announce_version()); /* Minor version. */
+ SSVAL(p,8,announce_interval); /* In seconds - according to spec. */
+
+ p += 10;
+ p += push_string(NULL, p, server_name, 15, STR_ASCII|STR_UPPER|STR_TERMINATE);
+ p += push_string(NULL, p, server_comment, sizeof(pstring)-15, STR_ASCII|STR_UPPER|STR_TERMINATE);
+
+ send_mailslot(False,LANMAN_MAILSLOT, outbuf, PTR_DIFF(p,outbuf),
+ from_name, 0x0, to_name, to_type, to_ip, subrec->myip,
+ DGRAM_PORT);
+}
+
+/****************************************************************************
+ We are a local master browser. Announce this to WORKGROUP<1e>.
+****************************************************************************/
+
+static void send_local_master_announcement(struct subnet_record *subrec, struct work_record *work,
+ struct server_record *servrec)
+{
+ /* Ensure we don't have the prohibited bit set. */
+ uint32 type = servrec->serv.type & ~SV_TYPE_LOCAL_LIST_ONLY;
+
+ DEBUG(3,("send_local_master_announcement: type %x for name %s on subnet %s for workgroup %s\n",
+ type, global_myname(), subrec->subnet_name, work->work_group));
+
+ send_announcement(subrec, ANN_LocalMasterAnnouncement,
+ global_myname(), /* From nbt name. */
+ work->work_group, 0x1e, /* To nbt name. */
+ subrec->bcast_ip, /* To ip. */
+ work->announce_interval, /* Time until next announce. */
+ global_myname(), /* Name to announce. */
+ type, /* Type field. */
+ servrec->serv.comment);
+}
+
+/****************************************************************************
+ Announce the workgroup WORKGROUP to MSBROWSE<01>.
+****************************************************************************/
+
+static void send_workgroup_announcement(struct subnet_record *subrec, struct work_record *work)
+{
+ DEBUG(3,("send_workgroup_announcement: on subnet %s for workgroup %s\n",
+ subrec->subnet_name, work->work_group));
+
+ send_announcement(subrec, ANN_DomainAnnouncement,
+ global_myname(), /* From nbt name. */
+ MSBROWSE, 0x1, /* To nbt name. */
+ subrec->bcast_ip, /* To ip. */
+ work->announce_interval, /* Time until next announce. */
+ work->work_group, /* Name to announce. */
+ SV_TYPE_DOMAIN_ENUM|SV_TYPE_NT, /* workgroup announce flags. */
+ global_myname()); /* From name as comment. */
+}
+
+/****************************************************************************
+ Announce the given host to WORKGROUP<1d>.
+****************************************************************************/
+
+static void send_host_announcement(struct subnet_record *subrec, struct work_record *work,
+ struct server_record *servrec)
+{
+ /* Ensure we don't have the prohibited bits set. */
+ uint32 type = servrec->serv.type & ~SV_TYPE_LOCAL_LIST_ONLY;
+
+ DEBUG(3,("send_host_announcement: type %x for host %s on subnet %s for workgroup %s\n",
+ type, servrec->serv.name, subrec->subnet_name, work->work_group));
+
+ send_announcement(subrec, ANN_HostAnnouncement,
+ servrec->serv.name, /* From nbt name. */
+ work->work_group, 0x1d, /* To nbt name. */
+ subrec->bcast_ip, /* To ip. */
+ work->announce_interval, /* Time until next announce. */
+ servrec->serv.name, /* Name to announce. */
+ type, /* Type field. */
+ servrec->serv.comment);
+}
+
+/****************************************************************************
+ Announce the given LanMan host
+****************************************************************************/
+
+static void send_lm_host_announcement(struct subnet_record *subrec, struct work_record *work,
+ struct server_record *servrec, int lm_interval)
+{
+ /* Ensure we don't have the prohibited bits set. */
+ uint32 type = servrec->serv.type & ~SV_TYPE_LOCAL_LIST_ONLY;
+
+ DEBUG(3,("send_lm_host_announcement: type %x for host %s on subnet %s for workgroup %s, ttl: %d\n",
+ type, servrec->serv.name, subrec->subnet_name, work->work_group, lm_interval));
+
+ send_lm_announcement(subrec, ANN_HostAnnouncement,
+ servrec->serv.name, /* From nbt name. */
+ work->work_group, 0x00, /* To nbt name. */
+ subrec->bcast_ip, /* To ip. */
+ lm_interval, /* Time until next announce. */
+ servrec->serv.name, /* Name to announce (fstring not netbios name struct). */
+ type, /* Type field. */
+ servrec->serv.comment);
+}
+
+/****************************************************************************
+ Announce a server record.
+ ****************************************************************************/
+
+static void announce_server(struct subnet_record *subrec, struct work_record *work,
+ struct server_record *servrec)
+{
+ /* Only do domain announcements if we are a master and it's
+ our primary name we're being asked to announce. */
+
+ if (AM_LOCAL_MASTER_BROWSER(work) && strequal(global_myname(),servrec->serv.name)) {
+ send_local_master_announcement(subrec, work, servrec);
+ send_workgroup_announcement(subrec, work);
+ } else {
+ send_host_announcement(subrec, work, servrec);
+ }
+}
+
+/****************************************************************************
+ Go through all my registered names on all broadcast subnets and announce
+ them if the timeout requires it.
+ **************************************************************************/
+
+void announce_my_server_names(time_t t)
+{
+ struct subnet_record *subrec;
+
+ for (subrec = FIRST_SUBNET; subrec; subrec = NEXT_SUBNET_EXCLUDING_UNICAST(subrec)) {
+ struct work_record *work = find_workgroup_on_subnet(subrec, lp_workgroup());
+
+ if(work) {
+ struct server_record *servrec;
+
+ if (work->needannounce) {
+ /* Drop back to a max 3 minute announce. This is to prevent a
+ single lost packet from breaking things for too long. */
+
+ work->announce_interval = MIN(work->announce_interval,
+ CHECK_TIME_MIN_HOST_ANNCE*60);
+ work->lastannounce_time = t - (work->announce_interval+1);
+ work->needannounce = False;
+ }
+
+ /* Announce every minute at first then progress to every 12 mins */
+ if ((t - work->lastannounce_time) < work->announce_interval)
+ continue;
+
+ if (work->announce_interval < (CHECK_TIME_MAX_HOST_ANNCE * 60))
+ work->announce_interval += 60;
+
+ work->lastannounce_time = t;
+
+ for (servrec = work->serverlist; servrec; servrec = servrec->next) {
+ if (is_myname(servrec->serv.name))
+ announce_server(subrec, work, servrec);
+ }
+ } /* if work */
+ } /* for subrec */
+}
+
+/****************************************************************************
+ Go through all my registered names on all broadcast subnets and announce
+ them as a LanMan server if the timeout requires it.
+**************************************************************************/
+
+void announce_my_lm_server_names(time_t t)
+{
+ struct subnet_record *subrec;
+ static time_t last_lm_announce_time=0;
+ int announce_interval = lp_lm_interval();
+ int lm_announce = lp_lm_announce();
+
+ if ((announce_interval <= 0) || (lm_announce <= 0)) {
+ /* user absolutely does not want LM announcements to be sent. */
+ return;
+ }
+
+ if ((lm_announce >= 2) && (!found_lm_clients)) {
+ /* has been set to 2 (Auto) but no LM clients detected (yet). */
+ return;
+ }
+
+ /* Otherwise: must have been set to 1 (Yes), or LM clients *have*
+ been detected. */
+
+ for (subrec = FIRST_SUBNET; subrec; subrec = NEXT_SUBNET_EXCLUDING_UNICAST(subrec)) {
+ struct work_record *work = find_workgroup_on_subnet(subrec, lp_workgroup());
+
+ if(work) {
+ struct server_record *servrec;
+
+ if (last_lm_announce_time && ((t - last_lm_announce_time) < announce_interval ))
+ continue;
+
+ last_lm_announce_time = t;
+
+ for (servrec = work->serverlist; servrec; servrec = servrec->next) {
+ if (is_myname(servrec->serv.name))
+ /* skipping equivalent of announce_server() */
+ send_lm_host_announcement(subrec, work, servrec, announce_interval);
+ }
+ } /* if work */
+ } /* for subrec */
+}
+
+/* Announce timer. Moved into global static so it can be reset
+ when a machine becomes a local master browser. */
+static time_t announce_timer_last=0;
+
+/****************************************************************************
+ Reset the announce_timer so that a local master browser announce will be done
+ immediately.
+ ****************************************************************************/
+
+void reset_announce_timer(void)
+{
+ announce_timer_last = time(NULL) - (CHECK_TIME_MST_ANNOUNCE * 60);
+}
+
+/****************************************************************************
+ Announce myself as a local master browser to a domain master browser.
+ **************************************************************************/
+
+void announce_myself_to_domain_master_browser(time_t t)
+{
+ struct subnet_record *subrec;
+ struct work_record *work;
+
+ if(!we_are_a_wins_client()) {
+ DEBUG(10,("announce_myself_to_domain_master_browser: no unicast subnet, ignoring.\n"));
+ return;
+ }
+
+ if (!announce_timer_last)
+ announce_timer_last = t;
+
+ if ((t-announce_timer_last) < (CHECK_TIME_MST_ANNOUNCE * 60)) {
+ DEBUG(10,("announce_myself_to_domain_master_browser: t (%d) - last(%d) < %d\n",
+ (int)t, (int)announce_timer_last,
+ CHECK_TIME_MST_ANNOUNCE * 60 ));
+ return;
+ }
+
+ announce_timer_last = t;
+
+ /* Look over all our broadcast subnets to see if any of them
+ has the state set as local master browser. */
+
+ for (subrec = FIRST_SUBNET; subrec; subrec = NEXT_SUBNET_EXCLUDING_UNICAST(subrec)) {
+ for (work = subrec->workgrouplist; work; work = work->next) {
+ if (AM_LOCAL_MASTER_BROWSER(work)) {
+ DEBUG(4,( "announce_myself_to_domain_master_browser: I am a local master browser for \
+workgroup %s on subnet %s\n", work->work_group, subrec->subnet_name));
+
+ /* Look in nmbd_browsersync.c for the rest of this code. */
+ announce_and_sync_with_domain_master_browser(subrec, work);
+ }
+ }
+ }
+}
+
+/****************************************************************************
+Announce all samba's server entries as 'gone'.
+This must *only* be called on shutdown.
+****************************************************************************/
+
+void announce_my_servers_removed(void)
+{
+ int announce_interval = lp_lm_interval();
+ int lm_announce = lp_lm_announce();
+ struct subnet_record *subrec;
+
+ for (subrec = FIRST_SUBNET; subrec; subrec = NEXT_SUBNET_EXCLUDING_UNICAST(subrec)) {
+ struct work_record *work;
+ for (work = subrec->workgrouplist; work; work = work->next) {
+ struct server_record *servrec;
+
+ work->announce_interval = 0;
+ for (servrec = work->serverlist; servrec; servrec = servrec->next) {
+ if (!is_myname(servrec->serv.name))
+ continue;
+ servrec->serv.type = 0;
+ if(AM_LOCAL_MASTER_BROWSER(work))
+ send_local_master_announcement(subrec, work, servrec);
+ send_host_announcement(subrec, work, servrec);
+
+ if ((announce_interval <= 0) || (lm_announce <= 0)) {
+ /* user absolutely does not want LM announcements to be sent. */
+ continue;
+ }
+
+ if ((lm_announce >= 2) && (!found_lm_clients)) {
+ /* has been set to 2 (Auto) but no LM clients detected (yet). */
+ continue;
+ }
+
+ /*
+ * lm announce was set or we have seen lm announcements, so do
+ * a lm announcement of host removed.
+ */
+
+ send_lm_host_announcement(subrec, work, servrec, 0);
+ }
+ }
+ }
+}
+
+/****************************************************************************
+ Do all the "remote" announcements. These are used to put ourselves
+ on a remote browse list. They are done blind, no checking is done to
+ see if there is actually a local master browser at the other end.
+ **************************************************************************/
+
+void announce_remote(time_t t)
+{
+ char *s;
+ const char *ptr;
+ static time_t last_time = 0;
+ pstring s2;
+ struct in_addr addr;
+ char *comment;
+ int stype = lp_default_server_announce();
+
+ if (last_time && (t < (last_time + REMOTE_ANNOUNCE_INTERVAL)))
+ return;
+
+ last_time = t;
+
+ s = lp_remote_announce();
+ if (!*s)
+ return;
+
+ comment = string_truncate(lp_serverstring(), MAX_SERVER_STRING_LENGTH);
+
+ for (ptr=s; next_token(&ptr,s2,NULL,sizeof(s2)); ) {
+ /* The entries are of the form a.b.c.d/WORKGROUP with
+ WORKGROUP being optional */
+ const char *wgroup;
+ char *pwgroup;
+ int i;
+
+ pwgroup = strchr_m(s2,'/');
+ if (pwgroup)
+ *pwgroup++ = 0;
+ if (!pwgroup || !*pwgroup)
+ wgroup = lp_workgroup();
+ else
+ wgroup = pwgroup;
+
+ addr = *interpret_addr2(s2);
+
+ /* Announce all our names including aliases */
+ /* Give the ip address as the address of our first
+ broadcast subnet. */
+
+ for(i=0; my_netbios_names(i); i++) {
+ const char *name = my_netbios_names(i);
+
+ DEBUG(5,("announce_remote: Doing remote announce for server %s to IP %s.\n",
+ name, inet_ntoa(addr) ));
+
+ send_announcement(FIRST_SUBNET, ANN_HostAnnouncement,
+ name, /* From nbt name. */
+ wgroup, 0x1d, /* To nbt name. */
+ addr, /* To ip. */
+ REMOTE_ANNOUNCE_INTERVAL, /* Time until next announce. */
+ name, /* Name to announce. */
+ stype, /* Type field. */
+ comment);
+ }
+ }
+}
+
+/****************************************************************************
+ Implement the 'remote browse sync' feature Andrew added.
+ These are used to put our browse lists into remote browse lists.
+**************************************************************************/
+
+void browse_sync_remote(time_t t)
+{
+ char *s;
+ const char *ptr;
+ static time_t last_time = 0;
+ pstring s2;
+ struct in_addr addr;
+ struct work_record *work;
+ pstring outbuf;
+ char *p;
+ unstring myname;
+
+ if (last_time && (t < (last_time + REMOTE_ANNOUNCE_INTERVAL)))
+ return;
+
+ last_time = t;
+
+ s = lp_remote_browse_sync();
+ if (!*s)
+ return;
+
+ /*
+ * We only do this if we are the local master browser
+ * for our workgroup on the firsst subnet.
+ */
+
+ if((work = find_workgroup_on_subnet(FIRST_SUBNET, lp_workgroup())) == NULL) {
+ DEBUG(0,("browse_sync_remote: Cannot find workgroup %s on subnet %s\n",
+ lp_workgroup(), FIRST_SUBNET->subnet_name ));
+ return;
+ }
+
+ if(!AM_LOCAL_MASTER_BROWSER(work)) {
+ DEBUG(5,("browse_sync_remote: We can only do this if we are a local master browser \
+for workgroup %s on subnet %s.\n", lp_workgroup(), FIRST_SUBNET->subnet_name ));
+ return;
+ }
+
+ memset(outbuf,'\0',sizeof(outbuf));
+ p = outbuf;
+ SCVAL(p,0,ANN_MasterAnnouncement);
+ p++;
+
+ unstrcpy(myname, global_myname());
+ strupper_m(myname);
+ myname[15]='\0';
+ push_pstring_base(p, myname, outbuf);
+
+ p = skip_string(p,1);
+
+ for (ptr=s; next_token(&ptr,s2,NULL,sizeof(s2)); ) {
+ /* The entries are of the form a.b.c.d */
+ addr = *interpret_addr2(s2);
+
+ DEBUG(5,("announce_remote: Doing remote browse sync announce for server %s to IP %s.\n",
+ global_myname(), inet_ntoa(addr) ));
+
+ send_mailslot(True, BROWSE_MAILSLOT, outbuf,PTR_DIFF(p,outbuf),
+ global_myname(), 0x0, "*", 0x0, addr, FIRST_SUBNET->myip, DGRAM_PORT);
+ }
+}
diff --git a/source/nmbd/nmbd_serverlistdb.c b/source/nmbd/nmbd_serverlistdb.c
new file mode 100644
index 00000000000..e6fad8319d9
--- /dev/null
+++ b/source/nmbd/nmbd_serverlistdb.c
@@ -0,0 +1,422 @@
+/*
+ Unix SMB/CIFS implementation.
+ NBT netbios routines and daemon - version 2
+ Copyright (C) Andrew Tridgell 1994-1998
+ Copyright (C) Luke Kenneth Casson Leighton 1994-1998
+ Copyright (C) Jeremy Allison 1994-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 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"
+
+extern int ClientNMB;
+
+int updatecount = 0;
+
+/*******************************************************************
+ Remove all the servers in a work group.
+ ******************************************************************/
+
+void remove_all_servers(struct work_record *work)
+{
+ struct server_record *servrec;
+ struct server_record *nexts;
+
+ for (servrec = work->serverlist; servrec; servrec = nexts) {
+ DEBUG(7,("remove_all_servers: Removing server %s\n",servrec->serv.name));
+ nexts = servrec->next;
+
+ if (servrec->prev)
+ servrec->prev->next = servrec->next;
+ if (servrec->next)
+ servrec->next->prev = servrec->prev;
+
+ if (work->serverlist == servrec)
+ work->serverlist = servrec->next;
+
+ ZERO_STRUCTP(servrec);
+ SAFE_FREE(servrec);
+ }
+
+ work->subnet->work_changed = True;
+}
+
+/***************************************************************************
+ Add a server into the a workgroup serverlist.
+ **************************************************************************/
+
+static void add_server_to_workgroup(struct work_record *work,
+ struct server_record *servrec)
+{
+ struct server_record *servrec2;
+
+ if (!work->serverlist) {
+ work->serverlist = servrec;
+ servrec->prev = NULL;
+ servrec->next = NULL;
+ return;
+ }
+
+ for (servrec2 = work->serverlist; servrec2->next; servrec2 = servrec2->next)
+ ;
+
+ servrec2->next = servrec;
+ servrec->next = NULL;
+ servrec->prev = servrec2;
+ work->subnet->work_changed = True;
+}
+
+/****************************************************************************
+ Find a server in a server list.
+ **************************************************************************/
+
+struct server_record *find_server_in_workgroup(struct work_record *work, const char *name)
+{
+ struct server_record *ret;
+
+ for (ret = work->serverlist; ret; ret = ret->next) {
+ if (strequal(ret->serv.name,name))
+ return ret;
+ }
+ return NULL;
+}
+
+
+/****************************************************************************
+ Remove a server entry from this workgroup.
+ ****************************************************************************/
+
+void remove_server_from_workgroup(struct work_record *work, struct server_record *servrec)
+{
+ if (servrec->prev)
+ servrec->prev->next = servrec->next;
+ if (servrec->next)
+ servrec->next->prev = servrec->prev;
+
+ if (work->serverlist == servrec)
+ work->serverlist = servrec->next;
+
+ ZERO_STRUCTP(servrec);
+ SAFE_FREE(servrec);
+ work->subnet->work_changed = True;
+}
+
+/****************************************************************************
+ Create a server entry on this workgroup.
+ ****************************************************************************/
+
+struct server_record *create_server_on_workgroup(struct work_record *work,
+ const char *name,int servertype,
+ int ttl, const char *comment)
+{
+ struct server_record *servrec;
+
+ if (name[0] == '*') {
+ DEBUG(7,("create_server_on_workgroup: not adding name starting with '*' (%s)\n",
+ name));
+ return (NULL);
+ }
+
+ if((servrec = find_server_in_workgroup(work, name)) != NULL) {
+ DEBUG(0,("create_server_on_workgroup: Server %s already exists on \
+workgroup %s. This is a bug.\n", name, work->work_group));
+ return NULL;
+ }
+
+ if((servrec = (struct server_record *)malloc(sizeof(*servrec))) == NULL) {
+ DEBUG(0,("create_server_entry_on_workgroup: malloc fail !\n"));
+ return NULL;
+ }
+
+ memset((char *)servrec,'\0',sizeof(*servrec));
+
+ servrec->subnet = work->subnet;
+
+ fstrcpy(servrec->serv.name,name);
+ fstrcpy(servrec->serv.comment,comment);
+ strupper_m(servrec->serv.name);
+ servrec->serv.type = servertype;
+
+ update_server_ttl(servrec, ttl);
+
+ add_server_to_workgroup(work, servrec);
+
+ DEBUG(3,("create_server_on_workgroup: Created server entry %s of type %x (%s) on \
+workgroup %s.\n", name,servertype,comment, work->work_group));
+
+ work->subnet->work_changed = True;
+
+ return(servrec);
+}
+
+/*******************************************************************
+ Update the ttl field of a server record.
+*******************************************************************/
+
+void update_server_ttl(struct server_record *servrec, int ttl)
+{
+ if(ttl > lp_max_ttl())
+ ttl = lp_max_ttl();
+
+ if(is_myname(servrec->serv.name))
+ servrec->death_time = PERMANENT_TTL;
+ else
+ servrec->death_time = (ttl != PERMANENT_TTL) ? time(NULL)+(ttl*3) : PERMANENT_TTL;
+
+ servrec->subnet->work_changed = True;
+}
+
+/*******************************************************************
+ Expire old servers in the serverlist. A time of -1 indicates
+ everybody dies except those with a death_time of PERMANENT_TTL (which is 0).
+ This should only be called from expire_workgroups_and_servers().
+ ******************************************************************/
+
+void expire_servers(struct work_record *work, time_t t)
+{
+ struct server_record *servrec;
+ struct server_record *nexts;
+
+ for (servrec = work->serverlist; servrec; servrec = nexts) {
+ nexts = servrec->next;
+
+ if ((servrec->death_time != PERMANENT_TTL) && ((t == -1) || (servrec->death_time < t))) {
+ DEBUG(3,("expire_old_servers: Removing timed out server %s\n",servrec->serv.name));
+ remove_server_from_workgroup(work, servrec);
+ work->subnet->work_changed = True;
+ }
+ }
+}
+
+/*******************************************************************
+ Decide if we should write out a server record for this server.
+ We return zero if we should not. Check if we've already written
+ out this server record from an earlier subnet.
+******************************************************************/
+
+static uint32 write_this_server_name( struct subnet_record *subrec,
+ struct work_record *work,
+ struct server_record *servrec)
+{
+ struct subnet_record *ssub;
+ struct work_record *iwork;
+
+ /* Go through all the subnets we have already seen. */
+ for (ssub = FIRST_SUBNET; ssub != subrec; ssub = NEXT_SUBNET_INCLUDING_UNICAST(ssub)) {
+ for(iwork = ssub->workgrouplist; iwork; iwork = iwork->next) {
+ if(find_server_in_workgroup( iwork, servrec->serv.name) != NULL) {
+ /*
+ * We have already written out this server record, don't
+ * do it again. This gives precedence to servers we have seen
+ * on the broadcast subnets over servers that may have been
+ * added via a sync on the unicast_subet.
+ *
+ * The correct way to do this is to have a serverlist file
+ * per subnet - this means changes to smbd as well. I may
+ * add this at a later date (JRA).
+ */
+
+ return 0;
+ }
+ }
+ }
+
+ return servrec->serv.type;
+}
+
+/*******************************************************************
+ Decide if we should write out a workgroup record for this workgroup.
+ We return zero if we should not. Don't write out lp_workgroup() (we've
+ already done it) and also don't write out a second workgroup record
+ on the unicast subnet that we've already written out on one of the
+ broadcast subnets.
+******************************************************************/
+
+static uint32 write_this_workgroup_name( struct subnet_record *subrec,
+ struct work_record *work)
+{
+ struct subnet_record *ssub;
+
+ if(strequal(lp_workgroup(), work->work_group))
+ return 0;
+
+ /* This is a workgroup we have seen on a broadcast subnet. All
+ these have the same type. */
+
+ if(subrec != unicast_subnet)
+ return (SV_TYPE_DOMAIN_ENUM|SV_TYPE_NT|SV_TYPE_LOCAL_LIST_ONLY);
+
+ for(ssub = FIRST_SUBNET; ssub; ssub = NEXT_SUBNET_EXCLUDING_UNICAST(ssub)) {
+ /* This is the unicast subnet so check if we've already written out
+ this subnet when we passed over the broadcast subnets. */
+
+ if(find_workgroup_on_subnet( ssub, work->work_group) != NULL)
+ return 0;
+ }
+
+ /* All workgroups on the unicast subnet (except our own, which we
+ have already written out) cannot be local. */
+
+ return (SV_TYPE_DOMAIN_ENUM|SV_TYPE_NT);
+}
+
+/*******************************************************************
+ Write out the browse.dat file.
+ ******************************************************************/
+
+void write_browse_list_entry(XFILE *fp, const char *name, uint32 rec_type,
+ const char *local_master_browser_name, const char *description)
+{
+ fstring tmp;
+
+ slprintf(tmp,sizeof(tmp)-1, "\"%s\"", name);
+ x_fprintf(fp, "%-25s ", tmp);
+ x_fprintf(fp, "%08x ", rec_type);
+ slprintf(tmp, sizeof(tmp)-1, "\"%s\" ", local_master_browser_name);
+ x_fprintf(fp, "%-30s", tmp);
+ x_fprintf(fp, "\"%s\"\n", description);
+}
+
+void write_browse_list(time_t t, BOOL force_write)
+{
+ struct subnet_record *subrec;
+ struct work_record *work;
+ struct server_record *servrec;
+ pstring fname,fnamenew;
+ uint32 stype;
+ int i;
+ XFILE *fp;
+ BOOL list_changed = force_write;
+ static time_t lasttime = 0;
+
+ /* Always dump if we're being told to by a signal. */
+ if(force_write == False) {
+ if (!lasttime)
+ lasttime = t;
+ if (t - lasttime < 5)
+ return;
+ }
+
+ lasttime = t;
+
+ dump_workgroups(force_write);
+
+ for (subrec = FIRST_SUBNET; subrec ; subrec = NEXT_SUBNET_INCLUDING_UNICAST(subrec)) {
+ if(subrec->work_changed) {
+ list_changed = True;
+ break;
+ }
+ }
+
+ if(!list_changed)
+ return;
+
+ updatecount++;
+
+ pstrcpy(fname,lp_lockdir());
+ trim_char(fname,'\0' ,'/');
+ pstrcat(fname,"/");
+ pstrcat(fname,SERVER_LIST);
+ pstrcpy(fnamenew,fname);
+ pstrcat(fnamenew,".");
+
+ fp = x_fopen(fnamenew,O_WRONLY|O_CREAT|O_TRUNC, 0644);
+
+ if (!fp) {
+ DEBUG(0,("write_browse_list: Can't open file %s. Error was %s\n",
+ fnamenew,strerror(errno)));
+ return;
+ }
+
+ /*
+ * Write out a record for our workgroup. Use the record from the first
+ * subnet.
+ */
+
+ if((work = find_workgroup_on_subnet(FIRST_SUBNET, lp_workgroup())) == NULL) {
+ DEBUG(0,("write_browse_list: Fatal error - cannot find my workgroup %s\n",
+ lp_workgroup()));
+ x_fclose(fp);
+ return;
+ }
+
+ write_browse_list_entry(fp, work->work_group,
+ SV_TYPE_DOMAIN_ENUM|SV_TYPE_NT|SV_TYPE_LOCAL_LIST_ONLY,
+ work->local_master_browser_name, work->work_group);
+
+ /*
+ * We need to do something special for our own names.
+ * This is due to the fact that we may be a local master browser on
+ * one of our broadcast subnets, and a domain master on the unicast
+ * subnet. We iterate over the subnets and only write out the name
+ * once.
+ */
+
+ for (i=0; my_netbios_names(i); i++) {
+ stype = 0;
+ for (subrec = FIRST_SUBNET; subrec ; subrec = NEXT_SUBNET_INCLUDING_UNICAST(subrec)) {
+ if((work = find_workgroup_on_subnet( subrec, lp_workgroup() )) == NULL)
+ continue;
+ if((servrec = find_server_in_workgroup( work, my_netbios_names(i))) == NULL)
+ continue;
+
+ stype |= servrec->serv.type;
+ }
+
+ /* Output server details, plus what workgroup they're in. */
+ write_browse_list_entry(fp, my_netbios_names(i), stype,
+ string_truncate(lp_serverstring(), MAX_SERVER_STRING_LENGTH), lp_workgroup());
+ }
+
+ for (subrec = FIRST_SUBNET; subrec ; subrec = NEXT_SUBNET_INCLUDING_UNICAST(subrec)) {
+ subrec->work_changed = False;
+
+ for (work = subrec->workgrouplist; work ; work = work->next) {
+ /* Write out a workgroup record for a workgroup. */
+ uint32 wg_type = write_this_workgroup_name( subrec, work);
+
+ if(wg_type) {
+ write_browse_list_entry(fp, work->work_group, wg_type,
+ work->local_master_browser_name,
+ work->work_group);
+ }
+
+ /* Now write out any server records a workgroup may have. */
+
+ for (servrec = work->serverlist; servrec ; servrec = servrec->next) {
+ uint32 serv_type;
+
+ /* We have already written our names here. */
+ if(is_myname(servrec->serv.name))
+ continue;
+
+ serv_type = write_this_server_name(subrec, work, servrec);
+ if(serv_type) {
+ /* Output server details, plus what workgroup they're in. */
+ write_browse_list_entry(fp, servrec->serv.name, serv_type,
+ servrec->serv.comment, work->work_group);
+ }
+ }
+ }
+ }
+
+ x_fclose(fp);
+ unlink(fname);
+ chmod(fnamenew,0644);
+ rename(fnamenew,fname);
+ DEBUG(3,("write_browse_list: Wrote browse list into file %s\n",fname));
+}
diff --git a/source/nmbd/nmbd_subnetdb.c b/source/nmbd/nmbd_subnetdb.c
new file mode 100644
index 00000000000..02a91f27606
--- /dev/null
+++ b/source/nmbd/nmbd_subnetdb.c
@@ -0,0 +1,344 @@
+/*
+ Unix SMB/CIFS implementation.
+ NBT netbios routines and daemon - version 2
+ Copyright (C) Andrew Tridgell 1994-1998
+ Copyright (C) Luke Kenneth Casson Leighton 1994-1998
+ Copyright (C) Jeremy Allison 1994-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 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.
+
+ Revision History:
+
+*/
+
+#include "includes.h"
+
+extern int ClientNMB;
+extern int ClientDGRAM;
+extern int global_nmb_port;
+
+/* This is the broadcast subnets database. */
+struct subnet_record *subnetlist = NULL;
+
+/* Extra subnets - keep these separate so enumeration code doesn't
+ run onto it by mistake. */
+
+struct subnet_record *unicast_subnet = NULL;
+struct subnet_record *remote_broadcast_subnet = NULL;
+struct subnet_record *wins_server_subnet = NULL;
+
+extern uint16 samba_nb_type; /* Samba's NetBIOS name type. */
+
+/****************************************************************************
+ Add a subnet into the list.
+ **************************************************************************/
+
+static void add_subnet(struct subnet_record *subrec)
+{
+ DLIST_ADD(subnetlist, subrec);
+}
+
+/* ************************************************************************** **
+ * Comparison routine for ordering the splay-tree based namelists assoicated
+ * with each subnet record.
+ *
+ * Input: Item - Pointer to the comparison key.
+ * Node - Pointer to a node the splay tree.
+ *
+ * Output: The return value will be <0 , ==0, or >0 depending upon the
+ * ordinal relationship of the two keys.
+ *
+ * ************************************************************************** **
+ */
+static int namelist_entry_compare( ubi_trItemPtr Item, ubi_trNodePtr Node )
+{
+ struct name_record *NR = (struct name_record *)Node;
+
+ if( DEBUGLVL( 10 ) ) {
+ struct nmb_name *Iname = (struct nmb_name *)Item;
+
+ Debug1( "nmbd_subnetdb:namelist_entry_compare()\n" );
+ Debug1( "%d == memcmp( \"%s\", \"%s\", %d )\n",
+ memcmp( Item, &(NR->name), sizeof(struct nmb_name) ),
+ nmb_namestr(Iname), nmb_namestr(&NR->name), (int)sizeof(struct nmb_name) );
+ }
+
+ return( memcmp( Item, &(NR->name), sizeof(struct nmb_name) ) );
+}
+
+/****************************************************************************
+stop listening on a subnet
+we don't free the record as we don't have proper reference counting for it
+yet and it may be in use by a response record
+ ****************************************************************************/
+
+void close_subnet(struct subnet_record *subrec)
+{
+ DLIST_REMOVE(subnetlist, subrec);
+
+ if (subrec->dgram_sock != -1) {
+ close(subrec->dgram_sock);
+ subrec->dgram_sock = -1;
+ }
+ if (subrec->nmb_sock != -1) {
+ close(subrec->nmb_sock);
+ subrec->nmb_sock = -1;
+ }
+}
+
+/****************************************************************************
+ Create a subnet entry.
+ ****************************************************************************/
+
+static struct subnet_record *make_subnet(const char *name, enum subnet_type type,
+ struct in_addr myip, struct in_addr bcast_ip,
+ struct in_addr mask_ip)
+{
+ struct subnet_record *subrec = NULL;
+ int nmb_sock, dgram_sock;
+
+ /* Check if we are creating a non broadcast subnet - if so don't create
+ sockets. */
+
+ if(type != NORMAL_SUBNET) {
+ nmb_sock = -1;
+ dgram_sock = -1;
+ } else {
+ /*
+ * Attempt to open the sockets on port 137/138 for this interface
+ * and bind them.
+ * Fail the subnet creation if this fails.
+ */
+
+ if((nmb_sock = open_socket_in(SOCK_DGRAM, global_nmb_port,0, myip.s_addr,True)) == -1) {
+ if( DEBUGLVL( 0 ) ) {
+ Debug1( "nmbd_subnetdb:make_subnet()\n" );
+ Debug1( " Failed to open nmb socket on interface %s ", inet_ntoa(myip) );
+ Debug1( "for port %d. ", global_nmb_port );
+ Debug1( "Error was %s\n", strerror(errno) );
+ }
+ return NULL;
+ }
+
+ if((dgram_sock = open_socket_in(SOCK_DGRAM,DGRAM_PORT,3, myip.s_addr,True)) == -1) {
+ if( DEBUGLVL( 0 ) ) {
+ Debug1( "nmbd_subnetdb:make_subnet()\n" );
+ Debug1( " Failed to open dgram socket on interface %s ", inet_ntoa(myip) );
+ Debug1( "for port %d. ", DGRAM_PORT );
+ Debug1( "Error was %s\n", strerror(errno) );
+ }
+ return NULL;
+ }
+
+ /* Make sure we can broadcast from these sockets. */
+ set_socket_options(nmb_sock,"SO_BROADCAST");
+ set_socket_options(dgram_sock,"SO_BROADCAST");
+ }
+
+ subrec = (struct subnet_record *)malloc(sizeof(*subrec));
+ if (!subrec) {
+ DEBUG(0,("make_subnet: malloc fail !\n"));
+ close(nmb_sock);
+ close(dgram_sock);
+ return(NULL);
+ }
+
+ memset( (char *)subrec, '\0', sizeof(*subrec) );
+ (void)ubi_trInitTree( subrec->namelist,
+ namelist_entry_compare,
+ ubi_trOVERWRITE );
+
+ if((subrec->subnet_name = strdup(name)) == NULL) {
+ DEBUG(0,("make_subnet: malloc fail for subnet name !\n"));
+ close(nmb_sock);
+ close(dgram_sock);
+ ZERO_STRUCTP(subrec);
+ SAFE_FREE(subrec);
+ return(NULL);
+ }
+
+ DEBUG(2, ("making subnet name:%s ", name ));
+ DEBUG(2, ("Broadcast address:%s ", inet_ntoa(bcast_ip)));
+ DEBUG(2, ("Subnet mask:%s\n", inet_ntoa(mask_ip)));
+
+ subrec->namelist_changed = False;
+ subrec->work_changed = False;
+
+ subrec->bcast_ip = bcast_ip;
+ subrec->mask_ip = mask_ip;
+ subrec->myip = myip;
+ subrec->type = type;
+ subrec->nmb_sock = nmb_sock;
+ subrec->dgram_sock = dgram_sock;
+
+ return subrec;
+}
+
+/****************************************************************************
+ Create a normal subnet
+**************************************************************************/
+
+struct subnet_record *make_normal_subnet(struct interface *iface)
+{
+ struct subnet_record *subrec;
+
+ subrec = make_subnet(inet_ntoa(iface->ip), NORMAL_SUBNET,
+ iface->ip, iface->bcast, iface->nmask);
+ if (subrec) {
+ add_subnet(subrec);
+ }
+ return subrec;
+}
+
+/****************************************************************************
+ Create subnet entries.
+**************************************************************************/
+
+BOOL create_subnets(void)
+{
+ int num_interfaces = iface_count();
+ int i;
+ struct in_addr unicast_ip, ipzero;
+ extern struct in_addr loopback_ip;
+
+ if(num_interfaces == 0) {
+ DEBUG(0,("create_subnets: No local interfaces !\n"));
+ DEBUG(0,("create_subnets: Waiting for an interface to appear ...\n"));
+ while (iface_count() == 0) {
+ sleep(5);
+ load_interfaces();
+ }
+ }
+
+ num_interfaces = iface_count();
+
+ /*
+ * Create subnets from all the local interfaces and thread them onto
+ * the linked list.
+ */
+
+ for (i = 0 ; i < num_interfaces; i++) {
+ struct interface *iface = get_interface(i);
+
+ /*
+ * We don't want to add a loopback interface, in case
+ * someone has added 127.0.0.1 for smbd, nmbd needs to
+ * ignore it here. JRA.
+ */
+
+ if (ip_equal(iface->ip, loopback_ip)) {
+ DEBUG(2,("create_subnets: Ignoring loopback interface.\n" ));
+ continue;
+ }
+
+ if (!make_normal_subnet(iface))
+ return False;
+ }
+
+ if (lp_we_are_a_wins_server()) {
+ /* Pick the first interface ip address as the WINS server ip. */
+ unicast_ip = *iface_n_ip(0);
+ } else {
+ /* note that we do not set the wins server IP here. We just
+ set it at zero and let the wins registration code cope
+ with getting the IPs right for each packet */
+ zero_ip(&unicast_ip);
+ }
+
+ /*
+ * Create the unicast and remote broadcast subnets.
+ * Don't put these onto the linked list.
+ * The ip address of the unicast subnet is set to be
+ * the WINS server address, if it exists, or ipzero if not.
+ */
+
+ unicast_subnet = make_subnet( "UNICAST_SUBNET", UNICAST_SUBNET,
+ unicast_ip, unicast_ip, unicast_ip);
+
+ zero_ip(&ipzero);
+
+ remote_broadcast_subnet = make_subnet( "REMOTE_BROADCAST_SUBNET",
+ REMOTE_BROADCAST_SUBNET,
+ ipzero, ipzero, ipzero);
+
+ if((unicast_subnet == NULL) || (remote_broadcast_subnet == NULL))
+ return False;
+
+ /*
+ * If we are WINS server, create the WINS_SERVER_SUBNET - don't put on
+ * the linked list.
+ */
+
+ if (lp_we_are_a_wins_server()) {
+ if( (wins_server_subnet = make_subnet( "WINS_SERVER_SUBNET",
+ WINS_SERVER_SUBNET,
+ ipzero, ipzero, ipzero )) == NULL )
+ return False;
+ }
+
+ return True;
+}
+
+/*******************************************************************
+Function to tell us if we can use the unicast subnet.
+******************************************************************/
+
+BOOL we_are_a_wins_client(void)
+{
+ if (wins_srv_count() > 0) {
+ return True;
+ }
+
+ return False;
+}
+
+/*******************************************************************
+Access function used by NEXT_SUBNET_INCLUDING_UNICAST
+******************************************************************/
+
+struct subnet_record *get_next_subnet_maybe_unicast(struct subnet_record *subrec)
+{
+ if(subrec == unicast_subnet)
+ return NULL;
+ else if((subrec->next == NULL) && we_are_a_wins_client())
+ return unicast_subnet;
+ else
+ return subrec->next;
+}
+
+/*******************************************************************
+ Access function used by retransmit_or_expire_response_records() in
+ nmbd_packets.c. Patch from Andrey Alekseyev <fetch@muffin.arcadia.spb.ru>
+ Needed when we need to enumerate all the broadcast, unicast and
+ WINS subnets.
+******************************************************************/
+
+struct subnet_record *get_next_subnet_maybe_unicast_or_wins_server(struct subnet_record *subrec)
+{
+ if(subrec == unicast_subnet) {
+ if(wins_server_subnet)
+ return wins_server_subnet;
+ else
+ return NULL;
+ }
+
+ if(wins_server_subnet && subrec == wins_server_subnet)
+ return NULL;
+
+ if((subrec->next == NULL) && we_are_a_wins_client())
+ return unicast_subnet;
+ else
+ return subrec->next;
+}
diff --git a/source/nmbd/nmbd_synclists.c b/source/nmbd/nmbd_synclists.c
new file mode 100644
index 00000000000..86f1f760fd2
--- /dev/null
+++ b/source/nmbd/nmbd_synclists.c
@@ -0,0 +1,304 @@
+/*
+ Unix SMB/CIFS implementation.
+ NBT netbios routines and daemon - version 2
+ Copyright (C) Andrew Tridgell 1994-1998
+ Copyright (C) Luke Kenneth Casson Leighton 1994-1998
+ Copyright (C) Jeremy Allison 1994-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 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.
+
+*/
+
+/* this file handles asynchronous browse synchronisation requests. The
+ requests are done by forking and putting the result in a file in the
+ locks directory. We do it this way because we don't want nmbd to be
+ blocked waiting for some server to respond on a TCP connection. This
+ also allows us to have more than 1 sync going at once (tridge) */
+
+#include "includes.h"
+
+struct sync_record {
+ struct sync_record *next, *prev;
+ unstring workgroup;
+ unstring server;
+ pstring fname;
+ struct in_addr ip;
+ pid_t pid;
+};
+
+/* a linked list of current sync connections */
+static struct sync_record *syncs;
+
+static XFILE *fp;
+
+/*******************************************************************
+ This is the NetServerEnum callback.
+ Note sname and comment are in UNIX codepage format.
+ ******************************************************************/
+
+static void callback(const char *sname, uint32 stype,
+ const char *comment, void *state)
+{
+ x_fprintf(fp,"\"%s\" %08X \"%s\"\n", sname, stype, comment);
+}
+
+/*******************************************************************
+ Synchronise browse lists with another browse server.
+ Log in on the remote server's SMB port to their IPC$ service,
+ do a NetServerEnum and record the results in fname
+******************************************************************/
+
+static void sync_child(char *name, int nm_type,
+ char *workgroup,
+ struct in_addr ip, BOOL local, BOOL servers,
+ char *fname)
+{
+ extern fstring local_machine;
+ fstring unix_workgroup;
+ static struct cli_state cli;
+ uint32 local_type = local ? SV_TYPE_LOCAL_LIST_ONLY : 0;
+ struct nmb_name called, calling;
+
+ /* W2K DMB's return empty browse lists on port 445. Use 139.
+ * Patch from Andy Levine andyl@epicrealm.com.
+ */
+
+ if (!cli_initialise(&cli) || !cli_set_port(&cli, 139) || !cli_connect(&cli, name, &ip)) {
+ return;
+ }
+
+ make_nmb_name(&calling, local_machine, 0x0);
+ make_nmb_name(&called , name, nm_type);
+
+ if (!cli_session_request(&cli, &calling, &called)) {
+ cli_shutdown(&cli);
+ return;
+ }
+
+ if (!cli_negprot(&cli)) {
+ cli_shutdown(&cli);
+ return;
+ }
+
+ if (!cli_session_setup(&cli, "", "", 1, "", 0, workgroup)) {
+ cli_shutdown(&cli);
+ return;
+ }
+
+ if (!cli_send_tconX(&cli, "IPC$", "IPC", "", 1)) {
+ cli_shutdown(&cli);
+ return;
+ }
+
+ /* All the cli_XX functions take UNIX character set. */
+ fstrcpy(unix_workgroup, cli.server_domain?cli.server_domain:workgroup);
+
+ /* Fetch a workgroup list. */
+ cli_NetServerEnum(&cli, unix_workgroup,
+ local_type|SV_TYPE_DOMAIN_ENUM,
+ callback, NULL);
+
+ /* Now fetch a server list. */
+ if (servers) {
+ fstrcpy(unix_workgroup, workgroup);
+ cli_NetServerEnum(&cli, unix_workgroup,
+ local?SV_TYPE_LOCAL_LIST_ONLY:SV_TYPE_ALL,
+ callback, NULL);
+ }
+
+ cli_shutdown(&cli);
+}
+
+/*******************************************************************
+ initialise a browse sync with another browse server. Log in on the
+ remote server's SMB port to their IPC$ service, do a NetServerEnum
+ and record the results
+******************************************************************/
+
+void sync_browse_lists(struct work_record *work,
+ char *name, int nm_type,
+ struct in_addr ip, BOOL local, BOOL servers)
+{
+ struct sync_record *s;
+ static int counter;
+
+ START_PROFILE(sync_browse_lists);
+ /* Check we're not trying to sync with ourselves. This can
+ happen if we are a domain *and* a local master browser. */
+ if (ismyip(ip)) {
+done:
+ END_PROFILE(sync_browse_lists);
+ return;
+ }
+
+ s = (struct sync_record *)malloc(sizeof(*s));
+ if (!s) goto done;
+
+ ZERO_STRUCTP(s);
+
+ unstrcpy(s->workgroup, work->work_group);
+ unstrcpy(s->server, name);
+ s->ip = ip;
+
+ slprintf(s->fname, sizeof(pstring)-1,
+ "%s/sync.%d", lp_lockdir(), counter++);
+ all_string_sub(s->fname,"//", "/", 0);
+
+ DLIST_ADD(syncs, s);
+
+ /* the parent forks and returns, leaving the child to do the
+ actual sync and call END_PROFILE*/
+ CatchChild();
+ if ((s->pid = sys_fork())) return;
+
+ BlockSignals( False, SIGTERM );
+
+ DEBUG(2,("Initiating browse sync for %s to %s(%s)\n",
+ work->work_group, name, inet_ntoa(ip)));
+
+ fp = x_fopen(s->fname,O_WRONLY|O_CREAT|O_TRUNC, 0644);
+ if (!fp) {
+ END_PROFILE(sync_browse_lists);
+ _exit(1);
+ }
+
+ sync_child(name, nm_type, work->work_group, ip, local, servers,
+ s->fname);
+
+ x_fclose(fp);
+ END_PROFILE(sync_browse_lists);
+ _exit(0);
+}
+
+/**********************************************************************
+ Handle one line from a completed sync file.
+ **********************************************************************/
+
+static void complete_one(struct sync_record *s,
+ char *sname, uint32 stype, char *comment)
+{
+ struct work_record *work;
+ struct server_record *servrec;
+
+ stype &= ~SV_TYPE_LOCAL_LIST_ONLY;
+
+ if (stype & SV_TYPE_DOMAIN_ENUM) {
+ /* See if we can find the workgroup on this subnet. */
+ if((work=find_workgroup_on_subnet(unicast_subnet, sname))) {
+ /* We already know about this workgroup -
+ update the ttl. */
+ update_workgroup_ttl(work,lp_max_ttl());
+ } else {
+ /* Create the workgroup on the subnet. */
+ work = create_workgroup_on_subnet(unicast_subnet,
+ sname, lp_max_ttl());
+ if (work) {
+ /* remember who the master is */
+ unstrcpy(work->local_master_browser_name, comment);
+ }
+ }
+ return;
+ }
+
+ work = find_workgroup_on_subnet(unicast_subnet, s->workgroup);
+ if (!work) {
+ DEBUG(3,("workgroup %s doesn't exist on unicast subnet?\n",
+ s->workgroup));
+ return;
+ }
+
+ if ((servrec = find_server_in_workgroup( work, sname))) {
+ /* Check that this is not a locally known
+ server - if so ignore the entry. */
+ if(!(servrec->serv.type & SV_TYPE_LOCAL_LIST_ONLY)) {
+ /* We already know about this server - update
+ the ttl. */
+ update_server_ttl(servrec, lp_max_ttl());
+ /* Update the type. */
+ servrec->serv.type = stype;
+ }
+ return;
+ }
+
+ /* Create the server in the workgroup. */
+ create_server_on_workgroup(work, sname,stype, lp_max_ttl(), comment);
+}
+
+/**********************************************************************
+ Read the completed sync info.
+**********************************************************************/
+
+static void complete_sync(struct sync_record *s)
+{
+ XFILE *f;
+ unstring server, type_str;
+ unsigned type;
+ pstring comment;
+ pstring line;
+ const char *ptr;
+ int count=0;
+
+ f = x_fopen(s->fname,O_RDONLY, 0);
+
+ if (!f)
+ return;
+
+ while (!x_feof(f)) {
+
+ if (!fgets_slash(line,sizeof(pstring),f))
+ continue;
+
+ ptr = line;
+
+ if (!next_token(&ptr,server,NULL,sizeof(server)) ||
+ !next_token(&ptr,type_str,NULL, sizeof(type_str)) ||
+ !next_token(&ptr,comment,NULL, sizeof(comment))) {
+ continue;
+ }
+
+ sscanf(type_str, "%X", &type);
+
+ complete_one(s, server, type, comment);
+
+ count++;
+ }
+
+ x_fclose(f);
+
+ unlink(s->fname);
+
+ DEBUG(2,("sync with %s(%s) for workgroup %s completed (%d records)\n",
+ s->server, inet_ntoa(s->ip), s->workgroup, count));
+}
+
+/**********************************************************************
+ Check for completion of any of the child processes.
+**********************************************************************/
+
+void sync_check_completion(void)
+{
+ struct sync_record *s, *next;
+
+ for (s=syncs;s;s=next) {
+ next = s->next;
+ if (!process_exists(s->pid)) {
+ /* it has completed - grab the info */
+ complete_sync(s);
+ DLIST_REMOVE(syncs, s);
+ ZERO_STRUCTP(s);
+ SAFE_FREE(s);
+ }
+ }
+}
diff --git a/source/nmbd/nmbd_winsproxy.c b/source/nmbd/nmbd_winsproxy.c
new file mode 100644
index 00000000000..cce168adb2f
--- /dev/null
+++ b/source/nmbd/nmbd_winsproxy.c
@@ -0,0 +1,213 @@
+/*
+ Unix SMB/CIFS implementation.
+ NBT netbios routines and daemon - version 2
+
+ Copyright (C) Jeremy Allison 1994-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 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"
+
+/****************************************************************************
+Function called when the name lookup succeeded.
+****************************************************************************/
+
+static void wins_proxy_name_query_request_success( struct subnet_record *subrec,
+ struct userdata_struct *userdata,
+ struct nmb_name *nmbname, struct in_addr ip, struct res_rec *rrec)
+{
+ unstring name;
+ struct packet_struct *original_packet;
+ struct subnet_record *orig_broadcast_subnet;
+ struct name_record *namerec;
+ uint16 nb_flags;
+ int num_ips;
+ int i;
+ int ttl = 3600; /* By default one hour in the cache. */
+ struct in_addr *iplist;
+
+ /* Extract the original packet and the original broadcast subnet from
+ the userdata. */
+
+ memcpy( (char *)&orig_broadcast_subnet, userdata->data, sizeof(struct subnet_record *) );
+ memcpy( (char *)&original_packet, &userdata->data[sizeof(struct subnet_record *)],
+ sizeof(struct packet_struct *) );
+
+ nb_flags = get_nb_flags( rrec->rdata );
+
+ num_ips = rrec->rdlength / 6;
+ if(num_ips == 0) {
+ DEBUG(0,("wins_proxy_name_query_request_success: Invalid number of IP records (0) \
+returned for name %s.\n", nmb_namestr(nmbname) ));
+ return;
+ }
+
+ if(num_ips == 1) {
+ iplist = &ip;
+ } else {
+ if((iplist = (struct in_addr *)malloc( num_ips * sizeof(struct in_addr) )) == NULL) {
+ DEBUG(0,("wins_proxy_name_query_request_success: malloc fail !\n"));
+ return;
+ }
+
+ for(i = 0; i < num_ips; i++)
+ putip( (char *)&iplist[i], (char *)&rrec->rdata[ (i*6) + 2]);
+ }
+
+ /* Add the queried name to the original subnet as a WINS_PROXY_NAME. */
+
+ if(rrec == PERMANENT_TTL)
+ ttl = lp_max_ttl();
+
+ pull_ascii_nstring(name, sizeof(name), nmbname->name);
+ namerec = add_name_to_subnet( orig_broadcast_subnet, name,
+ nmbname->name_type, nb_flags, ttl,
+ WINS_PROXY_NAME, num_ips, iplist );
+
+ if(iplist != &ip)
+ SAFE_FREE(iplist);
+
+ /*
+ * Check that none of the IP addresses we are returning is on the
+ * same broadcast subnet as the original requesting packet. If it
+ * is then don't reply (although we still need to add the name
+ * to the cache) as the actual machine will be replying also
+ * and we don't want two replies to a broadcast query.
+ */
+
+ if(namerec && original_packet->packet.nmb.header.nm_flags.bcast) {
+ for( i = 0; i < namerec->data.num_ips; i++) {
+ if( same_net( namerec->data.ip[i], orig_broadcast_subnet->myip,
+ orig_broadcast_subnet->mask_ip ) ) {
+ DEBUG( 5, ( "wins_proxy_name_query_request_success: name %s is a WINS \
+proxy name and is also on the same subnet (%s) as the requestor. \
+Not replying.\n", nmb_namestr(&namerec->name), orig_broadcast_subnet->subnet_name ) );
+ return;
+ }
+ }
+ }
+
+ /* Finally reply to the original name query. */
+ reply_netbios_packet(original_packet, /* Packet to reply to. */
+ 0, /* Result code. */
+ NMB_QUERY, /* nmbd type code. */
+ NMB_NAME_QUERY_OPCODE, /* opcode. */
+ ttl, /* ttl. */
+ rrec->rdata, /* data to send. */
+ rrec->rdlength); /* data length. */
+}
+
+/****************************************************************************
+Function called when the name lookup failed.
+****************************************************************************/
+
+static void wins_proxy_name_query_request_fail(struct subnet_record *subrec,
+ struct response_record *rrec,
+ struct nmb_name *question_name, int fail_code)
+{
+ DEBUG(4,("wins_proxy_name_query_request_fail: WINS server returned error code %d for lookup \
+of name %s.\n", fail_code, nmb_namestr(question_name) ));
+}
+
+/****************************************************************************
+Function to make a deep copy of the userdata we will need when the WINS
+proxy query returns.
+****************************************************************************/
+
+static struct userdata_struct *wins_proxy_userdata_copy_fn(struct userdata_struct *userdata)
+{
+ struct packet_struct *p, *copy_of_p;
+ struct userdata_struct *new_userdata = (struct userdata_struct *)malloc( userdata->userdata_len );
+
+ if(new_userdata == NULL)
+ return NULL;
+
+ new_userdata->copy_fn = userdata->copy_fn;
+ new_userdata->free_fn = userdata->free_fn;
+ new_userdata->userdata_len = userdata->userdata_len;
+
+ /* Copy the subnet_record pointer. */
+ memcpy( new_userdata->data, userdata->data, sizeof(struct subnet_record *) );
+
+ /* Extract the pointer to the packet struct */
+ memcpy((char *)&p, &userdata->data[sizeof(struct subnet_record *)], sizeof(struct packet_struct *) );
+
+ /* Do a deep copy of the packet. */
+ if((copy_of_p = copy_packet(p)) == NULL) {
+ SAFE_FREE(new_userdata);
+ return NULL;
+ }
+
+ /* Lock the copy. */
+ copy_of_p->locked = True;
+
+ memcpy( &new_userdata->data[sizeof(struct subnet_record *)], (char *)&copy_of_p,
+ sizeof(struct packet_struct *) );
+
+ return new_userdata;
+}
+
+/****************************************************************************
+Function to free the deep copy of the userdata we used when the WINS
+proxy query returned.
+****************************************************************************/
+
+static void wins_proxy_userdata_free_fn(struct userdata_struct *userdata)
+{
+ struct packet_struct *p;
+
+ /* Extract the pointer to the packet struct */
+ memcpy((char *)&p, &userdata->data[sizeof(struct subnet_record *)],
+ sizeof(struct packet_struct *));
+
+ /* Unlock the packet. */
+ p->locked = False;
+
+ free_packet(p);
+ ZERO_STRUCTP(userdata);
+ SAFE_FREE(userdata);
+}
+
+/****************************************************************************
+ Make a WINS query on behalf of a broadcast client name query request.
+****************************************************************************/
+
+void make_wins_proxy_name_query_request( struct subnet_record *subrec,
+ struct packet_struct *incoming_packet,
+ struct nmb_name *question_name)
+{
+ long *ud[(sizeof(struct userdata_struct) + sizeof(struct subrec *) +
+ sizeof(struct packet_struct *))/sizeof(long *) + 1];
+ struct userdata_struct *userdata = (struct userdata_struct *)ud;
+ unstring qname;
+
+ memset(ud, '\0', sizeof(ud));
+
+ userdata->copy_fn = wins_proxy_userdata_copy_fn;
+ userdata->free_fn = wins_proxy_userdata_free_fn;
+ userdata->userdata_len = sizeof(ud);
+ memcpy( userdata->data, (char *)&subrec, sizeof(struct subnet_record *));
+ memcpy( &userdata->data[sizeof(struct subnet_record *)], (char *)&incoming_packet,
+ sizeof(struct packet_struct *));
+
+ /* Now use the unicast subnet to query the name with the WINS server. */
+ pull_ascii_nstring(qname, sizeof(qname), question_name->name);
+ query_name( unicast_subnet, qname, question_name->name_type,
+ wins_proxy_name_query_request_success,
+ wins_proxy_name_query_request_fail,
+ userdata);
+}
diff --git a/source/nmbd/nmbd_winsserver.c b/source/nmbd/nmbd_winsserver.c
new file mode 100644
index 00000000000..0f0190adb61
--- /dev/null
+++ b/source/nmbd/nmbd_winsserver.c
@@ -0,0 +1,2015 @@
+/*
+ Unix SMB/CIFS implementation.
+ NBT netbios routines and daemon - version 2
+
+ Copyright (C) Jeremy Allison 1994-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 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"
+
+#define WINS_LIST "wins.dat"
+#define WINS_VERSION 1
+
+/****************************************************************************
+ Change the wins owner address in the record.
+*****************************************************************************/
+
+static void update_wins_owner(struct name_record *namerec, struct in_addr wins_ip)
+{
+ if (namerec==NULL)
+ return;
+ namerec->data.wins_ip=wins_ip;
+}
+
+/****************************************************************************
+ Create the wins flags based on the nb flags and the input value.
+*****************************************************************************/
+
+static void update_wins_flag(struct name_record *namerec, int flags)
+{
+ if (namerec==NULL)
+ return;
+
+ namerec->data.wins_flags=0x0;
+
+ /* if it's a group, it can be a normal or a special one */
+ if (namerec->data.nb_flags & NB_GROUP) {
+ if (namerec->name.name_type==0x1C)
+ namerec->data.wins_flags|=WINS_SGROUP;
+ else
+ if (namerec->data.num_ips>1)
+ namerec->data.wins_flags|=WINS_SGROUP;
+ else
+ namerec->data.wins_flags|=WINS_NGROUP;
+ } else {
+ /* can be unique or multi-homed */
+ if (namerec->data.num_ips>1)
+ namerec->data.wins_flags|=WINS_MHOMED;
+ else
+ namerec->data.wins_flags|=WINS_UNIQUE;
+ }
+
+ /* the node type are the same bits */
+ namerec->data.wins_flags|=namerec->data.nb_flags&NB_NODETYPEMASK;
+
+ /* the static bit is elsewhere */
+ if (namerec->data.death_time == PERMANENT_TTL)
+ namerec->data.wins_flags|=WINS_STATIC;
+
+ /* and add the given bits */
+ namerec->data.wins_flags|=flags;
+
+ DEBUG(8,("update_wins_flag: nbflags: 0x%x, ttl: 0x%d, flags: 0x%x, winsflags: 0x%x\n",
+ namerec->data.nb_flags, (int)namerec->data.death_time, flags, namerec->data.wins_flags));
+}
+
+/****************************************************************************
+ Return the general ID value and increase it if requested.
+*****************************************************************************/
+
+static void get_global_id_and_update(SMB_BIG_UINT *current_id, BOOL update)
+{
+ /*
+ * it's kept as a static here, to prevent people from messing
+ * with the value directly
+ */
+
+ static SMB_BIG_UINT general_id = 1;
+
+ DEBUG(5,("get_global_id_and_update: updating version ID: %d\n", (int)general_id));
+
+ *current_id = general_id;
+
+ if (update)
+ general_id++;
+}
+
+/****************************************************************************
+ Possibly call the WINS hook external program when a WINS change is made.
+*****************************************************************************/
+
+static void wins_hook(const char *operation, struct name_record *namerec, int ttl)
+{
+ pstring command;
+ char *cmd = lp_wins_hook();
+ char *p, *namestr;
+ int i;
+
+ if (!cmd || !*cmd) return;
+
+ for (p=namerec->name.name; *p; p++) {
+ if (!(isalnum((int)*p) || strchr_m("._-",*p))) {
+ DEBUG(3,("not calling wins hook for invalid name %s\n", nmb_namestr(&namerec->name)));
+ return;
+ }
+ }
+
+ /* Use the name without the nametype (and scope) appended */
+
+ namestr = nmb_namestr(&namerec->name);
+ if ((p = strchr(namestr, '<')))
+ *p = 0;
+
+ p = command;
+ p += slprintf(p, sizeof(command)-1, "%s %s %s %02x %d",
+ cmd,
+ operation,
+ namestr,
+ namerec->name.name_type,
+ ttl);
+
+ for (i=0;i<namerec->data.num_ips;i++) {
+ p += slprintf(p, sizeof(command) - (p-command) -1, " %s", inet_ntoa(namerec->data.ip[i]));
+ }
+
+ DEBUG(3,("calling wins hook for %s\n", nmb_namestr(&namerec->name)));
+ smbrun(command, NULL);
+}
+
+
+/****************************************************************************
+Determine if this packet should be allocated to the WINS server.
+*****************************************************************************/
+
+BOOL packet_is_for_wins_server(struct packet_struct *packet)
+{
+ struct nmb_packet *nmb = &packet->packet.nmb;
+
+ /* Only unicast packets go to a WINS server. */
+ if((wins_server_subnet == NULL) || (nmb->header.nm_flags.bcast == True)) {
+ DEBUG(10, ("packet_is_for_wins_server: failing WINS test #1.\n"));
+ return False;
+ }
+
+ /* Check for node status requests. */
+ if (nmb->question.question_type != QUESTION_TYPE_NB_QUERY)
+ return False;
+
+ switch(nmb->header.opcode) {
+ /*
+ * A WINS server issues WACKS, not receives them.
+ */
+ case NMB_WACK_OPCODE:
+ DEBUG(10, ("packet_is_for_wins_server: failing WINS test #2 (WACK).\n"));
+ return False;
+ /*
+ * A WINS server only processes registration and
+ * release requests, not responses.
+ */
+ case NMB_NAME_REG_OPCODE:
+ case NMB_NAME_MULTIHOMED_REG_OPCODE:
+ case NMB_NAME_REFRESH_OPCODE_8: /* ambiguity in rfc1002 about which is correct. */
+ case NMB_NAME_REFRESH_OPCODE_9: /* WinNT uses 8 by default. */
+ if(nmb->header.response) {
+ DEBUG(10, ("packet_is_for_wins_server: failing WINS test #3 (response = 1).\n"));
+ return False;
+ }
+ break;
+
+ case NMB_NAME_RELEASE_OPCODE:
+ if(nmb->header.response) {
+ DEBUG(10, ("packet_is_for_wins_server: failing WINS test #4 (response = 1).\n"));
+ return False;
+ }
+ break;
+
+ /*
+ * Only process unicast name queries with rd = 1.
+ */
+ case NMB_NAME_QUERY_OPCODE:
+ if(!nmb->header.response && !nmb->header.nm_flags.recursion_desired) {
+ DEBUG(10, ("packet_is_for_wins_server: failing WINS test #5 (response = 1).\n"));
+ return False;
+ }
+ break;
+ }
+
+ return True;
+}
+
+/****************************************************************************
+Utility function to decide what ttl to give a register/refresh request.
+*****************************************************************************/
+
+static int get_ttl_from_packet(struct nmb_packet *nmb)
+{
+ int ttl = nmb->additional->ttl;
+
+ if(ttl < lp_min_wins_ttl() )
+ ttl = lp_min_wins_ttl();
+
+ if(ttl > lp_max_wins_ttl() )
+ ttl = lp_max_wins_ttl();
+
+ return ttl;
+}
+
+/****************************************************************************
+Load or create the WINS database.
+*****************************************************************************/
+
+BOOL initialise_wins(void)
+{
+ time_t time_now = time(NULL);
+ XFILE *fp;
+ pstring line;
+
+ if(!lp_we_are_a_wins_server())
+ return True;
+
+ add_samba_names_to_subnet(wins_server_subnet);
+
+ if((fp = x_fopen(lock_path(WINS_LIST),O_RDONLY,0)) == NULL) {
+ DEBUG(2,("initialise_wins: Can't open wins database file %s. Error was %s\n",
+ WINS_LIST, strerror(errno) ));
+ return True;
+ }
+
+ while (!x_feof(fp)) {
+ pstring name_str, ip_str, ttl_str, nb_flags_str;
+ unsigned int num_ips;
+ pstring name;
+ struct in_addr *ip_list;
+ int type = 0;
+ int nb_flags;
+ int ttl;
+ const char *ptr;
+ char *p;
+ BOOL got_token;
+ BOOL was_ip;
+ int i;
+ unsigned int hash;
+ int version;
+
+ /* Read a line from the wins.dat file. Strips whitespace
+ from the beginning and end of the line. */
+ if (!fgets_slash(line,sizeof(pstring),fp))
+ continue;
+
+ if (*line == '#')
+ continue;
+
+ if (strncmp(line,"VERSION ", 8) == 0) {
+ if (sscanf(line,"VERSION %d %u", &version, &hash) != 2 ||
+ version != WINS_VERSION) {
+ DEBUG(0,("Discarding invalid wins.dat file [%s]\n",line));
+ x_fclose(fp);
+ return True;
+ }
+ continue;
+ }
+
+ ptr = line;
+
+ /*
+ * Now we handle multiple IP addresses per name we need
+ * to iterate over the line twice. The first time to
+ * determine how many IP addresses there are, the second
+ * time to actually parse them into the ip_list array.
+ */
+
+ if (!next_token(&ptr,name_str,NULL,sizeof(name_str))) {
+ DEBUG(0,("initialise_wins: Failed to parse name when parsing line %s\n", line ));
+ continue;
+ }
+
+ if (!next_token(&ptr,ttl_str,NULL,sizeof(ttl_str))) {
+ DEBUG(0,("initialise_wins: Failed to parse time to live when parsing line %s\n", line ));
+ continue;
+ }
+
+ /*
+ * Determine the number of IP addresses per line.
+ */
+ num_ips = 0;
+ do {
+ got_token = next_token(&ptr,ip_str,NULL,sizeof(ip_str));
+ was_ip = False;
+
+ if(got_token && strchr(ip_str, '.')) {
+ num_ips++;
+ was_ip = True;
+ }
+ } while( got_token && was_ip);
+
+ if(num_ips == 0) {
+ DEBUG(0,("initialise_wins: Missing IP address when parsing line %s\n", line ));
+ continue;
+ }
+
+ if(!got_token) {
+ DEBUG(0,("initialise_wins: Missing nb_flags when parsing line %s\n", line ));
+ continue;
+ }
+
+ /* Allocate the space for the ip_list. */
+ if((ip_list = (struct in_addr *)malloc( num_ips * sizeof(struct in_addr))) == NULL) {
+ DEBUG(0,("initialise_wins: Malloc fail !\n"));
+ return False;
+ }
+
+ /* Reset and re-parse the line. */
+ ptr = line;
+ next_token(&ptr,name_str,NULL,sizeof(name_str));
+ next_token(&ptr,ttl_str,NULL,sizeof(ttl_str));
+ for(i = 0; i < num_ips; i++) {
+ next_token(&ptr, ip_str, NULL, sizeof(ip_str));
+ ip_list[i] = *interpret_addr2(ip_str);
+ }
+ next_token(&ptr,nb_flags_str,NULL, sizeof(nb_flags_str));
+
+ /*
+ * Deal with SELF or REGISTER name encoding. Default is REGISTER
+ * for compatibility with old nmbds.
+ */
+
+ if(nb_flags_str[strlen(nb_flags_str)-1] == 'S') {
+ DEBUG(5,("initialise_wins: Ignoring SELF name %s\n", line));
+ SAFE_FREE(ip_list);
+ continue;
+ }
+
+ if(nb_flags_str[strlen(nb_flags_str)-1] == 'R')
+ nb_flags_str[strlen(nb_flags_str)-1] = '\0';
+
+ /* Netbios name. # divides the name from the type (hex): netbios#xx */
+ pstrcpy(name,name_str);
+
+ if((p = strchr(name,'#')) != NULL) {
+ *p = 0;
+ sscanf(p+1,"%x",&type);
+ }
+
+ /* Decode the netbios flags (hex) and the time-to-live (in seconds). */
+ sscanf(nb_flags_str,"%x",&nb_flags);
+ sscanf(ttl_str,"%d",&ttl);
+
+ /* add all entries that have 60 seconds or more to live */
+ if ((ttl - 60) > time_now || ttl == PERMANENT_TTL) {
+ if(ttl != PERMANENT_TTL)
+ ttl -= time_now;
+
+ DEBUG( 4, ("initialise_wins: add name: %s#%02x ttl = %d first IP %s flags = %2x\n",
+ name, type, ttl, inet_ntoa(ip_list[0]), nb_flags));
+
+ (void)add_name_to_subnet( wins_server_subnet, name, type, nb_flags,
+ ttl, REGISTER_NAME, num_ips, ip_list );
+ } else {
+ DEBUG(4, ("initialise_wins: not adding name (ttl problem) %s#%02x ttl = %d first IP %s flags = %2x\n",
+ name, type, ttl, inet_ntoa(ip_list[0]), nb_flags));
+ }
+
+ SAFE_FREE(ip_list);
+ }
+
+ x_fclose(fp);
+ return True;
+}
+
+/****************************************************************************
+Send a WINS WACK (Wait ACKnowledgement) response.
+**************************************************************************/
+
+static void send_wins_wack_response(int ttl, struct packet_struct *p)
+{
+ struct nmb_packet *nmb = &p->packet.nmb;
+ unsigned char rdata[2];
+
+ rdata[0] = rdata[1] = 0;
+
+ /* Taken from nmblib.c - we need to send back almost
+ identical bytes from the requesting packet header. */
+
+ rdata[0] = (nmb->header.opcode & 0xF) << 3;
+ if (nmb->header.nm_flags.authoritative && nmb->header.response)
+ rdata[0] |= 0x4;
+ if (nmb->header.nm_flags.trunc)
+ rdata[0] |= 0x2;
+ if (nmb->header.nm_flags.recursion_desired)
+ rdata[0] |= 0x1;
+ if (nmb->header.nm_flags.recursion_available && nmb->header.response)
+ rdata[1] |= 0x80;
+ if (nmb->header.nm_flags.bcast)
+ rdata[1] |= 0x10;
+
+ reply_netbios_packet(p, /* Packet to reply to. */
+ 0, /* Result code. */
+ NMB_WAIT_ACK, /* nmbd type code. */
+ NMB_WACK_OPCODE, /* opcode. */
+ ttl, /* ttl. */
+ (char *)rdata, /* data to send. */
+ 2); /* data length. */
+}
+
+/****************************************************************************
+Send a WINS name registration response.
+**************************************************************************/
+
+static void send_wins_name_registration_response(int rcode, int ttl, struct packet_struct *p)
+{
+ struct nmb_packet *nmb = &p->packet.nmb;
+ char rdata[6];
+
+ memcpy(&rdata[0], &nmb->additional->rdata[0], 6);
+
+ reply_netbios_packet(p, /* Packet to reply to. */
+ rcode, /* Result code. */
+ WINS_REG, /* nmbd type code. */
+ NMB_NAME_REG_OPCODE, /* opcode. */
+ ttl, /* ttl. */
+ rdata, /* data to send. */
+ 6); /* data length. */
+}
+
+/***********************************************************************
+ Deal with a name refresh request to a WINS server.
+************************************************************************/
+
+void wins_process_name_refresh_request( struct subnet_record *subrec,
+ struct packet_struct *p )
+{
+ struct nmb_packet *nmb = &p->packet.nmb;
+ struct nmb_name *question = &nmb->question.question_name;
+ BOOL bcast = nmb->header.nm_flags.bcast;
+ uint16 nb_flags = get_nb_flags(nmb->additional->rdata);
+ BOOL group = (nb_flags & NB_GROUP) ? True : False;
+ struct name_record *namerec = NULL;
+ int ttl = get_ttl_from_packet(nmb);
+ struct in_addr from_ip;
+ struct in_addr our_fake_ip = *interpret_addr2("0.0.0.0");
+
+ putip( (char *)&from_ip, &nmb->additional->rdata[2] );
+
+ if(bcast) {
+ /*
+ * We should only get unicast name refresh packets here.
+ * Anyone trying to refresh broadcast should not be going
+ * to a WINS server. Log an error here.
+ */
+ if( DEBUGLVL( 0 ) ) {
+ dbgtext( "wins_process_name_refresh_request: " );
+ dbgtext( "Broadcast name refresh request received " );
+ dbgtext( "for name %s ", nmb_namestr(question) );
+ dbgtext( "from IP %s ", inet_ntoa(from_ip) );
+ dbgtext( "on subnet %s. ", subrec->subnet_name );
+ dbgtext( "Error - Broadcasts should not be sent " );
+ dbgtext( "to a WINS server\n" );
+ }
+ return;
+ }
+
+ if( DEBUGLVL( 3 ) ) {
+ dbgtext( "wins_process_name_refresh_request: " );
+ dbgtext( "Name refresh for name %s IP %s\n",
+ nmb_namestr(question), inet_ntoa(from_ip) );
+ }
+
+ /*
+ * See if the name already exists.
+ * If not, handle it as a name registration and return.
+ */
+ namerec = find_name_on_subnet(subrec, question, FIND_ANY_NAME);
+
+ /*
+ * If this is a refresh request and the name doesn't exist then
+ * treat it like a registration request. This allows us to recover
+ * from errors (tridge)
+ */
+ if(namerec == NULL) {
+ if( DEBUGLVL( 3 ) ) {
+ dbgtext( "wins_process_name_refresh_request: " );
+ dbgtext( "Name refresh for name %s ",
+ nmb_namestr( question ) );
+ dbgtext( "and the name does not exist. Treating " );
+ dbgtext( "as registration.\n" );
+ }
+ wins_process_name_registration_request(subrec,p);
+ return;
+ }
+
+ /*
+ * if the name is present but not active, simply remove it
+ * and treat the refresh request as a registration & return.
+ */
+ if (namerec != NULL && !WINS_STATE_ACTIVE(namerec)) {
+ if( DEBUGLVL( 5 ) ) {
+ dbgtext( "wins_process_name_refresh_request: " );
+ dbgtext( "Name (%s) in WINS ", nmb_namestr(question) );
+ dbgtext( "was not active - removing it.\n" );
+ }
+ remove_name_from_namelist( subrec, namerec );
+ namerec = NULL;
+ wins_process_name_registration_request( subrec, p );
+ return;
+ }
+
+ /*
+ * Check that the group bits for the refreshing name and the
+ * name in our database match. If not, refuse the refresh.
+ * [crh: Why RFS_ERR instead of ACT_ERR? Is this what MS does?]
+ */
+ if( (namerec != NULL) &&
+ ( (group && !NAME_GROUP(namerec))
+ || (!group && NAME_GROUP(namerec)) ) ) {
+ if( DEBUGLVL( 3 ) ) {
+ dbgtext( "wins_process_name_refresh_request: " );
+ dbgtext( "Name %s ", nmb_namestr(question) );
+ dbgtext( "group bit = %s does not match ",
+ group ? "True" : "False" );
+ dbgtext( "group bit in WINS for this name.\n" );
+ }
+ send_wins_name_registration_response(RFS_ERR, 0, p);
+ return;
+ }
+
+ /*
+ * For a unique name check that the person refreshing the name is
+ * one of the registered IP addresses. If not - fail the refresh.
+ * Do the same for group names with a type of 0x1c.
+ * Just return success for unique 0x1d refreshes. For normal group
+ * names update the ttl and return success.
+ */
+ if( (!group || (group && (question->name_type == 0x1c)))
+ && find_ip_in_name_record(namerec, from_ip) ) {
+ /*
+ * Update the ttl.
+ */
+ update_name_ttl(namerec, ttl);
+
+ /*
+ * if the record is a replica:
+ * we take ownership and update the version ID.
+ */
+ if (!ip_equal(namerec->data.wins_ip, our_fake_ip)) {
+ update_wins_owner(namerec, our_fake_ip);
+ get_global_id_and_update(&namerec->data.id, True);
+ }
+
+ send_wins_name_registration_response(0, ttl, p);
+ wins_hook("refresh", namerec, ttl);
+ return;
+ } else if((group && (question->name_type == 0x1c))) {
+ /*
+ * Added by crh for bug #1079.
+ * Fix from Bert Driehuis
+ */
+ if( DEBUGLVL( 3 ) ) {
+ dbgtext( "wins_process_name_refresh_request: " );
+ dbgtext( "Name refresh for name %s, ",
+ nmb_namestr(question) );
+ dbgtext( "but IP address %s ", inet_ntoa(from_ip) );
+ dbgtext( "is not yet associated with " );
+ dbgtext( "that name. Treating as registration.\n" );
+ }
+ wins_process_name_registration_request(subrec,p);
+ return;
+ } else if(group) {
+ /*
+ * Normal groups are all registered with an IP address of
+ * 255.255.255.255 so we can't search for the IP address.
+ */
+ update_name_ttl(namerec, ttl);
+ send_wins_name_registration_response(0, ttl, p);
+ return;
+ } else if(!group && (question->name_type == 0x1d)) {
+ /*
+ * Special name type - just pretend the refresh succeeded.
+ */
+ send_wins_name_registration_response(0, ttl, p);
+ return;
+ } else {
+ /*
+ * Fail the refresh.
+ */
+ if( DEBUGLVL( 3 ) ) {
+ dbgtext( "wins_process_name_refresh_request: " );
+ dbgtext( "Name refresh for name %s with IP %s ",
+ nmb_namestr(question), inet_ntoa(from_ip) );
+ dbgtext( "and is IP is not known to the name.\n" );
+ }
+ send_wins_name_registration_response(RFS_ERR, 0, p);
+ return;
+ }
+}
+
+/***********************************************************************
+ Deal with a name registration request query success to a client that
+ owned the name.
+
+ We have a locked pointer to the original packet stashed away in the
+ userdata pointer. The success here is actually a failure as it means
+ the client we queried wants to keep the name, so we must return
+ a registration failure to the original requestor.
+************************************************************************/
+
+static void wins_register_query_success(struct subnet_record *subrec,
+ struct userdata_struct *userdata,
+ struct nmb_name *question_name,
+ struct in_addr ip,
+ struct res_rec *answers)
+{
+ struct packet_struct *orig_reg_packet;
+
+ memcpy((char *)&orig_reg_packet, userdata->data, sizeof(struct packet_struct *));
+
+ DEBUG(3,("wins_register_query_success: Original client at IP %s still wants the \
+name %s. Rejecting registration request.\n", inet_ntoa(ip), nmb_namestr(question_name) ));
+
+ send_wins_name_registration_response(RFS_ERR, 0, orig_reg_packet);
+
+ orig_reg_packet->locked = False;
+ free_packet(orig_reg_packet);
+}
+
+/***********************************************************************
+ Deal with a name registration request query failure to a client that
+ owned the name.
+
+ We have a locked pointer to the original packet stashed away in the
+ userdata pointer. The failure here is actually a success as it means
+ the client we queried didn't want to keep the name, so we can remove
+ the old name record and then successfully add the new name.
+************************************************************************/
+
+static void wins_register_query_fail(struct subnet_record *subrec,
+ struct response_record *rrec,
+ struct nmb_name *question_name,
+ int rcode)
+{
+ struct userdata_struct *userdata = rrec->userdata;
+ struct packet_struct *orig_reg_packet;
+ struct name_record *namerec = NULL;
+
+ memcpy((char *)&orig_reg_packet, userdata->data, sizeof(struct packet_struct *));
+
+ /*
+ * We want to just add the name, as we now know the original owner
+ * didn't want it. But we can't just do that as an arbitary
+ * amount of time may have taken place between the name query
+ * request and this timeout/error response. So we check that
+ * the name still exists and is in the same state - if so
+ * we remove it and call wins_process_name_registration_request()
+ * as we know it will do the right thing now.
+ */
+
+ namerec = find_name_on_subnet(subrec, question_name, FIND_ANY_NAME);
+
+ if( (namerec != NULL) && (namerec->data.source == REGISTER_NAME) && ip_equal(rrec->packet->ip, *namerec->data.ip) ) {
+ remove_name_from_namelist( subrec, namerec);
+ namerec = NULL;
+ }
+
+ if(namerec == NULL)
+ wins_process_name_registration_request(subrec, orig_reg_packet);
+ else
+ DEBUG(2,("wins_register_query_fail: The state of the WINS database changed between \
+querying for name %s in order to replace it and this reply.\n", nmb_namestr(question_name) ));
+
+ orig_reg_packet->locked = False;
+ free_packet(orig_reg_packet);
+}
+
+/***********************************************************************
+ Deal with a name registration request to a WINS server.
+
+ Use the following pseudocode :
+
+ registering_group
+ |
+ |
+ +--------name exists
+ | |
+ | |
+ | +--- existing name is group
+ | | |
+ | | |
+ | | +--- add name (return).
+ | |
+ | |
+ | +--- exiting name is unique
+ | |
+ | |
+ | +--- query existing owner (return).
+ |
+ |
+ +--------name doesn't exist
+ |
+ |
+ +--- add name (return).
+
+ registering_unique
+ |
+ |
+ +--------name exists
+ | |
+ | |
+ | +--- existing name is group
+ | | |
+ | | |
+ | | +--- fail add (return).
+ | |
+ | |
+ | +--- exiting name is unique
+ | |
+ | |
+ | +--- query existing owner (return).
+ |
+ |
+ +--------name doesn't exist
+ |
+ |
+ +--- add name (return).
+
+ As can be seen from the above, the two cases may be collapsed onto each
+ other with the exception of the case where the name already exists and
+ is a group name. This case we handle with an if statement.
+
+************************************************************************/
+
+void wins_process_name_registration_request(struct subnet_record *subrec,
+ struct packet_struct *p)
+{
+ unstring name;
+ struct nmb_packet *nmb = &p->packet.nmb;
+ struct nmb_name *question = &nmb->question.question_name;
+ BOOL bcast = nmb->header.nm_flags.bcast;
+ uint16 nb_flags = get_nb_flags(nmb->additional->rdata);
+ int ttl = get_ttl_from_packet(nmb);
+ struct name_record *namerec = NULL;
+ struct in_addr from_ip;
+ BOOL registering_group_name = (nb_flags & NB_GROUP) ? True : False;
+ struct in_addr our_fake_ip = *interpret_addr2("0.0.0.0");
+
+ putip((char *)&from_ip,&nmb->additional->rdata[2]);
+
+ if(bcast) {
+ /*
+ * We should only get unicast name registration packets here.
+ * Anyone trying to register broadcast should not be going to a WINS
+ * server. Log an error here.
+ */
+
+ DEBUG(0,("wins_process_name_registration_request: broadcast name registration request \
+received for name %s from IP %s on subnet %s. Error - should not be sent to WINS server\n",
+ nmb_namestr(question), inet_ntoa(from_ip), subrec->subnet_name));
+ return;
+ }
+
+ DEBUG(3,("wins_process_name_registration_request: %s name registration for name %s \
+IP %s\n", registering_group_name ? "Group" : "Unique", nmb_namestr(question), inet_ntoa(from_ip) ));
+
+ /*
+ * See if the name already exists.
+ */
+
+ namerec = find_name_on_subnet(subrec, question, FIND_ANY_NAME);
+
+ /*
+ * if the record exists but NOT in active state,
+ * consider it dead.
+ */
+ if ( (namerec != NULL) && !WINS_STATE_ACTIVE(namerec)) {
+ DEBUG(5,("wins_process_name_registration_request: Name (%s) in WINS was \
+not active - removing it.\n", nmb_namestr(question) ));
+ remove_name_from_namelist( subrec, namerec );
+ namerec = NULL;
+ }
+
+ /*
+ * Deal with the case where the name found was a dns entry.
+ * Remove it as we now have a NetBIOS client registering the
+ * name.
+ */
+
+ if( (namerec != NULL) && ( (namerec->data.source == DNS_NAME) || (namerec->data.source == DNSFAIL_NAME) ) ) {
+ DEBUG(5,("wins_process_name_registration_request: Name (%s) in WINS was \
+a dns lookup - removing it.\n", nmb_namestr(question) ));
+ remove_name_from_namelist( subrec, namerec );
+ namerec = NULL;
+ }
+
+ /*
+ * Reject if the name exists and is not a REGISTER_NAME.
+ * (ie. Don't allow any static names to be overwritten.
+ */
+
+ if((namerec != NULL) && (namerec->data.source != REGISTER_NAME)) {
+ DEBUG( 3, ( "wins_process_name_registration_request: Attempt \
+to register name %s. Name already exists in WINS with source type %d.\n",
+ nmb_namestr(question), namerec->data.source ));
+ send_wins_name_registration_response(RFS_ERR, 0, p);
+ return;
+ }
+
+ /*
+ * Special policy decisions based on MS documentation.
+ * 1). All group names (except names ending in 0x1c) are added as 255.255.255.255.
+ * 2). All unique names ending in 0x1d are ignored, although a positive response is sent.
+ */
+
+ /*
+ * A group name is always added as the local broadcast address, except
+ * for group names ending in 0x1c.
+ * Group names with type 0x1c are registered with individual IP addresses.
+ */
+
+ if(registering_group_name && (question->name_type != 0x1c))
+ from_ip = *interpret_addr2("255.255.255.255");
+
+ /*
+ * Ignore all attempts to register a unique 0x1d name, although return success.
+ */
+
+ if(!registering_group_name && (question->name_type == 0x1d)) {
+ DEBUG(3,("wins_process_name_registration_request: Ignoring request \
+to register name %s from IP %s.\n", nmb_namestr(question), inet_ntoa(p->ip) ));
+ send_wins_name_registration_response(0, ttl, p);
+ return;
+ }
+
+ /*
+ * Next two cases are the 'if statement' mentioned above.
+ */
+
+ if((namerec != NULL) && NAME_GROUP(namerec)) {
+ if(registering_group_name) {
+ /*
+ * If we are adding a group name, the name exists and is also a group entry just add this
+ * IP address to it and update the ttl.
+ */
+
+ DEBUG(3,("wins_process_name_registration_request: Adding IP %s to group name %s.\n",
+ inet_ntoa(from_ip), nmb_namestr(question) ));
+
+ /*
+ * Check the ip address is not already in the group.
+ */
+
+ if(!find_ip_in_name_record(namerec, from_ip)) {
+ add_ip_to_name_record(namerec, from_ip);
+ /* we need to update the record for replication */
+ get_global_id_and_update(&namerec->data.id, True);
+
+ /*
+ * if the record is a replica, we must change
+ * the wins owner to us to make the replication updates
+ * it on the other wins servers.
+ * And when the partner will receive this record,
+ * it will update its own record.
+ */
+
+ update_wins_owner(namerec, our_fake_ip);
+ }
+ update_name_ttl(namerec, ttl);
+ send_wins_name_registration_response(0, ttl, p);
+ return;
+ } else {
+
+ /*
+ * If we are adding a unique name, the name exists in the WINS db
+ * and is a group name then reject the registration.
+ *
+ * explanation: groups have a higher priority than unique names.
+ */
+
+ DEBUG(3,("wins_process_name_registration_request: Attempt to register name %s. Name \
+already exists in WINS as a GROUP name.\n", nmb_namestr(question) ));
+ send_wins_name_registration_response(RFS_ERR, 0, p);
+ return;
+ }
+ }
+
+ /*
+ * From here on down we know that if the name exists in the WINS db it is
+ * a unique name, not a group name.
+ */
+
+ /*
+ * If the name exists and is one of our names then check the
+ * registering IP address. If it's not one of ours then automatically
+ * reject without doing the query - we know we will reject it.
+ */
+
+ if ( namerec != NULL )
+ pull_ascii_nstring(name, sizeof(name), namerec->name.name);
+
+ if( is_myname(name) ) {
+ if(!ismyip(from_ip)) {
+ DEBUG(3,("wins_process_name_registration_request: Attempt to register name %s. Name \
+is one of our (WINS server) names. Denying registration.\n", nmb_namestr(question) ));
+ send_wins_name_registration_response(RFS_ERR, 0, p);
+ return;
+ } else {
+ /*
+ * It's one of our names and one of our IP's - update the ttl.
+ */
+ update_name_ttl(namerec, ttl);
+ send_wins_name_registration_response(0, ttl, p);
+ wins_hook("refresh", namerec, ttl);
+ return;
+ }
+ }
+
+ /*
+ * If the name exists and it is a unique registration and the registering IP
+ * is the same as the (single) already registered IP then just update the ttl.
+ *
+ * But not if the record is an active replica. IF it's a replica, it means it can be
+ * the same client which has moved and not yet expired. So we don't update
+ * the ttl in this case and go beyond to do a WACK and query the old client
+ */
+
+ if( !registering_group_name
+ && (namerec != NULL)
+ && (namerec->data.num_ips == 1)
+ && ip_equal( namerec->data.ip[0], from_ip )
+ && ip_equal(namerec->data.wins_ip, our_fake_ip) ) {
+ update_name_ttl( namerec, ttl );
+ send_wins_name_registration_response( 0, ttl, p );
+ wins_hook("refresh", namerec, ttl);
+ return;
+ }
+
+ /*
+ * Finally if the name exists do a query to the registering machine
+ * to see if they still claim to have the name.
+ */
+
+ if( namerec != NULL ) {
+ long *ud[(sizeof(struct userdata_struct) + sizeof(struct packet_struct *))/sizeof(long *) + 1];
+ struct userdata_struct *userdata = (struct userdata_struct *)ud;
+
+ /*
+ * First send a WACK to the registering machine.
+ */
+
+ send_wins_wack_response(60, p);
+
+ /*
+ * When the reply comes back we need the original packet.
+ * Lock this so it won't be freed and then put it into
+ * the userdata structure.
+ */
+
+ p->locked = True;
+
+ userdata = (struct userdata_struct *)ud;
+
+ userdata->copy_fn = NULL;
+ userdata->free_fn = NULL;
+ userdata->userdata_len = sizeof(struct packet_struct *);
+ memcpy(userdata->data, (char *)&p, sizeof(struct packet_struct *) );
+
+ /*
+ * Use the new call to send a query directly to an IP address.
+ * This sends the query directly to the IP address, and ensures
+ * the recursion desired flag is not set (you were right Luke :-).
+ * This function should *only* be called from the WINS server
+ * code. JRA.
+ */
+
+ pull_ascii_nstring(name, sizeof(name), question->name);
+ query_name_from_wins_server( *namerec->data.ip,
+ name,
+ question->name_type,
+ wins_register_query_success,
+ wins_register_query_fail,
+ userdata );
+ return;
+ }
+
+ /*
+ * Name did not exist - add it.
+ */
+
+ pull_ascii_nstring(name, sizeof(name), question->name);
+ add_name_to_subnet( subrec, name, question->name_type,
+ nb_flags, ttl, REGISTER_NAME, 1, &from_ip);
+
+ if ((namerec = find_name_on_subnet(subrec, question, FIND_ANY_NAME))) {
+ get_global_id_and_update(&namerec->data.id, True);
+ update_wins_owner(namerec, our_fake_ip);
+ update_wins_flag(namerec, WINS_ACTIVE);
+ wins_hook("add", namerec, ttl);
+ }
+
+ send_wins_name_registration_response(0, ttl, p);
+}
+
+/***********************************************************************
+ Deal with a mutihomed name query success to the machine that
+ requested the multihomed name registration.
+
+ We have a locked pointer to the original packet stashed away in the
+ userdata pointer.
+************************************************************************/
+
+static void wins_multihomed_register_query_success(struct subnet_record *subrec,
+ struct userdata_struct *userdata,
+ struct nmb_name *question_name,
+ struct in_addr ip,
+ struct res_rec *answers)
+{
+ struct packet_struct *orig_reg_packet;
+ struct nmb_packet *nmb;
+ struct name_record *namerec = NULL;
+ struct in_addr from_ip;
+ int ttl;
+ struct in_addr our_fake_ip = *interpret_addr2("0.0.0.0");
+
+ memcpy((char *)&orig_reg_packet, userdata->data, sizeof(struct packet_struct *));
+
+ nmb = &orig_reg_packet->packet.nmb;
+
+ putip((char *)&from_ip,&nmb->additional->rdata[2]);
+ ttl = get_ttl_from_packet(nmb);
+
+ /*
+ * We want to just add the new IP, as we now know the requesting
+ * machine claims to own it. But we can't just do that as an arbitary
+ * amount of time may have taken place between the name query
+ * request and this response. So we check that
+ * the name still exists and is in the same state - if so
+ * we just add the extra IP and update the ttl.
+ */
+
+ namerec = find_name_on_subnet(subrec, question_name, FIND_ANY_NAME);
+
+ if( (namerec == NULL) || (namerec->data.source != REGISTER_NAME) || !WINS_STATE_ACTIVE(namerec) ) {
+ DEBUG(3,("wins_multihomed_register_query_success: name %s is not in the correct state to add \
+a subsequent IP address.\n", nmb_namestr(question_name) ));
+ send_wins_name_registration_response(RFS_ERR, 0, orig_reg_packet);
+
+ orig_reg_packet->locked = False;
+ free_packet(orig_reg_packet);
+
+ return;
+ }
+
+ if(!find_ip_in_name_record(namerec, from_ip))
+ add_ip_to_name_record(namerec, from_ip);
+
+ get_global_id_and_update(&namerec->data.id, True);
+ update_wins_owner(namerec, our_fake_ip);
+ update_wins_flag(namerec, WINS_ACTIVE);
+ update_name_ttl(namerec, ttl);
+ send_wins_name_registration_response(0, ttl, orig_reg_packet);
+ wins_hook("add", namerec, ttl);
+
+ orig_reg_packet->locked = False;
+ free_packet(orig_reg_packet);
+}
+
+/***********************************************************************
+ Deal with a name registration request query failure to a client that
+ owned the name.
+
+ We have a locked pointer to the original packet stashed away in the
+ userdata pointer.
+************************************************************************/
+
+static void wins_multihomed_register_query_fail(struct subnet_record *subrec,
+ struct response_record *rrec,
+ struct nmb_name *question_name,
+ int rcode)
+{
+ struct userdata_struct *userdata = rrec->userdata;
+ struct packet_struct *orig_reg_packet;
+
+ memcpy((char *)&orig_reg_packet, userdata->data, sizeof(struct packet_struct *));
+
+ DEBUG(3,("wins_multihomed_register_query_fail: Registering machine at IP %s failed to answer \
+query successfully for name %s.\n", inet_ntoa(orig_reg_packet->ip), nmb_namestr(question_name) ));
+ send_wins_name_registration_response(RFS_ERR, 0, orig_reg_packet);
+
+ orig_reg_packet->locked = False;
+ free_packet(orig_reg_packet);
+ return;
+}
+
+/***********************************************************************
+ Deal with a multihomed name registration request to a WINS server.
+ These cannot be group name registrations.
+***********************************************************************/
+
+void wins_process_multihomed_name_registration_request( struct subnet_record *subrec,
+ struct packet_struct *p)
+{
+ struct nmb_packet *nmb = &p->packet.nmb;
+ struct nmb_name *question = &nmb->question.question_name;
+ BOOL bcast = nmb->header.nm_flags.bcast;
+ uint16 nb_flags = get_nb_flags(nmb->additional->rdata);
+ int ttl = get_ttl_from_packet(nmb);
+ struct name_record *namerec = NULL;
+ struct in_addr from_ip;
+ BOOL group = (nb_flags & NB_GROUP) ? True : False;
+ struct in_addr our_fake_ip = *interpret_addr2("0.0.0.0");
+ unstring qname;
+
+ putip((char *)&from_ip,&nmb->additional->rdata[2]);
+
+ if(bcast) {
+ /*
+ * We should only get unicast name registration packets here.
+ * Anyone trying to register broadcast should not be going to a WINS
+ * server. Log an error here.
+ */
+
+ DEBUG(0,("wins_process_multihomed_name_registration_request: broadcast name registration request \
+received for name %s from IP %s on subnet %s. Error - should not be sent to WINS server\n",
+ nmb_namestr(question), inet_ntoa(from_ip), subrec->subnet_name));
+ return;
+ }
+
+ /*
+ * Only unique names should be registered multihomed.
+ */
+
+ if(group) {
+ DEBUG(0,("wins_process_multihomed_name_registration_request: group name registration request \
+received for name %s from IP %s on subnet %s. Errror - group names should not be multihomed.\n",
+ nmb_namestr(question), inet_ntoa(from_ip), subrec->subnet_name));
+ return;
+ }
+
+ DEBUG(3,("wins_process_multihomed_name_registration_request: name registration for name %s \
+IP %s\n", nmb_namestr(question), inet_ntoa(from_ip) ));
+
+ /*
+ * Deal with policy regarding 0x1d names.
+ */
+
+ if(question->name_type == 0x1d) {
+ DEBUG(3,("wins_process_multihomed_name_registration_request: Ignoring request \
+to register name %s from IP %s.", nmb_namestr(question), inet_ntoa(p->ip) ));
+ send_wins_name_registration_response(0, ttl, p);
+ return;
+ }
+
+ /*
+ * See if the name already exists.
+ */
+
+ namerec = find_name_on_subnet(subrec, question, FIND_ANY_NAME);
+
+ /*
+ * if the record exists but NOT in active state,
+ * consider it dead.
+ */
+
+ if ((namerec != NULL) && !WINS_STATE_ACTIVE(namerec)) {
+ DEBUG(5,("wins_process_multihomed_name_registration_request: Name (%s) in WINS was not active - removing it.\n", nmb_namestr(question)));
+ remove_name_from_namelist(subrec, namerec);
+ namerec = NULL;
+ }
+
+ /*
+ * Deal with the case where the name found was a dns entry.
+ * Remove it as we now have a NetBIOS client registering the
+ * name.
+ */
+
+ if( (namerec != NULL) && ( (namerec->data.source == DNS_NAME) || (namerec->data.source == DNSFAIL_NAME) ) ) {
+ DEBUG(5,("wins_process_multihomed_name_registration_request: Name (%s) in WINS was a dns lookup \
+- removing it.\n", nmb_namestr(question) ));
+ remove_name_from_namelist( subrec, namerec);
+ namerec = NULL;
+ }
+
+ /*
+ * Reject if the name exists and is not a REGISTER_NAME.
+ * (ie. Don't allow any static names to be overwritten.
+ */
+
+ if( (namerec != NULL) && (namerec->data.source != REGISTER_NAME) ) {
+ DEBUG( 3, ( "wins_process_multihomed_name_registration_request: Attempt \
+to register name %s. Name already exists in WINS with source type %d.\n",
+ nmb_namestr(question), namerec->data.source ));
+ send_wins_name_registration_response(RFS_ERR, 0, p);
+ return;
+ }
+
+ /*
+ * Reject if the name exists and is a GROUP name and is active.
+ */
+
+ if((namerec != NULL) && NAME_GROUP(namerec) && WINS_STATE_ACTIVE(namerec)) {
+ DEBUG(3,("wins_process_multihomed_name_registration_request: Attempt to register name %s. Name \
+already exists in WINS as a GROUP name.\n", nmb_namestr(question) ));
+ send_wins_name_registration_response(RFS_ERR, 0, p);
+ return;
+ }
+
+ /*
+ * From here on down we know that if the name exists in the WINS db it is
+ * a unique name, not a group name.
+ */
+
+ /*
+ * If the name exists and is one of our names then check the
+ * registering IP address. If it's not one of ours then automatically
+ * reject without doing the query - we know we will reject it.
+ */
+
+ if((namerec != NULL) && (is_myname(namerec->name.name)) ) {
+ if(!ismyip(from_ip)) {
+ DEBUG(3,("wins_process_multihomed_name_registration_request: Attempt to register name %s. Name \
+is one of our (WINS server) names. Denying registration.\n", nmb_namestr(question) ));
+ send_wins_name_registration_response(RFS_ERR, 0, p);
+ return;
+ } else {
+ /*
+ * It's one of our names and one of our IP's. Ensure the IP is in the record and
+ * update the ttl. Update the version ID to force replication.
+ */
+ if(!find_ip_in_name_record(namerec, from_ip)) {
+ get_global_id_and_update(&namerec->data.id, True);
+ update_wins_owner(namerec, our_fake_ip);
+ update_wins_flag(namerec, WINS_ACTIVE);
+
+ add_ip_to_name_record(namerec, from_ip);
+ wins_hook("add", namerec, ttl);
+ } else {
+ wins_hook("refresh", namerec, ttl);
+ }
+
+ update_name_ttl(namerec, ttl);
+ send_wins_name_registration_response(0, ttl, p);
+ return;
+ }
+ }
+
+ /*
+ * If the name exists and is active, check if the IP address is already registered
+ * to that name. If so then update the ttl and reply success.
+ */
+
+ if((namerec != NULL) && find_ip_in_name_record(namerec, from_ip) && WINS_STATE_ACTIVE(namerec)) {
+ update_name_ttl(namerec, ttl);
+
+ /*
+ * If it's a replica, we need to become the wins owner
+ * to force the replication
+ */
+ if (!ip_equal(namerec->data.wins_ip, our_fake_ip)) {
+ get_global_id_and_update(&namerec->data.id, True);
+ update_wins_owner(namerec, our_fake_ip);
+ update_wins_flag(namerec, WINS_ACTIVE);
+ }
+
+ send_wins_name_registration_response(0, ttl, p);
+ wins_hook("refresh", namerec, ttl);
+ return;
+ }
+
+ /*
+ * If the name exists do a query to the owner
+ * to see if they still want the name.
+ */
+
+ if(namerec != NULL) {
+ long *ud[(sizeof(struct userdata_struct) + sizeof(struct packet_struct *))/sizeof(long *) + 1];
+ struct userdata_struct *userdata = (struct userdata_struct *)ud;
+
+ /*
+ * First send a WACK to the registering machine.
+ */
+
+ send_wins_wack_response(60, p);
+
+ /*
+ * When the reply comes back we need the original packet.
+ * Lock this so it won't be freed and then put it into
+ * the userdata structure.
+ */
+
+ p->locked = True;
+
+ userdata = (struct userdata_struct *)ud;
+
+ userdata->copy_fn = NULL;
+ userdata->free_fn = NULL;
+ userdata->userdata_len = sizeof(struct packet_struct *);
+ memcpy(userdata->data, (char *)&p, sizeof(struct packet_struct *) );
+
+ /*
+ * Use the new call to send a query directly to an IP address.
+ * This sends the query directly to the IP address, and ensures
+ * the recursion desired flag is not set (you were right Luke :-).
+ * This function should *only* be called from the WINS server
+ * code. JRA.
+ *
+ * Note that this packet is sent to the current owner of the name,
+ * not the person who sent the packet
+ */
+
+ pull_ascii_nstring( qname, sizeof(qname), question->name);
+ query_name_from_wins_server( namerec->data.ip[0],
+ qname,
+ question->name_type,
+ wins_multihomed_register_query_success,
+ wins_multihomed_register_query_fail,
+ userdata );
+
+ return;
+ }
+
+ /*
+ * Name did not exist - add it.
+ */
+
+ pull_ascii_nstring( qname, sizeof(qname), question->name);
+ add_name_to_subnet( subrec, qname, question->name_type,
+ nb_flags, ttl, REGISTER_NAME, 1, &from_ip);
+
+ if ((namerec = find_name_on_subnet(subrec, question, FIND_ANY_NAME))) {
+ get_global_id_and_update(&namerec->data.id, True);
+ update_wins_owner(namerec, our_fake_ip);
+ update_wins_flag(namerec, WINS_ACTIVE);
+ wins_hook("add", namerec, ttl);
+ }
+
+ send_wins_name_registration_response(0, ttl, p);
+}
+
+/***********************************************************************
+ Deal with the special name query for *<1b>.
+***********************************************************************/
+
+static void process_wins_dmb_query_request(struct subnet_record *subrec,
+ struct packet_struct *p)
+{
+ struct name_record *namerec = NULL;
+ char *prdata;
+ int num_ips;
+
+ /*
+ * Go through all the ACTIVE names in the WINS db looking for those
+ * ending in <1b>. Use this to calculate the number of IP
+ * addresses we need to return.
+ */
+
+ num_ips = 0;
+ for( namerec = (struct name_record *)ubi_trFirst( subrec->namelist );
+ namerec; namerec = (struct name_record *)ubi_trNext( namerec ) ) {
+ if(WINS_STATE_ACTIVE(namerec) && namerec->name.name_type == 0x1b )
+ num_ips += namerec->data.num_ips;
+ }
+
+ if(num_ips == 0) {
+ /*
+ * There are no 0x1b names registered. Return name query fail.
+ */
+ send_wins_name_query_response(NAM_ERR, p, NULL);
+ return;
+ }
+
+ if((prdata = (char *)malloc( num_ips * 6 )) == NULL) {
+ DEBUG(0,("process_wins_dmb_query_request: Malloc fail !.\n"));
+ return;
+ }
+
+ /*
+ * Go through all the names again in the WINS db looking for those
+ * ending in <1b>. Add their IP addresses into the list we will
+ * return.
+ */
+
+ num_ips = 0;
+ for( namerec = (struct name_record *)ubi_trFirst( subrec->namelist );
+ namerec; namerec = (struct name_record *)ubi_trNext( namerec ) ) {
+ if(WINS_STATE_ACTIVE(namerec) && namerec->name.name_type == 0x1b) {
+ int i;
+ for(i = 0; i < namerec->data.num_ips; i++) {
+ set_nb_flags(&prdata[num_ips * 6],namerec->data.nb_flags);
+ putip((char *)&prdata[(num_ips * 6) + 2], &namerec->data.ip[i]);
+ num_ips++;
+ }
+ }
+ }
+
+ /*
+ * Send back the reply containing the IP list.
+ */
+
+ reply_netbios_packet(p, /* Packet to reply to. */
+ 0, /* Result code. */
+ WINS_QUERY, /* nmbd type code. */
+ NMB_NAME_QUERY_OPCODE, /* opcode. */
+ lp_min_wins_ttl(), /* ttl. */
+ prdata, /* data to send. */
+ num_ips*6); /* data length. */
+
+ SAFE_FREE(prdata);
+}
+
+/****************************************************************************
+Send a WINS name query response.
+**************************************************************************/
+
+void send_wins_name_query_response(int rcode, struct packet_struct *p,
+ struct name_record *namerec)
+{
+ char rdata[6];
+ char *prdata = rdata;
+ int reply_data_len = 0;
+ int ttl = 0;
+ int i;
+
+ memset(rdata,'\0',6);
+
+ if(rcode == 0) {
+ ttl = (namerec->data.death_time != PERMANENT_TTL) ? namerec->data.death_time - p->timestamp : lp_max_wins_ttl();
+
+ /* Copy all known ip addresses into the return data. */
+ /* Optimise for the common case of one IP address so we don't need a malloc. */
+
+ if( namerec->data.num_ips == 1 ) {
+ prdata = rdata;
+ } else {
+ if((prdata = (char *)malloc( namerec->data.num_ips * 6 )) == NULL) {
+ DEBUG(0,("send_wins_name_query_response: malloc fail !\n"));
+ return;
+ }
+ }
+
+ for(i = 0; i < namerec->data.num_ips; i++) {
+ set_nb_flags(&prdata[i*6],namerec->data.nb_flags);
+ putip((char *)&prdata[2+(i*6)], &namerec->data.ip[i]);
+ }
+
+ sort_query_replies(prdata, i, p->ip);
+ reply_data_len = namerec->data.num_ips * 6;
+ }
+
+ reply_netbios_packet(p, /* Packet to reply to. */
+ rcode, /* Result code. */
+ WINS_QUERY, /* nmbd type code. */
+ NMB_NAME_QUERY_OPCODE, /* opcode. */
+ ttl, /* ttl. */
+ prdata, /* data to send. */
+ reply_data_len); /* data length. */
+
+ if(prdata != rdata)
+ SAFE_FREE(prdata);
+}
+
+/***********************************************************************
+ Deal with a name query.
+***********************************************************************/
+
+void wins_process_name_query_request(struct subnet_record *subrec,
+ struct packet_struct *p)
+{
+ struct nmb_packet *nmb = &p->packet.nmb;
+ struct nmb_name *question = &nmb->question.question_name;
+ struct name_record *namerec = NULL;
+ unstring qname;
+
+ DEBUG(3,("wins_process_name_query: name query for name %s from IP %s\n",
+ nmb_namestr(question), inet_ntoa(p->ip) ));
+
+ /*
+ * Special name code. If the queried name is *<1b> then search
+ * the entire WINS database and return a list of all the IP addresses
+ * registered to any <1b> name. This is to allow domain master browsers
+ * to discover other domains that may not have a presence on their subnet.
+ */
+
+ pull_ascii_nstring(qname, sizeof(qname), question->name);
+ if(strequal( qname, "*") && (question->name_type == 0x1b)) {
+ process_wins_dmb_query_request( subrec, p);
+ return;
+ }
+
+ namerec = find_name_on_subnet(subrec, question, FIND_ANY_NAME);
+
+ if(namerec != NULL) {
+ /*
+ * If the name is not anymore in active state then reply not found.
+ * it's fair even if we keep it in the cache for days.
+ */
+ if (!WINS_STATE_ACTIVE(namerec)) {
+ DEBUG(3,("wins_process_name_query: name query for name %s - name expired. Returning fail.\n",
+ nmb_namestr(question) ));
+ send_wins_name_query_response(NAM_ERR, p, namerec);
+ return;
+ }
+
+ /*
+ * If it's a DNSFAIL_NAME then reply name not found.
+ */
+
+ if( namerec->data.source == DNSFAIL_NAME ) {
+ DEBUG(3,("wins_process_name_query: name query for name %s returning DNS fail.\n",
+ nmb_namestr(question) ));
+ send_wins_name_query_response(NAM_ERR, p, namerec);
+ return;
+ }
+
+ /*
+ * If the name has expired then reply name not found.
+ */
+
+ if( (namerec->data.death_time != PERMANENT_TTL) && (namerec->data.death_time < p->timestamp) ) {
+ DEBUG(3,("wins_process_name_query: name query for name %s - name expired. Returning fail.\n",
+ nmb_namestr(question) ));
+ send_wins_name_query_response(NAM_ERR, p, namerec);
+ return;
+ }
+
+ DEBUG(3,("wins_process_name_query: name query for name %s returning first IP %s.\n",
+ nmb_namestr(question), inet_ntoa(namerec->data.ip[0]) ));
+
+ send_wins_name_query_response(0, p, namerec);
+ return;
+ }
+
+ /*
+ * Name not found in WINS - try a dns query if it's a 0x20 name.
+ */
+
+ if(lp_dns_proxy() && ((question->name_type == 0x20) || question->name_type == 0)) {
+ DEBUG(3,("wins_process_name_query: name query for name %s not found - doing dns lookup.\n",
+ nmb_namestr(question) ));
+
+ queue_dns_query(p, question, &namerec);
+ return;
+ }
+
+ /*
+ * Name not found - return error.
+ */
+
+ send_wins_name_query_response(NAM_ERR, p, NULL);
+}
+
+/****************************************************************************
+Send a WINS name release response.
+**************************************************************************/
+
+static void send_wins_name_release_response(int rcode, struct packet_struct *p)
+{
+ struct nmb_packet *nmb = &p->packet.nmb;
+ char rdata[6];
+
+ memcpy(&rdata[0], &nmb->additional->rdata[0], 6);
+
+ reply_netbios_packet(p, /* Packet to reply to. */
+ rcode, /* Result code. */
+ NMB_REL, /* nmbd type code. */
+ NMB_NAME_RELEASE_OPCODE, /* opcode. */
+ 0, /* ttl. */
+ rdata, /* data to send. */
+ 6); /* data length. */
+}
+
+/***********************************************************************
+ Deal with a name release.
+***********************************************************************/
+
+void wins_process_name_release_request(struct subnet_record *subrec,
+ struct packet_struct *p)
+{
+ struct nmb_packet *nmb = &p->packet.nmb;
+ struct nmb_name *question = &nmb->question.question_name;
+ BOOL bcast = nmb->header.nm_flags.bcast;
+ uint16 nb_flags = get_nb_flags(nmb->additional->rdata);
+ struct name_record *namerec = NULL;
+ struct in_addr from_ip;
+ BOOL releasing_group_name = (nb_flags & NB_GROUP) ? True : False;;
+
+ putip((char *)&from_ip,&nmb->additional->rdata[2]);
+
+ if(bcast) {
+ /*
+ * We should only get unicast name registration packets here.
+ * Anyone trying to register broadcast should not be going to a WINS
+ * server. Log an error here.
+ */
+
+ DEBUG(0,("wins_process_name_release_request: broadcast name registration request \
+received for name %s from IP %s on subnet %s. Error - should not be sent to WINS server\n",
+ nmb_namestr(question), inet_ntoa(from_ip), subrec->subnet_name));
+ return;
+ }
+
+ DEBUG(3,("wins_process_name_release_request: %s name release for name %s \
+IP %s\n", releasing_group_name ? "Group" : "Unique", nmb_namestr(question), inet_ntoa(from_ip) ));
+
+ /*
+ * Deal with policy regarding 0x1d names.
+ */
+
+ if(!releasing_group_name && (question->name_type == 0x1d)) {
+ DEBUG(3,("wins_process_name_release_request: Ignoring request \
+to release name %s from IP %s.", nmb_namestr(question), inet_ntoa(p->ip) ));
+ send_wins_name_release_response(0, p);
+ return;
+ }
+
+ /*
+ * See if the name already exists.
+ */
+
+ namerec = find_name_on_subnet(subrec, question, FIND_ANY_NAME);
+
+ if( (namerec == NULL) || ((namerec != NULL) && (namerec->data.source != REGISTER_NAME)) ) {
+ send_wins_name_release_response(NAM_ERR, p);
+ return;
+ }
+
+ /*
+ * Check that the sending machine has permission to release this name.
+ * If it's a group name not ending in 0x1c then just say yes and let
+ * the group time out.
+ */
+
+ if(releasing_group_name && (question->name_type != 0x1c)) {
+ send_wins_name_release_response(0, p);
+ return;
+ }
+
+ /*
+ * Check that the releasing node is on the list of IP addresses
+ * for this name. Disallow the release if not.
+ */
+
+ if(!find_ip_in_name_record(namerec, from_ip)) {
+ DEBUG(3,("wins_process_name_release_request: Refusing request to \
+release name %s as IP %s is not one of the known IP's for this name.\n",
+ nmb_namestr(question), inet_ntoa(from_ip) ));
+ send_wins_name_release_response(NAM_ERR, p);
+ return;
+ }
+
+ /*
+ * Check if the record is active. IF it's already released
+ * or tombstoned, refuse the release.
+ */
+
+ if (!WINS_STATE_ACTIVE(namerec)) {
+ DEBUG(3,("wins_process_name_release_request: Refusing request to \
+release name %s as this record is not active anymore.\n", nmb_namestr(question) ));
+ send_wins_name_release_response(NAM_ERR, p);
+ return;
+ }
+
+ /*
+ * Check if the record is a 0x1c group
+ * and has more then one ip
+ * remove only this address.
+ */
+
+ if(releasing_group_name && (question->name_type == 0x1c) && (namerec->data.num_ips > 1)) {
+ remove_ip_from_name_record(namerec, from_ip);
+ DEBUG(3,("wins_process_name_release_request: Remove IP %s from NAME: %s\n",
+ inet_ntoa(from_ip),nmb_namestr(question)));
+ send_wins_name_release_response(0, p);
+ return;
+ }
+
+ /*
+ * Send a release response.
+ * Flag the name as released and update the ttl
+ */
+
+ send_wins_name_release_response(0, p);
+
+ namerec->data.wins_flags |= WINS_RELEASED;
+ update_name_ttl(namerec, EXTINCTION_INTERVAL);
+
+ wins_hook("delete", namerec, 0);
+}
+
+/*******************************************************************
+ WINS time dependent processing.
+******************************************************************/
+
+void initiate_wins_processing(time_t t)
+{
+ static time_t lasttime = 0;
+ struct name_record *namerec;
+ struct name_record *next_namerec;
+ struct in_addr our_fake_ip = *interpret_addr2("0.0.0.0");
+
+ if (!lasttime)
+ lasttime = t;
+ if (t - lasttime < 20)
+ return;
+
+ lasttime = t;
+
+ if(!lp_we_are_a_wins_server())
+ return;
+
+ for( namerec = (struct name_record *)ubi_trFirst( wins_server_subnet->namelist );
+ namerec;
+ namerec = next_namerec ) {
+ next_namerec = (struct name_record *)ubi_trNext( namerec );
+
+ if( (namerec->data.death_time != PERMANENT_TTL)
+ && (namerec->data.death_time < t) ) {
+
+ if( namerec->data.source == SELF_NAME ) {
+ DEBUG( 3, ( "expire_names_on_subnet: Subnet %s not expiring SELF name %s\n",
+ wins_server_subnet->subnet_name, nmb_namestr(&namerec->name) ) );
+ namerec->data.death_time += 300;
+ namerec->subnet->namelist_changed = True;
+ continue;
+ }
+
+ /* handle records, samba is the wins owner */
+ if (ip_equal(namerec->data.wins_ip, our_fake_ip)) {
+ switch (namerec->data.wins_flags | WINS_STATE_MASK) {
+ case WINS_ACTIVE:
+ namerec->data.wins_flags&=~WINS_STATE_MASK;
+ namerec->data.wins_flags|=WINS_RELEASED;
+ namerec->data.death_time = t + EXTINCTION_INTERVAL;
+ DEBUG(3,("initiate_wins_processing: expiring %s\n", nmb_namestr(&namerec->name)));
+ break;
+ case WINS_RELEASED:
+ namerec->data.wins_flags&=~WINS_STATE_MASK;
+ namerec->data.wins_flags|=WINS_TOMBSTONED;
+ namerec->data.death_time = t + EXTINCTION_TIMEOUT;
+ get_global_id_and_update(&namerec->data.id, True);
+ DEBUG(3,("initiate_wins_processing: tombstoning %s\n", nmb_namestr(&namerec->name)));
+ break;
+ case WINS_TOMBSTONED:
+ DEBUG(3,("initiate_wins_processing: deleting %s\n", nmb_namestr(&namerec->name)));
+ remove_name_from_namelist( wins_server_subnet, namerec );
+ break;
+ }
+ } else {
+ switch (namerec->data.wins_flags | WINS_STATE_MASK) {
+ case WINS_ACTIVE:
+ /* that's not as MS says it should be */
+ namerec->data.wins_flags&=~WINS_STATE_MASK;
+ namerec->data.wins_flags|=WINS_TOMBSTONED;
+ namerec->data.death_time = t + EXTINCTION_TIMEOUT;
+ DEBUG(3,("initiate_wins_processing: tombstoning %s\n", nmb_namestr(&namerec->name)));
+ case WINS_TOMBSTONED:
+ DEBUG(3,("initiate_wins_processing: deleting %s\n", nmb_namestr(&namerec->name)));
+ remove_name_from_namelist( wins_server_subnet, namerec );
+ break;
+ case WINS_RELEASED:
+ DEBUG(0,("initiate_wins_processing: %s is in released state and\
+we are not the wins owner !\n", nmb_namestr(&namerec->name)));
+ break;
+ }
+ }
+
+ }
+ }
+
+ if(wins_server_subnet->namelist_changed)
+ wins_write_database(True);
+
+ wins_server_subnet->namelist_changed = False;
+}
+
+/*******************************************************************
+ Write out the current WINS database.
+******************************************************************/
+
+void wins_write_database(BOOL background)
+{
+ struct name_record *namerec;
+ pstring fname, fnamenew;
+
+ XFILE *fp;
+
+ if(!lp_we_are_a_wins_server())
+ return;
+
+ /* We will do the writing in a child process to ensure that the parent doesn't block while this is done */
+ if (background) {
+ CatchChild();
+ if (sys_fork()) {
+ return;
+ }
+ }
+
+ slprintf(fname,sizeof(fname)-1,"%s/%s", lp_lockdir(), WINS_LIST);
+ all_string_sub(fname,"//", "/", 0);
+ slprintf(fnamenew,sizeof(fnamenew)-1,"%s.%u", fname, (unsigned int)sys_getpid());
+
+ if((fp = x_fopen(fnamenew,O_WRONLY|O_CREAT,0644)) == NULL) {
+ DEBUG(0,("wins_write_database: Can't open %s. Error was %s\n", fnamenew, strerror(errno)));
+ if (background) {
+ _exit(0);
+ }
+ return;
+ }
+
+ DEBUG(4,("wins_write_database: Dump of WINS name list.\n"));
+
+ x_fprintf(fp,"VERSION %d %u\n", WINS_VERSION, 0);
+
+ for( namerec = (struct name_record *)ubi_trFirst( wins_server_subnet->namelist ); namerec; namerec = (struct name_record *)ubi_trNext( namerec ) ) {
+ int i;
+ struct tm *tm;
+
+ DEBUGADD(4,("%-19s ", nmb_namestr(&namerec->name) ));
+
+ if( namerec->data.death_time != PERMANENT_TTL ) {
+ char *ts, *nl;
+
+ tm = LocalTime(&namerec->data.death_time);
+ ts = asctime(tm);
+ nl = strrchr( ts, '\n' );
+ if( NULL != nl )
+ *nl = '\0';
+ DEBUGADD(4,("TTL = %s ", ts ));
+ } else {
+ DEBUGADD(4,("TTL = PERMANENT "));
+ }
+
+ for (i = 0; i < namerec->data.num_ips; i++)
+ DEBUGADD(4,("%15s ", inet_ntoa(namerec->data.ip[i]) ));
+ DEBUGADD(4,("%2x\n", namerec->data.nb_flags ));
+
+ if( namerec->data.source == REGISTER_NAME ) {
+ unstring name;
+ pull_ascii_nstring(name, sizeof(name), namerec->name.name);
+ x_fprintf(fp, "\"%s#%02x\" %d ", name,namerec->name.name_type, /* Ignore scope. */
+ (int)namerec->data.death_time);
+
+ for (i = 0; i < namerec->data.num_ips; i++)
+ x_fprintf( fp, "%s ", inet_ntoa( namerec->data.ip[i] ) );
+ x_fprintf( fp, "%2xR\n", namerec->data.nb_flags );
+ }
+ }
+
+ x_fclose(fp);
+ chmod(fnamenew,0644);
+ unlink(fname);
+ rename(fnamenew,fname);
+ if (background) {
+ _exit(0);
+ }
+}
+
+/****************************************************************************
+ Process a internal Samba message receiving a wins record.
+***************************************************************************/
+
+void nmbd_wins_new_entry(int msg_type, pid_t src, void *buf, size_t len)
+{
+ WINS_RECORD *record;
+ struct name_record *namerec = NULL;
+ struct name_record *new_namerec = NULL;
+ struct nmb_name question;
+ BOOL overwrite=False;
+ struct in_addr our_fake_ip = *interpret_addr2("0.0.0.0");
+ int i;
+
+ if (buf==NULL)
+ return;
+
+ /* Record should use UNIX codepage. Ensure this is so in the wrepld code. JRA. */
+ record=(WINS_RECORD *)buf;
+
+ make_nmb_name(&question, record->name, record->type);
+
+ namerec = find_name_on_subnet(wins_server_subnet, &question, FIND_ANY_NAME);
+
+ /* record doesn't exist, add it */
+ if (namerec == NULL) {
+ DEBUG(3,("nmbd_wins_new_entry: adding new replicated record: %s<%02x> for wins server: %s\n",
+ record->name, record->type, inet_ntoa(record->wins_ip)));
+
+ new_namerec=add_name_to_subnet( wins_server_subnet, record->name, record->type, record->nb_flags,
+ EXTINCTION_INTERVAL, REGISTER_NAME, record->num_ips, record->ip);
+ if (new_namerec!=NULL) {
+ update_wins_owner(new_namerec, record->wins_ip);
+ update_wins_flag(new_namerec, record->wins_flags);
+ new_namerec->data.id=record->id;
+
+ wins_server_subnet->namelist_changed = True;
+ }
+ }
+
+ /* check if we have a conflict */
+ if (namerec != NULL) {
+ /* both records are UNIQUE */
+ if (namerec->data.wins_flags&WINS_UNIQUE && record->wins_flags&WINS_UNIQUE) {
+
+ /* the database record is a replica */
+ if (!ip_equal(namerec->data.wins_ip, our_fake_ip)) {
+ if (namerec->data.wins_flags&WINS_ACTIVE && record->wins_flags&WINS_TOMBSTONED) {
+ if (ip_equal(namerec->data.wins_ip, record->wins_ip))
+ overwrite=True;
+ } else
+ overwrite=True;
+ } else {
+ /* we are the wins owner of the database record */
+ /* the 2 records have the same IP address */
+ if (ip_equal(namerec->data.ip[0], record->ip[0])) {
+ if (namerec->data.wins_flags&WINS_ACTIVE && record->wins_flags&WINS_TOMBSTONED)
+ get_global_id_and_update(&namerec->data.id, True);
+ else
+ overwrite=True;
+
+ } else {
+ /* the 2 records have different IP address */
+ if (namerec->data.wins_flags&WINS_ACTIVE) {
+ if (record->wins_flags&WINS_TOMBSTONED)
+ get_global_id_and_update(&namerec->data.id, True);
+ if (record->wins_flags&WINS_ACTIVE)
+ /* send conflict challenge to the replica node */
+ ;
+ } else
+ overwrite=True;
+ }
+
+ }
+ }
+
+ /* the replica is a standard group */
+ if (record->wins_flags&WINS_NGROUP || record->wins_flags&WINS_SGROUP) {
+ /* if the database record is unique and active force a name release */
+ if (namerec->data.wins_flags&WINS_UNIQUE)
+ /* send a release name to the unique node */
+ ;
+ overwrite=True;
+
+ }
+
+ /* the replica is a special group */
+ if (record->wins_flags&WINS_SGROUP && namerec->data.wins_flags&WINS_SGROUP) {
+ if (namerec->data.wins_flags&WINS_ACTIVE) {
+ for (i=0; i<record->num_ips; i++)
+ if(!find_ip_in_name_record(namerec, record->ip[i]))
+ add_ip_to_name_record(namerec, record->ip[i]);
+ } else {
+ overwrite=True;
+ }
+ }
+
+ /* the replica is a multihomed host */
+
+ /* I'm giving up on multi homed. Too much complex to understand */
+
+ if (record->wins_flags&WINS_MHOMED) {
+ if (! (namerec->data.wins_flags&WINS_ACTIVE)) {
+ if ( !(namerec->data.wins_flags&WINS_RELEASED) && !(namerec->data.wins_flags&WINS_NGROUP))
+ overwrite=True;
+ }
+ else {
+ if (ip_equal(record->wins_ip, namerec->data.wins_ip))
+ overwrite=True;
+
+ if (ip_equal(namerec->data.wins_ip, our_fake_ip))
+ if (namerec->data.wins_flags&WINS_UNIQUE)
+ get_global_id_and_update(&namerec->data.id, True);
+
+ }
+
+ if (record->wins_flags&WINS_ACTIVE && namerec->data.wins_flags&WINS_ACTIVE)
+ if (namerec->data.wins_flags&WINS_UNIQUE ||
+ namerec->data.wins_flags&WINS_MHOMED)
+ if (ip_equal(record->wins_ip, namerec->data.wins_ip))
+ overwrite=True;
+
+ }
+
+ if (overwrite == False)
+ DEBUG(3, ("nmbd_wins_new_entry: conflict in adding record: %s<%02x> from wins server: %s\n",
+ record->name, record->type, inet_ntoa(record->wins_ip)));
+ else {
+ DEBUG(3, ("nmbd_wins_new_entry: replacing record: %s<%02x> from wins server: %s\n",
+ record->name, record->type, inet_ntoa(record->wins_ip)));
+
+ /* remove the old record and add a new one */
+ remove_name_from_namelist( wins_server_subnet, namerec );
+ new_namerec=add_name_to_subnet( wins_server_subnet, record->name, record->type, record->nb_flags,
+ EXTINCTION_INTERVAL, REGISTER_NAME, record->num_ips, record->ip);
+ if (new_namerec!=NULL) {
+ update_wins_owner(new_namerec, record->wins_ip);
+ update_wins_flag(new_namerec, record->wins_flags);
+ new_namerec->data.id=record->id;
+
+ wins_server_subnet->namelist_changed = True;
+ }
+
+ wins_server_subnet->namelist_changed = True;
+ }
+
+ }
+}
diff --git a/source/nmbd/nmbd_workgroupdb.c b/source/nmbd/nmbd_workgroupdb.c
new file mode 100644
index 00000000000..8880cb58bb4
--- /dev/null
+++ b/source/nmbd/nmbd_workgroupdb.c
@@ -0,0 +1,319 @@
+/*
+ Unix SMB/CIFS implementation.
+ NBT netbios routines and daemon - version 2
+ Copyright (C) Andrew Tridgell 1994-1998
+ Copyright (C) Luke Kenneth Casson Leighton 1994-1998
+ Copyright (C) Jeremy Allison 1994-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 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"
+
+extern int ClientNMB;
+
+extern uint16 samba_nb_type;
+
+int workgroup_count = 0; /* unique index key: one for each workgroup */
+
+/****************************************************************************
+ Add a workgroup into the list.
+**************************************************************************/
+
+static void add_workgroup(struct subnet_record *subrec, struct work_record *work)
+{
+ work->subnet = subrec;
+ DLIST_ADD(subrec->workgrouplist, work);
+ subrec->work_changed = True;
+}
+
+/****************************************************************************
+ Create an empty workgroup.
+**************************************************************************/
+
+static struct work_record *create_workgroup(const char *name, int ttl)
+{
+ struct work_record *work;
+ struct subnet_record *subrec;
+ nstring nname;
+
+ int t = -1;
+
+ if((work = (struct work_record *)malloc(sizeof(*work))) == NULL) {
+ DEBUG(0,("create_workgroup: malloc fail !\n"));
+ return NULL;
+ }
+ memset((char *)work, '\0', sizeof(*work));
+
+ errno = 0;
+ push_ascii_nstring(nname, name);
+ if (errno == E2BIG) {
+ unstring tname;
+ pull_ascii_nstring(tname, sizeof(tname), nname);
+ unstrcpy(work->work_group,tname);
+ DEBUG(0,("create_workgroup: workgroup name %s is too long. Truncating to %s\n",
+ name, tname));
+ } else {
+ unstrcpy(work->work_group,name);
+ }
+ work->serverlist = NULL;
+
+ work->RunningElection = False;
+ work->ElectionCount = 0;
+ work->announce_interval = 0;
+ work->needelection = False;
+ work->needannounce = True;
+ work->lastannounce_time = time(NULL);
+ work->mst_state = lp_local_master() ? MST_POTENTIAL : MST_NONE;
+ work->dom_state = DOMAIN_NONE;
+ work->log_state = LOGON_NONE;
+
+ work->death_time = (ttl != PERMANENT_TTL) ? time(NULL)+(ttl*3) : PERMANENT_TTL;
+
+ /* Make sure all token representations of workgroups are unique. */
+
+ for (subrec = FIRST_SUBNET; subrec && (t == -1); subrec = NEXT_SUBNET_INCLUDING_UNICAST(subrec)) {
+ struct work_record *w;
+ for (w = subrec->workgrouplist; w && t == -1; w = w->next) {
+ if (strequal(w->work_group, work->work_group))
+ t = w->token;
+ }
+ }
+
+ if (t == -1)
+ work->token = ++workgroup_count;
+ else
+ work->token = t;
+
+ /* No known local master browser as yet. */
+ *work->local_master_browser_name = '\0';
+
+ /* No known domain master browser as yet. */
+ *work->dmb_name.name = '\0';
+ zero_ip(&work->dmb_addr);
+
+ /* WfWg uses 01040b01 */
+ /* Win95 uses 01041501 */
+ /* NTAS uses ???????? */
+ work->ElectionCriterion = (MAINTAIN_LIST)|(BROWSER_ELECTION_VERSION<<8);
+ work->ElectionCriterion |= (lp_os_level() << 24);
+ if (lp_domain_master())
+ work->ElectionCriterion |= 0x80;
+
+ return work;
+}
+
+/*******************************************************************
+ Remove a workgroup.
+******************************************************************/
+
+static struct work_record *remove_workgroup_from_subnet(struct subnet_record *subrec,
+ struct work_record *work)
+{
+ struct work_record *ret_work = NULL;
+
+ DEBUG(3,("remove_workgroup: Removing workgroup %s\n", work->work_group));
+
+ ret_work = work->next;
+
+ remove_all_servers(work);
+
+ if (!work->serverlist) {
+ if (work->prev)
+ work->prev->next = work->next;
+ if (work->next)
+ work->next->prev = work->prev;
+
+ if (subrec->workgrouplist == work)
+ subrec->workgrouplist = work->next;
+
+ ZERO_STRUCTP(work);
+ SAFE_FREE(work);
+ }
+
+ subrec->work_changed = True;
+
+ return ret_work;
+}
+
+/****************************************************************************
+ Find a workgroup in the workgroup list of a subnet.
+**************************************************************************/
+
+struct work_record *find_workgroup_on_subnet(struct subnet_record *subrec,
+ const char *name)
+{
+ struct work_record *ret;
+
+ DEBUG(4, ("find_workgroup_on_subnet: workgroup search for %s on subnet %s: ",
+ name, subrec->subnet_name));
+
+ for (ret = subrec->workgrouplist; ret; ret = ret->next) {
+ if (strequal(ret->work_group,name)) {
+ DEBUGADD(4, ("found.\n"));
+ return(ret);
+ }
+ }
+ DEBUGADD(4, ("not found.\n"));
+ return NULL;
+}
+
+/****************************************************************************
+ Create a workgroup in the workgroup list of the subnet.
+**************************************************************************/
+
+struct work_record *create_workgroup_on_subnet(struct subnet_record *subrec,
+ const char *name, int ttl)
+{
+ struct work_record *work = NULL;
+
+ DEBUG(4,("create_workgroup_on_subnet: creating group %s on subnet %s\n",
+ name, subrec->subnet_name));
+
+ if ((work = create_workgroup(name, ttl))) {
+ add_workgroup(subrec, work);
+ subrec->work_changed = True;
+ return(work);
+ }
+
+ return NULL;
+}
+
+/****************************************************************************
+ Update a workgroup ttl.
+**************************************************************************/
+
+void update_workgroup_ttl(struct work_record *work, int ttl)
+{
+ if(work->death_time != PERMANENT_TTL)
+ work->death_time = time(NULL)+(ttl*3);
+ work->subnet->work_changed = True;
+}
+
+/****************************************************************************
+ Fail function called if we cannot register the WORKGROUP<0> and
+ WORKGROUP<1e> names on the net.
+**************************************************************************/
+
+static void fail_register(struct subnet_record *subrec, struct response_record *rrec,
+ struct nmb_name *nmbname)
+{
+ DEBUG(0,("fail_register: Failed to register name %s on subnet %s.\n",
+ nmb_namestr(nmbname), subrec->subnet_name));
+}
+
+/****************************************************************************
+ If the workgroup is our primary workgroup, add the required names to it.
+**************************************************************************/
+
+void initiate_myworkgroup_startup(struct subnet_record *subrec, struct work_record *work)
+{
+ int i;
+
+ if(!strequal(lp_workgroup(), work->work_group))
+ return;
+
+ /* If this is a broadcast subnet then start elections on it if we are so configured. */
+
+ if ((subrec != unicast_subnet) && (subrec != remote_broadcast_subnet) &&
+ (subrec != wins_server_subnet) && lp_preferred_master() && lp_local_master()) {
+ DEBUG(3, ("initiate_myworkgroup_startup: preferred master startup for \
+workgroup %s on subnet %s\n", work->work_group, subrec->subnet_name));
+ work->needelection = True;
+ work->ElectionCriterion |= (1<<3);
+ }
+
+ /* Register the WORKGROUP<0> and WORKGROUP<1e> names on the network. */
+
+ register_name(subrec,lp_workgroup(),0x0,samba_nb_type|NB_GROUP, NULL, fail_register,NULL);
+ register_name(subrec,lp_workgroup(),0x1e,samba_nb_type|NB_GROUP, NULL, fail_register,NULL);
+
+ for( i = 0; my_netbios_names(i); i++) {
+ const char *name = my_netbios_names(i);
+ int stype = lp_default_server_announce() | (lp_local_master() ? SV_TYPE_POTENTIAL_BROWSER : 0 );
+
+ if(!strequal(global_myname(), name))
+ stype &= ~(SV_TYPE_MASTER_BROWSER|SV_TYPE_POTENTIAL_BROWSER|SV_TYPE_DOMAIN_MASTER|SV_TYPE_DOMAIN_MEMBER);
+
+ create_server_on_workgroup(work,name,stype|SV_TYPE_LOCAL_LIST_ONLY, PERMANENT_TTL,
+ string_truncate(lp_serverstring(), MAX_SERVER_STRING_LENGTH));
+ DEBUG(3,("initiate_myworkgroup_startup: Added server name entry %s \
+on subnet %s\n", name, subrec->subnet_name));
+ }
+}
+
+/****************************************************************************
+ Dump a copy of the workgroup database into the log file.
+ **************************************************************************/
+
+void dump_workgroups(BOOL force_write)
+{
+ struct subnet_record *subrec;
+ int debuglevel = force_write ? 0 : 4;
+
+ for (subrec = FIRST_SUBNET; subrec; subrec = NEXT_SUBNET_INCLUDING_UNICAST(subrec)) {
+ if (subrec->workgrouplist) {
+ struct work_record *work;
+
+ if( DEBUGLVL( debuglevel ) ) {
+ dbgtext( "dump_workgroups()\n " );
+ dbgtext( "dump workgroup on subnet %15s: ", subrec->subnet_name );
+ dbgtext( "netmask=%15s:\n", inet_ntoa(subrec->mask_ip) );
+ }
+
+ for (work = subrec->workgrouplist; work; work = work->next) {
+ DEBUGADD( debuglevel, ( "\t%s(%d) current master browser = %s\n", work->work_group,
+ work->token, *work->local_master_browser_name ? work->local_master_browser_name : "UNKNOWN" ) );
+ if (work->serverlist) {
+ struct server_record *servrec;
+ for (servrec = work->serverlist; servrec; servrec = servrec->next) {
+ DEBUGADD( debuglevel, ( "\t\t%s %8x (%s)\n",
+ servrec->serv.name,
+ servrec->serv.type,
+ servrec->serv.comment ) );
+ }
+ }
+ }
+ }
+ }
+}
+
+/****************************************************************************
+ Expire any dead servers on all workgroups. If the workgroup has expired
+ remove it.
+ **************************************************************************/
+
+void expire_workgroups_and_servers(time_t t)
+{
+ struct subnet_record *subrec;
+
+ for (subrec = FIRST_SUBNET; subrec; subrec = NEXT_SUBNET_INCLUDING_UNICAST(subrec)) {
+ struct work_record *work;
+ struct work_record *nextwork;
+
+ for (work = subrec->workgrouplist; work; work = nextwork) {
+ nextwork = work->next;
+ expire_servers(work, t);
+
+ if ((work->serverlist == NULL) && (work->death_time != PERMANENT_TTL) &&
+ ((t == -1) || (work->death_time < t))) {
+ DEBUG(3,("expire_workgroups_and_servers: Removing timed out workgroup %s\n",
+ work->work_group));
+ remove_workgroup_from_subnet(subrec, work);
+ }
+ }
+ }
+}
diff --git a/source/nsswitch/.cvsignore b/source/nsswitch/.cvsignore
new file mode 100644
index 00000000000..658d50a680f
--- /dev/null
+++ b/source/nsswitch/.cvsignore
@@ -0,0 +1,4 @@
+*.po
+*.po32
+diffs
+winbindd_proto.h
diff --git a/source/nsswitch/pam_winbind.c b/source/nsswitch/pam_winbind.c
new file mode 100644
index 00000000000..123f6703665
--- /dev/null
+++ b/source/nsswitch/pam_winbind.c
@@ -0,0 +1,754 @@
+/* pam_winbind module
+
+ Copyright Andrew Tridgell <tridge@samba.org> 2000
+ Copyright Tim Potter <tpot@samba.org> 2000
+ Copyright Andrew Bartlett <abartlet@samba.org> 2002
+
+ largely based on pam_userdb by Christian Gafton <gafton@redhat.com>
+ also contains large slabs of code from pam_unix by Elliot Lee <sopwith@redhat.com>
+ (see copyright below for full details)
+*/
+
+#include "pam_winbind.h"
+
+/* data tokens */
+
+#define MAX_PASSWD_TRIES 3
+
+/* some syslogging */
+static void _pam_log(int err, const char *format, ...)
+{
+ va_list args;
+
+ va_start(args, format);
+ openlog(MODULE_NAME, LOG_CONS|LOG_PID, LOG_AUTH);
+ vsyslog(err, format, args);
+ va_end(args);
+ closelog();
+}
+
+static int _pam_parse(int argc, const char **argv)
+{
+ int ctrl;
+ /* step through arguments */
+ for (ctrl = 0; argc-- > 0; ++argv) {
+
+ /* generic options */
+
+ if (!strcmp(*argv,"debug"))
+ ctrl |= WINBIND_DEBUG_ARG;
+ else if (!strcasecmp(*argv, "use_authtok"))
+ ctrl |= WINBIND_USE_AUTHTOK_ARG;
+ else if (!strcasecmp(*argv, "use_first_pass"))
+ ctrl |= WINBIND_USE_FIRST_PASS_ARG;
+ else if (!strcasecmp(*argv, "try_first_pass"))
+ ctrl |= WINBIND_TRY_FIRST_PASS_ARG;
+ else if (!strcasecmp(*argv, "unknown_ok"))
+ ctrl |= WINBIND_UNKNOWN_OK_ARG;
+ else {
+ _pam_log(LOG_ERR, "pam_parse: unknown option; %s", *argv);
+ }
+ }
+
+ return ctrl;
+}
+
+/* --- authentication management functions --- */
+
+/* Attempt a conversation */
+
+static int converse(pam_handle_t *pamh, int nargs,
+ struct pam_message **message,
+ struct pam_response **response)
+{
+ int retval;
+ struct pam_conv *conv;
+
+ retval = pam_get_item(pamh, PAM_CONV, (const void **) &conv ) ;
+ if (retval == PAM_SUCCESS) {
+ retval = conv->conv(nargs, (const struct pam_message **)message,
+ response, conv->appdata_ptr);
+ }
+
+ return retval; /* propagate error status */
+}
+
+
+static int _make_remark(pam_handle_t * pamh, int type, const char *text)
+{
+ int retval = PAM_SUCCESS;
+
+ struct pam_message *pmsg[1], msg[1];
+ struct pam_response *resp;
+
+ pmsg[0] = &msg[0];
+ msg[0].msg = text;
+ msg[0].msg_style = type;
+
+ resp = NULL;
+ retval = converse(pamh, 1, pmsg, &resp);
+
+ if (resp) {
+ _pam_drop_reply(resp, 1);
+ }
+ return retval;
+}
+
+static int pam_winbind_request(enum winbindd_cmd req_type,
+ struct winbindd_request *request,
+ struct winbindd_response *response)
+{
+
+ /* Fill in request and send down pipe */
+ init_request(request, req_type);
+
+ if (write_sock(request, sizeof(*request)) == -1) {
+ _pam_log(LOG_ERR, "write to socket failed!");
+ close_sock();
+ return PAM_SERVICE_ERR;
+ }
+
+ /* Wait for reply */
+ if (read_reply(response) == -1) {
+ _pam_log(LOG_ERR, "read from socket failed!");
+ close_sock();
+ return PAM_SERVICE_ERR;
+ }
+
+ /* We are done with the socket - close it and avoid mischeif */
+ close_sock();
+
+ /* Copy reply data from socket */
+ if (response->result != WINBINDD_OK) {
+ if (response->data.auth.pam_error != PAM_SUCCESS) {
+ _pam_log(LOG_ERR, "request failed: %s, PAM error was %d, NT error was %s",
+ response->data.auth.error_string,
+ response->data.auth.pam_error,
+ response->data.auth.nt_status_string);
+ return response->data.auth.pam_error;
+ } else {
+ _pam_log(LOG_ERR, "request failed, but PAM error 0!");
+ return PAM_SERVICE_ERR;
+ }
+ }
+
+ return PAM_SUCCESS;
+}
+
+static int pam_winbind_request_log(enum winbindd_cmd req_type,
+ struct winbindd_request *request,
+ struct winbindd_response *response,
+ int ctrl,
+ const char *user)
+{
+ int retval;
+
+ retval = pam_winbind_request(req_type, request, response);
+
+ switch (retval) {
+ case PAM_AUTH_ERR:
+ /* incorrect password */
+ _pam_log(LOG_WARNING, "user `%s' denied access (incorrect password)", user);
+ return retval;
+ case PAM_ACCT_EXPIRED:
+ /* account expired */
+ _pam_log(LOG_WARNING, "user `%s' account expired", user);
+ return retval;
+ case PAM_AUTHTOK_EXPIRED:
+ /* password expired */
+ _pam_log(LOG_WARNING, "user `%s' password expired", user);
+ return retval;
+ case PAM_NEW_AUTHTOK_REQD:
+ /* password expired */
+ _pam_log(LOG_WARNING, "user `%s' new password required", user);
+ return retval;
+ case PAM_USER_UNKNOWN:
+ /* the user does not exist */
+ if (ctrl & WINBIND_DEBUG_ARG)
+ _pam_log(LOG_NOTICE, "user `%s' not found",
+ user);
+ if (ctrl & WINBIND_UNKNOWN_OK_ARG) {
+ return PAM_IGNORE;
+ }
+ return retval;
+ case PAM_SUCCESS:
+ if (req_type == WINBINDD_PAM_AUTH) {
+ /* Otherwise, the authentication looked good */
+ _pam_log(LOG_NOTICE, "user '%s' granted acces", user);
+ } else if (req_type == WINBINDD_PAM_CHAUTHTOK) {
+ /* Otherwise, the authentication looked good */
+ _pam_log(LOG_NOTICE, "user '%s' password changed", user);
+ } else {
+ /* Otherwise, the authentication looked good */
+ _pam_log(LOG_NOTICE, "user '%s' OK", user);
+ }
+ return retval;
+ default:
+ /* we don't know anything about this return value */
+ _pam_log(LOG_ERR, "internal module error (retval = %d, user = `%s'",
+ retval, user);
+ return retval;
+ }
+}
+
+/* talk to winbindd */
+static int winbind_auth_request(const char *user, const char *pass, int ctrl)
+{
+ struct winbindd_request request;
+ struct winbindd_response response;
+
+ ZERO_STRUCT(request);
+
+ strncpy(request.data.auth.user, user,
+ sizeof(request.data.auth.user)-1);
+
+ strncpy(request.data.auth.pass, pass,
+ sizeof(request.data.auth.pass)-1);
+
+
+ return pam_winbind_request_log(WINBINDD_PAM_AUTH, &request, &response, ctrl, user);
+}
+
+/* talk to winbindd */
+static int winbind_chauthtok_request(const char *user, const char *oldpass,
+ const char *newpass, int ctrl)
+{
+ struct winbindd_request request;
+ struct winbindd_response response;
+
+ ZERO_STRUCT(request);
+
+ if (request.data.chauthtok.user == NULL) return -2;
+
+ strncpy(request.data.chauthtok.user, user,
+ sizeof(request.data.chauthtok.user) - 1);
+
+ if (oldpass != NULL) {
+ strncpy(request.data.chauthtok.oldpass, oldpass,
+ sizeof(request.data.chauthtok.oldpass) - 1);
+ } else {
+ request.data.chauthtok.oldpass[0] = '\0';
+ }
+
+ if (newpass != NULL) {
+ strncpy(request.data.chauthtok.newpass, newpass,
+ sizeof(request.data.chauthtok.newpass) - 1);
+ } else {
+ request.data.chauthtok.newpass[0] = '\0';
+ }
+
+ return pam_winbind_request_log(WINBINDD_PAM_CHAUTHTOK, &request, &response, ctrl, user);
+}
+
+/*
+ * Checks if a user has an account
+ *
+ * return values:
+ * 1 = User not found
+ * 0 = OK
+ * -1 = System error
+ */
+static int valid_user(const char *user)
+{
+ if (getpwnam(user)) return 0;
+ return 1;
+}
+
+static char *_pam_delete(register char *xx)
+{
+ _pam_overwrite(xx);
+ _pam_drop(xx);
+ return NULL;
+}
+
+/*
+ * obtain a password from the user
+ */
+
+static int _winbind_read_password(pam_handle_t * pamh
+ ,unsigned int ctrl
+ ,const char *comment
+ ,const char *prompt1
+ ,const char *prompt2
+ ,const char **pass)
+{
+ int authtok_flag;
+ int retval;
+ const char *item;
+ char *token;
+
+ /*
+ * make sure nothing inappropriate gets returned
+ */
+
+ *pass = token = NULL;
+
+ /*
+ * which authentication token are we getting?
+ */
+
+ authtok_flag = on(WINBIND__OLD_PASSWORD, ctrl) ? PAM_OLDAUTHTOK : PAM_AUTHTOK;
+
+ /*
+ * should we obtain the password from a PAM item ?
+ */
+
+ if (on(WINBIND_TRY_FIRST_PASS_ARG, ctrl) || on(WINBIND_USE_FIRST_PASS_ARG, ctrl)) {
+ retval = pam_get_item(pamh, authtok_flag, (const void **) &item);
+ if (retval != PAM_SUCCESS) {
+ /* very strange. */
+ _pam_log(LOG_ALERT,
+ "pam_get_item returned error to unix-read-password"
+ );
+ return retval;
+ } else if (item != NULL) { /* we have a password! */
+ *pass = item;
+ item = NULL;
+ return PAM_SUCCESS;
+ } else if (on(WINBIND_USE_FIRST_PASS_ARG, ctrl)) {
+ return PAM_AUTHTOK_RECOVER_ERR; /* didn't work */
+ } else if (on(WINBIND_USE_AUTHTOK_ARG, ctrl)
+ && off(WINBIND__OLD_PASSWORD, ctrl)) {
+ return PAM_AUTHTOK_RECOVER_ERR;
+ }
+ }
+ /*
+ * getting here implies we will have to get the password from the
+ * user directly.
+ */
+
+ {
+ struct pam_message msg[3], *pmsg[3];
+ struct pam_response *resp;
+ int i, replies;
+
+ /* prepare to converse */
+
+ if (comment != NULL) {
+ pmsg[0] = &msg[0];
+ msg[0].msg_style = PAM_TEXT_INFO;
+ msg[0].msg = comment;
+ i = 1;
+ } else {
+ i = 0;
+ }
+
+ pmsg[i] = &msg[i];
+ msg[i].msg_style = PAM_PROMPT_ECHO_OFF;
+ msg[i++].msg = prompt1;
+ replies = 1;
+
+ if (prompt2 != NULL) {
+ pmsg[i] = &msg[i];
+ msg[i].msg_style = PAM_PROMPT_ECHO_OFF;
+ msg[i++].msg = prompt2;
+ ++replies;
+ }
+ /* so call the conversation expecting i responses */
+ resp = NULL;
+ retval = converse(pamh, i, pmsg, &resp);
+
+ if (resp != NULL) {
+
+ /* interpret the response */
+
+ if (retval == PAM_SUCCESS) { /* a good conversation */
+
+ token = x_strdup(resp[i - replies].resp);
+ if (token != NULL) {
+ if (replies == 2) {
+
+ /* verify that password entered correctly */
+ if (!resp[i - 1].resp
+ || strcmp(token, resp[i - 1].resp)) {
+ _pam_delete(token); /* mistyped */
+ retval = PAM_AUTHTOK_RECOVER_ERR;
+ _make_remark(pamh ,PAM_ERROR_MSG, MISTYPED_PASS);
+ }
+ }
+ } else {
+ _pam_log(LOG_NOTICE
+ ,"could not recover authentication token");
+ }
+
+ }
+ /*
+ * tidy up the conversation (resp_retcode) is ignored
+ * -- what is it for anyway? AGM
+ */
+
+ _pam_drop_reply(resp, i);
+
+ } else {
+ retval = (retval == PAM_SUCCESS)
+ ? PAM_AUTHTOK_RECOVER_ERR : retval;
+ }
+ }
+
+ if (retval != PAM_SUCCESS) {
+ if (on(WINBIND_DEBUG_ARG, ctrl))
+ _pam_log(LOG_DEBUG,
+ "unable to obtain a password");
+ return retval;
+ }
+ /* 'token' is the entered password */
+
+ /* we store this password as an item */
+
+ retval = pam_set_item(pamh, authtok_flag, token);
+ _pam_delete(token); /* clean it up */
+ if (retval != PAM_SUCCESS
+ || (retval = pam_get_item(pamh, authtok_flag
+ ,(const void **) &item))
+ != PAM_SUCCESS) {
+
+ _pam_log(LOG_CRIT, "error manipulating password");
+ return retval;
+
+ }
+
+ *pass = item;
+ item = NULL; /* break link to password */
+
+ return PAM_SUCCESS;
+}
+
+PAM_EXTERN
+int pam_sm_authenticate(pam_handle_t *pamh, int flags,
+ int argc, const char **argv)
+{
+ const char *username;
+ const char *password;
+ int retval = PAM_AUTH_ERR;
+
+ /* parse arguments */
+ int ctrl = _pam_parse(argc, argv);
+
+ /* Get the username */
+ retval = pam_get_user(pamh, &username, NULL);
+ if ((retval != PAM_SUCCESS) || (!username)) {
+ if (ctrl & WINBIND_DEBUG_ARG)
+ _pam_log(LOG_DEBUG,"can not get the username");
+ return PAM_SERVICE_ERR;
+ }
+
+ retval = _winbind_read_password(pamh, ctrl, NULL,
+ "Password: ", NULL,
+ &password);
+
+ if (retval != PAM_SUCCESS) {
+ _pam_log(LOG_ERR, "Could not retrieve user's password");
+ return PAM_AUTHTOK_ERR;
+ }
+
+ if (ctrl & WINBIND_DEBUG_ARG) {
+
+ /* Let's not give too much away in the log file */
+
+#ifdef DEBUG_PASSWORD
+ _pam_log(LOG_INFO, "Verify user `%s' with password `%s'",
+ username, password);
+#else
+ _pam_log(LOG_INFO, "Verify user `%s'", username);
+#endif
+ }
+
+ /* Now use the username to look up password */
+ return winbind_auth_request(username, password, ctrl);
+}
+
+PAM_EXTERN
+int pam_sm_setcred(pam_handle_t *pamh, int flags,
+ int argc, const char **argv)
+{
+ return PAM_SUCCESS;
+}
+
+/*
+ * Account management. We want to verify that the account exists
+ * before returning PAM_SUCCESS
+ */
+PAM_EXTERN
+int pam_sm_acct_mgmt(pam_handle_t *pamh, int flags,
+ int argc, const char **argv)
+{
+ const char *username;
+ int retval = PAM_USER_UNKNOWN;
+
+ /* parse arguments */
+ int ctrl = _pam_parse(argc, argv);
+
+ /* Get the username */
+ retval = pam_get_user(pamh, &username, NULL);
+ if ((retval != PAM_SUCCESS) || (!username)) {
+ if (ctrl & WINBIND_DEBUG_ARG)
+ _pam_log(LOG_DEBUG,"can not get the username");
+ return PAM_SERVICE_ERR;
+ }
+
+ /* Verify the username */
+ retval = valid_user(username);
+ switch (retval) {
+ case -1:
+ /* some sort of system error. The log was already printed */
+ return PAM_SERVICE_ERR;
+ case 1:
+ /* the user does not exist */
+ if (ctrl & WINBIND_DEBUG_ARG)
+ _pam_log(LOG_NOTICE, "user `%s' not found",
+ username);
+ if (ctrl & WINBIND_UNKNOWN_OK_ARG)
+ return PAM_IGNORE;
+ return PAM_USER_UNKNOWN;
+ case 0:
+ /* Otherwise, the authentication looked good */
+ _pam_log(LOG_NOTICE, "user '%s' granted acces", username);
+ return PAM_SUCCESS;
+ default:
+ /* we don't know anything about this return value */
+ _pam_log(LOG_ERR, "internal module error (retval = %d, user = `%s'",
+ retval, username);
+ return PAM_SERVICE_ERR;
+ }
+
+ /* should not be reached */
+ return PAM_IGNORE;
+}
+PAM_EXTERN
+int pam_sm_open_session(pam_handle_t *pamh, int flags,
+ int argc, const char **argv)
+{
+ /* parse arguments */
+ int ctrl = _pam_parse(argc, argv);
+ if (ctrl & WINBIND_DEBUG_ARG)
+ _pam_log(LOG_DEBUG,"libpam_winbind:pam_sm_open_session handler");
+ return PAM_SUCCESS;
+}
+PAM_EXTERN
+int pam_sm_close_session(pam_handle_t *pamh, int flags,
+ int argc, const char **argv)
+{
+ /* parse arguments */
+ int ctrl = _pam_parse(argc, argv);
+ if (ctrl & WINBIND_DEBUG_ARG)
+ _pam_log(LOG_DEBUG,"libpam_winbind:pam_sm_close_session handler");
+ return PAM_SUCCESS;
+}
+
+
+
+PAM_EXTERN int pam_sm_chauthtok(pam_handle_t * pamh, int flags,
+ int argc, const char **argv)
+{
+ unsigned int lctrl;
+ int retval;
+ unsigned int ctrl = _pam_parse(argc, argv);
+
+ /* <DO NOT free() THESE> */
+ const char *user;
+ char *pass_old, *pass_new;
+ /* </DO NOT free() THESE> */
+
+ char *Announce;
+
+ int retry = 0;
+
+ /*
+ * First get the name of a user
+ */
+ retval = pam_get_user(pamh, &user, "Username: ");
+ if (retval == PAM_SUCCESS) {
+ if (user == NULL) {
+ _pam_log(LOG_ERR, "username was NULL!");
+ return PAM_USER_UNKNOWN;
+ }
+ if (retval == PAM_SUCCESS && on(WINBIND_DEBUG_ARG, ctrl))
+ _pam_log(LOG_DEBUG, "username [%s] obtained",
+ user);
+ } else {
+ if (on(WINBIND_DEBUG_ARG, ctrl))
+ _pam_log(LOG_DEBUG,
+ "password - could not identify user");
+ return retval;
+ }
+
+ /*
+ * obtain and verify the current password (OLDAUTHTOK) for
+ * the user.
+ */
+
+ if (flags & PAM_PRELIM_CHECK) {
+
+ /* instruct user what is happening */
+#define greeting "Changing password for "
+ Announce = (char *) malloc(sizeof(greeting) + strlen(user));
+ if (Announce == NULL) {
+ _pam_log(LOG_CRIT,
+ "password - out of memory");
+ return PAM_BUF_ERR;
+ }
+ (void) strcpy(Announce, greeting);
+ (void) strcpy(Announce + sizeof(greeting) - 1, user);
+#undef greeting
+
+ lctrl = ctrl | WINBIND__OLD_PASSWORD;
+ retval = _winbind_read_password(pamh, lctrl
+ ,Announce
+ ,"(current) NT password: "
+ ,NULL
+ ,(const char **) &pass_old);
+ free(Announce);
+
+ if (retval != PAM_SUCCESS) {
+ _pam_log(LOG_NOTICE
+ ,"password - (old) token not obtained");
+ return retval;
+ }
+ /* verify that this is the password for this user */
+
+ retval = winbind_auth_request(user, pass_old, ctrl);
+
+ if (retval != PAM_ACCT_EXPIRED
+ && retval != PAM_AUTHTOK_EXPIRED
+ && retval != PAM_NEW_AUTHTOK_REQD
+ && retval != PAM_SUCCESS) {
+ pass_old = NULL;
+ return retval;
+ }
+
+ retval = pam_set_item(pamh, PAM_OLDAUTHTOK, (const void *) pass_old);
+ pass_old = NULL;
+ if (retval != PAM_SUCCESS) {
+ _pam_log(LOG_CRIT,
+ "failed to set PAM_OLDAUTHTOK");
+ }
+ } else if (flags & PAM_UPDATE_AUTHTOK) {
+
+ /*
+ * obtain the proposed password
+ */
+
+ /*
+ * get the old token back.
+ */
+
+ retval = pam_get_item(pamh, PAM_OLDAUTHTOK
+ ,(const void **) &pass_old);
+
+ if (retval != PAM_SUCCESS) {
+ _pam_log(LOG_NOTICE, "user not authenticated");
+ return retval;
+ }
+
+ lctrl = ctrl;
+
+ if (on(WINBIND_USE_AUTHTOK_ARG, lctrl)) {
+ ctrl = WINBIND_USE_FIRST_PASS_ARG | lctrl;
+ }
+ retry = 0;
+ retval = PAM_AUTHTOK_ERR;
+ while ((retval != PAM_SUCCESS) && (retry++ < MAX_PASSWD_TRIES)) {
+ /*
+ * use_authtok is to force the use of a previously entered
+ * password -- needed for pluggable password strength checking
+ */
+
+ retval = _winbind_read_password(pamh, lctrl
+ ,NULL
+ ,"Enter new NT password: "
+ ,"Retype new NT password: "
+ ,(const char **) &pass_new);
+
+ if (retval != PAM_SUCCESS) {
+ if (on(WINBIND_DEBUG_ARG, ctrl)) {
+ _pam_log(LOG_ALERT
+ ,"password - new password not obtained");
+ }
+ pass_old = NULL;/* tidy up */
+ return retval;
+ }
+
+ /*
+ * At this point we know who the user is and what they
+ * propose as their new password. Verify that the new
+ * password is acceptable.
+ */
+
+ if (pass_new[0] == '\0') {/* "\0" password = NULL */
+ pass_new = NULL;
+ }
+ }
+
+ /*
+ * By reaching here we have approved the passwords and must now
+ * rebuild the password database file.
+ */
+
+ retval = winbind_chauthtok_request(user, pass_old, pass_new, ctrl);
+ _pam_overwrite(pass_new);
+ _pam_overwrite(pass_old);
+ pass_old = pass_new = NULL;
+ } else {
+ retval = PAM_SERVICE_ERR;
+ }
+
+ return retval;
+}
+
+#ifdef PAM_STATIC
+
+/* static module data */
+
+struct pam_module _pam_winbind_modstruct = {
+ MODULE_NAME,
+ pam_sm_authenticate,
+ pam_sm_setcred,
+ pam_sm_acct_mgmt,
+ pam_sm_open_session,
+ pam_sm_close_session,
+ pam_sm_chauthtok
+};
+
+#endif
+
+/*
+ * Copyright (c) Andrew Tridgell <tridge@samba.org> 2000
+ * Copyright (c) Tim Potter <tpot@samba.org> 2000
+ * Copyright (c) Andrew Bartlettt <abartlet@samba.org> 2002
+ * Copyright (c) Jan Rêkorajski 1999.
+ * Copyright (c) Andrew G. Morgan 1996-8.
+ * Copyright (c) Alex O. Yuriev, 1996.
+ * Copyright (c) Cristian Gafton 1996.
+ * Copyright (C) Elliot Lee <sopwith@redhat.com> 1996, Red Hat Software.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, and the entire permission notice in its entirety,
+ * including the disclaimer of warranties.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * ALTERNATIVELY, this product may be distributed under the terms of
+ * the GNU Public License, in which case the provisions of the GPL are
+ * required INSTEAD OF the above restrictions. (This clause is
+ * necessary due to a potential bad interaction between the GPL and
+ * the restrictions contained in a BSD-style copyright.)
+ *
+ * THIS SOFTWARE IS PROVIDED `AS IS'' AND ANY EXPRESS OR IMPLIED
+ * 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 BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (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/source/nsswitch/pam_winbind.h b/source/nsswitch/pam_winbind.h
new file mode 100644
index 00000000000..0afcceb6aa2
--- /dev/null
+++ b/source/nsswitch/pam_winbind.h
@@ -0,0 +1,96 @@
+/* pam_winbind header file
+ (Solaris needs some macros from Linux for common PAM code)
+
+ Shirish Kalele 2000
+*/
+
+#ifdef HAVE_FEATURES_H
+#include <features.h>
+#endif
+
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <syslog.h>
+#include <stdarg.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <errno.h>
+
+#include <config.h>
+
+#define MODULE_NAME "pam_winbind"
+#define PAM_SM_AUTH
+#define PAM_SM_ACCOUNT
+#define PAM_SM_PASSWORD
+
+#if defined(SUNOS5) || defined(SUNOS4) || defined(HPUX) || defined(FREEBSD)
+
+/* Solaris always uses dynamic pam modules */
+#define PAM_EXTERN extern
+#include <security/pam_appl.h>
+
+#ifndef PAM_AUTHTOK_RECOVER_ERR
+#define PAM_AUTHTOK_RECOVER_ERR PAM_AUTHTOK_RECOVERY_ERR
+#endif
+
+#endif
+
+#ifdef HAVE_SECURITY_PAM_MODULES_H
+#include <security/pam_modules.h>
+#endif
+
+#ifdef HAVE_SECURITY__PAM_MACROS_H
+#include <security/_pam_macros.h>
+#else
+/* Define required macros from (Linux PAM 0.68) security/_pam_macros.h */
+#define _pam_drop_reply(/* struct pam_response * */ reply, /* int */ replies) \
+do { \
+ int reply_i; \
+ \
+ for (reply_i=0; reply_i<replies; ++reply_i) { \
+ if (reply[reply_i].resp) { \
+ _pam_overwrite(reply[reply_i].resp); \
+ free(reply[reply_i].resp); \
+ } \
+ } \
+ if (reply) \
+ free(reply); \
+} while (0)
+
+#define _pam_overwrite(x) \
+do { \
+ register char *__xx__; \
+ if ((__xx__=(x))) \
+ while (*__xx__) \
+ *__xx__++ = '\0'; \
+} while (0)
+
+/*
+ * Don't just free it, forget it too.
+ */
+
+#define _pam_drop(X) SAFE_FREE(X)
+
+#define x_strdup(s) ( (s) ? strdup(s):NULL )
+#endif
+
+#define WINBIND_DEBUG_ARG (1<<0)
+#define WINBIND_USE_AUTHTOK_ARG (1<<1)
+#define WINBIND_UNKNOWN_OK_ARG (1<<2)
+#define WINBIND_TRY_FIRST_PASS_ARG (1<<3)
+#define WINBIND_USE_FIRST_PASS_ARG (1<<4)
+#define WINBIND__OLD_PASSWORD (1<<5)
+
+/*
+ * here is the string to inform the user that the new passwords they
+ * typed were not the same.
+ */
+
+#define MISTYPED_PASS "Sorry, passwords do not match"
+
+#define on(x, y) (x & y)
+#define off(x, y) (!(x & y))
+
+#include "winbind_client.h"
diff --git a/source/nsswitch/wb_client.c b/source/nsswitch/wb_client.c
new file mode 100644
index 00000000000..32dfc8decac
--- /dev/null
+++ b/source/nsswitch/wb_client.c
@@ -0,0 +1,693 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ winbind client code
+
+ Copyright (C) Tim Potter 2000
+ Copyright (C) Andrew Tridgell 2000
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 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 Library General Public
+ License along with this library; if not, write to the
+ Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+*/
+
+#include "includes.h"
+#include "nsswitch/winbind_nss.h"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_WINBIND
+
+extern DOM_SID global_sid_NULL; /* NULL sid */
+
+NSS_STATUS winbindd_request(int req_type,
+ struct winbindd_request *request,
+ struct winbindd_response *response);
+
+/* Call winbindd to convert a name to a sid */
+
+BOOL winbind_lookup_name(const char *dom_name, const char *name, DOM_SID *sid,
+ enum SID_NAME_USE *name_type)
+{
+ struct winbindd_request request;
+ struct winbindd_response response;
+ NSS_STATUS result;
+
+ if (!sid || !name_type)
+ return False;
+
+ /* Send off request */
+
+ ZERO_STRUCT(request);
+ ZERO_STRUCT(response);
+
+ fstrcpy(request.data.name.dom_name, dom_name);
+ fstrcpy(request.data.name.name, name);
+
+ if ((result = winbindd_request(WINBINDD_LOOKUPNAME, &request,
+ &response)) == NSS_STATUS_SUCCESS) {
+ if (!string_to_sid(sid, response.data.sid.sid))
+ return False;
+ *name_type = (enum SID_NAME_USE)response.data.sid.type;
+ }
+
+ return result == NSS_STATUS_SUCCESS;
+}
+
+/* Call winbindd to convert sid to name */
+
+BOOL winbind_lookup_sid(const DOM_SID *sid,
+ fstring dom_name, fstring name,
+ enum SID_NAME_USE *name_type)
+{
+ struct winbindd_request request;
+ struct winbindd_response response;
+ NSS_STATUS result;
+ fstring sid_str;
+
+ /* Initialise request */
+
+ ZERO_STRUCT(request);
+ ZERO_STRUCT(response);
+
+ sid_to_string(sid_str, sid);
+ fstrcpy(request.data.sid, sid_str);
+
+ /* Make request */
+
+ result = winbindd_request(WINBINDD_LOOKUPSID, &request, &response);
+
+ /* Copy out result */
+
+ if (result == NSS_STATUS_SUCCESS) {
+ fstrcpy(dom_name, response.data.name.dom_name);
+ fstrcpy(name, response.data.name.name);
+ *name_type = (enum SID_NAME_USE)response.data.name.type;
+
+ DEBUG(10, ("winbind_lookup_sid: SUCCESS: SID %s -> %s %s\n",
+ sid_str, dom_name, name));
+ }
+
+ return (result == NSS_STATUS_SUCCESS);
+}
+
+/* Call winbindd to convert SID to uid */
+
+BOOL winbind_sid_to_uid(uid_t *puid, const DOM_SID *sid)
+{
+ struct winbindd_request request;
+ struct winbindd_response response;
+ int result;
+ fstring sid_str;
+
+ if (!puid)
+ return False;
+
+ /* Initialise request */
+
+ ZERO_STRUCT(request);
+ ZERO_STRUCT(response);
+
+ sid_to_string(sid_str, sid);
+ fstrcpy(request.data.sid, sid_str);
+
+ /* Make request */
+
+ result = winbindd_request(WINBINDD_SID_TO_UID, &request, &response);
+
+ /* Copy out result */
+
+ if (result == NSS_STATUS_SUCCESS) {
+ *puid = response.data.uid;
+ }
+
+ return (result == NSS_STATUS_SUCCESS);
+}
+
+/* Call winbindd to convert uid to sid */
+
+BOOL winbind_uid_to_sid(DOM_SID *sid, uid_t uid)
+{
+ struct winbindd_request request;
+ struct winbindd_response response;
+ int result;
+
+ if (!sid)
+ return False;
+
+ /* Initialise request */
+
+ ZERO_STRUCT(request);
+ ZERO_STRUCT(response);
+
+ request.data.uid = uid;
+
+ /* Make request */
+
+ result = winbindd_request(WINBINDD_UID_TO_SID, &request, &response);
+
+ /* Copy out result */
+
+ if (result == NSS_STATUS_SUCCESS) {
+ if (!string_to_sid(sid, response.data.sid.sid))
+ return False;
+ } else {
+ sid_copy(sid, &global_sid_NULL);
+ }
+
+ return (result == NSS_STATUS_SUCCESS);
+}
+
+/* Call winbindd to convert SID to gid */
+
+BOOL winbind_sid_to_gid(gid_t *pgid, const DOM_SID *sid)
+{
+ struct winbindd_request request;
+ struct winbindd_response response;
+ int result;
+ fstring sid_str;
+
+ if (!pgid)
+ return False;
+
+ /* Initialise request */
+
+ ZERO_STRUCT(request);
+ ZERO_STRUCT(response);
+
+ sid_to_string(sid_str, sid);
+ fstrcpy(request.data.sid, sid_str);
+
+ /* Make request */
+
+ result = winbindd_request(WINBINDD_SID_TO_GID, &request, &response);
+
+ /* Copy out result */
+
+ if (result == NSS_STATUS_SUCCESS) {
+ *pgid = response.data.gid;
+ }
+
+ return (result == NSS_STATUS_SUCCESS);
+}
+
+/* Call winbindd to convert gid to sid */
+
+BOOL winbind_gid_to_sid(DOM_SID *sid, gid_t gid)
+{
+ struct winbindd_request request;
+ struct winbindd_response response;
+ int result;
+
+ if (!sid)
+ return False;
+
+ /* Initialise request */
+
+ ZERO_STRUCT(request);
+ ZERO_STRUCT(response);
+
+ request.data.gid = gid;
+
+ /* Make request */
+
+ result = winbindd_request(WINBINDD_GID_TO_SID, &request, &response);
+
+ /* Copy out result */
+
+ if (result == NSS_STATUS_SUCCESS) {
+ if (!string_to_sid(sid, response.data.sid.sid))
+ return False;
+ } else {
+ sid_copy(sid, &global_sid_NULL);
+ }
+
+ return (result == NSS_STATUS_SUCCESS);
+}
+
+BOOL winbind_allocate_rid(uint32 *rid)
+{
+ struct winbindd_request request;
+ struct winbindd_response response;
+ int result;
+
+ /* Initialise request */
+
+ ZERO_STRUCT(request);
+ ZERO_STRUCT(response);
+
+ /* Make request */
+
+ result = winbindd_request(WINBINDD_ALLOCATE_RID, &request, &response);
+
+ if (result != NSS_STATUS_SUCCESS)
+ return False;
+
+ /* Copy out result */
+ *rid = response.data.rid;
+
+ return True;
+}
+
+/* Fetch the list of groups a user is a member of from winbindd. This is
+ used by winbind_getgroups. */
+
+static int wb_getgroups(const char *user, gid_t **groups)
+{
+ struct winbindd_request request;
+ struct winbindd_response response;
+ int result;
+
+ /* Call winbindd */
+
+ fstrcpy(request.data.username, user);
+
+ ZERO_STRUCT(response);
+
+ result = winbindd_request(WINBINDD_GETGROUPS, &request, &response);
+
+ if (result == NSS_STATUS_SUCCESS) {
+
+ /* Return group list. Don't forget to free the group list
+ when finished. */
+
+ *groups = (gid_t *)response.extra_data;
+ return response.data.num_entries;
+ }
+
+ return -1;
+}
+
+/* Call winbindd to initialise group membership. This is necessary for
+ some systems (i.e RH5.2) that do not have an initgroups function as part
+ of the nss extension. In RH5.2 this is implemented using getgrent()
+ which can be amazingly inefficient as well as having problems with
+ username case. */
+
+int winbind_initgroups(char *user, gid_t gid)
+{
+ gid_t *tgr, *groups = NULL;
+ int result;
+
+ /* Call normal initgroups if we are a local user */
+
+ if (!strchr(user, *lp_winbind_separator())) {
+ return initgroups(user, gid);
+ }
+
+ result = wb_getgroups(user, &groups);
+
+ DEBUG(10,("winbind_getgroups: %s: result = %s\n", user,
+ result == -1 ? "FAIL" : "SUCCESS"));
+
+ if (result != -1) {
+ int ngroups = result, i;
+ BOOL is_member = False;
+
+ /* Check to see if the passed gid is already in the list */
+
+ for (i = 0; i < ngroups; i++) {
+ if (groups[i] == gid) {
+ is_member = True;
+ }
+ }
+
+ /* Add group to list if necessary */
+
+ if (!is_member) {
+ tgr = (gid_t *)Realloc(groups, sizeof(gid_t) * ngroups + 1);
+
+ if (!tgr) {
+ errno = ENOMEM;
+ result = -1;
+ goto done;
+ }
+ else groups = tgr;
+
+ groups[ngroups] = gid;
+ ngroups++;
+ }
+
+ /* Set the groups */
+
+ if (sys_setgroups(ngroups, groups) == -1) {
+ errno = EPERM;
+ result = -1;
+ goto done;
+ }
+
+ } else {
+
+ /* The call failed. Set errno to something so we don't get
+ a bogus value from the last failed system call. */
+
+ errno = EIO;
+ }
+
+ /* Free response data if necessary */
+
+ done:
+ SAFE_FREE(groups);
+
+ return result;
+}
+
+/* Return a list of groups the user is a member of. This function is
+ useful for large systems where inverting the group database would be too
+ time consuming. If size is zero, list is not modified and the total
+ number of groups for the user is returned. */
+
+int winbind_getgroups(const char *user, gid_t **list)
+{
+ /*
+ * Don't do the lookup if the name has no separator _and_ we are not in
+ * 'winbind use default domain' mode.
+ */
+
+ if (!(strchr(user, *lp_winbind_separator()) || lp_winbind_use_default_domain()))
+ return -1;
+
+ /* Fetch list of groups */
+
+ return wb_getgroups(user, list);
+}
+
+/**********************************************************************
+ simple wrapper function to see if winbindd is alive
+**********************************************************************/
+
+BOOL winbind_ping( void )
+{
+ NSS_STATUS result;
+
+ result = winbindd_request(WINBINDD_PING, NULL, NULL);
+
+ return result == NSS_STATUS_SUCCESS;
+}
+
+/**********************************************************************
+ Ask winbindd to create a local user
+**********************************************************************/
+
+BOOL winbind_create_user( const char *name, uint32 *rid )
+{
+ struct winbindd_request request;
+ struct winbindd_response response;
+ NSS_STATUS result;
+
+ if ( !lp_winbind_enable_local_accounts() )
+ return False;
+
+ if ( !name )
+ return False;
+
+ DEBUG(10,("winbind_create_user: %s\n", name));
+
+ ZERO_STRUCT(request);
+ ZERO_STRUCT(response);
+
+ /* see if the caller wants a new RID returned */
+
+ if ( rid )
+ request.flags = WBFLAG_ALLOCATE_RID;
+
+ fstrcpy( request.data.acct_mgt.username, name );
+ fstrcpy( request.data.acct_mgt.groupname, "" );
+
+ result = winbindd_request( WINBINDD_CREATE_USER, &request, &response);
+
+ if ( rid )
+ *rid = response.data.rid;
+
+ return result == NSS_STATUS_SUCCESS;
+}
+
+/**********************************************************************
+ Ask winbindd to create a local group
+**********************************************************************/
+
+BOOL winbind_create_group( const char *name, uint32 *rid )
+{
+ struct winbindd_request request;
+ struct winbindd_response response;
+ NSS_STATUS result;
+
+ if ( !lp_winbind_enable_local_accounts() )
+ return False;
+
+ if ( !name )
+ return False;
+
+ DEBUG(10,("winbind_create_group: %s\n", name));
+
+ ZERO_STRUCT(request);
+ ZERO_STRUCT(response);
+
+ /* see if the caller wants a new RID returned */
+
+ if ( rid )
+ request.flags = WBFLAG_ALLOCATE_RID;
+
+ fstrcpy( request.data.acct_mgt.groupname, name );
+
+
+ result = winbindd_request( WINBINDD_CREATE_GROUP, &request, &response);
+
+ if ( rid )
+ *rid = response.data.rid;
+
+ return result == NSS_STATUS_SUCCESS;
+}
+
+/**********************************************************************
+ Ask winbindd to add a user to a local group
+**********************************************************************/
+
+BOOL winbind_add_user_to_group( const char *user, const char *group )
+{
+ struct winbindd_request request;
+ struct winbindd_response response;
+ NSS_STATUS result;
+
+ if ( !lp_winbind_enable_local_accounts() )
+ return False;
+
+ if ( !user || !group )
+ return False;
+
+ ZERO_STRUCT(request);
+ ZERO_STRUCT(response);
+
+ DEBUG(10,("winbind_add_user_to_group: user(%s), group(%s) \n",
+ user, group));
+
+ fstrcpy( request.data.acct_mgt.username, user );
+ fstrcpy( request.data.acct_mgt.groupname, group );
+
+ result = winbindd_request( WINBINDD_ADD_USER_TO_GROUP, &request, &response);
+
+ return result == NSS_STATUS_SUCCESS;
+}
+
+/**********************************************************************
+ Ask winbindd to remove a user to a local group
+**********************************************************************/
+
+BOOL winbind_remove_user_from_group( const char *user, const char *group )
+{
+ struct winbindd_request request;
+ struct winbindd_response response;
+ NSS_STATUS result;
+
+ if ( !lp_winbind_enable_local_accounts() )
+ return False;
+
+ if ( !user || !group )
+ return False;
+
+ ZERO_STRUCT(request);
+ ZERO_STRUCT(response);
+
+ DEBUG(10,("winbind_remove_user_from_group: user(%s), group(%s) \n",
+ user, group));
+
+ ZERO_STRUCT(response);
+
+ fstrcpy( request.data.acct_mgt.username, user );
+ fstrcpy( request.data.acct_mgt.groupname, group );
+
+ result = winbindd_request( WINBINDD_REMOVE_USER_FROM_GROUP, &request, &response);
+
+ return result == NSS_STATUS_SUCCESS;
+}
+
+/**********************************************************************
+ Ask winbindd to set the primary group for a user local user
+**********************************************************************/
+
+BOOL winbind_set_user_primary_group( const char *user, const char *group )
+{
+ struct winbindd_request request;
+ struct winbindd_response response;
+ NSS_STATUS result;
+
+ if ( !lp_winbind_enable_local_accounts() )
+ return False;
+
+ if ( !user || !group )
+ return False;
+
+ ZERO_STRUCT(request);
+ ZERO_STRUCT(response);
+
+ DEBUG(10,("winbind_set_user_primary_group: user(%s), group(%s) \n",
+ user, group));
+
+ fstrcpy( request.data.acct_mgt.username, user );
+ fstrcpy( request.data.acct_mgt.groupname, group );
+
+ result = winbindd_request( WINBINDD_SET_USER_PRIMARY_GROUP, &request, &response);
+
+ return result == NSS_STATUS_SUCCESS;
+}
+
+
+/**********************************************************************
+ Ask winbindd to remove a user from its lists of accounts
+**********************************************************************/
+
+BOOL winbind_delete_user( const char *user )
+{
+ struct winbindd_request request;
+ struct winbindd_response response;
+ NSS_STATUS result;
+
+ if ( !lp_winbind_enable_local_accounts() )
+ return False;
+
+ if ( !user )
+ return False;
+
+ ZERO_STRUCT(request);
+ ZERO_STRUCT(response);
+
+ DEBUG(10,("winbind_delete_user: user (%s)\n", user));
+
+ fstrcpy( request.data.acct_mgt.username, user );
+
+ result = winbindd_request( WINBINDD_DELETE_USER, &request, &response);
+
+ return result == NSS_STATUS_SUCCESS;
+}
+
+/**********************************************************************
+ Ask winbindd to remove a group from its lists of accounts
+**********************************************************************/
+
+BOOL winbind_delete_group( const char *group )
+{
+ struct winbindd_request request;
+ struct winbindd_response response;
+ NSS_STATUS result;
+
+ if ( !lp_winbind_enable_local_accounts() )
+ return False;
+
+ if ( !group )
+ return False;
+
+ ZERO_STRUCT(request);
+ ZERO_STRUCT(response);
+
+ DEBUG(10,("winbind_delete_group: group (%s)\n", group));
+
+ fstrcpy( request.data.acct_mgt.groupname, group );
+
+ result = winbindd_request( WINBINDD_DELETE_GROUP, &request, &response);
+
+ return result == NSS_STATUS_SUCCESS;
+}
+
+/***********************************************************************/
+/* Call winbindd to convert SID to uid. Do not allocate */
+
+BOOL winbind_sid_to_uid_query(uid_t *puid, const DOM_SID *sid)
+{
+ struct winbindd_request request;
+ struct winbindd_response response;
+ int result;
+ fstring sid_str;
+
+ if (!puid)
+ return False;
+
+ /* Initialise request */
+
+ ZERO_STRUCT(request);
+ ZERO_STRUCT(response);
+
+ sid_to_string(sid_str, sid);
+ fstrcpy(request.data.sid, sid_str);
+
+ request.flags = WBFLAG_QUERY_ONLY;
+
+ /* Make request */
+
+ result = winbindd_request(WINBINDD_SID_TO_UID, &request, &response);
+
+ /* Copy out result */
+
+ if (result == NSS_STATUS_SUCCESS) {
+ *puid = response.data.uid;
+ }
+
+ return (result == NSS_STATUS_SUCCESS);
+}
+
+/* Call winbindd to convert SID to gid. Do not allocate */
+
+BOOL winbind_sid_to_gid_query(gid_t *pgid, const DOM_SID *sid)
+{
+ struct winbindd_request request;
+ struct winbindd_response response;
+ int result;
+ fstring sid_str;
+
+ if (!pgid)
+ return False;
+
+ /* Initialise request */
+
+ ZERO_STRUCT(request);
+ ZERO_STRUCT(response);
+
+ sid_to_string(sid_str, sid);
+ fstrcpy(request.data.sid, sid_str);
+
+ request.flags = WBFLAG_QUERY_ONLY;
+
+ /* Make request */
+
+ result = winbindd_request(WINBINDD_SID_TO_GID, &request, &response);
+
+ /* Copy out result */
+
+ if (result == NSS_STATUS_SUCCESS) {
+ *pgid = response.data.gid;
+ }
+
+ return (result == NSS_STATUS_SUCCESS);
+}
+
+/***********************************************************************/
+
diff --git a/source/nsswitch/wb_common.c b/source/nsswitch/wb_common.c
new file mode 100644
index 00000000000..40221b69feb
--- /dev/null
+++ b/source/nsswitch/wb_common.c
@@ -0,0 +1,494 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ winbind client common code
+
+ Copyright (C) Tim Potter 2000
+ Copyright (C) Andrew Tridgell 2000
+ Copyright (C) Andrew Bartlett 2002
+
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 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 Library General Public
+ License along with this library; if not, write to the
+ Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+*/
+
+#include "winbind_client.h"
+
+/* Global variables. These are effectively the client state information */
+
+int winbindd_fd = -1; /* fd for winbindd socket */
+
+/* Free a response structure */
+
+void free_response(struct winbindd_response *response)
+{
+ /* Free any allocated extra_data */
+
+ if (response)
+ SAFE_FREE(response->extra_data);
+}
+
+/* Initialise a request structure */
+
+void init_request(struct winbindd_request *request, int request_type)
+{
+ request->length = sizeof(struct winbindd_request);
+
+ request->cmd = (enum winbindd_cmd)request_type;
+ request->pid = getpid();
+
+}
+
+/* Initialise a response structure */
+
+void init_response(struct winbindd_response *response)
+{
+ /* Initialise return value */
+
+ response->result = WINBINDD_ERROR;
+}
+
+/* Close established socket */
+
+void close_sock(void)
+{
+ if (winbindd_fd != -1) {
+ close(winbindd_fd);
+ winbindd_fd = -1;
+ }
+}
+
+/* Make sure socket handle isn't stdin, stdout or stderr */
+#define RECURSION_LIMIT 3
+
+static int make_nonstd_fd_internals(int fd, int limit /* Recursion limiter */)
+{
+ int new_fd;
+ if (fd >= 0 && fd <= 2) {
+#ifdef F_DUPFD
+ if ((new_fd = fcntl(fd, F_DUPFD, 3)) == -1) {
+ return -1;
+ }
+ /* Paranoia */
+ if (new_fd < 3) {
+ close(new_fd);
+ return -1;
+ }
+ close(fd);
+ return new_fd;
+#else
+ if (limit <= 0)
+ return -1;
+
+ new_fd = dup(fd);
+ if (new_fd == -1)
+ return -1;
+
+ /* use the program stack to hold our list of FDs to close */
+ new_fd = make_nonstd_fd_internals(new_fd, limit - 1);
+ close(fd);
+ return new_fd;
+#endif
+ }
+ return fd;
+}
+
+static int make_safe_fd(int fd)
+{
+ int result, flags;
+ int new_fd = make_nonstd_fd_internals(fd, RECURSION_LIMIT);
+ if (new_fd == -1) {
+ close(fd);
+ return -1;
+ }
+ /* Socket should be closed on exec() */
+
+#ifdef FD_CLOEXEC
+ result = flags = fcntl(new_fd, F_GETFD, 0);
+ if (flags >= 0) {
+ flags |= FD_CLOEXEC;
+ result = fcntl( new_fd, F_SETFD, flags );
+ }
+ if (result < 0) {
+ close(new_fd);
+ return -1;
+ }
+#endif
+ return new_fd;
+}
+
+/* Connect to winbindd socket */
+
+static int winbind_named_pipe_sock(const char *dir)
+{
+ struct sockaddr_un sunaddr;
+ struct stat st;
+ pstring path;
+ int fd;
+
+ /* Check permissions on unix socket directory */
+
+ if (lstat(dir, &st) == -1) {
+ return -1;
+ }
+
+ if (!S_ISDIR(st.st_mode) ||
+ (st.st_uid != 0 && st.st_uid != geteuid())) {
+ return -1;
+ }
+
+ /* Connect to socket */
+
+ strncpy(path, dir, sizeof(path) - 1);
+ path[sizeof(path) - 1] = '\0';
+
+ strncat(path, "/", sizeof(path) - 1 - strlen(path));
+ path[sizeof(path) - 1] = '\0';
+
+ strncat(path, WINBINDD_SOCKET_NAME, sizeof(path) - 1 - strlen(path));
+ path[sizeof(path) - 1] = '\0';
+
+ ZERO_STRUCT(sunaddr);
+ sunaddr.sun_family = AF_UNIX;
+ strncpy(sunaddr.sun_path, path, sizeof(sunaddr.sun_path) - 1);
+
+ /* If socket file doesn't exist, don't bother trying to connect
+ with retry. This is an attempt to make the system usable when
+ the winbindd daemon is not running. */
+
+ if (lstat(path, &st) == -1) {
+ return -1;
+ }
+
+ /* Check permissions on unix socket file */
+
+ if (!S_ISSOCK(st.st_mode) ||
+ (st.st_uid != 0 && st.st_uid != geteuid())) {
+ return -1;
+ }
+
+ /* Connect to socket */
+
+ if ((fd = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) {
+ return -1;
+ }
+
+ if ((fd = make_safe_fd( fd)) == -1) {
+ return fd;
+ }
+
+ if (connect(fd, (struct sockaddr *)&sunaddr,
+ sizeof(sunaddr)) == -1) {
+ close(fd);
+ return -1;
+ }
+
+ return fd;
+}
+
+/* Connect to winbindd socket */
+
+int winbind_open_pipe_sock(void)
+{
+#ifdef HAVE_UNIXSOCKET
+ static pid_t our_pid;
+ struct winbindd_request request;
+ struct winbindd_response response;
+ ZERO_STRUCT(request);
+ ZERO_STRUCT(response);
+
+ if (our_pid != getpid()) {
+ close_sock();
+ our_pid = getpid();
+ }
+
+ if (winbindd_fd != -1) {
+ return winbindd_fd;
+ }
+
+ if ((winbindd_fd = winbind_named_pipe_sock(WINBINDD_SOCKET_DIR)) == -1) {
+ return -1;
+ }
+
+ /* version-check the socket */
+
+ if ((winbindd_request(WINBINDD_INTERFACE_VERSION, &request, &response) != NSS_STATUS_SUCCESS) || (response.data.interface_version != WINBIND_INTERFACE_VERSION)) {
+ close_sock();
+ return -1;
+ }
+
+ /* try and get priv pipe */
+
+ if (winbindd_request(WINBINDD_PRIV_PIPE_DIR, &request, &response) == NSS_STATUS_SUCCESS) {
+ int fd;
+ if ((fd = winbind_named_pipe_sock(response.extra_data)) != -1) {
+ close(winbindd_fd);
+ winbindd_fd = fd;
+ }
+ }
+
+ SAFE_FREE(response.extra_data);
+
+ return winbindd_fd;
+#else
+ return -1;
+#endif /* HAVE_UNIXSOCKET */
+}
+
+/* Write data to winbindd socket */
+
+int write_sock(void *buffer, int count)
+{
+ int result, nwritten;
+
+ /* Open connection to winbind daemon */
+
+ restart:
+
+ if (winbind_open_pipe_sock() == -1) {
+ return -1;
+ }
+
+ /* Write data to socket */
+
+ nwritten = 0;
+
+ while(nwritten < count) {
+ struct timeval tv;
+ fd_set r_fds;
+
+ /* Catch pipe close on other end by checking if a read()
+ call would not block by calling select(). */
+
+ FD_ZERO(&r_fds);
+ FD_SET(winbindd_fd, &r_fds);
+ ZERO_STRUCT(tv);
+
+ if (select(winbindd_fd + 1, &r_fds, NULL, NULL, &tv) == -1) {
+ close_sock();
+ return -1; /* Select error */
+ }
+
+ /* Write should be OK if fd not available for reading */
+
+ if (!FD_ISSET(winbindd_fd, &r_fds)) {
+
+ /* Do the write */
+
+ result = write(winbindd_fd,
+ (char *)buffer + nwritten,
+ count - nwritten);
+
+ if ((result == -1) || (result == 0)) {
+
+ /* Write failed */
+
+ close_sock();
+ return -1;
+ }
+
+ nwritten += result;
+
+ } else {
+
+ /* Pipe has closed on remote end */
+
+ close_sock();
+ goto restart;
+ }
+ }
+
+ return nwritten;
+}
+
+/* Read data from winbindd socket */
+
+static int read_sock(void *buffer, int count)
+{
+ int result = 0, nread = 0;
+
+ /* Read data from socket */
+
+ while(nread < count) {
+
+ result = read(winbindd_fd, (char *)buffer + nread,
+ count - nread);
+
+ if ((result == -1) || (result == 0)) {
+
+ /* Read failed. I think the only useful thing we
+ can do here is just return -1 and fail since the
+ transaction has failed half way through. */
+
+ close_sock();
+ return -1;
+ }
+
+ nread += result;
+ }
+
+ return result;
+}
+
+/* Read reply */
+
+int read_reply(struct winbindd_response *response)
+{
+ int result1, result2 = 0;
+
+ if (!response) {
+ return -1;
+ }
+
+ /* Read fixed length response */
+
+ if ((result1 = read_sock(response, sizeof(struct winbindd_response)))
+ == -1) {
+
+ return -1;
+ }
+
+ /* We actually send the pointer value of the extra_data field from
+ the server. This has no meaning in the client's address space
+ so we clear it out. */
+
+ response->extra_data = NULL;
+
+ /* Read variable length response */
+
+ if (response->length > sizeof(struct winbindd_response)) {
+ int extra_data_len = response->length -
+ sizeof(struct winbindd_response);
+
+ /* Mallocate memory for extra data */
+
+ if (!(response->extra_data = malloc(extra_data_len))) {
+ return -1;
+ }
+
+ if ((result2 = read_sock(response->extra_data, extra_data_len))
+ == -1) {
+ free_response(response);
+ return -1;
+ }
+ }
+
+ /* Return total amount of data read */
+
+ return result1 + result2;
+}
+
+/*
+ * send simple types of requests
+ */
+
+NSS_STATUS winbindd_send_request(int req_type, struct winbindd_request *request)
+{
+ struct winbindd_request lrequest;
+ char *env;
+ int value;
+
+ /* Check for our tricky environment variable */
+
+ if ( (env = getenv(WINBINDD_DONT_ENV)) != NULL ) {
+ value = atoi(env);
+ if ( value == 1 )
+ return NSS_STATUS_NOTFOUND;
+ }
+
+ if (!request) {
+ ZERO_STRUCT(lrequest);
+ request = &lrequest;
+ }
+
+ /* Fill in request and send down pipe */
+
+ init_request(request, req_type);
+
+ if (write_sock(request, sizeof(*request)) == -1) {
+ return NSS_STATUS_UNAVAIL;
+ }
+
+ return NSS_STATUS_SUCCESS;
+}
+
+/*
+ * Get results from winbindd request
+ */
+
+NSS_STATUS winbindd_get_response(struct winbindd_response *response)
+{
+ struct winbindd_response lresponse;
+
+ if (!response) {
+ ZERO_STRUCT(lresponse);
+ response = &lresponse;
+ }
+
+ init_response(response);
+
+ /* Wait for reply */
+ if (read_reply(response) == -1) {
+ return NSS_STATUS_UNAVAIL;
+ }
+
+ /* Throw away extra data if client didn't request it */
+ if (response == &lresponse) {
+ free_response(response);
+ }
+
+ /* Copy reply data from socket */
+ if (response->result != WINBINDD_OK) {
+ return NSS_STATUS_NOTFOUND;
+ }
+
+ return NSS_STATUS_SUCCESS;
+}
+
+/* Handle simple types of requests */
+
+NSS_STATUS winbindd_request(int req_type,
+ struct winbindd_request *request,
+ struct winbindd_response *response)
+{
+ NSS_STATUS status;
+
+ status = winbindd_send_request(req_type, request);
+ if (status != NSS_STATUS_SUCCESS)
+ return(status);
+ return winbindd_get_response(response);
+}
+
+/*************************************************************************
+ A couple of simple functions to disable winbindd lookups and re-
+ enable them
+ ************************************************************************/
+
+/* Use putenv() instead of setenv() in these functions as not all
+ environments have the latter. */
+
+BOOL winbind_off( void )
+{
+ static char *s = WINBINDD_DONT_ENV "=1";
+
+ return putenv(s) != -1;
+}
+
+BOOL winbind_on( void )
+{
+ static char *s = WINBINDD_DONT_ENV "=0";
+
+ return putenv(s) != -1;
+}
diff --git a/source/nsswitch/wbinfo.c b/source/nsswitch/wbinfo.c
new file mode 100644
index 00000000000..5c1722dcaa5
--- /dev/null
+++ b/source/nsswitch/wbinfo.c
@@ -0,0 +1,1313 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ Winbind status program.
+
+ Copyright (C) Tim Potter 2000-2003
+ 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 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 "winbindd.h"
+#include "debug.h"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_WINBIND
+
+extern int winbindd_fd;
+
+static char winbind_separator(void)
+{
+ struct winbindd_response response;
+ static BOOL got_sep;
+ static char sep;
+
+ if (got_sep)
+ return sep;
+
+ ZERO_STRUCT(response);
+
+ /* Send off request */
+
+ if (winbindd_request(WINBINDD_INFO, NULL, &response) !=
+ NSS_STATUS_SUCCESS) {
+ d_printf("could not obtain winbind separator!\n");
+ /* HACK: (this module should not call lp_ funtions) */
+ return *lp_winbind_separator();
+ }
+
+ sep = response.data.info.winbind_separator;
+ got_sep = True;
+
+ if (!sep) {
+ d_printf("winbind separator was NULL!\n");
+ /* HACK: (this module should not call lp_ funtions) */
+ sep = *lp_winbind_separator();
+ }
+
+ return sep;
+}
+
+static const char *get_winbind_domain(void)
+{
+ struct winbindd_response response;
+ static fstring winbind_domain;
+
+ ZERO_STRUCT(response);
+
+ /* Send off request */
+
+ if (winbindd_request(WINBINDD_DOMAIN_NAME, NULL, &response) !=
+ NSS_STATUS_SUCCESS) {
+ d_printf("could not obtain winbind domain name!\n");
+
+ /* HACK: (this module should not call lp_ funtions) */
+ return lp_workgroup();
+ }
+
+ fstrcpy(winbind_domain, response.data.domain_name);
+
+ return winbind_domain;
+
+}
+
+/* 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_wbinfo_domain_user(const char *domuser, fstring domain,
+ fstring user)
+{
+
+ char *p = strchr(domuser,winbind_separator());
+
+ if (!p) {
+ fstrcpy(user, domuser);
+ fstrcpy(domain, get_winbind_domain());
+ return True;
+ }
+
+ fstrcpy(user, p+1);
+ fstrcpy(domain, domuser);
+ domain[PTR_DIFF(p, domuser)] = 0;
+ strupper_m(domain);
+
+ return True;
+}
+
+/* List groups a user is a member of */
+
+static BOOL wbinfo_get_usergroups(char *user)
+{
+ struct winbindd_request request;
+ struct winbindd_response response;
+ NSS_STATUS result;
+ int i;
+
+ ZERO_STRUCT(response);
+
+ /* Send request */
+
+ fstrcpy(request.data.username, user);
+
+ result = winbindd_request(WINBINDD_GETGROUPS, &request, &response);
+
+ if (result != NSS_STATUS_SUCCESS)
+ return False;
+
+ for (i = 0; i < response.data.num_entries; i++)
+ d_printf("%d\n", (int)((gid_t *)response.extra_data)[i]);
+
+ SAFE_FREE(response.extra_data);
+
+ return True;
+}
+
+
+/* List group SIDs a user SID is a member of */
+static BOOL wbinfo_get_usersids(char *user_sid)
+{
+ struct winbindd_request request;
+ struct winbindd_response response;
+ NSS_STATUS result;
+ int i;
+ const char *s;
+
+ ZERO_STRUCT(response);
+
+ /* Send request */
+ fstrcpy(request.data.sid, user_sid);
+
+ result = winbindd_request(WINBINDD_GETUSERSIDS, &request, &response);
+
+ if (result != NSS_STATUS_SUCCESS)
+ return False;
+
+ s = response.extra_data;
+ for (i = 0; i < response.data.num_entries; i++) {
+ d_printf("%s\n", s);
+ s += strlen(s) + 1;
+ }
+
+ SAFE_FREE(response.extra_data);
+
+ return True;
+}
+
+/* Convert NetBIOS name to IP */
+
+static BOOL wbinfo_wins_byname(char *name)
+{
+ struct winbindd_request request;
+ struct winbindd_response response;
+
+ ZERO_STRUCT(request);
+ ZERO_STRUCT(response);
+
+ /* Send request */
+
+ fstrcpy(request.data.winsreq, name);
+
+ if (winbindd_request(WINBINDD_WINS_BYNAME, &request, &response) !=
+ NSS_STATUS_SUCCESS) {
+ return False;
+ }
+
+ /* Display response */
+
+ printf("%s\n", response.data.winsresp);
+
+ return True;
+}
+
+/* Convert IP to NetBIOS name */
+
+static BOOL wbinfo_wins_byip(char *ip)
+{
+ struct winbindd_request request;
+ struct winbindd_response response;
+
+ ZERO_STRUCT(request);
+ ZERO_STRUCT(response);
+
+ /* Send request */
+
+ fstrcpy(request.data.winsreq, ip);
+
+ if (winbindd_request(WINBINDD_WINS_BYIP, &request, &response) !=
+ NSS_STATUS_SUCCESS) {
+ return False;
+ }
+
+ /* Display response */
+
+ printf("%s\n", response.data.winsresp);
+
+ return True;
+}
+
+/* List trusted domains */
+
+static BOOL wbinfo_list_domains(void)
+{
+ struct winbindd_response response;
+ fstring name;
+
+ ZERO_STRUCT(response);
+
+ /* Send request */
+
+ if (winbindd_request(WINBINDD_LIST_TRUSTDOM, NULL, &response) !=
+ NSS_STATUS_SUCCESS)
+ return False;
+
+ /* Display response */
+
+ if (response.extra_data) {
+ const char *extra_data = (char *)response.extra_data;
+
+ while(next_token(&extra_data, name, ",", sizeof(fstring)))
+ d_printf("%s\n", name);
+
+ SAFE_FREE(response.extra_data);
+ }
+
+ return True;
+}
+
+
+/* show sequence numbers */
+static BOOL wbinfo_show_sequence(const char *domain)
+{
+ struct winbindd_request request;
+ struct winbindd_response response;
+
+ ZERO_STRUCT(response);
+ ZERO_STRUCT(request);
+
+ if ( domain )
+ fstrcpy( request.domain_name, domain );
+
+ /* Send request */
+
+ if (winbindd_request(WINBINDD_SHOW_SEQUENCE, &request, &response) !=
+ NSS_STATUS_SUCCESS)
+ return False;
+
+ /* Display response */
+
+ if (response.extra_data) {
+ char *extra_data = (char *)response.extra_data;
+ d_printf("%s", extra_data);
+ SAFE_FREE(response.extra_data);
+ }
+
+ return True;
+}
+
+/* Show domain info */
+
+static BOOL wbinfo_domain_info(const char *domain_name)
+{
+ struct winbindd_request request;
+ struct winbindd_response response;
+
+ ZERO_STRUCT(request);
+ ZERO_STRUCT(response);
+
+ fstrcpy(request.domain_name, domain_name);
+
+ /* Send request */
+
+ if (winbindd_request(WINBINDD_DOMAIN_INFO, &request, &response) !=
+ NSS_STATUS_SUCCESS)
+ return False;
+
+ /* Display response */
+
+ d_printf("Name : %s\n", response.data.domain_info.name);
+ d_printf("Alt_Name : %s\n", response.data.domain_info.alt_name);
+
+ d_printf("SID : %s\n", response.data.domain_info.sid);
+
+ d_printf("Active Directory : %s\n",
+ response.data.domain_info.active_directory ? "Yes" : "No");
+ d_printf("Native : %s\n",
+ response.data.domain_info.native_mode ? "Yes" : "No");
+
+ d_printf("Primary : %s\n",
+ response.data.domain_info.primary ? "Yes" : "No");
+
+ d_printf("Sequence : %d\n", response.data.domain_info.sequence_number);
+
+ return True;
+}
+
+/* Check trust account password */
+
+static BOOL wbinfo_check_secret(void)
+{
+ struct winbindd_response response;
+ NSS_STATUS result;
+
+ ZERO_STRUCT(response);
+
+ result = winbindd_request(WINBINDD_CHECK_MACHACC, NULL, &response);
+
+ d_printf("checking the trust secret via RPC calls %s\n",
+ (result == NSS_STATUS_SUCCESS) ? "succeeded" : "failed");
+
+ if (result != NSS_STATUS_SUCCESS)
+ d_printf("error code was %s (0x%x)\n",
+ response.data.auth.nt_status_string,
+ response.data.auth.nt_status);
+
+ return result == NSS_STATUS_SUCCESS;
+}
+
+/* Convert uid to sid */
+
+static BOOL wbinfo_uid_to_sid(uid_t uid)
+{
+ struct winbindd_request request;
+ struct winbindd_response response;
+
+ ZERO_STRUCT(request);
+ ZERO_STRUCT(response);
+
+ /* Send request */
+
+ request.data.uid = uid;
+
+ if (winbindd_request(WINBINDD_UID_TO_SID, &request, &response) !=
+ NSS_STATUS_SUCCESS)
+ return False;
+
+ /* Display response */
+
+ d_printf("%s\n", response.data.sid.sid);
+
+ return True;
+}
+
+/* Convert gid to sid */
+
+static BOOL wbinfo_gid_to_sid(gid_t gid)
+{
+ struct winbindd_request request;
+ struct winbindd_response response;
+
+ ZERO_STRUCT(request);
+ ZERO_STRUCT(response);
+
+ /* Send request */
+
+ request.data.gid = gid;
+
+ if (winbindd_request(WINBINDD_GID_TO_SID, &request, &response) !=
+ NSS_STATUS_SUCCESS)
+ return False;
+
+ /* Display response */
+
+ d_printf("%s\n", response.data.sid.sid);
+
+ return True;
+}
+
+/* Convert sid to uid */
+
+static BOOL wbinfo_sid_to_uid(char *sid)
+{
+ struct winbindd_request request;
+ struct winbindd_response response;
+
+ ZERO_STRUCT(request);
+ ZERO_STRUCT(response);
+
+ /* Send request */
+
+ fstrcpy(request.data.sid, sid);
+
+ if (winbindd_request(WINBINDD_SID_TO_UID, &request, &response) !=
+ NSS_STATUS_SUCCESS)
+ return False;
+
+ /* Display response */
+
+ d_printf("%d\n", (int)response.data.uid);
+
+ return True;
+}
+
+static BOOL wbinfo_sid_to_gid(char *sid)
+{
+ struct winbindd_request request;
+ struct winbindd_response response;
+
+ ZERO_STRUCT(request);
+ ZERO_STRUCT(response);
+
+ /* Send request */
+
+ fstrcpy(request.data.sid, sid);
+
+ if (winbindd_request(WINBINDD_SID_TO_GID, &request, &response) !=
+ NSS_STATUS_SUCCESS)
+ return False;
+
+ /* Display response */
+
+ d_printf("%d\n", (int)response.data.gid);
+
+ return True;
+}
+
+static BOOL wbinfo_allocate_rid(void)
+{
+ uint32 rid;
+
+ if (!winbind_allocate_rid(&rid))
+ return False;
+
+ d_printf("New rid: %d\n", rid);
+
+ return True;
+}
+
+/* Convert sid to string */
+
+static BOOL wbinfo_lookupsid(char *sid)
+{
+ struct winbindd_request request;
+ struct winbindd_response response;
+
+ ZERO_STRUCT(request);
+ ZERO_STRUCT(response);
+
+ /* Send off request */
+
+ fstrcpy(request.data.sid, sid);
+
+ if (winbindd_request(WINBINDD_LOOKUPSID, &request, &response) !=
+ NSS_STATUS_SUCCESS)
+ return False;
+
+ /* Display response */
+
+ d_printf("%s%c%s %d\n", response.data.name.dom_name,
+ winbind_separator(), response.data.name.name,
+ response.data.name.type);
+
+ return True;
+}
+
+/* Convert string to sid */
+
+static BOOL wbinfo_lookupname(char *name)
+{
+ struct winbindd_request request;
+ struct winbindd_response response;
+
+ /* Send off request */
+
+ ZERO_STRUCT(request);
+ ZERO_STRUCT(response);
+
+ parse_wbinfo_domain_user(name, request.data.name.dom_name,
+ request.data.name.name);
+
+ if (winbindd_request(WINBINDD_LOOKUPNAME, &request, &response) !=
+ NSS_STATUS_SUCCESS)
+ return False;
+
+ /* Display response */
+
+ d_printf("%s %s (%d)\n", response.data.sid.sid, sid_type_lookup(response.data.sid.type), response.data.sid.type);
+
+ return True;
+}
+
+/* Authenticate a user with a plaintext password */
+
+static BOOL wbinfo_auth(char *username)
+{
+ struct winbindd_request request;
+ struct winbindd_response response;
+ NSS_STATUS result;
+ char *p;
+
+ /* Send off request */
+
+ ZERO_STRUCT(request);
+ ZERO_STRUCT(response);
+
+ p = strchr(username, '%');
+
+ if (p) {
+ *p = 0;
+ fstrcpy(request.data.auth.user, username);
+ fstrcpy(request.data.auth.pass, p + 1);
+ *p = '%';
+ } else
+ fstrcpy(request.data.auth.user, username);
+
+ result = winbindd_request(WINBINDD_PAM_AUTH, &request, &response);
+
+ /* Display response */
+
+ d_printf("plaintext password authentication %s\n",
+ (result == NSS_STATUS_SUCCESS) ? "succeeded" : "failed");
+
+ if (response.data.auth.nt_status)
+ d_printf("error code was %s (0x%x)\nerror messsage was: %s\n",
+ response.data.auth.nt_status_string,
+ response.data.auth.nt_status,
+ response.data.auth.error_string);
+
+ return result == NSS_STATUS_SUCCESS;
+}
+
+/* Authenticate a user with a challenge/response */
+
+static BOOL wbinfo_auth_crap(char *username)
+{
+ struct winbindd_request request;
+ struct winbindd_response response;
+ NSS_STATUS result;
+ fstring name_user;
+ fstring name_domain;
+ fstring pass;
+ char *p;
+
+ /* Send off request */
+
+ ZERO_STRUCT(request);
+ ZERO_STRUCT(response);
+
+ p = strchr(username, '%');
+
+ if (p) {
+ *p = 0;
+ fstrcpy(pass, p + 1);
+ }
+
+ parse_wbinfo_domain_user(username, name_domain, name_user);
+
+ if (push_utf8_fstring(request.data.auth_crap.user, name_user) == -1) {
+ d_printf("unable to create utf8 string for '%s'\n",
+ name_user);
+ return False;
+ }
+
+ if (push_utf8_fstring(request.data.auth_crap.domain,
+ name_domain) == -1) {
+ d_printf("unable to create utf8 string for '%s'\n",
+ name_domain);
+ return False;
+ }
+
+ generate_random_buffer(request.data.auth_crap.chal, 8, False);
+
+ SMBencrypt(pass, request.data.auth_crap.chal,
+ (uchar *)request.data.auth_crap.lm_resp);
+ SMBNTencrypt(pass, request.data.auth_crap.chal,
+ (uchar *)request.data.auth_crap.nt_resp);
+
+ request.data.auth_crap.lm_resp_len = 24;
+ request.data.auth_crap.nt_resp_len = 24;
+
+ result = winbindd_request(WINBINDD_PAM_AUTH_CRAP, &request, &response);
+
+ /* Display response */
+
+ d_printf("challenge/response password authentication %s\n",
+ (result == NSS_STATUS_SUCCESS) ? "succeeded" : "failed");
+
+ if (response.data.auth.nt_status)
+ d_printf("error code was %s (0x%x)\nerror messsage was: %s\n",
+ response.data.auth.nt_status_string,
+ response.data.auth.nt_status,
+ response.data.auth.error_string);
+
+ return result == NSS_STATUS_SUCCESS;
+}
+
+/* Authenticate a user with a plaintext password and set a token */
+
+static BOOL wbinfo_klog(char *username)
+{
+ struct winbindd_request request;
+ struct winbindd_response response;
+ NSS_STATUS result;
+ char *p;
+
+ /* Send off request */
+
+ ZERO_STRUCT(request);
+ ZERO_STRUCT(response);
+
+ p = strchr(username, '%');
+
+ if (p) {
+ *p = 0;
+ fstrcpy(request.data.auth.user, username);
+ fstrcpy(request.data.auth.pass, p + 1);
+ *p = '%';
+ } else {
+ fstrcpy(request.data.auth.user, username);
+ fstrcpy(request.data.auth.pass, getpass("Password: "));
+ }
+
+ request.flags |= WBFLAG_PAM_AFS_TOKEN;
+
+ result = winbindd_request(WINBINDD_PAM_AUTH, &request, &response);
+
+ /* Display response */
+
+ d_printf("plaintext password authentication %s\n",
+ (result == NSS_STATUS_SUCCESS) ? "succeeded" : "failed");
+
+ if (response.data.auth.nt_status)
+ d_printf("error code was %s (0x%x)\nerror messsage was: %s\n",
+ response.data.auth.nt_status_string,
+ response.data.auth.nt_status,
+ response.data.auth.error_string);
+
+ if (result != NSS_STATUS_SUCCESS)
+ return False;
+
+ if (response.extra_data == NULL) {
+ d_printf("Did not get token data\n");
+ return False;
+ }
+
+ if (!afs_settoken_str((char *)response.extra_data)) {
+ d_printf("Could not set token\n");
+ return False;
+ }
+
+ d_printf("Successfully created AFS token\n");
+ return True;
+}
+
+/******************************************************************
+ create a winbindd user
+******************************************************************/
+
+static BOOL wbinfo_create_user(char *username)
+{
+ struct winbindd_request request;
+ struct winbindd_response response;
+ NSS_STATUS result;
+
+ /* Send off request */
+
+ ZERO_STRUCT(request);
+ ZERO_STRUCT(response);
+
+ request.flags = WBFLAG_ALLOCATE_RID;
+ fstrcpy(request.data.acct_mgt.username, username);
+
+ result = winbindd_request(WINBINDD_CREATE_USER, &request, &response);
+
+ if ( result == NSS_STATUS_SUCCESS )
+ d_printf("New RID is %d\n", response.data.rid);
+
+ return result == NSS_STATUS_SUCCESS;
+}
+
+/******************************************************************
+ remove a winbindd user
+******************************************************************/
+
+static BOOL wbinfo_delete_user(char *username)
+{
+ struct winbindd_request request;
+ struct winbindd_response response;
+ NSS_STATUS result;
+
+ /* Send off request */
+
+ ZERO_STRUCT(request);
+ ZERO_STRUCT(response);
+
+ fstrcpy(request.data.acct_mgt.username, username);
+
+ result = winbindd_request(WINBINDD_DELETE_USER, &request, &response);
+
+ return result == NSS_STATUS_SUCCESS;
+}
+
+/******************************************************************
+ create a winbindd group
+******************************************************************/
+
+static BOOL wbinfo_create_group(char *groupname)
+{
+ struct winbindd_request request;
+ struct winbindd_response response;
+ NSS_STATUS result;
+
+ /* Send off request */
+
+ ZERO_STRUCT(request);
+ ZERO_STRUCT(response);
+
+ fstrcpy(request.data.acct_mgt.groupname, groupname);
+
+ result = winbindd_request(WINBINDD_CREATE_GROUP, &request, &response);
+
+ return result == NSS_STATUS_SUCCESS;
+}
+
+/******************************************************************
+ remove a winbindd group
+******************************************************************/
+
+static BOOL wbinfo_delete_group(char *groupname)
+{
+ struct winbindd_request request;
+ struct winbindd_response response;
+ NSS_STATUS result;
+
+ /* Send off request */
+
+ ZERO_STRUCT(request);
+ ZERO_STRUCT(response);
+
+ fstrcpy(request.data.acct_mgt.groupname, groupname);
+
+ result = winbindd_request(WINBINDD_DELETE_GROUP, &request, &response);
+
+ return result == NSS_STATUS_SUCCESS;
+}
+
+/******************************************************************
+ parse a string in the form user:group
+******************************************************************/
+
+static BOOL parse_user_group( const char *string, fstring user, fstring group )
+{
+ char *p;
+
+ if ( !string )
+ return False;
+
+ if ( !(p = strchr( string, ':' )) )
+ return False;
+
+ *p = '\0';
+ p++;
+
+ fstrcpy( user, string );
+ fstrcpy( group, p );
+
+ return True;
+}
+
+/******************************************************************
+ add a user to a winbindd group
+******************************************************************/
+
+static BOOL wbinfo_add_user_to_group(char *string)
+{
+ struct winbindd_request request;
+ struct winbindd_response response;
+ NSS_STATUS result;
+
+ /* Send off request */
+
+ ZERO_STRUCT(request);
+ ZERO_STRUCT(response);
+
+ if ( !parse_user_group( string, request.data.acct_mgt.username,
+ request.data.acct_mgt.groupname))
+ {
+ d_printf("Can't parse user:group from %s\n", string);
+ return False;
+ }
+
+ result = winbindd_request(WINBINDD_ADD_USER_TO_GROUP, &request, &response);
+
+ return result == NSS_STATUS_SUCCESS;
+}
+
+/******************************************************************
+ remove a user from a winbindd group
+******************************************************************/
+
+static BOOL wbinfo_remove_user_from_group(char *string)
+{
+ struct winbindd_request request;
+ struct winbindd_response response;
+ NSS_STATUS result;
+
+ /* Send off request */
+
+ ZERO_STRUCT(request);
+ ZERO_STRUCT(response);
+
+ if ( !parse_user_group( string, request.data.acct_mgt.username,
+ request.data.acct_mgt.groupname))
+ {
+ d_printf("Can't parse user:group from %s\n", string);
+ return False;
+ }
+
+ result = winbindd_request(WINBINDD_REMOVE_USER_FROM_GROUP, &request, &response);
+
+ return result == NSS_STATUS_SUCCESS;
+}
+
+/* Print domain users */
+
+static BOOL print_domain_users(const char *domain)
+{
+ struct winbindd_request request;
+ struct winbindd_response response;
+ const char *extra_data;
+ fstring name;
+
+ /* Send request to winbind daemon */
+
+ ZERO_STRUCT(request);
+ ZERO_STRUCT(response);
+
+ if (domain) {
+ /* '.' is the special sign for our own domwin */
+ if ( strequal(domain, ".") )
+ fstrcpy( request.domain_name, lp_workgroup() );
+ else
+ fstrcpy( request.domain_name, domain );
+ }
+
+ if (winbindd_request(WINBINDD_LIST_USERS, &request, &response) !=
+ NSS_STATUS_SUCCESS)
+ return False;
+
+ /* Look through extra data */
+
+ if (!response.extra_data)
+ return False;
+
+ extra_data = (const char *)response.extra_data;
+
+ while(next_token(&extra_data, name, ",", sizeof(fstring)))
+ d_printf("%s\n", name);
+
+ SAFE_FREE(response.extra_data);
+
+ return True;
+}
+
+/* Print domain groups */
+
+static BOOL print_domain_groups(const char *domain)
+{
+ struct winbindd_request request;
+ struct winbindd_response response;
+ const char *extra_data;
+ fstring name;
+
+ ZERO_STRUCT(request);
+ ZERO_STRUCT(response);
+
+ if (domain) {
+ if ( strequal(domain, ".") )
+ fstrcpy( request.domain_name, lp_workgroup() );
+ else
+ fstrcpy( request.domain_name, domain );
+ }
+
+ if (winbindd_request(WINBINDD_LIST_GROUPS, &request, &response) !=
+ NSS_STATUS_SUCCESS)
+ return False;
+
+ /* Look through extra data */
+
+ if (!response.extra_data)
+ return False;
+
+ extra_data = (const char *)response.extra_data;
+
+ while(next_token(&extra_data, name, ",", sizeof(fstring)))
+ d_printf("%s\n", name);
+
+ SAFE_FREE(response.extra_data);
+
+ return True;
+}
+
+/* Set the authorised user for winbindd access in secrets.tdb */
+
+static BOOL wbinfo_set_auth_user(char *username)
+{
+ char *password;
+ fstring user, domain;
+
+ /* Separate into user and password */
+
+ parse_wbinfo_domain_user(username, domain, user);
+
+ password = strchr(user, '%');
+
+ if (password) {
+ *password = 0;
+ password++;
+ } else {
+ char *thepass = getpass("Password: ");
+ if (thepass) {
+ password = thepass;
+ } else
+ password = "";
+ }
+
+ /* Store or remove DOMAIN\username%password in secrets.tdb */
+
+ secrets_init();
+
+ if (user[0]) {
+
+ if (!secrets_store(SECRETS_AUTH_USER, user,
+ strlen(user) + 1)) {
+ d_fprintf(stderr, "error storing username\n");
+ return False;
+ }
+
+ /* We always have a domain name added by the
+ parse_wbinfo_domain_user() function. */
+
+ if (!secrets_store(SECRETS_AUTH_DOMAIN, domain,
+ strlen(domain) + 1)) {
+ d_fprintf(stderr, "error storing domain name\n");
+ return False;
+ }
+
+ } else {
+ secrets_delete(SECRETS_AUTH_USER);
+ secrets_delete(SECRETS_AUTH_DOMAIN);
+ }
+
+ if (password[0]) {
+
+ if (!secrets_store(SECRETS_AUTH_PASSWORD, password,
+ strlen(password) + 1)) {
+ d_fprintf(stderr, "error storing password\n");
+ return False;
+ }
+
+ } else
+ secrets_delete(SECRETS_AUTH_PASSWORD);
+
+ return True;
+}
+
+static void wbinfo_get_auth_user(void)
+{
+ char *user, *domain, *password;
+
+ /* Lift data from secrets file */
+
+ secrets_fetch_ipc_userpass(&user, &domain, &password);
+
+ if ((!user || !*user) && (!domain || !*domain ) && (!password || !*password)){
+
+ SAFE_FREE(user);
+ SAFE_FREE(domain);
+ SAFE_FREE(password);
+ d_printf("No authorised user configured\n");
+ return;
+ }
+
+ /* Pretty print authorised user info */
+
+ d_printf("%s%s%s%s%s\n", domain ? domain : "", domain ? lp_winbind_separator(): "",
+ user, password ? "%" : "", password ? password : "");
+
+ SAFE_FREE(user);
+ SAFE_FREE(domain);
+ SAFE_FREE(password);
+}
+
+static BOOL wbinfo_ping(void)
+{
+ NSS_STATUS result;
+
+ result = winbindd_request(WINBINDD_PING, NULL, NULL);
+
+ /* Display response */
+
+ d_printf("Ping to winbindd %s on fd %d\n",
+ (result == NSS_STATUS_SUCCESS) ? "succeeded" : "failed", winbindd_fd);
+
+ return result == NSS_STATUS_SUCCESS;
+}
+
+/* Main program */
+
+enum {
+ OPT_SET_AUTH_USER = 1000,
+ OPT_GET_AUTH_USER,
+ OPT_DOMAIN_NAME,
+ OPT_SEQUENCE,
+ OPT_USERSIDS
+};
+
+int main(int argc, char **argv)
+{
+ int opt;
+
+ poptContext pc;
+ static char *string_arg;
+ static char *opt_domain_name;
+ static int int_arg;
+ int result = 1;
+
+ struct poptOption long_options[] = {
+ POPT_AUTOHELP
+
+ /* longName, shortName, argInfo, argPtr, value, descrip,
+ argDesc */
+
+ { "domain-users", 'u', POPT_ARG_NONE, 0, 'u', "Lists all domain users", "domain"},
+ { "domain-groups", 'g', POPT_ARG_NONE, 0, 'g', "Lists all domain groups", "domain" },
+ { "WINS-by-name", 'N', POPT_ARG_STRING, &string_arg, 'N', "Converts NetBIOS name to IP", "NETBIOS-NAME" },
+ { "WINS-by-ip", 'I', POPT_ARG_STRING, &string_arg, 'I', "Converts IP address to NetBIOS name", "IP" },
+ { "name-to-sid", 'n', POPT_ARG_STRING, &string_arg, 'n', "Converts name to sid", "NAME" },
+ { "sid-to-name", 's', POPT_ARG_STRING, &string_arg, 's', "Converts sid to name", "SID" },
+ { "uid-to-sid", 'U', POPT_ARG_INT, &int_arg, 'U', "Converts uid to sid" , "UID" },
+ { "gid-to-sid", 'G', POPT_ARG_INT, &int_arg, 'G', "Converts gid to sid", "GID" },
+ { "sid-to-uid", 'S', POPT_ARG_STRING, &string_arg, 'S', "Converts sid to uid", "SID" },
+ { "sid-to-gid", 'Y', POPT_ARG_STRING, &string_arg, 'Y', "Converts sid to gid", "SID" },
+ { "allocate-rid", 'A', POPT_ARG_NONE, 0, 'A', "Get a new RID out of idmap" },
+ { "create-user", 'c', POPT_ARG_STRING, &string_arg, 'c', "Create a local user account", "name" },
+ { "delete-user", 'x', POPT_ARG_STRING, &string_arg, 'x', "Delete a local user account", "name" },
+ { "create-group", 'C', POPT_ARG_STRING, &string_arg, 'C', "Create a local group", "name" },
+ { "delete-group", 'X', POPT_ARG_STRING, &string_arg, 'X', "Delete a local group", "name" },
+ { "add-to-group", 'o', POPT_ARG_STRING, &string_arg, 'o', "Add user to group", "user:group" },
+ { "del-from-group", 'O', POPT_ARG_STRING, &string_arg, 'O', "Remove user from group", "user:group" },
+ { "check-secret", 't', POPT_ARG_NONE, 0, 't', "Check shared secret" },
+ { "trusted-domains", 'm', POPT_ARG_NONE, 0, 'm', "List trusted domains" },
+ { "sequence", 0, POPT_ARG_NONE, 0, OPT_SEQUENCE, "Show sequence numbers of all domains" },
+ { "domain-info", 'D', POPT_ARG_STRING, &string_arg, 'D', "Show most of the info we have about the domain" },
+ { "user-groups", 'r', POPT_ARG_STRING, &string_arg, 'r', "Get user groups", "USER" },
+ { "user-sids", 0, POPT_ARG_STRING, &string_arg, OPT_USERSIDS, "Get user group sids for user SID", "SID" },
+ { "authenticate", 'a', POPT_ARG_STRING, &string_arg, 'a', "authenticate user", "user%password" },
+ { "set-auth-user", 0, POPT_ARG_STRING, &string_arg, OPT_SET_AUTH_USER, "Store user and password used by winbindd (root only)", "user%password" },
+ { "get-auth-user", 0, POPT_ARG_NONE, NULL, OPT_GET_AUTH_USER, "Retrieve user and password used by winbindd (root only)", NULL },
+ { "ping", 'p', POPT_ARG_NONE, 0, 'p', "Ping winbindd to see if it is alive" },
+ { "domain", 0, POPT_ARG_STRING, &opt_domain_name, OPT_DOMAIN_NAME, "Define to the domain to restrict operation", "domain" },
+#ifdef WITH_FAKE_KASERVER
+ { "klog", 'k', POPT_ARG_STRING, &string_arg, 'k', "set an AFS token from winbind", "user%password" },
+#endif
+ POPT_COMMON_VERSION
+ POPT_TABLEEND
+ };
+
+ /* Samba client initialisation */
+
+ if (!lp_load(dyn_CONFIGFILE, True, False, False)) {
+ d_fprintf(stderr, "wbinfo: error opening config file %s. Error was %s\n",
+ dyn_CONFIGFILE, strerror(errno));
+ exit(1);
+ }
+
+ if (!init_names())
+ return 1;
+
+ load_interfaces();
+
+ /* Parse options */
+
+ pc = poptGetContext("wbinfo", argc, (const char **)argv, long_options, 0);
+
+ /* Parse command line options */
+
+ if (argc == 1) {
+ poptPrintHelp(pc, stderr, 0);
+ return 1;
+ }
+
+ while((opt = poptGetNextOpt(pc)) != -1) {
+ /* get the generic configuration parameters like --domain */
+ }
+
+ poptFreeContext(pc);
+
+ pc = poptGetContext(NULL, argc, (const char **)argv, long_options,
+ POPT_CONTEXT_KEEP_FIRST);
+
+ while((opt = poptGetNextOpt(pc)) != -1) {
+ switch (opt) {
+ case 'u':
+ if (!print_domain_users(opt_domain_name)) {
+ d_printf("Error looking up domain users\n");
+ goto done;
+ }
+ break;
+ case 'g':
+ if (!print_domain_groups(opt_domain_name)) {
+ d_printf("Error looking up domain groups\n");
+ goto done;
+ }
+ break;
+ case 's':
+ if (!wbinfo_lookupsid(string_arg)) {
+ d_printf("Could not lookup sid %s\n", string_arg);
+ goto done;
+ }
+ break;
+ case 'n':
+ if (!wbinfo_lookupname(string_arg)) {
+ d_printf("Could not lookup name %s\n", string_arg);
+ goto done;
+ }
+ break;
+ case 'N':
+ if (!wbinfo_wins_byname(string_arg)) {
+ d_printf("Could not lookup WINS by name %s\n", string_arg);
+ goto done;
+ }
+ break;
+ case 'I':
+ if (!wbinfo_wins_byip(string_arg)) {
+ d_printf("Could not lookup WINS by IP %s\n", string_arg);
+ goto done;
+ }
+ break;
+ case 'U':
+ if (!wbinfo_uid_to_sid(int_arg)) {
+ d_printf("Could not convert uid %d to sid\n", int_arg);
+ goto done;
+ }
+ break;
+ case 'G':
+ if (!wbinfo_gid_to_sid(int_arg)) {
+ d_printf("Could not convert gid %d to sid\n",
+ int_arg);
+ goto done;
+ }
+ break;
+ case 'S':
+ if (!wbinfo_sid_to_uid(string_arg)) {
+ d_printf("Could not convert sid %s to uid\n",
+ string_arg);
+ goto done;
+ }
+ break;
+ case 'Y':
+ if (!wbinfo_sid_to_gid(string_arg)) {
+ d_printf("Could not convert sid %s to gid\n",
+ string_arg);
+ goto done;
+ }
+ break;
+ case 'A':
+ if (!wbinfo_allocate_rid()) {
+ d_printf("Could not allocate a RID\n");
+ goto done;
+ }
+ break;
+ case 't':
+ if (!wbinfo_check_secret()) {
+ d_printf("Could not check secret\n");
+ goto done;
+ }
+ break;
+ case 'm':
+ if (!wbinfo_list_domains()) {
+ d_printf("Could not list trusted domains\n");
+ goto done;
+ }
+ break;
+ case OPT_SEQUENCE:
+ if (!wbinfo_show_sequence(opt_domain_name)) {
+ d_printf("Could not show sequence numbers\n");
+ goto done;
+ }
+ break;
+ case 'D':
+ if (!wbinfo_domain_info(string_arg)) {
+ d_printf("Could not get domain info\n");
+ goto done;
+ }
+ break;
+ case 'r':
+ if (!wbinfo_get_usergroups(string_arg)) {
+ d_printf("Could not get groups for user %s\n",
+ string_arg);
+ goto done;
+ }
+ break;
+ case OPT_USERSIDS:
+ if (!wbinfo_get_usersids(string_arg)) {
+ d_printf("Could not get group SIDs for user SID %s\n",
+ string_arg);
+ goto done;
+ }
+ break;
+ case 'a': {
+ BOOL got_error = False;
+
+ if (!wbinfo_auth(string_arg)) {
+ d_printf("Could not authenticate user %s with "
+ "plaintext password\n", string_arg);
+ got_error = True;
+ }
+
+ if (!wbinfo_auth_crap(string_arg)) {
+ d_printf("Could not authenticate user %s with "
+ "challenge/response\n", string_arg);
+ got_error = True;
+ }
+
+ if (got_error)
+ goto done;
+ break;
+ }
+ case 'k':
+ if (!wbinfo_klog(string_arg)) {
+ d_printf("Could not klog user\n");
+ goto done;
+ }
+ break;
+ case 'c':
+ if ( !wbinfo_create_user(string_arg) ) {
+ d_printf("Could not create user account\n");
+ goto done;
+ }
+ break;
+ case 'C':
+ if ( !wbinfo_create_group(string_arg) ) {
+ d_printf("Could not create group\n");
+ goto done;
+ }
+ break;
+ case 'o':
+ if ( !wbinfo_add_user_to_group(string_arg) ) {
+ d_printf("Could not add user to group\n");
+ goto done;
+ }
+ break;
+ case 'O':
+ if ( !wbinfo_remove_user_from_group(string_arg) ) {
+ d_printf("Could not remove user from group\n");
+ goto done;
+ }
+ break;
+ case 'x':
+ if ( !wbinfo_delete_user(string_arg) ) {
+ d_printf("Could not delete user account\n");
+ goto done;
+ }
+ break;
+ case 'X':
+ if ( !wbinfo_delete_group(string_arg) ) {
+ d_printf("Could not delete group\n");
+ goto done;
+ }
+ break;
+ case 'p':
+ if (!wbinfo_ping()) {
+ d_printf("could not ping winbindd!\n");
+ goto done;
+ }
+ break;
+ case OPT_SET_AUTH_USER:
+ wbinfo_set_auth_user(string_arg);
+ break;
+ case OPT_GET_AUTH_USER:
+ wbinfo_get_auth_user();
+ break;
+ /* generic configuration options */
+ case OPT_DOMAIN_NAME:
+ break;
+ default:
+ d_fprintf(stderr, "Invalid option\n");
+ poptPrintHelp(pc, stderr, 0);
+ goto done;
+ }
+ }
+
+ result = 0;
+
+ /* Exit code */
+
+ done:
+ poptFreeContext(pc);
+ return result;
+}
diff --git a/source/nsswitch/winbind_client.h b/source/nsswitch/winbind_client.h
new file mode 100644
index 00000000000..4de2d57cc7d
--- /dev/null
+++ b/source/nsswitch/winbind_client.h
@@ -0,0 +1,16 @@
+#include "winbind_nss_config.h"
+#include "winbindd_nss.h"
+
+void init_request(struct winbindd_request *req,int rq_type);
+NSS_STATUS winbindd_send_request(int req_type,
+ struct winbindd_request *request);
+NSS_STATUS winbindd_get_response(struct winbindd_response *response);
+NSS_STATUS winbindd_request(int req_type,
+ struct winbindd_request *request,
+ struct winbindd_response *response);
+int winbind_open_pipe_sock(void);
+int write_sock(void *buffer, int count);
+int read_reply(struct winbindd_response *response);
+void close_sock(void);
+void free_response(struct winbindd_response *response);
+
diff --git a/source/nsswitch/winbind_nss.h b/source/nsswitch/winbind_nss.h
new file mode 100644
index 00000000000..5416ae211e3
--- /dev/null
+++ b/source/nsswitch/winbind_nss.h
@@ -0,0 +1,71 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ A common place to work out how to define NSS_STATUS on various
+ platforms.
+
+ Copyright (C) Tim Potter 2000
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 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 Library General Public
+ License along with this library; if not, write to the
+ Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+*/
+
+#ifndef _NSSWITCH_NSS_H
+#define _NSSWITCH_NSS_H
+
+#ifdef HAVE_NSS_COMMON_H
+
+/*
+ * Sun Solaris
+ */
+
+#include "nsswitch/winbind_nss_solaris.h"
+
+#elif HAVE_NSS_H
+
+/*
+ * Linux (glibc)
+ */
+
+#include <nss.h>
+typedef enum nss_status NSS_STATUS;
+
+#elif HAVE_NS_API_H
+
+/*
+ * SGI IRIX
+ */
+
+#include "nsswitch/winbind_nss_irix.h"
+
+#elif defined(HPUX) && defined(HAVE_NSSWITCH_H)
+
+/* HP-UX 11 */
+
+#include "nsswitch/winbind_nss_hpux.h"
+
+#else /* Nothing's defined. Neither gnu nor sun nor hp */
+
+typedef enum
+{
+ NSS_STATUS_SUCCESS=0,
+ NSS_STATUS_NOTFOUND=1,
+ NSS_STATUS_UNAVAIL=2,
+ NSS_STATUS_TRYAGAIN=3
+} NSS_STATUS;
+
+#endif
+
+#endif /* _NSSWITCH_NSS_H */
diff --git a/source/nsswitch/winbind_nss_aix.c b/source/nsswitch/winbind_nss_aix.c
new file mode 100644
index 00000000000..3e00e54e5c4
--- /dev/null
+++ b/source/nsswitch/winbind_nss_aix.c
@@ -0,0 +1,1019 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ AIX loadable authentication module, providing identification and
+ authentication routines against Samba winbind/Windows NT Domain
+
+ Copyright (C) Tim Potter 2003
+ Copyright (C) Steve Roylance 2003
+ Copyright (C) Andrew Tridgell 2003-2004
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 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 Library General Public
+ License along with this library; if not, write to the
+ Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+*/
+
+/*
+
+ To install this module copy nsswitch/WINBIND to /usr/lib/security and add
+ "WINBIND" in /usr/lib/security/methods.cfg and /etc/security/user
+
+ Note that this module also provides authentication and password
+ changing routines, so you do not need to install the winbind PAM
+ module.
+
+ see
+ http://publib16.boulder.ibm.com/doc_link/en_US/a_doc_lib/aixprggd/kernextc/sec_load_mod.htm
+ for some information in the interface that this module implements
+
+ Many thanks to Julianne Haugh for explaining some of the finer
+ details of this interface.
+
+ To debug this module use uess_test.c (which you can get from tridge)
+ or set "options=debug" in /usr/lib/security/methods.cfg
+
+*/
+
+#include <stdlib.h>
+#include <string.h>
+#include <usersec.h>
+#include <errno.h>
+#include <stdarg.h>
+
+#include "winbind_client.h"
+
+#define WB_AIX_ENCODED '_'
+
+static int debug_enabled;
+
+
+static void logit(const char *format, ...)
+{
+ va_list ap;
+ FILE *f;
+ if (!debug_enabled) {
+ return;
+ }
+ f = fopen("/tmp/WINBIND_DEBUG.log", "a");
+ if (!f) return;
+ va_start(ap, format);
+ vfprintf(f, format, ap);
+ va_end(ap);
+ fclose(f);
+}
+
+
+#define HANDLE_ERRORS(ret) do { \
+ if ((ret) == NSS_STATUS_NOTFOUND) { \
+ errno = ENOENT; \
+ return NULL; \
+ } else if ((ret) != NSS_STATUS_SUCCESS) { \
+ errno = EIO; \
+ return NULL; \
+ } \
+} while (0)
+
+#define STRCPY_RET(dest, src) \
+do { \
+ if (strlen(src)+1 > sizeof(dest)) { errno = EINVAL; return -1; } \
+ strcpy(dest, src); \
+} while (0)
+
+#define STRCPY_RETNULL(dest, src) \
+do { \
+ if (strlen(src)+1 > sizeof(dest)) { errno = EINVAL; return NULL; } \
+ strcpy(dest, src); \
+} while (0)
+
+
+/* free a passwd structure */
+static void free_pwd(struct passwd *pwd)
+{
+ free(pwd->pw_name);
+ free(pwd->pw_passwd);
+ free(pwd->pw_gecos);
+ free(pwd->pw_dir);
+ free(pwd->pw_shell);
+ free(pwd);
+}
+
+/* free a group structure */
+static void free_grp(struct group *grp)
+{
+ int i;
+
+ free(grp->gr_name);
+ free(grp->gr_passwd);
+
+ if (!grp->gr_mem) {
+ free(grp);
+ return;
+ }
+
+ for (i=0; grp->gr_mem[i]; i++) {
+ free(grp->gr_mem[i]);
+ }
+
+ free(grp->gr_mem);
+ free(grp);
+}
+
+
+/* replace commas with nulls, and null terminate */
+static void replace_commas(char *s)
+{
+ char *p, *p0=s;
+ for (p=strchr(s, ','); p; p = strchr(p+1, ',')) {
+ *p=0;
+ p0 = p+1;
+ }
+
+ p0[strlen(p0)+1] = 0;
+}
+
+
+/* the decode_*() routines are used to cope with the fact that AIX 5.2
+ and below cannot handle user or group names longer than 8
+ characters in some interfaces. We use the normalize method to
+ provide a mapping to a username that fits, by using the form '_UID'
+ or '_GID'.
+
+ this only works if you can guarantee that the WB_AIX_ENCODED char
+ is not used as the first char of any other username
+*/
+static unsigned decode_id(const char *name)
+{
+ unsigned id;
+ sscanf(name+1, "%u", &id);
+ return id;
+}
+
+static char *decode_user(const char *name)
+{
+ struct passwd *pwd;
+ unsigned id;
+ char *ret;
+ static struct passwd *wb_aix_getpwuid(uid_t uid);
+
+ sscanf(name+1, "%u", &id);
+ pwd = wb_aix_getpwuid(id);
+ if (!pwd) {
+ return NULL;
+ }
+ ret = strdup(pwd->pw_name);
+
+ free_pwd(pwd);
+
+ logit("decoded '%s' -> '%s'\n", name, ret);
+
+ return ret;
+}
+
+
+/*
+ fill a struct passwd from a winbindd_pw struct, allocating as a single block
+*/
+static struct passwd *fill_pwent(struct winbindd_pw *pw)
+{
+ struct passwd *result;
+
+ result = calloc(1, sizeof(struct passwd));
+ if (!result) {
+ errno = ENOMEM;
+ return NULL;
+ }
+
+ result->pw_uid = pw->pw_uid;
+ result->pw_gid = pw->pw_gid;
+ result->pw_name = strdup(pw->pw_name);
+ result->pw_passwd = strdup(pw->pw_passwd);
+ result->pw_gecos = strdup(pw->pw_gecos);
+ result->pw_dir = strdup(pw->pw_dir);
+ result->pw_shell = strdup(pw->pw_shell);
+
+ return result;
+}
+
+
+/*
+ fill a struct group from a winbindd_pw struct, allocating as a single block
+*/
+static struct group *fill_grent(struct winbindd_gr *gr, char *gr_mem)
+{
+ int i;
+ struct group *result;
+ char *p, *name;
+
+ result = calloc(1, sizeof(struct group));
+ if (!result) {
+ errno = ENOMEM;
+ return NULL;
+ }
+
+ result->gr_gid = gr->gr_gid;
+
+ result->gr_name = strdup(gr->gr_name);
+ result->gr_passwd = strdup(gr->gr_passwd);
+
+ /* Group membership */
+ if ((gr->num_gr_mem < 0) || !gr_mem) {
+ gr->num_gr_mem = 0;
+ }
+
+ if (gr->num_gr_mem == 0) {
+ /* Group is empty */
+ return result;
+ }
+
+ result->gr_mem = (char **)malloc(sizeof(char *) * (gr->num_gr_mem+1));
+ if (!result->gr_mem) {
+ errno = ENOMEM;
+ return NULL;
+ }
+
+ /* Start looking at extra data */
+ i=0;
+ for (name = strtok_r(gr_mem, ",", &p);
+ name;
+ name = strtok_r(NULL, ",", &p)) {
+ if (i == gr->num_gr_mem) {
+ break;
+ }
+ result->gr_mem[i] = strdup(name);
+ i++;
+ }
+
+ /* Terminate list */
+ result->gr_mem[i] = NULL;
+
+ return result;
+}
+
+
+
+/* take a group id and return a filled struct group */
+static struct group *wb_aix_getgrgid(gid_t gid)
+{
+ struct winbindd_response response;
+ struct winbindd_request request;
+ struct group *grp;
+ NSS_STATUS ret;
+
+ logit("getgrgid %d\n", gid);
+
+ ZERO_STRUCT(response);
+ ZERO_STRUCT(request);
+
+ request.data.gid = gid;
+
+ ret = winbindd_request(WINBINDD_GETGRGID, &request, &response);
+
+ logit("getgrgid ret=%d\n", ret);
+
+ HANDLE_ERRORS(ret);
+
+ grp = fill_grent(&response.data.gr, response.extra_data);
+
+ free_response(&response);
+
+ return grp;
+}
+
+/* take a group name and return a filled struct group */
+static struct group *wb_aix_getgrnam(const char *name)
+{
+ struct winbindd_response response;
+ struct winbindd_request request;
+ NSS_STATUS ret;
+ struct group *grp;
+
+ if (*name == WB_AIX_ENCODED) {
+ return wb_aix_getgrgid(decode_id(name));
+ }
+
+ logit("getgrnam '%s'\n", name);
+
+ ZERO_STRUCT(response);
+ ZERO_STRUCT(request);
+
+ STRCPY_RETNULL(request.data.groupname, name);
+
+ ret = winbindd_request(WINBINDD_GETGRNAM, &request, &response);
+
+ HANDLE_ERRORS(ret);
+
+ grp = fill_grent(&response.data.gr, response.extra_data);
+
+ free_response(&response);
+
+ return grp;
+}
+
+
+/* this call doesn't have to fill in the gr_mem, but we do anyway
+ for simplicity */
+static struct group *wb_aix_getgracct(void *id, int type)
+{
+ if (type == 1) {
+ return wb_aix_getgrnam((char *)id);
+ }
+ if (type == 0) {
+ return wb_aix_getgrgid(*(int *)id);
+ }
+ errno = EINVAL;
+ return NULL;
+}
+
+
+/* take a username and return a string containing a comma-separated
+ list of group id numbers to which the user belongs */
+static char *wb_aix_getgrset(char *user)
+{
+ struct winbindd_response response;
+ struct winbindd_request request;
+ NSS_STATUS ret;
+ int i, idx;
+ char *tmpbuf;
+ int num_gids;
+ gid_t *gid_list;
+ char *r_user = user;
+
+ if (*user == WB_AIX_ENCODED) {
+ r_user = decode_user(r_user);
+ if (!r_user) {
+ errno = ENOENT;
+ return NULL;
+ }
+ }
+
+ logit("getgrset '%s'\n", r_user);
+
+ STRCPY_RETNULL(request.data.username, r_user);
+
+ if (*user == WB_AIX_ENCODED) {
+ free(r_user);
+ }
+
+ ret = winbindd_request(WINBINDD_GETGROUPS, &request, &response);
+
+ HANDLE_ERRORS(ret);
+
+ num_gids = response.data.num_entries;
+ gid_list = (gid_t *)response.extra_data;
+
+ /* allocate a space large enough to contruct the string */
+ tmpbuf = malloc(num_gids*12);
+ if (!tmpbuf) {
+ return NULL;
+ }
+
+ for (idx=i=0; i < num_gids-1; i++) {
+ idx += sprintf(tmpbuf+idx, "%u,", gid_list[i]);
+ }
+ idx += sprintf(tmpbuf+idx, "%u", gid_list[i]);
+
+ free_response(&response);
+
+ return tmpbuf;
+}
+
+
+/* take a uid and return a filled struct passwd */
+static struct passwd *wb_aix_getpwuid(uid_t uid)
+{
+ struct winbindd_response response;
+ struct winbindd_request request;
+ NSS_STATUS ret;
+ struct passwd *pwd;
+
+ logit("getpwuid '%d'\n", uid);
+
+ ZERO_STRUCT(response);
+ ZERO_STRUCT(request);
+
+ request.data.uid = uid;
+
+ ret = winbindd_request(WINBINDD_GETPWUID, &request, &response);
+
+ HANDLE_ERRORS(ret);
+
+ pwd = fill_pwent(&response.data.pw);
+
+ free_response(&response);
+
+ logit("getpwuid gave ptr %p\n", pwd);
+
+ return pwd;
+}
+
+
+/* take a username and return a filled struct passwd */
+static struct passwd *wb_aix_getpwnam(const char *name)
+{
+ struct winbindd_response response;
+ struct winbindd_request request;
+ NSS_STATUS ret;
+ struct passwd *pwd;
+
+ if (*name == WB_AIX_ENCODED) {
+ return wb_aix_getpwuid(decode_id(name));
+ }
+
+ logit("getpwnam '%s'\n", name);
+
+ ZERO_STRUCT(response);
+ ZERO_STRUCT(request);
+
+ STRCPY_RETNULL(request.data.username, name);
+
+ ret = winbindd_request(WINBINDD_GETPWNAM, &request, &response);
+
+ HANDLE_ERRORS(ret);
+
+ pwd = fill_pwent(&response.data.pw);
+
+ free_response(&response);
+
+ logit("getpwnam gave ptr %p\n", pwd);
+
+ return pwd;
+}
+
+/*
+ list users
+*/
+static int wb_aix_lsuser(char *attributes[], attrval_t results[], int size)
+{
+ NSS_STATUS ret;
+ struct winbindd_request request;
+ struct winbindd_response response;
+ int len;
+ char *s;
+
+ if (size != 1 || strcmp(attributes[0], S_USERS) != 0) {
+ logit("invalid lsuser op\n");
+ errno = EINVAL;
+ return -1;
+ }
+
+ ZERO_STRUCT(request);
+ ZERO_STRUCT(response);
+
+ ret = winbindd_request(WINBINDD_LIST_USERS, &request, &response);
+ if (ret != 0) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ len = strlen(response.extra_data);
+
+ s = malloc(len+2);
+ if (!s) {
+ free_response(&response);
+ errno = ENOMEM;
+ return -1;
+ }
+
+ memcpy(s, response.extra_data, len+1);
+
+ replace_commas(s);
+
+ results[0].attr_un.au_char = s;
+ results[0].attr_flag = 0;
+
+ free_response(&response);
+
+ return 0;
+}
+
+
+/*
+ list groups
+*/
+static int wb_aix_lsgroup(char *attributes[], attrval_t results[], int size)
+{
+ NSS_STATUS ret;
+ struct winbindd_request request;
+ struct winbindd_response response;
+ int len;
+ char *s;
+
+ if (size != 1 || strcmp(attributes[0], S_GROUPS) != 0) {
+ logit("invalid lsgroup op\n");
+ errno = EINVAL;
+ return -1;
+ }
+
+ ZERO_STRUCT(request);
+ ZERO_STRUCT(response);
+
+ ret = winbindd_request(WINBINDD_LIST_GROUPS, &request, &response);
+ if (ret != 0) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ len = strlen(response.extra_data);
+
+ s = malloc(len+2);
+ if (!s) {
+ free_response(&response);
+ errno = ENOMEM;
+ return -1;
+ }
+
+ memcpy(s, response.extra_data, len+1);
+
+ replace_commas(s);
+
+ results[0].attr_un.au_char = s;
+ results[0].attr_flag = 0;
+
+ free_response(&response);
+
+ return 0;
+}
+
+
+static attrval_t pwd_to_group(struct passwd *pwd)
+{
+ attrval_t r;
+ struct group *grp = wb_aix_getgrgid(pwd->pw_gid);
+
+ if (!grp) {
+ r.attr_flag = EINVAL;
+ } else {
+ r.attr_flag = 0;
+ r.attr_un.au_char = strdup(grp->gr_name);
+ free_grp(grp);
+ }
+
+ return r;
+}
+
+static attrval_t pwd_to_groupsids(struct passwd *pwd)
+{
+ attrval_t r;
+ char *s, *p;
+
+ s = wb_aix_getgrset(pwd->pw_name);
+ if (!s) {
+ r.attr_flag = EINVAL;
+ return r;
+ }
+
+ p = malloc(strlen(s)+2);
+ if (!p) {
+ r.attr_flag = ENOMEM;
+ return r;
+ }
+
+ strcpy(p, s);
+ replace_commas(p);
+ free(s);
+
+ r.attr_un.au_char = p;
+
+ return r;
+}
+
+static attrval_t pwd_to_sid(struct passwd *pwd)
+{
+ struct winbindd_request request;
+ struct winbindd_response response;
+ attrval_t r;
+
+ ZERO_STRUCT(request);
+ ZERO_STRUCT(response);
+
+ request.data.uid = pwd->pw_uid;
+
+ if (winbindd_request(WINBINDD_UID_TO_SID, &request, &response) !=
+ NSS_STATUS_SUCCESS) {
+ r.attr_flag = ENOENT;
+ } else {
+ r.attr_flag = 0;
+ r.attr_un.au_char = strdup(response.data.sid.sid);
+ }
+
+ return r;
+}
+
+static int wb_aix_user_attrib(const char *key, char *attributes[],
+ attrval_t results[], int size)
+{
+ struct passwd *pwd;
+ int i;
+
+ pwd = wb_aix_getpwnam(key);
+ if (!pwd) {
+ errno = ENOENT;
+ return -1;
+ }
+
+ for (i=0;i<size;i++) {
+ results[i].attr_flag = 0;
+
+ if (strcmp(attributes[i], S_ID) == 0) {
+ results[i].attr_un.au_int = pwd->pw_uid;
+ } else if (strcmp(attributes[i], S_PWD) == 0) {
+ results[i].attr_un.au_char = strdup(pwd->pw_passwd);
+ } else if (strcmp(attributes[i], S_HOME) == 0) {
+ results[i].attr_un.au_char = strdup(pwd->pw_dir);
+ } else if (strcmp(attributes[0], S_SHELL) == 0) {
+ results[i].attr_un.au_char = strdup(pwd->pw_shell);
+ } else if (strcmp(attributes[0], S_REGISTRY) == 0) {
+ results[i].attr_un.au_char = strdup("WINBIND");
+ } else if (strcmp(attributes[0], S_GECOS) == 0) {
+ results[i].attr_un.au_char = strdup(pwd->pw_gecos);
+ } else if (strcmp(attributes[0], S_PGRP) == 0) {
+ results[i] = pwd_to_group(pwd);
+ } else if (strcmp(attributes[0], S_GECOS) == 0) {
+ results[i].attr_un.au_char = strdup(pwd->pw_gecos);
+ } else if (strcmp(attributes[0], S_GROUPSIDS) == 0) {
+ results[i] = pwd_to_groupsids(pwd);
+ } else if (strcmp(attributes[0], "SID") == 0) {
+ results[i] = pwd_to_sid(pwd);
+ } else {
+ logit("Unknown user attribute '%s'\n", attributes[i]);
+ results[i].attr_flag = EINVAL;
+ }
+ }
+
+ free_pwd(pwd);
+
+ return 0;
+}
+
+static int wb_aix_group_attrib(const char *key, char *attributes[],
+ attrval_t results[], int size)
+{
+ struct group *grp;
+ int i;
+
+ grp = wb_aix_getgrnam(key);
+ if (!grp) {
+ errno = ENOENT;
+ return -1;
+ }
+
+ for (i=0;i<size;i++) {
+ results[i].attr_flag = 0;
+
+ if (strcmp(attributes[i], S_PWD) == 0) {
+ results[i].attr_un.au_char = strdup(grp->gr_passwd);
+ } else if (strcmp(attributes[i], S_ID) == 0) {
+ results[i].attr_un.au_int = grp->gr_gid;
+ } else {
+ logit("Unknown group attribute '%s'\n", attributes[i]);
+ results[i].attr_flag = EINVAL;
+ }
+ }
+
+ free_grp(grp);
+
+ return 0;
+}
+
+
+/*
+ called for user/group enumerations
+*/
+static int wb_aix_getentry(char *key, char *table, char *attributes[],
+ attrval_t results[], int size)
+{
+ logit("Got getentry with key='%s' table='%s' size=%d attributes[0]='%s'\n",
+ key, table, size, attributes[0]);
+
+ if (strcmp(key, "ALL") == 0 &&
+ strcmp(table, "user") == 0) {
+ return wb_aix_lsuser(attributes, results, size);
+ }
+
+ if (strcmp(key, "ALL") == 0 &&
+ strcmp(table, "group") == 0) {
+ return wb_aix_lsgroup(attributes, results, size);
+ }
+
+ if (strcmp(table, "user") == 0) {
+ return wb_aix_user_attrib(key, attributes, results, size);
+ }
+
+ if (strcmp(table, "group") == 0) {
+ return wb_aix_group_attrib(key, attributes, results, size);
+ }
+
+ logit("Unknown getentry operation key='%s' table='%s'\n", key, table);
+
+ errno = ENOSYS;
+ return -1;
+}
+
+
+
+/*
+ called to start the backend
+*/
+static void *wb_aix_open(const char *name, const char *domain, int mode, char *options)
+{
+ if (strstr(options, "debug")) {
+ debug_enabled = 1;
+ }
+ logit("open name='%s' mode=%d domain='%s' options='%s'\n", name, domain,
+ mode, options);
+ return NULL;
+}
+
+static void wb_aix_close(void *token)
+{
+ logit("close\n");
+ return;
+}
+
+/*
+ return a list of additional attributes supported by the backend
+*/
+static attrlist_t **wb_aix_attrlist(void)
+{
+ attrlist_t **ret;
+ logit("method attrlist called\n");
+ ret = malloc(2*sizeof(attrlist_t *) + sizeof(attrlist_t));
+ if (!ret) {
+ errno = ENOMEM;
+ return NULL;
+ }
+
+ ret[0] = (attrlist_t *)(ret+2);
+
+ /* just one extra attribute - the windows SID */
+ ret[0]->al_name = strdup("SID");
+ ret[0]->al_flags = AL_USERATTR;
+ ret[0]->al_type = SEC_CHAR;
+ ret[1] = NULL;
+
+ return ret;
+}
+
+
+/*
+ turn a long username into a short one. Needed to cope with the 8 char
+ username limit in AIX 5.2 and below
+*/
+static int wb_aix_normalize(char *longname, char *shortname)
+{
+ struct passwd *pwd;
+
+ logit("normalize '%s'\n", longname);
+
+ /* automatically cope with AIX 5.3 with longer usernames
+ when it comes out */
+ if (S_NAMELEN > strlen(longname)) {
+ strcpy(shortname, longname);
+ return 1;
+ }
+
+ pwd = wb_aix_getpwnam(longname);
+ if (!pwd) {
+ errno = ENOENT;
+ return 0;
+ }
+
+ sprintf(shortname, "%c%07u", WB_AIX_ENCODED, pwd->pw_uid);
+
+ free_pwd(pwd);
+
+ return 1;
+}
+
+
+/*
+ authenticate a user
+ */
+static int wb_aix_authenticate(char *user, char *pass,
+ int *reenter, char **message)
+{
+ struct winbindd_request request;
+ struct winbindd_response response;
+ NSS_STATUS result;
+ char *r_user = user;
+
+ logit("authenticate '%s' response='%s'\n", user, pass);
+
+ *reenter = 0;
+ *message = NULL;
+
+ /* Send off request */
+ ZERO_STRUCT(request);
+ ZERO_STRUCT(response);
+
+ if (*user == WB_AIX_ENCODED) {
+ r_user = decode_user(r_user);
+ if (!r_user) {
+ return AUTH_NOTFOUND;
+ }
+ }
+
+ STRCPY_RET(request.data.auth.user, r_user);
+ STRCPY_RET(request.data.auth.pass, pass);
+
+ if (*user == WB_AIX_ENCODED) {
+ free(r_user);
+ }
+
+ result = winbindd_request(WINBINDD_PAM_AUTH, &request, &response);
+
+ free_response(&response);
+
+ logit("auth result %d for '%s'\n", result, user);
+
+ if (result == NSS_STATUS_SUCCESS) {
+ errno = 0;
+ return AUTH_SUCCESS;
+ }
+
+ return AUTH_FAILURE;
+}
+
+
+/*
+ change a user password
+*/
+static int wb_aix_chpass(char *user, char *oldpass, char *newpass, char **message)
+{
+ struct winbindd_request request;
+ struct winbindd_response response;
+ NSS_STATUS result;
+ char *r_user = user;
+
+ if (*user == WB_AIX_ENCODED) {
+ r_user = decode_user(r_user);
+ if (!r_user) {
+ errno = ENOENT;
+ return -1;
+ }
+ }
+
+ logit("chpass '%s' old='%s' new='%s'\n", r_user, oldpass, newpass);
+
+ *message = NULL;
+
+ /* Send off request */
+ ZERO_STRUCT(request);
+ ZERO_STRUCT(response);
+
+ STRCPY_RET(request.data.chauthtok.user, r_user);
+ STRCPY_RET(request.data.chauthtok.oldpass, oldpass);
+ STRCPY_RET(request.data.chauthtok.newpass, newpass);
+
+ if (*user == WB_AIX_ENCODED) {
+ free(r_user);
+ }
+
+ result = winbindd_request(WINBINDD_PAM_CHAUTHTOK, &request, &response);
+
+ free_response(&response);
+
+ if (result == NSS_STATUS_SUCCESS) {
+ errno = 0;
+ return 0;
+ }
+
+ errno = EINVAL;
+ return -1;
+}
+
+/*
+ don't do any password strength testing for now
+*/
+static int wb_aix_passwdrestrictions(char *user, char *newpass, char *oldpass,
+ char **message)
+{
+ logit("passwdresrictions called for '%s'\n", user);
+ return 0;
+}
+
+
+static int wb_aix_passwdexpired(char *user, char **message)
+{
+ logit("passwdexpired '%s'\n", user);
+ /* we should check the account bits here */
+ return 0;
+}
+
+
+/*
+ we can't return a crypt() password
+*/
+static char *wb_aix_getpasswd(char *user)
+{
+ logit("getpasswd '%s'\n", user);
+ errno = ENOSYS;
+ return NULL;
+}
+
+/*
+ this is called to update things like the last login time. We don't
+ currently pass this onto the DC
+*/
+static int wb_aix_putentry(char *key, char *table, char *attributes[],
+ attrval_t values[], int size)
+{
+ logit("putentry key='%s' table='%s' attrib='%s'\n",
+ key, table, size>=1?attributes[0]:"<null>");
+ errno = ENOSYS;
+ return -1;
+}
+
+static int wb_aix_commit(char *key, char *table)
+{
+ logit("commit key='%s' table='%s'\n");
+ errno = ENOSYS;
+ return -1;
+}
+
+static int wb_aix_getgrusers(char *group, void *result, int type, int *size)
+{
+ logit("getgrusers group='%s'\n", group);
+ errno = ENOSYS;
+ return -1;
+}
+
+
+#define DECL_METHOD(x) \
+int method_ ## x(void) \
+{ \
+ logit("UNIMPLEMENTED METHOD '%s'\n", #x); \
+ errno = EINVAL; \
+ return -1; \
+}
+
+#if LOG_UNIMPLEMENTED_CALLS
+DECL_METHOD(delgroup);
+DECL_METHOD(deluser);
+DECL_METHOD(newgroup);
+DECL_METHOD(newuser);
+DECL_METHOD(putgrent);
+DECL_METHOD(putgrusers);
+DECL_METHOD(putpwent);
+DECL_METHOD(lock);
+DECL_METHOD(unlock);
+DECL_METHOD(getcred);
+DECL_METHOD(setcred);
+DECL_METHOD(deletecred);
+#endif
+
+int wb_aix_init(struct secmethod_table *methods)
+{
+ ZERO_STRUCTP(methods);
+
+ methods->method_version = SECMETHOD_VERSION_520;
+
+ methods->method_getgrgid = wb_aix_getgrgid;
+ methods->method_getgrnam = wb_aix_getgrnam;
+ methods->method_getgrset = wb_aix_getgrset;
+ methods->method_getpwnam = wb_aix_getpwnam;
+ methods->method_getpwuid = wb_aix_getpwuid;
+ methods->method_getentry = wb_aix_getentry;
+ methods->method_open = wb_aix_open;
+ methods->method_close = wb_aix_close;
+ methods->method_normalize = wb_aix_normalize;
+ methods->method_passwdexpired = wb_aix_passwdexpired;
+ methods->method_putentry = wb_aix_putentry;
+ methods->method_getpasswd = wb_aix_getpasswd;
+ methods->method_authenticate = wb_aix_authenticate;
+ methods->method_commit = wb_aix_commit;
+ methods->method_chpass = wb_aix_chpass;
+ methods->method_passwdrestrictions = wb_aix_passwdrestrictions;
+ methods->method_getgracct = wb_aix_getgracct;
+ methods->method_getgrusers = wb_aix_getgrusers;
+ methods->method_attrlist = wb_aix_attrlist;
+
+#if LOG_UNIMPLEMENTED_CALLS
+ methods->method_delgroup = method_delgroup;
+ methods->method_deluser = method_deluser;
+ methods->method_newgroup = method_newgroup;
+ methods->method_newuser = method_newuser;
+ methods->method_putgrent = method_putgrent;
+ methods->method_putgrusers = method_putgrusers;
+ methods->method_putpwent = method_putpwent;
+ methods->method_lock = method_lock;
+ methods->method_unlock = method_unlock;
+ methods->method_getcred = method_getcred;
+ methods->method_setcred = method_setcred;
+ methods->method_deletecred = method_deletecred;
+#endif
+
+ return AUTH_SUCCESS;
+}
+
diff --git a/source/nsswitch/winbind_nss_config.h b/source/nsswitch/winbind_nss_config.h
new file mode 100644
index 00000000000..77d1dbe26e0
--- /dev/null
+++ b/source/nsswitch/winbind_nss_config.h
@@ -0,0 +1,142 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ Winbind daemon for ntdom nss module
+
+ Copyright (C) Tim Potter 2000
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 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 Library General Public
+ License along with this library; if not, write to the
+ Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+*/
+
+#ifndef _WINBIND_NSS_CONFIG_H
+#define _WINBIND_NSS_CONFIG_H
+
+/* Include header files from data in config.h file */
+
+#ifndef NO_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#ifdef HAVE_SYS_SELECT_H
+#include <sys/select.h>
+#endif
+
+#ifdef HAVE_SYS_SOCKET_H
+#include <sys/socket.h>
+#endif
+
+#ifdef HAVE_UNIXSOCKET
+#include <sys/un.h>
+#endif
+
+#ifdef HAVE_SYS_TIME_H
+#include <sys/time.h>
+#endif
+
+#ifdef HAVE_GRP_H
+#include <grp.h>
+#endif
+
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+
+#ifdef HAVE_FCNTL_H
+#include <fcntl.h>
+#else
+#ifdef HAVE_SYS_FCNTL_H
+#include <sys/fcntl.h>
+#endif
+#endif
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <errno.h>
+#include <pwd.h>
+#include "nsswitch/winbind_nss.h"
+
+/* I'm trying really hard not to include anything from smb.h with the
+ result of some silly looking redeclaration of structures. */
+
+#ifndef _PSTRING
+#define _PSTRING
+#define PSTRING_LEN 1024
+#define FSTRING_LEN 256
+typedef char pstring[PSTRING_LEN];
+typedef char fstring[FSTRING_LEN];
+#endif
+
+#ifndef _BOOL
+#define _BOOL /* So we don't typedef BOOL again in vfs.h */
+#define False (0)
+#define True (1)
+#define Auto (2)
+typedef int BOOL;
+#endif
+
+#if !defined(uint32)
+#if (SIZEOF_INT == 4)
+#define uint32 unsigned int
+#elif (SIZEOF_LONG == 4)
+#define uint32 unsigned long
+#elif (SIZEOF_SHORT == 4)
+#define uint32 unsigned short
+#endif
+#endif
+
+#if !defined(uint16)
+#if (SIZEOF_SHORT == 4)
+#define uint16 __ERROR___CANNOT_DETERMINE_TYPE_FOR_INT16;
+#else /* SIZEOF_SHORT != 4 */
+#define uint16 unsigned short
+#endif /* SIZEOF_SHORT != 4 */
+#endif
+
+#ifndef uint8
+#define uint8 unsigned char
+#endif
+
+/* zero a structure */
+#ifndef ZERO_STRUCT
+#define ZERO_STRUCT(x) memset((char *)&(x), 0, sizeof(x))
+#endif
+
+/* zero a structure given a pointer to the structure */
+#ifndef ZERO_STRUCTP
+#define ZERO_STRUCTP(x) { if ((x) != NULL) memset((char *)(x), 0, sizeof(*(x))); }
+#endif
+
+/* Some systems (SCO) treat UNIX domain sockets as FIFOs */
+
+#ifndef S_IFSOCK
+#define S_IFSOCK S_IFIFO
+#endif
+
+#ifndef S_ISSOCK
+#define S_ISSOCK(mode) ((mode & S_IFSOCK) == S_IFSOCK)
+#endif
+
+#endif
diff --git a/source/nsswitch/winbind_nss_freebsd.c b/source/nsswitch/winbind_nss_freebsd.c
new file mode 100644
index 00000000000..b73a4ce44f8
--- /dev/null
+++ b/source/nsswitch/winbind_nss_freebsd.c
@@ -0,0 +1,81 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ AIX loadable authentication module, providing identification
+ routines against Samba winbind/Windows NT Domain
+
+ Copyright (C) Aaron Collins 2003
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 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 Library General Public
+ License along with this library; if not, write to the
+ Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+*/
+
+#include "winbind_client.h"
+
+/* Make sure that the module gets registered needed by freebsd 5.1 */
+
+extern enum nss_status _nss_winbind_getgrent_r(struct group *, char *, size_t,
+ int *);
+extern enum nss_status _nss_winbind_getgrnam_r(const char *, struct group *,
+ char *, size_t, int *);
+extern enum nss_status _nss_winbind_getgrgid_r(gid_t gid, struct group *, char *,
+ size_t, int *);
+extern enum nss_status _nss_winbind_setgrent(void);
+extern enum nss_status _nss_winbind_endgrent(void);
+
+extern enum nss_status _nss_winbind_getpwent_r(struct passwd *, char *, size_t,
+ int *);
+extern enum nss_status _nss_winbind_getpwnam_r(const char *, struct passwd *,
+ char *, size_t, int *);
+extern enum nss_status _nss_winbind_getpwuid_r(gid_t gid, struct passwd *, char *,
+ size_t, int *);
+extern enum nss_status _nss_winbind_setpwent(void);
+extern enum nss_status _nss_winbind_endpwent(void);
+
+NSS_METHOD_PROTOTYPE(__nss_compat_getgrnam_r);
+NSS_METHOD_PROTOTYPE(__nss_compat_getgrgid_r);
+NSS_METHOD_PROTOTYPE(__nss_compat_getgrent_r);
+NSS_METHOD_PROTOTYPE(__nss_compat_setgrent);
+NSS_METHOD_PROTOTYPE(__nss_compat_endgrent);
+
+NSS_METHOD_PROTOTYPE(__nss_compat_getpwnam_r);
+NSS_METHOD_PROTOTYPE(__nss_compat_getpwuid_r);
+NSS_METHOD_PROTOTYPE(__nss_compat_getpwent_r);
+NSS_METHOD_PROTOTYPE(__nss_compat_setpwent);
+NSS_METHOD_PROTOTYPE(__nss_compat_endpwent);
+
+static ns_mtab methods[] = {
+{ NSDB_GROUP, "getgrnam_r", __nss_compat_getgrnam_r, _nss_winbind_getgrnam_r },
+{ NSDB_GROUP, "getgrgid_r", __nss_compat_getgrgid_r, _nss_winbind_getgrgid_r },
+{ NSDB_GROUP, "getgrent_r", __nss_compat_getgrent_r, _nss_winbind_getgrent_r },
+{ NSDB_GROUP, "endgrent", __nss_compat_setgrent, _nss_winbind_setgrent },
+{ NSDB_GROUP, "setgrent", __nss_compat_endgrent, _nss_winbind_endgrent },
+
+{ NSDB_PASSWD, "getpwnam_r", __nss_compat_getpwnam_r, _nss_winbind_getpwnam_r },
+{ NSDB_PASSWD, "getpwuid_r", __nss_compat_getpwuid_r, _nss_winbind_getpwuid_r },
+{ NSDB_PASSWD, "getpwent_r", __nss_compat_getpwent_r, _nss_winbind_getpwent_r },
+{ NSDB_PASSWD, "endpwent", __nss_compat_setpwent, _nss_winbind_setpwent },
+{ NSDB_PASSWD, "setpwent", __nss_compat_endpwent, _nss_winbind_endpwent },
+
+};
+
+ns_mtab *
+nss_module_register(const char *source, unsigned int *mtabsize,
+ nss_module_unregister_fn *unreg)
+{
+ *mtabsize = sizeof(methods)/sizeof(methods[0]);
+ *unreg = NULL;
+ return (methods);
+}
diff --git a/source/nsswitch/winbind_nss_hpux.h b/source/nsswitch/winbind_nss_hpux.h
new file mode 100644
index 00000000000..d2a5057bf51
--- /dev/null
+++ b/source/nsswitch/winbind_nss_hpux.h
@@ -0,0 +1,139 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ Donated by HP to enable Winbindd to build on HPUX 11.x.
+ Copyright (C) Jeremy Allison 2002.
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 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 Library General Public
+ License along with this library; if not, write to the
+ Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+*/
+
+#ifndef _WINBIND_NSS_HPUX_H
+#define _WINBIND_NSS_HPUX_H
+
+#include <nsswitch.h>
+
+#define NSS_STATUS_SUCCESS NSS_SUCCESS
+#define NSS_STATUS_NOTFOUND NSS_NOTFOUND
+#define NSS_STATUS_UNAVAIL NSS_UNAVAIL
+#define NSS_STATUS_TRYAGAIN NSS_TRYAGAIN
+
+#ifdef HAVE_SYNCH_H
+#include <synch.h>
+#endif
+#ifdef HAVE_PTHREAD_H
+#include <pthread.h>
+#endif
+
+typedef enum {
+ NSS_SUCCESS,
+ NSS_NOTFOUND,
+ NSS_UNAVAIL,
+ NSS_TRYAGAIN
+} nss_status_t;
+
+typedef nss_status_t NSS_STATUS;
+
+struct nss_backend;
+
+typedef nss_status_t (*nss_backend_op_t)(struct nss_backend *, void *args);
+
+struct nss_backend {
+ nss_backend_op_t *ops;
+ int n_ops;
+};
+typedef struct nss_backend nss_backend_t;
+typedef int nss_dbop_t;
+
+#include <errno.h>
+#include <netdb.h>
+#include <limits.h>
+
+#ifndef NSS_INCLUDE_UNSAFE
+#define NSS_INCLUDE_UNSAFE 1 /* Build old, MT-unsafe interfaces, */
+#endif /* NSS_INCLUDE_UNSAFE */
+
+enum nss_netgr_argn {
+ NSS_NETGR_MACHINE,
+ NSS_NETGR_USER,
+ NSS_NETGR_DOMAIN,
+ NSS_NETGR_N
+};
+
+enum nss_netgr_status {
+ NSS_NETGR_FOUND,
+ NSS_NETGR_NO,
+ NSS_NETGR_NOMEM
+};
+
+typedef unsigned nss_innetgr_argc;
+typedef char **nss_innetgr_argv;
+
+struct nss_innetgr_1arg {
+ nss_innetgr_argc argc;
+ nss_innetgr_argv argv;
+};
+
+typedef struct {
+ void *result; /* "result" parameter to getXbyY_r() */
+ char *buffer; /* "buffer" " " */
+ int buflen; /* "buflen" " " */
+} nss_XbyY_buf_t;
+
+extern nss_XbyY_buf_t *_nss_XbyY_buf_alloc(int struct_size, int buffer_size);
+extern void _nss_XbyY_buf_free(nss_XbyY_buf_t *);
+
+union nss_XbyY_key {
+ uid_t uid;
+ gid_t gid;
+ const char *name;
+ int number;
+ struct {
+ long net;
+ int type;
+ } netaddr;
+ struct {
+ const char *addr;
+ int len;
+ int type;
+ } hostaddr;
+ struct {
+ union {
+ const char *name;
+ int port;
+ } serv;
+ const char *proto;
+ } serv;
+ void *ether;
+};
+
+typedef struct nss_XbyY_args {
+ nss_XbyY_buf_t buf;
+ int stayopen;
+ /*
+ * Support for setXXXent(stayopen)
+ * Used only in hosts, protocols,
+ * networks, rpc, and services.
+ */
+ int (*str2ent)(const char *instr, int instr_len, void *ent, char *buffer, int buflen);
+ union nss_XbyY_key key;
+
+ void *returnval;
+ int erange;
+ int h_errno;
+ nss_status_t status;
+} nss_XbyY_args_t;
+
+#endif /* _WINBIND_NSS_HPUX_H */
diff --git a/source/nsswitch/winbind_nss_irix.c b/source/nsswitch/winbind_nss_irix.c
new file mode 100644
index 00000000000..3a9d6c01ab5
--- /dev/null
+++ b/source/nsswitch/winbind_nss_irix.c
@@ -0,0 +1,515 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ Windows NT Domain nsswitch module
+
+ Copyright (C) Tim Potter 2000
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 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 Library General Public
+ License along with this library; if not, write to the
+ Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+*/
+
+#include "winbind_client.h"
+
+#ifdef HAVE_NS_API_H
+#undef VOLATILE
+#include <ns_daemon.h>
+#endif
+
+/* Maximum number of users to pass back over the unix domain socket
+ per call. This is not a static limit on the total number of users
+ or groups returned in total. */
+
+#define MAX_GETPWENT_USERS 250
+#define MAX_GETGRENT_USERS 250
+
+/* Prototypes from wb_common.c */
+
+extern int winbindd_fd;
+
+#ifdef HAVE_NS_API_H
+
+/* IRIX version */
+
+static int send_next_request(nsd_file_t *, struct winbindd_request *);
+static int do_list(int state, nsd_file_t *rq);
+
+static nsd_file_t *current_rq = NULL;
+static int current_winbind_xid = 0;
+static int next_winbind_xid = 0;
+
+typedef struct winbind_xid {
+ int xid;
+ nsd_file_t *rq;
+ struct winbindd_request *request;
+ struct winbind_xid *next;
+} winbind_xid_t;
+
+static winbind_xid_t *winbind_xids = (winbind_xid_t *)0;
+
+static int
+winbind_xid_new(int xid, nsd_file_t *rq, struct winbindd_request *request)
+{
+ winbind_xid_t *new;
+
+ nsd_logprintf(NSD_LOG_LOW,
+ "entering winbind_xid_new xid = %d rq = 0x%x, request = 0x%x\n",
+ xid, rq, request);
+ new = (winbind_xid_t *)nsd_calloc(1,sizeof(winbind_xid_t));
+ if (!new) {
+ nsd_logprintf(NSD_LOG_RESOURCE,"winbind_xid_new: failed malloc\n");
+ return NSD_ERROR;
+ }
+
+ new->xid = xid;
+ new->rq = rq;
+ new->request = request;
+ new->next = winbind_xids;
+ winbind_xids = new;
+
+ return NSD_CONTINUE;
+}
+
+/*
+** This routine will look down the xid list and return the request
+** associated with an xid. We remove the record if it is found.
+*/
+nsd_file_t *
+winbind_xid_lookup(int xid, struct winbindd_request **requestp)
+{
+ winbind_xid_t **last, *dx;
+ nsd_file_t *result=0;
+
+ for (last = &winbind_xids, dx = winbind_xids; dx && (dx->xid != xid);
+ last = &dx->next, dx = dx->next);
+ if (dx) {
+ *last = dx->next;
+ result = dx->rq;
+ *requestp = dx->request;
+ SAFE_FREE(dx);
+ }
+ nsd_logprintf(NSD_LOG_LOW,
+ "entering winbind_xid_lookup xid = %d rq = 0x%x, request = 0x%x\n",
+ xid, result, dx->request);
+
+ return result;
+}
+
+static int
+winbind_startnext_timeout(nsd_file_t **rqp, nsd_times_t *to)
+{
+ nsd_file_t *rq;
+ struct winbindd_request *request;
+
+ nsd_logprintf(NSD_LOG_MIN, "timeout (winbind startnext)\n");
+ rq = to->t_file;
+ *rqp = rq;
+ nsd_timeout_remove(rq);
+ request = to->t_clientdata;
+ return(send_next_request(rq, request));
+}
+
+static void
+dequeue_request()
+{
+ nsd_file_t *rq;
+ struct winbindd_request *request;
+
+ /*
+ * Check for queued requests
+ */
+ if (winbind_xids) {
+ nsd_logprintf(NSD_LOG_MIN, "timeout (winbind) unqueue xid %d\n",
+ current_winbind_xid);
+ rq = winbind_xid_lookup(current_winbind_xid++, &request);
+ /* cause a timeout on the queued request so we can send it */
+ nsd_timeout_new(rq,1,winbind_startnext_timeout,request);
+ }
+}
+
+static int
+do_request(nsd_file_t *rq, struct winbindd_request *request)
+{
+ if (winbind_xids == NULL) {
+ /*
+ * No outstanding requests.
+ * Send off the request to winbindd
+ */
+ nsd_logprintf(NSD_LOG_MIN, "lookup (winbind) sending request\n");
+ return(send_next_request(rq, request));
+ } else {
+ /*
+ * Just queue it up for now - previous callout or timout
+ * will start it up
+ */
+ nsd_logprintf(NSD_LOG_MIN,
+ "lookup (winbind): queue request xid = %d\n",
+ next_winbind_xid);
+ return(winbind_xid_new(next_winbind_xid++, rq, request));
+ }
+}
+
+static int
+winbind_callback(nsd_file_t **rqp, int fd)
+{
+ struct winbindd_response response;
+ struct winbindd_pw *pw = &response.data.pw;
+ struct winbindd_gr *gr = &response.data.gr;
+ nsd_file_t *rq;
+ NSS_STATUS status;
+ fstring result;
+ char *members;
+ int i, maxlen;
+
+ dequeue_request();
+
+ nsd_logprintf(NSD_LOG_MIN, "entering callback (winbind)\n");
+
+ rq = current_rq;
+ *rqp = rq;
+
+ nsd_timeout_remove(rq);
+ nsd_callback_remove(fd);
+
+ ZERO_STRUCT(response);
+ status = winbindd_get_response(&response);
+
+ if (status != NSS_STATUS_SUCCESS) {
+ /* free any extra data area in response structure */
+ free_response(&response);
+ nsd_logprintf(NSD_LOG_MIN,
+ "callback (winbind) returning not found, status = %d\n",
+ status);
+ rq->f_status = NS_NOTFOUND;
+ return NSD_NEXT;
+ }
+
+ maxlen = sizeof(result) - 1;
+
+ switch ((int)rq->f_cmd_data) {
+ case WINBINDD_WINS_BYNAME:
+ case WINBINDD_WINS_BYIP:
+ snprintf(result,maxlen,"%s\n",response.data.winsresp);
+ break;
+ case WINBINDD_GETPWUID:
+ case WINBINDD_GETPWNAM:
+ snprintf(result,maxlen,"%s:%s:%d:%d:%s:%s:%s\n",
+ pw->pw_name,
+ pw->pw_passwd,
+ pw->pw_uid,
+ pw->pw_gid,
+ pw->pw_gecos,
+ pw->pw_dir,
+ pw->pw_shell);
+ break;
+ case WINBINDD_GETGRNAM:
+ case WINBINDD_GETGRGID:
+ if (gr->num_gr_mem && response.extra_data)
+ members = response.extra_data;
+ else
+ members = "";
+ snprintf(result,maxlen,"%s:%s:%d:%s\n",
+ gr->gr_name, gr->gr_passwd, gr->gr_gid, members);
+ break;
+ case WINBINDD_SETGRENT:
+ case WINBINDD_SETPWENT:
+ nsd_logprintf(NSD_LOG_MIN, "callback (winbind) - SETPWENT/SETGRENT\n");
+ free_response(&response);
+ return(do_list(1,rq));
+ case WINBINDD_GETGRENT:
+ case WINBINDD_GETGRLST:
+ nsd_logprintf(NSD_LOG_MIN,
+ "callback (winbind) - %d GETGRENT responses\n",
+ response.data.num_entries);
+ if (response.data.num_entries) {
+ gr = (struct winbindd_gr *)response.extra_data;
+ if (! gr ) {
+ nsd_logprintf(NSD_LOG_MIN, " no extra_data\n");
+ free_response(&response);
+ return NSD_ERROR;
+ }
+ members = (char *)response.extra_data +
+ (response.data.num_entries * sizeof(struct winbindd_gr));
+ for (i = 0; i < response.data.num_entries; i++) {
+ snprintf(result,maxlen,"%s:%s:%d:%s\n",
+ gr->gr_name, gr->gr_passwd, gr->gr_gid,
+ &members[gr->gr_mem_ofs]);
+ nsd_logprintf(NSD_LOG_MIN, " GETGRENT %s\n",result);
+ nsd_append_element(rq,NS_SUCCESS,result,strlen(result));
+ gr++;
+ }
+ }
+ i = response.data.num_entries;
+ free_response(&response);
+ if (i < MAX_GETPWENT_USERS)
+ return(do_list(2,rq));
+ else
+ return(do_list(1,rq));
+ case WINBINDD_GETPWENT:
+ nsd_logprintf(NSD_LOG_MIN,
+ "callback (winbind) - %d GETPWENT responses\n",
+ response.data.num_entries);
+ if (response.data.num_entries) {
+ pw = (struct winbindd_pw *)response.extra_data;
+ if (! pw ) {
+ nsd_logprintf(NSD_LOG_MIN, " no extra_data\n");
+ free_response(&response);
+ return NSD_ERROR;
+ }
+ for (i = 0; i < response.data.num_entries; i++) {
+ snprintf(result,maxlen,"%s:%s:%d:%d:%s:%s:%s",
+ pw->pw_name,
+ pw->pw_passwd,
+ pw->pw_uid,
+ pw->pw_gid,
+ pw->pw_gecos,
+ pw->pw_dir,
+ pw->pw_shell);
+ nsd_logprintf(NSD_LOG_MIN, " GETPWENT %s\n",result);
+ nsd_append_element(rq,NS_SUCCESS,result,strlen(result));
+ pw++;
+ }
+ }
+ i = response.data.num_entries;
+ free_response(&response);
+ if (i < MAX_GETPWENT_USERS)
+ return(do_list(2,rq));
+ else
+ return(do_list(1,rq));
+ case WINBINDD_ENDGRENT:
+ case WINBINDD_ENDPWENT:
+ nsd_logprintf(NSD_LOG_MIN, "callback (winbind) - ENDPWENT/ENDGRENT\n");
+ nsd_append_element(rq,NS_SUCCESS,"\n",1);
+ free_response(&response);
+ return NSD_NEXT;
+ default:
+ free_response(&response);
+ nsd_logprintf(NSD_LOG_MIN, "callback (winbind) - no valid command\n");
+ return NSD_NEXT;
+ }
+ nsd_logprintf(NSD_LOG_MIN, "callback (winbind) %s\n", result);
+ /* free any extra data area in response structure */
+ free_response(&response);
+ nsd_set_result(rq,NS_SUCCESS,result,strlen(result),VOLATILE);
+ return NSD_OK;
+}
+
+static int
+winbind_timeout(nsd_file_t **rqp, nsd_times_t *to)
+{
+ nsd_file_t *rq;
+
+ dequeue_request();
+
+ nsd_logprintf(NSD_LOG_MIN, "timeout (winbind)\n");
+
+ rq = to->t_file;
+ *rqp = rq;
+
+ /* Remove the callback and timeout */
+ nsd_callback_remove(winbindd_fd);
+ nsd_timeout_remove(rq);
+
+ rq->f_status = NS_NOTFOUND;
+ return NSD_NEXT;
+}
+
+static int
+send_next_request(nsd_file_t *rq, struct winbindd_request *request)
+{
+ NSS_STATUS status;
+ long timeout;
+
+ timeout = 1000;
+
+ nsd_logprintf(NSD_LOG_MIN, "send_next_request (winbind) %d to = %d\n",
+ rq->f_cmd_data, timeout);
+ status = winbindd_send_request((int)rq->f_cmd_data,request);
+ SAFE_FREE(request);
+
+ if (status != NSS_STATUS_SUCCESS) {
+ nsd_logprintf(NSD_LOG_MIN,
+ "send_next_request (winbind) error status = %d\n",status);
+ rq->f_status = status;
+ return NSD_NEXT;
+ }
+
+ current_rq = rq;
+
+ /*
+ * Set up callback and timeouts
+ */
+ nsd_logprintf(NSD_LOG_MIN, "send_next_request (winbind) fd = %d\n",winbindd_fd);
+ nsd_callback_new(winbindd_fd,winbind_callback,NSD_READ);
+ nsd_timeout_new(rq,timeout,winbind_timeout,(void *)0);
+ return NSD_CONTINUE;
+}
+
+int init(void)
+{
+ nsd_logprintf(NSD_LOG_MIN, "entering init (winbind)\n");
+ return(NSD_OK);
+}
+
+int lookup(nsd_file_t *rq)
+{
+ char *map;
+ char *key;
+ struct winbindd_request *request;
+
+ nsd_logprintf(NSD_LOG_MIN, "entering lookup (winbind)\n");
+ if (! rq)
+ return NSD_ERROR;
+
+ map = nsd_attr_fetch_string(rq->f_attrs, "table", (char*)0);
+ key = nsd_attr_fetch_string(rq->f_attrs, "key", (char*)0);
+ if (! map || ! key) {
+ nsd_logprintf(NSD_LOG_MIN, "lookup (winbind) table or key not defined\n");
+ rq->f_status = NS_BADREQ;
+ return NSD_ERROR;
+ }
+
+ nsd_logprintf(NSD_LOG_MIN, "lookup (winbind %s)\n",map);
+
+ request = (struct winbindd_request *)nsd_calloc(1,sizeof(struct winbindd_request));
+ if (! request) {
+ nsd_logprintf(NSD_LOG_RESOURCE,
+ "lookup (winbind): failed malloc\n");
+ return NSD_ERROR;
+ }
+
+ if (strcasecmp(map,"passwd.byuid") == 0) {
+ request->data.uid = atoi(key);
+ rq->f_cmd_data = (void *)WINBINDD_GETPWUID;
+ } else if (strcasecmp(map,"passwd.byname") == 0) {
+ strncpy(request->data.username, key,
+ sizeof(request->data.username) - 1);
+ request->data.username[sizeof(request->data.username) - 1] = '\0';
+ rq->f_cmd_data = (void *)WINBINDD_GETPWNAM;
+ } else if (strcasecmp(map,"group.byname") == 0) {
+ strncpy(request->data.groupname, key,
+ sizeof(request->data.groupname) - 1);
+ request->data.groupname[sizeof(request->data.groupname) - 1] = '\0';
+ rq->f_cmd_data = (void *)WINBINDD_GETGRNAM;
+ } else if (strcasecmp(map,"group.bygid") == 0) {
+ request->data.gid = atoi(key);
+ rq->f_cmd_data = (void *)WINBINDD_GETGRGID;
+ } else if (strcasecmp(map,"hosts.byname") == 0) {
+ strncpy(request->data.winsreq, key, sizeof(request->data.winsreq) - 1);
+ request->data.winsreq[sizeof(request->data.winsreq) - 1] = '\0';
+ rq->f_cmd_data = (void *)WINBINDD_WINS_BYNAME;
+ } else if (strcasecmp(map,"hosts.byaddr") == 0) {
+ strncpy(request->data.winsreq, key, sizeof(request->data.winsreq) - 1);
+ request->data.winsreq[sizeof(request->data.winsreq) - 1] = '\0';
+ rq->f_cmd_data = (void *)WINBINDD_WINS_BYIP;
+ } else {
+ /*
+ * Don't understand this map - just return not found
+ */
+ nsd_logprintf(NSD_LOG_MIN, "lookup (winbind) unknown table\n");
+ SAFE_FREE(request);
+ rq->f_status = NS_NOTFOUND;
+ return NSD_NEXT;
+ }
+
+ return(do_request(rq, request));
+}
+
+int list(nsd_file_t *rq)
+{
+ char *map;
+
+ nsd_logprintf(NSD_LOG_MIN, "entering list (winbind)\n");
+ if (! rq)
+ return NSD_ERROR;
+
+ map = nsd_attr_fetch_string(rq->f_attrs, "table", (char*)0);
+ if (! map ) {
+ nsd_logprintf(NSD_LOG_MIN, "list (winbind) table not defined\n");
+ rq->f_status = NS_BADREQ;
+ return NSD_ERROR;
+ }
+
+ nsd_logprintf(NSD_LOG_MIN, "list (winbind %s)\n",map);
+
+ return (do_list(0,rq));
+}
+
+static int
+do_list(int state, nsd_file_t *rq)
+{
+ char *map;
+ struct winbindd_request *request;
+
+ nsd_logprintf(NSD_LOG_MIN, "entering do_list (winbind) state = %d\n",state);
+
+ map = nsd_attr_fetch_string(rq->f_attrs, "table", (char*)0);
+ request = (struct winbindd_request *)nsd_calloc(1,sizeof(struct winbindd_request));
+ if (! request) {
+ nsd_logprintf(NSD_LOG_RESOURCE,
+ "do_list (winbind): failed malloc\n");
+ return NSD_ERROR;
+ }
+
+ if (strcasecmp(map,"passwd.byname") == 0) {
+ switch (state) {
+ case 0:
+ rq->f_cmd_data = (void *)WINBINDD_SETPWENT;
+ break;
+ case 1:
+ request->data.num_entries = MAX_GETPWENT_USERS;
+ rq->f_cmd_data = (void *)WINBINDD_GETPWENT;
+ break;
+ case 2:
+ rq->f_cmd_data = (void *)WINBINDD_ENDPWENT;
+ break;
+ default:
+ nsd_logprintf(NSD_LOG_MIN, "do_list (winbind) unknown state\n");
+ SAFE_FREE(request);
+ rq->f_status = NS_NOTFOUND;
+ return NSD_NEXT;
+ }
+ } else if (strcasecmp(map,"group.byname") == 0) {
+ switch (state) {
+ case 0:
+ rq->f_cmd_data = (void *)WINBINDD_SETGRENT;
+ break;
+ case 1:
+ request->data.num_entries = MAX_GETGRENT_USERS;
+ rq->f_cmd_data = (void *)WINBINDD_GETGRENT;
+ break;
+ case 2:
+ rq->f_cmd_data = (void *)WINBINDD_ENDGRENT;
+ break;
+ default:
+ nsd_logprintf(NSD_LOG_MIN, "do_list (winbind) unknown state\n");
+ SAFE_FREE(request);
+ rq->f_status = NS_NOTFOUND;
+ return NSD_NEXT;
+ }
+ } else {
+ /*
+ * Don't understand this map - just return not found
+ */
+ nsd_logprintf(NSD_LOG_MIN, "do_list (winbind) unknown table\n");
+ SAFE_FREE(request);
+ rq->f_status = NS_NOTFOUND;
+ return NSD_NEXT;
+ }
+
+ return(do_request(rq, request));
+}
+
+#endif /* HAVE_NS_API_H */
diff --git a/source/nsswitch/winbind_nss_irix.h b/source/nsswitch/winbind_nss_irix.h
new file mode 100644
index 00000000000..7878abb981a
--- /dev/null
+++ b/source/nsswitch/winbind_nss_irix.h
@@ -0,0 +1,48 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ Winbind daemon for ntdom nss module
+
+ Copyright (C) Tim Potter 2000
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 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 Library General Public
+ License along with this library; if not, write to the
+ Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+*/
+
+#ifndef _WINBIND_NSS_IRIX_H
+#define _WINBIND_NSS_IRIX_H
+
+/* following required to prevent warnings of double definition
+ * of datum from ns_api.h
+*/
+#ifdef DATUM
+#define _DATUM_DEFINED
+#endif
+
+#include <ns_api.h>
+
+typedef enum
+{
+ NSS_STATUS_SUCCESS=NS_SUCCESS,
+ NSS_STATUS_NOTFOUND=NS_NOTFOUND,
+ NSS_STATUS_UNAVAIL=NS_UNAVAIL,
+ NSS_STATUS_TRYAGAIN=NS_TRYAGAIN
+} NSS_STATUS;
+
+#define NSD_MEM_STATIC 0
+#define NSD_MEM_VOLATILE 1
+#define NSD_MEM_DYNAMIC 2
+
+#endif /* _WINBIND_NSS_IRIX_H */
diff --git a/source/nsswitch/winbind_nss_linux.c b/source/nsswitch/winbind_nss_linux.c
new file mode 100644
index 00000000000..ae2bcc7ade9
--- /dev/null
+++ b/source/nsswitch/winbind_nss_linux.c
@@ -0,0 +1,1149 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ Windows NT Domain nsswitch module
+
+ Copyright (C) Tim Potter 2000
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 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 Library General Public
+ License along with this library; if not, write to the
+ Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+*/
+
+#include "winbind_client.h"
+
+/* Maximum number of users to pass back over the unix domain socket
+ per call. This is not a static limit on the total number of users
+ or groups returned in total. */
+
+#define MAX_GETPWENT_USERS 250
+#define MAX_GETGRENT_USERS 250
+
+/* Prototypes from wb_common.c */
+
+extern int winbindd_fd;
+
+/* Allocate some space from the nss static buffer. The buffer and buflen
+ are the pointers passed in by the C library to the _nss_ntdom_*
+ functions. */
+
+static char *get_static(char **buffer, size_t *buflen, size_t len)
+{
+ char *result;
+
+ /* Error check. We return false if things aren't set up right, or
+ there isn't enough buffer space left. */
+
+ if ((buffer == NULL) || (buflen == NULL) || (*buflen < len)) {
+ return NULL;
+ }
+
+ /* Return an index into the static buffer */
+
+ result = *buffer;
+ *buffer += len;
+ *buflen -= len;
+
+ return result;
+}
+
+/* I've copied the strtok() replacement function next_token() from
+ lib/util_str.c as I really don't want to have to link in any other
+ objects if I can possibly avoid it. */
+
+BOOL next_token(char **ptr,char *buff,char *sep, size_t bufsize)
+{
+ char *s;
+ BOOL quoted;
+ size_t len=1;
+
+ if (!ptr) return(False);
+
+ s = *ptr;
+
+ /* default to simple separators */
+ if (!sep) sep = " \t\n\r";
+
+ /* find the first non sep char */
+ while (*s && strchr(sep,*s)) s++;
+
+ /* nothing left? */
+ if (! *s) return(False);
+
+ /* copy over the token */
+ for (quoted = False; len < bufsize && *s && (quoted || !strchr(sep,*s)); s++) {
+ if (*s == '\"') {
+ quoted = !quoted;
+ } else {
+ len++;
+ *buff++ = *s;
+ }
+ }
+
+ *ptr = (*s) ? s+1 : s;
+ *buff = 0;
+
+ return(True);
+}
+
+
+/* Fill a pwent structure from a winbindd_response structure. We use
+ the static data passed to us by libc to put strings and stuff in.
+ Return NSS_STATUS_TRYAGAIN if we run out of memory. */
+
+static NSS_STATUS fill_pwent(struct passwd *result,
+ struct winbindd_pw *pw,
+ char **buffer, size_t *buflen)
+{
+ /* User name */
+
+ if ((result->pw_name =
+ get_static(buffer, buflen, strlen(pw->pw_name) + 1)) == NULL) {
+
+ /* Out of memory */
+
+ return NSS_STATUS_TRYAGAIN;
+ }
+
+ strcpy(result->pw_name, pw->pw_name);
+
+ /* Password */
+
+ if ((result->pw_passwd =
+ get_static(buffer, buflen, strlen(pw->pw_passwd) + 1)) == NULL) {
+
+ /* Out of memory */
+
+ return NSS_STATUS_TRYAGAIN;
+ }
+
+ strcpy(result->pw_passwd, pw->pw_passwd);
+
+ /* [ug]id */
+
+ result->pw_uid = pw->pw_uid;
+ result->pw_gid = pw->pw_gid;
+
+ /* GECOS */
+
+ if ((result->pw_gecos =
+ get_static(buffer, buflen, strlen(pw->pw_gecos) + 1)) == NULL) {
+
+ /* Out of memory */
+
+ return NSS_STATUS_TRYAGAIN;
+ }
+
+ strcpy(result->pw_gecos, pw->pw_gecos);
+
+ /* Home directory */
+
+ if ((result->pw_dir =
+ get_static(buffer, buflen, strlen(pw->pw_dir) + 1)) == NULL) {
+
+ /* Out of memory */
+
+ return NSS_STATUS_TRYAGAIN;
+ }
+
+ strcpy(result->pw_dir, pw->pw_dir);
+
+ /* Logon shell */
+
+ if ((result->pw_shell =
+ get_static(buffer, buflen, strlen(pw->pw_shell) + 1)) == NULL) {
+
+ /* Out of memory */
+
+ return NSS_STATUS_TRYAGAIN;
+ }
+
+ strcpy(result->pw_shell, pw->pw_shell);
+
+ /* The struct passwd for Solaris has some extra fields which must
+ be initialised or nscd crashes. */
+
+#if HAVE_PASSWD_PW_COMMENT
+ result->pw_comment = "";
+#endif
+
+#if HAVE_PASSWD_PW_AGE
+ result->pw_age = "";
+#endif
+
+ return NSS_STATUS_SUCCESS;
+}
+
+/* Fill a grent structure from a winbindd_response structure. We use
+ the static data passed to us by libc to put strings and stuff in.
+ Return NSS_STATUS_TRYAGAIN if we run out of memory. */
+
+static NSS_STATUS fill_grent(struct group *result, struct winbindd_gr *gr,
+ char *gr_mem, char **buffer, size_t *buflen)
+{
+ fstring name;
+ int i;
+ char *tst;
+
+ /* Group name */
+
+ if ((result->gr_name =
+ get_static(buffer, buflen, strlen(gr->gr_name) + 1)) == NULL) {
+
+ /* Out of memory */
+
+ return NSS_STATUS_TRYAGAIN;
+ }
+
+ strcpy(result->gr_name, gr->gr_name);
+
+ /* Password */
+
+ if ((result->gr_passwd =
+ get_static(buffer, buflen, strlen(gr->gr_passwd) + 1)) == NULL) {
+
+ /* Out of memory */
+
+ return NSS_STATUS_TRYAGAIN;
+ }
+
+ strcpy(result->gr_passwd, gr->gr_passwd);
+
+ /* gid */
+
+ result->gr_gid = gr->gr_gid;
+
+ /* Group membership */
+
+ if ((gr->num_gr_mem < 0) || !gr_mem) {
+ gr->num_gr_mem = 0;
+ }
+
+ /* this next value is a pointer to a pointer so let's align it */
+
+ /* Calculate number of extra bytes needed to align on pointer size boundry */
+ if ((i = (unsigned long)(*buffer) % sizeof(char*)) != 0)
+ i = sizeof(char*) - i;
+
+ if ((tst = get_static(buffer, buflen, ((gr->num_gr_mem + 1) *
+ sizeof(char *)+i))) == NULL) {
+
+ /* Out of memory */
+
+ return NSS_STATUS_TRYAGAIN;
+ }
+ result->gr_mem = (char **)(tst + i);
+
+ if (gr->num_gr_mem == 0) {
+
+ /* Group is empty */
+
+ *(result->gr_mem) = NULL;
+ return NSS_STATUS_SUCCESS;
+ }
+
+ /* Start looking at extra data */
+
+ i = 0;
+
+ while(next_token((char **)&gr_mem, name, ",", sizeof(fstring))) {
+
+ /* Allocate space for member */
+
+ if (((result->gr_mem)[i] =
+ get_static(buffer, buflen, strlen(name) + 1)) == NULL) {
+
+ /* Out of memory */
+
+ return NSS_STATUS_TRYAGAIN;
+ }
+
+ strcpy((result->gr_mem)[i], name);
+ i++;
+ }
+
+ /* Terminate list */
+
+ (result->gr_mem)[i] = NULL;
+
+ return NSS_STATUS_SUCCESS;
+}
+
+/*
+ * NSS user functions
+ */
+
+static struct winbindd_response getpwent_response;
+
+static int ndx_pw_cache; /* Current index into pwd cache */
+static int num_pw_cache; /* Current size of pwd cache */
+
+/* Rewind "file pointer" to start of ntdom password database */
+
+NSS_STATUS
+_nss_winbind_setpwent(void)
+{
+#ifdef DEBUG_NSS
+ fprintf(stderr, "[%5d]: setpwent\n", getpid());
+#endif
+
+ if (num_pw_cache > 0) {
+ ndx_pw_cache = num_pw_cache = 0;
+ free_response(&getpwent_response);
+ }
+
+ return winbindd_request(WINBINDD_SETPWENT, NULL, NULL);
+}
+
+/* Close ntdom password database "file pointer" */
+
+NSS_STATUS
+_nss_winbind_endpwent(void)
+{
+#ifdef DEBUG_NSS
+ fprintf(stderr, "[%5d]: endpwent\n", getpid());
+#endif
+
+ if (num_pw_cache > 0) {
+ ndx_pw_cache = num_pw_cache = 0;
+ free_response(&getpwent_response);
+ }
+
+ return winbindd_request(WINBINDD_ENDPWENT, NULL, NULL);
+}
+
+/* Fetch the next password entry from ntdom password database */
+
+NSS_STATUS
+_nss_winbind_getpwent_r(struct passwd *result, char *buffer,
+ size_t buflen, int *errnop)
+{
+ NSS_STATUS ret;
+ struct winbindd_request request;
+ static int called_again;
+
+#ifdef DEBUG_NSS
+ fprintf(stderr, "[%5d]: getpwent\n", getpid());
+#endif
+
+ /* Return an entry from the cache if we have one, or if we are
+ called again because we exceeded our static buffer. */
+
+ if ((ndx_pw_cache < num_pw_cache) || called_again) {
+ goto return_result;
+ }
+
+ /* Else call winbindd to get a bunch of entries */
+
+ if (num_pw_cache > 0) {
+ free_response(&getpwent_response);
+ }
+
+ ZERO_STRUCT(request);
+ ZERO_STRUCT(getpwent_response);
+
+ request.data.num_entries = MAX_GETPWENT_USERS;
+
+ ret = winbindd_request(WINBINDD_GETPWENT, &request,
+ &getpwent_response);
+
+ if (ret == NSS_STATUS_SUCCESS) {
+ struct winbindd_pw *pw_cache;
+
+ /* Fill cache */
+
+ ndx_pw_cache = 0;
+ num_pw_cache = getpwent_response.data.num_entries;
+
+ /* Return a result */
+
+ return_result:
+
+ pw_cache = getpwent_response.extra_data;
+
+ /* Check data is valid */
+
+ if (pw_cache == NULL) {
+ return NSS_STATUS_NOTFOUND;
+ }
+
+ ret = fill_pwent(result, &pw_cache[ndx_pw_cache],
+ &buffer, &buflen);
+
+ /* Out of memory - try again */
+
+ if (ret == NSS_STATUS_TRYAGAIN) {
+ called_again = True;
+ *errnop = errno = ERANGE;
+ return ret;
+ }
+
+ *errnop = errno = 0;
+ called_again = False;
+ ndx_pw_cache++;
+
+ /* If we've finished with this lot of results free cache */
+
+ if (ndx_pw_cache == num_pw_cache) {
+ ndx_pw_cache = num_pw_cache = 0;
+ free_response(&getpwent_response);
+ }
+ }
+
+ return ret;
+}
+
+/* Return passwd struct from uid */
+
+NSS_STATUS
+_nss_winbind_getpwuid_r(uid_t uid, struct passwd *result, char *buffer,
+ size_t buflen, int *errnop)
+{
+ NSS_STATUS ret;
+ static struct winbindd_response response;
+ struct winbindd_request request;
+ static int keep_response=0;
+
+ /* If our static buffer needs to be expanded we are called again */
+ if (!keep_response) {
+
+ /* Call for the first time */
+
+ ZERO_STRUCT(response);
+ ZERO_STRUCT(request);
+
+ request.data.uid = uid;
+
+ ret = winbindd_request(WINBINDD_GETPWUID, &request, &response);
+
+ if (ret == NSS_STATUS_SUCCESS) {
+ ret = fill_pwent(result, &response.data.pw,
+ &buffer, &buflen);
+
+ if (ret == NSS_STATUS_TRYAGAIN) {
+ keep_response = True;
+ *errnop = errno = ERANGE;
+ return ret;
+ }
+ }
+
+ } else {
+
+ /* We've been called again */
+
+ ret = fill_pwent(result, &response.data.pw, &buffer, &buflen);
+
+ if (ret == NSS_STATUS_TRYAGAIN) {
+ keep_response = True;
+ *errnop = errno = ERANGE;
+ return ret;
+ }
+
+ keep_response = False;
+ *errnop = errno = 0;
+ }
+
+ free_response(&response);
+ return ret;
+}
+
+/* Return passwd struct from username */
+
+NSS_STATUS
+_nss_winbind_getpwnam_r(const char *name, struct passwd *result, char *buffer,
+ size_t buflen, int *errnop)
+{
+ NSS_STATUS ret;
+ static struct winbindd_response response;
+ struct winbindd_request request;
+ static int keep_response;
+
+#ifdef DEBUG_NSS
+ fprintf(stderr, "[%5d]: getpwnam %s\n", getpid(), name);
+#endif
+
+ /* If our static buffer needs to be expanded we are called again */
+
+ if (!keep_response) {
+
+ /* Call for the first time */
+
+ ZERO_STRUCT(response);
+ ZERO_STRUCT(request);
+
+ strncpy(request.data.username, name,
+ sizeof(request.data.username) - 1);
+ request.data.username
+ [sizeof(request.data.username) - 1] = '\0';
+
+ ret = winbindd_request(WINBINDD_GETPWNAM, &request, &response);
+
+ if (ret == NSS_STATUS_SUCCESS) {
+ ret = fill_pwent(result, &response.data.pw, &buffer,
+ &buflen);
+
+ if (ret == NSS_STATUS_TRYAGAIN) {
+ keep_response = True;
+ *errnop = errno = ERANGE;
+ return ret;
+ }
+ }
+
+ } else {
+
+ /* We've been called again */
+
+ ret = fill_pwent(result, &response.data.pw, &buffer, &buflen);
+
+ if (ret == NSS_STATUS_TRYAGAIN) {
+ keep_response = True;
+ *errnop = errno = ERANGE;
+ return ret;
+ }
+
+ keep_response = False;
+ *errnop = errno = 0;
+ }
+
+ free_response(&response);
+ return ret;
+}
+
+/*
+ * NSS group functions
+ */
+
+static struct winbindd_response getgrent_response;
+
+static int ndx_gr_cache; /* Current index into grp cache */
+static int num_gr_cache; /* Current size of grp cache */
+
+/* Rewind "file pointer" to start of ntdom group database */
+
+NSS_STATUS
+_nss_winbind_setgrent(void)
+{
+#ifdef DEBUG_NSS
+ fprintf(stderr, "[%5d]: setgrent\n", getpid());
+#endif
+
+ if (num_gr_cache > 0) {
+ ndx_gr_cache = num_gr_cache = 0;
+ free_response(&getgrent_response);
+ }
+
+ return winbindd_request(WINBINDD_SETGRENT, NULL, NULL);
+}
+
+/* Close "file pointer" for ntdom group database */
+
+NSS_STATUS
+_nss_winbind_endgrent(void)
+{
+#ifdef DEBUG_NSS
+ fprintf(stderr, "[%5d]: endgrent\n", getpid());
+#endif
+
+ if (num_gr_cache > 0) {
+ ndx_gr_cache = num_gr_cache = 0;
+ free_response(&getgrent_response);
+ }
+
+ return winbindd_request(WINBINDD_ENDGRENT, NULL, NULL);
+}
+
+/* Get next entry from ntdom group database */
+
+static NSS_STATUS
+winbind_getgrent(enum winbindd_cmd cmd,
+ struct group *result,
+ char *buffer, size_t buflen, int *errnop)
+{
+ NSS_STATUS ret;
+ static struct winbindd_request request;
+ static int called_again;
+
+
+#ifdef DEBUG_NSS
+ fprintf(stderr, "[%5d]: getgrent\n", getpid());
+#endif
+
+ /* Return an entry from the cache if we have one, or if we are
+ called again because we exceeded our static buffer. */
+
+ if ((ndx_gr_cache < num_gr_cache) || called_again) {
+ goto return_result;
+ }
+
+ /* Else call winbindd to get a bunch of entries */
+
+ if (num_gr_cache > 0) {
+ free_response(&getgrent_response);
+ }
+
+ ZERO_STRUCT(request);
+ ZERO_STRUCT(getgrent_response);
+
+ request.data.num_entries = MAX_GETGRENT_USERS;
+
+ ret = winbindd_request(cmd, &request,
+ &getgrent_response);
+
+ if (ret == NSS_STATUS_SUCCESS) {
+ struct winbindd_gr *gr_cache;
+ int mem_ofs;
+
+ /* Fill cache */
+
+ ndx_gr_cache = 0;
+ num_gr_cache = getgrent_response.data.num_entries;
+
+ /* Return a result */
+
+ return_result:
+
+ gr_cache = getgrent_response.extra_data;
+
+ /* Check data is valid */
+
+ if (gr_cache == NULL) {
+ return NSS_STATUS_NOTFOUND;
+ }
+
+ /* Fill group membership. The offset into the extra data
+ for the group membership is the reported offset plus the
+ size of all the winbindd_gr records returned. */
+
+ mem_ofs = gr_cache[ndx_gr_cache].gr_mem_ofs +
+ num_gr_cache * sizeof(struct winbindd_gr);
+
+ ret = fill_grent(result, &gr_cache[ndx_gr_cache],
+ ((char *)getgrent_response.extra_data)+mem_ofs,
+ &buffer, &buflen);
+
+ /* Out of memory - try again */
+
+ if (ret == NSS_STATUS_TRYAGAIN) {
+ called_again = True;
+ *errnop = errno = ERANGE;
+ return ret;
+ }
+
+ *errnop = 0;
+ called_again = False;
+ ndx_gr_cache++;
+
+ /* If we've finished with this lot of results free cache */
+
+ if (ndx_gr_cache == num_gr_cache) {
+ ndx_gr_cache = num_gr_cache = 0;
+ free_response(&getgrent_response);
+ }
+ }
+
+ return ret;
+}
+
+
+NSS_STATUS
+_nss_winbind_getgrent_r(struct group *result,
+ char *buffer, size_t buflen, int *errnop)
+{
+ return winbind_getgrent(WINBINDD_GETGRENT, result, buffer, buflen, errnop);
+}
+
+NSS_STATUS
+_nss_winbind_getgrlst_r(struct group *result,
+ char *buffer, size_t buflen, int *errnop)
+{
+ return winbind_getgrent(WINBINDD_GETGRLST, result, buffer, buflen, errnop);
+}
+
+/* Return group struct from group name */
+
+NSS_STATUS
+_nss_winbind_getgrnam_r(const char *name,
+ struct group *result, char *buffer,
+ size_t buflen, int *errnop)
+{
+ NSS_STATUS ret;
+ static struct winbindd_response response;
+ struct winbindd_request request;
+ static int keep_response;
+
+#ifdef DEBUG_NSS
+ fprintf(stderr, "[%5d]: getgrnam %s\n", getpid(), name);
+#endif
+
+ /* If our static buffer needs to be expanded we are called again */
+
+ if (!keep_response) {
+
+ /* Call for the first time */
+
+ ZERO_STRUCT(request);
+ ZERO_STRUCT(response);
+
+ strncpy(request.data.groupname, name,
+ sizeof(request.data.groupname));
+ request.data.groupname
+ [sizeof(request.data.groupname) - 1] = '\0';
+
+ ret = winbindd_request(WINBINDD_GETGRNAM, &request, &response);
+
+ if (ret == NSS_STATUS_SUCCESS) {
+ ret = fill_grent(result, &response.data.gr,
+ response.extra_data,
+ &buffer, &buflen);
+
+ if (ret == NSS_STATUS_TRYAGAIN) {
+ keep_response = True;
+ *errnop = errno = ERANGE;
+ return ret;
+ }
+ }
+
+ } else {
+
+ /* We've been called again */
+
+ ret = fill_grent(result, &response.data.gr,
+ response.extra_data, &buffer, &buflen);
+
+ if (ret == NSS_STATUS_TRYAGAIN) {
+ keep_response = True;
+ *errnop = errno = ERANGE;
+ return ret;
+ }
+
+ keep_response = False;
+ *errnop = 0;
+ }
+
+ free_response(&response);
+ return ret;
+}
+
+/* Return group struct from gid */
+
+NSS_STATUS
+_nss_winbind_getgrgid_r(gid_t gid,
+ struct group *result, char *buffer,
+ size_t buflen, int *errnop)
+{
+ NSS_STATUS ret;
+ static struct winbindd_response response;
+ struct winbindd_request request;
+ static int keep_response;
+
+#ifdef DEBUG_NSS
+ fprintf(stderr, "[%5d]: getgrgid %d\n", getpid(), gid);
+#endif
+
+ /* If our static buffer needs to be expanded we are called again */
+
+ if (!keep_response) {
+
+ /* Call for the first time */
+
+ ZERO_STRUCT(request);
+ ZERO_STRUCT(response);
+
+ request.data.gid = gid;
+
+ ret = winbindd_request(WINBINDD_GETGRGID, &request, &response);
+
+ if (ret == NSS_STATUS_SUCCESS) {
+
+ ret = fill_grent(result, &response.data.gr,
+ response.extra_data,
+ &buffer, &buflen);
+
+ if (ret == NSS_STATUS_TRYAGAIN) {
+ keep_response = True;
+ *errnop = errno = ERANGE;
+ return ret;
+ }
+ }
+
+ } else {
+
+ /* We've been called again */
+
+ ret = fill_grent(result, &response.data.gr,
+ response.extra_data, &buffer, &buflen);
+
+ if (ret == NSS_STATUS_TRYAGAIN) {
+ keep_response = True;
+ *errnop = errno = ERANGE;
+ return ret;
+ }
+
+ keep_response = False;
+ *errnop = 0;
+ }
+
+ free_response(&response);
+ return ret;
+}
+
+/* Initialise supplementary groups */
+
+NSS_STATUS
+_nss_winbind_initgroups_dyn(char *user, gid_t group, long int *start,
+ long int *size, gid_t **groups, long int limit,
+ int *errnop)
+{
+ NSS_STATUS ret;
+ struct winbindd_request request;
+ struct winbindd_response response;
+ int i;
+
+#ifdef DEBUG_NSS
+ fprintf(stderr, "[%5d]: initgroups %s (%d)\n", getpid(),
+ user, group);
+#endif
+
+ ZERO_STRUCT(request);
+ ZERO_STRUCT(response);
+
+ strncpy(request.data.username, user,
+ sizeof(request.data.username) - 1);
+
+ ret = winbindd_request(WINBINDD_GETGROUPS, &request, &response);
+
+ if (ret == NSS_STATUS_SUCCESS) {
+ int num_gids = response.data.num_entries;
+ gid_t *gid_list = (gid_t *)response.extra_data;
+
+ /* Copy group list to client */
+
+ for (i = 0; i < num_gids; i++) {
+
+ /* Skip primary group */
+
+ if (gid_list[i] == group) continue;
+
+ /* Add to buffer */
+
+ if (*start == *size && limit <= 0) {
+ (*groups) = realloc(
+ (*groups), (2 * (*size) + 1) * sizeof(**groups));
+ if (! *groups) goto done;
+ *size = 2 * (*size) + 1;
+ }
+
+ if (*start == *size) goto done;
+
+ (*groups)[*start] = gid_list[i];
+ *start += 1;
+
+ /* Filled buffer? */
+
+ if (*start == limit) goto done;
+ }
+ }
+
+ /* Back to your regularly scheduled programming */
+
+ done:
+ return ret;
+}
+
+
+/* return a list of group SIDs for a user SID */
+NSS_STATUS
+_nss_winbind_getusersids(const char *user_sid, char **group_sids,
+ int *num_groups,
+ char *buffer, size_t buf_size, int *errnop)
+{
+ NSS_STATUS ret;
+ struct winbindd_request request;
+ struct winbindd_response response;
+
+#ifdef DEBUG_NSS
+ fprintf(stderr, "[%5d]: getusersids %s\n", getpid(), user_sid);
+#endif
+
+ ZERO_STRUCT(request);
+ ZERO_STRUCT(response);
+
+ strncpy(request.data.sid, user_sid,sizeof(request.data.sid) - 1);
+ request.data.sid[sizeof(request.data.sid) - 1] = '\0';
+
+ ret = winbindd_request(WINBINDD_GETUSERSIDS, &request, &response);
+
+ if (ret != NSS_STATUS_SUCCESS) {
+ goto done;
+ }
+
+ if (buf_size < response.length - sizeof(response)) {
+ ret = NSS_STATUS_TRYAGAIN;
+ errno = *errnop = ERANGE;
+ goto done;
+ }
+
+ *num_groups = response.data.num_entries;
+ *group_sids = buffer;
+ memcpy(buffer, response.extra_data, response.length - sizeof(response));
+ errno = *errnop = 0;
+
+ done:
+ free_response(&response);
+ return ret;
+}
+
+
+/* map a user or group name to a SID string */
+NSS_STATUS
+_nss_winbind_nametosid(const char *name, char **sid, char *buffer,
+ size_t buflen, int *errnop)
+{
+ NSS_STATUS ret;
+ struct winbindd_response response;
+ struct winbindd_request request;
+
+#ifdef DEBUG_NSS
+ fprintf(stderr, "[%5d]: nametosid %s\n", getpid(), name);
+#endif
+
+ ZERO_STRUCT(response);
+ ZERO_STRUCT(request);
+
+ strncpy(request.data.name.name, name,
+ sizeof(request.data.name.name) - 1);
+ request.data.name.name[sizeof(request.data.name.name) - 1] = '\0';
+
+ ret = winbindd_request(WINBINDD_LOOKUPNAME, &request, &response);
+ if (ret != NSS_STATUS_SUCCESS) {
+ *errnop = errno = EINVAL;
+ goto failed;
+ }
+
+ if (buflen < strlen(response.data.sid.sid)+1) {
+ ret = NSS_STATUS_TRYAGAIN;
+ *errnop = errno = ERANGE;
+ goto failed;
+ }
+
+ *errnop = errno = 0;
+ *sid = buffer;
+ strcpy(*sid, response.data.sid.sid);
+
+failed:
+ free_response(&response);
+ return ret;
+}
+
+/* map a sid string to a user or group name */
+NSS_STATUS
+_nss_winbind_sidtoname(const char *sid, char **name, char *buffer,
+ size_t buflen, int *errnop)
+{
+ NSS_STATUS ret;
+ struct winbindd_response response;
+ struct winbindd_request request;
+ static char sep_char;
+ unsigned needed;
+
+#ifdef DEBUG_NSS
+ fprintf(stderr, "[%5d]: sidtoname %s\n", getpid(), sid);
+#endif
+
+ /* we need to fetch the separator first time through */
+ if (!sep_char) {
+ ZERO_STRUCT(response);
+ ZERO_STRUCT(request);
+
+ ret = winbindd_request(WINBINDD_INFO, &request, &response);
+ if (ret != NSS_STATUS_SUCCESS) {
+ *errnop = errno = EINVAL;
+ goto failed;
+ }
+
+ sep_char = response.data.info.winbind_separator;
+ free_response(&response);
+ }
+
+
+ strncpy(request.data.sid, sid,
+ sizeof(request.data.sid) - 1);
+ request.data.sid[sizeof(request.data.sid) - 1] = '\0';
+
+ ret = winbindd_request(WINBINDD_LOOKUPSID, &request, &response);
+ if (ret != NSS_STATUS_SUCCESS) {
+ *errnop = errno = EINVAL;
+ goto failed;
+ }
+
+ needed =
+ strlen(response.data.name.dom_name) +
+ strlen(response.data.name.name) + 2;
+
+ if (buflen < needed) {
+ ret = NSS_STATUS_TRYAGAIN;
+ *errnop = errno = ERANGE;
+ goto failed;
+ }
+
+ snprintf(buffer, needed, "%s%c%s",
+ response.data.name.dom_name,
+ sep_char,
+ response.data.name.name);
+
+ *name = buffer;
+ *errnop = errno = 0;
+
+failed:
+ free_response(&response);
+ return ret;
+}
+
+/* map a sid to a uid */
+NSS_STATUS
+_nss_winbind_sidtouid(const char *sid, uid_t *uid, int *errnop)
+{
+ NSS_STATUS ret;
+ struct winbindd_response response;
+ struct winbindd_request request;
+
+#ifdef DEBUG_NSS
+ fprintf(stderr, "[%5d]: sidtouid %s\n", getpid(), sid);
+#endif
+
+ ZERO_STRUCT(request);
+ ZERO_STRUCT(response);
+
+ strncpy(request.data.sid, sid, sizeof(request.data.sid) - 1);
+ request.data.sid[sizeof(request.data.sid) - 1] = '\0';
+
+ ret = winbindd_request(WINBINDD_SID_TO_UID, &request, &response);
+ if (ret != NSS_STATUS_SUCCESS) {
+ *errnop = errno = EINVAL;
+ goto failed;
+ }
+
+ *uid = response.data.uid;
+
+failed:
+ return ret;
+}
+
+/* map a sid to a gid */
+NSS_STATUS
+_nss_winbind_sidtogid(const char *sid, gid_t *gid, int *errnop)
+{
+ NSS_STATUS ret;
+ struct winbindd_response response;
+ struct winbindd_request request;
+
+#ifdef DEBUG_NSS
+ fprintf(stderr, "[%5d]: sidtogid %s\n", getpid(), sid);
+#endif
+
+ ZERO_STRUCT(request);
+ ZERO_STRUCT(response);
+
+ strncpy(request.data.sid, sid, sizeof(request.data.sid) - 1);
+ request.data.sid[sizeof(request.data.sid) - 1] = '\0';
+
+ ret = winbindd_request(WINBINDD_SID_TO_GID, &request, &response);
+ if (ret != NSS_STATUS_SUCCESS) {
+ *errnop = errno = EINVAL;
+ goto failed;
+ }
+
+ *gid = response.data.gid;
+
+failed:
+ return ret;
+}
+
+/* map a uid to a SID string */
+NSS_STATUS
+_nss_winbind_uidtosid(uid_t uid, char **sid, char *buffer,
+ size_t buflen, int *errnop)
+{
+ NSS_STATUS ret;
+ struct winbindd_response response;
+ struct winbindd_request request;
+
+#ifdef DEBUG_NSS
+ fprintf(stderr, "[%5u]: uidtosid %u\n", (unsigned int)getpid(), (unsigned int)uid);
+#endif
+
+ ZERO_STRUCT(response);
+ ZERO_STRUCT(request);
+
+ request.data.uid = uid;
+
+ ret = winbindd_request(WINBINDD_UID_TO_SID, &request, &response);
+ if (ret != NSS_STATUS_SUCCESS) {
+ *errnop = errno = EINVAL;
+ goto failed;
+ }
+
+ if (buflen < strlen(response.data.sid.sid)+1) {
+ ret = NSS_STATUS_TRYAGAIN;
+ *errnop = errno = ERANGE;
+ goto failed;
+ }
+
+ *errnop = errno = 0;
+ *sid = buffer;
+ strcpy(*sid, response.data.sid.sid);
+
+failed:
+ free_response(&response);
+ return ret;
+}
+
+/* map a gid to a SID string */
+NSS_STATUS
+_nss_winbind_gidtosid(gid_t gid, char **sid, char *buffer,
+ size_t buflen, int *errnop)
+{
+ NSS_STATUS ret;
+ struct winbindd_response response;
+ struct winbindd_request request;
+
+#ifdef DEBUG_NSS
+ fprintf(stderr, "[%5u]: gidtosid %u\n", (unsigned int)getpid(), (unsigned int)gid);
+#endif
+
+ ZERO_STRUCT(response);
+ ZERO_STRUCT(request);
+
+ request.data.gid = gid;
+
+ ret = winbindd_request(WINBINDD_GID_TO_SID, &request, &response);
+ if (ret != NSS_STATUS_SUCCESS) {
+ *errnop = errno = EINVAL;
+ goto failed;
+ }
+
+ if (buflen < strlen(response.data.sid.sid)+1) {
+ ret = NSS_STATUS_TRYAGAIN;
+ *errnop = errno = ERANGE;
+ goto failed;
+ }
+
+ *errnop = errno = 0;
+ *sid = buffer;
+ strcpy(*sid, response.data.sid.sid);
+
+failed:
+ free_response(&response);
+ return ret;
+}
diff --git a/source/nsswitch/winbind_nss_linux.h b/source/nsswitch/winbind_nss_linux.h
new file mode 100644
index 00000000000..1c7e8300373
--- /dev/null
+++ b/source/nsswitch/winbind_nss_linux.h
@@ -0,0 +1,35 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ Winbind daemon for ntdom nss module
+
+ Copyright (C) Tim Potter 2000
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 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 Library General Public
+ License along with this library; if not, write to the
+ Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+*/
+
+#ifndef _WINBIND_NSS_LINUX_H
+#define _WINBIND_NSS_LINUX_H
+
+#if HAVE_NSS_H
+
+#include <nss.h>
+
+typedef enum nss_status NSS_STATUS;
+
+#endif /* HAVE_NSS_H */
+
+#endif /* _WINBIND_NSS_LINUX_H */
diff --git a/source/nsswitch/winbind_nss_solaris.c b/source/nsswitch/winbind_nss_solaris.c
new file mode 100644
index 00000000000..8f03eb4cd6e
--- /dev/null
+++ b/source/nsswitch/winbind_nss_solaris.c
@@ -0,0 +1,315 @@
+/*
+ Solaris NSS wrapper for winbind
+ - Shirish Kalele 2000
+
+ Based on Luke Howard's ldap_nss module for Solaris
+ */
+
+/*
+ Copyright (C) 1997-2003 Luke Howard.
+ This file is part of the nss_ldap library.
+
+ The nss_ldap library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ The nss_ldap 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 Library General Public
+ License along with the nss_ldap library; see the file COPYING.LIB. If not,
+ write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+*/
+
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/param.h>
+#include <string.h>
+#include <pwd.h>
+#include "includes.h"
+#include <syslog.h>
+#if !defined(HPUX)
+#include <sys/syslog.h>
+#endif /*hpux*/
+#include "winbind_nss_config.h"
+
+#if defined(HAVE_NSS_COMMON_H) || defined(HPUX)
+
+#undef NSS_DEBUG
+
+#ifdef NSS_DEBUG
+#define NSS_DEBUG(str) syslog(LOG_DEBUG, "nss_winbind: %s", str);
+#else
+#define NSS_DEBUG(str) ;
+#endif
+
+#define NSS_ARGS(args) ((nss_XbyY_args_t *)args)
+
+#define make_pwent_str(dest, src) \
+{ \
+ if((dest = get_static(buffer, buflen, strlen(src)+1)) == NULL) \
+ { \
+ *errnop = ERANGE; \
+ NSS_DEBUG("ERANGE error"); \
+ return NSS_STATUS_TRYAGAIN; \
+ } \
+ strcpy(dest, src); \
+}
+
+static NSS_STATUS _nss_winbind_setpwent_solwrap (nss_backend_t* be, void* args)
+{
+ NSS_DEBUG("_nss_winbind_setpwent_solwrap");
+ return _nss_winbind_setpwent();
+}
+
+static NSS_STATUS
+_nss_winbind_endpwent_solwrap (nss_backend_t * be, void *args)
+{
+ NSS_DEBUG("_nss_winbind_endpwent_solwrap");
+ return _nss_winbind_endpwent();
+}
+
+static NSS_STATUS
+_nss_winbind_getpwent_solwrap (nss_backend_t* be, void *args)
+{
+ NSS_STATUS ret;
+ char* buffer = NSS_ARGS(args)->buf.buffer;
+ int buflen = NSS_ARGS(args)->buf.buflen;
+ struct passwd* result = (struct passwd*) NSS_ARGS(args)->buf.result;
+ int* errnop = &NSS_ARGS(args)->erange;
+ char logmsg[80];
+
+ ret = _nss_winbind_getpwent_r(result, buffer,
+ buflen, errnop);
+
+ if(ret == NSS_STATUS_SUCCESS)
+ {
+ snprintf(logmsg, 79, "_nss_winbind_getpwent_solwrap: Returning user: %s\n",
+ result->pw_name);
+ NSS_DEBUG(logmsg);
+ NSS_ARGS(args)->returnval = (void*) result;
+ } else {
+ snprintf(logmsg, 79, "_nss_winbind_getpwent_solwrap: Returning error: %d.\n",ret);
+ NSS_DEBUG(logmsg);
+ }
+
+ return ret;
+}
+
+static NSS_STATUS
+_nss_winbind_getpwnam_solwrap (nss_backend_t* be, void* args)
+{
+ NSS_STATUS ret;
+ struct passwd* result = (struct passwd*) NSS_ARGS(args)->buf.result;
+
+ NSS_DEBUG("_nss_winbind_getpwnam_solwrap");
+
+ ret = _nss_winbind_getpwnam_r (NSS_ARGS(args)->key.name,
+ result,
+ NSS_ARGS(args)->buf.buffer,
+ NSS_ARGS(args)->buf.buflen,
+ &NSS_ARGS(args)->erange);
+ if(ret == NSS_STATUS_SUCCESS)
+ NSS_ARGS(args)->returnval = (void*) result;
+
+ return ret;
+}
+
+static NSS_STATUS
+_nss_winbind_getpwuid_solwrap(nss_backend_t* be, void* args)
+{
+ NSS_STATUS ret;
+ struct passwd* result = (struct passwd*) NSS_ARGS(args)->buf.result;
+
+ NSS_DEBUG("_nss_winbind_getpwuid_solwrap");
+ ret = _nss_winbind_getpwuid_r (NSS_ARGS(args)->key.uid,
+ result,
+ NSS_ARGS(args)->buf.buffer,
+ NSS_ARGS(args)->buf.buflen,
+ &NSS_ARGS(args)->erange);
+ if(ret == NSS_STATUS_SUCCESS)
+ NSS_ARGS(args)->returnval = (void*) result;
+
+ return ret;
+}
+
+static NSS_STATUS _nss_winbind_passwd_destr (nss_backend_t * be, void *args)
+{
+ SAFE_FREE(be);
+ NSS_DEBUG("_nss_winbind_passwd_destr");
+ return NSS_STATUS_SUCCESS;
+}
+
+static nss_backend_op_t passwd_ops[] =
+{
+ _nss_winbind_passwd_destr,
+ _nss_winbind_endpwent_solwrap, /* NSS_DBOP_ENDENT */
+ _nss_winbind_setpwent_solwrap, /* NSS_DBOP_SETENT */
+ _nss_winbind_getpwent_solwrap, /* NSS_DBOP_GETENT */
+ _nss_winbind_getpwnam_solwrap, /* NSS_DBOP_PASSWD_BYNAME */
+ _nss_winbind_getpwuid_solwrap /* NSS_DBOP_PASSWD_BYUID */
+};
+
+nss_backend_t*
+_nss_winbind_passwd_constr (const char* db_name,
+ const char* src_name,
+ const char* cfg_args)
+{
+ nss_backend_t *be;
+
+ if(!(be = (nss_backend_t*) malloc(sizeof(nss_backend_t))) )
+ return NULL;
+
+ be->ops = passwd_ops;
+ be->n_ops = sizeof(passwd_ops) / sizeof(nss_backend_op_t);
+
+ NSS_DEBUG("Initialized nss_winbind passwd backend");
+ return be;
+}
+
+/*****************************************************************
+ GROUP database backend
+ *****************************************************************/
+
+static NSS_STATUS _nss_winbind_setgrent_solwrap (nss_backend_t* be, void* args)
+{
+ NSS_DEBUG("_nss_winbind_setgrent_solwrap");
+ return _nss_winbind_setgrent();
+}
+
+static NSS_STATUS
+_nss_winbind_endgrent_solwrap (nss_backend_t * be, void *args)
+{
+ NSS_DEBUG("_nss_winbind_endgrent_solwrap");
+ return _nss_winbind_endgrent();
+}
+
+static NSS_STATUS
+_nss_winbind_getgrent_solwrap(nss_backend_t* be, void* args)
+{
+ NSS_STATUS ret;
+ char* buffer = NSS_ARGS(args)->buf.buffer;
+ int buflen = NSS_ARGS(args)->buf.buflen;
+ struct group* result = (struct group*) NSS_ARGS(args)->buf.result;
+ int* errnop = &NSS_ARGS(args)->erange;
+ char logmsg[80];
+
+ ret = _nss_winbind_getgrent_r(result, buffer,
+ buflen, errnop);
+
+ if(ret == NSS_STATUS_SUCCESS)
+ {
+ snprintf(logmsg, 79, "_nss_winbind_getgrent_solwrap: Returning group: %s\n", result->gr_name);
+ NSS_DEBUG(logmsg);
+ NSS_ARGS(args)->returnval = (void*) result;
+ } else {
+ snprintf(logmsg, 79, "_nss_winbind_getgrent_solwrap: Returning error: %d.\n", ret);
+ NSS_DEBUG(logmsg);
+ }
+
+ return ret;
+
+}
+
+static NSS_STATUS
+_nss_winbind_getgrnam_solwrap(nss_backend_t* be, void* args)
+{
+ NSS_STATUS ret;
+ struct group* result = (struct group*) NSS_ARGS(args)->buf.result;
+
+ NSS_DEBUG("_nss_winbind_getgrnam_solwrap");
+ ret = _nss_winbind_getgrnam_r(NSS_ARGS(args)->key.name,
+ result,
+ NSS_ARGS(args)->buf.buffer,
+ NSS_ARGS(args)->buf.buflen,
+ &NSS_ARGS(args)->erange);
+
+ if(ret == NSS_STATUS_SUCCESS)
+ NSS_ARGS(args)->returnval = (void*) result;
+
+ return ret;
+}
+
+static NSS_STATUS
+_nss_winbind_getgrgid_solwrap(nss_backend_t* be, void* args)
+{
+ NSS_STATUS ret;
+ struct group* result = (struct group*) NSS_ARGS(args)->buf.result;
+
+ NSS_DEBUG("_nss_winbind_getgrgid_solwrap");
+ ret = _nss_winbind_getgrgid_r (NSS_ARGS(args)->key.gid,
+ result,
+ NSS_ARGS(args)->buf.buffer,
+ NSS_ARGS(args)->buf.buflen,
+ &NSS_ARGS(args)->erange);
+
+ if(ret == NSS_STATUS_SUCCESS)
+ NSS_ARGS(args)->returnval = (void*) result;
+
+ return ret;
+}
+
+static NSS_STATUS
+_nss_winbind_getgroupsbymember_solwrap(nss_backend_t* be, void* args)
+{
+ int errnop;
+ struct nss_groupsbymem *gmem = (struct nss_groupsbymem *)args;
+
+ NSS_DEBUG("_nss_winbind_getgroupsbymember");
+
+ _nss_winbind_initgroups_dyn(gmem->username,
+ gmem->gid_array[0], /* Primary Group */
+ &gmem->numgids,
+ &gmem->maxgids,
+ &gmem->gid_array,
+ gmem->maxgids,
+ &errnop);
+
+ /*
+ * Always return NOTFOUND so nsswitch will get info from all
+ * the database backends specified in the nsswitch.conf file.
+ */
+ return NSS_STATUS_NOTFOUND;
+}
+
+static NSS_STATUS
+_nss_winbind_group_destr (nss_backend_t* be, void* args)
+{
+ SAFE_FREE(be);
+ NSS_DEBUG("_nss_winbind_group_destr");
+ return NSS_STATUS_SUCCESS;
+}
+
+static nss_backend_op_t group_ops[] =
+{
+ _nss_winbind_group_destr,
+ _nss_winbind_endgrent_solwrap,
+ _nss_winbind_setgrent_solwrap,
+ _nss_winbind_getgrent_solwrap,
+ _nss_winbind_getgrnam_solwrap,
+ _nss_winbind_getgrgid_solwrap,
+ _nss_winbind_getgroupsbymember_solwrap
+};
+
+nss_backend_t*
+_nss_winbind_group_constr (const char* db_name,
+ const char* src_name,
+ const char* cfg_args)
+{
+ nss_backend_t* be;
+
+ if(!(be = (nss_backend_t*) malloc(sizeof(nss_backend_t))) )
+ return NULL;
+
+ be->ops = group_ops;
+ be->n_ops = sizeof(group_ops) / sizeof(nss_backend_op_t);
+
+ NSS_DEBUG("Initialized nss_winbind group backend");
+ return be;
+}
+
+#endif /* SUN_NSS */
diff --git a/source/nsswitch/winbind_nss_solaris.h b/source/nsswitch/winbind_nss_solaris.h
new file mode 100644
index 00000000000..567de411aa6
--- /dev/null
+++ b/source/nsswitch/winbind_nss_solaris.h
@@ -0,0 +1,61 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ Winbind daemon for ntdom nss module
+
+ Copyright (C) Tim Potter 2000
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 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 Library General Public
+ License along with this library; if not, write to the
+ Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+*/
+
+#ifndef _WINBIND_NSS_SOLARIS_H
+#define _WINBIND_NSS_SOLARIS_H
+
+#include <nss_common.h>
+#include <nss_dbdefs.h>
+#include <nsswitch.h>
+
+typedef nss_status_t NSS_STATUS;
+
+#define NSS_STATUS_SUCCESS NSS_SUCCESS
+#define NSS_STATUS_NOTFOUND NSS_NOTFOUND
+#define NSS_STATUS_UNAVAIL NSS_UNAVAIL
+#define NSS_STATUS_TRYAGAIN NSS_TRYAGAIN
+
+/* The solaris winbind is implemented as a wrapper around the linux
+ version. */
+
+NSS_STATUS _nss_winbind_setpwent(void);
+NSS_STATUS _nss_winbind_endpwent(void);
+NSS_STATUS _nss_winbind_getpwent_r(struct passwd* result, char* buffer,
+ size_t buflen, int* errnop);
+NSS_STATUS _nss_winbind_getpwuid_r(uid_t, struct passwd*, char* buffer,
+ size_t buflen, int* errnop);
+NSS_STATUS _nss_winbind_getpwnam_r(const char* name, struct passwd* result,
+ char* buffer, size_t buflen, int* errnop);
+
+NSS_STATUS _nss_winbind_setgrent(void);
+NSS_STATUS _nss_winbind_endgrent(void);
+NSS_STATUS _nss_winbind_getgrent_r(struct group* result, char* buffer,
+ size_t buflen, int* errnop);
+NSS_STATUS _nss_winbind_getgrnam_r(const char *name,
+ struct group *result, char *buffer,
+ size_t buflen, int *errnop);
+NSS_STATUS _nss_winbind_getgrgid_r(gid_t gid,
+ struct group *result, char *buffer,
+ size_t buflen, int *errnop);
+
+#endif /* _WINBIND_NSS_SOLARIS_H */
diff --git a/source/nsswitch/winbindd.c b/source/nsswitch/winbindd.c
new file mode 100644
index 00000000000..283b2e4a89c
--- /dev/null
+++ b/source/nsswitch/winbindd.c
@@ -0,0 +1,947 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ Winbind daemon for ntdom nss module
+
+ Copyright (C) by Tim Potter 2000-2002
+ Copyright (C) Andrew Tridgell 2002
+ 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 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 "winbindd.h"
+
+BOOL opt_nocache = False;
+BOOL opt_dual_daemon = True;
+
+/* Reload configuration */
+
+static BOOL reload_services_file(void)
+{
+ BOOL ret;
+
+ if (lp_loaded()) {
+ pstring fname;
+
+ pstrcpy(fname,lp_configfile());
+ if (file_exist(fname,NULL) && !strcsequal(fname,dyn_CONFIGFILE)) {
+ pstrcpy(dyn_CONFIGFILE,fname);
+ }
+ }
+
+ reopen_logs();
+ ret = lp_load(dyn_CONFIGFILE,False,False,True);
+
+ reopen_logs();
+ load_interfaces();
+
+ return(ret);
+}
+
+
+#if DUMP_CORE
+
+/**************************************************************************** **
+ Prepare to dump a core file - carefully!
+ **************************************************************************** */
+
+static BOOL dump_core(void)
+{
+ char *p;
+ pstring dname;
+ pstrcpy( dname, lp_logfile() );
+ if ((p=strrchr(dname,'/')))
+ *p=0;
+ pstrcat( dname, "/corefiles" );
+ mkdir( dname, 0700 );
+ sys_chown( dname, getuid(), getgid() );
+ chmod( dname, 0700 );
+ if ( chdir(dname) )
+ return( False );
+ umask( ~(0700) );
+
+#ifdef HAVE_GETRLIMIT
+#ifdef RLIMIT_CORE
+ {
+ struct rlimit rlp;
+ getrlimit( RLIMIT_CORE, &rlp );
+ rlp.rlim_cur = MAX( 4*1024*1024, rlp.rlim_cur );
+ setrlimit( RLIMIT_CORE, &rlp );
+ getrlimit( RLIMIT_CORE, &rlp );
+ DEBUG( 3, ( "Core limits now %d %d\n", (int)rlp.rlim_cur, (int)rlp.rlim_max ) );
+ }
+#endif
+#endif
+
+ DEBUG(0,("Dumping core in %s\n",dname));
+ abort();
+ return( True );
+} /* dump_core */
+#endif
+
+/**************************************************************************** **
+ Handle a fault..
+ **************************************************************************** */
+
+static void fault_quit(void)
+{
+#if DUMP_CORE
+ dump_core();
+#endif
+}
+
+static void winbindd_status(void)
+{
+ struct winbindd_cli_state *tmp;
+
+ DEBUG(0, ("winbindd status:\n"));
+
+ /* Print client state information */
+
+ DEBUG(0, ("\t%d clients currently active\n", winbindd_num_clients()));
+
+ if (DEBUGLEVEL >= 2 && winbindd_num_clients()) {
+ DEBUG(2, ("\tclient list:\n"));
+ for(tmp = winbindd_client_list(); tmp; tmp = tmp->next) {
+ DEBUG(2, ("\t\tpid %lu, sock %d, rbl %d, wbl %d\n",
+ (unsigned long)tmp->pid, tmp->sock, tmp->read_buf_len,
+ tmp->write_buf_len));
+ }
+ }
+}
+
+/* Print winbindd status to log file */
+
+static void print_winbindd_status(void)
+{
+ winbindd_status();
+ winbindd_cm_status();
+}
+
+/* Flush client cache */
+
+static void flush_caches(void)
+{
+#if 0
+ /* Clear cached user and group enumation info */
+ if (!opt_dual_daemon) /* Until we have coherent cache flush. */
+ wcache_flush_cache();
+#endif
+
+ /* We need to invalidate cached user list entries on a SIGHUP
+ otherwise cached access denied errors due to restrict anonymous
+ hang around until the sequence number changes. */
+
+ wcache_invalidate_cache();
+}
+
+/* Handle the signal by unlinking socket and exiting */
+
+static void terminate(void)
+{
+ pstring path;
+
+ idmap_close();
+
+ /* Remove socket file */
+ pstr_sprintf(path, "%s/%s",
+ WINBINDD_SOCKET_DIR, WINBINDD_SOCKET_NAME);
+ unlink(path);
+ exit(0);
+}
+
+static BOOL do_sigterm;
+
+static void termination_handler(int signum)
+{
+ do_sigterm = True;
+ sys_select_signal();
+}
+
+static BOOL do_sigusr2;
+
+static void sigusr2_handler(int signum)
+{
+ do_sigusr2 = True;
+ sys_select_signal();
+}
+
+static BOOL do_sighup;
+
+static void sighup_handler(int signum)
+{
+ do_sighup = True;
+ sys_select_signal();
+}
+
+/* React on 'smbcontrol winbindd reload-config' in the same way as on SIGHUP*/
+static void msg_reload_services(int msg_type, pid_t src, void *buf, size_t len)
+{
+ /* Flush various caches */
+ flush_caches();
+ reload_services_file();
+}
+
+/* React on 'smbcontrol winbindd shutdown' in the same way as on SIGTERM*/
+static void msg_shutdown(int msg_type, pid_t src, void *buf, size_t len)
+{
+ terminate();
+}
+
+struct dispatch_table {
+ enum winbindd_cmd cmd;
+ enum winbindd_result (*fn)(struct winbindd_cli_state *state);
+ const char *winbindd_cmd_name;
+};
+
+static struct dispatch_table dispatch_table[] = {
+
+ /* User functions */
+
+ { WINBINDD_GETPWNAM, winbindd_getpwnam, "GETPWNAM" },
+ { WINBINDD_GETPWUID, winbindd_getpwuid, "GETPWUID" },
+
+ { WINBINDD_SETPWENT, winbindd_setpwent, "SETPWENT" },
+ { WINBINDD_ENDPWENT, winbindd_endpwent, "ENDPWENT" },
+ { WINBINDD_GETPWENT, winbindd_getpwent, "GETPWENT" },
+
+ { WINBINDD_GETGROUPS, winbindd_getgroups, "GETGROUPS" },
+ { WINBINDD_GETUSERSIDS, winbindd_getusersids, "GETUSERSIDS" },
+
+ /* Group functions */
+
+ { WINBINDD_GETGRNAM, winbindd_getgrnam, "GETGRNAM" },
+ { WINBINDD_GETGRGID, winbindd_getgrgid, "GETGRGID" },
+ { WINBINDD_SETGRENT, winbindd_setgrent, "SETGRENT" },
+ { WINBINDD_ENDGRENT, winbindd_endgrent, "ENDGRENT" },
+ { WINBINDD_GETGRENT, winbindd_getgrent, "GETGRENT" },
+ { WINBINDD_GETGRLST, winbindd_getgrent, "GETGRLST" },
+
+ /* PAM auth functions */
+
+ { WINBINDD_PAM_AUTH, winbindd_pam_auth, "PAM_AUTH" },
+ { WINBINDD_PAM_AUTH_CRAP, winbindd_pam_auth_crap, "AUTH_CRAP" },
+ { WINBINDD_PAM_CHAUTHTOK, winbindd_pam_chauthtok, "CHAUTHTOK" },
+
+ /* Enumeration functions */
+
+ { WINBINDD_LIST_USERS, winbindd_list_users, "LIST_USERS" },
+ { WINBINDD_LIST_GROUPS, winbindd_list_groups, "LIST_GROUPS" },
+ { WINBINDD_LIST_TRUSTDOM, winbindd_list_trusted_domains, "LIST_TRUSTDOM" },
+ { WINBINDD_SHOW_SEQUENCE, winbindd_show_sequence, "SHOW_SEQUENCE" },
+
+ /* SID related functions */
+
+ { WINBINDD_LOOKUPSID, winbindd_lookupsid, "LOOKUPSID" },
+ { WINBINDD_LOOKUPNAME, winbindd_lookupname, "LOOKUPNAME" },
+
+ /* Lookup related functions */
+
+ { WINBINDD_SID_TO_UID, winbindd_sid_to_uid, "SID_TO_UID" },
+ { WINBINDD_SID_TO_GID, winbindd_sid_to_gid, "SID_TO_GID" },
+ { WINBINDD_GID_TO_SID, winbindd_gid_to_sid, "GID_TO_SID" },
+ { WINBINDD_UID_TO_SID, winbindd_uid_to_sid, "UID_TO_SID" },
+ { WINBINDD_ALLOCATE_RID, winbindd_allocate_rid, "ALLOCATE_RID" },
+
+ /* Miscellaneous */
+
+ { WINBINDD_CHECK_MACHACC, winbindd_check_machine_acct, "CHECK_MACHACC" },
+ { WINBINDD_PING, winbindd_ping, "PING" },
+ { WINBINDD_INFO, winbindd_info, "INFO" },
+ { WINBINDD_INTERFACE_VERSION, winbindd_interface_version, "INTERFACE_VERSION" },
+ { WINBINDD_DOMAIN_NAME, winbindd_domain_name, "DOMAIN_NAME" },
+ { WINBINDD_DOMAIN_INFO, winbindd_domain_info, "DOMAIN_INFO" },
+ { WINBINDD_NETBIOS_NAME, winbindd_netbios_name, "NETBIOS_NAME" },
+ { WINBINDD_PRIV_PIPE_DIR, winbindd_priv_pipe_dir, "WINBINDD_PRIV_PIPE_DIR" },
+
+ /* WINS functions */
+
+ { WINBINDD_WINS_BYNAME, winbindd_wins_byname, "WINS_BYNAME" },
+ { WINBINDD_WINS_BYIP, winbindd_wins_byip, "WINS_BYIP" },
+
+ /* UNIX account management functions */
+ { WINBINDD_CREATE_USER, winbindd_create_user, "CREATE_USER" },
+ { WINBINDD_CREATE_GROUP, winbindd_create_group, "CREATE_GROUP" },
+ { WINBINDD_ADD_USER_TO_GROUP, winbindd_add_user_to_group, "ADD_USER_TO_GROUP" },
+ { WINBINDD_REMOVE_USER_FROM_GROUP, winbindd_remove_user_from_group,"REMOVE_USER_FROM_GROUP"},
+ { WINBINDD_SET_USER_PRIMARY_GROUP, winbindd_set_user_primary_group,"SET_USER_PRIMARY_GROUP"},
+ { WINBINDD_DELETE_USER, winbindd_delete_user, "DELETE_USER" },
+ { WINBINDD_DELETE_GROUP, winbindd_delete_group, "DELETE_GROUP" },
+
+ /* End of list */
+
+ { WINBINDD_NUM_CMDS, NULL, "NONE" }
+};
+
+static void process_request(struct winbindd_cli_state *state)
+{
+ struct dispatch_table *table = dispatch_table;
+
+ /* Free response data - we may be interrupted and receive another
+ command before being able to send this data off. */
+
+ SAFE_FREE(state->response.extra_data);
+
+ ZERO_STRUCT(state->response);
+
+ state->response.result = WINBINDD_ERROR;
+ state->response.length = sizeof(struct winbindd_response);
+
+ /* Process command */
+
+ for (table = dispatch_table; table->fn; table++) {
+ if (state->request.cmd == table->cmd) {
+ DEBUG(10,("process_request: request fn %s\n", table->winbindd_cmd_name ));
+ state->response.result = table->fn(state);
+ break;
+ }
+ }
+
+ if (!table->fn)
+ DEBUG(10,("process_request: unknown request fn number %d\n", (int)state->request.cmd ));
+
+ /* In case extra data pointer is NULL */
+
+ if (!state->response.extra_data)
+ state->response.length = sizeof(struct winbindd_response);
+}
+
+/* Process a new connection by adding it to the client connection list */
+
+static void new_connection(int listen_sock, BOOL privileged)
+{
+ struct sockaddr_un sunaddr;
+ struct winbindd_cli_state *state;
+ socklen_t len;
+ int sock;
+
+ /* Accept connection */
+
+ len = sizeof(sunaddr);
+
+ do {
+ sock = accept(listen_sock, (struct sockaddr *)&sunaddr, &len);
+ } while (sock == -1 && errno == EINTR);
+
+ if (sock == -1)
+ return;
+
+ DEBUG(6,("accepted socket %d\n", sock));
+
+ /* Create new connection structure */
+
+ if ((state = (struct winbindd_cli_state *)
+ malloc(sizeof(*state))) == NULL)
+ return;
+
+ ZERO_STRUCTP(state);
+ state->sock = sock;
+
+ state->last_access = time(NULL);
+
+ state->privileged = privileged;
+
+ /* Add to connection list */
+
+ winbindd_add_client(state);
+}
+
+/* Remove a client connection from client connection list */
+
+static void remove_client(struct winbindd_cli_state *state)
+{
+ /* It's a dead client - hold a funeral */
+
+ if (state != NULL) {
+
+ /* Close socket */
+
+ close(state->sock);
+
+ /* Free any getent state */
+
+ free_getent_state(state->getpwent_state);
+ free_getent_state(state->getgrent_state);
+
+ /* We may have some extra data that was not freed if the
+ client was killed unexpectedly */
+
+ SAFE_FREE(state->response.extra_data);
+
+ /* Remove from list and free */
+
+ winbindd_remove_client(state);
+ SAFE_FREE(state);
+ }
+}
+
+
+/* Shutdown client connection which has been idle for the longest time */
+
+static BOOL remove_idle_client(void)
+{
+ struct winbindd_cli_state *state, *remove_state = NULL;
+ time_t last_access = 0;
+ int nidle = 0;
+
+ for (state = winbindd_client_list(); state; state = state->next) {
+ if (state->read_buf_len == 0 && state->write_buf_len == 0 &&
+ !state->getpwent_state && !state->getgrent_state) {
+ nidle++;
+ if (!last_access || state->last_access < last_access) {
+ last_access = state->last_access;
+ remove_state = state;
+ }
+ }
+ }
+
+ if (remove_state) {
+ DEBUG(5,("Found %d idle client connections, shutting down sock %d, pid %u\n",
+ nidle, remove_state->sock, (unsigned int)remove_state->pid));
+ remove_client(remove_state);
+ return True;
+ }
+
+ return False;
+}
+
+/* Process a complete received packet from a client */
+
+void winbind_process_packet(struct winbindd_cli_state *state)
+{
+ /* Process request */
+
+ /* Ensure null termination of entire request */
+ state->request.null_term = '\0';
+
+ state->pid = state->request.pid;
+
+ process_request(state);
+
+ /* Update client state */
+
+ state->read_buf_len = 0;
+ state->write_buf_len = sizeof(struct winbindd_response);
+
+ /* we might need to send it to the dual daemon */
+ if (opt_dual_daemon) {
+ dual_send_request(state);
+ }
+}
+
+/* Read some data from a client connection */
+
+void winbind_client_read(struct winbindd_cli_state *state)
+{
+ int n;
+
+ /* Read data */
+
+ n = sys_read(state->sock, state->read_buf_len +
+ (char *)&state->request,
+ sizeof(state->request) - state->read_buf_len);
+
+ DEBUG(10,("client_read: read %d bytes. Need %ld more for a full request.\n", n, (unsigned long)(sizeof(state->request) - n - state->read_buf_len) ));
+
+ /* Read failed, kill client */
+
+ if (n == -1 || n == 0) {
+ DEBUG(5,("read failed on sock %d, pid %lu: %s\n",
+ state->sock, (unsigned long)state->pid,
+ (n == -1) ? strerror(errno) : "EOF"));
+
+ state->finished = True;
+ return;
+ }
+
+ /* Update client state */
+
+ state->read_buf_len += n;
+ state->last_access = time(NULL);
+}
+
+/* Write some data to a client connection */
+
+static void client_write(struct winbindd_cli_state *state)
+{
+ char *data;
+ int num_written;
+
+ /* Write some data */
+ /*
+ * The fancy calculation of data below allows us to handle the
+ * case where write (sys_write) does not write all the data we
+ * gave it. In that case, we will come back through here again
+ * because of the loop above us, and we want to pick up where
+ * we left off.
+ */
+
+ if (!state->write_extra_data) {
+
+ /* Write response structure */
+
+ data = (char *)&state->response + sizeof(state->response) -
+ state->write_buf_len;
+
+ } else {
+
+ /* Write extra data */
+
+ data = (char *)state->response.extra_data +
+ state->response.length -
+ sizeof(struct winbindd_response) -
+ state->write_buf_len;
+ }
+
+ num_written = sys_write(state->sock, data, state->write_buf_len);
+
+ DEBUG(10,("client_write: wrote %d bytes.\n", num_written ));
+
+ /* Write failed, kill cilent */
+
+ if (num_written == -1 || num_written == 0) {
+
+ DEBUG(3,("write failed on sock %d, pid %lu: %s\n",
+ state->sock, (unsigned long)state->pid,
+ (num_written == -1) ? strerror(errno) : "EOF"));
+
+ state->finished = True;
+
+ SAFE_FREE(state->response.extra_data);
+
+ return;
+ }
+
+ /* Update client state */
+
+ state->write_buf_len -= num_written;
+ state->last_access = time(NULL);
+
+ /* Have we written all data? */
+
+ if (state->write_buf_len == 0) {
+
+ /* Take care of extra data */
+
+ if (state->write_extra_data) {
+
+ SAFE_FREE(state->response.extra_data);
+
+ state->write_extra_data = False;
+
+ DEBUG(10,("client_write: client_write: complete response written.\n"));
+
+ } else if (state->response.length >
+ sizeof(struct winbindd_response)) {
+
+ /* Start writing extra data */
+
+ state->write_buf_len =
+ state->response.length -
+ sizeof(struct winbindd_response);
+
+ DEBUG(10,("client_write: need to write %d extra data bytes.\n", (int)state->write_buf_len));
+
+ state->write_extra_data = True;
+ }
+ }
+}
+
+/* Process incoming clients on listen_sock. We use a tricky non-blocking,
+ non-forking, non-threaded model which allows us to handle many
+ simultaneous connections while remaining impervious to many denial of
+ service attacks. */
+
+static void process_loop(void)
+{
+ /* We'll be doing this a lot */
+
+ while (1) {
+ struct winbindd_cli_state *state;
+ fd_set r_fds, w_fds;
+ int maxfd, listen_sock, listen_priv_sock, selret;
+ struct timeval timeout;
+
+ /* Handle messages */
+
+ message_dispatch();
+
+ /* refresh the trusted domain cache */
+
+ rescan_trusted_domains();
+
+ /* Free up temporary memory */
+
+ lp_talloc_free();
+ main_loop_talloc_free();
+
+ /* Initialise fd lists for select() */
+
+ listen_sock = open_winbindd_socket();
+ listen_priv_sock = open_winbindd_priv_socket();
+
+ if (listen_sock == -1 || listen_priv_sock == -1) {
+ perror("open_winbind_socket");
+ exit(1);
+ }
+
+ maxfd = MAX(listen_sock, listen_priv_sock);
+
+ FD_ZERO(&r_fds);
+ FD_ZERO(&w_fds);
+ FD_SET(listen_sock, &r_fds);
+ FD_SET(listen_priv_sock, &r_fds);
+
+ timeout.tv_sec = WINBINDD_ESTABLISH_LOOP;
+ timeout.tv_usec = 0;
+
+ if (opt_dual_daemon) {
+ maxfd = dual_select_setup(&w_fds, maxfd);
+ }
+
+ /* Set up client readers and writers */
+
+ state = winbindd_client_list();
+
+ while (state) {
+
+ /* Dispose of client connection if it is marked as
+ finished */
+
+ if (state->finished) {
+ struct winbindd_cli_state *next = state->next;
+
+ remove_client(state);
+ state = next;
+ continue;
+ }
+
+ /* Select requires we know the highest fd used */
+
+ if (state->sock > maxfd)
+ maxfd = state->sock;
+
+ /* Add fd for reading */
+
+ if (state->read_buf_len != sizeof(state->request))
+ FD_SET(state->sock, &r_fds);
+
+ /* Add fd for writing */
+
+ if (state->write_buf_len)
+ FD_SET(state->sock, &w_fds);
+
+ state = state->next;
+ }
+
+ /* Call select */
+
+ selret = sys_select(maxfd + 1, &r_fds, &w_fds, NULL, &timeout);
+
+ if (selret == 0)
+ continue;
+
+ if ((selret == -1 && errno != EINTR) || selret == 0) {
+
+ /* Select error, something is badly wrong */
+
+ perror("select");
+ exit(1);
+ }
+
+ /* Create a new connection if listen_sock readable */
+
+ if (selret > 0) {
+
+ if (opt_dual_daemon) {
+ dual_select(&w_fds);
+ }
+
+ if (FD_ISSET(listen_sock, &r_fds)) {
+ while (winbindd_num_clients() > WINBINDD_MAX_SIMULTANEOUS_CLIENTS - 1) {
+ DEBUG(5,("winbindd: Exceeding %d client connections, removing idle connection.\n",
+ WINBINDD_MAX_SIMULTANEOUS_CLIENTS));
+ if (!remove_idle_client()) {
+ DEBUG(0,("winbindd: Exceeding %d client connections, no idle connection found\n",
+ WINBINDD_MAX_SIMULTANEOUS_CLIENTS));
+ break;
+ }
+ }
+ /* new, non-privileged connection */
+ new_connection(listen_sock, False);
+ }
+
+ if (FD_ISSET(listen_priv_sock, &r_fds)) {
+ while (winbindd_num_clients() > WINBINDD_MAX_SIMULTANEOUS_CLIENTS - 1) {
+ DEBUG(5,("winbindd: Exceeding %d client connections, removing idle connection.\n",
+ WINBINDD_MAX_SIMULTANEOUS_CLIENTS));
+ if (!remove_idle_client()) {
+ DEBUG(0,("winbindd: Exceeding %d client connections, no idle connection found\n",
+ WINBINDD_MAX_SIMULTANEOUS_CLIENTS));
+ break;
+ }
+ }
+ /* new, privileged connection */
+ new_connection(listen_priv_sock, True);
+ }
+
+ /* Process activity on client connections */
+
+ for (state = winbindd_client_list(); state;
+ state = state->next) {
+
+ /* Data available for reading */
+
+ if (FD_ISSET(state->sock, &r_fds)) {
+
+ /* Read data */
+
+ winbind_client_read(state);
+
+ /*
+ * If we have the start of a
+ * packet, then check the
+ * length field to make sure
+ * the client's not talking
+ * Mock Swedish.
+ */
+
+ if (state->read_buf_len >= sizeof(uint32)
+ && *(uint32 *) &state->request != sizeof(state->request)) {
+ DEBUG(0,("process_loop: Invalid request size from pid %lu: %d bytes sent, should be %ld\n",
+ (unsigned long)state->request.pid, *(uint32 *) &state->request, (unsigned long)sizeof(state->request)));
+ DEBUGADD(0, ("This usually means that you are running old wbinfo, pam_winbind or libnss_winbind clients\n"));
+
+ remove_client(state);
+ break;
+ }
+
+ /* A request packet might be
+ complete */
+
+ if (state->read_buf_len ==
+ sizeof(state->request)) {
+ winbind_process_packet(state);
+ }
+ }
+
+ /* Data available for writing */
+
+ if (FD_ISSET(state->sock, &w_fds))
+ client_write(state);
+ }
+ }
+
+#if 0
+ winbindd_check_cache_size(time(NULL));
+#endif
+
+ /* Check signal handling things */
+
+ if (do_sigterm)
+ terminate();
+
+ if (do_sighup) {
+
+ DEBUG(3, ("got SIGHUP\n"));
+
+ msg_reload_services(MSG_SMB_CONF_UPDATED, (pid_t) 0, NULL, 0);
+ do_sighup = False;
+ }
+
+ if (do_sigusr2) {
+ print_winbindd_status();
+ do_sigusr2 = False;
+ }
+ }
+}
+
+/* Main function */
+
+struct winbindd_state server_state; /* Server state information */
+
+int main(int argc, char **argv)
+{
+ pstring logfile;
+ static BOOL interactive = False;
+ static BOOL Fork = True;
+ static BOOL log_stdout = False;
+ struct poptOption long_options[] = {
+ POPT_AUTOHELP
+ { "stdout", 'S', POPT_ARG_VAL, &log_stdout, True, "Log to stdout" },
+ { "foreground", 'F', POPT_ARG_VAL, &Fork, False, "Daemon in foreground mode" },
+ { "interactive", 'i', POPT_ARG_NONE, NULL, 'i', "Interactive mode" },
+ { "single-daemon", 'Y', POPT_ARG_VAL, &opt_dual_daemon, False, "Single daemon mode" },
+ { "no-caching", 'n', POPT_ARG_VAL, &opt_nocache, True, "Disable caching" },
+ POPT_COMMON_SAMBA
+ POPT_TABLEEND
+ };
+ poptContext pc;
+ int opt;
+
+ /* glibc (?) likes to print "User defined signal 1" and exit if a
+ SIGUSR[12] is received before a handler is installed */
+
+ CatchSignal(SIGUSR1, SIG_IGN);
+ CatchSignal(SIGUSR2, SIG_IGN);
+
+ fault_setup((void (*)(void *))fault_quit );
+
+ /* Initialise for running in non-root mode */
+
+ sec_init();
+
+ set_remote_machine_name("winbindd", False);
+
+ /* Set environment variable so we don't recursively call ourselves.
+ This may also be useful interactively. */
+
+ setenv(WINBINDD_DONT_ENV, "1", 1);
+
+ /* Initialise samba/rpc client stuff */
+
+ pc = poptGetContext("winbindd", argc, (const char **)argv, long_options,
+ POPT_CONTEXT_KEEP_FIRST);
+
+ while ((opt = poptGetNextOpt(pc)) != -1) {
+ switch (opt) {
+ /* Don't become a daemon */
+ case 'i':
+ interactive = True;
+ log_stdout = True;
+ Fork = False;
+ break;
+ }
+ }
+
+
+ if (log_stdout && Fork) {
+ printf("Can't log to stdout (-S) unless daemon is in foreground +(-F) or interactive (-i)\n");
+ poptPrintUsage(pc, stderr, 0);
+ exit(1);
+ }
+
+ pstr_sprintf(logfile, "%s/log.winbindd", dyn_LOGFILEBASE);
+ lp_set_logfile(logfile);
+ setup_logging("winbindd", log_stdout);
+ reopen_logs();
+
+ DEBUG(1, ("winbindd version %s started.\n", SAMBA_VERSION_STRING) );
+ DEBUGADD( 1, ( "Copyright The Samba Team 2000-2004\n" ) );
+
+ if (!reload_services_file()) {
+ DEBUG(0, ("error opening config file\n"));
+ exit(1);
+ }
+
+ /* Setup names. */
+
+ if (!init_names())
+ exit(1);
+
+ load_interfaces();
+
+ if (!secrets_init()) {
+
+ DEBUG(0,("Could not initialize domain trust account secrets. Giving up\n"));
+ return False;
+ }
+
+ /* Enable netbios namecache */
+
+ namecache_enable();
+
+ /* Check winbindd parameters are valid */
+
+ ZERO_STRUCT(server_state);
+
+ if (!winbindd_param_init())
+ return 1;
+
+ /* Winbind daemon initialisation */
+
+ if (!winbindd_upgrade_idmap())
+ return 1;
+
+ if (!idmap_init(lp_idmap_backend()))
+ return 1;
+
+ /* Unblock all signals we are interested in as they may have been
+ blocked by the parent process. */
+
+ BlockSignals(False, SIGINT);
+ BlockSignals(False, SIGQUIT);
+ BlockSignals(False, SIGTERM);
+ BlockSignals(False, SIGUSR1);
+ BlockSignals(False, SIGUSR2);
+ BlockSignals(False, SIGHUP);
+
+ /* Setup signal handlers */
+
+ CatchSignal(SIGINT, termination_handler); /* Exit on these sigs */
+ CatchSignal(SIGQUIT, termination_handler);
+ CatchSignal(SIGTERM, termination_handler);
+
+ CatchSignal(SIGPIPE, SIG_IGN); /* Ignore sigpipe */
+
+ CatchSignal(SIGUSR2, sigusr2_handler); /* Debugging sigs */
+ CatchSignal(SIGHUP, sighup_handler);
+
+ if (!interactive)
+ become_daemon(Fork);
+
+ pidfile_create("winbindd");
+
+#if HAVE_SETPGID
+ /*
+ * If we're interactive we want to set our own process group for
+ * signal management.
+ */
+ if (interactive)
+ setpgid( (pid_t)0, (pid_t)0);
+#endif
+
+ if (opt_dual_daemon) {
+ do_dual_daemon();
+ }
+
+ /* Initialise messaging system */
+
+ if (!message_init()) {
+ DEBUG(0, ("unable to initialise messaging system\n"));
+ exit(1);
+ }
+
+ /* React on 'smbcontrol winbindd reload-config' in the same way
+ as to SIGHUP signal */
+ message_register(MSG_SMB_CONF_UPDATED, msg_reload_services);
+ message_register(MSG_SHUTDOWN, msg_shutdown);
+
+ poptFreeContext(pc);
+
+ netsamlogon_cache_init(); /* Non-critical */
+
+ init_domain_list();
+
+ /* Loop waiting for requests */
+
+ process_loop();
+
+ trustdom_cache_shutdown();
+
+ return 0;
+}
diff --git a/source/nsswitch/winbindd.h b/source/nsswitch/winbindd.h
new file mode 100644
index 00000000000..5c05a1b0457
--- /dev/null
+++ b/source/nsswitch/winbindd.h
@@ -0,0 +1,238 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ Winbind daemon for ntdom nss module
+
+ Copyright (C) Tim Potter 2000
+ Copyright (C) Jim McDonough <jmcd@us.ibm.com> 2003
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 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 Library General Public
+ License along with this library; if not, write to the
+ Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+*/
+
+#ifndef _WINBINDD_H
+#define _WINBINDD_H
+
+#include "nterr.h"
+
+#include "winbindd_nss.h"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_WINBIND
+
+/* Client state structure */
+
+struct winbindd_cli_state {
+ struct winbindd_cli_state *prev, *next; /* Linked list pointers */
+ int sock; /* Open socket from client */
+ pid_t pid; /* pid of client */
+ int read_buf_len, write_buf_len; /* Indexes in request/response */
+ BOOL finished; /* Can delete from list */
+ BOOL write_extra_data; /* Write extra_data field */
+ time_t last_access; /* Time of last access (read or write) */
+ BOOL privileged; /* Is the client 'privileged' */
+
+ struct winbindd_request request; /* Request from client */
+ struct winbindd_response response; /* Respose to client */
+ struct getent_state *getpwent_state; /* State for getpwent() */
+ struct getent_state *getgrent_state; /* State for getgrent() */
+};
+
+/* State between get{pw,gr}ent() calls */
+
+struct getent_state {
+ struct getent_state *prev, *next;
+ void *sam_entries;
+ uint32 sam_entry_index, num_sam_entries;
+ BOOL got_sam_entries;
+ fstring domain_name;
+};
+
+/* Storage for cached getpwent() user entries */
+
+struct getpwent_user {
+ fstring name; /* Account name */
+ fstring gecos; /* User information */
+ DOM_SID user_sid; /* NT user and primary group SIDs */
+ DOM_SID group_sid;
+};
+
+/* Server state structure */
+
+struct winbindd_state {
+
+ /* User and group id pool */
+
+ uid_t uid_low, uid_high; /* Range of uids to allocate */
+ gid_t gid_low, gid_high; /* Range of gids to allocate */
+};
+
+extern struct winbindd_state server_state; /* Server information */
+
+typedef struct {
+ char *acct_name;
+ char *full_name;
+ DOM_SID *user_sid; /* NT user and primary group SIDs */
+ DOM_SID *group_sid;
+} WINBIND_USERINFO;
+
+/* Structures to hold per domain information */
+
+struct winbindd_domain {
+ fstring name; /* Domain name */
+ fstring alt_name; /* alt Domain name (if any) */
+ DOM_SID sid; /* SID for this domain */
+ BOOL native_mode; /* is this a win2k domain in native mode ? */
+ BOOL active_directory; /* is this a win2k active directory ? */
+ BOOL primary; /* is this our primary domain ? */
+ BOOL internal; /* BUILTIN and member SAM */
+
+ /* Lookup methods for this domain (LDAP or RPC) */
+ struct winbindd_methods *methods;
+
+ /* the backend methods are used by the cache layer to find the right
+ backend */
+ struct winbindd_methods *backend;
+
+ /* Private data for the backends (used for connection cache) */
+
+ void *private;
+
+ /* Sequence number stuff */
+
+ time_t last_seq_check;
+ uint32 sequence_number;
+ NTSTATUS last_status;
+
+ /* Linked list info */
+
+ struct winbindd_domain *prev, *next;
+};
+
+/* per-domain methods. This is how LDAP vs RPC is selected
+ */
+struct winbindd_methods {
+ /* does this backend provide a consistent view of the data? (ie. is the primary group
+ always correct) */
+ BOOL consistent;
+
+ /* get a list of users, returning a WINBIND_USERINFO for each one */
+ NTSTATUS (*query_user_list)(struct winbindd_domain *domain,
+ TALLOC_CTX *mem_ctx,
+ uint32 *num_entries,
+ WINBIND_USERINFO **info);
+
+ /* get a list of domain groups */
+ NTSTATUS (*enum_dom_groups)(struct winbindd_domain *domain,
+ TALLOC_CTX *mem_ctx,
+ uint32 *num_entries,
+ struct acct_info **info);
+
+ /* get a list of domain local groups */
+ NTSTATUS (*enum_local_groups)(struct winbindd_domain *domain,
+ TALLOC_CTX *mem_ctx,
+ uint32 *num_entries,
+ struct acct_info **info);
+
+ /* convert one user or group name to a sid */
+ NTSTATUS (*name_to_sid)(struct winbindd_domain *domain,
+ TALLOC_CTX *mem_ctx,
+ const char *name,
+ DOM_SID *sid,
+ enum SID_NAME_USE *type);
+
+ /* convert a sid to a user or group name */
+ NTSTATUS (*sid_to_name)(struct winbindd_domain *domain,
+ TALLOC_CTX *mem_ctx,
+ const DOM_SID *sid,
+ char **name,
+ enum SID_NAME_USE *type);
+
+ /* lookup user info for a given SID */
+ NTSTATUS (*query_user)(struct winbindd_domain *domain,
+ TALLOC_CTX *mem_ctx,
+ const DOM_SID *user_sid,
+ WINBIND_USERINFO *user_info);
+
+ /* lookup all groups that a user is a member of. The backend
+ can also choose to lookup by username or rid for this
+ function */
+ NTSTATUS (*lookup_usergroups)(struct winbindd_domain *domain,
+ TALLOC_CTX *mem_ctx,
+ const DOM_SID *user_sid,
+ uint32 *num_groups, DOM_SID ***user_gids);
+
+ /* find all members of the group with the specified group_rid */
+ NTSTATUS (*lookup_groupmem)(struct winbindd_domain *domain,
+ TALLOC_CTX *mem_ctx,
+ const DOM_SID *group_sid,
+ uint32 *num_names,
+ DOM_SID ***sid_mem, char ***names,
+ uint32 **name_types);
+
+ /* return the current global sequence number */
+ NTSTATUS (*sequence_number)(struct winbindd_domain *domain, uint32 *seq);
+
+ /* enumerate trusted domains */
+ NTSTATUS (*trusted_domains)(struct winbindd_domain *domain,
+ TALLOC_CTX *mem_ctx,
+ uint32 *num_domains,
+ char ***names,
+ char ***alt_names,
+ DOM_SID **dom_sids);
+
+ /* find the domain sid */
+ NTSTATUS (*domain_sid)(struct winbindd_domain *domain,
+ DOM_SID *sid);
+
+ /* setup the list of alternate names for the domain, if any */
+ NTSTATUS (*alternate_name)(struct winbindd_domain *domain);
+};
+
+/* Used to glue a policy handle and cli_state together */
+
+typedef struct {
+ struct cli_state *cli;
+ POLICY_HND pol;
+} CLI_POLICY_HND;
+
+/* Filled out by IDMAP backends */
+struct winbindd_idmap_methods {
+ /* Called when backend is first loaded */
+ BOOL (*init)(void);
+
+ BOOL (*get_sid_from_uid)(uid_t uid, DOM_SID *sid);
+ BOOL (*get_sid_from_gid)(gid_t gid, DOM_SID *sid);
+
+ BOOL (*get_uid_from_sid)(DOM_SID *sid, uid_t *uid);
+ BOOL (*get_gid_from_sid)(DOM_SID *sid, gid_t *gid);
+
+ /* Called when backend is unloaded */
+ BOOL (*close)(void);
+ /* Called to dump backend status */
+ void (*status)(void);
+};
+
+#include "../nsswitch/winbindd_proto.h"
+
+#include "rpc_parse.h"
+#include "rpc_client.h"
+
+#define WINBINDD_ESTABLISH_LOOP 30
+#define WINBINDD_RESCAN_FREQ 300
+
+#define DOM_SEQUENCE_NONE ((uint32)-1)
+
+#endif /* _WINBINDD_H */
diff --git a/source/nsswitch/winbindd_acct.c b/source/nsswitch/winbindd_acct.c
new file mode 100644
index 00000000000..e6496695cb6
--- /dev/null
+++ b/source/nsswitch/winbindd_acct.c
@@ -0,0 +1,1223 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ Winbind account management functions
+
+ Copyright (C) by Gerald (Jerry) Carter 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 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 "winbindd.h"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_WINBIND
+
+#define WBKEY_PASSWD "WBA_PASSWD"
+#define WBKEY_GROUP "WBA_GROUP"
+
+#define NUM_PW_FIELDS 7
+#define NUM_GRP_FIELDS 4
+
+/* Globals */
+
+static TDB_CONTEXT *account_tdb;
+
+extern userdom_struct current_user_info;
+
+struct _check_primary_grp {
+ gid_t gid;
+ BOOL found;
+};
+
+/**********************************************************************
+**********************************************************************/
+
+static void free_winbindd_gr( WINBINDD_GR *grp )
+{
+ int i;
+
+ if ( !grp )
+ return;
+
+ for ( i=0; i<grp->num_gr_mem; i++ )
+ SAFE_FREE( grp->gr_mem[i] );
+
+ SAFE_FREE( grp->gr_mem );
+
+ return;
+}
+
+/*****************************************************************************
+ Initialise auto-account database.
+*****************************************************************************/
+
+static BOOL winbindd_accountdb_init(void)
+{
+ /* see if we've already opened the tdb */
+
+ if ( account_tdb )
+ return True;
+
+ /* winbindd_idmap.tdb should always be opened by the idmap_init()
+ code first */
+
+ if ( !(account_tdb = idmap_tdb_handle()) ) {
+ DEBUG(0, ("winbindd_accountdb_init: Unable to retreive handle for database\n"));
+ return False;
+ }
+
+ /* yeah! */
+
+ return True;
+}
+
+/**********************************************************************
+ Convert a string in /etc/passwd format to a struct passwd* entry
+**********************************************************************/
+
+static WINBINDD_PW* string2passwd( char *string )
+{
+ static WINBINDD_PW pw;
+ char *p, *str;
+ char *fields[NUM_PW_FIELDS];
+ int i;
+
+ if ( !string )
+ return NULL;
+
+ ZERO_STRUCTP( &pw );
+
+ DEBUG(10,("string2passwd: converting \"%s\"\n", string));
+
+ ZERO_STRUCT( fields );
+
+ for ( i=0, str=string; i<NUM_PW_FIELDS-1; i++ ) {
+ if ( !(p = strchr( str, ':' )) ) {
+ DEBUG(0,("string2passwd: parsing failure\n"));
+ return NULL;
+ }
+ *p = '\0';
+ if ( str )
+ fields[i] = str;
+ str = p + 1;
+ }
+ if ( str )
+ fields[i] = str;
+
+ /* copy fields */
+
+ fstrcpy( pw.pw_name, fields[0] );
+ fstrcpy( pw.pw_passwd, fields[1] );
+ pw.pw_uid = atoi( fields[2] );
+ pw.pw_gid = atoi( fields[3] );
+ fstrcpy( pw.pw_gecos, fields[4] );
+ fstrcpy( pw.pw_dir, fields[5] );
+ fstrcpy( pw.pw_shell, fields[6] );
+
+
+ /* last minute sanity checks */
+
+ if ( pw.pw_uid==0 || pw.pw_gid==0 ) {
+ DEBUG(0,("string2passwd: Failure! uid==%lu, gid==%lu\n",
+ (unsigned long)pw.pw_uid, (unsigned long)pw.pw_gid));
+ return NULL;
+ }
+
+ DEBUG(10,("string2passwd: Success\n"));
+
+ return &pw;
+}
+
+/**********************************************************************
+ Convert a struct passwd* to a string formatted for /etc/passwd
+**********************************************************************/
+
+static char* passwd2string( const WINBINDD_PW *pw )
+{
+ static pstring string;
+ int ret;
+
+ if ( !pw || !pw->pw_name )
+ return NULL;
+
+ DEBUG(10,("passwd2string: converting passwd struct for %s\n",
+ pw->pw_name));
+
+ ret = pstr_sprintf( string, "%s:%s:%lu:%lu:%s:%s:%s",
+ pw->pw_name,
+ pw->pw_passwd ? pw->pw_passwd : "x",
+ (unsigned long)pw->pw_uid,
+ (unsigned long)pw->pw_gid,
+ pw->pw_gecos,
+ pw->pw_dir,
+ pw->pw_shell );
+
+ if ( ret < 0 ) {
+ DEBUG(0,("passwd2string: pstr_sprintf() failed!\n"));
+ return NULL;
+ }
+
+ return string;
+}
+
+/**********************************************************************
+ Convert a string in /etc/group format to a struct group* entry
+**********************************************************************/
+
+static WINBINDD_GR* string2group( char *string )
+{
+ static WINBINDD_GR grp;
+ char *p, *str;
+ char *fields[NUM_GRP_FIELDS];
+ int i;
+ char **gr_members = NULL;
+ int num_gr_members = 0;
+
+ if ( !string )
+ return NULL;
+
+ ZERO_STRUCTP( &grp );
+
+ DEBUG(10,("string2group: converting \"%s\"\n", string));
+
+ ZERO_STRUCT( fields );
+
+ for ( i=0, str=string; i<NUM_GRP_FIELDS-1; i++ ) {
+ if ( !(p = strchr( str, ':' )) ) {
+ DEBUG(0,("string2group: parsing failure\n"));
+ return NULL;
+ }
+ *p = '\0';
+ if ( str )
+ fields[i] = str;
+ str = p + 1;
+ }
+
+ /* group members */
+
+ if ( *str ) {
+ /* we already know we have a non-empty string */
+
+ num_gr_members = count_chars(str, ',') + 1;
+
+ /* if there was at least one comma, then there
+ are n+1 members */
+ if ( num_gr_members ) {
+ fstring buffer;
+
+ gr_members = (char**)smb_xmalloc(sizeof(char*)*(num_gr_members+1));
+
+ i = 0;
+ while ( next_token(&str, buffer, ",", sizeof(buffer)) && i<num_gr_members ) {
+ gr_members[i++] = smb_xstrdup(buffer);
+ }
+
+ gr_members[i] = NULL;
+ }
+ }
+
+
+ /* copy fields */
+
+ fstrcpy( grp.gr_name, fields[0] );
+ fstrcpy( grp.gr_passwd, fields[1] );
+ grp.gr_gid = atoi( fields[2] );
+
+ grp.num_gr_mem = num_gr_members;
+ grp.gr_mem = gr_members;
+
+ /* last minute sanity checks */
+
+ if ( grp.gr_gid == 0 ) {
+ DEBUG(0,("string2group: Failure! gid==%lu\n", (unsigned long)grp.gr_gid));
+ SAFE_FREE( gr_members );
+ return NULL;
+ }
+
+ DEBUG(10,("string2group: Success\n"));
+
+ return &grp;
+}
+
+/**********************************************************************
+ Convert a struct group* to a string formatted for /etc/group
+**********************************************************************/
+
+static char* group2string( const WINBINDD_GR *grp )
+{
+ static pstring string;
+ int ret;
+ char *member, *gr_mem_str;
+ int num_members;
+ int i, size;
+
+ if ( !grp || !grp->gr_name )
+ return NULL;
+
+ DEBUG(10,("group2string: converting passwd struct for %s\n",
+ grp->gr_name));
+
+ if ( grp->num_gr_mem ) {
+ int idx = 0;
+
+ member = grp->gr_mem[0];
+ size = 0;
+ num_members = 0;
+
+ while ( member ) {
+ size += strlen(member) + 1;
+ num_members++;
+ member = grp->gr_mem[num_members];
+ }
+
+ gr_mem_str = smb_xmalloc(size);
+
+ for ( i=0; i<num_members; i++ ) {
+ snprintf( &gr_mem_str[idx], size-idx, "%s,", grp->gr_mem[i] );
+ idx += strlen(grp->gr_mem[i]) + 1;
+ }
+ /* add trailing NULL (also removes trailing ',' */
+ gr_mem_str[size-1] = '\0';
+ }
+ else {
+ /* no members */
+ gr_mem_str = smb_xmalloc(sizeof(fstring));
+ fstrcpy( gr_mem_str, "" );
+ }
+
+ ret = pstr_sprintf( string, "%s:%s:%lu:%s",
+ grp->gr_name,
+ grp->gr_passwd ? grp->gr_passwd : "*",
+ (unsigned long)grp->gr_gid,
+ gr_mem_str );
+
+ SAFE_FREE( gr_mem_str );
+
+ if ( ret < 0 ) {
+ DEBUG(0,("group2string: pstr_sprintf() failed!\n"));
+ return NULL;
+ }
+
+ return string;
+}
+
+/**********************************************************************
+**********************************************************************/
+
+static char* acct_userkey_byname( const char *name )
+{
+ static fstring key;
+
+ fstr_sprintf( key, "%s/NAME/%s", WBKEY_PASSWD, name );
+
+ return key;
+}
+
+/**********************************************************************
+**********************************************************************/
+
+static char* acct_userkey_byuid( uid_t uid )
+{
+ static fstring key;
+
+ fstr_sprintf( key, "%s/UID/%lu", WBKEY_PASSWD, (unsigned long)uid );
+
+ return key;
+}
+
+/**********************************************************************
+**********************************************************************/
+
+static char* acct_groupkey_byname( const char *name )
+{
+ static fstring key;
+
+ fstr_sprintf( key, "%s/NAME/%s", WBKEY_GROUP, name );
+
+ return key;
+}
+
+/**********************************************************************
+**********************************************************************/
+
+static char* acct_groupkey_bygid( gid_t gid )
+{
+ static fstring key;
+
+ fstr_sprintf( key, "%s/GID/%lu", WBKEY_GROUP, (unsigned long)gid );
+
+ return key;
+}
+
+/**********************************************************************
+**********************************************************************/
+
+WINBINDD_PW* wb_getpwnam( const char * name )
+{
+ char *keystr;
+ TDB_DATA data;
+ static WINBINDD_PW *pw;
+
+ if ( !account_tdb && !winbindd_accountdb_init() ) {
+ DEBUG(0,("wb_getpwnam: Failed to open winbindd account db\n"));
+ return NULL;
+ }
+
+
+ keystr = acct_userkey_byname( name );
+
+ data = tdb_fetch_bystring( account_tdb, keystr );
+
+ pw = NULL;
+
+ if ( data.dptr ) {
+ pw = string2passwd( data.dptr );
+ SAFE_FREE( data.dptr );
+ }
+
+ DEBUG(5,("wb_getpwnam: %s user (%s)\n",
+ (pw ? "Found" : "Did not find"), name ));
+
+ return pw;
+}
+
+/**********************************************************************
+**********************************************************************/
+
+WINBINDD_PW* wb_getpwuid( const uid_t uid )
+{
+ char *keystr;
+ TDB_DATA data;
+ static WINBINDD_PW *pw;
+
+ if ( !account_tdb && !winbindd_accountdb_init() ) {
+ DEBUG(0,("wb_getpwuid: Failed to open winbindd account db\n"));
+ return NULL;
+ }
+
+ data = tdb_fetch_bystring( account_tdb, acct_userkey_byuid(uid) );
+ if ( !data.dptr ) {
+ DEBUG(4,("wb_getpwuid: failed to locate uid == %lu\n", (unsigned long)uid));
+ return NULL;
+ }
+ keystr = acct_userkey_byname( data.dptr );
+
+ SAFE_FREE( data.dptr );
+
+ data = tdb_fetch_bystring( account_tdb, keystr );
+
+ pw = NULL;
+
+ if ( data.dptr ) {
+ pw = string2passwd( data.dptr );
+ SAFE_FREE( data.dptr );
+ }
+
+ DEBUG(5,("wb_getpwuid: %s user (uid == %lu)\n",
+ (pw ? "Found" : "Did not find"), (unsigned long)uid ));
+
+ return pw;
+}
+
+/**********************************************************************
+**********************************************************************/
+
+static BOOL wb_storepwnam( const WINBINDD_PW *pw )
+{
+ char *namekey, *uidkey;
+ TDB_DATA data;
+ char *str;
+ int ret = 0;
+ fstring username;
+
+ if ( !account_tdb && !winbindd_accountdb_init() ) {
+ DEBUG(0,("wb_storepwnam: Failed to open winbindd account db\n"));
+ return False;
+ }
+
+ namekey = acct_userkey_byname( pw->pw_name );
+
+ /* lock the main entry first */
+
+ if ( tdb_lock_bystring(account_tdb, namekey, 0) == -1 ) {
+ DEBUG(0,("wb_storepwnam: Failed to lock %s\n", namekey));
+ return False;
+ }
+
+ str = passwd2string( pw );
+
+ data.dptr = str;
+ data.dsize = strlen(str) + 1;
+
+ if ( (tdb_store_bystring(account_tdb, namekey, data, TDB_REPLACE)) == -1 ) {
+ DEBUG(0,("wb_storepwnam: Failed to store \"%s\"\n", str));
+ ret = -1;
+ goto done;
+ }
+
+ /* store the uid index */
+
+ uidkey = acct_userkey_byuid(pw->pw_uid);
+
+ fstrcpy( username, pw->pw_name );
+ data.dptr = username;
+ data.dsize = strlen(username) + 1;
+
+ if ( (tdb_store_bystring(account_tdb, uidkey, data, TDB_REPLACE)) == -1 ) {
+ DEBUG(0,("wb_storepwnam: Failed to store uid key \"%s\"\n", str));
+ tdb_delete_bystring(account_tdb, namekey);
+ ret = -1;
+ goto done;
+ }
+
+ DEBUG(10,("wb_storepwnam: Success -> \"%s\"\n", str));
+
+done:
+ tdb_unlock_bystring( account_tdb, namekey );
+
+ return ( ret == 0 );
+}
+
+/**********************************************************************
+**********************************************************************/
+
+WINBINDD_GR* wb_getgrnam( const char * name )
+{
+ char *keystr;
+ TDB_DATA data;
+ static WINBINDD_GR *grp;
+
+ if ( !account_tdb && !winbindd_accountdb_init() ) {
+ DEBUG(0,("wb_getgrnam: Failed to open winbindd account db\n"));
+ return NULL;
+ }
+
+
+ keystr = acct_groupkey_byname( name );
+
+ data = tdb_fetch_bystring( account_tdb, keystr );
+
+ grp = NULL;
+
+ if ( data.dptr ) {
+ grp = string2group( data.dptr );
+ SAFE_FREE( data.dptr );
+ }
+
+ DEBUG(5,("wb_getgrnam: %s group (%s)\n",
+ (grp ? "Found" : "Did not find"), name ));
+
+ return grp;
+}
+
+/**********************************************************************
+**********************************************************************/
+
+WINBINDD_GR* wb_getgrgid( gid_t gid )
+{
+ char *keystr;
+ TDB_DATA data;
+ static WINBINDD_GR *grp;
+
+ if ( !account_tdb && !winbindd_accountdb_init() ) {
+ DEBUG(0,("wb_getgrgid: Failed to open winbindd account db\n"));
+ return NULL;
+ }
+
+ data = tdb_fetch_bystring( account_tdb, acct_groupkey_bygid(gid) );
+ if ( !data.dptr ) {
+ DEBUG(4,("wb_getgrgid: failed to locate gid == %lu\n",
+ (unsigned long)gid));
+ return NULL;
+ }
+ keystr = acct_groupkey_byname( data.dptr );
+
+ SAFE_FREE( data.dptr );
+
+ data = tdb_fetch_bystring( account_tdb, keystr );
+
+ grp = NULL;
+
+ if ( data.dptr ) {
+ grp = string2group( data.dptr );
+ SAFE_FREE( data.dptr );
+ }
+
+ DEBUG(5,("wb_getgrgid: %s group (gid == %lu)\n",
+ (grp ? "Found" : "Did not find"), (unsigned long)gid ));
+
+ return grp;
+}
+
+/**********************************************************************
+**********************************************************************/
+
+static BOOL wb_storegrnam( const WINBINDD_GR *grp )
+{
+ char *namekey, *gidkey;
+ TDB_DATA data;
+ char *str;
+ int ret = 0;
+ fstring groupname;
+
+ if ( !account_tdb && !winbindd_accountdb_init() ) {
+ DEBUG(0,("wb_storepwnam: Failed to open winbindd account db\n"));
+ return False;
+ }
+
+ namekey = acct_groupkey_byname( grp->gr_name );
+
+ /* lock the main entry first */
+
+ if ( tdb_lock_bystring(account_tdb, namekey, 0) == -1 ) {
+ DEBUG(0,("wb_storegrnam: Failed to lock %s\n", namekey));
+ return False;
+ }
+
+ str = group2string( grp );
+
+ data.dptr = str;
+ data.dsize = strlen(str) + 1;
+
+ if ( (tdb_store_bystring(account_tdb, namekey, data, TDB_REPLACE)) == -1 ) {
+ DEBUG(0,("wb_storegrnam: Failed to store \"%s\"\n", str));
+ ret = -1;
+ goto done;
+ }
+
+ /* store the gid index */
+
+ gidkey = acct_groupkey_bygid(grp->gr_gid);
+
+ fstrcpy( groupname, grp->gr_name );
+ data.dptr = groupname;
+ data.dsize = strlen(groupname) + 1;
+
+ if ( (tdb_store_bystring(account_tdb, gidkey, data, TDB_REPLACE)) == -1 ) {
+ DEBUG(0,("wb_storegrnam: Failed to store gid key \"%s\"\n", str));
+ tdb_delete_bystring(account_tdb, namekey);
+ ret = -1;
+ goto done;
+ }
+
+ DEBUG(10,("wb_storegrnam: Success -> \"%s\"\n", str));
+
+done:
+ tdb_unlock_bystring( account_tdb, namekey );
+
+ return ( ret == 0 );
+}
+
+/**********************************************************************
+**********************************************************************/
+
+static BOOL wb_addgrpmember( WINBINDD_GR *grp, const char *user )
+{
+ int i;
+ char **members;
+
+ if ( !grp || !user )
+ return False;
+
+ for ( i=0; i<grp->num_gr_mem; i++ ) {
+ if ( StrCaseCmp( grp->gr_mem[i], user ) == 0 )
+ return True;
+ }
+
+ /* add one new slot and keep an extra for the terminating NULL */
+ members = Realloc( grp->gr_mem, (grp->num_gr_mem+2)*sizeof(char*) );
+ if ( !members )
+ return False;
+
+ grp->gr_mem = members;
+ grp->gr_mem[grp->num_gr_mem++] = smb_xstrdup(user);
+ grp->gr_mem[grp->num_gr_mem] = NULL;
+
+ return True;
+}
+
+/**********************************************************************
+**********************************************************************/
+
+static BOOL wb_delgrpmember( WINBINDD_GR *grp, const char *user )
+{
+ int i;
+ BOOL found = False;
+
+ if ( !grp || !user )
+ return False;
+
+ for ( i=0; i<grp->num_gr_mem; i++ ) {
+ if ( StrCaseCmp( grp->gr_mem[i], user ) == 0 ) {
+ found = True;
+ break;
+ }
+ }
+
+ if ( !found )
+ return False;
+
+ /* still some remaining members */
+
+ if ( grp->num_gr_mem > 1 ) {
+ SAFE_FREE(grp->gr_mem[i]);
+ grp->num_gr_mem--;
+ grp->gr_mem[i] = grp->gr_mem[grp->num_gr_mem];
+ grp->gr_mem[grp->num_gr_mem] = NULL;
+ }
+ else { /* last one */
+ free_winbindd_gr( grp );
+ grp->gr_mem = NULL;
+ grp->num_gr_mem = 0;
+ }
+
+ return True;
+}
+
+/**********************************************************************
+**********************************************************************/
+
+static int cleangroups_traverse_fn(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf,
+ void *state)
+{
+ int len;
+ fstring key;
+ char *name = (char*)state;
+
+ fstr_sprintf( key, "%s/NAME", WBKEY_GROUP );
+ len = strlen(key);
+
+ /* if this is a group entry then, check the members */
+
+ if ( (strncmp(kbuf.dptr, key, len) == 0) && dbuf.dptr ) {
+ WINBINDD_GR *grp;
+
+ if ( !(grp = string2group( dbuf.dptr )) ) {
+ DEBUG(0,("cleangroups_traverse_fn: Failure to parse [%s]\n",
+ dbuf.dptr));
+ return 0;
+ }
+
+ /* just try to delete the user and rely on wb_delgrpmember()
+ to tell you whether or not the group changed. This is more
+ effecient than testing group membership first since the
+ checks for deleting a user from a group is essentially the
+ same as checking if he/she is a member */
+
+ if ( wb_delgrpmember( grp, name ) ) {
+ DEBUG(10,("cleanupgroups_traverse_fn: Removed user (%s) from group (%s)\n",
+ name, grp->gr_name));
+ wb_storegrnam( grp );
+ }
+
+ free_winbindd_gr( grp );
+ }
+
+ return 0;
+}
+
+/**********************************************************************
+**********************************************************************/
+
+static BOOL wb_delete_user( WINBINDD_PW *pw)
+{
+ char *namekey;
+ char *uidkey;
+
+ if ( !account_tdb && !winbindd_accountdb_init() ) {
+ DEBUG(0,("wb_delete_user: Failed to open winbindd account db\n"));
+ return False;
+ }
+
+ namekey = acct_userkey_byname( pw->pw_name );
+
+ /* lock the main entry first */
+
+ if ( tdb_lock_bystring(account_tdb, namekey, 0) == -1 ) {
+ DEBUG(0,("wb_delete_user: Failed to lock %s\n", namekey));
+ return False;
+ }
+
+ /* remove user from all groups */
+
+ tdb_traverse(account_tdb, cleangroups_traverse_fn, (void *)pw->pw_name);
+
+ /* remove the user */
+ uidkey = acct_userkey_byuid( pw->pw_uid );
+
+ tdb_delete_bystring( account_tdb, namekey );
+ tdb_delete_bystring( account_tdb, uidkey );
+
+ tdb_unlock_bystring( account_tdb, namekey );
+
+ return True;
+}
+
+/**********************************************************************
+**********************************************************************/
+
+static int isprimarygroup_traverse_fn(TDB_CONTEXT *the_tdb, TDB_DATA kbuf,
+ TDB_DATA dbuf, void *params)
+{
+ int len;
+ fstring key;
+ struct _check_primary_grp *check = (struct _check_primary_grp*)params;
+
+ fstr_sprintf( key, "%s/NAME", WBKEY_PASSWD );
+ len = strlen(key);
+
+ /* if this is a group entry then, check the members */
+
+ if ( (strncmp(kbuf.dptr, key, len) == 0) && dbuf.dptr ) {
+ WINBINDD_PW *pw;;
+
+ if ( !(pw = string2passwd( dbuf.dptr )) ) {
+ DEBUG(0,("isprimarygroup_traverse_fn: Failure to parse [%s]\n",
+ dbuf.dptr));
+ return 0;
+ }
+
+ if ( check->gid == pw->pw_gid ) {
+ check->found = True;
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+
+/**********************************************************************
+**********************************************************************/
+
+static BOOL wb_delete_group( WINBINDD_GR *grp )
+{
+ struct _check_primary_grp check;
+ char *namekey;
+ char *gidkey;
+
+ if ( !account_tdb && !winbindd_accountdb_init() ) {
+ DEBUG(0,("wb_delete_group: Failed to open winbindd account db\n"));
+ return False;
+ }
+
+ /* lock the main entry first */
+
+ namekey = acct_groupkey_byname( grp->gr_name );
+ if ( tdb_lock_bystring(account_tdb, namekey, 0) == -1 ) {
+ DEBUG(0,("wb_delete_group: Failed to lock %s\n", namekey));
+ return False;
+ }
+
+ /* is this group the primary group for any user? If
+ so deny delete */
+
+ check.found = False;
+ tdb_traverse(account_tdb, isprimarygroup_traverse_fn, (void *)&check);
+
+ if ( check.found ) {
+ DEBUG(4,("wb_delete_group: Cannot delete group (%s) since it "
+ "is the primary group for some users\n", grp->gr_name));
+ return False;
+ }
+
+ /* We're clear. Delete the group */
+
+ DEBUG(5,("wb_delete_group: Removing group (%s)\n", grp->gr_name));
+
+ gidkey = acct_groupkey_bygid( grp->gr_gid );
+
+ tdb_delete_bystring( account_tdb, namekey );
+ tdb_delete_bystring( account_tdb, gidkey );
+
+ tdb_unlock_bystring( account_tdb, namekey );
+
+ return True;
+}
+
+/**********************************************************************
+ Create a new "UNIX" user for the system given a username
+**********************************************************************/
+
+enum winbindd_result winbindd_create_user(struct winbindd_cli_state *state)
+{
+ char *user, *group;
+ unid_t id;
+ WINBINDD_PW pw, *pw_check;
+ WINBINDD_GR *wb_grp;
+ struct group *unix_grp;
+ gid_t primary_gid;
+ uint32 flags = state->request.flags;
+ uint32 rid;
+
+ if ( !state->privileged ) {
+ DEBUG(2, ("winbindd_create_user: non-privileged access denied!\n"));
+ return WINBINDD_ERROR;
+ }
+
+ /* Ensure null termination */
+ state->request.data.acct_mgt.username[sizeof(state->request.data.acct_mgt.username)-1]='\0';
+ state->request.data.acct_mgt.groupname[sizeof(state->request.data.acct_mgt.groupname)-1]='\0';
+
+ user = state->request.data.acct_mgt.username;
+ group = state->request.data.acct_mgt.groupname;
+
+ DEBUG(3, ("[%5lu]: create_user: user=>(%s), group=>(%s)\n",
+ (unsigned long)state->pid, user, group));
+
+ if ( (pw_check=wb_getpwnam(user)) != NULL ) {
+ DEBUG(0,("winbindd_create_user: Refusing to create user that already exists (%s)\n",
+ user));
+ return WINBINDD_ERROR;
+ }
+
+
+ if ( !*group )
+ group = lp_template_primary_group();
+
+ /* validate the primary group
+ 1) lookup in local tdb first
+ 2) call getgrnam() as a last resort */
+
+ if ( (wb_grp=wb_getgrnam(group)) != NULL ) {
+ primary_gid = wb_grp->gr_gid;
+ free_winbindd_gr( wb_grp );
+ }
+ else if ( (unix_grp=sys_getgrnam(group)) != NULL ) {
+ primary_gid = unix_grp->gr_gid;
+ }
+ else {
+ DEBUG(2,("winbindd_create_user: Cannot validate gid for group (%s)\n", group));
+ return WINBINDD_ERROR;
+ }
+
+ /* get a new uid */
+
+ if ( !NT_STATUS_IS_OK(idmap_allocate_id( &id, ID_USERID)) ) {
+ DEBUG(0,("winbindd_create_user: idmap_allocate_id() failed!\n"));
+ return WINBINDD_ERROR;
+ }
+
+ /* The substitution of %U and %D in the 'template homedir' is done
+ by lp_string() calling standard_sub_basic(). */
+
+ fstrcpy( current_user_info.smb_name, user );
+ sub_set_smb_name( user );
+ fstrcpy( current_user_info.domain, get_global_sam_name() );
+
+ /* fill in the passwd struct */
+
+ fstrcpy( pw.pw_name, user );
+ fstrcpy( pw.pw_passwd, "x" );
+ fstrcpy( pw.pw_gecos, user);
+ fstrcpy( pw.pw_dir, lp_template_homedir() );
+ fstrcpy( pw.pw_shell, lp_template_shell() );
+
+ pw.pw_uid = id.uid;
+ pw.pw_gid = primary_gid;
+
+ /* store the new entry */
+
+ if ( !wb_storepwnam(&pw) )
+ return WINBINDD_ERROR;
+
+ /* do we need a new RID? */
+
+ if ( flags & WBFLAG_ALLOCATE_RID ) {
+ if ( !NT_STATUS_IS_OK(idmap_allocate_rid(&rid, USER_RID_TYPE)) ) {
+ DEBUG(0,("winbindd_create_user: RID allocation failure! Cannot create user (%s)\n",
+ user));
+ wb_delete_user( &pw );
+
+ return WINBINDD_ERROR;
+ }
+
+ state->response.data.rid = rid;
+ }
+
+ return WINBINDD_OK;
+}
+
+/**********************************************************************
+ Create a new "UNIX" group for the system given a username
+**********************************************************************/
+
+enum winbindd_result winbindd_create_group(struct winbindd_cli_state *state)
+{
+ char *group;
+ unid_t id;
+ WINBINDD_GR grp, *grp_check;
+ uint32 flags = state->request.flags;
+ uint32 rid;
+
+ if ( !state->privileged ) {
+ DEBUG(2, ("winbindd_create_group: non-privileged access denied!\n"));
+ return WINBINDD_ERROR;
+ }
+
+ /* Ensure null termination */
+ state->request.data.acct_mgt.groupname[sizeof(state->request.data.acct_mgt.groupname)-1]='\0';
+ group = state->request.data.acct_mgt.groupname;
+
+ DEBUG(3, ("[%5lu]: create_group: (%s)\n", (unsigned long)state->pid, group));
+
+ if ( (grp_check=wb_getgrnam(group)) != NULL ) {
+ DEBUG(0,("winbindd_create_group: Refusing to create group that already exists (%s)\n",
+ group));
+ return WINBINDD_ERROR;
+ }
+
+ /* get a new gid */
+
+ if ( !NT_STATUS_IS_OK(idmap_allocate_id( &id, ID_GROUPID)) ) {
+ DEBUG(0,("winbindd_create_group: idmap_allocate_id() failed!\n"));
+ return WINBINDD_ERROR;
+ }
+
+ /* fill in the group struct */
+
+ fstrcpy( grp.gr_name, group );
+ fstrcpy( grp.gr_passwd, "*" );
+
+ grp.gr_gid = id.gid;
+ grp.gr_mem = NULL; /* start with no members */
+ grp.num_gr_mem = 0;
+
+ if ( !wb_storegrnam(&grp) )
+ return WINBINDD_ERROR;
+
+ /* do we need a new RID? */
+
+ if ( flags & WBFLAG_ALLOCATE_RID ) {
+ if ( !NT_STATUS_IS_OK(idmap_allocate_rid(&rid, GROUP_RID_TYPE)) ) {
+ DEBUG(0,("winbindd_create_group: RID allocation failure! Cannot create group (%s)\n",
+ group));
+ wb_delete_group( &grp );
+
+ return WINBINDD_ERROR;
+ }
+
+ state->response.data.rid = rid;
+ }
+
+ return WINBINDD_OK;
+}
+
+/**********************************************************************
+ Add a user to the membership for a group.
+**********************************************************************/
+
+enum winbindd_result winbindd_add_user_to_group(struct winbindd_cli_state *state)
+{
+ WINBINDD_PW *pw;
+ WINBINDD_GR *grp;
+ char *user, *group;
+ BOOL ret;
+
+ if ( !state->privileged ) {
+ DEBUG(2, ("winbindd_add_user_to_group: non-privileged access denied!\n"));
+ return WINBINDD_ERROR;
+ }
+
+ /* Ensure null termination */
+ state->request.data.acct_mgt.groupname[sizeof(state->request.data.acct_mgt.groupname)-1]='\0';
+ state->request.data.acct_mgt.username[sizeof(state->request.data.acct_mgt.username)-1]='\0';
+ group = state->request.data.acct_mgt.groupname;
+ user = state->request.data.acct_mgt.username;
+
+ DEBUG(3, ("[%5lu]: add_user_to_group: add %s to %s\n", (unsigned long)state->pid,
+ user, group));
+
+ /* make sure it is a valid user */
+
+ if ( !(pw = wb_getpwnam( user )) ) {
+ DEBUG(4,("winbindd_add_user_to_group: Cannot add a non-existent user\n"));
+ return WINBINDD_ERROR;
+ }
+
+ /* make sure it is a valid group */
+
+ if ( !(grp = wb_getgrnam( group )) ) {
+ DEBUG(4,("winbindd_add_user_to_group: Cannot add a user to a non-extistent group\n"));
+ return WINBINDD_ERROR;
+ }
+
+ if ( !wb_addgrpmember( grp, user ) )
+ return WINBINDD_ERROR;
+
+ ret = wb_storegrnam(grp);
+
+ free_winbindd_gr( grp );
+
+ return ( ret ? WINBINDD_OK : WINBINDD_ERROR );
+}
+
+/**********************************************************************
+ Remove a user from the membership of a group
+**********************************************************************/
+
+enum winbindd_result winbindd_remove_user_from_group(struct winbindd_cli_state *state)
+{
+ WINBINDD_GR *grp;
+ char *user, *group;
+ BOOL ret;
+
+ if ( !state->privileged ) {
+ DEBUG(2, ("winbindd_remove_user_from_group: non-privileged access denied!\n"));
+ return WINBINDD_ERROR;
+ }
+
+ /* Ensure null termination */
+ state->request.data.acct_mgt.groupname[sizeof(state->request.data.acct_mgt.groupname)-1]='\0';
+ state->request.data.acct_mgt.username[sizeof(state->request.data.acct_mgt.username)-1]='\0';
+ group = state->request.data.acct_mgt.groupname;
+ user = state->request.data.acct_mgt.username;
+
+ DEBUG(3, ("[%5lu]: remove_user_from_group: delete %s from %s\n", (unsigned long)state->pid,
+ user, group));
+
+ /* don't worry about checking the username since we're removing it anyways */
+
+ /* make sure it is a valid group */
+
+ if ( !(grp = wb_getgrnam( group )) ) {
+ DEBUG(4,("winbindd_remove_user_from_group: Cannot remove a user from a non-extistent group\n"));
+ return WINBINDD_ERROR;
+ }
+
+ if ( !wb_delgrpmember( grp, user ) )
+ return WINBINDD_ERROR;
+
+ ret = wb_storegrnam(grp);
+
+ free_winbindd_gr( grp );
+
+ return ( ret ? WINBINDD_OK : WINBINDD_ERROR );
+}
+
+/**********************************************************************
+ Set the primary group membership of a user
+**********************************************************************/
+
+enum winbindd_result winbindd_set_user_primary_group(struct winbindd_cli_state *state)
+{
+ WINBINDD_PW *pw;
+ WINBINDD_GR *grp;
+ char *user, *group;
+
+ if ( !state->privileged ) {
+ DEBUG(2, ("winbindd_set_user_primary_group: non-privileged access denied!\n"));
+ return WINBINDD_ERROR;
+ }
+
+ /* Ensure null termination */
+ state->request.data.acct_mgt.groupname[sizeof(state->request.data.acct_mgt.groupname)-1]='\0';
+ state->request.data.acct_mgt.username[sizeof(state->request.data.acct_mgt.username)-1]='\0';
+ group = state->request.data.acct_mgt.groupname;
+ user = state->request.data.acct_mgt.username;
+
+ DEBUG(3, ("[%5lu]: set_user_primary_group: group %s for user %s\n",
+ (unsigned long)state->pid, group, user));
+
+ /* make sure it is a valid user */
+
+ if ( !(pw = wb_getpwnam( user )) ) {
+ DEBUG(4,("winbindd_add_user_to_group: Cannot add a non-existent user\n"));
+ return WINBINDD_ERROR;
+ }
+
+ /* make sure it is a valid group */
+
+ if ( !(grp = wb_getgrnam( group )) ) {
+ DEBUG(4,("winbindd_add_user_to_group: Cannot add a user to a non-extistent group\n"));
+ return WINBINDD_ERROR;
+ }
+
+ pw->pw_gid = grp->gr_gid;
+
+ free_winbindd_gr( grp );
+
+ return ( wb_storepwnam(pw) ? WINBINDD_OK : WINBINDD_ERROR );
+}
+
+/**********************************************************************
+ Delete a user from the winbindd account tdb.
+**********************************************************************/
+
+enum winbindd_result winbindd_delete_user(struct winbindd_cli_state *state)
+{
+ WINBINDD_PW *pw;
+ char *user;
+
+ if ( !state->privileged ) {
+ DEBUG(2, ("winbindd_delete_user: non-privileged access denied!\n"));
+ return WINBINDD_ERROR;
+ }
+
+ /* Ensure null termination */
+ state->request.data.acct_mgt.username[sizeof(state->request.data.acct_mgt.username)-1]='\0';
+ user = state->request.data.acct_mgt.username;
+
+ DEBUG(3, ("[%5lu]: delete_user: %s\n", (unsigned long)state->pid, user));
+
+ /* make sure it is a valid user */
+
+ if ( !(pw = wb_getpwnam( user )) ) {
+ DEBUG(4,("winbindd_delete_user: Cannot delete a non-existent user\n"));
+ return WINBINDD_ERROR;
+ }
+
+ return ( wb_delete_user(pw) ? WINBINDD_OK : WINBINDD_ERROR );
+}
+
+/**********************************************************************
+ Delete a group from winbindd's account tdb.
+**********************************************************************/
+
+enum winbindd_result winbindd_delete_group(struct winbindd_cli_state *state)
+{
+ WINBINDD_GR *grp;
+ char *group;
+ BOOL ret;
+
+ if ( !state->privileged ) {
+ DEBUG(2, ("winbindd_delete_group: non-privileged access denied!\n"));
+ return WINBINDD_ERROR;
+ }
+
+ /* Ensure null termination */
+ state->request.data.acct_mgt.username[sizeof(state->request.data.acct_mgt.groupname)-1]='\0';
+ group = state->request.data.acct_mgt.groupname;
+
+ DEBUG(3, ("[%5lu]: delete_group: %s\n", (unsigned long)state->pid, group));
+
+ /* make sure it is a valid group */
+
+ if ( !(grp = wb_getgrnam( group )) ) {
+ DEBUG(4,("winbindd_delete_group: Cannot delete a non-existent group\n"));
+ return WINBINDD_ERROR;
+ }
+
+ ret = wb_delete_group(grp);
+
+ free_winbindd_gr( grp );
+
+ return ( ret ? WINBINDD_OK : WINBINDD_ERROR );
+}
+
+
+
diff --git a/source/nsswitch/winbindd_ads.c b/source/nsswitch/winbindd_ads.c
new file mode 100644
index 00000000000..cd8b8e0e246
--- /dev/null
+++ b/source/nsswitch/winbindd_ads.c
@@ -0,0 +1,1018 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ Winbind ADS backend functions
+
+ Copyright (C) Andrew Tridgell 2001
+ Copyright (C) Andrew Bartlett <abartlet@samba.org> 2003
+ Copyright (C) Gerald (Jerry) Carter 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 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 "winbindd.h"
+
+#ifdef HAVE_ADS
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_WINBIND
+
+/*
+ return our ads connections structure for a domain. We keep the connection
+ open to make things faster
+*/
+static ADS_STRUCT *ads_cached_connection(struct winbindd_domain *domain)
+{
+ ADS_STRUCT *ads;
+ ADS_STATUS status;
+
+ if (domain->private) {
+ ads = (ADS_STRUCT *)domain->private;
+
+ /* check for a valid structure */
+
+ DEBUG(7, ("Current tickets expire at %d\n, time is now %d\n",
+ (uint32) ads->auth.expire, (uint32) time(NULL)));
+ if ( ads->config.realm && (ads->auth.expire > time(NULL))) {
+ return ads;
+ }
+ else {
+ /* we own this ADS_STRUCT so make sure it goes away */
+ ads->is_mine = True;
+ ads_destroy( &ads );
+ ads_kdestroy("MEMORY:winbind_ccache");
+ domain->private = NULL;
+ }
+ }
+
+ /* we don't want this to affect the users ccache */
+ setenv("KRB5CCNAME", "MEMORY:winbind_ccache", 1);
+
+ ads = ads_init(domain->alt_name, domain->name, NULL);
+ if (!ads) {
+ DEBUG(1,("ads_init for domain %s failed\n", domain->name));
+ return NULL;
+ }
+
+ /* the machine acct password might have change - fetch it every time */
+ SAFE_FREE(ads->auth.password);
+ ads->auth.password = secrets_fetch_machine_password(lp_workgroup(), NULL, NULL);
+
+ SAFE_FREE(ads->auth.realm);
+ ads->auth.realm = strdup(lp_realm());
+
+ status = ads_connect(ads);
+ if (!ADS_ERR_OK(status) || !ads->config.realm) {
+ extern struct winbindd_methods msrpc_methods, cache_methods;
+ DEBUG(1,("ads_connect for domain %s failed: %s\n",
+ domain->name, ads_errstr(status)));
+ ads_destroy(&ads);
+
+ /* if we get ECONNREFUSED then it might be a NT4
+ server, fall back to MSRPC */
+ if (status.error_type == ENUM_ADS_ERROR_SYSTEM &&
+ status.err.rc == ECONNREFUSED) {
+ DEBUG(1,("Trying MSRPC methods\n"));
+ if (domain->methods == &cache_methods) {
+ domain->backend = &msrpc_methods;
+ } else {
+ domain->methods = &msrpc_methods;
+ }
+ }
+ return NULL;
+ }
+
+ /* set the flag that says we don't own the memory even
+ though we do so that ads_destroy() won't destroy the
+ structure we pass back by reference */
+
+ ads->is_mine = False;
+
+ domain->private = (void *)ads;
+ return ads;
+}
+
+
+/* Query display info for a realm. This is the basic user list fn */
+static NTSTATUS query_user_list(struct winbindd_domain *domain,
+ TALLOC_CTX *mem_ctx,
+ uint32 *num_entries,
+ WINBIND_USERINFO **info)
+{
+ ADS_STRUCT *ads = NULL;
+ const char *attrs[] = {"userPrincipalName",
+ "sAMAccountName",
+ "name", "objectSid", "primaryGroupID",
+ "sAMAccountType", NULL};
+ int i, count;
+ ADS_STATUS rc;
+ void *res = NULL;
+ void *msg = NULL;
+ NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
+
+ *num_entries = 0;
+
+ DEBUG(3,("ads: query_user_list\n"));
+
+ ads = ads_cached_connection(domain);
+
+ if (!ads) {
+ domain->last_status = NT_STATUS_SERVER_DISABLED;
+ goto done;
+ }
+
+ rc = ads_search_retry(ads, &res, "(objectClass=user)", attrs);
+ if (!ADS_ERR_OK(rc) || !res) {
+ DEBUG(1,("query_user_list ads_search: %s\n", ads_errstr(rc)));
+ goto done;
+ }
+
+ count = ads_count_replies(ads, res);
+ if (count == 0) {
+ DEBUG(1,("query_user_list: No users found\n"));
+ goto done;
+ }
+
+ (*info) = talloc_zero(mem_ctx, count * sizeof(**info));
+ if (!*info) {
+ status = NT_STATUS_NO_MEMORY;
+ goto done;
+ }
+
+ i = 0;
+
+ for (msg = ads_first_entry(ads, res); msg; msg = ads_next_entry(ads, msg)) {
+ char *name, *gecos;
+ DOM_SID sid;
+ DOM_SID *sid2;
+ DOM_SID *group_sid;
+ uint32 group;
+ uint32 atype;
+
+ if (!ads_pull_uint32(ads, msg, "sAMAccountType", &atype) ||
+ ads_atype_map(atype) != SID_NAME_USER) {
+ DEBUG(1,("Not a user account? atype=0x%x\n", atype));
+ continue;
+ }
+
+ name = ads_pull_username(ads, mem_ctx, msg);
+ gecos = ads_pull_string(ads, mem_ctx, msg, "name");
+ if (!ads_pull_sid(ads, msg, "objectSid", &sid)) {
+ DEBUG(1,("No sid for %s !?\n", name));
+ continue;
+ }
+ if (!ads_pull_uint32(ads, msg, "primaryGroupID", &group)) {
+ DEBUG(1,("No primary group for %s !?\n", name));
+ continue;
+ }
+
+ sid2 = talloc(mem_ctx, sizeof(*sid2));
+ if (!sid2) {
+ status = NT_STATUS_NO_MEMORY;
+ goto done;
+ }
+
+ sid_copy(sid2, &sid);
+
+ group_sid = rid_to_talloced_sid(domain, mem_ctx, group);
+
+ (*info)[i].acct_name = name;
+ (*info)[i].full_name = gecos;
+ (*info)[i].user_sid = sid2;
+ (*info)[i].group_sid = group_sid;
+ i++;
+ }
+
+ (*num_entries) = i;
+ status = NT_STATUS_OK;
+
+ DEBUG(3,("ads query_user_list gave %d entries\n", (*num_entries)));
+
+done:
+ if (res)
+ ads_msgfree(ads, res);
+
+ return status;
+}
+
+/* list all domain groups */
+static NTSTATUS enum_dom_groups(struct winbindd_domain *domain,
+ TALLOC_CTX *mem_ctx,
+ uint32 *num_entries,
+ struct acct_info **info)
+{
+ ADS_STRUCT *ads = NULL;
+ const char *attrs[] = {"userPrincipalName", "sAMAccountName",
+ "name", "objectSid",
+ "sAMAccountType", NULL};
+ int i, count;
+ ADS_STATUS rc;
+ void *res = NULL;
+ void *msg = NULL;
+ NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
+ uint32 group_flags;
+
+ *num_entries = 0;
+
+ DEBUG(3,("ads: enum_dom_groups\n"));
+
+ ads = ads_cached_connection(domain);
+
+ if (!ads) {
+ domain->last_status = NT_STATUS_SERVER_DISABLED;
+ goto done;
+ }
+
+ rc = ads_search_retry(ads, &res, "(objectCategory=group)", attrs);
+ if (!ADS_ERR_OK(rc) || !res) {
+ DEBUG(1,("enum_dom_groups ads_search: %s\n", ads_errstr(rc)));
+ goto done;
+ }
+
+ count = ads_count_replies(ads, res);
+ if (count == 0) {
+ DEBUG(1,("enum_dom_groups: No groups found\n"));
+ goto done;
+ }
+
+ (*info) = talloc_zero(mem_ctx, count * sizeof(**info));
+ if (!*info) {
+ status = NT_STATUS_NO_MEMORY;
+ goto done;
+ }
+
+ i = 0;
+
+ group_flags = ATYPE_GLOBAL_GROUP;
+
+ /* only grab domain local groups for our domain */
+ if ( domain->native_mode && strequal(lp_realm(), domain->alt_name) )
+ group_flags |= ATYPE_LOCAL_GROUP;
+
+ for (msg = ads_first_entry(ads, res); msg; msg = ads_next_entry(ads, msg)) {
+ char *name, *gecos;
+ DOM_SID sid;
+ uint32 rid;
+ uint32 account_type;
+
+ if (!ads_pull_uint32(ads, msg, "sAMAccountType", &account_type) || !(account_type & group_flags) )
+ continue;
+
+ name = ads_pull_username(ads, mem_ctx, msg);
+ gecos = ads_pull_string(ads, mem_ctx, msg, "name");
+ if (!ads_pull_sid(ads, msg, "objectSid", &sid)) {
+ DEBUG(1,("No sid for %s !?\n", name));
+ continue;
+ }
+
+ if (!sid_peek_check_rid(&domain->sid, &sid, &rid)) {
+ DEBUG(1,("No rid for %s !?\n", name));
+ continue;
+ }
+
+ fstrcpy((*info)[i].acct_name, name);
+ fstrcpy((*info)[i].acct_desc, gecos);
+ (*info)[i].rid = rid;
+ i++;
+ }
+
+ (*num_entries) = i;
+
+ status = NT_STATUS_OK;
+
+ DEBUG(3,("ads enum_dom_groups gave %d entries\n", (*num_entries)));
+
+done:
+ if (res)
+ ads_msgfree(ads, res);
+
+ return status;
+}
+
+/* list all domain local groups */
+static NTSTATUS enum_local_groups(struct winbindd_domain *domain,
+ TALLOC_CTX *mem_ctx,
+ uint32 *num_entries,
+ struct acct_info **info)
+{
+ /*
+ * This is a stub function only as we returned the domain
+ * local groups in enum_dom_groups() if the domain->native field
+ * was true. This is a simple performance optimization when
+ * using LDAP.
+ *
+ * if we ever need to enumerate domain local groups separately,
+ * then this the optimization in enum_dom_groups() will need
+ * to be split out
+ */
+ *num_entries = 0;
+
+ return NT_STATUS_OK;
+}
+
+/* convert a single name to a sid in a domain */
+static NTSTATUS name_to_sid(struct winbindd_domain *domain,
+ TALLOC_CTX *mem_ctx,
+ const char *name,
+ DOM_SID *sid,
+ enum SID_NAME_USE *type)
+{
+ ADS_STRUCT *ads;
+
+ DEBUG(3,("ads: name_to_sid\n"));
+
+ ads = ads_cached_connection(domain);
+
+ if (!ads) {
+ domain->last_status = NT_STATUS_SERVER_DISABLED;
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ return ads_name_to_sid(ads, name, sid, type);
+}
+
+/* convert a sid to a user or group name */
+static NTSTATUS sid_to_name(struct winbindd_domain *domain,
+ TALLOC_CTX *mem_ctx,
+ const DOM_SID *sid,
+ char **name,
+ enum SID_NAME_USE *type)
+{
+ ADS_STRUCT *ads = NULL;
+ DEBUG(3,("ads: sid_to_name\n"));
+
+ ads = ads_cached_connection(domain);
+
+ if (!ads) {
+ domain->last_status = NT_STATUS_SERVER_DISABLED;
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ return ads_sid_to_name(ads, mem_ctx, sid, name, type);
+}
+
+
+/* convert a DN to a name, SID and name type
+ this might become a major speed bottleneck if groups have
+ lots of users, in which case we could cache the results
+*/
+static BOOL dn_lookup(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
+ const char *dn,
+ char **name, uint32 *name_type, DOM_SID *sid)
+{
+ void *res = NULL;
+ const char *attrs[] = {"userPrincipalName", "sAMAccountName",
+ "objectSid", "sAMAccountType", NULL};
+ ADS_STATUS rc;
+ uint32 atype;
+ DEBUG(3,("ads: dn_lookup\n"));
+
+ rc = ads_search_retry_dn(ads, &res, dn, attrs);
+
+ if (!ADS_ERR_OK(rc) || !res) {
+ goto failed;
+ }
+
+ (*name) = ads_pull_username(ads, mem_ctx, res);
+
+ if (!ads_pull_uint32(ads, res, "sAMAccountType", &atype)) {
+ goto failed;
+ }
+ (*name_type) = ads_atype_map(atype);
+
+ if (!ads_pull_sid(ads, res, "objectSid", sid)) {
+ goto failed;
+ }
+
+ if (res)
+ ads_msgfree(ads, res);
+
+ return True;
+
+failed:
+ if (res)
+ ads_msgfree(ads, res);
+
+ return False;
+}
+
+/* Lookup user information from a rid */
+static NTSTATUS query_user(struct winbindd_domain *domain,
+ TALLOC_CTX *mem_ctx,
+ const DOM_SID *sid,
+ WINBIND_USERINFO *info)
+{
+ ADS_STRUCT *ads = NULL;
+ const char *attrs[] = {"userPrincipalName",
+ "sAMAccountName",
+ "name",
+ "primaryGroupID", NULL};
+ ADS_STATUS rc;
+ int count;
+ void *msg = NULL;
+ char *ldap_exp;
+ char *sidstr;
+ uint32 group_rid;
+ NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
+ DOM_SID *sid2;
+ fstring sid_string;
+
+ DEBUG(3,("ads: query_user\n"));
+
+ ads = ads_cached_connection(domain);
+
+ if (!ads) {
+ domain->last_status = NT_STATUS_SERVER_DISABLED;
+ goto done;
+ }
+
+ sidstr = sid_binstring(sid);
+ asprintf(&ldap_exp, "(objectSid=%s)", sidstr);
+ rc = ads_search_retry(ads, &msg, ldap_exp, attrs);
+ free(ldap_exp);
+ free(sidstr);
+ if (!ADS_ERR_OK(rc) || !msg) {
+ DEBUG(1,("query_user(sid=%s) ads_search: %s\n", sid_to_string(sid_string, sid), ads_errstr(rc)));
+ goto done;
+ }
+
+ count = ads_count_replies(ads, msg);
+ if (count != 1) {
+ DEBUG(1,("query_user(sid=%s): Not found\n", sid_to_string(sid_string, sid)));
+ goto done;
+ }
+
+ info->acct_name = ads_pull_username(ads, mem_ctx, msg);
+ info->full_name = ads_pull_string(ads, mem_ctx, msg, "name");
+
+ if (!ads_pull_uint32(ads, msg, "primaryGroupID", &group_rid)) {
+ DEBUG(1,("No primary group for %s !?\n", sid_to_string(sid_string, sid)));
+ goto done;
+ }
+
+ sid2 = talloc(mem_ctx, sizeof(*sid2));
+ if (!sid2) {
+ status = NT_STATUS_NO_MEMORY;
+ goto done;
+ }
+ sid_copy(sid2, sid);
+
+ info->user_sid = sid2;
+
+ info->group_sid = rid_to_talloced_sid(domain, mem_ctx, group_rid);
+
+ status = NT_STATUS_OK;
+
+ DEBUG(3,("ads query_user gave %s\n", info->acct_name));
+done:
+ if (msg)
+ ads_msgfree(ads, msg);
+
+ return status;
+}
+
+/* Lookup groups a user is a member of - alternate method, for when
+ tokenGroups are not available. */
+static NTSTATUS lookup_usergroups_alt(struct winbindd_domain *domain,
+ TALLOC_CTX *mem_ctx,
+ const char *user_dn,
+ DOM_SID *primary_group,
+ uint32 *num_groups, DOM_SID ***user_gids)
+{
+ ADS_STATUS rc;
+ NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
+ int count;
+ void *res = NULL;
+ void *msg = NULL;
+ char *ldap_exp;
+ ADS_STRUCT *ads;
+ const char *group_attrs[] = {"objectSid", NULL};
+ char *escaped_dn;
+
+ DEBUG(3,("ads: lookup_usergroups_alt\n"));
+
+ ads = ads_cached_connection(domain);
+
+ if (!ads) {
+ domain->last_status = NT_STATUS_SERVER_DISABLED;
+ goto done;
+ }
+
+ if (!(escaped_dn = escape_ldap_string_alloc(user_dn))) {
+ status = NT_STATUS_NO_MEMORY;
+ goto done;
+ }
+
+ /* buggy server, no tokenGroups. Instead lookup what groups this user
+ is a member of by DN search on member*/
+
+ if (!(ldap_exp = talloc_asprintf(mem_ctx, "(&(member=%s)(objectClass=group))", escaped_dn))) {
+ DEBUG(1,("lookup_usergroups(dn=%s) asprintf failed!\n", user_dn));
+ SAFE_FREE(escaped_dn);
+ status = NT_STATUS_NO_MEMORY;
+ goto done;
+ }
+
+ SAFE_FREE(escaped_dn);
+
+ rc = ads_search_retry(ads, &res, ldap_exp, group_attrs);
+
+ if (!ADS_ERR_OK(rc) || !res) {
+ DEBUG(1,("lookup_usergroups ads_search member=%s: %s\n", user_dn, ads_errstr(rc)));
+ return ads_ntstatus(rc);
+ }
+
+ count = ads_count_replies(ads, res);
+ if (count == 0) {
+ DEBUG(5,("lookup_usergroups: No supp groups found\n"));
+
+ status = ads_ntstatus(rc);
+ goto done;
+ }
+
+ (*user_gids) = talloc_zero(mem_ctx, sizeof(**user_gids) * (count + 1));
+ (*user_gids)[0] = primary_group;
+
+ *num_groups = 1;
+
+ for (msg = ads_first_entry(ads, res); msg; msg = ads_next_entry(ads, msg)) {
+ DOM_SID group_sid;
+
+ if (!ads_pull_sid(ads, msg, "objectSid", &group_sid)) {
+ DEBUG(1,("No sid for this group ?!?\n"));
+ continue;
+ }
+
+ if (sid_equal(&group_sid, primary_group)) continue;
+
+ (*user_gids)[*num_groups] = talloc(mem_ctx, sizeof(***user_gids));
+ if (!(*user_gids)[*num_groups]) {
+ status = NT_STATUS_NO_MEMORY;
+ goto done;
+ }
+
+ sid_copy((*user_gids)[*num_groups], &group_sid);
+
+ (*num_groups)++;
+
+ }
+
+ status = NT_STATUS_OK;
+
+ DEBUG(3,("ads lookup_usergroups (alt) for dn=%s\n", user_dn));
+done:
+ if (res)
+ ads_msgfree(ads, res);
+
+ return status;
+}
+
+/* Lookup groups a user is a member of. */
+static NTSTATUS lookup_usergroups(struct winbindd_domain *domain,
+ TALLOC_CTX *mem_ctx,
+ const DOM_SID *sid,
+ uint32 *num_groups, DOM_SID ***user_gids)
+{
+ ADS_STRUCT *ads = NULL;
+ const char *attrs[] = {"tokenGroups", "primaryGroupID", NULL};
+ ADS_STATUS rc;
+ int count;
+ LDAPMessage *msg = NULL;
+ char *user_dn;
+ DOM_SID *sids;
+ int i;
+ DOM_SID *primary_group;
+ uint32 primary_group_rid;
+ fstring sid_string;
+ NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
+
+ DEBUG(3,("ads: lookup_usergroups\n"));
+ *num_groups = 0;
+
+ ads = ads_cached_connection(domain);
+
+ if (!ads) {
+ domain->last_status = NT_STATUS_SERVER_DISABLED;
+ status = NT_STATUS_SERVER_DISABLED;
+ goto done;
+ }
+
+ rc = ads_sid_to_dn(ads, mem_ctx, sid, &user_dn);
+ if (!ADS_ERR_OK(rc)) {
+ status = ads_ntstatus(rc);
+ goto done;
+ }
+
+ rc = ads_search_retry_dn(ads, (void**)&msg, user_dn, attrs);
+ if (!ADS_ERR_OK(rc)) {
+ status = ads_ntstatus(rc);
+ DEBUG(1,("lookup_usergroups(sid=%s) ads_search tokenGroups: %s\n",
+ sid_to_string(sid_string, sid), ads_errstr(rc)));
+ goto done;
+ }
+
+ if (!msg) {
+ DEBUG(1,("lookup_usergroups(sid=%s) ads_search tokenGroups: NULL msg\n",
+ sid_to_string(sid_string, sid)));
+ status = NT_STATUS_UNSUCCESSFUL;
+ goto done;
+ }
+
+ if (!ads_pull_uint32(ads, msg, "primaryGroupID", &primary_group_rid)) {
+ DEBUG(1,("%s: No primary group for sid=%s !?\n",
+ domain->name, sid_to_string(sid_string, sid)));
+ goto done;
+ }
+
+ primary_group = rid_to_talloced_sid(domain, mem_ctx, primary_group_rid);
+
+ count = ads_pull_sids(ads, mem_ctx, msg, "tokenGroups", &sids);
+
+ if (msg)
+ ads_msgfree(ads, msg);
+
+ /* there must always be at least one group in the token,
+ unless we are talking to a buggy Win2k server */
+ if (count == 0) {
+ return lookup_usergroups_alt(domain, mem_ctx, user_dn,
+ primary_group,
+ num_groups, user_gids);
+ }
+
+ (*user_gids) = talloc_zero(mem_ctx, sizeof(**user_gids) * (count + 1));
+ (*user_gids)[0] = primary_group;
+
+ *num_groups = 1;
+
+ for (i=0;i<count;i++) {
+ if (sid_equal(&sids[i], primary_group)) continue;
+
+ (*user_gids)[*num_groups] = talloc(mem_ctx, sizeof(***user_gids));
+ if (!(*user_gids)[*num_groups]) {
+ status = NT_STATUS_NO_MEMORY;
+ goto done;
+ }
+
+ sid_copy((*user_gids)[*num_groups], &sids[i]);
+ (*num_groups)++;
+ }
+
+ status = NT_STATUS_OK;
+ DEBUG(3,("ads lookup_usergroups for sid=%s\n", sid_to_string(sid_string, sid)));
+done:
+ return status;
+}
+
+/*
+ find the members of a group, given a group rid and domain
+ */
+static NTSTATUS lookup_groupmem(struct winbindd_domain *domain,
+ TALLOC_CTX *mem_ctx,
+ const DOM_SID *group_sid, uint32 *num_names,
+ DOM_SID ***sid_mem, char ***names,
+ uint32 **name_types)
+{
+ ADS_STATUS rc;
+ int count;
+ void *res=NULL;
+ ADS_STRUCT *ads = NULL;
+ char *ldap_exp;
+ NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
+ char *sidstr;
+ char **members;
+ int i, num_members;
+ fstring sid_string;
+ BOOL more_values;
+ const char **attrs;
+ uint32 first_usn;
+ uint32 current_usn;
+ int num_retries = 0;
+
+ DEBUG(10,("ads: lookup_groupmem %s sid=%s\n", domain->name,
+ sid_string_static(group_sid)));
+
+ *num_names = 0;
+
+ ads = ads_cached_connection(domain);
+
+ if (!ads) {
+ domain->last_status = NT_STATUS_SERVER_DISABLED;
+ goto done;
+ }
+
+ sidstr = sid_binstring(group_sid);
+
+ /* search for all members of the group */
+ if (!(ldap_exp = talloc_asprintf(mem_ctx, "(objectSid=%s)",sidstr))) {
+ SAFE_FREE(sidstr);
+ DEBUG(1, ("ads: lookup_groupmem: tallloc_asprintf for ldap_exp failed!\n"));
+ status = NT_STATUS_NO_MEMORY;
+ goto done;
+ }
+ SAFE_FREE(sidstr);
+
+ members = NULL;
+ num_members = 0;
+
+ attrs = talloc(mem_ctx, 3 * sizeof(*attrs));
+ attrs[1] = talloc_strdup(mem_ctx, "usnChanged");
+ attrs[2] = NULL;
+
+ do {
+ if (num_members == 0)
+ attrs[0] = talloc_strdup(mem_ctx, "member");
+
+ DEBUG(10, ("Searching for attrs[0] = %s, attrs[1] = %s\n", attrs[0], attrs[1]));
+
+ rc = ads_search_retry(ads, &res, ldap_exp, attrs);
+
+ if (!ADS_ERR_OK(rc) || !res) {
+ DEBUG(1,("ads: lookup_groupmem ads_search: %s\n",
+ ads_errstr(rc)));
+ status = ads_ntstatus(rc);
+ goto done;
+ }
+
+ count = ads_count_replies(ads, res);
+ if (count == 0)
+ break;
+
+ if (num_members == 0) {
+ if (!ads_pull_uint32(ads, res, "usnChanged", &first_usn)) {
+ DEBUG(1, ("ads: lookup_groupmem could not pull usnChanged!\n"));
+ goto done;
+ }
+ }
+
+ if (!ads_pull_uint32(ads, res, "usnChanged", &current_usn)) {
+ DEBUG(1, ("ads: lookup_groupmem could not pull usnChanged!\n"));
+ goto done;
+ }
+
+ if (first_usn != current_usn) {
+ DEBUG(5, ("ads: lookup_groupmem USN on this record changed"
+ " - restarting search\n"));
+ if (num_retries < 5) {
+ num_retries++;
+ num_members = 0;
+ continue;
+ } else {
+ DEBUG(5, ("ads: lookup_groupmem USN on this record changed"
+ " - restarted search too many times, aborting!\n"));
+ status = NT_STATUS_UNSUCCESSFUL;
+ goto done;
+ }
+ }
+
+ members = ads_pull_strings_range(ads, mem_ctx, res,
+ "member",
+ members,
+ &attrs[0],
+ &num_members,
+ &more_values);
+
+ if ((members == NULL) || (num_members == 0))
+ break;
+
+ } while (more_values);
+
+ /* now we need to turn a list of members into rids, names and name types
+ the problem is that the members are in the form of distinguised names
+ */
+
+ (*sid_mem) = talloc_zero(mem_ctx, sizeof(**sid_mem) * num_members);
+ (*name_types) = talloc_zero(mem_ctx, sizeof(**name_types) * num_members);
+ (*names) = talloc_zero(mem_ctx, sizeof(**names) * num_members);
+
+ for (i=0;i<num_members;i++) {
+ uint32 name_type;
+ char *name;
+ DOM_SID sid;
+
+ if (dn_lookup(ads, mem_ctx, members[i], &name, &name_type, &sid)) {
+ (*names)[*num_names] = name;
+ (*name_types)[*num_names] = name_type;
+ (*sid_mem)[*num_names] = talloc(mem_ctx, sizeof(***sid_mem));
+ if (!(*sid_mem)[*num_names]) {
+ status = NT_STATUS_NO_MEMORY;
+ goto done;
+ }
+ sid_copy((*sid_mem)[*num_names], &sid);
+ (*num_names)++;
+ }
+ }
+
+ status = NT_STATUS_OK;
+ DEBUG(3,("ads lookup_groupmem for sid=%s\n", sid_to_string(sid_string, group_sid)));
+done:
+
+ if (res)
+ ads_msgfree(ads, res);
+
+ return status;
+}
+
+/* find the sequence number for a domain */
+static NTSTATUS sequence_number(struct winbindd_domain *domain, uint32 *seq)
+{
+ ADS_STRUCT *ads = NULL;
+ ADS_STATUS rc;
+
+ DEBUG(3,("ads: fetch sequence_number for %s\n", domain->name));
+
+ *seq = DOM_SEQUENCE_NONE;
+
+ ads = ads_cached_connection(domain);
+
+ if (!ads) {
+ domain->last_status = NT_STATUS_SERVER_DISABLED;
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ rc = ads_USN(ads, seq);
+
+ if (!ADS_ERR_OK(rc)) {
+
+ /* its a dead connection ; don't destroy it
+ through since ads_USN() has already done
+ that indirectly */
+
+ domain->private = NULL;
+ }
+ return ads_ntstatus(rc);
+}
+
+/* get a list of trusted domains */
+static NTSTATUS trusted_domains(struct winbindd_domain *domain,
+ TALLOC_CTX *mem_ctx,
+ uint32 *num_domains,
+ char ***names,
+ char ***alt_names,
+ DOM_SID **dom_sids)
+{
+ NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+ struct ds_domain_trust *domains = NULL;
+ int count = 0;
+ int i;
+ struct cli_state *cli = NULL;
+ /* i think we only need our forest and downlevel trusted domains */
+ uint32 flags = DS_DOMAIN_IN_FOREST | DS_DOMAIN_DIRECT_OUTBOUND;
+
+ DEBUG(3,("ads: trusted_domains\n"));
+
+ *num_domains = 0;
+ *alt_names = NULL;
+ *names = NULL;
+ *dom_sids = NULL;
+
+ if ( !NT_STATUS_IS_OK(result = cm_fresh_connection(domain, PI_NETLOGON, &cli)) ) {
+ DEBUG(5, ("trusted_domains: Could not open a connection to %s for PIPE_NETLOGON (%s)\n",
+ domain->name, nt_errstr(result)));
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ if ( NT_STATUS_IS_OK(result) )
+ result = cli_ds_enum_domain_trusts( cli, mem_ctx, cli->desthost,
+ flags, &domains, (unsigned int *)&count );
+
+ if ( NT_STATUS_IS_OK(result) && count) {
+
+ /* Allocate memory for trusted domain names and sids */
+
+ if ( !(*names = (char **)talloc(mem_ctx, sizeof(char *) * count)) ) {
+ DEBUG(0, ("trusted_domains: out of memory\n"));
+ result = NT_STATUS_NO_MEMORY;
+ goto done;
+ }
+
+ if ( !(*alt_names = (char **)talloc(mem_ctx, sizeof(char *) * count)) ) {
+ DEBUG(0, ("trusted_domains: out of memory\n"));
+ result = NT_STATUS_NO_MEMORY;
+ goto done;
+ }
+
+ if ( !(*dom_sids = (DOM_SID *)talloc(mem_ctx, sizeof(DOM_SID) * count)) ) {
+ DEBUG(0, ("trusted_domains: out of memory\n"));
+ result = NT_STATUS_NO_MEMORY;
+ goto done;
+ }
+
+ /* Copy across names and sids */
+
+ for (i = 0; i < count; i++) {
+ (*names)[i] = domains[i].netbios_domain;
+ (*alt_names)[i] = domains[i].dns_domain;
+
+ sid_copy(&(*dom_sids)[i], &domains[i].sid);
+ }
+
+ *num_domains = count;
+ }
+
+done:
+
+ /* remove connection; This is a special case to the \NETLOGON pipe */
+
+ if ( cli )
+ cli_shutdown( cli );
+
+ return result;
+}
+
+/* find the domain sid for a domain */
+static NTSTATUS domain_sid(struct winbindd_domain *domain, DOM_SID *sid)
+{
+ ADS_STRUCT *ads;
+ ADS_STATUS rc;
+
+ DEBUG(3,("ads: domain_sid\n"));
+
+ ads = ads_cached_connection(domain);
+
+ if (!ads) {
+ domain->last_status = NT_STATUS_SERVER_DISABLED;
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ rc = ads_domain_sid(ads, sid);
+
+ if (!ADS_ERR_OK(rc)) {
+
+ /* its a dead connection; don't destroy it though
+ since that has already been done indirectly
+ by ads_domain_sid() */
+
+ domain->private = NULL;
+ }
+
+ return ads_ntstatus(rc);
+}
+
+
+/* find alternate names list for the domain - for ADS this is the
+ netbios name */
+static NTSTATUS alternate_name(struct winbindd_domain *domain)
+{
+ ADS_STRUCT *ads;
+ ADS_STATUS rc;
+ TALLOC_CTX *ctx;
+ const char *workgroup;
+
+ DEBUG(3,("ads: alternate_name\n"));
+
+ ads = ads_cached_connection(domain);
+
+ if (!ads) {
+ domain->last_status = NT_STATUS_SERVER_DISABLED;
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ if (!(ctx = talloc_init("alternate_name"))) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ rc = ads_workgroup_name(ads, ctx, &workgroup);
+
+ if (ADS_ERR_OK(rc)) {
+ fstrcpy(domain->name, workgroup);
+ fstrcpy(domain->alt_name, ads->config.realm);
+ strupper_m(domain->alt_name);
+ strupper_m(domain->name);
+ }
+
+ talloc_destroy(ctx);
+
+ return ads_ntstatus(rc);
+}
+
+/* the ADS backend methods are exposed via this structure */
+struct winbindd_methods ads_methods = {
+ True,
+ query_user_list,
+ enum_dom_groups,
+ enum_local_groups,
+ name_to_sid,
+ sid_to_name,
+ query_user,
+ lookup_usergroups,
+ lookup_groupmem,
+ sequence_number,
+ trusted_domains,
+ domain_sid,
+ alternate_name
+};
+
+#endif
diff --git a/source/nsswitch/winbindd_cache.c b/source/nsswitch/winbindd_cache.c
new file mode 100644
index 00000000000..73918d74f7d
--- /dev/null
+++ b/source/nsswitch/winbindd_cache.c
@@ -0,0 +1,1377 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ Winbind cache backend functions
+
+ Copyright (C) Andrew Tridgell 2001
+ Copyright (C) Gerald Carter 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 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 "winbindd.h"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_WINBIND
+
+struct winbind_cache {
+ TDB_CONTEXT *tdb;
+};
+
+struct cache_entry {
+ NTSTATUS status;
+ uint32 sequence_number;
+ uint8 *data;
+ uint32 len, ofs;
+};
+
+#define WINBINDD_MAX_CACHE_SIZE (50*1024*1024)
+
+static struct winbind_cache *wcache;
+
+/* flush the cache */
+void wcache_flush_cache(void)
+{
+ extern BOOL opt_nocache;
+
+ if (!wcache)
+ return;
+ if (wcache->tdb) {
+ tdb_close(wcache->tdb);
+ wcache->tdb = NULL;
+ }
+ if (opt_nocache)
+ return;
+
+ wcache->tdb = tdb_open_log(lock_path("winbindd_cache.tdb"), 5000,
+ TDB_CLEAR_IF_FIRST, O_RDWR|O_CREAT, 0600);
+
+ if (!wcache->tdb) {
+ DEBUG(0,("Failed to open winbindd_cache.tdb!\n"));
+ }
+ DEBUG(10,("wcache_flush_cache success\n"));
+}
+
+void winbindd_check_cache_size(time_t t)
+{
+ static time_t last_check_time;
+ struct stat st;
+
+ if (last_check_time == (time_t)0)
+ last_check_time = t;
+
+ if (t - last_check_time < 60 && t - last_check_time > 0)
+ return;
+
+ if (wcache == NULL || wcache->tdb == NULL) {
+ DEBUG(0, ("Unable to check size of tdb cache - cache not open !\n"));
+ return;
+ }
+
+ if (fstat(wcache->tdb->fd, &st) == -1) {
+ DEBUG(0, ("Unable to check size of tdb cache %s!\n", strerror(errno) ));
+ return;
+ }
+
+ if (st.st_size > WINBINDD_MAX_CACHE_SIZE) {
+ DEBUG(10,("flushing cache due to size (%lu) > (%lu)\n",
+ (unsigned long)st.st_size,
+ (unsigned long)WINBINDD_MAX_CACHE_SIZE));
+ wcache_flush_cache();
+ }
+}
+
+/* get the winbind_cache structure */
+static struct winbind_cache *get_cache(struct winbindd_domain *domain)
+{
+ struct winbind_cache *ret = wcache;
+
+ if (!domain->backend) {
+ extern struct winbindd_methods msrpc_methods;
+ switch (lp_security()) {
+#ifdef HAVE_ADS
+ case SEC_ADS: {
+ extern struct winbindd_methods ads_methods;
+ /* always obey the lp_security parameter for our domain */
+ if (domain->primary) {
+ domain->backend = &ads_methods;
+ break;
+ }
+
+ /* only use ADS for native modes at the momment.
+ The problem is the correct detection of mixed
+ mode domains from NT4 BDC's --jerry */
+
+ if ( domain->native_mode ) {
+ DEBUG(5,("get_cache: Setting ADS methods for domain %s\n",
+ domain->name));
+ domain->backend = &ads_methods;
+ break;
+ }
+
+ /* fall through */
+ }
+#endif
+ default:
+ DEBUG(5,("get_cache: Setting MS-RPC methods for domain %s\n",
+ domain->name));
+ domain->backend = &msrpc_methods;
+ }
+ }
+
+ if (ret)
+ return ret;
+
+ ret = smb_xmalloc(sizeof(*ret));
+ ZERO_STRUCTP(ret);
+
+ wcache = ret;
+ wcache_flush_cache();
+
+ return ret;
+}
+
+/*
+ free a centry structure
+*/
+static void centry_free(struct cache_entry *centry)
+{
+ if (!centry)
+ return;
+ SAFE_FREE(centry->data);
+ free(centry);
+}
+
+/*
+ pull a uint32 from a cache entry
+*/
+static uint32 centry_uint32(struct cache_entry *centry)
+{
+ uint32 ret;
+ if (centry->len - centry->ofs < 4) {
+ DEBUG(0,("centry corruption? needed 4 bytes, have %d\n",
+ centry->len - centry->ofs));
+ smb_panic("centry_uint32");
+ }
+ ret = IVAL(centry->data, centry->ofs);
+ centry->ofs += 4;
+ return ret;
+}
+
+/*
+ pull a uint8 from a cache entry
+*/
+static uint8 centry_uint8(struct cache_entry *centry)
+{
+ uint8 ret;
+ if (centry->len - centry->ofs < 1) {
+ DEBUG(0,("centry corruption? needed 1 bytes, have %d\n",
+ centry->len - centry->ofs));
+ smb_panic("centry_uint32");
+ }
+ ret = CVAL(centry->data, centry->ofs);
+ centry->ofs += 1;
+ return ret;
+}
+
+/* pull a string from a cache entry, using the supplied
+ talloc context
+*/
+static char *centry_string(struct cache_entry *centry, TALLOC_CTX *mem_ctx)
+{
+ uint32 len;
+ char *ret;
+
+ len = centry_uint8(centry);
+
+ if (len == 0xFF) {
+ /* a deliberate NULL string */
+ return NULL;
+ }
+
+ if (centry->len - centry->ofs < len) {
+ DEBUG(0,("centry corruption? needed %d bytes, have %d\n",
+ len, centry->len - centry->ofs));
+ smb_panic("centry_string");
+ }
+
+ ret = talloc(mem_ctx, len+1);
+ if (!ret) {
+ smb_panic("centry_string out of memory\n");
+ }
+ memcpy(ret,centry->data + centry->ofs, len);
+ ret[len] = 0;
+ centry->ofs += len;
+ return ret;
+}
+
+/* pull a string from a cache entry, using the supplied
+ talloc context
+*/
+static DOM_SID *centry_sid(struct cache_entry *centry, TALLOC_CTX *mem_ctx)
+{
+ DOM_SID *sid;
+ char *sid_string;
+
+ sid = talloc(mem_ctx, sizeof(*sid));
+ if (!sid)
+ return NULL;
+
+ sid_string = centry_string(centry, mem_ctx);
+ if (!string_to_sid(sid, sid_string)) {
+ return NULL;
+ }
+ return sid;
+}
+
+/* the server is considered down if it can't give us a sequence number */
+static BOOL wcache_server_down(struct winbindd_domain *domain)
+{
+ BOOL ret;
+
+ if (!wcache->tdb)
+ return False;
+
+ ret = (domain->sequence_number == DOM_SEQUENCE_NONE);
+
+ if (ret)
+ DEBUG(10,("wcache_server_down: server for Domain %s down\n",
+ domain->name ));
+ return ret;
+}
+
+static NTSTATUS fetch_cache_seqnum( struct winbindd_domain *domain, time_t now )
+{
+ TDB_DATA data;
+ fstring key;
+ uint32 time_diff;
+
+ if (!wcache->tdb) {
+ DEBUG(10,("fetch_cache_seqnum: tdb == NULL\n"));
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ fstr_sprintf( key, "SEQNUM/%s", domain->name );
+
+ data = tdb_fetch_bystring( wcache->tdb, key );
+ if ( !data.dptr || data.dsize!=8 ) {
+ DEBUG(10,("fetch_cache_seqnum: invalid data size key [%s]\n", key ));
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ domain->sequence_number = IVAL(data.dptr, 0);
+ domain->last_seq_check = IVAL(data.dptr, 4);
+
+ /* have we expired? */
+
+ time_diff = now - domain->last_seq_check;
+ if ( time_diff > lp_winbind_cache_time() ) {
+ DEBUG(10,("fetch_cache_seqnum: timeout [%s][%u @ %u]\n",
+ domain->name, domain->sequence_number,
+ (uint32)domain->last_seq_check));
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ DEBUG(10,("fetch_cache_seqnum: success [%s][%u @ %u]\n",
+ domain->name, domain->sequence_number,
+ (uint32)domain->last_seq_check));
+
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS store_cache_seqnum( struct winbindd_domain *domain )
+{
+ TDB_DATA data, key;
+ fstring key_str;
+ char buf[8];
+
+ if (!wcache->tdb) {
+ DEBUG(10,("store_cache_seqnum: tdb == NULL\n"));
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ fstr_sprintf( key_str, "SEQNUM/%s", domain->name );
+ key.dptr = key_str;
+ key.dsize = strlen(key_str)+1;
+
+ SIVAL(buf, 0, domain->sequence_number);
+ SIVAL(buf, 4, domain->last_seq_check);
+ data.dptr = buf;
+ data.dsize = 8;
+
+ if ( tdb_store( wcache->tdb, key, data, TDB_REPLACE) == -1 ) {
+ DEBUG(10,("store_cache_seqnum: tdb_store fail key [%s]\n", key_str ));
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ DEBUG(10,("store_cache_seqnum: success [%s][%u @ %u]\n",
+ domain->name, domain->sequence_number,
+ (uint32)domain->last_seq_check));
+
+ return NT_STATUS_OK;
+}
+
+/*
+ refresh the domain sequence number. If force is True
+ then always refresh it, no matter how recently we fetched it
+*/
+
+static void refresh_sequence_number(struct winbindd_domain *domain, BOOL force)
+{
+ NTSTATUS status;
+ unsigned time_diff;
+ time_t t = time(NULL);
+ unsigned cache_time = lp_winbind_cache_time();
+
+ get_cache( domain );
+
+#if 0 /* JERRY -- disable as the default cache time is now 5 minutes */
+ /* trying to reconnect is expensive, don't do it too often */
+ if (domain->sequence_number == DOM_SEQUENCE_NONE) {
+ cache_time *= 8;
+ }
+#endif
+
+ time_diff = t - domain->last_seq_check;
+
+ /* see if we have to refetch the domain sequence number */
+ if (!force && (time_diff < cache_time)) {
+ DEBUG(10, ("refresh_sequence_number: %s time ok\n", domain->name));
+ goto done;
+ }
+
+ /* try to get the sequence number from the tdb cache first */
+ /* this will update the timestamp as well */
+
+ status = fetch_cache_seqnum( domain, t );
+ if ( NT_STATUS_IS_OK(status) )
+ goto done;
+
+ status = domain->backend->sequence_number(domain, &domain->sequence_number);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ domain->sequence_number = DOM_SEQUENCE_NONE;
+ }
+
+ domain->last_status = status;
+ domain->last_seq_check = time(NULL);
+
+ /* save the new sequence number ni the cache */
+ store_cache_seqnum( domain );
+
+done:
+ DEBUG(10, ("refresh_sequence_number: %s seq number is now %d\n",
+ domain->name, domain->sequence_number));
+
+ return;
+}
+
+/*
+ decide if a cache entry has expired
+*/
+static BOOL centry_expired(struct winbindd_domain *domain, const char *keystr, struct cache_entry *centry)
+{
+ /* if the server is OK and our cache entry came from when it was down then
+ the entry is invalid */
+ if (domain->sequence_number != DOM_SEQUENCE_NONE &&
+ centry->sequence_number == DOM_SEQUENCE_NONE) {
+ DEBUG(10,("centry_expired: Key %s for domain %s invalid sequence.\n",
+ keystr, domain->name ));
+ return True;
+ }
+
+ /* if the server is down or the cache entry is not older than the
+ current sequence number then it is OK */
+ if (wcache_server_down(domain) ||
+ centry->sequence_number == domain->sequence_number) {
+ DEBUG(10,("centry_expired: Key %s for domain %s is good.\n",
+ keystr, domain->name ));
+ return False;
+ }
+
+ DEBUG(10,("centry_expired: Key %s for domain %s expired\n",
+ keystr, domain->name ));
+
+ /* it's expired */
+ return True;
+}
+
+/*
+ fetch an entry from the cache, with a varargs key. auto-fetch the sequence
+ number and return status
+*/
+static struct cache_entry *wcache_fetch(struct winbind_cache *cache,
+ struct winbindd_domain *domain,
+ const char *format, ...) PRINTF_ATTRIBUTE(3,4);
+static struct cache_entry *wcache_fetch(struct winbind_cache *cache,
+ struct winbindd_domain *domain,
+ const char *format, ...)
+{
+ va_list ap;
+ char *kstr;
+ TDB_DATA data;
+ struct cache_entry *centry;
+ TDB_DATA key;
+
+ refresh_sequence_number(domain, False);
+
+ va_start(ap, format);
+ smb_xvasprintf(&kstr, format, ap);
+ va_end(ap);
+
+ key.dptr = kstr;
+ key.dsize = strlen(kstr);
+ data = tdb_fetch(wcache->tdb, key);
+ if (!data.dptr) {
+ /* a cache miss */
+ free(kstr);
+ return NULL;
+ }
+
+ centry = smb_xmalloc(sizeof(*centry));
+ centry->data = (unsigned char *)data.dptr;
+ centry->len = data.dsize;
+ centry->ofs = 0;
+
+ if (centry->len < 8) {
+ /* huh? corrupt cache? */
+ DEBUG(10,("wcache_fetch: Corrupt cache for key %s domain %s (len < 8) ?\n",
+ kstr, domain->name ));
+ centry_free(centry);
+ free(kstr);
+ return NULL;
+ }
+
+ centry->status = NT_STATUS(centry_uint32(centry));
+ centry->sequence_number = centry_uint32(centry);
+
+ if (centry_expired(domain, kstr, centry)) {
+ extern BOOL opt_dual_daemon;
+
+ DEBUG(10,("wcache_fetch: entry %s expired for domain %s\n",
+ kstr, domain->name ));
+
+ if (opt_dual_daemon) {
+ extern BOOL background_process;
+ background_process = True;
+ DEBUG(10,("wcache_fetch: background processing expired entry %s for domain %s\n",
+ kstr, domain->name ));
+ } else {
+ centry_free(centry);
+ free(kstr);
+ return NULL;
+ }
+ }
+
+ DEBUG(10,("wcache_fetch: returning entry %s for domain %s\n",
+ kstr, domain->name ));
+
+ free(kstr);
+ return centry;
+}
+
+/*
+ make sure we have at least len bytes available in a centry
+*/
+static void centry_expand(struct cache_entry *centry, uint32 len)
+{
+ uint8 *p;
+ if (centry->len - centry->ofs >= len)
+ return;
+ centry->len *= 2;
+ p = realloc(centry->data, centry->len);
+ if (!p) {
+ DEBUG(0,("out of memory: needed %d bytes in centry_expand\n", centry->len));
+ smb_panic("out of memory in centry_expand");
+ }
+ centry->data = p;
+}
+
+/*
+ push a uint32 into a centry
+*/
+static void centry_put_uint32(struct cache_entry *centry, uint32 v)
+{
+ centry_expand(centry, 4);
+ SIVAL(centry->data, centry->ofs, v);
+ centry->ofs += 4;
+}
+
+/*
+ push a uint8 into a centry
+*/
+static void centry_put_uint8(struct cache_entry *centry, uint8 v)
+{
+ centry_expand(centry, 1);
+ SCVAL(centry->data, centry->ofs, v);
+ centry->ofs += 1;
+}
+
+/*
+ push a string into a centry
+ */
+static void centry_put_string(struct cache_entry *centry, const char *s)
+{
+ int len;
+
+ if (!s) {
+ /* null strings are marked as len 0xFFFF */
+ centry_put_uint8(centry, 0xFF);
+ return;
+ }
+
+ len = strlen(s);
+ /* can't handle more than 254 char strings. Truncating is probably best */
+ if (len > 254)
+ len = 254;
+ centry_put_uint8(centry, len);
+ centry_expand(centry, len);
+ memcpy(centry->data + centry->ofs, s, len);
+ centry->ofs += len;
+}
+
+static void centry_put_sid(struct cache_entry *centry, const DOM_SID *sid)
+{
+ fstring sid_string;
+ centry_put_string(centry, sid_to_string(sid_string, sid));
+}
+
+/*
+ start a centry for output. When finished, call centry_end()
+*/
+struct cache_entry *centry_start(struct winbindd_domain *domain, NTSTATUS status)
+{
+ struct cache_entry *centry;
+
+ if (!wcache->tdb)
+ return NULL;
+
+ centry = smb_xmalloc(sizeof(*centry));
+
+ centry->len = 8192; /* reasonable default */
+ centry->data = smb_xmalloc(centry->len);
+ centry->ofs = 0;
+ centry->sequence_number = domain->sequence_number;
+ centry_put_uint32(centry, NT_STATUS_V(status));
+ centry_put_uint32(centry, centry->sequence_number);
+ return centry;
+}
+
+/*
+ finish a centry and write it to the tdb
+*/
+static void centry_end(struct cache_entry *centry, const char *format, ...) PRINTF_ATTRIBUTE(2,3);
+static void centry_end(struct cache_entry *centry, const char *format, ...)
+{
+ va_list ap;
+ char *kstr;
+ TDB_DATA key, data;
+
+ va_start(ap, format);
+ smb_xvasprintf(&kstr, format, ap);
+ va_end(ap);
+
+ key.dptr = kstr;
+ key.dsize = strlen(kstr);
+ data.dptr = (char *)centry->data;
+ data.dsize = centry->ofs;
+
+ tdb_store(wcache->tdb, key, data, TDB_REPLACE);
+ free(kstr);
+}
+
+static void wcache_save_name_to_sid(struct winbindd_domain *domain,
+ NTSTATUS status,
+ const char *name, const DOM_SID *sid,
+ enum SID_NAME_USE type)
+{
+ struct cache_entry *centry;
+ fstring uname;
+ fstring sid_string;
+
+ centry = centry_start(domain, status);
+ if (!centry)
+ return;
+ centry_put_sid(centry, sid);
+ fstrcpy(uname, name);
+ strupper_m(uname);
+ centry_end(centry, "NS/%s", sid_to_string(sid_string, sid));
+ DEBUG(10,("wcache_save_name_to_sid: %s -> %s\n", uname, sid_string));
+ centry_free(centry);
+}
+
+static void wcache_save_sid_to_name(struct winbindd_domain *domain, NTSTATUS status,
+ const DOM_SID *sid, const char *name, enum SID_NAME_USE type)
+{
+ struct cache_entry *centry;
+ fstring sid_string;
+
+ centry = centry_start(domain, status);
+ if (!centry)
+ return;
+ if (NT_STATUS_IS_OK(status)) {
+ centry_put_uint32(centry, type);
+ centry_put_string(centry, name);
+ }
+ centry_end(centry, "SN/%s", sid_to_string(sid_string, sid));
+ DEBUG(10,("wcache_save_sid_to_name: %s -> %s\n", sid_string, name));
+ centry_free(centry);
+}
+
+
+static void wcache_save_user(struct winbindd_domain *domain, NTSTATUS status, WINBIND_USERINFO *info)
+{
+ struct cache_entry *centry;
+ fstring sid_string;
+
+ centry = centry_start(domain, status);
+ if (!centry)
+ return;
+ centry_put_string(centry, info->acct_name);
+ centry_put_string(centry, info->full_name);
+ centry_put_sid(centry, info->user_sid);
+ centry_put_sid(centry, info->group_sid);
+ centry_end(centry, "U/%s", sid_to_string(sid_string, info->user_sid));
+ DEBUG(10,("wcache_save_user: %s (acct_name %s)\n", sid_string, info->acct_name));
+ centry_free(centry);
+}
+
+
+/* Query display info. This is the basic user list fn */
+static NTSTATUS query_user_list(struct winbindd_domain *domain,
+ TALLOC_CTX *mem_ctx,
+ uint32 *num_entries,
+ WINBIND_USERINFO **info)
+{
+ struct winbind_cache *cache = get_cache(domain);
+ struct cache_entry *centry = NULL;
+ NTSTATUS status;
+ unsigned int i, retry;
+
+ if (!cache->tdb)
+ goto do_query;
+
+ centry = wcache_fetch(cache, domain, "UL/%s", domain->name);
+ if (!centry)
+ goto do_query;
+
+ *num_entries = centry_uint32(centry);
+
+ if (*num_entries == 0)
+ goto do_cached;
+
+ (*info) = talloc(mem_ctx, sizeof(**info) * (*num_entries));
+ if (! (*info))
+ smb_panic("query_user_list out of memory");
+ for (i=0; i<(*num_entries); i++) {
+ (*info)[i].acct_name = centry_string(centry, mem_ctx);
+ (*info)[i].full_name = centry_string(centry, mem_ctx);
+ (*info)[i].user_sid = centry_sid(centry, mem_ctx);
+ (*info)[i].group_sid = centry_sid(centry, mem_ctx);
+ }
+
+do_cached:
+ status = centry->status;
+
+ DEBUG(10,("query_user_list: [Cached] - cached list for domain %s status %s\n",
+ domain->name, get_friendly_nt_error_msg(status) ));
+
+ centry_free(centry);
+ return status;
+
+do_query:
+ *num_entries = 0;
+ *info = NULL;
+
+ /* Return status value returned by seq number check */
+
+ if (!NT_STATUS_IS_OK(domain->last_status))
+ return domain->last_status;
+
+ /* Put the query_user_list() in a retry loop. There appears to be
+ * some bug either with Windows 2000 or Samba's handling of large
+ * rpc replies. This manifests itself as sudden disconnection
+ * at a random point in the enumeration of a large (60k) user list.
+ * The retry loop simply tries the operation again. )-: It's not
+ * pretty but an acceptable workaround until we work out what the
+ * real problem is. */
+
+ retry = 0;
+ do {
+
+ DEBUG(10,("query_user_list: [Cached] - doing backend query for list for domain %s\n",
+ domain->name ));
+
+ status = domain->backend->query_user_list(domain, mem_ctx, num_entries, info);
+ if (!NT_STATUS_IS_OK(status))
+ DEBUG(3, ("query_user_list: returned 0x%08x, retrying\n", NT_STATUS_V(status)));
+ if (NT_STATUS_V(status) == NT_STATUS_V(NT_STATUS_UNSUCCESSFUL)) {
+ DEBUG(3, ("query_user_list: flushing connection cache\n"));
+ winbindd_cm_flush();
+ }
+
+ } while (NT_STATUS_V(status) == NT_STATUS_V(NT_STATUS_UNSUCCESSFUL) &&
+ (retry++ < 5));
+
+ /* and save it */
+ refresh_sequence_number(domain, False);
+ centry = centry_start(domain, status);
+ if (!centry)
+ goto skip_save;
+ centry_put_uint32(centry, *num_entries);
+ for (i=0; i<(*num_entries); i++) {
+ centry_put_string(centry, (*info)[i].acct_name);
+ centry_put_string(centry, (*info)[i].full_name);
+ centry_put_sid(centry, (*info)[i].user_sid);
+ centry_put_sid(centry, (*info)[i].group_sid);
+ if (domain->backend->consistent) {
+ /* when the backend is consistent we can pre-prime some mappings */
+ wcache_save_name_to_sid(domain, NT_STATUS_OK,
+ (*info)[i].acct_name,
+ (*info)[i].user_sid,
+ SID_NAME_USER);
+ wcache_save_sid_to_name(domain, NT_STATUS_OK,
+ (*info)[i].user_sid,
+ (*info)[i].acct_name,
+ SID_NAME_USER);
+ wcache_save_user(domain, NT_STATUS_OK, &(*info)[i]);
+ }
+ }
+ centry_end(centry, "UL/%s", domain->name);
+ centry_free(centry);
+
+skip_save:
+ return status;
+}
+
+/* list all domain groups */
+static NTSTATUS enum_dom_groups(struct winbindd_domain *domain,
+ TALLOC_CTX *mem_ctx,
+ uint32 *num_entries,
+ struct acct_info **info)
+{
+ struct winbind_cache *cache = get_cache(domain);
+ struct cache_entry *centry = NULL;
+ NTSTATUS status;
+ unsigned int i;
+
+ if (!cache->tdb)
+ goto do_query;
+
+ centry = wcache_fetch(cache, domain, "GL/%s/domain", domain->name);
+ if (!centry)
+ goto do_query;
+
+ *num_entries = centry_uint32(centry);
+
+ if (*num_entries == 0)
+ goto do_cached;
+
+ (*info) = talloc(mem_ctx, sizeof(**info) * (*num_entries));
+ if (! (*info))
+ smb_panic("enum_dom_groups out of memory");
+ for (i=0; i<(*num_entries); i++) {
+ fstrcpy((*info)[i].acct_name, centry_string(centry, mem_ctx));
+ fstrcpy((*info)[i].acct_desc, centry_string(centry, mem_ctx));
+ (*info)[i].rid = centry_uint32(centry);
+ }
+
+do_cached:
+ status = centry->status;
+
+ DEBUG(10,("enum_dom_groups: [Cached] - cached list for domain %s status %s\n",
+ domain->name, get_friendly_nt_error_msg(status) ));
+
+ centry_free(centry);
+ return status;
+
+do_query:
+ *num_entries = 0;
+ *info = NULL;
+
+ /* Return status value returned by seq number check */
+
+ if (!NT_STATUS_IS_OK(domain->last_status))
+ return domain->last_status;
+
+ DEBUG(10,("enum_dom_groups: [Cached] - doing backend query for list for domain %s\n",
+ domain->name ));
+
+ status = domain->backend->enum_dom_groups(domain, mem_ctx, num_entries, info);
+
+ /* and save it */
+ refresh_sequence_number(domain, False);
+ centry = centry_start(domain, status);
+ if (!centry)
+ goto skip_save;
+ centry_put_uint32(centry, *num_entries);
+ for (i=0; i<(*num_entries); i++) {
+ centry_put_string(centry, (*info)[i].acct_name);
+ centry_put_string(centry, (*info)[i].acct_desc);
+ centry_put_uint32(centry, (*info)[i].rid);
+ }
+ centry_end(centry, "GL/%s/domain", domain->name);
+ centry_free(centry);
+
+skip_save:
+ return status;
+}
+
+/* list all domain groups */
+static NTSTATUS enum_local_groups(struct winbindd_domain *domain,
+ TALLOC_CTX *mem_ctx,
+ uint32 *num_entries,
+ struct acct_info **info)
+{
+ struct winbind_cache *cache = get_cache(domain);
+ struct cache_entry *centry = NULL;
+ NTSTATUS status;
+ unsigned int i;
+
+ if (!cache->tdb)
+ goto do_query;
+
+ centry = wcache_fetch(cache, domain, "GL/%s/local", domain->name);
+ if (!centry)
+ goto do_query;
+
+ *num_entries = centry_uint32(centry);
+
+ if (*num_entries == 0)
+ goto do_cached;
+
+ (*info) = talloc(mem_ctx, sizeof(**info) * (*num_entries));
+ if (! (*info))
+ smb_panic("enum_dom_groups out of memory");
+ for (i=0; i<(*num_entries); i++) {
+ fstrcpy((*info)[i].acct_name, centry_string(centry, mem_ctx));
+ fstrcpy((*info)[i].acct_desc, centry_string(centry, mem_ctx));
+ (*info)[i].rid = centry_uint32(centry);
+ }
+
+do_cached:
+
+ /* If we are returning cached data and the domain controller
+ is down then we don't know whether the data is up to date
+ or not. Return NT_STATUS_MORE_PROCESSING_REQUIRED to
+ indicate this. */
+
+ if (wcache_server_down(domain)) {
+ DEBUG(10, ("enum_local_groups: returning cached user list and server was down\n"));
+ status = NT_STATUS_MORE_PROCESSING_REQUIRED;
+ } else
+ status = centry->status;
+
+ DEBUG(10,("enum_local_groups: [Cached] - cached list for domain %s status %s\n",
+ domain->name, get_friendly_nt_error_msg(status) ));
+
+ centry_free(centry);
+ return status;
+
+do_query:
+ *num_entries = 0;
+ *info = NULL;
+
+ /* Return status value returned by seq number check */
+
+ if (!NT_STATUS_IS_OK(domain->last_status))
+ return domain->last_status;
+
+ DEBUG(10,("enum_local_groups: [Cached] - doing backend query for list for domain %s\n",
+ domain->name ));
+
+ status = domain->backend->enum_local_groups(domain, mem_ctx, num_entries, info);
+
+ /* and save it */
+ refresh_sequence_number(domain, False);
+ centry = centry_start(domain, status);
+ if (!centry)
+ goto skip_save;
+ centry_put_uint32(centry, *num_entries);
+ for (i=0; i<(*num_entries); i++) {
+ centry_put_string(centry, (*info)[i].acct_name);
+ centry_put_string(centry, (*info)[i].acct_desc);
+ centry_put_uint32(centry, (*info)[i].rid);
+ }
+ centry_end(centry, "GL/%s/local", domain->name);
+ centry_free(centry);
+
+skip_save:
+ return status;
+}
+
+/* convert a single name to a sid in a domain */
+static NTSTATUS name_to_sid(struct winbindd_domain *domain,
+ TALLOC_CTX *mem_ctx,
+ const char *name,
+ DOM_SID *sid,
+ enum SID_NAME_USE *type)
+{
+ struct winbind_cache *cache = get_cache(domain);
+ struct cache_entry *centry = NULL;
+ NTSTATUS status;
+ fstring uname;
+ DOM_SID *sid2;
+
+ if (!cache->tdb)
+ goto do_query;
+
+ fstrcpy(uname, name);
+ strupper_m(uname);
+ centry = wcache_fetch(cache, domain, "NS/%s/%s", domain->name, uname);
+ if (!centry)
+ goto do_query;
+ *type = (enum SID_NAME_USE)centry_uint32(centry);
+ sid2 = centry_sid(centry, mem_ctx);
+ if (!sid2) {
+ ZERO_STRUCTP(sid);
+ } else {
+ sid_copy(sid, sid2);
+ }
+
+ status = centry->status;
+
+ DEBUG(10,("name_to_sid: [Cached] - cached name for domain %s status %s\n",
+ domain->name, get_friendly_nt_error_msg(status) ));
+
+ centry_free(centry);
+ return status;
+
+do_query:
+ ZERO_STRUCTP(sid);
+
+ /* If the seq number check indicated that there is a problem
+ * with this DC, then return that status... except for
+ * access_denied. This is special because the dc may be in
+ * "restrict anonymous = 1" mode, in which case it will deny
+ * most unauthenticated operations, but *will* allow the LSA
+ * name-to-sid that we try as a fallback. */
+
+ if (!(NT_STATUS_IS_OK(domain->last_status)
+ || NT_STATUS_EQUAL(domain->last_status, NT_STATUS_ACCESS_DENIED)))
+ return domain->last_status;
+
+ DEBUG(10,("name_to_sid: [Cached] - doing backend query for name for domain %s\n",
+ domain->name ));
+
+ status = domain->backend->name_to_sid(domain, mem_ctx, name, sid, type);
+
+ /* and save it */
+ wcache_save_name_to_sid(domain, status, name, sid, *type);
+
+ /* We can't save the sid to name mapping as we don't know the
+ correct case of the name without looking it up */
+
+ return status;
+}
+
+/* convert a sid to a user or group name. The sid is guaranteed to be in the domain
+ given */
+static NTSTATUS sid_to_name(struct winbindd_domain *domain,
+ TALLOC_CTX *mem_ctx,
+ const DOM_SID *sid,
+ char **name,
+ enum SID_NAME_USE *type)
+{
+ struct winbind_cache *cache = get_cache(domain);
+ struct cache_entry *centry = NULL;
+ NTSTATUS status;
+ fstring sid_string;
+
+ if (!cache->tdb)
+ goto do_query;
+
+ centry = wcache_fetch(cache, domain, "SN/%s", sid_to_string(sid_string, sid));
+ if (!centry)
+ goto do_query;
+ if (NT_STATUS_IS_OK(centry->status)) {
+ *type = (enum SID_NAME_USE)centry_uint32(centry);
+ *name = centry_string(centry, mem_ctx);
+ }
+ status = centry->status;
+
+ DEBUG(10,("sid_to_name: [Cached] - cached name for domain %s status %s\n",
+ domain->name, get_friendly_nt_error_msg(status) ));
+
+ centry_free(centry);
+ return status;
+
+do_query:
+ *name = NULL;
+
+ /* If the seq number check indicated that there is a problem
+ * with this DC, then return that status... except for
+ * access_denied. This is special because the dc may be in
+ * "restrict anonymous = 1" mode, in which case it will deny
+ * most unauthenticated operations, but *will* allow the LSA
+ * sid-to-name that we try as a fallback. */
+
+ if (!(NT_STATUS_IS_OK(domain->last_status)
+ || NT_STATUS_EQUAL(domain->last_status, NT_STATUS_ACCESS_DENIED)))
+ return domain->last_status;
+
+ DEBUG(10,("sid_to_name: [Cached] - doing backend query for name for domain %s\n",
+ domain->name ));
+
+ status = domain->backend->sid_to_name(domain, mem_ctx, sid, name, type);
+
+ /* and save it */
+ refresh_sequence_number(domain, False);
+ wcache_save_sid_to_name(domain, status, sid, *name, *type);
+ wcache_save_name_to_sid(domain, status, *name, sid, *type);
+
+ return status;
+}
+
+
+/* Lookup user information from a rid */
+static NTSTATUS query_user(struct winbindd_domain *domain,
+ TALLOC_CTX *mem_ctx,
+ const DOM_SID *user_sid,
+ WINBIND_USERINFO *info)
+{
+ struct winbind_cache *cache = get_cache(domain);
+ struct cache_entry *centry = NULL;
+ NTSTATUS status;
+
+ if (!cache->tdb)
+ goto do_query;
+
+ centry = wcache_fetch(cache, domain, "U/%s", sid_string_static(user_sid));
+
+ /* If we have an access denied cache entry and a cached info3 in the
+ samlogon cache then do a query. This will force the rpc back end
+ to return the info3 data. */
+
+ if (NT_STATUS_V(domain->last_status) == NT_STATUS_V(NT_STATUS_ACCESS_DENIED) &&
+ netsamlogon_cache_have(user_sid)) {
+ DEBUG(10, ("query_user: cached access denied and have cached info3\n"));
+ domain->last_status = NT_STATUS_OK;
+ centry_free(centry);
+ goto do_query;
+ }
+
+ if (!centry)
+ goto do_query;
+
+ info->acct_name = centry_string(centry, mem_ctx);
+ info->full_name = centry_string(centry, mem_ctx);
+ info->user_sid = centry_sid(centry, mem_ctx);
+ info->group_sid = centry_sid(centry, mem_ctx);
+ status = centry->status;
+
+ DEBUG(10,("query_user: [Cached] - cached info for domain %s status %s\n",
+ domain->name, get_friendly_nt_error_msg(status) ));
+
+ centry_free(centry);
+ return status;
+
+do_query:
+ ZERO_STRUCTP(info);
+
+ /* Return status value returned by seq number check */
+
+ if (!NT_STATUS_IS_OK(domain->last_status))
+ return domain->last_status;
+
+ DEBUG(10,("sid_to_name: [Cached] - doing backend query for info for domain %s\n",
+ domain->name ));
+
+ status = domain->backend->query_user(domain, mem_ctx, user_sid, info);
+
+ /* and save it */
+ refresh_sequence_number(domain, False);
+ wcache_save_user(domain, status, info);
+
+ return status;
+}
+
+
+/* Lookup groups a user is a member of. */
+static NTSTATUS lookup_usergroups(struct winbindd_domain *domain,
+ TALLOC_CTX *mem_ctx,
+ const DOM_SID *user_sid,
+ uint32 *num_groups, DOM_SID ***user_gids)
+{
+ struct winbind_cache *cache = get_cache(domain);
+ struct cache_entry *centry = NULL;
+ NTSTATUS status;
+ unsigned int i;
+ fstring sid_string;
+
+ if (!cache->tdb)
+ goto do_query;
+
+ centry = wcache_fetch(cache, domain, "UG/%s", sid_to_string(sid_string, user_sid));
+
+ /* If we have an access denied cache entry and a cached info3 in the
+ samlogon cache then do a query. This will force the rpc back end
+ to return the info3 data. */
+
+ if (NT_STATUS_V(domain->last_status) == NT_STATUS_V(NT_STATUS_ACCESS_DENIED) &&
+ netsamlogon_cache_have(user_sid)) {
+ DEBUG(10, ("query_user: cached access denied and have cached info3\n"));
+ domain->last_status = NT_STATUS_OK;
+ centry_free(centry);
+ goto do_query;
+ }
+
+ if (!centry)
+ goto do_query;
+
+ *num_groups = centry_uint32(centry);
+
+ if (*num_groups == 0)
+ goto do_cached;
+
+ (*user_gids) = talloc(mem_ctx, sizeof(**user_gids) * (*num_groups));
+ if (! (*user_gids))
+ smb_panic("lookup_usergroups out of memory");
+ for (i=0; i<(*num_groups); i++) {
+ (*user_gids)[i] = centry_sid(centry, mem_ctx);
+ }
+
+do_cached:
+ status = centry->status;
+
+ DEBUG(10,("lookup_usergroups: [Cached] - cached info for domain %s status %s\n",
+ domain->name, get_friendly_nt_error_msg(status) ));
+
+ centry_free(centry);
+ return status;
+
+do_query:
+ (*num_groups) = 0;
+ (*user_gids) = NULL;
+
+ /* Return status value returned by seq number check */
+
+ if (!NT_STATUS_IS_OK(domain->last_status))
+ return domain->last_status;
+
+ DEBUG(10,("lookup_usergroups: [Cached] - doing backend query for info for domain %s\n",
+ domain->name ));
+
+ status = domain->backend->lookup_usergroups(domain, mem_ctx, user_sid, num_groups, user_gids);
+
+ /* and save it */
+ refresh_sequence_number(domain, False);
+ centry = centry_start(domain, status);
+ if (!centry)
+ goto skip_save;
+ centry_put_uint32(centry, *num_groups);
+ for (i=0; i<(*num_groups); i++) {
+ centry_put_sid(centry, (*user_gids)[i]);
+ }
+ centry_end(centry, "UG/%s", sid_to_string(sid_string, user_sid));
+ centry_free(centry);
+
+skip_save:
+ return status;
+}
+
+
+static NTSTATUS lookup_groupmem(struct winbindd_domain *domain,
+ TALLOC_CTX *mem_ctx,
+ const DOM_SID *group_sid, uint32 *num_names,
+ DOM_SID ***sid_mem, char ***names,
+ uint32 **name_types)
+{
+ struct winbind_cache *cache = get_cache(domain);
+ struct cache_entry *centry = NULL;
+ NTSTATUS status;
+ unsigned int i;
+ fstring sid_string;
+
+ if (!cache->tdb)
+ goto do_query;
+
+ centry = wcache_fetch(cache, domain, "GM/%s", sid_to_string(sid_string, group_sid));
+ if (!centry)
+ goto do_query;
+
+ *num_names = centry_uint32(centry);
+
+ if (*num_names == 0)
+ goto do_cached;
+
+ (*sid_mem) = talloc(mem_ctx, sizeof(**sid_mem) * (*num_names));
+ (*names) = talloc(mem_ctx, sizeof(**names) * (*num_names));
+ (*name_types) = talloc(mem_ctx, sizeof(**name_types) * (*num_names));
+
+ if (! (*sid_mem) || ! (*names) || ! (*name_types)) {
+ smb_panic("lookup_groupmem out of memory");
+ }
+
+ for (i=0; i<(*num_names); i++) {
+ (*sid_mem)[i] = centry_sid(centry, mem_ctx);
+ (*names)[i] = centry_string(centry, mem_ctx);
+ (*name_types)[i] = centry_uint32(centry);
+ }
+
+do_cached:
+ status = centry->status;
+
+ DEBUG(10,("lookup_groupmem: [Cached] - cached info for domain %s status %s\n",
+ domain->name, get_friendly_nt_error_msg(status) ));
+
+ centry_free(centry);
+ return status;
+
+do_query:
+ (*num_names) = 0;
+ (*sid_mem) = NULL;
+ (*names) = NULL;
+ (*name_types) = NULL;
+
+ /* Return status value returned by seq number check */
+
+ if (!NT_STATUS_IS_OK(domain->last_status))
+ return domain->last_status;
+
+ DEBUG(10,("lookup_groupmem: [Cached] - doing backend query for info for domain %s\n",
+ domain->name ));
+
+ status = domain->backend->lookup_groupmem(domain, mem_ctx, group_sid, num_names,
+ sid_mem, names, name_types);
+
+ /* and save it */
+ refresh_sequence_number(domain, False);
+ centry = centry_start(domain, status);
+ if (!centry)
+ goto skip_save;
+ centry_put_uint32(centry, *num_names);
+ for (i=0; i<(*num_names); i++) {
+ centry_put_sid(centry, (*sid_mem)[i]);
+ centry_put_string(centry, (*names)[i]);
+ centry_put_uint32(centry, (*name_types)[i]);
+ }
+ centry_end(centry, "GM/%s", sid_to_string(sid_string, group_sid));
+ centry_free(centry);
+
+skip_save:
+ return status;
+}
+
+/* find the sequence number for a domain */
+static NTSTATUS sequence_number(struct winbindd_domain *domain, uint32 *seq)
+{
+ refresh_sequence_number(domain, False);
+
+ *seq = domain->sequence_number;
+
+ return NT_STATUS_OK;
+}
+
+/* enumerate trusted domains */
+static NTSTATUS trusted_domains(struct winbindd_domain *domain,
+ TALLOC_CTX *mem_ctx,
+ uint32 *num_domains,
+ char ***names,
+ char ***alt_names,
+ DOM_SID **dom_sids)
+{
+ get_cache(domain);
+
+ DEBUG(10,("trusted_domains: [Cached] - doing backend query for info for domain %s\n",
+ domain->name ));
+
+ /* we don't cache this call */
+ return domain->backend->trusted_domains(domain, mem_ctx, num_domains,
+ names, alt_names, dom_sids);
+}
+
+/* find the domain sid */
+static NTSTATUS domain_sid(struct winbindd_domain *domain, DOM_SID *sid)
+{
+ get_cache(domain);
+
+ DEBUG(10,("domain_sid: [Cached] - doing backend query for info for domain %s\n",
+ domain->name ));
+
+ /* we don't cache this call */
+ return domain->backend->domain_sid(domain, sid);
+}
+
+/* find the alternate names for the domain, if any */
+static NTSTATUS alternate_name(struct winbindd_domain *domain)
+{
+ get_cache(domain);
+
+ DEBUG(10,("alternate_name: [Cached] - doing backend query for info for domain %s\n",
+ domain->name ));
+
+ /* we don't cache this call */
+ return domain->backend->alternate_name(domain);
+}
+
+/* Invalidate cached user and group lists coherently */
+
+static int traverse_fn(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf,
+ void *state)
+{
+ if (strncmp(kbuf.dptr, "UL/", 3) == 0 ||
+ strncmp(kbuf.dptr, "GL/", 3) == 0)
+ tdb_delete(the_tdb, kbuf);
+
+ return 0;
+}
+
+/* Invalidate the getpwnam and getgroups entries for a winbindd domain */
+
+void wcache_invalidate_samlogon(struct winbindd_domain *domain,
+ NET_USER_INFO_3 *info3)
+{
+ struct winbind_cache *cache;
+
+ if (!domain)
+ return;
+
+ cache = get_cache(domain);
+ netsamlogon_clear_cached_user(cache->tdb, info3);
+}
+
+void wcache_invalidate_cache(void)
+{
+ struct winbindd_domain *domain;
+
+ for (domain = domain_list(); domain; domain = domain->next) {
+ struct winbind_cache *cache = get_cache(domain);
+
+ DEBUG(10, ("wcache_invalidate_cache: invalidating cache "
+ "entries for %s\n", domain->name));
+ if (cache)
+ tdb_traverse(cache->tdb, traverse_fn, NULL);
+ }
+}
+
+/* the ADS backend methods are exposed via this structure */
+struct winbindd_methods cache_methods = {
+ True,
+ query_user_list,
+ enum_dom_groups,
+ enum_local_groups,
+ name_to_sid,
+ sid_to_name,
+ query_user,
+ lookup_usergroups,
+ lookup_groupmem,
+ sequence_number,
+ trusted_domains,
+ domain_sid,
+ alternate_name
+};
diff --git a/source/nsswitch/winbindd_cm.c b/source/nsswitch/winbindd_cm.c
new file mode 100644
index 00000000000..29b856ec455
--- /dev/null
+++ b/source/nsswitch/winbindd_cm.c
@@ -0,0 +1,773 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ Winbind daemon connection manager
+
+ Copyright (C) Tim Potter 2001
+ 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 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.
+*/
+
+/*
+ We need to manage connections to domain controllers without having to
+ mess up the main winbindd code with other issues. The aim of the
+ connection manager is to:
+
+ - make connections to domain controllers and cache them
+ - re-establish connections when networks or servers go down
+ - centralise the policy on connection timeouts, domain controller
+ selection etc
+ - manage re-entrancy for when winbindd becomes able to handle
+ multiple outstanding rpc requests
+
+ Why not have connection management as part of the rpc layer like tng?
+ Good question. This code may morph into libsmb/rpc_cache.c or something
+ like that but at the moment it's simply staying as part of winbind. I
+ think the TNG architecture of forcing every user of the rpc layer to use
+ the connection caching system is a bad idea. It should be an optional
+ method of using the routines.
+
+ The TNG design is quite good but I disagree with some aspects of the
+ implementation. -tpot
+
+ */
+
+/*
+ TODO:
+
+ - I'm pretty annoyed by all the make_nmb_name() stuff. It should be
+ moved down into another function.
+
+ - Take care when destroying cli_structs as they can be shared between
+ various sam handles.
+
+ */
+
+#include "includes.h"
+#include "winbindd.h"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_WINBIND
+
+/* Global list of connections. Initially a DLIST but can become a hash
+ table or whatever later. */
+
+struct winbindd_cm_conn {
+ struct winbindd_cm_conn *prev, *next;
+ fstring domain;
+ fstring controller;
+ fstring pipe_name;
+ size_t mutex_ref_count;
+ struct cli_state *cli;
+ POLICY_HND pol;
+};
+
+static struct winbindd_cm_conn *cm_conns = NULL;
+
+
+/* Choose between anonymous or authenticated connections. We need to use
+ an authenticated connection if DCs have the RestrictAnonymous registry
+ entry set > 0, or the "Additional restrictions for anonymous
+ connections" set in the win2k Local Security Policy.
+
+ Caller to free() result in domain, username, password
+*/
+
+static void cm_get_ipc_userpass(char **username, char **domain, char **password)
+{
+ *username = secrets_fetch(SECRETS_AUTH_USER, NULL);
+ *domain = secrets_fetch(SECRETS_AUTH_DOMAIN, NULL);
+ *password = secrets_fetch(SECRETS_AUTH_PASSWORD, NULL);
+
+ if (*username && **username) {
+
+ if (!*domain || !**domain)
+ *domain = smb_xstrdup(lp_workgroup());
+
+ if (!*password || !**password)
+ *password = smb_xstrdup("");
+
+ DEBUG(3, ("IPC$ connections done by user %s\\%s\n",
+ *domain, *username));
+
+ } else {
+ DEBUG(3, ("IPC$ connections done anonymously\n"));
+ *username = smb_xstrdup("");
+ *domain = smb_xstrdup("");
+ *password = smb_xstrdup("");
+ }
+}
+
+/* Open a connction to the remote server, cache failures for 30 seconds */
+
+static NTSTATUS cm_open_connection(const struct winbindd_domain *domain, const int pipe_index,
+ struct winbindd_cm_conn *new_conn)
+{
+ NTSTATUS result;
+ char *machine_password;
+ char *machine_krb5_principal, *ipc_username, *ipc_domain, *ipc_password;
+ struct in_addr dc_ip;
+ int i;
+ BOOL retry = True;
+
+ ZERO_STRUCT(dc_ip);
+
+ fstrcpy(new_conn->domain, domain->name);
+
+ /* connection failure cache has been moved inside of get_dc_name
+ so we can deal with half dead DC's --jerry */
+
+ if (!get_dc_name(domain->name, domain->alt_name[0] ? domain->alt_name : NULL,
+ new_conn->controller, &dc_ip)) {
+ result = NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND;
+ add_failed_connection_entry(domain->name, "", result);
+ return result;
+ }
+
+ /* Initialise SMB connection */
+ fstrcpy(new_conn->pipe_name, get_pipe_name_from_index(pipe_index));
+
+/* grab stored passwords */
+ machine_password = secrets_fetch_machine_password(lp_workgroup(), NULL, NULL);
+
+ if (asprintf(&machine_krb5_principal, "%s$@%s", global_myname(), lp_realm()) == -1) {
+ SAFE_FREE(machine_password);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ cm_get_ipc_userpass(&ipc_username, &ipc_domain, &ipc_password);
+
+ for (i = 0; retry && (i < 3); i++) {
+ BOOL got_mutex;
+ if (!(got_mutex = secrets_named_mutex(new_conn->controller, WINBIND_SERVER_MUTEX_WAIT_TIME))) {
+ DEBUG(0,("cm_open_connection: mutex grab failed for %s\n", new_conn->controller));
+ result = NT_STATUS_POSSIBLE_DEADLOCK;
+ continue;
+ }
+
+ new_conn->cli = NULL;
+ result = cli_start_connection(&new_conn->cli, global_myname(),
+ new_conn->controller,
+ &dc_ip, 0, Undefined,
+ CLI_FULL_CONNECTION_USE_KERBEROS,
+ &retry);
+
+ if (NT_STATUS_IS_OK(result)) {
+
+ /* reset the error code */
+ result = NT_STATUS_UNSUCCESSFUL;
+
+ /* Krb5 session */
+
+ if ((lp_security() == SEC_ADS)
+ && (new_conn->cli->protocol >= PROTOCOL_NT1 && new_conn->cli->capabilities & CAP_EXTENDED_SECURITY)) {
+ ADS_STATUS ads_status;
+ new_conn->cli->use_kerberos = True;
+ DEBUG(5, ("connecting to %s from %s with kerberos principal [%s]\n",
+ new_conn->controller, global_myname(), machine_krb5_principal));
+
+ ads_status = cli_session_setup_spnego(new_conn->cli, machine_krb5_principal,
+ machine_password,
+ lp_workgroup());
+ if (!ADS_ERR_OK(ads_status)) {
+ DEBUG(4,("failed kerberos session setup with %s\n", ads_errstr(ads_status)));
+ result = ads_ntstatus(ads_status);
+ } else {
+ result = NT_STATUS_OK;
+ }
+ }
+ new_conn->cli->use_kerberos = False;
+
+ /* only do this is we have a username/password for thr IPC$ connection */
+
+ if ( !NT_STATUS_IS_OK(result)
+ && new_conn->cli->sec_mode & NEGOTIATE_SECURITY_CHALLENGE_RESPONSE
+ && strlen(ipc_username) )
+ {
+ DEBUG(5, ("connecting to %s from %s with username [%s]\\[%s]\n",
+ new_conn->controller, global_myname(), ipc_domain, ipc_username));
+
+ result = NT_STATUS_OK;
+
+ if (!cli_session_setup(new_conn->cli, ipc_username,
+ ipc_password, strlen(ipc_password)+1,
+ ipc_password, strlen(ipc_password)+1,
+ ipc_domain)) {
+ result = cli_nt_error(new_conn->cli);
+ DEBUG(4,("failed authenticated session setup with %s\n", nt_errstr(result)));
+ if (NT_STATUS_IS_OK(result))
+ result = NT_STATUS_UNSUCCESSFUL;
+ }
+ }
+
+ /* anonymous is all that is left if we get to here */
+
+ if (!NT_STATUS_IS_OK(result)) {
+
+ DEBUG(5, ("anonymous connection attempt to %s from %s\n",
+ new_conn->controller, global_myname()));
+
+ result = NT_STATUS_OK;
+
+ if (!cli_session_setup(new_conn->cli, "", NULL, 0, NULL, 0, ""))
+ {
+ result = cli_nt_error(new_conn->cli);
+ DEBUG(4,("failed anonymous session setup with %s\n", nt_errstr(result)));
+ if (NT_STATUS_IS_OK(result))
+ result = NT_STATUS_UNSUCCESSFUL;
+ }
+
+ }
+
+ if (NT_STATUS_IS_OK(result) && !cli_send_tconX(new_conn->cli, "IPC$", "IPC",
+ "", 0)) {
+ result = cli_nt_error(new_conn->cli);
+ DEBUG(1,("failed tcon_X with %s\n", nt_errstr(result)));
+ cli_shutdown(new_conn->cli);
+ if (NT_STATUS_IS_OK(result)) {
+ result = NT_STATUS_UNSUCCESSFUL;
+ }
+ }
+ }
+
+ if (NT_STATUS_IS_OK(result)) {
+ struct ntuser_creds creds;
+ init_creds(&creds, ipc_username, ipc_domain, ipc_password);
+ cli_init_creds(new_conn->cli, &creds);
+ }
+
+ if (got_mutex)
+ secrets_named_mutex_release(new_conn->controller);
+
+ if (NT_STATUS_IS_OK(result))
+ break;
+ }
+
+ SAFE_FREE(ipc_username);
+ SAFE_FREE(ipc_domain);
+ SAFE_FREE(ipc_password);
+ SAFE_FREE(machine_password);
+
+ if (!NT_STATUS_IS_OK(result)) {
+ add_failed_connection_entry(domain->name, new_conn->controller, result);
+ return result;
+ }
+
+ /* set the domain if empty; needed for schannel connections */
+ if ( !*new_conn->cli->domain )
+ fstrcpy( new_conn->cli->domain, domain->name );
+
+
+ if ( !cli_nt_session_open (new_conn->cli, pipe_index) ) {
+ result = NT_STATUS_PIPE_NOT_AVAILABLE;
+ /*
+ * only cache a failure if we are not trying to open the
+ * **win2k** specific lsarpc UUID. This could be an NT PDC
+ * and therefore a failure is normal. This should probably
+ * be abstracted to a check for 2k specific pipes and wondering
+ * if the PDC is an NT4 box. but since there is only one 2k
+ * specific UUID right now, i'm not going to bother. --jerry
+ */
+ if ( !is_win2k_pipe(pipe_index) )
+ add_failed_connection_entry(domain->name, new_conn->controller, result);
+ cli_shutdown(new_conn->cli);
+ return result;
+ }
+
+ return NT_STATUS_OK;
+}
+
+/************************************************************************
+ Wrapper around statuc cm_open_connection to retreive a freshly
+ setup cli_state struct
+************************************************************************/
+
+NTSTATUS cm_fresh_connection(struct winbindd_domain *domain, const int pipe_index,
+ struct cli_state **cli)
+{
+ NTSTATUS result;
+ struct winbindd_cm_conn conn;
+
+ result = cm_open_connection( domain, pipe_index, &conn );
+
+ if ( NT_STATUS_IS_OK(result) )
+ *cli = conn.cli;
+
+ return result;
+}
+
+/* Return true if a connection is still alive */
+
+static BOOL connection_ok(struct winbindd_cm_conn *conn)
+{
+ if (!conn) {
+ smb_panic("Invalid parameter passed to connection_ok(): conn was NULL!\n");
+ return False;
+ }
+
+ if (!conn->cli) {
+ DEBUG(3, ("Connection to %s for domain %s (pipe %s) has NULL conn->cli!\n",
+ conn->controller, conn->domain, conn->pipe_name));
+ return False;
+ }
+
+ if (!conn->cli->initialised) {
+ DEBUG(3, ("Connection to %s for domain %s (pipe %s) was never initialised!\n",
+ conn->controller, conn->domain, conn->pipe_name));
+ return False;
+ }
+
+ if (conn->cli->fd == -1) {
+ DEBUG(3, ("Connection to %s for domain %s (pipe %s) has died or was never started (fd == -1)\n",
+ conn->controller, conn->domain, conn->pipe_name));
+ return False;
+ }
+
+ return True;
+}
+
+/* Search the cache for a connection. If there is a broken one,
+ shut it down properly and return NULL. */
+
+static void find_cm_connection(struct winbindd_domain *domain, const char *pipe_name,
+ struct winbindd_cm_conn **conn_out)
+{
+ struct winbindd_cm_conn *conn;
+
+ for (conn = cm_conns; conn; ) {
+ if (strequal(conn->domain, domain->name) &&
+ strequal(conn->pipe_name, pipe_name)) {
+ if (!connection_ok(conn)) {
+ /* Dead connection - remove it. */
+ struct winbindd_cm_conn *conn_temp = conn->next;
+ if (conn->cli)
+ cli_shutdown(conn->cli);
+ DLIST_REMOVE(cm_conns, conn);
+ SAFE_FREE(conn);
+ conn = conn_temp; /* Keep the loop moving */
+ continue;
+ } else {
+ break;
+ }
+ }
+ conn = conn->next;
+ }
+
+ *conn_out = conn;
+}
+
+/* Initialize a new connection up to the RPC BIND. */
+
+static NTSTATUS new_cm_connection(struct winbindd_domain *domain, const char *pipe_name,
+ struct winbindd_cm_conn **conn_out)
+{
+ struct winbindd_cm_conn *conn;
+ NTSTATUS result;
+
+ if (!(conn = malloc(sizeof(*conn))))
+ return NT_STATUS_NO_MEMORY;
+
+ ZERO_STRUCTP(conn);
+
+ if (!NT_STATUS_IS_OK(result = cm_open_connection(domain, get_pipe_index(pipe_name), conn))) {
+ DEBUG(3, ("Could not open a connection to %s for %s (%s)\n",
+ domain->name, pipe_name, nt_errstr(result)));
+ SAFE_FREE(conn);
+ return result;
+ }
+ DLIST_ADD(cm_conns, conn);
+
+ *conn_out = conn;
+ return NT_STATUS_OK;
+}
+
+/* Get a connection to the remote DC and open the pipe. If there is already a connection, use that */
+
+static NTSTATUS get_connection_from_cache(struct winbindd_domain *domain, const char *pipe_name,
+ struct winbindd_cm_conn **conn_out)
+{
+ find_cm_connection(domain, pipe_name, conn_out);
+
+ if (*conn_out != NULL)
+ return NT_STATUS_OK;
+
+ return new_cm_connection(domain, pipe_name, conn_out);
+}
+
+/**********************************************************************************
+ We can 'sense' certain things about the DC by it's replies to certain questions.
+
+ This tells us if this particular remote server is Active Directory, and if it is
+ native mode.
+**********************************************************************************/
+
+void set_dc_type_and_flags( struct winbindd_domain *domain )
+{
+ NTSTATUS result;
+ struct winbindd_cm_conn conn;
+ DS_DOMINFO_CTR ctr;
+ TALLOC_CTX *mem_ctx = NULL;
+
+ ZERO_STRUCT( conn );
+ ZERO_STRUCT( ctr );
+
+ domain->native_mode = False;
+ domain->active_directory = False;
+
+ if ( !NT_STATUS_IS_OK(result = cm_open_connection(domain, PI_LSARPC_DS, &conn)) ) {
+ DEBUG(5, ("set_dc_type_and_flags: Could not open a connection to %s for PIPE_LSARPC (%s)\n",
+ domain->name, nt_errstr(result)));
+ return;
+ }
+
+ if ( conn.cli ) {
+ if ( !NT_STATUS_IS_OK(cli_ds_getprimarydominfo( conn.cli,
+ conn.cli->mem_ctx, DsRolePrimaryDomainInfoBasic, &ctr)) ) {
+ goto done;
+ }
+ }
+
+ if ( (ctr.basic->flags & DSROLE_PRIMARY_DS_RUNNING)
+ && !(ctr.basic->flags & DSROLE_PRIMARY_DS_MIXED_MODE) )
+ domain->native_mode = True;
+
+ /* Cheat - shut down the DS pipe, and open LSA */
+
+ cli_nt_session_close(conn.cli);
+
+ if ( cli_nt_session_open (conn.cli, PI_LSARPC) ) {
+ char *domain_name = NULL;
+ char *dns_name = NULL;
+ DOM_SID *dom_sid = NULL;
+
+ mem_ctx = talloc_init("set_dc_type_and_flags on domain %s\n", domain->name);
+ if (!mem_ctx) {
+ DEBUG(1, ("set_dc_type_and_flags: talloc_init() failed\n"));
+ return;
+ }
+
+ result = cli_lsa_open_policy2(conn.cli, mem_ctx, True,
+ SEC_RIGHTS_MAXIMUM_ALLOWED,
+ &conn.pol);
+
+ if (NT_STATUS_IS_OK(result)) {
+ /* This particular query is exactly what Win2k clients use
+ to determine that the DC is active directory */
+ result = cli_lsa_query_info_policy2(conn.cli, mem_ctx,
+ &conn.pol,
+ 12, &domain_name,
+ &dns_name, NULL,
+ NULL, &dom_sid);
+ }
+
+ if (NT_STATUS_IS_OK(result)) {
+ if (domain_name)
+ fstrcpy(domain->name, domain_name);
+
+ if (dns_name)
+ fstrcpy(domain->alt_name, dns_name);
+
+ if (dom_sid)
+ sid_copy(&domain->sid, dom_sid);
+
+ domain->active_directory = True;
+ } else {
+
+ result = cli_lsa_open_policy(conn.cli, mem_ctx, True,
+ SEC_RIGHTS_MAXIMUM_ALLOWED,
+ &conn.pol);
+
+ if (!NT_STATUS_IS_OK(result))
+ goto done;
+
+ result = cli_lsa_query_info_policy(conn.cli, mem_ctx,
+ &conn.pol, 5, &domain_name,
+ &dom_sid);
+
+ if (NT_STATUS_IS_OK(result)) {
+ if (domain_name)
+ fstrcpy(domain->name, domain_name);
+
+ if (dom_sid)
+ sid_copy(&domain->sid, dom_sid);
+ }
+ }
+ }
+
+done:
+
+ /* close the connection; no other calls use this pipe and it is called only
+ on reestablishing the domain list --jerry */
+
+ if ( conn.cli )
+ cli_shutdown( conn.cli );
+
+ talloc_destroy(mem_ctx);
+
+ return;
+}
+
+
+
+/* Return a LSA policy handle on a domain */
+
+NTSTATUS cm_get_lsa_handle(struct winbindd_domain *domain, CLI_POLICY_HND **return_hnd)
+{
+ struct winbindd_cm_conn *conn;
+ uint32 des_access = SEC_RIGHTS_MAXIMUM_ALLOWED;
+ NTSTATUS result;
+ static CLI_POLICY_HND hnd;
+
+ /* Look for existing connections */
+
+ if (!NT_STATUS_IS_OK(result = get_connection_from_cache(domain, PIPE_LSARPC, &conn)))
+ return result;
+
+ /* This *shitty* code needs scrapping ! JRA */
+
+ if (policy_handle_is_valid(&conn->pol)) {
+ hnd.pol = conn->pol;
+ hnd.cli = conn->cli;
+ *return_hnd = &hnd;
+
+ return NT_STATUS_OK;
+ }
+
+ result = cli_lsa_open_policy(conn->cli, conn->cli->mem_ctx, False,
+ des_access, &conn->pol);
+
+ if (!NT_STATUS_IS_OK(result)) {
+ /* Hit the cache code again. This cleans out the old connection and gets a new one */
+ if (conn->cli->fd == -1) { /* Try again, if the remote host disapeared */
+ if (!NT_STATUS_IS_OK(result = get_connection_from_cache(domain, PIPE_LSARPC, &conn)))
+ return result;
+
+ result = cli_lsa_open_policy(conn->cli, conn->cli->mem_ctx, False,
+ des_access, &conn->pol);
+ }
+
+ if (!NT_STATUS_IS_OK(result)) {
+ cli_shutdown(conn->cli);
+ DLIST_REMOVE(cm_conns, conn);
+ SAFE_FREE(conn);
+ return result;
+ }
+ }
+
+ hnd.pol = conn->pol;
+ hnd.cli = conn->cli;
+
+ *return_hnd = &hnd;
+
+ return NT_STATUS_OK;
+}
+
+/* Return a SAM policy handle on a domain */
+
+NTSTATUS cm_get_sam_handle(struct winbindd_domain *domain, CLI_POLICY_HND **return_hnd)
+{
+ struct winbindd_cm_conn *conn;
+ uint32 des_access = SEC_RIGHTS_MAXIMUM_ALLOWED;
+ NTSTATUS result;
+ static CLI_POLICY_HND hnd;
+
+ /* Look for existing connections */
+
+ if (!NT_STATUS_IS_OK(result = get_connection_from_cache(domain, PIPE_SAMR, &conn)))
+ return result;
+
+ /* This *shitty* code needs scrapping ! JRA */
+
+ if (policy_handle_is_valid(&conn->pol)) {
+ hnd.pol = conn->pol;
+ hnd.cli = conn->cli;
+
+ *return_hnd = &hnd;
+
+ return NT_STATUS_OK;
+ }
+
+ result = cli_samr_connect(conn->cli, conn->cli->mem_ctx,
+ des_access, &conn->pol);
+
+ if (!NT_STATUS_IS_OK(result)) {
+ /* Hit the cache code again. This cleans out the old connection and gets a new one */
+ if (conn->cli->fd == -1) { /* Try again, if the remote host disapeared */
+
+ if (!NT_STATUS_IS_OK(result = get_connection_from_cache(domain, PIPE_SAMR, &conn)))
+ return result;
+
+ result = cli_samr_connect(conn->cli, conn->cli->mem_ctx,
+ des_access, &conn->pol);
+ }
+
+ if (!NT_STATUS_IS_OK(result)) {
+
+ cli_shutdown(conn->cli);
+ DLIST_REMOVE(cm_conns, conn);
+ SAFE_FREE(conn);
+
+ return result;
+ }
+ }
+
+ hnd.pol = conn->pol;
+ hnd.cli = conn->cli;
+
+ *return_hnd = &hnd;
+
+ return NT_STATUS_OK;
+}
+
+/* Get a handle on a netlogon pipe. This is a bit of a hack to re-use the
+ netlogon pipe as no handle is returned. */
+
+NTSTATUS cm_get_netlogon_cli(struct winbindd_domain *domain,
+ const unsigned char *trust_passwd,
+ uint32 sec_channel_type,
+ BOOL fresh,
+ struct cli_state **cli)
+{
+ NTSTATUS result = NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND;
+ struct winbindd_cm_conn *conn;
+ fstring lock_name;
+ BOOL got_mutex;
+
+ if (!cli)
+ return NT_STATUS_INVALID_PARAMETER;
+
+ /* Open an initial conection - keep the mutex. */
+
+ find_cm_connection(domain, PIPE_NETLOGON, &conn);
+
+ if ( fresh && (conn != NULL) ) {
+ cli_shutdown(conn->cli);
+ conn->cli = NULL;
+
+ conn = NULL;
+
+ /* purge connection from cache */
+ find_cm_connection(domain, PIPE_NETLOGON, &conn);
+ if (conn != NULL) {
+ DEBUG(0,("Could not purge connection\n"));
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+ }
+
+ if (conn != NULL) {
+ *cli = conn->cli;
+ return NT_STATUS_OK;
+ }
+
+ result = new_cm_connection(domain, PIPE_NETLOGON, &conn);
+
+ if (!NT_STATUS_IS_OK(result))
+ return result;
+
+ fstr_sprintf(lock_name, "NETLOGON\\%s", conn->controller);
+
+ if (!(got_mutex = secrets_named_mutex(lock_name, WINBIND_SERVER_MUTEX_WAIT_TIME))) {
+ DEBUG(0,("cm_get_netlogon_cli: mutex grab failed for %s\n", conn->controller));
+ }
+
+ if ( sec_channel_type == SEC_CHAN_DOMAIN )
+ fstr_sprintf(conn->cli->mach_acct, "%s$", lp_workgroup());
+
+ /* This must be the remote domain (not ours) for schannel */
+
+ fstrcpy( conn->cli->domain, domain->name);
+
+ result = cli_nt_establish_netlogon(conn->cli, sec_channel_type, trust_passwd);
+
+ if (got_mutex)
+ secrets_named_mutex_release(lock_name);
+
+ if (!NT_STATUS_IS_OK(result)) {
+ cli_shutdown(conn->cli);
+ DLIST_REMOVE(cm_conns, conn);
+ SAFE_FREE(conn);
+ return result;
+ }
+
+ *cli = conn->cli;
+
+ return result;
+}
+
+/* Dump the current connection status */
+
+static void dump_conn_list(void)
+{
+ struct winbindd_cm_conn *con;
+
+ DEBUG(0, ("\tDomain Controller Pipe\n"));
+
+ for(con = cm_conns; con; con = con->next) {
+ char *msg;
+
+ /* Display pipe info */
+
+ if (asprintf(&msg, "\t%-15s %-15s %-16s", con->domain, con->controller, con->pipe_name) < 0) {
+ DEBUG(0, ("Error: not enough memory!\n"));
+ } else {
+ DEBUG(0, ("%s\n", msg));
+ SAFE_FREE(msg);
+ }
+ }
+}
+
+void winbindd_cm_status(void)
+{
+ /* List open connections */
+
+ DEBUG(0, ("winbindd connection manager status:\n"));
+
+ if (cm_conns)
+ dump_conn_list();
+ else
+ DEBUG(0, ("\tNo active connections\n"));
+}
+
+/* Close all cached connections */
+
+void winbindd_cm_flush(void)
+{
+ struct winbindd_cm_conn *conn, tmp;
+
+ /* Flush connection cache */
+
+ for (conn = cm_conns; conn; conn = conn->next) {
+
+ if (!connection_ok(conn))
+ continue;
+
+ DEBUG(10, ("Closing connection to %s on %s\n",
+ conn->pipe_name, conn->controller));
+
+ if (conn->cli)
+ cli_shutdown(conn->cli);
+
+ tmp.next = conn->next;
+
+ DLIST_REMOVE(cm_conns, conn);
+ SAFE_FREE(conn);
+ conn = &tmp;
+ }
+
+ /* Flush failed connection cache */
+
+ flush_negative_conn_cache();
+}
diff --git a/source/nsswitch/winbindd_dual.c b/source/nsswitch/winbindd_dual.c
new file mode 100644
index 00000000000..a9796afa367
--- /dev/null
+++ b/source/nsswitch/winbindd_dual.c
@@ -0,0 +1,214 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ Winbind background daemon
+
+ 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 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.
+*/
+
+/*
+ the idea of the optional dual daemon mode is ot prevent slow domain
+ responses from clagging up the rest of the system. When in dual
+ daemon mode winbindd always responds to requests from cache if the
+ request is in cache, and if the cached answer is stale then it asks
+ the "dual daemon" to update the cache for that request
+
+ */
+
+#include "includes.h"
+#include "winbindd.h"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_WINBIND
+
+extern BOOL opt_dual_daemon;
+BOOL background_process = False;
+int dual_daemon_pipe = -1;
+
+
+/* a list of requests ready to be sent to the dual daemon */
+struct dual_list {
+ struct dual_list *next;
+ char *data;
+ int length;
+ int offset;
+};
+
+static struct dual_list *dual_list;
+static struct dual_list *dual_list_end;
+
+/*
+ setup a select() including the dual daemon pipe
+ */
+int dual_select_setup(fd_set *fds, int maxfd)
+{
+ if (dual_daemon_pipe == -1 ||
+ !dual_list) {
+ return maxfd;
+ }
+
+ FD_SET(dual_daemon_pipe, fds);
+ if (dual_daemon_pipe > maxfd) {
+ maxfd = dual_daemon_pipe;
+ }
+ return maxfd;
+}
+
+
+/*
+ a hook called from the main winbindd select() loop to handle writes
+ to the dual daemon pipe
+*/
+void dual_select(fd_set *fds)
+{
+ int n;
+
+ if (dual_daemon_pipe == -1 ||
+ !dual_list ||
+ !FD_ISSET(dual_daemon_pipe, fds)) {
+ return;
+ }
+
+ n = sys_write(dual_daemon_pipe,
+ &dual_list->data[dual_list->offset],
+ dual_list->length - dual_list->offset);
+
+ if (n <= 0) {
+ /* the pipe is dead! fall back to normal operation */
+ dual_daemon_pipe = -1;
+ return;
+ }
+
+ dual_list->offset += n;
+
+ if (dual_list->offset == dual_list->length) {
+ struct dual_list *next;
+ next = dual_list->next;
+ free(dual_list->data);
+ free(dual_list);
+ dual_list = next;
+ if (!dual_list) {
+ dual_list_end = NULL;
+ }
+ }
+}
+
+/*
+ send a request to the background daemon
+ this is called for stale cached entries
+*/
+void dual_send_request(struct winbindd_cli_state *state)
+{
+ struct dual_list *list;
+
+ if (!background_process) return;
+
+ list = malloc(sizeof(*list));
+ if (!list) return;
+
+ list->next = NULL;
+ list->data = memdup(&state->request, sizeof(state->request));
+ list->length = sizeof(state->request);
+ list->offset = 0;
+
+ if (!dual_list_end) {
+ dual_list = list;
+ dual_list_end = list;
+ } else {
+ dual_list_end->next = list;
+ dual_list_end = list;
+ }
+
+ background_process = False;
+}
+
+
+/*
+the main dual daemon
+*/
+void do_dual_daemon(void)
+{
+ int fdpair[2];
+ struct winbindd_cli_state state;
+
+ if (pipe(fdpair) != 0) {
+ return;
+ }
+
+ ZERO_STRUCT(state);
+ state.pid = getpid();
+
+ dual_daemon_pipe = fdpair[1];
+ state.sock = fdpair[0];
+
+ if (fork() != 0) {
+ close(fdpair[0]);
+ return;
+ }
+ close(fdpair[1]);
+
+ /* tdb needs special fork handling */
+ if (tdb_reopen_all() == -1) {
+ DEBUG(0,("tdb_reopen_all failed.\n"));
+ _exit(0);
+ }
+
+ dual_daemon_pipe = -1;
+ opt_dual_daemon = False;
+
+ while (1) {
+ /* free up any talloc memory */
+ lp_talloc_free();
+ main_loop_talloc_free();
+
+ /* fetch a request from the main daemon */
+ winbind_client_read(&state);
+
+ if (state.finished) {
+ /* we lost contact with our parent */
+ exit(0);
+ }
+
+ /* process full rquests */
+ if (state.read_buf_len == sizeof(state.request)) {
+ DEBUG(4,("dual daemon request %d\n", (int)state.request.cmd));
+
+ /* special handling for the stateful requests */
+ switch (state.request.cmd) {
+ case WINBINDD_GETPWENT:
+ winbindd_setpwent(&state);
+ break;
+
+ case WINBINDD_GETGRENT:
+ case WINBINDD_GETGRLST:
+ winbindd_setgrent(&state);
+ break;
+ default:
+ break;
+ }
+
+ winbind_process_packet(&state);
+ SAFE_FREE(state.response.extra_data);
+
+ free_getent_state(state.getpwent_state);
+ free_getent_state(state.getgrent_state);
+ state.getpwent_state = NULL;
+ state.getgrent_state = NULL;
+ }
+ }
+}
+
diff --git a/source/nsswitch/winbindd_group.c b/source/nsswitch/winbindd_group.c
new file mode 100644
index 00000000000..94037e39200
--- /dev/null
+++ b/source/nsswitch/winbindd_group.c
@@ -0,0 +1,1211 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ Winbind daemon for ntdom nss module
+
+ Copyright (C) Tim Potter 2000
+ Copyright (C) Jeremy Allison 2001.
+ Copyright (C) Gerald (Jerry) Carter 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 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 "winbindd.h"
+
+extern BOOL opt_nocache;
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_WINBIND
+
+/*********************************************************************
+*********************************************************************/
+
+static int gr_mem_buffer( char **buffer, char **members, int num_members )
+{
+ int i;
+ int len = 0;
+ int idx = 0;
+
+ if ( num_members == 0 ) {
+ *buffer = NULL;
+ return 0;
+ }
+
+ for ( i=0; i<num_members; i++ )
+ len += strlen(members[i])+1;
+
+ *buffer = (char*)smb_xmalloc(len);
+ for ( i=0; i<num_members; i++ ) {
+ snprintf( &(*buffer)[idx], len-idx, "%s,", members[i]);
+ idx += strlen(members[i])+1;
+ }
+ /* terminate with NULL */
+ (*buffer)[len-1] = '\0';
+
+ return len;
+}
+
+/***************************************************************
+ Empty static struct for negative caching.
+****************************************************************/
+
+/* Fill a grent structure from various other information */
+
+static BOOL fill_grent(struct winbindd_gr *gr, const char *dom_name,
+ const char *gr_name, gid_t unix_gid)
+{
+ fstring full_group_name;
+ /* Fill in uid/gid */
+ fill_domain_username(full_group_name, dom_name, gr_name);
+
+ gr->gr_gid = unix_gid;
+
+ /* Group name and password */
+
+ safe_strcpy(gr->gr_name, full_group_name, sizeof(gr->gr_name) - 1);
+ safe_strcpy(gr->gr_passwd, "x", sizeof(gr->gr_passwd) - 1);
+
+ return True;
+}
+
+/* Fill in the group membership field of a NT group given by group_sid */
+
+static BOOL fill_grent_mem(struct winbindd_domain *domain,
+ DOM_SID *group_sid,
+ enum SID_NAME_USE group_name_type,
+ int *num_gr_mem, char **gr_mem, int *gr_mem_len)
+{
+ DOM_SID **sid_mem = NULL;
+ uint32 num_names = 0;
+ uint32 *name_types = NULL;
+ unsigned int buf_len, buf_ndx, i;
+ char **names = NULL, *buf;
+ BOOL result = False;
+ TALLOC_CTX *mem_ctx;
+ NTSTATUS status;
+ fstring sid_string;
+
+ if (!(mem_ctx = talloc_init("fill_grent_mem(%s)", domain->name)))
+ return False;
+
+ /* Initialise group membership information */
+
+ DEBUG(10, ("group SID %s\n", sid_to_string(sid_string, group_sid)));
+
+ *num_gr_mem = 0;
+
+ /* HACK ALERT!! This whole routine does not cope with group members
+ * from more than one domain, ie aliases. Thus we have to work it out
+ * ourselves in a special routine. */
+
+ if (domain->internal)
+ return fill_passdb_alias_grmem(domain, group_sid,
+ num_gr_mem,
+ gr_mem, gr_mem_len);
+
+ if ( !((group_name_type==SID_NAME_DOM_GRP) ||
+ ((group_name_type==SID_NAME_ALIAS) && domain->primary)) )
+ {
+ DEBUG(1, ("SID %s in domain %s isn't a domain group (%d)\n",
+ sid_to_string(sid_string, group_sid), domain->name,
+ group_name_type));
+ goto done;
+ }
+
+ /* Lookup group members */
+ status = domain->methods->lookup_groupmem(domain, mem_ctx, group_sid, &num_names,
+ &sid_mem, &names, &name_types);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(1, ("could not lookup membership for group rid %s in domain %s (error: %s)\n",
+ sid_to_string(sid_string, group_sid), domain->name, nt_errstr(status)));
+
+ goto done;
+ }
+
+ DEBUG(10, ("looked up %d names\n", num_names));
+
+ if (DEBUGLEVEL >= 10) {
+ for (i = 0; i < num_names; i++)
+ DEBUG(10, ("\t%20s %s %d\n", names[i], sid_to_string(sid_string, sid_mem[i]),
+ name_types[i]));
+ }
+
+ /* Add members to list */
+
+ buf = NULL;
+ buf_len = buf_ndx = 0;
+
+ again:
+
+ for (i = 0; i < num_names; i++) {
+ char *the_name;
+ fstring name;
+ int len;
+
+ the_name = names[i];
+
+ DEBUG(10, ("processing name %s\n", the_name));
+
+ /* FIXME: need to cope with groups within groups. These
+ occur in Universal groups on a Windows 2000 native mode
+ server. */
+
+ /* make sure to allow machine accounts */
+
+ if (name_types[i] != SID_NAME_USER && name_types[i] != SID_NAME_COMPUTER) {
+ DEBUG(3, ("name %s isn't a domain user\n", the_name));
+ continue;
+ }
+
+ /* Append domain name */
+
+ fill_domain_username(name, domain->name, the_name);
+
+ len = strlen(name);
+
+ /* Add to list or calculate buffer length */
+
+ if (!buf) {
+ buf_len += len + 1; /* List is comma separated */
+ (*num_gr_mem)++;
+ DEBUG(10, ("buf_len + %d = %d\n", len + 1, buf_len));
+ } else {
+ DEBUG(10, ("appending %s at ndx %d\n", name, len));
+ safe_strcpy(&buf[buf_ndx], name, len);
+ buf_ndx += len;
+ buf[buf_ndx] = ',';
+ buf_ndx++;
+ }
+ }
+
+ /* Allocate buffer */
+
+ if (!buf && buf_len != 0) {
+ if (!(buf = malloc(buf_len))) {
+ DEBUG(1, ("out of memory\n"));
+ result = False;
+ goto done;
+ }
+ memset(buf, 0, buf_len);
+ goto again;
+ }
+
+ if (buf && buf_ndx > 0) {
+ buf[buf_ndx - 1] = '\0';
+ }
+
+ *gr_mem = buf;
+ *gr_mem_len = buf_len;
+
+ DEBUG(10, ("num_mem = %d, len = %d, mem = %s\n", *num_gr_mem,
+ buf_len, *num_gr_mem ? buf : "NULL"));
+ result = True;
+
+done:
+
+ talloc_destroy(mem_ctx);
+
+ DEBUG(10, ("fill_grent_mem returning %d\n", result));
+
+ return result;
+}
+
+/* Return a group structure from a group name */
+
+enum winbindd_result winbindd_getgrnam(struct winbindd_cli_state *state)
+{
+ DOM_SID group_sid;
+ WINBINDD_GR *grp;
+ struct winbindd_domain *domain;
+ enum SID_NAME_USE name_type;
+ fstring name_domain, name_group;
+ char *tmp, *gr_mem;
+ int gr_mem_len;
+ gid_t gid;
+
+ /* Ensure null termination */
+ state->request.data.groupname[sizeof(state->request.data.groupname)-1]='\0';
+
+ DEBUG(3, ("[%5lu]: getgrnam %s\n", (unsigned long)state->pid,
+ state->request.data.groupname));
+
+ /* Parse domain and groupname */
+
+ memset(name_group, 0, sizeof(fstring));
+
+ tmp = state->request.data.groupname;
+
+ parse_domain_user(tmp, name_domain, name_group);
+
+ /* if no domain or our local domain, then do a local tdb search */
+
+ if ( (!*name_domain || strequal(name_domain, get_global_sam_name())) &&
+ ((grp = wb_getgrnam(name_group)) != NULL) ) {
+
+ char *buffer = NULL;
+
+ memcpy( &state->response.data.gr, grp, sizeof(WINBINDD_GR) );
+
+ gr_mem_len = gr_mem_buffer( &buffer, grp->gr_mem, grp->num_gr_mem );
+
+ state->response.data.gr.gr_mem_ofs = 0;
+ state->response.length += gr_mem_len;
+ state->response.extra_data = buffer; /* give the memory away */
+
+ return WINBINDD_OK;
+ }
+
+ /* if no domain or our local domain and no local tdb group, default to
+ * our local domain for aliases */
+
+ if ( !*name_domain || strequal(name_domain, get_global_sam_name()) ) {
+ fstrcpy(name_domain, get_global_sam_name());
+ }
+
+ /* Get info for the domain */
+
+ if ((domain = find_domain_from_name(name_domain)) == NULL) {
+ DEBUG(3, ("could not get domain sid for domain %s\n",
+ name_domain));
+ return WINBINDD_ERROR;
+ }
+ /* should we deal with users for our domain? */
+
+ if ( lp_winbind_trusted_domains_only() && domain->primary) {
+ DEBUG(7,("winbindd_getgrnam: My domain -- rejecting getgrnam() for %s\\%s.\n",
+ name_domain, name_group));
+ return WINBINDD_ERROR;
+ }
+
+ /* Get rid and name type from name */
+
+ if (!winbindd_lookup_sid_by_name(domain, name_group, &group_sid,
+ &name_type)) {
+ DEBUG(1, ("group %s in domain %s does not exist\n",
+ name_group, name_domain));
+ return WINBINDD_ERROR;
+ }
+
+ if ( !((name_type==SID_NAME_DOM_GRP) ||
+ ((name_type==SID_NAME_ALIAS) && domain->primary) ||
+ ((name_type==SID_NAME_ALIAS) && domain->internal)) )
+ {
+ DEBUG(1, ("name '%s' is not a local or domain group: %d\n",
+ name_group, name_type));
+ return WINBINDD_ERROR;
+ }
+
+ if (!NT_STATUS_IS_OK(idmap_sid_to_gid(&group_sid, &gid, 0))) {
+ DEBUG(1, ("error converting unix gid to sid\n"));
+ return WINBINDD_ERROR;
+ }
+
+ if (!fill_grent(&state->response.data.gr, name_domain,
+ name_group, gid) ||
+ !fill_grent_mem(domain, &group_sid, name_type,
+ &state->response.data.gr.num_gr_mem,
+ &gr_mem, &gr_mem_len)) {
+ return WINBINDD_ERROR;
+ }
+
+ /* Group membership lives at start of extra data */
+
+ state->response.data.gr.gr_mem_ofs = 0;
+
+ state->response.length += gr_mem_len;
+ state->response.extra_data = gr_mem;
+
+ return WINBINDD_OK;
+}
+
+/* Return a group structure from a gid number */
+
+enum winbindd_result winbindd_getgrgid(struct winbindd_cli_state *state)
+{
+ struct winbindd_domain *domain;
+ WINBINDD_GR *grp;
+ DOM_SID group_sid;
+ enum SID_NAME_USE name_type;
+ fstring dom_name;
+ fstring group_name;
+ int gr_mem_len;
+ char *gr_mem;
+
+ DEBUG(3, ("[%5lu]: getgrgid %lu\n", (unsigned long)state->pid,
+ (unsigned long)state->request.data.gid));
+
+ /* Bug out if the gid isn't in the winbind range */
+
+ if ((state->request.data.gid < server_state.gid_low) ||
+ (state->request.data.gid > server_state.gid_high))
+ return WINBINDD_ERROR;
+
+ /* alway try local tdb lookup first */
+ if ( ( grp=wb_getgrgid(state->request.data.gid)) != NULL ) {
+ char *buffer = NULL;
+
+ memcpy( &state->response.data.gr, grp, sizeof(WINBINDD_GR) );
+
+ gr_mem_len = gr_mem_buffer( &buffer, grp->gr_mem, grp->num_gr_mem );
+
+ state->response.data.gr.gr_mem_ofs = 0;
+ state->response.length += gr_mem_len;
+ state->response.extra_data = buffer; /* give away the memory */
+
+ return WINBINDD_OK;
+ }
+
+ /* Get rid from gid */
+ if (!NT_STATUS_IS_OK(idmap_gid_to_sid(&group_sid, state->request.data.gid))) {
+ DEBUG(1, ("could not convert gid %lu to rid\n",
+ (unsigned long)state->request.data.gid));
+ return WINBINDD_ERROR;
+ }
+
+ /* Get name from sid */
+
+ if (!winbindd_lookup_name_by_sid(&group_sid, dom_name, group_name, &name_type)) {
+ DEBUG(1, ("could not lookup sid\n"));
+ return WINBINDD_ERROR;
+ }
+
+ /* Fill in group structure */
+
+ domain = find_domain_from_sid(&group_sid);
+
+ if (!domain) {
+ DEBUG(1,("Can't find domain from sid\n"));
+ return WINBINDD_ERROR;
+ }
+
+ if ( !((name_type==SID_NAME_DOM_GRP) ||
+ ((name_type==SID_NAME_ALIAS) && domain->primary) ||
+ ((name_type==SID_NAME_ALIAS) && domain->internal)) )
+ {
+ DEBUG(1, ("name '%s' is not a local or domain group: %d\n",
+ group_name, name_type));
+ return WINBINDD_ERROR;
+ }
+
+ if (!fill_grent(&state->response.data.gr, dom_name, group_name,
+ state->request.data.gid) ||
+ !fill_grent_mem(domain, &group_sid, name_type,
+ &state->response.data.gr.num_gr_mem,
+ &gr_mem, &gr_mem_len))
+ return WINBINDD_ERROR;
+
+ /* Group membership lives at start of extra data */
+
+ state->response.data.gr.gr_mem_ofs = 0;
+
+ state->response.length += gr_mem_len;
+ state->response.extra_data = gr_mem;
+
+ return WINBINDD_OK;
+}
+
+/*
+ * set/get/endgrent functions
+ */
+
+/* "Rewind" file pointer for group database enumeration */
+
+enum winbindd_result winbindd_setgrent(struct winbindd_cli_state *state)
+{
+ struct winbindd_domain *domain;
+
+ DEBUG(3, ("[%5lu]: setgrent\n", (unsigned long)state->pid));
+
+ /* Check user has enabled this */
+
+ if (!lp_winbind_enum_groups())
+ return WINBINDD_ERROR;
+
+ /* Free old static data if it exists */
+
+ if (state->getgrent_state != NULL) {
+ free_getent_state(state->getgrent_state);
+ state->getgrent_state = NULL;
+ }
+
+ /* Create sam pipes for each domain we know about */
+
+ for (domain = domain_list(); domain != NULL; domain = domain->next) {
+ struct getent_state *domain_state;
+
+
+ /* don't add our domaina if we are a PDC or if we
+ are a member of a Samba domain */
+
+ if ( (IS_DC || lp_winbind_trusted_domains_only())
+ && domain->primary )
+ {
+ continue;
+ }
+
+ /* Create a state record for this domain */
+
+ if ((domain_state = (struct getent_state *)
+ malloc(sizeof(struct getent_state))) == NULL) {
+ DEBUG(1, ("winbindd_setgrent: malloc failed for domain_state!\n"));
+ return WINBINDD_ERROR;
+ }
+
+ ZERO_STRUCTP(domain_state);
+
+ fstrcpy(domain_state->domain_name, domain->name);
+
+ /* Add to list of open domains */
+
+ DLIST_ADD(state->getgrent_state, domain_state);
+ }
+
+ return WINBINDD_OK;
+}
+
+/* Close file pointer to ntdom group database */
+
+enum winbindd_result winbindd_endgrent(struct winbindd_cli_state *state)
+{
+ DEBUG(3, ("[%5lu]: endgrent\n", (unsigned long)state->pid));
+
+ free_getent_state(state->getgrent_state);
+ state->getgrent_state = NULL;
+
+ return WINBINDD_OK;
+}
+
+/* Get the list of domain groups and domain aliases for a domain. We fill in
+ the sam_entries and num_sam_entries fields with domain group information.
+ The dispinfo_ndx field is incremented to the index of the next group to
+ fetch. Return True if some groups were returned, False otherwise. */
+
+#define MAX_FETCH_SAM_ENTRIES 100
+
+static BOOL get_sam_group_entries(struct getent_state *ent)
+{
+ NTSTATUS status;
+ uint32 num_entries;
+ struct acct_info *name_list = NULL, *tmp_name_list = NULL;
+ TALLOC_CTX *mem_ctx;
+ BOOL result = False;
+ struct acct_info *sam_grp_entries = NULL;
+ struct winbindd_domain *domain;
+
+ if (ent->got_sam_entries)
+ return False;
+
+ if (!(mem_ctx = talloc_init("get_sam_group_entries(%s)",
+ ent->domain_name))) {
+ DEBUG(1, ("get_sam_group_entries: could not create talloc context!\n"));
+ return False;
+ }
+
+ /* Free any existing group info */
+
+ SAFE_FREE(ent->sam_entries);
+ ent->num_sam_entries = 0;
+ ent->got_sam_entries = True;
+
+ /* Enumerate domain groups */
+
+ num_entries = 0;
+
+ if (!(domain = find_domain_from_name(ent->domain_name))) {
+ DEBUG(3, ("no such domain %s in get_sam_group_entries\n", ent->domain_name));
+ goto done;
+ }
+
+ /* always get the domain global groups */
+
+ status = domain->methods->enum_dom_groups(domain, mem_ctx, &num_entries, &sam_grp_entries);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(3, ("get_sam_group_entries: could not enumerate domain groups! Error: %s\n", nt_errstr(status)));
+ result = False;
+ goto done;
+ }
+
+ /* Copy entries into return buffer */
+
+ if (num_entries) {
+ if ( !(name_list = malloc(sizeof(struct acct_info) * num_entries)) ) {
+ DEBUG(0,("get_sam_group_entries: Failed to malloc memory for %d domain groups!\n",
+ num_entries));
+ result = False;
+ goto done;
+ }
+ memcpy( name_list, sam_grp_entries, num_entries * sizeof(struct acct_info) );
+ }
+
+ ent->num_sam_entries = num_entries;
+
+ /* get the domain local groups if we are a member of a native win2k domain
+ and are not using LDAP to get the groups */
+
+ if ( ( lp_security() != SEC_ADS && domain->native_mode
+ && domain->primary) || domain->internal )
+ {
+ DEBUG(4,("get_sam_group_entries: Native Mode 2k domain; enumerating local groups as well\n"));
+
+ status = domain->methods->enum_local_groups(domain, mem_ctx, &num_entries, &sam_grp_entries);
+
+ if ( !NT_STATUS_IS_OK(status) ) {
+ DEBUG(3,("get_sam_group_entries: Failed to enumerate domain local groups!\n"));
+ num_entries = 0;
+ }
+ else
+ DEBUG(4,("get_sam_group_entries: Returned %d local groups\n", num_entries));
+
+ /* Copy entries into return buffer */
+
+ if ( num_entries ) {
+ if ( !(tmp_name_list = Realloc( name_list, sizeof(struct acct_info) * (ent->num_sam_entries+num_entries))) )
+ {
+ DEBUG(0,("get_sam_group_entries: Failed to realloc more memory for %d local groups!\n",
+ num_entries));
+ result = False;
+ SAFE_FREE( name_list );
+ goto done;
+ }
+
+ name_list = tmp_name_list;
+
+ memcpy( &name_list[ent->num_sam_entries], sam_grp_entries,
+ num_entries * sizeof(struct acct_info) );
+ }
+
+ ent->num_sam_entries += num_entries;
+ }
+
+
+ /* Fill in remaining fields */
+
+ ent->sam_entries = name_list;
+ ent->sam_entry_index = 0;
+
+ result = (ent->num_sam_entries > 0);
+
+ done:
+ talloc_destroy(mem_ctx);
+
+ return result;
+}
+
+/* Fetch next group entry from ntdom database */
+
+#define MAX_GETGRENT_GROUPS 500
+
+enum winbindd_result winbindd_getgrent(struct winbindd_cli_state *state)
+{
+ struct getent_state *ent;
+ struct winbindd_gr *group_list = NULL;
+ int num_groups, group_list_ndx = 0, i, gr_mem_list_len = 0;
+ char *new_extra_data, *gr_mem_list = NULL;
+
+ DEBUG(3, ("[%5lu]: getgrent\n", (unsigned long)state->pid));
+
+ /* Check user has enabled this */
+
+ if (!lp_winbind_enum_groups())
+ return WINBINDD_ERROR;
+
+ num_groups = MIN(MAX_GETGRENT_GROUPS, state->request.data.num_entries);
+
+ if ((state->response.extra_data =
+ malloc(num_groups * sizeof(struct winbindd_gr))) == NULL)
+ return WINBINDD_ERROR;
+
+ state->response.data.num_entries = 0;
+
+ group_list = (struct winbindd_gr *)state->response.extra_data;
+
+ if (!(ent = state->getgrent_state))
+ return WINBINDD_ERROR;
+
+ /* Start sending back groups */
+
+ for (i = 0; i < num_groups; i++) {
+ struct acct_info *name_list = NULL;
+ fstring domain_group_name;
+ uint32 result;
+ gid_t group_gid;
+ int gr_mem_len;
+ char *gr_mem, *new_gr_mem_list;
+ DOM_SID group_sid;
+ struct winbindd_domain *domain;
+
+ /* Do we need to fetch another chunk of groups? */
+
+ tryagain:
+
+ DEBUG(10, ("entry_index = %d, num_entries = %d\n",
+ ent->sam_entry_index, ent->num_sam_entries));
+
+ if (ent->num_sam_entries == ent->sam_entry_index) {
+
+ while(ent && !get_sam_group_entries(ent)) {
+ struct getent_state *next_ent;
+
+ DEBUG(10, ("freeing state info for domain %s\n", ent->domain_name));
+
+ /* Free state information for this domain */
+
+ SAFE_FREE(ent->sam_entries);
+
+ next_ent = ent->next;
+ DLIST_REMOVE(state->getgrent_state, ent);
+
+ SAFE_FREE(ent);
+ ent = next_ent;
+ }
+
+ /* No more domains */
+
+ if (!ent)
+ break;
+ }
+
+ name_list = ent->sam_entries;
+
+ if (!(domain =
+ find_domain_from_name(ent->domain_name))) {
+ DEBUG(3, ("No such domain %s in winbindd_getgrent\n", ent->domain_name));
+ result = False;
+ goto done;
+ }
+
+ /* Lookup group info */
+
+ sid_copy(&group_sid, &domain->sid);
+ sid_append_rid(&group_sid, name_list[ent->sam_entry_index].rid);
+
+ if (!NT_STATUS_IS_OK(idmap_sid_to_gid(&group_sid, &group_gid, 0))) {
+
+ DEBUG(1, ("could not look up gid for group %s\n",
+ name_list[ent->sam_entry_index].acct_name));
+
+ ent->sam_entry_index++;
+ goto tryagain;
+ }
+
+ DEBUG(10, ("got gid %lu for group %lu\n", (unsigned long)group_gid,
+ (unsigned long)name_list[ent->sam_entry_index].rid));
+
+ /* Fill in group entry */
+
+ fill_domain_username(domain_group_name, ent->domain_name,
+ name_list[ent->sam_entry_index].acct_name);
+
+ result = fill_grent(&group_list[group_list_ndx],
+ ent->domain_name,
+ name_list[ent->sam_entry_index].acct_name,
+ group_gid);
+
+ /* Fill in group membership entry */
+
+ if (result) {
+ DOM_SID member_sid;
+ group_list[group_list_ndx].num_gr_mem = 0;
+ gr_mem = NULL;
+ gr_mem_len = 0;
+
+ /* Get group membership */
+ if (state->request.cmd == WINBINDD_GETGRLST) {
+ result = True;
+ } else {
+ sid_copy(&member_sid, &domain->sid);
+ sid_append_rid(&member_sid, name_list[ent->sam_entry_index].rid);
+ result = fill_grent_mem(
+ domain,
+ &member_sid,
+ SID_NAME_DOM_GRP,
+ &group_list[group_list_ndx].num_gr_mem,
+ &gr_mem, &gr_mem_len);
+ }
+ }
+
+ if (result) {
+ /* Append to group membership list */
+ new_gr_mem_list = Realloc(
+ gr_mem_list,
+ gr_mem_list_len + gr_mem_len);
+
+ if (!new_gr_mem_list && (group_list[group_list_ndx].num_gr_mem != 0)) {
+ DEBUG(0, ("out of memory\n"));
+ SAFE_FREE(gr_mem_list);
+ gr_mem_list_len = 0;
+ break;
+ }
+
+ DEBUG(10, ("list_len = %d, mem_len = %d\n",
+ gr_mem_list_len, gr_mem_len));
+
+ gr_mem_list = new_gr_mem_list;
+
+ memcpy(&gr_mem_list[gr_mem_list_len], gr_mem,
+ gr_mem_len);
+
+ SAFE_FREE(gr_mem);
+
+ group_list[group_list_ndx].gr_mem_ofs =
+ gr_mem_list_len;
+
+ gr_mem_list_len += gr_mem_len;
+ }
+
+ ent->sam_entry_index++;
+
+ /* Add group to return list */
+
+ if (result) {
+
+ DEBUG(10, ("adding group num_entries = %d\n",
+ state->response.data.num_entries));
+
+ group_list_ndx++;
+ state->response.data.num_entries++;
+
+ state->response.length +=
+ sizeof(struct winbindd_gr);
+
+ } else {
+ DEBUG(0, ("could not lookup domain group %s\n",
+ domain_group_name));
+ }
+ }
+
+ /* Copy the list of group memberships to the end of the extra data */
+
+ if (group_list_ndx == 0)
+ goto done;
+
+ new_extra_data = Realloc(
+ state->response.extra_data,
+ group_list_ndx * sizeof(struct winbindd_gr) + gr_mem_list_len);
+
+ if (!new_extra_data) {
+ DEBUG(0, ("out of memory\n"));
+ group_list_ndx = 0;
+ SAFE_FREE(state->response.extra_data);
+ SAFE_FREE(gr_mem_list);
+
+ return WINBINDD_ERROR;
+ }
+
+ state->response.extra_data = new_extra_data;
+
+ memcpy(&((char *)state->response.extra_data)
+ [group_list_ndx * sizeof(struct winbindd_gr)],
+ gr_mem_list, gr_mem_list_len);
+
+ SAFE_FREE(gr_mem_list);
+
+ state->response.length += gr_mem_list_len;
+
+ DEBUG(10, ("returning %d groups, length = %d\n",
+ group_list_ndx, gr_mem_list_len));
+
+ /* Out of domains */
+
+ done:
+
+ return (group_list_ndx > 0) ? WINBINDD_OK : WINBINDD_ERROR;
+}
+
+/* List domain groups without mapping to unix ids */
+
+enum winbindd_result winbindd_list_groups(struct winbindd_cli_state *state)
+{
+ uint32 total_entries = 0;
+ struct winbindd_domain *domain;
+ const char *which_domain;
+ char *extra_data = NULL;
+ char *ted = NULL;
+ unsigned int extra_data_len = 0, i;
+
+ DEBUG(3, ("[%5lu]: list groups\n", (unsigned long)state->pid));
+
+ /* Ensure null termination */
+ state->request.domain_name[sizeof(state->request.domain_name)-1]='\0';
+ which_domain = state->request.domain_name;
+
+ /* Enumerate over trusted domains */
+
+ for (domain = domain_list(); domain; domain = domain->next) {
+ struct getent_state groups;
+
+ /* if we have a domain name restricting the request and this
+ one in the list doesn't match, then just bypass the remainder
+ of the loop */
+
+ if ( *which_domain && !strequal(which_domain, domain->name) )
+ continue;
+
+ ZERO_STRUCT(groups);
+
+ /* Get list of sam groups */
+
+ fstrcpy(groups.domain_name, domain->name);
+
+ get_sam_group_entries(&groups);
+
+ if (groups.num_sam_entries == 0) {
+ /* this domain is empty or in an error state */
+ continue;
+ }
+
+ /* keep track the of the total number of groups seen so
+ far over all domains */
+ total_entries += groups.num_sam_entries;
+
+ /* Allocate some memory for extra data. Note that we limit
+ account names to sizeof(fstring) = 128 characters. */
+ ted = Realloc(extra_data, sizeof(fstring) * total_entries);
+
+ if (!ted) {
+ DEBUG(0,("failed to enlarge buffer!\n"));
+ SAFE_FREE(extra_data);
+ return WINBINDD_ERROR;
+ } else
+ extra_data = ted;
+
+ /* Pack group list into extra data fields */
+ for (i = 0; i < groups.num_sam_entries; i++) {
+ char *group_name = ((struct acct_info *)
+ groups.sam_entries)[i].acct_name;
+ fstring name;
+
+ fill_domain_username(name, domain->name, group_name);
+ /* Append to extra data */
+ memcpy(&extra_data[extra_data_len], name,
+ strlen(name));
+ extra_data_len += strlen(name);
+ extra_data[extra_data_len++] = ',';
+ }
+
+ SAFE_FREE(groups.sam_entries);
+ }
+
+ /* Assign extra_data fields in response structure */
+ if (extra_data) {
+ extra_data[extra_data_len - 1] = '\0';
+ state->response.extra_data = extra_data;
+ state->response.length += extra_data_len;
+ }
+
+ /* No domains may have responded but that's still OK so don't
+ return an error. */
+
+ return WINBINDD_OK;
+}
+
+static void add_gid_to_array_unique(gid_t gid, gid_t **gids, int *num)
+{
+ int i;
+
+ if ((*num) >= groups_max())
+ return;
+
+ for (i=0; i<*num; i++) {
+ if ((*gids)[i] == gid)
+ return;
+ }
+
+ *gids = Realloc(*gids, (*num+1) * sizeof(gid_t));
+
+ if (*gids == NULL)
+ return;
+
+ (*gids)[*num] = gid;
+ *num += 1;
+}
+
+static void add_gids_from_sid(DOM_SID *sid, gid_t **gids, int *num)
+{
+ gid_t gid;
+ DOM_SID *aliases;
+ int j, num_aliases;
+
+ DEBUG(10, ("Adding gids from SID: %s\n", sid_string_static(sid)));
+
+ if (NT_STATUS_IS_OK(idmap_sid_to_gid(sid, &gid, 0)))
+ add_gid_to_array_unique(gid, gids, num);
+
+ /* Add nested group memberships */
+
+ if (!pdb_enum_alias_memberships(sid, &aliases, &num_aliases))
+ return;
+
+ for (j=0; j<num_aliases; j++) {
+
+ if (!NT_STATUS_IS_OK(sid_to_gid(&aliases[j], &gid)))
+ continue;
+
+ add_gid_to_array_unique(gid, gids, num);
+ }
+ SAFE_FREE(aliases);
+}
+
+/* Get user supplementary groups. This is much quicker than trying to
+ invert the groups database. We merge the groups from the gids and
+ other_sids info3 fields as trusted domain, universal group
+ memberships, and nested groups (win2k native mode only) are not
+ returned by the getgroups RPC call but are present in the info3. */
+
+enum winbindd_result winbindd_getgroups(struct winbindd_cli_state *state)
+{
+ fstring name_domain, name_user;
+ DOM_SID user_sid, group_sid;
+ enum SID_NAME_USE name_type;
+ uint32 num_groups = 0;
+ uint32 num_gids = 0;
+ NTSTATUS status;
+ DOM_SID **user_grpsids;
+ struct winbindd_domain *domain;
+ enum winbindd_result result = WINBINDD_ERROR;
+ gid_t *gid_list = NULL;
+ unsigned int i;
+ TALLOC_CTX *mem_ctx;
+ NET_USER_INFO_3 *info3 = NULL;
+
+ /* Ensure null termination */
+ state->request.data.username[sizeof(state->request.data.username)-1]='\0';
+
+ DEBUG(3, ("[%5lu]: getgroups %s\n", (unsigned long)state->pid,
+ state->request.data.username));
+
+ if (!(mem_ctx = talloc_init("winbindd_getgroups(%s)",
+ state->request.data.username)))
+ return WINBINDD_ERROR;
+
+ /* Parse domain and username */
+
+ parse_domain_user(state->request.data.username,
+ name_domain, name_user);
+
+ /* Get info for the domain */
+
+ if ((domain = find_domain_from_name(name_domain)) == NULL) {
+ DEBUG(7, ("could not find domain entry for domain %s\n",
+ name_domain));
+ goto done;
+ }
+
+ if ( domain->primary && lp_winbind_trusted_domains_only()) {
+ DEBUG(7,("winbindd_getpwnam: My domain -- rejecting getgroups() for %s\\%s.\n",
+ name_domain, name_user));
+ return WINBINDD_ERROR;
+ }
+
+ /* Get rid and name type from name. The following costs 1 packet */
+
+ if (!winbindd_lookup_sid_by_name(domain, name_user, &user_sid,
+ &name_type)) {
+ DEBUG(1, ("user '%s' does not exist\n", name_user));
+ goto done;
+ }
+
+ if (name_type != SID_NAME_USER && name_type != SID_NAME_COMPUTER) {
+ DEBUG(1, ("name '%s' is not a user name: %d\n",
+ name_user, name_type));
+ goto done;
+ }
+
+ add_gids_from_sid(&user_sid, &gid_list, &num_gids);
+
+ /* Treat the info3 cache as authoritative as the
+ lookup_usergroups() function may return cached data. */
+
+ if ( !opt_nocache && (info3 = netsamlogon_cache_get(mem_ctx, &user_sid))) {
+
+ DEBUG(10, ("winbindd_getgroups: info3 has %d groups, %d other sids\n",
+ info3->num_groups2, info3->num_other_sids));
+
+ num_groups = info3->num_other_sids + info3->num_groups2;
+
+ /* Go through each other sid and convert it to a gid */
+
+ for (i = 0; i < info3->num_other_sids; i++) {
+ fstring name;
+ fstring dom_name;
+ enum SID_NAME_USE sid_type;
+
+ /* Is this sid known to us? It can either be
+ a trusted domain sid or a foreign sid. */
+
+ if (!winbindd_lookup_name_by_sid( &info3->other_sids[i].sid,
+ dom_name, name, &sid_type))
+ {
+ DEBUG(10, ("winbindd_getgroups: could not lookup name for %s\n",
+ sid_string_static(&info3->other_sids[i].sid)));
+ continue;
+ }
+
+ /* Check it is a domain group or an alias (domain local group)
+ in a win2k native mode domain. */
+
+ if ( !((sid_type==SID_NAME_DOM_GRP) ||
+ ((sid_type==SID_NAME_ALIAS) && domain->primary)) )
+ {
+ DEBUG(10, ("winbindd_getgroups: sid type %d "
+ "for %s is not a domain group\n",
+ sid_type,
+ sid_string_static(
+ &info3->other_sids[i].sid)));
+ continue;
+ }
+
+ add_gids_from_sid(&info3->other_sids[i].sid,
+ &gid_list, &num_gids);
+
+ if (gid_list == NULL)
+ goto done;
+ }
+
+ for (i = 0; i < info3->num_groups2; i++) {
+
+ /* create the group SID */
+
+ sid_copy( &group_sid, &domain->sid );
+ sid_append_rid( &group_sid, info3->gids[i].g_rid );
+
+ add_gids_from_sid(&group_sid, &gid_list, &num_gids);
+
+ if (gid_list == NULL)
+ goto done;
+ }
+
+ SAFE_FREE(info3);
+
+ } else {
+ status = domain->methods->lookup_usergroups(domain, mem_ctx,
+ &user_sid, &num_groups,
+ &user_grpsids);
+ if (!NT_STATUS_IS_OK(status))
+ goto done;
+
+ if (state->response.extra_data)
+ goto done;
+
+ for (i = 0; i < num_groups; i++) {
+ add_gids_from_sid(user_grpsids[i],
+ &gid_list, &num_gids);
+
+ if (gid_list == NULL)
+ goto done;
+ }
+ }
+
+ /* Send data back to client */
+
+ state->response.data.num_entries = num_gids;
+ state->response.extra_data = gid_list;
+ state->response.length += num_gids * sizeof(gid_t);
+
+ result = WINBINDD_OK;
+
+ done:
+
+ talloc_destroy(mem_ctx);
+
+ return result;
+}
+
+
+/* Get user supplementary sids. This is equivalent to the
+ winbindd_getgroups() function but it involves a SID->SIDs mapping
+ rather than a NAME->SID->SIDS->GIDS mapping, which means we avoid
+ idmap. This call is designed to be used with applications that need
+ to do ACL evaluation themselves. Note that the cached info3 data is
+ not used
+
+ this function assumes that the SID that comes in is a user SID. If
+ you pass in another type of SID then you may get unpredictable
+ results.
+*/
+enum winbindd_result winbindd_getusersids(struct winbindd_cli_state *state)
+{
+ DOM_SID user_sid;
+ NTSTATUS status;
+ DOM_SID **user_grpsids;
+ struct winbindd_domain *domain;
+ enum winbindd_result result = WINBINDD_ERROR;
+ unsigned int i;
+ TALLOC_CTX *mem_ctx;
+ char *ret = NULL;
+ uint32 num_groups;
+ unsigned ofs, ret_size = 0;
+
+ /* Ensure null termination */
+ state->request.data.sid[sizeof(state->request.data.sid)-1]='\0';
+
+ if (!string_to_sid(&user_sid, state->request.data.sid)) {
+ DEBUG(1, ("Could not get convert sid %s from string\n", state->request.data.sid));
+ return WINBINDD_ERROR;
+ }
+
+ if (!(mem_ctx = talloc_init("winbindd_getusersids(%s)",
+ state->request.data.username))) {
+ return WINBINDD_ERROR;
+ }
+
+ /* Get info for the domain */
+ if ((domain = find_domain_from_sid(&user_sid)) == NULL) {
+ DEBUG(0,("could not find domain entry for sid %s\n",
+ sid_string_static(&user_sid)));
+ goto done;
+ }
+
+ status = domain->methods->lookup_usergroups(domain, mem_ctx,
+ &user_sid, &num_groups,
+ &user_grpsids);
+ if (!NT_STATUS_IS_OK(status))
+ goto done;
+
+ if (num_groups == 0) {
+ goto no_groups;
+ }
+
+ /* work out the response size */
+ for (i = 0; i < num_groups; i++) {
+ const char *s = sid_string_static(user_grpsids[i]);
+ ret_size += strlen(s) + 1;
+ }
+
+ /* build the reply */
+ ret = malloc(ret_size);
+ if (!ret) goto done;
+ ofs = 0;
+ for (i = 0; i < num_groups; i++) {
+ const char *s = sid_string_static(user_grpsids[i]);
+ safe_strcpy(ret + ofs, s, ret_size - ofs - 1);
+ ofs += strlen(ret+ofs) + 1;
+ }
+
+no_groups:
+ /* Send data back to client */
+ state->response.data.num_entries = num_groups;
+ state->response.extra_data = ret;
+ state->response.length += ret_size;
+ result = WINBINDD_OK;
+
+ done:
+ talloc_destroy(mem_ctx);
+
+ return result;
+}
+
diff --git a/source/nsswitch/winbindd_misc.c b/source/nsswitch/winbindd_misc.c
new file mode 100644
index 00000000000..18478992f3e
--- /dev/null
+++ b/source/nsswitch/winbindd_misc.c
@@ -0,0 +1,307 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ Winbind daemon - miscellaneous other functions
+
+ Copyright (C) Tim Potter 2000
+ 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 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 "winbindd.h"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_WINBIND
+
+/* Check the machine account password is valid */
+
+enum winbindd_result winbindd_check_machine_acct(struct winbindd_cli_state *state)
+{
+ NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+ uchar trust_passwd[16];
+ int num_retries = 0;
+ struct cli_state *cli;
+ uint32 sec_channel_type;
+ struct winbindd_domain *contact_domain;
+
+ DEBUG(3, ("[%5lu]: check machine account\n", (unsigned long)state->pid));
+
+ /* Get trust account password */
+
+ again:
+ if (!secrets_fetch_trust_account_password(
+ lp_workgroup(), trust_passwd, NULL, &sec_channel_type)) {
+ result = NT_STATUS_INTERNAL_ERROR;
+ goto done;
+ }
+
+
+ contact_domain = find_our_domain();
+ if (!contact_domain) {
+ result = NT_STATUS_CANT_ACCESS_DOMAIN_INFO;
+ DEBUG(1, ("Cannot find our own domain!\n"));
+ goto done;
+ }
+
+ /* This call does a cli_nt_setup_creds() which implicitly checks
+ the trust account password. */
+ /* Don't shut this down - it belongs to the connection cache code */
+
+ result = cm_get_netlogon_cli(contact_domain,
+ trust_passwd, sec_channel_type, True, &cli);
+
+ if (!NT_STATUS_IS_OK(result)) {
+ DEBUG(3, ("could not open handle to NETLOGON pipe\n"));
+ goto done;
+ }
+
+ /* There is a race condition between fetching the trust account
+ password and the periodic machine password change. So it's
+ possible that the trust account password has been changed on us.
+ We are returned NT_STATUS_ACCESS_DENIED if this happens. */
+
+#define MAX_RETRIES 8
+
+ if ((num_retries < MAX_RETRIES) &&
+ NT_STATUS_V(result) == NT_STATUS_V(NT_STATUS_ACCESS_DENIED)) {
+ num_retries++;
+ goto again;
+ }
+
+ /* Pass back result code - zero for success, other values for
+ specific failures. */
+
+ DEBUG(3, ("secret is %s\n", NT_STATUS_IS_OK(result) ?
+ "good" : "bad"));
+
+ done:
+ state->response.data.auth.nt_status = NT_STATUS_V(result);
+ fstrcpy(state->response.data.auth.nt_status_string, nt_errstr(result));
+ fstrcpy(state->response.data.auth.error_string, nt_errstr(result));
+ state->response.data.auth.pam_error = nt_status_to_pam(result);
+
+ DEBUG(NT_STATUS_IS_OK(result) ? 5 : 2, ("Checking the trust account password returned %s\n",
+ state->response.data.auth.nt_status_string));
+
+ return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;
+}
+
+enum winbindd_result winbindd_list_trusted_domains(struct winbindd_cli_state
+ *state)
+{
+ struct winbindd_domain *domain;
+ int total_entries = 0, extra_data_len = 0;
+ char *ted, *extra_data = NULL;
+
+ DEBUG(3, ("[%5lu]: list trusted domains\n", (unsigned long)state->pid));
+
+ /* We need to refresh the trusted domain list as the domains may
+ have changed since we last looked. There may be a sequence
+ number or something we should use but I haven't found it yet. */
+
+ if (!init_domain_list()) {
+ DEBUG(1, ("winbindd_list_trusted_domains: could not "
+ "refresh trusted domain list\n"));
+ return WINBINDD_ERROR;
+ }
+
+ for(domain = domain_list(); domain; domain = domain->next) {
+
+ /* Skip own domain */
+
+ if (domain->primary) continue;
+
+ /* Add domain to list */
+
+ total_entries++;
+ ted = Realloc(extra_data, sizeof(fstring) *
+ total_entries);
+
+ if (!ted) {
+ DEBUG(0,("winbindd_list_trusted_domains: failed to enlarge buffer!\n"));
+ SAFE_FREE(extra_data);
+ return WINBINDD_ERROR;
+ } else
+ extra_data = ted;
+
+ memcpy(&extra_data[extra_data_len], domain->name,
+ strlen(domain->name));
+
+ extra_data_len += strlen(domain->name);
+ extra_data[extra_data_len++] = ',';
+ }
+
+ if (extra_data) {
+ if (extra_data_len > 1)
+ extra_data[extra_data_len - 1] = '\0';
+ state->response.extra_data = extra_data;
+ state->response.length += extra_data_len;
+ }
+
+ return WINBINDD_OK;
+}
+
+
+enum winbindd_result winbindd_show_sequence(struct winbindd_cli_state *state)
+{
+ struct winbindd_domain *domain;
+ char *extra_data = NULL;
+ const char *which_domain;
+
+ DEBUG(3, ("[%5lu]: show sequence\n", (unsigned long)state->pid));
+
+ /* Ensure null termination */
+ state->request.domain_name[sizeof(state->request.domain_name)-1]='\0';
+ which_domain = state->request.domain_name;
+
+ extra_data = strdup("");
+
+ /* this makes for a very simple data format, and is easily parsable as well
+ if that is ever needed */
+ for (domain = domain_list(); domain; domain = domain->next) {
+ char *s;
+
+ /* if we have a domain name restricting the request and this
+ one in the list doesn't match, then just bypass the remainder
+ of the loop */
+
+ if ( *which_domain && !strequal(which_domain, domain->name) )
+ continue;
+
+ domain->methods->sequence_number(domain, &domain->sequence_number);
+
+ if (DOM_SEQUENCE_NONE == (unsigned)domain->sequence_number) {
+ asprintf(&s,"%s%s : DISCONNECTED\n", extra_data,
+ domain->name);
+ } else {
+ asprintf(&s,"%s%s : %u\n", extra_data,
+ domain->name, (unsigned)domain->sequence_number);
+ }
+ free(extra_data);
+ extra_data = s;
+ }
+
+ state->response.extra_data = extra_data;
+ /* must add one to length to copy the 0 for string termination */
+ state->response.length += strlen(extra_data) + 1;
+
+ return WINBINDD_OK;
+}
+
+enum winbindd_result winbindd_domain_info(struct winbindd_cli_state *state)
+{
+ struct winbindd_domain *domain;
+
+ DEBUG(3, ("[%5lu]: domain_info [%s]\n", (unsigned long)state->pid,
+ state->request.domain_name));
+
+ domain = find_domain_from_name(state->request.domain_name);
+
+ if (domain == NULL) {
+ DEBUG(3, ("Did not find domain [%s]\n",
+ state->request.domain_name));
+ return WINBINDD_ERROR;
+ }
+
+ fstrcpy(state->response.data.domain_info.name, domain->name);
+ fstrcpy(state->response.data.domain_info.alt_name, domain->alt_name);
+ fstrcpy(state->response.data.domain_info.sid,
+ sid_string_static(&domain->sid));
+
+ state->response.data.domain_info.native_mode = domain->native_mode;
+ state->response.data.domain_info.active_directory = domain->active_directory;
+ state->response.data.domain_info.primary = domain->primary;
+
+ state->response.data.domain_info.sequence_number =
+ domain->sequence_number;
+
+ return WINBINDD_OK;
+}
+
+enum winbindd_result winbindd_ping(struct winbindd_cli_state
+ *state)
+{
+ DEBUG(3, ("[%5lu]: ping\n", (unsigned long)state->pid));
+
+ return WINBINDD_OK;
+}
+
+/* List various tidbits of information */
+
+enum winbindd_result winbindd_info(struct winbindd_cli_state *state)
+{
+
+ DEBUG(3, ("[%5lu]: request misc info\n", (unsigned long)state->pid));
+
+ state->response.data.info.winbind_separator = *lp_winbind_separator();
+ fstrcpy(state->response.data.info.samba_version, SAMBA_VERSION_STRING);
+
+ return WINBINDD_OK;
+}
+
+/* Tell the client the current interface version */
+
+enum winbindd_result winbindd_interface_version(struct winbindd_cli_state *state)
+{
+
+ DEBUG(3, ("[%5lu]: request interface version\n", (unsigned long)state->pid));
+
+ state->response.data.interface_version = WINBIND_INTERFACE_VERSION;
+
+ return WINBINDD_OK;
+}
+
+/* What domain are we a member of? */
+
+enum winbindd_result winbindd_domain_name(struct winbindd_cli_state *state)
+{
+
+ DEBUG(3, ("[%5lu]: request domain name\n", (unsigned long)state->pid));
+
+ fstrcpy(state->response.data.domain_name, lp_workgroup());
+
+ return WINBINDD_OK;
+}
+
+/* What's my name again? */
+
+enum winbindd_result winbindd_netbios_name(struct winbindd_cli_state *state)
+{
+
+ DEBUG(3, ("[%5lu]: request netbios name\n", (unsigned long)state->pid));
+
+ fstrcpy(state->response.data.netbios_name, global_myname());
+
+ return WINBINDD_OK;
+}
+
+/* Where can I find the privilaged pipe? */
+
+enum winbindd_result winbindd_priv_pipe_dir(struct winbindd_cli_state *state)
+{
+
+ DEBUG(3, ("[%5lu]: request location of privileged pipe\n", (unsigned long)state->pid));
+
+ state->response.extra_data = strdup(get_winbind_priv_pipe_dir());
+ if (!state->response.extra_data)
+ return WINBINDD_ERROR;
+
+ /* must add one to length to copy the 0 for string termination */
+ state->response.length += strlen((char *)state->response.extra_data) + 1;
+
+ return WINBINDD_OK;
+}
diff --git a/source/nsswitch/winbindd_nss.h b/source/nsswitch/winbindd_nss.h
new file mode 100644
index 00000000000..43c9e68cd9f
--- /dev/null
+++ b/source/nsswitch/winbindd_nss.h
@@ -0,0 +1,288 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ Winbind daemon for ntdom nss module
+
+ Copyright (C) Tim Potter 2000
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 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 Library General Public
+ License along with this library; if not, write to the
+ Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+*/
+
+#ifndef SAFE_FREE
+#define SAFE_FREE(x) do { if(x) {free(x); x=NULL;} } while(0)
+#endif
+
+#ifndef _WINBINDD_NTDOM_H
+#define _WINBINDD_NTDOM_H
+
+#define WINBINDD_SOCKET_NAME "pipe" /* Name of PF_UNIX socket */
+#define WINBINDD_SOCKET_DIR "/tmp/.winbindd" /* Name of PF_UNIX dir */
+#define WINBINDD_PRIV_SOCKET_SUBDIR "winbindd_privileged" /* name of subdirectory of lp_lockdir() to hold the 'privileged' pipe */
+#define WINBINDD_DOMAIN_ENV "WINBINDD_DOMAIN" /* Environment variables */
+#define WINBINDD_DONT_ENV "_NO_WINBINDD"
+
+/* Update this when you change the interface. */
+
+#define WINBIND_INTERFACE_VERSION 10
+
+/* Socket commands */
+
+enum winbindd_cmd {
+
+ WINBINDD_INTERFACE_VERSION, /* Always a well known value */
+
+ /* Get users and groups */
+
+ WINBINDD_GETPWNAM,
+ WINBINDD_GETPWUID,
+ WINBINDD_GETGRNAM,
+ WINBINDD_GETGRGID,
+ WINBINDD_GETGROUPS,
+
+ /* Enumerate users and groups */
+
+ WINBINDD_SETPWENT,
+ WINBINDD_ENDPWENT,
+ WINBINDD_GETPWENT,
+ WINBINDD_SETGRENT,
+ WINBINDD_ENDGRENT,
+ WINBINDD_GETGRENT,
+
+ /* PAM authenticate and password change */
+
+ WINBINDD_PAM_AUTH,
+ WINBINDD_PAM_AUTH_CRAP,
+ WINBINDD_PAM_CHAUTHTOK,
+
+ /* List various things */
+
+ WINBINDD_LIST_USERS, /* List w/o rid->id mapping */
+ WINBINDD_LIST_GROUPS, /* Ditto */
+ WINBINDD_LIST_TRUSTDOM,
+
+ /* SID conversion */
+
+ WINBINDD_LOOKUPSID,
+ WINBINDD_LOOKUPNAME,
+
+ /* Lookup functions */
+
+ WINBINDD_SID_TO_UID,
+ WINBINDD_SID_TO_GID,
+ WINBINDD_UID_TO_SID,
+ WINBINDD_GID_TO_SID,
+ WINBINDD_ALLOCATE_RID,
+
+ /* Miscellaneous other stuff */
+
+ WINBINDD_CHECK_MACHACC, /* Check machine account pw works */
+ WINBINDD_PING, /* Just tell me winbind is running */
+ WINBINDD_INFO, /* Various bit of info. Currently just tidbits */
+ WINBINDD_DOMAIN_NAME, /* The domain this winbind server is a member of (lp_workgroup()) */
+
+ WINBINDD_DOMAIN_INFO, /* Most of what we know from
+ struct winbindd_domain */
+
+ WINBINDD_SHOW_SEQUENCE, /* display sequence numbers of domains */
+
+ /* WINS commands */
+
+ WINBINDD_WINS_BYIP,
+ WINBINDD_WINS_BYNAME,
+
+ /* account management commands */
+
+ WINBINDD_CREATE_USER,
+ WINBINDD_CREATE_GROUP,
+ WINBINDD_ADD_USER_TO_GROUP,
+ WINBINDD_REMOVE_USER_FROM_GROUP,
+ WINBINDD_SET_USER_PRIMARY_GROUP,
+ WINBINDD_DELETE_USER,
+ WINBINDD_DELETE_GROUP,
+
+ /* this is like GETGRENT but gives an empty group list */
+ WINBINDD_GETGRLST,
+
+ WINBINDD_NETBIOS_NAME, /* The netbios name of the server */
+ /* Placeholder for end of cmd list */
+
+ /* find the location of our privileged pipe */
+ WINBINDD_PRIV_PIPE_DIR,
+
+ /* return a list of group sids for a user sid */
+ WINBINDD_GETUSERSIDS,
+
+ WINBINDD_NUM_CMDS
+};
+
+typedef struct winbindd_pw {
+ fstring pw_name;
+ fstring pw_passwd;
+ uid_t pw_uid;
+ gid_t pw_gid;
+ fstring pw_gecos;
+ fstring pw_dir;
+ fstring pw_shell;
+} WINBINDD_PW;
+
+
+typedef struct winbindd_gr {
+ fstring gr_name;
+ fstring gr_passwd;
+ gid_t gr_gid;
+ int num_gr_mem;
+ int gr_mem_ofs; /* offset to group membership */
+ char **gr_mem;
+} WINBINDD_GR;
+
+
+#define WBFLAG_PAM_INFO3_NDR 0x0001
+#define WBFLAG_PAM_INFO3_TEXT 0x0002
+#define WBFLAG_PAM_NTKEY 0x0004
+#define WBFLAG_PAM_LMKEY 0x0008
+#define WBFLAG_PAM_CONTACT_TRUSTDOM 0x0010
+#define WBFLAG_QUERY_ONLY 0x0020
+#define WBFLAG_ALLOCATE_RID 0x0040
+#define WBFLAG_PAM_UNIX_NAME 0x0080
+#define WBFLAG_PAM_AFS_TOKEN 0x0100
+
+/* Winbind request structure */
+
+struct winbindd_request {
+ uint32 length;
+ enum winbindd_cmd cmd; /* Winbindd command to execute */
+ pid_t pid; /* pid of calling process */
+ uint32 flags; /* flags relavant to a given request */
+ fstring domain_name; /* name of domain for which the request applies */
+
+ union {
+ fstring winsreq; /* WINS request */
+ fstring username; /* getpwnam */
+ fstring groupname; /* getgrnam */
+ uid_t uid; /* getpwuid, uid_to_sid */
+ gid_t gid; /* getgrgid, gid_to_sid */
+ struct {
+ /* We deliberatedly don't split into domain/user to
+ avoid having the client know what the separator
+ character is. */
+ fstring user;
+ fstring pass;
+ } auth; /* pam_winbind auth module */
+ struct {
+ unsigned char chal[8];
+ fstring user;
+ fstring domain;
+ fstring lm_resp;
+ uint16 lm_resp_len;
+ fstring nt_resp;
+ uint16 nt_resp_len;
+ fstring workstation;
+ } auth_crap;
+ struct {
+ fstring user;
+ fstring oldpass;
+ fstring newpass;
+ } chauthtok; /* pam_winbind passwd module */
+ fstring sid; /* lookupsid, sid_to_[ug]id */
+ struct {
+ fstring dom_name; /* lookupname */
+ fstring name;
+ } name;
+ uint32 num_entries; /* getpwent, getgrent */
+ struct {
+ fstring username;
+ fstring groupname;
+ } acct_mgt;
+ } data;
+ char null_term;
+};
+
+/* Response values */
+
+enum winbindd_result {
+ WINBINDD_ERROR,
+ WINBINDD_OK
+};
+
+/* Winbind response structure */
+
+struct winbindd_response {
+
+ /* Header information */
+
+ uint32 length; /* Length of response */
+ enum winbindd_result result; /* Result code */
+
+ /* Fixed length return data */
+
+ union {
+ int interface_version; /* Try to ensure this is always in the same spot... */
+
+ fstring winsresp; /* WINS response */
+
+ /* getpwnam, getpwuid */
+
+ struct winbindd_pw pw;
+
+ /* getgrnam, getgrgid */
+
+ struct winbindd_gr gr;
+
+ uint32 num_entries; /* getpwent, getgrent */
+ struct winbindd_sid {
+ fstring sid; /* lookupname, [ug]id_to_sid */
+ int type;
+ } sid;
+ struct winbindd_name {
+ fstring dom_name; /* lookupsid */
+ fstring name;
+ int type;
+ } name;
+ uid_t uid; /* sid_to_uid */
+ gid_t gid; /* sid_to_gid */
+ struct winbindd_info {
+ char winbind_separator;
+ fstring samba_version;
+ } info;
+ fstring domain_name;
+ fstring netbios_name;
+
+ struct auth_reply {
+ uint32 nt_status;
+ fstring nt_status_string;
+ fstring error_string;
+ int pam_error;
+ char nt_session_key[16];
+ char first_8_lm_hash[8];
+ } auth;
+ uint32 rid; /* create user or group or allocate rid */
+ struct {
+ fstring name;
+ fstring alt_name;
+ fstring sid;
+ BOOL native_mode;
+ BOOL active_directory;
+ BOOL primary;
+ uint32 sequence_number;
+ } domain_info;
+ } data;
+
+ /* Variable length return data */
+
+ void *extra_data; /* getgrnam, getgrgid, getgrent */
+};
+
+#endif
diff --git a/source/nsswitch/winbindd_pam.c b/source/nsswitch/winbindd_pam.c
new file mode 100644
index 00000000000..1d232edfe31
--- /dev/null
+++ b/source/nsswitch/winbindd_pam.c
@@ -0,0 +1,568 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ Winbind daemon - pam auth funcions
+
+ Copyright (C) Andrew Tridgell 2000
+ Copyright (C) Tim Potter 2001
+ Copyright (C) Andrew Bartlett 2001-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 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 "winbindd.h"
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_WINBIND
+
+
+static NTSTATUS append_info3_as_ndr(TALLOC_CTX *mem_ctx,
+ struct winbindd_cli_state *state,
+ NET_USER_INFO_3 *info3)
+{
+ prs_struct ps;
+ uint32 size;
+ if (!prs_init(&ps, 256 /* Random, non-zero number */, mem_ctx, MARSHALL)) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ if (!net_io_user_info3("", info3, &ps, 1, 3)) {
+ prs_mem_free(&ps);
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ size = prs_data_size(&ps);
+ state->response.extra_data = malloc(size);
+ if (!state->response.extra_data) {
+ prs_mem_free(&ps);
+ return NT_STATUS_NO_MEMORY;
+ }
+ prs_copy_all_data_out(state->response.extra_data, &ps);
+ state->response.length += size;
+ prs_mem_free(&ps);
+ return NT_STATUS_OK;
+}
+
+/**********************************************************************
+ Authenticate a user with a clear test password
+**********************************************************************/
+
+enum winbindd_result winbindd_pam_auth(struct winbindd_cli_state *state)
+{
+ NTSTATUS result;
+ fstring name_domain, name_user;
+ unsigned char trust_passwd[16];
+ time_t last_change_time;
+ uint32 sec_channel_type;
+ NET_USER_INFO_3 info3;
+ struct cli_state *cli = NULL;
+ uchar chal[8];
+ TALLOC_CTX *mem_ctx = NULL;
+ DATA_BLOB lm_resp;
+ DATA_BLOB nt_resp;
+ DOM_CRED ret_creds;
+ int attempts = 0;
+ unsigned char local_lm_response[24];
+ unsigned char local_nt_response[24];
+ struct winbindd_domain *contact_domain;
+ BOOL retry;
+
+ /* Ensure null termination */
+ state->request.data.auth.user[sizeof(state->request.data.auth.user)-1]='\0';
+
+ /* Ensure null termination */
+ state->request.data.auth.pass[sizeof(state->request.data.auth.pass)-1]='\0';
+
+ DEBUG(3, ("[%5lu]: pam auth %s\n", (unsigned long)state->pid,
+ state->request.data.auth.user));
+
+ if (!(mem_ctx = talloc_init("winbind pam auth for %s", state->request.data.auth.user))) {
+ DEBUG(0, ("winbindd_pam_auth: could not talloc_init()!\n"));
+ result = NT_STATUS_NO_MEMORY;
+ goto done;
+ }
+
+ /* Parse domain and username */
+
+ parse_domain_user(state->request.data.auth.user, name_domain, name_user);
+
+ /* do password magic */
+
+ generate_random_buffer(chal, 8, False);
+ SMBencrypt(state->request.data.auth.pass, chal, local_lm_response);
+
+ SMBNTencrypt(state->request.data.auth.pass, chal, local_nt_response);
+
+ lm_resp = data_blob_talloc(mem_ctx, local_lm_response, sizeof(local_lm_response));
+ nt_resp = data_blob_talloc(mem_ctx, local_nt_response, sizeof(local_nt_response));
+
+ /* what domain should we contact? */
+
+ if ( IS_DC ) {
+ if (!(contact_domain = find_domain_from_name(name_domain))) {
+ DEBUG(3, ("Authentication for domain for [%s] -> [%s]\\[%s] failed as %s is not a trusted domain\n",
+ state->request.data.auth.user, name_domain, name_user, name_domain));
+ result = NT_STATUS_NO_SUCH_USER;
+ goto done;
+ }
+
+ } else {
+ if (is_myname(name_domain)) {
+ DEBUG(3, ("Authentication for domain %s (local domain to this server) not supported at this stage\n", name_domain));
+ result = NT_STATUS_NO_SUCH_USER;
+ goto done;
+ }
+
+ if (!(contact_domain = find_our_domain())) {
+ DEBUG(1, ("Authenticatoin for [%s] -> [%s]\\[%s] in our domain failed - we can't find our domain!\n",
+ state->request.data.auth.user, name_domain, name_user));
+ result = NT_STATUS_NO_SUCH_USER;
+ goto done;
+ }
+ }
+
+ if ( !get_trust_pw(contact_domain->name, trust_passwd, &last_change_time, &sec_channel_type) ) {
+ result = NT_STATUS_CANT_ACCESS_DOMAIN_INFO;
+ goto done;
+ }
+
+ /* check authentication loop */
+
+ do {
+ ZERO_STRUCT(info3);
+ ZERO_STRUCT(ret_creds);
+ retry = False;
+
+ /* Don't shut this down - it belongs to the connection cache code */
+ result = cm_get_netlogon_cli(contact_domain, trust_passwd,
+ sec_channel_type, False, &cli);
+
+ if (!NT_STATUS_IS_OK(result)) {
+ DEBUG(3, ("could not open handle to NETLOGON pipe\n"));
+ goto done;
+ }
+
+ result = cli_netlogon_sam_network_logon(cli, mem_ctx,
+ &ret_creds,
+ name_user, name_domain,
+ global_myname(), chal,
+ lm_resp, nt_resp,
+ &info3);
+ attempts += 1;
+
+ /* We have to try a second time as cm_get_netlogon_cli
+ might not yet have noticed that the DC has killed
+ our connection. */
+
+ if ( cli->fd == -1 ) {
+ retry = True;
+ continue;
+ }
+
+ /* if we get access denied, a possible cuase was that we had and open
+ connection to the DC, but someone changed our machine account password
+ out from underneath us using 'net rpc changetrustpw' */
+
+ if ( NT_STATUS_V(result) == NT_STATUS_V(NT_STATUS_ACCESS_DENIED) ) {
+ DEBUG(3,("winbindd_pam_auth: sam_logon returned ACCESS_DENIED. Maybe the trust account "
+ "password was changed and we didn't know it. Killing connections to domain %s\n",
+ name_domain));
+ winbindd_cm_flush();
+ retry = True;
+ cli = NULL;
+ }
+
+ } while ( (attempts < 2) && retry );
+
+ clnt_deal_with_creds(cli->sess_key, &(cli->clnt_cred), &ret_creds);
+
+ if (NT_STATUS_IS_OK(result)) {
+ netsamlogon_cache_store( cli->mem_ctx, &info3 );
+ wcache_invalidate_samlogon(find_domain_from_name(name_domain), &info3);
+ }
+
+done:
+ /* give us a more useful (more correct?) error code */
+ if ((NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND) || (NT_STATUS_EQUAL(result, NT_STATUS_UNSUCCESSFUL)))) {
+ result = NT_STATUS_NO_LOGON_SERVERS;
+ }
+
+ state->response.data.auth.nt_status = NT_STATUS_V(result);
+ fstrcpy(state->response.data.auth.nt_status_string, nt_errstr(result));
+
+ /* we might have given a more useful error above */
+ if (!*state->response.data.auth.error_string)
+ fstrcpy(state->response.data.auth.error_string, get_friendly_nt_error_msg(result));
+ state->response.data.auth.pam_error = nt_status_to_pam(result);
+
+ DEBUG(NT_STATUS_IS_OK(result) ? 5 : 2, ("Plain-text authentication for user %s returned %s (PAM: %d)\n",
+ state->request.data.auth.user,
+ state->response.data.auth.nt_status_string,
+ state->response.data.auth.pam_error));
+
+ if ( NT_STATUS_IS_OK(result) &&
+ (state->request.flags & WBFLAG_PAM_AFS_TOKEN) ) {
+
+ char *afsname = strdup(lp_afs_username_map());
+ char *cell;
+
+ if (afsname == NULL) goto no_token;
+
+ afsname = realloc_string_sub(afsname, "%D", name_domain);
+ afsname = realloc_string_sub(afsname, "%u", name_user);
+ afsname = realloc_string_sub(afsname, "%U", name_user);
+
+ if (afsname == NULL) goto no_token;
+
+ strlower_m(afsname);
+
+ cell = strchr(afsname, '@');
+
+ if (cell == NULL) goto no_token;
+
+ *cell = '\0';
+ cell += 1;
+
+ /* Append an AFS token string */
+ state->response.extra_data =
+ afs_createtoken_str(afsname, cell);
+
+ if (state->response.extra_data != NULL)
+ state->response.length +=
+ strlen(state->response.extra_data)+1;
+
+ no_token:
+ SAFE_FREE(afsname);
+ }
+
+ if (mem_ctx)
+ talloc_destroy(mem_ctx);
+
+ return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;
+}
+
+/**********************************************************************
+ Challenge Response Authentication Protocol
+**********************************************************************/
+
+enum winbindd_result winbindd_pam_auth_crap(struct winbindd_cli_state *state)
+{
+ NTSTATUS result;
+ unsigned char trust_passwd[16];
+ time_t last_change_time;
+ uint32 sec_channel_type;
+ NET_USER_INFO_3 info3;
+ struct cli_state *cli = NULL;
+ TALLOC_CTX *mem_ctx = NULL;
+ char *name_user = NULL;
+ const char *name_domain = NULL;
+ const char *workstation;
+ struct winbindd_domain *contact_domain;
+ DOM_CRED ret_creds;
+ int attempts = 0;
+ BOOL retry;
+
+ DATA_BLOB lm_resp, nt_resp;
+
+ if (!state->privileged) {
+ char *error_string = NULL;
+ DEBUG(2, ("winbindd_pam_auth_crap: non-privileged access denied. !\n"));
+ DEBUGADD(2, ("winbindd_pam_auth_crap: Ensure permissions on %s are set correctly.\n",
+ get_winbind_priv_pipe_dir()));
+ /* send a better message than ACCESS_DENIED */
+ asprintf(&error_string, "winbind client not authorized to use winbindd_pam_auth_crap. Ensure permissions on %s are set correctly.",
+ get_winbind_priv_pipe_dir());
+ push_utf8_fstring(state->response.data.auth.error_string, error_string);
+ SAFE_FREE(error_string);
+ result = NT_STATUS_ACCESS_DENIED;
+ goto done;
+ }
+
+ /* Ensure null termination */
+ state->request.data.auth_crap.user[sizeof(state->request.data.auth_crap.user)-1]=0;
+ state->request.data.auth_crap.domain[sizeof(state->request.data.auth_crap.domain)-1]=0;
+
+ if (!(mem_ctx = talloc_init("winbind pam auth crap for (utf8) %s", state->request.data.auth_crap.user))) {
+ DEBUG(0, ("winbindd_pam_auth_crap: could not talloc_init()!\n"));
+ result = NT_STATUS_NO_MEMORY;
+ goto done;
+ }
+
+ if (pull_utf8_talloc(mem_ctx, &name_user, state->request.data.auth_crap.user) == (size_t)-1) {
+ DEBUG(0, ("winbindd_pam_auth_crap: pull_utf8_talloc failed!\n"));
+ result = NT_STATUS_UNSUCCESSFUL;
+ goto done;
+ }
+
+ if (*state->request.data.auth_crap.domain) {
+ char *dom = NULL;
+ if (pull_utf8_talloc(mem_ctx, &dom, state->request.data.auth_crap.domain) == (size_t)-1) {
+ DEBUG(0, ("winbindd_pam_auth_crap: pull_utf8_talloc failed!\n"));
+ result = NT_STATUS_UNSUCCESSFUL;
+ goto done;
+ }
+ name_domain = dom;
+ } else if (lp_winbind_use_default_domain()) {
+ name_domain = lp_workgroup();
+ } else {
+ DEBUG(5,("no domain specified with username (%s) - failing auth\n",
+ name_user));
+ result = NT_STATUS_NO_SUCH_USER;
+ goto done;
+ }
+
+ DEBUG(3, ("[%5lu]: pam auth crap domain: %s user: %s\n", (unsigned long)state->pid,
+ name_domain, name_user));
+
+ if (*state->request.data.auth_crap.workstation) {
+ char *wrk = NULL;
+ if (pull_utf8_talloc(mem_ctx, &wrk, state->request.data.auth_crap.workstation) == (size_t)-1) {
+ DEBUG(0, ("winbindd_pam_auth_crap: pull_utf8_talloc failed!\n"));
+ result = NT_STATUS_UNSUCCESSFUL;
+ goto done;
+ }
+ workstation = wrk;
+ } else {
+ workstation = global_myname();
+ }
+
+ if (state->request.data.auth_crap.lm_resp_len > sizeof(state->request.data.auth_crap.lm_resp)
+ || state->request.data.auth_crap.nt_resp_len > sizeof(state->request.data.auth_crap.nt_resp)) {
+ DEBUG(0, ("winbindd_pam_auth_crap: invalid password length %u/%u\n",
+ state->request.data.auth_crap.lm_resp_len,
+ state->request.data.auth_crap.nt_resp_len));
+ result = NT_STATUS_INVALID_PARAMETER;
+ goto done;
+ }
+
+ lm_resp = data_blob_talloc(mem_ctx, state->request.data.auth_crap.lm_resp, state->request.data.auth_crap.lm_resp_len);
+ nt_resp = data_blob_talloc(mem_ctx, state->request.data.auth_crap.nt_resp, state->request.data.auth_crap.nt_resp_len);
+
+
+ /* what domain should we contact? */
+
+ if ( IS_DC ) {
+ if (!(contact_domain = find_domain_from_name(name_domain))) {
+ DEBUG(3, ("Authentication for domain for [%s] -> [%s]\\[%s] failed as %s is not a trusted domain\n",
+ state->request.data.auth.user, name_domain, name_user, name_domain));
+ result = NT_STATUS_NO_SUCH_USER;
+ goto done;
+ }
+
+ } else {
+ if (is_myname(name_domain)) {
+ DEBUG(3, ("Authentication for domain %s (local domain to this server) not supported at this stage\n", name_domain));
+ result = NT_STATUS_NO_SUCH_USER;
+ goto done;
+ }
+
+ if (!(contact_domain = find_our_domain())) {
+ DEBUG(1, ("Authenticatoin for [%s] -> [%s]\\[%s] in our domain failed - we can't find our domain!\n",
+ state->request.data.auth.user, name_domain, name_user));
+ result = NT_STATUS_NO_SUCH_USER;
+ goto done;
+ }
+ }
+
+ if ( !get_trust_pw(contact_domain->name, trust_passwd, &last_change_time, &sec_channel_type) ) {
+ result = NT_STATUS_CANT_ACCESS_DOMAIN_INFO;
+ goto done;
+ }
+
+ do {
+ ZERO_STRUCT(info3);
+ ZERO_STRUCT(ret_creds);
+ retry = False;
+
+ /* Don't shut this down - it belongs to the connection cache code */
+ result = cm_get_netlogon_cli(contact_domain, trust_passwd, sec_channel_type, False, &cli);
+
+ if (!NT_STATUS_IS_OK(result)) {
+ DEBUG(3, ("could not open handle to NETLOGON pipe (error: %s)\n",
+ nt_errstr(result)));
+ goto done;
+ }
+
+ result = cli_netlogon_sam_network_logon(cli, mem_ctx,
+ &ret_creds,
+ name_user, name_domain,
+ workstation,
+ state->request.data.auth_crap.chal,
+ lm_resp, nt_resp,
+ &info3);
+
+ attempts += 1;
+
+ /* We have to try a second time as cm_get_netlogon_cli
+ might not yet have noticed that the DC has killed
+ our connection. */
+
+ if ( cli->fd == -1 ) {
+ retry = True;
+ continue;
+ }
+
+ /* if we get access denied, a possible cause was that we had and open
+ connection to the DC, but someone changed our machine account password
+ out from underneath us using 'net rpc changetrustpw' */
+
+ if ( NT_STATUS_V(result) == NT_STATUS_V(NT_STATUS_ACCESS_DENIED) ) {
+ DEBUG(3,("winbindd_pam_auth_crap: sam_logon returned ACCESS_DENIED. Maybe the trust account "
+ "password was changed and we didn't know it. Killing connections to domain %s\n",
+ contact_domain->name));
+ winbindd_cm_flush();
+ retry = True;
+ cli = NULL;
+ }
+
+ } while ( (attempts < 2) && retry );
+
+ clnt_deal_with_creds(cli->sess_key, &(cli->clnt_cred), &ret_creds);
+
+ if (NT_STATUS_IS_OK(result)) {
+ netsamlogon_cache_store( cli->mem_ctx, &info3 );
+ wcache_invalidate_samlogon(find_domain_from_name(name_domain), &info3);
+
+ if (state->request.flags & WBFLAG_PAM_INFO3_NDR) {
+ result = append_info3_as_ndr(mem_ctx, state, &info3);
+ } else if (state->request.flags & WBFLAG_PAM_UNIX_NAME) {
+ /* ntlm_auth should return the unix username, per
+ 'winbind use default domain' settings and the like */
+
+ fstring username_out;
+ const char *nt_username, *nt_domain;
+ if (!(nt_username = unistr2_tdup(mem_ctx, &(info3.uni_user_name)))) {
+ /* If the server didn't give us one, just use the one we sent them */
+ nt_username = name_user;
+ }
+
+ if (!(nt_domain = unistr2_tdup(mem_ctx, &(info3.uni_logon_dom)))) {
+ /* If the server didn't give us one, just use the one we sent them */
+ nt_domain = name_domain;
+ }
+
+ fill_domain_username(username_out, nt_domain, nt_username);
+
+ DEBUG(5, ("Setting unix username to [%s]\n", username_out));
+
+ /* this interface is in UTF8 */
+ if (push_utf8_allocate((char **)&state->response.extra_data, username_out) == -1) {
+ result = NT_STATUS_NO_MEMORY;
+ goto done;
+ }
+ state->response.length += strlen(state->response.extra_data)+1;
+ }
+
+ if (state->request.flags & WBFLAG_PAM_NTKEY) {
+ memcpy(state->response.data.auth.nt_session_key, info3.user_sess_key, sizeof(state->response.data.auth.nt_session_key) /* 16 */);
+ }
+ if (state->request.flags & WBFLAG_PAM_LMKEY) {
+ memcpy(state->response.data.auth.first_8_lm_hash, info3.padding, sizeof(state->response.data.auth.first_8_lm_hash) /* 8 */);
+ }
+ }
+
+done:
+ /* give us a more useful (more correct?) error code */
+ if ((NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND) || (NT_STATUS_EQUAL(result, NT_STATUS_UNSUCCESSFUL)))) {
+ result = NT_STATUS_NO_LOGON_SERVERS;
+ }
+
+ state->response.data.auth.nt_status = NT_STATUS_V(result);
+ push_utf8_fstring(state->response.data.auth.nt_status_string, nt_errstr(result));
+
+ /* we might have given a more useful error above */
+ if (!*state->response.data.auth.error_string)
+ push_utf8_fstring(state->response.data.auth.error_string, get_friendly_nt_error_msg(result));
+ state->response.data.auth.pam_error = nt_status_to_pam(result);
+
+ DEBUG(NT_STATUS_IS_OK(result) ? 5 : 2,
+ ("NTLM CRAP authentication for user [%s]\\[%s] returned %s (PAM: %d)\n",
+ name_domain,
+ name_user,
+ state->response.data.auth.nt_status_string,
+ state->response.data.auth.pam_error));
+
+ if (mem_ctx)
+ talloc_destroy(mem_ctx);
+
+ return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;
+}
+
+/* Change a user password */
+
+enum winbindd_result winbindd_pam_chauthtok(struct winbindd_cli_state *state)
+{
+ NTSTATUS result;
+ char *oldpass, *newpass;
+ fstring domain, user;
+ CLI_POLICY_HND *hnd;
+ TALLOC_CTX *mem_ctx;
+ struct winbindd_domain *contact_domain;
+
+ DEBUG(3, ("[%5lu]: pam chauthtok %s\n", (unsigned long)state->pid,
+ state->request.data.chauthtok.user));
+
+ if (!(mem_ctx = talloc_init("winbind password change for (utf8) %s",
+ state->request.data.chauthtok.user))) {
+ DEBUG(0, ("winbindd_pam_auth_crap: could not talloc_init()!\n"));
+ result = NT_STATUS_NO_MEMORY;
+ goto done;
+ }
+
+ /* Setup crap */
+
+ if (state == NULL)
+ return WINBINDD_ERROR;
+
+ parse_domain_user(state->request.data.chauthtok.user, domain, user);
+
+ if (!(contact_domain = find_domain_from_name(domain))) {
+ DEBUG(3, ("Cannot change password for [%s] -> [%s]\\[%s] as %s is not a trusted domain\n",
+ state->request.data.chauthtok.user, domain, user, domain));
+ result = NT_STATUS_NO_SUCH_USER;
+ goto done;
+ }
+
+ /* Change password */
+
+ oldpass = state->request.data.chauthtok.oldpass;
+ newpass = state->request.data.chauthtok.newpass;
+
+ /* Get sam handle */
+
+ if (!NT_STATUS_IS_OK(result = cm_get_sam_handle(contact_domain, &hnd)) ) {
+ DEBUG(1, ("could not get SAM handle on DC for %s\n", domain));
+ goto done;
+ }
+
+ result = cli_samr_chgpasswd_user(hnd->cli, mem_ctx, user, newpass, oldpass);
+
+done:
+ state->response.data.auth.nt_status = NT_STATUS_V(result);
+ fstrcpy(state->response.data.auth.nt_status_string, nt_errstr(result));
+ fstrcpy(state->response.data.auth.error_string, nt_errstr(result));
+ state->response.data.auth.pam_error = nt_status_to_pam(result);
+
+ DEBUG(NT_STATUS_IS_OK(result) ? 5 : 2,
+ ("Password change for user [%s]\\[%s] returned %s (PAM: %d)\n",
+ domain,
+ user,
+ state->response.data.auth.nt_status_string,
+ state->response.data.auth.pam_error));
+
+ if (mem_ctx)
+ talloc_destroy(mem_ctx);
+
+ return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;
+}
diff --git a/source/nsswitch/winbindd_passdb.c b/source/nsswitch/winbindd_passdb.c
new file mode 100644
index 00000000000..36f5297efeb
--- /dev/null
+++ b/source/nsswitch/winbindd_passdb.c
@@ -0,0 +1,339 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ Winbind rpc backend functions
+
+ Copyright (C) Tim Potter 2000-2001,2003
+ Copyright (C) Simo Sorce 2003
+ 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 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 "winbindd.h"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_WINBIND
+
+static void
+add_member(const char *domain, const char *user,
+ char **members, int *num_members)
+{
+ fstring name;
+
+ fill_domain_username(name, domain, user);
+ safe_strcat(name, ",", sizeof(name)-1);
+ string_append(members, name);
+ *num_members += 1;
+}
+
+/**********************************************************************
+ Add member users resulting from sid. Expand if it is a domain group.
+**********************************************************************/
+
+static void
+add_expanded_sid(const DOM_SID *sid, char **members, int *num_members)
+{
+ DOM_SID dom_sid;
+ uint32 rid;
+ struct winbindd_domain *domain;
+ int i;
+
+ char *name = NULL;
+ enum SID_NAME_USE type;
+
+ uint32 num_names;
+ DOM_SID **sid_mem;
+ char **names;
+ uint32 *types;
+
+ NTSTATUS result;
+
+ TALLOC_CTX *mem_ctx = talloc_init("add_expanded_sid");
+
+ if (mem_ctx == NULL) {
+ DEBUG(1, ("talloc_init failed\n"));
+ return;
+ }
+
+ sid_copy(&dom_sid, sid);
+ sid_split_rid(&dom_sid, &rid);
+
+ domain = find_domain_from_sid(&dom_sid);
+
+ if (domain == NULL) {
+ DEBUG(3, ("Could not find domain for sid %s\n",
+ sid_string_static(sid)));
+ goto done;
+ }
+
+ result = domain->methods->sid_to_name(domain, mem_ctx, sid,
+ &name, &type);
+
+ if (!NT_STATUS_IS_OK(result)) {
+ DEBUG(3, ("sid_to_name failed for sid %s\n",
+ sid_string_static(sid)));
+ goto done;
+ }
+
+ DEBUG(10, ("Found name %s, type %d\n", name, type));
+
+ if (type == SID_NAME_USER) {
+ add_member(domain->name, name, members, num_members);
+ goto done;
+ }
+
+ if (type != SID_NAME_DOM_GRP) {
+ DEBUG(10, ("Alias member %s neither user nor group, ignore\n",
+ name));
+ goto done;
+ }
+
+ /* Expand the domain group */
+
+ result = domain->methods->lookup_groupmem(domain, mem_ctx,
+ sid, &num_names,
+ &sid_mem, &names,
+ &types);
+
+ if (!NT_STATUS_IS_OK(result)) {
+ DEBUG(10, ("Could not lookup group members for %s: %s\n",
+ name, nt_errstr(result)));
+ goto done;
+ }
+
+ for (i=0; i<num_names; i++) {
+ DEBUG(10, ("Adding group member SID %s\n",
+ sid_string_static(sid_mem[i])));
+
+ if (types[i] != SID_NAME_USER) {
+ DEBUG(1, ("Hmmm. Member %s of group %s is no user. "
+ "Ignoring.\n", names[i], name));
+ continue;
+ }
+
+ add_member(domain->name, names[i], members, num_members);
+ }
+
+ done:
+ talloc_destroy(mem_ctx);
+ return;
+}
+
+BOOL fill_passdb_alias_grmem(struct winbindd_domain *domain,
+ DOM_SID *group_sid,
+ int *num_gr_mem, char **gr_mem, int *gr_mem_len)
+{
+ DOM_SID *members;
+ int i, num_members;
+
+ *num_gr_mem = 0;
+ *gr_mem = NULL;
+ *gr_mem_len = 0;
+
+ if (!pdb_enum_aliasmem(group_sid, &members, &num_members))
+ return True;
+
+ for (i=0; i<num_members; i++) {
+ add_expanded_sid(&members[i], gr_mem, num_gr_mem);
+ }
+
+ SAFE_FREE(members);
+
+ if (*gr_mem != NULL) {
+ int len;
+
+ /* We have at least one member, strip off the last "," */
+ len = strlen(*gr_mem);
+ (*gr_mem)[len-1] = '\0';
+ *gr_mem_len = len;
+ }
+
+ return True;
+}
+
+/* Query display info for a domain. This returns enough information plus a
+ bit extra to give an overview of domain users for the User Manager
+ application. */
+static NTSTATUS query_user_list(struct winbindd_domain *domain,
+ TALLOC_CTX *mem_ctx,
+ uint32 *num_entries,
+ WINBIND_USERINFO **info)
+{
+ /* We don't have users */
+ *num_entries = 0;
+ *info = NULL;
+ return NT_STATUS_OK;
+}
+
+/* list all domain groups */
+static NTSTATUS enum_dom_groups(struct winbindd_domain *domain,
+ TALLOC_CTX *mem_ctx,
+ uint32 *num_entries,
+ struct acct_info **info)
+{
+ /* We don't have domain groups */
+ *num_entries = 0;
+ *info = NULL;
+ return NT_STATUS_OK;
+}
+
+/* List all domain groups */
+
+static NTSTATUS enum_local_groups(struct winbindd_domain *domain,
+ TALLOC_CTX *mem_ctx,
+ uint32 *num_entries,
+ struct acct_info **info)
+{
+ struct acct_info *talloced_info;
+
+ /* Hmm. One billion aliases should be enough for a start */
+
+ if (!pdb_enum_aliases(&domain->sid, 0, 1000000000,
+ num_entries, info)) {
+ /* Nothing to report, just exit. */
+ return NT_STATUS_OK;
+ }
+
+ talloced_info = (struct acct_info *)
+ talloc_memdup(mem_ctx, *info,
+ *num_entries * sizeof(struct acct_info));
+
+ SAFE_FREE(*info);
+ *info = talloced_info;
+
+ return NT_STATUS_OK;
+}
+
+/* convert a single name to a sid in a domain */
+static NTSTATUS name_to_sid(struct winbindd_domain *domain,
+ TALLOC_CTX *mem_ctx,
+ const char *name,
+ DOM_SID *sid,
+ enum SID_NAME_USE *type)
+{
+ DEBUG(10, ("Finding name %s\n", name));
+
+ if (!pdb_find_alias(name, sid))
+ return NT_STATUS_NONE_MAPPED;
+
+ *type = SID_NAME_ALIAS;
+ return NT_STATUS_OK;
+}
+
+/*
+ convert a domain SID to a user or group name
+*/
+static NTSTATUS sid_to_name(struct winbindd_domain *domain,
+ TALLOC_CTX *mem_ctx,
+ const DOM_SID *sid,
+ char **name,
+ enum SID_NAME_USE *type)
+{
+ struct acct_info info;
+
+ DEBUG(10, ("Converting SID %s\n", sid_string_static(sid)));
+
+ if (!pdb_get_aliasinfo(sid, &info))
+ return NT_STATUS_NONE_MAPPED;
+
+ *name = talloc_strdup(mem_ctx, info.acct_name);
+ *type = SID_NAME_ALIAS;
+
+ return NT_STATUS_OK;
+}
+
+/* Lookup user information from a rid or username. */
+static NTSTATUS query_user(struct winbindd_domain *domain,
+ TALLOC_CTX *mem_ctx,
+ const DOM_SID *user_sid,
+ WINBIND_USERINFO *user_info)
+{
+ return NT_STATUS_NO_SUCH_USER;
+}
+
+/* Lookup groups a user is a member of. I wish Unix had a call like this! */
+static NTSTATUS lookup_usergroups(struct winbindd_domain *domain,
+ TALLOC_CTX *mem_ctx,
+ const DOM_SID *user_sid,
+ uint32 *num_groups, DOM_SID ***user_gids)
+{
+ return NT_STATUS_NO_SUCH_USER;
+}
+
+
+/* Lookup group membership given a rid. */
+static NTSTATUS lookup_groupmem(struct winbindd_domain *domain,
+ TALLOC_CTX *mem_ctx,
+ const DOM_SID *group_sid, uint32 *num_names,
+ DOM_SID ***sid_mem, char ***names,
+ uint32 **name_types)
+{
+ return NT_STATUS_OK;
+}
+
+/* find the sequence number for a domain */
+static NTSTATUS sequence_number(struct winbindd_domain *domain, uint32 *seq)
+{
+ *seq = 1;
+ return NT_STATUS_OK;
+}
+
+/* get a list of trusted domains */
+static NTSTATUS trusted_domains(struct winbindd_domain *domain,
+ TALLOC_CTX *mem_ctx,
+ uint32 *num_domains,
+ char ***names,
+ char ***alt_names,
+ DOM_SID **dom_sids)
+{
+ return NT_STATUS_OK;
+}
+
+/* find the domain sid for a domain */
+static NTSTATUS domain_sid(struct winbindd_domain *domain, DOM_SID *sid)
+{
+ sid_copy(sid, &domain->sid);
+ return NT_STATUS_OK;
+}
+
+/* find alternate names list for the domain
+ * should we look for netbios aliases??
+ SSS */
+static NTSTATUS alternate_name(struct winbindd_domain *domain)
+{
+ DEBUG(3,("pdb: alternate_name\n"));
+
+ return NT_STATUS_OK;
+}
+
+
+/* the rpc backend methods are exposed via this structure */
+struct winbindd_methods passdb_methods = {
+ False,
+ query_user_list,
+ enum_dom_groups,
+ enum_local_groups,
+ name_to_sid,
+ sid_to_name,
+ query_user,
+ lookup_usergroups,
+ lookup_groupmem,
+ sequence_number,
+ trusted_domains,
+ domain_sid,
+ alternate_name
+};
diff --git a/source/nsswitch/winbindd_rpc.c b/source/nsswitch/winbindd_rpc.c
new file mode 100644
index 00000000000..25d5f64df67
--- /dev/null
+++ b/source/nsswitch/winbindd_rpc.c
@@ -0,0 +1,1007 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ Winbind rpc backend functions
+
+ Copyright (C) Tim Potter 2000-2001,2003
+ 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 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 "winbindd.h"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_WINBIND
+
+
+/* Query display info for a domain. This returns enough information plus a
+ bit extra to give an overview of domain users for the User Manager
+ application. */
+static NTSTATUS query_user_list(struct winbindd_domain *domain,
+ TALLOC_CTX *mem_ctx,
+ uint32 *num_entries,
+ WINBIND_USERINFO **info)
+{
+ CLI_POLICY_HND *hnd;
+ NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+ POLICY_HND dom_pol;
+ BOOL got_dom_pol = False;
+ uint32 des_access = SEC_RIGHTS_MAXIMUM_ALLOWED;
+ unsigned int i, start_idx, retry;
+ uint32 loop_count;
+
+ DEBUG(3,("rpc: query_user_list\n"));
+
+ *num_entries = 0;
+ *info = NULL;
+
+ retry = 0;
+ do {
+ /* Get sam handle */
+
+ if ( !NT_STATUS_IS_OK(result = cm_get_sam_handle(domain, &hnd)) )
+ return result;
+
+ /* Get domain handle */
+
+ result = cli_samr_open_domain(hnd->cli, mem_ctx, &hnd->pol,
+ des_access, &domain->sid, &dom_pol);
+
+ } while (!NT_STATUS_IS_OK(result) && (retry++ < 1) && hnd && hnd->cli && hnd->cli->fd == -1);
+
+ if (!NT_STATUS_IS_OK(result))
+ goto done;
+
+ got_dom_pol = True;
+
+ i = start_idx = 0;
+ loop_count = 0;
+
+ do {
+ TALLOC_CTX *ctx2;
+ uint32 num_dom_users, j;
+ uint32 max_entries, max_size;
+ SAM_DISPINFO_CTR ctr;
+ SAM_DISPINFO_1 info1;
+
+ ZERO_STRUCT( ctr );
+ ZERO_STRUCT( info1 );
+ ctr.sam.info1 = &info1;
+
+ if (!(ctx2 = talloc_init("winbindd enum_users"))) {
+ result = NT_STATUS_NO_MEMORY;
+ goto done;
+ }
+
+ /* this next bit is copied from net_user_list_internal() */
+
+ get_query_dispinfo_params( loop_count, &max_entries, &max_size );
+
+ result = cli_samr_query_dispinfo(hnd->cli, mem_ctx, &dom_pol,
+ &start_idx, 1, &num_dom_users, max_entries, max_size, &ctr);
+
+ loop_count++;
+
+ *num_entries += num_dom_users;
+
+ *info = talloc_realloc( mem_ctx, *info,
+ (*num_entries) * sizeof(WINBIND_USERINFO));
+
+ if (!(*info)) {
+ result = NT_STATUS_NO_MEMORY;
+ talloc_destroy(ctx2);
+ goto done;
+ }
+
+ for (j = 0; j < num_dom_users; i++, j++) {
+ fstring username, fullname;
+ uint32 rid = ctr.sam.info1->sam[j].rid_user;
+
+ unistr2_to_ascii( username, &(&ctr.sam.info1->str[j])->uni_acct_name, sizeof(username)-1);
+ unistr2_to_ascii( fullname, &(&ctr.sam.info1->str[j])->uni_full_name, sizeof(fullname)-1);
+
+ (*info)[i].acct_name = talloc_strdup(mem_ctx, username );
+ (*info)[i].full_name = talloc_strdup(mem_ctx, fullname );
+ (*info)[i].user_sid = rid_to_talloced_sid(domain, mem_ctx, rid );
+
+ /* For the moment we set the primary group for
+ every user to be the Domain Users group.
+ There are serious problems with determining
+ the actual primary group for large domains.
+ This should really be made into a 'winbind
+ force group' smb.conf parameter or
+ something like that. */
+
+ (*info)[i].group_sid = rid_to_talloced_sid(domain,
+ mem_ctx, DOMAIN_GROUP_RID_USERS);
+ }
+
+ talloc_destroy(ctx2);
+
+ } while (NT_STATUS_EQUAL(result, STATUS_MORE_ENTRIES));
+
+ done:
+
+ if (got_dom_pol)
+ cli_samr_close(hnd->cli, mem_ctx, &dom_pol);
+
+ return result;
+}
+
+/* list all domain groups */
+static NTSTATUS enum_dom_groups(struct winbindd_domain *domain,
+ TALLOC_CTX *mem_ctx,
+ uint32 *num_entries,
+ struct acct_info **info)
+{
+ uint32 des_access = SEC_RIGHTS_MAXIMUM_ALLOWED;
+ CLI_POLICY_HND *hnd;
+ POLICY_HND dom_pol;
+ NTSTATUS status;
+ uint32 start = 0;
+ int retry;
+ NTSTATUS result;
+
+ *num_entries = 0;
+ *info = NULL;
+
+ DEBUG(3,("rpc: enum_dom_groups\n"));
+
+ retry = 0;
+ do {
+ if (!NT_STATUS_IS_OK(result = cm_get_sam_handle(domain, &hnd)))
+ return result;
+
+ status = cli_samr_open_domain(hnd->cli, mem_ctx,
+ &hnd->pol, des_access, &domain->sid, &dom_pol);
+ } while (!NT_STATUS_IS_OK(status) && (retry++ < 1) && hnd && hnd->cli && hnd->cli->fd == -1);
+
+ if (!NT_STATUS_IS_OK(status))
+ return status;
+
+ do {
+ struct acct_info *info2 = NULL;
+ uint32 count = 0;
+ TALLOC_CTX *mem_ctx2;
+
+ mem_ctx2 = talloc_init("enum_dom_groups[rpc]");
+
+ /* start is updated by this call. */
+ status = cli_samr_enum_dom_groups(hnd->cli, mem_ctx2, &dom_pol,
+ &start,
+ 0xFFFF, /* buffer size? */
+ &info2, &count);
+
+ if (!NT_STATUS_IS_OK(status) &&
+ !NT_STATUS_EQUAL(status, STATUS_MORE_ENTRIES)) {
+ talloc_destroy(mem_ctx2);
+ break;
+ }
+
+ (*info) = talloc_realloc(mem_ctx, *info,
+ sizeof(**info) * ((*num_entries) + count));
+ if (! *info) {
+ talloc_destroy(mem_ctx2);
+ cli_samr_close(hnd->cli, mem_ctx, &dom_pol);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ memcpy(&(*info)[*num_entries], info2, count*sizeof(*info2));
+ (*num_entries) += count;
+ talloc_destroy(mem_ctx2);
+ } while (NT_STATUS_EQUAL(status, STATUS_MORE_ENTRIES));
+
+ cli_samr_close(hnd->cli, mem_ctx, &dom_pol);
+
+ return status;
+}
+
+/* List all domain groups */
+
+static NTSTATUS enum_local_groups(struct winbindd_domain *domain,
+ TALLOC_CTX *mem_ctx,
+ uint32 *num_entries,
+ struct acct_info **info)
+{
+ uint32 des_access = SEC_RIGHTS_MAXIMUM_ALLOWED;
+ CLI_POLICY_HND *hnd;
+ POLICY_HND dom_pol;
+ NTSTATUS result;
+ int retry;
+
+ *num_entries = 0;
+ *info = NULL;
+
+ retry = 0;
+ do {
+ if ( !NT_STATUS_IS_OK(result = cm_get_sam_handle(domain, &hnd)) )
+ return result;
+
+ result = cli_samr_open_domain( hnd->cli, mem_ctx, &hnd->pol,
+ des_access, &domain->sid, &dom_pol);
+ } while (!NT_STATUS_IS_OK(result) && (retry++ < 1) && hnd && hnd->cli && hnd->cli->fd == -1);
+
+ if ( !NT_STATUS_IS_OK(result))
+ return result;
+
+ do {
+ struct acct_info *info2 = NULL;
+ uint32 count = 0, start = *num_entries;
+ TALLOC_CTX *mem_ctx2;
+
+ mem_ctx2 = talloc_init("enum_dom_local_groups[rpc]");
+
+ result = cli_samr_enum_als_groups( hnd->cli, mem_ctx2, &dom_pol,
+ &start, 0xFFFF, &info2, &count);
+
+ if ( !NT_STATUS_IS_OK(result)
+ && !NT_STATUS_EQUAL(result, STATUS_MORE_ENTRIES) )
+ {
+ talloc_destroy(mem_ctx2);
+ break;
+ }
+
+ (*info) = talloc_realloc(mem_ctx, *info,
+ sizeof(**info) * ((*num_entries) + count));
+ if (! *info) {
+ talloc_destroy(mem_ctx2);
+ cli_samr_close(hnd->cli, mem_ctx, &dom_pol);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ memcpy(&(*info)[*num_entries], info2, count*sizeof(*info2));
+ (*num_entries) += count;
+ talloc_destroy(mem_ctx2);
+ } while (NT_STATUS_EQUAL(result, STATUS_MORE_ENTRIES));
+
+ cli_samr_close(hnd->cli, mem_ctx, &dom_pol);
+
+ return result;
+}
+
+/* convert a single name to a sid in a domain */
+static NTSTATUS name_to_sid(struct winbindd_domain *domain,
+ TALLOC_CTX *mem_ctx,
+ const char *name,
+ DOM_SID *sid,
+ enum SID_NAME_USE *type)
+{
+ CLI_POLICY_HND *hnd;
+ NTSTATUS result;
+ DOM_SID *sids = NULL;
+ uint32 *types = NULL;
+ const char *full_name;
+ int retry;
+
+ DEBUG(3,("rpc: name_to_sid name=%s\n", name));
+
+ full_name = talloc_asprintf(mem_ctx, "%s\\%s", domain->name, name);
+
+ if (!full_name) {
+ DEBUG(0, ("talloc_asprintf failed!\n"));
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ DEBUG(3,("name_to_sid [rpc] %s for domain %s\n", name, domain->name ));
+
+ retry = 0;
+ do {
+ if (!NT_STATUS_IS_OK(result = cm_get_lsa_handle(domain, &hnd))) {
+ return result;
+ }
+
+ result = cli_lsa_lookup_names(hnd->cli, mem_ctx, &hnd->pol, 1,
+ &full_name, &sids, &types);
+ } while (!NT_STATUS_IS_OK(result) && (retry++ < 1) &&
+ hnd && hnd->cli && hnd->cli->fd == -1);
+
+ /* Return rid and type if lookup successful */
+
+ if (NT_STATUS_IS_OK(result)) {
+ sid_copy(sid, &sids[0]);
+ *type = (enum SID_NAME_USE)types[0];
+ }
+
+ return result;
+}
+
+/*
+ convert a domain SID to a user or group name
+*/
+static NTSTATUS sid_to_name(struct winbindd_domain *domain,
+ TALLOC_CTX *mem_ctx,
+ const DOM_SID *sid,
+ char **name,
+ enum SID_NAME_USE *type)
+{
+ CLI_POLICY_HND *hnd;
+ char **domains;
+ char **names;
+ uint32 *types;
+ NTSTATUS result;
+ int retry;
+
+ DEBUG(3,("sid_to_name [rpc] %s for domain %s\n", sid_string_static(sid),
+ domain->name ));
+
+ retry = 0;
+ do {
+ if (!NT_STATUS_IS_OK(result = cm_get_lsa_handle(domain, &hnd)))
+ return result;
+
+ result = cli_lsa_lookup_sids(hnd->cli, mem_ctx, &hnd->pol,
+ 1, sid, &domains, &names, &types);
+ } while (!NT_STATUS_IS_OK(result) && (retry++ < 1) &&
+ hnd && hnd->cli && hnd->cli->fd == -1);
+
+ if (NT_STATUS_IS_OK(result)) {
+ *type = (enum SID_NAME_USE)types[0];
+ *name = names[0];
+ DEBUG(5,("Mapped sid to [%s]\\[%s]\n", domains[0], *name));
+
+ /* Paranoia */
+ if (!strequal(domain->name, domains[0])) {
+ DEBUG(1, ("domain name from domain param and PDC lookup return differ! (%s vs %s)\n", domain->name, domains[0]));
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+ }
+
+ return result;
+}
+
+/* Lookup user information from a rid or username. */
+static NTSTATUS query_user(struct winbindd_domain *domain,
+ TALLOC_CTX *mem_ctx,
+ const DOM_SID *user_sid,
+ WINBIND_USERINFO *user_info)
+{
+ CLI_POLICY_HND *hnd = NULL;
+ NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+ POLICY_HND dom_pol, user_pol;
+ BOOL got_dom_pol = False, got_user_pol = False;
+ SAM_USERINFO_CTR *ctr;
+ int retry;
+ fstring sid_string;
+ uint32 user_rid;
+ NET_USER_INFO_3 *user;
+
+ DEBUG(3,("rpc: query_user rid=%s\n", sid_to_string(sid_string, user_sid)));
+ if (!sid_peek_check_rid(&domain->sid, user_sid, &user_rid)) {
+ goto done;
+ }
+
+ /* try netsamlogon cache first */
+
+ if ( (user = netsamlogon_cache_get( mem_ctx, user_sid )) != NULL )
+ {
+
+ DEBUG(5,("query_user: Cache lookup succeeded for %s\n",
+ sid_string_static(user_sid)));
+
+ user_info->user_sid = rid_to_talloced_sid( domain, mem_ctx, user_rid );
+ user_info->group_sid = rid_to_talloced_sid( domain, mem_ctx, user->group_rid );
+
+ user_info->acct_name = unistr2_tdup(mem_ctx, &user->uni_user_name);
+ user_info->full_name = unistr2_tdup(mem_ctx, &user->uni_full_name);
+
+ SAFE_FREE(user);
+
+ return NT_STATUS_OK;
+ }
+
+ /* no cache; hit the wire */
+
+ retry = 0;
+ do {
+ /* Get sam handle; if we fail here there is no hope */
+
+ if (!NT_STATUS_IS_OK(result = cm_get_sam_handle(domain, &hnd)))
+ goto done;
+
+ /* Get domain handle */
+
+ result = cli_samr_open_domain(hnd->cli, mem_ctx, &hnd->pol,
+ SEC_RIGHTS_MAXIMUM_ALLOWED,
+ &domain->sid, &dom_pol);
+ } while (!NT_STATUS_IS_OK(result) && (retry++ < 1) &&
+ hnd && hnd->cli && hnd->cli->fd == -1);
+
+ if (!NT_STATUS_IS_OK(result))
+ goto done;
+
+ got_dom_pol = True;
+
+ /* Get user handle */
+ result = cli_samr_open_user(hnd->cli, mem_ctx, &dom_pol,
+ SEC_RIGHTS_MAXIMUM_ALLOWED, user_rid, &user_pol);
+
+ if (!NT_STATUS_IS_OK(result))
+ goto done;
+
+ got_user_pol = True;
+
+ /* Get user info */
+ result = cli_samr_query_userinfo(hnd->cli, mem_ctx, &user_pol,
+ 0x15, &ctr);
+
+ if (!NT_STATUS_IS_OK(result))
+ goto done;
+
+ cli_samr_close(hnd->cli, mem_ctx, &user_pol);
+ got_user_pol = False;
+
+ user_info->user_sid = rid_to_talloced_sid(domain, mem_ctx, user_rid);
+ user_info->group_sid = rid_to_talloced_sid(domain, mem_ctx, ctr->info.id21->group_rid);
+ user_info->acct_name = unistr2_tdup(mem_ctx,
+ &ctr->info.id21->uni_user_name);
+ user_info->full_name = unistr2_tdup(mem_ctx,
+ &ctr->info.id21->uni_full_name);
+
+ done:
+ /* Clean up policy handles */
+ if (got_user_pol)
+ cli_samr_close(hnd->cli, mem_ctx, &user_pol);
+
+ if (got_dom_pol)
+ cli_samr_close(hnd->cli, mem_ctx, &dom_pol);
+
+ return result;
+}
+
+/* Lookup groups a user is a member of. I wish Unix had a call like this! */
+static NTSTATUS lookup_usergroups(struct winbindd_domain *domain,
+ TALLOC_CTX *mem_ctx,
+ const DOM_SID *user_sid,
+ uint32 *num_groups, DOM_SID ***user_grpsids)
+{
+ CLI_POLICY_HND *hnd;
+ NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+ POLICY_HND dom_pol, user_pol;
+ uint32 des_access = SEC_RIGHTS_MAXIMUM_ALLOWED;
+ BOOL got_dom_pol = False, got_user_pol = False;
+ DOM_GID *user_groups;
+ unsigned int i;
+ unsigned int retry;
+ fstring sid_string;
+ uint32 user_rid;
+ NET_USER_INFO_3 *user;
+
+ DEBUG(3,("rpc: lookup_usergroups sid=%s\n", sid_to_string(sid_string, user_sid)));
+
+ *num_groups = 0;
+ *user_grpsids = NULL;
+
+ /* so lets see if we have a cached user_info_3 */
+
+ if ( (user = netsamlogon_cache_get( mem_ctx, user_sid )) != NULL )
+ {
+ DEBUG(5,("query_user: Cache lookup succeeded for %s\n",
+ sid_string_static(user_sid)));
+
+ *num_groups = user->num_groups;
+
+ (*user_grpsids) = talloc(mem_ctx, sizeof(DOM_SID*) * (*num_groups));
+ for (i=0;i<(*num_groups);i++) {
+ (*user_grpsids)[i] = rid_to_talloced_sid(domain, mem_ctx, user->gids[i].g_rid);
+ }
+
+ SAFE_FREE(user);
+
+ return NT_STATUS_OK;
+ }
+
+ /* no cache; hit the wire */
+
+ retry = 0;
+ do {
+ /* Get sam handle; if we fail here there is no hope */
+
+ if (!NT_STATUS_IS_OK(result = cm_get_sam_handle(domain, &hnd)))
+ goto done;
+
+ /* Get domain handle */
+
+ result = cli_samr_open_domain(hnd->cli, mem_ctx, &hnd->pol,
+ des_access, &domain->sid, &dom_pol);
+ } while (!NT_STATUS_IS_OK(result) && (retry++ < 1) &&
+ hnd && hnd->cli && hnd->cli->fd == -1);
+
+ if (!NT_STATUS_IS_OK(result))
+ goto done;
+
+ got_dom_pol = True;
+
+
+ if (!sid_peek_check_rid(&domain->sid, user_sid, &user_rid)) {
+ goto done;
+ }
+
+ /* Get user handle */
+ result = cli_samr_open_user(hnd->cli, mem_ctx, &dom_pol,
+ des_access, user_rid, &user_pol);
+
+ if (!NT_STATUS_IS_OK(result))
+ goto done;
+
+ got_user_pol = True;
+
+ /* Query user rids */
+ result = cli_samr_query_usergroups(hnd->cli, mem_ctx, &user_pol,
+ num_groups, &user_groups);
+
+ if (!NT_STATUS_IS_OK(result) || (*num_groups) == 0)
+ goto done;
+
+ (*user_grpsids) = talloc(mem_ctx, sizeof(DOM_SID*) * (*num_groups));
+ if (!(*user_grpsids)) {
+ result = NT_STATUS_NO_MEMORY;
+ goto done;
+ }
+
+ for (i=0;i<(*num_groups);i++) {
+ (*user_grpsids)[i] = rid_to_talloced_sid(domain, mem_ctx, user_groups[i].g_rid);
+ }
+
+ done:
+ /* Clean up policy handles */
+ if (got_user_pol)
+ cli_samr_close(hnd->cli, mem_ctx, &user_pol);
+
+ if (got_dom_pol)
+ cli_samr_close(hnd->cli, mem_ctx, &dom_pol);
+
+ return result;
+}
+
+
+/* Lookup group membership given a rid. */
+static NTSTATUS lookup_groupmem(struct winbindd_domain *domain,
+ TALLOC_CTX *mem_ctx,
+ const DOM_SID *group_sid, uint32 *num_names,
+ DOM_SID ***sid_mem, char ***names,
+ uint32 **name_types)
+{
+ CLI_POLICY_HND *hnd = NULL;
+ NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+ uint32 i, total_names = 0;
+ POLICY_HND dom_pol, group_pol;
+ uint32 des_access = SEC_RIGHTS_MAXIMUM_ALLOWED;
+ BOOL got_dom_pol = False, got_group_pol = False;
+ uint32 *rid_mem = NULL;
+ uint32 group_rid;
+ int retry;
+ unsigned int j;
+ fstring sid_string;
+
+ DEBUG(10,("rpc: lookup_groupmem %s sid=%s\n", domain->name, sid_to_string(sid_string, group_sid)));
+
+ if (!sid_peek_check_rid(&domain->sid, group_sid, &group_rid)) {
+ goto done;
+ }
+
+ *num_names = 0;
+
+ retry = 0;
+ do {
+ /* Get sam handle */
+ if (!NT_STATUS_IS_OK(result = cm_get_sam_handle(domain, &hnd)))
+ goto done;
+
+ /* Get domain handle */
+
+ result = cli_samr_open_domain(hnd->cli, mem_ctx, &hnd->pol,
+ des_access, &domain->sid, &dom_pol);
+ } while (!NT_STATUS_IS_OK(result) && (retry++ < 1) && hnd && hnd->cli && hnd->cli->fd == -1);
+
+ if (!NT_STATUS_IS_OK(result))
+ goto done;
+
+ got_dom_pol = True;
+
+ /* Get group handle */
+
+ result = cli_samr_open_group(hnd->cli, mem_ctx, &dom_pol,
+ des_access, group_rid, &group_pol);
+
+ if (!NT_STATUS_IS_OK(result))
+ goto done;
+
+ got_group_pol = True;
+
+ /* Step #1: Get a list of user rids that are the members of the
+ group. */
+
+ result = cli_samr_query_groupmem(hnd->cli, mem_ctx,
+ &group_pol, num_names, &rid_mem,
+ name_types);
+
+ if (!NT_STATUS_IS_OK(result))
+ goto done;
+
+ if (!*num_names) {
+ names = NULL;
+ name_types = NULL;
+ sid_mem = NULL;
+ goto done;
+ }
+
+ /* Step #2: Convert list of rids into list of usernames. Do this
+ in bunches of ~1000 to avoid crashing NT4. It looks like there
+ is a buffer overflow or something like that lurking around
+ somewhere. */
+
+#define MAX_LOOKUP_RIDS 900
+
+ *names = talloc_zero(mem_ctx, *num_names * sizeof(char *));
+ *name_types = talloc_zero(mem_ctx, *num_names * sizeof(uint32));
+ *sid_mem = talloc_zero(mem_ctx, *num_names * sizeof(DOM_SID *));
+
+ for (j=0;j<(*num_names);j++) {
+ (*sid_mem)[j] = rid_to_talloced_sid(domain, mem_ctx, (rid_mem)[j]);
+ }
+
+ if (*num_names>0 && (!*names || !*name_types)) {
+ result = NT_STATUS_NO_MEMORY;
+ goto done;
+ }
+
+ for (i = 0; i < *num_names; i += MAX_LOOKUP_RIDS) {
+ int num_lookup_rids = MIN(*num_names - i, MAX_LOOKUP_RIDS);
+ uint32 tmp_num_names = 0;
+ char **tmp_names = NULL;
+ uint32 *tmp_types = NULL;
+
+ /* Lookup a chunk of rids */
+
+ result = cli_samr_lookup_rids(hnd->cli, mem_ctx,
+ &dom_pol, 1000, /* flags */
+ num_lookup_rids,
+ &rid_mem[i],
+ &tmp_num_names,
+ &tmp_names, &tmp_types);
+
+ /* see if we have a real error (and yes the STATUS_SOME_UNMAPPED is
+ the one returned from 2k) */
+
+ if (!NT_STATUS_IS_OK(result) && NT_STATUS_V(result) != NT_STATUS_V(STATUS_SOME_UNMAPPED))
+ goto done;
+
+ /* Copy result into array. The talloc system will take
+ care of freeing the temporary arrays later on. */
+
+ memcpy(&(*names)[i], tmp_names, sizeof(char *) *
+ tmp_num_names);
+
+ memcpy(&(*name_types)[i], tmp_types, sizeof(uint32) *
+ tmp_num_names);
+
+ total_names += tmp_num_names;
+ }
+
+ *num_names = total_names;
+
+ result = NT_STATUS_OK;
+
+done:
+ if (got_group_pol)
+ cli_samr_close(hnd->cli, mem_ctx, &group_pol);
+
+ if (got_dom_pol)
+ cli_samr_close(hnd->cli, mem_ctx, &dom_pol);
+
+ return result;
+}
+
+#ifdef HAVE_LDAP
+
+#include <ldap.h>
+
+static SIG_ATOMIC_T gotalarm;
+
+/***************************************************************
+ Signal function to tell us we timed out.
+****************************************************************/
+
+static void gotalarm_sig(void)
+{
+ gotalarm = 1;
+}
+
+static LDAP *ldap_open_with_timeout(const char *server, int port, unsigned int to)
+{
+ LDAP *ldp = NULL;
+
+ /* Setup timeout */
+ gotalarm = 0;
+ CatchSignal(SIGALRM, SIGNAL_CAST gotalarm_sig);
+ alarm(to);
+ /* End setup timeout. */
+
+ ldp = ldap_open(server, port);
+
+ /* Teardown timeout. */
+ CatchSignal(SIGALRM, SIGNAL_CAST SIG_IGN);
+ alarm(0);
+
+ return ldp;
+}
+
+static int get_ldap_seq(const char *server, int port, uint32 *seq)
+{
+ int ret = -1;
+ struct timeval to;
+ char *attrs[] = {"highestCommittedUSN", NULL};
+ LDAPMessage *res = NULL;
+ char **values = NULL;
+ LDAP *ldp = NULL;
+
+ *seq = DOM_SEQUENCE_NONE;
+
+ /*
+ * 10 second timeout on open. This is needed as the search timeout
+ * doesn't seem to apply to doing an open as well. JRA.
+ */
+
+ if ((ldp = ldap_open_with_timeout(server, port, 10)) == NULL)
+ return -1;
+
+ /* Timeout if no response within 20 seconds. */
+ to.tv_sec = 10;
+ to.tv_usec = 0;
+
+ if (ldap_search_st(ldp, "", LDAP_SCOPE_BASE, "(objectclass=*)", &attrs[0], 0, &to, &res))
+ goto done;
+
+ if (ldap_count_entries(ldp, res) != 1)
+ goto done;
+
+ values = ldap_get_values(ldp, res, "highestCommittedUSN");
+ if (!values || !values[0])
+ goto done;
+
+ *seq = atoi(values[0]);
+ ret = 0;
+
+ done:
+
+ if (values)
+ ldap_value_free(values);
+ if (res)
+ ldap_msgfree(res);
+ if (ldp)
+ ldap_unbind(ldp);
+ return ret;
+}
+
+/**********************************************************************
+ Get the sequence number for a Windows AD native mode domain using
+ LDAP queries
+**********************************************************************/
+
+static int get_ldap_sequence_number( const char* domain, uint32 *seq)
+{
+ int ret = -1;
+ int i, port = LDAP_PORT;
+ struct ip_service *ip_list = NULL;
+ int count;
+
+ if ( !get_sorted_dc_list(domain, &ip_list, &count, False) ) {
+ DEBUG(3, ("Could not look up dc's for domain %s\n", domain));
+ return False;
+ }
+
+ /* Finally return first DC that we can contact */
+
+ for (i = 0; i < count; i++) {
+ fstring ipstr;
+
+ /* since the is an LDAP lookup, default to the LDAP_PORT is not set */
+ port = (ip_list[i].port!= PORT_NONE) ? ip_list[i].port : LDAP_PORT;
+
+ fstrcpy( ipstr, inet_ntoa(ip_list[i].ip) );
+
+ if (is_zero_ip(ip_list[i].ip))
+ continue;
+
+ if ( (ret = get_ldap_seq( ipstr, port, seq)) == 0 )
+ goto done;
+
+ /* add to failed connection cache */
+ add_failed_connection_entry( domain, ipstr, NT_STATUS_UNSUCCESSFUL );
+ }
+
+done:
+ if ( ret == 0 ) {
+ DEBUG(3, ("get_ldap_sequence_number: Retrieved sequence number for Domain (%s) from DC (%s:%d)\n",
+ domain, inet_ntoa(ip_list[i].ip), port));
+ }
+
+ SAFE_FREE(ip_list);
+
+ return ret;
+}
+
+#endif /* HAVE_LDAP */
+
+/* find the sequence number for a domain */
+static NTSTATUS sequence_number(struct winbindd_domain *domain, uint32 *seq)
+{
+ TALLOC_CTX *mem_ctx;
+ CLI_POLICY_HND *hnd;
+ SAM_UNK_CTR ctr;
+ uint16 switch_value = 2;
+ NTSTATUS result;
+ POLICY_HND dom_pol;
+ BOOL got_dom_pol = False;
+ uint32 des_access = SEC_RIGHTS_MAXIMUM_ALLOWED;
+ int retry;
+
+ DEBUG(10,("rpc: fetch sequence_number for %s\n", domain->name));
+
+ *seq = DOM_SEQUENCE_NONE;
+
+ if (!(mem_ctx = talloc_init("sequence_number[rpc]")))
+ return NT_STATUS_NO_MEMORY;
+
+ retry = 0;
+ do {
+#ifdef HAVE_LDAP
+ if ( domain->native_mode )
+ {
+ DEBUG(8,("using get_ldap_seq() to retrieve the sequence number\n"));
+
+ if ( get_ldap_sequence_number( domain->name, seq ) == 0 ) {
+ result = NT_STATUS_OK;
+ DEBUG(10,("domain_sequence_number: LDAP for domain %s is %u\n",
+ domain->name, *seq));
+ goto done;
+ }
+
+ DEBUG(10,("domain_sequence_number: failed to get LDAP sequence number for domain %s\n",
+ domain->name ));
+ }
+#endif /* HAVE_LDAP */
+ /* Get sam handle */
+ if (!NT_STATUS_IS_OK(result = cm_get_sam_handle(domain, &hnd)))
+ goto done;
+
+ /* Get domain handle */
+ result = cli_samr_open_domain(hnd->cli, mem_ctx, &hnd->pol,
+ des_access, &domain->sid, &dom_pol);
+ } while (!NT_STATUS_IS_OK(result) && (retry++ < 1) && hnd && hnd->cli && hnd->cli->fd == -1);
+
+ if (!NT_STATUS_IS_OK(result))
+ goto done;
+
+ got_dom_pol = True;
+
+ /* Query domain info */
+
+ result = cli_samr_query_dom_info(hnd->cli, mem_ctx, &dom_pol,
+ switch_value, &ctr);
+
+ if (NT_STATUS_IS_OK(result)) {
+ *seq = ctr.info.inf2.seq_num;
+ DEBUG(10,("domain_sequence_number: for domain %s is %u\n", domain->name, (unsigned)*seq));
+ } else {
+ DEBUG(10,("domain_sequence_number: failed to get sequence number (%u) for domain %s\n",
+ (unsigned)*seq, domain->name ));
+ }
+
+ done:
+
+ if (got_dom_pol)
+ cli_samr_close(hnd->cli, mem_ctx, &dom_pol);
+
+ talloc_destroy(mem_ctx);
+
+ return result;
+}
+
+/* get a list of trusted domains */
+static NTSTATUS trusted_domains(struct winbindd_domain *domain,
+ TALLOC_CTX *mem_ctx,
+ uint32 *num_domains,
+ char ***names,
+ char ***alt_names,
+ DOM_SID **dom_sids)
+{
+ CLI_POLICY_HND *hnd;
+ NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+ uint32 enum_ctx = 0;
+ int retry;
+
+ DEBUG(3,("rpc: trusted_domains\n"));
+
+ *num_domains = 0;
+ *alt_names = NULL;
+
+ retry = 0;
+ do {
+ if (!NT_STATUS_IS_OK(result = cm_get_lsa_handle(find_our_domain(), &hnd)))
+ goto done;
+
+ result = cli_lsa_enum_trust_dom(hnd->cli, mem_ctx,
+ &hnd->pol, &enum_ctx,
+ num_domains, names, dom_sids);
+ } while (!NT_STATUS_IS_OK(result) && (retry++ < 1) && hnd && hnd->cli && hnd->cli->fd == -1);
+
+done:
+ return result;
+}
+
+/* find the domain sid for a domain */
+static NTSTATUS domain_sid(struct winbindd_domain *domain, DOM_SID *sid)
+{
+ NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+ TALLOC_CTX *mem_ctx;
+ CLI_POLICY_HND *hnd;
+ char *level5_dom;
+ DOM_SID *alloc_sid;
+ int retry;
+
+ DEBUG(3,("rpc: domain_sid\n"));
+
+ if (!(mem_ctx = talloc_init("domain_sid[rpc]")))
+ return NT_STATUS_NO_MEMORY;
+
+ retry = 0;
+ do {
+ /* Get lsa handle */
+ if (!NT_STATUS_IS_OK(result = cm_get_lsa_handle(domain, &hnd)))
+ goto done;
+
+ result = cli_lsa_query_info_policy(hnd->cli, mem_ctx,
+ &hnd->pol, 0x05, &level5_dom, &alloc_sid);
+ } while (!NT_STATUS_IS_OK(result) && (retry++ < 1) && hnd && hnd->cli && hnd->cli->fd == -1);
+
+ if (NT_STATUS_IS_OK(result)) {
+ if (alloc_sid) {
+ sid_copy(sid, alloc_sid);
+ } else {
+ result = NT_STATUS_NO_MEMORY;
+ }
+ }
+
+done:
+ talloc_destroy(mem_ctx);
+ return result;
+}
+
+/* find alternate names list for the domain - none for rpc */
+static NTSTATUS alternate_name(struct winbindd_domain *domain)
+{
+ return NT_STATUS_OK;
+}
+
+
+/* the rpc backend methods are exposed via this structure */
+struct winbindd_methods msrpc_methods = {
+ False,
+ query_user_list,
+ enum_dom_groups,
+ enum_local_groups,
+ name_to_sid,
+ sid_to_name,
+ query_user,
+ lookup_usergroups,
+ lookup_groupmem,
+ sequence_number,
+ trusted_domains,
+ domain_sid,
+ alternate_name
+};
diff --git a/source/nsswitch/winbindd_sid.c b/source/nsswitch/winbindd_sid.c
new file mode 100644
index 00000000000..d4206558c5e
--- /dev/null
+++ b/source/nsswitch/winbindd_sid.c
@@ -0,0 +1,456 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ Winbind daemon - sid related functions
+
+ 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 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 "winbindd.h"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_WINBIND
+
+/* Convert a string */
+
+enum winbindd_result winbindd_lookupsid(struct winbindd_cli_state *state)
+{
+ enum SID_NAME_USE type;
+ DOM_SID sid;
+ fstring name;
+ fstring dom_name;
+
+ /* Ensure null termination */
+ state->request.data.sid[sizeof(state->request.data.sid)-1]='\0';
+
+ DEBUG(3, ("[%5lu]: lookupsid %s\n", (unsigned long)state->pid,
+ state->request.data.sid));
+
+ /* Lookup sid from PDC using lsa_lookup_sids() */
+
+ if (!string_to_sid(&sid, state->request.data.sid)) {
+ DEBUG(5, ("%s not a SID\n", state->request.data.sid));
+ return WINBINDD_ERROR;
+ }
+
+ /* Lookup the sid */
+
+ if (!winbindd_lookup_name_by_sid(&sid, dom_name, name, &type)) {
+ return WINBINDD_ERROR;
+ }
+
+ fstrcpy(state->response.data.name.dom_name, dom_name);
+ fstrcpy(state->response.data.name.name, name);
+
+ state->response.data.name.type = type;
+
+ return WINBINDD_OK;
+}
+
+
+/**
+ * Look up the SID for a qualified name.
+ **/
+enum winbindd_result winbindd_lookupname(struct winbindd_cli_state *state)
+{
+ enum SID_NAME_USE type;
+ fstring sid_str;
+ char *name_domain, *name_user;
+ DOM_SID sid;
+ struct winbindd_domain *domain;
+ char *p;
+
+ /* Ensure null termination */
+ state->request.data.sid[sizeof(state->request.data.name.dom_name)-1]='\0';
+
+ /* Ensure null termination */
+ state->request.data.sid[sizeof(state->request.data.name.name)-1]='\0';
+
+ /* cope with the name being a fully qualified name */
+ p = strstr(state->request.data.name.name, lp_winbind_separator());
+ if (p) {
+ *p = 0;
+ name_domain = state->request.data.name.name;
+ name_user = p+1;
+ } else {
+ name_domain = state->request.data.name.dom_name;
+ name_user = state->request.data.name.name;
+ }
+
+ DEBUG(3, ("[%5lu]: lookupname %s%s%s\n", (unsigned long)state->pid,
+ name_domain, lp_winbind_separator(), name_user));
+
+ if ((domain = find_domain_from_name(name_domain)) == NULL) {
+ DEBUG(0, ("could not find domain entry for domain %s\n",
+ name_domain));
+ return WINBINDD_ERROR;
+ }
+
+ /* Lookup name from PDC using lsa_lookup_names() */
+ if (!winbindd_lookup_sid_by_name(domain, name_user, &sid, &type)) {
+ return WINBINDD_ERROR;
+ }
+
+ sid_to_string(sid_str, &sid);
+ fstrcpy(state->response.data.sid.sid, sid_str);
+ state->response.data.sid.type = type;
+
+ return WINBINDD_OK;
+}
+
+/* Convert a sid to a uid. We assume we only have one rid attached to the
+ sid. */
+
+enum winbindd_result winbindd_sid_to_uid(struct winbindd_cli_state *state)
+{
+ DOM_SID sid;
+ uint32 flags = 0x0;
+
+ /* Ensure null termination */
+ state->request.data.sid[sizeof(state->request.data.sid)-1]='\0';
+
+ DEBUG(3, ("[%5lu]: sid to uid %s\n", (unsigned long)state->pid,
+ state->request.data.sid));
+
+ if (!string_to_sid(&sid, state->request.data.sid)) {
+ DEBUG(1, ("Could not get convert sid %s from string\n", state->request.data.sid));
+ return WINBINDD_ERROR;
+ }
+
+ /* This gets a little tricky. If we assume that usernames are syncd between
+ /etc/passwd and the windows domain (such as a member of a Samba domain),
+ the we need to get the uid from the OS and not alocate one ourselves */
+
+ if ( lp_winbind_trusted_domains_only() ) {
+ struct winbindd_domain *domain = NULL;
+ DOM_SID sid2;
+ uint32 rid;
+
+ domain = find_our_domain();
+ if ( !domain ) {
+ DEBUG(0,("winbindd_sid_to_uid: can't find my own domain!\n"));
+ return WINBINDD_ERROR;
+ }
+
+ sid_copy( &sid2, &sid );
+ sid_split_rid( &sid2, &rid );
+
+ if ( sid_equal( &sid2, &domain->sid ) ) {
+
+ fstring domain_name;
+ fstring user;
+ enum SID_NAME_USE type;
+ struct passwd *pw = NULL;
+ unid_t id;
+
+ /* ok...here's we know that we are dealing with our
+ own domain (the one to which we are joined). And
+ we know that there must be a UNIX account for this user.
+ So we lookup the sid and the call getpwnam().*/
+
+
+ /* But first check and see if we don't already have a mapping */
+
+ flags = ID_QUERY_ONLY;
+ if ( NT_STATUS_IS_OK(idmap_sid_to_uid(&sid, &(state->response.data.uid), flags)) )
+ return WINBINDD_OK;
+
+ /* now fall back to the hard way */
+
+ if ( !winbindd_lookup_name_by_sid(&sid, domain_name, user, &type) )
+ return WINBINDD_ERROR;
+
+ if ( !(pw = getpwnam(user)) ) {
+ DEBUG(0,("winbindd_sid_to_uid: 'winbind trusted domains only' is "
+ "set but this user [%s] doesn't exist!\n", user));
+ return WINBINDD_ERROR;
+ }
+
+ state->response.data.uid = pw->pw_uid;
+
+ id.uid = pw->pw_uid;
+ idmap_set_mapping( &sid, id, ID_USERID );
+
+ return WINBINDD_OK;
+ }
+
+ }
+
+ if ( state->request.flags & WBFLAG_QUERY_ONLY )
+ flags = ID_QUERY_ONLY;
+
+ /* Find uid for this sid and return it */
+
+ if ( !NT_STATUS_IS_OK(idmap_sid_to_uid(&sid, &(state->response.data.uid), flags)) ) {
+ DEBUG(1, ("Could not get uid for sid %s\n", state->request.data.sid));
+ return WINBINDD_ERROR;
+ }
+
+ return WINBINDD_OK;
+}
+
+/* Convert a sid to a gid. We assume we only have one rid attached to the
+ sid.*/
+
+enum winbindd_result winbindd_sid_to_gid(struct winbindd_cli_state *state)
+{
+ DOM_SID sid;
+ uint32 flags = 0x0;
+
+ /* Ensure null termination */
+ state->request.data.sid[sizeof(state->request.data.sid)-1]='\0';
+
+ DEBUG(3, ("[%5lu]: sid to gid %s\n", (unsigned long)state->pid,
+ state->request.data.sid));
+
+ if (!string_to_sid(&sid, state->request.data.sid)) {
+ DEBUG(1, ("Could not cvt string to sid %s\n", state->request.data.sid));
+ return WINBINDD_ERROR;
+ }
+
+ /* This gets a little tricky. If we assume that usernames are syncd between
+ /etc/passwd and the windows domain (such as a member of a Samba domain),
+ the we need to get the uid from the OS and not alocate one ourselves */
+
+ if ( lp_winbind_trusted_domains_only() ) {
+ struct winbindd_domain *domain = NULL;
+ DOM_SID sid2;
+ uint32 rid;
+ unid_t id;
+
+ domain = find_our_domain();
+ if ( !domain ) {
+ DEBUG(0,("winbindd_sid_to_uid: can't find my own domain!\n"));
+ return WINBINDD_ERROR;
+ }
+
+ sid_copy( &sid2, &sid );
+ sid_split_rid( &sid2, &rid );
+
+ if ( sid_equal( &sid2, &domain->sid ) ) {
+
+ fstring domain_name;
+ fstring group;
+ enum SID_NAME_USE type;
+ struct group *grp = NULL;
+
+ /* ok...here's we know that we are dealing with our
+ own domain (the one to which we are joined). And
+ we know that there must be a UNIX account for this group.
+ So we lookup the sid and the call getpwnam().*/
+
+ /* But first check and see if we don't already have a mapping */
+
+ flags = ID_QUERY_ONLY;
+ if ( NT_STATUS_IS_OK(idmap_sid_to_gid(&sid, &(state->response.data.gid), flags)) )
+ return WINBINDD_OK;
+
+ /* now fall back to the hard way */
+
+ if ( !winbindd_lookup_name_by_sid(&sid, domain_name, group, &type) )
+ return WINBINDD_ERROR;
+
+ if ( !(grp = getgrnam(group)) ) {
+ DEBUG(0,("winbindd_sid_to_uid: 'winbind trusted domains only' is "
+ "set but this group [%s] doesn't exist!\n", group));
+ return WINBINDD_ERROR;
+ }
+
+ state->response.data.gid = grp->gr_gid;
+
+ id.gid = grp->gr_gid;
+ idmap_set_mapping( &sid, id, ID_GROUPID );
+
+ return WINBINDD_OK;
+ }
+
+ }
+
+ if ( state->request.flags & WBFLAG_QUERY_ONLY )
+ flags = ID_QUERY_ONLY;
+
+ /* Find gid for this sid and return it */
+ if ( !NT_STATUS_IS_OK(idmap_sid_to_gid(&sid, &(state->response.data.gid), flags)) ) {
+ DEBUG(1, ("Could not get gid for sid %s\n", state->request.data.sid));
+ return WINBINDD_ERROR;
+ }
+
+ return WINBINDD_OK;
+}
+
+/* Convert a uid to a sid */
+
+enum winbindd_result winbindd_uid_to_sid(struct winbindd_cli_state *state)
+{
+ DOM_SID sid;
+
+ DEBUG(3, ("[%5lu]: uid to sid %lu\n", (unsigned long)state->pid,
+ (unsigned long)state->request.data.uid));
+
+ if ( (state->request.data.uid < server_state.uid_low )
+ || (state->request.data.uid > server_state.uid_high) )
+ {
+ struct passwd *pw = NULL;
+ enum SID_NAME_USE type;
+ unid_t id;
+ struct winbindd_domain *domain;
+
+ /* SPECIAL CASE FOR MEMBERS OF SAMBA DOMAINS */
+
+ /* if we don't trust /etc/password then when can't know
+ anything about this uid */
+
+ if ( !lp_winbind_trusted_domains_only() )
+ return WINBINDD_ERROR;
+
+
+ /* look for an idmap entry first */
+
+ if ( NT_STATUS_IS_OK(idmap_uid_to_sid(&sid, state->request.data.uid)) )
+ goto done;
+
+ /* if users exist in /etc/passwd, we should try to
+ use that uid. Get the username and the lookup the SID */
+
+ if ( !(pw = getpwuid(state->request.data.uid)) )
+ return WINBINDD_ERROR;
+
+ if ( !(domain = find_our_domain()) ) {
+ DEBUG(0,("winbindd_uid_to_sid: can't find my own domain!\n"));
+ return WINBINDD_ERROR;
+ }
+
+ if ( !winbindd_lookup_sid_by_name(domain, pw->pw_name, &sid, &type) )
+ return WINBINDD_ERROR;
+
+ if ( type != SID_NAME_USER )
+ return WINBINDD_ERROR;
+
+ /* don't fail if we can't store it */
+
+ id.uid = pw->pw_uid;
+ idmap_set_mapping( &sid, id, ID_USERID );
+
+ goto done;
+ }
+
+ /* Lookup rid for this uid */
+
+ if (!NT_STATUS_IS_OK(idmap_uid_to_sid(&sid, state->request.data.uid))) {
+ DEBUG(1, ("Could not convert uid %lu to rid\n",
+ (unsigned long)state->request.data.uid));
+ return WINBINDD_ERROR;
+ }
+
+done:
+ sid_to_string(state->response.data.sid.sid, &sid);
+ state->response.data.sid.type = SID_NAME_USER;
+
+ return WINBINDD_OK;
+}
+
+/* Convert a gid to a sid */
+
+enum winbindd_result winbindd_gid_to_sid(struct winbindd_cli_state *state)
+{
+ DOM_SID sid;
+
+ DEBUG(3, ("[%5lu]: gid to sid %lu\n", (unsigned long)state->pid,
+ (unsigned long)state->request.data.gid));
+
+ if ( (state->request.data.gid < server_state.gid_low)
+ || (state->request.data.gid > server_state.gid_high) )
+ {
+ struct group *grp = NULL;
+ enum SID_NAME_USE type;
+ unid_t id;
+ struct winbindd_domain *domain;
+
+ /* SPECIAL CASE FOR MEMBERS OF SAMBA DOMAINS */
+
+ /* if we don't trust /etc/group then when can't know
+ anything about this gid */
+
+ if ( !lp_winbind_trusted_domains_only() )
+ return WINBINDD_ERROR;
+
+ /* look for an idmap entry first */
+
+ if ( NT_STATUS_IS_OK(idmap_gid_to_sid(&sid, state->request.data.gid)) )
+ goto done;
+
+ /* if users exist in /etc/group, we should try to
+ use that gid. Get the username and the lookup the SID */
+
+ if ( !(grp = getgrgid(state->request.data.gid)) )
+ return WINBINDD_ERROR;
+
+ if ( !(domain = find_our_domain()) ) {
+ DEBUG(0,("winbindd_uid_to_sid: can't find my own domain!\n"));
+ return WINBINDD_ERROR;
+ }
+
+ if ( !winbindd_lookup_sid_by_name(domain, grp->gr_name, &sid, &type) )
+ return WINBINDD_ERROR;
+
+ if ( type!=SID_NAME_DOM_GRP && type!=SID_NAME_ALIAS )
+ return WINBINDD_ERROR;
+
+ /* don't fail if we can't store it */
+
+ id.gid = grp->gr_gid;
+ idmap_set_mapping( &sid, id, ID_GROUPID );
+
+ goto done;
+ }
+
+ /* Lookup sid for this uid */
+
+ if (!NT_STATUS_IS_OK(idmap_gid_to_sid(&sid, state->request.data.gid))) {
+ DEBUG(1, ("Could not convert gid %lu to sid\n",
+ (unsigned long)state->request.data.gid));
+ return WINBINDD_ERROR;
+ }
+
+done:
+ /* Construct sid and return it */
+ sid_to_string(state->response.data.sid.sid, &sid);
+ state->response.data.sid.type = SID_NAME_DOM_GRP;
+
+ return WINBINDD_OK;
+}
+
+enum winbindd_result winbindd_allocate_rid(struct winbindd_cli_state *state)
+{
+ if ( !state->privileged ) {
+ DEBUG(2, ("winbindd_allocate_rid: non-privileged access "
+ "denied!\n"));
+ return WINBINDD_ERROR;
+ }
+
+ /* We tell idmap to always allocate a user RID. There might be a good
+ * reason to keep RID allocation for users to even and groups to
+ * odd. This needs discussion I think. For now only allocate user
+ * rids. */
+
+ if (!NT_STATUS_IS_OK(idmap_allocate_rid(&state->response.data.rid,
+ USER_RID_TYPE)))
+ return WINBINDD_ERROR;
+
+ return WINBINDD_OK;
+}
diff --git a/source/nsswitch/winbindd_user.c b/source/nsswitch/winbindd_user.c
new file mode 100644
index 00000000000..d08377c888f
--- /dev/null
+++ b/source/nsswitch/winbindd_user.c
@@ -0,0 +1,667 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ Winbind daemon - user related functions
+
+ Copyright (C) Tim Potter 2000
+ Copyright (C) Jeremy Allison 2001.
+ Copyright (C) Gerald (Jerry) Carter 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 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 "winbindd.h"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_WINBIND
+
+extern userdom_struct current_user_info;
+
+/* Fill a pwent structure with information we have obtained */
+
+static BOOL winbindd_fill_pwent(char *dom_name, char *user_name,
+ DOM_SID *user_sid, DOM_SID *group_sid,
+ char *full_name, struct winbindd_pw *pw)
+{
+ fstring output_username;
+ char *homedir;
+ char *shell;
+ fstring sid_string;
+
+ if (!pw || !dom_name || !user_name)
+ return False;
+
+ /* Resolve the uid number */
+
+ if (!NT_STATUS_IS_OK(idmap_sid_to_uid(user_sid, &(pw->pw_uid), 0))) {
+ DEBUG(1, ("error getting user id for sid %s\n", sid_to_string(sid_string, user_sid)));
+ return False;
+ }
+
+ /* Resolve the gid number */
+
+ if (!NT_STATUS_IS_OK(idmap_sid_to_gid(group_sid, &(pw->pw_gid), 0))) {
+ DEBUG(1, ("error getting group id for sid %s\n", sid_to_string(sid_string, group_sid)));
+ return False;
+ }
+
+ /* Username */
+
+ fill_domain_username(output_username, dom_name, user_name);
+
+ safe_strcpy(pw->pw_name, output_username, sizeof(pw->pw_name) - 1);
+
+ /* Full name (gecos) */
+
+ safe_strcpy(pw->pw_gecos, full_name, sizeof(pw->pw_gecos) - 1);
+
+ /* Home directory and shell - use template config parameters. The
+ defaults are /tmp for the home directory and /bin/false for
+ shell. */
+
+ /* The substitution of %U and %D in the 'template homedir' is done
+ by alloc_sub_specified() below. */
+
+ fstrcpy(current_user_info.domain, dom_name);
+
+ homedir = alloc_sub_specified(lp_template_homedir(), user_name, dom_name, pw->pw_uid, pw->pw_gid);
+
+ if (!homedir)
+ return False;
+
+ safe_strcpy(pw->pw_dir, homedir, sizeof(pw->pw_dir) - 1);
+
+ SAFE_FREE(homedir);
+
+ shell = alloc_sub_specified(lp_template_shell(), user_name, dom_name, pw->pw_uid, pw->pw_gid);
+
+ if (!shell)
+ return False;
+
+ safe_strcpy(pw->pw_shell, shell,
+ sizeof(pw->pw_shell) - 1);
+
+ /* Password - set to "x" as we can't generate anything useful here.
+ Authentication can be done using the pam_winbind module. */
+
+ safe_strcpy(pw->pw_passwd, "x", sizeof(pw->pw_passwd) - 1);
+
+ return True;
+}
+
+/* Return a password structure from a username. */
+
+enum winbindd_result winbindd_getpwnam(struct winbindd_cli_state *state)
+{
+ WINBIND_USERINFO user_info;
+ WINBINDD_PW *pw;
+ DOM_SID user_sid;
+ NTSTATUS status;
+ fstring name_domain, name_user;
+ enum SID_NAME_USE name_type;
+ struct winbindd_domain *domain;
+ TALLOC_CTX *mem_ctx;
+
+ /* Ensure null termination */
+ state->request.data.username[sizeof(state->request.data.username)-1]='\0';
+
+ DEBUG(3, ("[%5lu]: getpwnam %s\n", (unsigned long)state->pid,
+ state->request.data.username));
+
+ /* Parse domain and username */
+
+ parse_domain_user(state->request.data.username,
+ name_domain, name_user);
+
+ /* if this is our local domain (or no domain), the do a local tdb search */
+
+ if ( !*name_domain || strequal(name_domain, get_global_sam_name()) ) {
+ if ( !(pw = wb_getpwnam(name_user)) ) {
+ DEBUG(5,("winbindd_getpwnam: lookup for %s\\%s failed\n",
+ name_domain, name_user));
+ return WINBINDD_ERROR;
+ }
+ memcpy( &state->response.data.pw, pw, sizeof(WINBINDD_PW) );
+ return WINBINDD_OK;
+ }
+
+ /* should we deal with users for our domain? */
+
+ if ((domain = find_domain_from_name(name_domain)) == NULL) {
+ DEBUG(5, ("no such domain: %s\n", name_domain));
+ return WINBINDD_ERROR;
+ }
+
+ if ( domain->primary && lp_winbind_trusted_domains_only()) {
+ DEBUG(7,("winbindd_getpwnam: My domain -- rejecting getpwnam() for %s\\%s.\n",
+ name_domain, name_user));
+ return WINBINDD_ERROR;
+ }
+
+ /* Get rid and name type from name */
+
+ if (!winbindd_lookup_sid_by_name(domain, name_user, &user_sid, &name_type)) {
+ DEBUG(1, ("user '%s' does not exist\n", name_user));
+ return WINBINDD_ERROR;
+ }
+
+ if (name_type != SID_NAME_USER && name_type != SID_NAME_COMPUTER) {
+ DEBUG(1, ("name '%s' is not a user name: %d\n", name_user,
+ name_type));
+ return WINBINDD_ERROR;
+ }
+
+ /* Get some user info. */
+
+ if (!(mem_ctx = talloc_init("winbindd_getpwnam([%s]\\[%s])",
+ name_domain, name_user))) {
+ DEBUG(1, ("out of memory\n"));
+ return WINBINDD_ERROR;
+ }
+
+ status = domain->methods->query_user(domain, mem_ctx, &user_sid,
+ &user_info);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(1, ("error getting user info for user '[%s]\\[%s]'\n",
+ name_domain, name_user));
+ talloc_destroy(mem_ctx);
+ return WINBINDD_ERROR;
+ }
+
+ /* Now take all this information and fill in a passwd structure */
+ if (!winbindd_fill_pwent(name_domain, name_user,
+ user_info.user_sid, user_info.group_sid,
+ user_info.full_name,
+ &state->response.data.pw)) {
+ talloc_destroy(mem_ctx);
+ return WINBINDD_ERROR;
+ }
+
+ talloc_destroy(mem_ctx);
+
+ return WINBINDD_OK;
+}
+
+/* Return a password structure given a uid number */
+
+enum winbindd_result winbindd_getpwuid(struct winbindd_cli_state *state)
+{
+ DOM_SID user_sid;
+ struct winbindd_domain *domain;
+ WINBINDD_PW *pw;
+ fstring dom_name;
+ fstring user_name;
+ enum SID_NAME_USE name_type;
+ WINBIND_USERINFO user_info;
+ TALLOC_CTX *mem_ctx;
+ NTSTATUS status;
+ gid_t gid;
+
+ /* Bug out if the uid isn't in the winbind range */
+
+ if ((state->request.data.uid < server_state.uid_low ) ||
+ (state->request.data.uid > server_state.uid_high))
+ return WINBINDD_ERROR;
+
+ DEBUG(3, ("[%5lu]: getpwuid %lu\n", (unsigned long)state->pid,
+ (unsigned long)state->request.data.uid));
+
+ /* always try local tdb first */
+
+ if ( (pw = wb_getpwuid(state->request.data.uid)) != NULL ) {
+ memcpy( &state->response.data.pw, pw, sizeof(WINBINDD_PW) );
+ return WINBINDD_OK;
+ }
+
+ /* Get rid from uid */
+
+ if (!NT_STATUS_IS_OK(idmap_uid_to_sid(&user_sid, state->request.data.uid))) {
+ DEBUG(1, ("could not convert uid %lu to SID\n",
+ (unsigned long)state->request.data.uid));
+ return WINBINDD_ERROR;
+ }
+
+ /* Get name and name type from rid */
+
+ if (!winbindd_lookup_name_by_sid(&user_sid, dom_name, user_name, &name_type)) {
+ fstring temp;
+
+ sid_to_string(temp, &user_sid);
+ DEBUG(1, ("could not lookup sid %s\n", temp));
+ return WINBINDD_ERROR;
+ }
+
+ domain = find_domain_from_sid(&user_sid);
+
+ if (!domain) {
+ DEBUG(1,("Can't find domain from sid\n"));
+ return WINBINDD_ERROR;
+ }
+
+ /* Get some user info */
+
+ if (!(mem_ctx = talloc_init("winbind_getpwuid(%lu)",
+ (unsigned long)state->request.data.uid))) {
+
+ DEBUG(1, ("out of memory\n"));
+ return WINBINDD_ERROR;
+ }
+
+ status = domain->methods->query_user(domain, mem_ctx, &user_sid,
+ &user_info);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(1, ("error getting user info for user '%s'\n",
+ user_name));
+ talloc_destroy(mem_ctx);
+ return WINBINDD_ERROR;
+ }
+
+ /* Check group has a gid number */
+
+ if (!NT_STATUS_IS_OK(idmap_sid_to_gid(user_info.group_sid, &gid, 0))) {
+ DEBUG(1, ("error getting group id for user %s\n", user_name));
+ talloc_destroy(mem_ctx);
+ return WINBINDD_ERROR;
+ }
+
+ /* Fill in password structure */
+
+ if (!winbindd_fill_pwent(domain->name, user_name, user_info.user_sid,
+ user_info.group_sid,
+ user_info.full_name, &state->response.data.pw)) {
+ talloc_destroy(mem_ctx);
+ return WINBINDD_ERROR;
+ }
+
+ talloc_destroy(mem_ctx);
+
+ return WINBINDD_OK;
+}
+
+/*
+ * set/get/endpwent functions
+ */
+
+/* Rewind file pointer for ntdom passwd database */
+
+enum winbindd_result winbindd_setpwent(struct winbindd_cli_state *state)
+{
+ struct winbindd_domain *domain;
+
+ DEBUG(3, ("[%5lu]: setpwent\n", (unsigned long)state->pid));
+
+ /* Check user has enabled this */
+
+ if (!lp_winbind_enum_users())
+ return WINBINDD_ERROR;
+
+ /* Free old static data if it exists */
+
+ if (state->getpwent_state != NULL) {
+ free_getent_state(state->getpwent_state);
+ state->getpwent_state = NULL;
+ }
+
+#if 0 /* JERRY */
+ /* add any local users we have */
+
+ if ( (domain_state = (struct getent_state *)malloc(sizeof(struct getent_state))) == NULL )
+ return WINBINDD_ERROR;
+
+ ZERO_STRUCTP(domain_state);
+
+ /* Add to list of open domains */
+
+ DLIST_ADD(state->getpwent_state, domain_state);
+#endif
+
+ /* Create sam pipes for each domain we know about */
+
+ for(domain = domain_list(); domain != NULL; domain = domain->next) {
+ struct getent_state *domain_state;
+
+
+ /* don't add our domaina if we are a PDC or if we
+ are a member of a Samba domain */
+
+ if ( (IS_DC || lp_winbind_trusted_domains_only())
+ && strequal(domain->name, lp_workgroup()) )
+ {
+ continue;
+ }
+
+ /* Create a state record for this domain */
+
+ if ((domain_state = (struct getent_state *)
+ malloc(sizeof(struct getent_state))) == NULL)
+ return WINBINDD_ERROR;
+
+ ZERO_STRUCTP(domain_state);
+
+ fstrcpy(domain_state->domain_name, domain->name);
+
+ /* Add to list of open domains */
+
+ DLIST_ADD(state->getpwent_state, domain_state);
+ }
+
+ return WINBINDD_OK;
+}
+
+/* Close file pointer to ntdom passwd database */
+
+enum winbindd_result winbindd_endpwent(struct winbindd_cli_state *state)
+{
+ DEBUG(3, ("[%5lu]: endpwent\n", (unsigned long)state->pid));
+
+ free_getent_state(state->getpwent_state);
+ state->getpwent_state = NULL;
+
+ return WINBINDD_OK;
+}
+
+/* Get partial list of domain users for a domain. We fill in the sam_entries,
+ and num_sam_entries fields with domain user information. The dispinfo_ndx
+ field is incremented to the index of the next user to fetch. Return True if
+ some users were returned, False otherwise. */
+
+#define MAX_FETCH_SAM_ENTRIES 100
+
+static BOOL get_sam_user_entries(struct getent_state *ent)
+{
+ NTSTATUS status;
+ uint32 num_entries;
+ WINBIND_USERINFO *info;
+ struct getpwent_user *name_list = NULL;
+ BOOL result = False;
+ TALLOC_CTX *mem_ctx;
+ struct winbindd_domain *domain;
+ struct winbindd_methods *methods;
+ unsigned int i;
+
+ if (ent->num_sam_entries)
+ return False;
+
+ if (!(mem_ctx = talloc_init("get_sam_user_entries(%s)",
+ ent->domain_name)))
+ return False;
+
+ if (!(domain = find_domain_from_name(ent->domain_name))) {
+ DEBUG(3, ("no such domain %s in get_sam_user_entries\n",
+ ent->domain_name));
+ return False;
+ }
+
+ methods = domain->methods;
+
+ /* Free any existing user info */
+
+ SAFE_FREE(ent->sam_entries);
+ ent->num_sam_entries = 0;
+
+ /* Call query_user_list to get a list of usernames and user rids */
+
+ num_entries = 0;
+
+ status = methods->query_user_list(domain, mem_ctx, &num_entries,
+ &info);
+
+ if (num_entries) {
+ struct getpwent_user *tnl;
+
+ tnl = (struct getpwent_user *)Realloc(name_list,
+ sizeof(struct getpwent_user) *
+ (ent->num_sam_entries +
+ num_entries));
+
+ if (!tnl) {
+ DEBUG(0,("get_sam_user_entries realloc failed.\n"));
+ SAFE_FREE(name_list);
+ goto done;
+ } else
+ name_list = tnl;
+ }
+
+ for (i = 0; i < num_entries; i++) {
+ /* Store account name and gecos */
+ if (!info[i].acct_name) {
+ fstrcpy(name_list[ent->num_sam_entries + i].name, "");
+ } else {
+ fstrcpy(name_list[ent->num_sam_entries + i].name,
+ info[i].acct_name);
+ }
+ if (!info[i].full_name) {
+ fstrcpy(name_list[ent->num_sam_entries + i].gecos, "");
+ } else {
+ fstrcpy(name_list[ent->num_sam_entries + i].gecos,
+ info[i].full_name);
+ }
+
+ /* User and group ids */
+ sid_copy(&name_list[ent->num_sam_entries+i].user_sid, info[i].user_sid);
+ sid_copy(&name_list[ent->num_sam_entries+i].group_sid, info[i].group_sid);
+ }
+
+ ent->num_sam_entries += num_entries;
+
+ /* Fill in remaining fields */
+
+ ent->sam_entries = name_list;
+ ent->sam_entry_index = 0;
+ result = ent->num_sam_entries > 0;
+
+ done:
+
+ talloc_destroy(mem_ctx);
+
+ return result;
+}
+
+/* Fetch next passwd entry from ntdom database */
+
+#define MAX_GETPWENT_USERS 500
+
+enum winbindd_result winbindd_getpwent(struct winbindd_cli_state *state)
+{
+ struct getent_state *ent;
+ struct winbindd_pw *user_list;
+ int num_users, user_list_ndx = 0, i;
+
+ DEBUG(3, ("[%5lu]: getpwent\n", (unsigned long)state->pid));
+
+ /* Check user has enabled this */
+
+ if (!lp_winbind_enum_users())
+ return WINBINDD_ERROR;
+
+ /* Allocate space for returning a chunk of users */
+
+ num_users = MIN(MAX_GETPWENT_USERS, state->request.data.num_entries);
+
+ if ((state->response.extra_data =
+ malloc(num_users * sizeof(struct winbindd_pw))) == NULL)
+ return WINBINDD_ERROR;
+
+ memset(state->response.extra_data, 0, num_users *
+ sizeof(struct winbindd_pw));
+
+ user_list = (struct winbindd_pw *)state->response.extra_data;
+
+ if (!(ent = state->getpwent_state))
+ return WINBINDD_ERROR;
+
+ /* Start sending back users */
+
+ for (i = 0; i < num_users; i++) {
+ struct getpwent_user *name_list = NULL;
+ uint32 result;
+
+ /* Do we need to fetch another chunk of users? */
+
+ if (ent->num_sam_entries == ent->sam_entry_index) {
+
+ while(ent && !get_sam_user_entries(ent)) {
+ struct getent_state *next_ent;
+
+ /* Free state information for this domain */
+
+ SAFE_FREE(ent->sam_entries);
+
+ next_ent = ent->next;
+ DLIST_REMOVE(state->getpwent_state, ent);
+
+ SAFE_FREE(ent);
+ ent = next_ent;
+ }
+
+ /* No more domains */
+
+ if (!ent)
+ break;
+ }
+
+ name_list = ent->sam_entries;
+
+ /* Lookup user info */
+
+ result = winbindd_fill_pwent(
+ ent->domain_name,
+ name_list[ent->sam_entry_index].name,
+ &name_list[ent->sam_entry_index].user_sid,
+ &name_list[ent->sam_entry_index].group_sid,
+ name_list[ent->sam_entry_index].gecos,
+ &user_list[user_list_ndx]);
+
+ ent->sam_entry_index++;
+
+ /* Add user to return list */
+
+ if (result) {
+
+ user_list_ndx++;
+ state->response.data.num_entries++;
+ state->response.length +=
+ sizeof(struct winbindd_pw);
+
+ } else
+ DEBUG(1, ("could not lookup domain user %s\n",
+ name_list[ent->sam_entry_index].name));
+ }
+
+ /* Out of domains */
+
+ return (user_list_ndx > 0) ? WINBINDD_OK : WINBINDD_ERROR;
+}
+
+/* List domain users without mapping to unix ids */
+
+enum winbindd_result winbindd_list_users(struct winbindd_cli_state *state)
+{
+ struct winbindd_domain *domain;
+ WINBIND_USERINFO *info;
+ const char *which_domain;
+ uint32 num_entries = 0, total_entries = 0;
+ char *ted, *extra_data = NULL;
+ int extra_data_len = 0;
+ TALLOC_CTX *mem_ctx;
+ enum winbindd_result rv = WINBINDD_ERROR;
+
+ DEBUG(3, ("[%5lu]: list users\n", (unsigned long)state->pid));
+
+ if (!(mem_ctx = talloc_init("winbindd_list_users")))
+ return WINBINDD_ERROR;
+
+ /* Ensure null termination */
+ state->request.domain_name[sizeof(state->request.domain_name)-1]='\0';
+ which_domain = state->request.domain_name;
+
+ /* Enumerate over trusted domains */
+
+ for (domain = domain_list(); domain; domain = domain->next) {
+ NTSTATUS status;
+ struct winbindd_methods *methods;
+ unsigned int i;
+
+ /* if we have a domain name restricting the request and this
+ one in the list doesn't match, then just bypass the remainder
+ of the loop */
+
+ if ( *which_domain && !strequal(which_domain, domain->name) )
+ continue;
+
+ methods = domain->methods;
+
+ /* Query display info */
+ status = methods->query_user_list(domain, mem_ctx,
+ &num_entries, &info);
+
+ if (num_entries == 0)
+ continue;
+
+ /* Allocate some memory for extra data */
+ total_entries += num_entries;
+
+ ted = Realloc(extra_data, sizeof(fstring) * total_entries);
+
+ if (!ted) {
+ DEBUG(0,("failed to enlarge buffer!\n"));
+ SAFE_FREE(extra_data);
+ goto done;
+ } else
+ extra_data = ted;
+
+ /* Pack user list into extra data fields */
+
+ for (i = 0; i < num_entries; i++) {
+ fstring acct_name, name;
+
+ if (!info[i].acct_name) {
+ fstrcpy(acct_name, "");
+ } else {
+ fstrcpy(acct_name, info[i].acct_name);
+ }
+
+ fill_domain_username(name, domain->name, acct_name);
+
+ /* Append to extra data */
+ memcpy(&extra_data[extra_data_len], name,
+ strlen(name));
+ extra_data_len += strlen(name);
+ extra_data[extra_data_len++] = ',';
+ }
+ }
+
+ /* Assign extra_data fields in response structure */
+
+ if (extra_data) {
+ extra_data[extra_data_len - 1] = '\0';
+ state->response.extra_data = extra_data;
+ state->response.length += extra_data_len;
+ }
+
+ /* No domains responded but that's still OK so don't return an
+ error. */
+
+ rv = WINBINDD_OK;
+
+ done:
+
+ talloc_destroy(mem_ctx);
+
+ return rv;
+}
diff --git a/source/nsswitch/winbindd_util.c b/source/nsswitch/winbindd_util.c
new file mode 100644
index 00000000000..1aa4923e96f
--- /dev/null
+++ b/source/nsswitch/winbindd_util.c
@@ -0,0 +1,994 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ Winbind daemon for ntdom nss module
+
+ Copyright (C) Tim Potter 2000-2001
+ Copyright (C) 2001 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 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 "winbindd.h"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_WINBIND
+
+/**
+ * @file winbindd_util.c
+ *
+ * Winbind daemon for NT domain authentication nss module.
+ **/
+
+
+/**
+ * Used to clobber name fields that have an undefined value.
+ *
+ * Correct code should never look at a field that has this value.
+ **/
+
+static const fstring name_deadbeef = "<deadbeef>";
+
+/* The list of trusted domains. Note that the list can be deleted and
+ recreated using the init_domain_list() function so pointers to
+ individual winbindd_domain structures cannot be made. Keep a copy of
+ the domain name instead. */
+
+static struct winbindd_domain *_domain_list;
+
+/**
+ When was the last scan of trusted domains done?
+
+ 0 == not ever
+*/
+
+static time_t last_trustdom_scan;
+
+struct winbindd_domain *domain_list(void)
+{
+ /* Initialise list */
+
+ if (!_domain_list)
+ if (!init_domain_list())
+ return NULL;
+
+ return _domain_list;
+}
+
+/* Free all entries in the trusted domain list */
+
+void free_domain_list(void)
+{
+ struct winbindd_domain *domain = _domain_list;
+
+ while(domain) {
+ struct winbindd_domain *next = domain->next;
+
+ DLIST_REMOVE(_domain_list, domain);
+ SAFE_FREE(domain);
+ domain = next;
+ }
+}
+
+static BOOL is_internal_domain(const DOM_SID *sid)
+{
+ DOM_SID tmp_sid;
+
+ if (sid_equal(sid, get_global_sam_sid()))
+ return True;
+
+ string_to_sid(&tmp_sid, "S-1-5-32");
+ if (sid_equal(sid, &tmp_sid))
+ return True;
+
+ return False;
+}
+
+
+/* Add a trusted domain to our list of domains */
+static struct winbindd_domain *add_trusted_domain(const char *domain_name, const char *alt_name,
+ struct winbindd_methods *methods,
+ DOM_SID *sid)
+{
+ struct winbindd_domain *domain;
+ const char *alternative_name = NULL;
+ static const DOM_SID null_sid;
+
+ /* ignore alt_name if we are not in an AD domain */
+
+ if ( (lp_security() == SEC_ADS) && alt_name && *alt_name) {
+ alternative_name = alt_name;
+ }
+
+ /* We can't call domain_list() as this function is called from
+ init_domain_list() and we'll get stuck in a loop. */
+ for (domain = _domain_list; domain; domain = domain->next) {
+ if (strequal(domain_name, domain->name) ||
+ strequal(domain_name, domain->alt_name)) {
+ return domain;
+ }
+ if (alternative_name && *alternative_name) {
+ if (strequal(alternative_name, domain->name) ||
+ strequal(alternative_name, domain->alt_name)) {
+ return domain;
+ }
+ }
+ if (sid) {
+ if (sid_equal(sid, &null_sid) ) {
+
+ } else if (sid_equal(sid, &domain->sid)) {
+ return domain;
+ }
+ }
+ }
+
+ /* Create new domain entry */
+
+ if ((domain = (struct winbindd_domain *)malloc(sizeof(*domain))) == NULL)
+ return NULL;
+
+ /* Fill in fields */
+
+ ZERO_STRUCTP(domain);
+
+ /* prioritise the short name */
+ if (strchr_m(domain_name, '.') && alternative_name && *alternative_name) {
+ fstrcpy(domain->name, alternative_name);
+ fstrcpy(domain->alt_name, domain_name);
+ } else {
+ fstrcpy(domain->name, domain_name);
+ if (alternative_name) {
+ fstrcpy(domain->alt_name, alternative_name);
+ }
+ }
+
+ domain->methods = methods;
+ domain->backend = NULL;
+ domain->internal = is_internal_domain(sid);
+ domain->sequence_number = DOM_SEQUENCE_NONE;
+ domain->last_seq_check = 0;
+ if (sid) {
+ sid_copy(&domain->sid, sid);
+ }
+
+ /* set flags about native_mode, active_directory */
+
+ if (!domain->internal)
+ set_dc_type_and_flags( domain );
+
+ DEBUG(3,("add_trusted_domain: %s is an %s %s domain\n", domain->name,
+ domain->active_directory ? "ADS" : "NT4",
+ domain->native_mode ? "native mode" :
+ ((domain->active_directory && !domain->native_mode) ? "mixed mode" : "")));
+
+ /* Link to domain list */
+ DLIST_ADD(_domain_list, domain);
+
+ DEBUG(1,("Added domain %s %s %s\n",
+ domain->name, domain->alt_name,
+ &domain->sid?sid_string_static(&domain->sid):""));
+
+ return domain;
+}
+
+/********************************************************************
+ rescan our domains looking for new trusted domains
+********************************************************************/
+
+static void add_trusted_domains( struct winbindd_domain *domain )
+{
+ TALLOC_CTX *mem_ctx;
+ NTSTATUS result;
+ time_t t;
+ char **names;
+ char **alt_names;
+ int num_domains = 0;
+ DOM_SID *dom_sids, null_sid;
+ int i;
+ struct winbindd_domain *new_domain;
+
+ /* trusted domains might be disabled */
+ if (!lp_allow_trusted_domains()) {
+ return;
+ }
+
+ DEBUG(5, ("scanning trusted domain list\n"));
+
+ if (!(mem_ctx = talloc_init("init_domain_list")))
+ return;
+
+ ZERO_STRUCTP(&null_sid);
+
+ t = time(NULL);
+
+ /* ask the DC what domains it trusts */
+
+ result = domain->methods->trusted_domains(domain, mem_ctx, (unsigned int *)&num_domains,
+ &names, &alt_names, &dom_sids);
+
+ if ( NT_STATUS_IS_OK(result) ) {
+
+ /* Add each domain to the trusted domain list */
+
+ for(i = 0; i < num_domains; i++) {
+ DEBUG(10,("Found domain %s\n", names[i]));
+ add_trusted_domain(names[i], alt_names?alt_names[i]:NULL,
+ domain->methods, &dom_sids[i]);
+
+ /* if the SID was empty, we better set it now */
+
+ if ( sid_equal(&dom_sids[i], &null_sid) ) {
+
+ new_domain = find_domain_from_name(names[i]);
+
+ /* this should never happen */
+ if ( !new_domain ) {
+ DEBUG(0,("rescan_trust_domains: can't find the domain I just added! [%s]\n",
+ names[i]));
+ break;
+ }
+
+ /* call the cache method; which will operate on the winbindd_domain \
+ passed in and choose either rpc or ads as appropriate */
+
+ result = domain->methods->domain_sid( new_domain, &new_domain->sid );
+
+ if ( NT_STATUS_IS_OK(result) )
+ sid_copy( &dom_sids[i], &new_domain->sid );
+ }
+
+ /* store trusted domain in the cache */
+ trustdom_cache_store(names[i], alt_names ? alt_names[i] : NULL,
+ &dom_sids[i], t + WINBINDD_RESCAN_FREQ);
+ }
+ }
+
+ talloc_destroy(mem_ctx);
+}
+
+/********************************************************************
+ Periodically we need to refresh the trusted domain cache for smbd
+********************************************************************/
+
+void rescan_trusted_domains( void )
+{
+ time_t now = time(NULL);
+ struct winbindd_domain *mydomain = NULL;
+
+ /* see if the time has come... */
+
+ if ( (now > last_trustdom_scan) && ((now-last_trustdom_scan) < WINBINDD_RESCAN_FREQ) )
+ return;
+
+ if ( (mydomain = find_our_domain()) == NULL ) {
+ DEBUG(0,("rescan_trusted_domains: Can't find my own domain!\n"));
+ return;
+ }
+
+ /* this will only add new domains we didn't already know about */
+
+ add_trusted_domains( mydomain );
+
+ last_trustdom_scan = now;
+
+ return;
+}
+
+/* Look up global info for the winbind daemon */
+BOOL init_domain_list(void)
+{
+ extern struct winbindd_methods cache_methods;
+ struct winbindd_domain *domain;
+
+ /* Free existing list */
+ free_domain_list();
+
+ /* Add ourselves as the first entry. */
+
+ domain = add_trusted_domain( lp_workgroup(), lp_realm(), &cache_methods, NULL);
+
+ domain->primary = True;
+
+ /* get any alternate name for the primary domain */
+
+ cache_methods.alternate_name(domain);
+
+ /* now we have the correct netbios (short) domain name */
+
+ if ( *domain->name )
+ set_global_myworkgroup( domain->name );
+
+ if (!secrets_fetch_domain_sid(domain->name, &domain->sid)) {
+ DEBUG(1, ("Could not fetch sid for our domain %s\n",
+ domain->name));
+ return False;
+ }
+
+ /* do an initial scan for trusted domains */
+ add_trusted_domains(domain);
+
+ /* Add our local SAM domains */
+ {
+ DOM_SID sid;
+ extern struct winbindd_methods passdb_methods;
+ struct winbindd_domain *dom;
+
+ string_to_sid(&sid, "S-1-5-32");
+
+ dom = add_trusted_domain("BUILTIN", NULL, &passdb_methods,
+ &sid);
+ dom->internal = True;
+
+ dom = add_trusted_domain(get_global_sam_name(), NULL,
+ &passdb_methods,
+ get_global_sam_sid());
+ dom->internal = True;
+ }
+
+ /* avoid rescanning this right away */
+ last_trustdom_scan = time(NULL);
+ return True;
+}
+
+/**
+ * Given a domain name, return the struct winbindd domain info for it
+ *
+ * @note Do *not* pass lp_workgroup() to this function. domain_list
+ * may modify it's value, and free that pointer. Instead, our local
+ * domain may be found by calling find_our_domain().
+ * directly.
+ *
+ *
+ * @return The domain structure for the named domain, if it is working.
+ */
+
+struct winbindd_domain *find_domain_from_name(const char *domain_name)
+{
+ struct winbindd_domain *domain;
+
+ /* Search through list */
+
+ for (domain = domain_list(); domain != NULL; domain = domain->next) {
+ if (strequal(domain_name, domain->name) ||
+ (domain->alt_name[0] && strequal(domain_name, domain->alt_name))) {
+ return domain;
+ }
+ }
+
+ /* Not found */
+
+ return NULL;
+}
+
+/* Given a domain sid, return the struct winbindd domain info for it */
+
+struct winbindd_domain *find_domain_from_sid(DOM_SID *sid)
+{
+ struct winbindd_domain *domain;
+
+ /* Search through list */
+
+ for (domain = domain_list(); domain != NULL; domain = domain->next) {
+ if (sid_compare_domain(sid, &domain->sid) == 0)
+ return domain;
+ }
+
+ /* Not found */
+
+ return NULL;
+}
+
+/* Given a domain sid, return the struct winbindd domain info for it */
+
+struct winbindd_domain *find_our_domain(void)
+{
+ struct winbindd_domain *domain;
+
+ /* Search through list */
+
+ for (domain = domain_list(); domain != NULL; domain = domain->next) {
+ if (domain->primary)
+ return domain;
+ }
+
+ /* Not found */
+
+ return NULL;
+}
+
+/* Lookup a sid in a domain from a name */
+
+BOOL winbindd_lookup_sid_by_name(struct winbindd_domain *domain,
+ const char *name, DOM_SID *sid,
+ enum SID_NAME_USE *type)
+{
+ NTSTATUS result;
+ TALLOC_CTX *mem_ctx;
+
+ mem_ctx = talloc_init("lookup_sid_by_name for %s\n", name);
+ if (!mem_ctx)
+ return False;
+
+ /* Lookup name */
+ result = domain->methods->name_to_sid(domain, mem_ctx, name, sid, type);
+
+ talloc_destroy(mem_ctx);
+
+ /* Return rid and type if lookup successful */
+ if (!NT_STATUS_IS_OK(result)) {
+ *type = SID_NAME_UNKNOWN;
+ }
+
+ return NT_STATUS_IS_OK(result);
+}
+
+/**
+ * @brief Lookup a name in a domain from a sid.
+ *
+ * @param sid Security ID you want to look up.
+ * @param name On success, set to the name corresponding to @p sid.
+ * @param dom_name On success, set to the 'domain name' corresponding to @p sid.
+ * @param type On success, contains the type of name: alias, group or
+ * user.
+ * @retval True if the name exists, in which case @p name and @p type
+ * are set, otherwise False.
+ **/
+BOOL winbindd_lookup_name_by_sid(DOM_SID *sid,
+ fstring dom_name,
+ fstring name,
+ enum SID_NAME_USE *type)
+{
+ char *names;
+ NTSTATUS result;
+ TALLOC_CTX *mem_ctx;
+ BOOL rv = False;
+ struct winbindd_domain *domain;
+
+ domain = find_domain_from_sid(sid);
+
+ if (!domain) {
+ DEBUG(1,("Can't find domain from sid\n"));
+ return False;
+ }
+
+ /* Lookup name */
+
+ if (!(mem_ctx = talloc_init("winbindd_lookup_name_by_sid")))
+ return False;
+
+ result = domain->methods->sid_to_name(domain, mem_ctx, sid, &names, type);
+
+ /* Return name and type if successful */
+
+ if ((rv = NT_STATUS_IS_OK(result))) {
+ fstrcpy(dom_name, domain->name);
+ fstrcpy(name, names);
+ } else {
+ *type = SID_NAME_UNKNOWN;
+ fstrcpy(name, name_deadbeef);
+ }
+
+ talloc_destroy(mem_ctx);
+
+ return rv;
+}
+
+
+/* Free state information held for {set,get,end}{pw,gr}ent() functions */
+
+void free_getent_state(struct getent_state *state)
+{
+ struct getent_state *temp;
+
+ /* Iterate over state list */
+
+ temp = state;
+
+ while(temp != NULL) {
+ struct getent_state *next;
+
+ /* Free sam entries then list entry */
+
+ SAFE_FREE(state->sam_entries);
+ DLIST_REMOVE(state, state);
+ next = temp->next;
+
+ SAFE_FREE(temp);
+ temp = next;
+ }
+}
+
+/* Parse winbindd related parameters */
+
+BOOL winbindd_param_init(void)
+{
+ /* Parse winbind uid and winbind_gid parameters */
+
+ if (!lp_idmap_uid(&server_state.uid_low, &server_state.uid_high)) {
+ DEBUG(0, ("winbindd: idmap uid range missing or invalid\n"));
+ DEBUG(0, ("winbindd: cannot continue, exiting.\n"));
+ return False;
+ }
+
+ if (!lp_idmap_gid(&server_state.gid_low, &server_state.gid_high)) {
+ DEBUG(0, ("winbindd: idmap gid range missing or invalid\n"));
+ DEBUG(0, ("winbindd: cannot continue, exiting.\n"));
+ return False;
+ }
+
+ return True;
+}
+
+/* Check if a domain is present in a comma-separated list of domains */
+
+BOOL check_domain_env(char *domain_env, char *domain)
+{
+ fstring name;
+ const char *tmp = domain_env;
+
+ while(next_token(&tmp, name, ",", sizeof(fstring))) {
+ if (strequal(name, domain))
+ return True;
+ }
+
+ return False;
+}
+
+/* Is this a domain which we may assume no DOMAIN\ prefix? */
+
+static BOOL assume_domain(const char *domain) {
+ if ((lp_winbind_use_default_domain()
+ || lp_winbind_trusted_domains_only()) &&
+ strequal(lp_workgroup(), domain))
+ return True;
+
+ if (strequal(get_global_sam_name(), domain))
+ return True;
+
+ return False;
+}
+
+/* Parse a string of the form DOMAIN/user into a domain and a user */
+
+BOOL parse_domain_user(const char *domuser, fstring domain, fstring user)
+{
+ char *p = strchr(domuser,*lp_winbind_separator());
+
+ if ( !p ) {
+ fstrcpy(user, domuser);
+
+ if ( assume_domain(lp_workgroup())) {
+ fstrcpy(domain, lp_workgroup());
+ } else {
+ fstrcpy( domain, get_global_sam_name() );
+ }
+ }
+ else {
+ fstrcpy(user, p+1);
+ fstrcpy(domain, domuser);
+ domain[PTR_DIFF(p, domuser)] = 0;
+ }
+
+ strupper_m(domain);
+
+ return True;
+}
+
+/*
+ Fill DOMAIN\\USERNAME entry accounting 'winbind use default domain' and
+ 'winbind separator' options.
+ This means:
+ - omit DOMAIN when 'winbind use default domain = true' and DOMAIN is
+ lp_workgroup()
+
+ If we are a PDC or BDC, and this is for our domain, do likewise.
+
+ Also, if omit DOMAIN if 'winbind trusted domains only = true', as the
+ username is then unqualified in unix
+
+*/
+void fill_domain_username(fstring name, const char *domain, const char *user)
+{
+ if (assume_domain(domain)) {
+ strlcpy(name, user, sizeof(fstring));
+ } else {
+ slprintf(name, sizeof(fstring) - 1, "%s%s%s",
+ domain, lp_winbind_separator(),
+ user);
+ }
+}
+
+/*
+ * Winbindd socket accessor functions
+ */
+
+char *get_winbind_priv_pipe_dir(void)
+{
+ return lock_path(WINBINDD_PRIV_SOCKET_SUBDIR);
+}
+
+/* Open the winbindd socket */
+
+static int _winbindd_socket = -1;
+static int _winbindd_priv_socket = -1;
+
+int open_winbindd_socket(void)
+{
+ if (_winbindd_socket == -1) {
+ _winbindd_socket = create_pipe_sock(
+ WINBINDD_SOCKET_DIR, WINBINDD_SOCKET_NAME, 0755);
+ DEBUG(10, ("open_winbindd_socket: opened socket fd %d\n",
+ _winbindd_socket));
+ }
+
+ return _winbindd_socket;
+}
+
+int open_winbindd_priv_socket(void)
+{
+ if (_winbindd_priv_socket == -1) {
+ _winbindd_priv_socket = create_pipe_sock(
+ get_winbind_priv_pipe_dir(), WINBINDD_SOCKET_NAME, 0750);
+ DEBUG(10, ("open_winbindd_priv_socket: opened socket fd %d\n",
+ _winbindd_priv_socket));
+ }
+
+ return _winbindd_priv_socket;
+}
+
+/* Close the winbindd socket */
+
+void close_winbindd_socket(void)
+{
+ if (_winbindd_socket != -1) {
+ DEBUG(10, ("close_winbindd_socket: closing socket fd %d\n",
+ _winbindd_socket));
+ close(_winbindd_socket);
+ _winbindd_socket = -1;
+ }
+ if (_winbindd_priv_socket != -1) {
+ DEBUG(10, ("close_winbindd_socket: closing socket fd %d\n",
+ _winbindd_priv_socket));
+ close(_winbindd_priv_socket);
+ _winbindd_priv_socket = -1;
+ }
+}
+
+/*
+ * Client list accessor functions
+ */
+
+static struct winbindd_cli_state *_client_list;
+static int _num_clients;
+
+/* Return list of all connected clients */
+
+struct winbindd_cli_state *winbindd_client_list(void)
+{
+ return _client_list;
+}
+
+/* Add a connection to the list */
+
+void winbindd_add_client(struct winbindd_cli_state *cli)
+{
+ DLIST_ADD(_client_list, cli);
+ _num_clients++;
+}
+
+/* Remove a client from the list */
+
+void winbindd_remove_client(struct winbindd_cli_state *cli)
+{
+ DLIST_REMOVE(_client_list, cli);
+ _num_clients--;
+}
+
+/* Close all open clients */
+
+void winbindd_kill_all_clients(void)
+{
+ struct winbindd_cli_state *cl = winbindd_client_list();
+
+ DEBUG(10, ("winbindd_kill_all_clients: going postal\n"));
+
+ while (cl) {
+ struct winbindd_cli_state *next;
+
+ next = cl->next;
+ winbindd_remove_client(cl);
+ cl = next;
+ }
+}
+
+/* Return number of open clients */
+
+int winbindd_num_clients(void)
+{
+ return _num_clients;
+}
+
+/* Help with RID -> SID conversion */
+
+DOM_SID *rid_to_talloced_sid(struct winbindd_domain *domain,
+ TALLOC_CTX *mem_ctx,
+ uint32 rid)
+{
+ DOM_SID *sid;
+ sid = talloc(mem_ctx, sizeof(*sid));
+ if (!sid) {
+ smb_panic("rid_to_to_talloced_sid: talloc for DOM_SID failed!\n");
+ }
+ sid_copy(sid, &domain->sid);
+ sid_append_rid(sid, rid);
+ return sid;
+}
+
+/*****************************************************************************
+ For idmap conversion: convert one record to new format
+ Ancient versions (eg 2.2.3a) of winbindd_idmap.tdb mapped DOMAINNAME/rid
+ instead of the SID.
+*****************************************************************************/
+static int convert_fn(TDB_CONTEXT *tdb, TDB_DATA key, TDB_DATA data, void *state)
+{
+ struct winbindd_domain *domain;
+ char *p;
+ DOM_SID sid;
+ uint32 rid;
+ fstring keystr;
+ fstring dom_name;
+ TDB_DATA key2;
+ BOOL *failed = (BOOL *)state;
+
+ DEBUG(10,("Converting %s\n", key.dptr));
+
+ p = strchr(key.dptr, '/');
+ if (!p)
+ return 0;
+
+ *p = 0;
+ fstrcpy(dom_name, key.dptr);
+ *p++ = '/';
+
+ domain = find_domain_from_name(dom_name);
+ if (domain == NULL) {
+ /* We must delete the old record. */
+ DEBUG(0,("Unable to find domain %s\n", dom_name ));
+ DEBUG(0,("deleting record %s\n", key.dptr ));
+
+ if (tdb_delete(tdb, key) != 0) {
+ DEBUG(0, ("Unable to delete record %s\n", key.dptr));
+ *failed = True;
+ return -1;
+ }
+
+ return 0;
+ }
+
+ rid = atoi(p);
+
+ sid_copy(&sid, &domain->sid);
+ sid_append_rid(&sid, rid);
+
+ sid_to_string(keystr, &sid);
+ key2.dptr = keystr;
+ key2.dsize = strlen(keystr) + 1;
+
+ if (tdb_store(tdb, key2, data, TDB_INSERT) != 0) {
+ DEBUG(0,("Unable to add record %s\n", key2.dptr ));
+ *failed = True;
+ return -1;
+ }
+
+ if (tdb_store(tdb, data, key2, TDB_REPLACE) != 0) {
+ DEBUG(0,("Unable to update record %s\n", data.dptr ));
+ *failed = True;
+ return -1;
+ }
+
+ if (tdb_delete(tdb, key) != 0) {
+ DEBUG(0,("Unable to delete record %s\n", key.dptr ));
+ *failed = True;
+ return -1;
+ }
+
+ return 0;
+}
+
+/* These definitions are from sam/idmap_tdb.c. Replicated here just
+ out of laziness.... :-( */
+
+/* High water mark keys */
+#define HWM_GROUP "GROUP HWM"
+#define HWM_USER "USER HWM"
+
+/* idmap version determines auto-conversion */
+#define IDMAP_VERSION 2
+
+
+/*****************************************************************************
+ Convert the idmap database from an older version.
+*****************************************************************************/
+
+static BOOL idmap_convert(const char *idmap_name)
+{
+ int32 vers;
+ BOOL bigendianheader;
+ BOOL failed = False;
+ TDB_CONTEXT *idmap_tdb;
+
+ if (!(idmap_tdb = tdb_open_log(idmap_name, 0,
+ TDB_DEFAULT, O_RDWR,
+ 0600))) {
+ DEBUG(0, ("idmap_convert: Unable to open idmap database\n"));
+ return False;
+ }
+
+ bigendianheader = (idmap_tdb->flags & TDB_BIGENDIAN) ? True : False;
+
+ vers = tdb_fetch_int32(idmap_tdb, "IDMAP_VERSION");
+
+ if (((vers == -1) && bigendianheader) || (IREV(vers) == IDMAP_VERSION)) {
+ /* Arrggghh ! Bytereversed or old big-endian - make order independent ! */
+ /*
+ * high and low records were created on a
+ * big endian machine and will need byte-reversing.
+ */
+
+ int32 wm;
+
+ wm = tdb_fetch_int32(idmap_tdb, HWM_USER);
+
+ if (wm != -1) {
+ wm = IREV(wm);
+ } else {
+ wm = server_state.uid_low;
+ }
+
+ if (tdb_store_int32(idmap_tdb, HWM_USER, wm) == -1) {
+ DEBUG(0, ("idmap_convert: Unable to byteswap user hwm in idmap database\n"));
+ tdb_close(idmap_tdb);
+ return False;
+ }
+
+ wm = tdb_fetch_int32(idmap_tdb, HWM_GROUP);
+ if (wm != -1) {
+ wm = IREV(wm);
+ } else {
+ wm = server_state.gid_low;
+ }
+
+ if (tdb_store_int32(idmap_tdb, HWM_GROUP, wm) == -1) {
+ DEBUG(0, ("idmap_convert: Unable to byteswap group hwm in idmap database\n"));
+ tdb_close(idmap_tdb);
+ return False;
+ }
+ }
+
+ /* the old format stored as DOMAIN/rid - now we store the SID direct */
+ tdb_traverse(idmap_tdb, convert_fn, &failed);
+
+ if (failed) {
+ DEBUG(0, ("Problem during conversion\n"));
+ tdb_close(idmap_tdb);
+ return False;
+ }
+
+ if (tdb_store_int32(idmap_tdb, "IDMAP_VERSION", IDMAP_VERSION) == -1) {
+ DEBUG(0, ("idmap_convert: Unable to dtore idmap version in databse\n"));
+ tdb_close(idmap_tdb);
+ return False;
+ }
+
+ tdb_close(idmap_tdb);
+ return True;
+}
+
+/*****************************************************************************
+ Convert the idmap database from an older version if necessary
+*****************************************************************************/
+
+BOOL winbindd_upgrade_idmap(void)
+{
+ pstring idmap_name;
+ pstring backup_name;
+ SMB_STRUCT_STAT stbuf;
+ TDB_CONTEXT *idmap_tdb;
+
+ pstrcpy(idmap_name, lock_path("winbindd_idmap.tdb"));
+
+ if (!file_exist(idmap_name, &stbuf)) {
+ /* nothing to convert return */
+ return True;
+ }
+
+ if (!(idmap_tdb = tdb_open_log(idmap_name, 0,
+ TDB_DEFAULT, O_RDWR,
+ 0600))) {
+ DEBUG(0, ("idmap_convert: Unable to open idmap database\n"));
+ return False;
+ }
+
+ if (tdb_fetch_int32(idmap_tdb, "IDMAP_VERSION") == IDMAP_VERSION) {
+ /* nothing to convert return */
+ tdb_close(idmap_tdb);
+ return True;
+ }
+
+ /* backup_tdb expects the tdb not to be open */
+ tdb_close(idmap_tdb);
+
+ DEBUG(0, ("Upgrading winbindd_idmap.tdb from an old version\n"));
+
+ pstrcpy(backup_name, idmap_name);
+ pstrcat(backup_name, ".bak");
+
+ if (backup_tdb(idmap_name, backup_name) != 0) {
+ DEBUG(0, ("Could not backup idmap database\n"));
+ return False;
+ }
+
+ return idmap_convert(idmap_name);
+}
+
+/*******************************************************************
+ wrapper around retrieving the trust account password
+*******************************************************************/
+
+BOOL get_trust_pw(const char *domain, uint8 ret_pwd[16],
+ time_t *pass_last_set_time, uint32 *channel)
+{
+ DOM_SID sid;
+ char *pwd;
+
+ /* if we are a DC and this is not our domain, then lookup an account
+ for the domain trust */
+
+ if ( IS_DC && !strequal(domain, lp_workgroup()) && lp_allow_trusted_domains() )
+ {
+ if ( !secrets_fetch_trusted_domain_password(domain, &pwd, &sid,
+ pass_last_set_time) )
+ {
+ DEBUG(0, ("get_trust_pw: could not fetch trust account "
+ "password for trusted domain %s\n", domain));
+ return False;
+ }
+
+ *channel = SEC_CHAN_DOMAIN;
+ E_md4hash(pwd, ret_pwd);
+ SAFE_FREE(pwd);
+
+ return True;
+ }
+ else /* just get the account for our domain (covers
+ ROLE_DOMAIN_MEMBER as well */
+ {
+ /* get the machine trust account for our domain */
+
+ if ( !secrets_fetch_trust_account_password (lp_workgroup(), ret_pwd,
+ pass_last_set_time, channel) )
+ {
+ DEBUG(0, ("get_trust_pw: could not fetch trust account "
+ "password for my domain %s\n", domain));
+ return False;
+ }
+
+ return True;
+ }
+
+ /* Failure */
+}
+
diff --git a/source/nsswitch/winbindd_wins.c b/source/nsswitch/winbindd_wins.c
new file mode 100644
index 00000000000..a1eef159c0a
--- /dev/null
+++ b/source/nsswitch/winbindd_wins.c
@@ -0,0 +1,226 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ Winbind daemon - WINS related functions
+
+ Copyright (C) Andrew Tridgell 1999
+ Copyright (C) Herb Lewis 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 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 "winbindd.h"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_WINBIND
+
+/* Use our own create socket code so we don't recurse.... */
+
+static int wins_lookup_open_socket_in(void)
+{
+ struct sockaddr_in sock;
+ int val=1;
+ int res;
+
+ memset((char *)&sock,'\0',sizeof(sock));
+
+#ifdef HAVE_SOCK_SIN_LEN
+ sock.sin_len = sizeof(sock);
+#endif
+ sock.sin_port = 0;
+ sock.sin_family = AF_INET;
+ sock.sin_addr.s_addr = interpret_addr("0.0.0.0");
+ res = socket(AF_INET, SOCK_DGRAM, 0);
+ if (res == -1)
+ return -1;
+
+ setsockopt(res,SOL_SOCKET,SO_REUSEADDR,(char *)&val,sizeof(val));
+#ifdef SO_REUSEPORT
+ setsockopt(res,SOL_SOCKET,SO_REUSEPORT,(char *)&val,sizeof(val));
+#endif /* SO_REUSEPORT */
+
+ /* now we've got a socket - we need to bind it */
+
+ if (bind(res, (struct sockaddr * ) &sock,sizeof(sock)) < 0) {
+ close(res);
+ return(-1);
+ }
+
+ set_socket_options(res,"SO_BROADCAST");
+
+ return res;
+}
+
+
+static struct node_status *lookup_byaddr_backend(char *addr, int *count)
+{
+ int fd;
+ struct in_addr ip;
+ struct nmb_name nname;
+ struct node_status *status;
+
+ fd = wins_lookup_open_socket_in();
+ if (fd == -1)
+ return NULL;
+
+ make_nmb_name(&nname, "*", 0);
+ ip = *interpret_addr2(addr);
+ status = node_status_query(fd,&nname,ip, count);
+
+ close(fd);
+ return status;
+}
+
+static struct in_addr *lookup_byname_backend(const char *name, int *count)
+{
+ int fd;
+ struct ip_service *ret = NULL;
+ struct in_addr *return_ip;
+ int j, i, flags = 0;
+
+ *count = 0;
+
+ /* always try with wins first */
+ if (resolve_wins(name,0x20,&ret,count)) {
+ if ( count == 0 )
+ return NULL;
+ if ( (return_ip = (struct in_addr *)malloc((*count)*sizeof(struct in_addr))) == NULL ) {
+ free( ret );
+ return NULL;
+ }
+
+ /* copy the IP addresses */
+ for ( i=0; i<(*count); i++ )
+ return_ip[i] = ret[i].ip;
+
+ return return_ip;
+ }
+
+ fd = wins_lookup_open_socket_in();
+ if (fd == -1) {
+ return NULL;
+ }
+
+ /* uggh, we have to broadcast to each interface in turn */
+ for (j=iface_count() - 1;
+ j >= 0;
+ j--) {
+ struct in_addr *bcast = iface_n_bcast(j);
+ return_ip = name_query(fd,name,0x20,True,True,*bcast,count, &flags, NULL);
+ if (return_ip) break;
+ }
+
+ close(fd);
+ return return_ip;
+}
+
+/* Get hostname from IP */
+
+enum winbindd_result winbindd_wins_byip(struct winbindd_cli_state *state)
+{
+ fstring response;
+ int i, count, maxlen, size;
+ struct node_status *status;
+
+ /* Ensure null termination */
+ state->request.data.winsreq[sizeof(state->request.data.winsreq)-1]='\0';
+
+ DEBUG(3, ("[%5lu]: wins_byip %s\n", (unsigned long)state->pid,
+ state->request.data.winsreq));
+
+ *response = '\0';
+ maxlen = sizeof(response) - 1;
+
+ if ((status = lookup_byaddr_backend(state->request.data.winsreq, &count))){
+ size = strlen(state->request.data.winsreq);
+ if (size > maxlen) {
+ SAFE_FREE(status);
+ return WINBINDD_ERROR;
+ }
+ fstrcat(response,state->request.data.winsreq);
+ fstrcat(response,"\t");
+ for (i = 0; i < count; i++) {
+ /* ignore group names */
+ if (status[i].flags & 0x80) continue;
+ if (status[i].type == 0x20) {
+ size = sizeof(status[i].name) + strlen(response);
+ if (size > maxlen) {
+ SAFE_FREE(status);
+ return WINBINDD_ERROR;
+ }
+ fstrcat(response, status[i].name);
+ fstrcat(response, " ");
+ }
+ }
+ /* make last character a newline */
+ response[strlen(response)-1] = '\n';
+ SAFE_FREE(status);
+ }
+ fstrcpy(state->response.data.winsresp,response);
+ return WINBINDD_OK;
+}
+
+/* Get IP from hostname */
+
+enum winbindd_result winbindd_wins_byname(struct winbindd_cli_state *state)
+{
+ struct in_addr *ip_list;
+ int i, count, maxlen, size;
+ fstring response;
+ char * addr;
+
+ /* Ensure null termination */
+ state->request.data.winsreq[sizeof(state->request.data.winsreq)-1]='\0';
+
+ DEBUG(3, ("[%5lu]: wins_byname %s\n", (unsigned long)state->pid,
+ state->request.data.winsreq));
+
+ *response = '\0';
+ maxlen = sizeof(response) - 1;
+
+ if ((ip_list = lookup_byname_backend(state->request.data.winsreq,&count))){
+ for (i = count; i ; i--) {
+ addr = inet_ntoa(ip_list[i-1]);
+ size = strlen(addr);
+ if (size > maxlen) {
+ SAFE_FREE(ip_list);
+ return WINBINDD_ERROR;
+ }
+ if (i != 0) {
+ /* Clear out the newline character */
+ /* But only if there is something in there,
+ otherwise we clobber something in the stack */
+ if (strlen(response))
+ response[strlen(response)-1] = ' ';
+ }
+ fstrcat(response,addr);
+ fstrcat(response,"\t");
+ }
+ size = strlen(state->request.data.winsreq) + strlen(response);
+ if (size > maxlen) {
+ SAFE_FREE(ip_list);
+ return WINBINDD_ERROR;
+ }
+ fstrcat(response,state->request.data.winsreq);
+ fstrcat(response,"\n");
+ SAFE_FREE(ip_list);
+ } else
+ return WINBINDD_ERROR;
+
+ fstrcpy(state->response.data.winsresp,response);
+
+ return WINBINDD_OK;
+}
diff --git a/source/nsswitch/wins.c b/source/nsswitch/wins.c
new file mode 100644
index 00000000000..100a103924c
--- /dev/null
+++ b/source/nsswitch/wins.c
@@ -0,0 +1,379 @@
+/*
+ Unix SMB/CIFS implementation.
+ a WINS nsswitch module
+ 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 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.
+
+*/
+
+#define NO_SYSLOG
+
+#include "includes.h"
+#ifdef HAVE_NS_API_H
+#undef VOLATILE
+
+#include <ns_daemon.h>
+#endif
+
+#ifndef INADDRSZ
+#define INADDRSZ 4
+#endif
+
+static int initialised;
+
+extern BOOL AllowDebugChange;
+
+/* Use our own create socket code so we don't recurse.... */
+
+static int wins_lookup_open_socket_in(void)
+{
+ struct sockaddr_in sock;
+ int val=1;
+ int res;
+
+ memset((char *)&sock,'\0',sizeof(sock));
+
+#ifdef HAVE_SOCK_SIN_LEN
+ sock.sin_len = sizeof(sock);
+#endif
+ sock.sin_port = 0;
+ sock.sin_family = AF_INET;
+ sock.sin_addr.s_addr = interpret_addr("0.0.0.0");
+ res = socket(AF_INET, SOCK_DGRAM, 0);
+ if (res == -1)
+ return -1;
+
+ setsockopt(res,SOL_SOCKET,SO_REUSEADDR,(char *)&val,sizeof(val));
+#ifdef SO_REUSEPORT
+ setsockopt(res,SOL_SOCKET,SO_REUSEPORT,(char *)&val,sizeof(val));
+#endif /* SO_REUSEPORT */
+
+ /* now we've got a socket - we need to bind it */
+
+ if (bind(res, (struct sockaddr * ) &sock,sizeof(sock)) < 0) {
+ close(res);
+ return(-1);
+ }
+
+ set_socket_options(res,"SO_BROADCAST");
+
+ return res;
+}
+
+
+static void nss_wins_init(void)
+{
+ initialised = 1;
+ DEBUGLEVEL = 0;
+ AllowDebugChange = False;
+
+ TimeInit();
+ setup_logging("nss_wins",False);
+ lp_load(dyn_CONFIGFILE,True,False,False);
+ load_interfaces();
+}
+
+static struct in_addr *lookup_byname_backend(const char *name, int *count)
+{
+ int fd = -1;
+ struct ip_service *address = NULL;
+ struct in_addr *ret;
+ int j, flags = 0;
+
+ if (!initialised) {
+ nss_wins_init();
+ }
+
+ *count = 0;
+
+ /* always try with wins first */
+ if (resolve_wins(name,0x20,&address,count)) {
+ if ( (ret = (struct in_addr *)malloc(sizeof(struct in_addr))) == NULL ) {
+ free( address );
+ return NULL;
+ }
+ *ret = address[0].ip;
+ free( address );
+ return ret;
+ }
+
+ fd = wins_lookup_open_socket_in();
+ if (fd == -1) {
+ return NULL;
+ }
+
+ /* uggh, we have to broadcast to each interface in turn */
+ for (j=iface_count() - 1;j >= 0;j--) {
+ struct in_addr *bcast = iface_n_bcast(j);
+ ret = name_query(fd,name,0x20,True,True,*bcast,count, &flags, NULL);
+ if (ret) break;
+ }
+
+ close(fd);
+ return ret;
+}
+
+#ifdef HAVE_NS_API_H
+
+static struct node_status *lookup_byaddr_backend(char *addr, int *count)
+{
+ int fd;
+ struct in_addr ip;
+ struct nmb_name nname;
+ struct node_status *status;
+
+ if (!initialised) {
+ nss_wins_init();
+ }
+
+ fd = wins_lookup_open_socket_in();
+ if (fd == -1)
+ return NULL;
+
+ make_nmb_name(&nname, "*", 0);
+ ip = *interpret_addr2(addr);
+ status = node_status_query(fd,&nname,ip, count);
+
+ close(fd);
+ return status;
+}
+
+/* IRIX version */
+
+int init(void)
+{
+ nsd_logprintf(NSD_LOG_MIN, "entering init (wins)\n");
+ nss_wins_init();
+ return NSD_OK;
+}
+
+int lookup(nsd_file_t *rq)
+{
+ char *map;
+ char *key;
+ char *addr;
+ struct in_addr *ip_list;
+ struct node_status *status;
+ int i, count, len, size;
+ char response[1024];
+ BOOL found = False;
+
+ nsd_logprintf(NSD_LOG_MIN, "entering lookup (wins)\n");
+ if (! rq)
+ return NSD_ERROR;
+
+ map = nsd_attr_fetch_string(rq->f_attrs, "table", (char*)0);
+ if (! map) {
+ rq->f_status = NS_FATAL;
+ return NSD_ERROR;
+ }
+
+ key = nsd_attr_fetch_string(rq->f_attrs, "key", (char*)0);
+ if (! key || ! *key) {
+ rq->f_status = NS_FATAL;
+ return NSD_ERROR;
+ }
+
+ response[0] = '\0';
+ len = sizeof(response) - 2;
+
+ /*
+ * response needs to be a string of the following format
+ * ip_address[ ip_address]*\tname[ alias]*
+ */
+ if (StrCaseCmp(map,"hosts.byaddr") == 0) {
+ if ( status = lookup_byaddr_backend(key, &count)) {
+ size = strlen(key) + 1;
+ if (size > len) {
+ free(status);
+ return NSD_ERROR;
+ }
+ len -= size;
+ strncat(response,key,size);
+ strncat(response,"\t",1);
+ for (i = 0; i < count; i++) {
+ /* ignore group names */
+ if (status[i].flags & 0x80) continue;
+ if (status[i].type == 0x20) {
+ size = sizeof(status[i].name) + 1;
+ if (size > len) {
+ free(status);
+ return NSD_ERROR;
+ }
+ len -= size;
+ strncat(response, status[i].name, size);
+ strncat(response, " ", 1);
+ found = True;
+ }
+ }
+ response[strlen(response)-1] = '\n';
+ free(status);
+ }
+ } else if (StrCaseCmp(map,"hosts.byname") == 0) {
+ if (ip_list = lookup_byname_backend(key, &count)) {
+ for (i = count; i ; i--) {
+ addr = inet_ntoa(ip_list[i-1]);
+ size = strlen(addr) + 1;
+ if (size > len) {
+ free(ip_list);
+ return NSD_ERROR;
+ }
+ len -= size;
+ if (i != 0)
+ response[strlen(response)-1] = ' ';
+ strncat(response,addr,size);
+ strncat(response,"\t",1);
+ }
+ size = strlen(key) + 1;
+ if (size > len) {
+ free(ip_list);
+ return NSD_ERROR;
+ }
+ strncat(response,key,size);
+ strncat(response,"\n",1);
+ found = True;
+ free(ip_list);
+ }
+ }
+
+ if (found) {
+ nsd_logprintf(NSD_LOG_LOW, "lookup (wins %s) %s\n",map,response);
+ nsd_set_result(rq,NS_SUCCESS,response,strlen(response),VOLATILE);
+ return NSD_OK;
+ }
+ nsd_logprintf(NSD_LOG_LOW, "lookup (wins) not found\n");
+ rq->f_status = NS_NOTFOUND;
+ return NSD_NEXT;
+}
+
+#else
+
+/* Allocate some space from the nss static buffer. The buffer and buflen
+ are the pointers passed in by the C library to the _nss_*_*
+ functions. */
+
+static char *get_static(char **buffer, size_t *buflen, int len)
+{
+ char *result;
+
+ /* Error check. We return false if things aren't set up right, or
+ there isn't enough buffer space left. */
+
+ if ((buffer == NULL) || (buflen == NULL) || (*buflen < len)) {
+ return NULL;
+ }
+
+ /* Return an index into the static buffer */
+
+ result = *buffer;
+ *buffer += len;
+ *buflen -= len;
+
+ return result;
+}
+
+/****************************************************************************
+gethostbyname() - we ignore any domain portion of the name and only
+handle names that are at most 15 characters long
+ **************************************************************************/
+NSS_STATUS
+_nss_wins_gethostbyname_r(const char *hostname, struct hostent *he,
+ char *buffer, size_t buflen, int *h_errnop)
+{
+ struct in_addr *ip_list;
+ int i, count;
+ fstring name;
+ size_t namelen;
+
+ memset(he, '\0', sizeof(*he));
+ fstrcpy(name, hostname);
+
+ /* Do lookup */
+
+ ip_list = lookup_byname_backend(name, &count);
+
+ if (!ip_list)
+ return NSS_STATUS_NOTFOUND;
+
+ /* Copy h_name */
+
+ namelen = strlen(name) + 1;
+
+ if ((he->h_name = get_static(&buffer, &buflen, namelen)) == NULL)
+ return NSS_STATUS_TRYAGAIN;
+
+ memcpy(he->h_name, name, namelen);
+
+ /* Copy h_addr_list, align to pointer boundary first */
+
+ if ((i = (unsigned long)(buffer) % sizeof(char*)) != 0)
+ i = sizeof(char*) - i;
+
+ if (get_static(&buffer, &buflen, i) == NULL)
+ return NSS_STATUS_TRYAGAIN;
+
+ if ((he->h_addr_list = (char **)get_static(
+ &buffer, &buflen, (count + 1) * sizeof(char *))) == NULL)
+ return NSS_STATUS_TRYAGAIN;
+
+ for (i = 0; i < count; i++) {
+ if ((he->h_addr_list[i] = get_static(&buffer, &buflen,
+ INADDRSZ)) == NULL)
+ return NSS_STATUS_TRYAGAIN;
+ memcpy(he->h_addr_list[i], &ip_list[i], INADDRSZ);
+ }
+
+ he->h_addr_list[count] = NULL;
+
+ if (ip_list)
+ free(ip_list);
+
+ /* Set h_addr_type and h_length */
+
+ he->h_addrtype = AF_INET;
+ he->h_length = INADDRSZ;
+
+ /* Set h_aliases */
+
+ if ((i = (unsigned long)(buffer) % sizeof(char*)) != 0)
+ i = sizeof(char*) - i;
+
+ if (get_static(&buffer, &buflen, i) == NULL)
+ return NSS_STATUS_TRYAGAIN;
+
+ if ((he->h_aliases = (char **)get_static(
+ &buffer, &buflen, sizeof(char *))) == NULL)
+ return NSS_STATUS_TRYAGAIN;
+
+ he->h_aliases[0] = NULL;
+
+ return NSS_STATUS_SUCCESS;
+}
+
+
+NSS_STATUS
+_nss_wins_gethostbyname2_r(const char *name, int af, struct hostent *he,
+ char *buffer, size_t buflen, int *h_errnop)
+{
+ if(af!=AF_INET) {
+ *h_errnop = NO_DATA;
+ return NSS_STATUS_UNAVAIL;
+ }
+
+ return _nss_wins_gethostbyname_r(
+ name, he, buffer, buflen, h_errnop);
+}
+#endif
diff --git a/source/pam_smbpass/.cvsignore b/source/pam_smbpass/.cvsignore
new file mode 100644
index 00000000000..6d609cec52b
--- /dev/null
+++ b/source/pam_smbpass/.cvsignore
@@ -0,0 +1 @@
+*.po
diff --git a/source/pam_smbpass/CHANGELOG b/source/pam_smbpass/CHANGELOG
new file mode 100644
index 00000000000..96ef7840084
--- /dev/null
+++ b/source/pam_smbpass/CHANGELOG
@@ -0,0 +1,31 @@
+version 0.7.5 25 Mar 2001
+ - Use Samba 2.2.0 (alpha) as the target codebase, since it doesn't look
+ like Samba will be offering shared libraries in the near future.
+ - added a Makefile and support scripts to make the build process easier.
+ - imported some Solaris fixes that I've been sitting on.
+
+version 0.7.4 20 Jan 2000
+ - added a 'migrate' option to the authentication code which makes no
+ effort to authenticate the user, or even to ask for a password, but
+ it can be useful for filling in an SMB password db.
+
+version 0.7.3 19 Jan 2000
+ - updated to use the SAMBA_TNG Samba branch, allowing us to dynamically
+ link against Luke's new shared libs (libsamba, libsmb).
+
+version 0.7.2 20 Jul 1999
+ - miscellaneous bugfixes. Cleanup of legacy pam_pwdb code.
+ - fixed return value of pam_sm_setcred function.
+ - fix to autoconf support
+ - clarified some of the messages being logged
+
+version 0.6, 15 Jul 1999
+ - updated to use the new Samba (2.0) password database API.
+ - added autoconf support. May now theoretically compile on more
+ platforms than PAM itself does.
+ - added support for account management functions (i.e., disabled
+ accounts)
+
+version 0.5, 4 Apr 1998
+ - added support for hashed passwords as input. Now capable of serving
+ as an authentication agent for encrypted network transactions.
diff --git a/source/pam_smbpass/INSTALL b/source/pam_smbpass/INSTALL
new file mode 100644
index 00000000000..ae2ba02bbbc
--- /dev/null
+++ b/source/pam_smbpass/INSTALL
@@ -0,0 +1,64 @@
+
+Because pam_smbpass is derived from the Samba smbpasswd utility, recent
+versions of pam_smbpass require a copy of the Samba source code to be
+available on the build system. Version 0.7.5 has been tested against
+Samba 2.2.0-alpha3, and this is the recommended version of Samba to use
+for building pam_smbpass. This only affects /building/ pam_smbpass; you
+can still run any version of the Samba server that you want, although
+clearly it saves some disk space to have only one copy of the source
+code on your system (Samba 2.2.0-alpha3 takes roughly 32MB of disk space
+to build pam_smbpass).
+
+Version 0.7.5 features a new build system to make it easier to build
+pam_smbpass.
+
+
+Using the new build system
+==========================
+
+If you don't have a copy of the Samba source code on your machine, and you
+don't have a preferred Samba version (or mirror site), you can build
+pam_smbpass by just typing 'make'.
+
+If you want to use a version other than 2.2.0-alpha3, or you want to
+download the source code from a faster Samba mirror (see
+<http://us1.samba.org/samba/> for a list of mirror sites), please download
+the source code and unpack it before running make. The build scripts will
+attempt to autodetect your Samba source directory, and if it can't be
+found automatically, you will be given the opportunity to specify an
+alternate directory for the Samba sources.
+
+Feedback is welcome if you try (or succeed!) to build pam_smbpass with
+other versions of Samba.
+
+
+Options to 'make'
+=================
+
+By default, pam_smbpass will configure the Samba build tree with the
+options
+
+ --with-fhs --with-privatedir=/etc --with-configdir=/etc
+
+This will configure pam_smbpass to look for the smbpasswd file as
+/etc/smbpasswd (or /etc/smbpasswd.tdb), and the smb.conf file as
+/etc/smb.conf. You can override these options by setting CONFIGOPTS when
+calling make. E.g., if you have your smb.conf file in /usr/etc and your
+smbpasswd file in /usr/etc/private, you might run
+
+ make CONFIGOPTS="--with-privatedir=/usr/etc/private --with-configdir=/usr/etc"
+
+For a complete list of available configuration options, see
+'./samba/configure --help'
+
+
+Installing the module
+=====================
+
+If all goes well in the build process, the file pam_smbpass.so will be
+created in the current directory. Simply install the module into your
+system's PAM module directory:
+
+ install -m 755 -s bin/pam_smbpass.so /lib/security
+
+and you're all set.
diff --git a/source/pam_smbpass/README b/source/pam_smbpass/README
new file mode 100644
index 00000000000..6cdb76f9c3f
--- /dev/null
+++ b/source/pam_smbpass/README
@@ -0,0 +1,60 @@
+25 Mar 2001
+
+pam_smbpass is a PAM module which can be used on conforming systems to
+keep the smbpasswd (Samba password) database in sync with the unix
+password file. PAM (Pluggable Authentication Modules) is an API supported
+under some Unices, such as Solaris, HPUX and Linux, that provides a
+generic interface to authentication mechanisms.
+
+For more information on PAM, see http://ftp.kernel.org/pub/linux/libs/pam/
+
+This module authenticates a local smbpasswd user database. If you require
+support for authenticating against a remote SMB server, or if you're
+concerned about the presence of suid root binaries on your system, it is
+recommended that you use pam_winbind instead.
+
+Options recognized by this module are as follows:
+
+ debug - log more debugging info
+ audit - like debug, but also logs unknown usernames
+ use_first_pass - don't prompt the user for passwords;
+ take them from PAM_ items instead
+ try_first_pass - try to get the password from a previous
+ PAM module, fall back to prompting the user
+ use_authtok - like try_first_pass, but *fail* if the new
+ PAM_AUTHTOK has not been previously set.
+ (intended for stacking password modules only)
+ not_set_pass - don't make passwords used by this module
+ available to other modules.
+ nodelay - don't insert ~1 second delays on authentication
+ failure.
+ nullok - null passwords are allowed.
+ nonull - null passwords are not allowed. Used to
+ override the Samba configuration.
+ migrate - only meaningful in an "auth" context;
+ used to update smbpasswd file with a
+ password used for successful authentication.
+ smbconf=<file> - specify an alternate path to the smb.conf
+ file.
+
+See the samples/ directory for example PAM configurations using this
+module.
+
+Thanks go to the following people:
+
+* Andrew Morgan <morgan@transmeta.com>, for providing the Linux-PAM
+framework, without which none of this would have happened
+
+* Christian Gafton <gafton@redhat.com> and Andrew Morgan again, for the
+pam_pwdb module upon which pam_smbpass was originally based
+
+* Luke Leighton <lkcl@switchboard.net> for being receptive to the idea,
+and for the occasional good-natured complaint about the project's status
+that keep me working on it :)
+
+* and of course, all the other members of the Samba team
+<http://www.samba.org/samba/team.html>, for creating a great product
+and for giving this project a purpose
+
+---------------------
+Stephen Langasek <vorlon@netexpress.net>
diff --git a/source/pam_smbpass/TODO b/source/pam_smbpass/TODO
new file mode 100644
index 00000000000..20cf4fb0987
--- /dev/null
+++ b/source/pam_smbpass/TODO
@@ -0,0 +1,7 @@
+This is a tentative TODO file which will probably get much longer before
+it gets much shorter.
+
+- Recognizing (and overriding) debug options in the smb.conf file
+- Support for 'name=value' parameters in the PAM config
+- Compliant handling of unrecognized PAM parameters (i.e., fail on error)
+-
diff --git a/source/pam_smbpass/general.h b/source/pam_smbpass/general.h
new file mode 100644
index 00000000000..4f13d601313
--- /dev/null
+++ b/source/pam_smbpass/general.h
@@ -0,0 +1,130 @@
+#ifndef LINUX
+/* This is only needed by modules in the Sun implementation. */
+#include <security/pam_appl.h>
+#endif /* LINUX */
+
+#include <security/pam_modules.h>
+
+#ifndef PAM_AUTHTOK_RECOVER_ERR
+#define PAM_AUTHTOK_RECOVER_ERR PAM_AUTHTOK_RECOVERY_ERR
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <syslog.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/wait.h>
+
+/*
+ * here is the string to inform the user that the new passwords they
+ * typed were not the same.
+ */
+
+#define MISTYPED_PASS "Sorry, passwords do not match"
+
+/* type definition for the control options */
+
+typedef struct {
+ const char *token;
+ unsigned int mask; /* shall assume 32 bits of flags */
+ unsigned int flag;
+} SMB_Ctrls;
+
+#ifndef False
+#define False (0)
+#endif
+
+#ifndef True
+#define True (1)
+#endif
+
+/* macro to determine if a given flag is on */
+#define on(x,ctrl) (smb_args[x].flag & ctrl)
+
+/* macro to determine that a given flag is NOT on */
+#define off(x,ctrl) (!on(x,ctrl))
+
+/* macro to turn on/off a ctrl flag manually */
+#define set(x,ctrl) (ctrl = ((ctrl)&smb_args[x].mask)|smb_args[x].flag)
+#define unset(x,ctrl) (ctrl &= ~(smb_args[x].flag))
+
+#ifndef __linux__
+#define strncasecmp(s1,s2,n) StrnCaseCmp(s1,s2,n)
+#endif
+
+/* the generic mask */
+#define _ALL_ON_ (~0U)
+
+/* end of macro definitions definitions for the control flags */
+
+/*
+ * These are the options supported by the smb password module, very
+ * similar to the pwdb options
+ */
+
+#define SMB__OLD_PASSWD 0 /* internal */
+#define SMB__VERIFY_PASSWD 1 /* internal */
+
+#define SMB_AUDIT 2 /* print more things than debug..
+ some information may be sensitive */
+#define SMB_USE_FIRST_PASS 3
+#define SMB_TRY_FIRST_PASS 4
+#define SMB_NOT_SET_PASS 5 /* don't set the AUTHTOK items */
+
+#define SMB__NONULL 6 /* internal */
+#define SMB__QUIET 7 /* internal */
+#define SMB_USE_AUTHTOK 8 /* insist on reading PAM_AUTHTOK */
+#define SMB__NULLOK 9 /* Null token ok */
+#define SMB_DEBUG 10 /* send more info to syslog(3) */
+#define SMB_NODELAY 11 /* admin does not want a fail-delay */
+#define SMB_MIGRATE 12 /* Does no authentication, just
+ updates the smb database. */
+#define SMB_CONF_FILE 13 /* Alternate location of smb.conf */
+
+#define SMB_CTRLS_ 14 /* number of ctrl arguments defined */
+
+static const SMB_Ctrls smb_args[SMB_CTRLS_] = {
+/* symbol token name ctrl mask ctrl *
+ * ------------------ ------------------ -------------- ---------- */
+
+/* SMB__OLD_PASSWD */ { NULL, _ALL_ON_, 01 },
+/* SMB__VERIFY_PASSWD */ { NULL, _ALL_ON_, 02 },
+/* SMB_AUDIT */ { "audit", _ALL_ON_, 04 },
+/* SMB_USE_FIRST_PASS */ { "use_first_pass", _ALL_ON_^(030), 010 },
+/* SMB_TRY_FIRST_PASS */ { "try_first_pass", _ALL_ON_^(030), 020 },
+/* SMB_NOT_SET_PASS */ { "not_set_pass", _ALL_ON_, 040 },
+/* SMB__NONULL */ { "nonull", _ALL_ON_, 0100 },
+/* SMB__QUIET */ { NULL, _ALL_ON_, 0200 },
+/* SMB_USE_AUTHTOK */ { "use_authtok", _ALL_ON_, 0400 },
+/* SMB__NULLOK */ { "nullok", _ALL_ON_^(0100), 0 },
+/* SMB_DEBUG */ { "debug", _ALL_ON_, 01000 },
+/* SMB_NODELAY */ { "nodelay", _ALL_ON_, 02000 },
+/* SMB_MIGRATE */ { "migrate", _ALL_ON_^(0100), 04000 },
+/* SMB_CONF_FILE */ { "smbconf=", _ALL_ON_, 0 },
+};
+
+#define SMB_DEFAULTS (smb_args[SMB__NONULL].flag)
+
+/*
+ * the following is used to keep track of the number of times a user fails
+ * to authenticate themself.
+ */
+
+#define FAIL_PREFIX "-SMB-FAIL-"
+#define SMB_MAX_RETRIES 3
+
+struct _pam_failed_auth {
+ char *user; /* user that's failed to be authenticated */
+ int id; /* uid of requested user */
+ char *agent; /* attempt from user with name */
+ int count; /* number of failures so far */
+};
+
+/*
+ * General use functions go here
+ */
+
+/* from support.c */
+int make_remark(pam_handle_t *, unsigned int, int, const char *);
diff --git a/source/pam_smbpass/pam_smb_acct.c b/source/pam_smbpass/pam_smb_acct.c
new file mode 100644
index 00000000000..2ea7eea7d87
--- /dev/null
+++ b/source/pam_smbpass/pam_smb_acct.c
@@ -0,0 +1,121 @@
+/* Unix NT password database implementation, version 0.7.5.
+ *
+ * This program is free software; you can 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.
+*/
+
+/* indicate the following groups are defined */
+#define PAM_SM_ACCT
+
+#include "includes.h"
+
+#ifndef LINUX
+
+/* This is only used in the Sun implementation. */
+#include <security/pam_appl.h>
+
+#endif /* LINUX */
+
+#include <security/pam_modules.h>
+
+#include "general.h"
+
+#include "support.h"
+
+
+/*
+ * pam_sm_acct_mgmt() verifies whether or not the account is disabled.
+ *
+ */
+
+int pam_sm_acct_mgmt( pam_handle_t *pamh, int flags,
+ int argc, const char **argv )
+{
+ unsigned int ctrl;
+ int retval;
+
+ const char *name;
+ SAM_ACCOUNT *sampass = NULL;
+ void (*oldsig_handler)(int);
+ extern BOOL in_client;
+
+ /* Samba initialization. */
+ setup_logging( "pam_smbpass", False );
+ in_client = True;
+
+ ctrl = set_ctrl( flags, argc, argv );
+
+ /* get the username */
+
+ retval = pam_get_user( pamh, &name, "Username: " );
+ if (retval != PAM_SUCCESS) {
+ if (on( SMB_DEBUG, ctrl )) {
+ _log_err( LOG_DEBUG, "acct: could not identify user" );
+ }
+ return retval;
+ }
+ if (on( SMB_DEBUG, ctrl )) {
+ _log_err( LOG_DEBUG, "acct: username [%s] obtained", name );
+ }
+
+ /* Getting into places that might use LDAP -- protect the app
+ from a SIGPIPE it's not expecting */
+ oldsig_handler = CatchSignal(SIGPIPE, SIGNAL_CAST SIG_IGN);
+ if (!initialize_password_db(True)) {
+ _log_err( LOG_ALERT, "Cannot access samba password database" );
+ CatchSignal(SIGPIPE, SIGNAL_CAST oldsig_handler);
+ return PAM_AUTHINFO_UNAVAIL;
+ }
+
+ /* Get the user's record. */
+ pdb_init_sam(&sampass);
+ pdb_getsampwnam(sampass, name );
+
+ if (!sampass) {
+ CatchSignal(SIGPIPE, SIGNAL_CAST oldsig_handler);
+ return PAM_USER_UNKNOWN;
+ }
+
+ if (pdb_get_acct_ctrl(sampass) & ACB_DISABLED) {
+ if (on( SMB_DEBUG, ctrl )) {
+ _log_err( LOG_DEBUG
+ , "acct: account %s is administratively disabled", name );
+ }
+ make_remark( pamh, ctrl, PAM_ERROR_MSG
+ , "Your account has been disabled; "
+ "please see your system administrator." );
+
+ CatchSignal(SIGPIPE, SIGNAL_CAST oldsig_handler);
+ return PAM_ACCT_EXPIRED;
+ }
+
+ /* TODO: support for expired passwords. */
+
+ CatchSignal(SIGPIPE, SIGNAL_CAST oldsig_handler);
+ return PAM_SUCCESS;
+}
+
+/* static module data */
+#ifdef PAM_STATIC
+struct pam_module _pam_smbpass_acct_modstruct = {
+ "pam_smbpass",
+ NULL,
+ NULL,
+ pam_sm_acct_mgmt,
+ NULL,
+ NULL,
+ NULL
+};
+#endif
+
diff --git a/source/pam_smbpass/pam_smb_auth.c b/source/pam_smbpass/pam_smb_auth.c
new file mode 100644
index 00000000000..4452538d32c
--- /dev/null
+++ b/source/pam_smbpass/pam_smb_auth.c
@@ -0,0 +1,256 @@
+/* Unix NT password database implementation, version 0.7.5.
+ *
+ * This program is free software; you can 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.
+*/
+
+/* indicate the following groups are defined */
+#define PAM_SM_AUTH
+
+#include "includes.h"
+#include "debug.h"
+
+#ifndef LINUX
+
+/* This is only used in the Sun implementation. */
+#include <security/pam_appl.h>
+
+#endif /* LINUX */
+
+#include <security/pam_modules.h>
+
+#include "general.h"
+
+#include "support.h"
+
+#define AUTH_RETURN \
+do { \
+ /* Restore application signal handler */ \
+ CatchSignal(SIGPIPE, SIGNAL_CAST oldsig_handler); \
+ if(ret_data) { \
+ *ret_data = retval; \
+ pam_set_data( pamh, "smb_setcred_return" \
+ , (void *) ret_data, NULL ); \
+ } \
+ return retval; \
+} while (0)
+
+static int _smb_add_user(pam_handle_t *pamh, unsigned int ctrl,
+ const char *name, SAM_ACCOUNT *sampass, BOOL exist);
+
+
+/*
+ * pam_sm_authenticate() authenticates users against the samba password file.
+ *
+ * First, obtain the password from the user. Then use a
+ * routine in 'support.c' to authenticate the user.
+ */
+
+#define _SMB_AUTHTOK "-SMB-PASS"
+
+int pam_sm_authenticate(pam_handle_t *pamh, int flags,
+ int argc, const char **argv)
+{
+ unsigned int ctrl;
+ int retval, *ret_data = NULL;
+ SAM_ACCOUNT *sampass = NULL;
+ extern BOOL in_client;
+ const char *name;
+ void (*oldsig_handler)(int);
+ BOOL found;
+
+ /* Points to memory managed by the PAM library. Do not free. */
+ char *p = NULL;
+
+
+ /* Samba initialization. */
+ setup_logging("pam_smbpass",False);
+ in_client = True;
+
+ ctrl = set_ctrl(flags, argc, argv);
+
+ /* Get a few bytes so we can pass our return value to
+ pam_sm_setcred(). */
+ ret_data = malloc(sizeof(int));
+
+ /* get the username */
+ retval = pam_get_user( pamh, &name, "Username: " );
+ if ( retval != PAM_SUCCESS ) {
+ if (on( SMB_DEBUG, ctrl )) {
+ _log_err(LOG_DEBUG, "auth: could not identify user");
+ }
+ AUTH_RETURN;
+ }
+ if (on( SMB_DEBUG, ctrl )) {
+ _log_err( LOG_DEBUG, "username [%s] obtained", name );
+ }
+
+ /* Getting into places that might use LDAP -- protect the app
+ from a SIGPIPE it's not expecting */
+ oldsig_handler = CatchSignal(SIGPIPE, SIGNAL_CAST SIG_IGN);
+
+ if (!initialize_password_db(True)) {
+ _log_err( LOG_ALERT, "Cannot access samba password database" );
+ retval = PAM_AUTHINFO_UNAVAIL;
+ AUTH_RETURN;
+ }
+
+ pdb_init_sam(&sampass);
+
+ found = pdb_getsampwnam( sampass, name );
+
+ if (on( SMB_MIGRATE, ctrl )) {
+ retval = _smb_add_user(pamh, ctrl, name, sampass, found);
+ pdb_free_sam(&sampass);
+ AUTH_RETURN;
+ }
+
+ if (!found) {
+ _log_err(LOG_ALERT, "Failed to find entry for user %s.", name);
+ retval = PAM_USER_UNKNOWN;
+ pdb_free_sam(&sampass);
+ sampass = NULL;
+ AUTH_RETURN;
+ }
+
+ /* if this user does not have a password... */
+
+ if (_smb_blankpasswd( ctrl, sampass )) {
+ pdb_free_sam(&sampass);
+ retval = PAM_SUCCESS;
+ AUTH_RETURN;
+ }
+
+ /* get this user's authentication token */
+
+ retval = _smb_read_password(pamh, ctrl, NULL, "Password: ", NULL, _SMB_AUTHTOK, &p);
+ if (retval != PAM_SUCCESS ) {
+ _log_err(LOG_CRIT, "auth: no password provided for [%s]"
+ , name);
+ pdb_free_sam(&sampass);
+ AUTH_RETURN;
+ }
+
+ /* verify the password of this user */
+
+ retval = _smb_verify_password( pamh, sampass, p, ctrl );
+ pdb_free_sam(&sampass);
+ p = NULL;
+ AUTH_RETURN;
+}
+
+/*
+ * This function is for setting samba credentials. If anyone comes up
+ * with any credentials they think should be set, let me know.
+ */
+
+int pam_sm_setcred(pam_handle_t *pamh, int flags,
+ int argc, const char **argv)
+{
+ int retval, *pretval = NULL;
+
+ retval = PAM_SUCCESS;
+
+ pam_get_data(pamh, "smb_setcred_return", (const void **) &pretval);
+ if(pretval) {
+ retval = *pretval;
+ SAFE_FREE(pretval);
+ }
+ pam_set_data(pamh, "smb_setcred_return", NULL, NULL);
+
+ return retval;
+}
+
+
+/* Helper function for adding a user to the db. */
+static int _smb_add_user(pam_handle_t *pamh, unsigned int ctrl,
+ const char *name, SAM_ACCOUNT *sampass, BOOL exist)
+{
+ pstring err_str;
+ pstring msg_str;
+ const char *pass = NULL;
+ int retval;
+
+ err_str[0] = '\0';
+ msg_str[0] = '\0';
+
+ /* Get the authtok; if we don't have one, silently fail. */
+ retval = pam_get_item( pamh, PAM_AUTHTOK, (const void **) &pass );
+
+ if (retval != PAM_SUCCESS) {
+ _log_err( LOG_ALERT
+ , "pam_get_item returned error to pam_sm_authenticate" );
+ return PAM_AUTHTOK_RECOVER_ERR;
+ } else if (pass == NULL) {
+ return PAM_AUTHTOK_RECOVER_ERR;
+ }
+
+ /* Add the user to the db if they aren't already there. */
+ if (!exist) {
+ retval = local_password_change( name, LOCAL_ADD_USER|LOCAL_SET_PASSWORD,
+ pass, err_str,
+ sizeof(err_str),
+ msg_str, sizeof(msg_str) );
+ if (!retval && *err_str)
+ {
+ err_str[PSTRING_LEN-1] = '\0';
+ make_remark( pamh, ctrl, PAM_ERROR_MSG, err_str );
+ }
+ else if (*msg_str)
+ {
+ msg_str[PSTRING_LEN-1] = '\0';
+ make_remark( pamh, ctrl, PAM_TEXT_INFO, msg_str );
+ }
+ pass = NULL;
+
+ return PAM_IGNORE;
+ }
+ else {
+ /* Change the user's password IFF it's null. */
+ if ((pdb_get_lanman_passwd(sampass) == NULL) && (pdb_get_acct_ctrl(sampass) & ACB_PWNOTREQ))
+ {
+ retval = local_password_change( name, 0, pass, err_str, sizeof(err_str),
+ msg_str, sizeof(msg_str) );
+ if (!retval && *err_str)
+ {
+ err_str[PSTRING_LEN-1] = '\0';
+ make_remark( pamh, ctrl, PAM_ERROR_MSG, err_str );
+ }
+ else if (*msg_str)
+ {
+ msg_str[PSTRING_LEN-1] = '\0';
+ make_remark( pamh, ctrl, PAM_TEXT_INFO, msg_str );
+ }
+ }
+ }
+
+ pass = NULL;
+
+ return PAM_IGNORE;
+}
+
+
+/* static module data */
+#ifdef PAM_STATIC
+struct pam_module _pam_smbpass_auth_modstruct = {
+ "pam_smbpass",
+ pam_sm_authenticate,
+ pam_sm_setcred,
+ NULL,
+ NULL,
+ NULL,
+ NULL
+};
+#endif
+
diff --git a/source/pam_smbpass/pam_smb_passwd.c b/source/pam_smbpass/pam_smb_passwd.c
new file mode 100644
index 00000000000..bef587a916c
--- /dev/null
+++ b/source/pam_smbpass/pam_smb_passwd.c
@@ -0,0 +1,349 @@
+/*
+ Unix SMB/CIFS implementation.
+ Use PAM to update user passwords in the local SAM
+ Copyright (C) Steve Langasek 1998-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 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.
+
+*/
+
+/* indicate the following groups are defined */
+#define PAM_SM_PASSWORD
+
+#include "includes.h"
+
+/* This is only used in the Sun implementation. FIXME: we really
+ want a define here that distinguishes between the Solaris PAM
+ and others (including FreeBSD). */
+
+#ifndef LINUX
+#include <security/pam_appl.h>
+#endif
+
+#include <security/pam_modules.h>
+
+#include "general.h"
+
+#include "support.h"
+
+int smb_update_db( pam_handle_t *pamh, int ctrl, const char *user, const char *pass_new )
+{
+ int retval;
+ pstring err_str;
+ pstring msg_str;
+
+ err_str[0] = '\0';
+ msg_str[0] = '\0';
+
+ retval = local_password_change( user, LOCAL_SET_PASSWORD, pass_new,
+ err_str, sizeof(err_str),
+ msg_str, sizeof(msg_str) );
+
+ if (!retval) {
+ if (*err_str) {
+ err_str[PSTRING_LEN-1] = '\0';
+ make_remark( pamh, ctrl, PAM_ERROR_MSG, err_str );
+ }
+
+ /* FIXME: what value is appropriate here? */
+ retval = PAM_AUTHTOK_ERR;
+ } else {
+ if (*msg_str) {
+ msg_str[PSTRING_LEN-1] = '\0';
+ make_remark( pamh, ctrl, PAM_TEXT_INFO, msg_str );
+ }
+ retval = PAM_SUCCESS;
+ }
+
+ return retval;
+}
+
+
+/* data tokens */
+
+#define _SMB_OLD_AUTHTOK "-SMB-OLD-PASS"
+#define _SMB_NEW_AUTHTOK "-SMB-NEW-PASS"
+
+/*
+ * FUNCTION: pam_sm_chauthtok()
+ *
+ * This function is called twice by the PAM library, once with
+ * PAM_PRELIM_CHECK set, and then again with PAM_UPDATE_AUTHTOK set. With
+ * Linux-PAM, these two passes generally involve first checking the old
+ * token and then changing the token. This is what we do here.
+ *
+ * Having obtained a new password. The function updates the
+ * SMB_PASSWD_FILE file (normally, $(LIBDIR)/smbpasswd).
+ */
+
+int pam_sm_chauthtok(pam_handle_t *pamh, int flags,
+ int argc, const char **argv)
+{
+ unsigned int ctrl;
+ int retval;
+
+ extern BOOL in_client;
+
+ SAM_ACCOUNT *sampass = NULL;
+ void (*oldsig_handler)(int);
+ const char *user;
+ char *pass_old;
+ char *pass_new;
+
+ NTSTATUS nt_status;
+
+ /* Samba initialization. */
+ setup_logging( "pam_smbpass", False );
+ in_client = True;
+
+ ctrl = set_ctrl(flags, argc, argv);
+
+ /*
+ * First get the name of a user. No need to do anything if we can't
+ * determine this.
+ */
+
+ retval = pam_get_user( pamh, &user, "Username: " );
+ if (retval != PAM_SUCCESS) {
+ if (on( SMB_DEBUG, ctrl )) {
+ _log_err( LOG_DEBUG, "password: could not identify user" );
+ }
+ return retval;
+ }
+ if (on( SMB_DEBUG, ctrl )) {
+ _log_err( LOG_DEBUG, "username [%s] obtained", user );
+ }
+
+ /* Getting into places that might use LDAP -- protect the app
+ from a SIGPIPE it's not expecting */
+ oldsig_handler = CatchSignal(SIGPIPE, SIGNAL_CAST SIG_IGN);
+
+ if (!initialize_password_db(True)) {
+ _log_err( LOG_ALERT, "Cannot access samba password database" );
+ CatchSignal(SIGPIPE, SIGNAL_CAST oldsig_handler);
+ return PAM_AUTHINFO_UNAVAIL;
+ }
+
+ /* obtain user record */
+ if (!NT_STATUS_IS_OK(nt_status = pdb_init_sam(&sampass))) {
+ CatchSignal(SIGPIPE, SIGNAL_CAST oldsig_handler);
+ return nt_status_to_pam(nt_status);
+ }
+
+ if (!pdb_getsampwnam(sampass,user)) {
+ _log_err( LOG_ALERT, "Failed to find entry for user %s.", user );
+ CatchSignal(SIGPIPE, SIGNAL_CAST oldsig_handler);
+ return PAM_USER_UNKNOWN;
+ }
+
+ if (flags & PAM_PRELIM_CHECK) {
+ /*
+ * obtain and verify the current password (OLDAUTHTOK) for
+ * the user.
+ */
+
+ char *Announce;
+
+ if (_smb_blankpasswd( ctrl, sampass )) {
+
+ pdb_free_sam(&sampass);
+ CatchSignal(SIGPIPE, SIGNAL_CAST oldsig_handler);
+ return PAM_SUCCESS;
+ }
+
+ /* Password change by root, or for an expired token, doesn't
+ require authentication. Is this a good choice? */
+ if (getuid() != 0 && !(flags & PAM_CHANGE_EXPIRED_AUTHTOK)) {
+
+ /* tell user what is happening */
+#define greeting "Changing password for "
+ Announce = (char *) malloc(sizeof(greeting)+strlen(user));
+ if (Announce == NULL) {
+ _log_err(LOG_CRIT, "password: out of memory");
+ pdb_free_sam(&sampass);
+ CatchSignal(SIGPIPE, SIGNAL_CAST oldsig_handler);
+ return PAM_BUF_ERR;
+ }
+ strncpy( Announce, greeting, sizeof(greeting) );
+ strncpy( Announce+sizeof(greeting)-1, user, strlen(user)+1 );
+#undef greeting
+
+ set( SMB__OLD_PASSWD, ctrl );
+ retval = _smb_read_password( pamh, ctrl, Announce, "Current SMB password: ",
+ NULL, _SMB_OLD_AUTHTOK, &pass_old );
+ SAFE_FREE( Announce );
+
+ if (retval != PAM_SUCCESS) {
+ _log_err( LOG_NOTICE
+ , "password - (old) token not obtained" );
+ pdb_free_sam(&sampass);
+ CatchSignal(SIGPIPE, SIGNAL_CAST oldsig_handler);
+ return retval;
+ }
+
+ /* verify that this is the password for this user */
+
+ retval = _smb_verify_password( pamh, sampass, pass_old, ctrl );
+
+ } else {
+ pass_old = NULL;
+ retval = PAM_SUCCESS; /* root doesn't have to */
+ }
+
+ pass_old = NULL;
+ pdb_free_sam(&sampass);
+ CatchSignal(SIGPIPE, SIGNAL_CAST oldsig_handler);
+ return retval;
+
+ } else if (flags & PAM_UPDATE_AUTHTOK) {
+
+ /*
+ * obtain the proposed password
+ */
+
+ /*
+ * get the old token back. NULL was ok only if root [at this
+ * point we assume that this has already been enforced on a
+ * previous call to this function].
+ */
+
+ if (off( SMB_NOT_SET_PASS, ctrl )) {
+ retval = pam_get_item( pamh, PAM_OLDAUTHTOK,
+ (const void **)&pass_old );
+ } else {
+ retval = pam_get_data( pamh, _SMB_OLD_AUTHTOK,
+ (const void **)&pass_old );
+ if (retval == PAM_NO_MODULE_DATA) {
+ pass_old = NULL;
+ retval = PAM_SUCCESS;
+ }
+ }
+
+ if (retval != PAM_SUCCESS) {
+ _log_err( LOG_NOTICE, "password: user not authenticated" );
+ pdb_free_sam(&sampass);
+ CatchSignal(SIGPIPE, SIGNAL_CAST oldsig_handler);
+ return retval;
+ }
+
+ /*
+ * use_authtok is to force the use of a previously entered
+ * password -- needed for pluggable password strength checking
+ * or other module stacking
+ */
+
+ if (on( SMB_USE_AUTHTOK, ctrl )) {
+ set( SMB_USE_FIRST_PASS, ctrl );
+ }
+
+ retval = _smb_read_password( pamh, ctrl
+ , NULL
+ , "Enter new SMB password: "
+ , "Retype new SMB password: "
+ , _SMB_NEW_AUTHTOK
+ , &pass_new );
+
+ if (retval != PAM_SUCCESS) {
+ if (on( SMB_DEBUG, ctrl )) {
+ _log_err( LOG_ALERT
+ , "password: new password not obtained" );
+ }
+ pass_old = NULL; /* tidy up */
+ pdb_free_sam(&sampass);
+ CatchSignal(SIGPIPE, SIGNAL_CAST oldsig_handler);
+ return retval;
+ }
+
+ /*
+ * At this point we know who the user is and what they
+ * propose as their new password. Verify that the new
+ * password is acceptable.
+ */
+
+ if (pass_new[0] == '\0') { /* "\0" password = NULL */
+ pass_new = NULL;
+ }
+
+ retval = _pam_smb_approve_pass(pamh, ctrl, pass_old, pass_new);
+
+ if (retval != PAM_SUCCESS) {
+ _log_err(LOG_NOTICE, "new password not acceptable");
+ pass_new = pass_old = NULL; /* tidy up */
+ pdb_free_sam(&sampass);
+ CatchSignal(SIGPIPE, SIGNAL_CAST oldsig_handler);
+ return retval;
+ }
+
+ /*
+ * By reaching here we have approved the passwords and must now
+ * rebuild the smb password file.
+ */
+
+ /* update the password database */
+
+ retval = smb_update_db(pamh, ctrl, user, pass_new);
+ if (retval == PAM_SUCCESS) {
+ uid_t uid;
+
+ /* password updated */
+ if (!NT_STATUS_IS_OK(sid_to_uid(pdb_get_user_sid(sampass), &uid))) {
+ _log_err( LOG_NOTICE, "Unable to get uid for user %s",
+ pdb_get_username(sampass));
+ _log_err( LOG_NOTICE, "password for (%s) changed by (%s/%d)",
+ user, uidtoname(getuid()), getuid());
+ } else {
+ _log_err( LOG_NOTICE, "password for (%s/%d) changed by (%s/%d)",
+ user, uid, uidtoname(getuid()), getuid());
+ }
+ } else {
+ _log_err( LOG_ERR, "password change failed for user %s", user);
+ }
+
+ pass_old = pass_new = NULL;
+ if (sampass) {
+ pdb_free_sam(&sampass);
+ sampass = NULL;
+ }
+
+ } else { /* something has broken with the library */
+
+ _log_err( LOG_ALERT, "password received unknown request" );
+ retval = PAM_ABORT;
+
+ }
+
+ if (sampass) {
+ pdb_free_sam(&sampass);
+ sampass = NULL;
+ }
+
+ pdb_free_sam(&sampass);
+ CatchSignal(SIGPIPE, SIGNAL_CAST oldsig_handler);
+ return retval;
+}
+
+/* static module data */
+#ifdef PAM_STATIC
+struct pam_module _pam_smbpass_passwd_modstruct = {
+ "pam_smbpass",
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ pam_sm_chauthtok
+};
+#endif
+
diff --git a/source/pam_smbpass/samples/README b/source/pam_smbpass/samples/README
new file mode 100644
index 00000000000..d77603306f1
--- /dev/null
+++ b/source/pam_smbpass/samples/README
@@ -0,0 +1,3 @@
+This directory contains example configurations demonstrating various uses
+of pam_smbpass. These examples use Linux-style /etc/pam.d syntax, and
+must be modified for use on Solaris systems.
diff --git a/source/pam_smbpass/samples/kdc-pdc b/source/pam_smbpass/samples/kdc-pdc
new file mode 100644
index 00000000000..70f1998f32a
--- /dev/null
+++ b/source/pam_smbpass/samples/kdc-pdc
@@ -0,0 +1,15 @@
+#%PAM-1.0
+# kdc-pdc
+#
+# A sample PAM configuration that shows pam_smbpass used together with
+# pam_krb5. This could be useful on a Samba PDC that is also a member of
+# a Kerberos realm.
+
+auth requisite pam_nologin.so
+auth requisite pam_krb5.so
+auth optional pam_smbpass.so migrate
+account required pam_krb5.so
+password requisite pam_cracklib.so retry=3
+password optional pam_smbpass.so nullok use_authtok try_first_pass
+password required pam_krb5.so use_authtok try_first_pass
+session required pam_krb5.so
diff --git a/source/pam_smbpass/samples/password-mature b/source/pam_smbpass/samples/password-mature
new file mode 100644
index 00000000000..6d73e0906fc
--- /dev/null
+++ b/source/pam_smbpass/samples/password-mature
@@ -0,0 +1,14 @@
+#%PAM-1.0
+# password-mature
+#
+# A sample PAM configuration for a 'mature' smbpasswd installation.
+# private/smbpasswd is fully populated, and we consider it an error if
+# the smbpasswd doesn't exist or doesn't match the Unix password.
+
+auth requisite pam_nologin.so
+auth required pam_unix.so
+account required pam_unix.so
+password requisite pam_cracklib.so retry=3
+password requisite pam_unix.so shadow md5 use_authtok try_first_pass
+password required pam_smbpass.so use_authtok use_first_pass
+session required pam_unix.so
diff --git a/source/pam_smbpass/samples/password-migration b/source/pam_smbpass/samples/password-migration
new file mode 100644
index 00000000000..305cb53858e
--- /dev/null
+++ b/source/pam_smbpass/samples/password-migration
@@ -0,0 +1,18 @@
+#%PAM-1.0
+# password-migration
+#
+# A sample PAM configuration that shows the use of pam_smbpass to migrate
+# from plaintext to encrypted passwords for Samba. Unlike other methods,
+# this can be used for users who have never connected to Samba shares:
+# password migration takes place when users ftp in, login using ssh, pop
+# their mail, etc.
+
+auth requisite pam_nologin.so
+# pam_smbpass is called IFF pam_unix succeeds.
+auth requisite pam_unix.so
+auth optional pam_smbpass.so migrate
+account required pam_unix.so
+password requisite pam_cracklib.so retry=3
+password requisite pam_unix.so shadow md5 use_authtok try_first_pass
+password optional pam_smbpass.so nullok use_authtok try_first_pass
+session required pam_unix.so
diff --git a/source/pam_smbpass/samples/password-sync b/source/pam_smbpass/samples/password-sync
new file mode 100644
index 00000000000..0a950dd2e9a
--- /dev/null
+++ b/source/pam_smbpass/samples/password-sync
@@ -0,0 +1,15 @@
+#%PAM-1.0
+# password-sync
+#
+# A sample PAM configuration that shows the use of pam_smbpass to make
+# sure private/smbpasswd is kept in sync when /etc/passwd (/etc/shadow)
+# is changed. Useful when an expired password might be changed by an
+# application (such as ssh).
+
+auth requisite pam_nologin.so
+auth required pam_unix.so
+account required pam_unix.so
+password requisite pam_cracklib.so retry=3
+password requisite pam_unix.so shadow md5 use_authtok try_first_pass
+password required pam_smbpass.so nullok use_authtok try_first_pass
+session required pam_unix.so
diff --git a/source/pam_smbpass/support.c b/source/pam_smbpass/support.c
new file mode 100644
index 00000000000..8a0432c8550
--- /dev/null
+++ b/source/pam_smbpass/support.c
@@ -0,0 +1,635 @@
+ /* Unix NT password database implementation, version 0.6.
+ *
+ * This program is free software; you can 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 "general.h"
+
+ #include "support.h"
+
+
+ #define _pam_overwrite(x) \
+ do { \
+ register char *__xx__; \
+ if ((__xx__=(x))) \
+ while (*__xx__) \
+ *__xx__++ = '\0'; \
+ } while (0)
+
+ /*
+ * Don't just free it, forget it too.
+ */
+
+ #define _pam_drop(X) \
+ do { \
+ if (X) { \
+ free(X); \
+ X=NULL; \
+ } \
+ } while (0)
+
+ #define _pam_drop_reply(/* struct pam_response * */ reply, /* int */ replies) \
+ do { \
+ int reply_i; \
+ \
+ for (reply_i=0; reply_i<replies; ++reply_i) { \
+ if (reply[reply_i].resp) { \
+ _pam_overwrite(reply[reply_i].resp); \
+ free(reply[reply_i].resp); \
+ } \
+ } \
+ if (reply) \
+ free(reply); \
+ } while (0)
+
+
+ int converse(pam_handle_t *, int, int, struct pam_message **,
+ struct pam_response **);
+ int make_remark(pam_handle_t *, unsigned int, int, const char *);
+ void _cleanup(pam_handle_t *, void *, int);
+ char *_pam_delete(register char *);
+
+ /* default configuration file location */
+
+ char *servicesf = dyn_CONFIGFILE;
+
+ /* syslogging function for errors and other information */
+
+ void _log_err( int err, const char *format, ... )
+ {
+ va_list args;
+
+ va_start( args, format );
+ openlog( "PAM_smbpass", LOG_CONS | LOG_PID, LOG_AUTH );
+ vsyslog( err, format, args );
+ va_end( args );
+ closelog();
+ }
+
+ /* this is a front-end for module-application conversations */
+
+ int converse( pam_handle_t * pamh, int ctrl, int nargs
+ , struct pam_message **message
+ , struct pam_response **response )
+ {
+ int retval;
+ struct pam_conv *conv;
+
+ retval = pam_get_item(pamh, PAM_CONV, (const void **) &conv);
+ if (retval == PAM_SUCCESS) {
+
+ retval = conv->conv(nargs, (const struct pam_message **) message
+ ,response, conv->appdata_ptr);
+
+ if (retval != PAM_SUCCESS && on(SMB_DEBUG, ctrl)) {
+ _log_err(LOG_DEBUG, "conversation failure [%s]"
+ ,pam_strerror(pamh, retval));
+ }
+ } else {
+ _log_err(LOG_ERR, "couldn't obtain coversation function [%s]"
+ ,pam_strerror(pamh, retval));
+ }
+
+ return retval; /* propagate error status */
+ }
+
+ int make_remark( pam_handle_t * pamh, unsigned int ctrl
+ , int type, const char *text )
+ {
+ if (off(SMB__QUIET, ctrl)) {
+ struct pam_message *pmsg[1], msg[1];
+ struct pam_response *resp;
+
+ pmsg[0] = &msg[0];
+ msg[0].msg = text;
+ msg[0].msg_style = type;
+ resp = NULL;
+
+ return converse(pamh, ctrl, 1, pmsg, &resp);
+ }
+ return PAM_SUCCESS;
+ }
+
+
+ /* set the control flags for the SMB module. */
+
+int set_ctrl( int flags, int argc, const char **argv )
+{
+ int i = 0;
+ const char *service_file = dyn_CONFIGFILE;
+ unsigned int ctrl;
+
+ ctrl = SMB_DEFAULTS; /* the default selection of options */
+
+ /* set some flags manually */
+
+ /* A good, sane default (matches Samba's behavior). */
+ set( SMB__NONULL, ctrl );
+
+ /* initialize service file location */
+ service_file=servicesf;
+
+ if (flags & PAM_SILENT) {
+ set( SMB__QUIET, ctrl );
+ }
+
+ /* Run through the arguments once, looking for an alternate smb config
+ file location */
+ while (i < argc) {
+ int j;
+
+ for (j = 0; j < SMB_CTRLS_; ++j) {
+ if (smb_args[j].token
+ && !strncmp(argv[i], smb_args[j].token, strlen(smb_args[j].token)))
+ {
+ break;
+ }
+ }
+
+ if (j == SMB_CONF_FILE) {
+ service_file = argv[i] + 8;
+ }
+ i++;
+ }
+
+ /* Read some options from the Samba config. Can be overridden by
+ the PAM config. */
+ if(lp_load(service_file,True,False,False) == False) {
+ _log_err( LOG_ERR, "Error loading service file %s", service_file );
+ }
+
+ secrets_init();
+
+ if (lp_null_passwords()) {
+ set( SMB__NULLOK, ctrl );
+ }
+
+ /* now parse the rest of the arguments to this module */
+
+ while (argc-- > 0) {
+ int j;
+
+ for (j = 0; j < SMB_CTRLS_; ++j) {
+ if (smb_args[j].token
+ && !strncmp(*argv, smb_args[j].token, strlen(smb_args[j].token)))
+ {
+ break;
+ }
+ }
+
+ if (j >= SMB_CTRLS_) {
+ _log_err( LOG_ERR, "unrecognized option [%s]", *argv );
+ } else {
+ ctrl &= smb_args[j].mask; /* for turning things off */
+ ctrl |= smb_args[j].flag; /* for turning things on */
+ }
+
+ ++argv; /* step to next argument */
+ }
+
+ /* auditing is a more sensitive version of debug */
+
+ if (on( SMB_AUDIT, ctrl )) {
+ set( SMB_DEBUG, ctrl );
+ }
+ /* return the set of flags */
+
+ return ctrl;
+}
+
+/* use this to free strings. ESPECIALLY password strings */
+
+char * _pam_delete( register char *xx )
+{
+ _pam_overwrite( xx );
+ _pam_drop( xx );
+ return NULL;
+}
+
+void _cleanup( pam_handle_t * pamh, void *x, int error_status )
+{
+ x = _pam_delete( (char *) x );
+}
+
+/* JHT
+ *
+ * Safe duplication of character strings. "Paranoid"; don't leave
+ * evidence of old token around for later stack analysis.
+ *
+ */
+char * smbpXstrDup( const char *x )
+{
+ register char *new = NULL;
+
+ if (x != NULL) {
+ register int i;
+
+ for (i = 0; x[i]; ++i); /* length of string */
+ if ((new = malloc(++i)) == NULL) {
+ i = 0;
+ _log_err( LOG_CRIT, "out of memory in smbpXstrDup" );
+ } else {
+ while (i-- > 0) {
+ new[i] = x[i];
+ }
+ }
+ x = NULL;
+ }
+ return new; /* return the duplicate or NULL on error */
+}
+
+/* ************************************************************** *
+ * Useful non-trivial functions *
+ * ************************************************************** */
+
+void _cleanup_failures( pam_handle_t * pamh, void *fl, int err )
+{
+ int quiet;
+ const char *service = NULL;
+ struct _pam_failed_auth *failure;
+
+#ifdef PAM_DATA_SILENT
+ quiet = err & PAM_DATA_SILENT; /* should we log something? */
+#else
+ quiet = 0;
+#endif
+#ifdef PAM_DATA_REPLACE
+ err &= PAM_DATA_REPLACE; /* are we just replacing data? */
+#endif
+ failure = (struct _pam_failed_auth *) fl;
+
+ if (failure != NULL) {
+
+#ifdef PAM_DATA_SILENT
+ if (!quiet && !err) { /* under advisement from Sun,may go away */
+#else
+ if (!quiet) { /* under advisement from Sun,may go away */
+#endif
+
+ /* log the number of authentication failures */
+ if (failure->count != 0) {
+ pam_get_item( pamh, PAM_SERVICE, (const void **) &service );
+ _log_err( LOG_NOTICE
+ , "%d authentication %s "
+ "from %s for service %s as %s(%d)"
+ , failure->count
+ , failure->count == 1 ? "failure" : "failures"
+ , failure->agent
+ , service == NULL ? "**unknown**" : service
+ , failure->user, failure->id );
+ if (failure->count > SMB_MAX_RETRIES) {
+ _log_err( LOG_ALERT
+ , "service(%s) ignoring max retries; %d > %d"
+ , service == NULL ? "**unknown**" : service
+ , failure->count
+ , SMB_MAX_RETRIES );
+ }
+ }
+ }
+ _pam_delete( failure->agent ); /* tidy up */
+ _pam_delete( failure->user ); /* tidy up */
+ SAFE_FREE( failure );
+ }
+}
+
+int _smb_verify_password( pam_handle_t * pamh, SAM_ACCOUNT *sampass,
+ const char *p, unsigned int ctrl )
+{
+ uchar lm_pw[16];
+ uchar nt_pw[16];
+ int retval = PAM_AUTH_ERR;
+ char *data_name;
+ const char *name;
+
+ if (!sampass)
+ return PAM_ABORT;
+
+ name = pdb_get_username(sampass);
+
+#ifdef HAVE_PAM_FAIL_DELAY
+ if (off( SMB_NODELAY, ctrl )) {
+ (void) pam_fail_delay( pamh, 1000000 ); /* 1 sec delay for on failure */
+ }
+#endif
+
+ if (!pdb_get_lanman_passwd(sampass))
+ {
+ _log_err( LOG_DEBUG, "user %s has null SMB password"
+ , name );
+
+ if (off( SMB__NONULL, ctrl )
+ && (pdb_get_acct_ctrl(sampass) & ACB_PWNOTREQ))
+ { /* this means we've succeeded */
+ return PAM_SUCCESS;
+ } else {
+ const char *service;
+
+ pam_get_item( pamh, PAM_SERVICE, (const void **)&service );
+ _log_err( LOG_NOTICE, "failed auth request by %s for service %s as %s",
+ uidtoname(getuid()), service ? service : "**unknown**", name);
+ return PAM_AUTH_ERR;
+ }
+ }
+
+ data_name = (char *) malloc( sizeof(FAIL_PREFIX) + strlen( name ));
+ if (data_name == NULL) {
+ _log_err( LOG_CRIT, "no memory for data-name" );
+ }
+ strncpy( data_name, FAIL_PREFIX, sizeof(FAIL_PREFIX) );
+ strncpy( data_name + sizeof(FAIL_PREFIX) - 1, name, strlen( name ) + 1 );
+
+ /*
+ * The password we were given wasn't an encrypted password, or it
+ * didn't match the one we have. We encrypt the password now and try
+ * again.
+ */
+
+ nt_lm_owf_gen(p, nt_pw, lm_pw);
+
+ /* the moment of truth -- do we agree with the password? */
+
+ if (!memcmp( nt_pw, pdb_get_nt_passwd(sampass), 16 )) {
+
+ retval = PAM_SUCCESS;
+ if (data_name) { /* reset failures */
+ pam_set_data(pamh, data_name, NULL, _cleanup_failures);
+ }
+ } else {
+
+ const char *service;
+
+ pam_get_item( pamh, PAM_SERVICE, (const void **)&service );
+
+ if (data_name != NULL) {
+ struct _pam_failed_auth *new = NULL;
+ const struct _pam_failed_auth *old = NULL;
+
+ /* get a failure recorder */
+
+ new = (struct _pam_failed_auth *)
+ malloc( sizeof(struct _pam_failed_auth) );
+
+ if (new != NULL) {
+
+ /* any previous failures for this user ? */
+ pam_get_data(pamh, data_name, (const void **) &old);
+
+ if (old != NULL) {
+ new->count = old->count + 1;
+ if (new->count >= SMB_MAX_RETRIES) {
+ retval = PAM_MAXTRIES;
+ }
+ } else {
+ _log_err(LOG_NOTICE,
+ "failed auth request by %s for service %s as %s",
+ uidtoname(getuid()),
+ service ? service : "**unknown**", name);
+ new->count = 1;
+ }
+ if (!NT_STATUS_IS_OK(sid_to_uid(pdb_get_user_sid(sampass), &(new->id)))) {
+ _log_err(LOG_NOTICE,
+ "failed auth request by %s for service %s as %s",
+ uidtoname(getuid()),
+ service ? service : "**unknown**", name);
+ }
+ new->user = smbpXstrDup( name );
+ new->agent = smbpXstrDup( uidtoname( getuid() ) );
+ pam_set_data( pamh, data_name, new, _cleanup_failures );
+
+ } else {
+ _log_err( LOG_CRIT, "no memory for failure recorder" );
+ _log_err(LOG_NOTICE,
+ "failed auth request by %s for service %s as %s(%d)",
+ uidtoname(getuid()),
+ service ? service : "**unknown**", name);
+ }
+ } else {
+ _log_err(LOG_NOTICE,
+ "failed auth request by %s for service %s as %s(%d)",
+ uidtoname(getuid()),
+ service ? service : "**unknown**", name);
+ retval = PAM_AUTH_ERR;
+ }
+ }
+
+ _pam_delete( data_name );
+
+ return retval;
+}
+
+
+/*
+ * _smb_blankpasswd() is a quick check for a blank password
+ *
+ * returns TRUE if user does not have a password
+ * - to avoid prompting for one in such cases (CG)
+ */
+
+int _smb_blankpasswd( unsigned int ctrl, SAM_ACCOUNT *sampass )
+{
+ int retval;
+
+ /*
+ * This function does not have to be too smart if something goes
+ * wrong, return FALSE and let this case to be treated somewhere
+ * else (CG)
+ */
+
+ if (on( SMB__NONULL, ctrl ))
+ return 0; /* will fail but don't let on yet */
+
+ if (pdb_get_lanman_passwd(sampass) == NULL)
+ retval = 1;
+ else
+ retval = 0;
+
+ return retval;
+}
+
+/*
+ * obtain a password from the user
+ */
+
+int _smb_read_password( pam_handle_t * pamh, unsigned int ctrl,
+ const char *comment, const char *prompt1,
+ const char *prompt2, const char *data_name, char **pass )
+{
+ int authtok_flag;
+ int retval;
+ char *item = NULL;
+ char *token;
+
+ struct pam_message msg[3], *pmsg[3];
+ struct pam_response *resp;
+ int i, expect;
+
+
+ /* make sure nothing inappropriate gets returned */
+
+ *pass = token = NULL;
+
+ /* which authentication token are we getting? */
+
+ authtok_flag = on(SMB__OLD_PASSWD, ctrl) ? PAM_OLDAUTHTOK : PAM_AUTHTOK;
+
+ /* should we obtain the password from a PAM item ? */
+
+ if (on(SMB_TRY_FIRST_PASS, ctrl) || on(SMB_USE_FIRST_PASS, ctrl)) {
+ retval = pam_get_item( pamh, authtok_flag, (const void **) &item );
+ if (retval != PAM_SUCCESS) {
+ /* very strange. */
+ _log_err( LOG_ALERT
+ , "pam_get_item returned error to smb_read_password" );
+ return retval;
+ } else if (item != NULL) { /* we have a password! */
+ *pass = item;
+ item = NULL;
+ return PAM_SUCCESS;
+ } else if (on( SMB_USE_FIRST_PASS, ctrl )) {
+ return PAM_AUTHTOK_RECOVER_ERR; /* didn't work */
+ } else if (on( SMB_USE_AUTHTOK, ctrl )
+ && off( SMB__OLD_PASSWD, ctrl ))
+ {
+ return PAM_AUTHTOK_RECOVER_ERR;
+ }
+ }
+
+ /*
+ * getting here implies we will have to get the password from the
+ * user directly.
+ */
+
+ /* prepare to converse */
+ if (comment != NULL && off(SMB__QUIET, ctrl)) {
+ pmsg[0] = &msg[0];
+ msg[0].msg_style = PAM_TEXT_INFO;
+ msg[0].msg = comment;
+ i = 1;
+ } else {
+ i = 0;
+ }
+
+ pmsg[i] = &msg[i];
+ msg[i].msg_style = PAM_PROMPT_ECHO_OFF;
+ msg[i++].msg = prompt1;
+
+ if (prompt2 != NULL) {
+ pmsg[i] = &msg[i];
+ msg[i].msg_style = PAM_PROMPT_ECHO_OFF;
+ msg[i++].msg = prompt2;
+ expect = 2;
+ } else
+ expect = 1;
+
+ resp = NULL;
+
+ retval = converse( pamh, ctrl, i, pmsg, &resp );
+
+ if (resp != NULL) {
+ int j = comment ? 1 : 0;
+ /* interpret the response */
+
+ if (retval == PAM_SUCCESS) { /* a good conversation */
+
+ token = smbpXstrDup(resp[j++].resp);
+ if (token != NULL) {
+ if (expect == 2) {
+ /* verify that password entered correctly */
+ if (!resp[j].resp || strcmp( token, resp[j].resp )) {
+ _pam_delete( token );
+ retval = PAM_AUTHTOK_RECOVER_ERR;
+ make_remark( pamh, ctrl, PAM_ERROR_MSG
+ , MISTYPED_PASS );
+ }
+ }
+ } else {
+ _log_err(LOG_NOTICE, "could not recover authentication token");
+ }
+ }
+
+ /* tidy up */
+ _pam_drop_reply( resp, expect );
+
+ } else {
+ retval = (retval == PAM_SUCCESS) ? PAM_AUTHTOK_RECOVER_ERR : retval;
+ }
+
+ if (retval != PAM_SUCCESS) {
+ if (on( SMB_DEBUG, ctrl ))
+ _log_err( LOG_DEBUG, "unable to obtain a password" );
+ return retval;
+ }
+ /* 'token' is the entered password */
+
+ if (off( SMB_NOT_SET_PASS, ctrl )) {
+
+ /* we store this password as an item */
+
+ retval = pam_set_item( pamh, authtok_flag, (const void *)token );
+ _pam_delete( token ); /* clean it up */
+ if (retval != PAM_SUCCESS
+ || (retval = pam_get_item( pamh, authtok_flag
+ ,(const void **)&item )) != PAM_SUCCESS)
+ {
+ _log_err( LOG_CRIT, "error manipulating password" );
+ return retval;
+ }
+ } else {
+ /*
+ * then store it as data specific to this module. pam_end()
+ * will arrange to clean it up.
+ */
+
+ retval = pam_set_data( pamh, data_name, (void *) token, _cleanup );
+ if (retval != PAM_SUCCESS
+ || (retval = pam_get_data( pamh, data_name, (const void **)&item ))
+ != PAM_SUCCESS)
+ {
+ _log_err( LOG_CRIT, "error manipulating password data [%s]"
+ , pam_strerror( pamh, retval ));
+ _pam_delete( token );
+ item = NULL;
+ return retval;
+ }
+ token = NULL; /* break link to password */
+ }
+
+ *pass = item;
+ item = NULL; /* break link to password */
+
+ return PAM_SUCCESS;
+}
+
+int _pam_smb_approve_pass(pam_handle_t * pamh,
+ unsigned int ctrl,
+ const char *pass_old,
+ const char *pass_new )
+{
+
+ /* Further checks should be handled through module stacking. -SRL */
+ if (pass_new == NULL || (pass_old && !strcmp( pass_old, pass_new )))
+ {
+ if (on(SMB_DEBUG, ctrl)) {
+ _log_err( LOG_DEBUG,
+ "passwd: bad authentication token (null or unchanged)" );
+ }
+ make_remark( pamh, ctrl, PAM_ERROR_MSG, pass_new == NULL ?
+ "No password supplied" : "Password unchanged" );
+ return PAM_AUTHTOK_ERR;
+ }
+
+ return PAM_SUCCESS;
+}
diff --git a/source/pam_smbpass/support.h b/source/pam_smbpass/support.h
new file mode 100644
index 00000000000..a13a2d0aeb9
--- /dev/null
+++ b/source/pam_smbpass/support.h
@@ -0,0 +1,50 @@
+/* syslogging function for errors and other information */
+extern void _log_err(int, const char *, ...);
+
+/* set the control flags for the UNIX module. */
+extern int set_ctrl(int, int, const char **);
+
+/* generic function for freeing pam data segments */
+extern void _cleanup(pam_handle_t *, void *, int);
+
+/*
+ * Safe duplication of character strings. "Paranoid"; don't leave
+ * evidence of old token around for later stack analysis.
+ */
+
+extern char *smbpXstrDup(const char *);
+
+/* ************************************************************** *
+ * Useful non-trivial functions *
+ * ************************************************************** */
+
+extern void _cleanup_failures(pam_handle_t *, void *, int);
+
+/* compare 2 strings */
+extern BOOL strequal(const char *, const char *);
+
+extern struct smb_passwd *
+_my_get_smbpwnam(FILE *, const char *, BOOL *, BOOL *, long *);
+
+extern int _smb_verify_password( pam_handle_t *pamh , SAM_ACCOUNT *sampass,
+ const char *p, unsigned int ctrl );
+
+/*
+ * this function obtains the name of the current user and ensures
+ * that the PAM_USER item is set to this value
+ */
+
+extern int _smb_get_user(pam_handle_t *, unsigned int,
+ const char *, const char **);
+
+/* _smb_blankpasswd() is a quick check for a blank password */
+
+extern int _smb_blankpasswd(unsigned int, SAM_ACCOUNT *);
+
+
+/* obtain a password from the user */
+extern int _smb_read_password( pam_handle_t *, unsigned int, const char*,
+ const char *, const char *, const char *, char **);
+
+extern int _pam_smb_approve_pass(pam_handle_t *, unsigned int, const char *,
+ const char *);
diff --git a/source/param/.cvsignore b/source/param/.cvsignore
new file mode 100644
index 00000000000..76e2d7ff3eb
--- /dev/null
+++ b/source/param/.cvsignore
@@ -0,0 +1,3 @@
+*.po32
+*.po
+
diff --git a/source/param/config_ldap.c b/source/param/config_ldap.c
new file mode 100644
index 00000000000..fe4693fb583
--- /dev/null
+++ b/source/param/config_ldap.c
@@ -0,0 +1,351 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ ModConfig LDAP backend
+
+ Copyright (C) Simo Sorce 2003
+ Copyright (C) Jim McDonough <jmcd@us.ibm.com> 2003
+ Copyright (C) Gerald Carter 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 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"
+
+/*#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_CONFIG
+*/
+
+#include <lber.h>
+#include <ldap.h>
+
+#include "smbldap.h"
+
+#define LDAP_OBJ_SAMBA_CONFIG "sambaConfig"
+#define LDAP_OBJ_SAMBA_SHARE "sambaShare"
+#define LDAP_OBJ_SAMBA_OPTION "sambaConfigOption"
+
+#define LDAP_ATTR_LIST_END 0
+#define LDAP_ATTR_BOOL 1
+#define LDAP_ATTR_INTEGER 2
+#define LDAP_ATTR_STRING 3
+#define LDAP_ATTR_LIST 4
+#define LDAP_ATTR_NAME 5
+
+
+struct ldap_config_state {
+ struct smbldap_state *smbldap_state;
+ TALLOC_CTX *mem_ctx;
+};
+
+ATTRIB_MAP_ENTRY option_attr_list[] = {
+ { LDAP_ATTR_NAME, "sambaOptionName" },
+ { LDAP_ATTR_LIST, "sambaListOption" },
+ { LDAP_ATTR_STRING, "sambaStringOption" },
+ { LDAP_ATTR_INTEGER, "sambaIntegerOption" },
+ { LDAP_ATTR_BOOL, "sambaBoolOption" },
+ { LDAP_ATTR_LIST_END, NULL }
+};
+
+static struct ldap_config_state ldap_state;
+static char *config_base_dn;
+
+static NTSTATUS ldap_config_close(void);
+
+/*
+TODO:
+ search each section
+ start with global, then with others
+ for each section parse all options
+*/
+
+static NTSTATUS parse_section(
+ const char *dn,
+ BOOL (*pfunc)(const char *, const char *))
+{
+ TALLOC_CTX *mem_ctx;
+ NTSTATUS ret = NT_STATUS_UNSUCCESSFUL;
+ LDAPMessage *result = NULL;
+ LDAPMessage *entry = NULL;
+ pstring filter;
+ pstring option_name;
+ pstring option_value;
+ char **attr_list = NULL;
+ int rc;
+ int count;
+
+ mem_ctx = talloc_init("parse_section");
+
+ /* search for the options */
+ pstr_sprintf(filter, "objectClass=%s",
+ LDAP_OBJ_SAMBA_OPTION);
+
+ DEBUG(0, ("Searching for:[%s]\n", filter));
+
+ attr_list = get_attr_list(option_attr_list);
+ rc = smbldap_search(ldap_state.smbldap_state,
+ dn, LDAP_SCOPE_ONELEVEL,
+ filter, attr_list, 0, &result);
+
+ if (rc != LDAP_SUCCESS) {
+ DEBUG(0,("parse_section: %s object not found\n", LDAP_OBJ_SAMBA_CONFIG));
+ goto done;
+ }
+
+ count = ldap_count_entries(ldap_state.smbldap_state->ldap_struct, result);
+ entry = ldap_first_entry(ldap_state.smbldap_state->ldap_struct, result);
+ while (entry) {
+ int o;
+
+ if (!smbldap_get_single_pstring(ldap_state.smbldap_state->ldap_struct, entry, "sambaOptionName", option_name)) {
+ goto done;
+ }
+
+ option_value[0] = '\0';
+ for (o = 1; option_attr_list[o].name != NULL; o++) {
+ if (smbldap_get_single_pstring(ldap_state.smbldap_state->ldap_struct, entry, option_attr_list[o].name, option_value)) {
+ break;
+ }
+ }
+ if (option_value[0] != '\0') {
+ if (!pfunc(option_name, option_value)) {
+ goto done;
+ }
+ } else {
+ DEBUG(0,("parse_section: Missing value for option: %s\n", option_name));
+ goto done;
+ }
+
+ entry = ldap_next_entry(ldap_state.smbldap_state->ldap_struct, entry);
+ }
+
+ ret = NT_STATUS_OK;
+
+done:
+ talloc_destroy(mem_ctx);
+ free_attr_list(attr_list);
+ if (result) ldap_msgfree(result);
+
+ return ret;
+}
+
+/*****************************************************************************
+ load configuration from ldap
+*****************************************************************************/
+
+static NTSTATUS ldap_config_load(
+ BOOL (*sfunc)(const char *),
+ BOOL (*pfunc)(const char *, const char *))
+{
+ TALLOC_CTX *mem_ctx;
+ NTSTATUS ret = NT_STATUS_UNSUCCESSFUL;
+ LDAPMessage *result = NULL;
+ LDAPMessage *entry = NULL;
+ pstring filter;
+ pstring attr_text;
+ char *config_dn = NULL;
+ char *temp;
+ int rc;
+ int count;
+ const char *config_attr_list[] = {"description", NULL};
+ const char *share_attr_list[] = {"sambaShareName", "description", NULL};
+ char **share_dn;
+ char **share_name;
+
+ mem_ctx = talloc_init("ldap_config_load");
+
+ /* search for the base config dn */
+ pstr_sprintf(filter, "objectClass=%s",
+ LDAP_OBJ_SAMBA_CONFIG);
+
+ DEBUG(0, ("Searching for:[%s]\n", filter));
+
+ rc = smbldap_search(ldap_state.smbldap_state,
+ config_base_dn, LDAP_SCOPE_SUBTREE,
+ filter, config_attr_list, 0, &result);
+
+ if (rc != LDAP_SUCCESS) {
+ DEBUG(0,("ldap_config_load: %s object not found\n", LDAP_OBJ_SAMBA_CONFIG));
+ goto done;
+ }
+
+ count = ldap_count_entries(ldap_state.smbldap_state->ldap_struct, result);
+ if (count != 1) {
+ DEBUG(0,("ldap_config_load: single %s object not found\n", LDAP_OBJ_SAMBA_CONFIG));
+ goto done;
+ }
+
+ if (!(temp = smbldap_get_dn(ldap_state.smbldap_state->ldap_struct, result))) {
+ goto done;
+ }
+ config_dn = talloc_strdup(mem_ctx, temp);
+ SAFE_FREE(temp);
+ if (!config_dn) {
+ goto done;
+ }
+
+ entry = ldap_first_entry(ldap_state.smbldap_state->ldap_struct, result);
+
+ if (!smbldap_get_single_pstring(ldap_state.smbldap_state->ldap_struct, entry, "description", attr_text)) {
+ DEBUG(0, ("ldap_config_load: no description field in %s object\n", LDAP_OBJ_SAMBA_CONFIG));
+ }
+
+ if (result) ldap_msgfree(result);
+/* TODO: finish up the last section, see loadparm's lp_load()*/
+
+ /* retrive the section list */
+ pstr_sprintf(filter, "objectClass=%s",
+ LDAP_OBJ_SAMBA_SHARE);
+
+ DEBUG(0, ("Searching for:[%s]\n", filter));
+
+ rc = smbldap_search(ldap_state.smbldap_state,
+ config_dn, LDAP_SCOPE_SUBTREE,
+ filter, share_attr_list, 0, &result);
+
+ if (rc != LDAP_SUCCESS) {
+ DEBUG(0,("ldap_config_load: %s object not found\n", LDAP_OBJ_SAMBA_CONFIG));
+ goto done;
+ }
+
+ count = ldap_count_entries(ldap_state.smbldap_state->ldap_struct, result);
+ DEBUG(0, ("config_ldap: Found %d shares\n", count));
+ if (count) {
+ int i;
+
+ share_dn = talloc(mem_ctx, (count + 1) * sizeof(char *));
+ share_name = talloc(mem_ctx, (count) * sizeof(char *));
+ if (!share_dn || !share_name) {
+ DEBUG(0,("config_ldap: Out of memory!\n"));
+ goto done;
+ }
+ entry = ldap_first_entry(ldap_state.smbldap_state->ldap_struct, result);
+ i = 0;
+ while (entry) {
+ if (!(temp = smbldap_get_dn(ldap_state.smbldap_state->ldap_struct, entry))) {
+ goto done;
+ }
+ if (!smbldap_get_single_pstring(ldap_state.smbldap_state->ldap_struct, entry, "sambaShareName", attr_text)) {
+ goto done;
+ }
+ share_dn[i] = talloc_strdup(mem_ctx, temp);
+ share_name[i] = talloc_strdup(mem_ctx, attr_text);
+ if (!share_dn[i] || !share_name[i]) {
+ DEBUG(0,("config_ldap: Out of memory!\n"));
+ goto done;
+ }
+
+ DEBUG(0, ("config_ldap: Found share [%s] (%s)\n", attr_text, temp));
+ SAFE_FREE(temp);
+
+ entry = ldap_next_entry(ldap_state.smbldap_state->ldap_struct, entry);
+ i++;
+ if (entry && (count == i)) {
+ DEBUG(0, ("Error too many entryes in ldap result\n"));
+ goto done;
+ }
+ }
+ share_dn[i] = NULL;
+ }
+
+ /* parse global section*/
+ if (!sfunc("global")) {
+ goto done;
+ }
+ if (!NT_STATUS_IS_OK(parse_section(config_dn, pfunc))) {
+ goto done;
+ } else { /* parse shares */
+ int i;
+
+ for (i = 0; share_dn[i] != NULL; i++) {
+ if (!sfunc(share_name[i])) {
+ goto done;
+ }
+ if (!NT_STATUS_IS_OK(parse_section(share_dn[i], pfunc))) {
+ goto done;
+ }
+ }
+ }
+
+done:
+ talloc_destroy(mem_ctx);
+ if (result) ldap_msgfree(result);
+
+ return ret;
+}
+
+/*****************************************************************************
+ Initialise config_ldap module
+*****************************************************************************/
+
+static NTSTATUS ldap_config_init(char *params)
+{
+ NTSTATUS nt_status;
+ const char *location;
+ const char *basedn;
+
+ ldap_state.mem_ctx = talloc_init("config_ldap");
+ if (!ldap_state.mem_ctx) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ /* we assume only location is passed through an inline parameter
+ * other options go via parametrical options */
+ if (params) {
+ location = params;
+ } else {
+ location = lp_parm_const_string(GLOBAL_SECTION_SNUM, "config_ldap", "url", "ldap://localhost");
+ }
+ DEBUG(0,("config_ldap: location=%s\n", location));
+ basedn = lp_parm_const_string(GLOBAL_SECTION_SNUM, "config_ldap", "basedn", NULL);
+ if (basedn) config_base_dn = smb_xstrdup(basedn);
+
+ if (!NT_STATUS_IS_OK(nt_status =
+ smbldap_init(ldap_state.mem_ctx, location,
+ &ldap_state.smbldap_state))) {
+ talloc_destroy(ldap_state.mem_ctx);
+ DEBUG(0,("config_ldap: smbldap_init failed!\n"));
+ return nt_status;
+ }
+
+ return NT_STATUS_OK;
+}
+
+/*****************************************************************************
+ End the LDAP session
+*****************************************************************************/
+
+static NTSTATUS ldap_config_close(void)
+{
+
+ smbldap_free_struct(&(ldap_state).smbldap_state);
+ talloc_destroy(ldap_state.mem_ctx);
+
+ DEBUG(5,("The connection to the LDAP server was closed\n"));
+ /* maybe free the results here --metze */
+
+ return NT_STATUS_OK;
+}
+
+static struct config_functions functions = {
+ ldap_config_init,
+ ldap_config_load,
+ ldap_config_close
+};
+
+NTSTATUS config_ldap_init(void)
+{
+ return smb_register_config(SAMBA_CONFIG_INTERFACE_VERSION, "ldap", &functions);
+}
diff --git a/source/param/loadparm.c b/source/param/loadparm.c
new file mode 100644
index 00000000000..b92fa64ee0c
--- /dev/null
+++ b/source/param/loadparm.c
@@ -0,0 +1,4277 @@
+/*
+ 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
+
+ This program is free software; you can 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.
+*/
+
+/*
+ * 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"
+
+BOOL in_client = False; /* Not in the client by default */
+BOOL bLoaded = False;
+
+extern userdom_struct current_user_info;
+extern pstring user_socket_options;
+
+#ifndef GLOBAL_NAME
+#define GLOBAL_NAME "global"
+#endif
+
+#ifndef PRINTERS_NAME
+#define PRINTERS_NAME "printers"
+#endif
+
+#ifndef HOMES_NAME
+#define HOMES_NAME "homes"
+#endif
+
+/* some helpful bits */
+#define LP_SNUM_OK(i) (((i) >= 0) && ((i) < iNumServices) && ServicePtrs[(i)]->valid)
+#define VALID(i) ServicePtrs[i]->valid
+
+int keepalive = DEFAULT_KEEPALIVE;
+BOOL use_getwd_cache = True;
+
+extern int extra_time_offset;
+
+static BOOL defaults_saved = False;
+
+typedef struct _param_opt_struct param_opt_struct;
+struct _param_opt_struct {
+ param_opt_struct *prev, *next;
+ char *key;
+ char *value;
+ char **list;
+};
+
+/*
+ * This structure describes global (ie., server-wide) parameters.
+ */
+typedef struct
+{
+ char *szConfigBackend;
+ char *smb_ports;
+ char *dos_charset;
+ char *unix_charset;
+ char *display_charset;
+ char *szPrintcapname;
+ char *szEnumPortsCommand;
+ char *szAddPrinterCommand;
+ char *szDeletePrinterCommand;
+ char *szOs2DriverMap;
+ char *szLockDir;
+ char *szPidDir;
+ char *szRootdir;
+ char *szDefaultService;
+ char *szDfree;
+ char *szGetQuota;
+ char *szSetQuota;
+ char *szMsgCommand;
+ char *szHostsEquiv;
+ char *szServerString;
+ char *szAutoServices;
+ char *szPasswdProgram;
+ char *szPasswdChat;
+ char *szLogFile;
+ char *szConfigFile;
+ char *szSMBPasswdFile;
+ char *szPrivateDir;
+ char **szPassdbBackend;
+ char *szGumsBackend;
+ char **szPreloadModules;
+ char *szPasswordServer;
+ char *szSocketOptions;
+ char *szRealm;
+ char *szAfsUsernameMap;
+ char *szUsernameMap;
+ char *szLogonScript;
+ char *szLogonPath;
+ char *szLogonDrive;
+ char *szLogonHome;
+ char **szWINSservers;
+ char **szInterfaces;
+ char *szRemoteAnnounce;
+ char *szRemoteBrowseSync;
+ char *szSocketAddress;
+ char *szNISHomeMapName;
+ char *szAnnounceVersion; /* This is initialised in init_globals */
+ char *szWorkgroup;
+ char *szNetbiosName;
+ char **szNetbiosAliases;
+ char *szNetbiosScope;
+ char *szDomainOtherSIDs;
+ char *szNameResolveOrder;
+ char *szPanicAction;
+ char *szAddUserScript;
+ char *szDelUserScript;
+ char *szAddGroupScript;
+ char *szDelGroupScript;
+ char *szAddUserToGroupScript;
+ char *szDelUserFromGroupScript;
+ char *szSetPrimaryGroupScript;
+ char *szAddMachineScript;
+ char *szShutdownScript;
+ char *szAbortShutdownScript;
+ char *szWINSHook;
+ char *szWINSPartners;
+ char *szUtmpDir;
+ char *szWtmpDir;
+ BOOL bUtmp;
+ char *szIdmapUID;
+ char *szIdmapGID;
+ BOOL bEnableRidAlgorithm;
+ int AlgorithmicRidBase;
+ char *szTemplatePrimaryGroup;
+ char *szTemplateHomedir;
+ char *szTemplateShell;
+ char *szWinbindSeparator;
+ BOOL bWinbindEnableLocalAccounts;
+ BOOL bWinbindEnumUsers;
+ BOOL bWinbindEnumGroups;
+ BOOL bWinbindUseDefaultDomain;
+ BOOL bWinbindTrustedDomainsOnly;
+ char *szWinbindBackend;
+ char *szIdmapBackend;
+ char *szAddShareCommand;
+ char *szChangeShareCommand;
+ char *szDeleteShareCommand;
+ char *szGuestaccount;
+ char *szManglingMethod;
+ int mangle_prefix;
+ int max_log_size;
+ char *szLogLevel;
+ int max_xmit;
+ int max_mux;
+ int max_open_files;
+ int pwordlevel;
+ int unamelevel;
+ int deadtime;
+ int maxprotocol;
+ int minprotocol;
+ int security;
+ char **AuthMethods;
+ BOOL paranoid_server_security;
+ int maxdisksize;
+ int lpqcachetime;
+ int iMaxSmbdProcesses;
+ BOOL bDisableSpoolss;
+ int iTotalPrintJobs;
+ int syslog;
+ int os_level;
+ int enhanced_browsing;
+ int max_ttl;
+ int max_wins_ttl;
+ int min_wins_ttl;
+ int lm_announce;
+ int lm_interval;
+ int announce_as; /* This is initialised in init_globals */
+ int machine_password_timeout;
+ int change_notify_timeout;
+ int map_to_guest;
+ int min_passwd_length;
+ BOOL use_cracklib;
+ int oplock_break_wait_time;
+ int winbind_cache_time;
+ int iLockSpinCount;
+ int iLockSpinTime;
+ char *szLdapMachineSuffix;
+ char *szLdapUserSuffix;
+ char *szLdapIdmapSuffix;
+ char *szLdapGroupSuffix;
+ char *szLdapPrivilegeSuffix;
+#ifdef WITH_LDAP_SAMCONFIG
+ int ldap_port;
+ char *szLdapServer;
+#endif
+ int ldap_ssl;
+ char *szLdapSuffix;
+ char *szLdapFilter;
+ char *szLdapAdminDn;
+ char *szAclCompat;
+ int ldap_passwd_sync;
+ int ldap_replication_sleep;
+ BOOL ldap_delete_dn;
+ BOOL bMsAddPrinterWizard;
+ BOOL bDNSproxy;
+ BOOL bWINSsupport;
+ BOOL bWINSproxy;
+ BOOL bLocalMaster;
+ BOOL bPreferredMaster;
+ BOOL bDomainMaster;
+ BOOL bDomainLogons;
+ BOOL bEncryptPasswords;
+ BOOL bUpdateEncrypt;
+ int clientSchannel;
+ int serverSchannel;
+ BOOL bNullPasswords;
+ BOOL bObeyPamRestrictions;
+ BOOL bLoadPrinters;
+ BOOL bLargeReadwrite;
+ BOOL bReadRaw;
+ BOOL bWriteRaw;
+ BOOL bReadbmpx;
+ BOOL bSyslogOnly;
+ BOOL bBrowseList;
+ BOOL bNISHomeMap;
+ BOOL bTimeServer;
+ BOOL bBindInterfacesOnly;
+ BOOL bPamPasswordChange;
+ BOOL bUnixPasswdSync;
+ BOOL bPasswdChatDebug;
+ int iPasswdChatTimeout;
+ BOOL bTimestampLogs;
+ BOOL bNTSmbSupport;
+ BOOL bNTPipeSupport;
+ BOOL bNTStatusSupport;
+ BOOL bStatCache;
+ BOOL bKernelOplocks;
+ BOOL bAllowTrustedDomains;
+ BOOL bLanmanAuth;
+ BOOL bNTLMAuth;
+ BOOL bUseSpnego;
+ BOOL bClientLanManAuth;
+ BOOL bClientNTLMv2Auth;
+ BOOL bClientPlaintextAuth;
+ BOOL bClientUseSpnego;
+ BOOL bDebugHiresTimestamp;
+ BOOL bDebugPid;
+ BOOL bDebugUid;
+ BOOL bHostMSDfs;
+ BOOL bUnicode;
+ BOOL bUseMmap;
+ BOOL bHostnameLookups;
+ BOOL bUnixExtensions;
+ BOOL bDisableNetbios;
+ BOOL bKernelChangeNotify;
+ int restrict_anonymous;
+ int name_cache_timeout;
+ int client_signing;
+ int server_signing;
+ param_opt_struct *param_opt;
+}
+global;
+
+static global Globals;
+
+/*
+ * This structure describes a single service.
+ */
+typedef struct
+{
+ BOOL valid;
+ BOOL autoloaded;
+ char *szService;
+ char *szPath;
+ char *szUsername;
+ char **szInvalidUsers;
+ char **szValidUsers;
+ char **szAdminUsers;
+ char *szCopy;
+ char *szInclude;
+ char *szPreExec;
+ char *szPostExec;
+ char *szRootPreExec;
+ char *szRootPostExec;
+ char *szPrintcommand;
+ char *szLpqcommand;
+ char *szLprmcommand;
+ char *szLppausecommand;
+ char *szLpresumecommand;
+ char *szQueuepausecommand;
+ char *szQueueresumecommand;
+ char *szPrintername;
+ char *szDontdescend;
+ char **szHostsallow;
+ char **szHostsdeny;
+ char *szMagicScript;
+ char *szMagicOutput;
+ char *szMangledMap;
+ char *szVetoFiles;
+ char *szHideFiles;
+ char *szVetoOplockFiles;
+ char *comment;
+ char *force_user;
+ char *force_group;
+ char **readlist;
+ char **writelist;
+ char **printer_admin;
+ char *volume;
+ char *fstype;
+ char **szVfsObjects;
+ char *szMSDfsProxy;
+ int iMinPrintSpace;
+ int iMaxPrintJobs;
+ int iMaxReportedPrintJobs;
+ int iWriteCacheSize;
+ int iCreate_mask;
+ int iCreate_force_mode;
+ int iSecurity_mask;
+ int iSecurity_force_mode;
+ int iDir_mask;
+ int iDir_force_mode;
+ int iDir_Security_mask;
+ int iDir_Security_force_mode;
+ int iMaxConnections;
+ int iDefaultCase;
+ int iPrinting;
+ int iOplockContentionLimit;
+ int iCSCPolicy;
+ int iBlock_size;
+ BOOL bPreexecClose;
+ BOOL bRootpreexecClose;
+ BOOL bCaseSensitive;
+ BOOL bCasePreserve;
+ BOOL bShortCasePreserve;
+ BOOL bCaseMangle;
+ BOOL bHideDotFiles;
+ BOOL bHideSpecialFiles;
+ BOOL bHideUnReadable;
+ BOOL bHideUnWriteableFiles;
+ BOOL bBrowseable;
+ BOOL bAvailable;
+ BOOL bRead_only;
+ BOOL bNo_set_dir;
+ BOOL bGuest_only;
+ BOOL bGuest_ok;
+ BOOL bPrint_ok;
+ BOOL bMap_system;
+ BOOL bMap_hidden;
+ BOOL bMap_archive;
+ BOOL bStoreDosAttributes;
+ BOOL bLocking;
+ BOOL bStrictLocking;
+ BOOL bPosixLocking;
+ BOOL bShareModes;
+ BOOL bOpLocks;
+ BOOL bLevel2OpLocks;
+ BOOL bOnlyUser;
+ BOOL bMangledNames;
+ BOOL bWidelinks;
+ BOOL bSymlinks;
+ BOOL bSyncAlways;
+ BOOL bStrictAllocate;
+ BOOL bStrictSync;
+ char magic_char;
+ BOOL *copymap;
+ BOOL bDeleteReadonly;
+ BOOL bFakeOplocks;
+ BOOL bDeleteVetoFiles;
+ BOOL bDosFilemode;
+ BOOL bDosFiletimes;
+ BOOL bDosFiletimeResolution;
+ BOOL bFakeDirCreateTimes;
+ BOOL bBlockingLocks;
+ BOOL bInheritPerms;
+ BOOL bInheritACLS;
+ BOOL bMSDfsRoot;
+ BOOL bUseClientDriver;
+ BOOL bDefaultDevmode;
+ BOOL bNTAclSupport;
+ BOOL bUseSendfile;
+ BOOL bProfileAcls;
+ BOOL bMap_acl_inherit;
+ BOOL bAfs_Share;
+ BOOL bEASupport;
+ param_opt_struct *param_opt;
+
+ char dummy[3]; /* for alignment */
+}
+service;
+
+
+/* This is a default service used to prime a services structure */
+static service sDefault = {
+ True, /* valid */
+ False, /* not autoloaded */
+ NULL, /* szService */
+ NULL, /* szPath */
+ NULL, /* szUsername */
+ NULL, /* szInvalidUsers */
+ NULL, /* szValidUsers */
+ NULL, /* szAdminUsers */
+ NULL, /* szCopy */
+ NULL, /* szInclude */
+ NULL, /* szPreExec */
+ NULL, /* szPostExec */
+ NULL, /* szRootPreExec */
+ NULL, /* szRootPostExec */
+ NULL, /* szPrintcommand */
+ NULL, /* szLpqcommand */
+ NULL, /* szLprmcommand */
+ NULL, /* szLppausecommand */
+ NULL, /* szLpresumecommand */
+ NULL, /* szQueuepausecommand */
+ NULL, /* szQueueresumecommand */
+ NULL, /* szPrintername */
+ NULL, /* szDontdescend */
+ NULL, /* szHostsallow */
+ NULL, /* szHostsdeny */
+ NULL, /* szMagicScript */
+ NULL, /* szMagicOutput */
+ NULL, /* szMangledMap */
+ NULL, /* szVetoFiles */
+ NULL, /* szHideFiles */
+ NULL, /* szVetoOplockFiles */
+ NULL, /* comment */
+ NULL, /* force user */
+ NULL, /* force group */
+ NULL, /* readlist */
+ NULL, /* writelist */
+ NULL, /* printer admin */
+ NULL, /* volume */
+ NULL, /* fstype */
+ NULL, /* vfs objects */
+ NULL, /* szMSDfsProxy */
+ 0, /* iMinPrintSpace */
+ 1000, /* iMaxPrintJobs */
+ 0, /* iMaxReportedPrintJobs */
+ 0, /* iWriteCacheSize */
+ 0744, /* iCreate_mask */
+ 0000, /* iCreate_force_mode */
+ 0777, /* iSecurity_mask */
+ 0, /* iSecurity_force_mode */
+ 0755, /* iDir_mask */
+ 0000, /* iDir_force_mode */
+ 0777, /* iDir_Security_mask */
+ 0, /* iDir_Security_force_mode */
+ 0, /* iMaxConnections */
+ CASE_LOWER, /* iDefaultCase */
+ DEFAULT_PRINTING, /* iPrinting */
+ 2, /* iOplockContentionLimit */
+ 0, /* iCSCPolicy */
+ 1024, /* iBlock_size */
+ False, /* bPreexecClose */
+ False, /* bRootpreexecClose */
+ False, /* case sensitive */
+ True, /* case preserve */
+ True, /* short case preserve */
+ False, /* case mangle */
+ True, /* bHideDotFiles */
+ False, /* bHideSpecialFiles */
+ False, /* bHideUnReadable */
+ False, /* bHideUnWriteableFiles */
+ True, /* bBrowseable */
+ True, /* bAvailable */
+ True, /* bRead_only */
+ True, /* bNo_set_dir */
+ False, /* bGuest_only */
+ False, /* bGuest_ok */
+ False, /* bPrint_ok */
+ False, /* bMap_system */
+ False, /* bMap_hidden */
+ True, /* bMap_archive */
+ False, /* bStoreDosAttributes */
+ True, /* bLocking */
+ True, /* bStrictLocking */
+ True, /* bPosixLocking */
+ True, /* bShareModes */
+ True, /* bOpLocks */
+ True, /* bLevel2OpLocks */
+ False, /* bOnlyUser */
+ True, /* bMangledNames */
+ True, /* bWidelinks */
+ True, /* bSymlinks */
+ False, /* bSyncAlways */
+ False, /* bStrictAllocate */
+ False, /* bStrictSync */
+ '~', /* magic char */
+ NULL, /* copymap */
+ False, /* bDeleteReadonly */
+ False, /* bFakeOplocks */
+ False, /* bDeleteVetoFiles */
+ False, /* bDosFilemode */
+ False, /* bDosFiletimes */
+ False, /* bDosFiletimeResolution */
+ False, /* bFakeDirCreateTimes */
+ True, /* bBlockingLocks */
+ False, /* bInheritPerms */
+ False, /* bInheritACLS */
+ False, /* bMSDfsRoot */
+ False, /* bUseClientDriver */
+ False, /* bDefaultDevmode */
+ True, /* bNTAclSupport */
+ False, /* bUseSendfile */
+ False, /* bProfileAcls */
+ False, /* bMap_acl_inherit */
+ False, /* bAfs_Share */
+ False, /* bEASupport */
+
+ NULL, /* Parametric options */
+
+ "" /* dummy */
+};
+
+/* local variables */
+static service **ServicePtrs = NULL;
+static int iNumServices = 0;
+static int iServiceIndex = 0;
+static BOOL bInGlobalSection = True;
+static BOOL bGlobalOnly = False;
+static int server_role;
+static int default_server_announce;
+
+#define NUMPARAMETERS (sizeof(parm_table) / sizeof(struct parm_struct))
+
+/* prototypes for the special type handlers */
+static BOOL handle_include(const char *pszParmValue, char **ptr);
+static BOOL handle_copy(const char *pszParmValue, char **ptr);
+static BOOL handle_netbios_name(const char *pszParmValue, char **ptr);
+static BOOL handle_idmap_uid(const char *pszParmValue, char **ptr);
+static BOOL handle_idmap_gid(const char *pszParmValue, char **ptr);
+static BOOL handle_debug_list( const char *pszParmValue, char **ptr );
+static BOOL handle_workgroup( const char *pszParmValue, char **ptr );
+static BOOL handle_netbios_aliases( const char *pszParmValue, char **ptr );
+static BOOL handle_netbios_scope( const char *pszParmValue, char **ptr );
+static BOOL handle_charset( const char *pszParmValue, char **ptr );
+
+static BOOL handle_acl_compatibility(const char *pszParmValue, char **ptr);
+
+static void set_server_role(void);
+static void set_default_server_announce_type(void);
+static void set_allowed_client_auth(void);
+
+static const struct enum_list enum_protocol[] = {
+ {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"},
+ {SEC_SERVER, "SERVER"},
+ {SEC_DOMAIN, "DOMAIN"},
+#ifdef HAVE_ADS
+ {SEC_ADS, "ADS"},
+#endif
+ {-1, NULL}
+};
+
+static const struct enum_list enum_printing[] = {
+ {PRINT_SYSV, "sysv"},
+ {PRINT_AIX, "aix"},
+ {PRINT_HPUX, "hpux"},
+ {PRINT_BSD, "bsd"},
+ {PRINT_QNX, "qnx"},
+ {PRINT_PLP, "plp"},
+ {PRINT_LPRNG, "lprng"},
+ {PRINT_CUPS, "cups"},
+ {PRINT_LPRNT, "nt"},
+ {PRINT_LPROS2, "os2"},
+#ifdef DEVELOPER
+ {PRINT_TEST, "test"},
+ {PRINT_VLP, "vlp"},
+#endif /* DEVELOPER */
+ {-1, NULL}
+};
+
+static const struct enum_list enum_ldap_ssl[] = {
+#ifdef WITH_LDAP_SAMCONFIG
+ {LDAP_SSL_ON, "Yes"},
+ {LDAP_SSL_ON, "yes"},
+ {LDAP_SSL_ON, "on"},
+ {LDAP_SSL_ON, "On"},
+#endif
+ {LDAP_SSL_OFF, "no"},
+ {LDAP_SSL_OFF, "No"},
+ {LDAP_SSL_OFF, "off"},
+ {LDAP_SSL_OFF, "Off"},
+ {LDAP_SSL_START_TLS, "start tls"},
+ {LDAP_SSL_START_TLS, "Start_tls"},
+ {-1, NULL}
+};
+
+static const struct enum_list enum_ldap_passwd_sync[] = {
+ {LDAP_PASSWD_SYNC_OFF, "no"},
+ {LDAP_PASSWD_SYNC_OFF, "No"},
+ {LDAP_PASSWD_SYNC_OFF, "off"},
+ {LDAP_PASSWD_SYNC_OFF, "Off"},
+ {LDAP_PASSWD_SYNC_ON, "Yes"},
+ {LDAP_PASSWD_SYNC_ON, "yes"},
+ {LDAP_PASSWD_SYNC_ON, "on"},
+ {LDAP_PASSWD_SYNC_ON, "On"},
+ {LDAP_PASSWD_SYNC_ONLY, "Only"},
+ {LDAP_PASSWD_SYNC_ONLY, "only"},
+ {-1, NULL}
+};
+
+/* Types of machine we can announce as. */
+#define ANNOUNCE_AS_NT_SERVER 1
+#define ANNOUNCE_AS_WIN95 2
+#define ANNOUNCE_AS_WFW 3
+#define ANNOUNCE_AS_NT_WORKSTATION 4
+
+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_case[] = {
+ {CASE_LOWER, "lower"},
+ {CASE_UPPER, "upper"},
+ {-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 */
+#define CSC_POLICY_MANUAL 0
+#define CSC_POLICY_DOCUMENTS 1
+#define CSC_POLICY_PROGRAMS 2
+#define 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[] = {
+ {False, "No"},
+ {False, "False"},
+ {False, "0"},
+ {False, "Off"},
+ {False, "disabled"},
+ {True, "Yes"},
+ {True, "True"},
+ {True, "1"},
+ {True, "On"},
+ {True, "enabled"},
+ {Auto, "auto"},
+ {Required, "required"},
+ {Required, "mandatory"},
+ {Required, "force"},
+ {Required, "forced"},
+ {Required, "enforced"},
+ {-1, NULL}
+};
+
+
+/*
+ Do you want session setups at user level security with a invalid
+ password to be rejected or allowed in as guest? WinNT rejects them
+ but it can be a pain as it means "net view" needs to use a password
+
+ You have 3 choices in the setting of map_to_guest:
+
+ "Never" means session setups with an invalid password
+ are rejected. This is the default.
+
+ "Bad User" means session setups with an invalid password
+ are rejected, unless the username does not exist, in which case it
+ is treated as a guest login
+
+ "Bad Password" means session setups with an invalid password
+ are treated as a guest login
+
+ Note that map_to_guest only has an effect in user or server
+ level security.
+*/
+
+static const struct enum_list enum_map_to_guest[] = {
+ {NEVER_MAP_TO_GUEST, "Never"},
+ {MAP_TO_GUEST_ON_BAD_USER, "Bad User"},
+ {MAP_TO_GUEST_ON_BAD_PASSWORD, "Bad Password"},
+ {-1, NULL}
+};
+
+/* Note: We do not initialise the defaults union - it is not allowed in ANSI C
+ *
+ * The FLAG_HIDE is explicit. Paramters set this way do NOT appear in any edit
+ * screen in SWAT. This is used to exclude parameters as well as to squash all
+ * parameters that have been duplicated by pseudonyms.
+ *
+ * NOTE: To display a parameter in BASIC view set FLAG_BASIC
+ * Any parameter that does NOT have FLAG_ADVANCED will not disply at all
+ * Set FLAG_SHARE and FLAG_PRINT to specifically display parameters in
+ * respective views.
+ *
+ * NOTE2: Handling of duplicated (synonym) paramters:
+ * Only the first occurance of a parameter should be enabled by FLAG_BASIC
+ * and/or FLAG_ADVANCED. All duplicates following the first mention should be
+ * set to FLAG_HIDE. ie: Make you must place the parameter that has the preferred
+ * name first, and all synonyms must follow it with the FLAG_HIDE attribute.
+ */
+
+static struct parm_struct parm_table[] = {
+ {N_("Base Options"), P_SEP, P_SEPARATOR},
+
+ {"config backend", P_STRING, P_GLOBAL, &Globals.szConfigBackend, NULL, NULL, FLAG_ADVANCED},
+ {"dos charset", P_STRING, P_GLOBAL, &Globals.dos_charset, handle_charset, NULL, FLAG_ADVANCED},
+ {"unix charset", P_STRING, P_GLOBAL, &Globals.unix_charset, handle_charset, NULL, FLAG_ADVANCED},
+ {"display charset", P_STRING, P_GLOBAL, &Globals.display_charset, handle_charset, NULL, FLAG_ADVANCED},
+ {"comment", P_STRING, P_LOCAL, &sDefault.comment, NULL, NULL, FLAG_BASIC | FLAG_ADVANCED | FLAG_SHARE | FLAG_PRINT},
+ {"path", P_STRING, P_LOCAL, &sDefault.szPath, NULL, NULL, FLAG_BASIC | FLAG_ADVANCED | FLAG_SHARE | FLAG_PRINT},
+ {"directory", P_STRING, P_LOCAL, &sDefault.szPath, NULL, NULL, FLAG_HIDE},
+ {"workgroup", P_USTRING, P_GLOBAL, &Globals.szWorkgroup, handle_workgroup, NULL, FLAG_BASIC | FLAG_ADVANCED | FLAG_WIZARD},
+#ifdef WITH_ADS
+ {"realm", P_USTRING, P_GLOBAL, &Globals.szRealm, NULL, NULL, FLAG_BASIC | FLAG_ADVANCED | FLAG_WIZARD},
+#endif
+ {"netbios name", P_USTRING, P_GLOBAL, &Globals.szNetbiosName, handle_netbios_name, NULL, FLAG_BASIC | FLAG_ADVANCED | FLAG_WIZARD},
+ {"netbios aliases", P_LIST, P_GLOBAL, &Globals.szNetbiosAliases, handle_netbios_aliases, NULL, FLAG_ADVANCED},
+ {"netbios scope", P_USTRING, P_GLOBAL, &Globals.szNetbiosScope, handle_netbios_scope, NULL, FLAG_ADVANCED},
+ {"server string", P_STRING, P_GLOBAL, &Globals.szServerString, NULL, NULL, FLAG_BASIC | FLAG_ADVANCED },
+ {"interfaces", P_LIST, P_GLOBAL, &Globals.szInterfaces, NULL, NULL, FLAG_BASIC | FLAG_ADVANCED | FLAG_WIZARD},
+ {"bind interfaces only", P_BOOL, P_GLOBAL, &Globals.bBindInterfacesOnly, NULL, NULL, FLAG_ADVANCED | FLAG_WIZARD},
+
+ {N_("Security Options"), P_SEP, P_SEPARATOR},
+
+ {"security", P_ENUM, P_GLOBAL, &Globals.security, NULL, enum_security, FLAG_BASIC | FLAG_ADVANCED | FLAG_WIZARD},
+ {"auth methods", P_LIST, P_GLOBAL, &Globals.AuthMethods, NULL, NULL, FLAG_ADVANCED},
+ {"encrypt passwords", P_BOOL, P_GLOBAL, &Globals.bEncryptPasswords, NULL, NULL, FLAG_BASIC | FLAG_ADVANCED | FLAG_WIZARD},
+ {"update encrypted", P_BOOL, P_GLOBAL, &Globals.bUpdateEncrypt, NULL, NULL, FLAG_ADVANCED},
+ {"client schannel", P_ENUM, P_GLOBAL, &Globals.clientSchannel, NULL, enum_bool_auto, FLAG_BASIC | FLAG_ADVANCED},
+ {"server schannel", P_ENUM, P_GLOBAL, &Globals.serverSchannel, NULL, enum_bool_auto, FLAG_BASIC | FLAG_ADVANCED},
+ {"allow trusted domains", P_BOOL, P_GLOBAL, &Globals.bAllowTrustedDomains, NULL, NULL, FLAG_ADVANCED},
+ {"hosts equiv", P_STRING, P_GLOBAL, &Globals.szHostsEquiv, NULL, NULL, FLAG_ADVANCED},
+ {"min passwd length", P_INTEGER, P_GLOBAL, &Globals.min_passwd_length, NULL, NULL, FLAG_ADVANCED},
+ {"min password length", P_INTEGER, P_GLOBAL, &Globals.min_passwd_length, NULL, NULL, FLAG_ADVANCED},
+ {"use cracklib", P_BOOL, P_GLOBAL, &Globals.use_cracklib, NULL, NULL, FLAG_ADVANCED},
+ {"map to guest", P_ENUM, P_GLOBAL, &Globals.map_to_guest, NULL, enum_map_to_guest, FLAG_ADVANCED},
+ {"null passwords", P_BOOL, P_GLOBAL, &Globals.bNullPasswords, NULL, NULL, FLAG_ADVANCED},
+ {"obey pam restrictions", P_BOOL, P_GLOBAL, &Globals.bObeyPamRestrictions, NULL, NULL, FLAG_ADVANCED},
+ {"password server", P_STRING, P_GLOBAL, &Globals.szPasswordServer, NULL, NULL, FLAG_ADVANCED | FLAG_WIZARD},
+ {"smb passwd file", P_STRING, P_GLOBAL, &Globals.szSMBPasswdFile, NULL, NULL, FLAG_ADVANCED},
+ {"private dir", P_STRING, P_GLOBAL, &Globals.szPrivateDir, NULL, NULL, FLAG_ADVANCED},
+ {"passdb backend", P_LIST, P_GLOBAL, &Globals.szPassdbBackend, NULL, NULL, FLAG_ADVANCED | FLAG_WIZARD},
+ {"gums backend", P_STRING, P_GLOBAL, &Globals.szGumsBackend, NULL, NULL, FLAG_ADVANCED | FLAG_WIZARD},
+ {"algorithmic rid base", P_INTEGER, P_GLOBAL, &Globals.AlgorithmicRidBase, NULL, NULL, FLAG_ADVANCED},
+ {"root directory", P_STRING, P_GLOBAL, &Globals.szRootdir, NULL, NULL, FLAG_ADVANCED},
+ {"root dir", P_STRING, P_GLOBAL, &Globals.szRootdir, NULL, NULL, FLAG_HIDE},
+ {"root", P_STRING, P_GLOBAL, &Globals.szRootdir, NULL, NULL, FLAG_HIDE},
+ {"guest account", P_STRING, P_GLOBAL, &Globals.szGuestaccount, NULL, NULL, FLAG_BASIC | FLAG_ADVANCED},
+
+ {"pam password change", P_BOOL, P_GLOBAL, &Globals.bPamPasswordChange, NULL, NULL, FLAG_ADVANCED},
+ {"passwd program", P_STRING, P_GLOBAL, &Globals.szPasswdProgram, NULL, NULL, FLAG_ADVANCED},
+ {"passwd chat", P_STRING, P_GLOBAL, &Globals.szPasswdChat, NULL, NULL, FLAG_ADVANCED},
+ {"passwd chat debug", P_BOOL, P_GLOBAL, &Globals.bPasswdChatDebug, NULL, NULL, FLAG_ADVANCED},
+ {"passwd chat timeout", P_INTEGER, P_GLOBAL, &Globals.iPasswdChatTimeout, NULL, NULL, FLAG_ADVANCED},
+ {"username map", P_STRING, P_GLOBAL, &Globals.szUsernameMap, NULL, NULL, FLAG_ADVANCED},
+ {"password level", P_INTEGER, P_GLOBAL, &Globals.pwordlevel, NULL, NULL, FLAG_ADVANCED},
+ {"username level", P_INTEGER, P_GLOBAL, &Globals.unamelevel, NULL, NULL, FLAG_ADVANCED},
+ {"unix password sync", P_BOOL, P_GLOBAL, &Globals.bUnixPasswdSync, NULL, NULL, FLAG_ADVANCED},
+ {"restrict anonymous", P_INTEGER, P_GLOBAL, &Globals.restrict_anonymous, NULL, NULL, FLAG_ADVANCED},
+ {"lanman auth", P_BOOL, P_GLOBAL, &Globals.bLanmanAuth, NULL, NULL, FLAG_ADVANCED},
+ {"ntlm auth", P_BOOL, P_GLOBAL, &Globals.bNTLMAuth, NULL, NULL, FLAG_ADVANCED},
+ {"client NTLMv2 auth", P_BOOL, P_GLOBAL, &Globals.bClientNTLMv2Auth, NULL, NULL, FLAG_ADVANCED},
+ {"client lanman auth", P_BOOL, P_GLOBAL, &Globals.bClientLanManAuth, NULL, NULL, FLAG_ADVANCED},
+ {"client plaintext auth", P_BOOL, P_GLOBAL, &Globals.bClientPlaintextAuth, NULL, NULL, FLAG_ADVANCED},
+
+ {"username", P_STRING, P_LOCAL, &sDefault.szUsername, NULL, NULL, FLAG_ADVANCED | FLAG_GLOBAL | FLAG_SHARE},
+ {"user", P_STRING, P_LOCAL, &sDefault.szUsername, NULL, NULL, FLAG_HIDE},
+ {"users", P_STRING, P_LOCAL, &sDefault.szUsername, NULL, NULL, FLAG_HIDE},
+
+ {"invalid users", P_LIST, P_LOCAL, &sDefault.szInvalidUsers, NULL, NULL, FLAG_ADVANCED | FLAG_GLOBAL | FLAG_SHARE},
+ {"valid users", P_LIST, P_LOCAL, &sDefault.szValidUsers, NULL, NULL, FLAG_ADVANCED | FLAG_GLOBAL | FLAG_SHARE},
+ {"admin users", P_LIST, P_LOCAL, &sDefault.szAdminUsers, NULL, NULL, FLAG_ADVANCED | FLAG_GLOBAL | FLAG_SHARE},
+ {"read list", P_LIST, P_LOCAL, &sDefault.readlist, NULL, NULL, FLAG_ADVANCED | FLAG_GLOBAL | FLAG_SHARE},
+ {"write list", P_LIST, P_LOCAL, &sDefault.writelist, NULL, NULL, FLAG_ADVANCED | FLAG_GLOBAL | FLAG_SHARE},
+ {"printer admin", P_LIST, P_LOCAL, &sDefault.printer_admin, NULL, NULL, FLAG_ADVANCED | FLAG_GLOBAL | FLAG_PRINT},
+ {"force user", P_STRING, P_LOCAL, &sDefault.force_user, NULL, NULL, FLAG_ADVANCED | FLAG_SHARE},
+ {"force group", P_STRING, P_LOCAL, &sDefault.force_group, NULL, NULL, FLAG_ADVANCED | FLAG_SHARE},
+ {"group", P_STRING, P_LOCAL, &sDefault.force_group, NULL, NULL, FLAG_ADVANCED},
+
+ {"read only", P_BOOL, P_LOCAL, &sDefault.bRead_only, NULL, NULL, FLAG_BASIC | FLAG_ADVANCED | FLAG_SHARE},
+ {"write ok", P_BOOLREV, P_LOCAL, &sDefault.bRead_only, NULL, NULL, FLAG_HIDE},
+ {"writeable", P_BOOLREV, P_LOCAL, &sDefault.bRead_only, NULL, NULL, FLAG_HIDE},
+ {"writable", P_BOOLREV, P_LOCAL, &sDefault.bRead_only, NULL, NULL, FLAG_HIDE},
+
+ {"create mask", P_OCTAL, P_LOCAL, &sDefault.iCreate_mask, NULL, NULL, FLAG_ADVANCED | FLAG_GLOBAL | FLAG_SHARE},
+ {"create mode", P_OCTAL, P_LOCAL, &sDefault.iCreate_mask, NULL, NULL, FLAG_HIDE},
+ {"force create mode", P_OCTAL, P_LOCAL, &sDefault.iCreate_force_mode, NULL, NULL, FLAG_ADVANCED | FLAG_GLOBAL | FLAG_SHARE},
+ {"security mask", P_OCTAL, P_LOCAL, &sDefault.iSecurity_mask, NULL, NULL, FLAG_ADVANCED | FLAG_GLOBAL | FLAG_SHARE},
+ {"force security mode", P_OCTAL, P_LOCAL, &sDefault.iSecurity_force_mode, NULL, NULL, FLAG_ADVANCED | FLAG_GLOBAL | FLAG_SHARE},
+ {"directory mask", P_OCTAL, P_LOCAL, &sDefault.iDir_mask, NULL, NULL, FLAG_ADVANCED | FLAG_GLOBAL | FLAG_SHARE},
+ {"directory mode", P_OCTAL, P_LOCAL, &sDefault.iDir_mask, NULL, NULL, FLAG_ADVANCED | FLAG_GLOBAL},
+ {"force directory mode", P_OCTAL, P_LOCAL, &sDefault.iDir_force_mode, NULL, NULL, FLAG_ADVANCED | FLAG_GLOBAL | FLAG_SHARE},
+ {"directory security mask", P_OCTAL, P_LOCAL, &sDefault.iDir_Security_mask, NULL, NULL, FLAG_ADVANCED | FLAG_GLOBAL | FLAG_SHARE},
+ {"force directory security mode", P_OCTAL, P_LOCAL, &sDefault.iDir_Security_force_mode, NULL, NULL, FLAG_ADVANCED | FLAG_GLOBAL | FLAG_SHARE},
+ {"inherit permissions", P_BOOL, P_LOCAL, &sDefault.bInheritPerms, NULL, NULL, FLAG_ADVANCED | FLAG_SHARE},
+ {"inherit acls", P_BOOL, P_LOCAL, &sDefault.bInheritACLS, NULL, NULL, FLAG_ADVANCED | FLAG_SHARE},
+ {"guest only", P_BOOL, P_LOCAL, &sDefault.bGuest_only, NULL, NULL, FLAG_ADVANCED | FLAG_SHARE},
+ {"only guest", P_BOOL, P_LOCAL, &sDefault.bGuest_only, NULL, NULL, FLAG_HIDE},
+
+ {"guest ok", P_BOOL, P_LOCAL, &sDefault.bGuest_ok, NULL, NULL, FLAG_BASIC | FLAG_ADVANCED | FLAG_SHARE | FLAG_PRINT},
+ {"public", P_BOOL, P_LOCAL, &sDefault.bGuest_ok, NULL, NULL, FLAG_HIDE},
+
+ {"only user", P_BOOL, P_LOCAL, &sDefault.bOnlyUser, NULL, NULL, FLAG_ADVANCED | FLAG_SHARE | FLAG_DEPRECATED},
+ {"hosts allow", P_LIST, P_LOCAL, &sDefault.szHostsallow, NULL, NULL, FLAG_GLOBAL | FLAG_BASIC | FLAG_ADVANCED | FLAG_SHARE | FLAG_PRINT},
+ {"allow hosts", P_LIST, P_LOCAL, &sDefault.szHostsallow, NULL, NULL, FLAG_HIDE},
+ {"hosts deny", P_LIST, P_LOCAL, &sDefault.szHostsdeny, NULL, NULL, FLAG_GLOBAL | FLAG_BASIC | FLAG_ADVANCED | FLAG_SHARE | FLAG_PRINT},
+ {"deny hosts", P_LIST, P_LOCAL, &sDefault.szHostsdeny, NULL, NULL, FLAG_HIDE},
+ {"preload modules", P_LIST, P_GLOBAL, &Globals.szPreloadModules, NULL, NULL, FLAG_ADVANCED | FLAG_GLOBAL},
+
+ {N_("Logging Options"), P_SEP, P_SEPARATOR},
+
+ {"log level", P_STRING, P_GLOBAL, &Globals.szLogLevel, handle_debug_list, NULL, FLAG_ADVANCED},
+ {"debuglevel", P_STRING, P_GLOBAL, &Globals.szLogLevel, handle_debug_list, NULL, FLAG_HIDE},
+ {"syslog", P_INTEGER, P_GLOBAL, &Globals.syslog, NULL, NULL, FLAG_ADVANCED},
+ {"syslog only", P_BOOL, P_GLOBAL, &Globals.bSyslogOnly, NULL, NULL, FLAG_ADVANCED},
+ {"log file", P_STRING, P_GLOBAL, &Globals.szLogFile, NULL, NULL, FLAG_ADVANCED},
+
+ {"max log size", P_INTEGER, P_GLOBAL, &Globals.max_log_size, NULL, NULL, FLAG_ADVANCED},
+ {"timestamp logs", P_BOOL, P_GLOBAL, &Globals.bTimestampLogs, NULL, NULL, FLAG_ADVANCED},
+ {"debug timestamp", P_BOOL, P_GLOBAL, &Globals.bTimestampLogs, NULL, NULL, FLAG_ADVANCED},
+ {"debug hires timestamp", P_BOOL, P_GLOBAL, &Globals.bDebugHiresTimestamp, NULL, NULL, FLAG_ADVANCED},
+ {"debug pid", P_BOOL, P_GLOBAL, &Globals.bDebugPid, NULL, NULL, FLAG_ADVANCED},
+ {"debug uid", P_BOOL, P_GLOBAL, &Globals.bDebugUid, NULL, NULL, FLAG_ADVANCED},
+
+ {N_("Protocol Options"), P_SEP, P_SEPARATOR},
+
+ {"smb ports", P_STRING, P_GLOBAL, &Globals.smb_ports, NULL, NULL, FLAG_ADVANCED},
+ {"protocol", P_ENUM, P_GLOBAL, &Globals.maxprotocol, NULL, enum_protocol, FLAG_ADVANCED},
+ {"large readwrite", P_BOOL, P_GLOBAL, &Globals.bLargeReadwrite, NULL, NULL, FLAG_ADVANCED},
+ {"max protocol", P_ENUM, P_GLOBAL, &Globals.maxprotocol, NULL, enum_protocol, FLAG_ADVANCED},
+ {"min protocol", P_ENUM, P_GLOBAL, &Globals.minprotocol, NULL, enum_protocol, FLAG_ADVANCED},
+ {"unicode", P_BOOL, P_GLOBAL, &Globals.bUnicode, NULL, NULL, FLAG_ADVANCED},
+ {"read bmpx", P_BOOL, P_GLOBAL, &Globals.bReadbmpx, NULL, NULL, FLAG_ADVANCED},
+ {"read raw", P_BOOL, P_GLOBAL, &Globals.bReadRaw, NULL, NULL, FLAG_ADVANCED},
+ {"write raw", P_BOOL, P_GLOBAL, &Globals.bWriteRaw, NULL, NULL, FLAG_ADVANCED},
+ {"disable netbios", P_BOOL, P_GLOBAL, &Globals.bDisableNetbios, NULL, NULL, FLAG_ADVANCED},
+
+ {"acl compatibility", P_STRING, P_GLOBAL, &Globals.szAclCompat, handle_acl_compatibility, NULL, FLAG_ADVANCED | FLAG_SHARE | FLAG_GLOBAL},
+ {"ea support", P_BOOL, P_LOCAL, &sDefault.bEASupport, NULL, NULL, FLAG_ADVANCED | FLAG_SHARE | FLAG_GLOBAL},
+ {"nt acl support", P_BOOL, P_LOCAL, &sDefault.bNTAclSupport, NULL, NULL, FLAG_ADVANCED | FLAG_SHARE | FLAG_GLOBAL},
+ {"nt pipe support", P_BOOL, P_GLOBAL, &Globals.bNTPipeSupport, NULL, NULL, FLAG_ADVANCED},
+ {"nt status support", P_BOOL, P_GLOBAL, &Globals.bNTStatusSupport, NULL, NULL, FLAG_ADVANCED},
+ {"profile acls", P_BOOL, P_LOCAL, &sDefault.bProfileAcls, NULL, NULL, FLAG_ADVANCED | FLAG_GLOBAL | FLAG_SHARE},
+
+ {"announce version", P_STRING, P_GLOBAL, &Globals.szAnnounceVersion, NULL, NULL, FLAG_ADVANCED},
+ {"announce as", P_ENUM, P_GLOBAL, &Globals.announce_as, NULL, enum_announce_as, FLAG_ADVANCED},
+ {"map acl inherit", P_BOOL, P_LOCAL, &sDefault.bMap_acl_inherit, NULL, NULL, FLAG_ADVANCED | FLAG_SHARE | FLAG_GLOBAL},
+ {"afs share", P_BOOL, P_LOCAL, &sDefault.bAfs_Share, NULL, NULL, FLAG_ADVANCED | FLAG_SHARE | FLAG_GLOBAL},
+ {"max mux", P_INTEGER, P_GLOBAL, &Globals.max_mux, NULL, NULL, FLAG_ADVANCED},
+ {"max xmit", P_INTEGER, P_GLOBAL, &Globals.max_xmit, NULL, NULL, FLAG_ADVANCED},
+
+ {"name resolve order", P_STRING, P_GLOBAL, &Globals.szNameResolveOrder, NULL, NULL, FLAG_ADVANCED | FLAG_WIZARD},
+ {"max ttl", P_INTEGER, P_GLOBAL, &Globals.max_ttl, NULL, NULL, FLAG_ADVANCED},
+ {"max wins ttl", P_INTEGER, P_GLOBAL, &Globals.max_wins_ttl, NULL, NULL, FLAG_ADVANCED},
+ {"min wins ttl", P_INTEGER, P_GLOBAL, &Globals.min_wins_ttl, NULL, NULL, FLAG_ADVANCED},
+ {"time server", P_BOOL, P_GLOBAL, &Globals.bTimeServer, NULL, NULL, FLAG_ADVANCED},
+ {"unix extensions", P_BOOL, P_GLOBAL, &Globals.bUnixExtensions, NULL, NULL, FLAG_ADVANCED},
+ {"use spnego", P_BOOL, P_GLOBAL, &Globals.bUseSpnego, NULL, NULL, FLAG_ADVANCED},
+ {"client signing", P_ENUM, P_GLOBAL, &Globals.client_signing, NULL, enum_smb_signing_vals, FLAG_ADVANCED},
+ {"server signing", P_ENUM, P_GLOBAL, &Globals.server_signing, NULL, enum_smb_signing_vals, FLAG_ADVANCED},
+ {"client use spnego", P_BOOL, P_GLOBAL, &Globals.bClientUseSpnego, NULL, NULL, FLAG_ADVANCED},
+
+ {N_("Tuning Options"), P_SEP, P_SEPARATOR},
+
+ {"block size", P_INTEGER, P_LOCAL, &sDefault.iBlock_size, NULL, NULL, FLAG_ADVANCED | FLAG_SHARE | FLAG_GLOBAL},
+ {"change notify timeout", P_INTEGER, P_GLOBAL, &Globals.change_notify_timeout, NULL, NULL, FLAG_ADVANCED},
+ {"deadtime", P_INTEGER, P_GLOBAL, &Globals.deadtime, NULL, NULL, FLAG_ADVANCED},
+ {"getwd cache", P_BOOL, P_GLOBAL, &use_getwd_cache, NULL, NULL, FLAG_ADVANCED},
+ {"keepalive", P_INTEGER, P_GLOBAL, &keepalive, NULL, NULL, FLAG_ADVANCED},
+ {"kernel change notify", P_BOOL, P_GLOBAL, &Globals.bKernelChangeNotify, NULL, NULL, FLAG_ADVANCED},
+
+ {"lpq cache time", P_INTEGER, P_GLOBAL, &Globals.lpqcachetime, NULL, NULL, FLAG_ADVANCED},
+ {"max smbd processes", P_INTEGER, P_GLOBAL, &Globals.iMaxSmbdProcesses, NULL, NULL, FLAG_ADVANCED},
+ {"max connections", P_INTEGER, P_LOCAL, &sDefault.iMaxConnections, NULL, NULL, FLAG_ADVANCED | FLAG_SHARE},
+ {"paranoid server security", P_BOOL, P_GLOBAL, &Globals.paranoid_server_security, NULL, NULL, FLAG_ADVANCED},
+ {"max disk size", P_INTEGER, P_GLOBAL, &Globals.maxdisksize, NULL, NULL, FLAG_ADVANCED},
+ {"max open files", P_INTEGER, P_GLOBAL, &Globals.max_open_files, NULL, NULL, FLAG_ADVANCED},
+ {"min print space", P_INTEGER, P_LOCAL, &sDefault.iMinPrintSpace, NULL, NULL, FLAG_ADVANCED | FLAG_PRINT},
+
+ {"socket options", P_GSTRING, P_GLOBAL, user_socket_options, NULL, NULL, FLAG_ADVANCED},
+ {"strict allocate", P_BOOL, P_LOCAL, &sDefault.bStrictAllocate, NULL, NULL, FLAG_ADVANCED | FLAG_SHARE},
+ {"strict sync", P_BOOL, P_LOCAL, &sDefault.bStrictSync, NULL, NULL, FLAG_ADVANCED | FLAG_SHARE},
+ {"sync always", P_BOOL, P_LOCAL, &sDefault.bSyncAlways, NULL, NULL, FLAG_ADVANCED | FLAG_SHARE},
+ {"use mmap", P_BOOL, P_GLOBAL, &Globals.bUseMmap, NULL, NULL, FLAG_ADVANCED},
+ {"use sendfile", P_BOOL, P_LOCAL, &sDefault.bUseSendfile, NULL, NULL, FLAG_ADVANCED | FLAG_SHARE},
+ {"hostname lookups", P_BOOL, P_GLOBAL, &Globals.bHostnameLookups, NULL, NULL, FLAG_ADVANCED},
+ {"write cache size", P_INTEGER, P_LOCAL, &sDefault.iWriteCacheSize, NULL, NULL, FLAG_ADVANCED | FLAG_SHARE},
+
+ {"name cache timeout", P_INTEGER, P_GLOBAL, &Globals.name_cache_timeout, NULL, NULL, FLAG_ADVANCED},
+
+ {N_("Printing Options"), P_SEP, P_SEPARATOR},
+
+ {"max reported print jobs", P_INTEGER, P_LOCAL, &sDefault.iMaxReportedPrintJobs, NULL, NULL, FLAG_ADVANCED | FLAG_PRINT},
+ {"max print jobs", P_INTEGER, P_LOCAL, &sDefault.iMaxPrintJobs, NULL, NULL, FLAG_ADVANCED | FLAG_PRINT},
+ {"load printers", P_BOOL, P_GLOBAL, &Globals.bLoadPrinters, NULL, NULL, FLAG_ADVANCED | FLAG_PRINT},
+ {"printcap name", P_STRING, P_GLOBAL, &Globals.szPrintcapname, NULL, NULL, FLAG_ADVANCED | FLAG_PRINT},
+ {"printcap", P_STRING, P_GLOBAL, &Globals.szPrintcapname, NULL, NULL, FLAG_HIDE},
+ {"printable", P_BOOL, P_LOCAL, &sDefault.bPrint_ok, NULL, NULL, FLAG_ADVANCED | FLAG_PRINT},
+ {"print ok", P_BOOL, P_LOCAL, &sDefault.bPrint_ok, NULL, NULL, FLAG_HIDE},
+ {"printing", P_ENUM, P_LOCAL, &sDefault.iPrinting, NULL, enum_printing, FLAG_ADVANCED | FLAG_PRINT | FLAG_GLOBAL},
+ {"print command", P_STRING, P_LOCAL, &sDefault.szPrintcommand, NULL, NULL, FLAG_ADVANCED | FLAG_PRINT | FLAG_GLOBAL},
+ {"disable spoolss", P_BOOL, P_GLOBAL, &Globals.bDisableSpoolss, NULL, NULL, FLAG_ADVANCED | FLAG_PRINT | FLAG_GLOBAL},
+ {"lpq command", P_STRING, P_LOCAL, &sDefault.szLpqcommand, NULL, NULL, FLAG_ADVANCED | FLAG_PRINT | FLAG_GLOBAL},
+ {"lprm command", P_STRING, P_LOCAL, &sDefault.szLprmcommand, NULL, NULL, FLAG_ADVANCED | FLAG_PRINT | FLAG_GLOBAL},
+ {"lppause command", P_STRING, P_LOCAL, &sDefault.szLppausecommand, NULL, NULL, FLAG_ADVANCED | FLAG_PRINT | FLAG_GLOBAL},
+ {"lpresume command", P_STRING, P_LOCAL, &sDefault.szLpresumecommand, NULL, NULL, FLAG_ADVANCED | FLAG_PRINT | FLAG_GLOBAL},
+ {"queuepause command", P_STRING, P_LOCAL, &sDefault.szQueuepausecommand, NULL, NULL, FLAG_ADVANCED | FLAG_PRINT | FLAG_GLOBAL},
+ {"queueresume command", P_STRING, P_LOCAL, &sDefault.szQueueresumecommand, NULL, NULL, FLAG_ADVANCED | FLAG_PRINT | FLAG_GLOBAL},
+
+ {"enumports command", P_STRING, P_GLOBAL, &Globals.szEnumPortsCommand, NULL, NULL, FLAG_ADVANCED},
+ {"addprinter command", P_STRING, P_GLOBAL, &Globals.szAddPrinterCommand, NULL, NULL, FLAG_ADVANCED},
+ {"deleteprinter command", P_STRING, P_GLOBAL, &Globals.szDeletePrinterCommand, NULL, NULL, FLAG_ADVANCED},
+ {"show add printer wizard", P_BOOL, P_GLOBAL, &Globals.bMsAddPrinterWizard, NULL, NULL, FLAG_ADVANCED},
+ {"os2 driver map", P_STRING, P_GLOBAL, &Globals.szOs2DriverMap, NULL, NULL, FLAG_ADVANCED},
+
+ {"printer name", P_STRING, P_LOCAL, &sDefault.szPrintername, NULL, NULL, FLAG_ADVANCED | FLAG_PRINT},
+ {"printer", P_STRING, P_LOCAL, &sDefault.szPrintername, NULL, NULL, FLAG_HIDE},
+ {"use client driver", P_BOOL, P_LOCAL, &sDefault.bUseClientDriver, NULL, NULL, FLAG_ADVANCED | FLAG_PRINT},
+ {"default devmode", P_BOOL, P_LOCAL, &sDefault.bDefaultDevmode, NULL, NULL, FLAG_ADVANCED | FLAG_PRINT},
+
+ {N_("Filename Handling"), P_SEP, P_SEPARATOR},
+ {"mangling method", P_STRING, P_GLOBAL, &Globals.szManglingMethod, NULL, NULL, FLAG_ADVANCED},
+ {"mangle prefix", P_INTEGER, P_GLOBAL, &Globals.mangle_prefix, NULL, NULL, FLAG_ADVANCED},
+
+ {"default case", P_ENUM, P_LOCAL, &sDefault.iDefaultCase, NULL, enum_case, FLAG_ADVANCED | FLAG_SHARE},
+ {"case sensitive", P_BOOL, P_LOCAL, &sDefault.bCaseSensitive, NULL, NULL, FLAG_ADVANCED | FLAG_SHARE | FLAG_GLOBAL},
+ {"casesignames", P_BOOL, P_LOCAL, &sDefault.bCaseSensitive, NULL, NULL, FLAG_HIDE},
+ {"preserve case", P_BOOL, P_LOCAL, &sDefault.bCasePreserve, NULL, NULL, FLAG_ADVANCED | FLAG_SHARE | FLAG_GLOBAL},
+ {"short preserve case", P_BOOL, P_LOCAL, &sDefault.bShortCasePreserve, NULL, NULL, FLAG_ADVANCED | FLAG_SHARE | FLAG_GLOBAL},
+ {"mangle case", P_BOOL, P_LOCAL, &sDefault.bCaseMangle, NULL, NULL, FLAG_ADVANCED | FLAG_SHARE | FLAG_GLOBAL},
+ {"mangling char", P_CHAR, P_LOCAL, &sDefault.magic_char, NULL, NULL, FLAG_ADVANCED | FLAG_SHARE | FLAG_GLOBAL},
+ {"hide dot files", P_BOOL, P_LOCAL, &sDefault.bHideDotFiles, NULL, NULL, FLAG_ADVANCED | FLAG_SHARE | FLAG_GLOBAL},
+ {"hide special files", P_BOOL, P_LOCAL, &sDefault.bHideSpecialFiles, NULL, NULL, FLAG_ADVANCED | FLAG_SHARE | FLAG_GLOBAL},
+ {"hide unreadable", P_BOOL, P_LOCAL, &sDefault.bHideUnReadable, NULL, NULL, FLAG_ADVANCED | FLAG_SHARE | FLAG_GLOBAL},
+ {"hide unwriteable files", P_BOOL, P_LOCAL, &sDefault.bHideUnWriteableFiles, NULL, NULL, FLAG_ADVANCED | FLAG_SHARE | FLAG_GLOBAL},
+ {"delete veto files", P_BOOL, P_LOCAL, &sDefault.bDeleteVetoFiles, NULL, NULL, FLAG_ADVANCED | FLAG_SHARE | FLAG_GLOBAL},
+ {"veto files", P_STRING, P_LOCAL, &sDefault.szVetoFiles, NULL, NULL, FLAG_ADVANCED | FLAG_SHARE | FLAG_GLOBAL },
+ {"hide files", P_STRING, P_LOCAL, &sDefault.szHideFiles, NULL, NULL, FLAG_ADVANCED | FLAG_SHARE | FLAG_GLOBAL },
+ {"veto oplock files", P_STRING, P_LOCAL, &sDefault.szVetoOplockFiles, NULL, NULL, FLAG_ADVANCED | FLAG_SHARE | FLAG_GLOBAL },
+ {"map system", P_BOOL, P_LOCAL, &sDefault.bMap_system, NULL, NULL, FLAG_ADVANCED | FLAG_SHARE | FLAG_GLOBAL},
+ {"map hidden", P_BOOL, P_LOCAL, &sDefault.bMap_hidden, NULL, NULL, FLAG_ADVANCED | FLAG_SHARE | FLAG_GLOBAL},
+ {"map archive", P_BOOL, P_LOCAL, &sDefault.bMap_archive, NULL, NULL, FLAG_ADVANCED | FLAG_SHARE | FLAG_GLOBAL},
+ {"mangled names", P_BOOL, P_LOCAL, &sDefault.bMangledNames, NULL, NULL, FLAG_ADVANCED | FLAG_SHARE | FLAG_GLOBAL},
+ {"mangled map", P_STRING, P_LOCAL, &sDefault.szMangledMap, NULL, NULL, FLAG_ADVANCED | FLAG_SHARE | FLAG_GLOBAL | FLAG_DEPRECATED },
+ {"stat cache", P_BOOL, P_GLOBAL, &Globals.bStatCache, NULL, NULL, FLAG_ADVANCED},
+ {"store dos attributes", P_BOOL, P_LOCAL, &sDefault.bStoreDosAttributes, NULL, NULL, FLAG_ADVANCED | FLAG_SHARE | FLAG_GLOBAL},
+
+ {N_("Domain Options"), P_SEP, P_SEPARATOR},
+
+ {"machine password timeout", P_INTEGER, P_GLOBAL, &Globals.machine_password_timeout, NULL, NULL, FLAG_ADVANCED | FLAG_WIZARD},
+
+ {N_("Logon Options"), P_SEP, P_SEPARATOR},
+
+ {"add user script", P_STRING, P_GLOBAL, &Globals.szAddUserScript, NULL, NULL, FLAG_ADVANCED},
+ {"delete user script", P_STRING, P_GLOBAL, &Globals.szDelUserScript, NULL, NULL, FLAG_ADVANCED},
+ {"add group script", P_STRING, P_GLOBAL, &Globals.szAddGroupScript, NULL, NULL, FLAG_ADVANCED},
+ {"delete group script", P_STRING, P_GLOBAL, &Globals.szDelGroupScript, NULL, NULL, FLAG_ADVANCED},
+ {"add user to group script", P_STRING, P_GLOBAL, &Globals.szAddUserToGroupScript, NULL, NULL, FLAG_ADVANCED},
+ {"delete user from group script", P_STRING, P_GLOBAL, &Globals.szDelUserFromGroupScript, NULL, NULL, FLAG_ADVANCED},
+ {"set primary group script", P_STRING, P_GLOBAL, &Globals.szSetPrimaryGroupScript, NULL, NULL, FLAG_ADVANCED},
+ {"add machine script", P_STRING, P_GLOBAL, &Globals.szAddMachineScript, NULL, NULL, FLAG_ADVANCED},
+ {"shutdown script", P_STRING, P_GLOBAL, &Globals.szShutdownScript, NULL, NULL, FLAG_ADVANCED},
+ {"abort shutdown script", P_STRING, P_GLOBAL, &Globals.szAbortShutdownScript, NULL, NULL, FLAG_ADVANCED},
+
+ {"logon script", P_STRING, P_GLOBAL, &Globals.szLogonScript, NULL, NULL, FLAG_ADVANCED},
+ {"logon path", P_STRING, P_GLOBAL, &Globals.szLogonPath, NULL, NULL, FLAG_ADVANCED},
+ {"logon drive", P_STRING, P_GLOBAL, &Globals.szLogonDrive, NULL, NULL, FLAG_ADVANCED},
+ {"logon home", P_STRING, P_GLOBAL, &Globals.szLogonHome, NULL, NULL, FLAG_ADVANCED},
+ {"domain logons", P_BOOL, P_GLOBAL, &Globals.bDomainLogons, NULL, NULL, FLAG_ADVANCED},
+
+ {N_("Browse Options"), P_SEP, P_SEPARATOR},
+
+ {"os level", P_INTEGER, P_GLOBAL, &Globals.os_level, NULL, NULL, FLAG_BASIC | FLAG_ADVANCED},
+ {"lm announce", P_ENUM, P_GLOBAL, &Globals.lm_announce, NULL, enum_bool_auto, FLAG_ADVANCED},
+ {"lm interval", P_INTEGER, P_GLOBAL, &Globals.lm_interval, NULL, NULL, FLAG_ADVANCED},
+ {"preferred master", P_ENUM, P_GLOBAL, &Globals.bPreferredMaster, NULL, enum_bool_auto, FLAG_BASIC | FLAG_ADVANCED},
+ {"prefered master", P_ENUM, P_GLOBAL, &Globals.bPreferredMaster, NULL, enum_bool_auto, FLAG_HIDE},
+ {"local master", P_BOOL, P_GLOBAL, &Globals.bLocalMaster, NULL, NULL, FLAG_BASIC | FLAG_ADVANCED},
+ {"domain master", P_ENUM, P_GLOBAL, &Globals.bDomainMaster, NULL, enum_bool_auto, FLAG_BASIC | FLAG_ADVANCED},
+ {"browse list", P_BOOL, P_GLOBAL, &Globals.bBrowseList, NULL, NULL, FLAG_ADVANCED},
+ {"browseable", P_BOOL, P_LOCAL, &sDefault.bBrowseable, NULL, NULL, FLAG_BASIC | FLAG_ADVANCED | FLAG_SHARE | FLAG_PRINT},
+ {"browsable", P_BOOL, P_LOCAL, &sDefault.bBrowseable, NULL, NULL, FLAG_HIDE},
+ {"enhanced browsing", P_BOOL, P_GLOBAL, &Globals.enhanced_browsing, NULL, NULL, FLAG_ADVANCED},
+
+ {N_("WINS Options"), P_SEP, P_SEPARATOR},
+
+ {"dns proxy", P_BOOL, P_GLOBAL, &Globals.bDNSproxy, NULL, NULL, FLAG_ADVANCED},
+ {"wins proxy", P_BOOL, P_GLOBAL, &Globals.bWINSproxy, NULL, NULL, FLAG_ADVANCED},
+
+ {"wins server", P_LIST, P_GLOBAL, &Globals.szWINSservers, NULL, NULL, FLAG_BASIC | FLAG_ADVANCED | FLAG_WIZARD},
+ {"wins support", P_BOOL, P_GLOBAL, &Globals.bWINSsupport, NULL, NULL, FLAG_BASIC | FLAG_ADVANCED | FLAG_WIZARD},
+ {"wins hook", P_STRING, P_GLOBAL, &Globals.szWINSHook, NULL, NULL, FLAG_ADVANCED},
+ {"wins partners", P_STRING, P_GLOBAL, &Globals.szWINSPartners, NULL, NULL, FLAG_ADVANCED | FLAG_WIZARD},
+
+ {N_("Locking Options"), P_SEP, P_SEPARATOR},
+
+ {"blocking locks", P_BOOL, P_LOCAL, &sDefault.bBlockingLocks, NULL, NULL, FLAG_ADVANCED | FLAG_SHARE | FLAG_GLOBAL},
+ {"csc policy", P_ENUM, P_LOCAL, &sDefault.iCSCPolicy, NULL, enum_csc_policy, FLAG_ADVANCED | FLAG_SHARE | FLAG_GLOBAL},
+ {"fake oplocks", P_BOOL, P_LOCAL, &sDefault.bFakeOplocks, NULL, NULL, FLAG_ADVANCED | FLAG_SHARE},
+ {"kernel oplocks", P_BOOL, P_GLOBAL, &Globals.bKernelOplocks, NULL, NULL, FLAG_ADVANCED | FLAG_GLOBAL},
+ {"locking", P_BOOL, P_LOCAL, &sDefault.bLocking, NULL, NULL, FLAG_ADVANCED | FLAG_SHARE | FLAG_GLOBAL},
+ {"lock spin count", P_INTEGER, P_GLOBAL, &Globals.iLockSpinCount, NULL, NULL, FLAG_ADVANCED | FLAG_GLOBAL},
+ {"lock spin time", P_INTEGER, P_GLOBAL, &Globals.iLockSpinTime, NULL, NULL, FLAG_ADVANCED | FLAG_GLOBAL},
+
+ {"oplocks", P_BOOL, P_LOCAL, &sDefault.bOpLocks, NULL, NULL, FLAG_ADVANCED | FLAG_SHARE | FLAG_GLOBAL},
+ {"level2 oplocks", P_BOOL, P_LOCAL, &sDefault.bLevel2OpLocks, NULL, NULL, FLAG_ADVANCED | FLAG_SHARE | FLAG_GLOBAL},
+ {"oplock break wait time", P_INTEGER, P_GLOBAL, &Globals.oplock_break_wait_time, NULL, NULL, FLAG_ADVANCED | FLAG_GLOBAL},
+ {"oplock contention limit", P_INTEGER, P_LOCAL, &sDefault.iOplockContentionLimit, NULL, NULL, FLAG_ADVANCED | FLAG_SHARE | FLAG_GLOBAL},
+ {"posix locking", P_BOOL, P_LOCAL, &sDefault.bPosixLocking, NULL, NULL, FLAG_ADVANCED | FLAG_SHARE | FLAG_GLOBAL},
+ {"strict locking", P_BOOL, P_LOCAL, &sDefault.bStrictLocking, NULL, NULL, FLAG_ADVANCED | FLAG_SHARE | FLAG_GLOBAL},
+ {"share modes", P_BOOL, P_LOCAL, &sDefault.bShareModes, NULL, NULL, FLAG_ADVANCED | FLAG_SHARE | FLAG_GLOBAL},
+
+ {N_("Ldap Options"), P_SEP, P_SEPARATOR},
+
+#ifdef WITH_LDAP_SAMCONFIG
+ {"ldap server", P_STRING, P_GLOBAL, &Globals.szLdapServer, NULL, NULL, FLAG_ADVANCED},
+ {"ldap port", P_INTEGER, P_GLOBAL, &Globals.ldap_port, NULL, NULL, FLAG_ADVANCED},
+#endif
+ {"ldap suffix", P_STRING, P_GLOBAL, &Globals.szLdapSuffix, NULL, NULL, FLAG_ADVANCED},
+ {"ldap machine suffix", P_STRING, P_GLOBAL, &Globals.szLdapMachineSuffix, NULL, NULL, FLAG_ADVANCED},
+ {"ldap user suffix", P_STRING, P_GLOBAL, &Globals.szLdapUserSuffix, NULL, NULL, FLAG_ADVANCED},
+ {"ldap group suffix", P_STRING, P_GLOBAL, &Globals.szLdapGroupSuffix, NULL, NULL, FLAG_ADVANCED},
+ {"ldap idmap suffix", P_STRING, P_GLOBAL, &Globals.szLdapIdmapSuffix, NULL, NULL, FLAG_ADVANCED},
+ {"ldap privilege suffix", P_STRING, P_GLOBAL, &Globals.szLdapPrivilegeSuffix, NULL, NULL, FLAG_ADVANCED},
+ {"ldap filter", P_STRING, P_GLOBAL, &Globals.szLdapFilter, NULL, NULL, FLAG_ADVANCED},
+ {"ldap admin dn", P_STRING, P_GLOBAL, &Globals.szLdapAdminDn, NULL, NULL, FLAG_ADVANCED},
+ {"ldap ssl", P_ENUM, P_GLOBAL, &Globals.ldap_ssl, NULL, enum_ldap_ssl, FLAG_ADVANCED},
+ {"ldap passwd sync", P_ENUM, P_GLOBAL, &Globals.ldap_passwd_sync, NULL, enum_ldap_passwd_sync, FLAG_ADVANCED},
+ {"ldap delete dn", P_BOOL, P_GLOBAL, &Globals.ldap_delete_dn, NULL, NULL, FLAG_ADVANCED},
+ {"ldap replication sleep", P_INTEGER, P_GLOBAL, &Globals.ldap_replication_sleep, NULL, NULL, FLAG_ADVANCED},
+
+ {N_("Miscellaneous Options"), P_SEP, P_SEPARATOR},
+ {"add share command", P_STRING, P_GLOBAL, &Globals.szAddShareCommand, NULL, NULL, FLAG_ADVANCED},
+ {"change share command", P_STRING, P_GLOBAL, &Globals.szChangeShareCommand, NULL, NULL, FLAG_ADVANCED},
+ {"delete share command", P_STRING, P_GLOBAL, &Globals.szDeleteShareCommand, NULL, NULL, FLAG_ADVANCED},
+
+ {"config file", P_STRING, P_GLOBAL, &Globals.szConfigFile, NULL, NULL, FLAG_HIDE},
+ {"preload", P_STRING, P_GLOBAL, &Globals.szAutoServices, NULL, NULL, FLAG_ADVANCED},
+ {"auto services", P_STRING, P_GLOBAL, &Globals.szAutoServices, NULL, NULL, FLAG_ADVANCED},
+ {"lock directory", P_STRING, P_GLOBAL, &Globals.szLockDir, NULL, NULL, FLAG_ADVANCED},
+ {"lock dir", P_STRING, P_GLOBAL, &Globals.szLockDir, NULL, NULL, FLAG_HIDE},
+ {"pid directory", P_STRING, P_GLOBAL, &Globals.szPidDir, NULL, NULL, FLAG_ADVANCED},
+#ifdef WITH_UTMP
+ {"utmp directory", P_STRING, P_GLOBAL, &Globals.szUtmpDir, NULL, NULL, FLAG_ADVANCED},
+ {"wtmp directory", P_STRING, P_GLOBAL, &Globals.szWtmpDir, NULL, NULL, FLAG_ADVANCED},
+ {"utmp", P_BOOL, P_GLOBAL, &Globals.bUtmp, NULL, NULL, FLAG_ADVANCED},
+#endif
+
+ {"default service", P_STRING, P_GLOBAL, &Globals.szDefaultService, NULL, NULL, FLAG_ADVANCED},
+ {"default", P_STRING, P_GLOBAL, &Globals.szDefaultService, NULL, NULL, FLAG_ADVANCED},
+ {"message command", P_STRING, P_GLOBAL, &Globals.szMsgCommand, NULL, NULL, FLAG_ADVANCED},
+ {"dfree command", P_STRING, P_GLOBAL, &Globals.szDfree, NULL, NULL, FLAG_ADVANCED},
+ {"get quota command", P_STRING, P_GLOBAL, &Globals.szGetQuota, NULL, NULL, FLAG_ADVANCED},
+ {"set quota command", P_STRING, P_GLOBAL, &Globals.szSetQuota, NULL, NULL, FLAG_ADVANCED},
+ {"remote announce", P_STRING, P_GLOBAL, &Globals.szRemoteAnnounce, NULL, NULL, FLAG_ADVANCED},
+ {"remote browse sync", P_STRING, P_GLOBAL, &Globals.szRemoteBrowseSync, NULL, NULL, FLAG_ADVANCED},
+ {"socket address", P_STRING, P_GLOBAL, &Globals.szSocketAddress, NULL, NULL, FLAG_ADVANCED},
+ {"homedir map", P_STRING, P_GLOBAL, &Globals.szNISHomeMapName, NULL, NULL, FLAG_ADVANCED},
+ {"afs username map", P_USTRING, P_GLOBAL, &Globals.szAfsUsernameMap, NULL, NULL, FLAG_ADVANCED},
+ {"time offset", P_INTEGER, P_GLOBAL, &extra_time_offset, NULL, NULL, FLAG_ADVANCED},
+ {"NIS homedir", P_BOOL, P_GLOBAL, &Globals.bNISHomeMap, NULL, NULL, FLAG_ADVANCED},
+ {"-valid", P_BOOL, P_LOCAL, &sDefault.valid, NULL, NULL, FLAG_HIDE},
+
+ {"copy", P_STRING, P_LOCAL, &sDefault.szCopy, handle_copy, NULL, FLAG_HIDE},
+ {"include", P_STRING, P_LOCAL, &sDefault.szInclude, handle_include, NULL, FLAG_HIDE},
+ {"exec", P_STRING, P_LOCAL, &sDefault.szPreExec, NULL, NULL, FLAG_ADVANCED | FLAG_SHARE | FLAG_PRINT},
+ {"preexec", P_STRING, P_LOCAL, &sDefault.szPreExec, NULL, NULL, FLAG_ADVANCED},
+
+ {"preexec close", P_BOOL, P_LOCAL, &sDefault.bPreexecClose, NULL, NULL, FLAG_ADVANCED | FLAG_SHARE},
+ {"postexec", P_STRING, P_LOCAL, &sDefault.szPostExec, NULL, NULL, FLAG_ADVANCED | FLAG_SHARE | FLAG_PRINT},
+ {"root preexec", P_STRING, P_LOCAL, &sDefault.szRootPreExec, NULL, NULL, FLAG_ADVANCED | FLAG_SHARE | FLAG_PRINT},
+ {"root preexec close", P_BOOL, P_LOCAL, &sDefault.bRootpreexecClose, NULL, NULL, FLAG_ADVANCED | FLAG_SHARE},
+ {"root postexec", P_STRING, P_LOCAL, &sDefault.szRootPostExec, NULL, NULL, FLAG_ADVANCED | FLAG_SHARE | FLAG_PRINT},
+ {"available", P_BOOL, P_LOCAL, &sDefault.bAvailable, NULL, NULL, FLAG_BASIC | FLAG_ADVANCED | FLAG_SHARE | FLAG_PRINT},
+ {"volume", P_STRING, P_LOCAL, &sDefault.volume, NULL, NULL, FLAG_ADVANCED | FLAG_SHARE },
+ {"fstype", P_STRING, P_LOCAL, &sDefault.fstype, NULL, NULL, FLAG_ADVANCED | FLAG_SHARE},
+ {"set directory", P_BOOLREV, P_LOCAL, &sDefault.bNo_set_dir, NULL, NULL, FLAG_ADVANCED | FLAG_SHARE},
+ {"wide links", P_BOOL, P_LOCAL, &sDefault.bWidelinks, NULL, NULL, FLAG_ADVANCED | FLAG_SHARE | FLAG_GLOBAL},
+ {"follow symlinks", P_BOOL, P_LOCAL, &sDefault.bSymlinks, NULL, NULL, FLAG_ADVANCED | FLAG_SHARE | FLAG_GLOBAL},
+ {"dont descend", P_STRING, P_LOCAL, &sDefault.szDontdescend, NULL, NULL, FLAG_ADVANCED | FLAG_SHARE},
+ {"magic script", P_STRING, P_LOCAL, &sDefault.szMagicScript, NULL, NULL, FLAG_ADVANCED | FLAG_SHARE},
+ {"magic output", P_STRING, P_LOCAL, &sDefault.szMagicOutput, NULL, NULL, FLAG_ADVANCED | FLAG_SHARE},
+ {"delete readonly", P_BOOL, P_LOCAL, &sDefault.bDeleteReadonly, NULL, NULL, FLAG_ADVANCED | FLAG_SHARE | FLAG_GLOBAL},
+ {"dos filemode", P_BOOL, P_LOCAL, &sDefault.bDosFilemode, NULL, NULL, FLAG_ADVANCED | FLAG_SHARE | FLAG_GLOBAL},
+ {"dos filetimes", P_BOOL, P_LOCAL, &sDefault.bDosFiletimes, NULL, NULL, FLAG_ADVANCED | FLAG_SHARE | FLAG_GLOBAL},
+ {"dos filetime resolution", P_BOOL, P_LOCAL, &sDefault.bDosFiletimeResolution, NULL, NULL, FLAG_ADVANCED | FLAG_SHARE | FLAG_GLOBAL},
+
+ {"fake directory create times", P_BOOL, P_LOCAL, &sDefault.bFakeDirCreateTimes, NULL, NULL, FLAG_ADVANCED | FLAG_SHARE | FLAG_GLOBAL},
+ {"panic action", P_STRING, P_GLOBAL, &Globals.szPanicAction, NULL, NULL, FLAG_ADVANCED},
+
+ {N_("VFS module options"), P_SEP, P_SEPARATOR},
+
+ {"vfs objects", P_LIST, P_LOCAL, &sDefault.szVfsObjects, NULL, NULL, FLAG_ADVANCED | FLAG_SHARE},
+ {"vfs object", P_LIST, P_LOCAL, &sDefault.szVfsObjects, NULL, NULL, FLAG_HIDE},
+
+
+ {"msdfs root", P_BOOL, P_LOCAL, &sDefault.bMSDfsRoot, NULL, NULL, FLAG_ADVANCED | FLAG_SHARE},
+ {"msdfs proxy", P_STRING, P_LOCAL, &sDefault.szMSDfsProxy, NULL, NULL, FLAG_ADVANCED | FLAG_SHARE},
+ {"host msdfs", P_BOOL, P_GLOBAL, &Globals.bHostMSDfs, NULL, NULL, FLAG_ADVANCED},
+
+ {N_("Winbind options"), P_SEP, P_SEPARATOR},
+
+ {"enable rid algorithm", P_BOOL, P_GLOBAL, &Globals.bEnableRidAlgorithm, NULL, NULL, FLAG_DEPRECATED},
+ {"idmap backend", P_STRING, P_GLOBAL, &Globals.szIdmapBackend, NULL, NULL, FLAG_ADVANCED},
+ {"idmap uid", P_STRING, P_GLOBAL, &Globals.szIdmapUID, handle_idmap_uid, NULL, FLAG_ADVANCED},
+ {"winbind uid", P_STRING, P_GLOBAL, &Globals.szIdmapUID, handle_idmap_uid, NULL, FLAG_HIDE},
+ {"idmap gid", P_STRING, P_GLOBAL, &Globals.szIdmapGID, handle_idmap_gid, NULL, FLAG_ADVANCED},
+ {"winbind gid", P_STRING, P_GLOBAL, &Globals.szIdmapGID, handle_idmap_gid, NULL, FLAG_HIDE},
+ {"template primary group", P_STRING, P_GLOBAL, &Globals.szTemplatePrimaryGroup, NULL, NULL, FLAG_ADVANCED},
+ {"template homedir", P_STRING, P_GLOBAL, &Globals.szTemplateHomedir, NULL, NULL, FLAG_ADVANCED},
+ {"template shell", P_STRING, P_GLOBAL, &Globals.szTemplateShell, NULL, NULL, FLAG_ADVANCED},
+ {"winbind separator", P_STRING, P_GLOBAL, &Globals.szWinbindSeparator, NULL, NULL, FLAG_ADVANCED},
+ {"winbind cache time", P_INTEGER, P_GLOBAL, &Globals.winbind_cache_time, NULL, NULL, FLAG_ADVANCED},
+ {"winbind enable local accounts", P_BOOL, P_GLOBAL, &Globals.bWinbindEnableLocalAccounts, NULL, NULL, FLAG_ADVANCED},
+ {"winbind enum users", P_BOOL, P_GLOBAL, &Globals.bWinbindEnumUsers, NULL, NULL, FLAG_ADVANCED},
+ {"winbind enum groups", P_BOOL, P_GLOBAL, &Globals.bWinbindEnumGroups, NULL, NULL, FLAG_ADVANCED},
+ {"winbind use default domain", P_BOOL, P_GLOBAL, &Globals.bWinbindUseDefaultDomain, NULL, NULL, FLAG_ADVANCED},
+ {"winbind trusted domains only", P_BOOL, P_GLOBAL, &Globals.bWinbindTrustedDomainsOnly, NULL, NULL, FLAG_ADVANCED},
+
+ {NULL, P_BOOL, P_NONE, NULL, NULL, NULL, 0}
+};
+
+/***************************************************************************
+ Initialise the sDefault parameter structure for the printer values.
+***************************************************************************/
+
+static void init_printer_values(service *pService)
+{
+ if ( pService == NULL ) {
+ DEBUG(0,("init_printer_values: NULL pointer\n"));
+ return;
+ }
+
+ /* choose defaults depending on the type of printing */
+ switch (pService->iPrinting) {
+ case PRINT_BSD:
+ case PRINT_AIX:
+ case PRINT_LPRNT:
+ case PRINT_LPROS2:
+ string_set(&pService->szLpqcommand, "lpq -P'%p'");
+ string_set(&pService->szLprmcommand, "lprm -P'%p' %j");
+ string_set(&pService->szPrintcommand,
+ "lpr -r -P'%p' %s");
+ break;
+
+ case PRINT_LPRNG:
+ case PRINT_PLP:
+ string_set(&pService->szLpqcommand, "lpq -P'%p'");
+ string_set(&pService->szLprmcommand, "lprm -P'%p' %j");
+ string_set(&pService->szPrintcommand,
+ "lpr -r -P'%p' %s");
+ string_set(&pService->szQueuepausecommand,
+ "lpc stop '%p'");
+ string_set(&pService->szQueueresumecommand,
+ "lpc start '%p'");
+ string_set(&pService->szLppausecommand,
+ "lpc hold '%p' %j");
+ string_set(&pService->szLpresumecommand,
+ "lpc release '%p' %j");
+ break;
+
+ case PRINT_CUPS:
+#ifdef HAVE_CUPS
+ string_set(&pService->szLpqcommand, "");
+ string_set(&pService->szLprmcommand, "");
+ string_set(&pService->szPrintcommand, "");
+ string_set(&pService->szLppausecommand, "");
+ string_set(&pService->szLpresumecommand, "");
+ string_set(&pService->szQueuepausecommand, "");
+ string_set(&pService->szQueueresumecommand, "");
+
+ string_set(&Globals.szPrintcapname, "cups");
+#else
+ string_set(&pService->szLpqcommand,
+ "/usr/bin/lpstat -o '%p'");
+ string_set(&pService->szLprmcommand,
+ "/usr/bin/cancel '%p-%j'");
+ string_set(&pService->szPrintcommand,
+ "/usr/bin/lp -d '%p' %s; rm %s");
+ string_set(&pService->szLppausecommand,
+ "lp -i '%p-%j' -H hold");
+ string_set(&pService->szLpresumecommand,
+ "lp -i '%p-%j' -H resume");
+ string_set(&pService->szQueuepausecommand,
+ "/usr/bin/disable '%p'");
+ string_set(&pService->szQueueresumecommand,
+ "/usr/bin/enable '%p'");
+ string_set(&Globals.szPrintcapname, "lpstat");
+#endif /* HAVE_CUPS */
+ break;
+
+ case PRINT_SYSV:
+ case PRINT_HPUX:
+ string_set(&pService->szLpqcommand, "lpstat -o%p");
+ string_set(&pService->szLprmcommand, "cancel %p-%j");
+ string_set(&pService->szPrintcommand,
+ "lp -c -d%p %s; rm %s");
+ string_set(&pService->szQueuepausecommand,
+ "disable %p");
+ string_set(&pService->szQueueresumecommand,
+ "enable %p");
+#ifndef HPUX
+ string_set(&pService->szLppausecommand,
+ "lp -i %p-%j -H hold");
+ string_set(&pService->szLpresumecommand,
+ "lp -i %p-%j -H resume");
+#endif /* HPUX */
+ break;
+
+ case PRINT_QNX:
+ string_set(&pService->szLpqcommand, "lpq -P%p");
+ string_set(&pService->szLprmcommand, "lprm -P%p %j");
+ string_set(&pService->szPrintcommand, "lp -r -P%p %s");
+ break;
+
+#ifdef DEVELOPER
+ case PRINT_TEST:
+ case PRINT_VLP:
+ string_set(&pService->szPrintcommand, "vlp print %p %s");
+ string_set(&pService->szLpqcommand, "vlp lpq %p");
+ string_set(&pService->szLprmcommand, "vlp lprm %p %j");
+ string_set(&pService->szLppausecommand, "vlp lppause %p %j");
+ string_set(&pService->szLpresumecommand, "vlp lpresum %p %j");
+ string_set(&pService->szQueuepausecommand, "vlp queuepause %p");
+ string_set(&pService->szQueueresumecommand, "vlp queueresume %p");
+ break;
+#endif /* DEVELOPER */
+
+ }
+}
+
+/***************************************************************************
+ Initialise the global parameter structure.
+***************************************************************************/
+
+static void init_globals(void)
+{
+ static BOOL done_init = False;
+ pstring s;
+
+ if (!done_init) {
+ int i;
+ memset((void *)&Globals, '\0', sizeof(Globals));
+
+ for (i = 0; parm_table[i].label; i++)
+ if ((parm_table[i].type == P_STRING ||
+ parm_table[i].type == P_USTRING) &&
+ parm_table[i].ptr)
+ string_set(parm_table[i].ptr, "");
+
+ string_set(&sDefault.fstype, FSTYPE_STRING);
+
+ done_init = True;
+ }
+
+
+ DEBUG(3, ("Initialising global parameters\n"));
+
+ string_set(&Globals.szConfigBackend, NULL);
+
+ string_set(&Globals.szSMBPasswdFile, dyn_SMB_PASSWD_FILE);
+ string_set(&Globals.szPrivateDir, dyn_PRIVATE_DIR);
+
+ /* use the new 'hash2' method by default, with a prefix of 1 */
+ string_set(&Globals.szManglingMethod, "hash2");
+ Globals.mangle_prefix = 1;
+
+ string_set(&Globals.szGuestaccount, GUEST_ACCOUNT);
+
+ /* using UTF8 by default allows us to support all chars */
+ string_set(&Globals.unix_charset, DEFAULT_UNIX_CHARSET);
+
+#if defined(HAVE_NL_LANGINFO) && defined(CODESET)
+ /* If the system supports nl_langinfo(), try to grab the value
+ from the user's locale */
+ string_set(&Globals.display_charset, "LOCALE");
+#else
+ string_set(&Globals.display_charset, DEFAULT_DISPLAY_CHARSET);
+#endif
+
+ /* Use codepage 850 as a default for the dos character set */
+ string_set(&Globals.dos_charset, DEFAULT_DOS_CHARSET);
+
+ /*
+ * Allow the default PASSWD_CHAT to be overridden in local.h.
+ */
+ string_set(&Globals.szPasswdChat, DEFAULT_PASSWD_CHAT);
+
+ set_global_myname(myhostname());
+ string_set(&Globals.szNetbiosName,global_myname());
+
+ set_global_myworkgroup(WORKGROUP);
+ string_set(&Globals.szWorkgroup, lp_workgroup());
+
+ string_set(&Globals.szPasswdProgram, "");
+ string_set(&Globals.szPrintcapname, PRINTCAP_NAME);
+ string_set(&Globals.szPidDir, dyn_PIDDIR);
+ string_set(&Globals.szLockDir, dyn_LOCKDIR);
+ string_set(&Globals.szSocketAddress, "0.0.0.0");
+ pstrcpy(s, "Samba ");
+ pstrcat(s, SAMBA_VERSION_STRING);
+ string_set(&Globals.szServerString, s);
+ slprintf(s, sizeof(s) - 1, "%d.%d", DEFAULT_MAJOR_VERSION,
+ DEFAULT_MINOR_VERSION);
+ string_set(&Globals.szAnnounceVersion, s);
+
+ pstrcpy(user_socket_options, DEFAULT_SOCKET_OPTIONS);
+
+ string_set(&Globals.szLogonDrive, "");
+ /* %N is the NIS auto.home server if -DAUTOHOME is used, else same as %L */
+ string_set(&Globals.szLogonHome, "\\\\%N\\%U");
+ string_set(&Globals.szLogonPath, "\\\\%N\\%U\\profile");
+
+ string_set(&Globals.szNameResolveOrder, "lmhosts wins host bcast");
+ string_set(&Globals.szPasswordServer, "*");
+
+ Globals.AlgorithmicRidBase = BASE_RID;
+
+ Globals.bLoadPrinters = True;
+ /* Was 65535 (0xFFFF). 0x4101 matches W2K and causes major speed improvements... */
+ /* Discovered by 2 days of pain by Don McCall @ HP :-). */
+ Globals.max_xmit = 0x4104;
+ Globals.max_mux = 50; /* This is *needed* for profile support. */
+ Globals.lpqcachetime = 10;
+ Globals.bDisableSpoolss = False;
+ Globals.iMaxSmbdProcesses = 0;/* no limit specified */
+ Globals.iTotalPrintJobs = 0; /* no limit specified */
+ Globals.pwordlevel = 0;
+ Globals.unamelevel = 0;
+ Globals.deadtime = 0;
+ Globals.bLargeReadwrite = True;
+ Globals.max_log_size = 5000;
+ Globals.max_open_files = MAX_OPEN_FILES;
+ Globals.maxprotocol = PROTOCOL_NT1;
+ Globals.minprotocol = PROTOCOL_CORE;
+ Globals.security = SEC_USER;
+ Globals.paranoid_server_security = True;
+ Globals.bEncryptPasswords = True;
+ Globals.bUpdateEncrypt = False;
+ Globals.clientSchannel = Auto;
+ Globals.serverSchannel = Auto;
+ Globals.bReadRaw = True;
+ Globals.bWriteRaw = True;
+ Globals.bReadbmpx = False;
+ Globals.bNullPasswords = False;
+ Globals.bObeyPamRestrictions = False;
+ Globals.syslog = 1;
+ Globals.bSyslogOnly = False;
+ Globals.bTimestampLogs = True;
+ string_set(&Globals.szLogLevel, "0");
+ Globals.bDebugHiresTimestamp = False;
+ Globals.bDebugPid = False;
+ Globals.bDebugUid = False;
+ Globals.max_ttl = 60 * 60 * 24 * 3; /* 3 days default. */
+ Globals.max_wins_ttl = 60 * 60 * 24 * 6; /* 6 days default. */
+ Globals.min_wins_ttl = 60 * 60 * 6; /* 6 hours default. */
+ Globals.machine_password_timeout = 60 * 60 * 24 * 7; /* 7 days default. */
+ Globals.change_notify_timeout = 60; /* 1 minute default. */
+ Globals.bKernelChangeNotify = True; /* On if we have it. */
+ Globals.lm_announce = 2; /* = Auto: send only if LM clients found */
+ Globals.lm_interval = 60;
+ Globals.announce_as = ANNOUNCE_AS_NT_SERVER;
+#if (defined(HAVE_NETGROUP) && defined(WITH_AUTOMOUNT))
+ Globals.bNISHomeMap = False;
+#ifdef WITH_NISPLUS_HOME
+ string_set(&Globals.szNISHomeMapName, "auto_home.org_dir");
+#else
+ string_set(&Globals.szNISHomeMapName, "auto.home");
+#endif
+#endif
+ Globals.bTimeServer = False;
+ Globals.bBindInterfacesOnly = False;
+ Globals.bUnixPasswdSync = False;
+ Globals.bPamPasswordChange = False;
+ Globals.bPasswdChatDebug = False;
+ Globals.iPasswdChatTimeout = 2; /* 2 second default. */
+ Globals.bUnicode = True; /* Do unicode on the wire by default */
+ Globals.bNTPipeSupport = True; /* Do NT pipes by default. */
+ Globals.bNTStatusSupport = True; /* Use NT status by default. */
+ Globals.bStatCache = True; /* use stat cache by default */
+ Globals.restrict_anonymous = 0;
+ Globals.bClientLanManAuth = True; /* Do use the LanMan hash if it is available */
+ Globals.bClientPlaintextAuth = True; /* Do use a plaintext password if is requested by the server */
+ Globals.bLanmanAuth = True; /* Do use the LanMan hash if it is available */
+ Globals.bNTLMAuth = True; /* Do use NTLMv1 if it is available (otherwise NTLMv2) */
+ Globals.bClientNTLMv2Auth = False; /* Client should not use NTLMv2, as we can't tell that the server supports it. */
+ /* Note, that we will use NTLM2 session security (which is different), if it is available */
+
+ Globals.map_to_guest = 0; /* By Default, "Never" */
+ Globals.min_passwd_length = MINPASSWDLENGTH; /* By Default, 5. */
+ Globals.use_cracklib = False;
+ Globals.oplock_break_wait_time = 0; /* By Default, 0 msecs. */
+ Globals.enhanced_browsing = True;
+ Globals.iLockSpinCount = 3; /* Try 3 times. */
+ Globals.iLockSpinTime = 10; /* usec. */
+#ifdef MMAP_BLACKLIST
+ Globals.bUseMmap = False;
+#else
+ Globals.bUseMmap = True;
+#endif
+ Globals.bUnixExtensions = True;
+
+ /* hostname lookups can be very expensive and are broken on
+ a large number of sites (tridge) */
+ Globals.bHostnameLookups = False;
+
+#ifdef WITH_LDAP_SAMCONFIG
+ string_set(&Globals.szLdapServer, "localhost");
+ Globals.ldap_port = 636;
+ Globals.szPassdbBackend = str_list_make("ldapsam_compat", NULL);
+#else
+ Globals.szPassdbBackend = str_list_make("smbpasswd", NULL);
+#endif /* WITH_LDAP_SAMCONFIG */
+ string_set(&Globals.szGumsBackend, "tdbsam2");
+
+ string_set(&Globals.szLdapSuffix, "");
+ string_set(&Globals.szLdapFilter, "(uid=%u)");
+ string_set(&Globals.szLdapMachineSuffix, "");
+ string_set(&Globals.szLdapUserSuffix, "");
+ string_set(&Globals.szLdapGroupSuffix, "");
+ string_set(&Globals.szLdapIdmapSuffix, "");
+ string_set(&Globals.szLdapPrivilegeSuffix, "");
+
+ string_set(&Globals.szLdapAdminDn, "");
+ Globals.ldap_ssl = LDAP_SSL_ON;
+ Globals.ldap_passwd_sync = LDAP_PASSWD_SYNC_OFF;
+ Globals.ldap_delete_dn = False;
+ Globals.ldap_replication_sleep = 1000; /* wait 1 sec for replication */
+
+/* these parameters are set to defaults that are more appropriate
+ for the increasing samba install base:
+
+ as a member of the workgroup, that will possibly become a
+ _local_ master browser (lm = True). this is opposed to a forced
+ local master browser startup (pm = True).
+
+ doesn't provide WINS server service by default (wsupp = False),
+ and doesn't provide domain master browser services by default, either.
+
+*/
+
+ Globals.bMsAddPrinterWizard = True;
+ Globals.bPreferredMaster = Auto; /* depending on bDomainMaster */
+ Globals.os_level = 20;
+ Globals.bLocalMaster = True;
+ Globals.bDomainMaster = Auto; /* depending on bDomainLogons */
+ Globals.bDomainLogons = False;
+ Globals.bBrowseList = True;
+ Globals.bWINSsupport = False;
+ Globals.bWINSproxy = False;
+
+ Globals.bDNSproxy = True;
+
+ /* this just means to use them if they exist */
+ Globals.bKernelOplocks = True;
+
+ Globals.bAllowTrustedDomains = True;
+
+ string_set(&Globals.szTemplateShell, "/bin/false");
+ string_set(&Globals.szTemplateHomedir, "/home/%D/%U");
+ string_set(&Globals.szTemplatePrimaryGroup, "nobody");
+ string_set(&Globals.szWinbindSeparator, "\\");
+ string_set(&Globals.szAclCompat, "");
+
+ Globals.winbind_cache_time = 300; /* 5 minutes */
+ Globals.bWinbindEnableLocalAccounts = True;
+ Globals.bWinbindEnumUsers = True;
+ Globals.bWinbindEnumGroups = True;
+ Globals.bWinbindUseDefaultDomain = False;
+ Globals.bWinbindTrustedDomainsOnly = False;
+
+ Globals.bEnableRidAlgorithm = True;
+
+ Globals.name_cache_timeout = 660; /* In seconds */
+
+ Globals.bUseSpnego = True;
+ Globals.bClientUseSpnego = True;
+
+ Globals.client_signing = Auto;
+ Globals.server_signing = False;
+
+ string_set(&Globals.smb_ports, SMB_PORTS);
+}
+
+static TALLOC_CTX *lp_talloc;
+
+/******************************************************************* a
+ Free up temporary memory - called from the main loop.
+********************************************************************/
+
+void lp_talloc_free(void)
+{
+ if (!lp_talloc)
+ return;
+ talloc_destroy(lp_talloc);
+ lp_talloc = NULL;
+}
+
+/*******************************************************************
+ 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 char *lp_string(const char *s)
+{
+ char *ret, *tmpstr;
+
+ /* 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 (!lp_talloc)
+ lp_talloc = talloc_init("lp_talloc");
+
+ tmpstr = alloc_sub_basic(current_user_info.smb_name, s);
+ if (trim_char(tmpstr, '\"', '\"')) {
+ if (strchr(tmpstr,'\"') != NULL) {
+ SAFE_FREE(tmpstr);
+ tmpstr = alloc_sub_basic(current_user_info.smb_name,s);
+ }
+ }
+ ret = talloc_strdup(lp_talloc, tmpstr);
+ SAFE_FREE(tmpstr);
+
+ return (ret);
+}
+
+/*
+ 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,ptr) \
+ char *fn_name(void) {return(lp_string(*(char **)(ptr) ? *(char **)(ptr) : ""));}
+#define FN_GLOBAL_CONST_STRING(fn_name,ptr) \
+ const char *fn_name(void) {return(*(const char **)(ptr) ? *(const char **)(ptr) : "");}
+#define FN_GLOBAL_LIST(fn_name,ptr) \
+ const char **fn_name(void) {return(*(const char ***)(ptr));}
+#define FN_GLOBAL_BOOL(fn_name,ptr) \
+ BOOL fn_name(void) {return(*(BOOL *)(ptr));}
+#define FN_GLOBAL_CHAR(fn_name,ptr) \
+ char fn_name(void) {return(*(char *)(ptr));}
+#define FN_GLOBAL_INTEGER(fn_name,ptr) \
+ int fn_name(void) {return(*(int *)(ptr));}
+
+#define FN_LOCAL_STRING(fn_name,val) \
+ char *fn_name(int i) {return(lp_string((LP_SNUM_OK(i) && ServicePtrs[(i)]->val) ? ServicePtrs[(i)]->val : sDefault.val));}
+#define FN_LOCAL_CONST_STRING(fn_name,val) \
+ const char *fn_name(int i) {return (const char *)((LP_SNUM_OK(i) && ServicePtrs[(i)]->val) ? ServicePtrs[(i)]->val : sDefault.val);}
+#define FN_LOCAL_LIST(fn_name,val) \
+ const char **fn_name(int i) {return(const char **)(LP_SNUM_OK(i)? ServicePtrs[(i)]->val : sDefault.val);}
+#define FN_LOCAL_BOOL(fn_name,val) \
+ BOOL fn_name(int i) {return(LP_SNUM_OK(i)? ServicePtrs[(i)]->val : sDefault.val);}
+#define FN_LOCAL_CHAR(fn_name,val) \
+ char fn_name(int i) {return(LP_SNUM_OK(i)? ServicePtrs[(i)]->val : sDefault.val);}
+#define FN_LOCAL_INTEGER(fn_name,val) \
+ int fn_name(int i) {return(LP_SNUM_OK(i)? ServicePtrs[(i)]->val : sDefault.val);}
+
+FN_GLOBAL_STRING(lp_config_backend, &Globals.szConfigBackend)
+FN_GLOBAL_STRING(lp_smb_ports, &Globals.smb_ports)
+FN_GLOBAL_STRING(lp_dos_charset, &Globals.dos_charset)
+FN_GLOBAL_STRING(lp_unix_charset, &Globals.unix_charset)
+FN_GLOBAL_STRING(lp_display_charset, &Globals.display_charset)
+FN_GLOBAL_STRING(lp_logfile, &Globals.szLogFile)
+FN_GLOBAL_STRING(lp_configfile, &Globals.szConfigFile)
+FN_GLOBAL_STRING(lp_smb_passwd_file, &Globals.szSMBPasswdFile)
+FN_GLOBAL_STRING(lp_private_dir, &Globals.szPrivateDir)
+FN_GLOBAL_STRING(lp_serverstring, &Globals.szServerString)
+FN_GLOBAL_STRING(lp_printcapname, &Globals.szPrintcapname)
+FN_GLOBAL_STRING(lp_enumports_cmd, &Globals.szEnumPortsCommand)
+FN_GLOBAL_STRING(lp_addprinter_cmd, &Globals.szAddPrinterCommand)
+FN_GLOBAL_STRING(lp_deleteprinter_cmd, &Globals.szDeletePrinterCommand)
+FN_GLOBAL_STRING(lp_os2_driver_map, &Globals.szOs2DriverMap)
+FN_GLOBAL_STRING(lp_lockdir, &Globals.szLockDir)
+FN_GLOBAL_STRING(lp_piddir, &Globals.szPidDir)
+FN_GLOBAL_STRING(lp_mangling_method, &Globals.szManglingMethod)
+FN_GLOBAL_INTEGER(lp_mangle_prefix, &Globals.mangle_prefix)
+FN_GLOBAL_STRING(lp_utmpdir, &Globals.szUtmpDir)
+FN_GLOBAL_STRING(lp_wtmpdir, &Globals.szWtmpDir)
+FN_GLOBAL_BOOL(lp_utmp, &Globals.bUtmp)
+FN_GLOBAL_STRING(lp_rootdir, &Globals.szRootdir)
+FN_GLOBAL_STRING(lp_defaultservice, &Globals.szDefaultService)
+FN_GLOBAL_STRING(lp_msg_command, &Globals.szMsgCommand)
+FN_GLOBAL_STRING(lp_dfree_command, &Globals.szDfree)
+FN_GLOBAL_STRING(lp_get_quota_command, &Globals.szGetQuota)
+FN_GLOBAL_STRING(lp_set_quota_command, &Globals.szSetQuota)
+FN_GLOBAL_STRING(lp_hosts_equiv, &Globals.szHostsEquiv)
+FN_GLOBAL_STRING(lp_auto_services, &Globals.szAutoServices)
+FN_GLOBAL_STRING(lp_passwd_program, &Globals.szPasswdProgram)
+FN_GLOBAL_STRING(lp_passwd_chat, &Globals.szPasswdChat)
+FN_GLOBAL_STRING(lp_passwordserver, &Globals.szPasswordServer)
+FN_GLOBAL_STRING(lp_name_resolve_order, &Globals.szNameResolveOrder)
+FN_GLOBAL_STRING(lp_realm, &Globals.szRealm)
+FN_GLOBAL_STRING(lp_afs_username_map, &Globals.szAfsUsernameMap)
+FN_GLOBAL_STRING(lp_username_map, &Globals.szUsernameMap)
+FN_GLOBAL_CONST_STRING(lp_logon_script, &Globals.szLogonScript)
+FN_GLOBAL_CONST_STRING(lp_logon_path, &Globals.szLogonPath)
+FN_GLOBAL_CONST_STRING(lp_logon_drive, &Globals.szLogonDrive)
+FN_GLOBAL_CONST_STRING(lp_logon_home, &Globals.szLogonHome)
+FN_GLOBAL_STRING(lp_remote_announce, &Globals.szRemoteAnnounce)
+FN_GLOBAL_STRING(lp_remote_browse_sync, &Globals.szRemoteBrowseSync)
+FN_GLOBAL_LIST(lp_wins_server_list, &Globals.szWINSservers)
+FN_GLOBAL_LIST(lp_interfaces, &Globals.szInterfaces)
+FN_GLOBAL_STRING(lp_socket_address, &Globals.szSocketAddress)
+FN_GLOBAL_STRING(lp_nis_home_map_name, &Globals.szNISHomeMapName)
+static FN_GLOBAL_STRING(lp_announce_version, &Globals.szAnnounceVersion)
+FN_GLOBAL_LIST(lp_netbios_aliases, &Globals.szNetbiosAliases)
+FN_GLOBAL_LIST(lp_passdb_backend, &Globals.szPassdbBackend)
+FN_GLOBAL_STRING(lp_gums_backend, &Globals.szGumsBackend)
+FN_GLOBAL_LIST(lp_preload_modules, &Globals.szPreloadModules)
+FN_GLOBAL_STRING(lp_panic_action, &Globals.szPanicAction)
+FN_GLOBAL_STRING(lp_adduser_script, &Globals.szAddUserScript)
+FN_GLOBAL_STRING(lp_deluser_script, &Globals.szDelUserScript)
+
+FN_GLOBAL_CONST_STRING(lp_guestaccount, &Globals.szGuestaccount)
+FN_GLOBAL_STRING(lp_addgroup_script, &Globals.szAddGroupScript)
+FN_GLOBAL_STRING(lp_delgroup_script, &Globals.szDelGroupScript)
+FN_GLOBAL_STRING(lp_addusertogroup_script, &Globals.szAddUserToGroupScript)
+FN_GLOBAL_STRING(lp_deluserfromgroup_script, &Globals.szDelUserFromGroupScript)
+FN_GLOBAL_STRING(lp_setprimarygroup_script, &Globals.szSetPrimaryGroupScript)
+
+FN_GLOBAL_STRING(lp_addmachine_script, &Globals.szAddMachineScript)
+
+FN_GLOBAL_STRING(lp_shutdown_script, &Globals.szShutdownScript)
+FN_GLOBAL_STRING(lp_abort_shutdown_script, &Globals.szAbortShutdownScript)
+
+FN_GLOBAL_STRING(lp_wins_hook, &Globals.szWINSHook)
+FN_GLOBAL_STRING(lp_wins_partners, &Globals.szWINSPartners)
+FN_GLOBAL_STRING(lp_template_primary_group, &Globals.szTemplatePrimaryGroup)
+FN_GLOBAL_CONST_STRING(lp_template_homedir, &Globals.szTemplateHomedir)
+FN_GLOBAL_CONST_STRING(lp_template_shell, &Globals.szTemplateShell)
+FN_GLOBAL_CONST_STRING(lp_winbind_separator, &Globals.szWinbindSeparator)
+FN_GLOBAL_STRING(lp_acl_compatibility, &Globals.szAclCompat)
+FN_GLOBAL_BOOL(lp_winbind_enable_local_accounts, &Globals.bWinbindEnableLocalAccounts)
+FN_GLOBAL_BOOL(lp_winbind_enum_users, &Globals.bWinbindEnumUsers)
+FN_GLOBAL_BOOL(lp_winbind_enum_groups, &Globals.bWinbindEnumGroups)
+FN_GLOBAL_BOOL(lp_winbind_use_default_domain, &Globals.bWinbindUseDefaultDomain)
+FN_GLOBAL_BOOL(lp_winbind_trusted_domains_only, &Globals.bWinbindTrustedDomainsOnly)
+
+FN_GLOBAL_STRING(lp_idmap_backend, &Globals.szIdmapBackend)
+FN_GLOBAL_BOOL(lp_enable_rid_algorithm, &Globals.bEnableRidAlgorithm)
+
+#ifdef WITH_LDAP_SAMCONFIG
+FN_GLOBAL_STRING(lp_ldap_server, &Globals.szLdapServer)
+FN_GLOBAL_INTEGER(lp_ldap_port, &Globals.ldap_port)
+#endif
+FN_GLOBAL_STRING(lp_ldap_suffix, &Globals.szLdapSuffix)
+FN_GLOBAL_STRING(lp_ldap_filter, &Globals.szLdapFilter)
+FN_GLOBAL_STRING(lp_ldap_admin_dn, &Globals.szLdapAdminDn)
+FN_GLOBAL_INTEGER(lp_ldap_ssl, &Globals.ldap_ssl)
+FN_GLOBAL_INTEGER(lp_ldap_passwd_sync, &Globals.ldap_passwd_sync)
+FN_GLOBAL_BOOL(lp_ldap_delete_dn, &Globals.ldap_delete_dn)
+FN_GLOBAL_INTEGER(lp_ldap_replication_sleep, &Globals.ldap_replication_sleep)
+FN_GLOBAL_STRING(lp_add_share_cmd, &Globals.szAddShareCommand)
+FN_GLOBAL_STRING(lp_change_share_cmd, &Globals.szChangeShareCommand)
+FN_GLOBAL_STRING(lp_delete_share_cmd, &Globals.szDeleteShareCommand)
+
+FN_GLOBAL_BOOL(lp_disable_netbios, &Globals.bDisableNetbios)
+FN_GLOBAL_BOOL(lp_ms_add_printer_wizard, &Globals.bMsAddPrinterWizard)
+FN_GLOBAL_BOOL(lp_dns_proxy, &Globals.bDNSproxy)
+FN_GLOBAL_BOOL(lp_wins_support, &Globals.bWINSsupport)
+FN_GLOBAL_BOOL(lp_we_are_a_wins_server, &Globals.bWINSsupport)
+FN_GLOBAL_BOOL(lp_wins_proxy, &Globals.bWINSproxy)
+FN_GLOBAL_BOOL(lp_local_master, &Globals.bLocalMaster)
+FN_GLOBAL_BOOL(lp_domain_logons, &Globals.bDomainLogons)
+FN_GLOBAL_BOOL(lp_load_printers, &Globals.bLoadPrinters)
+FN_GLOBAL_BOOL(lp_readbmpx, &Globals.bReadbmpx)
+FN_GLOBAL_BOOL(lp_readraw, &Globals.bReadRaw)
+FN_GLOBAL_BOOL(lp_large_readwrite, &Globals.bLargeReadwrite)
+FN_GLOBAL_BOOL(lp_writeraw, &Globals.bWriteRaw)
+FN_GLOBAL_BOOL(lp_null_passwords, &Globals.bNullPasswords)
+FN_GLOBAL_BOOL(lp_obey_pam_restrictions, &Globals.bObeyPamRestrictions)
+FN_GLOBAL_BOOL(lp_encrypted_passwords, &Globals.bEncryptPasswords)
+FN_GLOBAL_BOOL(lp_update_encrypted, &Globals.bUpdateEncrypt)
+FN_GLOBAL_INTEGER(lp_client_schannel, &Globals.clientSchannel)
+FN_GLOBAL_INTEGER(lp_server_schannel, &Globals.serverSchannel)
+FN_GLOBAL_BOOL(lp_syslog_only, &Globals.bSyslogOnly)
+FN_GLOBAL_BOOL(lp_timestamp_logs, &Globals.bTimestampLogs)
+FN_GLOBAL_BOOL(lp_debug_hires_timestamp, &Globals.bDebugHiresTimestamp)
+FN_GLOBAL_BOOL(lp_debug_pid, &Globals.bDebugPid)
+FN_GLOBAL_BOOL(lp_debug_uid, &Globals.bDebugUid)
+FN_GLOBAL_BOOL(lp_browse_list, &Globals.bBrowseList)
+FN_GLOBAL_BOOL(lp_nis_home_map, &Globals.bNISHomeMap)
+static FN_GLOBAL_BOOL(lp_time_server, &Globals.bTimeServer)
+FN_GLOBAL_BOOL(lp_bind_interfaces_only, &Globals.bBindInterfacesOnly)
+FN_GLOBAL_BOOL(lp_pam_password_change, &Globals.bPamPasswordChange)
+FN_GLOBAL_BOOL(lp_unix_password_sync, &Globals.bUnixPasswdSync)
+FN_GLOBAL_BOOL(lp_passwd_chat_debug, &Globals.bPasswdChatDebug)
+FN_GLOBAL_INTEGER(lp_passwd_chat_timeout, &Globals.iPasswdChatTimeout)
+FN_GLOBAL_BOOL(lp_unicode, &Globals.bUnicode)
+FN_GLOBAL_BOOL(lp_nt_pipe_support, &Globals.bNTPipeSupport)
+FN_GLOBAL_BOOL(lp_nt_status_support, &Globals.bNTStatusSupport)
+FN_GLOBAL_BOOL(lp_stat_cache, &Globals.bStatCache)
+FN_GLOBAL_BOOL(lp_allow_trusted_domains, &Globals.bAllowTrustedDomains)
+FN_GLOBAL_INTEGER(lp_restrict_anonymous, &Globals.restrict_anonymous)
+FN_GLOBAL_BOOL(lp_lanman_auth, &Globals.bLanmanAuth)
+FN_GLOBAL_BOOL(lp_ntlm_auth, &Globals.bNTLMAuth)
+FN_GLOBAL_BOOL(lp_client_plaintext_auth, &Globals.bClientPlaintextAuth)
+FN_GLOBAL_BOOL(lp_client_lanman_auth, &Globals.bClientLanManAuth)
+FN_GLOBAL_BOOL(lp_client_ntlmv2_auth, &Globals.bClientNTLMv2Auth)
+FN_GLOBAL_BOOL(lp_host_msdfs, &Globals.bHostMSDfs)
+FN_GLOBAL_BOOL(lp_kernel_oplocks, &Globals.bKernelOplocks)
+FN_GLOBAL_BOOL(lp_enhanced_browsing, &Globals.enhanced_browsing)
+FN_GLOBAL_BOOL(lp_use_mmap, &Globals.bUseMmap)
+FN_GLOBAL_BOOL(lp_unix_extensions, &Globals.bUnixExtensions)
+FN_GLOBAL_BOOL(lp_use_spnego, &Globals.bUseSpnego)
+FN_GLOBAL_BOOL(lp_client_use_spnego, &Globals.bClientUseSpnego)
+FN_GLOBAL_BOOL(lp_hostname_lookups, &Globals.bHostnameLookups)
+FN_GLOBAL_BOOL(lp_kernel_change_notify, &Globals.bKernelChangeNotify)
+FN_GLOBAL_INTEGER(lp_os_level, &Globals.os_level)
+FN_GLOBAL_INTEGER(lp_max_ttl, &Globals.max_ttl)
+FN_GLOBAL_INTEGER(lp_max_wins_ttl, &Globals.max_wins_ttl)
+FN_GLOBAL_INTEGER(lp_min_wins_ttl, &Globals.min_wins_ttl)
+FN_GLOBAL_INTEGER(lp_max_log_size, &Globals.max_log_size)
+FN_GLOBAL_INTEGER(lp_max_open_files, &Globals.max_open_files)
+FN_GLOBAL_INTEGER(lp_maxxmit, &Globals.max_xmit)
+FN_GLOBAL_INTEGER(lp_maxmux, &Globals.max_mux)
+FN_GLOBAL_INTEGER(lp_passwordlevel, &Globals.pwordlevel)
+FN_GLOBAL_INTEGER(lp_usernamelevel, &Globals.unamelevel)
+FN_GLOBAL_INTEGER(lp_deadtime, &Globals.deadtime)
+FN_GLOBAL_INTEGER(lp_maxprotocol, &Globals.maxprotocol)
+FN_GLOBAL_INTEGER(lp_minprotocol, &Globals.minprotocol)
+FN_GLOBAL_INTEGER(lp_security, &Globals.security)
+FN_GLOBAL_LIST(lp_auth_methods, &Globals.AuthMethods)
+FN_GLOBAL_BOOL(lp_paranoid_server_security, &Globals.paranoid_server_security)
+FN_GLOBAL_INTEGER(lp_maxdisksize, &Globals.maxdisksize)
+FN_GLOBAL_INTEGER(lp_lpqcachetime, &Globals.lpqcachetime)
+FN_GLOBAL_INTEGER(lp_max_smbd_processes, &Globals.iMaxSmbdProcesses)
+FN_GLOBAL_INTEGER(lp_disable_spoolss, &Globals.bDisableSpoolss)
+FN_GLOBAL_INTEGER(lp_syslog, &Globals.syslog)
+static FN_GLOBAL_INTEGER(lp_announce_as, &Globals.announce_as)
+FN_GLOBAL_INTEGER(lp_lm_announce, &Globals.lm_announce)
+FN_GLOBAL_INTEGER(lp_lm_interval, &Globals.lm_interval)
+FN_GLOBAL_INTEGER(lp_machine_password_timeout, &Globals.machine_password_timeout)
+FN_GLOBAL_INTEGER(lp_change_notify_timeout, &Globals.change_notify_timeout)
+FN_GLOBAL_INTEGER(lp_map_to_guest, &Globals.map_to_guest)
+FN_GLOBAL_INTEGER(lp_min_passwd_length, &Globals.min_passwd_length)
+FN_GLOBAL_BOOL(lp_use_cracklib, &Globals.use_cracklib)
+FN_GLOBAL_INTEGER(lp_oplock_break_wait_time, &Globals.oplock_break_wait_time)
+FN_GLOBAL_INTEGER(lp_lock_spin_count, &Globals.iLockSpinCount)
+FN_GLOBAL_INTEGER(lp_lock_sleep_time, &Globals.iLockSpinTime)
+FN_LOCAL_STRING(lp_preexec, szPreExec)
+FN_LOCAL_STRING(lp_postexec, szPostExec)
+FN_LOCAL_STRING(lp_rootpreexec, szRootPreExec)
+FN_LOCAL_STRING(lp_rootpostexec, szRootPostExec)
+FN_LOCAL_STRING(lp_servicename, szService)
+FN_LOCAL_CONST_STRING(lp_const_servicename, szService)
+FN_LOCAL_STRING(lp_pathname, szPath)
+FN_LOCAL_STRING(lp_dontdescend, szDontdescend)
+FN_LOCAL_STRING(lp_username, szUsername)
+FN_LOCAL_LIST(lp_invalid_users, szInvalidUsers)
+FN_LOCAL_LIST(lp_valid_users, szValidUsers)
+FN_LOCAL_LIST(lp_admin_users, szAdminUsers)
+FN_LOCAL_STRING(lp_printcommand, szPrintcommand)
+FN_LOCAL_STRING(lp_lpqcommand, szLpqcommand)
+FN_LOCAL_STRING(lp_lprmcommand, szLprmcommand)
+FN_LOCAL_STRING(lp_lppausecommand, szLppausecommand)
+FN_LOCAL_STRING(lp_lpresumecommand, szLpresumecommand)
+FN_LOCAL_STRING(lp_queuepausecommand, szQueuepausecommand)
+FN_LOCAL_STRING(lp_queueresumecommand, szQueueresumecommand)
+static FN_LOCAL_STRING(_lp_printername, szPrintername)
+FN_LOCAL_LIST(lp_hostsallow, szHostsallow)
+FN_LOCAL_LIST(lp_hostsdeny, szHostsdeny)
+FN_LOCAL_STRING(lp_magicscript, szMagicScript)
+FN_LOCAL_STRING(lp_magicoutput, szMagicOutput)
+FN_LOCAL_STRING(lp_comment, comment)
+FN_LOCAL_STRING(lp_force_user, force_user)
+FN_LOCAL_STRING(lp_force_group, force_group)
+FN_LOCAL_LIST(lp_readlist, readlist)
+FN_LOCAL_LIST(lp_writelist, writelist)
+FN_LOCAL_LIST(lp_printer_admin, printer_admin)
+FN_LOCAL_STRING(lp_fstype, fstype)
+FN_LOCAL_LIST(lp_vfs_objects, szVfsObjects)
+FN_LOCAL_STRING(lp_msdfs_proxy, szMSDfsProxy)
+static FN_LOCAL_STRING(lp_volume, volume)
+FN_LOCAL_STRING(lp_mangled_map, szMangledMap)
+FN_LOCAL_STRING(lp_veto_files, szVetoFiles)
+FN_LOCAL_STRING(lp_hide_files, szHideFiles)
+FN_LOCAL_STRING(lp_veto_oplocks, szVetoOplockFiles)
+FN_LOCAL_BOOL(lp_msdfs_root, bMSDfsRoot)
+FN_LOCAL_BOOL(lp_autoloaded, autoloaded)
+FN_LOCAL_BOOL(lp_preexec_close, bPreexecClose)
+FN_LOCAL_BOOL(lp_rootpreexec_close, bRootpreexecClose)
+FN_LOCAL_BOOL(lp_casesensitive, bCaseSensitive)
+FN_LOCAL_BOOL(lp_preservecase, bCasePreserve)
+FN_LOCAL_BOOL(lp_shortpreservecase, bShortCasePreserve)
+FN_LOCAL_BOOL(lp_casemangle, bCaseMangle)
+FN_LOCAL_BOOL(lp_hide_dot_files, bHideDotFiles)
+FN_LOCAL_BOOL(lp_hide_special_files, bHideSpecialFiles)
+FN_LOCAL_BOOL(lp_hideunreadable, bHideUnReadable)
+FN_LOCAL_BOOL(lp_hideunwriteable_files, bHideUnWriteableFiles)
+FN_LOCAL_BOOL(lp_browseable, bBrowseable)
+FN_LOCAL_BOOL(lp_readonly, bRead_only)
+FN_LOCAL_BOOL(lp_no_set_dir, bNo_set_dir)
+FN_LOCAL_BOOL(lp_guest_ok, bGuest_ok)
+FN_LOCAL_BOOL(lp_guest_only, bGuest_only)
+FN_LOCAL_BOOL(lp_print_ok, bPrint_ok)
+FN_LOCAL_BOOL(lp_map_hidden, bMap_hidden)
+FN_LOCAL_BOOL(lp_map_archive, bMap_archive)
+FN_LOCAL_BOOL(lp_store_dos_attributes, bStoreDosAttributes)
+FN_LOCAL_BOOL(lp_locking, bLocking)
+FN_LOCAL_BOOL(lp_strict_locking, bStrictLocking)
+FN_LOCAL_BOOL(lp_posix_locking, bPosixLocking)
+FN_LOCAL_BOOL(lp_share_modes, bShareModes)
+FN_LOCAL_BOOL(lp_oplocks, bOpLocks)
+FN_LOCAL_BOOL(lp_level2_oplocks, bLevel2OpLocks)
+FN_LOCAL_BOOL(lp_onlyuser, bOnlyUser)
+FN_LOCAL_BOOL(lp_manglednames, bMangledNames)
+FN_LOCAL_BOOL(lp_widelinks, bWidelinks)
+FN_LOCAL_BOOL(lp_symlinks, bSymlinks)
+FN_LOCAL_BOOL(lp_syncalways, bSyncAlways)
+FN_LOCAL_BOOL(lp_strict_allocate, bStrictAllocate)
+FN_LOCAL_BOOL(lp_strict_sync, bStrictSync)
+FN_LOCAL_BOOL(lp_map_system, bMap_system)
+FN_LOCAL_BOOL(lp_delete_readonly, bDeleteReadonly)
+FN_LOCAL_BOOL(lp_fake_oplocks, bFakeOplocks)
+FN_LOCAL_BOOL(lp_recursive_veto_delete, bDeleteVetoFiles)
+FN_LOCAL_BOOL(lp_dos_filemode, bDosFilemode)
+FN_LOCAL_BOOL(lp_dos_filetimes, bDosFiletimes)
+FN_LOCAL_BOOL(lp_dos_filetime_resolution, bDosFiletimeResolution)
+FN_LOCAL_BOOL(lp_fake_dir_create_times, bFakeDirCreateTimes)
+FN_LOCAL_BOOL(lp_blocking_locks, bBlockingLocks)
+FN_LOCAL_BOOL(lp_inherit_perms, bInheritPerms)
+FN_LOCAL_BOOL(lp_inherit_acls, bInheritACLS)
+FN_LOCAL_BOOL(lp_use_client_driver, bUseClientDriver)
+FN_LOCAL_BOOL(lp_default_devmode, bDefaultDevmode)
+FN_LOCAL_BOOL(lp_nt_acl_support, bNTAclSupport)
+FN_LOCAL_BOOL(lp_ea_support, bEASupport)
+FN_LOCAL_BOOL(_lp_use_sendfile, bUseSendfile)
+FN_LOCAL_BOOL(lp_profile_acls, bProfileAcls)
+FN_LOCAL_BOOL(lp_map_acl_inherit, bMap_acl_inherit)
+FN_LOCAL_BOOL(lp_afs_share, bAfs_Share)
+FN_LOCAL_INTEGER(lp_create_mask, iCreate_mask)
+FN_LOCAL_INTEGER(lp_force_create_mode, iCreate_force_mode)
+FN_LOCAL_INTEGER(lp_security_mask, iSecurity_mask)
+FN_LOCAL_INTEGER(lp_force_security_mode, iSecurity_force_mode)
+FN_LOCAL_INTEGER(lp_dir_mask, iDir_mask)
+FN_LOCAL_INTEGER(lp_force_dir_mode, iDir_force_mode)
+FN_LOCAL_INTEGER(lp_dir_security_mask, iDir_Security_mask)
+FN_LOCAL_INTEGER(lp_force_dir_security_mode, iDir_Security_force_mode)
+FN_LOCAL_INTEGER(lp_max_connections, iMaxConnections)
+FN_LOCAL_INTEGER(lp_defaultcase, iDefaultCase)
+FN_LOCAL_INTEGER(lp_minprintspace, iMinPrintSpace)
+FN_LOCAL_INTEGER(lp_printing, iPrinting)
+FN_LOCAL_INTEGER(lp_max_reported_jobs, iMaxReportedPrintJobs)
+FN_LOCAL_INTEGER(lp_oplock_contention_limit, iOplockContentionLimit)
+FN_LOCAL_INTEGER(lp_csc_policy, iCSCPolicy)
+FN_LOCAL_INTEGER(lp_write_cache_size, iWriteCacheSize)
+FN_LOCAL_INTEGER(lp_block_size, iBlock_size)
+FN_LOCAL_CHAR(lp_magicchar, magic_char)
+FN_GLOBAL_INTEGER(lp_winbind_cache_time, &Globals.winbind_cache_time)
+FN_GLOBAL_INTEGER(lp_algorithmic_rid_base, &Globals.AlgorithmicRidBase)
+FN_GLOBAL_INTEGER(lp_name_cache_timeout, &Globals.name_cache_timeout)
+FN_GLOBAL_INTEGER(lp_client_signing, &Globals.client_signing)
+FN_GLOBAL_INTEGER(lp_server_signing, &Globals.server_signing)
+
+/* local prototypes */
+
+static int map_parameter(const char *pszParmName);
+static BOOL set_boolean(BOOL *pb, const char *pszParmValue);
+static int getservicebyname(const char *pszServiceName,
+ service * pserviceDest);
+static void copy_service(service * pserviceDest,
+ service * pserviceSource, BOOL *pcopymapDest);
+static BOOL service_ok(int iService);
+static BOOL do_parameter(const char *pszParmName, const char *pszParmValue);
+static BOOL do_section(const char *pszSectionName);
+static void init_copymap(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 */
+static param_opt_struct *get_parametrics(int snum, const char *type, const char *option)
+{
+ BOOL global_section = False;
+ char* param_key;
+ param_opt_struct *data;
+
+ if (snum >= iNumServices) return NULL;
+
+ if (snum < 0) {
+ data = Globals.param_opt;
+ global_section = True;
+ } else {
+ data = ServicePtrs[snum]->param_opt;
+ }
+
+ asprintf(&param_key, "%s:%s", type, option);
+ if (!param_key) {
+ DEBUG(0,("asprintf failed!\n"));
+ return NULL;
+ }
+
+ while (data) {
+ if (strcmp(data->key, param_key) == 0) {
+ string_free(&param_key);
+ return data;
+ }
+ data = data->next;
+ }
+
+ if (!global_section) {
+ /* Try to fetch the same option but from globals */
+ /* but only if we are not already working with Globals */
+ data = Globals.param_opt;
+ while (data) {
+ if (strcmp(data->key, param_key) == 0) {
+ string_free(&param_key);
+ return data;
+ }
+ data = data->next;
+ }
+ }
+
+ string_free(&param_key);
+
+ 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 atoi(s);
+}
+
+/*******************************************************************
+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, 10);
+}
+
+/*******************************************************************
+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(&ret,s)) {
+ DEBUG(0,("lp_bool(%s): value is not boolean!\n",s));
+ return False;
+ }
+
+ return ret;
+}
+
+/*******************************************************************
+convenience routine to return enum parameters.
+********************************************************************/
+static int lp_enum(const char *s,const struct enum_list *_enum)
+{
+ int i;
+
+ if (!s || !_enum) {
+ DEBUG(0,("lp_enum(%s,enum): is called with NULL!\n",s));
+ return (-1);
+ }
+
+ for (i=0; _enum[i].name; i++) {
+ if (strequal(_enum[i].name,s))
+ return _enum[i].value;
+ }
+
+ DEBUG(0,("lp_enum(%s,enum): value is not in enum_list!\n",s));
+ return (-1);
+}
+
+
+/* DO NOT USE lp_parm_string ANYMORE!!!!
+ * use lp_parm_const_string or lp_parm_talloc_string
+ *
+ * lp_parm_string is only used to let old modules find this symbol
+ */
+#undef lp_parm_string
+ char *lp_parm_string(const char *servicename, const char *type, const char *option)
+{
+ return lp_parm_talloc_string(lp_servicenumber(servicename), type, option, NULL);
+}
+
+/* Return parametric option from a given service. Type is a part of option before ':' */
+/* Parametric option has following syntax: 'Type: option = value' */
+/* the returned value is talloced in lp_talloc */
+char *lp_parm_talloc_string(int snum, const char *type, const char *option, const char *def)
+{
+ param_opt_struct *data = get_parametrics(snum, type, option);
+
+ if (data == NULL||data->value==NULL) {
+ if (def) {
+ return lp_string(def);
+ } else {
+ return NULL;
+ }
+ }
+
+ return lp_string(data->value);
+}
+
+/* Return parametric option from a given service. Type is a part of option before ':' */
+/* Parametric option has following syntax: 'Type: option = value' */
+const char *lp_parm_const_string(int snum, const char *type, const char *option, const char *def)
+{
+ param_opt_struct *data = get_parametrics(snum, type, option);
+
+ if (data == NULL||data->value==NULL)
+ return def;
+
+ return data->value;
+}
+
+/* Return parametric option from a given service. Type is a part of option before ':' */
+/* Parametric option has following syntax: 'Type: option = value' */
+
+const char **lp_parm_string_list(int snum, const char *type, const char *option, const char **def)
+{
+ param_opt_struct *data = get_parametrics(snum, type, option);
+
+ if (data == NULL||data->value==NULL)
+ return (const char **)def;
+
+ if (data->list==NULL) {
+ data->list = str_list_make(data->value, NULL);
+ }
+
+ return (const char **)data->list;
+}
+
+/* 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(int snum, const char *type, const char *option, int def)
+{
+ param_opt_struct *data = get_parametrics(snum, type, option);
+
+ if (data && data->value && *data->value)
+ return lp_int(data->value);
+
+ return def;
+}
+
+/* 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(int snum, const char *type, const char *option, unsigned long def)
+{
+ param_opt_struct *data = get_parametrics(snum, type, option);
+
+ if (data && data->value && *data->value)
+ return lp_ulong(data->value);
+
+ return def;
+}
+
+/* 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(int snum, const char *type, const char *option, BOOL def)
+{
+ param_opt_struct *data = get_parametrics(snum, type, option);
+
+ if (data && data->value && *data->value)
+ return lp_bool(data->value);
+
+ return def;
+}
+
+/* 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_enum(int snum, const char *type, const char *option,
+ const struct enum_list *_enum, int def)
+{
+ param_opt_struct *data = get_parametrics(snum, type, option);
+
+ if (data && data->value && *data->value && _enum)
+ return lp_enum(data->value, _enum);
+
+ return def;
+}
+
+
+/***************************************************************************
+ Initialise a service to the defaults.
+***************************************************************************/
+
+static void init_service(service * pservice)
+{
+ memset((char *)pservice, '\0', sizeof(service));
+ copy_service(pservice, &sDefault, NULL);
+}
+
+/***************************************************************************
+ Free the dynamically allocated parts of a service struct.
+***************************************************************************/
+
+static void free_service(service *pservice)
+{
+ int i;
+ param_opt_struct *data, *pdata;
+ if (!pservice)
+ return;
+
+ if (pservice->szService)
+ DEBUG(5, ("free_service: Freeing service %s\n",
+ pservice->szService));
+
+ string_free(&pservice->szService);
+ SAFE_FREE(pservice->copymap);
+
+ for (i = 0; parm_table[i].label; i++) {
+ if ((parm_table[i].type == P_STRING ||
+ parm_table[i].type == P_USTRING) &&
+ parm_table[i].class == P_LOCAL)
+ string_free((char **)
+ (((char *)pservice) +
+ PTR_DIFF(parm_table[i].ptr, &sDefault)));
+ else if (parm_table[i].type == P_LIST &&
+ parm_table[i].class == P_LOCAL)
+ str_list_free((char ***)
+ (((char *)pservice) +
+ PTR_DIFF(parm_table[i].ptr, &sDefault)));
+ }
+
+ data = pservice->param_opt;
+ if (data)
+ DEBUG(5,("Freeing parametrics:\n"));
+ while (data) {
+ DEBUG(5,("[%s = %s]\n", data->key, data->value));
+ string_free(&data->key);
+ string_free(&data->value);
+ str_list_free(&data->list);
+ pdata = data->next;
+ SAFE_FREE(data);
+ data = pdata;
+ }
+
+ ZERO_STRUCTP(pservice);
+}
+
+/***************************************************************************
+ Add a new service to the services array initialising it with the given
+ service.
+***************************************************************************/
+
+static int add_a_service(const service *pservice, const char *name)
+{
+ int i;
+ service tservice;
+ int num_to_alloc = iNumServices + 1;
+ param_opt_struct *data, *pdata;
+
+ tservice = *pservice;
+
+ /* it might already exist */
+ if (name) {
+ i = getservicebyname(name, NULL);
+ if (i >= 0) {
+ /* Clean all parametric options for service */
+ /* They will be added during parsing again */
+ data = ServicePtrs[i]->param_opt;
+ while (data) {
+ string_free(&data->key);
+ string_free(&data->value);
+ str_list_free(&data->list);
+ pdata = data->next;
+ SAFE_FREE(data);
+ data = pdata;
+ }
+ ServicePtrs[i]->param_opt = NULL;
+ return (i);
+ }
+ }
+
+ /* find an invalid one */
+ for (i = 0; i < iNumServices; i++)
+ if (!ServicePtrs[i]->valid)
+ break;
+
+ /* if not, then create one */
+ if (i == iNumServices) {
+ service **tsp;
+
+ tsp = (service **) Realloc(ServicePtrs,
+ sizeof(service *) *
+ num_to_alloc);
+
+ if (!tsp) {
+ DEBUG(0,("add_a_service: failed to enlarge ServicePtrs!\n"));
+ return (-1);
+ }
+ else {
+ ServicePtrs = tsp;
+ ServicePtrs[iNumServices] =
+ (service *) malloc(sizeof(service));
+ }
+ if (!ServicePtrs[iNumServices]) {
+ DEBUG(0,("add_a_service: out of memory!\n"));
+ return (-1);
+ }
+
+ iNumServices++;
+ } else
+ free_service(ServicePtrs[i]);
+
+ ServicePtrs[i]->valid = True;
+
+ init_service(ServicePtrs[i]);
+ copy_service(ServicePtrs[i], &tservice, NULL);
+ if (name)
+ string_set(&ServicePtrs[i]->szService, name);
+ return (i);
+}
+
+/***************************************************************************
+ Add a new home service, with the specified home directory, defaults coming
+ from service ifrom.
+***************************************************************************/
+
+BOOL lp_add_home(const char *pszHomename, int iDefaultService,
+ const char *user, const char *pszHomedir)
+{
+ int i;
+ pstring newHomedir;
+
+ i = add_a_service(ServicePtrs[iDefaultService], pszHomename);
+
+ if (i < 0)
+ return (False);
+
+ if (!(*(ServicePtrs[iDefaultService]->szPath))
+ || strequal(ServicePtrs[iDefaultService]->szPath, lp_pathname(GLOBAL_SECTION_SNUM))) {
+ pstrcpy(newHomedir, pszHomedir);
+ string_set(&ServicePtrs[i]->szPath, newHomedir);
+ }
+
+ if (!(*(ServicePtrs[i]->comment))) {
+ pstring comment;
+ slprintf(comment, sizeof(comment) - 1,
+ "Home directory of %s", user);
+ string_set(&ServicePtrs[i]->comment, comment);
+ }
+
+ /* set the browseable flag from the gloabl default */
+
+ ServicePtrs[i]->bBrowseable = sDefault.bBrowseable;
+
+ DEBUG(3, ("adding home's share [%s] for user '%s' at '%s'\n", pszHomename,
+ user, newHomedir));
+
+ return (True);
+}
+
+/***************************************************************************
+ Add a new service, based on an old one.
+***************************************************************************/
+
+int lp_add_service(const char *pszService, int iDefaultService)
+{
+ return (add_a_service(ServicePtrs[iDefaultService], pszService));
+}
+
+/***************************************************************************
+ Add the IPC service.
+***************************************************************************/
+
+static BOOL lp_add_ipc(const char *ipc_name, BOOL guest_ok)
+{
+ pstring comment;
+ int i = add_a_service(&sDefault, ipc_name);
+
+ if (i < 0)
+ return (False);
+
+ slprintf(comment, sizeof(comment) - 1,
+ "IPC Service (%s)", Globals.szServerString);
+
+ string_set(&ServicePtrs[i]->szPath, tmpdir());
+ string_set(&ServicePtrs[i]->szUsername, "");
+ string_set(&ServicePtrs[i]->comment, comment);
+ string_set(&ServicePtrs[i]->fstype, "IPC");
+ ServicePtrs[i]->iMaxConnections = 0;
+ ServicePtrs[i]->bAvailable = True;
+ ServicePtrs[i]->bRead_only = True;
+ ServicePtrs[i]->bGuest_only = False;
+ ServicePtrs[i]->bGuest_ok = guest_ok;
+ ServicePtrs[i]->bPrint_ok = False;
+ ServicePtrs[i]->bBrowseable = sDefault.bBrowseable;
+
+ DEBUG(3, ("adding IPC service\n"));
+
+ return (True);
+}
+
+/***************************************************************************
+ Add a new printer service, with defaults coming from service iFrom.
+***************************************************************************/
+
+BOOL lp_add_printer(const char *pszPrintername, int iDefaultService)
+{
+ const char *comment = "From Printcap";
+ int i = add_a_service(ServicePtrs[iDefaultService], pszPrintername);
+
+ if (i < 0)
+ 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(&ServicePtrs[i]->szPrintername, pszPrintername);
+ string_set(&ServicePtrs[i]->comment, comment);
+
+ /* set the browseable flag from the gloabl default */
+ ServicePtrs[i]->bBrowseable = sDefault.bBrowseable;
+
+ /* Printers cannot be read_only. */
+ ServicePtrs[i]->bRead_only = False;
+ /* No share modes on printer services. */
+ ServicePtrs[i]->bShareModes = False;
+ /* No oplocks on printer services. */
+ ServicePtrs[i]->bOpLocks = False;
+ /* Printer services must be printable. */
+ ServicePtrs[i]->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);
+}
+
+/***************************************************************************
+ Set a boolean variable from the text value stored in the passed string.
+ Returns True in success, False if the passed string does not correctly
+ represent a boolean.
+***************************************************************************/
+
+static BOOL set_boolean(BOOL *pb, const char *pszParmValue)
+{
+ BOOL bRetval;
+
+ bRetval = True;
+ if (strwicmp(pszParmValue, "yes") == 0 ||
+ strwicmp(pszParmValue, "true") == 0 ||
+ strwicmp(pszParmValue, "1") == 0)
+ *pb = True;
+ else if (strwicmp(pszParmValue, "no") == 0 ||
+ strwicmp(pszParmValue, "False") == 0 ||
+ strwicmp(pszParmValue, "0") == 0)
+ *pb = False;
+ else {
+ DEBUG(0,
+ ("ERROR: Badly formed boolean in configuration file: \"%s\".\n",
+ pszParmValue));
+ bRetval = False;
+ }
+ return (bRetval);
+}
+
+/***************************************************************************
+Find a service by name. Otherwise works like get_service.
+***************************************************************************/
+
+static int getservicebyname(const char *pszServiceName, service * pserviceDest)
+{
+ int iService;
+
+ for (iService = iNumServices - 1; iService >= 0; iService--)
+ if (VALID(iService) &&
+ strwicmp(ServicePtrs[iService]->szService, pszServiceName) == 0) {
+ if (pserviceDest != NULL)
+ copy_service(pserviceDest, ServicePtrs[iService], NULL);
+ break;
+ }
+
+ return (iService);
+}
+
+/***************************************************************************
+ Copy a service structure to another.
+ If pcopymapDest is NULL then copy all fields
+***************************************************************************/
+
+static void copy_service(service * pserviceDest, service * pserviceSource, BOOL *pcopymapDest)
+{
+ int i;
+ BOOL bcopyall = (pcopymapDest == NULL);
+ param_opt_struct *data, *pdata, *paramo;
+ BOOL not_added;
+
+ for (i = 0; parm_table[i].label; i++)
+ if (parm_table[i].ptr && parm_table[i].class == P_LOCAL &&
+ (bcopyall || pcopymapDest[i])) {
+ void *def_ptr = parm_table[i].ptr;
+ void *src_ptr =
+ ((char *)pserviceSource) + PTR_DIFF(def_ptr,
+ &sDefault);
+ void *dest_ptr =
+ ((char *)pserviceDest) + PTR_DIFF(def_ptr,
+ &sDefault);
+
+ switch (parm_table[i].type) {
+ case P_BOOL:
+ case P_BOOLREV:
+ *(BOOL *)dest_ptr = *(BOOL *)src_ptr;
+ break;
+
+ case P_INTEGER:
+ case P_ENUM:
+ case P_OCTAL:
+ *(int *)dest_ptr = *(int *)src_ptr;
+ break;
+
+ case P_CHAR:
+ *(char *)dest_ptr = *(char *)src_ptr;
+ break;
+
+ case P_STRING:
+ string_set(dest_ptr,
+ *(char **)src_ptr);
+ break;
+
+ case P_USTRING:
+ string_set(dest_ptr,
+ *(char **)src_ptr);
+ strupper_m(*(char **)dest_ptr);
+ break;
+ case P_LIST:
+ str_list_copy((char ***)dest_ptr, *(const char ***)src_ptr);
+ break;
+ default:
+ break;
+ }
+ }
+
+ if (bcopyall) {
+ init_copymap(pserviceDest);
+ if (pserviceSource->copymap)
+ memcpy((void *)pserviceDest->copymap,
+ (void *)pserviceSource->copymap,
+ sizeof(BOOL) * 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) {
+ string_free(&pdata->value);
+ str_list_free(&data->list);
+ pdata->value = strdup(data->value);
+ not_added = False;
+ break;
+ }
+ pdata = pdata->next;
+ }
+ if (not_added) {
+ paramo = smb_xmalloc(sizeof(param_opt_struct));
+ paramo->key = strdup(data->key);
+ paramo->value = strdup(data->value);
+ paramo->list = NULL;
+ 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(int iService)
+{
+ BOOL bRetval;
+
+ bRetval = True;
+ if (ServicePtrs[iService]->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(ServicePtrs[iService]->szService, PRINTERS_NAME) == 0) {
+ if (!ServicePtrs[iService]->bPrint_ok) {
+ DEBUG(0, ("WARNING: [%s] service MUST be printable!\n",
+ ServicePtrs[iService]->szService));
+ ServicePtrs[iService]->bPrint_ok = True;
+ }
+ /* [printers] service must also be non-browsable. */
+ if (ServicePtrs[iService]->bBrowseable)
+ ServicePtrs[iService]->bBrowseable = False;
+ }
+
+ if (ServicePtrs[iService]->szPath[0] == '\0' &&
+ strwicmp(ServicePtrs[iService]->szService, HOMES_NAME) != 0) {
+ DEBUG(0, ("No path in service %s - using %s\n",
+ ServicePtrs[iService]->szService, tmpdir()));
+ string_set(&ServicePtrs[iService]->szPath, tmpdir());
+ }
+
+ /* If a service is flagged unavailable, log the fact at level 0. */
+ if (!ServicePtrs[iService]->bAvailable)
+ DEBUG(1, ("NOTE: Service %s is flagged unavailable.\n",
+ ServicePtrs[iService]->szService));
+
+ return (bRetval);
+}
+
+static struct file_lists {
+ struct file_lists *next;
+ char *name;
+ char *subfname;
+ time_t modtime;
+} *file_lists = NULL;
+
+/*******************************************************************
+ 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(const char *fname, const char *subfname)
+{
+ struct file_lists *f = file_lists;
+
+ while (f) {
+ if (f->name && !strcmp(f->name, fname))
+ break;
+ f = f->next;
+ }
+
+ if (!f) {
+ f = (struct file_lists *)malloc(sizeof(file_lists[0]));
+ if (!f)
+ return;
+ f->next = file_lists;
+ f->name = strdup(fname);
+ if (!f->name) {
+ SAFE_FREE(f);
+ return;
+ }
+ f->subfname = strdup(subfname);
+ if (!f->subfname) {
+ SAFE_FREE(f);
+ return;
+ }
+ 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(void)
+{
+ struct file_lists *f = file_lists;
+ char *username;
+
+ DEBUG(6, ("lp_file_list_changed()\n"));
+
+ /* get the username for substituion -- preference to the current_user_info */
+ if ( strlen( current_user_info.smb_name ) != 0 )
+ username = current_user_info.smb_name;
+ else
+ username = sub_get_smb_name();
+
+
+ while (f) {
+ pstring n2;
+ time_t mod_time;
+
+ pstrcpy(n2, f->name);
+ standard_sub_basic(current_user_info.smb_name, n2,sizeof(n2));
+
+ 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;
+ SAFE_FREE(f->subfname);
+ f->subfname = strdup(n2);
+ return (True);
+ }
+ f = f->next;
+ }
+ return (False);
+}
+
+/***************************************************************************
+ Run standard_sub_basic on netbios name... needed because global_myname
+ is not accessed through any lp_ macro.
+ Note: We must *NOT* use string_set() here as ptr points to global_myname.
+***************************************************************************/
+
+static BOOL handle_netbios_name(const char *pszParmValue, char **ptr)
+{
+ BOOL ret;
+ pstring netbios_name;
+
+ pstrcpy(netbios_name, pszParmValue);
+
+ standard_sub_basic(current_user_info.smb_name, netbios_name,sizeof(netbios_name));
+
+ ret = set_global_myname(netbios_name);
+ string_set(&Globals.szNetbiosName,global_myname());
+
+ DEBUG(4, ("handle_netbios_name: set global_myname to: %s\n",
+ global_myname()));
+
+ return ret;
+}
+
+static BOOL handle_charset(const char *pszParmValue, char **ptr)
+{
+ if (strcmp(*ptr, pszParmValue) != 0) {
+ string_set(ptr, pszParmValue);
+ init_iconv();
+ }
+ return True;
+}
+
+static BOOL handle_workgroup(const char *pszParmValue, char **ptr)
+{
+ BOOL ret;
+
+ ret = set_global_myworkgroup(pszParmValue);
+ string_set(&Globals.szWorkgroup,lp_workgroup());
+
+ return ret;
+}
+
+static BOOL handle_netbios_scope(const char *pszParmValue, char **ptr)
+{
+ BOOL ret;
+
+ ret = set_global_scope(pszParmValue);
+ string_set(&Globals.szNetbiosScope,global_scope());
+
+ return ret;
+}
+
+static BOOL handle_netbios_aliases(const char *pszParmValue, char **ptr)
+{
+ Globals.szNetbiosAliases = str_list_make(pszParmValue, NULL);
+ return set_netbios_aliases((const char **)Globals.szNetbiosAliases);
+}
+
+/***************************************************************************
+ Handle the include operation.
+***************************************************************************/
+
+static BOOL handle_include(const char *pszParmValue, char **ptr)
+{
+ pstring fname;
+ pstrcpy(fname, pszParmValue);
+
+ standard_sub_basic(current_user_info.smb_name, fname,sizeof(fname));
+
+ add_to_file_list(pszParmValue, fname);
+
+ string_set(ptr, fname);
+
+ if (file_exist(fname, NULL))
+ return (pm_process(fname, do_section, do_parameter));
+
+ DEBUG(2, ("Can't find include file %s\n", fname));
+
+ return (False);
+}
+
+/***************************************************************************
+ Handle the interpretation of the copy parameter.
+***************************************************************************/
+
+static BOOL handle_copy(const char *pszParmValue, char **ptr)
+{
+ BOOL bRetval;
+ int iTemp;
+ service serviceTemp;
+
+ string_set(ptr, pszParmValue);
+
+ init_service(&serviceTemp);
+
+ bRetval = False;
+
+ DEBUG(3, ("Copying service from service %s\n", pszParmValue));
+
+ if ((iTemp = getservicebyname(pszParmValue, &serviceTemp)) >= 0) {
+ if (iTemp == iServiceIndex) {
+ DEBUG(0, ("Can't copy service %s - unable to copy self!\n", pszParmValue));
+ } else {
+ copy_service(ServicePtrs[iServiceIndex],
+ &serviceTemp,
+ ServicePtrs[iServiceIndex]->copymap);
+ bRetval = True;
+ }
+ } else {
+ DEBUG(0, ("Unable to copy service - source not found: %s\n", pszParmValue));
+ bRetval = False;
+ }
+
+ free_service(&serviceTemp);
+ return (bRetval);
+}
+
+/***************************************************************************
+ Handle idmap/non unix account uid and gid allocation parameters. The format of these
+ parameters is:
+
+ [global]
+
+ idmap uid = 1000-1999
+ idmap gid = 700-899
+
+ We only do simple parsing checks here. The strings are parsed into useful
+ structures in the idmap daemon code.
+
+***************************************************************************/
+
+/* Some lp_ routines to return idmap [ug]id information */
+
+static uid_t idmap_uid_low, idmap_uid_high;
+static gid_t idmap_gid_low, idmap_gid_high;
+
+BOOL lp_idmap_uid(uid_t *low, uid_t *high)
+{
+ if (idmap_uid_low == 0 || idmap_uid_high == 0)
+ return False;
+
+ if (low)
+ *low = idmap_uid_low;
+
+ if (high)
+ *high = idmap_uid_high;
+
+ return True;
+}
+
+BOOL lp_idmap_gid(gid_t *low, gid_t *high)
+{
+ if (idmap_gid_low == 0 || idmap_gid_high == 0)
+ return False;
+
+ if (low)
+ *low = idmap_gid_low;
+
+ if (high)
+ *high = idmap_gid_high;
+
+ return True;
+}
+
+/* Do some simple checks on "idmap [ug]id" parameter values */
+
+static BOOL handle_idmap_uid(const char *pszParmValue, char **ptr)
+{
+ uint32 low, high;
+
+ if (sscanf(pszParmValue, "%u-%u", &low, &high) != 2 || high < low)
+ return False;
+
+ /* Parse OK */
+
+ string_set(ptr, pszParmValue);
+
+ idmap_uid_low = low;
+ idmap_uid_high = high;
+
+ return True;
+}
+
+static BOOL handle_idmap_gid(const char *pszParmValue, char **ptr)
+{
+ uint32 low, high;
+
+ if (sscanf(pszParmValue, "%u-%u", &low, &high) != 2 || high < low)
+ return False;
+
+ /* Parse OK */
+
+ string_set(ptr, pszParmValue);
+
+ idmap_gid_low = low;
+ idmap_gid_high = high;
+
+ return True;
+}
+
+/***************************************************************************
+ Handle the DEBUG level list.
+***************************************************************************/
+
+static BOOL handle_debug_list( const char *pszParmValueIn, char **ptr )
+{
+ pstring pszParmValue;
+
+ pstrcpy(pszParmValue, pszParmValueIn);
+ string_set(ptr, pszParmValueIn);
+ return debug_parse_levels( pszParmValue );
+}
+
+/***************************************************************************
+ Handle ldap suffixes - default to ldapsuffix if sub-suffixes are not defined.
+***************************************************************************/
+
+static char* append_ldap_suffix( const char *str )
+{
+ char *suffix_string;
+
+
+ if (!lp_talloc)
+ lp_talloc = talloc_init("lp_talloc");
+
+ suffix_string = talloc_asprintf( lp_talloc, "%s,%s", str, Globals.szLdapSuffix );
+ if ( !suffix_string ) {
+ DEBUG(0,("append_ldap_suffix: talloc_asprintf() failed!\n"));
+ return NULL;
+ }
+
+ return suffix_string;
+}
+
+char *lp_ldap_machine_suffix(void)
+{
+ if (Globals.szLdapMachineSuffix[0])
+ return append_ldap_suffix(Globals.szLdapMachineSuffix);
+
+ return lp_string(Globals.szLdapSuffix);
+}
+
+char *lp_ldap_user_suffix(void)
+{
+ if (Globals.szLdapUserSuffix[0])
+ return append_ldap_suffix(Globals.szLdapUserSuffix);
+
+ return lp_string(Globals.szLdapSuffix);
+}
+
+char *lp_ldap_group_suffix(void)
+{
+ if (Globals.szLdapGroupSuffix[0])
+ return append_ldap_suffix(Globals.szLdapGroupSuffix);
+
+ return lp_string(Globals.szLdapSuffix);
+}
+
+char *lp_ldap_idmap_suffix(void)
+{
+ if (Globals.szLdapIdmapSuffix[0])
+ return append_ldap_suffix(Globals.szLdapIdmapSuffix);
+
+ return lp_string(Globals.szLdapSuffix);
+}
+
+char *lp_ldap_privilege_suffix(void)
+{
+ if (Globals.szLdapPrivilegeSuffix[0])
+ return append_ldap_suffix(Globals.szLdapPrivilegeSuffix);
+
+ return lp_string(Globals.szLdapSuffix);
+}
+
+/***************************************************************************
+***************************************************************************/
+
+static BOOL handle_acl_compatibility(const char *pszParmValue, char **ptr)
+{
+ if (strequal(pszParmValue, "auto"))
+ string_set(ptr, "");
+ else if (strequal(pszParmValue, "winnt"))
+ string_set(ptr, "winnt");
+ else if (strequal(pszParmValue, "win2k"))
+ string_set(ptr, "win2k");
+ else
+ return False;
+
+ return True;
+}
+
+/***************************************************************************
+ Initialise a copymap.
+***************************************************************************/
+
+static void init_copymap(service * pservice)
+{
+ int i;
+ SAFE_FREE(pservice->copymap);
+ pservice->copymap = (BOOL *)malloc(sizeof(BOOL) * NUMPARAMETERS);
+ if (!pservice->copymap)
+ DEBUG(0,
+ ("Couldn't allocate copymap!! (size %d)\n",
+ (int)NUMPARAMETERS));
+ else
+ for (i = 0; i < NUMPARAMETERS; i++)
+ pservice->copymap[i] = True;
+}
+
+/***************************************************************************
+ Return the local pointer to a parameter given the service number and the
+ pointer into the default structure.
+***************************************************************************/
+
+void *lp_local_ptr(int snum, void *ptr)
+{
+ return (void *)(((char *)ServicePtrs[snum]) + PTR_DIFF(ptr, &sDefault));
+}
+
+/***************************************************************************
+ Process a parameter for a particular service number. If snum < 0
+ then assume we are in the globals.
+***************************************************************************/
+
+BOOL lp_do_parameter(int snum, const char *pszParmName, const char *pszParmValue)
+{
+ int parmnum, i, slen;
+ void *parm_ptr = NULL; /* where we are going to store the result */
+ void *def_ptr = NULL;
+ pstring param_key;
+ char *sep;
+ param_opt_struct *paramo, *data;
+ BOOL not_added;
+
+ parmnum = map_parameter(pszParmName);
+
+ if (parmnum < 0) {
+ if ((sep=strchr(pszParmName, ':')) != NULL) {
+ *sep = '\0';
+ ZERO_STRUCT(param_key);
+ pstr_sprintf(param_key, "%s:", pszParmName);
+ slen = strlen(param_key);
+ pstrcat(param_key, sep+1);
+ trim_char(param_key+slen, ' ', ' ');
+ not_added = True;
+ data = (snum < 0) ? Globals.param_opt :
+ ServicePtrs[snum]->param_opt;
+ /* Traverse destination */
+ while (data) {
+ /* If we already have same option, override it */
+ if (strcmp(data->key, param_key) == 0) {
+ string_free(&data->value);
+ str_list_free(&data->list);
+ data->value = strdup(pszParmValue);
+ not_added = False;
+ break;
+ }
+ data = data->next;
+ }
+ if (not_added) {
+ paramo = smb_xmalloc(sizeof(param_opt_struct));
+ paramo->key = strdup(param_key);
+ paramo->value = strdup(pszParmValue);
+ paramo->list = NULL;
+ if (snum < 0) {
+ DLIST_ADD(Globals.param_opt, paramo);
+ } else {
+ DLIST_ADD(ServicePtrs[snum]->param_opt, paramo);
+ }
+ }
+
+ *sep = ':';
+ return (True);
+ }
+ DEBUG(0, ("Ignoring unknown parameter \"%s\"\n", pszParmName));
+ return (True);
+ }
+
+ if (parm_table[parmnum].flags & FLAG_DEPRECATED) {
+ DEBUG(1, ("WARNING: The \"%s\" option is deprecated\n",
+ pszParmName));
+ }
+
+ def_ptr = parm_table[parmnum].ptr;
+
+ /* we might point at a service, the default service or a global */
+ if (snum < 0) {
+ parm_ptr = def_ptr;
+ } else {
+ if (parm_table[parmnum].class == P_GLOBAL) {
+ DEBUG(0,
+ ("Global parameter %s found in service section!\n",
+ pszParmName));
+ return (True);
+ }
+ parm_ptr =
+ ((char *)ServicePtrs[snum]) + PTR_DIFF(def_ptr,
+ &sDefault);
+ }
+
+ if (snum >= 0) {
+ if (!ServicePtrs[snum]->copymap)
+ init_copymap(ServicePtrs[snum]);
+
+ /* 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].ptr == parm_table[parmnum].ptr)
+ ServicePtrs[snum]->copymap[i] = False;
+ }
+
+ /* if it is a special case then go ahead */
+ if (parm_table[parmnum].special) {
+ parm_table[parmnum].special(pszParmValue, (char **)parm_ptr);
+ return (True);
+ }
+
+ /* now switch on the type of variable it is */
+ switch (parm_table[parmnum].type)
+ {
+ case P_BOOL:
+ set_boolean(parm_ptr, pszParmValue);
+ break;
+
+ case P_BOOLREV:
+ set_boolean(parm_ptr, pszParmValue);
+ *(BOOL *)parm_ptr = !*(BOOL *)parm_ptr;
+ break;
+
+ case P_INTEGER:
+ *(int *)parm_ptr = atoi(pszParmValue);
+ break;
+
+ case P_CHAR:
+ *(char *)parm_ptr = *pszParmValue;
+ break;
+
+ case P_OCTAL:
+ sscanf(pszParmValue, "%o", (int *)parm_ptr);
+ break;
+
+ case P_LIST:
+ str_list_free(parm_ptr);
+ *(char ***)parm_ptr = str_list_make(pszParmValue, NULL);
+ break;
+
+ case P_STRING:
+ string_set(parm_ptr, pszParmValue);
+ break;
+
+ case P_USTRING:
+ string_set(parm_ptr, pszParmValue);
+ strupper_m(*(char **)parm_ptr);
+ break;
+
+ case P_GSTRING:
+ pstrcpy((char *)parm_ptr, pszParmValue);
+ break;
+
+ case P_UGSTRING:
+ pstrcpy((char *)parm_ptr, pszParmValue);
+ strupper_m((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;
+ }
+ }
+ break;
+ case P_SEP:
+ break;
+ }
+
+ return (True);
+}
+
+/***************************************************************************
+ Process a parameter.
+***************************************************************************/
+
+static BOOL do_parameter(const char *pszParmName, const char *pszParmValue)
+{
+ if (!bInGlobalSection && bGlobalOnly)
+ return (True);
+
+ DEBUGADD(4, ("doing parameter %s = %s\n", pszParmName, pszParmValue));
+
+ return (lp_do_parameter(bInGlobalSection ? -2 : iServiceIndex,
+ pszParmName, pszParmValue));
+}
+
+/***************************************************************************
+ 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 *)ptr));
+ break;
+
+ case P_BOOLREV:
+ fprintf(f, "%s", BOOLSTR(!*(BOOL *)ptr));
+ break;
+
+ case P_INTEGER:
+ fprintf(f, "%d", *(int *)ptr);
+ break;
+
+ case P_CHAR:
+ fprintf(f, "%c", *(char *)ptr);
+ break;
+
+ case P_OCTAL:
+ fprintf(f, "%s", octal_string(*(int *)ptr));
+ break;
+
+ case P_LIST:
+ if ((char ***)ptr && *(char ***)ptr) {
+ char **list = *(char ***)ptr;
+
+ for (; *list; list++) {
+ /* surround strings with whitespace in single quotes */
+ if ( strchr_m( *list, ' ' ) )
+ fprintf(f, "\'%s\'%s", *list, ((*(list+1))?", ":""));
+ else
+ fprintf(f, "%s%s", *list, ((*(list+1))?", ":""));
+ }
+ }
+ break;
+
+ case P_GSTRING:
+ case P_UGSTRING:
+ if ((char *)ptr) {
+ fprintf(f, "%s", (char *)ptr);
+ }
+ break;
+
+ case P_STRING:
+ case P_USTRING:
+ if (*(char **)ptr) {
+ fprintf(f, "%s", *(char **)ptr);
+ }
+ break;
+ case P_SEP:
+ break;
+ }
+}
+
+/***************************************************************************
+ Check if two parameters are equal.
+***************************************************************************/
+
+static BOOL equal_parameter(parm_type type, void *ptr1, void *ptr2)
+{
+ switch (type) {
+ case P_BOOL:
+ case P_BOOLREV:
+ return (*((BOOL *)ptr1) == *((BOOL *)ptr2));
+
+ case P_INTEGER:
+ case P_ENUM:
+ case P_OCTAL:
+ return (*((int *)ptr1) == *((int *)ptr2));
+
+ case P_CHAR:
+ return (*((char *)ptr1) == *((char *)ptr2));
+
+ case P_LIST:
+ return str_list_compare(*(char ***)ptr1, *(char ***)ptr2);
+
+ case P_GSTRING:
+ case P_UGSTRING:
+ {
+ char *p1 = (char *)ptr1, *p2 = (char *)ptr2;
+ if (p1 && !*p1)
+ p1 = NULL;
+ if (p2 && !*p2)
+ p2 = NULL;
+ return (p1 == p2 || strequal(p1, p2));
+ }
+ 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));
+ }
+ case P_SEP:
+ break;
+ }
+ return (False);
+}
+
+/***************************************************************************
+ Initialize any local varients in the sDefault table.
+***************************************************************************/
+
+void init_locals(void)
+{
+ /* None as yet. */
+}
+
+/***************************************************************************
+ 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)
+{
+ BOOL bRetval;
+ BOOL isglobal = ((strwicmp(pszSectionName, GLOBAL_NAME) == 0) ||
+ (strwicmp(pszSectionName, GLOBAL_NAME2) == 0));
+ bRetval = False;
+
+ /* if we were in a global section then do the local inits */
+ if (bInGlobalSection && !isglobal)
+ init_locals();
+
+ /* if we've just struck a global section, note the fact. */
+ bInGlobalSection = isglobal;
+
+ /* check for multiple global sections */
+ if (bInGlobalSection) {
+ DEBUG(3, ("Processing section \"[%s]\"\n", pszSectionName));
+ return (True);
+ }
+
+ if (!bInGlobalSection && bGlobalOnly)
+ return (True);
+
+ /* if we have a current service, tidy it up before moving on */
+ bRetval = True;
+
+ if (iServiceIndex >= 0)
+ bRetval = service_ok(iServiceIndex);
+
+ /* 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 ((iServiceIndex = add_a_service(&sDefault, pszSectionName))
+ < 0) {
+ DEBUG(0, ("Failed to add a new service\n"));
+ return (False);
+ }
+ }
+
+ return (bRetval);
+}
+
+
+/***************************************************************************
+ Determine if a partcular base parameter is currentl set to the default value.
+***************************************************************************/
+
+static BOOL is_default(int i)
+{
+ if (!defaults_saved)
+ return False;
+ switch (parm_table[i].type) {
+ case P_LIST:
+ return str_list_compare (parm_table[i].def.lvalue,
+ *(char ***)parm_table[i].ptr);
+ case P_STRING:
+ case P_USTRING:
+ return strequal(parm_table[i].def.svalue,
+ *(char **)parm_table[i].ptr);
+ case P_GSTRING:
+ case P_UGSTRING:
+ return strequal(parm_table[i].def.svalue,
+ (char *)parm_table[i].ptr);
+ case P_BOOL:
+ case P_BOOLREV:
+ return parm_table[i].def.bvalue ==
+ *(BOOL *)parm_table[i].ptr;
+ case P_CHAR:
+ return parm_table[i].def.cvalue ==
+ *(char *)parm_table[i].ptr;
+ case P_INTEGER:
+ case P_OCTAL:
+ case P_ENUM:
+ return parm_table[i].def.ivalue ==
+ *(int *)parm_table[i].ptr;
+ case P_SEP:
+ break;
+ }
+ return False;
+}
+
+/***************************************************************************
+Display the contents of the global structure.
+***************************************************************************/
+
+static void dump_globals(FILE *f)
+{
+ int i;
+ param_opt_struct *data;
+
+ fprintf(f, "# Global parameters\n[global]\n");
+
+ for (i = 0; parm_table[i].label; i++)
+ if (parm_table[i].class == P_GLOBAL &&
+ parm_table[i].ptr &&
+ (i == 0 || (parm_table[i].ptr != parm_table[i - 1].ptr))) {
+ if (defaults_saved && is_default(i))
+ continue;
+ fprintf(f, "\t%s = ", parm_table[i].label);
+ print_parameter(&parm_table[i], parm_table[i].ptr, f);
+ fprintf(f, "\n");
+ }
+ if (Globals.param_opt != NULL) {
+ data = Globals.param_opt;
+ while(data) {
+ fprintf(f, "\t%s = %s\n", data->key, data->value);
+ data = data->next;
+ }
+ }
+
+}
+
+/***************************************************************************
+ Return True if a local parameter is currently set to the global default.
+***************************************************************************/
+
+BOOL lp_is_default(int snum, struct parm_struct *parm)
+{
+ int pdiff = PTR_DIFF(parm->ptr, &sDefault);
+
+ return equal_parameter(parm->type,
+ ((char *)ServicePtrs[snum]) + pdiff,
+ ((char *)&sDefault) + pdiff);
+}
+
+/***************************************************************************
+ Display the contents of a single services record.
+***************************************************************************/
+
+static void dump_a_service(service * pService, FILE * f)
+{
+ int i;
+ param_opt_struct *data;
+
+ if (pService != &sDefault)
+ fprintf(f, "\n[%s]\n", pService->szService);
+
+ for (i = 0; parm_table[i].label; i++)
+ if (parm_table[i].class == P_LOCAL &&
+ parm_table[i].ptr &&
+ (*parm_table[i].label != '-') &&
+ (i == 0 || (parm_table[i].ptr != parm_table[i - 1].ptr))) {
+ int pdiff = PTR_DIFF(parm_table[i].ptr, &sDefault);
+
+ if (pService == &sDefault) {
+ if (defaults_saved && is_default(i))
+ continue;
+ } else {
+ if (equal_parameter(parm_table[i].type,
+ ((char *)pService) +
+ pdiff,
+ ((char *)&sDefault) +
+ pdiff))
+ continue;
+ }
+
+ fprintf(f, "\t%s = ", parm_table[i].label);
+ print_parameter(&parm_table[i],
+ ((char *)pService) + pdiff, f);
+ fprintf(f, "\n");
+ }
+ if (pService->param_opt != NULL) {
+ data = pService->param_opt;
+ while(data) {
+ fprintf(f, "\t%s = %s\n", data->key, data->value);
+ data = data->next;
+ }
+ }
+}
+
+
+/***************************************************************************
+ Return info about the next service in a service. snum==GLOBAL_SECTION_SNUM gives the globals.
+ Return NULL when out of parameters.
+***************************************************************************/
+
+struct parm_struct *lp_next_parameter(int snum, int *i, int allparameters)
+{
+ if (snum < 0) {
+ /* do the globals */
+ for (; parm_table[*i].label; (*i)++) {
+ if (parm_table[*i].class == P_SEPARATOR)
+ return &parm_table[(*i)++];
+
+ if (!parm_table[*i].ptr
+ || (*parm_table[*i].label == '-'))
+ continue;
+
+ if ((*i) > 0
+ && (parm_table[*i].ptr ==
+ parm_table[(*i) - 1].ptr))
+ continue;
+
+ return &parm_table[(*i)++];
+ }
+ } else {
+ service *pService = ServicePtrs[snum];
+
+ for (; parm_table[*i].label; (*i)++) {
+ if (parm_table[*i].class == P_SEPARATOR)
+ return &parm_table[(*i)++];
+
+ if (parm_table[*i].class == P_LOCAL &&
+ parm_table[*i].ptr &&
+ (*parm_table[*i].label != '-') &&
+ ((*i) == 0 ||
+ (parm_table[*i].ptr !=
+ parm_table[(*i) - 1].ptr)))
+ {
+ int pdiff =
+ PTR_DIFF(parm_table[*i].ptr,
+ &sDefault);
+
+ if (allparameters ||
+ !equal_parameter(parm_table[*i].type,
+ ((char *)pService) +
+ pdiff,
+ ((char *)&sDefault) +
+ pdiff))
+ {
+ return &parm_table[(*i)++];
+ }
+ }
+ }
+ }
+
+ return NULL;
+}
+
+
+#if 0
+/***************************************************************************
+ Display the contents of a single copy structure.
+***************************************************************************/
+static void dump_copy_map(BOOL *pcopymap)
+{
+ int i;
+ if (!pcopymap)
+ return;
+
+ printf("\n\tNon-Copied parameters:\n");
+
+ for (i = 0; parm_table[i].label; i++)
+ if (parm_table[i].class == P_LOCAL &&
+ parm_table[i].ptr && !pcopymap[i] &&
+ (i == 0 || (parm_table[i].ptr != parm_table[i - 1].ptr)))
+ {
+ printf("\t\t%s\n", parm_table[i].label);
+ }
+}
+#endif
+
+/***************************************************************************
+ Return TRUE if the passed service number is within range.
+***************************************************************************/
+
+BOOL lp_snum_ok(int iService)
+{
+ return (LP_SNUM_OK(iService) && ServicePtrs[iService]->bAvailable);
+}
+
+/***************************************************************************
+ Auto-load some home services.
+***************************************************************************/
+
+static void lp_add_auto_services(char *str)
+{
+ char *s;
+ char *p;
+ int homes;
+
+ if (!str)
+ return;
+
+ s = strdup(str);
+ if (!s)
+ return;
+
+ homes = lp_servicenumber(HOMES_NAME);
+
+ for (p = strtok(s, LIST_SEP); p; p = strtok(NULL, LIST_SEP)) {
+ char *home = get_user_home_dir(p);
+
+ if (lp_servicenumber(p) >= 0)
+ continue;
+
+ if (home && homes >= 0)
+ lp_add_home(p, homes, p, home);
+ }
+ SAFE_FREE(s);
+}
+
+/***************************************************************************
+ Auto-load one printer.
+***************************************************************************/
+
+void lp_add_one_printer(char *name, char *comment)
+{
+ int printers = lp_servicenumber(PRINTERS_NAME);
+ int i;
+
+ if (lp_servicenumber(name) < 0) {
+ lp_add_printer(name, printers);
+ if ((i = lp_servicenumber(name)) >= 0) {
+ string_set(&ServicePtrs[i]->comment, comment);
+ ServicePtrs[i]->autoloaded = True;
+ }
+ }
+}
+
+/***************************************************************************
+ Have we loaded a services file yet?
+***************************************************************************/
+
+BOOL lp_loaded(void)
+{
+ return (bLoaded);
+}
+
+/***************************************************************************
+ Unload unused services.
+***************************************************************************/
+
+void lp_killunused(BOOL (*snumused) (int))
+{
+ int i;
+ for (i = 0; i < iNumServices; i++) {
+ if (!VALID(i))
+ continue;
+
+ if (!snumused || !snumused(i)) {
+ ServicePtrs[i]->valid = False;
+ free_service(ServicePtrs[i]);
+ }
+ }
+}
+
+/***************************************************************************
+ Unload a service.
+***************************************************************************/
+
+void lp_killservice(int iServiceIn)
+{
+ if (VALID(iServiceIn)) {
+ ServicePtrs[iServiceIn]->valid = False;
+ free_service(ServicePtrs[iServiceIn]);
+ }
+}
+
+/***************************************************************************
+ Save the curent values of all global and sDefault parameters into the
+ defaults union. This allows swat and testparm to show only the
+ changed (ie. non-default) parameters.
+***************************************************************************/
+
+static void lp_save_defaults(void)
+{
+ int i;
+ for (i = 0; parm_table[i].label; i++) {
+ if (i > 0 && parm_table[i].ptr == parm_table[i - 1].ptr)
+ continue;
+ switch (parm_table[i].type) {
+ case P_LIST:
+ str_list_copy(&(parm_table[i].def.lvalue),
+ *(const char ***)parm_table[i].ptr);
+ break;
+ case P_STRING:
+ case P_USTRING:
+ if (parm_table[i].ptr) {
+ parm_table[i].def.svalue = strdup(*(char **)parm_table[i].ptr);
+ } else {
+ parm_table[i].def.svalue = NULL;
+ }
+ break;
+ case P_GSTRING:
+ case P_UGSTRING:
+ if (parm_table[i].ptr) {
+ parm_table[i].def.svalue = strdup((char *)parm_table[i].ptr);
+ } else {
+ parm_table[i].def.svalue = NULL;
+ }
+ break;
+ case P_BOOL:
+ case P_BOOLREV:
+ parm_table[i].def.bvalue =
+ *(BOOL *)parm_table[i].ptr;
+ break;
+ case P_CHAR:
+ parm_table[i].def.cvalue =
+ *(char *)parm_table[i].ptr;
+ break;
+ case P_INTEGER:
+ case P_OCTAL:
+ case P_ENUM:
+ parm_table[i].def.ivalue =
+ *(int *)parm_table[i].ptr;
+ break;
+ case P_SEP:
+ break;
+ }
+ }
+ defaults_saved = True;
+}
+
+/*******************************************************************
+ Set the server type we will announce as via nmbd.
+********************************************************************/
+
+static void set_server_role(void)
+{
+ server_role = ROLE_STANDALONE;
+
+ switch (lp_security()) {
+ case SEC_SHARE:
+ if (lp_domain_logons())
+ DEBUG(0, ("Server's Role (logon server) conflicts with share-level security\n"));
+ break;
+ case SEC_SERVER:
+ if (lp_domain_logons())
+ DEBUG(0, ("Server's Role (logon server) conflicts with server-level security\n"));
+ server_role = ROLE_DOMAIN_MEMBER;
+ break;
+ case SEC_DOMAIN:
+ if (lp_domain_logons()) {
+ DEBUG(1, ("Server's Role (logon server) NOT ADVISED with domain-level security\n"));
+ server_role = ROLE_DOMAIN_BDC;
+ break;
+ }
+ server_role = ROLE_DOMAIN_MEMBER;
+ break;
+ case SEC_ADS:
+ if (lp_domain_logons()) {
+ server_role = ROLE_DOMAIN_PDC;
+ break;
+ }
+ server_role = ROLE_DOMAIN_MEMBER;
+ break;
+ case SEC_USER:
+ if (lp_domain_logons()) {
+
+ if (Globals.bDomainMaster) /* auto or yes */
+ server_role = ROLE_DOMAIN_PDC;
+ else
+ server_role = ROLE_DOMAIN_BDC;
+ }
+ break;
+ default:
+ DEBUG(0, ("Server's Role undefined due to unknown security mode\n"));
+ break;
+ }
+
+ DEBUG(10, ("set_server_role: role = "));
+
+ switch(server_role) {
+ case ROLE_STANDALONE:
+ DEBUGADD(10, ("ROLE_STANDALONE\n"));
+ break;
+ case ROLE_DOMAIN_MEMBER:
+ DEBUGADD(10, ("ROLE_DOMAIN_MEMBER\n"));
+ break;
+ case ROLE_DOMAIN_BDC:
+ DEBUGADD(10, ("ROLE_DOMAIN_BDC\n"));
+ break;
+ case ROLE_DOMAIN_PDC:
+ DEBUGADD(10, ("ROLE_DOMAIN_PDC\n"));
+ break;
+ }
+}
+
+/***********************************************************
+ If we should send plaintext/LANMAN passwords in the clinet
+************************************************************/
+static void set_allowed_client_auth(void)
+{
+ if (Globals.bClientNTLMv2Auth) {
+ Globals.bClientLanManAuth = False;
+ }
+ if (!Globals.bClientLanManAuth) {
+ Globals.bClientPlaintextAuth = False;
+ }
+}
+
+/***************************************************************************
+ Load the services array from the services file. Return True on success,
+ False on failure.
+***************************************************************************/
+
+BOOL lp_load(const char *pszFname, BOOL global_only, BOOL save_defaults,
+ BOOL add_ipc)
+{
+ pstring n2;
+ BOOL bRetval;
+ param_opt_struct *data, *pdata;
+ char *username;
+
+ pstrcpy(n2, pszFname);
+
+ /* get the username for substituion -- preference to the current_user_info */
+
+ if ( strlen( current_user_info.smb_name ) != 0 )
+ username = current_user_info.smb_name;
+ else
+ username = sub_get_smb_name();
+
+ standard_sub_basic( username, n2,sizeof(n2) );
+
+ add_to_file_list(pszFname, n2);
+
+ bRetval = False;
+
+ DEBUG(3, ("lp_load: refreshing parameters\n"));
+
+ bInGlobalSection = True;
+ bGlobalOnly = global_only;
+
+ init_globals();
+ debug_init();
+
+ if (save_defaults)
+ {
+ init_locals();
+ lp_save_defaults();
+ }
+
+ if (Globals.param_opt != NULL) {
+ data = Globals.param_opt;
+ while (data) {
+ string_free(&data->key);
+ string_free(&data->value);
+ str_list_free(&data->list);
+ pdata = data->next;
+ SAFE_FREE(data);
+ data = pdata;
+ }
+ Globals.param_opt = NULL;
+ }
+
+ /* We get sections first, so have to start 'behind' to make up */
+ iServiceIndex = -1;
+ bRetval = pm_process(n2, do_section, do_parameter);
+
+ /* finish up the last section */
+ DEBUG(4, ("pm_process() returned %s\n", BOOLSTR(bRetval)));
+ if (bRetval)
+ if (iServiceIndex >= 0)
+ bRetval = service_ok(iServiceIndex);
+
+ if (*(lp_config_backend())) {
+ modconf_init(lp_config_backend());
+ modconf_load(do_section, do_parameter);
+ }
+
+ lp_add_auto_services(lp_auto_services());
+
+ if (add_ipc) {
+ /* When 'restrict anonymous = 2' guest connections to ipc$
+ are denied */
+ lp_add_ipc("IPC$", (lp_restrict_anonymous() < 2));
+ lp_add_ipc("ADMIN$", False);
+ }
+
+ set_server_role();
+ set_default_server_announce_type();
+ set_allowed_client_auth();
+
+ bLoaded = True;
+
+ /* Now we check bWINSsupport and set szWINSserver to 127.0.0.1 */
+ /* if bWINSsupport is true and we are in the client */
+ if (in_client && Globals.bWINSsupport) {
+ lp_do_parameter(GLOBAL_SECTION_SNUM, "wins server", "127.0.0.1");
+ }
+
+ init_iconv();
+ init_printer_values(&sDefault);
+
+ return (bRetval);
+}
+
+/***************************************************************************
+ Reset the max number of services.
+***************************************************************************/
+
+void lp_resetnumservices(void)
+{
+ iNumServices = 0;
+}
+
+/***************************************************************************
+ Return the max number of services.
+***************************************************************************/
+
+int lp_numservices(void)
+{
+ return (iNumServices);
+}
+
+/***************************************************************************
+Display the contents of the services array in human-readable form.
+***************************************************************************/
+
+void lp_dump(FILE *f, BOOL show_defaults, int maxtoprint)
+{
+ int iService;
+
+ if (show_defaults)
+ defaults_saved = False;
+
+ dump_globals(f);
+
+ dump_a_service(&sDefault, f);
+
+ for (iService = 0; iService < maxtoprint; iService++)
+ lp_dump_one(f, show_defaults, iService);
+}
+
+/***************************************************************************
+Display the contents of one service in human-readable form.
+***************************************************************************/
+
+void lp_dump_one(FILE * f, BOOL show_defaults, int snum)
+{
+ if (VALID(snum)) {
+ if (ServicePtrs[snum]->szService[0] == '\0')
+ return;
+ dump_a_service(ServicePtrs[snum], f);
+ }
+}
+
+/***************************************************************************
+Return the number of the service with the given name, or -1 if it doesn't
+exist. Note that this is a DIFFERENT ANIMAL from the internal function
+getservicebyname()! This works ONLY if all services have been loaded, and
+does not copy the found service.
+***************************************************************************/
+
+int lp_servicenumber(const char *pszServiceName)
+{
+ int iService;
+ fstring serviceName;
+
+ if (!pszServiceName)
+ return GLOBAL_SECTION_SNUM;
+
+ for (iService = iNumServices - 1; iService >= 0; iService--) {
+ if (VALID(iService) && ServicePtrs[iService]->szService) {
+ /*
+ * The substitution here is used to support %U is
+ * service names
+ */
+ fstrcpy(serviceName, ServicePtrs[iService]->szService);
+ standard_sub_basic(current_user_info.smb_name, serviceName,sizeof(serviceName));
+ if (strequal(serviceName, pszServiceName))
+ break;
+ }
+ }
+
+ if (iService < 0) {
+ DEBUG(7,("lp_servicenumber: couldn't find %s\n", pszServiceName));
+ return GLOBAL_SECTION_SNUM;
+ }
+
+ return (iService);
+}
+
+/*******************************************************************
+ A useful volume label function.
+********************************************************************/
+
+char *volume_label(int snum)
+{
+ char *ret = lp_volume(snum);
+ if (!*ret)
+ return lp_servicename(snum);
+ return (ret);
+}
+
+
+/*******************************************************************
+ Set the server type we will announce as via nmbd.
+********************************************************************/
+
+static void set_default_server_announce_type(void)
+{
+ default_server_announce = 0;
+ default_server_announce |= SV_TYPE_WORKSTATION;
+ default_server_announce |= SV_TYPE_SERVER;
+ default_server_announce |= SV_TYPE_SERVER_UNIX;
+
+ /* note that the flag should be set only if we have a
+ printer service but nmbd doesn't actually load the
+ services so we can't tell --jerry */
+
+ default_server_announce |= SV_TYPE_PRINTQ_SERVER;
+
+ switch (lp_announce_as()) {
+ 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()) {
+ case ROLE_DOMAIN_MEMBER:
+ default_server_announce |= SV_TYPE_DOMAIN_MEMBER;
+ break;
+ case ROLE_DOMAIN_PDC:
+ default_server_announce |= SV_TYPE_DOMAIN_CTRL;
+ break;
+ case ROLE_DOMAIN_BDC:
+ default_server_announce |= SV_TYPE_DOMAIN_BAKCTRL;
+ break;
+ case ROLE_STANDALONE:
+ default:
+ break;
+ }
+ if (lp_time_server())
+ default_server_announce |= SV_TYPE_TIME_SOURCE;
+
+ if (lp_host_msdfs())
+ default_server_announce |= SV_TYPE_DFS_SERVER;
+}
+
+/***********************************************************
+ returns role of Samba server
+************************************************************/
+
+int lp_server_role(void)
+{
+ return server_role;
+}
+
+/***********************************************************
+ If we are PDC then prefer us as DMB
+************************************************************/
+
+BOOL lp_domain_master(void)
+{
+ if (Globals.bDomainMaster == Auto)
+ return (lp_server_role() == ROLE_DOMAIN_PDC);
+
+ return Globals.bDomainMaster;
+}
+
+/***********************************************************
+ If we are DMB then prefer us as LMB
+************************************************************/
+
+BOOL lp_preferred_master(void)
+{
+ if (Globals.bPreferredMaster == Auto)
+ return (lp_local_master() && lp_domain_master());
+
+ return Globals.bPreferredMaster;
+}
+
+/*******************************************************************
+ Remove a service.
+********************************************************************/
+
+void lp_remove_service(int snum)
+{
+ ServicePtrs[snum]->valid = False;
+}
+
+/*******************************************************************
+ Copy a service.
+********************************************************************/
+
+void lp_copy_service(int snum, const char *new_name)
+{
+ do_section(new_name);
+ if (snum >= 0) {
+ snum = lp_servicenumber(new_name);
+ if (snum >= 0)
+ lp_do_parameter(snum, "copy", lp_servicename(snum));
+ }
+}
+
+
+/*******************************************************************
+ Get the default server type we will announce as via nmbd.
+********************************************************************/
+
+int lp_default_server_announce(void)
+{
+ return default_server_announce;
+}
+
+/*******************************************************************
+ Split the announce version into major and minor numbers.
+********************************************************************/
+
+int lp_major_announce_version(void)
+{
+ static BOOL got_major = False;
+ static int major_version = DEFAULT_MAJOR_VERSION;
+ char *vers;
+ char *p;
+
+ if (got_major)
+ return major_version;
+
+ got_major = True;
+ if ((vers = lp_announce_version()) == NULL)
+ return major_version;
+
+ if ((p = strchr_m(vers, '.')) == 0)
+ return major_version;
+
+ *p = '\0';
+ major_version = atoi(vers);
+ return major_version;
+}
+
+int lp_minor_announce_version(void)
+{
+ static BOOL got_minor = False;
+ static int minor_version = DEFAULT_MINOR_VERSION;
+ char *vers;
+ char *p;
+
+ if (got_minor)
+ return minor_version;
+
+ got_minor = True;
+ if ((vers = lp_announce_version()) == NULL)
+ return minor_version;
+
+ if ((p = strchr_m(vers, '.')) == 0)
+ return minor_version;
+
+ p++;
+ minor_version = atoi(p);
+ return minor_version;
+}
+
+/***********************************************************
+ Set the global name resolution order (used in smbclient).
+************************************************************/
+
+void lp_set_name_resolve_order(const char *new_order)
+{
+ string_set(&Globals.szNameResolveOrder, new_order);
+}
+
+const char *lp_printername(int snum)
+{
+ const char *ret = _lp_printername(snum);
+ if (ret == NULL || (ret != NULL && *ret == '\0'))
+ ret = lp_const_servicename(snum);
+
+ return ret;
+}
+
+
+/****************************************************************
+ Compatibility fn. for 2.2.2 code.....
+*****************************************************************/
+
+void get_private_directory(pstring privdir)
+{
+ pstrcpy (privdir, lp_private_dir());
+}
+
+/***********************************************************
+ Allow daemons such as winbindd to fix their logfile name.
+************************************************************/
+
+void lp_set_logfile(const char *name)
+{
+ string_set(&Globals.szLogFile, name);
+ pstrcpy(debugf, name);
+}
+
+/*******************************************************************
+ Return the NetBIOS called name, or my IP - but never global_myname().
+********************************************************************/
+
+const char *get_called_name(void)
+{
+ extern fstring local_machine;
+ static fstring called_name;
+
+ if (!*local_machine) {
+ fstrcpy(called_name, get_my_primary_ip());
+ DEBUG(8,("get_called_name: assuming that client used IP address [%s] as called name.\n",
+ called_name));
+ return called_name;
+ }
+
+ return local_machine;
+}
+
+/*******************************************************************
+ Return the max print jobs per queue.
+********************************************************************/
+
+int lp_maxprintjobs(int snum)
+{
+ int maxjobs = LP_SNUM_OK(snum) ? ServicePtrs[snum]->iMaxPrintJobs : sDefault.iMaxPrintJobs;
+ if (maxjobs <= 0 || maxjobs >= PRINT_MAX_JOBID)
+ maxjobs = PRINT_MAX_JOBID - 1;
+
+ return maxjobs;
+}
+
+/*******************************************************************
+ Ensure we don't use sendfile if server smb signing is active.
+********************************************************************/
+
+BOOL lp_use_sendfile(int snum)
+{
+ return (_lp_use_sendfile(snum) && !srv_is_signing_active());
+}
diff --git a/source/param/modconf.c b/source/param/modconf.c
new file mode 100644
index 00000000000..a9ab6f9b4a2
--- /dev/null
+++ b/source/param/modconf.c
@@ -0,0 +1,96 @@
+/*
+ Unix SMB/CIFS implementation.
+ Configuration Modules Support
+ Copyright (C) Simo Sorce 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 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"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_IDMAP
+
+struct modconf_struct {
+ char *name;
+ struct config_functions *fns;
+};
+
+static struct modconf_struct module;
+
+NTSTATUS smb_register_config(int version, const char *name, struct config_functions *fns)
+{
+ if ((version != SAMBA_CONFIG_INTERFACE_VERSION)) {
+ DEBUG(0, ("smb_register_config: Failed to register config module.\n"
+ "The module has been compiled with a different interface version (%d).\n"
+ "The supported version is: %d\n",
+ version, SAMBA_CONFIG_INTERFACE_VERSION));
+ return NT_STATUS_OBJECT_TYPE_MISMATCH;
+ }
+
+ if (!name || !name[0]) {
+ DEBUG(0,("smb_register_config: Name missing!\n"));
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ module.name = smb_xstrdup(name);
+ module.fns = fns;
+ DEBUG(5, ("smb_register_config: Successfully registeres config backend '%s'\n", name));
+ return NT_STATUS_OK;
+}
+
+/**********************************************************************
+ * Init the configuration module
+ *********************************************************************/
+
+BOOL modconf_init(const char *config_backend)
+{
+ NTSTATUS ret;
+ BOOL bret = False;
+ char *name;
+ char *params;
+
+ /* nothing to do */
+ if (!config_backend)
+ return True;
+
+ name = smb_xstrdup(config_backend);
+ if ((params = strchr(name, ':')) != NULL ) {
+ *params = '\0';
+ params++;
+ }
+
+ ret = smb_probe_module("config", name);
+
+ if (NT_STATUS_IS_OK(ret) && NT_STATUS_IS_OK(module.fns->init(params)))
+ bret = True;
+
+ SAFE_FREE(name);
+ return bret;
+}
+
+BOOL modconf_load(BOOL (*sfunc)(const char *),BOOL (*pfunc)(const char *, const char *))
+{
+ if (module.fns) {
+ if (NT_STATUS_IS_OK(module.fns->load(sfunc, pfunc))) {
+ return True;
+ }
+ }
+ return False;
+}
+
+NTSTATUS modconf_close(void)
+{
+ return module.fns->close();
+}
diff --git a/source/param/params.c b/source/param/params.c
new file mode 100644
index 00000000000..892e5476cc0
--- /dev/null
+++ b/source/param/params.c
@@ -0,0 +1,599 @@
+/* -------------------------------------------------------------------------- **
+ * Microsoft Network Services for Unix, AKA., Andrew Tridgell's SAMBA.
+ *
+ * This module Copyright (C) 1990-1998 Karl Auer
+ *
+ * Rewritten almost completely by Christopher R. Hertel
+ * at the University of Minnesota, September, 1997.
+ * This module Copyright (C) 1997-1998 by the University of Minnesota
+ * -------------------------------------------------------------------------- **
+ *
+ * This program is free software; you can 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 name: params
+ *
+ * -------------------------------------------------------------------------- **
+ *
+ * This module performs lexical analysis and initial parsing of a
+ * Windows-like parameter file. It recognizes and handles four token
+ * types: section-name, parameter-name, parameter-value, and
+ * end-of-file. Comments and line continuation are handled
+ * internally.
+ *
+ * The entry point to the module is function pm_process(). This
+ * function opens the source file, calls the Parse() function to parse
+ * the input, and then closes the file when either the EOF is reached
+ * or a fatal error is encountered.
+ *
+ * A sample parameter file might look like this:
+ *
+ * [section one]
+ * parameter one = value string
+ * parameter two = another value
+ * [section two]
+ * new parameter = some value or t'other
+ *
+ * The parameter file is divided into sections by section headers:
+ * section names enclosed in square brackets (eg. [section one]).
+ * Each section contains parameter lines, each of which consist of a
+ * parameter name and value delimited by an equal sign. Roughly, the
+ * syntax is:
+ *
+ * <file> :== { <section> } EOF
+ *
+ * <section> :== <section header> { <parameter line> }
+ *
+ * <section header> :== '[' NAME ']'
+ *
+ * <parameter line> :== NAME '=' VALUE '\n'
+ *
+ * Blank lines and comment lines are ignored. Comment lines are lines
+ * beginning with either a semicolon (';') or a pound sign ('#').
+ *
+ * All whitespace in section names and parameter names is compressed
+ * to single spaces. Leading and trailing whitespace is stipped from
+ * both names and values.
+ *
+ * Only the first equals sign in a parameter line is significant.
+ * Parameter values may contain equals signs, square brackets and
+ * semicolons. Internal whitespace is retained in parameter values,
+ * with the exception of the '\r' character, which is stripped for
+ * historic reasons. Parameter names may not start with a left square
+ * bracket, an equal sign, a pound sign, or a semicolon, because these
+ * are used to identify other tokens.
+ *
+ * -------------------------------------------------------------------------- **
+ */
+
+#include "includes.h"
+
+/* -------------------------------------------------------------------------- **
+ * Constants...
+ */
+
+#define BUFR_INC 1024
+
+
+/* -------------------------------------------------------------------------- **
+ * Variables...
+ *
+ * DEBUGLEVEL - The ubiquitous DEBUGLEVEL. This determines which DEBUG()
+ * messages will be produced.
+ * bufr - pointer to a global buffer. This is probably a kludge,
+ * but it was the nicest kludge I could think of (for now).
+ * bSize - The size of the global buffer <bufr>.
+ */
+
+static char *bufr = NULL;
+static int bSize = 0;
+
+/* we can't use FILE* due to the 256 fd limit - use this cheap hack
+ instead */
+typedef struct {
+ char *buf;
+ char *p;
+ size_t size;
+} myFILE;
+
+static int mygetc(myFILE *f)
+{
+ if (f->p >= f->buf+f->size) return EOF;
+ /* be sure to return chars >127 as positive values */
+ return (int)( *(f->p++) & 0x00FF );
+}
+
+static void myfile_close(myFILE *f)
+{
+ if (!f) return;
+ SAFE_FREE(f->buf);
+ SAFE_FREE(f);
+}
+
+/* -------------------------------------------------------------------------- **
+ * Functions...
+ */
+
+static int EatWhitespace( myFILE *InFile )
+ /* ------------------------------------------------------------------------ **
+ * Scan past whitespace (see ctype(3C)) and return the first non-whitespace
+ * character, or newline, or EOF.
+ *
+ * Input: InFile - Input source.
+ *
+ * Output: The next non-whitespace character in the input stream.
+ *
+ * Notes: Because the config files use a line-oriented grammar, we
+ * explicitly exclude the newline character from the list of
+ * whitespace characters.
+ * - Note that both EOF (-1) and the nul character ('\0') are
+ * considered end-of-file markers.
+ *
+ * ------------------------------------------------------------------------ **
+ */
+ {
+ int c;
+
+ for( c = mygetc( InFile ); isspace( c ) && ('\n' != c); c = mygetc( InFile ) )
+ ;
+ return( c );
+ } /* EatWhitespace */
+
+static int EatComment( myFILE *InFile )
+ /* ------------------------------------------------------------------------ **
+ * Scan to the end of a comment.
+ *
+ * Input: InFile - Input source.
+ *
+ * Output: The character that marks the end of the comment. Normally,
+ * this will be a newline, but it *might* be an EOF.
+ *
+ * Notes: Because the config files use a line-oriented grammar, we
+ * explicitly exclude the newline character from the list of
+ * whitespace characters.
+ * - Note that both EOF (-1) and the nul character ('\0') are
+ * considered end-of-file markers.
+ *
+ * ------------------------------------------------------------------------ **
+ */
+ {
+ int c;
+
+ for( c = mygetc( InFile ); ('\n'!=c) && (EOF!=c) && (c>0); c = mygetc( InFile ) )
+ ;
+ return( c );
+ } /* EatComment */
+
+/*****************************************************************************
+ * Scan backards within a string to discover if the last non-whitespace
+ * character is a line-continuation character ('\\').
+ *
+ * Input: line - A pointer to a buffer containing the string to be
+ * scanned.
+ * pos - This is taken to be the offset of the end of the
+ * string. This position is *not* scanned.
+ *
+ * Output: The offset of the '\\' character if it was found, or -1 to
+ * indicate that it was not.
+ *
+ *****************************************************************************/
+
+static int Continuation(char *line, int pos )
+{
+ pos--;
+ while( (pos >= 0) && isspace((int)line[pos]))
+ pos--;
+
+ return (((pos >= 0) && ('\\' == line[pos])) ? pos : -1 );
+}
+
+
+static BOOL Section( myFILE *InFile, BOOL (*sfunc)(const char *) )
+ /* ------------------------------------------------------------------------ **
+ * Scan a section name, and pass the name to function sfunc().
+ *
+ * Input: InFile - Input source.
+ * sfunc - Pointer to the function to be called if the section
+ * name is successfully read.
+ *
+ * Output: True if the section name was read and True was returned from
+ * <sfunc>. False if <sfunc> failed or if a lexical error was
+ * encountered.
+ *
+ * ------------------------------------------------------------------------ **
+ */
+ {
+ int c;
+ int i;
+ int end;
+ const char *func = "params.c:Section() -";
+
+ i = 0; /* <i> is the offset of the next free byte in bufr[] and */
+ end = 0; /* <end> is the current "end of string" offset. In most */
+ /* cases these will be the same, but if the last */
+ /* character written to bufr[] is a space, then <end> */
+ /* will be one less than <i>. */
+
+ c = EatWhitespace( InFile ); /* We've already got the '['. Scan */
+ /* past initial white space. */
+
+ while( (EOF != c) && (c > 0) )
+ {
+
+ /* Check that the buffer is big enough for the next character. */
+ if( i > (bSize - 2) )
+ {
+ char *tb;
+
+ tb = Realloc( bufr, bSize +BUFR_INC );
+ if( NULL == tb )
+ {
+ DEBUG(0, ("%s Memory re-allocation failure.", func) );
+ return( False );
+ }
+ bufr = tb;
+ bSize += BUFR_INC;
+ }
+
+ /* Handle a single character. */
+ switch( c )
+ {
+ case ']': /* Found the closing bracket. */
+ bufr[end] = '\0';
+ if( 0 == end ) /* Don't allow an empty name. */
+ {
+ DEBUG(0, ("%s Empty section name in configuration file.\n", func ));
+ return( False );
+ }
+ if( !sfunc(bufr) ) /* Got a valid name. Deal with it. */
+ return( False );
+ (void)EatComment( InFile ); /* Finish off the line. */
+ return( True );
+
+ case '\n': /* Got newline before closing ']'. */
+ i = Continuation( bufr, i ); /* Check for line continuation. */
+ if( i < 0 )
+ {
+ bufr[end] = '\0';
+ DEBUG(0, ("%s Badly formed line in configuration file: %s\n",
+ func, bufr ));
+ return( False );
+ }
+ end = ( (i > 0) && (' ' == bufr[i - 1]) ) ? (i - 1) : (i);
+ c = mygetc( InFile ); /* Continue with next line. */
+ break;
+
+ default: /* All else are a valid name chars. */
+ if( isspace( c ) ) /* One space per whitespace region. */
+ {
+ bufr[end] = ' ';
+ i = end + 1;
+ c = EatWhitespace( InFile );
+ }
+ else /* All others copy verbatim. */
+ {
+ bufr[i++] = c;
+ end = i;
+ c = mygetc( InFile );
+ }
+ }
+ }
+
+ /* We arrive here if we've met the EOF before the closing bracket. */
+ DEBUG(0, ("%s Unexpected EOF in the configuration file: %s\n", func, bufr ));
+ return( False );
+ } /* Section */
+
+static BOOL Parameter( myFILE *InFile, BOOL (*pfunc)(const char *, const char *), int c )
+ /* ------------------------------------------------------------------------ **
+ * Scan a parameter name and value, and pass these two fields to pfunc().
+ *
+ * Input: InFile - The input source.
+ * pfunc - A pointer to the function that will be called to
+ * process the parameter, once it has been scanned.
+ * c - The first character of the parameter name, which
+ * would have been read by Parse(). Unlike a comment
+ * line or a section header, there is no lead-in
+ * character that can be discarded.
+ *
+ * Output: True if the parameter name and value were scanned and processed
+ * successfully, else False.
+ *
+ * Notes: This function is in two parts. The first loop scans the
+ * parameter name. Internal whitespace is compressed, and an
+ * equal sign (=) terminates the token. Leading and trailing
+ * whitespace is discarded. The second loop scans the parameter
+ * value. When both have been successfully identified, they are
+ * passed to pfunc() for processing.
+ *
+ * ------------------------------------------------------------------------ **
+ */
+ {
+ int i = 0; /* Position within bufr. */
+ int end = 0; /* bufr[end] is current end-of-string. */
+ int vstart = 0; /* Starting position of the parameter value. */
+ const char *func = "params.c:Parameter() -";
+
+ /* Read the parameter name. */
+ while( 0 == vstart ) /* Loop until we've found the start of the value. */
+ {
+
+ if( i > (bSize - 2) ) /* Ensure there's space for next char. */
+ {
+ char *tb;
+
+ tb = Realloc( bufr, bSize + BUFR_INC );
+ if( NULL == tb )
+ {
+ DEBUG(0, ("%s Memory re-allocation failure.", func) );
+ return( False );
+ }
+ bufr = tb;
+ bSize += BUFR_INC;
+ }
+
+ switch( c )
+ {
+ case '=': /* Equal sign marks end of param name. */
+ if( 0 == end ) /* Don't allow an empty name. */
+ {
+ DEBUG(0, ("%s Invalid parameter name in config. file.\n", func ));
+ return( False );
+ }
+ bufr[end++] = '\0'; /* Mark end of string & advance. */
+ i = end; /* New string starts here. */
+ vstart = end; /* New string is parameter value. */
+ bufr[i] = '\0'; /* New string is nul, for now. */
+ break;
+
+ case '\n': /* Find continuation char, else error. */
+ i = Continuation( bufr, i );
+ if( i < 0 )
+ {
+ bufr[end] = '\0';
+ DEBUG(1,("%s Ignoring badly formed line in configuration file: %s\n",
+ func, bufr ));
+ return( True );
+ }
+ end = ( (i > 0) && (' ' == bufr[i - 1]) ) ? (i - 1) : (i);
+ c = mygetc( InFile ); /* Read past eoln. */
+ break;
+
+ case '\0': /* Shouldn't have EOF within param name. */
+ case EOF:
+ bufr[i] = '\0';
+ DEBUG(1,("%s Unexpected end-of-file at: %s\n", func, bufr ));
+ return( True );
+
+ default:
+ if( isspace( c ) ) /* One ' ' per whitespace region. */
+ {
+ bufr[end] = ' ';
+ i = end + 1;
+ c = EatWhitespace( InFile );
+ }
+ else /* All others verbatim. */
+ {
+ bufr[i++] = c;
+ end = i;
+ c = mygetc( InFile );
+ }
+ }
+ }
+
+ /* Now parse the value. */
+ c = EatWhitespace( InFile ); /* Again, trim leading whitespace. */
+ while( (EOF !=c) && (c > 0) )
+ {
+
+ if( i > (bSize - 2) ) /* Make sure there's enough room. */
+ {
+ char *tb;
+
+ tb = Realloc( bufr, bSize + BUFR_INC );
+ if( NULL == tb )
+ {
+ DEBUG(0, ("%s Memory re-allocation failure.", func) );
+ return( False );
+ }
+ bufr = tb;
+ bSize += BUFR_INC;
+ }
+
+ switch( c )
+ {
+ case '\r': /* Explicitly remove '\r' because the older */
+ c = mygetc( InFile ); /* version called fgets_slash() which also */
+ break; /* removes them. */
+
+ case '\n': /* Marks end of value unless there's a '\'. */
+ i = Continuation( bufr, i );
+ if( i < 0 )
+ c = 0;
+ else
+ {
+ for( end = i; (end >= 0) && isspace((int)bufr[end]); end-- )
+ ;
+ c = mygetc( InFile );
+ }
+ break;
+
+ default: /* All others verbatim. Note that spaces do */
+ bufr[i++] = c; /* not advance <end>. This allows trimming */
+ if( !isspace( c ) ) /* of whitespace at the end of the line. */
+ end = i;
+ c = mygetc( InFile );
+ break;
+ }
+ }
+ bufr[end] = '\0'; /* End of value. */
+
+ return( pfunc( bufr, &bufr[vstart] ) ); /* Pass name & value to pfunc(). */
+ } /* Parameter */
+
+static BOOL Parse( myFILE *InFile,
+ BOOL (*sfunc)(const char *),
+ BOOL (*pfunc)(const char *, const char *) )
+ /* ------------------------------------------------------------------------ **
+ * Scan & parse the input.
+ *
+ * Input: InFile - Input source.
+ * sfunc - Function to be called when a section name is scanned.
+ * See Section().
+ * pfunc - Function to be called when a parameter is scanned.
+ * See Parameter().
+ *
+ * Output: True if the file was successfully scanned, else False.
+ *
+ * Notes: The input can be viewed in terms of 'lines'. There are four
+ * types of lines:
+ * Blank - May contain whitespace, otherwise empty.
+ * Comment - First non-whitespace character is a ';' or '#'.
+ * The remainder of the line is ignored.
+ * Section - First non-whitespace character is a '['.
+ * Parameter - The default case.
+ *
+ * ------------------------------------------------------------------------ **
+ */
+ {
+ int c;
+
+ c = EatWhitespace( InFile );
+ while( (EOF != c) && (c > 0) )
+ {
+ switch( c )
+ {
+ case '\n': /* Blank line. */
+ c = EatWhitespace( InFile );
+ break;
+
+ case ';': /* Comment line. */
+ case '#':
+ c = EatComment( InFile );
+ break;
+
+ case '[': /* Section Header. */
+ if( !Section( InFile, sfunc ) )
+ return( False );
+ c = EatWhitespace( InFile );
+ break;
+
+ case '\\': /* Bogus backslash. */
+ c = EatWhitespace( InFile );
+ break;
+
+ default: /* Parameter line. */
+ if( !Parameter( InFile, pfunc, c ) )
+ return( False );
+ c = EatWhitespace( InFile );
+ break;
+ }
+ }
+ return( True );
+ } /* Parse */
+
+static myFILE *OpenConfFile( const char *FileName )
+ /* ------------------------------------------------------------------------ **
+ * Open a configuration file.
+ *
+ * Input: FileName - The pathname of the config file to be opened.
+ *
+ * Output: A pointer of type (char **) to the lines of the file
+ *
+ * ------------------------------------------------------------------------ **
+ */
+ {
+ const char *func = "params.c:OpenConfFile() -";
+ extern BOOL in_client;
+ int lvl = in_client?1:0;
+ myFILE *ret;
+
+ ret = (myFILE *)malloc(sizeof(*ret));
+ if (!ret) return NULL;
+
+ ret->buf = file_load(FileName, &ret->size);
+ if( NULL == ret->buf )
+ {
+ DEBUG( lvl,
+ ("%s Unable to open configuration file \"%s\":\n\t%s\n",
+ func, FileName, strerror(errno)) );
+ SAFE_FREE(ret);
+ return NULL;
+ }
+
+ ret->p = ret->buf;
+ return( ret );
+ } /* OpenConfFile */
+
+BOOL pm_process( const char *FileName,
+ BOOL (*sfunc)(const char *),
+ BOOL (*pfunc)(const char *, const char *) )
+ /* ------------------------------------------------------------------------ **
+ * Process the named parameter file.
+ *
+ * Input: FileName - The pathname of the parameter file to be opened.
+ * sfunc - A pointer to a function that will be called when
+ * a section name is discovered.
+ * pfunc - A pointer to a function that will be called when
+ * a parameter name and value are discovered.
+ *
+ * Output: TRUE if the file was successfully parsed, else FALSE.
+ *
+ * ------------------------------------------------------------------------ **
+ */
+ {
+ int result;
+ myFILE *InFile;
+ const char *func = "params.c:pm_process() -";
+
+ InFile = OpenConfFile( FileName ); /* Open the config file. */
+ if( NULL == InFile )
+ return( False );
+
+ DEBUG( 3, ("%s Processing configuration file \"%s\"\n", func, FileName) );
+
+ if( NULL != bufr ) /* If we already have a buffer */
+ result = Parse( InFile, sfunc, pfunc ); /* (recursive call), then just */
+ /* use it. */
+
+ else /* If we don't have a buffer */
+ { /* allocate one, then parse, */
+ bSize = BUFR_INC; /* then free. */
+ bufr = (char *)malloc( bSize );
+ if( NULL == bufr )
+ {
+ DEBUG(0,("%s memory allocation failure.\n", func));
+ myfile_close(InFile);
+ return( False );
+ }
+ result = Parse( InFile, sfunc, pfunc );
+ SAFE_FREE( bufr );
+ bufr = NULL;
+ bSize = 0;
+ }
+
+ myfile_close(InFile);
+
+ if( !result ) /* Generic failure. */
+ {
+ DEBUG(0,("%s Failed. Error returned from params.c:parse().\n", func));
+ return( False );
+ }
+
+ return( True ); /* Generic success. */
+ } /* pm_process */
+
+/* -------------------------------------------------------------------------- */
diff --git a/source/passdb/.cvsignore b/source/passdb/.cvsignore
new file mode 100644
index 00000000000..5f2a5c4cf75
--- /dev/null
+++ b/source/passdb/.cvsignore
@@ -0,0 +1,2 @@
+*.po
+*.po32
diff --git a/source/passdb/login_cache.c b/source/passdb/login_cache.c
new file mode 100644
index 00000000000..4b760172ec2
--- /dev/null
+++ b/source/passdb/login_cache.c
@@ -0,0 +1,174 @@
+/*
+ Unix SMB/CIFS implementation.
+ SAM_ACCOUNT local cache for
+ Copyright (C) Jim McDonough (jmcd@us.ibm.com) 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 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"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_PASSDB
+
+#define LOGIN_CACHE_FILE "login_cache.tdb"
+
+#define SAM_CACHE_FORMAT "dwwd"
+
+static TDB_CONTEXT *cache;
+
+BOOL login_cache_init(void)
+{
+ char* cache_fname = NULL;
+
+ /* skip file open if it's already opened */
+ if (cache) return True;
+
+ asprintf(&cache_fname, "%s/%s", lp_lockdir(), LOGIN_CACHE_FILE);
+ if (cache_fname)
+ DEBUG(5, ("Opening cache file at %s\n", cache_fname));
+ else {
+ DEBUG(0, ("Filename allocation failed.\n"));
+ return False;
+ }
+
+ cache = tdb_open_log(cache_fname, 0, TDB_DEFAULT,
+ O_RDWR|O_CREAT, 0644);
+
+ if (!cache)
+ DEBUG(5, ("Attempt to open %s failed.\n", cache_fname));
+
+ SAFE_FREE(cache_fname);
+
+ return (cache ? True : False);
+}
+
+BOOL login_cache_shutdown(void)
+{
+ /* tdb_close routine returns -1 on error */
+ if (!cache) return False;
+ DEBUG(5, ("Closing cache file\n"));
+ return tdb_close(cache) != -1;
+}
+
+/* if we can't read the cache, oh well, no need to return anything */
+LOGIN_CACHE * login_cache_read(SAM_ACCOUNT *sampass)
+{
+ TDB_DATA keybuf, databuf;
+ LOGIN_CACHE *entry;
+
+ if (!login_cache_init())
+ return NULL;
+
+ keybuf.dptr = strdup(pdb_get_nt_username(sampass));
+ if (!keybuf.dptr || !strlen(keybuf.dptr)) {
+ SAFE_FREE(keybuf.dptr);
+ return NULL;
+ }
+ keybuf.dsize = strlen(keybuf.dptr) + 1;
+
+ DEBUG(7, ("Looking up login cache for user %s\n",
+ keybuf.dptr));
+ databuf = tdb_fetch(cache, keybuf);
+ SAFE_FREE(keybuf.dptr);
+
+ if (!(entry = malloc(sizeof(LOGIN_CACHE)))) {
+ DEBUG(1, ("Unable to allocate cache entry buffer!\n"));
+ SAFE_FREE(databuf.dptr);
+ return NULL;
+ }
+
+ if (tdb_unpack (databuf.dptr, databuf.dsize, SAM_CACHE_FORMAT,
+ &entry->entry_timestamp, &entry->acct_ctrl,
+ &entry->bad_password_count,
+ &entry->bad_password_time) == -1) {
+ DEBUG(7, ("No cache entry found\n"));
+ SAFE_FREE(databuf.dptr);
+ return NULL;
+ }
+
+ DEBUG(5, ("Found login cache entry: timestamp %12u, flags 0x%x, count %d, time %12u\n",
+ entry->entry_timestamp, entry->acct_ctrl,
+ entry->bad_password_count, entry->bad_password_time));
+ return entry;
+}
+
+BOOL login_cache_write(const SAM_ACCOUNT *sampass, LOGIN_CACHE entry)
+{
+
+ TDB_DATA keybuf, databuf;
+ BOOL ret;
+
+
+ keybuf.dptr = strdup(pdb_get_nt_username(sampass));
+ if (!keybuf.dptr || !strlen(keybuf.dptr)) {
+ SAFE_FREE(keybuf.dptr);
+ return False;
+ }
+ keybuf.dsize = strlen(keybuf.dptr) + 1;
+
+ entry.entry_timestamp = time(NULL);
+
+ databuf.dsize =
+ tdb_pack(NULL, 0, SAM_CACHE_FORMAT,
+ entry.entry_timestamp,
+ entry.acct_ctrl,
+ entry.bad_password_count,
+ entry.bad_password_time);
+ databuf.dptr = malloc(databuf.dsize);
+ if (!databuf.dptr) {
+ SAFE_FREE(keybuf.dptr);
+ return False;
+ }
+
+ if (tdb_pack(databuf.dptr, databuf.dsize, SAM_CACHE_FORMAT,
+ entry.entry_timestamp,
+ entry.acct_ctrl,
+ entry.bad_password_count,
+ entry.bad_password_time)
+ != databuf.dsize) {
+ SAFE_FREE(keybuf.dptr);
+ SAFE_FREE(databuf.dptr);
+ return False;
+ }
+
+ ret = tdb_store(cache, keybuf, databuf, 0);
+ SAFE_FREE(keybuf.dptr);
+ SAFE_FREE(databuf.dptr);
+ return ret == 0;
+}
+
+BOOL login_cache_delentry(const SAM_ACCOUNT *sampass)
+{
+ int ret;
+ TDB_DATA keybuf;
+
+ if (!login_cache_init())
+ return False;
+
+ keybuf.dptr = strdup(pdb_get_nt_username(sampass));
+ if (!keybuf.dptr || !strlen(keybuf.dptr)) {
+ SAFE_FREE(keybuf.dptr);
+ return False;
+ }
+ keybuf.dsize = strlen(keybuf.dptr) + 1;
+ DEBUG(9, ("About to delete entry for %s\n", keybuf.dptr));
+ ret = tdb_delete(cache, keybuf);
+ DEBUG(9, ("tdb_delete returned %d\n", ret));
+
+ SAFE_FREE(keybuf.dptr);
+ return ret == 0;
+}
+
diff --git a/source/passdb/lookup_sid.c b/source/passdb/lookup_sid.c
new file mode 100644
index 00000000000..a365cba0082
--- /dev/null
+++ b/source/passdb/lookup_sid.c
@@ -0,0 +1,529 @@
+/*
+ Unix SMB/CIFS implementation.
+ uid/user handling
+ Copyright (C) Andrew Tridgell 1992-1998
+ Copyright (C) Gerald (Jerry) Carter 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 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"
+
+/*****************************************************************
+ *THE CANONICAL* convert name to SID function.
+ Tries local lookup first - for local domains - then uses winbind.
+*****************************************************************/
+
+BOOL lookup_name(const char *domain, const char *name, DOM_SID *psid, enum SID_NAME_USE *name_type)
+{
+ fstring sid;
+ BOOL local_lookup = False;
+
+ *name_type = SID_NAME_UNKNOWN;
+
+ /* If we are looking up a domain user, make sure it is
+ for the local machine only */
+
+ if (strequal(domain, get_global_sam_name())) {
+ if (local_lookup_name(name, psid, name_type)) {
+ DEBUG(10,
+ ("lookup_name: (local) [%s]\\[%s] -> SID %s (type %s: %u)\n",
+ domain, name, sid_to_string(sid,psid),
+ sid_type_lookup(*name_type), (unsigned int)*name_type));
+ return True;
+ }
+ } else {
+ /* Remote */
+ if (winbind_lookup_name(domain, name, psid, name_type)) {
+
+ DEBUG(10,("lookup_name (winbindd): [%s]\\[%s] -> SID %s (type %u)\n",
+ domain, name, sid_to_string(sid, psid),
+ (unsigned int)*name_type));
+ return True;
+ }
+ }
+
+ DEBUG(10, ("lookup_name: %s lookup for [%s]\\[%s] failed\n",
+ local_lookup ? "local" : "winbind", domain, name));
+
+ return False;
+}
+
+/*****************************************************************
+ *THE CANONICAL* convert SID to name function.
+ Tries local lookup first - for local sids, then tries winbind.
+*****************************************************************/
+
+BOOL lookup_sid(const DOM_SID *sid, fstring dom_name, fstring name, enum SID_NAME_USE *name_type)
+{
+ if (!name_type)
+ return False;
+
+ *name_type = SID_NAME_UNKNOWN;
+
+ /* Check if this is our own sid. This should perhaps be done by
+ winbind? For the moment handle it here. */
+
+ if (sid->num_auths == 5) {
+ DOM_SID tmp_sid;
+ uint32 rid;
+
+ sid_copy(&tmp_sid, sid);
+ sid_split_rid(&tmp_sid, &rid);
+
+ if (sid_equal(get_global_sam_sid(), &tmp_sid)) {
+
+ return map_domain_sid_to_name(&tmp_sid, dom_name) &&
+ local_lookup_sid(sid, name, name_type);
+ }
+ }
+
+ if (!winbind_lookup_sid(sid, dom_name, name, name_type)) {
+ fstring sid_str;
+ DOM_SID tmp_sid;
+ uint32 rid;
+
+ DEBUG(10,("lookup_sid: winbind lookup for SID %s failed - trying local.\n", sid_to_string(sid_str, sid) ));
+
+ sid_copy(&tmp_sid, sid);
+ sid_split_rid(&tmp_sid, &rid);
+ return map_domain_sid_to_name(&tmp_sid, dom_name) &&
+ lookup_known_rid(&tmp_sid, rid, name, name_type);
+ }
+ return True;
+}
+
+BOOL sid_to_local_user_name(const DOM_SID *sid, fstring username)
+{
+ fstring dom_name;
+ fstring name;
+ enum SID_NAME_USE type;
+
+ if (!sid_check_is_in_our_domain(sid))
+ return False;
+
+ if (!lookup_sid(sid, dom_name, name, &type))
+ return False;
+
+ if (type != SID_NAME_USER)
+ return False;
+
+ fstrcpy(username, name);
+ return True;
+}
+
+BOOL sid_to_local_dom_grp_name(const DOM_SID *sid, fstring groupname)
+{
+ fstring dom_name;
+ fstring name;
+ enum SID_NAME_USE type;
+
+ if (!sid_check_is_in_our_domain(sid))
+ return False;
+
+ if (!lookup_sid(sid, dom_name, name, &type))
+ return False;
+
+ if (type != SID_NAME_DOM_GRP)
+ return False;
+
+ fstrcpy(groupname, name);
+ return True;
+}
+
+
+/*****************************************************************
+ Id mapping cache. This is to avoid Winbind mappings already
+ seen by smbd to be queried too frequently, keeping winbindd
+ busy, and blocking smbd while winbindd is busy with other
+ stuff. Written by Michael Steffens <michael.steffens@hp.com>,
+ modified to use linked lists by jra.
+*****************************************************************/
+
+#define MAX_UID_SID_CACHE_SIZE 100
+#define TURNOVER_UID_SID_CACHE_SIZE 10
+#define MAX_GID_SID_CACHE_SIZE 100
+#define TURNOVER_GID_SID_CACHE_SIZE 10
+
+static size_t n_uid_sid_cache = 0;
+static size_t n_gid_sid_cache = 0;
+
+static struct uid_sid_cache {
+ struct uid_sid_cache *next, *prev;
+ uid_t uid;
+ DOM_SID sid;
+ enum SID_NAME_USE sidtype;
+} *uid_sid_cache_head;
+
+static struct gid_sid_cache {
+ struct gid_sid_cache *next, *prev;
+ gid_t gid;
+ DOM_SID sid;
+ enum SID_NAME_USE sidtype;
+} *gid_sid_cache_head;
+
+/*****************************************************************
+ Find a SID given a uid.
+*****************************************************************/
+
+static BOOL fetch_sid_from_uid_cache(DOM_SID *psid, uid_t uid)
+{
+ struct uid_sid_cache *pc;
+
+ for (pc = uid_sid_cache_head; pc; pc = pc->next) {
+ if (pc->uid == uid) {
+ fstring sid;
+ *psid = pc->sid;
+ DEBUG(3,("fetch sid from uid cache %u -> %s\n",
+ (unsigned int)uid, sid_to_string(sid, psid)));
+ DLIST_PROMOTE(uid_sid_cache_head, pc);
+ return True;
+ }
+ }
+ return False;
+}
+
+/*****************************************************************
+ Find a uid given a SID.
+*****************************************************************/
+
+static BOOL fetch_uid_from_cache( uid_t *puid, const DOM_SID *psid )
+{
+ struct uid_sid_cache *pc;
+
+ for (pc = uid_sid_cache_head; pc; pc = pc->next) {
+ if (sid_compare(&pc->sid, psid) == 0) {
+ fstring sid;
+ *puid = pc->uid;
+ DEBUG(3,("fetch uid from cache %u -> %s\n",
+ (unsigned int)*puid, sid_to_string(sid, psid)));
+ DLIST_PROMOTE(uid_sid_cache_head, pc);
+ return True;
+ }
+ }
+ return False;
+}
+
+/*****************************************************************
+ Store uid to SID mapping in cache.
+*****************************************************************/
+
+static void store_uid_sid_cache(const DOM_SID *psid, uid_t uid)
+{
+ struct uid_sid_cache *pc;
+
+ if (n_uid_sid_cache >= MAX_UID_SID_CACHE_SIZE && n_uid_sid_cache > TURNOVER_UID_SID_CACHE_SIZE) {
+ /* Delete the last TURNOVER_UID_SID_CACHE_SIZE entries. */
+ struct uid_sid_cache *pc_next;
+ size_t i;
+
+ for (i = 0, pc = uid_sid_cache_head; i < (n_uid_sid_cache - TURNOVER_UID_SID_CACHE_SIZE); i++, pc = pc->next)
+ ;
+ for(; pc; pc = pc_next) {
+ pc_next = pc->next;
+ DLIST_REMOVE(uid_sid_cache_head,pc);
+ SAFE_FREE(pc);
+ n_uid_sid_cache--;
+ }
+ }
+
+ pc = (struct uid_sid_cache *)malloc(sizeof(struct uid_sid_cache));
+ if (!pc)
+ return;
+ pc->uid = uid;
+ sid_copy(&pc->sid, psid);
+ DLIST_ADD(uid_sid_cache_head, pc);
+ n_uid_sid_cache++;
+}
+
+/*****************************************************************
+ Find a SID given a gid.
+*****************************************************************/
+
+static BOOL fetch_sid_from_gid_cache(DOM_SID *psid, gid_t gid)
+{
+ struct gid_sid_cache *pc;
+
+ for (pc = gid_sid_cache_head; pc; pc = pc->next) {
+ if (pc->gid == gid) {
+ fstring sid;
+ *psid = pc->sid;
+ DEBUG(3,("fetch sid from gid cache %u -> %s\n",
+ (unsigned int)gid, sid_to_string(sid, psid)));
+ DLIST_PROMOTE(gid_sid_cache_head, pc);
+ return True;
+ }
+ }
+ return False;
+}
+
+/*****************************************************************
+ Find a gid given a SID.
+*****************************************************************/
+
+static BOOL fetch_gid_from_cache(gid_t *pgid, const DOM_SID *psid)
+{
+ struct gid_sid_cache *pc;
+
+ for (pc = gid_sid_cache_head; pc; pc = pc->next) {
+ if (sid_compare(&pc->sid, psid) == 0) {
+ fstring sid;
+ *pgid = pc->gid;
+ DEBUG(3,("fetch uid from cache %u -> %s\n",
+ (unsigned int)*pgid, sid_to_string(sid, psid)));
+ DLIST_PROMOTE(gid_sid_cache_head, pc);
+ return True;
+ }
+ }
+ return False;
+}
+
+/*****************************************************************
+ Store gid to SID mapping in cache.
+*****************************************************************/
+
+static void store_gid_sid_cache(const DOM_SID *psid, gid_t gid)
+{
+ struct gid_sid_cache *pc;
+
+ if (n_gid_sid_cache >= MAX_GID_SID_CACHE_SIZE && n_gid_sid_cache > TURNOVER_GID_SID_CACHE_SIZE) {
+ /* Delete the last TURNOVER_GID_SID_CACHE_SIZE entries. */
+ struct gid_sid_cache *pc_next;
+ size_t i;
+
+ for (i = 0, pc = gid_sid_cache_head; i < (n_gid_sid_cache - TURNOVER_GID_SID_CACHE_SIZE); i++, pc = pc->next)
+ ;
+ for(; pc; pc = pc_next) {
+ pc_next = pc->next;
+ DLIST_REMOVE(gid_sid_cache_head,pc);
+ SAFE_FREE(pc);
+ n_gid_sid_cache--;
+ }
+ }
+
+ pc = (struct gid_sid_cache *)malloc(sizeof(struct gid_sid_cache));
+ if (!pc)
+ return;
+ pc->gid = gid;
+ sid_copy(&pc->sid, psid);
+ DLIST_ADD(gid_sid_cache_head, pc);
+ n_gid_sid_cache++;
+}
+
+/*****************************************************************
+ *THE CANONICAL* convert uid_t to SID function.
+*****************************************************************/
+
+NTSTATUS uid_to_sid(DOM_SID *psid, uid_t uid)
+{
+ fstring sid;
+ uid_t low, high;
+
+ ZERO_STRUCTP(psid);
+
+ if (fetch_sid_from_uid_cache(psid, uid))
+ return ( psid ? NT_STATUS_OK : NT_STATUS_UNSUCCESSFUL );
+
+ /* DC's never use winbindd to resolve users outside the
+ defined idmap range */
+
+ if ( lp_server_role()==ROLE_DOMAIN_MEMBER
+ || (lp_idmap_uid(&low, &high) && uid >= low && uid <= high) )
+ {
+ if (winbind_uid_to_sid(psid, uid)) {
+
+ DEBUG(10,("uid_to_sid: winbindd %u -> %s\n",
+ (unsigned int)uid, sid_to_string(sid, psid)));
+
+ if (psid)
+ store_uid_sid_cache(psid, uid);
+ return ( psid ? NT_STATUS_OK : NT_STATUS_UNSUCCESSFUL );
+ }
+ }
+
+ if (!local_uid_to_sid(psid, uid)) {
+ DEBUG(10,("uid_to_sid: local %u failed to map to sid\n", (unsigned int)uid ));
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ DEBUG(10,("uid_to_sid: local %u -> %s\n", (unsigned int)uid, sid_to_string(sid, psid)));
+
+ store_uid_sid_cache(psid, uid);
+ return NT_STATUS_OK;
+}
+
+/*****************************************************************
+ *THE CANONICAL* convert gid_t to SID function.
+*****************************************************************/
+
+NTSTATUS gid_to_sid(DOM_SID *psid, gid_t gid)
+{
+ fstring sid;
+ gid_t low, high;
+
+ ZERO_STRUCTP(psid);
+
+ if (fetch_sid_from_gid_cache(psid, gid))
+ return ( psid ? NT_STATUS_OK : NT_STATUS_UNSUCCESSFUL );
+
+ /* DC's never use winbindd to resolve groups outside the
+ defined idmap range */
+
+ if ( lp_server_role()==ROLE_DOMAIN_MEMBER
+ || (lp_idmap_gid(&low, &high) && gid >= low && gid <= high) )
+ {
+ if (winbind_gid_to_sid(psid, gid)) {
+
+ DEBUG(10,("gid_to_sid: winbindd %u -> %s\n",
+ (unsigned int)gid, sid_to_string(sid, psid)));
+
+ if (psid)
+ store_gid_sid_cache(psid, gid);
+ return ( psid ? NT_STATUS_OK : NT_STATUS_UNSUCCESSFUL );
+ }
+ }
+
+ if (!local_gid_to_sid(psid, gid)) {
+ DEBUG(10,("gid_to_sid: local %u failed to map to sid\n", (unsigned int)gid ));
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ DEBUG(10,("gid_to_sid: local %u -> %s\n", (unsigned int)gid, sid_to_string(sid, psid)));
+
+ store_gid_sid_cache(psid, gid);
+ return NT_STATUS_OK;
+}
+
+/*****************************************************************
+ *THE CANONICAL* convert SID to uid function.
+*****************************************************************/
+
+NTSTATUS sid_to_uid(const DOM_SID *psid, uid_t *puid)
+{
+ fstring dom_name, name, sid_str;
+ enum SID_NAME_USE name_type;
+
+ if (fetch_uid_from_cache(puid, psid))
+ return NT_STATUS_OK;
+
+ /* if this is our SID then go straight to a local lookup */
+
+ if ( sid_compare_domain(get_global_sam_sid(), psid) == 0 ) {
+ DEBUG(10,("sid_to_uid: my domain (%s) - trying local.\n",
+ sid_string_static(psid) ));
+
+ if ( local_sid_to_uid(puid, psid, &name_type) )
+ goto success;
+
+ DEBUG(10,("sid_to_uid: local lookup failed\n"));
+
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ /* If it is not our local domain, only hope is winbindd */
+
+ if ( !winbind_lookup_sid(psid, dom_name, name, &name_type) ) {
+ DEBUG(10,("sid_to_uid: winbind lookup for non-local sid %s failed\n",
+ sid_string_static(psid) ));
+
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ /* If winbindd does know the SID, ensure this is a user */
+
+ if (name_type != SID_NAME_USER) {
+ DEBUG(10,("sid_to_uid: winbind lookup succeeded but SID is not a user (%u)\n",
+ (unsigned int)name_type ));
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ /* get the uid. Has to work or else we are dead in the water */
+
+ if ( !winbind_sid_to_uid(puid, psid) ) {
+ DEBUG(10,("sid_to_uid: winbind failed to allocate a new uid for sid %s\n",
+ sid_to_string(sid_str, psid) ));
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+success:
+ DEBUG(10,("sid_to_uid: %s -> %u\n", sid_to_string(sid_str, psid),
+ (unsigned int)*puid ));
+
+ store_uid_sid_cache(psid, *puid);
+
+ return NT_STATUS_OK;
+}
+/*****************************************************************
+ *THE CANONICAL* convert SID to gid function.
+ Group mapping is used for gids that maps to Wellknown SIDs
+*****************************************************************/
+
+NTSTATUS sid_to_gid(const DOM_SID *psid, gid_t *pgid)
+{
+ fstring dom_name, name, sid_str;
+ enum SID_NAME_USE name_type;
+
+ if (fetch_gid_from_cache(pgid, psid))
+ return NT_STATUS_OK;
+
+ /*
+ * First we must look up the name and decide if this is a group sid.
+ * Group mapping can deal with foreign SIDs
+ */
+
+ if (!winbind_lookup_sid(psid, dom_name, name, &name_type)) {
+ DEBUG(10,("sid_to_gid: winbind lookup for sid %s failed - trying local.\n",
+ sid_to_string(sid_str, psid) ));
+
+ if ( local_sid_to_gid(pgid, psid, &name_type) )
+ goto success;
+
+ DEBUG(10,("sid_to_gid: no one knows this SID\n"));
+
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ /* winbindd knows it; Ensure this is a group sid */
+
+ if ((name_type != SID_NAME_DOM_GRP) && (name_type != SID_NAME_ALIAS)
+ && (name_type != SID_NAME_WKN_GRP))
+ {
+ DEBUG(10,("sid_to_gid: winbind lookup succeeded but SID is not a known group (%u)\n",
+ (unsigned int)name_type ));
+
+ /* winbindd is running and knows about this SID. Just the wrong type.
+ Don't fallback to a local lookup here */
+
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ /* winbindd knows it and it is a type of group; sid_to_gid must succeed
+ or we are dead in the water */
+
+ if ( !winbind_sid_to_gid(pgid, psid) ) {
+ DEBUG(10,("sid_to_uid: winbind failed to allocate a new gid for sid %s\n",
+ sid_to_string(sid_str, psid) ));
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+success:
+ DEBUG(10,("sid_to_gid: %s -> %u\n", sid_to_string(sid_str, psid),
+ (unsigned int)*pgid ));
+
+ store_gid_sid_cache(psid, *pgid);
+
+ return NT_STATUS_OK;
+}
+
diff --git a/source/passdb/machine_sid.c b/source/passdb/machine_sid.c
new file mode 100644
index 00000000000..47b9e2d487e
--- /dev/null
+++ b/source/passdb/machine_sid.c
@@ -0,0 +1,213 @@
+/*
+ Unix SMB/CIFS implementation.
+ Password and authentication handling
+ Copyright (C) Jeremy Allison 1996-2002
+ Copyright (C) Andrew Tridgell 2002
+ Copyright (C) Gerald (Jerry) Carter 2000
+ Copyright (C) Stefan (metze) Metzmacher 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 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"
+
+/* NOTE! the global_sam_sid is the SID of our local SAM. This is only
+ equal to the domain SID when we are a DC, otherwise its our
+ workstation SID */
+static DOM_SID *global_sam_sid=NULL;
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_PASSDB
+
+/****************************************************************************
+ Read a SID from a file. This is for compatibility with the old MACHINE.SID
+ style of SID storage
+****************************************************************************/
+static BOOL read_sid_from_file(const char *fname, DOM_SID *sid)
+{
+ char **lines;
+ int numlines;
+ BOOL ret;
+
+ lines = file_lines_load(fname, &numlines);
+
+ if (!lines || numlines < 1) {
+ if (lines) file_lines_free(lines);
+ return False;
+ }
+
+ ret = string_to_sid(sid, lines[0]);
+ file_lines_free(lines);
+ return ret;
+}
+
+/*
+ generate a random sid - used to build our own sid if we don't have one
+*/
+static void generate_random_sid(DOM_SID *sid)
+{
+ int i;
+ uchar raw_sid_data[12];
+
+ memset((char *)sid, '\0', sizeof(*sid));
+ sid->sid_rev_num = 1;
+ sid->id_auth[5] = 5;
+ sid->num_auths = 0;
+ sid->sub_auths[sid->num_auths++] = 21;
+
+ generate_random_buffer(raw_sid_data, 12, True);
+ for (i = 0; i < 3; i++)
+ sid->sub_auths[sid->num_auths++] = IVAL(raw_sid_data, i*4);
+}
+
+/****************************************************************************
+ Generate the global machine sid.
+****************************************************************************/
+
+static DOM_SID *pdb_generate_sam_sid(void)
+{
+ DOM_SID domain_sid;
+ char *fname = NULL;
+ BOOL is_dc = False;
+ DOM_SID *sam_sid;
+
+ if(!(sam_sid=(DOM_SID *)malloc(sizeof(DOM_SID))))
+ return NULL;
+
+ generate_wellknown_sids();
+
+ switch (lp_server_role()) {
+ case ROLE_DOMAIN_PDC:
+ case ROLE_DOMAIN_BDC:
+ is_dc = True;
+ break;
+ default:
+ is_dc = False;
+ break;
+ }
+
+ if (is_dc) {
+ if (secrets_fetch_domain_sid(lp_workgroup(), &domain_sid)) {
+ sid_copy(sam_sid, &domain_sid);
+ return sam_sid;
+ }
+ }
+
+ if (secrets_fetch_domain_sid(global_myname(), sam_sid)) {
+
+ /* We got our sid. If not a pdc/bdc, we're done. */
+ if (!is_dc)
+ return sam_sid;
+
+ if (!secrets_fetch_domain_sid(lp_workgroup(), &domain_sid)) {
+
+ /* No domain sid and we're a pdc/bdc. Store it */
+
+ if (!secrets_store_domain_sid(lp_workgroup(), sam_sid)) {
+ DEBUG(0,("pdb_generate_sam_sid: Can't store domain SID as a pdc/bdc.\n"));
+ SAFE_FREE(sam_sid);
+ return NULL;
+ }
+ return sam_sid;
+ }
+
+ if (!sid_equal(&domain_sid, sam_sid)) {
+
+ /* Domain name sid doesn't match global sam sid. Re-store domain sid as 'local' sid. */
+
+ DEBUG(0,("pdb_generate_sam_sid: Mismatched SIDs as a pdc/bdc.\n"));
+ if (!secrets_store_domain_sid(global_myname(), &domain_sid)) {
+ DEBUG(0,("pdb_generate_sam_sid: Can't re-store domain SID for local sid as PDC/BDC.\n"));
+ SAFE_FREE(sam_sid);
+ return NULL;
+ }
+ return sam_sid;
+ }
+
+ return sam_sid;
+
+ }
+
+ /* check for an old MACHINE.SID file for backwards compatibility */
+ asprintf(&fname, "%s/MACHINE.SID", lp_private_dir());
+
+ if (read_sid_from_file(fname, sam_sid)) {
+ /* remember it for future reference and unlink the old MACHINE.SID */
+ if (!secrets_store_domain_sid(global_myname(), sam_sid)) {
+ DEBUG(0,("pdb_generate_sam_sid: Failed to store SID from file.\n"));
+ SAFE_FREE(fname);
+ SAFE_FREE(sam_sid);
+ return NULL;
+ }
+ unlink(fname);
+ if (is_dc) {
+ if (!secrets_store_domain_sid(lp_workgroup(), sam_sid)) {
+ DEBUG(0,("pdb_generate_sam_sid: Failed to store domain SID from file.\n"));
+ SAFE_FREE(fname);
+ SAFE_FREE(sam_sid);
+ return NULL;
+ }
+ }
+
+ /* Stored the old sid from MACHINE.SID successfully.*/
+ SAFE_FREE(fname);
+ return sam_sid;
+ }
+
+ SAFE_FREE(fname);
+
+ /* we don't have the SID in secrets.tdb, we will need to
+ generate one and save it */
+ generate_random_sid(sam_sid);
+
+ if (!secrets_store_domain_sid(global_myname(), sam_sid)) {
+ DEBUG(0,("pdb_generate_sam_sid: Failed to store generated machine SID.\n"));
+ SAFE_FREE(sam_sid);
+ return NULL;
+ }
+ if (is_dc) {
+ if (!secrets_store_domain_sid(lp_workgroup(), sam_sid)) {
+ DEBUG(0,("pdb_generate_sam_sid: Failed to store generated domain SID.\n"));
+ SAFE_FREE(sam_sid);
+ return NULL;
+ }
+ }
+
+ return sam_sid;
+}
+
+/* return our global_sam_sid */
+DOM_SID *get_global_sam_sid(void)
+{
+ if (global_sam_sid != NULL)
+ return global_sam_sid;
+
+ /* memory for global_sam_sid is allocated in
+ pdb_generate_sam_sid() as needed */
+
+ if (!(global_sam_sid = pdb_generate_sam_sid())) {
+ smb_panic("Could not generate a machine SID\n");
+ }
+
+ return global_sam_sid;
+}
+
+/**
+ * Force get_global_sam_sid to requery the backends
+ */
+void reset_global_sam_sid(void)
+{
+ SAFE_FREE(global_sam_sid);
+}
diff --git a/source/passdb/passdb.c b/source/passdb/passdb.c
new file mode 100644
index 00000000000..73f613535d9
--- /dev/null
+++ b/source/passdb/passdb.c
@@ -0,0 +1,2388 @@
+/*
+ Unix SMB/CIFS implementation.
+ Password and authentication handling
+ Copyright (C) Jeremy Allison 1996-2001
+ Copyright (C) Luke Kenneth Casson Leighton 1996-1998
+ Copyright (C) Gerald (Jerry) Carter 2000-2001
+ Copyright (C) Andrew Bartlett 2001-2002
+ Copyright (C) Simo Sorce 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 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"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_PASSDB
+
+/******************************************************************
+ get the default domain/netbios name to be used when
+ testing authentication. For example, if you connect
+ to a Windows member server using a bogus domain name, the
+ Windows box will map the BOGUS\user to DOMAIN\user. A
+ standalone box will map to WKS\user.
+******************************************************************/
+
+const char *get_default_sam_name(void)
+{
+ /* standalone servers can only use the local netbios name */
+ if ( lp_server_role() == ROLE_STANDALONE )
+ return global_myname();
+
+ /* Windows domain members default to the DOMAIN
+ name when not specified */
+ return lp_workgroup();
+}
+
+/******************************************************************
+ get the default domain/netbios name to be used when dealing
+ with our passdb list of accounts
+******************************************************************/
+
+const char *get_global_sam_name(void)
+{
+ if ((lp_server_role() == ROLE_DOMAIN_PDC) || (lp_server_role() == ROLE_DOMAIN_BDC)) {
+ return lp_workgroup();
+ }
+ return global_myname();
+}
+
+/************************************************************
+ Fill the SAM_ACCOUNT with default values.
+ ***********************************************************/
+
+void pdb_fill_default_sam(SAM_ACCOUNT *user)
+{
+ ZERO_STRUCT(user->private); /* Don't touch the talloc context */
+
+ /* no initial methods */
+ user->methods = NULL;
+
+ /* Don't change these timestamp settings without a good reason.
+ They are important for NT member server compatibility. */
+
+ user->private.logon_time = (time_t)0;
+ user->private.pass_last_set_time = (time_t)0;
+ user->private.pass_can_change_time = (time_t)0;
+ user->private.logoff_time =
+ user->private.kickoff_time =
+ user->private.pass_must_change_time = get_time_t_max();
+ user->private.fields_present = 0x00ffffff;
+ user->private.logon_divs = 168; /* hours per week */
+ user->private.hours_len = 21; /* 21 times 8 bits = 168 */
+ memset(user->private.hours, 0xff, user->private.hours_len); /* available at all hours */
+ user->private.bad_password_count = 0;
+ user->private.logon_count = 0;
+ user->private.unknown_6 = 0x000004ec; /* don't know */
+
+ /* Some parts of samba strlen their pdb_get...() returns,
+ so this keeps the interface unchanged for now. */
+
+ user->private.username = "";
+ user->private.domain = "";
+ user->private.nt_username = "";
+ user->private.full_name = "";
+ user->private.home_dir = "";
+ user->private.logon_script = "";
+ user->private.profile_path = "";
+ user->private.acct_desc = "";
+ user->private.workstations = "";
+ user->private.unknown_str = "";
+ user->private.munged_dial = "";
+
+ user->private.plaintext_pw = NULL;
+
+ /*
+ Unless we know otherwise have a Account Control Bit
+ value of 'normal user'. This helps User Manager, which
+ asks for a filtered list of users.
+ */
+
+ user->private.acct_ctrl = ACB_NORMAL;
+}
+
+static void destroy_pdb_talloc(SAM_ACCOUNT **user)
+{
+ if (*user) {
+ data_blob_clear_free(&((*user)->private.lm_pw));
+ data_blob_clear_free(&((*user)->private.nt_pw));
+
+ if((*user)->private.plaintext_pw!=NULL)
+ memset((*user)->private.plaintext_pw,'\0',strlen((*user)->private.plaintext_pw));
+ talloc_destroy((*user)->mem_ctx);
+ *user = NULL;
+ }
+}
+
+
+/**********************************************************************
+ Allocates memory and initialises a struct sam_passwd on supplied mem_ctx.
+***********************************************************************/
+
+NTSTATUS pdb_init_sam_talloc(TALLOC_CTX *mem_ctx, SAM_ACCOUNT **user)
+{
+ if (*user != NULL) {
+ DEBUG(0,("pdb_init_sam_talloc: SAM_ACCOUNT was non NULL\n"));
+#if 0
+ smb_panic("non-NULL pointer passed to pdb_init_sam\n");
+#endif
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ if (!mem_ctx) {
+ DEBUG(0,("pdb_init_sam_talloc: mem_ctx was NULL!\n"));
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ *user=(SAM_ACCOUNT *)talloc(mem_ctx, sizeof(SAM_ACCOUNT));
+
+ if (*user==NULL) {
+ DEBUG(0,("pdb_init_sam_talloc: error while allocating memory\n"));
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ (*user)->mem_ctx = mem_ctx;
+
+ (*user)->free_fn = NULL;
+
+ pdb_fill_default_sam(*user);
+
+ return NT_STATUS_OK;
+}
+
+
+/*************************************************************
+ Allocates memory and initialises a struct sam_passwd.
+ ************************************************************/
+
+NTSTATUS pdb_init_sam(SAM_ACCOUNT **user)
+{
+ TALLOC_CTX *mem_ctx;
+ NTSTATUS nt_status;
+
+ mem_ctx = talloc_init("passdb internal SAM_ACCOUNT allocation");
+
+ if (!mem_ctx) {
+ DEBUG(0,("pdb_init_sam: error while doing talloc_init()\n"));
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ if (!NT_STATUS_IS_OK(nt_status = pdb_init_sam_talloc(mem_ctx, user))) {
+ talloc_destroy(mem_ctx);
+ return nt_status;
+ }
+
+ (*user)->free_fn = destroy_pdb_talloc;
+
+ return NT_STATUS_OK;
+}
+
+/**************************************************************************
+ * This function will take care of all the steps needed to correctly
+ * allocate and set the user SID, please do use this function to create new
+ * users, messing with SIDs is not good.
+ *
+ * account_data must be provided initialized, pwd may be null.
+ * SSS
+ ***************************************************************************/
+
+static NTSTATUS pdb_set_sam_sids(SAM_ACCOUNT *account_data, const struct passwd *pwd)
+{
+ const char *guest_account = lp_guestaccount();
+ GROUP_MAP map;
+ BOOL ret;
+
+ if (!account_data || !pwd) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ /* this is a hack this thing should not be set
+ this way --SSS */
+ if (!(guest_account && *guest_account)) {
+ DEBUG(1, ("NULL guest account!?!?\n"));
+ return NT_STATUS_UNSUCCESSFUL;
+ } else {
+ /* Ensure this *must* be set right */
+ if (strcmp(pwd->pw_name, guest_account) == 0) {
+ if (!pdb_set_user_sid_from_rid(account_data, DOMAIN_USER_RID_GUEST, PDB_DEFAULT)) {
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+ if (!pdb_set_group_sid_from_rid(account_data, DOMAIN_GROUP_RID_GUESTS, PDB_DEFAULT)) {
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+ return NT_STATUS_OK;
+ }
+ }
+
+ if (!pdb_set_user_sid_from_rid(account_data, fallback_pdb_uid_to_user_rid(pwd->pw_uid), PDB_SET)) {
+ DEBUG(0,("Can't set User SID from RID!\n"));
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ /* call the mapping code here */
+ become_root();
+ ret = pdb_getgrgid(&map, pwd->pw_gid);
+ unbecome_root();
+
+ if( ret ) {
+ if (!pdb_set_group_sid(account_data, &map.sid, PDB_SET)){
+ DEBUG(0,("Can't set Group SID!\n"));
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+ }
+ else {
+ if (!pdb_set_group_sid_from_rid(account_data, pdb_gid_to_group_rid(pwd->pw_gid), PDB_SET)) {
+ DEBUG(0,("Can't set Group SID\n"));
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+ }
+
+ return NT_STATUS_OK;
+}
+
+/*************************************************************
+ Initialises a struct sam_passwd with sane values.
+ ************************************************************/
+
+NTSTATUS pdb_fill_sam_pw(SAM_ACCOUNT *sam_account, const struct passwd *pwd)
+{
+ NTSTATUS ret;
+
+ if (!pwd) {
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ pdb_fill_default_sam(sam_account);
+
+ pdb_set_username(sam_account, pwd->pw_name, PDB_SET);
+ pdb_set_fullname(sam_account, pwd->pw_gecos, PDB_SET);
+
+ pdb_set_unix_homedir(sam_account, pwd->pw_dir, PDB_SET);
+
+ pdb_set_domain (sam_account, get_global_sam_name(), PDB_DEFAULT);
+
+ /* When we get a proper uid -> SID and SID -> uid allocation
+ mechinism, we should call it here.
+
+ We can't just set this to 0 or allow it only to be filled
+ in when added to the backend, because the user's SID
+ may already be in security descriptors etc.
+
+ -- abartlet 11-May-02
+ */
+
+ ret = pdb_set_sam_sids(sam_account, pwd);
+ if (!NT_STATUS_IS_OK(ret)) return ret;
+
+ /* check if this is a user account or a machine account */
+ if (pwd->pw_name[strlen(pwd->pw_name)-1] != '$')
+ {
+ pdb_set_profile_path(sam_account,
+ talloc_sub_specified((sam_account)->mem_ctx,
+ lp_logon_path(),
+ pwd->pw_name, global_myname(),
+ pwd->pw_uid, pwd->pw_gid),
+ PDB_DEFAULT);
+
+ pdb_set_homedir(sam_account,
+ talloc_sub_specified((sam_account)->mem_ctx,
+ lp_logon_home(),
+ pwd->pw_name, global_myname(),
+ pwd->pw_uid, pwd->pw_gid),
+ PDB_DEFAULT);
+
+ pdb_set_dir_drive(sam_account,
+ talloc_sub_specified((sam_account)->mem_ctx,
+ lp_logon_drive(),
+ pwd->pw_name, global_myname(),
+ pwd->pw_uid, pwd->pw_gid),
+ PDB_DEFAULT);
+
+ pdb_set_logon_script(sam_account,
+ talloc_sub_specified((sam_account)->mem_ctx,
+ lp_logon_script(),
+ pwd->pw_name, global_myname(),
+ pwd->pw_uid, pwd->pw_gid),
+ PDB_DEFAULT);
+ if (!pdb_set_acct_ctrl(sam_account, ACB_NORMAL, PDB_DEFAULT)) {
+ DEBUG(1, ("Failed to set 'normal account' flags for user %s.\n", pwd->pw_name));
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+ } else {
+ if (!pdb_set_acct_ctrl(sam_account, ACB_WSTRUST, PDB_DEFAULT)) {
+ DEBUG(1, ("Failed to set 'trusted workstation account' flags for user %s.\n", pwd->pw_name));
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+ }
+ return NT_STATUS_OK;
+}
+
+
+/*************************************************************
+ Initialises a struct sam_passwd with sane values.
+ ************************************************************/
+
+NTSTATUS pdb_init_sam_pw(SAM_ACCOUNT **new_sam_acct, const struct passwd *pwd)
+{
+ NTSTATUS nt_status;
+
+ if (!pwd) {
+ new_sam_acct = NULL;
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ if (!NT_STATUS_IS_OK(nt_status = pdb_init_sam(new_sam_acct))) {
+ new_sam_acct = NULL;
+ return nt_status;
+ }
+
+ if (!NT_STATUS_IS_OK(nt_status = pdb_fill_sam_pw(*new_sam_acct, pwd))) {
+ pdb_free_sam(new_sam_acct);
+ new_sam_acct = NULL;
+ return nt_status;
+ }
+
+ return NT_STATUS_OK;
+}
+
+
+/*************************************************************
+ Initialises a SAM_ACCOUNT ready to add a new account, based
+ on the UNIX user. Pass in a RID if you have one
+ ************************************************************/
+
+NTSTATUS pdb_init_sam_new(SAM_ACCOUNT **new_sam_acct, const char *username,
+ uint32 rid)
+{
+ NTSTATUS nt_status = NT_STATUS_NO_MEMORY;
+ struct passwd *pwd;
+ BOOL ret;
+
+ pwd = Get_Pwnam(username);
+
+ if (!pwd)
+ return NT_STATUS_NO_SUCH_USER;
+
+ if (!NT_STATUS_IS_OK(nt_status = pdb_init_sam_pw(new_sam_acct, pwd))) {
+ *new_sam_acct = NULL;
+ return nt_status;
+ }
+
+ /* see if we need to generate a new rid using the 2.2 algorithm */
+ if ( rid == 0 && lp_enable_rid_algorithm() ) {
+ DEBUG(10,("pdb_init_sam_new: no RID specified. Generating one via old algorithm\n"));
+ rid = fallback_pdb_uid_to_user_rid(pwd->pw_uid);
+ }
+
+ /* set the new SID */
+
+ ret = pdb_set_user_sid_from_rid( *new_sam_acct, rid, PDB_SET );
+
+ return (ret ? NT_STATUS_OK : NT_STATUS_NO_SUCH_USER);
+}
+
+
+/**
+ * Free the contets of the SAM_ACCOUNT, but not the structure.
+ *
+ * Also wipes the LM and NT hashes and plaintext password from
+ * memory.
+ *
+ * @param user SAM_ACCOUNT to free members of.
+ **/
+
+static void pdb_free_sam_contents(SAM_ACCOUNT *user)
+{
+
+ /* Kill off sensitive data. Free()ed by the
+ talloc mechinism */
+
+ data_blob_clear_free(&(user->private.lm_pw));
+ data_blob_clear_free(&(user->private.nt_pw));
+ if (user->private.plaintext_pw!=NULL)
+ memset(user->private.plaintext_pw,'\0',strlen(user->private.plaintext_pw));
+
+ if (user->private.backend_private_data && user->private.backend_private_data_free_fn) {
+ user->private.backend_private_data_free_fn(&user->private.backend_private_data);
+ }
+}
+
+
+/************************************************************
+ Reset the SAM_ACCOUNT and free the NT/LM hashes.
+ ***********************************************************/
+
+NTSTATUS pdb_reset_sam(SAM_ACCOUNT *user)
+{
+ if (user == NULL) {
+ DEBUG(0,("pdb_reset_sam: SAM_ACCOUNT was NULL\n"));
+#if 0
+ smb_panic("NULL pointer passed to pdb_free_sam\n");
+#endif
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ pdb_free_sam_contents(user);
+
+ pdb_fill_default_sam(user);
+
+ return NT_STATUS_OK;
+}
+
+
+/************************************************************
+ Free the SAM_ACCOUNT and the member pointers.
+ ***********************************************************/
+
+NTSTATUS pdb_free_sam(SAM_ACCOUNT **user)
+{
+ if (*user == NULL) {
+ DEBUG(0,("pdb_free_sam: SAM_ACCOUNT was NULL\n"));
+#if 0
+ smb_panic("NULL pointer passed to pdb_free_sam\n");
+#endif
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ pdb_free_sam_contents(*user);
+
+ if ((*user)->free_fn) {
+ (*user)->free_fn(user);
+ }
+
+ return NT_STATUS_OK;
+}
+
+/**********************************************************
+ Encode the account control bits into a string.
+ length = length of string to encode into (including terminating
+ null). length *MUST BE MORE THAN 2* !
+ **********************************************************/
+
+char *pdb_encode_acct_ctrl(uint16 acct_ctrl, size_t length)
+{
+ static fstring acct_str;
+
+ size_t i = 0;
+
+ SMB_ASSERT(length <= sizeof(acct_str));
+
+ acct_str[i++] = '[';
+
+ if (acct_ctrl & ACB_PWNOTREQ ) acct_str[i++] = 'N';
+ if (acct_ctrl & ACB_DISABLED ) acct_str[i++] = 'D';
+ if (acct_ctrl & ACB_HOMDIRREQ) acct_str[i++] = 'H';
+ if (acct_ctrl & ACB_TEMPDUP ) acct_str[i++] = 'T';
+ if (acct_ctrl & ACB_NORMAL ) acct_str[i++] = 'U';
+ if (acct_ctrl & ACB_MNS ) acct_str[i++] = 'M';
+ if (acct_ctrl & ACB_WSTRUST ) acct_str[i++] = 'W';
+ if (acct_ctrl & ACB_SVRTRUST ) acct_str[i++] = 'S';
+ if (acct_ctrl & ACB_AUTOLOCK ) acct_str[i++] = 'L';
+ if (acct_ctrl & ACB_PWNOEXP ) acct_str[i++] = 'X';
+ if (acct_ctrl & ACB_DOMTRUST ) acct_str[i++] = 'I';
+
+ for ( ; i < length - 2 ; i++ )
+ acct_str[i] = ' ';
+
+ i = length - 2;
+ acct_str[i++] = ']';
+ acct_str[i++] = '\0';
+
+ return acct_str;
+}
+
+/**********************************************************
+ Decode the account control bits from a string.
+ **********************************************************/
+
+uint16 pdb_decode_acct_ctrl(const char *p)
+{
+ uint16 acct_ctrl = 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': { acct_ctrl |= ACB_PWNOTREQ ; break; /* 'N'o password. */ }
+ case 'D': { acct_ctrl |= ACB_DISABLED ; break; /* 'D'isabled. */ }
+ case 'H': { acct_ctrl |= ACB_HOMDIRREQ; break; /* 'H'omedir required. */ }
+ case 'T': { acct_ctrl |= ACB_TEMPDUP ; break; /* 'T'emp account. */ }
+ case 'U': { acct_ctrl |= ACB_NORMAL ; break; /* 'U'ser account (normal). */ }
+ case 'M': { acct_ctrl |= ACB_MNS ; break; /* 'M'NS logon user account. What is this ? */ }
+ case 'W': { acct_ctrl |= ACB_WSTRUST ; break; /* 'W'orkstation account. */ }
+ case 'S': { acct_ctrl |= ACB_SVRTRUST ; break; /* 'S'erver account. */ }
+ case 'L': { acct_ctrl |= ACB_AUTOLOCK ; break; /* 'L'ocked account. */ }
+ case 'X': { acct_ctrl |= ACB_PWNOEXP ; break; /* No 'X'piry on password */ }
+ case 'I': { acct_ctrl |= ACB_DOMTRUST ; break; /* 'I'nterdomain trust account. */ }
+ case ' ': { break; }
+ case ':':
+ case '\n':
+ case '\0':
+ case ']':
+ default: { finished = True; }
+ }
+ }
+
+ return acct_ctrl;
+}
+
+/*************************************************************
+ Routine to set 32 hex password characters from a 16 byte array.
+**************************************************************/
+
+void pdb_sethexpwd(char *p, const unsigned char *pwd, uint16 acct_ctrl)
+{
+ if (pwd != NULL) {
+ int i;
+ for (i = 0; i < 16; i++)
+ slprintf(&p[i*2], 3, "%02X", pwd[i]);
+ } else {
+ if (acct_ctrl & ACB_PWNOTREQ)
+ safe_strcpy(p, "NO PASSWORDXXXXXXXXXXXXXXXXXXXXX", 33);
+ else
+ safe_strcpy(p, "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX", 33);
+ }
+}
+
+/*************************************************************
+ Routine to get the 32 hex characters and turn them
+ into a 16 byte array.
+**************************************************************/
+
+BOOL pdb_gethexpwd(const char *p, unsigned char *pwd)
+{
+ int i;
+ unsigned char lonybble, hinybble;
+ const char *hexchars = "0123456789ABCDEF";
+ char *p1, *p2;
+
+ if (!p)
+ return (False);
+
+ for (i = 0; i < 32; i += 2) {
+ hinybble = toupper(p[i]);
+ lonybble = toupper(p[i + 1]);
+
+ p1 = strchr(hexchars, hinybble);
+ p2 = strchr(hexchars, lonybble);
+
+ if (!p1 || !p2)
+ return (False);
+
+ hinybble = PTR_DIFF(p1, hexchars);
+ lonybble = PTR_DIFF(p2, hexchars);
+
+ pwd[i / 2] = (hinybble << 4) | lonybble;
+ }
+ return (True);
+}
+
+int algorithmic_rid_base(void)
+{
+ static int rid_offset = 0;
+
+ if (rid_offset != 0)
+ return rid_offset;
+
+ rid_offset = lp_algorithmic_rid_base();
+
+ if (rid_offset < BASE_RID) {
+ /* Try to prevent admin foot-shooting, we can't put algorithmic
+ rids below 1000, that's the 'well known RIDs' on NT */
+ DEBUG(0, ("'algorithmic rid base' must be equal to or above %ld\n", BASE_RID));
+ rid_offset = BASE_RID;
+ }
+ if (rid_offset & 1) {
+ DEBUG(0, ("algorithmic rid base must be even\n"));
+ rid_offset += 1;
+ }
+ return rid_offset;
+}
+
+/*******************************************************************
+ Converts NT user RID to a UNIX uid.
+ ********************************************************************/
+
+uid_t fallback_pdb_user_rid_to_uid(uint32 user_rid)
+{
+ int rid_offset = algorithmic_rid_base();
+ return (uid_t)(((user_rid & (~USER_RID_TYPE)) - rid_offset)/RID_MULTIPLIER);
+}
+
+/*******************************************************************
+ converts UNIX uid to an NT User RID.
+ ********************************************************************/
+
+uint32 fallback_pdb_uid_to_user_rid(uid_t uid)
+{
+ int rid_offset = algorithmic_rid_base();
+ return (((((uint32)uid)*RID_MULTIPLIER) + rid_offset) | USER_RID_TYPE);
+}
+
+/*******************************************************************
+ Converts NT group RID to a UNIX gid.
+ ********************************************************************/
+
+gid_t pdb_group_rid_to_gid(uint32 group_rid)
+{
+ int rid_offset = algorithmic_rid_base();
+ return (gid_t)(((group_rid & (~GROUP_RID_TYPE))- rid_offset)/RID_MULTIPLIER);
+}
+
+/*******************************************************************
+ converts NT Group RID to a UNIX uid.
+
+ warning: you must not call that function only
+ you must do a call to the group mapping first.
+ there is not anymore a direct link between the gid and the rid.
+ ********************************************************************/
+
+uint32 pdb_gid_to_group_rid(gid_t gid)
+{
+ int rid_offset = algorithmic_rid_base();
+ return (((((uint32)gid)*RID_MULTIPLIER) + rid_offset) | GROUP_RID_TYPE);
+}
+
+/*******************************************************************
+ Decides if a RID is a well known RID.
+ ********************************************************************/
+
+static BOOL pdb_rid_is_well_known(uint32 rid)
+{
+ /* Not using rid_offset here, because this is the actual
+ NT fixed value (1000) */
+
+ return (rid < BASE_RID);
+}
+
+/*******************************************************************
+ Decides if a RID is a user or group RID.
+ ********************************************************************/
+
+BOOL fallback_pdb_rid_is_user(uint32 rid)
+{
+ /* lkcl i understand that NT attaches an enumeration to a RID
+ * such that it can be identified as either a user, group etc
+ * type. there are 5 such categories, and they are documented.
+ */
+ /* However, they are not in the RID, just somthing you can query
+ seperatly. Sorry luke :-) */
+
+ if(pdb_rid_is_well_known(rid)) {
+ /*
+ * The only well known user RIDs are DOMAIN_USER_RID_ADMIN
+ * and DOMAIN_USER_RID_GUEST.
+ */
+ if(rid == DOMAIN_USER_RID_ADMIN || rid == DOMAIN_USER_RID_GUEST)
+ return True;
+ } else if((rid & RID_TYPE_MASK) == USER_RID_TYPE) {
+ return True;
+ }
+ return False;
+}
+
+/*******************************************************************
+ Convert a rid into a name. Used in the lookup SID rpc.
+ ********************************************************************/
+
+BOOL local_lookup_sid(const DOM_SID *sid, char *name, enum SID_NAME_USE *psid_name_use)
+{
+ uint32 rid;
+ SAM_ACCOUNT *sam_account = NULL;
+ GROUP_MAP map;
+ BOOL ret;
+
+ if (!sid_peek_check_rid(get_global_sam_sid(), sid, &rid)){
+ DEBUG(0,("local_lookup_sid: sid_peek_check_rid return False! SID: %s\n",
+ sid_string_static(&map.sid)));
+ return False;
+ }
+ *psid_name_use = SID_NAME_UNKNOWN;
+
+ DEBUG(5,("local_lookup_sid: looking up RID %u.\n", (unsigned int)rid));
+
+ if (rid == DOMAIN_USER_RID_ADMIN) {
+ const char **admin_list = lp_admin_users(-1);
+ *psid_name_use = SID_NAME_USER;
+ if (admin_list) {
+ const char *p = *admin_list;
+ if(!next_token(&p, name, NULL, sizeof(fstring)))
+ fstrcpy(name, "Administrator");
+ } else {
+ fstrcpy(name, "Administrator");
+ }
+ return True;
+ }
+
+ if (!NT_STATUS_IS_OK(pdb_init_sam(&sam_account))) {
+ return False;
+ }
+
+ /* see if the passdb can help us with the name of the user */
+
+ /* BEING ROOT BLLOCK */
+ become_root();
+ if (pdb_getsampwsid(sam_account, sid)) {
+ unbecome_root(); /* -----> EXIT BECOME_ROOT() */
+ fstrcpy(name, pdb_get_username(sam_account));
+ *psid_name_use = SID_NAME_USER;
+
+ pdb_free_sam(&sam_account);
+
+ return True;
+ }
+ pdb_free_sam(&sam_account);
+
+ ret = pdb_getgrsid(&map, *sid);
+ unbecome_root();
+ /* END BECOME_ROOT BLOCK */
+
+ if ( ret ) {
+ if (map.gid!=(gid_t)-1) {
+ DEBUG(5,("local_lookup_sid: mapped group %s to gid %u\n", map.nt_name, (unsigned int)map.gid));
+ } else {
+ DEBUG(5,("local_lookup_sid: mapped group %s to no unix gid. Returning name.\n", map.nt_name));
+ }
+
+ fstrcpy(name, map.nt_name);
+ *psid_name_use = map.sid_name_use;
+ return True;
+ }
+
+ if (fallback_pdb_rid_is_user(rid)) {
+ uid_t uid;
+ struct passwd *pw = NULL;
+
+ DEBUG(5, ("assuming RID %u is a user\n", (unsigned)rid));
+
+ uid = fallback_pdb_user_rid_to_uid(rid);
+ pw = sys_getpwuid( uid );
+
+ DEBUG(5,("local_lookup_sid: looking up uid %u %s\n", (unsigned int)uid,
+ pw ? "succeeded" : "failed" ));
+
+ if ( !pw )
+ fstr_sprintf(name, "unix_user.%u", (unsigned int)uid);
+ else
+ fstrcpy( name, pw->pw_name );
+
+ DEBUG(5,("local_lookup_sid: found user %s for rid %u\n", name,
+ (unsigned int)rid ));
+
+ *psid_name_use = SID_NAME_USER;
+
+ return ( pw != NULL );
+ } else {
+ gid_t gid;
+ struct group *gr;
+
+ DEBUG(5, ("assuming RID %u is a group\n", (unsigned)rid));
+
+ gid = pdb_group_rid_to_gid(rid);
+ gr = getgrgid(gid);
+
+ DEBUG(5,("local_lookup_sid: looking up gid %u %s\n", (unsigned int)gid,
+ gr ? "succeeded" : "failed" ));
+
+ if( !gr )
+ fstr_sprintf(name, "unix_group.%u", (unsigned int)gid);
+ else
+ fstrcpy( name, gr->gr_name);
+
+ DEBUG(5,("local_lookup_sid: found group %s for rid %u\n", name,
+ (unsigned int)rid ));
+
+ /* assume fallback groups aer domain global groups */
+
+ *psid_name_use = SID_NAME_DOM_GRP;
+
+ return ( gr != NULL );
+ }
+}
+
+/*******************************************************************
+ Convert a name into a SID. Used in the lookup name rpc.
+ ********************************************************************/
+
+BOOL local_lookup_name(const char *c_user, DOM_SID *psid, enum SID_NAME_USE *psid_name_use)
+{
+ extern DOM_SID global_sid_World_Domain;
+ DOM_SID local_sid;
+ fstring user;
+ SAM_ACCOUNT *sam_account = NULL;
+ struct group *grp;
+ GROUP_MAP map;
+
+ *psid_name_use = SID_NAME_UNKNOWN;
+
+ /*
+ * user may be quoted a const string, and map_username and
+ * friends can modify it. Make a modifiable copy. JRA.
+ */
+
+ fstrcpy(user, c_user);
+
+ sid_copy(&local_sid, get_global_sam_sid());
+
+ /*
+ * Special case for MACHINE\Everyone. Map to the world_sid.
+ */
+
+ if(strequal(user, "Everyone")) {
+ sid_copy( psid, &global_sid_World_Domain);
+ sid_append_rid(psid, 0);
+ *psid_name_use = SID_NAME_ALIAS;
+ return True;
+ }
+
+ (void)map_username(user);
+
+ if (!NT_STATUS_IS_OK(pdb_init_sam(&sam_account))) {
+ return False;
+ }
+
+ /* BEGIN ROOT BLOCK */
+
+ become_root();
+ if (pdb_getsampwnam(sam_account, user)) {
+ unbecome_root();
+ sid_copy(psid, pdb_get_user_sid(sam_account));
+ *psid_name_use = SID_NAME_USER;
+
+ pdb_free_sam(&sam_account);
+ return True;
+ }
+
+ pdb_free_sam(&sam_account);
+
+ /*
+ * Maybe it was a group ?
+ */
+
+ /* check if it's a mapped group */
+ if (pdb_getgrnam(&map, user)) {
+ /* yes it's a mapped group */
+ sid_copy(&local_sid, &map.sid);
+ *psid_name_use = map.sid_name_use;
+ } else {
+ /* it's not a mapped group */
+ grp = getgrnam(user);
+ if(!grp) {
+ unbecome_root(); /* ---> exit form block */
+ return False;
+ }
+
+ /*
+ *check if it's mapped, if it is reply it doesn't exist
+ *
+ * that's to prevent this case:
+ *
+ * unix group ug is mapped to nt group ng
+ * someone does a lookup on ug
+ * we must not reply as it doesn't "exist" anymore
+ * for NT. For NT only ng exists.
+ * JFM, 30/11/2001
+ */
+
+ if (pdb_getgrgid(&map, grp->gr_gid)){
+ unbecome_root(); /* ---> exit form block */
+ return False;
+ }
+
+ sid_append_rid( &local_sid, pdb_gid_to_group_rid(grp->gr_gid));
+ *psid_name_use = SID_NAME_ALIAS;
+ }
+ unbecome_root();
+ /* END ROOT BLOCK */
+
+ sid_copy( psid, &local_sid);
+
+ return True;
+}
+
+/*************************************************************
+ Change a password entry in the local smbpasswd file.
+ *************************************************************/
+
+BOOL local_password_change(const char *user_name, int local_flags,
+ const char *new_passwd,
+ char *err_str, size_t err_str_len,
+ char *msg_str, size_t msg_str_len)
+{
+ SAM_ACCOUNT *sam_pass=NULL;
+ uint16 other_acb;
+
+ *err_str = '\0';
+ *msg_str = '\0';
+
+ /* Get the smb passwd entry for this user */
+ pdb_init_sam(&sam_pass);
+
+ become_root();
+ if(!pdb_getsampwnam(sam_pass, user_name)) {
+ unbecome_root();
+ pdb_free_sam(&sam_pass);
+
+ if ((local_flags & LOCAL_ADD_USER) || (local_flags & LOCAL_DELETE_USER)) {
+ /* Might not exist in /etc/passwd. Use rid algorithm here */
+ if (!NT_STATUS_IS_OK(pdb_init_sam_new(&sam_pass, user_name, 0))) {
+ slprintf(err_str, err_str_len-1, "Failed to initialise SAM_ACCOUNT for user %s.\n", user_name);
+ return False;
+ }
+ } else {
+ slprintf(err_str, err_str_len-1,"Failed to find entry for user %s.\n", user_name);
+ return False;
+ }
+ } else {
+ unbecome_root();
+ /* the entry already existed */
+ local_flags &= ~LOCAL_ADD_USER;
+ }
+
+ /* the 'other' acb bits not being changed here */
+ other_acb = (pdb_get_acct_ctrl(sam_pass) & (!(ACB_WSTRUST|ACB_DOMTRUST|ACB_SVRTRUST|ACB_NORMAL)));
+ if (local_flags & LOCAL_TRUST_ACCOUNT) {
+ if (!pdb_set_acct_ctrl(sam_pass, ACB_WSTRUST | other_acb, PDB_CHANGED) ) {
+ slprintf(err_str, err_str_len - 1, "Failed to set 'trusted workstation account' flags for user %s.\n", user_name);
+ pdb_free_sam(&sam_pass);
+ return False;
+ }
+ } else if (local_flags & LOCAL_INTERDOM_ACCOUNT) {
+ if (!pdb_set_acct_ctrl(sam_pass, ACB_DOMTRUST | other_acb, PDB_CHANGED)) {
+ slprintf(err_str, err_str_len - 1, "Failed to set 'domain trust account' flags for user %s.\n", user_name);
+ pdb_free_sam(&sam_pass);
+ return False;
+ }
+ } else {
+ if (!pdb_set_acct_ctrl(sam_pass, ACB_NORMAL | other_acb, PDB_CHANGED)) {
+ slprintf(err_str, err_str_len - 1, "Failed to set 'normal account' flags for user %s.\n", user_name);
+ pdb_free_sam(&sam_pass);
+ return False;
+ }
+ }
+
+ /*
+ * We are root - just write the new password
+ * and the valid last change time.
+ */
+
+ if (local_flags & LOCAL_DISABLE_USER) {
+ if (!pdb_set_acct_ctrl (sam_pass, pdb_get_acct_ctrl(sam_pass)|ACB_DISABLED, PDB_CHANGED)) {
+ slprintf(err_str, err_str_len-1, "Failed to set 'disabled' flag for user %s.\n", user_name);
+ pdb_free_sam(&sam_pass);
+ return False;
+ }
+ } else if (local_flags & LOCAL_ENABLE_USER) {
+ if (!pdb_set_acct_ctrl (sam_pass, pdb_get_acct_ctrl(sam_pass)&(~ACB_DISABLED), PDB_CHANGED)) {
+ slprintf(err_str, err_str_len-1, "Failed to unset 'disabled' flag for user %s.\n", user_name);
+ pdb_free_sam(&sam_pass);
+ return False;
+ }
+ }
+
+ if (local_flags & LOCAL_SET_NO_PASSWORD) {
+ if (!pdb_set_acct_ctrl (sam_pass, pdb_get_acct_ctrl(sam_pass)|ACB_PWNOTREQ, PDB_CHANGED)) {
+ slprintf(err_str, err_str_len-1, "Failed to set 'no password required' flag for user %s.\n", user_name);
+ pdb_free_sam(&sam_pass);
+ return False;
+ }
+ } else if (local_flags & LOCAL_SET_PASSWORD) {
+ /*
+ * If we're dealing with setting a completely empty user account
+ * ie. One with a password of 'XXXX', but not set disabled (like
+ * an account created from scratch) then if the old password was
+ * 'XX's then getsmbpwent will have set the ACB_DISABLED flag.
+ * We remove that as we're giving this user their first password
+ * and the decision hasn't really been made to disable them (ie.
+ * don't create them disabled). JRA.
+ */
+ if ((pdb_get_lanman_passwd(sam_pass)==NULL) && (pdb_get_acct_ctrl(sam_pass)&ACB_DISABLED)) {
+ if (!pdb_set_acct_ctrl (sam_pass, pdb_get_acct_ctrl(sam_pass)&(~ACB_DISABLED), PDB_CHANGED)) {
+ slprintf(err_str, err_str_len-1, "Failed to unset 'disabled' flag for user %s.\n", user_name);
+ pdb_free_sam(&sam_pass);
+ return False;
+ }
+ }
+ if (!pdb_set_acct_ctrl (sam_pass, pdb_get_acct_ctrl(sam_pass)&(~ACB_PWNOTREQ), PDB_CHANGED)) {
+ slprintf(err_str, err_str_len-1, "Failed to unset 'no password required' flag for user %s.\n", user_name);
+ pdb_free_sam(&sam_pass);
+ return False;
+ }
+
+ if (!pdb_set_plaintext_passwd (sam_pass, new_passwd)) {
+ slprintf(err_str, err_str_len-1, "Failed to set password for user %s.\n", user_name);
+ pdb_free_sam(&sam_pass);
+ return False;
+ }
+ }
+
+ if (local_flags & LOCAL_ADD_USER) {
+ if (pdb_add_sam_account(sam_pass)) {
+ slprintf(msg_str, msg_str_len-1, "Added user %s.\n", user_name);
+ pdb_free_sam(&sam_pass);
+ return True;
+ } else {
+ slprintf(err_str, err_str_len-1, "Failed to add entry for user %s.\n", user_name);
+ pdb_free_sam(&sam_pass);
+ return False;
+ }
+ } else if (local_flags & LOCAL_DELETE_USER) {
+ if (!pdb_delete_sam_account(sam_pass)) {
+ slprintf(err_str,err_str_len-1, "Failed to delete entry for user %s.\n", user_name);
+ pdb_free_sam(&sam_pass);
+ return False;
+ }
+ slprintf(msg_str, msg_str_len-1, "Deleted user %s.\n", user_name);
+ } else {
+ if(!pdb_update_sam_account(sam_pass)) {
+ slprintf(err_str, err_str_len-1, "Failed to modify entry for user %s.\n", user_name);
+ pdb_free_sam(&sam_pass);
+ return False;
+ }
+ if(local_flags & LOCAL_DISABLE_USER)
+ slprintf(msg_str, msg_str_len-1, "Disabled user %s.\n", user_name);
+ else if (local_flags & LOCAL_ENABLE_USER)
+ slprintf(msg_str, msg_str_len-1, "Enabled user %s.\n", user_name);
+ else if (local_flags & LOCAL_SET_NO_PASSWORD)
+ slprintf(msg_str, msg_str_len-1, "User %s password set to none.\n", user_name);
+ }
+
+ pdb_free_sam(&sam_pass);
+ return True;
+}
+
+/****************************************************************************
+ Convert a uid to SID - algorithmic.
+****************************************************************************/
+
+DOM_SID *algorithmic_uid_to_sid(DOM_SID *psid, uid_t uid)
+{
+ if ( !lp_enable_rid_algorithm() )
+ return NULL;
+
+ DEBUG(8,("algorithmic_uid_to_sid: falling back to RID algorithm\n"));
+ sid_copy( psid, get_global_sam_sid() );
+ sid_append_rid( psid, fallback_pdb_uid_to_user_rid(uid) );
+ DEBUG(10,("algorithmic_uid_to_sid: uid (%d) -> SID %s.\n",
+ (unsigned int)uid, sid_string_static(psid) ));
+
+ return psid;
+}
+
+/****************************************************************************
+ Convert a uid to SID - locally.
+****************************************************************************/
+
+DOM_SID *local_uid_to_sid(DOM_SID *psid, uid_t uid)
+{
+ SAM_ACCOUNT *sampw = NULL;
+ struct passwd *unix_pw;
+ BOOL ret;
+
+ unix_pw = sys_getpwuid( uid );
+
+ if ( !unix_pw ) {
+ DEBUG(4,("local_uid_to_sid: host has no idea of uid %lu\n", (unsigned long)uid));
+ return algorithmic_uid_to_sid( psid, uid);
+ }
+
+ if ( !NT_STATUS_IS_OK(pdb_init_sam(&sampw)) ) {
+ DEBUG(0,("local_uid_to_sid: failed to allocate SAM_ACCOUNT object\n"));
+ return NULL;
+ }
+
+ become_root();
+ ret = pdb_getsampwnam( sampw, unix_pw->pw_name );
+ unbecome_root();
+
+ if ( ret )
+ sid_copy( psid, pdb_get_user_sid(sampw) );
+ else {
+ DEBUG(4,("local_uid_to_sid: User %s [uid == %lu] has no samba account\n",
+ unix_pw->pw_name, (unsigned long)uid));
+
+ return algorithmic_uid_to_sid( psid, uid);
+ }
+
+ DEBUG(10,("local_uid_to_sid: uid (%d) -> SID %s (%s).\n",
+ (unsigned int)uid, sid_string_static(psid), unix_pw->pw_name));
+
+ return psid;
+}
+
+/****************************************************************************
+ Convert a SID to uid - locally.
+****************************************************************************/
+
+BOOL local_sid_to_uid(uid_t *puid, const DOM_SID *psid, enum SID_NAME_USE *name_type)
+{
+ SAM_ACCOUNT *sampw = NULL;
+ struct passwd *unix_pw;
+ const char *user_name;
+
+ *name_type = SID_NAME_UNKNOWN;
+
+ /*
+ * We can only convert to a uid if this is our local
+ * Domain SID (ie. we are the controling authority).
+ */
+ if (!sid_check_is_in_our_domain(psid) ) {
+ DEBUG(5,("local_sid_to_uid: this SID (%s) is not from our domain\n", sid_string_static(psid)));
+ return False;
+ }
+
+ /* lookup the user account */
+
+ if ( !NT_STATUS_IS_OK(pdb_init_sam(&sampw)) ) {
+ DEBUG(0,("local_sid_to_uid: Failed to allocate memory for SAM_ACCOUNT object\n"));
+ return False;
+ }
+
+ become_root();
+ if ( !pdb_getsampwsid(sampw, psid) ) {
+ unbecome_root();
+ DEBUG(8,("local_sid_to_uid: Could not find SID %s in passdb\n",
+ sid_string_static(psid)));
+ return False;
+ }
+ unbecome_root();
+
+ user_name = pdb_get_username(sampw);
+
+ unix_pw = sys_getpwnam( user_name );
+
+ if ( !unix_pw ) {
+ DEBUG(0,("local_sid_to_uid: %s found in passdb but getpwnam() return NULL!\n",
+ user_name));
+ pdb_free_sam( &sampw );
+ return False;
+ }
+
+ *puid = unix_pw->pw_uid;
+
+ DEBUG(10,("local_sid_to_uid: SID %s -> uid (%u) (%s).\n", sid_string_static(psid),
+ (unsigned int)*puid, user_name ));
+
+ *name_type = SID_NAME_USER;
+
+ return True;
+}
+
+/****************************************************************************
+ Convert a gid to SID - locally.
+****************************************************************************/
+
+DOM_SID *local_gid_to_sid(DOM_SID *psid, gid_t gid)
+{
+ GROUP_MAP group;
+ BOOL ret;
+
+ /* we don't need to disable winbindd since the gid is stored in
+ the GROUP_MAP object */
+
+ /* done as root since ldap backend requires root to open a connection */
+
+ become_root();
+ ret = pdb_getgrgid( &group, gid );
+ unbecome_root();
+
+ if ( !ret ) {
+
+ /* fallback to rid mapping if enabled */
+
+ if ( lp_enable_rid_algorithm() ) {
+ sid_copy(psid, get_global_sam_sid());
+ sid_append_rid(psid, pdb_gid_to_group_rid(gid));
+
+ DEBUG(10,("local_gid_to_sid: Fall back to algorithmic mapping: %u -> %s\n",
+ (unsigned int)gid, sid_string_static(psid)));
+
+ return psid;
+ }
+ else
+ return NULL;
+ }
+
+ sid_copy( psid, &group.sid );
+
+ DEBUG(10,("local_gid_to_sid: gid (%d) -> SID %s.\n",
+ (unsigned int)gid, sid_string_static(psid)));
+
+ return psid;
+}
+
+/****************************************************************************
+ Convert a SID to gid - locally.
+****************************************************************************/
+
+BOOL local_sid_to_gid(gid_t *pgid, const DOM_SID *psid, enum SID_NAME_USE *name_type)
+{
+ uint32 rid;
+ GROUP_MAP group;
+ BOOL ret;
+
+ *name_type = SID_NAME_UNKNOWN;
+
+ /* This call can enumerate group mappings for foreign sids as well.
+ So don't check for a match against our domain SID */
+
+ /* we don't need to disable winbindd since the gid is stored in
+ the GROUP_MAP object */
+
+ become_root();
+ ret = pdb_getgrsid(&group, *psid);
+ unbecome_root();
+
+ if ( !ret ) {
+
+ /* fallback to rid mapping if enabled */
+
+ if ( lp_enable_rid_algorithm() ) {
+
+ if (!sid_check_is_in_our_domain(psid) ) {
+ DEBUG(5,("local_sid_to_gid: RID algorithm only supported for our domain (%s is not)\n", sid_string_static(psid)));
+ return False;
+ }
+
+ if (!sid_peek_rid(psid, &rid)) {
+ DEBUG(10,("local_sid_to_uid: invalid SID!\n"));
+ return False;
+ }
+
+ DEBUG(10,("local_sid_to_gid: Fall back to algorithmic mapping\n"));
+
+ if (fallback_pdb_rid_is_user(rid)) {
+ DEBUG(3, ("local_sid_to_gid: SID %s is *NOT* a group\n", sid_string_static(psid)));
+ return False;
+ } else {
+ *pgid = pdb_group_rid_to_gid(rid);
+ DEBUG(10,("local_sid_to_gid: mapping: %s -> %u\n", sid_string_static(psid), (unsigned int)(*pgid)));
+ return True;
+ }
+ }
+
+ return False;
+ }
+
+ *pgid = group.gid;
+
+ DEBUG(10,("local_sid_to_gid: SID %s -> gid (%u)\n", sid_string_static(psid),
+ (unsigned int)*pgid));
+
+ return True;
+}
+
+/**********************************************************************
+ Marshall/unmarshall SAM_ACCOUNT structs.
+ *********************************************************************/
+
+#define TDB_FORMAT_STRING_V0 "ddddddBBBBBBBBBBBBddBBwdwdBwwd"
+#define TDB_FORMAT_STRING_V1 "dddddddBBBBBBBBBBBBddBBwdwdBwwd"
+
+/**********************************************************************
+ Intialize a SAM_ACCOUNT struct from a BYTE buffer of size len
+ *********************************************************************/
+
+BOOL init_sam_from_buffer(SAM_ACCOUNT *sampass, uint8 *buf, uint32 buflen)
+{
+ return(init_sam_from_buffer_v1(sampass, buf, buflen));
+}
+
+/**********************************************************************
+ Intialize a BYTE buffer from a SAM_ACCOUNT struct
+ *********************************************************************/
+
+uint32 init_buffer_from_sam (uint8 **buf, const SAM_ACCOUNT *sampass, BOOL size_only)
+{
+ return(init_buffer_from_sam_v1(buf, sampass, size_only));
+}
+
+
+BOOL init_sam_from_buffer_v0(SAM_ACCOUNT *sampass, uint8 *buf, uint32 buflen)
+{
+
+ /* times are stored as 32bit integer
+ take care on system with 64bit wide time_t
+ --SSS */
+ uint32 logon_time,
+ logoff_time,
+ kickoff_time,
+ pass_last_set_time,
+ pass_can_change_time,
+ pass_must_change_time;
+ char *username;
+ char *domain;
+ char *nt_username;
+ char *dir_drive;
+ char *unknown_str;
+ char *munged_dial;
+ char *fullname;
+ char *homedir;
+ char *logon_script;
+ char *profile_path;
+ char *acct_desc;
+ char *workstations;
+ uint32 username_len, domain_len, nt_username_len,
+ dir_drive_len, unknown_str_len, munged_dial_len,
+ fullname_len, homedir_len, logon_script_len,
+ profile_path_len, acct_desc_len, workstations_len;
+
+ uint32 user_rid, group_rid, remove_me, hours_len, unknown_6;
+ uint16 acct_ctrl, logon_divs;
+ uint16 bad_password_count, logon_count;
+ uint8 *hours;
+ static uint8 *lm_pw_ptr, *nt_pw_ptr;
+ uint32 len = 0;
+ uint32 lm_pw_len, nt_pw_len, hourslen;
+ BOOL ret = True;
+
+ if(sampass == NULL || buf == NULL) {
+ DEBUG(0, ("init_sam_from_buffer: NULL parameters found!\n"));
+ return False;
+ }
+
+ /* unpack the buffer into variables */
+ len = tdb_unpack ((char *)buf, buflen, TDB_FORMAT_STRING_V0,
+ &logon_time,
+ &logoff_time,
+ &kickoff_time,
+ &pass_last_set_time,
+ &pass_can_change_time,
+ &pass_must_change_time,
+ &username_len, &username,
+ &domain_len, &domain,
+ &nt_username_len, &nt_username,
+ &fullname_len, &fullname,
+ &homedir_len, &homedir,
+ &dir_drive_len, &dir_drive,
+ &logon_script_len, &logon_script,
+ &profile_path_len, &profile_path,
+ &acct_desc_len, &acct_desc,
+ &workstations_len, &workstations,
+ &unknown_str_len, &unknown_str,
+ &munged_dial_len, &munged_dial,
+ &user_rid,
+ &group_rid,
+ &lm_pw_len, &lm_pw_ptr,
+ &nt_pw_len, &nt_pw_ptr,
+ &acct_ctrl,
+ &remove_me, /* remove on the next TDB_FORMAT upgarde */
+ &logon_divs,
+ &hours_len,
+ &hourslen, &hours,
+ &bad_password_count,
+ &logon_count,
+ &unknown_6);
+
+ if (len == (uint32) -1) {
+ ret = False;
+ goto done;
+ }
+
+ pdb_set_logon_time(sampass, logon_time, PDB_SET);
+ pdb_set_logoff_time(sampass, logoff_time, PDB_SET);
+ pdb_set_kickoff_time(sampass, kickoff_time, PDB_SET);
+ pdb_set_pass_can_change_time(sampass, pass_can_change_time, PDB_SET);
+ pdb_set_pass_must_change_time(sampass, pass_must_change_time, PDB_SET);
+ pdb_set_pass_last_set_time(sampass, pass_last_set_time, PDB_SET);
+
+ pdb_set_username(sampass, username, PDB_SET);
+ pdb_set_domain(sampass, domain, PDB_SET);
+ pdb_set_nt_username(sampass, nt_username, PDB_SET);
+ pdb_set_fullname(sampass, fullname, PDB_SET);
+
+ if (homedir) {
+ pdb_set_homedir(sampass, homedir, PDB_SET);
+ }
+ else {
+ pdb_set_homedir(sampass,
+ talloc_sub_basic(sampass->mem_ctx, username, lp_logon_home()),
+ PDB_DEFAULT);
+ }
+
+ if (dir_drive)
+ pdb_set_dir_drive(sampass, dir_drive, PDB_SET);
+ else {
+ pdb_set_dir_drive(sampass,
+ talloc_sub_basic(sampass->mem_ctx, username, lp_logon_drive()),
+ PDB_DEFAULT);
+ }
+
+ if (logon_script)
+ pdb_set_logon_script(sampass, logon_script, PDB_SET);
+ else {
+ pdb_set_logon_script(sampass,
+ talloc_sub_basic(sampass->mem_ctx, username, lp_logon_script()),
+ PDB_DEFAULT);
+ }
+
+ if (profile_path) {
+ pdb_set_profile_path(sampass, profile_path, PDB_SET);
+ } else {
+ pdb_set_profile_path(sampass,
+ talloc_sub_basic(sampass->mem_ctx, username, lp_logon_path()),
+ PDB_DEFAULT);
+ }
+
+ pdb_set_acct_desc(sampass, acct_desc, PDB_SET);
+ pdb_set_workstations(sampass, workstations, PDB_SET);
+ pdb_set_munged_dial(sampass, munged_dial, PDB_SET);
+
+ if (lm_pw_ptr && lm_pw_len == LM_HASH_LEN) {
+ if (!pdb_set_lanman_passwd(sampass, lm_pw_ptr, PDB_SET)) {
+ ret = False;
+ goto done;
+ }
+ }
+
+ if (nt_pw_ptr && nt_pw_len == NT_HASH_LEN) {
+ if (!pdb_set_nt_passwd(sampass, nt_pw_ptr, PDB_SET)) {
+ ret = False;
+ goto done;
+ }
+ }
+
+ pdb_set_user_sid_from_rid(sampass, user_rid, PDB_SET);
+ pdb_set_group_sid_from_rid(sampass, group_rid, PDB_SET);
+ pdb_set_hours_len(sampass, hours_len, PDB_SET);
+ pdb_set_bad_password_count(sampass, bad_password_count, PDB_SET);
+ pdb_set_logon_count(sampass, logon_count, PDB_SET);
+ pdb_set_unknown_6(sampass, unknown_6, PDB_SET);
+ pdb_set_acct_ctrl(sampass, acct_ctrl, PDB_SET);
+ pdb_set_logon_divs(sampass, logon_divs, PDB_SET);
+ pdb_set_hours(sampass, hours, PDB_SET);
+
+done:
+
+ SAFE_FREE(username);
+ SAFE_FREE(domain);
+ SAFE_FREE(nt_username);
+ SAFE_FREE(fullname);
+ SAFE_FREE(homedir);
+ SAFE_FREE(dir_drive);
+ SAFE_FREE(logon_script);
+ SAFE_FREE(profile_path);
+ SAFE_FREE(acct_desc);
+ SAFE_FREE(workstations);
+ SAFE_FREE(munged_dial);
+ SAFE_FREE(unknown_str);
+ SAFE_FREE(hours);
+
+ return ret;
+}
+
+
+uint32 init_buffer_from_sam_v0 (uint8 **buf, const SAM_ACCOUNT *sampass, BOOL size_only)
+{
+ size_t len, buflen;
+
+ /* times are stored as 32bit integer
+ take care on system with 64bit wide time_t
+ --SSS */
+ uint32 logon_time,
+ logoff_time,
+ kickoff_time,
+ pass_last_set_time,
+ pass_can_change_time,
+ pass_must_change_time;
+
+ uint32 user_rid, group_rid;
+
+ const char *username;
+ const char *domain;
+ const char *nt_username;
+ const char *dir_drive;
+ const char *unknown_str;
+ const char *munged_dial;
+ const char *fullname;
+ const char *homedir;
+ const char *logon_script;
+ const char *profile_path;
+ const char *acct_desc;
+ const char *workstations;
+ uint32 username_len, domain_len, nt_username_len,
+ dir_drive_len, unknown_str_len, munged_dial_len,
+ fullname_len, homedir_len, logon_script_len,
+ profile_path_len, acct_desc_len, workstations_len;
+
+ const uint8 *lm_pw;
+ const uint8 *nt_pw;
+ uint32 lm_pw_len = 16;
+ uint32 nt_pw_len = 16;
+
+ /* do we have a valid SAM_ACCOUNT pointer? */
+ if (sampass == NULL) {
+ DEBUG(0, ("init_buffer_from_sam: SAM_ACCOUNT is NULL!\n"));
+ return -1;
+ }
+
+ *buf = NULL;
+ buflen = 0;
+
+ logon_time = (uint32)pdb_get_logon_time(sampass);
+ logoff_time = (uint32)pdb_get_logoff_time(sampass);
+ kickoff_time = (uint32)pdb_get_kickoff_time(sampass);
+ pass_can_change_time = (uint32)pdb_get_pass_can_change_time(sampass);
+ pass_must_change_time = (uint32)pdb_get_pass_must_change_time(sampass);
+ pass_last_set_time = (uint32)pdb_get_pass_last_set_time(sampass);
+
+ user_rid = pdb_get_user_rid(sampass);
+ group_rid = pdb_get_group_rid(sampass);
+
+ username = pdb_get_username(sampass);
+ if (username)
+ username_len = strlen(username) +1;
+ else
+ username_len = 0;
+
+ domain = pdb_get_domain(sampass);
+ if (domain)
+ domain_len = strlen(domain) +1;
+ else
+ domain_len = 0;
+
+ nt_username = pdb_get_nt_username(sampass);
+ if (nt_username)
+ nt_username_len = strlen(nt_username) +1;
+ else
+ nt_username_len = 0;
+
+ fullname = pdb_get_fullname(sampass);
+ if (fullname)
+ fullname_len = strlen(fullname) +1;
+ else
+ fullname_len = 0;
+
+ /*
+ * Only updates fields which have been set (not defaults from smb.conf)
+ */
+
+ if (!IS_SAM_DEFAULT(sampass, PDB_DRIVE))
+ dir_drive = pdb_get_dir_drive(sampass);
+ else
+ dir_drive = NULL;
+ if (dir_drive)
+ dir_drive_len = strlen(dir_drive) +1;
+ else
+ dir_drive_len = 0;
+
+ if (!IS_SAM_DEFAULT(sampass, PDB_SMBHOME))
+ homedir = pdb_get_homedir(sampass);
+ else
+ homedir = NULL;
+ if (homedir)
+ homedir_len = strlen(homedir) +1;
+ else
+ homedir_len = 0;
+
+ if (!IS_SAM_DEFAULT(sampass, PDB_LOGONSCRIPT))
+ logon_script = pdb_get_logon_script(sampass);
+ else
+ logon_script = NULL;
+ if (logon_script)
+ logon_script_len = strlen(logon_script) +1;
+ else
+ logon_script_len = 0;
+
+ if (!IS_SAM_DEFAULT(sampass, PDB_PROFILE))
+ profile_path = pdb_get_profile_path(sampass);
+ else
+ profile_path = NULL;
+ if (profile_path)
+ profile_path_len = strlen(profile_path) +1;
+ else
+ profile_path_len = 0;
+
+ lm_pw = pdb_get_lanman_passwd(sampass);
+ if (!lm_pw)
+ lm_pw_len = 0;
+
+ nt_pw = pdb_get_nt_passwd(sampass);
+ if (!nt_pw)
+ nt_pw_len = 0;
+
+ acct_desc = pdb_get_acct_desc(sampass);
+ if (acct_desc)
+ acct_desc_len = strlen(acct_desc) +1;
+ else
+ acct_desc_len = 0;
+
+ workstations = pdb_get_workstations(sampass);
+ if (workstations)
+ workstations_len = strlen(workstations) +1;
+ else
+ workstations_len = 0;
+
+ unknown_str = NULL;
+ unknown_str_len = 0;
+
+ munged_dial = pdb_get_munged_dial(sampass);
+ if (munged_dial)
+ munged_dial_len = strlen(munged_dial) +1;
+ else
+ munged_dial_len = 0;
+
+ /* one time to get the size needed */
+ len = tdb_pack(NULL, 0, TDB_FORMAT_STRING_V0,
+ logon_time,
+ logoff_time,
+ kickoff_time,
+ pass_last_set_time,
+ pass_can_change_time,
+ pass_must_change_time,
+ username_len, username,
+ domain_len, domain,
+ nt_username_len, nt_username,
+ fullname_len, fullname,
+ homedir_len, homedir,
+ dir_drive_len, dir_drive,
+ logon_script_len, logon_script,
+ profile_path_len, profile_path,
+ acct_desc_len, acct_desc,
+ workstations_len, workstations,
+ unknown_str_len, unknown_str,
+ munged_dial_len, munged_dial,
+ user_rid,
+ group_rid,
+ lm_pw_len, lm_pw,
+ nt_pw_len, nt_pw,
+ pdb_get_acct_ctrl(sampass),
+ 0, /* was: fileds_present, to be removed on format change */
+ pdb_get_logon_divs(sampass),
+ pdb_get_hours_len(sampass),
+ MAX_HOURS_LEN, pdb_get_hours(sampass),
+ pdb_get_bad_password_count(sampass),
+ pdb_get_logon_count(sampass),
+ pdb_get_unknown_6(sampass));
+
+
+ if (size_only)
+ return buflen;
+
+ /* malloc the space needed */
+ if ( (*buf=(uint8*)malloc(len)) == NULL) {
+ DEBUG(0,("init_buffer_from_sam: Unable to malloc() memory for buffer!\n"));
+ return (-1);
+ }
+
+ /* now for the real call to tdb_pack() */
+ buflen = tdb_pack((char *)*buf, len, TDB_FORMAT_STRING_V0,
+ logon_time,
+ logoff_time,
+ kickoff_time,
+ pass_last_set_time,
+ pass_can_change_time,
+ pass_must_change_time,
+ username_len, username,
+ domain_len, domain,
+ nt_username_len, nt_username,
+ fullname_len, fullname,
+ homedir_len, homedir,
+ dir_drive_len, dir_drive,
+ logon_script_len, logon_script,
+ profile_path_len, profile_path,
+ acct_desc_len, acct_desc,
+ workstations_len, workstations,
+ unknown_str_len, unknown_str,
+ munged_dial_len, munged_dial,
+ user_rid,
+ group_rid,
+ lm_pw_len, lm_pw,
+ nt_pw_len, nt_pw,
+ pdb_get_acct_ctrl(sampass),
+ 0, /* was: fileds_present, to be removed on format change */
+ pdb_get_logon_divs(sampass),
+ pdb_get_hours_len(sampass),
+ MAX_HOURS_LEN, pdb_get_hours(sampass),
+ pdb_get_bad_password_count(sampass),
+ pdb_get_logon_count(sampass),
+ pdb_get_unknown_6(sampass));
+
+
+ /* check to make sure we got it correct */
+ if (buflen != len) {
+ DEBUG(0, ("init_buffer_from_sam: somthing odd is going on here: bufflen (%lu) != len (%lu) in tdb_pack operations!\n",
+ (unsigned long)buflen, (unsigned long)len));
+ /* error */
+ SAFE_FREE (*buf);
+ return (-1);
+ }
+
+ return (buflen);
+}
+
+
+BOOL init_sam_from_buffer_v1(SAM_ACCOUNT *sampass, uint8 *buf, uint32 buflen)
+{
+
+ /* times are stored as 32bit integer
+ take care on system with 64bit wide time_t
+ --SSS */
+ uint32 logon_time,
+ logoff_time,
+ kickoff_time,
+ bad_password_time,
+ pass_last_set_time,
+ pass_can_change_time,
+ pass_must_change_time;
+ char *username;
+ char *domain;
+ char *nt_username;
+ char *dir_drive;
+ char *unknown_str;
+ char *munged_dial;
+ char *fullname;
+ char *homedir;
+ char *logon_script;
+ char *profile_path;
+ char *acct_desc;
+ char *workstations;
+ uint32 username_len, domain_len, nt_username_len,
+ dir_drive_len, unknown_str_len, munged_dial_len,
+ fullname_len, homedir_len, logon_script_len,
+ profile_path_len, acct_desc_len, workstations_len;
+
+ uint32 user_rid, group_rid, remove_me, hours_len, unknown_6;
+ uint16 acct_ctrl, logon_divs;
+ uint16 bad_password_count, logon_count;
+ uint8 *hours;
+ static uint8 *lm_pw_ptr, *nt_pw_ptr;
+ uint32 len = 0;
+ uint32 lm_pw_len, nt_pw_len, hourslen;
+ BOOL ret = True;
+
+ if(sampass == NULL || buf == NULL) {
+ DEBUG(0, ("init_sam_from_buffer: NULL parameters found!\n"));
+ return False;
+ }
+
+ /* unpack the buffer into variables */
+ len = tdb_unpack ((char *)buf, buflen, TDB_FORMAT_STRING_V1,
+ &logon_time,
+ &logoff_time,
+ &kickoff_time,
+ &bad_password_time,
+ &pass_last_set_time,
+ &pass_can_change_time,
+ &pass_must_change_time,
+ &username_len, &username,
+ &domain_len, &domain,
+ &nt_username_len, &nt_username,
+ &fullname_len, &fullname,
+ &homedir_len, &homedir,
+ &dir_drive_len, &dir_drive,
+ &logon_script_len, &logon_script,
+ &profile_path_len, &profile_path,
+ &acct_desc_len, &acct_desc,
+ &workstations_len, &workstations,
+ &unknown_str_len, &unknown_str,
+ &munged_dial_len, &munged_dial,
+ &user_rid,
+ &group_rid,
+ &lm_pw_len, &lm_pw_ptr,
+ &nt_pw_len, &nt_pw_ptr,
+ &acct_ctrl,
+ &remove_me,
+ &logon_divs,
+ &hours_len,
+ &hourslen, &hours,
+ &bad_password_count,
+ &logon_count,
+ &unknown_6);
+
+ if (len == (uint32) -1) {
+ ret = False;
+ goto done;
+ }
+
+ pdb_set_logon_time(sampass, logon_time, PDB_SET);
+ pdb_set_logoff_time(sampass, logoff_time, PDB_SET);
+ pdb_set_kickoff_time(sampass, kickoff_time, PDB_SET);
+ pdb_set_bad_password_time(sampass, bad_password_time, PDB_SET);
+ pdb_set_pass_can_change_time(sampass, pass_can_change_time, PDB_SET);
+ pdb_set_pass_must_change_time(sampass, pass_must_change_time, PDB_SET);
+ pdb_set_pass_last_set_time(sampass, pass_last_set_time, PDB_SET);
+
+ pdb_set_username(sampass, username, PDB_SET);
+ pdb_set_domain(sampass, domain, PDB_SET);
+ pdb_set_nt_username(sampass, nt_username, PDB_SET);
+ pdb_set_fullname(sampass, fullname, PDB_SET);
+
+ if (homedir) {
+ pdb_set_homedir(sampass, homedir, PDB_SET);
+ }
+ else {
+ pdb_set_homedir(sampass,
+ talloc_sub_basic(sampass->mem_ctx, username, lp_logon_home()),
+ PDB_DEFAULT);
+ }
+
+ if (dir_drive)
+ pdb_set_dir_drive(sampass, dir_drive, PDB_SET);
+ else {
+ pdb_set_dir_drive(sampass,
+ talloc_sub_basic(sampass->mem_ctx, username, lp_logon_drive()),
+ PDB_DEFAULT);
+ }
+
+ if (logon_script)
+ pdb_set_logon_script(sampass, logon_script, PDB_SET);
+ else {
+ pdb_set_logon_script(sampass,
+ talloc_sub_basic(sampass->mem_ctx, username, lp_logon_script()),
+ PDB_DEFAULT);
+ }
+
+ if (profile_path) {
+ pdb_set_profile_path(sampass, profile_path, PDB_SET);
+ } else {
+ pdb_set_profile_path(sampass,
+ talloc_sub_basic(sampass->mem_ctx, username, lp_logon_path()),
+ PDB_DEFAULT);
+ }
+
+ pdb_set_acct_desc(sampass, acct_desc, PDB_SET);
+ pdb_set_workstations(sampass, workstations, PDB_SET);
+ pdb_set_munged_dial(sampass, munged_dial, PDB_SET);
+
+ if (lm_pw_ptr && lm_pw_len == LM_HASH_LEN) {
+ if (!pdb_set_lanman_passwd(sampass, lm_pw_ptr, PDB_SET)) {
+ ret = False;
+ goto done;
+ }
+ }
+
+ if (nt_pw_ptr && nt_pw_len == NT_HASH_LEN) {
+ if (!pdb_set_nt_passwd(sampass, nt_pw_ptr, PDB_SET)) {
+ ret = False;
+ goto done;
+ }
+ }
+
+ pdb_set_user_sid_from_rid(sampass, user_rid, PDB_SET);
+ pdb_set_group_sid_from_rid(sampass, group_rid, PDB_SET);
+ pdb_set_hours_len(sampass, hours_len, PDB_SET);
+ pdb_set_bad_password_count(sampass, bad_password_count, PDB_SET);
+ pdb_set_logon_count(sampass, logon_count, PDB_SET);
+ pdb_set_unknown_6(sampass, unknown_6, PDB_SET);
+ pdb_set_acct_ctrl(sampass, acct_ctrl, PDB_SET);
+ pdb_set_logon_divs(sampass, logon_divs, PDB_SET);
+ pdb_set_hours(sampass, hours, PDB_SET);
+
+done:
+
+ SAFE_FREE(lm_pw_ptr);
+ SAFE_FREE(nt_pw_ptr);
+ SAFE_FREE(username);
+ SAFE_FREE(domain);
+ SAFE_FREE(nt_username);
+ SAFE_FREE(fullname);
+ SAFE_FREE(homedir);
+ SAFE_FREE(dir_drive);
+ SAFE_FREE(logon_script);
+ SAFE_FREE(profile_path);
+ SAFE_FREE(acct_desc);
+ SAFE_FREE(workstations);
+ SAFE_FREE(munged_dial);
+ SAFE_FREE(unknown_str);
+ SAFE_FREE(hours);
+
+ return ret;
+}
+
+
+uint32 init_buffer_from_sam_v1 (uint8 **buf, const SAM_ACCOUNT *sampass, BOOL size_only)
+{
+ size_t len, buflen;
+
+ /* times are stored as 32bit integer
+ take care on system with 64bit wide time_t
+ --SSS */
+ uint32 logon_time,
+ logoff_time,
+ kickoff_time,
+ bad_password_time,
+ pass_last_set_time,
+ pass_can_change_time,
+ pass_must_change_time;
+
+ uint32 user_rid, group_rid;
+
+ const char *username;
+ const char *domain;
+ const char *nt_username;
+ const char *dir_drive;
+ const char *unknown_str;
+ const char *munged_dial;
+ const char *fullname;
+ const char *homedir;
+ const char *logon_script;
+ const char *profile_path;
+ const char *acct_desc;
+ const char *workstations;
+ uint32 username_len, domain_len, nt_username_len,
+ dir_drive_len, unknown_str_len, munged_dial_len,
+ fullname_len, homedir_len, logon_script_len,
+ profile_path_len, acct_desc_len, workstations_len;
+
+ const uint8 *lm_pw;
+ const uint8 *nt_pw;
+ uint32 lm_pw_len = 16;
+ uint32 nt_pw_len = 16;
+
+ /* do we have a valid SAM_ACCOUNT pointer? */
+ if (sampass == NULL) {
+ DEBUG(0, ("init_buffer_from_sam: SAM_ACCOUNT is NULL!\n"));
+ return -1;
+ }
+
+ *buf = NULL;
+ buflen = 0;
+
+ logon_time = (uint32)pdb_get_logon_time(sampass);
+ logoff_time = (uint32)pdb_get_logoff_time(sampass);
+ kickoff_time = (uint32)pdb_get_kickoff_time(sampass);
+ bad_password_time = (uint32)pdb_get_bad_password_time(sampass);
+ pass_can_change_time = (uint32)pdb_get_pass_can_change_time(sampass);
+ pass_must_change_time = (uint32)pdb_get_pass_must_change_time(sampass);
+ pass_last_set_time = (uint32)pdb_get_pass_last_set_time(sampass);
+
+ user_rid = pdb_get_user_rid(sampass);
+ group_rid = pdb_get_group_rid(sampass);
+
+ username = pdb_get_username(sampass);
+ if (username)
+ username_len = strlen(username) +1;
+ else
+ username_len = 0;
+
+ domain = pdb_get_domain(sampass);
+ if (domain)
+ domain_len = strlen(domain) +1;
+ else
+ domain_len = 0;
+
+ nt_username = pdb_get_nt_username(sampass);
+ if (nt_username)
+ nt_username_len = strlen(nt_username) +1;
+ else
+ nt_username_len = 0;
+
+ fullname = pdb_get_fullname(sampass);
+ if (fullname)
+ fullname_len = strlen(fullname) +1;
+ else
+ fullname_len = 0;
+
+ /*
+ * Only updates fields which have been set (not defaults from smb.conf)
+ */
+
+ if (!IS_SAM_DEFAULT(sampass, PDB_DRIVE))
+ dir_drive = pdb_get_dir_drive(sampass);
+ else
+ dir_drive = NULL;
+ if (dir_drive)
+ dir_drive_len = strlen(dir_drive) +1;
+ else
+ dir_drive_len = 0;
+
+ if (!IS_SAM_DEFAULT(sampass, PDB_SMBHOME))
+ homedir = pdb_get_homedir(sampass);
+ else
+ homedir = NULL;
+ if (homedir)
+ homedir_len = strlen(homedir) +1;
+ else
+ homedir_len = 0;
+
+ if (!IS_SAM_DEFAULT(sampass, PDB_LOGONSCRIPT))
+ logon_script = pdb_get_logon_script(sampass);
+ else
+ logon_script = NULL;
+ if (logon_script)
+ logon_script_len = strlen(logon_script) +1;
+ else
+ logon_script_len = 0;
+
+ if (!IS_SAM_DEFAULT(sampass, PDB_PROFILE))
+ profile_path = pdb_get_profile_path(sampass);
+ else
+ profile_path = NULL;
+ if (profile_path)
+ profile_path_len = strlen(profile_path) +1;
+ else
+ profile_path_len = 0;
+
+ lm_pw = pdb_get_lanman_passwd(sampass);
+ if (!lm_pw)
+ lm_pw_len = 0;
+
+ nt_pw = pdb_get_nt_passwd(sampass);
+ if (!nt_pw)
+ nt_pw_len = 0;
+
+ acct_desc = pdb_get_acct_desc(sampass);
+ if (acct_desc)
+ acct_desc_len = strlen(acct_desc) +1;
+ else
+ acct_desc_len = 0;
+
+ workstations = pdb_get_workstations(sampass);
+ if (workstations)
+ workstations_len = strlen(workstations) +1;
+ else
+ workstations_len = 0;
+
+ unknown_str = NULL;
+ unknown_str_len = 0;
+
+ munged_dial = pdb_get_munged_dial(sampass);
+ if (munged_dial)
+ munged_dial_len = strlen(munged_dial) +1;
+ else
+ munged_dial_len = 0;
+
+ /* one time to get the size needed */
+ len = tdb_pack(NULL, 0, TDB_FORMAT_STRING_V1,
+ logon_time,
+ logoff_time,
+ kickoff_time,
+ bad_password_time,
+ pass_last_set_time,
+ pass_can_change_time,
+ pass_must_change_time,
+ username_len, username,
+ domain_len, domain,
+ nt_username_len, nt_username,
+ fullname_len, fullname,
+ homedir_len, homedir,
+ dir_drive_len, dir_drive,
+ logon_script_len, logon_script,
+ profile_path_len, profile_path,
+ acct_desc_len, acct_desc,
+ workstations_len, workstations,
+ unknown_str_len, unknown_str,
+ munged_dial_len, munged_dial,
+ user_rid,
+ group_rid,
+ lm_pw_len, lm_pw,
+ nt_pw_len, nt_pw,
+ pdb_get_acct_ctrl(sampass),
+ 0,
+ pdb_get_logon_divs(sampass),
+ pdb_get_hours_len(sampass),
+ MAX_HOURS_LEN, pdb_get_hours(sampass),
+ pdb_get_bad_password_count(sampass),
+ pdb_get_logon_count(sampass),
+ pdb_get_unknown_6(sampass));
+
+
+ if (size_only)
+ return buflen;
+
+ /* malloc the space needed */
+ if ( (*buf=(uint8*)malloc(len)) == NULL) {
+ DEBUG(0,("init_buffer_from_sam: Unable to malloc() memory for buffer!\n"));
+ return (-1);
+ }
+
+ /* now for the real call to tdb_pack() */
+ buflen = tdb_pack((char *)*buf, len, TDB_FORMAT_STRING_V1,
+ logon_time,
+ logoff_time,
+ kickoff_time,
+ bad_password_time,
+ pass_last_set_time,
+ pass_can_change_time,
+ pass_must_change_time,
+ username_len, username,
+ domain_len, domain,
+ nt_username_len, nt_username,
+ fullname_len, fullname,
+ homedir_len, homedir,
+ dir_drive_len, dir_drive,
+ logon_script_len, logon_script,
+ profile_path_len, profile_path,
+ acct_desc_len, acct_desc,
+ workstations_len, workstations,
+ unknown_str_len, unknown_str,
+ munged_dial_len, munged_dial,
+ user_rid,
+ group_rid,
+ lm_pw_len, lm_pw,
+ nt_pw_len, nt_pw,
+ pdb_get_acct_ctrl(sampass),
+ 0,
+ pdb_get_logon_divs(sampass),
+ pdb_get_hours_len(sampass),
+ MAX_HOURS_LEN, pdb_get_hours(sampass),
+ pdb_get_bad_password_count(sampass),
+ pdb_get_logon_count(sampass),
+ pdb_get_unknown_6(sampass));
+
+
+ /* check to make sure we got it correct */
+ if (buflen != len) {
+ DEBUG(0, ("init_buffer_from_sam: somthing odd is going on here: bufflen (%lu) != len (%lu) in tdb_pack operations!\n",
+ (unsigned long)buflen, (unsigned long)len));
+ /* error */
+ SAFE_FREE (*buf);
+ return (-1);
+ }
+
+ return (buflen);
+}
+
+
+/**********************************************************************
+**********************************************************************/
+
+static BOOL get_free_ugid_range(uint32 *low, uint32 *high)
+{
+ uid_t u_low, u_high;
+ gid_t g_low, g_high;
+
+ if (!lp_idmap_uid(&u_low, &u_high) || !lp_idmap_gid(&g_low, &g_high)) {
+ return False;
+ }
+
+ *low = (u_low < g_low) ? u_low : g_low;
+ *high = (u_high < g_high) ? u_high : g_high;
+
+ return True;
+}
+
+/******************************************************************
+ Get the the non-algorithmic RID range if idmap range are defined
+******************************************************************/
+
+BOOL get_free_rid_range(uint32 *low, uint32 *high)
+{
+ uint32 id_low, id_high;
+
+ if (!lp_enable_rid_algorithm()) {
+ *low = BASE_RID;
+ *high = (uint32)-1;
+ }
+
+ if (!get_free_ugid_range(&id_low, &id_high)) {
+ return False;
+ }
+
+ *low = fallback_pdb_uid_to_user_rid(id_low);
+ if (fallback_pdb_user_rid_to_uid((uint32)-1) < id_high) {
+ *high = (uint32)-1;
+ } else {
+ *high = fallback_pdb_uid_to_user_rid(id_high);
+ }
+
+ return True;
+}
+
+/*********************************************************************
+ Update the bad password count checking the AP_RESET_COUNT_TIME
+*********************************************************************/
+
+BOOL pdb_update_bad_password_count(SAM_ACCOUNT *sampass, BOOL *updated)
+{
+ time_t LastBadPassword;
+ uint16 BadPasswordCount;
+ uint32 resettime;
+
+ if (!sampass) return False;
+
+ BadPasswordCount = pdb_get_bad_password_count(sampass);
+ if (!BadPasswordCount) {
+ DEBUG(9, ("No bad password attempts.\n"));
+ return True;
+ }
+
+ if (!account_policy_get(AP_RESET_COUNT_TIME, &resettime)) {
+ DEBUG(0, ("pdb_update_bad_password_count: account_policy_get failed.\n"));
+ return False;
+ }
+
+ /* First, check if there is a reset time to compare */
+ if ((resettime == (uint32) -1) || (resettime == 0)) {
+ DEBUG(9, ("No reset time, can't reset bad pw count\n"));
+ return True;
+ }
+
+ LastBadPassword = pdb_get_bad_password_time(sampass);
+ DEBUG(7, ("LastBadPassword=%d, resettime=%d, current time=%d.\n",
+ (uint32) LastBadPassword, resettime, (uint32)time(NULL)));
+ if (time(NULL) > (LastBadPassword + (time_t)resettime*60)){
+ pdb_set_bad_password_count(sampass, 0, PDB_CHANGED);
+ pdb_set_bad_password_time(sampass, 0, PDB_CHANGED);
+ if (updated) *updated = True;
+ }
+
+ return True;
+}
+
+/*********************************************************************
+ Update the ACB_AUTOLOCK flag checking the AP_LOCK_ACCOUNT_DURATION
+*********************************************************************/
+
+BOOL pdb_update_autolock_flag(SAM_ACCOUNT *sampass, BOOL *updated)
+{
+ uint32 duration;
+ time_t LastBadPassword;
+
+ if (!sampass) return False;
+
+ if (!(pdb_get_acct_ctrl(sampass) & ACB_AUTOLOCK)) {
+ DEBUG(9, ("Account not autolocked, no check needed\n"));
+ return True;
+ }
+
+ if (!account_policy_get(AP_LOCK_ACCOUNT_DURATION, &duration)) {
+ DEBUG(0, ("pdb_update_autolock_flag: account_policy_get failed.\n"));
+ return False;
+ }
+
+ /* First, check if there is a duration to compare */
+ if ((duration == (uint32) -1) || (duration == 0)) {
+ DEBUG(9, ("No reset duration, can't reset autolock\n"));
+ return True;
+ }
+
+ LastBadPassword = pdb_get_bad_password_time(sampass);
+ DEBUG(7, ("LastBadPassword=%d, duration=%d, current time =%d.\n",
+ (uint32)LastBadPassword, duration*60, (uint32)time(NULL)));
+ if ((time(NULL) > (LastBadPassword + (time_t) duration * 60))) {
+ pdb_set_acct_ctrl(sampass,
+ pdb_get_acct_ctrl(sampass) & ~ACB_AUTOLOCK,
+ PDB_CHANGED);
+ pdb_set_bad_password_count(sampass, 0, PDB_CHANGED);
+ pdb_set_bad_password_time(sampass, 0, PDB_CHANGED);
+ if (updated) *updated = True;
+ }
+
+ return True;
+}
+
+/*********************************************************************
+ Increment the bad_password_count
+*********************************************************************/
+
+BOOL pdb_increment_bad_password_count(SAM_ACCOUNT *sampass)
+{
+ uint32 account_policy_lockout;
+ BOOL autolock_updated = False, badpw_updated = False;
+
+ if (!sampass)
+ return False;
+
+ /* Retrieve the account lockout policy */
+ if (!account_policy_get(AP_BAD_ATTEMPT_LOCKOUT,
+ &account_policy_lockout)) {
+ DEBUG(0, ("pdb_increment_bad_password_count: account_policy_get failed.\n"));
+ return False;
+ }
+
+ /* If there is no policy, we don't need to continue checking */
+ if (!account_policy_lockout) {
+ DEBUG(9, ("No lockout policy, don't track bad passwords\n"));
+ return True;
+ }
+
+ /* Check if the autolock needs to be cleared */
+ if (!pdb_update_autolock_flag(sampass, &autolock_updated))
+ return False;
+
+ /* Check if the badpw count needs to be reset */
+ if (!pdb_update_bad_password_count(sampass, &badpw_updated))
+ return False;
+
+ /*
+ Ok, now we can assume that any resetting that needs to be
+ done has been done, and just get on with incrementing
+ and autolocking if necessary
+ */
+
+ pdb_set_bad_password_count(sampass,
+ pdb_get_bad_password_count(sampass)+1,
+ PDB_CHANGED);
+ pdb_set_bad_password_time(sampass, time(NULL), PDB_CHANGED);
+
+
+ if (pdb_get_bad_password_count(sampass) < account_policy_lockout)
+ return True;
+
+ if (!pdb_set_acct_ctrl(sampass,
+ pdb_get_acct_ctrl(sampass) | ACB_AUTOLOCK,
+ PDB_CHANGED)) {
+ DEBUG(1, ("pdb_increment_bad_password_count:failed to set 'autolock' flag. \n"));
+ return False;
+ }
+
+ return True;
+}
+
+BOOL get_sids_from_priv(const char *privname, DOM_SID **sids, int *num)
+{
+ char *sids_string;
+ char *s;
+ fstring tok;
+
+ if (!pdb_get_privilege_entry(privname, &sids_string))
+ return False;
+
+ s = sids_string;
+
+ while (next_token(&s, tok, ",", sizeof(tok))) {
+ DOM_SID sid;
+ DEBUG(10, ("converting SID %s\n", tok));
+
+ if (!string_to_sid(&sid, tok)) {
+ DEBUG(3, ("Could not convert SID\n"));
+ continue;
+ }
+
+ add_sid_to_array(&sid, sids, num);
+ }
+
+ SAFE_FREE(sids_string);
+ return True;
+}
+
+BOOL get_priv_for_sid(const DOM_SID *sid, PRIVILEGE_SET *priv)
+{
+ extern PRIVS privs[];
+ int i;
+ for (i=1; i<PRIV_ALL_INDEX-1; i++) {
+ DOM_SID *sids;
+ int j, num;
+
+ if (!get_sids_from_priv(privs[i].priv, &sids, &num))
+ continue;
+
+ for (j=0; j<num; j++) {
+ if (sid_compare(sid, &sids[j]) == 0)
+ add_privilege_by_name(priv, privs[i].priv);
+ }
+ SAFE_FREE(sids);
+ }
+ return True;
+}
diff --git a/source/passdb/pdb_compat.c b/source/passdb/pdb_compat.c
new file mode 100644
index 00000000000..abd572a7c14
--- /dev/null
+++ b/source/passdb/pdb_compat.c
@@ -0,0 +1,104 @@
+/*
+ Unix SMB/CIFS implementation.
+ SAM_ACCOUNT access routines
+ Copyright (C) Jeremy Allison 1996-2001
+ Copyright (C) Luke Kenneth Casson Leighton 1996-1998
+ Copyright (C) Gerald (Jerry) Carter 2000-2001
+ Copyright (C) Andrew Bartlett 2001-2002
+ Copyright (C) Stefan (metze) Metzmacher 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 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"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_PASSDB
+
+uint32 pdb_get_user_rid (const SAM_ACCOUNT *sampass)
+{
+ uint32 u_rid;
+
+ if (sampass)
+ if (sid_peek_check_rid(get_global_sam_sid(), pdb_get_user_sid(sampass),&u_rid))
+ return u_rid;
+
+ return (0);
+}
+
+uint32 pdb_get_group_rid (const SAM_ACCOUNT *sampass)
+{
+ uint32 g_rid;
+
+ if (sampass)
+ if (sid_peek_check_rid(get_global_sam_sid(), pdb_get_group_sid(sampass),&g_rid))
+ return g_rid;
+ return (0);
+}
+
+BOOL pdb_set_user_sid_from_rid (SAM_ACCOUNT *sampass, uint32 rid, enum pdb_value_state flag)
+{
+ DOM_SID u_sid;
+ const DOM_SID *global_sam_sid;
+
+ if (!sampass)
+ return False;
+
+ if (!(global_sam_sid = get_global_sam_sid())) {
+ DEBUG(1, ("pdb_set_user_sid_from_rid: Could not read global sam sid!\n"));
+ return False;
+ }
+
+ sid_copy(&u_sid, global_sam_sid);
+
+ if (!sid_append_rid(&u_sid, rid))
+ return False;
+
+ if (!pdb_set_user_sid(sampass, &u_sid, flag))
+ return False;
+
+ DEBUG(10, ("pdb_set_user_sid_from_rid:\n\tsetting user sid %s from rid %d\n",
+ sid_string_static(&u_sid),rid));
+
+ return True;
+}
+
+BOOL pdb_set_group_sid_from_rid (SAM_ACCOUNT *sampass, uint32 grid, enum pdb_value_state flag)
+{
+ DOM_SID g_sid;
+ const DOM_SID *global_sam_sid;
+
+ if (!sampass)
+ return False;
+
+ if (!(global_sam_sid = get_global_sam_sid())) {
+ DEBUG(1, ("pdb_set_user_sid_from_rid: Could not read global sam sid!\n"));
+ return False;
+ }
+
+ sid_copy(&g_sid, global_sam_sid);
+
+ if (!sid_append_rid(&g_sid, grid))
+ return False;
+
+ if (!pdb_set_group_sid(sampass, &g_sid, flag))
+ return False;
+
+ DEBUG(10, ("pdb_set_group_sid_from_rid:\n\tsetting group sid %s from rid %d\n",
+ sid_string_static(&g_sid), grid));
+
+ return True;
+}
+
diff --git a/source/passdb/pdb_get_set.c b/source/passdb/pdb_get_set.c
new file mode 100644
index 00000000000..908588c8988
--- /dev/null
+++ b/source/passdb/pdb_get_set.c
@@ -0,0 +1,1158 @@
+/*
+ Unix SMB/CIFS implementation.
+ SAM_ACCOUNT access routines
+ Copyright (C) Jeremy Allison 1996-2001
+ Copyright (C) Luke Kenneth Casson Leighton 1996-1998
+ Copyright (C) Gerald (Jerry) Carter 2000-2001
+ Copyright (C) Andrew Bartlett 2001-2002
+ Copyright (C) Stefan (metze) Metzmacher 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 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"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_PASSDB
+
+/**
+ * @todo Redefine this to NULL, but this changes the API because
+ * much of samba assumes that the pdb_get...() funtions
+ * return pstrings. (ie not null-pointers).
+ * See also pdb_fill_default_sam().
+ */
+
+#define PDB_NOT_QUITE_NULL ""
+
+/*********************************************************************
+ Collection of get...() functions for SAM_ACCOUNT.
+ ********************************************************************/
+
+uint16 pdb_get_acct_ctrl (const SAM_ACCOUNT *sampass)
+{
+ if (sampass)
+ return (sampass->private.acct_ctrl);
+ else
+ return (ACB_DISABLED);
+}
+
+time_t pdb_get_logon_time (const SAM_ACCOUNT *sampass)
+{
+ if (sampass)
+ return (sampass->private.logon_time);
+ else
+ return (0);
+}
+
+time_t pdb_get_logoff_time (const SAM_ACCOUNT *sampass)
+{
+ if (sampass)
+ return (sampass->private.logoff_time);
+ else
+ return (-1);
+}
+
+time_t pdb_get_kickoff_time (const SAM_ACCOUNT *sampass)
+{
+ if (sampass)
+ return (sampass->private.kickoff_time);
+ else
+ return (-1);
+}
+
+time_t pdb_get_bad_password_time (const SAM_ACCOUNT *sampass)
+{
+ if (sampass)
+ return (sampass->private.bad_password_time);
+ else
+ return (-1);
+}
+
+time_t pdb_get_pass_last_set_time (const SAM_ACCOUNT *sampass)
+{
+ if (sampass)
+ return (sampass->private.pass_last_set_time);
+ else
+ return (-1);
+}
+
+time_t pdb_get_pass_can_change_time (const SAM_ACCOUNT *sampass)
+{
+ if (sampass)
+ return (sampass->private.pass_can_change_time);
+ else
+ return (-1);
+}
+
+time_t pdb_get_pass_must_change_time (const SAM_ACCOUNT *sampass)
+{
+ if (sampass)
+ return (sampass->private.pass_must_change_time);
+ else
+ return (-1);
+}
+
+uint16 pdb_get_logon_divs (const SAM_ACCOUNT *sampass)
+{
+ if (sampass)
+ return (sampass->private.logon_divs);
+ else
+ return (-1);
+}
+
+uint32 pdb_get_hours_len (const SAM_ACCOUNT *sampass)
+{
+ if (sampass)
+ return (sampass->private.hours_len);
+ else
+ return (-1);
+}
+
+const uint8* pdb_get_hours (const SAM_ACCOUNT *sampass)
+{
+ if (sampass)
+ return (sampass->private.hours);
+ else
+ return (NULL);
+}
+
+const uint8* pdb_get_nt_passwd (const SAM_ACCOUNT *sampass)
+{
+ if (sampass) {
+ SMB_ASSERT((!sampass->private.nt_pw.data)
+ || sampass->private.nt_pw.length == NT_HASH_LEN);
+ return ((uint8*)sampass->private.nt_pw.data);
+ }
+ else
+ return (NULL);
+}
+
+const uint8* pdb_get_lanman_passwd (const SAM_ACCOUNT *sampass)
+{
+ if (sampass) {
+ SMB_ASSERT((!sampass->private.lm_pw.data)
+ || sampass->private.lm_pw.length == LM_HASH_LEN);
+ return ((uint8*)sampass->private.lm_pw.data);
+ }
+ else
+ return (NULL);
+}
+
+/* Return the plaintext password if known. Most of the time
+ it isn't, so don't assume anything magic about this function.
+
+ Used to pass the plaintext to passdb backends that might
+ want to store more than just the NTLM hashes.
+*/
+const char* pdb_get_plaintext_passwd (const SAM_ACCOUNT *sampass)
+{
+ if (sampass) {
+ return (sampass->private.plaintext_pw);
+ }
+ else
+ return (NULL);
+}
+const DOM_SID *pdb_get_user_sid(const SAM_ACCOUNT *sampass)
+{
+ if (sampass)
+ return &sampass->private.user_sid;
+ else
+ return (NULL);
+}
+
+const DOM_SID *pdb_get_group_sid(const SAM_ACCOUNT *sampass)
+{
+ if (sampass)
+ return &sampass->private.group_sid;
+ else
+ return (NULL);
+}
+
+/**
+ * Get flags showing what is initalised in the SAM_ACCOUNT
+ * @param sampass the SAM_ACCOUNT in question
+ * @return the flags indicating the members initialised in the struct.
+ **/
+
+enum pdb_value_state pdb_get_init_flags (const SAM_ACCOUNT *sampass, enum pdb_elements element)
+{
+ enum pdb_value_state ret = PDB_DEFAULT;
+
+ if (!sampass || !sampass->private.change_flags || !sampass->private.set_flags)
+ return ret;
+
+ if (bitmap_query(sampass->private.set_flags, element)) {
+ DEBUG(11, ("element %d: SET\n", element));
+ ret = PDB_SET;
+ }
+
+ if (bitmap_query(sampass->private.change_flags, element)) {
+ DEBUG(11, ("element %d: CHANGED\n", element));
+ ret = PDB_CHANGED;
+ }
+
+ if (ret == PDB_DEFAULT) {
+ DEBUG(11, ("element %d: DEFAULT\n", element));
+ }
+
+ return ret;
+}
+
+const char* pdb_get_username (const SAM_ACCOUNT *sampass)
+{
+ if (sampass)
+ return (sampass->private.username);
+ else
+ return (NULL);
+}
+
+const char* pdb_get_domain (const SAM_ACCOUNT *sampass)
+{
+ if (sampass)
+ return (sampass->private.domain);
+ else
+ return (NULL);
+}
+
+const char* pdb_get_nt_username (const SAM_ACCOUNT *sampass)
+{
+ if (sampass)
+ return (sampass->private.nt_username);
+ else
+ return (NULL);
+}
+
+const char* pdb_get_fullname (const SAM_ACCOUNT *sampass)
+{
+ if (sampass)
+ return (sampass->private.full_name);
+ else
+ return (NULL);
+}
+
+const char* pdb_get_homedir (const SAM_ACCOUNT *sampass)
+{
+ if (sampass)
+ return (sampass->private.home_dir);
+ else
+ return (NULL);
+}
+
+const char* pdb_get_unix_homedir (const SAM_ACCOUNT *sampass)
+{
+ if (sampass)
+ return (sampass->private.unix_home_dir);
+ else
+ return (NULL);
+}
+
+const char* pdb_get_dir_drive (const SAM_ACCOUNT *sampass)
+{
+ if (sampass)
+ return (sampass->private.dir_drive);
+ else
+ return (NULL);
+}
+
+const char* pdb_get_logon_script (const SAM_ACCOUNT *sampass)
+{
+ if (sampass)
+ return (sampass->private.logon_script);
+ else
+ return (NULL);
+}
+
+const char* pdb_get_profile_path (const SAM_ACCOUNT *sampass)
+{
+ if (sampass)
+ return (sampass->private.profile_path);
+ else
+ return (NULL);
+}
+
+const char* pdb_get_acct_desc (const SAM_ACCOUNT *sampass)
+{
+ if (sampass)
+ return (sampass->private.acct_desc);
+ else
+ return (NULL);
+}
+
+const char* pdb_get_workstations (const SAM_ACCOUNT *sampass)
+{
+ if (sampass)
+ return (sampass->private.workstations);
+ else
+ return (NULL);
+}
+
+const char* pdb_get_unknown_str (const SAM_ACCOUNT *sampass)
+{
+ if (sampass)
+ return (sampass->private.unknown_str);
+ else
+ return (NULL);
+}
+
+const char* pdb_get_munged_dial (const SAM_ACCOUNT *sampass)
+{
+ if (sampass)
+ return (sampass->private.munged_dial);
+ else
+ return (NULL);
+}
+
+uint16 pdb_get_bad_password_count(const SAM_ACCOUNT *sampass)
+{
+ if (sampass)
+ return (sampass->private.bad_password_count);
+ else
+ return 0;
+}
+
+uint16 pdb_get_logon_count(const SAM_ACCOUNT *sampass)
+{
+ if (sampass)
+ return (sampass->private.logon_count);
+ else
+ return 0;
+}
+
+uint32 pdb_get_unknown_6 (const SAM_ACCOUNT *sampass)
+{
+ if (sampass)
+ return (sampass->private.unknown_6);
+ else
+ return (-1);
+}
+
+void *pdb_get_backend_private_data (const SAM_ACCOUNT *sampass, const struct pdb_methods *my_methods)
+{
+ if (sampass && my_methods == sampass->private.backend_private_methods)
+ return sampass->private.backend_private_data;
+ else
+ return NULL;
+}
+
+/*********************************************************************
+ Collection of set...() functions for SAM_ACCOUNT.
+ ********************************************************************/
+
+BOOL pdb_set_acct_ctrl (SAM_ACCOUNT *sampass, uint16 acct_ctrl, enum pdb_value_state flag)
+{
+ if (!sampass)
+ return False;
+
+ sampass->private.acct_ctrl = acct_ctrl;
+
+ return pdb_set_init_flags(sampass, PDB_ACCTCTRL, flag);
+}
+
+BOOL pdb_set_logon_time (SAM_ACCOUNT *sampass, time_t mytime, enum pdb_value_state flag)
+{
+ if (!sampass)
+ return False;
+
+ sampass->private.logon_time = mytime;
+
+ return pdb_set_init_flags(sampass, PDB_LOGONTIME, flag);
+}
+
+BOOL pdb_set_logoff_time (SAM_ACCOUNT *sampass, time_t mytime, enum pdb_value_state flag)
+{
+ if (!sampass)
+ return False;
+
+ sampass->private.logoff_time = mytime;
+
+ return pdb_set_init_flags(sampass, PDB_LOGOFFTIME, flag);
+}
+
+BOOL pdb_set_kickoff_time (SAM_ACCOUNT *sampass, time_t mytime, enum pdb_value_state flag)
+{
+ if (!sampass)
+ return False;
+
+ sampass->private.kickoff_time = mytime;
+
+ return pdb_set_init_flags(sampass, PDB_KICKOFFTIME, flag);
+}
+
+BOOL pdb_set_bad_password_time (SAM_ACCOUNT *sampass, time_t mytime,
+ enum pdb_value_state flag)
+{
+ if (!sampass)
+ return False;
+
+ sampass->private.bad_password_time = mytime;
+
+ return pdb_set_init_flags(sampass, PDB_BAD_PASSWORD_TIME, flag);
+}
+
+BOOL pdb_set_pass_can_change_time (SAM_ACCOUNT *sampass, time_t mytime, enum pdb_value_state flag)
+{
+ if (!sampass)
+ return False;
+
+ sampass->private.pass_can_change_time = mytime;
+
+ return pdb_set_init_flags(sampass, PDB_CANCHANGETIME, flag);
+}
+
+BOOL pdb_set_pass_must_change_time (SAM_ACCOUNT *sampass, time_t mytime, enum pdb_value_state flag)
+{
+ if (!sampass)
+ return False;
+
+ sampass->private.pass_must_change_time = mytime;
+
+ return pdb_set_init_flags(sampass, PDB_MUSTCHANGETIME, flag);
+}
+
+BOOL pdb_set_pass_last_set_time (SAM_ACCOUNT *sampass, time_t mytime, enum pdb_value_state flag)
+{
+ if (!sampass)
+ return False;
+
+ sampass->private.pass_last_set_time = mytime;
+
+ return pdb_set_init_flags(sampass, PDB_PASSLASTSET, flag);
+}
+
+BOOL pdb_set_hours_len (SAM_ACCOUNT *sampass, uint32 len, enum pdb_value_state flag)
+{
+ if (!sampass)
+ return False;
+
+ sampass->private.hours_len = len;
+
+ return pdb_set_init_flags(sampass, PDB_HOURSLEN, flag);
+}
+
+BOOL pdb_set_logon_divs (SAM_ACCOUNT *sampass, uint16 hours, enum pdb_value_state flag)
+{
+ if (!sampass)
+ return False;
+
+ sampass->private.logon_divs = hours;
+
+ return pdb_set_init_flags(sampass, PDB_LOGONDIVS, flag);
+}
+
+/**
+ * Set flags showing what is initalised in the SAM_ACCOUNT
+ * @param sampass the SAM_ACCOUNT in question
+ * @param flag The *new* flag to be set. Old flags preserved
+ * this flag is only added.
+ **/
+
+BOOL pdb_set_init_flags (SAM_ACCOUNT *sampass, enum pdb_elements element, enum pdb_value_state value_flag)
+{
+ if (!sampass || !sampass->mem_ctx)
+ return False;
+
+ if (!sampass->private.set_flags) {
+ if ((sampass->private.set_flags =
+ bitmap_talloc(sampass->mem_ctx,
+ PDB_COUNT))==NULL) {
+ DEBUG(0,("bitmap_talloc failed\n"));
+ return False;
+ }
+ }
+ if (!sampass->private.change_flags) {
+ if ((sampass->private.change_flags =
+ bitmap_talloc(sampass->mem_ctx,
+ PDB_COUNT))==NULL) {
+ DEBUG(0,("bitmap_talloc failed\n"));
+ return False;
+ }
+ }
+
+ switch(value_flag) {
+ case PDB_CHANGED:
+ if (!bitmap_set(sampass->private.change_flags, element)) {
+ DEBUG(0,("Can't set flag: %d in change_flags.\n",element));
+ return False;
+ }
+ if (!bitmap_set(sampass->private.set_flags, element)) {
+ DEBUG(0,("Can't set flag: %d in set_flags.\n",element));
+ return False;
+ }
+ DEBUG(11, ("element %d -> now CHANGED\n", element));
+ break;
+ case PDB_SET:
+ if (!bitmap_clear(sampass->private.change_flags, element)) {
+ DEBUG(0,("Can't set flag: %d in change_flags.\n",element));
+ return False;
+ }
+ if (!bitmap_set(sampass->private.set_flags, element)) {
+ DEBUG(0,("Can't set flag: %d in set_flags.\n",element));
+ return False;
+ }
+ DEBUG(10, ("element %d -> now SET\n", element));
+ break;
+ case PDB_DEFAULT:
+ default:
+ if (!bitmap_clear(sampass->private.change_flags, element)) {
+ DEBUG(0,("Can't set flag: %d in change_flags.\n",element));
+ return False;
+ }
+ if (!bitmap_clear(sampass->private.set_flags, element)) {
+ DEBUG(0,("Can't set flag: %d in set_flags.\n",element));
+ return False;
+ }
+ DEBUG(11, ("element %d -> now DEFAULT\n", element));
+ break;
+ }
+
+ return True;
+}
+
+BOOL pdb_set_user_sid (SAM_ACCOUNT *sampass, const DOM_SID *u_sid, enum pdb_value_state flag)
+{
+ if (!sampass || !u_sid)
+ return False;
+
+ sid_copy(&sampass->private.user_sid, u_sid);
+
+ DEBUG(10, ("pdb_set_user_sid: setting user sid %s\n",
+ sid_string_static(&sampass->private.user_sid)));
+
+ return pdb_set_init_flags(sampass, PDB_USERSID, flag);
+}
+
+BOOL pdb_set_user_sid_from_string (SAM_ACCOUNT *sampass, fstring u_sid, enum pdb_value_state flag)
+{
+ DOM_SID new_sid;
+
+ if (!sampass || !u_sid)
+ return False;
+
+ DEBUG(10, ("pdb_set_user_sid_from_string: setting user sid %s\n",
+ u_sid));
+
+ if (!string_to_sid(&new_sid, u_sid)) {
+ DEBUG(1, ("pdb_set_user_sid_from_string: %s isn't a valid SID!\n", u_sid));
+ return False;
+ }
+
+ if (!pdb_set_user_sid(sampass, &new_sid, flag)) {
+ DEBUG(1, ("pdb_set_user_sid_from_string: could not set sid %s on SAM_ACCOUNT!\n", u_sid));
+ return False;
+ }
+
+ return True;
+}
+
+BOOL pdb_set_group_sid (SAM_ACCOUNT *sampass, const DOM_SID *g_sid, enum pdb_value_state flag)
+{
+ if (!sampass || !g_sid)
+ return False;
+
+ sid_copy(&sampass->private.group_sid, g_sid);
+
+ DEBUG(10, ("pdb_set_group_sid: setting group sid %s\n",
+ sid_string_static(&sampass->private.group_sid)));
+
+ return pdb_set_init_flags(sampass, PDB_GROUPSID, flag);
+}
+
+BOOL pdb_set_group_sid_from_string (SAM_ACCOUNT *sampass, fstring g_sid, enum pdb_value_state flag)
+{
+ DOM_SID new_sid;
+ if (!sampass || !g_sid)
+ return False;
+
+ DEBUG(10, ("pdb_set_group_sid_from_string: setting group sid %s\n",
+ g_sid));
+
+ if (!string_to_sid(&new_sid, g_sid)) {
+ DEBUG(1, ("pdb_set_group_sid_from_string: %s isn't a valid SID!\n", g_sid));
+ return False;
+ }
+
+ if (!pdb_set_group_sid(sampass, &new_sid, flag)) {
+ DEBUG(1, ("pdb_set_group_sid_from_string: could not set sid %s on SAM_ACCOUNT!\n", g_sid));
+ return False;
+ }
+ return True;
+}
+
+/*********************************************************************
+ Set the user's UNIX name.
+ ********************************************************************/
+
+BOOL pdb_set_username(SAM_ACCOUNT *sampass, const char *username, enum pdb_value_state flag)
+{
+ if (!sampass)
+ return False;
+
+ if (username) {
+ DEBUG(10, ("pdb_set_username: setting username %s, was %s\n", username,
+ (sampass->private.username)?(sampass->private.username):"NULL"));
+
+ sampass->private.username = talloc_strdup(sampass->mem_ctx, username);
+
+ if (!sampass->private.username) {
+ DEBUG(0, ("pdb_set_username: talloc_strdup() failed!\n"));
+ return False;
+ }
+
+ } else {
+ sampass->private.username = PDB_NOT_QUITE_NULL;
+ }
+
+ return pdb_set_init_flags(sampass, PDB_USERNAME, flag);
+}
+
+/*********************************************************************
+ Set the domain name.
+ ********************************************************************/
+
+BOOL pdb_set_domain(SAM_ACCOUNT *sampass, const char *domain, enum pdb_value_state flag)
+{
+ if (!sampass)
+ return False;
+
+ if (domain) {
+ DEBUG(10, ("pdb_set_domain: setting domain %s, was %s\n", domain,
+ (sampass->private.domain)?(sampass->private.domain):"NULL"));
+
+ sampass->private.domain = talloc_strdup(sampass->mem_ctx, domain);
+
+ if (!sampass->private.domain) {
+ DEBUG(0, ("pdb_set_domain: talloc_strdup() failed!\n"));
+ return False;
+ }
+
+ } else {
+ sampass->private.domain = PDB_NOT_QUITE_NULL;
+ }
+
+ return pdb_set_init_flags(sampass, PDB_DOMAIN, flag);
+}
+
+/*********************************************************************
+ Set the user's NT name.
+ ********************************************************************/
+
+BOOL pdb_set_nt_username(SAM_ACCOUNT *sampass, const char *nt_username, enum pdb_value_state flag)
+{
+ if (!sampass)
+ return False;
+
+ if (nt_username) {
+ DEBUG(10, ("pdb_set_nt_username: setting nt username %s, was %s\n", nt_username,
+ (sampass->private.nt_username)?(sampass->private.nt_username):"NULL"));
+
+ sampass->private.nt_username = talloc_strdup(sampass->mem_ctx, nt_username);
+
+ if (!sampass->private.nt_username) {
+ DEBUG(0, ("pdb_set_nt_username: talloc_strdup() failed!\n"));
+ return False;
+ }
+
+ } else {
+ sampass->private.nt_username = PDB_NOT_QUITE_NULL;
+ }
+
+ return pdb_set_init_flags(sampass, PDB_NTUSERNAME, flag);
+}
+
+/*********************************************************************
+ Set the user's full name.
+ ********************************************************************/
+
+BOOL pdb_set_fullname(SAM_ACCOUNT *sampass, const char *full_name, enum pdb_value_state flag)
+{
+ if (!sampass)
+ return False;
+
+ if (full_name) {
+ DEBUG(10, ("pdb_set_full_name: setting full name %s, was %s\n", full_name,
+ (sampass->private.full_name)?(sampass->private.full_name):"NULL"));
+
+ sampass->private.full_name = talloc_strdup(sampass->mem_ctx, full_name);
+
+ if (!sampass->private.full_name) {
+ DEBUG(0, ("pdb_set_fullname: talloc_strdup() failed!\n"));
+ return False;
+ }
+
+ } else {
+ sampass->private.full_name = PDB_NOT_QUITE_NULL;
+ }
+
+ return pdb_set_init_flags(sampass, PDB_FULLNAME, flag);
+}
+
+/*********************************************************************
+ Set the user's logon script.
+ ********************************************************************/
+
+BOOL pdb_set_logon_script(SAM_ACCOUNT *sampass, const char *logon_script, enum pdb_value_state flag)
+{
+ if (!sampass)
+ return False;
+
+ if (logon_script) {
+ DEBUG(10, ("pdb_set_logon_script: setting logon script %s, was %s\n", logon_script,
+ (sampass->private.logon_script)?(sampass->private.logon_script):"NULL"));
+
+ sampass->private.logon_script = talloc_strdup(sampass->mem_ctx, logon_script);
+
+ if (!sampass->private.logon_script) {
+ DEBUG(0, ("pdb_set_logon_script: talloc_strdup() failed!\n"));
+ return False;
+ }
+
+ } else {
+ sampass->private.logon_script = PDB_NOT_QUITE_NULL;
+ }
+
+ return pdb_set_init_flags(sampass, PDB_LOGONSCRIPT, flag);
+}
+
+/*********************************************************************
+ Set the user's profile path.
+ ********************************************************************/
+
+BOOL pdb_set_profile_path (SAM_ACCOUNT *sampass, const char *profile_path, enum pdb_value_state flag)
+{
+ if (!sampass)
+ return False;
+
+ if (profile_path) {
+ DEBUG(10, ("pdb_set_profile_path: setting profile path %s, was %s\n", profile_path,
+ (sampass->private.profile_path)?(sampass->private.profile_path):"NULL"));
+
+ sampass->private.profile_path = talloc_strdup(sampass->mem_ctx, profile_path);
+
+ if (!sampass->private.profile_path) {
+ DEBUG(0, ("pdb_set_profile_path: talloc_strdup() failed!\n"));
+ return False;
+ }
+
+ } else {
+ sampass->private.profile_path = PDB_NOT_QUITE_NULL;
+ }
+
+ return pdb_set_init_flags(sampass, PDB_PROFILE, flag);
+}
+
+/*********************************************************************
+ Set the user's directory drive.
+ ********************************************************************/
+
+BOOL pdb_set_dir_drive (SAM_ACCOUNT *sampass, const char *dir_drive, enum pdb_value_state flag)
+{
+ if (!sampass)
+ return False;
+
+ if (dir_drive) {
+ DEBUG(10, ("pdb_set_dir_drive: setting dir drive %s, was %s\n", dir_drive,
+ (sampass->private.dir_drive)?(sampass->private.dir_drive):"NULL"));
+
+ sampass->private.dir_drive = talloc_strdup(sampass->mem_ctx, dir_drive);
+
+ if (!sampass->private.dir_drive) {
+ DEBUG(0, ("pdb_set_dir_drive: talloc_strdup() failed!\n"));
+ return False;
+ }
+
+ } else {
+ sampass->private.dir_drive = PDB_NOT_QUITE_NULL;
+ }
+
+ return pdb_set_init_flags(sampass, PDB_DRIVE, flag);
+}
+
+/*********************************************************************
+ Set the user's home directory.
+ ********************************************************************/
+
+BOOL pdb_set_homedir (SAM_ACCOUNT *sampass, const char *home_dir, enum pdb_value_state flag)
+{
+ if (!sampass)
+ return False;
+
+ if (home_dir) {
+ DEBUG(10, ("pdb_set_homedir: setting home dir %s, was %s\n", home_dir,
+ (sampass->private.home_dir)?(sampass->private.home_dir):"NULL"));
+
+ sampass->private.home_dir = talloc_strdup(sampass->mem_ctx, home_dir);
+
+ if (!sampass->private.home_dir) {
+ DEBUG(0, ("pdb_set_home_dir: talloc_strdup() failed!\n"));
+ return False;
+ }
+
+ } else {
+ sampass->private.home_dir = PDB_NOT_QUITE_NULL;
+ }
+
+ return pdb_set_init_flags(sampass, PDB_SMBHOME, flag);
+}
+
+/*********************************************************************
+ Set the user's unix home directory.
+ ********************************************************************/
+
+BOOL pdb_set_unix_homedir (SAM_ACCOUNT *sampass, const char *unix_home_dir, enum pdb_value_state flag)
+{
+ if (!sampass)
+ return False;
+
+ if (unix_home_dir) {
+ DEBUG(10, ("pdb_set_unix_homedir: setting home dir %s, was %s\n", unix_home_dir,
+ (sampass->private.unix_home_dir)?(sampass->private.unix_home_dir):"NULL"));
+
+ sampass->private.unix_home_dir = talloc_strdup(sampass->mem_ctx,
+ unix_home_dir);
+
+ if (!sampass->private.unix_home_dir) {
+ DEBUG(0, ("pdb_set_unix_home_dir: talloc_strdup() failed!\n"));
+ return False;
+ }
+
+ } else {
+ sampass->private.unix_home_dir = PDB_NOT_QUITE_NULL;
+ }
+
+ return pdb_set_init_flags(sampass, PDB_UNIXHOMEDIR, flag);
+}
+
+/*********************************************************************
+ Set the user's account description.
+ ********************************************************************/
+
+BOOL pdb_set_acct_desc (SAM_ACCOUNT *sampass, const char *acct_desc, enum pdb_value_state flag)
+{
+ if (!sampass)
+ return False;
+
+ if (acct_desc) {
+ sampass->private.acct_desc = talloc_strdup(sampass->mem_ctx, acct_desc);
+
+ if (!sampass->private.acct_desc) {
+ DEBUG(0, ("pdb_set_acct_desc: talloc_strdup() failed!\n"));
+ return False;
+ }
+
+ } else {
+ sampass->private.acct_desc = PDB_NOT_QUITE_NULL;
+ }
+
+ return pdb_set_init_flags(sampass, PDB_ACCTDESC, flag);
+}
+
+/*********************************************************************
+ Set the user's workstation allowed list.
+ ********************************************************************/
+
+BOOL pdb_set_workstations (SAM_ACCOUNT *sampass, const char *workstations, enum pdb_value_state flag)
+{
+ if (!sampass)
+ return False;
+
+ if (workstations) {
+ DEBUG(10, ("pdb_set_workstations: setting workstations %s, was %s\n", workstations,
+ (sampass->private.workstations)?(sampass->private.workstations):"NULL"));
+
+ sampass->private.workstations = talloc_strdup(sampass->mem_ctx, workstations);
+
+ if (!sampass->private.workstations) {
+ DEBUG(0, ("pdb_set_workstations: talloc_strdup() failed!\n"));
+ return False;
+ }
+
+ } else {
+ sampass->private.workstations = PDB_NOT_QUITE_NULL;
+ }
+
+ return pdb_set_init_flags(sampass, PDB_WORKSTATIONS, flag);
+}
+
+/*********************************************************************
+ Set the user's 'unknown_str', whatever the heck this actually is...
+ ********************************************************************/
+
+BOOL pdb_set_unknown_str (SAM_ACCOUNT *sampass, const char *unknown_str, enum pdb_value_state flag)
+{
+ if (!sampass)
+ return False;
+
+ if (unknown_str) {
+ sampass->private.unknown_str = talloc_strdup(sampass->mem_ctx, unknown_str);
+
+ if (!sampass->private.unknown_str) {
+ DEBUG(0, ("pdb_set_unknown_str: talloc_strdup() failed!\n"));
+ return False;
+ }
+
+ } else {
+ sampass->private.unknown_str = PDB_NOT_QUITE_NULL;
+ }
+
+ return pdb_set_init_flags(sampass, PDB_UNKNOWNSTR, flag);
+}
+
+/*********************************************************************
+ Set the user's dial string.
+ ********************************************************************/
+
+BOOL pdb_set_munged_dial (SAM_ACCOUNT *sampass, const char *munged_dial, enum pdb_value_state flag)
+{
+ if (!sampass)
+ return False;
+
+ if (munged_dial) {
+ sampass->private.munged_dial = talloc_strdup(sampass->mem_ctx, munged_dial);
+
+ if (!sampass->private.munged_dial) {
+ DEBUG(0, ("pdb_set_munged_dial: talloc_strdup() failed!\n"));
+ return False;
+ }
+
+ } else {
+ sampass->private.munged_dial = PDB_NOT_QUITE_NULL;
+ }
+
+ return pdb_set_init_flags(sampass, PDB_MUNGEDDIAL, flag);
+}
+
+/*********************************************************************
+ Set the user's NT hash.
+ ********************************************************************/
+
+BOOL pdb_set_nt_passwd (SAM_ACCOUNT *sampass, const uint8 pwd[NT_HASH_LEN], enum pdb_value_state flag)
+{
+ if (!sampass)
+ return False;
+
+ data_blob_clear_free(&sampass->private.nt_pw);
+
+ if (pwd) {
+ sampass->private.nt_pw = data_blob(pwd, NT_HASH_LEN);
+ } else {
+ sampass->private.nt_pw = data_blob(NULL, 0);
+ }
+
+ return pdb_set_init_flags(sampass, PDB_NTPASSWD, flag);
+}
+
+/*********************************************************************
+ Set the user's LM hash.
+ ********************************************************************/
+
+BOOL pdb_set_lanman_passwd (SAM_ACCOUNT *sampass, const uint8 pwd[LM_HASH_LEN], enum pdb_value_state flag)
+{
+ if (!sampass)
+ return False;
+
+ data_blob_clear_free(&sampass->private.lm_pw);
+
+ if (pwd) {
+ sampass->private.lm_pw = data_blob(pwd, LM_HASH_LEN);
+ } else {
+ sampass->private.lm_pw = data_blob(NULL, 0);
+ }
+
+ return pdb_set_init_flags(sampass, PDB_LMPASSWD, flag);
+}
+
+/*********************************************************************
+ Set the user's plaintext password only (base procedure, see helper
+ below)
+ ********************************************************************/
+
+BOOL pdb_set_plaintext_pw_only (SAM_ACCOUNT *sampass, const char *password, enum pdb_value_state flag)
+{
+ if (!sampass)
+ return False;
+
+ if (password) {
+ if (sampass->private.plaintext_pw!=NULL)
+ memset(sampass->private.plaintext_pw,'\0',strlen(sampass->private.plaintext_pw)+1);
+
+ sampass->private.plaintext_pw = talloc_strdup(sampass->mem_ctx, password);
+
+ if (!sampass->private.plaintext_pw) {
+ DEBUG(0, ("pdb_set_unknown_str: talloc_strdup() failed!\n"));
+ return False;
+ }
+
+ } else {
+ sampass->private.plaintext_pw = NULL;
+ }
+
+ return pdb_set_init_flags(sampass, PDB_PLAINTEXT_PW, flag);
+}
+
+BOOL pdb_set_bad_password_count(SAM_ACCOUNT *sampass, uint16 bad_password_count, enum pdb_value_state flag)
+{
+ if (!sampass)
+ return False;
+
+ sampass->private.bad_password_count = bad_password_count;
+
+ return pdb_set_init_flags(sampass, PDB_BAD_PASSWORD_COUNT, flag);
+}
+
+BOOL pdb_set_logon_count(SAM_ACCOUNT *sampass, uint16 logon_count, enum pdb_value_state flag)
+{
+ if (!sampass)
+ return False;
+
+ sampass->private.logon_count = logon_count;
+
+ return pdb_set_init_flags(sampass, PDB_LOGON_COUNT, flag);
+}
+
+BOOL pdb_set_unknown_6 (SAM_ACCOUNT *sampass, uint32 unkn, enum pdb_value_state flag)
+{
+ if (!sampass)
+ return False;
+
+ sampass->private.unknown_6 = unkn;
+
+ return pdb_set_init_flags(sampass, PDB_UNKNOWN6, flag);
+}
+
+BOOL pdb_set_hours (SAM_ACCOUNT *sampass, const uint8 *hours, enum pdb_value_state flag)
+{
+ if (!sampass)
+ return False;
+
+ if (!hours) {
+ memset ((char *)sampass->private.hours, 0, MAX_HOURS_LEN);
+ return True;
+ }
+
+ memcpy (sampass->private.hours, hours, MAX_HOURS_LEN);
+
+ return pdb_set_init_flags(sampass, PDB_HOURS, flag);
+}
+
+BOOL pdb_set_backend_private_data (SAM_ACCOUNT *sampass, void *private_data,
+ void (*free_fn)(void **),
+ const struct pdb_methods *my_methods,
+ enum pdb_value_state flag)
+{
+ if (!sampass)
+ return False;
+
+ if (sampass->private.backend_private_data && sampass->private.backend_private_data_free_fn) {
+ sampass->private.backend_private_data_free_fn(&sampass->private.backend_private_data);
+ }
+
+ sampass->private.backend_private_data = private_data;
+ sampass->private.backend_private_data_free_fn = free_fn;
+ sampass->private.backend_private_methods = my_methods;
+
+ return pdb_set_init_flags(sampass, PDB_BACKEND_PRIVATE_DATA, flag);
+}
+
+
+/* Helpful interfaces to the above */
+
+/*********************************************************************
+ Sets the last changed times and must change times for a normal
+ password change.
+ ********************************************************************/
+
+BOOL pdb_set_pass_changed_now (SAM_ACCOUNT *sampass)
+{
+ uint32 expire;
+ uint32 min_age;
+
+ if (!sampass)
+ return False;
+
+ if (!pdb_set_pass_last_set_time (sampass, time(NULL), PDB_CHANGED))
+ return False;
+
+ if (!account_policy_get(AP_MAX_PASSWORD_AGE, &expire)
+ || (expire==(uint32)-1) || (expire == 0)) {
+ if (!pdb_set_pass_must_change_time (sampass, get_time_t_max(), PDB_CHANGED))
+ return False;
+ } else {
+ if (!pdb_set_pass_must_change_time (sampass,
+ pdb_get_pass_last_set_time(sampass)
+ + expire, PDB_CHANGED))
+ return False;
+ }
+
+ if (!account_policy_get(AP_MIN_PASSWORD_AGE, &min_age)
+ || (min_age==(uint32)-1)) {
+ if (!pdb_set_pass_can_change_time (sampass, 0, PDB_CHANGED))
+ return False;
+ } else {
+ if (!pdb_set_pass_can_change_time (sampass,
+ pdb_get_pass_last_set_time(sampass)
+ + min_age, PDB_CHANGED))
+ return False;
+ }
+ return True;
+}
+
+/*********************************************************************
+ Set the user's PLAINTEXT password. Used as an interface to the above.
+ Also sets the last change time to NOW.
+ ********************************************************************/
+
+BOOL pdb_set_plaintext_passwd (SAM_ACCOUNT *sampass, const char *plaintext)
+{
+ uchar new_lanman_p16[16];
+ uchar new_nt_p16[16];
+
+ if (!sampass || !plaintext)
+ return False;
+
+ /* Calculate the MD4 hash (NT compatible) of the password */
+ E_md4hash(plaintext, new_nt_p16);
+
+ if (!pdb_set_nt_passwd (sampass, new_nt_p16, PDB_CHANGED))
+ return False;
+
+ if (!E_deshash(plaintext, new_lanman_p16)) {
+ /* E_deshash returns false for 'long' passwords (> 14
+ DOS chars). This allows us to match Win2k, which
+ does not store a LM hash for these passwords (which
+ would reduce the effective password length to 14 */
+
+ if (!pdb_set_lanman_passwd (sampass, NULL, PDB_CHANGED))
+ return False;
+ } else {
+ if (!pdb_set_lanman_passwd (sampass, new_lanman_p16, PDB_CHANGED))
+ return False;
+ }
+
+ if (!pdb_set_plaintext_pw_only (sampass, plaintext, PDB_CHANGED))
+ return False;
+
+ if (!pdb_set_pass_changed_now (sampass))
+ return False;
+
+ return True;
+}
+
+/* check for any PDB_SET/CHANGED field and fill the appropriate mask bit */
+uint32 pdb_build_fields_present (SAM_ACCOUNT *sampass)
+{
+ /* value set to all for testing */
+ return 0x00ffffff;
+}
+
diff --git a/source/passdb/pdb_guest.c b/source/passdb/pdb_guest.c
new file mode 100644
index 00000000000..8c1d4c7b0fe
--- /dev/null
+++ b/source/passdb/pdb_guest.c
@@ -0,0 +1,179 @@
+/*
+ * 'Guest' password backend for samba
+ * Copyright (C) Jelmer Vernooij 2002
+ * Copyright (C) Andrew Bartlett 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 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"
+
+/******************************************************************
+ Lookup a name in the SAM database
+ ******************************************************************/
+
+static NTSTATUS guestsam_getsampwnam (struct pdb_methods *methods, SAM_ACCOUNT *sam_account, const char *sname)
+{
+ const char *guest_account = lp_guestaccount();
+
+ if (!sam_account || !sname) {
+ DEBUG(0,("invalid name specified"));
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ if (!(guest_account && *guest_account)) {
+ DEBUG(1, ("NULL guest account!?!?\n"));
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ if (!methods) {
+ DEBUG(0,("invalid methods\n"));
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+ if (!strequal(guest_account, sname)) {
+ return NT_STATUS_NO_SUCH_USER;
+ }
+
+ pdb_fill_default_sam(sam_account);
+
+ if (!pdb_set_username(sam_account, guest_account, PDB_SET))
+ return NT_STATUS_UNSUCCESSFUL;
+
+ if (!pdb_set_fullname(sam_account, guest_account, PDB_SET))
+ return NT_STATUS_UNSUCCESSFUL;
+
+ if (!pdb_set_domain(sam_account, get_global_sam_name(), PDB_DEFAULT))
+ return NT_STATUS_UNSUCCESSFUL;
+
+ if (!pdb_set_acct_ctrl(sam_account, ACB_NORMAL, PDB_DEFAULT))
+ return NT_STATUS_UNSUCCESSFUL;
+
+ if (!pdb_set_user_sid_from_rid(sam_account, DOMAIN_USER_RID_GUEST, PDB_SET))
+ return NT_STATUS_UNSUCCESSFUL;
+
+ if (!pdb_set_group_sid_from_rid(sam_account, DOMAIN_GROUP_RID_GUESTS, PDB_DEFAULT))
+ return NT_STATUS_UNSUCCESSFUL;
+
+ return NT_STATUS_OK;
+}
+
+
+/***************************************************************************
+ Search by rid
+ **************************************************************************/
+
+static NTSTATUS guestsam_getsampwrid (struct pdb_methods *methods,
+ SAM_ACCOUNT *sam_account, uint32 rid)
+{
+ if (rid != DOMAIN_USER_RID_GUEST) {
+ return NT_STATUS_NO_SUCH_USER;
+ }
+
+ if (!sam_account) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ return guestsam_getsampwnam (methods, sam_account, lp_guestaccount());
+}
+
+static NTSTATUS guestsam_getsampwsid(struct pdb_methods *my_methods, SAM_ACCOUNT * user, const DOM_SID *sid)
+{
+ uint32 rid;
+ if (!sid_peek_check_rid(get_global_sam_sid(), sid, &rid))
+ return NT_STATUS_NO_SUCH_USER;
+
+ return guestsam_getsampwrid(my_methods, user, rid);
+}
+
+
+/***************************************************************************
+ Updates a SAM_ACCOUNT
+
+ This isn't a particulary practical option for pdb_guest. We certainly don't
+ want to twidde the filesystem, so what should we do?
+
+ Current plan is to transparently add the account. It should appear
+ as if the pdb_guest version was modified, but its actually stored somehwere.
+ ****************************************************************************/
+
+static NTSTATUS guestsam_update_sam_account (struct pdb_methods *methods, SAM_ACCOUNT *newpwd)
+{
+#if 1 /* JERRY */
+
+ /* apparently thr build farm relies upon this heavior :-( */
+
+ return methods->parent->pdb_add_sam_account(methods->parent, newpwd);
+#else
+ /* I don't think we should allow any modification of
+ the guest account as SID will could messed up with
+ the smbpasswd backend --jerry */
+
+ return NT_STATUS_NOT_IMPLEMENTED;
+#endif
+}
+
+NTSTATUS pdb_init_guestsam(PDB_CONTEXT *pdb_context, PDB_METHODS **pdb_method, const char *location)
+{
+ NTSTATUS nt_status;
+
+ if (!pdb_context) {
+ DEBUG(0, ("invalid pdb_context specified\n"));
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ if (!NT_STATUS_IS_OK(nt_status = make_pdb_methods(pdb_context->mem_ctx, pdb_method))) {
+ return nt_status;
+ }
+
+ (*pdb_method)->name = "guestsam";
+
+ (*pdb_method)->getsampwnam = guestsam_getsampwnam;
+ (*pdb_method)->getsampwsid = guestsam_getsampwsid;
+ (*pdb_method)->update_sam_account = guestsam_update_sam_account;
+
+ /* we should do no group mapping here */
+ (*pdb_method)->getgrsid = pdb_nop_getgrsid;
+ (*pdb_method)->getgrgid = pdb_nop_getgrgid;
+ (*pdb_method)->getgrnam = pdb_nop_getgrnam;
+ (*pdb_method)->add_group_mapping_entry = pdb_nop_add_group_mapping_entry;
+ (*pdb_method)->update_group_mapping_entry = pdb_nop_update_group_mapping_entry;
+ (*pdb_method)->delete_group_mapping_entry = pdb_nop_delete_group_mapping_entry;
+ (*pdb_method)->enum_group_mapping = pdb_nop_enum_group_mapping;
+
+ /* we do not handle groups in guest backend */
+/* FIXME
+ (*pdb_method)->get_group_info_by_sid = pdb_nop_get_group_info_by_sid;
+ (*pdb_method)->get_group_list = pdb_nop_get_group_list;
+ (*pdb_method)->get_group_sids = pdb_nop_get_group_sids;
+ (*pdb_method)->add_group = pdb_nop_add_group;
+ (*pdb_method)->update_group = pdb_nop_update_group;
+ (*pdb_method)->delete_group = pdb_nop_delete_group;
+ (*pdb_method)->add_sid_to_group = pdb_nop_add_sid_to_group;
+ (*pdb_method)->remove_sid_from_group = pdb_nop_remove_sid_from_group;
+ (*pdb_method)->get_group_info_by_name = pdb_nop_get_group_info_by_name;
+ (*pdb_method)->get_group_info_by_nt_name = pdb_nop_get_group_info_by_nt_name;
+ (*pdb_method)->get_group_uids = pdb_nop_get_group_uids;
+*/
+
+
+ /* There's not very much to initialise here */
+ return NT_STATUS_OK;
+}
+
+NTSTATUS pdb_guest_init(void)
+{
+ return smb_register_passdb(PASSDB_INTERFACE_VERSION, "guest", pdb_init_guestsam);
+}
+
diff --git a/source/passdb/pdb_gums.c b/source/passdb/pdb_gums.c
new file mode 100644
index 00000000000..f34d3a94b5a
--- /dev/null
+++ b/source/passdb/pdb_gums.c
@@ -0,0 +1,464 @@
+/*
+ * GUMS password backend for samba
+ * Copyright (C) Simo Sorce 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 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"
+
+#define SET_OR_FAIL(func, label) do { if (!NT_STATUS_IS_OK(func)) { DEBUG(0, ("%s: Setting gums object data failed!\n", FUNCTION_MACRO)); goto label; } } while(0)
+#define BOOL_SET_OR_FAIL(func, label) do { if (!func) { DEBUG(0, ("%s: Setting sam object data failed!\n", FUNCTION_MACRO)); goto label; } } while(0)
+
+struct gums_gw_data {
+ GUMS_FUNCTIONS *fns;
+ void *handle;
+};
+
+static NTSTATUS gums_object_to_sam_account(SAM_ACCOUNT *sa, GUMS_OBJECT *go)
+{
+ NTSTATUS ret;
+ NTTIME nt_time;
+ DATA_BLOB pwd;
+
+ if (!go || !sa)
+ return NT_STATUS_INVALID_PARAMETER;
+/*
+ if (!NT_STATUS_IS_OK(ret = pdb_init_sam(sa))) {
+ DEBUG(0, ("gums_object_to_sam_account: error occurred while creating sam_account object!\n"));
+ goto error;
+ }
+*/
+ if (gums_get_object_type(go) != GUMS_OBJ_NORMAL_USER)
+ return NT_STATUS_OBJECT_TYPE_MISMATCH;
+
+ BOOL_SET_OR_FAIL(pdb_set_acct_ctrl(sa, gums_get_user_acct_ctrl(go), PDB_SET), error);
+
+ /* domain */
+ /* unix_homedir ? */
+
+ nt_time = gums_get_user_logon_time(go);
+ BOOL_SET_OR_FAIL(pdb_set_logon_time(sa, nt_time_to_unix(&nt_time), PDB_SET), error);
+ nt_time = gums_get_user_logoff_time(go);
+ BOOL_SET_OR_FAIL(pdb_set_logoff_time(sa, nt_time_to_unix(&nt_time), PDB_SET), error);
+ nt_time = gums_get_user_kickoff_time(go);
+ BOOL_SET_OR_FAIL(pdb_set_kickoff_time(sa, nt_time_to_unix(&nt_time), PDB_SET), error);
+ nt_time = gums_get_user_pass_last_set_time(go);
+ BOOL_SET_OR_FAIL(pdb_set_pass_last_set_time(sa, nt_time_to_unix(&nt_time), PDB_SET), error);
+ nt_time = gums_get_user_pass_can_change_time(go);
+ BOOL_SET_OR_FAIL(pdb_set_pass_can_change_time(sa, nt_time_to_unix(&nt_time), PDB_SET), error);
+ nt_time = gums_get_user_pass_must_change_time(go);
+ BOOL_SET_OR_FAIL(pdb_set_pass_must_change_time(sa, nt_time_to_unix(&nt_time), PDB_SET), error);
+ BOOL_SET_OR_FAIL(pdb_set_hours_len(sa, gums_get_user_hours_len(go), PDB_SET), error);
+ BOOL_SET_OR_FAIL(pdb_set_logon_divs(sa, gums_get_user_logon_divs(go), PDB_SET), error);
+ BOOL_SET_OR_FAIL(pdb_set_user_sid(sa, gums_get_object_sid(go), PDB_SET), error);
+ BOOL_SET_OR_FAIL(pdb_set_group_sid(sa, gums_get_user_pri_group(go), PDB_SET), error);
+ BOOL_SET_OR_FAIL(pdb_set_username(sa, gums_get_object_name(go), PDB_SET), error);
+ BOOL_SET_OR_FAIL(pdb_set_nt_username(sa, gums_get_object_name(go), PDB_SET), error);
+ BOOL_SET_OR_FAIL(pdb_set_fullname(sa, gums_get_user_fullname(go), PDB_SET), error);
+ BOOL_SET_OR_FAIL(pdb_set_logon_script(sa, gums_get_user_logon_script(go), PDB_SET), error);
+ BOOL_SET_OR_FAIL(pdb_set_profile_path(sa, gums_get_user_profile_path(go), PDB_SET), error);
+ BOOL_SET_OR_FAIL(pdb_set_dir_drive(sa, gums_get_user_dir_drive(go), PDB_SET), error);
+ BOOL_SET_OR_FAIL(pdb_set_homedir(sa, gums_get_user_homedir(go), PDB_SET), error);
+ BOOL_SET_OR_FAIL(pdb_set_acct_desc(sa, gums_get_object_description(go), PDB_SET), error);
+ BOOL_SET_OR_FAIL(pdb_set_workstations(sa, gums_get_user_workstations(go), PDB_SET), error);
+ BOOL_SET_OR_FAIL(pdb_set_unknown_str(sa, gums_get_user_unknown_str(go), PDB_SET), error);
+ BOOL_SET_OR_FAIL(pdb_set_munged_dial(sa, gums_get_user_munged_dial(go), PDB_SET), error);
+
+ pwd = gums_get_user_nt_pwd(go);
+ if (!pdb_set_nt_passwd(sa, pwd.data, PDB_SET)) {
+ DEBUG(5, ("gums_object_to_sam_account: unable to set nt password"));
+ data_blob_clear_free(&pwd);
+ ret = NT_STATUS_UNSUCCESSFUL;
+ goto error;
+ }
+ data_blob_clear_free(&pwd);
+ pwd = gums_get_user_lm_pwd(go);
+ if (!pdb_set_lanman_passwd(sa, pwd.data, PDB_SET)) {
+ DEBUG(5, ("gums_object_to_sam_account: unable to set lanman password"));
+ data_blob_clear_free(&pwd);
+ ret = NT_STATUS_UNSUCCESSFUL;
+ goto error;
+ }
+ data_blob_clear_free(&pwd);
+
+ BOOL_SET_OR_FAIL(pdb_set_bad_password_count(sa, gums_get_user_bad_password_count(go), PDB_SET), error);
+ BOOL_SET_OR_FAIL(pdb_set_unknown_6(sa, gums_get_user_unknown_6(go), PDB_SET), error);
+ BOOL_SET_OR_FAIL(pdb_set_hours(sa, gums_get_user_hours(go), PDB_SET), error);
+
+ return NT_STATUS_OK;
+
+error:
+ if (sa && (sa->free_fn)) {
+ sa->free_fn(&sa);
+ }
+
+ return ret;
+}
+
+static NTSTATUS sam_account_to_gums_object(GUMS_OBJECT *go, SAM_ACCOUNT *sa)
+{
+ NTSTATUS ret;
+ NTTIME nt_time;
+ DATA_BLOB pwd;
+
+ if (!go || !sa)
+ return NT_STATUS_INVALID_PARAMETER;
+
+/*
+ ret = gums_create_object(go, GUMS_OBJ_NORMAL_USER);
+ if (!NT_STATUS_IS_OK(ret)) {
+ DEBUG(0, ("sam_account_to_gums_object: error occurred while creating gums object!\n"));
+ goto error;
+ }
+*/
+
+ /* sec_desc */
+
+ SET_OR_FAIL(gums_set_object_name(go, pdb_get_username(sa)), error);
+
+ SET_OR_FAIL(gums_set_object_sid(go, pdb_get_user_sid(sa)), error);
+ SET_OR_FAIL(gums_set_user_pri_group(go, pdb_get_group_sid(sa)), error);
+
+ if (pdb_get_acct_desc(sa))
+ SET_OR_FAIL(gums_set_object_description(go, pdb_get_acct_desc(sa)), error);
+ if (pdb_get_fullname(sa))
+ SET_OR_FAIL(gums_set_user_fullname(go, pdb_get_fullname(sa)), error);
+ if (pdb_get_homedir(sa))
+ SET_OR_FAIL(gums_set_user_homedir(go, pdb_get_homedir(sa)), error);
+ if (pdb_get_dir_drive(sa))
+ SET_OR_FAIL(gums_set_user_dir_drive(go, pdb_get_dir_drive(sa)), error);
+ if (pdb_get_logon_script(sa))
+ SET_OR_FAIL(gums_set_user_logon_script(go, pdb_get_logon_script(sa)), error);
+ if (pdb_get_profile_path(sa))
+ SET_OR_FAIL(gums_set_user_profile_path(go, pdb_get_profile_path(sa)), error);
+ if (pdb_get_workstations(sa))
+ SET_OR_FAIL(gums_set_user_workstations(go, pdb_get_workstations(sa)), error);
+ if (pdb_get_unknown_str(sa))
+ SET_OR_FAIL(gums_set_user_unknown_str(go, pdb_get_unknown_str(sa)), error);
+ if (pdb_get_munged_dial(sa))
+ SET_OR_FAIL(gums_set_user_munged_dial(go, pdb_get_munged_dial(sa)), error);
+ SET_OR_FAIL(gums_set_user_logon_divs(go, pdb_get_logon_divs(sa)), error);
+ if (pdb_get_hours(sa))
+ SET_OR_FAIL(gums_set_user_hours(go, pdb_get_hours_len(sa), pdb_get_hours(sa)), error);
+ SET_OR_FAIL(gums_set_user_bad_password_count(go, pdb_get_bad_password_count(sa)), error);
+ SET_OR_FAIL(gums_set_user_unknown_6(go, pdb_get_unknown_6(sa)), error);
+
+ unix_to_nt_time(&nt_time, pdb_get_logon_time(sa));
+ SET_OR_FAIL(gums_set_user_logon_time(go, nt_time), error);
+ unix_to_nt_time(&nt_time, pdb_get_logoff_time(sa));
+ SET_OR_FAIL(gums_set_user_logoff_time(go, nt_time), error);
+ unix_to_nt_time(&nt_time, pdb_get_kickoff_time(sa));
+ SET_OR_FAIL(gums_set_user_kickoff_time(go, nt_time), error);
+ unix_to_nt_time(&nt_time, pdb_get_pass_last_set_time(sa));
+ SET_OR_FAIL(gums_set_user_pass_last_set_time(go, nt_time), error);
+ unix_to_nt_time(&nt_time, pdb_get_pass_can_change_time(sa));
+ SET_OR_FAIL(gums_set_user_pass_can_change_time(go, nt_time), error);
+ unix_to_nt_time(&nt_time, pdb_get_pass_must_change_time(sa));
+ SET_OR_FAIL(gums_set_user_pass_must_change_time(go, nt_time), error);
+
+ pwd = data_blob(pdb_get_nt_passwd(sa), NT_HASH_LEN);
+ ret = gums_set_user_nt_pwd(go, pwd);
+ data_blob_clear_free(&pwd);
+ if (!NT_STATUS_IS_OK(ret)) {
+ DEBUG(5, ("sam_account_to_gums_object: failed to set nt password!\n"));
+ goto error;
+ }
+ pwd = data_blob(pdb_get_lanman_passwd(sa), LM_HASH_LEN);
+ ret = gums_set_user_lm_pwd(go, pwd);
+ data_blob_clear_free(&pwd);
+ if (!NT_STATUS_IS_OK(ret)) {
+ DEBUG(5, ("sam_account_to_gums_object: failed to set lanman password!\n"));
+ goto error;
+ }
+
+ SET_OR_FAIL(gums_set_user_acct_ctrl(go, pdb_get_acct_ctrl(sa)), error);
+
+ return NT_STATUS_OK;
+
+error:
+ gums_reset_object(go);
+ return ret;
+}
+
+static NTSTATUS gums_setsampwent(struct pdb_methods *methods, BOOL update)
+{
+ struct gums_gw_data *ggwd = (struct gums_gw_data *)(methods->private_data);
+
+ return ggwd->fns->enumerate_objects_start(&(ggwd->handle), NULL, GUMS_OBJ_NORMAL_USER);
+}
+
+static NTSTATUS gums_getsampwent(struct pdb_methods *methods, SAM_ACCOUNT *account)
+{
+ NTSTATUS ret;
+ GUMS_OBJECT *go;
+ struct gums_gw_data *ggwd = (struct gums_gw_data *)(methods->private_data);
+
+ if (!NT_STATUS_IS_OK(ret = ggwd->fns->enumerate_objects_get_next(&go, ggwd->handle))) {
+ return ret;
+ }
+
+ ret = gums_object_to_sam_account(account, go);
+
+ gums_destroy_object(&go);
+ return ret;
+}
+
+static void gums_endsampwent(struct pdb_methods *methods)
+{
+ struct gums_gw_data *ggwd = (struct gums_gw_data *)(methods->private_data);
+
+ ggwd->fns->enumerate_objects_stop(ggwd->handle);
+}
+
+/******************************************************************
+ Lookup a name in the SAM database
+ ******************************************************************/
+
+static NTSTATUS gums_getsampwnam (struct pdb_methods *methods, SAM_ACCOUNT *account, const char *name)
+{
+ NTSTATUS ret;
+ GUMS_OBJECT *go;
+ struct gums_gw_data *ggwd = (struct gums_gw_data *)(methods->private_data);
+
+ if (!account || !name)
+ return NT_STATUS_INVALID_PARAMETER;
+
+ if (!NT_STATUS_IS_OK(ret = ggwd->fns->get_object_from_name(&go, global_myname(), name, GUMS_OBJ_NORMAL_USER))) {
+ DEBUG(10, ("gums_getsampwnam: unable to find account with name %s", name));
+ return ret;
+ }
+
+ ret = gums_object_to_sam_account(account, go);
+
+ gums_destroy_object(&go);
+ return ret;
+}
+
+/***************************************************************************
+ Search by SID
+ **************************************************************************/
+
+static NTSTATUS gums_getsampwsid(struct pdb_methods *methods, SAM_ACCOUNT *account, const DOM_SID *sid)
+{
+ NTSTATUS ret;
+ GUMS_OBJECT *go;
+ struct gums_gw_data *ggwd = (struct gums_gw_data *)(methods->private_data);
+
+ if (!account || !sid)
+ return NT_STATUS_INVALID_PARAMETER;
+
+ if (!NT_STATUS_IS_OK(ret = ggwd->fns->get_object_from_sid(&go, sid, GUMS_OBJ_NORMAL_USER))) {
+ DEBUG(10, ("gums_getsampwsid: unable to find account with sid %s", sid_string_static(sid)));
+ return ret;
+ }
+
+ ret = gums_object_to_sam_account(account, go);
+
+ gums_destroy_object(&go);
+ return ret;
+}
+
+/***************************************************************************
+ Search by rid
+ **************************************************************************/
+
+#if 0
+
+static NTSTATUS gums_getsampwrid (struct pdb_methods *methods,
+ SAM_ACCOUNT *account, uint32 rid)
+{
+ DOM_SID sid;
+
+ sid_copy(&sid, get_global_sam_sid());
+ sid_append_rid(&sid, rid);
+ gums_getsampwsid(methods, account, &sid);
+
+ return NT_STATUS_OK;
+}
+
+#endif
+
+/***************************************************************************
+ Updates a SAM_ACCOUNT
+
+ This isn't a particulary practical option for pdb_guest. We certainly don't
+ want to twidde the filesystem, so what should we do?
+
+ Current plan is to transparently add the account. It should appear
+ as if the pdb_guest version was modified, but its actually stored somehwere.
+ ****************************************************************************/
+
+static NTSTATUS gums_add_sam_account (struct pdb_methods *methods, SAM_ACCOUNT *account)
+{
+ NTSTATUS ret;
+ GUMS_OBJECT *go;
+ struct gums_gw_data *ggwd = (struct gums_gw_data *)(methods->private_data);
+
+ if (!account)
+ return NT_STATUS_INVALID_PARAMETER;
+
+ if (!NT_STATUS_IS_OK(ret = gums_create_object(&go, GUMS_OBJ_NORMAL_USER))) {
+ DEBUG(0, ("gums_add_sam_account: error occurred while creating gums object!\n"));
+ return ret;
+ }
+
+ if (!NT_STATUS_IS_OK(ret = sam_account_to_gums_object(go, account))) {
+ DEBUG(0, ("gums_add_sam_account: error occurred while converting object!\n"));
+ goto done;
+ }
+
+ if (!NT_STATUS_IS_OK(ret = ggwd->fns->set_object(go))) {
+ DEBUG(0, ("gums_add_sam_account: unable to store account!\n"));
+ goto done;
+ }
+
+done:
+ gums_destroy_object(&go);
+ return ret;
+}
+
+static NTSTATUS gums_update_sam_account (struct pdb_methods *methods, SAM_ACCOUNT *account)
+{
+ NTSTATUS ret;
+ GUMS_OBJECT *go;
+ struct gums_gw_data *ggwd = (struct gums_gw_data *)(methods->private_data);
+
+ if (!account)
+ return NT_STATUS_INVALID_PARAMETER;
+
+ if (!NT_STATUS_IS_OK(ret = ggwd->fns->get_object_from_sid(&go, pdb_get_user_sid(account), GUMS_OBJ_NORMAL_USER))) {
+ DEBUG(0, ("gums_update_sam_account: update on invalid account!\n"));
+ return ret;
+ }
+
+ if (!NT_STATUS_IS_OK(ret = sam_account_to_gums_object(go, account))) {
+ DEBUG(0, ("gums_update_sam_account: error occurred while converting object!\n"));
+ goto done;
+ }
+
+ if (!NT_STATUS_IS_OK(ret = ggwd->fns->set_object(go))) {
+ DEBUG(0, ("gums_update_sam_account: unable to store account!\n"));
+ goto done;
+ }
+
+done:
+ gums_destroy_object(&go);
+ return ret;
+}
+
+static NTSTATUS gums_delete_sam_account (struct pdb_methods *methods, SAM_ACCOUNT *account)
+{
+ NTSTATUS ret;
+ struct gums_gw_data *ggwd = (struct gums_gw_data *)(methods->private_data);
+
+ if (!account)
+ return NT_STATUS_INVALID_PARAMETER;
+
+ if (!NT_STATUS_IS_OK(ret = ggwd->fns->delete_object(pdb_get_user_sid(account)))) {
+ DEBUG(0, ("gums_add_sam_account: unable to store account!\n"));
+ }
+
+ return ret;
+}
+
+
+static void free_gw_private_data(void **vp)
+{
+ struct gums_gw_data *ggwd = (struct gums_gw_data *)vp;
+ ggwd->fns->free_private_data(&(ggwd->fns->private_data));
+ ggwd->fns = NULL;
+ ggwd->handle = NULL;
+ SAFE_FREE(vp);
+}
+
+NTSTATUS pdb_init_gums_gateway(PDB_CONTEXT *pdb_context, PDB_METHODS **pdb_method, const char *location)
+{
+ NTSTATUS ret;
+ struct gums_gw_data *ggwd;
+
+ if (!pdb_context) {
+ DEBUG(0, ("invalid pdb_context specified\n"));
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ if (!NT_STATUS_IS_OK(ret = gums_setup_backend(lp_gums_backend()))) {
+ DEBUG(0, ("pdb_init_gums_gateway: initialization error!\n"));
+ return ret;
+ }
+
+ ggwd = (struct gums_gw_data *)malloc(sizeof(struct gums_gw_data));
+ if (!ggwd)
+ return NT_STATUS_NO_MEMORY;
+ memset(ggwd, 0, sizeof(struct gums_gw_data));
+
+ if (!NT_STATUS_IS_OK(ret = get_gums_fns(&(ggwd->fns)))) {
+ goto error;
+ }
+
+ if (!NT_STATUS_IS_OK(ret = make_pdb_methods(pdb_context->mem_ctx, pdb_method))) {
+ goto error;
+ }
+
+ (*pdb_method)->name = "gums_gateway";
+
+ (*pdb_method)->setsampwent = gums_setsampwent;
+ (*pdb_method)->getsampwent = gums_getsampwent;
+ (*pdb_method)->endsampwent = gums_endsampwent;
+ (*pdb_method)->getsampwnam = gums_getsampwnam;
+ (*pdb_method)->getsampwsid = gums_getsampwsid;
+ (*pdb_method)->add_sam_account = gums_add_sam_account;
+ (*pdb_method)->update_sam_account = gums_update_sam_account;
+ (*pdb_method)->delete_sam_account = gums_delete_sam_account;
+
+ /* we should do no group mapping here */
+/* (*pdb_method)->getgrsid = gums_getgrsid;
+ (*pdb_method)->getgrgid = gums_getgrgid;
+ (*pdb_method)->getgrnam = gums_getgrnam;
+ (*pdb_method)->add_group_mapping_entry = gums_add_group_mapping_entry;
+ (*pdb_method)->update_group_mapping_entry = gums_update_group_mapping_entry;
+ (*pdb_method)->delete_group_mapping_entry = gums_delete_group_mapping_entry;
+ (*pdb_method)->enum_group_mapping = gums_enum_group_mapping;*/
+
+ /* we do not handle groups in guest backend */
+/* FIXME
+ (*pdb_method)->get_group_info_by_sid = gums_get_group_info_by_sid;
+ (*pdb_method)->get_group_list = gums_get_group_list;
+ (*pdb_method)->get_group_sids = gums_get_group_sids;
+ (*pdb_method)->add_group = gums_add_group;
+ (*pdb_method)->update_group = gums_update_group;
+ (*pdb_method)->delete_group = gums_delete_group;
+ (*pdb_method)->add_sid_to_group = gums_add_sid_to_group;
+ (*pdb_method)->remove_sid_from_group = gums_remove_sid_from_group;
+ (*pdb_method)->get_group_info_by_name = gums_get_group_info_by_name;
+ (*pdb_method)->get_group_info_by_nt_name = gums_get_group_info_by_nt_name;
+ (*pdb_method)->get_group_uids = gums_get_group_uids;
+*/
+
+ (*pdb_method)->private_data = ggwd;
+ (*pdb_method)->free_private_data = free_gw_private_data;
+
+ return NT_STATUS_OK;
+
+error:
+ SAFE_FREE(ggwd);
+ return ret;
+}
+
+NTSTATUS pdb_gums_init(void)
+{
+ return smb_register_passdb(PASSDB_INTERFACE_VERSION, "gums", pdb_init_gums_gateway);
+}
+
diff --git a/source/passdb/pdb_interface.c b/source/passdb/pdb_interface.c
new file mode 100644
index 00000000000..b1620aa9eb6
--- /dev/null
+++ b/source/passdb/pdb_interface.c
@@ -0,0 +1,1601 @@
+/*
+ Unix SMB/CIFS implementation.
+ Password and authentication handling
+ Copyright (C) Andrew Bartlett 2002
+ Copyright (C) Jelmer Vernooij 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 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"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_PASSDB
+
+static struct pdb_init_function_entry *backends = NULL;
+
+static void lazy_initialize_passdb(void)
+{
+ static BOOL initialized = False;
+ if(initialized)return;
+ static_init_pdb;
+ initialized = True;
+}
+
+static struct pdb_init_function_entry *pdb_find_backend_entry(const char *name);
+
+/*******************************************************************
+ Clean up uninitialised passwords. The only way to tell
+ that these values are not 'real' is that they do not
+ have a valid last set time. Instead, the value is fixed at 0.
+ Therefore we use that as the key for 'is this a valid password'.
+ However, it is perfectly valid to have a 'default' last change
+ time, such LDAP with a missing attribute would produce.
+********************************************************************/
+
+static void pdb_force_pw_initialization(SAM_ACCOUNT *pass)
+{
+ const char *lm_pwd, *nt_pwd;
+
+ /* only reset a password if the last set time has been
+ explicitly been set to zero. A default last set time
+ is ignored */
+
+ if ( (pdb_get_init_flags(pass, PDB_PASSLASTSET) != PDB_DEFAULT)
+ && (pdb_get_pass_last_set_time(pass) == 0) )
+ {
+
+ if (pdb_get_init_flags(pass, PDB_LMPASSWD) != PDB_DEFAULT)
+ {
+ lm_pwd = pdb_get_lanman_passwd(pass);
+ if (lm_pwd)
+ pdb_set_lanman_passwd(pass, NULL, PDB_CHANGED);
+ }
+ if (pdb_get_init_flags(pass, PDB_NTPASSWD) != PDB_DEFAULT)
+ {
+ nt_pwd = pdb_get_nt_passwd(pass);
+ if (nt_pwd)
+ pdb_set_nt_passwd(pass, NULL, PDB_CHANGED);
+ }
+ }
+
+ return;
+}
+
+NTSTATUS smb_register_passdb(int version, const char *name, pdb_init_function init)
+{
+ struct pdb_init_function_entry *entry = backends;
+
+ if(version != PASSDB_INTERFACE_VERSION) {
+ DEBUG(0,("Can't register passdb backend!\n"
+ "You tried to register a passdb module with PASSDB_INTERFACE_VERSION %d, "
+ "while this version of samba uses version %d\n",
+ version,PASSDB_INTERFACE_VERSION));
+ return NT_STATUS_OBJECT_TYPE_MISMATCH;
+ }
+
+ if (!name || !init) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ DEBUG(5,("Attempting to register passdb backend %s\n", name));
+
+ /* Check for duplicates */
+ if (pdb_find_backend_entry(name)) {
+ DEBUG(0,("There already is a passdb backend registered with the name %s!\n", name));
+ return NT_STATUS_OBJECT_NAME_COLLISION;
+ }
+
+ entry = smb_xmalloc(sizeof(struct pdb_init_function_entry));
+ entry->name = smb_xstrdup(name);
+ entry->init = init;
+
+ DLIST_ADD(backends, entry);
+ DEBUG(5,("Successfully added passdb backend '%s'\n", name));
+ return NT_STATUS_OK;
+}
+
+static struct pdb_init_function_entry *pdb_find_backend_entry(const char *name)
+{
+ struct pdb_init_function_entry *entry = backends;
+
+ while(entry) {
+ if (strcmp(entry->name, name)==0) return entry;
+ entry = entry->next;
+ }
+
+ return NULL;
+}
+
+static NTSTATUS context_setsampwent(struct pdb_context *context, BOOL update)
+{
+ NTSTATUS ret = NT_STATUS_UNSUCCESSFUL;
+
+ if (!context) {
+ DEBUG(0, ("invalid pdb_context specified!\n"));
+ return ret;
+ }
+
+ context->pwent_methods = context->pdb_methods;
+
+ if (!context->pwent_methods) {
+ /* No passdbs at all */
+ return ret;
+ }
+
+ while (NT_STATUS_IS_ERR(ret = context->pwent_methods->setsampwent(context->pwent_methods, update))) {
+ context->pwent_methods = context->pwent_methods->next;
+ if (context->pwent_methods == NULL)
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+ return ret;
+}
+
+static void context_endsampwent(struct pdb_context *context)
+{
+ if ((!context)){
+ DEBUG(0, ("invalid pdb_context specified!\n"));
+ return;
+ }
+
+ if (context->pwent_methods && context->pwent_methods->endsampwent)
+ context->pwent_methods->endsampwent(context->pwent_methods);
+
+ /* So we won't get strange data when calling getsampwent now */
+ context->pwent_methods = NULL;
+}
+
+static NTSTATUS context_getsampwent(struct pdb_context *context, SAM_ACCOUNT *user)
+{
+ NTSTATUS ret = NT_STATUS_UNSUCCESSFUL;
+
+ if ((!context) || (!context->pwent_methods)) {
+ DEBUG(0, ("invalid pdb_context specified!\n"));
+ return ret;
+ }
+ /* Loop until we find something useful */
+ while (NT_STATUS_IS_ERR(ret = context->pwent_methods->getsampwent(context->pwent_methods, user))) {
+
+ context->pwent_methods->endsampwent(context->pwent_methods);
+
+ context->pwent_methods = context->pwent_methods->next;
+
+ /* All methods are checked now. There are no more entries */
+ if (context->pwent_methods == NULL)
+ return ret;
+
+ context->pwent_methods->setsampwent(context->pwent_methods, False);
+ }
+ user->methods = context->pwent_methods;
+ pdb_force_pw_initialization(user);
+ return ret;
+}
+
+static NTSTATUS context_getsampwnam(struct pdb_context *context, SAM_ACCOUNT *sam_acct, const char *username)
+{
+ NTSTATUS ret = NT_STATUS_UNSUCCESSFUL;
+
+ struct pdb_methods *curmethods;
+ if ((!context)) {
+ DEBUG(0, ("invalid pdb_context specified!\n"));
+ return ret;
+ }
+ curmethods = context->pdb_methods;
+ while (curmethods){
+ if (NT_STATUS_IS_OK(ret = curmethods->getsampwnam(curmethods, sam_acct, username))) {
+ pdb_force_pw_initialization(sam_acct);
+ sam_acct->methods = curmethods;
+ return ret;
+ }
+ curmethods = curmethods->next;
+ }
+
+ return ret;
+}
+
+static NTSTATUS context_getsampwsid(struct pdb_context *context, SAM_ACCOUNT *sam_acct, const DOM_SID *sid)
+{
+ NTSTATUS ret = NT_STATUS_UNSUCCESSFUL;
+
+ struct pdb_methods *curmethods;
+ if ((!context)) {
+ DEBUG(0, ("invalid pdb_context specified!\n"));
+ return ret;
+ }
+
+ curmethods = context->pdb_methods;
+
+ while (curmethods){
+ if (NT_STATUS_IS_OK(ret = curmethods->getsampwsid(curmethods, sam_acct, sid))) {
+ pdb_force_pw_initialization(sam_acct);
+ sam_acct->methods = curmethods;
+ return ret;
+ }
+ curmethods = curmethods->next;
+ }
+
+ return ret;
+}
+
+static NTSTATUS context_add_sam_account(struct pdb_context *context, SAM_ACCOUNT *sam_acct)
+{
+ NTSTATUS ret = NT_STATUS_UNSUCCESSFUL;
+
+ if ((!context) || (!context->pdb_methods)) {
+ DEBUG(0, ("invalid pdb_context specified!\n"));
+ return ret;
+ }
+
+ /** @todo This is where a 're-read on add' should be done */
+ /* We now add a new account to the first database listed.
+ * Should we? */
+
+ return context->pdb_methods->add_sam_account(context->pdb_methods, sam_acct);
+}
+
+static NTSTATUS context_update_sam_account(struct pdb_context *context, SAM_ACCOUNT *sam_acct)
+{
+ NTSTATUS ret = NT_STATUS_UNSUCCESSFUL;
+
+ if (!context) {
+ DEBUG(0, ("invalid pdb_context specified!\n"));
+ return ret;
+ }
+
+ if (!sam_acct || !sam_acct->methods){
+ DEBUG(0, ("invalid sam_acct specified\n"));
+ return ret;
+ }
+
+ /** @todo This is where a 're-read on update' should be done */
+
+ return sam_acct->methods->update_sam_account(sam_acct->methods, sam_acct);
+}
+
+static NTSTATUS context_delete_sam_account(struct pdb_context *context, SAM_ACCOUNT *sam_acct)
+{
+ NTSTATUS ret = NT_STATUS_UNSUCCESSFUL;
+
+ struct pdb_methods *pdb_selected;
+ if (!context) {
+ DEBUG(0, ("invalid pdb_context specified!\n"));
+ return ret;
+ }
+
+ if (!sam_acct->methods){
+ pdb_selected = context->pdb_methods;
+ /* There's no passdb backend specified for this account.
+ * Try to delete it in every passdb available
+ * Needed to delete accounts in smbpasswd that are not
+ * in /etc/passwd.
+ */
+ while (pdb_selected){
+ if (NT_STATUS_IS_OK(ret = pdb_selected->delete_sam_account(pdb_selected, sam_acct))) {
+ return ret;
+ }
+ pdb_selected = pdb_selected->next;
+ }
+ return ret;
+ }
+
+ if (!sam_acct->methods->delete_sam_account){
+ DEBUG(0,("invalid sam_acct->methods->delete_sam_account\n"));
+ return ret;
+ }
+
+ return sam_acct->methods->delete_sam_account(sam_acct->methods, sam_acct);
+}
+
+static NTSTATUS context_getgrsid(struct pdb_context *context,
+ GROUP_MAP *map, DOM_SID sid)
+{
+ NTSTATUS ret = NT_STATUS_UNSUCCESSFUL;
+
+ struct pdb_methods *curmethods;
+ if ((!context)) {
+ DEBUG(0, ("invalid pdb_context specified!\n"));
+ return ret;
+ }
+ curmethods = context->pdb_methods;
+ while (curmethods){
+ ret = curmethods->getgrsid(curmethods, map, sid);
+ if (NT_STATUS_IS_OK(ret)) {
+ map->methods = curmethods;
+ return ret;
+ }
+ curmethods = curmethods->next;
+ }
+
+ return ret;
+}
+
+static NTSTATUS context_getgrgid(struct pdb_context *context,
+ GROUP_MAP *map, gid_t gid)
+{
+ NTSTATUS ret = NT_STATUS_UNSUCCESSFUL;
+
+ struct pdb_methods *curmethods;
+ if ((!context)) {
+ DEBUG(0, ("invalid pdb_context specified!\n"));
+ return ret;
+ }
+ curmethods = context->pdb_methods;
+ while (curmethods){
+ ret = curmethods->getgrgid(curmethods, map, gid);
+ if (NT_STATUS_IS_OK(ret)) {
+ map->methods = curmethods;
+ return ret;
+ }
+ curmethods = curmethods->next;
+ }
+
+ return ret;
+}
+
+static NTSTATUS context_getgrnam(struct pdb_context *context,
+ GROUP_MAP *map, const char *name)
+{
+ NTSTATUS ret = NT_STATUS_UNSUCCESSFUL;
+
+ struct pdb_methods *curmethods;
+ if ((!context)) {
+ DEBUG(0, ("invalid pdb_context specified!\n"));
+ return ret;
+ }
+ curmethods = context->pdb_methods;
+ while (curmethods){
+ ret = curmethods->getgrnam(curmethods, map, name);
+ if (NT_STATUS_IS_OK(ret)) {
+ map->methods = curmethods;
+ return ret;
+ }
+ curmethods = curmethods->next;
+ }
+
+ return ret;
+}
+
+static NTSTATUS context_add_group_mapping_entry(struct pdb_context *context,
+ GROUP_MAP *map)
+{
+ NTSTATUS ret = NT_STATUS_UNSUCCESSFUL;
+
+ if ((!context) || (!context->pdb_methods)) {
+ DEBUG(0, ("invalid pdb_context specified!\n"));
+ return ret;
+ }
+
+ return context->pdb_methods->add_group_mapping_entry(context->pdb_methods,
+ map);
+}
+
+static NTSTATUS context_update_group_mapping_entry(struct pdb_context *context,
+ GROUP_MAP *map)
+{
+ NTSTATUS ret = NT_STATUS_UNSUCCESSFUL;
+
+ if ((!context) || (!context->pdb_methods)) {
+ DEBUG(0, ("invalid pdb_context specified!\n"));
+ return ret;
+ }
+
+ return context->
+ pdb_methods->update_group_mapping_entry(context->pdb_methods, map);
+}
+
+static NTSTATUS context_delete_group_mapping_entry(struct pdb_context *context,
+ DOM_SID sid)
+{
+ NTSTATUS ret = NT_STATUS_UNSUCCESSFUL;
+
+ if ((!context) || (!context->pdb_methods)) {
+ DEBUG(0, ("invalid pdb_context specified!\n"));
+ return ret;
+ }
+
+ return context->
+ pdb_methods->delete_group_mapping_entry(context->pdb_methods, sid);
+}
+
+static NTSTATUS context_enum_group_mapping(struct pdb_context *context,
+ enum SID_NAME_USE sid_name_use,
+ GROUP_MAP **rmap, int *num_entries,
+ BOOL unix_only)
+{
+ NTSTATUS ret = NT_STATUS_UNSUCCESSFUL;
+
+ if ((!context) || (!context->pdb_methods)) {
+ DEBUG(0, ("invalid pdb_context specified!\n"));
+ return ret;
+ }
+
+ return context->pdb_methods->enum_group_mapping(context->pdb_methods,
+ sid_name_use, rmap,
+ num_entries, unix_only);
+}
+
+static NTSTATUS context_find_alias(struct pdb_context *context,
+ const char *name, DOM_SID *sid)
+{
+ NTSTATUS ret = NT_STATUS_UNSUCCESSFUL;
+
+ if ((!context) || (!context->pdb_methods)) {
+ DEBUG(0, ("invalid pdb_context specified!\n"));
+ return ret;
+ }
+
+ return context->pdb_methods->find_alias(context->pdb_methods,
+ name, sid);
+}
+
+static NTSTATUS context_create_alias(struct pdb_context *context,
+ const char *name, uint32 *rid)
+{
+ NTSTATUS ret = NT_STATUS_UNSUCCESSFUL;
+
+ if ((!context) || (!context->pdb_methods)) {
+ DEBUG(0, ("invalid pdb_context specified!\n"));
+ return ret;
+ }
+
+ return context->pdb_methods->create_alias(context->pdb_methods,
+ name, rid);
+}
+
+static NTSTATUS context_delete_alias(struct pdb_context *context,
+ const DOM_SID *sid)
+{
+ NTSTATUS ret = NT_STATUS_UNSUCCESSFUL;
+
+ if ((!context) || (!context->pdb_methods)) {
+ DEBUG(0, ("invalid pdb_context specified!\n"));
+ return ret;
+ }
+
+ return context->pdb_methods->delete_alias(context->pdb_methods, sid);
+}
+
+static NTSTATUS context_enum_aliases(struct pdb_context *context,
+ const DOM_SID *sid,
+ uint32 start_idx, uint32 max_entries,
+ uint32 *num_aliases,
+ struct acct_info **info)
+{
+ NTSTATUS ret = NT_STATUS_UNSUCCESSFUL;
+
+ if ((!context) || (!context->pdb_methods)) {
+ DEBUG(0, ("invalid pdb_context specified!\n"));
+ return ret;
+ }
+
+ return context->pdb_methods->enum_aliases(context->pdb_methods,
+ sid, start_idx, max_entries,
+ num_aliases, info);
+}
+
+static NTSTATUS context_get_aliasinfo(struct pdb_context *context,
+ const DOM_SID *sid,
+ struct acct_info *info)
+{
+ NTSTATUS ret = NT_STATUS_UNSUCCESSFUL;
+
+ if ((!context) || (!context->pdb_methods)) {
+ DEBUG(0, ("invalid pdb_context specified!\n"));
+ return ret;
+ }
+
+ return context->pdb_methods->get_aliasinfo(context->pdb_methods,
+ sid, info);
+}
+
+static NTSTATUS context_set_aliasinfo(struct pdb_context *context,
+ const DOM_SID *sid,
+ struct acct_info *info)
+{
+ NTSTATUS ret = NT_STATUS_UNSUCCESSFUL;
+
+ if ((!context) || (!context->pdb_methods)) {
+ DEBUG(0, ("invalid pdb_context specified!\n"));
+ return ret;
+ }
+
+ return context->pdb_methods->set_aliasinfo(context->pdb_methods,
+ sid, info);
+}
+
+static NTSTATUS context_add_aliasmem(struct pdb_context *context,
+ const DOM_SID *alias,
+ const DOM_SID *member)
+{
+ NTSTATUS ret = NT_STATUS_UNSUCCESSFUL;
+
+ if ((!context) || (!context->pdb_methods)) {
+ DEBUG(0, ("invalid pdb_context specified!\n"));
+ return ret;
+ }
+
+ return context->pdb_methods->add_aliasmem(context->pdb_methods,
+ alias, member);
+}
+
+static NTSTATUS context_del_aliasmem(struct pdb_context *context,
+ const DOM_SID *alias,
+ const DOM_SID *member)
+{
+ NTSTATUS ret = NT_STATUS_UNSUCCESSFUL;
+
+ if ((!context) || (!context->pdb_methods)) {
+ DEBUG(0, ("invalid pdb_context specified!\n"));
+ return ret;
+ }
+
+ return context->pdb_methods->del_aliasmem(context->pdb_methods,
+ alias, member);
+}
+
+static NTSTATUS context_enum_aliasmem(struct pdb_context *context,
+ const DOM_SID *alias, DOM_SID **members,
+ int *num)
+{
+ NTSTATUS ret = NT_STATUS_UNSUCCESSFUL;
+
+ if ((!context) || (!context->pdb_methods)) {
+ DEBUG(0, ("invalid pdb_context specified!\n"));
+ return ret;
+ }
+
+ return context->pdb_methods->enum_aliasmem(context->pdb_methods,
+ alias, members, num);
+}
+
+static NTSTATUS context_enum_alias_memberships(struct pdb_context *context,
+ const DOM_SID *sid,
+ DOM_SID **aliases, int *num)
+{
+ NTSTATUS ret = NT_STATUS_UNSUCCESSFUL;
+
+ if ((!context) || (!context->pdb_methods)) {
+ DEBUG(0, ("invalid pdb_context specified!\n"));
+ return ret;
+ }
+
+ return context->pdb_methods->
+ enum_alias_memberships(context->pdb_methods, sid, aliases,
+ num);
+}
+
+static NTSTATUS context_settrustpwent(struct pdb_context *context)
+{
+ NTSTATUS ret = NT_STATUS_UNSUCCESSFUL;
+ struct pdb_methods *cur_methods;
+
+ if (!context) {
+ DEBUG(0, ("invalid pdb_context specified!\n"));
+ return ret;
+ }
+
+ cur_methods = context->pdb_methods;
+
+ while (cur_methods) {
+ ret = cur_methods->settrustpwent(cur_methods);
+ if (NT_STATUS_IS_OK(ret)) {
+ context->pdb_methods = cur_methods;
+ return ret;
+ }
+ cur_methods = cur_methods->next;
+ }
+
+ return ret;
+}
+
+static NTSTATUS context_gettrustpwent(struct pdb_context *context,
+ SAM_TRUST_PASSWD *trust)
+{
+ NTSTATUS ret = NT_STATUS_UNSUCCESSFUL;
+ struct pdb_methods *cur_methods;
+
+ if (!context) {
+ DEBUG(0, ("invalid pdb_context specified!\n"));
+ return ret;
+ }
+
+ cur_methods = context->pdb_methods;
+
+ while (cur_methods) {
+ ret = cur_methods->gettrustpwent(cur_methods, trust);
+ if (!NT_STATUS_IS_ERR(ret)) {
+ /* prevent from segfaulting when gettrustpwent
+ was called just to rewind enumeration */
+ if (trust) trust->methods = cur_methods;
+ return ret;
+ }
+ cur_methods = cur_methods->next;
+ }
+
+ return ret;
+}
+
+static NTSTATUS context_gettrustpwnam(struct pdb_context *context,
+ SAM_TRUST_PASSWD *trust,
+ const char *name)
+{
+ NTSTATUS ret = NT_STATUS_UNSUCCESSFUL;
+ struct pdb_methods *cur_methods;
+
+ if (!context) {
+ DEBUG(0, ("invalid pdb_context specified!\n"));
+ return ret;
+ }
+
+ cur_methods = context->pdb_methods;
+
+ while (cur_methods) {
+ ret = cur_methods->gettrustpwnam(cur_methods, trust, name);
+ if (NT_STATUS_IS_OK(ret)) {
+ trust->methods = cur_methods;
+ return ret;
+ }
+ cur_methods = cur_methods->next;
+ }
+
+ return ret;
+}
+
+static NTSTATUS context_gettrustpwsid(struct pdb_context *context,
+ SAM_TRUST_PASSWD *trust,
+ const DOM_SID *sid)
+{
+ NTSTATUS ret = NT_STATUS_UNSUCCESSFUL;
+ struct pdb_methods *cur_methods;
+
+ if (!context) {
+ DEBUG(0, ("invalid pdb_context specified!\n"));
+ return ret;
+ }
+
+ cur_methods = context->pdb_methods;
+
+ while (cur_methods) {
+ ret = cur_methods->gettrustpwsid(cur_methods, trust, sid);
+ if (NT_STATUS_IS_OK(ret)) {
+ trust->methods = cur_methods;
+ return ret;
+ }
+ cur_methods = cur_methods->next;
+ }
+
+ return ret;
+}
+
+static NTSTATUS context_add_trust_passwd(struct pdb_context *context,
+ SAM_TRUST_PASSWD *trust)
+{
+ NTSTATUS ret = NT_STATUS_UNSUCCESSFUL;
+
+ if (!context) {
+ DEBUG(0, ("invalid pdb_context specified!\n"));
+ return ret;
+ }
+
+ return context->pdb_methods->add_trust_passwd(context->pdb_methods, trust);
+}
+
+static NTSTATUS context_update_trust_passwd(struct pdb_context *context,
+ SAM_TRUST_PASSWD *trust)
+{
+ NTSTATUS ret = NT_STATUS_UNSUCCESSFUL;
+
+ if (!context) {
+ DEBUG(0, ("invalid pdb_context specified!\n"));
+ return ret;
+ }
+
+ if (!trust || !trust->methods) {
+ DEBUG(0, ("invalid trust pointer specified!\n"));
+ return ret;
+ }
+
+ return trust->methods->update_trust_passwd(trust->methods, trust);
+}
+
+static NTSTATUS context_delete_trust_passwd(struct pdb_context *context,
+ SAM_TRUST_PASSWD *trust)
+{
+ NTSTATUS ret = NT_STATUS_UNSUCCESSFUL;
+
+ if (!context) {
+ DEBUG(0, ("invalid pdb_context specified!\n"));
+ return ret;
+ }
+
+ if (!trust || !trust->methods) {
+ DEBUG(0, ("invalid trust pointer specified!\n"));
+ return ret;
+ }
+
+ return trust->methods->delete_trust_passwd(trust->methods, trust);
+}
+
+static NTSTATUS context_add_sid_to_privilege(struct pdb_context *context, const char *priv_name, const DOM_SID *sid)
+{
+ NTSTATUS ret = NT_STATUS_UNSUCCESSFUL;
+
+ struct pdb_methods *curmethods;
+ if ((!context)) {
+ DEBUG(0, ("invalid pdb_context specified!\n"));
+ return ret;
+ }
+ curmethods = context->pdb_methods;
+ while (curmethods){
+ if (NT_STATUS_IS_OK(ret = curmethods->add_sid_to_privilege(curmethods, priv_name, sid))) {
+ return ret;
+ }
+ curmethods = curmethods->next;
+ }
+
+ return ret;
+}
+
+static NTSTATUS context_remove_sid_from_privilege(struct pdb_context *context, const char *priv_name, const DOM_SID *sid)
+{
+ NTSTATUS ret = NT_STATUS_UNSUCCESSFUL;
+
+ struct pdb_methods *curmethods;
+ if ((!context)) {
+ DEBUG(0, ("invalid pdb_context specified!\n"));
+ return ret;
+ }
+ curmethods = context->pdb_methods;
+ while (curmethods){
+ if (NT_STATUS_IS_OK(ret = curmethods->remove_sid_from_privilege(curmethods, priv_name, sid))) {
+ return ret;
+ }
+ curmethods = curmethods->next;
+ }
+
+ return ret;
+}
+
+static NTSTATUS context_get_privilege_set(struct pdb_context *context, DOM_SID *sid_list, int num_sids, PRIVILEGE_SET *privset)
+{
+ NTSTATUS ret = NT_STATUS_UNSUCCESSFUL;
+
+ struct pdb_methods *curmethods;
+ if ((!context)) {
+ DEBUG(0, ("invalid pdb_context specified!\n"));
+ return ret;
+ }
+ curmethods = context->pdb_methods;
+ while (curmethods){
+ if (NT_STATUS_IS_OK(ret = curmethods->get_privilege_set(curmethods, sid_list, num_sids, privset))) {
+ return ret;
+ }
+ curmethods = curmethods->next;
+ }
+
+ return ret;
+}
+
+static NTSTATUS context_get_privilege_entry(struct pdb_context *context, const char *privname, char **sid_list)
+{
+ NTSTATUS ret = NT_STATUS_UNSUCCESSFUL;
+
+ struct pdb_methods *curmethods;
+ if ((!context)) {
+ DEBUG(0, ("invalid pdb_context specified!\n"));
+ return ret;
+ }
+ curmethods = context->pdb_methods;
+ while (curmethods){
+ if (NT_STATUS_IS_OK(ret = curmethods->get_privilege_entry(curmethods, privname, sid_list))) {
+ return ret;
+ }
+ curmethods = curmethods->next;
+ }
+
+ return ret;
+}
+
+/******************************************************************
+ Free and cleanup a pdb context, any associated data and anything
+ that the attached modules might have associated.
+ *******************************************************************/
+
+static void free_pdb_context(struct pdb_context **context)
+{
+ struct pdb_methods *pdb_selected = (*context)->pdb_methods;
+
+ while (pdb_selected){
+ if(pdb_selected->free_private_data)
+ pdb_selected->free_private_data(&(pdb_selected->private_data));
+ pdb_selected = pdb_selected->next;
+ }
+
+ talloc_destroy((*context)->mem_ctx);
+ *context = NULL;
+}
+
+/******************************************************************
+ Make a pdb_methods from scratch
+ *******************************************************************/
+
+static NTSTATUS make_pdb_methods_name(struct pdb_methods **methods, struct pdb_context *context, const char *selected)
+{
+ char *module_name = smb_xstrdup(selected);
+ char *module_location = NULL, *p;
+ struct pdb_init_function_entry *entry;
+ NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
+
+ lazy_initialize_passdb();
+
+ p = strchr(module_name, ':');
+
+ if (p) {
+ *p = 0;
+ module_location = p+1;
+ trim_char(module_location, ' ', ' ');
+ }
+
+ trim_char(module_name, ' ', ' ');
+
+
+ DEBUG(5,("Attempting to find an passdb backend to match %s (%s)\n", selected, module_name));
+
+ entry = pdb_find_backend_entry(module_name);
+
+ /* Try to find a module that contains this module */
+ if (!entry) {
+ DEBUG(2,("No builtin backend found, trying to load plugin\n"));
+ if(NT_STATUS_IS_OK(smb_probe_module("pdb", module_name)) && !(entry = pdb_find_backend_entry(module_name))) {
+ DEBUG(0,("Plugin is available, but doesn't register passdb backend %s\n", module_name));
+ SAFE_FREE(module_name);
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+ }
+
+ /* No such backend found */
+ if(!entry) {
+ DEBUG(0,("No builtin nor plugin backend for %s found\n", module_name));
+ SAFE_FREE(module_name);
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ DEBUG(5,("Found pdb backend %s\n", module_name));
+ nt_status = entry->init(context, methods, module_location);
+ if (NT_STATUS_IS_OK(nt_status)) {
+ DEBUG(5,("pdb backend %s has a valid init\n", selected));
+ } else {
+ DEBUG(0,("pdb backend %s did not correctly init (error was %s)\n", selected, nt_errstr(nt_status)));
+ }
+ SAFE_FREE(module_name);
+ return nt_status;
+}
+
+/******************************************************************
+ Make a pdb_context from scratch.
+ *******************************************************************/
+
+static NTSTATUS make_pdb_context(struct pdb_context **context)
+{
+ TALLOC_CTX *mem_ctx;
+
+ mem_ctx = talloc_init("pdb_context internal allocation context");
+
+ if (!mem_ctx) {
+ DEBUG(0, ("make_pdb_context: talloc init failed!\n"));
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ *context = talloc(mem_ctx, sizeof(**context));
+ if (!*context) {
+ DEBUG(0, ("make_pdb_context: talloc failed!\n"));
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ ZERO_STRUCTP(*context);
+
+ (*context)->mem_ctx = mem_ctx;
+
+ (*context)->pdb_setsampwent = context_setsampwent;
+ (*context)->pdb_endsampwent = context_endsampwent;
+ (*context)->pdb_getsampwent = context_getsampwent;
+ (*context)->pdb_getsampwnam = context_getsampwnam;
+ (*context)->pdb_getsampwsid = context_getsampwsid;
+ (*context)->pdb_add_sam_account = context_add_sam_account;
+ (*context)->pdb_update_sam_account = context_update_sam_account;
+ (*context)->pdb_delete_sam_account = context_delete_sam_account;
+ (*context)->pdb_getgrsid = context_getgrsid;
+ (*context)->pdb_getgrgid = context_getgrgid;
+ (*context)->pdb_getgrnam = context_getgrnam;
+ (*context)->pdb_add_group_mapping_entry = context_add_group_mapping_entry;
+ (*context)->pdb_update_group_mapping_entry = context_update_group_mapping_entry;
+ (*context)->pdb_delete_group_mapping_entry = context_delete_group_mapping_entry;
+ (*context)->pdb_enum_group_mapping = context_enum_group_mapping;
+ (*context)->pdb_find_alias = context_find_alias;
+ (*context)->pdb_create_alias = context_create_alias;
+ (*context)->pdb_delete_alias = context_delete_alias;
+ (*context)->pdb_enum_aliases = context_enum_aliases;
+ (*context)->pdb_get_aliasinfo = context_get_aliasinfo;
+ (*context)->pdb_set_aliasinfo = context_set_aliasinfo;
+ (*context)->pdb_add_aliasmem = context_add_aliasmem;
+ (*context)->pdb_del_aliasmem = context_del_aliasmem;
+ (*context)->pdb_enum_aliasmem = context_enum_aliasmem;
+ (*context)->pdb_enum_alias_memberships = context_enum_alias_memberships;
+ (*context)->pdb_settrustpwent = context_settrustpwent;
+ (*context)->pdb_gettrustpwent = context_gettrustpwent;
+ (*context)->pdb_gettrustpwnam = context_gettrustpwnam;
+ (*context)->pdb_gettrustpwsid = context_gettrustpwsid;
+ (*context)->pdb_add_trust_passwd = context_add_trust_passwd;
+ (*context)->pdb_update_trust_passwd = context_update_trust_passwd;
+ (*context)->pdb_delete_trust_passwd = context_delete_trust_passwd;
+ (*context)->pdb_add_sid_to_privilege = context_add_sid_to_privilege;
+ (*context)->pdb_remove_sid_from_privilege = context_remove_sid_from_privilege;
+ (*context)->pdb_get_privilege_set = context_get_privilege_set;
+ (*context)->pdb_get_privilege_entry = context_get_privilege_entry;
+
+ (*context)->free_fn = free_pdb_context;
+
+ return NT_STATUS_OK;
+}
+
+
+/******************************************************************
+ Make a pdb_context, given an array of strings
+ *******************************************************************/
+
+NTSTATUS make_pdb_context_list(struct pdb_context **context, const char **selected)
+{
+ int i = 0;
+ struct pdb_methods *curmethods, *tmpmethods;
+ NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
+ BOOL have_guest = False;
+
+ if (!NT_STATUS_IS_OK(nt_status = make_pdb_context(context))) {
+ return nt_status;
+ }
+
+ if (!selected) {
+ DEBUG(0, ("ERROR: empty passdb backend list!\n"));
+ return nt_status;
+ }
+
+ while (selected[i]){
+ if (strcmp(selected[i], "guest") == 0) {
+ have_guest = True;
+ }
+ /* Try to initialise pdb */
+ DEBUG(5,("Trying to load: %s\n", selected[i]));
+ if (!NT_STATUS_IS_OK(nt_status = make_pdb_methods_name(&curmethods, *context, selected[i]))) {
+ DEBUG(1, ("Loading %s failed!\n", selected[i]));
+ free_pdb_context(context);
+ return nt_status;
+ }
+ curmethods->parent = *context;
+ DLIST_ADD_END((*context)->pdb_methods, curmethods, tmpmethods);
+ i++;
+ }
+
+ if (have_guest)
+ return NT_STATUS_OK;
+
+ if ( (lp_guestaccount() == NULL) ||
+ (*lp_guestaccount() == '\0') ) {
+ /* We explicitly don't want guest access. No idea what
+ else that breaks, but be it that way. */
+ return NT_STATUS_OK;
+ }
+
+ if (!NT_STATUS_IS_OK(nt_status = make_pdb_methods_name(&curmethods,
+ *context,
+ "guest"))) {
+ DEBUG(1, ("Loading guest module failed!\n"));
+ free_pdb_context(context);
+ return nt_status;
+ }
+
+ curmethods->parent = *context;
+ DLIST_ADD_END((*context)->pdb_methods, curmethods, tmpmethods);
+
+ return NT_STATUS_OK;
+}
+
+/******************************************************************
+ Make a pdb_context, given a text string.
+ *******************************************************************/
+
+NTSTATUS make_pdb_context_string(struct pdb_context **context, const char *selected)
+{
+ NTSTATUS ret;
+ char **newsel = str_list_make(selected, NULL);
+ ret = make_pdb_context_list(context, (const char **)newsel);
+ str_list_free(&newsel);
+ return ret;
+}
+
+/******************************************************************
+ Return an already initialised pdb_context, to facilitate backward
+ compatibility (see functions below).
+*******************************************************************/
+
+static struct pdb_context *pdb_get_static_context(BOOL reload)
+{
+ static struct pdb_context *pdb_context = NULL;
+
+ if ((pdb_context) && (reload)) {
+ pdb_context->free_fn(&pdb_context);
+ if (!NT_STATUS_IS_OK(make_pdb_context_list(&pdb_context, lp_passdb_backend()))) {
+ return NULL;
+ }
+ }
+
+ if (!pdb_context) {
+ if (!NT_STATUS_IS_OK(make_pdb_context_list(&pdb_context, lp_passdb_backend()))) {
+ return NULL;
+ }
+ }
+
+ return pdb_context;
+}
+
+/******************************************************************
+ Backward compatibility functions for the original passdb interface
+*******************************************************************/
+
+BOOL pdb_setsampwent(BOOL update)
+{
+ struct pdb_context *pdb_context = pdb_get_static_context(False);
+
+ if (!pdb_context) {
+ return False;
+ }
+
+ return NT_STATUS_IS_OK(pdb_context->pdb_setsampwent(pdb_context, update));
+}
+
+void pdb_endsampwent(void)
+{
+ struct pdb_context *pdb_context = pdb_get_static_context(False);
+
+ if (!pdb_context) {
+ return;
+ }
+
+ pdb_context->pdb_endsampwent(pdb_context);
+}
+
+BOOL pdb_getsampwent(SAM_ACCOUNT *user)
+{
+ struct pdb_context *pdb_context = pdb_get_static_context(False);
+
+ if (!pdb_context) {
+ return False;
+ }
+
+ return NT_STATUS_IS_OK(pdb_context->pdb_getsampwent(pdb_context, user));
+}
+
+BOOL pdb_getsampwnam(SAM_ACCOUNT *sam_acct, const char *username)
+{
+ struct pdb_context *pdb_context = pdb_get_static_context(False);
+
+ if (!pdb_context) {
+ return False;
+ }
+
+ return NT_STATUS_IS_OK(pdb_context->pdb_getsampwnam(pdb_context, sam_acct, username));
+}
+
+BOOL pdb_getsampwsid(SAM_ACCOUNT *sam_acct, const DOM_SID *sid)
+{
+ struct pdb_context *pdb_context = pdb_get_static_context(False);
+
+ if (!pdb_context) {
+ return False;
+ }
+
+ return NT_STATUS_IS_OK(pdb_context->pdb_getsampwsid(pdb_context, sam_acct, sid));
+}
+
+BOOL pdb_add_sam_account(SAM_ACCOUNT *sam_acct)
+{
+ struct pdb_context *pdb_context = pdb_get_static_context(False);
+ const char *lm_pw, *nt_pw;
+ uint16 acb_flags;
+
+ if (!pdb_context) {
+ return False;
+ }
+
+ /* disable acccounts with no passwords (that has not
+ been allowed by the ACB_PWNOTREQ bit */
+
+ lm_pw = pdb_get_lanman_passwd( sam_acct );
+ nt_pw = pdb_get_nt_passwd( sam_acct );
+ acb_flags = pdb_get_acct_ctrl( sam_acct );
+ if ( !lm_pw && !nt_pw && !(acb_flags&ACB_PWNOTREQ) ) {
+ acb_flags |= ACB_DISABLED;
+ pdb_set_acct_ctrl( sam_acct, acb_flags, PDB_CHANGED );
+ }
+
+ return NT_STATUS_IS_OK(pdb_context->pdb_add_sam_account(pdb_context, sam_acct));
+}
+
+BOOL pdb_update_sam_account(SAM_ACCOUNT *sam_acct)
+{
+ struct pdb_context *pdb_context = pdb_get_static_context(False);
+ const char *lm_pw, *nt_pw;
+ uint16 acb_flags;
+
+ if (!pdb_context) {
+ return False;
+ }
+
+ /* disable acccounts with no passwords (that has not
+ been allowed by the ACB_PWNOTREQ bit */
+
+ lm_pw = pdb_get_lanman_passwd( sam_acct );
+ nt_pw = pdb_get_nt_passwd( sam_acct );
+ acb_flags = pdb_get_acct_ctrl( sam_acct );
+ if ( !lm_pw && !nt_pw && !(acb_flags&ACB_PWNOTREQ) ) {
+ acb_flags |= ACB_DISABLED;
+ pdb_set_acct_ctrl( sam_acct, acb_flags, PDB_CHANGED );
+ }
+
+ return NT_STATUS_IS_OK(pdb_context->pdb_update_sam_account(pdb_context, sam_acct));
+}
+
+BOOL pdb_delete_sam_account(SAM_ACCOUNT *sam_acct)
+{
+ struct pdb_context *pdb_context = pdb_get_static_context(False);
+
+ if (!pdb_context) {
+ return False;
+ }
+
+ return NT_STATUS_IS_OK(pdb_context->pdb_delete_sam_account(pdb_context, sam_acct));
+}
+
+BOOL pdb_getgrsid(GROUP_MAP *map, DOM_SID sid)
+{
+ struct pdb_context *pdb_context = pdb_get_static_context(False);
+
+ if (!pdb_context) {
+ return False;
+ }
+
+ return NT_STATUS_IS_OK(pdb_context->
+ pdb_getgrsid(pdb_context, map, sid));
+}
+
+BOOL pdb_getgrgid(GROUP_MAP *map, gid_t gid)
+{
+ struct pdb_context *pdb_context = pdb_get_static_context(False);
+
+ if (!pdb_context) {
+ return False;
+ }
+
+ return NT_STATUS_IS_OK(pdb_context->
+ pdb_getgrgid(pdb_context, map, gid));
+}
+
+BOOL pdb_getgrnam(GROUP_MAP *map, const char *name)
+{
+ struct pdb_context *pdb_context = pdb_get_static_context(False);
+
+ if (!pdb_context) {
+ return False;
+ }
+
+ return NT_STATUS_IS_OK(pdb_context->
+ pdb_getgrnam(pdb_context, map, name));
+}
+
+BOOL pdb_add_group_mapping_entry(GROUP_MAP *map)
+{
+ struct pdb_context *pdb_context = pdb_get_static_context(False);
+
+ if (!pdb_context) {
+ return False;
+ }
+
+ return NT_STATUS_IS_OK(pdb_context->
+ pdb_add_group_mapping_entry(pdb_context, map));
+}
+
+BOOL pdb_update_group_mapping_entry(GROUP_MAP *map)
+{
+ struct pdb_context *pdb_context = pdb_get_static_context(False);
+
+ if (!pdb_context) {
+ return False;
+ }
+
+ return NT_STATUS_IS_OK(pdb_context->
+ pdb_update_group_mapping_entry(pdb_context, map));
+}
+
+BOOL pdb_delete_group_mapping_entry(DOM_SID sid)
+{
+ struct pdb_context *pdb_context = pdb_get_static_context(False);
+
+ if (!pdb_context) {
+ return False;
+ }
+
+ return NT_STATUS_IS_OK(pdb_context->
+ pdb_delete_group_mapping_entry(pdb_context, sid));
+}
+
+BOOL pdb_enum_group_mapping(enum SID_NAME_USE sid_name_use, GROUP_MAP **rmap,
+ int *num_entries, BOOL unix_only)
+{
+ struct pdb_context *pdb_context = pdb_get_static_context(False);
+
+ if (!pdb_context) {
+ return False;
+ }
+
+ return NT_STATUS_IS_OK(pdb_context->
+ pdb_enum_group_mapping(pdb_context, sid_name_use,
+ rmap, num_entries, unix_only));
+}
+
+BOOL pdb_find_alias(const char *name, DOM_SID *sid)
+{
+ struct pdb_context *pdb_context = pdb_get_static_context(False);
+
+ if (!pdb_context) {
+ return False;
+ }
+
+ return NT_STATUS_IS_OK(pdb_context->pdb_find_alias(pdb_context,
+ name, sid));
+}
+
+BOOL pdb_create_alias(const char *name, uint32 *rid)
+{
+ struct pdb_context *pdb_context = pdb_get_static_context(False);
+
+ if (!pdb_context) {
+ return False;
+ }
+
+ return NT_STATUS_IS_OK(pdb_context->pdb_create_alias(pdb_context,
+ name, rid));
+}
+
+BOOL pdb_delete_alias(const DOM_SID *sid)
+{
+ struct pdb_context *pdb_context = pdb_get_static_context(False);
+
+ if (!pdb_context) {
+ return False;
+ }
+
+ return NT_STATUS_IS_OK(pdb_context->pdb_delete_alias(pdb_context,
+ sid));
+
+}
+
+BOOL pdb_enum_aliases(const DOM_SID *sid, uint32 start_idx, uint32 max_entries,
+ uint32 *num_aliases, struct acct_info **info)
+{
+ struct pdb_context *pdb_context = pdb_get_static_context(False);
+
+ if (!pdb_context) {
+ return False;
+ }
+
+ return NT_STATUS_IS_OK(pdb_context->pdb_enum_aliases(pdb_context, sid,
+ start_idx,
+ max_entries,
+ num_aliases,
+ info));
+}
+
+BOOL pdb_get_aliasinfo(const DOM_SID *sid, struct acct_info *info)
+{
+ struct pdb_context *pdb_context = pdb_get_static_context(False);
+
+ if (!pdb_context) {
+ return False;
+ }
+
+ return NT_STATUS_IS_OK(pdb_context->pdb_get_aliasinfo(pdb_context, sid,
+ info));
+}
+
+BOOL pdb_set_aliasinfo(const DOM_SID *sid, struct acct_info *info)
+{
+ struct pdb_context *pdb_context = pdb_get_static_context(False);
+
+ if (!pdb_context) {
+ return False;
+ }
+
+ return NT_STATUS_IS_OK(pdb_context->pdb_set_aliasinfo(pdb_context, sid,
+ info));
+}
+
+BOOL pdb_add_aliasmem(const DOM_SID *alias, const DOM_SID *member)
+{
+ struct pdb_context *pdb_context = pdb_get_static_context(False);
+
+ if (!pdb_context) {
+ return False;
+ }
+
+ return NT_STATUS_IS_OK(pdb_context->
+ pdb_add_aliasmem(pdb_context, alias, member));
+}
+
+BOOL pdb_del_aliasmem(const DOM_SID *alias, const DOM_SID *member)
+{
+ struct pdb_context *pdb_context = pdb_get_static_context(False);
+
+ if (!pdb_context) {
+ return False;
+ }
+
+ return NT_STATUS_IS_OK(pdb_context->
+ pdb_del_aliasmem(pdb_context, alias, member));
+}
+
+BOOL pdb_enum_aliasmem(const DOM_SID *alias,
+ DOM_SID **members, int *num_members)
+{
+ struct pdb_context *pdb_context = pdb_get_static_context(False);
+
+ if (!pdb_context) {
+ return False;
+ }
+
+ return NT_STATUS_IS_OK(pdb_context->
+ pdb_enum_aliasmem(pdb_context, alias,
+ members, num_members));
+}
+
+BOOL pdb_enum_alias_memberships(const DOM_SID *sid,
+ DOM_SID **aliases, int *num)
+{
+ struct pdb_context *pdb_context = pdb_get_static_context(False);
+
+ if (!pdb_context) {
+ return False;
+ }
+
+ return NT_STATUS_IS_OK(pdb_context->
+ pdb_enum_alias_memberships(pdb_context, sid,
+ aliases, num));
+}
+
+BOOL pdb_add_sid_to_privilege(char *priv_name, DOM_SID *sid)
+{
+ struct pdb_context *pdb_context = pdb_get_static_context(False);
+
+ if (!pdb_context) {
+ return False;
+ }
+
+ return NT_STATUS_IS_OK(pdb_context->
+ pdb_add_sid_to_privilege(pdb_context, priv_name, sid));
+}
+
+BOOL pdb_remove_sid_from_privilege(char *priv_name, DOM_SID *sid)
+{
+ struct pdb_context *pdb_context = pdb_get_static_context(False);
+
+ if (!pdb_context) {
+ return False;
+ }
+
+ return NT_STATUS_IS_OK(pdb_context->
+ pdb_remove_sid_from_privilege(pdb_context, priv_name, sid));
+}
+
+BOOL pdb_get_privilege_set(DOM_SID *sid_list, int num_sids, PRIVILEGE_SET *privset)
+{
+ struct pdb_context *pdb_context = pdb_get_static_context(False);
+
+ if (!pdb_context) {
+ return False;
+ }
+
+ return NT_STATUS_IS_OK(pdb_context->
+ pdb_get_privilege_set(pdb_context, sid_list, num_sids, privset));
+}
+
+BOOL pdb_get_privilege_entry(const char *privname, char **sid_list)
+{
+ struct pdb_context *pdb_context = pdb_get_static_context(False);
+
+ if (!pdb_context) {
+ return False;
+ }
+
+ return NT_STATUS_IS_OK(pdb_context->
+ pdb_get_privilege_entry(pdb_context, privname, sid_list));
+}
+
+/***************************************************************
+ Initialize the static context (at smbd startup etc).
+
+ If uninitialised, context will auto-init on first use.
+ ***************************************************************/
+
+BOOL initialize_password_db(BOOL reload)
+{
+ return (pdb_get_static_context(reload) != NULL);
+}
+
+
+/***************************************************************************
+ Default implementations of some functions.
+ ****************************************************************************/
+
+static NTSTATUS pdb_default_getsampwnam (struct pdb_methods *methods, SAM_ACCOUNT *user, const char *sname)
+{
+ return NT_STATUS_NO_SUCH_USER;
+}
+
+static NTSTATUS pdb_default_getsampwsid(struct pdb_methods *my_methods, SAM_ACCOUNT * user, const DOM_SID *sid)
+{
+ return NT_STATUS_NO_SUCH_USER;
+}
+
+static NTSTATUS pdb_default_add_sam_account (struct pdb_methods *methods, SAM_ACCOUNT *newpwd)
+{
+ DEBUG(0,("this backend (%s) should not be listed as the first passdb backend! You can't add users to it.\n", methods->name));
+ return NT_STATUS_NOT_IMPLEMENTED;
+}
+
+static NTSTATUS pdb_default_update_sam_account (struct pdb_methods *methods, SAM_ACCOUNT *newpwd)
+{
+ return NT_STATUS_NOT_IMPLEMENTED;
+}
+
+static NTSTATUS pdb_default_delete_sam_account (struct pdb_methods *methods, SAM_ACCOUNT *pwd)
+{
+ return NT_STATUS_NOT_IMPLEMENTED;
+}
+
+static NTSTATUS pdb_default_setsampwent(struct pdb_methods *methods, BOOL update)
+{
+ return NT_STATUS_NOT_IMPLEMENTED;
+}
+
+static NTSTATUS pdb_default_getsampwent(struct pdb_methods *methods, SAM_ACCOUNT *user)
+{
+ return NT_STATUS_NOT_IMPLEMENTED;
+}
+
+static void pdb_default_endsampwent(struct pdb_methods *methods)
+{
+ return; /* NT_STATUS_NOT_IMPLEMENTED; */
+}
+
+static NTSTATUS pdb_default_settrustpwent(struct pdb_methods *methods)
+{
+ return NT_STATUS_NOT_IMPLEMENTED;
+}
+
+static NTSTATUS pdb_default_gettrustpwent(struct pdb_methods *methods, SAM_TRUST_PASSWD* trust)
+{
+ return NT_STATUS_NOT_IMPLEMENTED;
+}
+
+static NTSTATUS pdb_default_gettrustpwnam(struct pdb_methods *methods, SAM_TRUST_PASSWD* trust,
+ const char* name)
+{
+ return NT_STATUS_NOT_IMPLEMENTED;
+}
+
+static NTSTATUS pdb_default_gettrustpwsid(struct pdb_methods *methods, SAM_TRUST_PASSWD* trust,
+ const DOM_SID* sid)
+{
+ return NT_STATUS_NOT_IMPLEMENTED;
+}
+
+static NTSTATUS pdb_default_add_trust_passwd(struct pdb_methods *methods, const SAM_TRUST_PASSWD* trust)
+{
+ return NT_STATUS_NOT_IMPLEMENTED;
+}
+
+static NTSTATUS pdb_default_update_trust_passwd(struct pdb_methods *methods, const SAM_TRUST_PASSWD* trust)
+{
+ return NT_STATUS_NOT_IMPLEMENTED;
+}
+
+static NTSTATUS pdb_default_delete_trust_passwd(struct pdb_methods *methods, const SAM_TRUST_PASSWD* trust)
+{
+ return NT_STATUS_NOT_IMPLEMENTED;
+}
+
+static NTSTATUS pdb_default_add_sid_to_privilege(struct pdb_methods *methods, const char *priv_name, const DOM_SID *sid)
+{
+ return NT_STATUS_NOT_IMPLEMENTED;
+}
+
+static NTSTATUS pdb_default_remove_sid_from_privilege(struct pdb_methods *methods, const char *priv_name, const DOM_SID *sid)
+{
+ return NT_STATUS_NOT_IMPLEMENTED;
+}
+
+static NTSTATUS pdb_default_get_privilege_set(struct pdb_methods *methods, DOM_SID *sid_list, int num_sids, PRIVILEGE_SET *privset)
+{
+ /* by default return the empty privilege set as otherwise login will
+ * be denied if a backend does not support privilege sets */
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS pdb_default_get_privilege_entry(struct pdb_methods *methods, const char *privname, char **sid_list)
+{
+ return NT_STATUS_NOT_IMPLEMENTED;
+}
+
+
+NTSTATUS make_pdb_methods(TALLOC_CTX *mem_ctx, PDB_METHODS **methods)
+{
+ *methods = talloc(mem_ctx, sizeof(struct pdb_methods));
+
+ if (!*methods) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ ZERO_STRUCTP(*methods);
+
+ (*methods)->setsampwent = pdb_default_setsampwent;
+ (*methods)->endsampwent = pdb_default_endsampwent;
+ (*methods)->getsampwent = pdb_default_getsampwent;
+ (*methods)->getsampwnam = pdb_default_getsampwnam;
+ (*methods)->getsampwsid = pdb_default_getsampwsid;
+ (*methods)->add_sam_account = pdb_default_add_sam_account;
+ (*methods)->update_sam_account = pdb_default_update_sam_account;
+ (*methods)->delete_sam_account = pdb_default_delete_sam_account;
+
+ (*methods)->getgrsid = pdb_default_getgrsid;
+ (*methods)->getgrgid = pdb_default_getgrgid;
+ (*methods)->getgrnam = pdb_default_getgrnam;
+ (*methods)->add_group_mapping_entry = pdb_default_add_group_mapping_entry;
+ (*methods)->update_group_mapping_entry = pdb_default_update_group_mapping_entry;
+ (*methods)->delete_group_mapping_entry = pdb_default_delete_group_mapping_entry;
+ (*methods)->enum_group_mapping = pdb_default_enum_group_mapping;
+ (*methods)->find_alias = pdb_default_find_alias;
+ (*methods)->create_alias = pdb_default_create_alias;
+ (*methods)->delete_alias = pdb_default_delete_alias;
+ (*methods)->enum_aliases = pdb_default_enum_aliases;
+ (*methods)->get_aliasinfo = pdb_default_get_aliasinfo;
+ (*methods)->set_aliasinfo = pdb_default_set_aliasinfo;
+ (*methods)->add_aliasmem = pdb_default_add_aliasmem;
+ (*methods)->del_aliasmem = pdb_default_del_aliasmem;
+ (*methods)->enum_aliasmem = pdb_default_enum_aliasmem;
+ (*methods)->enum_alias_memberships = pdb_default_alias_memberships;
+
+ (*methods)->settrustpwent = pdb_default_settrustpwent;
+ (*methods)->gettrustpwent = pdb_default_gettrustpwent;
+ (*methods)->gettrustpwnam = pdb_default_gettrustpwnam;
+ (*methods)->gettrustpwsid = pdb_default_gettrustpwsid;
+ (*methods)->add_trust_passwd = pdb_default_add_trust_passwd;
+ (*methods)->update_trust_passwd = pdb_default_update_trust_passwd;
+ (*methods)->delete_trust_passwd = pdb_default_delete_trust_passwd;
+
+ (*methods)->add_sid_to_privilege = pdb_default_add_sid_to_privilege;
+ (*methods)->remove_sid_from_privilege = pdb_default_remove_sid_from_privilege;
+ (*methods)->get_privilege_set = pdb_default_get_privilege_set;
+ (*methods)->get_privilege_entry = pdb_default_get_privilege_entry;
+
+ return NT_STATUS_OK;
+}
diff --git a/source/passdb/pdb_ldap.c b/source/passdb/pdb_ldap.c
new file mode 100644
index 00000000000..23ab0f9965d
--- /dev/null
+++ b/source/passdb/pdb_ldap.c
@@ -0,0 +1,3117 @@
+/*
+ Unix SMB/CIFS mplementation.
+ LDAP protocol helper functions for SAMBA
+ Copyright (C) Jean François Micouleau 1998
+ Copyright (C) Gerald Carter 2001-2003
+ Copyright (C) Shahms King 2001
+ Copyright (C) Andrew Bartlett 2002-2003
+ Copyright (C) Stefan (metze) Metzmacher 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 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.
+
+*/
+
+/* TODO:
+* persistent connections: if using NSS LDAP, many connections are made
+* however, using only one within Samba would be nice
+*
+* Clean up SSL stuff, compile on OpenLDAP 1.x, 2.x, and Netscape SDK
+*
+* Other LDAP based login attributes: accountExpires, etc.
+* (should be the domain of Samba proper, but the sam_password/SAM_ACCOUNT
+* structures don't have fields for some of these attributes)
+*
+* SSL is done, but can't get the certificate based authentication to work
+* against on my test platform (Linux 2.4, OpenLDAP 2.x)
+*/
+
+/* NOTE: this will NOT work against an Active Directory server
+* due to the fact that the two password fields cannot be retrieved
+* from a server; recommend using security = domain in this situation
+* and/or winbind
+*/
+
+#include "includes.h"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_PASSDB
+
+#include <lber.h>
+#include <ldap.h>
+
+/*
+ * Work around versions of the LDAP client libs that don't have the OIDs
+ * defined, or have them defined under the old name.
+ * This functionality is really a factor of the server, not the client
+ *
+ */
+
+#if defined(LDAP_EXOP_X_MODIFY_PASSWD) && !defined(LDAP_EXOP_MODIFY_PASSWD)
+#define LDAP_EXOP_MODIFY_PASSWD LDAP_EXOP_X_MODIFY_PASSWD
+#elif !defined(LDAP_EXOP_MODIFY_PASSWD)
+#define LDAP_EXOP_MODIFY_PASSWD "1.3.6.1.4.1.4203.1.11.1"
+#endif
+
+#if defined(LDAP_EXOP_X_MODIFY_PASSWD_ID) && !defined(LDAP_EXOP_MODIFY_PASSWD_ID)
+#define LDAP_TAG_EXOP_MODIFY_PASSWD_ID LDAP_EXOP_X_MODIFY_PASSWD_ID
+#elif !defined(LDAP_EXOP_MODIFY_PASSWD_ID)
+#define LDAP_TAG_EXOP_MODIFY_PASSWD_ID ((ber_tag_t) 0x80U)
+#endif
+
+#if defined(LDAP_EXOP_X_MODIFY_PASSWD_NEW) && !defined(LDAP_EXOP_MODIFY_PASSWD_NEW)
+#define LDAP_TAG_EXOP_MODIFY_PASSWD_NEW LDAP_EXOP_X_MODIFY_PASSWD_NEW
+#elif !defined(LDAP_EXOP_MODIFY_PASSWD_NEW)
+#define LDAP_TAG_EXOP_MODIFY_PASSWD_NEW ((ber_tag_t) 0x82U)
+#endif
+
+
+#ifndef SAM_ACCOUNT
+#define SAM_ACCOUNT struct sam_passwd
+#endif
+
+#include "smbldap.h"
+
+struct ldapsam_privates {
+ struct smbldap_state *smbldap_state;
+
+ /* Former statics */
+ LDAPMessage *result;
+ LDAPMessage *entry;
+ int index;
+
+ const char *domain_name;
+ DOM_SID domain_sid;
+
+ /* configuration items */
+ int schema_ver;
+};
+
+/**********************************************************************
+ Free a LDAPMessage (one is stored on the SAM_ACCOUNT).
+ **********************************************************************/
+
+static void private_data_free_fn(void **result)
+{
+ ldap_msgfree(*result);
+ *result = NULL;
+}
+
+/**********************************************************************
+ Get the attribute name given a user schame version.
+ **********************************************************************/
+
+static const char* get_userattr_key2string( int schema_ver, int key )
+{
+ switch ( schema_ver ) {
+ case SCHEMAVER_SAMBAACCOUNT:
+ return get_attr_key2string( attrib_map_v22, key );
+
+ case SCHEMAVER_SAMBASAMACCOUNT:
+ return get_attr_key2string( attrib_map_v30, key );
+
+ default:
+ DEBUG(0,("get_userattr_key2string: unknown schema version specified\n"));
+ break;
+ }
+ return NULL;
+}
+
+/**********************************************************************
+ Return the list of attribute names given a user schema version.
+**********************************************************************/
+
+static char** get_userattr_list( int schema_ver )
+{
+ switch ( schema_ver ) {
+ case SCHEMAVER_SAMBAACCOUNT:
+ return get_attr_list( attrib_map_v22 );
+
+ case SCHEMAVER_SAMBASAMACCOUNT:
+ return get_attr_list( attrib_map_v30 );
+ default:
+ DEBUG(0,("get_userattr_list: unknown schema version specified!\n"));
+ break;
+ }
+
+ return NULL;
+}
+
+/*******************************************************************
+ Generate the LDAP search filter for the objectclass based on the
+ version of the schema we are using.
+******************************************************************/
+
+static const char* get_objclass_filter( int schema_ver )
+{
+ static fstring objclass_filter;
+
+ switch( schema_ver ) {
+ case SCHEMAVER_SAMBAACCOUNT:
+ fstr_sprintf( objclass_filter, "(objectclass=%s)", LDAP_OBJ_SAMBAACCOUNT );
+ break;
+ case SCHEMAVER_SAMBASAMACCOUNT:
+ fstr_sprintf( objclass_filter, "(objectclass=%s)", LDAP_OBJ_SAMBASAMACCOUNT );
+ break;
+ default:
+ DEBUG(0,("get_objclass_filter: Invalid schema version specified!\n"));
+ break;
+ }
+
+ return objclass_filter;
+}
+
+/*******************************************************************
+ Run the search by name.
+******************************************************************/
+
+static int ldapsam_search_suffix_by_name (struct ldapsam_privates *ldap_state,
+ const char *user,
+ LDAPMessage ** result, char **attr)
+{
+ pstring filter;
+ char *escape_user = escape_ldap_string_alloc(user);
+
+ if (!escape_user) {
+ return LDAP_NO_MEMORY;
+ }
+
+ /*
+ * in the filter expression, replace %u with the real name
+ * so in ldap filter, %u MUST exist :-)
+ */
+ pstr_sprintf(filter, "(&%s%s)", lp_ldap_filter(),
+ get_objclass_filter(ldap_state->schema_ver));
+
+ /*
+ * have to use this here because $ is filtered out
+ * in pstring_sub
+ */
+
+
+ all_string_sub(filter, "%u", escape_user, sizeof(pstring));
+ SAFE_FREE(escape_user);
+
+ return smbldap_search_suffix(ldap_state->smbldap_state, filter, attr, result);
+}
+
+/*******************************************************************
+ Run the search by rid.
+******************************************************************/
+
+static int ldapsam_search_suffix_by_rid (struct ldapsam_privates *ldap_state,
+ uint32 rid, LDAPMessage ** result,
+ char **attr)
+{
+ pstring filter;
+ int rc;
+
+ pstr_sprintf(filter, "(&(rid=%i)%s)", rid,
+ get_objclass_filter(ldap_state->schema_ver));
+
+ rc = smbldap_search_suffix(ldap_state->smbldap_state, filter, attr, result);
+
+ return rc;
+}
+
+/*******************************************************************
+ Run the search by SID.
+******************************************************************/
+
+static int ldapsam_search_suffix_by_sid (struct ldapsam_privates *ldap_state,
+ const DOM_SID *sid, LDAPMessage ** result,
+ char **attr)
+{
+ pstring filter;
+ int rc;
+ fstring sid_string;
+
+ pstr_sprintf(filter, "(&(%s=%s)%s)",
+ get_userattr_key2string(ldap_state->schema_ver, LDAP_ATTR_USER_SID),
+ sid_to_string(sid_string, sid),
+ get_objclass_filter(ldap_state->schema_ver));
+
+ rc = smbldap_search_suffix(ldap_state->smbldap_state, filter, attr, result);
+
+ return rc;
+}
+
+/*******************************************************************
+ Delete complete object or objectclass and attrs from
+ object found in search_result depending on lp_ldap_delete_dn
+******************************************************************/
+
+static NTSTATUS ldapsam_delete_entry(struct ldapsam_privates *ldap_state,
+ LDAPMessage *result,
+ const char *objectclass,
+ char **attrs)
+{
+ int rc;
+ LDAPMessage *entry = NULL;
+ LDAPMod **mods = NULL;
+ char *name, *dn;
+ BerElement *ptr = NULL;
+
+ rc = ldap_count_entries(ldap_state->smbldap_state->ldap_struct, result);
+
+ if (rc != 1) {
+ DEBUG(0, ("ldapsam_delete_entry: Entry must exist exactly once!\n"));
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ entry = ldap_first_entry(ldap_state->smbldap_state->ldap_struct, result);
+ dn = smbldap_get_dn(ldap_state->smbldap_state->ldap_struct, entry);
+ if (!dn) {
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ if (lp_ldap_delete_dn()) {
+ NTSTATUS ret = NT_STATUS_OK;
+ rc = smbldap_delete(ldap_state->smbldap_state, dn);
+
+ if (rc != LDAP_SUCCESS) {
+ DEBUG(0, ("ldapsam_delete_entry: Could not delete object %s\n", dn));
+ ret = NT_STATUS_UNSUCCESSFUL;
+ }
+ SAFE_FREE(dn);
+ return ret;
+ }
+
+ /* Ok, delete only the SAM attributes */
+
+ for (name = ldap_first_attribute(ldap_state->smbldap_state->ldap_struct, entry, &ptr);
+ name != NULL;
+ name = ldap_next_attribute(ldap_state->smbldap_state->ldap_struct, entry, ptr)) {
+ char **attrib;
+
+ /* We are only allowed to delete the attributes that
+ really exist. */
+
+ for (attrib = attrs; *attrib != NULL; attrib++) {
+ if (StrCaseCmp(*attrib, name) == 0) {
+ DEBUG(10, ("ldapsam_delete_entry: deleting attribute %s\n", name));
+ smbldap_set_mod(&mods, LDAP_MOD_DELETE, name, NULL);
+ }
+ }
+
+ ldap_memfree(name);
+ }
+
+ if (ptr != NULL) {
+ ber_free(ptr, 0);
+ }
+
+ smbldap_set_mod(&mods, LDAP_MOD_DELETE, "objectClass", objectclass);
+
+ rc = smbldap_modify(ldap_state->smbldap_state, dn, mods);
+ ldap_mods_free(mods, True);
+
+ if (rc != LDAP_SUCCESS) {
+ char *ld_error = NULL;
+ ldap_get_option(ldap_state->smbldap_state->ldap_struct, LDAP_OPT_ERROR_STRING,
+ &ld_error);
+
+ DEBUG(0, ("ldapsam_delete_entry: Could not delete attributes for %s, error: %s (%s)\n",
+ dn, ldap_err2string(rc), ld_error?ld_error:"unknown"));
+ SAFE_FREE(ld_error);
+ SAFE_FREE(dn);
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ SAFE_FREE(dn);
+ return NT_STATUS_OK;
+}
+
+/* New Interface is being implemented here */
+
+#if 0 /* JERRY - not uesed anymore */
+
+/**********************************************************************
+Initialize SAM_ACCOUNT from an LDAP query (unix attributes only)
+*********************************************************************/
+static BOOL get_unix_attributes (struct ldapsam_privates *ldap_state,
+ SAM_ACCOUNT * sampass,
+ LDAPMessage * entry,
+ gid_t *gid)
+{
+ pstring homedir;
+ pstring temp;
+ char **ldap_values;
+ char **values;
+
+ if ((ldap_values = ldap_get_values (ldap_state->smbldap_state->ldap_struct, entry, "objectClass")) == NULL) {
+ DEBUG (1, ("get_unix_attributes: no objectClass! \n"));
+ return False;
+ }
+
+ for (values=ldap_values;*values;values++) {
+ if (strequal(*values, LDAP_OBJ_POSIXACCOUNT )) {
+ break;
+ }
+ }
+
+ if (!*values) { /*end of array, no posixAccount */
+ DEBUG(10, ("user does not have %s attributes\n", LDAP_OBJ_POSIXACCOUNT));
+ ldap_value_free(ldap_values);
+ return False;
+ }
+ ldap_value_free(ldap_values);
+
+ if ( !smbldap_get_single_pstring(ldap_state->smbldap_state->ldap_struct, entry,
+ get_userattr_key2string(ldap_state->schema_ver, LDAP_ATTR_UNIX_HOME), homedir) )
+ {
+ return False;
+ }
+
+ if ( !smbldap_get_single_pstring(ldap_state->smbldap_state->ldap_struct, entry,
+ get_userattr_key2string(ldap_state->schema_ver, LDAP_ATTR_GIDNUMBER), temp) )
+ {
+ return False;
+ }
+
+ *gid = (gid_t)atol(temp);
+
+ pdb_set_unix_homedir(sampass, homedir, PDB_SET);
+
+ DEBUG(10, ("user has %s attributes\n", LDAP_OBJ_POSIXACCOUNT));
+
+ return True;
+}
+
+#endif
+
+static time_t ldapsam_get_entry_timestamp(
+ struct ldapsam_privates *ldap_state,
+ LDAPMessage * entry)
+{
+ pstring temp;
+ struct tm tm;
+
+ if (!smbldap_get_single_pstring(
+ ldap_state->smbldap_state->ldap_struct, entry,
+ get_userattr_key2string(ldap_state->schema_ver,
+ LDAP_ATTR_MOD_TIMESTAMP),
+ temp))
+ return (time_t) 0;
+
+ strptime(temp, "%Y%m%d%H%M%SZ", &tm);
+ tzset();
+ return timegm(&tm);
+}
+
+/**********************************************************************
+ Initialize SAM_ACCOUNT from an LDAP query.
+ (Based on init_sam_from_buffer in pdb_tdb.c)
+*********************************************************************/
+
+static BOOL init_sam_from_ldap (struct ldapsam_privates *ldap_state,
+ SAM_ACCOUNT * sampass,
+ LDAPMessage * entry)
+{
+ time_t logon_time,
+ logoff_time,
+ kickoff_time,
+ pass_last_set_time,
+ pass_can_change_time,
+ pass_must_change_time,
+ ldap_entry_time,
+ bad_password_time;
+ pstring username,
+ domain,
+ nt_username,
+ fullname,
+ homedir,
+ dir_drive,
+ logon_script,
+ profile_path,
+ acct_desc,
+ workstations;
+ char munged_dial[2048];
+ uint32 user_rid;
+ uint8 smblmpwd[LM_HASH_LEN],
+ smbntpwd[NT_HASH_LEN];
+ uint16 acct_ctrl = 0,
+ logon_divs;
+ uint16 bad_password_count = 0,
+ logon_count = 0;
+ uint32 hours_len;
+ uint8 hours[MAX_HOURS_LEN];
+ pstring temp;
+ LOGIN_CACHE *cache_entry = NULL;
+
+ /*
+ * do a little initialization
+ */
+ username[0] = '\0';
+ domain[0] = '\0';
+ nt_username[0] = '\0';
+ fullname[0] = '\0';
+ homedir[0] = '\0';
+ dir_drive[0] = '\0';
+ logon_script[0] = '\0';
+ profile_path[0] = '\0';
+ acct_desc[0] = '\0';
+ munged_dial[0] = '\0';
+ workstations[0] = '\0';
+
+
+ if (sampass == NULL || ldap_state == NULL || entry == NULL) {
+ DEBUG(0, ("init_sam_from_ldap: NULL parameters found!\n"));
+ return False;
+ }
+
+ if (ldap_state->smbldap_state->ldap_struct == NULL) {
+ DEBUG(0, ("init_sam_from_ldap: ldap_state->smbldap_state->ldap_struct is NULL!\n"));
+ return False;
+ }
+
+ if (!smbldap_get_single_pstring(ldap_state->smbldap_state->ldap_struct, entry, "uid", username)) {
+ DEBUG(1, ("init_sam_from_ldap: No uid attribute found for this user!\n"));
+ return False;
+ }
+
+ DEBUG(2, ("init_sam_from_ldap: Entry found for user: %s\n", username));
+
+ pstrcpy(nt_username, username);
+
+ pstrcpy(domain, ldap_state->domain_name);
+
+ pdb_set_username(sampass, username, PDB_SET);
+
+ pdb_set_domain(sampass, domain, PDB_DEFAULT);
+ pdb_set_nt_username(sampass, nt_username, PDB_SET);
+
+ /* deal with different attributes between the schema first */
+
+ if ( ldap_state->schema_ver == SCHEMAVER_SAMBASAMACCOUNT ) {
+ if (smbldap_get_single_pstring(ldap_state->smbldap_state->ldap_struct, entry,
+ get_userattr_key2string(ldap_state->schema_ver, LDAP_ATTR_USER_SID), temp)) {
+ pdb_set_user_sid_from_string(sampass, temp, PDB_SET);
+ }
+
+ if (smbldap_get_single_pstring(ldap_state->smbldap_state->ldap_struct, entry,
+ get_userattr_key2string(ldap_state->schema_ver, LDAP_ATTR_PRIMARY_GROUP_SID), temp)) {
+ pdb_set_group_sid_from_string(sampass, temp, PDB_SET);
+ } else {
+ pdb_set_group_sid_from_rid(sampass, DOMAIN_GROUP_RID_USERS, PDB_DEFAULT);
+ }
+ } else {
+ if (smbldap_get_single_pstring(ldap_state->smbldap_state->ldap_struct, entry,
+ get_userattr_key2string(ldap_state->schema_ver, LDAP_ATTR_USER_RID), temp)) {
+ user_rid = (uint32)atol(temp);
+ pdb_set_user_sid_from_rid(sampass, user_rid, PDB_SET);
+ }
+
+ if (!smbldap_get_single_pstring(ldap_state->smbldap_state->ldap_struct, entry,
+ get_userattr_key2string(ldap_state->schema_ver, LDAP_ATTR_PRIMARY_GROUP_RID), temp)) {
+ pdb_set_group_sid_from_rid(sampass, DOMAIN_GROUP_RID_USERS, PDB_DEFAULT);
+ } else {
+ uint32 group_rid;
+
+ group_rid = (uint32)atol(temp);
+
+ /* for some reason, we often have 0 as a primary group RID.
+ Make sure that we treat this just as a 'default' value */
+
+ if ( group_rid > 0 )
+ pdb_set_group_sid_from_rid(sampass, group_rid, PDB_SET);
+ else
+ pdb_set_group_sid_from_rid(sampass, DOMAIN_GROUP_RID_USERS, PDB_DEFAULT);
+ }
+ }
+
+ if (pdb_get_init_flags(sampass,PDB_USERSID) == PDB_DEFAULT) {
+ DEBUG(1, ("init_sam_from_ldap: no %s or %s attribute found for this user %s\n",
+ get_userattr_key2string(ldap_state->schema_ver, LDAP_ATTR_USER_SID),
+ get_userattr_key2string(ldap_state->schema_ver, LDAP_ATTR_USER_RID),
+ username));
+ return False;
+ }
+
+ if (!smbldap_get_single_pstring(ldap_state->smbldap_state->ldap_struct, entry,
+ get_userattr_key2string(ldap_state->schema_ver, LDAP_ATTR_PWD_LAST_SET), temp)) {
+ /* leave as default */
+ } else {
+ pass_last_set_time = (time_t) atol(temp);
+ pdb_set_pass_last_set_time(sampass, pass_last_set_time, PDB_SET);
+ }
+
+ if (!smbldap_get_single_pstring(ldap_state->smbldap_state->ldap_struct, entry,
+ get_userattr_key2string(ldap_state->schema_ver, LDAP_ATTR_LOGON_TIME), temp)) {
+ /* leave as default */
+ } else {
+ logon_time = (time_t) atol(temp);
+ pdb_set_logon_time(sampass, logon_time, PDB_SET);
+ }
+
+ if (!smbldap_get_single_pstring(ldap_state->smbldap_state->ldap_struct, entry,
+ get_userattr_key2string(ldap_state->schema_ver, LDAP_ATTR_LOGOFF_TIME), temp)) {
+ /* leave as default */
+ } else {
+ logoff_time = (time_t) atol(temp);
+ pdb_set_logoff_time(sampass, logoff_time, PDB_SET);
+ }
+
+ if (!smbldap_get_single_pstring(ldap_state->smbldap_state->ldap_struct, entry,
+ get_userattr_key2string(ldap_state->schema_ver, LDAP_ATTR_KICKOFF_TIME), temp)) {
+ /* leave as default */
+ } else {
+ kickoff_time = (time_t) atol(temp);
+ pdb_set_kickoff_time(sampass, kickoff_time, PDB_SET);
+ }
+
+ if (!smbldap_get_single_pstring(ldap_state->smbldap_state->ldap_struct, entry,
+ get_userattr_key2string(ldap_state->schema_ver, LDAP_ATTR_PWD_CAN_CHANGE), temp)) {
+ /* leave as default */
+ } else {
+ pass_can_change_time = (time_t) atol(temp);
+ pdb_set_pass_can_change_time(sampass, pass_can_change_time, PDB_SET);
+ }
+
+ if (!smbldap_get_single_pstring(ldap_state->smbldap_state->ldap_struct, entry,
+ get_userattr_key2string(ldap_state->schema_ver, LDAP_ATTR_PWD_MUST_CHANGE), temp)) {
+ /* leave as default */
+ } else {
+ pass_must_change_time = (time_t) atol(temp);
+ pdb_set_pass_must_change_time(sampass, pass_must_change_time, PDB_SET);
+ }
+
+ /* recommend that 'gecos' and 'displayName' should refer to the same
+ * attribute OID. userFullName depreciated, only used by Samba
+ * primary rules of LDAP: don't make a new attribute when one is already defined
+ * that fits your needs; using cn then displayName rather than 'userFullName'
+ */
+
+ if (!smbldap_get_single_pstring(ldap_state->smbldap_state->ldap_struct, entry,
+ get_userattr_key2string(ldap_state->schema_ver, LDAP_ATTR_DISPLAY_NAME), fullname)) {
+ if (!smbldap_get_single_pstring(ldap_state->smbldap_state->ldap_struct, entry,
+ get_userattr_key2string(ldap_state->schema_ver, LDAP_ATTR_CN), fullname)) {
+ /* leave as default */
+ } else {
+ pdb_set_fullname(sampass, fullname, PDB_SET);
+ }
+ } else {
+ pdb_set_fullname(sampass, fullname, PDB_SET);
+ }
+
+ if (!smbldap_get_single_pstring(ldap_state->smbldap_state->ldap_struct, entry,
+ get_userattr_key2string(ldap_state->schema_ver, LDAP_ATTR_HOME_DRIVE), dir_drive))
+ {
+ pdb_set_dir_drive( sampass,
+ talloc_sub_basic(sampass->mem_ctx, username, lp_logon_drive()),
+ PDB_DEFAULT );
+ } else {
+ pdb_set_dir_drive(sampass, dir_drive, PDB_SET);
+ }
+
+ if (!smbldap_get_single_pstring(ldap_state->smbldap_state->ldap_struct, entry,
+ get_userattr_key2string(ldap_state->schema_ver, LDAP_ATTR_HOME_PATH), homedir))
+ {
+ pdb_set_homedir( sampass,
+ talloc_sub_basic(sampass->mem_ctx, username, lp_logon_home()),
+ PDB_DEFAULT );
+ } else {
+ pdb_set_homedir(sampass, homedir, PDB_SET);
+ }
+
+ if (!smbldap_get_single_pstring(ldap_state->smbldap_state->ldap_struct, entry,
+ get_userattr_key2string(ldap_state->schema_ver, LDAP_ATTR_LOGON_SCRIPT), logon_script))
+ {
+ pdb_set_logon_script( sampass,
+ talloc_sub_basic(sampass->mem_ctx, username, lp_logon_script()),
+ PDB_DEFAULT );
+ } else {
+ pdb_set_logon_script(sampass, logon_script, PDB_SET);
+ }
+
+ if (!smbldap_get_single_pstring(ldap_state->smbldap_state->ldap_struct, entry,
+ get_userattr_key2string(ldap_state->schema_ver, LDAP_ATTR_PROFILE_PATH), profile_path))
+ {
+ pdb_set_profile_path( sampass,
+ talloc_sub_basic( sampass->mem_ctx, username, lp_logon_path()),
+ PDB_DEFAULT );
+ } else {
+ pdb_set_profile_path(sampass, profile_path, PDB_SET);
+ }
+
+ if (!smbldap_get_single_pstring(ldap_state->smbldap_state->ldap_struct, entry,
+ get_userattr_key2string(ldap_state->schema_ver, LDAP_ATTR_DESC), acct_desc))
+ {
+ /* leave as default */
+ } else {
+ pdb_set_acct_desc(sampass, acct_desc, PDB_SET);
+ }
+
+ if (!smbldap_get_single_pstring(ldap_state->smbldap_state->ldap_struct, entry,
+ get_userattr_key2string(ldap_state->schema_ver, LDAP_ATTR_USER_WKS), workstations)) {
+ /* leave as default */;
+ } else {
+ pdb_set_workstations(sampass, workstations, PDB_SET);
+ }
+
+ if (!smbldap_get_single_attribute(ldap_state->smbldap_state->ldap_struct, entry,
+ get_userattr_key2string(ldap_state->schema_ver, LDAP_ATTR_MUNGED_DIAL), munged_dial, sizeof(munged_dial))) {
+ /* leave as default */;
+ } else {
+ pdb_set_munged_dial(sampass, munged_dial, PDB_SET);
+ }
+
+ /* FIXME: hours stuff should be cleaner */
+
+ logon_divs = 168;
+ hours_len = 21;
+ memset(hours, 0xff, hours_len);
+
+ if (!smbldap_get_single_pstring (ldap_state->smbldap_state->ldap_struct, entry,
+ get_userattr_key2string(ldap_state->schema_ver, LDAP_ATTR_LMPW), temp)) {
+ /* leave as default */
+ } else {
+ pdb_gethexpwd(temp, smblmpwd);
+ memset((char *)temp, '\0', strlen(temp)+1);
+ if (!pdb_set_lanman_passwd(sampass, smblmpwd, PDB_SET))
+ return False;
+ ZERO_STRUCT(smblmpwd);
+ }
+
+ if (!smbldap_get_single_pstring (ldap_state->smbldap_state->ldap_struct, entry,
+ get_userattr_key2string(ldap_state->schema_ver, LDAP_ATTR_NTPW), temp)) {
+ /* leave as default */
+ } else {
+ pdb_gethexpwd(temp, smbntpwd);
+ memset((char *)temp, '\0', strlen(temp)+1);
+ if (!pdb_set_nt_passwd(sampass, smbntpwd, PDB_SET))
+ return False;
+ ZERO_STRUCT(smbntpwd);
+ }
+
+ if (!smbldap_get_single_pstring (ldap_state->smbldap_state->ldap_struct, entry,
+ get_userattr_key2string(ldap_state->schema_ver, LDAP_ATTR_ACB_INFO), temp)) {
+ acct_ctrl |= ACB_NORMAL;
+ } else {
+ acct_ctrl = pdb_decode_acct_ctrl(temp);
+
+ if (acct_ctrl == 0)
+ acct_ctrl |= ACB_NORMAL;
+
+ pdb_set_acct_ctrl(sampass, acct_ctrl, PDB_SET);
+ }
+
+ pdb_set_hours_len(sampass, hours_len, PDB_SET);
+ pdb_set_logon_divs(sampass, logon_divs, PDB_SET);
+
+/* pdb_set_munged_dial(sampass, munged_dial, PDB_SET); */
+
+ if (!smbldap_get_single_pstring(ldap_state->smbldap_state->ldap_struct, entry,
+ get_userattr_key2string(ldap_state->schema_ver, LDAP_ATTR_BAD_PASSWORD_COUNT), temp)) {
+ /* leave as default */
+ } else {
+ bad_password_count = (uint32) atol(temp);
+ pdb_set_bad_password_count(sampass, bad_password_count, PDB_SET);
+ }
+
+ if (!smbldap_get_single_pstring(ldap_state->smbldap_state->ldap_struct, entry,
+ get_userattr_key2string(ldap_state->schema_ver, LDAP_ATTR_BAD_PASSWORD_TIME), temp)) {
+ /* leave as default */
+ } else {
+ bad_password_time = (time_t) atol(temp);
+ pdb_set_bad_password_time(sampass, bad_password_time, PDB_SET);
+ }
+
+
+ if (!smbldap_get_single_pstring(ldap_state->smbldap_state->ldap_struct, entry,
+ get_userattr_key2string(ldap_state->schema_ver, LDAP_ATTR_LOGON_COUNT), temp)) {
+ /* leave as default */
+ } else {
+ logon_count = (uint32) atol(temp);
+ pdb_set_logon_count(sampass, logon_count, PDB_SET);
+ }
+
+ /* pdb_set_unknown_6(sampass, unknown6, PDB_SET); */
+
+ pdb_set_hours(sampass, hours, PDB_SET);
+
+ /* check the timestamp of the cache vs ldap entry */
+ if (!(ldap_entry_time = ldapsam_get_entry_timestamp(ldap_state,
+ entry)))
+ return True;
+
+ /* see if we have newer updates */
+ if (!(cache_entry = login_cache_read(sampass))) {
+ DEBUG (9, ("No cache entry, bad count = %u, bad time = %u\n",
+ (unsigned int)pdb_get_bad_password_count(sampass),
+ (unsigned int)pdb_get_bad_password_time(sampass)));
+ return True;
+ }
+
+ DEBUG(7, ("ldap time is %u, cache time is %u, bad time = %u\n",
+ (unsigned int)ldap_entry_time, (unsigned int)cache_entry->entry_timestamp,
+ (unsigned int)cache_entry->bad_password_time));
+
+ if (ldap_entry_time > cache_entry->entry_timestamp) {
+ /* cache is older than directory , so
+ we need to delete the entry but allow the
+ fields to be written out */
+ login_cache_delentry(sampass);
+ } else {
+ /* read cache in */
+ pdb_set_acct_ctrl(sampass,
+ pdb_get_acct_ctrl(sampass) |
+ (cache_entry->acct_ctrl & ACB_AUTOLOCK),
+ PDB_SET);
+ pdb_set_bad_password_count(sampass,
+ cache_entry->bad_password_count,
+ PDB_SET);
+ pdb_set_bad_password_time(sampass,
+ cache_entry->bad_password_time,
+ PDB_SET);
+ }
+
+ SAFE_FREE(cache_entry);
+ return True;
+}
+
+/**********************************************************************
+ Initialize SAM_ACCOUNT from an LDAP query.
+ (Based on init_buffer_from_sam in pdb_tdb.c)
+*********************************************************************/
+
+static BOOL init_ldap_from_sam (struct ldapsam_privates *ldap_state,
+ LDAPMessage *existing,
+ LDAPMod *** mods, SAM_ACCOUNT * sampass,
+ BOOL (*need_update)(const SAM_ACCOUNT *,
+ enum pdb_elements))
+{
+ pstring temp;
+ uint32 rid;
+
+ if (mods == NULL || sampass == NULL) {
+ DEBUG(0, ("init_ldap_from_sam: NULL parameters found!\n"));
+ return False;
+ }
+
+ *mods = NULL;
+
+ /*
+ * took out adding "objectclass: sambaAccount"
+ * do this on a per-mod basis
+ */
+ if (need_update(sampass, PDB_USERNAME))
+ smbldap_make_mod(ldap_state->smbldap_state->ldap_struct, existing, mods,
+ "uid", pdb_get_username(sampass));
+
+ DEBUG(2, ("init_ldap_from_sam: Setting entry for user: %s\n", pdb_get_username(sampass)));
+
+ /* only update the RID if we actually need to */
+ if (need_update(sampass, PDB_USERSID)) {
+ fstring sid_string;
+ fstring dom_sid_string;
+ const DOM_SID *user_sid = pdb_get_user_sid(sampass);
+
+ switch ( ldap_state->schema_ver ) {
+ case SCHEMAVER_SAMBAACCOUNT:
+ if (!sid_peek_check_rid(&ldap_state->domain_sid, user_sid, &rid)) {
+ DEBUG(1, ("init_ldap_from_sam: User's SID (%s) is not for this domain (%s), cannot add to LDAP!\n",
+ sid_to_string(sid_string, user_sid),
+ sid_to_string(dom_sid_string, &ldap_state->domain_sid)));
+ return False;
+ }
+ slprintf(temp, sizeof(temp) - 1, "%i", rid);
+ smbldap_make_mod(ldap_state->smbldap_state->ldap_struct, existing, mods,
+ get_userattr_key2string(ldap_state->schema_ver, LDAP_ATTR_USER_RID),
+ temp);
+ break;
+
+ case SCHEMAVER_SAMBASAMACCOUNT:
+ smbldap_make_mod(ldap_state->smbldap_state->ldap_struct, existing, mods,
+ get_userattr_key2string(ldap_state->schema_ver, LDAP_ATTR_USER_SID),
+ sid_to_string(sid_string, user_sid));
+ break;
+
+ default:
+ DEBUG(0,("init_ldap_from_sam: unknown schema version specified\n"));
+ break;
+ }
+ }
+
+ /* we don't need to store the primary group RID - so leaving it
+ 'free' to hang off the unix primary group makes life easier */
+
+ if (need_update(sampass, PDB_GROUPSID)) {
+ fstring sid_string;
+ fstring dom_sid_string;
+ const DOM_SID *group_sid = pdb_get_group_sid(sampass);
+
+ switch ( ldap_state->schema_ver ) {
+ case SCHEMAVER_SAMBAACCOUNT:
+ if (!sid_peek_check_rid(&ldap_state->domain_sid, group_sid, &rid)) {
+ DEBUG(1, ("init_ldap_from_sam: User's Primary Group SID (%s) is not for this domain (%s), cannot add to LDAP!\n",
+ sid_to_string(sid_string, group_sid),
+ sid_to_string(dom_sid_string, &ldap_state->domain_sid)));
+ return False;
+ }
+
+ slprintf(temp, sizeof(temp) - 1, "%i", rid);
+ smbldap_make_mod(ldap_state->smbldap_state->ldap_struct, existing, mods,
+ get_userattr_key2string(ldap_state->schema_ver,
+ LDAP_ATTR_PRIMARY_GROUP_RID), temp);
+ break;
+
+ case SCHEMAVER_SAMBASAMACCOUNT:
+ smbldap_make_mod(ldap_state->smbldap_state->ldap_struct, existing, mods,
+ get_userattr_key2string(ldap_state->schema_ver,
+ LDAP_ATTR_PRIMARY_GROUP_SID), sid_to_string(sid_string, group_sid));
+ break;
+
+ default:
+ DEBUG(0,("init_ldap_from_sam: unknown schema version specified\n"));
+ break;
+ }
+
+ }
+
+ /* displayName, cn, and gecos should all be the same
+ * most easily accomplished by giving them the same OID
+ * gecos isn't set here b/c it should be handled by the
+ * add-user script
+ * We change displayName only and fall back to cn if
+ * it does not exist.
+ */
+
+ if (need_update(sampass, PDB_FULLNAME))
+ smbldap_make_mod(ldap_state->smbldap_state->ldap_struct, existing, mods,
+ get_userattr_key2string(ldap_state->schema_ver, LDAP_ATTR_DISPLAY_NAME),
+ pdb_get_fullname(sampass));
+
+ if (need_update(sampass, PDB_ACCTDESC))
+ smbldap_make_mod(ldap_state->smbldap_state->ldap_struct, existing, mods,
+ get_userattr_key2string(ldap_state->schema_ver, LDAP_ATTR_DESC),
+ pdb_get_acct_desc(sampass));
+
+ if (need_update(sampass, PDB_WORKSTATIONS))
+ smbldap_make_mod(ldap_state->smbldap_state->ldap_struct, existing, mods,
+ get_userattr_key2string(ldap_state->schema_ver, LDAP_ATTR_USER_WKS),
+ pdb_get_workstations(sampass));
+
+ if (need_update(sampass, PDB_MUNGEDDIAL))
+ smbldap_make_mod(ldap_state->smbldap_state->ldap_struct, existing, mods,
+ get_userattr_key2string(ldap_state->schema_ver, LDAP_ATTR_MUNGED_DIAL),
+ pdb_get_munged_dial(sampass));
+
+ if (need_update(sampass, PDB_SMBHOME))
+ smbldap_make_mod(ldap_state->smbldap_state->ldap_struct, existing, mods,
+ get_userattr_key2string(ldap_state->schema_ver, LDAP_ATTR_HOME_PATH),
+ pdb_get_homedir(sampass));
+
+ if (need_update(sampass, PDB_DRIVE))
+ smbldap_make_mod(ldap_state->smbldap_state->ldap_struct, existing, mods,
+ get_userattr_key2string(ldap_state->schema_ver, LDAP_ATTR_HOME_DRIVE),
+ pdb_get_dir_drive(sampass));
+
+ if (need_update(sampass, PDB_LOGONSCRIPT))
+ smbldap_make_mod(ldap_state->smbldap_state->ldap_struct, existing, mods,
+ get_userattr_key2string(ldap_state->schema_ver, LDAP_ATTR_LOGON_SCRIPT),
+ pdb_get_logon_script(sampass));
+
+ if (need_update(sampass, PDB_PROFILE))
+ smbldap_make_mod(ldap_state->smbldap_state->ldap_struct, existing, mods,
+ get_userattr_key2string(ldap_state->schema_ver, LDAP_ATTR_PROFILE_PATH),
+ pdb_get_profile_path(sampass));
+
+ slprintf(temp, sizeof(temp) - 1, "%li", pdb_get_logon_time(sampass));
+ if (need_update(sampass, PDB_LOGONTIME))
+ smbldap_make_mod(ldap_state->smbldap_state->ldap_struct, existing, mods,
+ get_userattr_key2string(ldap_state->schema_ver, LDAP_ATTR_LOGON_TIME), temp);
+
+ slprintf(temp, sizeof(temp) - 1, "%li", pdb_get_logoff_time(sampass));
+ if (need_update(sampass, PDB_LOGOFFTIME))
+ smbldap_make_mod(ldap_state->smbldap_state->ldap_struct, existing, mods,
+ get_userattr_key2string(ldap_state->schema_ver, LDAP_ATTR_LOGOFF_TIME), temp);
+
+ slprintf (temp, sizeof (temp) - 1, "%li", pdb_get_kickoff_time(sampass));
+ if (need_update(sampass, PDB_KICKOFFTIME))
+ smbldap_make_mod(ldap_state->smbldap_state->ldap_struct, existing, mods,
+ get_userattr_key2string(ldap_state->schema_ver, LDAP_ATTR_KICKOFF_TIME), temp);
+
+ slprintf (temp, sizeof (temp) - 1, "%li", pdb_get_pass_can_change_time(sampass));
+ if (need_update(sampass, PDB_CANCHANGETIME))
+ smbldap_make_mod(ldap_state->smbldap_state->ldap_struct, existing, mods,
+ get_userattr_key2string(ldap_state->schema_ver, LDAP_ATTR_PWD_CAN_CHANGE), temp);
+
+ slprintf (temp, sizeof (temp) - 1, "%li", pdb_get_pass_must_change_time(sampass));
+ if (need_update(sampass, PDB_MUSTCHANGETIME))
+ smbldap_make_mod(ldap_state->smbldap_state->ldap_struct, existing, mods,
+ get_userattr_key2string(ldap_state->schema_ver, LDAP_ATTR_PWD_MUST_CHANGE), temp);
+
+
+ if ((pdb_get_acct_ctrl(sampass)&(ACB_WSTRUST|ACB_SVRTRUST|ACB_DOMTRUST))
+ || (lp_ldap_passwd_sync()!=LDAP_PASSWD_SYNC_ONLY)) {
+
+ if (need_update(sampass, PDB_LMPASSWD)) {
+ const uchar *lm_pw = pdb_get_lanman_passwd(sampass);
+ if (lm_pw) {
+ pdb_sethexpwd(temp, lm_pw,
+ pdb_get_acct_ctrl(sampass));
+ smbldap_make_mod(ldap_state->smbldap_state->ldap_struct, existing, mods,
+ get_userattr_key2string(ldap_state->schema_ver, LDAP_ATTR_LMPW),
+ temp);
+ } else {
+ smbldap_make_mod(ldap_state->smbldap_state->ldap_struct, existing, mods,
+ get_userattr_key2string(ldap_state->schema_ver, LDAP_ATTR_LMPW),
+ NULL);
+ }
+ }
+ if (need_update(sampass, PDB_NTPASSWD)) {
+ const uchar *nt_pw = pdb_get_nt_passwd(sampass);
+ if (nt_pw) {
+ pdb_sethexpwd(temp, nt_pw,
+ pdb_get_acct_ctrl(sampass));
+ smbldap_make_mod(ldap_state->smbldap_state->ldap_struct, existing, mods,
+ get_userattr_key2string(ldap_state->schema_ver, LDAP_ATTR_NTPW),
+ temp);
+ } else {
+ smbldap_make_mod(ldap_state->smbldap_state->ldap_struct, existing, mods,
+ get_userattr_key2string(ldap_state->schema_ver, LDAP_ATTR_NTPW),
+ NULL);
+ }
+ }
+
+ if (need_update(sampass, PDB_PASSLASTSET)) {
+ slprintf (temp, sizeof (temp) - 1, "%li", pdb_get_pass_last_set_time(sampass));
+ smbldap_make_mod(ldap_state->smbldap_state->ldap_struct, existing, mods,
+ get_userattr_key2string(ldap_state->schema_ver, LDAP_ATTR_PWD_LAST_SET),
+ temp);
+ }
+ }
+
+ /* FIXME: Hours stuff goes in LDAP */
+
+ if (need_update(sampass, PDB_ACCTCTRL))
+ smbldap_make_mod(ldap_state->smbldap_state->ldap_struct, existing, mods,
+ get_userattr_key2string(ldap_state->schema_ver, LDAP_ATTR_ACB_INFO),
+ pdb_encode_acct_ctrl (pdb_get_acct_ctrl(sampass), NEW_PW_FORMAT_SPACE_PADDED_LEN));
+
+ /* password lockout cache:
+ - If we are now autolocking or clearing, we write to ldap
+ - If we are clearing, we delete the cache entry
+ - If the count is > 0, we update the cache
+
+ This even means when autolocking, we cache, just in case the
+ update doesn't work, and we have to cache the autolock flag */
+
+ if (need_update(sampass, PDB_BAD_PASSWORD_COUNT)) /* &&
+ need_update(sampass, PDB_BAD_PASSWORD_TIME)) */ {
+ uint16 badcount = pdb_get_bad_password_count(sampass);
+ time_t badtime = pdb_get_bad_password_time(sampass);
+ uint32 pol;
+ account_policy_get(AP_BAD_ATTEMPT_LOCKOUT, &pol);
+
+ DEBUG(3, ("updating bad password fields, policy=%u, count=%u, time=%u\n",
+ (unsigned int)pol, (unsigned int)badcount, (unsigned int)badtime));
+
+ if ((badcount >= pol) || (badcount == 0)) {
+ DEBUG(7, ("making mods to update ldap, count=%u, time=%u\n",
+ (unsigned int)badcount, (unsigned int)badtime));
+ slprintf (temp, sizeof (temp) - 1, "%li", (long)badcount);
+ smbldap_make_mod(
+ ldap_state->smbldap_state->ldap_struct,
+ existing, mods,
+ get_userattr_key2string(
+ ldap_state->schema_ver,
+ LDAP_ATTR_BAD_PASSWORD_COUNT),
+ temp);
+
+ slprintf (temp, sizeof (temp) - 1, "%li", badtime);
+ smbldap_make_mod(
+ ldap_state->smbldap_state->ldap_struct,
+ existing, mods,
+ get_userattr_key2string(
+ ldap_state->schema_ver,
+ LDAP_ATTR_BAD_PASSWORD_TIME),
+ temp);
+ }
+ if (badcount == 0) {
+ DEBUG(7, ("bad password count is reset, deleting login cache entry for %s\n", pdb_get_nt_username(sampass)));
+ login_cache_delentry(sampass);
+ } else {
+ LOGIN_CACHE cache_entry ={time(NULL),
+ pdb_get_acct_ctrl(sampass),
+ badcount, badtime};
+ DEBUG(7, ("Updating bad password count and time in login cache\n"));
+ login_cache_write(sampass, cache_entry);
+ }
+ }
+
+ return True;
+}
+
+/**********************************************************************
+ Connect to LDAP server for password enumeration.
+*********************************************************************/
+
+static NTSTATUS ldapsam_setsampwent(struct pdb_methods *my_methods, BOOL update)
+{
+ struct ldapsam_privates *ldap_state = (struct ldapsam_privates *)my_methods->private_data;
+ int rc;
+ pstring filter;
+ char **attr_list;
+
+ pstr_sprintf( filter, "(&%s%s)", lp_ldap_filter(),
+ get_objclass_filter(ldap_state->schema_ver));
+ all_string_sub(filter, "%u", "*", sizeof(pstring));
+
+ attr_list = get_userattr_list(ldap_state->schema_ver);
+ rc = smbldap_search_suffix(ldap_state->smbldap_state, filter,
+ attr_list, &ldap_state->result);
+ free_attr_list( attr_list );
+
+ if (rc != LDAP_SUCCESS) {
+ DEBUG(0, ("ldapsam_setsampwent: LDAP search failed: %s\n", ldap_err2string(rc)));
+ DEBUG(3, ("ldapsam_setsampwent: Query was: %s, %s\n", lp_ldap_suffix(), filter));
+ ldap_msgfree(ldap_state->result);
+ ldap_state->result = NULL;
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ DEBUG(2, ("ldapsam_setsampwent: %d entries in the base!\n",
+ ldap_count_entries(ldap_state->smbldap_state->ldap_struct,
+ ldap_state->result)));
+
+ ldap_state->entry = ldap_first_entry(ldap_state->smbldap_state->ldap_struct,
+ ldap_state->result);
+ ldap_state->index = 0;
+
+ return NT_STATUS_OK;
+}
+
+/**********************************************************************
+ End enumeration of the LDAP password list.
+*********************************************************************/
+
+static void ldapsam_endsampwent(struct pdb_methods *my_methods)
+{
+ struct ldapsam_privates *ldap_state = (struct ldapsam_privates *)my_methods->private_data;
+ if (ldap_state->result) {
+ ldap_msgfree(ldap_state->result);
+ ldap_state->result = NULL;
+ }
+}
+
+/**********************************************************************
+Get the next entry in the LDAP password database.
+*********************************************************************/
+
+static NTSTATUS ldapsam_getsampwent(struct pdb_methods *my_methods, SAM_ACCOUNT *user)
+{
+ NTSTATUS ret = NT_STATUS_UNSUCCESSFUL;
+ struct ldapsam_privates *ldap_state = (struct ldapsam_privates *)my_methods->private_data;
+ BOOL bret = False;
+
+ while (!bret) {
+ if (!ldap_state->entry)
+ return ret;
+
+ ldap_state->index++;
+ bret = init_sam_from_ldap(ldap_state, user, ldap_state->entry);
+
+ ldap_state->entry = ldap_next_entry(ldap_state->smbldap_state->ldap_struct,
+ ldap_state->entry);
+ }
+
+ return NT_STATUS_OK;
+}
+
+/**********************************************************************
+Get SAM_ACCOUNT entry from LDAP by username.
+*********************************************************************/
+
+static NTSTATUS ldapsam_getsampwnam(struct pdb_methods *my_methods, SAM_ACCOUNT *user, const char *sname)
+{
+ NTSTATUS ret = NT_STATUS_UNSUCCESSFUL;
+ struct ldapsam_privates *ldap_state = (struct ldapsam_privates *)my_methods->private_data;
+ LDAPMessage *result = NULL;
+ LDAPMessage *entry = NULL;
+ int count;
+ char ** attr_list;
+ int rc;
+
+ attr_list = get_userattr_list( ldap_state->schema_ver );
+ rc = ldapsam_search_suffix_by_name(ldap_state, sname, &result, attr_list);
+ free_attr_list( attr_list );
+
+ if ( rc != LDAP_SUCCESS )
+ return NT_STATUS_NO_SUCH_USER;
+
+ count = ldap_count_entries(ldap_state->smbldap_state->ldap_struct, result);
+
+ if (count < 1) {
+ DEBUG(4, ("ldapsam_getsampwnam: Unable to locate user [%s] count=%d\n", sname, count));
+ ldap_msgfree(result);
+ return NT_STATUS_NO_SUCH_USER;
+ } else if (count > 1) {
+ DEBUG(1, ("ldapsam_getsampwnam: Duplicate entries for this user [%s] Failing. count=%d\n", sname, count));
+ ldap_msgfree(result);
+ return NT_STATUS_NO_SUCH_USER;
+ }
+
+ entry = ldap_first_entry(ldap_state->smbldap_state->ldap_struct, result);
+ if (entry) {
+ if (!init_sam_from_ldap(ldap_state, user, entry)) {
+ DEBUG(1,("ldapsam_getsampwnam: init_sam_from_ldap failed for user '%s'!\n", sname));
+ ldap_msgfree(result);
+ return NT_STATUS_NO_SUCH_USER;
+ }
+ pdb_set_backend_private_data(user, result,
+ private_data_free_fn,
+ my_methods, PDB_CHANGED);
+ ret = NT_STATUS_OK;
+ } else {
+ ldap_msgfree(result);
+ }
+ return ret;
+}
+
+static int ldapsam_get_ldap_user_by_sid(struct ldapsam_privates *ldap_state,
+ const DOM_SID *sid, LDAPMessage **result)
+{
+ int rc = -1;
+ char ** attr_list;
+ uint32 rid;
+
+ switch ( ldap_state->schema_ver ) {
+ case SCHEMAVER_SAMBASAMACCOUNT:
+ attr_list = get_userattr_list(ldap_state->schema_ver);
+ rc = ldapsam_search_suffix_by_sid(ldap_state, sid, result, attr_list);
+ free_attr_list( attr_list );
+
+ if ( rc != LDAP_SUCCESS )
+ return rc;
+ break;
+
+ case SCHEMAVER_SAMBAACCOUNT:
+ if (!sid_peek_check_rid(&ldap_state->domain_sid, sid, &rid)) {
+ return rc;
+ }
+
+ attr_list = get_userattr_list(ldap_state->schema_ver);
+ rc = ldapsam_search_suffix_by_rid(ldap_state, rid, result, attr_list );
+ free_attr_list( attr_list );
+
+ if ( rc != LDAP_SUCCESS )
+ return rc;
+ break;
+ }
+ return rc;
+}
+
+/**********************************************************************
+ Get SAM_ACCOUNT entry from LDAP by SID.
+*********************************************************************/
+
+static NTSTATUS ldapsam_getsampwsid(struct pdb_methods *my_methods, SAM_ACCOUNT * user, const DOM_SID *sid)
+{
+ struct ldapsam_privates *ldap_state = (struct ldapsam_privates *)my_methods->private_data;
+ LDAPMessage *result = NULL;
+ LDAPMessage *entry = NULL;
+ int count;
+ int rc;
+ fstring sid_string;
+
+ rc = ldapsam_get_ldap_user_by_sid(ldap_state,
+ sid, &result);
+ if (rc != LDAP_SUCCESS)
+ return NT_STATUS_NO_SUCH_USER;
+
+ count = ldap_count_entries(ldap_state->smbldap_state->ldap_struct, result);
+
+ if (count < 1) {
+ DEBUG(4, ("ldapsam_getsampwsid: Unable to locate SID [%s] count=%d\n", sid_to_string(sid_string, sid),
+ count));
+ ldap_msgfree(result);
+ return NT_STATUS_NO_SUCH_USER;
+ } else if (count > 1) {
+ DEBUG(1, ("ldapsam_getsampwsid: More than one user with SID [%s]. Failing. count=%d\n", sid_to_string(sid_string, sid),
+ count));
+ ldap_msgfree(result);
+ return NT_STATUS_NO_SUCH_USER;
+ }
+
+ entry = ldap_first_entry(ldap_state->smbldap_state->ldap_struct, result);
+ if (!entry) {
+ ldap_msgfree(result);
+ return NT_STATUS_NO_SUCH_USER;
+ }
+
+ if (!init_sam_from_ldap(ldap_state, user, entry)) {
+ DEBUG(1,("ldapsam_getsampwrid: init_sam_from_ldap failed!\n"));
+ ldap_msgfree(result);
+ return NT_STATUS_NO_SUCH_USER;
+ }
+
+ pdb_set_backend_private_data(user, result,
+ private_data_free_fn,
+ my_methods, PDB_CHANGED);
+ return NT_STATUS_OK;
+}
+
+/********************************************************************
+ Do the actual modification - also change a plaintext passord if
+ it it set.
+**********************************************************************/
+
+static NTSTATUS ldapsam_modify_entry(struct pdb_methods *my_methods,
+ SAM_ACCOUNT *newpwd, char *dn,
+ LDAPMod **mods, int ldap_op,
+ BOOL (*need_update)(const SAM_ACCOUNT *, enum pdb_elements))
+{
+ struct ldapsam_privates *ldap_state = (struct ldapsam_privates *)my_methods->private_data;
+ int rc;
+
+ if (!my_methods || !newpwd || !dn) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ if (!mods) {
+ DEBUG(5,("ldapsam_modify_entry: mods is empty: nothing to modify\n"));
+ /* may be password change below however */
+ } else {
+ switch(ldap_op) {
+ case LDAP_MOD_ADD:
+ smbldap_set_mod(&mods, LDAP_MOD_ADD,
+ "objectclass",
+ LDAP_OBJ_ACCOUNT);
+ rc = smbldap_add(ldap_state->smbldap_state,
+ dn, mods);
+ break;
+ case LDAP_MOD_REPLACE:
+ rc = smbldap_modify(ldap_state->smbldap_state,
+ dn ,mods);
+ break;
+ default:
+ DEBUG(0,("ldapsam_modify_entry: Wrong LDAP operation type: %d!\n",
+ ldap_op));
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ if (rc!=LDAP_SUCCESS) {
+ char *ld_error = NULL;
+ ldap_get_option(ldap_state->smbldap_state->ldap_struct, LDAP_OPT_ERROR_STRING,
+ &ld_error);
+ DEBUG(1, ("ldapsam_modify_entry: Failed to %s user dn= %s with: %s\n\t%s\n",
+ ldap_op == LDAP_MOD_ADD ? "add" : "modify",
+ dn, ldap_err2string(rc),
+ ld_error?ld_error:"unknown"));
+ SAFE_FREE(ld_error);
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+ }
+
+ if (!(pdb_get_acct_ctrl(newpwd)&(ACB_WSTRUST|ACB_SVRTRUST|ACB_DOMTRUST)) &&
+ (lp_ldap_passwd_sync() != LDAP_PASSWD_SYNC_OFF) &&
+ need_update(newpwd, PDB_PLAINTEXT_PW) &&
+ (pdb_get_plaintext_passwd(newpwd)!=NULL)) {
+ BerElement *ber;
+ struct berval *bv;
+ char *retoid;
+ struct berval *retdata;
+ char *utf8_password;
+ char *utf8_dn;
+
+ if (push_utf8_allocate(&utf8_password, pdb_get_plaintext_passwd(newpwd)) == (size_t)-1) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ if (push_utf8_allocate(&utf8_dn, dn) == (size_t)-1) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ if ((ber = ber_alloc_t(LBER_USE_DER))==NULL) {
+ DEBUG(0,("ber_alloc_t returns NULL\n"));
+ SAFE_FREE(utf8_password);
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ ber_printf (ber, "{");
+ ber_printf (ber, "ts", LDAP_TAG_EXOP_MODIFY_PASSWD_ID, utf8_dn);
+ ber_printf (ber, "ts", LDAP_TAG_EXOP_MODIFY_PASSWD_NEW, utf8_password);
+ ber_printf (ber, "N}");
+
+ if ((rc = ber_flatten (ber, &bv))<0) {
+ DEBUG(0,("ldapsam_modify_entry: ber_flatten returns a value <0\n"));
+ ber_free(ber,1);
+ SAFE_FREE(utf8_dn);
+ SAFE_FREE(utf8_password);
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ SAFE_FREE(utf8_dn);
+ SAFE_FREE(utf8_password);
+ ber_free(ber, 1);
+
+ if ((rc = smbldap_extended_operation(ldap_state->smbldap_state,
+ LDAP_EXOP_MODIFY_PASSWD,
+ bv, NULL, NULL, &retoid,
+ &retdata)) != LDAP_SUCCESS) {
+ char *ld_error = NULL;
+ ldap_get_option(ldap_state->smbldap_state->ldap_struct, LDAP_OPT_ERROR_STRING,
+ &ld_error);
+ DEBUG(0,("ldapsam_modify_entry: LDAP Password could not be changed for user %s: %s\n\t%s\n",
+ pdb_get_username(newpwd), ldap_err2string(rc), ld_error?ld_error:"unknown"));
+ SAFE_FREE(ld_error);
+ ber_bvfree(bv);
+ return NT_STATUS_UNSUCCESSFUL;
+ } else {
+ DEBUG(3,("ldapsam_modify_entry: LDAP Password changed for user %s\n",pdb_get_username(newpwd)));
+#ifdef DEBUG_PASSWORD
+ DEBUG(100,("ldapsam_modify_entry: LDAP Password changed to %s\n",pdb_get_plaintext_passwd(newpwd)));
+#endif
+ ber_bvfree(retdata);
+ ber_memfree(retoid);
+ }
+ ber_bvfree(bv);
+ }
+ return NT_STATUS_OK;
+}
+
+/**********************************************************************
+ Delete entry from LDAP for username.
+*********************************************************************/
+
+static NTSTATUS ldapsam_delete_sam_account(struct pdb_methods *my_methods, SAM_ACCOUNT * sam_acct)
+{
+ struct ldapsam_privates *ldap_state = (struct ldapsam_privates *)my_methods->private_data;
+ const char *sname;
+ int rc;
+ LDAPMessage *result = NULL;
+ NTSTATUS ret;
+ char **attr_list;
+ fstring objclass;
+
+ if (!sam_acct) {
+ DEBUG(0, ("ldapsam_delete_sam_account: sam_acct was NULL!\n"));
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ sname = pdb_get_username(sam_acct);
+
+ DEBUG (3, ("ldapsam_delete_sam_account: Deleting user %s from LDAP.\n", sname));
+
+ attr_list= get_userattr_list( ldap_state->schema_ver );
+ rc = ldapsam_search_suffix_by_name(ldap_state, sname, &result, attr_list);
+
+ if (rc != LDAP_SUCCESS) {
+ free_attr_list( attr_list );
+ return NT_STATUS_NO_SUCH_USER;
+ }
+
+ switch ( ldap_state->schema_ver ) {
+ case SCHEMAVER_SAMBASAMACCOUNT:
+ fstrcpy( objclass, LDAP_OBJ_SAMBASAMACCOUNT );
+ break;
+
+ case SCHEMAVER_SAMBAACCOUNT:
+ fstrcpy( objclass, LDAP_OBJ_SAMBAACCOUNT );
+ break;
+ default:
+ fstrcpy( objclass, "UNKNOWN" );
+ DEBUG(0,("ldapsam_delete_sam_account: Unknown schema version specified!\n"));
+ break;
+ }
+
+ ret = ldapsam_delete_entry(ldap_state, result, objclass, attr_list );
+ ldap_msgfree(result);
+ free_attr_list( attr_list );
+
+ return ret;
+}
+
+/**********************************************************************
+ Helper function to determine for update_sam_account whether
+ we need LDAP modification.
+*********************************************************************/
+
+static BOOL element_is_changed(const SAM_ACCOUNT *sampass,
+ enum pdb_elements element)
+{
+ return IS_SAM_CHANGED(sampass, element);
+}
+
+/**********************************************************************
+ Update SAM_ACCOUNT.
+*********************************************************************/
+
+static NTSTATUS ldapsam_update_sam_account(struct pdb_methods *my_methods, SAM_ACCOUNT * newpwd)
+{
+ NTSTATUS ret = NT_STATUS_UNSUCCESSFUL;
+ struct ldapsam_privates *ldap_state = (struct ldapsam_privates *)my_methods->private_data;
+ int rc = 0;
+ char *dn;
+ LDAPMessage *result = NULL;
+ LDAPMessage *entry = NULL;
+ LDAPMod **mods = NULL;
+ char **attr_list;
+
+ result = pdb_get_backend_private_data(newpwd, my_methods);
+ if (!result) {
+ attr_list = get_userattr_list(ldap_state->schema_ver);
+ rc = ldapsam_search_suffix_by_name(ldap_state, pdb_get_username(newpwd), &result, attr_list );
+ free_attr_list( attr_list );
+ if (rc != LDAP_SUCCESS) {
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+ pdb_set_backend_private_data(newpwd, result, private_data_free_fn, my_methods, PDB_CHANGED);
+ }
+
+ if (ldap_count_entries(ldap_state->smbldap_state->ldap_struct, result) == 0) {
+ DEBUG(0, ("ldapsam_update_sam_account: No user to modify!\n"));
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ entry = ldap_first_entry(ldap_state->smbldap_state->ldap_struct, result);
+ dn = smbldap_get_dn(ldap_state->smbldap_state->ldap_struct, entry);
+ if (!dn) {
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ DEBUG(4, ("ldapsam_update_sam_account: user %s to be modified has dn: %s\n", pdb_get_username(newpwd), dn));
+
+ if (!init_ldap_from_sam(ldap_state, entry, &mods, newpwd,
+ element_is_changed)) {
+ DEBUG(0, ("ldapsam_update_sam_account: init_ldap_from_sam failed!\n"));
+ SAFE_FREE(dn);
+ if (mods != NULL)
+ ldap_mods_free(mods,True);
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ if (mods == NULL) {
+ DEBUG(4,("ldapsam_update_sam_account: mods is empty: nothing to update for user: %s\n",
+ pdb_get_username(newpwd)));
+ SAFE_FREE(dn);
+ return NT_STATUS_OK;
+ }
+
+ ret = ldapsam_modify_entry(my_methods,newpwd,dn,mods,LDAP_MOD_REPLACE, element_is_changed);
+ ldap_mods_free(mods,True);
+ SAFE_FREE(dn);
+
+ if (!NT_STATUS_IS_OK(ret)) {
+ char *ld_error = NULL;
+ ldap_get_option(ldap_state->smbldap_state->ldap_struct, LDAP_OPT_ERROR_STRING,
+ &ld_error);
+ DEBUG(0,("ldapsam_update_sam_account: failed to modify user with uid = %s, error: %s (%s)\n",
+ pdb_get_username(newpwd), ld_error?ld_error:"(unknwon)", ldap_err2string(rc)));
+ SAFE_FREE(ld_error);
+ return ret;
+ }
+
+ DEBUG(2, ("ldapsam_update_sam_account: successfully modified uid = %s in the LDAP database\n",
+ pdb_get_username(newpwd)));
+ return NT_STATUS_OK;
+}
+
+/**********************************************************************
+ Helper function to determine for update_sam_account whether
+ we need LDAP modification.
+ *********************************************************************/
+
+static BOOL element_is_set_or_changed(const SAM_ACCOUNT *sampass,
+ enum pdb_elements element)
+{
+ return (IS_SAM_SET(sampass, element) ||
+ IS_SAM_CHANGED(sampass, element));
+}
+
+/**********************************************************************
+ Add SAM_ACCOUNT to LDAP.
+*********************************************************************/
+
+static NTSTATUS ldapsam_add_sam_account(struct pdb_methods *my_methods, SAM_ACCOUNT * newpwd)
+{
+ NTSTATUS ret = NT_STATUS_UNSUCCESSFUL;
+ struct ldapsam_privates *ldap_state = (struct ldapsam_privates *)my_methods->private_data;
+ int rc;
+ LDAPMessage *result = NULL;
+ LDAPMessage *entry = NULL;
+ pstring dn;
+ LDAPMod **mods = NULL;
+ int ldap_op = LDAP_MOD_REPLACE;
+ uint32 num_result;
+ char **attr_list;
+ char *escape_user;
+ const char *username = pdb_get_username(newpwd);
+ const DOM_SID *sid = pdb_get_user_sid(newpwd);
+ pstring filter;
+ fstring sid_string;
+
+ if (!username || !*username) {
+ DEBUG(0, ("ldapsam_add_sam_account: Cannot add user without a username!\n"));
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ /* free this list after the second search or in case we exit on failure */
+ attr_list = get_userattr_list(ldap_state->schema_ver);
+
+ rc = ldapsam_search_suffix_by_name (ldap_state, username, &result, attr_list);
+
+ if (rc != LDAP_SUCCESS) {
+ free_attr_list( attr_list );
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ if (ldap_count_entries(ldap_state->smbldap_state->ldap_struct, result) != 0) {
+ DEBUG(0,("ldapsam_add_sam_account: User '%s' already in the base, with samba attributes\n",
+ username));
+ ldap_msgfree(result);
+ free_attr_list( attr_list );
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+ ldap_msgfree(result);
+ result = NULL;
+
+ if (element_is_set_or_changed(newpwd, PDB_USERSID)) {
+ rc = ldapsam_get_ldap_user_by_sid(ldap_state,
+ sid, &result);
+ if (rc == LDAP_SUCCESS) {
+ if (ldap_count_entries(ldap_state->smbldap_state->ldap_struct, result) != 0) {
+ DEBUG(0,("ldapsam_add_sam_account: SID '%s' already in the base, with samba attributes\n",
+ sid_to_string(sid_string, sid)));
+ free_attr_list( attr_list );
+ ldap_msgfree(result);
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+ ldap_msgfree(result);
+ }
+ }
+
+ /* does the entry already exist but without a samba attributes?
+ we need to return the samba attributes here */
+
+ escape_user = escape_ldap_string_alloc( username );
+ pstrcpy( filter, lp_ldap_filter() );
+ all_string_sub( filter, "%u", escape_user, sizeof(filter) );
+ SAFE_FREE( escape_user );
+
+ rc = smbldap_search_suffix(ldap_state->smbldap_state,
+ filter, attr_list, &result);
+ if ( rc != LDAP_SUCCESS ) {
+ free_attr_list( attr_list );
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ num_result = ldap_count_entries(ldap_state->smbldap_state->ldap_struct, result);
+
+ if (num_result > 1) {
+ DEBUG (0, ("ldapsam_add_sam_account: More than one user with that uid exists: bailing out!\n"));
+ free_attr_list( attr_list );
+ ldap_msgfree(result);
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ /* Check if we need to update an existing entry */
+ if (num_result == 1) {
+ char *tmp;
+
+ DEBUG(3,("ldapsam_add_sam_account: User exists without samba attributes: adding them\n"));
+ ldap_op = LDAP_MOD_REPLACE;
+ entry = ldap_first_entry (ldap_state->smbldap_state->ldap_struct, result);
+ tmp = smbldap_get_dn (ldap_state->smbldap_state->ldap_struct, entry);
+ if (!tmp) {
+ free_attr_list( attr_list );
+ ldap_msgfree(result);
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+ slprintf (dn, sizeof (dn) - 1, "%s", tmp);
+ SAFE_FREE(tmp);
+
+ } else if (ldap_state->schema_ver == SCHEMAVER_SAMBASAMACCOUNT) {
+
+ /* There might be a SID for this account already - say an idmap entry */
+
+ pstr_sprintf(filter, "(&(%s=%s)(|(objectClass=%s)(objectClass=%s)))",
+ get_userattr_key2string(ldap_state->schema_ver, LDAP_ATTR_USER_SID),
+ sid_to_string(sid_string, sid),
+ LDAP_OBJ_IDMAP_ENTRY,
+ LDAP_OBJ_SID_ENTRY);
+
+ /* free old result before doing a new search */
+ if (result != NULL) {
+ ldap_msgfree(result);
+ result = NULL;
+ }
+ rc = smbldap_search_suffix(ldap_state->smbldap_state,
+ filter, attr_list, &result);
+
+ if ( rc != LDAP_SUCCESS ) {
+ free_attr_list( attr_list );
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ num_result = ldap_count_entries(ldap_state->smbldap_state->ldap_struct, result);
+
+ if (num_result > 1) {
+ DEBUG (0, ("ldapsam_add_sam_account: More than one user with that uid exists: bailing out!\n"));
+ free_attr_list( attr_list );
+ ldap_msgfree(result);
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ /* Check if we need to update an existing entry */
+ if (num_result == 1) {
+ char *tmp;
+
+ DEBUG(3,("ldapsam_add_sam_account: User exists without samba attributes: adding them\n"));
+ ldap_op = LDAP_MOD_REPLACE;
+ entry = ldap_first_entry (ldap_state->smbldap_state->ldap_struct, result);
+ tmp = smbldap_get_dn (ldap_state->smbldap_state->ldap_struct, entry);
+ if (!tmp) {
+ free_attr_list( attr_list );
+ ldap_msgfree(result);
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+ slprintf (dn, sizeof (dn) - 1, "%s", tmp);
+ SAFE_FREE(tmp);
+ }
+ }
+
+ free_attr_list( attr_list );
+
+ if (num_result == 0) {
+ /* Check if we need to add an entry */
+ DEBUG(3,("ldapsam_add_sam_account: Adding new user\n"));
+ ldap_op = LDAP_MOD_ADD;
+ if (username[strlen(username)-1] == '$') {
+ slprintf (dn, sizeof (dn) - 1, "uid=%s,%s", username, lp_ldap_machine_suffix ());
+ } else {
+ slprintf (dn, sizeof (dn) - 1, "uid=%s,%s", username, lp_ldap_user_suffix ());
+ }
+ }
+
+ if (!init_ldap_from_sam(ldap_state, entry, &mods, newpwd,
+ element_is_set_or_changed)) {
+ DEBUG(0, ("ldapsam_add_sam_account: init_ldap_from_sam failed!\n"));
+ ldap_msgfree(result);
+ if (mods != NULL)
+ ldap_mods_free(mods,True);
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ ldap_msgfree(result);
+
+ if (mods == NULL) {
+ DEBUG(0,("ldapsam_add_sam_account: mods is empty: nothing to add for user: %s\n",pdb_get_username(newpwd)));
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+ switch ( ldap_state->schema_ver ) {
+ case SCHEMAVER_SAMBAACCOUNT:
+ smbldap_set_mod(&mods, LDAP_MOD_ADD, "objectclass", LDAP_OBJ_SAMBAACCOUNT);
+ break;
+ case SCHEMAVER_SAMBASAMACCOUNT:
+ smbldap_set_mod(&mods, LDAP_MOD_ADD, "objectclass", LDAP_OBJ_SAMBASAMACCOUNT);
+ break;
+ default:
+ DEBUG(0,("ldapsam_add_sam_account: invalid schema version specified\n"));
+ break;
+ }
+
+ ret = ldapsam_modify_entry(my_methods,newpwd,dn,mods,ldap_op, element_is_set_or_changed);
+ if (!NT_STATUS_IS_OK(ret)) {
+ DEBUG(0,("ldapsam_add_sam_account: failed to modify/add user with uid = %s (dn = %s)\n",
+ pdb_get_username(newpwd),dn));
+ ldap_mods_free(mods, True);
+ return ret;
+ }
+
+ DEBUG(2,("ldapsam_add_sam_account: added: uid == %s in the LDAP database\n", pdb_get_username(newpwd)));
+ ldap_mods_free(mods, True);
+
+ return NT_STATUS_OK;
+}
+
+/**********************************************************************
+ *********************************************************************/
+
+static int ldapsam_search_one_group (struct ldapsam_privates *ldap_state,
+ const char *filter,
+ LDAPMessage ** result)
+{
+ int scope = LDAP_SCOPE_SUBTREE;
+ int rc;
+ char **attr_list;
+
+ attr_list = get_attr_list(groupmap_attr_list);
+ rc = smbldap_search(ldap_state->smbldap_state,
+ lp_ldap_group_suffix (), scope,
+ filter, attr_list, 0, result);
+ free_attr_list( attr_list );
+
+ if (rc != LDAP_SUCCESS) {
+ char *ld_error = NULL;
+ ldap_get_option(ldap_state->smbldap_state->ldap_struct, LDAP_OPT_ERROR_STRING,
+ &ld_error);
+ DEBUG(0, ("ldapsam_search_one_group: "
+ "Problem during the LDAP search: LDAP error: %s (%s)\n",
+ ld_error?ld_error:"(unknown)", ldap_err2string(rc)));
+ DEBUGADD(3, ("ldapsam_search_one_group: Query was: %s, %s\n",
+ lp_ldap_group_suffix(), filter));
+ SAFE_FREE(ld_error);
+ }
+
+ return rc;
+}
+
+/**********************************************************************
+ *********************************************************************/
+
+static BOOL init_group_from_ldap(struct ldapsam_privates *ldap_state,
+ GROUP_MAP *map, LDAPMessage *entry)
+{
+ pstring temp;
+
+ if (ldap_state == NULL || map == NULL || entry == NULL ||
+ ldap_state->smbldap_state->ldap_struct == NULL) {
+ DEBUG(0, ("init_group_from_ldap: NULL parameters found!\n"));
+ return False;
+ }
+
+ if (!smbldap_get_single_pstring(ldap_state->smbldap_state->ldap_struct, entry,
+ get_attr_key2string(groupmap_attr_list, LDAP_ATTR_GIDNUMBER), temp)) {
+ DEBUG(0, ("init_group_from_ldap: Mandatory attribute %s not found\n",
+ get_attr_key2string( groupmap_attr_list, LDAP_ATTR_GIDNUMBER)));
+ return False;
+ }
+ DEBUG(2, ("init_group_from_ldap: Entry found for group: %s\n", temp));
+
+ map->gid = (gid_t)atol(temp);
+
+ if (!smbldap_get_single_pstring(ldap_state->smbldap_state->ldap_struct, entry,
+ get_attr_key2string( groupmap_attr_list, LDAP_ATTR_GROUP_SID), temp)) {
+ DEBUG(0, ("init_group_from_ldap: Mandatory attribute %s not found\n",
+ get_attr_key2string( groupmap_attr_list, LDAP_ATTR_GROUP_SID)));
+ return False;
+ }
+
+ if (!string_to_sid(&map->sid, temp)) {
+ DEBUG(1, ("SID string [%s] could not be read as a valid SID\n", temp));
+ return False;
+ }
+
+ if (!smbldap_get_single_pstring(ldap_state->smbldap_state->ldap_struct, entry,
+ get_attr_key2string( groupmap_attr_list, LDAP_ATTR_GROUP_TYPE), temp)) {
+ DEBUG(0, ("init_group_from_ldap: Mandatory attribute %s not found\n",
+ get_attr_key2string( groupmap_attr_list, LDAP_ATTR_GROUP_TYPE)));
+ return False;
+ }
+ map->sid_name_use = (enum SID_NAME_USE)atol(temp);
+
+ if ((map->sid_name_use < SID_NAME_USER) ||
+ (map->sid_name_use > SID_NAME_UNKNOWN)) {
+ DEBUG(0, ("init_group_from_ldap: Unknown Group type: %d\n", map->sid_name_use));
+ return False;
+ }
+
+ if (!smbldap_get_single_pstring(ldap_state->smbldap_state->ldap_struct, entry,
+ get_attr_key2string( groupmap_attr_list, LDAP_ATTR_DISPLAY_NAME), temp)) {
+ temp[0] = '\0';
+ if (!smbldap_get_single_pstring(ldap_state->smbldap_state->ldap_struct, entry,
+ get_attr_key2string( groupmap_attr_list, LDAP_ATTR_CN), temp))
+ {
+ DEBUG(0, ("init_group_from_ldap: Attributes cn not found either \
+for gidNumber(%lu)\n",(unsigned long)map->gid));
+ return False;
+ }
+ }
+ fstrcpy(map->nt_name, temp);
+
+ if (!smbldap_get_single_pstring(ldap_state->smbldap_state->ldap_struct, entry,
+ get_attr_key2string( groupmap_attr_list, LDAP_ATTR_DESC), temp)) {
+ temp[0] = '\0';
+ }
+ fstrcpy(map->comment, temp);
+
+ return True;
+}
+
+/**********************************************************************
+ *********************************************************************/
+
+static BOOL init_ldap_from_group(LDAP *ldap_struct,
+ LDAPMessage *existing,
+ LDAPMod ***mods,
+ const GROUP_MAP *map)
+{
+ pstring tmp;
+
+ if (mods == NULL || map == NULL) {
+ DEBUG(0, ("init_ldap_from_group: NULL parameters found!\n"));
+ return False;
+ }
+
+ *mods = NULL;
+
+ sid_to_string(tmp, &map->sid);
+
+ smbldap_make_mod(ldap_struct, existing, mods,
+ get_attr_key2string(groupmap_attr_list, LDAP_ATTR_GROUP_SID), tmp);
+ pstr_sprintf(tmp, "%i", map->sid_name_use);
+ smbldap_make_mod(ldap_struct, existing, mods,
+ get_attr_key2string(groupmap_attr_list, LDAP_ATTR_GROUP_TYPE), tmp);
+
+ smbldap_make_mod(ldap_struct, existing, mods,
+ get_attr_key2string( groupmap_attr_list, LDAP_ATTR_DISPLAY_NAME), map->nt_name);
+ smbldap_make_mod(ldap_struct, existing, mods,
+ get_attr_key2string( groupmap_attr_list, LDAP_ATTR_DESC), map->comment);
+
+ return True;
+}
+
+/**********************************************************************
+ *********************************************************************/
+
+static NTSTATUS ldapsam_getgroup(struct pdb_methods *methods,
+ const char *filter,
+ GROUP_MAP *map)
+{
+ struct ldapsam_privates *ldap_state =
+ (struct ldapsam_privates *)methods->private_data;
+ LDAPMessage *result = NULL;
+ LDAPMessage *entry = NULL;
+ int count;
+
+ if (ldapsam_search_one_group(ldap_state, filter, &result)
+ != LDAP_SUCCESS) {
+ return NT_STATUS_NO_SUCH_GROUP;
+ }
+
+ count = ldap_count_entries(ldap_state->smbldap_state->ldap_struct, result);
+
+ if (count < 1) {
+ DEBUG(4, ("ldapsam_getgroup: Did not find group\n"));
+ ldap_msgfree(result);
+ return NT_STATUS_NO_SUCH_GROUP;
+ }
+
+ if (count > 1) {
+ DEBUG(1, ("ldapsam_getgroup: Duplicate entries for filter %s: count=%d\n",
+ filter, count));
+ ldap_msgfree(result);
+ return NT_STATUS_NO_SUCH_GROUP;
+ }
+
+ entry = ldap_first_entry(ldap_state->smbldap_state->ldap_struct, result);
+
+ if (!entry) {
+ ldap_msgfree(result);
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ if (!init_group_from_ldap(ldap_state, map, entry)) {
+ DEBUG(1, ("ldapsam_getgroup: init_group_from_ldap failed for group filter %s\n",
+ filter));
+ ldap_msgfree(result);
+ return NT_STATUS_NO_SUCH_GROUP;
+ }
+
+ ldap_msgfree(result);
+ return NT_STATUS_OK;
+}
+
+/**********************************************************************
+ *********************************************************************/
+
+static NTSTATUS ldapsam_getgrsid(struct pdb_methods *methods, GROUP_MAP *map,
+ DOM_SID sid)
+{
+ pstring filter;
+
+ pstr_sprintf(filter, "(&(objectClass=%s)(%s=%s))",
+ LDAP_OBJ_GROUPMAP,
+ get_attr_key2string(groupmap_attr_list, LDAP_ATTR_GROUP_SID),
+ sid_string_static(&sid));
+
+ return ldapsam_getgroup(methods, filter, map);
+}
+
+/**********************************************************************
+ *********************************************************************/
+
+static NTSTATUS ldapsam_getgrgid(struct pdb_methods *methods, GROUP_MAP *map,
+ gid_t gid)
+{
+ pstring filter;
+
+ pstr_sprintf(filter, "(&(objectClass=%s)(%s=%lu))",
+ LDAP_OBJ_GROUPMAP,
+ get_attr_key2string(groupmap_attr_list, LDAP_ATTR_GIDNUMBER),
+ (unsigned long)gid);
+
+ return ldapsam_getgroup(methods, filter, map);
+}
+
+/**********************************************************************
+ *********************************************************************/
+
+static NTSTATUS ldapsam_getgrnam(struct pdb_methods *methods, GROUP_MAP *map,
+ const char *name)
+{
+ pstring filter;
+ char *escape_name = escape_ldap_string_alloc(name);
+
+ if (!escape_name) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ pstr_sprintf(filter, "(&(objectClass=%s)(|(%s=%s)(%s=%s)))",
+ LDAP_OBJ_GROUPMAP,
+ get_attr_key2string(groupmap_attr_list, LDAP_ATTR_DISPLAY_NAME), escape_name,
+ get_attr_key2string(groupmap_attr_list, LDAP_ATTR_CN), escape_name);
+
+ SAFE_FREE(escape_name);
+
+ return ldapsam_getgroup(methods, filter, map);
+}
+
+/**********************************************************************
+ *********************************************************************/
+
+static int ldapsam_search_one_group_by_gid(struct ldapsam_privates *ldap_state,
+ gid_t gid,
+ LDAPMessage **result)
+{
+ pstring filter;
+
+ pstr_sprintf(filter, "(&(|(objectClass=%s)(objectclass=%s))(%s=%lu))",
+ LDAP_OBJ_POSIXGROUP, LDAP_OBJ_IDMAP_ENTRY,
+ get_attr_key2string(groupmap_attr_list, LDAP_ATTR_GIDNUMBER),
+ (unsigned long)gid);
+
+ return ldapsam_search_one_group(ldap_state, filter, result);
+}
+
+/**********************************************************************
+ *********************************************************************/
+
+static NTSTATUS ldapsam_add_group_mapping_entry(struct pdb_methods *methods,
+ GROUP_MAP *map)
+{
+ struct ldapsam_privates *ldap_state =
+ (struct ldapsam_privates *)methods->private_data;
+ LDAPMessage *result = NULL;
+ LDAPMod **mods = NULL;
+ int count;
+
+ char *tmp;
+ pstring dn;
+ LDAPMessage *entry;
+
+ GROUP_MAP dummy;
+
+ int rc;
+
+ if (NT_STATUS_IS_OK(ldapsam_getgrgid(methods, &dummy,
+ map->gid))) {
+ DEBUG(0, ("ldapsam_add_group_mapping_entry: Group %ld already exists in LDAP\n", (unsigned long)map->gid));
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ rc = ldapsam_search_one_group_by_gid(ldap_state, map->gid, &result);
+ if (rc != LDAP_SUCCESS) {
+ ldap_msgfree(result);
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ count = ldap_count_entries(ldap_state->smbldap_state->ldap_struct, result);
+
+ if ( count == 0 ) {
+ /* There's no posixGroup account, let's try to find an
+ * appropriate idmap entry for aliases */
+
+ pstring suffix;
+ pstring filter;
+ char **attr_list;
+
+ ldap_msgfree(result);
+
+ pstrcpy( suffix, lp_ldap_idmap_suffix() );
+ pstr_sprintf(filter, "(&(objectClass=%s)(%s=%u))",
+ LDAP_OBJ_IDMAP_ENTRY, LDAP_ATTRIBUTE_GIDNUMBER,
+ map->gid);
+
+ attr_list = get_attr_list( sidmap_attr_list );
+ rc = smbldap_search(ldap_state->smbldap_state, suffix,
+ LDAP_SCOPE_SUBTREE, filter, attr_list,
+ 0, &result);
+
+ free_attr_list(attr_list);
+
+ if (rc != LDAP_SUCCESS) {
+ DEBUG(3,("Failure looking up entry (%s)\n",
+ ldap_err2string(rc) ));
+ ldap_msgfree(result);
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+ }
+
+ count = ldap_count_entries(ldap_state->smbldap_state->ldap_struct, result);
+ if ( count == 0 ) {
+ ldap_msgfree(result);
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ if (count > 1) {
+ DEBUG(2, ("ldapsam_add_group_mapping_entry: Group %lu must exist exactly once in LDAP\n",
+ (unsigned long)map->gid));
+ ldap_msgfree(result);
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ entry = ldap_first_entry(ldap_state->smbldap_state->ldap_struct, result);
+ tmp = smbldap_get_dn(ldap_state->smbldap_state->ldap_struct, entry);
+ if (!tmp) {
+ ldap_msgfree(result);
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+ pstrcpy(dn, tmp);
+ SAFE_FREE(tmp);
+
+ if (!init_ldap_from_group(ldap_state->smbldap_state->ldap_struct,
+ result, &mods, map)) {
+ DEBUG(0, ("ldapsam_add_group_mapping_entry: init_ldap_from_group failed!\n"));
+ ldap_mods_free(mods, True);
+ ldap_msgfree(result);
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ ldap_msgfree(result);
+
+ if (mods == NULL) {
+ DEBUG(0, ("ldapsam_add_group_mapping_entry: mods is empty\n"));
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ smbldap_set_mod(&mods, LDAP_MOD_ADD, "objectClass", LDAP_OBJ_GROUPMAP );
+
+ rc = smbldap_modify(ldap_state->smbldap_state, dn, mods);
+ ldap_mods_free(mods, True);
+
+ if (rc != LDAP_SUCCESS) {
+ char *ld_error = NULL;
+ ldap_get_option(ldap_state->smbldap_state->ldap_struct, LDAP_OPT_ERROR_STRING,
+ &ld_error);
+ DEBUG(0, ("ldapsam_add_group_mapping_entry: failed to add group %lu error: %s (%s)\n", (unsigned long)map->gid,
+ ld_error ? ld_error : "(unknown)", ldap_err2string(rc)));
+ SAFE_FREE(ld_error);
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ DEBUG(2, ("ldapsam_add_group_mapping_entry: successfully modified group %lu in LDAP\n", (unsigned long)map->gid));
+ return NT_STATUS_OK;
+}
+
+/**********************************************************************
+ *********************************************************************/
+
+static NTSTATUS ldapsam_update_group_mapping_entry(struct pdb_methods *methods,
+ GROUP_MAP *map)
+{
+ struct ldapsam_privates *ldap_state =
+ (struct ldapsam_privates *)methods->private_data;
+ int rc;
+ char *dn = NULL;
+ LDAPMessage *result = NULL;
+ LDAPMessage *entry = NULL;
+ LDAPMod **mods = NULL;
+
+ rc = ldapsam_search_one_group_by_gid(ldap_state, map->gid, &result);
+
+ if (rc != LDAP_SUCCESS) {
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ if (ldap_count_entries(ldap_state->smbldap_state->ldap_struct, result) == 0) {
+ DEBUG(0, ("ldapsam_update_group_mapping_entry: No group to modify!\n"));
+ ldap_msgfree(result);
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ entry = ldap_first_entry(ldap_state->smbldap_state->ldap_struct, result);
+
+ if (!init_ldap_from_group(ldap_state->smbldap_state->ldap_struct,
+ result, &mods, map)) {
+ DEBUG(0, ("ldapsam_update_group_mapping_entry: init_ldap_from_group failed\n"));
+ ldap_msgfree(result);
+ if (mods != NULL)
+ ldap_mods_free(mods,True);
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ if (mods == NULL) {
+ DEBUG(4, ("ldapsam_update_group_mapping_entry: mods is empty: nothing to do\n"));
+ ldap_msgfree(result);
+ return NT_STATUS_OK;
+ }
+
+ dn = smbldap_get_dn(ldap_state->smbldap_state->ldap_struct, entry);
+ if (!dn) {
+ ldap_msgfree(result);
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+ rc = smbldap_modify(ldap_state->smbldap_state, dn, mods);
+ SAFE_FREE(dn);
+
+ ldap_mods_free(mods, True);
+ ldap_msgfree(result);
+
+ if (rc != LDAP_SUCCESS) {
+ char *ld_error = NULL;
+ ldap_get_option(ldap_state->smbldap_state->ldap_struct, LDAP_OPT_ERROR_STRING,
+ &ld_error);
+ DEBUG(0, ("ldapsam_update_group_mapping_entry: failed to modify group %lu error: %s (%s)\n", (unsigned long)map->gid,
+ ld_error ? ld_error : "(unknown)", ldap_err2string(rc)));
+ SAFE_FREE(ld_error);
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ DEBUG(2, ("ldapsam_update_group_mapping_entry: successfully modified group %lu in LDAP\n", (unsigned long)map->gid));
+ return NT_STATUS_OK;
+}
+
+/**********************************************************************
+ *********************************************************************/
+
+static NTSTATUS ldapsam_delete_group_mapping_entry(struct pdb_methods *methods,
+ DOM_SID sid)
+{
+ struct ldapsam_privates *ldap_state = (struct ldapsam_privates *)methods->private_data;
+ pstring sidstring, filter;
+ LDAPMessage *result = NULL;
+ int rc;
+ NTSTATUS ret;
+ char **attr_list;
+
+ sid_to_string(sidstring, &sid);
+
+ pstr_sprintf(filter, "(&(objectClass=%s)(%s=%s))",
+ LDAP_OBJ_GROUPMAP, LDAP_ATTRIBUTE_SID, sidstring);
+
+ rc = ldapsam_search_one_group(ldap_state, filter, &result);
+
+ if (rc != LDAP_SUCCESS) {
+ return NT_STATUS_NO_SUCH_GROUP;
+ }
+
+ attr_list = get_attr_list( groupmap_attr_list_to_delete );
+ ret = ldapsam_delete_entry(ldap_state, result, LDAP_OBJ_GROUPMAP, attr_list);
+ free_attr_list ( attr_list );
+
+ ldap_msgfree(result);
+
+ return ret;
+}
+
+/**********************************************************************
+ *********************************************************************/
+
+static NTSTATUS ldapsam_setsamgrent(struct pdb_methods *my_methods, BOOL update)
+{
+ struct ldapsam_privates *ldap_state = (struct ldapsam_privates *)my_methods->private_data;
+ fstring filter;
+ int rc;
+ char **attr_list;
+
+ pstr_sprintf( filter, "(objectclass=%s)", LDAP_OBJ_GROUPMAP);
+ attr_list = get_attr_list( groupmap_attr_list );
+ rc = smbldap_search(ldap_state->smbldap_state, lp_ldap_group_suffix(),
+ LDAP_SCOPE_SUBTREE, filter,
+ attr_list, 0, &ldap_state->result);
+ free_attr_list( attr_list );
+
+ if (rc != LDAP_SUCCESS) {
+ DEBUG(0, ("ldapsam_setsamgrent: LDAP search failed: %s\n", ldap_err2string(rc)));
+ DEBUG(3, ("ldapsam_setsamgrent: Query was: %s, %s\n", lp_ldap_group_suffix(), filter));
+ ldap_msgfree(ldap_state->result);
+ ldap_state->result = NULL;
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ DEBUG(2, ("ldapsam_setsampwent: %d entries in the base!\n",
+ ldap_count_entries(ldap_state->smbldap_state->ldap_struct,
+ ldap_state->result)));
+
+ ldap_state->entry = ldap_first_entry(ldap_state->smbldap_state->ldap_struct, ldap_state->result);
+ ldap_state->index = 0;
+
+ return NT_STATUS_OK;
+}
+
+/**********************************************************************
+ *********************************************************************/
+
+static void ldapsam_endsamgrent(struct pdb_methods *my_methods)
+{
+ ldapsam_endsampwent(my_methods);
+}
+
+/**********************************************************************
+ *********************************************************************/
+
+static NTSTATUS ldapsam_getsamgrent(struct pdb_methods *my_methods,
+ GROUP_MAP *map)
+{
+ NTSTATUS ret = NT_STATUS_UNSUCCESSFUL;
+ struct ldapsam_privates *ldap_state = (struct ldapsam_privates *)my_methods->private_data;
+ BOOL bret = False;
+
+ while (!bret) {
+ if (!ldap_state->entry)
+ return ret;
+
+ ldap_state->index++;
+ bret = init_group_from_ldap(ldap_state, map, ldap_state->entry);
+
+ ldap_state->entry = ldap_next_entry(ldap_state->smbldap_state->ldap_struct,
+ ldap_state->entry);
+ }
+
+ return NT_STATUS_OK;
+}
+
+/**********************************************************************
+ *********************************************************************/
+
+static NTSTATUS ldapsam_enum_group_mapping(struct pdb_methods *methods,
+ enum SID_NAME_USE sid_name_use,
+ GROUP_MAP **rmap, int *num_entries,
+ BOOL unix_only)
+{
+ GROUP_MAP map;
+ GROUP_MAP *mapt;
+ int entries = 0;
+
+ *num_entries = 0;
+ *rmap = NULL;
+
+ if (!NT_STATUS_IS_OK(ldapsam_setsamgrent(methods, False))) {
+ DEBUG(0, ("ldapsam_enum_group_mapping: Unable to open passdb\n"));
+ return NT_STATUS_ACCESS_DENIED;
+ }
+
+ while (NT_STATUS_IS_OK(ldapsam_getsamgrent(methods, &map))) {
+ if (sid_name_use != SID_NAME_UNKNOWN &&
+ sid_name_use != map.sid_name_use) {
+ DEBUG(11,("ldapsam_enum_group_mapping: group %s is not of the requested type\n", map.nt_name));
+ continue;
+ }
+ if (unix_only==ENUM_ONLY_MAPPED && map.gid==-1) {
+ DEBUG(11,("ldapsam_enum_group_mapping: group %s is non mapped\n", map.nt_name));
+ continue;
+ }
+
+ mapt=(GROUP_MAP *)Realloc((*rmap), (entries+1)*sizeof(GROUP_MAP));
+ if (!mapt) {
+ DEBUG(0,("ldapsam_enum_group_mapping: Unable to enlarge group map!\n"));
+ SAFE_FREE(*rmap);
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+ else
+ (*rmap) = mapt;
+
+ mapt[entries] = map;
+
+ entries += 1;
+
+ }
+ ldapsam_endsamgrent(methods);
+
+ *num_entries = entries;
+
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS ldapsam_modify_aliasmem(struct pdb_methods *methods,
+ const DOM_SID *alias,
+ const DOM_SID *member,
+ int modop)
+{
+ struct ldapsam_privates *ldap_state =
+ (struct ldapsam_privates *)methods->private_data;
+ char *dn;
+ LDAPMessage *result = NULL;
+ LDAPMessage *entry = NULL;
+ int count;
+ LDAPMod **mods = NULL;
+ int rc;
+
+ pstring filter;
+
+ pstr_sprintf(filter, "(&(|(objectClass=%s)(objectclass=%s))(%s=%s))",
+ LDAP_OBJ_GROUPMAP, LDAP_OBJ_IDMAP_ENTRY,
+ get_attr_key2string(groupmap_attr_list,
+ LDAP_ATTR_GROUP_SID),
+ sid_string_static(alias));
+
+ if (ldapsam_search_one_group(ldap_state, filter,
+ &result) != LDAP_SUCCESS)
+ return NT_STATUS_NO_SUCH_ALIAS;
+
+ count = ldap_count_entries(ldap_state->smbldap_state->ldap_struct,
+ result);
+
+ if (count < 1) {
+ DEBUG(4, ("ldapsam_add_aliasmem: Did not find alias\n"));
+ ldap_msgfree(result);
+ return NT_STATUS_NO_SUCH_ALIAS;
+ }
+
+ if (count > 1) {
+ DEBUG(1, ("ldapsam_getgroup: Duplicate entries for filter %s: "
+ "count=%d\n", filter, count));
+ ldap_msgfree(result);
+ return NT_STATUS_NO_SUCH_ALIAS;
+ }
+
+ entry = ldap_first_entry(ldap_state->smbldap_state->ldap_struct,
+ result);
+
+ if (!entry) {
+ ldap_msgfree(result);
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ dn = smbldap_get_dn(ldap_state->smbldap_state->ldap_struct, entry);
+ if (!dn) {
+ ldap_msgfree(result);
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ smbldap_set_mod(&mods, modop,
+ get_attr_key2string(groupmap_attr_list,
+ LDAP_ATTR_SID_LIST),
+ sid_string_static(member));
+
+ rc = smbldap_modify(ldap_state->smbldap_state, dn, mods);
+
+ ldap_mods_free(mods, True);
+ ldap_msgfree(result);
+
+ if (rc != LDAP_SUCCESS) {
+ char *ld_error = NULL;
+ ldap_get_option(ldap_state->smbldap_state->ldap_struct,
+ LDAP_OPT_ERROR_STRING,&ld_error);
+
+ DEBUG(0, ("ldapsam_delete_entry: Could not delete attributes "
+ "for %s, error: %s (%s)\n", dn, ldap_err2string(rc),
+ ld_error?ld_error:"unknown"));
+ SAFE_FREE(ld_error);
+ SAFE_FREE(dn);
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ SAFE_FREE(dn);
+
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS ldapsam_add_aliasmem(struct pdb_methods *methods,
+ const DOM_SID *alias,
+ const DOM_SID *member)
+{
+ return ldapsam_modify_aliasmem(methods, alias, member, LDAP_MOD_ADD);
+}
+
+static NTSTATUS ldapsam_del_aliasmem(struct pdb_methods *methods,
+ const DOM_SID *alias,
+ const DOM_SID *member)
+{
+ return ldapsam_modify_aliasmem(methods, alias, member,
+ LDAP_MOD_DELETE);
+}
+
+static NTSTATUS ldapsam_enum_aliasmem(struct pdb_methods *methods,
+ const DOM_SID *alias, DOM_SID **members,
+ int *num_members)
+{
+ struct ldapsam_privates *ldap_state =
+ (struct ldapsam_privates *)methods->private_data;
+ LDAPMessage *result = NULL;
+ LDAPMessage *entry = NULL;
+ int count;
+ char **values;
+ int i;
+ pstring filter;
+
+ *members = NULL;
+ *num_members = 0;
+
+ pstr_sprintf(filter, "(&(|(objectClass=%s)(objectclass=%s))(%s=%s))",
+ LDAP_OBJ_GROUPMAP, LDAP_OBJ_IDMAP_ENTRY,
+ get_attr_key2string(groupmap_attr_list,
+ LDAP_ATTR_GROUP_SID),
+ sid_string_static(alias));
+
+ if (ldapsam_search_one_group(ldap_state, filter,
+ &result) != LDAP_SUCCESS)
+ return NT_STATUS_NO_SUCH_ALIAS;
+
+ count = ldap_count_entries(ldap_state->smbldap_state->ldap_struct,
+ result);
+
+ if (count < 1) {
+ DEBUG(4, ("ldapsam_add_aliasmem: Did not find alias\n"));
+ ldap_msgfree(result);
+ return NT_STATUS_NO_SUCH_ALIAS;
+ }
+
+ if (count > 1) {
+ DEBUG(1, ("ldapsam_getgroup: Duplicate entries for filter %s: "
+ "count=%d\n", filter, count));
+ ldap_msgfree(result);
+ return NT_STATUS_NO_SUCH_ALIAS;
+ }
+
+ entry = ldap_first_entry(ldap_state->smbldap_state->ldap_struct,
+ result);
+
+ if (!entry) {
+ ldap_msgfree(result);
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ values = ldap_get_values(ldap_state->smbldap_state->ldap_struct,
+ entry,
+ get_attr_key2string(groupmap_attr_list,
+ LDAP_ATTR_SID_LIST));
+
+ if (values == NULL) {
+ ldap_msgfree(result);
+ return NT_STATUS_OK;
+ }
+
+ count = ldap_count_values(values);
+
+ for (i=0; i<count; i++) {
+ DOM_SID member;
+
+ if (!string_to_sid(&member, values[i]))
+ continue;
+
+ add_sid_to_array(&member, members, num_members);
+ }
+
+ ldap_value_free(values);
+ ldap_msgfree(result);
+
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS ldapsam_alias_memberships(struct pdb_methods *methods,
+ const DOM_SID *sid,
+ DOM_SID **aliases, int *num)
+{
+ struct ldapsam_privates *ldap_state =
+ (struct ldapsam_privates *)methods->private_data;
+
+ fstring sid_string;
+ const char *attrs[] = { LDAP_ATTRIBUTE_SID, NULL };
+
+ LDAPMessage *result = NULL;
+ LDAPMessage *entry = NULL;
+ int count;
+ int rc;
+ pstring filter;
+
+ sid_to_string(sid_string, sid);
+ pstr_sprintf(filter, "(&(|(objectclass=%s)(objectclass=%s))(%s=%s))",
+ LDAP_OBJ_GROUPMAP, LDAP_OBJ_IDMAP_ENTRY,
+ get_attr_key2string(groupmap_attr_list,
+ LDAP_ATTR_SID_LIST), sid_string);
+
+ rc = smbldap_search(ldap_state->smbldap_state, lp_ldap_group_suffix(),
+ LDAP_SCOPE_SUBTREE, filter, attrs, 0, &result);
+
+ if (rc != LDAP_SUCCESS)
+ return NT_STATUS_UNSUCCESSFUL;
+
+ *aliases = NULL;
+ *num = 0;
+
+ count = ldap_count_entries(ldap_state->smbldap_state->ldap_struct,
+ result);
+
+ if (count < 1) {
+ ldap_msgfree(result);
+ return NT_STATUS_OK;
+ }
+
+
+ for (entry = ldap_first_entry(ldap_state->smbldap_state->ldap_struct,
+ result);
+ entry != NULL;
+ entry = ldap_next_entry(ldap_state->smbldap_state->ldap_struct,
+ entry))
+ {
+ DOM_SID alias;
+ char **vals;
+ vals = ldap_get_values(ldap_state->smbldap_state->ldap_struct,
+ entry, LDAP_ATTRIBUTE_SID);
+
+ if (vals == NULL)
+ continue;
+
+ if (vals[0] == NULL) {
+ ldap_value_free(vals);
+ continue;
+ }
+
+ if (!string_to_sid(&alias, vals[0])) {
+ ldap_value_free(vals);
+ continue;
+ }
+
+ add_sid_to_array(&alias, aliases, num);
+ ldap_value_free(vals);
+ }
+
+ ldap_msgfree(result);
+ return NT_STATUS_OK;
+}
+
+/**********************************************************************
+ Privileges related functions
+ *********************************************************************/
+
+static NTSTATUS ldapsam_modify_sid_list_for_privilege(struct pdb_methods *my_methods, const char *privname, const DOM_SID *sid, int ldap_op)
+{
+ struct ldapsam_privates *ldap_state = (struct ldapsam_privates *)my_methods->private_data;
+ NTSTATUS ret = NT_STATUS_UNSUCCESSFUL;
+ LDAPMessage *entry = NULL;
+ LDAPMod **mods = NULL;
+ fstring sid_str;
+ fstring filter;
+ char **attr_list, *dn;
+ int rc;
+
+ if ((sid == NULL) || (!sid_to_string(sid_str, sid))) {
+ DEBUG(3, ("ldapsam_modify_sid_list_for_privilege: Invalid SID\n"));
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ pstr_sprintf(filter, "(&(objectclass=%s)(sambaPrivName=%s))", LDAP_OBJ_PRIVILEGE, privname);
+ attr_list = get_attr_list(privilege_attr_list);
+ rc = smbldap_search(ldap_state->smbldap_state, lp_ldap_privilege_suffix(),
+ LDAP_SCOPE_SUBTREE, filter,
+ attr_list, 0, &ldap_state->result);
+ free_attr_list(attr_list);
+
+
+ if (rc != LDAP_SUCCESS) {
+ DEBUG(0, ("ldapsam_modify_sid_list_for_privilege: LDAP search failed: %s\n", ldap_err2string(rc)));
+ DEBUG(3, ("ldapsam_modify_sid_list_for_privilege: Query was: %s, %s\n", lp_ldap_privilege_suffix(), filter));
+ ldap_msgfree(ldap_state->result);
+ ldap_state->result = NULL;
+ goto done;
+ }
+
+ if (ldap_count_entries(ldap_state->smbldap_state->ldap_struct, ldap_state->result) == 0) {
+ /* if the privilege does not exist and we are adding then
+ * create it */
+ if (ldap_op == LDAP_MOD_ADD) {
+
+ DEBUG(3, ("Privilege not found on ldap tree, creating a new entry\n"));
+ if (asprintf(&dn, "sambaPrivName=%s,%s", privname, lp_ldap_privilege_suffix()) < 0) {
+ DEBUG(0, ("ldapsam_modify_sid_list_for_privilege: Out of memory\n"));
+ goto done;
+ }
+
+ smbldap_make_mod(ldap_state->smbldap_state->ldap_struct, entry, &mods, "sambaPrivName", privname);
+
+ smbldap_set_mod(&mods, LDAP_MOD_ADD, "objectclass", LDAP_OBJ_PRIVILEGE);
+
+ rc = smbldap_add(ldap_state->smbldap_state, dn, mods);
+
+ if (rc != LDAP_SUCCESS) {
+ char *ld_error = NULL;
+
+ ldap_get_option(ldap_state->smbldap_state->ldap_struct, LDAP_OPT_ERROR_STRING, &ld_error);
+ DEBUG(1,
+ ("ldapsam_modify_sid_list_for_privilege:"
+ "Failed to add privilege (%s) dn= %s with: %s\n\t%s\n",
+ privname,
+ dn, ldap_err2string(rc),
+ ld_error ? ld_error : "unknown")
+ );
+
+ SAFE_FREE(ld_error);
+ goto done;
+ }
+
+ pstr_sprintf(filter, "(&(objectclass=%s)(sambaPrivName=%s))", LDAP_OBJ_PRIVILEGE, privname);
+ attr_list = get_attr_list(privilege_attr_list);
+ rc = smbldap_search(ldap_state->smbldap_state, lp_ldap_privilege_suffix(),
+ LDAP_SCOPE_SUBTREE, filter,
+ attr_list, 0, &ldap_state->result);
+ free_attr_list(attr_list);
+
+ if (rc != LDAP_SUCCESS) {
+ DEBUG(0, ("ldapsam_modify_sid_list_for_privilege: LDAP search failed: %s\n", ldap_err2string(rc)));
+ DEBUG(3, ("ldapsam_modify_sid_list_for_privilege: Query was: %s, %s\n", lp_ldap_privilege_suffix(), filter));
+ ldap_msgfree(ldap_state->result);
+ ldap_state->result = NULL;
+ goto done;
+ }
+ } else {
+ goto done;
+ }
+ }
+ /* entry found */
+ entry = ldap_first_entry(ldap_state->smbldap_state->ldap_struct, ldap_state->result);
+
+ /* retrieve the dn */
+ dn = smbldap_get_dn(ldap_state->smbldap_state->ldap_struct, entry);
+ if (!dn) {
+ goto done;
+ }
+
+ /* prepare the modification */
+ smbldap_set_mod(&mods, ldap_op, "sambaSIDList", sid_str);
+
+ /* modify the privilege */
+ rc = smbldap_modify(ldap_state->smbldap_state, dn, mods);
+
+ /* free used structures */
+ ldap_mods_free(mods, True);
+
+ if (rc != LDAP_SUCCESS) {
+ char *ld_error = NULL;
+
+ ldap_get_option(ldap_state->smbldap_state->ldap_struct, LDAP_OPT_ERROR_STRING, &ld_error);
+ DEBUG(1,
+ ("ldapsam_modify_sid_list_for_privilege:"
+ "Failed to %s sid for privilege (%s) dn= %s with: %s\n\t%s\n",
+ (ldap_op == LDAP_MOD_ADD) ? "add" : "remove",
+ privname,
+ dn, ldap_err2string(rc),
+ ld_error ? ld_error : "unknown")
+ );
+ SAFE_FREE(ld_error);
+ goto done;
+ }
+
+ ret = NT_STATUS_OK;
+
+done:
+ return ret;
+}
+
+static NTSTATUS ldapsam_add_sid_to_privilege(struct pdb_methods *my_methods, const char *privname, const DOM_SID *sid)
+{
+ return ldapsam_modify_sid_list_for_privilege(my_methods, privname, sid, LDAP_MOD_ADD);
+}
+
+static NTSTATUS ldapsam_remove_sid_from_privilege(struct pdb_methods *my_methods, const char *privname, const DOM_SID *sid)
+{
+ return ldapsam_modify_sid_list_for_privilege(my_methods, privname, sid, LDAP_MOD_DELETE);
+}
+
+static NTSTATUS ldapsam_get_privilege_set(struct pdb_methods *my_methods, DOM_SID *user_sids, int num_sids, PRIVILEGE_SET *privset)
+{
+ struct ldapsam_privates *ldap_state = (struct ldapsam_privates *)my_methods->private_data;
+ NTSTATUS ret = NT_STATUS_UNSUCCESSFUL;
+ LDAPMessage *entry = NULL;
+ fstring sid_str;
+ fstring filter;
+ char **sid_list;
+ char **attr_list;
+ int rc, i;
+
+ sid_list = (char **)malloc(sizeof(char *) * (num_sids + 1));
+ for (i = 0; i < num_sids; i++) {
+ sid_to_string(sid_str, &user_sids[i]);
+ sid_list[i] = strdup(sid_str);
+ if ( ! sid_list[i]) {
+ ret = NT_STATUS_NO_MEMORY;
+ goto done;
+ }
+ }
+ sid_list[i] = NULL;
+
+ pstr_sprintf(filter, "(objectclass=%s)", LDAP_OBJ_PRIVILEGE);
+ attr_list = get_attr_list(privilege_attr_list);
+ rc = smbldap_search(ldap_state->smbldap_state, lp_ldap_privilege_suffix(),
+ LDAP_SCOPE_SUBTREE, filter,
+ attr_list, 0, &ldap_state->result);
+ free_attr_list(attr_list);
+
+ if (rc != LDAP_SUCCESS) {
+ DEBUG(0, ("ldapsam_get_privilege_set: LDAP search failed: %s\n", ldap_err2string(rc)));
+ DEBUG(3, ("ldapsam_get_privilege_set: Query was: %s, %s\n", lp_ldap_privilege_suffix(), filter));
+ ldap_msgfree(ldap_state->result);
+ ldap_state->result = NULL;
+ goto done;
+ }
+
+ if (ldap_count_entries(ldap_state->smbldap_state->ldap_struct, ldap_state->result) == 0) {
+ DEBUG(3, ("ldapsam_get_privilege_set: No privileges in ldap tree\n"));
+ ret = NT_STATUS_OK;
+ goto done;
+ }
+
+ DEBUG(2, ("ldapsam_get_privilege_set: %d entries in the base!\n",
+ ldap_count_entries(ldap_state->smbldap_state->ldap_struct, ldap_state->result)));
+
+ entry = ldap_first_entry(ldap_state->smbldap_state->ldap_struct, ldap_state->result);
+
+ while (entry != NULL) {
+ char **values = NULL;
+
+ for(i=0; sid_list[i] != NULL; i++) {
+ pstring privname;
+ int j;
+
+ if (!smbldap_get_single_attribute(ldap_state->smbldap_state->ldap_struct, entry, "sambaPrivName", privname, sizeof(pstring))) {
+ goto loop;
+ }
+
+ if ((values = ldap_get_values(ldap_state->smbldap_state->ldap_struct, entry, LDAP_ATTRIBUTE_SID_LIST)) == NULL) {
+ DEBUG(10, ("ldapsam_get_privilege_set: SID List not found skipping privilege\n"));
+ goto loop;
+ }
+
+ j = 0;
+ while (values[j] != 0) {
+ if (strcmp(values[j], sid_list[i]) == 0) {
+ DEBUG(10, ("sid [%s] found in users sid list\n", sid_list[i]));
+ DEBUG(10, ("adding privilege [%s] to the users privilege list\n", privname));
+ add_privilege_by_name(privset, privname);
+ goto loop;
+ }
+ j++;
+ }
+
+ if (values) {
+ ldap_value_free(values);
+ values = NULL;
+ }
+ }
+ loop:
+ if (values) {
+ ldap_value_free(values);
+ }
+
+ entry = ldap_next_entry(ldap_state->smbldap_state->ldap_struct, entry);
+ }
+
+ ret = NT_STATUS_OK;
+
+done:
+ i = 0;
+ while (sid_list[i]) {
+ free(sid_list[i]);
+ i++;
+ }
+ free(sid_list);
+
+ return ret;
+}
+
+static NTSTATUS ldapsam_get_privilege_entry(struct pdb_methods *my_methods, const char *privname,
+ char **sid_list)
+{
+ struct ldapsam_privates *ldap_state = (struct ldapsam_privates *)my_methods->private_data;
+ NTSTATUS ret = NT_STATUS_UNSUCCESSFUL;
+ LDAPMessage *entry = NULL;
+ fstring filter;
+ char **attr_list, **values;
+ int rc, i, len;
+
+ *sid_list = NULL;
+ pstr_sprintf(filter, "(&(objectclass=%s)(sambaPrivName=%s))", LDAP_OBJ_PRIVILEGE, privname);
+ attr_list = get_attr_list(privilege_attr_list);
+ rc = smbldap_search(ldap_state->smbldap_state, lp_ldap_privilege_suffix(),
+ LDAP_SCOPE_SUBTREE, filter,
+ attr_list, 0, &ldap_state->result);
+ free_attr_list(attr_list);
+
+ if (rc != LDAP_SUCCESS) {
+ DEBUG(0, ("ldapsam_get_privilege_entry: LDAP search failed: %s\n", ldap_err2string(rc)));
+ DEBUG(3, ("ldapsam_get_privilege_entry: Query was: %s, %s\n", lp_ldap_privilege_suffix(), filter));
+ ldap_msgfree(ldap_state->result);
+ ldap_state->result = NULL;
+ ret = NT_STATUS_UNSUCCESSFUL;
+ goto done;
+ }
+
+ if (ldap_count_entries(ldap_state->smbldap_state->ldap_struct, ldap_state->result) == 0) {
+ DEBUG(3, ("ldapsam_get_privilege_entry: No such privilege (%s) in ldap tree\n", privname));
+ goto done;
+ }
+
+ entry = ldap_first_entry(ldap_state->smbldap_state->ldap_struct, ldap_state->result);
+
+ if ((values = ldap_get_values(ldap_state->smbldap_state->ldap_struct, entry, LDAP_ATTRIBUTE_SID_LIST)) == NULL) {
+ DEBUG(10, ("ldapsam_get_privilege_entry: SID List not found skipping privilege\n"));
+ ret = NT_STATUS_OK;
+ goto done;
+ }
+
+ for (i = 0, len = 0; values[i] != 0; i++ ) {
+ len = len + strlen(values[i]) + 1;
+ }
+
+ *sid_list = (char *)malloc(len);
+ if ((*sid_list) == NULL) {
+ DEBUG(0, ("ldapsam_get_privilege_entry: Out of memory!\n"));
+ ldap_value_free(values);
+ ret = NT_STATUS_NO_MEMORY;
+ goto done;
+ }
+
+ (*sid_list)[0] = '\0';
+
+ for (i = 0; values[i] != 0; i++ ) {
+ if (i != 0) {
+ strlcat(*sid_list, ",", len);
+ }
+ strlcat(*sid_list, values[i], len);
+ }
+
+ ldap_value_free(values);
+ ret = NT_STATUS_OK;
+done:
+ return ret;
+}
+
+
+/**********************************************************************
+ Housekeeping
+ *********************************************************************/
+
+static void free_private_data(void **vp)
+{
+ struct ldapsam_privates **ldap_state = (struct ldapsam_privates **)vp;
+
+ smbldap_free_struct(&(*ldap_state)->smbldap_state);
+
+ if ((*ldap_state)->result != NULL) {
+ ldap_msgfree((*ldap_state)->result);
+ (*ldap_state)->result = NULL;
+ }
+
+ *ldap_state = NULL;
+
+ /* No need to free any further, as it is talloc()ed */
+}
+
+/**********************************************************************
+ Intitalise the parts of the pdb_context that are common to all pdb_ldap modes
+ *********************************************************************/
+
+static NTSTATUS pdb_init_ldapsam_common(PDB_CONTEXT *pdb_context, PDB_METHODS **pdb_method,
+ const char *location)
+{
+ NTSTATUS nt_status;
+ struct ldapsam_privates *ldap_state;
+
+ if (!NT_STATUS_IS_OK(nt_status = make_pdb_methods(pdb_context->mem_ctx, pdb_method))) {
+ return nt_status;
+ }
+
+ (*pdb_method)->name = "ldapsam";
+
+ (*pdb_method)->setsampwent = ldapsam_setsampwent;
+ (*pdb_method)->endsampwent = ldapsam_endsampwent;
+ (*pdb_method)->getsampwent = ldapsam_getsampwent;
+ (*pdb_method)->getsampwnam = ldapsam_getsampwnam;
+ (*pdb_method)->getsampwsid = ldapsam_getsampwsid;
+ (*pdb_method)->add_sam_account = ldapsam_add_sam_account;
+ (*pdb_method)->update_sam_account = ldapsam_update_sam_account;
+ (*pdb_method)->delete_sam_account = ldapsam_delete_sam_account;
+
+ (*pdb_method)->getgrsid = ldapsam_getgrsid;
+ (*pdb_method)->getgrgid = ldapsam_getgrgid;
+ (*pdb_method)->getgrnam = ldapsam_getgrnam;
+ (*pdb_method)->add_group_mapping_entry = ldapsam_add_group_mapping_entry;
+ (*pdb_method)->update_group_mapping_entry = ldapsam_update_group_mapping_entry;
+ (*pdb_method)->delete_group_mapping_entry = ldapsam_delete_group_mapping_entry;
+ (*pdb_method)->enum_group_mapping = ldapsam_enum_group_mapping;
+
+ (*pdb_method)->add_sid_to_privilege = ldapsam_add_sid_to_privilege;
+ (*pdb_method)->remove_sid_from_privilege = ldapsam_remove_sid_from_privilege;
+ (*pdb_method)->get_privilege_set = ldapsam_get_privilege_set;
+ (*pdb_method)->get_privilege_entry = ldapsam_get_privilege_entry;
+
+ /* TODO: Setup private data and free */
+
+ ldap_state = talloc_zero(pdb_context->mem_ctx, sizeof(*ldap_state));
+ if (!ldap_state) {
+ DEBUG(0, ("pdb_init_ldapsam_common: talloc() failed for ldapsam private_data!\n"));
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ if (!NT_STATUS_IS_OK(nt_status =
+ smbldap_init(pdb_context->mem_ctx, location,
+ &ldap_state->smbldap_state)));
+
+ ldap_state->domain_name = talloc_strdup(pdb_context->mem_ctx, get_global_sam_name());
+ if (!ldap_state->domain_name) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ (*pdb_method)->private_data = ldap_state;
+
+ (*pdb_method)->free_private_data = free_private_data;
+
+ return NT_STATUS_OK;
+}
+
+/**********************************************************************
+ Initialise the 'compat' mode for pdb_ldap
+ *********************************************************************/
+
+static NTSTATUS pdb_init_ldapsam_compat(PDB_CONTEXT *pdb_context, PDB_METHODS **pdb_method, const char *location)
+{
+ NTSTATUS nt_status;
+ struct ldapsam_privates *ldap_state;
+
+#ifdef WITH_LDAP_SAMCONFIG
+ if (!location) {
+ int ldap_port = lp_ldap_port();
+
+ /* remap default port if not using SSL (ie clear or TLS) */
+ if ( (lp_ldap_ssl() != LDAP_SSL_ON) && (ldap_port == 636) ) {
+ ldap_port = 389;
+ }
+
+ location = talloc_asprintf(pdb_context->mem_ctx, "%s://%s:%d", lp_ldap_ssl() == LDAP_SSL_ON ? "ldaps" : "ldap", lp_ldap_server(), ldap_port);
+ if (!location) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ }
+#endif
+
+ if (!NT_STATUS_IS_OK(nt_status = pdb_init_ldapsam_common(pdb_context, pdb_method, location))) {
+ return nt_status;
+ }
+
+ (*pdb_method)->name = "ldapsam_compat";
+
+ ldap_state = (*pdb_method)->private_data;
+ ldap_state->schema_ver = SCHEMAVER_SAMBAACCOUNT;
+
+ sid_copy(&ldap_state->domain_sid, get_global_sam_sid());
+
+ return NT_STATUS_OK;
+}
+
+/**********************************************************************
+ Initialise the normal mode for pdb_ldap
+ *********************************************************************/
+
+static NTSTATUS pdb_init_ldapsam(PDB_CONTEXT *pdb_context, PDB_METHODS **pdb_method, const char *location)
+{
+ NTSTATUS nt_status;
+ struct ldapsam_privates *ldap_state;
+ uint32 alg_rid_base;
+ pstring alg_rid_base_string;
+ LDAPMessage *result = NULL;
+ LDAPMessage *entry = NULL;
+ DOM_SID ldap_domain_sid;
+ DOM_SID secrets_domain_sid;
+ pstring domain_sid_string;
+
+ if (!NT_STATUS_IS_OK(nt_status = pdb_init_ldapsam_common(pdb_context, pdb_method, location))) {
+ return nt_status;
+ }
+
+ (*pdb_method)->name = "ldapsam";
+
+ (*pdb_method)->add_aliasmem = ldapsam_add_aliasmem;
+ (*pdb_method)->del_aliasmem = ldapsam_del_aliasmem;
+ (*pdb_method)->enum_aliasmem = ldapsam_enum_aliasmem;
+ (*pdb_method)->enum_alias_memberships = ldapsam_alias_memberships;
+
+ ldap_state = (*pdb_method)->private_data;
+ ldap_state->schema_ver = SCHEMAVER_SAMBASAMACCOUNT;
+
+ /* Try to setup the Domain Name, Domain SID, algorithmic rid base */
+
+ nt_status = smbldap_search_domain_info(ldap_state->smbldap_state, &result,
+ ldap_state->domain_name, True);
+
+ if ( !NT_STATUS_IS_OK(nt_status) ) {
+ DEBUG(2, ("pdb_init_ldapsam: WARNING: Could not get domain info, nor add one to the domain\n"));
+ DEBUGADD(2, ("pdb_init_ldapsam: Continuing on regardless, will be unable to allocate new users/groups, \
+and will risk BDCs having inconsistant SIDs\n"));
+ sid_copy(&ldap_state->domain_sid, get_global_sam_sid());
+ return NT_STATUS_OK;
+ }
+
+ /* Given that the above might fail, everything below this must be optional */
+
+ entry = ldap_first_entry(ldap_state->smbldap_state->ldap_struct, result);
+ if (!entry) {
+ DEBUG(0, ("pdb_init_ldapsam: Could not get domain info entry\n"));
+ ldap_msgfree(result);
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ if (smbldap_get_single_pstring(ldap_state->smbldap_state->ldap_struct, entry,
+ get_userattr_key2string(ldap_state->schema_ver, LDAP_ATTR_USER_SID),
+ domain_sid_string)) {
+ BOOL found_sid;
+ if (!string_to_sid(&ldap_domain_sid, domain_sid_string)) {
+ DEBUG(1, ("pdb_init_ldapsam: SID [%s] could not be read as a valid SID\n", domain_sid_string));
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+ found_sid = secrets_fetch_domain_sid(ldap_state->domain_name, &secrets_domain_sid);
+ if (!found_sid || !sid_equal(&secrets_domain_sid, &ldap_domain_sid)) {
+ fstring new_sid_str, old_sid_str;
+ DEBUG(1, ("pdb_init_ldapsam: Resetting SID for domain %s based on pdb_ldap results %s -> %s\n",
+ ldap_state->domain_name,
+ sid_to_string(old_sid_str, &secrets_domain_sid),
+ sid_to_string(new_sid_str, &ldap_domain_sid)));
+
+ /* reset secrets.tdb sid */
+ secrets_store_domain_sid(ldap_state->domain_name, &ldap_domain_sid);
+ DEBUG(1, ("New global sam SID: %s\n", sid_to_string(new_sid_str, get_global_sam_sid())));
+ }
+ sid_copy(&ldap_state->domain_sid, &ldap_domain_sid);
+ }
+
+ if (smbldap_get_single_pstring(ldap_state->smbldap_state->ldap_struct, entry,
+ get_userattr_key2string(ldap_state->schema_ver, LDAP_ATTR_ALGORITHMIC_RID_BASE),
+ alg_rid_base_string)) {
+ alg_rid_base = (uint32)atol(alg_rid_base_string);
+ if (alg_rid_base != algorithmic_rid_base()) {
+ DEBUG(0, ("The value of 'algorithmic RID base' has changed since the LDAP\n"
+ "database was initialised. Aborting. \n"));
+ ldap_msgfree(result);
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+ }
+ ldap_msgfree(result);
+
+ return NT_STATUS_OK;
+}
+
+NTSTATUS pdb_ldap_init(void)
+{
+ NTSTATUS nt_status;
+ if (!NT_STATUS_IS_OK(nt_status = smb_register_passdb(PASSDB_INTERFACE_VERSION, "ldapsam", pdb_init_ldapsam)))
+ return nt_status;
+
+ if (!NT_STATUS_IS_OK(nt_status = smb_register_passdb(PASSDB_INTERFACE_VERSION, "ldapsam_compat", pdb_init_ldapsam_compat)))
+ return nt_status;
+
+ return NT_STATUS_OK;
+}
diff --git a/source/passdb/pdb_mysql.c b/source/passdb/pdb_mysql.c
new file mode 100644
index 00000000000..deed27dbe49
--- /dev/null
+++ b/source/passdb/pdb_mysql.c
@@ -0,0 +1,497 @@
+/*
+ * MySQL password backend for samba
+ * Copyright (C) Jelmer Vernooij 2002-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 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 <mysql/mysql.h>
+
+#define CONFIG_HOST_DEFAULT "localhost"
+#define CONFIG_USER_DEFAULT "samba"
+#define CONFIG_PASS_DEFAULT ""
+#define CONFIG_PORT_DEFAULT "3306"
+#define CONFIG_DB_DEFAULT "samba"
+
+static int mysqlsam_debug_level = DBGC_ALL;
+
+#undef DBGC_CLASS
+#define DBGC_CLASS mysqlsam_debug_level
+
+typedef struct pdb_mysql_data {
+ MYSQL *handle;
+ MYSQL_RES *pwent;
+ const char *location;
+} pdb_mysql_data;
+
+#define SET_DATA(data,methods) { \
+ if(!methods){ \
+ DEBUG(0, ("invalid methods!\n")); \
+ return NT_STATUS_INVALID_PARAMETER; \
+ } \
+ data = (struct pdb_mysql_data *)methods->private_data; \
+ if(!data || !(data->handle)){ \
+ DEBUG(0, ("invalid handle!\n")); \
+ return NT_STATUS_INVALID_HANDLE; \
+ } \
+}
+
+#define config_value( data, name, default_value ) \
+ lp_parm_const_string( GLOBAL_SECTION_SNUM, (data)->location, name, default_value )
+
+static long xatol(const char *d)
+{
+ if(!d) return 0;
+ return atol(d);
+}
+
+static NTSTATUS row_to_sam_account(MYSQL_RES * r, SAM_ACCOUNT * u)
+{
+ MYSQL_ROW row;
+ pstring temp;
+ unsigned int num_fields;
+ DOM_SID sid;
+
+ num_fields = mysql_num_fields(r);
+ row = mysql_fetch_row(r);
+ if (!row)
+ return NT_STATUS_INVALID_PARAMETER;
+
+ pdb_set_logon_time(u, xatol(row[0]), PDB_SET);
+ pdb_set_logoff_time(u, xatol(row[1]), PDB_SET);
+ pdb_set_kickoff_time(u, xatol(row[2]), PDB_SET);
+ pdb_set_pass_last_set_time(u, xatol(row[3]), PDB_SET);
+ pdb_set_pass_can_change_time(u, xatol(row[4]), PDB_SET);
+ pdb_set_pass_must_change_time(u, xatol(row[5]), PDB_SET);
+ pdb_set_username(u, row[6], PDB_SET);
+ pdb_set_domain(u, row[7], PDB_SET);
+ pdb_set_nt_username(u, row[8], PDB_SET);
+ pdb_set_fullname(u, row[9], PDB_SET);
+ pdb_set_homedir(u, row[10], PDB_SET);
+ pdb_set_dir_drive(u, row[11], PDB_SET);
+ pdb_set_logon_script(u, row[12], PDB_SET);
+ pdb_set_profile_path(u, row[13], PDB_SET);
+ pdb_set_acct_desc(u, row[14], PDB_SET);
+ pdb_set_workstations(u, row[15], PDB_SET);
+ pdb_set_unknown_str(u, row[16], PDB_SET);
+ pdb_set_munged_dial(u, row[17], PDB_SET);
+
+ if(!row[18] || !string_to_sid(&sid, row[18])) {
+ DEBUG(0,("No user SID retrieved from database!\n"));
+ } else {
+ pdb_set_user_sid(u, &sid, PDB_SET);
+ }
+
+ if(row[19]) {
+ string_to_sid(&sid, row[19]);
+ pdb_set_group_sid(u, &sid, PDB_SET);
+ }
+
+ if (pdb_gethexpwd(row[20], temp))
+ pdb_set_lanman_passwd(u, temp, PDB_SET);
+ if (pdb_gethexpwd(row[21], temp))
+ pdb_set_nt_passwd(u, temp, PDB_SET);
+
+ /* Only use plaintext password storage when lanman and nt are
+ * NOT used */
+ if (!row[20] || !row[21])
+ pdb_set_plaintext_passwd(u, row[22]);
+
+ pdb_set_acct_ctrl(u, xatol(row[23]), PDB_SET);
+ pdb_set_logon_divs(u, xatol(row[25]), PDB_SET);
+ pdb_set_hours_len(u, xatol(row[26]), PDB_SET);
+ pdb_set_bad_password_count(u, xatol(row[27]), PDB_SET);
+ pdb_set_logon_count(u, xatol(row[28]), PDB_SET);
+ pdb_set_unknown_6(u, xatol(row[29]), PDB_SET);
+
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS mysqlsam_setsampwent(struct pdb_methods *methods, BOOL update)
+{
+ struct pdb_mysql_data *data =
+ (struct pdb_mysql_data *) methods->private_data;
+ char *query;
+ int ret;
+
+ if (!data || !(data->handle)) {
+ DEBUG(0, ("invalid handle!\n"));
+ return NT_STATUS_INVALID_HANDLE;
+ }
+
+ query = sql_account_query_select(data->location, update, SQL_SEARCH_NONE, NULL);
+
+ ret = mysql_query(data->handle, query);
+ SAFE_FREE(query);
+
+ if (ret) {
+ DEBUG(0,
+ ("Error executing MySQL query %s\n", mysql_error(data->handle)));
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ data->pwent = mysql_store_result(data->handle);
+
+ if (data->pwent == NULL) {
+ DEBUG(0,
+ ("Error storing results: %s\n", mysql_error(data->handle)));
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ DEBUG(5,
+ ("mysqlsam_setsampwent succeeded(%llu results)!\n",
+ mysql_num_rows(data->pwent)));
+
+ return NT_STATUS_OK;
+}
+
+/***************************************************************
+ End enumeration of the passwd list.
+ ****************************************************************/
+
+static void mysqlsam_endsampwent(struct pdb_methods *methods)
+{
+ struct pdb_mysql_data *data =
+ (struct pdb_mysql_data *) methods->private_data;
+
+ if (data == NULL) {
+ DEBUG(0, ("invalid handle!\n"));
+ return;
+ }
+
+ if (data->pwent != NULL)
+ mysql_free_result(data->pwent);
+
+ data->pwent = NULL;
+
+ DEBUG(5, ("mysql_endsampwent called\n"));
+}
+
+/*****************************************************************
+ Get one SAM_ACCOUNT from the list (next in line)
+ *****************************************************************/
+
+static NTSTATUS mysqlsam_getsampwent(struct pdb_methods *methods, SAM_ACCOUNT * user)
+{
+ struct pdb_mysql_data *data;
+
+ SET_DATA(data, methods);
+
+ if (data->pwent == NULL) {
+ DEBUG(0, ("invalid pwent\n"));
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ return row_to_sam_account(data->pwent, user);
+}
+
+static NTSTATUS mysqlsam_select_by_field(struct pdb_methods * methods, SAM_ACCOUNT * user,
+ enum sql_search_field field, const char *sname)
+{
+ char *esc_sname;
+ char *query;
+ NTSTATUS ret;
+ MYSQL_RES *res;
+ int mysql_ret;
+ struct pdb_mysql_data *data;
+ char *tmp_sname;
+
+ SET_DATA(data, methods);
+
+ esc_sname = malloc(strlen(sname) * 2 + 1);
+ if (!esc_sname) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ tmp_sname = smb_xstrdup(sname);
+
+ /* Escape sname */
+ mysql_real_escape_string(data->handle, esc_sname, tmp_sname,
+ strlen(tmp_sname));
+
+ SAFE_FREE(tmp_sname);
+
+ if (user == NULL) {
+ DEBUG(0, ("pdb_getsampwnam: SAM_ACCOUNT is NULL.\n"));
+ SAFE_FREE(esc_sname);
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ query = sql_account_query_select(data->location, True, field, esc_sname);
+
+ SAFE_FREE(esc_sname);
+
+ DEBUG(5, ("Executing query %s\n", query));
+
+ mysql_ret = mysql_query(data->handle, query);
+
+ SAFE_FREE(query);
+
+ if (mysql_ret) {
+ DEBUG(0,
+ ("Error while executing MySQL query %s\n",
+ mysql_error(data->handle)));
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ res = mysql_store_result(data->handle);
+ if (res == NULL) {
+ DEBUG(0,
+ ("Error storing results: %s\n", mysql_error(data->handle)));
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ ret = row_to_sam_account(res, user);
+ mysql_free_result(res);
+
+ return ret;
+}
+
+/******************************************************************
+ Lookup a name in the SAM database
+ ******************************************************************/
+
+static NTSTATUS mysqlsam_getsampwnam(struct pdb_methods *methods, SAM_ACCOUNT * user,
+ const char *sname)
+{
+ struct pdb_mysql_data *data;
+
+ SET_DATA(data, methods);
+
+ if (!sname) {
+ DEBUG(0, ("invalid name specified"));
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ return mysqlsam_select_by_field(methods, user,
+ SQL_SEARCH_USER_NAME, sname);
+}
+
+
+/***************************************************************************
+ Search by sid
+ **************************************************************************/
+
+static NTSTATUS mysqlsam_getsampwsid(struct pdb_methods *methods, SAM_ACCOUNT * user,
+ const DOM_SID * sid)
+{
+ struct pdb_mysql_data *data;
+ fstring sid_str;
+
+ SET_DATA(data, methods);
+
+ sid_to_string(sid_str, sid);
+
+ return mysqlsam_select_by_field(methods, user, SQL_SEARCH_USER_SID, sid_str);
+}
+
+/***************************************************************************
+ Delete a SAM_ACCOUNT
+ ****************************************************************************/
+
+static NTSTATUS mysqlsam_delete_sam_account(struct pdb_methods *methods,
+ SAM_ACCOUNT * sam_pass)
+{
+ const char *sname = pdb_get_username(sam_pass);
+ char *esc;
+ char *query;
+ int ret;
+ struct pdb_mysql_data *data;
+ char *tmp_sname;
+
+ SET_DATA(data, methods);
+
+ if (!methods) {
+ DEBUG(0, ("invalid methods!\n"));
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ data = (struct pdb_mysql_data *) methods->private_data;
+ if (!data || !(data->handle)) {
+ DEBUG(0, ("invalid handle!\n"));
+ return NT_STATUS_INVALID_HANDLE;
+ }
+
+ if (!sname) {
+ DEBUG(0, ("invalid name specified\n"));
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ /* Escape sname */
+ esc = malloc(strlen(sname) * 2 + 1);
+ if (!esc) {
+ DEBUG(0, ("Can't allocate memory to store escaped name\n"));
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ tmp_sname = smb_xstrdup(sname);
+
+ mysql_real_escape_string(data->handle, esc, tmp_sname,
+ strlen(tmp_sname));
+
+ SAFE_FREE(tmp_sname);
+
+ query = sql_account_query_delete(data->location, esc);
+
+ SAFE_FREE(esc);
+
+ ret = mysql_query(data->handle, query);
+
+ SAFE_FREE(query);
+
+ if (ret) {
+ DEBUG(0,
+ ("Error while executing query: %s\n",
+ mysql_error(data->handle)));
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ DEBUG(5, ("User '%s' deleted\n", sname));
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS mysqlsam_replace_sam_account(struct pdb_methods *methods,
+ const SAM_ACCOUNT * newpwd, char isupdate)
+{
+ struct pdb_mysql_data *data;
+ char *query;
+
+ if (!methods) {
+ DEBUG(0, ("invalid methods!\n"));
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ data = (struct pdb_mysql_data *) methods->private_data;
+
+ if (data == NULL || data->handle == NULL) {
+ DEBUG(0, ("invalid handle!\n"));
+ return NT_STATUS_INVALID_HANDLE;
+ }
+
+ query = sql_account_query_update(data->location, newpwd, isupdate);
+
+ /* Execute the query */
+ if (mysql_query(data->handle, query)) {
+ DEBUG(0,
+ ("Error executing %s, %s\n", query,
+ mysql_error(data->handle)));
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+ SAFE_FREE(query);
+
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS mysqlsam_add_sam_account(struct pdb_methods *methods, SAM_ACCOUNT * newpwd)
+{
+ return mysqlsam_replace_sam_account(methods, newpwd, 0);
+}
+
+static NTSTATUS mysqlsam_update_sam_account(struct pdb_methods *methods,
+ SAM_ACCOUNT * newpwd)
+{
+ return mysqlsam_replace_sam_account(methods, newpwd, 1);
+}
+
+static NTSTATUS mysqlsam_init(struct pdb_context * pdb_context, struct pdb_methods ** pdb_method,
+ const char *location)
+{
+ NTSTATUS nt_status;
+ struct pdb_mysql_data *data;
+
+ mysqlsam_debug_level = debug_add_class("mysqlsam");
+ if (mysqlsam_debug_level == -1) {
+ mysqlsam_debug_level = DBGC_ALL;
+ DEBUG(0,
+ ("mysqlsam: Couldn't register custom debugging class!\n"));
+ }
+
+
+ if (!pdb_context) {
+ DEBUG(0, ("invalid pdb_methods specified\n"));
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ if (!NT_STATUS_IS_OK
+ (nt_status = make_pdb_methods(pdb_context->mem_ctx, pdb_method))) {
+ return nt_status;
+ }
+
+ (*pdb_method)->name = "mysqlsam";
+
+ (*pdb_method)->setsampwent = mysqlsam_setsampwent;
+ (*pdb_method)->endsampwent = mysqlsam_endsampwent;
+ (*pdb_method)->getsampwent = mysqlsam_getsampwent;
+ (*pdb_method)->getsampwnam = mysqlsam_getsampwnam;
+ (*pdb_method)->getsampwsid = mysqlsam_getsampwsid;
+ (*pdb_method)->add_sam_account = mysqlsam_add_sam_account;
+ (*pdb_method)->update_sam_account = mysqlsam_update_sam_account;
+ (*pdb_method)->delete_sam_account = mysqlsam_delete_sam_account;
+
+ data = talloc(pdb_context->mem_ctx, sizeof(struct pdb_mysql_data));
+ (*pdb_method)->private_data = data;
+ data->handle = NULL;
+ data->pwent = NULL;
+
+ if (!location) {
+ DEBUG(0, ("No identifier specified. Check the Samba HOWTO Collection for details\n"));
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ data->location = smb_xstrdup(location);
+
+ DEBUG(1,
+ ("Connecting to database server, host: %s, user: %s, password: %s, database: %s, port: %ld\n",
+ config_value(data, "mysql host", CONFIG_HOST_DEFAULT),
+ config_value(data, "mysql user", CONFIG_USER_DEFAULT),
+ config_value(data, "mysql password", CONFIG_PASS_DEFAULT),
+ config_value(data, "mysql database", CONFIG_DB_DEFAULT),
+ xatol(config_value(data, "mysql port", CONFIG_PORT_DEFAULT))));
+
+ /* Do the mysql initialization */
+ data->handle = mysql_init(NULL);
+ if (!data->handle) {
+ DEBUG(0, ("Failed to connect to server\n"));
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ if(!sql_account_config_valid(data->location)) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ /* Process correct entry in $HOME/.my.conf */
+ if (!mysql_real_connect(data->handle,
+ config_value(data, "mysql host", CONFIG_HOST_DEFAULT),
+ config_value(data, "mysql user", CONFIG_USER_DEFAULT),
+ config_value(data, "mysql password", CONFIG_PASS_DEFAULT),
+ config_value(data, "mysql database", CONFIG_DB_DEFAULT),
+ xatol(config_value (data, "mysql port", CONFIG_PORT_DEFAULT)),
+ NULL, 0)) {
+ DEBUG(0,
+ ("Failed to connect to mysql database: error: %s\n",
+ mysql_error(data->handle)));
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ DEBUG(5, ("Connected to mysql db\n"));
+
+ return NT_STATUS_OK;
+}
+
+NTSTATUS pdb_mysql_init(void)
+{
+ return smb_register_passdb(PASSDB_INTERFACE_VERSION, "mysql", mysqlsam_init);
+}
diff --git a/source/passdb/pdb_pgsql.c b/source/passdb/pdb_pgsql.c
new file mode 100644
index 00000000000..1731c720a21
--- /dev/null
+++ b/source/passdb/pdb_pgsql.c
@@ -0,0 +1,488 @@
+/*
+ * PostgresSQL password backend for samba
+ * Copyright (C) Hamish Friedlander 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 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 <libpq-fe.h>
+
+#define CONFIG_HOST_DEFAULT "localhost"
+#define CONFIG_USER_DEFAULT "samba"
+#define CONFIG_PASS_DEFAULT ""
+#define CONFIG_PORT_DEFAULT "5432"
+#define CONFIG_DB_DEFAULT "samba"
+
+/* handles for doing db transactions */
+typedef struct pdb_pgsql_data {
+ PGconn *handle ;
+ PGresult *pwent ;
+ long currow ;
+
+ const char *location ;
+} pdb_pgsql_data ;
+
+#define SET_DATA(data,methods) { \
+ if(!methods){ \
+ DEBUG(0, ("invalid methods!\n")); \
+ return NT_STATUS_INVALID_PARAMETER; \
+ } \
+ data = (struct pdb_pgsql_data *)methods->private_data; \
+ if(!data || !(data->handle)){ \
+ DEBUG(0, ("invalid handle!\n")); \
+ return NT_STATUS_INVALID_HANDLE; \
+ } \
+}
+
+#define SET_DATA_QUIET(data,methods) { \
+ if(!methods){ \
+ DEBUG(0, ("invalid methods!\n")); \
+ return ; \
+ } \
+ data = (struct pdb_pgsql_data *)methods->private_data; \
+ if(!data || !(data->handle)){ \
+ DEBUG(0, ("invalid handle!\n")); \
+ return ; \
+ } \
+}
+
+
+#define config_value( data, name, default_value ) \
+ lp_parm_const_string( GLOBAL_SECTION_SNUM, (data)->location, name, default_value )
+
+static long PQgetlong( PGresult *r, long row, long col )
+{
+ if ( PQgetisnull( r, row, col ) ) return 0 ;
+
+ return atol( PQgetvalue( r, row, col ) ) ;
+}
+
+static NTSTATUS row_to_sam_account ( PGresult *r, long row, SAM_ACCOUNT *u )
+{
+ pstring temp ;
+ DOM_SID sid ;
+
+ if ( row >= PQntuples( r ) ) return NT_STATUS_INVALID_PARAMETER ;
+
+ pdb_set_logon_time ( u, PQgetlong ( r, row, 0 ), PDB_SET ) ;
+ pdb_set_logoff_time ( u, PQgetlong ( r, row, 1 ), PDB_SET ) ;
+ pdb_set_kickoff_time ( u, PQgetlong ( r, row, 2 ), PDB_SET ) ;
+ pdb_set_pass_last_set_time ( u, PQgetlong ( r, row, 3 ), PDB_SET ) ;
+ pdb_set_pass_can_change_time ( u, PQgetlong ( r, row, 4 ), PDB_SET ) ;
+ pdb_set_pass_must_change_time( u, PQgetlong ( r, row, 5 ), PDB_SET ) ;
+ pdb_set_username ( u, PQgetvalue( r, row, 6 ), PDB_SET ) ;
+ pdb_set_domain ( u, PQgetvalue( r, row, 7 ), PDB_SET ) ;
+ pdb_set_nt_username ( u, PQgetvalue( r, row, 8 ), PDB_SET ) ;
+ pdb_set_fullname ( u, PQgetvalue( r, row, 9 ), PDB_SET ) ;
+ pdb_set_homedir ( u, PQgetvalue( r, row, 10 ), PDB_SET ) ;
+ pdb_set_dir_drive ( u, PQgetvalue( r, row, 11 ), PDB_SET ) ;
+ pdb_set_logon_script ( u, PQgetvalue( r, row, 12 ), PDB_SET ) ;
+ pdb_set_profile_path ( u, PQgetvalue( r, row, 13 ), PDB_SET ) ;
+ pdb_set_acct_desc ( u, PQgetvalue( r, row, 14 ), PDB_SET ) ;
+ pdb_set_workstations ( u, PQgetvalue( r, row, 15 ), PDB_SET ) ;
+ pdb_set_unknown_str ( u, PQgetvalue( r, row, 16 ), PDB_SET ) ;
+ pdb_set_munged_dial ( u, PQgetvalue( r, row, 17 ), PDB_SET ) ;
+
+ pdb_set_acct_ctrl ( u, PQgetlong ( r, row, 23 ), PDB_SET ) ;
+ pdb_set_logon_divs ( u, PQgetlong ( r, row, 25 ), PDB_SET ) ;
+ pdb_set_hours_len ( u, PQgetlong ( r, row, 26 ), PDB_SET ) ;
+ pdb_set_logon_count ( u, PQgetlong ( r, row, 27 ), PDB_SET ) ;
+ pdb_set_unknown_6 ( u, PQgetlong ( r, row, 28 ), PDB_SET ) ;
+
+ if ( !PQgetisnull( r, row, 18 ) ) string_to_sid( &sid, PQgetvalue( r, row, 18 ) ) ;
+ pdb_set_user_sid ( u, &sid, PDB_SET ) ;
+ if ( !PQgetisnull( r, row, 19 ) ) string_to_sid( &sid, PQgetvalue( r, row, 19 ) ) ;
+ pdb_set_group_sid( u, &sid, PDB_SET ) ;
+
+ if ( pdb_gethexpwd( PQgetvalue( r, row, 20 ), temp ), PDB_SET ) pdb_set_lanman_passwd( u, temp, PDB_SET ) ;
+ if ( pdb_gethexpwd( PQgetvalue( r, row, 21 ), temp ), PDB_SET ) pdb_set_nt_passwd ( u, temp, PDB_SET ) ;
+
+ /* Only use plaintext password storage when lanman and nt are NOT used */
+ if ( PQgetisnull( r, row, 20 ) || PQgetisnull( r, row, 21 ) ) pdb_set_plaintext_passwd( u, PQgetvalue( r, row, 22 ) ) ;
+
+ return NT_STATUS_OK ;
+}
+
+static NTSTATUS pgsqlsam_setsampwent(struct pdb_methods *methods, BOOL update)
+{
+ struct pdb_pgsql_data *data ;
+ char *query ;
+ NTSTATUS retval ;
+
+ SET_DATA( data, methods ) ;
+
+ query = sql_account_query_select(data->location, update, SQL_SEARCH_NONE, NULL);
+
+ /* Do it */
+ DEBUG( 5, ("Executing query %s\n", query) ) ;
+ data->pwent = PQexec( data->handle, query ) ;
+ data->currow = 0 ;
+
+ /* Result? */
+ if ( data->pwent == NULL )
+ {
+ DEBUG( 0, ("Error executing %s, %s\n", query, PQerrorMessage( data->handle ) ) ) ;
+ retval = NT_STATUS_UNSUCCESSFUL ;
+ }
+ else if ( PQresultStatus( data->pwent ) != PGRES_TUPLES_OK )
+ {
+ DEBUG( 0, ("Error executing %s, %s\n", query, PQresultErrorMessage( data->pwent ) ) ) ;
+ retval = NT_STATUS_UNSUCCESSFUL ;
+ }
+ else
+ {
+ DEBUG( 5, ("pgsqlsam_setsampwent succeeded(%llu results)!\n", PQntuples(data->pwent)) ) ;
+ retval = NT_STATUS_OK ;
+ }
+
+ SAFE_FREE(query);
+ return retval ;
+}
+
+/***************************************************************
+ End enumeration of the passwd list.
+ ****************************************************************/
+
+static void pgsqlsam_endsampwent(struct pdb_methods *methods)
+{
+ struct pdb_pgsql_data *data ;
+
+ SET_DATA_QUIET( data, methods ) ;
+
+ if (data->pwent != NULL)
+ {
+ PQclear( data->pwent ) ;
+ }
+
+ data->pwent = NULL ;
+ data->currow = 0 ;
+
+ DEBUG( 5, ("pgsql_endsampwent called\n") ) ;
+}
+
+/*****************************************************************
+ Get one SAM_ACCOUNT from the list (next in line)
+ *****************************************************************/
+
+static NTSTATUS pgsqlsam_getsampwent( struct pdb_methods *methods, SAM_ACCOUNT *user )
+{
+ struct pdb_pgsql_data *data;
+ NTSTATUS retval ;
+
+ SET_DATA( data, methods ) ;
+
+ if ( data->pwent == NULL )
+ {
+ DEBUG( 0, ("invalid pwent\n") ) ;
+ return NT_STATUS_INVALID_PARAMETER ;
+ }
+
+ retval = row_to_sam_account( data->pwent, data->currow, user ) ;
+ data->currow++ ;
+
+ return retval ;
+}
+
+static NTSTATUS pgsqlsam_select_by_field ( struct pdb_methods *methods, SAM_ACCOUNT *user, enum sql_search_field field, const char *sname )
+{
+ struct pdb_pgsql_data *data ;
+
+ char *esc ;
+ char *query ;
+
+ PGresult *result ;
+ NTSTATUS retval ;
+
+ SET_DATA(data, methods);
+
+ if ( user == NULL )
+ {
+ DEBUG( 0, ("pdb_getsampwnam: SAM_ACCOUNT is NULL.\n") ) ;
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ DEBUG( 5, ("pgsqlsam_select_by_field: getting data where %d = %s(nonescaped)\n", field, sname) ) ;
+
+ /* Escape sname */
+ esc = malloc(strlen(sname) * 2 + 1);
+ if ( !esc )
+ {
+ DEBUG(0, ("Can't allocate memory to store escaped name\n"));
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ //tmp_sname = smb_xstrdup(sname);
+ PQescapeString( esc, sname, strlen(sname) ) ;
+
+ query = sql_account_query_select(data->location, True, field, esc);
+
+ /* Do it */
+ DEBUG( 5, ("Executing query %s\n", query) ) ;
+ result = PQexec( data->handle, query ) ;
+
+ /* Result? */
+ if ( result == NULL )
+ {
+ DEBUG( 0, ("Error executing %s, %s\n", query, PQerrorMessage( data->handle ) ) ) ;
+ retval = NT_STATUS_UNSUCCESSFUL ;
+ }
+ else if ( PQresultStatus( result ) != PGRES_TUPLES_OK )
+ {
+ DEBUG( 0, ("Error executing %s, %s\n", query, PQresultErrorMessage( result ) ) ) ;
+ retval = NT_STATUS_UNSUCCESSFUL ;
+ }
+ else
+ {
+ retval = row_to_sam_account( result, 0, user ) ;
+ }
+
+ SAFE_FREE( esc ) ;
+ SAFE_FREE( query ) ;
+
+ PQclear( result ) ;
+
+ return retval ;
+}
+
+/******************************************************************
+ Lookup a name in the SAM database
+ ******************************************************************/
+
+static NTSTATUS pgsqlsam_getsampwnam ( struct pdb_methods *methods, SAM_ACCOUNT *user, const char *sname )
+{
+ struct pdb_pgsql_data *data;
+
+ SET_DATA(data, methods);
+
+ if ( !sname )
+ {
+ DEBUG( 0, ("invalid name specified") ) ;
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ return pgsqlsam_select_by_field( methods, user, SQL_SEARCH_USER_NAME, sname ) ;
+}
+
+
+/***************************************************************************
+ Search by sid
+ **************************************************************************/
+
+static NTSTATUS pgsqlsam_getsampwsid ( struct pdb_methods *methods, SAM_ACCOUNT *user, const DOM_SID *sid )
+{
+ struct pdb_pgsql_data *data;
+ fstring sid_str;
+
+ SET_DATA( data, methods ) ;
+
+ sid_to_string( sid_str, sid ) ;
+
+ return pgsqlsam_select_by_field( methods, user, SQL_SEARCH_USER_SID, sid_str ) ;
+}
+
+/***************************************************************************
+ Delete a SAM_ACCOUNT
+ ****************************************************************************/
+
+static NTSTATUS pgsqlsam_delete_sam_account( struct pdb_methods *methods, SAM_ACCOUNT *sam_pass )
+{
+ struct pdb_pgsql_data *data ;
+
+ const char *sname = pdb_get_username( sam_pass ) ;
+ char *esc ;
+ char *query ;
+
+ PGresult *result ;
+ NTSTATUS retval ;
+
+ SET_DATA(data, methods);
+
+ if ( !sname )
+ {
+ DEBUG( 0, ("invalid name specified\n") ) ;
+ return NT_STATUS_INVALID_PARAMETER ;
+ }
+
+ /* Escape sname */
+ esc = malloc(strlen(sname) * 2 + 1);
+ if ( !esc )
+ {
+ DEBUG(0, ("Can't allocate memory to store escaped name\n"));
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ PQescapeString( esc, sname, strlen(sname) ) ;
+
+ query = sql_account_query_delete(data->location, esc);
+
+ /* Do it */
+ result = PQexec( data->handle, query ) ;
+
+ if ( result == NULL )
+ {
+ DEBUG( 0, ("Error executing %s, %s\n", query, PQerrorMessage( data->handle ) ) ) ;
+ retval = NT_STATUS_UNSUCCESSFUL ;
+ }
+ else if ( PQresultStatus( result ) != PGRES_COMMAND_OK )
+ {
+ DEBUG( 0, ("Error executing %s, %s\n", query, PQresultErrorMessage( result ) ) ) ;
+ retval = NT_STATUS_UNSUCCESSFUL ;
+ }
+ else
+ {
+ DEBUG( 5, ("User '%s' deleted\n", sname) ) ;
+ retval = NT_STATUS_OK ;
+ }
+
+ SAFE_FREE( esc ) ;
+ SAFE_FREE( query ) ;
+
+ return retval ;
+}
+
+static NTSTATUS pgsqlsam_replace_sam_account( struct pdb_methods *methods, const SAM_ACCOUNT *newpwd, char isupdate )
+{
+ struct pdb_pgsql_data *data ;
+ char *query;
+ PGresult *result ;
+
+ if ( !methods )
+ {
+ DEBUG( 0, ("invalid methods!\n") ) ;
+ return NT_STATUS_INVALID_PARAMETER ;
+ }
+
+ data = (struct pdb_pgsql_data *) methods->private_data ;
+
+ if ( data == NULL || data->handle == NULL )
+ {
+ DEBUG( 0, ("invalid handle!\n") ) ;
+ return NT_STATUS_INVALID_HANDLE ;
+ }
+
+ query = sql_account_query_update(data->location, newpwd, isupdate);
+
+ result = PQexec( data->handle, query ) ;
+
+
+ /* Execute the query */
+ if ( result == NULL )
+ {
+ DEBUG( 0, ("Error executing %s, %s\n", query, PQerrorMessage( data->handle ) ) ) ;
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+ else if ( PQresultStatus( result ) != PGRES_COMMAND_OK )
+ {
+ DEBUG( 0, ("Error executing %s, %s\n", query, PQresultErrorMessage( result ) ) ) ;
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+ SAFE_FREE(query);
+
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS pgsqlsam_add_sam_account ( struct pdb_methods *methods, SAM_ACCOUNT *newpwd )
+{
+ return pgsqlsam_replace_sam_account( methods, newpwd, 0 ) ;
+}
+
+static NTSTATUS pgsqlsam_update_sam_account ( struct pdb_methods *methods, SAM_ACCOUNT *newpwd )
+{
+ return pgsqlsam_replace_sam_account( methods, newpwd, 1 ) ;
+}
+
+static NTSTATUS pgsqlsam_init ( struct pdb_context *pdb_context, struct pdb_methods **pdb_method, const char *location )
+{
+ NTSTATUS nt_status ;
+ struct pdb_pgsql_data *data ;
+
+ if ( !pdb_context )
+ {
+ DEBUG( 0, ("invalid pdb_methods specified\n") ) ;
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ if (!NT_STATUS_IS_OK
+ (nt_status = make_pdb_methods(pdb_context->mem_ctx, pdb_method))) {
+ return nt_status;
+ }
+
+ (*pdb_method)->name = "pgsqlsam" ;
+
+ (*pdb_method)->setsampwent = pgsqlsam_setsampwent ;
+ (*pdb_method)->endsampwent = pgsqlsam_endsampwent ;
+ (*pdb_method)->getsampwent = pgsqlsam_getsampwent ;
+ (*pdb_method)->getsampwnam = pgsqlsam_getsampwnam ;
+ (*pdb_method)->getsampwsid = pgsqlsam_getsampwsid ;
+ (*pdb_method)->add_sam_account = pgsqlsam_add_sam_account ;
+ (*pdb_method)->update_sam_account = pgsqlsam_update_sam_account ;
+ (*pdb_method)->delete_sam_account = pgsqlsam_delete_sam_account ;
+
+ data = talloc( pdb_context->mem_ctx, sizeof( struct pdb_pgsql_data ) ) ;
+ (*pdb_method)->private_data = data ;
+ data->handle = NULL ;
+ data->pwent = NULL ;
+
+ if ( !location )
+ {
+ DEBUG( 0, ("No identifier specified. Check the Samba HOWTO Collection for details\n") ) ;
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ data->location = smb_xstrdup( location ) ;
+
+ if(!sql_account_config_valid(data->location)) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ DEBUG
+ (
+ 1,
+ (
+ "Connecting to database server, host: %s, user: %s, password: XXXXXX, database: %s, port: %s\n",
+ config_value( data, "pgsql host" , CONFIG_HOST_DEFAULT ),
+ config_value( data, "pgsql user" , CONFIG_USER_DEFAULT ),
+ config_value( data, "pgsql database", CONFIG_DB_DEFAULT ),
+ config_value( data, "pgsql port" , CONFIG_PORT_DEFAULT )
+ )
+ ) ;
+
+ /* Do the pgsql initialization */
+ data->handle = PQsetdbLogin(
+ config_value( data, "pgsql host" , CONFIG_HOST_DEFAULT ),
+ config_value( data, "pgsql port" , CONFIG_PORT_DEFAULT ),
+ NULL,
+ NULL,
+ config_value( data, "pgsql database", CONFIG_DB_DEFAULT ),
+ config_value( data, "pgsql user" , CONFIG_USER_DEFAULT ),
+ config_value( data, "pgsql password", CONFIG_PASS_DEFAULT )
+ ) ;
+
+ if ( PQstatus( data->handle ) != CONNECTION_OK )
+ {
+ DEBUG( 0, ("Failed to connect to pgsql database: error: %s\n", PQerrorMessage( data->handle )) ) ;
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ DEBUG( 5, ("Connected to pgsql database\n") ) ;
+ return NT_STATUS_OK;
+}
+
+NTSTATUS pdb_pgsql_init(void)
+{
+ return smb_register_passdb( PASSDB_INTERFACE_VERSION, "pgsql", pgsqlsam_init ) ;
+}
diff --git a/source/passdb/pdb_plugin.c b/source/passdb/pdb_plugin.c
new file mode 100644
index 00000000000..027cd0b5d33
--- /dev/null
+++ b/source/passdb/pdb_plugin.c
@@ -0,0 +1,80 @@
+/*
+ Unix SMB/CIFS implementation.
+ Loadable passdb module interface.
+ Copyright (C) Jelmer Vernooij 2002
+ 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 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"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_PASSDB
+
+NTSTATUS pdb_init_plugin(PDB_CONTEXT *pdb_context, PDB_METHODS **pdb_method, const char *location)
+{
+ void * dl_handle;
+ char *plugin_location, *plugin_name, *p;
+ pdb_init_function plugin_init;
+ int (*plugin_version)(void);
+
+ if (location == NULL) {
+ DEBUG(0, ("The plugin module needs an argument!\n"));
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ plugin_name = smb_xstrdup(location);
+ p = strchr(plugin_name, ':');
+ if (p) {
+ *p = 0;
+ plugin_location = p+1;
+ trim_char(plugin_location, ' ', ' ');
+ } else {
+ plugin_location = NULL;
+ }
+ trim_char(plugin_name, ' ', ' ');
+
+ DEBUG(5, ("Trying to load sam plugin %s\n", plugin_name));
+ dl_handle = sys_dlopen(plugin_name, RTLD_NOW );
+ if (!dl_handle) {
+ DEBUG(0, ("Failed to load sam plugin %s using sys_dlopen (%s)\n", plugin_name, sys_dlerror()));
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ plugin_version = sys_dlsym(dl_handle, "pdb_version");
+ if (!plugin_version) {
+ sys_dlclose(dl_handle);
+ DEBUG(0, ("Failed to find function 'pdb_version' using sys_dlsym in sam plugin %s (%s)\n", plugin_name, sys_dlerror()));
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ if (plugin_version() != PASSDB_INTERFACE_VERSION) {
+ sys_dlclose(dl_handle);
+ DEBUG(0, ("Wrong PASSDB_INTERFACE_VERSION! sam plugin has version %d and version %d is needed! Please update!\n",
+ plugin_version(),PASSDB_INTERFACE_VERSION));
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ plugin_init = sys_dlsym(dl_handle, "pdb_init");
+ if (!plugin_init) {
+ sys_dlclose(dl_handle);
+ DEBUG(0, ("Failed to find function 'pdb_init' using sys_dlsym in sam plugin %s (%s)\n", plugin_name, sys_dlerror()));
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ DEBUG(5, ("Starting sam plugin %s with location %s\n", plugin_name, plugin_location));
+ return plugin_init(pdb_context, pdb_method, plugin_location);
+}
diff --git a/source/passdb/pdb_smbpasswd.c b/source/passdb/pdb_smbpasswd.c
new file mode 100644
index 00000000000..562d50f89e3
--- /dev/null
+++ b/source/passdb/pdb_smbpasswd.c
@@ -0,0 +1,1517 @@
+/*
+ * Unix SMB/CIFS implementation.
+ * SMB parameters and setup
+ * Copyright (C) Andrew Tridgell 1992-1998
+ * Modified by Jeremy Allison 1995.
+ * Modified by Gerald (Jerry) Carter 2000-2001,2003
+ * Modified by 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 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"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_PASSDB
+
+/*
+ smb_passwd is analogous to sam_passwd used everywhere
+ else. However, smb_passwd is limited to the information
+ stored by an smbpasswd entry
+ */
+
+struct smb_passwd
+{
+ uint32 smb_userid; /* this is actually the unix uid_t */
+ const char *smb_name; /* username string */
+
+ const unsigned char *smb_passwd; /* Null if no password */
+ const unsigned char *smb_nt_passwd; /* Null if no password */
+
+ uint16 acct_ctrl; /* account info (ACB_xxxx bit-mask) */
+ time_t pass_last_set_time; /* password last set time */
+};
+
+struct smbpasswd_privates
+{
+ /* used for maintain locks on the smbpasswd file */
+ int pw_file_lock_depth;
+
+ /* Global File pointer */
+ FILE *pw_file;
+
+ /* formerly static variables */
+ struct smb_passwd pw_buf;
+ pstring user_name;
+ unsigned char smbpwd[16];
+ unsigned char smbntpwd[16];
+
+ /* retrive-once info */
+ const char *smbpasswd_file;
+};
+
+enum pwf_access_type { PWF_READ, PWF_UPDATE, PWF_CREATE };
+
+/***************************************************************
+ Lock an fd. Abandon after waitsecs seconds.
+****************************************************************/
+
+static BOOL pw_file_lock(int fd, int type, int secs, int *plock_depth)
+{
+ if (fd < 0)
+ return False;
+
+ if(*plock_depth == 0) {
+ if (!do_file_lock(fd, secs, type)) {
+ DEBUG(10,("pw_file_lock: locking file failed, error = %s.\n",
+ strerror(errno)));
+ return False;
+ }
+ }
+
+ (*plock_depth)++;
+
+ return True;
+}
+
+/***************************************************************
+ Unlock an fd. Abandon after waitsecs seconds.
+****************************************************************/
+
+static BOOL pw_file_unlock(int fd, int *plock_depth)
+{
+ BOOL ret=True;
+
+ if (fd == 0 || *plock_depth == 0) {
+ return True;
+ }
+
+ if(*plock_depth == 1)
+ ret = do_file_lock(fd, 5, F_UNLCK);
+
+ if (*plock_depth > 0)
+ (*plock_depth)--;
+
+ if(!ret)
+ DEBUG(10,("pw_file_unlock: unlocking file failed, error = %s.\n",
+ strerror(errno)));
+ return ret;
+}
+
+
+/**************************************************************
+ Intialize a smb_passwd struct
+ *************************************************************/
+
+static void pdb_init_smb(struct smb_passwd *user)
+{
+ if (user == NULL)
+ return;
+ ZERO_STRUCTP (user);
+
+ user->pass_last_set_time = (time_t)0;
+}
+
+/***************************************************************
+ Internal fn to enumerate the smbpasswd list. Returns a void pointer
+ to ensure no modification outside this module. Checks for atomic
+ rename of smbpasswd file on update or create once the lock has
+ been granted to prevent race conditions. JRA.
+****************************************************************/
+
+static FILE *startsmbfilepwent(const char *pfile, enum pwf_access_type type, int *lock_depth)
+{
+ FILE *fp = NULL;
+ const char *open_mode = NULL;
+ int race_loop = 0;
+ int lock_type = F_RDLCK;
+
+ if (!*pfile) {
+ DEBUG(0, ("startsmbfilepwent: No SMB password file set\n"));
+ return (NULL);
+ }
+
+ switch(type) {
+ case PWF_READ:
+ open_mode = "rb";
+ lock_type = F_RDLCK;
+ break;
+ case PWF_UPDATE:
+ open_mode = "r+b";
+ lock_type = F_WRLCK;
+ break;
+ case PWF_CREATE:
+ /*
+ * Ensure atomic file creation.
+ */
+ {
+ int i, fd = -1;
+
+ for(i = 0; i < 5; i++) {
+ if((fd = sys_open(pfile, O_CREAT|O_TRUNC|O_EXCL|O_RDWR, 0600))!=-1)
+ break;
+ sys_usleep(200); /* Spin, spin... */
+ }
+ if(fd == -1) {
+ DEBUG(0,("startsmbfilepwent_internal: too many race conditions creating file %s\n", pfile));
+ return NULL;
+ }
+ close(fd);
+ open_mode = "r+b";
+ lock_type = F_WRLCK;
+ break;
+ }
+ }
+
+ for(race_loop = 0; race_loop < 5; race_loop++) {
+ DEBUG(10, ("startsmbfilepwent_internal: opening file %s\n", pfile));
+
+ if((fp = sys_fopen(pfile, open_mode)) == NULL) {
+
+ /*
+ * If smbpasswd file doesn't exist, then create new one. This helps to avoid
+ * confusing error msg when adding user account first time.
+ */
+ if (errno == ENOENT) {
+ if ((fp = sys_fopen(pfile, "a+")) != NULL) {
+ DEBUG(0, ("startsmbfilepwent_internal: file %s did not exist. File successfully created.\n", pfile));
+
+ } else {
+ DEBUG(0, ("startsmbfilepwent_internal: file %s did not exist. Couldn't create new one. Error was: %s",
+ pfile, strerror(errno)));
+ return NULL;
+ }
+
+ } else {
+ DEBUG(0, ("startsmbfilepwent_internal: unable to open file %s. Error was: %s\n", pfile, strerror(errno)));
+ return NULL;
+ }
+ }
+
+ if (!pw_file_lock(fileno(fp), lock_type, 5, lock_depth)) {
+ DEBUG(0, ("startsmbfilepwent_internal: unable to lock file %s. Error was %s\n", pfile, strerror(errno) ));
+ fclose(fp);
+ return NULL;
+ }
+
+ /*
+ * Only check for replacement races on update or create.
+ * For read we don't mind if the data is one record out of date.
+ */
+
+ if(type == PWF_READ) {
+ break;
+ } else {
+ SMB_STRUCT_STAT sbuf1, sbuf2;
+
+ /*
+ * Avoid the potential race condition between the open and the lock
+ * by doing a stat on the filename and an fstat on the fd. If the
+ * two inodes differ then someone did a rename between the open and
+ * the lock. Back off and try the open again. Only do this 5 times to
+ * prevent infinate loops. JRA.
+ */
+
+ if (sys_stat(pfile,&sbuf1) != 0) {
+ DEBUG(0, ("startsmbfilepwent_internal: unable to stat file %s. Error was %s\n", pfile, strerror(errno)));
+ pw_file_unlock(fileno(fp), lock_depth);
+ fclose(fp);
+ return NULL;
+ }
+
+ if (sys_fstat(fileno(fp),&sbuf2) != 0) {
+ DEBUG(0, ("startsmbfilepwent_internal: unable to fstat file %s. Error was %s\n", pfile, strerror(errno)));
+ pw_file_unlock(fileno(fp), lock_depth);
+ fclose(fp);
+ return NULL;
+ }
+
+ if( sbuf1.st_ino == sbuf2.st_ino) {
+ /* No race. */
+ break;
+ }
+
+ /*
+ * Race occurred - back off and try again...
+ */
+
+ pw_file_unlock(fileno(fp), lock_depth);
+ fclose(fp);
+ }
+ }
+
+ if(race_loop == 5) {
+ DEBUG(0, ("startsmbfilepwent_internal: too many race conditions opening file %s\n", pfile));
+ return NULL;
+ }
+
+ /* Set a buffer to do more efficient reads */
+ setvbuf(fp, (char *)NULL, _IOFBF, 1024);
+
+ /* Make sure it is only rw by the owner */
+#ifdef HAVE_FCHMOD
+ if(fchmod(fileno(fp), S_IRUSR|S_IWUSR) == -1) {
+#else
+ if(chmod(pfile, S_IRUSR|S_IWUSR) == -1) {
+#endif
+ DEBUG(0, ("startsmbfilepwent_internal: failed to set 0600 permissions on password file %s. \
+Error was %s\n.", pfile, strerror(errno) ));
+ pw_file_unlock(fileno(fp), lock_depth);
+ fclose(fp);
+ return NULL;
+ }
+
+ /* We have a lock on the file. */
+ return fp;
+}
+
+/***************************************************************
+ End enumeration of the smbpasswd list.
+****************************************************************/
+static void endsmbfilepwent(FILE *fp, int *lock_depth)
+{
+ if (!fp) {
+ return;
+ }
+
+ pw_file_unlock(fileno(fp), lock_depth);
+ fclose(fp);
+ DEBUG(7, ("endsmbfilepwent_internal: closed password file.\n"));
+}
+
+/*************************************************************************
+ Routine to return the next entry in the smbpasswd list.
+ *************************************************************************/
+
+static struct smb_passwd *getsmbfilepwent(struct smbpasswd_privates *smbpasswd_state, FILE *fp)
+{
+ /* Static buffers we will return. */
+ struct smb_passwd *pw_buf = &smbpasswd_state->pw_buf;
+ char *user_name = smbpasswd_state->user_name;
+ unsigned char *smbpwd = smbpasswd_state->smbpwd;
+ unsigned char *smbntpwd = smbpasswd_state->smbntpwd;
+ char linebuf[256];
+ unsigned char c;
+ unsigned char *p;
+ long uidval;
+ size_t linebuf_len;
+
+ if(fp == NULL) {
+ DEBUG(0,("getsmbfilepwent: Bad password file pointer.\n"));
+ return NULL;
+ }
+
+ pdb_init_smb(pw_buf);
+
+ pw_buf->acct_ctrl = ACB_NORMAL;
+
+ /*
+ * Scan the file, a line at a time and check if the name matches.
+ */
+ while (!feof(fp)) {
+ linebuf[0] = '\0';
+
+ fgets(linebuf, 256, fp);
+ if (ferror(fp)) {
+ return NULL;
+ }
+
+ /*
+ * Check if the string is terminated with a newline - if not
+ * then we must keep reading and discard until we get one.
+ */
+ if ((linebuf_len = strlen(linebuf)) == 0)
+ continue;
+
+ if (linebuf[linebuf_len - 1] != '\n') {
+ c = '\0';
+ while (!ferror(fp) && !feof(fp)) {
+ c = fgetc(fp);
+ if (c == '\n')
+ break;
+ }
+ } else
+ linebuf[linebuf_len - 1] = '\0';
+
+#ifdef DEBUG_PASSWORD
+ DEBUG(100, ("getsmbfilepwent: got line |%s|\n", linebuf));
+#endif
+ if ((linebuf[0] == 0) && feof(fp)) {
+ DEBUG(4, ("getsmbfilepwent: end of file reached\n"));
+ break;
+ }
+ /*
+ * The line we have should be of the form :-
+ *
+ * username:uid:32hex bytes:[Account type]:LCT-12345678....other flags presently
+ * ignored....
+ *
+ * or,
+ *
+ * username:uid:32hex bytes:32hex bytes:[Account type]:LCT-12345678....ignored....
+ *
+ * if Windows NT compatible passwords are also present.
+ * [Account type] is an ascii encoding of the type of account.
+ * LCT-(8 hex digits) is the time_t value of the last change time.
+ */
+
+ if (linebuf[0] == '#' || linebuf[0] == '\0') {
+ DEBUG(6, ("getsmbfilepwent: skipping comment or blank line\n"));
+ continue;
+ }
+ p = (unsigned char *) strchr_m(linebuf, ':');
+ if (p == NULL) {
+ DEBUG(0, ("getsmbfilepwent: malformed password entry (no :)\n"));
+ continue;
+ }
+ /*
+ * As 256 is shorter than a pstring we don't need to check
+ * length here - if this ever changes....
+ */
+ SMB_ASSERT(sizeof(pstring) > sizeof(linebuf));
+
+ strncpy(user_name, linebuf, PTR_DIFF(p, linebuf));
+ user_name[PTR_DIFF(p, linebuf)] = '\0';
+
+ /* Get smb uid. */
+
+ p++; /* Go past ':' */
+
+ if(*p == '-') {
+ DEBUG(0, ("getsmbfilepwent: uids in the smbpasswd file must not be negative.\n"));
+ continue;
+ }
+
+ if (!isdigit(*p)) {
+ DEBUG(0, ("getsmbfilepwent: malformed password entry (uid not number)\n"));
+ continue;
+ }
+
+ uidval = atoi((char *) p);
+
+ while (*p && isdigit(*p))
+ p++;
+
+ if (*p != ':') {
+ DEBUG(0, ("getsmbfilepwent: malformed password entry (no : after uid)\n"));
+ continue;
+ }
+
+ pw_buf->smb_name = user_name;
+ pw_buf->smb_userid = uidval;
+
+ /*
+ * Now get the password value - this should be 32 hex digits
+ * which are the ascii representations of a 16 byte string.
+ * Get two at a time and put them into the password.
+ */
+
+ /* Skip the ':' */
+ p++;
+
+ if (linebuf_len < (PTR_DIFF(p, linebuf) + 33)) {
+ DEBUG(0, ("getsmbfilepwent: malformed password entry (passwd too short)\n"));
+ continue;
+ }
+
+ if (p[32] != ':') {
+ DEBUG(0, ("getsmbfilepwent: malformed password entry (no terminating :)\n"));
+ continue;
+ }
+
+ if (strnequal((char *) p, "NO PASSWORD", 11)) {
+ pw_buf->smb_passwd = NULL;
+ pw_buf->acct_ctrl |= ACB_PWNOTREQ;
+ } else {
+ if (*p == '*' || *p == 'X') {
+ /* NULL LM password */
+ pw_buf->smb_passwd = NULL;
+ DEBUG(10, ("getsmbfilepwent: LM password for user %s invalidated\n", user_name));
+ } else if (pdb_gethexpwd((char *)p, smbpwd)) {
+ pw_buf->smb_passwd = smbpwd;
+ } else {
+ pw_buf->smb_passwd = NULL;
+ DEBUG(0, ("getsmbfilepwent: Malformed Lanman password entry (non hex chars)\n"));
+ }
+ }
+
+ /*
+ * Now check if the NT compatible password is
+ * available.
+ */
+ pw_buf->smb_nt_passwd = NULL;
+
+ p += 33; /* Move to the first character of the line after
+ the lanman password. */
+ if ((linebuf_len >= (PTR_DIFF(p, linebuf) + 33)) && (p[32] == ':')) {
+ if (*p != '*' && *p != 'X') {
+ if(pdb_gethexpwd((char *)p,smbntpwd))
+ pw_buf->smb_nt_passwd = smbntpwd;
+ }
+ p += 33; /* Move to the first character of the line after
+ the NT password. */
+ }
+
+ DEBUG(5,("getsmbfilepwent: returning passwd entry for user %s, uid %ld\n",
+ user_name, uidval));
+
+ if (*p == '[')
+ {
+ unsigned char *end_p = (unsigned char *)strchr_m((char *)p, ']');
+ pw_buf->acct_ctrl = pdb_decode_acct_ctrl((char*)p);
+
+ /* Must have some account type set. */
+ if(pw_buf->acct_ctrl == 0)
+ pw_buf->acct_ctrl = ACB_NORMAL;
+
+ /* Now try and get the last change time. */
+ if(end_p)
+ p = end_p + 1;
+ if(*p == ':') {
+ p++;
+ if(*p && (StrnCaseCmp((char *)p, "LCT-", 4)==0)) {
+ int i;
+ p += 4;
+ for(i = 0; i < 8; i++) {
+ if(p[i] == '\0' || !isxdigit(p[i]))
+ break;
+ }
+ if(i == 8) {
+ /*
+ * p points at 8 characters of hex digits -
+ * read into a time_t as the seconds since
+ * 1970 that the password was last changed.
+ */
+ pw_buf->pass_last_set_time = (time_t)strtol((char *)p, NULL, 16);
+ }
+ }
+ }
+ } else {
+ /* 'Old' style file. Fake up based on user name. */
+ /*
+ * Currently trust accounts are kept in the same
+ * password file as 'normal accounts'. If this changes
+ * we will have to fix this code. JRA.
+ */
+ if(pw_buf->smb_name[strlen(pw_buf->smb_name) - 1] == '$') {
+ pw_buf->acct_ctrl &= ~ACB_NORMAL;
+ pw_buf->acct_ctrl |= ACB_WSTRUST;
+ }
+ }
+
+ return pw_buf;
+ }
+
+ DEBUG(5,("getsmbfilepwent: end of file reached.\n"));
+ return NULL;
+}
+
+/************************************************************************
+ Create a new smbpasswd entry - malloced space returned.
+*************************************************************************/
+
+static char *format_new_smbpasswd_entry(const struct smb_passwd *newpwd)
+{
+ int new_entry_length;
+ char *new_entry;
+ char *p;
+
+ new_entry_length = strlen(newpwd->smb_name) + 1 + 15 + 1 + 32 + 1 + 32 + 1 + NEW_PW_FORMAT_SPACE_PADDED_LEN + 1 + 13 + 2;
+
+ if((new_entry = (char *)malloc( new_entry_length )) == NULL) {
+ DEBUG(0, ("format_new_smbpasswd_entry: Malloc failed adding entry for user %s.\n", newpwd->smb_name ));
+ return NULL;
+ }
+
+ slprintf(new_entry, new_entry_length - 1, "%s:%u:", newpwd->smb_name, (unsigned)newpwd->smb_userid);
+
+ p = new_entry+strlen(new_entry);
+
+ pdb_sethexpwd(p, newpwd->smb_passwd, newpwd->acct_ctrl);
+
+ p+=strlen(p); *p = ':'; p++;
+
+ pdb_sethexpwd(p, newpwd->smb_nt_passwd, newpwd->acct_ctrl);
+
+ p+=strlen(p); *p = ':'; p++;
+
+ /* Add the account encoding and the last change time. */
+ slprintf((char *)p, new_entry_length - 1 - (p - new_entry), "%s:LCT-%08X:\n",
+ pdb_encode_acct_ctrl(newpwd->acct_ctrl, NEW_PW_FORMAT_SPACE_PADDED_LEN),
+ (uint32)newpwd->pass_last_set_time);
+
+ return new_entry;
+}
+
+/************************************************************************
+ Routine to add an entry to the smbpasswd file.
+*************************************************************************/
+
+static BOOL add_smbfilepwd_entry(struct smbpasswd_privates *smbpasswd_state, struct smb_passwd *newpwd)
+{
+ const char *pfile = smbpasswd_state->smbpasswd_file;
+ struct smb_passwd *pwd = NULL;
+ FILE *fp = NULL;
+ int wr_len;
+ int fd;
+ size_t new_entry_length;
+ char *new_entry;
+ SMB_OFF_T offpos;
+ uint32 max_found_uid = 0;
+
+ /* Open the smbpassword file - for update. */
+ fp = startsmbfilepwent(pfile, PWF_UPDATE, &(smbpasswd_state->pw_file_lock_depth));
+
+ if (fp == NULL && errno == ENOENT) {
+ /* Try again - create. */
+ fp = startsmbfilepwent(pfile, PWF_CREATE, &(smbpasswd_state->pw_file_lock_depth));
+ }
+
+ if (fp == NULL) {
+ DEBUG(0, ("add_smbfilepwd_entry: unable to open file.\n"));
+ return False;
+ }
+
+ /*
+ * Scan the file, a line at a time and check if the name matches.
+ */
+
+ while ((pwd = getsmbfilepwent(smbpasswd_state, fp)) != NULL)
+ {
+ if (strequal(newpwd->smb_name, pwd->smb_name))
+ {
+ DEBUG(0, ("add_smbfilepwd_entry: entry with name %s already exists\n", pwd->smb_name));
+ endsmbfilepwent(fp, &(smbpasswd_state->pw_file_lock_depth));
+ return False;
+ }
+
+ /* Look for a free uid for use in non-unix accounts */
+ if (pwd->smb_userid > max_found_uid) {
+ max_found_uid = pwd->smb_userid;
+ }
+ }
+
+ /* Ok - entry doesn't exist. We can add it */
+
+ /* Create a new smb passwd entry and set it to the given password. */
+ /*
+ * The add user write needs to be atomic - so get the fd from
+ * the fp and do a raw write() call.
+ */
+ fd = fileno(fp);
+
+ if((offpos = sys_lseek(fd, 0, SEEK_END)) == -1)
+ {
+ DEBUG(0, ("add_smbfilepwd_entry(sys_lseek): Failed to add entry for user %s to file %s. \
+Error was %s\n", newpwd->smb_name, pfile, strerror(errno)));
+ endsmbfilepwent(fp, &(smbpasswd_state->pw_file_lock_depth));
+ return False;
+ }
+
+ if((new_entry = format_new_smbpasswd_entry(newpwd)) == NULL)
+ {
+ DEBUG(0, ("add_smbfilepwd_entry(malloc): Failed to add entry for user %s to file %s. \
+Error was %s\n", newpwd->smb_name, pfile, strerror(errno)));
+ endsmbfilepwent(fp, &(smbpasswd_state->pw_file_lock_depth));
+ return False;
+ }
+
+ new_entry_length = strlen(new_entry);
+
+#ifdef DEBUG_PASSWORD
+ DEBUG(100, ("add_smbfilepwd_entry(%d): new_entry_len %d made line |%s|",
+ fd, new_entry_length, new_entry));
+#endif
+
+ if ((wr_len = write(fd, new_entry, new_entry_length)) != new_entry_length)
+ {
+ DEBUG(0, ("add_smbfilepwd_entry(write): %d Failed to add entry for user %s to file %s. \
+Error was %s\n", wr_len, newpwd->smb_name, pfile, strerror(errno)));
+
+ /* Remove the entry we just wrote. */
+ if(sys_ftruncate(fd, offpos) == -1)
+ {
+ DEBUG(0, ("add_smbfilepwd_entry: ERROR failed to ftruncate file %s. \
+Error was %s. Password file may be corrupt ! Please examine by hand !\n",
+ newpwd->smb_name, strerror(errno)));
+ }
+
+ endsmbfilepwent(fp, &(smbpasswd_state->pw_file_lock_depth));
+ free(new_entry);
+ return False;
+ }
+
+ free(new_entry);
+ endsmbfilepwent(fp, &(smbpasswd_state->pw_file_lock_depth));
+ return True;
+}
+
+/************************************************************************
+ Routine to search the smbpasswd file for an entry matching the username.
+ and then modify its password entry. We can't use the startsmbpwent()/
+ getsmbpwent()/endsmbpwent() interfaces here as we depend on looking
+ in the actual file to decide how much room we have to write data.
+ override = False, normal
+ override = True, override XXXXXXXX'd out password or NO PASS
+************************************************************************/
+
+static BOOL mod_smbfilepwd_entry(struct smbpasswd_privates *smbpasswd_state, const struct smb_passwd* pwd)
+{
+ /* Static buffers we will return. */
+ pstring user_name;
+
+ char linebuf[256];
+ char readbuf[1024];
+ unsigned char c;
+ fstring ascii_p16;
+ fstring encode_bits;
+ unsigned char *p = NULL;
+ size_t linebuf_len = 0;
+ FILE *fp;
+ int lockfd;
+ const char *pfile = smbpasswd_state->smbpasswd_file;
+ BOOL found_entry = False;
+ BOOL got_pass_last_set_time = False;
+
+ SMB_OFF_T pwd_seekpos = 0;
+
+ int i;
+ int wr_len;
+ int fd;
+
+ if (!*pfile) {
+ DEBUG(0, ("No SMB password file set\n"));
+ return False;
+ }
+ DEBUG(10, ("mod_smbfilepwd_entry: opening file %s\n", pfile));
+
+ fp = sys_fopen(pfile, "r+");
+
+ if (fp == NULL) {
+ DEBUG(0, ("mod_smbfilepwd_entry: unable to open file %s\n", pfile));
+ return False;
+ }
+ /* Set a buffer to do more efficient reads */
+ setvbuf(fp, readbuf, _IOFBF, sizeof(readbuf));
+
+ lockfd = fileno(fp);
+
+ if (!pw_file_lock(lockfd, F_WRLCK, 5, &(smbpasswd_state->pw_file_lock_depth))) {
+ DEBUG(0, ("mod_smbfilepwd_entry: unable to lock file %s\n", pfile));
+ fclose(fp);
+ return False;
+ }
+
+ /* Make sure it is only rw by the owner */
+ chmod(pfile, 0600);
+
+ /* We have a write lock on the file. */
+ /*
+ * Scan the file, a line at a time and check if the name matches.
+ */
+ while (!feof(fp)) {
+ pwd_seekpos = sys_ftell(fp);
+
+ linebuf[0] = '\0';
+
+ fgets(linebuf, sizeof(linebuf), fp);
+ if (ferror(fp)) {
+ pw_file_unlock(lockfd, &(smbpasswd_state->pw_file_lock_depth));
+ fclose(fp);
+ return False;
+ }
+
+ /*
+ * Check if the string is terminated with a newline - if not
+ * then we must keep reading and discard until we get one.
+ */
+ linebuf_len = strlen(linebuf);
+ if (linebuf[linebuf_len - 1] != '\n') {
+ c = '\0';
+ while (!ferror(fp) && !feof(fp)) {
+ c = fgetc(fp);
+ if (c == '\n') {
+ break;
+ }
+ }
+ } else {
+ linebuf[linebuf_len - 1] = '\0';
+ }
+
+#ifdef DEBUG_PASSWORD
+ DEBUG(100, ("mod_smbfilepwd_entry: got line |%s|\n", linebuf));
+#endif
+
+ if ((linebuf[0] == 0) && feof(fp)) {
+ DEBUG(4, ("mod_smbfilepwd_entry: end of file reached\n"));
+ break;
+ }
+
+ /*
+ * The line we have should be of the form :-
+ *
+ * username:uid:[32hex bytes]:....other flags presently
+ * ignored....
+ *
+ * or,
+ *
+ * username:uid:[32hex bytes]:[32hex bytes]:[attributes]:LCT-XXXXXXXX:...ignored.
+ *
+ * if Windows NT compatible passwords are also present.
+ */
+
+ if (linebuf[0] == '#' || linebuf[0] == '\0') {
+ DEBUG(6, ("mod_smbfilepwd_entry: skipping comment or blank line\n"));
+ continue;
+ }
+
+ p = (unsigned char *) strchr_m(linebuf, ':');
+
+ if (p == NULL) {
+ DEBUG(0, ("mod_smbfilepwd_entry: malformed password entry (no :)\n"));
+ continue;
+ }
+
+ /*
+ * As 256 is shorter than a pstring we don't need to check
+ * length here - if this ever changes....
+ */
+
+ SMB_ASSERT(sizeof(user_name) > sizeof(linebuf));
+
+ strncpy(user_name, linebuf, PTR_DIFF(p, linebuf));
+ user_name[PTR_DIFF(p, linebuf)] = '\0';
+ if (strequal(user_name, pwd->smb_name)) {
+ found_entry = True;
+ break;
+ }
+ }
+
+ if (!found_entry) {
+ pw_file_unlock(lockfd, &(smbpasswd_state->pw_file_lock_depth));
+ fclose(fp);
+
+ DEBUG(2, ("Cannot update entry for user %s, as they don't exist in the smbpasswd file!\n",
+ pwd->smb_name));
+ return False;
+ }
+
+ DEBUG(6, ("mod_smbfilepwd_entry: entry exists\n"));
+
+ /* User name matches - get uid and password */
+ p++; /* Go past ':' */
+
+ if (!isdigit(*p)) {
+ DEBUG(0, ("mod_smbfilepwd_entry: malformed password entry (uid not number)\n"));
+ pw_file_unlock(lockfd, &(smbpasswd_state->pw_file_lock_depth));
+ fclose(fp);
+ return False;
+ }
+
+ while (*p && isdigit(*p))
+ p++;
+ if (*p != ':') {
+ DEBUG(0, ("mod_smbfilepwd_entry: malformed password entry (no : after uid)\n"));
+ pw_file_unlock(lockfd, &(smbpasswd_state->pw_file_lock_depth));
+ fclose(fp);
+ return False;
+ }
+
+ /*
+ * Now get the password value - this should be 32 hex digits
+ * which are the ascii representations of a 16 byte string.
+ * Get two at a time and put them into the password.
+ */
+ p++;
+
+ /* Record exact password position */
+ pwd_seekpos += PTR_DIFF(p, linebuf);
+
+ if (linebuf_len < (PTR_DIFF(p, linebuf) + 33)) {
+ DEBUG(0, ("mod_smbfilepwd_entry: malformed password entry (passwd too short)\n"));
+ pw_file_unlock(lockfd,&(smbpasswd_state->pw_file_lock_depth));
+ fclose(fp);
+ return (False);
+ }
+
+ if (p[32] != ':') {
+ DEBUG(0, ("mod_smbfilepwd_entry: malformed password entry (no terminating :)\n"));
+ pw_file_unlock(lockfd,&(smbpasswd_state->pw_file_lock_depth));
+ fclose(fp);
+ return False;
+ }
+
+ /* Now check if the NT compatible password is
+ available. */
+ p += 33; /* Move to the first character of the line after
+ the lanman password. */
+ if (linebuf_len < (PTR_DIFF(p, linebuf) + 33)) {
+ DEBUG(0, ("mod_smbfilepwd_entry: malformed password entry (passwd too short)\n"));
+ pw_file_unlock(lockfd,&(smbpasswd_state->pw_file_lock_depth));
+ fclose(fp);
+ return (False);
+ }
+
+ if (p[32] != ':') {
+ DEBUG(0, ("mod_smbfilepwd_entry: malformed password entry (no terminating :)\n"));
+ pw_file_unlock(lockfd,&(smbpasswd_state->pw_file_lock_depth));
+ fclose(fp);
+ return False;
+ }
+
+ /*
+ * Now check if the account info and the password last
+ * change time is available.
+ */
+ p += 33; /* Move to the first character of the line after
+ the NT password. */
+
+ if (*p == '[') {
+
+ i = 0;
+ encode_bits[i++] = *p++;
+ while((linebuf_len > PTR_DIFF(p, linebuf)) && (*p != ']'))
+ encode_bits[i++] = *p++;
+
+ encode_bits[i++] = ']';
+ encode_bits[i++] = '\0';
+
+ if(i == NEW_PW_FORMAT_SPACE_PADDED_LEN) {
+ /*
+ * We are using a new format, space padded
+ * acct ctrl field. Encode the given acct ctrl
+ * bits into it.
+ */
+ fstrcpy(encode_bits, pdb_encode_acct_ctrl(pwd->acct_ctrl, NEW_PW_FORMAT_SPACE_PADDED_LEN));
+ } else {
+ DEBUG(0,("mod_smbfilepwd_entry: Using old smbpasswd format. This is no longer supported.!\n"));
+ DEBUG(0,("mod_smbfilepwd_entry: No changes made, failing.!\n"));
+ return False;
+ }
+
+ /* Go past the ']' */
+ if(linebuf_len > PTR_DIFF(p, linebuf))
+ p++;
+
+ if((linebuf_len > PTR_DIFF(p, linebuf)) && (*p == ':')) {
+ p++;
+
+ /* We should be pointing at the LCT entry. */
+ if((linebuf_len > (PTR_DIFF(p, linebuf) + 13)) && (StrnCaseCmp((char *)p, "LCT-", 4) == 0)) {
+
+ p += 4;
+ for(i = 0; i < 8; i++) {
+ if(p[i] == '\0' || !isxdigit(p[i]))
+ break;
+ }
+ if(i == 8) {
+ /*
+ * p points at 8 characters of hex digits -
+ * read into a time_t as the seconds since
+ * 1970 that the password was last changed.
+ */
+ got_pass_last_set_time = True;
+ } /* i == 8 */
+ } /* *p && StrnCaseCmp() */
+ } /* p == ':' */
+ } /* p == '[' */
+
+ /* Entry is correctly formed. */
+
+ /* Create the 32 byte representation of the new p16 */
+ pdb_sethexpwd(ascii_p16, pwd->smb_passwd, pwd->acct_ctrl);
+
+ /* Add on the NT md4 hash */
+ ascii_p16[32] = ':';
+ wr_len = 66;
+ pdb_sethexpwd(ascii_p16+33, pwd->smb_nt_passwd, pwd->acct_ctrl);
+ ascii_p16[65] = ':';
+ ascii_p16[66] = '\0'; /* null-terminate the string so that strlen works */
+
+ /* Add on the account info bits and the time of last
+ password change. */
+
+ if(got_pass_last_set_time) {
+ slprintf(&ascii_p16[strlen(ascii_p16)],
+ sizeof(ascii_p16)-(strlen(ascii_p16)+1),
+ "%s:LCT-%08X:",
+ encode_bits, (uint32)pwd->pass_last_set_time );
+ wr_len = strlen(ascii_p16);
+ }
+
+#ifdef DEBUG_PASSWORD
+ DEBUG(100,("mod_smbfilepwd_entry: "));
+ dump_data(100, ascii_p16, wr_len);
+#endif
+
+ if(wr_len > sizeof(linebuf)) {
+ DEBUG(0, ("mod_smbfilepwd_entry: line to write (%d) is too long.\n", wr_len+1));
+ pw_file_unlock(lockfd,&(smbpasswd_state->pw_file_lock_depth));
+ fclose(fp);
+ return (False);
+ }
+
+ /*
+ * Do an atomic write into the file at the position defined by
+ * seekpos.
+ */
+
+ /* The mod user write needs to be atomic - so get the fd from
+ the fp and do a raw write() call.
+ */
+
+ fd = fileno(fp);
+
+ if (sys_lseek(fd, pwd_seekpos - 1, SEEK_SET) != pwd_seekpos - 1) {
+ DEBUG(0, ("mod_smbfilepwd_entry: seek fail on file %s.\n", pfile));
+ pw_file_unlock(lockfd,&(smbpasswd_state->pw_file_lock_depth));
+ fclose(fp);
+ return False;
+ }
+
+ /* Sanity check - ensure the areas we are writing are framed by ':' */
+ if (read(fd, linebuf, wr_len+1) != wr_len+1) {
+ DEBUG(0, ("mod_smbfilepwd_entry: read fail on file %s.\n", pfile));
+ pw_file_unlock(lockfd,&(smbpasswd_state->pw_file_lock_depth));
+ fclose(fp);
+ return False;
+ }
+
+ if ((linebuf[0] != ':') || (linebuf[wr_len] != ':')) {
+ DEBUG(0, ("mod_smbfilepwd_entry: check on passwd file %s failed.\n", pfile));
+ pw_file_unlock(lockfd,&(smbpasswd_state->pw_file_lock_depth));
+ fclose(fp);
+ return False;
+ }
+
+ if (sys_lseek(fd, pwd_seekpos, SEEK_SET) != pwd_seekpos) {
+ DEBUG(0, ("mod_smbfilepwd_entry: seek fail on file %s.\n", pfile));
+ pw_file_unlock(lockfd,&(smbpasswd_state->pw_file_lock_depth));
+ fclose(fp);
+ return False;
+ }
+
+ if (write(fd, ascii_p16, wr_len) != wr_len) {
+ DEBUG(0, ("mod_smbfilepwd_entry: write failed in passwd file %s\n", pfile));
+ pw_file_unlock(lockfd,&(smbpasswd_state->pw_file_lock_depth));
+ fclose(fp);
+ return False;
+ }
+
+ pw_file_unlock(lockfd,&(smbpasswd_state->pw_file_lock_depth));
+ fclose(fp);
+ return True;
+}
+
+/************************************************************************
+ Routine to delete an entry in the smbpasswd file by name.
+*************************************************************************/
+
+static BOOL del_smbfilepwd_entry(struct smbpasswd_privates *smbpasswd_state, const char *name)
+{
+ const char *pfile = smbpasswd_state->smbpasswd_file;
+ pstring pfile2;
+ struct smb_passwd *pwd = NULL;
+ FILE *fp = NULL;
+ FILE *fp_write = NULL;
+ int pfile2_lockdepth = 0;
+
+ slprintf(pfile2, sizeof(pfile2)-1, "%s.%u", pfile, (unsigned)sys_getpid() );
+
+ /*
+ * Open the smbpassword file - for update. It needs to be update
+ * as we need any other processes to wait until we have replaced
+ * it.
+ */
+
+ if((fp = startsmbfilepwent(pfile, PWF_UPDATE, &(smbpasswd_state->pw_file_lock_depth))) == NULL) {
+ DEBUG(0, ("del_smbfilepwd_entry: unable to open file %s.\n", pfile));
+ return False;
+ }
+
+ /*
+ * Create the replacement password file.
+ */
+ if((fp_write = startsmbfilepwent(pfile2, PWF_CREATE, &pfile2_lockdepth)) == NULL) {
+ DEBUG(0, ("del_smbfilepwd_entry: unable to open file %s.\n", pfile));
+ endsmbfilepwent(fp, &(smbpasswd_state->pw_file_lock_depth));
+ return False;
+ }
+
+ /*
+ * Scan the file, a line at a time and check if the name matches.
+ */
+
+ while ((pwd = getsmbfilepwent(smbpasswd_state, fp)) != NULL) {
+ char *new_entry;
+ size_t new_entry_length;
+
+ if (strequal(name, pwd->smb_name)) {
+ DEBUG(10, ("add_smbfilepwd_entry: found entry with name %s - deleting it.\n", name));
+ continue;
+ }
+
+ /*
+ * We need to copy the entry out into the second file.
+ */
+
+ if((new_entry = format_new_smbpasswd_entry(pwd)) == NULL)
+ {
+ DEBUG(0, ("del_smbfilepwd_entry(malloc): Failed to copy entry for user %s to file %s. \
+Error was %s\n", pwd->smb_name, pfile2, strerror(errno)));
+ unlink(pfile2);
+ endsmbfilepwent(fp, &(smbpasswd_state->pw_file_lock_depth));
+ endsmbfilepwent(fp_write, &pfile2_lockdepth);
+ return False;
+ }
+
+ new_entry_length = strlen(new_entry);
+
+ if(fwrite(new_entry, 1, new_entry_length, fp_write) != new_entry_length)
+ {
+ DEBUG(0, ("del_smbfilepwd_entry(write): Failed to copy entry for user %s to file %s. \
+Error was %s\n", pwd->smb_name, pfile2, strerror(errno)));
+ unlink(pfile2);
+ endsmbfilepwent(fp, &(smbpasswd_state->pw_file_lock_depth));
+ endsmbfilepwent(fp_write, &pfile2_lockdepth);
+ free(new_entry);
+ return False;
+ }
+
+ free(new_entry);
+ }
+
+ /*
+ * Ensure pfile2 is flushed before rename.
+ */
+
+ if(fflush(fp_write) != 0)
+ {
+ DEBUG(0, ("del_smbfilepwd_entry: Failed to flush file %s. Error was %s\n", pfile2, strerror(errno)));
+ endsmbfilepwent(fp, &(smbpasswd_state->pw_file_lock_depth));
+ endsmbfilepwent(fp_write,&pfile2_lockdepth);
+ return False;
+ }
+
+ /*
+ * Do an atomic rename - then release the locks.
+ */
+
+ if(rename(pfile2,pfile) != 0) {
+ unlink(pfile2);
+ }
+
+ endsmbfilepwent(fp, &(smbpasswd_state->pw_file_lock_depth));
+ endsmbfilepwent(fp_write,&pfile2_lockdepth);
+ return True;
+}
+
+/*********************************************************************
+ Create a smb_passwd struct from a SAM_ACCOUNT.
+ We will not allocate any new memory. The smb_passwd struct
+ should only stay around as long as the SAM_ACCOUNT does.
+ ********************************************************************/
+static BOOL build_smb_pass (struct smb_passwd *smb_pw, const SAM_ACCOUNT *sampass)
+{
+ uint32 rid;
+
+ if (sampass == NULL)
+ return False;
+ ZERO_STRUCTP(smb_pw);
+
+ if (!IS_SAM_DEFAULT(sampass, PDB_USERSID)) {
+ rid = pdb_get_user_rid(sampass);
+
+ /* If the user specified a RID, make sure its able to be both stored and retreived */
+ if (rid == DOMAIN_USER_RID_GUEST) {
+ struct passwd *passwd = getpwnam_alloc(lp_guestaccount());
+ if (!passwd) {
+ DEBUG(0, ("Could not find gest account via getpwnam()! (%s)\n", lp_guestaccount()));
+ return False;
+ }
+ smb_pw->smb_userid=passwd->pw_uid;
+ passwd_free(&passwd);
+
+ } else if (fallback_pdb_rid_is_user(rid)) {
+ smb_pw->smb_userid=fallback_pdb_user_rid_to_uid(rid);
+ } else {
+ DEBUG(0,("build_sam_pass: Failing attempt to store user with non-uid based user RID. \n"));
+ return False;
+ }
+ }
+
+ smb_pw->smb_name=(const char*)pdb_get_username(sampass);
+
+ smb_pw->smb_passwd=pdb_get_lanman_passwd(sampass);
+ smb_pw->smb_nt_passwd=pdb_get_nt_passwd(sampass);
+
+ smb_pw->acct_ctrl=pdb_get_acct_ctrl(sampass);
+ smb_pw->pass_last_set_time=pdb_get_pass_last_set_time(sampass);
+
+ return True;
+}
+
+/*********************************************************************
+ Create a SAM_ACCOUNT from a smb_passwd struct
+ ********************************************************************/
+static BOOL build_sam_account(struct smbpasswd_privates *smbpasswd_state,
+ SAM_ACCOUNT *sam_pass, const struct smb_passwd *pw_buf)
+{
+ struct passwd *pwfile;
+
+ if (sam_pass==NULL) {
+ DEBUG(5,("build_sam_account: SAM_ACCOUNT is NULL\n"));
+ return False;
+ }
+
+ /* verify the user account exists */
+
+ if ( !(pwfile = getpwnam_alloc(pw_buf->smb_name)) ) {
+ DEBUG(0,("build_sam_account: smbpasswd database is corrupt! username %s with uid "
+ "%u is not in unix passwd database!\n", pw_buf->smb_name, pw_buf->smb_userid));
+ return False;
+ }
+
+ if (!NT_STATUS_IS_OK(pdb_fill_sam_pw(sam_pass, pwfile)))
+ return False;
+
+ passwd_free(&pwfile);
+
+ /* set remaining fields */
+
+ pdb_set_nt_passwd (sam_pass, pw_buf->smb_nt_passwd, PDB_SET);
+ pdb_set_lanman_passwd (sam_pass, pw_buf->smb_passwd, PDB_SET);
+ pdb_set_acct_ctrl (sam_pass, pw_buf->acct_ctrl, PDB_SET);
+ pdb_set_pass_last_set_time (sam_pass, pw_buf->pass_last_set_time, PDB_SET);
+ pdb_set_pass_can_change_time (sam_pass, pw_buf->pass_last_set_time, PDB_SET);
+
+ return True;
+}
+
+/*****************************************************************
+ Functions to be implemented by the new passdb API
+ ****************************************************************/
+static NTSTATUS smbpasswd_setsampwent (struct pdb_methods *my_methods, BOOL update)
+{
+ struct smbpasswd_privates *smbpasswd_state = (struct smbpasswd_privates*)my_methods->private_data;
+
+ smbpasswd_state->pw_file = startsmbfilepwent(smbpasswd_state->smbpasswd_file,
+ update ? PWF_UPDATE : PWF_READ,
+ &(smbpasswd_state->pw_file_lock_depth));
+
+ /* did we fail? Should we try to create it? */
+ if (!smbpasswd_state->pw_file && update && errno == ENOENT)
+ {
+ FILE *fp;
+ /* slprintf(msg_str,msg_str_len-1,
+ "smbpasswd file did not exist - attempting to create it.\n"); */
+ DEBUG(0,("smbpasswd file did not exist - attempting to create it.\n"));
+ fp = sys_fopen(smbpasswd_state->smbpasswd_file, "w");
+ if (fp)
+ {
+ fprintf(fp, "# Samba SMB password file\n");
+ fclose(fp);
+ }
+
+ smbpasswd_state->pw_file = startsmbfilepwent(smbpasswd_state->smbpasswd_file,
+ update ? PWF_UPDATE : PWF_READ,
+ &(smbpasswd_state->pw_file_lock_depth));
+ }
+
+ if (smbpasswd_state->pw_file != NULL)
+ return NT_STATUS_OK;
+ else
+ return NT_STATUS_UNSUCCESSFUL;
+}
+
+static void smbpasswd_endsampwent (struct pdb_methods *my_methods)
+{
+ struct smbpasswd_privates *smbpasswd_state = (struct smbpasswd_privates*)my_methods->private_data;
+ endsmbfilepwent(smbpasswd_state->pw_file, &(smbpasswd_state->pw_file_lock_depth));
+}
+
+/*****************************************************************
+ ****************************************************************/
+static NTSTATUS smbpasswd_getsampwent(struct pdb_methods *my_methods, SAM_ACCOUNT *user)
+{
+ NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
+ struct smbpasswd_privates *smbpasswd_state = (struct smbpasswd_privates*)my_methods->private_data;
+ struct smb_passwd *pw_buf=NULL;
+ BOOL done = False;
+ DEBUG(5,("pdb_getsampwent\n"));
+
+ if (user==NULL) {
+ DEBUG(5,("pdb_getsampwent (smbpasswd): user is NULL\n"));
+#if 0
+ smb_panic("NULL pointer passed to getsampwent (smbpasswd)\n");
+#endif
+ return nt_status;
+ }
+
+ while (!done)
+ {
+ /* do we have an entry? */
+ pw_buf = getsmbfilepwent(smbpasswd_state, smbpasswd_state->pw_file);
+ if (pw_buf == NULL)
+ return nt_status;
+
+ /* build the SAM_ACCOUNT entry from the smb_passwd struct.
+ We loop in case the user in the pdb does not exist in
+ the local system password file */
+ if (build_sam_account(smbpasswd_state, user, pw_buf))
+ done = True;
+ }
+
+ DEBUG(5,("getsampwent (smbpasswd): done\n"));
+
+ /* success */
+ return NT_STATUS_OK;
+}
+
+
+/****************************************************************
+ Search smbpasswd file by iterating over the entries. Do not
+ call getpwnam() for unix account information until we have found
+ the correct entry
+ ***************************************************************/
+static NTSTATUS smbpasswd_getsampwnam(struct pdb_methods *my_methods,
+ SAM_ACCOUNT *sam_acct, const char *username)
+{
+ NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
+ struct smbpasswd_privates *smbpasswd_state = (struct smbpasswd_privates*)my_methods->private_data;
+ struct smb_passwd *smb_pw;
+ void *fp = NULL;
+
+ DEBUG(10, ("getsampwnam (smbpasswd): search by name: %s\n", username));
+
+ /* startsmbfilepwent() is used here as we don't want to lookup
+ the UNIX account in the local system password file until
+ we have a match. */
+ fp = startsmbfilepwent(smbpasswd_state->smbpasswd_file, PWF_READ, &(smbpasswd_state->pw_file_lock_depth));
+
+ if (fp == NULL) {
+ DEBUG(0, ("Unable to open passdb database.\n"));
+ return nt_status;
+ }
+
+ while ( ((smb_pw=getsmbfilepwent(smbpasswd_state, fp)) != NULL)&& (!strequal(smb_pw->smb_name, username)) )
+ /* do nothing....another loop */ ;
+
+ endsmbfilepwent(fp, &(smbpasswd_state->pw_file_lock_depth));
+
+
+ /* did we locate the username in smbpasswd */
+ if (smb_pw == NULL)
+ return nt_status;
+
+ DEBUG(10, ("getsampwnam (smbpasswd): found by name: %s\n", smb_pw->smb_name));
+
+ if (!sam_acct) {
+ DEBUG(10,("getsampwnam (smbpasswd): SAM_ACCOUNT is NULL\n"));
+#if 0
+ smb_panic("NULL pointer passed to pdb_getsampwnam\n");
+#endif
+ return nt_status;
+ }
+
+ /* now build the SAM_ACCOUNT */
+ if (!build_sam_account(smbpasswd_state, sam_acct, smb_pw))
+ return nt_status;
+
+ /* success */
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS smbpasswd_getsampwsid(struct pdb_methods *my_methods, SAM_ACCOUNT *sam_acct, const DOM_SID *sid)
+{
+ NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
+ struct smbpasswd_privates *smbpasswd_state = (struct smbpasswd_privates*)my_methods->private_data;
+ struct smb_passwd *smb_pw;
+ void *fp = NULL;
+ fstring sid_str;
+ uint32 rid;
+
+ DEBUG(10, ("smbpasswd_getsampwrid: search by sid: %s\n", sid_to_string(sid_str, sid)));
+
+ if (!sid_peek_check_rid(get_global_sam_sid(), sid, &rid))
+ return NT_STATUS_UNSUCCESSFUL;
+
+ /* More special case 'guest account' hacks... */
+ if (rid == DOMAIN_USER_RID_GUEST) {
+ const char *guest_account = lp_guestaccount();
+ if (!(guest_account && *guest_account)) {
+ DEBUG(1, ("Guest account not specfied!\n"));
+ return nt_status;
+ }
+ return smbpasswd_getsampwnam(my_methods, sam_acct, guest_account);
+ }
+
+ /* Open the sam password file - not for update. */
+ fp = startsmbfilepwent(smbpasswd_state->smbpasswd_file, PWF_READ, &(smbpasswd_state->pw_file_lock_depth));
+
+ if (fp == NULL) {
+ DEBUG(0, ("Unable to open passdb database.\n"));
+ return nt_status;
+ }
+
+ while ( ((smb_pw=getsmbfilepwent(smbpasswd_state, fp)) != NULL) && (fallback_pdb_uid_to_user_rid(smb_pw->smb_userid) != rid) )
+ /* do nothing */ ;
+
+ endsmbfilepwent(fp, &(smbpasswd_state->pw_file_lock_depth));
+
+
+ /* did we locate the username in smbpasswd */
+ if (smb_pw == NULL)
+ return nt_status;
+
+ DEBUG(10, ("getsampwrid (smbpasswd): found by name: %s\n", smb_pw->smb_name));
+
+ if (!sam_acct) {
+ DEBUG(10,("getsampwrid: (smbpasswd) SAM_ACCOUNT is NULL\n"));
+#if 0
+ smb_panic("NULL pointer passed to pdb_getsampwrid\n");
+#endif
+ return nt_status;
+ }
+
+ /* now build the SAM_ACCOUNT */
+ if (!build_sam_account (smbpasswd_state, sam_acct, smb_pw))
+ return nt_status;
+
+ /* build_sam_account might change the SID on us, if the name was for the guest account */
+ if (NT_STATUS_IS_OK(nt_status) && !sid_equal(pdb_get_user_sid(sam_acct), sid)) {
+ fstring sid_string1, sid_string2;
+ DEBUG(1, ("looking for user with sid %s instead returned %s for account %s!?!\n",
+ sid_to_string(sid_string1, sid), sid_to_string(sid_string2, pdb_get_user_sid(sam_acct)), pdb_get_username(sam_acct)));
+ return NT_STATUS_NO_SUCH_USER;
+ }
+
+ /* success */
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS smbpasswd_add_sam_account(struct pdb_methods *my_methods, SAM_ACCOUNT *sampass)
+{
+ struct smbpasswd_privates *smbpasswd_state = (struct smbpasswd_privates*)my_methods->private_data;
+ struct smb_passwd smb_pw;
+
+ /* convert the SAM_ACCOUNT */
+ if (!build_smb_pass(&smb_pw, sampass)) {
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ /* add the entry */
+ if(!add_smbfilepwd_entry(smbpasswd_state, &smb_pw)) {
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS smbpasswd_update_sam_account(struct pdb_methods *my_methods, SAM_ACCOUNT *sampass)
+{
+ struct smbpasswd_privates *smbpasswd_state = (struct smbpasswd_privates*)my_methods->private_data;
+ struct smb_passwd smb_pw;
+
+ /* convert the SAM_ACCOUNT */
+ if (!build_smb_pass(&smb_pw, sampass)) {
+ DEBUG(0, ("smbpasswd_update_sam_account: build_smb_pass failed!\n"));
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ /* update the entry */
+ if(!mod_smbfilepwd_entry(smbpasswd_state, &smb_pw)) {
+ DEBUG(0, ("smbpasswd_update_sam_account: mod_smbfilepwd_entry failed!\n"));
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS smbpasswd_delete_sam_account (struct pdb_methods *my_methods, SAM_ACCOUNT *sampass)
+{
+ struct smbpasswd_privates *smbpasswd_state = (struct smbpasswd_privates*)my_methods->private_data;
+
+ const char *username = pdb_get_username(sampass);
+
+ if (del_smbfilepwd_entry(smbpasswd_state, username))
+ return NT_STATUS_OK;
+
+ return NT_STATUS_UNSUCCESSFUL;
+}
+
+static void free_private_data(void **vp)
+{
+ struct smbpasswd_privates **privates = (struct smbpasswd_privates**)vp;
+
+ endsmbfilepwent((*privates)->pw_file, &((*privates)->pw_file_lock_depth));
+
+ *privates = NULL;
+ /* No need to free any further, as it is talloc()ed */
+}
+
+static NTSTATUS pdb_init_smbpasswd(PDB_CONTEXT *pdb_context, PDB_METHODS **pdb_method, const char *location)
+{
+ NTSTATUS nt_status;
+ struct smbpasswd_privates *privates;
+
+ if (!NT_STATUS_IS_OK(nt_status = make_pdb_methods(pdb_context->mem_ctx, pdb_method))) {
+ return nt_status;
+ }
+
+ (*pdb_method)->name = "smbpasswd";
+
+ (*pdb_method)->setsampwent = smbpasswd_setsampwent;
+ (*pdb_method)->endsampwent = smbpasswd_endsampwent;
+ (*pdb_method)->getsampwent = smbpasswd_getsampwent;
+ (*pdb_method)->getsampwnam = smbpasswd_getsampwnam;
+ (*pdb_method)->getsampwsid = smbpasswd_getsampwsid;
+ (*pdb_method)->add_sam_account = smbpasswd_add_sam_account;
+ (*pdb_method)->update_sam_account = smbpasswd_update_sam_account;
+ (*pdb_method)->delete_sam_account = smbpasswd_delete_sam_account;
+
+ /* Setup private data and free function */
+
+ privates = talloc_zero(pdb_context->mem_ctx, sizeof(struct smbpasswd_privates));
+
+ if (!privates) {
+ DEBUG(0, ("talloc() failed for smbpasswd private_data!\n"));
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ /* Store some config details */
+
+ if (location) {
+ privates->smbpasswd_file = talloc_strdup(pdb_context->mem_ctx, location);
+ } else {
+ privates->smbpasswd_file = talloc_strdup(pdb_context->mem_ctx, lp_smb_passwd_file());
+ }
+
+ if (!privates->smbpasswd_file) {
+ DEBUG(0, ("talloc_strdp() failed for storing smbpasswd location!\n"));
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ (*pdb_method)->private_data = privates;
+
+ (*pdb_method)->free_private_data = free_private_data;
+
+ return NT_STATUS_OK;
+}
+
+NTSTATUS pdb_smbpasswd_init(void)
+{
+ return smb_register_passdb(PASSDB_INTERFACE_VERSION, "smbpasswd", pdb_init_smbpasswd);
+}
diff --git a/source/passdb/pdb_sql.c b/source/passdb/pdb_sql.c
new file mode 100644
index 00000000000..ffb8313a970
--- /dev/null
+++ b/source/passdb/pdb_sql.c
@@ -0,0 +1,493 @@
+/*
+ * Common PDB SQL backend functions
+ * Copyright (C) Jelmer Vernooij 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 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"
+
+#define CONFIG_TABLE_DEFAULT "user"
+#define CONFIG_LOGON_TIME_DEFAULT "logon_time"
+#define CONFIG_LOGOFF_TIME_DEFAULT "logoff_time"
+#define CONFIG_KICKOFF_TIME_DEFAULT "kickoff_time"
+#define CONFIG_PASS_LAST_SET_TIME_DEFAULT "pass_last_set_time"
+#define CONFIG_PASS_CAN_CHANGE_TIME_DEFAULT "pass_can_change_time"
+#define CONFIG_PASS_MUST_CHANGE_TIME_DEFAULT "pass_must_change_time"
+#define CONFIG_USERNAME_DEFAULT "username"
+#define CONFIG_DOMAIN_DEFAULT "domain"
+#define CONFIG_NT_USERNAME_DEFAULT "nt_username"
+#define CONFIG_FULLNAME_DEFAULT "nt_fullname"
+#define CONFIG_HOME_DIR_DEFAULT "home_dir"
+#define CONFIG_DIR_DRIVE_DEFAULT "dir_drive"
+#define CONFIG_LOGON_SCRIPT_DEFAULT "logon_script"
+#define CONFIG_PROFILE_PATH_DEFAULT "profile_path"
+#define CONFIG_ACCT_DESC_DEFAULT "acct_desc"
+#define CONFIG_WORKSTATIONS_DEFAULT "workstations"
+#define CONFIG_UNKNOWN_STR_DEFAULT "unknown_str"
+#define CONFIG_MUNGED_DIAL_DEFAULT "munged_dial"
+#define CONFIG_USER_SID_DEFAULT "user_sid"
+#define CONFIG_GROUP_SID_DEFAULT "group_sid"
+#define CONFIG_LM_PW_DEFAULT "lm_pw"
+#define CONFIG_NT_PW_DEFAULT "nt_pw"
+#define CONFIG_PLAIN_PW_DEFAULT "NULL"
+#define CONFIG_ACCT_CTRL_DEFAULT "acct_ctrl"
+#define CONFIG_LOGON_DIVS_DEFAULT "logon_divs"
+#define CONFIG_HOURS_LEN_DEFAULT "hours_len"
+#define CONFIG_BAD_PASSWORD_COUNT_DEFAULT "bad_password_count"
+#define CONFIG_LOGON_COUNT_DEFAULT "logon_count"
+#define CONFIG_UNKNOWN_6_DEFAULT "unknown_6"
+
+/* Used to construct insert and update queries */
+
+typedef struct pdb_sql_query {
+ char update;
+ TALLOC_CTX *mem_ctx;
+ char *part1;
+ char *part2;
+} pdb_sql_query;
+
+static void pdb_sql_int_field(struct pdb_sql_query *q, const char *name, int value)
+{
+ if (!name || strchr(name, '\''))
+ return; /* This field shouldn't be set by us */
+
+ if (q->update) {
+ q->part1 =
+ talloc_asprintf_append(q->mem_ctx, q->part1,
+ "%s = %d,", name, value);
+ } else {
+ q->part1 =
+ talloc_asprintf_append(q->mem_ctx, q->part1, "%s,", name);
+ q->part2 =
+ talloc_asprintf_append(q->mem_ctx, q->part2, "%d,", value);
+ }
+}
+
+char *sql_escape_string(const char *unesc)
+{
+ char *esc = malloc(strlen(unesc) * 2 + 3);
+ size_t pos_unesc = 0, pos_esc = 0;
+
+ for(pos_unesc = 0; unesc[pos_unesc]; pos_unesc++) {
+ switch(unesc[pos_unesc]) {
+ case '\\':
+ case '\'':
+ case '"':
+ esc[pos_esc] = '\\'; pos_esc++;
+ default:
+ esc[pos_esc] = unesc[pos_unesc]; pos_esc++;
+ break;
+ }
+ }
+
+ esc[pos_esc] = '\0';
+
+ return esc;
+}
+
+static NTSTATUS pdb_sql_string_field(struct pdb_sql_query *q,
+ const char *name, const char *value)
+{
+ char *esc_value;
+
+ if (!name || !value || !strcmp(value, "") || strchr(name, '\''))
+ return NT_STATUS_INVALID_PARAMETER; /* This field shouldn't be set by module */
+
+ esc_value = sql_escape_string(value);
+
+ if (q->update) {
+ q->part1 =
+ talloc_asprintf_append(q->mem_ctx, q->part1,
+ "%s = '%s',", name, esc_value);
+ } else {
+ q->part1 =
+ talloc_asprintf_append(q->mem_ctx, q->part1, "%s,", name);
+ q->part2 =
+ talloc_asprintf_append(q->mem_ctx, q->part2, "'%s',",
+ esc_value);
+ }
+
+ SAFE_FREE(esc_value);
+
+ return NT_STATUS_OK;
+}
+
+#define config_value(data,name,default_value) \
+ lp_parm_const_string(GLOBAL_SECTION_SNUM, data, name, default_value)
+
+static const char * config_value_write(const char *location, const char *name, const char *default_value)
+{
+ char const *v = NULL;
+ char const *swrite = NULL;
+
+ v = lp_parm_const_string(GLOBAL_SECTION_SNUM, location, name, default_value);
+
+ if (!v)
+ return NULL;
+
+ swrite = strrchr(v, ':');
+
+ /* Default to the same field as read field */
+ if (!swrite)
+ return v;
+
+ swrite++;
+
+ /* If the field is 0 chars long, we shouldn't write to it */
+ if (!strlen(swrite) || !strcmp(swrite, "NULL"))
+ return NULL;
+
+ /* Otherwise, use the additionally specified */
+ return swrite;
+}
+
+static const char * config_value_read(const char *location, const char *name, const char *default_value)
+{
+ char *v = NULL;
+ char *swrite;
+
+ v = lp_parm_talloc_string(GLOBAL_SECTION_SNUM, location, name, default_value);
+
+ if (!v)
+ return "NULL";
+
+ swrite = strrchr(v, ':');
+
+ /* If no write is specified, there are no problems */
+ if (!swrite) {
+ if (strlen(v) == 0)
+ return "NULL";
+ return (const char *)v;
+ }
+
+ /* Otherwise, we have to cut the ':write_part' */
+ *swrite = '\0';
+ if (strlen(v) == 0)
+ return "NULL";
+
+ return (const char *)v;
+}
+
+char *sql_account_query_select(const char *data, BOOL update, enum sql_search_field field, const char *value)
+{
+ const char *field_string;
+ char *query;
+
+ switch(field) {
+ case SQL_SEARCH_NONE:
+ field_string = "'1'";
+ value = "1";
+ break;
+
+ case SQL_SEARCH_USER_SID:
+ field_string = config_value_read(data, "user sid column",
+ CONFIG_USER_SID_DEFAULT);
+ break;
+
+ case SQL_SEARCH_USER_NAME:
+ field_string = config_value_read(data, "username column",
+ CONFIG_USERNAME_DEFAULT);
+ break;
+ }
+
+ asprintf(&query,
+ "SELECT %s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s FROM %s WHERE %s = '%s'",
+ config_value_read(data, "logon time column",
+ CONFIG_LOGON_TIME_DEFAULT),
+ config_value_read(data, "logoff time column",
+ CONFIG_LOGOFF_TIME_DEFAULT),
+ config_value_read(data, "kickoff time column",
+ CONFIG_KICKOFF_TIME_DEFAULT),
+ config_value_read(data, "pass last set time column",
+ CONFIG_PASS_LAST_SET_TIME_DEFAULT),
+ config_value_read(data, "pass can change time column",
+ CONFIG_PASS_CAN_CHANGE_TIME_DEFAULT),
+ config_value_read(data, "pass must change time column",
+ CONFIG_PASS_MUST_CHANGE_TIME_DEFAULT),
+ config_value_read(data, "username column",
+ CONFIG_USERNAME_DEFAULT),
+ config_value_read(data, "domain column",
+ CONFIG_DOMAIN_DEFAULT),
+ config_value_read(data, "nt username column",
+ CONFIG_NT_USERNAME_DEFAULT),
+ config_value_read(data, "fullname column",
+ CONFIG_FULLNAME_DEFAULT),
+ config_value_read(data, "home dir column",
+ CONFIG_HOME_DIR_DEFAULT),
+ config_value_read(data, "dir drive column",
+ CONFIG_DIR_DRIVE_DEFAULT),
+ config_value_read(data, "logon script column",
+ CONFIG_LOGON_SCRIPT_DEFAULT),
+ config_value_read(data, "profile path column",
+ CONFIG_PROFILE_PATH_DEFAULT),
+ config_value_read(data, "acct desc column",
+ CONFIG_ACCT_DESC_DEFAULT),
+ config_value_read(data, "workstations column",
+ CONFIG_WORKSTATIONS_DEFAULT),
+ config_value_read(data, "unknown string column",
+ CONFIG_UNKNOWN_STR_DEFAULT),
+ config_value_read(data, "munged dial column",
+ CONFIG_MUNGED_DIAL_DEFAULT),
+ config_value_read(data, "user sid column",
+ CONFIG_USER_SID_DEFAULT),
+ config_value_read(data, "group sid column",
+ CONFIG_GROUP_SID_DEFAULT),
+ config_value_read(data, "lanman pass column",
+ CONFIG_LM_PW_DEFAULT),
+ config_value_read(data, "nt pass column",
+ CONFIG_NT_PW_DEFAULT),
+ config_value_read(data, "plain pass column",
+ CONFIG_PLAIN_PW_DEFAULT),
+ config_value_read(data, "acct ctrl column",
+ CONFIG_ACCT_CTRL_DEFAULT),
+ config_value_read(data, "logon divs column",
+ CONFIG_LOGON_DIVS_DEFAULT),
+ config_value_read(data, "hours len column",
+ CONFIG_HOURS_LEN_DEFAULT),
+ config_value_read(data, "bad password count column",
+ CONFIG_BAD_PASSWORD_COUNT_DEFAULT),
+ config_value_read(data, "logon count column",
+ CONFIG_LOGON_COUNT_DEFAULT),
+ config_value_read(data, "unknown 6 column",
+ CONFIG_UNKNOWN_6_DEFAULT),
+ config_value(data, "table", CONFIG_TABLE_DEFAULT),
+ field_string, value
+ );
+ return query;
+}
+
+char *sql_account_query_delete(const char *data, const char *esc)
+{
+ char *query;
+
+ asprintf(&query, "DELETE FROM %s WHERE %s = '%s'",
+ config_value(data, "table", CONFIG_TABLE_DEFAULT),
+ config_value_read(data, "username column",
+ CONFIG_USERNAME_DEFAULT), esc);
+ return query;
+}
+
+char *sql_account_query_update(const char *location, const SAM_ACCOUNT *newpwd, char isupdate)
+{
+ char *ret;
+ pstring temp;
+ pdb_sql_query query;
+ fstring sid_str;
+
+ query.update = isupdate;
+
+ /* I know this is somewhat overkill but only the talloc
+ * functions have asprint_append and the 'normal' asprintf
+ * is a GNU extension */
+ query.mem_ctx = talloc_init("sql_query_update");
+ query.part2 = talloc_asprintf(query.mem_ctx, "%s", "");
+ if (query.update) {
+ query.part1 =
+ talloc_asprintf(query.mem_ctx, "UPDATE %s SET ",
+ config_value(location, "table",
+ CONFIG_TABLE_DEFAULT));
+ } else {
+ query.part1 =
+ talloc_asprintf(query.mem_ctx, "INSERT INTO %s (",
+ config_value(location, "table",
+ CONFIG_TABLE_DEFAULT));
+ }
+
+ pdb_sql_int_field(&query,
+ config_value_write(location, "acct ctrl column",
+ CONFIG_ACCT_CTRL_DEFAULT),
+ pdb_get_acct_ctrl(newpwd));
+
+ if (pdb_get_init_flags(newpwd, PDB_LOGONTIME) != PDB_DEFAULT) {
+ pdb_sql_int_field(&query,
+ config_value_write(location,
+ "logon time column",
+ CONFIG_LOGON_TIME_DEFAULT),
+ pdb_get_logon_time(newpwd));
+ }
+
+ if (pdb_get_init_flags(newpwd, PDB_LOGOFFTIME) != PDB_DEFAULT) {
+ pdb_sql_int_field(&query,
+ config_value_write(location,
+ "logoff time column",
+ CONFIG_LOGOFF_TIME_DEFAULT),
+ pdb_get_logoff_time(newpwd));
+ }
+
+ if (pdb_get_init_flags(newpwd, PDB_KICKOFFTIME) != PDB_DEFAULT) {
+ pdb_sql_int_field(&query,
+ config_value_write(location,
+ "kickoff time column",
+ CONFIG_KICKOFF_TIME_DEFAULT),
+ pdb_get_kickoff_time(newpwd));
+ }
+
+ if (pdb_get_init_flags(newpwd, PDB_CANCHANGETIME) != PDB_DEFAULT) {
+ pdb_sql_int_field(&query,
+ config_value_write(location,
+ "pass can change time column",
+ CONFIG_PASS_CAN_CHANGE_TIME_DEFAULT),
+ pdb_get_pass_can_change_time(newpwd));
+ }
+
+ if (pdb_get_init_flags(newpwd, PDB_MUSTCHANGETIME) != PDB_DEFAULT) {
+ pdb_sql_int_field(&query,
+ config_value_write(location,
+ "pass must change time column",
+ CONFIG_PASS_MUST_CHANGE_TIME_DEFAULT),
+ pdb_get_pass_must_change_time(newpwd));
+ }
+
+ if (pdb_get_pass_last_set_time(newpwd)) {
+ pdb_sql_int_field(&query,
+ config_value_write(location,
+ "pass last set time column",
+ CONFIG_PASS_LAST_SET_TIME_DEFAULT),
+ pdb_get_pass_last_set_time(newpwd));
+ }
+
+ if (pdb_get_hours_len(newpwd)) {
+ pdb_sql_int_field(&query,
+ config_value_write(location,
+ "hours len column",
+ CONFIG_HOURS_LEN_DEFAULT),
+ pdb_get_hours_len(newpwd));
+ }
+
+ if (pdb_get_logon_divs(newpwd)) {
+ pdb_sql_int_field(&query,
+ config_value_write(location,
+ "logon divs column",
+ CONFIG_LOGON_DIVS_DEFAULT),
+ pdb_get_logon_divs(newpwd));
+ }
+
+ pdb_sql_string_field(&query,
+ config_value_write(location, "user sid column",
+ CONFIG_USER_SID_DEFAULT),
+ sid_to_string(sid_str,
+ pdb_get_user_sid(newpwd)));
+
+ pdb_sql_string_field(&query,
+ config_value_write(location, "group sid column",
+ CONFIG_GROUP_SID_DEFAULT),
+ sid_to_string(sid_str,
+ pdb_get_group_sid(newpwd)));
+
+ pdb_sql_string_field(&query,
+ config_value_write(location, "username column",
+ CONFIG_USERNAME_DEFAULT),
+ pdb_get_username(newpwd));
+
+ pdb_sql_string_field(&query,
+ config_value_write(location, "domain column",
+ CONFIG_DOMAIN_DEFAULT),
+ pdb_get_domain(newpwd));
+
+ pdb_sql_string_field(&query,
+ config_value_write(location,
+ "nt username column",
+ CONFIG_NT_USERNAME_DEFAULT),
+ pdb_get_nt_username(newpwd));
+
+ pdb_sql_string_field(&query,
+ config_value_write(location, "fullname column",
+ CONFIG_FULLNAME_DEFAULT),
+ pdb_get_fullname(newpwd));
+
+ pdb_sql_string_field(&query,
+ config_value_write(location,
+ "logon script column",
+ CONFIG_LOGON_SCRIPT_DEFAULT),
+ pdb_get_logon_script(newpwd));
+
+ pdb_sql_string_field(&query,
+ config_value_write(location,
+ "profile path column",
+ CONFIG_PROFILE_PATH_DEFAULT),
+ pdb_get_profile_path(newpwd));
+
+ pdb_sql_string_field(&query,
+ config_value_write(location, "dir drive column",
+ CONFIG_DIR_DRIVE_DEFAULT),
+ pdb_get_dir_drive(newpwd));
+
+ pdb_sql_string_field(&query,
+ config_value_write(location, "home dir column",
+ CONFIG_HOME_DIR_DEFAULT),
+ pdb_get_homedir(newpwd));
+
+ pdb_sql_string_field(&query,
+ config_value_write(location,
+ "workstations column",
+ CONFIG_WORKSTATIONS_DEFAULT),
+ pdb_get_workstations(newpwd));
+
+ pdb_sql_string_field(&query,
+ config_value_write(location,
+ "unknown string column",
+ CONFIG_UNKNOWN_STR_DEFAULT),
+ pdb_get_workstations(newpwd));
+
+ pdb_sethexpwd(temp, pdb_get_lanman_passwd(newpwd),
+ pdb_get_acct_ctrl(newpwd));
+ pdb_sql_string_field(&query,
+ config_value_write(location,
+ "lanman pass column",
+ CONFIG_LM_PW_DEFAULT), temp);
+
+ pdb_sethexpwd(temp, pdb_get_nt_passwd(newpwd),
+ pdb_get_acct_ctrl(newpwd));
+ pdb_sql_string_field(&query,
+ config_value_write(location, "nt pass column",
+ CONFIG_NT_PW_DEFAULT), temp);
+
+ if (query.update) {
+ query.part1[strlen(query.part1) - 1] = '\0';
+ query.part1 =
+ talloc_asprintf_append(query.mem_ctx, query.part1,
+ " WHERE %s = '%s'",
+ config_value_read(location,
+ "user sid column",
+ CONFIG_USER_SID_DEFAULT),
+ sid_to_string(sid_str, pdb_get_user_sid (newpwd)));
+ } else {
+ query.part2[strlen(query.part2) - 1] = ')';
+ query.part1[strlen(query.part1) - 1] = ')';
+ query.part1 =
+ talloc_asprintf_append(query.mem_ctx, query.part1,
+ " VALUES (%s", query.part2);
+ }
+
+ ret = strdup(query.part1);
+ talloc_destroy(query.mem_ctx);
+ return ret;
+}
+
+BOOL sql_account_config_valid(const char *data)
+{
+ const char *sid_column, *username_column;
+
+ sid_column = config_value_read(data, "user sid column", CONFIG_USER_SID_DEFAULT);
+ username_column = config_value_read(data, "username column", CONFIG_USERNAME_DEFAULT);
+
+ if(!strcmp(sid_column,"NULL") || !strcmp(username_column, "NULL")) {
+ DEBUG(0,("Please specify both a valid 'user sid column' and a valid 'username column' in smb.conf\n"));
+ return False;
+ }
+
+ return True;
+}
diff --git a/source/passdb/pdb_tdb.c b/source/passdb/pdb_tdb.c
new file mode 100644
index 00000000000..2af6609ef6c
--- /dev/null
+++ b/source/passdb/pdb_tdb.c
@@ -0,0 +1,1374 @@
+/*
+ * Unix SMB/CIFS implementation.
+ * SMB parameters and setup
+ * Copyright (C) Andrew Tridgell 1992-1998
+ * Copyright (C) Simo Sorce 2000-2002
+ * Copyright (C) Gerald Carter 2000
+ * Copyright (C) Jeremy Allison 2001
+ * 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 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"
+
+#if 0 /* when made a module use this */
+
+static int tdbsam_debug_level = DBGC_ALL;
+#undef DBGC_CLASS
+#define DBGC_CLASS tdbsam_debug_level
+
+#else
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_PASSDB
+
+#endif
+
+#define TDBSAM_VERSION 1 /* Most recent TDBSAM version */
+#define TDBSAM_VERSION_STRING "INFO/version"
+#define PASSDB_FILE_NAME "passdb.tdb"
+#define USERPREFIX "USER_"
+#define RIDPREFIX "RID_"
+#define PRIVPREFIX "PRIV_"
+#define tdbsamver_t int32
+
+struct tdbsam_privates {
+ TDB_CONTEXT *passwd_tdb;
+
+ /* retrive-once info */
+ const char *tdbsam_location;
+};
+
+struct pwent_list {
+ struct pwent_list *prev, *next;
+ TDB_DATA key;
+};
+static struct pwent_list *tdbsam_pwent_list;
+
+
+/**
+ * Convert old TDBSAM to the latest version.
+ * @param pdb_tdb A pointer to the opened TDBSAM file which must be converted.
+ * This file must be opened with read/write access.
+ * @param from Current version of the TDBSAM file.
+ * @return True if the conversion has been successful, false otherwise.
+ **/
+
+static BOOL tdbsam_convert(TDB_CONTEXT *pdb_tdb, tdbsamver_t from)
+{
+ const char * vstring = TDBSAM_VERSION_STRING;
+ SAM_ACCOUNT *user = NULL;
+ const char *prefix = USERPREFIX;
+ TDB_DATA data, key, old_key;
+ uint8 *buf = NULL;
+ BOOL ret;
+
+ if (pdb_tdb == NULL) {
+ DEBUG(0,("tdbsam_convert: Bad TDB Context pointer.\n"));
+ return False;
+ }
+
+ /* handle a Samba upgrade */
+ tdb_lock_bystring(pdb_tdb, vstring, 0);
+
+ if (!NT_STATUS_IS_OK(pdb_init_sam(&user))) {
+ DEBUG(0,("tdbsam_convert: cannot initialized a SAM_ACCOUNT.\n"));
+ return False;
+ }
+
+ /* Enumerate all records and convert them */
+ key = tdb_firstkey(pdb_tdb);
+
+ while (key.dptr) {
+
+ /* skip all non-USER entries (eg. RIDs) */
+ while ((key.dsize != 0) && (strncmp(key.dptr, prefix, strlen (prefix)))) {
+ old_key = key;
+ /* increment to next in line */
+ key = tdb_nextkey(pdb_tdb, key);
+ SAFE_FREE(old_key.dptr);
+ }
+
+ if (key.dptr) {
+
+ /* read from tdbsam */
+ data = tdb_fetch(pdb_tdb, key);
+ if (!data.dptr) {
+ DEBUG(0,("tdbsam_convert: database entry not found: %s.\n",key.dptr));
+ return False;
+ }
+
+ if (!NT_STATUS_IS_OK(pdb_reset_sam(user))) {
+ DEBUG(0,("tdbsam_convert: cannot reset SAM_ACCOUNT.\n"));
+ SAFE_FREE(data.dptr);
+ return False;
+ }
+
+ /* unpack the buffer from the former format */
+ DEBUG(10,("tdbsam_convert: Try unpacking a record with (key:%s) (version:%d)\n", key.dptr, from));
+ switch (from) {
+ case 0:
+ ret = init_sam_from_buffer_v0(user, (uint8 *)data.dptr, data.dsize);
+ break;
+ case 1:
+ ret = init_sam_from_buffer_v1(user, (uint8 *)data.dptr, data.dsize);
+ break;
+ default:
+ /* unknown tdbsam version */
+ ret = False;
+ }
+ if (!ret) {
+ DEBUG(0,("tdbsam_convert: Bad SAM_ACCOUNT entry returned from TDB (key:%s) (version:%d)\n", key.dptr, from));
+ SAFE_FREE(data.dptr);
+ return False;
+ }
+
+ /* pack from the buffer into the new format */
+ DEBUG(10,("tdbsam_convert: Try packing a record (key:%s) (version:%d)\n", key.dptr, from));
+ if ((data.dsize=init_buffer_from_sam (&buf, user, False)) == -1) {
+ DEBUG(0,("tdbsam_convert: cannot pack the SAM_ACCOUNT into the new format\n"));
+ SAFE_FREE(data.dptr);
+ return False;
+ }
+ data.dptr = (char *)buf;
+
+ /* Store the buffer inside the TDBSAM */
+ if (tdb_store(pdb_tdb, key, data, TDB_MODIFY) != TDB_SUCCESS) {
+ DEBUG(0,("tdbsam_convert: cannot store the SAM_ACCOUNT (key:%s) in new format\n",key.dptr));
+ SAFE_FREE(data.dptr);
+ return False;
+ }
+
+ SAFE_FREE(data.dptr);
+
+ /* increment to next in line */
+ old_key = key;
+ key = tdb_nextkey(pdb_tdb, key);
+ SAFE_FREE(old_key.dptr);
+ }
+
+ }
+
+ pdb_free_sam(&user);
+
+ /* upgrade finished */
+ tdb_store_int32(pdb_tdb, vstring, TDBSAM_VERSION);
+ tdb_unlock_bystring(pdb_tdb, vstring);
+
+ return(True);
+}
+
+/**
+ * Open the TDB passwd database, check version and convert it if needed.
+ * @param name filename of the tdbsam file.
+ * @param open_flags file access mode.
+ * @return a TDB_CONTEXT handle on the tdbsam file.
+ **/
+
+static TDB_CONTEXT * tdbsam_tdbopen (const char *name, int open_flags)
+{
+ TDB_CONTEXT *pdb_tdb;
+ tdbsamver_t version;
+
+ /* Try to open tdb passwd */
+ if (!(pdb_tdb = tdb_open_log(name, 0, TDB_DEFAULT,
+ open_flags, 0600))) {
+ DEBUG(0, ("Unable to open/create TDB passwd\n"));
+ return NULL;
+ }
+
+ /* Check the version */
+ version = (tdbsamver_t) tdb_fetch_int32(pdb_tdb,
+ TDBSAM_VERSION_STRING);
+ if (version == -1)
+ version = 0; /* Version not found, assume version 0 */
+
+ /* Compare the version */
+ if (version > TDBSAM_VERSION) {
+ /* Version more recent than the latest known */
+ DEBUG(0, ("TDBSAM version unknown: %d\n", version));
+ tdb_close(pdb_tdb);
+ pdb_tdb = NULL;
+ }
+ else if (version < TDBSAM_VERSION) {
+ /* Older version, must be converted */
+ DEBUG(1, ("TDBSAM version too old (%d), trying to convert it.\n", version));
+
+ /* Reopen the pdb file with read-write access if needed */
+ if (!(open_flags & O_RDWR)) {
+ DEBUG(10, ("tdbsam_tdbopen: TDB file opened with read only access, reopen it with read-write access.\n"));
+ tdb_close(pdb_tdb);
+ pdb_tdb = tdb_open_log(name, 0, TDB_DEFAULT, (open_flags & 07777770) | O_RDWR, 0600);
+ }
+
+ /* Convert */
+ if (!tdbsam_convert(pdb_tdb, version)){
+ DEBUG(0, ("tdbsam_tdbopen: Error when trying to convert tdbsam: %s\n",name));
+ tdb_close(pdb_tdb);
+ pdb_tdb = NULL;
+ } else {
+ DEBUG(1, ("TDBSAM converted successfully.\n"));
+ }
+
+ /* Reopen the pdb file as it must be */
+ if (!(open_flags & O_RDWR)) {
+ tdb_close(pdb_tdb);
+ pdb_tdb = tdb_open_log(name, 0, TDB_DEFAULT, open_flags, 0600);
+ }
+ }
+
+ return pdb_tdb;
+}
+
+/*****************************************************************************
+ Utility functions to close the tdb sam database
+ ****************************************************************************/
+
+static void tdbsam_tdbclose ( struct tdbsam_privates *state )
+{
+ if ( !state )
+ return;
+
+ if ( state->passwd_tdb ) {
+ tdb_close( state->passwd_tdb );
+ state->passwd_tdb = NULL;
+ }
+
+ return;
+
+}
+
+/****************************************************************************
+ creates a list of user keys
+****************************************************************************/
+
+static int tdbsam_traverse_setpwent(TDB_CONTEXT *t, TDB_DATA key, TDB_DATA data, void *state)
+{
+ const char *prefix = USERPREFIX;
+ int prefixlen = strlen (prefix);
+ struct pwent_list *ptr;
+
+ if ( strncmp(key.dptr, prefix, prefixlen) == 0 ) {
+ if ( !(ptr=(struct pwent_list*)malloc(sizeof(struct pwent_list))) ) {
+ DEBUG(0,("tdbsam_traverse_setpwent: Failed to malloc new entry for list\n"));
+
+ /* just return 0 and let the traversal continue */
+ return 0;
+ }
+ ZERO_STRUCTP(ptr);
+
+ /* save a copy of the key */
+
+ ptr->key.dptr = memdup( key.dptr, key.dsize );
+ ptr->key.dsize = key.dsize;
+
+ DLIST_ADD( tdbsam_pwent_list, ptr );
+
+ }
+
+
+ return 0;
+}
+
+/***************************************************************
+ Open the TDB passwd database for SAM account enumeration.
+ Save a list of user keys for iteration.
+****************************************************************/
+
+static NTSTATUS tdbsam_setsampwent(struct pdb_methods *my_methods, BOOL update)
+{
+ uint32 flags = update ? (O_RDWR|O_CREAT) : O_RDONLY;
+
+ struct tdbsam_privates *tdb_state = (struct tdbsam_privates *)my_methods->private_data;
+
+ if ( !(tdb_state->passwd_tdb = tdbsam_tdbopen(tdb_state->tdbsam_location, flags )) )
+ return NT_STATUS_UNSUCCESSFUL;
+
+ tdb_traverse( tdb_state->passwd_tdb, tdbsam_traverse_setpwent, NULL );
+
+ return NT_STATUS_OK;
+}
+
+
+/***************************************************************
+ End enumeration of the TDB passwd list.
+****************************************************************/
+
+static void tdbsam_endsampwent(struct pdb_methods *my_methods)
+{
+ struct tdbsam_privates *tdb_state = (struct tdbsam_privates *)my_methods->private_data;
+ struct pwent_list *ptr, *ptr_next;
+
+ tdbsam_tdbclose( tdb_state );
+
+ /* clear out any remaining entries in the list */
+
+ for ( ptr=tdbsam_pwent_list; ptr; ptr = ptr_next ) {
+ ptr_next = ptr->next;
+ DLIST_REMOVE( tdbsam_pwent_list, ptr );
+ SAFE_FREE( ptr->key.dptr);
+ SAFE_FREE( ptr );
+ }
+
+ DEBUG(7, ("endtdbpwent: closed sam database.\n"));
+}
+
+/*****************************************************************
+ Get one SAM_ACCOUNT from the TDB (next in line)
+*****************************************************************/
+
+static NTSTATUS tdbsam_getsampwent(struct pdb_methods *my_methods, SAM_ACCOUNT *user)
+{
+ NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
+ struct tdbsam_privates *tdb_state = (struct tdbsam_privates *)my_methods->private_data;
+ TDB_DATA data;
+ struct pwent_list *pkey;
+
+ if ( !user ) {
+ DEBUG(0,("tdbsam_getsampwent: SAM_ACCOUNT is NULL.\n"));
+ return nt_status;
+ }
+
+ if ( !tdbsam_pwent_list ) {
+ DEBUG(4,("tdbsam_getsampwent: end of list\n"));
+ tdbsam_tdbclose( tdb_state );
+ return nt_status;
+ }
+
+ if ( !tdb_state->passwd_tdb ) {
+ if ( !(tdb_state->passwd_tdb = tdbsam_tdbopen(tdb_state->tdbsam_location, O_RDONLY)) )
+ return nt_status;
+ }
+
+ /* pull the next entry */
+
+ pkey = tdbsam_pwent_list;
+ DLIST_REMOVE( tdbsam_pwent_list, pkey );
+
+ data = tdb_fetch(tdb_state->passwd_tdb, pkey->key);
+
+ SAFE_FREE( pkey->key.dptr);
+ SAFE_FREE( pkey);
+
+ if (!data.dptr) {
+ DEBUG(5,("pdb_getsampwent: database entry not found. Was the user deleted?\n"));
+ return nt_status;
+ }
+
+ if (!init_sam_from_buffer(user, (unsigned char *)data.dptr, data.dsize)) {
+ DEBUG(0,("pdb_getsampwent: Bad SAM_ACCOUNT entry returned from TDB!\n"));
+ }
+
+ SAFE_FREE( data.dptr );
+
+
+ return NT_STATUS_OK;
+}
+
+/******************************************************************
+ Lookup a name in the SAM TDB
+******************************************************************/
+
+static NTSTATUS tdbsam_getsampwnam (struct pdb_methods *my_methods, SAM_ACCOUNT *user, const char *sname)
+{
+ NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
+ struct tdbsam_privates *tdb_state = (struct tdbsam_privates *)my_methods->private_data;
+ TDB_CONTEXT *pwd_tdb;
+ TDB_DATA data, key;
+ fstring keystr;
+ fstring name;
+
+ if ( !user ) {
+ DEBUG(0,("pdb_getsampwnam: SAM_ACCOUNT is NULL.\n"));
+ return nt_status;
+ }
+
+ /* Data is stored in all lower-case */
+ fstrcpy(name, sname);
+ strlower_m(name);
+
+ /* set search key */
+ slprintf(keystr, sizeof(keystr)-1, "%s%s", USERPREFIX, name);
+ key.dptr = keystr;
+ key.dsize = strlen(keystr) + 1;
+
+ /* open the accounts TDB */
+ if (!(pwd_tdb = tdbsam_tdbopen(tdb_state->tdbsam_location, O_RDONLY))) {
+
+ if (errno == ENOENT) {
+ /*
+ * TDB file doesn't exist, so try to create new one. This is useful to avoid
+ * confusing error msg when adding user account first time
+ */
+ if (!(pwd_tdb = tdbsam_tdbopen(tdb_state->tdbsam_location, O_CREAT ))) {
+ DEBUG(0, ("pdb_getsampwnam: TDB passwd (%s) did not exist. File successfully created.\n",
+ tdb_state->tdbsam_location));
+ } else {
+ DEBUG(0, ("pdb_getsampwnam: TDB passwd (%s) does not exist. Couldn't create new one. Error was: %s\n",
+ tdb_state->tdbsam_location, strerror(errno)));
+ }
+
+ /* requested user isn't there anyway */
+ nt_status = NT_STATUS_NO_SUCH_USER;
+ return nt_status;
+ }
+ DEBUG(0, ("pdb_getsampwnam: Unable to open TDB passwd (%s)!\n", tdb_state->tdbsam_location));
+ return nt_status;
+ }
+
+ /* get the record */
+ data = tdb_fetch(pwd_tdb, key);
+ if (!data.dptr) {
+ DEBUG(5,("pdb_getsampwnam (TDB): error fetching database.\n"));
+ DEBUGADD(5, (" Error: %s\n", tdb_errorstr(pwd_tdb)));
+ DEBUGADD(5, (" Key: %s\n", keystr));
+ tdb_close(pwd_tdb);
+ return nt_status;
+ }
+
+ /* unpack the buffer */
+ if (!init_sam_from_buffer(user, (unsigned char *)data.dptr, data.dsize)) {
+ DEBUG(0,("pdb_getsampwent: Bad SAM_ACCOUNT entry returned from TDB!\n"));
+ SAFE_FREE(data.dptr);
+ tdb_close(pwd_tdb);
+ return nt_status;
+ }
+ SAFE_FREE(data.dptr);
+
+ /* no further use for database, close it now */
+ tdb_close(pwd_tdb);
+
+ return NT_STATUS_OK;
+}
+
+/***************************************************************************
+ Search by rid
+ **************************************************************************/
+
+static NTSTATUS tdbsam_getsampwrid (struct pdb_methods *my_methods, SAM_ACCOUNT *user, uint32 rid)
+{
+ NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
+ struct tdbsam_privates *tdb_state = (struct tdbsam_privates *)my_methods->private_data;
+ TDB_CONTEXT *pwd_tdb;
+ TDB_DATA data, key;
+ fstring keystr;
+ fstring name;
+
+ if (user==NULL) {
+ DEBUG(0,("pdb_getsampwrid: SAM_ACCOUNT is NULL.\n"));
+ return nt_status;
+ }
+
+ /* set search key */
+ slprintf(keystr, sizeof(keystr)-1, "%s%.8x", RIDPREFIX, rid);
+ key.dptr = keystr;
+ key.dsize = strlen (keystr) + 1;
+
+ /* open the accounts TDB */
+ if (!(pwd_tdb = tdbsam_tdbopen(tdb_state->tdbsam_location, O_RDONLY))) {
+ DEBUG(0, ("pdb_getsampwrid: Unable to open TDB rid database!\n"));
+ return nt_status;
+ }
+
+ /* get the record */
+ data = tdb_fetch (pwd_tdb, key);
+ if (!data.dptr) {
+ DEBUG(5,("pdb_getsampwrid (TDB): error looking up RID %d by key %s.\n", rid, keystr));
+ DEBUGADD(5, (" Error: %s\n", tdb_errorstr(pwd_tdb)));
+ tdb_close (pwd_tdb);
+ return nt_status;
+ }
+
+
+ fstrcpy(name, data.dptr);
+ SAFE_FREE(data.dptr);
+
+ tdb_close (pwd_tdb);
+
+ return tdbsam_getsampwnam (my_methods, user, name);
+}
+
+static NTSTATUS tdbsam_getsampwsid(struct pdb_methods *my_methods, SAM_ACCOUNT * user, const DOM_SID *sid)
+{
+ uint32 rid;
+ if (!sid_peek_check_rid(get_global_sam_sid(), sid, &rid))
+ return NT_STATUS_UNSUCCESSFUL;
+ return tdbsam_getsampwrid(my_methods, user, rid);
+}
+
+/***************************************************************************
+ Delete a SAM_ACCOUNT
+****************************************************************************/
+
+static NTSTATUS tdbsam_delete_sam_account(struct pdb_methods *my_methods, SAM_ACCOUNT *sam_pass)
+{
+ NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
+ struct tdbsam_privates *tdb_state = (struct tdbsam_privates *)my_methods->private_data;
+ TDB_CONTEXT *pwd_tdb;
+ TDB_DATA key;
+ fstring keystr;
+ uint32 rid;
+ fstring name;
+
+ fstrcpy(name, pdb_get_username(sam_pass));
+ strlower_m(name);
+
+ /* open the TDB */
+ if (!(pwd_tdb = tdbsam_tdbopen(tdb_state->tdbsam_location, O_RDWR))) {
+ DEBUG(0, ("Unable to open TDB passwd!"));
+ return nt_status;
+ }
+
+ /* set the search key */
+ slprintf(keystr, sizeof(keystr)-1, "%s%s", USERPREFIX, name);
+ key.dptr = keystr;
+ key.dsize = strlen (keystr) + 1;
+
+ rid = pdb_get_user_rid(sam_pass);
+
+ /* it's outaa here! 8^) */
+ if (tdb_delete(pwd_tdb, key) != TDB_SUCCESS) {
+ DEBUG(5, ("Error deleting entry from tdb passwd database!\n"));
+ DEBUGADD(5, (" Error: %s\n", tdb_errorstr(pwd_tdb)));
+ tdb_close(pwd_tdb);
+ return nt_status;
+ }
+
+ /* delete also the RID key */
+
+ /* set the search key */
+ slprintf(keystr, sizeof(keystr)-1, "%s%.8x", RIDPREFIX, rid);
+ key.dptr = keystr;
+ key.dsize = strlen (keystr) + 1;
+
+ /* it's outaa here! 8^) */
+ if (tdb_delete(pwd_tdb, key) != TDB_SUCCESS) {
+ DEBUG(5, ("Error deleting entry from tdb rid database!\n"));
+ DEBUGADD(5, (" Error: %s\n", tdb_errorstr(pwd_tdb)));
+ tdb_close(pwd_tdb);
+ return nt_status;
+ }
+
+ tdb_close(pwd_tdb);
+
+ return NT_STATUS_OK;
+}
+
+/***************************************************************************
+ Update the TDB SAM
+****************************************************************************/
+
+static BOOL tdb_update_sam(struct pdb_methods *my_methods, SAM_ACCOUNT* newpwd, int flag)
+{
+ struct tdbsam_privates *tdb_state = (struct tdbsam_privates *)my_methods->private_data;
+ TDB_CONTEXT *pwd_tdb = NULL;
+ TDB_DATA key, data;
+ uint8 *buf = NULL;
+ fstring keystr;
+ fstring name;
+ BOOL ret = True;
+ uint32 user_rid;
+
+ /* invalidate the existing TDB iterator if it is open */
+
+ if (tdb_state->passwd_tdb) {
+ tdb_close(tdb_state->passwd_tdb);
+ tdb_state->passwd_tdb = NULL;
+ }
+
+ /* open the account TDB passwd*/
+
+ pwd_tdb = tdbsam_tdbopen(tdb_state->tdbsam_location, O_RDWR | O_CREAT);
+
+ if (!pwd_tdb) {
+ DEBUG(0, ("tdb_update_sam: Unable to open TDB passwd (%s)!\n",
+ tdb_state->tdbsam_location));
+ return False;
+ }
+
+ if (!pdb_get_group_rid(newpwd)) {
+ DEBUG (0,("tdb_update_sam: Failing to store a SAM_ACCOUNT for [%s] without a primary group RID\n",
+ pdb_get_username(newpwd)));
+ ret = False;
+ goto done;
+ }
+
+ if ( !(user_rid = pdb_get_user_rid(newpwd)) ) {
+ DEBUG(0,("tdb_update_sam: SAM_ACCOUNT (%s) with no RID!\n", pdb_get_username(newpwd)));
+ ret = False;
+ goto done;
+ }
+
+ /* copy the SAM_ACCOUNT struct into a BYTE buffer for storage */
+ if ((data.dsize=init_buffer_from_sam (&buf, newpwd, False)) == -1) {
+ DEBUG(0,("tdb_update_sam: ERROR - Unable to copy SAM_ACCOUNT info BYTE buffer!\n"));
+ ret = False;
+ goto done;
+ }
+ data.dptr = (char *)buf;
+
+ fstrcpy(name, pdb_get_username(newpwd));
+ strlower_m(name);
+
+ DEBUG(5, ("Storing %saccount %s with RID %d\n", flag == TDB_INSERT ? "(new) " : "", name, user_rid));
+
+ /* setup the USER index key */
+ slprintf(keystr, sizeof(keystr)-1, "%s%s", USERPREFIX, name);
+ key.dptr = keystr;
+ key.dsize = strlen(keystr) + 1;
+
+ /* add the account */
+ if (tdb_store(pwd_tdb, key, data, flag) != TDB_SUCCESS) {
+ DEBUG(0, ("Unable to modify passwd TDB!"));
+ DEBUGADD(0, (" Error: %s", tdb_errorstr(pwd_tdb)));
+ DEBUGADD(0, (" occured while storing the main record (%s)\n", keystr));
+ ret = False;
+ goto done;
+ }
+
+ /* setup RID data */
+ data.dsize = strlen(name) + 1;
+ data.dptr = name;
+
+ /* setup the RID index key */
+ slprintf(keystr, sizeof(keystr)-1, "%s%.8x", RIDPREFIX, user_rid);
+ key.dptr = keystr;
+ key.dsize = strlen (keystr) + 1;
+
+ /* add the reference */
+ if (tdb_store(pwd_tdb, key, data, flag) != TDB_SUCCESS) {
+ DEBUG(0, ("Unable to modify TDB passwd !"));
+ DEBUGADD(0, (" Error: %s\n", tdb_errorstr(pwd_tdb)));
+ DEBUGADD(0, (" occured while storing the RID index (%s)\n", keystr));
+ ret = False;
+ goto done;
+ }
+
+done:
+ /* cleanup */
+ tdb_close (pwd_tdb);
+ SAFE_FREE(buf);
+
+ return (ret);
+}
+
+/***************************************************************************
+ Modifies an existing SAM_ACCOUNT
+****************************************************************************/
+
+static NTSTATUS tdbsam_update_sam_account (struct pdb_methods *my_methods, SAM_ACCOUNT *newpwd)
+{
+ if (tdb_update_sam(my_methods, newpwd, TDB_MODIFY))
+ return NT_STATUS_OK;
+ else
+ return NT_STATUS_UNSUCCESSFUL;
+}
+
+/***************************************************************************
+ Adds an existing SAM_ACCOUNT
+****************************************************************************/
+
+static NTSTATUS tdbsam_add_sam_account (struct pdb_methods *my_methods, SAM_ACCOUNT *newpwd)
+{
+ if (tdb_update_sam(my_methods, newpwd, TDB_INSERT))
+ return NT_STATUS_OK;
+ else
+ return NT_STATUS_UNSUCCESSFUL;
+}
+
+static void free_private_data(void **vp)
+{
+ struct tdbsam_privates **tdb_state = (struct tdbsam_privates **)vp;
+ tdbsam_tdbclose(*tdb_state);
+ *tdb_state = NULL;
+
+ /* No need to free any further, as it is talloc()ed */
+}
+
+/**
+ * Start trust passwords enumeration. This function is a simple
+ * wrapper for calling gettrustpwent with null pointer passed.
+ *
+ * @param methods methods belonging in pdb context (module)
+ * @return nt status of performed operation
+ **/
+
+static NTSTATUS tdbsam_settrustpwent(struct pdb_methods *methods)
+{
+ /* rewind enumeration from beginning */
+ return methods->gettrustpwent(methods, NULL);
+}
+
+
+/**
+ * Enumerate across trust passwords (machine and interdomain nt/ads)
+ *
+ * @param methods methods belonging in pdb context (module)
+ * @param trust trust password structure
+ *
+ * @return nt status of performed operation
+ **/
+
+static NTSTATUS tdbsam_gettrustpwent(struct pdb_methods *methods, SAM_TRUST_PASSWD *trust)
+{
+ NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
+ struct trust_passwd_data t;
+ TALLOC_CTX *mem_ctx;
+
+ TRUSTDOM **trustdom;
+ static int enum_ctx;
+ int num_domains = 0;
+ unsigned int max_domains = 1;
+ char *dom_name, *dom_pass;
+
+ smb_ucs2_t *uni_dom_name;
+ uint8 mach_pass[16];
+ uint32 sec_chan;
+
+ if (!methods) return NT_STATUS_UNSUCCESSFUL;
+
+ /*
+ * NT domain trust passwords
+ */
+
+ /* rewind enumeration when passed NULL pointer as a trust */
+ if (!trust) {
+ enum_ctx = 0;
+ return NT_STATUS_OK;
+ }
+
+ mem_ctx = talloc_init("tdbsam_gettrustpwent: trust password enumeration");
+
+ /* fetch next trusted domain (one at a time) and its full information */
+ nt_status = secrets_get_trusted_domains(mem_ctx, &enum_ctx, max_domains, &num_domains,
+ &trustdom);
+ if (num_domains) {
+ pull_ucs2_talloc(mem_ctx, &dom_name, trustdom[0]->name);
+ if (secrets_fetch_trusted_domain_password(dom_name, &dom_pass, &t.domain_sid,
+ &t.mod_time)) {
+
+ t.uni_name_len = strnlen_w(trustdom[0]->name, 32);
+ strncpy_w(t.uni_name, trustdom[0]->name, t.uni_name_len);
+ safe_strcpy(t.pass, dom_pass, FSTRING_LEN - 1);
+ t.flags = PASS_DOMAIN_TRUST_NT;
+
+ SAFE_FREE(dom_pass);
+ talloc_destroy(mem_ctx);
+ trust->private = t;
+ return nt_status;
+ } else {
+ talloc_destroy(mem_ctx);
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+ }
+
+ /*
+ * NT machine trust password
+ */
+
+ if (secrets_lock_trust_account_password(lp_workgroup(), True)) {
+ sec_chan = get_default_sec_channel();
+ if (secrets_fetch_trust_account_password(lp_workgroup(), mach_pass, &t.mod_time,
+ &sec_chan)) {
+
+ t.uni_name_len = strlen(lp_workgroup());
+ push_ucs2_talloc(mem_ctx, &uni_dom_name, lp_workgroup());
+ strncpy_w(t.uni_name, uni_dom_name, t.uni_name_len);
+ safe_strcpy(t.pass, mach_pass, FSTRING_LEN - 1);
+ t.flags = PASS_MACHINE_TRUST_NT;
+ if (!secrets_fetch_domain_sid(lp_workgroup(), &t.domain_sid)) {
+ talloc_destroy(mem_ctx);
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ talloc_destroy(mem_ctx);
+ trust->private = t;
+ return NT_STATUS_NO_MORE_ENTRIES;
+ }
+ secrets_lock_trust_account_password(lp_workgroup(), False);
+ } else {
+ talloc_destroy(mem_ctx);
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ /*
+ * ADS machine trust password (TODO)
+ */
+
+
+ /*
+ * if nothing is to be returned then reset domain name
+ * and return "no more entries"
+ */
+ nt_status = NT_STATUS_NO_MORE_ENTRIES;
+ trust->private.uni_name_len = 0;
+ trust->private.uni_name[t.uni_name_len] = 0;
+
+ talloc_destroy(mem_ctx);
+ return nt_status;
+}
+
+
+/**
+ * Get trust password by trusted party name
+ *
+ * @param methods methods belonging to pdb context (module)
+ * @param trust trust password structure
+ * @param sid trusted party name
+ *
+ * @return nt status of performed operation
+ **/
+
+static NTSTATUS tdbsam_gettrustpwnam(struct pdb_methods *methods, SAM_TRUST_PASSWD *trust,
+ const char *name)
+{
+ NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
+ char domain_name[32];
+
+ if (!methods || !trust || !name) return nt_status;
+
+ do {
+ /* get trust password (next in turn) */
+ nt_status = tdbsam_gettrustpwent(methods, trust);
+
+ /* convert unicode name and do case insensitive compare */
+ pull_ucs2(NULL, domain_name, trust->private.uni_name, sizeof(domain_name),
+ trust->private.uni_name_len, STR_TERMINATE);
+ if (!StrnCaseCmp(domain_name, name, sizeof(domain_name)))
+ return NT_STATUS_OK;
+
+ } while (NT_STATUS_EQUAL(nt_status, STATUS_MORE_ENTRIES));
+
+ return nt_status;
+}
+
+
+/**
+ * Get trust password by trusted party sid
+ *
+ * @param methods methods belonging to pdb context (module)
+ * @param trust trust password structure
+ * @param sid trusted party sid
+ *
+ * @return nt status of performed operation
+ **/
+
+static NTSTATUS tdbsam_gettrustpwsid(struct pdb_methods *methods, SAM_TRUST_PASSWD *trust,
+ const DOM_SID *sid)
+{
+ NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
+
+ if (!methods || !trust || !sid) return nt_status;
+
+ do {
+ nt_status = tdbsam_gettrustpwent(methods, trust);
+
+ if (sid_equal(&trust->private.domain_sid, sid))
+ return NT_STATUS_OK;
+
+ } while (NT_STATUS_EQUAL(nt_status, STATUS_MORE_ENTRIES));
+
+ return nt_status;
+}
+
+
+/**
+ * Add new trust password.
+ *
+ * @param methods methods belonging in pdb context (module)
+ * @param trust trust password structure
+ *
+ * @return nt status of performed operation
+ **/
+
+static NTSTATUS tdbsam_add_trust_passwd(struct pdb_methods *methods, const SAM_TRUST_PASSWD *trust)
+{
+ NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
+ BOOL status = False;
+ TALLOC_CTX *mem_ctx;
+
+ char* domain = NULL;
+ struct trust_passwd_data t = trust->private;
+ uint32 sec_chan;
+
+ mem_ctx = talloc_init("tdbsam_add_trust_passwd: storing new trust password");
+
+ /* convert unicode name to char* (used to form the key) */
+ pull_ucs2_talloc(mem_ctx, &domain, t.uni_name);
+
+ /* add nt machine trust password */
+ if (t.flags & (PASS_MACHINE_TRUST_NT | PASS_SERVER_TRUST_NT)) {
+ sec_chan = (t.flags & PASS_MACHINE_TRUST_NT) ? SEC_CHAN_WKSTA : SEC_CHAN_BDC;
+ status = secrets_store_machine_password(t.pass, domain, sec_chan);
+ if (status)
+ status = secrets_store_domain_sid(domain, &t.domain_sid);
+
+ nt_status = status ? NT_STATUS_OK : NT_STATUS_UNSUCCESSFUL;
+
+ /* add nt domain trust password */
+ } else if (t.flags & PASS_DOMAIN_TRUST_NT) {
+ status = secrets_store_trusted_domain_password(domain, t.uni_name, t.uni_name_len,
+ t.pass, t.domain_sid);
+ nt_status = status ? NT_STATUS_OK : NT_STATUS_UNSUCCESSFUL;
+
+ /* add ads machine trust password (TODO) */
+ } else if (t.flags & PASS_MACHINE_TRUST_ADS) {
+ }
+
+ talloc_destroy(mem_ctx);
+ return nt_status;
+}
+
+
+/**
+ * Update trust password.
+ *
+ * @param methods methods belonging in pdb context (module)
+ * @param trust trust password structure
+ *
+ * @return nt status of performed operation
+ **/
+
+static NTSTATUS tdbsam_update_trust_passwd(struct pdb_methods *methods, const SAM_TRUST_PASSWD* trust)
+{
+ NTSTATUS nt_status = NT_STATUS_NOT_IMPLEMENTED;
+ return nt_status;
+}
+
+
+/**
+ * Delete trust password.
+ *
+ * @param methods methods belonging in pdb context (module)
+ * @param trust trust password structure
+ *
+ * @return nt status of performed operation
+ **/
+
+static NTSTATUS tdbsam_delete_trust_passwd(struct pdb_methods *methods, const SAM_TRUST_PASSWD* trust)
+{
+ NTSTATUS nt_status = NT_STATUS_NOT_IMPLEMENTED;
+ return nt_status;
+}
+
+
+/***************************************************************************
+ Add sid to privilege
+****************************************************************************/
+
+static NTSTATUS tdbsam_add_sid_to_privilege(struct pdb_methods *my_methods, const char *priv_name, const DOM_SID *sid)
+{
+ struct tdbsam_privates *tdb_state = (struct tdbsam_privates *)my_methods->private_data;
+ TDB_CONTEXT *pwd_tdb = NULL;
+ TDB_DATA key, data;
+ fstring keystr;
+ fstring name;
+ NTSTATUS ret = NT_STATUS_UNSUCCESSFUL;
+ fstring sid_str;
+ char *sid_list = NULL, *s = NULL;
+ size_t str_size;
+ int flag;
+
+ /* invalidate the existing TDB iterator if it is open */
+
+ if (tdb_state->passwd_tdb) {
+ tdb_close(tdb_state->passwd_tdb);
+ tdb_state->passwd_tdb = NULL;
+ }
+
+ /* open the account TDB passwd*/
+
+ pwd_tdb = tdbsam_tdbopen(tdb_state->tdbsam_location, O_RDWR | O_CREAT);
+
+ if (!pwd_tdb) {
+ DEBUG(0, ("tdb_add_sid_to_privilege: Unable to open TDB passwd (%s)!\n",
+ tdb_state->tdbsam_location));
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ /* setup the PRIV index key */
+ fstrcpy(name, priv_name);
+ strlower_m(name);
+
+ slprintf(keystr, sizeof(keystr)-1, "%s%s", PRIVPREFIX, name);
+ key.dptr = keystr;
+ key.dsize = strlen(keystr) + 1;
+
+ /* check if the privilege already exist in the database */
+
+ /* get the record */
+ data = tdb_fetch (pwd_tdb, key);
+
+ if (data.dptr) {
+ /* check the list is not empty */
+ if (*(data.dptr)) {
+ sid_list = strdup(data.dptr);
+ if (!sid_list) {
+ DEBUG(0, ("tdbsam_add_sid_to_privilege: Out of Memory!\n"));
+ goto done;
+ }
+ }
+ SAFE_FREE(data.dptr);
+
+ flag = TDB_MODIFY;
+ } else {
+ /* if privilege does not exist create one */
+ flag = TDB_INSERT;
+ }
+
+ /* add the given sid */
+ sid_to_string(sid_str, sid);
+
+ if (sid_list) {
+ str_size = strlen(sid_list) + strlen(sid_str) + 2;
+ s = realloc(sid_list, str_size);
+ if (!s) {
+ DEBUG(0, ("tdbsam_add_sid_to_privilege: Out of Memory!\n"));
+ ret = NT_STATUS_NO_MEMORY;
+ goto done;
+ }
+ sid_list = s;
+ s = &sid_list[strlen(sid_list)];
+ snprintf(s, strlen(sid_str) + 2, ",%s", sid_str);
+
+ } else {
+ sid_list = strdup(sid_str);
+ if (!sid_list) {
+ DEBUG(0, ("tdbsam_add_sid_to_privilege: Out of Memory!\n"));
+ ret = NT_STATUS_NO_MEMORY;
+ goto done;
+ }
+
+ }
+
+ /* copy the PRIVILEGE struct into a BYTE buffer for storage */
+ data.dsize = strlen(sid_list) + 1;
+ data.dptr = sid_list;
+
+ /* add the account */
+ if (tdb_store(pwd_tdb, key, data, flag) != TDB_SUCCESS) {
+ DEBUG(0, ("Unable to modify passwd TDB!"));
+ DEBUGADD(0, (" Error: %s", tdb_errorstr(pwd_tdb)));
+ DEBUGADD(0, (" occured while storing the main record (%s)\n", keystr));
+ goto done;
+ }
+
+ ret = NT_STATUS_OK;
+
+done:
+ /* cleanup */
+ tdb_close (pwd_tdb);
+ SAFE_FREE(sid_list);
+
+ return (ret);
+}
+
+/***************************************************************************
+ Reomve sid to privilege
+****************************************************************************/
+
+static NTSTATUS tdbsam_remove_sid_from_privilege(struct pdb_methods *my_methods, const char *priv_name, const DOM_SID *sid)
+{
+ struct tdbsam_privates *tdb_state = (struct tdbsam_privates *)my_methods->private_data;
+ TDB_CONTEXT *pwd_tdb = NULL;
+ TDB_DATA key, data;
+ fstring keystr;
+ fstring name;
+ NTSTATUS ret = NT_STATUS_UNSUCCESSFUL;
+ fstring sid_str;
+ char *sid_list = NULL, *s = NULL;
+
+ /* invalidate the existing TDB iterator if it is open */
+
+ if (tdb_state->passwd_tdb) {
+ tdb_close(tdb_state->passwd_tdb);
+ tdb_state->passwd_tdb = NULL;
+ }
+
+ /* open the account TDB passwd*/
+
+ pwd_tdb = tdbsam_tdbopen(tdb_state->tdbsam_location, O_RDWR | O_CREAT);
+
+ if (!pwd_tdb) {
+ DEBUG(0, ("tdbsam_remove_sid_from_privilege: Unable to open TDB passwd (%s)!\n",
+ tdb_state->tdbsam_location));
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ /* setup the PRIV index key */
+ fstrcpy(name, priv_name);
+ strlower_m(name);
+
+ slprintf(keystr, sizeof(keystr)-1, "%s%s", PRIVPREFIX, name);
+ key.dptr = keystr;
+ key.dsize = strlen(keystr) + 1;
+
+ /* check if the privilege already exist in the database */
+
+ /* get the record */
+ data = tdb_fetch (pwd_tdb, key);
+
+ /* if privilege does not exist, just leave */
+ if (!data.dptr) {
+ ret = NT_STATUS_OK;
+ goto done;
+ }
+
+ if (data.dptr) {
+ sid_list = strdup(data.dptr);
+ if (!sid_list) {
+ DEBUG(0, ("tdbsam_remove_sid_from_privilege: Out of Memory!\n"));
+ goto done;
+ }
+ SAFE_FREE(data.dptr);
+ }
+
+ /* remove the given sid */
+ sid_to_string(sid_str, sid);
+
+ s = strstr(sid_list, sid_str);
+ if (s) {
+ char *p;
+ p = strstr(s, ",");
+ if (p) {
+ size_t l = strlen(sid_list) + 1 - (s - sid_list);
+ memmove(s, ++p, l);
+ } else {
+ if (s != sid_list)
+ s--;
+ *s = '\0';
+ }
+ } else {
+ /* sid not found */
+ ret = NT_STATUS_UNSUCCESSFUL;
+ goto done;
+ }
+
+ /* copy the PRIVILEGE struct into a BYTE buffer for storage */
+ data.dsize = strlen(sid_list) + 1;
+ data.dptr = sid_list;
+
+ /* add the account */
+ if (tdb_store(pwd_tdb, key, data, TDB_MODIFY) != TDB_SUCCESS) {
+ DEBUG(0, ("Unable to modify passwd TDB!"));
+ DEBUGADD(0, (" Error: %s", tdb_errorstr(pwd_tdb)));
+ DEBUGADD(0, (" occured while storing the main record (%s)\n", keystr));
+ goto done;
+ }
+
+ ret = NT_STATUS_OK;
+
+done:
+ /* cleanup */
+ tdb_close (pwd_tdb);
+ SAFE_FREE(sid_list);
+
+ return (ret);
+}
+
+/***************************************************************************
+ get the privilege list for the given token
+****************************************************************************/
+
+struct priv_traverse {
+ char **sid_list;
+ PRIVILEGE_SET *privset;
+};
+
+static int tdbsam_traverse_privilege(TDB_CONTEXT *t, TDB_DATA key, TDB_DATA data, void *state)
+{
+ struct priv_traverse *pt = (struct priv_traverse *)state;
+ int prefixlen = strlen(PRIVPREFIX);
+
+ if (strncmp(key.dptr, PRIVPREFIX, prefixlen) == 0) {
+
+ /* add to privilege_set if any of the sid in the token
+ * is contained in the privilege */
+ int i;
+
+ for(i=0; pt->sid_list[i] != NULL; i++) {
+ char *c, *s;
+ int len;
+
+ s = data.dptr;
+ while ((c=strchr(s, ',')) !=NULL) {
+ len = MAX((c - s), strlen(pt->sid_list[i]));
+ if (strncmp(s, pt->sid_list[i], len) == 0) {
+ DEBUG(10, ("sid [%s] found in users sid list\n", pt->sid_list[i]));
+ DEBUG(10, ("adding privilege [%s] to the users privilege list\n", &(key.dptr[prefixlen])));
+ add_privilege_by_name(pt->privset, &(key.dptr[prefixlen]));
+ return 0;
+ }
+ s = c + 1;
+ }
+ len = MAX(strlen(s), strlen(pt->sid_list[i]));
+ if (strncmp(s, pt->sid_list[i], len) == 0) {
+ DEBUG(10, ("sid [%s] found in users sid list\n", pt->sid_list[i]));
+ DEBUG(10, ("adding privilege [%s] to the users privilege list\n", &(key.dptr[prefixlen])));
+ add_privilege_by_name(pt->privset, &(key.dptr[prefixlen]));
+ return 0;
+ }
+ }
+ }
+
+ return 0;
+}
+
+static NTSTATUS tdbsam_get_privilege_set(struct pdb_methods *my_methods, DOM_SID *user_sids, int num_sids, PRIVILEGE_SET *privset)
+{
+ struct tdbsam_privates *tdb_state = (struct tdbsam_privates *)my_methods->private_data;
+ NTSTATUS ret = NT_STATUS_UNSUCCESSFUL;
+ TDB_CONTEXT *pwd_tdb = NULL;
+ struct priv_traverse pt;
+ fstring sid_str;
+ char **sid_list;
+ int i;
+
+ if (!(pwd_tdb = tdbsam_tdbopen(tdb_state->tdbsam_location, O_RDONLY )))
+ return NT_STATUS_UNSUCCESSFUL;
+
+ sid_list = (char **)malloc(sizeof(char *) * (num_sids + 1));
+ for (i = 0; i < num_sids; i++) {
+ sid_to_string(sid_str, &user_sids[i]);
+ sid_list[i] = strdup(sid_str);
+ if ( ! sid_list[i]) {
+ ret = NT_STATUS_NO_MEMORY;
+ goto done;
+ }
+ }
+ sid_list[i] = NULL;
+
+ pt.sid_list = sid_list;
+ pt.privset = privset;
+ tdb_traverse(pwd_tdb, tdbsam_traverse_privilege, &pt);
+
+ ret = NT_STATUS_OK;
+
+done:
+ i = 0;
+ while (sid_list[i]) {
+ free(sid_list[i]);
+ i++;
+ }
+ free(sid_list);
+
+ tdb_close(pwd_tdb);
+
+ return ret;
+}
+
+static NTSTATUS tdbsam_get_privilege_entry(struct pdb_methods *my_methods, const char *privname, char **sid_list)
+{
+ NTSTATUS ret = NT_STATUS_UNSUCCESSFUL;
+ TDB_CONTEXT *pwd_tdb = NULL;
+ TDB_DATA key, data;
+ fstring name;
+ fstring keystr;
+
+ struct tdbsam_privates *tdb_state = (struct tdbsam_privates *)my_methods->private_data;
+
+ if (!(pwd_tdb = tdbsam_tdbopen(tdb_state->tdbsam_location, O_RDONLY)))
+ return ret;
+
+ /* setup the PRIV index key */
+ fstrcpy(name, privname);
+ strlower_m(name);
+
+ slprintf(keystr, sizeof(keystr)-1, "%s%s", PRIVPREFIX, name);
+ key.dptr = keystr;
+ key.dsize = strlen(keystr) + 1;
+
+ data = tdb_fetch(pwd_tdb, key);
+ if (!data.dptr)
+ goto done;
+
+ *sid_list = strdup(data.dptr);
+ SAFE_FREE(data.dptr);
+
+ if (!*sid_list)
+ goto done;
+
+ ret = NT_STATUS_OK;
+done:
+ tdb_close(pwd_tdb);
+ return ret;
+}
+
+
+
+
+
+
+
+static NTSTATUS pdb_init_tdbsam(PDB_CONTEXT *pdb_context, PDB_METHODS **pdb_method, const char *location)
+{
+ NTSTATUS nt_status;
+ struct tdbsam_privates *tdb_state;
+
+ if (!NT_STATUS_IS_OK(nt_status = make_pdb_methods(pdb_context->mem_ctx, pdb_method))) {
+ return nt_status;
+ }
+
+ (*pdb_method)->name = "tdbsam";
+
+ (*pdb_method)->setsampwent = tdbsam_setsampwent;
+ (*pdb_method)->endsampwent = tdbsam_endsampwent;
+ (*pdb_method)->getsampwent = tdbsam_getsampwent;
+ (*pdb_method)->getsampwnam = tdbsam_getsampwnam;
+ (*pdb_method)->getsampwsid = tdbsam_getsampwsid;
+ (*pdb_method)->add_sam_account = tdbsam_add_sam_account;
+ (*pdb_method)->update_sam_account = tdbsam_update_sam_account;
+ (*pdb_method)->delete_sam_account = tdbsam_delete_sam_account;
+ (*pdb_method)->settrustpwent = tdbsam_settrustpwent;
+ (*pdb_method)->gettrustpwent = tdbsam_gettrustpwent;
+ (*pdb_method)->gettrustpwnam = tdbsam_gettrustpwnam;
+ (*pdb_method)->gettrustpwsid = tdbsam_gettrustpwsid;
+ (*pdb_method)->add_trust_passwd = tdbsam_add_trust_passwd;
+ (*pdb_method)->update_trust_passwd = tdbsam_update_trust_passwd;
+ (*pdb_method)->delete_trust_passwd = tdbsam_delete_trust_passwd;
+ (*pdb_method)->add_sid_to_privilege = tdbsam_add_sid_to_privilege;
+ (*pdb_method)->remove_sid_from_privilege = tdbsam_remove_sid_from_privilege;
+ (*pdb_method)->get_privilege_set = tdbsam_get_privilege_set;
+ (*pdb_method)->get_privilege_entry = tdbsam_get_privilege_entry;
+
+ tdb_state = talloc_zero(pdb_context->mem_ctx, sizeof(struct tdbsam_privates));
+
+ if (!tdb_state) {
+ DEBUG(0, ("talloc() failed for tdbsam private_data!\n"));
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ if (location) {
+ tdb_state->tdbsam_location = talloc_strdup(pdb_context->mem_ctx, location);
+ } else {
+ pstring tdbfile;
+ get_private_directory(tdbfile);
+ pstrcat(tdbfile, "/");
+ pstrcat(tdbfile, PASSDB_FILE_NAME);
+ tdb_state->tdbsam_location = talloc_strdup(pdb_context->mem_ctx, tdbfile);
+ }
+
+ (*pdb_method)->private_data = tdb_state;
+
+ (*pdb_method)->free_private_data = free_private_data;
+
+ return NT_STATUS_OK;
+}
+
+NTSTATUS pdb_tdbsam_init(void)
+{
+ return smb_register_passdb(PASSDB_INTERFACE_VERSION, "tdbsam", pdb_init_tdbsam);
+}
diff --git a/source/passdb/pdb_xml.c b/source/passdb/pdb_xml.c
new file mode 100644
index 00000000000..2738ad40e2a
--- /dev/null
+++ b/source/passdb/pdb_xml.c
@@ -0,0 +1,563 @@
+
+/*
+ * XML password backend for samba
+ * Copyright (C) Jelmer Vernooij 2002
+ * Some parts based on the libxml gjobread example by Daniel Veillard
+ *
+ * This program is free software; you can 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.
+ */
+
+/* FIXME:
+ * - Support stdin input by using '-'
+ * - Be faster. Don't rewrite the whole file when adding a user, but store it in the memory and save it when exiting. Requires changes to samba source.
+ * - Gives the ability to read/write to standard input/output
+ * - Do locking!
+ * - Better names!
+ */
+
+
+#define XML_URL "http://samba.org/~jelmer/sambapdb.dtd"
+
+#include "includes.h"
+
+#include <libxml/xmlmemory.h>
+#include <libxml/parser.h>
+
+static int xmlsam_debug_level = DBGC_ALL;
+
+#undef DBGC_CLASS
+#define DBGC_CLASS xmlsam_debug_level
+
+static char * iota(int a) {
+ static char tmp[10];
+
+ snprintf(tmp, 9, "%d", a);
+ return tmp;
+}
+
+static BOOL parsePass(xmlDocPtr doc, xmlNsPtr ns, xmlNodePtr cur, SAM_ACCOUNT * u)
+{
+ pstring temp;
+
+ cur = cur->xmlChildrenNode;
+ while (cur != NULL) {
+ if (strcmp(cur->name, "crypt"))
+ DEBUG(0, ("Unknown element %s\n", cur->name));
+ else {
+ if (!strcmp(xmlGetProp(cur, "type"), "nt")
+ &&
+ pdb_gethexpwd(xmlNodeListGetString
+ (doc, cur->xmlChildrenNode, 1), temp))
+ pdb_set_nt_passwd(u, temp, PDB_SET);
+ else if (!strcmp(xmlGetProp(cur, "type"), "lanman")
+ &&
+ pdb_gethexpwd(xmlNodeListGetString
+ (doc, cur->xmlChildrenNode, 1), temp))
+ pdb_set_lanman_passwd(u, temp, PDB_SET);
+ else
+ DEBUG(0,
+ ("Unknown crypt type: %s\n",
+ xmlGetProp(cur, "type")));
+ }
+ cur = cur->next;
+ }
+ return True;
+}
+
+static BOOL parseUser(xmlDocPtr doc, xmlNsPtr ns, xmlNodePtr cur, SAM_ACCOUNT * u)
+{
+ char *tmp;
+ DOM_SID sid;
+
+ tmp = xmlGetProp(cur, "sid");
+ if (tmp){
+ string_to_sid(&sid, tmp);
+ pdb_set_user_sid(u, &sid, PDB_SET);
+ }
+ pdb_set_username(u, xmlGetProp(cur, "name"), PDB_SET);
+ /* We don't care what the top level element name is */
+ cur = cur->xmlChildrenNode;
+ while (cur != NULL) {
+ if ((!strcmp(cur->name, "group")) && (cur->ns == ns)) {
+ tmp = xmlGetProp(cur, "sid");
+ if (tmp){
+ string_to_sid(&sid, tmp);
+ pdb_set_group_sid(u, &sid, PDB_SET);
+ }
+ }
+
+ else if ((!strcmp(cur->name, "domain")) && (cur->ns == ns))
+ pdb_set_domain(u,
+ xmlNodeListGetString(doc, cur->xmlChildrenNode,
+ 1), PDB_SET);
+
+ else if (!strcmp(cur->name, "fullname") && cur->ns == ns)
+ pdb_set_fullname(u,
+ xmlNodeListGetString(doc,
+ cur->xmlChildrenNode,
+ 1), PDB_SET);
+
+ else if (!strcmp(cur->name, "nt_username") && cur->ns == ns)
+ pdb_set_nt_username(u,
+ xmlNodeListGetString(doc,
+ cur->xmlChildrenNode,
+ 1), PDB_SET);
+
+ else if (!strcmp(cur->name, "logon_script") && cur->ns == ns)
+ pdb_set_logon_script(u,
+ xmlNodeListGetString(doc,
+ cur->xmlChildrenNode,
+ 1), PDB_SET);
+
+ else if (!strcmp(cur->name, "profile_path") && cur->ns == ns)
+ pdb_set_profile_path(u,
+ xmlNodeListGetString(doc,
+ cur->xmlChildrenNode,
+ 1), PDB_SET);
+
+ else if (!strcmp(cur->name, "logon_time") && cur->ns == ns)
+ pdb_set_logon_time(u,
+ atol(xmlNodeListGetString
+ (doc, cur->xmlChildrenNode, 1)), PDB_SET);
+
+ else if (!strcmp(cur->name, "logoff_time") && cur->ns == ns)
+ pdb_set_logoff_time(u,
+ atol(xmlNodeListGetString
+ (doc, cur->xmlChildrenNode, 1)),
+ PDB_SET);
+
+ else if (!strcmp(cur->name, "kickoff_time") && cur->ns == ns)
+ pdb_set_kickoff_time(u,
+ atol(xmlNodeListGetString
+ (doc, cur->xmlChildrenNode, 1)),
+ PDB_SET);
+
+ else if (!strcmp(cur->name, "logon_divs") && cur->ns == ns)
+ pdb_set_logon_divs(u,
+ atol(xmlNodeListGetString
+ (doc, cur->xmlChildrenNode, 1)), PDB_SET);
+
+ else if (!strcmp(cur->name, "hours_len") && cur->ns == ns)
+ pdb_set_hours_len(u,
+ atol(xmlNodeListGetString
+ (doc, cur->xmlChildrenNode, 1)), PDB_SET);
+
+ else if (!strcmp(cur->name, "bad_password_count") && cur->ns == ns)
+ pdb_set_bad_password_count(u,
+ atol(xmlNodeListGetString
+ (doc, cur->xmlChildrenNode, 1)), PDB_SET);
+
+ else if (!strcmp(cur->name, "logon_count") && cur->ns == ns)
+ pdb_set_logon_count(u,
+ atol(xmlNodeListGetString
+ (doc, cur->xmlChildrenNode, 1)), PDB_SET);
+
+ else if (!strcmp(cur->name, "unknown_6") && cur->ns == ns)
+ pdb_set_unknown_6(u,
+ atol(xmlNodeListGetString
+ (doc, cur->xmlChildrenNode, 1)), PDB_SET);
+
+ else if (!strcmp(cur->name, "homedir") && cur->ns == ns)
+ pdb_set_homedir(u,
+ xmlNodeListGetString(doc, cur->xmlChildrenNode,
+ 1), PDB_SET);
+
+ else if (!strcmp(cur->name, "unknown_str") && cur->ns == ns)
+ pdb_set_unknown_str(u,
+ xmlNodeListGetString(doc,
+ cur->xmlChildrenNode,
+ 1), PDB_SET);
+
+ else if (!strcmp(cur->name, "dir_drive") && cur->ns == ns)
+ pdb_set_dir_drive(u,
+ xmlNodeListGetString(doc,
+ cur->xmlChildrenNode,
+ 1), PDB_SET);
+
+ else if (!strcmp(cur->name, "munged_dial") && cur->ns == ns)
+ pdb_set_munged_dial(u,
+ xmlNodeListGetString(doc,
+ cur->xmlChildrenNode,
+ 1), PDB_SET);
+
+ else if (!strcmp(cur->name, "acct_desc") && cur->ns == ns)
+ pdb_set_acct_desc(u,
+ xmlNodeListGetString(doc,
+ cur->xmlChildrenNode,
+ 1), PDB_SET);
+
+ else if (!strcmp(cur->name, "acct_ctrl") && cur->ns == ns)
+ pdb_set_acct_ctrl(u,
+ atol(xmlNodeListGetString
+ (doc, cur->xmlChildrenNode, 1)), PDB_SET);
+
+ else if (!strcmp(cur->name, "workstations") && cur->ns == ns)
+ pdb_set_workstations(u,
+ xmlNodeListGetString(doc,
+ cur->xmlChildrenNode,
+ 1), PDB_SET);
+
+ else if ((!strcmp(cur->name, "password")) && (cur->ns == ns)) {
+ tmp = xmlGetProp(cur, "last_set");
+ if (tmp)
+ pdb_set_pass_last_set_time(u, atol(tmp), PDB_SET);
+ tmp = xmlGetProp(cur, "must_change");
+ if (tmp)
+ pdb_set_pass_must_change_time(u, atol(tmp), PDB_SET);
+ tmp = xmlGetProp(cur, "can_change");
+ if (tmp)
+ pdb_set_pass_can_change_time(u, atol(tmp), PDB_SET);
+ parsePass(doc, ns, cur, u);
+ }
+
+ else
+ DEBUG(0, ("Unknown element %s\n", cur->name));
+ cur = cur->next;
+ }
+
+ return True;
+}
+
+typedef struct pdb_xml {
+ char *location;
+ char written;
+ xmlDocPtr doc;
+ xmlNodePtr users;
+ xmlNodePtr pwent;
+ xmlNsPtr ns;
+} pdb_xml;
+
+static xmlNodePtr parseSambaXMLFile(struct pdb_xml *data)
+{
+ xmlNodePtr cur;
+
+ data->doc = xmlParseFile(data->location);
+ if (data->doc == NULL)
+ return NULL;
+
+ cur = xmlDocGetRootElement(data->doc);
+ if (!cur) {
+ DEBUG(0, ("empty document\n"));
+ xmlFreeDoc(data->doc);
+ return NULL;
+ }
+ data->ns = xmlSearchNsByHref(data->doc, cur, XML_URL);
+ if (!data->ns) {
+ DEBUG(0,
+ ("document of the wrong type, samba user namespace not found\n"));
+ xmlFreeDoc(data->doc);
+ return NULL;
+ }
+ if (strcmp(cur->name, "samba")) {
+ DEBUG(0, ("document of the wrong type, root node != samba"));
+ xmlFreeDoc(data->doc);
+ return NULL;
+ }
+
+ cur = cur->xmlChildrenNode;
+ while (cur && xmlIsBlankNode(cur)) {
+ cur = cur->next;
+ }
+ if (!cur)
+ return NULL;
+ if ((strcmp(cur->name, "users")) || (cur->ns != data->ns)) {
+ DEBUG(0, ("document of the wrong type, was '%s', users expected",
+ cur->name));
+ DEBUG(0, ("xmlDocDump follows\n"));
+ xmlDocDump(stderr, data->doc);
+ DEBUG(0, ("xmlDocDump finished\n"));
+ xmlFreeDoc(data->doc);
+ return NULL;
+ }
+ data->users = cur;
+ cur = cur->xmlChildrenNode;
+ return cur;
+}
+
+static NTSTATUS xmlsam_setsampwent(struct pdb_methods *methods, BOOL update)
+{
+ pdb_xml *data;
+
+ if (!methods) {
+ DEBUG(0, ("Invalid methods\n"));
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+ data = (pdb_xml *) methods->private_data;
+ if (!data) {
+ DEBUG(0, ("Invalid pdb_xml_data\n"));
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+ data->pwent = parseSambaXMLFile(data);
+ if (!data->pwent)
+ return NT_STATUS_UNSUCCESSFUL;
+
+ return NT_STATUS_OK;
+}
+
+/***************************************************************
+ End enumeration of the passwd list.
+ ****************************************************************/
+
+static void xmlsam_endsampwent(struct pdb_methods *methods)
+{
+ pdb_xml *data;
+
+ if (!methods) {
+ DEBUG(0, ("Invalid methods\n"));
+ return;
+ }
+
+ data = (pdb_xml *) methods->private_data;
+
+ if (!data) {
+ DEBUG(0, ("Invalid pdb_xml_data\n"));
+ return;
+ }
+
+ xmlFreeDoc(data->doc);
+ data->doc = NULL;
+ data->pwent = NULL;
+}
+
+/*****************************************************************
+ Get one SAM_ACCOUNT from the list (next in line)
+ *****************************************************************/
+
+static NTSTATUS xmlsam_getsampwent(struct pdb_methods *methods, SAM_ACCOUNT * user)
+{
+ pdb_xml *data;
+
+ if (!methods) {
+ DEBUG(0, ("Invalid methods\n"));
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+ data = (pdb_xml *) methods->private_data;
+
+ if (!data) {
+ DEBUG(0, ("Invalid pdb_xml_data\n"));
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ while (data->pwent) {
+ if ((!strcmp(data->pwent->name, "user")) &&
+ (data->pwent->ns == data->ns)) {
+
+ parseUser(data->doc, data->ns, data->pwent, user);
+ data->pwent = data->pwent->next;
+ return NT_STATUS_OK;
+ }
+ data->pwent = data->pwent->next;
+ }
+ return NT_STATUS_UNSUCCESSFUL;
+}
+
+/***************************************************************************
+ Adds an existing SAM_ACCOUNT
+ ****************************************************************************/
+
+static NTSTATUS xmlsam_add_sam_account(struct pdb_methods *methods, SAM_ACCOUNT * u)
+{
+ pstring temp;
+ fstring sid_str;
+ xmlNodePtr cur, user, pass, root;
+ pdb_xml *data;
+
+ DEBUG(10, ("xmlsam_add_sam_account called!\n"));
+
+ if (!methods) {
+ DEBUG(0, ("Invalid methods\n"));
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ data = (pdb_xml *) methods->private_data;
+ if (!data) {
+ DEBUG(0, ("Invalid pdb_xml_data\n"));
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ /* Create a new document if we can't open the current one */
+ if (!parseSambaXMLFile(data)) {
+ DEBUG(0, ("Can't load current XML file, creating a new one\n"));
+ data->doc = xmlNewDoc(XML_DEFAULT_VERSION);
+ root = xmlNewDocNode(data->doc, NULL, "samba", NULL);
+ cur = xmlDocSetRootElement(data->doc, root);
+ data->ns = xmlNewNs(root, XML_URL, "samba");
+ data->users = xmlNewChild(root, data->ns, "users", NULL);
+ }
+
+ user = xmlNewChild(data->users, data->ns, "user", NULL);
+ xmlNewProp(user, "sid",
+ sid_to_string(sid_str, pdb_get_user_sid(u)));
+
+ if (pdb_get_username(u) && strcmp(pdb_get_username(u), ""))
+ xmlNewProp(user, "name", pdb_get_username(u));
+
+ cur = xmlNewChild(user, data->ns, "group", NULL);
+
+ xmlNewProp(cur, "sid",
+ sid_to_string(sid_str, pdb_get_group_sid(u)));
+
+ if (pdb_get_init_flags(u, PDB_LOGONTIME) != PDB_DEFAULT)
+ xmlNewChild(user, data->ns, "logon_time",
+ iota(pdb_get_logon_time(u)));
+
+ if (pdb_get_init_flags(u, PDB_LOGOFFTIME) != PDB_DEFAULT)
+ xmlNewChild(user, data->ns, "logoff_time",
+ iota(pdb_get_logoff_time(u)));
+
+ if (pdb_get_init_flags(u, PDB_KICKOFFTIME) != PDB_DEFAULT)
+ xmlNewChild(user, data->ns, "kickoff_time",
+ iota(pdb_get_kickoff_time(u)));
+
+ if (pdb_get_domain(u) && strcmp(pdb_get_domain(u), ""))
+ xmlNewChild(user, data->ns, "domain", pdb_get_domain(u));
+
+ if (pdb_get_nt_username(u) && strcmp(pdb_get_nt_username(u), ""))
+ xmlNewChild(user, data->ns, "nt_username", pdb_get_nt_username(u));
+
+ if (pdb_get_fullname(u) && strcmp(pdb_get_fullname(u), ""))
+ xmlNewChild(user, data->ns, "fullname", pdb_get_fullname(u));
+
+ if (pdb_get_homedir(u) && strcmp(pdb_get_homedir(u), ""))
+ xmlNewChild(user, data->ns, "homedir", pdb_get_homedir(u));
+
+ if (pdb_get_dir_drive(u) && strcmp(pdb_get_dir_drive(u), ""))
+ xmlNewChild(user, data->ns, "dir_drive", pdb_get_dir_drive(u));
+
+ if (pdb_get_logon_script(u) && strcmp(pdb_get_logon_script(u), ""))
+ xmlNewChild(user, data->ns, "logon_script",
+ pdb_get_logon_script(u));
+
+ if (pdb_get_profile_path(u) && strcmp(pdb_get_profile_path(u), ""))
+ xmlNewChild(user, data->ns, "profile_path",
+ pdb_get_profile_path(u));
+
+ if (pdb_get_acct_desc(u) && strcmp(pdb_get_acct_desc(u), ""))
+ xmlNewChild(user, data->ns, "acct_desc", pdb_get_acct_desc(u));
+
+ if (pdb_get_workstations(u) && strcmp(pdb_get_workstations(u), ""))
+ xmlNewChild(user, data->ns, "workstations",
+ pdb_get_workstations(u));
+
+ if (pdb_get_unknown_str(u) && strcmp(pdb_get_unknown_str(u), ""))
+ xmlNewChild(user, data->ns, "unknown_str", pdb_get_unknown_str(u));
+
+ if (pdb_get_munged_dial(u) && strcmp(pdb_get_munged_dial(u), ""))
+ xmlNewChild(user, data->ns, "munged_dial", pdb_get_munged_dial(u));
+
+
+ /* Password stuff */
+ pass = xmlNewChild(user, data->ns, "password", NULL);
+ if (pdb_get_pass_last_set_time(u))
+ xmlNewProp(pass, "last_set", iota(pdb_get_pass_last_set_time(u)));
+ if (pdb_get_init_flags(u, PDB_CANCHANGETIME) != PDB_DEFAULT)
+ xmlNewProp(pass, "can_change",
+ iota(pdb_get_pass_can_change_time(u)));
+
+ if (pdb_get_init_flags(u, PDB_MUSTCHANGETIME) != PDB_DEFAULT)
+ xmlNewProp(pass, "must_change",
+ iota(pdb_get_pass_must_change_time(u)));
+
+
+ if (pdb_get_lanman_passwd(u)) {
+ pdb_sethexpwd(temp, pdb_get_lanman_passwd(u),
+ pdb_get_acct_ctrl(u));
+ cur = xmlNewChild(pass, data->ns, "crypt", temp);
+ xmlNewProp(cur, "type", "lanman");
+ }
+
+ if (pdb_get_nt_passwd(u)) {
+ pdb_sethexpwd(temp, pdb_get_nt_passwd(u), pdb_get_acct_ctrl(u));
+ cur = xmlNewChild(pass, data->ns, "crypt", temp);
+ xmlNewProp(cur, "type", "nt");
+ }
+
+ xmlNewChild(user, data->ns, "acct_ctrl", iota(pdb_get_acct_ctrl(u)));
+
+ if (pdb_get_logon_divs(u))
+ xmlNewChild(user, data->ns, "logon_divs",
+ iota(pdb_get_logon_divs(u)));
+
+ if (pdb_get_hours_len(u))
+ xmlNewChild(user, data->ns, "hours_len",
+ iota(pdb_get_hours_len(u)));
+
+ xmlNewChild(user, data->ns, "bad_password_count", iota(pdb_get_bad_password_count(u)));
+ xmlNewChild(user, data->ns, "logon_count", iota(pdb_get_logon_count(u)));
+ xmlNewChild(user, data->ns, "unknown_6", iota(pdb_get_unknown_6(u)));
+ xmlSaveFile(data->location, data->doc);
+
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS xmlsam_init(PDB_CONTEXT * pdb_context, PDB_METHODS ** pdb_method,
+ const char *location)
+{
+ NTSTATUS nt_status;
+ pdb_xml *data;
+
+ xmlsam_debug_level = debug_add_class("xmlsam");
+ if (xmlsam_debug_level == -1) {
+ xmlsam_debug_level = DBGC_ALL;
+ DEBUG(0, ("xmlsam: Couldn't register custom debugging class!\n"));
+ }
+
+ if (!pdb_context) {
+ DEBUG(0, ("invalid pdb_methods specified\n"));
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ if (!NT_STATUS_IS_OK
+ (nt_status = make_pdb_methods(pdb_context->mem_ctx, pdb_method))) {
+ return nt_status;
+ }
+
+ (*pdb_method)->name = "xmlsam";
+
+ (*pdb_method)->setsampwent = xmlsam_setsampwent;
+ (*pdb_method)->endsampwent = xmlsam_endsampwent;
+ (*pdb_method)->getsampwent = xmlsam_getsampwent;
+ (*pdb_method)->add_sam_account = xmlsam_add_sam_account;
+ (*pdb_method)->getsampwnam = NULL;
+ (*pdb_method)->getsampwsid = NULL;
+ (*pdb_method)->update_sam_account = NULL;
+ (*pdb_method)->delete_sam_account = NULL;
+ (*pdb_method)->get_group_info_by_sid = NULL;
+ (*pdb_method)->get_group_list = NULL;
+ (*pdb_method)->get_group_sids = NULL;
+ (*pdb_method)->add_group = NULL;
+ (*pdb_method)->update_group = NULL;
+ (*pdb_method)->delete_group = NULL;
+ (*pdb_method)->add_sid_to_group = NULL;
+ (*pdb_method)->remove_sid_from_group = NULL;
+ (*pdb_method)->get_group_info_by_name = NULL;
+ (*pdb_method)->get_group_info_by_nt_name = NULL;
+ (*pdb_method)->get_group_uids = NULL;
+
+ data = talloc(pdb_context->mem_ctx, sizeof(pdb_xml));
+ data->location = talloc_strdup(pdb_context->mem_ctx, (location ? location : "passdb.xml"));
+ data->pwent = NULL;
+ data->written = 0;
+ (*pdb_method)->private_data = data;
+
+ LIBXML_TEST_VERSION xmlKeepBlanksDefault(0);
+
+ return NT_STATUS_OK;
+}
+
+NTSTATUS pdb_xml_init(void)
+{
+ return smb_register_passdb(PASSDB_INTERFACE_VERSION, "xml", xmlsam_init);
+}
diff --git a/source/passdb/privileges.c b/source/passdb/privileges.c
new file mode 100644
index 00000000000..624817e32e0
--- /dev/null
+++ b/source/passdb/privileges.c
@@ -0,0 +1,341 @@
+/*
+ * Unix SMB/CIFS implementation.
+ *
+ * default privileges backend for passdb
+ *
+ * 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 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"
+
+/*
+ this is a local implementation of a privileges backend, with
+ privileges stored in a tdb. Most passdb implementations will
+ probably use this backend, although some (such as pdb_ldap) will
+ store the privileges in another manner.
+
+ The basic principle is that the backend should store a list of SIDs
+ associated with each right, where a right is a string name such as
+ 'SeTakeOwnershipPrivilege'. The SIDs can be of any type, and do not
+ need to belong to the local domain.
+
+ The way this is used is that certain places in the code which
+ require access control will ask the privileges backend 'does this
+ user have the following privilege'. The 'user' will be a NT_TOKEN,
+ which is essentially just a list of SIDs. If any of those SIDs are
+ listed in the list of SIDs for that privilege then the answer will
+ be 'yes'. That will usually mean that the user gets unconditional
+ access to that functionality, regradless of any ACLs. In this way
+ privileges act in a similar fashion to unix setuid bits.
+*/
+
+/*
+ The terms 'right' and 'privilege' are used interchangably in this
+ file. This follows MSDN convention where the LSA calls are calls on
+ 'rights', which really means privileges. My apologies for the
+ confusion.
+*/
+
+
+/* 15 seconds seems like an ample time for timeouts on the privileges db */
+#define LOCK_TIMEOUT 15
+
+
+/* the tdb handle for the privileges database */
+static TDB_CONTEXT *tdb;
+
+
+/* initialise the privilege database */
+BOOL privilege_init(void)
+{
+ tdb = tdb_open_log(lock_path("privilege.tdb"), 0, TDB_DEFAULT,
+ O_RDWR|O_CREAT, 0600);
+ if (!tdb) {
+ DEBUG(0,("Failed to open privilege database\n"));
+ return False;
+ }
+
+ return True;
+}
+
+/*
+ lock the record for a particular privilege (write lock)
+*/
+static NTSTATUS privilege_lock_right(const char *right)
+{
+ if (tdb_lock_bystring(tdb, right, LOCK_TIMEOUT) != 0) {
+ return NT_STATUS_INTERNAL_ERROR;
+ }
+ return NT_STATUS_OK;
+}
+
+/*
+ unlock the record for a particular privilege (write lock)
+*/
+static void privilege_unlock_right(const char *right)
+{
+ tdb_unlock_bystring(tdb, right);
+}
+
+
+/*
+ return a list of SIDs that have a particular right
+*/
+NTSTATUS privilege_enum_account_with_right(const char *right,
+ uint32 *count,
+ DOM_SID **sids)
+{
+ TDB_DATA data;
+ char *p;
+ int i;
+
+ if (!tdb) {
+ return NT_STATUS_INTERNAL_ERROR;
+ }
+
+ data = tdb_fetch_bystring(tdb, right);
+ if (!data.dptr) {
+ *count = 0;
+ *sids = NULL;
+ return NT_STATUS_OK;
+ }
+
+ /* count them */
+ for (i=0, p=data.dptr; p<data.dptr+data.dsize; i++) {
+ p += strlen(p) + 1;
+ }
+ *count = i;
+
+ /* allocate and parse */
+ *sids = malloc(sizeof(DOM_SID) * *count);
+ if (! *sids) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ for (i=0, p=data.dptr; p<data.dptr+data.dsize; i++) {
+ if (!string_to_sid(&(*sids)[i], p)) {
+ free(data.dptr);
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ }
+ p += strlen(p) + 1;
+ }
+
+ free(data.dptr);
+
+ return NT_STATUS_OK;
+}
+
+/*
+ set what accounts have a given right - this is an internal interface
+*/
+static NTSTATUS privilege_set_accounts_with_right(const char *right,
+ uint32 count,
+ DOM_SID *sids)
+{
+ TDB_DATA data;
+ char *p;
+ int i;
+
+ if (!tdb) {
+ return NT_STATUS_INTERNAL_ERROR;
+ }
+
+ /* allocate the maximum size that we might use */
+ data.dptr = malloc(count * ((MAXSUBAUTHS*11) + 30));
+ if (!data.dptr) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ p = data.dptr;
+
+ for (i=0;i<count;i++) {
+ sid_to_string(p, &sids[i]);
+ p += strlen(p) + 1;
+ }
+
+ data.dsize = PTR_DIFF(p, data.dptr);
+
+ if (tdb_store_bystring(tdb, right, data, TDB_REPLACE) != 0) {
+ free(data.dptr);
+ return NT_STATUS_INTERNAL_ERROR;
+ }
+
+ free(data.dptr);
+ return NT_STATUS_OK;
+}
+
+
+/*
+ add a SID to the list of SIDs for a right
+*/
+NTSTATUS privilege_add_account_right(const char *right,
+ DOM_SID *sid)
+{
+ NTSTATUS status;
+ DOM_SID *current_sids;
+ uint32 current_count;
+ int i;
+
+ status = privilege_lock_right(right);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ status = privilege_enum_account_with_right(right, &current_count, &current_sids);
+ if (!NT_STATUS_IS_OK(status)) {
+ privilege_unlock_right(right);
+ return status;
+ }
+
+ /* maybe that SID is already listed? this is not an error */
+ for (i=0;i<current_count;i++) {
+ if (sid_equal(&current_sids[i], sid)) {
+ privilege_unlock_right(right);
+ free(current_sids);
+ return NT_STATUS_OK;
+ }
+ }
+
+ /* add it in */
+ current_sids = Realloc(current_sids, sizeof(current_sids[0]) * (current_count+1));
+ if (!current_sids) {
+ privilege_unlock_right(right);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ sid_copy(&current_sids[current_count], sid);
+ current_count++;
+
+ status = privilege_set_accounts_with_right(right, current_count, current_sids);
+
+ free(current_sids);
+ privilege_unlock_right(right);
+
+ return status;
+}
+
+
+/*
+ remove a SID from the list of SIDs for a right
+*/
+NTSTATUS privilege_remove_account_right(const char *right,
+ DOM_SID *sid)
+{
+ NTSTATUS status;
+ DOM_SID *current_sids;
+ uint32 current_count;
+ int i;
+
+ status = privilege_lock_right(right);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ status = privilege_enum_account_with_right(right, &current_count, &current_sids);
+ if (!NT_STATUS_IS_OK(status)) {
+ privilege_unlock_right(right);
+ return status;
+ }
+
+ for (i=0;i<current_count;i++) {
+ if (sid_equal(&current_sids[i], sid)) {
+ /* found it - so remove it */
+ if (current_count-i > 1) {
+ memmove(&current_sids[i], &current_sids[i+1],
+ sizeof(current_sids[0]) * ((current_count-i)-1));
+ }
+ current_count--;
+ status = privilege_set_accounts_with_right(right,
+ current_count,
+ current_sids);
+ free(current_sids);
+ privilege_unlock_right(right);
+ return status;
+ }
+ }
+
+ /* removing a right that you don't have is not an error */
+
+ safe_free(current_sids);
+ privilege_unlock_right(right);
+ return NT_STATUS_OK;
+}
+
+
+/*
+ an internal function for checking if a SID has a right
+*/
+static BOOL privilege_sid_has_right(DOM_SID *sid, const char *right)
+{
+ NTSTATUS status;
+ uint32 count;
+ DOM_SID *sids;
+ int i;
+
+ status = privilege_enum_account_with_right(right, &count, &sids);
+ if (!NT_STATUS_IS_OK(status)) {
+ return False;
+ }
+ for (i=0;i<count;i++) {
+ if (sid_equal(sid, &sids[i])) {
+ free(sids);
+ return True;
+ }
+ }
+
+ safe_free(sids);
+ return False;
+}
+
+/*
+ list the rights for an account. This involves traversing the database
+*/
+NTSTATUS privilege_enum_account_rights(DOM_SID *sid,
+ uint32 *count,
+ char ***rights)
+{
+ TDB_DATA key, nextkey;
+ char *right;
+
+ if (!tdb) {
+ return NT_STATUS_INTERNAL_ERROR;
+ }
+
+ *rights = NULL;
+ *count = 0;
+
+ for (key = tdb_firstkey(tdb); key.dptr; key = nextkey) {
+ nextkey = tdb_nextkey(tdb, key);
+
+ right = key.dptr;
+
+ if (privilege_sid_has_right(sid, right)) {
+ (*rights) = (char **)Realloc(*rights,sizeof(char *) * ((*count)+1));
+ if (! *rights) {
+ safe_free(nextkey.dptr);
+ free(key.dptr);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ (*rights)[*count] = strdup(right);
+ (*count)++;
+ }
+
+ free(key.dptr);
+ }
+
+ return NT_STATUS_OK;
+}
diff --git a/source/passdb/secrets.c b/source/passdb/secrets.c
new file mode 100644
index 00000000000..7531435e84f
--- /dev/null
+++ b/source/passdb/secrets.c
@@ -0,0 +1,824 @@
+/*
+ Unix SMB/CIFS implementation.
+ Copyright (C) Andrew Tridgell 1992-2001
+ Copyright (C) Andrew Bartlett 2002
+ Copyright (C) Rafal Szczesniak 2002
+ Copyright (C) Tim Potter 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 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.
+*/
+
+/* the Samba secrets database stores any generated, private information
+ such as the local SID and machine trust password */
+
+#include "includes.h"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_PASSDB
+
+static TDB_CONTEXT *tdb;
+
+/* open up the secrets database */
+BOOL secrets_init(void)
+{
+ pstring fname;
+
+ if (tdb)
+ return True;
+
+ pstrcpy(fname, lp_private_dir());
+ pstrcat(fname,"/secrets.tdb");
+
+ tdb = tdb_open_log(fname, 0, TDB_DEFAULT, O_RDWR|O_CREAT, 0600);
+
+ if (!tdb) {
+ DEBUG(0,("Failed to open %s\n", fname));
+ return False;
+ }
+ return True;
+}
+
+/* read a entry from the secrets database - the caller must free the result
+ if size is non-null then the size of the entry is put in there
+ */
+void *secrets_fetch(const char *key, size_t *size)
+{
+ TDB_DATA kbuf, dbuf;
+ secrets_init();
+ if (!tdb)
+ return NULL;
+ kbuf.dptr = (char *)key;
+ kbuf.dsize = strlen(key);
+ dbuf = tdb_fetch(tdb, kbuf);
+ if (size)
+ *size = dbuf.dsize;
+ return dbuf.dptr;
+}
+
+/* store a secrets entry
+ */
+BOOL secrets_store(const char *key, const void *data, size_t size)
+{
+ TDB_DATA kbuf, dbuf;
+ secrets_init();
+ if (!tdb)
+ return False;
+ kbuf.dptr = (char *)key;
+ kbuf.dsize = strlen(key);
+ dbuf.dptr = (char *)data;
+ dbuf.dsize = size;
+ return tdb_store(tdb, kbuf, dbuf, TDB_REPLACE) == 0;
+}
+
+
+/* delete a secets database entry
+ */
+BOOL secrets_delete(const char *key)
+{
+ TDB_DATA kbuf;
+ secrets_init();
+ if (!tdb)
+ return False;
+ kbuf.dptr = (char *)key;
+ kbuf.dsize = strlen(key);
+ return tdb_delete(tdb, kbuf) == 0;
+}
+
+BOOL secrets_store_domain_sid(const char *domain, const DOM_SID *sid)
+{
+ fstring key;
+ BOOL ret;
+
+ slprintf(key, sizeof(key)-1, "%s/%s", SECRETS_DOMAIN_SID, domain);
+ strupper_m(key);
+ ret = secrets_store(key, sid, sizeof(DOM_SID));
+
+ /* Force a re-query, in case we modified our domain */
+ if (ret)
+ reset_global_sam_sid();
+ return ret;
+}
+
+BOOL secrets_fetch_domain_sid(const char *domain, DOM_SID *sid)
+{
+ DOM_SID *dyn_sid;
+ fstring key;
+ size_t size;
+
+ slprintf(key, sizeof(key)-1, "%s/%s", SECRETS_DOMAIN_SID, domain);
+ strupper_m(key);
+ dyn_sid = (DOM_SID *)secrets_fetch(key, &size);
+
+ if (dyn_sid == NULL)
+ return False;
+
+ if (size != sizeof(DOM_SID))
+ {
+ SAFE_FREE(dyn_sid);
+ return False;
+ }
+
+ *sid = *dyn_sid;
+ SAFE_FREE(dyn_sid);
+ return True;
+}
+
+BOOL secrets_store_domain_guid(const char *domain, struct uuid *guid)
+{
+ fstring key;
+
+ slprintf(key, sizeof(key)-1, "%s/%s", SECRETS_DOMAIN_GUID, domain);
+ strupper_m(key);
+ return secrets_store(key, guid, sizeof(struct uuid));
+}
+
+BOOL secrets_fetch_domain_guid(const char *domain, struct uuid *guid)
+{
+ struct uuid *dyn_guid;
+ fstring key;
+ size_t size;
+ struct uuid new_guid;
+
+ slprintf(key, sizeof(key)-1, "%s/%s", SECRETS_DOMAIN_GUID, domain);
+ strupper_m(key);
+ dyn_guid = (struct uuid *)secrets_fetch(key, &size);
+
+ if ((!dyn_guid) && (lp_server_role() == ROLE_DOMAIN_PDC)) {
+ smb_uuid_generate_random(&new_guid);
+ if (!secrets_store_domain_guid(domain, &new_guid))
+ return False;
+ dyn_guid = (struct uuid *)secrets_fetch(key, &size);
+ if (dyn_guid == NULL)
+ return False;
+ }
+
+ if (size != sizeof(struct uuid))
+ {
+ DEBUG(1,("UUID size %d is wrong!\n", (int)size));
+ SAFE_FREE(dyn_guid);
+ return False;
+ }
+
+ *guid = *dyn_guid;
+ SAFE_FREE(dyn_guid);
+ return True;
+}
+
+/**
+ * Form a key for fetching the machine trust account password
+ *
+ * @param domain domain name
+ *
+ * @return stored password's key
+ **/
+const char *trust_keystr(const char *domain)
+{
+ static fstring keystr;
+
+ slprintf(keystr,sizeof(keystr)-1,"%s/%s",
+ SECRETS_MACHINE_ACCT_PASS, domain);
+ strupper_m(keystr);
+
+ return keystr;
+}
+
+/**
+ * Form a key for fetching a trusted domain password
+ *
+ * @param domain trusted domain name
+ *
+ * @return stored password's key
+ **/
+static char *trustdom_keystr(const char *domain)
+{
+ static pstring keystr;
+
+ pstr_sprintf(keystr, "%s/%s", SECRETS_DOMTRUST_ACCT_PASS, domain);
+ strupper_m(keystr);
+
+ return keystr;
+}
+
+/************************************************************************
+ Lock the trust password entry.
+************************************************************************/
+
+BOOL secrets_lock_trust_account_password(const char *domain, BOOL dolock)
+{
+ if (!tdb)
+ return False;
+
+ if (dolock)
+ return (tdb_lock_bystring(tdb, trust_keystr(domain),0) == 0);
+ else
+ tdb_unlock_bystring(tdb, trust_keystr(domain));
+ return True;
+}
+
+/************************************************************************
+ Routine to get the default secure channel type for trust accounts
+************************************************************************/
+
+uint32 get_default_sec_channel(void)
+{
+ if (lp_server_role() == ROLE_DOMAIN_BDC ||
+ lp_server_role() == ROLE_DOMAIN_PDC) {
+ return SEC_CHAN_BDC;
+ } else {
+ return SEC_CHAN_WKSTA;
+ }
+}
+
+/************************************************************************
+ Routine to get the trust account password for a domain.
+ The user of this function must have locked the trust password file using
+ the above secrets_lock_trust_account_password().
+************************************************************************/
+
+BOOL secrets_fetch_trust_account_password(const char *domain, uint8 ret_pwd[16],
+ time_t *pass_last_set_time,
+ uint32 *channel)
+{
+ struct machine_acct_pass *pass;
+ char *plaintext;
+ size_t size;
+
+ plaintext = secrets_fetch_machine_password(domain, pass_last_set_time,
+ channel);
+ if (plaintext) {
+ DEBUG(4,("Using cleartext machine password\n"));
+ E_md4hash(plaintext, ret_pwd);
+ SAFE_FREE(plaintext);
+ return True;
+ }
+
+ if (!(pass = secrets_fetch(trust_keystr(domain), &size))) {
+ DEBUG(5, ("secrets_fetch failed!\n"));
+ return False;
+ }
+
+ if (size != sizeof(*pass)) {
+ DEBUG(0, ("secrets were of incorrect size!\n"));
+ return False;
+ }
+
+ if (pass_last_set_time) *pass_last_set_time = pass->mod_time;
+ memcpy(ret_pwd, pass->hash, 16);
+ SAFE_FREE(pass);
+
+ if (channel)
+ *channel = get_default_sec_channel();
+
+ return True;
+}
+
+/************************************************************************
+ Routine to get account password to trusted domain
+************************************************************************/
+
+BOOL secrets_fetch_trusted_domain_password(const char *domain, char** pwd,
+ DOM_SID *sid, time_t *pass_last_set_time)
+{
+ struct trusted_dom_pass pass;
+ size_t size;
+
+ /* unpacking structures */
+ char* pass_buf;
+ int pass_len = 0;
+
+ ZERO_STRUCT(pass);
+
+ /* fetching trusted domain password structure */
+ if (!(pass_buf = secrets_fetch(trustdom_keystr(domain), &size))) {
+ DEBUG(5, ("secrets_fetch failed!\n"));
+ return False;
+ }
+
+ /* unpack trusted domain password */
+ pass_len = tdb_trusted_dom_pass_unpack(pass_buf, size, &pass);
+ SAFE_FREE(pass_buf);
+
+ if (pass_len != size) {
+ DEBUG(5, ("Invalid secrets size. Unpacked data doesn't match trusted_dom_pass structure.\n"));
+ return False;
+ }
+
+ /* the trust's password */
+ if (pwd) {
+ *pwd = strdup(pass.pass);
+ if (!*pwd) {
+ return False;
+ }
+ }
+
+ /* last change time */
+ if (pass_last_set_time) *pass_last_set_time = pass.mod_time;
+
+ /* domain sid */
+ sid_copy(sid, &pass.domain_sid);
+
+ return True;
+}
+
+
+/**
+ * Routine to store the password for trusted domain
+ *
+ * @param domain remote domain name
+ * @param pwd plain text password of trust relationship
+ * @param sid remote domain sid
+ *
+ * @return true if succeeded
+ **/
+
+BOOL secrets_store_trusted_domain_password(const char* domain, smb_ucs2_t *uni_dom_name,
+ size_t uni_name_len, const char* pwd,
+ DOM_SID sid)
+{
+ /* packing structures */
+ pstring pass_buf;
+ int pass_len = 0;
+ int pass_buf_len = sizeof(pass_buf);
+
+ struct trusted_dom_pass pass;
+ ZERO_STRUCT(pass);
+
+ /* unicode domain name and its length */
+ if (!uni_dom_name)
+ return False;
+
+ strncpy_w(pass.uni_name, uni_dom_name, sizeof(pass.uni_name) - 1);
+ pass.uni_name_len = uni_name_len;
+
+ /* last change time */
+ pass.mod_time = time(NULL);
+
+ /* password of the trust */
+ pass.pass_len = strlen(pwd);
+ fstrcpy(pass.pass, pwd);
+
+ /* domain sid */
+ sid_copy(&pass.domain_sid, &sid);
+
+ pass_len = tdb_trusted_dom_pass_pack(pass_buf, pass_buf_len, &pass);
+
+ return secrets_store(trustdom_keystr(domain), (void *)&pass_buf, pass_len);
+}
+
+/************************************************************************
+ Routine to set the plaintext machine account password for a realm
+the password is assumed to be a null terminated ascii string
+************************************************************************/
+
+BOOL secrets_store_machine_password(const char *pass, const char *domain, uint32 sec_channel)
+{
+ char *key = NULL;
+ BOOL ret;
+ uint32 last_change_time;
+ uint32 sec_channel_type;
+
+ asprintf(&key, "%s/%s", SECRETS_MACHINE_PASSWORD, domain);
+ if (!key)
+ return False;
+ strupper_m(key);
+
+ ret = secrets_store(key, pass, strlen(pass)+1);
+ SAFE_FREE(key);
+
+ if (!ret)
+ return ret;
+
+ asprintf(&key, "%s/%s", SECRETS_MACHINE_LAST_CHANGE_TIME, domain);
+ if (!key)
+ return False;
+ strupper_m(key);
+
+ SIVAL(&last_change_time, 0, time(NULL));
+ ret = secrets_store(key, &last_change_time, sizeof(last_change_time));
+ SAFE_FREE(key);
+
+ asprintf(&key, "%s/%s", SECRETS_MACHINE_SEC_CHANNEL_TYPE, domain);
+ if (!key)
+ return False;
+ strupper_m(key);
+
+ SIVAL(&sec_channel_type, 0, sec_channel);
+ ret = secrets_store(key, &sec_channel_type, sizeof(sec_channel_type));
+ SAFE_FREE(key);
+
+ return ret;
+}
+
+
+/************************************************************************
+ Routine to fetch the plaintext machine account password for a realm
+the password is assumed to be a null terminated ascii string
+************************************************************************/
+char *secrets_fetch_machine_password(const char *domain,
+ time_t *pass_last_set_time,
+ uint32 *channel)
+{
+ char *key = NULL;
+ char *ret;
+ asprintf(&key, "%s/%s", SECRETS_MACHINE_PASSWORD, domain);
+ strupper_m(key);
+ ret = (char *)secrets_fetch(key, NULL);
+ SAFE_FREE(key);
+
+ if (pass_last_set_time) {
+ size_t size;
+ uint32 *last_set_time;
+ asprintf(&key, "%s/%s", SECRETS_MACHINE_LAST_CHANGE_TIME, domain);
+ strupper_m(key);
+ last_set_time = secrets_fetch(key, &size);
+ if (last_set_time) {
+ *pass_last_set_time = IVAL(last_set_time,0);
+ SAFE_FREE(last_set_time);
+ } else {
+ *pass_last_set_time = 0;
+ }
+ SAFE_FREE(key);
+ }
+
+ if (channel) {
+ size_t size;
+ uint32 *channel_type;
+ asprintf(&key, "%s/%s", SECRETS_MACHINE_SEC_CHANNEL_TYPE, domain);
+ strupper_m(key);
+ channel_type = secrets_fetch(key, &size);
+ if (channel_type) {
+ *channel = IVAL(channel_type,0);
+ SAFE_FREE(channel_type);
+ } else {
+ *channel = get_default_sec_channel();
+ }
+ SAFE_FREE(key);
+ }
+
+ return ret;
+}
+
+
+
+/************************************************************************
+ Routine to delete the machine trust account password file for a domain.
+************************************************************************/
+
+BOOL trust_password_delete(const char *domain)
+{
+ return secrets_delete(trust_keystr(domain));
+}
+
+/************************************************************************
+ Routine to delete the password for trusted domain
+************************************************************************/
+
+BOOL trusted_domain_password_delete(const char *domain)
+{
+ return secrets_delete(trustdom_keystr(domain));
+}
+
+
+/*******************************************************************
+ Reset the 'done' variables so after a client process is created
+ from a fork call these calls will be re-done. This should be
+ expanded if more variables need reseting.
+ ******************************************************************/
+
+void reset_globals_after_fork(void)
+{
+ unsigned char dummy;
+
+ secrets_init();
+
+ /*
+ * Increment the global seed value to ensure every smbd starts
+ * with a new random seed.
+ */
+
+ if (tdb) {
+ uint32 initial_val = sys_getpid();
+ tdb_change_int32_atomic(tdb, "INFO/random_seed", (int *)&initial_val, 1);
+ set_rand_reseed_data((unsigned char *)&initial_val, sizeof(initial_val));
+ }
+
+ /*
+ * Re-seed the random crypto generator, so all smbd's
+ * started from the same parent won't generate the same
+ * sequence.
+ */
+ generate_random_buffer( &dummy, 1, True);
+}
+
+BOOL secrets_store_ldap_pw(const char* dn, char* pw)
+{
+ char *key = NULL;
+ BOOL ret;
+
+ if (asprintf(&key, "%s/%s", SECRETS_LDAP_BIND_PW, dn) < 0) {
+ DEBUG(0, ("secrets_store_ldap_pw: asprintf failed!\n"));
+ return False;
+ }
+
+ ret = secrets_store(key, pw, strlen(pw)+1);
+
+ SAFE_FREE(key);
+ return ret;
+}
+
+
+/**
+ * Get trusted domains info from secrets.tdb.
+ *
+ * The linked list is allocated on the supplied talloc context, caller gets to destroy
+ * when done.
+ *
+ * @param ctx Allocation context
+ * @param enum_ctx Starting index, eg. we can start fetching at third
+ * or sixth trusted domain entry. Zero is the first index.
+ * Value it is set to is the enum context for the next enumeration.
+ * @param num_domains Number of domain entries to fetch at one call
+ * @param domains Pointer to array of trusted domain structs to be filled up
+ *
+ * @return nt status code of rpc response
+ **/
+
+NTSTATUS secrets_get_trusted_domains(TALLOC_CTX* ctx, int* enum_ctx, unsigned int max_num_domains,
+ int *num_domains, TRUSTDOM ***domains)
+{
+ TDB_LIST_NODE *keys, *k;
+ TRUSTDOM *dom = NULL;
+ char *pattern;
+ unsigned int start_idx;
+ uint32 idx = 0;
+ size_t size, packed_size = 0;
+ fstring dom_name;
+ char *packed_pass;
+ struct trusted_dom_pass *pass = talloc_zero(ctx, sizeof(struct trusted_dom_pass));
+ NTSTATUS status;
+
+ if (!secrets_init()) return NT_STATUS_ACCESS_DENIED;
+
+ if (!pass) {
+ DEBUG(0, ("talloc_zero failed!\n"));
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ *num_domains = 0;
+ start_idx = *enum_ctx;
+
+ /* generate searching pattern */
+ if (!(pattern = talloc_asprintf(ctx, "%s/*", SECRETS_DOMTRUST_ACCT_PASS))) {
+ DEBUG(0, ("secrets_get_trusted_domains: talloc_asprintf() failed!\n"));
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ DEBUG(5, ("secrets_get_trusted_domains: looking for %d domains, starting at index %d\n",
+ max_num_domains, *enum_ctx));
+
+ *domains = talloc_zero(ctx, sizeof(**domains)*max_num_domains);
+
+ /* fetching trusted domains' data and collecting them in a list */
+ keys = tdb_search_keys(tdb, pattern);
+
+ /*
+ * if there's no keys returned ie. no trusted domain,
+ * return "no more entries" code
+ */
+ status = NT_STATUS_NO_MORE_ENTRIES;
+
+ /* searching for keys in secrets db -- way to go ... */
+ for (k = keys; k; k = k->next) {
+ char *secrets_key;
+
+ /* important: ensure null-termination of the key string */
+ secrets_key = strndup(k->node_key.dptr, k->node_key.dsize);
+ if (!secrets_key) {
+ DEBUG(0, ("strndup failed!\n"));
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ packed_pass = secrets_fetch(secrets_key, &size);
+ packed_size = tdb_trusted_dom_pass_unpack(packed_pass, size, pass);
+ /* packed representation isn't needed anymore */
+ SAFE_FREE(packed_pass);
+
+ if (size != packed_size) {
+ DEBUG(2, ("Secrets record %s is invalid!\n", secrets_key));
+ continue;
+ }
+
+ pull_ucs2_fstring(dom_name, pass->uni_name);
+ DEBUG(18, ("Fetched secret record num %d.\nDomain name: %s, SID: %s\n",
+ idx, dom_name, sid_string_static(&pass->domain_sid)));
+
+ SAFE_FREE(secrets_key);
+
+ if (idx >= start_idx && idx < start_idx + max_num_domains) {
+ dom = talloc_zero(ctx, sizeof(*dom));
+ if (!dom) {
+ /* free returned tdb record */
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ /* copy domain sid */
+ SMB_ASSERT(sizeof(dom->sid) == sizeof(pass->domain_sid));
+ memcpy(&(dom->sid), &(pass->domain_sid), sizeof(dom->sid));
+
+ /* copy unicode domain name */
+ dom->name = talloc_strdup_w(ctx, pass->uni_name);
+
+ (*domains)[idx - start_idx] = dom;
+
+ DEBUG(18, ("Secret record is in required range.\n \
+ start_idx = %d, max_num_domains = %d. Added to returned array.\n",
+ start_idx, max_num_domains));
+
+ *enum_ctx = idx + 1;
+ (*num_domains)++;
+
+ /* set proper status code to return */
+ if (k->next) {
+ /* there are yet some entries to enumerate */
+ status = STATUS_MORE_ENTRIES;
+ } else {
+ /* this is the last entry in the whole enumeration */
+ status = NT_STATUS_OK;
+ }
+ } else {
+ DEBUG(18, ("Secret is outside the required range.\n \
+ start_idx = %d, max_num_domains = %d. Not added to returned array\n",
+ start_idx, max_num_domains));
+ }
+
+ idx++;
+ }
+
+ DEBUG(5, ("secrets_get_trusted_domains: got %d domains\n", *num_domains));
+
+ /* free the results of searching the keys */
+ tdb_search_list_free(keys);
+
+ return status;
+}
+
+/*******************************************************************************
+ Lock the secrets tdb based on a string - this is used as a primitive form of mutex
+ between smbd instances.
+*******************************************************************************/
+
+BOOL secrets_named_mutex(const char *name, unsigned int timeout)
+{
+ int ret = 0;
+
+ if (!message_init())
+ return False;
+
+ ret = tdb_lock_bystring(tdb, name, timeout);
+ if (ret == 0)
+ DEBUG(10,("secrets_named_mutex: got mutex for %s\n", name ));
+
+ return (ret == 0);
+}
+
+/*******************************************************************************
+ Unlock a named mutex.
+*******************************************************************************/
+
+void secrets_named_mutex_release(const char *name)
+{
+ tdb_unlock_bystring(tdb, name);
+ DEBUG(10,("secrets_named_mutex: released mutex for %s\n", name ));
+}
+
+/*********************************************************
+ Check to see if we must talk to the PDC to avoid sam
+ sync delays
+ ********************************************************/
+
+BOOL must_use_pdc( const char *domain )
+{
+ time_t now = time(NULL);
+ time_t last_change_time;
+ unsigned char passwd[16];
+
+ if ( !secrets_fetch_trust_account_password(domain, passwd, &last_change_time, NULL) )
+ return False;
+
+ /*
+ * If the time the machine password has changed
+ * was less than about 15 minutes then we need to contact
+ * the PDC only, as we cannot be sure domain replication
+ * has yet taken place. Bug found by Gerald (way to go
+ * Gerald !). JRA.
+ */
+
+ if ( now - last_change_time < SAM_SYNC_WINDOW )
+ return True;
+
+ return False;
+
+}
+
+/*******************************************************************************
+ Store a complete AFS keyfile into secrets.tdb.
+*******************************************************************************/
+
+BOOL secrets_store_afs_keyfile(const char *cell, const struct afs_keyfile *keyfile)
+{
+ fstring key;
+
+ if ((cell == NULL) || (keyfile == NULL))
+ return False;
+
+ if (ntohl(keyfile->nkeys) > SECRETS_AFS_MAXKEYS)
+ return False;
+
+ slprintf(key, sizeof(key)-1, "%s/%s", SECRETS_AFS_KEYFILE, cell);
+ return secrets_store(key, keyfile, sizeof(struct afs_keyfile));
+}
+
+/*******************************************************************************
+ Fetch the current (highest) AFS key from secrets.tdb
+*******************************************************************************/
+BOOL secrets_fetch_afs_key(const char *cell, struct afs_key *result)
+{
+ fstring key;
+ struct afs_keyfile *keyfile;
+ size_t size;
+ uint32 i;
+
+ slprintf(key, sizeof(key)-1, "%s/%s", SECRETS_AFS_KEYFILE, cell);
+
+ keyfile = (struct afs_keyfile *)secrets_fetch(key, &size);
+
+ if (keyfile == NULL)
+ return False;
+
+ if (size != sizeof(struct afs_keyfile)) {
+ SAFE_FREE(keyfile);
+ return False;
+ }
+
+ i = ntohl(keyfile->nkeys);
+
+ if (i > SECRETS_AFS_MAXKEYS) {
+ SAFE_FREE(keyfile);
+ return False;
+ }
+
+ *result = keyfile->entry[i-1];
+
+ result->kvno = ntohl(result->kvno);
+
+ return True;
+}
+
+/******************************************************************************
+ When kerberos is not available, choose between anonymous or
+ authenticated connections.
+
+ We need to use an authenticated connection if DCs have the
+ RestrictAnonymous registry entry set > 0, or the "Additional
+ restrictions for anonymous connections" set in the win2k Local
+ Security Policy.
+
+ Caller to free() result in domain, username, password
+*******************************************************************************/
+void secrets_fetch_ipc_userpass(char **username, char **domain, char **password)
+{
+ *username = secrets_fetch(SECRETS_AUTH_USER, NULL);
+ *domain = secrets_fetch(SECRETS_AUTH_DOMAIN, NULL);
+ *password = secrets_fetch(SECRETS_AUTH_PASSWORD, NULL);
+
+ if (*username && **username) {
+
+ if (!*domain || !**domain)
+ *domain = smb_xstrdup(lp_workgroup());
+
+ if (!*password || !**password)
+ *password = smb_xstrdup("");
+
+ DEBUG(3, ("IPC$ connections done by user %s\\%s\n",
+ *domain, *username));
+
+ } else {
+ DEBUG(3, ("IPC$ connections done anonymously\n"));
+ *username = smb_xstrdup("");
+ *domain = smb_xstrdup("");
+ *password = smb_xstrdup("");
+ }
+}
+
diff --git a/source/passdb/util_sam_sid.c b/source/passdb/util_sam_sid.c
new file mode 100644
index 00000000000..3617498eec1
--- /dev/null
+++ b/source/passdb/util_sam_sid.c
@@ -0,0 +1,332 @@
+/*
+ Unix SMB/CIFS implementation.
+ Samba utility functions
+ Copyright (C) Andrew Tridgell 1992-1998
+ Copyright (C) Luke Kenneth Caseson Leighton 1998-1999
+ Copyright (C) Jeremy Allison 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 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"
+
+#define MAX_SID_NAMES 7
+
+typedef struct _known_sid_users {
+ uint32 rid;
+ enum SID_NAME_USE sid_name_use;
+ const char *known_user_name;
+} known_sid_users;
+
+static struct sid_name_map_info
+{
+ DOM_SID *sid;
+ const char *name;
+ const known_sid_users *known_users;
+} sid_name_map[MAX_SID_NAMES];
+
+extern DOM_SID global_sid_Builtin; /* Local well-known domain */
+extern DOM_SID global_sid_World_Domain; /* Everyone domain */
+extern DOM_SID global_sid_Creator_Owner_Domain; /* Creator Owner domain */
+extern DOM_SID global_sid_NT_Authority; /* NT Authority */
+
+
+static BOOL sid_name_map_initialized = False;
+/* static known_sid_users no_users[] = {{0, 0, NULL}}; */
+
+static const known_sid_users everyone_users[] = {
+ { 0, SID_NAME_WKN_GRP, "Everyone" },
+ {0, (enum SID_NAME_USE)0, NULL}};
+
+static const known_sid_users creator_owner_users[] = {
+ { 0, SID_NAME_WKN_GRP, "Creator Owner" },
+ { 1, SID_NAME_WKN_GRP, "Creator Group" },
+ {0, (enum SID_NAME_USE)0, NULL}};
+
+static const known_sid_users nt_authority_users[] = {
+ { 1, SID_NAME_ALIAS, "Dialup" },
+ { 2, SID_NAME_ALIAS, "Network"},
+ { 3, SID_NAME_ALIAS, "Batch"},
+ { 4, SID_NAME_ALIAS, "Interactive"},
+ { 6, SID_NAME_ALIAS, "Service"},
+ { 7, SID_NAME_ALIAS, "AnonymousLogon"},
+ { 8, SID_NAME_ALIAS, "Proxy"},
+ { 9, SID_NAME_ALIAS, "ServerLogon"},
+ { 11, SID_NAME_ALIAS, "Authenticated Users"},
+ { 18, SID_NAME_ALIAS, "SYSTEM"},
+ { 0, (enum SID_NAME_USE)0, NULL}};
+
+static const known_sid_users builtin_groups[] = {
+ { BUILTIN_ALIAS_RID_ADMINS, SID_NAME_ALIAS, "Administrators" },
+ { BUILTIN_ALIAS_RID_USERS, SID_NAME_ALIAS, "Users" },
+ { BUILTIN_ALIAS_RID_GUESTS, SID_NAME_ALIAS, "Guests" },
+ { BUILTIN_ALIAS_RID_POWER_USERS, SID_NAME_ALIAS, "Power Users" },
+ { BUILTIN_ALIAS_RID_ACCOUNT_OPS, SID_NAME_ALIAS, "Account Operators" },
+ { BUILTIN_ALIAS_RID_SYSTEM_OPS, SID_NAME_ALIAS, "Server Operators" },
+ { BUILTIN_ALIAS_RID_PRINT_OPS, SID_NAME_ALIAS, "Print Operators" },
+ { BUILTIN_ALIAS_RID_BACKUP_OPS, SID_NAME_ALIAS, "Backup Operators" },
+ { BUILTIN_ALIAS_RID_REPLICATOR, SID_NAME_ALIAS, "Replicator" },
+ { BUILTIN_ALIAS_RID_RAS_SERVERS, SID_NAME_ALIAS, "RAS Servers" },
+ { BUILTIN_ALIAS_RID_PRE_2K_ACCESS, SID_NAME_ALIAS, "Pre-Windows 2000 Compatible Access" },
+ { 0, (enum SID_NAME_USE)0, NULL}};
+
+/**************************************************************************
+ Quick init function.
+*************************************************************************/
+
+static void init_sid_name_map (void)
+{
+ int i = 0;
+
+ if (sid_name_map_initialized) return;
+
+ generate_wellknown_sids();
+
+ if ((lp_security() == SEC_USER) && lp_domain_logons()) {
+ sid_name_map[i].sid = get_global_sam_sid();
+ /* This is not lp_workgroup() for good reason:
+ it must stay around longer than the lp_*()
+ strings do */
+ sid_name_map[i].name = strdup(lp_workgroup());
+ sid_name_map[i].known_users = NULL;
+ i++;
+ sid_name_map[i].sid = get_global_sam_sid();
+ sid_name_map[i].name = strdup(global_myname());
+ sid_name_map[i].known_users = NULL;
+ i++;
+ } else {
+ sid_name_map[i].sid = get_global_sam_sid();
+ sid_name_map[i].name = strdup(global_myname());
+ sid_name_map[i].known_users = NULL;
+ i++;
+ }
+
+ sid_name_map[i].sid = &global_sid_Builtin;
+ sid_name_map[i].name = "BUILTIN";
+ sid_name_map[i].known_users = &builtin_groups[0];
+ i++;
+
+ sid_name_map[i].sid = &global_sid_World_Domain;
+ sid_name_map[i].name = "";
+ sid_name_map[i].known_users = &everyone_users[0];
+ i++;
+
+ sid_name_map[i].sid = &global_sid_Creator_Owner_Domain;
+ sid_name_map[i].name = "";
+ sid_name_map[i].known_users = &creator_owner_users[0];
+ i++;
+
+ sid_name_map[i].sid = &global_sid_NT_Authority;
+ sid_name_map[i].name = "NT Authority";
+ sid_name_map[i].known_users = &nt_authority_users[0];
+ i++;
+
+ /* End of array. */
+ sid_name_map[i].sid = NULL;
+ sid_name_map[i].name = NULL;
+ sid_name_map[i].known_users = NULL;
+
+ sid_name_map_initialized = True;
+
+ return;
+}
+
+/**************************************************************************
+ Turns a domain SID into a name, returned in the nt_domain argument.
+***************************************************************************/
+
+BOOL map_domain_sid_to_name(DOM_SID *sid, fstring nt_domain)
+{
+ fstring sid_str;
+ int i = 0;
+
+ sid_to_string(sid_str, sid);
+
+ if (!sid_name_map_initialized)
+ init_sid_name_map();
+
+ DEBUG(5,("map_domain_sid_to_name: %s\n", sid_str));
+
+ if (nt_domain == NULL)
+ return False;
+
+ while (sid_name_map[i].sid != NULL) {
+ sid_to_string(sid_str, sid_name_map[i].sid);
+ DEBUG(5,("map_domain_sid_to_name: compare: %s\n", sid_str));
+ if (sid_equal(sid_name_map[i].sid, sid)) {
+ fstrcpy(nt_domain, sid_name_map[i].name);
+ DEBUG(5,("map_domain_sid_to_name: found '%s'\n", nt_domain));
+ return True;
+ }
+ i++;
+ }
+
+ DEBUG(5,("map_domain_sid_to_name: mapping for %s not found\n", sid_str));
+
+ return False;
+}
+
+/**************************************************************************
+ Looks up a known username from one of the known domains.
+***************************************************************************/
+
+BOOL lookup_known_rid(DOM_SID *sid, uint32 rid, char *name, enum SID_NAME_USE *psid_name_use)
+{
+ int i = 0;
+ struct sid_name_map_info *psnm;
+
+ if (!sid_name_map_initialized)
+ init_sid_name_map();
+
+ for(i = 0; sid_name_map[i].sid != NULL; i++) {
+ psnm = &sid_name_map[i];
+ if(sid_equal(psnm->sid, sid)) {
+ int j;
+ for(j = 0; psnm->known_users && psnm->known_users[j].known_user_name != NULL; j++) {
+ if(rid == psnm->known_users[j].rid) {
+ DEBUG(5,("lookup_builtin_rid: rid = %u, domain = '%s', user = '%s'\n",
+ (unsigned int)rid, psnm->name, psnm->known_users[j].known_user_name ));
+ fstrcpy( name, psnm->known_users[j].known_user_name);
+ *psid_name_use = psnm->known_users[j].sid_name_use;
+ return True;
+ }
+ }
+ }
+ }
+
+ return False;
+}
+
+/**************************************************************************
+ Turns a domain name into a SID.
+ *** side-effect: if the domain name is NULL, it is set to our domain ***
+***************************************************************************/
+
+BOOL map_domain_name_to_sid(DOM_SID *sid, char *nt_domain)
+{
+ int i = 0;
+
+ if (nt_domain == NULL) {
+ DEBUG(5,("map_domain_name_to_sid: mapping NULL domain to our SID.\n"));
+ sid_copy(sid, get_global_sam_sid());
+ return True;
+ }
+
+ if (nt_domain[0] == 0) {
+ fstrcpy(nt_domain, global_myname());
+ DEBUG(5,("map_domain_name_to_sid: overriding blank name to %s\n", nt_domain));
+ sid_copy(sid, get_global_sam_sid());
+ return True;
+ }
+
+ DEBUG(5,("map_domain_name_to_sid: %s\n", nt_domain));
+
+ if (!sid_name_map_initialized)
+ init_sid_name_map();
+
+ while (sid_name_map[i].name != NULL) {
+ DEBUG(5,("map_domain_name_to_sid: compare: %s\n", sid_name_map[i].name));
+ if (strequal(sid_name_map[i].name, nt_domain)) {
+ fstring sid_str;
+ sid_copy(sid, sid_name_map[i].sid);
+ sid_to_string(sid_str, sid_name_map[i].sid);
+ DEBUG(5,("map_domain_name_to_sid: found %s\n", sid_str));
+ return True;
+ }
+ i++;
+ }
+
+ DEBUG(0,("map_domain_name_to_sid: mapping to %s not found.\n", nt_domain));
+ return False;
+}
+
+/*****************************************************************
+ Check if the SID is our domain SID (S-1-5-21-x-y-z).
+*****************************************************************/
+
+BOOL sid_check_is_domain(const DOM_SID *sid)
+{
+ return sid_equal(sid, get_global_sam_sid());
+}
+
+/*****************************************************************
+ Check if the SID is our domain SID (S-1-5-21-x-y-z).
+*****************************************************************/
+
+BOOL sid_check_is_in_our_domain(const DOM_SID *sid)
+{
+ DOM_SID dom_sid;
+ uint32 rid;
+
+ sid_copy(&dom_sid, sid);
+ sid_split_rid(&dom_sid, &rid);
+
+ return sid_equal(&dom_sid, get_global_sam_sid());
+}
+
+/**************************************************************************
+ Try and map a name to one of the well known SIDs.
+***************************************************************************/
+
+BOOL map_name_to_wellknown_sid(DOM_SID *sid, enum SID_NAME_USE *use, const char *name)
+{
+ int i, j;
+
+ if (!sid_name_map_initialized)
+ init_sid_name_map();
+
+ for (i=0; sid_name_map[i].sid != NULL; i++) {
+ const known_sid_users *users = sid_name_map[i].known_users;
+
+ if (users == NULL)
+ continue;
+
+ for (j=0; users[j].known_user_name != NULL; j++) {
+ if ( strequal(users[j].known_user_name, name) ) {
+ sid_copy(sid, sid_name_map[i].sid);
+ sid_append_rid(sid, users[j].rid);
+ *use = users[j].sid_name_use;
+ return True;
+ }
+ }
+ }
+
+ return False;
+}
+
+void add_sid_to_array(const DOM_SID *sid, DOM_SID **sids, int *num)
+{
+ *sids = Realloc(*sids, ((*num)+1) * sizeof(DOM_SID));
+
+ if (*sids == NULL)
+ return;
+
+ sid_copy(&((*sids)[*num]), sid);
+ *num += 1;
+
+ return;
+}
+
+void add_sid_to_array_unique(const DOM_SID *sid, DOM_SID **sids, int *num)
+{
+ int i;
+
+ for (i=0; i<*num; i++) {
+ if (sid_compare(sid, &(*sids)[i]) == 0)
+ return;
+ }
+
+ add_sid_to_array(sid, sids, num);
+}
diff --git a/source/po/de.msg b/source/po/de.msg
new file mode 100644
index 00000000000..b1b84a21b67
--- /dev/null
+++ b/source/po/de.msg
@@ -0,0 +1,593 @@
+# German messages for international release of SWAT.
+# Copyright (C) 2001 Andreas Moroder
+#
+# This program is free software; you can 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.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: i18n_swat\n"
+"POT-Creation-Date: 2003-10-06 05:30+0900\n"
+"PO-Revision-Date: 2000-02-08 14:45+0100\n"
+"Last-Translator: Andreas Moroder\n"
+"Language-Team: (Samba Team) <samba-technical@samba.org>\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=iso-8859-1\n"
+"Content-Transfer-Encoding: 8bit\n"
+
+#: ../web/swat.c:117
+#, c-format
+msgid "ERROR: Can't open %s"
+msgstr "ERRORE: Kann %s nicht öffnen"
+
+#: ../web/swat.c:200
+msgid "Help"
+msgstr "Hilfe"
+
+#: ../web/swat.c:206 ../web/swat.c:220 ../web/swat.c:235 ../web/swat.c:243 ../web/swat.c:252 ../web/swat.c:261 ../web/swat.c:267 ../web/swat.c:273 ../web/swat.c:286
+msgid "Set Default"
+msgstr "Setze Standardwerte"
+
+#: ../web/swat.c:408
+#, c-format
+msgid "failed to open %s for writing"
+msgstr ""
+
+#: ../web/swat.c:431
+#, c-format
+msgid "Can't reload %s"
+msgstr ""
+
+#: ../web/swat.c:501
+#, c-format
+msgid "Logged in as <b>%s</b>"
+msgstr "Verbunden als <b>%s</b>"
+
+#: ../web/swat.c:505
+msgid "Home"
+msgstr "Home"
+
+#: ../web/swat.c:507
+msgid "Globals"
+msgstr "Globals"
+
+#: ../web/swat.c:508
+msgid "Shares"
+msgstr "Freigaben"
+
+#: ../web/swat.c:509
+msgid "Printers"
+msgstr "Drucker"
+
+#: ../web/swat.c:510
+msgid "Wizard"
+msgstr ""
+
+#: ../web/swat.c:513
+msgid "Status"
+msgstr "Status"
+
+#: ../web/swat.c:514
+msgid "View Config"
+msgstr "Zeige Konfiguration"
+
+#: ../web/swat.c:516
+msgid "Password Management"
+msgstr "Passwortverwaltung"
+
+#: ../web/swat.c:526
+msgid "Current View Is"
+msgstr "Aktuelle Konfiguration"
+
+#: ../web/swat.c:527 ../web/swat.c:530
+msgid "Basic"
+msgstr "Basis Ansicht"
+
+#: ../web/swat.c:528 ../web/swat.c:531
+msgid "Advanced"
+msgstr "Erweiterte Ansicht"
+
+#: ../web/swat.c:529
+msgid "Change View To"
+msgstr "Ändere Passwort"
+
+#: ../web/swat.c:554
+msgid "Current Config"
+msgstr "Aktuelle Konfiguration"
+
+#: ../web/swat.c:558
+msgid "Normal View"
+msgstr "Normale Ansich"
+
+#: ../web/swat.c:560
+msgid "Full View"
+msgstr "Komplette Ansicht"
+
+#. Here we first set and commit all the parameters that were selected
+#. in the previous screen.
+#: ../web/swat.c:579
+msgid "Wizard Parameter Edit Page"
+msgstr ""
+
+#: ../web/swat.c:608
+msgid "Note: smb.conf file has been read and rewritten"
+msgstr ""
+
+#. Here we go ...
+#: ../web/swat.c:716
+msgid "Samba Configuration Wizard"
+msgstr ""
+
+#: ../web/swat.c:720
+msgid "The \"Rewrite smb.conf file\" button will clear the smb.conf file of all default values and of comments."
+msgstr ""
+
+#: ../web/swat.c:721
+msgid "The same will happen if you press the commit button."
+msgstr ""
+
+#: ../web/swat.c:724
+msgid "Rewrite smb.conf file"
+msgstr ""
+
+#: ../web/swat.c:725
+msgid "Commit"
+msgstr "Kommentar"
+
+#: ../web/swat.c:726
+msgid "Edit Parameter Values"
+msgstr "Drucker Parameter"
+
+#: ../web/swat.c:732
+msgid "Server Type"
+msgstr ""
+
+#: ../web/swat.c:733
+msgid "Stand Alone"
+msgstr ""
+
+#: ../web/swat.c:734
+msgid "Domain Member"
+msgstr "Domänen master"
+
+#: ../web/swat.c:735
+msgid "Domain Controller"
+msgstr "Domänen master"
+
+#: ../web/swat.c:738
+msgid "Unusual Type in smb.conf - Please Select New Mode"
+msgstr ""
+
+#: ../web/swat.c:740
+msgid "Configure WINS As"
+msgstr ""
+
+#: ../web/swat.c:741
+msgid "Not Used"
+msgstr "nicht hinabsteigen"
+
+#: ../web/swat.c:742
+msgid "Server for client use"
+msgstr ""
+
+#: ../web/swat.c:743
+msgid "Client of another WINS server"
+msgstr ""
+
+#: ../web/swat.c:745
+msgid "Remote WINS Server"
+msgstr ""
+
+#: ../web/swat.c:756
+msgid "Error: WINS Server Mode and WINS Support both set in smb.conf"
+msgstr ""
+
+#: ../web/swat.c:757
+msgid "Please Select desired WINS mode above."
+msgstr ""
+
+#: ../web/swat.c:759
+msgid "Expose Home Directories"
+msgstr ""
+
+#: ../web/swat.c:774
+msgid "The above configuration options will set multiple parameters and will generally assist with rapid Samba deployment."
+msgstr ""
+
+#: ../web/swat.c:787
+msgid "Global Parameters"
+msgstr "Globale Variablen"
+
+#: ../web/swat.c:815 ../web/swat.c:916 ../web/swat.c:1265
+msgid "Commit Changes"
+msgstr "Speichere Änderungen"
+
+#: ../web/swat.c:819 ../web/swat.c:919 ../web/swat.c:1267
+msgid "Reset Values"
+msgstr "Setze Werte zurück"
+
+#: ../web/swat.c:844
+msgid "Share Parameters"
+msgstr "Parameter der Freigabe"
+
+#: ../web/swat.c:887
+msgid "Choose Share"
+msgstr "Wähle Freigabe"
+
+#: ../web/swat.c:901
+msgid "Delete Share"
+msgstr "Lösche Freigabe"
+
+#: ../web/swat.c:908
+msgid "Create Share"
+msgstr "Erstelle Freigabe"
+
+#: ../web/swat.c:944
+msgid "password change in demo mode rejected"
+msgstr "Änderung des Passworts im Demo modus nicht aktiv"
+
+#: ../web/swat.c:957
+msgid "Can't setup password database vectors."
+msgstr ""
+
+#: ../web/swat.c:983
+msgid " Must specify \"User Name\" "
+msgstr " \"Benutzername\" muss angegeben werden "
+
+#: ../web/swat.c:999
+msgid " Must specify \"Old Password\" "
+msgstr " \"Altes Passwort\" muß angegeben werden "
+
+#: ../web/swat.c:1005
+msgid " Must specify \"Remote Machine\" "
+msgstr " \"Remote Maschine\" muß angegeben werden "
+
+#: ../web/swat.c:1012
+msgid " Must specify \"New, and Re-typed Passwords\" "
+msgstr " \"Neues/Bestätige Passwort\" muß angegeben werden "
+
+#: ../web/swat.c:1018
+msgid " Re-typed password didn't match new password "
+msgstr " Das bestätigte Passwort stimmt nicht mit dem neuen Passwort überein"
+
+#: ../web/swat.c:1048
+#, c-format
+msgid " The passwd for '%s' has been changed."
+msgstr " Das Passwort für '%s' wurde geändert."
+
+#: ../web/swat.c:1051
+#, c-format
+msgid " The passwd for '%s' has NOT been changed."
+msgstr " Das Passwort für '%s' wurde nicht geändert."
+
+#: ../web/swat.c:1076
+msgid "Server Password Management"
+msgstr "Verwaltung des Server Passwortes"
+
+#.
+#. * Create all the dialog boxes for data collection
+#.
+#: ../web/swat.c:1085 ../web/swat.c:1132
+msgid "User Name"
+msgstr "Benutzername"
+
+#: ../web/swat.c:1088 ../web/swat.c:1134
+msgid "Old Password"
+msgstr "Altes Passwort"
+
+#: ../web/swat.c:1091 ../web/swat.c:1136
+msgid "New Password"
+msgstr "Neues Passwort"
+
+#: ../web/swat.c:1093 ../web/swat.c:1138
+msgid "Re-type New Password"
+msgstr "Bestätige neues Passwort"
+
+#: ../web/swat.c:1101 ../web/swat.c:1149
+msgid "Change Password"
+msgstr "Ändere Passwort"
+
+#: ../web/swat.c:1104
+msgid "Add New User"
+msgstr "Füge Benutzer hinzu"
+
+#: ../web/swat.c:1106
+msgid "Delete User"
+msgstr "Lösche Benutzer"
+
+#: ../web/swat.c:1108
+msgid "Disable User"
+msgstr "Desaktiviere Benutzer"
+
+#: ../web/swat.c:1110
+msgid "Enable User"
+msgstr "Aktiviere Benutzer"
+
+#: ../web/swat.c:1123
+msgid "Client/Server Password Management"
+msgstr "Client/Server Passwort Verwaltung"
+
+#: ../web/swat.c:1140
+msgid "Remote Machine"
+msgstr "Remote Maschine"
+
+#: ../web/swat.c:1179
+msgid "Printer Parameters"
+msgstr "Drucker Parameter"
+
+#: ../web/swat.c:1181
+msgid "Important Note:"
+msgstr "Wichtige Hinweise:"
+
+#: ../web/swat.c:1182
+msgid "Printer names marked with [*] in the Choose Printer drop-down box "
+msgstr "Mit [*] gekennzeichnete Druckername in der Druckerauswahlliste"
+
+#: ../web/swat.c:1183
+msgid "are autoloaded printers from "
+msgstr "wurde automatisch geladen von :"
+
+#: ../web/swat.c:1184
+msgid "Printcap Name"
+msgstr "Printcap Name"
+
+#: ../web/swat.c:1185
+msgid "Attempting to delete these printers from SWAT will have no effect."
+msgstr "Der Versuch diese Drucker von SWAT aus zu löschen wird keine Auswirkung haben."
+
+#: ../web/swat.c:1231
+msgid "Choose Printer"
+msgstr "Wähle Drucker"
+
+#: ../web/swat.c:1250
+msgid "Delete Printer"
+msgstr "Lösche Drucker"
+
+#: ../web/swat.c:1257
+msgid "Create Printer"
+msgstr "Ersteller Drucker"
+
+#: ../web/statuspage.c:123
+msgid "RDONLY "
+msgstr ""
+
+#: ../web/statuspage.c:124
+msgid "WRONLY "
+msgstr ""
+
+#: ../web/statuspage.c:125
+msgid "RDWR "
+msgstr ""
+
+#: ../web/statuspage.c:309
+msgid "Server Status"
+msgstr "Server Status"
+
+#: ../web/statuspage.c:314
+msgid "Auto Refresh"
+msgstr "Automatische Aktualisierung"
+
+#: ../web/statuspage.c:315 ../web/statuspage.c:320
+msgid "Refresh Interval: "
+msgstr "Aktualisierungsintervall: "
+
+#: ../web/statuspage.c:319
+msgid "Stop Refreshing"
+msgstr "Stop Aktualisierung"
+
+#: ../web/statuspage.c:334
+msgid "version:"
+msgstr "Version:"
+
+#: ../web/statuspage.c:337
+msgid "smbd:"
+msgstr ""
+
+#: ../web/statuspage.c:337 ../web/statuspage.c:350 ../web/statuspage.c:364
+msgid "running"
+msgstr "aktiv"
+
+#: ../web/statuspage.c:337 ../web/statuspage.c:350 ../web/statuspage.c:364
+msgid "not running"
+msgstr "inaktiv"
+
+#: ../web/statuspage.c:341
+msgid "Stop smbd"
+msgstr "Stopp smbd"
+
+#: ../web/statuspage.c:343
+msgid "Start smbd"
+msgstr "Start smbd"
+
+#: ../web/statuspage.c:345
+msgid "Restart smbd"
+msgstr "Neustart smbd"
+
+#: ../web/statuspage.c:350
+msgid "nmbd:"
+msgstr ""
+
+#: ../web/statuspage.c:354
+msgid "Stop nmbd"
+msgstr "Stopp nmbd"
+
+#: ../web/statuspage.c:356
+msgid "Start nmbd"
+msgstr "Start nmbd"
+
+#: ../web/statuspage.c:358
+msgid "Restart nmbd"
+msgstr "Neustart nmbd"
+
+#: ../web/statuspage.c:364
+msgid "winbindd:"
+msgstr ""
+
+#: ../web/statuspage.c:368
+msgid "Stop winbindd"
+msgstr "Stopp winbindd"
+
+#: ../web/statuspage.c:370
+msgid "Start winbindd"
+msgstr "Start winbindd"
+
+#: ../web/statuspage.c:372
+msgid "Restart winbindd"
+msgstr "Neustart winbindd"
+
+#. stop, restart all
+#: ../web/statuspage.c:381
+msgid "Stop All"
+msgstr ""
+
+#: ../web/statuspage.c:382
+msgid "Restart All"
+msgstr "Neustart Alle"
+
+#. start all
+#: ../web/statuspage.c:386
+msgid "Start All"
+msgstr "Start Alle"
+
+#: ../web/statuspage.c:393
+msgid "Active Connections"
+msgstr "Aktive Verbindungen"
+
+#: ../web/statuspage.c:395 ../web/statuspage.c:408 ../web/statuspage.c:416
+msgid "PID"
+msgstr ""
+
+#: ../web/statuspage.c:395 ../web/statuspage.c:408
+msgid "Client"
+msgstr ""
+
+#: ../web/statuspage.c:395
+msgid "IP address"
+msgstr "IP Adresse"
+
+#: ../web/statuspage.c:395 ../web/statuspage.c:408 ../web/statuspage.c:416
+msgid "Date"
+msgstr "Datum"
+
+#: ../web/statuspage.c:397
+msgid "Kill"
+msgstr "Kill"
+
+#: ../web/statuspage.c:405
+msgid "Active Shares"
+msgstr "Aktive Freigaben"
+
+#: ../web/statuspage.c:408
+msgid "Share"
+msgstr "Freigabe"
+
+#: ../web/statuspage.c:408
+msgid "User"
+msgstr "Benutzer"
+
+#: ../web/statuspage.c:408
+msgid "Group"
+msgstr "Gruppe"
+
+#: ../web/statuspage.c:414
+msgid "Open Files"
+msgstr "Offene Dateien"
+
+#: ../web/statuspage.c:416
+msgid "Sharing"
+msgstr ""
+
+#: ../web/statuspage.c:416
+msgid "R/W"
+msgstr ""
+
+#: ../web/statuspage.c:416
+msgid "Oplock"
+msgstr ""
+
+#: ../web/statuspage.c:416
+msgid "File"
+msgstr ""
+
+#: ../web/statuspage.c:425
+msgid "Show Client in col 1"
+msgstr ""
+
+#: ../web/statuspage.c:426
+msgid "Show PID in col 1"
+msgstr ""
+
+#: ../param/loadparm.c:755
+msgid "Base Options"
+msgstr "Basisoptionen"
+
+#: ../param/loadparm.c:775
+msgid "Security Options"
+msgstr "Sicherheitsoptionen"
+
+#: ../param/loadparm.c:859
+msgid "Logging Options"
+msgstr "Log Optionen"
+
+#: ../param/loadparm.c:874
+msgid "Protocol Options"
+msgstr "Protokoll Optionen"
+
+#: ../param/loadparm.c:911
+msgid "Tuning Options"
+msgstr "Optimierungsoptionen"
+
+#: ../param/loadparm.c:940
+msgid "Printing Options"
+msgstr "Druckoptionen"
+
+#: ../param/loadparm.c:970
+msgid "Filename Handling"
+msgstr "Verwaltung Dateinamen"
+
+#: ../param/loadparm.c:996
+msgid "Domain Options"
+msgstr "Domänen Optionen"
+
+#: ../param/loadparm.c:1000
+msgid "Logon Options"
+msgstr "Login optionen"
+
+#: ../param/loadparm.c:1019
+msgid "Browse Options"
+msgstr "Browsing Optionen"
+
+#: ../param/loadparm.c:1033
+msgid "WINS Options"
+msgstr "WINS Optionen"
+
+#: ../param/loadparm.c:1043
+msgid "Locking Options"
+msgstr "Locking Optionen"
+
+#: ../param/loadparm.c:1061
+msgid "Ldap Options"
+msgstr "LDAP Optionen"
+
+#: ../param/loadparm.c:1078
+msgid "Miscellaneous Options"
+msgstr "Verschiedene Optionen"
+
+#: ../param/loadparm.c:1138
+msgid "VFS module options"
+msgstr "VFS Optionen"
+
+#: ../param/loadparm.c:1148
+msgid "Winbind options"
+msgstr "Winbind Optionen"
diff --git a/source/po/en.msg b/source/po/en.msg
new file mode 100644
index 00000000000..6a1fcb55bac
--- /dev/null
+++ b/source/po/en.msg
@@ -0,0 +1,593 @@
+# English messages for international release of SWAT.
+# Copyright (C) 2003 TAKAHASHI Motonobu <monyo@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.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: i18n_swat \n"
+"POT-Creation-Date: 2003-10-06 05:30+0900\n"
+"PO-Revision-Date: 2000-02-08 12:48+09:00\n"
+"Last-Translator: TAKAHASHI Motonobu <monyo@samba.org>\n"
+"Language-Team: (Samba Team) <samba-technical@samba.org>\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=US-ASCII\n"
+"Content-Transfer-Encoding: \n"
+
+#: ../web/swat.c:117
+#, c-format
+msgid "ERROR: Can't open %s"
+msgstr ""
+
+#: ../web/swat.c:200
+msgid "Help"
+msgstr ""
+
+#: ../web/swat.c:206 ../web/swat.c:220 ../web/swat.c:235 ../web/swat.c:243 ../web/swat.c:252 ../web/swat.c:261 ../web/swat.c:267 ../web/swat.c:273 ../web/swat.c:286
+msgid "Set Default"
+msgstr ""
+
+#: ../web/swat.c:408
+#, c-format
+msgid "failed to open %s for writing"
+msgstr ""
+
+#: ../web/swat.c:431
+#, c-format
+msgid "Can't reload %s"
+msgstr ""
+
+#: ../web/swat.c:501
+#, c-format
+msgid "Logged in as <b>%s</b>"
+msgstr ""
+
+#: ../web/swat.c:505
+msgid "Home"
+msgstr ""
+
+#: ../web/swat.c:507
+msgid "Globals"
+msgstr ""
+
+#: ../web/swat.c:508
+msgid "Shares"
+msgstr ""
+
+#: ../web/swat.c:509
+msgid "Printers"
+msgstr ""
+
+#: ../web/swat.c:510
+msgid "Wizard"
+msgstr ""
+
+#: ../web/swat.c:513
+msgid "Status"
+msgstr ""
+
+#: ../web/swat.c:514
+msgid "View Config"
+msgstr ""
+
+#: ../web/swat.c:516
+msgid "Password Management"
+msgstr ""
+
+#: ../web/swat.c:526
+msgid "Current View Is"
+msgstr ""
+
+#: ../web/swat.c:527 ../web/swat.c:530
+msgid "Basic"
+msgstr ""
+
+#: ../web/swat.c:528 ../web/swat.c:531
+msgid "Advanced"
+msgstr ""
+
+#: ../web/swat.c:529
+msgid "Change View To"
+msgstr ""
+
+#: ../web/swat.c:554
+msgid "Current Config"
+msgstr ""
+
+#: ../web/swat.c:558
+msgid "Normal View"
+msgstr ""
+
+#: ../web/swat.c:560
+msgid "Full View"
+msgstr ""
+
+#. Here we first set and commit all the parameters that were selected
+#. in the previous screen.
+#: ../web/swat.c:579
+msgid "Wizard Parameter Edit Page"
+msgstr ""
+
+#: ../web/swat.c:608
+msgid "Note: smb.conf file has been read and rewritten"
+msgstr ""
+
+#. Here we go ...
+#: ../web/swat.c:716
+msgid "Samba Configuration Wizard"
+msgstr ""
+
+#: ../web/swat.c:720
+msgid "The \"Rewrite smb.conf file\" button will clear the smb.conf file of all default values and of comments."
+msgstr ""
+
+#: ../web/swat.c:721
+msgid "The same will happen if you press the commit button."
+msgstr ""
+
+#: ../web/swat.c:724
+msgid "Rewrite smb.conf file"
+msgstr ""
+
+#: ../web/swat.c:725
+msgid "Commit"
+msgstr ""
+
+#: ../web/swat.c:726
+msgid "Edit Parameter Values"
+msgstr ""
+
+#: ../web/swat.c:732
+msgid "Server Type"
+msgstr ""
+
+#: ../web/swat.c:733
+msgid "Stand Alone"
+msgstr ""
+
+#: ../web/swat.c:734
+msgid "Domain Member"
+msgstr ""
+
+#: ../web/swat.c:735
+msgid "Domain Controller"
+msgstr ""
+
+#: ../web/swat.c:738
+msgid "Unusual Type in smb.conf - Please Select New Mode"
+msgstr ""
+
+#: ../web/swat.c:740
+msgid "Configure WINS As"
+msgstr ""
+
+#: ../web/swat.c:741
+msgid "Not Used"
+msgstr ""
+
+#: ../web/swat.c:742
+msgid "Server for client use"
+msgstr ""
+
+#: ../web/swat.c:743
+msgid "Client of another WINS server"
+msgstr ""
+
+#: ../web/swat.c:745
+msgid "Remote WINS Server"
+msgstr ""
+
+#: ../web/swat.c:756
+msgid "Error: WINS Server Mode and WINS Support both set in smb.conf"
+msgstr ""
+
+#: ../web/swat.c:757
+msgid "Please Select desired WINS mode above."
+msgstr ""
+
+#: ../web/swat.c:759
+msgid "Expose Home Directories"
+msgstr ""
+
+#: ../web/swat.c:774
+msgid "The above configuration options will set multiple parameters and will generally assist with rapid Samba deployment."
+msgstr ""
+
+#: ../web/swat.c:787
+msgid "Global Parameters"
+msgstr ""
+
+#: ../web/swat.c:815 ../web/swat.c:916 ../web/swat.c:1265
+msgid "Commit Changes"
+msgstr ""
+
+#: ../web/swat.c:819 ../web/swat.c:919 ../web/swat.c:1267
+msgid "Reset Values"
+msgstr ""
+
+#: ../web/swat.c:844
+msgid "Share Parameters"
+msgstr ""
+
+#: ../web/swat.c:887
+msgid "Choose Share"
+msgstr ""
+
+#: ../web/swat.c:901
+msgid "Delete Share"
+msgstr ""
+
+#: ../web/swat.c:908
+msgid "Create Share"
+msgstr ""
+
+#: ../web/swat.c:944
+msgid "password change in demo mode rejected"
+msgstr ""
+
+#: ../web/swat.c:957
+msgid "Can't setup password database vectors."
+msgstr ""
+
+#: ../web/swat.c:983
+msgid " Must specify \"User Name\" "
+msgstr ""
+
+#: ../web/swat.c:999
+msgid " Must specify \"Old Password\" "
+msgstr ""
+
+#: ../web/swat.c:1005
+msgid " Must specify \"Remote Machine\" "
+msgstr ""
+
+#: ../web/swat.c:1012
+msgid " Must specify \"New, and Re-typed Passwords\" "
+msgstr ""
+
+#: ../web/swat.c:1018
+msgid " Re-typed password didn't match new password "
+msgstr ""
+
+#: ../web/swat.c:1048
+#, c-format
+msgid " The passwd for '%s' has been changed."
+msgstr ""
+
+#: ../web/swat.c:1051
+#, c-format
+msgid " The passwd for '%s' has NOT been changed."
+msgstr ""
+
+#: ../web/swat.c:1076
+msgid "Server Password Management"
+msgstr ""
+
+#.
+#. * Create all the dialog boxes for data collection
+#.
+#: ../web/swat.c:1085 ../web/swat.c:1132
+msgid "User Name"
+msgstr ""
+
+#: ../web/swat.c:1088 ../web/swat.c:1134
+msgid "Old Password"
+msgstr ""
+
+#: ../web/swat.c:1091 ../web/swat.c:1136
+msgid "New Password"
+msgstr ""
+
+#: ../web/swat.c:1093 ../web/swat.c:1138
+msgid "Re-type New Password"
+msgstr ""
+
+#: ../web/swat.c:1101 ../web/swat.c:1149
+msgid "Change Password"
+msgstr ""
+
+#: ../web/swat.c:1104
+msgid "Add New User"
+msgstr ""
+
+#: ../web/swat.c:1106
+msgid "Delete User"
+msgstr ""
+
+#: ../web/swat.c:1108
+msgid "Disable User"
+msgstr ""
+
+#: ../web/swat.c:1110
+msgid "Enable User"
+msgstr ""
+
+#: ../web/swat.c:1123
+msgid "Client/Server Password Management"
+msgstr ""
+
+#: ../web/swat.c:1140
+msgid "Remote Machine"
+msgstr ""
+
+#: ../web/swat.c:1179
+msgid "Printer Parameters"
+msgstr ""
+
+#: ../web/swat.c:1181
+msgid "Important Note:"
+msgstr ""
+
+#: ../web/swat.c:1182
+msgid "Printer names marked with [*] in the Choose Printer drop-down box "
+msgstr ""
+
+#: ../web/swat.c:1183
+msgid "are autoloaded printers from "
+msgstr ""
+
+#: ../web/swat.c:1184
+msgid "Printcap Name"
+msgstr ""
+
+#: ../web/swat.c:1185
+msgid "Attempting to delete these printers from SWAT will have no effect."
+msgstr ""
+
+#: ../web/swat.c:1231
+msgid "Choose Printer"
+msgstr ""
+
+#: ../web/swat.c:1250
+msgid "Delete Printer"
+msgstr ""
+
+#: ../web/swat.c:1257
+msgid "Create Printer"
+msgstr ""
+
+#: ../web/statuspage.c:123
+msgid "RDONLY "
+msgstr ""
+
+#: ../web/statuspage.c:124
+msgid "WRONLY "
+msgstr ""
+
+#: ../web/statuspage.c:125
+msgid "RDWR "
+msgstr ""
+
+#: ../web/statuspage.c:309
+msgid "Server Status"
+msgstr ""
+
+#: ../web/statuspage.c:314
+msgid "Auto Refresh"
+msgstr ""
+
+#: ../web/statuspage.c:315 ../web/statuspage.c:320
+msgid "Refresh Interval: "
+msgstr ""
+
+#: ../web/statuspage.c:319
+msgid "Stop Refreshing"
+msgstr ""
+
+#: ../web/statuspage.c:334
+msgid "version:"
+msgstr ""
+
+#: ../web/statuspage.c:337
+msgid "smbd:"
+msgstr ""
+
+#: ../web/statuspage.c:337 ../web/statuspage.c:350 ../web/statuspage.c:364
+msgid "running"
+msgstr ""
+
+#: ../web/statuspage.c:337 ../web/statuspage.c:350 ../web/statuspage.c:364
+msgid "not running"
+msgstr ""
+
+#: ../web/statuspage.c:341
+msgid "Stop smbd"
+msgstr ""
+
+#: ../web/statuspage.c:343
+msgid "Start smbd"
+msgstr ""
+
+#: ../web/statuspage.c:345
+msgid "Restart smbd"
+msgstr ""
+
+#: ../web/statuspage.c:350
+msgid "nmbd:"
+msgstr ""
+
+#: ../web/statuspage.c:354
+msgid "Stop nmbd"
+msgstr ""
+
+#: ../web/statuspage.c:356
+msgid "Start nmbd"
+msgstr ""
+
+#: ../web/statuspage.c:358
+msgid "Restart nmbd"
+msgstr ""
+
+#: ../web/statuspage.c:364
+msgid "winbindd:"
+msgstr ""
+
+#: ../web/statuspage.c:368
+msgid "Stop winbindd"
+msgstr ""
+
+#: ../web/statuspage.c:370
+msgid "Start winbindd"
+msgstr ""
+
+#: ../web/statuspage.c:372
+msgid "Restart winbindd"
+msgstr ""
+
+#. stop, restart all
+#: ../web/statuspage.c:381
+msgid "Stop All"
+msgstr ""
+
+#: ../web/statuspage.c:382
+msgid "Restart All"
+msgstr ""
+
+#. start all
+#: ../web/statuspage.c:386
+msgid "Start All"
+msgstr ""
+
+#: ../web/statuspage.c:393
+msgid "Active Connections"
+msgstr ""
+
+#: ../web/statuspage.c:395 ../web/statuspage.c:408 ../web/statuspage.c:416
+msgid "PID"
+msgstr ""
+
+#: ../web/statuspage.c:395 ../web/statuspage.c:408
+msgid "Client"
+msgstr ""
+
+#: ../web/statuspage.c:395
+msgid "IP address"
+msgstr ""
+
+#: ../web/statuspage.c:395 ../web/statuspage.c:408 ../web/statuspage.c:416
+msgid "Date"
+msgstr ""
+
+#: ../web/statuspage.c:397
+msgid "Kill"
+msgstr ""
+
+#: ../web/statuspage.c:405
+msgid "Active Shares"
+msgstr ""
+
+#: ../web/statuspage.c:408
+msgid "Share"
+msgstr ""
+
+#: ../web/statuspage.c:408
+msgid "User"
+msgstr ""
+
+#: ../web/statuspage.c:408
+msgid "Group"
+msgstr ""
+
+#: ../web/statuspage.c:414
+msgid "Open Files"
+msgstr ""
+
+#: ../web/statuspage.c:416
+msgid "Sharing"
+msgstr ""
+
+#: ../web/statuspage.c:416
+msgid "R/W"
+msgstr ""
+
+#: ../web/statuspage.c:416
+msgid "Oplock"
+msgstr ""
+
+#: ../web/statuspage.c:416
+msgid "File"
+msgstr ""
+
+#: ../web/statuspage.c:425
+msgid "Show Client in col 1"
+msgstr ""
+
+#: ../web/statuspage.c:426
+msgid "Show PID in col 1"
+msgstr ""
+
+#: ../param/loadparm.c:755
+msgid "Base Options"
+msgstr ""
+
+#: ../param/loadparm.c:775
+msgid "Security Options"
+msgstr ""
+
+#: ../param/loadparm.c:859
+msgid "Logging Options"
+msgstr ""
+
+#: ../param/loadparm.c:874
+msgid "Protocol Options"
+msgstr ""
+
+#: ../param/loadparm.c:911
+msgid "Tuning Options"
+msgstr ""
+
+#: ../param/loadparm.c:940
+msgid "Printing Options"
+msgstr ""
+
+#: ../param/loadparm.c:970
+msgid "Filename Handling"
+msgstr ""
+
+#: ../param/loadparm.c:996
+msgid "Domain Options"
+msgstr ""
+
+#: ../param/loadparm.c:1000
+msgid "Logon Options"
+msgstr ""
+
+#: ../param/loadparm.c:1019
+msgid "Browse Options"
+msgstr ""
+
+#: ../param/loadparm.c:1033
+msgid "WINS Options"
+msgstr ""
+
+#: ../param/loadparm.c:1043
+msgid "Locking Options"
+msgstr ""
+
+#: ../param/loadparm.c:1061
+msgid "Ldap Options"
+msgstr ""
+
+#: ../param/loadparm.c:1078
+msgid "Miscellaneous Options"
+msgstr ""
+
+#: ../param/loadparm.c:1138
+msgid "VFS module options"
+msgstr ""
+
+#: ../param/loadparm.c:1148
+msgid "Winbind options"
+msgstr ""
diff --git a/source/po/fr.msg b/source/po/fr.msg
new file mode 100644
index 00000000000..134f1d6390b
--- /dev/null
+++ b/source/po/fr.msg
@@ -0,0 +1,593 @@
+# French messages for international release of SWAT.
+# Copyright (C) 2001 François Le Lay <fanch@tuxfamily.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.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: i18n_swat \n"
+"POT-Creation-Date: 2003-10-06 05:30+0900\n"
+"PO-Revision-Date: 2000-02-08 14:45+0100\n"
+"Last-Translator: François Le Lay <fanch@tuxfamily.org>\n"
+"Language-Team: (Samba Team) <samba-technical@samba.org>\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=iso-8859-1\n"
+"Content-Transfer-Encoding: \n"
+
+#: ../web/swat.c:117
+#, c-format
+msgid "ERROR: Can't open %s"
+msgstr ""
+
+#: ../web/swat.c:200
+msgid "Help"
+msgstr "Aide"
+
+#: ../web/swat.c:206 ../web/swat.c:220 ../web/swat.c:235 ../web/swat.c:243 ../web/swat.c:252 ../web/swat.c:261 ../web/swat.c:267 ../web/swat.c:273 ../web/swat.c:286
+msgid "Set Default"
+msgstr "Définir par défaut"
+
+#: ../web/swat.c:408
+#, c-format
+msgid "failed to open %s for writing"
+msgstr ""
+
+#: ../web/swat.c:431
+#, c-format
+msgid "Can't reload %s"
+msgstr ""
+
+#: ../web/swat.c:501
+#, c-format
+msgid "Logged in as <b>%s</b>"
+msgstr "Connecté en tant que <b>%s</b>"
+
+#: ../web/swat.c:505
+msgid "Home"
+msgstr "Home"
+
+#: ../web/swat.c:507
+msgid "Globals"
+msgstr "Paramètres Généraux"
+
+#: ../web/swat.c:508
+msgid "Shares"
+msgstr "Partages"
+
+#: ../web/swat.c:509
+msgid "Printers"
+msgstr "Imprimantes"
+
+#: ../web/swat.c:510
+msgid "Wizard"
+msgstr ""
+
+#: ../web/swat.c:513
+msgid "Status"
+msgstr "Statut"
+
+#: ../web/swat.c:514
+msgid "View Config"
+msgstr "Voir Configuration"
+
+#: ../web/swat.c:516
+msgid "Password Management"
+msgstr "Gestion des mots de passe"
+
+#: ../web/swat.c:526
+msgid "Current View Is"
+msgstr "Configuration Actuelle"
+
+#: ../web/swat.c:527 ../web/swat.c:530
+msgid "Basic"
+msgstr "Vue Basique"
+
+#: ../web/swat.c:528 ../web/swat.c:531
+msgid "Advanced"
+msgstr "Vue Détaillée"
+
+#: ../web/swat.c:529
+msgid "Change View To"
+msgstr "Modifier le mot de passe"
+
+#: ../web/swat.c:554
+msgid "Current Config"
+msgstr "Configuration Actuelle"
+
+#: ../web/swat.c:558
+msgid "Normal View"
+msgstr "Vue Normale"
+
+#: ../web/swat.c:560
+msgid "Full View"
+msgstr "Vue Complète"
+
+#. Here we first set and commit all the parameters that were selected
+#. in the previous screen.
+#: ../web/swat.c:579
+msgid "Wizard Parameter Edit Page"
+msgstr ""
+
+#: ../web/swat.c:608
+msgid "Note: smb.conf file has been read and rewritten"
+msgstr ""
+
+#. Here we go ...
+#: ../web/swat.c:716
+msgid "Samba Configuration Wizard"
+msgstr ""
+
+#: ../web/swat.c:720
+msgid "The \"Rewrite smb.conf file\" button will clear the smb.conf file of all default values and of comments."
+msgstr ""
+
+#: ../web/swat.c:721
+msgid "The same will happen if you press the commit button."
+msgstr ""
+
+#: ../web/swat.c:724
+msgid "Rewrite smb.conf file"
+msgstr ""
+
+#: ../web/swat.c:725
+msgid "Commit"
+msgstr "commentaire"
+
+#: ../web/swat.c:726
+msgid "Edit Parameter Values"
+msgstr "Paramètres Imprimantes"
+
+#: ../web/swat.c:732
+msgid "Server Type"
+msgstr ""
+
+#: ../web/swat.c:733
+msgid "Stand Alone"
+msgstr ""
+
+#: ../web/swat.c:734
+msgid "Domain Member"
+msgstr "master de domaine"
+
+#: ../web/swat.c:735
+msgid "Domain Controller"
+msgstr "master de domaine"
+
+#: ../web/swat.c:738
+msgid "Unusual Type in smb.conf - Please Select New Mode"
+msgstr ""
+
+#: ../web/swat.c:740
+msgid "Configure WINS As"
+msgstr ""
+
+#: ../web/swat.c:741
+msgid "Not Used"
+msgstr "ne pas descendre"
+
+#: ../web/swat.c:742
+msgid "Server for client use"
+msgstr ""
+
+#: ../web/swat.c:743
+msgid "Client of another WINS server"
+msgstr ""
+
+#: ../web/swat.c:745
+msgid "Remote WINS Server"
+msgstr ""
+
+#: ../web/swat.c:756
+msgid "Error: WINS Server Mode and WINS Support both set in smb.conf"
+msgstr ""
+
+#: ../web/swat.c:757
+msgid "Please Select desired WINS mode above."
+msgstr ""
+
+#: ../web/swat.c:759
+msgid "Expose Home Directories"
+msgstr ""
+
+#: ../web/swat.c:774
+msgid "The above configuration options will set multiple parameters and will generally assist with rapid Samba deployment."
+msgstr ""
+
+#: ../web/swat.c:787
+msgid "Global Parameters"
+msgstr "Variables Globales"
+
+#: ../web/swat.c:815 ../web/swat.c:916 ../web/swat.c:1265
+msgid "Commit Changes"
+msgstr "Sauver les modifications"
+
+#: ../web/swat.c:819 ../web/swat.c:919 ../web/swat.c:1267
+msgid "Reset Values"
+msgstr "Réinitialiser Valeurs"
+
+#: ../web/swat.c:844
+msgid "Share Parameters"
+msgstr "Paramètres de partage"
+
+#: ../web/swat.c:887
+msgid "Choose Share"
+msgstr "Choisir un partage"
+
+#: ../web/swat.c:901
+msgid "Delete Share"
+msgstr "Supprimer un partage"
+
+#: ../web/swat.c:908
+msgid "Create Share"
+msgstr "Créer un partage"
+
+#: ../web/swat.c:944
+msgid "password change in demo mode rejected"
+msgstr "changement de mot de passe en mode démo rejeté"
+
+#: ../web/swat.c:957
+msgid "Can't setup password database vectors."
+msgstr ""
+
+#: ../web/swat.c:983
+msgid " Must specify \"User Name\" "
+msgstr " Le champ \"Nom d'utilisateur\" doit être spécifié"
+
+#: ../web/swat.c:999
+msgid " Must specify \"Old Password\" "
+msgstr " Le champ \"Ancien mot de passe\" doît être spécifié"
+
+#: ../web/swat.c:1005
+msgid " Must specify \"Remote Machine\" "
+msgstr " Le champ \"Machine Distante\" doît être spécifié"
+
+#: ../web/swat.c:1012
+msgid " Must specify \"New, and Re-typed Passwords\" "
+msgstr "Les champs \"Nouveau mot de passe\" et \"Confirmation du nouveau mot de passe\" doivent être spécifiés \n"
+
+#: ../web/swat.c:1018
+msgid " Re-typed password didn't match new password "
+msgstr " Echec de la confirmation du nouveau mot de passe"
+
+#: ../web/swat.c:1048
+#, c-format
+msgid " The passwd for '%s' has been changed."
+msgstr " Le mot de passe de '%s' a été modifié. "
+
+#: ../web/swat.c:1051
+#, c-format
+msgid " The passwd for '%s' has NOT been changed."
+msgstr " Le mot de passe de '%s' n'a PAS été modifié. \n"
+
+#: ../web/swat.c:1076
+msgid "Server Password Management"
+msgstr "Gestion des mots de passe serveur"
+
+#.
+#. * Create all the dialog boxes for data collection
+#.
+#: ../web/swat.c:1085 ../web/swat.c:1132
+msgid "User Name"
+msgstr " Nom d'utilisateur : "
+
+#: ../web/swat.c:1088 ../web/swat.c:1134
+msgid "Old Password"
+msgstr " Ancien mot de passe : "
+
+#: ../web/swat.c:1091 ../web/swat.c:1136
+msgid "New Password"
+msgstr " Nouveau mot de passe : "
+
+#: ../web/swat.c:1093 ../web/swat.c:1138
+msgid "Re-type New Password"
+msgstr " Confirmation du nouveau mot de passe : "
+
+#: ../web/swat.c:1101 ../web/swat.c:1149
+msgid "Change Password"
+msgstr "Modifier le mot de passe"
+
+#: ../web/swat.c:1104
+msgid "Add New User"
+msgstr "Nouvel Utilisateur"
+
+#: ../web/swat.c:1106
+msgid "Delete User"
+msgstr "Supprimer Utilisateur"
+
+#: ../web/swat.c:1108
+msgid "Disable User"
+msgstr "Désactiver Utilisateur"
+
+#: ../web/swat.c:1110
+msgid "Enable User"
+msgstr "Activer Utilisateur"
+
+#: ../web/swat.c:1123
+msgid "Client/Server Password Management"
+msgstr "Gestion des mots de passe Client/Serveur"
+
+#: ../web/swat.c:1140
+msgid "Remote Machine"
+msgstr " Machine distante : "
+
+#: ../web/swat.c:1179
+msgid "Printer Parameters"
+msgstr "Paramètres Imprimantes"
+
+#: ../web/swat.c:1181
+msgid "Important Note:"
+msgstr "Note Importante:"
+
+#: ../web/swat.c:1182
+msgid "Printer names marked with [*] in the Choose Printer drop-down box "
+msgstr "Les Noms d'imprimantes marqués du signe [*] dans le menu déroulant Choisir Imprimante"
+
+#: ../web/swat.c:1183
+msgid "are autoloaded printers from "
+msgstr "désignent des imprimantes automatiquement chargées depuis le "
+
+#: ../web/swat.c:1184
+msgid "Printcap Name"
+msgstr "Nom Printcap"
+
+#: ../web/swat.c:1185
+msgid "Attempting to delete these printers from SWAT will have no effect."
+msgstr "Essayer de supprimer ces imprimantes depuis SWAT n'aura aucun effet."
+
+#: ../web/swat.c:1231
+msgid "Choose Printer"
+msgstr "Choisir Imprimante"
+
+#: ../web/swat.c:1250
+msgid "Delete Printer"
+msgstr "Supprimer Imprimante"
+
+#: ../web/swat.c:1257
+msgid "Create Printer"
+msgstr "Créer Imprimante"
+
+#: ../web/statuspage.c:123
+msgid "RDONLY "
+msgstr ""
+
+#: ../web/statuspage.c:124
+msgid "WRONLY "
+msgstr ""
+
+#: ../web/statuspage.c:125
+msgid "RDWR "
+msgstr ""
+
+#: ../web/statuspage.c:309
+msgid "Server Status"
+msgstr "Statut du Serveur"
+
+#: ../web/statuspage.c:314
+msgid "Auto Refresh"
+msgstr "Rafraîchissement Automatique"
+
+#: ../web/statuspage.c:315 ../web/statuspage.c:320
+msgid "Refresh Interval: "
+msgstr "Intervalle de rafraîchissement: "
+
+#: ../web/statuspage.c:319
+msgid "Stop Refreshing"
+msgstr "Stopper Rafraîchissement"
+
+#: ../web/statuspage.c:334
+msgid "version:"
+msgstr "version:"
+
+#: ../web/statuspage.c:337
+msgid "smbd:"
+msgstr ""
+
+#: ../web/statuspage.c:337 ../web/statuspage.c:350 ../web/statuspage.c:364
+msgid "running"
+msgstr "actif"
+
+#: ../web/statuspage.c:337 ../web/statuspage.c:350 ../web/statuspage.c:364
+msgid "not running"
+msgstr "non actif"
+
+#: ../web/statuspage.c:341
+msgid "Stop smbd"
+msgstr "Stopper smbd"
+
+#: ../web/statuspage.c:343
+msgid "Start smbd"
+msgstr "Lancer smbd"
+
+#: ../web/statuspage.c:345
+msgid "Restart smbd"
+msgstr "Relancer smbd"
+
+#: ../web/statuspage.c:350
+msgid "nmbd:"
+msgstr ""
+
+#: ../web/statuspage.c:354
+msgid "Stop nmbd"
+msgstr "Stopper nmbd"
+
+#: ../web/statuspage.c:356
+msgid "Start nmbd"
+msgstr "Lancer nmbd"
+
+#: ../web/statuspage.c:358
+msgid "Restart nmbd"
+msgstr "Relancer nmbd"
+
+#: ../web/statuspage.c:364
+msgid "winbindd:"
+msgstr ""
+
+#: ../web/statuspage.c:368
+msgid "Stop winbindd"
+msgstr "Stopper nmbd"
+
+#: ../web/statuspage.c:370
+msgid "Start winbindd"
+msgstr "Lancer nmbd"
+
+#: ../web/statuspage.c:372
+msgid "Restart winbindd"
+msgstr "Relancer nmbd"
+
+#. stop, restart all
+#: ../web/statuspage.c:381
+msgid "Stop All"
+msgstr ""
+
+#: ../web/statuspage.c:382
+msgid "Restart All"
+msgstr "Relancer nmbd"
+
+#. start all
+#: ../web/statuspage.c:386
+msgid "Start All"
+msgstr "Lancer nmbd"
+
+#: ../web/statuspage.c:393
+msgid "Active Connections"
+msgstr "Connections Actives"
+
+#: ../web/statuspage.c:395 ../web/statuspage.c:408 ../web/statuspage.c:416
+msgid "PID"
+msgstr ""
+
+#: ../web/statuspage.c:395 ../web/statuspage.c:408
+msgid "Client"
+msgstr ""
+
+#: ../web/statuspage.c:395
+msgid "IP address"
+msgstr "adresse IP"
+
+#: ../web/statuspage.c:395 ../web/statuspage.c:408 ../web/statuspage.c:416
+msgid "Date"
+msgstr "Date"
+
+#: ../web/statuspage.c:397
+msgid "Kill"
+msgstr "Terminer"
+
+#: ../web/statuspage.c:405
+msgid "Active Shares"
+msgstr "Partages Actifs"
+
+#: ../web/statuspage.c:408
+msgid "Share"
+msgstr "Partager"
+
+#: ../web/statuspage.c:408
+msgid "User"
+msgstr "Utilisateur"
+
+#: ../web/statuspage.c:408
+msgid "Group"
+msgstr "Groupe"
+
+#: ../web/statuspage.c:414
+msgid "Open Files"
+msgstr "Fichiers Ouverts"
+
+#: ../web/statuspage.c:416
+msgid "Sharing"
+msgstr ""
+
+#: ../web/statuspage.c:416
+msgid "R/W"
+msgstr ""
+
+#: ../web/statuspage.c:416
+msgid "Oplock"
+msgstr ""
+
+#: ../web/statuspage.c:416
+msgid "File"
+msgstr "Fichier"
+
+#: ../web/statuspage.c:425
+msgid "Show Client in col 1"
+msgstr ""
+
+#: ../web/statuspage.c:426
+msgid "Show PID in col 1"
+msgstr ""
+
+#: ../param/loadparm.c:755
+msgid "Base Options"
+msgstr "Options de base"
+
+#: ../param/loadparm.c:775
+msgid "Security Options"
+msgstr "Options de Sécurité"
+
+#: ../param/loadparm.c:859
+msgid "Logging Options"
+msgstr "Options de Logging"
+
+#: ../param/loadparm.c:874
+msgid "Protocol Options"
+msgstr "Options de Protocole"
+
+#: ../param/loadparm.c:911
+msgid "Tuning Options"
+msgstr "Options de réglage"
+
+#: ../param/loadparm.c:940
+msgid "Printing Options"
+msgstr "Options d'impression"
+
+#: ../param/loadparm.c:970
+msgid "Filename Handling"
+msgstr "Gestion des noms de fichier"
+
+#: ../param/loadparm.c:996
+msgid "Domain Options"
+msgstr "Options de Domaine"
+
+#: ../param/loadparm.c:1000
+msgid "Logon Options"
+msgstr "Options de Logon"
+
+#: ../param/loadparm.c:1019
+msgid "Browse Options"
+msgstr "Options de Navigation"
+
+#: ../param/loadparm.c:1033
+msgid "WINS Options"
+msgstr "Options WINS"
+
+#: ../param/loadparm.c:1043
+msgid "Locking Options"
+msgstr "Options de Verrouillage"
+
+#: ../param/loadparm.c:1061
+msgid "Ldap Options"
+msgstr "Options Ldap"
+
+#: ../param/loadparm.c:1078
+msgid "Miscellaneous Options"
+msgstr "Options Diverses"
+
+#: ../param/loadparm.c:1138
+msgid "VFS module options"
+msgstr "Options VFS"
+
+#: ../param/loadparm.c:1148
+msgid "Winbind options"
+msgstr "Options Winbind"
diff --git a/source/po/genmsg b/source/po/genmsg
new file mode 100755
index 00000000000..08d5bd222f8
--- /dev/null
+++ b/source/po/genmsg
@@ -0,0 +1,40 @@
+#!/bin/sh
+# Copyright (C) 2003 TAKAHASHI Motonobu <monyo@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.
+#
+
+FILES='../web/swat.c ../web/statuspage.c ../param/loadparm.c'
+LANGS='en ja tr pl fr de it'
+
+XGETTEXT=xgettext
+MSGMERGE=msgmerge
+
+WIDTH=256
+
+$XGETTEXT --default-domain="i18n_swat" \
+ --add-comments \
+ --keyword=_ --keyword=N_ \
+ --width=${WIDTH} \
+ $FILES
+
+for lang in $LANGS; do
+ echo -n $lang
+ mv ${lang}.msg ${lang}.msg.old
+ $MSGMERGE --width=${WIDTH} ${lang}.msg.old i18n_swat.po -o ${lang}.msg
+done
+
+rm i18n_swat.po
+
diff --git a/source/po/it.msg b/source/po/it.msg
new file mode 100644
index 00000000000..be447a84d47
--- /dev/null
+++ b/source/po/it.msg
@@ -0,0 +1,593 @@
+# Italian messages for international release of SWAT.
+# Copyright (C) 2001 Simo Sorce <idra@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.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: i18n_swat \n"
+"POT-Creation-Date: 2003-10-06 05:30+0900\n"
+"PO-Revision-Date: 2000-02-08 14:45+0100\n"
+"Last-Translator: Simo Sorce <idra@samba.org>\n"
+"Language-Team: (Samba Team) <samba-technical@samba.org>\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=iso-8859-1\n"
+"Content-Transfer-Encoding: \n"
+
+#: ../web/swat.c:117
+#, c-format
+msgid "ERROR: Can't open %s"
+msgstr ""
+
+#: ../web/swat.c:200
+msgid "Help"
+msgstr "Aiuto"
+
+#: ../web/swat.c:206 ../web/swat.c:220 ../web/swat.c:235 ../web/swat.c:243 ../web/swat.c:252 ../web/swat.c:261 ../web/swat.c:267 ../web/swat.c:273 ../web/swat.c:286
+msgid "Set Default"
+msgstr "Imposta Default"
+
+#: ../web/swat.c:408
+#, c-format
+msgid "failed to open %s for writing"
+msgstr ""
+
+#: ../web/swat.c:431
+#, c-format
+msgid "Can't reload %s"
+msgstr ""
+
+#: ../web/swat.c:501
+#, c-format
+msgid "Logged in as <b>%s</b>"
+msgstr "Connesso come <b>%s</b><p>\n"
+
+#: ../web/swat.c:505
+msgid "Home"
+msgstr "Home"
+
+#: ../web/swat.c:507
+msgid "Globals"
+msgstr "Globali"
+
+#: ../web/swat.c:508
+msgid "Shares"
+msgstr "Condivisioni"
+
+#: ../web/swat.c:509
+msgid "Printers"
+msgstr "Stampanti"
+
+#: ../web/swat.c:510
+msgid "Wizard"
+msgstr ""
+
+#: ../web/swat.c:513
+msgid "Status"
+msgstr "Stato"
+
+#: ../web/swat.c:514
+msgid "View Config"
+msgstr "Visualizza Configurazione"
+
+#: ../web/swat.c:516
+msgid "Password Management"
+msgstr "Gestione Password"
+
+#: ../web/swat.c:526
+msgid "Current View Is"
+msgstr "Configurazione Attuale"
+
+#: ../web/swat.c:527 ../web/swat.c:530
+msgid "Basic"
+msgstr "Vista Semplice"
+
+#: ../web/swat.c:528 ../web/swat.c:531
+msgid "Advanced"
+msgstr "Vista Avanzata"
+
+#: ../web/swat.c:529
+msgid "Change View To"
+msgstr "Cambia Password"
+
+#: ../web/swat.c:554
+msgid "Current Config"
+msgstr "Configurazione Attuale"
+
+#: ../web/swat.c:558
+msgid "Normal View"
+msgstr "Vista Normale"
+
+#: ../web/swat.c:560
+msgid "Full View"
+msgstr "Vista Completa"
+
+#. Here we first set and commit all the parameters that were selected
+#. in the previous screen.
+#: ../web/swat.c:579
+msgid "Wizard Parameter Edit Page"
+msgstr ""
+
+#: ../web/swat.c:608
+msgid "Note: smb.conf file has been read and rewritten"
+msgstr ""
+
+#. Here we go ...
+#: ../web/swat.c:716
+msgid "Samba Configuration Wizard"
+msgstr ""
+
+#: ../web/swat.c:720
+msgid "The \"Rewrite smb.conf file\" button will clear the smb.conf file of all default values and of comments."
+msgstr ""
+
+#: ../web/swat.c:721
+msgid "The same will happen if you press the commit button."
+msgstr ""
+
+#: ../web/swat.c:724
+msgid "Rewrite smb.conf file"
+msgstr ""
+
+#: ../web/swat.c:725
+msgid "Commit"
+msgstr "commento"
+
+#: ../web/swat.c:726
+msgid "Edit Parameter Values"
+msgstr "Parametri Stampante"
+
+#: ../web/swat.c:732
+msgid "Server Type"
+msgstr ""
+
+#: ../web/swat.c:733
+msgid "Stand Alone"
+msgstr ""
+
+#: ../web/swat.c:734
+msgid "Domain Member"
+msgstr "master dominio"
+
+#: ../web/swat.c:735
+msgid "Domain Controller"
+msgstr "master dominio"
+
+#: ../web/swat.c:738
+msgid "Unusual Type in smb.conf - Please Select New Mode"
+msgstr ""
+
+#: ../web/swat.c:740
+msgid "Configure WINS As"
+msgstr ""
+
+#: ../web/swat.c:741
+msgid "Not Used"
+msgstr "non discendere"
+
+#: ../web/swat.c:742
+msgid "Server for client use"
+msgstr ""
+
+#: ../web/swat.c:743
+msgid "Client of another WINS server"
+msgstr ""
+
+#: ../web/swat.c:745
+msgid "Remote WINS Server"
+msgstr ""
+
+#: ../web/swat.c:756
+msgid "Error: WINS Server Mode and WINS Support both set in smb.conf"
+msgstr ""
+
+#: ../web/swat.c:757
+msgid "Please Select desired WINS mode above."
+msgstr ""
+
+#: ../web/swat.c:759
+msgid "Expose Home Directories"
+msgstr ""
+
+#: ../web/swat.c:774
+msgid "The above configuration options will set multiple parameters and will generally assist with rapid Samba deployment."
+msgstr ""
+
+#: ../web/swat.c:787
+msgid "Global Parameters"
+msgstr "Variabili Globali"
+
+#: ../web/swat.c:815 ../web/swat.c:916 ../web/swat.c:1265
+msgid "Commit Changes"
+msgstr "Salva Modifiche"
+
+#: ../web/swat.c:819 ../web/swat.c:919 ../web/swat.c:1267
+msgid "Reset Values"
+msgstr "Resetta Valori"
+
+#: ../web/swat.c:844
+msgid "Share Parameters"
+msgstr "Parametri Condivisioni"
+
+#: ../web/swat.c:887
+msgid "Choose Share"
+msgstr "Scegli Condivisione"
+
+#: ../web/swat.c:901
+msgid "Delete Share"
+msgstr "Cancella Condivisione"
+
+#: ../web/swat.c:908
+msgid "Create Share"
+msgstr "Crea Condivisione"
+
+#: ../web/swat.c:944
+msgid "password change in demo mode rejected"
+msgstr "cambio password in modalita' demo rigettata"
+
+#: ../web/swat.c:957
+msgid "Can't setup password database vectors."
+msgstr ""
+
+#: ../web/swat.c:983
+msgid " Must specify \"User Name\" "
+msgstr " \"Nome Utente\" deve essere specificato "
+
+#: ../web/swat.c:999
+msgid " Must specify \"Old Password\" "
+msgstr " \"Vecchia Password\" deve essere specificato "
+
+#: ../web/swat.c:1005
+msgid " Must specify \"Remote Machine\" "
+msgstr " \"Macchina Remota\" deve essere specificato "
+
+#: ../web/swat.c:1012
+msgid " Must specify \"New, and Re-typed Passwords\" "
+msgstr " \"Nuova/Conferma Password\" devono essere specificati "
+
+#: ../web/swat.c:1018
+msgid " Re-typed password didn't match new password "
+msgstr " la password di conferma non e' uguale alla nuova password "
+
+#: ../web/swat.c:1048
+#, c-format
+msgid " The passwd for '%s' has been changed."
+msgstr " La password per '%s' e' stata cambiata."
+
+#: ../web/swat.c:1051
+#, c-format
+msgid " The passwd for '%s' has NOT been changed."
+msgstr " La password per '%s' non e' stata cambianta."
+
+#: ../web/swat.c:1076
+msgid "Server Password Management"
+msgstr "Gestione Password del Server"
+
+#.
+#. * Create all the dialog boxes for data collection
+#.
+#: ../web/swat.c:1085 ../web/swat.c:1132
+msgid "User Name"
+msgstr " Nome Utente"
+
+#: ../web/swat.c:1088 ../web/swat.c:1134
+msgid "Old Password"
+msgstr " Vecchia Password"
+
+#: ../web/swat.c:1091 ../web/swat.c:1136
+msgid "New Password"
+msgstr " Nuova Password"
+
+#: ../web/swat.c:1093 ../web/swat.c:1138
+msgid "Re-type New Password"
+msgstr " Conferma nuova Password"
+
+#: ../web/swat.c:1101 ../web/swat.c:1149
+msgid "Change Password"
+msgstr "Cambia Password"
+
+#: ../web/swat.c:1104
+msgid "Add New User"
+msgstr "Aggiungi Nuovo Utente"
+
+#: ../web/swat.c:1106
+msgid "Delete User"
+msgstr "Cancella Utente"
+
+#: ../web/swat.c:1108
+msgid "Disable User"
+msgstr "Disabilita Utente"
+
+#: ../web/swat.c:1110
+msgid "Enable User"
+msgstr "Abilita Utente"
+
+#: ../web/swat.c:1123
+msgid "Client/Server Password Management"
+msgstr "Gestione Password Client/Server"
+
+#: ../web/swat.c:1140
+msgid "Remote Machine"
+msgstr " Macchina Remota"
+
+#: ../web/swat.c:1179
+msgid "Printer Parameters"
+msgstr "Parametri Stampante"
+
+#: ../web/swat.c:1181
+msgid "Important Note:"
+msgstr "Nota Importante:"
+
+#: ../web/swat.c:1182
+msgid "Printer names marked with [*] in the Choose Printer drop-down box "
+msgstr "nomi di stampante marcati con [*] nel riquadro a scomparsa Scegli Stampante"
+
+#: ../web/swat.c:1183
+msgid "are autoloaded printers from "
+msgstr "sono stampanti caricate automaticamente da "
+
+#: ../web/swat.c:1184
+msgid "Printcap Name"
+msgstr "Nome Printcap"
+
+#: ../web/swat.c:1185
+msgid "Attempting to delete these printers from SWAT will have no effect."
+msgstr "Il tentativo di cancellare queste stampanti da sWAT non avara' effetto.\n"
+
+#: ../web/swat.c:1231
+msgid "Choose Printer"
+msgstr "Scegli Stampante"
+
+#: ../web/swat.c:1250
+msgid "Delete Printer"
+msgstr "Cancella Stampante"
+
+#: ../web/swat.c:1257
+msgid "Create Printer"
+msgstr "Crea Stampante"
+
+#: ../web/statuspage.c:123
+msgid "RDONLY "
+msgstr ""
+
+#: ../web/statuspage.c:124
+msgid "WRONLY "
+msgstr ""
+
+#: ../web/statuspage.c:125
+msgid "RDWR "
+msgstr ""
+
+#: ../web/statuspage.c:309
+msgid "Server Status"
+msgstr "Stato del Server"
+
+#: ../web/statuspage.c:314
+msgid "Auto Refresh"
+msgstr "Rinfresco Automatico"
+
+#: ../web/statuspage.c:315 ../web/statuspage.c:320
+msgid "Refresh Interval: "
+msgstr "Intervallo Rinfresco: "
+
+#: ../web/statuspage.c:319
+msgid "Stop Refreshing"
+msgstr "Ferma Rinfresco"
+
+#: ../web/statuspage.c:334
+msgid "version:"
+msgstr "versione:"
+
+#: ../web/statuspage.c:337
+msgid "smbd:"
+msgstr ""
+
+#: ../web/statuspage.c:337 ../web/statuspage.c:350 ../web/statuspage.c:364
+msgid "running"
+msgstr "attivo"
+
+#: ../web/statuspage.c:337 ../web/statuspage.c:350 ../web/statuspage.c:364
+msgid "not running"
+msgstr "non attivo"
+
+#: ../web/statuspage.c:341
+msgid "Stop smbd"
+msgstr "Ferma smbd"
+
+#: ../web/statuspage.c:343
+msgid "Start smbd"
+msgstr "Lancia smbd"
+
+#: ../web/statuspage.c:345
+msgid "Restart smbd"
+msgstr "Rilancia smbd"
+
+#: ../web/statuspage.c:350
+msgid "nmbd:"
+msgstr ""
+
+#: ../web/statuspage.c:354
+msgid "Stop nmbd"
+msgstr "Ferma nmbd"
+
+#: ../web/statuspage.c:356
+msgid "Start nmbd"
+msgstr "Lancia nmbd"
+
+#: ../web/statuspage.c:358
+msgid "Restart nmbd"
+msgstr "Rilancia nmbd"
+
+#: ../web/statuspage.c:364
+msgid "winbindd:"
+msgstr ""
+
+#: ../web/statuspage.c:368
+msgid "Stop winbindd"
+msgstr "Ferma nmbd"
+
+#: ../web/statuspage.c:370
+msgid "Start winbindd"
+msgstr "Lancia nmbd"
+
+#: ../web/statuspage.c:372
+msgid "Restart winbindd"
+msgstr "Rilancia nmbd"
+
+#. stop, restart all
+#: ../web/statuspage.c:381
+msgid "Stop All"
+msgstr ""
+
+#: ../web/statuspage.c:382
+msgid "Restart All"
+msgstr "Rilancia nmbd"
+
+#. start all
+#: ../web/statuspage.c:386
+msgid "Start All"
+msgstr "Lancia nmbd"
+
+#: ../web/statuspage.c:393
+msgid "Active Connections"
+msgstr "Connessioni Attive"
+
+#: ../web/statuspage.c:395 ../web/statuspage.c:408 ../web/statuspage.c:416
+msgid "PID"
+msgstr ""
+
+#: ../web/statuspage.c:395 ../web/statuspage.c:408
+msgid "Client"
+msgstr ""
+
+#: ../web/statuspage.c:395
+msgid "IP address"
+msgstr "indirizzo IP"
+
+#: ../web/statuspage.c:395 ../web/statuspage.c:408 ../web/statuspage.c:416
+msgid "Date"
+msgstr "Data"
+
+#: ../web/statuspage.c:397
+msgid "Kill"
+msgstr "Termina"
+
+#: ../web/statuspage.c:405
+msgid "Active Shares"
+msgstr "Condivisioni Attive"
+
+#: ../web/statuspage.c:408
+msgid "Share"
+msgstr "Condivisione"
+
+#: ../web/statuspage.c:408
+msgid "User"
+msgstr "Utente"
+
+#: ../web/statuspage.c:408
+msgid "Group"
+msgstr "Gruppo"
+
+#: ../web/statuspage.c:414
+msgid "Open Files"
+msgstr "File Aperti"
+
+#: ../web/statuspage.c:416
+msgid "Sharing"
+msgstr ""
+
+#: ../web/statuspage.c:416
+msgid "R/W"
+msgstr ""
+
+#: ../web/statuspage.c:416
+msgid "Oplock"
+msgstr ""
+
+#: ../web/statuspage.c:416
+msgid "File"
+msgstr ""
+
+#: ../web/statuspage.c:425
+msgid "Show Client in col 1"
+msgstr ""
+
+#: ../web/statuspage.c:426
+msgid "Show PID in col 1"
+msgstr ""
+
+#: ../param/loadparm.c:755
+msgid "Base Options"
+msgstr "Opzioni Basilari"
+
+#: ../param/loadparm.c:775
+msgid "Security Options"
+msgstr "Opzioni di Sicurezza"
+
+#: ../param/loadparm.c:859
+msgid "Logging Options"
+msgstr "Opzioni di Log"
+
+#: ../param/loadparm.c:874
+msgid "Protocol Options"
+msgstr "Opzioni Protocollo"
+
+#: ../param/loadparm.c:911
+msgid "Tuning Options"
+msgstr "Opzioni Tuning"
+
+#: ../param/loadparm.c:940
+msgid "Printing Options"
+msgstr "Opzioni di Stampa"
+
+#: ../param/loadparm.c:970
+msgid "Filename Handling"
+msgstr "Gestione Nomi File"
+
+#: ../param/loadparm.c:996
+msgid "Domain Options"
+msgstr "Opzioni Dominio"
+
+#: ../param/loadparm.c:1000
+msgid "Logon Options"
+msgstr "Opzioni di Logon"
+
+#: ../param/loadparm.c:1019
+msgid "Browse Options"
+msgstr "Opzioni Browsing"
+
+#: ../param/loadparm.c:1033
+msgid "WINS Options"
+msgstr "opzioni WINS"
+
+#: ../param/loadparm.c:1043
+msgid "Locking Options"
+msgstr "Opzioni Locking"
+
+#: ../param/loadparm.c:1061
+msgid "Ldap Options"
+msgstr "Opzioni Ldap"
+
+#: ../param/loadparm.c:1078
+msgid "Miscellaneous Options"
+msgstr "Opzioni Generiche"
+
+#: ../param/loadparm.c:1138
+msgid "VFS module options"
+msgstr "Opzioni VFS"
+
+#: ../param/loadparm.c:1148
+msgid "Winbind options"
+msgstr "Opzioni Winbind"
diff --git a/source/po/ja.msg b/source/po/ja.msg
new file mode 100644
index 00000000000..affb2764141
--- /dev/null
+++ b/source/po/ja.msg
@@ -0,0 +1,595 @@
+# Japanese messages for international release of SWAT.
+# Copyright (C) 2003 TAKAHASHI Motonobu <monyo@samba.org>
+# Copyright (C) 2000 Ryo Kawahara <rkawa@lbe.co.jp>
+#
+# This program is free software; you can 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.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: i18n-swat\n"
+"POT-Creation-Date: 2003-10-06 05:30+0900\n"
+"PO-Revision-Date: 2003-09-23 04:38+900\n"
+"Last-Translator: TAKAHASHI Motonobu <monyo@samba.org>\n"
+"Language-Team: Samba Users Group Japan <sugj-tech@samba.gr.jp>\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=CP932\n"
+"Content-Transfer-Encoding: 8bit\n"
+
+#: ../web/swat.c:117
+#, c-format
+msgid "ERROR: Can't open %s"
+msgstr "%s ‚ƒI[ƒvƒ“‚Ċ‚Ğ‚Ü‚ı‚ñ"
+
+#: ../web/swat.c:200
+msgid "Help"
+msgstr "ƒwƒ‹ƒv"
+
+#: ../web/swat.c:206 ../web/swat.c:220 ../web/swat.c:235 ../web/swat.c:243 ../web/swat.c:252 ../web/swat.c:261 ../web/swat.c:267 ../web/swat.c:273 ../web/swat.c:286
+msgid "Set Default"
+msgstr "ƒfƒtƒHƒ‹ƒg’l"
+
+#: ../web/swat.c:408
+#, c-format
+msgid "failed to open %s for writing"
+msgstr "%s ‚‘‚Ѝž‚Ŭ—p‚ɃI[ƒvƒ“‚Ċ‚Ğ‚Ü‚ı‚ñ"
+
+#: ../web/swat.c:431
+#, c-format
+msgid "Can't reload %s"
+msgstr "%s ‚Ä“Ç‚Ŭž‚Ŭ‚Ċ‚Ğ‚Ü‚ı‚ñ\n"
+
+# msgid "Logged in as <b>%s</b><p>\n"
+#: ../web/swat.c:501
+#, c-format
+msgid "Logged in as <b>%s</b>"
+msgstr "<b>%s</b>‚Ĉ‚µ‚ăƒOƒCƒ“"
+
+#: ../web/swat.c:505
+msgid "Home"
+msgstr "ƒz[ƒ€"
+
+#: ../web/swat.c:507
+msgid "Globals"
+msgstr "ƒOƒ[ƒoƒ‹"
+
+#: ../web/swat.c:508
+msgid "Shares"
+msgstr "ƒtƒ@ƒCƒ‹‹¤—L"
+
+#: ../web/swat.c:509
+msgid "Printers"
+msgstr "ˆóü‹¤—L"
+
+#: ../web/swat.c:510
+msgid "Wizard"
+msgstr "ƒEƒBƒU[ƒh"
+
+#: ../web/swat.c:513
+msgid "Status"
+msgstr "ƒT[ƒo‚̏ó‘Ô"
+
+#: ../web/swat.c:514
+msgid "View Config"
+msgstr "ŒğŬ‚̐Ŭ’è"
+
+#: ../web/swat.c:516
+msgid "Password Management"
+msgstr "ƒpƒXƒ[ƒhŠÇ—"
+
+#: ../web/swat.c:526
+msgid "Current View Is"
+msgstr "ŒğŬ‚Ì•\ŽĤƒ‚[ƒh"
+
+#: ../web/swat.c:527 ../web/swat.c:530
+msgid "Basic"
+msgstr "•W€•\ŽĤ"
+
+#: ../web/swat.c:528 ../web/swat.c:531
+msgid "Advanced"
+msgstr "Ú×•\ŽĤ"
+
+#: ../web/swat.c:529
+msgid "Change View To"
+msgstr "•\ŽĤƒ‚[ƒh‚̕ύX"
+
+#: ../web/swat.c:554
+msgid "Current Config"
+msgstr "ŒğŬ‚̐Ŭ’è"
+
+#: ../web/swat.c:558
+msgid "Normal View"
+msgstr "•W€•\ŽĤ"
+
+#: ../web/swat.c:560
+msgid "Full View"
+msgstr "Š‘S•\ŽĤ"
+
+#. Here we first set and commit all the parameters that were selected
+#. in the previous screen.
+#: ../web/swat.c:579
+msgid "Wizard Parameter Edit Page"
+msgstr "ƒEƒBƒU[ƒh‚É‚ĉ‚éƒpƒ‰ƒ[ƒ^•ÒWƒy[ƒW"
+
+#: ../web/swat.c:608
+msgid "Note: smb.conf file has been read and rewritten"
+msgstr "smb.conf ƒtƒ@ƒCƒ‹‚ޏ‘‚ĞŠ·‚Ĥ‚ç‚ê‚Ü‚µ‚½B"
+
+#. Here we go ...
+#: ../web/swat.c:716
+msgid "Samba Configuration Wizard"
+msgstr "Samba Ŭ’èƒEƒBƒU[ƒh"
+
+#: ../web/swat.c:720
+msgid "The \"Rewrite smb.conf file\" button will clear the smb.conf file of all default values and of comments."
+msgstr "usmb.conf ‚̏‘Š·vƒ{ƒ^ƒ“‚‰Ÿ‚·‚Ĉ smb.conf ƒtƒ@ƒCƒ‹’†‚Ì‚·‚ׂẴfƒtƒHƒ‹ƒg’l‚âƒRƒƒ“ƒg‚͍폜‚³‚ê‚Ü‚·B"
+
+#: ../web/swat.c:721
+msgid "The same will happen if you press the commit button."
+msgstr "ucommitvƒ{ƒ^ƒ“‚‰Ÿ‚µ‚½ê‡‚É‚à“Ż—l‚̕ύX‚ލs‚È‚í‚ê‚Ü‚·B"
+
+#: ../web/swat.c:724
+msgid "Rewrite smb.conf file"
+msgstr "smb.conf ‚̏‘Š·"
+
+#: ../web/swat.c:725
+msgid "Commit"
+msgstr "Ŭ’è‚”½‰f"
+
+#: ../web/swat.c:726
+msgid "Edit Parameter Values"
+msgstr "Šeƒpƒ‰ƒ[ƒ^‚̕ҏW"
+
+#: ../web/swat.c:732
+msgid "Server Type"
+msgstr "ƒT[ƒoƒ^ƒCƒv"
+
+#: ../web/swat.c:733
+msgid "Stand Alone"
+msgstr "ƒXƒ^ƒ“ƒhƒAƒƒ“"
+
+#: ../web/swat.c:734
+msgid "Domain Member"
+msgstr "ƒhƒƒCƒ“ƒƒ“ƒo"
+
+#: ../web/swat.c:735
+msgid "Domain Controller"
+msgstr "ƒhƒƒCƒ“ƒRƒ“ƒgƒ[ƒ‰"
+
+#: ../web/swat.c:738
+msgid "Unusual Type in smb.conf - Please Select New Mode"
+msgstr "’ʏí‚ÌŒ`Ž‚Ċ‚Í‚È‚˘ - V‚µ‚˘ƒ‚[ƒh‚‘I‘‚Ì‚ħ‚Ĉ"
+
+#: ../web/swat.c:740
+msgid "Configure WINS As"
+msgstr "WINS"
+
+#: ../web/swat.c:741
+msgid "Not Used"
+msgstr "Žg‚í‚È‚˘"
+
+#: ../web/swat.c:742
+msgid "Server for client use"
+msgstr "ƒT[ƒo‚Ĉ‚µ‚č\Ĵ"
+
+#: ../web/swat.c:743
+msgid "Client of another WINS server"
+msgstr "•Ê‚Ì WINS ƒT[ƒo‚̃Nƒ‰ƒCƒAƒ“ƒg‚Ĉ‚µ‚č\Ĵ"
+
+#: ../web/swat.c:745
+msgid "Remote WINS Server"
+msgstr "•Ê‚Ì WINS ƒT[ƒo"
+
+#: ../web/swat.c:756
+msgid "Error: WINS Server Mode and WINS Support both set in smb.conf"
+msgstr "ƒGƒ‰[: wins server ‚Ĉ wins support ‚Ì—ĵƒpƒ‰ƒ[ƒ^‚Ş smb.conf ‚ĊŽw’肳‚ê‚Ä‚˘‚Ü‚·"
+
+#: ../web/swat.c:757
+msgid "Please Select desired WINS mode above."
+msgstr "‚˘‚¸‚ê‚İ‚Ì WINS ƒ‚[ƒh‚‘I‘‚µ‚Ä‚­‚‚³‚˘B"
+
+#: ../web/swat.c:759
+msgid "Expose Home Directories"
+msgstr "ƒz[ƒ€ƒfƒBƒŒƒNƒgƒŠ‚ÌŒöŠJ"
+
+#: ../web/swat.c:774
+msgid "The above configuration options will set multiple parameters and will generally assist with rapid Samba deployment."
+msgstr "‹L‚̐Ŭ’èƒIƒvƒVƒ‡ƒ“‚É‚ĉ‚èA•Ħ”‚̃pƒ‰ƒ[ƒ^‚Ş“ŻŠú‚µ‚ĐŬ’肳‚ê‚邽‚߁A Samba ‚̉^—p‚v‘Ĵ‚ÉŠJŽn‚·‚鏂Ċ‚̏•‚Ż‚Ĉ‚È‚é‚Ċ‚µ‚ċ‚¤B"
+
+#: ../web/swat.c:787
+msgid "Global Parameters"
+msgstr "Global ƒpƒ‰ƒ[ƒ^"
+
+#: ../web/swat.c:815 ../web/swat.c:916 ../web/swat.c:1265
+msgid "Commit Changes"
+msgstr "•ÏX‚”½‰f"
+
+#: ../web/swat.c:819 ../web/swat.c:919 ../web/swat.c:1267
+msgid "Reset Values"
+msgstr "•ÏX‚ŽĉÁ"
+
+#: ../web/swat.c:844
+msgid "Share Parameters"
+msgstr "ƒtƒ@ƒCƒ‹‹¤—L ƒpƒ‰ƒ[ƒ^"
+
+#: ../web/swat.c:887
+msgid "Choose Share"
+msgstr "ƒtƒ@ƒCƒ‹‹¤—L‚Ì‘I‘"
+
+#: ../web/swat.c:901
+msgid "Delete Share"
+msgstr "ƒtƒ@ƒCƒ‹‹¤—L‚̍폜"
+
+#: ../web/swat.c:908
+msgid "Create Share"
+msgstr "ƒtƒ@ƒCƒ‹‹¤—L‚̍ìĴ"
+
+#: ../web/swat.c:944
+msgid "password change in demo mode rejected"
+msgstr "ƒfƒ‚Eƒ‚[ƒh‚Ċ‚̃pƒXƒ[ƒh•ÏX‚Í‚Ċ‚Ğ‚Ü‚ı‚ñ"
+
+#: ../web/swat.c:957
+msgid "Can't setup password database vectors."
+msgstr "ƒpƒXƒ[ƒhEƒf[ƒ^ƒx[ƒX‚ތ݂‚Ż‚ç‚ê‚Ü‚ı‚ñ"
+
+#: ../web/swat.c:983
+msgid " Must specify \"User Name\" "
+msgstr "uƒ†[ƒU–ĵv—“‚É“ü—Í‚µ‚Ä‚­‚‚³‚˘"
+
+#: ../web/swat.c:999
+msgid " Must specify \"Old Password\" "
+msgstr "u‹ŒƒpƒXƒ[ƒhv—“‚É“ü—Í‚µ‚Ä‚­‚‚³‚˘"
+
+#: ../web/swat.c:1005
+msgid " Must specify \"Remote Machine\" "
+msgstr "uƒŠƒ‚[ƒgƒ}ƒVƒ“v—“‚É“ü—Í‚µ‚Ä‚­‚‚³‚˘"
+
+#: ../web/swat.c:1012
+msgid " Must specify \"New, and Re-typed Passwords\" "
+msgstr "uVƒpƒXƒ[ƒhv—“‚ĈuVƒpƒXƒ[ƒh‚̍ēü—́v—“‚É“ü—Í‚µ‚Ä‚­‚‚³‚˘"
+
+#: ../web/swat.c:1018
+msgid " Re-typed password didn't match new password "
+msgstr "uVƒpƒXƒ[ƒh‚̍ēü—́v—“‚Ì“ü—Í“à—e‚ށuVƒpƒXƒ[ƒhv—“‚Ì“ü—Í‚Ĉˆê’v‚µ‚Ä‚˘‚Ü‚ı‚ñB"
+
+#: ../web/swat.c:1048
+#, c-format
+msgid " The passwd for '%s' has been changed."
+msgstr " %s ‚̃pƒXƒ[ƒh‚͕ύX‚³‚ê‚Ü‚µ‚½B"
+
+#: ../web/swat.c:1051
+#, c-format
+msgid " The passwd for '%s' has NOT been changed."
+msgstr " '%s' ‚̃pƒXƒ[ƒh‚͕ύX‚³‚ê‚Ü‚ı‚ñ‚Ċ‚µ‚½B"
+
+#: ../web/swat.c:1076
+msgid "Server Password Management"
+msgstr "ƒ[ƒJƒ‹ƒ}ƒVƒ“‚̃pƒXƒ[ƒhŠÇ—"
+
+#.
+#. * Create all the dialog boxes for data collection
+#.
+#: ../web/swat.c:1085 ../web/swat.c:1132
+msgid "User Name"
+msgstr "ƒ†[ƒU–ĵ"
+
+#: ../web/swat.c:1088 ../web/swat.c:1134
+msgid "Old Password"
+msgstr "‹ŒƒpƒXƒ[ƒh"
+
+#: ../web/swat.c:1091 ../web/swat.c:1136
+msgid "New Password"
+msgstr "VƒpƒXƒ[ƒh"
+
+#: ../web/swat.c:1093 ../web/swat.c:1138
+msgid "Re-type New Password"
+msgstr "VƒpƒXƒ[ƒh‚̍ēü—Í"
+
+#: ../web/swat.c:1101 ../web/swat.c:1149
+msgid "Change Password"
+msgstr "ƒpƒXƒ[ƒh•ÏX"
+
+#: ../web/swat.c:1104
+msgid "Add New User"
+msgstr "V‹Kƒ†[ƒU’ljÁ"
+
+#: ../web/swat.c:1106
+msgid "Delete User"
+msgstr "ƒ†[ƒU‚̍폜"
+
+#: ../web/swat.c:1108
+msgid "Disable User"
+msgstr "ƒ†[ƒU‚Ì–³Œĝ‰ğ"
+
+#: ../web/swat.c:1110
+msgid "Enable User"
+msgstr "ƒ†[ƒU‚Ì—LŒĝ‰ğ"
+
+#: ../web/swat.c:1123
+msgid "Client/Server Password Management"
+msgstr "ƒŠƒ‚[ƒgƒ}ƒVƒ“‚̃pƒXƒ[ƒhŠÇ—"
+
+#: ../web/swat.c:1140
+msgid "Remote Machine"
+msgstr "ƒŠƒ‚[ƒgƒ}ƒVƒ“"
+
+#: ../web/swat.c:1179
+msgid "Printer Parameters"
+msgstr "ˆóü‹¤—L ƒpƒ‰ƒ[ƒ^"
+
+#: ../web/swat.c:1181
+msgid "Important Note:"
+msgstr "*’"
+
+#: ../web/swat.c:1182
+msgid "Printer names marked with [*] in the Choose Printer drop-down box "
+msgstr "–ĵ‘O‚̐ĉ“Ş‚É [*] ‚ނ‚˘‚½ƒvƒŠƒ“ƒ^"
+
+#: ../web/swat.c:1183
+msgid "are autoloaded printers from "
+msgstr "‚Í"
+
+#: ../web/swat.c:1184
+msgid "Printcap Name"
+msgstr "printcap name ƒpƒ‰ƒ[ƒ^"
+
+#: ../web/swat.c:1185
+msgid "Attempting to delete these printers from SWAT will have no effect."
+msgstr "‚İ‚çŽİ“Ŭ’肳‚ꂽ‚à‚Ì‚Ċ‚·‚İ‚çAíœ‚·‚é‚ħ‚Ĉ‚Í‚Ċ‚Ğ‚Ü‚ı‚ñB"
+
+#: ../web/swat.c:1231
+msgid "Choose Printer"
+msgstr "ˆóü‹¤—L‚Ì‘I‘"
+
+#: ../web/swat.c:1250
+msgid "Delete Printer"
+msgstr "ˆóü‹¤—L‚̍폜"
+
+#: ../web/swat.c:1257
+msgid "Create Printer"
+msgstr "ˆóü‹¤—L‚̍ìĴ"
+
+#: ../web/statuspage.c:123
+msgid "RDONLY "
+msgstr "ŽQĈ‚Ì‚Ŭ "
+
+#: ../web/statuspage.c:124
+msgid "WRONLY "
+msgstr "XV‚Ì‚Ŭ "
+
+#: ../web/statuspage.c:125
+msgid "RDWR "
+msgstr "ŽQĈ/XV "
+
+#: ../web/statuspage.c:309
+msgid "Server Status"
+msgstr "ƒT[ƒo‚̏ó‘Ô"
+
+#: ../web/statuspage.c:314
+msgid "Auto Refresh"
+msgstr "Žİ“XV‚ÌŠJŽn"
+
+#: ../web/statuspage.c:315 ../web/statuspage.c:320
+msgid "Refresh Interval: "
+msgstr "XVŠÔŠu: "
+
+#: ../web/statuspage.c:319
+msgid "Stop Refreshing"
+msgstr "Žİ“XV‚Ì’âŽ~"
+
+#: ../web/statuspage.c:334
+msgid "version:"
+msgstr "ƒo[ƒWƒ‡ƒ“"
+
+#: ../web/statuspage.c:337
+msgid "smbd:"
+msgstr ""
+
+#: ../web/statuspage.c:337 ../web/statuspage.c:350 ../web/statuspage.c:364
+msgid "running"
+msgstr "ŽÀs’†"
+
+#: ../web/statuspage.c:337 ../web/statuspage.c:350 ../web/statuspage.c:364
+msgid "not running"
+msgstr "’âŽ~’†"
+
+#: ../web/statuspage.c:341
+msgid "Stop smbd"
+msgstr "smbd ‚Ì’âŽ~"
+
+#: ../web/statuspage.c:343
+msgid "Start smbd"
+msgstr "smbd ‚Ì‹N“"
+
+#: ../web/statuspage.c:345
+msgid "Restart smbd"
+msgstr "smbd ‚̍ċN“"
+
+#: ../web/statuspage.c:350
+msgid "nmbd:"
+msgstr ""
+
+#: ../web/statuspage.c:354
+msgid "Stop nmbd"
+msgstr "nmbd ‚Ì’âŽ~"
+
+#: ../web/statuspage.c:356
+msgid "Start nmbd"
+msgstr "nmbd ‚Ì‹N“"
+
+#: ../web/statuspage.c:358
+msgid "Restart nmbd"
+msgstr "nmbd ‚̍ċN“"
+
+#: ../web/statuspage.c:364
+msgid "winbindd:"
+msgstr ""
+
+#: ../web/statuspage.c:368
+msgid "Stop winbindd"
+msgstr "winbindd ‚Ì’âŽ~"
+
+#: ../web/statuspage.c:370
+msgid "Start winbindd"
+msgstr "winbindd ‚Ì‹N“"
+
+#: ../web/statuspage.c:372
+msgid "Restart winbindd"
+msgstr "winbindd ‚̍ċN“"
+
+#. stop, restart all
+#: ../web/statuspage.c:381
+msgid "Stop All"
+msgstr "‚·‚ׂĒâŽ~"
+
+#: ../web/statuspage.c:382
+msgid "Restart All"
+msgstr "‚·‚ׂčċN“"
+
+#. start all
+#: ../web/statuspage.c:386
+msgid "Start All"
+msgstr "‚·‚ׂċN“"
+
+#: ../web/statuspage.c:393
+msgid "Active Connections"
+msgstr "Ú‘ħ’†‚̃Nƒ‰ƒCƒAƒ“ƒg"
+
+#: ../web/statuspage.c:395 ../web/statuspage.c:408 ../web/statuspage.c:416
+msgid "PID"
+msgstr ""
+
+#: ../web/statuspage.c:395 ../web/statuspage.c:408
+msgid "Client"
+msgstr "ƒNƒ‰ƒCƒAƒ“ƒg"
+
+#: ../web/statuspage.c:395
+msgid "IP address"
+msgstr "IPƒAƒhƒŒƒX"
+
+#: ../web/statuspage.c:395 ../web/statuspage.c:408 ../web/statuspage.c:416
+msgid "Date"
+msgstr "“ú•t"
+
+#: ../web/statuspage.c:397
+msgid "Kill"
+msgstr "Ĝ’f"
+
+#: ../web/statuspage.c:405
+msgid "Active Shares"
+msgstr "Ú‘ħ’†‚Ì‹¤—L"
+
+#: ../web/statuspage.c:408
+msgid "Share"
+msgstr "‹¤—L–ĵ"
+
+#: ../web/statuspage.c:408
+msgid "User"
+msgstr "ƒ†[ƒU"
+
+#: ../web/statuspage.c:408
+msgid "Group"
+msgstr "ƒOƒ‹[ƒv"
+
+#: ../web/statuspage.c:414
+msgid "Open Files"
+msgstr "Žg—p’†‚̃tƒ@ƒCƒ‹"
+
+#: ../web/statuspage.c:416
+msgid "Sharing"
+msgstr "”r‘ĵƒ‚[ƒh"
+
+#: ../web/statuspage.c:416
+msgid "R/W"
+msgstr "ŽQĈ/XV"
+
+#: ../web/statuspage.c:416
+msgid "Oplock"
+msgstr ""
+
+#: ../web/statuspage.c:416
+msgid "File"
+msgstr "ƒtƒ@ƒCƒ‹–ĵ"
+
+#: ../web/statuspage.c:425
+msgid "Show Client in col 1"
+msgstr "ƒNƒ‰ƒCƒAƒ“ƒg–ĵ‚ĉ“Ş‚É•\ŽĤ"
+
+#: ../web/statuspage.c:426
+msgid "Show PID in col 1"
+msgstr "PID‚ĉ“Ş‚É•\ŽĤ"
+
+#: ../param/loadparm.c:755
+msgid "Base Options"
+msgstr "Šî–{ ƒIƒvƒVƒ‡ƒ“"
+
+#: ../param/loadparm.c:775
+msgid "Security Options"
+msgstr "ƒZƒLƒ…ƒŠƒeƒB ƒIƒvƒVƒ‡ƒ“"
+
+#: ../param/loadparm.c:859
+msgid "Logging Options"
+msgstr "ƒƒMƒ“ƒO ƒIƒvƒVƒ‡ƒ“"
+
+#: ../param/loadparm.c:874
+msgid "Protocol Options"
+msgstr "ƒvƒƒgƒRƒ‹ ƒIƒvƒVƒ‡ƒ“"
+
+#: ../param/loadparm.c:911
+msgid "Tuning Options"
+msgstr "ƒ`ƒ…[ƒjƒ“ƒO ƒIƒvƒVƒ‡ƒ“"
+
+#: ../param/loadparm.c:940
+msgid "Printing Options"
+msgstr "ˆóü ƒIƒvƒVƒ‡ƒ“"
+
+#: ../param/loadparm.c:970
+msgid "Filename Handling"
+msgstr "ƒtƒ@ƒCƒ‹–ĵ‚ÌŽĉˆµ"
+
+#: ../param/loadparm.c:996
+msgid "Domain Options"
+msgstr "ƒhƒƒCƒ“ ƒIƒvƒVƒ‡ƒ“"
+
+#: ../param/loadparm.c:1000
+msgid "Logon Options"
+msgstr "ƒƒOƒIƒ“ ƒIƒvƒVƒ‡ƒ“"
+
+#: ../param/loadparm.c:1019
+msgid "Browse Options"
+msgstr "ƒuƒ‰ƒEƒWƒ“ƒO ƒIƒvƒVƒ‡ƒ“"
+
+#: ../param/loadparm.c:1033
+msgid "WINS Options"
+msgstr "WINS ƒIƒvƒVƒ‡ƒ“"
+
+#: ../param/loadparm.c:1043
+msgid "Locking Options"
+msgstr "ƒƒbƒLƒ“ƒO ƒIƒvƒVƒ‡ƒ“"
+
+#: ../param/loadparm.c:1061
+msgid "Ldap Options"
+msgstr "LDAP ƒIƒvƒVƒ‡ƒ“"
+
+#: ../param/loadparm.c:1078
+msgid "Miscellaneous Options"
+msgstr "‚ğ‚Ì‘ĵ‚̃IƒvƒVƒ‡ƒ“"
+
+#: ../param/loadparm.c:1138
+msgid "VFS module options"
+msgstr "VFS ƒIƒvƒVƒ‡ƒ“"
+
+#: ../param/loadparm.c:1148
+msgid "Winbind options"
+msgstr "Winbind ƒIƒvƒVƒ‡ƒ“"
diff --git a/source/po/nl.msg b/source/po/nl.msg
new file mode 100644
index 00000000000..8d7b050ce90
--- /dev/null
+++ b/source/po/nl.msg
@@ -0,0 +1,593 @@
+# Dutch messages for international release of SWAT.
+# Copyright (C) 2003 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.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: i18n_swat \n"
+"POT-Creation-Date: 2003-10-06 05:30+0900\n"
+"PO-Revision-Date: 2000-02-08 12:48+09:00\n"
+"Last-Translator: Jelmer Vernooij <jelmer@samba.org>\n"
+"Language-Team: (Samba Team) <samba-technical@samba.org>\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=US-ASCII\n"
+"Content-Transfer-Encoding: \n"
+
+#: ../web/swat.c:117
+#, c-format
+msgid "ERROR: Can't open %s"
+msgstr "FOUT: Kan %s niet openen"
+
+#: ../web/swat.c:200
+msgid "Help"
+msgstr "Help"
+
+#: ../web/swat.c:206 ../web/swat.c:220 ../web/swat.c:235 ../web/swat.c:243 ../web/swat.c:252 ../web/swat.c:261 ../web/swat.c:267 ../web/swat.c:273 ../web/swat.c:286
+msgid "Set Default"
+msgstr "Stel Standaard In"
+
+#: ../web/swat.c:408
+#, c-format
+msgid "failed to open %s for writing"
+msgstr "kon %s niet openen voor schrijven"
+
+#: ../web/swat.c:431
+#, c-format
+msgid "Can't reload %s"
+msgstr "Kan %s niet herladen"
+
+#: ../web/swat.c:501
+#, c-format
+msgid "Logged in as <b>%s</b>"
+msgstr "Ingelogd als <b>%s</b>"
+
+#: ../web/swat.c:505
+msgid "Home"
+msgstr "Home"
+
+#: ../web/swat.c:507
+msgid "Globals"
+msgstr "Algemene Instellingen"
+
+#: ../web/swat.c:508
+msgid "Shares"
+msgstr "Gedeelde Bronnen"
+
+#: ../web/swat.c:509
+msgid "Printers"
+msgstr "Printers"
+
+#: ../web/swat.c:510
+msgid "Wizard"
+msgstr "Wizard"
+
+#: ../web/swat.c:513
+msgid "Status"
+msgstr "Status"
+
+#: ../web/swat.c:514
+msgid "View Config"
+msgstr "Bekijk Configuratie"
+
+#: ../web/swat.c:516
+msgid "Password Management"
+msgstr "Wachtwoord Beheer"
+
+#: ../web/swat.c:526
+msgid "Current View Is"
+msgstr "Huidige weergave is"
+
+#: ../web/swat.c:527 ../web/swat.c:530
+msgid "Basic"
+msgstr "Basis"
+
+#: ../web/swat.c:528 ../web/swat.c:531
+msgid "Advanced"
+msgstr "Geadvanceerd"
+
+#: ../web/swat.c:529
+msgid "Change View To"
+msgstr "Verander Weergave In"
+
+#: ../web/swat.c:554
+msgid "Current Config"
+msgstr "Huidige Configuratie"
+
+#: ../web/swat.c:558
+msgid "Normal View"
+msgstr "Normale Weergave"
+
+#: ../web/swat.c:560
+msgid "Full View"
+msgstr "Volledige Weergave"
+
+#. Here we first set and commit all the parameters that were selected
+#. in the previous screen.
+#: ../web/swat.c:579
+msgid "Wizard Parameter Edit Page"
+msgstr "Wizard Instellingen Veranderen Pagina"
+
+#: ../web/swat.c:608
+msgid "Note: smb.conf file has been read and rewritten"
+msgstr "N.B.: het smb.conf bestand is gelezen en herschreven"
+
+#. Here we go ...
+#: ../web/swat.c:716
+msgid "Samba Configuration Wizard"
+msgstr "Samba Configuratie Wizard"
+
+#: ../web/swat.c:720
+msgid "The \"Rewrite smb.conf file\" button will clear the smb.conf file of all default values and of comments."
+msgstr "De \"Herschrijf smb.conf bestand\" knop zal alle standaardwaardes en alle commentaar verwijderen."
+
+#: ../web/swat.c:721
+msgid "The same will happen if you press the commit button."
+msgstr "Hetzelfde zal gebeuren als u de \"toepassen\" knop gebruikt."
+
+#: ../web/swat.c:724
+msgid "Rewrite smb.conf file"
+msgstr "Herschrijf smb.conf bestand"
+
+#: ../web/swat.c:725
+msgid "Commit"
+msgstr "Toepassen"
+
+#: ../web/swat.c:726
+msgid "Edit Parameter Values"
+msgstr "Bewerk Configuratie Waardes"
+
+#: ../web/swat.c:732
+msgid "Server Type"
+msgstr "Server Type"
+
+#: ../web/swat.c:733
+msgid "Stand Alone"
+msgstr "Stand Alone"
+
+#: ../web/swat.c:734
+msgid "Domain Member"
+msgstr "Domein Lid"
+
+#: ../web/swat.c:735
+msgid "Domain Controller"
+msgstr "Domein Controller"
+
+#: ../web/swat.c:738
+msgid "Unusual Type in smb.conf - Please Select New Mode"
+msgstr "Ongebruikelijk Type in smb.conf - Selecteer een nieuwe modus"
+
+#: ../web/swat.c:740
+msgid "Configure WINS As"
+msgstr "Configureer WINS Als"
+
+#: ../web/swat.c:741
+msgid "Not Used"
+msgstr "Niet gebruikt"
+
+#: ../web/swat.c:742
+msgid "Server for client use"
+msgstr "Server voor client gebruik"
+
+#: ../web/swat.c:743
+msgid "Client of another WINS server"
+msgstr "Client van een andere WINS server"
+
+#: ../web/swat.c:745
+msgid "Remote WINS Server"
+msgstr "Naam of IP-adres WINS Server"
+
+#: ../web/swat.c:756
+msgid "Error: WINS Server Mode and WINS Support both set in smb.conf"
+msgstr "Fout: WINS Server Modus en WINS Ondersteuning beiden ingesteld in smb.conf"
+
+#: ../web/swat.c:757
+msgid "Please Select desired WINS mode above."
+msgstr "Selecteer hierboven de gewenste WINS modus."
+
+#: ../web/swat.c:759
+msgid "Expose Home Directories"
+msgstr "Stel Home Directories Open"
+
+#: ../web/swat.c:774
+msgid "The above configuration options will set multiple parameters and will generally assist with rapid Samba deployment."
+msgstr "The configuratie hierboven zal meerdere variabelen veranderen en zal over het algemeen zorgen voor snelle installatie van Samba."
+
+#: ../web/swat.c:787
+msgid "Global Parameters"
+msgstr "Algemene Instellingen"
+
+#: ../web/swat.c:815 ../web/swat.c:916 ../web/swat.c:1265
+msgid "Commit Changes"
+msgstr "Pas Veranderingen Toe"
+
+#: ../web/swat.c:819 ../web/swat.c:919 ../web/swat.c:1267
+msgid "Reset Values"
+msgstr "Beginwaarden"
+
+#: ../web/swat.c:844
+msgid "Share Parameters"
+msgstr "Instellingen Gedeelde Bronnen"
+
+#: ../web/swat.c:887
+msgid "Choose Share"
+msgstr "Kies Bron"
+
+#: ../web/swat.c:901
+msgid "Delete Share"
+msgstr "Verwijder Bron"
+
+#: ../web/swat.c:908
+msgid "Create Share"
+msgstr "Maak Bron"
+
+#: ../web/swat.c:944
+msgid "password change in demo mode rejected"
+msgstr "wachtwoord veranderen in demo modus geweigerd"
+
+#: ../web/swat.c:957
+msgid "Can't setup password database vectors."
+msgstr "Kan wachtwoord database vectors niet opzetten."
+
+#: ../web/swat.c:983
+msgid " Must specify \"User Name\" "
+msgstr " \"Gebruikersnaam\" moet opgegeven worden "
+
+#: ../web/swat.c:999
+msgid " Must specify \"Old Password\" "
+msgstr " \"Oude wachtwoord\" moet opgegeven worden "
+
+#: ../web/swat.c:1005
+msgid " Must specify \"Remote Machine\" "
+msgstr " \"Server Naam of IP\" moet opgegeven worden "
+
+#: ../web/swat.c:1012
+msgid " Must specify \"New, and Re-typed Passwords\" "
+msgstr " \"Nieuw, en bevestiging Wachtwoorden\" moeten opgegeven worden "
+
+#: ../web/swat.c:1018
+msgid " Re-typed password didn't match new password "
+msgstr " Bevestigingswachtwoord was anders dan nieuwe wachtwoord "
+
+#: ../web/swat.c:1048
+#, c-format
+msgid " The passwd for '%s' has been changed."
+msgstr " Het wachtwoord voor '%s' is veranderd."
+
+#: ../web/swat.c:1051
+#, c-format
+msgid " The passwd for '%s' has NOT been changed."
+msgstr " Het wachtwoord voor '%s' is niet veranderd."
+
+#: ../web/swat.c:1076
+msgid "Server Password Management"
+msgstr "Server Wachtwoord Beheer"
+
+#.
+#. * Create all the dialog boxes for data collection
+#.
+#: ../web/swat.c:1085 ../web/swat.c:1132
+msgid "User Name"
+msgstr "Gebuikersnaam"
+
+#: ../web/swat.c:1088 ../web/swat.c:1134
+msgid "Old Password"
+msgstr "Oud Wachtwoord"
+
+#: ../web/swat.c:1091 ../web/swat.c:1136
+msgid "New Password"
+msgstr "Nieuw Wachtwoord"
+
+#: ../web/swat.c:1093 ../web/swat.c:1138
+msgid "Re-type New Password"
+msgstr "Bevestiging Nieuw Wachtwoord"
+
+#: ../web/swat.c:1101 ../web/swat.c:1149
+msgid "Change Password"
+msgstr "Verander Wachtwoord"
+
+#: ../web/swat.c:1104
+msgid "Add New User"
+msgstr "Voeg Gebruiker Toe"
+
+#: ../web/swat.c:1106
+msgid "Delete User"
+msgstr "Verwijder Gebruiker"
+
+#: ../web/swat.c:1108
+msgid "Disable User"
+msgstr "Maak gebruiker inactief"
+
+#: ../web/swat.c:1110
+msgid "Enable User"
+msgstr "Maak gebruiker actief"
+
+#: ../web/swat.c:1123
+msgid "Client/Server Password Management"
+msgstr "Client/Server Wachtwoord Beheer"
+
+#: ../web/swat.c:1140
+msgid "Remote Machine"
+msgstr "Naam of IP Server"
+
+#: ../web/swat.c:1179
+msgid "Printer Parameters"
+msgstr "Printer Instellingen"
+
+#: ../web/swat.c:1181
+msgid "Important Note:"
+msgstr "Belangrijk:"
+
+#: ../web/swat.c:1182
+msgid "Printer names marked with [*] in the Choose Printer drop-down box "
+msgstr "Printer namen gemarkeerd met [*] in het Kies Printer veld "
+
+#: ../web/swat.c:1183
+msgid "are autoloaded printers from "
+msgstr "zijn automatisch geladen uit "
+
+#: ../web/swat.c:1184
+msgid "Printcap Name"
+msgstr "Printcap Naam"
+
+#: ../web/swat.c:1185
+msgid "Attempting to delete these printers from SWAT will have no effect."
+msgstr "Proberen deze printers te verwijderen vanuit SWAT zal geen effect hebben."
+
+#: ../web/swat.c:1231
+msgid "Choose Printer"
+msgstr "Kies Printer"
+
+#: ../web/swat.c:1250
+msgid "Delete Printer"
+msgstr "Verwijder Printer"
+
+#: ../web/swat.c:1257
+msgid "Create Printer"
+msgstr "Maak Printer"
+
+#: ../web/statuspage.c:123
+msgid "RDONLY "
+msgstr "RDONLY"
+
+#: ../web/statuspage.c:124
+msgid "WRONLY "
+msgstr "WRONLY"
+
+#: ../web/statuspage.c:125
+msgid "RDWR "
+msgstr "RDWR"
+
+#: ../web/statuspage.c:309
+msgid "Server Status"
+msgstr "Server Status"
+
+#: ../web/statuspage.c:314
+msgid "Auto Refresh"
+msgstr "Automatisch Verversen"
+
+#: ../web/statuspage.c:315 ../web/statuspage.c:320
+msgid "Refresh Interval: "
+msgstr "Ververs Interval:"
+
+#: ../web/statuspage.c:319
+msgid "Stop Refreshing"
+msgstr "Stop met Verversen"
+
+#: ../web/statuspage.c:334
+msgid "version:"
+msgstr "versie:"
+
+#: ../web/statuspage.c:337
+msgid "smbd:"
+msgstr "smbd:"
+
+#: ../web/statuspage.c:337 ../web/statuspage.c:350 ../web/statuspage.c:364
+msgid "running"
+msgstr "draaiend"
+
+#: ../web/statuspage.c:337 ../web/statuspage.c:350 ../web/statuspage.c:364
+msgid "not running"
+msgstr "niet draaiend"
+
+#: ../web/statuspage.c:341
+msgid "Stop smbd"
+msgstr "Stop smbd"
+
+#: ../web/statuspage.c:343
+msgid "Start smbd"
+msgstr "Start smbd"
+
+#: ../web/statuspage.c:345
+msgid "Restart smbd"
+msgstr "Herstart smbd"
+
+#: ../web/statuspage.c:350
+msgid "nmbd:"
+msgstr "nmbd:"
+
+#: ../web/statuspage.c:354
+msgid "Stop nmbd"
+msgstr "Stop nmbd"
+
+#: ../web/statuspage.c:356
+msgid "Start nmbd"
+msgstr "Start nmbd"
+
+#: ../web/statuspage.c:358
+msgid "Restart nmbd"
+msgstr "Herstart nmbd"
+
+#: ../web/statuspage.c:364
+msgid "winbindd:"
+msgstr "winbindd:"
+
+#: ../web/statuspage.c:368
+msgid "Stop winbindd"
+msgstr "Stop winbindd"
+
+#: ../web/statuspage.c:370
+msgid "Start winbindd"
+msgstr "Start winbindd"
+
+#: ../web/statuspage.c:372
+msgid "Restart winbindd"
+msgstr "Herstart winbindd"
+
+#. stop, restart all
+#: ../web/statuspage.c:381
+msgid "Stop All"
+msgstr "Stop Alles"
+
+#: ../web/statuspage.c:382
+msgid "Restart All"
+msgstr "Herstart Alles"
+
+#. start all
+#: ../web/statuspage.c:386
+msgid "Start All"
+msgstr "Start Alles"
+
+#: ../web/statuspage.c:393
+msgid "Active Connections"
+msgstr "Actieve Verbindingen"
+
+#: ../web/statuspage.c:395 ../web/statuspage.c:408 ../web/statuspage.c:416
+msgid "PID"
+msgstr "PID"
+
+#: ../web/statuspage.c:395 ../web/statuspage.c:408
+msgid "Client"
+msgstr "Client"
+
+#: ../web/statuspage.c:395
+msgid "IP address"
+msgstr "IP adres"
+
+#: ../web/statuspage.c:395 ../web/statuspage.c:408 ../web/statuspage.c:416
+msgid "Date"
+msgstr "Datum"
+
+#: ../web/statuspage.c:397
+msgid "Kill"
+msgstr "Kill"
+
+#: ../web/statuspage.c:405
+msgid "Active Shares"
+msgstr "Actieve Bronnen"
+
+#: ../web/statuspage.c:408
+msgid "Share"
+msgstr "Bron"
+
+#: ../web/statuspage.c:408
+msgid "User"
+msgstr "Gebruiker"
+
+#: ../web/statuspage.c:408
+msgid "Group"
+msgstr "Groep"
+
+#: ../web/statuspage.c:414
+msgid "Open Files"
+msgstr "Geopende Bestanden"
+
+#: ../web/statuspage.c:416
+msgid "Sharing"
+msgstr "Gedeeld"
+
+#: ../web/statuspage.c:416
+msgid "R/W"
+msgstr "Lees/Schrijf"
+
+#: ../web/statuspage.c:416
+msgid "Oplock"
+msgstr "Oplock"
+
+#: ../web/statuspage.c:416
+msgid "File"
+msgstr "Bestand"
+
+#: ../web/statuspage.c:425
+msgid "Show Client in col 1"
+msgstr "Toon Client in kolom 1"
+
+#: ../web/statuspage.c:426
+msgid "Show PID in col 1"
+msgstr "Toon PID in kolom 1"
+
+#: ../param/loadparm.c:755
+msgid "Base Options"
+msgstr "Basis Opties"
+
+#: ../param/loadparm.c:775
+msgid "Security Options"
+msgstr "Veiligheidsopties"
+
+#: ../param/loadparm.c:859
+msgid "Logging Options"
+msgstr "Log Opties"
+
+#: ../param/loadparm.c:874
+msgid "Protocol Options"
+msgstr "Protocol Opties"
+
+#: ../param/loadparm.c:911
+msgid "Tuning Options"
+msgstr "Fijntune Opties"
+
+#: ../param/loadparm.c:940
+msgid "Printing Options"
+msgstr "Printer Opties"
+
+#: ../param/loadparm.c:970
+msgid "Filename Handling"
+msgstr "Bestandsnaam Afhandeling"
+
+#: ../param/loadparm.c:996
+msgid "Domain Options"
+msgstr "Domein Opties"
+
+#: ../param/loadparm.c:1000
+msgid "Logon Options"
+msgstr "Logon Opties"
+
+#: ../param/loadparm.c:1019
+msgid "Browse Options"
+msgstr "Verken Opties"
+
+#: ../param/loadparm.c:1033
+msgid "WINS Options"
+msgstr "WINS Opties"
+
+#: ../param/loadparm.c:1043
+msgid "Locking Options"
+msgstr "Locking Opties"
+
+#: ../param/loadparm.c:1061
+msgid "Ldap Options"
+msgstr "LDAP Opties"
+
+#: ../param/loadparm.c:1078
+msgid "Miscellaneous Options"
+msgstr "Verscheidene Opties"
+
+#: ../param/loadparm.c:1138
+msgid "VFS module options"
+msgstr "VFS module opties"
+
+#: ../param/loadparm.c:1148
+msgid "Winbind options"
+msgstr "Winbind opties"
diff --git a/source/po/pl.msg b/source/po/pl.msg
new file mode 100644
index 00000000000..a7e56453bfc
--- /dev/null
+++ b/source/po/pl.msg
@@ -0,0 +1,593 @@
+# Polish messages for international release of SWAT.
+# Copyright (C) 2001 Rafal Szczesniak <mimir@spin.ict.pwr.wroc.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.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: i18n_swat \n"
+"POT-Creation-Date: 2003-10-06 05:30+0900\n"
+"PO-Revision-Date: 2001-08-15 22:45+02:00\n"
+"Last-Translator: Rafal Szczesniak <mimir@spin.ict.pwr.wroc.pl>\n"
+"Language-Team: pl\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=iso-8859-2\n"
+"Content-Transfer-Encoding: \n"
+
+#: ../web/swat.c:117
+#, c-format
+msgid "ERROR: Can't open %s"
+msgstr ""
+
+#: ../web/swat.c:200
+msgid "Help"
+msgstr "Pomoc"
+
+#: ../web/swat.c:206 ../web/swat.c:220 ../web/swat.c:235 ../web/swat.c:243 ../web/swat.c:252 ../web/swat.c:261 ../web/swat.c:267 ../web/swat.c:273 ../web/swat.c:286
+msgid "Set Default"
+msgstr "Ustaw domyĥlnie"
+
+#: ../web/swat.c:408
+#, c-format
+msgid "failed to open %s for writing"
+msgstr ""
+
+#: ../web/swat.c:431
+#, c-format
+msgid "Can't reload %s"
+msgstr ""
+
+#: ../web/swat.c:501
+#, c-format
+msgid "Logged in as <b>%s</b>"
+msgstr "Zalogowany jako <b>%s</b><p>\n"
+
+#: ../web/swat.c:505
+msgid "Home"
+msgstr "Strona domowa"
+
+#: ../web/swat.c:507
+msgid "Globals"
+msgstr "Ustawienia globalne"
+
+#: ../web/swat.c:508
+msgid "Shares"
+msgstr "Wspó³udzia³y"
+
+#: ../web/swat.c:509
+msgid "Printers"
+msgstr "Drukarki"
+
+#: ../web/swat.c:510
+msgid "Wizard"
+msgstr ""
+
+#: ../web/swat.c:513
+msgid "Status"
+msgstr "Status"
+
+#: ../web/swat.c:514
+msgid "View Config"
+msgstr "Przejrzyj Konfiguracjê"
+
+#: ../web/swat.c:516
+msgid "Password Management"
+msgstr "Zarzħdzanie Has³ami"
+
+#: ../web/swat.c:526
+msgid "Current View Is"
+msgstr "Bieżħca Konfiguracja"
+
+#: ../web/swat.c:527 ../web/swat.c:530
+msgid "Basic"
+msgstr "Widok Podstawowy"
+
+#: ../web/swat.c:528 ../web/swat.c:531
+msgid "Advanced"
+msgstr "Widok Zaawansowany"
+
+#: ../web/swat.c:529
+msgid "Change View To"
+msgstr "Zmieñ Has³o"
+
+#: ../web/swat.c:554
+msgid "Current Config"
+msgstr "Bieżħca Konfiguracja"
+
+#: ../web/swat.c:558
+msgid "Normal View"
+msgstr "Normalny Widok"
+
+#: ../web/swat.c:560
+msgid "Full View"
+msgstr "Pe³ny Widok"
+
+#. Here we first set and commit all the parameters that were selected
+#. in the previous screen.
+#: ../web/swat.c:579
+msgid "Wizard Parameter Edit Page"
+msgstr ""
+
+#: ../web/swat.c:608
+msgid "Note: smb.conf file has been read and rewritten"
+msgstr ""
+
+#. Here we go ...
+#: ../web/swat.c:716
+msgid "Samba Configuration Wizard"
+msgstr ""
+
+#: ../web/swat.c:720
+msgid "The \"Rewrite smb.conf file\" button will clear the smb.conf file of all default values and of comments."
+msgstr ""
+
+#: ../web/swat.c:721
+msgid "The same will happen if you press the commit button."
+msgstr ""
+
+#: ../web/swat.c:724
+msgid "Rewrite smb.conf file"
+msgstr ""
+
+#: ../web/swat.c:725
+msgid "Commit"
+msgstr "Potwierdĵ Zmiany"
+
+#: ../web/swat.c:726
+msgid "Edit Parameter Values"
+msgstr "Parametry Drukarki"
+
+#: ../web/swat.c:732
+msgid "Server Type"
+msgstr ""
+
+#: ../web/swat.c:733
+msgid "Stand Alone"
+msgstr "Uruchom nmbd"
+
+#: ../web/swat.c:734
+msgid "Domain Member"
+msgstr ""
+
+#: ../web/swat.c:735
+msgid "Domain Controller"
+msgstr ""
+
+#: ../web/swat.c:738
+msgid "Unusual Type in smb.conf - Please Select New Mode"
+msgstr ""
+
+#: ../web/swat.c:740
+msgid "Configure WINS As"
+msgstr ""
+
+#: ../web/swat.c:741
+msgid "Not Used"
+msgstr ""
+
+#: ../web/swat.c:742
+msgid "Server for client use"
+msgstr ""
+
+#: ../web/swat.c:743
+msgid "Client of another WINS server"
+msgstr ""
+
+#: ../web/swat.c:745
+msgid "Remote WINS Server"
+msgstr ""
+
+#: ../web/swat.c:756
+msgid "Error: WINS Server Mode and WINS Support both set in smb.conf"
+msgstr ""
+
+#: ../web/swat.c:757
+msgid "Please Select desired WINS mode above."
+msgstr ""
+
+#: ../web/swat.c:759
+msgid "Expose Home Directories"
+msgstr ""
+
+#: ../web/swat.c:774
+msgid "The above configuration options will set multiple parameters and will generally assist with rapid Samba deployment."
+msgstr ""
+
+#: ../web/swat.c:787
+msgid "Global Parameters"
+msgstr "Zmienne Globalne"
+
+#: ../web/swat.c:815 ../web/swat.c:916 ../web/swat.c:1265
+msgid "Commit Changes"
+msgstr "Potwierdĵ Zmiany"
+
+#: ../web/swat.c:819 ../web/swat.c:919 ../web/swat.c:1267
+msgid "Reset Values"
+msgstr "Zresetuj Wartoĥci"
+
+#: ../web/swat.c:844
+msgid "Share Parameters"
+msgstr "Parametry Wspó³udzia³u"
+
+#: ../web/swat.c:887
+msgid "Choose Share"
+msgstr "Wybierz Wspó³udzia³"
+
+#: ../web/swat.c:901
+msgid "Delete Share"
+msgstr "Usuñ Wspó³udzia³"
+
+#: ../web/swat.c:908
+msgid "Create Share"
+msgstr "Utwórz Wspó³udzia³"
+
+#: ../web/swat.c:944
+msgid "password change in demo mode rejected"
+msgstr "zmiana has³a w trybie demo odrzucona\n"
+
+#: ../web/swat.c:957
+msgid "Can't setup password database vectors."
+msgstr ""
+
+#: ../web/swat.c:983
+msgid " Must specify \"User Name\" "
+msgstr " Musisz podaĉ \"Nazwê Użytkownika\" \n"
+
+#: ../web/swat.c:999
+msgid " Must specify \"Old Password\" "
+msgstr " Musisz podaĉ \"Stare Has³o\" \n"
+
+#: ../web/swat.c:1005
+msgid " Must specify \"Remote Machine\" "
+msgstr " Musisz podaĉ \"Zdalnħ Maszynê\" \n"
+
+#: ../web/swat.c:1012
+msgid " Must specify \"New, and Re-typed Passwords\" "
+msgstr " Musisz podaĉ \"Nowe Has³o, i ponownie wpisane Nowe Has³o\" \n"
+
+#: ../web/swat.c:1018
+msgid " Re-typed password didn't match new password "
+msgstr " Ponownie wpisane has³o nie pasuje do nowego has³a\n"
+
+#: ../web/swat.c:1048
+#, c-format
+msgid " The passwd for '%s' has been changed."
+msgstr " Has³o dla '%s' zosta³o zmienione. \n"
+
+#: ../web/swat.c:1051
+#, c-format
+msgid " The passwd for '%s' has NOT been changed."
+msgstr " Has³o dla '%s' NIE zosta³o zmienione. \n"
+
+#: ../web/swat.c:1076
+msgid "Server Password Management"
+msgstr "Zarzħdzanie Has³ami na Serwerze"
+
+#.
+#. * Create all the dialog boxes for data collection
+#.
+#: ../web/swat.c:1085 ../web/swat.c:1132
+msgid "User Name"
+msgstr " Nazwa Użytkownika"
+
+#: ../web/swat.c:1088 ../web/swat.c:1134
+msgid "Old Password"
+msgstr " Stare Has³o"
+
+#: ../web/swat.c:1091 ../web/swat.c:1136
+msgid "New Password"
+msgstr " Nowe Has³o"
+
+#: ../web/swat.c:1093 ../web/swat.c:1138
+msgid "Re-type New Password"
+msgstr " Ponownie wpisz Nowe Has³o"
+
+#: ../web/swat.c:1101 ../web/swat.c:1149
+msgid "Change Password"
+msgstr "Zmieñ Has³o"
+
+#: ../web/swat.c:1104
+msgid "Add New User"
+msgstr "Dodaj Nowego Użytkownika"
+
+#: ../web/swat.c:1106
+msgid "Delete User"
+msgstr "Usuñ Użytkownika"
+
+#: ../web/swat.c:1108
+msgid "Disable User"
+msgstr "Zablokuj Użytkownika"
+
+#: ../web/swat.c:1110
+msgid "Enable User"
+msgstr "Odblokuj Użytkownika"
+
+#: ../web/swat.c:1123
+msgid "Client/Server Password Management"
+msgstr "Zarzħdzanie Has³ami Klient/Serwer"
+
+#: ../web/swat.c:1140
+msgid "Remote Machine"
+msgstr " Zdalna Maszyna"
+
+#: ../web/swat.c:1179
+msgid "Printer Parameters"
+msgstr "Parametry Drukarki"
+
+#: ../web/swat.c:1181
+msgid "Important Note:"
+msgstr "Ważna Informacja:"
+
+#: ../web/swat.c:1182
+msgid "Printer names marked with [*] in the Choose Printer drop-down box "
+msgstr "Nazwy Drukarek zaznaczone [*] w rozwijanym polu Wybierz Drukarkê "
+
+#: ../web/swat.c:1183
+msgid "are autoloaded printers from "
+msgstr "sħ drukarkami automatycznie ³adowanymi z "
+
+#: ../web/swat.c:1184
+msgid "Printcap Name"
+msgstr "Nazwa Printcap"
+
+#: ../web/swat.c:1185
+msgid "Attempting to delete these printers from SWAT will have no effect."
+msgstr "Próby usuniêcia tych drukarek ze SWAT nie przyniosħ efektu.\n"
+
+#: ../web/swat.c:1231
+msgid "Choose Printer"
+msgstr "Wybierz Drukarkê"
+
+#: ../web/swat.c:1250
+msgid "Delete Printer"
+msgstr "Usuñ Drukarkê"
+
+#: ../web/swat.c:1257
+msgid "Create Printer"
+msgstr "Utwórz Drukarkê"
+
+#: ../web/statuspage.c:123
+msgid "RDONLY "
+msgstr ""
+
+#: ../web/statuspage.c:124
+msgid "WRONLY "
+msgstr ""
+
+#: ../web/statuspage.c:125
+msgid "RDWR "
+msgstr ""
+
+#: ../web/statuspage.c:309
+msgid "Server Status"
+msgstr "Status Serwera"
+
+#: ../web/statuspage.c:314
+msgid "Auto Refresh"
+msgstr "Automatyczne Odĥwieżanie"
+
+#: ../web/statuspage.c:315 ../web/statuspage.c:320
+msgid "Refresh Interval: "
+msgstr "Interwa³ Odĥwieżania: "
+
+#: ../web/statuspage.c:319
+msgid "Stop Refreshing"
+msgstr "Zatrzymaj Odĥwieżanie"
+
+#: ../web/statuspage.c:334
+msgid "version:"
+msgstr "wersja:"
+
+#: ../web/statuspage.c:337
+msgid "smbd:"
+msgstr ""
+
+#: ../web/statuspage.c:337 ../web/statuspage.c:350 ../web/statuspage.c:364
+msgid "running"
+msgstr "dzia³a"
+
+#: ../web/statuspage.c:337 ../web/statuspage.c:350 ../web/statuspage.c:364
+msgid "not running"
+msgstr "nie dzia³a"
+
+#: ../web/statuspage.c:341
+msgid "Stop smbd"
+msgstr "Zatrzymaj smbd"
+
+#: ../web/statuspage.c:343
+msgid "Start smbd"
+msgstr "Uruchom smbd"
+
+#: ../web/statuspage.c:345
+msgid "Restart smbd"
+msgstr "Zrestartuj smbd"
+
+#: ../web/statuspage.c:350
+msgid "nmbd:"
+msgstr ""
+
+#: ../web/statuspage.c:354
+msgid "Stop nmbd"
+msgstr "Zatrzymaj nmbd"
+
+#: ../web/statuspage.c:356
+msgid "Start nmbd"
+msgstr "Uruchom nmbd"
+
+#: ../web/statuspage.c:358
+msgid "Restart nmbd"
+msgstr "Zrestartuj nmbd"
+
+#: ../web/statuspage.c:364
+msgid "winbindd:"
+msgstr ""
+
+#: ../web/statuspage.c:368
+msgid "Stop winbindd"
+msgstr "Zatrzymaj nmbd"
+
+#: ../web/statuspage.c:370
+msgid "Start winbindd"
+msgstr "Uruchom nmbd"
+
+#: ../web/statuspage.c:372
+msgid "Restart winbindd"
+msgstr "Zrestartuj nmbd"
+
+#. stop, restart all
+#: ../web/statuspage.c:381
+msgid "Stop All"
+msgstr ""
+
+#: ../web/statuspage.c:382
+msgid "Restart All"
+msgstr "Zrestartuj nmbd"
+
+#. start all
+#: ../web/statuspage.c:386
+msgid "Start All"
+msgstr "Uruchom nmbd"
+
+#: ../web/statuspage.c:393
+msgid "Active Connections"
+msgstr "Aktywne Po³ħczenia"
+
+#: ../web/statuspage.c:395 ../web/statuspage.c:408 ../web/statuspage.c:416
+msgid "PID"
+msgstr ""
+
+#: ../web/statuspage.c:395 ../web/statuspage.c:408
+msgid "Client"
+msgstr "Klient"
+
+#: ../web/statuspage.c:395
+msgid "IP address"
+msgstr "adres IP"
+
+#: ../web/statuspage.c:395 ../web/statuspage.c:408 ../web/statuspage.c:416
+msgid "Date"
+msgstr "Data"
+
+#: ../web/statuspage.c:397
+msgid "Kill"
+msgstr "Zatrzymaj"
+
+#: ../web/statuspage.c:405
+msgid "Active Shares"
+msgstr "Aktywne Wspó³udzia³y"
+
+#: ../web/statuspage.c:408
+msgid "Share"
+msgstr "Wspó³udzia³"
+
+#: ../web/statuspage.c:408
+msgid "User"
+msgstr "Użytkownik"
+
+#: ../web/statuspage.c:408
+msgid "Group"
+msgstr "Grupa"
+
+#: ../web/statuspage.c:414
+msgid "Open Files"
+msgstr "Otwarte Pliki"
+
+#: ../web/statuspage.c:416
+msgid "Sharing"
+msgstr "Wspó³dzielenie"
+
+#: ../web/statuspage.c:416
+msgid "R/W"
+msgstr ""
+
+#: ../web/statuspage.c:416
+msgid "Oplock"
+msgstr ""
+
+#: ../web/statuspage.c:416
+msgid "File"
+msgstr "Plik"
+
+#: ../web/statuspage.c:425
+msgid "Show Client in col 1"
+msgstr ""
+
+#: ../web/statuspage.c:426
+msgid "Show PID in col 1"
+msgstr ""
+
+#: ../param/loadparm.c:755
+msgid "Base Options"
+msgstr "Bazowe Opcje"
+
+#: ../param/loadparm.c:775
+msgid "Security Options"
+msgstr "Opcje Zabezpieczeñ"
+
+#: ../param/loadparm.c:859
+msgid "Logging Options"
+msgstr "Opcje Blokowania"
+
+#: ../param/loadparm.c:874
+msgid "Protocol Options"
+msgstr "Opcje Protoko³u"
+
+#: ../param/loadparm.c:911
+msgid "Tuning Options"
+msgstr "Opcje Dostrajajħce"
+
+#: ../param/loadparm.c:940
+msgid "Printing Options"
+msgstr "Opcje Drukowania"
+
+#: ../param/loadparm.c:970
+msgid "Filename Handling"
+msgstr "Obs³uga Nazw Plików"
+
+#: ../param/loadparm.c:996
+msgid "Domain Options"
+msgstr "Opcje Domeny"
+
+#: ../param/loadparm.c:1000
+msgid "Logon Options"
+msgstr "Opcje Logowania"
+
+#: ../param/loadparm.c:1019
+msgid "Browse Options"
+msgstr "Opcje Przeglħdania"
+
+#: ../param/loadparm.c:1033
+msgid "WINS Options"
+msgstr "Opcje WINS"
+
+#: ../param/loadparm.c:1043
+msgid "Locking Options"
+msgstr "Opcje Blokowania"
+
+#: ../param/loadparm.c:1061
+msgid "Ldap Options"
+msgstr "Opcje Ldap"
+
+#: ../param/loadparm.c:1078
+msgid "Miscellaneous Options"
+msgstr "Pozosta³e Opcje"
+
+#: ../param/loadparm.c:1138
+msgid "VFS module options"
+msgstr "Opcje WINS"
+
+#: ../param/loadparm.c:1148
+msgid "Winbind options"
+msgstr "Opcje Drukowania"
diff --git a/source/po/tr.msg b/source/po/tr.msg
new file mode 100644
index 00000000000..8ef551da46a
--- /dev/null
+++ b/source/po/tr.msg
@@ -0,0 +1,594 @@
+# Turkish messages for international release of SWAT.
+# Copyright (C) 2001 Deniz Akkus Kanca <deniz@arayan.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 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.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: i18n_swat \n"
+"POT-Creation-Date: 2003-10-06 05:30+0900\n"
+"PO-Revision-Date: 2001-09-20 22:51EEST\n"
+"Last-Translator: Deniz Akkus Kanca <deniz@arayan.com>\n"
+"Language-Team: Turkish <gnu-tr-u12a@lists.sourceforge.net>\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=ISO-8859-9\n"
+"Content-Transfer-Encoding: 8bit\n"
+"X-Generator: KBabel 0.9.1\n"
+
+#: ../web/swat.c:117
+#, c-format
+msgid "ERROR: Can't open %s"
+msgstr ""
+
+#: ../web/swat.c:200
+msgid "Help"
+msgstr "Yardŭm"
+
+#: ../web/swat.c:206 ../web/swat.c:220 ../web/swat.c:235 ../web/swat.c:243 ../web/swat.c:252 ../web/swat.c:261 ../web/swat.c:267 ../web/swat.c:273 ../web/swat.c:286
+msgid "Set Default"
+msgstr "Öntanŭmlŭya Ayarla"
+
+#: ../web/swat.c:408
+#, c-format
+msgid "failed to open %s for writing"
+msgstr ""
+
+#: ../web/swat.c:431
+#, c-format
+msgid "Can't reload %s"
+msgstr ""
+
+#: ../web/swat.c:501
+#, c-format
+msgid "Logged in as <b>%s</b>"
+msgstr "<b>%s</b> kimlii ile oturum açŭlmŭŝ"
+
+#: ../web/swat.c:505
+msgid "Home"
+msgstr "Ev"
+
+#: ../web/swat.c:507
+msgid "Globals"
+msgstr "Evrenseller"
+
+#: ../web/swat.c:508
+msgid "Shares"
+msgstr "Paylaŝŭmlar"
+
+#: ../web/swat.c:509
+msgid "Printers"
+msgstr "Yazŭcŭlar"
+
+#: ../web/swat.c:510
+msgid "Wizard"
+msgstr ""
+
+#: ../web/swat.c:513
+msgid "Status"
+msgstr "Durum"
+
+#: ../web/swat.c:514
+msgid "View Config"
+msgstr "Ayarlara Gözat"
+
+#: ../web/swat.c:516
+msgid "Password Management"
+msgstr "Ŝifre Yönetimi"
+
+#: ../web/swat.c:526
+msgid "Current View Is"
+msgstr "Ŝimdiki Ayarlar"
+
+#: ../web/swat.c:527 ../web/swat.c:530
+msgid "Basic"
+msgstr "Temel Görünüm"
+
+#: ../web/swat.c:528 ../web/swat.c:531
+msgid "Advanced"
+msgstr "Geliŝmiŝ Görünüm"
+
+#: ../web/swat.c:529
+msgid "Change View To"
+msgstr "Ŝifre Deiŝtir"
+
+#: ../web/swat.c:554
+msgid "Current Config"
+msgstr "Ŝimdiki Ayarlar"
+
+#: ../web/swat.c:558
+msgid "Normal View"
+msgstr "Normal Görünüm"
+
+#: ../web/swat.c:560
+msgid "Full View"
+msgstr "Tam Görünüm"
+
+#. Here we first set and commit all the parameters that were selected
+#. in the previous screen.
+#: ../web/swat.c:579
+msgid "Wizard Parameter Edit Page"
+msgstr ""
+
+#: ../web/swat.c:608
+msgid "Note: smb.conf file has been read and rewritten"
+msgstr ""
+
+#. Here we go ...
+#: ../web/swat.c:716
+msgid "Samba Configuration Wizard"
+msgstr ""
+
+#: ../web/swat.c:720
+msgid "The \"Rewrite smb.conf file\" button will clear the smb.conf file of all default values and of comments."
+msgstr ""
+
+#: ../web/swat.c:721
+msgid "The same will happen if you press the commit button."
+msgstr ""
+
+#: ../web/swat.c:724
+msgid "Rewrite smb.conf file"
+msgstr ""
+
+#: ../web/swat.c:725
+msgid "Commit"
+msgstr "açŭklama"
+
+#: ../web/swat.c:726
+msgid "Edit Parameter Values"
+msgstr "Yazŭcŭ Bilgileri"
+
+#: ../web/swat.c:732
+msgid "Server Type"
+msgstr ""
+
+#: ../web/swat.c:733
+msgid "Stand Alone"
+msgstr "Nmbd'yi çalŭŝtŭr"
+
+#: ../web/swat.c:734
+msgid "Domain Member"
+msgstr "alan sunucusu"
+
+#: ../web/swat.c:735
+msgid "Domain Controller"
+msgstr "alan sunucusu"
+
+#: ../web/swat.c:738
+msgid "Unusual Type in smb.conf - Please Select New Mode"
+msgstr ""
+
+#: ../web/swat.c:740
+msgid "Configure WINS As"
+msgstr ""
+
+#: ../web/swat.c:741
+msgid "Not Used"
+msgstr "dont descend"
+
+#: ../web/swat.c:742
+msgid "Server for client use"
+msgstr ""
+
+#: ../web/swat.c:743
+msgid "Client of another WINS server"
+msgstr ""
+
+#: ../web/swat.c:745
+msgid "Remote WINS Server"
+msgstr ""
+
+#: ../web/swat.c:756
+msgid "Error: WINS Server Mode and WINS Support both set in smb.conf"
+msgstr ""
+
+#: ../web/swat.c:757
+msgid "Please Select desired WINS mode above."
+msgstr ""
+
+#: ../web/swat.c:759
+msgid "Expose Home Directories"
+msgstr ""
+
+#: ../web/swat.c:774
+msgid "The above configuration options will set multiple parameters and will generally assist with rapid Samba deployment."
+msgstr ""
+
+#: ../web/swat.c:787
+msgid "Global Parameters"
+msgstr "Genel Deiŝkenler"
+
+#: ../web/swat.c:815 ../web/swat.c:916 ../web/swat.c:1265
+msgid "Commit Changes"
+msgstr "Deiŝiklikleri Kaydet"
+
+#: ../web/swat.c:819 ../web/swat.c:919 ../web/swat.c:1267
+msgid "Reset Values"
+msgstr "Deerleri Ŭlk Haline Getir"
+
+#: ../web/swat.c:844
+msgid "Share Parameters"
+msgstr "Paylaŝŭm Parametreleri"
+
+#: ../web/swat.c:887
+msgid "Choose Share"
+msgstr "Paylaŝŭm Seçin"
+
+#: ../web/swat.c:901
+msgid "Delete Share"
+msgstr "Paylaŝŭm Kaldŭr"
+
+#: ../web/swat.c:908
+msgid "Create Share"
+msgstr "Paylaŝŭm Oluŝtur"
+
+#: ../web/swat.c:944
+msgid "password change in demo mode rejected"
+msgstr "demo kipinde ŝifre deiŝiklii kabul edilmedi\n"
+
+#: ../web/swat.c:957
+msgid "Can't setup password database vectors."
+msgstr ""
+
+#: ../web/swat.c:983
+msgid " Must specify \"User Name\" "
+msgstr " \"Kullanŭcŭ Adŭ\" belirtilmeli \n"
+
+#: ../web/swat.c:999
+msgid " Must specify \"Old Password\" "
+msgstr " \"Eski Ŝifre\" belirtilmeli \n"
+
+#: ../web/swat.c:1005
+msgid " Must specify \"Remote Machine\" "
+msgstr " \"Uzak Makina\" belirtilmeli \n"
+
+#: ../web/swat.c:1012
+msgid " Must specify \"New, and Re-typed Passwords\" "
+msgstr " \"Yeni ve Tekrar Girilmiŝ Ŝifreler\" belirtilmeli \n"
+
+#: ../web/swat.c:1018
+msgid " Re-typed password didn't match new password "
+msgstr " Tekrar girilen ŝifre yeni ŝifre ile eŝleŝmedi\n"
+
+#: ../web/swat.c:1048
+#, c-format
+msgid " The passwd for '%s' has been changed."
+msgstr " '%s' için ŝifre deiŝtirildi."
+
+#: ../web/swat.c:1051
+#, c-format
+msgid " The passwd for '%s' has NOT been changed."
+msgstr " '%s' için ŝifre DEŬŜTŬRŬLMEDŬ."
+
+#: ../web/swat.c:1076
+msgid "Server Password Management"
+msgstr "Sunucu Ŝifre Yönetimi"
+
+#.
+#. * Create all the dialog boxes for data collection
+#.
+#: ../web/swat.c:1085 ../web/swat.c:1132
+msgid "User Name"
+msgstr " Kullanŭcŭ Adŭ"
+
+#: ../web/swat.c:1088 ../web/swat.c:1134
+msgid "Old Password"
+msgstr " Eski Ŝifre"
+
+#: ../web/swat.c:1091 ../web/swat.c:1136
+msgid "New Password"
+msgstr " Yeni Ŝifre"
+
+#: ../web/swat.c:1093 ../web/swat.c:1138
+msgid "Re-type New Password"
+msgstr " Yeni Ŝifre Tekrarŭ"
+
+#: ../web/swat.c:1101 ../web/swat.c:1149
+msgid "Change Password"
+msgstr "Ŝifre Deiŝtir"
+
+#: ../web/swat.c:1104
+msgid "Add New User"
+msgstr "Kull. Ekle"
+
+#: ../web/swat.c:1106
+msgid "Delete User"
+msgstr "Kull. Sil"
+
+#: ../web/swat.c:1108
+msgid "Disable User"
+msgstr "Kull. Etkisizleŝtir"
+
+#: ../web/swat.c:1110
+msgid "Enable User"
+msgstr "Kull. Etkinleŝtir"
+
+#: ../web/swat.c:1123
+msgid "Client/Server Password Management"
+msgstr "Ŭstemci/Sunucu Ŝifre Yönetimi"
+
+#: ../web/swat.c:1140
+msgid "Remote Machine"
+msgstr " Uzak Makina"
+
+#: ../web/swat.c:1179
+msgid "Printer Parameters"
+msgstr "Yazŭcŭ Bilgileri"
+
+#: ../web/swat.c:1181
+msgid "Important Note:"
+msgstr "Önemli Not:"
+
+#: ../web/swat.c:1182
+msgid "Printer names marked with [*] in the Choose Printer drop-down box "
+msgstr "Yazŭcŭ Seç kutusunda [*] ile iŝaretlenmiŝ yazŭcŭ isimleri "
+
+#: ../web/swat.c:1183
+msgid "are autoloaded printers from "
+msgstr "otomatik yüklenen yazŭcŭlar "
+
+#: ../web/swat.c:1184
+msgid "Printcap Name"
+msgstr "Printcap Adŭ"
+
+#: ../web/swat.c:1185
+msgid "Attempting to delete these printers from SWAT will have no effect."
+msgstr "Bu yazŭcŭlarŭ SWAT'dan silmek etkisiz olacaktŭr.\n"
+
+#: ../web/swat.c:1231
+msgid "Choose Printer"
+msgstr "Yazŭcŭ Seç"
+
+#: ../web/swat.c:1250
+msgid "Delete Printer"
+msgstr "Yazŭcŭ Sil"
+
+#: ../web/swat.c:1257
+msgid "Create Printer"
+msgstr "Yazŭcŭ Oluŝtur"
+
+#: ../web/statuspage.c:123
+msgid "RDONLY "
+msgstr "SALTOKUNUR "
+
+#: ../web/statuspage.c:124
+msgid "WRONLY "
+msgstr "SALTYAZILIR "
+
+#: ../web/statuspage.c:125
+msgid "RDWR "
+msgstr "O/Y "
+
+#: ../web/statuspage.c:309
+msgid "Server Status"
+msgstr "Sunucu Durumu"
+
+#: ../web/statuspage.c:314
+msgid "Auto Refresh"
+msgstr "Oto Tazele"
+
+#: ../web/statuspage.c:315 ../web/statuspage.c:320
+msgid "Refresh Interval: "
+msgstr "Tazeleme Aralŭŭ: "
+
+#: ../web/statuspage.c:319
+msgid "Stop Refreshing"
+msgstr "Tazelemeyi Durdur"
+
+#: ../web/statuspage.c:334
+msgid "version:"
+msgstr "sürüm:"
+
+#: ../web/statuspage.c:337
+msgid "smbd:"
+msgstr "smbd:"
+
+#: ../web/statuspage.c:337 ../web/statuspage.c:350 ../web/statuspage.c:364
+msgid "running"
+msgstr "çalŭŝŭyor"
+
+#: ../web/statuspage.c:337 ../web/statuspage.c:350 ../web/statuspage.c:364
+msgid "not running"
+msgstr "çalŭŝmŭyor"
+
+#: ../web/statuspage.c:341
+msgid "Stop smbd"
+msgstr "Smbd'yi durdur"
+
+#: ../web/statuspage.c:343
+msgid "Start smbd"
+msgstr "Smbd'yi çalŭŝtŭr"
+
+#: ../web/statuspage.c:345
+msgid "Restart smbd"
+msgstr "Smbd'yi yeniden çalŭŝtŭr"
+
+#: ../web/statuspage.c:350
+msgid "nmbd:"
+msgstr "nmbd:"
+
+#: ../web/statuspage.c:354
+msgid "Stop nmbd"
+msgstr "Nmbd'yi durdur"
+
+#: ../web/statuspage.c:356
+msgid "Start nmbd"
+msgstr "Nmbd'yi çalŭŝtŭr"
+
+#: ../web/statuspage.c:358
+msgid "Restart nmbd"
+msgstr "Nmbd'yi yeniden çalŭŝtŭr"
+
+#: ../web/statuspage.c:364
+msgid "winbindd:"
+msgstr "winbind uid"
+
+#: ../web/statuspage.c:368
+msgid "Stop winbindd"
+msgstr "Nmbd'yi durdur"
+
+#: ../web/statuspage.c:370
+msgid "Start winbindd"
+msgstr "Nmbd'yi çalŭŝtŭr"
+
+#: ../web/statuspage.c:372
+msgid "Restart winbindd"
+msgstr "Nmbd'yi yeniden çalŭŝtŭr"
+
+#. stop, restart all
+#: ../web/statuspage.c:381
+msgid "Stop All"
+msgstr ""
+
+#: ../web/statuspage.c:382
+msgid "Restart All"
+msgstr "Nmbd'yi yeniden çalŭŝtŭr"
+
+#. start all
+#: ../web/statuspage.c:386
+msgid "Start All"
+msgstr "Nmbd'yi çalŭŝtŭr"
+
+#: ../web/statuspage.c:393
+msgid "Active Connections"
+msgstr "Aktif Balantŭlar"
+
+#: ../web/statuspage.c:395 ../web/statuspage.c:408 ../web/statuspage.c:416
+msgid "PID"
+msgstr "PID"
+
+#: ../web/statuspage.c:395 ../web/statuspage.c:408
+msgid "Client"
+msgstr "Ŭstemci"
+
+#: ../web/statuspage.c:395
+msgid "IP address"
+msgstr "IP numarasŭ"
+
+#: ../web/statuspage.c:395 ../web/statuspage.c:408 ../web/statuspage.c:416
+msgid "Date"
+msgstr "Tarih"
+
+#: ../web/statuspage.c:397
+msgid "Kill"
+msgstr "Kapat"
+
+#: ../web/statuspage.c:405
+msgid "Active Shares"
+msgstr "Aktif Paylaŝŭmlar"
+
+#: ../web/statuspage.c:408
+msgid "Share"
+msgstr "Paylaŝŭm"
+
+#: ../web/statuspage.c:408
+msgid "User"
+msgstr "Kullanŭcŭ"
+
+#: ../web/statuspage.c:408
+msgid "Group"
+msgstr "Grup"
+
+#: ../web/statuspage.c:414
+msgid "Open Files"
+msgstr "Açŭk Dosyalar"
+
+#: ../web/statuspage.c:416
+msgid "Sharing"
+msgstr "Paylaŝŭlŭyor"
+
+#: ../web/statuspage.c:416
+msgid "R/W"
+msgstr "O/Y"
+
+#: ../web/statuspage.c:416
+msgid "Oplock"
+msgstr "Oplock"
+
+#: ../web/statuspage.c:416
+msgid "File"
+msgstr "Dosya"
+
+#: ../web/statuspage.c:425
+msgid "Show Client in col 1"
+msgstr ""
+
+#: ../web/statuspage.c:426
+msgid "Show PID in col 1"
+msgstr ""
+
+#: ../param/loadparm.c:755
+msgid "Base Options"
+msgstr "Temel Seçenekler"
+
+#: ../param/loadparm.c:775
+msgid "Security Options"
+msgstr "Güvenlik Seçenekleri"
+
+#: ../param/loadparm.c:859
+msgid "Logging Options"
+msgstr "Günlük Kaydŭ Seçenekleri"
+
+#: ../param/loadparm.c:874
+msgid "Protocol Options"
+msgstr "Protokol Seçenekleri"
+
+#: ../param/loadparm.c:911
+msgid "Tuning Options"
+msgstr "Ayar Seçenekleri"
+
+#: ../param/loadparm.c:940
+msgid "Printing Options"
+msgstr "Yazdŭrma Seçenekleri"
+
+#: ../param/loadparm.c:970
+msgid "Filename Handling"
+msgstr "Dosyaadŭ Ŭŝlenmesi"
+
+#: ../param/loadparm.c:996
+msgid "Domain Options"
+msgstr "Alan Seçenekleri"
+
+#: ../param/loadparm.c:1000
+msgid "Logon Options"
+msgstr "Sistem Giriŝ Seçenekleri"
+
+#: ../param/loadparm.c:1019
+msgid "Browse Options"
+msgstr "Gözatma Seçenekleri"
+
+#: ../param/loadparm.c:1033
+msgid "WINS Options"
+msgstr "WINS Seçenekleri"
+
+#: ../param/loadparm.c:1043
+msgid "Locking Options"
+msgstr "Kilitleme Seçenekleri"
+
+#: ../param/loadparm.c:1061
+msgid "Ldap Options"
+msgstr "Ldap Seçenekleri"
+
+#: ../param/loadparm.c:1078
+msgid "Miscellaneous Options"
+msgstr "Dier Seçenekler"
+
+#: ../param/loadparm.c:1138
+msgid "VFS module options"
+msgstr "VFS Seçenekleri"
+
+#: ../param/loadparm.c:1148
+msgid "Winbind options"
+msgstr "Winbind seçenekleri"
diff --git a/source/popt/.cvsignore b/source/popt/.cvsignore
new file mode 100644
index 00000000000..86b08b58d2c
--- /dev/null
+++ b/source/popt/.cvsignore
@@ -0,0 +1,8 @@
+ID
+Makefile
+config.cache
+config.h
+config.log
+config.status
+rsync
+zlib/dummy
diff --git a/source/popt/CHANGES b/source/popt/CHANGES
new file mode 100644
index 00000000000..b6ab2aa3088
--- /dev/null
+++ b/source/popt/CHANGES
@@ -0,0 +1,43 @@
+1.3 ->
+ - heavy dose of const's
+ - poptParseArgvString() now NULL terminates the list
+
+1.2.3 -> 1.3
+ - added support for single -
+ - misc bug fixes
+ - portability improvements
+
+1.2.2 -> 1.2.3
+ - fixed memset() in help message generation (Dale Hawkins)
+ - added extern "C" stuff to popt.h for C++ compilers (Dale Hawkins)
+ - const'ified poptParseArgvString (Jeff Garzik)
+
+1.2.1 -> 1.2.2
+ - fixed bug in chaind alias happens which seems to have only
+ affected --triggers in rpm
+ - added POPT_ARG_VAL
+ - popt.3 installed by default
+
+1.2 -> 1.2.1
+ - added POPT_ARG_INTL_DOMAIN (Elliot Lee)
+ - updated Makefile's to be more GNUish (Elliot Lee)
+
+1.1 -> 1.2
+ - added popt.3 man page (Robert Lynch)
+ - don't use mmap anymore (its lack of portability isn't worth the
+ trouble)
+ - added test script
+ - added support for exec
+ - removed support for *_POPT_ALIASES env variable -- it was a bad
+ idea
+ - reorganized into multiple source files
+ - added automatic help generation, POPT_AUTOHELP
+ - added table callbacks
+ - added table inclusion
+ - updated man page for new features
+ - added test scripts
+
+1.0 -> 1.1
+ - moved to autoconf (Fred Fish)
+ - added STRERROR replacement (Norbert Warmuth)
+ - added const keywords (Bruce Perens)
diff --git a/source/popt/COPYING b/source/popt/COPYING
new file mode 100644
index 00000000000..b4c7ca876c6
--- /dev/null
+++ b/source/popt/COPYING
@@ -0,0 +1,22 @@
+Copyright (c) 1998 Red Hat Software
+
+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
+X CONSORTIUM 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.
+
+Except as contained in this notice, the name of the X Consortium shall not be
+used in advertising or otherwise to promote the sale, use or other dealings
+in this Software without prior written authorization from the X Consortium.
diff --git a/source/popt/README b/source/popt/README
new file mode 100644
index 00000000000..7fccc836ffa
--- /dev/null
+++ b/source/popt/README
@@ -0,0 +1,18 @@
+This is the popt command line option parsing library. While it is similiar
+to getopt(3), it contains a number of enhancements, including:
+
+ 1) popt is fully reentrant
+ 2) popt can parse arbitrary argv[] style arrays while
+ getopt(2) makes this quite difficult
+ 3) popt allows users to alias command line arguments
+ 4) popt provides convience functions for parsting strings
+ into argv[] style arrays
+
+popt is used by rpm, the Red Hat install program, and many other Red Hat
+utilities, all of which provide excellent examples of how to use popt.
+Complete documentation on popt is available in popt.ps (included in this
+tarball), which is excerpted with permission from the book "Linux
+Application Development" by Michael K. Johnson and Erik Troan (availble
+from Addison Wesley in May, 1998).
+
+Comments on popt should be addressed to ewt@redhat.com.
diff --git a/source/popt/dummy.in b/source/popt/dummy.in
new file mode 100644
index 00000000000..e69de29bb2d
--- /dev/null
+++ b/source/popt/dummy.in
diff --git a/source/popt/findme.c b/source/popt/findme.c
new file mode 100644
index 00000000000..f2ad05bb3fb
--- /dev/null
+++ b/source/popt/findme.c
@@ -0,0 +1,46 @@
+/* (C) 1998 Red Hat Software, Inc. -- Licensing details are in the COPYING
+ file accompanying popt source distributions, available from
+ ftp://ftp.redhat.com/pub/code/popt */
+
+#include "system.h"
+#include "findme.h"
+
+const char * findProgramPath(const char * argv0) {
+ char * path = getenv("PATH");
+ char * pathbuf;
+ char * start, * chptr;
+ char * buf, *local = NULL;
+
+ /* If there is a / in the argv[0], it has to be an absolute
+ path */
+ if (strchr(argv0, '/'))
+ return xstrdup(argv0);
+
+ if (!path) return NULL;
+
+ local = start = pathbuf = malloc(strlen(path) + 1);
+ buf = malloc(strlen(path) + strlen(argv0) + 2);
+ strcpy(pathbuf, path);
+
+ chptr = NULL;
+ do {
+ if ((chptr = strchr(start, ':')))
+ *chptr = '\0';
+ sprintf(buf, "%s/%s", start, argv0);
+
+ if (!access(buf, X_OK)) {
+ if (local) free(local);
+ return buf;
+ }
+
+ if (chptr)
+ start = chptr + 1;
+ else
+ start = NULL;
+ } while (start && *start);
+
+ free(buf);
+ if (local) free(local);
+
+ return NULL;
+}
diff --git a/source/popt/findme.h b/source/popt/findme.h
new file mode 100644
index 00000000000..5e93963d603
--- /dev/null
+++ b/source/popt/findme.h
@@ -0,0 +1,10 @@
+/* (C) 1998 Red Hat Software, Inc. -- Licensing details are in the COPYING
+ file accompanying popt source distributions, available from
+ ftp://ftp.redhat.com/pub/code/popt */
+
+#ifndef H_FINDME
+#define H_FINDME
+
+const char * findProgramPath(const char * argv0);
+
+#endif
diff --git a/source/popt/popt.c b/source/popt/popt.c
new file mode 100644
index 00000000000..9fa8650312c
--- /dev/null
+++ b/source/popt/popt.c
@@ -0,0 +1,782 @@
+/* (C) 1998 Red Hat Software, Inc. -- Licensing details are in the COPYING
+ file accompanying popt source distributions, available from
+ ftp://ftp.redhat.com/pub/code/popt */
+
+#include "system.h"
+#include "findme.h"
+#include "poptint.h"
+
+#ifndef HAVE_STRERROR
+static char * strerror(int errno) {
+ extern int sys_nerr;
+ extern char * sys_errlist[];
+
+ if ((0 <= errno) && (errno < sys_nerr))
+ return sys_errlist[errno];
+ else
+ return POPT_("unknown errno");
+}
+#endif
+
+void poptSetExecPath(poptContext con, const char * path, int allowAbsolute) {
+ if (con->execPath) xfree(con->execPath);
+ con->execPath = xstrdup(path);
+ con->execAbsolute = allowAbsolute;
+}
+
+static void invokeCallbacks(poptContext con, const struct poptOption * table,
+ int post) {
+ const struct poptOption * opt = table;
+ poptCallbackType cb;
+
+ while (opt->longName || opt->shortName || opt->arg) {
+ if ((opt->argInfo & POPT_ARG_MASK) == POPT_ARG_INCLUDE_TABLE) {
+ invokeCallbacks(con, opt->arg, post);
+ } else if (((opt->argInfo & POPT_ARG_MASK) == POPT_ARG_CALLBACK) &&
+ ((!post && (opt->argInfo & POPT_CBFLAG_PRE)) ||
+ ( post && (opt->argInfo & POPT_CBFLAG_POST)))) {
+ cb = (poptCallbackType)opt->arg;
+ cb(con, post ? POPT_CALLBACK_REASON_POST : POPT_CALLBACK_REASON_PRE,
+ NULL, NULL, opt->descrip);
+ }
+ opt++;
+ }
+}
+
+poptContext poptGetContext(const char * name, int argc, const char ** argv,
+ const struct poptOption * options, int flags) {
+ poptContext con = malloc(sizeof(*con));
+
+ memset(con, 0, sizeof(*con));
+
+ con->os = con->optionStack;
+ con->os->argc = argc;
+ con->os->argv = argv;
+ con->os->argb = NULL;
+
+ if (!(flags & POPT_CONTEXT_KEEP_FIRST))
+ con->os->next = 1; /* skip argv[0] */
+
+ con->leftovers = calloc( (argc + 1), sizeof(char *) );
+ con->options = options;
+ con->aliases = NULL;
+ con->numAliases = 0;
+ con->flags = flags;
+ con->execs = NULL;
+ con->numExecs = 0;
+ con->finalArgvAlloced = argc * 2;
+ con->finalArgv = calloc( con->finalArgvAlloced, sizeof(*con->finalArgv) );
+ con->execAbsolute = 1;
+ con->arg_strip = NULL;
+
+ if (getenv("POSIXLY_CORRECT") || getenv("POSIX_ME_HARDER"))
+ con->flags |= POPT_CONTEXT_POSIXMEHARDER;
+
+ if (name)
+ con->appName = strcpy(malloc(strlen(name) + 1), name);
+
+ invokeCallbacks(con, con->options, 0);
+
+ return con;
+}
+
+static void cleanOSE(struct optionStackEntry *os)
+{
+ if (os->nextArg) {
+ xfree(os->nextArg);
+ os->nextArg = NULL;
+ }
+ if (os->argv) {
+ xfree(os->argv);
+ os->argv = NULL;
+ }
+ if (os->argb) {
+ PBM_FREE(os->argb);
+ os->argb = NULL;
+ }
+}
+
+void poptResetContext(poptContext con) {
+ int i;
+
+ while (con->os > con->optionStack) {
+ cleanOSE(con->os--);
+ }
+ if (con->os->argb) {
+ PBM_FREE(con->os->argb);
+ con->os->argb = NULL;
+ }
+ con->os->currAlias = NULL;
+ con->os->nextCharArg = NULL;
+ con->os->nextArg = NULL;
+ con->os->next = 1; /* skip argv[0] */
+
+ con->numLeftovers = 0;
+ con->nextLeftover = 0;
+ con->restLeftover = 0;
+ con->doExec = NULL;
+
+ for (i = 0; i < con->finalArgvCount; i++) {
+ if (con->finalArgv[i]) {
+ xfree(con->finalArgv[i]);
+ con->finalArgv[i] = NULL;
+ }
+ }
+
+ con->finalArgvCount = 0;
+
+ if (con->arg_strip) {
+ PBM_FREE(con->arg_strip);
+ con->arg_strip = NULL;
+ }
+}
+
+/* Only one of longName, shortName may be set at a time */
+static int handleExec(poptContext con, char * longName, char shortName) {
+ int i;
+
+ i = con->numExecs - 1;
+ if (longName) {
+ while (i >= 0 && (!con->execs[i].longName ||
+ strcmp(con->execs[i].longName, longName))) i--;
+ } else {
+ while (i >= 0 &&
+ con->execs[i].shortName != shortName) i--;
+ }
+
+ if (i < 0) return 0;
+
+ if (con->flags & POPT_CONTEXT_NO_EXEC)
+ return 1;
+
+ if (con->doExec == NULL) {
+ con->doExec = con->execs + i;
+ return 1;
+ }
+
+ /* We already have an exec to do; remember this option for next
+ time 'round */
+ if ((con->finalArgvCount + 1) >= (con->finalArgvAlloced)) {
+ con->finalArgvAlloced += 10;
+ con->finalArgv = realloc(con->finalArgv,
+ sizeof(*con->finalArgv) * con->finalArgvAlloced);
+ }
+
+ i = con->finalArgvCount++;
+ { char *s = malloc((longName ? strlen(longName) : 0) + 3);
+ if (longName)
+ sprintf(s, "--%s", longName);
+ else
+ sprintf(s, "-%c", shortName);
+ con->finalArgv[i] = s;
+ }
+
+ return 1;
+}
+
+/* Only one of longName, shortName may be set at a time */
+static int handleAlias(poptContext con, const char * longName, char shortName,
+ /*@keep@*/ const char * nextCharArg) {
+ int i;
+
+ if (con->os->currAlias && con->os->currAlias->longName && longName &&
+ !strcmp(con->os->currAlias->longName, longName))
+ return 0;
+ if (con->os->currAlias && shortName &&
+ shortName == con->os->currAlias->shortName)
+ return 0;
+
+ i = con->numAliases - 1;
+ if (longName) {
+ while (i >= 0 && (!con->aliases[i].longName ||
+ strcmp(con->aliases[i].longName, longName))) i--;
+ } else {
+ while (i >= 0 &&
+ con->aliases[i].shortName != shortName) i--;
+ }
+
+ if (i < 0) return 0;
+
+ if ((con->os - con->optionStack + 1) == POPT_OPTION_DEPTH)
+ return POPT_ERROR_OPTSTOODEEP;
+
+ if (nextCharArg && *nextCharArg)
+ con->os->nextCharArg = nextCharArg;
+
+ con->os++;
+ con->os->next = 0;
+ con->os->stuffed = 0;
+ con->os->nextArg = NULL;
+ con->os->nextCharArg = NULL;
+ con->os->currAlias = con->aliases + i;
+ poptDupArgv(con->os->currAlias->argc, con->os->currAlias->argv,
+ &con->os->argc, &con->os->argv);
+ con->os->argb = NULL;
+
+ return 1;
+}
+
+static void execCommand(poptContext con) {
+ const char ** argv;
+ int pos = 0;
+ const char * script = con->doExec->script;
+
+ argv = malloc(sizeof(*argv) *
+ (6 + con->numLeftovers + con->finalArgvCount));
+
+ if (!con->execAbsolute && strchr(script, '/')) return;
+
+ if (!strchr(script, '/') && con->execPath) {
+ char *s = malloc(strlen(con->execPath) + strlen(script) + 2);
+ sprintf(s, "%s/%s", con->execPath, script);
+ argv[pos] = s;
+ } else {
+ argv[pos] = script;
+ }
+ pos++;
+
+ argv[pos] = findProgramPath(con->os->argv[0]);
+ if (argv[pos]) pos++;
+ argv[pos++] = ";";
+
+ memcpy(argv + pos, con->finalArgv, sizeof(*argv) * con->finalArgvCount);
+ pos += con->finalArgvCount;
+
+ if (con->numLeftovers) {
+ argv[pos++] = "--";
+ memcpy(argv + pos, con->leftovers, sizeof(*argv) * con->numLeftovers);
+ pos += con->numLeftovers;
+ }
+
+ argv[pos++] = NULL;
+
+#ifdef __hpux
+ setresuid(getuid(), getuid(),-1);
+#else
+/*
+ * XXX " ... on BSD systems setuid() should be preferred over setreuid()"
+ * XXX sez' Timur Bakeyev <mc@bat.ru>
+ * XXX from Norbert Warmuth <nwarmuth@privat.circular.de>
+ */
+#if defined(HAVE_SETUID)
+ setuid(getuid());
+#elif defined (HAVE_SETREUID)
+ setreuid(getuid(), getuid()); /*hlauer: not portable to hpux9.01 */
+#else
+ ; /* Can't drop privileges */
+#endif
+#endif
+
+ execvp(argv[0], (char *const *)argv);
+}
+
+/*@observer@*/ static const struct poptOption *
+findOption(const struct poptOption * table, const char * longName,
+ char shortName,
+ /*@out@*/ poptCallbackType * callback, /*@out@*/ const void ** callbackData,
+ int singleDash)
+{
+ const struct poptOption * opt = table;
+ const struct poptOption * opt2;
+ const struct poptOption * cb = NULL;
+
+ /* This happens when a single - is given */
+ if (singleDash && !shortName && !*longName)
+ shortName = '-';
+
+ while (opt->longName || opt->shortName || opt->arg) {
+ if ((opt->argInfo & POPT_ARG_MASK) == POPT_ARG_INCLUDE_TABLE) {
+ opt2 = findOption(opt->arg, longName, shortName, callback,
+ callbackData, singleDash);
+ if (opt2) {
+ if (*callback && !*callbackData)
+ *callbackData = opt->descrip;
+ return opt2;
+ }
+ } else if ((opt->argInfo & POPT_ARG_MASK) == POPT_ARG_CALLBACK) {
+ cb = opt;
+ } else if (longName && opt->longName &&
+ (!singleDash || (opt->argInfo & POPT_ARGFLAG_ONEDASH)) &&
+ !strcmp(longName, opt->longName)) {
+ break;
+ } else if (shortName && shortName == opt->shortName) {
+ break;
+ }
+ opt++;
+ }
+
+ if (!opt->longName && !opt->shortName) return NULL;
+ *callbackData = NULL;
+ *callback = NULL;
+ if (cb) {
+ *callback = (poptCallbackType)cb->arg;
+ if (!(cb->argInfo & POPT_CBFLAG_INC_DATA))
+ *callbackData = cb->descrip;
+ }
+
+ return opt;
+}
+
+static const char *findNextArg(poptContext con, unsigned argx, int delete)
+{
+ struct optionStackEntry * os = con->os;
+ const char * arg;
+
+ do {
+ int i;
+ arg = NULL;
+ while (os->next == os->argc && os > con->optionStack) os--;
+ if (os->next == os->argc && os == con->optionStack) break;
+ for (i = os->next; i < os->argc; i++) {
+ if (os->argb && PBM_ISSET(i, os->argb)) continue;
+ if (*os->argv[i] == '-') continue;
+ if (--argx > 0) continue;
+ arg = os->argv[i];
+ if (delete) {
+ if (os->argb == NULL) os->argb = PBM_ALLOC(os->argc);
+ PBM_SET(i, os->argb);
+ }
+ break;
+ }
+ if (os > con->optionStack) os--;
+ } while (arg == NULL);
+ return arg;
+}
+
+static /*@only@*/ const char * expandNextArg(poptContext con, const char * s)
+{
+ const char *a;
+ size_t alen;
+ char *t, *te;
+ size_t tn = strlen(s) + 1;
+ char c;
+
+ te = t = malloc(tn);;
+ while ((c = *s++) != '\0') {
+ switch (c) {
+#if 0 /* XXX can't do this */
+ case '\\': /* escape */
+ c = *s++;
+ break;
+#endif
+ case '!':
+ if (!(s[0] == '#' && s[1] == ':' && s[2] == '+'))
+ break;
+ if ((a = findNextArg(con, 1, 1)) == NULL)
+ break;
+ s += 3;
+
+ alen = strlen(a);
+ tn += alen;
+ *te = '\0';
+ t = realloc(t, tn);
+ te = t + strlen(t);
+ strncpy(te, a, alen); te += alen;
+ continue;
+ /*@notreached@*/ break;
+ default:
+ break;
+ }
+ *te++ = c;
+ }
+ *te = '\0';
+ t = realloc(t, strlen(t)+1); /* XXX memory leak, hard to plug */
+ return t;
+}
+
+static void poptStripArg(poptContext con, int which)
+{
+ if(con->arg_strip == NULL) {
+ con->arg_strip = PBM_ALLOC(con->optionStack[0].argc);
+ }
+ PBM_SET(which, con->arg_strip);
+}
+
+/* returns 'val' element, -1 on last item, POPT_ERROR_* on error */
+int poptGetNextOpt(poptContext con)
+{
+ const struct poptOption * opt = NULL;
+ int done = 0;
+
+ /* looks a bit tricky to get rid of alloca properly in this fn */
+#if HAVE_ALLOCA_H
+#define ALLOCA(x) alloca(x)
+#else
+#define ALLOCA(x) malloc(x)
+#endif
+
+
+ while (!done) {
+ const char * origOptString = NULL;
+ poptCallbackType cb = NULL;
+ const void * cbData = NULL;
+ const char * longArg = NULL;
+ int canstrip = 0;
+
+ while (!con->os->nextCharArg && con->os->next == con->os->argc
+ && con->os > con->optionStack) {
+ cleanOSE(con->os--);
+ }
+ if (!con->os->nextCharArg && con->os->next == con->os->argc) {
+ invokeCallbacks(con, con->options, 1);
+ if (con->doExec) execCommand(con);
+ return -1;
+ }
+
+ /* Process next long option */
+ if (!con->os->nextCharArg) {
+ char * localOptString, * optString;
+ int thisopt;
+
+ if (con->os->argb && PBM_ISSET(con->os->next, con->os->argb)) {
+ con->os->next++;
+ continue;
+ }
+ thisopt=con->os->next;
+ origOptString = con->os->argv[con->os->next++];
+
+ if (con->restLeftover || *origOptString != '-') {
+ con->leftovers[con->numLeftovers++] = origOptString;
+ if (con->flags & POPT_CONTEXT_POSIXMEHARDER)
+ con->restLeftover = 1;
+ continue;
+ }
+
+ /* Make a copy we can hack at */
+ localOptString = optString =
+ strcpy(ALLOCA(strlen(origOptString) + 1),
+ origOptString);
+
+ if (!optString[0])
+ return POPT_ERROR_BADOPT;
+
+ if (optString[1] == '-' && !optString[2]) {
+ con->restLeftover = 1;
+ continue;
+ } else {
+ char *oe;
+ int singleDash;
+
+ optString++;
+ if (*optString == '-')
+ singleDash = 0, optString++;
+ else
+ singleDash = 1;
+
+ /* XXX aliases with arg substitution need "--alias=arg" */
+ if (handleAlias(con, optString, '\0', NULL))
+ continue;
+ if (handleExec(con, optString, '\0'))
+ continue;
+
+ /* Check for "--long=arg" option. */
+ for (oe = optString; *oe && *oe != '='; oe++)
+ ;
+ if (*oe == '=') {
+ *oe++ = '\0';
+ /* XXX longArg is mapped back to persistent storage. */
+ longArg = origOptString + (oe - localOptString);
+ }
+
+ opt = findOption(con->options, optString, '\0', &cb, &cbData,
+ singleDash);
+ if (!opt && !singleDash)
+ return POPT_ERROR_BADOPT;
+ }
+
+ if (!opt) {
+ con->os->nextCharArg = origOptString + 1;
+ } else {
+ if(con->os == con->optionStack &&
+ opt->argInfo & POPT_ARGFLAG_STRIP) {
+ canstrip = 1;
+ poptStripArg(con, thisopt);
+ }
+ }
+ }
+
+ /* Process next short option */
+ if (con->os->nextCharArg) {
+ origOptString = con->os->nextCharArg;
+
+ con->os->nextCharArg = NULL;
+
+ if (handleAlias(con, NULL, *origOptString,
+ origOptString + 1)) {
+ origOptString++;
+ continue;
+ }
+ if (handleExec(con, NULL, *origOptString))
+ continue;
+
+ opt = findOption(con->options, NULL, *origOptString, &cb,
+ &cbData, 0);
+ if (!opt)
+ return POPT_ERROR_BADOPT;
+
+ origOptString++;
+ if (*origOptString)
+ con->os->nextCharArg = origOptString;
+ }
+
+ if (opt->arg && (opt->argInfo & POPT_ARG_MASK) == POPT_ARG_NONE) {
+ *((int *)opt->arg) = 1;
+ } else if ((opt->argInfo & POPT_ARG_MASK) == POPT_ARG_VAL) {
+ if (opt->arg)
+ *((int *) opt->arg) = opt->val;
+ } else if ((opt->argInfo & POPT_ARG_MASK) != POPT_ARG_NONE) {
+ if (con->os->nextArg) {
+ xfree(con->os->nextArg);
+ con->os->nextArg = NULL;
+ }
+ if (longArg) {
+ con->os->nextArg = expandNextArg(con, longArg);
+ } else if (con->os->nextCharArg) {
+ con->os->nextArg = expandNextArg(con, con->os->nextCharArg);
+ con->os->nextCharArg = NULL;
+ } else {
+ while (con->os->next == con->os->argc &&
+ con->os > con->optionStack) {
+ cleanOSE(con->os--);
+ }
+ if (con->os->next == con->os->argc)
+ return POPT_ERROR_NOARG;
+
+ /* make sure this isn't part of a short arg or the
+ result of an alias expansion */
+ if(con->os == con->optionStack &&
+ opt->argInfo & POPT_ARGFLAG_STRIP &&
+ canstrip) {
+ poptStripArg(con, con->os->next);
+ }
+
+ con->os->nextArg = expandNextArg(con, con->os->argv[con->os->next++]);
+ }
+
+ if (opt->arg) {
+ long aLong;
+ char *end;
+
+ switch (opt->argInfo & POPT_ARG_MASK) {
+ case POPT_ARG_STRING:
+ /* XXX memory leak, hard to plug */
+ *((const char **) opt->arg) = xstrdup(con->os->nextArg);
+ break;
+
+ case POPT_ARG_INT:
+ case POPT_ARG_LONG:
+ aLong = strtol(con->os->nextArg, &end, 0);
+ if (!(end && *end == '\0'))
+ return POPT_ERROR_BADNUMBER;
+
+ if (aLong == LONG_MIN || aLong == LONG_MAX)
+ return POPT_ERROR_OVERFLOW;
+ if ((opt->argInfo & POPT_ARG_MASK) == POPT_ARG_LONG) {
+ *((long *) opt->arg) = aLong;
+ } else {
+ if (aLong > INT_MAX || aLong < INT_MIN)
+ return POPT_ERROR_OVERFLOW;
+ *((int *) opt->arg) = aLong;
+ }
+ break;
+
+ default:
+ fprintf(stdout, POPT_("option type (%d) not implemented in popt\n"),
+ opt->argInfo & POPT_ARG_MASK);
+ exit(EXIT_FAILURE);
+ }
+ }
+ }
+
+ if (cb)
+ cb(con, POPT_CALLBACK_REASON_OPTION, opt, con->os->nextArg, cbData);
+ else if (opt->val && ((opt->argInfo & POPT_ARG_MASK) != POPT_ARG_VAL))
+ done = 1;
+
+ if ((con->finalArgvCount + 2) >= (con->finalArgvAlloced)) {
+ con->finalArgvAlloced += 10;
+ con->finalArgv = realloc(con->finalArgv,
+ sizeof(*con->finalArgv) * con->finalArgvAlloced);
+ }
+
+ { char *s = malloc((opt->longName ? strlen(opt->longName) : 0) + 3);
+ if (opt->longName)
+ sprintf(s, "--%s", opt->longName);
+ else
+ sprintf(s, "-%c", opt->shortName);
+ con->finalArgv[con->finalArgvCount++] = s;
+ }
+
+ if (opt->arg && (opt->argInfo & POPT_ARG_MASK) != POPT_ARG_NONE
+ && (opt->argInfo & POPT_ARG_MASK) != POPT_ARG_VAL) {
+ con->finalArgv[con->finalArgvCount++] = xstrdup(con->os->nextArg);
+ }
+ }
+
+ return opt->val;
+}
+
+const char * poptGetOptArg(poptContext con) {
+ const char * ret = con->os->nextArg;
+ con->os->nextArg = NULL;
+ return ret;
+}
+
+const char * poptGetArg(poptContext con) {
+ if (con->numLeftovers == con->nextLeftover) return NULL;
+ return con->leftovers[con->nextLeftover++];
+}
+
+const char * poptPeekArg(poptContext con) {
+ if (con->numLeftovers == con->nextLeftover) return NULL;
+ return con->leftovers[con->nextLeftover];
+}
+
+const char ** poptGetArgs(poptContext con) {
+ if (con->numLeftovers == con->nextLeftover) return NULL;
+
+ /* some apps like [like RPM ;-) ] need this NULL terminated */
+ con->leftovers[con->numLeftovers] = NULL;
+
+ return (con->leftovers + con->nextLeftover);
+}
+
+void poptFreeContext(poptContext con) {
+ int i;
+
+ poptResetContext(con);
+ if (con->os->argb) free(con->os->argb);
+
+ for (i = 0; i < con->numAliases; i++) {
+ if (con->aliases[i].longName) xfree(con->aliases[i].longName);
+ free(con->aliases[i].argv);
+ }
+
+ for (i = 0; i < con->numExecs; i++) {
+ if (con->execs[i].longName) xfree(con->execs[i].longName);
+ xfree(con->execs[i].script);
+ }
+ if (con->execs) xfree(con->execs);
+
+ free(con->leftovers);
+ free(con->finalArgv);
+ if (con->appName) xfree(con->appName);
+ if (con->aliases) free(con->aliases);
+ if (con->otherHelp) xfree(con->otherHelp);
+ if (con->execPath) xfree(con->execPath);
+ if (con->arg_strip) PBM_FREE(con->arg_strip);
+
+ free(con);
+}
+
+int poptAddAlias(poptContext con, struct poptAlias newAlias,
+ /*@unused@*/ int flags)
+{
+ int aliasNum = con->numAliases++;
+ struct poptAlias * alias;
+
+ /* SunOS won't realloc(NULL, ...) */
+ if (!con->aliases)
+ con->aliases = malloc(sizeof(newAlias) * con->numAliases);
+ else
+ con->aliases = realloc(con->aliases,
+ sizeof(newAlias) * con->numAliases);
+ alias = con->aliases + aliasNum;
+
+ alias->longName = (newAlias.longName)
+ ? strcpy(malloc(strlen(newAlias.longName) + 1), newAlias.longName)
+ : NULL;
+ alias->shortName = newAlias.shortName;
+ alias->argc = newAlias.argc;
+ alias->argv = newAlias.argv;
+
+ return 0;
+}
+
+const char * poptBadOption(poptContext con, int flags) {
+ struct optionStackEntry * os;
+
+ if (flags & POPT_BADOPTION_NOALIAS)
+ os = con->optionStack;
+ else
+ os = con->os;
+
+ return os->argv[os->next - 1];
+}
+
+#define POPT_ERROR_NOARG -10
+#define POPT_ERROR_BADOPT -11
+#define POPT_ERROR_OPTSTOODEEP -13
+#define POPT_ERROR_BADQUOTE -15 /* only from poptParseArgString() */
+#define POPT_ERROR_ERRNO -16 /* only from poptParseArgString() */
+
+const char *poptStrerror(const int error) {
+ switch (error) {
+ case POPT_ERROR_NOARG:
+ return POPT_("missing argument");
+ case POPT_ERROR_BADOPT:
+ return POPT_("unknown option");
+ case POPT_ERROR_OPTSTOODEEP:
+ return POPT_("aliases nested too deeply");
+ case POPT_ERROR_BADQUOTE:
+ return POPT_("error in paramter quoting");
+ case POPT_ERROR_BADNUMBER:
+ return POPT_("invalid numeric value");
+ case POPT_ERROR_OVERFLOW:
+ return POPT_("number too large or too small");
+ case POPT_ERROR_ERRNO:
+ return strerror(errno);
+ default:
+ return POPT_("unknown error");
+ }
+}
+
+int poptStuffArgs(poptContext con, const char ** argv) {
+ int argc;
+
+ if ((con->os - con->optionStack) == POPT_OPTION_DEPTH)
+ return POPT_ERROR_OPTSTOODEEP;
+
+ for (argc = 0; argv[argc]; argc++)
+ ;
+
+ con->os++;
+ con->os->next = 0;
+ con->os->nextArg = NULL;
+ con->os->nextCharArg = NULL;
+ con->os->currAlias = NULL;
+ poptDupArgv(argc, argv, &con->os->argc, &con->os->argv);
+ con->os->argb = NULL;
+ con->os->stuffed = 1;
+
+ return 0;
+}
+
+const char * poptGetInvocationName(poptContext con) {
+ return con->os->argv[0];
+}
+
+int poptStrippedArgv(poptContext con, int argc, char **argv)
+{
+ int i,j=1, numargs=argc;
+
+ for(i=1; i<argc; i++) {
+ if(PBM_ISSET(i, con->arg_strip)) {
+ numargs--;
+ }
+ }
+
+ for(i=1; i<argc; i++) {
+ if(PBM_ISSET(i, con->arg_strip)) {
+ continue;
+ } else {
+ if(j<numargs) {
+ argv[j++]=argv[i];
+ } else {
+ argv[j++]='\0';
+ }
+ }
+ }
+
+ return(numargs);
+}
diff --git a/source/popt/popt.h b/source/popt/popt.h
new file mode 100644
index 00000000000..c33cedaec99
--- /dev/null
+++ b/source/popt/popt.h
@@ -0,0 +1,130 @@
+/* (C) 1998 Red Hat Software, Inc. -- Licensing details are in the COPYING
+ file accompanying popt source distributions, available from
+ ftp://ftp.redhat.com/pub/code/popt */
+
+#ifndef H_POPT
+#define H_POPT
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <stdio.h> /* for FILE * */
+
+#define POPT_OPTION_DEPTH 10
+
+#define POPT_ARG_NONE 0
+#define POPT_ARG_STRING 1
+#define POPT_ARG_INT 2
+#define POPT_ARG_LONG 3
+#define POPT_ARG_INCLUDE_TABLE 4 /* arg points to table */
+#define POPT_ARG_CALLBACK 5 /* table-wide callback... must be
+ set first in table; arg points
+ to callback, descrip points to
+ callback data to pass */
+#define POPT_ARG_INTL_DOMAIN 6 /* set the translation domain
+ for this table and any
+ included tables; arg points
+ to the domain string */
+#define POPT_ARG_VAL 7 /* arg should take value val */
+#define POPT_ARG_MASK 0x0000FFFF
+#define POPT_ARGFLAG_ONEDASH 0x80000000 /* allow -longoption */
+#define POPT_ARGFLAG_DOC_HIDDEN 0x40000000 /* don't show in help/usage */
+#define POPT_ARGFLAG_STRIP 0x20000000 /* strip this arg from argv (only applies to long args) */
+#define POPT_CBFLAG_PRE 0x80000000 /* call the callback before parse */
+#define POPT_CBFLAG_POST 0x40000000 /* call the callback after parse */
+#define POPT_CBFLAG_INC_DATA 0x20000000 /* use data from the include line,
+ not the subtable */
+
+#define POPT_ERROR_NOARG -10
+#define POPT_ERROR_BADOPT -11
+#define POPT_ERROR_OPTSTOODEEP -13
+#define POPT_ERROR_BADQUOTE -15 /* only from poptParseArgString() */
+#define POPT_ERROR_ERRNO -16 /* only from poptParseArgString() */
+#define POPT_ERROR_BADNUMBER -17
+#define POPT_ERROR_OVERFLOW -18
+
+/* poptBadOption() flags */
+#define POPT_BADOPTION_NOALIAS (1 << 0) /* don't go into an alias */
+
+/* poptGetContext() flags */
+#define POPT_CONTEXT_NO_EXEC (1 << 0) /* ignore exec expansions */
+#define POPT_CONTEXT_KEEP_FIRST (1 << 1) /* pay attention to argv[0] */
+#define POPT_CONTEXT_POSIXMEHARDER (1 << 2) /* options can't follow args */
+
+struct poptOption {
+ /*@observer@*/ /*@null@*/ const char * longName; /* may be NULL */
+ char shortName; /* may be '\0' */
+ int argInfo;
+ /*@shared@*/ /*@null@*/ void * arg; /* depends on argInfo */
+ int val; /* 0 means don't return, just update flag */
+ /*@shared@*/ /*@null@*/ const char * descrip; /* description for autohelp -- may be NULL */
+ /*@shared@*/ /*@null@*/ const char * argDescrip; /* argument description for autohelp */
+};
+
+struct poptAlias {
+ /*@owned@*/ /*@null@*/ const char * longName; /* may be NULL */
+ char shortName; /* may be '\0' */
+ int argc;
+ /*@owned@*/ const char ** argv; /* must be free()able */
+};
+
+extern struct poptOption poptHelpOptions[];
+#define POPT_AUTOHELP { NULL, '\0', POPT_ARG_INCLUDE_TABLE, poptHelpOptions, \
+ 0, "Help options", NULL },
+
+typedef struct poptContext_s * poptContext;
+#ifndef __cplusplus
+typedef struct poptOption * poptOption;
+#endif
+
+enum poptCallbackReason { POPT_CALLBACK_REASON_PRE,
+ POPT_CALLBACK_REASON_POST,
+ POPT_CALLBACK_REASON_OPTION };
+typedef void (*poptCallbackType)(poptContext con,
+ enum poptCallbackReason reason,
+ const struct poptOption * opt,
+ const char * arg, const void * data);
+
+/*@only@*/ poptContext poptGetContext(/*@keep@*/ const char * name,
+ int argc, /*@keep@*/ const char ** argv,
+ /*@keep@*/ const struct poptOption * options, int flags);
+void poptResetContext(poptContext con);
+
+/* returns 'val' element, -1 on last item, POPT_ERROR_* on error */
+int poptGetNextOpt(poptContext con);
+/* returns NULL if no argument is available */
+/*@observer@*/ /*@null@*/ const char * poptGetOptArg(poptContext con);
+/* returns NULL if no more options are available */
+/*@observer@*/ /*@null@*/ const char * poptGetArg(poptContext con);
+/*@observer@*/ /*@null@*/ const char * poptPeekArg(poptContext con);
+/*@observer@*/ /*@null@*/ const char ** poptGetArgs(poptContext con);
+/* returns the option which caused the most recent error */
+/*@observer@*/ const char * poptBadOption(poptContext con, int flags);
+void poptFreeContext( /*@only@*/ poptContext con);
+int poptStuffArgs(poptContext con, /*@keep@*/ const char ** argv);
+int poptAddAlias(poptContext con, struct poptAlias alias, int flags);
+int poptReadConfigFile(poptContext con, const char * fn);
+/* like above, but reads /etc/popt and $HOME/.popt along with environment
+ vars */
+int poptReadDefaultConfig(poptContext con, int useEnv);
+/* argv should be freed -- this allows ', ", and \ quoting, but ' is treated
+ the same as " and both may include \ quotes */
+int poptDupArgv(int argc, const char **argv,
+ /*@out@*/ int * argcPtr, /*@out@*/ const char *** argvPtr);
+int poptParseArgvString(const char * s,
+ /*@out@*/ int * argcPtr, /*@out@*/ const char *** argvPtr);
+/*@observer@*/ const char *poptStrerror(const int error);
+void poptSetExecPath(poptContext con, const char * path, int allowAbsolute);
+void poptPrintHelp(poptContext con, FILE * f, int flags);
+void poptPrintUsage(poptContext con, FILE * f, int flags);
+void poptSetOtherOptionHelp(poptContext con, const char * text);
+/*@observer@*/ const char * poptGetInvocationName(poptContext con);
+/* shuffles argv pointers to remove stripped args, returns new argc */
+int poptStrippedArgv(poptContext con, int argc, char **argv);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/source/popt/poptconfig.c b/source/popt/poptconfig.c
new file mode 100644
index 00000000000..eb769413630
--- /dev/null
+++ b/source/popt/poptconfig.c
@@ -0,0 +1,142 @@
+/* (C) 1998 Red Hat Software, Inc. -- Licensing details are in the COPYING
+ file accompanying popt source distributions, available from
+ ftp://ftp.redhat.com/pub/code/popt */
+
+#include "system.h"
+#include "poptint.h"
+
+static void configLine(poptContext con, char * line) {
+ int nameLength = strlen(con->appName);
+ char * opt;
+ struct poptAlias alias;
+ char * entryType;
+ char * longName = NULL;
+ char shortName = '\0';
+
+ if (strncmp(line, con->appName, nameLength)) return;
+ line += nameLength;
+ if (!*line || !isspace(*line)) return;
+ while (*line && isspace(*line)) line++;
+ entryType = line;
+
+ while (!*line || !isspace(*line)) line++;
+ *line++ = '\0';
+ while (*line && isspace(*line)) line++;
+ if (!*line) return;
+ opt = line;
+
+ while (!*line || !isspace(*line)) line++;
+ *line++ = '\0';
+ while (*line && isspace(*line)) line++;
+ if (!*line) return;
+
+ if (opt[0] == '-' && opt[1] == '-')
+ longName = opt + 2;
+ else if (opt[0] == '-' && !opt[2])
+ shortName = opt[1];
+
+ if (!strcmp(entryType, "alias")) {
+ if (poptParseArgvString(line, &alias.argc, &alias.argv)) return;
+ alias.longName = longName, alias.shortName = shortName;
+ poptAddAlias(con, alias, 0);
+ } else if (!strcmp(entryType, "exec")) {
+ con->execs = realloc(con->execs,
+ sizeof(*con->execs) * (con->numExecs + 1));
+ if (longName)
+ con->execs[con->numExecs].longName = xstrdup(longName);
+ else
+ con->execs[con->numExecs].longName = NULL;
+
+ con->execs[con->numExecs].shortName = shortName;
+ con->execs[con->numExecs].script = xstrdup(line);
+
+ con->numExecs++;
+ }
+}
+
+int poptReadConfigFile(poptContext con, const char * fn) {
+ char * file=NULL, * chptr, * end;
+ char * buf=NULL, * dst;
+ int fd, rc;
+ int fileLength;
+
+ fd = open(fn, O_RDONLY);
+ if (fd < 0) {
+ if (errno == ENOENT)
+ return 0;
+ else
+ return POPT_ERROR_ERRNO;
+ }
+
+ fileLength = lseek(fd, 0, SEEK_END);
+ (void) lseek(fd, 0, 0);
+
+ file = malloc(fileLength + 1);
+ if (read(fd, file, fileLength) != fileLength) {
+ rc = errno;
+ close(fd);
+ errno = rc;
+ if (file) free(file);
+ return POPT_ERROR_ERRNO;
+ }
+ close(fd);
+
+ dst = buf = malloc(fileLength + 1);
+
+ chptr = file;
+ end = (file + fileLength);
+ while (chptr < end) {
+ switch (*chptr) {
+ case '\n':
+ *dst = '\0';
+ dst = buf;
+ while (*dst && isspace(*dst)) dst++;
+ if (*dst && *dst != '#') {
+ configLine(con, dst);
+ }
+ chptr++;
+ break;
+ case '\\':
+ *dst++ = *chptr++;
+ if (chptr < end) {
+ if (*chptr == '\n')
+ dst--, chptr++;
+ /* \ at the end of a line does not insert a \n */
+ else
+ *dst++ = *chptr++;
+ }
+ break;
+ default:
+ *dst++ = *chptr++;
+ break;
+ }
+ }
+
+ free(file);
+ free(buf);
+
+ return 0;
+}
+
+int poptReadDefaultConfig(poptContext con, /*@unused@*/ int useEnv) {
+ char * fn, * home;
+ int rc;
+
+ if (!con->appName) return 0;
+
+ rc = poptReadConfigFile(con, "/etc/popt");
+ if (rc) return rc;
+ if (getuid() != geteuid()) return 0;
+
+ if ((home = getenv("HOME"))) {
+ fn = malloc(strlen(home) + 20);
+ strcpy(fn, home);
+ strcat(fn, "/.popt");
+ rc = poptReadConfigFile(con, fn);
+ free(fn);
+ if (rc) return rc;
+ }
+
+ return 0;
+}
+
diff --git a/source/popt/popthelp.c b/source/popt/popthelp.c
new file mode 100644
index 00000000000..6b790a63e78
--- /dev/null
+++ b/source/popt/popthelp.c
@@ -0,0 +1,301 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+
+/* (C) 1998 Red Hat Software, Inc. -- Licensing details are in the COPYING
+ file accompanying popt source distributions, available from
+ ftp://ftp.redhat.com/pub/code/popt */
+
+#include "system.h"
+#include "poptint.h"
+
+static void displayArgs(poptContext con,
+ /*@unused@*/ enum poptCallbackReason foo,
+ struct poptOption * key,
+ /*@unused@*/ const char * arg, /*@unused@*/ void * data) {
+ if (key->shortName== '?')
+ poptPrintHelp(con, stdout, 0);
+ else
+ poptPrintUsage(con, stdout, 0);
+ exit(0);
+}
+
+struct poptOption poptHelpOptions[] = {
+ { NULL, '\0', POPT_ARG_CALLBACK, (void *)&displayArgs, '\0', NULL, NULL },
+ { "help", '?', 0, NULL, '?', N_("Show this help message"), NULL },
+ { "usage", '\0', 0, NULL, 'u', N_("Display brief usage message"), NULL },
+ { NULL, '\0', 0, NULL, 0, NULL, NULL }
+} ;
+
+
+/*@observer@*/ /*@null@*/ static const char *
+getTableTranslationDomain(const struct poptOption *table)
+{
+ const struct poptOption *opt;
+
+ for(opt = table;
+ opt->longName || opt->shortName || opt->arg;
+ opt++) {
+ if(opt->argInfo == POPT_ARG_INTL_DOMAIN)
+ return opt->arg;
+ }
+
+ return NULL;
+}
+
+/*@observer@*/ /*@null@*/ static const char *
+getArgDescrip(const struct poptOption * opt, const char *translation_domain)
+{
+ if (!(opt->argInfo & POPT_ARG_MASK)) return NULL;
+
+ if (opt == (poptHelpOptions + 1) || opt == (poptHelpOptions + 2))
+ if (opt->argDescrip) return POPT_(opt->argDescrip);
+
+ if (opt->argDescrip) return D_(translation_domain, opt->argDescrip);
+ return POPT_("ARG");
+}
+
+static void singleOptionHelp(FILE * f, int maxLeftCol,
+ const struct poptOption * opt,
+ const char *translation_domain) {
+ int indentLength = maxLeftCol + 5;
+ int lineLength = 79 - indentLength;
+ const char * help = D_(translation_domain, opt->descrip);
+ int helpLength;
+ const char * ch;
+ char format[10];
+ char * left;
+ const char * argDescrip = getArgDescrip(opt, translation_domain);
+
+ left = malloc(maxLeftCol + 1);
+ *left = '\0';
+
+ if (opt->longName && opt->shortName)
+ sprintf(left, "-%c, --%s", opt->shortName, opt->longName);
+ else if (opt->shortName)
+ sprintf(left, "-%c", opt->shortName);
+ else if (opt->longName)
+ sprintf(left, "--%s", opt->longName);
+ if (!*left) return ;
+ if (argDescrip) {
+ strcat(left, "=");
+ strcat(left, argDescrip);
+ }
+
+ if (help)
+ fprintf(f," %-*s ", maxLeftCol, left);
+ else {
+ fprintf(f," %s\n", left);
+ goto out;
+ }
+
+ helpLength = strlen(help);
+ while (helpLength > lineLength) {
+ ch = help + lineLength - 1;
+ while (ch > help && !isspace(*ch)) ch--;
+ if (ch == help) break; /* give up */
+ while (ch > (help + 1) && isspace(*ch)) ch--;
+ ch++;
+
+ sprintf(format, "%%.%ds\n%%%ds", (int) (ch - help), indentLength);
+ fprintf(f, format, help, " ");
+ help = ch;
+ while (isspace(*help) && *help) help++;
+ helpLength = strlen(help);
+ }
+
+ if (helpLength) fprintf(f, "%s\n", help);
+
+out:
+ free(left);
+}
+
+static int maxArgWidth(const struct poptOption * opt,
+ const char * translation_domain) {
+ int max = 0;
+ int this;
+ const char * s;
+
+ while (opt->longName || opt->shortName || opt->arg) {
+ if ((opt->argInfo & POPT_ARG_MASK) == POPT_ARG_INCLUDE_TABLE) {
+ this = maxArgWidth(opt->arg, translation_domain);
+ if (this > max) max = this;
+ } else if (!(opt->argInfo & POPT_ARGFLAG_DOC_HIDDEN)) {
+ this = opt->shortName ? 2 : 0;
+ if (opt->longName) {
+ if (this) this += 2;
+ this += strlen(opt->longName) + 2;
+ }
+
+ s = getArgDescrip(opt, translation_domain);
+ if (s)
+ this += strlen(s) + 1;
+ if (this > max) max = this;
+ }
+
+ opt++;
+ }
+
+ return max;
+}
+
+static void singleTableHelp(FILE * f, const struct poptOption * table,
+ int left,
+ const char *translation_domain) {
+ const struct poptOption * opt;
+ const char *sub_transdom;
+
+ opt = table;
+ while (opt->longName || opt->shortName || opt->arg) {
+ if ((opt->longName || opt->shortName) &&
+ !(opt->argInfo & POPT_ARGFLAG_DOC_HIDDEN))
+ singleOptionHelp(f, left, opt, translation_domain);
+ opt++;
+ }
+
+ opt = table;
+ while (opt->longName || opt->shortName || opt->arg) {
+ if ((opt->argInfo & POPT_ARG_MASK) == POPT_ARG_INCLUDE_TABLE) {
+ sub_transdom = getTableTranslationDomain(opt->arg);
+ if(!sub_transdom)
+ sub_transdom = translation_domain;
+
+ if (opt->descrip)
+ fprintf(f, "\n%s\n", D_(sub_transdom, opt->descrip));
+
+ singleTableHelp(f, opt->arg, left, sub_transdom);
+ }
+ opt++;
+ }
+}
+
+static int showHelpIntro(poptContext con, FILE * f) {
+ int len = 6;
+ const char * fn;
+
+ fprintf(f, POPT_("Usage:"));
+ if (!(con->flags & POPT_CONTEXT_KEEP_FIRST)) {
+ fn = con->optionStack->argv[0];
+ if (strchr(fn, '/')) fn = strchr(fn, '/') + 1;
+ fprintf(f, " %s", fn);
+ len += strlen(fn) + 1;
+ }
+
+ return len;
+}
+
+void poptPrintHelp(poptContext con, FILE * f, /*@unused@*/ int flags) {
+ int leftColWidth;
+
+ showHelpIntro(con, f);
+ if (con->otherHelp)
+ fprintf(f, " %s\n", con->otherHelp);
+ else
+ fprintf(f, " %s\n", POPT_("[OPTION...]"));
+
+ leftColWidth = maxArgWidth(con->options, NULL);
+ singleTableHelp(f, con->options, leftColWidth, NULL);
+}
+
+static int singleOptionUsage(FILE * f, int cursor,
+ const struct poptOption * opt,
+ const char *translation_domain) {
+ int len = 3;
+ char shortStr[2] = { '\0', '\0' };
+ const char * item = shortStr;
+ const char * argDescrip = getArgDescrip(opt, translation_domain);
+
+ if (opt->shortName) {
+ if (!(opt->argInfo & POPT_ARG_MASK))
+ return cursor; /* we did these already */
+ len++;
+ *shortStr = opt->shortName;
+ shortStr[1] = '\0';
+ } else if (opt->longName) {
+ len += 1 + strlen(opt->longName);
+ item = opt->longName;
+ }
+
+ if (len == 3) return cursor;
+
+ if (argDescrip)
+ len += strlen(argDescrip) + 1;
+
+ if ((cursor + len) > 79) {
+ fprintf(f, "\n ");
+ cursor = 7;
+ }
+
+ fprintf(f, " [-%s%s%s%s]", opt->shortName ? "" : "-", item,
+ argDescrip ? (opt->shortName ? " " : "=") : "",
+ argDescrip ? argDescrip : "");
+
+ return cursor + len + 1;
+}
+
+static int singleTableUsage(FILE * f, int cursor, const struct poptOption * table,
+ const char *translation_domain) {
+ const struct poptOption * opt;
+
+ opt = table;
+ while (opt->longName || opt->shortName || opt->arg) {
+ if ((opt->argInfo & POPT_ARG_MASK) == POPT_ARG_INTL_DOMAIN)
+ translation_domain = (const char *)opt->arg;
+ else if ((opt->argInfo & POPT_ARG_MASK) == POPT_ARG_INCLUDE_TABLE)
+ cursor = singleTableUsage(f, cursor, opt->arg,
+ translation_domain);
+ else if ((opt->longName || opt->shortName) &&
+ !(opt->argInfo & POPT_ARGFLAG_DOC_HIDDEN))
+ cursor = singleOptionUsage(f, cursor, opt, translation_domain);
+
+ opt++;
+ }
+
+ return cursor;
+}
+
+static int showShortOptions(const struct poptOption * opt, FILE * f,
+ char * str) {
+ char s[300]; /* this is larger then the ascii set, so
+ it should do just fine */
+
+ s[0] = '\0';
+ if (str == NULL) {
+ memset(s, 0, sizeof(s));
+ str = s;
+ }
+
+ while (opt->longName || opt->shortName || opt->arg) {
+ if (opt->shortName && !(opt->argInfo & POPT_ARG_MASK))
+ str[strlen(str)] = opt->shortName;
+ else if ((opt->argInfo & POPT_ARG_MASK) == POPT_ARG_INCLUDE_TABLE)
+ showShortOptions(opt->arg, f, str);
+
+ opt++;
+ }
+
+ if (s != str || !*s)
+ return 0;
+
+ fprintf(f, " [-%s]", s);
+ return strlen(s) + 4;
+}
+
+void poptPrintUsage(poptContext con, FILE * f, /*@unused@*/ int flags) {
+ int cursor;
+
+ cursor = showHelpIntro(con, f);
+ cursor += showShortOptions(con->options, f, NULL);
+ singleTableUsage(f, cursor, con->options, NULL);
+
+ if (con->otherHelp) {
+ cursor += strlen(con->otherHelp) + 1;
+ if (cursor > 79) fprintf(f, "\n ");
+ fprintf(f, " %s", con->otherHelp);
+ }
+
+ fprintf(f, "\n");
+}
+
+void poptSetOtherOptionHelp(poptContext con, const char * text) {
+ if (con->otherHelp) xfree(con->otherHelp);
+ con->otherHelp = xstrdup(text);
+}
diff --git a/source/popt/poptint.h b/source/popt/poptint.h
new file mode 100644
index 00000000000..1847ffafe67
--- /dev/null
+++ b/source/popt/poptint.h
@@ -0,0 +1,71 @@
+/* (C) 1998 Red Hat Software, Inc. -- Licensing details are in the COPYING
+ file accompanying popt source distributions, available from
+ ftp://ftp.redhat.com/pub/code/popt */
+
+#ifndef H_POPTINT
+#define H_POPTINT
+
+/* Bit mask macros. */
+typedef unsigned int __pbm_bits;
+#define __PBM_NBITS (8 * sizeof (__pbm_bits))
+#define __PBM_IX(d) ((d) / __PBM_NBITS)
+#define __PBM_MASK(d) ((__pbm_bits) 1 << ((d) % __PBM_NBITS))
+typedef struct {
+ __pbm_bits bits[1];
+} pbm_set;
+#define __PBM_BITS(set) ((set)->bits)
+
+#define PBM_ALLOC(d) calloc(__PBM_IX (d) + 1, sizeof(__pbm_bits))
+#define PBM_FREE(s) free(s);
+#define PBM_SET(d, s) (__PBM_BITS (s)[__PBM_IX (d)] |= __PBM_MASK (d))
+#define PBM_CLR(d, s) (__PBM_BITS (s)[__PBM_IX (d)] &= ~__PBM_MASK (d))
+#define PBM_ISSET(d, s) ((__PBM_BITS (s)[__PBM_IX (d)] & __PBM_MASK (d)) != 0)
+
+struct optionStackEntry {
+ int argc;
+ /*@only@*/ const char ** argv;
+ /*@only@*/ pbm_set * argb;
+ int next;
+ /*@only@*/ const char * nextArg;
+ /*@keep@*/ const char * nextCharArg;
+ /*@dependent@*/ struct poptAlias * currAlias;
+ int stuffed;
+};
+
+struct execEntry {
+ const char * longName;
+ char shortName;
+ const char * script;
+};
+
+struct poptContext_s {
+ struct optionStackEntry optionStack[POPT_OPTION_DEPTH];
+ /*@dependent@*/ struct optionStackEntry * os;
+ /*@owned@*/ const char ** leftovers;
+ int numLeftovers;
+ int nextLeftover;
+ /*@keep@*/ const struct poptOption * options;
+ int restLeftover;
+ /*@only@*/ const char * appName;
+ /*@only@*/ struct poptAlias * aliases;
+ int numAliases;
+ int flags;
+ struct execEntry * execs;
+ int numExecs;
+ /*@only@*/ const char ** finalArgv;
+ int finalArgvCount;
+ int finalArgvAlloced;
+ /*@dependent@*/ struct execEntry * doExec;
+ /*@only@*/ const char * execPath;
+ int execAbsolute;
+ /*@only@*/ const char * otherHelp;
+ pbm_set * arg_strip;
+};
+
+#define xfree(_a) free((void *)_a)
+
+#define POPT_(foo) (foo)
+#define D_(dom, str) (str)
+#define N_(foo) (foo)
+
+#endif
diff --git a/source/popt/poptparse.c b/source/popt/poptparse.c
new file mode 100644
index 00000000000..8f00769be9f
--- /dev/null
+++ b/source/popt/poptparse.c
@@ -0,0 +1,102 @@
+/* (C) 1998 Red Hat Software, Inc. -- Licensing details are in the COPYING
+ file accompanying popt source distributions, available from
+ ftp://ftp.redhat.com/pub/code/popt */
+
+#include "system.h"
+
+#define POPT_ARGV_ARRAY_GROW_DELTA 5
+
+int poptDupArgv(int argc, const char **argv,
+ int * argcPtr, const char *** argvPtr)
+{
+ size_t nb = (argc + 1) * sizeof(*argv);
+ const char ** argv2;
+ char * dst;
+ int i;
+
+ for (i = 0; i < argc; i++) {
+ if (argv[i] == NULL)
+ return POPT_ERROR_NOARG;
+ nb += strlen(argv[i]) + 1;
+ }
+
+ dst = malloc(nb);
+ argv2 = (void *) dst;
+ dst += (argc + 1) * sizeof(*argv);
+
+ for (i = 0; i < argc; i++) {
+ argv2[i] = dst;
+ dst += strlen(strcpy(dst, argv[i])) + 1;
+ }
+ argv2[argc] = NULL;
+
+ *argvPtr = argv2;
+ *argcPtr = argc;
+ return 0;
+}
+
+int poptParseArgvString(const char * s, int * argcPtr, const char *** argvPtr)
+{
+ const char * src;
+ char quote = '\0';
+ int argvAlloced = POPT_ARGV_ARRAY_GROW_DELTA;
+ const char ** argv = malloc(sizeof(*argv) * argvAlloced);
+ int argc = 0;
+ int buflen = strlen(s) + 1;
+ char *buf0 = calloc(buflen, 1);
+ char *buf = buf0;
+
+ argv[argc] = buf;
+
+ for (src = s; *src; src++) {
+ if (quote == *src) {
+ quote = '\0';
+ } else if (quote) {
+ if (*src == '\\') {
+ src++;
+ if (!*src) {
+ free(argv);
+ free(buf0);
+ return POPT_ERROR_BADQUOTE;
+ }
+ if (*src != quote) *buf++ = '\\';
+ }
+ *buf++ = *src;
+ } else if (isspace(*src)) {
+ if (*argv[argc]) {
+ buf++, argc++;
+ if (argc == argvAlloced) {
+ argvAlloced += POPT_ARGV_ARRAY_GROW_DELTA;
+ argv = realloc(argv, sizeof(*argv) * argvAlloced);
+ }
+ argv[argc] = buf;
+ }
+ } else switch (*src) {
+ case '"':
+ case '\'':
+ quote = *src;
+ break;
+ case '\\':
+ src++;
+ if (!*src) {
+ free(argv);
+ free(buf0);
+ return POPT_ERROR_BADQUOTE;
+ }
+ /*@fallthrough@*/
+ default:
+ *buf++ = *src;
+ break;
+ }
+ }
+
+ if (strlen(argv[argc])) {
+ argc++, buf++;
+ }
+
+ (void) poptDupArgv(argc, argv, argcPtr, argvPtr);
+
+ free(argv);
+ free(buf0);
+ return 0;
+}
diff --git a/source/popt/system.h b/source/popt/system.h
new file mode 100644
index 00000000000..059c0458176
--- /dev/null
+++ b/source/popt/system.h
@@ -0,0 +1,53 @@
+#include "config.h"
+
+#include <ctype.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <limits.h>
+
+#if HAVE_MCHECK_H
+#include <mcheck.h>
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#ifdef __NeXT
+/* access macros are not declared in non posix mode in unistd.h -
+ don't try to use posix on NeXTstep 3.3 ! */
+#include <libc.h>
+#endif
+
+/* AIX requires this to be the first thing in the file. */
+#ifndef __GNUC__
+# if HAVE_ALLOCA_H
+# include <alloca.h>
+# else
+# ifdef _AIX
+#pragma alloca
+# else
+# ifndef alloca /* predefined by HP cc +Olibcalls */
+char *alloca ();
+# endif
+# endif
+# endif
+#elif defined(__GNUC__) && defined(__STRICT_ANSI__)
+#define alloca __builtin_alloca
+#endif
+
+/*@only@*/ char * xstrdup (const char *str);
+
+#if HAVE_MCHECK_H && defined(__GNUC__)
+#define vmefail() (fprintf(stderr, "virtual memory exhausted.\n"), exit(EXIT_FAILURE), NULL)
+#define xstrdup(_str) (strcpy((malloc(strlen(_str)+1) ? : vmefail()), (_str)))
+#else
+#define xstrdup(_str) strdup(_str)
+#endif /* HAVE_MCHECK_H && defined(__GNUC__) */
+
+
+#include "popt.h"
diff --git a/source/printing/.cvsignore b/source/printing/.cvsignore
new file mode 100644
index 00000000000..e69de29bb2d
--- /dev/null
+++ b/source/printing/.cvsignore
diff --git a/source/printing/load.c b/source/printing/load.c
new file mode 100644
index 00000000000..cd90cbb6f33
--- /dev/null
+++ b/source/printing/load.c
@@ -0,0 +1,73 @@
+/*
+ Unix SMB/CIFS implementation.
+ load printer lists
+ Copyright (C) Andrew Tridgell 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 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"
+
+
+/***************************************************************************
+auto-load printer services
+***************************************************************************/
+void add_all_printers(void)
+{
+ int printers = lp_servicenumber(PRINTERS_NAME);
+
+ if (printers < 0) return;
+
+ pcap_printer_fn(lp_add_one_printer);
+}
+
+/***************************************************************************
+auto-load some homes and printer services
+***************************************************************************/
+static void add_auto_printers(void)
+{
+ const char *p;
+ int printers;
+ char *str = strdup(lp_auto_services());
+
+ if (!str) return;
+
+ printers = lp_servicenumber(PRINTERS_NAME);
+
+ if (printers < 0) {
+ SAFE_FREE(str);
+ return;
+ }
+
+ for (p=strtok(str,LIST_SEP);p;p=strtok(NULL,LIST_SEP)) {
+ if (lp_servicenumber(p) >= 0) continue;
+
+ if (pcap_printername_ok(p,NULL)) {
+ lp_add_printer(p,printers);
+ }
+ }
+
+ SAFE_FREE(str);
+}
+
+/***************************************************************************
+load automatic printer services
+***************************************************************************/
+void load_printers(void)
+{
+ add_auto_printers();
+ if (lp_load_printers())
+ add_all_printers();
+}
diff --git a/source/printing/lpq_parse.c b/source/printing/lpq_parse.c
new file mode 100644
index 00000000000..b7e41964f1b
--- /dev/null
+++ b/source/printing/lpq_parse.c
@@ -0,0 +1,1016 @@
+/*
+ Unix SMB/CIFS implementation.
+ lpq parsing routines
+ 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 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"
+
+static const char *Months[13] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun",
+ "Jul", "Aug", "Sep", "Oct", "Nov", "Dec", "Err"};
+
+
+/*******************************************************************
+process time fields
+********************************************************************/
+static time_t EntryTime(fstring tok[], int ptr, int count, int minimum)
+{
+ time_t jobtime,jobtime1;
+
+ jobtime = time(NULL); /* default case: take current time */
+ if (count >= minimum) {
+ struct tm *t;
+ int i, day, hour, min, sec;
+ char *c;
+
+ for (i=0; i<13; i++) if (!strncmp(tok[ptr], Months[i],3)) break; /* Find month */
+ if (i<12) {
+ t = localtime(&jobtime);
+ day = atoi(tok[ptr+1]);
+ c=(char *)(tok[ptr+2]);
+ *(c+2)=0;
+ hour = atoi(c);
+ *(c+5)=0;
+ min = atoi(c+3);
+ if(*(c+6) != 0)sec = atoi(c+6);
+ else sec=0;
+
+ if ((t->tm_mon < i)||
+ ((t->tm_mon == i)&&
+ ((t->tm_mday < day)||
+ ((t->tm_mday == day)&&
+ (t->tm_hour*60+t->tm_min < hour*60+min)))))
+ t->tm_year--; /* last year's print job */
+
+ t->tm_mon = i;
+ t->tm_mday = day;
+ t->tm_hour = hour;
+ t->tm_min = min;
+ t->tm_sec = sec;
+ jobtime1 = mktime(t);
+ if (jobtime1 != (time_t)-1)
+ jobtime = jobtime1;
+ }
+ }
+ return jobtime;
+}
+
+
+/****************************************************************************
+parse a lpq line
+
+here is an example of lpq output under bsd
+
+Warning: no daemon present
+Rank Owner Job Files Total Size
+1st tridge 148 README 8096 bytes
+
+here is an example of lpq output under osf/1
+
+Warning: no daemon present
+Rank Pri Owner Job Files Total Size
+1st 0 tridge 148 README 8096 bytes
+
+
+<allan@umich.edu> June 30, 1998.
+Modified to handle file names with spaces, like the parse_lpq_lprng code
+further below.
+****************************************************************************/
+static BOOL parse_lpq_bsd(char *line,print_queue_struct *buf,BOOL first)
+{
+#ifdef OSF1
+#define RANKTOK 0
+#define PRIOTOK 1
+#define USERTOK 2
+#define JOBTOK 3
+#define FILETOK 4
+#define TOTALTOK (count - 2)
+#define NTOK 6
+#define MAXTOK 128
+#else /* OSF1 */
+#define RANKTOK 0
+#define USERTOK 1
+#define JOBTOK 2
+#define FILETOK 3
+#define TOTALTOK (count - 2)
+#define NTOK 5
+#define MAXTOK 128
+#endif /* OSF1 */
+
+ char *tok[MAXTOK];
+ int count = 0;
+ pstring line2;
+
+ pstrcpy(line2,line);
+
+#ifdef OSF1
+ {
+ size_t length;
+ length = strlen(line2);
+ if (line2[length-3] == ':')
+ return(False);
+ }
+#endif /* OSF1 */
+
+ /* FIXME: Use next_token rather than strtok! */
+ tok[0] = strtok(line2," \t");
+ count++;
+
+ while (((tok[count] = strtok(NULL," \t")) != NULL) && (count < MAXTOK)) {
+ count++;
+ }
+
+ /* we must get at least NTOK tokens */
+ if (count < NTOK)
+ return(False);
+
+ /* the Job and Total columns must be integer */
+ if (!isdigit((int)*tok[JOBTOK]) || !isdigit((int)*tok[TOTALTOK])) return(False);
+
+ buf->job = atoi(tok[JOBTOK]);
+ buf->size = atoi(tok[TOTALTOK]);
+ buf->status = strequal(tok[RANKTOK],"active")?LPQ_PRINTING:LPQ_QUEUED;
+ buf->time = time(NULL);
+ fstrcpy(buf->fs_user,tok[USERTOK]);
+ fstrcpy(buf->fs_file,tok[FILETOK]);
+
+ if ((FILETOK + 1) != TOTALTOK) {
+ int i;
+
+ for (i = (FILETOK + 1); i < TOTALTOK; i++) {
+ /* FIXME: Using fstrcat rather than other means is a bit
+ * inefficient; this might be a problem for enormous queues with
+ * many fields. */
+ fstrcat(buf->fs_file, " ");
+ fstrcat(buf->fs_file, tok[i]);
+ }
+ /* Ensure null termination. */
+ fstrterminate(buf->fs_file);
+ }
+
+#ifdef PRIOTOK
+ buf->priority = atoi(tok[PRIOTOK]);
+#else
+ buf->priority = 1;
+#endif
+ return(True);
+}
+
+/*
+<magnus@hum.auc.dk>
+LPRng_time modifies the current date by inserting the hour and minute from
+the lpq output. The lpq time looks like "23:15:07"
+
+<allan@umich.edu> June 30, 1998.
+Modified to work with the re-written parse_lpq_lprng routine.
+
+<J.P.M.v.Itegem@tue.nl> Dec 17,1999
+Modified to work with lprng 3.16
+With lprng 3.16 The lpq time looks like
+ "23:15:07"
+ "23:15:07.100"
+ "1999-12-16-23:15:07"
+ "1999-12-16-23:15:07.100"
+
+*/
+static time_t LPRng_time(char *time_string)
+{
+ time_t jobtime;
+ struct tm t;
+
+ jobtime = time(NULL); /* default case: take current time */
+ t = *localtime(&jobtime);
+
+ if ( atoi(time_string) < 24 ){
+ t.tm_hour = atoi(time_string);
+ t.tm_min = atoi(time_string+3);
+ t.tm_sec = atoi(time_string+6);
+ } else {
+ t.tm_year = atoi(time_string)-1900;
+ t.tm_mon = atoi(time_string+5)-1;
+ t.tm_mday = atoi(time_string+8);
+ t.tm_hour = atoi(time_string+11);
+ t.tm_min = atoi(time_string+14);
+ t.tm_sec = atoi(time_string+17);
+ }
+ jobtime = mktime(&t);
+
+ return jobtime;
+}
+
+
+/****************************************************************************
+ parse a lprng lpq line
+ <allan@umich.edu> June 30, 1998.
+ Re-wrote this to handle file names with spaces, multiple file names on one
+ lpq line, etc;
+****************************************************************************/
+static BOOL parse_lpq_lprng(char *line,print_queue_struct *buf,BOOL first)
+{
+#define LPRNG_RANKTOK 0
+#define LPRNG_USERTOK 1
+#define LPRNG_PRIOTOK 2
+#define LPRNG_JOBTOK 3
+#define LPRNG_FILETOK 4
+#define LPRNG_TOTALTOK (num_tok - 2)
+#define LPRNG_TIMETOK (num_tok - 1)
+#define LPRNG_NTOK 7
+#define LPRNG_MAXTOK 128 /* PFMA just to keep us from running away. */
+
+ fstring tokarr[LPRNG_MAXTOK];
+ const char *cptr;
+ char *ptr;
+ int num_tok = 0;
+
+ cptr = line;
+ while(next_token( &cptr, tokarr[num_tok], " \t", sizeof(fstring)) && (num_tok < LPRNG_MAXTOK))
+ num_tok++;
+
+ /* We must get at least LPRNG_NTOK tokens. */
+ if (num_tok < LPRNG_NTOK) {
+ return(False);
+ }
+
+ if (!isdigit((int)*tokarr[LPRNG_JOBTOK]) || !isdigit((int)*tokarr[LPRNG_TOTALTOK])) {
+ return(False);
+ }
+
+ buf->job = atoi(tokarr[LPRNG_JOBTOK]);
+ buf->size = atoi(tokarr[LPRNG_TOTALTOK]);
+
+ if (strequal(tokarr[LPRNG_RANKTOK],"active")) {
+ buf->status = LPQ_PRINTING;
+ } else if (strequal(tokarr[LPRNG_RANKTOK],"done")) {
+ buf->status = LPQ_PRINTED;
+ } else if (isdigit((int)*tokarr[LPRNG_RANKTOK])) {
+ buf->status = LPQ_QUEUED;
+ } else {
+ buf->status = LPQ_PAUSED;
+ }
+
+ buf->priority = *tokarr[LPRNG_PRIOTOK] -'A';
+
+ buf->time = LPRng_time(tokarr[LPRNG_TIMETOK]);
+
+ fstrcpy(buf->fs_user,tokarr[LPRNG_USERTOK]);
+
+ /* The '@hostname' prevents windows from displaying the printing icon
+ * for the current user on the taskbar. Plop in a null.
+ */
+
+ if ((ptr = strchr_m(buf->fs_user,'@')) != NULL) {
+ *ptr = '\0';
+ }
+
+ fstrcpy(buf->fs_file,tokarr[LPRNG_FILETOK]);
+
+ if ((LPRNG_FILETOK + 1) != LPRNG_TOTALTOK) {
+ int i;
+
+ for (i = (LPRNG_FILETOK + 1); i < LPRNG_TOTALTOK; i++) {
+ /* FIXME: Using fstrcat rather than other means is a bit
+ * inefficient; this might be a problem for enormous queues with
+ * many fields. */
+ fstrcat(buf->fs_file, " ");
+ fstrcat(buf->fs_file, tokarr[i]);
+ }
+ /* Ensure null termination. */
+ fstrterminate(buf->fs_file);
+ }
+
+ return(True);
+}
+
+
+
+/*******************************************************************
+parse lpq on an aix system
+
+Queue Dev Status Job Files User PP % Blks Cp Rnk
+------- ----- --------- --- ------------------ ---------- ---- -- ----- --- ---
+lazer lazer READY
+lazer lazer RUNNING 537 6297doc.A kvintus@IE 0 10 2445 1 1
+ QUEUED 538 C.ps root@IEDVB 124 1 2
+ QUEUED 539 E.ps root@IEDVB 28 1 3
+ QUEUED 540 L.ps root@IEDVB 172 1 4
+ QUEUED 541 P.ps root@IEDVB 22 1 5
+********************************************************************/
+static BOOL parse_lpq_aix(char *line,print_queue_struct *buf,BOOL first)
+{
+ fstring tok[11];
+ int count=0;
+ const char *cline = line;
+
+ /* handle the case of "(standard input)" as a filename */
+ string_sub(line,"standard input","STDIN",0);
+ all_string_sub(line,"(","\"",0);
+ all_string_sub(line,")","\"",0);
+
+ for (count=0;
+ count<10 &&
+ next_token(&cline,tok[count],NULL, sizeof(tok[count]));
+ count++) ;
+
+ /* we must get 6 tokens */
+ if (count < 10)
+ {
+ if ((count == 7) && ((strcmp(tok[0],"QUEUED") == 0) || (strcmp(tok[0],"HELD") == 0)))
+ {
+ /* the 2nd and 5th columns must be integer */
+ if (!isdigit((int)*tok[1]) || !isdigit((int)*tok[4])) return(False);
+ buf->size = atoi(tok[4]) * 1024;
+ /* if the fname contains a space then use STDIN */
+ if (strchr_m(tok[2],' '))
+ fstrcpy(tok[2],"STDIN");
+
+ /* only take the last part of the filename */
+ {
+ fstring tmp;
+ char *p = strrchr_m(tok[2],'/');
+ if (p)
+ {
+ fstrcpy(tmp,p+1);
+ fstrcpy(tok[2],tmp);
+ }
+ }
+
+
+ buf->job = atoi(tok[1]);
+ buf->status = strequal(tok[0],"HELD")?LPQ_PAUSED:LPQ_QUEUED;
+ buf->priority = 0;
+ buf->time = time(NULL);
+ fstrcpy(buf->fs_user,tok[3]);
+ fstrcpy(buf->fs_file,tok[2]);
+ }
+ else
+ {
+ DEBUG(6,("parse_lpq_aix count=%d\n", count));
+ return(False);
+ }
+ }
+ else
+ {
+ /* the 4th and 9th columns must be integer */
+ if (!isdigit((int)*tok[3]) || !isdigit((int)*tok[8])) return(False);
+ buf->size = atoi(tok[8]) * 1024;
+ /* if the fname contains a space then use STDIN */
+ if (strchr_m(tok[4],' '))
+ fstrcpy(tok[4],"STDIN");
+
+ /* only take the last part of the filename */
+ {
+ fstring tmp;
+ char *p = strrchr_m(tok[4],'/');
+ if (p)
+ {
+ fstrcpy(tmp,p+1);
+ fstrcpy(tok[4],tmp);
+ }
+ }
+
+
+ buf->job = atoi(tok[3]);
+ buf->status = strequal(tok[2],"RUNNING")?LPQ_PRINTING:LPQ_QUEUED;
+ buf->priority = 0;
+ buf->time = time(NULL);
+ fstrcpy(buf->fs_user,tok[5]);
+ fstrcpy(buf->fs_file,tok[4]);
+ }
+
+
+ return(True);
+}
+
+
+/****************************************************************************
+parse a lpq line
+here is an example of lpq output under hpux; note there's no space after -o !
+$> lpstat -oljplus
+ljplus-2153 user priority 0 Jan 19 08:14 on ljplus
+ util.c 125697 bytes
+ server.c 110712 bytes
+ljplus-2154 user priority 0 Jan 19 08:14 from client
+ (standard input) 7551 bytes
+****************************************************************************/
+static BOOL parse_lpq_hpux(char *line, print_queue_struct *buf, BOOL first)
+{
+ /* must read two lines to process, therefore keep some values static */
+ static BOOL header_line_ok=False, base_prio_reset=False;
+ static fstring jobuser;
+ static int jobid;
+ static int jobprio;
+ static time_t jobtime;
+ static int jobstat=LPQ_QUEUED;
+ /* to store minimum priority to print, lpstat command should be invoked
+ with -p option first, to work */
+ static int base_prio;
+ int count;
+ char htab = '\011';
+ const char *cline = line;
+ fstring tok[12];
+
+ /* If a line begins with a horizontal TAB, it is a subline type */
+
+ if (line[0] == htab) { /* subline */
+ /* check if it contains the base priority */
+ if (!strncmp(line,"\tfence priority : ",18)) {
+ base_prio=atoi(&line[18]);
+ DEBUG(4, ("fence priority set at %d\n", base_prio));
+ }
+ if (!header_line_ok) return (False); /* incorrect header line */
+ /* handle the case of "(standard input)" as a filename */
+ string_sub(line,"standard input","STDIN",0);
+ all_string_sub(line,"(","\"",0);
+ all_string_sub(line,")","\"",0);
+
+ for (count=0; count<2 && next_token(&cline,tok[count],NULL,sizeof(tok[count])); count++) ;
+ /* we must get 2 tokens */
+ if (count < 2) return(False);
+
+ /* the 2nd column must be integer */
+ if (!isdigit((int)*tok[1])) return(False);
+
+ /* if the fname contains a space then use STDIN */
+ if (strchr_m(tok[0],' '))
+ fstrcpy(tok[0],"STDIN");
+
+ buf->size = atoi(tok[1]);
+ fstrcpy(buf->fs_file,tok[0]);
+
+ /* fill things from header line */
+ buf->time = jobtime;
+ buf->job = jobid;
+ buf->status = jobstat;
+ buf->priority = jobprio;
+ fstrcpy(buf->fs_user,jobuser);
+
+ return(True);
+ }
+ else { /* header line */
+ header_line_ok=False; /* reset it */
+ if (first) {
+ if (!base_prio_reset) {
+ base_prio=0; /* reset it */
+ base_prio_reset=True;
+ }
+ }
+ else if (base_prio) base_prio_reset=False;
+
+ /* handle the dash in the job id */
+ string_sub(line,"-"," ",0);
+
+ for (count=0; count<12 && next_token(&cline,tok[count],NULL,sizeof(tok[count])); count++) ;
+
+ /* we must get 8 tokens */
+ if (count < 8) return(False);
+
+ /* first token must be printer name (cannot check ?) */
+ /* the 2nd, 5th & 7th column must be integer */
+ if (!isdigit((int)*tok[1]) || !isdigit((int)*tok[4]) || !isdigit((int)*tok[6])) return(False);
+ jobid = atoi(tok[1]);
+ fstrcpy(jobuser,tok[2]);
+ jobprio = atoi(tok[4]);
+
+ /* process time */
+ jobtime=EntryTime(tok, 5, count, 8);
+ if (jobprio < base_prio) {
+ jobstat = LPQ_PAUSED;
+ DEBUG (4, ("job %d is paused: prio %d < %d; jobstat=%d\n", jobid, jobprio, base_prio, jobstat));
+ }
+ else {
+ jobstat = LPQ_QUEUED;
+ if ((count >8) && (((strequal(tok[8],"on")) ||
+ ((strequal(tok[8],"from")) &&
+ ((count > 10)&&(strequal(tok[10],"on")))))))
+ jobstat = LPQ_PRINTING;
+ }
+
+ header_line_ok=True; /* information is correct */
+ return(False); /* need subline info to include into queuelist */
+ }
+}
+
+
+/****************************************************************************
+parse a lpstat line
+
+here is an example of "lpstat -o dcslw" output under sysv
+
+dcslw-896 tridge 4712 Dec 20 10:30:30 on dcslw
+dcslw-897 tridge 4712 Dec 20 10:30:30 being held
+
+****************************************************************************/
+static BOOL parse_lpq_sysv(char *line,print_queue_struct *buf,BOOL first)
+{
+ fstring tok[9];
+ int count=0;
+ char *p;
+ const char *cline = line;
+
+ /*
+ * Handle the dash in the job id, but make sure that we skip over
+ * the printer name in case we have a dash in that.
+ * Patch from Dom.Mitchell@palmerharvey.co.uk.
+ */
+
+ /*
+ * Move to the first space.
+ */
+ for (p = line ; !isspace(*p) && *p; p++)
+ ;
+
+ /*
+ * Back up until the last '-' character or
+ * start of line.
+ */
+ for (; (p >= line) && (*p != '-'); p--)
+ ;
+
+ if((p >= line) && (*p == '-'))
+ *p = ' ';
+
+ for (count=0; count<9 && next_token(&cline,tok[count],NULL,sizeof(tok[count])); count++)
+ ;
+
+ /* we must get 7 tokens */
+ if (count < 7)
+ return(False);
+
+ /* the 2nd and 4th, 6th columns must be integer */
+ if (!isdigit((int)*tok[1]) || !isdigit((int)*tok[3]))
+ return(False);
+ if (!isdigit((int)*tok[5]))
+ return(False);
+
+ /* if the user contains a ! then trim the first part of it */
+ if ((p=strchr_m(tok[2],'!'))) {
+ fstring tmp;
+ fstrcpy(tmp,p+1);
+ fstrcpy(tok[2],tmp);
+ }
+
+ buf->job = atoi(tok[1]);
+ buf->size = atoi(tok[3]);
+ if (count > 7 && strequal(tok[7],"on"))
+ buf->status = LPQ_PRINTING;
+ else if (count > 8 && strequal(tok[7],"being") && strequal(tok[8],"held"))
+ buf->status = LPQ_PAUSED;
+ else
+ buf->status = LPQ_QUEUED;
+ buf->priority = 0;
+ buf->time = EntryTime(tok, 4, count, 7);
+ fstrcpy(buf->fs_user,tok[2]);
+ fstrcpy(buf->fs_file,tok[2]);
+ return(True);
+}
+
+/****************************************************************************
+parse a lpq line
+
+here is an example of lpq output under qnx
+Spooler: /qnx/spooler, on node 1
+Printer: txt (ready)
+0000: root [job #1 ] active 1146 bytes /etc/profile
+0001: root [job #2 ] ready 2378 bytes /etc/install
+0002: root [job #3 ] ready 1146 bytes -- standard input --
+****************************************************************************/
+static BOOL parse_lpq_qnx(char *line,print_queue_struct *buf,BOOL first)
+{
+ fstring tok[7];
+ int count=0;
+ const char *cline = line;
+
+ DEBUG(4,("antes [%s]\n", line));
+
+ /* handle the case of "-- standard input --" as a filename */
+ string_sub(line,"standard input","STDIN",0);
+ DEBUG(4,("despues [%s]\n", line));
+ all_string_sub(line,"-- ","\"",0);
+ all_string_sub(line," --","\"",0);
+ DEBUG(4,("despues 1 [%s]\n", line));
+
+ string_sub(line,"[job #","",0);
+ string_sub(line,"]","",0);
+ DEBUG(4,("despues 2 [%s]\n", line));
+
+ for (count=0; count<7 && next_token(&cline,tok[count],NULL,sizeof(tok[count])); count++) ;
+
+ /* we must get 7 tokens */
+ if (count < 7)
+ return(False);
+
+ /* the 3rd and 5th columns must be integer */
+ if (!isdigit((int)*tok[2]) || !isdigit((int)*tok[4])) return(False);
+
+ /* only take the last part of the filename */
+ {
+ fstring tmp;
+ char *p = strrchr_m(tok[6],'/');
+ if (p)
+ {
+ fstrcpy(tmp,p+1);
+ fstrcpy(tok[6],tmp);
+ }
+ }
+
+
+ buf->job = atoi(tok[2]);
+ buf->size = atoi(tok[4]);
+ buf->status = strequal(tok[3],"active")?LPQ_PRINTING:LPQ_QUEUED;
+ buf->priority = 0;
+ buf->time = time(NULL);
+ fstrcpy(buf->fs_user,tok[1]);
+ fstrcpy(buf->fs_file,tok[6]);
+ return(True);
+}
+
+
+/****************************************************************************
+ parse a lpq line for the plp printing system
+ Bertrand Wallrich <Bertrand.Wallrich@loria.fr>
+
+redone by tridge. Here is a sample queue:
+
+Local Printer 'lp2' (fjall):
+ Printing (started at Jun 15 13:33:58, attempt 1).
+ Rank Owner Pr Opt Job Host Files Size Date
+ active tridge X - 6 fjall /etc/hosts 739 Jun 15 13:33
+ 3rd tridge X - 7 fjall /etc/hosts 739 Jun 15 13:33
+
+****************************************************************************/
+static BOOL parse_lpq_plp(char *line,print_queue_struct *buf,BOOL first)
+{
+ fstring tok[11];
+ int count=0;
+ const char *cline = line;
+
+ /* handle the case of "(standard input)" as a filename */
+ string_sub(line,"stdin","STDIN",0);
+ all_string_sub(line,"(","\"",0);
+ all_string_sub(line,")","\"",0);
+
+ for (count=0; count<11 && next_token(&cline,tok[count],NULL,sizeof(tok[count])); count++) ;
+
+ /* we must get 11 tokens */
+ if (count < 11)
+ return(False);
+
+ /* the first must be "active" or begin with an integer */
+ if (strcmp(tok[0],"active") && !isdigit((int)tok[0][0]))
+ return(False);
+
+ /* the 5th and 8th must be integer */
+ if (!isdigit((int)*tok[4]) || !isdigit((int)*tok[7]))
+ return(False);
+
+ /* if the fname contains a space then use STDIN */
+ if (strchr_m(tok[6],' '))
+ fstrcpy(tok[6],"STDIN");
+
+ /* only take the last part of the filename */
+ {
+ fstring tmp;
+ char *p = strrchr_m(tok[6],'/');
+ if (p)
+ {
+ fstrcpy(tmp,p+1);
+ fstrcpy(tok[6],tmp);
+ }
+ }
+
+
+ buf->job = atoi(tok[4]);
+
+ buf->size = atoi(tok[7]);
+ if (strchr_m(tok[7],'K'))
+ buf->size *= 1024;
+ if (strchr_m(tok[7],'M'))
+ buf->size *= 1024*1024;
+
+ buf->status = strequal(tok[0],"active")?LPQ_PRINTING:LPQ_QUEUED;
+ buf->priority = 0;
+ buf->time = time(NULL);
+ fstrcpy(buf->fs_user,tok[1]);
+ fstrcpy(buf->fs_file,tok[6]);
+ return(True);
+}
+
+/*******************************************************************
+parse lpq on an NT system
+
+ Windows 2000 LPD Server
+ Printer \\10.0.0.2\NP17PCL (Paused)
+
+Owner Status Jobname Job-Id Size Pages Priority
+----------------------------------------------------------------------------
+root (9.99. Printing /usr/lib/rhs/rhs-pr 3 625 0 1
+root (9.99. Paused /usr/lib/rhs/rhs-pr 4 625 0 1
+jmcd Waiting Re: Samba Open Sour 26 32476 1 1
+
+********************************************************************/
+static BOOL parse_lpq_nt(char *line,print_queue_struct *buf,BOOL first)
+{
+#define LPRNT_OWNSIZ 11
+#define LPRNT_STATSIZ 9
+#define LPRNT_JOBSIZ 19
+#define LPRNT_IDSIZ 6
+#define LPRNT_SIZSIZ 9
+ typedef struct
+ {
+ char owner[LPRNT_OWNSIZ];
+ char space1;
+ char status[LPRNT_STATSIZ];
+ char space2;
+ char jobname[LPRNT_JOBSIZ];
+ char space3;
+ char jobid[LPRNT_IDSIZ];
+ char space4;
+ char size[LPRNT_SIZSIZ];
+ char terminator;
+ } nt_lpq_line;
+
+ nt_lpq_line parse_line;
+#define LPRNT_PRINTING "Printing"
+#define LPRNT_WAITING "Waiting"
+#define LPRNT_PAUSED "Paused"
+
+ memset(&parse_line, '\0', sizeof(parse_line));
+ strncpy((char *) &parse_line, line, sizeof(parse_line) -1);
+
+ if (strlen((char *) &parse_line) != sizeof(parse_line) - 1)
+ return(False);
+
+ /* Just want the first word in the owner field - the username */
+ if (strchr_m(parse_line.owner, ' '))
+ *(strchr_m(parse_line.owner, ' ')) = '\0';
+ else
+ parse_line.space1 = '\0';
+
+ /* Make sure we have an owner */
+ if (!strlen(parse_line.owner))
+ return(False);
+
+ /* Make sure the status is valid */
+ parse_line.space2 = '\0';
+ trim_char(parse_line.status, '\0', ' ');
+ if (!strequal(parse_line.status, LPRNT_PRINTING) &&
+ !strequal(parse_line.status, LPRNT_PAUSED) &&
+ !strequal(parse_line.status, LPRNT_WAITING))
+ return(False);
+
+ parse_line.space3 = '\0';
+ trim_char(parse_line.jobname, '\0', ' ');
+
+ buf->job = atoi(parse_line.jobid);
+ buf->priority = 0;
+ buf->size = atoi(parse_line.size);
+ buf->time = time(NULL);
+ fstrcpy(buf->fs_user, parse_line.owner);
+ fstrcpy(buf->fs_file, parse_line.jobname);
+ if (strequal(parse_line.status, LPRNT_PRINTING))
+ buf->status = LPQ_PRINTING;
+ else if (strequal(parse_line.status, LPRNT_PAUSED))
+ buf->status = LPQ_PAUSED;
+ else
+ buf->status = LPQ_QUEUED;
+
+ return(True);
+}
+
+/*******************************************************************
+parse lpq on an OS2 system
+
+JobID File Name Rank Size Status Comment
+----- --------------- ------ -------- ------------ ------------
+ 3 Control 1 68 Queued root@psflinu
+ 4 /etc/motd 2 11666 Queued root@psflinu
+
+********************************************************************/
+static BOOL parse_lpq_os2(char *line,print_queue_struct *buf,BOOL first)
+{
+#define LPROS2_IDSIZ 5
+#define LPROS2_JOBSIZ 15
+#define LPROS2_SIZSIZ 8
+#define LPROS2_STATSIZ 12
+#define LPROS2_OWNSIZ 12
+ typedef struct
+ {
+ char jobid[LPROS2_IDSIZ];
+ char space1[2];
+ char jobname[LPROS2_JOBSIZ];
+ char space2[14];
+ char size[LPROS2_SIZSIZ];
+ char space3[4];
+ char status[LPROS2_STATSIZ];
+ char space4[4];
+ char owner[LPROS2_OWNSIZ];
+ char terminator;
+ } os2_lpq_line;
+
+ os2_lpq_line parse_line;
+#define LPROS2_PRINTING "Printing"
+#define LPROS2_WAITING "Queued"
+#define LPROS2_PAUSED "Paused"
+
+ memset(&parse_line, '\0', sizeof(parse_line));
+ strncpy((char *) &parse_line, line, sizeof(parse_line) -1);
+
+ if (strlen((char *) &parse_line) != sizeof(parse_line) - 1)
+ return(False);
+
+ /* Get the jobid */
+ buf->job = atoi(parse_line.jobid);
+
+ /* Get the job name */
+ parse_line.space2[0] = '\0';
+ trim_char(parse_line.jobname, '\0', ' ');
+ fstrcpy(buf->fs_file, parse_line.jobname);
+
+ buf->priority = 0;
+ buf->size = atoi(parse_line.size);
+ buf->time = time(NULL);
+
+ /* Make sure we have an owner */
+ if (!strlen(parse_line.owner))
+ return(False);
+
+ /* Make sure we have a valid status */
+ parse_line.space4[0] = '\0';
+ trim_char(parse_line.status, '\0', ' ');
+ if (!strequal(parse_line.status, LPROS2_PRINTING) &&
+ !strequal(parse_line.status, LPROS2_PAUSED) &&
+ !strequal(parse_line.status, LPROS2_WAITING))
+ return(False);
+
+ fstrcpy(buf->fs_user, parse_line.owner);
+ if (strequal(parse_line.status, LPROS2_PRINTING))
+ buf->status = LPQ_PRINTING;
+ else if (strequal(parse_line.status, LPROS2_PAUSED))
+ buf->status = LPQ_PAUSED;
+ else
+ buf->status = LPQ_QUEUED;
+
+ return(True);
+}
+
+static const char *stat0_strings[] = { "enabled", "online", "idle", "no entries", "free", "ready", NULL };
+static const char *stat1_strings[] = { "offline", "disabled", "down", "off", "waiting", "no daemon", NULL };
+static const char *stat2_strings[] = { "jam", "paper", "error", "responding", "not accepting", "not running", "turned off", NULL };
+
+#ifdef DEVELOPER
+
+/****************************************************************************
+parse a vlp line
+****************************************************************************/
+static BOOL parse_lpq_vlp(char *line,print_queue_struct *buf,BOOL first)
+{
+ int toknum = 0;
+ fstring tok;
+ const char *cline = line;
+
+ /* First line is printer status */
+
+ if (!isdigit(line[0])) return False;
+
+ /* Parse a print job entry */
+
+ while(next_token(&cline, tok, NULL, sizeof(fstring))) {
+ switch (toknum) {
+ case 0:
+ buf->job = atoi(tok);
+ break;
+ case 1:
+ buf->size = atoi(tok);
+ break;
+ case 2:
+ buf->status = atoi(tok);
+ break;
+ case 3:
+ buf->time = atoi(tok);
+ break;
+ case 4:
+ fstrcpy(buf->fs_user, tok);
+ break;
+ case 5:
+ fstrcpy(buf->fs_file, tok);
+ break;
+ }
+ toknum++;
+ }
+
+ return True;
+}
+
+#endif /* DEVELOPER */
+
+/****************************************************************************
+parse a lpq line. Choose printing style
+****************************************************************************/
+
+BOOL parse_lpq_entry(int snum,char *line,
+ print_queue_struct *buf,
+ print_status_struct *status,BOOL first)
+{
+ BOOL ret;
+
+ switch (lp_printing(snum))
+ {
+ case PRINT_SYSV:
+ ret = parse_lpq_sysv(line,buf,first);
+ break;
+ case PRINT_AIX:
+ ret = parse_lpq_aix(line,buf,first);
+ break;
+ case PRINT_HPUX:
+ ret = parse_lpq_hpux(line,buf,first);
+ break;
+ case PRINT_QNX:
+ ret = parse_lpq_qnx(line,buf,first);
+ break;
+ case PRINT_LPRNG:
+ ret = parse_lpq_lprng(line,buf,first);
+ break;
+ case PRINT_PLP:
+ ret = parse_lpq_plp(line,buf,first);
+ break;
+ case PRINT_LPRNT:
+ ret = parse_lpq_nt(line,buf,first);
+ break;
+ case PRINT_LPROS2:
+ ret = parse_lpq_os2(line,buf,first);
+ break;
+#ifdef DEVELOPER
+ case PRINT_VLP:
+ case PRINT_TEST:
+ ret = parse_lpq_vlp(line,buf,first);
+ break;
+#endif /* DEVELOPER */
+ default:
+ ret = parse_lpq_bsd(line,buf,first);
+ break;
+ }
+
+ /* We don't want the newline in the status message. */
+ {
+ char *p = strchr_m(line,'\n');
+ if (p) *p = 0;
+ }
+
+ /* in the LPRNG case, we skip lines starting by a space.*/
+ if (line && !ret && (lp_printing(snum)==PRINT_LPRNG) )
+ {
+ if (line[0]==' ')
+ return ret;
+ }
+
+
+ if (status && !ret)
+ {
+ /* a few simple checks to see if the line might be a
+ printer status line:
+ handle them so that most severe condition is shown */
+ int i;
+ strlower_m(line);
+
+ switch (status->status) {
+ case LPSTAT_OK:
+ for (i=0; stat0_strings[i]; i++)
+ if (strstr_m(line,stat0_strings[i])) {
+ fstrcpy(status->message,line);
+ status->status=LPSTAT_OK;
+ return ret;
+ }
+ case LPSTAT_STOPPED:
+ for (i=0; stat1_strings[i]; i++)
+ if (strstr_m(line,stat1_strings[i])) {
+ fstrcpy(status->message,line);
+ status->status=LPSTAT_STOPPED;
+ return ret;
+ }
+ case LPSTAT_ERROR:
+ for (i=0; stat2_strings[i]; i++)
+ if (strstr_m(line,stat2_strings[i])) {
+ fstrcpy(status->message,line);
+ status->status=LPSTAT_ERROR;
+ return ret;
+ }
+ break;
+ }
+ }
+
+ return(ret);
+}
diff --git a/source/printing/notify.c b/source/printing/notify.c
new file mode 100644
index 00000000000..7750239630c
--- /dev/null
+++ b/source/printing/notify.c
@@ -0,0 +1,555 @@
+/*
+ Unix SMB/Netbios implementation.
+ Version 3.0
+ printing backend routines
+ Copyright (C) Tim Potter, 2002
+ Copyright (C) Gerald Carter, 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 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 "printing.h"
+
+static TALLOC_CTX *send_ctx;
+
+static unsigned int num_messages;
+
+static struct notify_queue {
+ struct notify_queue *next, *prev;
+ struct spoolss_notify_msg *msg;
+ struct timeval tv;
+ char *buf;
+ size_t buflen;
+} *notify_queue_head = NULL;
+
+
+static BOOL create_send_ctx(void)
+{
+ if (!send_ctx)
+ send_ctx = talloc_init("print notify queue");
+
+ if (!send_ctx)
+ return False;
+
+ return True;
+}
+
+/****************************************************************************
+ Turn a queue name into a snum.
+****************************************************************************/
+
+int print_queue_snum(const char *qname)
+{
+ int snum = lp_servicenumber(qname);
+ if (snum == -1 || !lp_print_ok(snum))
+ return -1;
+ return snum;
+}
+
+/*******************************************************************
+ Used to decide if we need a short select timeout.
+*******************************************************************/
+
+BOOL print_notify_messages_pending(void)
+{
+ return (notify_queue_head != NULL);
+}
+
+/*******************************************************************
+ Flatten data into a message.
+*******************************************************************/
+
+static BOOL flatten_message(struct notify_queue *q)
+{
+ struct spoolss_notify_msg *msg = q->msg;
+ char *buf = NULL;
+ size_t buflen = 0, len;
+
+again:
+ len = 0;
+
+ /* Pack header */
+
+ len += tdb_pack(buf + len, buflen - len, "f", msg->printer);
+
+ len += tdb_pack(buf + len, buflen - len, "ddddddd",
+ (uint32)q->tv.tv_sec, (uint32)q->tv.tv_usec,
+ msg->type, msg->field, msg->id, msg->len, msg->flags);
+
+ /* Pack data */
+
+ if (msg->len == 0)
+ len += tdb_pack(buf + len, buflen - len, "dd",
+ msg->notify.value[0], msg->notify.value[1]);
+ else
+ len += tdb_pack(buf + len, buflen - len, "B",
+ msg->len, msg->notify.data);
+
+ if (buflen != len) {
+ buf = talloc_realloc(send_ctx, buf, len);
+ if (!buf)
+ return False;
+ buflen = len;
+ goto again;
+ }
+
+ q->buf = buf;
+ q->buflen = buflen;
+
+ return True;
+}
+
+/*******************************************************************
+ Send the batched messages - on a per-printer basis.
+*******************************************************************/
+
+static void print_notify_send_messages_to_printer(const char *printer, unsigned int timeout)
+{
+ char *buf;
+ struct notify_queue *pq, *pq_next;
+ size_t msg_count = 0, offset = 0;
+ size_t num_pids = 0;
+ size_t i;
+ pid_t *pid_list = NULL;
+
+ /* Count the space needed to send the messages. */
+ for (pq = notify_queue_head; pq; pq = pq->next) {
+ if (strequal(printer, pq->msg->printer)) {
+ if (!flatten_message(pq)) {
+ DEBUG(0,("print_notify_send_messages: Out of memory\n"));
+ talloc_destroy_pool(send_ctx);
+ num_messages = 0;
+ return;
+ }
+ offset += (pq->buflen + 4);
+ msg_count++;
+ }
+ }
+ offset += 4; /* For count. */
+
+ buf = talloc(send_ctx, offset);
+ if (!buf) {
+ DEBUG(0,("print_notify_send_messages: Out of memory\n"));
+ talloc_destroy_pool(send_ctx);
+ num_messages = 0;
+ return;
+ }
+
+ offset = 0;
+ SIVAL(buf,offset,msg_count);
+ offset += 4;
+ for (pq = notify_queue_head; pq; pq = pq_next) {
+ pq_next = pq->next;
+
+ if (strequal(printer, pq->msg->printer)) {
+ SIVAL(buf,offset,pq->buflen);
+ offset += 4;
+ memcpy(buf + offset, pq->buf, pq->buflen);
+ offset += pq->buflen;
+
+ /* Remove from list. */
+ DLIST_REMOVE(notify_queue_head, pq);
+ }
+ }
+
+ DEBUG(5, ("print_notify_send_messages_to_printer: sending %lu print notify message%s to printer %s\n",
+ (unsigned long)msg_count, msg_count != 1 ? "s" : "", printer));
+
+ /*
+ * Get the list of PID's to send to.
+ */
+
+ if (!print_notify_pid_list(printer, send_ctx, &num_pids, &pid_list))
+ return;
+
+ for (i = 0; i < num_pids; i++) {
+ unsigned int q_len = messages_pending_for_pid(pid_list[i]);
+ if (q_len > 1000) {
+ DEBUG(5, ("print_notify_send_messages_to_printer: discarding notify to printer %s as queue length = %u\n",
+ printer, q_len ));
+ continue;
+ }
+ message_send_pid_with_timeout(pid_list[i], MSG_PRINTER_NOTIFY2, buf, offset, True, timeout);
+ }
+}
+
+/*******************************************************************
+ Actually send the batched messages.
+*******************************************************************/
+
+void print_notify_send_messages(unsigned int timeout)
+{
+ if (!print_notify_messages_pending())
+ return;
+
+ if (!create_send_ctx())
+ return;
+
+ while (print_notify_messages_pending())
+ print_notify_send_messages_to_printer(notify_queue_head->msg->printer, timeout);
+
+ talloc_destroy_pool(send_ctx);
+ num_messages = 0;
+}
+
+/**********************************************************************
+ deep copy a SPOOLSS_NOTIFY_MSG structure using a TALLOC_CTX
+ *********************************************************************/
+
+static BOOL copy_notify2_msg( SPOOLSS_NOTIFY_MSG *to, SPOOLSS_NOTIFY_MSG *from )
+{
+
+ if ( !to || !from )
+ return False;
+
+ memcpy( to, from, sizeof(SPOOLSS_NOTIFY_MSG) );
+
+ if ( from->len ) {
+ to->notify.data = talloc_memdup(send_ctx, from->notify.data, from->len );
+ if ( !to->notify.data ) {
+ DEBUG(0,("copy_notify2_msg: talloc_memdup() of size [%d] failed!\n", from->len ));
+ return False;
+ }
+ }
+
+
+ return True;
+}
+
+/*******************************************************************
+ Batch up print notify messages.
+*******************************************************************/
+
+static void send_spoolss_notify2_msg(SPOOLSS_NOTIFY_MSG *msg)
+{
+ struct notify_queue *pnqueue, *tmp_ptr;
+
+ /*
+ * Ensure we only have one job total_bytes and job total_pages for
+ * each job. There is no point in sending multiple messages that match
+ * as they will just cause flickering updates in the client.
+ */
+
+ if ((num_messages < 100) && (msg->type == JOB_NOTIFY_TYPE) &&
+ (msg->field == JOB_NOTIFY_TOTAL_BYTES || msg->field == JOB_NOTIFY_TOTAL_PAGES)) {
+
+ for (tmp_ptr = notify_queue_head; tmp_ptr; tmp_ptr = tmp_ptr->next) {
+ if (tmp_ptr->msg->type == msg->type &&
+ tmp_ptr->msg->field == msg->field &&
+ tmp_ptr->msg->id == msg->id &&
+ tmp_ptr->msg->flags == msg->flags &&
+ strequal(tmp_ptr->msg->printer, msg->printer)) {
+
+ DEBUG(5, ("send_spoolss_notify2_msg: replacing message 0x%02x/0x%02x for printer %s \
+in notify_queue\n", msg->type, msg->field, msg->printer));
+
+ tmp_ptr->msg = msg;
+ return;
+ }
+ }
+ }
+
+ /* Store the message on the pending queue. */
+
+ pnqueue = talloc(send_ctx, sizeof(*pnqueue));
+ if (!pnqueue) {
+ DEBUG(0,("send_spoolss_notify2_msg: Out of memory.\n"));
+ return;
+ }
+
+ /* allocate a new msg structure and copy the fields */
+
+ if ( !(pnqueue->msg = (SPOOLSS_NOTIFY_MSG*)talloc(send_ctx, sizeof(SPOOLSS_NOTIFY_MSG))) ) {
+ DEBUG(0,("send_spoolss_notify2_msg: talloc() of size [%lu] failed!\n",
+ (unsigned long)sizeof(SPOOLSS_NOTIFY_MSG)));
+ return;
+ }
+ copy_notify2_msg(pnqueue->msg, msg);
+ gettimeofday(&pnqueue->tv, NULL);
+ pnqueue->buf = NULL;
+ pnqueue->buflen = 0;
+
+ DEBUG(5, ("send_spoolss_notify2_msg: appending message 0x%02x/0x%02x for printer %s \
+to notify_queue_head\n", msg->type, msg->field, msg->printer));
+
+ /*
+ * Note we add to the end of the list to ensure
+ * the messages are sent in the order they were received. JRA.
+ */
+
+ DLIST_ADD_END(notify_queue_head, pnqueue, tmp_ptr);
+ num_messages++;
+}
+
+static void send_notify_field_values(const char *printer_name, uint32 type,
+ uint32 field, uint32 id, uint32 value1,
+ uint32 value2, uint32 flags)
+{
+ struct spoolss_notify_msg *msg;
+
+ if (lp_disable_spoolss())
+ return;
+
+ if (!create_send_ctx())
+ return;
+
+ msg = (struct spoolss_notify_msg *)talloc(send_ctx, sizeof(struct spoolss_notify_msg));
+ if (!msg)
+ return;
+
+ ZERO_STRUCTP(msg);
+
+ fstrcpy(msg->printer, printer_name);
+ msg->type = type;
+ msg->field = field;
+ msg->id = id;
+ msg->notify.value[0] = value1;
+ msg->notify.value[1] = value2;
+ msg->flags = flags;
+
+ send_spoolss_notify2_msg(msg);
+}
+
+static void send_notify_field_buffer(const char *printer_name, uint32 type,
+ uint32 field, uint32 id, uint32 len,
+ char *buffer)
+{
+ struct spoolss_notify_msg *msg;
+
+ if (lp_disable_spoolss())
+ return;
+
+ if (!create_send_ctx())
+ return;
+
+ msg = (struct spoolss_notify_msg *)talloc(send_ctx, sizeof(struct spoolss_notify_msg));
+ if (!msg)
+ return;
+
+ ZERO_STRUCTP(msg);
+
+ fstrcpy(msg->printer, printer_name);
+ msg->type = type;
+ msg->field = field;
+ msg->id = id;
+ msg->len = len;
+ msg->notify.data = buffer;
+
+ send_spoolss_notify2_msg(msg);
+}
+
+/* Send a message that the printer status has changed */
+
+void notify_printer_status_byname(const char *printer_name, uint32 status)
+{
+ /* Printer status stored in value1 */
+
+ send_notify_field_values(printer_name, PRINTER_NOTIFY_TYPE,
+ PRINTER_NOTIFY_STATUS, 0,
+ status, 0, 0);
+}
+
+void notify_printer_status(int snum, uint32 status)
+{
+ const char *printer_name = SERVICE(snum);
+
+ if (printer_name)
+ notify_printer_status_byname(printer_name, status);
+}
+
+void notify_job_status_byname(const char *printer_name, uint32 jobid, uint32 status,
+ uint32 flags)
+{
+ /* Job id stored in id field, status in value1 */
+
+ send_notify_field_values(printer_name, JOB_NOTIFY_TYPE,
+ JOB_NOTIFY_STATUS, jobid,
+ status, 0, flags);
+}
+
+void notify_job_status(int snum, uint32 jobid, uint32 status)
+{
+ const char *printer_name = SERVICE(snum);
+
+ notify_job_status_byname(printer_name, jobid, status, 0);
+}
+
+void notify_job_total_bytes(int snum, uint32 jobid, uint32 size)
+{
+ const char *printer_name = SERVICE(snum);
+
+ /* Job id stored in id field, status in value1 */
+
+ send_notify_field_values(printer_name, JOB_NOTIFY_TYPE,
+ JOB_NOTIFY_TOTAL_BYTES, jobid,
+ size, 0, 0);
+}
+
+void notify_job_total_pages(int snum, uint32 jobid, uint32 pages)
+{
+ const char *printer_name = SERVICE(snum);
+
+ /* Job id stored in id field, status in value1 */
+
+ send_notify_field_values(printer_name, JOB_NOTIFY_TYPE,
+ JOB_NOTIFY_TOTAL_PAGES, jobid,
+ pages, 0, 0);
+}
+
+void notify_job_username(int snum, uint32 jobid, char *name)
+{
+ const char *printer_name = SERVICE(snum);
+
+ send_notify_field_buffer(
+ printer_name, JOB_NOTIFY_TYPE, JOB_NOTIFY_USER_NAME,
+ jobid, strlen(name) + 1, name);
+}
+
+void notify_job_name(int snum, uint32 jobid, char *name)
+{
+ const char *printer_name = SERVICE(snum);
+
+ send_notify_field_buffer(
+ printer_name, JOB_NOTIFY_TYPE, JOB_NOTIFY_DOCUMENT,
+ jobid, strlen(name) + 1, name);
+}
+
+void notify_job_submitted(int snum, uint32 jobid, time_t submitted)
+{
+ const char *printer_name = SERVICE(snum);
+
+ send_notify_field_buffer(
+ printer_name, JOB_NOTIFY_TYPE, JOB_NOTIFY_SUBMITTED,
+ jobid, sizeof(submitted), (char *)&submitted);
+}
+
+void notify_printer_driver(int snum, char *driver_name)
+{
+ const char *printer_name = SERVICE(snum);
+
+ send_notify_field_buffer(
+ printer_name, PRINTER_NOTIFY_TYPE, PRINTER_NOTIFY_DRIVER_NAME,
+ snum, strlen(driver_name) + 1, driver_name);
+}
+
+void notify_printer_comment(int snum, char *comment)
+{
+ const char *printer_name = SERVICE(snum);
+
+ send_notify_field_buffer(
+ printer_name, PRINTER_NOTIFY_TYPE, PRINTER_NOTIFY_COMMENT,
+ snum, strlen(comment) + 1, comment);
+}
+
+void notify_printer_sharename(int snum, char *share_name)
+{
+ const char *printer_name = SERVICE(snum);
+
+ send_notify_field_buffer(
+ printer_name, PRINTER_NOTIFY_TYPE, PRINTER_NOTIFY_SHARE_NAME,
+ snum, strlen(share_name) + 1, share_name);
+}
+
+void notify_printer_port(int snum, char *port_name)
+{
+ const char *printer_name = SERVICE(snum);
+
+ send_notify_field_buffer(
+ printer_name, PRINTER_NOTIFY_TYPE, PRINTER_NOTIFY_PORT_NAME,
+ snum, strlen(port_name) + 1, port_name);
+}
+
+void notify_printer_location(int snum, char *location)
+{
+ const char *printer_name = SERVICE(snum);
+
+ send_notify_field_buffer(
+ printer_name, PRINTER_NOTIFY_TYPE, PRINTER_NOTIFY_LOCATION,
+ snum, strlen(location) + 1, location);
+}
+
+void notify_printer_byname( const char *printername, uint32 change, char *value )
+{
+ int snum = print_queue_snum(printername);
+ int type = PRINTER_NOTIFY_TYPE;
+
+ if ( snum == -1 )
+ return;
+
+ send_notify_field_buffer( printername, type, change, snum, strlen(value)+1, value );
+}
+
+
+/****************************************************************************
+ Return a malloced list of pid_t's that are interested in getting update
+ messages on this print queue. Used in printing/notify to send the messages.
+****************************************************************************/
+
+BOOL print_notify_pid_list(const char *printername, TALLOC_CTX *mem_ctx, size_t *p_num_pids, pid_t **pp_pid_list)
+{
+ struct tdb_print_db *pdb = NULL;
+ TDB_CONTEXT *tdb = NULL;
+ TDB_DATA data;
+ BOOL ret = True;
+ size_t i, num_pids, offset;
+ pid_t *pid_list;
+
+ *p_num_pids = 0;
+ *pp_pid_list = NULL;
+
+ pdb = get_print_db_byname(printername);
+ if (!pdb)
+ return False;
+ tdb = pdb->tdb;
+
+ if (tdb_read_lock_bystring(tdb, NOTIFY_PID_LIST_KEY, 10) == -1) {
+ DEBUG(0,("print_notify_pid_list: Failed to lock printer %s database\n",
+ printername));
+ if (pdb)
+ release_print_db(pdb);
+ return False;
+ }
+
+ data = get_printer_notify_pid_list( tdb, printername, True );
+
+ if (!data.dptr) {
+ ret = True;
+ goto done;
+ }
+
+ num_pids = data.dsize / 8;
+
+ if ((pid_list = (pid_t *)talloc(mem_ctx, sizeof(pid_t) * num_pids)) == NULL) {
+ ret = False;
+ goto done;
+ }
+
+ for( i = 0, offset = 0; offset < data.dsize; offset += 8, i++)
+ pid_list[i] = (pid_t)IVAL(data.dptr, offset);
+
+ *pp_pid_list = pid_list;
+ *p_num_pids = num_pids;
+
+ ret = True;
+
+ done:
+
+ tdb_read_unlock_bystring(tdb, NOTIFY_PID_LIST_KEY);
+ if (pdb)
+ release_print_db(pdb);
+ SAFE_FREE(data.dptr);
+ return ret;
+}
diff --git a/source/printing/nt_printing.c b/source/printing/nt_printing.c
new file mode 100644
index 00000000000..3c860fc5650
--- /dev/null
+++ b/source/printing/nt_printing.c
@@ -0,0 +1,4990 @@
+/*
+ * Unix SMB/CIFS implementation.
+ * RPC Pipe client / server routines
+ * Copyright (C) Andrew Tridgell 1992-2000,
+ * Copyright (C) Jean François Micouleau 1998-2000.
+ * Copyright (C) Gerald Carter 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 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"
+
+extern DOM_SID global_sid_World;
+
+static TDB_CONTEXT *tdb_forms; /* used for forms files */
+static TDB_CONTEXT *tdb_drivers; /* used for driver files */
+static TDB_CONTEXT *tdb_printers; /* used for printers files */
+
+#define FORMS_PREFIX "FORMS/"
+#define DRIVERS_PREFIX "DRIVERS/"
+#define DRIVER_INIT_PREFIX "DRIVER_INIT/"
+#define PRINTERS_PREFIX "PRINTERS/"
+#define SECDESC_PREFIX "SECDESC/"
+#define GLOBAL_C_SETPRINTER "GLOBALS/c_setprinter"
+
+#define NTDRIVERS_DATABASE_VERSION_1 1
+#define NTDRIVERS_DATABASE_VERSION_2 2
+#define NTDRIVERS_DATABASE_VERSION_3 3 /* little endian version of v2 */
+
+#define NTDRIVERS_DATABASE_VERSION NTDRIVERS_DATABASE_VERSION_3
+
+/* Map generic permissions to printer object specific permissions */
+
+GENERIC_MAPPING printer_generic_mapping = {
+ PRINTER_READ,
+ PRINTER_WRITE,
+ PRINTER_EXECUTE,
+ PRINTER_ALL_ACCESS
+};
+
+STANDARD_MAPPING printer_std_mapping = {
+ PRINTER_READ,
+ PRINTER_WRITE,
+ PRINTER_EXECUTE,
+ PRINTER_ALL_ACCESS
+};
+
+/* Map generic permissions to print server object specific permissions */
+
+GENERIC_MAPPING printserver_generic_mapping = {
+ SERVER_READ,
+ SERVER_WRITE,
+ SERVER_EXECUTE,
+ SERVER_ALL_ACCESS
+};
+
+STANDARD_MAPPING printserver_std_mapping = {
+ SERVER_READ,
+ SERVER_WRITE,
+ SERVER_EXECUTE,
+ SERVER_ALL_ACCESS
+};
+
+/* We need one default form to support our default printer. Msoft adds the
+forms it wants and in the ORDER it wants them (note: DEVMODE papersize is an
+array index). Letter is always first, so (for the current code) additions
+always put things in the correct order. */
+static const nt_forms_struct default_forms[] = {
+ {"Letter",0x1,0x34b5c,0x44368,0x0,0x0,0x34b5c,0x44368},
+ {"Letter Small",0x1,0x34b5c,0x44368,0x0,0x0,0x34b5c,0x44368},
+ {"Tabloid",0x1,0x44368,0x696b8,0x0,0x0,0x44368,0x696b8},
+ {"Ledger",0x1,0x696b8,0x44368,0x0,0x0,0x696b8,0x44368},
+ {"Legal",0x1,0x34b5c,0x56d10,0x0,0x0,0x34b5c,0x56d10},
+ {"Statement",0x1,0x221b4,0x34b5c,0x0,0x0,0x221b4,0x34b5c},
+ {"Executive",0x1,0x2cf56,0x411cc,0x0,0x0,0x2cf56,0x411cc},
+ {"A3",0x1,0x48828,0x668a0,0x0,0x0,0x48828,0x668a0},
+ {"A4",0x1,0x33450,0x48828,0x0,0x0,0x33450,0x48828},
+ {"A4 Small",0x1,0x33450,0x48828,0x0,0x0,0x33450,0x48828},
+ {"A5",0x1,0x24220,0x33450,0x0,0x0,0x24220,0x33450},
+ {"B4 (JIS)",0x1,0x3ebe8,0x58de0,0x0,0x0,0x3ebe8,0x58de0},
+ {"B5 (JIS)",0x1,0x2c6f0,0x3ebe8,0x0,0x0,0x2c6f0,0x3ebe8},
+ {"Folio",0x1,0x34b5c,0x509d8,0x0,0x0,0x34b5c,0x509d8},
+ {"Quarto",0x1,0x347d8,0x43238,0x0,0x0,0x347d8,0x43238},
+ {"10x14",0x1,0x3e030,0x56d10,0x0,0x0,0x3e030,0x56d10},
+ {"11x17",0x1,0x44368,0x696b8,0x0,0x0,0x44368,0x696b8},
+ {"Note",0x1,0x34b5c,0x44368,0x0,0x0,0x34b5c,0x44368},
+ {"Envelope #9",0x1,0x18079,0x37091,0x0,0x0,0x18079,0x37091},
+ {"Envelope #10",0x1,0x19947,0x3ae94,0x0,0x0,0x19947,0x3ae94},
+ {"Envelope #11",0x1,0x1be7c,0x40565,0x0,0x0,0x1be7c,0x40565},
+ {"Envelope #12",0x1,0x1d74a,0x44368,0x0,0x0,0x1d74a,0x44368},
+ {"Envelope #14",0x1,0x1f018,0x47504,0x0,0x0,0x1f018,0x47504},
+ {"C size sheet",0x1,0x696b8,0x886d0,0x0,0x0,0x696b8,0x886d0},
+ {"D size sheet",0x1,0x886d0,0xd2d70,0x0,0x0,0x886d0,0xd2d70},
+ {"E size sheet",0x1,0xd2d70,0x110da0,0x0,0x0,0xd2d70,0x110da0},
+ {"Envelope DL",0x1,0x1adb0,0x35b60,0x0,0x0,0x1adb0,0x35b60},
+ {"Envelope C5",0x1,0x278d0,0x37e88,0x0,0x0,0x278d0,0x37e88},
+ {"Envelope C3",0x1,0x4f1a0,0x6fd10,0x0,0x0,0x4f1a0,0x6fd10},
+ {"Envelope C4",0x1,0x37e88,0x4f1a0,0x0,0x0,0x37e88,0x4f1a0},
+ {"Envelope C6",0x1,0x1bd50,0x278d0,0x0,0x0,0x1bd50,0x278d0},
+ {"Envelope C65",0x1,0x1bd50,0x37e88,0x0,0x0,0x1bd50,0x37e88},
+ {"Envelope B4",0x1,0x3d090,0x562e8,0x0,0x0,0x3d090,0x562e8},
+ {"Envelope B5",0x1,0x2af80,0x3d090,0x0,0x0,0x2af80,0x3d090},
+ {"Envelope B6",0x1,0x2af80,0x1e848,0x0,0x0,0x2af80,0x1e848},
+ {"Envelope",0x1,0x1adb0,0x38270,0x0,0x0,0x1adb0,0x38270},
+ {"Envelope Monarch",0x1,0x18079,0x2e824,0x0,0x0,0x18079,0x2e824},
+ {"6 3/4 Envelope",0x1,0x167ab,0x284ec,0x0,0x0,0x167ab,0x284ec},
+ {"US Std Fanfold",0x1,0x5c3e1,0x44368,0x0,0x0,0x5c3e1,0x44368},
+ {"German Std Fanfold",0x1,0x34b5c,0x4a6a0,0x0,0x0,0x34b5c,0x4a6a0},
+ {"German Legal Fanfold",0x1,0x34b5c,0x509d8,0x0,0x0,0x34b5c,0x509d8},
+ {"B4 (ISO)",0x1,0x3d090,0x562e8,0x0,0x0,0x3d090,0x562e8},
+ {"Japanese Postcard",0x1,0x186a0,0x24220,0x0,0x0,0x186a0,0x24220},
+ {"9x11",0x1,0x37cf8,0x44368,0x0,0x0,0x37cf8,0x44368},
+ {"10x11",0x1,0x3e030,0x44368,0x0,0x0,0x3e030,0x44368},
+ {"15x11",0x1,0x5d048,0x44368,0x0,0x0,0x5d048,0x44368},
+ {"Envelope Invite",0x1,0x35b60,0x35b60,0x0,0x0,0x35b60,0x35b60},
+ {"Reserved48",0x1,0x1,0x1,0x0,0x0,0x1,0x1},
+ {"Reserved49",0x1,0x1,0x1,0x0,0x0,0x1,0x1},
+ {"Letter Extra",0x1,0x3ae94,0x4a6a0,0x0,0x0,0x3ae94,0x4a6a0},
+ {"Legal Extra",0x1,0x3ae94,0x5d048,0x0,0x0,0x3ae94,0x5d048},
+ {"Tabloid Extra",0x1,0x4a6a0,0x6f9f0,0x0,0x0,0x4a6a0,0x6f9f0},
+ {"A4 Extra",0x1,0x397c2,0x4eb16,0x0,0x0,0x397c2,0x4eb16},
+ {"Letter Transverse",0x1,0x34b5c,0x44368,0x0,0x0,0x34b5c,0x44368},
+ {"A4 Transverse",0x1,0x33450,0x48828,0x0,0x0,0x33450,0x48828},
+ {"Letter Extra Transverse",0x1,0x3ae94,0x4a6a0,0x0,0x0,0x3ae94,0x4a6a0},
+ {"Super A",0x1,0x376b8,0x56ea0,0x0,0x0,0x376b8,0x56ea0},
+ {"Super B",0x1,0x4a768,0x76e58,0x0,0x0,0x4a768,0x76e58},
+ {"Letter Plus",0x1,0x34b5c,0x4eb16,0x0,0x0,0x34b5c,0x4eb16},
+ {"A4 Plus",0x1,0x33450,0x50910,0x0,0x0,0x33450,0x50910},
+ {"A5 Transverse",0x1,0x24220,0x33450,0x0,0x0,0x24220,0x33450},
+ {"B5 (JIS) Transverse",0x1,0x2c6f0,0x3ebe8,0x0,0x0,0x2c6f0,0x3ebe8},
+ {"A3 Extra",0x1,0x4e9d0,0x6ca48,0x0,0x0,0x4e9d0,0x6ca48},
+ {"A5 Extra",0x1,0x2a7b0,0x395f8,0x0,0x0,0x2a7b0,0x395f8},
+ {"B5 (ISO) Extra",0x1,0x31128,0x43620,0x0,0x0,0x31128,0x43620},
+ {"A2",0x1,0x668a0,0x91050,0x0,0x0,0x668a0,0x91050},
+ {"A3 Transverse",0x1,0x48828,0x668a0,0x0,0x0,0x48828,0x668a0},
+ {"A3 Extra Transverse",0x1,0x4e9d0,0x6ca48,0x0,0x0,0x4e9d0,0x6ca48},
+ {"Japanese Double Postcard",0x1,0x30d40,0x24220,0x0,0x0,0x30d40,0x24220},
+ {"A6",0x1,0x19a28,0x24220,0x0,0x0,0x19a28,0x24220},
+ {"Japanese Envelope Kaku #2",0x1,0x3a980,0x510e0,0x0,0x0,0x3a980,0x510e0},
+ {"Japanese Envelope Kaku #3",0x1,0x34bc0,0x43a08,0x0,0x0,0x34bc0,0x43a08},
+ {"Japanese Envelope Chou #3",0x1,0x1d4c0,0x395f8,0x0,0x0,0x1d4c0,0x395f8},
+ {"Japanese Envelope Chou #4",0x1,0x15f90,0x320c8,0x0,0x0,0x15f90,0x320c8},
+ {"Letter Rotated",0x1,0x44368,0x34b5c,0x0,0x0,0x44368,0x34b5c},
+ {"A3 Rotated",0x1,0x668a0,0x48828,0x0,0x0,0x668a0,0x48828},
+ {"A4 Rotated",0x1,0x48828,0x33450,0x0,0x0,0x48828,0x33450},
+ {"A5 Rotated",0x1,0x33450,0x24220,0x0,0x0,0x33450,0x24220},
+ {"B4 (JIS) Rotated",0x1,0x58de0,0x3ebe8,0x0,0x0,0x58de0,0x3ebe8},
+ {"B5 (JIS) Rotated",0x1,0x3ebe8,0x2c6f0,0x0,0x0,0x3ebe8,0x2c6f0},
+ {"Japanese Postcard Rotated",0x1,0x24220,0x186a0,0x0,0x0,0x24220,0x186a0},
+ {"Double Japan Postcard Rotated",0x1,0x24220,0x30d40,0x0,0x0,0x24220,0x30d40},
+ {"A6 Rotated",0x1,0x24220,0x19a28,0x0,0x0,0x24220,0x19a28},
+ {"Japan Envelope Kaku #2 Rotated",0x1,0x510e0,0x3a980,0x0,0x0,0x510e0,0x3a980},
+ {"Japan Envelope Kaku #3 Rotated",0x1,0x43a08,0x34bc0,0x0,0x0,0x43a08, 0x34bc0},
+ {"Japan Envelope Chou #3 Rotated",0x1,0x395f8,0x1d4c0,0x0,0x0,0x395f8,0x1d4c0},
+ {"Japan Envelope Chou #4 Rotated",0x1,0x320c8,0x15f90,0x0,0x0,0x320c8,0x15f90},
+ {"B6 (JIS)",0x1,0x1f400,0x2c6f0,0x0,0x0,0x1f400,0x2c6f0},
+ {"B6 (JIS) Rotated",0x1,0x2c6f0,0x1f400,0x0,0x0,0x2c6f0,0x1f400},
+ {"12x11",0x1,0x4a724,0x443e1,0x0,0x0,0x4a724,0x443e1},
+ {"Japan Envelope You #4",0x1,0x19a28,0x395f8,0x0,0x0,0x19a28,0x395f8},
+ {"Japan Envelope You #4 Rotated",0x1,0x395f8,0x19a28,0x0,0x0,0x395f8,0x19a28},
+ {"PRC 16K",0x1,0x2de60,0x3f7a0,0x0,0x0,0x2de60,0x3f7a0},
+ {"PRC 32K",0x1,0x1fbd0,0x2cec0,0x0,0x0,0x1fbd0,0x2cec0},
+ {"PRC 32K(Big)",0x1,0x222e0,0x318f8,0x0,0x0,0x222e0,0x318f8},
+ {"PRC Envelope #1",0x1,0x18e70,0x28488,0x0,0x0,0x18e70,0x28488},
+ {"PRC Envelope #2",0x1,0x18e70,0x2af80,0x0,0x0,0x18e70,0x2af80},
+ {"PRC Envelope #3",0x1,0x1e848,0x2af80,0x0,0x0,0x1e848,0x2af80},
+ {"PRC Envelope #4",0x1,0x1adb0,0x32c80,0x0,0x0,0x1adb0,0x32c80},
+ {"PRC Envelope #5",0x1,0x1adb0,0x35b60,0x0,0x0,0x1adb0,0x35b60},
+ {"PRC Envelope #6",0x1,0x1d4c0,0x38270,0x0,0x0,0x1d4c0,0x38270},
+ {"PRC Envelope #7",0x1,0x27100,0x38270,0x0,0x0,0x27100,0x38270},
+ {"PRC Envelope #8",0x1,0x1d4c0,0x4b708,0x0,0x0,0x1d4c0,0x4b708},
+ {"PRC Envelope #9",0x1,0x37e88,0x4f1a0,0x0,0x0,0x37e88,0x4f1a0},
+ {"PRC Envelope #10",0x1,0x4f1a0,0x6fd10,0x0,0x0,0x4f1a0,0x6fd10},
+ {"PRC 16K Rotated",0x1,0x3f7a0,0x2de60,0x0,0x0,0x3f7a0,0x2de60},
+ {"PRC 32K Rotated",0x1,0x2cec0,0x1fbd0,0x0,0x0,0x2cec0,0x1fbd0},
+ {"PRC 32K(Big) Rotated",0x1,0x318f8,0x222e0,0x0,0x0,0x318f8,0x222e0},
+ {"PRC Envelope #1 Rotated",0x1,0x28488,0x18e70,0x0,0x0,0x28488,0x18e70},
+ {"PRC Envelope #2 Rotated",0x1,0x2af80,0x18e70,0x0,0x0,0x2af80,0x18e70},
+ {"PRC Envelope #3 Rotated",0x1,0x2af80,0x1e848,0x0,0x0,0x2af80,0x1e848},
+ {"PRC Envelope #4 Rotated",0x1,0x32c80,0x1adb0,0x0,0x0,0x32c80,0x1adb0},
+ {"PRC Envelope #5 Rotated",0x1,0x35b60,0x1adb0,0x0,0x0,0x35b60,0x1adb0},
+ {"PRC Envelope #6 Rotated",0x1,0x38270,0x1d4c0,0x0,0x0,0x38270,0x1d4c0},
+ {"PRC Envelope #7 Rotated",0x1,0x38270,0x27100,0x0,0x0,0x38270,0x27100},
+ {"PRC Envelope #8 Rotated",0x1,0x4b708,0x1d4c0,0x0,0x0,0x4b708,0x1d4c0},
+ {"PRC Envelope #9 Rotated",0x1,0x4f1a0,0x37e88,0x0,0x0,0x4f1a0,0x37e88},
+ {"PRC Envelope #10 Rotated",0x1,0x6fd10,0x4f1a0,0x0,0x0,0x6fd10,0x4f1a0}
+};
+
+struct table_node {
+ const char *long_archi;
+ const char *short_archi;
+ int version;
+};
+
+#define SPL_ARCH_WIN40 "WIN40"
+#define SPL_ARCH_W32X86 "W32X86"
+#define SPL_ARCH_W32MIPS "W32MIPS"
+#define SPL_ARCH_W32ALPHA "W32ALPHA"
+#define SPL_ARCH_W32PPC "W32PPC"
+
+static const struct table_node archi_table[]= {
+
+ {"Windows 4.0", SPL_ARCH_WIN40, 0 },
+ {"Windows NT x86", SPL_ARCH_W32X86, 2 },
+ {"Windows NT R4000", SPL_ARCH_W32MIPS, 2 },
+ {"Windows NT Alpha_AXP", SPL_ARCH_W32ALPHA, 2 },
+ {"Windows NT PowerPC", SPL_ARCH_W32PPC, 2 },
+ {NULL, "", -1 }
+};
+
+static BOOL upgrade_to_version_3(void)
+{
+ TDB_DATA kbuf, newkey, dbuf;
+
+ DEBUG(0,("upgrade_to_version_3: upgrading print tdb's to version 3\n"));
+
+ for (kbuf = tdb_firstkey(tdb_drivers); kbuf.dptr;
+ newkey = tdb_nextkey(tdb_drivers, kbuf), safe_free(kbuf.dptr), kbuf=newkey) {
+
+ dbuf = tdb_fetch(tdb_drivers, kbuf);
+
+ if (strncmp(kbuf.dptr, FORMS_PREFIX, strlen(FORMS_PREFIX)) == 0) {
+ DEBUG(0,("upgrade_to_version_3:moving form\n"));
+ if (tdb_store(tdb_forms, kbuf, dbuf, TDB_REPLACE) != 0) {
+ SAFE_FREE(dbuf.dptr);
+ DEBUG(0,("upgrade_to_version_3: failed to move form. Error (%s).\n", tdb_errorstr(tdb_forms)));
+ return False;
+ }
+ if (tdb_delete(tdb_drivers, kbuf) != 0) {
+ SAFE_FREE(dbuf.dptr);
+ DEBUG(0,("upgrade_to_version_3: failed to delete form. Error (%s)\n", tdb_errorstr(tdb_drivers)));
+ return False;
+ }
+ }
+
+ if (strncmp(kbuf.dptr, PRINTERS_PREFIX, strlen(PRINTERS_PREFIX)) == 0) {
+ DEBUG(0,("upgrade_to_version_3:moving printer\n"));
+ if (tdb_store(tdb_printers, kbuf, dbuf, TDB_REPLACE) != 0) {
+ SAFE_FREE(dbuf.dptr);
+ DEBUG(0,("upgrade_to_version_3: failed to move printer. Error (%s)\n", tdb_errorstr(tdb_printers)));
+ return False;
+ }
+ if (tdb_delete(tdb_drivers, kbuf) != 0) {
+ SAFE_FREE(dbuf.dptr);
+ DEBUG(0,("upgrade_to_version_3: failed to delete printer. Error (%s)\n", tdb_errorstr(tdb_drivers)));
+ return False;
+ }
+ }
+
+ if (strncmp(kbuf.dptr, SECDESC_PREFIX, strlen(SECDESC_PREFIX)) == 0) {
+ DEBUG(0,("upgrade_to_version_3:moving secdesc\n"));
+ if (tdb_store(tdb_printers, kbuf, dbuf, TDB_REPLACE) != 0) {
+ SAFE_FREE(dbuf.dptr);
+ DEBUG(0,("upgrade_to_version_3: failed to move secdesc. Error (%s)\n", tdb_errorstr(tdb_printers)));
+ return False;
+ }
+ if (tdb_delete(tdb_drivers, kbuf) != 0) {
+ SAFE_FREE(dbuf.dptr);
+ DEBUG(0,("upgrade_to_version_3: failed to delete secdesc. Error (%s)\n", tdb_errorstr(tdb_drivers)));
+ return False;
+ }
+ }
+
+ SAFE_FREE(dbuf.dptr);
+ }
+
+ return True;
+}
+
+/****************************************************************************
+ Open the NT printing tdbs. Done once before fork().
+****************************************************************************/
+
+BOOL nt_printing_init(void)
+{
+ static pid_t local_pid;
+ const char *vstring = "INFO/version";
+
+ if (tdb_drivers && tdb_printers && tdb_forms && local_pid == sys_getpid())
+ return True;
+
+ if (tdb_drivers)
+ tdb_close(tdb_drivers);
+ tdb_drivers = tdb_open_log(lock_path("ntdrivers.tdb"), 0, TDB_DEFAULT, O_RDWR|O_CREAT, 0600);
+ if (!tdb_drivers) {
+ DEBUG(0,("nt_printing_init: Failed to open nt drivers database %s (%s)\n",
+ lock_path("ntdrivers.tdb"), strerror(errno) ));
+ return False;
+ }
+
+ if (tdb_printers)
+ tdb_close(tdb_printers);
+ tdb_printers = tdb_open_log(lock_path("ntprinters.tdb"), 0, TDB_DEFAULT, O_RDWR|O_CREAT, 0600);
+ if (!tdb_printers) {
+ DEBUG(0,("nt_printing_init: Failed to open nt printers database %s (%s)\n",
+ lock_path("ntprinters.tdb"), strerror(errno) ));
+ return False;
+ }
+
+ if (tdb_forms)
+ tdb_close(tdb_forms);
+ tdb_forms = tdb_open_log(lock_path("ntforms.tdb"), 0, TDB_DEFAULT, O_RDWR|O_CREAT, 0600);
+ if (!tdb_forms) {
+ DEBUG(0,("nt_printing_init: Failed to open nt forms database %s (%s)\n",
+ lock_path("ntforms.tdb"), strerror(errno) ));
+ return False;
+ }
+
+ local_pid = sys_getpid();
+
+ /* handle a Samba upgrade */
+ tdb_lock_bystring(tdb_drivers, vstring, 0);
+ {
+ int32 vers_id;
+
+ /* Cope with byte-reversed older versions of the db. */
+ vers_id = tdb_fetch_int32(tdb_drivers, vstring);
+ if ((vers_id == NTDRIVERS_DATABASE_VERSION_2) || (IREV(vers_id) == NTDRIVERS_DATABASE_VERSION_2)) {
+ /* Written on a bigendian machine with old fetch_int code. Save as le. */
+ /* The only upgrade between V2 and V3 is to save the version in little-endian. */
+ tdb_store_int32(tdb_drivers, vstring, NTDRIVERS_DATABASE_VERSION);
+ vers_id = NTDRIVERS_DATABASE_VERSION;
+ }
+
+ if (vers_id != NTDRIVERS_DATABASE_VERSION) {
+
+ if ((vers_id == NTDRIVERS_DATABASE_VERSION_1) || (IREV(vers_id) == NTDRIVERS_DATABASE_VERSION_1)) {
+ if (!upgrade_to_version_3())
+ return False;
+ } else
+ tdb_traverse(tdb_drivers, tdb_traverse_delete_fn, NULL);
+
+ tdb_store_int32(tdb_drivers, vstring, NTDRIVERS_DATABASE_VERSION);
+ }
+ }
+ tdb_unlock_bystring(tdb_drivers, vstring);
+
+ update_c_setprinter(True);
+
+ /*
+ * register callback to handle updating printers as new
+ * drivers are installed
+ */
+
+ message_register( MSG_PRINTER_DRVUPGRADE, do_drv_upgrade_printer );
+
+ /*
+ * register callback to handle updating printer data
+ * when a driver is initialized
+ */
+
+ message_register( MSG_PRINTERDATA_INIT_RESET, reset_all_printerdata );
+
+
+ return True;
+}
+
+/*******************************************************************
+ tdb traversal function for counting printers.
+********************************************************************/
+
+static int traverse_counting_printers(TDB_CONTEXT *t, TDB_DATA key,
+ TDB_DATA data, void *context)
+{
+ int *printer_count = (int*)context;
+
+ if (memcmp(PRINTERS_PREFIX, key.dptr, sizeof(PRINTERS_PREFIX)-1) == 0) {
+ (*printer_count)++;
+ DEBUG(10,("traverse_counting_printers: printer = [%s] printer_count = %d\n", key.dptr, *printer_count));
+ }
+
+ return 0;
+}
+
+/*******************************************************************
+ Update the spooler global c_setprinter. This variable is initialized
+ when the parent smbd starts with the number of existing printers. It
+ is monotonically increased by the current number of printers *after*
+ each add or delete printer RPC. Only Microsoft knows why... JRR020119
+********************************************************************/
+
+uint32 update_c_setprinter(BOOL initialize)
+{
+ int32 c_setprinter;
+ int32 printer_count = 0;
+
+ tdb_lock_bystring(tdb_printers, GLOBAL_C_SETPRINTER, 0);
+
+ /* Traverse the tdb, counting the printers */
+ tdb_traverse(tdb_printers, traverse_counting_printers, (void *)&printer_count);
+
+ /* If initializing, set c_setprinter to current printers count
+ * otherwise, bump it by the current printer count
+ */
+ if (!initialize)
+ c_setprinter = tdb_fetch_int32(tdb_printers, GLOBAL_C_SETPRINTER) + printer_count;
+ else
+ c_setprinter = printer_count;
+
+ DEBUG(10,("update_c_setprinter: c_setprinter = %u\n", (unsigned int)c_setprinter));
+ tdb_store_int32(tdb_printers, GLOBAL_C_SETPRINTER, c_setprinter);
+
+ tdb_unlock_bystring(tdb_printers, GLOBAL_C_SETPRINTER);
+
+ return (uint32)c_setprinter;
+}
+
+/*******************************************************************
+ Get the spooler global c_setprinter, accounting for initialization.
+********************************************************************/
+
+uint32 get_c_setprinter(void)
+{
+ int32 c_setprinter = tdb_fetch_int32(tdb_printers, GLOBAL_C_SETPRINTER);
+
+ if (c_setprinter == (int32)-1)
+ c_setprinter = update_c_setprinter(True);
+
+ DEBUG(10,("get_c_setprinter: c_setprinter = %d\n", c_setprinter));
+
+ return (uint32)c_setprinter;
+}
+
+/****************************************************************************
+ Get builtin form struct list.
+****************************************************************************/
+
+int get_builtin_ntforms(nt_forms_struct **list)
+{
+ *list = (nt_forms_struct *)memdup(&default_forms[0], sizeof(default_forms));
+ return sizeof(default_forms) / sizeof(default_forms[0]);
+}
+
+/****************************************************************************
+ get a builtin form struct
+****************************************************************************/
+
+BOOL get_a_builtin_ntform(UNISTR2 *uni_formname,nt_forms_struct *form)
+{
+ int i,count;
+ fstring form_name;
+ unistr2_to_ascii(form_name, uni_formname, sizeof(form_name)-1);
+ DEBUGADD(6,("Looking for builtin form %s \n", form_name));
+ count = sizeof(default_forms) / sizeof(default_forms[0]);
+ for (i=0;i<count;i++) {
+ if (strequal(form_name,default_forms[i].name)) {
+ DEBUGADD(6,("Found builtin form %s \n", form_name));
+ memcpy(form,&default_forms[i],sizeof(*form));
+ break;
+ }
+ }
+
+ return (i !=count);
+}
+
+/****************************************************************************
+get a form struct list
+****************************************************************************/
+int get_ntforms(nt_forms_struct **list)
+{
+ TDB_DATA kbuf, newkey, dbuf;
+ nt_forms_struct *tl;
+ nt_forms_struct form;
+ int ret;
+ int i;
+ int n = 0;
+
+ for (kbuf = tdb_firstkey(tdb_forms);
+ kbuf.dptr;
+ newkey = tdb_nextkey(tdb_forms, kbuf), safe_free(kbuf.dptr), kbuf=newkey)
+ {
+ if (strncmp(kbuf.dptr, FORMS_PREFIX, strlen(FORMS_PREFIX)) != 0)
+ continue;
+
+ dbuf = tdb_fetch(tdb_forms, kbuf);
+ if (!dbuf.dptr)
+ continue;
+
+ fstrcpy(form.name, kbuf.dptr+strlen(FORMS_PREFIX));
+ ret = tdb_unpack(dbuf.dptr, dbuf.dsize, "dddddddd",
+ &i, &form.flag, &form.width, &form.length, &form.left,
+ &form.top, &form.right, &form.bottom);
+ SAFE_FREE(dbuf.dptr);
+ if (ret != dbuf.dsize)
+ continue;
+
+ tl = Realloc(*list, sizeof(nt_forms_struct)*(n+1));
+ if (!tl) {
+ DEBUG(0,("get_ntforms: Realloc fail.\n"));
+ return 0;
+ }
+ *list = tl;
+ (*list)[n] = form;
+ n++;
+ }
+
+
+ return n;
+}
+
+/****************************************************************************
+write a form struct list
+****************************************************************************/
+int write_ntforms(nt_forms_struct **list, int number)
+{
+ pstring buf, key;
+ int len;
+ TDB_DATA kbuf,dbuf;
+ int i;
+
+ for (i=0;i<number;i++) {
+ /* save index, so list is rebuilt in correct order */
+ len = tdb_pack(buf, sizeof(buf), "dddddddd",
+ i, (*list)[i].flag, (*list)[i].width, (*list)[i].length,
+ (*list)[i].left, (*list)[i].top, (*list)[i].right,
+ (*list)[i].bottom);
+ if (len > sizeof(buf)) break;
+ slprintf(key, sizeof(key)-1, "%s%s", FORMS_PREFIX, (*list)[i].name);
+ kbuf.dsize = strlen(key)+1;
+ kbuf.dptr = key;
+ dbuf.dsize = len;
+ dbuf.dptr = buf;
+ if (tdb_store(tdb_forms, kbuf, dbuf, TDB_REPLACE) != 0) break;
+ }
+
+ return i;
+}
+
+/****************************************************************************
+add a form struct at the end of the list
+****************************************************************************/
+BOOL add_a_form(nt_forms_struct **list, const FORM *form, int *count)
+{
+ int n=0;
+ BOOL update;
+ fstring form_name;
+ nt_forms_struct *tl;
+
+ /*
+ * NT tries to add forms even when
+ * they are already in the base
+ * only update the values if already present
+ */
+
+ update=False;
+
+ unistr2_to_ascii(form_name, &form->name, sizeof(form_name)-1);
+ for (n=0; n<*count; n++) {
+ if (!strncmp((*list)[n].name, form_name, strlen(form_name))) {
+ DEBUG(103, ("NT workaround, [%s] already exists\n", form_name));
+ update=True;
+ break;
+ }
+ }
+
+ if (update==False) {
+ if((tl=Realloc(*list, (n+1)*sizeof(nt_forms_struct))) == NULL) {
+ DEBUG(0,("add_a_form: failed to enlarge forms list!\n"));
+ return False;
+ }
+ *list = tl;
+ unistr2_to_ascii((*list)[n].name, &form->name, sizeof((*list)[n].name)-1);
+ (*count)++;
+ }
+
+ (*list)[n].flag=form->flags;
+ (*list)[n].width=form->size_x;
+ (*list)[n].length=form->size_y;
+ (*list)[n].left=form->left;
+ (*list)[n].top=form->top;
+ (*list)[n].right=form->right;
+ (*list)[n].bottom=form->bottom;
+
+ return True;
+}
+
+/****************************************************************************
+ Delete a named form struct.
+****************************************************************************/
+
+BOOL delete_a_form(nt_forms_struct **list, UNISTR2 *del_name, int *count, WERROR *ret)
+{
+ pstring key;
+ TDB_DATA kbuf;
+ int n=0;
+ fstring form_name;
+
+ *ret = WERR_OK;
+
+ unistr2_to_ascii(form_name, del_name, sizeof(form_name)-1);
+
+ for (n=0; n<*count; n++) {
+ if (!strncmp((*list)[n].name, form_name, strlen(form_name))) {
+ DEBUG(103, ("delete_a_form, [%s] in list\n", form_name));
+ break;
+ }
+ }
+
+ if (n == *count) {
+ DEBUG(10,("delete_a_form, [%s] not found\n", form_name));
+ *ret = WERR_INVALID_PARAM;
+ return False;
+ }
+
+ slprintf(key, sizeof(key)-1, "%s%s", FORMS_PREFIX, (*list)[n].name);
+ kbuf.dsize = strlen(key)+1;
+ kbuf.dptr = key;
+ if (tdb_delete(tdb_forms, kbuf) != 0) {
+ *ret = WERR_NOMEM;
+ return False;
+ }
+
+ return True;
+}
+
+/****************************************************************************
+ Update a form struct.
+****************************************************************************/
+
+void update_a_form(nt_forms_struct **list, const FORM *form, int count)
+{
+ int n=0;
+ fstring form_name;
+ unistr2_to_ascii(form_name, &(form->name), sizeof(form_name)-1);
+
+ DEBUG(106, ("[%s]\n", form_name));
+ for (n=0; n<count; n++) {
+ DEBUGADD(106, ("n [%d]:[%s]\n", n, (*list)[n].name));
+ if (!strncmp((*list)[n].name, form_name, strlen(form_name)))
+ break;
+ }
+
+ if (n==count) return;
+
+ (*list)[n].flag=form->flags;
+ (*list)[n].width=form->size_x;
+ (*list)[n].length=form->size_y;
+ (*list)[n].left=form->left;
+ (*list)[n].top=form->top;
+ (*list)[n].right=form->right;
+ (*list)[n].bottom=form->bottom;
+}
+
+/****************************************************************************
+ Get the nt drivers list.
+ Traverse the database and look-up the matching names.
+****************************************************************************/
+int get_ntdrivers(fstring **list, const char *architecture, uint32 version)
+{
+ int total=0;
+ const char *short_archi;
+ fstring *fl;
+ pstring key;
+ TDB_DATA kbuf, newkey;
+
+ short_archi = get_short_archi(architecture);
+ slprintf(key, sizeof(key)-1, "%s%s/%d/", DRIVERS_PREFIX, short_archi, version);
+
+ for (kbuf = tdb_firstkey(tdb_drivers);
+ kbuf.dptr;
+ newkey = tdb_nextkey(tdb_drivers, kbuf), safe_free(kbuf.dptr), kbuf=newkey) {
+
+ if (strncmp(kbuf.dptr, key, strlen(key)) != 0)
+ continue;
+
+ if((fl = Realloc(*list, sizeof(fstring)*(total+1))) == NULL) {
+ DEBUG(0,("get_ntdrivers: failed to enlarge list!\n"));
+ return -1;
+ }
+ else *list = fl;
+
+ fstrcpy((*list)[total], kbuf.dptr+strlen(key));
+ total++;
+ }
+
+ return(total);
+}
+
+/****************************************************************************
+function to do the mapping between the long architecture name and
+the short one.
+****************************************************************************/
+const char *get_short_archi(const char *long_archi)
+{
+ int i=-1;
+
+ DEBUG(107,("Getting architecture dependant directory\n"));
+ do {
+ i++;
+ } while ( (archi_table[i].long_archi!=NULL ) &&
+ StrCaseCmp(long_archi, archi_table[i].long_archi) );
+
+ if (archi_table[i].long_archi==NULL) {
+ DEBUGADD(10,("Unknown architecture [%s] !\n", long_archi));
+ return NULL;
+ }
+
+ /* this might be client code - but shouldn't this be an fstrcpy etc? */
+
+
+ DEBUGADD(108,("index: [%d]\n", i));
+ DEBUGADD(108,("long architecture: [%s]\n", archi_table[i].long_archi));
+ DEBUGADD(108,("short architecture: [%s]\n", archi_table[i].short_archi));
+
+ return archi_table[i].short_archi;
+}
+
+/****************************************************************************
+ Version information in Microsoft files is held in a VS_VERSION_INFO structure.
+ There are two case to be covered here: PE (Portable Executable) and NE (New
+ Executable) files. Both files support the same INFO structure, but PE files
+ store the signature in unicode, and NE files store it as !unicode.
+ returns -1 on error, 1 on version info found, and 0 on no version info found.
+****************************************************************************/
+
+static int get_file_version(files_struct *fsp, char *fname,uint32 *major, uint32 *minor)
+{
+ int i;
+ char *buf = NULL;
+ ssize_t byte_count;
+
+ if ((buf=malloc(PE_HEADER_SIZE)) == NULL) {
+ DEBUG(0,("get_file_version: PE file [%s] PE Header malloc failed bytes = %d\n",
+ fname, PE_HEADER_SIZE));
+ goto error_exit;
+ }
+
+ /* Note: DOS_HEADER_SIZE < malloc'ed PE_HEADER_SIZE */
+ if ((byte_count = vfs_read_data(fsp, buf, DOS_HEADER_SIZE)) < DOS_HEADER_SIZE) {
+ DEBUG(3,("get_file_version: File [%s] DOS header too short, bytes read = %lu\n",
+ fname, (unsigned long)byte_count));
+ goto no_version_info;
+ }
+
+ /* Is this really a DOS header? */
+ if (SVAL(buf,DOS_HEADER_MAGIC_OFFSET) != DOS_HEADER_MAGIC) {
+ DEBUG(6,("get_file_version: File [%s] bad DOS magic = 0x%x\n",
+ fname, SVAL(buf,DOS_HEADER_MAGIC_OFFSET)));
+ goto no_version_info;
+ }
+
+ /* Skip OEM header (if any) and the DOS stub to start of Windows header */
+ if (SMB_VFS_LSEEK(fsp, fsp->fd, SVAL(buf,DOS_HEADER_LFANEW_OFFSET), SEEK_SET) == (SMB_OFF_T)-1) {
+ DEBUG(3,("get_file_version: File [%s] too short, errno = %d\n",
+ fname, errno));
+ /* Assume this isn't an error... the file just looks sort of like a PE/NE file */
+ goto no_version_info;
+ }
+
+ if ((byte_count = vfs_read_data(fsp, buf, PE_HEADER_SIZE)) < PE_HEADER_SIZE) {
+ DEBUG(3,("get_file_version: File [%s] Windows header too short, bytes read = %lu\n",
+ fname, (unsigned long)byte_count));
+ /* Assume this isn't an error... the file just looks sort of like a PE/NE file */
+ goto no_version_info;
+ }
+
+ /* The header may be a PE (Portable Executable) or an NE (New Executable) */
+ if (IVAL(buf,PE_HEADER_SIGNATURE_OFFSET) == PE_HEADER_SIGNATURE) {
+ unsigned int num_sections;
+ unsigned int section_table_bytes;
+
+ if (SVAL(buf,PE_HEADER_MACHINE_OFFSET) != PE_HEADER_MACHINE_I386) {
+ DEBUG(3,("get_file_version: PE file [%s] wrong machine = 0x%x\n",
+ fname, SVAL(buf,PE_HEADER_MACHINE_OFFSET)));
+ /* At this point, we assume the file is in error. It still could be somthing
+ * else besides a PE file, but it unlikely at this point.
+ */
+ goto error_exit;
+ }
+
+ /* get the section table */
+ num_sections = SVAL(buf,PE_HEADER_NUMBER_OF_SECTIONS);
+ section_table_bytes = num_sections * PE_HEADER_SECT_HEADER_SIZE;
+ if (section_table_bytes == 0)
+ goto error_exit;
+
+ SAFE_FREE(buf);
+ if ((buf=malloc(section_table_bytes)) == NULL) {
+ DEBUG(0,("get_file_version: PE file [%s] section table malloc failed bytes = %d\n",
+ fname, section_table_bytes));
+ goto error_exit;
+ }
+
+ if ((byte_count = vfs_read_data(fsp, buf, section_table_bytes)) < section_table_bytes) {
+ DEBUG(3,("get_file_version: PE file [%s] Section header too short, bytes read = %lu\n",
+ fname, (unsigned long)byte_count));
+ goto error_exit;
+ }
+
+ /* Iterate the section table looking for the resource section ".rsrc" */
+ for (i = 0; i < num_sections; i++) {
+ int sec_offset = i * PE_HEADER_SECT_HEADER_SIZE;
+
+ if (strcmp(".rsrc", &buf[sec_offset+PE_HEADER_SECT_NAME_OFFSET]) == 0) {
+ unsigned int section_pos = IVAL(buf,sec_offset+PE_HEADER_SECT_PTR_DATA_OFFSET);
+ unsigned int section_bytes = IVAL(buf,sec_offset+PE_HEADER_SECT_SIZE_DATA_OFFSET);
+
+ if (section_bytes == 0)
+ goto error_exit;
+
+ SAFE_FREE(buf);
+ if ((buf=malloc(section_bytes)) == NULL) {
+ DEBUG(0,("get_file_version: PE file [%s] version malloc failed bytes = %d\n",
+ fname, section_bytes));
+ goto error_exit;
+ }
+
+ /* Seek to the start of the .rsrc section info */
+ if (SMB_VFS_LSEEK(fsp, fsp->fd, section_pos, SEEK_SET) == (SMB_OFF_T)-1) {
+ DEBUG(3,("get_file_version: PE file [%s] too short for section info, errno = %d\n",
+ fname, errno));
+ goto error_exit;
+ }
+
+ if ((byte_count = vfs_read_data(fsp, buf, section_bytes)) < section_bytes) {
+ DEBUG(3,("get_file_version: PE file [%s] .rsrc section too short, bytes read = %lu\n",
+ fname, (unsigned long)byte_count));
+ goto error_exit;
+ }
+
+ if (section_bytes < VS_VERSION_INFO_UNICODE_SIZE)
+ goto error_exit;
+
+ for (i=0; i<section_bytes-VS_VERSION_INFO_UNICODE_SIZE; i++) {
+ /* Scan for 1st 3 unicoded bytes followed by word aligned magic value */
+ if (buf[i] == 'V' && buf[i+1] == '\0' && buf[i+2] == 'S') {
+ /* Align to next long address */
+ int pos = (i + sizeof(VS_SIGNATURE)*2 + 3) & 0xfffffffc;
+
+ if (IVAL(buf,pos) == VS_MAGIC_VALUE) {
+ *major = IVAL(buf,pos+VS_MAJOR_OFFSET);
+ *minor = IVAL(buf,pos+VS_MINOR_OFFSET);
+
+ DEBUG(6,("get_file_version: PE file [%s] Version = %08x:%08x (%d.%d.%d.%d)\n",
+ fname, *major, *minor,
+ (*major>>16)&0xffff, *major&0xffff,
+ (*minor>>16)&0xffff, *minor&0xffff));
+ SAFE_FREE(buf);
+ return 1;
+ }
+ }
+ }
+ }
+ }
+
+ /* Version info not found, fall back to origin date/time */
+ DEBUG(10,("get_file_version: PE file [%s] has no version info\n", fname));
+ SAFE_FREE(buf);
+ return 0;
+
+ } else if (SVAL(buf,NE_HEADER_SIGNATURE_OFFSET) == NE_HEADER_SIGNATURE) {
+ if (CVAL(buf,NE_HEADER_TARGET_OS_OFFSET) != NE_HEADER_TARGOS_WIN ) {
+ DEBUG(3,("get_file_version: NE file [%s] wrong target OS = 0x%x\n",
+ fname, CVAL(buf,NE_HEADER_TARGET_OS_OFFSET)));
+ /* At this point, we assume the file is in error. It still could be somthing
+ * else besides a NE file, but it unlikely at this point. */
+ goto error_exit;
+ }
+
+ /* Allocate a bit more space to speed up things */
+ SAFE_FREE(buf);
+ if ((buf=malloc(VS_NE_BUF_SIZE)) == NULL) {
+ DEBUG(0,("get_file_version: NE file [%s] malloc failed bytes = %d\n",
+ fname, PE_HEADER_SIZE));
+ goto error_exit;
+ }
+
+ /* This is a HACK! I got tired of trying to sort through the messy
+ * 'NE' file format. If anyone wants to clean this up please have at
+ * it, but this works. 'NE' files will eventually fade away. JRR */
+ while((byte_count = vfs_read_data(fsp, buf, VS_NE_BUF_SIZE)) > 0) {
+ /* Cover case that should not occur in a well formed 'NE' .dll file */
+ if (byte_count-VS_VERSION_INFO_SIZE <= 0) break;
+
+ for(i=0; i<byte_count; i++) {
+ /* Fast skip past data that can't possibly match */
+ if (buf[i] != 'V') continue;
+
+ /* Potential match data crosses buf boundry, move it to beginning
+ * of buf, and fill the buf with as much as it will hold. */
+ if (i>byte_count-VS_VERSION_INFO_SIZE) {
+ int bc;
+
+ memcpy(buf, &buf[i], byte_count-i);
+ if ((bc = vfs_read_data(fsp, &buf[byte_count-i], VS_NE_BUF_SIZE-
+ (byte_count-i))) < 0) {
+
+ DEBUG(0,("get_file_version: NE file [%s] Read error, errno=%d\n",
+ fname, errno));
+ goto error_exit;
+ }
+
+ byte_count = bc + (byte_count - i);
+ if (byte_count<VS_VERSION_INFO_SIZE) break;
+
+ i = 0;
+ }
+
+ /* Check that the full signature string and the magic number that
+ * follows exist (not a perfect solution, but the chances that this
+ * occurs in code is, well, remote. Yes I know I'm comparing the 'V'
+ * twice, as it is simpler to read the code. */
+ if (strcmp(&buf[i], VS_SIGNATURE) == 0) {
+ /* Compute skip alignment to next long address */
+ int skip = -(SMB_VFS_LSEEK(fsp, fsp->fd, 0, SEEK_CUR) - (byte_count - i) +
+ sizeof(VS_SIGNATURE)) & 3;
+ if (IVAL(buf,i+sizeof(VS_SIGNATURE)+skip) != 0xfeef04bd) continue;
+
+ *major = IVAL(buf,i+sizeof(VS_SIGNATURE)+skip+VS_MAJOR_OFFSET);
+ *minor = IVAL(buf,i+sizeof(VS_SIGNATURE)+skip+VS_MINOR_OFFSET);
+ DEBUG(6,("get_file_version: NE file [%s] Version = %08x:%08x (%d.%d.%d.%d)\n",
+ fname, *major, *minor,
+ (*major>>16)&0xffff, *major&0xffff,
+ (*minor>>16)&0xffff, *minor&0xffff));
+ SAFE_FREE(buf);
+ return 1;
+ }
+ }
+ }
+
+ /* Version info not found, fall back to origin date/time */
+ DEBUG(0,("get_file_version: NE file [%s] Version info not found\n", fname));
+ SAFE_FREE(buf);
+ return 0;
+
+ } else
+ /* Assume this isn't an error... the file just looks sort of like a PE/NE file */
+ DEBUG(3,("get_file_version: File [%s] unknown file format, signature = 0x%x\n",
+ fname, IVAL(buf,PE_HEADER_SIGNATURE_OFFSET)));
+
+ no_version_info:
+ SAFE_FREE(buf);
+ return 0;
+
+ error_exit:
+ SAFE_FREE(buf);
+ return -1;
+}
+
+/****************************************************************************
+Drivers for Microsoft systems contain multiple files. Often, multiple drivers
+share one or more files. During the MS installation process files are checked
+to insure that only a newer version of a shared file is installed over an
+older version. There are several possibilities for this comparison. If there
+is no previous version, the new one is newer (obviously). If either file is
+missing the version info structure, compare the creation date (on Unix use
+the modification date). Otherwise chose the numerically larger version number.
+****************************************************************************/
+
+static int file_version_is_newer(connection_struct *conn, fstring new_file, fstring old_file)
+{
+ BOOL use_version = True;
+ pstring filepath;
+
+ uint32 new_major;
+ uint32 new_minor;
+ time_t new_create_time;
+
+ uint32 old_major;
+ uint32 old_minor;
+ time_t old_create_time;
+
+ int access_mode;
+ int action;
+ files_struct *fsp = NULL;
+ SMB_STRUCT_STAT st;
+ SMB_STRUCT_STAT stat_buf;
+ BOOL bad_path;
+
+ ZERO_STRUCT(st);
+ ZERO_STRUCT(stat_buf);
+ new_create_time = (time_t)0;
+ old_create_time = (time_t)0;
+
+ /* Get file version info (if available) for previous file (if it exists) */
+ pstrcpy(filepath, old_file);
+
+ unix_convert(filepath,conn,NULL,&bad_path,&stat_buf);
+
+ fsp = open_file_shared(conn, filepath, &stat_buf,
+ SET_OPEN_MODE(DOS_OPEN_RDONLY),
+ (FILE_FAIL_IF_NOT_EXIST|FILE_EXISTS_OPEN),
+ FILE_ATTRIBUTE_NORMAL, 0, &access_mode, &action);
+ if (!fsp) {
+ /* Old file not found, so by definition new file is in fact newer */
+ DEBUG(10,("file_version_is_newer: Can't open old file [%s], errno = %d\n",
+ filepath, errno));
+ return True;
+
+ } else {
+ int ret = get_file_version(fsp, old_file, &old_major, &old_minor);
+ if (ret == -1) goto error_exit;
+
+ if (!ret) {
+ DEBUG(6,("file_version_is_newer: Version info not found [%s], use mod time\n",
+ old_file));
+ use_version = False;
+ if (SMB_VFS_FSTAT(fsp, fsp->fd, &st) == -1) goto error_exit;
+ old_create_time = st.st_mtime;
+ DEBUGADD(6,("file_version_is_newer: mod time = %ld sec\n", old_create_time));
+ }
+ }
+ close_file(fsp, True);
+
+ /* Get file version info (if available) for new file */
+ pstrcpy(filepath, new_file);
+ unix_convert(filepath,conn,NULL,&bad_path,&stat_buf);
+
+ fsp = open_file_shared(conn, filepath, &stat_buf,
+ SET_OPEN_MODE(DOS_OPEN_RDONLY),
+ (FILE_FAIL_IF_NOT_EXIST|FILE_EXISTS_OPEN),
+ FILE_ATTRIBUTE_NORMAL, 0, &access_mode, &action);
+ if (!fsp) {
+ /* New file not found, this shouldn't occur if the caller did its job */
+ DEBUG(3,("file_version_is_newer: Can't open new file [%s], errno = %d\n",
+ filepath, errno));
+ goto error_exit;
+
+ } else {
+ int ret = get_file_version(fsp, new_file, &new_major, &new_minor);
+ if (ret == -1) goto error_exit;
+
+ if (!ret) {
+ DEBUG(6,("file_version_is_newer: Version info not found [%s], use mod time\n",
+ new_file));
+ use_version = False;
+ if (SMB_VFS_FSTAT(fsp, fsp->fd, &st) == -1) goto error_exit;
+ new_create_time = st.st_mtime;
+ DEBUGADD(6,("file_version_is_newer: mod time = %ld sec\n", new_create_time));
+ }
+ }
+ close_file(fsp, True);
+
+ if (use_version && (new_major != old_major || new_minor != old_minor)) {
+ /* Compare versions and choose the larger version number */
+ if (new_major > old_major ||
+ (new_major == old_major && new_minor > old_minor)) {
+
+ DEBUG(6,("file_version_is_newer: Replacing [%s] with [%s]\n", old_file, new_file));
+ return True;
+ }
+ else {
+ DEBUG(6,("file_version_is_newer: Leaving [%s] unchanged\n", old_file));
+ return False;
+ }
+
+ } else {
+ /* Compare modification time/dates and choose the newest time/date */
+ if (new_create_time > old_create_time) {
+ DEBUG(6,("file_version_is_newer: Replacing [%s] with [%s]\n", old_file, new_file));
+ return True;
+ }
+ else {
+ DEBUG(6,("file_version_is_newer: Leaving [%s] unchanged\n", old_file));
+ return False;
+ }
+ }
+
+ error_exit:
+ if(fsp)
+ close_file(fsp, True);
+ return -1;
+}
+
+/****************************************************************************
+Determine the correct cVersion associated with an architecture and driver
+****************************************************************************/
+static uint32 get_correct_cversion(const char *architecture, fstring driverpath_in,
+ struct current_user *user, WERROR *perr)
+{
+ int cversion;
+ int access_mode;
+ int action;
+ NTSTATUS nt_status;
+ pstring driverpath;
+ DATA_BLOB null_pw;
+ fstring res_type;
+ files_struct *fsp = NULL;
+ BOOL bad_path;
+ SMB_STRUCT_STAT st;
+ connection_struct *conn;
+
+ ZERO_STRUCT(st);
+
+ *perr = WERR_INVALID_PARAM;
+
+ /* If architecture is Windows 95/98/ME, the version is always 0. */
+ if (strcmp(architecture, "WIN40") == 0) {
+ DEBUG(10,("get_correct_cversion: Driver is Win9x, cversion = 0\n"));
+ *perr = WERR_OK;
+ return 0;
+ }
+
+ /*
+ * Connect to the print$ share under the same account as the user connected
+ * to the rpc pipe. Note we must still be root to do this.
+ */
+
+ /* Null password is ok - we are already an authenticated user... */
+ null_pw = data_blob(NULL, 0);
+ fstrcpy(res_type, "A:");
+ become_root();
+ conn = make_connection_with_chdir("print$", null_pw, res_type, user->vuid, &nt_status);
+ unbecome_root();
+
+ if (conn == NULL) {
+ DEBUG(0,("get_correct_cversion: Unable to connect\n"));
+ *perr = ntstatus_to_werror(nt_status);
+ return -1;
+ }
+
+ /* We are temporarily becoming the connection user. */
+ if (!become_user(conn, user->vuid)) {
+ DEBUG(0,("get_correct_cversion: Can't become user!\n"));
+ *perr = WERR_ACCESS_DENIED;
+ return -1;
+ }
+
+ /* Open the driver file (Portable Executable format) and determine the
+ * deriver the cversion. */
+ slprintf(driverpath, sizeof(driverpath)-1, "%s/%s", architecture, driverpath_in);
+
+ unix_convert(driverpath,conn,NULL,&bad_path,&st);
+
+ fsp = open_file_shared(conn, driverpath, &st,
+ SET_OPEN_MODE(DOS_OPEN_RDONLY),
+ (FILE_FAIL_IF_NOT_EXIST|FILE_EXISTS_OPEN),
+ FILE_ATTRIBUTE_NORMAL, 0, &access_mode, &action);
+ if (!fsp) {
+ DEBUG(3,("get_correct_cversion: Can't open file [%s], errno = %d\n",
+ driverpath, errno));
+ *perr = WERR_ACCESS_DENIED;
+ goto error_exit;
+ }
+ else {
+ uint32 major;
+ uint32 minor;
+ int ret = get_file_version(fsp, driverpath, &major, &minor);
+ if (ret == -1) goto error_exit;
+
+ if (!ret) {
+ DEBUG(6,("get_correct_cversion: Version info not found [%s]\n", driverpath));
+ goto error_exit;
+ }
+
+ /*
+ * This is a Microsoft'ism. See references in MSDN to VER_FILEVERSION
+ * for more details. Version in this case is not just the version of the
+ * file, but the version in the sense of kernal mode (2) vs. user mode
+ * (3) drivers. Other bits of the version fields are the version info.
+ * JRR 010716
+ */
+ cversion = major & 0x0000ffff;
+ switch (cversion) {
+ case 2: /* WinNT drivers */
+ case 3: /* Win2K drivers */
+ break;
+
+ default:
+ DEBUG(6,("get_correct_cversion: cversion invalid [%s] cversion = %d\n",
+ driverpath, cversion));
+ goto error_exit;
+ }
+
+ DEBUG(10,("get_correct_cversion: Version info found [%s] major = 0x%x minor = 0x%x\n",
+ driverpath, major, minor));
+ }
+
+ DEBUG(10,("get_correct_cversion: Driver file [%s] cversion = %d\n",
+ driverpath, cversion));
+
+ close_file(fsp, True);
+ close_cnum(conn, user->vuid);
+ unbecome_user();
+ *perr = WERR_OK;
+ return cversion;
+
+
+ error_exit:
+
+ if(fsp)
+ close_file(fsp, True);
+
+ close_cnum(conn, user->vuid);
+ unbecome_user();
+ return -1;
+}
+
+/****************************************************************************
+****************************************************************************/
+static WERROR clean_up_driver_struct_level_3(NT_PRINTER_DRIVER_INFO_LEVEL_3 *driver,
+ struct current_user *user)
+{
+ const char *architecture;
+ fstring new_name;
+ char *p;
+ int i;
+ WERROR err;
+
+ /* clean up the driver name.
+ * we can get .\driver.dll
+ * or worse c:\windows\system\driver.dll !
+ */
+ /* using an intermediate string to not have overlaping memcpy()'s */
+ if ((p = strrchr(driver->driverpath,'\\')) != NULL) {
+ fstrcpy(new_name, p+1);
+ fstrcpy(driver->driverpath, new_name);
+ }
+
+ if ((p = strrchr(driver->datafile,'\\')) != NULL) {
+ fstrcpy(new_name, p+1);
+ fstrcpy(driver->datafile, new_name);
+ }
+
+ if ((p = strrchr(driver->configfile,'\\')) != NULL) {
+ fstrcpy(new_name, p+1);
+ fstrcpy(driver->configfile, new_name);
+ }
+
+ if ((p = strrchr(driver->helpfile,'\\')) != NULL) {
+ fstrcpy(new_name, p+1);
+ fstrcpy(driver->helpfile, new_name);
+ }
+
+ if (driver->dependentfiles) {
+ for (i=0; *driver->dependentfiles[i]; i++) {
+ if ((p = strrchr(driver->dependentfiles[i],'\\')) != NULL) {
+ fstrcpy(new_name, p+1);
+ fstrcpy(driver->dependentfiles[i], new_name);
+ }
+ }
+ }
+
+ architecture = get_short_archi(driver->environment);
+
+ /* jfm:7/16/2000 the client always sends the cversion=0.
+ * The server should check which version the driver is by reading
+ * the PE header of driver->driverpath.
+ *
+ * For Windows 95/98 the version is 0 (so the value sent is correct)
+ * For Windows NT (the architecture doesn't matter)
+ * NT 3.1: cversion=0
+ * NT 3.5/3.51: cversion=1
+ * NT 4: cversion=2
+ * NT2K: cversion=3
+ */
+ if ((driver->cversion = get_correct_cversion( architecture,
+ driver->driverpath, user, &err)) == -1)
+ return err;
+
+ return WERR_OK;
+}
+
+/****************************************************************************
+****************************************************************************/
+static WERROR clean_up_driver_struct_level_6(NT_PRINTER_DRIVER_INFO_LEVEL_6 *driver, struct current_user *user)
+{
+ const char *architecture;
+ fstring new_name;
+ char *p;
+ int i;
+ WERROR err;
+
+ /* clean up the driver name.
+ * we can get .\driver.dll
+ * or worse c:\windows\system\driver.dll !
+ */
+ /* using an intermediate string to not have overlaping memcpy()'s */
+ if ((p = strrchr(driver->driverpath,'\\')) != NULL) {
+ fstrcpy(new_name, p+1);
+ fstrcpy(driver->driverpath, new_name);
+ }
+
+ if ((p = strrchr(driver->datafile,'\\')) != NULL) {
+ fstrcpy(new_name, p+1);
+ fstrcpy(driver->datafile, new_name);
+ }
+
+ if ((p = strrchr(driver->configfile,'\\')) != NULL) {
+ fstrcpy(new_name, p+1);
+ fstrcpy(driver->configfile, new_name);
+ }
+
+ if ((p = strrchr(driver->helpfile,'\\')) != NULL) {
+ fstrcpy(new_name, p+1);
+ fstrcpy(driver->helpfile, new_name);
+ }
+
+ if (driver->dependentfiles) {
+ for (i=0; *driver->dependentfiles[i]; i++) {
+ if ((p = strrchr(driver->dependentfiles[i],'\\')) != NULL) {
+ fstrcpy(new_name, p+1);
+ fstrcpy(driver->dependentfiles[i], new_name);
+ }
+ }
+ }
+
+ architecture = get_short_archi(driver->environment);
+
+ /* jfm:7/16/2000 the client always sends the cversion=0.
+ * The server should check which version the driver is by reading
+ * the PE header of driver->driverpath.
+ *
+ * For Windows 95/98 the version is 0 (so the value sent is correct)
+ * For Windows NT (the architecture doesn't matter)
+ * NT 3.1: cversion=0
+ * NT 3.5/3.51: cversion=1
+ * NT 4: cversion=2
+ * NT2K: cversion=3
+ */
+ if ((driver->version = get_correct_cversion(architecture, driver->driverpath, user, &err)) == -1)
+ return err;
+
+ return WERR_OK;
+}
+
+/****************************************************************************
+****************************************************************************/
+WERROR clean_up_driver_struct(NT_PRINTER_DRIVER_INFO_LEVEL driver_abstract,
+ uint32 level, struct current_user *user)
+{
+ switch (level) {
+ case 3:
+ {
+ NT_PRINTER_DRIVER_INFO_LEVEL_3 *driver;
+ driver=driver_abstract.info_3;
+ return clean_up_driver_struct_level_3(driver, user);
+ }
+ case 6:
+ {
+ NT_PRINTER_DRIVER_INFO_LEVEL_6 *driver;
+ driver=driver_abstract.info_6;
+ return clean_up_driver_struct_level_6(driver, user);
+ }
+ default:
+ return WERR_INVALID_PARAM;
+ }
+}
+
+/****************************************************************************
+ This function sucks and should be replaced. JRA.
+****************************************************************************/
+
+static void convert_level_6_to_level3(NT_PRINTER_DRIVER_INFO_LEVEL_3 *dst, NT_PRINTER_DRIVER_INFO_LEVEL_6 *src)
+{
+ dst->cversion = src->version;
+
+ fstrcpy( dst->name, src->name);
+ fstrcpy( dst->environment, src->environment);
+ fstrcpy( dst->driverpath, src->driverpath);
+ fstrcpy( dst->datafile, src->datafile);
+ fstrcpy( dst->configfile, src->configfile);
+ fstrcpy( dst->helpfile, src->helpfile);
+ fstrcpy( dst->monitorname, src->monitorname);
+ fstrcpy( dst->defaultdatatype, src->defaultdatatype);
+ dst->dependentfiles = src->dependentfiles;
+}
+
+#if 0 /* Debugging function */
+
+static char* ffmt(unsigned char *c){
+ int i;
+ static char ffmt_str[17];
+
+ for (i=0; i<16; i++) {
+ if ((c[i] < ' ') || (c[i] > '~'))
+ ffmt_str[i]='.';
+ else
+ ffmt_str[i]=c[i];
+ }
+ ffmt_str[16]='\0';
+ return ffmt_str;
+}
+
+#endif
+
+/****************************************************************************
+****************************************************************************/
+BOOL move_driver_to_download_area(NT_PRINTER_DRIVER_INFO_LEVEL driver_abstract, uint32 level,
+ struct current_user *user, WERROR *perr)
+{
+ NT_PRINTER_DRIVER_INFO_LEVEL_3 *driver;
+ NT_PRINTER_DRIVER_INFO_LEVEL_3 converted_driver;
+ const char *architecture;
+ pstring new_dir;
+ pstring old_name;
+ pstring new_name;
+ DATA_BLOB null_pw;
+ connection_struct *conn;
+ NTSTATUS nt_status;
+ pstring inbuf;
+ pstring outbuf;
+ fstring res_type;
+ int ver = 0;
+ int i;
+
+ memset(inbuf, '\0', sizeof(inbuf));
+ memset(outbuf, '\0', sizeof(outbuf));
+ *perr = WERR_OK;
+
+ if (level==3)
+ driver=driver_abstract.info_3;
+ else if (level==6) {
+ convert_level_6_to_level3(&converted_driver, driver_abstract.info_6);
+ driver = &converted_driver;
+ } else {
+ DEBUG(0,("move_driver_to_download_area: Unknown info level (%u)\n", (unsigned int)level ));
+ return False;
+ }
+
+ architecture = get_short_archi(driver->environment);
+
+ /*
+ * Connect to the print$ share under the same account as the user connected to the rpc pipe.
+ * Note we must be root to do this.
+ */
+
+ null_pw = data_blob(NULL, 0);
+ fstrcpy(res_type, "A:");
+ become_root();
+ conn = make_connection_with_chdir("print$", null_pw, res_type, user->vuid, &nt_status);
+ unbecome_root();
+
+ if (conn == NULL) {
+ DEBUG(0,("move_driver_to_download_area: Unable to connect\n"));
+ *perr = ntstatus_to_werror(nt_status);
+ return False;
+ }
+
+ /*
+ * Save who we are - we are temporarily becoming the connection user.
+ */
+
+ if (!become_user(conn, conn->vuid)) {
+ DEBUG(0,("move_driver_to_download_area: Can't become user!\n"));
+ return False;
+ }
+
+ /*
+ * make the directories version and version\driver_name
+ * under the architecture directory.
+ */
+ DEBUG(5,("Creating first directory\n"));
+ slprintf(new_dir, sizeof(new_dir)-1, "%s/%d", architecture, driver->cversion);
+ mkdir_internal(conn, new_dir);
+
+ /* For each driver file, archi\filexxx.yyy, if there is a duplicate file
+ * listed for this driver which has already been moved, skip it (note:
+ * drivers may list the same file name several times. Then check if the
+ * file already exists in archi\cversion\, if so, check that the version
+ * info (or time stamps if version info is unavailable) is newer (or the
+ * date is later). If it is, move it to archi\cversion\filexxx.yyy.
+ * Otherwise, delete the file.
+ *
+ * If a file is not moved to archi\cversion\ because of an error, all the
+ * rest of the 'unmoved' driver files are removed from archi\. If one or
+ * more of the driver's files was already moved to archi\cversion\, it
+ * potentially leaves the driver in a partially updated state. Version
+ * trauma will most likely occur if an client attempts to use any printer
+ * bound to the driver. Perhaps a rewrite to make sure the moves can be
+ * done is appropriate... later JRR
+ */
+
+ DEBUG(5,("Moving files now !\n"));
+
+ if (driver->driverpath && strlen(driver->driverpath)) {
+ slprintf(new_name, sizeof(new_name)-1, "%s/%s", architecture, driver->driverpath);
+ slprintf(old_name, sizeof(old_name)-1, "%s/%s", new_dir, driver->driverpath);
+ if (ver != -1 && (ver=file_version_is_newer(conn, new_name, old_name)) > 0) {
+ NTSTATUS status;
+ status = rename_internals(conn, new_name, old_name, 0, True);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(0,("move_driver_to_download_area: Unable to rename [%s] to [%s]\n",
+ new_name, old_name));
+ *perr = ntstatus_to_werror(status);
+ unlink_internals(conn, 0, new_name);
+ ver = -1;
+ }
+ }
+ else
+ unlink_internals(conn, 0, new_name);
+ }
+
+ if (driver->datafile && strlen(driver->datafile)) {
+ if (!strequal(driver->datafile, driver->driverpath)) {
+ slprintf(new_name, sizeof(new_name)-1, "%s/%s", architecture, driver->datafile);
+ slprintf(old_name, sizeof(old_name)-1, "%s/%s", new_dir, driver->datafile);
+ if (ver != -1 && (ver=file_version_is_newer(conn, new_name, old_name)) > 0) {
+ NTSTATUS status;
+ status = rename_internals(conn, new_name, old_name, 0, True);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(0,("move_driver_to_download_area: Unable to rename [%s] to [%s]\n",
+ new_name, old_name));
+ *perr = ntstatus_to_werror(status);
+ unlink_internals(conn, 0, new_name);
+ ver = -1;
+ }
+ }
+ else
+ unlink_internals(conn, 0, new_name);
+ }
+ }
+
+ if (driver->configfile && strlen(driver->configfile)) {
+ if (!strequal(driver->configfile, driver->driverpath) &&
+ !strequal(driver->configfile, driver->datafile)) {
+ slprintf(new_name, sizeof(new_name)-1, "%s/%s", architecture, driver->configfile);
+ slprintf(old_name, sizeof(old_name)-1, "%s/%s", new_dir, driver->configfile);
+ if (ver != -1 && (ver=file_version_is_newer(conn, new_name, old_name)) > 0) {
+ NTSTATUS status;
+ status = rename_internals(conn, new_name, old_name, 0, True);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(0,("move_driver_to_download_area: Unable to rename [%s] to [%s]\n",
+ new_name, old_name));
+ *perr = ntstatus_to_werror(status);
+ unlink_internals(conn, 0, new_name);
+ ver = -1;
+ }
+ }
+ else
+ unlink_internals(conn, 0, new_name);
+ }
+ }
+
+ if (driver->helpfile && strlen(driver->helpfile)) {
+ if (!strequal(driver->helpfile, driver->driverpath) &&
+ !strequal(driver->helpfile, driver->datafile) &&
+ !strequal(driver->helpfile, driver->configfile)) {
+ slprintf(new_name, sizeof(new_name)-1, "%s/%s", architecture, driver->helpfile);
+ slprintf(old_name, sizeof(old_name)-1, "%s/%s", new_dir, driver->helpfile);
+ if (ver != -1 && (ver=file_version_is_newer(conn, new_name, old_name)) > 0) {
+ NTSTATUS status;
+ status = rename_internals(conn, new_name, old_name, 0, True);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(0,("move_driver_to_download_area: Unable to rename [%s] to [%s]\n",
+ new_name, old_name));
+ *perr = ntstatus_to_werror(status);
+ unlink_internals(conn, 0, new_name);
+ ver = -1;
+ }
+ }
+ else
+ unlink_internals(conn, 0, new_name);
+ }
+ }
+
+ if (driver->dependentfiles) {
+ for (i=0; *driver->dependentfiles[i]; i++) {
+ if (!strequal(driver->dependentfiles[i], driver->driverpath) &&
+ !strequal(driver->dependentfiles[i], driver->datafile) &&
+ !strequal(driver->dependentfiles[i], driver->configfile) &&
+ !strequal(driver->dependentfiles[i], driver->helpfile)) {
+ int j;
+ for (j=0; j < i; j++) {
+ if (strequal(driver->dependentfiles[i], driver->dependentfiles[j])) {
+ goto NextDriver;
+ }
+ }
+
+ slprintf(new_name, sizeof(new_name)-1, "%s/%s", architecture, driver->dependentfiles[i]);
+ slprintf(old_name, sizeof(old_name)-1, "%s/%s", new_dir, driver->dependentfiles[i]);
+ if (ver != -1 && (ver=file_version_is_newer(conn, new_name, old_name)) > 0) {
+ NTSTATUS status;
+ status = rename_internals(conn, new_name, old_name, 0, True);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(0,("move_driver_to_download_area: Unable to rename [%s] to [%s]\n",
+ new_name, old_name));
+ *perr = ntstatus_to_werror(status);
+ unlink_internals(conn, 0, new_name);
+ ver = -1;
+ }
+ }
+ else
+ unlink_internals(conn, 0, new_name);
+ }
+ NextDriver: ;
+ }
+ }
+
+ close_cnum(conn, user->vuid);
+ unbecome_user();
+
+ return ver == -1 ? False : True;
+}
+
+/****************************************************************************
+****************************************************************************/
+static uint32 add_a_printer_driver_3(NT_PRINTER_DRIVER_INFO_LEVEL_3 *driver)
+{
+ int len, buflen;
+ const char *architecture;
+ pstring directory;
+ fstring temp_name;
+ pstring key;
+ char *buf;
+ int i, ret;
+ TDB_DATA kbuf, dbuf;
+
+ architecture = get_short_archi(driver->environment);
+
+ /* The names are relative. We store them in the form: \print$\arch\version\driver.xxx
+ * \\server is added in the rpc server layer.
+ * It does make sense to NOT store the server's name in the printer TDB.
+ */
+
+ slprintf(directory, sizeof(directory)-1, "\\print$\\%s\\%d\\", architecture, driver->cversion);
+
+ /* .inf files do not always list a file for each of the four standard files.
+ * Don't prepend a path to a null filename, or client claims:
+ * "The server on which the printer resides does not have a suitable
+ * <printer driver name> printer driver installed. Click OK if you
+ * wish to install the driver on your local machine."
+ */
+ if (strlen(driver->driverpath)) {
+ fstrcpy(temp_name, driver->driverpath);
+ slprintf(driver->driverpath, sizeof(driver->driverpath)-1, "%s%s", directory, temp_name);
+ }
+
+ if (strlen(driver->datafile)) {
+ fstrcpy(temp_name, driver->datafile);
+ slprintf(driver->datafile, sizeof(driver->datafile)-1, "%s%s", directory, temp_name);
+ }
+
+ if (strlen(driver->configfile)) {
+ fstrcpy(temp_name, driver->configfile);
+ slprintf(driver->configfile, sizeof(driver->configfile)-1, "%s%s", directory, temp_name);
+ }
+
+ if (strlen(driver->helpfile)) {
+ fstrcpy(temp_name, driver->helpfile);
+ slprintf(driver->helpfile, sizeof(driver->helpfile)-1, "%s%s", directory, temp_name);
+ }
+
+ if (driver->dependentfiles) {
+ for (i=0; *driver->dependentfiles[i]; i++) {
+ fstrcpy(temp_name, driver->dependentfiles[i]);
+ slprintf(driver->dependentfiles[i], sizeof(driver->dependentfiles[i])-1, "%s%s", directory, temp_name);
+ }
+ }
+
+ slprintf(key, sizeof(key)-1, "%s%s/%d/%s", DRIVERS_PREFIX, architecture, driver->cversion, driver->name);
+
+ DEBUG(5,("add_a_printer_driver_3: Adding driver with key %s\n", key ));
+
+ buf = NULL;
+ len = buflen = 0;
+
+ again:
+ len = 0;
+ len += tdb_pack(buf+len, buflen-len, "dffffffff",
+ driver->cversion,
+ driver->name,
+ driver->environment,
+ driver->driverpath,
+ driver->datafile,
+ driver->configfile,
+ driver->helpfile,
+ driver->monitorname,
+ driver->defaultdatatype);
+
+ if (driver->dependentfiles) {
+ for (i=0; *driver->dependentfiles[i]; i++) {
+ len += tdb_pack(buf+len, buflen-len, "f",
+ driver->dependentfiles[i]);
+ }
+ }
+
+ if (len != buflen) {
+ char *tb;
+
+ tb = (char *)Realloc(buf, len);
+ if (!tb) {
+ DEBUG(0,("add_a_printer_driver_3: failed to enlarge buffer\n!"));
+ ret = -1;
+ goto done;
+ }
+ else buf = tb;
+ buflen = len;
+ goto again;
+ }
+
+
+ kbuf.dptr = key;
+ kbuf.dsize = strlen(key)+1;
+ dbuf.dptr = buf;
+ dbuf.dsize = len;
+
+ ret = tdb_store(tdb_drivers, kbuf, dbuf, TDB_REPLACE);
+
+done:
+ if (ret)
+ DEBUG(0,("add_a_printer_driver_3: Adding driver with key %s failed.\n", key ));
+
+ SAFE_FREE(buf);
+ return ret;
+}
+
+/****************************************************************************
+****************************************************************************/
+static uint32 add_a_printer_driver_6(NT_PRINTER_DRIVER_INFO_LEVEL_6 *driver)
+{
+ NT_PRINTER_DRIVER_INFO_LEVEL_3 info3;
+
+ ZERO_STRUCT(info3);
+ info3.cversion = driver->version;
+ fstrcpy(info3.name,driver->name);
+ fstrcpy(info3.environment,driver->environment);
+ fstrcpy(info3.driverpath,driver->driverpath);
+ fstrcpy(info3.datafile,driver->datafile);
+ fstrcpy(info3.configfile,driver->configfile);
+ fstrcpy(info3.helpfile,driver->helpfile);
+ fstrcpy(info3.monitorname,driver->monitorname);
+ fstrcpy(info3.defaultdatatype,driver->defaultdatatype);
+ info3.dependentfiles = driver->dependentfiles;
+
+ return add_a_printer_driver_3(&info3);
+}
+
+
+/****************************************************************************
+****************************************************************************/
+static WERROR get_a_printer_driver_3_default(NT_PRINTER_DRIVER_INFO_LEVEL_3 **info_ptr, const char *driver, const char *arch)
+{
+ NT_PRINTER_DRIVER_INFO_LEVEL_3 info;
+
+ ZERO_STRUCT(info);
+
+ fstrcpy(info.name, driver);
+ fstrcpy(info.defaultdatatype, "RAW");
+
+ fstrcpy(info.driverpath, "");
+ fstrcpy(info.datafile, "");
+ fstrcpy(info.configfile, "");
+ fstrcpy(info.helpfile, "");
+
+ if ((info.dependentfiles=(fstring *)malloc(2*sizeof(fstring))) == NULL)
+ return WERR_NOMEM;
+
+ memset(info.dependentfiles, '\0', 2*sizeof(fstring));
+ fstrcpy(info.dependentfiles[0], "");
+
+ *info_ptr = memdup(&info, sizeof(info));
+
+ return WERR_OK;
+}
+
+/****************************************************************************
+****************************************************************************/
+static WERROR get_a_printer_driver_3(NT_PRINTER_DRIVER_INFO_LEVEL_3 **info_ptr, fstring drivername, const char *arch, uint32 version)
+{
+ NT_PRINTER_DRIVER_INFO_LEVEL_3 driver;
+ TDB_DATA kbuf, dbuf;
+ const char *architecture;
+ int len = 0;
+ int i;
+ pstring key;
+
+ ZERO_STRUCT(driver);
+
+ architecture = get_short_archi(arch);
+
+ /* Windows 4.0 (i.e. win9x) should always use a version of 0 */
+
+ if ( strcmp( architecture, SPL_ARCH_WIN40 ) == 0 )
+ version = 0;
+
+ DEBUG(8,("get_a_printer_driver_3: [%s%s/%d/%s]\n", DRIVERS_PREFIX, architecture, version, drivername));
+
+ slprintf(key, sizeof(key)-1, "%s%s/%d/%s", DRIVERS_PREFIX, architecture, version, drivername);
+
+ kbuf.dptr = key;
+ kbuf.dsize = strlen(key)+1;
+
+ dbuf = tdb_fetch(tdb_drivers, kbuf);
+ if (!dbuf.dptr)
+ return WERR_UNKNOWN_PRINTER_DRIVER;
+
+ len += tdb_unpack(dbuf.dptr, dbuf.dsize, "dffffffff",
+ &driver.cversion,
+ driver.name,
+ driver.environment,
+ driver.driverpath,
+ driver.datafile,
+ driver.configfile,
+ driver.helpfile,
+ driver.monitorname,
+ driver.defaultdatatype);
+
+ i=0;
+ while (len < dbuf.dsize) {
+ fstring *tddfs;
+
+ tddfs = (fstring *)Realloc(driver.dependentfiles,
+ sizeof(fstring)*(i+2));
+ if (tddfs == NULL) {
+ DEBUG(0,("get_a_printer_driver_3: failed to enlarge buffer!\n"));
+ break;
+ }
+ else driver.dependentfiles = tddfs;
+
+ len += tdb_unpack(dbuf.dptr+len, dbuf.dsize-len, "f",
+ &driver.dependentfiles[i]);
+ i++;
+ }
+
+ if (driver.dependentfiles != NULL)
+ fstrcpy(driver.dependentfiles[i], "");
+
+ SAFE_FREE(dbuf.dptr);
+
+ if (len != dbuf.dsize) {
+ SAFE_FREE(driver.dependentfiles);
+
+ return get_a_printer_driver_3_default(info_ptr, drivername, arch);
+ }
+
+ *info_ptr = (NT_PRINTER_DRIVER_INFO_LEVEL_3 *)memdup(&driver, sizeof(driver));
+
+ return WERR_OK;
+}
+
+/****************************************************************************
+ Debugging function, dump at level 6 the struct in the logs.
+****************************************************************************/
+
+static uint32 dump_a_printer_driver(NT_PRINTER_DRIVER_INFO_LEVEL driver, uint32 level)
+{
+ uint32 result;
+ NT_PRINTER_DRIVER_INFO_LEVEL_3 *info3;
+ int i;
+
+ DEBUG(20,("Dumping printer driver at level [%d]\n", level));
+
+ switch (level)
+ {
+ case 3:
+ {
+ if (driver.info_3 == NULL)
+ result=5;
+ else {
+ info3=driver.info_3;
+
+ DEBUGADD(20,("version:[%d]\n", info3->cversion));
+ DEBUGADD(20,("name:[%s]\n", info3->name));
+ DEBUGADD(20,("environment:[%s]\n", info3->environment));
+ DEBUGADD(20,("driverpath:[%s]\n", info3->driverpath));
+ DEBUGADD(20,("datafile:[%s]\n", info3->datafile));
+ DEBUGADD(20,("configfile:[%s]\n", info3->configfile));
+ DEBUGADD(20,("helpfile:[%s]\n", info3->helpfile));
+ DEBUGADD(20,("monitorname:[%s]\n", info3->monitorname));
+ DEBUGADD(20,("defaultdatatype:[%s]\n", info3->defaultdatatype));
+
+ for (i=0; info3->dependentfiles &&
+ *info3->dependentfiles[i]; i++) {
+ DEBUGADD(20,("dependentfile:[%s]\n",
+ info3->dependentfiles[i]));
+ }
+ result=0;
+ }
+ break;
+ }
+ default:
+ DEBUGADD(20,("dump_a_printer_driver: Level %u not implemented\n", (unsigned int)level));
+ result=1;
+ break;
+ }
+
+ return result;
+}
+
+/****************************************************************************
+****************************************************************************/
+int pack_devicemode(NT_DEVICEMODE *nt_devmode, char *buf, int buflen)
+{
+ int len = 0;
+
+ len += tdb_pack(buf+len, buflen-len, "p", nt_devmode);
+
+ if (!nt_devmode)
+ return len;
+
+ len += tdb_pack(buf+len, buflen-len, "ffwwwwwwwwwwwwwwwwwwddddddddddddddp",
+ nt_devmode->devicename,
+ nt_devmode->formname,
+
+ nt_devmode->specversion,
+ nt_devmode->driverversion,
+ nt_devmode->size,
+ nt_devmode->driverextra,
+ nt_devmode->orientation,
+ nt_devmode->papersize,
+ nt_devmode->paperlength,
+ nt_devmode->paperwidth,
+ nt_devmode->scale,
+ nt_devmode->copies,
+ nt_devmode->defaultsource,
+ nt_devmode->printquality,
+ nt_devmode->color,
+ nt_devmode->duplex,
+ nt_devmode->yresolution,
+ nt_devmode->ttoption,
+ nt_devmode->collate,
+ nt_devmode->logpixels,
+
+ nt_devmode->fields,
+ nt_devmode->bitsperpel,
+ nt_devmode->pelswidth,
+ nt_devmode->pelsheight,
+ nt_devmode->displayflags,
+ nt_devmode->displayfrequency,
+ nt_devmode->icmmethod,
+ nt_devmode->icmintent,
+ nt_devmode->mediatype,
+ nt_devmode->dithertype,
+ nt_devmode->reserved1,
+ nt_devmode->reserved2,
+ nt_devmode->panningwidth,
+ nt_devmode->panningheight,
+ nt_devmode->private);
+
+
+ if (nt_devmode->private) {
+ len += tdb_pack(buf+len, buflen-len, "B",
+ nt_devmode->driverextra,
+ nt_devmode->private);
+ }
+
+ DEBUG(8,("Packed devicemode [%s]\n", nt_devmode->formname));
+
+ return len;
+}
+
+/****************************************************************************
+ Pack all values in all printer keys
+ ***************************************************************************/
+
+static int pack_values(NT_PRINTER_DATA *data, char *buf, int buflen)
+{
+ int len = 0;
+ int i, j;
+ REGISTRY_VALUE *val;
+ REGVAL_CTR *val_ctr;
+ pstring path;
+ int num_values;
+
+ if ( !data )
+ return 0;
+
+ /* loop over all keys */
+
+ for ( i=0; i<data->num_keys; i++ ) {
+ val_ctr = &data->keys[i].values;
+ num_values = regval_ctr_numvals( val_ctr );
+
+ /* loop over all values */
+
+ for ( j=0; j<num_values; j++ ) {
+ /* pathname should be stored as <key>\<value> */
+
+ val = regval_ctr_specific_value( val_ctr, j );
+ pstrcpy( path, data->keys[i].name );
+ pstrcat( path, "\\" );
+ pstrcat( path, regval_name(val) );
+
+ len += tdb_pack(buf+len, buflen-len, "pPdB",
+ val,
+ path,
+ regval_type(val),
+ regval_size(val),
+ regval_data_p(val) );
+ }
+
+ }
+
+ /* terminator */
+
+ len += tdb_pack(buf+len, buflen-len, "p", NULL);
+
+ return len;
+}
+
+
+/****************************************************************************
+ Delete a printer - this just deletes the printer info file, any open
+ handles are not affected.
+****************************************************************************/
+
+uint32 del_a_printer(char *sharename)
+{
+ pstring key;
+ TDB_DATA kbuf;
+
+ slprintf(key, sizeof(key)-1, "%s%s", PRINTERS_PREFIX, sharename);
+
+ kbuf.dptr=key;
+ kbuf.dsize=strlen(key)+1;
+
+ tdb_delete(tdb_printers, kbuf);
+ return 0;
+}
+
+/* FIXME!!! Reorder so this forward declaration is not necessary --jerry */
+static WERROR get_a_printer_2(NT_PRINTER_INFO_LEVEL_2 **, const char* sharename);
+static void free_nt_printer_info_level_2(NT_PRINTER_INFO_LEVEL_2 **);
+/****************************************************************************
+****************************************************************************/
+static WERROR update_a_printer_2(NT_PRINTER_INFO_LEVEL_2 *info)
+{
+ pstring key;
+ char *buf;
+ int buflen, len;
+ WERROR ret;
+ TDB_DATA kbuf, dbuf;
+
+ /*
+ * in addprinter: no servername and the printer is the name
+ * in setprinter: servername is \\server
+ * and printer is \\server\\printer
+ *
+ * Samba manages only local printers.
+ * we currently don't support things like path=\\other_server\printer
+ */
+
+ if (info->servername[0]!='\0') {
+ trim_string(info->printername, info->servername, NULL);
+ trim_char(info->printername, '\\', '\0');
+ info->servername[0]='\0';
+ }
+
+ /*
+ * JFM: one day I'll forget.
+ * below that's info->portname because that's the SAMBA sharename
+ * and I made NT 'thinks' it's the portname
+ * the info->sharename is the thing you can name when you add a printer
+ * that's the short-name when you create shared printer for 95/98
+ * So I've made a limitation in SAMBA: you can only have 1 printer model
+ * behind a SAMBA share.
+ */
+
+ buf = NULL;
+ buflen = 0;
+
+ again:
+ len = 0;
+ len += tdb_pack(buf+len, buflen-len, "dddddddddddfffffPfffff",
+ info->attributes,
+ info->priority,
+ info->default_priority,
+ info->starttime,
+ info->untiltime,
+ info->status,
+ info->cjobs,
+ info->averageppm,
+ info->changeid,
+ info->c_setprinter,
+ info->setuptime,
+ info->servername,
+ info->printername,
+ info->sharename,
+ info->portname,
+ info->drivername,
+ info->comment,
+ info->location,
+ info->sepfile,
+ info->printprocessor,
+ info->datatype,
+ info->parameters);
+
+ len += pack_devicemode(info->devmode, buf+len, buflen-len);
+
+ len += pack_values( &info->data, buf+len, buflen-len );
+
+ if (buflen != len) {
+ char *tb;
+
+ tb = (char *)Realloc(buf, len);
+ if (!tb) {
+ DEBUG(0,("update_a_printer_2: failed to enlarge buffer!\n"));
+ ret = WERR_NOMEM;
+ goto done;
+ }
+ else buf = tb;
+ buflen = len;
+ goto again;
+ }
+
+
+ slprintf(key, sizeof(key)-1, "%s%s", PRINTERS_PREFIX, info->sharename);
+
+ kbuf.dptr = key;
+ kbuf.dsize = strlen(key)+1;
+ dbuf.dptr = buf;
+ dbuf.dsize = len;
+
+ ret = (tdb_store(tdb_printers, kbuf, dbuf, TDB_REPLACE) == 0? WERR_OK : WERR_NOMEM);
+
+done:
+ if (!W_ERROR_IS_OK(ret))
+ DEBUG(8, ("error updating printer to tdb on disk\n"));
+
+ SAFE_FREE(buf);
+
+ DEBUG(8,("packed printer [%s] with driver [%s] portname=[%s] len=%d\n",
+ info->sharename, info->drivername, info->portname, len));
+
+ return ret;
+}
+
+
+/****************************************************************************
+ Malloc and return an NT devicemode.
+****************************************************************************/
+
+NT_DEVICEMODE *construct_nt_devicemode(const fstring default_devicename)
+{
+
+ char adevice[MAXDEVICENAME];
+ NT_DEVICEMODE *nt_devmode = (NT_DEVICEMODE *)malloc(sizeof(NT_DEVICEMODE));
+
+ if (nt_devmode == NULL) {
+ DEBUG(0,("construct_nt_devicemode: malloc fail.\n"));
+ return NULL;
+ }
+
+ ZERO_STRUCTP(nt_devmode);
+
+ safe_strcpy(adevice, default_devicename, sizeof(adevice)-1);
+ fstrcpy(nt_devmode->devicename, adevice);
+
+ fstrcpy(nt_devmode->formname, "Letter");
+
+ nt_devmode->specversion = 0x0401;
+ nt_devmode->driverversion = 0x0400;
+ nt_devmode->size = 0x00DC;
+ nt_devmode->driverextra = 0x0000;
+ nt_devmode->fields = FORMNAME | TTOPTION | PRINTQUALITY |
+ DEFAULTSOURCE | COPIES | SCALE |
+ PAPERSIZE | ORIENTATION;
+ nt_devmode->orientation = 1;
+ nt_devmode->papersize = PAPER_LETTER;
+ nt_devmode->paperlength = 0;
+ nt_devmode->paperwidth = 0;
+ nt_devmode->scale = 0x64;
+ nt_devmode->copies = 1;
+ nt_devmode->defaultsource = BIN_FORMSOURCE;
+ nt_devmode->printquality = RES_HIGH; /* 0x0258 */
+ nt_devmode->color = COLOR_MONOCHROME;
+ nt_devmode->duplex = DUP_SIMPLEX;
+ nt_devmode->yresolution = 0;
+ nt_devmode->ttoption = TT_SUBDEV;
+ nt_devmode->collate = COLLATE_FALSE;
+ nt_devmode->icmmethod = 0;
+ nt_devmode->icmintent = 0;
+ nt_devmode->mediatype = 0;
+ nt_devmode->dithertype = 0;
+
+ /* non utilisés par un driver d'imprimante */
+ nt_devmode->logpixels = 0;
+ nt_devmode->bitsperpel = 0;
+ nt_devmode->pelswidth = 0;
+ nt_devmode->pelsheight = 0;
+ nt_devmode->displayflags = 0;
+ nt_devmode->displayfrequency = 0;
+ nt_devmode->reserved1 = 0;
+ nt_devmode->reserved2 = 0;
+ nt_devmode->panningwidth = 0;
+ nt_devmode->panningheight = 0;
+
+ nt_devmode->private = NULL;
+ return nt_devmode;
+}
+
+/****************************************************************************
+ Deepcopy an NT devicemode.
+****************************************************************************/
+
+NT_DEVICEMODE *dup_nt_devicemode(NT_DEVICEMODE *nt_devicemode)
+{
+ NT_DEVICEMODE *new_nt_devicemode = NULL;
+
+ if ( !nt_devicemode )
+ return NULL;
+
+ if ((new_nt_devicemode = (NT_DEVICEMODE *)memdup(nt_devicemode, sizeof(NT_DEVICEMODE))) == NULL) {
+ DEBUG(0,("dup_nt_devicemode: malloc fail.\n"));
+ return NULL;
+ }
+
+ new_nt_devicemode->private = NULL;
+ if (nt_devicemode->private != NULL) {
+ if ((new_nt_devicemode->private = memdup(nt_devicemode->private, nt_devicemode->driverextra)) == NULL) {
+ SAFE_FREE(new_nt_devicemode);
+ DEBUG(0,("dup_nt_devicemode: malloc fail.\n"));
+ return NULL;
+ }
+ }
+
+ return new_nt_devicemode;
+}
+
+/****************************************************************************
+ Clean up and deallocate a (maybe partially) allocated NT_DEVICEMODE.
+****************************************************************************/
+
+void free_nt_devicemode(NT_DEVICEMODE **devmode_ptr)
+{
+ NT_DEVICEMODE *nt_devmode = *devmode_ptr;
+
+ if(nt_devmode == NULL)
+ return;
+
+ DEBUG(106,("free_nt_devicemode: deleting DEVMODE\n"));
+
+ SAFE_FREE(nt_devmode->private);
+ SAFE_FREE(*devmode_ptr);
+}
+
+/****************************************************************************
+ Clean up and deallocate a (maybe partially) allocated NT_PRINTER_INFO_LEVEL_2.
+****************************************************************************/
+static void free_nt_printer_info_level_2(NT_PRINTER_INFO_LEVEL_2 **info_ptr)
+{
+ NT_PRINTER_INFO_LEVEL_2 *info = *info_ptr;
+ NT_PRINTER_DATA *data;
+ int i;
+
+ if ( !info )
+ return;
+
+ DEBUG(106,("free_nt_printer_info_level_2: deleting info\n"));
+
+ free_nt_devicemode(&info->devmode);
+
+ /* clean up all registry keys */
+
+ data = &info->data;
+ for ( i=0; i<data->num_keys; i++ ) {
+ SAFE_FREE( data->keys[i].name );
+ regval_ctr_destroy( &data->keys[i].values );
+ }
+ SAFE_FREE( data->keys );
+
+ /* finally the top level structure */
+
+ SAFE_FREE( *info_ptr );
+}
+
+
+/****************************************************************************
+****************************************************************************/
+int unpack_devicemode(NT_DEVICEMODE **nt_devmode, char *buf, int buflen)
+{
+ int len = 0;
+ int extra_len = 0;
+ NT_DEVICEMODE devmode;
+
+ ZERO_STRUCT(devmode);
+
+ len += tdb_unpack(buf+len, buflen-len, "p", nt_devmode);
+
+ if (!*nt_devmode) return len;
+
+ len += tdb_unpack(buf+len, buflen-len, "ffwwwwwwwwwwwwwwwwwwddddddddddddddp",
+ devmode.devicename,
+ devmode.formname,
+
+ &devmode.specversion,
+ &devmode.driverversion,
+ &devmode.size,
+ &devmode.driverextra,
+ &devmode.orientation,
+ &devmode.papersize,
+ &devmode.paperlength,
+ &devmode.paperwidth,
+ &devmode.scale,
+ &devmode.copies,
+ &devmode.defaultsource,
+ &devmode.printquality,
+ &devmode.color,
+ &devmode.duplex,
+ &devmode.yresolution,
+ &devmode.ttoption,
+ &devmode.collate,
+ &devmode.logpixels,
+
+ &devmode.fields,
+ &devmode.bitsperpel,
+ &devmode.pelswidth,
+ &devmode.pelsheight,
+ &devmode.displayflags,
+ &devmode.displayfrequency,
+ &devmode.icmmethod,
+ &devmode.icmintent,
+ &devmode.mediatype,
+ &devmode.dithertype,
+ &devmode.reserved1,
+ &devmode.reserved2,
+ &devmode.panningwidth,
+ &devmode.panningheight,
+ &devmode.private);
+
+ if (devmode.private) {
+ /* the len in tdb_unpack is an int value and
+ * devmode.driverextra is only a short
+ */
+ len += tdb_unpack(buf+len, buflen-len, "B", &extra_len, &devmode.private);
+ devmode.driverextra=(uint16)extra_len;
+
+ /* check to catch an invalid TDB entry so we don't segfault */
+ if (devmode.driverextra == 0) {
+ devmode.private = NULL;
+ }
+ }
+
+ *nt_devmode = (NT_DEVICEMODE *)memdup(&devmode, sizeof(devmode));
+
+ DEBUG(8,("Unpacked devicemode [%s](%s)\n", devmode.devicename, devmode.formname));
+ if (devmode.private)
+ DEBUG(8,("with a private section of %d bytes\n", devmode.driverextra));
+
+ return len;
+}
+
+/****************************************************************************
+ Allocate and initialize a new slot.
+***************************************************************************/
+
+static int add_new_printer_key( NT_PRINTER_DATA *data, const char *name )
+{
+ NT_PRINTER_KEY *d;
+ int key_index;
+
+ if ( !data || !name )
+ return -1;
+
+ /* allocate another slot in the NT_PRINTER_KEY array */
+
+ d = Realloc( data->keys, sizeof(NT_PRINTER_KEY)*(data->num_keys+1) );
+ if ( d )
+ data->keys = d;
+
+ key_index = data->num_keys;
+
+ /* initialze new key */
+
+ data->num_keys++;
+ data->keys[key_index].name = strdup( name );
+
+ ZERO_STRUCTP( &data->keys[key_index].values );
+
+ regval_ctr_init( &data->keys[key_index].values );
+
+ DEBUG(10,("add_new_printer_key: Inserted new data key [%s]\n", name ));
+
+ return key_index;
+}
+
+/****************************************************************************
+ search for a registry key name in the existing printer data
+ ***************************************************************************/
+
+int lookup_printerkey( NT_PRINTER_DATA *data, const char *name )
+{
+ int key_index = -1;
+ int i;
+
+ if ( !data || !name )
+ return -1;
+
+ DEBUG(12,("lookup_printerkey: Looking for [%s]\n", name));
+
+ /* loop over all existing keys */
+
+ for ( i=0; i<data->num_keys; i++ ) {
+ if ( strequal(data->keys[i].name, name) ) {
+ DEBUG(12,("lookup_printerkey: Found [%s]!\n", name));
+ key_index = i;
+ break;
+
+ }
+ }
+
+ return key_index;
+}
+
+/****************************************************************************
+ ***************************************************************************/
+
+uint32 get_printer_subkeys( NT_PRINTER_DATA *data, const char* key, fstring **subkeys )
+{
+ int i, j;
+ int key_len;
+ int num_subkeys = 0;
+ char *p;
+ fstring *ptr, *subkeys_ptr = NULL;
+ fstring subkeyname;
+
+ if ( !data )
+ return 0;
+
+ for ( i=0; i<data->num_keys; i++ ) {
+ if ( StrnCaseCmp(data->keys[i].name, key, strlen(key)) == 0 ) {
+ /* match sure it is a subkey and not the key itself */
+
+ key_len = strlen( key );
+ if ( strlen(data->keys[i].name) == key_len )
+ continue;
+
+ /* get subkey path */
+
+ p = data->keys[i].name + key_len;
+ if ( *p == '\\' )
+ p++;
+ fstrcpy( subkeyname, p );
+ if ( (p = strchr( subkeyname, '\\' )) )
+ *p = '\0';
+
+ /* don't add a key more than once */
+
+ for ( j=0; j<num_subkeys; j++ ) {
+ if ( strequal( subkeys_ptr[j], subkeyname ) )
+ break;
+ }
+
+ if ( j != num_subkeys )
+ continue;
+
+ /* found a match, so allocate space and copy the name */
+
+ if ( !(ptr = Realloc( subkeys_ptr, (num_subkeys+2)*sizeof(fstring))) ) {
+ DEBUG(0,("get_printer_subkeys: Realloc failed for [%d] entries!\n",
+ num_subkeys+1));
+ SAFE_FREE( subkeys );
+ return 0;
+ }
+
+ subkeys_ptr = ptr;
+ fstrcpy( subkeys_ptr[num_subkeys], subkeyname );
+ num_subkeys++;
+ }
+
+ }
+
+ /* tag of the end */
+
+ if (num_subkeys)
+ fstrcpy(subkeys_ptr[num_subkeys], "" );
+
+ *subkeys = subkeys_ptr;
+
+ return num_subkeys;
+}
+
+#ifdef HAVE_ADS
+static void map_sz_into_ctr(REGVAL_CTR *ctr, const char *val_name,
+ const char *sz)
+{
+ smb_ucs2_t conv_str[1024];
+ size_t str_size;
+
+ regval_ctr_delvalue(ctr, val_name);
+ str_size = push_ucs2(NULL, conv_str, sz, sizeof(conv_str),
+ STR_TERMINATE | STR_NOALIGN);
+ regval_ctr_addvalue(ctr, val_name, REG_SZ,
+ (char *) conv_str, str_size);
+}
+
+static void map_dword_into_ctr(REGVAL_CTR *ctr, const char *val_name,
+ uint32 dword)
+{
+ regval_ctr_delvalue(ctr, val_name);
+ regval_ctr_addvalue(ctr, val_name, REG_DWORD,
+ (char *) &dword, sizeof(dword));
+}
+
+static void map_bool_into_ctr(REGVAL_CTR *ctr, const char *val_name,
+ BOOL b)
+{
+ uint8 bin_bool = (b ? 1 : 0);
+ regval_ctr_delvalue(ctr, val_name);
+ regval_ctr_addvalue(ctr, val_name, REG_BINARY,
+ (char *) &bin_bool, sizeof(bin_bool));
+}
+
+static void map_single_multi_sz_into_ctr(REGVAL_CTR *ctr, const char *val_name,
+ const char *multi_sz)
+{
+ smb_ucs2_t *conv_strs = NULL;
+ size_t str_size;
+
+ /* a multi-sz has to have a null string terminator, i.e., the last
+ string must be followed by two nulls */
+ str_size = (strlen(multi_sz) + 2) * sizeof(smb_ucs2_t);
+ conv_strs = calloc(str_size, 1);
+
+ push_ucs2(NULL, conv_strs, multi_sz, str_size,
+ STR_TERMINATE | STR_NOALIGN);
+
+ regval_ctr_delvalue(ctr, val_name);
+ regval_ctr_addvalue(ctr, val_name, REG_MULTI_SZ,
+ (char *) conv_strs, str_size);
+ safe_free(conv_strs);
+
+}
+
+/****************************************************************************
+ * Map the NT_PRINTER_INFO_LEVEL_2 data into DsSpooler keys for publishing.
+ *
+ * @param info2 NT_PRINTER_INFO_LEVEL_2 describing printer - gets modified
+ * @return BOOL indicating success or failure
+ ***************************************************************************/
+
+static BOOL map_nt_printer_info2_to_dsspooler(NT_PRINTER_INFO_LEVEL_2 *info2)
+{
+ REGVAL_CTR *ctr = NULL;
+ fstring longname;
+ char *allocated_string = NULL;
+ const char *ascii_str;
+ int i;
+
+ if ((i = lookup_printerkey(&info2->data, SPOOL_DSSPOOLER_KEY)) < 0)
+ i = add_new_printer_key(&info2->data, SPOOL_DSSPOOLER_KEY);
+ ctr = &info2->data.keys[i].values;
+
+ map_sz_into_ctr(ctr, SPOOL_REG_PRINTERNAME, info2->sharename);
+ map_sz_into_ctr(ctr, SPOOL_REG_SHORTSERVERNAME, global_myname());
+
+ get_mydnsfullname(longname);
+ map_sz_into_ctr(ctr, SPOOL_REG_SERVERNAME, longname);
+
+ asprintf(&allocated_string, "\\\\%s\\%s", longname, info2->sharename);
+ map_sz_into_ctr(ctr, SPOOL_REG_UNCNAME, allocated_string);
+ SAFE_FREE(allocated_string);
+
+ map_dword_into_ctr(ctr, SPOOL_REG_VERSIONNUMBER, 4);
+ map_sz_into_ctr(ctr, SPOOL_REG_DRIVERNAME, info2->drivername);
+ map_sz_into_ctr(ctr, SPOOL_REG_LOCATION, info2->location);
+ map_sz_into_ctr(ctr, SPOOL_REG_DESCRIPTION, info2->comment);
+ map_single_multi_sz_into_ctr(ctr, SPOOL_REG_PORTNAME, info2->portname);
+ map_sz_into_ctr(ctr, SPOOL_REG_PRINTSEPARATORFILE, info2->sepfile);
+ map_dword_into_ctr(ctr, SPOOL_REG_PRINTSTARTTIME, info2->starttime);
+ map_dword_into_ctr(ctr, SPOOL_REG_PRINTENDTIME, info2->untiltime);
+ map_dword_into_ctr(ctr, SPOOL_REG_PRIORITY, info2->priority);
+
+ map_bool_into_ctr(ctr, SPOOL_REG_PRINTKEEPPRINTEDJOBS,
+ (info2->attributes &
+ PRINTER_ATTRIBUTE_KEEPPRINTEDJOBS));
+
+ switch (info2->attributes & 0x3) {
+ case 0:
+ ascii_str = SPOOL_REGVAL_PRINTWHILESPOOLING;
+ break;
+ case 1:
+ ascii_str = SPOOL_REGVAL_PRINTAFTERSPOOLED;
+ break;
+ case 2:
+ ascii_str = SPOOL_REGVAL_PRINTDIRECT;
+ break;
+ default:
+ ascii_str = "unknown";
+ }
+ map_sz_into_ctr(ctr, SPOOL_REG_PRINTSPOOLING, ascii_str);
+
+ return True;
+}
+
+static void store_printer_guid(NT_PRINTER_INFO_LEVEL_2 *info2,
+ struct uuid guid)
+{
+ int i;
+ REGVAL_CTR *ctr=NULL;
+
+ /* find the DsSpooler key */
+ if ((i = lookup_printerkey(&info2->data, SPOOL_DSSPOOLER_KEY)) < 0)
+ i = add_new_printer_key(&info2->data, SPOOL_DSSPOOLER_KEY);
+ ctr = &info2->data.keys[i].values;
+
+ regval_ctr_delvalue(ctr, "objectGUID");
+ regval_ctr_addvalue(ctr, "objectGUID", REG_BINARY,
+ (char *) &guid, sizeof(struct uuid));
+}
+
+static WERROR publish_it(NT_PRINTER_INFO_LEVEL *printer)
+{
+ ADS_STATUS ads_rc;
+ TALLOC_CTX *ctx = talloc_init("publish_it");
+ ADS_MODLIST mods = ads_init_mods(ctx);
+ char *prt_dn = NULL, *srv_dn, *srv_cn_0;
+ char *srv_dn_utf8, **srv_cn_utf8;
+ void *res = NULL;
+ ADS_STRUCT *ads;
+ const char *attrs[] = {"objectGUID", NULL};
+ struct uuid guid;
+ WERROR win_rc = WERR_OK;
+
+ ZERO_STRUCT(guid);
+ /* set the DsSpooler info and attributes */
+ if (!(map_nt_printer_info2_to_dsspooler(printer->info_2)))
+ return WERR_NOMEM;
+ printer->info_2->attributes |= PRINTER_ATTRIBUTE_PUBLISHED;
+ win_rc = mod_a_printer(*printer, 2);
+ if (!W_ERROR_IS_OK(win_rc)) {
+ DEBUG(3, ("err %d saving data\n",
+ W_ERROR_V(win_rc)));
+ return win_rc;
+ }
+
+ /* Build the ads mods */
+ get_local_printer_publishing_data(ctx, &mods,
+ &printer->info_2->data);
+ ads_mod_str(ctx, &mods, SPOOL_REG_PRINTERNAME,
+ printer->info_2->sharename);
+
+ /* initial ads structure */
+
+ ads = ads_init(NULL, NULL, NULL);
+ if (!ads) {
+ DEBUG(3, ("ads_init() failed\n"));
+ return WERR_SERVER_UNAVAILABLE;
+ }
+ setenv(KRB5_ENV_CCNAME, "MEMORY:prtpub_cache", 1);
+ SAFE_FREE(ads->auth.password);
+ ads->auth.password = secrets_fetch_machine_password(lp_workgroup(),
+ NULL, NULL);
+
+ /* ads_connect() will find the DC for us */
+ ads_rc = ads_connect(ads);
+ if (!ADS_ERR_OK(ads_rc)) {
+ DEBUG(3, ("ads_connect failed: %s\n", ads_errstr(ads_rc)));
+ ads_destroy(&ads);
+ return WERR_ACCESS_DENIED;
+ }
+
+ /* figure out where to publish */
+ ads_find_machine_acct(ads, &res, global_myname());
+
+ /* We use ldap_get_dn here as we need the answer
+ * in utf8 to call ldap_explode_dn(). JRA. */
+
+ srv_dn_utf8 = ldap_get_dn(ads->ld, res);
+ if (!srv_dn_utf8) {
+ ads_destroy(&ads);
+ return WERR_SERVER_UNAVAILABLE;
+ }
+ ads_msgfree(ads, res);
+ srv_cn_utf8 = ldap_explode_dn(srv_dn_utf8, 1);
+ if (!srv_cn_utf8) {
+ ldap_memfree(srv_dn_utf8);
+ ads_destroy(&ads);
+ return WERR_SERVER_UNAVAILABLE;
+ }
+ /* Now convert to CH_UNIX. */
+ if (pull_utf8_allocate(&srv_dn, srv_dn_utf8) == (size_t)-1) {
+ ldap_memfree(srv_dn_utf8);
+ ldap_memfree(srv_cn_utf8);
+ ads_destroy(&ads);
+ return WERR_SERVER_UNAVAILABLE;
+ }
+ if (pull_utf8_allocate(&srv_cn_0, srv_cn_utf8[0]) == (size_t)-1) {
+ ldap_memfree(srv_dn_utf8);
+ ldap_memfree(srv_cn_utf8);
+ ads_destroy(&ads);
+ SAFE_FREE(srv_dn);
+ return WERR_SERVER_UNAVAILABLE;
+ }
+
+ ldap_memfree(srv_dn_utf8);
+ ldap_memfree(srv_cn_utf8);
+
+ asprintf(&prt_dn, "cn=%s-%s,%s", srv_cn_0,
+ printer->info_2->sharename, srv_dn);
+
+ SAFE_FREE(srv_dn);
+ SAFE_FREE(srv_cn_0);
+
+ /* publish it */
+ ads_rc = ads_add_printer_entry(ads, prt_dn, ctx, &mods);
+ if (LDAP_ALREADY_EXISTS == ads_rc.err.rc)
+ ads_rc = ads_mod_printer_entry(ads, prt_dn, ctx,&mods);
+
+ /* retreive the guid and store it locally */
+ if (ADS_ERR_OK(ads_search_dn(ads, &res, prt_dn, attrs))) {
+ ads_memfree(ads, prt_dn);
+ ads_pull_guid(ads, res, &guid);
+ ads_msgfree(ads, res);
+ store_printer_guid(printer->info_2, guid);
+ win_rc = mod_a_printer(*printer, 2);
+ }
+
+ safe_free(prt_dn);
+ ads_destroy(&ads);
+
+ return WERR_OK;
+}
+
+WERROR unpublish_it(NT_PRINTER_INFO_LEVEL *printer)
+{
+ ADS_STATUS ads_rc;
+ ADS_STRUCT *ads;
+ void *res;
+ char *prt_dn = NULL;
+ WERROR win_rc;
+
+ printer->info_2->attributes ^= PRINTER_ATTRIBUTE_PUBLISHED;
+ win_rc = mod_a_printer(*printer, 2);
+ if (!W_ERROR_IS_OK(win_rc)) {
+ DEBUG(3, ("err %d saving data\n",
+ W_ERROR_V(win_rc)));
+ return win_rc;
+ }
+
+ ads = ads_init(NULL, NULL, NULL);
+ if (!ads) {
+ DEBUG(3, ("ads_init() failed\n"));
+ return WERR_SERVER_UNAVAILABLE;
+ }
+ setenv(KRB5_ENV_CCNAME, "MEMORY:prtpub_cache", 1);
+ SAFE_FREE(ads->auth.password);
+ ads->auth.password = secrets_fetch_machine_password(lp_workgroup(),
+ NULL, NULL);
+
+ /* ads_connect() will find the DC for us */
+ ads_rc = ads_connect(ads);
+ if (!ADS_ERR_OK(ads_rc)) {
+ DEBUG(3, ("ads_connect failed: %s\n", ads_errstr(ads_rc)));
+ ads_destroy(&ads);
+ return WERR_ACCESS_DENIED;
+ }
+
+ /* remove the printer from the directory */
+ ads_rc = ads_find_printer_on_server(ads, &res,
+ printer->info_2->sharename, global_myname());
+ if (ADS_ERR_OK(ads_rc) && ads_count_replies(ads, res)) {
+ prt_dn = ads_get_dn(ads, res);
+ ads_msgfree(ads, res);
+ ads_rc = ads_del_dn(ads, prt_dn);
+ ads_memfree(ads, prt_dn);
+ }
+
+ ads_destroy(&ads);
+ return WERR_OK;
+}
+
+/****************************************************************************
+ * Publish a printer in the directory
+ *
+ * @param snum describing printer service
+ * @return WERROR indicating status of publishing
+ ***************************************************************************/
+
+WERROR nt_printer_publish(Printer_entry *print_hnd, int snum, int action)
+{
+ NT_PRINTER_INFO_LEVEL *printer = NULL;
+ WERROR win_rc;
+
+ win_rc = get_a_printer(print_hnd, &printer, 2, lp_servicename(snum));
+ if (!W_ERROR_IS_OK(win_rc))
+ return win_rc;
+
+ switch(action) {
+ case SPOOL_DS_PUBLISH:
+ case SPOOL_DS_UPDATE:
+ win_rc = publish_it(printer);
+ break;
+ case SPOOL_DS_UNPUBLISH:
+ win_rc = unpublish_it(printer);
+ break;
+ default:
+ win_rc = WERR_NOT_SUPPORTED;
+ }
+
+
+ free_a_printer(&printer, 2);
+ return win_rc;
+}
+
+BOOL is_printer_published(Printer_entry *print_hnd, int snum,
+ struct uuid *guid)
+{
+ NT_PRINTER_INFO_LEVEL *printer = NULL;
+ REGVAL_CTR *ctr;
+ REGISTRY_VALUE *guid_val;
+ WERROR win_rc;
+ int i;
+
+
+ win_rc = get_a_printer(print_hnd, &printer, 2, lp_servicename(snum));
+ if (!W_ERROR_IS_OK(win_rc))
+ return False;
+
+ if (!(printer->info_2->attributes & PRINTER_ATTRIBUTE_PUBLISHED))
+ return False;
+
+ if ((i = lookup_printerkey(&printer->info_2->data,
+ SPOOL_DSSPOOLER_KEY)) < 0)
+ return False;
+
+ if (!(ctr = &printer->info_2->data.keys[i].values)) {
+ return False;
+ }
+
+ if (!(guid_val = regval_ctr_getvalue(ctr, "objectGUID"))) {
+ return False;
+ }
+
+ if (regval_size(guid_val) == sizeof(struct uuid))
+ memcpy(guid, regval_data_p(guid_val), sizeof(struct uuid));
+
+ return True;
+}
+
+#else
+WERROR nt_printer_publish(Printer_entry *print_hnd, int snum, int action)
+{
+ return WERR_OK;
+}
+BOOL is_printer_published(Printer_entry *print_hnd, int snum,
+ struct uuid *guid)
+{
+ return False;
+}
+#endif
+/****************************************************************************
+ ***************************************************************************/
+
+WERROR delete_all_printer_data( NT_PRINTER_INFO_LEVEL_2 *p2, const char *key )
+{
+ NT_PRINTER_DATA *data;
+ int i;
+ int removed_keys = 0;
+ int empty_slot;
+
+ data = &p2->data;
+ empty_slot = data->num_keys;
+
+ if ( !key )
+ return WERR_INVALID_PARAM;
+
+ /* remove all keys */
+
+ if ( !strlen(key) ) {
+ for ( i=0; i<data->num_keys; i++ ) {
+ DEBUG(8,("delete_all_printer_data: Removed all Printer Data from key [%s]\n",
+ data->keys[i].name));
+
+ SAFE_FREE( data->keys[i].name );
+ regval_ctr_destroy( &data->keys[i].values );
+ }
+
+ DEBUG(8,("delete_all_printer_data: Removed all Printer Data from printer [%s]\n",
+ p2->printername ));
+
+ SAFE_FREE( data->keys );
+ ZERO_STRUCTP( data );
+
+ return WERR_OK;
+ }
+
+ /* remove a specific key (and all subkeys) */
+
+ for ( i=0; i<data->num_keys; i++ ) {
+ if ( StrnCaseCmp( data->keys[i].name, key, strlen(key)) == 0 ) {
+ DEBUG(8,("delete_all_printer_data: Removed all Printer Data from key [%s]\n",
+ data->keys[i].name));
+
+ SAFE_FREE( data->keys[i].name );
+ regval_ctr_destroy( &data->keys[i].values );
+
+ /* mark the slot as empty */
+
+ ZERO_STRUCTP( &data->keys[i] );
+ }
+ }
+
+ /* find the first empty slot */
+
+ for ( i=0; i<data->num_keys; i++ ) {
+ if ( !data->keys[i].name ) {
+ empty_slot = i;
+ removed_keys++;
+ break;
+ }
+ }
+
+ if ( i == data->num_keys )
+ /* nothing was removed */
+ return WERR_INVALID_PARAM;
+
+ /* move everything down */
+
+ for ( i=empty_slot+1; i<data->num_keys; i++ ) {
+ if ( data->keys[i].name ) {
+ memcpy( &data->keys[empty_slot], &data->keys[i], sizeof(NT_PRINTER_KEY) );
+ ZERO_STRUCTP( &data->keys[i] );
+ empty_slot++;
+ removed_keys++;
+ }
+ }
+
+ /* update count */
+
+ data->num_keys -= removed_keys;
+
+ /* sanity check to see if anything is left */
+
+ if ( !data->num_keys ) {
+ DEBUG(8,("delete_all_printer_data: No keys left for printer [%s]\n", p2->printername ));
+
+ SAFE_FREE( data->keys );
+ ZERO_STRUCTP( data );
+ }
+
+ return WERR_OK;
+}
+
+/****************************************************************************
+ ***************************************************************************/
+
+WERROR delete_printer_data( NT_PRINTER_INFO_LEVEL_2 *p2, const char *key, const char *value )
+{
+ WERROR result = WERR_OK;
+ int key_index;
+
+ /* we must have names on non-zero length */
+
+ if ( !key || !*key|| !value || !*value )
+ return WERR_INVALID_NAME;
+
+ /* find the printer key first */
+
+ key_index = lookup_printerkey( &p2->data, key );
+ if ( key_index == -1 )
+ return WERR_OK;
+
+ /* make sure the value exists so we can return the correct error code */
+
+ if ( !regval_ctr_getvalue( &p2->data.keys[key_index].values, value ) )
+ return WERR_BADFILE;
+
+ regval_ctr_delvalue( &p2->data.keys[key_index].values, value );
+
+ DEBUG(8,("delete_printer_data: Removed key => [%s], value => [%s]\n",
+ key, value ));
+
+ return result;
+}
+
+/****************************************************************************
+ ***************************************************************************/
+
+WERROR add_printer_data( NT_PRINTER_INFO_LEVEL_2 *p2, const char *key, const char *value,
+ uint32 type, uint8 *data, int real_len )
+{
+ WERROR result = WERR_OK;
+ int key_index;
+
+ /* we must have names on non-zero length */
+
+ if ( !key || !*key|| !value || !*value )
+ return WERR_INVALID_NAME;
+
+ /* find the printer key first */
+
+ key_index = lookup_printerkey( &p2->data, key );
+ if ( key_index == -1 )
+ key_index = add_new_printer_key( &p2->data, key );
+
+ if ( key_index == -1 )
+ return WERR_NOMEM;
+
+ regval_ctr_addvalue( &p2->data.keys[key_index].values, value,
+ type, (const char *)data, real_len );
+
+ DEBUG(8,("add_printer_data: Added key => [%s], value => [%s], type=> [%d], size => [%d]\n",
+ key, value, type, real_len ));
+
+ return result;
+}
+
+/****************************************************************************
+ ***************************************************************************/
+
+REGISTRY_VALUE* get_printer_data( NT_PRINTER_INFO_LEVEL_2 *p2, const char *key, const char *value )
+{
+ int key_index;
+
+ if ( (key_index = lookup_printerkey( &p2->data, key )) == -1 )
+ return NULL;
+
+ DEBUG(8,("get_printer_data: Attempting to lookup key => [%s], value => [%s]\n",
+ key, value ));
+
+ return regval_ctr_getvalue( &p2->data.keys[key_index].values, value );
+}
+
+/****************************************************************************
+ Unpack a list of registry values frem the TDB
+ ***************************************************************************/
+
+static int unpack_values(NT_PRINTER_DATA *printer_data, char *buf, int buflen)
+{
+ int len = 0;
+ uint32 type;
+ pstring string, valuename, keyname;
+ char *str;
+ int size;
+ uint8 *data_p;
+ REGISTRY_VALUE *regval_p;
+ int key_index;
+
+ /* add the "PrinterDriverData" key first for performance reasons */
+
+ add_new_printer_key( printer_data, SPOOL_PRINTERDATA_KEY );
+
+ /* loop and unpack the rest of the registry values */
+
+ while ( True ) {
+
+ /* check to see if there are any more registry values */
+
+ len += tdb_unpack(buf+len, buflen-len, "p", &regval_p);
+ if ( !regval_p )
+ break;
+
+ /* unpack the next regval */
+
+ len += tdb_unpack(buf+len, buflen-len, "fdB",
+ string,
+ &type,
+ &size,
+ &data_p);
+
+ /*
+ * break of the keyname from the value name.
+ * Should only be one '\' in the string returned.
+ */
+
+ str = strrchr( string, '\\');
+
+ /* Put in "PrinterDriverData" is no key specified */
+
+ if ( !str ) {
+ pstrcpy( keyname, SPOOL_PRINTERDATA_KEY );
+ pstrcpy( valuename, string );
+ }
+ else {
+ *str = '\0';
+ pstrcpy( keyname, string );
+ pstrcpy( valuename, str+1 );
+ }
+
+ /* see if we need a new key */
+
+ if ( (key_index=lookup_printerkey( printer_data, keyname )) == -1 )
+ key_index = add_new_printer_key( printer_data, keyname );
+
+ if ( key_index == -1 ) {
+ DEBUG(0,("unpack_values: Failed to allocate a new key [%s]!\n",
+ keyname));
+ break;
+ }
+
+ /* add the new value */
+
+ regval_ctr_addvalue( &printer_data->keys[key_index].values, valuename, type, (const char *)data_p, size );
+
+ SAFE_FREE(data_p); /* 'B' option to tdbpack does a malloc() */
+
+ DEBUG(8,("specific: [%s:%s], len: %d\n", keyname, valuename, size));
+ }
+
+ return len;
+}
+
+/****************************************************************************
+ ***************************************************************************/
+
+static void map_to_os2_driver(fstring drivername)
+{
+ static BOOL initialised=False;
+ static fstring last_from,last_to;
+ char *mapfile = lp_os2_driver_map();
+ char **lines = NULL;
+ int numlines = 0;
+ int i;
+
+ if (!strlen(drivername))
+ return;
+
+ if (!*mapfile)
+ return;
+
+ if (!initialised) {
+ *last_from = *last_to = 0;
+ initialised = True;
+ }
+
+ if (strequal(drivername,last_from)) {
+ DEBUG(3,("Mapped Windows driver %s to OS/2 driver %s\n",drivername,last_to));
+ fstrcpy(drivername,last_to);
+ return;
+ }
+
+ lines = file_lines_load(mapfile, &numlines);
+ if (numlines == 0) {
+ DEBUG(0,("No entries in OS/2 driver map %s\n",mapfile));
+ return;
+ }
+
+ DEBUG(4,("Scanning OS/2 driver map %s\n",mapfile));
+
+ for( i = 0; i < numlines; i++) {
+ char *nt_name = lines[i];
+ char *os2_name = strchr(nt_name,'=');
+
+ if (!os2_name)
+ continue;
+
+ *os2_name++ = 0;
+
+ while (isspace(*nt_name))
+ nt_name++;
+
+ if (!*nt_name || strchr("#;",*nt_name))
+ continue;
+
+ {
+ int l = strlen(nt_name);
+ while (l && isspace(nt_name[l-1])) {
+ nt_name[l-1] = 0;
+ l--;
+ }
+ }
+
+ while (isspace(*os2_name))
+ os2_name++;
+
+ {
+ int l = strlen(os2_name);
+ while (l && isspace(os2_name[l-1])) {
+ os2_name[l-1] = 0;
+ l--;
+ }
+ }
+
+ if (strequal(nt_name,drivername)) {
+ DEBUG(3,("Mapped windows driver %s to os2 driver%s\n",drivername,os2_name));
+ fstrcpy(last_from,drivername);
+ fstrcpy(last_to,os2_name);
+ fstrcpy(drivername,os2_name);
+ file_lines_free(lines);
+ return;
+ }
+ }
+
+ file_lines_free(lines);
+}
+
+/****************************************************************************
+ Get a default printer info 2 struct.
+****************************************************************************/
+static WERROR get_a_printer_2_default(NT_PRINTER_INFO_LEVEL_2 **info_ptr, const char *sharename)
+{
+ int snum;
+ NT_PRINTER_INFO_LEVEL_2 info;
+
+ ZERO_STRUCT(info);
+
+ snum = lp_servicenumber(sharename);
+
+ slprintf(info.servername, sizeof(info.servername)-1, "\\\\%s", get_called_name());
+ slprintf(info.printername, sizeof(info.printername)-1, "\\\\%s\\%s",
+ get_called_name(), sharename);
+ fstrcpy(info.sharename, sharename);
+ fstrcpy(info.portname, SAMBA_PRINTER_PORT_NAME);
+
+ /* by setting the driver name to an empty string, a local NT admin
+ can now run the **local** APW to install a local printer driver
+ for a Samba shared printer in 2.2. Without this, drivers **must** be
+ installed on the Samba server for NT clients --jerry */
+#if 0 /* JERRY --do not uncomment-- */
+ if (!*info.drivername)
+ fstrcpy(info.drivername, "NO DRIVER AVAILABLE FOR THIS PRINTER");
+#endif
+
+
+ DEBUG(10,("get_a_printer_2_default: driver name set to [%s]\n", info.drivername));
+
+ pstrcpy(info.comment, "");
+ fstrcpy(info.printprocessor, "winprint");
+ fstrcpy(info.datatype, "RAW");
+
+ info.attributes = PRINTER_ATTRIBUTE_SAMBA;
+
+ info.starttime = 0; /* Minutes since 12:00am GMT */
+ info.untiltime = 0; /* Minutes since 12:00am GMT */
+ info.priority = 1;
+ info.default_priority = 1;
+ info.setuptime = (uint32)time(NULL);
+
+ /*
+ * I changed this as I think it is better to have a generic
+ * DEVMODE than to crash Win2k explorer.exe --jerry
+ * See the HP Deskjet 990c Win2k drivers for an example.
+ *
+ * However the default devmode appears to cause problems
+ * with the HP CLJ 8500 PCL driver. Hence the addition of
+ * the "default devmode" parameter --jerry 22/01/2002
+ */
+
+ if (lp_default_devmode(snum)) {
+ if ((info.devmode = construct_nt_devicemode(info.printername)) == NULL)
+ goto fail;
+ }
+ else {
+ info.devmode = NULL;
+ }
+
+ /* This will get the current RPC talloc context, but we should be
+ passing this as a parameter... fixme... JRA ! */
+
+ if (!nt_printing_getsec(get_talloc_ctx(), sharename, &info.secdesc_buf))
+ goto fail;
+
+ *info_ptr = (NT_PRINTER_INFO_LEVEL_2 *)memdup(&info, sizeof(info));
+ if (! *info_ptr) {
+ DEBUG(0,("get_a_printer_2_default: malloc fail.\n"));
+ goto fail;
+ }
+
+ return WERR_OK;
+
+ fail:
+ if (info.devmode)
+ free_nt_devicemode(&info.devmode);
+ return WERR_ACCESS_DENIED;
+}
+
+/****************************************************************************
+****************************************************************************/
+static WERROR get_a_printer_2(NT_PRINTER_INFO_LEVEL_2 **info_ptr, const char *sharename)
+{
+ pstring key;
+ NT_PRINTER_INFO_LEVEL_2 info;
+ int len = 0;
+ TDB_DATA kbuf, dbuf;
+ fstring printername;
+ char adevice[MAXDEVICENAME];
+
+ ZERO_STRUCT(info);
+
+ slprintf(key, sizeof(key)-1, "%s%s", PRINTERS_PREFIX, sharename);
+
+ kbuf.dptr = key;
+ kbuf.dsize = strlen(key)+1;
+
+ dbuf = tdb_fetch(tdb_printers, kbuf);
+ if (!dbuf.dptr)
+ return get_a_printer_2_default(info_ptr, sharename);
+
+ len += tdb_unpack(dbuf.dptr+len, dbuf.dsize-len, "dddddddddddfffffPfffff",
+ &info.attributes,
+ &info.priority,
+ &info.default_priority,
+ &info.starttime,
+ &info.untiltime,
+ &info.status,
+ &info.cjobs,
+ &info.averageppm,
+ &info.changeid,
+ &info.c_setprinter,
+ &info.setuptime,
+ info.servername,
+ info.printername,
+ info.sharename,
+ info.portname,
+ info.drivername,
+ info.comment,
+ info.location,
+ info.sepfile,
+ info.printprocessor,
+ info.datatype,
+ info.parameters);
+
+ /* Samba has to have shared raw drivers. */
+ info.attributes = PRINTER_ATTRIBUTE_SAMBA;
+
+ /* Restore the stripped strings. */
+ slprintf(info.servername, sizeof(info.servername)-1, "\\\\%s", get_called_name());
+ slprintf(printername, sizeof(printername)-1, "\\\\%s\\%s", get_called_name(),
+ info.printername);
+ fstrcpy(info.printername, printername);
+
+ len += unpack_devicemode(&info.devmode,dbuf.dptr+len, dbuf.dsize-len);
+
+ /*
+ * Some client drivers freak out if there is a NULL devmode
+ * (probably the driver is not checking before accessing
+ * the devmode pointer) --jerry
+ *
+ * See comments in get_a_printer_2_default()
+ */
+
+ if (lp_default_devmode(lp_servicenumber(sharename)) && !info.devmode) {
+ DEBUG(8,("get_a_printer_2: Constructing a default device mode for [%s]\n",
+ printername));
+ info.devmode = construct_nt_devicemode(printername);
+ }
+
+ safe_strcpy(adevice, info.printername, sizeof(adevice)-1);
+ if (info.devmode) {
+ fstrcpy(info.devmode->devicename, adevice);
+ }
+
+ len += unpack_values( &info.data, dbuf.dptr+len, dbuf.dsize-len );
+
+ /* This will get the current RPC talloc context, but we should be
+ passing this as a parameter... fixme... JRA ! */
+
+ nt_printing_getsec(get_talloc_ctx(), sharename, &info.secdesc_buf);
+
+ /* Fix for OS/2 drivers. */
+
+ if (get_remote_arch() == RA_OS2)
+ map_to_os2_driver(info.drivername);
+
+ SAFE_FREE(dbuf.dptr);
+ *info_ptr=memdup(&info, sizeof(info));
+
+ DEBUG(9,("Unpacked printer [%s] name [%s] running driver [%s]\n",
+ sharename, info.printername, info.drivername));
+
+ return WERR_OK;
+}
+
+/****************************************************************************
+ Debugging function, dump at level 6 the struct in the logs.
+****************************************************************************/
+static uint32 dump_a_printer(NT_PRINTER_INFO_LEVEL printer, uint32 level)
+{
+ uint32 result;
+ NT_PRINTER_INFO_LEVEL_2 *info2;
+
+ DEBUG(106,("Dumping printer at level [%d]\n", level));
+
+ switch (level) {
+ case 2:
+ {
+ if (printer.info_2 == NULL)
+ result=5;
+ else
+ {
+ info2=printer.info_2;
+
+ DEBUGADD(106,("attributes:[%d]\n", info2->attributes));
+ DEBUGADD(106,("priority:[%d]\n", info2->priority));
+ DEBUGADD(106,("default_priority:[%d]\n", info2->default_priority));
+ DEBUGADD(106,("starttime:[%d]\n", info2->starttime));
+ DEBUGADD(106,("untiltime:[%d]\n", info2->untiltime));
+ DEBUGADD(106,("status:[%d]\n", info2->status));
+ DEBUGADD(106,("cjobs:[%d]\n", info2->cjobs));
+ DEBUGADD(106,("averageppm:[%d]\n", info2->averageppm));
+ DEBUGADD(106,("changeid:[%d]\n", info2->changeid));
+ DEBUGADD(106,("c_setprinter:[%d]\n", info2->c_setprinter));
+ DEBUGADD(106,("setuptime:[%d]\n", info2->setuptime));
+
+ DEBUGADD(106,("servername:[%s]\n", info2->servername));
+ DEBUGADD(106,("printername:[%s]\n", info2->printername));
+ DEBUGADD(106,("sharename:[%s]\n", info2->sharename));
+ DEBUGADD(106,("portname:[%s]\n", info2->portname));
+ DEBUGADD(106,("drivername:[%s]\n", info2->drivername));
+ DEBUGADD(106,("comment:[%s]\n", info2->comment));
+ DEBUGADD(106,("location:[%s]\n", info2->location));
+ DEBUGADD(106,("sepfile:[%s]\n", info2->sepfile));
+ DEBUGADD(106,("printprocessor:[%s]\n", info2->printprocessor));
+ DEBUGADD(106,("datatype:[%s]\n", info2->datatype));
+ DEBUGADD(106,("parameters:[%s]\n", info2->parameters));
+ result=0;
+ }
+ break;
+ }
+ default:
+ DEBUGADD(106,("dump_a_printer: Level %u not implemented\n", (unsigned int)level ));
+ result=1;
+ break;
+ }
+
+ return result;
+}
+
+/****************************************************************************
+ Update the changeid time.
+ This is SO NASTY as some drivers need this to change, others need it
+ static. This value will change every second, and I must hope that this
+ is enough..... DON'T CHANGE THIS CODE WITHOUT A TEST MATRIX THE SIZE OF
+ UTAH ! JRA.
+****************************************************************************/
+
+static uint32 rev_changeid(void)
+{
+ struct timeval tv;
+
+ get_process_uptime(&tv);
+
+#if 1 /* JERRY */
+ /* Return changeid as msec since spooler restart */
+ return tv.tv_sec * 1000 + tv.tv_usec / 1000;
+#else
+ /*
+ * This setting seems to work well but is too untested
+ * to replace the above calculation. Left in for experiementation
+ * of the reader --jerry (Tue Mar 12 09:15:05 CST 2002)
+ */
+ return tv.tv_sec * 10 + tv.tv_usec / 100000;
+#endif
+}
+
+/*
+ * The function below are the high level ones.
+ * only those ones must be called from the spoolss code.
+ * JFM.
+ */
+
+/****************************************************************************
+ Modify a printer. This is called from SETPRINTERDATA/DELETEPRINTERDATA.
+****************************************************************************/
+
+WERROR mod_a_printer(NT_PRINTER_INFO_LEVEL printer, uint32 level)
+{
+ WERROR result;
+
+ dump_a_printer(printer, level);
+
+ /*
+ * invalidate cache for all open handles to this printer.
+ * cache for a given handle will be updated on the next
+ * get_a_printer()
+ */
+
+ invalidate_printer_hnd_cache( printer.info_2->sharename );
+
+ switch (level) {
+ case 2:
+ {
+ /*
+ * Update the changestamp. Emperical tests show that the
+ * ChangeID is always updated,but c_setprinter is
+ * global spooler variable (not per printer).
+ */
+
+ /* ChangeID **must** be increasing over the lifetime
+ of client's spoolss service in order for the
+ client's cache to show updates */
+
+ printer.info_2->changeid = rev_changeid();
+
+ /*
+ * Because one day someone will ask:
+ * NT->NT An admin connection to a remote
+ * printer show changes imeediately in
+ * the properities dialog
+ *
+ * A non-admin connection will only show the
+ * changes after viewing the properites page
+ * 2 times. Seems to be related to a
+ * race condition in the client between the spooler
+ * updating the local cache and the Explorer.exe GUI
+ * actually displaying the properties.
+ *
+ * This is fixed in Win2k. admin/non-admin
+ * connections both display changes immediately.
+ *
+ * 14/12/01 --jerry
+ */
+
+ result=update_a_printer_2(printer.info_2);
+
+ break;
+ }
+ default:
+ result=WERR_UNKNOWN_LEVEL;
+ break;
+ }
+
+ return result;
+}
+
+/****************************************************************************
+ Initialize printer devmode & data with previously saved driver init values.
+****************************************************************************/
+
+static BOOL set_driver_init_2( NT_PRINTER_INFO_LEVEL_2 *info_ptr )
+{
+ int len = 0;
+ pstring key;
+ TDB_DATA kbuf, dbuf;
+ NT_PRINTER_INFO_LEVEL_2 info;
+
+
+ ZERO_STRUCT(info);
+
+ /*
+ * Delete any printer data 'values' already set. When called for driver
+ * replace, there will generally be some, but during an add printer, there
+ * should not be any (if there are delete them).
+ */
+
+ delete_all_printer_data( info_ptr, "" );
+
+ slprintf(key, sizeof(key)-1, "%s%s", DRIVER_INIT_PREFIX, info_ptr->drivername);
+
+ kbuf.dptr = key;
+ kbuf.dsize = strlen(key)+1;
+
+ dbuf = tdb_fetch(tdb_drivers, kbuf);
+ if (!dbuf.dptr) {
+ /*
+ * When changing to a driver that has no init info in the tdb, remove
+ * the previous drivers init info and leave the new on blank.
+ */
+ free_nt_devicemode(&info_ptr->devmode);
+ return False;
+ }
+
+ /*
+ * Get the saved DEVMODE..
+ */
+
+ len += unpack_devicemode(&info.devmode,dbuf.dptr+len, dbuf.dsize-len);
+
+ /*
+ * The saved DEVMODE contains the devicename from the printer used during
+ * the initialization save. Change it to reflect the new printer.
+ */
+
+ if ( info.devmode ) {
+ ZERO_STRUCT(info.devmode->devicename);
+ fstrcpy(info.devmode->devicename, info_ptr->printername);
+ }
+
+ /*
+ * NT/2k does not change out the entire DeviceMode of a printer
+ * when changing the driver. Only the driverextra, private, &
+ * driverversion fields. --jerry (Thu Mar 14 08:58:43 CST 2002)
+ *
+ * Later examination revealed that Windows NT/2k does reset the
+ * the printer's device mode, bit **only** when you change a
+ * property of the device mode such as the page orientation.
+ * --jerry
+ */
+
+
+ /* Bind the saved DEVMODE to the new the printer */
+
+ free_nt_devicemode(&info_ptr->devmode);
+ info_ptr->devmode = info.devmode;
+
+ DEBUG(10,("set_driver_init_2: Set printer [%s] init %s DEVMODE for driver [%s]\n",
+ info_ptr->printername, info_ptr->devmode?"VALID":"NULL", info_ptr->drivername));
+
+ /* Add the printer data 'values' to the new printer */
+
+ len += unpack_values( &info_ptr->data, dbuf.dptr+len, dbuf.dsize-len );
+
+
+ SAFE_FREE(dbuf.dptr);
+
+ return True;
+}
+
+/****************************************************************************
+ Initialize printer devmode & data with previously saved driver init values.
+ When a printer is created using AddPrinter, the drivername bound to the
+ printer is used to lookup previously saved driver initialization info, which
+ is bound to the new printer.
+****************************************************************************/
+
+BOOL set_driver_init(NT_PRINTER_INFO_LEVEL *printer, uint32 level)
+{
+ BOOL result = False;
+
+ switch (level) {
+ case 2:
+ result = set_driver_init_2(printer->info_2);
+ break;
+
+ default:
+ DEBUG(0,("set_driver_init: Programmer's error! Unknown driver_init level [%d]\n",
+ level));
+ break;
+ }
+
+ return result;
+}
+
+/****************************************************************************
+ Delete driver init data stored for a specified driver
+****************************************************************************/
+
+BOOL del_driver_init(char *drivername)
+{
+ pstring key;
+ TDB_DATA kbuf;
+
+ if (!drivername || !*drivername) {
+ DEBUG(3,("del_driver_init: No drivername specified!\n"));
+ return False;
+ }
+
+ slprintf(key, sizeof(key)-1, "%s%s", DRIVER_INIT_PREFIX, drivername);
+
+ kbuf.dptr = key;
+ kbuf.dsize = strlen(key)+1;
+
+ DEBUG(6,("del_driver_init: Removing driver init data for [%s]\n", drivername));
+
+ return (tdb_delete(tdb_drivers, kbuf) == 0);
+}
+
+/****************************************************************************
+ Pack up the DEVMODE and values for a printer into a 'driver init' entry
+ in the tdb. Note: this is different from the driver entry and the printer
+ entry. There should be a single driver init entry for each driver regardless
+ of whether it was installed from NT or 2K. Technically, they should be
+ different, but they work out to the same struct.
+****************************************************************************/
+
+static uint32 update_driver_init_2(NT_PRINTER_INFO_LEVEL_2 *info)
+{
+ pstring key;
+ char *buf;
+ int buflen, len, ret;
+ TDB_DATA kbuf, dbuf;
+
+ buf = NULL;
+ buflen = 0;
+
+ again:
+ len = 0;
+ len += pack_devicemode(info->devmode, buf+len, buflen-len);
+
+ len += pack_values( &info->data, buf+len, buflen-len );
+
+ if (buflen != len) {
+ char *tb;
+
+ tb = (char *)Realloc(buf, len);
+ if (!tb) {
+ DEBUG(0, ("update_driver_init_2: failed to enlarge buffer!\n"));
+ ret = -1;
+ goto done;
+ }
+ else
+ buf = tb;
+ buflen = len;
+ goto again;
+ }
+
+ slprintf(key, sizeof(key)-1, "%s%s", DRIVER_INIT_PREFIX, info->drivername);
+
+ kbuf.dptr = key;
+ kbuf.dsize = strlen(key)+1;
+ dbuf.dptr = buf;
+ dbuf.dsize = len;
+
+ ret = tdb_store(tdb_drivers, kbuf, dbuf, TDB_REPLACE);
+
+done:
+ if (ret == -1)
+ DEBUG(8, ("update_driver_init_2: error updating printer init to tdb on disk\n"));
+
+ SAFE_FREE(buf);
+
+ DEBUG(10,("update_driver_init_2: Saved printer [%s] init DEVMODE & values for driver [%s]\n",
+ info->sharename, info->drivername));
+
+ return ret;
+}
+
+/****************************************************************************
+ Update (i.e. save) the driver init info (DEVMODE and values) for a printer
+****************************************************************************/
+
+uint32 update_driver_init(NT_PRINTER_INFO_LEVEL printer, uint32 level)
+{
+ uint32 result;
+
+ dump_a_printer(printer, level);
+
+ switch (level) {
+ case 2:
+ result = update_driver_init_2(printer.info_2);
+ break;
+ default:
+ result = 1;
+ break;
+ }
+
+ return result;
+}
+
+/****************************************************************************
+ Convert the printer data value, a REG_BINARY array, into an initialization
+ DEVMODE. Note: the array must be parsed as if it was a DEVMODE in an rpc...
+ got to keep the endians happy :).
+****************************************************************************/
+
+static BOOL convert_driver_init( TALLOC_CTX *ctx, NT_DEVICEMODE *nt_devmode, uint8 *data, uint32 data_len )
+{
+ BOOL result = False;
+ prs_struct ps;
+ DEVICEMODE devmode;
+
+ ZERO_STRUCT(devmode);
+
+ prs_init(&ps, 0, ctx, UNMARSHALL);
+ ps.data_p = (char *)data;
+ ps.buffer_size = data_len;
+
+ if (spoolss_io_devmode("phantom DEVMODE", &ps, 0, &devmode))
+ result = convert_devicemode("", &devmode, &nt_devmode);
+ else
+ DEBUG(10,("convert_driver_init: error parsing DEVMODE\n"));
+
+ return result;
+}
+
+/****************************************************************************
+ Set the DRIVER_INIT info in the tdb. Requires Win32 client code that:
+
+ 1. Use the driver's config DLL to this UNC printername and:
+ a. Call DrvPrintEvent with PRINTER_EVENT_INITIALIZE
+ b. Call DrvConvertDevMode with CDM_DRIVER_DEFAULT to get default DEVMODE
+ 2. Call SetPrinterData with the 'magic' key and the DEVMODE as data.
+
+ The last step triggers saving the "driver initialization" information for
+ this printer into the tdb. Later, new printers that use this driver will
+ have this initialization information bound to them. This simulates the
+ driver initialization, as if it had run on the Samba server (as it would
+ have done on NT).
+
+ The Win32 client side code requirement sucks! But until we can run arbitrary
+ Win32 printer driver code on any Unix that Samba runs on, we are stuck with it.
+
+ It would have been easier to use SetPrinter because all the UNMARSHALLING of
+ the DEVMODE is done there, but 2K/XP clients do not set the DEVMODE... think
+ about it and you will realize why. JRR 010720
+****************************************************************************/
+
+static WERROR save_driver_init_2(NT_PRINTER_INFO_LEVEL *printer, uint8 *data, uint32 data_len )
+{
+ WERROR status = WERR_OK;
+ TALLOC_CTX *ctx = NULL;
+ NT_DEVICEMODE *nt_devmode = NULL;
+ NT_DEVICEMODE *tmp_devmode = printer->info_2->devmode;
+
+ /*
+ * When the DEVMODE is already set on the printer, don't try to unpack it.
+ */
+ DEBUG(8,("save_driver_init_2: Enter...\n"));
+
+ if ( !printer->info_2->devmode && data_len ) {
+ /*
+ * Set devmode on printer info, so entire printer initialization can be
+ * saved to tdb.
+ */
+
+ if ((ctx = talloc_init("save_driver_init_2")) == NULL)
+ return WERR_NOMEM;
+
+ if ((nt_devmode = (NT_DEVICEMODE*)malloc(sizeof(NT_DEVICEMODE))) == NULL) {
+ status = WERR_NOMEM;
+ goto done;
+ }
+
+ ZERO_STRUCTP(nt_devmode);
+
+ /*
+ * The DEVMODE is held in the 'data' component of the param in raw binary.
+ * Convert it to to a devmode structure
+ */
+ if ( !convert_driver_init( ctx, nt_devmode, data, data_len )) {
+ DEBUG(10,("save_driver_init_2: error converting DEVMODE\n"));
+ status = WERR_INVALID_PARAM;
+ goto done;
+ }
+
+ printer->info_2->devmode = nt_devmode;
+ }
+
+ /*
+ * Pack up and add (or update) the DEVMODE and any current printer data to
+ * a 'driver init' element in the tdb
+ *
+ */
+
+ if ( update_driver_init(*printer, 2) != 0 ) {
+ DEBUG(10,("save_driver_init_2: error updating DEVMODE\n"));
+ status = WERR_NOMEM;
+ goto done;
+ }
+
+ /*
+ * If driver initialization info was successfully saved, set the current
+ * printer to match it. This allows initialization of the current printer
+ * as well as the driver.
+ */
+ status = mod_a_printer(*printer, 2);
+ if (!W_ERROR_IS_OK(status)) {
+ DEBUG(10,("save_driver_init_2: error setting DEVMODE on printer [%s]\n",
+ printer->info_2->printername));
+ }
+
+ done:
+ talloc_destroy(ctx);
+ free_nt_devicemode( &nt_devmode );
+
+ printer->info_2->devmode = tmp_devmode;
+
+ return status;
+}
+
+/****************************************************************************
+ Update the driver init info (DEVMODE and specifics) for a printer
+****************************************************************************/
+
+WERROR save_driver_init(NT_PRINTER_INFO_LEVEL *printer, uint32 level, uint8 *data, uint32 data_len)
+{
+ WERROR status = WERR_OK;
+
+ switch (level) {
+ case 2:
+ status = save_driver_init_2( printer, data, data_len );
+ break;
+ default:
+ status = WERR_UNKNOWN_LEVEL;
+ break;
+ }
+
+ return status;
+}
+
+/****************************************************************************
+ Deep copy a NT_PRINTER_DATA
+****************************************************************************/
+
+static NTSTATUS copy_printer_data( NT_PRINTER_DATA *dst, NT_PRINTER_DATA *src )
+{
+ int i, j, num_vals, new_key_index;
+ REGVAL_CTR *src_key, *dst_key;
+
+ if ( !dst || !src )
+ return NT_STATUS_NO_MEMORY;
+
+ for ( i=0; i<src->num_keys; i++ ) {
+
+ /* create a new instance of the printerkey in the destination
+ printer_data object */
+
+ new_key_index = add_new_printer_key( dst, src->keys[i].name );
+ dst_key = &dst->keys[new_key_index].values;
+
+ src_key = &src->keys[i].values;
+ num_vals = regval_ctr_numvals( src_key );
+
+ /* dup the printer entire printer key */
+
+ for ( j=0; j<num_vals; j++ ) {
+ regval_ctr_copyvalue( dst_key, regval_ctr_specific_value(src_key, j) );
+ }
+ }
+
+ return NT_STATUS_OK;
+}
+
+/****************************************************************************
+ Deep copy a NT_PRINTER_INFO_LEVEL_2 structure using malloc()'d memeory
+ Caller must free.
+****************************************************************************/
+
+NT_PRINTER_INFO_LEVEL_2* dup_printer_2( TALLOC_CTX *ctx, NT_PRINTER_INFO_LEVEL_2 *printer )
+{
+ NT_PRINTER_INFO_LEVEL_2 *copy;
+
+ if ( !printer )
+ return NULL;
+
+ if ( !(copy = (NT_PRINTER_INFO_LEVEL_2 *)malloc(sizeof(NT_PRINTER_INFO_LEVEL_2))) )
+ return NULL;
+
+ memcpy( copy, printer, sizeof(NT_PRINTER_INFO_LEVEL_2) );
+
+ /* malloc()'d members copied here */
+
+ copy->devmode = dup_nt_devicemode( printer->devmode );
+
+ ZERO_STRUCT( copy->data );
+ copy_printer_data( &copy->data, &printer->data );
+
+ /* this is talloc()'d; very ugly that we have a structure that
+ is half malloc()'d and half talloc()'d but that is the way
+ that the PRINTER_INFO stuff is written right now. --jerry */
+
+ copy->secdesc_buf = dup_sec_desc_buf( ctx, printer->secdesc_buf );
+
+ return copy;
+}
+
+/****************************************************************************
+ Get a NT_PRINTER_INFO_LEVEL struct. It returns malloced memory.
+
+ Previously the code had a memory allocation problem because it always
+ used the TALLOC_CTX from the Printer_entry*. This context lasts
+ as a long as the original handle is open. So if the client made a lot
+ of getprinter[data]() calls, the memory usage would climb. Now we use
+ a short lived TALLOC_CTX for printer_info_2 objects returned. We
+ still use the Printer_entry->ctx for maintaining the cache copy though
+ since that object must live as long as the handle by definition.
+ --jerry
+
+****************************************************************************/
+
+WERROR get_a_printer( Printer_entry *print_hnd, NT_PRINTER_INFO_LEVEL **pp_printer, uint32 level,
+ const char *sharename)
+{
+ WERROR result;
+ NT_PRINTER_INFO_LEVEL *printer = NULL;
+
+ *pp_printer = NULL;
+
+ DEBUG(10,("get_a_printer: [%s] level %u\n", sharename, (unsigned int)level));
+
+ switch (level) {
+ case 2:
+ if ((printer = (NT_PRINTER_INFO_LEVEL *)malloc(sizeof(NT_PRINTER_INFO_LEVEL))) == NULL) {
+ DEBUG(0,("get_a_printer: malloc fail.\n"));
+ return WERR_NOMEM;
+ }
+ ZERO_STRUCTP(printer);
+
+ /*
+ * check for cache first. A Printer handle cannot changed
+ * to another printer object so we only check that the printer
+ * is actually for a printer and that the printer_info pointer
+ * is valid
+ */
+ if ( print_hnd
+ && (print_hnd->printer_type==PRINTER_HANDLE_IS_PRINTER)
+ && print_hnd->printer_info )
+ {
+ /* get_talloc_ctx() works here because we need a short
+ lived talloc context */
+
+ if ( !(printer->info_2 = dup_printer_2(get_talloc_ctx(), print_hnd->printer_info->info_2)) )
+ {
+ DEBUG(0,("get_a_printer: unable to copy cached printer info!\n"));
+
+ SAFE_FREE(printer);
+ return WERR_NOMEM;
+ }
+
+ DEBUG(10,("get_a_printer: using cached copy of printer_info_2\n"));
+
+ *pp_printer = printer;
+ result = WERR_OK;
+
+ break;
+ }
+
+ /* no cache for this handle; see if we can match one from another handle.
+ Make sure to use a short lived talloc ctx */
+
+ if ( print_hnd )
+ result = find_printer_in_print_hnd_cache(get_talloc_ctx(), &printer->info_2, sharename);
+
+ /* fail to disk if we don't have it with any open handle */
+
+ if ( !print_hnd || !W_ERROR_IS_OK(result) )
+ result = get_a_printer_2(&printer->info_2, sharename);
+
+ /* we have a new printer now. Save it with this handle */
+
+ if ( W_ERROR_IS_OK(result) ) {
+ dump_a_printer(*printer, level);
+
+ /* save a copy in cache */
+ if ( print_hnd && (print_hnd->printer_type==PRINTER_HANDLE_IS_PRINTER)) {
+ if ( !print_hnd->printer_info )
+ print_hnd->printer_info = (NT_PRINTER_INFO_LEVEL *)malloc(sizeof(NT_PRINTER_INFO_LEVEL));
+
+ if ( print_hnd->printer_info ) {
+ /* make sure to use the handle's talloc ctx here since
+ the printer_2 object must last until the handle is closed */
+
+ print_hnd->printer_info->info_2 = dup_printer_2(print_hnd->ctx, printer->info_2);
+
+ /* don't fail the lookup just because the cache update failed */
+ if ( !print_hnd->printer_info->info_2 )
+ DEBUG(0,("get_a_printer: unable to copy new printer info!\n"));
+ }
+ }
+ *pp_printer = printer;
+ }
+ else
+ SAFE_FREE(printer);
+
+ break;
+
+ default:
+ result=WERR_UNKNOWN_LEVEL;
+ break;
+ }
+
+ DEBUG(10,("get_a_printer: [%s] level %u returning %s\n", sharename, (unsigned int)level, dos_errstr(result)));
+
+ return result;
+}
+
+/****************************************************************************
+ Deletes a NT_PRINTER_INFO_LEVEL struct.
+****************************************************************************/
+
+uint32 free_a_printer(NT_PRINTER_INFO_LEVEL **pp_printer, uint32 level)
+{
+ uint32 result;
+ NT_PRINTER_INFO_LEVEL *printer = *pp_printer;
+
+ DEBUG(104,("freeing a printer at level [%d]\n", level));
+
+ if (printer == NULL)
+ return 0;
+
+ switch (level) {
+ case 2:
+ if (printer->info_2 != NULL) {
+ free_nt_printer_info_level_2(&printer->info_2);
+ result=0;
+ } else
+ result=4;
+ break;
+
+ default:
+ result=1;
+ break;
+ }
+
+ SAFE_FREE(*pp_printer);
+ return result;
+}
+
+/****************************************************************************
+****************************************************************************/
+uint32 add_a_printer_driver(NT_PRINTER_DRIVER_INFO_LEVEL driver, uint32 level)
+{
+ uint32 result;
+ DEBUG(104,("adding a printer at level [%d]\n", level));
+ dump_a_printer_driver(driver, level);
+
+ switch (level) {
+ case 3:
+ result=add_a_printer_driver_3(driver.info_3);
+ break;
+
+ case 6:
+ result=add_a_printer_driver_6(driver.info_6);
+ break;
+
+ default:
+ result=1;
+ break;
+ }
+
+ return result;
+}
+/****************************************************************************
+****************************************************************************/
+
+WERROR get_a_printer_driver(NT_PRINTER_DRIVER_INFO_LEVEL *driver, uint32 level,
+ fstring drivername, const char *architecture, uint32 version)
+{
+ WERROR result;
+
+ switch (level) {
+ case 3:
+ /* Sometime we just want any version of the driver */
+
+ if ( version == DRIVER_ANY_VERSION ) {
+ /* look for Win2k first and then for NT4 */
+ result = get_a_printer_driver_3(&driver->info_3, drivername,
+ architecture, 3);
+
+ if ( !W_ERROR_IS_OK(result) ) {
+ result = get_a_printer_driver_3( &driver->info_3,
+ drivername, architecture, 2 );
+ }
+ } else {
+ result = get_a_printer_driver_3(&driver->info_3, drivername,
+ architecture, version);
+ }
+ break;
+
+ default:
+ result=W_ERROR(1);
+ break;
+ }
+
+ if (W_ERROR_IS_OK(result))
+ dump_a_printer_driver(*driver, level);
+
+ return result;
+}
+
+/****************************************************************************
+****************************************************************************/
+uint32 free_a_printer_driver(NT_PRINTER_DRIVER_INFO_LEVEL driver, uint32 level)
+{
+ uint32 result;
+
+ switch (level) {
+ case 3:
+ {
+ NT_PRINTER_DRIVER_INFO_LEVEL_3 *info3;
+ if (driver.info_3 != NULL)
+ {
+ info3=driver.info_3;
+ SAFE_FREE(info3->dependentfiles);
+ ZERO_STRUCTP(info3);
+ SAFE_FREE(info3);
+ result=0;
+ } else {
+ result=4;
+ }
+ break;
+ }
+ case 6:
+ {
+ NT_PRINTER_DRIVER_INFO_LEVEL_6 *info6;
+ if (driver.info_6 != NULL) {
+ info6=driver.info_6;
+ SAFE_FREE(info6->dependentfiles);
+ SAFE_FREE(info6->previousnames);
+ ZERO_STRUCTP(info6);
+ SAFE_FREE(info6);
+ result=0;
+ } else {
+ result=4;
+ }
+ break;
+ }
+ default:
+ result=1;
+ break;
+ }
+ return result;
+}
+
+
+/****************************************************************************
+ Determine whether or not a particular driver is currently assigned
+ to a printer
+****************************************************************************/
+
+BOOL printer_driver_in_use ( NT_PRINTER_DRIVER_INFO_LEVEL_3 *info_3 )
+{
+ int snum;
+ int n_services = lp_numservices();
+ NT_PRINTER_INFO_LEVEL *printer = NULL;
+
+ if ( !info_3 )
+ return False;
+
+ DEBUG(5,("printer_driver_in_use: Beginning search through ntprinters.tdb...\n"));
+
+ /* loop through the printers.tdb and check for the drivername */
+
+ for (snum=0; snum<n_services; snum++) {
+ if ( !(lp_snum_ok(snum) && lp_print_ok(snum) ) )
+ continue;
+
+ if ( !W_ERROR_IS_OK(get_a_printer(NULL, &printer, 2, lp_servicename(snum))) )
+ continue;
+
+ if ( !StrCaseCmp(info_3->name, printer->info_2->drivername) ) {
+ free_a_printer( &printer, 2 );
+ return True;
+ }
+
+ free_a_printer( &printer, 2 );
+ }
+
+ DEBUG(5,("printer_driver_in_use: Completed search through ntprinters.tdb...\n"));
+
+ /* report that the driver is not in use by default */
+
+ return False;
+}
+
+
+/**********************************************************************
+ Check to see if a ogiven file is in use by *info
+ *********************************************************************/
+
+static BOOL drv_file_in_use( char* file, NT_PRINTER_DRIVER_INFO_LEVEL_3 *info )
+{
+ int i = 0;
+
+ if ( !info )
+ return False;
+
+ if ( strequal(file, info->driverpath) )
+ return True;
+
+ if ( strequal(file, info->datafile) )
+ return True;
+
+ if ( strequal(file, info->configfile) )
+ return True;
+
+ if ( strequal(file, info->helpfile) )
+ return True;
+
+ /* see of there are any dependent files to examine */
+
+ if ( !info->dependentfiles )
+ return False;
+
+ while ( *info->dependentfiles[i] ) {
+ if ( strequal(file, info->dependentfiles[i]) )
+ return True;
+ i++;
+ }
+
+ return False;
+
+}
+
+/**********************************************************************
+ Utility function to remove the dependent file pointed to by the
+ input parameter from the list
+ *********************************************************************/
+
+static void trim_dependent_file( fstring files[], int idx )
+{
+
+ /* bump everything down a slot */
+
+ while( *files[idx+1] ) {
+ fstrcpy( files[idx], files[idx+1] );
+ idx++;
+ }
+
+ *files[idx] = '\0';
+
+ return;
+}
+
+/**********************************************************************
+ Check if any of the files used by src are also used by drv
+ *********************************************************************/
+
+static BOOL trim_overlap_drv_files( NT_PRINTER_DRIVER_INFO_LEVEL_3 *src,
+ NT_PRINTER_DRIVER_INFO_LEVEL_3 *drv )
+{
+ BOOL in_use = False;
+ int i = 0;
+
+ if ( !src || !drv )
+ return False;
+
+ /* check each file. Remove it from the src structure if it overlaps */
+
+ if ( drv_file_in_use(src->driverpath, drv) ) {
+ in_use = True;
+ DEBUG(10,("Removing driverfile [%s] from list\n", src->driverpath));
+ fstrcpy( src->driverpath, "" );
+ }
+
+ if ( drv_file_in_use(src->datafile, drv) ) {
+ in_use = True;
+ DEBUG(10,("Removing datafile [%s] from list\n", src->datafile));
+ fstrcpy( src->datafile, "" );
+ }
+
+ if ( drv_file_in_use(src->configfile, drv) ) {
+ in_use = True;
+ DEBUG(10,("Removing configfile [%s] from list\n", src->configfile));
+ fstrcpy( src->configfile, "" );
+ }
+
+ if ( drv_file_in_use(src->helpfile, drv) ) {
+ in_use = True;
+ DEBUG(10,("Removing helpfile [%s] from list\n", src->helpfile));
+ fstrcpy( src->helpfile, "" );
+ }
+
+ /* are there any dependentfiles to examine? */
+
+ if ( !src->dependentfiles )
+ return in_use;
+
+ while ( *src->dependentfiles[i] ) {
+ if ( drv_file_in_use(src->dependentfiles[i], drv) ) {
+ in_use = True;
+ DEBUG(10,("Removing [%s] from dependent file list\n", src->dependentfiles[i]));
+ trim_dependent_file( src->dependentfiles, i );
+ } else
+ i++;
+ }
+
+ return in_use;
+}
+
+/****************************************************************************
+ Determine whether or not a particular driver files are currently being
+ used by any other driver.
+
+ Return value is True if any files were in use by other drivers
+ and False otherwise.
+
+ Upon return, *info has been modified to only contain the driver files
+ which are not in use
+****************************************************************************/
+
+BOOL printer_driver_files_in_use ( NT_PRINTER_DRIVER_INFO_LEVEL_3 *info )
+{
+ int i;
+ int ndrivers;
+ uint32 version;
+ fstring *list = NULL;
+ NT_PRINTER_DRIVER_INFO_LEVEL driver;
+
+ if ( !info )
+ return False;
+
+ version = info->cversion;
+
+ /* loop over all driver versions */
+
+ DEBUG(5,("printer_driver_files_in_use: Beginning search through ntdrivers.tdb...\n"));
+
+ /* get the list of drivers */
+
+ list = NULL;
+ ndrivers = get_ntdrivers(&list, info->environment, version);
+
+ DEBUGADD(4,("we have:[%d] drivers in environment [%s] and version [%d]\n",
+ ndrivers, info->environment, version));
+
+ /* check each driver for overlap in files */
+
+ for (i=0; i<ndrivers; i++) {
+ DEBUGADD(5,("\tdriver: [%s]\n", list[i]));
+
+ ZERO_STRUCT(driver);
+
+ if ( !W_ERROR_IS_OK(get_a_printer_driver(&driver, 3, list[i], info->environment, version)) ) {
+ SAFE_FREE(list);
+ return True;
+ }
+
+ /* check if d2 uses any files from d1 */
+ /* only if this is a different driver than the one being deleted */
+
+ if ( !strequal(info->name, driver.info_3->name) ) {
+ if ( trim_overlap_drv_files(info, driver.info_3) ) {
+ free_a_printer_driver(driver, 3);
+ SAFE_FREE( list );
+ return True;
+ }
+ }
+
+ free_a_printer_driver(driver, 3);
+ }
+
+ SAFE_FREE(list);
+
+ DEBUG(5,("printer_driver_files_in_use: Completed search through ntdrivers.tdb...\n"));
+
+ driver.info_3 = info;
+
+ if ( DEBUGLEVEL >= 20 )
+ dump_a_printer_driver( driver, 3 );
+
+ return False;
+}
+
+/****************************************************************************
+ Actually delete the driver files. Make sure that
+ printer_driver_files_in_use() return False before calling
+ this.
+****************************************************************************/
+
+static BOOL delete_driver_files( NT_PRINTER_DRIVER_INFO_LEVEL_3 *info_3, struct current_user *user )
+{
+ int i = 0;
+ char *s;
+ connection_struct *conn;
+ DATA_BLOB null_pw;
+ NTSTATUS nt_status;
+ fstring res_type;
+
+ if ( !info_3 )
+ return False;
+
+ DEBUG(6,("delete_driver_files: deleting driver [%s] - version [%d]\n", info_3->name, info_3->cversion));
+
+ /*
+ * Connect to the print$ share under the same account as the
+ * user connected to the rpc pipe. Note we must be root to
+ * do this.
+ */
+
+ null_pw = data_blob( NULL, 0 );
+ fstrcpy(res_type, "A:");
+ become_root();
+ conn = make_connection_with_chdir( "print$", null_pw, res_type, user->vuid, &nt_status );
+ unbecome_root();
+
+ if ( !conn ) {
+ DEBUG(0,("delete_driver_files: Unable to connect\n"));
+ return False;
+ }
+
+ /* Save who we are - we are temporarily becoming the connection user. */
+
+ if ( !become_user(conn, conn->vuid) ) {
+ DEBUG(0,("delete_driver_files: Can't become user!\n"));
+ return False;
+ }
+
+ /* now delete the files; must strip the '\print$' string from
+ fron of path */
+
+ if ( *info_3->driverpath ) {
+ if ( (s = strchr( &info_3->driverpath[1], '\\' )) != NULL ) {
+ DEBUG(10,("deleting driverfile [%s]\n", s));
+ unlink_internals(conn, 0, s);
+ }
+ }
+
+ if ( *info_3->configfile ) {
+ if ( (s = strchr( &info_3->configfile[1], '\\' )) != NULL ) {
+ DEBUG(10,("deleting configfile [%s]\n", s));
+ unlink_internals(conn, 0, s);
+ }
+ }
+
+ if ( *info_3->datafile ) {
+ if ( (s = strchr( &info_3->datafile[1], '\\' )) != NULL ) {
+ DEBUG(10,("deleting datafile [%s]\n", s));
+ unlink_internals(conn, 0, s);
+ }
+ }
+
+ if ( *info_3->helpfile ) {
+ if ( (s = strchr( &info_3->helpfile[1], '\\' )) != NULL ) {
+ DEBUG(10,("deleting helpfile [%s]\n", s));
+ unlink_internals(conn, 0, s);
+ }
+ }
+
+ /* check if we are done removing files */
+
+ if ( info_3->dependentfiles ) {
+ while ( *info_3->dependentfiles[i] ) {
+ char *file;
+
+ /* bypass the "\print$" portion of the path */
+
+ if ( (file = strchr( info_3->dependentfiles[i]+1, '\\' )) != NULL ) {
+ DEBUG(10,("deleting dependent file [%s]\n", file));
+ unlink_internals(conn, 0, file );
+ }
+
+ i++;
+ }
+ }
+
+ unbecome_user();
+
+ return True;
+}
+
+/****************************************************************************
+ Remove a printer driver from the TDB. This assumes that the the driver was
+ previously looked up.
+ ***************************************************************************/
+
+WERROR delete_printer_driver( NT_PRINTER_DRIVER_INFO_LEVEL_3 *info_3, struct current_user *user,
+ uint32 version, BOOL delete_files )
+{
+ pstring key;
+ const char *arch;
+ TDB_DATA kbuf, dbuf;
+ NT_PRINTER_DRIVER_INFO_LEVEL ctr;
+
+ /* delete the tdb data first */
+
+ arch = get_short_archi(info_3->environment);
+ slprintf(key, sizeof(key)-1, "%s%s/%d/%s", DRIVERS_PREFIX,
+ arch, version, info_3->name);
+
+ DEBUG(5,("delete_printer_driver: key = [%s] delete_files = %s\n",
+ key, delete_files ? "TRUE" : "FALSE" ));
+
+ ctr.info_3 = info_3;
+ dump_a_printer_driver( ctr, 3 );
+
+ kbuf.dptr=key;
+ kbuf.dsize=strlen(key)+1;
+
+ /* check if the driver actually exists for this environment */
+
+ dbuf = tdb_fetch( tdb_drivers, kbuf );
+ if ( !dbuf.dptr ) {
+ DEBUG(8,("delete_printer_driver: Driver unknown [%s]\n", key));
+ return WERR_UNKNOWN_PRINTER_DRIVER;
+ }
+
+ SAFE_FREE( dbuf.dptr );
+
+ /* ok... the driver exists so the delete should return success */
+
+ if (tdb_delete(tdb_drivers, kbuf) == -1) {
+ DEBUG (0,("delete_printer_driver: fail to delete %s!\n", key));
+ return WERR_ACCESS_DENIED;
+ }
+
+ /*
+ * now delete any associated files if delete_files == True
+ * even if this part failes, we return succes because the
+ * driver doesn not exist any more
+ */
+
+ if ( delete_files )
+ delete_driver_files( info_3, user );
+
+
+ DEBUG(5,("delete_printer_driver: driver delete successful [%s]\n", key));
+
+ return WERR_OK;
+ }
+
+/****************************************************************************
+ Store a security desc for a printer.
+****************************************************************************/
+
+WERROR nt_printing_setsec(const char *printername, SEC_DESC_BUF *secdesc_ctr)
+{
+ SEC_DESC_BUF *new_secdesc_ctr = NULL;
+ SEC_DESC_BUF *old_secdesc_ctr = NULL;
+ prs_struct ps;
+ TALLOC_CTX *mem_ctx = NULL;
+ fstring key;
+ WERROR status;
+
+ mem_ctx = talloc_init("nt_printing_setsec");
+ if (mem_ctx == NULL)
+ return WERR_NOMEM;
+
+ /* The old owner and group sids of the security descriptor are not
+ present when new ACEs are added or removed by changing printer
+ permissions through NT. If they are NULL in the new security
+ descriptor then copy them over from the old one. */
+
+ if (!secdesc_ctr->sec->owner_sid || !secdesc_ctr->sec->grp_sid) {
+ DOM_SID *owner_sid, *group_sid;
+ SEC_ACL *dacl, *sacl;
+ SEC_DESC *psd = NULL;
+ size_t size;
+
+ nt_printing_getsec(mem_ctx, printername, &old_secdesc_ctr);
+
+ /* Pick out correct owner and group sids */
+
+ owner_sid = secdesc_ctr->sec->owner_sid ?
+ secdesc_ctr->sec->owner_sid :
+ old_secdesc_ctr->sec->owner_sid;
+
+ group_sid = secdesc_ctr->sec->grp_sid ?
+ secdesc_ctr->sec->grp_sid :
+ old_secdesc_ctr->sec->grp_sid;
+
+ dacl = secdesc_ctr->sec->dacl ?
+ secdesc_ctr->sec->dacl :
+ old_secdesc_ctr->sec->dacl;
+
+ sacl = secdesc_ctr->sec->sacl ?
+ secdesc_ctr->sec->sacl :
+ old_secdesc_ctr->sec->sacl;
+
+ /* Make a deep copy of the security descriptor */
+
+ psd = make_sec_desc(mem_ctx, secdesc_ctr->sec->revision, secdesc_ctr->sec->type,
+ owner_sid, group_sid,
+ sacl,
+ dacl,
+ &size);
+
+ new_secdesc_ctr = make_sec_desc_buf(mem_ctx, size, psd);
+ }
+
+ if (!new_secdesc_ctr) {
+ new_secdesc_ctr = secdesc_ctr;
+ }
+
+ /* Store the security descriptor in a tdb */
+
+ prs_init(&ps, (uint32)sec_desc_size(new_secdesc_ctr->sec) +
+ sizeof(SEC_DESC_BUF), mem_ctx, MARSHALL);
+
+ if (!sec_io_desc_buf("nt_printing_setsec", &new_secdesc_ctr,
+ &ps, 1)) {
+ status = WERR_BADFUNC;
+ goto out;
+ }
+
+ slprintf(key, sizeof(key)-1, "SECDESC/%s", printername);
+
+ if (tdb_prs_store(tdb_printers, key, &ps)==0) {
+ status = WERR_OK;
+ } else {
+ DEBUG(1,("Failed to store secdesc for %s\n", printername));
+ status = WERR_BADFUNC;
+ }
+
+ /* Free malloc'ed memory */
+
+ out:
+
+ prs_mem_free(&ps);
+ if (mem_ctx)
+ talloc_destroy(mem_ctx);
+ return status;
+}
+
+/****************************************************************************
+ Construct a default security descriptor buffer for a printer.
+****************************************************************************/
+
+static SEC_DESC_BUF *construct_default_printer_sdb(TALLOC_CTX *ctx)
+{
+ SEC_ACE ace[3];
+ SEC_ACCESS sa;
+ SEC_ACL *psa = NULL;
+ SEC_DESC_BUF *sdb = NULL;
+ SEC_DESC *psd = NULL;
+ DOM_SID owner_sid;
+ size_t sd_size;
+
+ /* Create an ACE where Everyone is allowed to print */
+
+ init_sec_access(&sa, PRINTER_ACE_PRINT);
+ init_sec_ace(&ace[0], &global_sid_World, SEC_ACE_TYPE_ACCESS_ALLOWED,
+ sa, SEC_ACE_FLAG_CONTAINER_INHERIT);
+
+ /* Make the security descriptor owned by the Administrators group
+ on the PDC of the domain. */
+
+ if (secrets_fetch_domain_sid(lp_workgroup(), &owner_sid)) {
+ sid_append_rid(&owner_sid, DOMAIN_USER_RID_ADMIN);
+ } else {
+
+ /* Backup plan - make printer owned by admins.
+ This should emulate a lanman printer as security
+ settings can't be changed. */
+
+ sid_copy(&owner_sid, get_global_sam_sid());
+ sid_append_rid(&owner_sid, DOMAIN_USER_RID_ADMIN);
+ }
+
+ init_sec_access(&sa, PRINTER_ACE_FULL_CONTROL);
+ init_sec_ace(&ace[1], &owner_sid, SEC_ACE_TYPE_ACCESS_ALLOWED,
+ sa, SEC_ACE_FLAG_OBJECT_INHERIT |
+ SEC_ACE_FLAG_INHERIT_ONLY);
+
+ init_sec_access(&sa, PRINTER_ACE_FULL_CONTROL);
+ init_sec_ace(&ace[2], &owner_sid, SEC_ACE_TYPE_ACCESS_ALLOWED,
+ sa, SEC_ACE_FLAG_CONTAINER_INHERIT);
+
+ /* The ACL revision number in rpc_secdesc.h differs from the one
+ created by NT when setting ACE entries in printer
+ descriptors. NT4 complains about the property being edited by a
+ NT5 machine. */
+
+ if ((psa = make_sec_acl(ctx, NT4_ACL_REVISION, 3, ace)) != NULL) {
+ psd = make_sec_desc(ctx, SEC_DESC_REVISION, SEC_DESC_SELF_RELATIVE,
+ &owner_sid, NULL,
+ NULL, psa, &sd_size);
+ }
+
+ if (!psd) {
+ DEBUG(0,("construct_default_printer_sd: Failed to make SEC_DESC.\n"));
+ return NULL;
+ }
+
+ sdb = make_sec_desc_buf(ctx, sd_size, psd);
+
+ DEBUG(4,("construct_default_printer_sdb: size = %u.\n",
+ (unsigned int)sd_size));
+
+ return sdb;
+}
+
+/****************************************************************************
+ Get a security desc for a printer.
+****************************************************************************/
+
+BOOL nt_printing_getsec(TALLOC_CTX *ctx, const char *printername, SEC_DESC_BUF **secdesc_ctr)
+{
+ prs_struct ps;
+ fstring key;
+ char *temp;
+
+ if (strlen(printername) > 2 && (temp = strchr(printername + 2, '\\'))) {
+ printername = temp + 1;
+ }
+
+ /* Fetch security descriptor from tdb */
+
+ slprintf(key, sizeof(key)-1, "SECDESC/%s", printername);
+
+ if (tdb_prs_fetch(tdb_printers, key, &ps, ctx)!=0 ||
+ !sec_io_desc_buf("nt_printing_getsec", secdesc_ctr, &ps, 1)) {
+
+ DEBUG(4,("using default secdesc for %s\n", printername));
+
+ if (!(*secdesc_ctr = construct_default_printer_sdb(ctx))) {
+ return False;
+ }
+
+ /* Save default security descriptor for later */
+
+ prs_init(&ps, (uint32)sec_desc_size((*secdesc_ctr)->sec) +
+ sizeof(SEC_DESC_BUF), ctx, MARSHALL);
+
+ if (sec_io_desc_buf("nt_printing_getsec", secdesc_ctr, &ps, 1))
+ tdb_prs_store(tdb_printers, key, &ps);
+
+ prs_mem_free(&ps);
+
+ return True;
+ }
+
+ /* If security descriptor is owned by S-1-1-0 and winbindd is up,
+ this security descriptor has been created when winbindd was
+ down. Take ownership of security descriptor. */
+
+ if (sid_equal((*secdesc_ctr)->sec->owner_sid, &global_sid_World)) {
+ DOM_SID owner_sid;
+
+ /* Change sd owner to workgroup administrator */
+
+ if (secrets_fetch_domain_sid(lp_workgroup(), &owner_sid)) {
+ SEC_DESC_BUF *new_secdesc_ctr = NULL;
+ SEC_DESC *psd = NULL;
+ size_t size;
+
+ /* Create new sd */
+
+ sid_append_rid(&owner_sid, DOMAIN_USER_RID_ADMIN);
+
+ psd = make_sec_desc(ctx, (*secdesc_ctr)->sec->revision, (*secdesc_ctr)->sec->type,
+ &owner_sid,
+ (*secdesc_ctr)->sec->grp_sid,
+ (*secdesc_ctr)->sec->sacl,
+ (*secdesc_ctr)->sec->dacl,
+ &size);
+
+ new_secdesc_ctr = make_sec_desc_buf(ctx, size, psd);
+
+ /* Swap with other one */
+
+ *secdesc_ctr = new_secdesc_ctr;
+
+ /* Set it */
+
+ nt_printing_setsec(printername, *secdesc_ctr);
+ }
+ }
+
+ if (DEBUGLEVEL >= 10) {
+ SEC_ACL *the_acl = (*secdesc_ctr)->sec->dacl;
+ int i;
+
+ DEBUG(10, ("secdesc_ctr for %s has %d aces:\n",
+ printername, the_acl->num_aces));
+
+ for (i = 0; i < the_acl->num_aces; i++) {
+ fstring sid_str;
+
+ sid_to_string(sid_str, &the_acl->ace[i].trustee);
+
+ DEBUG(10, ("%s %d %d 0x%08x\n", sid_str,
+ the_acl->ace[i].type, the_acl->ace[i].flags,
+ the_acl->ace[i].info.mask));
+ }
+ }
+
+ prs_mem_free(&ps);
+ return True;
+}
+
+/* error code:
+ 0: everything OK
+ 1: level not implemented
+ 2: file doesn't exist
+ 3: can't allocate memory
+ 4: can't free memory
+ 5: non existant struct
+*/
+
+/*
+ A printer and a printer driver are 2 different things.
+ NT manages them separatelly, Samba does the same.
+ Why ? Simply because it's easier and it makes sense !
+
+ Now explanation: You have 3 printers behind your samba server,
+ 2 of them are the same make and model (laser A and B). But laser B
+ has an 3000 sheet feeder and laser A doesn't such an option.
+ Your third printer is an old dot-matrix model for the accounting :-).
+
+ If the /usr/local/samba/lib directory (default dir), you will have
+ 5 files to describe all of this.
+
+ 3 files for the printers (1 by printer):
+ NTprinter_laser A
+ NTprinter_laser B
+ NTprinter_accounting
+ 2 files for the drivers (1 for the laser and 1 for the dot matrix)
+ NTdriver_printer model X
+ NTdriver_printer model Y
+
+jfm: I should use this comment for the text file to explain
+ same thing for the forms BTW.
+ Je devrais mettre mes commentaires en francais, ca serait mieux :-)
+
+*/
+
+/* Convert generic access rights to printer object specific access rights.
+ It turns out that NT4 security descriptors use generic access rights and
+ NT5 the object specific ones. */
+
+void map_printer_permissions(SEC_DESC *sd)
+{
+ int i;
+
+ for (i = 0; sd->dacl && i < sd->dacl->num_aces; i++) {
+ se_map_generic(&sd->dacl->ace[i].info.mask,
+ &printer_generic_mapping);
+ }
+}
+
+/****************************************************************************
+ Check a user has permissions to perform the given operation. We use the
+ permission constants defined in include/rpc_spoolss.h to check the various
+ actions we perform when checking printer access.
+
+ PRINTER_ACCESS_ADMINISTER:
+ print_queue_pause, print_queue_resume, update_printer_sec,
+ update_printer, spoolss_addprinterex_level_2,
+ _spoolss_setprinterdata
+
+ PRINTER_ACCESS_USE:
+ print_job_start
+
+ JOB_ACCESS_ADMINISTER:
+ print_job_delete, print_job_pause, print_job_resume,
+ print_queue_purge
+
+ ****************************************************************************/
+BOOL print_access_check(struct current_user *user, int snum, int access_type)
+{
+ SEC_DESC_BUF *secdesc = NULL;
+ uint32 access_granted;
+ NTSTATUS status;
+ BOOL result;
+ const char *pname;
+ TALLOC_CTX *mem_ctx = NULL;
+ extern struct current_user current_user;
+
+ /* If user is NULL then use the current_user structure */
+
+ if (!user)
+ user = &current_user;
+
+ /* Always allow root or printer admins to do anything */
+
+ if (user->uid == 0 ||
+ user_in_list(uidtoname(user->uid), lp_printer_admin(snum), user->groups, user->ngroups)) {
+ return True;
+ }
+
+ /* Get printer name */
+
+ pname = PRINTERNAME(snum);
+
+ if (!pname || !*pname) {
+ errno = EACCES;
+ return False;
+ }
+
+ /* Get printer security descriptor */
+
+ if(!(mem_ctx = talloc_init("print_access_check"))) {
+ errno = ENOMEM;
+ return False;
+ }
+
+ nt_printing_getsec(mem_ctx, pname, &secdesc);
+
+ if (access_type == JOB_ACCESS_ADMINISTER) {
+ SEC_DESC_BUF *parent_secdesc = secdesc;
+
+ /* Create a child security descriptor to check permissions
+ against. This is because print jobs are child objects
+ objects of a printer. */
+
+ secdesc = se_create_child_secdesc(mem_ctx, parent_secdesc->sec, False);
+
+ /* Now this is the bit that really confuses me. The access
+ type needs to be changed from JOB_ACCESS_ADMINISTER to
+ PRINTER_ACCESS_ADMINISTER for this to work. Something
+ to do with the child (job) object becoming like a
+ printer?? -tpot */
+
+ access_type = PRINTER_ACCESS_ADMINISTER;
+ }
+
+ /* Check access */
+
+ map_printer_permissions(secdesc->sec);
+
+ result = se_access_check(secdesc->sec, user->nt_user_token, access_type,
+ &access_granted, &status);
+
+ DEBUG(4, ("access check was %s\n", result ? "SUCCESS" : "FAILURE"));
+
+ talloc_destroy(mem_ctx);
+
+ if (!result)
+ errno = EACCES;
+
+ return result;
+}
+
+/****************************************************************************
+ Check the time parameters allow a print operation.
+*****************************************************************************/
+
+BOOL print_time_access_check(int snum)
+{
+ NT_PRINTER_INFO_LEVEL *printer = NULL;
+ BOOL ok = False;
+ time_t now = time(NULL);
+ struct tm *t;
+ uint32 mins;
+
+ if (!W_ERROR_IS_OK(get_a_printer(NULL, &printer, 2, lp_servicename(snum))))
+ return False;
+
+ if (printer->info_2->starttime == 0 && printer->info_2->untiltime == 0)
+ ok = True;
+
+ t = gmtime(&now);
+ mins = (uint32)t->tm_hour*60 + (uint32)t->tm_min;
+
+ if (mins >= printer->info_2->starttime && mins <= printer->info_2->untiltime)
+ ok = True;
+
+ free_a_printer(&printer, 2);
+
+ if (!ok)
+ errno = EACCES;
+
+ return ok;
+}
+
diff --git a/source/printing/pcap.c b/source/printing/pcap.c
new file mode 100644
index 00000000000..a5fb53a320d
--- /dev/null
+++ b/source/printing/pcap.c
@@ -0,0 +1,413 @@
+/*
+ Unix SMB/CIFS implementation.
+ printcap parsing
+ Copyright (C) Karl Auer 1993-1998
+
+ Re-working by Martin Kiff, 1994
+
+ Re-written again by Andrew Tridgell
+
+ Modified for SVID support by Norm Jacobs, 1997
+
+ Modified for CUPS support by Michael Sweet, 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 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.
+*/
+
+/*
+ * Parse printcap file.
+ *
+ * This module does exactly one thing - it looks into the printcap file
+ * and tells callers if a specified string appears as a printer name.
+ *
+ * The way this module looks at the printcap file is very simplistic.
+ * Only the local printcap file is inspected (no searching of NIS
+ * databases etc).
+ *
+ * There are assumed to be one or more printer names per record, held
+ * as a set of sub-fields separated by vertical bar symbols ('|') in the
+ * first field of the record. The field separator is assumed to be a colon
+ * ':' and the record separator a newline.
+ *
+ * Lines ending with a backspace '\' are assumed to flag that the following
+ * line is a continuation line so that a set of lines can be read as one
+ * printcap entry.
+ *
+ * A line stating with a hash '#' is assumed to be a comment and is ignored
+ * Comments are discarded before the record is strung together from the
+ * set of continuation lines.
+ *
+ * Opening a pipe for "lpc status" and reading that would probably
+ * be pretty effective. Code to do this already exists in the freely
+ * distributable PCNFS server code.
+ *
+ * Modified to call SVID/XPG4 support if printcap name is set to "lpstat"
+ * in smb.conf under Solaris.
+ *
+ * Modified to call CUPS support if printcap name is set to "cups"
+ * in smb.conf.
+ */
+
+#include "includes.h"
+
+#ifdef AIX
+/* ******************************************
+ Extend for AIX system and qconfig file
+ from 'boulard@univ-rennes1.fr
+ ****************************************** */
+static int strlocate(char *xpLine,char *xpS)
+{
+ int iS,iL,iRet;
+ char *p;
+ iS = strlen(xpS);
+ iL = strlen(xpLine);
+
+ iRet = 0;
+ p = xpLine;
+ while (iL >= iS)
+ {
+ if (strncmp(p,xpS,iS) == 0) {iRet =1;break;};
+ p++;
+ iL--;
+ }
+ /*DEBUG(3,(" strlocate %s in line '%s',ret=%d\n",xpS,xpLine,iRet));*/
+
+ return(iRet);
+}
+
+
+/* ******************************************************************* */
+/* * Scan qconfig and search all virtual printer (device printer) * */
+/* ******************************************************************* */
+static void ScanQconfig_fn(char *psz,void (*fn)(char *, char *))
+{
+ int iEtat;
+ XFILE *pfile;
+ char *line,*p;
+ pstring name,comment;
+ line = NULL;
+ *name = 0;
+ *comment = 0;
+
+ if ((pfile = x_fopen(psz, O_RDONLY, 0)) == NULL)
+ {
+ DEBUG(0,( "Unable to open qconfig file %s for read!\n", psz));
+ return;
+ }
+
+ iEtat = 0;
+ /* scan qconfig file for searching <printername>: */
+ for (;(line = fgets_slash(NULL,sizeof(pstring),pfile)); safe_free(line))
+ {
+ if (*line == '*' || *line == 0)
+ continue;
+ switch (iEtat)
+ {
+ case 0: /* locate an entry */
+ if (*line == '\t' || *line == ' ') continue;
+ if ((p=strchr_m(line,':')))
+ {
+ *p = '\0';
+ p = strtok(line,":");
+ if (strcmp(p,"bsh")!=0)
+ {
+ pstrcpy(name,p);
+ iEtat = 1;
+ continue;
+ }
+ }
+ break;
+ case 1: /* scanning device stanza */
+ if (*line == '*' || *line == 0) continue;
+ if (*line != '\t' && *line != ' ')
+ {
+ /* name is found without stanza device */
+ /* probably a good printer ??? */
+ fn(name,comment);
+ iEtat = 0;
+ continue;
+ }
+
+ if (strlocate(line,"backend"))
+ {
+ /* it's a device, not a virtual printer*/
+ iEtat = 0;
+ }
+ else if (strlocate(line,"device"))
+ {
+ /* it's a good virtual printer */
+ fn(name,comment);
+ iEtat = 0;
+ continue;
+ }
+ break;
+ }
+ }
+ x_fclose(pfile);
+}
+
+/* Scan qconfig file and locate de printername */
+
+static BOOL ScanQconfig(char *psz,char *pszPrintername)
+{
+ int iLg,iEtat;
+ XFILE *pfile;
+ char *pName;
+ char *line;
+
+ pName = NULL;
+ line = NULL;
+ if ((pszPrintername!= NULL) && ((iLg = strlen(pszPrintername)) > 0))
+ pName = malloc(iLg+10);
+ if (pName == NULL)
+ {
+ DEBUG(0,(" Unable to allocate memory for printer %s\n",pszPrintername));
+ return(False);
+ }
+ if ((pfile = x_fopen(psz, O_RDONLY, 0)) == NULL)
+ {
+ DEBUG(0,( "Unable to open qconfig file %s for read!\n", psz));
+ SAFE_FREE(pName);
+ return(False);
+ }
+ slprintf(pName, iLg + 9, "%s:",pszPrintername);
+ iLg = strlen(pName);
+ /*DEBUG(3,( " Looking for entry %s\n",pName));*/
+ iEtat = 0;
+ /* scan qconfig file for searching <printername>: */
+ for (;(line = fgets_slash(NULL,sizeof(pstring),pfile)); safe_free(line))
+ {
+ if (*line == '*' || *line == 0)
+ continue;
+ switch (iEtat)
+ {
+ case 0: /* scanning entry */
+ if (strncmp(line,pName,iLg) == 0)
+ {
+ iEtat = 1;
+ continue;
+ }
+ break;
+ case 1: /* scanning device stanza */
+ if (*line == '*' || *line == 0) continue;
+ if (*line != '\t' && *line != ' ')
+ {
+ /* name is found without stanza device */
+ /* probably a good printer ??? */
+ free (line);
+ SAFE_FREE(pName);
+ x_fclose(pfile);
+ return(True);
+ }
+
+ if (strlocate(line,"backend"))
+ {
+ /* it's a device, not a virtual printer*/
+ iEtat = 0;
+ }
+ else if (strlocate(line,"device"))
+ {
+ /* it's a good virtual printer */
+ free (line);
+ SAFE_FREE(pName);
+ x_fclose(pfile);
+ return(True);
+ }
+ break;
+ }
+ }
+ free (pName);
+ x_fclose(pfile);
+ return(False);
+}
+#endif /* AIX */
+
+
+/***************************************************************************
+Scan printcap file pszPrintcapname for a printer called pszPrintername.
+Return True if found, else False. Returns False on error, too, after logging
+the error at level 0. For generality, the printcap name may be passed - if
+passed as NULL, the configuration will be queried for the name.
+***************************************************************************/
+BOOL pcap_printername_ok(const char *pszPrintername, const char *pszPrintcapname)
+{
+ char *line=NULL;
+ const char *psz;
+ char *p,*q;
+ XFILE *pfile;
+
+ if (pszPrintername == NULL || pszPrintername[0] == '\0')
+ {
+ DEBUG(0,( "Attempt to locate null printername! Internal error?\n"));
+ return(False);
+ }
+
+ /* only go looking if no printcap name supplied */
+ if ((psz = pszPrintcapname) == NULL || psz[0] == '\0')
+ if (((psz = lp_printcapname()) == NULL) || (psz[0] == '\0'))
+ {
+ DEBUG(0,( "No printcap file name configured!\n"));
+ return(False);
+ }
+
+#ifdef HAVE_CUPS
+ if (strequal(psz, "cups"))
+ return (cups_printername_ok(pszPrintername));
+#endif /* HAVE_CUPS */
+
+#ifdef SYSV
+ if (strequal(psz, "lpstat"))
+ return (sysv_printername_ok(pszPrintername));
+#endif
+
+#ifdef AIX
+ if (strlocate(psz,"/qconfig"))
+ return(ScanQconfig(psz,pszPrintername));
+#endif
+
+ if ((pfile = x_fopen(psz, O_RDONLY, 0)) == NULL)
+ {
+ DEBUG(0,( "Unable to open printcap file %s for read!\n", psz));
+ return(False);
+ }
+
+ for (;(line = fgets_slash(NULL,sizeof(pstring),pfile)); safe_free(line))
+ {
+ if (*line == '#' || *line == 0)
+ continue;
+
+ /* now we have a real printer line - cut it off at the first : */
+ p = strchr_m(line,':');
+ if (p) *p = 0;
+
+ /* now just check if the name is in the list */
+ /* NOTE: I avoid strtok as the fn calling this one may be using it */
+ for (p=line; p; p=q)
+ {
+ if ((q = strchr_m(p,'|'))) *q++ = 0;
+
+ if (strequal(p,pszPrintername))
+ {
+ SAFE_FREE(line);
+ x_fclose(pfile);
+ return(True);
+ }
+ p = q;
+ }
+ }
+
+ x_fclose(pfile);
+ return(False);
+}
+
+
+/***************************************************************************
+run a function on each printer name in the printcap file. The function is
+passed the primary name and the comment (if possible). Note the fn() takes
+strings in DOS codepage. This means the xxx_printer_fn() calls must be fixed
+to return DOS codepage. FIXME !! JRA.
+***************************************************************************/
+void pcap_printer_fn(void (*fn)(char *, char *))
+{
+ pstring name,comment;
+ char *line;
+ char *psz;
+ char *p,*q;
+ XFILE *pfile;
+
+ /* only go looking if no printcap name supplied */
+ if (((psz = lp_printcapname()) == NULL) || (psz[0] == '\0'))
+ {
+ DEBUG(0,( "No printcap file name configured!\n"));
+ return;
+ }
+
+#ifdef HAVE_CUPS
+ if (strequal(psz, "cups")) {
+ cups_printer_fn(fn);
+ return;
+ }
+#endif /* HAVE_CUPS */
+
+#ifdef SYSV
+ if (strequal(psz, "lpstat")) {
+ sysv_printer_fn(fn);
+ return;
+ }
+#endif
+
+#ifdef AIX
+ if (strlocate(psz,"/qconfig"))
+ {
+ ScanQconfig_fn(psz,fn);
+ return;
+ }
+#endif
+
+ if ((pfile = x_fopen(psz, O_RDONLY, 0)) == NULL)
+ {
+ DEBUG(0,( "Unable to open printcap file %s for read!\n", psz));
+ return;
+ }
+
+ for (;(line = fgets_slash(NULL,sizeof(pstring),pfile)); safe_free(line))
+ {
+ if (*line == '#' || *line == 0)
+ continue;
+
+ /* now we have a real printer line - cut it off at the first : */
+ p = strchr_m(line,':');
+ if (p) *p = 0;
+
+ /* now find the most likely printer name and comment
+ this is pure guesswork, but it's better than nothing */
+ *name = 0;
+ *comment = 0;
+ for (p=line; p; p=q)
+ {
+ BOOL has_punctuation;
+ if ((q = strchr_m(p,'|'))) *q++ = 0;
+
+ has_punctuation = (strchr_m(p,' ') || strchr_m(p,'\t') || strchr_m(p,'(') || strchr_m(p,')'));
+
+ if (strlen(p)>strlen(comment) && has_punctuation)
+ {
+ pstrcpy(comment,p);
+ continue;
+ }
+
+ if (strlen(p) <= MAXPRINTERLEN && strlen(p)>strlen(name) && !has_punctuation)
+ {
+ if (!*comment) pstrcpy(comment,name);
+ pstrcpy(name,p);
+ continue;
+ }
+
+ if (!strchr_m(comment,' ') &&
+ strlen(p) > strlen(comment))
+ {
+ pstrcpy(comment,p);
+ continue;
+ }
+ }
+
+ comment[60] = 0;
+ name[MAXPRINTERLEN] = 0;
+
+ if (*name)
+ fn(name,comment);
+ }
+ x_fclose(pfile);
+}
diff --git a/source/printing/print_cups.c b/source/printing/print_cups.c
new file mode 100644
index 00000000000..f0096a17c2c
--- /dev/null
+++ b/source/printing/print_cups.c
@@ -0,0 +1,1303 @@
+/*
+ * Support code for the Common UNIX Printing System ("CUPS")
+ *
+ * Copyright 1999-2003 by Michael R Sweet.
+ *
+ * This program is free software; you can 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 "printing.h"
+
+#ifdef HAVE_CUPS
+#include <cups/cups.h>
+#include <cups/language.h>
+
+
+/*
+ * CUPS printing interface definitions...
+ */
+
+static int cups_job_delete(int snum, struct printjob *pjob);
+static int cups_job_pause(int snum, struct printjob *pjob);
+static int cups_job_resume(int snum, struct printjob *pjob);
+static int cups_job_submit(int snum, struct printjob *pjob);
+static int cups_queue_get(int snum, print_queue_struct **q,
+ print_status_struct *status);
+static int cups_queue_pause(int snum);
+static int cups_queue_resume(int snum);
+
+
+struct printif cups_printif =
+ {
+ cups_queue_get,
+ cups_queue_pause,
+ cups_queue_resume,
+ cups_job_delete,
+ cups_job_pause,
+ cups_job_resume,
+ cups_job_submit,
+ };
+
+/*
+ * 'cups_passwd_cb()' - The CUPS password callback...
+ */
+
+static const char * /* O - Password or NULL */
+cups_passwd_cb(const char *prompt) /* I - Prompt */
+{
+ /*
+ * Always return NULL to indicate that no password is available...
+ */
+
+ return (NULL);
+}
+
+
+/*
+ * 'cups_printer_fn()' - Call a function for every printer known to the
+ * system.
+ */
+
+void cups_printer_fn(void (*fn)(char *, char *))
+{
+ /* I - Function to call */
+ http_t *http; /* HTTP connection to server */
+ ipp_t *request, /* IPP Request */
+ *response; /* IPP Response */
+ ipp_attribute_t *attr; /* Current attribute */
+ cups_lang_t *language; /* Default language */
+ char *name, /* printer-name attribute */
+ *make_model, /* printer-make-and-model attribute */
+ *info; /* printer-info attribute */
+ static const char *requested[] =/* Requested attributes */
+ {
+ "printer-name",
+ "printer-make-and-model",
+ "printer-info"
+ };
+
+
+ DEBUG(5,("cups_printer_fn(%p)\n", fn));
+
+ /*
+ * Make sure we don't ask for passwords...
+ */
+
+ cupsSetPasswordCB(cups_passwd_cb);
+
+ /*
+ * Try to connect to the server...
+ */
+
+ if ((http = httpConnect(cupsServer(), ippPort())) == NULL)
+ {
+ DEBUG(0,("Unable to connect to CUPS server %s - %s\n",
+ cupsServer(), strerror(errno)));
+ return;
+ }
+
+ /*
+ * Build a CUPS_GET_PRINTERS request, which requires the following
+ * attributes:
+ *
+ * attributes-charset
+ * attributes-natural-language
+ * requested-attributes
+ */
+
+ request = ippNew();
+
+ request->request.op.operation_id = CUPS_GET_PRINTERS;
+ request->request.op.request_id = 1;
+
+ language = cupsLangDefault();
+
+ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_CHARSET,
+ "attributes-charset", NULL, cupsLangEncoding(language));
+
+ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_LANGUAGE,
+ "attributes-natural-language", NULL, language->language);
+
+ ippAddStrings(request, IPP_TAG_OPERATION, IPP_TAG_NAME,
+ "requested-attributes",
+ (sizeof(requested) / sizeof(requested[0])),
+ NULL, requested);
+
+ /*
+ * Do the request and get back a response...
+ */
+
+ if ((response = cupsDoRequest(http, request, "/")) == NULL)
+ {
+ DEBUG(0,("Unable to get printer list - %s\n",
+ ippErrorString(cupsLastError())));
+ httpClose(http);
+ return;
+ }
+
+ for (attr = response->attrs; attr != NULL;)
+ {
+ /*
+ * Skip leading attributes until we hit a printer...
+ */
+
+ while (attr != NULL && attr->group_tag != IPP_TAG_PRINTER)
+ attr = attr->next;
+
+ if (attr == NULL)
+ break;
+
+ /*
+ * Pull the needed attributes from this printer...
+ */
+
+ name = NULL;
+ make_model = NULL;
+ info = NULL;
+
+ while (attr != NULL && attr->group_tag == IPP_TAG_PRINTER)
+ {
+ if (strcmp(attr->name, "printer-name") == 0 &&
+ attr->value_tag == IPP_TAG_NAME)
+ name = attr->values[0].string.text;
+
+ if (strcmp(attr->name, "printer-make-and-model") == 0 &&
+ attr->value_tag == IPP_TAG_TEXT)
+ make_model = attr->values[0].string.text;
+
+ if (strcmp(attr->name, "printer-info") == 0 &&
+ attr->value_tag == IPP_TAG_TEXT)
+ info = attr->values[0].string.text;
+
+ attr = attr->next;
+ }
+
+ /*
+ * See if we have everything needed...
+ */
+
+ if (name == NULL)
+ break;
+
+ if (info == NULL || !info[0])
+ (*fn)(name, make_model);
+ else
+ (*fn)(name, info);
+
+
+ }
+
+ ippDelete(response);
+
+
+ /*
+ * Build a CUPS_GET_CLASSES request, which requires the following
+ * attributes:
+ *
+ * attributes-charset
+ * attributes-natural-language
+ * requested-attributes
+ */
+
+ request = ippNew();
+
+ request->request.op.operation_id = CUPS_GET_CLASSES;
+ request->request.op.request_id = 1;
+
+ language = cupsLangDefault();
+
+ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_CHARSET,
+ "attributes-charset", NULL, cupsLangEncoding(language));
+
+ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_LANGUAGE,
+ "attributes-natural-language", NULL, language->language);
+
+ ippAddStrings(request, IPP_TAG_OPERATION, IPP_TAG_NAME,
+ "requested-attributes",
+ (sizeof(requested) / sizeof(requested[0])),
+ NULL, requested);
+
+ /*
+ * Do the request and get back a response...
+ */
+
+ if ((response = cupsDoRequest(http, request, "/")) == NULL)
+ {
+ DEBUG(0,("Unable to get printer list - %s\n",
+ ippErrorString(cupsLastError())));
+ httpClose(http);
+ return;
+ }
+
+ for (attr = response->attrs; attr != NULL;)
+ {
+ /*
+ * Skip leading attributes until we hit a printer...
+ */
+
+ while (attr != NULL && attr->group_tag != IPP_TAG_PRINTER)
+ attr = attr->next;
+
+ if (attr == NULL)
+ break;
+
+ /*
+ * Pull the needed attributes from this printer...
+ */
+
+ name = NULL;
+ make_model = NULL;
+ info = NULL;
+
+ while (attr != NULL && attr->group_tag == IPP_TAG_PRINTER)
+ {
+ if (strcmp(attr->name, "printer-name") == 0 &&
+ attr->value_tag == IPP_TAG_NAME)
+ name = attr->values[0].string.text;
+
+ if (strcmp(attr->name, "printer-make-and-model") == 0 &&
+ attr->value_tag == IPP_TAG_TEXT)
+ make_model = attr->values[0].string.text;
+
+ if (strcmp(attr->name, "printer-info") == 0 &&
+ attr->value_tag == IPP_TAG_TEXT)
+ info = attr->values[0].string.text;
+
+ attr = attr->next;
+ }
+
+ /*
+ * See if we have everything needed...
+ */
+
+ if (name == NULL)
+ break;
+
+ if (info == NULL || !info[0])
+ (*fn)(name, make_model);
+ else
+ (*fn)(name, info);
+
+
+ }
+
+ ippDelete(response);
+
+ /*
+ * Close the connection to the server...
+ */
+
+ httpClose(http);
+}
+
+
+/*
+ * 'cups_printername_ok()' - Provide the equivalent of pcap_printername_ok()
+ * for CUPS.
+ * O - 1 if printer name OK
+ * I - Name of printer
+ */
+int cups_printername_ok(const char *name)
+{
+ http_t *http; /* HTTP connection to server */
+ ipp_t *request, /* IPP Request */
+ *response; /* IPP Response */
+ cups_lang_t *language; /* Default language */
+ char uri[HTTP_MAX_URI]; /* printer-uri attribute */
+
+
+ DEBUG(5,("cups_printername_ok(\"%s\")\n", name));
+
+ /*
+ * Make sure we don't ask for passwords...
+ */
+
+ cupsSetPasswordCB(cups_passwd_cb);
+
+ /*
+ * Try to connect to the server...
+ */
+
+ if ((http = httpConnect(cupsServer(), ippPort())) == NULL)
+ {
+ DEBUG(3,("Unable to connect to CUPS server %s - %s\n",
+ cupsServer(), strerror(errno)));
+ return (0);
+ }
+
+ /*
+ * Build an IPP_GET_PRINTER_ATTRS request, which requires the following
+ * attributes:
+ *
+ * attributes-charset
+ * attributes-natural-language
+ * requested-attributes
+ * printer-uri
+ */
+
+ request = ippNew();
+
+ request->request.op.operation_id = IPP_GET_PRINTER_ATTRIBUTES;
+ request->request.op.request_id = 1;
+
+ language = cupsLangDefault();
+
+ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_CHARSET,
+ "attributes-charset", NULL, cupsLangEncoding(language));
+
+ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_LANGUAGE,
+ "attributes-natural-language", NULL, language->language);
+
+ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME,
+ "requested-attributes", NULL, "printer-uri");
+
+ slprintf(uri, sizeof(uri) - 1, "ipp://localhost/printers/%s", name);
+
+ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI,
+ "printer-uri", NULL, uri);
+
+ /*
+ * Do the request and get back a response...
+ */
+
+ if ((response = cupsDoRequest(http, request, "/")) == NULL)
+ {
+ DEBUG(3,("Unable to get printer status for %s - %s\n", name,
+ ippErrorString(cupsLastError())));
+ httpClose(http);
+ return (0);
+ }
+
+ httpClose(http);
+
+ if (response->request.status.status_code >= IPP_OK_CONFLICT)
+ {
+ DEBUG(3,("Unable to get printer status for %s - %s\n", name,
+ ippErrorString(response->request.status.status_code)));
+ ippDelete(response);
+ return (0);
+ }
+ else
+ {
+ ippDelete(response);
+ return (1);
+ }
+}
+
+
+/*
+ * 'cups_job_delete()' - Delete a job.
+ */
+
+static int
+cups_job_delete(int snum, struct printjob *pjob)
+{
+ int ret; /* Return value */
+ http_t *http; /* HTTP connection to server */
+ ipp_t *request, /* IPP Request */
+ *response; /* IPP Response */
+ cups_lang_t *language; /* Default language */
+ char uri[HTTP_MAX_URI]; /* printer-uri attribute */
+
+
+ DEBUG(5,("cups_job_delete(%d, %p (%d))\n", snum, pjob, pjob->sysjob));
+
+ /*
+ * Make sure we don't ask for passwords...
+ */
+
+ cupsSetPasswordCB(cups_passwd_cb);
+
+ /*
+ * Try to connect to the server...
+ */
+
+ if ((http = httpConnect(cupsServer(), ippPort())) == NULL)
+ {
+ DEBUG(0,("Unable to connect to CUPS server %s - %s\n",
+ cupsServer(), strerror(errno)));
+ return (1);
+ }
+
+ /*
+ * Build an IPP_CANCEL_JOB request, which requires the following
+ * attributes:
+ *
+ * attributes-charset
+ * attributes-natural-language
+ * job-uri
+ * requesting-user-name
+ */
+
+ request = ippNew();
+
+ request->request.op.operation_id = IPP_CANCEL_JOB;
+ request->request.op.request_id = 1;
+
+ language = cupsLangDefault();
+
+ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_CHARSET,
+ "attributes-charset", NULL, cupsLangEncoding(language));
+
+ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_LANGUAGE,
+ "attributes-natural-language", NULL, language->language);
+
+ slprintf(uri, sizeof(uri) - 1, "ipp://localhost/jobs/%d", pjob->sysjob);
+
+ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "job-uri", NULL, uri);
+
+ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "requesting-user-name",
+ NULL, pjob->user);
+
+ /*
+ * Do the request and get back a response...
+ */
+
+ ret = 1;
+
+ if ((response = cupsDoRequest(http, request, "/jobs")) != NULL)
+ {
+ if (response->request.status.status_code >= IPP_OK_CONFLICT)
+ DEBUG(0,("Unable to cancel job %d - %s\n", pjob->sysjob,
+ ippErrorString(cupsLastError())));
+ else
+ ret = 0;
+
+ ippDelete(response);
+ }
+ else
+ DEBUG(0,("Unable to cancel job %d - %s\n", pjob->sysjob,
+ ippErrorString(cupsLastError())));
+
+ httpClose(http);
+
+ return (ret);
+}
+
+
+/*
+ * 'cups_job_pause()' - Pause a job.
+ */
+
+static int
+cups_job_pause(int snum, struct printjob *pjob)
+{
+ int ret; /* Return value */
+ http_t *http; /* HTTP connection to server */
+ ipp_t *request, /* IPP Request */
+ *response; /* IPP Response */
+ cups_lang_t *language; /* Default language */
+ char uri[HTTP_MAX_URI]; /* printer-uri attribute */
+
+
+ DEBUG(5,("cups_job_pause(%d, %p (%d))\n", snum, pjob, pjob->sysjob));
+
+ /*
+ * Make sure we don't ask for passwords...
+ */
+
+ cupsSetPasswordCB(cups_passwd_cb);
+
+ /*
+ * Try to connect to the server...
+ */
+
+ if ((http = httpConnect(cupsServer(), ippPort())) == NULL)
+ {
+ DEBUG(0,("Unable to connect to CUPS server %s - %s\n",
+ cupsServer(), strerror(errno)));
+ return (1);
+ }
+
+ /*
+ * Build an IPP_HOLD_JOB request, which requires the following
+ * attributes:
+ *
+ * attributes-charset
+ * attributes-natural-language
+ * job-uri
+ * requesting-user-name
+ */
+
+ request = ippNew();
+
+ request->request.op.operation_id = IPP_HOLD_JOB;
+ request->request.op.request_id = 1;
+
+ language = cupsLangDefault();
+
+ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_CHARSET,
+ "attributes-charset", NULL, cupsLangEncoding(language));
+
+ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_LANGUAGE,
+ "attributes-natural-language", NULL, language->language);
+
+ slprintf(uri, sizeof(uri) - 1, "ipp://localhost/jobs/%d", pjob->sysjob);
+
+ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "job-uri", NULL, uri);
+
+ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "requesting-user-name",
+ NULL, pjob->user);
+
+ /*
+ * Do the request and get back a response...
+ */
+
+ ret = 1;
+
+ if ((response = cupsDoRequest(http, request, "/jobs")) != NULL)
+ {
+ if (response->request.status.status_code >= IPP_OK_CONFLICT)
+ DEBUG(0,("Unable to hold job %d - %s\n", pjob->sysjob,
+ ippErrorString(cupsLastError())));
+ else
+ ret = 0;
+
+ ippDelete(response);
+ }
+ else
+ DEBUG(0,("Unable to hold job %d - %s\n", pjob->sysjob,
+ ippErrorString(cupsLastError())));
+
+ httpClose(http);
+
+ return (ret);
+}
+
+
+/*
+ * 'cups_job_resume()' - Resume a paused job.
+ */
+
+static int
+cups_job_resume(int snum, struct printjob *pjob)
+{
+ int ret; /* Return value */
+ http_t *http; /* HTTP connection to server */
+ ipp_t *request, /* IPP Request */
+ *response; /* IPP Response */
+ cups_lang_t *language; /* Default language */
+ char uri[HTTP_MAX_URI]; /* printer-uri attribute */
+
+
+ DEBUG(5,("cups_job_resume(%d, %p (%d))\n", snum, pjob, pjob->sysjob));
+
+ /*
+ * Make sure we don't ask for passwords...
+ */
+
+ cupsSetPasswordCB(cups_passwd_cb);
+
+ /*
+ * Try to connect to the server...
+ */
+
+ if ((http = httpConnect(cupsServer(), ippPort())) == NULL)
+ {
+ DEBUG(0,("Unable to connect to CUPS server %s - %s\n",
+ cupsServer(), strerror(errno)));
+ return (1);
+ }
+
+ /*
+ * Build an IPP_RELEASE_JOB request, which requires the following
+ * attributes:
+ *
+ * attributes-charset
+ * attributes-natural-language
+ * job-uri
+ * requesting-user-name
+ */
+
+ request = ippNew();
+
+ request->request.op.operation_id = IPP_RELEASE_JOB;
+ request->request.op.request_id = 1;
+
+ language = cupsLangDefault();
+
+ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_CHARSET,
+ "attributes-charset", NULL, cupsLangEncoding(language));
+
+ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_LANGUAGE,
+ "attributes-natural-language", NULL, language->language);
+
+ slprintf(uri, sizeof(uri) - 1, "ipp://localhost/jobs/%d", pjob->sysjob);
+
+ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "job-uri", NULL, uri);
+
+ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "requesting-user-name",
+ NULL, pjob->user);
+
+ /*
+ * Do the request and get back a response...
+ */
+
+ ret = 1;
+
+ if ((response = cupsDoRequest(http, request, "/jobs")) != NULL)
+ {
+ if (response->request.status.status_code >= IPP_OK_CONFLICT)
+ DEBUG(0,("Unable to release job %d - %s\n", pjob->sysjob,
+ ippErrorString(cupsLastError())));
+ else
+ ret = 0;
+
+ ippDelete(response);
+ }
+ else
+ DEBUG(0,("Unable to release job %d - %s\n", pjob->sysjob,
+ ippErrorString(cupsLastError())));
+
+ httpClose(http);
+
+ return (ret);
+}
+
+
+/*
+ * 'cups_job_submit()' - Submit a job for printing.
+ */
+
+static int
+cups_job_submit(int snum, struct printjob *pjob)
+{
+ int ret; /* Return value */
+ http_t *http; /* HTTP connection to server */
+ ipp_t *request, /* IPP Request */
+ *response; /* IPP Response */
+ cups_lang_t *language; /* Default language */
+ char uri[HTTP_MAX_URI]; /* printer-uri attribute */
+ char *clientname; /* hostname of client for job-originating-host attribute */
+ pstring new_jobname;
+
+ DEBUG(5,("cups_job_submit(%d, %p (%d))\n", snum, pjob, pjob->sysjob));
+
+ /*
+ * Make sure we don't ask for passwords...
+ */
+
+ cupsSetPasswordCB(cups_passwd_cb);
+
+ /*
+ * Try to connect to the server...
+ */
+
+ if ((http = httpConnect(cupsServer(), ippPort())) == NULL)
+ {
+ DEBUG(0,("Unable to connect to CUPS server %s - %s\n",
+ cupsServer(), strerror(errno)));
+ return (1);
+ }
+
+ /*
+ * Build an IPP_PRINT_JOB request, which requires the following
+ * attributes:
+ *
+ * attributes-charset
+ * attributes-natural-language
+ * printer-uri
+ * requesting-user-name
+ * [document-data]
+ */
+
+ request = ippNew();
+
+ request->request.op.operation_id = IPP_PRINT_JOB;
+ request->request.op.request_id = 1;
+
+ language = cupsLangDefault();
+
+ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_CHARSET,
+ "attributes-charset", NULL, cupsLangEncoding(language));
+
+ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_LANGUAGE,
+ "attributes-natural-language", NULL, language->language);
+
+ slprintf(uri, sizeof(uri) - 1, "ipp://localhost/printers/%s",
+ PRINTERNAME(snum));
+
+ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI,
+ "printer-uri", NULL, uri);
+
+ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "requesting-user-name",
+ NULL, pjob->user);
+
+ clientname = client_name();
+ if (strcmp(clientname, "UNKNOWN") == 0) {
+ clientname = client_addr();
+ }
+
+ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME,
+ "job-originating-host-name", NULL,
+ clientname);
+
+ pstr_sprintf(new_jobname,"%s%.8u %s", PRINT_SPOOL_PREFIX,
+ (unsigned int)pjob->smbjob, pjob->jobname);
+
+ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "job-name", NULL,
+ new_jobname);
+
+ /*
+ * Do the request and get back a response...
+ */
+
+ slprintf(uri, sizeof(uri) - 1, "/printers/%s", PRINTERNAME(snum));
+
+ ret = 1;
+ if ((response = cupsDoFileRequest(http, request, uri,
+ pjob->filename)) != NULL)
+ {
+ if (response->request.status.status_code >= IPP_OK_CONFLICT)
+ DEBUG(0,("Unable to print file to %s - %s\n", PRINTERNAME(snum),
+ ippErrorString(cupsLastError())));
+ else
+ ret = 0;
+
+ ippDelete(response);
+ }
+ else
+ DEBUG(0,("Unable to print file to `%s' - %s\n", PRINTERNAME(snum),
+ ippErrorString(cupsLastError())));
+
+ httpClose(http);
+
+ if ( ret == 0 )
+ unlink(pjob->filename);
+ /* else print_job_end will do it for us */
+
+ return (ret);
+}
+
+
+/*
+ * 'cups_queue_get()' - Get all the jobs in the print queue.
+ */
+
+static int
+cups_queue_get(int snum, print_queue_struct **q, print_status_struct *status)
+{
+ http_t *http; /* HTTP connection to server */
+ ipp_t *request, /* IPP Request */
+ *response; /* IPP Response */
+ ipp_attribute_t *attr; /* Current attribute */
+ cups_lang_t *language; /* Default language */
+ char uri[HTTP_MAX_URI]; /* printer-uri attribute */
+ int qcount, /* Number of active queue entries */
+ qalloc; /* Number of queue entries allocated */
+ print_queue_struct *queue, /* Queue entries */
+ *temp; /* Temporary pointer for queue */
+ const char *user_name, /* job-originating-user-name attribute */
+ *job_name; /* job-name attribute */
+ int job_id; /* job-id attribute */
+ int job_k_octets; /* job-k-octets attribute */
+ time_t job_time; /* time-at-creation attribute */
+ ipp_jstate_t job_status; /* job-status attribute */
+ int job_priority; /* job-priority attribute */
+ static const char *jattrs[] = /* Requested job attributes */
+ {
+ "job-id",
+ "job-k-octets",
+ "job-name",
+ "job-originating-user-name",
+ "job-priority",
+ "job-state",
+ "time-at-creation",
+ };
+ static const char *pattrs[] = /* Requested printer attributes */
+ {
+ "printer-state",
+ "printer-state-message"
+ };
+
+
+ DEBUG(5,("cups_queue_get(%d, %p, %p)\n", snum, q, status));
+
+ /*
+ * Make sure we don't ask for passwords...
+ */
+
+ cupsSetPasswordCB(cups_passwd_cb);
+
+ /*
+ * Try to connect to the server...
+ */
+
+ if ((http = httpConnect(cupsServer(), ippPort())) == NULL)
+ {
+ DEBUG(0,("Unable to connect to CUPS server %s - %s\n",
+ cupsServer(), strerror(errno)));
+ return (0);
+ }
+
+ /*
+ * Generate the printer URI...
+ */
+
+ slprintf(uri, sizeof(uri) - 1, "ipp://localhost/printers/%s",
+ PRINTERNAME(snum));
+
+ /*
+ * Build an IPP_GET_JOBS request, which requires the following
+ * attributes:
+ *
+ * attributes-charset
+ * attributes-natural-language
+ * requested-attributes
+ * printer-uri
+ */
+
+ request = ippNew();
+
+ request->request.op.operation_id = IPP_GET_JOBS;
+ request->request.op.request_id = 1;
+
+ language = cupsLangDefault();
+
+ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_CHARSET,
+ "attributes-charset", NULL, cupsLangEncoding(language));
+
+ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_LANGUAGE,
+ "attributes-natural-language", NULL, language->language);
+
+ ippAddStrings(request, IPP_TAG_OPERATION, IPP_TAG_NAME,
+ "requested-attributes",
+ (sizeof(jattrs) / sizeof(jattrs[0])),
+ NULL, jattrs);
+
+ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI,
+ "printer-uri", NULL, uri);
+
+ /*
+ * Do the request and get back a response...
+ */
+
+ if ((response = cupsDoRequest(http, request, "/")) == NULL)
+ {
+ DEBUG(0,("Unable to get jobs for %s - %s\n", uri,
+ ippErrorString(cupsLastError())));
+ httpClose(http);
+ return (0);
+ }
+
+ if (response->request.status.status_code >= IPP_OK_CONFLICT)
+ {
+ DEBUG(0,("Unable to get jobs for %s - %s\n", uri,
+ ippErrorString(response->request.status.status_code)));
+ ippDelete(response);
+ httpClose(http);
+
+ return (0);
+ }
+
+ /*
+ * Process the jobs...
+ */
+
+ qcount = 0;
+ qalloc = 0;
+ queue = NULL;
+
+ for (attr = response->attrs; attr != NULL; attr = attr->next)
+ {
+ /*
+ * Skip leading attributes until we hit a job...
+ */
+
+ while (attr != NULL && attr->group_tag != IPP_TAG_JOB)
+ attr = attr->next;
+
+ if (attr == NULL)
+ break;
+
+ /*
+ * Allocate memory as needed...
+ */
+ if (qcount >= qalloc)
+ {
+ qalloc += 16;
+
+ temp = Realloc(queue, sizeof(print_queue_struct) * qalloc);
+
+ if (temp == NULL)
+ {
+ DEBUG(0,("cups_queue_get: Not enough memory!"));
+ ippDelete(response);
+ httpClose(http);
+
+ SAFE_FREE(queue);
+ return (0);
+ }
+
+ queue = temp;
+ }
+
+ temp = queue + qcount;
+ memset(temp, 0, sizeof(print_queue_struct));
+
+ /*
+ * Pull the needed attributes from this job...
+ */
+
+ job_id = 0;
+ job_priority = 50;
+ job_status = IPP_JOB_PENDING;
+ job_time = 0;
+ job_k_octets = 0;
+ user_name = NULL;
+ job_name = NULL;
+
+ while (attr != NULL && attr->group_tag == IPP_TAG_JOB)
+ {
+ if (attr->name == NULL)
+ {
+ attr = attr->next;
+ break;
+ }
+
+ if (strcmp(attr->name, "job-id") == 0 &&
+ attr->value_tag == IPP_TAG_INTEGER)
+ job_id = attr->values[0].integer;
+
+ if (strcmp(attr->name, "job-k-octets") == 0 &&
+ attr->value_tag == IPP_TAG_INTEGER)
+ job_k_octets = attr->values[0].integer;
+
+ if (strcmp(attr->name, "job-priority") == 0 &&
+ attr->value_tag == IPP_TAG_INTEGER)
+ job_priority = attr->values[0].integer;
+
+ if (strcmp(attr->name, "job-state") == 0 &&
+ attr->value_tag == IPP_TAG_ENUM)
+ job_status = (ipp_jstate_t)(attr->values[0].integer);
+
+ if (strcmp(attr->name, "time-at-creation") == 0 &&
+ attr->value_tag == IPP_TAG_INTEGER)
+ job_time = attr->values[0].integer;
+
+ if (strcmp(attr->name, "job-name") == 0 &&
+ attr->value_tag == IPP_TAG_NAME)
+ job_name = attr->values[0].string.text;
+
+ if (strcmp(attr->name, "job-originating-user-name") == 0 &&
+ attr->value_tag == IPP_TAG_NAME)
+ user_name = attr->values[0].string.text;
+
+ attr = attr->next;
+ }
+
+ /*
+ * See if we have everything needed...
+ */
+
+ if (user_name == NULL || job_name == NULL || job_id == 0)
+ {
+ if (attr == NULL)
+ break;
+ else
+ continue;
+ }
+
+ temp->job = job_id;
+ temp->size = job_k_octets * 1024;
+ temp->status = job_status == IPP_JOB_PENDING ? LPQ_QUEUED :
+ job_status == IPP_JOB_STOPPED ? LPQ_PAUSED :
+ job_status == IPP_JOB_HELD ? LPQ_PAUSED :
+ LPQ_PRINTING;
+ temp->priority = job_priority;
+ temp->time = job_time;
+ strncpy(temp->fs_user, user_name, sizeof(temp->fs_user) - 1);
+ strncpy(temp->fs_file, job_name, sizeof(temp->fs_file) - 1);
+
+ qcount ++;
+
+ if (attr == NULL)
+ break;
+ }
+
+ ippDelete(response);
+
+ /*
+ * Build an IPP_GET_PRINTER_ATTRIBUTES request, which requires the
+ * following attributes:
+ *
+ * attributes-charset
+ * attributes-natural-language
+ * requested-attributes
+ * printer-uri
+ */
+
+ request = ippNew();
+
+ request->request.op.operation_id = IPP_GET_PRINTER_ATTRIBUTES;
+ request->request.op.request_id = 1;
+
+ language = cupsLangDefault();
+
+ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_CHARSET,
+ "attributes-charset", NULL, cupsLangEncoding(language));
+
+ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_LANGUAGE,
+ "attributes-natural-language", NULL, language->language);
+
+ ippAddStrings(request, IPP_TAG_OPERATION, IPP_TAG_NAME,
+ "requested-attributes",
+ (sizeof(pattrs) / sizeof(pattrs[0])),
+ NULL, pattrs);
+
+ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI,
+ "printer-uri", NULL, uri);
+
+ /*
+ * Do the request and get back a response...
+ */
+
+ if ((response = cupsDoRequest(http, request, "/")) == NULL)
+ {
+ DEBUG(0,("Unable to get printer status for %s - %s\n", PRINTERNAME(snum),
+ ippErrorString(cupsLastError())));
+ httpClose(http);
+ *q = queue;
+ return (qcount);
+ }
+
+ if (response->request.status.status_code >= IPP_OK_CONFLICT)
+ {
+ DEBUG(0,("Unable to get printer status for %s - %s\n", PRINTERNAME(snum),
+ ippErrorString(response->request.status.status_code)));
+ ippDelete(response);
+ httpClose(http);
+ *q = queue;
+ return (qcount);
+ }
+
+ /*
+ * Get the current printer status and convert it to the SAMBA values.
+ */
+
+ if ((attr = ippFindAttribute(response, "printer-state", IPP_TAG_ENUM)) != NULL)
+ {
+ if (attr->values[0].integer == IPP_PRINTER_STOPPED)
+ status->status = LPSTAT_STOPPED;
+ else
+ status->status = LPSTAT_OK;
+ }
+
+ if ((attr = ippFindAttribute(response, "printer-state-message",
+ IPP_TAG_TEXT)) != NULL)
+ fstrcpy(status->message, attr->values[0].string.text);
+
+ ippDelete(response);
+
+ /*
+ * Return the job queue...
+ */
+
+ httpClose(http);
+
+ *q = queue;
+ return (qcount);
+}
+
+
+/*
+ * 'cups_queue_pause()' - Pause a print queue.
+ */
+
+static int
+cups_queue_pause(int snum)
+{
+ extern userdom_struct current_user_info;
+ int ret; /* Return value */
+ http_t *http; /* HTTP connection to server */
+ ipp_t *request, /* IPP Request */
+ *response; /* IPP Response */
+ cups_lang_t *language; /* Default language */
+ char uri[HTTP_MAX_URI]; /* printer-uri attribute */
+
+
+ DEBUG(5,("cups_queue_pause(%d)\n", snum));
+
+ /*
+ * Make sure we don't ask for passwords...
+ */
+
+ cupsSetPasswordCB(cups_passwd_cb);
+
+ /*
+ * Try to connect to the server...
+ */
+
+ if ((http = httpConnect(cupsServer(), ippPort())) == NULL)
+ {
+ DEBUG(0,("Unable to connect to CUPS server %s - %s\n",
+ cupsServer(), strerror(errno)));
+ return (1);
+ }
+
+ /*
+ * Build an IPP_PAUSE_PRINTER request, which requires the following
+ * attributes:
+ *
+ * attributes-charset
+ * attributes-natural-language
+ * printer-uri
+ * requesting-user-name
+ */
+
+ request = ippNew();
+
+ request->request.op.operation_id = IPP_PAUSE_PRINTER;
+ request->request.op.request_id = 1;
+
+ language = cupsLangDefault();
+
+ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_CHARSET,
+ "attributes-charset", NULL, cupsLangEncoding(language));
+
+ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_LANGUAGE,
+ "attributes-natural-language", NULL, language->language);
+
+ slprintf(uri, sizeof(uri) - 1, "ipp://localhost/printers/%s",
+ PRINTERNAME(snum));
+
+ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri", NULL, uri);
+
+ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "requesting-user-name",
+ NULL, current_user_info.unix_name);
+
+ /*
+ * Do the request and get back a response...
+ */
+
+ ret = 1;
+
+ if ((response = cupsDoRequest(http, request, "/admin/")) != NULL)
+ {
+ if (response->request.status.status_code >= IPP_OK_CONFLICT)
+ DEBUG(0,("Unable to pause printer %s - %s\n", PRINTERNAME(snum),
+ ippErrorString(cupsLastError())));
+ else
+ ret = 0;
+
+ ippDelete(response);
+ }
+ else
+ DEBUG(0,("Unable to pause printer %s - %s\n", PRINTERNAME(snum),
+ ippErrorString(cupsLastError())));
+
+ httpClose(http);
+
+ return (ret);
+}
+
+
+/*
+ * 'cups_queue_resume()' - Restart a print queue.
+ */
+
+static int
+cups_queue_resume(int snum)
+{
+ extern userdom_struct current_user_info;
+ int ret; /* Return value */
+ http_t *http; /* HTTP connection to server */
+ ipp_t *request, /* IPP Request */
+ *response; /* IPP Response */
+ cups_lang_t *language; /* Default language */
+ char uri[HTTP_MAX_URI]; /* printer-uri attribute */
+
+
+ DEBUG(5,("cups_queue_resume(%d)\n", snum));
+
+ /*
+ * Make sure we don't ask for passwords...
+ */
+
+ cupsSetPasswordCB(cups_passwd_cb);
+
+ /*
+ * Try to connect to the server...
+ */
+
+ if ((http = httpConnect(cupsServer(), ippPort())) == NULL)
+ {
+ DEBUG(0,("Unable to connect to CUPS server %s - %s\n",
+ cupsServer(), strerror(errno)));
+ return (1);
+ }
+
+ /*
+ * Build an IPP_RESUME_PRINTER request, which requires the following
+ * attributes:
+ *
+ * attributes-charset
+ * attributes-natural-language
+ * printer-uri
+ * requesting-user-name
+ */
+
+ request = ippNew();
+
+ request->request.op.operation_id = IPP_RESUME_PRINTER;
+ request->request.op.request_id = 1;
+
+ language = cupsLangDefault();
+
+ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_CHARSET,
+ "attributes-charset", NULL, cupsLangEncoding(language));
+
+ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_LANGUAGE,
+ "attributes-natural-language", NULL, language->language);
+
+ slprintf(uri, sizeof(uri) - 1, "ipp://localhost/printers/%s",
+ PRINTERNAME(snum));
+
+ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri", NULL, uri);
+
+ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "requesting-user-name",
+ NULL, current_user_info.unix_name);
+
+ /*
+ * Do the request and get back a response...
+ */
+
+ ret = 1;
+
+ if ((response = cupsDoRequest(http, request, "/admin/")) != NULL)
+ {
+ if (response->request.status.status_code >= IPP_OK_CONFLICT)
+ DEBUG(0,("Unable to resume printer %s - %s\n", PRINTERNAME(snum),
+ ippErrorString(cupsLastError())));
+ else
+ ret = 0;
+
+ ippDelete(response);
+ }
+ else
+ DEBUG(0,("Unable to resume printer %s - %s\n", PRINTERNAME(snum),
+ ippErrorString(cupsLastError())));
+
+ httpClose(http);
+
+ return (ret);
+}
+
+
+#else
+ /* this keeps fussy compilers happy */
+ void print_cups_dummy(void) {}
+#endif /* HAVE_CUPS */
diff --git a/source/printing/print_generic.c b/source/printing/print_generic.c
new file mode 100644
index 00000000000..9e0ea85bb9a
--- /dev/null
+++ b/source/printing/print_generic.c
@@ -0,0 +1,247 @@
+/*
+ Unix SMB/CIFS implementation.
+ printing command routines
+ Copyright (C) Andrew Tridgell 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 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 "printing.h"
+
+
+/*
+ * Generic printing interface definitions...
+ */
+
+static int generic_job_delete(int snum, struct printjob *pjob);
+static int generic_job_pause(int snum, struct printjob *pjob);
+static int generic_job_resume(int snum, struct printjob *pjob);
+static int generic_job_submit(int snum, struct printjob *pjob);
+static int generic_queue_get(int snum, print_queue_struct **q,
+ print_status_struct *status);
+static int generic_queue_pause(int snum);
+static int generic_queue_resume(int snum);
+
+
+struct printif generic_printif =
+ {
+ generic_queue_get,
+ generic_queue_pause,
+ generic_queue_resume,
+ generic_job_delete,
+ generic_job_pause,
+ generic_job_resume,
+ generic_job_submit,
+ };
+
+/****************************************************************************
+run a given print command
+a null terminated list of value/substitute pairs is provided
+for local substitution strings
+****************************************************************************/
+static int print_run_command(int snum,char *command, int *outfd, ...)
+{
+
+ pstring syscmd;
+ char *arg;
+ int ret;
+ va_list ap;
+ va_start(ap, outfd);
+
+ if (!command || !*command) return -1;
+
+ if (!VALID_SNUM(snum)) {
+ DEBUG(0,("Invalid snum %d for command %s\n", snum, command));
+ return -1;
+ }
+
+ pstrcpy(syscmd, command);
+
+ while ((arg = va_arg(ap, char *))) {
+ char *value = va_arg(ap,char *);
+ pstring_sub(syscmd, arg, value);
+ }
+ va_end(ap);
+
+ pstring_sub(syscmd, "%p", PRINTERNAME(snum));
+ standard_sub_snum(snum,syscmd,sizeof(syscmd));
+
+ ret = smbrun(syscmd,outfd);
+
+ DEBUG(3,("Running the command `%s' gave %d\n",syscmd,ret));
+
+ return ret;
+}
+
+
+/****************************************************************************
+delete a print job
+****************************************************************************/
+static int generic_job_delete(int snum, struct printjob *pjob)
+{
+ fstring jobstr;
+
+ /* need to delete the spooled entry */
+ slprintf(jobstr, sizeof(jobstr)-1, "%d", pjob->sysjob);
+ return print_run_command(
+ snum,
+ lp_lprmcommand(snum), NULL,
+ "%j", jobstr,
+ "%T", http_timestring(pjob->starttime),
+ NULL);
+}
+
+/****************************************************************************
+pause a job
+****************************************************************************/
+static int generic_job_pause(int snum, struct printjob *pjob)
+{
+ fstring jobstr;
+
+ /* need to pause the spooled entry */
+ slprintf(jobstr, sizeof(jobstr)-1, "%d", pjob->sysjob);
+ return print_run_command(snum,
+ lp_lppausecommand(snum), NULL,
+ "%j", jobstr,
+ NULL);
+}
+
+/****************************************************************************
+resume a job
+****************************************************************************/
+static int generic_job_resume(int snum, struct printjob *pjob)
+{
+ fstring jobstr;
+
+ /* need to pause the spooled entry */
+ slprintf(jobstr, sizeof(jobstr)-1, "%d", pjob->sysjob);
+ return print_run_command(snum,
+ lp_lpresumecommand(snum), NULL,
+ "%j", jobstr,
+ NULL);
+}
+
+/****************************************************************************
+ Submit a file for printing - called from print_job_end()
+****************************************************************************/
+
+static int generic_job_submit(int snum, struct printjob *pjob)
+{
+ int ret;
+ pstring current_directory;
+ pstring print_directory;
+ char *wd, *p;
+ pstring jobname;
+ fstring job_page_count, job_size;
+
+ /* we print from the directory path to give the best chance of
+ parsing the lpq output */
+ wd = sys_getwd(current_directory);
+ if (!wd)
+ return 0;
+
+ pstrcpy(print_directory, pjob->filename);
+ p = strrchr_m(print_directory,'/');
+ if (!p)
+ return 0;
+ *p++ = 0;
+
+ if (chdir(print_directory) != 0)
+ return 0;
+
+ pstrcpy(jobname, pjob->jobname);
+ pstring_sub(jobname, "'", "_");
+ slprintf(job_page_count, sizeof(job_page_count)-1, "%d", pjob->page_count);
+ slprintf(job_size, sizeof(job_size)-1, "%lu", (unsigned long)pjob->size);
+
+ /* send it to the system spooler */
+ ret = print_run_command(snum,
+ lp_printcommand(snum), NULL,
+ "%s", p,
+ "%J", jobname,
+ "%f", p,
+ "%z", job_size,
+ "%c", job_page_count,
+ NULL);
+
+ chdir(wd);
+
+ return ret;
+}
+
+
+/****************************************************************************
+get the current list of queued jobs
+****************************************************************************/
+static int generic_queue_get(int snum, print_queue_struct **q, print_status_struct *status)
+{
+ char **qlines;
+ int fd;
+ int numlines, i, qcount;
+ print_queue_struct *queue = NULL;
+ fstring printer_name;
+
+ fstrcpy(printer_name, lp_servicename(snum));
+
+ print_run_command(snum, lp_lpqcommand(snum), &fd, NULL);
+
+ if (fd == -1) {
+ DEBUG(5,("generic_queue_get: Can't read print queue status for printer %s\n",
+ printer_name ));
+ return 0;
+ }
+
+ numlines = 0;
+ qlines = fd_lines_load(fd, &numlines);
+ close(fd);
+
+ /* turn the lpq output into a series of job structures */
+ qcount = 0;
+ ZERO_STRUCTP(status);
+ if (numlines)
+ queue = (print_queue_struct *)malloc(sizeof(print_queue_struct)*(numlines+1));
+
+ if (queue) {
+ memset(queue, '\0', sizeof(print_queue_struct)*(numlines+1));
+ for (i=0; i<numlines; i++) {
+ /* parse the line */
+ if (parse_lpq_entry(snum,qlines[i],
+ &queue[qcount],status,qcount==0)) {
+ qcount++;
+ }
+ }
+ }
+ file_lines_free(qlines);
+
+ *q = queue;
+ return qcount;
+}
+
+/****************************************************************************
+ pause a queue
+****************************************************************************/
+static int generic_queue_pause(int snum)
+{
+ return print_run_command(snum, lp_queuepausecommand(snum), NULL, NULL);
+}
+
+/****************************************************************************
+ resume a queue
+****************************************************************************/
+static int generic_queue_resume(int snum)
+{
+ return print_run_command(snum, lp_queueresumecommand(snum), NULL, NULL);
+}
diff --git a/source/printing/print_svid.c b/source/printing/print_svid.c
new file mode 100644
index 00000000000..07b157bcd95
--- /dev/null
+++ b/source/printing/print_svid.c
@@ -0,0 +1,144 @@
+/*
+ * Copyright (C) 1997-1998 by Norm Jacobs, Colorado Springs, Colorado, USA
+ * Copyright (C) 1997-1998 by Sun Microsystem, Inc.
+ * All Rights Reserved
+ *
+ * This program is free software; you can 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.
+ */
+
+/*
+ * This module implements support for gathering and comparing available
+ * printer information on a SVID or XPG4 compliant system. It does this
+ * through the use of the SVID/XPG4 command "lpstat(1)".
+ *
+ * The expectations is that execution of the command "lpstat -v" will
+ * generate responses in the form of:
+ *
+ * device for serial: /dev/term/b
+ * system for fax: server
+ * system for color: server (as printer chroma)
+ */
+
+
+#include "includes.h"
+
+#ifdef SYSV
+
+typedef struct printer {
+ char *name;
+ struct printer *next;
+} printer_t;
+static printer_t *printers = NULL;
+
+static void populate_printers(void)
+{
+ char **lines;
+ int i;
+
+ lines = file_lines_pload("/usr/bin/lpstat -v", NULL);
+ if (!lines) return;
+
+ for (i=0;lines[i];i++) {
+ printer_t *ptmp;
+ char *name, *tmp;
+ char *buf = lines[i];
+
+ /* eat "system/device for " */
+ if (((tmp = strchr_m(buf, ' ')) == NULL) ||
+ ((tmp = strchr_m(++tmp, ' ')) == NULL))
+ continue;
+
+ /*
+ * In case we're only at the "for ".
+ */
+
+ if(!strncmp("for ",++tmp,4)) {
+ tmp=strchr_m(tmp, ' ');
+ tmp++;
+ }
+
+ /* Eat whitespace. */
+
+ while(*tmp == ' ')
+ ++tmp;
+
+ /*
+ * On HPUX there is an extra line that can be ignored.
+ * d.thibadeau 2001/08/09
+ */
+ if(!strncmp("remote to",tmp,9))
+ continue;
+
+ name = tmp;
+
+ /* truncate the ": ..." */
+ if ((tmp = strchr_m(name, ':')) != NULL)
+ *tmp = '\0';
+
+ /* add it to the cache */
+ if ((ptmp = malloc(sizeof (*ptmp))) != NULL) {
+ ZERO_STRUCTP(ptmp);
+ if((ptmp->name = strdup(name)) == NULL)
+ DEBUG(0,("populate_printers: malloc fail in strdup !\n"));
+ ptmp->next = printers;
+ printers = ptmp;
+ } else {
+ DEBUG(0,("populate_printers: malloc fail for ptmp\n"));
+ }
+ }
+
+ file_lines_free(lines);
+}
+
+
+/*
+ * provide the equivalent of pcap_printer_fn() for SVID/XPG4 conforming
+ * systems. It was unclear why pcap_printer_fn() was tossing names longer
+ * than 8 characters. I suspect that its a protocol limit, but amazingly
+ * names longer than 8 characters appear to work with my test
+ * clients (Win95/NT).
+ */
+void sysv_printer_fn(void (*fn)(char *, char *))
+{
+ printer_t *tmp;
+
+ if (printers == NULL)
+ populate_printers();
+ for (tmp = printers; tmp != NULL; tmp = tmp->next)
+ (fn)(tmp->name, "");
+}
+
+
+/*
+ * provide the equivalent of pcap_printername_ok() for SVID/XPG4 conforming
+ * systems.
+ */
+int sysv_printername_ok(const char *name)
+{
+ printer_t *tmp;
+
+ if (printers == NULL)
+ populate_printers();
+ for (tmp = printers; tmp != NULL; tmp = tmp->next)
+ if (strcmp(tmp->name, name) == 0)
+ return (True);
+ return (False);
+}
+
+#else
+/* this keeps fussy compilers happy */
+ void print_svid_dummy(void);
+ void print_svid_dummy(void) {}
+#endif
diff --git a/source/printing/printfsp.c b/source/printing/printfsp.c
new file mode 100644
index 00000000000..0b6d4fdbe1c
--- /dev/null
+++ b/source/printing/printfsp.c
@@ -0,0 +1,120 @@
+/*
+ Unix SMB/CIFS implementation.
+ printing backend routines for smbd - using files_struct rather
+ than only snum
+ Copyright (C) Andrew Tridgell 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 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"
+
+/***************************************************************************
+open a print file and setup a fsp for it. This is a wrapper around
+print_job_start().
+***************************************************************************/
+
+files_struct *print_fsp_open(connection_struct *conn, char *fname)
+{
+ int jobid;
+ SMB_STRUCT_STAT sbuf;
+ extern struct current_user current_user;
+ files_struct *fsp = file_new(conn);
+ fstring name;
+
+ if(!fsp)
+ return NULL;
+
+ fstrcpy( name, "Remote Downlevel Document");
+ if (fname) {
+ char *p = strrchr(fname, '/');
+ fstrcat(name, " ");
+ if (!p)
+ p = fname;
+ fstrcat(name, p);
+ }
+
+ jobid = print_job_start(&current_user, SNUM(conn), name, NULL);
+ if (jobid == -1) {
+ file_free(fsp);
+ return NULL;
+ }
+
+ /* Convert to RAP id. */
+ fsp->rap_print_jobid = pjobid_to_rap(SNUM(conn), jobid);
+ if (fsp->rap_print_jobid == 0) {
+ /* We need to delete the entry in the tdb. */
+ pjob_delete(SNUM(conn), jobid);
+ file_free(fsp);
+ return NULL;
+ }
+
+ /* setup a full fsp */
+ fsp->fd = print_job_fd(SNUM(conn),jobid);
+ GetTimeOfDay(&fsp->open_time);
+ fsp->vuid = current_user.vuid;
+ fsp->size = 0;
+ fsp->pos = -1;
+ fsp->can_lock = True;
+ fsp->can_read = False;
+ fsp->can_write = True;
+ fsp->share_mode = 0;
+ fsp->print_file = True;
+ fsp->modified = False;
+ fsp->oplock_type = NO_OPLOCK;
+ fsp->sent_oplock_break = NO_BREAK_SENT;
+ fsp->is_directory = False;
+ fsp->directory_delete_on_close = False;
+ string_set(&fsp->fsp_name,print_job_fname(SNUM(conn),jobid));
+ fsp->wbmpx_ptr = NULL;
+ fsp->wcp = NULL;
+ SMB_VFS_FSTAT(fsp,fsp->fd, &sbuf);
+ fsp->mode = sbuf.st_mode;
+ fsp->inode = sbuf.st_ino;
+ fsp->dev = sbuf.st_dev;
+
+ conn->num_files_open++;
+
+ return fsp;
+}
+
+/****************************************************************************
+print a file - called on closing the file
+****************************************************************************/
+void print_fsp_end(files_struct *fsp, BOOL normal_close)
+{
+ uint32 jobid;
+ int snum;
+
+ if (fsp->share_mode == FILE_DELETE_ON_CLOSE) {
+ /*
+ * Truncate the job. print_job_end will take
+ * care of deleting it for us. JRA.
+ */
+ sys_ftruncate(fsp->fd, 0);
+ }
+
+ if (fsp->fsp_name) {
+ string_free(&fsp->fsp_name);
+ }
+
+ if (!rap_to_pjobid(fsp->rap_print_jobid, &snum, &jobid)) {
+ DEBUG(3,("print_fsp_end: Unable to convert RAP jobid %u to print jobid.\n",
+ (unsigned int)fsp->rap_print_jobid ));
+ return;
+ }
+
+ print_job_end(SNUM(fsp->conn),jobid, normal_close);
+}
diff --git a/source/printing/printing.c b/source/printing/printing.c
new file mode 100644
index 00000000000..e4ef1f52d0e
--- /dev/null
+++ b/source/printing/printing.c
@@ -0,0 +1,2420 @@
+/*
+ Unix SMB/Netbios implementation.
+ Version 3.0
+ printing backend routines
+ Copyright (C) Andrew Tridgell 1992-2000
+ Copyright (C) Jeremy Allison 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 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 "printing.h"
+
+/* Current printer interface */
+static BOOL remove_from_jobs_changed(int snum, uint32 jobid);
+
+/*
+ the printing backend revolves around a tdb database that stores the
+ SMB view of the print queue
+
+ The key for this database is a jobid - a internally generated number that
+ uniquely identifies a print job
+
+ reading the print queue involves two steps:
+ - possibly running lpq and updating the internal database from that
+ - reading entries from the database
+
+ jobids are assigned when a job starts spooling.
+*/
+
+/***************************************************************************
+ Nightmare. LANMAN jobid's are 16 bit numbers..... We must map them to 32
+ bit RPC jobids.... JRA.
+***************************************************************************/
+
+static TDB_CONTEXT *rap_tdb;
+static uint16 next_rap_jobid;
+
+uint16 pjobid_to_rap(int snum, uint32 jobid)
+{
+ uint16 rap_jobid;
+ TDB_DATA data, key;
+ char jinfo[8];
+
+ DEBUG(10,("pjobid_to_rap: called.\n"));
+
+ if (!rap_tdb) {
+ /* Create the in-memory tdb. */
+ rap_tdb = tdb_open_log(NULL, 0, TDB_INTERNAL, (O_RDWR|O_CREAT), 0644);
+ if (!rap_tdb)
+ return 0;
+ }
+
+ SIVAL(&jinfo,0,(int32)snum);
+ SIVAL(&jinfo,4,jobid);
+
+ key.dptr = (char *)&jinfo;
+ key.dsize = sizeof(jinfo);
+ data = tdb_fetch(rap_tdb, key);
+ if (data.dptr && data.dsize == sizeof(uint16)) {
+ rap_jobid = SVAL(data.dptr, 0);
+ SAFE_FREE(data.dptr);
+ DEBUG(10,("pjobid_to_rap: jobid %u maps to RAP jobid %u\n",
+ (unsigned int)jobid,
+ (unsigned int)rap_jobid));
+ return rap_jobid;
+ }
+ SAFE_FREE(data.dptr);
+ /* Not found - create and store mapping. */
+ rap_jobid = ++next_rap_jobid;
+ if (rap_jobid == 0)
+ rap_jobid = ++next_rap_jobid;
+ data.dptr = (char *)&rap_jobid;
+ data.dsize = sizeof(rap_jobid);
+ tdb_store(rap_tdb, key, data, TDB_REPLACE);
+ tdb_store(rap_tdb, data, key, TDB_REPLACE);
+
+ DEBUG(10,("pjobid_to_rap: created jobid %u maps to RAP jobid %u\n",
+ (unsigned int)jobid,
+ (unsigned int)rap_jobid));
+ return rap_jobid;
+}
+
+BOOL rap_to_pjobid(uint16 rap_jobid, int *psnum, uint32 *pjobid)
+{
+ TDB_DATA data, key;
+
+ DEBUG(10,("rap_to_pjobid called.\n"));
+
+ if (!rap_tdb)
+ return False;
+
+ key.dptr = (char *)&rap_jobid;
+ key.dsize = sizeof(rap_jobid);
+ data = tdb_fetch(rap_tdb, key);
+ if (data.dptr && data.dsize == 8) {
+ *psnum = IVAL(data.dptr,0);
+ *pjobid = IVAL(data.dptr,4);
+ DEBUG(10,("rap_to_pjobid: jobid %u maps to RAP jobid %u\n",
+ (unsigned int)*pjobid,
+ (unsigned int)rap_jobid));
+ SAFE_FREE(data.dptr);
+ return True;
+ }
+
+ DEBUG(10,("rap_to_pjobid: Failed to lookup RAP jobid %u\n",
+ (unsigned int)rap_jobid));
+ SAFE_FREE(data.dptr);
+ return False;
+}
+
+static void rap_jobid_delete(int snum, uint32 jobid)
+{
+ TDB_DATA key, data;
+ uint16 rap_jobid;
+ char jinfo[8];
+
+ DEBUG(10,("rap_jobid_delete: called.\n"));
+
+ if (!rap_tdb)
+ return;
+
+ SIVAL(&jinfo,0,(int32)snum);
+ SIVAL(&jinfo,4,jobid);
+
+ key.dptr = (char *)&jinfo;
+ key.dsize = sizeof(jinfo);
+ data = tdb_fetch(rap_tdb, key);
+ if (!data.dptr || (data.dsize != sizeof(uint16))) {
+ DEBUG(10,("rap_jobid_delete: cannot find jobid %u\n",
+ (unsigned int)jobid ));
+ SAFE_FREE(data.dptr);
+ return;
+ }
+
+ DEBUG(10,("rap_jobid_delete: deleting jobid %u\n",
+ (unsigned int)jobid ));
+
+ rap_jobid = SVAL(data.dptr, 0);
+ SAFE_FREE(data.dptr);
+ data.dptr = (char *)&rap_jobid;
+ data.dsize = sizeof(rap_jobid);
+ tdb_delete(rap_tdb, key);
+ tdb_delete(rap_tdb, data);
+}
+
+static pid_t local_pid;
+
+static int get_queue_status(int, print_status_struct *);
+
+/****************************************************************************
+ Initialise the printing backend. Called once at startup before the fork().
+****************************************************************************/
+
+BOOL print_backend_init(void)
+{
+ const char *sversion = "INFO/version";
+ pstring printing_path;
+ int services = lp_numservices();
+ int snum;
+
+ if (local_pid == sys_getpid())
+ return True;
+
+ unlink(lock_path("printing.tdb"));
+ pstrcpy(printing_path,lock_path("printing"));
+ mkdir(printing_path,0755);
+
+ local_pid = sys_getpid();
+
+ /* handle a Samba upgrade */
+
+ for (snum = 0; snum < services; snum++) {
+ struct tdb_print_db *pdb;
+ if (!lp_print_ok(snum))
+ continue;
+
+ pdb = get_print_db_byname(lp_const_servicename(snum));
+ if (!pdb)
+ continue;
+ if (tdb_lock_bystring(pdb->tdb, sversion, 0) == -1) {
+ DEBUG(0,("print_backend_init: Failed to open printer %s database\n", lp_const_servicename(snum) ));
+ release_print_db(pdb);
+ return False;
+ }
+ if (tdb_fetch_int32(pdb->tdb, sversion) != PRINT_DATABASE_VERSION) {
+ tdb_traverse(pdb->tdb, tdb_traverse_delete_fn, NULL);
+ tdb_store_int32(pdb->tdb, sversion, PRINT_DATABASE_VERSION);
+ }
+ tdb_unlock_bystring(pdb->tdb, sversion);
+ release_print_db(pdb);
+ }
+
+ close_all_print_db(); /* Don't leave any open. */
+
+ /* do NT print initialization... */
+ return nt_printing_init();
+}
+
+/****************************************************************************
+ Shut down printing backend. Called once at shutdown to close the tdb.
+****************************************************************************/
+
+void printing_end(void)
+{
+ close_all_print_db(); /* Don't leave any open. */
+}
+
+/****************************************************************************
+ Retrieve the set of printing functions for a given service. This allows
+ us to set the printer function table based on the value of the 'printing'
+ service parameter.
+
+ Use the generic interface as the default and only use cups interface only
+ when asked for (and only when supported)
+****************************************************************************/
+
+static struct printif *get_printer_fns( int snum )
+{
+ struct printif *printer_fns = &generic_printif;
+
+#ifdef HAVE_CUPS
+ if ( lp_printing(snum) == PRINT_CUPS ) {
+ printer_fns = &cups_printif;
+ }
+#endif /* HAVE_CUPS */
+
+ return printer_fns;
+}
+
+/****************************************************************************
+ Useful function to generate a tdb key.
+****************************************************************************/
+
+static TDB_DATA print_key(uint32 jobid)
+{
+ static uint32 j;
+ TDB_DATA ret;
+
+ j = jobid;
+ ret.dptr = (void *)&j;
+ ret.dsize = sizeof(j);
+ return ret;
+}
+
+/***********************************************************************
+ unpack a pjob from a tdb buffer
+***********************************************************************/
+
+int unpack_pjob( char* buf, int buflen, struct printjob *pjob )
+{
+ int len = 0;
+ int used;
+ uint32 pjpid, pjsysjob, pjfd, pjstarttime, pjstatus;
+ uint32 pjsize, pjpage_count, pjspooled, pjsmbjob;
+
+ if ( !buf || !pjob )
+ return -1;
+
+ len += tdb_unpack(buf+len, buflen-len, "dddddddddffff",
+ &pjpid,
+ &pjsysjob,
+ &pjfd,
+ &pjstarttime,
+ &pjstatus,
+ &pjsize,
+ &pjpage_count,
+ &pjspooled,
+ &pjsmbjob,
+ pjob->filename,
+ pjob->jobname,
+ pjob->user,
+ pjob->queuename);
+
+ if ( len == -1 )
+ return -1;
+
+ if ( (used = unpack_devicemode(&pjob->nt_devmode, buf+len, buflen-len)) == -1 )
+ return -1;
+
+ len += used;
+
+ pjob->pid = pjpid;
+ pjob->sysjob = pjsysjob;
+ pjob->fd = pjfd;
+ pjob->starttime = pjstarttime;
+ pjob->status = pjstatus;
+ pjob->size = pjsize;
+ pjob->page_count = pjpage_count;
+ pjob->spooled = pjspooled;
+ pjob->smbjob = pjsmbjob;
+
+ return len;
+
+}
+
+/****************************************************************************
+ Useful function to find a print job in the database.
+****************************************************************************/
+
+static struct printjob *print_job_find(int snum, uint32 jobid)
+{
+ static struct printjob pjob;
+ TDB_DATA ret;
+ struct tdb_print_db *pdb = get_print_db_byname(lp_const_servicename(snum));
+
+
+ if (!pdb)
+ return NULL;
+
+ ret = tdb_fetch(pdb->tdb, print_key(jobid));
+ release_print_db(pdb);
+
+ if (!ret.dptr)
+ return NULL;
+
+ if ( pjob.nt_devmode )
+ free_nt_devicemode( &pjob.nt_devmode );
+
+ ZERO_STRUCT( pjob );
+
+ if ( unpack_pjob( ret.dptr, ret.dsize, &pjob ) == -1 ) {
+ SAFE_FREE(ret.dptr);
+ return NULL;
+ }
+
+ SAFE_FREE(ret.dptr);
+ return &pjob;
+}
+
+/* Convert a unix jobid to a smb jobid */
+
+static uint32 sysjob_to_jobid_value;
+
+static int unixjob_traverse_fn(TDB_CONTEXT *the_tdb, TDB_DATA key,
+ TDB_DATA data, void *state)
+{
+ struct printjob *pjob;
+ int *sysjob = (int *)state;
+
+ if (!data.dptr || data.dsize == 0)
+ return 0;
+
+ pjob = (struct printjob *)data.dptr;
+ if (key.dsize != sizeof(uint32))
+ return 0;
+
+ if (*sysjob == pjob->sysjob) {
+ uint32 *jobid = (uint32 *)key.dptr;
+
+ sysjob_to_jobid_value = *jobid;
+ return 1;
+ }
+
+ return 0;
+}
+
+/****************************************************************************
+ This is a *horribly expensive call as we have to iterate through all the
+ current printer tdb's. Don't do this often ! JRA.
+****************************************************************************/
+
+uint32 sysjob_to_jobid(int unix_jobid)
+{
+ int services = lp_numservices();
+ int snum;
+
+ sysjob_to_jobid_value = (uint32)-1;
+
+ for (snum = 0; snum < services; snum++) {
+ struct tdb_print_db *pdb;
+ if (!lp_print_ok(snum))
+ continue;
+ pdb = get_print_db_byname(lp_const_servicename(snum));
+ if (pdb)
+ tdb_traverse(pdb->tdb, unixjob_traverse_fn, &unix_jobid);
+ release_print_db(pdb);
+ if (sysjob_to_jobid_value != (uint32)-1)
+ return sysjob_to_jobid_value;
+ }
+ return (uint32)-1;
+}
+
+/****************************************************************************
+ Send notifications based on what has changed after a pjob_store.
+****************************************************************************/
+
+static struct {
+ uint32 lpq_status;
+ uint32 spoolss_status;
+} lpq_to_spoolss_status_map[] = {
+ { LPQ_QUEUED, JOB_STATUS_QUEUED },
+ { LPQ_PAUSED, JOB_STATUS_PAUSED },
+ { LPQ_SPOOLING, JOB_STATUS_SPOOLING },
+ { LPQ_PRINTING, JOB_STATUS_PRINTING },
+ { LPQ_DELETING, JOB_STATUS_DELETING },
+ { LPQ_OFFLINE, JOB_STATUS_OFFLINE },
+ { LPQ_PAPEROUT, JOB_STATUS_PAPEROUT },
+ { LPQ_PRINTED, JOB_STATUS_PRINTED },
+ { LPQ_DELETED, JOB_STATUS_DELETED },
+ { LPQ_BLOCKED, JOB_STATUS_BLOCKED },
+ { LPQ_USER_INTERVENTION, JOB_STATUS_USER_INTERVENTION },
+ { -1, 0 }
+};
+
+/* Convert a lpq status value stored in printing.tdb into the
+ appropriate win32 API constant. */
+
+static uint32 map_to_spoolss_status(uint32 lpq_status)
+{
+ int i = 0;
+
+ while (lpq_to_spoolss_status_map[i].lpq_status != -1) {
+ if (lpq_to_spoolss_status_map[i].lpq_status == lpq_status)
+ return lpq_to_spoolss_status_map[i].spoolss_status;
+ i++;
+ }
+
+ return 0;
+}
+
+static void pjob_store_notify(int snum, uint32 jobid, struct printjob *old_data,
+ struct printjob *new_data)
+{
+ BOOL new_job = False;
+
+ if (!old_data)
+ new_job = True;
+
+ /* Notify the job name first */
+
+ if (new_job || !strequal(old_data->jobname, new_data->jobname))
+ notify_job_name(snum, jobid, new_data->jobname);
+
+ /* Job attributes that can't be changed. We only send
+ notification for these on a new job. */
+
+ if (new_job) {
+ notify_job_submitted(snum, jobid, new_data->starttime);
+ notify_job_username(snum, jobid, new_data->user);
+ }
+
+ /* Job attributes of a new job or attributes that can be
+ modified. */
+
+ if (new_job || old_data->status != new_data->status)
+ notify_job_status(snum, jobid, map_to_spoolss_status(new_data->status));
+
+ if (new_job || old_data->size != new_data->size)
+ notify_job_total_bytes(snum, jobid, new_data->size);
+
+ if (new_job || old_data->page_count != new_data->page_count)
+ notify_job_total_pages(snum, jobid, new_data->page_count);
+}
+
+/****************************************************************************
+ Store a job structure back to the database.
+****************************************************************************/
+
+static BOOL pjob_store(int snum, uint32 jobid, struct printjob *pjob)
+{
+ TDB_DATA old_data, new_data;
+ BOOL ret = False;
+ struct tdb_print_db *pdb = get_print_db_byname(lp_const_servicename(snum));
+ char *buf = NULL;
+ int len, newlen, buflen;
+
+
+ if (!pdb)
+ return False;
+
+ /* Get old data */
+
+ old_data = tdb_fetch(pdb->tdb, print_key(jobid));
+
+ /* Doh! Now we have to pack/unpack data since the NT_DEVICEMODE was added */
+
+ newlen = 0;
+
+ do {
+ len = 0;
+ buflen = newlen;
+ len += tdb_pack(buf+len, buflen-len, "dddddddddffff",
+ (uint32)pjob->pid,
+ (uint32)pjob->sysjob,
+ (uint32)pjob->fd,
+ (uint32)pjob->starttime,
+ (uint32)pjob->status,
+ (uint32)pjob->size,
+ (uint32)pjob->page_count,
+ (uint32)pjob->spooled,
+ (uint32)pjob->smbjob,
+ pjob->filename,
+ pjob->jobname,
+ pjob->user,
+ pjob->queuename);
+
+ len += pack_devicemode(pjob->nt_devmode, buf+len, buflen-len);
+
+ if (buflen != len) {
+ char *tb;
+
+ tb = (char *)Realloc(buf, len);
+ if (!tb) {
+ DEBUG(0,("pjob_store: failed to enlarge buffer!\n"));
+ goto done;
+ }
+ else
+ buf = tb;
+ newlen = len;
+ }
+ } while ( buflen != len );
+
+
+ /* Store new data */
+
+ new_data.dptr = buf;
+ new_data.dsize = len;
+ ret = (tdb_store(pdb->tdb, print_key(jobid), new_data, TDB_REPLACE) == 0);
+
+ release_print_db(pdb);
+
+ /* Send notify updates for what has changed */
+
+ if ( ret ) {
+ struct printjob old_pjob;
+
+ if ( old_data.dsize )
+ {
+ if ( unpack_pjob( old_data.dptr, old_data.dsize, &old_pjob ) != -1 )
+ {
+ pjob_store_notify( snum, jobid, &old_pjob , pjob );
+ free_nt_devicemode( &old_pjob.nt_devmode );
+ }
+ }
+ else {
+ /* new job */
+ pjob_store_notify( snum, jobid, NULL, pjob );
+ }
+ }
+
+done:
+ SAFE_FREE( old_data.dptr );
+ SAFE_FREE( buf );
+
+ return ret;
+}
+
+/****************************************************************************
+ Remove a job structure from the database.
+****************************************************************************/
+
+void pjob_delete(int snum, uint32 jobid)
+{
+ struct printjob *pjob = print_job_find(snum, jobid);
+ uint32 job_status = 0;
+ struct tdb_print_db *pdb = get_print_db_byname(lp_const_servicename(snum));
+
+ if (!pdb)
+ return;
+
+ if (!pjob) {
+ DEBUG(5, ("pjob_delete(): we were asked to delete nonexistent job %u\n",
+ (unsigned int)jobid));
+ release_print_db(pdb);
+ return;
+ }
+
+ /* Send a notification that a job has been deleted */
+
+ job_status = map_to_spoolss_status(pjob->status);
+
+ /* We must cycle through JOB_STATUS_DELETING and
+ JOB_STATUS_DELETED for the port monitor to delete the job
+ properly. */
+
+ job_status |= JOB_STATUS_DELETING;
+ notify_job_status(snum, jobid, job_status);
+
+ job_status |= JOB_STATUS_DELETED;
+ notify_job_status(snum, jobid, job_status);
+
+ /* Remove from printing.tdb */
+
+ tdb_delete(pdb->tdb, print_key(jobid));
+ release_print_db(pdb);
+ rap_jobid_delete(snum, jobid);
+}
+
+/****************************************************************************
+ Parse a file name from the system spooler to generate a jobid.
+****************************************************************************/
+
+static uint32 print_parse_jobid(char *fname)
+{
+ int jobid;
+
+ if (strncmp(fname,PRINT_SPOOL_PREFIX,strlen(PRINT_SPOOL_PREFIX)) != 0)
+ return (uint32)-1;
+ fname += strlen(PRINT_SPOOL_PREFIX);
+
+ jobid = atoi(fname);
+ if (jobid <= 0)
+ return (uint32)-1;
+
+ return (uint32)jobid;
+}
+
+/****************************************************************************
+ List a unix job in the print database.
+****************************************************************************/
+
+static void print_unix_job(int snum, print_queue_struct *q, uint32 jobid)
+{
+ struct printjob pj, *old_pj;
+
+ if (jobid == (uint32)-1)
+ jobid = q->job + UNIX_JOB_START;
+
+ /* Preserve the timestamp on an existing unix print job */
+
+ old_pj = print_job_find(snum, jobid);
+
+ ZERO_STRUCT(pj);
+
+ pj.pid = (pid_t)-1;
+ pj.sysjob = q->job;
+ pj.fd = -1;
+ pj.starttime = old_pj ? old_pj->starttime : q->time;
+ pj.status = q->status;
+ pj.size = q->size;
+ pj.spooled = True;
+ fstrcpy(pj.filename, old_pj ? old_pj->filename : "");
+ if (jobid < UNIX_JOB_START) {
+ pj.smbjob = True;
+ fstrcpy(pj.jobname, old_pj ? old_pj->jobname : "Remote Downlevel Document");
+ } else {
+ pj.smbjob = False;
+ fstrcpy(pj.jobname, old_pj ? old_pj->jobname : q->fs_file);
+ }
+ fstrcpy(pj.user, old_pj ? old_pj->user : q->fs_user);
+ fstrcpy(pj.queuename, old_pj ? old_pj->queuename : lp_const_servicename(snum));
+
+ pjob_store(snum, jobid, &pj);
+}
+
+
+struct traverse_struct {
+ print_queue_struct *queue;
+ int qcount, snum, maxcount, total_jobs;
+ time_t lpq_time;
+};
+
+/****************************************************************************
+ Utility fn to delete any jobs that are no longer active.
+****************************************************************************/
+
+static int traverse_fn_delete(TDB_CONTEXT *t, TDB_DATA key, TDB_DATA data, void *state)
+{
+ struct traverse_struct *ts = (struct traverse_struct *)state;
+ struct printjob pjob;
+ uint32 jobid;
+ int i = 0;
+
+ if ( key.dsize != sizeof(jobid) )
+ return 0;
+
+ jobid = IVAL(key.dptr, 0);
+ if ( unpack_pjob( data.dptr, data.dsize, &pjob ) == -1 )
+ return 0;
+ free_nt_devicemode( &pjob.nt_devmode );
+
+
+ if (ts->snum != lp_servicenumber(pjob.queuename)) {
+ /* this isn't for the queue we are looking at - this cannot happen with the split tdb's. JRA */
+ return 0;
+ }
+
+ if (!pjob.smbjob) {
+ /* remove a unix job if it isn't in the system queue any more */
+
+ for (i=0;i<ts->qcount;i++) {
+ uint32 u_jobid = (ts->queue[i].job + UNIX_JOB_START);
+ if (jobid == u_jobid)
+ break;
+ }
+ if (i == ts->qcount) {
+ DEBUG(10,("traverse_fn_delete: pjob %u deleted due to !smbjob\n",
+ (unsigned int)jobid ));
+ pjob_delete(ts->snum, jobid);
+ return 0;
+ }
+
+ /* need to continue the the bottom of the function to
+ save the correct attributes */
+ }
+
+ /* maybe it hasn't been spooled yet */
+ if (!pjob.spooled) {
+ /* if a job is not spooled and the process doesn't
+ exist then kill it. This cleans up after smbd
+ deaths */
+ if (!process_exists(pjob.pid)) {
+ DEBUG(10,("traverse_fn_delete: pjob %u deleted due to !process_exists (%u)\n",
+ (unsigned int)jobid, (unsigned int)pjob.pid ));
+ pjob_delete(ts->snum, jobid);
+ } else
+ ts->total_jobs++;
+ return 0;
+ }
+
+ /* this check only makes sense for jobs submitted from Windows clients */
+
+ if ( pjob.smbjob ) {
+ for (i=0;i<ts->qcount;i++) {
+ uint32 curr_jobid = print_parse_jobid(ts->queue[i].fs_file);
+ if (jobid == curr_jobid)
+ break;
+ }
+ }
+
+ /* The job isn't in the system queue - we have to assume it has
+ completed, so delete the database entry. */
+
+ if (i == ts->qcount) {
+
+ /* A race can occur between the time a job is spooled and
+ when it appears in the lpq output. This happens when
+ the job is added to printing.tdb when another smbd
+ running print_queue_update() has completed a lpq and
+ is currently traversing the printing tdb and deleting jobs.
+ Don't delete the job if it was submitted after the lpq_time. */
+
+ if (pjob.starttime < ts->lpq_time) {
+ DEBUG(10,("traverse_fn_delete: pjob %u deleted due to pjob.starttime (%u) < ts->lpq_time (%u)\n",
+ (unsigned int)jobid,
+ (unsigned int)pjob.starttime,
+ (unsigned int)ts->lpq_time ));
+ pjob_delete(ts->snum, jobid);
+ } else
+ ts->total_jobs++;
+ return 0;
+ }
+
+ /* Save the pjob attributes we will store. */
+ /* FIXME!!! This is the only place where queue->job
+ represents the SMB jobid --jerry */
+ ts->queue[i].job = jobid;
+ ts->queue[i].size = pjob.size;
+ ts->queue[i].page_count = pjob.page_count;
+ ts->queue[i].status = pjob.status;
+ ts->queue[i].priority = 1;
+ ts->queue[i].time = pjob.starttime;
+ fstrcpy(ts->queue[i].fs_user, pjob.user);
+ fstrcpy(ts->queue[i].fs_file, pjob.jobname);
+
+ ts->total_jobs++;
+
+ return 0;
+}
+
+/****************************************************************************
+ Check if the print queue has been updated recently enough.
+****************************************************************************/
+
+static void print_cache_flush(int snum)
+{
+ fstring key;
+ const char *printername = lp_const_servicename(snum);
+ struct tdb_print_db *pdb = get_print_db_byname(printername);
+
+ if (!pdb)
+ return;
+ slprintf(key, sizeof(key)-1, "CACHE/%s", printername);
+ tdb_store_int32(pdb->tdb, key, -1);
+ release_print_db(pdb);
+}
+
+/****************************************************************************
+ Check if someone already thinks they are doing the update.
+****************************************************************************/
+
+static pid_t get_updating_pid(fstring printer_name)
+{
+ fstring keystr;
+ TDB_DATA data, key;
+ pid_t updating_pid;
+ struct tdb_print_db *pdb = get_print_db_byname(printer_name);
+
+ if (!pdb)
+ return (pid_t)-1;
+ slprintf(keystr, sizeof(keystr)-1, "UPDATING/%s", printer_name);
+ key.dptr = keystr;
+ key.dsize = strlen(keystr);
+
+ data = tdb_fetch(pdb->tdb, key);
+ release_print_db(pdb);
+ if (!data.dptr || data.dsize != sizeof(pid_t)) {
+ SAFE_FREE(data.dptr);
+ return (pid_t)-1;
+ }
+
+ updating_pid = IVAL(data.dptr, 0);
+ SAFE_FREE(data.dptr);
+
+ if (process_exists(updating_pid))
+ return updating_pid;
+
+ return (pid_t)-1;
+}
+
+/****************************************************************************
+ Set the fact that we're doing the update, or have finished doing the update
+ in the tdb.
+****************************************************************************/
+
+static void set_updating_pid(const fstring printer_name, BOOL delete)
+{
+ fstring keystr;
+ TDB_DATA key;
+ TDB_DATA data;
+ pid_t updating_pid = sys_getpid();
+ uint8 buffer[4];
+
+ struct tdb_print_db *pdb = get_print_db_byname(printer_name);
+
+ if (!pdb)
+ return;
+
+ slprintf(keystr, sizeof(keystr)-1, "UPDATING/%s", printer_name);
+ key.dptr = keystr;
+ key.dsize = strlen(keystr);
+
+ if (delete) {
+ tdb_delete(pdb->tdb, key);
+ release_print_db(pdb);
+ return;
+ }
+
+ SIVAL( buffer, 0, updating_pid);
+ data.dptr = (void *)buffer;
+ data.dsize = 4; /* we always assume this is a 4 byte value */
+
+ tdb_store(pdb->tdb, key, data, TDB_REPLACE);
+ release_print_db(pdb);
+}
+
+/****************************************************************************
+ Sort print jobs by submittal time.
+****************************************************************************/
+
+static int printjob_comp(print_queue_struct *j1, print_queue_struct *j2)
+{
+ /* Silly cases */
+
+ if (!j1 && !j2)
+ return 0;
+ if (!j1)
+ return -1;
+ if (!j2)
+ return 1;
+
+ /* Sort on job start time */
+
+ if (j1->time == j2->time)
+ return 0;
+ return (j1->time > j2->time) ? 1 : -1;
+}
+
+/****************************************************************************
+ Store the sorted queue representation for later portmon retrieval.
+****************************************************************************/
+
+static void store_queue_struct(struct tdb_print_db *pdb, struct traverse_struct *pts)
+{
+ TDB_DATA data, key;
+ int max_reported_jobs = lp_max_reported_jobs(pts->snum);
+ print_queue_struct *queue = pts->queue;
+ size_t len;
+ size_t i;
+ uint qcount;
+
+ if (max_reported_jobs && (max_reported_jobs < pts->qcount))
+ pts->qcount = max_reported_jobs;
+ qcount = pts->qcount;
+
+ /* Work out the size. */
+ data.dsize = 0;
+ data.dsize += tdb_pack(NULL, 0, "d", qcount);
+
+ for (i = 0; i < pts->qcount; i++) {
+ data.dsize += tdb_pack(NULL, 0, "ddddddff",
+ (uint32)queue[i].job,
+ (uint32)queue[i].size,
+ (uint32)queue[i].page_count,
+ (uint32)queue[i].status,
+ (uint32)queue[i].priority,
+ (uint32)queue[i].time,
+ queue[i].fs_user,
+ queue[i].fs_file);
+ }
+
+ if ((data.dptr = malloc(data.dsize)) == NULL)
+ return;
+
+ len = 0;
+ len += tdb_pack(data.dptr + len, data.dsize - len, "d", qcount);
+ for (i = 0; i < pts->qcount; i++) {
+ len += tdb_pack(data.dptr + len, data.dsize - len, "ddddddff",
+ (uint32)queue[i].job,
+ (uint32)queue[i].size,
+ (uint32)queue[i].page_count,
+ (uint32)queue[i].status,
+ (uint32)queue[i].priority,
+ (uint32)queue[i].time,
+ queue[i].fs_user,
+ queue[i].fs_file);
+ }
+
+ key.dptr = "INFO/linear_queue_array";
+ key.dsize = strlen(key.dptr);
+ tdb_store(pdb->tdb, key, data, TDB_REPLACE);
+ SAFE_FREE(data.dptr);
+ return;
+}
+
+static TDB_DATA get_jobs_changed_data(struct tdb_print_db *pdb)
+{
+ TDB_DATA data, key;
+
+ key.dptr = "INFO/jobs_changed";
+ key.dsize = strlen(key.dptr);
+ ZERO_STRUCT(data);
+
+ data = tdb_fetch(pdb->tdb, key);
+ if (data.dptr == NULL || data.dsize == 0 || (data.dsize % 4 != 0)) {
+ SAFE_FREE(data.dptr);
+ ZERO_STRUCT(data);
+ }
+
+ return data;
+}
+
+static void check_job_changed(int snum, TDB_DATA data, uint32 jobid)
+{
+ unsigned int i;
+ unsigned int job_count = data.dsize / 4;
+
+ for (i = 0; i < job_count; i++) {
+ uint32 ch_jobid;
+
+ ch_jobid = IVAL(data.dptr, i*4);
+ if (ch_jobid == jobid)
+ remove_from_jobs_changed(snum, jobid);
+ }
+}
+
+/****************************************************************************
+ Update the internal database from the system print queue for a queue.
+****************************************************************************/
+
+static void print_queue_update(int snum)
+{
+ int i, qcount;
+ print_queue_struct *queue = NULL;
+ print_status_struct status;
+ print_status_struct old_status;
+ struct printjob *pjob;
+ struct traverse_struct tstruct;
+ fstring keystr, printer_name, cachestr;
+ TDB_DATA data, key;
+ TDB_DATA jcdata;
+ struct tdb_print_db *pdb;
+ struct printif *current_printif = get_printer_fns( snum );
+
+ fstrcpy(printer_name, lp_const_servicename(snum));
+ pdb = get_print_db_byname(printer_name);
+ if (!pdb)
+ return;
+
+ /*
+ * Check to see if someone else is doing this update.
+ * This is essentially a mutex on the update.
+ */
+
+ if (get_updating_pid(printer_name) != -1) {
+ release_print_db(pdb);
+ return;
+ }
+
+ /* Lock the queue for the database update */
+
+ slprintf(keystr, sizeof(keystr) - 1, "LOCK/%s", printer_name);
+ /* Only wait 10 seconds for this. */
+ if (tdb_lock_bystring(pdb->tdb, keystr, 10) == -1) {
+ DEBUG(0,("print_queue_update: Failed to lock printer %s database\n", printer_name));
+ release_print_db(pdb);
+ return;
+ }
+
+ /*
+ * Ensure that no one else got in here.
+ * If the updating pid is still -1 then we are
+ * the winner.
+ */
+
+ if (get_updating_pid(printer_name) != -1) {
+ /*
+ * Someone else is doing the update, exit.
+ */
+ tdb_unlock_bystring(pdb->tdb, keystr);
+ release_print_db(pdb);
+ return;
+ }
+
+ /*
+ * We're going to do the update ourselves.
+ */
+
+ /* Tell others we're doing the update. */
+ set_updating_pid(printer_name, False);
+
+ /*
+ * Allow others to enter and notice we're doing
+ * the update.
+ */
+
+ tdb_unlock_bystring(pdb->tdb, keystr);
+
+ /*
+ * Update the cache time FIRST ! Stops others even
+ * attempting to get the lock and doing this
+ * if the lpq takes a long time.
+ */
+
+ slprintf(cachestr, sizeof(cachestr)-1, "CACHE/%s", printer_name);
+ tdb_store_int32(pdb->tdb, cachestr, (int)time(NULL));
+
+ /* get the current queue using the appropriate interface */
+ ZERO_STRUCT(status);
+
+ qcount = (*(current_printif->queue_get))(snum, &queue, &status);
+
+ DEBUG(3, ("%d job%s in queue for %s\n", qcount, (qcount != 1) ?
+ "s" : "", printer_name));
+
+ /* Sort the queue by submission time otherwise they are displayed
+ in hash order. */
+
+ qsort(queue, qcount, sizeof(print_queue_struct),
+ QSORT_CAST(printjob_comp));
+
+ /*
+ any job in the internal database that is marked as spooled
+ and doesn't exist in the system queue is considered finished
+ and removed from the database
+
+ any job in the system database but not in the internal database
+ is added as a unix job
+
+ fill in any system job numbers as we go
+ */
+
+ jcdata = get_jobs_changed_data(pdb);
+
+ for (i=0; i<qcount; i++) {
+ uint32 jobid = print_parse_jobid(queue[i].fs_file);
+
+ if (jobid == (uint32)-1) {
+ /* assume its a unix print job */
+ print_unix_job(snum, &queue[i], jobid);
+ continue;
+ }
+
+ /* we have an active SMB print job - update its status */
+ pjob = print_job_find(snum, jobid);
+ if (!pjob) {
+ /* err, somethings wrong. Probably smbd was restarted
+ with jobs in the queue. All we can do is treat them
+ like unix jobs. Pity. */
+ print_unix_job(snum, &queue[i], jobid);
+ continue;
+ }
+
+ pjob->sysjob = queue[i].job;
+ pjob->status = queue[i].status;
+ pjob_store(snum, jobid, pjob);
+ check_job_changed(snum, jcdata, jobid);
+ }
+
+ SAFE_FREE(jcdata.dptr);
+
+ /* now delete any queued entries that don't appear in the
+ system queue */
+ tstruct.queue = queue;
+ tstruct.qcount = qcount;
+ tstruct.snum = snum;
+ tstruct.total_jobs = 0;
+ tstruct.lpq_time = time(NULL);
+
+ tdb_traverse(pdb->tdb, traverse_fn_delete, (void *)&tstruct);
+
+ /* Store the linearised queue, max jobs only. */
+ store_queue_struct(pdb, &tstruct);
+
+ SAFE_FREE(tstruct.queue);
+
+ DEBUG(10,("print_queue_update: printer %s INFO/total_jobs = %d\n",
+ printer_name, tstruct.total_jobs ));
+
+ tdb_store_int32(pdb->tdb, "INFO/total_jobs", tstruct.total_jobs);
+
+ get_queue_status(snum, &old_status);
+ if (old_status.qcount != qcount)
+ DEBUG(10,("print_queue_update: queue status change %d jobs -> %d jobs for printer %s\n",
+ old_status.qcount, qcount, printer_name ));
+
+ /* store the new queue status structure */
+ slprintf(keystr, sizeof(keystr)-1, "STATUS/%s", printer_name);
+ key.dptr = keystr;
+ key.dsize = strlen(keystr);
+
+ status.qcount = qcount;
+ data.dptr = (void *)&status;
+ data.dsize = sizeof(status);
+ tdb_store(pdb->tdb, key, data, TDB_REPLACE);
+
+ /*
+ * Update the cache time again. We want to do this call
+ * as little as possible...
+ */
+
+ slprintf(keystr, sizeof(keystr)-1, "CACHE/%s", printer_name);
+ tdb_store_int32(pdb->tdb, keystr, (int32)time(NULL));
+
+ /* Delete our pid from the db. */
+ set_updating_pid(printer_name, True);
+ release_print_db(pdb);
+}
+
+/****************************************************************************
+ Create/Update an entry in the print tdb that will allow us to send notify
+ updates only to interested smbd's.
+****************************************************************************/
+
+BOOL print_notify_register_pid(int snum)
+{
+ TDB_DATA data;
+ struct tdb_print_db *pdb = NULL;
+ TDB_CONTEXT *tdb = NULL;
+ const char *printername;
+ uint32 mypid = (uint32)sys_getpid();
+ BOOL ret = False;
+ size_t i;
+
+ /* if (snum == -1), then the change notify request was
+ on a print server handle and we need to register on
+ all print queus */
+
+ if (snum == -1)
+ {
+ int num_services = lp_numservices();
+ int idx;
+
+ for ( idx=0; idx<num_services; idx++ ) {
+ if (lp_snum_ok(idx) && lp_print_ok(idx) )
+ print_notify_register_pid(idx);
+ }
+
+ return True;
+ }
+ else /* register for a specific printer */
+ {
+ printername = lp_const_servicename(snum);
+ pdb = get_print_db_byname(printername);
+ if (!pdb)
+ return False;
+ tdb = pdb->tdb;
+ }
+
+ if (tdb_lock_bystring(tdb, NOTIFY_PID_LIST_KEY, 10) == -1) {
+ DEBUG(0,("print_notify_register_pid: Failed to lock printer %s\n",
+ printername));
+ if (pdb)
+ release_print_db(pdb);
+ return False;
+ }
+
+ data = get_printer_notify_pid_list( tdb, printername, True );
+
+ /* Add ourselves and increase the refcount. */
+
+ for (i = 0; i < data.dsize; i += 8) {
+ if (IVAL(data.dptr,i) == mypid) {
+ uint32 new_refcount = IVAL(data.dptr, i+4) + 1;
+ SIVAL(data.dptr, i+4, new_refcount);
+ break;
+ }
+ }
+
+ if (i == data.dsize) {
+ /* We weren't in the list. Realloc. */
+ data.dptr = Realloc(data.dptr, data.dsize + 8);
+ if (!data.dptr) {
+ DEBUG(0,("print_notify_register_pid: Relloc fail for printer %s\n",
+ printername));
+ goto done;
+ }
+ data.dsize += 8;
+ SIVAL(data.dptr,data.dsize - 8,mypid);
+ SIVAL(data.dptr,data.dsize - 4,1); /* Refcount. */
+ }
+
+ /* Store back the record. */
+ if (tdb_store_bystring(tdb, NOTIFY_PID_LIST_KEY, data, TDB_REPLACE) == -1) {
+ DEBUG(0,("print_notify_register_pid: Failed to update pid \
+list for printer %s\n", printername));
+ goto done;
+ }
+
+ ret = True;
+
+ done:
+
+ tdb_unlock_bystring(tdb, NOTIFY_PID_LIST_KEY);
+ if (pdb)
+ release_print_db(pdb);
+ SAFE_FREE(data.dptr);
+ return ret;
+}
+
+/****************************************************************************
+ Update an entry in the print tdb that will allow us to send notify
+ updates only to interested smbd's.
+****************************************************************************/
+
+BOOL print_notify_deregister_pid(int snum)
+{
+ TDB_DATA data;
+ struct tdb_print_db *pdb = NULL;
+ TDB_CONTEXT *tdb = NULL;
+ const char *printername;
+ uint32 mypid = (uint32)sys_getpid();
+ size_t i;
+ BOOL ret = False;
+
+ /* if ( snum == -1 ), we are deregister a print server handle
+ which means to deregister on all print queues */
+
+ if (snum == -1)
+ {
+ int num_services = lp_numservices();
+ int idx;
+
+ for ( idx=0; idx<num_services; idx++ ) {
+ if ( lp_snum_ok(idx) && lp_print_ok(idx) )
+ print_notify_deregister_pid(idx);
+ }
+
+ return True;
+ }
+ else /* deregister a specific printer */
+ {
+ printername = lp_const_servicename(snum);
+ pdb = get_print_db_byname(printername);
+ if (!pdb)
+ return False;
+ tdb = pdb->tdb;
+ }
+
+ if (tdb_lock_bystring(tdb, NOTIFY_PID_LIST_KEY, 10) == -1) {
+ DEBUG(0,("print_notify_register_pid: Failed to lock \
+printer %s database\n", printername));
+ if (pdb)
+ release_print_db(pdb);
+ return False;
+ }
+
+ data = get_printer_notify_pid_list( tdb, printername, True );
+
+ /* Reduce refcount. Remove ourselves if zero. */
+
+ for (i = 0; i < data.dsize; ) {
+ if (IVAL(data.dptr,i) == mypid) {
+ uint32 refcount = IVAL(data.dptr, i+4);
+
+ refcount--;
+
+ if (refcount == 0) {
+ if (data.dsize - i > 8)
+ memmove( &data.dptr[i], &data.dptr[i+8], data.dsize - i - 8);
+ data.dsize -= 8;
+ continue;
+ }
+ SIVAL(data.dptr, i+4, refcount);
+ }
+
+ i += 8;
+ }
+
+ if (data.dsize == 0)
+ SAFE_FREE(data.dptr);
+
+ /* Store back the record. */
+ if (tdb_store_bystring(tdb, NOTIFY_PID_LIST_KEY, data, TDB_REPLACE) == -1) {
+ DEBUG(0,("print_notify_register_pid: Failed to update pid \
+list for printer %s\n", printername));
+ goto done;
+ }
+
+ ret = True;
+
+ done:
+
+ tdb_unlock_bystring(tdb, NOTIFY_PID_LIST_KEY);
+ if (pdb)
+ release_print_db(pdb);
+ SAFE_FREE(data.dptr);
+ return ret;
+}
+
+/****************************************************************************
+ Check if a jobid is valid. It is valid if it exists in the database.
+****************************************************************************/
+
+BOOL print_job_exists(int snum, uint32 jobid)
+{
+ struct tdb_print_db *pdb = get_print_db_byname(lp_const_servicename(snum));
+ BOOL ret;
+
+ if (!pdb)
+ return False;
+ ret = tdb_exists(pdb->tdb, print_key(jobid));
+ release_print_db(pdb);
+ return ret;
+}
+
+/****************************************************************************
+ Give the fd used for a jobid.
+****************************************************************************/
+
+int print_job_fd(int snum, uint32 jobid)
+{
+ struct printjob *pjob = print_job_find(snum, jobid);
+ if (!pjob)
+ return -1;
+ /* don't allow another process to get this info - it is meaningless */
+ if (pjob->pid != local_pid)
+ return -1;
+ return pjob->fd;
+}
+
+/****************************************************************************
+ Give the filename used for a jobid.
+ Only valid for the process doing the spooling and when the job
+ has not been spooled.
+****************************************************************************/
+
+char *print_job_fname(int snum, uint32 jobid)
+{
+ struct printjob *pjob = print_job_find(snum, jobid);
+ if (!pjob || pjob->spooled || pjob->pid != local_pid)
+ return NULL;
+ return pjob->filename;
+}
+
+
+/****************************************************************************
+ Give the filename used for a jobid.
+ Only valid for the process doing the spooling and when the job
+ has not been spooled.
+****************************************************************************/
+
+NT_DEVICEMODE *print_job_devmode(int snum, uint32 jobid)
+{
+ struct printjob *pjob = print_job_find(snum, jobid);
+
+ if ( !pjob )
+ return NULL;
+
+ return pjob->nt_devmode;
+}
+
+/****************************************************************************
+ Set the place in the queue for a job.
+****************************************************************************/
+
+BOOL print_job_set_place(int snum, uint32 jobid, int place)
+{
+ DEBUG(2,("print_job_set_place not implemented yet\n"));
+ return False;
+}
+
+/****************************************************************************
+ Set the name of a job. Only possible for owner.
+****************************************************************************/
+
+BOOL print_job_set_name(int snum, uint32 jobid, char *name)
+{
+ struct printjob *pjob = print_job_find(snum, jobid);
+ if (!pjob || pjob->pid != local_pid)
+ return False;
+
+ fstrcpy(pjob->jobname, name);
+ return pjob_store(snum, jobid, pjob);
+}
+
+/***************************************************************************
+ Remove a jobid from the 'jobs changed' list.
+***************************************************************************/
+
+static BOOL remove_from_jobs_changed(int snum, uint32 jobid)
+{
+ const char *printername = lp_const_servicename(snum);
+ struct tdb_print_db *pdb = get_print_db_byname(printername);
+ TDB_DATA data, key;
+ size_t job_count, i;
+ BOOL ret = False;
+ BOOL gotlock = False;
+
+ key.dptr = "INFO/jobs_changed";
+ key.dsize = strlen(key.dptr);
+ ZERO_STRUCT(data);
+
+ if (tdb_chainlock_with_timeout(pdb->tdb, key, 5) == -1)
+ goto out;
+
+ gotlock = True;
+
+ data = tdb_fetch(pdb->tdb, key);
+
+ if (data.dptr == NULL || data.dsize == 0 || (data.dsize % 4 != 0))
+ goto out;
+
+ job_count = data.dsize / 4;
+ for (i = 0; i < job_count; i++) {
+ uint32 ch_jobid;
+
+ ch_jobid = IVAL(data.dptr, i*4);
+ if (ch_jobid == jobid) {
+ if (i < job_count -1 )
+ memmove(data.dptr + (i*4), data.dptr + (i*4) + 4, (job_count - i - 1)*4 );
+ data.dsize -= 4;
+ if (tdb_store(pdb->tdb, key, data, TDB_REPLACE) == -1)
+ goto out;
+ break;
+ }
+ }
+
+ ret = True;
+ out:
+
+ if (gotlock)
+ tdb_chainunlock(pdb->tdb, key);
+ SAFE_FREE(data.dptr);
+ release_print_db(pdb);
+ if (ret)
+ DEBUG(10,("remove_from_jobs_changed: removed jobid %u\n", (unsigned int)jobid ));
+ else
+ DEBUG(10,("remove_from_jobs_changed: Failed to remove jobid %u\n", (unsigned int)jobid ));
+ return ret;
+}
+
+/****************************************************************************
+ Delete a print job - don't update queue.
+****************************************************************************/
+
+static BOOL print_job_delete1(int snum, uint32 jobid)
+{
+ struct printjob *pjob = print_job_find(snum, jobid);
+ int result = 0;
+ struct printif *current_printif = get_printer_fns( snum );
+
+ if (!pjob)
+ return False;
+
+ /*
+ * If already deleting just return.
+ */
+
+ if (pjob->status == LPQ_DELETING)
+ return True;
+
+ /* Hrm - we need to be able to cope with deleting a job before it
+ has reached the spooler. */
+
+ if (pjob->sysjob == -1) {
+ DEBUG(5, ("attempt to delete job %u not seen by lpr\n", (unsigned int)jobid));
+ }
+
+ /* Set the tdb entry to be deleting. */
+
+ pjob->status = LPQ_DELETING;
+ pjob_store(snum, jobid, pjob);
+
+ if (pjob->spooled && pjob->sysjob != -1)
+ result = (*(current_printif->job_delete))(snum, pjob);
+ else
+ remove_from_jobs_changed(snum, jobid);
+
+ /* Delete the tdb entry if the delete succeeded or the job hasn't
+ been spooled. */
+
+ if (result == 0) {
+ const char *printername = lp_const_servicename(snum);
+ struct tdb_print_db *pdb = get_print_db_byname(printername);
+ int njobs = 1;
+
+ if (!pdb)
+ return False;
+ pjob_delete(snum, jobid);
+ /* Ensure we keep a rough count of the number of total jobs... */
+ tdb_change_int32_atomic(pdb->tdb, "INFO/total_jobs", &njobs, -1);
+ release_print_db(pdb);
+ }
+
+ return (result == 0);
+}
+
+/****************************************************************************
+ Return true if the current user owns the print job.
+****************************************************************************/
+
+static BOOL is_owner(struct current_user *user, int snum, uint32 jobid)
+{
+ struct printjob *pjob = print_job_find(snum, jobid);
+ user_struct *vuser;
+
+ if (!pjob || !user)
+ return False;
+
+ if ((vuser = get_valid_user_struct(user->vuid)) != NULL) {
+ return strequal(pjob->user, vuser->user.smb_name);
+ } else {
+ return strequal(pjob->user, uidtoname(user->uid));
+ }
+}
+
+/****************************************************************************
+ Delete a print job.
+****************************************************************************/
+
+BOOL print_job_delete(struct current_user *user, int snum, uint32 jobid, WERROR *errcode)
+{
+ BOOL owner, deleted;
+ char *fname;
+
+ *errcode = WERR_OK;
+
+ owner = is_owner(user, snum, jobid);
+
+ /* Check access against security descriptor or whether the user
+ owns their job. */
+
+ if (!owner &&
+ !print_access_check(user, snum, JOB_ACCESS_ADMINISTER)) {
+ DEBUG(3, ("delete denied by security descriptor\n"));
+ *errcode = WERR_ACCESS_DENIED;
+
+ /* BEGIN_ADMIN_LOG */
+ sys_adminlog( LOG_ERR,
+ "Permission denied-- user not allowed to delete, \
+pause, or resume print job. User name: %s. Printer name: %s.",
+ uidtoname(user->uid), PRINTERNAME(snum) );
+ /* END_ADMIN_LOG */
+
+ return False;
+ }
+
+ /*
+ * get the spooled filename of the print job
+ * if this works, then the file has not been spooled
+ * to the underlying print system. Just delete the
+ * spool file & return.
+ */
+
+ if ( (fname = print_job_fname( snum, jobid )) != NULL )
+ {
+ /* remove the spool file */
+ DEBUG(10,("print_job_delete: Removing spool file [%s]\n", fname ));
+ if ( unlink( fname ) == -1 ) {
+ *errcode = map_werror_from_unix(errno);
+ return False;
+ }
+
+ return True;
+ }
+
+ if (!print_job_delete1(snum, jobid)) {
+ *errcode = WERR_ACCESS_DENIED;
+ return False;
+ }
+
+ /* force update the database and say the delete failed if the
+ job still exists */
+
+ print_queue_update(snum);
+
+ deleted = !print_job_exists(snum, jobid);
+ if ( !deleted )
+ *errcode = WERR_ACCESS_DENIED;
+
+ return deleted;
+}
+
+/****************************************************************************
+ Pause a job.
+****************************************************************************/
+
+BOOL print_job_pause(struct current_user *user, int snum, uint32 jobid, WERROR *errcode)
+{
+ struct printjob *pjob = print_job_find(snum, jobid);
+ int ret = -1;
+ struct printif *current_printif = get_printer_fns( snum );
+
+ if (!pjob || !user)
+ return False;
+
+ if (!pjob->spooled || pjob->sysjob == -1)
+ return False;
+
+ if (!is_owner(user, snum, jobid) &&
+ !print_access_check(user, snum, JOB_ACCESS_ADMINISTER)) {
+ DEBUG(3, ("pause denied by security descriptor\n"));
+
+ /* BEGIN_ADMIN_LOG */
+ sys_adminlog( LOG_ERR,
+ "Permission denied-- user not allowed to delete, \
+pause, or resume print job. User name: %s. Printer name: %s.",
+ uidtoname(user->uid), PRINTERNAME(snum) );
+ /* END_ADMIN_LOG */
+
+ *errcode = WERR_ACCESS_DENIED;
+ return False;
+ }
+
+ /* need to pause the spooled entry */
+ ret = (*(current_printif->job_pause))(snum, pjob);
+
+ if (ret != 0) {
+ *errcode = WERR_INVALID_PARAM;
+ return False;
+ }
+
+ /* force update the database */
+ print_cache_flush(snum);
+
+ /* Send a printer notify message */
+
+ notify_job_status(snum, jobid, JOB_STATUS_PAUSED);
+
+ /* how do we tell if this succeeded? */
+
+ return True;
+}
+
+/****************************************************************************
+ Resume a job.
+****************************************************************************/
+
+BOOL print_job_resume(struct current_user *user, int snum, uint32 jobid, WERROR *errcode)
+{
+ struct printjob *pjob = print_job_find(snum, jobid);
+ int ret;
+ struct printif *current_printif = get_printer_fns( snum );
+
+ if (!pjob || !user)
+ return False;
+
+ if (!pjob->spooled || pjob->sysjob == -1)
+ return False;
+
+ if (!is_owner(user, snum, jobid) &&
+ !print_access_check(user, snum, JOB_ACCESS_ADMINISTER)) {
+ DEBUG(3, ("resume denied by security descriptor\n"));
+ *errcode = WERR_ACCESS_DENIED;
+
+ /* BEGIN_ADMIN_LOG */
+ sys_adminlog( LOG_ERR,
+ "Permission denied-- user not allowed to delete, \
+pause, or resume print job. User name: %s. Printer name: %s.",
+ uidtoname(user->uid), PRINTERNAME(snum) );
+ /* END_ADMIN_LOG */
+ return False;
+ }
+
+ ret = (*(current_printif->job_resume))(snum, pjob);
+
+ if (ret != 0) {
+ *errcode = WERR_INVALID_PARAM;
+ return False;
+ }
+
+ /* force update the database */
+ print_cache_flush(snum);
+
+ /* Send a printer notify message */
+
+ notify_job_status(snum, jobid, JOB_STATUS_QUEUED);
+
+ return True;
+}
+
+/****************************************************************************
+ Write to a print file.
+****************************************************************************/
+
+int print_job_write(int snum, uint32 jobid, const char *buf, int size)
+{
+ int return_code;
+ struct printjob *pjob = print_job_find(snum, jobid);
+
+ if (!pjob)
+ return -1;
+ /* don't allow another process to get this info - it is meaningless */
+ if (pjob->pid != local_pid)
+ return -1;
+
+ return_code = write(pjob->fd, buf, size);
+ if (return_code>0) {
+ pjob->size += size;
+ pjob_store(snum, jobid, pjob);
+ }
+ return return_code;
+}
+
+/****************************************************************************
+ Check if the print queue has been updated recently enough.
+****************************************************************************/
+
+static BOOL print_cache_expired(int snum)
+{
+ fstring key;
+ time_t last_qscan_time, time_now = time(NULL);
+ const char *printername = lp_const_servicename(snum);
+ struct tdb_print_db *pdb = get_print_db_byname(printername);
+
+ if (!pdb)
+ return False;
+
+ slprintf(key, sizeof(key), "CACHE/%s", printername);
+ last_qscan_time = (time_t)tdb_fetch_int32(pdb->tdb, key);
+
+ /*
+ * Invalidate the queue for 3 reasons.
+ * (1). last queue scan time == -1.
+ * (2). Current time - last queue scan time > allowed cache time.
+ * (3). last queue scan time > current time + MAX_CACHE_VALID_TIME (1 hour by default).
+ * This last test picks up machines for which the clock has been moved
+ * forward, an lpq scan done and then the clock moved back. Otherwise
+ * that last lpq scan would stay around for a loooong loooong time... :-). JRA.
+ */
+
+ if (last_qscan_time == ((time_t)-1) || (time_now - last_qscan_time) >= lp_lpqcachetime() ||
+ last_qscan_time > (time_now + MAX_CACHE_VALID_TIME)) {
+ DEBUG(3, ("print cache expired for queue %s \
+(last_qscan_time = %d, time now = %d, qcachetime = %d)\n", printername,
+ (int)last_qscan_time, (int)time_now, (int)lp_lpqcachetime() ));
+ release_print_db(pdb);
+ return True;
+ }
+ release_print_db(pdb);
+ return False;
+}
+
+/****************************************************************************
+ Get the queue status - do not update if db is out of date.
+****************************************************************************/
+
+static int get_queue_status(int snum, print_status_struct *status)
+{
+ fstring keystr;
+ TDB_DATA data, key;
+ const char *printername = lp_const_servicename(snum);
+ struct tdb_print_db *pdb = get_print_db_byname(printername);
+ int len;
+
+ if (!pdb)
+ return 0;
+
+ if (status) {
+ ZERO_STRUCTP(status);
+ slprintf(keystr, sizeof(keystr)-1, "STATUS/%s", printername);
+ key.dptr = keystr;
+ key.dsize = strlen(keystr);
+ data = tdb_fetch(pdb->tdb, key);
+ if (data.dptr) {
+ if (data.dsize == sizeof(print_status_struct))
+ /* this memcpy is ok since the status struct was
+ not packed before storing it in the tdb */
+ memcpy(status, data.dptr, sizeof(print_status_struct));
+ SAFE_FREE(data.dptr);
+ }
+ }
+ len = tdb_fetch_int32(pdb->tdb, "INFO/total_jobs");
+ release_print_db(pdb);
+ return (len == -1 ? 0 : len);
+}
+
+/****************************************************************************
+ Determine the number of jobs in a queue.
+****************************************************************************/
+
+int print_queue_length(int snum, print_status_struct *pstatus)
+{
+ print_status_struct status;
+ int len;
+
+ /* make sure the database is up to date */
+ if (print_cache_expired(snum))
+ print_queue_update(snum);
+
+ /* also fetch the queue status */
+ memset(&status, 0, sizeof(status));
+ len = get_queue_status(snum, &status);
+
+ if (pstatus)
+ *pstatus = status;
+
+ return len;
+}
+
+/***************************************************************************
+ Allocate a jobid. Hold the lock for as short a time as possible.
+***************************************************************************/
+
+static BOOL allocate_print_jobid(struct tdb_print_db *pdb, int snum, const char *printername, uint32 *pjobid)
+{
+ int i;
+ uint32 jobid;
+
+ *pjobid = (uint32)-1;
+
+ for (i = 0; i < 3; i++) {
+ /* Lock the database - only wait 20 seconds. */
+ if (tdb_lock_bystring(pdb->tdb, "INFO/nextjob", 20) == -1) {
+ DEBUG(0,("allocate_print_jobid: failed to lock printing database %s\n", printername ));
+ return False;
+ }
+
+ if (!tdb_fetch_uint32(pdb->tdb, "INFO/nextjob", &jobid)) {
+ if (tdb_error(pdb->tdb) != TDB_ERR_NOEXIST) {
+ DEBUG(0, ("allocate_print_jobid: failed to fetch INFO/nextjob for print queue %s\n",
+ printername ));
+ return False;
+ }
+ jobid = 0;
+ }
+
+ jobid = NEXT_JOBID(jobid);
+
+ if (tdb_store_int32(pdb->tdb, "INFO/nextjob", jobid)==-1) {
+ DEBUG(3, ("allocate_print_jobid: failed to store INFO/nextjob.\n"));
+ tdb_unlock_bystring(pdb->tdb, "INFO/nextjob");
+ return False;
+ }
+
+ /* We've finished with the INFO/nextjob lock. */
+ tdb_unlock_bystring(pdb->tdb, "INFO/nextjob");
+
+ if (!print_job_exists(snum, jobid))
+ break;
+ }
+
+ if (i > 2) {
+ DEBUG(0, ("allocate_print_jobid: failed to allocate a print job for queue %s\n",
+ printername ));
+ /* Probably full... */
+ errno = ENOSPC;
+ return False;
+ }
+
+ /* Store a dummy placeholder. */
+ {
+ TDB_DATA dum;
+ dum.dptr = NULL;
+ dum.dsize = 0;
+ if (tdb_store(pdb->tdb, print_key(jobid), dum, TDB_INSERT) == -1) {
+ DEBUG(3, ("allocate_print_jobid: jobid (%d) failed to store placeholder.\n",
+ jobid ));
+ return False;
+ }
+ }
+
+ *pjobid = jobid;
+ return True;
+}
+
+/***************************************************************************
+ Append a jobid to the 'jobs changed' list.
+***************************************************************************/
+
+static BOOL add_to_jobs_changed(struct tdb_print_db *pdb, uint32 jobid)
+{
+ TDB_DATA data, key;
+
+ key.dptr = "INFO/jobs_changed";
+ key.dsize = strlen(key.dptr);
+ data.dptr = (char *)&jobid;
+ data.dsize = 4;
+
+ DEBUG(10,("add_to_jobs_changed: Added jobid %u\n", (unsigned int)jobid ));
+
+ return (tdb_append(pdb->tdb, key, data) == 0);
+}
+
+/***************************************************************************
+ Start spooling a job - return the jobid.
+***************************************************************************/
+
+uint32 print_job_start(struct current_user *user, int snum, char *jobname, NT_DEVICEMODE *nt_devmode )
+{
+ uint32 jobid;
+ char *path;
+ struct printjob pjob;
+ user_struct *vuser;
+ const char *printername = lp_const_servicename(snum);
+ struct tdb_print_db *pdb = get_print_db_byname(printername);
+ int njobs;
+
+ errno = 0;
+
+ if (!pdb)
+ return (uint32)-1;
+
+ if (!print_access_check(user, snum, PRINTER_ACCESS_USE)) {
+ DEBUG(3, ("print_job_start: job start denied by security descriptor\n"));
+ release_print_db(pdb);
+ return (uint32)-1;
+ }
+
+ if (!print_time_access_check(snum)) {
+ DEBUG(3, ("print_job_start: job start denied by time check\n"));
+ release_print_db(pdb);
+ return (uint32)-1;
+ }
+
+ path = lp_pathname(snum);
+
+ /* see if we have sufficient disk space */
+ if (lp_minprintspace(snum)) {
+ SMB_BIG_UINT dspace, dsize;
+ if (sys_fsusage(path, &dspace, &dsize) == 0 &&
+ dspace < 2*(SMB_BIG_UINT)lp_minprintspace(snum)) {
+ DEBUG(3, ("print_job_start: disk space check failed.\n"));
+ release_print_db(pdb);
+ errno = ENOSPC;
+ return (uint32)-1;
+ }
+ }
+
+ /* for autoloaded printers, check that the printcap entry still exists */
+ if (lp_autoloaded(snum) && !pcap_printername_ok(lp_const_servicename(snum), NULL)) {
+ DEBUG(3, ("print_job_start: printer name %s check failed.\n", lp_const_servicename(snum) ));
+ release_print_db(pdb);
+ errno = ENOENT;
+ return (uint32)-1;
+ }
+
+ /* Insure the maximum queue size is not violated */
+ if ((njobs = print_queue_length(snum,NULL)) > lp_maxprintjobs(snum)) {
+ DEBUG(3, ("print_job_start: Queue %s number of jobs (%d) larger than max printjobs per queue (%d).\n",
+ printername, njobs, lp_maxprintjobs(snum) ));
+ release_print_db(pdb);
+ errno = ENOSPC;
+ return (uint32)-1;
+ }
+
+ DEBUG(10,("print_job_start: Queue %s number of jobs (%d), max printjobs = %d\n",
+ printername, njobs, lp_maxprintjobs(snum) ));
+
+ if (!allocate_print_jobid(pdb, snum, printername, &jobid))
+ goto fail;
+
+ /* create the database entry */
+
+ ZERO_STRUCT(pjob);
+
+ pjob.pid = local_pid;
+ pjob.sysjob = -1;
+ pjob.fd = -1;
+ pjob.starttime = time(NULL);
+ pjob.status = LPQ_SPOOLING;
+ pjob.size = 0;
+ pjob.spooled = False;
+ pjob.smbjob = True;
+ pjob.nt_devmode = nt_devmode;
+
+ fstrcpy(pjob.jobname, jobname);
+
+ if ((vuser = get_valid_user_struct(user->vuid)) != NULL) {
+ fstrcpy(pjob.user, vuser->user.smb_name);
+ } else {
+ fstrcpy(pjob.user, uidtoname(user->uid));
+ }
+
+ fstrcpy(pjob.queuename, lp_const_servicename(snum));
+
+ /* we have a job entry - now create the spool file */
+ slprintf(pjob.filename, sizeof(pjob.filename)-1, "%s/%s%.8u.XXXXXX",
+ path, PRINT_SPOOL_PREFIX, (unsigned int)jobid);
+ pjob.fd = smb_mkstemp(pjob.filename);
+
+ if (pjob.fd == -1) {
+ if (errno == EACCES) {
+ /* Common setup error, force a report. */
+ DEBUG(0, ("print_job_start: insufficient permissions \
+to open spool file %s.\n", pjob.filename));
+ } else {
+ /* Normal case, report at level 3 and above. */
+ DEBUG(3, ("print_job_start: can't open spool file %s,\n", pjob.filename));
+ DEBUGADD(3, ("errno = %d (%s).\n", errno, strerror(errno)));
+ }
+ goto fail;
+ }
+
+ pjob_store(snum, jobid, &pjob);
+
+ /* Update the 'jobs changed' entry used by print_queue_status. */
+ add_to_jobs_changed(pdb, jobid);
+
+ /* Ensure we keep a rough count of the number of total jobs... */
+ tdb_change_int32_atomic(pdb->tdb, "INFO/total_jobs", &njobs, 1);
+
+ release_print_db(pdb);
+
+ return jobid;
+
+ fail:
+ if (jobid != -1)
+ pjob_delete(snum, jobid);
+
+ release_print_db(pdb);
+
+ DEBUG(3, ("print_job_start: returning fail. Error = %s\n", strerror(errno) ));
+ return (uint32)-1;
+}
+
+/****************************************************************************
+ Update the number of pages spooled to jobid
+****************************************************************************/
+
+void print_job_endpage(int snum, uint32 jobid)
+{
+ struct printjob *pjob = print_job_find(snum, jobid);
+ if (!pjob)
+ return;
+ /* don't allow another process to get this info - it is meaningless */
+ if (pjob->pid != local_pid)
+ return;
+
+ pjob->page_count++;
+ pjob_store(snum, jobid, pjob);
+}
+
+/****************************************************************************
+ Print a file - called on closing the file. This spools the job.
+ If normal close is false then we're tearing down the jobs - treat as an
+ error.
+****************************************************************************/
+
+BOOL print_job_end(int snum, uint32 jobid, BOOL normal_close)
+{
+ struct printjob *pjob = print_job_find(snum, jobid);
+ int ret;
+ SMB_STRUCT_STAT sbuf;
+ struct printif *current_printif = get_printer_fns( snum );
+
+ if (!pjob)
+ return False;
+
+ if (pjob->spooled || pjob->pid != local_pid)
+ return False;
+
+ if (normal_close && (sys_fstat(pjob->fd, &sbuf) == 0)) {
+ pjob->size = sbuf.st_size;
+ close(pjob->fd);
+ pjob->fd = -1;
+ } else {
+
+ /*
+ * Not a normal close or we couldn't stat the job file,
+ * so something has gone wrong. Cleanup.
+ */
+ close(pjob->fd);
+ pjob->fd = -1;
+ DEBUG(3,("print_job_end: failed to stat file for jobid %d\n", jobid ));
+ goto fail;
+ }
+
+ /* Technically, this is not quite right. If the printer has a separator
+ * page turned on, the NT spooler prints the separator page even if the
+ * print job is 0 bytes. 010215 JRR */
+ if (pjob->size == 0 || pjob->status == LPQ_DELETING) {
+ /* don't bother spooling empty files or something being deleted. */
+ DEBUG(5,("print_job_end: canceling spool of %s (%s)\n",
+ pjob->filename, pjob->size ? "deleted" : "zero length" ));
+ unlink(pjob->filename);
+ pjob_delete(snum, jobid);
+ return True;
+ }
+
+ pjob->smbjob = jobid;
+
+ ret = (*(current_printif->job_submit))(snum, pjob);
+
+ if (ret)
+ goto fail;
+
+ /* The print job has been sucessfully handed over to the back-end */
+
+ pjob->spooled = True;
+ pjob->status = LPQ_QUEUED;
+ pjob_store(snum, jobid, pjob);
+
+ /* make sure the database is up to date */
+ if (print_cache_expired(snum))
+ print_queue_update(snum);
+
+ return True;
+
+fail:
+
+ /* The print job was not succesfully started. Cleanup */
+ /* Still need to add proper error return propagation! 010122:JRR */
+ unlink(pjob->filename);
+ pjob_delete(snum, jobid);
+ remove_from_jobs_changed(snum, jobid);
+ return False;
+}
+
+/****************************************************************************
+ Get a snapshot of jobs in the system without traversing.
+****************************************************************************/
+
+static BOOL get_stored_queue_info(struct tdb_print_db *pdb, int snum, int *pcount, print_queue_struct **ppqueue)
+{
+ TDB_DATA data, key, cgdata;
+ print_queue_struct *queue = NULL;
+ uint32 qcount = 0;
+ uint32 extra_count = 0;
+ int total_count = 0;
+ size_t len = 0;
+ uint32 i;
+ int max_reported_jobs = lp_max_reported_jobs(snum);
+ BOOL ret = False;
+
+ /* make sure the database is up to date */
+ if (print_cache_expired(snum))
+ print_queue_update(snum);
+
+ *pcount = 0;
+ *ppqueue = NULL;
+
+ ZERO_STRUCT(data);
+ ZERO_STRUCT(cgdata);
+ key.dptr = "INFO/linear_queue_array";
+ key.dsize = strlen(key.dptr);
+
+ /* Get the stored queue data. */
+ data = tdb_fetch(pdb->tdb, key);
+
+ if (data.dptr && data.dsize >= sizeof(qcount))
+ len += tdb_unpack(data.dptr + len, data.dsize - len, "d", &qcount);
+
+ /* Get the changed jobs list. */
+ key.dptr = "INFO/jobs_changed";
+ key.dsize = strlen(key.dptr);
+
+ cgdata = tdb_fetch(pdb->tdb, key);
+ if (cgdata.dptr != NULL && (cgdata.dsize % 4 == 0))
+ extra_count = cgdata.dsize/4;
+
+ DEBUG(5,("get_stored_queue_info: qcount = %u, extra_count = %u\n", (unsigned int)qcount, (unsigned int)extra_count));
+
+ /* Allocate the queue size. */
+ if (qcount == 0 && extra_count == 0)
+ goto out;
+
+ if ((queue = (print_queue_struct *)malloc(sizeof(print_queue_struct)*(qcount + extra_count))) == NULL)
+ goto out;
+
+ /* Retrieve the linearised queue data. */
+
+ for( i = 0; i < qcount; i++) {
+ uint32 qjob, qsize, qpage_count, qstatus, qpriority, qtime;
+ len += tdb_unpack(data.dptr + len, data.dsize - len, "ddddddff",
+ &qjob,
+ &qsize,
+ &qpage_count,
+ &qstatus,
+ &qpriority,
+ &qtime,
+ queue[i].fs_user,
+ queue[i].fs_file);
+ queue[i].job = qjob;
+ queue[i].size = qsize;
+ queue[i].page_count = qpage_count;
+ queue[i].status = qstatus;
+ queue[i].priority = qpriority;
+ queue[i].time = qtime;
+ }
+
+ total_count = qcount;
+
+ /* Add in the changed jobids. */
+ for( i = 0; i < extra_count; i++) {
+ uint32 jobid;
+ struct printjob *pjob;
+
+ jobid = IVAL(cgdata.dptr, i*4);
+ DEBUG(5,("get_stored_queue_info: changed job = %u\n", (unsigned int)jobid));
+ pjob = print_job_find(snum, jobid);
+ if (!pjob) {
+ DEBUG(5,("get_stored_queue_info: failed to find changed job = %u\n", (unsigned int)jobid));
+ remove_from_jobs_changed(snum, jobid);
+ continue;
+ }
+
+ queue[total_count].job = jobid;
+ queue[total_count].size = pjob->size;
+ queue[total_count].page_count = pjob->page_count;
+ queue[total_count].status = pjob->status;
+ queue[total_count].priority = 1;
+ queue[total_count].time = pjob->starttime;
+ fstrcpy(queue[total_count].fs_user, pjob->user);
+ fstrcpy(queue[total_count].fs_file, pjob->jobname);
+ total_count++;
+ }
+
+ /* Sort the queue by submission time otherwise they are displayed
+ in hash order. */
+
+ qsort(queue, total_count, sizeof(print_queue_struct), QSORT_CAST(printjob_comp));
+
+ DEBUG(5,("get_stored_queue_info: total_count = %u\n", (unsigned int)total_count));
+
+ if (max_reported_jobs && total_count > max_reported_jobs)
+ total_count = max_reported_jobs;
+
+ *ppqueue = queue;
+ *pcount = total_count;
+
+ ret = True;
+
+ out:
+
+ SAFE_FREE(data.dptr);
+ SAFE_FREE(cgdata.dptr);
+ return ret;
+}
+
+/****************************************************************************
+ Get a printer queue listing.
+ set queue = NULL and status = NULL if you just want to update the cache
+****************************************************************************/
+
+int print_queue_status(int snum,
+ print_queue_struct **ppqueue,
+ print_status_struct *status)
+{
+ fstring keystr;
+ TDB_DATA data, key;
+ const char *printername;
+ struct tdb_print_db *pdb;
+ int count = 0;
+
+ /* make sure the database is up to date */
+
+ if (print_cache_expired(snum))
+ print_queue_update(snum);
+
+ /* return if we are done */
+ if ( !ppqueue || !status )
+ return 0;
+
+ *ppqueue = NULL;
+ printername = lp_const_servicename(snum);
+ pdb = get_print_db_byname(printername);
+
+ if (!pdb)
+ return 0;
+
+ /*
+ * Fetch the queue status. We must do this first, as there may
+ * be no jobs in the queue.
+ */
+
+ ZERO_STRUCTP(status);
+ slprintf(keystr, sizeof(keystr)-1, "STATUS/%s", printername);
+ key.dptr = keystr;
+ key.dsize = strlen(keystr);
+ data = tdb_fetch(pdb->tdb, key);
+ if (data.dptr) {
+ if (data.dsize == sizeof(*status)) {
+ /* this memcpy is ok since the status struct was
+ not packed before storing it in the tdb */
+ memcpy(status, data.dptr, sizeof(*status));
+ }
+ SAFE_FREE(data.dptr);
+ }
+
+ /*
+ * Now, fetch the print queue information. We first count the number
+ * of entries, and then only retrieve the queue if necessary.
+ */
+
+ if (!get_stored_queue_info(pdb, snum, &count, ppqueue)) {
+ release_print_db(pdb);
+ return 0;
+ }
+
+ release_print_db(pdb);
+ return count;
+}
+
+/****************************************************************************
+ Pause a queue.
+****************************************************************************/
+
+BOOL print_queue_pause(struct current_user *user, int snum, WERROR *errcode)
+{
+ int ret;
+ struct printif *current_printif = get_printer_fns( snum );
+
+ if (!print_access_check(user, snum, PRINTER_ACCESS_ADMINISTER)) {
+ *errcode = WERR_ACCESS_DENIED;
+ return False;
+ }
+
+ ret = (*(current_printif->queue_pause))(snum);
+
+ if (ret != 0) {
+ *errcode = WERR_INVALID_PARAM;
+ return False;
+ }
+
+ /* force update the database */
+ print_cache_flush(snum);
+
+ /* Send a printer notify message */
+
+ notify_printer_status(snum, PRINTER_STATUS_PAUSED);
+
+ return True;
+}
+
+/****************************************************************************
+ Resume a queue.
+****************************************************************************/
+
+BOOL print_queue_resume(struct current_user *user, int snum, WERROR *errcode)
+{
+ int ret;
+ struct printif *current_printif = get_printer_fns( snum );
+
+ if (!print_access_check(user, snum, PRINTER_ACCESS_ADMINISTER)) {
+ *errcode = WERR_ACCESS_DENIED;
+ return False;
+ }
+
+ ret = (*(current_printif->queue_resume))(snum);
+
+ if (ret != 0) {
+ *errcode = WERR_INVALID_PARAM;
+ return False;
+ }
+
+ /* make sure the database is up to date */
+ if (print_cache_expired(snum))
+ print_queue_update(snum);
+
+ /* Send a printer notify message */
+
+ notify_printer_status(snum, PRINTER_STATUS_OK);
+
+ return True;
+}
+
+/****************************************************************************
+ Purge a queue - implemented by deleting all jobs that we can delete.
+****************************************************************************/
+
+BOOL print_queue_purge(struct current_user *user, int snum, WERROR *errcode)
+{
+ print_queue_struct *queue;
+ print_status_struct status;
+ int njobs, i;
+ BOOL can_job_admin;
+
+ /* Force and update so the count is accurate (i.e. not a cached count) */
+ print_queue_update(snum);
+
+ can_job_admin = print_access_check(user, snum, JOB_ACCESS_ADMINISTER);
+ njobs = print_queue_status(snum, &queue, &status);
+
+ for (i=0;i<njobs;i++) {
+ BOOL owner = is_owner(user, snum, queue[i].job);
+
+ if (owner || can_job_admin) {
+ print_job_delete1(snum, queue[i].job);
+ }
+ }
+
+ SAFE_FREE(queue);
+
+ return True;
+}
diff --git a/source/printing/printing_db.c b/source/printing/printing_db.c
new file mode 100644
index 00000000000..d402aa366f4
--- /dev/null
+++ b/source/printing/printing_db.c
@@ -0,0 +1,206 @@
+/*
+ Unix SMB/Netbios implementation.
+ Version 3.0
+ printing backend routines
+ Copyright (C) Andrew Tridgell 1992-2000
+ Copyright (C) Jeremy Allison 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 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 "printing.h"
+
+static struct tdb_print_db *print_db_head;
+
+/****************************************************************************
+ Function to find or create the printer specific job tdb given a printername.
+ Limits the number of tdb's open to MAX_PRINT_DBS_OPEN.
+****************************************************************************/
+
+struct tdb_print_db *get_print_db_byname(const char *printername)
+{
+ struct tdb_print_db *p = NULL, *last_entry = NULL;
+ int num_open = 0;
+ pstring printdb_path;
+ BOOL done_become_root = False;
+
+ for (p = print_db_head, last_entry = print_db_head; p; p = p->next) {
+ /* Ensure the list terminates... JRA. */
+ SMB_ASSERT(p->next != print_db_head);
+
+ if (p->tdb && strequal(p->printer_name, printername)) {
+ DLIST_PROMOTE(print_db_head, p);
+ p->ref_count++;
+ return p;
+ }
+ num_open++;
+ last_entry = p;
+ }
+
+ /* Not found. */
+ if (num_open >= MAX_PRINT_DBS_OPEN) {
+ /* Try and recycle the last entry. */
+ DLIST_PROMOTE(print_db_head, last_entry);
+
+ for (p = print_db_head; p; p = p->next) {
+ if (p->ref_count)
+ continue;
+ if (p->tdb) {
+ if (tdb_close(print_db_head->tdb)) {
+ DEBUG(0,("get_print_db: Failed to close tdb for printer %s\n",
+ print_db_head->printer_name ));
+ return NULL;
+ }
+ }
+ p->tdb = NULL;
+ p->ref_count = 0;
+ memset(p->printer_name, '\0', sizeof(p->printer_name));
+ break;
+ }
+ if (p) {
+ DLIST_PROMOTE(print_db_head, p);
+ p = print_db_head;
+ }
+ }
+
+ if (!p) {
+ /* Create one. */
+ p = (struct tdb_print_db *)malloc(sizeof(struct tdb_print_db));
+ if (!p) {
+ DEBUG(0,("get_print_db: malloc fail !\n"));
+ return NULL;
+ }
+ ZERO_STRUCTP(p);
+ DLIST_ADD(print_db_head, p);
+ }
+
+ pstrcpy(printdb_path, lock_path("printing/"));
+ pstrcat(printdb_path, printername);
+ pstrcat(printdb_path, ".tdb");
+
+ if (geteuid() != 0) {
+ become_root();
+ done_become_root = True;
+ }
+
+ p->tdb = tdb_open_ex(printdb_path, 5000, TDB_DEFAULT, O_RDWR|O_CREAT,
+ 0600, smbd_tdb_log);
+
+ if (done_become_root)
+ unbecome_root();
+
+ if (!p->tdb) {
+ DEBUG(0,("get_print_db: Failed to open printer backend database %s.\n",
+ printdb_path ));
+ DLIST_REMOVE(print_db_head, p);
+ SAFE_FREE(p);
+ return NULL;
+ }
+ fstrcpy(p->printer_name, printername);
+ p->ref_count++;
+ return p;
+}
+
+/***************************************************************************
+ Remove a reference count.
+****************************************************************************/
+
+void release_print_db( struct tdb_print_db *pdb)
+{
+ pdb->ref_count--;
+ SMB_ASSERT(pdb->ref_count >= 0);
+}
+
+/***************************************************************************
+ Close all open print db entries.
+****************************************************************************/
+
+void close_all_print_db(void)
+{
+ struct tdb_print_db *p = NULL, *next_p = NULL;
+
+ for (p = print_db_head; p; p = next_p) {
+ next_p = p->next;
+
+ if (p->tdb)
+ tdb_close(p->tdb);
+ DLIST_REMOVE(print_db_head, p);
+ ZERO_STRUCTP(p);
+ SAFE_FREE(p);
+ }
+}
+
+
+/****************************************************************************
+ Fetch and clean the pid_t record list for all pids interested in notify
+ messages. data needs freeing on exit.
+****************************************************************************/
+
+TDB_DATA get_printer_notify_pid_list(TDB_CONTEXT *tdb, const char *printer_name, BOOL cleanlist)
+{
+ TDB_DATA data;
+ size_t i;
+
+ ZERO_STRUCT(data);
+
+ data = tdb_fetch_bystring( tdb, NOTIFY_PID_LIST_KEY );
+
+ if (!data.dptr) {
+ ZERO_STRUCT(data);
+ return data;
+ }
+
+ if (data.dsize % 8) {
+ DEBUG(0,("get_printer_notify_pid_list: Size of record for printer %s not a multiple of 8 !\n", printer_name ));
+ tdb_delete_bystring(tdb, NOTIFY_PID_LIST_KEY );
+ SAFE_FREE(data.dptr);
+ ZERO_STRUCT(data);
+ return data;
+ }
+
+ if (!cleanlist)
+ return data;
+
+ /*
+ * Weed out all dead entries.
+ */
+
+ for( i = 0; i < data.dsize; i += 8) {
+ pid_t pid = (pid_t)IVAL(data.dptr, i);
+
+ if (pid == sys_getpid())
+ continue;
+
+ /* Entry is dead if process doesn't exist or refcount is zero. */
+
+ while ((i < data.dsize) && ((IVAL(data.dptr, i + 4) == 0) || !process_exists(pid))) {
+
+ /* Refcount == zero is a logic error and should never happen. */
+ if (IVAL(data.dptr, i + 4) == 0) {
+ DEBUG(0,("get_printer_notify_pid_list: Refcount == 0 for pid = %u printer %s !\n",
+ (unsigned int)pid, printer_name ));
+ }
+
+ if (data.dsize - i > 8)
+ memmove( &data.dptr[i], &data.dptr[i+8], data.dsize - i - 8);
+ data.dsize -= 8;
+ }
+ }
+
+ return data;
+}
+
+
diff --git a/source/profile/profile.c b/source/profile/profile.c
new file mode 100644
index 00000000000..689f67da997
--- /dev/null
+++ b/source/profile/profile.c
@@ -0,0 +1,166 @@
+/*
+ Unix SMB/CIFS implementation.
+ store smbd profiling information in shared memory
+ 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 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"
+
+#ifdef WITH_PROFILE
+#define IPC_PERMS ((SHM_R | SHM_W) | (SHM_R>>3) | (SHM_R>>6))
+#endif /* WITH_PROFILE */
+
+#ifdef WITH_PROFILE
+static int shm_id;
+static BOOL read_only;
+#endif
+
+struct profile_header *profile_h;
+struct profile_stats *profile_p;
+
+BOOL do_profile_flag = False;
+BOOL do_profile_times = False;
+
+struct timeval profile_starttime;
+struct timeval profile_endtime;
+struct timeval profile_starttime_nested;
+struct timeval profile_endtime_nested;
+
+/****************************************************************************
+receive a set profile level message
+****************************************************************************/
+void profile_message(int msg_type, pid_t src, void *buf, size_t len)
+{
+ int level;
+
+ memcpy(&level, buf, sizeof(int));
+#ifdef WITH_PROFILE
+ switch (level) {
+ case 0: /* turn off profiling */
+ do_profile_flag = False;
+ do_profile_times = False;
+ DEBUG(1,("INFO: Profiling turned OFF from pid %d\n", (int)src));
+ break;
+ case 1: /* turn on counter profiling only */
+ do_profile_flag = True;
+ do_profile_times = False;
+ DEBUG(1,("INFO: Profiling counts turned ON from pid %d\n", (int)src));
+ break;
+ case 2: /* turn on complete profiling */
+ do_profile_flag = True;
+ do_profile_times = True;
+ DEBUG(1,("INFO: Full profiling turned ON from pid %d\n", (int)src));
+ break;
+ case 3: /* reset profile values */
+ memset((char *)profile_p, 0, sizeof(*profile_p));
+ DEBUG(1,("INFO: Profiling values cleared from pid %d\n", (int)src));
+ break;
+ }
+#else /* WITH_PROFILE */
+ DEBUG(1,("INFO: Profiling support unavailable in this build.\n"));
+#endif /* WITH_PROFILE */
+}
+
+/****************************************************************************
+receive a request profile level message
+****************************************************************************/
+void reqprofile_message(int msg_type, pid_t src, void *buf, size_t len)
+{
+ int level;
+
+#ifdef WITH_PROFILE
+ level = 1 + (do_profile_flag?2:0) + (do_profile_times?4:0);
+#else
+ level = 0;
+#endif
+ DEBUG(1,("INFO: Received REQ_PROFILELEVEL message from PID %u\n",(unsigned int)src));
+ message_send_pid(src, MSG_PROFILELEVEL, &level, sizeof(int), True);
+}
+
+/*******************************************************************
+ open the profiling shared memory area
+ ******************************************************************/
+#ifdef WITH_PROFILE
+BOOL profile_setup(BOOL rdonly)
+{
+ struct shmid_ds shm_ds;
+
+ read_only = rdonly;
+
+ again:
+ /* try to use an existing key */
+ shm_id = shmget(PROF_SHMEM_KEY, 0, 0);
+
+ /* if that failed then create one. There is a race condition here
+ if we are running from inetd. Bad luck. */
+ if (shm_id == -1) {
+ if (read_only) return False;
+ shm_id = shmget(PROF_SHMEM_KEY, sizeof(*profile_h),
+ IPC_CREAT | IPC_EXCL | IPC_PERMS);
+ }
+
+ if (shm_id == -1) {
+ DEBUG(0,("Can't create or use IPC area. Error was %s\n",
+ strerror(errno)));
+ return False;
+ }
+
+
+ profile_h = (struct profile_header *)shmat(shm_id, 0,
+ read_only?SHM_RDONLY:0);
+ if ((long)profile_p == -1) {
+ DEBUG(0,("Can't attach to IPC area. Error was %s\n",
+ strerror(errno)));
+ return False;
+ }
+
+ /* find out who created this memory area */
+ if (shmctl(shm_id, IPC_STAT, &shm_ds) != 0) {
+ DEBUG(0,("ERROR shmctl : can't IPC_STAT. Error was %s\n",
+ strerror(errno)));
+ return False;
+ }
+
+ if (shm_ds.shm_perm.cuid != sec_initial_uid() || shm_ds.shm_perm.cgid != sec_initial_gid()) {
+ DEBUG(0,("ERROR: we did not create the shmem (owned by another user)\n"));
+ return False;
+ }
+
+ if (shm_ds.shm_segsz != sizeof(*profile_h)) {
+ DEBUG(0,("WARNING: profile size is %d (expected %d). Deleting\n",
+ (int)shm_ds.shm_segsz, sizeof(*profile_h)));
+ if (shmctl(shm_id, IPC_RMID, &shm_ds) == 0) {
+ goto again;
+ } else {
+ return False;
+ }
+ }
+
+ if (!read_only && (shm_ds.shm_nattch == 1)) {
+ memset((char *)profile_h, 0, sizeof(*profile_h));
+ profile_h->prof_shm_magic = PROF_SHM_MAGIC;
+ profile_h->prof_shm_version = PROF_SHM_VERSION;
+ DEBUG(3,("Initialised profile area\n"));
+ }
+
+ profile_p = &profile_h->stats;
+ message_register(MSG_PROFILE, profile_message);
+ message_register(MSG_REQ_PROFILELEVEL, reqprofile_message);
+ return True;
+}
+#endif /* WITH_PROFILE */
diff --git a/source/python/.cvsignore b/source/python/.cvsignore
new file mode 100644
index 00000000000..7e99e367f84
--- /dev/null
+++ b/source/python/.cvsignore
@@ -0,0 +1 @@
+*.pyc \ No newline at end of file
diff --git a/source/python/README b/source/python/README
new file mode 100644
index 00000000000..04f794215ab
--- /dev/null
+++ b/source/python/README
@@ -0,0 +1,28 @@
+This directory contains Python bindings to allow you to access various
+aspects of Samba. At the moment their status is "experimental" and
+they are not built by default.
+
+In order to be able to compile samba-python you need to have python
+and the python-dev packages installed.
+
+Python libraries are always built for a particular version of Python
+(2.2, 2.1, etc), and libraries built for one version will not be seen
+by another. By default Samba's libraries are built for whatever is
+installed as "python" on your $PATH, but you can override this using
+the --with-python option. For example
+
+ $ ./configure --with-python=python2.2
+
+To build:
+
+$ autoconf
+$ ./configure
+$ make python_ext
+
+Now, you can install the modules:
+
+$ cp build/lib.*/*.so /usr/lib/python2.1/lib-dynload/
+
+(the directory /usr/lib/python2.1 may vary, depending on your installation)
+
+Samba-python should work now!
diff --git a/source/python/examples/spoolss/changeid.py b/source/python/examples/spoolss/changeid.py
new file mode 100755
index 00000000000..85fe0efe8a4
--- /dev/null
+++ b/source/python/examples/spoolss/changeid.py
@@ -0,0 +1,34 @@
+#!/usr/bin/python
+#
+# Display the changeid for a list of printers given on the command line
+#
+# Sample usage:
+#
+# changeid.py '\\win2kdc1\magpie'
+#
+
+import sys
+from samba import spoolss
+
+if len(sys.argv) == 1:
+ print "Usage: changeid.py <printername>"
+ sys.exit(1)
+
+for printer in sys.argv[1:]:
+
+ # Open printer handle
+
+ try:
+ hnd = spoolss.openprinter(printer)
+ except:
+ print "error opening printer %s" % printer
+ sys.exit(1)
+
+ # Fetch and display changeid
+
+ info = hnd.getprinter(level = 0)
+ print info["change_id"]
+
+ # Clean up
+
+ spoolss.closeprinter(hnd)
diff --git a/source/python/examples/spoolss/enumprinters.py b/source/python/examples/spoolss/enumprinters.py
new file mode 100755
index 00000000000..478c46bc24c
--- /dev/null
+++ b/source/python/examples/spoolss/enumprinters.py
@@ -0,0 +1,36 @@
+#!/usr/bin/env python
+#
+# Display information on all printers on a print server. Defaults to
+# printer info level 1.
+#
+# Example: enumprinters.py win2kdc1
+#
+
+import sys
+from samba import spoolss
+
+if len(sys.argv) < 2 or len(sys.argv) > 3:
+ print "Usage: enumprinters.py <servername> [infolevel]"
+ sys.exit(1)
+
+printserver = sys.argv[1]
+
+level = 1
+if len(sys.argv) == 3:
+ level = int(sys.argv[2])
+
+# Get list of printers
+
+try:
+ printer_list = spoolss.enumprinters("\\\\%s" % printserver)
+except:
+ print "error enumerating printers on %s" % printserver
+ sys.exit(1)
+
+# Display basic info
+
+for printer in printer_list:
+ h = spoolss.openprinter("\\\\%s\\%s" % (printserver, printer))
+ info = h.getprinter(level = level)
+ print "Printer info %d for %s: %s" % (level, printer, info)
+ print
diff --git a/source/python/examples/spoolss/psec.py b/source/python/examples/spoolss/psec.py
new file mode 100755
index 00000000000..498a0ef1744
--- /dev/null
+++ b/source/python/examples/spoolss/psec.py
@@ -0,0 +1,88 @@
+#!/usr/bin/env python
+#
+# Get or set the security descriptor on a printer
+#
+
+import sys, re, string
+from samba import spoolss
+
+if len(sys.argv) != 3:
+ print "Usage: psec.py getsec|setsec printername"
+ sys.exit(1)
+
+op = sys.argv[1]
+printername = sys.argv[2]
+
+# Display security descriptor
+
+if op == "getsec":
+
+ try:
+ hnd = spoolss.openprinter(printername)
+ except:
+ print "error opening printer %s" % printername
+ sys.exit(1)
+
+ secdesc = hnd.getprinter(level = 3)["security_descriptor"]
+
+ print secdesc["owner_sid"]
+ print secdesc["group_sid"]
+
+ for acl in secdesc["dacl"]["ace_list"]:
+ print "%d %d 0x%08x %s" % (acl["type"], acl["flags"],
+ acl["mask"], acl["trustee"])
+
+ spoolss.closeprinter(hnd)
+
+ sys.exit(0)
+
+# Set security descriptor
+
+if op == "setsec":
+
+ # Open printer
+
+ try:
+ hnd = spoolss.openprinter(printername,
+ creds = {"domain": "NPSD-TEST2",
+ "username": "Administrator",
+ "password": "penguin"})
+ except:
+ print "error opening printer %s" % printername
+ sys.exit(1)
+
+ # Read lines from standard input and build security descriptor
+
+ lines = sys.stdin.readlines()
+
+ secdesc = {}
+
+ secdesc["owner_sid"] = lines[0]
+ secdesc["group_sid"] = lines[1]
+
+ secdesc["revision"] = 1
+ secdesc["dacl"] = {}
+ secdesc["dacl"]["revision"] = 2
+ secdesc["dacl"]["ace_list"] = []
+
+ for acl in lines[2:]:
+ match = re.match("(\d+) (\d+) (0[xX][\dA-Fa-f]+) (\S+)", acl)
+ secdesc["dacl"]["ace_list"].append(
+ {"type": int(match.group(1)), "flags": int(match.group(2)),
+ "mask": string.atoi(match.group(3), 0), "trustee": match.group(4)})
+
+ # Build info3 structure
+
+ info3 = {}
+
+ info3["flags"] = 0x8004 # self-relative, dacl present
+ info3["level"] = 3
+ info3["security_descriptor"] = secdesc
+
+ hnd.setprinter(info3)
+
+ spoolss.closeprinter(hnd)
+ sys.exit(0)
+
+print "invalid operation %s" % op
+sys.exit(1)
diff --git a/source/python/examples/tdbpack/.cvsignore b/source/python/examples/tdbpack/.cvsignore
new file mode 100644
index 00000000000..52e4e61140d
--- /dev/null
+++ b/source/python/examples/tdbpack/.cvsignore
@@ -0,0 +1,2 @@
+*.pyc
+*.pyo
diff --git a/source/python/examples/tdbpack/oldtdbutil.py b/source/python/examples/tdbpack/oldtdbutil.py
new file mode 100644
index 00000000000..ac435b8bacf
--- /dev/null
+++ b/source/python/examples/tdbpack/oldtdbutil.py
@@ -0,0 +1,144 @@
+#!/usr/bin/python
+#############################################################
+# tdbutil
+#
+# Purpose:
+# Contains functions that are used to pack and unpack data
+# from Samba's tdb databases. Samba sometimes represents complex
+# data structures as a single value in a database. These functions
+# allow other python scripts to package data types into a single python
+# string and unpackage them.
+#
+#
+# XXXXX: This code is no longer used; it's just here for testing
+# compatibility with the new (much faster) C implementation.
+#
+##############################################################
+import string
+
+def pack(format,list):
+ retstring = ''
+ listind = 0
+
+ # Cycle through format entries
+ for type in format:
+ # Null Terminated String
+ if (type == 'f' or type == 'P'):
+ retstring = retstring + list[listind] + "\000"
+ # 4 Byte Number
+ if (type == 'd'):
+ retstring = retstring + PackNum(list[listind],4)
+ # 2 Byte Number
+ if (type == 'w'):
+ retstring = retstring + PackNum(list[listind],2)
+ # Pointer Value
+ if (type == 'p'):
+ if (list[listind]):
+ retstring = retstring + PackNum(1,4)
+ else:
+ retstring = retstring + PackNum(0,4)
+ # Buffer and Length
+ if (type == 'B'):
+ # length
+ length = list[listind]
+ retstring = retstring + PackNum(length,4)
+ length = int(length)
+ listind = listind + 1
+ # buffer
+ retstring = retstring + list[listind][:length]
+
+ listind = listind + 1
+
+ return retstring
+
+def unpack(format,buffer):
+ retlist = []
+ bufind = 0
+
+ lasttype = ""
+ for type in format:
+ # Pointer Value
+ if (type == 'p'):
+ newvalue = UnpackNum(buffer[bufind:bufind+4])
+ bufind = bufind + 4
+ if (newvalue):
+ newvalue = 1L
+ else:
+ newvalue = 0L
+ retlist.append(newvalue)
+ # Previous character till end of data
+ elif (type == '$'):
+ if (lasttype == 'f'):
+ while (bufind < len(buffer)):
+ newstring = ''
+ while (buffer[bufind] != '\000'):
+ newstring = newstring + buffer[bufind]
+ bufind = bufind + 1
+ bufind = bufind + 1
+ retlist.append(newstring)
+ # Null Terminated String
+ elif (type == 'f' or type == 'P'):
+ newstring = ''
+ while (buffer[bufind] != '\000'):
+ newstring = newstring + buffer[bufind]
+ bufind = bufind + 1
+ bufind = bufind + 1
+ retlist.append(newstring)
+ # 4 Byte Number
+ elif (type == 'd'):
+ newvalue = UnpackNum(buffer[bufind:bufind+4])
+ bufind = bufind + 4
+ retlist.append(newvalue)
+ # 2 Byte Number
+ elif (type == 'w'):
+ newvalue = UnpackNum(buffer[bufind:bufind+2])
+ bufind = bufind + 2
+ retlist.append(newvalue)
+ # Length and Buffer
+ elif (type == 'B'):
+ # Length
+ length = UnpackNum(buffer[bufind:bufind+4])
+ bufind = bufind + 4
+ retlist.append(length)
+ length = int(length)
+ # Buffer
+ retlist.append(buffer[bufind:bufind+length])
+ bufind = bufind + length
+
+ lasttype = type
+
+ return ((retlist,buffer[bufind:]))
+
+def PackNum(myint,size):
+ retstring = ''
+ size = size * 2
+ hint = hex(myint)[2:]
+
+ # Check for long notation
+ if (hint[-1:] == 'L'):
+ hint = hint[:-1]
+
+ addon = size - len(hint)
+ for i in range(0,addon):
+ hint = '0' + hint
+
+ while (size > 0):
+ val = string.atoi(hint[size-2:size],16)
+ retstring = retstring + chr(val)
+ size = size - 2
+
+ return retstring
+
+def UnpackNum(buffer):
+ size = len(buffer)
+ mystring = ''
+
+ for i in range(size-1,-1,-1):
+ val = hex(ord(buffer[i]))[2:]
+ if (len(val) == 1):
+ val = '0' + val
+ mystring = mystring + val
+ if (len(mystring) > 4):
+ return string.atol(mystring,16)
+ else:
+ return string.atoi(mystring,16)
diff --git a/source/python/examples/tdbpack/tdbtimetrial.py b/source/python/examples/tdbpack/tdbtimetrial.py
new file mode 100755
index 00000000000..be6404899d8
--- /dev/null
+++ b/source/python/examples/tdbpack/tdbtimetrial.py
@@ -0,0 +1,12 @@
+#! /usr/bin/python2.2
+
+def run_trial():
+ # import tdbutil
+ from samba.tdbpack import pack
+
+ for i in xrange(500000):
+ pack("ddffd", (10, 2, "mbp", "martin", 0))
+ #s = "\n\0\0\0" + "\x02\0\0\0" + "mbp\0" + "martin\0" + "\0\0\0\0"
+
+if __name__ == '__main__':
+ run_trial()
diff --git a/source/python/examples/tdbpack/test_tdbpack.py b/source/python/examples/tdbpack/test_tdbpack.py
new file mode 100755
index 00000000000..837600f789e
--- /dev/null
+++ b/source/python/examples/tdbpack/test_tdbpack.py
@@ -0,0 +1,253 @@
+#! /usr/bin/env python2.2
+
+__doc__ = """test case for samba.tdbpack functions
+
+tdbpack provides a means of pickling values into binary formats
+compatible with that used by the samba tdbpack()/tdbunpack()
+functions.
+
+Numbers are always stored in little-endian format; strings are stored
+in either DOS or Unix codepage as appropriate.
+
+The format for any particular element is encoded as a short ASCII
+string, with one character per field."""
+
+# Copyright (C) 2002 Hewlett-Packard.
+
+__author__ = 'Martin Pool <mbp@sourcefrog.net>'
+
+import unittest
+import oldtdbutil
+import samba.tdbpack
+
+both_unpackers = (samba.tdbpack.unpack, oldtdbutil.unpack)
+both_packers = (samba.tdbpack.pack, oldtdbutil.pack)
+
+
+
+# # ('B', [10, 'hello'], '\x0a\0\0\0hello'),
+# ('BB', [11, 'hello\0world', 3, 'now'],
+# '\x0b\0\0\0hello\0world\x03\0\0\0now'),
+# ('pd', [1, 10], '\x01\0\0\0\x0a\0\0\0'),
+# ('BBB', [5, 'hello', 0, '', 5, 'world'],
+# '\x05\0\0\0hello\0\0\0\0\x05\0\0\0world'),
+
+ # strings are sequences in Python, there's no getting away
+ # from it
+# ('ffff', 'evil', 'e\0v\0i\0l\0'),
+# ('BBBB', 'evil',
+# '\x01\0\0\0e'
+# '\x01\0\0\0v'
+# '\x01\0\0\0i'
+# '\x01\0\0\0l'),
+
+# ('', [], ''),
+
+# # exercise some long strings
+# ('PP', ['hello' * 255, 'world' * 255],
+# 'hello' * 255 + '\0' + 'world' * 255 + '\0'),
+# ('PP', ['hello' * 40000, 'world' * 50000],
+# 'hello' * 40000 + '\0' + 'world' * 50000 + '\0'),
+# ('B', [(5*51), 'hello' * 51], '\xff\0\0\0' + 'hello' * 51),
+# ('BB', [(5 * 40000), 'hello' * 40000,
+# (5 * 50000), 'world' * 50000],
+# '\x40\x0d\x03\0' + 'hello' * 40000 + '\x90\xd0\x03\x00' + 'world' * 50000),
+
+
+class PackTests(unittest.TestCase):
+ symm_cases = [
+ ('w', [42], '\x2a\0'),
+ ('www', [42, 2, 69], '\x2a\0\x02\0\x45\0'),
+ ('wd', [42, 256], '\x2a\0\0\x01\0\0'),
+ ('w', [0], '\0\0'),
+ ('w', [255], '\xff\0'),
+ ('w', [256], '\0\x01'),
+ ('w', [0xdead], '\xad\xde'),
+ ('w', [0xffff], '\xff\xff'),
+ ('p', [0], '\0\0\0\0'),
+ ('p', [1], '\x01\0\0\0'),
+ ('d', [0x01020304], '\x04\x03\x02\x01'),
+ ('d', [0x7fffffff], '\xff\xff\xff\x7f'),
+ ('d', [0x80000000L], '\x00\x00\x00\x80'),
+ ('d', [0x80000069L], '\x69\x00\x00\x80'),
+ ('d', [0xffffffffL], '\xff\xff\xff\xff'),
+ ('d', [0xffffff00L], '\x00\xff\xff\xff'),
+ ('ddd', [1, 10, 50], '\x01\0\0\0\x0a\0\0\0\x32\0\0\0'),
+ ('ff', ['hello', 'world'], 'hello\0world\0'),
+ ('fP', ['hello', 'world'], 'hello\0world\0'),
+ ('PP', ['hello', 'world'], 'hello\0world\0'),
+ ('B', [0, ''], '\0\0\0\0'),
+# old implementation is wierd when string is not the right length
+# ('B', [2, 'hello'], '\x0a\0\0\0hello'),
+ ('B', [5, 'hello'], '\x05\0\0\0hello'),
+ ]
+
+ def test_symmetric(self):
+ """Cookbook of symmetric pack/unpack tests
+ """
+ for packer in [samba.tdbpack.pack]: # both_packers:
+ for unpacker in both_unpackers:
+ for format, values, expected in self.symm_cases:
+ out_packed = packer(format, values)
+ self.assertEquals(out_packed, expected)
+ out, rest = unpacker(format, expected)
+ self.assertEquals(rest, '')
+ self.assertEquals(list(values), list(out))
+
+ def test_large(self):
+ """Test large pack/unpack strings"""
+ large_cases = [('w' * 1000, xrange(1000)), ]
+ for packer in both_packers:
+ for unpacker in both_unpackers:
+ for format, values in large_cases:
+ packed = packer(format, values)
+ out, rest = unpacker(format, packed)
+ self.assertEquals(rest, '')
+ self.assertEquals(list(values), list(out))
+
+
+ def test_pack(self):
+ """Cookbook of expected pack values
+
+ These can't be used for the symmetric test because the unpacked value is
+ not "canonical".
+ """
+ cases = [('w', (42,), '\x2a\0'),
+ ]
+
+ for packer in both_packers:
+ for format, values, expected in cases:
+ self.assertEquals(packer(format, values), expected)
+
+ def test_unpack_extra(self):
+ # Test leftover data
+ for unpacker in both_unpackers:
+ for format, values, packed in self.symm_cases:
+ out, rest = unpacker(format, packed + 'hello sailor!')
+ self.assertEquals(rest, 'hello sailor!')
+ self.assertEquals(list(values), list(out))
+
+
+ def test_pack_extra(self):
+ """Leftover values when packing"""
+ cases = [
+ ('d', [10, 20], [10]),
+ ('d', [10, 'hello'], [10]),
+ ('ff', ['hello', 'world', 'sailor'], ['hello', 'world']),
+ ]
+ for unpacker in both_unpackers:
+ for packer in both_packers:
+ for format, values, chopped in cases:
+ bin = packer(format, values)
+ out, rest = unpacker(format, bin)
+ self.assertEquals(list(out), list(chopped))
+ self.assertEquals(rest, '')
+
+
+ def test_unpack(self):
+ """Cookbook of tricky unpack tests"""
+ cases = [
+ # Apparently I couldn't think of any tests that weren't
+ # symmetric :-/
+ ]
+ for unpacker in both_unpackers:
+ for format, values, expected in cases:
+ out, rest = unpacker(format, expected)
+ self.assertEquals(rest, '')
+ self.assertEquals(list(values), list(out))
+
+
+ def test_pack_failures(self):
+ """Expected errors for incorrect packing"""
+ cases = [('w', []),
+# ('w', ()),
+# ('w', {}),
+ ('ww', [2]),
+ ('w', 2),
+# ('w', None),
+ ('wwwwwwwwwwww', []),
+# ('w', [0x60A15EC5L]),
+# ('w', [None]),
+ ('d', []),
+ ('p', []),
+ ('f', [2]),
+ ('P', [None]),
+ ('P', ()),
+ ('f', [hex]),
+ ('fw', ['hello']),
+# ('f', [u'hello']),
+ ('B', [2]),
+ (None, [2, 3, 4]),
+ (ord('f'), [20]),
+ # old code doesn't distinguish string from seq-of-char
+# (['w', 'w'], [2, 2]),
+ # old code just ignores invalid characters
+# ('Q', [2]),
+# ('fQ', ['2', 3]),
+# ('fQ', ['2']),
+ (2, [2]),
+ # old code doesn't typecheck format
+# ({}, {})
+ ]
+ for packer in both_packers:
+ for format, values in cases:
+ try:
+ packer(format, values)
+ except StandardError:
+ pass
+ else:
+ raise AssertionError("didn't get exception: format %s, values %s, packer %s"
+ % (`format`, `values`, `packer`))
+
+
+ def test_unpack_failures(self):
+ """Expected errors for incorrect unpacking"""
+ cases = [
+# This ought to be illegal, but the old code doesn't prohibit it
+# ('$', '', ValueError),
+# ('Q', '', ValueError),
+# ('Q$', '', ValueError),
+ ('f', '', IndexError),
+ ('d', '', IndexError),
+# This is an illegal packing, but the old code doesn't trap
+# ('d', '2', IndexError),
+# ('d', '22', IndexError),
+# ('d', '222', IndexError),
+# ('p', '\x01\0', IndexError),
+# ('w', '2', IndexError),
+# ('B', '\xff\0\0\0hello', IndexError),
+# ('B', '\xff\0', IndexError),
+ ('w', '', IndexError),
+ ('f', 'hello', IndexError),
+ ('f', '', IndexError),
+# ('B', '\x01\0\0\0', IndexError),
+# ('B', '\x05\0\0\0hell', IndexError),
+ ('B', '\xff\xff\xff\xff', ValueError),
+# ('B', 'foobar', IndexError),
+# ('BB', '\x01\0\0\0a\x01', IndexError),
+ ]
+
+ for unpacker in both_unpackers:
+ for format, values, throwable_class in cases:
+ try:
+ unpacker(format, values)
+ except StandardError:
+ pass
+ else:
+ raise AssertionError("didn't get exception: format %s, values %s, unpacker %s"
+ % (`format`, `values`, `unpacker`))
+
+ def test_unpack_repeated(self):
+ cases = [(('df$',
+ '\x00\x00\x00\x00HP C LaserJet 4500-PS\x00Windows 4.0\x00\\print$\\WIN40\\0\\PSCRIPT.DRV\x00\\print$\\WIN40\\0\\PSCRIPT.DRV\x00\\print$\\WIN40\\0\\PSCRIPT.DRV\x00\\print$\\WIN40\\0\\PSCRIPT.HLP\x00\x00RAW\x00\\print$\\WIN40\\0\\readme.wri\x00\\print$\\WIN40\\0\\pscript.drv\x00\\print$\\WIN40\\0\\pscript.hlp\x00'),
+ ([0L, 'HP C LaserJet 4500-PS', 'Windows 4.0', '\\print$\\WIN40\\0\\PSCRIPT.DRV', '\\print$\\WIN40\\0\\PSCRIPT.DRV', '\\print$\\WIN40\\0\\PSCRIPT.DRV', '\\print$\\WIN40\\0\\PSCRIPT.HLP', '', 'RAW', '\\print$\\WIN40\\0\\readme.wri', '\\print$\\WIN40\\0\\pscript.drv', '\\print$\\WIN40\\0\\pscript.hlp'], ''))]
+ for unpacker in both_unpackers:
+ for input, expected in cases:
+ result = apply(unpacker, input)
+ if result != expected:
+ raise AssertionError("%s:\n input: %s\n output: %s\n expected: %s" % (`unpacker`, `input`, `result`, `expected`))
+
+
+if __name__ == '__main__':
+ unittest.main()
+
diff --git a/source/python/gprinterdata b/source/python/gprinterdata
new file mode 100755
index 00000000000..cd062076c0b
--- /dev/null
+++ b/source/python/gprinterdata
@@ -0,0 +1,39 @@
+#!/usr/bin/env python
+
+import sys
+from gtkdictbrowser import GtkDictBrowser, hex_string
+import gtk
+from samba import spoolss
+import string
+import printerdata
+
+# Initialise printerdata dictionary
+
+if len(sys.argv) < 2 or len(sys.argv) > 3:
+ print "Usage: gprinterdata [--ex] <printer>"
+ print "where <printer> is a UNC printer name."
+ sys.exit(1)
+
+try:
+ host = string.replace(sys.argv[len(sys.argv) - 1], "/", "\\")
+ if sys.argv[1] == "--ex":
+ t = printerdata.printerdata_ex(host)
+ else:
+ t = printerdata.printerdata(host)
+except:
+ print "gprinterdata: error opening %s" % sys.argv[len(sys.argv) - 1]
+ sys.exit(1)
+
+# Create interface
+
+db = GtkDictBrowser(t)
+db.register_get_value_text_fn("", hex_string)
+db.build_ui('gprinterdata')
+
+# Override Python's handling of ctrl-c so we can break out of the
+# gui from the command line.
+
+import signal
+signal.signal(signal.SIGINT, signal.SIG_DFL)
+
+gtk.mainloop()
diff --git a/source/python/gtdbtool b/source/python/gtdbtool
new file mode 100755
index 00000000000..129f4fe0e2e
--- /dev/null
+++ b/source/python/gtdbtool
@@ -0,0 +1,39 @@
+#!/usr/bin/env python
+
+import sys
+from gtkdictbrowser import GtkDictBrowser
+import gtk
+from samba import tdb
+import string
+
+# Open handle on tdb
+
+if len(sys.argv) != 2:
+ print "Usage: gdbtool <tdbfile>"
+ sys.exit(1)
+
+try:
+ t = tdb.open(sys.argv[1])
+except tdb.error, t:
+ print "gtdbtool: error opening %s: %s" % (sys.argv[1], t)
+ sys.exit(1)
+
+# Create interface
+
+db = GtkDictBrowser(t)
+
+def display_key_x00(key):
+ """Remove \x00 from all keys as they mucks up GTK."""
+ return string.replace(key, "\x00", "")
+
+db.register_get_key_text_fn(display_key_x00)
+
+db.build_ui('gtdbtool')
+
+# Override Python's handling of ctrl-c so we can break out of the
+# gui from the command line.
+
+import signal
+signal.signal(signal.SIGINT, signal.SIG_DFL)
+
+gtk.mainloop()
diff --git a/source/python/gtkdictbrowser.py b/source/python/gtkdictbrowser.py
new file mode 100755
index 00000000000..dd8bed8f478
--- /dev/null
+++ b/source/python/gtkdictbrowser.py
@@ -0,0 +1,272 @@
+#!/usr/bin/python
+#
+# Browse a Python dictionary in a two pane graphical interface written
+# in GTK.
+#
+# The GtkDictBrowser class is supposed to be generic enough to allow
+# applications to override enough methods and produce a
+# domain-specific browser provided the information is presented as a
+# Python dictionary.
+#
+# Possible applications:
+#
+# - Windows registry browser
+# - SPOOLSS printerdata browser
+# - tdb file browser
+#
+
+from gtk import *
+import string, re
+
+class GtkDictBrowser:
+
+ def __init__(self, dict):
+ self.dict = dict
+
+ # This variable stores a list of (regexp, function) used to
+ # convert the raw value data to a displayable string.
+
+ self.get_value_text_fns = []
+ self.get_key_text = lambda x: x
+
+ # We can filter the list of keys displayed using a regex
+
+ self.filter_regex = ""
+
+ # Create and configure user interface widgets. A string argument is
+ # used to set the window title.
+
+ def build_ui(self, title):
+ win = GtkWindow()
+ win.set_title(title)
+
+ win.connect("destroy", mainquit)
+
+ hpaned = GtkHPaned()
+ win.add(hpaned)
+ hpaned.set_border_width(5)
+ hpaned.show()
+
+ vbox = GtkVBox()
+ hpaned.add1(vbox)
+ vbox.show()
+
+ scrolled_win = GtkScrolledWindow()
+ scrolled_win.set_policy(POLICY_AUTOMATIC, POLICY_AUTOMATIC)
+ vbox.pack_start(scrolled_win)
+ scrolled_win.show()
+
+ hbox = GtkHBox()
+ vbox.pack_end(hbox, expand = 0, padding = 5)
+ hbox.show()
+
+ label = GtkLabel("Filter:")
+ hbox.pack_start(label, expand = 0, padding = 5)
+ label.show()
+
+ self.entry = GtkEntry()
+ hbox.pack_end(self.entry, padding = 5)
+ self.entry.show()
+
+ self.entry.connect("activate", self.filter_activated)
+
+ self.list = GtkList()
+ self.list.set_selection_mode(SELECTION_MULTIPLE)
+ self.list.set_selection_mode(SELECTION_BROWSE)
+ scrolled_win.add_with_viewport(self.list)
+ self.list.show()
+
+ self.list.connect("select_child", self.key_selected)
+
+ scrolled_win = GtkScrolledWindow()
+ scrolled_win.set_policy(POLICY_AUTOMATIC, POLICY_AUTOMATIC)
+ hpaned.add2(scrolled_win)
+ scrolled_win.set_usize(500,400)
+ scrolled_win.show()
+
+ self.text = GtkText()
+ self.text.set_editable(FALSE)
+ scrolled_win.add_with_viewport(self.text)
+ self.text.show()
+
+ self.text.connect("event", self.event_handler)
+
+ self.menu = GtkMenu()
+ self.menu.show()
+
+ self.font = load_font("fixed")
+
+ self.update_keylist()
+
+ win.show()
+
+ # Add a key to the left hand side of the user interface
+
+ def add_key(self, key):
+ display_key = self.get_key_text(key)
+ list_item = GtkListItem(display_key)
+ list_item.set_data("raw_key", key) # Store raw key in item data
+ self.list.add(list_item)
+ list_item.show()
+
+ # Event handler registered by build_ui()
+
+ def event_handler(self, event, menu):
+ return FALSE
+
+ # Set the text to appear in the right hand side of the user interface
+
+ def set_value_text(self, item):
+
+ # Clear old old value in text window
+
+ self.text.delete_text(0, self.text.get_length())
+
+ if type(item) == str:
+
+ # The text widget has trouble inserting text containing NULL
+ # characters.
+
+ item = string.replace(item, "\x00", ".")
+
+ self.text.insert(self.font, None, None, item)
+
+ else:
+
+ # A non-text item
+
+ self.text.insert(self.font, None, None, repr(item))
+
+ # This function is called when a key is selected in the left hand side
+ # of the user interface.
+
+ def key_selected(self, list, list_item):
+ key = list_item.children()[0].get()
+
+ # Look for a match in the value display function list
+
+ text = self.dict[list_item.get_data("raw_key")]
+
+ for entry in self.get_value_text_fns:
+ if re.match(entry[0], key):
+ text = entry[1](text)
+ break
+
+ self.set_value_text(text)
+
+ # Refresh the key list by removing all items and re-inserting them.
+ # Items are only inserted if they pass through the filter regexp.
+
+ def update_keylist(self):
+ self.list.remove_items(self.list.children())
+ self.set_value_text("")
+ for k in self.dict.keys():
+ if re.match(self.filter_regex, k):
+ self.add_key(k)
+
+ # Invoked when the user hits return in the filter text entry widget.
+
+ def filter_activated(self, entry):
+ self.filter_regex = entry.get_text()
+ self.update_keylist()
+
+ # Register a key display function
+
+ def register_get_key_text_fn(self, fn):
+ self.get_key_text = fn
+
+ # Register a value display function
+
+ def register_get_value_text_fn(self, regexp, fn):
+ self.get_value_text_fns.append((regexp, fn))
+
+#
+# A utility function to convert a string to the standard hex + ascii format.
+# To display all values in hex do:
+# register_get_value_text_fn("", gtkdictbrowser.hex_string)
+#
+
+def hex_string(data):
+ """Return a hex dump of a string as a string.
+
+ The output produced is in the standard 16 characters per line hex +
+ ascii format:
+
+ 00000000: 40 00 00 00 00 00 00 00 40 00 00 00 01 00 04 80 @....... @.......
+ 00000010: 01 01 00 00 00 00 00 01 00 00 00 00 ........ ....
+ """
+
+ pos = 0 # Position in data
+ line = 0 # Line of data
+
+ hex = "" # Hex display
+ ascii = "" # ASCII display
+
+ result = ""
+
+ while pos < len(data):
+
+ # Start with header
+
+ if pos % 16 == 0:
+ hex = "%08x: " % (line * 16)
+ ascii = ""
+
+ # Add character
+
+ hex = hex + "%02x " % (ord(data[pos]))
+
+ if ord(data[pos]) < 32 or ord(data[pos]) > 176:
+ ascii = ascii + '.'
+ else:
+ ascii = ascii + data[pos]
+
+ pos = pos + 1
+
+ # Add separator if half way
+
+ if pos % 16 == 8:
+ hex = hex + " "
+ ascii = ascii + " "
+
+ # End of line
+
+ if pos % 16 == 0:
+ result = result + "%s %s\n" % (hex, ascii)
+ line = line + 1
+
+ # Leftover bits
+
+ if pos % 16 != 0:
+
+ # Pad hex string
+
+ for i in range(0, (16 - (pos % 16))):
+ hex = hex + " "
+
+ # Half way separator
+
+ if (pos % 16) < 8:
+ hex = hex + " "
+
+ result = result + "%s %s\n" % (hex, ascii)
+
+ return result
+
+# For testing purposes, create a fixed dictionary to browse with
+
+if __name__ == "__main__":
+
+ dict = {"chicken": "ham", "spam": "fun", "subdict": {"a": "b", "c": "d"}}
+
+ db = GtkDictBrowser(dict)
+
+ db.build_ui("GtkDictBrowser")
+
+ # Override Python's handling of ctrl-c so we can break out of the
+ # gui from the command line.
+
+ import signal
+ signal.signal(signal.SIGINT, signal.SIG_DFL)
+
+ mainloop()
diff --git a/source/python/py_common.c b/source/python/py_common.c
new file mode 100644
index 00000000000..02d22bbdab5
--- /dev/null
+++ b/source/python/py_common.c
@@ -0,0 +1,259 @@
+/*
+ Python wrappers for DCERPC/SMB client routines.
+
+ Copyright (C) Tim Potter, 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 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 "python/py_common.h"
+
+/* Return a tuple of (error code, error string) from a WERROR */
+
+PyObject *py_werror_tuple(WERROR werror)
+{
+ return Py_BuildValue("[is]", W_ERROR_V(werror),
+ dos_errstr(werror));
+}
+
+/* Return a tuple of (error code, error string) from a WERROR */
+
+PyObject *py_ntstatus_tuple(NTSTATUS ntstatus)
+{
+ return Py_BuildValue("[is]", NT_STATUS_V(ntstatus),
+ nt_errstr(ntstatus));
+}
+
+/* Initialise samba client routines */
+
+static BOOL initialised;
+
+void py_samba_init(void)
+{
+ if (initialised)
+ return;
+
+ /* Load configuration file */
+
+ if (!lp_load(dyn_CONFIGFILE, True, False, False))
+ fprintf(stderr, "Can't load %s\n", dyn_CONFIGFILE);
+
+ /* Misc other stuff */
+
+ load_interfaces();
+ init_names();
+
+ initialised = True;
+}
+
+/* Debuglevel routines */
+
+PyObject *get_debuglevel(PyObject *self, PyObject *args)
+{
+ PyObject *debuglevel;
+
+ if (!PyArg_ParseTuple(args, ""))
+ return NULL;
+
+ debuglevel = PyInt_FromLong(DEBUGLEVEL);
+
+ return debuglevel;
+}
+
+PyObject *set_debuglevel(PyObject *self, PyObject *args)
+{
+ int debuglevel;
+
+ if (!PyArg_ParseTuple(args, "i", &debuglevel))
+ return NULL;
+
+ DEBUGLEVEL = debuglevel;
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+/* Initialise logging */
+
+PyObject *py_setup_logging(PyObject *self, PyObject *args, PyObject *kw)
+{
+ BOOL interactive = False;
+ char *logfilename = NULL;
+ static char *kwlist[] = {"interactive", "logfilename", NULL};
+
+ if (!PyArg_ParseTupleAndKeywords(
+ args, kw, "|is", kwlist, &interactive, &logfilename))
+ return NULL;
+
+ if (interactive && logfilename) {
+ PyErr_SetString(PyExc_RuntimeError,
+ "can't be interactive and set log file name");
+ return NULL;
+ }
+
+ if (interactive)
+ setup_logging("spoolss", True);
+
+ if (logfilename) {
+ lp_set_logfile(logfilename);
+ setup_logging(logfilename, False);
+ reopen_logs();
+ }
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+/* Parse credentials from a python dictionary. The dictionary can
+ only have the keys "username", "domain" and "password". Return
+ True for valid credentials in which case the username, domain and
+ password are set to pointers to their values from the dicationary.
+ If returns False, the errstr is set to point at some mallocated
+ memory describing the error. */
+
+BOOL py_parse_creds(PyObject *creds, char **username, char **domain,
+ char **password, char **errstr)
+{
+ /* Initialise anonymous credentials */
+
+ *username = "";
+ *domain = "";
+ *password = "";
+
+ if (creds && PyDict_Size(creds) > 0) {
+ PyObject *username_obj, *password_obj, *domain_obj;
+ PyObject *key, *value;
+ int i;
+
+ /* Check for presence of required fields */
+
+ username_obj = PyDict_GetItemString(creds, "username");
+ domain_obj = PyDict_GetItemString(creds, "domain");
+ password_obj = PyDict_GetItemString(creds, "password");
+
+ if (!username_obj) {
+ *errstr = strdup("no username field in credential");
+ return False;
+ }
+
+ if (!domain_obj) {
+ *errstr = strdup("no domain field in credential");
+ return False;
+ }
+
+ if (!password_obj) {
+ *errstr = strdup("no password field in credential");
+ return False;
+ }
+
+ /* Check type of required fields */
+
+ if (!PyString_Check(username_obj)) {
+ *errstr = strdup("username field is not string type");
+ return False;
+ }
+
+ if (!PyString_Check(domain_obj)) {
+ *errstr = strdup("domain field is not string type");
+ return False;
+ }
+
+ if (!PyString_Check(password_obj)) {
+ *errstr = strdup("password field is not string type");
+ return False;
+ }
+
+ /* Look for any extra fields */
+
+ i = 0;
+
+ while (PyDict_Next(creds, &i, &key, &value)) {
+ if (strcmp(PyString_AsString(key), "domain") != 0 &&
+ strcmp(PyString_AsString(key), "username") != 0 &&
+ strcmp(PyString_AsString(key), "password") != 0) {
+ asprintf(errstr,
+ "creds contain extra field '%s'",
+ PyString_AsString(key));
+ return False;
+ }
+ }
+
+ /* Assign values */
+
+ *username = PyString_AsString(username_obj);
+ *domain = PyString_AsString(domain_obj);
+ *password = PyString_AsString(password_obj);
+ }
+
+ *errstr = NULL;
+
+ return True;
+}
+
+/* Return a cli_state to a RPC pipe on the given server. Use the
+ credentials passed if not NULL. If an error occurs errstr is set to a
+ string describing the error and NULL is returned. If set, errstr must
+ be freed by calling free(). */
+
+struct cli_state *open_pipe_creds(char *server, PyObject *creds,
+ int pipe_idx, char **errstr)
+{
+ char *username, *password, *domain;
+ struct cli_state *cli;
+ NTSTATUS result;
+
+ /* Extract credentials from the python dictionary */
+
+ if (!py_parse_creds(creds, &username, &domain, &password, errstr))
+ return NULL;
+
+ /* Now try to connect */
+
+ result = cli_full_connection(
+ &cli, NULL, server, NULL, 0, "IPC$", "IPC",
+ username, domain, password, 0, Undefined, NULL);
+
+ if (!NT_STATUS_IS_OK(result)) {
+ *errstr = strdup("error connecting to IPC$ pipe");
+ return NULL;
+ }
+
+ if (!cli_nt_session_open(cli, pipe_idx)) {
+ cli_shutdown(cli);
+ asprintf(errstr, "error opening pipe index %d", pipe_idx);
+ return NULL;
+ }
+
+ *errstr = NULL;
+
+ return cli;
+}
+
+/* Return true if a dictionary contains a "level" key with an integer
+ value. Set the value if so. */
+
+BOOL get_level_value(PyObject *dict, uint32 *level)
+{
+ PyObject *obj;
+
+ if (!(obj = PyDict_GetItemString(dict, "level")) ||
+ !PyInt_Check(obj))
+ return False;
+
+ if (level)
+ *level = PyInt_AsLong(obj);
+
+ return True;
+}
diff --git a/source/python/py_common.h b/source/python/py_common.h
new file mode 100644
index 00000000000..2bbd148ff4b
--- /dev/null
+++ b/source/python/py_common.h
@@ -0,0 +1,67 @@
+/*
+ Python wrappers for DCERPC/SMB client routines.
+
+ Copyright (C) Tim Potter, 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 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 _PY_COMMON_H
+#define _PY_COMMON_H
+
+#include "includes.h"
+
+/* This symbol is used in both includes.h and Python.h which causes an
+ annoying compiler warning. */
+
+#ifdef HAVE_FSTAT
+#undef HAVE_FSTAT
+#endif
+
+#include "Python.h"
+
+/* Return a cli_state struct opened on the specified pipe. If credentials
+ are passed use them. */
+
+typedef struct cli_state *(cli_pipe_fn)(
+ struct cli_state *cli, char *system_name,
+ struct ntuser_creds *creds);
+
+/* The following definitions come from python/py_common.c */
+
+PyObject *py_werror_tuple(WERROR werror);
+PyObject *py_ntstatus_tuple(NTSTATUS ntstatus);
+void py_samba_init(void);
+PyObject *get_debuglevel(PyObject *self, PyObject *args);
+PyObject *set_debuglevel(PyObject *self, PyObject *args);
+PyObject *py_setup_logging(PyObject *self, PyObject *args, PyObject *kw);
+BOOL py_parse_creds(PyObject *creds, char **username, char **domain,
+ char **password, char **errstr);
+struct cli_state *open_pipe_creds(char *server, PyObject *creds,
+ int pipe_idx, char **errstr);
+BOOL get_level_value(PyObject *dict, uint32 *level);
+
+/* The following definitions come from python/py_ntsec.c */
+
+BOOL py_from_SID(PyObject **obj, DOM_SID *sid);
+BOOL py_to_SID(DOM_SID *sid, PyObject *obj);
+BOOL py_from_ACE(PyObject **dict, SEC_ACE *ace);
+BOOL py_to_ACE(SEC_ACE *ace, PyObject *dict);
+BOOL py_from_ACL(PyObject **dict, SEC_ACL *acl);
+BOOL py_to_ACL(SEC_ACL *acl, PyObject *dict, TALLOC_CTX *mem_ctx);
+BOOL py_from_SECDESC(PyObject **dict, SEC_DESC *sd);
+BOOL py_to_SECDESC(SEC_DESC **sd, PyObject *dict, TALLOC_CTX *mem_ctx);
+
+#endif /* _PY_COMMON_H */
diff --git a/source/python/py_conv.c b/source/python/py_conv.c
new file mode 100644
index 00000000000..d0a2d78aabd
--- /dev/null
+++ b/source/python/py_conv.c
@@ -0,0 +1,223 @@
+/*
+ Python wrappers for DCERPC/SMB client routines.
+
+ Copyright (C) Tim Potter, 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 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 "py_conv.h"
+
+/* Helper for rpcstr_pull() function */
+
+static void fstr_pull(fstring str, UNISTR *uni)
+{
+ rpcstr_pull(str, uni->buffer, sizeof(fstring), -1, STR_TERMINATE);
+}
+
+static void fstr_pull2(fstring str, UNISTR2 *uni)
+{
+ rpcstr_pull(str, uni->buffer, sizeof(fstring), -1, STR_TERMINATE);
+}
+
+/* Convert a structure to a Python dict */
+
+PyObject *from_struct(void *s, struct pyconv *conv)
+{
+ PyObject *obj, *item;
+ int i;
+
+ obj = PyDict_New();
+
+ for (i = 0; conv[i].name; i++) {
+ switch (conv[i].type) {
+ case PY_UNISTR: {
+ UNISTR *u = (UNISTR *)((char *)s + conv[i].offset);
+ fstring str = "";
+
+ if (u->buffer)
+ fstr_pull(str, u);
+
+ item = PyString_FromString(str);
+ PyDict_SetItemString(obj, conv[i].name, item);
+
+ break;
+ }
+ case PY_UNISTR2: {
+ UNISTR2 *u = (UNISTR2 *)((char *)s + conv[i].offset);
+ fstring str = "";
+
+ if (u->buffer)
+ fstr_pull2(str, u);
+
+ item = PyString_FromString(str);
+ PyDict_SetItemString(obj, conv[i].name, item);
+
+ break;
+ }
+ case PY_UINT32: {
+ uint32 *u = (uint32 *)((char *)s + conv[i].offset);
+
+ item = PyInt_FromLong(*u);
+ PyDict_SetItemString(obj, conv[i].name, item);
+
+ break;
+ }
+ case PY_UINT16: {
+ uint16 *u = (uint16 *)((char *)s + conv[i].offset);
+
+ item = PyInt_FromLong(*u);
+ PyDict_SetItemString(obj, conv[i].name, item);
+
+ break;
+ }
+ case PY_STRING: {
+ char *str = (char *)s + conv[i].offset;
+
+ item = PyString_FromString(str);
+ PyDict_SetItemString(obj, conv[i].name, item);
+
+ break;
+ }
+ case PY_UID: {
+ uid_t *uid = (uid_t *)((char *)s + conv[i].offset);
+
+ item = PyInt_FromLong(*uid);
+ PyDict_SetItemString(obj, conv[i].name, item);
+
+ break;
+ }
+ case PY_GID: {
+ gid_t *gid = (gid_t *)((char *)s + conv[i].offset);
+
+ item = PyInt_FromLong(*gid);
+ PyDict_SetItemString(obj, conv[i].name, item);
+
+ break;
+ }
+ default:
+
+ break;
+ }
+ }
+
+ return obj;
+}
+
+/* Convert a Python dict to a structure */
+
+BOOL to_struct(void *s, PyObject *dict, struct pyconv *conv)
+{
+ PyObject *visited, *key, *value;
+ BOOL result = False;
+ int i;
+
+ visited = PyDict_New();
+
+ for (i = 0; conv[i].name; i++) {
+ PyObject *obj;
+
+ obj = PyDict_GetItemString(dict, conv[i].name);
+
+ if (!obj)
+ goto done;
+
+ switch (conv[i].type) {
+ case PY_UNISTR: {
+ UNISTR *u = (UNISTR *)((char *)s + conv[i].offset);
+ char *str = "";
+
+ if (!PyString_Check(obj))
+ goto done;
+
+ str = PyString_AsString(obj);
+ init_unistr(u, str);
+
+ break;
+ }
+ case PY_UINT32: {
+ uint32 *u = (uint32 *)((char *)s + conv[i].offset);
+
+ if (!PyInt_Check(obj))
+ goto done;
+
+ *u = PyInt_AsLong(obj);
+
+ break;
+ }
+ case PY_UINT16: {
+ uint16 *u = (uint16 *)((char *)s + conv[i].offset);
+
+ if (!PyInt_Check(obj))
+ goto done;
+
+ *u = PyInt_AsLong(obj);
+ break;
+ }
+ default:
+ break;
+ }
+
+ /* Mark as visited */
+
+ PyDict_SetItemString(visited, conv[i].name,
+ PyInt_FromLong(1));
+ }
+
+ /* Iterate over each item in the input dictionary and see if it was
+ visited. If it wasn't then the user has added some extra crap
+ to the dictionary. */
+
+ i = 0;
+
+ while (PyDict_Next(dict, &i, &key, &value)) {
+ if (!PyDict_GetItem(visited, key))
+ goto done;
+ }
+
+ result = True;
+
+done:
+ /* We must decrement the reference count here or the visited
+ dictionary will not be freed. */
+
+ Py_DECREF(visited);
+
+ return result;
+}
+
+/* Convert a NULL terminated list of NULL terminated unicode strings
+ to a list of (char *) strings */
+
+PyObject *from_unistr_list(uint16 *dependentfiles)
+{
+ PyObject *list;
+ int offset = 0;
+
+ list = PyList_New(0);
+
+ while (*(dependentfiles + offset) != 0) {
+ fstring name;
+ int len;
+
+ len = rpcstr_pull(name, dependentfiles + offset,
+ sizeof(fstring), -1, STR_TERMINATE);
+
+ offset += len / 2;
+ PyList_Append(list, PyString_FromString(name));
+ }
+
+ return list;
+}
diff --git a/source/python/py_conv.h b/source/python/py_conv.h
new file mode 100644
index 00000000000..798661c3a0e
--- /dev/null
+++ b/source/python/py_conv.h
@@ -0,0 +1,44 @@
+/*
+ Python wrappers for DCERPC/SMB client routines.
+
+ Copyright (C) Tim Potter, 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 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 _PY_CONV_H
+#define _PY_CONV_H
+
+#include "python/py_common.h"
+
+enum pyconv_types { PY_UNISTR, PY_UNISTR2, PY_UINT32, PY_UINT16, PY_STRING,
+ PY_UID, PY_GID };
+
+struct pyconv {
+ char *name; /* Name of member */
+ enum pyconv_types type; /* Type */
+ size_t offset; /* Offset into structure */
+};
+
+PyObject *from_struct(void *s, struct pyconv *conv);
+BOOL to_struct(void *s, PyObject *dict, struct pyconv *conv);
+PyObject *from_unistr_list(uint16 *dependentfiles);
+
+/* Another version of offsetof (-: */
+
+#undef offsetof
+#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
+
+#endif /* _PY_CONV_H */
diff --git a/source/python/py_lsa.c b/source/python/py_lsa.c
new file mode 100644
index 00000000000..07191be8682
--- /dev/null
+++ b/source/python/py_lsa.c
@@ -0,0 +1,484 @@
+/*
+ Python wrappers for DCERPC/SMB client routines.
+
+ Copyright (C) Tim Potter, 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 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 "python/py_lsa.h"
+
+PyObject *new_lsa_policy_hnd_object(struct cli_state *cli, TALLOC_CTX *mem_ctx,
+ POLICY_HND *pol)
+{
+ lsa_policy_hnd_object *o;
+
+ o = PyObject_New(lsa_policy_hnd_object, &lsa_policy_hnd_type);
+
+ o->cli = cli;
+ o->mem_ctx = mem_ctx;
+ memcpy(&o->pol, pol, sizeof(POLICY_HND));
+
+ return (PyObject*)o;
+}
+
+/*
+ * Exceptions raised by this module
+ */
+
+PyObject *lsa_error; /* This indicates a non-RPC related error
+ such as name lookup failure */
+
+PyObject *lsa_ntstatus; /* This exception is raised when a RPC call
+ returns a status code other than
+ NT_STATUS_OK */
+
+/*
+ * Open/close lsa handles
+ */
+
+static PyObject *lsa_open_policy(PyObject *self, PyObject *args,
+ PyObject *kw)
+{
+ static char *kwlist[] = { "servername", "creds", "access", NULL };
+ char *server, *errstr;
+ PyObject *creds = NULL, *result = NULL;
+ uint32 desired_access = MAXIMUM_ALLOWED_ACCESS;
+ struct cli_state *cli = NULL;
+ NTSTATUS ntstatus;
+ TALLOC_CTX *mem_ctx = NULL;
+ POLICY_HND hnd;
+
+ if (!PyArg_ParseTupleAndKeywords(
+ args, kw, "s|Oi", kwlist, &server, &creds, &desired_access))
+ return NULL;
+
+ if (creds && creds != Py_None && !PyDict_Check(creds)) {
+ PyErr_SetString(PyExc_TypeError,
+ "credentials must be dictionary or None");
+ return NULL;
+ }
+
+ if (server[0] != '\\' || server[1] != '\\') {
+ PyErr_SetString(PyExc_ValueError, "UNC name required");
+ return NULL;
+ }
+
+ server += 2;
+
+ if (!(cli = open_pipe_creds(server, creds, PI_LSARPC, &errstr))) {
+ PyErr_SetString(lsa_error, errstr);
+ free(errstr);
+ return NULL;
+ }
+
+ if (!(mem_ctx = talloc_init("lsa_open_policy"))) {
+ PyErr_SetString(lsa_error, "unable to init talloc context\n");
+ goto done;
+ }
+
+ ntstatus = cli_lsa_open_policy(cli, mem_ctx, True,
+ SEC_RIGHTS_MAXIMUM_ALLOWED, &hnd);
+
+ if (!NT_STATUS_IS_OK(ntstatus)) {
+ PyErr_SetObject(lsa_ntstatus, py_ntstatus_tuple(ntstatus));
+ goto done;
+ }
+
+ result = new_lsa_policy_hnd_object(cli, mem_ctx, &hnd);
+
+done:
+ if (!result) {
+ if (cli)
+ cli_shutdown(cli);
+
+ talloc_destroy(mem_ctx);
+ }
+
+ return result;
+}
+
+static PyObject *lsa_close(PyObject *self, PyObject *args, PyObject *kw)
+{
+ PyObject *po;
+ lsa_policy_hnd_object *hnd;
+ NTSTATUS result;
+
+ /* Parse parameters */
+
+ if (!PyArg_ParseTuple(args, "O!", &lsa_policy_hnd_type, &po))
+ return NULL;
+
+ hnd = (lsa_policy_hnd_object *)po;
+
+ /* Call rpc function */
+
+ result = cli_lsa_close(hnd->cli, hnd->mem_ctx, &hnd->pol);
+
+ /* Cleanup samba stuff */
+
+ cli_shutdown(hnd->cli);
+ talloc_destroy(hnd->mem_ctx);
+
+ /* Return value */
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static PyObject *lsa_lookup_names(PyObject *self, PyObject *args)
+{
+ PyObject *py_names, *result = NULL;
+ NTSTATUS ntstatus;
+ lsa_policy_hnd_object *hnd = (lsa_policy_hnd_object *)self;
+ int num_names, i;
+ const char **names;
+ DOM_SID *sids;
+ TALLOC_CTX *mem_ctx = NULL;
+ uint32 *name_types;
+
+ if (!PyArg_ParseTuple(args, "O", &py_names))
+ return NULL;
+
+ if (!PyList_Check(py_names) && !PyString_Check(py_names)) {
+ PyErr_SetString(PyExc_TypeError, "must be list or string");
+ return NULL;
+ }
+
+ if (!(mem_ctx = talloc_init("lsa_lookup_names"))) {
+ PyErr_SetString(lsa_error, "unable to init talloc context\n");
+ goto done;
+ }
+
+ if (PyList_Check(py_names)) {
+
+ /* Convert list to char ** array */
+
+ num_names = PyList_Size(py_names);
+ names = (const char **)talloc(mem_ctx, num_names * sizeof(char *));
+
+ for (i = 0; i < num_names; i++) {
+ PyObject *obj = PyList_GetItem(py_names, i);
+
+ names[i] = talloc_strdup(mem_ctx, PyString_AsString(obj));
+ }
+
+ } else {
+
+ /* Just a single element */
+
+ num_names = 1;
+ names = (const char **)talloc(mem_ctx, sizeof(char *));
+
+ names[0] = PyString_AsString(py_names);
+ }
+
+ ntstatus = cli_lsa_lookup_names(hnd->cli, mem_ctx, &hnd->pol,
+ num_names, names, &sids, &name_types);
+
+ if (!NT_STATUS_IS_OK(ntstatus) && NT_STATUS_V(ntstatus) != 0x107) {
+ PyErr_SetObject(lsa_ntstatus, py_ntstatus_tuple(ntstatus));
+ goto done;
+ }
+
+ result = PyList_New(num_names);
+
+ for (i = 0; i < num_names; i++) {
+ PyObject *sid_obj, *obj;
+
+ py_from_SID(&sid_obj, &sids[i]);
+
+ obj = Py_BuildValue("(Ni)", sid_obj, name_types[i]);
+
+ PyList_SetItem(result, i, obj);
+ }
+
+ done:
+ talloc_destroy(mem_ctx);
+
+ return result;
+}
+
+static PyObject *lsa_lookup_sids(PyObject *self, PyObject *args,
+ PyObject *kw)
+{
+ PyObject *py_sids, *result = NULL;
+ NTSTATUS ntstatus;
+ int num_sids, i;
+ char **domains, **names;
+ uint32 *types;
+ lsa_policy_hnd_object *hnd = (lsa_policy_hnd_object *)self;
+ TALLOC_CTX *mem_ctx = NULL;
+ DOM_SID *sids;
+
+ if (!PyArg_ParseTuple(args, "O", &py_sids))
+ return NULL;
+
+ if (!PyList_Check(py_sids) && !PyString_Check(py_sids)) {
+ PyErr_SetString(PyExc_TypeError, "must be list or string");
+ return NULL;
+ }
+
+ if (!(mem_ctx = talloc_init("lsa_lookup_sids"))) {
+ PyErr_SetString(lsa_error, "unable to init talloc context\n");
+ goto done;
+ }
+
+ if (PyList_Check(py_sids)) {
+
+ /* Convert dictionary to char ** array */
+
+ num_sids = PyList_Size(py_sids);
+ sids = (DOM_SID *)talloc(mem_ctx, num_sids * sizeof(DOM_SID));
+
+ memset(sids, 0, num_sids * sizeof(DOM_SID));
+
+ for (i = 0; i < num_sids; i++) {
+ PyObject *obj = PyList_GetItem(py_sids, i);
+
+ if (!string_to_sid(&sids[i], PyString_AsString(obj))) {
+ PyErr_SetString(PyExc_ValueError, "string_to_sid failed");
+ goto done;
+ }
+ }
+
+ } else {
+
+ /* Just a single element */
+
+ num_sids = 1;
+ sids = (DOM_SID *)talloc(mem_ctx, sizeof(DOM_SID));
+
+ if (!string_to_sid(&sids[0], PyString_AsString(py_sids))) {
+ PyErr_SetString(PyExc_ValueError, "string_to_sid failed");
+ goto done;
+ }
+ }
+
+ ntstatus = cli_lsa_lookup_sids(hnd->cli, mem_ctx, &hnd->pol,
+ num_sids, sids, &domains, &names,
+ &types);
+
+ if (!NT_STATUS_IS_OK(ntstatus)) {
+ PyErr_SetObject(lsa_ntstatus, py_ntstatus_tuple(ntstatus));
+ goto done;
+ }
+
+ result = PyList_New(num_sids);
+
+ for (i = 0; i < num_sids; i++) {
+ PyObject *obj;
+
+ obj = Py_BuildValue("{sssssi}", "username", names[i],
+ "domain", domains[i], "name_type",
+ types[i]);
+
+ PyList_SetItem(result, i, obj);
+ }
+
+ done:
+ talloc_destroy(mem_ctx);
+
+ return result;
+}
+
+static PyObject *lsa_enum_trust_dom(PyObject *self, PyObject *args)
+{
+ lsa_policy_hnd_object *hnd = (lsa_policy_hnd_object *)self;
+ NTSTATUS ntstatus;
+ uint32 enum_ctx = 0, num_domains, i;
+ char **domain_names;
+ DOM_SID *domain_sids;
+ PyObject *result;
+
+ if (!PyArg_ParseTuple(args, ""))
+ return NULL;
+
+ ntstatus = cli_lsa_enum_trust_dom(
+ hnd->cli, hnd->mem_ctx, &hnd->pol, &enum_ctx,
+ &num_domains, &domain_names, &domain_sids);
+
+ if (!NT_STATUS_IS_OK(ntstatus)) {
+ PyErr_SetObject(lsa_ntstatus, py_ntstatus_tuple(ntstatus));
+ return NULL;
+ }
+
+ result = PyList_New(num_domains);
+
+ for (i = 0; i < num_domains; i++) {
+ fstring sid_str;
+
+ sid_to_string(sid_str, &domain_sids[i]);
+ PyList_SetItem(
+ result, i,
+ Py_BuildValue("(ss)", domain_names[i], sid_str));
+ }
+
+ return result;
+}
+
+/*
+ * Method dispatch tables
+ */
+
+static PyMethodDef lsa_hnd_methods[] = {
+
+ /* SIDs<->names */
+
+ { "lookup_sids", (PyCFunction)lsa_lookup_sids,
+ METH_VARARGS | METH_KEYWORDS,
+ "Convert sids to names." },
+
+ { "lookup_names", (PyCFunction)lsa_lookup_names,
+ METH_VARARGS | METH_KEYWORDS,
+ "Convert names to sids." },
+
+ /* Trusted domains */
+
+ { "enum_trusted_domains", (PyCFunction)lsa_enum_trust_dom,
+ METH_VARARGS,
+ "Enumerate trusted domains." },
+
+ { NULL }
+};
+
+static void py_lsa_policy_hnd_dealloc(PyObject* self)
+{
+ PyObject_Del(self);
+}
+
+static PyObject *py_lsa_policy_hnd_getattr(PyObject *self, char *attrname)
+{
+ return Py_FindMethod(lsa_hnd_methods, self, attrname);
+}
+
+PyTypeObject lsa_policy_hnd_type = {
+ PyObject_HEAD_INIT(NULL)
+ 0,
+ "LSA Policy Handle",
+ sizeof(lsa_policy_hnd_object),
+ 0,
+ py_lsa_policy_hnd_dealloc, /*tp_dealloc*/
+ 0, /*tp_print*/
+ py_lsa_policy_hnd_getattr, /*tp_getattr*/
+ 0, /*tp_setattr*/
+ 0, /*tp_compare*/
+ 0, /*tp_repr*/
+ 0, /*tp_as_number*/
+ 0, /*tp_as_sequence*/
+ 0, /*tp_as_mapping*/
+ 0, /*tp_hash */
+};
+
+static PyMethodDef lsa_methods[] = {
+
+ /* Open/close lsa handles */
+
+ { "open_policy", (PyCFunction)lsa_open_policy,
+ METH_VARARGS | METH_KEYWORDS,
+ "Open a policy handle" },
+
+ { "close", (PyCFunction)lsa_close,
+ METH_VARARGS,
+ "Close a policy handle" },
+
+ /* Other stuff - this should really go into a samba config module
+ but for the moment let's leave it here. */
+
+ { "setup_logging", (PyCFunction)py_setup_logging,
+ METH_VARARGS | METH_KEYWORDS,
+ "Set up debug logging.\n"
+"\n"
+"Initialises Samba's debug logging system. One argument is expected which\n"
+"is a boolean specifying whether debugging is interactive and sent to stdout\n"
+"or logged to a file.\n"
+"\n"
+"Example:\n"
+"\n"
+">>> lsa.setup_logging(interactive = 1)" },
+
+ { "get_debuglevel", (PyCFunction)get_debuglevel,
+ METH_VARARGS,
+ "Set the current debug level.\n"
+"\n"
+"Example:\n"
+"\n"
+">>> lsa.get_debuglevel()\n"
+"0" },
+
+ { "set_debuglevel", (PyCFunction)set_debuglevel,
+ METH_VARARGS,
+ "Get the current debug level.\n"
+"\n"
+"Example:\n"
+"\n"
+">>> lsa.set_debuglevel(10)" },
+
+ { NULL }
+};
+
+static struct const_vals {
+ char *name;
+ uint32 value;
+} module_const_vals[] = {
+ { NULL }
+};
+
+static void const_init(PyObject *dict)
+{
+ struct const_vals *tmp;
+ PyObject *obj;
+
+ for (tmp = module_const_vals; tmp->name; tmp++) {
+ obj = PyInt_FromLong(tmp->value);
+ PyDict_SetItemString(dict, tmp->name, obj);
+ Py_DECREF(obj);
+ }
+}
+
+/*
+ * Module initialisation
+ */
+
+void initlsa(void)
+{
+ PyObject *module, *dict;
+
+ /* Initialise module */
+
+ module = Py_InitModule("lsa", lsa_methods);
+ dict = PyModule_GetDict(module);
+
+ lsa_error = PyErr_NewException("lsa.error", NULL, NULL);
+ PyDict_SetItemString(dict, "error", lsa_error);
+
+ lsa_ntstatus = PyErr_NewException("lsa.ntstatus", NULL, NULL);
+ PyDict_SetItemString(dict, "ntstatus", lsa_ntstatus);
+
+ /* Initialise policy handle object */
+
+ lsa_policy_hnd_type.ob_type = &PyType_Type;
+
+ /* Initialise constants */
+
+ const_init(dict);
+
+ /* Do samba initialisation */
+
+ py_samba_init();
+
+ setup_logging("lsa", True);
+ DEBUGLEVEL = 10;
+}
diff --git a/source/python/py_lsa.h b/source/python/py_lsa.h
new file mode 100644
index 00000000000..99f3de50b1f
--- /dev/null
+++ b/source/python/py_lsa.h
@@ -0,0 +1,41 @@
+/*
+ Python wrappers for DCERPC/SMB client routines.
+
+ Copyright (C) Tim Potter, 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 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 _PY_LSA_H
+#define _PY_LSA_H
+
+#include "python/py_common.h"
+
+/* LSA policy handle object */
+
+typedef struct {
+ PyObject_HEAD
+ struct cli_state *cli;
+ TALLOC_CTX *mem_ctx;
+ POLICY_HND pol;
+} lsa_policy_hnd_object;
+
+/* Exceptions raised by this module */
+
+extern PyTypeObject lsa_policy_hnd_type;
+
+extern PyObject *lsa_error;
+
+#endif /* _PY_LSA_H */
diff --git a/source/python/py_ntsec.c b/source/python/py_ntsec.c
new file mode 100644
index 00000000000..5ce5e8fc1be
--- /dev/null
+++ b/source/python/py_ntsec.c
@@ -0,0 +1,288 @@
+/*
+ Python wrappers for DCERPC/SMB client routines.
+
+ Copyright (C) Tim Potter, 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 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 "python/py_common.h"
+
+/* Convert a SID to a Python dict */
+
+BOOL py_from_SID(PyObject **obj, DOM_SID *sid)
+{
+ fstring sidstr;
+
+ if (!sid) {
+ Py_INCREF(Py_None);
+ *obj = Py_None;
+ return True;
+ }
+
+ if (!sid_to_string(sidstr, sid))
+ return False;
+
+ *obj = PyString_FromString(sidstr);
+
+ return True;
+}
+
+BOOL py_to_SID(DOM_SID *sid, PyObject *obj)
+{
+ if (!PyString_Check(obj))
+ return False;
+
+ return string_to_sid(sid, PyString_AsString(obj));
+}
+
+BOOL py_from_ACE(PyObject **dict, SEC_ACE *ace)
+{
+ PyObject *obj;
+
+ if (!ace) {
+ Py_INCREF(Py_None);
+ *dict = Py_None;
+ return True;
+ }
+
+ *dict = Py_BuildValue("{sisisi}", "type", ace->type,
+ "flags", ace->flags,
+ "mask", ace->info.mask);
+
+ if (py_from_SID(&obj, &ace->trustee)) {
+ PyDict_SetItemString(*dict, "trustee", obj);
+ Py_DECREF(obj);
+ }
+
+ return True;
+}
+
+BOOL py_to_ACE(SEC_ACE *ace, PyObject *dict)
+{
+ PyObject *obj;
+ uint8 ace_type, ace_flags;
+ DOM_SID trustee;
+ SEC_ACCESS sec_access;
+
+ if (!PyDict_Check(dict))
+ return False;
+
+ if (!(obj = PyDict_GetItemString(dict, "type")) ||
+ !PyInt_Check(obj))
+ return False;
+
+ ace_type = PyInt_AsLong(obj);
+
+ if (!(obj = PyDict_GetItemString(dict, "flags")) ||
+ !PyInt_Check(obj))
+ return False;
+
+ ace_flags = PyInt_AsLong(obj);
+
+ if (!(obj = PyDict_GetItemString(dict, "trustee")) ||
+ !PyString_Check(obj))
+ return False;
+
+ if (!py_to_SID(&trustee, obj))
+ return False;
+
+ if (!(obj = PyDict_GetItemString(dict, "mask")) ||
+ !PyInt_Check(obj))
+ return False;
+
+ sec_access.mask = PyInt_AsLong(obj);
+
+ init_sec_ace(ace, &trustee, ace_type, sec_access, ace_flags);
+
+ /* Fill in size field */
+
+ ace->size = SEC_ACE_HEADER_SIZE + sid_size(&trustee);
+
+ return True;
+}
+
+BOOL py_from_ACL(PyObject **dict, SEC_ACL *acl)
+{
+ PyObject *ace_list;
+ int i;
+
+ if (!acl) {
+ Py_INCREF(Py_None);
+ *dict = Py_None;
+ return True;
+ }
+
+ ace_list = PyList_New(acl->num_aces);
+
+ for (i = 0; i < acl->num_aces; i++) {
+ PyObject *obj;
+
+ if (py_from_ACE(&obj, &acl->ace[i]))
+ PyList_SetItem(ace_list, i, obj);
+ }
+
+ *dict = Py_BuildValue("{sisN}", "revision", acl->revision,
+ "ace_list", ace_list);
+
+ return True;
+}
+
+BOOL py_to_ACL(SEC_ACL *acl, PyObject *dict, TALLOC_CTX *mem_ctx)
+{
+ PyObject *obj;
+ uint32 i;
+
+ if (!(obj = PyDict_GetItemString(dict, "revision")) ||
+ !PyInt_Check(obj))
+ return False;
+
+ acl->revision = PyInt_AsLong(obj);
+
+ if (!(obj = PyDict_GetItemString(dict, "ace_list")) ||
+ !PyList_Check(obj))
+ return False;
+
+ acl->num_aces = PyList_Size(obj);
+
+ acl->ace = talloc(mem_ctx, acl->num_aces * sizeof(SEC_ACE));
+ acl->size = SEC_ACL_HEADER_SIZE;
+
+ for (i = 0; i < acl->num_aces; i++) {
+ PyObject *py_ace = PyList_GetItem(obj, i);
+
+ if (!py_to_ACE(&acl->ace[i], py_ace))
+ return False;
+
+ acl->size += acl->ace[i].size;
+ }
+
+ return True;
+}
+
+BOOL py_from_SECDESC(PyObject **dict, SEC_DESC *sd)
+{
+ PyObject *obj;
+
+ *dict = PyDict_New();
+
+ obj = PyInt_FromLong(sd->revision);
+ PyDict_SetItemString(*dict, "revision", obj);
+ Py_DECREF(obj);
+
+ if (py_from_SID(&obj, sd->owner_sid)) {
+ PyDict_SetItemString(*dict, "owner_sid", obj);
+ Py_DECREF(obj);
+ }
+
+ if (py_from_SID(&obj, sd->grp_sid)) {
+ PyDict_SetItemString(*dict, "group_sid", obj);
+ Py_DECREF(obj);
+ }
+
+ if (py_from_ACL(&obj, sd->dacl)) {
+ PyDict_SetItemString(*dict, "dacl", obj);
+ Py_DECREF(obj);
+ }
+
+ if (py_from_ACL(&obj, sd->sacl)) {
+ PyDict_SetItemString(*dict, "sacl", obj);
+ Py_DECREF(obj);
+ }
+
+ return True;
+}
+
+BOOL py_to_SECDESC(SEC_DESC **sd, PyObject *dict, TALLOC_CTX *mem_ctx)
+{
+ PyObject *obj;
+ uint16 revision;
+ DOM_SID owner_sid, group_sid;
+ SEC_ACL sacl, dacl;
+ BOOL got_dacl = False, got_sacl = False;
+ BOOL got_owner_sid = False, got_group_sid = False;
+
+ ZERO_STRUCT(dacl); ZERO_STRUCT(sacl);
+ ZERO_STRUCT(owner_sid); ZERO_STRUCT(group_sid);
+
+ if (!(obj = PyDict_GetItemString(dict, "revision")))
+ return False;
+
+ revision = PyInt_AsLong(obj);
+
+ if ((obj = PyDict_GetItemString(dict, "owner_sid"))) {
+
+ if (obj != Py_None) {
+
+ if (!py_to_SID(&owner_sid, obj))
+ return False;
+
+ got_owner_sid = True;
+ }
+ }
+
+ if ((obj = PyDict_GetItemString(dict, "group_sid"))) {
+
+ if (obj != Py_None) {
+
+ if (!py_to_SID(&group_sid, obj))
+ return False;
+
+ got_group_sid = True;
+ }
+ }
+
+ if ((obj = PyDict_GetItemString(dict, "dacl"))) {
+
+ if (obj != Py_None) {
+
+ if (!py_to_ACL(&dacl, obj, mem_ctx))
+ return False;
+
+ got_dacl = True;
+ }
+ }
+
+ if ((obj = PyDict_GetItemString(dict, "sacl"))) {
+
+ if (obj != Py_None) {
+
+ if (!py_to_ACL(&sacl, obj, mem_ctx))
+ return False;
+
+ got_sacl = True;
+ }
+ }
+
+#if 0 /* For new secdesc code */
+ *sd = make_sec_desc(mem_ctx, revision,
+ got_owner_sid ? &owner_sid : NULL,
+ got_group_sid ? &group_sid : NULL,
+ got_sacl ? &sacl : NULL,
+ got_dacl ? &dacl : NULL);
+#else
+ {
+ size_t sd_size;
+
+ *sd = make_sec_desc(mem_ctx, revision, SEC_DESC_SELF_RELATIVE,
+ got_owner_sid ? &owner_sid : NULL,
+ got_group_sid ? &group_sid : NULL,
+ got_sacl ? &sacl : NULL,
+ got_dacl ? &dacl : NULL, &sd_size);
+ }
+#endif
+
+ return True;
+}
diff --git a/source/python/py_samr.c b/source/python/py_samr.c
new file mode 100644
index 00000000000..57acd74bedb
--- /dev/null
+++ b/source/python/py_samr.c
@@ -0,0 +1,676 @@
+/*
+ Python wrappers for DCERPC/SMB client routines.
+
+ Copyright (C) Tim Potter, 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 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 "python/py_samr.h"
+
+/*
+ * Exceptions raised by this module
+ */
+
+PyObject *samr_error; /* This indicates a non-RPC related error
+ such as name lookup failure */
+
+PyObject *samr_ntstatus; /* This exception is raised when a RPC call
+ returns a status code other than
+ NT_STATUS_OK */
+
+/* SAMR group handle object */
+
+static void py_samr_group_hnd_dealloc(PyObject* self)
+{
+ PyObject_Del(self);
+}
+
+static PyMethodDef samr_group_methods[] = {
+ { NULL }
+};
+
+static PyObject *py_samr_group_hnd_getattr(PyObject *self, char *attrname)
+{
+ return Py_FindMethod(samr_group_methods, self, attrname);
+}
+
+PyTypeObject samr_group_hnd_type = {
+ PyObject_HEAD_INIT(NULL)
+ 0,
+ "SAMR Group Handle",
+ sizeof(samr_group_hnd_object),
+ 0,
+ py_samr_group_hnd_dealloc, /*tp_dealloc*/
+ 0, /*tp_print*/
+ py_samr_group_hnd_getattr, /*tp_getattr*/
+ 0, /*tp_setattr*/
+ 0, /*tp_compare*/
+ 0, /*tp_repr*/
+ 0, /*tp_as_number*/
+ 0, /*tp_as_sequence*/
+ 0, /*tp_as_mapping*/
+ 0, /*tp_hash */
+};
+
+PyObject *new_samr_group_hnd_object(struct cli_state *cli, TALLOC_CTX *mem_ctx,
+ POLICY_HND *pol)
+{
+ samr_group_hnd_object *o;
+
+ o = PyObject_New(samr_group_hnd_object, &samr_group_hnd_type);
+
+ o->cli = cli;
+ o->mem_ctx = mem_ctx;
+ memcpy(&o->group_pol, pol, sizeof(POLICY_HND));
+
+ return (PyObject*)o;
+}
+
+/* Alias handle object */
+
+static void py_samr_alias_hnd_dealloc(PyObject* self)
+{
+ PyObject_Del(self);
+}
+
+static PyMethodDef samr_alias_methods[] = {
+ { NULL }
+};
+
+static PyObject *py_samr_alias_hnd_getattr(PyObject *self, char *attrname)
+{
+ return Py_FindMethod(samr_alias_methods, self, attrname);
+}
+
+PyTypeObject samr_alias_hnd_type = {
+ PyObject_HEAD_INIT(NULL)
+ 0,
+ "SAMR Alias Handle",
+ sizeof(samr_alias_hnd_object),
+ 0,
+ py_samr_alias_hnd_dealloc, /*tp_dealloc*/
+ 0, /*tp_print*/
+ py_samr_alias_hnd_getattr, /*tp_getattr*/
+ 0, /*tp_setattr*/
+ 0, /*tp_compare*/
+ 0, /*tp_repr*/
+ 0, /*tp_as_number*/
+ 0, /*tp_as_sequence*/
+ 0, /*tp_as_mapping*/
+ 0, /*tp_hash */
+};
+
+PyObject *new_samr_alias_hnd_object(struct cli_state *cli, TALLOC_CTX *mem_ctx,
+ POLICY_HND *pol)
+{
+ samr_alias_hnd_object *o;
+
+ o = PyObject_New(samr_alias_hnd_object, &samr_alias_hnd_type);
+
+ o->cli = cli;
+ o->mem_ctx = mem_ctx;
+ memcpy(&o->alias_pol, pol, sizeof(POLICY_HND));
+
+ return (PyObject*)o;
+}
+
+/* SAMR user handle object */
+
+static void py_samr_user_hnd_dealloc(PyObject* self)
+{
+ PyObject_Del(self);
+}
+
+static PyObject *samr_set_user_info2(PyObject *self, PyObject *args,
+ PyObject *kw)
+{
+ samr_user_hnd_object *user_hnd = (samr_user_hnd_object *)self;
+ static char *kwlist[] = { "dict", NULL };
+ PyObject *info, *result = NULL;
+ SAM_USERINFO_CTR ctr;
+ TALLOC_CTX *mem_ctx;
+ uchar sess_key[16];
+ NTSTATUS ntstatus;
+ int level;
+ union {
+ SAM_USER_INFO_10 id10;
+ SAM_USER_INFO_21 id21;
+ } pinfo;
+
+ if (!PyArg_ParseTupleAndKeywords(
+ args, kw, "O!", kwlist, &PyDict_Type, &info))
+ return NULL;
+
+ if (!get_level_value(info, &level)) {
+ PyErr_SetString(samr_error, "invalid info level");
+ return NULL;
+ }
+
+ ZERO_STRUCT(ctr);
+
+ ctr.switch_value = level;
+
+ switch(level) {
+ case 0x10:
+ ctr.info.id10 = &pinfo.id10;
+
+ if (!py_to_SAM_USER_INFO_10(ctr.info.id10, info)) {
+ PyErr_SetString(
+ samr_error, "error converting user info");
+ goto done;
+ }
+
+ break;
+ case 21:
+ ctr.info.id21 = &pinfo.id21;
+
+ if (!py_to_SAM_USER_INFO_21(ctr.info.id21, info)) {
+ PyErr_SetString(
+ samr_error, "error converting user info");
+ goto done;
+ }
+
+ break;
+ default:
+ PyErr_SetString(samr_error, "unsupported info level");
+ goto done;
+ }
+
+ /* Call RPC function */
+
+ if (!(mem_ctx = talloc_init("samr_set_user_info2"))) {
+ PyErr_SetString(
+ samr_error, "unable to init talloc context\n");
+ goto done;
+ }
+
+ ntstatus = cli_samr_set_userinfo2(
+ user_hnd->cli, mem_ctx, &user_hnd->user_pol, level,
+ sess_key, &ctr);
+
+ talloc_destroy(mem_ctx);
+
+ if (!NT_STATUS_IS_OK(ntstatus)) {
+ PyErr_SetObject(samr_ntstatus, py_ntstatus_tuple(ntstatus));
+ goto done;
+ }
+
+ Py_INCREF(Py_None);
+ result = Py_None;
+
+done:
+ return result;
+}
+
+static PyObject *samr_delete_dom_user(PyObject *self, PyObject *args,
+ PyObject *kw)
+{
+ samr_user_hnd_object *user_hnd = (samr_user_hnd_object *)self;
+ static char *kwlist[] = { NULL };
+ NTSTATUS ntstatus;
+ TALLOC_CTX *mem_ctx;
+ PyObject *result = NULL;
+
+ if (!PyArg_ParseTupleAndKeywords(
+ args, kw, "", kwlist))
+ return NULL;
+
+ if (!(mem_ctx = talloc_init("samr_delete_dom_user"))) {
+ PyErr_SetString(samr_error, "unable to init talloc context");
+ return NULL;
+ }
+
+ ntstatus = cli_samr_delete_dom_user(
+ user_hnd->cli, mem_ctx, &user_hnd->user_pol);
+
+ if (!NT_STATUS_IS_OK(ntstatus)) {
+ PyErr_SetObject(samr_ntstatus, py_ntstatus_tuple(ntstatus));
+ goto done;
+ }
+
+ Py_INCREF(Py_None);
+ result = Py_None;
+
+done:
+ talloc_destroy(mem_ctx);
+
+ return result;
+}
+
+static PyMethodDef samr_user_methods[] = {
+ { "delete_domain_user", (PyCFunction)samr_delete_dom_user,
+ METH_VARARGS | METH_KEYWORDS,
+ "Delete domain user." },
+ { "set_user_info2", (PyCFunction)samr_set_user_info2,
+ METH_VARARGS | METH_KEYWORDS,
+ "Set user info 2" },
+ { NULL }
+};
+
+static PyObject *py_samr_user_hnd_getattr(PyObject *self, char *attrname)
+{
+ return Py_FindMethod(samr_user_methods, self, attrname);
+}
+
+PyTypeObject samr_user_hnd_type = {
+ PyObject_HEAD_INIT(NULL)
+ 0,
+ "SAMR User Handle",
+ sizeof(samr_user_hnd_object),
+ 0,
+ py_samr_user_hnd_dealloc, /*tp_dealloc*/
+ 0, /*tp_print*/
+ py_samr_user_hnd_getattr, /*tp_getattr*/
+ 0, /*tp_setattr*/
+ 0, /*tp_compare*/
+ 0, /*tp_repr*/
+ 0, /*tp_as_number*/
+ 0, /*tp_as_sequence*/
+ 0, /*tp_as_mapping*/
+ 0, /*tp_hash */
+};
+
+PyObject *new_samr_user_hnd_object(struct cli_state *cli, TALLOC_CTX *mem_ctx,
+ POLICY_HND *pol)
+{
+ samr_user_hnd_object *o;
+
+ o = PyObject_New(samr_user_hnd_object, &samr_user_hnd_type);
+
+ o->cli = cli;
+ o->mem_ctx = mem_ctx;
+ memcpy(&o->user_pol, pol, sizeof(POLICY_HND));
+
+ return (PyObject*)o;
+}
+
+/* SAMR connect handle object */
+
+static void py_samr_connect_hnd_dealloc(PyObject* self)
+{
+ PyObject_Del(self);
+}
+
+PyObject *new_samr_domain_hnd_object(struct cli_state *cli, TALLOC_CTX *mem_ctx,
+ POLICY_HND *pol)
+{
+ samr_domain_hnd_object *o;
+
+ o = PyObject_New(samr_domain_hnd_object, &samr_domain_hnd_type);
+
+ o->cli = cli;
+ o->mem_ctx = mem_ctx;
+ memcpy(&o->domain_pol, pol, sizeof(POLICY_HND));
+
+ return (PyObject*)o;
+}
+
+static PyObject *samr_open_domain(PyObject *self, PyObject *args, PyObject *kw)
+{
+ samr_connect_hnd_object *connect_hnd = (samr_connect_hnd_object *)self;
+ static char *kwlist[] = { "sid", "access", NULL };
+ uint32 desired_access = MAXIMUM_ALLOWED_ACCESS;
+ char *sid_str;
+ DOM_SID sid;
+ TALLOC_CTX *mem_ctx = NULL;
+ POLICY_HND domain_pol;
+ NTSTATUS ntstatus;
+ PyObject *result = NULL;
+
+ if (!PyArg_ParseTupleAndKeywords(
+ args, kw, "s|i", kwlist, &sid_str, &desired_access))
+ return NULL;
+
+ if (!string_to_sid(&sid, sid_str)) {
+ PyErr_SetString(PyExc_TypeError, "string is not a sid");
+ return NULL;
+ }
+
+ if (!(mem_ctx = talloc_init("samr_open_domain"))) {
+ PyErr_SetString(samr_error, "unable to init talloc context");
+ return NULL;
+ }
+
+ ntstatus = cli_samr_open_domain(
+ connect_hnd->cli, mem_ctx, &connect_hnd->connect_pol,
+ desired_access, &sid, &domain_pol);
+
+ if (!NT_STATUS_IS_OK(ntstatus)) {
+ PyErr_SetObject(samr_ntstatus, py_ntstatus_tuple(ntstatus));
+ goto done;
+ }
+
+ result = new_samr_domain_hnd_object(
+ connect_hnd->cli, mem_ctx, &domain_pol);
+
+done:
+ if (!result) {
+ if (mem_ctx)
+ talloc_destroy(mem_ctx);
+ }
+
+ return result;
+}
+
+static PyMethodDef samr_connect_methods[] = {
+ { "open_domain", (PyCFunction)samr_open_domain,
+ METH_VARARGS | METH_KEYWORDS,
+ "Open a handle on a domain" },
+
+ { NULL }
+};
+
+static PyObject *py_samr_connect_hnd_getattr(PyObject *self, char *attrname)
+{
+ return Py_FindMethod(samr_connect_methods, self, attrname);
+}
+
+PyTypeObject samr_connect_hnd_type = {
+ PyObject_HEAD_INIT(NULL)
+ 0,
+ "SAMR Connect Handle",
+ sizeof(samr_connect_hnd_object),
+ 0,
+ py_samr_connect_hnd_dealloc, /*tp_dealloc*/
+ 0, /*tp_print*/
+ py_samr_connect_hnd_getattr, /*tp_getattr*/
+ 0, /*tp_setattr*/
+ 0, /*tp_compare*/
+ 0, /*tp_repr*/
+ 0, /*tp_as_number*/
+ 0, /*tp_as_sequence*/
+ 0, /*tp_as_mapping*/
+ 0, /*tp_hash */
+};
+
+PyObject *new_samr_connect_hnd_object(struct cli_state *cli, TALLOC_CTX *mem_ctx,
+ POLICY_HND *pol)
+{
+ samr_connect_hnd_object *o;
+
+ o = PyObject_New(samr_connect_hnd_object, &samr_connect_hnd_type);
+
+ o->cli = cli;
+ o->mem_ctx = mem_ctx;
+ memcpy(&o->connect_pol, pol, sizeof(POLICY_HND));
+
+ return (PyObject*)o;
+}
+
+/* SAMR domain handle object */
+
+static void py_samr_domain_hnd_dealloc(PyObject* self)
+{
+ PyObject_Del(self);
+}
+
+static PyObject *samr_enum_dom_groups(PyObject *self, PyObject *args,
+ PyObject *kw)
+{
+ samr_domain_hnd_object *domain_hnd = (samr_domain_hnd_object *)self;
+ static char *kwlist[] = { NULL };
+ TALLOC_CTX *mem_ctx;
+/* uint32 desired_access = MAXIMUM_ALLOWED_ACCESS; */
+ uint32 start_idx, size, num_dom_groups;
+ struct acct_info *dom_groups;
+ NTSTATUS result;
+ PyObject *py_result = NULL;
+
+ if (!PyArg_ParseTupleAndKeywords(args, kw, "", kwlist))
+ return NULL;
+
+ if (!(mem_ctx = talloc_init("samr_enum_dom_groups"))) {
+ PyErr_SetString(samr_error, "unable to init talloc context");
+ return NULL;
+ }
+
+ start_idx = 0;
+ size = 0xffff;
+
+ do {
+ result = cli_samr_enum_dom_groups(
+ domain_hnd->cli, mem_ctx, &domain_hnd->domain_pol,
+ &start_idx, size, &dom_groups, &num_dom_groups);
+
+ if (NT_STATUS_IS_OK(result) ||
+ NT_STATUS_V(result) == NT_STATUS_V(STATUS_MORE_ENTRIES)) {
+ py_from_acct_info(&py_result, dom_groups,
+ num_dom_groups);
+ }
+
+ } while (NT_STATUS_V(result) == NT_STATUS_V(STATUS_MORE_ENTRIES));
+
+ return py_result;
+}
+
+static PyObject *samr_create_dom_user(PyObject *self, PyObject *args,
+ PyObject *kw)
+{
+ samr_domain_hnd_object *domain_hnd = (samr_domain_hnd_object *)self;
+ static char *kwlist[] = { "account_name", "acb_info", NULL };
+ char *account_name;
+ NTSTATUS ntstatus;
+ uint32 unknown = 0xe005000b; /* Access mask? */
+ uint32 user_rid;
+ PyObject *result = NULL;
+ TALLOC_CTX *mem_ctx;
+ uint16 acb_info = ACB_NORMAL;
+ POLICY_HND user_pol;
+
+ if (!PyArg_ParseTupleAndKeywords(
+ args, kw, "s|i", kwlist, &account_name, &acb_info))
+ return NULL;
+
+ if (!(mem_ctx = talloc_init("samr_create_dom_user"))) {
+ PyErr_SetString(samr_error, "unable to init talloc context");
+ return NULL;
+ }
+
+ ntstatus = cli_samr_create_dom_user(
+ domain_hnd->cli, mem_ctx, &domain_hnd->domain_pol,
+ account_name, acb_info, unknown, &user_pol, &user_rid);
+
+ if (!NT_STATUS_IS_OK(ntstatus)) {
+ PyErr_SetObject(samr_ntstatus, py_ntstatus_tuple(ntstatus));
+ talloc_destroy(mem_ctx);
+ goto done;
+ }
+
+ result = new_samr_user_hnd_object(
+ domain_hnd->cli, mem_ctx, &user_pol);
+
+done:
+
+ return result;
+}
+
+static PyMethodDef samr_domain_methods[] = {
+ { "enum_domain_groups", (PyCFunction)samr_enum_dom_groups,
+ METH_VARARGS | METH_KEYWORDS, "Enumerate domain groups" },
+ { "create_domain_user", (PyCFunction)samr_create_dom_user,
+ METH_VARARGS | METH_KEYWORDS, "Create domain user" },
+ { NULL }
+};
+
+static PyObject *py_samr_domain_hnd_getattr(PyObject *self, char *attrname)
+{
+ return Py_FindMethod(samr_domain_methods, self, attrname);
+}
+
+PyTypeObject samr_domain_hnd_type = {
+ PyObject_HEAD_INIT(NULL)
+ 0,
+ "SAMR Domain Handle",
+ sizeof(samr_domain_hnd_object),
+ 0,
+ py_samr_domain_hnd_dealloc, /*tp_dealloc*/
+ 0, /*tp_print*/
+ py_samr_domain_hnd_getattr, /*tp_getattr*/
+ 0, /*tp_setattr*/
+ 0, /*tp_compare*/
+ 0, /*tp_repr*/
+ 0, /*tp_as_number*/
+ 0, /*tp_as_sequence*/
+ 0, /*tp_as_mapping*/
+ 0, /*tp_hash */
+};
+
+static PyObject *samr_connect(PyObject *self, PyObject *args, PyObject *kw)
+{
+ static char *kwlist[] = { "server", "creds", "access", NULL };
+ uint32 desired_access = MAXIMUM_ALLOWED_ACCESS;
+ char *server, *errstr;
+ struct cli_state *cli = NULL;
+ POLICY_HND hnd;
+ TALLOC_CTX *mem_ctx = NULL;
+ PyObject *result = NULL, *creds = NULL;
+ NTSTATUS ntstatus;
+
+ if (!PyArg_ParseTupleAndKeywords(
+ args, kw, "s|Oi", kwlist, &server, &creds,
+ &desired_access))
+ return NULL;
+
+ if (server[0] != '\\' || server[1] != '\\') {
+ PyErr_SetString(PyExc_ValueError, "UNC name required");
+ return NULL;
+ }
+
+ server += 2;
+
+ if (creds && creds != Py_None && !PyDict_Check(creds)) {
+ PyErr_SetString(PyExc_TypeError,
+ "credentials must be dictionary or None");
+ return NULL;
+ }
+
+ if (!(cli = open_pipe_creds(server, creds, PI_SAMR, &errstr))) {
+ PyErr_SetString(samr_error, errstr);
+ free(errstr);
+ return NULL;
+ }
+
+ if (!(mem_ctx = talloc_init("samr_connect"))) {
+ PyErr_SetString(samr_ntstatus,
+ "unable to init talloc context\n");
+ goto done;
+ }
+
+ ntstatus = cli_samr_connect(cli, mem_ctx, desired_access, &hnd);
+
+ if (!NT_STATUS_IS_OK(ntstatus)) {
+ cli_shutdown(cli);
+ PyErr_SetObject(samr_ntstatus, py_ntstatus_tuple(ntstatus));
+ goto done;
+ }
+
+ result = new_samr_connect_hnd_object(cli, mem_ctx, &hnd);
+
+done:
+ if (!result) {
+ if (cli)
+ cli_shutdown(cli);
+
+ if (mem_ctx)
+ talloc_destroy(mem_ctx);
+ }
+
+ return result;
+}
+
+/*
+ * Module initialisation
+ */
+
+static PyMethodDef samr_methods[] = {
+
+ /* Open/close samr connect handles */
+
+ { "connect", (PyCFunction)samr_connect,
+ METH_VARARGS | METH_KEYWORDS,
+ "Open a connect handle" },
+
+ { NULL }
+};
+
+static struct const_vals {
+ char *name;
+ uint32 value;
+} module_const_vals[] = {
+
+ /* Account control bits */
+
+ { "ACB_DISABLED", 0x0001 },
+ { "ACB_HOMDIRREQ", 0x0002 },
+ { "ACB_PWNOTREQ", 0x0004 },
+ { "ACB_TEMPDUP", 0x0008 },
+ { "ACB_NORMAL", 0x0010 },
+ { "ACB_MNS", 0x0020 },
+ { "ACB_DOMTRUST", 0x0040 },
+ { "ACB_WSTRUST", 0x0080 },
+ { "ACB_SVRTRUST", 0x0100 },
+ { "ACB_PWNOEXP", 0x0200 },
+ { "ACB_AUTOLOCK", 0x0400 },
+
+ { NULL }
+};
+
+static void const_init(PyObject *dict)
+{
+ struct const_vals *tmp;
+ PyObject *obj;
+
+ for (tmp = module_const_vals; tmp->name; tmp++) {
+ obj = PyInt_FromLong(tmp->value);
+ PyDict_SetItemString(dict, tmp->name, obj);
+ Py_DECREF(obj);
+ }
+}
+
+void initsamr(void)
+{
+ PyObject *module, *dict;
+
+ /* Initialise module */
+
+ module = Py_InitModule("samr", samr_methods);
+ dict = PyModule_GetDict(module);
+
+ samr_error = PyErr_NewException("samr.error", NULL, NULL);
+ PyDict_SetItemString(dict, "error", samr_error);
+
+ samr_ntstatus = PyErr_NewException("samr.ntstatus", NULL, NULL);
+ PyDict_SetItemString(dict, "ntstatus", samr_ntstatus);
+
+ /* Initialise policy handle object */
+
+ samr_connect_hnd_type.ob_type = &PyType_Type;
+ samr_domain_hnd_type.ob_type = &PyType_Type;
+ samr_user_hnd_type.ob_type = &PyType_Type;
+ samr_group_hnd_type.ob_type = &PyType_Type;
+ samr_alias_hnd_type.ob_type = &PyType_Type;
+
+ /* Initialise constants */
+
+ const_init(dict);
+
+ /* Do samba initialisation */
+
+ py_samba_init();
+
+ setup_logging("samr", True);
+ DEBUGLEVEL = 10;
+}
diff --git a/source/python/py_samr.h b/source/python/py_samr.h
new file mode 100644
index 00000000000..4d2b0675b47
--- /dev/null
+++ b/source/python/py_samr.h
@@ -0,0 +1,86 @@
+/*
+ Python wrappers for DCERPC/SMB client routines.
+
+ Copyright (C) Tim Potter, 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 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 _PY_SAMR_H
+#define _PY_SAMR_H
+
+#include "python/py_common.h"
+
+/* SAMR connect policy handle object */
+
+typedef struct {
+ PyObject_HEAD
+ struct cli_state *cli;
+ TALLOC_CTX *mem_ctx;
+ POLICY_HND connect_pol;
+} samr_connect_hnd_object;
+
+/* SAMR domain policy handle object */
+
+typedef struct {
+ PyObject_HEAD
+ struct cli_state *cli;
+ TALLOC_CTX *mem_ctx;
+ POLICY_HND domain_pol;
+} samr_domain_hnd_object;
+
+/* SAMR user policy handle object */
+
+typedef struct {
+ PyObject_HEAD
+ struct cli_state *cli;
+ TALLOC_CTX *mem_ctx;
+ POLICY_HND user_pol;
+} samr_user_hnd_object;
+
+/* SAMR group policy handle object */
+
+typedef struct {
+ PyObject_HEAD
+ struct cli_state *cli;
+ TALLOC_CTX *mem_ctx;
+ POLICY_HND group_pol;
+} samr_group_hnd_object;
+
+/* SAMR alias policy handle object */
+
+typedef struct {
+ PyObject_HEAD
+ struct cli_state *cli;
+ TALLOC_CTX *mem_ctx;
+ POLICY_HND alias_pol;
+} samr_alias_hnd_object;
+
+extern PyTypeObject samr_connect_hnd_type, samr_domain_hnd_type,
+ samr_user_hnd_type, samr_group_hnd_type, samr_alias_hnd_type;
+
+/* Exceptions raised by this module */
+
+extern PyObject *samr_error;
+
+/* The following definitions are from py_samr_conv.c */
+
+BOOL py_from_acct_info(PyObject **array, struct acct_info *info, int num_accts);
+BOOL py_from_SAM_USER_INFO_10(PyObject **dict, SAM_USER_INFO_10 *info);
+BOOL py_to_SAM_USER_INFO_10(SAM_USER_INFO_10 *info, PyObject *dict);
+BOOL py_from_SAM_USER_INFO_21(PyObject **dict, SAM_USER_INFO_21 *info);
+BOOL py_to_SAM_USER_INFO_21(SAM_USER_INFO_21 *info, PyObject *dict);
+
+#endif /* _PY_SAMR_H */
diff --git a/source/python/py_samr_conv.c b/source/python/py_samr_conv.c
new file mode 100644
index 00000000000..7523ee7dfcb
--- /dev/null
+++ b/source/python/py_samr_conv.c
@@ -0,0 +1,131 @@
+/*
+ Python wrappers for DCERPC/SMB client routines.
+
+ Copyright (C) Tim Potter, 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 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 "python/py_samr.h"
+#include "python/py_conv.h"
+
+/*
+ * Convert between SAM_USER_INFO_10 and Python
+ */
+
+struct pyconv py_SAM_USER_INFO_10[] = {
+ { "acb_info", PY_UINT32, offsetof(SAM_USER_INFO_10, acb_info) },
+ { NULL }
+};
+
+BOOL py_from_SAM_USER_INFO_10(PyObject **dict, SAM_USER_INFO_10 *info)
+{
+ *dict = from_struct(info, py_SAM_USER_INFO_10);
+ PyDict_SetItemString(*dict, "level", PyInt_FromLong(0x10));
+ return True;
+}
+
+BOOL py_to_SAM_USER_INFO_10(SAM_USER_INFO_10 *info, PyObject *dict)
+{
+ PyObject *obj, *dict_copy = PyDict_Copy(dict);
+ BOOL result = False;
+
+ if (!(obj = PyDict_GetItemString(dict_copy, "level")) ||
+ !PyInt_Check(obj))
+ goto done;
+
+ PyDict_DelItemString(dict_copy, "level");
+
+ if (!to_struct(info, dict_copy, py_SAM_USER_INFO_10))
+ goto done;
+
+ result = True;
+
+done:
+ Py_DECREF(dict_copy);
+ return result;
+}
+
+/*
+ * Convert between SAM_USER_INFO_21 and Python
+ */
+
+struct pyconv py_SAM_USER_INFO_21[] = {
+ { NULL }
+};
+
+BOOL py_from_SAM_USER_INFO_21(PyObject **dict, SAM_USER_INFO_21 *info)
+{
+ *dict = from_struct(info, py_SAM_USER_INFO_21);
+ PyDict_SetItemString(*dict, "level", PyInt_FromLong(21));
+ return True;
+}
+
+BOOL py_to_SAM_USER_INFO_21(SAM_USER_INFO_21 *info, PyObject *dict)
+{
+ PyObject *obj, *dict_copy = PyDict_Copy(dict);
+ BOOL result = False;
+
+ if (!(obj = PyDict_GetItemString(dict_copy, "level")) ||
+ !PyInt_Check(obj))
+ goto done;
+
+ PyDict_DelItemString(dict_copy, "level");
+
+ if (!to_struct(info, dict_copy, py_SAM_USER_INFO_21))
+ goto done;
+
+ result = True;
+
+done:
+ Py_DECREF(dict_copy);
+ return result;
+}
+
+/*
+ * Convert between acct_info and Python
+ */
+
+BOOL py_from_acct_info(PyObject **array, struct acct_info *info, int num_accts)
+{
+ int i;
+
+ *array = PyList_New(num_accts);
+
+ for (i = 0; i < num_accts; i++) {
+ PyObject *obj;
+
+ obj = PyDict_New();
+
+ PyDict_SetItemString(
+ obj, "name", PyString_FromString(info[i].acct_name));
+
+ PyDict_SetItemString(
+ obj, "description",
+ PyString_FromString(info[i].acct_desc));
+
+ PyDict_SetItemString(obj, "rid", PyInt_FromLong(info[i].rid));
+
+ PyList_SetItem(*array, i, obj);
+ }
+
+ return True;
+}
+
+BOOL py_to_acct_info(PRINTER_INFO_3 *info, PyObject *dict,
+ TALLOC_CTX *mem_ctx)
+{
+ return False;
+}
diff --git a/source/python/py_smb.c b/source/python/py_smb.c
new file mode 100644
index 00000000000..e5e65061965
--- /dev/null
+++ b/source/python/py_smb.c
@@ -0,0 +1,442 @@
+/*
+ Python wrappers for DCERPC/SMB client routines.
+
+ Copyright (C) Tim Potter, 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 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 "python/py_smb.h"
+
+/* Create a new cli_state python object */
+
+PyObject *new_cli_state_object(struct cli_state *cli)
+{
+ cli_state_object *o;
+
+ o = PyObject_New(cli_state_object, &cli_state_type);
+
+ o->cli = cli;
+
+ return (PyObject*)o;
+}
+
+static PyObject *py_smb_connect(PyObject *self, PyObject *args, PyObject *kw)
+{
+ static char *kwlist[] = { "server", NULL };
+ struct cli_state *cli;
+ char *server;
+ struct in_addr ip;
+
+ if (!PyArg_ParseTupleAndKeywords(args, kw, "s", kwlist, &server))
+ return NULL;
+
+ if (!(cli = cli_initialise(NULL)))
+ return NULL;
+
+ ZERO_STRUCT(ip);
+
+ if (!cli_connect(cli, server, &ip))
+ return NULL;
+
+ return new_cli_state_object(cli);
+}
+
+static PyObject *py_smb_session_request(PyObject *self, PyObject *args,
+ PyObject *kw)
+{
+ cli_state_object *cli = (cli_state_object *)self;
+ static char *kwlist[] = { "called", "calling", NULL };
+ char *calling_name = NULL, *called_name;
+ struct nmb_name calling, called;
+ BOOL result;
+
+ if (!PyArg_ParseTupleAndKeywords(args, kw, "s|s", kwlist, &called_name,
+ &calling_name))
+ return NULL;
+
+ if (!calling_name)
+ calling_name = global_myname();
+
+ make_nmb_name(&calling, calling_name, 0x00);
+ make_nmb_name(&called, called_name, 0x20);
+
+ result = cli_session_request(cli->cli, &calling, &called);
+
+ return Py_BuildValue("i", result);
+}
+
+static PyObject *py_smb_negprot(PyObject *self, PyObject *args, PyObject *kw)
+{
+ cli_state_object *cli = (cli_state_object *)self;
+ static char *kwlist[] = { NULL };
+ BOOL result;
+
+ if (!PyArg_ParseTupleAndKeywords(args, kw, "", kwlist))
+ return NULL;
+
+ result = cli_negprot(cli->cli);
+
+ return Py_BuildValue("i", result);
+}
+
+static PyObject *py_smb_session_setup(PyObject *self, PyObject *args,
+ PyObject *kw)
+{
+ cli_state_object *cli = (cli_state_object *)self;
+ static char *kwlist[] = { "creds", NULL };
+ PyObject *creds;
+ char *username, *domain, *password, *errstr;
+ BOOL result;
+
+ if (!PyArg_ParseTupleAndKeywords(args, kw, "|O", kwlist, &creds))
+ return NULL;
+
+ if (!py_parse_creds(creds, &username, &domain, &password, &errstr)) {
+ free(errstr);
+ return NULL;
+ }
+
+ result = cli_session_setup(
+ cli->cli, username, password, strlen(password) + 1,
+ password, strlen(password) + 1, domain);
+
+ if (cli_is_error(cli->cli)) {
+ PyErr_SetString(PyExc_RuntimeError, "session setup failed");
+ return NULL;
+ }
+
+ return Py_BuildValue("i", result);
+}
+
+static PyObject *py_smb_tconx(PyObject *self, PyObject *args, PyObject *kw)
+{
+ cli_state_object *cli = (cli_state_object *)self;
+ static char *kwlist[] = { "service", NULL };
+ char *service;
+ BOOL result;
+
+ if (!PyArg_ParseTupleAndKeywords(args, kw, "s", kwlist, &service))
+ return NULL;
+
+ result = cli_send_tconX(
+ cli->cli, service, strequal(service, "IPC$") ? "IPC" :
+ "?????", "", 1);
+
+ if (cli_is_error(cli->cli)) {
+ PyErr_SetString(PyExc_RuntimeError, "tconx failed");
+ return NULL;
+ }
+
+ return Py_BuildValue("i", result);
+}
+
+static PyObject *py_smb_nt_create_andx(PyObject *self, PyObject *args,
+ PyObject *kw)
+{
+ cli_state_object *cli = (cli_state_object *)self;
+ static char *kwlist[] = { "filename", "desired_access",
+ "file_attributes", "share_access",
+ "create_disposition", NULL };
+ char *filename;
+ uint32 desired_access, file_attributes = 0,
+ share_access = FILE_SHARE_READ | FILE_SHARE_WRITE,
+ create_disposition = FILE_EXISTS_OPEN, create_options = 0;
+ int result;
+
+ /* Parse parameters */
+
+ if (!PyArg_ParseTupleAndKeywords(
+ args, kw, "si|iii", kwlist, &filename, &desired_access,
+ &file_attributes, &share_access, &create_disposition,
+ &create_options))
+ return NULL;
+
+ result = cli_nt_create_full(
+ cli->cli, filename, 0, desired_access, file_attributes,
+ share_access, create_disposition, create_options, 0);
+
+ if (cli_is_error(cli->cli)) {
+ PyErr_SetString(PyExc_RuntimeError, "nt_create_andx failed");
+ return NULL;
+ }
+
+ /* Return FID */
+
+ return PyInt_FromLong(result);
+}
+
+static PyObject *py_smb_close(PyObject *self, PyObject *args,
+ PyObject *kw)
+{
+ cli_state_object *cli = (cli_state_object *)self;
+ static char *kwlist[] = { "fnum", NULL };
+ BOOL result;
+ int fnum;
+
+ /* Parse parameters */
+
+ if (!PyArg_ParseTupleAndKeywords(
+ args, kw, "i", kwlist, &fnum))
+ return NULL;
+
+ result = cli_close(cli->cli, fnum);
+
+ return PyInt_FromLong(result);
+}
+
+static PyObject *py_smb_unlink(PyObject *self, PyObject *args,
+ PyObject *kw)
+{
+ cli_state_object *cli = (cli_state_object *)self;
+ static char *kwlist[] = { "filename", NULL };
+ char *filename;
+ BOOL result;
+
+ /* Parse parameters */
+
+ if (!PyArg_ParseTupleAndKeywords(
+ args, kw, "s", kwlist, &filename))
+ return NULL;
+
+ result = cli_unlink(cli->cli, filename);
+
+ return PyInt_FromLong(result);
+}
+
+static PyObject *py_smb_query_secdesc(PyObject *self, PyObject *args,
+ PyObject *kw)
+{
+ cli_state_object *cli = (cli_state_object *)self;
+ static char *kwlist[] = { "fnum", NULL };
+ PyObject *result = NULL;
+ SEC_DESC *secdesc = NULL;
+ int fnum;
+ TALLOC_CTX *mem_ctx = NULL;
+
+ /* Parse parameters */
+
+ if (!PyArg_ParseTupleAndKeywords(
+ args, kw, "i", kwlist, &fnum))
+ return NULL;
+
+ mem_ctx = talloc_init("py_smb_query_secdesc");
+
+ secdesc = cli_query_secdesc(cli->cli, fnum, mem_ctx);
+
+ if (cli_is_error(cli->cli)) {
+ PyErr_SetString(PyExc_RuntimeError, "query_secdesc failed");
+ goto done;
+ }
+
+ if (!secdesc) {
+ Py_INCREF(Py_None);
+ result = Py_None;
+ goto done;
+ }
+
+ if (!py_from_SECDESC(&result, secdesc)) {
+ PyErr_SetString(
+ PyExc_TypeError,
+ "Invalid security descriptor returned");
+ goto done;
+ }
+
+ done:
+ talloc_destroy(mem_ctx);
+
+ return result;
+
+}
+
+static PyObject *py_smb_set_secdesc(PyObject *self, PyObject *args,
+ PyObject *kw)
+{
+ cli_state_object *cli = (cli_state_object *)self;
+ static char *kwlist[] = { "fnum", "security_descriptor", NULL };
+ PyObject *result = NULL;
+ PyObject *py_secdesc;
+ SEC_DESC *secdesc;
+ TALLOC_CTX *mem_ctx = NULL;
+ int fnum;
+ BOOL err;
+
+ /* Parse parameters */
+
+ if (!PyArg_ParseTupleAndKeywords(
+ args, kw, "iO", kwlist, &fnum, &py_secdesc))
+ return NULL;
+
+ mem_ctx = talloc_init("py_smb_set_secdesc");
+
+ if (!py_to_SECDESC(&secdesc, py_secdesc, mem_ctx)) {
+ PyErr_SetString(PyExc_TypeError,
+ "Invalid security descriptor");
+ goto done;
+ }
+
+ err = cli_set_secdesc(cli->cli, fnum, secdesc);
+
+ if (cli_is_error(cli->cli)) {
+ PyErr_SetString(PyExc_RuntimeError, "set_secdesc failed");
+ goto done;
+ }
+
+ result = PyInt_FromLong(err);
+ done:
+ talloc_destroy(mem_ctx);
+
+ return result;
+}
+
+static PyMethodDef smb_hnd_methods[] = {
+
+ /* Session and connection handling */
+
+ { "session_request", (PyCFunction)py_smb_session_request,
+ METH_VARARGS | METH_KEYWORDS, "Request a session" },
+
+ { "negprot", (PyCFunction)py_smb_negprot,
+ METH_VARARGS | METH_KEYWORDS, "Protocol negotiation" },
+
+ { "session_setup", (PyCFunction)py_smb_session_setup,
+ METH_VARARGS | METH_KEYWORDS, "Session setup" },
+
+ { "tconx", (PyCFunction)py_smb_tconx,
+ METH_VARARGS | METH_KEYWORDS, "Tree connect" },
+
+ /* File operations */
+
+ { "nt_create_andx", (PyCFunction)py_smb_nt_create_andx,
+ METH_VARARGS | METH_KEYWORDS, "NT Create&X" },
+
+ { "close", (PyCFunction)py_smb_close,
+ METH_VARARGS | METH_KEYWORDS, "Close" },
+
+ { "unlink", (PyCFunction)py_smb_unlink,
+ METH_VARARGS | METH_KEYWORDS, "Unlink" },
+
+ /* Security descriptors */
+
+ { "query_secdesc", (PyCFunction)py_smb_query_secdesc,
+ METH_VARARGS | METH_KEYWORDS, "Query security descriptor" },
+
+ { "set_secdesc", (PyCFunction)py_smb_set_secdesc,
+ METH_VARARGS | METH_KEYWORDS, "Set security descriptor" },
+
+ { NULL }
+};
+
+/*
+ * Method dispatch tables
+ */
+
+static PyMethodDef smb_methods[] = {
+
+ { "connect", (PyCFunction)py_smb_connect, METH_VARARGS | METH_KEYWORDS,
+ "Connect to a host" },
+
+ /* Other stuff - this should really go into a samba config module
+ but for the moment let's leave it here. */
+
+ { "setup_logging", (PyCFunction)py_setup_logging,
+ METH_VARARGS | METH_KEYWORDS,
+ "Set up debug logging.\n"
+"\n"
+"Initialises Samba's debug logging system. One argument is expected which\n"
+"is a boolean specifying whether debugging is interactive and sent to stdout\n"
+"or logged to a file.\n"
+"\n"
+"Example:\n"
+"\n"
+">>> smb.setup_logging(interactive = 1)" },
+
+ { "get_debuglevel", (PyCFunction)get_debuglevel,
+ METH_VARARGS,
+ "Set the current debug level.\n"
+"\n"
+"Example:\n"
+"\n"
+">>> smb.get_debuglevel()\n"
+"0" },
+
+ { "set_debuglevel", (PyCFunction)set_debuglevel,
+ METH_VARARGS,
+ "Get the current debug level.\n"
+"\n"
+"Example:\n"
+"\n"
+">>> smb.set_debuglevel(10)" },
+
+ { NULL }
+};
+
+static void py_cli_state_dealloc(PyObject* self)
+{
+ cli_state_object *cli = (cli_state_object *)self;
+
+ if (cli->cli)
+ cli_shutdown(cli->cli);
+
+ PyObject_Del(self);
+}
+
+static PyObject *py_cli_state_getattr(PyObject *self, char *attrname)
+{
+ return Py_FindMethod(smb_hnd_methods, self, attrname);
+}
+
+PyTypeObject cli_state_type = {
+ PyObject_HEAD_INIT(NULL)
+ 0,
+ "SMB client connection",
+ sizeof(cli_state_object),
+ 0,
+ py_cli_state_dealloc, /*tp_dealloc*/
+ 0, /*tp_print*/
+ py_cli_state_getattr, /*tp_getattr*/
+ 0, /*tp_setattr*/
+ 0, /*tp_compare*/
+ 0, /*tp_repr*/
+ 0, /*tp_as_number*/
+ 0, /*tp_as_sequence*/
+ 0, /*tp_as_mapping*/
+ 0, /*tp_hash */
+};
+
+/*
+ * Module initialisation
+ */
+
+void initsmb(void)
+{
+ PyObject *module, *dict;
+
+ /* Initialise module */
+
+ module = Py_InitModule("smb", smb_methods);
+ dict = PyModule_GetDict(module);
+
+ /* Initialise policy handle object */
+
+ cli_state_type.ob_type = &PyType_Type;
+
+ /* Do samba initialisation */
+
+ py_samba_init();
+
+ setup_logging("smb", True);
+ DEBUGLEVEL = 3;
+}
diff --git a/source/python/py_smb.h b/source/python/py_smb.h
new file mode 100644
index 00000000000..31bcf4aab2e
--- /dev/null
+++ b/source/python/py_smb.h
@@ -0,0 +1,39 @@
+/*
+ Python wrappers for DCERPC/SMB client routines.
+
+ Copyright (C) Tim Potter, 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 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 _PY_SMB_H
+#define _PY_SMB_H
+
+#include "python/py_common.h"
+
+/* cli_state handle object */
+
+typedef struct {
+ PyObject_HEAD
+ struct cli_state *cli;
+} cli_state_object;
+
+/* Exceptions raised by this module */
+
+extern PyTypeObject cli_state_type;
+
+extern PyObject *smb_ntstatus;
+
+#endif /* _PY_SMB_H */
diff --git a/source/python/py_spoolss.c b/source/python/py_spoolss.c
new file mode 100644
index 00000000000..b8df5cbf113
--- /dev/null
+++ b/source/python/py_spoolss.c
@@ -0,0 +1,485 @@
+/*
+ Python wrappers for DCERPC/SMB client routines.
+
+ Copyright (C) Tim Potter, 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 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 "python/py_spoolss.h"
+
+/* Exceptions this module can raise */
+
+PyObject *spoolss_error, *spoolss_werror;
+
+/*
+ * Method dispatch table
+ */
+
+static PyMethodDef spoolss_methods[] = {
+
+ /* Open/close printer handles */
+
+ { "openprinter", (PyCFunction)spoolss_openprinter, METH_VARARGS | METH_KEYWORDS,
+ "Open a printer by name in UNC format.\n"
+"\n"
+"Optionally a dictionary of (domain, username, password) may be given in\n"
+"which case they are used when opening the RPC pipe. An access mask may\n"
+"also be given which defaults to MAXIMUM_ALLOWED_ACCESS.\n"
+"\n"
+"Example:\n"
+"\n"
+">>> hnd = spoolss.openprinter(\"\\\\\\\\NPSD-PDC2\\\\meanie\")"},
+
+ { "closeprinter", spoolss_closeprinter, METH_VARARGS,
+ "Close a printer handle opened with openprinter or addprinter.\n"
+"\n"
+"Example:\n"
+"\n"
+">>> spoolss.closeprinter(hnd)"},
+
+ { "addprinterex", (PyCFunction)spoolss_addprinterex, METH_VARARGS,
+ "addprinterex()"},
+
+ /* Server enumeratation functions */
+
+ { "enumprinters", (PyCFunction)spoolss_enumprinters,
+ METH_VARARGS | METH_KEYWORDS,
+ "Enumerate printers on a print server.\n"
+"\n"
+"Return a list of printers on a print server. The credentials, info level\n"
+"and flags may be specified as keyword arguments.\n"
+"\n"
+"Example:\n"
+"\n"
+">>> print spoolss.enumprinters(\"\\\\\\\\npsd-pdc2\")\n"
+"[{'comment': 'i am a comment', 'printer_name': 'meanie', 'flags': 8388608, \n"
+" 'description': 'meanie,Generic / Text Only,i am a location'}, \n"
+" {'comment': '', 'printer_name': 'fileprint', 'flags': 8388608, \n"
+" 'description': 'fileprint,Generic / Text Only,'}]"},
+
+ { "enumports", (PyCFunction)spoolss_enumports,
+ METH_VARARGS | METH_KEYWORDS,
+ "Enumerate ports on a print server.\n"
+"\n"
+"Return a list of ports on a print server.\n"
+"\n"
+"Example:\n"
+"\n"
+">>> print spoolss.enumports(\"\\\\\\\\npsd-pdc2\")\n"
+"[{'name': 'LPT1:'}, {'name': 'LPT2:'}, {'name': 'COM1:'}, \n"
+"{'name': 'COM2:'}, {'name': 'FILE:'}, {'name': '\\\\nautilus1\\zpekt3r'}]"},
+
+ { "enumprinterdrivers", (PyCFunction)spoolss_enumprinterdrivers,
+ METH_VARARGS | METH_KEYWORDS,
+ "Enumerate printer drivers on a print server.\n"
+"\n"
+"Return a list of printer drivers."},
+
+ /* Miscellaneous other commands */
+
+ { "getprinterdriverdir", (PyCFunction)spoolss_getprinterdriverdir,
+ METH_VARARGS | METH_KEYWORDS,
+ "Return printer driver directory.\n"
+"\n"
+"Return the printer driver directory for a given architecture. The\n"
+"architecture defaults to \"Windows NT x86\"."},
+
+ /* Other stuff - this should really go into a samba config module
+ but for the moment let's leave it here. */
+
+ { "setup_logging", (PyCFunction)py_setup_logging,
+ METH_VARARGS | METH_KEYWORDS,
+ "Set up debug logging.\n"
+"\n"
+"Initialises Samba's debug logging system. One argument is expected which\n"
+"is a boolean specifying whether debugging is interactive and sent to stdout\n"
+"or logged to a file.\n"
+"\n"
+"Example:\n"
+"\n"
+">>> spoolss.setup_logging(interactive = 1)" },
+
+ { "get_debuglevel", (PyCFunction)get_debuglevel,
+ METH_VARARGS,
+ "Set the current debug level.\n"
+"\n"
+"Example:\n"
+"\n"
+">>> spoolss.get_debuglevel()\n"
+"0" },
+
+ { "set_debuglevel", (PyCFunction)set_debuglevel,
+ METH_VARARGS,
+ "Get the current debug level.\n"
+"\n"
+"Example:\n"
+"\n"
+">>> spoolss.set_debuglevel(10)" },
+
+ /* Printer driver routines */
+
+ { "addprinterdriver", (PyCFunction)spoolss_addprinterdriver,
+ METH_VARARGS | METH_KEYWORDS,
+ "Add a printer driver." },
+
+ { "addprinterdriverex", (PyCFunction)spoolss_addprinterdriverex,
+ METH_VARARGS | METH_KEYWORDS,
+ "Add a printer driver." },
+
+ { "deleteprinterdriver", (PyCFunction)spoolss_deleteprinterdriver,
+ METH_VARARGS | METH_KEYWORDS,
+ "Delete a printer driver." },
+
+ { "deleteprinterdriverex", (PyCFunction)spoolss_deleteprinterdriverex,
+ METH_VARARGS | METH_KEYWORDS,
+ "Delete a printer driver." },
+
+ { NULL }
+};
+
+/* Methods attached to a spoolss handle object */
+
+static PyMethodDef spoolss_hnd_methods[] = {
+
+ /* Printer info */
+
+ { "getprinter", (PyCFunction)spoolss_hnd_getprinter,
+ METH_VARARGS | METH_KEYWORDS,
+ "Get printer information.\n"
+"\n"
+"Return a dictionary of print information. The info level defaults to 1.\n"
+"\n"
+"Example:\n"
+"\n"
+">>> hnd.getprinter()\n"
+"{'comment': 'i am a comment', 'printer_name': '\\\\NPSD-PDC2\\meanie',\n"
+" 'description': '\\\\NPSD-PDC2\\meanie,Generic / Text Only,i am a location',\n"
+" 'flags': 8388608}"},
+
+ { "setprinter", (PyCFunction)spoolss_hnd_setprinter,
+ METH_VARARGS | METH_KEYWORDS,
+ "Set printer information."},
+
+ /* Printer drivers */
+
+ { "getprinterdriver", (PyCFunction)spoolss_hnd_getprinterdriver,
+ METH_VARARGS | METH_KEYWORDS,
+ "Return printer driver information.\n"
+"\n"
+"Return a dictionary of printer driver information for the printer driver\n"
+"bound to this printer."},
+
+ /* Forms */
+
+ { "enumforms", (PyCFunction)spoolss_hnd_enumforms,
+ METH_VARARGS | METH_KEYWORDS,
+ "Enumerate supported forms.\n"
+"\n"
+"Return a list of forms supported by this printer or print server."},
+
+ { "setform", (PyCFunction)spoolss_hnd_setform,
+ METH_VARARGS | METH_KEYWORDS,
+ "Set form data.\n"
+"\n"
+"Set the form given by the dictionary argument."},
+
+ { "addform", (PyCFunction)spoolss_hnd_addform,
+ METH_VARARGS | METH_KEYWORDS,
+ "Add a new form." },
+
+ { "getform", (PyCFunction)spoolss_hnd_getform,
+ METH_VARARGS | METH_KEYWORDS,
+ "Get form properties." },
+
+ { "deleteform", (PyCFunction)spoolss_hnd_deleteform,
+ METH_VARARGS | METH_KEYWORDS,
+ "Delete a form." },
+
+ /* Job related methods */
+
+ { "enumjobs", (PyCFunction)spoolss_hnd_enumjobs,
+ METH_VARARGS | METH_KEYWORDS,
+ "Enumerate jobs." },
+
+ { "setjob", (PyCFunction)spoolss_hnd_setjob,
+ METH_VARARGS | METH_KEYWORDS,
+ "Set job information." },
+
+ { "getjob", (PyCFunction)spoolss_hnd_getjob,
+ METH_VARARGS | METH_KEYWORDS,
+ "Get job information." },
+
+ { "startpageprinter", (PyCFunction)spoolss_hnd_startpageprinter,
+ METH_VARARGS | METH_KEYWORDS,
+ "Notify spooler that a page is about to be printed." },
+
+ { "endpageprinter", (PyCFunction)spoolss_hnd_endpageprinter,
+ METH_VARARGS | METH_KEYWORDS,
+ "Notify spooler that a page is about to be printed." },
+
+ { "startdocprinter", (PyCFunction)spoolss_hnd_startdocprinter,
+ METH_VARARGS | METH_KEYWORDS,
+ "Notify spooler that a document is about to be printed." },
+
+ { "enddocprinter", (PyCFunction)spoolss_hnd_enddocprinter,
+ METH_VARARGS | METH_KEYWORDS,
+ "Notify spooler that a document is about to be printed." },
+
+ { "writeprinter", (PyCFunction)spoolss_hnd_writeprinter,
+ METH_VARARGS | METH_KEYWORDS,
+ "Write job data to a printer." },
+
+ { "addjob", (PyCFunction)spoolss_hnd_addjob,
+ METH_VARARGS | METH_KEYWORDS,
+ "Add a job to the list of print jobs." },
+
+ /* Printer data */
+
+ { "getprinterdata", (PyCFunction)spoolss_hnd_getprinterdata,
+ METH_VARARGS | METH_KEYWORDS,
+ "Get printer data." },
+
+ { "setprinterdata", (PyCFunction)spoolss_hnd_setprinterdata,
+ METH_VARARGS | METH_KEYWORDS,
+ "Set printer data." },
+
+ { "enumprinterdata", (PyCFunction)spoolss_hnd_enumprinterdata,
+ METH_VARARGS | METH_KEYWORDS,
+ "Enumerate printer data." },
+
+ { "deleteprinterdata", (PyCFunction)spoolss_hnd_deleteprinterdata,
+ METH_VARARGS | METH_KEYWORDS,
+ "Delete printer data." },
+
+ { "getprinterdataex", (PyCFunction)spoolss_hnd_getprinterdataex,
+ METH_VARARGS | METH_KEYWORDS,
+ "Get printer data." },
+
+ { "setprinterdataex", (PyCFunction)spoolss_hnd_setprinterdataex,
+ METH_VARARGS | METH_KEYWORDS,
+ "Set printer data." },
+
+ { "enumprinterdataex", (PyCFunction)spoolss_hnd_enumprinterdataex,
+ METH_VARARGS | METH_KEYWORDS,
+ "Enumerate printer data." },
+
+ { "deleteprinterdataex", (PyCFunction)spoolss_hnd_deleteprinterdataex,
+ METH_VARARGS | METH_KEYWORDS,
+ "Delete printer data." },
+
+ { "enumprinterkey", (PyCFunction)spoolss_hnd_enumprinterkey,
+ METH_VARARGS | METH_KEYWORDS,
+ "Enumerate printer key." },
+
+#if 0
+ /* Not implemented */
+
+ { "deleteprinterkey", (PyCFunction)spoolss_hnd_deleteprinterkey,
+ METH_VARARGS | METH_KEYWORDS,
+ "Delete printer key." },
+#endif
+
+ { NULL }
+
+};
+
+static void py_policy_hnd_dealloc(PyObject* self)
+{
+ spoolss_policy_hnd_object *hnd;
+
+ /* Close down policy handle and free talloc context */
+
+ hnd = (spoolss_policy_hnd_object*)self;
+
+ cli_shutdown(hnd->cli);
+ talloc_destroy(hnd->mem_ctx);
+
+ PyObject_Del(self);
+}
+
+static PyObject *py_policy_hnd_getattr(PyObject *self, char *attrname)
+{
+ return Py_FindMethod(spoolss_hnd_methods, self, attrname);
+}
+
+static char spoolss_type_doc[] =
+"Python wrapper for Windows NT SPOOLSS rpc pipe.";
+
+PyTypeObject spoolss_policy_hnd_type = {
+ PyObject_HEAD_INIT(NULL)
+ 0,
+ "spoolss.hnd",
+ sizeof(spoolss_policy_hnd_object),
+ 0,
+ py_policy_hnd_dealloc, /* tp_dealloc*/
+ 0, /* tp_print*/
+ py_policy_hnd_getattr, /* tp_getattr*/
+ 0, /* tp_setattr*/
+ 0, /* tp_compare*/
+ 0, /* tp_repr*/
+ 0, /* tp_as_number*/
+ 0, /* tp_as_sequence*/
+ 0, /* tp_as_mapping*/
+ 0, /* tp_hash */
+ 0, /* tp_call */
+ 0, /* tp_str */
+ 0, /* tp_getattro */
+ 0, /* tp_setattro */
+ 0, /* tp_as_buffer*/
+ Py_TPFLAGS_DEFAULT, /* tp_flags */
+ spoolss_type_doc, /* tp_doc */
+};
+
+/* Initialise constants */
+
+static struct const_vals {
+ char *name;
+ uint32 value;
+} module_const_vals[] = {
+
+ /* Access permissions */
+
+ { "MAXIMUM_ALLOWED_ACCESS", MAXIMUM_ALLOWED_ACCESS },
+ { "SERVER_ALL_ACCESS", SERVER_ALL_ACCESS },
+ { "SERVER_READ", SERVER_READ },
+ { "SERVER_WRITE", SERVER_WRITE },
+ { "SERVER_EXECUTE", SERVER_EXECUTE },
+ { "SERVER_ACCESS_ADMINISTER", SERVER_ACCESS_ADMINISTER },
+ { "SERVER_ACCESS_ENUMERATE", SERVER_ACCESS_ENUMERATE },
+ { "PRINTER_ALL_ACCESS", PRINTER_ALL_ACCESS },
+ { "PRINTER_READ", PRINTER_READ },
+ { "PRINTER_WRITE", PRINTER_WRITE },
+ { "PRINTER_EXECUTE", PRINTER_EXECUTE },
+ { "PRINTER_ACCESS_ADMINISTER", PRINTER_ACCESS_ADMINISTER },
+ { "PRINTER_ACCESS_USE", PRINTER_ACCESS_USE },
+ { "JOB_ACCESS_ADMINISTER", JOB_ACCESS_ADMINISTER },
+ { "JOB_ALL_ACCESS", JOB_ALL_ACCESS },
+ { "JOB_READ", JOB_READ },
+ { "JOB_WRITE", JOB_WRITE },
+ { "JOB_EXECUTE", JOB_EXECUTE },
+ { "STANDARD_RIGHTS_ALL_ACCESS", STANDARD_RIGHTS_ALL_ACCESS },
+ { "STANDARD_RIGHTS_EXECUTE_ACCESS", STANDARD_RIGHTS_EXECUTE_ACCESS },
+ { "STANDARD_RIGHTS_READ_ACCESS", STANDARD_RIGHTS_READ_ACCESS },
+ { "STANDARD_RIGHTS_REQUIRED_ACCESS", STANDARD_RIGHTS_REQUIRED_ACCESS },
+ { "STANDARD_RIGHTS_WRITE_ACCESS", STANDARD_RIGHTS_WRITE_ACCESS },
+
+ /* Printer enumeration flags */
+
+ { "PRINTER_ENUM_DEFAULT", PRINTER_ENUM_DEFAULT },
+ { "PRINTER_ENUM_LOCAL", PRINTER_ENUM_LOCAL },
+ { "PRINTER_ENUM_CONNECTIONS", PRINTER_ENUM_CONNECTIONS },
+ { "PRINTER_ENUM_FAVORITE", PRINTER_ENUM_FAVORITE },
+ { "PRINTER_ENUM_NAME", PRINTER_ENUM_NAME },
+ { "PRINTER_ENUM_REMOTE", PRINTER_ENUM_REMOTE },
+ { "PRINTER_ENUM_SHARED", PRINTER_ENUM_SHARED },
+ { "PRINTER_ENUM_NETWORK", PRINTER_ENUM_NETWORK },
+
+ /* Form types */
+
+ { "FORM_USER", FORM_USER },
+ { "FORM_BUILTIN", FORM_BUILTIN },
+ { "FORM_PRINTER", FORM_PRINTER },
+
+ /* WERRORs */
+
+ { "WERR_OK", 0 },
+ { "WERR_BADFILE", 2 },
+ { "WERR_ACCESS_DENIED", 5 },
+ { "WERR_BADFID", 6 },
+ { "WERR_BADFUNC", 1 },
+ { "WERR_INSUFFICIENT_BUFFER", 122 },
+ { "WERR_NO_SUCH_SHARE", 67 },
+ { "WERR_ALREADY_EXISTS", 80 },
+ { "WERR_INVALID_PARAM", 87 },
+ { "WERR_NOT_SUPPORTED", 50 },
+ { "WERR_BAD_PASSWORD", 86 },
+ { "WERR_NOMEM", 8 },
+ { "WERR_INVALID_NAME", 123 },
+ { "WERR_UNKNOWN_LEVEL", 124 },
+ { "WERR_OBJECT_PATH_INVALID", 161 },
+ { "WERR_NO_MORE_ITEMS", 259 },
+ { "WERR_MORE_DATA", 234 },
+ { "WERR_UNKNOWN_PRINTER_DRIVER", 1797 },
+ { "WERR_INVALID_PRINTER_NAME", 1801 },
+ { "WERR_PRINTER_ALREADY_EXISTS", 1802 },
+ { "WERR_INVALID_DATATYPE", 1804 },
+ { "WERR_INVALID_ENVIRONMENT", 1805 },
+ { "WERR_INVALID_FORM_NAME", 1902 },
+ { "WERR_INVALID_FORM_SIZE", 1903 },
+ { "WERR_BUF_TOO_SMALL", 2123 },
+ { "WERR_JOB_NOT_FOUND", 2151 },
+ { "WERR_DEST_NOT_FOUND", 2152 },
+ { "WERR_NOT_LOCAL_DOMAIN", 2320 },
+ { "WERR_PRINTER_DRIVER_IN_USE", 3001 },
+ { "WERR_STATUS_MORE_ENTRIES ", 0x0105 },
+
+ /* Job control constants */
+
+ { "JOB_CONTROL_PAUSE", JOB_CONTROL_PAUSE },
+ { "JOB_CONTROL_RESUME", JOB_CONTROL_RESUME },
+ { "JOB_CONTROL_CANCEL", JOB_CONTROL_CANCEL },
+ { "JOB_CONTROL_RESTART", JOB_CONTROL_RESTART },
+ { "JOB_CONTROL_DELETE", JOB_CONTROL_DELETE },
+
+ { NULL },
+};
+
+static void const_init(PyObject *dict)
+{
+ struct const_vals *tmp;
+ PyObject *obj;
+
+ for (tmp = module_const_vals; tmp->name; tmp++) {
+ obj = PyInt_FromLong(tmp->value);
+ PyDict_SetItemString(dict, tmp->name, obj);
+ Py_DECREF(obj);
+ }
+}
+
+/* Module initialisation */
+
+void initspoolss(void)
+{
+ PyObject *module, *dict;
+
+ /* Initialise module */
+
+ module = Py_InitModule("spoolss", spoolss_methods);
+ dict = PyModule_GetDict(module);
+
+ /* Exceptions we can raise */
+
+ spoolss_error = PyErr_NewException("spoolss.error", NULL, NULL);
+ PyDict_SetItemString(dict, "error", spoolss_error);
+
+ spoolss_werror = PyErr_NewException("spoolss.werror", NULL, NULL);
+ PyDict_SetItemString(dict, "werror", spoolss_werror);
+
+ /* Initialise policy handle object */
+
+ spoolss_policy_hnd_type.ob_type = &PyType_Type;
+
+ PyDict_SetItemString(dict, "spoolss.hnd",
+ (PyObject *)&spoolss_policy_hnd_type);
+
+ /* Initialise constants */
+
+ const_init(dict);
+
+ /* Do samba initialisation */
+
+ py_samba_init();
+}
diff --git a/source/python/py_spoolss.h b/source/python/py_spoolss.h
new file mode 100644
index 00000000000..34b48190cd1
--- /dev/null
+++ b/source/python/py_spoolss.h
@@ -0,0 +1,160 @@
+/*
+ Python wrappers for DCERPC/SMB client routines.
+
+ Copyright (C) Tim Potter, 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 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 _PY_SPOOLSS_H
+#define _PY_SPOOLSS_H
+
+#include "python/py_common.h"
+
+/* Spoolss policy handle object */
+
+typedef struct {
+ PyObject_HEAD
+ struct cli_state *cli;
+ TALLOC_CTX *mem_ctx;
+ POLICY_HND pol;
+} spoolss_policy_hnd_object;
+
+/* Exceptions raised by this module */
+
+extern PyTypeObject spoolss_policy_hnd_type;
+
+extern PyObject *spoolss_error, *spoolss_werror;
+
+/* The following definitions come from python/py_spoolss_common.c */
+
+PyObject *new_spoolss_policy_hnd_object(struct cli_state *cli,
+ TALLOC_CTX *mem_ctx, POLICY_HND *pol);
+
+/* The following definitions come from python/py_spoolss_drivers.c */
+
+PyObject *spoolss_enumprinterdrivers(PyObject *self, PyObject *args,
+ PyObject *kw);
+PyObject *spoolss_hnd_getprinterdriver(PyObject *self, PyObject *args,
+ PyObject *kw);
+PyObject *spoolss_getprinterdriverdir(PyObject *self, PyObject *args,
+ PyObject *kw);
+PyObject *spoolss_addprinterdriver(PyObject *self, PyObject *args,
+ PyObject *kw);
+PyObject *spoolss_addprinterdriverex(PyObject *self, PyObject *args,
+ PyObject *kw);
+PyObject *spoolss_deleteprinterdriver(PyObject *self, PyObject *args,
+ PyObject *kw);
+PyObject *spoolss_deleteprinterdriverex(PyObject *self, PyObject *args,
+ PyObject *kw);
+
+/* The following definitions come from python/py_spoolss_drivers_conv.c */
+
+BOOL py_from_DRIVER_INFO_1(PyObject **dict, DRIVER_INFO_1 *info);
+BOOL py_to_DRIVER_INFO_1(DRIVER_INFO_1 *info, PyObject *dict);
+BOOL py_from_DRIVER_INFO_2(PyObject **dict, DRIVER_INFO_2 *info);
+BOOL py_to_DRIVER_INFO_2(DRIVER_INFO_2 *info, PyObject *dict);
+BOOL py_from_DRIVER_INFO_3(PyObject **dict, DRIVER_INFO_3 *info);
+BOOL py_to_DRIVER_INFO_3(DRIVER_INFO_3 *info, PyObject *dict);
+BOOL py_from_DRIVER_INFO_6(PyObject **dict, DRIVER_INFO_6 *info);
+BOOL py_to_DRIVER_INFO_6(DRIVER_INFO_6 *info, PyObject *dict);
+BOOL py_from_DRIVER_DIRECTORY_1(PyObject **dict, DRIVER_DIRECTORY_1 *info);
+BOOL py_to_DRIVER_DIRECTORY_1(DRIVER_DIRECTORY_1 *info, PyObject *dict);
+
+/* The following definitions come from python/py_spoolss_forms.c */
+
+PyObject *spoolss_hnd_addform(PyObject *self, PyObject *args, PyObject *kw);
+PyObject *spoolss_hnd_getform(PyObject *self, PyObject *args, PyObject *kw);
+PyObject *spoolss_hnd_setform(PyObject *self, PyObject *args, PyObject *kw);
+PyObject *spoolss_hnd_deleteform(PyObject *self, PyObject *args, PyObject *kw);
+PyObject *spoolss_hnd_enumforms(PyObject *self, PyObject *args, PyObject *kw);
+
+/* The following definitions come from python/py_spoolss_forms_conv.c */
+
+BOOL py_from_FORM_1(PyObject **dict, FORM_1 *form);
+BOOL py_to_FORM(FORM *form, PyObject *dict);
+
+/* The following definitions come from python/py_spoolss_jobs.c */
+
+PyObject *spoolss_hnd_enumjobs(PyObject *self, PyObject *args, PyObject *kw);
+PyObject *spoolss_hnd_setjob(PyObject *self, PyObject *args, PyObject *kw);
+PyObject *spoolss_hnd_getjob(PyObject *self, PyObject *args, PyObject *kw);
+PyObject *spoolss_hnd_startpageprinter(PyObject *self, PyObject *args, PyObject *kw);
+PyObject *spoolss_hnd_endpageprinter(PyObject *self, PyObject *args, PyObject *kw);
+PyObject *spoolss_hnd_startdocprinter(PyObject *self, PyObject *args, PyObject *kw);
+PyObject *spoolss_hnd_enddocprinter(PyObject *self, PyObject *args, PyObject *kw);
+PyObject *spoolss_hnd_writeprinter(PyObject *self, PyObject *args, PyObject *kw);
+PyObject *spoolss_hnd_addjob(PyObject *self, PyObject *args, PyObject *kw);
+
+/* The following definitions come from python/py_spoolss_jobs_conv.c */
+
+BOOL py_from_JOB_INFO_1(PyObject **dict, JOB_INFO_1 *info);
+BOOL py_to_JOB_INFO_1(JOB_INFO_1 *info, PyObject *dict);
+BOOL py_from_JOB_INFO_2(PyObject **dict, JOB_INFO_2 *info);
+BOOL py_to_JOB_INFO_2(JOB_INFO_2 *info, PyObject *dict);
+BOOL py_from_DOC_INFO_1(PyObject **dict, DOC_INFO_1 *info);
+BOOL py_to_DOC_INFO_1(DOC_INFO_1 *info, PyObject *dict);
+
+/* The following definitions come from python/py_spoolss_ports.c */
+
+PyObject *spoolss_enumports(PyObject *self, PyObject *args, PyObject *kw);
+
+/* The following definitions come from python/py_spoolss_ports_conv.c */
+
+BOOL py_from_PORT_INFO_1(PyObject **dict, PORT_INFO_1 *info);
+BOOL py_to_PORT_INFO_1(PORT_INFO_1 *info, PyObject *dict);
+BOOL py_from_PORT_INFO_2(PyObject **dict, PORT_INFO_2 *info);
+BOOL py_to_PORT_INFO_2(PORT_INFO_2 *info, PyObject *dict);
+
+/* The following definitions come from python/py_spoolss_printerdata.c */
+
+PyObject *spoolss_hnd_getprinterdata(PyObject *self, PyObject *args, PyObject *kw);
+PyObject *spoolss_hnd_setprinterdata(PyObject *self, PyObject *args, PyObject *kw);
+PyObject *spoolss_hnd_enumprinterdata(PyObject *self, PyObject *args, PyObject *kw);
+PyObject *spoolss_hnd_deleteprinterdata(PyObject *self, PyObject *args, PyObject *kw);
+PyObject *spoolss_hnd_getprinterdataex(PyObject *self, PyObject *args, PyObject *kw);
+PyObject *spoolss_hnd_setprinterdataex(PyObject *self, PyObject *args, PyObject *kw);
+PyObject *spoolss_hnd_enumprinterdataex(PyObject *self, PyObject *args, PyObject *kw);
+PyObject *spoolss_hnd_deleteprinterdataex(PyObject *self, PyObject *args, PyObject *kw);
+PyObject *spoolss_hnd_enumprinterkey(PyObject *self, PyObject *args,
+ PyObject *kw);
+PyObject *spoolss_hnd_deleteprinterkey(PyObject *self, PyObject *args,
+ PyObject *kw);
+
+/* The following definitions come from python/py_spoolss_printers.c */
+
+PyObject *spoolss_openprinter(PyObject *self, PyObject *args, PyObject *kw);
+PyObject *spoolss_closeprinter(PyObject *self, PyObject *args);
+PyObject *spoolss_hnd_getprinter(PyObject *self, PyObject *args, PyObject *kw);
+PyObject *spoolss_hnd_setprinter(PyObject *self, PyObject *args, PyObject *kw);
+PyObject *spoolss_enumprinters(PyObject *self, PyObject *args, PyObject *kw);
+PyObject *spoolss_addprinterex(PyObject *self, PyObject *args, PyObject *kw);
+
+/* The following definitions come from python/py_spoolss_printers_conv.c */
+
+BOOL py_from_DEVICEMODE(PyObject **dict, DEVICEMODE *devmode);
+BOOL py_to_DEVICEMODE(DEVICEMODE *devmode, PyObject *dict);
+BOOL py_from_PRINTER_INFO_0(PyObject **dict, PRINTER_INFO_0 *info);
+BOOL py_to_PRINTER_INFO_0(PRINTER_INFO_0 *info, PyObject *dict);
+BOOL py_from_PRINTER_INFO_1(PyObject **dict, PRINTER_INFO_1 *info);
+BOOL py_to_PRINTER_INFO_1(PRINTER_INFO_1 *info, PyObject *dict);
+BOOL py_from_PRINTER_INFO_2(PyObject **dict, PRINTER_INFO_2 *info);
+BOOL py_to_PRINTER_INFO_2(PRINTER_INFO_2 *info, PyObject *dict,
+ TALLOC_CTX *mem_ctx);
+BOOL py_from_PRINTER_INFO_3(PyObject **dict, PRINTER_INFO_3 *info);
+BOOL py_to_PRINTER_INFO_3(PRINTER_INFO_3 *info, PyObject *dict,
+ TALLOC_CTX *mem_ctx);
+
+#endif /* _PY_SPOOLSS_H */
diff --git a/source/python/py_spoolss_common.c b/source/python/py_spoolss_common.c
new file mode 100644
index 00000000000..f34d2ac6c7e
--- /dev/null
+++ b/source/python/py_spoolss_common.c
@@ -0,0 +1,35 @@
+/*
+ Python wrappers for DCERPC/SMB client routines.
+
+ 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 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 "python/py_spoolss.h"
+
+PyObject *new_spoolss_policy_hnd_object(struct cli_state *cli,
+ TALLOC_CTX *mem_ctx, POLICY_HND *pol)
+{
+ spoolss_policy_hnd_object *o;
+
+ o = PyObject_New(spoolss_policy_hnd_object, &spoolss_policy_hnd_type);
+
+ o->cli = cli;
+ o->mem_ctx = mem_ctx;
+ memcpy(&o->pol, pol, sizeof(POLICY_HND));
+
+ return (PyObject*)o;
+}
diff --git a/source/python/py_spoolss_drivers.c b/source/python/py_spoolss_drivers.c
new file mode 100644
index 00000000000..12190519ecc
--- /dev/null
+++ b/source/python/py_spoolss_drivers.c
@@ -0,0 +1,422 @@
+/*
+ Python wrappers for DCERPC/SMB client routines.
+
+ Copyright (C) Tim Potter, 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 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 "python/py_spoolss.h"
+
+/* Enumerate printer drivers */
+
+PyObject *spoolss_enumprinterdrivers(PyObject *self, PyObject *args,
+ PyObject *kw)
+{
+ WERROR werror;
+ PyObject *result = NULL, *creds = NULL;
+ PRINTER_DRIVER_CTR ctr;
+ int level = 1, i;
+ uint32 needed, num_drivers;
+ char *arch = "Windows NT x86", *server, *errstr;
+ static char *kwlist[] = {"server", "level", "creds", "arch", NULL};
+ struct cli_state *cli = NULL;
+ TALLOC_CTX *mem_ctx = NULL;
+
+ /* Parse parameters */
+
+ if (!PyArg_ParseTupleAndKeywords(
+ args, kw, "s|iOs", kwlist, &server, &level, &creds,
+ &arch))
+ return NULL;
+
+ if (server[0] != '\\' || server[1] != '\\') {
+ PyErr_SetString(PyExc_ValueError, "UNC name required");
+ return NULL;
+ }
+
+ server += 2;
+
+ if (creds && creds != Py_None && !PyDict_Check(creds)) {
+ PyErr_SetString(PyExc_TypeError,
+ "credentials must be dictionary or None");
+ return NULL;
+ }
+
+ /* Call rpc function */
+
+ if (!(cli = open_pipe_creds(server, creds, PI_SPOOLSS, &errstr))) {
+ PyErr_SetString(spoolss_error, errstr);
+ free(errstr);
+ goto done;
+ }
+
+ if (!(mem_ctx = talloc_init("spoolss_enumprinterdrivers"))) {
+ PyErr_SetString(
+ spoolss_error, "unable to init talloc context\n");
+ goto done;
+ }
+
+ werror = cli_spoolss_enumprinterdrivers(
+ cli, mem_ctx, 0, &needed, level, arch,
+ &num_drivers, &ctr);
+
+ if (W_ERROR_V(werror) == ERRinsufficientbuffer)
+ werror = cli_spoolss_enumprinterdrivers(
+ cli, mem_ctx, needed, NULL, level, arch,
+ &num_drivers, &ctr);
+
+ if (!W_ERROR_IS_OK(werror)) {
+ PyErr_SetObject(spoolss_werror, py_werror_tuple(werror));
+ goto done;
+ }
+
+ /* Return value */
+
+ switch (level) {
+ case 1:
+ result = PyDict_New();
+
+ for (i = 0; i < num_drivers; i++) {
+ PyObject *value;
+ fstring name;
+
+ rpcstr_pull(name, ctr.info1[i].name.buffer,
+ sizeof(fstring), -1, STR_TERMINATE);
+
+ py_from_DRIVER_INFO_1(&value, &ctr.info1[i]);
+
+ PyDict_SetItemString(result, name, value);
+ }
+
+ break;
+ case 2:
+ result = PyDict_New();
+
+ for(i = 0; i < num_drivers; i++) {
+ PyObject *value;
+ fstring name;
+
+ rpcstr_pull(name, ctr.info2[i].name.buffer,
+ sizeof(fstring), -1, STR_TERMINATE);
+
+ py_from_DRIVER_INFO_2(&value, &ctr.info2[i]);
+
+ PyDict_SetItemString(result, name, value);
+ }
+
+ break;
+ case 3:
+ result = PyDict_New();
+
+ for(i = 0; i < num_drivers; i++) {
+ PyObject *value;
+ fstring name;
+
+ rpcstr_pull(name, ctr.info3[i].name.buffer,
+ sizeof(fstring), -1, STR_TERMINATE);
+
+ py_from_DRIVER_INFO_3(&value, &ctr.info3[i]);
+
+ PyDict_SetItemString(result, name, value);
+ }
+
+ break;
+ case 6:
+ result = PyDict_New();
+
+ for(i = 0; i < num_drivers; i++) {
+ PyObject *value;
+ fstring name;
+
+ rpcstr_pull(name, ctr.info6[i].name.buffer,
+ sizeof(fstring), -1, STR_TERMINATE);
+
+ py_from_DRIVER_INFO_6(&value, &ctr.info6[i]);
+
+ PyList_SetItem(result, i, value);
+ }
+
+ break;
+ default:
+ PyErr_SetString(spoolss_error, "unknown info level");
+ goto done;
+ }
+
+ done:
+ if (cli)
+ cli_shutdown(cli);
+
+ if (mem_ctx)
+ talloc_destroy(mem_ctx);
+
+ return result;
+}
+
+/* Fetch printer driver */
+
+PyObject *spoolss_hnd_getprinterdriver(PyObject *self, PyObject *args,
+ PyObject *kw)
+{
+ spoolss_policy_hnd_object *hnd = (spoolss_policy_hnd_object *)self;
+ WERROR werror;
+ PyObject *result = Py_None;
+ PRINTER_DRIVER_CTR ctr;
+ int level = 1;
+ uint32 needed;
+ char *arch = "Windows NT x86";
+ int version = 2;
+ static char *kwlist[] = {"level", "arch", NULL};
+
+ /* Parse parameters */
+
+ if (!PyArg_ParseTupleAndKeywords(
+ args, kw, "|is", kwlist, &level, &arch))
+ return NULL;
+
+ /* Call rpc function */
+
+ werror = cli_spoolss_getprinterdriver(
+ hnd->cli, hnd->mem_ctx, 0, &needed, &hnd->pol, level,
+ version, arch, &ctr);
+
+ if (W_ERROR_V(werror) == ERRinsufficientbuffer)
+ werror = cli_spoolss_getprinterdriver(
+ hnd->cli, hnd->mem_ctx, needed, NULL, &hnd->pol,
+ level, version, arch, &ctr);
+
+ if (!W_ERROR_IS_OK(werror)) {
+ PyErr_SetObject(spoolss_werror, py_werror_tuple(werror));
+ return NULL;
+ }
+
+ /* Return value */
+
+ switch (level) {
+ case 1:
+ py_from_DRIVER_INFO_1(&result, ctr.info1);
+ break;
+ case 2:
+ py_from_DRIVER_INFO_2(&result, ctr.info2);
+ break;
+ case 3:
+ py_from_DRIVER_INFO_3(&result, ctr.info3);
+ break;
+ case 6:
+ py_from_DRIVER_INFO_6(&result, ctr.info6);
+ break;
+ default:
+ PyErr_SetString(spoolss_error, "unsupported info level");
+ return NULL;
+ }
+
+ Py_INCREF(result);
+ return result;
+}
+
+/* Fetch printer driver directory */
+
+PyObject *spoolss_getprinterdriverdir(PyObject *self, PyObject *args,
+ PyObject *kw)
+{
+ WERROR werror;
+ PyObject *result = NULL, *creds = NULL;
+ DRIVER_DIRECTORY_CTR ctr;
+ uint32 needed, level = 1;
+ char *arch = "Windows NT x86", *server, *errstr;
+ static char *kwlist[] = {"server", "level", "arch", "creds", NULL};
+ struct cli_state *cli = NULL;
+ TALLOC_CTX *mem_ctx = NULL;
+
+ /* Parse parameters */
+
+ if (!PyArg_ParseTupleAndKeywords(
+ args, kw, "s|isO", kwlist, &server, &level,
+ &arch, &creds))
+ return NULL;
+
+ if (server[0] != '\\' || server[1] != '\\') {
+ PyErr_SetString(PyExc_ValueError, "UNC name required");
+ return NULL;
+ }
+
+ server += 2;
+
+ if (creds && creds != Py_None && !PyDict_Check(creds)) {
+ PyErr_SetString(PyExc_TypeError,
+ "credentials must be dictionary or None");
+ return NULL;
+ }
+
+ /* Call rpc function */
+
+ if (!(cli = open_pipe_creds(server, creds, PI_SPOOLSS, &errstr))) {
+ PyErr_SetString(spoolss_error, errstr);
+ free(errstr);
+ goto done;
+ }
+
+ if (!(mem_ctx = talloc_init("spoolss_getprinterdriverdir"))) {
+ PyErr_SetString(
+ spoolss_error, "unable to init talloc context\n");
+ goto done;
+ }
+
+ werror = cli_spoolss_getprinterdriverdir(
+ cli, mem_ctx, 0, &needed, level, arch, &ctr);
+
+ if (W_ERROR_V(werror) == ERRinsufficientbuffer)
+ werror = cli_spoolss_getprinterdriverdir(
+ cli, mem_ctx, needed, NULL, level, arch, &ctr);
+
+ if (!W_ERROR_IS_OK(werror)) {
+ PyErr_SetObject(spoolss_werror, py_werror_tuple(werror));
+ goto done;
+ }
+
+ /* Return value */
+
+ switch (level) {
+ case 1:
+ py_from_DRIVER_DIRECTORY_1(&result, ctr.info1);
+ break;
+ default:
+ PyErr_SetString(spoolss_error, "unknown info level");
+ goto done;
+ }
+
+ done:
+ if (cli)
+ cli_shutdown(cli);
+
+ if (mem_ctx)
+ talloc_destroy(mem_ctx);
+
+ return result;
+}
+
+PyObject *spoolss_addprinterdriver(PyObject *self, PyObject *args,
+ PyObject *kw)
+{
+ static char *kwlist[] = { "server", "info", "creds", NULL };
+ char *server, *errstr;
+ uint32 level;
+ PyObject *info, *result = NULL, *creds = NULL;
+ WERROR werror;
+ TALLOC_CTX *mem_ctx = NULL;
+ struct cli_state *cli = NULL;
+ PRINTER_DRIVER_CTR ctr;
+ union {
+ DRIVER_INFO_3 driver_3;
+ } dinfo;
+
+ if (!PyArg_ParseTupleAndKeywords(
+ args, kw, "sO!|O", kwlist, &server, &PyDict_Type,
+ &info, &creds))
+ return NULL;
+
+ if (server[0] == '\\' || server[1] == '\\')
+ server += 2;
+
+ if (creds && creds != Py_None && !PyDict_Check(creds)) {
+ PyErr_SetString(PyExc_TypeError,
+ "credentials must be dictionary or None");
+ return NULL;
+ }
+
+ if (!(mem_ctx = talloc_init("spoolss_addprinterdriver"))) {
+ PyErr_SetString(
+ spoolss_error, "unable to init talloc context\n");
+ return NULL;
+ }
+
+ if (!(cli = open_pipe_creds(server, creds, PI_SPOOLSS, &errstr))) {
+ PyErr_SetString(spoolss_error, errstr);
+ free(errstr);
+ goto done;
+ }
+
+ if (!get_level_value(info, &level)) {
+ PyErr_SetString(spoolss_error, "invalid info level");
+ goto done;
+ }
+
+ if (level != 3) {
+ PyErr_SetString(spoolss_error, "unsupported info level");
+ goto done;
+ }
+
+ ZERO_STRUCT(ctr);
+ ZERO_STRUCT(dinfo);
+
+ switch(level) {
+ case 3:
+ ctr.info3 = &dinfo.driver_3;
+
+ if (!py_to_DRIVER_INFO_3(&dinfo.driver_3, info)) {
+ PyErr_SetString(spoolss_error,
+ "error converting to driver info 3");
+ goto done;
+ }
+
+ break;
+ default:
+ PyErr_SetString(spoolss_error, "unsupported info level");
+ goto done;
+ }
+
+ werror = cli_spoolss_addprinterdriver(cli, mem_ctx, level, &ctr);
+
+ if (!W_ERROR_IS_OK(werror)) {
+ PyErr_SetObject(spoolss_werror, py_werror_tuple(werror));
+ goto done;
+ }
+
+ Py_INCREF(Py_None);
+ result = Py_None;
+
+done:
+ if (cli)
+ cli_shutdown(cli);
+
+ if (mem_ctx)
+ talloc_destroy(mem_ctx);
+
+ return result;
+
+}
+
+PyObject *spoolss_addprinterdriverex(PyObject *self, PyObject *args,
+ PyObject *kw)
+{
+ /* Not supported by Samba server */
+
+ PyErr_SetString(spoolss_error, "Not implemented");
+ return NULL;
+}
+
+PyObject *spoolss_deleteprinterdriver(PyObject *self, PyObject *args,
+ PyObject *kw)
+{
+ PyErr_SetString(spoolss_error, "Not implemented");
+ return NULL;
+}
+
+PyObject *spoolss_deleteprinterdriverex(PyObject *self, PyObject *args,
+ PyObject *kw)
+{
+ PyErr_SetString(spoolss_error, "Not implemented");
+ return NULL;
+}
diff --git a/source/python/py_spoolss_drivers_conv.c b/source/python/py_spoolss_drivers_conv.c
new file mode 100644
index 00000000000..9bc84080529
--- /dev/null
+++ b/source/python/py_spoolss_drivers_conv.c
@@ -0,0 +1,179 @@
+/*
+ Python wrappers for DCERPC/SMB client routines.
+
+ Copyright (C) Tim Potter, 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 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 "python/py_spoolss.h"
+#include "python/py_conv.h"
+
+/* Structure/hash conversions */
+
+struct pyconv py_DRIVER_INFO_1[] = {
+ { "name", PY_UNISTR, offsetof(DRIVER_INFO_1, name) },
+ { NULL }
+};
+
+struct pyconv py_DRIVER_INFO_2[] = {
+ { "version", PY_UINT32, offsetof(DRIVER_INFO_2, version) },
+ { "name", PY_UNISTR, offsetof(DRIVER_INFO_2, name) },
+ { "architecture", PY_UNISTR, offsetof(DRIVER_INFO_2, architecture) },
+ { "driver_path", PY_UNISTR, offsetof(DRIVER_INFO_2, driverpath) },
+ { "data_file", PY_UNISTR, offsetof(DRIVER_INFO_2, datafile) },
+ { "config_file", PY_UNISTR, offsetof(DRIVER_INFO_2, configfile) },
+ { NULL }
+};
+
+struct pyconv py_DRIVER_INFO_3[] = {
+ { "version", PY_UINT32, offsetof(DRIVER_INFO_3, version) },
+ { "name", PY_UNISTR, offsetof(DRIVER_INFO_3, name) },
+ { "architecture", PY_UNISTR, offsetof(DRIVER_INFO_3, architecture) },
+ { "driver_path", PY_UNISTR, offsetof(DRIVER_INFO_3, driverpath) },
+ { "data_file", PY_UNISTR, offsetof(DRIVER_INFO_3, datafile) },
+ { "config_file", PY_UNISTR, offsetof(DRIVER_INFO_3, configfile) },
+ { "help_file", PY_UNISTR, offsetof(DRIVER_INFO_3, helpfile) },
+ { "monitor_name", PY_UNISTR, offsetof(DRIVER_INFO_3, monitorname) },
+ { "default_datatype", PY_UNISTR, offsetof(DRIVER_INFO_3, defaultdatatype) },
+ { NULL }
+};
+
+struct pyconv py_DRIVER_INFO_6[] = {
+ { "version", PY_UINT32, offsetof(DRIVER_INFO_6, version) },
+ { "name", PY_UNISTR, offsetof(DRIVER_INFO_6, name) },
+ { "architecture", PY_UNISTR, offsetof(DRIVER_INFO_6, architecture) },
+ { "driver_path", PY_UNISTR, offsetof(DRIVER_INFO_6, driverpath) },
+ { "data_file", PY_UNISTR, offsetof(DRIVER_INFO_6, datafile) },
+ { "config_file", PY_UNISTR, offsetof(DRIVER_INFO_6, configfile) },
+ { "help_file", PY_UNISTR, offsetof(DRIVER_INFO_6, helpfile) },
+ { "monitor_name", PY_UNISTR, offsetof(DRIVER_INFO_6, monitorname) },
+ { "default_datatype", PY_UNISTR, offsetof(DRIVER_INFO_6, defaultdatatype) },
+ /* driver_date */
+ { "padding", PY_UINT32, offsetof(DRIVER_INFO_6, padding) },
+ { "driver_version_low", PY_UINT32, offsetof(DRIVER_INFO_6, driver_version_low) },
+ { "driver_version_high", PY_UINT32, offsetof(DRIVER_INFO_6, driver_version_high) },
+ { "mfg_name", PY_UNISTR, offsetof(DRIVER_INFO_6, mfgname) },
+ { "oem_url", PY_UNISTR, offsetof(DRIVER_INFO_6, oem_url) },
+ { "hardware_id", PY_UNISTR, offsetof(DRIVER_INFO_6, hardware_id) },
+ { "provider", PY_UNISTR, offsetof(DRIVER_INFO_6, provider) },
+
+ { NULL }
+};
+
+struct pyconv py_DRIVER_DIRECTORY_1[] = {
+ { "name", PY_UNISTR, offsetof(DRIVER_DIRECTORY_1, name) },
+ { NULL }
+};
+
+static uint16 *to_dependentfiles(PyObject *dict)
+{
+ return (uint16 *)"abcd\0";
+}
+
+BOOL py_from_DRIVER_INFO_1(PyObject **dict, DRIVER_INFO_1 *info)
+{
+ *dict = from_struct(info, py_DRIVER_INFO_1);
+ PyDict_SetItemString(*dict, "level", PyInt_FromLong(1));
+
+ return True;
+}
+
+BOOL py_to_DRIVER_INFO_1(DRIVER_INFO_1 *info, PyObject *dict)
+{
+ return False;
+}
+
+BOOL py_from_DRIVER_INFO_2(PyObject **dict, DRIVER_INFO_2 *info)
+{
+ *dict = from_struct(info, py_DRIVER_INFO_2);
+ PyDict_SetItemString(*dict, "level", PyInt_FromLong(2));
+
+ return True;
+}
+
+BOOL py_to_DRIVER_INFO_2(DRIVER_INFO_2 *info, PyObject *dict)
+{
+ return False;
+}
+
+BOOL py_from_DRIVER_INFO_3(PyObject **dict, DRIVER_INFO_3 *info)
+{
+ *dict = from_struct(info, py_DRIVER_INFO_3);
+
+ PyDict_SetItemString(*dict, "level", PyInt_FromLong(3));
+
+ PyDict_SetItemString(
+ *dict, "dependent_files",
+ from_unistr_list(info->dependentfiles));
+
+ return True;
+}
+
+BOOL py_to_DRIVER_INFO_3(DRIVER_INFO_3 *info, PyObject *dict)
+{
+ PyObject *obj, *dict_copy = PyDict_Copy(dict);
+ BOOL result = False;
+
+ if (!(obj = PyDict_GetItemString(dict_copy, "dependent_files")) ||
+ !PyList_Check(obj))
+ goto done;
+
+ info->dependentfiles = to_dependentfiles(obj);
+
+ PyDict_DelItemString(dict_copy, "dependent_files");
+
+ if (!(obj = PyDict_GetItemString(dict_copy, "level")) ||
+ !PyInt_Check(obj))
+ goto done;
+
+ PyDict_DelItemString(dict_copy, "level");
+
+ if (!to_struct(info, dict_copy, py_DRIVER_INFO_3))
+ goto done;
+
+ result = True;
+
+done:
+ Py_DECREF(dict_copy);
+ return result;
+}
+
+BOOL py_from_DRIVER_INFO_6(PyObject **dict, DRIVER_INFO_6 *info)
+{
+ *dict = from_struct(info, py_DRIVER_INFO_6);
+ PyDict_SetItemString(*dict, "level", PyInt_FromLong(6));
+ PyDict_SetItemString(
+ *dict, "dependent_files",
+ from_unistr_list(info->dependentfiles));
+ return True;
+}
+
+BOOL py_to_DRIVER_INFO_6(DRIVER_INFO_6 *info, PyObject *dict)
+{
+ return False;
+}
+
+BOOL py_from_DRIVER_DIRECTORY_1(PyObject **dict, DRIVER_DIRECTORY_1 *info)
+{
+ *dict = from_struct(info, py_DRIVER_DIRECTORY_1);
+ PyDict_SetItemString(*dict, "level", PyInt_FromLong(1));
+ return True;
+}
+
+BOOL py_to_DRIVER_DIRECTORY_1(DRIVER_DIRECTORY_1 *info, PyObject *dict)
+{
+ return False;
+}
diff --git a/source/python/py_spoolss_forms.c b/source/python/py_spoolss_forms.c
new file mode 100644
index 00000000000..66a6540e074
--- /dev/null
+++ b/source/python/py_spoolss_forms.c
@@ -0,0 +1,266 @@
+/*
+ Python wrappers for DCERPC/SMB client routines.
+
+ Copyright (C) Tim Potter, 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 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 "python/py_spoolss.h"
+
+/* Add a form */
+
+PyObject *spoolss_hnd_addform(PyObject *self, PyObject *args, PyObject *kw)
+{
+ spoolss_policy_hnd_object *hnd = (spoolss_policy_hnd_object *)self;
+ WERROR werror;
+ PyObject *info;
+ FORM form;
+ int level;
+ static char *kwlist[] = {"form", NULL};
+
+ /* Parse parameters */
+
+ if (!PyArg_ParseTupleAndKeywords(
+ args, kw, "O!", kwlist, &PyDict_Type, &info))
+ return NULL;
+
+ /* Call rpc function */
+
+ if (!py_to_FORM(&form, info)) {
+ PyErr_SetString(spoolss_error, "invalid form");
+ return NULL;
+ }
+
+ if (!get_level_value(info, &level)) {
+ PyErr_SetString(spoolss_error, "invalid info level");
+ return NULL;
+ }
+
+ if (level != 1) {
+ PyErr_SetString(spoolss_error, "unsupported info level");
+ return NULL;
+ }
+
+ switch (level) {
+ case 1: {
+ PyObject *obj = PyDict_GetItemString(info, "name");
+ char *form_name = PyString_AsString(obj);
+
+ init_unistr2(&form.name, form_name, UNI_STR_TERMINATE);
+ break;
+ }
+ default:
+ PyErr_SetString(spoolss_error, "unsupported info level");
+ return NULL;
+ }
+
+ werror = cli_spoolss_addform(hnd->cli, hnd->mem_ctx, &hnd->pol,
+ level, &form);
+
+
+ if (!W_ERROR_IS_OK(werror)) {
+ PyErr_SetObject(spoolss_werror, py_werror_tuple(werror));
+ return NULL;
+ }
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+/* Get form properties */
+
+PyObject *spoolss_hnd_getform(PyObject *self, PyObject *args, PyObject *kw)
+{
+ spoolss_policy_hnd_object *hnd = (spoolss_policy_hnd_object *)self;
+ WERROR werror;
+ PyObject *result;
+ char *form_name;
+ int level = 1;
+ static char *kwlist[] = {"form_name", "level", NULL};
+ uint32 needed;
+ FORM_1 form;
+
+ /* Parse parameters */
+
+ if (!PyArg_ParseTupleAndKeywords(
+ args, kw, "s|i", kwlist, &form_name, &level))
+ return NULL;
+
+ /* Call rpc function */
+
+ werror = cli_spoolss_getform(hnd->cli, hnd->mem_ctx, 0, &needed,
+ &hnd->pol, form_name, level, &form);
+
+ if (W_ERROR_V(werror) == ERRinsufficientbuffer)
+ werror = cli_spoolss_getform(
+ hnd->cli, hnd->mem_ctx, needed, NULL, &hnd->pol,
+ form_name, 1, &form);
+
+ if (!W_ERROR_IS_OK(werror)) {
+ PyErr_SetObject(spoolss_werror, py_werror_tuple(werror));
+ return NULL;
+ }
+
+ result = Py_None;
+
+ switch(level) {
+ case 1:
+ py_from_FORM_1(&result, &form);
+ break;
+ }
+
+ Py_INCREF(result);
+ return result;
+}
+
+/* Set form properties */
+
+PyObject *spoolss_hnd_setform(PyObject *self, PyObject *args, PyObject *kw)
+{
+ spoolss_policy_hnd_object *hnd = (spoolss_policy_hnd_object *)self;
+ WERROR werror;
+ PyObject *info, *form_name;
+ int level;
+ static char *kwlist[] = { "form", NULL};
+ FORM form;
+
+ /* Parse parameters */
+
+ if (!PyArg_ParseTupleAndKeywords(
+ args, kw, "O!", kwlist, &PyDict_Type, &info))
+ return NULL;
+
+ if (!get_level_value(info, &level)) {
+ PyErr_SetString(spoolss_error, "invalid info level");
+ return NULL;
+ }
+
+ if (level != 1) {
+ PyErr_SetString(spoolss_error, "unsupported info level");
+ return NULL;
+ }
+
+ /* Call rpc function */
+
+ if (!py_to_FORM(&form, info)) {
+ PyErr_SetString(spoolss_error, "invalid form");
+ return NULL;
+ }
+
+ form_name = PyDict_GetItemString(info, "name");
+
+ werror = cli_spoolss_setform(
+ hnd->cli, hnd->mem_ctx, &hnd->pol, level,
+ PyString_AsString(form_name), &form);
+
+ if (!W_ERROR_IS_OK(werror)) {
+ PyErr_SetObject(spoolss_werror, py_werror_tuple(werror));
+ return NULL;
+ }
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+/* Delete a form */
+
+PyObject *spoolss_hnd_deleteform(PyObject *self, PyObject *args, PyObject *kw)
+{
+ spoolss_policy_hnd_object *hnd = (spoolss_policy_hnd_object *)self;
+ WERROR werror;
+ static char *kwlist[] = {"form_name", NULL};
+ char *form_name;
+
+ /* Parse parameters */
+
+ if (!PyArg_ParseTupleAndKeywords(
+ args, kw, "s", kwlist, &form_name))
+ return NULL;
+
+ /* Call rpc function */
+
+ werror = cli_spoolss_deleteform(
+ hnd->cli, hnd->mem_ctx, &hnd->pol, form_name);
+
+ if (!W_ERROR_IS_OK(werror)) {
+ PyErr_SetObject(spoolss_werror, py_werror_tuple(werror));
+ return NULL;
+ }
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+/* Enumerate forms */
+
+PyObject *spoolss_hnd_enumforms(PyObject *self, PyObject *args, PyObject *kw)
+{
+ PyObject *result;
+ spoolss_policy_hnd_object *hnd = (spoolss_policy_hnd_object *)self;
+ WERROR werror;
+ uint32 level = 1, num_forms, needed, i;
+ static char *kwlist[] = {"level", NULL};
+ FORM_1 *forms;
+
+ /* Parse parameters */
+
+ if (!PyArg_ParseTupleAndKeywords(
+ args, kw, "|i", kwlist, &level))
+ return NULL;
+
+ /* Call rpc function */
+
+ werror = cli_spoolss_enumforms(
+ hnd->cli, hnd->mem_ctx, 0, &needed, &hnd->pol, level,
+ &num_forms, &forms);
+
+ if (W_ERROR_V(werror) == ERRinsufficientbuffer)
+ werror = cli_spoolss_enumforms(
+ hnd->cli, hnd->mem_ctx, needed, NULL, &hnd->pol, level,
+ &num_forms, &forms);
+
+ if (!W_ERROR_IS_OK(werror)) {
+ PyErr_SetObject(spoolss_werror, py_werror_tuple(werror));
+ return NULL;
+ }
+
+ switch(level) {
+ case 1:
+ result = PyDict_New();
+
+ for (i = 0; i < num_forms; i++) {
+ PyObject *value;
+ fstring name;
+
+ rpcstr_pull(name, forms[i].name.buffer,
+ sizeof(fstring), -1, STR_TERMINATE);
+
+ py_from_FORM_1(&value, &forms[i]);
+
+ PyDict_SetItemString(
+ value, "level", PyInt_FromLong(1));
+
+ PyDict_SetItemString(result, name, value);
+ }
+
+ break;
+ default:
+ PyErr_SetString(spoolss_error, "unknown info level");
+ return NULL;
+ }
+
+ return result;
+}
diff --git a/source/python/py_spoolss_forms_conv.c b/source/python/py_spoolss_forms_conv.c
new file mode 100644
index 00000000000..ede729cad33
--- /dev/null
+++ b/source/python/py_spoolss_forms_conv.c
@@ -0,0 +1,91 @@
+/*
+ Python wrappers for DCERPC/SMB client routines.
+
+ Copyright (C) Tim Potter, 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 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 "python/py_spoolss.h"
+#include "python/py_conv.h"
+
+struct pyconv py_FORM[] = {
+ { "flags", PY_UINT32, offsetof(FORM, flags) },
+ { "width", PY_UINT32, offsetof(FORM, size_x) },
+ { "length", PY_UINT32, offsetof(FORM, size_y) },
+ { "top", PY_UINT32, offsetof(FORM, top) },
+ { "left", PY_UINT32, offsetof(FORM, left) },
+ { "right", PY_UINT32, offsetof(FORM, right) },
+ { "bottom", PY_UINT32, offsetof(FORM, bottom) },
+ { NULL }
+};
+
+struct pyconv py_FORM_1[] = {
+ { "flags", PY_UINT32, offsetof(FORM_1, flag) },
+ { "width", PY_UINT32, offsetof(FORM_1, width) },
+ { "length", PY_UINT32, offsetof(FORM_1, length) },
+ { "top", PY_UINT32, offsetof(FORM_1, top) },
+ { "left", PY_UINT32, offsetof(FORM_1, left) },
+ { "right", PY_UINT32, offsetof(FORM_1, right) },
+ { "bottom", PY_UINT32, offsetof(FORM_1, bottom) },
+ { "name", PY_UNISTR, offsetof(FORM_1, name) },
+ { NULL }
+};
+
+BOOL py_from_FORM_1(PyObject **dict, FORM_1 *form)
+{
+ *dict = from_struct(form, py_FORM_1);
+
+ PyDict_SetItemString(*dict, "level", PyInt_FromLong(1));
+
+ return True;
+}
+
+BOOL py_to_FORM(FORM *form, PyObject *dict)
+{
+ PyObject *obj, *dict_copy = PyDict_Copy(dict);
+ char *name;
+ BOOL result = False;
+
+ if (!(obj = PyDict_GetItemString(dict_copy, "name")) ||
+ !PyString_Check(obj))
+ goto done;
+
+ PyDict_DelItemString(dict_copy, "name");
+
+ if (!(obj = PyDict_GetItemString(dict_copy, "level")) ||
+ !PyInt_Check(obj))
+ goto done;
+
+ PyDict_DelItemString(dict_copy, "level");
+
+ if (!to_struct(form, dict_copy, py_FORM))
+ goto done;
+
+ /* Careful! We can't call PyString_AsString(obj) then delete
+ obj and still expect to have our pointer pointing somewhere
+ useful. */
+
+ obj = PyDict_GetItemString(dict, "name");
+ name = PyString_AsString(obj);
+
+ init_unistr2(&form->name, name, UNI_STR_TERMINATE);
+
+ result = True;
+
+done:
+ Py_DECREF(dict_copy);
+ return result;
+}
diff --git a/source/python/py_spoolss_jobs.c b/source/python/py_spoolss_jobs.c
new file mode 100644
index 00000000000..59754bd36dd
--- /dev/null
+++ b/source/python/py_spoolss_jobs.c
@@ -0,0 +1,377 @@
+/*
+ Python wrappers for DCERPC/SMB client routines.
+
+ Copyright (C) Tim Potter, 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 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 "python/py_spoolss.h"
+
+/* Enumerate jobs */
+
+PyObject *spoolss_hnd_enumjobs(PyObject *self, PyObject *args, PyObject *kw)
+{
+ spoolss_policy_hnd_object *hnd = (spoolss_policy_hnd_object *)self;
+ WERROR werror;
+ PyObject *result;
+ int level = 1;
+ uint32 i, needed, num_jobs;
+ static char *kwlist[] = {"level", NULL};
+ JOB_INFO_CTR ctr;
+
+ /* Parse parameters */
+
+ if (!PyArg_ParseTupleAndKeywords(args, kw, "|i", kwlist, &level))
+ return NULL;
+
+ /* Call rpc function */
+
+ werror = cli_spoolss_enumjobs(
+ hnd->cli, hnd->mem_ctx, 0, &needed, &hnd->pol, level, 0,
+ 1000, &num_jobs, &ctr);
+
+ if (W_ERROR_V(werror) == ERRinsufficientbuffer)
+ werror = cli_spoolss_enumjobs(
+ hnd->cli, hnd->mem_ctx, needed, NULL, &hnd->pol,
+ level, 0, 1000, &num_jobs, &ctr);
+
+ /* Return value */
+
+ result = Py_None;
+
+ if (!W_ERROR_IS_OK(werror)) {
+ PyErr_SetObject(spoolss_werror, py_werror_tuple(werror));
+ goto done;
+ }
+
+ result = PyList_New(num_jobs);
+
+ switch (level) {
+ case 1:
+ for (i = 0; i < num_jobs; i++) {
+ PyObject *value;
+
+ py_from_JOB_INFO_1(&value, &ctr.job.job_info_1[i]);
+
+ PyList_SetItem(result, i, value);
+ }
+
+ break;
+ case 2:
+ for(i = 0; i < num_jobs; i++) {
+ PyObject *value;
+
+ py_from_JOB_INFO_2(&value, &ctr.job.job_info_2[i]);
+
+ PyList_SetItem(result, i, value);
+ }
+
+ break;
+ }
+
+ done:
+ Py_INCREF(result);
+ return result;
+}
+
+/* Set job command */
+
+PyObject *spoolss_hnd_setjob(PyObject *self, PyObject *args, PyObject *kw)
+{
+ spoolss_policy_hnd_object *hnd = (spoolss_policy_hnd_object *)self;
+ WERROR werror;
+ uint32 level = 0, command, jobid;
+ static char *kwlist[] = {"jobid", "command", "level", NULL};
+
+ /* Parse parameters */
+
+ if (!PyArg_ParseTupleAndKeywords(
+ args, kw, "ii|i", kwlist, &jobid, &command, &level))
+ return NULL;
+
+ /* Call rpc function */
+
+ werror = cli_spoolss_setjob(hnd->cli, hnd->mem_ctx, &hnd->pol,
+ jobid, level, command);
+
+ if (!W_ERROR_IS_OK(werror)) {
+ PyErr_SetObject(spoolss_werror, py_werror_tuple(werror));
+ return NULL;
+ }
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+/* Get job */
+
+PyObject *spoolss_hnd_getjob(PyObject *self, PyObject *args, PyObject *kw)
+{
+ spoolss_policy_hnd_object *hnd = (spoolss_policy_hnd_object *)self;
+ WERROR werror;
+ PyObject *result;
+ uint32 level = 1, jobid, needed;
+ static char *kwlist[] = {"jobid", "level", NULL};
+ JOB_INFO_CTR ctr;
+
+ /* Parse parameters */
+
+ if (!PyArg_ParseTupleAndKeywords(
+ args, kw, "i|i", kwlist, &jobid, &level))
+ return NULL;
+
+ /* Call rpc function */
+
+ werror = cli_spoolss_getjob(hnd->cli, hnd->mem_ctx, 0, &needed,
+ &hnd->pol, jobid, level, &ctr);
+
+ if (W_ERROR_V(werror) == ERRinsufficientbuffer)
+ werror = cli_spoolss_getjob(
+ hnd->cli, hnd->mem_ctx, needed, NULL, &hnd->pol,
+ jobid, level, &ctr);
+
+ if (!W_ERROR_IS_OK(werror)) {
+ PyErr_SetObject(spoolss_werror, py_werror_tuple(werror));
+ return NULL;
+ }
+
+ switch(level) {
+ case 1:
+ py_from_JOB_INFO_1(&result, &ctr.job.job_info_1[0]);
+ break;
+ case 2:
+ py_from_JOB_INFO_2(&result, &ctr.job.job_info_2[0]);
+ break;
+ }
+
+ return result;
+}
+
+/* Start page printer. This notifies the spooler that a page is about to be
+ printed on the specified printer. */
+
+PyObject *spoolss_hnd_startpageprinter(PyObject *self, PyObject *args, PyObject *kw)
+{
+ spoolss_policy_hnd_object *hnd = (spoolss_policy_hnd_object *)self;
+ WERROR werror;
+ static char *kwlist[] = { NULL };
+
+ /* Parse parameters */
+
+ if (!PyArg_ParseTupleAndKeywords(args, kw, "", kwlist))
+ return NULL;
+
+ /* Call rpc function */
+
+ werror = cli_spoolss_startpageprinter(
+ hnd->cli, hnd->mem_ctx, &hnd->pol);
+
+ if (!W_ERROR_IS_OK(werror)) {
+ PyErr_SetObject(spoolss_werror, py_werror_tuple(werror));
+ return NULL;
+ }
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+/* End page printer. This notifies the spooler that a page has finished
+ being printed on the specified printer. */
+
+PyObject *spoolss_hnd_endpageprinter(PyObject *self, PyObject *args, PyObject *kw)
+{
+ spoolss_policy_hnd_object *hnd = (spoolss_policy_hnd_object *)self;
+ WERROR werror;
+ static char *kwlist[] = { NULL };
+
+ /* Parse parameters */
+
+ if (!PyArg_ParseTupleAndKeywords(args, kw, "", kwlist))
+ return NULL;
+
+ /* Call rpc function */
+
+ werror = cli_spoolss_endpageprinter(
+ hnd->cli, hnd->mem_ctx, &hnd->pol);
+
+ if (!W_ERROR_IS_OK(werror)) {
+ PyErr_SetObject(spoolss_werror, py_werror_tuple(werror));
+ return NULL;
+ }
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+/* Start doc printer. This notifies the spooler that a document is about to be
+ printed on the specified printer. */
+
+PyObject *spoolss_hnd_startdocprinter(PyObject *self, PyObject *args, PyObject *kw)
+{
+ spoolss_policy_hnd_object *hnd = (spoolss_policy_hnd_object *)self;
+ WERROR werror;
+ static char *kwlist[] = { "document_info", NULL };
+ PyObject *info, *obj;
+ uint32 level, jobid;
+ char *document_name = NULL, *output_file = NULL, *data_type = NULL;
+
+ /* Parse parameters */
+
+ if (!PyArg_ParseTupleAndKeywords(
+ args, kw, "O!", kwlist, &PyDict_Type, &info))
+ return NULL;
+
+ /* Check document_info parameter */
+
+ if (!get_level_value(info, &level)) {
+ PyErr_SetString(spoolss_error, "invalid info level");
+ return NULL;
+ }
+
+ if (level != 1) {
+ PyErr_SetString(spoolss_error, "unsupported info level");
+ return NULL;
+ }
+
+ if ((obj = PyDict_GetItemString(info, "document_name"))) {
+
+ if (!PyString_Check(obj) && obj != Py_None) {
+ PyErr_SetString(spoolss_error,
+ "document_name not a string");
+ return NULL;
+ }
+
+ if (PyString_Check(obj))
+ document_name = PyString_AsString(obj);
+
+ } else {
+ PyErr_SetString(spoolss_error, "no document_name present");
+ return NULL;
+ }
+
+ if ((obj = PyDict_GetItemString(info, "output_file"))) {
+
+ if (!PyString_Check(obj) && obj != Py_None) {
+ PyErr_SetString(spoolss_error,
+ "output_file not a string");
+ return NULL;
+ }
+
+ if (PyString_Check(obj))
+ output_file = PyString_AsString(obj);
+
+ } else {
+ PyErr_SetString(spoolss_error, "no output_file present");
+ return NULL;
+ }
+
+ if ((obj = PyDict_GetItemString(info, "data_type"))) {
+
+ if (!PyString_Check(obj) && obj != Py_None) {
+ PyErr_SetString(spoolss_error,
+ "data_type not a string");
+ return NULL;
+ }
+
+ if (PyString_Check(obj))
+ data_type = PyString_AsString(obj);
+
+ } else {
+ PyErr_SetString(spoolss_error, "no data_type present");
+ return NULL;
+ }
+
+ /* Call rpc function */
+
+ werror = cli_spoolss_startdocprinter(
+ hnd->cli, hnd->mem_ctx, &hnd->pol, document_name,
+ output_file, data_type, &jobid);
+
+ if (!W_ERROR_IS_OK(werror)) {
+ PyErr_SetObject(spoolss_werror, py_werror_tuple(werror));
+ return NULL;
+ }
+
+ /* The return value is zero for an error (where does the status
+ code come from now??) and the return value is the jobid
+ allocated for the new job. */
+
+ return Py_BuildValue("i", jobid);
+}
+
+/* End doc printer. This notifies the spooler that a document has finished
+ being printed on the specified printer. */
+
+PyObject *spoolss_hnd_enddocprinter(PyObject *self, PyObject *args, PyObject *kw)
+{
+ spoolss_policy_hnd_object *hnd = (spoolss_policy_hnd_object *)self;
+ WERROR werror;
+ static char *kwlist[] = { NULL };
+
+ /* Parse parameters */
+
+ if (!PyArg_ParseTupleAndKeywords(args, kw, "", kwlist))
+ return NULL;
+
+ /* Call rpc function */
+
+ werror = cli_spoolss_enddocprinter(hnd->cli, hnd->mem_ctx, &hnd->pol);
+
+ if (!W_ERROR_IS_OK(werror)) {
+ PyErr_SetObject(spoolss_werror, py_werror_tuple(werror));
+ return NULL;
+ }
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+/* Write data to a printer */
+
+PyObject *spoolss_hnd_writeprinter(PyObject *self, PyObject *args, PyObject *kw)
+{
+ spoolss_policy_hnd_object *hnd = (spoolss_policy_hnd_object *)self;
+ WERROR werror;
+ static char *kwlist[] = { "data", NULL };
+ PyObject *data;
+ uint32 num_written;
+
+ /* Parse parameters */
+
+ if (!PyArg_ParseTupleAndKeywords(
+ args, kw, "O!", kwlist, &PyString_Type, &data))
+ return NULL;
+
+ /* Call rpc function */
+
+ werror = cli_spoolss_writeprinter(
+ hnd->cli, hnd->mem_ctx, &hnd->pol, PyString_Size(data),
+ PyString_AsString(data), &num_written);
+
+ if (!W_ERROR_IS_OK(werror)) {
+ PyErr_SetObject(spoolss_werror, py_werror_tuple(werror));
+ return NULL;
+ }
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+PyObject *spoolss_hnd_addjob(PyObject *self, PyObject *args, PyObject *kw)
+{
+ PyErr_SetString(spoolss_error, "Not implemented");
+ return NULL;
+}
diff --git a/source/python/py_spoolss_jobs_conv.c b/source/python/py_spoolss_jobs_conv.c
new file mode 100644
index 00000000000..cb04ec6713c
--- /dev/null
+++ b/source/python/py_spoolss_jobs_conv.c
@@ -0,0 +1,102 @@
+/*
+ Python wrappers for DCERPC/SMB client routines.
+
+ Copyright (C) Tim Potter, 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 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 "python/py_spoolss.h"
+#include "python/py_conv.h"
+
+struct pyconv py_JOB_INFO_1[] = {
+ { "jobid", PY_UINT32, offsetof(JOB_INFO_1, jobid) },
+ { "printer_name", PY_UNISTR, offsetof(JOB_INFO_1, printername) },
+ { "server_name", PY_UNISTR, offsetof(JOB_INFO_1, machinename) },
+ { "user_name", PY_UNISTR, offsetof(JOB_INFO_1, username) },
+ { "document_name", PY_UNISTR, offsetof(JOB_INFO_1, document) },
+ { "data_type", PY_UNISTR, offsetof(JOB_INFO_1, datatype) },
+ { "text_status", PY_UNISTR, offsetof(JOB_INFO_1, text_status) },
+ { "status", PY_UINT32, offsetof(JOB_INFO_1, status) },
+ { "priority", PY_UINT32, offsetof(JOB_INFO_1, priority) },
+ { "position", PY_UINT32, offsetof(JOB_INFO_1, position) },
+ { "total_pages", PY_UINT32, offsetof(JOB_INFO_1, totalpages) },
+ { "pages_printed", PY_UINT32, offsetof(JOB_INFO_1, pagesprinted) },
+ { NULL }
+};
+
+struct pyconv py_JOB_INFO_2[] = {
+ { "jobid", PY_UINT32, offsetof(JOB_INFO_2, jobid) },
+ { "printer_name", PY_UNISTR, offsetof(JOB_INFO_2, printername) },
+ { "server_name", PY_UNISTR, offsetof(JOB_INFO_2, machinename) },
+ { "user_name", PY_UNISTR, offsetof(JOB_INFO_2, username) },
+ { "document_name", PY_UNISTR, offsetof(JOB_INFO_2, document) },
+ { "notify_name", PY_UNISTR, offsetof(JOB_INFO_2, notifyname) },
+ { "data_type", PY_UNISTR, offsetof(JOB_INFO_2, datatype) },
+ { "print_processor", PY_UNISTR, offsetof(JOB_INFO_2, printprocessor) },
+ { "parameters", PY_UNISTR, offsetof(JOB_INFO_2, parameters) },
+ { "driver_name", PY_UNISTR, offsetof(JOB_INFO_2, drivername) },
+ { "text_status", PY_UNISTR, offsetof(JOB_INFO_2, text_status) },
+ { "status", PY_UINT32, offsetof(JOB_INFO_2, status) },
+ { "priority", PY_UINT32, offsetof(JOB_INFO_2, priority) },
+ { "position", PY_UINT32, offsetof(JOB_INFO_2, position) },
+ { "start_time", PY_UINT32, offsetof(JOB_INFO_2, starttime) },
+ { "until_time", PY_UINT32, offsetof(JOB_INFO_2, untiltime) },
+ { "total_pages", PY_UINT32, offsetof(JOB_INFO_2, totalpages) },
+ { "size", PY_UINT32, offsetof(JOB_INFO_2, size) },
+ { "time_elapsed", PY_UINT32, offsetof(JOB_INFO_2, timeelapsed) },
+ { "pages_printed", PY_UINT32, offsetof(JOB_INFO_2, pagesprinted) },
+ { NULL }
+};
+
+struct pyconv py_DOC_INFO_1[] = {
+ { "document_name", PY_UNISTR, offsetof(DOC_INFO_1, docname) },
+ { "output_file", PY_UNISTR, offsetof(DOC_INFO_1, outputfile) },
+ { "data_type", PY_UNISTR, offsetof(DOC_INFO_1, datatype) },
+ { NULL }
+};
+
+BOOL py_from_JOB_INFO_1(PyObject **dict, JOB_INFO_1 *info)
+{
+ *dict = from_struct(info, py_JOB_INFO_1);
+ return True;
+}
+
+BOOL py_to_JOB_INFO_1(JOB_INFO_1 *info, PyObject *dict)
+{
+ return False;
+}
+
+BOOL py_from_JOB_INFO_2(PyObject **dict, JOB_INFO_2 *info)
+{
+ *dict = from_struct(info, py_JOB_INFO_2);
+ return True;
+}
+
+BOOL py_to_JOB_INFO_2(JOB_INFO_2 *info, PyObject *dict)
+{
+ return False;
+}
+
+BOOL py_from_DOC_INFO_1(PyObject **dict, DOC_INFO_1 *info)
+{
+ *dict = from_struct(info, py_DOC_INFO_1);
+ return True;
+}
+
+BOOL py_to_DOC_INFO_1(DOC_INFO_1 *info, PyObject *dict)
+{
+ return to_struct(info, dict, py_DOC_INFO_1);
+}
diff --git a/source/python/py_spoolss_ports.c b/source/python/py_spoolss_ports.c
new file mode 100644
index 00000000000..ddc8868f0f5
--- /dev/null
+++ b/source/python/py_spoolss_ports.c
@@ -0,0 +1,137 @@
+/*
+ Python wrappers for DCERPC/SMB client routines.
+
+ Copyright (C) Tim Potter, 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 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 "python/py_spoolss.h"
+
+/* Enumerate ports */
+
+PyObject *spoolss_enumports(PyObject *self, PyObject *args, PyObject *kw)
+{
+ WERROR werror;
+ PyObject *result = NULL, *creds = NULL;
+ uint32 level = 1;
+ uint32 i, needed, num_ports;
+ static char *kwlist[] = {"server", "level", "creds", NULL};
+ TALLOC_CTX *mem_ctx = NULL;
+ struct cli_state *cli = NULL;
+ char *server, *errstr;
+ PORT_INFO_CTR ctr;
+
+ /* Parse parameters */
+
+ if (!PyArg_ParseTupleAndKeywords(
+ args, kw, "s|iO", kwlist, &server, &level, &creds))
+ return NULL;
+
+ if (server[0] != '\\' || server[1] != '\\') {
+ PyErr_SetString(PyExc_ValueError, "UNC name required");
+ return NULL;
+ }
+
+ server += 2;
+
+ if (creds && creds != Py_None && !PyDict_Check(creds)) {
+ PyErr_SetString(PyExc_TypeError,
+ "credentials must be dictionary or None");
+ return NULL;
+ }
+
+ if (!(cli = open_pipe_creds(server, creds, PI_SPOOLSS, &errstr))) {
+ PyErr_SetString(spoolss_error, errstr);
+ free(errstr);
+ goto done;
+ }
+
+ if (!(mem_ctx = talloc_init("spoolss_enumports"))) {
+ PyErr_SetString(
+ spoolss_error, "unable to init talloc context\n");
+ goto done;
+ }
+
+ /* Call rpc function */
+
+ werror = cli_spoolss_enum_ports(
+ cli, mem_ctx, 0, &needed, level, &num_ports, &ctr);
+
+ if (W_ERROR_V(werror) == ERRinsufficientbuffer)
+ werror = cli_spoolss_enum_ports(
+ cli, mem_ctx, needed, NULL, level,
+ &num_ports, &ctr);
+
+ if (!W_ERROR_IS_OK(werror)) {
+ PyErr_SetObject(spoolss_werror, py_werror_tuple(werror));
+ goto done;
+ }
+
+ /* Return value */
+
+ switch (level) {
+ case 1:
+ result = PyDict_New();
+
+ for (i = 0; i < num_ports; i++) {
+ PyObject *value;
+ fstring name;
+
+ rpcstr_pull(name, ctr.port.info_1[i].port_name.buffer,
+ sizeof(fstring), -1, STR_TERMINATE);
+
+ py_from_PORT_INFO_1(&value, &ctr.port.info_1[i]);
+
+ PyDict_SetItemString(
+ value, "level", PyInt_FromLong(1));
+
+ PyDict_SetItemString(result, name, value);
+ }
+
+ break;
+ case 2:
+ result = PyDict_New();
+
+ for(i = 0; i < num_ports; i++) {
+ PyObject *value;
+ fstring name;
+
+ rpcstr_pull(name, ctr.port.info_2[i].port_name.buffer,
+ sizeof(fstring), -1, STR_TERMINATE);
+
+ py_from_PORT_INFO_2(&value, &ctr.port.info_2[i]);
+
+ PyDict_SetItemString(
+ value, "level", PyInt_FromLong(2));
+
+ PyDict_SetItemString(result, name, value);
+ }
+
+ break;
+ default:
+ PyErr_SetString(spoolss_error, "unknown info level");
+ goto done;
+ }
+
+ done:
+ if (cli)
+ cli_shutdown(cli);
+
+ if (mem_ctx)
+ talloc_destroy(mem_ctx);
+
+ return result;
+}
diff --git a/source/python/py_spoolss_ports_conv.c b/source/python/py_spoolss_ports_conv.c
new file mode 100644
index 00000000000..3f6d94bf7e7
--- /dev/null
+++ b/source/python/py_spoolss_ports_conv.c
@@ -0,0 +1,58 @@
+/*
+ Python wrappers for DCERPC/SMB client routines.
+
+ Copyright (C) Tim Potter, 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 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 "python/py_spoolss.h"
+#include "python/py_conv.h"
+
+struct pyconv py_PORT_INFO_1[] = {
+ { "name", PY_UNISTR, offsetof(PORT_INFO_1, port_name) },
+ { NULL }
+};
+
+struct pyconv py_PORT_INFO_2[] = {
+ { "name", PY_UNISTR, offsetof(PORT_INFO_2, port_name) },
+ { "monitor_name", PY_UNISTR, offsetof(PORT_INFO_2, monitor_name) },
+ { "description", PY_UNISTR, offsetof(PORT_INFO_2, description) },
+ { "reserved", PY_UINT32, offsetof(PORT_INFO_2, reserved) },
+ { "type", PY_UINT32, offsetof(PORT_INFO_2, port_type) },
+ { NULL }
+};
+
+BOOL py_from_PORT_INFO_1(PyObject **dict, PORT_INFO_1 *info)
+{
+ *dict = from_struct(info, py_PORT_INFO_1);
+ return True;
+}
+
+BOOL py_to_PORT_INFO_1(PORT_INFO_1 *info, PyObject *dict)
+{
+ return False;
+}
+
+BOOL py_from_PORT_INFO_2(PyObject **dict, PORT_INFO_2 *info)
+{
+ *dict = from_struct(info, py_PORT_INFO_2);
+ return True;
+}
+
+BOOL py_to_PORT_INFO_2(PORT_INFO_2 *info, PyObject *dict)
+{
+ return False;
+}
diff --git a/source/python/py_spoolss_printerdata.c b/source/python/py_spoolss_printerdata.c
new file mode 100644
index 00000000000..f165475b080
--- /dev/null
+++ b/source/python/py_spoolss_printerdata.c
@@ -0,0 +1,450 @@
+/*
+ Python wrappers for DCERPC/SMB client routines.
+
+ Copyright (C) Tim Potter, 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 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 "python/py_spoolss.h"
+#include "python/py_conv.h"
+
+static BOOL py_from_printerdata(PyObject **dict, char *key, char *value,
+ uint16 data_type, uint8 *data,
+ uint32 data_size)
+{
+ *dict = PyDict_New();
+
+ PyDict_SetItemString(*dict, "key", Py_BuildValue("s", key ? key : ""));
+ PyDict_SetItemString(*dict, "value", Py_BuildValue("s", value));
+ PyDict_SetItemString(*dict, "type", Py_BuildValue("i", data_type));
+
+ PyDict_SetItemString(*dict, "data",
+ Py_BuildValue("s#", data, data_size));
+
+ return True;
+}
+
+static BOOL py_to_printerdata(char **key, char **value, uint16 *data_type,
+ uint8 **data, uint32 *data_size,
+ PyObject *dict)
+{
+ PyObject *obj;
+
+ if ((obj = PyDict_GetItemString(dict, "key"))) {
+
+ if (!PyString_Check(obj)) {
+ PyErr_SetString(spoolss_error,
+ "key not a string");
+ return False;
+ }
+
+ if (key) {
+ *key = PyString_AsString(obj);
+
+ if (!key[0])
+ *key = NULL;
+ }
+ } else
+ *key = NULL;
+
+ if ((obj = PyDict_GetItemString(dict, "value"))) {
+
+ if (!PyString_Check(obj)) {
+ PyErr_SetString(spoolss_error,
+ "value not a string");
+ return False;
+ }
+
+ *value = PyString_AsString(obj);
+ } else {
+ PyErr_SetString(spoolss_error, "no value present");
+ return False;
+ }
+
+ if ((obj = PyDict_GetItemString(dict, "type"))) {
+
+ if (!PyInt_Check(obj)) {
+ PyErr_SetString(spoolss_error,
+ "type not an integer");
+ return False;
+ }
+
+ *data_type = PyInt_AsLong(obj);
+ } else {
+ PyErr_SetString(spoolss_error, "no type present");
+ return False;
+ }
+
+ if ((obj = PyDict_GetItemString(dict, "data"))) {
+
+ if (!PyString_Check(obj)) {
+ PyErr_SetString(spoolss_error,
+ "data not a string");
+ return False;
+ }
+
+ *data = PyString_AsString(obj);
+ *data_size = PyString_Size(obj);
+ } else {
+ PyErr_SetString(spoolss_error, "no data present");
+ return False;
+ }
+
+ return True;
+}
+
+PyObject *spoolss_hnd_getprinterdata(PyObject *self, PyObject *args, PyObject *kw)
+{
+ spoolss_policy_hnd_object *hnd = (spoolss_policy_hnd_object *)self;
+ static char *kwlist[] = { "value", NULL };
+ char *valuename;
+ WERROR werror;
+ uint32 needed;
+ PyObject *result;
+ REGISTRY_VALUE value;
+
+ /* Parse parameters */
+
+ if (!PyArg_ParseTupleAndKeywords(args, kw, "s", kwlist, &valuename))
+ return NULL;
+
+ /* Call rpc function */
+
+ werror = cli_spoolss_getprinterdata(
+ hnd->cli, hnd->mem_ctx, 0, &needed, &hnd->pol, valuename,
+ &value);
+
+ if (W_ERROR_V(werror) == ERRmoredata)
+ werror = cli_spoolss_getprinterdata(
+ hnd->cli, hnd->mem_ctx, needed, NULL, &hnd->pol,
+ valuename, &value);
+
+ if (!W_ERROR_IS_OK(werror)) {
+ PyErr_SetObject(spoolss_werror, py_werror_tuple(werror));
+ return NULL;
+ }
+
+ py_from_printerdata(
+ &result, NULL, valuename, value.type, value.data_p,
+ value.size);
+
+ return result;
+}
+
+PyObject *spoolss_hnd_setprinterdata(PyObject *self, PyObject *args, PyObject *kw)
+{
+ spoolss_policy_hnd_object *hnd = (spoolss_policy_hnd_object *)self;
+ static char *kwlist[] = { "data", NULL };
+ PyObject *py_data;
+ char *valuename;
+ WERROR werror;
+ REGISTRY_VALUE value;
+
+ if (!PyArg_ParseTupleAndKeywords(
+ args, kw, "O!", kwlist, &PyDict_Type, &py_data))
+ return NULL;
+
+ if (!py_to_printerdata(
+ NULL, &valuename, &value.type, &value.data_p,
+ &value.size, py_data))
+ return NULL;
+
+ fstrcpy(value.valuename, valuename);
+
+ /* Call rpc function */
+
+ werror = cli_spoolss_setprinterdata(
+ hnd->cli, hnd->mem_ctx, &hnd->pol, &value);
+
+ if (!W_ERROR_IS_OK(werror)) {
+ PyErr_SetObject(spoolss_werror, py_werror_tuple(werror));
+ return NULL;
+ }
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+PyObject *spoolss_hnd_enumprinterdata(PyObject *self, PyObject *args, PyObject *kw)
+{
+ spoolss_policy_hnd_object *hnd = (spoolss_policy_hnd_object *)self;
+ static char *kwlist[] = { NULL };
+ uint32 data_needed, value_needed, ndx = 0;
+ WERROR werror;
+ PyObject *result;
+ REGISTRY_VALUE value;
+
+ if (!PyArg_ParseTupleAndKeywords(args, kw, "", kwlist))
+ return NULL;
+
+ /* Get max buffer sizes for value and data */
+
+ werror = cli_spoolss_enumprinterdata(
+ hnd->cli, hnd->mem_ctx, &hnd->pol, ndx, 0, 0,
+ &value_needed, &data_needed, NULL);
+
+ if (!W_ERROR_IS_OK(werror)) {
+ PyErr_SetObject(spoolss_werror, py_werror_tuple(werror));
+ return NULL;
+ }
+
+ /* Iterate over all printerdata */
+
+ result = PyDict_New();
+
+ while (W_ERROR_IS_OK(werror)) {
+ PyObject *obj;
+
+ werror = cli_spoolss_enumprinterdata(
+ hnd->cli, hnd->mem_ctx, &hnd->pol, ndx,
+ value_needed, data_needed, NULL, NULL, &value);
+
+ if (py_from_printerdata(
+ &obj, NULL, value.valuename, value.type,
+ value.data_p, value.size))
+ PyDict_SetItemString(result, value.valuename, obj);
+
+ ndx++;
+ }
+
+ return result;
+}
+
+PyObject *spoolss_hnd_deleteprinterdata(PyObject *self, PyObject *args, PyObject *kw)
+{
+ spoolss_policy_hnd_object *hnd = (spoolss_policy_hnd_object *)self;
+ static char *kwlist[] = { "value", NULL };
+ char *value;
+ WERROR werror;
+
+ /* Parse parameters */
+
+ if (!PyArg_ParseTupleAndKeywords(args, kw, "s", kwlist, &value))
+ return NULL;
+
+ /* Call rpc function */
+
+ werror = cli_spoolss_deleteprinterdata(
+ hnd->cli, hnd->mem_ctx, &hnd->pol, value);
+
+ if (!W_ERROR_IS_OK(werror)) {
+ PyErr_SetObject(spoolss_werror, py_werror_tuple(werror));
+ return NULL;
+ }
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+PyObject *spoolss_hnd_getprinterdataex(PyObject *self, PyObject *args, PyObject *kw)
+{
+ spoolss_policy_hnd_object *hnd = (spoolss_policy_hnd_object *)self;
+ static char *kwlist[] = { "key", "value", NULL };
+ char *key, *valuename;
+ WERROR werror;
+ uint32 needed;
+ PyObject *result;
+ REGISTRY_VALUE value;
+
+ /* Parse parameters */
+
+ if (!PyArg_ParseTupleAndKeywords(args, kw, "ss", kwlist, &key, &valuename))
+ return NULL;
+
+ /* Call rpc function */
+
+ werror = cli_spoolss_getprinterdataex(
+ hnd->cli, hnd->mem_ctx, 0, &needed, &hnd->pol, key,
+ valuename, &value);
+
+ if (W_ERROR_V(werror) == ERRmoredata)
+ werror = cli_spoolss_getprinterdataex(
+ hnd->cli, hnd->mem_ctx, needed, NULL, &hnd->pol, key,
+ valuename, &value);
+
+ if (!W_ERROR_IS_OK(werror)) {
+ PyErr_SetObject(spoolss_werror, py_werror_tuple(werror));
+ return NULL;
+ }
+
+ py_from_printerdata(
+ &result, key, valuename, value.type, value.data_p, value.size);
+
+ return result;
+}
+
+PyObject *spoolss_hnd_setprinterdataex(PyObject *self, PyObject *args, PyObject *kw)
+{
+ spoolss_policy_hnd_object *hnd = (spoolss_policy_hnd_object *)self;
+ static char *kwlist[] = { "data", NULL };
+ PyObject *py_data;
+ char *keyname, *valuename;
+ WERROR werror;
+ REGISTRY_VALUE value;
+
+ if (!PyArg_ParseTupleAndKeywords(
+ args, kw, "O!", kwlist, &PyDict_Type, &py_data))
+ return NULL;
+
+ if (!py_to_printerdata(
+ &keyname, &valuename, &value.type, &value.data_p, &value.size, py_data))
+ return NULL;
+
+ fstrcpy(value.valuename, valuename);
+
+ /* Call rpc function */
+
+ werror = cli_spoolss_setprinterdataex(
+ hnd->cli, hnd->mem_ctx, &hnd->pol, keyname, &value);
+
+ if (!W_ERROR_IS_OK(werror)) {
+ PyErr_SetObject(spoolss_werror, py_werror_tuple(werror));
+ return NULL;
+ }
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+PyObject *spoolss_hnd_enumprinterdataex(PyObject *self, PyObject *args, PyObject *kw)
+{
+ spoolss_policy_hnd_object *hnd = (spoolss_policy_hnd_object *)self;
+ static char *kwlist[] = { "key", NULL };
+ uint32 needed, i;
+ char *key;
+ WERROR werror;
+ PyObject *result;
+ REGVAL_CTR ctr;
+
+ if (!PyArg_ParseTupleAndKeywords(args, kw, "s", kwlist, &key))
+ return NULL;
+
+ /* Get max buffer sizes for value and data */
+
+ werror = cli_spoolss_enumprinterdataex(
+ hnd->cli, hnd->mem_ctx, 0, &needed, &hnd->pol, key, &ctr);
+
+ if (W_ERROR_V(werror) == ERRmoredata)
+ werror = cli_spoolss_enumprinterdataex(
+ hnd->cli, hnd->mem_ctx, needed, NULL, &hnd->pol, key,
+ &ctr);
+
+ if (!W_ERROR_IS_OK(werror)) {
+ PyErr_SetObject(spoolss_werror, py_werror_tuple(werror));
+ return NULL;
+ }
+
+ /* Iterate over all printerdata */
+
+ result = PyDict_New();
+
+ for (i = 0; i < regval_ctr_numvals(&ctr); i++) {
+ REGISTRY_VALUE *value;
+ PyObject *item;
+
+ item = PyDict_New();
+ value = regval_ctr_specific_value(&ctr, i);
+
+ if (py_from_printerdata(
+ &item, key, value->valuename, value->type,
+ value->data_p, value->size))
+ PyDict_SetItemString(result, value->valuename, item);
+ }
+
+ return result;
+}
+
+PyObject *spoolss_hnd_deleteprinterdataex(PyObject *self, PyObject *args, PyObject *kw)
+{
+ spoolss_policy_hnd_object *hnd = (spoolss_policy_hnd_object *)self;
+ static char *kwlist[] = { "key", "value", NULL };
+ char *key, *value;
+ WERROR werror;
+
+ /* Parse parameters */
+
+ if (!PyArg_ParseTupleAndKeywords(args, kw, "ss", kwlist, &key, &value))
+ return NULL;
+
+ /* Call rpc function */
+
+ werror = cli_spoolss_deleteprinterdataex(
+ hnd->cli, hnd->mem_ctx, &hnd->pol, key, value);
+
+ if (!W_ERROR_IS_OK(werror)) {
+ PyErr_SetObject(spoolss_werror, py_werror_tuple(werror));
+ return NULL;
+ }
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+PyObject *spoolss_hnd_enumprinterkey(PyObject *self, PyObject *args,
+ PyObject *kw)
+{
+ spoolss_policy_hnd_object *hnd = (spoolss_policy_hnd_object *)self;
+ static char *kwlist[] = { "key", NULL };
+ char *keyname;
+ WERROR werror;
+ uint32 needed, keylist_len;
+ uint16 *keylist;
+ PyObject *result;
+
+ /* Parse parameters */
+
+ if (!PyArg_ParseTupleAndKeywords(args, kw, "s", kwlist, &keyname))
+ return NULL;
+
+ /* Call rpc function */
+
+ werror = cli_spoolss_enumprinterkey(
+ hnd->cli, hnd->mem_ctx, 0, &needed, &hnd->pol,
+ keyname, &keylist, &keylist_len);
+
+ if (W_ERROR_V(werror) == ERRmoredata)
+ werror = cli_spoolss_enumprinterkey(
+ hnd->cli, hnd->mem_ctx, needed, NULL, &hnd->pol,
+ keyname, &keylist, &keylist_len);
+
+ if (!W_ERROR_IS_OK(werror)) {
+ PyErr_SetObject(spoolss_werror, py_werror_tuple(werror));
+ return NULL;
+ }
+
+ result = from_unistr_list(keylist);
+
+ return result;
+}
+
+#if 0
+
+PyObject *spoolss_hnd_deleteprinterkey(PyObject *self, PyObject *args,
+ PyObject *kw)
+{
+ spoolss_policy_hnd_object *hnd = (spoolss_policy_hnd_object *)self;
+ static char *kwlist[] = { "key", NULL };
+ char *keyname;
+ WERROR werror;
+
+ if (!PyArg_ParseTupleAndKeywords(args, kw, "s", kwlist, &keyname))
+ return NULL;
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+#endif
diff --git a/source/python/py_spoolss_printers.c b/source/python/py_spoolss_printers.c
new file mode 100644
index 00000000000..d011681acc2
--- /dev/null
+++ b/source/python/py_spoolss_printers.c
@@ -0,0 +1,475 @@
+/*
+ Python wrappers for DCERPC/SMB client routines.
+
+ Copyright (C) Tim Potter, 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 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 "python/py_spoolss.h"
+
+/* Open a printer */
+
+PyObject *spoolss_openprinter(PyObject *self, PyObject *args, PyObject *kw)
+{
+ char *unc_name, *server, *errstr;
+ TALLOC_CTX *mem_ctx = NULL;
+ POLICY_HND hnd;
+ WERROR werror;
+ PyObject *result = NULL, *creds = NULL;
+ static char *kwlist[] = { "printername", "creds", "access", NULL };
+ uint32 desired_access = MAXIMUM_ALLOWED_ACCESS;
+ struct cli_state *cli;
+
+ if (!PyArg_ParseTupleAndKeywords(
+ args, kw, "s|Oi", kwlist, &unc_name, &creds,
+ &desired_access))
+ return NULL;
+
+ if (unc_name[0] != '\\' || unc_name[1] != '\\') {
+ PyErr_SetString(PyExc_ValueError, "UNC name required");
+ return NULL;
+ }
+
+ server = strdup(unc_name + 2);
+
+ if (strchr(server, '\\')) {
+ char *c = strchr(server, '\\');
+ *c = 0;
+ }
+
+ if (creds && creds != Py_None && !PyDict_Check(creds)) {
+ PyErr_SetString(PyExc_TypeError,
+ "credentials must be dictionary or None");
+ return NULL;
+ }
+
+ if (!(cli = open_pipe_creds(server, creds, PI_SPOOLSS, &errstr))) {
+ PyErr_SetString(spoolss_error, errstr);
+ free(errstr);
+ goto done;
+ }
+
+ if (!(mem_ctx = talloc_init("spoolss_openprinter"))) {
+ PyErr_SetString(spoolss_error,
+ "unable to init talloc context\n");
+ goto done;
+ }
+
+ werror = cli_spoolss_open_printer_ex(
+ cli, mem_ctx, unc_name, "", desired_access, server,
+ "", &hnd);
+
+ if (!W_ERROR_IS_OK(werror)) {
+ PyErr_SetObject(spoolss_werror, py_werror_tuple(werror));
+ goto done;
+ }
+
+ result = new_spoolss_policy_hnd_object(cli, mem_ctx, &hnd);
+
+ done:
+ if (!result) {
+ if (cli)
+ cli_shutdown(cli);
+
+ if (mem_ctx)
+ talloc_destroy(mem_ctx);
+ }
+
+ SAFE_FREE(server);
+
+ return result;
+}
+
+/* Close a printer */
+
+PyObject *spoolss_closeprinter(PyObject *self, PyObject *args)
+{
+ PyObject *po;
+ spoolss_policy_hnd_object *hnd;
+ WERROR result;
+
+ /* Parse parameters */
+
+ if (!PyArg_ParseTuple(args, "O!", &spoolss_policy_hnd_type, &po))
+ return NULL;
+
+ hnd = (spoolss_policy_hnd_object *)po;
+
+ /* Call rpc function */
+
+ result = cli_spoolss_close_printer(hnd->cli, hnd->mem_ctx, &hnd->pol);
+
+ /* Return value */
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+/* Fetch printer information */
+
+PyObject *spoolss_hnd_getprinter(PyObject *self, PyObject *args, PyObject *kw)
+{
+ spoolss_policy_hnd_object *hnd = (spoolss_policy_hnd_object *)self;
+ WERROR werror;
+ PyObject *result = NULL;
+ PRINTER_INFO_CTR ctr;
+ int level = 1;
+ uint32 needed;
+ static char *kwlist[] = {"level", NULL};
+
+ /* Parse parameters */
+
+ if (!PyArg_ParseTupleAndKeywords(args, kw, "|i", kwlist, &level))
+ return NULL;
+
+ ZERO_STRUCT(ctr);
+
+ /* Call rpc function */
+
+ werror = cli_spoolss_getprinter(
+ hnd->cli, hnd->mem_ctx, 0, &needed, &hnd->pol, level, &ctr);
+
+ if (W_ERROR_V(werror) == ERRinsufficientbuffer)
+ werror = cli_spoolss_getprinter(
+ hnd->cli, hnd->mem_ctx, needed, NULL, &hnd->pol,
+ level, &ctr);
+
+ /* Return value */
+
+ if (!W_ERROR_IS_OK(werror)) {
+ PyErr_SetObject(spoolss_werror, py_werror_tuple(werror));
+ return NULL;
+ }
+
+ result = Py_None;
+
+ switch (level) {
+
+ case 0:
+ py_from_PRINTER_INFO_0(&result, ctr.printers_0);
+ break;
+
+ case 1:
+ py_from_PRINTER_INFO_1(&result, ctr.printers_1);
+ break;
+
+ case 2:
+ py_from_PRINTER_INFO_2(&result, ctr.printers_2);
+ break;
+
+ case 3:
+ py_from_PRINTER_INFO_3(&result, ctr.printers_3);
+ break;
+ }
+
+ Py_INCREF(result);
+ return result;
+}
+
+/* Set printer information */
+
+PyObject *spoolss_hnd_setprinter(PyObject *self, PyObject *args, PyObject *kw)
+{
+ spoolss_policy_hnd_object *hnd = (spoolss_policy_hnd_object *)self;
+ WERROR werror;
+ PyObject *info;
+ PRINTER_INFO_CTR ctr;
+ uint32 level;
+ static char *kwlist[] = {"dict", NULL};
+ union {
+ PRINTER_INFO_1 printers_1;
+ PRINTER_INFO_2 printers_2;
+ PRINTER_INFO_3 printers_3;
+ } pinfo;
+
+ /* Parse parameters */
+
+ if (!PyArg_ParseTupleAndKeywords(
+ args, kw, "O!", kwlist, &PyDict_Type, &info))
+ return NULL;
+
+ if (!get_level_value(info, &level)) {
+ PyErr_SetString(spoolss_error, "invalid info level");
+ return NULL;
+ }
+
+ if (level < 1 && level > 3) {
+ PyErr_SetString(spoolss_error, "unsupported info level");
+ return NULL;
+ }
+
+ /* Fill in printer info */
+
+ ZERO_STRUCT(ctr);
+
+ switch (level) {
+ case 1:
+ ctr.printers_1 = &pinfo.printers_1;
+
+ if (!py_to_PRINTER_INFO_1(ctr.printers_1, info)){
+ PyErr_SetString(spoolss_error,
+ "error converting printer to info 1");
+ return NULL;
+ }
+
+ break;
+ case 2:
+ ctr.printers_2 = &pinfo.printers_2;
+
+ if (!py_to_PRINTER_INFO_2(ctr.printers_2, info,
+ hnd->mem_ctx)){
+ PyErr_SetString(spoolss_error,
+ "error converting printer to info 2");
+ return NULL;
+ }
+
+ break;
+ case 3:
+ ctr.printers_3 = &pinfo.printers_3;
+
+ if (!py_to_PRINTER_INFO_3(ctr.printers_3, info,
+ hnd->mem_ctx)) {
+ PyErr_SetString(spoolss_error,
+ "error converting to printer info 3");
+ return NULL;
+ }
+
+ break;
+ default:
+ PyErr_SetString(spoolss_error, "unsupported info level");
+ return NULL;
+ }
+
+ /* Call rpc function */
+
+ werror = cli_spoolss_setprinter(hnd->cli, hnd->mem_ctx, &hnd->pol,
+ level, &ctr, 0);
+
+ /* Return value */
+
+ if (!W_ERROR_IS_OK(werror)) {
+ PyErr_SetObject(spoolss_werror, py_werror_tuple(werror));
+ return NULL;
+ }
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+/* Enumerate printers */
+
+PyObject *spoolss_enumprinters(PyObject *self, PyObject *args, PyObject *kw)
+{
+ WERROR werror;
+ PyObject *result = NULL, *creds = NULL;
+ PRINTER_INFO_CTR ctr;
+ int level = 1, flags = PRINTER_ENUM_LOCAL, i;
+ uint32 needed, num_printers;
+ static char *kwlist[] = {"server", "name", "level", "flags",
+ "creds", NULL};
+ TALLOC_CTX *mem_ctx = NULL;
+ struct cli_state *cli = NULL;
+ char *server, *errstr, *name = NULL;
+
+ /* Parse parameters */
+
+ if (!PyArg_ParseTupleAndKeywords(
+ args, kw, "s|siiO", kwlist, &server, &name, &level,
+ &flags, &creds))
+ return NULL;
+
+ if (server[0] != '\\' || server[1] != '\\') {
+ PyErr_SetString(PyExc_ValueError, "UNC name required");
+ return NULL;
+ }
+
+ server += 2;
+
+ if (creds && creds != Py_None && !PyDict_Check(creds)) {
+ PyErr_SetString(PyExc_TypeError,
+ "credentials must be dictionary or None");
+ return NULL;
+ }
+
+ if (!(cli = open_pipe_creds(server, creds, PI_SPOOLSS, &errstr))) {
+ PyErr_SetString(spoolss_error, errstr);
+ free(errstr);
+ goto done;
+ }
+
+ if (!(mem_ctx = talloc_init("spoolss_enumprinters"))) {
+ PyErr_SetString(
+ spoolss_error, "unable to init talloc context\n");
+ goto done;
+ }
+
+ /* This RPC is weird. By setting the server name to different
+ values we can get different behaviour. If however the server
+ name is not specified, we default it to being the full server
+ name as this is probably what the caller intended. To pass a
+ NULL name, pass a value of "" */
+
+ if (!name)
+ name = server;
+ else {
+ if (!name[0])
+ name = NULL;
+ }
+
+ /* Call rpc function */
+
+ werror = cli_spoolss_enum_printers(
+ cli, mem_ctx, 0, &needed, name, flags, level,
+ &num_printers, &ctr);
+
+ if (W_ERROR_V(werror) == ERRinsufficientbuffer)
+ werror = cli_spoolss_enum_printers(
+ cli, mem_ctx, needed, NULL, name, flags,
+ level, &num_printers, &ctr);
+
+ if (!W_ERROR_IS_OK(werror)) {
+ PyErr_SetObject(spoolss_werror, py_werror_tuple(werror));
+ goto done;
+ }
+
+ /* Return value */
+
+ switch (level) {
+ case 0:
+ result = PyDict_New();
+
+ for (i = 0; i < num_printers; i++) {
+ PyObject *value;
+ fstring s;
+
+ rpcstr_pull(s, ctr.printers_0[i].printername.buffer,
+ sizeof(fstring), -1, STR_TERMINATE);
+
+ py_from_PRINTER_INFO_0(&value, &ctr.printers_0[i]);
+
+ PyDict_SetItemString(
+ value, "level", PyInt_FromLong(0));
+
+ PyDict_SetItemString(result, s, value);
+ }
+
+ break;
+ case 1:
+ result = PyDict_New();
+
+ for(i = 0; i < num_printers; i++) {
+ PyObject *value;
+ fstring s;
+
+ rpcstr_pull(s, ctr.printers_1[i].name.buffer,
+ sizeof(fstring), -1, STR_TERMINATE);
+
+ py_from_PRINTER_INFO_1(&value, &ctr.printers_1[i]);
+
+ PyDict_SetItemString(
+ value, "level", PyInt_FromLong(1));
+
+ PyDict_SetItemString(result, s, value);
+ }
+
+ break;
+ case 2:
+ result = PyDict_New();
+
+ for(i = 0; i < num_printers; i++) {
+ PyObject *value;
+ fstring s;
+
+ rpcstr_pull(s, ctr.printers_2[i].printername.buffer,
+ sizeof(fstring), -1, STR_TERMINATE);
+
+ py_from_PRINTER_INFO_2(&value, &ctr.printers_2[i]);
+
+ PyDict_SetItemString(
+ value, "level", PyInt_FromLong(2));
+
+ PyDict_SetItemString(result, s, value);
+ }
+
+ break;
+ default:
+ PyErr_SetString(spoolss_error, "unknown info level");
+ goto done;
+ }
+
+done:
+ if (cli)
+ cli_shutdown(cli);
+
+ if (mem_ctx)
+ talloc_destroy(mem_ctx);
+
+ return result;
+}
+
+/* Add a printer */
+
+PyObject *spoolss_addprinterex(PyObject *self, PyObject *args, PyObject *kw)
+{
+ static char *kwlist[] = { "server", "printername", "info", "creds",
+ NULL};
+ char *printername, *server, *errstr;
+ PyObject *info, *result = NULL, *creds = NULL;
+ struct cli_state *cli = NULL;
+ TALLOC_CTX *mem_ctx = NULL;
+ PRINTER_INFO_CTR ctr;
+ PRINTER_INFO_2 info2;
+ WERROR werror;
+
+ if (!PyArg_ParseTupleAndKeywords(
+ args, kw, "ssO!|O!", kwlist, &server, &printername,
+ &PyDict_Type, &info, &PyDict_Type, &creds))
+ return NULL;
+
+ if (!(cli = open_pipe_creds(server, creds, PI_SPOOLSS, &errstr))) {
+ PyErr_SetString(spoolss_error, errstr);
+ free(errstr);
+ goto done;
+ }
+
+ if (!(mem_ctx = talloc_init("spoolss_addprinterex"))) {
+ PyErr_SetString(
+ spoolss_error, "unable to init talloc context\n");
+ goto done;
+ }
+
+ if (!py_to_PRINTER_INFO_2(&info2, info, mem_ctx)) {
+ PyErr_SetString(spoolss_error,
+ "error converting to printer info 2");
+ goto done;
+ }
+
+ ctr.printers_2 = &info2;
+
+ werror = cli_spoolss_addprinterex(cli, mem_ctx, 2, &ctr);
+
+ Py_INCREF(Py_None);
+ result = Py_None;
+
+done:
+ if (cli)
+ cli_shutdown(cli);
+
+ if (mem_ctx)
+ talloc_destroy(mem_ctx);
+
+ return result;
+}
diff --git a/source/python/py_spoolss_printers_conv.c b/source/python/py_spoolss_printers_conv.c
new file mode 100644
index 00000000000..f7b2f516df5
--- /dev/null
+++ b/source/python/py_spoolss_printers_conv.c
@@ -0,0 +1,354 @@
+/*
+ Python wrappers for DCERPC/SMB client routines.
+
+ Copyright (C) Tim Potter, 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 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 "python/py_spoolss.h"
+#include "python/py_conv.h"
+
+struct pyconv py_PRINTER_INFO_0[] = {
+ { "name", PY_UNISTR, offsetof(PRINTER_INFO_0, printername) },
+ { "server_name", PY_UNISTR, offsetof(PRINTER_INFO_0, servername) },
+
+ { "cjobs", PY_UINT32, offsetof(PRINTER_INFO_0, cjobs) },
+ { "total_jobs", PY_UINT32, offsetof(PRINTER_INFO_0, total_jobs) },
+ { "total_bytes", PY_UINT32, offsetof(PRINTER_INFO_0, total_bytes) },
+
+ { "year", PY_UINT16, offsetof(PRINTER_INFO_0, year) },
+ { "month", PY_UINT16, offsetof(PRINTER_INFO_0, month) },
+ { "day_of_week", PY_UINT16, offsetof(PRINTER_INFO_0, dayofweek) },
+ { "day", PY_UINT16, offsetof(PRINTER_INFO_0, day) },
+ { "hour", PY_UINT16, offsetof(PRINTER_INFO_0, hour) },
+ { "minute", PY_UINT16, offsetof(PRINTER_INFO_0, minute) },
+ { "second", PY_UINT16, offsetof(PRINTER_INFO_0, second) },
+ { "milliseconds", PY_UINT16, offsetof(PRINTER_INFO_0, milliseconds) },
+
+ { "global_counter", PY_UINT32, offsetof(PRINTER_INFO_0, global_counter) },
+ { "total_pages", PY_UINT32, offsetof(PRINTER_INFO_0, total_pages) },
+
+ { "major_version", PY_UINT16, offsetof(PRINTER_INFO_0, major_version) },
+ { "build_version", PY_UINT16, offsetof(PRINTER_INFO_0, build_version) },
+
+ { "unknown7", PY_UINT32, offsetof(PRINTER_INFO_0, unknown7) },
+ { "unknown8", PY_UINT32, offsetof(PRINTER_INFO_0, unknown8) },
+ { "unknown9", PY_UINT32, offsetof(PRINTER_INFO_0, unknown9) },
+ { "session_counter", PY_UINT32, offsetof(PRINTER_INFO_0, session_counter)},
+ { "unknown11", PY_UINT32, offsetof(PRINTER_INFO_0, unknown11) },
+ { "printer_errors", PY_UINT32, offsetof(PRINTER_INFO_0, printer_errors) },
+ { "unknown13", PY_UINT32, offsetof(PRINTER_INFO_0, unknown13) },
+ { "unknown14", PY_UINT32, offsetof(PRINTER_INFO_0, unknown14) },
+ { "unknown15", PY_UINT32, offsetof(PRINTER_INFO_0, unknown15) },
+ { "unknown16", PY_UINT32, offsetof(PRINTER_INFO_0, unknown16) },
+ { "change_id", PY_UINT32, offsetof(PRINTER_INFO_0, change_id) },
+ { "unknown18", PY_UINT32, offsetof(PRINTER_INFO_0, unknown18) },
+ { "status", PY_UINT32, offsetof(PRINTER_INFO_0, status) },
+ { "unknown20", PY_UINT32, offsetof(PRINTER_INFO_0, unknown20) },
+ { "c_setprinter", PY_UINT32, offsetof(PRINTER_INFO_0, c_setprinter) },
+ { "unknown22", PY_UINT32, offsetof(PRINTER_INFO_0, unknown22) },
+ { "unknown23", PY_UINT32, offsetof(PRINTER_INFO_0, unknown23) },
+ { "unknown24", PY_UINT32, offsetof(PRINTER_INFO_0, unknown24) },
+ { "unknown25", PY_UINT32, offsetof(PRINTER_INFO_0, unknown25) },
+ { "unknown26", PY_UINT32, offsetof(PRINTER_INFO_0, unknown26) },
+ { "unknown27", PY_UINT32, offsetof(PRINTER_INFO_0, unknown27) },
+ { "unknown28", PY_UINT32, offsetof(PRINTER_INFO_0, unknown28) },
+ { "unknown29", PY_UINT32, offsetof(PRINTER_INFO_0, unknown29) },
+
+ { NULL }
+};
+
+struct pyconv py_PRINTER_INFO_1[] = {
+ { "name", PY_UNISTR, offsetof(PRINTER_INFO_1, name) },
+ { "description", PY_UNISTR, offsetof(PRINTER_INFO_1, description) },
+ { "comment", PY_UNISTR, offsetof(PRINTER_INFO_1, comment) },
+ { "flags", PY_UINT32, offsetof(PRINTER_INFO_1, flags) },
+ { NULL }
+};
+
+struct pyconv py_PRINTER_INFO_2[] = {
+ { "server_name", PY_UNISTR, offsetof(PRINTER_INFO_2, servername) },
+ { "name", PY_UNISTR, offsetof(PRINTER_INFO_2, printername) },
+ { "share_name", PY_UNISTR, offsetof(PRINTER_INFO_2, sharename) },
+ { "port_name", PY_UNISTR, offsetof(PRINTER_INFO_2, portname) },
+ { "driver_name", PY_UNISTR, offsetof(PRINTER_INFO_2, drivername) },
+ { "comment", PY_UNISTR, offsetof(PRINTER_INFO_2, comment) },
+ { "location", PY_UNISTR, offsetof(PRINTER_INFO_2, location) },
+ { "datatype", PY_UNISTR, offsetof(PRINTER_INFO_2, datatype) },
+ { "sepfile", PY_UNISTR, offsetof(PRINTER_INFO_2, sepfile) },
+ { "print_processor", PY_UNISTR, offsetof(PRINTER_INFO_2, printprocessor) },
+ { "parameters", PY_UNISTR, offsetof(PRINTER_INFO_2, parameters) },
+ { "attributes", PY_UINT32, offsetof(PRINTER_INFO_2, attributes) },
+ { "default_priority", PY_UINT32, offsetof(PRINTER_INFO_2, defaultpriority) },
+ { "priority", PY_UINT32, offsetof(PRINTER_INFO_2, priority) },
+ { "start_time", PY_UINT32, offsetof(PRINTER_INFO_2, starttime) },
+ { "until_time", PY_UINT32, offsetof(PRINTER_INFO_2, untiltime) },
+ { "status", PY_UINT32, offsetof(PRINTER_INFO_2, status) },
+ { "cjobs", PY_UINT32, offsetof(PRINTER_INFO_2, cjobs) },
+ { "average_ppm", PY_UINT32, offsetof(PRINTER_INFO_2, averageppm) },
+ { NULL }
+};
+
+struct pyconv py_PRINTER_INFO_3[] = {
+ { "flags", PY_UINT32, offsetof(PRINTER_INFO_3, flags) },
+ { NULL }
+};
+
+struct pyconv py_DEVICEMODE[] = {
+ { "device_name", PY_UNISTR, offsetof(DEVICEMODE, devicename) },
+ { "spec_version", PY_UINT16, offsetof(DEVICEMODE, specversion) },
+ { "driver_version", PY_UINT16, offsetof(DEVICEMODE, driverversion) },
+ { "size", PY_UINT16, offsetof(DEVICEMODE, size) },
+ { "fields", PY_UINT16, offsetof(DEVICEMODE, fields) },
+ { "orientation", PY_UINT16, offsetof(DEVICEMODE, orientation) },
+ { "paper_size", PY_UINT16, offsetof(DEVICEMODE, papersize) },
+ { "paper_width", PY_UINT16, offsetof(DEVICEMODE, paperwidth) },
+ { "paper_length", PY_UINT16, offsetof(DEVICEMODE, paperlength) },
+ { "scale", PY_UINT16, offsetof(DEVICEMODE, scale) },
+ { "copies", PY_UINT16, offsetof(DEVICEMODE, copies) },
+ { "default_source", PY_UINT16, offsetof(DEVICEMODE, defaultsource) },
+ { "print_quality", PY_UINT16, offsetof(DEVICEMODE, printquality) },
+ { "color", PY_UINT16, offsetof(DEVICEMODE, color) },
+ { "duplex", PY_UINT16, offsetof(DEVICEMODE, duplex) },
+ { "y_resolution", PY_UINT16, offsetof(DEVICEMODE, yresolution) },
+ { "tt_option", PY_UINT16, offsetof(DEVICEMODE, ttoption) },
+ { "collate", PY_UINT16, offsetof(DEVICEMODE, collate) },
+ { "form_name", PY_UNISTR, offsetof(DEVICEMODE, formname) },
+ { "log_pixels", PY_UINT16, offsetof(DEVICEMODE, logpixels) },
+ { "bits_per_pel", PY_UINT32, offsetof(DEVICEMODE, bitsperpel) },
+ { "pels_width", PY_UINT32, offsetof(DEVICEMODE, pelswidth) },
+ { "pels_height", PY_UINT32, offsetof(DEVICEMODE, pelsheight) },
+ { "display_flags", PY_UINT32, offsetof(DEVICEMODE, displayflags) },
+ { "display_frequency", PY_UINT32, offsetof(DEVICEMODE, displayfrequency) },
+ { "icm_method", PY_UINT32, offsetof(DEVICEMODE, icmmethod) },
+ { "icm_intent", PY_UINT32, offsetof(DEVICEMODE, icmintent) },
+ { "media_type", PY_UINT32, offsetof(DEVICEMODE, mediatype) },
+ { "dither_type", PY_UINT32, offsetof(DEVICEMODE, dithertype) },
+ { "reserved1", PY_UINT32, offsetof(DEVICEMODE, reserved1) },
+ { "reserved2", PY_UINT32, offsetof(DEVICEMODE, reserved2) },
+ { "panning_width", PY_UINT32, offsetof(DEVICEMODE, panningwidth) },
+ { "panning_height", PY_UINT32, offsetof(DEVICEMODE, panningheight) },
+ { NULL }
+};
+
+/*
+ * Convert between DEVICEMODE and Python
+ */
+
+BOOL py_from_DEVICEMODE(PyObject **dict, DEVICEMODE *devmode)
+{
+ *dict = from_struct(devmode, py_DEVICEMODE);
+
+ PyDict_SetItemString(*dict, "private",
+ PyString_FromStringAndSize(
+ devmode->private, devmode->driverextra));
+
+ return True;
+}
+
+BOOL py_to_DEVICEMODE(DEVICEMODE *devmode, PyObject *dict)
+{
+ PyObject *obj, *dict_copy = PyDict_Copy(dict);
+ BOOL result = False;
+
+ if (!(obj = PyDict_GetItemString(dict_copy, "private")))
+ goto done;
+
+ if (!PyString_Check(obj))
+ goto done;
+
+ devmode->private = PyString_AsString(obj);
+ devmode->driverextra = PyString_Size(obj);
+
+ PyDict_DelItemString(dict_copy, "private");
+
+ if (!to_struct(devmode, dict_copy, py_DEVICEMODE))
+ goto done;
+
+ result = True;
+
+done:
+ Py_DECREF(dict_copy);
+ return result;
+}
+
+/*
+ * Convert between PRINTER_INFO_0 and Python
+ */
+
+BOOL py_from_PRINTER_INFO_0(PyObject **dict, PRINTER_INFO_0 *info)
+{
+ *dict = from_struct(info, py_PRINTER_INFO_0);
+ PyDict_SetItemString(*dict, "level", PyInt_FromLong(0));
+ return True;
+}
+
+BOOL py_to_PRINTER_INFO_0(PRINTER_INFO_0 *info, PyObject *dict)
+{
+ return False;
+}
+
+/*
+ * Convert between PRINTER_INFO_1 and Python
+ */
+
+BOOL py_from_PRINTER_INFO_1(PyObject **dict, PRINTER_INFO_1 *info)
+{
+ *dict = from_struct(info, py_PRINTER_INFO_1);
+ PyDict_SetItemString(*dict, "level", PyInt_FromLong(1));
+ return True;
+}
+
+BOOL py_to_PRINTER_INFO_1(PRINTER_INFO_1 *info, PyObject *dict)
+{
+ PyObject *obj, *dict_copy = PyDict_Copy(dict);
+ BOOL result = False;
+
+ if (!(obj = PyDict_GetItemString(dict_copy, "level")) ||
+ !PyInt_Check(obj))
+ goto done;
+
+ PyDict_DelItemString(dict_copy, "level");
+
+ if (!to_struct(info, dict_copy, py_PRINTER_INFO_1))
+ goto done;
+
+ result = True;
+
+done:
+ Py_DECREF(dict_copy);
+ return result;
+}
+
+/*
+ * Convert between PRINTER_INFO_2 and Python
+ */
+
+BOOL py_from_PRINTER_INFO_2(PyObject **dict, PRINTER_INFO_2 *info)
+{
+ PyObject *obj;
+
+ *dict = from_struct(info, py_PRINTER_INFO_2);
+
+ /* The security descriptor could be NULL */
+
+ if (info->secdesc) {
+ if (py_from_SECDESC(&obj, info->secdesc))
+ PyDict_SetItemString(*dict, "security_descriptor", obj);
+ }
+
+ /* Bong! The devmode could be NULL */
+
+ if (info->devmode)
+ py_from_DEVICEMODE(&obj, info->devmode);
+ else
+ obj = PyDict_New();
+
+ PyDict_SetItemString(*dict, "device_mode", obj);
+
+ PyDict_SetItemString(*dict, "level", PyInt_FromLong(2));
+
+ return True;
+}
+
+BOOL py_to_PRINTER_INFO_2(PRINTER_INFO_2 *info, PyObject *dict,
+ TALLOC_CTX *mem_ctx)
+{
+ PyObject *obj, *dict_copy = PyDict_Copy(dict);
+ BOOL result = False;
+
+ /* Convert security descriptor - may be NULL */
+
+ info->secdesc = NULL;
+
+ if ((obj = PyDict_GetItemString(dict_copy, "security_descriptor"))) {
+
+ if (!PyDict_Check(obj))
+ goto done;
+
+ if (!py_to_SECDESC(&info->secdesc, obj, mem_ctx))
+ goto done;
+
+ PyDict_DelItemString(dict_copy, "security_descriptor");
+ }
+
+ /* Convert device mode */
+
+ if (!(obj = PyDict_GetItemString(dict_copy, "device_mode"))
+ || !PyDict_Check(obj))
+ goto done;
+
+ info->devmode = talloc(mem_ctx, sizeof(DEVICEMODE));
+
+ if (!py_to_DEVICEMODE(info->devmode, obj))
+ goto done;
+
+ PyDict_DelItemString(dict_copy, "device_mode");
+
+ /* Check info level */
+
+ if (!(obj = PyDict_GetItemString(dict_copy, "level")) ||
+ !PyInt_Check(obj))
+ goto done;
+
+ PyDict_DelItemString(dict_copy, "level");
+
+ /* Convert remaining elements of dictionary */
+
+ if (!to_struct(info, dict_copy, py_PRINTER_INFO_2))
+ goto done;
+
+ result = True;
+
+done:
+ Py_DECREF(dict_copy);
+ return result;
+}
+
+/*
+ * Convert between PRINTER_INFO_1 and Python
+ */
+
+BOOL py_from_PRINTER_INFO_3(PyObject **dict, PRINTER_INFO_3 *info)
+{
+ PyObject *obj;
+
+ *dict = from_struct(info, py_PRINTER_INFO_3);
+
+ if (py_from_SECDESC(&obj, info->secdesc))
+ PyDict_SetItemString(*dict, "security_descriptor", obj);
+
+ PyDict_SetItemString(*dict, "level", PyInt_FromLong(3));
+
+ return True;
+}
+
+BOOL py_to_PRINTER_INFO_3(PRINTER_INFO_3 *info, PyObject *dict,
+ TALLOC_CTX *mem_ctx)
+{
+ PyObject *obj;
+
+ if (!to_struct(info, dict, py_PRINTER_INFO_3))
+ return False;
+
+ if (!(obj = PyDict_GetItemString(dict, "security_descriptor")))
+ return False;
+
+ if (!py_to_SECDESC(&info->secdesc, obj, mem_ctx))
+ return False;
+
+ return True;
+}
diff --git a/source/python/py_srvsvc.c b/source/python/py_srvsvc.c
new file mode 100644
index 00000000000..3e5a42be234
--- /dev/null
+++ b/source/python/py_srvsvc.c
@@ -0,0 +1,215 @@
+/*
+ Python wrappers for DCERPC/SMB client routines.
+
+ 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 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 "python/py_srvsvc.h"
+
+/* Exceptions this module can raise */
+
+PyObject *srvsvc_error, *srvsvc_werror;
+
+static struct const_vals {
+ char *name;
+ uint32 value;
+} module_const_vals[] = {
+ { "SV_TYPE_WORKSTATION", SV_TYPE_WORKSTATION },
+ { "SV_TYPE_SERVER", SV_TYPE_SERVER },
+ { "SV_TYPE_SQLSERVER", SV_TYPE_SQLSERVER },
+ { "SV_TYPE_DOMAIN_CTRL", SV_TYPE_DOMAIN_CTRL },
+ { "SV_TYPE_DOMAIN_BAKCTRL", SV_TYPE_DOMAIN_BAKCTRL },
+ { "SV_TYPE_TIME_SOURCE", SV_TYPE_TIME_SOURCE },
+ { "SV_TYPE_AFP", SV_TYPE_AFP },
+ { "SV_TYPE_NOVELL", SV_TYPE_NOVELL },
+ { "SV_TYPE_DOMAIN_MEMBER", SV_TYPE_DOMAIN_MEMBER },
+ { "SV_TYPE_PRINTQ_SERVER", SV_TYPE_PRINTQ_SERVER },
+ { "SV_TYPE_DIALIN_SERVER", SV_TYPE_DIALIN_SERVER },
+ { "SV_TYPE_SERVER_UNIX", SV_TYPE_SERVER_UNIX },
+ { "SV_TYPE_NT", SV_TYPE_NT },
+ { "SV_TYPE_WFW", SV_TYPE_WFW },
+ { "SV_TYPE_SERVER_MFPN", SV_TYPE_SERVER_MFPN },
+ { "SV_TYPE_SERVER_NT", SV_TYPE_SERVER_NT },
+ { "SV_TYPE_POTENTIAL_BROWSER", SV_TYPE_POTENTIAL_BROWSER },
+ { "SV_TYPE_BACKUP_BROWSER", SV_TYPE_BACKUP_BROWSER },
+ { "SV_TYPE_MASTER_BROWSER", SV_TYPE_MASTER_BROWSER },
+ { "SV_TYPE_DOMAIN_MASTER", SV_TYPE_DOMAIN_MASTER },
+ { "SV_TYPE_SERVER_OSF", SV_TYPE_SERVER_OSF },
+ { "SV_TYPE_SERVER_VMS", SV_TYPE_SERVER_VMS },
+ { "SV_TYPE_WIN95_PLUS", SV_TYPE_WIN95_PLUS },
+ { "SV_TYPE_DFS_SERVER", SV_TYPE_DFS_SERVER },
+ { "SV_TYPE_ALTERNATE_XPORT", SV_TYPE_ALTERNATE_XPORT },
+ { "SV_TYPE_LOCAL_LIST_ONLY", SV_TYPE_LOCAL_LIST_ONLY },
+ { "SV_TYPE_DOMAIN_ENUM", SV_TYPE_DOMAIN_ENUM },
+ { NULL },
+};
+
+static void const_init(PyObject *dict)
+{
+ struct const_vals *tmp;
+ PyObject *obj;
+
+ for (tmp = module_const_vals; tmp->name; tmp++) {
+ obj = PyInt_FromLong(tmp->value);
+ PyDict_SetItemString(dict, tmp->name, obj);
+ Py_DECREF(obj);
+ }
+}
+
+/* NetServerGetInfo */
+
+PyObject *srvsvc_netservergetinfo(PyObject *self, PyObject *args,
+ PyObject *kw)
+{
+ static char *kwlist[] = { "server", "level", "creds", NULL };
+ char *unc_name, *server, *errstr;
+ PyObject *creds = NULL, *result = NULL;
+ struct cli_state *cli;
+ TALLOC_CTX *mem_ctx = NULL;
+ uint32 level;
+ SRV_INFO_CTR ctr;
+ WERROR status;
+
+ if (!PyArg_ParseTupleAndKeywords(
+ args, kw, "si|O", kwlist, &unc_name, &level, &creds))
+ return NULL;
+
+ if (unc_name[0] != '\\' || unc_name[1] != '\\') {
+ PyErr_SetString(PyExc_ValueError, "UNC name required");
+ return NULL;
+ }
+
+ server = strdup(unc_name + 2);
+
+ if (strchr(server, '\\')) {
+ char *c = strchr(server, '\\');
+ *c = 0;
+ }
+
+ if (creds && creds != Py_None && !PyDict_Check(creds)) {
+ PyErr_SetString(PyExc_TypeError,
+ "credentials must be dictionary or None");
+ return NULL;
+ }
+
+ if (!(cli = open_pipe_creds(server, creds, PI_SRVSVC, &errstr))) {
+ PyErr_SetString(srvsvc_error, errstr);
+ free(errstr);
+ goto done;
+ }
+
+ if (!(mem_ctx = talloc_init("srvsvc_netservergetinfo"))) {
+ PyErr_SetString(srvsvc_error,
+ "unable to init talloc context\n");
+ goto done;
+ }
+
+ ZERO_STRUCT(ctr);
+
+ status = cli_srvsvc_net_srv_get_info(cli, mem_ctx, level, &ctr);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ PyErr_SetObject(srvsvc_error, py_werror_tuple(status));
+ goto done;
+ }
+
+ if (level != ctr.switch_value) {
+ PyErr_SetString(srvsvc_error, "container level value wrong");
+ goto done;
+ }
+
+ switch(level) {
+ case 101:
+ py_from_SRV_INFO_101(&result, &ctr.srv.sv101);
+ break;
+ }
+
+ Py_INCREF(result);
+
+done:
+ if (mem_ctx)
+ talloc_destroy(mem_ctx);
+
+ return result;
+}
+
+/*
+ * Module initialisation
+ */
+
+static PyMethodDef srvsvc_methods[] = {
+ { "netservergetinfo", (PyCFunction)srvsvc_netservergetinfo,
+ METH_VARARGS | METH_KEYWORDS,
+ "Retrieve information about a particular server." },
+
+ { "setup_logging", (PyCFunction)py_setup_logging,
+ METH_VARARGS | METH_KEYWORDS,
+ "Set up debug logging.\n"
+"\n"
+"Initialises Samba's debug logging system. One argument is expected which\n"
+"is a boolean specifying whether debugging is interactive and sent to stdout\n"
+"or logged to a file.\n"
+"\n"
+"Example:\n"
+"\n"
+">>> srvsvc.setup_logging(interactive = 1)" },
+
+ { "get_debuglevel", (PyCFunction)get_debuglevel,
+ METH_VARARGS,
+ "Set the current debug level.\n"
+"\n"
+"Example:\n"
+"\n"
+">>> srvsvc.get_debuglevel()\n"
+"0" },
+
+ { "set_debuglevel", (PyCFunction)set_debuglevel,
+ METH_VARARGS,
+ "Get the current debug level.\n"
+"\n"
+"Example:\n"
+"\n"
+">>> srvsvc.set_debuglevel(10)" },
+
+ { NULL }
+};
+
+void initsrvsvc(void)
+{
+ PyObject *module, *dict;
+
+ /* Initialise module */
+
+ module = Py_InitModule("srvsvc", srvsvc_methods);
+ dict = PyModule_GetDict(module);
+
+ /* Exceptions we can raise */
+
+ srvsvc_error = PyErr_NewException("srvsvc.error", NULL, NULL);
+ PyDict_SetItemString(dict, "error", srvsvc_error);
+
+ srvsvc_werror = PyErr_NewException("srvsvc.werror", NULL, NULL);
+ PyDict_SetItemString(dict, "werror", srvsvc_werror);
+
+ /* Initialise constants */
+
+ const_init(dict);
+
+ /* Do samba initialisation */
+
+ py_samba_init();
+}
diff --git a/source/python/py_srvsvc.h b/source/python/py_srvsvc.h
new file mode 100644
index 00000000000..c5e71cb90f5
--- /dev/null
+++ b/source/python/py_srvsvc.h
@@ -0,0 +1,30 @@
+/*
+ Python wrappers for DCERPC/SMB client routines.
+
+ 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 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 _PY_SRVSVC_H
+#define _PY_SRVSVC_H
+
+#include "python/py_common.h"
+
+/* The following definitions come from python/py_srvsv.c */
+
+BOOL py_from_SRV_INFO_101(PyObject **dict, SRV_INFO_101 *info);
+
+#endif /* _PY_SRVSVC_H */
diff --git a/source/python/py_srvsvc_conv.c b/source/python/py_srvsvc_conv.c
new file mode 100644
index 00000000000..86c3761d0f4
--- /dev/null
+++ b/source/python/py_srvsvc_conv.c
@@ -0,0 +1,41 @@
+/*
+ Python wrappers for DCERPC/SMB client routines.
+
+ 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 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 "python/py_srvsvc.h"
+#include "python/py_conv.h"
+
+static struct pyconv py_SRV_INFO_101[] = {
+ { "platform_id", PY_UINT32, offsetof(SRV_INFO_101, platform_id) },
+ { "major_version", PY_UINT32, offsetof(SRV_INFO_101, ver_major) },
+ { "minor_version", PY_UINT32, offsetof(SRV_INFO_101, ver_minor) },
+ { "server_type", PY_UINT32, offsetof(SRV_INFO_101, srv_type) },
+ { "name", PY_UNISTR2, offsetof(SRV_INFO_101, uni_name) },
+ { "comment", PY_UNISTR2, offsetof(SRV_INFO_101, uni_comment) },
+ { NULL }
+};
+
+BOOL py_from_SRV_INFO_101(PyObject **dict, SRV_INFO_101 *info)
+{
+ *dict = from_struct(info, py_SRV_INFO_101);
+
+ PyDict_SetItemString(*dict, "level", PyInt_FromLong(101));
+
+ return True;
+}
diff --git a/source/python/py_tdb.c b/source/python/py_tdb.c
new file mode 100644
index 00000000000..37f64ce7802
--- /dev/null
+++ b/source/python/py_tdb.c
@@ -0,0 +1,702 @@
+/*
+ Python wrappers for TDB module
+
+ Copyright (C) Tim Potter, 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 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.
+*/
+
+/*
+ NOTE: Since tdb is licenced under the GPL any program that uses these bindings
+ must be distributed under the GPL license terms since this is what
+ the GPL requires.
+
+ http://www.gnu.org/licenses/gpl-faq.html#IfInterpreterIsGPL
+*/
+
+#include "includes.h"
+
+/* This symbol is used in both includes.h and Python.h which causes an
+ annoying compiler warning. */
+
+#ifdef HAVE_FSTAT
+#undef HAVE_FSTAT
+#endif
+
+#include "Python.h"
+
+/* Tdb exception */
+
+PyObject *py_tdb_error;
+
+/* tdb handle object */
+
+typedef struct {
+ PyObject_HEAD
+ TDB_CONTEXT *tdb;
+} tdb_hnd_object;
+
+PyTypeObject tdb_hnd_type;
+
+PyObject *new_tdb_hnd_object(TDB_CONTEXT *tdb)
+{
+ tdb_hnd_object *obj;
+
+ obj = PyObject_New(tdb_hnd_object, &tdb_hnd_type);
+ obj->tdb = tdb;
+
+ return (PyObject *)obj;
+}
+
+PyObject *py_tdb_close(PyObject *self, PyObject *args)
+{
+ tdb_hnd_object *obj;
+
+ if (!PyArg_ParseTuple(args, "O!", &tdb_hnd_type, &obj))
+ return NULL;
+
+ if (tdb_close(obj->tdb) == -1) {
+ obj->tdb = NULL;
+ PyErr_SetString(py_tdb_error, strerror(errno));
+ return NULL;
+ }
+
+ obj->tdb = NULL;
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+PyObject *py_tdb_open(PyObject *self, PyObject *args, PyObject *kw)
+{
+ static char *kwlist[] = { "name", "hash_size", "tdb_flags",
+ "open_flags", "mode", NULL };
+ char *name;
+ int hash_size = 0, flags = TDB_DEFAULT, open_flags = -1, open_mode = 0600;
+ TDB_CONTEXT *tdb;
+
+ if (!PyArg_ParseTupleAndKeywords(
+ args, kw, "s|iiii", kwlist, &name, &hash_size, &flags,
+ &open_flags, &open_mode))
+ return NULL;
+
+ /* Default open_flags to read/write */
+
+ if (open_flags == -1) {
+ if (access(name, W_OK) == -1)
+ open_flags = O_RDONLY;
+ else
+ open_flags = O_RDWR;
+ }
+
+ if (!(tdb = tdb_open(name, hash_size, flags, open_flags, open_mode))) {
+ PyErr_SetString(py_tdb_error, strerror(errno));
+ return NULL;
+ }
+
+ return new_tdb_hnd_object(tdb);
+}
+
+/*
+ * Allow a tdb to act as a python mapping (dictionary)
+ */
+
+static int tdb_traverse_count(TDB_CONTEXT *tdb, TDB_DATA key, TDB_DATA value,
+ void *state)
+{
+ /* Do nothing - tdb_traverse will return the number of records
+ traversed. */
+
+ return 0;
+}
+
+static int tdb_hnd_length(tdb_hnd_object *obj)
+{
+ int result;
+
+ result = tdb_traverse(obj->tdb, tdb_traverse_count, NULL);
+
+ return result;
+}
+
+static PyObject *tdb_hnd_subscript(tdb_hnd_object *obj, PyObject *key)
+{
+ TDB_DATA drec, krec;
+ PyObject *result;
+
+ if (!PyArg_Parse(key, "s#", &krec.dptr, &krec.dsize))
+ return NULL;
+
+ drec = tdb_fetch(obj->tdb, krec);
+
+ if (!drec.dptr) {
+ PyErr_SetString(PyExc_KeyError,
+ PyString_AsString(key));
+ return NULL;
+ }
+
+ result = PyString_FromStringAndSize(drec.dptr, drec.dsize);
+ free(drec.dptr);
+
+ return result;
+}
+
+static int tdb_ass_subscript(tdb_hnd_object *obj, PyObject *key, PyObject *value)
+{
+ TDB_DATA krec, drec;
+
+ if (!PyArg_Parse(key, "s#", &krec.dptr, &krec.dsize)) {
+ PyErr_SetString(PyExc_TypeError,
+ "tdb mappings have string indices only");
+ return -1;
+ }
+
+ if (!obj->tdb) {
+ PyErr_SetString(
+ py_tdb_error, "tdb object has been closed");
+ return -1;
+ }
+
+ if (!value) {
+
+ /* Delete value */
+
+ if (tdb_delete(obj->tdb, krec) == -1) {
+ PyErr_SetString(PyExc_KeyError,
+ PyString_AsString(value));
+ return -1;
+ }
+
+ } else {
+
+ /* Set value */
+
+ if (!PyArg_Parse(value, "s#", &drec.dptr, &drec.dsize)) {
+ PyErr_SetString(PyExc_TypeError,
+ "tdb mappings have string elements only");
+ return -1;
+ }
+
+ errno = 0;
+
+ if (tdb_store(obj->tdb, krec, drec, 0) < 0 ) {
+ if (errno != 0)
+ PyErr_SetFromErrno(py_tdb_error);
+ else
+ PyErr_SetString(
+ py_tdb_error,
+ (char *)tdb_errorstr(obj->tdb));
+
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+static PyMappingMethods tdb_mapping = {
+ (inquiry) tdb_hnd_length,
+ (binaryfunc) tdb_hnd_subscript,
+ (objobjargproc) tdb_ass_subscript
+};
+
+/*
+ * Utility methods
+ */
+
+/* Return non-zero if a given key exists in the tdb */
+
+PyObject *py_tdb_hnd_has_key(PyObject *self, PyObject *args)
+{
+ tdb_hnd_object *obj = (tdb_hnd_object *)self;
+ TDB_DATA key;
+
+ if (!PyArg_ParseTuple(args, "s#", &key.dptr, &key.dsize))
+ return NULL;
+
+ if (!obj->tdb) {
+ PyErr_SetString(
+ py_tdb_error, "tdb object has been closed");
+ return NULL;
+ }
+
+ return PyInt_FromLong(tdb_exists(obj->tdb, key));
+}
+
+/* Return a list of keys in the tdb */
+
+static int tdb_traverse_keys(TDB_CONTEXT *tdb, TDB_DATA key, TDB_DATA value,
+ void *state)
+{
+ PyObject *key_list = (PyObject *)state;
+
+ PyList_Append(key_list,
+ PyString_FromStringAndSize(key.dptr, key.dsize));
+
+ return 0;
+}
+
+PyObject *py_tdb_hnd_keys(PyObject *self, PyObject *args)
+{
+ tdb_hnd_object *obj = (tdb_hnd_object *)self;
+ PyObject *key_list = PyList_New(0);
+
+ if (!obj->tdb) {
+ PyErr_SetString(py_tdb_error, "tdb object has been closed");
+ return NULL;
+ }
+
+ if (tdb_traverse(obj->tdb, tdb_traverse_keys, key_list) == -1) {
+ PyErr_SetString(py_tdb_error, "error traversing tdb");
+ Py_DECREF(key_list);
+ return NULL;
+ }
+
+ return key_list;
+}
+
+PyObject *py_tdb_hnd_first_key(PyObject *self, PyObject *args)
+{
+ tdb_hnd_object *obj = (tdb_hnd_object *)self;
+ TDB_DATA key;
+
+ if (!obj->tdb) {
+ PyErr_SetString(py_tdb_error, "tdb object has been closed");
+ return NULL;
+ }
+
+ key = tdb_firstkey(obj->tdb);
+
+ return Py_BuildValue("s#", key.dptr, key.dsize);
+}
+
+PyObject *py_tdb_hnd_next_key(PyObject *self, PyObject *py_oldkey)
+{
+ tdb_hnd_object *obj = (tdb_hnd_object *)self;
+ TDB_DATA key, oldkey;
+
+ if (!obj->tdb) {
+ PyErr_SetString(py_tdb_error, "tdb object has been closed");
+ return NULL;
+ }
+
+ if (!PyArg_Parse(py_oldkey, "s#", &oldkey.dptr, &oldkey.dsize))
+ return NULL;
+
+ key = tdb_nextkey(obj->tdb, oldkey);
+
+ return Py_BuildValue("s#", key.dptr, key.dsize);
+}
+
+/*
+ * Locking routines
+ */
+
+PyObject *py_tdb_hnd_lock_all(PyObject *self, PyObject *args)
+{
+ tdb_hnd_object *obj = (tdb_hnd_object *)self;
+ int result;
+
+ if (!obj->tdb) {
+ PyErr_SetString(py_tdb_error, "tdb object has been closed");
+ return NULL;
+ }
+
+ result = tdb_lockall(obj->tdb);
+
+ return PyInt_FromLong(result != -1);
+}
+
+PyObject *py_tdb_hnd_unlock_all(PyObject *self, PyObject *args)
+{
+ tdb_hnd_object *obj = (tdb_hnd_object *)self;
+
+ if (!obj->tdb) {
+ PyErr_SetString(py_tdb_error, "tdb object has been closed");
+ return NULL;
+ }
+
+ tdb_unlockall(obj->tdb);
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+/* Return an array of keys from a python object which must be a string or a
+ list of strings. */
+
+static BOOL make_lock_list(PyObject *py_keys, TDB_DATA **keys, int *num_keys)
+{
+ /* Are we a list or a string? */
+
+ if (!PyList_Check(py_keys) && !PyString_Check(py_keys)) {
+ PyErr_SetString(PyExc_TypeError, "arg must be list of string");
+ return False;
+ }
+
+ if (PyList_Check(py_keys)) {
+ int i;
+
+ /* Turn python list into array of keys */
+
+ *num_keys = PyList_Size(py_keys);
+ *keys = (TDB_DATA *)malloc(sizeof(TDB_DATA) * (*num_keys));
+
+ for (i = 0; i < *num_keys; i++) {
+ PyObject *key = PyList_GetItem(py_keys, i);
+
+ if (!PyString_Check(key)) {
+ PyErr_SetString(
+ PyExc_TypeError,
+ "list elements must be strings");
+ return False;
+ }
+
+ PyArg_Parse(key, "s#", &(*keys)[i].dptr,
+ &(*keys)[i].dsize);
+ }
+
+ } else {
+
+ /* Turn python string into a single key */
+
+ *keys = (TDB_DATA *)malloc(sizeof(TDB_DATA));
+ *num_keys = 1;
+ PyArg_Parse(py_keys, "s#", &(*keys)->dptr, &(*keys)->dsize);
+ }
+
+ return True;
+}
+
+PyObject *py_tdb_hnd_lock(PyObject *self, PyObject *args)
+{
+ tdb_hnd_object *obj = (tdb_hnd_object *)self;
+ PyObject *py_keys;
+ TDB_DATA *keys;
+ int num_keys, result;
+
+ if (!obj->tdb) {
+ PyErr_SetString(py_tdb_error, "tdb object has been closed");
+ return NULL;
+ }
+
+ if (!PyArg_ParseTuple(args, "O", &py_keys))
+ return NULL;
+
+ if (!make_lock_list(py_keys, &keys, &num_keys))
+ return NULL;
+
+ result = tdb_lockkeys(obj->tdb, num_keys, keys);
+
+ free(keys);
+
+ return PyInt_FromLong(result != -1);
+}
+
+PyObject *py_tdb_hnd_unlock(PyObject *self, PyObject *args)
+{
+ tdb_hnd_object *obj = (tdb_hnd_object *)self;
+
+ if (!obj->tdb) {
+ PyErr_SetString(py_tdb_error, "tdb object has been closed");
+ return NULL;
+ }
+
+ if (!PyArg_ParseTuple(args, ""))
+ return NULL;
+
+ tdb_unlockkeys(obj->tdb);
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+/*
+ * tdb traversal
+ */
+
+struct traverse_info {
+ PyObject *callback;
+ PyObject *state;
+};
+
+static int tdb_traverse_traverse(TDB_CONTEXT *tdb, TDB_DATA key, TDB_DATA value,
+ void *state)
+{
+ struct traverse_info *info = state;
+ PyObject *arglist, *py_result;
+ int result;
+
+ arglist = Py_BuildValue("(s#s#O)", key.dptr, key.dsize, value.dptr,
+ value.dsize, info->state);
+
+ py_result = PyEval_CallObject(info->callback, arglist);
+
+ Py_DECREF(arglist);
+
+ if (!PyInt_Check(py_result)) {
+ result = 1; /* Hmm - non-integer object returned by callback */
+ goto done;
+ }
+
+ result = PyInt_AsLong(py_result);
+
+done:
+ Py_DECREF(py_result);
+ return result;
+}
+
+PyObject *py_tdb_hnd_traverse(PyObject *self, PyObject *args, PyObject *kw)
+{
+ tdb_hnd_object *obj = (tdb_hnd_object *)self;
+ static char *kwlist[] = { "traverse_fn", "state", NULL };
+ PyObject *state = Py_None, *callback;
+ struct traverse_info info;
+ int result;
+
+ if (!PyArg_ParseTupleAndKeywords(
+ args, kw, "O|O", kwlist, &callback, &state))
+ return NULL;
+
+ if (!PyCallable_Check(callback)) {
+ PyErr_SetString(PyExc_TypeError, "parameter must be callable");
+ return NULL;
+ }
+
+ Py_INCREF(callback);
+ Py_INCREF(state);
+
+ info.callback = callback;
+ info.state = state;
+
+ result = tdb_traverse(obj->tdb, tdb_traverse_traverse, &info);
+
+ Py_DECREF(callback);
+ Py_DECREF(state);
+
+ return PyInt_FromLong(result);
+}
+
+PyObject *py_tdb_hnd_chainlock(PyObject *self, PyObject *args)
+{
+ tdb_hnd_object *obj = (tdb_hnd_object *)self;
+ TDB_DATA key;
+ int result;
+
+ if (!obj->tdb) {
+ PyErr_SetString(py_tdb_error, "tdb object has been closed");
+ return NULL;
+ }
+
+ if (!PyArg_ParseTuple(args, "s#", &key.dptr, &key.dsize))
+ return NULL;
+
+ result = tdb_chainlock(obj->tdb, key);
+
+ return PyInt_FromLong(result != -1);
+}
+
+PyObject *py_tdb_hnd_chainunlock(PyObject *self, PyObject *args)
+{
+ tdb_hnd_object *obj = (tdb_hnd_object *)self;
+ TDB_DATA key;
+ int result;
+
+ if (!obj->tdb) {
+ PyErr_SetString(py_tdb_error, "tdb object has been closed");
+ return NULL;
+ }
+
+ if (!PyArg_ParseTuple(args, "s#", &key.dptr, &key.dsize))
+ return NULL;
+
+ result = tdb_chainunlock(obj->tdb, key);
+
+ return PyInt_FromLong(result != -1);
+}
+
+PyObject *py_tdb_hnd_lock_bystring(PyObject *self, PyObject *args)
+{
+ tdb_hnd_object *obj = (tdb_hnd_object *)self;
+ int result, timeout = 30;
+ char *s;
+
+ if (!obj->tdb) {
+ PyErr_SetString(py_tdb_error, "tdb object has been closed");
+ return NULL;
+ }
+
+ if (!PyArg_ParseTuple(args, "s|i", &s, &timeout))
+ return NULL;
+
+ result = tdb_lock_bystring(obj->tdb, s, timeout);
+
+ return PyInt_FromLong(result != -1);
+}
+
+PyObject *py_tdb_hnd_unlock_bystring(PyObject *self, PyObject *args)
+{
+ tdb_hnd_object *obj = (tdb_hnd_object *)self;
+ char *s;
+
+ if (!obj->tdb) {
+ PyErr_SetString(py_tdb_error, "tdb object has been closed");
+ return NULL;
+ }
+
+ if (!PyArg_ParseTuple(args, "s", &s))
+ return NULL;
+
+ tdb_unlock_bystring(obj->tdb, s);
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+/*
+ * Method dispatch table for this module
+ */
+
+static PyMethodDef tdb_methods[] = {
+ { "open", (PyCFunction)py_tdb_open, METH_VARARGS | METH_KEYWORDS },
+ { "close", (PyCFunction)py_tdb_close, METH_VARARGS },
+ { NULL }
+};
+
+/*
+ * Methods on a tdb object
+ */
+
+static PyMethodDef tdb_hnd_methods[] = {
+ { "keys", (PyCFunction)py_tdb_hnd_keys, METH_VARARGS },
+ { "has_key", (PyCFunction)py_tdb_hnd_has_key, METH_VARARGS },
+ { "first_key", (PyCFunction)py_tdb_hnd_first_key, METH_VARARGS },
+ { "next_key", (PyCFunction)py_tdb_hnd_next_key, METH_VARARGS },
+ { "lock_all", (PyCFunction)py_tdb_hnd_lock_all, METH_VARARGS },
+ { "unlock_all", (PyCFunction)py_tdb_hnd_unlock_all, METH_VARARGS },
+ { "lock", (PyCFunction)py_tdb_hnd_lock, METH_VARARGS },
+ { "unlock", (PyCFunction)py_tdb_hnd_unlock, METH_VARARGS },
+ { "traverse", (PyCFunction)py_tdb_hnd_traverse, METH_VARARGS | METH_KEYWORDS },
+ { "chainlock", (PyCFunction)py_tdb_hnd_chainlock, METH_VARARGS | METH_KEYWORDS },
+ { "chainunlock", (PyCFunction)py_tdb_hnd_chainunlock, METH_VARARGS | METH_KEYWORDS },
+ { "lock_bystring", (PyCFunction)py_tdb_hnd_lock_bystring, METH_VARARGS | METH_KEYWORDS },
+ { "unlock_bystring", (PyCFunction)py_tdb_hnd_unlock_bystring, METH_VARARGS | METH_KEYWORDS },
+ { NULL }
+};
+
+/* Deallocate a tdb handle object */
+
+static void tdb_hnd_dealloc(PyObject* self)
+{
+ tdb_hnd_object *hnd = (tdb_hnd_object *)self;
+
+ if (hnd->tdb) {
+ tdb_close(hnd->tdb);
+ hnd->tdb = NULL;
+ }
+}
+
+/* Return tdb handle attributes */
+
+static PyObject *tdb_hnd_getattr(PyObject *self, char *attrname)
+{
+ return Py_FindMethod(tdb_hnd_methods, self, attrname);
+}
+
+static char tdb_hnd_type_doc[] =
+"Python wrapper for tdb.";
+
+PyTypeObject tdb_hnd_type = {
+ PyObject_HEAD_INIT(NULL)
+ 0,
+ "tdb",
+ sizeof(tdb_hnd_object),
+ 0,
+ tdb_hnd_dealloc, /* tp_dealloc*/
+ 0, /* tp_print*/
+ tdb_hnd_getattr, /* tp_getattr*/
+ 0, /* tp_setattr*/
+ 0, /* tp_compare*/
+ 0, /* tp_repr*/
+ 0, /* tp_as_number*/
+ 0, /* tp_as_sequence*/
+ &tdb_mapping, /* tp_as_mapping*/
+ 0, /* tp_hash */
+ 0, /* tp_call */
+ 0, /* tp_str */
+ 0, /* tp_getattro */
+ 0, /* tp_setattro */
+ 0, /* tp_as_buffer*/
+ Py_TPFLAGS_DEFAULT, /* tp_flags */
+ tdb_hnd_type_doc, /* tp_doc */
+};
+
+/* Constants */
+
+static struct const_vals {
+ char *name;
+ uint32 value;
+} module_const_vals[] = {
+
+ /* Flags for tdb_open() */
+
+ { "TDB_DEFAULT", TDB_DEFAULT },
+ { "TDB_CLEAR_IF_FIRST", TDB_CLEAR_IF_FIRST },
+ { "TDB_INTERNAL", TDB_INTERNAL },
+ { "TDB_NOLOCK", TDB_NOLOCK },
+ { "TDB_NOMMAP", TDB_NOMMAP },
+ { "TDB_CONVERT", TDB_CONVERT },
+ { "TDB_BIGENDIAN", TDB_BIGENDIAN },
+
+ { NULL },
+};
+
+static void const_init(PyObject *dict)
+{
+ struct const_vals *tmp;
+ PyObject *obj;
+
+ for (tmp = module_const_vals; tmp->name; tmp++) {
+ obj = PyInt_FromLong(tmp->value);
+ PyDict_SetItemString(dict, tmp->name, obj);
+ Py_DECREF(obj);
+ }
+}
+
+/* Module initialisation */
+
+void inittdb(void)
+{
+ PyObject *module, *dict;
+
+ /* Initialise module */
+
+ module = Py_InitModule("tdb", tdb_methods);
+ dict = PyModule_GetDict(module);
+
+ py_tdb_error = PyErr_NewException("tdb.error", NULL, NULL);
+ PyDict_SetItemString(dict, "error", py_tdb_error);
+
+ /* Initialise policy handle object */
+
+ tdb_hnd_type.ob_type = &PyType_Type;
+
+ PyDict_SetItemString(dict, "tdb.hnd",
+ (PyObject *)&tdb_hnd_type);
+
+ /* Initialise constants */
+
+ const_init(dict);
+}
diff --git a/source/python/py_tdb.h b/source/python/py_tdb.h
new file mode 100644
index 00000000000..69f251c8c1f
--- /dev/null
+++ b/source/python/py_tdb.h
@@ -0,0 +1,26 @@
+/*
+ Python wrappers for DCERPC/SMB client routines.
+
+ Copyright (C) Tim Potter, 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 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 _PY_TDB_H
+#define _PY_TDB_H
+
+#include "python/py_common.h"
+
+#endif /* _PY_TDB_H */
diff --git a/source/python/py_tdbpack.c b/source/python/py_tdbpack.c
new file mode 100644
index 00000000000..4fa97af8a3c
--- /dev/null
+++ b/source/python/py_tdbpack.c
@@ -0,0 +1,721 @@
+/* -*- c-file-style: "python"; indent-tabs-mode: nil; -*-
+
+ Python wrapper for Samba tdb pack/unpack functions
+ Copyright (C) Martin Pool 2002, 2003
+
+
+ NOTE PYTHON STYLE GUIDE
+ http://www.python.org/peps/pep-0007.html
+
+
+ This program is free software; you can 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 "Python.h"
+
+/* This symbol is used in both config.h and Python.h which causes an
+ annoying compiler warning. */
+
+#ifdef HAVE_FSTAT
+#undef HAVE_FSTAT
+#endif
+
+/* This module is supposed to be standalone, however for portability
+ it would be good to use the FUNCTION_MACRO preprocessor define. */
+
+#include "include/config.h"
+
+#ifdef HAVE_FUNCTION_MACRO
+#define FUNCTION_MACRO (__FUNCTION__)
+#else
+#define FUNCTION_MACRO (__FILE__)
+#endif
+
+static PyObject * pytdbpack_number(char ch, PyObject *val_iter, PyObject *packed_list);
+static PyObject * pytdbpack_str(char ch,
+ PyObject *val_iter, PyObject *packed_list,
+ const char *encoding);
+static PyObject * pytdbpack_buffer(PyObject *val_iter, PyObject *packed_list);
+
+static PyObject *pytdbunpack_item(char, char **pbuf, int *plen, PyObject *);
+
+static PyObject *pytdbpack_data(const char *format_str,
+ PyObject *val_seq,
+ PyObject *val_list);
+
+static PyObject *
+pytdbunpack_string(char **pbuf, int *plen, const char *encoding);
+
+static void pack_le_uint32(unsigned long val_long, unsigned char *pbuf);
+
+
+static PyObject *pytdbpack_bad_type(char ch,
+ const char *expected,
+ PyObject *val_obj);
+
+static const char * pytdbpack_docstring =
+"Convert between Python values and Samba binary encodings.\n"
+"\n"
+"This module is conceptually similar to the standard 'struct' module, but it\n"
+"uses both a different binary format and a different description string.\n"
+"\n"
+"Samba's encoding is based on that used inside DCE-RPC and SMB: a\n"
+"little-endian, unpadded, non-self-describing binary format. It is intended\n"
+"that these functions be as similar as possible to the routines in Samba's\n"
+"tdb/tdbutil module, with appropriate adjustments for Python datatypes.\n"
+"\n"
+"Python strings are used to specify the format of data to be packed or\n"
+"unpacked.\n"
+"\n"
+"String encodings are implied by the database format: they may be either DOS\n"
+"codepage (currently hardcoded to 850), or Unix codepage (currently hardcoded\n"
+"to be the same as the default Python encoding).\n"
+"\n"
+"tdbpack format strings:\n"
+"\n"
+" 'f': NUL-terminated string in codepage iso8859-1\n"
+" \n"
+" 'P': same as 'f'\n"
+"\n"
+" 'F': NUL-terminated string in iso-8859-1\n"
+"\n"
+" 'd': 4 byte little-endian unsigned number\n"
+"\n"
+" 'w': 2 byte little-endian unsigned number\n"
+"\n"
+" 'P': \"Pointer\" value -- in the subset of DCERPC used by Samba, this is\n"
+" really just an \"exists\" or \"does not exist\" flag. The boolean\n"
+" value of the Python object is used.\n"
+" \n"
+" 'B': 4-byte LE length, followed by that many bytes of binary data.\n"
+" Corresponds to a Python integer giving the length, followed by a byte\n"
+" string of the appropriate length.\n"
+"\n"
+" '$': Special flag indicating that the preceding format code should be\n"
+" repeated while data remains. This is only supported for unpacking.\n"
+"\n"
+" Every code corresponds to a single Python object, except 'B' which\n"
+" corresponds to two values (length and contents), and '$', which produces\n"
+" however many make sense.\n";
+
+static char const pytdbpack_doc[] =
+"pack(format, values) -> buffer\n"
+"Pack Python objects into Samba binary format according to format string.\n"
+"\n"
+"arguments:\n"
+" format -- string of tdbpack format characters\n"
+" values -- sequence of value objects corresponding 1:1 to format characters\n"
+"\n"
+"returns:\n"
+" buffer -- string containing packed data\n"
+"\n"
+"raises:\n"
+" IndexError -- if there are too few values for the format\n"
+" ValueError -- if any of the format characters is illegal\n"
+" TypeError -- if the format is not a string, or values is not a sequence,\n"
+" or any of the values is of the wrong type for the corresponding\n"
+" format character\n"
+"\n"
+"notes:\n"
+" For historical reasons, it is not an error to pass more values than are consumed\n"
+" by the format.\n";
+
+
+static char const pytdbunpack_doc[] =
+"unpack(format, buffer) -> (values, rest)\n"
+"Unpack Samba binary data according to format string.\n"
+"\n"
+"arguments:\n"
+" format -- string of tdbpack characters\n"
+" buffer -- string of packed binary data\n"
+"\n"
+"returns:\n"
+" 2-tuple of:\n"
+" values -- sequence of values corresponding 1:1 to format characters\n"
+" rest -- string containing data that was not decoded, or '' if the\n"
+" whole string was consumed\n"
+"\n"
+"raises:\n"
+" IndexError -- if there is insufficient data in the buffer for the\n"
+" format (or if the data is corrupt and contains a variable-length\n"
+" field extending past the end)\n"
+" ValueError -- if any of the format characters is illegal\n"
+"\n"
+"notes:\n"
+" Because unconsumed data is returned, you can feed it back in to the\n"
+" unpacker to extract further fields. Alternatively, if you wish to modify\n"
+" some fields near the start of the data, you may be able to save time by\n"
+" only unpacking and repacking the necessary part.\n";
+
+
+const char *pytdb_dos_encoding = "cp850";
+
+/* NULL, meaning that the Samba default encoding *must* be the same as the
+ Python default encoding. */
+const char *pytdb_unix_encoding = NULL;
+
+
+/*
+ * Pack objects to bytes.
+ *
+ * All objects are first individually encoded onto a list, and then the list
+ * of strings is concatenated. This is faster than concatenating strings,
+ * and reasonably simple to code.
+ */
+static PyObject *
+pytdbpack(PyObject *self,
+ PyObject *args)
+{
+ char *format_str;
+ PyObject *val_seq, *val_iter = NULL,
+ *packed_list = NULL, *packed_str = NULL,
+ *empty_str = NULL;
+
+ /* TODO: Test passing wrong types or too many arguments */
+ if (!PyArg_ParseTuple(args, "sO", &format_str, &val_seq))
+ return NULL;
+
+ if (!(val_iter = PyObject_GetIter(val_seq)))
+ goto out;
+
+ /* Create list to hold strings until we're done, then join them all. */
+ if (!(packed_list = PyList_New(0)))
+ goto out;
+
+ if (!pytdbpack_data(format_str, val_iter, packed_list))
+ goto out;
+
+ /* this function is not officially documented but it works */
+ if (!(empty_str = PyString_InternFromString("")))
+ goto out;
+
+ packed_str = _PyString_Join(empty_str, packed_list);
+
+ out:
+ Py_XDECREF(empty_str);
+ Py_XDECREF(val_iter);
+ Py_XDECREF(packed_list);
+
+ return packed_str;
+}
+
+
+/*
+ Pack data according to FORMAT_STR from the elements of VAL_SEQ into
+ PACKED_BUF.
+
+ The string has already been checked out, so we know that VAL_SEQ is large
+ enough to hold the packed data, and that there are enough value items.
+ (However, their types may not have been thoroughly checked yet.)
+
+ In addition, val_seq is a Python Fast sequence.
+
+ Returns NULL for error (with exception set), or None.
+*/
+PyObject *
+pytdbpack_data(const char *format_str,
+ PyObject *val_iter,
+ PyObject *packed_list)
+{
+ int format_i, val_i = 0;
+
+ for (format_i = 0, val_i = 0; format_str[format_i]; format_i++) {
+ char ch = format_str[format_i];
+
+ switch (ch) {
+ /* dispatch to the appropriate packer for this type,
+ which should pull things off the iterator, and
+ append them to the packed_list */
+ case 'w':
+ case 'd':
+ case 'p':
+ if (!(packed_list = pytdbpack_number(ch, val_iter, packed_list)))
+ return NULL;
+ break;
+
+ case 'f':
+ case 'P':
+ if (!(packed_list = pytdbpack_str(ch, val_iter, packed_list, pytdb_unix_encoding)))
+ return NULL;
+ break;
+
+ case 'B':
+ if (!(packed_list = pytdbpack_buffer(val_iter, packed_list)))
+ return NULL;
+ break;
+
+ default:
+ PyErr_Format(PyExc_ValueError,
+ "%s: format character '%c' is not supported",
+ FUNCTION_MACRO, ch);
+ return NULL;
+ }
+ }
+
+ return packed_list;
+}
+
+
+static PyObject *
+pytdbpack_number(char ch, PyObject *val_iter, PyObject *packed_list)
+{
+ unsigned long val_long;
+ PyObject *val_obj = NULL, *long_obj = NULL, *result_obj = NULL;
+ PyObject *new_list = NULL;
+ unsigned char pack_buf[4];
+
+ if (!(val_obj = PyIter_Next(val_iter)))
+ goto out;
+
+ if (!(long_obj = PyNumber_Long(val_obj))) {
+ pytdbpack_bad_type(ch, "Number", val_obj);
+ goto out;
+ }
+
+ val_long = PyLong_AsUnsignedLong(long_obj);
+ pack_le_uint32(val_long, pack_buf);
+
+ /* pack as 32-bit; if just packing a 'w' 16-bit word then only take
+ the first two bytes. */
+
+ if (!(result_obj = PyString_FromStringAndSize(pack_buf, ch == 'w' ? 2 : 4)))
+ goto out;
+
+ if (PyList_Append(packed_list, result_obj) != -1)
+ new_list = packed_list;
+
+ out:
+ Py_XDECREF(val_obj);
+ Py_XDECREF(long_obj);
+ Py_XDECREF(result_obj);
+
+ return new_list;
+}
+
+
+/*
+ * Take one string from the iterator val_iter, convert it to 8-bit, and return
+ * it.
+ *
+ * If the input is neither a string nor Unicode, an exception is raised.
+ *
+ * If the input is Unicode, then it is converted to the appropriate encoding.
+ *
+ * If the input is a String, and encoding is not null, then it is converted to
+ * Unicode using the default decoding method, and then converted to the
+ * encoding. If the encoding is NULL, then the string is written out as-is --
+ * this is used when the default Python encoding is the same as the Samba
+ * encoding.
+ *
+ * I hope this approach avoids being too fragile w.r.t. being passed either
+ * Unicode or String objects.
+ */
+static PyObject *
+pytdbpack_str(char ch,
+ PyObject *val_iter, PyObject *packed_list, const char *encoding)
+{
+ PyObject *val_obj = NULL;
+ PyObject *unicode_obj = NULL;
+ PyObject *coded_str = NULL;
+ PyObject *nul_str = NULL;
+ PyObject *new_list = NULL;
+
+ if (!(val_obj = PyIter_Next(val_iter)))
+ goto out;
+
+ if (PyUnicode_Check(val_obj)) {
+ if (!(coded_str = PyUnicode_AsEncodedString(val_obj, encoding, NULL)))
+ goto out;
+ }
+ else if (PyString_Check(val_obj) && !encoding) {
+ /* For efficiency, we assume that the Python interpreter has
+ the same default string encoding as Samba's native string
+ encoding. On the PSA, both are always 8859-1. */
+ coded_str = val_obj;
+ Py_INCREF(coded_str);
+ }
+ else if (PyString_Check(val_obj)) {
+ /* String, but needs to be converted */
+ if (!(unicode_obj = PyString_AsDecodedObject(val_obj, NULL, NULL)))
+ goto out;
+ if (!(coded_str = PyUnicode_AsEncodedString(unicode_obj, encoding, NULL)))
+ goto out;
+ }
+ else {
+ pytdbpack_bad_type(ch, "String or Unicode", val_obj);
+ goto out;
+ }
+
+ if (!nul_str)
+ /* this is constant and often-used; hold it forever */
+ if (!(nul_str = PyString_FromStringAndSize("", 1)))
+ goto out;
+
+ if ((PyList_Append(packed_list, coded_str) != -1)
+ && (PyList_Append(packed_list, nul_str) != -1))
+ new_list = packed_list;
+
+ out:
+ Py_XDECREF(val_obj);
+ Py_XDECREF(unicode_obj);
+ Py_XDECREF(coded_str);
+
+ return new_list;
+}
+
+
+/*
+ * Pack (LENGTH, BUFFER) pair onto the list.
+ *
+ * The buffer must already be a String, not Unicode, because it contains 8-bit
+ * untranslated data. In some cases it will actually be UTF_16_LE data.
+ */
+static PyObject *
+pytdbpack_buffer(PyObject *val_iter, PyObject *packed_list)
+{
+ PyObject *val_obj;
+ PyObject *new_list = NULL;
+
+ /* pull off integer and stick onto list */
+ if (!(packed_list = pytdbpack_number('d', val_iter, packed_list)))
+ return NULL;
+
+ /* this assumes that the string is the right length; the old code did
+ the same. */
+ if (!(val_obj = PyIter_Next(val_iter)))
+ return NULL;
+
+ if (!PyString_Check(val_obj)) {
+ pytdbpack_bad_type('B', "String", val_obj);
+ goto out;
+ }
+
+ if (PyList_Append(packed_list, val_obj) != -1)
+ new_list = packed_list;
+
+ out:
+ Py_XDECREF(val_obj);
+ return new_list;
+}
+
+
+static PyObject *pytdbpack_bad_type(char ch,
+ const char *expected,
+ PyObject *val_obj)
+{
+ PyObject *r = PyObject_Repr(val_obj);
+ if (!r)
+ return NULL;
+ PyErr_Format(PyExc_TypeError,
+ "tdbpack: format '%c' requires %s, not %s",
+ ch, expected, PyString_AS_STRING(r));
+ Py_DECREF(r);
+ return val_obj;
+}
+
+
+/*
+ XXX: glib and Samba have quicker macro for doing the endianness conversions,
+ but I don't know of one in plain libc, and it's probably not a big deal. I
+ realize this is kind of dumb because we'll almost always be on x86, but
+ being safe is important.
+*/
+static void pack_le_uint32(unsigned long val_long, unsigned char *pbuf)
+{
+ pbuf[0] = val_long & 0xff;
+ pbuf[1] = (val_long >> 8) & 0xff;
+ pbuf[2] = (val_long >> 16) & 0xff;
+ pbuf[3] = (val_long >> 24) & 0xff;
+}
+
+
+static void pack_bytes(long len, const char *from,
+ unsigned char **pbuf)
+{
+ memcpy(*pbuf, from, len);
+ (*pbuf) += len;
+}
+
+
+
+static PyObject *
+pytdbunpack(PyObject *self,
+ PyObject *args)
+{
+ char *format_str, *packed_str, *ppacked;
+ PyObject *val_list = NULL, *ret_tuple = NULL;
+ PyObject *rest_string = NULL;
+ int format_len, packed_len;
+ char last_format = '#'; /* invalid */
+ int i;
+
+ /* get arguments */
+ if (!PyArg_ParseTuple(args, "ss#", &format_str, &packed_str, &packed_len))
+ return NULL;
+
+ format_len = strlen(format_str);
+
+ /* Allocate list to hold results. Initially empty, and we append
+ results as we go along. */
+ val_list = PyList_New(0);
+ if (!val_list)
+ goto failed;
+ ret_tuple = PyTuple_New(2);
+ if (!ret_tuple)
+ goto failed;
+
+ /* For every object, unpack. */
+ for (ppacked = packed_str, i = 0; i < format_len && format_str[i] != '$'; i++) {
+ last_format = format_str[i];
+ /* packed_len is reduced in place */
+ if (!pytdbunpack_item(format_str[i], &ppacked, &packed_len, val_list))
+ goto failed;
+ }
+
+ /* If the last character was '$', keep going until out of space */
+ if (format_str[i] == '$') {
+ if (i == 0) {
+ PyErr_Format(PyExc_ValueError,
+ "%s: '$' may not be first character in format",
+ FUNCTION_MACRO);
+ return NULL;
+ }
+ while (packed_len > 0)
+ if (!pytdbunpack_item(last_format, &ppacked, &packed_len, val_list))
+ goto failed;
+ }
+
+ /* save leftovers for next time */
+ rest_string = PyString_FromStringAndSize(ppacked, packed_len);
+ if (!rest_string)
+ goto failed;
+
+ /* return (values, rest) tuple; give up references to them */
+ PyTuple_SET_ITEM(ret_tuple, 0, val_list);
+ val_list = NULL;
+ PyTuple_SET_ITEM(ret_tuple, 1, rest_string);
+ val_list = NULL;
+ return ret_tuple;
+
+ failed:
+ /* handle failure: deallocate anything. XDECREF forms handle NULL
+ pointers for objects that haven't been allocated yet. */
+ Py_XDECREF(val_list);
+ Py_XDECREF(ret_tuple);
+ Py_XDECREF(rest_string);
+ return NULL;
+}
+
+
+static void
+pytdbunpack_err_too_short(void)
+{
+ PyErr_Format(PyExc_IndexError,
+ "%s: data too short for unpack format", FUNCTION_MACRO);
+}
+
+
+static PyObject *
+pytdbunpack_uint32(char **pbuf, int *plen)
+{
+ unsigned long v;
+ unsigned char *b;
+
+ if (*plen < 4) {
+ pytdbunpack_err_too_short();
+ return NULL;
+ }
+
+ b = *pbuf;
+ v = b[0] | b[1]<<8 | b[2]<<16 | b[3]<<24;
+
+ (*pbuf) += 4;
+ (*plen) -= 4;
+
+ return PyLong_FromUnsignedLong(v);
+}
+
+
+static PyObject *pytdbunpack_int16(char **pbuf, int *plen)
+{
+ long v;
+ unsigned char *b;
+
+ if (*plen < 2) {
+ pytdbunpack_err_too_short();
+ return NULL;
+ }
+
+ b = *pbuf;
+ v = b[0] | b[1]<<8;
+
+ (*pbuf) += 2;
+ (*plen) -= 2;
+
+ return PyInt_FromLong(v);
+}
+
+
+static PyObject *
+pytdbunpack_string(char **pbuf, int *plen, const char *encoding)
+{
+ int len;
+ char *nul_ptr, *start;
+
+ start = *pbuf;
+
+ nul_ptr = memchr(start, '\0', *plen);
+ if (!nul_ptr) {
+ pytdbunpack_err_too_short();
+ return NULL;
+ }
+
+ len = nul_ptr - start;
+
+ *pbuf += len + 1; /* skip \0 */
+ *plen -= len + 1;
+
+ return PyString_Decode(start, len, encoding, NULL);
+}
+
+
+static PyObject *
+pytdbunpack_buffer(char **pbuf, int *plen, PyObject *val_list)
+{
+ /* first get 32-bit len */
+ long slen;
+ unsigned char *b;
+ unsigned char *start;
+ PyObject *str_obj = NULL, *len_obj = NULL;
+
+ if (*plen < 4) {
+ pytdbunpack_err_too_short();
+ return NULL;
+ }
+
+ b = *pbuf;
+ slen = b[0] | b[1]<<8 | b[2]<<16 | b[3]<<24;
+
+ if (slen < 0) { /* surely you jest */
+ PyErr_Format(PyExc_ValueError,
+ "%s: buffer seems to have negative length", FUNCTION_MACRO);
+ return NULL;
+ }
+
+ (*pbuf) += 4;
+ (*plen) -= 4;
+ start = *pbuf;
+
+ if (*plen < slen) {
+ PyErr_Format(PyExc_IndexError,
+ "%s: not enough data to unpack buffer: "
+ "need %d bytes, have %d", FUNCTION_MACRO,
+ (int) slen, *plen);
+ return NULL;
+ }
+
+ (*pbuf) += slen;
+ (*plen) -= slen;
+
+ if (!(len_obj = PyInt_FromLong(slen)))
+ goto failed;
+
+ if (PyList_Append(val_list, len_obj) == -1)
+ goto failed;
+
+ if (!(str_obj = PyString_FromStringAndSize(start, slen)))
+ goto failed;
+
+ if (PyList_Append(val_list, str_obj) == -1)
+ goto failed;
+
+ Py_DECREF(len_obj);
+ Py_DECREF(str_obj);
+
+ return val_list;
+
+ failed:
+ Py_XDECREF(len_obj); /* handles NULL */
+ Py_XDECREF(str_obj);
+ return NULL;
+}
+
+
+/* Unpack a single field from packed data, according to format character CH.
+ Remaining data is at *PBUF, of *PLEN.
+
+ *PBUF is advanced, and *PLEN reduced to reflect the amount of data that has
+ been consumed.
+
+ Returns a reference to None, or NULL for failure.
+*/
+static PyObject *pytdbunpack_item(char ch,
+ char **pbuf,
+ int *plen,
+ PyObject *val_list)
+{
+ PyObject *unpacked;
+
+ if (ch == 'w') { /* 16-bit int */
+ unpacked = pytdbunpack_int16(pbuf, plen);
+ }
+ else if (ch == 'd' || ch == 'p') { /* 32-bit int */
+ /* pointers can just come through as integers */
+ unpacked = pytdbunpack_uint32(pbuf, plen);
+ }
+ else if (ch == 'f' || ch == 'P') { /* nul-term string */
+ unpacked = pytdbunpack_string(pbuf, plen, pytdb_unix_encoding);
+ }
+ else if (ch == 'B') { /* length, buffer */
+ return pytdbunpack_buffer(pbuf, plen, val_list);
+ }
+ else {
+ PyErr_Format(PyExc_ValueError,
+ "%s: format character '%c' is not supported",
+ FUNCTION_MACRO, ch);
+
+ return NULL;
+ }
+
+ /* otherwise OK */
+ if (!unpacked)
+ return NULL;
+
+ if (PyList_Append(val_list, unpacked) == -1)
+ val_list = NULL;
+
+ /* PyList_Append takes a new reference to the inserted object.
+ Therefore, we no longer need the original reference. */
+ Py_DECREF(unpacked);
+
+ return val_list;
+}
+
+
+
+
+
+
+static PyMethodDef pytdbpack_methods[] = {
+ { "pack", pytdbpack, METH_VARARGS, (char *) pytdbpack_doc },
+ { "unpack", pytdbunpack, METH_VARARGS, (char *) pytdbunpack_doc },
+};
+
+DL_EXPORT(void)
+inittdbpack(void)
+{
+ Py_InitModule3("tdbpack", pytdbpack_methods,
+ (char *) pytdbpack_docstring);
+}
diff --git a/source/python/py_winbind.c b/source/python/py_winbind.c
new file mode 100644
index 00000000000..130f78d7e12
--- /dev/null
+++ b/source/python/py_winbind.c
@@ -0,0 +1,800 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ Python wrapper for winbind client functions.
+
+ Copyright (C) Tim Potter 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 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 "py_winbind.h"
+
+/*
+ * Exceptions raised by this module
+ */
+
+PyObject *winbind_error; /* A winbind call returned WINBINDD_ERROR */
+
+/* Prototypes from common.h */
+
+NSS_STATUS winbindd_request(int req_type,
+ struct winbindd_request *request,
+ struct winbindd_response *response);
+
+/*
+ * Name <-> SID conversion
+ */
+
+/* Convert a name to a sid */
+
+static PyObject *py_name_to_sid(PyObject *self, PyObject *args)
+
+{
+ struct winbindd_request request;
+ struct winbindd_response response;
+ PyObject *result;
+ char *name, *p;
+ const char *sep;
+
+ if (!PyArg_ParseTuple(args, "s", &name))
+ return NULL;
+
+ ZERO_STRUCT(request);
+ ZERO_STRUCT(response);
+
+ sep = lp_winbind_separator();
+
+ if ((p = strchr(name, sep[0]))) {
+ *p = 0;
+ fstrcpy(request.data.name.dom_name, name);
+ fstrcpy(request.data.name.name, p + 1);
+ } else {
+ fstrcpy(request.data.name.dom_name, lp_workgroup());
+ fstrcpy(request.data.name.name, name);
+ }
+
+ if (winbindd_request(WINBINDD_LOOKUPNAME, &request, &response)
+ != NSS_STATUS_SUCCESS) {
+ PyErr_SetString(winbind_error, "lookup failed");
+ return NULL;
+ }
+
+ result = PyString_FromString(response.data.sid.sid);
+
+ return result;
+}
+
+/* Convert a sid to a name */
+
+static PyObject *py_sid_to_name(PyObject *self, PyObject *args)
+{
+ struct winbindd_request request;
+ struct winbindd_response response;
+ PyObject *result;
+ char *sid, *name;
+
+ if (!PyArg_ParseTuple(args, "s", &sid))
+ return NULL;
+
+ ZERO_STRUCT(request);
+ ZERO_STRUCT(response);
+
+ fstrcpy(request.data.sid, sid);
+
+ if (winbindd_request(WINBINDD_LOOKUPSID, &request, &response)
+ != NSS_STATUS_SUCCESS) {
+ PyErr_SetString(winbind_error, "lookup failed");
+ return NULL;
+ }
+
+ asprintf(&name, "%s%s%s", response.data.name.dom_name,
+ lp_winbind_separator(), response.data.name.name);
+
+ result = PyString_FromString(name);
+
+ free(name);
+
+ return result;
+}
+
+/*
+ * Enumerate users/groups
+ */
+
+/* Enumerate domain users */
+
+static PyObject *py_enum_domain_users(PyObject *self, PyObject *args)
+{
+ struct winbindd_response response;
+ PyObject *result;
+
+ if (!PyArg_ParseTuple(args, ""))
+ return NULL;
+
+ ZERO_STRUCT(response);
+
+ if (winbindd_request(WINBINDD_LIST_USERS, NULL, &response)
+ != NSS_STATUS_SUCCESS) {
+ PyErr_SetString(winbind_error, "lookup failed");
+ return NULL;
+ }
+
+ result = PyList_New(0);
+
+ if (response.extra_data) {
+ const char *extra_data = response.extra_data;
+ fstring name;
+
+ while (next_token(&extra_data, name, ",", sizeof(fstring)))
+ PyList_Append(result, PyString_FromString(name));
+ }
+
+ return result;
+}
+
+/* Enumerate domain groups */
+
+static PyObject *py_enum_domain_groups(PyObject *self, PyObject *args)
+{
+ struct winbindd_response response;
+ PyObject *result = NULL;
+
+ if (!PyArg_ParseTuple(args, ""))
+ return NULL;
+
+ ZERO_STRUCT(response);
+
+ if (winbindd_request(WINBINDD_LIST_GROUPS, NULL, &response)
+ != NSS_STATUS_SUCCESS) {
+ PyErr_SetString(winbind_error, "lookup failed");
+ return NULL;
+ }
+
+ result = PyList_New(0);
+
+ if (response.extra_data) {
+ const char *extra_data = response.extra_data;
+ fstring name;
+
+ while (next_token(&extra_data, name, ",", sizeof(fstring)))
+ PyList_Append(result, PyString_FromString(name));
+ }
+
+ return result;
+}
+
+/*
+ * Miscellaneous domain related
+ */
+
+/* Enumerate domain groups */
+
+static PyObject *py_enum_trust_dom(PyObject *self, PyObject *args)
+{
+ struct winbindd_response response;
+ PyObject *result = NULL;
+
+ if (!PyArg_ParseTuple(args, ""))
+ return NULL;
+
+ ZERO_STRUCT(response);
+
+ if (winbindd_request(WINBINDD_LIST_TRUSTDOM, NULL, &response)
+ != NSS_STATUS_SUCCESS) {
+ PyErr_SetString(winbind_error, "lookup failed");
+ return NULL;
+ }
+
+ result = PyList_New(0);
+
+ if (response.extra_data) {
+ const char *extra_data = response.extra_data;
+ fstring name;
+
+ while (next_token(&extra_data, name, ",", sizeof(fstring)))
+ PyList_Append(result, PyString_FromString(name));
+ }
+
+ return result;
+}
+
+/* Check machine account password */
+
+static PyObject *py_check_secret(PyObject *self, PyObject *args)
+{
+ struct winbindd_response response;
+
+ if (!PyArg_ParseTuple(args, ""))
+ return NULL;
+
+ ZERO_STRUCT(response);
+
+ if (winbindd_request(WINBINDD_CHECK_MACHACC, NULL, &response)
+ != NSS_STATUS_SUCCESS) {
+ PyErr_SetString(winbind_error, "lookup failed");
+ return NULL;
+ }
+
+ return PyInt_FromLong(response.data.num_entries);
+}
+
+/*
+ * Return a dictionary consisting of all the winbind related smb.conf
+ * parameters. This is stored in the module object.
+ */
+
+static PyObject *py_config_dict(void)
+{
+ PyObject *result;
+ uid_t ulow, uhi;
+ gid_t glow, ghi;
+
+ if (!(result = PyDict_New()))
+ return NULL;
+
+ /* Various string parameters */
+
+ PyDict_SetItemString(result, "workgroup",
+ PyString_FromString(lp_workgroup()));
+
+ PyDict_SetItemString(result, "separator",
+ PyString_FromString(lp_winbind_separator()));
+
+ PyDict_SetItemString(result, "template_homedir",
+ PyString_FromString(lp_template_homedir()));
+
+ PyDict_SetItemString(result, "template_shell",
+ PyString_FromString(lp_template_shell()));
+
+ /* idmap uid/gid range */
+
+ if (lp_idmap_uid(&ulow, &uhi)) {
+ PyDict_SetItemString(result, "uid_low", PyInt_FromLong(ulow));
+ PyDict_SetItemString(result, "uid_high", PyInt_FromLong(uhi));
+ }
+
+ if (lp_idmap_gid(&glow, &ghi)) {
+ PyDict_SetItemString(result, "gid_low", PyInt_FromLong(glow));
+ PyDict_SetItemString(result, "gid_high", PyInt_FromLong(ghi));
+ }
+
+ return result;
+}
+
+/*
+ * ID mapping
+ */
+
+/* Convert a uid to a SID */
+
+static PyObject *py_uid_to_sid(PyObject *self, PyObject *args)
+{
+ struct winbindd_request request;
+ struct winbindd_response response;
+ int id;
+
+ if (!PyArg_ParseTuple(args, "i", &id))
+ return NULL;
+
+ ZERO_STRUCT(request);
+ ZERO_STRUCT(response);
+
+ request.data.uid = id;
+
+ if (winbindd_request(WINBINDD_UID_TO_SID, &request, &response)
+ != NSS_STATUS_SUCCESS) {
+ PyErr_SetString(winbind_error, "lookup failed");
+ return NULL;
+ }
+
+ return PyString_FromString(response.data.sid.sid);
+}
+
+/* Convert a gid to a SID */
+
+static PyObject *py_gid_to_sid(PyObject *self, PyObject *args)
+{
+ struct winbindd_request request;
+ struct winbindd_response response;
+ int id;
+
+ if (!PyArg_ParseTuple(args, "i", &id))
+ return NULL;
+
+ ZERO_STRUCT(request);
+ ZERO_STRUCT(response);
+
+ request.data.gid = id;
+
+ if (winbindd_request(WINBINDD_GID_TO_SID, &request, &response)
+ != NSS_STATUS_SUCCESS) {
+ PyErr_SetString(winbind_error, "lookup failed");
+ return NULL;
+ }
+
+ return PyString_FromString(response.data.sid.sid);
+}
+
+/* Convert a sid to a uid */
+
+static PyObject *py_sid_to_uid(PyObject *self, PyObject *args)
+{
+ struct winbindd_request request;
+ struct winbindd_response response;
+ char *sid;
+
+ if (!PyArg_ParseTuple(args, "s", &sid))
+ return NULL;
+
+ ZERO_STRUCT(request);
+ ZERO_STRUCT(response);
+
+ fstrcpy(request.data.sid, sid);
+
+ if (winbindd_request(WINBINDD_SID_TO_UID, &request, &response)
+ != NSS_STATUS_SUCCESS) {
+ PyErr_SetString(winbind_error, "lookup failed");
+ return NULL;
+ }
+
+ return PyInt_FromLong(response.data.uid);
+}
+
+/* Convert a sid to a gid */
+
+static PyObject *py_sid_to_gid(PyObject *self, PyObject *args)
+{
+ struct winbindd_request request;
+ struct winbindd_response response;
+ char *sid;
+
+ if (!PyArg_ParseTuple(args, "s", &sid))
+ return NULL;
+
+ ZERO_STRUCT(request);
+ ZERO_STRUCT(response);
+
+ fstrcpy(request.data.sid, sid);
+
+ if (winbindd_request(WINBINDD_SID_TO_GID, &request, &response)
+ != NSS_STATUS_SUCCESS) {
+ PyErr_SetString(winbind_error, "lookup failed");
+ return NULL;
+ }
+
+ return PyInt_FromLong(response.data.gid);
+}
+
+/*
+ * PAM authentication functions
+ */
+
+/* Plaintext authentication */
+
+static PyObject *py_auth_plaintext(PyObject *self, PyObject *args)
+{
+ struct winbindd_request request;
+ struct winbindd_response response;
+ char *username, *password;
+
+ if (!PyArg_ParseTuple(args, "ss", &username, &password))
+ return NULL;
+
+ ZERO_STRUCT(request);
+ ZERO_STRUCT(response);
+
+ fstrcpy(request.data.auth.user, username);
+ fstrcpy(request.data.auth.pass, password);
+
+ if (winbindd_request(WINBINDD_PAM_AUTH, &request, &response)
+ != NSS_STATUS_SUCCESS) {
+ PyErr_SetString(winbind_error, "lookup failed");
+ return NULL;
+ }
+
+ return PyInt_FromLong(response.data.auth.nt_status);
+}
+
+/* Challenge/response authentication */
+
+static PyObject *py_auth_crap(PyObject *self, PyObject *args, PyObject *kw)
+{
+ static char *kwlist[] =
+ {"username", "password", "use_lm_hash", "use_nt_hash", NULL };
+ struct winbindd_request request;
+ struct winbindd_response response;
+ char *username, *password;
+ int use_lm_hash = 1, use_nt_hash = 1;
+
+ if (!PyArg_ParseTupleAndKeywords(
+ args, kw, "ss|ii", kwlist, &username, &password,
+ &use_lm_hash, &use_nt_hash))
+ return NULL;
+
+ ZERO_STRUCT(request);
+ ZERO_STRUCT(response);
+
+ if (push_utf8_fstring(request.data.auth_crap.user, username) == -1) {
+ PyErr_SetString(winbind_error, "unable to create utf8 string");
+ return NULL;
+ }
+
+ generate_random_buffer(request.data.auth_crap.chal, 8, False);
+
+ if (use_lm_hash) {
+ SMBencrypt((uchar *)password, request.data.auth_crap.chal,
+ (uchar *)request.data.auth_crap.lm_resp);
+ request.data.auth_crap.lm_resp_len = 24;
+ }
+
+ if (use_nt_hash) {
+ SMBNTencrypt((uchar *)password, request.data.auth_crap.chal,
+ (uchar *)request.data.auth_crap.nt_resp);
+ request.data.auth_crap.nt_resp_len = 24;
+ }
+
+ if (winbindd_request(WINBINDD_PAM_AUTH_CRAP, &request, &response)
+ != NSS_STATUS_SUCCESS) {
+ PyErr_SetString(winbind_error, "lookup failed");
+ return NULL;
+ }
+
+ return PyInt_FromLong(response.data.auth.nt_status);
+}
+
+#if 0 /* Include when auth_smbd merged to HEAD */
+
+/* Challenge/response authentication, with secret */
+
+static PyObject *py_auth_smbd(PyObject *self, PyObject *args, PyObject *kw)
+{
+ static char *kwlist[] =
+ {"username", "password", "use_lm_hash", "use_nt_hash", NULL };
+ struct winbindd_request request;
+ struct winbindd_response response;
+ char *username, *password;
+ int use_lm_hash = 1, use_nt_hash = 1;
+
+ if (!PyArg_ParseTupleAndKeywords(
+ args, kw, "ss|ii", kwlist, &username, &password,
+ &use_lm_hash, &use_nt_hash))
+ return NULL;
+
+ ZERO_STRUCT(request);
+ ZERO_STRUCT(response);
+
+ if (push_utf8_fstring(request.data.auth_crap.user, username) == -1) {
+ PyErr_SetString("unable to create utf8 string");
+ return NULL;
+ }
+
+ generate_random_buffer(request.data.smbd_auth_crap.chal, 8, False);
+
+ if (use_lm_hash) {
+ SMBencrypt((uchar *)password,
+ request.data.smbd_auth_crap.chal,
+ (uchar *)request.data.smbd_auth_crap.lm_resp);
+ request.data.smbd_auth_crap.lm_resp_len = 24;
+ }
+
+ if (use_nt_hash) {
+ SMBNTencrypt((uchar *)password,
+ request.data.smbd_auth_crap.chal,
+ (uchar *)request.data.smbd_auth_crap.nt_resp);
+ request.data.smbd_auth_crap.nt_resp_len = 24;
+ }
+
+ if (!secrets_fetch_trust_account_password(
+ lp_workgroup(), request.data.smbd_auth_crap.proof, NULL)) {
+ PyErr_SetString(
+ winbind_error, "unable to fetch domain secret");
+ return NULL;
+ }
+
+
+
+ if (winbindd_request(WINBINDD_SMBD_AUTH_CRAP, &request, &response)
+ != NSS_STATUS_SUCCESS) {
+ PyErr_SetString(winbind_error, "lookup failed");
+ return NULL;
+ }
+
+ return PyInt_FromLong(response.data.auth.nt_status);
+}
+
+#endif /* 0 */
+
+/* Get user info from name */
+
+static PyObject *py_getpwnam(PyObject *self, PyObject *args)
+{
+ struct winbindd_request request;
+ struct winbindd_response response;
+ char *username;
+ PyObject *result;
+
+ if (!PyArg_ParseTuple(args, "s", &username))
+ return NULL;
+
+ ZERO_STRUCT(request);
+ ZERO_STRUCT(response);
+
+ fstrcpy(request.data.username, username);
+
+ if (winbindd_request(WINBINDD_GETPWNAM, &request, &response)
+ != NSS_STATUS_SUCCESS) {
+ PyErr_SetString(winbind_error, "lookup failed");
+ return NULL;
+ }
+
+ if (!py_from_winbind_passwd(&result, &response)) {
+ result = Py_None;
+ Py_INCREF(result);
+ }
+
+ return result;
+}
+
+/* Get user info from uid */
+
+static PyObject *py_getpwuid(PyObject *self, PyObject *args)
+{
+ struct winbindd_request request;
+ struct winbindd_response response;
+ uid_t uid;
+ PyObject *result;
+
+ if (!PyArg_ParseTuple(args, "i", &uid))
+ return NULL;
+
+ ZERO_STRUCT(request);
+ ZERO_STRUCT(response);
+
+ request.data.uid = uid;
+
+ if (winbindd_request(WINBINDD_GETPWUID, &request, &response)
+ != NSS_STATUS_SUCCESS) {
+ PyErr_SetString(winbind_error, "lookup failed");
+ return NULL;
+ }
+
+ if (!py_from_winbind_passwd(&result, &response)) {
+ result = Py_None;
+ Py_INCREF(result);
+ }
+
+ return result;
+}
+
+/*
+ * Method dispatch table
+ */
+
+static PyMethodDef winbind_methods[] = {
+
+ { "getpwnam", (PyCFunction)py_getpwnam, METH_VARARGS, "getpwnam(3)" },
+ { "getpwuid", (PyCFunction)py_getpwuid, METH_VARARGS, "getpwuid(3)" },
+
+ /* Name <-> SID conversion */
+
+ { "name_to_sid", (PyCFunction)py_name_to_sid, METH_VARARGS,
+ "name_to_sid(s) -> string\n"
+"\n"
+"Return the SID for a name.\n"
+"\n"
+"Example:\n"
+"\n"
+">>> winbind.name_to_sid('FOO/Administrator')\n"
+"'S-1-5-21-406022937-1377575209-526660263-500' " },
+
+ { "sid_to_name", (PyCFunction)py_sid_to_name, METH_VARARGS,
+ "sid_to_name(s) -> string\n"
+"\n"
+"Return the name for a SID.\n"
+"\n"
+"Example:\n"
+"\n"
+">>> import winbind\n"
+">>> winbind.sid_to_name('S-1-5-21-406022937-1377575209-526660263-500')\n"
+"'FOO/Administrator' " },
+
+ /* Enumerate users/groups */
+
+ { "enum_domain_users", (PyCFunction)py_enum_domain_users, METH_VARARGS,
+ "enum_domain_users() -> list of strings\n"
+"\n"
+"Return a list of domain users.\n"
+"\n"
+"Example:\n"
+"\n"
+">>> winbind.enum_domain_users()\n"
+"['FOO/Administrator', 'FOO/anna', 'FOO/Anne Elk', 'FOO/build', \n"
+"'FOO/foo', 'FOO/foo2', 'FOO/foo3', 'FOO/Guest', 'FOO/user1', \n"
+"'FOO/whoops-ptang'] " },
+
+ { "enum_domain_groups", (PyCFunction)py_enum_domain_groups,
+ METH_VARARGS,
+ "enum_domain_groups() -> list of strings\n"
+"\n"
+"Return a list of domain groups.\n"
+"\n"
+"Example:\n"
+"\n"
+">>> winbind.enum_domain_groups()\n"
+"['FOO/cows', 'FOO/Domain Admins', 'FOO/Domain Guests', \n"
+"'FOO/Domain Users'] " },
+
+ /* ID mapping */
+
+ { "uid_to_sid", (PyCFunction)py_uid_to_sid, METH_VARARGS,
+ "uid_to_sid(int) -> string\n"
+"\n"
+"Return the SID for a UNIX uid.\n"
+"\n"
+"Example:\n"
+"\n"
+">>> winbind.uid_to_sid(10000) \n"
+"'S-1-5-21-406022937-1377575209-526660263-500' " },
+
+ { "gid_to_sid", (PyCFunction)py_gid_to_sid, METH_VARARGS,
+ "gid_to_sid(int) -> string\n"
+"\n"
+"Return the UNIX gid for a SID.\n"
+"\n"
+"Example:\n"
+"\n"
+">>> winbind.gid_to_sid(10001)\n"
+"'S-1-5-21-406022937-1377575209-526660263-512' " },
+
+ { "sid_to_uid", (PyCFunction)py_sid_to_uid, METH_VARARGS,
+ "sid_to_uid(string) -> int\n"
+"\n"
+"Return the UNIX uid for a SID.\n"
+"\n"
+"Example:\n"
+"\n"
+">>> winbind.sid_to_uid('S-1-5-21-406022937-1377575209-526660263-500')\n"
+"10000 " },
+
+ { "sid_to_gid", (PyCFunction)py_sid_to_gid, METH_VARARGS,
+ "sid_to_gid(string) -> int\n"
+"\n"
+"Return the UNIX gid corresponding to a SID.\n"
+"\n"
+"Example:\n"
+"\n"
+">>> winbind.sid_to_gid('S-1-5-21-406022937-1377575209-526660263-512')\n"
+"10001 " },
+
+ /* Miscellaneous */
+
+ { "check_secret", (PyCFunction)py_check_secret, METH_VARARGS,
+ "check_secret() -> int\n"
+"\n"
+"Check the machine trust account password. The NT status is returned\n"
+"with zero indicating success. " },
+
+ { "enum_trust_dom", (PyCFunction)py_enum_trust_dom, METH_VARARGS,
+ "enum_trust_dom() -> list of strings\n"
+"\n"
+"Return a list of trusted domains. The domain the server is a member \n"
+"of is not included.\n"
+"\n"
+"Example:\n"
+"\n"
+">>> winbind.enum_trust_dom()\n"
+"['NPSD-TEST2', 'SP2NDOM'] " },
+
+ /* PAM authorisation functions */
+
+ { "auth_plaintext", (PyCFunction)py_auth_plaintext, METH_VARARGS,
+ "auth_plaintext(s, s) -> int\n"
+"\n"
+"Authenticate a username and password using plaintext authentication.\n"
+"The NT status code is returned with zero indicating success." },
+
+ { "auth_crap", (PyCFunction)py_auth_crap, METH_VARARGS,
+ "auth_crap(s, s) -> int\n"
+"\n"
+"Authenticate a username and password using the challenge/response\n"
+"protocol. The NT status code is returned with zero indicating\n"
+"success." },
+
+#if 0 /* Include when smbd_auth merged to HEAD */
+
+ { "auth_smbd", (PyCFunction)py_auth_crap, METH_VARARGS,
+ "auth_smbd(s, s) -> int\n"
+"\n"
+"Authenticate a username and password using the challenge/response\n"
+"protocol but using the domain secret to prove we are root. The NT \n"
+"status code is returned with zero indicating success." },
+
+#endif
+
+ { NULL }
+};
+
+static struct const_vals {
+ char *name;
+ uint32 value;
+ char *docstring;
+} module_const_vals[] = {
+
+ /* Well known RIDs */
+
+ { "DOMAIN_USER_RID_ADMIN", DOMAIN_USER_RID_ADMIN,
+ "Well-known RID for Administrator user" },
+
+ { "DOMAIN_USER_RID_GUEST", DOMAIN_USER_RID_GUEST,
+ "Well-known RID for Guest user" },
+
+ { "DOMAIN_GROUP_RID_ADMINS", DOMAIN_GROUP_RID_ADMINS,
+ "Well-known RID for Domain Admins group" },
+
+ { "DOMAIN_GROUP_RID_USERS", DOMAIN_GROUP_RID_USERS,
+ "Well-known RID for Domain Users group" },
+
+ { "DOMAIN_GROUP_RID_GUESTS", DOMAIN_GROUP_RID_GUESTS,
+ "Well-known RID for Domain Guests group" },
+
+ { NULL }
+};
+
+static void const_init(PyObject *dict)
+{
+ struct const_vals *tmp;
+ PyObject *obj;
+
+ for (tmp = module_const_vals; tmp->name; tmp++) {
+ obj = PyInt_FromLong(tmp->value);
+ PyDict_SetItemString(dict, tmp->name, obj);
+ Py_DECREF(obj);
+ }
+}
+
+/*
+ * Module initialisation
+ */
+
+static char winbind_module__doc__[] =
+"A python extension to winbind client functions.";
+
+void initwinbind(void)
+{
+ PyObject *module, *dict;
+
+ /* Initialise module */
+
+ module = Py_InitModule3("winbind", winbind_methods,
+ winbind_module__doc__);
+
+ dict = PyModule_GetDict(module);
+
+ winbind_error = PyErr_NewException("winbind.error", NULL, NULL);
+ PyDict_SetItemString(dict, "error", winbind_error);
+
+ /* Do samba initialisation */
+
+ py_samba_init();
+
+ /* Initialise constants */
+
+ const_init(dict);
+
+ /* Insert configuration dictionary */
+
+ PyDict_SetItemString(dict, "config", py_config_dict());
+}
diff --git a/source/python/py_winbind.h b/source/python/py_winbind.h
new file mode 100644
index 00000000000..10927ea6c8f
--- /dev/null
+++ b/source/python/py_winbind.h
@@ -0,0 +1,30 @@
+/*
+ Python wrappers for DCERPC/SMB client routines.
+
+ Copyright (C) Tim Potter, 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 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 _PY_WINBIND_H
+#define _PY_WINBIND_H
+
+#include "python/py_common.h"
+
+/* The following definitions are from py_winbind_conv.c */
+
+BOOL py_from_winbind_passwd(PyObject **dict, struct winbindd_response *response);
+
+#endif /* _PY_WINBIND_H */
diff --git a/source/python/py_winbind_conv.c b/source/python/py_winbind_conv.c
new file mode 100644
index 00000000000..6e2eab59414
--- /dev/null
+++ b/source/python/py_winbind_conv.c
@@ -0,0 +1,41 @@
+/*
+ Python wrappers for DCERPC/SMB client routines.
+
+ Copyright (C) Tim Potter, 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 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 "python/py_common.h"
+#include "python/py_conv.h"
+
+/* Convert a struct passwd to a dictionary */
+
+static struct pyconv py_passwd[] = {
+ { "pw_name", PY_STRING, offsetof(struct winbindd_response, data.pw.pw_name) },
+ { "pw_passwd", PY_STRING, offsetof(struct winbindd_response, data.pw.pw_passwd) },
+ { "pw_uid", PY_UID, offsetof(struct winbindd_response, data.pw.pw_uid) },
+ { "pw_guid", PY_GID, offsetof(struct winbindd_response, data.pw.pw_gid) },
+ { "pw_gecos", PY_STRING, offsetof(struct winbindd_response, data.pw.pw_gecos) },
+ { "pw_dir", PY_STRING, offsetof(struct winbindd_response, data.pw.pw_dir) },
+ { "pw_shell", PY_STRING, offsetof(struct winbindd_response, data.pw.pw_shell) },
+ { NULL}
+};
+
+BOOL py_from_winbind_passwd(PyObject **dict, struct winbindd_response *response)
+{
+ *dict = from_struct(response, py_passwd);
+ return True;
+}
diff --git a/source/python/py_winreg.c b/source/python/py_winreg.c
new file mode 100644
index 00000000000..ce27f5c533b
--- /dev/null
+++ b/source/python/py_winreg.c
@@ -0,0 +1,82 @@
+/*
+ Python wrappers for DCERPC/SMB client routines.
+
+ Copyright (C) Tim Potter, 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 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 "python/py_winreg.h"
+
+static struct const_vals {
+ char *name;
+ uint32 value;
+} module_const_vals[] = {
+
+ /* Registry value types */
+
+ { "REG_NONE", REG_NONE },
+ { "REG_SZ", REG_SZ },
+ { "REG_EXPAND_SZ", REG_EXPAND_SZ },
+ { "REG_BINARY", REG_BINARY },
+ { "REG_DWORD", REG_DWORD },
+ { "REG_DWORD_LE", REG_DWORD_LE },
+ { "REG_DWORD_BE", REG_DWORD_BE },
+ { "REG_LINK", REG_LINK },
+ { "REG_MULTI_SZ", REG_MULTI_SZ },
+ { "REG_RESOURCE_LIST", REG_RESOURCE_LIST },
+ { "REG_FULL_RESOURCE_DESCRIPTOR", REG_FULL_RESOURCE_DESCRIPTOR },
+ { "REG_RESOURCE_REQUIREMENTS_LIST", REG_RESOURCE_REQUIREMENTS_LIST },
+
+ { NULL },
+};
+
+static void const_init(PyObject *dict)
+{
+ struct const_vals *tmp;
+ PyObject *obj;
+
+ for (tmp = module_const_vals; tmp->name; tmp++) {
+ obj = PyInt_FromLong(tmp->value);
+ PyDict_SetItemString(dict, tmp->name, obj);
+ Py_DECREF(obj);
+ }
+}
+
+/*
+ * Module initialisation
+ */
+
+static PyMethodDef winreg_methods[] = {
+ { NULL }
+};
+
+void initwinreg(void)
+{
+ PyObject *module, *dict;
+
+ /* Initialise module */
+
+ module = Py_InitModule("winreg", winreg_methods);
+ dict = PyModule_GetDict(module);
+
+ /* Initialise constants */
+
+ const_init(dict);
+
+ /* Do samba initialisation */
+
+ py_samba_init();
+}
diff --git a/source/python/py_winreg.h b/source/python/py_winreg.h
new file mode 100644
index 00000000000..95d5fc6ea95
--- /dev/null
+++ b/source/python/py_winreg.h
@@ -0,0 +1,26 @@
+/*
+ Python wrappers for DCERPC/SMB client routines.
+
+ Copyright (C) Tim Potter, 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 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 _PY_WINREG_H
+#define _PY_WINREG_H
+
+#include "python/py_common.h"
+
+#endif /* _PY_WINREG_H */
diff --git a/source/python/samba/.cvsignore b/source/python/samba/.cvsignore
new file mode 100644
index 00000000000..0d20b6487c6
--- /dev/null
+++ b/source/python/samba/.cvsignore
@@ -0,0 +1 @@
+*.pyc
diff --git a/source/python/samba/__init__.py b/source/python/samba/__init__.py
new file mode 100644
index 00000000000..c818ca3e044
--- /dev/null
+++ b/source/python/samba/__init__.py
@@ -0,0 +1,7 @@
+"""samba
+
+Various Python modules for interfacing to Samba.
+
+Try using help() to examine their documentation.
+"""
+
diff --git a/source/python/samba/printerdata.py b/source/python/samba/printerdata.py
new file mode 100644
index 00000000000..0b53a3dfb52
--- /dev/null
+++ b/source/python/samba/printerdata.py
@@ -0,0 +1,65 @@
+#!/usr/bin/env python
+
+#
+# A python module that maps printerdata to a dictionary. We define
+# two classes. The printerdata class maps to Get/Set/Enum/DeletePrinterData
+# and the printerdata_ex class maps to Get/Set/Enum/DeletePrinterDataEx
+#
+
+#
+# TODO:
+#
+# - Implement __delitem__
+#
+
+from samba import spoolss
+
+class printerdata:
+ def __init__(self, host, creds = {}, access = 0x02000000):
+ # For read access, use MAXIMUM_ALLOWED_ACCESS = 0x02000000
+ # For write access, use PRINTER_ACCESS_ADMINISTER = 0x00000004
+ self.hnd = spoolss.openprinter(host, creds = creds, access = access)
+
+ def keys(self):
+ return self.hnd.enumprinterdata().keys()
+
+ def __getitem__(self, key):
+ return self.hnd.getprinterdata(key)['data']
+
+ def __setitem__(self, key, value):
+ # Store as REG_BINARY for now
+ self.hnd.setprinterdata({"key": "", "value": key, "type": 3,
+ "data": value})
+
+class printerdata_ex:
+ def __init__(self, host, creds = {}, access = 0x02000000):
+ # For read access, use MAXIMUM_ALLOWED_ACCESS = 0x02000000
+ # For write access, use PRINTER_ACCESS_ADMINISTER = 0x00000004
+ self.host = host
+ self.top_level_keys = ["PrinterDriverData", "DsSpooler", "DsDriver",
+ "DsUser"]
+ self.creds = creds
+ self.access = access
+
+ def keys(self):
+ return self.top_level_keys
+
+ def has_key(self, key):
+ for k in self.top_level_keys:
+ if k == key:
+ return 1
+ return 0
+
+ class printerdata_ex_subkey:
+ def __init__(self, host, key, creds, access):
+ self.hnd = spoolss.openprinter(host, creds, access)
+ self.key = key
+
+ def keys(self):
+ return self.hnd.enumprinterdataex(self.key).keys()
+
+ def __getitem__(self, key):
+ return self.hnd.getprinterdataex(self.key, key)['data']
+
+ def __getitem__(self, key):
+ return self.printerdata_ex_subkey(self.host, key, creds, access)
diff --git a/source/python/setup.py b/source/python/setup.py
new file mode 100755
index 00000000000..4a4f6ad3f81
--- /dev/null
+++ b/source/python/setup.py
@@ -0,0 +1,200 @@
+# -*- mode: python -*-
+#
+# Unix SMB/CIFS implementation.
+# Module packaging setup for Samba python extensions
+#
+# Copyright (C) Tim Potter, 2002-2003
+# 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 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.
+#
+
+from distutils.core import setup
+from distutils.extension import Extension
+
+import sys, string, os
+
+# The Makefile passes in environment variable $PYTHON_OBJ as being the
+# list of Samba objects. This kind of goes against the distutils.cmd
+# method of adding setup commands and will also confuse people who are
+# familiar with the python Distutils module.
+
+samba_objs = os.environ.get("PYTHON_OBJS", "")
+
+samba_cflags = os.environ.get("PYTHON_CFLAGS", "")
+
+samba_srcdir = os.environ.get("SRCDIR", "")
+
+# These variables are filled in by configure
+
+samba_libs = os.environ.get("LIBS", "")
+
+obj_list = string.split(samba_objs)
+
+# Unfortunately the samba_libs variable contains both shared libraries
+# and linker flags. The python distutils doesn't like this so we have
+# to split $samba_libs into a flags component and a library component.
+
+libraries = []
+library_dirs = []
+
+for lib in string.split(samba_libs):
+ if lib[0:2] == "-l":
+ libraries.append(lib[2:])
+ continue
+ if lib[0:2] == "-L":
+ library_dirs.append(lib[2:])
+ continue
+ if lib[0:2] == "-W":
+ # Skip linker flags
+ continue
+ print "Unknown entry '%s' in $LIBS variable passed to setup.py" % lib
+ sys.exit(1)
+
+flags_list = string.split(samba_cflags)
+
+# Invoke distutils.setup
+
+setup(
+
+ # Overview information
+
+ name = "Samba Python Extensions",
+ version = "0.1",
+ author = "Tim Potter",
+ author_email = "tpot@samba.org",
+ license = "GPL",
+
+ # Get the "samba" directory of Python source. At the moment this
+ # just contains the __init__ file that makes it work as a
+ # subpackage. This is needed even though everything else is an
+ # extension module.
+ package_dir = {"samba": os.path.join(samba_srcdir, "python", "samba")},
+ packages = ["samba"],
+
+ # Module list
+ ext_package = "samba",
+ ext_modules = [
+
+ # SPOOLSS pipe module
+
+ Extension(name = "spoolss",
+ sources = [samba_srcdir + "python/py_spoolss.c",
+ samba_srcdir + "python/py_common.c",
+ samba_srcdir + "python/py_conv.c",
+ samba_srcdir + "python/py_ntsec.c",
+ samba_srcdir + "python/py_spoolss_common.c",
+ samba_srcdir + "python/py_spoolss_forms.c",
+ samba_srcdir + "python/py_spoolss_forms_conv.c",
+ samba_srcdir + "python/py_spoolss_drivers.c",
+ samba_srcdir + "python/py_spoolss_drivers_conv.c",
+ samba_srcdir + "python/py_spoolss_printers.c",
+ samba_srcdir + "python/py_spoolss_printers_conv.c",
+ samba_srcdir + "python/py_spoolss_printerdata.c",
+ samba_srcdir + "python/py_spoolss_ports.c",
+ samba_srcdir + "python/py_spoolss_ports_conv.c",
+ samba_srcdir + "python/py_spoolss_jobs.c",
+ samba_srcdir + "python/py_spoolss_jobs_conv.c",
+ ],
+ libraries = libraries,
+ library_dirs = ["/usr/kerberos/lib"] + library_dirs,
+ extra_compile_args = flags_list,
+ extra_objects = obj_list),
+
+ # LSA pipe module
+
+ Extension(name = "lsa",
+ sources = [samba_srcdir + "python/py_lsa.c",
+ samba_srcdir + "python/py_common.c",
+ samba_srcdir + "python/py_ntsec.c"],
+ libraries = libraries,
+ library_dirs = ["/usr/kerberos/lib"] + library_dirs,
+ extra_compile_args = flags_list,
+ extra_objects = obj_list),
+
+ # SAMR pipe module
+
+ Extension(name = "samr",
+ sources = [samba_srcdir + "python/py_samr.c",
+ samba_srcdir + "python/py_conv.c",
+ samba_srcdir + "python/py_samr_conv.c",
+ samba_srcdir + "python/py_common.c"],
+ libraries = libraries,
+ library_dirs = ["/usr/kerberos/lib"] + library_dirs,
+ extra_compile_args = flags_list,
+ extra_objects = obj_list),
+
+ # winbind client module
+
+ Extension(name = "winbind",
+ sources = [samba_srcdir + "python/py_winbind.c",
+ samba_srcdir + "python/py_winbind_conv.c",
+ samba_srcdir + "python/py_conv.c",
+ samba_srcdir + "python/py_common.c"],
+ libraries = libraries,
+ library_dirs = ["/usr/kerberos/lib"] + library_dirs,
+ extra_compile_args = flags_list,
+ extra_objects = obj_list),
+
+ # WINREG pipe module
+
+ Extension(name = "winreg",
+ sources = [samba_srcdir + "python/py_winreg.c",
+ samba_srcdir + "python/py_common.c"],
+ libraries = libraries,
+ library_dirs = ["/usr/kerberos/lib"] + library_dirs,
+ extra_compile_args = flags_list,
+ extra_objects = obj_list),
+
+ # SRVSVC pipe module
+
+ Extension(name = "srvsvc",
+ sources = [samba_srcdir + "python/py_srvsvc.c",
+ samba_srcdir + "python/py_conv.c",
+ samba_srcdir + "python/py_srvsvc_conv.c",
+ samba_srcdir + "python/py_common.c"],
+ libraries = libraries,
+ library_dirs = ["/usr/kerberos/lib"] + library_dirs,
+ extra_compile_args = flags_list,
+ extra_objects = obj_list),
+
+ # tdb module
+
+ Extension(name = "tdb",
+ sources = [samba_srcdir + "python/py_tdb.c"],
+ libraries = libraries,
+ library_dirs = ["/usr/kerberos/lib"] + library_dirs,
+ extra_compile_args = flags_list,
+ extra_objects = obj_list),
+
+ # libsmb module
+
+ Extension(name = "smb",
+ sources = [samba_srcdir + "python/py_smb.c",
+ samba_srcdir + "python/py_common.c",
+ samba_srcdir + "python/py_ntsec.c"],
+ libraries = libraries,
+ library_dirs = ["/usr/kerberos/lib"] + library_dirs,
+ extra_compile_args = flags_list,
+ extra_objects = obj_list),
+
+ # tdbpack/unpack extensions. Does not actually link to any Samba
+ # code, although it implements a compatible data format.
+
+ Extension(name = "tdbpack",
+ sources = [os.path.join(samba_srcdir, "python", "py_tdbpack.c")],
+ extra_compile_args = ["-I."])
+ ],
+)
diff --git a/source/registry/.cvsignore b/source/registry/.cvsignore
new file mode 100644
index 00000000000..5f2a5c4cf75
--- /dev/null
+++ b/source/registry/.cvsignore
@@ -0,0 +1,2 @@
+*.po
+*.po32
diff --git a/source/registry/reg_cachehook.c b/source/registry/reg_cachehook.c
new file mode 100644
index 00000000000..547eed392d8
--- /dev/null
+++ b/source/registry/reg_cachehook.c
@@ -0,0 +1,112 @@
+/*
+ * Unix SMB/CIFS implementation.
+ * RPC Pipe client / server routines
+ * Copyright (C) Gerald Carter 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 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.
+ */
+
+/* Implementation of registry hook cache tree */
+
+#include "includes.h"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_RPC_SRV
+
+static SORTED_TREE *cache_tree;
+extern REGISTRY_OPS regdb_ops; /* these are the default */
+static REGISTRY_HOOK default_hook = { KEY_TREE_ROOT, &regdb_ops };
+
+/**********************************************************************
+ Initialize the cache tree
+ *********************************************************************/
+
+BOOL reghook_cache_init( void )
+{
+ cache_tree = sorted_tree_init( &default_hook, NULL, NULL );
+
+ return ( cache_tree == NULL );
+}
+
+/**********************************************************************
+ Add a new REGISTRY_HOOK to the cache. Note that the keyname
+ is not in the exact format that a SORTED_TREE expects.
+ *********************************************************************/
+
+BOOL reghook_cache_add( REGISTRY_HOOK *hook )
+{
+ pstring key;
+
+ if ( !hook )
+ return False;
+
+ pstrcpy( key, "\\");
+ pstrcat( key, hook->keyname );
+
+ pstring_sub( key, "\\", "/" );
+
+ DEBUG(10,("reghook_cache_add: Adding key [%s]\n", key));
+
+ return sorted_tree_add( cache_tree, key, hook );
+}
+
+/**********************************************************************
+ Initialize the cache tree
+ *********************************************************************/
+
+REGISTRY_HOOK* reghook_cache_find( char *keyname )
+{
+ char *key;
+ int len;
+ REGISTRY_HOOK *hook;
+
+ if ( !keyname )
+ return NULL;
+
+ /* prepend the string with a '\' character */
+
+ len = strlen( keyname );
+ if ( !(key = malloc( len + 2 )) ) {
+ DEBUG(0,("reghook_cache_find: malloc failed for string [%s] !?!?!\n",
+ keyname));
+ return NULL;
+ }
+
+ *key = '\\';
+ strncpy( key+1, keyname, len+1);
+
+ /* swap to a form understood by the SORTED_TREE */
+
+ string_sub( key, "\\", "/", 0 );
+
+ DEBUG(10,("reghook_cache_find: Searching for keyname [%s]\n", key));
+
+ hook = sorted_tree_find( cache_tree, key ) ;
+
+ SAFE_FREE( key );
+
+ return hook;
+}
+
+/**********************************************************************
+ Initialize the cache tree
+ *********************************************************************/
+
+void reghook_dump_cache( int debuglevel )
+{
+ DEBUG(debuglevel,("reghook_dump_cache: Starting cache dump now...\n"));
+
+ sorted_tree_print_keys( cache_tree, debuglevel );
+}
diff --git a/source/registry/reg_db.c b/source/registry/reg_db.c
new file mode 100644
index 00000000000..cd5ec18f021
--- /dev/null
+++ b/source/registry/reg_db.c
@@ -0,0 +1,311 @@
+/*
+ * Unix SMB/CIFS implementation.
+ * RPC Pipe client / server routines
+ * Copyright (C) Gerald Carter 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 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.
+ */
+
+/* Implementation of internal registry database functions. */
+
+#include "includes.h"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_RPC_SRV
+
+static TDB_CONTEXT *tdb_reg;
+
+
+/***********************************************************************
+ Open the registry data in the tdb
+ ***********************************************************************/
+
+static BOOL init_registry_data( void )
+{
+ pstring keyname;
+ REGSUBKEY_CTR subkeys;
+
+ ZERO_STRUCTP( &subkeys );
+
+ /* HKEY_LOCAL_MACHINE */
+
+ regsubkey_ctr_init( &subkeys );
+ pstrcpy( keyname, KEY_HKLM );
+ regsubkey_ctr_addkey( &subkeys, "SYSTEM" );
+ if ( !regdb_store_reg_keys( keyname, &subkeys ))
+ return False;
+ regsubkey_ctr_destroy( &subkeys );
+
+ regsubkey_ctr_init( &subkeys );
+ pstrcpy( keyname, KEY_HKLM );
+ pstrcat( keyname, "/SYSTEM" );
+ regsubkey_ctr_addkey( &subkeys, "CurrentControlSet" );
+ if ( !regdb_store_reg_keys( keyname, &subkeys ))
+ return False;
+ regsubkey_ctr_destroy( &subkeys );
+
+ regsubkey_ctr_init( &subkeys );
+ pstrcpy( keyname, KEY_HKLM );
+ pstrcat( keyname, "/SYSTEM/CurrentControlSet" );
+ regsubkey_ctr_addkey( &subkeys, "Control" );
+ regsubkey_ctr_addkey( &subkeys, "Services" );
+ if ( !regdb_store_reg_keys( keyname, &subkeys ))
+ return False;
+ regsubkey_ctr_destroy( &subkeys );
+
+ regsubkey_ctr_init( &subkeys );
+ pstrcpy( keyname, KEY_HKLM );
+ pstrcat( keyname, "/SYSTEM/CurrentControlSet/Control" );
+ regsubkey_ctr_addkey( &subkeys, "Print" );
+ regsubkey_ctr_addkey( &subkeys, "ProductOptions" );
+ if ( !regdb_store_reg_keys( keyname, &subkeys ))
+ return False;
+ regsubkey_ctr_destroy( &subkeys );
+
+ pstrcpy( keyname, KEY_HKLM );
+ pstrcat( keyname, "/SYSTEM/CurrentControlSet/Control/ProductOptions" );
+ if ( !regdb_store_reg_keys( keyname, &subkeys ))
+ return False;
+
+ regsubkey_ctr_init( &subkeys );
+ pstrcpy( keyname, KEY_HKLM );
+ pstrcat( keyname, "/SYSTEM/CurrentControlSet/Services" );
+ regsubkey_ctr_addkey( &subkeys, "Netlogon" );
+ if ( !regdb_store_reg_keys( keyname, &subkeys ))
+ return False;
+ regsubkey_ctr_destroy( &subkeys );
+
+ regsubkey_ctr_init( &subkeys );
+ pstrcpy( keyname, KEY_HKLM );
+ pstrcat( keyname, "/SYSTEM/CurrentControlSet/Services/Netlogon" );
+ regsubkey_ctr_addkey( &subkeys, "Parameters" );
+ if ( !regdb_store_reg_keys( keyname, &subkeys ))
+ return False;
+ regsubkey_ctr_destroy( &subkeys );
+
+ pstrcpy( keyname, KEY_HKLM );
+ pstrcat( keyname, "/SYSTEM/CurrentControlSet/Services/Netlogon/Parameters" );
+ if ( !regdb_store_reg_keys( keyname, &subkeys ))
+ return False;
+
+ /* HKEY_USER */
+
+ pstrcpy( keyname, KEY_HKU );
+ if ( !regdb_store_reg_keys( keyname, &subkeys ) )
+ return False;
+
+ /* HKEY_CLASSES_ROOT*/
+
+ pstrcpy( keyname, KEY_HKCR );
+ if ( !regdb_store_reg_keys( keyname, &subkeys ) )
+ return False;
+
+ return True;
+}
+
+/***********************************************************************
+ Open the registry database
+ ***********************************************************************/
+
+BOOL init_registry_db( void )
+{
+ static pid_t local_pid;
+
+ if (tdb_reg && local_pid == sys_getpid())
+ return True;
+
+ /*
+ * try to open first without creating so we can determine
+ * if we need to init the data in the registry
+ */
+
+ tdb_reg = tdb_open_log(lock_path("registry.tdb"), 0, TDB_DEFAULT, O_RDWR, 0600);
+ if ( !tdb_reg )
+ {
+ tdb_reg = tdb_open_log(lock_path("registry.tdb"), 0, TDB_DEFAULT, O_RDWR|O_CREAT, 0600);
+ if ( !tdb_reg ) {
+ DEBUG(0,("init_registry: Failed to open registry %s (%s)\n",
+ lock_path("registry.tdb"), strerror(errno) ));
+ return False;
+ }
+
+ DEBUG(10,("init_registry: Successfully created registry tdb\n"));
+
+ /* create the registry here */
+ if ( !init_registry_data() ) {
+ DEBUG(0,("init_registry: Failed to initiailize data in registry!\n"));
+ return False;
+ }
+ }
+
+ local_pid = sys_getpid();
+
+ return True;
+}
+
+
+
+/***********************************************************************
+ Add subkey strings to the registry tdb under a defined key
+ fmt is the same format as tdb_pack except this function only supports
+ fstrings
+
+ The full path to the registry key is used as database after the
+ \'s are converted to /'s. Key string is also normalized to UPPER
+ case.
+ ***********************************************************************/
+
+BOOL regdb_store_reg_keys( char *keyname, REGSUBKEY_CTR *ctr )
+{
+ TDB_DATA kbuf, dbuf;
+ char *buffer, *tmpbuf;
+ int i = 0;
+ uint32 len, buflen;
+ BOOL ret = True;
+ uint32 num_subkeys = regsubkey_ctr_numkeys( ctr );
+
+ if ( !keyname )
+ return False;
+
+ strupper_m( keyname );
+
+ /* allocate some initial memory */
+
+ buffer = malloc(sizeof(pstring));
+ buflen = sizeof(pstring);
+ len = 0;
+
+ /* store the number of subkeys */
+
+ len += tdb_pack(buffer+len, buflen-len, "d", num_subkeys );
+
+ /* pack all the strings */
+
+ for (i=0; i<num_subkeys; i++) {
+ len += tdb_pack( buffer+len, buflen-len, "f", regsubkey_ctr_specific_key(ctr, i) );
+ if ( len > buflen ) {
+ /* allocate some extra space */
+ if ((tmpbuf = Realloc( buffer, len*2 )) == NULL) {
+ DEBUG(0,("regdb_store_reg_keys: Failed to realloc memory of size [%d]\n", len*2));
+ ret = False;
+ goto done;
+ }
+ buffer = tmpbuf;
+ buflen = len*2;
+
+ len = tdb_pack( buffer+len, buflen-len, "f", regsubkey_ctr_specific_key(ctr, i) );
+ }
+ }
+
+ /* finally write out the data */
+
+ kbuf.dptr = keyname;
+ kbuf.dsize = strlen(keyname)+1;
+ dbuf.dptr = buffer;
+ dbuf.dsize = len;
+ if ( tdb_store( tdb_reg, kbuf, dbuf, TDB_REPLACE ) == -1) {
+ ret = False;
+ goto done;
+ }
+
+done:
+ SAFE_FREE( buffer );
+
+ return ret;
+}
+
+/***********************************************************************
+ Retrieve an array of strings containing subkeys. Memory should be
+ released by the caller. The subkeys are stored in a catenated string
+ of null terminated character strings
+ ***********************************************************************/
+
+int regdb_fetch_reg_keys( char* key, REGSUBKEY_CTR *ctr )
+{
+ pstring path;
+ uint32 num_items;
+ TDB_DATA dbuf;
+ char *buf;
+ uint32 buflen, len;
+ int i;
+ fstring subkeyname;
+
+ DEBUG(10,("regdb_fetch_reg_keys: Enter key => [%s]\n", key ? key : "NULL"));
+
+ pstrcpy( path, key );
+
+ /* convert to key format */
+ pstring_sub( path, "\\", "/" );
+ strupper_m( path );
+
+ dbuf = tdb_fetch_bystring( tdb_reg, path );
+
+ buf = dbuf.dptr;
+ buflen = dbuf.dsize;
+
+ if ( !buf ) {
+ DEBUG(5,("regdb_fetch_reg_keys: tdb lookup failed to locate key [%s]\n", key));
+ return -1;
+ }
+
+ len = tdb_unpack( buf, buflen, "d", &num_items);
+
+ for (i=0; i<num_items; i++) {
+ len += tdb_unpack( buf+len, buflen-len, "f", subkeyname );
+ regsubkey_ctr_addkey( ctr, subkeyname );
+ }
+
+ SAFE_FREE( dbuf.dptr );
+
+ DEBUG(10,("regdb_fetch_reg_keys: Exit [%d] items\n", num_items));
+
+ return num_items;
+}
+
+
+/***********************************************************************
+ Retrieve an array of strings containing subkeys. Memory should be
+ released by the caller. The subkeys are stored in a catenated string
+ of null terminated character strings
+ ***********************************************************************/
+
+int regdb_fetch_reg_values( char* key, REGVAL_CTR *val )
+{
+ return 0;
+}
+
+/***********************************************************************
+ Stub function since we do not currently support storing registry
+ values in the registry.tdb
+ ***********************************************************************/
+
+BOOL regdb_store_reg_values( char *key, REGVAL_CTR *val )
+{
+ return False;
+}
+
+
+/*
+ * Table of function pointers for default access
+ */
+
+REGISTRY_OPS regdb_ops = {
+ regdb_fetch_reg_keys,
+ regdb_fetch_reg_values,
+ regdb_store_reg_keys,
+ regdb_store_reg_values
+};
+
+
diff --git a/source/registry/reg_frontend.c b/source/registry/reg_frontend.c
new file mode 100644
index 00000000000..a9dfb52f011
--- /dev/null
+++ b/source/registry/reg_frontend.c
@@ -0,0 +1,260 @@
+/*
+ * Unix SMB/CIFS implementation.
+ * RPC Pipe client / server routines
+ * Copyright (C) Gerald Carter 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 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.
+ */
+
+/* Implementation of registry frontend view functions. */
+
+#include "includes.h"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_RPC_SRV
+
+extern REGISTRY_OPS printing_ops;
+extern REGISTRY_OPS regdb_ops; /* these are the default */
+
+/* array of REGISTRY_HOOK's which are read into a tree for easy access */
+
+
+REGISTRY_HOOK reg_hooks[] = {
+ { KEY_PRINTING, &printing_ops },
+ { NULL, NULL }
+};
+
+
+/***********************************************************************
+ Open the registry database and initialize the REGISTRY_HOOK cache
+ ***********************************************************************/
+
+BOOL init_registry( void )
+{
+ int i;
+
+ if ( !init_registry_db() ) {
+ DEBUG(0,("init_registry: failed to initialize the registry tdb!\n"));
+ return False;
+ }
+
+ /* build the cache tree of registry hooks */
+
+ reghook_cache_init();
+
+ for ( i=0; reg_hooks[i].keyname; i++ ) {
+ if ( !reghook_cache_add(&reg_hooks[i]) )
+ return False;
+ }
+
+ if ( DEBUGLEVEL >= 20 )
+ reghook_dump_cache(20);
+
+ return True;
+}
+
+
+
+
+/***********************************************************************
+ High level wrapper function for storing registry subkeys
+ ***********************************************************************/
+
+BOOL store_reg_keys( REGISTRY_KEY *key, REGSUBKEY_CTR *subkeys )
+{
+ if ( key->hook && key->hook->ops && key->hook->ops->store_subkeys_fn )
+ return key->hook->ops->store_subkeys_fn( key->name, subkeys );
+ else
+ return False;
+
+}
+
+/***********************************************************************
+ High level wrapper function for storing registry values
+ ***********************************************************************/
+
+BOOL store_reg_values( REGISTRY_KEY *key, REGVAL_CTR *val )
+{
+ if ( key->hook && key->hook->ops && key->hook->ops->store_values_fn )
+ return key->hook->ops->store_values_fn( key->name, val );
+ else
+ return False;
+}
+
+
+/***********************************************************************
+ High level wrapper function for enumerating registry subkeys
+ Initialize the TALLOC_CTX if necessary
+ ***********************************************************************/
+
+int fetch_reg_keys( REGISTRY_KEY *key, REGSUBKEY_CTR *subkey_ctr )
+{
+ int result = -1;
+
+ if ( key->hook && key->hook->ops && key->hook->ops->subkey_fn )
+ result = key->hook->ops->subkey_fn( key->name, subkey_ctr );
+
+ return result;
+}
+
+/***********************************************************************
+ retreive a specific subkey specified by index. Caller is
+ responsible for freeing memory
+ ***********************************************************************/
+
+BOOL fetch_reg_keys_specific( REGISTRY_KEY *key, char** subkey, uint32 key_index )
+{
+ static REGSUBKEY_CTR ctr;
+ static pstring save_path;
+ static BOOL ctr_init = False;
+ char *s;
+
+ *subkey = NULL;
+
+ /* simple caching for performance; very basic heuristic */
+
+ if ( !ctr_init ) {
+ DEBUG(8,("fetch_reg_keys_specific: Initializing cache of subkeys for [%s]\n", key->name));
+ ZERO_STRUCTP( &ctr );
+ regsubkey_ctr_init( &ctr );
+
+ pstrcpy( save_path, key->name );
+
+ if ( fetch_reg_keys( key, &ctr) == -1 )
+ return False;
+
+ ctr_init = True;
+ }
+ /* clear the cache when key_index == 0 or the path has changed */
+ else if ( !key_index || StrCaseCmp( save_path, key->name) ) {
+
+ DEBUG(8,("fetch_reg_keys_specific: Updating cache of subkeys for [%s]\n", key->name));
+
+ regsubkey_ctr_destroy( &ctr );
+ regsubkey_ctr_init( &ctr );
+
+ pstrcpy( save_path, key->name );
+
+ if ( fetch_reg_keys( key, &ctr) == -1 )
+ return False;
+ }
+
+ if ( !(s = regsubkey_ctr_specific_key( &ctr, key_index )) )
+ return False;
+
+ *subkey = strdup( s );
+
+ return True;
+}
+
+
+/***********************************************************************
+ High level wrapper function for enumerating registry values
+ Initialize the TALLOC_CTX if necessary
+ ***********************************************************************/
+
+int fetch_reg_values( REGISTRY_KEY *key, REGVAL_CTR *val )
+{
+ int result = -1;
+
+ if ( key->hook && key->hook->ops && key->hook->ops->value_fn )
+ result = key->hook->ops->value_fn( key->name, val );
+
+ return result;
+}
+
+
+/***********************************************************************
+ retreive a specific subkey specified by index. Caller is
+ responsible for freeing memory
+ ***********************************************************************/
+
+BOOL fetch_reg_values_specific( REGISTRY_KEY *key, REGISTRY_VALUE **val, uint32 val_index )
+{
+ static REGVAL_CTR ctr;
+ static pstring save_path;
+ static BOOL ctr_init = False;
+ REGISTRY_VALUE *v;
+
+ *val = NULL;
+
+ /* simple caching for performance; very basic heuristic */
+
+ if ( !ctr_init ) {
+ DEBUG(8,("fetch_reg_values_specific: Initializing cache of values for [%s]\n", key->name));
+
+ ZERO_STRUCTP( &ctr );
+ regval_ctr_init( &ctr );
+
+ pstrcpy( save_path, key->name );
+
+ if ( fetch_reg_values( key, &ctr) == -1 )
+ return False;
+
+ ctr_init = True;
+ }
+ /* clear the cache when val_index == 0 or the path has changed */
+ else if ( !val_index || StrCaseCmp(save_path, key->name) ) {
+
+ DEBUG(8,("fetch_reg_values_specific: Updating cache of values for [%s]\n", key->name));
+
+ regval_ctr_destroy( &ctr );
+ regval_ctr_init( &ctr );
+
+ pstrcpy( save_path, key->name );
+
+ if ( fetch_reg_values( key, &ctr) == -1 )
+ return False;
+ }
+
+ if ( !(v = regval_ctr_specific_value( &ctr, val_index )) )
+ return False;
+
+ *val = dup_registry_value( v );
+
+ return True;
+}
+
+/***********************************************************************
+ Utility function for splitting the base path of a registry path off
+ by setting base and new_path to the apprapriate offsets withing the
+ path.
+
+ WARNING!! Does modify the original string!
+ ***********************************************************************/
+
+BOOL reg_split_path( char *path, char **base, char **new_path )
+{
+ char *p;
+
+ *new_path = *base = NULL;
+
+ if ( !path)
+ return False;
+
+ *base = path;
+
+ p = strchr( path, '\\' );
+
+ if ( p ) {
+ *p = '\0';
+ *new_path = p+1;
+ }
+
+ return True;
+}
+
+
+
diff --git a/source/registry/reg_objects.c b/source/registry/reg_objects.c
new file mode 100644
index 00000000000..9cfeb7faa97
--- /dev/null
+++ b/source/registry/reg_objects.c
@@ -0,0 +1,374 @@
+/*
+ * Unix SMB/CIFS implementation.
+ * RPC Pipe client / server routines
+ * Copyright (C) Gerald Carter 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 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.
+ */
+
+/* Implementation of registry frontend view functions. */
+
+#include "includes.h"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_RPC_SRV
+
+
+/***********************************************************************
+ Init the talloc context held by a REGSUBKEY_CTR structure
+ **********************************************************************/
+
+void regsubkey_ctr_init( REGSUBKEY_CTR *ctr )
+{
+ if ( !ctr->ctx )
+ ctr->ctx = talloc_init("regsubkey_ctr_init for ctr %p", ctr);
+}
+
+/***********************************************************************
+ Add a new key to the array
+ **********************************************************************/
+
+int regsubkey_ctr_addkey( REGSUBKEY_CTR *ctr, const char *keyname )
+{
+ uint32 len;
+ char **pp;
+
+ if ( keyname )
+ {
+ len = strlen( keyname );
+
+ /* allocate a space for the char* in the array */
+
+ if ( ctr->subkeys == 0 )
+ ctr->subkeys = talloc( ctr->ctx, sizeof(char*) );
+ else {
+ pp = talloc_realloc( ctr->ctx, ctr->subkeys, sizeof(char*)*(ctr->num_subkeys+1) );
+ if ( pp )
+ ctr->subkeys = pp;
+ }
+
+ /* allocate the string and save it in the array */
+
+ ctr->subkeys[ctr->num_subkeys] = talloc( ctr->ctx, len+1 );
+ strncpy( ctr->subkeys[ctr->num_subkeys], keyname, len+1 );
+ ctr->num_subkeys++;
+ }
+
+ return ctr->num_subkeys;
+}
+
+/***********************************************************************
+ How many keys does the container hold ?
+ **********************************************************************/
+
+int regsubkey_ctr_numkeys( REGSUBKEY_CTR *ctr )
+{
+ return ctr->num_subkeys;
+}
+
+/***********************************************************************
+ Retreive a specific key string
+ **********************************************************************/
+
+char* regsubkey_ctr_specific_key( REGSUBKEY_CTR *ctr, uint32 key_index )
+{
+ if ( ! (key_index < ctr->num_subkeys) )
+ return NULL;
+
+ return ctr->subkeys[key_index];
+}
+
+/***********************************************************************
+ free memory held by a REGSUBKEY_CTR structure
+ **********************************************************************/
+
+void regsubkey_ctr_destroy( REGSUBKEY_CTR *ctr )
+{
+ if ( ctr ) {
+ talloc_destroy( ctr->ctx );
+ ZERO_STRUCTP( ctr );
+ }
+}
+
+
+/*
+ * Utility functions for REGVAL_CTR
+ */
+
+/***********************************************************************
+ Init the talloc context held by a REGSUBKEY_CTR structure
+ **********************************************************************/
+
+void regval_ctr_init( REGVAL_CTR *ctr )
+{
+ if ( !ctr->ctx )
+ ctr->ctx = talloc_init("regval_ctr_init for ctr %p", ctr);
+}
+
+/***********************************************************************
+ How many keys does the container hold ?
+ **********************************************************************/
+
+int regval_ctr_numvals( REGVAL_CTR *ctr )
+{
+ return ctr->num_values;
+}
+
+/***********************************************************************
+ allocate memory for and duplicate a REGISTRY_VALUE.
+ This is malloc'd memory so the caller should free it when done
+ **********************************************************************/
+
+REGISTRY_VALUE* dup_registry_value( REGISTRY_VALUE *val )
+{
+ REGISTRY_VALUE *copy = NULL;
+
+ if ( !val )
+ return NULL;
+
+ if ( !(copy = malloc( sizeof(REGISTRY_VALUE) )) ) {
+ DEBUG(0,("dup_registry_value: malloc() failed!\n"));
+ return NULL;
+ }
+
+ /* copy all the non-pointer initial data */
+
+ memcpy( copy, val, sizeof(REGISTRY_VALUE) );
+ if ( val->data_p )
+ {
+ if ( !(copy->data_p = memdup( val->data_p, val->size )) ) {
+ DEBUG(0,("dup_registry_value: memdup() failed for [%d] bytes!\n",
+ val->size));
+ SAFE_FREE( copy );
+ }
+ }
+
+ return copy;
+}
+
+/**********************************************************************
+ free the memory allocated to a REGISTRY_VALUE
+ *********************************************************************/
+
+void free_registry_value( REGISTRY_VALUE *val )
+{
+ if ( !val )
+ return;
+
+ SAFE_FREE( val->data_p );
+ SAFE_FREE( val );
+
+ return;
+}
+
+/**********************************************************************
+ *********************************************************************/
+
+uint8* regval_data_p( REGISTRY_VALUE *val )
+{
+ return val->data_p;
+}
+
+/**********************************************************************
+ *********************************************************************/
+
+int regval_size( REGISTRY_VALUE *val )
+{
+ return val->size;
+}
+
+/**********************************************************************
+ *********************************************************************/
+
+char* regval_name( REGISTRY_VALUE *val )
+{
+ return val->valuename;
+}
+
+/**********************************************************************
+ *********************************************************************/
+
+uint32 regval_type( REGISTRY_VALUE *val )
+{
+ return val->type;
+}
+
+/***********************************************************************
+ Retreive a pointer to a specific value. Caller shoud dup the structure
+ since this memory may go away with a regval_ctr_destroy()
+ **********************************************************************/
+
+REGISTRY_VALUE* regval_ctr_specific_value( REGVAL_CTR *ctr, uint32 idx )
+{
+ if ( !(idx < ctr->num_values) )
+ return NULL;
+
+ return ctr->values[idx];
+}
+
+/***********************************************************************
+ Retrive the TALLOC_CTX associated with a REGISTRY_VALUE
+ **********************************************************************/
+
+TALLOC_CTX* regval_ctr_getctx( REGVAL_CTR *val )
+{
+ if ( !val )
+ return NULL;
+
+ return val->ctx;
+}
+
+/***********************************************************************
+ Add a new registry value to the array
+ **********************************************************************/
+
+int regval_ctr_addvalue( REGVAL_CTR *ctr, const char *name, uint16 type,
+ const char *data_p, size_t size )
+{
+ REGISTRY_VALUE **ppreg;
+
+ if ( name )
+ {
+ /* allocate a slot in the array of pointers */
+
+ if ( ctr->num_values == 0 )
+ ctr->values = talloc( ctr->ctx, sizeof(REGISTRY_VALUE*) );
+ else {
+ ppreg = talloc_realloc( ctr->ctx, ctr->values, sizeof(REGISTRY_VALUE*)*(ctr->num_values+1) );
+ if ( ppreg )
+ ctr->values = ppreg;
+ }
+
+ /* allocate a new value and store the pointer in the arrya */
+
+ ctr->values[ctr->num_values] = talloc( ctr->ctx, sizeof(REGISTRY_VALUE) );
+
+ /* init the value */
+
+ fstrcpy( ctr->values[ctr->num_values]->valuename, name );
+ ctr->values[ctr->num_values]->type = type;
+ ctr->values[ctr->num_values]->data_p = talloc_memdup( ctr->ctx, data_p, size );
+ ctr->values[ctr->num_values]->size = size;
+ ctr->num_values++;
+ }
+
+ return ctr->num_values;
+}
+
+/***********************************************************************
+ Add a new registry value to the array
+ **********************************************************************/
+
+int regval_ctr_copyvalue( REGVAL_CTR *ctr, REGISTRY_VALUE *val )
+{
+ REGISTRY_VALUE **ppreg;
+
+ if ( val )
+ {
+ /* allocate a slot in the array of pointers */
+
+ if ( ctr->num_values == 0 )
+ ctr->values = talloc( ctr->ctx, sizeof(REGISTRY_VALUE*) );
+ else {
+ ppreg = talloc_realloc( ctr->ctx, ctr->values, sizeof(REGISTRY_VALUE*)*(ctr->num_values+1) );
+ if ( ppreg )
+ ctr->values = ppreg;
+ }
+
+ /* allocate a new value and store the pointer in the arrya */
+
+ ctr->values[ctr->num_values] = talloc( ctr->ctx, sizeof(REGISTRY_VALUE) );
+
+ /* init the value */
+
+ fstrcpy( ctr->values[ctr->num_values]->valuename, val->valuename );
+ ctr->values[ctr->num_values]->type = val->type;
+ ctr->values[ctr->num_values]->data_p = talloc_memdup( ctr->ctx, val->data_p, val->size );
+ ctr->values[ctr->num_values]->size = val->size;
+ ctr->num_values++;
+ }
+
+ return ctr->num_values;
+}
+
+/***********************************************************************
+ Delete a single value from the registry container.
+ No need to free memory since it is talloc'd.
+ **********************************************************************/
+
+int regval_ctr_delvalue( REGVAL_CTR *ctr, const char *name )
+{
+ int i;
+
+ /* search for the value */
+ if (!(ctr->num_values))
+ return 0;
+
+ for ( i=0; i<ctr->num_values; i++ ) {
+ if ( strcmp( ctr->values[i]->valuename, name ) == 0)
+ break;
+ }
+
+ /* just return if we don't find it */
+
+ if ( i == ctr->num_values )
+ return ctr->num_values;
+
+ /* just shift everything down one */
+
+ for ( /* use previous i */; i<(ctr->num_values-1); i++ )
+ memcpy( ctr->values[i], ctr->values[i+1], sizeof(REGISTRY_VALUE) );
+
+ /* paranoia */
+
+ ZERO_STRUCTP( ctr->values[i] );
+
+ ctr->num_values--;
+
+ return ctr->num_values;
+}
+
+/***********************************************************************
+ Delete a single value from the registry container.
+ No need to free memory since it is talloc'd.
+ **********************************************************************/
+
+REGISTRY_VALUE* regval_ctr_getvalue( REGVAL_CTR *ctr, const char *name )
+{
+ int i;
+
+ /* search for the value */
+
+ for ( i=0; i<ctr->num_values; i++ ) {
+ if ( strequal( ctr->values[i]->valuename, name ) )
+ return ctr->values[i];
+ }
+
+ return NULL;
+}
+
+/***********************************************************************
+ free memory held by a REGVAL_CTR structure
+ **********************************************************************/
+
+void regval_ctr_destroy( REGVAL_CTR *ctr )
+{
+ if ( ctr ) {
+ talloc_destroy( ctr->ctx );
+ ZERO_STRUCTP( ctr );
+ }
+}
+
+
diff --git a/source/registry/reg_printing.c b/source/registry/reg_printing.c
new file mode 100644
index 00000000000..e50a5f4d4fd
--- /dev/null
+++ b/source/registry/reg_printing.c
@@ -0,0 +1,826 @@
+/*
+ * Unix SMB/CIFS implementation.
+ * RPC Pipe client / server routines
+ * Copyright (C) Gerald Carter 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 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.
+ */
+
+/* Implementation of registry virtual views for printing information */
+
+#include "includes.h"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_RPC_SRV
+
+#define MAX_TOP_LEVEL_KEYS 3
+
+/* some symbolic indexes into the top_level_keys */
+
+#define KEY_INDEX_ENVIR 0
+#define KEY_INDEX_FORMS 1
+#define KEY_INDEX_PRINTER 2
+
+static const char *top_level_keys[MAX_TOP_LEVEL_KEYS] = {
+ "Environments",
+ "Forms",
+ "Printers"
+};
+
+
+/**********************************************************************
+ It is safe to assume that every registry path passed into on of
+ the exported functions here begins with KEY_PRINTING else
+ these functions would have never been called. This is a small utility
+ function to strip the beginning of the path and make a copy that the
+ caller can modify. Note that the caller is responsible for releasing
+ the memory allocated here.
+ **********************************************************************/
+
+static char* trim_reg_path( char *path )
+{
+ char *p;
+ uint16 key_len = strlen(KEY_PRINTING);
+
+ /*
+ * sanity check...this really should never be True.
+ * It is only here to prevent us from accessing outside
+ * the path buffer in the extreme case.
+ */
+
+ if ( strlen(path) < key_len ) {
+ DEBUG(0,("trim_reg_path: Registry path too short! [%s]\n", path));
+ DEBUG(0,("trim_reg_path: KEY_PRINTING => [%s]!\n", KEY_PRINTING));
+ return NULL;
+ }
+
+
+ p = path + strlen( KEY_PRINTING );
+
+ if ( *p == '\\' )
+ p++;
+
+ if ( *p )
+ return strdup(p);
+ else
+ return NULL;
+}
+
+/**********************************************************************
+ handle enumeration of subkeys below KEY_PRINTING\Environments
+ *********************************************************************/
+
+static int print_subpath_environments( char *key, REGSUBKEY_CTR *subkeys )
+{
+ const char *environments[] = {
+ "Windows 4.0",
+ "Windows NT x86",
+ "Windows NT R4000",
+ "Windows NT Alpha_AXP",
+ "Windows NT PowerPC",
+ NULL };
+ fstring *drivers = NULL;
+ int i, env_index, num_drivers;
+ BOOL valid_env = False;
+ char *base, *new_path;
+ char *keystr;
+ char *key2 = NULL;
+ int num_subkeys = -1;
+
+ DEBUG(10,("print_subpath_environments: key=>[%s]\n", key ? key : "NULL" ));
+
+ /* listed architectures of installed drivers */
+
+ if ( !key )
+ {
+ /* Windows 9x drivers */
+
+ if ( get_ntdrivers( &drivers, environments[0], 0 ) )
+ regsubkey_ctr_addkey( subkeys, environments[0] );
+ SAFE_FREE( drivers );
+
+ /* Windows NT/2k intel drivers */
+
+ if ( get_ntdrivers( &drivers, environments[1], 2 )
+ || get_ntdrivers( &drivers, environments[1], 3 ) )
+ {
+ regsubkey_ctr_addkey( subkeys, environments[1] );
+ }
+ SAFE_FREE( drivers );
+
+ /* Windows NT 4.0; non-intel drivers */
+ for ( i=2; environments[i]; i++ ) {
+ if ( get_ntdrivers( &drivers, environments[i], 2 ) )
+ regsubkey_ctr_addkey( subkeys, environments[i] );
+
+ }
+ SAFE_FREE( drivers );
+
+ num_subkeys = regsubkey_ctr_numkeys( subkeys );
+ goto done;
+ }
+
+ /* we are dealing with a subkey of "Environments */
+
+ key2 = strdup( key );
+ keystr = key2;
+ reg_split_path( keystr, &base, &new_path );
+
+ /* sanity check */
+
+ for ( env_index=0; environments[env_index]; env_index++ ) {
+ if ( StrCaseCmp( environments[env_index], base ) == 0 ) {
+ valid_env = True;
+ break;
+ }
+ }
+
+ if ( !valid_env )
+ return -1;
+
+ /* enumerate driver versions; environment is environments[env_index] */
+
+ if ( !new_path ) {
+ switch ( env_index ) {
+ case 0: /* Win9x */
+ if ( get_ntdrivers( &drivers, environments[0], 0 ) ) {
+ regsubkey_ctr_addkey( subkeys, "0" );
+ SAFE_FREE( drivers );
+ }
+ break;
+ case 1: /* Windows NT/2k - intel */
+ if ( get_ntdrivers( &drivers, environments[1], 2 ) ) {
+ regsubkey_ctr_addkey( subkeys, "2" );
+ SAFE_FREE( drivers );
+ }
+ if ( get_ntdrivers( &drivers, environments[1], 3 ) ) {
+ regsubkey_ctr_addkey( subkeys, "3" );
+ SAFE_FREE( drivers );
+ }
+ break;
+ default: /* Windows NT - nonintel */
+ if ( get_ntdrivers( &drivers, environments[env_index], 2 ) ) {
+ regsubkey_ctr_addkey( subkeys, "2" );
+ SAFE_FREE( drivers );
+ }
+
+ }
+
+ num_subkeys = regsubkey_ctr_numkeys( subkeys );
+ goto done;
+ }
+
+ /* we finally get to enumerate the drivers */
+
+ keystr = new_path;
+ reg_split_path( keystr, &base, &new_path );
+
+ if ( !new_path ) {
+ num_drivers = get_ntdrivers( &drivers, environments[env_index], atoi(base) );
+ for ( i=0; i<num_drivers; i++ )
+ regsubkey_ctr_addkey( subkeys, drivers[i] );
+
+ num_subkeys = regsubkey_ctr_numkeys( subkeys );
+ goto done;
+ }
+
+done:
+ SAFE_FREE( key2 );
+
+ return num_subkeys;
+}
+
+/***********************************************************************
+ simple function to prune a pathname down to the basename of a file
+ **********************************************************************/
+
+static char* dos_basename ( char *path )
+{
+ char *p;
+
+ p = strrchr( path, '\\' );
+ if ( p )
+ p++;
+ else
+ p = path;
+
+ return p;
+}
+
+/**********************************************************************
+ handle enumeration of values below
+ KEY_PRINTING\Environments\<arch>\<version>\<drivername>
+ *********************************************************************/
+
+static int print_subpath_values_environments( char *key, REGVAL_CTR *val )
+{
+ char *keystr;
+ char *key2 = NULL;
+ char *base, *new_path;
+ fstring env;
+ fstring driver;
+ int version;
+ NT_PRINTER_DRIVER_INFO_LEVEL driver_ctr;
+ NT_PRINTER_DRIVER_INFO_LEVEL_3 *info3;
+ WERROR w_result;
+ char *buffer = NULL;
+ char *buffer2 = NULL;
+ int buffer_size = 0;
+ int i, length;
+ char *filename;
+ UNISTR2 data;;
+
+ DEBUG(8,("print_subpath_values_environments: Enter key => [%s]\n", key ? key : "NULL"));
+
+ if ( !key )
+ return 0;
+
+ /*
+ * The only key below KEY_PRINTING\Environments that
+ * posseses values is each specific printer driver
+ * First get the arch, version, & driver name
+ */
+
+ /* env */
+
+ key2 = strdup( key );
+ keystr = key2;
+ reg_split_path( keystr, &base, &new_path );
+ if ( !base || !new_path )
+ return 0;
+ fstrcpy( env, base );
+
+ /* version */
+
+ keystr = new_path;
+ reg_split_path( keystr, &base, &new_path );
+ if ( !base || !new_path )
+ return 0;
+ version = atoi( base );
+
+ /* printer driver name */
+
+ keystr = new_path;
+ reg_split_path( keystr, &base, &new_path );
+ /* new_path should be NULL here since this must be the last key */
+ if ( !base || new_path )
+ return 0;
+ fstrcpy( driver, base );
+
+ w_result = get_a_printer_driver( &driver_ctr, 3, driver, env, version );
+
+ if ( !W_ERROR_IS_OK(w_result) )
+ return -1;
+
+ /* build the values out of the driver information */
+ info3 = driver_ctr.info_3;
+
+ filename = dos_basename( info3->driverpath );
+ init_unistr2( &data, filename, UNI_STR_TERMINATE);
+ regval_ctr_addvalue( val, "Driver", REG_SZ, (char*)data.buffer, data.uni_str_len*sizeof(uint16) );
+
+ filename = dos_basename( info3->configfile );
+ init_unistr2( &data, filename, UNI_STR_TERMINATE);
+ regval_ctr_addvalue( val, "Configuration File", REG_SZ, (char*)data.buffer, data.uni_str_len*sizeof(uint16) );
+
+ filename = dos_basename( info3->datafile );
+ init_unistr2( &data, filename, UNI_STR_TERMINATE);
+ regval_ctr_addvalue( val, "Data File", REG_SZ, (char*)data.buffer, data.uni_str_len*sizeof(uint16) );
+
+ filename = dos_basename( info3->helpfile );
+ init_unistr2( &data, filename, UNI_STR_TERMINATE);
+ regval_ctr_addvalue( val, "Help File", REG_SZ, (char*)data.buffer, data.uni_str_len*sizeof(uint16) );
+
+ init_unistr2( &data, info3->defaultdatatype, UNI_STR_TERMINATE);
+ regval_ctr_addvalue( val, "Data Type", REG_SZ, (char*)data.buffer, data.uni_str_len*sizeof(uint16) );
+
+ regval_ctr_addvalue( val, "Version", REG_DWORD, (char*)&info3->cversion, sizeof(info3->cversion) );
+
+ if ( info3->dependentfiles ) {
+ /* place the list of dependent files in a single
+ character buffer, separating each file name by
+ a NULL */
+
+ for ( i=0; strcmp(info3->dependentfiles[i], ""); i++ ) {
+ /* strip the path to only the file's base name */
+
+ filename = dos_basename( info3->dependentfiles[i] );
+
+ length = strlen(filename);
+
+ buffer2 = Realloc( buffer, buffer_size + (length + 1)*sizeof(uint16) );
+ if ( !buffer2 )
+ break;
+ buffer = buffer2;
+
+ init_unistr2( &data, filename, UNI_STR_TERMINATE);
+ memcpy( buffer+buffer_size, (char*)data.buffer, data.uni_str_len*sizeof(uint16) );
+
+ buffer_size += (length + 1)*sizeof(uint16);
+ }
+
+ /* terminated by double NULL. Add the final one here */
+
+ buffer2 = Realloc( buffer, buffer_size + 2 );
+ if ( !buffer2 ) {
+ SAFE_FREE( buffer );
+ buffer_size = 0;
+ } else {
+ buffer = buffer2;
+ buffer[buffer_size++] = '\0';
+ buffer[buffer_size++] = '\0';
+ }
+ }
+
+ regval_ctr_addvalue( val, "Dependent Files", REG_MULTI_SZ, buffer, buffer_size );
+
+ free_a_printer_driver( driver_ctr, 3 );
+
+ SAFE_FREE( key2 );
+ SAFE_FREE( buffer );
+
+ DEBUG(8,("print_subpath_values_environments: Exit\n"));
+
+ return regval_ctr_numvals( val );
+}
+
+
+/**********************************************************************
+ handle enumeration of subkeys below KEY_PRINTING\Forms
+ Really just a stub function, but left here in case it needs to
+ be expanded later on
+ *********************************************************************/
+
+static int print_subpath_forms( char *key, REGSUBKEY_CTR *subkeys )
+{
+ DEBUG(10,("print_subpath_forms: key=>[%s]\n", key ? key : "NULL" ));
+
+ /* there are no subkeys */
+
+ if ( key )
+ return -1;
+
+ return 0;
+}
+
+/**********************************************************************
+ handle enumeration of values below KEY_PRINTING\Forms
+ *********************************************************************/
+
+static int print_subpath_values_forms( char *key, REGVAL_CTR *val )
+{
+ int num_values = 0;
+ uint32 data[8];
+ int form_index = 1;
+
+ DEBUG(10,("print_values_forms: key=>[%s]\n", key ? key : "NULL" ));
+
+ /* handle ..\Forms\ */
+
+ if ( !key )
+ {
+ nt_forms_struct *forms_list = NULL;
+ nt_forms_struct *form = NULL;
+ int i;
+
+ if ( (num_values = get_ntforms( &forms_list )) == 0 )
+ return 0;
+
+ DEBUG(10,("print_subpath_values_forms: [%d] user defined forms returned\n",
+ num_values));
+
+ /* handle user defined forms */
+
+ for ( i=0; i<num_values; i++ )
+ {
+ form = &forms_list[i];
+
+ data[0] = form->width;
+ data[1] = form->length;
+ data[2] = form->left;
+ data[3] = form->top;
+ data[4] = form->right;
+ data[5] = form->bottom;
+ data[6] = form_index++;
+ data[7] = form->flag;
+
+ regval_ctr_addvalue( val, form->name, REG_BINARY, (char*)data, sizeof(data) );
+
+ }
+
+ SAFE_FREE( forms_list );
+ forms_list = NULL;
+
+ /* handle built-on forms */
+
+ if ( (num_values = get_builtin_ntforms( &forms_list )) == 0 )
+ return 0;
+
+ DEBUG(10,("print_subpath_values_forms: [%d] built-in forms returned\n",
+ num_values));
+
+ for ( i=0; i<num_values; i++ )
+ {
+ form = &forms_list[i];
+
+ data[0] = form->width;
+ data[1] = form->length;
+ data[2] = form->left;
+ data[3] = form->top;
+ data[4] = form->right;
+ data[5] = form->bottom;
+ data[6] = form_index++;
+ data[7] = form->flag;
+
+ regval_ctr_addvalue( val, form->name, REG_BINARY, (char*)data, sizeof(data) );
+ }
+
+ SAFE_FREE( forms_list );
+ }
+
+ return num_values;
+}
+
+/**********************************************************************
+ handle enumeration of subkeys below KEY_PRINTING\Printers
+ *********************************************************************/
+
+static int print_subpath_printers( char *key, REGSUBKEY_CTR *subkeys )
+{
+ int n_services = lp_numservices();
+ int snum;
+ fstring sname;
+ int i;
+ int num_subkeys = 0;
+ char *keystr, *key2 = NULL;
+ char *base, *new_path;
+ NT_PRINTER_INFO_LEVEL *printer = NULL;
+ fstring *subkey_names = NULL;
+
+ DEBUG(10,("print_subpath_printers: key=>[%s]\n", key ? key : "NULL" ));
+
+ if ( !key )
+ {
+ /* enumerate all printers */
+
+ for (snum=0; snum<n_services; snum++) {
+ if ( !(lp_snum_ok(snum) && lp_print_ok(snum) ) )
+ continue;
+
+ fstrcpy( sname, lp_servicename(snum) );
+
+ regsubkey_ctr_addkey( subkeys, sname );
+ }
+
+ num_subkeys = regsubkey_ctr_numkeys( subkeys );
+ goto done;
+ }
+
+ /* get information for a specific printer */
+
+ key2 = strdup( key );
+ keystr = key2;
+ reg_split_path( keystr, &base, &new_path );
+
+ if ( !W_ERROR_IS_OK( get_a_printer(NULL, &printer, 2, base) ) )
+ goto done;
+
+ num_subkeys = get_printer_subkeys( &printer->info_2->data, new_path?new_path:"", &subkey_names );
+
+ for ( i=0; i<num_subkeys; i++ )
+ regsubkey_ctr_addkey( subkeys, subkey_names[i] );
+
+ free_a_printer( &printer, 2 );
+
+ /* no other subkeys below here */
+
+done:
+ SAFE_FREE( key2 );
+ SAFE_FREE( subkey_names );
+
+ return num_subkeys;
+}
+
+/**********************************************************************
+ handle enumeration of values below KEY_PRINTING\Printers
+ *********************************************************************/
+
+static int print_subpath_values_printers( char *key, REGVAL_CTR *val )
+{
+ int num_values = 0;
+ char *keystr, *key2 = NULL;
+ char *base, *new_path;
+ NT_PRINTER_INFO_LEVEL *printer = NULL;
+ NT_PRINTER_INFO_LEVEL_2 *info2;
+ DEVICEMODE *devmode;
+ prs_struct prs;
+ uint32 offset;
+ int snum;
+ fstring printername;
+ NT_PRINTER_DATA *p_data;
+ int i, key_index;
+ UNISTR2 data;
+
+ /*
+ * Theres are tw cases to deal with here
+ * (1) enumeration of printer_info_2 values
+ * (2) enumeration of the PrinterDriverData subney
+ */
+
+ if ( !key ) {
+ /* top level key has no values */
+ goto done;
+ }
+
+ key2 = strdup( key );
+ keystr = key2;
+ reg_split_path( keystr, &base, &new_path );
+
+ fstrcpy( printername, base );
+
+ if ( !new_path )
+ {
+ /* we are dealing with the printer itself */
+
+ if ( !W_ERROR_IS_OK( get_a_printer(NULL, &printer, 2, printername) ) )
+ goto done;
+
+ info2 = printer->info_2;
+
+
+ regval_ctr_addvalue( val, "Attributes", REG_DWORD, (char*)&info2->attributes, sizeof(info2->attributes) );
+ regval_ctr_addvalue( val, "Priority", REG_DWORD, (char*)&info2->priority, sizeof(info2->attributes) );
+ regval_ctr_addvalue( val, "ChangeID", REG_DWORD, (char*)&info2->changeid, sizeof(info2->changeid) );
+ regval_ctr_addvalue( val, "Default Priority", REG_DWORD, (char*)&info2->default_priority, sizeof(info2->default_priority) );
+ regval_ctr_addvalue( val, "Status", REG_DWORD, (char*)&info2->status, sizeof(info2->status) );
+ regval_ctr_addvalue( val, "StartTime", REG_DWORD, (char*)&info2->starttime, sizeof(info2->starttime) );
+ regval_ctr_addvalue( val, "UntilTime", REG_DWORD, (char*)&info2->untiltime, sizeof(info2->untiltime) );
+ regval_ctr_addvalue( val, "cjobs", REG_DWORD, (char*)&info2->cjobs, sizeof(info2->cjobs) );
+ regval_ctr_addvalue( val, "AveragePPM", REG_DWORD, (char*)&info2->averageppm, sizeof(info2->averageppm) );
+
+ init_unistr2( &data, info2->printername, UNI_STR_TERMINATE);
+ regval_ctr_addvalue( val, "Name", REG_SZ, (char*)data.buffer, data.uni_str_len*sizeof(uint16) );
+ init_unistr2( &data, info2->location, UNI_STR_TERMINATE);
+ regval_ctr_addvalue( val, "Location", REG_SZ, (char*)data.buffer, data.uni_str_len*sizeof(uint16) );
+ init_unistr2( &data, info2->comment, UNI_STR_TERMINATE);
+ regval_ctr_addvalue( val, "Comment", REG_SZ, (char*)data.buffer, data.uni_str_len*sizeof(uint16) );
+ init_unistr2( &data, info2->parameters, UNI_STR_TERMINATE);
+ regval_ctr_addvalue( val, "Parameters", REG_SZ, (char*)data.buffer, data.uni_str_len*sizeof(uint16) );
+ init_unistr2( &data, info2->portname, UNI_STR_TERMINATE);
+ regval_ctr_addvalue( val, "Port", REG_SZ, (char*)data.buffer, data.uni_str_len*sizeof(uint16) );
+ init_unistr2( &data, info2->servername, UNI_STR_TERMINATE);
+ regval_ctr_addvalue( val, "Server", REG_SZ, (char*)data.buffer, data.uni_str_len*sizeof(uint16) );
+ init_unistr2( &data, info2->sharename, UNI_STR_TERMINATE);
+ regval_ctr_addvalue( val, "Share", REG_SZ, (char*)data.buffer, data.uni_str_len*sizeof(uint16) );
+ init_unistr2( &data, info2->drivername, UNI_STR_TERMINATE);
+ regval_ctr_addvalue( val, "Driver", REG_SZ, (char*)data.buffer, data.uni_str_len*sizeof(uint16) );
+ init_unistr2( &data, info2->sepfile, UNI_STR_TERMINATE);
+ regval_ctr_addvalue( val, "Separator File", REG_SZ, (char*)data.buffer, data.uni_str_len*sizeof(uint16) );
+ init_unistr2( &data, "winprint", UNI_STR_TERMINATE);
+ regval_ctr_addvalue( val, "Print Processor", REG_SZ, (char*)data.buffer, data.uni_str_len*sizeof(uint16) );
+
+
+ /* use a prs_struct for converting the devmode and security
+ descriptor to REG_BIARY */
+
+ prs_init( &prs, MAX_PDU_FRAG_LEN, regval_ctr_getctx(val), MARSHALL);
+
+ /* stream the device mode */
+
+ snum = lp_servicenumber(info2->sharename);
+ if ( (devmode = construct_dev_mode( snum )) != NULL )
+ {
+ if ( spoolss_io_devmode( "devmode", &prs, 0, devmode ) ) {
+
+ offset = prs_offset( &prs );
+
+ regval_ctr_addvalue( val, "Default Devmode", REG_BINARY, prs_data_p(&prs), offset );
+ }
+
+
+ }
+
+ prs_mem_clear( &prs );
+ prs_set_offset( &prs, 0 );
+
+ if ( info2->secdesc_buf && info2->secdesc_buf->len )
+ {
+ if ( sec_io_desc("sec_desc", &info2->secdesc_buf->sec, &prs, 0 ) ) {
+
+ offset = prs_offset( &prs );
+
+ regval_ctr_addvalue( val, "Security", REG_BINARY, prs_data_p(&prs), offset );
+ }
+ }
+
+ prs_mem_free( &prs );
+
+ num_values = regval_ctr_numvals( val );
+
+ goto done;
+
+ }
+
+ /* now enumerate the key */
+
+ if ( !W_ERROR_IS_OK( get_a_printer(NULL, &printer, 2, printername) ) )
+ goto done;
+
+ /* iterate over all printer data and fill the regval container */
+
+ p_data = &printer->info_2->data;
+ if ( (key_index = lookup_printerkey( p_data, new_path )) == -1 ) {
+ DEBUG(10,("print_subpath_values_printer: Unknown keyname [%s]\n", new_path));
+ goto done;
+ }
+
+ num_values = regval_ctr_numvals( &p_data->keys[key_index].values );
+
+ for ( i=0; i<num_values; i++ )
+ regval_ctr_copyvalue( val, regval_ctr_specific_value(&p_data->keys[key_index].values, i) );
+
+
+done:
+ if ( printer )
+ free_a_printer( &printer, 2 );
+
+ SAFE_FREE( key2 );
+
+ return num_values;
+}
+
+/**********************************************************************
+ Routine to handle enumeration of subkeys and values
+ below KEY_PRINTING (depending on whether or not subkeys/val are
+ valid pointers.
+ *********************************************************************/
+
+static int handle_printing_subpath( char *key, REGSUBKEY_CTR *subkeys, REGVAL_CTR *val )
+{
+ int result = 0;
+ char *p, *base;
+ int i;
+
+ DEBUG(10,("handle_printing_subpath: key=>[%s]\n", key ));
+
+ /*
+ * break off the first part of the path
+ * topmost base **must** be one of the strings
+ * in top_level_keys[]
+ */
+
+ reg_split_path( key, &base, &p);
+
+ for ( i=0; i<MAX_TOP_LEVEL_KEYS; i++ ) {
+ if ( StrCaseCmp( top_level_keys[i], base ) == 0 )
+ break;
+ }
+
+ DEBUG(10,("handle_printing_subpath: base=>[%s], i==[%d]\n", base, i));
+
+ if ( !(i < MAX_TOP_LEVEL_KEYS) )
+ return -1;
+
+ /* Call routine to handle each top level key */
+ switch ( i )
+ {
+ case KEY_INDEX_ENVIR:
+ if ( subkeys )
+ print_subpath_environments( p, subkeys );
+ if ( val )
+ print_subpath_values_environments( p, val );
+ break;
+
+ case KEY_INDEX_FORMS:
+ if ( subkeys )
+ print_subpath_forms( p, subkeys );
+ if ( val )
+ print_subpath_values_forms( p, val );
+ break;
+
+ case KEY_INDEX_PRINTER:
+ if ( subkeys )
+ print_subpath_printers( p, subkeys );
+ if ( val )
+ print_subpath_values_printers( p, val );
+ break;
+
+ /* default case for top level key that has no handler */
+
+ default:
+ break;
+ }
+
+
+
+ return result;
+
+}
+/**********************************************************************
+ Enumerate registry subkey names given a registry path.
+ Caller is responsible for freeing memory to **subkeys
+ *********************************************************************/
+
+int printing_subkey_info( char *key, REGSUBKEY_CTR *subkey_ctr )
+{
+ char *path;
+ BOOL top_level = False;
+ int num_subkeys = 0;
+
+ DEBUG(10,("printing_subkey_info: key=>[%s]\n", key));
+
+ path = trim_reg_path( key );
+
+ /* check to see if we are dealing with the top level key */
+
+ if ( !path )
+ top_level = True;
+
+ if ( top_level ) {
+ for ( num_subkeys=0; num_subkeys<MAX_TOP_LEVEL_KEYS; num_subkeys++ )
+ regsubkey_ctr_addkey( subkey_ctr, top_level_keys[num_subkeys] );
+ }
+ else
+ num_subkeys = handle_printing_subpath( path, subkey_ctr, NULL );
+
+ SAFE_FREE( path );
+
+ return num_subkeys;
+}
+
+/**********************************************************************
+ Enumerate registry values given a registry path.
+ Caller is responsible for freeing memory
+ *********************************************************************/
+
+int printing_value_info( char *key, REGVAL_CTR *val )
+{
+ char *path;
+ BOOL top_level = False;
+ int num_values = 0;
+
+ DEBUG(10,("printing_value_info: key=>[%s]\n", key));
+
+ path = trim_reg_path( key );
+
+ /* check to see if we are dealing with the top level key */
+
+ if ( !path )
+ top_level = True;
+
+ /* fill in values from the getprinterdata_printer_server() */
+ if ( top_level )
+ num_values = 0;
+ else
+ num_values = handle_printing_subpath( path, NULL, val );
+
+
+ return num_values;
+}
+
+/**********************************************************************
+ Stub function which always returns failure since we don't want
+ people storing printing information directly via regostry calls
+ (for now at least)
+ *********************************************************************/
+
+BOOL printing_store_subkey( char *key, REGSUBKEY_CTR *subkeys )
+{
+ return False;
+}
+
+/**********************************************************************
+ Stub function which always returns failure since we don't want
+ people storing printing information directly via regostry calls
+ (for now at least)
+ *********************************************************************/
+
+BOOL printing_store_value( char *key, REGVAL_CTR *val )
+{
+ return False;
+}
+
+/*
+ * Table of function pointers for accessing printing data
+ */
+
+REGISTRY_OPS printing_ops = {
+ printing_subkey_info,
+ printing_value_info,
+ printing_store_subkey,
+ printing_store_value
+};
+
+
diff --git a/source/rpc_client/.cvsignore b/source/rpc_client/.cvsignore
new file mode 100644
index 00000000000..07da2225c72
--- /dev/null
+++ b/source/rpc_client/.cvsignore
@@ -0,0 +1,3 @@
+*.po
+*.po32
+
diff --git a/source/rpc_client/cli_dfs.c b/source/rpc_client/cli_dfs.c
new file mode 100644
index 00000000000..2136b69df07
--- /dev/null
+++ b/source/rpc_client/cli_dfs.c
@@ -0,0 +1,247 @@
+/*
+ Unix SMB/CIFS implementation.
+ RPC pipe client
+ Copyright (C) Tim Potter 2000-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 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"
+
+/* Query DFS support */
+
+NTSTATUS cli_dfs_exist(struct cli_state *cli, TALLOC_CTX *mem_ctx,
+ BOOL *dfs_exists)
+{
+ prs_struct qbuf, rbuf;
+ DFS_Q_DFS_EXIST q;
+ DFS_R_DFS_EXIST r;
+ NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+
+ ZERO_STRUCT(q);
+ ZERO_STRUCT(r);
+
+ /* Initialise parse structures */
+
+ prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL);
+ prs_init(&rbuf, 0, mem_ctx, UNMARSHALL);
+
+ /* Marshall data and send request */
+
+ init_dfs_q_dfs_exist(&q);
+
+ if (!dfs_io_q_dfs_exist("", &q, &qbuf, 0) ||
+ !rpc_api_pipe_req(cli, DFS_EXIST, &qbuf, &rbuf)) {
+ goto done;
+ }
+
+ /* Unmarshall response */
+
+ if (!dfs_io_r_dfs_exist("", &r, &rbuf, 0)) {
+ goto done;
+ }
+
+ /* Return result */
+
+ *dfs_exists = (r.status != 0);
+
+ result = NT_STATUS_OK;
+
+ done:
+ prs_mem_free(&qbuf);
+ prs_mem_free(&rbuf);
+
+ return result;
+}
+
+NTSTATUS cli_dfs_add(struct cli_state *cli, TALLOC_CTX *mem_ctx,
+ const char *entrypath, const char *servername,
+ const char *sharename, const char *comment, uint32 flags)
+{
+ prs_struct qbuf, rbuf;
+ DFS_Q_DFS_ADD q;
+ DFS_R_DFS_ADD r;
+ NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+
+ ZERO_STRUCT(q);
+ ZERO_STRUCT(r);
+
+ /* Initialise parse structures */
+
+ prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL);
+ prs_init(&rbuf, 0, mem_ctx, UNMARSHALL);
+
+ /* Marshall data and send request */
+
+ init_dfs_q_dfs_add(&q, entrypath, servername, sharename, comment,
+ flags);
+
+ if (!dfs_io_q_dfs_add("", &q, &qbuf, 0) ||
+ !rpc_api_pipe_req(cli, DFS_ADD, &qbuf, &rbuf)) {
+ goto done;
+ }
+
+ /* Unmarshall response */
+
+ if (!dfs_io_r_dfs_add("", &r, &rbuf, 0)) {
+ goto done;
+ }
+
+ /* Return result */
+
+ result = werror_to_ntstatus(r.status);
+
+ done:
+ prs_mem_free(&qbuf);
+ prs_mem_free(&rbuf);
+
+ return result;
+}
+
+NTSTATUS cli_dfs_remove(struct cli_state *cli, TALLOC_CTX *mem_ctx,
+ const char *entrypath, const char *servername,
+ const char *sharename)
+{
+ prs_struct qbuf, rbuf;
+ DFS_Q_DFS_REMOVE q;
+ DFS_R_DFS_REMOVE r;
+ NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+
+ ZERO_STRUCT(q);
+ ZERO_STRUCT(r);
+
+ /* Initialise parse structures */
+
+ prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL);
+ prs_init(&rbuf, 0, mem_ctx, UNMARSHALL);
+
+ /* Marshall data and send request */
+
+ init_dfs_q_dfs_remove(&q, entrypath, servername, sharename);
+
+ if (!dfs_io_q_dfs_remove("", &q, &qbuf, 0) ||
+ !rpc_api_pipe_req(cli, DFS_REMOVE, &qbuf, &rbuf)) {
+ goto done;
+ }
+
+ /* Unmarshall response */
+
+ if (!dfs_io_r_dfs_remove("", &r, &rbuf, 0)) {
+ goto done;
+ }
+
+ /* Return result */
+
+ result = werror_to_ntstatus(r.status);
+
+ done:
+ prs_mem_free(&qbuf);
+ prs_mem_free(&rbuf);
+
+ return result;
+}
+
+NTSTATUS cli_dfs_get_info(struct cli_state *cli, TALLOC_CTX *mem_ctx,
+ const char *entrypath, const char *servername,
+ const char *sharename, uint32 info_level,
+ DFS_INFO_CTR *ctr)
+
+{
+ prs_struct qbuf, rbuf;
+ DFS_Q_DFS_GET_INFO q;
+ DFS_R_DFS_GET_INFO r;
+ NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+
+ ZERO_STRUCT(q);
+ ZERO_STRUCT(r);
+
+ /* Initialise parse structures */
+
+ prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL);
+ prs_init(&rbuf, 0, mem_ctx, UNMARSHALL);
+
+ /* Marshall data and send request */
+
+ init_dfs_q_dfs_get_info(&q, entrypath, servername, sharename,
+ info_level);
+
+ if (!dfs_io_q_dfs_get_info("", &q, &qbuf, 0) ||
+ !rpc_api_pipe_req(cli, DFS_GET_INFO, &qbuf, &rbuf)) {
+ goto done;
+ }
+
+ /* Unmarshall response */
+
+ if (!dfs_io_r_dfs_get_info("", &r, &rbuf, 0)) {
+ goto done;
+ }
+
+ /* Return result */
+
+ result = werror_to_ntstatus(r.status);
+ *ctr = r.ctr;
+
+ done:
+ prs_mem_free(&qbuf);
+ prs_mem_free(&rbuf);
+
+ return result;
+}
+
+/* Enumerate dfs shares */
+
+NTSTATUS cli_dfs_enum(struct cli_state *cli, TALLOC_CTX *mem_ctx,
+ uint32 info_level, DFS_INFO_CTR *ctr)
+{
+ prs_struct qbuf, rbuf;
+ DFS_Q_DFS_ENUM q;
+ DFS_R_DFS_ENUM r;
+ NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+
+ ZERO_STRUCT(q);
+ ZERO_STRUCT(r);
+
+ /* Initialise parse structures */
+
+ prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL);
+ prs_init(&rbuf, 0, mem_ctx, UNMARSHALL);
+
+ /* Marshall data and send request */
+
+ init_dfs_q_dfs_enum(&q, info_level, ctr);
+
+ if (!dfs_io_q_dfs_enum("", &q, &qbuf, 0) ||
+ !rpc_api_pipe_req(cli, DFS_ENUM, &qbuf, &rbuf)) {
+ goto done;
+ }
+
+ /* Unmarshall response */
+
+ r.ctr = ctr;
+
+ if (!dfs_io_r_dfs_enum("", &r, &rbuf, 0)) {
+ goto done;
+ }
+
+ /* Return result */
+
+ result = werror_to_ntstatus(r.status);
+
+ done:
+ prs_mem_free(&qbuf);
+ prs_mem_free(&rbuf);
+
+ return result;
+}
diff --git a/source/rpc_client/cli_ds.c b/source/rpc_client/cli_ds.c
new file mode 100644
index 00000000000..09e63a47147
--- /dev/null
+++ b/source/rpc_client/cli_ds.c
@@ -0,0 +1,157 @@
+/*
+ Unix SMB/CIFS implementation.
+ RPC pipe client
+ Copyright (C) Gerald Carter 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 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"
+
+/* implementations of client side DsXXX() functions */
+
+/********************************************************************
+ Get information about the server and directory services
+********************************************************************/
+
+NTSTATUS cli_ds_getprimarydominfo(struct cli_state *cli, TALLOC_CTX *mem_ctx,
+ uint16 level, DS_DOMINFO_CTR *ctr)
+{
+ prs_struct qbuf, rbuf;
+ DS_Q_GETPRIMDOMINFO q;
+ DS_R_GETPRIMDOMINFO r;
+ NTSTATUS result;
+
+ ZERO_STRUCT(q);
+ ZERO_STRUCT(r);
+
+ /* Initialise parse structures */
+
+ prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL);
+ prs_init(&rbuf, 0, mem_ctx, UNMARSHALL);
+
+ q.level = level;
+
+ if (!ds_io_q_getprimdominfo("", &qbuf, 0, &q)
+ || !rpc_api_pipe_req(cli, DS_GETPRIMDOMINFO, &qbuf, &rbuf)) {
+ result = NT_STATUS_UNSUCCESSFUL;
+ goto done;
+ }
+
+ /* Unmarshall response */
+
+ if (!ds_io_r_getprimdominfo("", &rbuf, 0, &r)) {
+ result = NT_STATUS_UNSUCCESSFUL;
+ goto done;
+ }
+
+ /* Return basic info - if we are requesting at info != 1 then
+ there could be trouble. */
+
+ result = r.status;
+
+ if ( r.ptr && ctr ) {
+ ctr->basic = talloc(mem_ctx, sizeof(DSROLE_PRIMARY_DOMAIN_INFO_BASIC));
+ if (!ctr->basic)
+ goto done;
+ memcpy(ctr->basic, r.info.basic, sizeof(DSROLE_PRIMARY_DOMAIN_INFO_BASIC));
+ }
+
+done:
+ prs_mem_free(&qbuf);
+ prs_mem_free(&rbuf);
+
+ return result;
+}
+
+/********************************************************************
+ Enumerate trusted domains in an AD forest
+********************************************************************/
+
+NTSTATUS cli_ds_enum_domain_trusts(struct cli_state *cli, TALLOC_CTX *mem_ctx,
+ const char *server, uint32 flags,
+ struct ds_domain_trust **trusts, uint32 *num_domains)
+{
+ prs_struct qbuf, rbuf;
+ DS_Q_ENUM_DOM_TRUSTS q;
+ DS_R_ENUM_DOM_TRUSTS r;
+ NTSTATUS result;
+
+ ZERO_STRUCT(q);
+ ZERO_STRUCT(r);
+
+ /* Initialise parse structures */
+
+ prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL);
+ prs_init(&rbuf, 0, mem_ctx, UNMARSHALL);
+
+ init_q_ds_enum_domain_trusts( &q, server, flags );
+
+ if (!ds_io_q_enum_domain_trusts("", &qbuf, 0, &q)
+ || !rpc_api_pipe_req(cli, DS_ENUM_DOM_TRUSTS, &qbuf, &rbuf)) {
+ result = NT_STATUS_UNSUCCESSFUL;
+ goto done;
+ }
+
+ /* Unmarshall response */
+
+ if (!ds_io_r_enum_domain_trusts("", &rbuf, 0, &r)) {
+ result = NT_STATUS_UNSUCCESSFUL;
+ goto done;
+ }
+
+ result = r.status;
+
+ if ( NT_STATUS_IS_OK(result) ) {
+ int i;
+
+ *num_domains = r.num_domains;
+ *trusts = (struct ds_domain_trust*)talloc(mem_ctx, r.num_domains*sizeof(**trusts));
+
+ for ( i=0; i< *num_domains; i++ ) {
+ (*trusts)[i].flags = r.domains.trusts[i].flags;
+ (*trusts)[i].parent_index = r.domains.trusts[i].parent_index;
+ (*trusts)[i].trust_type = r.domains.trusts[i].trust_type;
+ (*trusts)[i].trust_attributes = r.domains.trusts[i].trust_attributes;
+ (*trusts)[i].guid = r.domains.trusts[i].guid;
+
+ if (r.domains.trusts[i].sid_ptr) {
+ sid_copy(&(*trusts)[i].sid, &r.domains.trusts[i].sid.sid);
+ } else {
+ ZERO_STRUCT((*trusts)[i].sid);
+ }
+
+ if (r.domains.trusts[i].netbios_ptr) {
+ (*trusts)[i].netbios_domain = unistr2_tdup( mem_ctx, &r.domains.trusts[i].netbios_domain );
+ } else {
+ (*trusts)[i].netbios_domain = NULL;
+ }
+
+ if (r.domains.trusts[i].dns_ptr) {
+ (*trusts)[i].dns_domain = unistr2_tdup( mem_ctx, &r.domains.trusts[i].dns_domain );
+ } else {
+ (*trusts)[i].dns_domain = NULL;
+ }
+ }
+ }
+
+done:
+ prs_mem_free(&qbuf);
+ prs_mem_free(&rbuf);
+
+ return result;
+}
+
+
diff --git a/source/rpc_client/cli_echo.c b/source/rpc_client/cli_echo.c
new file mode 100644
index 00000000000..03a4ab36ee0
--- /dev/null
+++ b/source/rpc_client/cli_echo.c
@@ -0,0 +1,187 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ RPC pipe client
+
+ 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 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"
+
+NTSTATUS cli_echo_add_one(struct cli_state *cli, TALLOC_CTX *mem_ctx,
+ uint32 request, uint32 *response)
+{
+ prs_struct qbuf, rbuf;
+ ECHO_Q_ADD_ONE q;
+ ECHO_R_ADD_ONE r;
+ BOOL result = False;
+
+ ZERO_STRUCT(q);
+ ZERO_STRUCT(r);
+
+ /* Initialise parse structures */
+
+ prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL);
+ prs_init(&rbuf, 0, mem_ctx, UNMARSHALL);
+
+ /* Marshall data and send request */
+
+ init_echo_q_add_one(&q, request);
+
+ if (!echo_io_q_add_one("", &q, &qbuf, 0) ||
+ !rpc_api_pipe_req(cli, ECHO_ADD_ONE, &qbuf, &rbuf))
+ goto done;
+
+ /* Unmarshall response */
+
+ if (!echo_io_r_add_one("", &r, &rbuf, 0))
+ goto done;
+
+ if (response)
+ *response = r.response;
+
+ result = True;
+
+ done:
+ prs_mem_free(&qbuf);
+ prs_mem_free(&rbuf);
+
+ return result ? NT_STATUS_OK : NT_STATUS_UNSUCCESSFUL;
+}
+
+NTSTATUS cli_echo_data(struct cli_state *cli, TALLOC_CTX *mem_ctx,
+ uint32 size, char *in_data, char **out_data)
+{
+ prs_struct qbuf, rbuf;
+ ECHO_Q_ECHO_DATA q;
+ ECHO_R_ECHO_DATA r;
+ BOOL result = False;
+
+ ZERO_STRUCT(q);
+ ZERO_STRUCT(r);
+
+ /* Initialise parse structures */
+
+ prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL);
+ prs_init(&rbuf, 0, mem_ctx, UNMARSHALL);
+
+ /* Marshall data and send request */
+
+ init_echo_q_echo_data(&q, size, in_data);
+
+ if (!echo_io_q_echo_data("", &q, &qbuf, 0) ||
+ !rpc_api_pipe_req(cli, ECHO_DATA, &qbuf, &rbuf))
+ goto done;
+
+ /* Unmarshall response */
+
+ if (!echo_io_r_echo_data("", &r, &rbuf, 0))
+ goto done;
+
+ result = True;
+
+ if (out_data) {
+ *out_data = talloc(mem_ctx, size);
+ memcpy(*out_data, r.data, size);
+ }
+
+ done:
+ prs_mem_free(&qbuf);
+ prs_mem_free(&rbuf);
+
+ return result ? NT_STATUS_OK : NT_STATUS_UNSUCCESSFUL;
+}
+
+NTSTATUS cli_echo_sink_data(struct cli_state *cli, TALLOC_CTX *mem_ctx,
+ uint32 size, char *in_data)
+{
+ prs_struct qbuf, rbuf;
+ ECHO_Q_SINK_DATA q;
+ ECHO_R_SINK_DATA r;
+ BOOL result = False;
+
+ ZERO_STRUCT(q);
+ ZERO_STRUCT(r);
+
+ /* Initialise parse structures */
+
+ prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL);
+ prs_init(&rbuf, 0, mem_ctx, UNMARSHALL);
+
+ /* Marshall data and send request */
+
+ init_echo_q_sink_data(&q, size, in_data);
+
+ if (!echo_io_q_sink_data("", &q, &qbuf, 0) ||
+ !rpc_api_pipe_req(cli, ECHO_SINK_DATA, &qbuf, &rbuf)) {
+ goto done;
+ }
+
+ /* Unmarshall response */
+
+ if (!echo_io_r_sink_data("", &r, &rbuf, 0)) {
+ goto done;
+ }
+
+ result = True;
+
+ done:
+ prs_mem_free(&qbuf);
+ prs_mem_free(&rbuf);
+
+ return result ? NT_STATUS_OK : NT_STATUS_UNSUCCESSFUL;
+}
+
+NTSTATUS cli_echo_source_data(struct cli_state *cli, TALLOC_CTX *mem_ctx,
+ uint32 size, char **out_data)
+{
+ prs_struct qbuf, rbuf;
+ ECHO_Q_SOURCE_DATA q;
+ ECHO_R_SOURCE_DATA r;
+ BOOL result = False;
+
+ ZERO_STRUCT(q);
+ ZERO_STRUCT(r);
+
+ /* Initialise parse structures */
+
+ prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL);
+ prs_init(&rbuf, 0, mem_ctx, UNMARSHALL);
+
+ /* Marshall data and send request */
+
+ init_echo_q_source_data(&q, size);
+
+ if (!echo_io_q_source_data("", &q, &qbuf, 0) ||
+ !rpc_api_pipe_req(cli, ECHO_SOURCE_DATA, &qbuf, &rbuf)) {
+ goto done;
+ }
+
+ /* Unmarshall response */
+
+ if (!echo_io_r_source_data("", &r, &rbuf, 0)) {
+ goto done;
+ }
+
+ result = True;
+
+ done:
+ prs_mem_free(&qbuf);
+ prs_mem_free(&rbuf);
+
+ return result ? NT_STATUS_OK : NT_STATUS_UNSUCCESSFUL;
+}
diff --git a/source/rpc_client/cli_epmapper.c b/source/rpc_client/cli_epmapper.c
new file mode 100644
index 00000000000..66362f16209
--- /dev/null
+++ b/source/rpc_client/cli_epmapper.c
@@ -0,0 +1,61 @@
+/*
+ Unix SMB/CIFS implementation.
+ RPC pipe client
+
+ 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 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"
+
+NTSTATUS cli_epm_map(struct cli_state *cli, TALLOC_CTX *mem_ctx,
+ EPM_HANDLE *handle, EPM_TOWER **tower,
+ EPM_HANDLE *entry_handle, uint32 *num_towers)
+{
+ prs_struct qbuf, rbuf;
+ EPM_Q_MAP q;
+ EPM_R_MAP r;
+ BOOL result = False;
+
+ ZERO_STRUCT(q);
+ ZERO_STRUCT(r);
+
+ /* Initialise parse structures */
+
+ prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL);
+ prs_init(&rbuf, 0, mem_ctx, UNMARSHALL);
+
+ /* Marshall data and send request */
+
+ init_epm_q_map(mem_ctx, &q, *tower, *num_towers);
+
+ if (!epm_io_q_map("map_query", &q, &qbuf, 0) ||
+ !rpc_api_pipe_req(cli, EPM_MAP_PIPE_NAME, &qbuf, &rbuf))
+ goto done;
+
+ /* Unmarshall response */
+
+ if (!epm_io_r_map("map_reply", &r, &rbuf, 0))
+ goto done;
+
+ result = True;
+
+ done:
+ prs_mem_free(&qbuf);
+ prs_mem_free(&rbuf);
+
+ return result ? NT_STATUS_OK : NT_STATUS_UNSUCCESSFUL;
+}
diff --git a/source/rpc_client/cli_lsarpc.c b/source/rpc_client/cli_lsarpc.c
new file mode 100644
index 00000000000..980a681387f
--- /dev/null
+++ b/source/rpc_client/cli_lsarpc.c
@@ -0,0 +1,1466 @@
+/*
+ Unix SMB/CIFS implementation.
+ RPC pipe client
+ Copyright (C) Tim Potter 2000-2001,
+ Copyright (C) Andrew Tridgell 1992-1997,2000,
+ Copyright (C) Luke Kenneth Casson Leighton 1996-1997,2000,
+ Copyright (C) Paul Ashton 1997,2000,
+ Copyright (C) Elrond 2000,
+ 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 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"
+
+/** @defgroup lsa LSA - Local Security Architecture
+ * @ingroup rpc_client
+ *
+ * @{
+ **/
+
+/**
+ * @file cli_lsarpc.c
+ *
+ * RPC client routines for the LSA RPC pipe. LSA means "local
+ * security authority", which is half of a password database.
+ **/
+
+/** Open a LSA policy handle
+ *
+ * @param cli Handle on an initialised SMB connection */
+
+NTSTATUS cli_lsa_open_policy(struct cli_state *cli, TALLOC_CTX *mem_ctx,
+ BOOL sec_qos, uint32 des_access, POLICY_HND *pol)
+{
+ prs_struct qbuf, rbuf;
+ LSA_Q_OPEN_POL q;
+ LSA_R_OPEN_POL r;
+ LSA_SEC_QOS qos;
+ NTSTATUS result;
+
+ ZERO_STRUCT(q);
+ ZERO_STRUCT(r);
+
+ /* Initialise parse structures */
+
+ prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL);
+ prs_init(&rbuf, 0, mem_ctx, UNMARSHALL);
+
+ /* Initialise input parameters */
+
+ if (sec_qos) {
+ init_lsa_sec_qos(&qos, 2, 1, 0);
+ init_q_open_pol(&q, '\\', 0, des_access, &qos);
+ } else {
+ init_q_open_pol(&q, '\\', 0, des_access, NULL);
+ }
+
+ /* Marshall data and send request */
+
+ if (!lsa_io_q_open_pol("", &q, &qbuf, 0) ||
+ !rpc_api_pipe_req(cli, LSA_OPENPOLICY, &qbuf, &rbuf)) {
+ result = NT_STATUS_UNSUCCESSFUL;
+ goto done;
+ }
+
+ /* Unmarshall response */
+
+ if (!lsa_io_r_open_pol("", &r, &rbuf, 0)) {
+ result = NT_STATUS_UNSUCCESSFUL;
+ goto done;
+ }
+
+ /* Return output parameters */
+
+ if (NT_STATUS_IS_OK(result = r.status)) {
+ *pol = r.pol;
+#ifdef __INSURE__
+ pol->marker = malloc(1);
+#endif
+ }
+
+ done:
+ prs_mem_free(&qbuf);
+ prs_mem_free(&rbuf);
+
+ return result;
+}
+
+/** Open a LSA policy handle
+ *
+ * @param cli Handle on an initialised SMB connection
+ */
+
+NTSTATUS cli_lsa_open_policy2(struct cli_state *cli, TALLOC_CTX *mem_ctx,
+ BOOL sec_qos, uint32 des_access, POLICY_HND *pol)
+{
+ prs_struct qbuf, rbuf;
+ LSA_Q_OPEN_POL2 q;
+ LSA_R_OPEN_POL2 r;
+ LSA_SEC_QOS qos;
+ NTSTATUS result;
+
+ ZERO_STRUCT(q);
+ ZERO_STRUCT(r);
+
+ /* Initialise parse structures */
+
+ prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL);
+ prs_init(&rbuf, 0, mem_ctx, UNMARSHALL);
+
+ /* Initialise input parameters */
+
+ if (sec_qos) {
+ init_lsa_sec_qos(&qos, 2, 1, 0);
+ init_q_open_pol2(&q, cli->srv_name_slash, 0, des_access,
+ &qos);
+ } else {
+ init_q_open_pol2(&q, cli->srv_name_slash, 0, des_access,
+ NULL);
+ }
+
+ /* Marshall data and send request */
+
+ if (!lsa_io_q_open_pol2("", &q, &qbuf, 0) ||
+ !rpc_api_pipe_req(cli, LSA_OPENPOLICY2, &qbuf, &rbuf)) {
+ result = NT_STATUS_UNSUCCESSFUL;
+ goto done;
+ }
+
+ /* Unmarshall response */
+
+ if (!lsa_io_r_open_pol2("", &r, &rbuf, 0)) {
+ result = NT_STATUS_UNSUCCESSFUL;
+ goto done;
+ }
+
+ /* Return output parameters */
+
+ if (NT_STATUS_IS_OK(result = r.status)) {
+ *pol = r.pol;
+#ifdef __INSURE__
+ pol->marker = (char *)malloc(1);
+#endif
+ }
+
+ done:
+ prs_mem_free(&qbuf);
+ prs_mem_free(&rbuf);
+
+ return result;
+}
+
+/** Close a LSA policy handle */
+
+NTSTATUS cli_lsa_close(struct cli_state *cli, TALLOC_CTX *mem_ctx,
+ POLICY_HND *pol)
+{
+ prs_struct qbuf, rbuf;
+ LSA_Q_CLOSE q;
+ LSA_R_CLOSE r;
+ NTSTATUS result;
+
+ ZERO_STRUCT(q);
+ ZERO_STRUCT(r);
+
+ /* Initialise parse structures */
+
+ prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL);
+ prs_init(&rbuf, 0, mem_ctx, UNMARSHALL);
+
+ /* Marshall data and send request */
+
+ init_lsa_q_close(&q, pol);
+
+ if (!lsa_io_q_close("", &q, &qbuf, 0) ||
+ !rpc_api_pipe_req(cli, LSA_CLOSE, &qbuf, &rbuf)) {
+ result = NT_STATUS_UNSUCCESSFUL;
+ goto done;
+ }
+
+ /* Unmarshall response */
+
+ if (!lsa_io_r_close("", &r, &rbuf, 0)) {
+ result = NT_STATUS_UNSUCCESSFUL;
+ goto done;
+ }
+
+ /* Return output parameters */
+
+ if (NT_STATUS_IS_OK(result = r.status)) {
+#ifdef __INSURE__
+ SAFE_FREE(pol->marker);
+#endif
+ *pol = r.pol;
+ }
+
+ done:
+ prs_mem_free(&qbuf);
+ prs_mem_free(&rbuf);
+
+ return result;
+}
+
+/** Lookup a list of sids */
+
+NTSTATUS cli_lsa_lookup_sids(struct cli_state *cli, TALLOC_CTX *mem_ctx,
+ POLICY_HND *pol, int num_sids, const DOM_SID *sids,
+ char ***domains, char ***names, uint32 **types)
+{
+ prs_struct qbuf, rbuf;
+ LSA_Q_LOOKUP_SIDS q;
+ LSA_R_LOOKUP_SIDS r;
+ DOM_R_REF ref;
+ LSA_TRANS_NAME_ENUM t_names;
+ NTSTATUS result;
+ int i;
+
+ ZERO_STRUCT(q);
+ ZERO_STRUCT(r);
+
+ /* Initialise parse structures */
+
+ prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL);
+ prs_init(&rbuf, 0, mem_ctx, UNMARSHALL);
+
+ /* Marshall data and send request */
+
+ init_q_lookup_sids(mem_ctx, &q, pol, num_sids, sids, 1);
+
+ if (!lsa_io_q_lookup_sids("", &q, &qbuf, 0) ||
+ !rpc_api_pipe_req(cli, LSA_LOOKUPSIDS, &qbuf, &rbuf)) {
+ result = NT_STATUS_UNSUCCESSFUL;
+ goto done;
+ }
+
+ /* Unmarshall response */
+
+ ZERO_STRUCT(ref);
+ ZERO_STRUCT(t_names);
+
+ r.dom_ref = &ref;
+ r.names = &t_names;
+
+ if (!lsa_io_r_lookup_sids("", &r, &rbuf, 0)) {
+ result = NT_STATUS_UNSUCCESSFUL;
+ goto done;
+ }
+
+ result = r.status;
+
+ if (!NT_STATUS_IS_OK(result) &&
+ NT_STATUS_V(result) != NT_STATUS_V(STATUS_SOME_UNMAPPED)) {
+
+ /* An actual error occured */
+
+ goto done;
+ }
+
+ /* Return output parameters */
+
+ if (r.mapped_count == 0) {
+ result = NT_STATUS_NONE_MAPPED;
+ goto done;
+ }
+
+ if (!((*domains) = (char **)talloc(mem_ctx, sizeof(char *) *
+ num_sids))) {
+ DEBUG(0, ("cli_lsa_lookup_sids(): out of memory\n"));
+ result = NT_STATUS_UNSUCCESSFUL;
+ goto done;
+ }
+
+ if (!((*names) = (char **)talloc(mem_ctx, sizeof(char *) *
+ num_sids))) {
+ DEBUG(0, ("cli_lsa_lookup_sids(): out of memory\n"));
+ result = NT_STATUS_UNSUCCESSFUL;
+ goto done;
+ }
+
+ if (!((*types) = (uint32 *)talloc(mem_ctx, sizeof(uint32) *
+ num_sids))) {
+ DEBUG(0, ("cli_lsa_lookup_sids(): out of memory\n"));
+ result = NT_STATUS_UNSUCCESSFUL;
+ goto done;
+ }
+
+ for (i = 0; i < num_sids; i++) {
+ fstring name, dom_name;
+ uint32 dom_idx = t_names.name[i].domain_idx;
+
+ /* Translate optimised name through domain index array */
+
+ if (dom_idx != 0xffffffff) {
+
+ rpcstr_pull_unistr2_fstring(
+ dom_name, &ref.ref_dom[dom_idx].uni_dom_name);
+ rpcstr_pull_unistr2_fstring(
+ name, &t_names.uni_name[i]);
+
+ (*names)[i] = talloc_strdup(mem_ctx, name);
+ (*domains)[i] = talloc_strdup(mem_ctx, dom_name);
+ (*types)[i] = t_names.name[i].sid_name_use;
+
+ if (((*names)[i] == NULL) || ((*domains)[i] == NULL)) {
+ DEBUG(0, ("cli_lsa_lookup_sids(): out of memory\n"));
+ result = NT_STATUS_UNSUCCESSFUL;
+ goto done;
+ }
+
+ } else {
+ (*names)[i] = NULL;
+ (*domains)[i] = NULL;
+ (*types)[i] = SID_NAME_UNKNOWN;
+ }
+ }
+
+ done:
+ prs_mem_free(&qbuf);
+ prs_mem_free(&rbuf);
+
+ return result;
+}
+
+/** Lookup a list of names */
+
+NTSTATUS cli_lsa_lookup_names(struct cli_state *cli, TALLOC_CTX *mem_ctx,
+ POLICY_HND *pol, int num_names,
+ const char **names, DOM_SID **sids,
+ uint32 **types)
+{
+ prs_struct qbuf, rbuf;
+ LSA_Q_LOOKUP_NAMES q;
+ LSA_R_LOOKUP_NAMES r;
+ DOM_R_REF ref;
+ NTSTATUS result;
+ int i;
+
+ ZERO_STRUCT(q);
+ ZERO_STRUCT(r);
+
+ /* Initialise parse structures */
+
+ prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL);
+ prs_init(&rbuf, 0, mem_ctx, UNMARSHALL);
+
+ /* Marshall data and send request */
+
+ init_q_lookup_names(mem_ctx, &q, pol, num_names, names);
+
+ if (!lsa_io_q_lookup_names("", &q, &qbuf, 0) ||
+ !rpc_api_pipe_req(cli, LSA_LOOKUPNAMES, &qbuf, &rbuf)) {
+ result = NT_STATUS_UNSUCCESSFUL;
+ goto done;
+ }
+
+ /* Unmarshall response */
+
+ ZERO_STRUCT(ref);
+ r.dom_ref = &ref;
+
+ if (!lsa_io_r_lookup_names("", &r, &rbuf, 0)) {
+ result = NT_STATUS_UNSUCCESSFUL;
+ goto done;
+ }
+
+ result = r.status;
+
+ if (!NT_STATUS_IS_OK(result) && NT_STATUS_V(result) !=
+ NT_STATUS_V(STATUS_SOME_UNMAPPED)) {
+
+ /* An actual error occured */
+
+ goto done;
+ }
+
+ /* Return output parameters */
+
+ if (r.mapped_count == 0) {
+ result = NT_STATUS_NONE_MAPPED;
+ goto done;
+ }
+
+ if (!((*sids = (DOM_SID *)talloc(mem_ctx, sizeof(DOM_SID) *
+ num_names)))) {
+ DEBUG(0, ("cli_lsa_lookup_sids(): out of memory\n"));
+ result = NT_STATUS_UNSUCCESSFUL;
+ goto done;
+ }
+
+ if (!((*types = (uint32 *)talloc(mem_ctx, sizeof(uint32) *
+ num_names)))) {
+ DEBUG(0, ("cli_lsa_lookup_sids(): out of memory\n"));
+ result = NT_STATUS_UNSUCCESSFUL;
+ goto done;
+ }
+
+ for (i = 0; i < num_names; i++) {
+ DOM_RID2 *t_rids = r.dom_rid;
+ uint32 dom_idx = t_rids[i].rid_idx;
+ uint32 dom_rid = t_rids[i].rid;
+ DOM_SID *sid = &(*sids)[i];
+
+ /* Translate optimised sid through domain index array */
+
+ if (dom_idx != 0xffffffff) {
+
+ sid_copy(sid, &ref.ref_dom[dom_idx].ref_dom.sid);
+
+ if (dom_rid != 0xffffffff) {
+ sid_append_rid(sid, dom_rid);
+ }
+
+ (*types)[i] = t_rids[i].type;
+ } else {
+ ZERO_STRUCTP(sid);
+ (*types)[i] = SID_NAME_UNKNOWN;
+ }
+ }
+
+ done:
+ prs_mem_free(&qbuf);
+ prs_mem_free(&rbuf);
+
+ return result;
+}
+
+/** Query info policy
+ *
+ * @param domain_sid - returned remote server's domain sid */
+
+NTSTATUS cli_lsa_query_info_policy(struct cli_state *cli, TALLOC_CTX *mem_ctx,
+ POLICY_HND *pol, uint16 info_class,
+ char **domain_name, DOM_SID **domain_sid)
+{
+ prs_struct qbuf, rbuf;
+ LSA_Q_QUERY_INFO q;
+ LSA_R_QUERY_INFO r;
+ NTSTATUS result;
+
+ ZERO_STRUCT(q);
+ ZERO_STRUCT(r);
+
+ /* Initialise parse structures */
+
+ prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL);
+ prs_init(&rbuf, 0, mem_ctx, UNMARSHALL);
+
+ /* Marshall data and send request */
+
+ init_q_query(&q, pol, info_class);
+
+ if (!lsa_io_q_query("", &q, &qbuf, 0) ||
+ !rpc_api_pipe_req(cli, LSA_QUERYINFOPOLICY, &qbuf, &rbuf)) {
+ result = NT_STATUS_UNSUCCESSFUL;
+ goto done;
+ }
+
+ /* Unmarshall response */
+
+ if (!lsa_io_r_query("", &r, &rbuf, 0)) {
+ result = NT_STATUS_UNSUCCESSFUL;
+ goto done;
+ }
+
+ if (!NT_STATUS_IS_OK(result = r.status)) {
+ goto done;
+ }
+
+ /* Return output parameters */
+
+ switch (info_class) {
+
+ case 3:
+ if (domain_name && (r.dom.id3.buffer_dom_name != 0)) {
+ *domain_name = unistr2_tdup(mem_ctx,
+ &r.dom.id3.
+ uni_domain_name);
+ }
+
+ if (domain_sid && (r.dom.id3.buffer_dom_sid != 0)) {
+ *domain_sid = talloc(mem_ctx, sizeof(**domain_sid));
+ if (*domain_sid) {
+ sid_copy(*domain_sid, &r.dom.id3.dom_sid.sid);
+ }
+ }
+
+ break;
+
+ case 5:
+
+ if (domain_name && (r.dom.id5.buffer_dom_name != 0)) {
+ *domain_name = unistr2_tdup(mem_ctx,
+ &r.dom.id5.
+ uni_domain_name);
+ }
+
+ if (domain_sid && (r.dom.id5.buffer_dom_sid != 0)) {
+ *domain_sid = talloc(mem_ctx, sizeof(**domain_sid));
+ if (*domain_sid) {
+ sid_copy(*domain_sid, &r.dom.id5.dom_sid.sid);
+ }
+ }
+ break;
+
+ default:
+ DEBUG(3, ("unknown info class %d\n", info_class));
+ break;
+ }
+
+ done:
+ prs_mem_free(&qbuf);
+ prs_mem_free(&rbuf);
+
+ return result;
+}
+
+/** Query info policy2
+ *
+ * @param domain_name - returned remote server's domain name
+ * @param dns_name - returned remote server's dns domain name
+ * @param forest_name - returned remote server's forest name
+ * @param domain_guid - returned remote server's domain guid
+ * @param domain_sid - returned remote server's domain sid */
+
+NTSTATUS cli_lsa_query_info_policy2(struct cli_state *cli, TALLOC_CTX *mem_ctx,
+ POLICY_HND *pol, uint16 info_class,
+ char **domain_name, char **dns_name,
+ char **forest_name, struct uuid **domain_guid,
+ DOM_SID **domain_sid)
+{
+ prs_struct qbuf, rbuf;
+ LSA_Q_QUERY_INFO2 q;
+ LSA_R_QUERY_INFO2 r;
+ NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+
+ if (info_class != 12)
+ goto done;
+
+ ZERO_STRUCT(q);
+ ZERO_STRUCT(r);
+
+ /* Initialise parse structures */
+
+ prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL);
+ prs_init(&rbuf, 0, mem_ctx, UNMARSHALL);
+
+ /* Marshall data and send request */
+
+ init_q_query2(&q, pol, info_class);
+
+ if (!lsa_io_q_query_info2("", &q, &qbuf, 0) ||
+ !rpc_api_pipe_req(cli, LSA_QUERYINFO2, &qbuf, &rbuf)) {
+ result = NT_STATUS_UNSUCCESSFUL;
+ goto done;
+ }
+
+ /* Unmarshall response */
+
+ if (!lsa_io_r_query_info2("", &r, &rbuf, 0)) {
+ result = NT_STATUS_UNSUCCESSFUL;
+ goto done;
+ }
+
+ if (!NT_STATUS_IS_OK(result = r.status)) {
+ goto done;
+ }
+
+ /* Return output parameters */
+
+ ZERO_STRUCTP(domain_guid);
+
+ if (domain_name && r.info.dns_dom_info.hdr_nb_dom_name.buffer) {
+ *domain_name = unistr2_tdup(mem_ctx,
+ &r.info.dns_dom_info
+ .uni_nb_dom_name);
+ }
+ if (dns_name && r.info.dns_dom_info.hdr_dns_dom_name.buffer) {
+ *dns_name = unistr2_tdup(mem_ctx,
+ &r.info.dns_dom_info
+ .uni_dns_dom_name);
+ }
+ if (forest_name && r.info.dns_dom_info.hdr_forest_name.buffer) {
+ *forest_name = unistr2_tdup(mem_ctx,
+ &r.info.dns_dom_info
+ .uni_forest_name);
+ }
+
+ if (domain_guid) {
+ *domain_guid = talloc(mem_ctx, sizeof(**domain_guid));
+ memcpy(*domain_guid,
+ &r.info.dns_dom_info.dom_guid,
+ sizeof(struct uuid));
+ }
+
+ if (domain_sid && r.info.dns_dom_info.ptr_dom_sid != 0) {
+ *domain_sid = talloc(mem_ctx, sizeof(**domain_sid));
+ if (*domain_sid) {
+ sid_copy(*domain_sid,
+ &r.info.dns_dom_info.dom_sid.sid);
+ }
+ }
+
+ done:
+ prs_mem_free(&qbuf);
+ prs_mem_free(&rbuf);
+
+ return result;
+}
+
+/**
+ * Enumerate list of trusted domains
+ *
+ * @param cli client state (cli_state) structure of the connection
+ * @param mem_ctx memory context
+ * @param pol opened lsa policy handle
+ * @param enum_ctx enumeration context ie. index of first returned domain entry
+ * @param pref_num_domains preferred max number of entries returned in one response
+ * @param num_domains total number of trusted domains returned by response
+ * @param domain_names returned trusted domain names
+ * @param domain_sids returned trusted domain sids
+ *
+ * @return nt status code of response
+ **/
+
+NTSTATUS cli_lsa_enum_trust_dom(struct cli_state *cli, TALLOC_CTX *mem_ctx,
+ POLICY_HND *pol, uint32 *enum_ctx,
+ uint32 *num_domains,
+ char ***domain_names, DOM_SID **domain_sids)
+{
+ prs_struct qbuf, rbuf;
+ LSA_Q_ENUM_TRUST_DOM q;
+ LSA_R_ENUM_TRUST_DOM r;
+ NTSTATUS result;
+ int i;
+
+ ZERO_STRUCT(q);
+ ZERO_STRUCT(r);
+
+ /* Initialise parse structures */
+
+ prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL);
+ prs_init(&rbuf, 0, mem_ctx, UNMARSHALL);
+
+ /* Marshall data and send request */
+
+ /* 64k is enough for about 2000 trusted domains */
+ init_q_enum_trust_dom(&q, pol, *enum_ctx, 0x10000);
+
+ if (!lsa_io_q_enum_trust_dom("", &q, &qbuf, 0) ||
+ !rpc_api_pipe_req(cli, LSA_ENUMTRUSTDOM, &qbuf, &rbuf)) {
+ result = NT_STATUS_UNSUCCESSFUL;
+ goto done;
+ }
+
+ /* Unmarshall response */
+
+ if (!lsa_io_r_enum_trust_dom("", &r, &rbuf, 0)) {
+ result = NT_STATUS_UNSUCCESSFUL;
+ goto done;
+ }
+
+ result = r.status;
+
+ if (!NT_STATUS_IS_OK(result) &&
+ !NT_STATUS_EQUAL(result, NT_STATUS_NO_MORE_ENTRIES) &&
+ !NT_STATUS_EQUAL(result, STATUS_MORE_ENTRIES)) {
+
+ /* An actual error ocured */
+
+ goto done;
+ }
+
+ /* Return output parameters */
+
+ if (r.num_domains) {
+
+ /* Allocate memory for trusted domain names and sids */
+
+ *domain_names = (char **)talloc(mem_ctx, sizeof(char *) *
+ r.num_domains);
+
+ if (!*domain_names) {
+ DEBUG(0, ("cli_lsa_enum_trust_dom(): out of memory\n"));
+ result = NT_STATUS_NO_MEMORY;
+ goto done;
+ }
+
+ *domain_sids = (DOM_SID *)talloc(mem_ctx, sizeof(DOM_SID) *
+ r.num_domains);
+ if (!domain_sids) {
+ DEBUG(0, ("cli_lsa_enum_trust_dom(): out of memory\n"));
+ result = NT_STATUS_NO_MEMORY;
+ goto done;
+ }
+
+ /* Copy across names and sids */
+
+ for (i = 0; i < r.num_domains; i++) {
+ fstring tmp;
+
+ unistr2_to_ascii(tmp, &r.uni_domain_name[i],
+ sizeof(tmp) - 1);
+ (*domain_names)[i] = talloc_strdup(mem_ctx, tmp);
+ sid_copy(&(*domain_sids)[i], &r.domain_sid[i].sid);
+ }
+ }
+
+ *num_domains = r.num_domains;
+ *enum_ctx = r.enum_context;
+
+ done:
+ prs_mem_free(&qbuf);
+ prs_mem_free(&rbuf);
+
+ return result;
+}
+
+
+/** Enumerate privileges*/
+
+NTSTATUS cli_lsa_enum_privilege(struct cli_state *cli, TALLOC_CTX *mem_ctx,
+ POLICY_HND *pol, uint32 *enum_context, uint32 pref_max_length,
+ uint32 *count, char ***privs_name, uint32 **privs_high, uint32 **privs_low)
+{
+ prs_struct qbuf, rbuf;
+ LSA_Q_ENUM_PRIVS q;
+ LSA_R_ENUM_PRIVS r;
+ NTSTATUS result;
+ int i;
+
+ ZERO_STRUCT(q);
+ ZERO_STRUCT(r);
+
+ /* Initialise parse structures */
+
+ prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL);
+ prs_init(&rbuf, 0, mem_ctx, UNMARSHALL);
+
+ /* Marshall data and send request */
+
+ init_q_enum_privs(&q, pol, *enum_context, pref_max_length);
+
+ if (!lsa_io_q_enum_privs("", &q, &qbuf, 0) ||
+ !rpc_api_pipe_req(cli, LSA_ENUM_PRIVS, &qbuf, &rbuf)) {
+ result = NT_STATUS_UNSUCCESSFUL;
+ goto done;
+ }
+
+ /* Unmarshall response */
+
+ if (!lsa_io_r_enum_privs("", &r, &rbuf, 0)) {
+ result = NT_STATUS_UNSUCCESSFUL;
+ goto done;
+ }
+
+ if (!NT_STATUS_IS_OK(result = r.status)) {
+ goto done;
+ }
+
+ /* Return output parameters */
+
+ *enum_context = r.enum_context;
+ *count = r.count;
+
+ if (!((*privs_name = (char **)talloc(mem_ctx, sizeof(char *) * r.count)))) {
+ DEBUG(0, ("(cli_lsa_enum_privilege): out of memory\n"));
+ result = NT_STATUS_UNSUCCESSFUL;
+ goto done;
+ }
+
+ if (!((*privs_high = (uint32 *)talloc(mem_ctx, sizeof(uint32) * r.count)))) {
+ DEBUG(0, ("(cli_lsa_enum_privilege): out of memory\n"));
+ result = NT_STATUS_UNSUCCESSFUL;
+ goto done;
+ }
+
+ if (!((*privs_low = (uint32 *)talloc(mem_ctx, sizeof(uint32) * r.count)))) {
+ DEBUG(0, ("(cli_lsa_enum_privilege): out of memory\n"));
+ result = NT_STATUS_UNSUCCESSFUL;
+ goto done;
+ }
+
+ for (i = 0; i < r.count; i++) {
+ fstring name;
+
+ rpcstr_pull_unistr2_fstring( name, &r.privs[i].name);
+
+ (*privs_name)[i] = talloc_strdup(mem_ctx, name);
+
+ (*privs_high)[i] = r.privs[i].luid_high;
+ (*privs_low)[i] = r.privs[i].luid_low;
+ }
+
+ done:
+ prs_mem_free(&qbuf);
+ prs_mem_free(&rbuf);
+
+ return result;
+}
+
+/** Get privilege name */
+
+NTSTATUS cli_lsa_get_dispname(struct cli_state *cli, TALLOC_CTX *mem_ctx,
+ POLICY_HND *pol, const char *name,
+ uint16 lang_id, uint16 lang_id_sys,
+ fstring description, uint16 *lang_id_desc)
+{
+ prs_struct qbuf, rbuf;
+ LSA_Q_PRIV_GET_DISPNAME q;
+ LSA_R_PRIV_GET_DISPNAME r;
+ NTSTATUS result;
+
+ ZERO_STRUCT(q);
+ ZERO_STRUCT(r);
+
+ /* Initialise parse structures */
+
+ prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL);
+ prs_init(&rbuf, 0, mem_ctx, UNMARSHALL);
+
+ /* Marshall data and send request */
+
+ init_lsa_priv_get_dispname(&q, pol, name, lang_id, lang_id_sys);
+
+ if (!lsa_io_q_priv_get_dispname("", &q, &qbuf, 0) ||
+ !rpc_api_pipe_req(cli, LSA_PRIV_GET_DISPNAME, &qbuf, &rbuf)) {
+ result = NT_STATUS_UNSUCCESSFUL;
+ goto done;
+ }
+
+ /* Unmarshall response */
+
+ if (!lsa_io_r_priv_get_dispname("", &r, &rbuf, 0)) {
+ result = NT_STATUS_UNSUCCESSFUL;
+ goto done;
+ }
+
+ if (!NT_STATUS_IS_OK(result = r.status)) {
+ goto done;
+ }
+
+ /* Return output parameters */
+
+ rpcstr_pull_unistr2_fstring(description , &r.desc);
+ *lang_id_desc = r.lang_id;
+
+ done:
+ prs_mem_free(&qbuf);
+ prs_mem_free(&rbuf);
+
+ return result;
+}
+
+/** Enumerate list of SIDs */
+
+NTSTATUS cli_lsa_enum_sids(struct cli_state *cli, TALLOC_CTX *mem_ctx,
+ POLICY_HND *pol, uint32 *enum_ctx, uint32 pref_max_length,
+ uint32 *num_sids, DOM_SID **sids)
+{
+ prs_struct qbuf, rbuf;
+ LSA_Q_ENUM_ACCOUNTS q;
+ LSA_R_ENUM_ACCOUNTS r;
+ NTSTATUS result;
+ int i;
+
+ ZERO_STRUCT(q);
+ ZERO_STRUCT(r);
+
+ /* Initialise parse structures */
+
+ prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL);
+ prs_init(&rbuf, 0, mem_ctx, UNMARSHALL);
+
+ /* Marshall data and send request */
+
+ init_lsa_q_enum_accounts(&q, pol, *enum_ctx, pref_max_length);
+
+ if (!lsa_io_q_enum_accounts("", &q, &qbuf, 0) ||
+ !rpc_api_pipe_req(cli, LSA_ENUM_ACCOUNTS, &qbuf, &rbuf)) {
+ result = NT_STATUS_UNSUCCESSFUL;
+ goto done;
+ }
+
+ /* Unmarshall response */
+
+ if (!lsa_io_r_enum_accounts("", &r, &rbuf, 0)) {
+ result = NT_STATUS_UNSUCCESSFUL;
+ goto done;
+ }
+
+ result = r.status;
+
+ if (!NT_STATUS_IS_OK(result = r.status)) {
+ goto done;
+ }
+
+ if (r.sids.num_entries==0)
+ goto done;
+
+ /* Return output parameters */
+
+ *sids = (DOM_SID *)talloc(mem_ctx, sizeof(DOM_SID) * r.sids.num_entries);
+ if (!*sids) {
+ DEBUG(0, ("(cli_lsa_enum_sids): out of memory\n"));
+ result = NT_STATUS_UNSUCCESSFUL;
+ goto done;
+ }
+
+ /* Copy across names and sids */
+
+ for (i = 0; i < r.sids.num_entries; i++) {
+ sid_copy(&(*sids)[i], &r.sids.sid[i].sid);
+ }
+
+ *num_sids= r.sids.num_entries;
+ *enum_ctx = r.enum_context;
+
+ done:
+ prs_mem_free(&qbuf);
+ prs_mem_free(&rbuf);
+
+ return result;
+}
+
+/** Create a LSA user handle
+ *
+ * @param cli Handle on an initialised SMB connection
+ *
+ * FIXME: The code is actually identical to open account
+ * TODO: Check and code what the function should exactly do
+ *
+ * */
+
+NTSTATUS cli_lsa_create_account(struct cli_state *cli, TALLOC_CTX *mem_ctx,
+ POLICY_HND *dom_pol, DOM_SID *sid, uint32 desired_access,
+ POLICY_HND *user_pol)
+{
+ prs_struct qbuf, rbuf;
+ LSA_Q_CREATEACCOUNT q;
+ LSA_R_CREATEACCOUNT r;
+ NTSTATUS result;
+
+ ZERO_STRUCT(q);
+ ZERO_STRUCT(r);
+
+ /* Initialise parse structures */
+
+ prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL);
+ prs_init(&rbuf, 0, mem_ctx, UNMARSHALL);
+
+ /* Initialise input parameters */
+
+ init_lsa_q_create_account(&q, dom_pol, sid, desired_access);
+
+ /* Marshall data and send request */
+
+ if (!lsa_io_q_create_account("", &q, &qbuf, 0) ||
+ !rpc_api_pipe_req(cli, LSA_CREATEACCOUNT, &qbuf, &rbuf)) {
+ result = NT_STATUS_UNSUCCESSFUL;
+ goto done;
+ }
+
+ /* Unmarshall response */
+
+ if (!lsa_io_r_create_account("", &r, &rbuf, 0)) {
+ result = NT_STATUS_UNSUCCESSFUL;
+ goto done;
+ }
+
+ /* Return output parameters */
+
+ if (NT_STATUS_IS_OK(result = r.status)) {
+ *user_pol = r.pol;
+ }
+
+ done:
+ prs_mem_free(&qbuf);
+ prs_mem_free(&rbuf);
+
+ return result;
+}
+
+/** Open a LSA user handle
+ *
+ * @param cli Handle on an initialised SMB connection */
+
+NTSTATUS cli_lsa_open_account(struct cli_state *cli, TALLOC_CTX *mem_ctx,
+ POLICY_HND *dom_pol, DOM_SID *sid, uint32 des_access,
+ POLICY_HND *user_pol)
+{
+ prs_struct qbuf, rbuf;
+ LSA_Q_OPENACCOUNT q;
+ LSA_R_OPENACCOUNT r;
+ NTSTATUS result;
+
+ ZERO_STRUCT(q);
+ ZERO_STRUCT(r);
+
+ /* Initialise parse structures */
+
+ prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL);
+ prs_init(&rbuf, 0, mem_ctx, UNMARSHALL);
+
+ /* Initialise input parameters */
+
+ init_lsa_q_open_account(&q, dom_pol, sid, des_access);
+
+ /* Marshall data and send request */
+
+ if (!lsa_io_q_open_account("", &q, &qbuf, 0) ||
+ !rpc_api_pipe_req(cli, LSA_OPENACCOUNT, &qbuf, &rbuf)) {
+ result = NT_STATUS_UNSUCCESSFUL;
+ goto done;
+ }
+
+ /* Unmarshall response */
+
+ if (!lsa_io_r_open_account("", &r, &rbuf, 0)) {
+ result = NT_STATUS_UNSUCCESSFUL;
+ goto done;
+ }
+
+ /* Return output parameters */
+
+ if (NT_STATUS_IS_OK(result = r.status)) {
+ *user_pol = r.pol;
+ }
+
+ done:
+ prs_mem_free(&qbuf);
+ prs_mem_free(&rbuf);
+
+ return result;
+}
+
+/** Enumerate user privileges
+ *
+ * @param cli Handle on an initialised SMB connection */
+
+NTSTATUS cli_lsa_enum_privsaccount(struct cli_state *cli, TALLOC_CTX *mem_ctx,
+ POLICY_HND *pol, uint32 *count, LUID_ATTR **set)
+{
+ prs_struct qbuf, rbuf;
+ LSA_Q_ENUMPRIVSACCOUNT q;
+ LSA_R_ENUMPRIVSACCOUNT r;
+ NTSTATUS result;
+ int i;
+
+ ZERO_STRUCT(q);
+ ZERO_STRUCT(r);
+
+ /* Initialise parse structures */
+
+ prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL);
+ prs_init(&rbuf, 0, mem_ctx, UNMARSHALL);
+
+ /* Initialise input parameters */
+
+ init_lsa_q_enum_privsaccount(&q, pol);
+
+ /* Marshall data and send request */
+
+ if (!lsa_io_q_enum_privsaccount("", &q, &qbuf, 0) ||
+ !rpc_api_pipe_req(cli, LSA_ENUMPRIVSACCOUNT, &qbuf, &rbuf)) {
+ result = NT_STATUS_UNSUCCESSFUL;
+ goto done;
+ }
+
+ /* Unmarshall response */
+
+ if (!lsa_io_r_enum_privsaccount("", &r, &rbuf, 0)) {
+ result = NT_STATUS_UNSUCCESSFUL;
+ goto done;
+ }
+
+ /* Return output parameters */
+
+ if (!NT_STATUS_IS_OK(result = r.status)) {
+ goto done;
+ }
+
+ if (r.count == 0)
+ goto done;
+
+ if (!((*set = (LUID_ATTR *)talloc(mem_ctx, sizeof(LUID_ATTR) * r.count)))) {
+ DEBUG(0, ("(cli_lsa_enum_privsaccount): out of memory\n"));
+ result = NT_STATUS_UNSUCCESSFUL;
+ goto done;
+ }
+
+ for (i=0; i<r.count; i++) {
+ (*set)[i].luid.low = r.set->set[i].luid.low;
+ (*set)[i].luid.high = r.set->set[i].luid.high;
+ (*set)[i].attr = r.set->set[i].attr;
+ }
+
+ *count=r.count;
+ done:
+ prs_mem_free(&qbuf);
+ prs_mem_free(&rbuf);
+
+ return result;
+}
+
+/** Get a privilege value given its name */
+
+NTSTATUS cli_lsa_lookupprivvalue(struct cli_state *cli, TALLOC_CTX *mem_ctx,
+ POLICY_HND *pol, const char *name, LUID *luid)
+{
+ prs_struct qbuf, rbuf;
+ LSA_Q_LOOKUPPRIVVALUE q;
+ LSA_R_LOOKUPPRIVVALUE r;
+ NTSTATUS result;
+
+ ZERO_STRUCT(q);
+ ZERO_STRUCT(r);
+
+ /* Initialise parse structures */
+
+ prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL);
+ prs_init(&rbuf, 0, mem_ctx, UNMARSHALL);
+
+ /* Marshall data and send request */
+
+ init_lsa_q_lookupprivvalue(&q, pol, name);
+
+ if (!lsa_io_q_lookupprivvalue("", &q, &qbuf, 0) ||
+ !rpc_api_pipe_req(cli, LSA_LOOKUPPRIVVALUE, &qbuf, &rbuf)) {
+ result = NT_STATUS_UNSUCCESSFUL;
+ goto done;
+ }
+
+ /* Unmarshall response */
+
+ if (!lsa_io_r_lookupprivvalue("", &r, &rbuf, 0)) {
+ result = NT_STATUS_UNSUCCESSFUL;
+ goto done;
+ }
+
+ if (!NT_STATUS_IS_OK(result = r.status)) {
+ goto done;
+ }
+
+ /* Return output parameters */
+
+ (*luid).low=r.luid.low;
+ (*luid).high=r.luid.high;
+
+ done:
+ prs_mem_free(&qbuf);
+ prs_mem_free(&rbuf);
+
+ return result;
+}
+
+/** Query LSA security object */
+
+NTSTATUS cli_lsa_query_secobj(struct cli_state *cli, TALLOC_CTX *mem_ctx,
+ POLICY_HND *pol, uint32 sec_info,
+ SEC_DESC_BUF **psdb)
+{
+ prs_struct qbuf, rbuf;
+ LSA_Q_QUERY_SEC_OBJ q;
+ LSA_R_QUERY_SEC_OBJ r;
+ NTSTATUS result;
+
+ ZERO_STRUCT(q);
+ ZERO_STRUCT(r);
+
+ /* Initialise parse structures */
+
+ prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL);
+ prs_init(&rbuf, 0, mem_ctx, UNMARSHALL);
+
+ /* Marshall data and send request */
+
+ init_q_query_sec_obj(&q, pol, sec_info);
+
+ if (!lsa_io_q_query_sec_obj("", &q, &qbuf, 0) ||
+ !rpc_api_pipe_req(cli, LSA_QUERYSECOBJ, &qbuf, &rbuf)) {
+ result = NT_STATUS_UNSUCCESSFUL;
+ goto done;
+ }
+
+ /* Unmarshall response */
+
+ if (!lsa_io_r_query_sec_obj("", &r, &rbuf, 0)) {
+ result = NT_STATUS_UNSUCCESSFUL;
+ goto done;
+ }
+
+ if (!NT_STATUS_IS_OK(result = r.status)) {
+ goto done;
+ }
+
+ /* Return output parameters */
+
+ if (psdb)
+ *psdb = r.buf;
+
+ done:
+ prs_mem_free(&qbuf);
+ prs_mem_free(&rbuf);
+
+ return result;
+}
+
+
+/* Enumerate account rights This is similar to enum_privileges but
+ takes a SID directly, avoiding the open_account call.
+*/
+
+NTSTATUS cli_lsa_enum_account_rights(struct cli_state *cli, TALLOC_CTX *mem_ctx,
+ POLICY_HND *pol, DOM_SID sid,
+ uint32 *count, char ***privs_name)
+{
+ prs_struct qbuf, rbuf;
+ LSA_Q_ENUM_ACCT_RIGHTS q;
+ LSA_R_ENUM_ACCT_RIGHTS r;
+ NTSTATUS result;
+ int i;
+
+ ZERO_STRUCT(q);
+ ZERO_STRUCT(r);
+
+ /* Initialise parse structures */
+
+ prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL);
+ prs_init(&rbuf, 0, mem_ctx, UNMARSHALL);
+
+ /* Marshall data and send request */
+ init_q_enum_acct_rights(&q, pol, 2, &sid);
+
+ if (!lsa_io_q_enum_acct_rights("", &q, &qbuf, 0) ||
+ !rpc_api_pipe_req(cli, LSA_ENUMACCTRIGHTS, &qbuf, &rbuf)) {
+ result = NT_STATUS_UNSUCCESSFUL;
+ goto done;
+ }
+
+ if (!lsa_io_r_enum_acct_rights("", &r, &rbuf, 0)) {
+ result = NT_STATUS_UNSUCCESSFUL;
+ goto done;
+ }
+
+ if (!NT_STATUS_IS_OK(result = r.status)) {
+ goto done;
+ }
+
+ *count = r.count;
+ if (! *count) {
+ goto done;
+ }
+
+ *privs_name = (char **)talloc(mem_ctx, (*count) * sizeof(char **));
+ for (i=0;i<*count;i++) {
+ pull_ucs2_talloc(mem_ctx, &(*privs_name)[i], r.rights.strings[i].string.buffer);
+ }
+
+done:
+
+ return result;
+}
+
+
+
+/* add account rights to an account. */
+
+NTSTATUS cli_lsa_add_account_rights(struct cli_state *cli, TALLOC_CTX *mem_ctx,
+ POLICY_HND *pol, DOM_SID sid,
+ uint32 count, const char **privs_name)
+{
+ prs_struct qbuf, rbuf;
+ LSA_Q_ADD_ACCT_RIGHTS q;
+ LSA_R_ADD_ACCT_RIGHTS r;
+ NTSTATUS result;
+
+ ZERO_STRUCT(q);
+
+ /* Initialise parse structures */
+ prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL);
+ prs_init(&rbuf, 0, mem_ctx, UNMARSHALL);
+
+ /* Marshall data and send request */
+ init_q_add_acct_rights(&q, pol, &sid, count, privs_name);
+
+ if (!lsa_io_q_add_acct_rights("", &q, &qbuf, 0) ||
+ !rpc_api_pipe_req(cli, LSA_ADDACCTRIGHTS, &qbuf, &rbuf)) {
+ result = NT_STATUS_UNSUCCESSFUL;
+ goto done;
+ }
+
+ /* Unmarshall response */
+
+ if (!lsa_io_r_add_acct_rights("", &r, &rbuf, 0)) {
+ result = NT_STATUS_UNSUCCESSFUL;
+ goto done;
+ }
+
+ if (!NT_STATUS_IS_OK(result = r.status)) {
+ goto done;
+ }
+done:
+
+ return result;
+}
+
+
+/* remove account rights for an account. */
+
+NTSTATUS cli_lsa_remove_account_rights(struct cli_state *cli, TALLOC_CTX *mem_ctx,
+ POLICY_HND *pol, DOM_SID sid, BOOL removeall,
+ uint32 count, const char **privs_name)
+{
+ prs_struct qbuf, rbuf;
+ LSA_Q_REMOVE_ACCT_RIGHTS q;
+ LSA_R_REMOVE_ACCT_RIGHTS r;
+ NTSTATUS result;
+
+ ZERO_STRUCT(q);
+
+ /* Initialise parse structures */
+ prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL);
+ prs_init(&rbuf, 0, mem_ctx, UNMARSHALL);
+
+ /* Marshall data and send request */
+ init_q_remove_acct_rights(&q, pol, &sid, removeall?1:0, count, privs_name);
+
+ if (!lsa_io_q_remove_acct_rights("", &q, &qbuf, 0) ||
+ !rpc_api_pipe_req(cli, LSA_REMOVEACCTRIGHTS, &qbuf, &rbuf)) {
+ result = NT_STATUS_UNSUCCESSFUL;
+ goto done;
+ }
+
+ /* Unmarshall response */
+
+ if (!lsa_io_r_remove_acct_rights("", &r, &rbuf, 0)) {
+ result = NT_STATUS_UNSUCCESSFUL;
+ goto done;
+ }
+
+ if (!NT_STATUS_IS_OK(result = r.status)) {
+ goto done;
+ }
+done:
+
+ return result;
+}
+
+
+#if 0
+
+/** An example of how to use the routines in this file. Fetch a DOMAIN
+ sid. Does complete cli setup / teardown anonymously. */
+
+BOOL fetch_domain_sid( char *domain, char *remote_machine, DOM_SID *psid)
+{
+ extern pstring global_myname;
+ struct cli_state cli;
+ NTSTATUS result;
+ POLICY_HND lsa_pol;
+ BOOL ret = False;
+
+ ZERO_STRUCT(cli);
+ if(cli_initialise(&cli) == False) {
+ DEBUG(0,("fetch_domain_sid: unable to initialize client connection.\n"));
+ return False;
+ }
+
+ if(!resolve_name( remote_machine, &cli.dest_ip, 0x20)) {
+ DEBUG(0,("fetch_domain_sid: Can't resolve address for %s\n", remote_machine));
+ goto done;
+ }
+
+ if (!cli_connect(&cli, remote_machine, &cli.dest_ip)) {
+ DEBUG(0,("fetch_domain_sid: unable to connect to SMB server on \
+machine %s. Error was : %s.\n", remote_machine, cli_errstr(&cli) ));
+ goto done;
+ }
+
+ if (!attempt_netbios_session_request(&cli, global_myname, remote_machine, &cli.dest_ip)) {
+ DEBUG(0,("fetch_domain_sid: machine %s rejected the NetBIOS session request.\n",
+ remote_machine));
+ goto done;
+ }
+
+ cli.protocol = PROTOCOL_NT1;
+
+ if (!cli_negprot(&cli)) {
+ DEBUG(0,("fetch_domain_sid: machine %s rejected the negotiate protocol. \
+Error was : %s.\n", remote_machine, cli_errstr(&cli) ));
+ goto done;
+ }
+
+ if (cli.protocol != PROTOCOL_NT1) {
+ DEBUG(0,("fetch_domain_sid: machine %s didn't negotiate NT protocol.\n",
+ remote_machine));
+ goto done;
+ }
+
+ /*
+ * Do an anonymous session setup.
+ */
+
+ if (!cli_session_setup(&cli, "", "", 0, "", 0, "")) {
+ DEBUG(0,("fetch_domain_sid: machine %s rejected the session setup. \
+Error was : %s.\n", remote_machine, cli_errstr(&cli) ));
+ goto done;
+ }
+
+ if (!(cli.sec_mode & NEGOTIATE_SECURITY_USER_LEVEL)) {
+ DEBUG(0,("fetch_domain_sid: machine %s isn't in user level security mode\n",
+ remote_machine));
+ goto done;
+ }
+
+ if (!cli_send_tconX(&cli, "IPC$", "IPC", "", 1)) {
+ DEBUG(0,("fetch_domain_sid: machine %s rejected the tconX on the IPC$ share. \
+Error was : %s.\n", remote_machine, cli_errstr(&cli) ));
+ goto done;
+ }
+
+ /* Fetch domain sid */
+
+ if (!cli_nt_session_open(&cli, PI_LSARPC)) {
+ DEBUG(0, ("fetch_domain_sid: Error connecting to SAM pipe\n"));
+ goto done;
+ }
+
+ result = cli_lsa_open_policy(&cli, cli.mem_ctx, True, SEC_RIGHTS_QUERY_VALUE, &lsa_pol);
+ if (!NT_STATUS_IS_OK(result)) {
+ DEBUG(0, ("fetch_domain_sid: Error opening lsa policy handle. %s\n",
+ nt_errstr(result) ));
+ goto done;
+ }
+
+ result = cli_lsa_query_info_policy(&cli, cli.mem_ctx, &lsa_pol, 5, domain, psid);
+ if (!NT_STATUS_IS_OK(result)) {
+ DEBUG(0, ("fetch_domain_sid: Error querying lsa policy handle. %s\n",
+ nt_errstr(result) ));
+ goto done;
+ }
+
+ ret = True;
+
+ done:
+
+ cli_shutdown(&cli);
+ return ret;
+}
+
+#endif
+
+/** @} **/
diff --git a/source/rpc_client/cli_netlogon.c b/source/rpc_client/cli_netlogon.c
new file mode 100644
index 00000000000..f6d88a19501
--- /dev/null
+++ b/source/rpc_client/cli_netlogon.c
@@ -0,0 +1,802 @@
+/*
+ Unix SMB/CIFS implementation.
+ NT Domain Authentication SMB / MSRPC client
+ Copyright (C) Andrew Tridgell 1992-2000
+ Copyright (C) Luke Kenneth Casson Leighton 1996-2000
+ Copyright (C) Tim Potter 2001
+ Copyright (C) Paul Ashton 1997.
+ Copyright (C) Jeremy Allison 1998.
+ Copyright (C) Andrew Bartlett 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 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"
+
+/* LSA Request Challenge. Sends our challenge to server, then gets
+ server response. These are used to generate the credentials. */
+
+NTSTATUS cli_net_req_chal(struct cli_state *cli, DOM_CHAL *clnt_chal,
+ DOM_CHAL *srv_chal)
+{
+ prs_struct qbuf, rbuf;
+ NET_Q_REQ_CHAL q;
+ NET_R_REQ_CHAL r;
+ NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+
+ prs_init(&qbuf, MAX_PDU_FRAG_LEN, cli->mem_ctx, MARSHALL);
+ prs_init(&rbuf, 0, cli->mem_ctx, UNMARSHALL);
+
+ /* create and send a MSRPC command with api NET_REQCHAL */
+
+ DEBUG(4,("cli_net_req_chal: LSA Request Challenge from %s to %s: %s\n",
+ global_myname(), cli->desthost, credstr(clnt_chal->data)));
+
+ /* store the parameters */
+ init_q_req_chal(&q, cli->srv_name_slash, global_myname(), clnt_chal);
+
+ /* Marshall data and send request */
+
+ if (!net_io_q_req_chal("", &q, &qbuf, 0) ||
+ !rpc_api_pipe_req(cli, NET_REQCHAL, &qbuf, &rbuf)) {
+ goto done;
+ }
+
+ /* Unmarhall response */
+
+ if (!net_io_r_req_chal("", &r, &rbuf, 0)) {
+ goto done;
+ }
+
+ result = r.status;
+
+ /* Return result */
+
+ if (NT_STATUS_IS_OK(result)) {
+ memcpy(srv_chal, r.srv_chal.data, sizeof(srv_chal->data));
+ }
+
+ done:
+ prs_mem_free(&qbuf);
+ prs_mem_free(&rbuf);
+
+ return result;
+}
+
+/****************************************************************************
+LSA Authenticate 2
+
+Send the client credential, receive back a server credential.
+Ensure that the server credential returned matches the session key
+encrypt of the server challenge originally received. JRA.
+****************************************************************************/
+
+NTSTATUS cli_net_auth2(struct cli_state *cli,
+ uint16 sec_chan,
+ uint32 *neg_flags, DOM_CHAL *srv_chal)
+{
+ prs_struct qbuf, rbuf;
+ NET_Q_AUTH_2 q;
+ NET_R_AUTH_2 r;
+ NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+
+ prs_init(&qbuf, MAX_PDU_FRAG_LEN, cli->mem_ctx, MARSHALL);
+ prs_init(&rbuf, 0, cli->mem_ctx, UNMARSHALL);
+
+ /* create and send a MSRPC command with api NET_AUTH2 */
+
+ DEBUG(4,("cli_net_auth2: srv:%s acct:%s sc:%x mc: %s chal %s neg: %x\n",
+ cli->srv_name_slash, cli->mach_acct, sec_chan, global_myname(),
+ credstr(cli->clnt_cred.challenge.data), *neg_flags));
+
+ /* store the parameters */
+ init_q_auth_2(&q, cli->srv_name_slash, cli->mach_acct,
+ sec_chan, global_myname(), &cli->clnt_cred.challenge,
+ *neg_flags);
+
+ /* turn parameters into data stream */
+
+ if (!net_io_q_auth_2("", &q, &qbuf, 0) ||
+ !rpc_api_pipe_req(cli, NET_AUTH2, &qbuf, &rbuf)) {
+ goto done;
+ }
+
+ /* Unmarshall response */
+
+ if (!net_io_r_auth_2("", &r, &rbuf, 0)) {
+ goto done;
+ }
+
+ result = r.status;
+
+ if (NT_STATUS_IS_OK(result)) {
+ UTIME zerotime;
+
+ /*
+ * Check the returned value using the initial
+ * server received challenge.
+ */
+
+ zerotime.time = 0;
+ if (cred_assert( &r.srv_chal, cli->sess_key, srv_chal,
+ zerotime) == 0) {
+
+ /*
+ * Server replied with bad credential. Fail.
+ */
+ DEBUG(0,("cli_net_auth2: server %s replied with bad credential (bad machine \
+password ?).\n", cli->desthost ));
+ result = NT_STATUS_ACCESS_DENIED;
+ goto done;
+ }
+ *neg_flags = r.srv_flgs.neg_flags;
+ }
+
+ done:
+ prs_mem_free(&qbuf);
+ prs_mem_free(&rbuf);
+
+ return result;
+}
+
+/****************************************************************************
+LSA Authenticate 3
+
+Send the client credential, receive back a server credential.
+Ensure that the server credential returned matches the session key
+encrypt of the server challenge originally received. JRA.
+****************************************************************************/
+
+NTSTATUS cli_net_auth3(struct cli_state *cli,
+ uint16 sec_chan,
+ uint32 *neg_flags, DOM_CHAL *srv_chal)
+{
+ prs_struct qbuf, rbuf;
+ NET_Q_AUTH_3 q;
+ NET_R_AUTH_3 r;
+ NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+
+ prs_init(&qbuf, MAX_PDU_FRAG_LEN, cli->mem_ctx, MARSHALL);
+ prs_init(&rbuf, 0, cli->mem_ctx, UNMARSHALL);
+
+ /* create and send a MSRPC command with api NET_AUTH2 */
+
+ DEBUG(4,("cli_net_auth3: srv:%s acct:%s sc:%x mc: %s chal %s neg: %x\n",
+ cli->srv_name_slash, cli->mach_acct, sec_chan, global_myname(),
+ credstr(cli->clnt_cred.challenge.data), *neg_flags));
+
+ /* store the parameters */
+ init_q_auth_3(&q, cli->srv_name_slash, cli->mach_acct,
+ sec_chan, global_myname(), &cli->clnt_cred.challenge,
+ *neg_flags);
+
+ /* turn parameters into data stream */
+
+ if (!net_io_q_auth_3("", &q, &qbuf, 0) ||
+ !rpc_api_pipe_req(cli, NET_AUTH3, &qbuf, &rbuf)) {
+ goto done;
+ }
+
+ /* Unmarshall response */
+
+ if (!net_io_r_auth_3("", &r, &rbuf, 0)) {
+ goto done;
+ }
+
+ result = r.status;
+
+ if (NT_STATUS_IS_OK(result)) {
+ UTIME zerotime;
+
+ /*
+ * Check the returned value using the initial
+ * server received challenge.
+ */
+
+ zerotime.time = 0;
+ if (cred_assert( &r.srv_chal, cli->sess_key, srv_chal,
+ zerotime) == 0) {
+
+ /*
+ * Server replied with bad credential. Fail.
+ */
+ DEBUG(0,("cli_net_auth3: server %s replied with bad credential (bad machine \
+password ?).\n", cli->desthost ));
+ result = NT_STATUS_ACCESS_DENIED;
+ goto done;
+ }
+ *neg_flags = r.srv_flgs.neg_flags;
+ }
+
+ done:
+ prs_mem_free(&qbuf);
+ prs_mem_free(&rbuf);
+
+ return result;
+}
+
+/* Initialize domain session credentials */
+
+NTSTATUS cli_nt_setup_creds(struct cli_state *cli,
+ uint16 sec_chan,
+ const unsigned char mach_pwd[16], uint32 *neg_flags, int level)
+{
+ DOM_CHAL clnt_chal;
+ DOM_CHAL srv_chal;
+ UTIME zerotime;
+ NTSTATUS result;
+
+ /******************* Request Challenge ********************/
+
+ generate_random_buffer(clnt_chal.data, 8, False);
+
+ /* send a client challenge; receive a server challenge */
+ result = cli_net_req_chal(cli, &clnt_chal, &srv_chal);
+
+ if (!NT_STATUS_IS_OK(result)) {
+ DEBUG(0,("cli_nt_setup_creds: request challenge failed\n"));
+ return result;
+ }
+
+ /**************** Long-term Session key **************/
+
+ /* calculate the session key */
+ cred_session_key(&clnt_chal, &srv_chal, mach_pwd,
+ cli->sess_key);
+ memset((char *)cli->sess_key+8, '\0', 8);
+
+ /******************* Authenticate 2/3 ********************/
+
+ /* calculate auth-2/3 credentials */
+ zerotime.time = 0;
+ cred_create(cli->sess_key, &clnt_chal, zerotime, &cli->clnt_cred.challenge);
+
+ /*
+ * Send client auth-2/3 challenge.
+ * Receive an auth-2/3 challenge response and check it.
+ */
+ switch (level) {
+ case 2:
+ result = cli_net_auth2(cli, sec_chan, neg_flags, &srv_chal);
+ break;
+ case 3:
+ result = cli_net_auth3(cli, sec_chan, neg_flags, &srv_chal);
+ break;
+ default:
+ DEBUG(1,("cli_nt_setup_creds: unsupported auth level: %d\n", level));
+ break;
+ }
+
+ if (!NT_STATUS_IS_OK(result))
+ DEBUG(3,("cli_nt_setup_creds: auth%d challenge failed %s\n", level, nt_errstr(result)));
+
+ return result;
+}
+
+/* Logon Control 2 */
+
+NTSTATUS cli_netlogon_logon_ctrl2(struct cli_state *cli, TALLOC_CTX *mem_ctx,
+ uint32 query_level)
+{
+ prs_struct qbuf, rbuf;
+ NET_Q_LOGON_CTRL2 q;
+ NET_R_LOGON_CTRL2 r;
+ NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+
+ ZERO_STRUCT(q);
+ ZERO_STRUCT(r);
+
+ /* Initialise parse structures */
+
+ prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL);
+ prs_init(&rbuf, 0, mem_ctx, UNMARSHALL);
+
+ /* Initialise input parameters */
+
+ init_net_q_logon_ctrl2(&q, cli->srv_name_slash, query_level);
+
+ /* Marshall data and send request */
+
+ if (!net_io_q_logon_ctrl2("", &q, &qbuf, 0) ||
+ !rpc_api_pipe_req(cli, NET_LOGON_CTRL2, &qbuf, &rbuf)) {
+ result = NT_STATUS_UNSUCCESSFUL;
+ goto done;
+ }
+
+ /* Unmarshall response */
+
+ if (!net_io_r_logon_ctrl2("", &r, &rbuf, 0)) {
+ result = NT_STATUS_UNSUCCESSFUL;
+ goto done;
+ }
+
+ result = r.status;
+
+ done:
+ prs_mem_free(&qbuf);
+ prs_mem_free(&rbuf);
+
+ return result;
+}
+
+/* GetDCName */
+
+NTSTATUS cli_netlogon_getdcname(struct cli_state *cli, TALLOC_CTX *mem_ctx,
+ const char *domainname, fstring dcname)
+{
+ prs_struct qbuf, rbuf;
+ NET_Q_GETDCNAME q;
+ NET_R_GETDCNAME r;
+ NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+
+ ZERO_STRUCT(q);
+ ZERO_STRUCT(r);
+
+ /* Initialise parse structures */
+
+ prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL);
+ prs_init(&rbuf, 0, mem_ctx, UNMARSHALL);
+
+ /* Initialise input parameters */
+
+ init_net_q_getdcname(&q, cli->srv_name_slash, domainname);
+
+ /* Marshall data and send request */
+
+ if (!net_io_q_getdcname("", &q, &qbuf, 0) ||
+ !rpc_api_pipe_req(cli, NET_GETDCNAME, &qbuf, &rbuf)) {
+ result = NT_STATUS_UNSUCCESSFUL;
+ goto done;
+ }
+
+ /* Unmarshall response */
+
+ if (!net_io_r_getdcname("", &r, &rbuf, 0)) {
+ result = NT_STATUS_UNSUCCESSFUL;
+ goto done;
+ }
+
+ result = r.status;
+
+ if (NT_STATUS_IS_OK(result))
+ rpcstr_pull_unistr2_fstring(dcname, &r.uni_dcname);
+
+ done:
+ prs_mem_free(&qbuf);
+ prs_mem_free(&rbuf);
+
+ return result;
+}
+
+/****************************************************************************
+Generate the next creds to use.
+****************************************************************************/
+
+static void gen_next_creds( struct cli_state *cli, DOM_CRED *new_clnt_cred)
+{
+ /*
+ * Create the new client credentials.
+ */
+
+ cli->clnt_cred.timestamp.time = time(NULL);
+
+ memcpy(new_clnt_cred, &cli->clnt_cred, sizeof(*new_clnt_cred));
+
+ /* Calculate the new credentials. */
+ cred_create(cli->sess_key, &(cli->clnt_cred.challenge),
+ new_clnt_cred->timestamp, &(new_clnt_cred->challenge));
+}
+
+/* Sam synchronisation */
+
+NTSTATUS cli_netlogon_sam_sync(struct cli_state *cli, TALLOC_CTX *mem_ctx, DOM_CRED *ret_creds,
+ uint32 database_id, uint32 next_rid, uint32 *num_deltas,
+ SAM_DELTA_HDR **hdr_deltas,
+ SAM_DELTA_CTR **deltas)
+{
+ prs_struct qbuf, rbuf;
+ NET_Q_SAM_SYNC q;
+ NET_R_SAM_SYNC r;
+ NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+ DOM_CRED clnt_creds;
+
+ ZERO_STRUCT(q);
+ ZERO_STRUCT(r);
+
+ /* Initialise parse structures */
+
+ prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL);
+ prs_init(&rbuf, 0, mem_ctx, UNMARSHALL);
+
+ /* Initialise input parameters */
+
+ gen_next_creds(cli, &clnt_creds);
+
+ init_net_q_sam_sync(&q, cli->srv_name_slash, cli->clnt_name_slash + 2,
+ &clnt_creds, ret_creds, database_id, next_rid);
+
+ /* Marshall data and send request */
+
+ if (!net_io_q_sam_sync("", &q, &qbuf, 0) ||
+ !rpc_api_pipe_req(cli, NET_SAM_SYNC, &qbuf, &rbuf)) {
+ result = NT_STATUS_UNSUCCESSFUL;
+ goto done;
+ }
+
+ /* Unmarshall response */
+
+ if (!net_io_r_sam_sync("", cli->sess_key, &r, &rbuf, 0)) {
+ result = NT_STATUS_UNSUCCESSFUL;
+ goto done;
+ }
+
+ /* Return results */
+
+ result = r.status;
+ *num_deltas = r.num_deltas2;
+ *hdr_deltas = r.hdr_deltas;
+ *deltas = r.deltas;
+
+ memcpy(ret_creds, &r.srv_creds, sizeof(*ret_creds));
+
+ done:
+ prs_mem_free(&qbuf);
+ prs_mem_free(&rbuf);
+
+ return result;
+}
+
+/* Sam synchronisation */
+
+NTSTATUS cli_netlogon_sam_deltas(struct cli_state *cli, TALLOC_CTX *mem_ctx,
+ uint32 database_id, UINT64_S seqnum,
+ uint32 *num_deltas,
+ SAM_DELTA_HDR **hdr_deltas,
+ SAM_DELTA_CTR **deltas)
+{
+ prs_struct qbuf, rbuf;
+ NET_Q_SAM_DELTAS q;
+ NET_R_SAM_DELTAS r;
+ NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+ DOM_CRED clnt_creds;
+
+ ZERO_STRUCT(q);
+ ZERO_STRUCT(r);
+
+ /* Initialise parse structures */
+
+ prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL);
+ prs_init(&rbuf, 0, mem_ctx, UNMARSHALL);
+
+ /* Initialise input parameters */
+
+ gen_next_creds(cli, &clnt_creds);
+
+ init_net_q_sam_deltas(&q, cli->srv_name_slash,
+ cli->clnt_name_slash + 2, &clnt_creds,
+ database_id, seqnum);
+
+ /* Marshall data and send request */
+
+ if (!net_io_q_sam_deltas("", &q, &qbuf, 0) ||
+ !rpc_api_pipe_req(cli, NET_SAM_DELTAS, &qbuf, &rbuf)) {
+ result = NT_STATUS_UNSUCCESSFUL;
+ goto done;
+ }
+
+ /* Unmarshall response */
+
+ if (!net_io_r_sam_deltas("", cli->sess_key, &r, &rbuf, 0)) {
+ result = NT_STATUS_UNSUCCESSFUL;
+ goto done;
+ }
+
+ /* Return results */
+
+ result = r.status;
+ *num_deltas = r.num_deltas2;
+ *hdr_deltas = r.hdr_deltas;
+ *deltas = r.deltas;
+
+ done:
+ prs_mem_free(&qbuf);
+ prs_mem_free(&rbuf);
+
+ return result;
+}
+
+/* Logon domain user */
+
+NTSTATUS cli_netlogon_sam_logon(struct cli_state *cli, TALLOC_CTX *mem_ctx,
+ DOM_CRED *ret_creds,
+ const char *username, const char *password,
+ int logon_type)
+{
+ prs_struct qbuf, rbuf;
+ NET_Q_SAM_LOGON q;
+ NET_R_SAM_LOGON r;
+ NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+ DOM_CRED clnt_creds, dummy_rtn_creds;
+ NET_ID_INFO_CTR ctr;
+ NET_USER_INFO_3 user;
+ int validation_level = 3;
+
+ ZERO_STRUCT(q);
+ ZERO_STRUCT(r);
+ ZERO_STRUCT(dummy_rtn_creds);
+
+ /* Initialise parse structures */
+
+ prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL);
+ prs_init(&rbuf, 0, mem_ctx, UNMARSHALL);
+
+ /* Initialise input parameters */
+
+ gen_next_creds(cli, &clnt_creds);
+
+ q.validation_level = validation_level;
+
+ if (ret_creds == NULL)
+ ret_creds = &dummy_rtn_creds;
+
+ ctr.switch_value = logon_type;
+
+ switch (logon_type) {
+ case INTERACTIVE_LOGON_TYPE: {
+ unsigned char lm_owf_user_pwd[16], nt_owf_user_pwd[16];
+
+ nt_lm_owf_gen(password, nt_owf_user_pwd, lm_owf_user_pwd);
+
+ init_id_info1(&ctr.auth.id1, lp_workgroup(),
+ 0, /* param_ctrl */
+ 0xdead, 0xbeef, /* LUID? */
+ username, cli->clnt_name_slash,
+ (const char *)cli->sess_key, lm_owf_user_pwd,
+ nt_owf_user_pwd);
+
+ break;
+ }
+ case NET_LOGON_TYPE: {
+ uint8 chal[8];
+ unsigned char local_lm_response[24];
+ unsigned char local_nt_response[24];
+
+ generate_random_buffer(chal, 8, False);
+
+ SMBencrypt(password, chal, local_lm_response);
+ SMBNTencrypt(password, chal, local_nt_response);
+
+ init_id_info2(&ctr.auth.id2, lp_workgroup(),
+ 0, /* param_ctrl */
+ 0xdead, 0xbeef, /* LUID? */
+ username, cli->clnt_name_slash, chal,
+ local_lm_response, 24, local_nt_response, 24);
+ break;
+ }
+ default:
+ DEBUG(0, ("switch value %d not supported\n",
+ ctr.switch_value));
+ goto done;
+ }
+
+ init_sam_info(&q.sam_id, cli->srv_name_slash, global_myname(),
+ &clnt_creds, ret_creds, logon_type,
+ &ctr);
+
+ /* Marshall data and send request */
+
+ if (!net_io_q_sam_logon("", &q, &qbuf, 0) ||
+ !rpc_api_pipe_req(cli, NET_SAMLOGON, &qbuf, &rbuf)) {
+ goto done;
+ }
+
+ /* Unmarshall response */
+
+ r.user = &user;
+
+ if (!net_io_r_sam_logon("", &r, &rbuf, 0)) {
+ goto done;
+ }
+
+ /* Return results */
+
+ result = r.status;
+ memcpy(ret_creds, &r.srv_creds, sizeof(*ret_creds));
+
+ done:
+ prs_mem_free(&qbuf);
+ prs_mem_free(&rbuf);
+
+ return result;
+}
+
+
+/**
+ * Logon domain user with an 'network' SAM logon
+ *
+ * @param info3 Pointer to a NET_USER_INFO_3 already allocated by the caller.
+ **/
+
+NTSTATUS cli_netlogon_sam_network_logon(struct cli_state *cli, TALLOC_CTX *mem_ctx,
+ DOM_CRED *ret_creds,
+ const char *username, const char *domain, const char *workstation,
+ const uint8 chal[8],
+ DATA_BLOB lm_response, DATA_BLOB nt_response,
+ NET_USER_INFO_3 *info3)
+
+{
+ prs_struct qbuf, rbuf;
+ NET_Q_SAM_LOGON q;
+ NET_R_SAM_LOGON r;
+ NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+ DOM_CRED clnt_creds, dummy_rtn_creds;
+ NET_ID_INFO_CTR ctr;
+ int validation_level = 3;
+ char *workstation_name_slash;
+ uint8 netlogon_sess_key[16];
+ static uint8 zeros[16];
+
+ ZERO_STRUCT(q);
+ ZERO_STRUCT(r);
+ ZERO_STRUCT(dummy_rtn_creds);
+
+ workstation_name_slash = talloc_asprintf(mem_ctx, "\\\\%s", workstation);
+
+ if (!workstation_name_slash) {
+ DEBUG(0, ("talloc_asprintf failed!\n"));
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ /* Initialise parse structures */
+
+ prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL);
+ prs_init(&rbuf, 0, mem_ctx, UNMARSHALL);
+
+ /* Initialise input parameters */
+
+ gen_next_creds(cli, &clnt_creds);
+
+ q.validation_level = validation_level;
+
+ if (ret_creds == NULL)
+ ret_creds = &dummy_rtn_creds;
+
+ ctr.switch_value = NET_LOGON_TYPE;
+
+ init_id_info2(&ctr.auth.id2, domain,
+ 0, /* param_ctrl */
+ 0xdead, 0xbeef, /* LUID? */
+ username, workstation_name_slash, (const uchar*)chal,
+ lm_response.data, lm_response.length, nt_response.data, nt_response.length);
+
+ init_sam_info(&q.sam_id, cli->srv_name_slash, global_myname(),
+ &clnt_creds, ret_creds, NET_LOGON_TYPE,
+ &ctr);
+
+ /* Marshall data and send request */
+
+ if (!net_io_q_sam_logon("", &q, &qbuf, 0) ||
+ !rpc_api_pipe_req(cli, NET_SAMLOGON, &qbuf, &rbuf)) {
+ goto done;
+ }
+
+ /* Unmarshall response */
+
+ r.user = info3;
+
+ if (!net_io_r_sam_logon("", &r, &rbuf, 0)) {
+ goto done;
+ }
+
+ ZERO_STRUCT(netlogon_sess_key);
+ memcpy(netlogon_sess_key, cli->sess_key, 8);
+
+ if (memcmp(zeros, info3->user_sess_key, 16) != 0) {
+ SamOEMhash(info3->user_sess_key, netlogon_sess_key, 16);
+ } else {
+ memset(info3->user_sess_key, '\0', 16);
+ }
+
+ if (memcmp(zeros, info3->padding, 16) != 0) {
+ SamOEMhash(info3->padding, netlogon_sess_key, 16);
+ } else {
+ memset(info3->padding, '\0', 16);
+ }
+
+ /* Return results */
+
+ result = r.status;
+ memcpy(ret_creds, &r.srv_creds, sizeof(*ret_creds));
+
+ done:
+ prs_mem_free(&qbuf);
+ prs_mem_free(&rbuf);
+
+ return result;
+}
+
+/***************************************************************************
+LSA Server Password Set.
+****************************************************************************/
+
+NTSTATUS cli_net_srv_pwset(struct cli_state *cli, TALLOC_CTX *mem_ctx,
+ const char *machine_name, uint8 hashed_mach_pwd[16])
+{
+ prs_struct rbuf;
+ prs_struct qbuf;
+ DOM_CRED new_clnt_cred;
+ NET_Q_SRV_PWSET q_s;
+ uint16 sec_chan_type = 2;
+ NTSTATUS nt_status;
+
+ gen_next_creds( cli, &new_clnt_cred);
+
+ prs_init(&qbuf , 1024, mem_ctx, MARSHALL);
+ prs_init(&rbuf, 0, mem_ctx, UNMARSHALL);
+
+ DEBUG(4,("cli_net_srv_pwset: srv:%s acct:%s sc: %d mc: %s clnt %s %x\n",
+ cli->srv_name_slash, cli->mach_acct, sec_chan_type, machine_name,
+ credstr(new_clnt_cred.challenge.data), new_clnt_cred.timestamp.time));
+
+ /* store the parameters */
+ init_q_srv_pwset(&q_s, cli->srv_name_slash, (const char *)cli->sess_key,
+ cli->mach_acct, sec_chan_type, machine_name,
+ &new_clnt_cred, hashed_mach_pwd);
+
+ /* turn parameters into data stream */
+ if(!net_io_q_srv_pwset("", &q_s, &qbuf, 0)) {
+ DEBUG(0,("cli_net_srv_pwset: Error : failed to marshall NET_Q_SRV_PWSET struct.\n"));
+ nt_status = NT_STATUS_UNSUCCESSFUL;
+ goto done;
+ }
+
+ /* send the data on \PIPE\ */
+ if (rpc_api_pipe_req(cli, NET_SRVPWSET, &qbuf, &rbuf))
+ {
+ NET_R_SRV_PWSET r_s;
+
+ if (!net_io_r_srv_pwset("", &r_s, &rbuf, 0)) {
+ nt_status = NT_STATUS_UNSUCCESSFUL;
+ goto done;
+ }
+
+ nt_status = r_s.status;
+
+ if (!NT_STATUS_IS_OK(r_s.status))
+ {
+ /* report error code */
+ DEBUG(0,("cli_net_srv_pwset: %s\n", nt_errstr(nt_status)));
+ goto done;
+ }
+
+ /* Update the credentials. */
+ if (!clnt_deal_with_creds(cli->sess_key, &(cli->clnt_cred), &(r_s.srv_cred)))
+ {
+ /*
+ * Server replied with bad credential. Fail.
+ */
+ DEBUG(0,("cli_net_srv_pwset: server %s replied with bad credential (bad machine \
+password ?).\n", cli->desthost ));
+ nt_status = NT_STATUS_UNSUCCESSFUL;
+ }
+ }
+
+ done:
+ prs_mem_free(&qbuf);
+ prs_mem_free(&rbuf);
+
+ return nt_status;
+}
+
diff --git a/source/rpc_client/cli_pipe.c b/source/rpc_client/cli_pipe.c
new file mode 100644
index 00000000000..df0d37a4631
--- /dev/null
+++ b/source/rpc_client/cli_pipe.c
@@ -0,0 +1,1651 @@
+/*
+ * Unix SMB/CIFS implementation.
+ * RPC Pipe client / server routines
+ * Copyright (C) Andrew Tridgell 1992-1998,
+ * Copyright (C) Luke Kenneth Casson Leighton 1996-1998,
+ * Copyright (C) Paul Ashton 1998.
+ * Copyright (C) Jeremy Allison 1999.
+ * Copyright (C) Andrew Bartlett 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 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"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_RPC_CLI
+
+extern struct pipe_id_info pipe_names[];
+
+/* convert pipe auth flags into the RPC auth type and level */
+
+void get_auth_type_level(int pipe_auth_flags, int *auth_type, int *auth_level)
+{
+ *auth_type = 0;
+ *auth_level = 0;
+ if (pipe_auth_flags & AUTH_PIPE_SEAL) {
+ *auth_level = RPC_PIPE_AUTH_SEAL_LEVEL;
+ } else if (pipe_auth_flags & AUTH_PIPE_SIGN) {
+ *auth_level = RPC_PIPE_AUTH_SIGN_LEVEL;
+ }
+
+ if (pipe_auth_flags & AUTH_PIPE_NETSEC) {
+ *auth_type = NETSEC_AUTH_TYPE;
+ } else if (pipe_auth_flags & AUTH_PIPE_NTLMSSP) {
+ *auth_type = NTLMSSP_AUTH_TYPE;
+ }
+}
+
+/********************************************************************
+ Rpc pipe call id.
+ ********************************************************************/
+
+static uint32 get_rpc_call_id(void)
+{
+ static uint32 call_id = 0;
+ return ++call_id;
+}
+
+/*******************************************************************
+ Use SMBreadX to get rest of one fragment's worth of rpc data.
+ ********************************************************************/
+
+static BOOL rpc_read(struct cli_state *cli, prs_struct *rdata, uint32 data_to_read, uint32 *rdata_offset)
+{
+ size_t size = (size_t)cli->max_recv_frag;
+ int stream_offset = 0;
+ int num_read;
+ char *pdata;
+ int extra_data_size = ((int)*rdata_offset) + ((int)data_to_read) - (int)prs_data_size(rdata);
+
+ DEBUG(5,("rpc_read: data_to_read: %u rdata offset: %u extra_data_size: %d\n",
+ (int)data_to_read, (unsigned int)*rdata_offset, extra_data_size));
+
+ /*
+ * Grow the buffer if needed to accommodate the data to be read.
+ */
+
+ if (extra_data_size > 0) {
+ if(!prs_force_grow(rdata, (uint32)extra_data_size)) {
+ DEBUG(0,("rpc_read: Failed to grow parse struct by %d bytes.\n", extra_data_size ));
+ return False;
+ }
+ DEBUG(5,("rpc_read: grew buffer by %d bytes to %u\n", extra_data_size, prs_data_size(rdata) ));
+ }
+
+ pdata = prs_data_p(rdata) + *rdata_offset;
+
+ do /* read data using SMBreadX */
+ {
+ uint32 ecode;
+ uint8 eclass;
+
+ if (size > (size_t)data_to_read)
+ size = (size_t)data_to_read;
+
+ num_read = (int)cli_read(cli, cli->nt_pipe_fnum, pdata, (off_t)stream_offset, size);
+
+ DEBUG(5,("rpc_read: num_read = %d, read offset: %d, to read: %d\n",
+ num_read, stream_offset, data_to_read));
+
+ if (cli_is_dos_error(cli)) {
+ cli_dos_error(cli, &eclass, &ecode);
+ if (eclass != ERRDOS && ecode != ERRmoredata) {
+ DEBUG(0,("rpc_read: Error %d/%u in cli_read\n",
+ eclass, (unsigned int)ecode));
+ return False;
+ }
+ }
+
+ data_to_read -= num_read;
+ stream_offset += num_read;
+ pdata += num_read;
+
+ } while (num_read > 0 && data_to_read > 0);
+ /* && err == (0x80000000 | STATUS_BUFFER_OVERFLOW)); */
+
+ /*
+ * Update the current offset into rdata by the amount read.
+ */
+ *rdata_offset += stream_offset;
+
+ return True;
+}
+
+/****************************************************************************
+ Checks the header. This will set the endian bit in the rdata prs_struct. JRA.
+ ****************************************************************************/
+
+static BOOL rpc_check_hdr(prs_struct *rdata, RPC_HDR *rhdr,
+ BOOL *first, BOOL *last, uint32 *len)
+{
+ DEBUG(5,("rpc_check_hdr: rdata->data_size = %u\n", (uint32)prs_data_size(rdata) ));
+
+ /* Next call sets endian bit. */
+
+ if(!smb_io_rpc_hdr("rpc_hdr ", rhdr, rdata, 0)) {
+ DEBUG(0,("rpc_check_hdr: Failed to unmarshall RPC_HDR.\n"));
+ return False;
+ }
+
+ if (prs_offset(rdata) != RPC_HEADER_LEN) {
+ DEBUG(0,("rpc_check_hdr: offset was %x, should be %x.\n", prs_offset(rdata), RPC_HEADER_LEN));
+ return False;
+ }
+
+ (*first) = ((rhdr->flags & RPC_FLG_FIRST) != 0);
+ (*last) = ((rhdr->flags & RPC_FLG_LAST ) != 0);
+ (*len) = (uint32)rhdr->frag_len - prs_data_size(rdata);
+
+ return (rhdr->pkt_type != RPC_FAULT);
+}
+
+/****************************************************************************
+ Verify data on an rpc pipe.
+ The VERIFY & SEAL code is only executed on packets that look like this :
+
+ Request/Response PDU's look like the following...
+
+ |<------------------PDU len----------------------------------------------->|
+ |<-HDR_LEN-->|<--REQ LEN------>|.............|<-AUTH_HDRLEN->|<-AUTH_LEN-->|
+
+ +------------+-----------------+-------------+---------------+-------------+
+ | RPC HEADER | REQ/RESP HEADER | DATA ...... | AUTH_HDR | AUTH DATA |
+ +------------+-----------------+-------------+---------------+-------------+
+
+ Never on bind requests/responses.
+ ****************************************************************************/
+
+static BOOL rpc_auth_pipe(struct cli_state *cli, prs_struct *rdata,
+ uint32 fragment_start, int len, int auth_len, uint8 pkt_type,
+ int *pauth_padding_len)
+{
+
+ /*
+ * The following is that length of the data we must sign or seal.
+ * This doesn't include the RPC headers or the auth_len or the RPC_HDR_AUTH_LEN
+ * preceeding the auth_data.
+ */
+
+ int data_len = len - RPC_HEADER_LEN - RPC_HDR_RESP_LEN - RPC_HDR_AUTH_LEN - auth_len;
+
+ /*
+ * The start of the data to sign/seal is just after the RPC headers.
+ */
+ char *reply_data = prs_data_p(rdata) + fragment_start + RPC_HEADER_LEN + RPC_HDR_REQ_LEN;
+
+ RPC_HDR_AUTH rhdr_auth;
+
+ char *dp = prs_data_p(rdata) + fragment_start + len -
+ RPC_HDR_AUTH_LEN - auth_len;
+ prs_struct auth_verf;
+
+ *pauth_padding_len = 0;
+
+ if (auth_len == 0) {
+ if (cli->pipe_auth_flags == 0) {
+ /* move along, nothing to see here */
+ return True;
+ }
+
+ DEBUG(2, ("No authenticaton header recienved on reply, but this pipe is authenticated\n"));
+ return False;
+ }
+
+ DEBUG(5,("rpc_auth_pipe: pkt_type: %d len: %d auth_len: %d NTLMSSP %s schannel %s sign %s seal %s \n",
+ pkt_type, len, auth_len,
+ BOOLSTR(cli->pipe_auth_flags & AUTH_PIPE_NTLMSSP),
+ BOOLSTR(cli->pipe_auth_flags & AUTH_PIPE_NETSEC),
+ BOOLSTR(cli->pipe_auth_flags & AUTH_PIPE_SIGN),
+ BOOLSTR(cli->pipe_auth_flags & AUTH_PIPE_SEAL)));
+
+ if (dp - prs_data_p(rdata) > prs_data_size(rdata)) {
+ DEBUG(0,("rpc_auth_pipe: schannel auth data > data size !\n"));
+ return False;
+ }
+
+ DEBUG(10,("rpc_auth_pipe: packet:\n"));
+ dump_data(100, dp, auth_len);
+
+ prs_init(&auth_verf, 0, cli->mem_ctx, UNMARSHALL);
+
+ /* The endinness must be preserved. JRA. */
+ prs_set_endian_data( &auth_verf, rdata->bigendian_data);
+
+ /* Point this new parse struct at the auth section of the main
+ parse struct - rather than copying it. Avoids needing to
+ free it on every error
+ */
+ prs_give_memory(&auth_verf, dp, RPC_HDR_AUTH_LEN + auth_len, False /* not dynamic */);
+ prs_set_offset(&auth_verf, 0);
+
+ {
+ int auth_type;
+ int auth_level;
+ if (!smb_io_rpc_hdr_auth("auth_hdr", &rhdr_auth, &auth_verf, 0)) {
+ DEBUG(0, ("rpc_auth_pipe: Could not parse auth header\n"));
+ return False;
+ }
+
+ /* Let the caller know how much padding at the end of the data */
+ *pauth_padding_len = rhdr_auth.padding;
+
+ /* Check it's the type of reply we were expecting to decode */
+
+ get_auth_type_level(cli->pipe_auth_flags, &auth_type, &auth_level);
+ if (rhdr_auth.auth_type != auth_type) {
+ DEBUG(0, ("BAD auth type %d (should be %d)\n",
+ rhdr_auth.auth_type, auth_type));
+ return False;
+ }
+
+ if (rhdr_auth.auth_level != auth_level) {
+ DEBUG(0, ("BAD auth level %d (should be %d)\n",
+ rhdr_auth.auth_level, auth_level));
+ return False;
+ }
+ }
+
+ if (pkt_type == RPC_BINDACK) {
+ if (cli->pipe_auth_flags & AUTH_PIPE_NTLMSSP) {
+ /* copy the next auth_len bytes into a buffer for
+ later use */
+
+ DATA_BLOB ntlmssp_verf = data_blob(NULL, auth_len);
+ BOOL store_ok;
+
+ /* save the reply away, for use a little later */
+ prs_copy_data_out((char *)ntlmssp_verf.data, &auth_verf, auth_len);
+
+ store_ok = (NT_STATUS_IS_OK(ntlmssp_store_response(cli->ntlmssp_pipe_state,
+ ntlmssp_verf)));
+
+ data_blob_free(&ntlmssp_verf);
+ return store_ok;
+ }
+ else if (cli->pipe_auth_flags & AUTH_PIPE_NETSEC) {
+ /* nothing to do here - we don't seem to be able to
+ validate the bindack based on VL's comments */
+ return True;
+ }
+ }
+
+ if (cli->pipe_auth_flags & AUTH_PIPE_NTLMSSP) {
+ NTSTATUS nt_status;
+ DATA_BLOB sig;
+ if ((cli->pipe_auth_flags & AUTH_PIPE_SIGN) ||
+ (cli->pipe_auth_flags & AUTH_PIPE_SEAL)) {
+ if (auth_len != RPC_AUTH_NTLMSSP_CHK_LEN) {
+ DEBUG(0,("rpc_auth_pipe: wrong ntlmssp auth len %d\n", auth_len));
+ return False;
+ }
+ sig = data_blob(NULL, auth_len);
+ prs_copy_data_out((char *)sig.data, &auth_verf, auth_len);
+ }
+
+ /*
+ * Unseal any sealed data in the PDU, not including the
+ * 8 byte auth_header or the auth_data.
+ */
+
+ /*
+ * Now unseal and check the auth verifier in the auth_data at
+ * the end of the packet.
+ */
+
+ if (cli->pipe_auth_flags & AUTH_PIPE_SEAL) {
+ if (data_len < 0) {
+ DEBUG(1, ("Can't unseal - data_len < 0!!\n"));
+ return False;
+ }
+ nt_status = ntlmssp_unseal_packet(cli->ntlmssp_pipe_state,
+ (unsigned char *)reply_data, data_len,
+ &sig);
+ }
+ else if (cli->pipe_auth_flags & AUTH_PIPE_SIGN) {
+ nt_status = ntlmssp_check_packet(cli->ntlmssp_pipe_state,
+ (const unsigned char *)reply_data, data_len,
+ &sig);
+ }
+
+ data_blob_free(&sig);
+
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ DEBUG(0, ("rpc_auth_pipe: could not validate "
+ "incoming NTLMSSP packet!\n"));
+ return False;
+ }
+ }
+
+ if (cli->pipe_auth_flags & AUTH_PIPE_NETSEC) {
+ RPC_AUTH_NETSEC_CHK chk;
+
+ if (auth_len != RPC_AUTH_NETSEC_CHK_LEN) {
+ DEBUG(0,("rpc_auth_pipe: wrong schannel auth len %d\n", auth_len));
+ return False;
+ }
+
+ if (!smb_io_rpc_auth_netsec_chk("schannel_auth_sign",
+ &chk, &auth_verf, 0)) {
+ DEBUG(0, ("rpc_auth_pipe: schannel unmarshalling "
+ "RPC_AUTH_NETSECK_CHK failed\n"));
+ return False;
+ }
+
+ if (!netsec_decode(&cli->auth_info,
+ cli->pipe_auth_flags,
+ SENDER_IS_ACCEPTOR,
+ &chk, reply_data, data_len)) {
+ DEBUG(0, ("rpc_auth_pipe: Could not decode schannel\n"));
+ return False;
+ }
+
+ cli->auth_info.seq_num++;
+
+ }
+ return True;
+}
+
+
+/****************************************************************************
+ Send data on an rpc pipe via trans, which *must* be the last fragment.
+ receive response data from an rpc pipe, which may be large...
+
+ Read the first fragment: unfortunately have to use SMBtrans for the first
+ bit, then SMBreadX for subsequent bits.
+
+ If first fragment received also wasn't the last fragment, continue
+ getting fragments until we _do_ receive the last fragment.
+
+ Request/Response PDU's look like the following...
+
+ |<------------------PDU len----------------------------------------------->|
+ |<-HDR_LEN-->|<--REQ LEN------>|.............|<-AUTH_HDRLEN->|<-AUTH_LEN-->|
+
+ +------------+-----------------+-------------+---------------+-------------+
+ | RPC HEADER | REQ/RESP HEADER | DATA ...... | AUTH_HDR | AUTH DATA |
+ +------------+-----------------+-------------+---------------+-------------+
+
+ Where the presence of the AUTH_HDR and AUTH are dependent on the
+ signing & sealing being negotiated.
+
+ ****************************************************************************/
+
+static BOOL rpc_api_pipe(struct cli_state *cli, prs_struct *data, prs_struct *rdata,
+ uint8 expected_pkt_type)
+{
+ uint32 len;
+ char *rparam = NULL;
+ uint32 rparam_len = 0;
+ uint16 setup[2];
+ BOOL first = True;
+ BOOL last = True;
+ RPC_HDR rhdr;
+ char *pdata = data ? prs_data_p(data) : NULL;
+ uint32 data_len = data ? prs_offset(data) : 0;
+ char *prdata = NULL;
+ uint32 rdata_len = 0;
+ uint32 current_offset = 0;
+ uint32 fragment_start = 0;
+ uint32 max_data = cli->max_xmit_frag ? cli->max_xmit_frag : 1024;
+ int auth_padding_len = 0;
+
+ /* Create setup parameters - must be in native byte order. */
+
+ setup[0] = TRANSACT_DCERPCCMD;
+ setup[1] = cli->nt_pipe_fnum; /* Pipe file handle. */
+
+ DEBUG(5,("rpc_api_pipe: fnum:%x\n", (int)cli->nt_pipe_fnum));
+
+ /* Send the RPC request and receive a response. For short RPC
+ calls (about 1024 bytes or so) the RPC request and response
+ appears in a SMBtrans request and response. Larger RPC
+ responses are received further on. */
+
+ if (!cli_api_pipe(cli, "\\PIPE\\",
+ setup, 2, 0, /* Setup, length, max */
+ NULL, 0, 0, /* Params, length, max */
+ pdata, data_len, max_data, /* data, length, max */
+ &rparam, &rparam_len, /* return params, len */
+ &prdata, &rdata_len)) /* return data, len */
+ {
+ DEBUG(0, ("cli_pipe: return critical error. Error was %s\n", cli_errstr(cli)));
+ return False;
+ }
+
+ /* Throw away returned params - we know we won't use them. */
+
+ SAFE_FREE(rparam);
+
+ if (prdata == NULL) {
+ DEBUG(0,("rpc_api_pipe: pipe %x failed to return data.\n",
+ (int)cli->nt_pipe_fnum));
+ return False;
+ }
+
+ /*
+ * Give this memory as dynamically allocated to the return parse
+ * struct.
+ */
+
+ prs_give_memory(rdata, prdata, rdata_len, True);
+ current_offset = rdata_len;
+
+ /* This next call sets the endian bit correctly in rdata. */
+
+ if (!rpc_check_hdr(rdata, &rhdr, &first, &last, &len)) {
+ prs_mem_free(rdata);
+ return False;
+ }
+
+ if (rhdr.pkt_type == RPC_BINDACK) {
+ if (!last && !first) {
+ DEBUG(5,("rpc_api_pipe: bug in server (AS/U?), setting fragment first/last ON.\n"));
+ first = True;
+ last = True;
+ }
+ }
+
+ if (rhdr.pkt_type == RPC_BINDNACK) {
+ DEBUG(3, ("Bind NACK received on pipe %x!\n", (int)cli->nt_pipe_fnum));
+ prs_mem_free(rdata);
+ return False;
+ }
+
+ if (rhdr.pkt_type == RPC_RESPONSE) {
+ RPC_HDR_RESP rhdr_resp;
+ if(!smb_io_rpc_hdr_resp("rpc_hdr_resp", &rhdr_resp, rdata, 0)) {
+ DEBUG(5,("rpc_api_pipe: failed to unmarshal RPC_HDR_RESP.\n"));
+ prs_mem_free(rdata);
+ return False;
+ }
+ }
+
+ if (rhdr.pkt_type != expected_pkt_type) {
+ DEBUG(3, ("Connection to pipe %x got an unexpected RPC packet type - %d, not %d\n", (int)cli->nt_pipe_fnum, rhdr.pkt_type, expected_pkt_type));
+ prs_mem_free(rdata);
+ return False;
+ }
+
+ DEBUG(5,("rpc_api_pipe: len left: %u smbtrans read: %u\n",
+ (unsigned int)len, (unsigned int)rdata_len ));
+
+ /* check if data to be sent back was too large for one SMBtrans */
+ /* err status is only informational: the _real_ check is on the
+ length */
+
+ if (len > 0) {
+ /* || err == (0x80000000 | STATUS_BUFFER_OVERFLOW)) */
+
+ /* Read the remaining part of the first response fragment */
+
+ if (!rpc_read(cli, rdata, len, &current_offset)) {
+ prs_mem_free(rdata);
+ return False;
+ }
+ }
+
+ /*
+ * Now we have a complete PDU, check the auth struct if any was sent.
+ */
+
+ if(!rpc_auth_pipe(cli, rdata, fragment_start, rhdr.frag_len,
+ rhdr.auth_len, rhdr.pkt_type, &auth_padding_len)) {
+ prs_mem_free(rdata);
+ return False;
+ }
+
+ if (rhdr.auth_len != 0) {
+ /*
+ * Drop the auth footers from the current offset.
+ * We need this if there are more fragments.
+ * The auth footers consist of the auth_data and the
+ * preceeding 8 byte auth_header.
+ */
+ current_offset -= (auth_padding_len + RPC_HDR_AUTH_LEN + rhdr.auth_len);
+ }
+
+ /*
+ * Only one rpc fragment, and it has been read.
+ */
+
+ if (first && last) {
+ DEBUG(6,("rpc_api_pipe: fragment first and last both set\n"));
+ return True;
+ }
+
+ /*
+ * Read more fragments using SMBreadX until we get one with the
+ * last bit set.
+ */
+
+ while (!last) {
+ RPC_HDR_RESP rhdr_resp;
+ int num_read;
+ char hdr_data[RPC_HEADER_LEN+RPC_HDR_RESP_LEN];
+ prs_struct hps;
+ uint8 eclass;
+ uint32 ecode;
+
+ /*
+ * First read the header of the next PDU.
+ */
+
+ prs_init(&hps, 0, cli->mem_ctx, UNMARSHALL);
+ prs_give_memory(&hps, hdr_data, sizeof(hdr_data), False);
+
+ num_read = cli_read(cli, cli->nt_pipe_fnum, hdr_data, 0, RPC_HEADER_LEN+RPC_HDR_RESP_LEN);
+ if (cli_is_dos_error(cli)) {
+ cli_dos_error(cli, &eclass, &ecode);
+ if (eclass != ERRDOS && ecode != ERRmoredata) {
+ DEBUG(0,("rpc_api_pipe: cli_read error : %d/%d\n", eclass, ecode));
+ return False;
+ }
+ }
+
+ DEBUG(5,("rpc_api_pipe: read header (size:%d)\n", num_read));
+
+ if (num_read != RPC_HEADER_LEN+RPC_HDR_RESP_LEN) {
+ DEBUG(0,("rpc_api_pipe: Error : requested %d bytes, got %d.\n",
+ RPC_HEADER_LEN+RPC_HDR_RESP_LEN, num_read ));
+ return False;
+ }
+
+ /* This call sets the endianness in hps. */
+
+ if (!rpc_check_hdr(&hps, &rhdr, &first, &last, &len))
+ return False;
+
+ /* Ensure the endianness in rdata is set correctly - must be same as hps. */
+
+ if (hps.bigendian_data != rdata->bigendian_data) {
+ DEBUG(0,("rpc_api_pipe: Error : Endianness changed from %s to %s\n",
+ rdata->bigendian_data ? "big" : "little",
+ hps.bigendian_data ? "big" : "little" ));
+ return False;
+ }
+
+ if(!smb_io_rpc_hdr_resp("rpc_hdr_resp", &rhdr_resp, &hps, 0)) {
+ DEBUG(0,("rpc_api_pipe: Error in unmarshalling RPC_HDR_RESP.\n"));
+ return False;
+ }
+
+ if (first) {
+ DEBUG(0,("rpc_api_pipe: secondary PDU rpc header has 'first' set !\n"));
+ return False;
+ }
+
+ /*
+ * Now read the rest of the PDU.
+ */
+
+ if (!rpc_read(cli, rdata, len, &current_offset)) {
+ prs_mem_free(rdata);
+ return False;
+ }
+
+ fragment_start = current_offset - len - RPC_HEADER_LEN - RPC_HDR_RESP_LEN;
+
+ /*
+ * Verify any authentication footer.
+ */
+
+
+ if(!rpc_auth_pipe(cli, rdata, fragment_start, rhdr.frag_len,
+ rhdr.auth_len, rhdr.pkt_type, &auth_padding_len)) {
+ prs_mem_free(rdata);
+ return False;
+ }
+
+ if (rhdr.auth_len != 0 ) {
+
+ /*
+ * Drop the auth footers from the current offset.
+ * The auth footers consist of the auth_data and the
+ * preceeding 8 byte auth_header.
+ * We need this if there are more fragments.
+ */
+ current_offset -= (auth_padding_len + RPC_HDR_AUTH_LEN + rhdr.auth_len);
+ }
+ }
+
+ return True;
+}
+
+/*******************************************************************
+ creates a DCE/RPC bind request
+
+ - initialises the parse structure.
+ - dynamically allocates the header data structure
+ - caller is expected to free the header data structure once used.
+
+ ********************************************************************/
+
+static NTSTATUS create_rpc_bind_req(struct cli_state *cli, prs_struct *rpc_out,
+ uint32 rpc_call_id,
+ RPC_IFACE *abstract, RPC_IFACE *transfer,
+ const char *my_name, const char *domain)
+{
+ RPC_HDR hdr;
+ RPC_HDR_RB hdr_rb;
+ RPC_HDR_AUTH hdr_auth;
+ int auth_len = 0;
+ int auth_type, auth_level;
+ size_t saved_hdr_offset = 0;
+
+ prs_struct auth_info;
+ prs_init(&auth_info, RPC_HDR_AUTH_LEN, /* we will need at least this much */
+ prs_get_mem_context(rpc_out), MARSHALL);
+
+ if (cli->pipe_auth_flags) {
+ get_auth_type_level(cli->pipe_auth_flags, &auth_type, &auth_level);
+
+ /*
+ * Create the auth structs we will marshall.
+ */
+
+ init_rpc_hdr_auth(&hdr_auth, auth_type, auth_level, 0x00, 1);
+
+ /*
+ * Now marshall the data into the temporary parse_struct.
+ */
+
+ if(!smb_io_rpc_hdr_auth("hdr_auth", &hdr_auth, &auth_info, 0)) {
+ DEBUG(0,("create_rpc_bind_req: failed to marshall RPC_HDR_AUTH.\n"));
+ prs_mem_free(&auth_info);
+ return NT_STATUS_NO_MEMORY;
+ }
+ saved_hdr_offset = prs_offset(&auth_info);
+ }
+
+ if (cli->pipe_auth_flags & AUTH_PIPE_NTLMSSP) {
+
+ NTSTATUS nt_status;
+ DATA_BLOB null_blob = data_blob(NULL, 0);
+ DATA_BLOB request;
+
+ DEBUG(5, ("Processing NTLMSSP Negotiate\n"));
+ nt_status = ntlmssp_update(cli->ntlmssp_pipe_state,
+ null_blob,
+ &request);
+
+ if (!NT_STATUS_EQUAL(nt_status,
+ NT_STATUS_MORE_PROCESSING_REQUIRED)) {
+ prs_mem_free(&auth_info);
+ return nt_status;
+ }
+
+ /* Auth len in the rpc header doesn't include auth_header. */
+ auth_len = request.length;
+ prs_copy_data_in(&auth_info, (char *)request.data, request.length);
+
+ DEBUG(5, ("NTLMSSP Negotiate:\n"));
+ dump_data(5, (const char *)request.data, request.length);
+
+ data_blob_free(&request);
+
+ } else if (cli->pipe_auth_flags & AUTH_PIPE_NETSEC) {
+ RPC_AUTH_NETSEC_NEG netsec_neg;
+
+ /* Use lp_workgroup() if domain not specified */
+
+ if (!domain || !domain[0]) {
+ DEBUG(10,("create_rpc_bind_req: no domain; assuming my own\n"));
+ domain = lp_workgroup();
+ }
+
+ init_rpc_auth_netsec_neg(&netsec_neg, domain, my_name);
+
+ /*
+ * Now marshall the data into the temporary parse_struct.
+ */
+
+ if(!smb_io_rpc_auth_netsec_neg("netsec_neg",
+ &netsec_neg, &auth_info, 0)) {
+ DEBUG(0,("Failed to marshall RPC_AUTH_NETSEC_NEG.\n"));
+ prs_mem_free(&auth_info);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ /* Auth len in the rpc header doesn't include auth_header. */
+ auth_len = prs_offset(&auth_info) - saved_hdr_offset;
+ }
+
+ /* Create the request RPC_HDR */
+ init_rpc_hdr(&hdr, RPC_BIND, 0x3, rpc_call_id,
+ RPC_HEADER_LEN + RPC_HDR_RB_LEN + prs_offset(&auth_info),
+ auth_len);
+
+ if(!smb_io_rpc_hdr("hdr" , &hdr, rpc_out, 0)) {
+ DEBUG(0,("create_rpc_bind_req: failed to marshall RPC_HDR.\n"));
+ prs_mem_free(&auth_info);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ /* create the bind request RPC_HDR_RB */
+ init_rpc_hdr_rb(&hdr_rb, MAX_PDU_FRAG_LEN, MAX_PDU_FRAG_LEN, 0x0,
+ 0x1, 0x0, 0x1, abstract, transfer);
+
+ /* Marshall the bind request data */
+ if(!smb_io_rpc_hdr_rb("", &hdr_rb, rpc_out, 0)) {
+ DEBUG(0,("create_rpc_bind_req: failed to marshall RPC_HDR_RB.\n"));
+ prs_mem_free(&auth_info);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ /*
+ * Grow the outgoing buffer to store any auth info.
+ */
+
+ if(auth_len != 0) {
+ if(!prs_append_prs_data( rpc_out, &auth_info)) {
+ DEBUG(0,("create_rpc_bind_req: failed to grow parse struct to add auth.\n"));
+ prs_mem_free(&auth_info);
+ return NT_STATUS_NO_MEMORY;
+ }
+ }
+ prs_mem_free(&auth_info);
+ return NT_STATUS_OK;
+}
+
+/*******************************************************************
+ Creates a DCE/RPC bind authentication response.
+ This is the packet that is sent back to the server once we
+ have received a BIND-ACK, to finish the third leg of
+ the authentication handshake.
+ ********************************************************************/
+
+static NTSTATUS create_rpc_bind_resp(struct cli_state *cli,
+ uint32 rpc_call_id,
+ prs_struct *rpc_out)
+{
+ NTSTATUS nt_status;
+ RPC_HDR hdr;
+ RPC_HDR_AUTHA hdr_autha;
+ DATA_BLOB ntlmssp_null_response = data_blob(NULL, 0);
+ DATA_BLOB ntlmssp_reply;
+ int auth_type, auth_level;
+
+ /* The response is picked up from the internal cache,
+ where it was placed by the rpc_auth_pipe() code */
+ nt_status = ntlmssp_update(cli->ntlmssp_pipe_state,
+ ntlmssp_null_response,
+ &ntlmssp_reply);
+
+ if (!NT_STATUS_EQUAL(nt_status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
+ return nt_status;
+ }
+
+ /* Create the request RPC_HDR */
+ init_rpc_hdr(&hdr, RPC_BINDRESP, 0x0, rpc_call_id,
+ RPC_HEADER_LEN + RPC_HDR_AUTHA_LEN + ntlmssp_reply.length,
+ ntlmssp_reply.length );
+
+ /* Marshall it. */
+ if(!smb_io_rpc_hdr("hdr", &hdr, rpc_out, 0)) {
+ DEBUG(0,("create_rpc_bind_resp: failed to marshall RPC_HDR.\n"));
+ data_blob_free(&ntlmssp_reply);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ get_auth_type_level(cli->pipe_auth_flags, &auth_type, &auth_level);
+
+ /* Create the request RPC_HDR_AUTHA */
+ init_rpc_hdr_autha(&hdr_autha, MAX_PDU_FRAG_LEN, MAX_PDU_FRAG_LEN,
+ auth_type, auth_level, 0x00);
+
+ if(!smb_io_rpc_hdr_autha("hdr_autha", &hdr_autha, rpc_out, 0)) {
+ DEBUG(0,("create_rpc_bind_resp: failed to marshall RPC_HDR_AUTHA.\n"));
+ data_blob_free(&ntlmssp_reply);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ /*
+ * Append the auth data to the outgoing buffer.
+ */
+
+ if(!prs_copy_data_in(rpc_out, (char *)ntlmssp_reply.data, ntlmssp_reply.length)) {
+ DEBUG(0,("create_rpc_bind_req: failed to grow parse struct to add auth.\n"));
+ data_blob_free(&ntlmssp_reply);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ data_blob_free(&ntlmssp_reply);
+ return NT_STATUS_OK;
+}
+
+
+/*******************************************************************
+ Creates a DCE/RPC request.
+ ********************************************************************/
+
+static uint32 create_rpc_request(prs_struct *rpc_out, uint8 op_num, int data_len, int auth_len, uint8 flags, uint32 oldid, uint32 data_left)
+{
+ uint32 alloc_hint;
+ RPC_HDR hdr;
+ RPC_HDR_REQ hdr_req;
+ uint32 callid = oldid ? oldid : get_rpc_call_id();
+
+ DEBUG(5,("create_rpc_request: opnum: 0x%x data_len: 0x%x\n", op_num, data_len));
+
+ /* create the rpc header RPC_HDR */
+ init_rpc_hdr(&hdr, RPC_REQUEST, flags,
+ callid, data_len, auth_len);
+
+ /*
+ * The alloc hint should be the amount of data, not including
+ * RPC headers & footers.
+ */
+
+ if (auth_len != 0)
+ alloc_hint = data_len - RPC_HEADER_LEN - RPC_HDR_AUTH_LEN - auth_len;
+ else
+ alloc_hint = data_len - RPC_HEADER_LEN;
+
+ DEBUG(10,("create_rpc_request: data_len: %x auth_len: %x alloc_hint: %x\n",
+ data_len, auth_len, alloc_hint));
+
+ /* Create the rpc request RPC_HDR_REQ */
+ init_rpc_hdr_req(&hdr_req, alloc_hint, op_num);
+
+ /* stream-time... */
+ if(!smb_io_rpc_hdr("hdr ", &hdr, rpc_out, 0))
+ return 0;
+
+ if(!smb_io_rpc_hdr_req("hdr_req", &hdr_req, rpc_out, 0))
+ return 0;
+
+ if (prs_offset(rpc_out) != RPC_HEADER_LEN + RPC_HDR_REQ_LEN)
+ return 0;
+
+ return callid;
+}
+
+/*******************************************************************
+ Puts an auth header into an rpc request.
+ ********************************************************************/
+
+static BOOL create_auth_hdr(prs_struct *outgoing_packet,
+ int auth_type,
+ int auth_level, int padding)
+{
+ RPC_HDR_AUTH hdr_auth;
+
+ init_rpc_hdr_auth(&hdr_auth, auth_type, auth_level,
+ padding, 1);
+ if(!smb_io_rpc_hdr_auth("hdr_auth", &hdr_auth,
+ outgoing_packet, 0)) {
+ DEBUG(0,("create_auth_hdr:Failed to marshal RPC_HDR_AUTH.\n"));
+ return False;
+ }
+ return True;
+}
+
+/**
+ * Send a request on an RPC pipe and get a response.
+ *
+ * @param data NDR contents of the request to be sent.
+ * @param rdata Unparsed NDR response data.
+**/
+
+BOOL rpc_api_pipe_req(struct cli_state *cli, uint8 op_num,
+ prs_struct *data, prs_struct *rdata)
+{
+ uint32 auth_len, real_auth_len, auth_hdr_len, max_data, data_left, data_sent;
+ NTSTATUS nt_status;
+ BOOL ret = False;
+ uint32 callid = 0;
+ fstring dump_name;
+
+ auth_len = 0;
+ real_auth_len = 0;
+ auth_hdr_len = 0;
+
+ if (cli->pipe_auth_flags & AUTH_PIPE_SIGN) {
+ if (cli->pipe_auth_flags & AUTH_PIPE_NTLMSSP) {
+ auth_len = RPC_AUTH_NTLMSSP_CHK_LEN;
+ }
+ if (cli->pipe_auth_flags & AUTH_PIPE_NETSEC) {
+ auth_len = RPC_AUTH_NETSEC_CHK_LEN;
+ }
+ auth_hdr_len = RPC_HDR_AUTH_LEN;
+ }
+
+ /*
+ * calc how much actual data we can send in a PDU fragment
+ */
+ max_data = cli->max_xmit_frag - RPC_HEADER_LEN - RPC_HDR_REQ_LEN -
+ auth_hdr_len - auth_len - 8;
+
+ for (data_left = prs_offset(data), data_sent = 0; data_left > 0;) {
+ prs_struct outgoing_packet;
+ prs_struct sec_blob;
+ uint32 data_len, send_size;
+ uint8 flags = 0;
+ uint32 auth_padding = 0;
+ DATA_BLOB sign_blob;
+
+ /*
+ * how much will we send this time
+ */
+ send_size = MIN(data_left, max_data);
+
+ if (!prs_init(&sec_blob, send_size, /* will need at least this much */
+ cli->mem_ctx, MARSHALL)) {
+ DEBUG(0,("Could not malloc %u bytes",
+ send_size+auth_padding));
+ return False;
+ }
+
+ if(!prs_append_some_prs_data(&sec_blob, data,
+ data_sent, send_size)) {
+ DEBUG(0,("Failed to append data to netsec blob\n"));
+ prs_mem_free(&sec_blob);
+ return False;
+ }
+
+ /*
+ * NT expects the data that is sealed to be 8-byte
+ * aligned. The padding must be encrypted as well and
+ * taken into account when generating the
+ * authentication verifier. The amount of padding must
+ * be stored in the auth header.
+ */
+
+ if (cli->pipe_auth_flags) {
+ size_t data_and_padding_size;
+ int auth_type;
+ int auth_level;
+ prs_align_uint64(&sec_blob);
+
+ get_auth_type_level(cli->pipe_auth_flags, &auth_type, &auth_level);
+
+ data_and_padding_size = prs_offset(&sec_blob);
+ auth_padding = data_and_padding_size - send_size;
+
+ /* insert the auth header */
+
+ if(!create_auth_hdr(&sec_blob, auth_type, auth_level, auth_padding)) {
+ prs_mem_free(&sec_blob);
+ return False;
+ }
+
+ /* create an NTLMSSP signature */
+ if (cli->pipe_auth_flags & AUTH_PIPE_NTLMSSP) {
+ /*
+ * Seal the outgoing data if requested.
+ */
+ if (cli->pipe_auth_flags & AUTH_PIPE_SEAL) {
+
+ nt_status = ntlmssp_seal_packet(cli->ntlmssp_pipe_state,
+ (unsigned char*)prs_data_p(&sec_blob),
+ data_and_padding_size,
+ &sign_blob);
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ prs_mem_free(&sec_blob);
+ return False;
+ }
+ }
+ else if (cli->pipe_auth_flags & AUTH_PIPE_SIGN) {
+
+ nt_status = ntlmssp_sign_packet(cli->ntlmssp_pipe_state,
+ (unsigned char*)prs_data_p(&sec_blob),
+ data_and_padding_size, &sign_blob);
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ prs_mem_free(&sec_blob);
+ return False;
+ }
+ }
+
+
+ /* write auth footer onto the packet */
+ real_auth_len = sign_blob.length;
+
+ prs_copy_data_in(&sec_blob, (char *)sign_blob.data, sign_blob.length);
+ data_blob_free(&sign_blob);
+
+ }
+ else if (cli->pipe_auth_flags & AUTH_PIPE_NETSEC) {
+ size_t parse_offset_marker;
+ RPC_AUTH_NETSEC_CHK verf;
+ DEBUG(10,("SCHANNEL seq_num=%d\n", cli->auth_info.seq_num));
+
+ netsec_encode(&cli->auth_info,
+ cli->pipe_auth_flags,
+ SENDER_IS_INITIATOR,
+ &verf,
+ prs_data_p(&sec_blob),
+ data_and_padding_size);
+
+ cli->auth_info.seq_num++;
+
+ /* write auth footer onto the packet */
+
+ parse_offset_marker = prs_offset(&sec_blob);
+ if (!smb_io_rpc_auth_netsec_chk("", &verf,
+ &sec_blob, 0)) {
+ prs_mem_free(&sec_blob);
+ return False;
+ }
+ real_auth_len = prs_offset(&sec_blob) - parse_offset_marker;
+ }
+ }
+
+ data_len = RPC_HEADER_LEN + RPC_HDR_REQ_LEN + prs_offset(&sec_blob);
+
+ /*
+ * Malloc parse struct to hold it (and enough for alignments).
+ */
+ if(!prs_init(&outgoing_packet, data_len + 8,
+ cli->mem_ctx, MARSHALL)) {
+ DEBUG(0,("rpc_api_pipe_req: Failed to malloc %u bytes.\n", (unsigned int)data_len ));
+ return False;
+ }
+
+ if (data_left == prs_offset(data))
+ flags |= RPC_FLG_FIRST;
+
+ if (data_left <= max_data)
+ flags |= RPC_FLG_LAST;
+ /*
+ * Write out the RPC header and the request header.
+ */
+ if(!(callid = create_rpc_request(&outgoing_packet, op_num,
+ data_len, real_auth_len, flags,
+ callid, data_left))) {
+ DEBUG(0,("rpc_api_pipe_req: Failed to create RPC request.\n"));
+ prs_mem_free(&outgoing_packet);
+ prs_mem_free(&sec_blob);
+ return False;
+ }
+
+ prs_append_prs_data(&outgoing_packet, &sec_blob);
+ prs_mem_free(&sec_blob);
+
+ DEBUG(100,("data_len: %x data_calc_len: %x\n", data_len,
+ prs_offset(&outgoing_packet)));
+
+ if (flags & RPC_FLG_LAST)
+ ret = rpc_api_pipe(cli, &outgoing_packet,
+ rdata, RPC_RESPONSE);
+ else {
+ cli_write(cli, cli->nt_pipe_fnum, 0x0008,
+ prs_data_p(&outgoing_packet),
+ data_sent, data_len);
+ }
+ prs_mem_free(&outgoing_packet);
+ data_sent += send_size;
+ data_left -= send_size;
+ }
+ /* Also capture received data */
+ slprintf(dump_name, sizeof(dump_name) - 1, "reply_%s",
+ cli_pipe_get_name(cli));
+ prs_dump(dump_name, op_num, rdata);
+
+ return ret;
+}
+
+/****************************************************************************
+ Set the handle state.
+****************************************************************************/
+
+static BOOL rpc_pipe_set_hnd_state(struct cli_state *cli, const char *pipe_name, uint16 device_state)
+{
+ BOOL state_set = False;
+ char param[2];
+ uint16 setup[2]; /* only need 2 uint16 setup parameters */
+ char *rparam = NULL;
+ char *rdata = NULL;
+ uint32 rparam_len, rdata_len;
+
+ if (pipe_name == NULL)
+ return False;
+
+ DEBUG(5,("Set Handle state Pipe[%x]: %s - device state:%x\n",
+ cli->nt_pipe_fnum, pipe_name, device_state));
+
+ /* create parameters: device state */
+ SSVAL(param, 0, device_state);
+
+ /* create setup parameters. */
+ setup[0] = 0x0001;
+ setup[1] = cli->nt_pipe_fnum; /* pipe file handle. got this from an SMBOpenX. */
+
+ /* send the data on \PIPE\ */
+ if (cli_api_pipe(cli, "\\PIPE\\",
+ setup, 2, 0, /* setup, length, max */
+ param, 2, 0, /* param, length, max */
+ NULL, 0, 1024, /* data, length, max */
+ &rparam, &rparam_len, /* return param, length */
+ &rdata, &rdata_len)) /* return data, length */
+ {
+ DEBUG(5, ("Set Handle state: return OK\n"));
+ state_set = True;
+ }
+
+ SAFE_FREE(rparam);
+ SAFE_FREE(rdata);
+
+ return state_set;
+}
+
+/****************************************************************************
+ check the rpc bind acknowledge response
+****************************************************************************/
+
+int get_pipe_index( const char *pipe_name )
+{
+ int pipe_idx = 0;
+
+ while (pipe_names[pipe_idx].client_pipe != NULL) {
+ if (strequal(pipe_name, pipe_names[pipe_idx].client_pipe ))
+ return pipe_idx;
+ pipe_idx++;
+ };
+
+ return -1;
+}
+
+
+/****************************************************************************
+ check the rpc bind acknowledge response
+****************************************************************************/
+
+const char* get_pipe_name_from_index( const int pipe_index )
+{
+
+ if ( (pipe_index < 0) || (pipe_index >= PI_MAX_PIPES) )
+ return NULL;
+
+ return pipe_names[pipe_index].client_pipe;
+}
+
+/****************************************************************************
+ Check to see if this pipe index points to one of
+ the pipes only supported by Win2k
+ ****************************************************************************/
+
+BOOL is_win2k_pipe( const int pipe_idx )
+{
+ switch ( pipe_idx )
+ {
+ case PI_LSARPC_DS:
+ return True;
+ }
+
+ return False;
+}
+
+/****************************************************************************
+ check the rpc bind acknowledge response
+****************************************************************************/
+
+static BOOL valid_pipe_name(const int pipe_idx, RPC_IFACE *abstract, RPC_IFACE *transfer)
+{
+ if ( pipe_idx >= PI_MAX_PIPES ) {
+ DEBUG(0,("valid_pipe_name: Programmer error! Invalid pipe index [%d]\n",
+ pipe_idx));
+ return False;
+ }
+
+ DEBUG(5,("Bind Abstract Syntax: "));
+ dump_data(5, (char*)&(pipe_names[pipe_idx].abstr_syntax),
+ sizeof(pipe_names[pipe_idx].abstr_syntax));
+ DEBUG(5,("Bind Transfer Syntax: "));
+ dump_data(5, (char*)&(pipe_names[pipe_idx].trans_syntax),
+ sizeof(pipe_names[pipe_idx].trans_syntax));
+
+ /* copy the required syntaxes out so we can do the right bind */
+
+ *transfer = pipe_names[pipe_idx].trans_syntax;
+ *abstract = pipe_names[pipe_idx].abstr_syntax;
+
+ return True;
+}
+
+/****************************************************************************
+ check the rpc bind acknowledge response
+****************************************************************************/
+
+static BOOL check_bind_response(RPC_HDR_BA *hdr_ba, const int pipe_idx, RPC_IFACE *transfer)
+{
+# if 0 /* JERRY -- apparently ASU forgets to fill in the server pipe name sometimes */
+ if ( hdr_ba->addr.len <= 0)
+ return False;
+
+ if ( !strequal(hdr_ba->addr.str, pipe_names[pipe_idx].client_pipe) &&
+ !strequal(hdr_ba->addr.str, pipe_names[pipe_idx].server_pipe) )
+ {
+ DEBUG(4,("bind_rpc_pipe: pipe_name %s != expected pipe %s. oh well!\n",
+ pipe_names[i].server_pipe ,hdr_ba->addr.str));
+ return False;
+ }
+
+ DEBUG(5,("bind_rpc_pipe: server pipe_name found: %s\n", pipe_names[i].server_pipe ));
+
+ if (pipe_names[pipe_idx].server_pipe == NULL) {
+ DEBUG(2,("bind_rpc_pipe: pipe name %s unsupported\n", hdr_ba->addr.str));
+ return False;
+ }
+#endif /* JERRY */
+
+ /* check the transfer syntax */
+ if ((hdr_ba->transfer.version != transfer->version) ||
+ (memcmp(&hdr_ba->transfer.uuid, &transfer->uuid, sizeof(transfer->uuid)) !=0)) {
+ DEBUG(2,("bind_rpc_pipe: transfer syntax differs\n"));
+ return False;
+ }
+
+ /* lkclXXXX only accept one result: check the result(s) */
+ if (hdr_ba->res.num_results != 0x1 || hdr_ba->res.result != 0) {
+ DEBUG(2,("bind_rpc_pipe: bind denied results: %d reason: %x\n",
+ hdr_ba->res.num_results, hdr_ba->res.reason));
+ }
+
+ DEBUG(5,("bind_rpc_pipe: accepted!\n"));
+ return True;
+}
+
+/****************************************************************************
+ Create and send the third packet in an RPC auth.
+****************************************************************************/
+
+static BOOL rpc_send_auth_reply(struct cli_state *cli, prs_struct *rdata, uint32 rpc_call_id)
+{
+ prs_struct rpc_out;
+ ssize_t ret;
+
+ prs_init(&rpc_out, RPC_HEADER_LEN + RPC_HDR_AUTHA_LEN, /* need at least this much */
+ cli->mem_ctx, MARSHALL);
+
+ if (!NT_STATUS_IS_OK(create_rpc_bind_resp(cli, rpc_call_id,
+ &rpc_out))) {
+ return False;
+ }
+
+ if ((ret = cli_write(cli, cli->nt_pipe_fnum, 0x8, prs_data_p(&rpc_out),
+ 0, (size_t)prs_offset(&rpc_out))) != (ssize_t)prs_offset(&rpc_out)) {
+ DEBUG(0,("rpc_send_auth_reply: cli_write failed. Return was %d\n", (int)ret));
+ prs_mem_free(&rpc_out);
+ return False;
+ }
+
+ prs_mem_free(&rpc_out);
+ return True;
+}
+
+/****************************************************************************
+ Do an rpc bind.
+****************************************************************************/
+
+static BOOL rpc_pipe_bind(struct cli_state *cli, int pipe_idx, const char *my_name)
+{
+ RPC_IFACE abstract;
+ RPC_IFACE transfer;
+ prs_struct rpc_out;
+ prs_struct rdata;
+ uint32 rpc_call_id;
+ char buffer[MAX_PDU_FRAG_LEN];
+
+ if ( (pipe_idx < 0) || (pipe_idx >= PI_MAX_PIPES) )
+ return False;
+
+ DEBUG(5,("Bind RPC Pipe[%x]: %s\n", cli->nt_pipe_fnum, pipe_names[pipe_idx].client_pipe));
+
+ if (!valid_pipe_name(pipe_idx, &abstract, &transfer))
+ return False;
+
+ prs_init(&rpc_out, 0, cli->mem_ctx, MARSHALL);
+
+ /*
+ * Use the MAX_PDU_FRAG_LEN buffer to store the bind request.
+ */
+
+ prs_give_memory( &rpc_out, buffer, sizeof(buffer), False);
+
+ rpc_call_id = get_rpc_call_id();
+
+ if (cli->pipe_auth_flags & AUTH_PIPE_NTLMSSP) {
+ NTSTATUS nt_status;
+ fstring password;
+
+ DEBUG(5, ("NTLMSSP authenticated pipe selected\n"));
+
+ nt_status = ntlmssp_client_start(&cli->ntlmssp_pipe_state);
+
+ if (!NT_STATUS_IS_OK(nt_status))
+ return False;
+
+ /* Currently the NTLMSSP code does not implement NTLM2 correctly for signing or sealing */
+
+ cli->ntlmssp_pipe_state->neg_flags &= ~NTLMSSP_NEGOTIATE_NTLM2;
+
+ nt_status = ntlmssp_set_username(cli->ntlmssp_pipe_state,
+ cli->user_name);
+ if (!NT_STATUS_IS_OK(nt_status))
+ return False;
+
+ nt_status = ntlmssp_set_domain(cli->ntlmssp_pipe_state,
+ cli->domain);
+ if (!NT_STATUS_IS_OK(nt_status))
+ return False;
+
+ if (cli->pwd.null_pwd) {
+ nt_status = ntlmssp_set_password(cli->ntlmssp_pipe_state,
+ NULL);
+ if (!NT_STATUS_IS_OK(nt_status))
+ return False;
+ } else {
+ pwd_get_cleartext(&cli->pwd, password);
+ nt_status = ntlmssp_set_password(cli->ntlmssp_pipe_state,
+ password);
+ if (!NT_STATUS_IS_OK(nt_status))
+ return False;
+ }
+
+ if (cli->pipe_auth_flags & AUTH_PIPE_SIGN) {
+ cli->ntlmssp_pipe_state->neg_flags |= NTLMSSP_NEGOTIATE_SIGN;
+ }
+
+ if (cli->pipe_auth_flags & AUTH_PIPE_SEAL) {
+ cli->ntlmssp_pipe_state->neg_flags |= NTLMSSP_NEGOTIATE_SEAL;
+ }
+ } else if (cli->pipe_auth_flags & AUTH_PIPE_NETSEC) {
+ cli->auth_info.seq_num = 0;
+ }
+
+ /* Marshall the outgoing data. */
+ create_rpc_bind_req(cli, &rpc_out, rpc_call_id,
+ &abstract, &transfer,
+ global_myname(), cli->domain);
+
+ /* Initialize the incoming data struct. */
+ prs_init(&rdata, 0, cli->mem_ctx, UNMARSHALL);
+
+ /* send data on \PIPE\. receive a response */
+ if (rpc_api_pipe(cli, &rpc_out, &rdata, RPC_BINDACK)) {
+ RPC_HDR_BA hdr_ba;
+
+ DEBUG(5, ("rpc_pipe_bind: rpc_api_pipe returned OK.\n"));
+
+ if(!smb_io_rpc_hdr_ba("", &hdr_ba, &rdata, 0)) {
+ DEBUG(0,("rpc_pipe_bind: Failed to unmarshall RPC_HDR_BA.\n"));
+ prs_mem_free(&rdata);
+ return False;
+ }
+
+ if(!check_bind_response(&hdr_ba, pipe_idx, &transfer)) {
+ DEBUG(2,("rpc_pipe_bind: check_bind_response failed.\n"));
+ prs_mem_free(&rdata);
+ return False;
+ }
+
+ cli->max_xmit_frag = hdr_ba.bba.max_tsize;
+ cli->max_recv_frag = hdr_ba.bba.max_rsize;
+
+ /*
+ * If we're doing NTLMSSP auth we need to send a reply to
+ * the bind-ack to complete the 3-way challenge response
+ * handshake.
+ */
+
+ if ((cli->pipe_auth_flags & AUTH_PIPE_NTLMSSP)
+ && !rpc_send_auth_reply(cli, &rdata, rpc_call_id)) {
+ DEBUG(0,("rpc_pipe_bind: rpc_send_auth_reply failed.\n"));
+ prs_mem_free(&rdata);
+ return False;
+ }
+ prs_mem_free(&rdata);
+ return True;
+ }
+
+ return False;
+}
+
+/****************************************************************************
+ Open a session.
+ ****************************************************************************/
+
+BOOL cli_nt_session_open(struct cli_state *cli, const int pipe_idx)
+{
+ int fnum;
+
+ /* At the moment we can't have more than one pipe open over
+ a cli connection. )-: */
+
+ SMB_ASSERT(cli->nt_pipe_fnum == 0);
+
+ /* The pipe index must fall within our array */
+
+ SMB_ASSERT((pipe_idx >= 0) && (pipe_idx < PI_MAX_PIPES));
+
+ if (cli->capabilities & CAP_NT_SMBS) {
+ if ((fnum = cli_nt_create(cli, &pipe_names[pipe_idx].client_pipe[5], DESIRED_ACCESS_PIPE)) == -1) {
+ DEBUG(0,("cli_nt_session_open: cli_nt_create failed on pipe %s to machine %s. Error was %s\n",
+ &pipe_names[pipe_idx].client_pipe[5], cli->desthost, cli_errstr(cli)));
+ return False;
+ }
+
+ cli->nt_pipe_fnum = (uint16)fnum;
+ } else {
+ if ((fnum = cli_open(cli, pipe_names[pipe_idx].client_pipe, O_CREAT|O_RDWR, DENY_NONE)) == -1) {
+ DEBUG(1,("cli_nt_session_open: cli_open failed on pipe %s to machine %s. Error was %s\n",
+ pipe_names[pipe_idx].client_pipe, cli->desthost, cli_errstr(cli)));
+ return False;
+ }
+
+ cli->nt_pipe_fnum = (uint16)fnum;
+
+ /**************** Set Named Pipe State ***************/
+ if (!rpc_pipe_set_hnd_state(cli, pipe_names[pipe_idx].client_pipe, 0x4300)) {
+ DEBUG(0,("cli_nt_session_open: pipe hnd state failed. Error was %s\n",
+ cli_errstr(cli)));
+ cli_close(cli, cli->nt_pipe_fnum);
+ cli->nt_pipe_fnum = 0;
+ return False;
+ }
+ }
+
+ /******************* bind request on pipe *****************/
+
+ if (!rpc_pipe_bind(cli, pipe_idx, global_myname())) {
+ DEBUG(2,("cli_nt_session_open: rpc bind to %s failed\n",
+ get_pipe_name_from_index(pipe_idx)));
+ cli_close(cli, cli->nt_pipe_fnum);
+ cli->nt_pipe_fnum = 0;
+ return False;
+ }
+
+ cli->pipe_idx = pipe_idx;
+
+ /*
+ * Setup the remote server name prefixed by \ and the machine account name.
+ */
+
+ fstrcpy(cli->srv_name_slash, "\\\\");
+ fstrcat(cli->srv_name_slash, cli->desthost);
+ strupper_m(cli->srv_name_slash);
+
+ fstrcpy(cli->clnt_name_slash, "\\\\");
+ fstrcat(cli->clnt_name_slash, global_myname());
+ strupper_m(cli->clnt_name_slash);
+
+ fstrcpy(cli->mach_acct, global_myname());
+ fstrcat(cli->mach_acct, "$");
+ strupper_m(cli->mach_acct);
+
+ /* Remember which pipe we're talking to */
+ fstrcpy(cli->pipe_name, pipe_names[pipe_idx].client_pipe);
+
+ return True;
+}
+
+
+/****************************************************************************
+ Open a session to the NETLOGON pipe using schannel.
+
+ (Assumes that the netlogon pipe is already open)
+ ****************************************************************************/
+
+NTSTATUS cli_nt_establish_netlogon(struct cli_state *cli, int sec_chan,
+ const uchar trust_password[16])
+{
+ NTSTATUS result;
+ uint32 neg_flags = NETLOGON_NEG_AUTH2_FLAGS;
+ int fnum;
+
+ cli_nt_netlogon_netsec_session_close(cli);
+
+ if (lp_client_schannel() != False)
+ neg_flags |= NETLOGON_NEG_SCHANNEL;
+
+ result = cli_nt_setup_creds(cli, sec_chan, trust_password,
+ &neg_flags, 2);
+
+ if (!NT_STATUS_IS_OK(result)) {
+ cli_nt_session_close(cli);
+ return result;
+ }
+
+ if ((lp_client_schannel() == True) &&
+ ((neg_flags & NETLOGON_NEG_SCHANNEL) == 0)) {
+
+ DEBUG(3, ("Server did not offer schannel\n"));
+ cli_nt_session_close(cli);
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ if ((lp_client_schannel() == False) ||
+ ((neg_flags & NETLOGON_NEG_SCHANNEL) == 0)) {
+ return NT_STATUS_OK;
+
+ /* keep the existing connection to NETLOGON open */
+
+ }
+
+ /* Server offered schannel, so try it. */
+
+ memcpy(cli->auth_info.sess_key, cli->sess_key,
+ sizeof(cli->auth_info.sess_key));
+
+ cli->saved_netlogon_pipe_fnum = cli->nt_pipe_fnum;
+
+ cli->pipe_auth_flags = AUTH_PIPE_NETSEC;
+ cli->pipe_auth_flags |= AUTH_PIPE_SIGN;
+ cli->pipe_auth_flags |= AUTH_PIPE_SEAL;
+
+ if (cli->capabilities & CAP_NT_SMBS) {
+
+ /* The secure channel connection must be opened on the same
+ session (TCP connection) as the one the challenge was
+ requested from. */
+ if ((fnum = cli_nt_create(cli, PIPE_NETLOGON_PLAIN,
+ DESIRED_ACCESS_PIPE)) == -1) {
+ DEBUG(0,("cli_nt_create failed to %s machine %s. "
+ "Error was %s\n",
+ PIPE_NETLOGON, cli->desthost,
+ cli_errstr(cli)));
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ cli->nt_pipe_fnum = (uint16)fnum;
+ } else {
+ if ((fnum = cli_open(cli, PIPE_NETLOGON,
+ O_CREAT|O_RDWR, DENY_NONE)) == -1) {
+ DEBUG(0,("cli_open failed on pipe %s to machine %s. "
+ "Error was %s\n",
+ PIPE_NETLOGON, cli->desthost,
+ cli_errstr(cli)));
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ cli->nt_pipe_fnum = (uint16)fnum;
+
+ /**************** Set Named Pipe State ***************/
+ if (!rpc_pipe_set_hnd_state(cli, PIPE_NETLOGON, 0x4300)) {
+ DEBUG(0,("Pipe hnd state failed. Error was %s\n",
+ cli_errstr(cli)));
+ cli_close(cli, cli->nt_pipe_fnum);
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+ }
+
+ if (!rpc_pipe_bind(cli, PI_NETLOGON, global_myname())) {
+ DEBUG(2,("rpc bind to %s failed\n", PIPE_NETLOGON));
+ cli_close(cli, cli->nt_pipe_fnum);
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ return NT_STATUS_OK;
+}
+
+
+NTSTATUS cli_nt_setup_netsec(struct cli_state *cli, int sec_chan, int auth_flags,
+ const uchar trust_password[16])
+{
+ NTSTATUS result;
+ uint32 neg_flags = NETLOGON_NEG_AUTH2_FLAGS;
+ cli->pipe_auth_flags = 0;
+
+ if (lp_client_schannel() == False) {
+ return NT_STATUS_OK;
+ }
+
+ if (!cli_nt_session_open(cli, PI_NETLOGON)) {
+ DEBUG(0, ("Could not initialise %s\n",
+ get_pipe_name_from_index(PI_NETLOGON)));
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ if (lp_client_schannel() != False)
+ neg_flags |= NETLOGON_NEG_SCHANNEL;
+
+ neg_flags |= NETLOGON_NEG_SCHANNEL;
+
+ result = cli_nt_setup_creds(cli, sec_chan, trust_password,
+ &neg_flags, 2);
+
+ if (!(neg_flags & NETLOGON_NEG_SCHANNEL)
+ && lp_client_schannel() == True) {
+ DEBUG(1, ("Could not negotiate SCHANNEL with the DC!\n"));
+ result = NT_STATUS_UNSUCCESSFUL;
+ }
+
+ if (!NT_STATUS_IS_OK(result)) {
+ ZERO_STRUCT(cli->auth_info.sess_key);
+ ZERO_STRUCT(cli->sess_key);
+ cli->pipe_auth_flags = 0;
+ cli_nt_session_close(cli);
+ return result;
+ }
+
+ memcpy(cli->auth_info.sess_key, cli->sess_key,
+ sizeof(cli->auth_info.sess_key));
+
+ cli->saved_netlogon_pipe_fnum = cli->nt_pipe_fnum;
+ cli->nt_pipe_fnum = 0;
+
+ /* doing schannel, not per-user auth */
+ cli->pipe_auth_flags = auth_flags;
+
+ return NT_STATUS_OK;
+}
+
+const char *cli_pipe_get_name(struct cli_state *cli)
+{
+ return cli->pipe_name;
+}
+
+
diff --git a/source/rpc_client/cli_reg.c b/source/rpc_client/cli_reg.c
new file mode 100644
index 00000000000..5cfbf68fb34
--- /dev/null
+++ b/source/rpc_client/cli_reg.c
@@ -0,0 +1,103 @@
+/*
+ Unix SMB/CIFS implementation.
+ RPC Pipe client
+
+ Copyright (C) Andrew Tridgell 1992-1998,
+ Copyright (C) Luke Kenneth Casson Leighton 1996-1998,
+ Copyright (C) Paul Ashton 1997-1998.
+ Copyright (C) Jeremy Allison 1999.
+ Copyright (C) Simo Sorce 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 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"
+
+/* Shutdown a server */
+
+NTSTATUS cli_reg_shutdown(struct cli_state * cli, TALLOC_CTX *mem_ctx,
+ const char *msg, uint32 timeout, BOOL do_reboot,
+ BOOL force)
+{
+ prs_struct qbuf;
+ prs_struct rbuf;
+ REG_Q_SHUTDOWN q_s;
+ REG_R_SHUTDOWN r_s;
+ NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+
+ if (msg == NULL) return NT_STATUS_INVALID_PARAMETER;
+
+ ZERO_STRUCT (q_s);
+ ZERO_STRUCT (r_s);
+
+ prs_init(&qbuf , MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL);
+ prs_init(&rbuf, 0, mem_ctx, UNMARSHALL);
+
+ /* Marshall data and send request */
+
+ init_reg_q_shutdown(&q_s, msg, timeout, do_reboot, force);
+
+ if (!reg_io_q_shutdown("", &q_s, &qbuf, 0) ||
+ !rpc_api_pipe_req(cli, REG_SHUTDOWN, &qbuf, &rbuf))
+ goto done;
+
+ /* Unmarshall response */
+
+ if(reg_io_r_shutdown("", &r_s, &rbuf, 0))
+ result = r_s.status;
+
+done:
+ prs_mem_free(&rbuf);
+ prs_mem_free(&qbuf);
+
+ return result;
+}
+
+
+/* Abort a server shutdown */
+
+NTSTATUS cli_reg_abort_shutdown(struct cli_state * cli, TALLOC_CTX *mem_ctx)
+{
+ prs_struct rbuf;
+ prs_struct qbuf;
+ REG_Q_ABORT_SHUTDOWN q_s;
+ REG_R_ABORT_SHUTDOWN r_s;
+ NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+
+ ZERO_STRUCT (q_s);
+ ZERO_STRUCT (r_s);
+
+ prs_init(&qbuf , MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL);
+ prs_init(&rbuf, 0, mem_ctx, UNMARSHALL);
+
+ /* Marshall data and send request */
+
+ init_reg_q_abort_shutdown(&q_s);
+
+ if (!reg_io_q_abort_shutdown("", &q_s, &qbuf, 0) ||
+ !rpc_api_pipe_req(cli, REG_ABORT_SHUTDOWN, &qbuf, &rbuf))
+ goto done;
+
+ /* Unmarshall response */
+
+ if (reg_io_r_abort_shutdown("", &r_s, &rbuf, 0))
+ result = r_s.status;
+
+done:
+ prs_mem_free(&rbuf);
+ prs_mem_free(&qbuf );
+
+ return result;
+}
diff --git a/source/rpc_client/cli_samr.c b/source/rpc_client/cli_samr.c
new file mode 100644
index 00000000000..d534745d25a
--- /dev/null
+++ b/source/rpc_client/cli_samr.c
@@ -0,0 +1,2102 @@
+/*
+ Unix SMB/CIFS implementation.
+ RPC pipe client
+ Copyright (C) Tim Potter 2000-2001,
+ Copyright (C) Andrew Tridgell 1992-1997,2000,
+ Copyright (C) Luke Kenneth Casson Leighton 1996-1997,2000,
+ Copyright (C) Paul Ashton 1997,2000,
+ Copyright (C) Elrond 2000,
+ 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 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"
+
+/* Connect to SAMR database */
+
+NTSTATUS cli_samr_connect(struct cli_state *cli, TALLOC_CTX *mem_ctx,
+ uint32 access_mask, POLICY_HND *connect_pol)
+{
+ prs_struct qbuf, rbuf;
+ SAMR_Q_CONNECT q;
+ SAMR_R_CONNECT r;
+ NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+
+ DEBUG(10,("cli_samr_connect to %s\n", cli->desthost));
+
+ ZERO_STRUCT(q);
+ ZERO_STRUCT(r);
+
+ /* Initialise parse structures */
+
+ prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL);
+ prs_init(&rbuf, 0, mem_ctx, UNMARSHALL);
+
+ /* Marshall data and send request */
+
+ init_samr_q_connect(&q, cli->desthost, access_mask);
+
+ if (!samr_io_q_connect("", &q, &qbuf, 0) ||
+ !rpc_api_pipe_req(cli, SAMR_CONNECT, &qbuf, &rbuf))
+ goto done;
+
+ /* Unmarshall response */
+
+ if (!samr_io_r_connect("", &r, &rbuf, 0))
+ goto done;
+
+ /* Return output parameters */
+
+ if (NT_STATUS_IS_OK(result = r.status)) {
+ *connect_pol = r.connect_pol;
+#ifdef __INSURE__
+ connect_pol->marker = malloc(1);
+#endif
+ }
+
+ done:
+ prs_mem_free(&qbuf);
+ prs_mem_free(&rbuf);
+
+ return result;
+}
+
+/* Connect to SAMR database */
+
+NTSTATUS cli_samr_connect4(struct cli_state *cli, TALLOC_CTX *mem_ctx,
+ uint32 access_mask, POLICY_HND *connect_pol)
+{
+ prs_struct qbuf, rbuf;
+ SAMR_Q_CONNECT4 q;
+ SAMR_R_CONNECT4 r;
+ NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+
+ DEBUG(10,("cli_samr_connect4 to %s\n", cli->desthost));
+
+ ZERO_STRUCT(q);
+ ZERO_STRUCT(r);
+
+ /* Initialise parse structures */
+
+ prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL);
+ prs_init(&rbuf, 0, mem_ctx, UNMARSHALL);
+
+ /* Marshall data and send request */
+
+ init_samr_q_connect4(&q, cli->desthost, access_mask);
+
+ if (!samr_io_q_connect4("", &q, &qbuf, 0) ||
+ !rpc_api_pipe_req(cli, SAMR_CONNECT4, &qbuf, &rbuf))
+ goto done;
+
+ /* Unmarshall response */
+
+ if (!samr_io_r_connect4("", &r, &rbuf, 0))
+ goto done;
+
+ /* Return output parameters */
+
+ if (NT_STATUS_IS_OK(result = r.status)) {
+ *connect_pol = r.connect_pol;
+#ifdef __INSURE__
+ connect_pol->marker = malloc(1);
+#endif
+ }
+
+ done:
+ prs_mem_free(&qbuf);
+ prs_mem_free(&rbuf);
+
+ return result;
+}
+
+/* Close SAMR handle */
+
+NTSTATUS cli_samr_close(struct cli_state *cli, TALLOC_CTX *mem_ctx,
+ POLICY_HND *connect_pol)
+{
+ prs_struct qbuf, rbuf;
+ SAMR_Q_CLOSE_HND q;
+ SAMR_R_CLOSE_HND r;
+ NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+
+ DEBUG(10,("cli_samr_close\n"));
+
+ ZERO_STRUCT(q);
+ ZERO_STRUCT(r);
+
+ /* Initialise parse structures */
+
+ prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL);
+ prs_init(&rbuf, 0, mem_ctx, UNMARSHALL);
+
+ /* Marshall data and send request */
+
+ init_samr_q_close_hnd(&q, connect_pol);
+
+ if (!samr_io_q_close_hnd("", &q, &qbuf, 0) ||
+ !rpc_api_pipe_req(cli, SAMR_CLOSE_HND, &qbuf, &rbuf))
+ goto done;
+
+ /* Unmarshall response */
+
+ if (!samr_io_r_close_hnd("", &r, &rbuf, 0))
+ goto done;
+
+ /* Return output parameters */
+
+ if (NT_STATUS_IS_OK(result = r.status)) {
+#ifdef __INSURE__
+ SAFE_FREE(connect_pol->marker);
+#endif
+ *connect_pol = r.pol;
+ }
+
+ done:
+ prs_mem_free(&qbuf);
+ prs_mem_free(&rbuf);
+
+ return result;
+}
+
+/* Open handle on a domain */
+
+NTSTATUS cli_samr_open_domain(struct cli_state *cli, TALLOC_CTX *mem_ctx,
+ POLICY_HND *connect_pol, uint32 access_mask,
+ const DOM_SID *domain_sid, POLICY_HND *domain_pol)
+{
+ prs_struct qbuf, rbuf;
+ SAMR_Q_OPEN_DOMAIN q;
+ SAMR_R_OPEN_DOMAIN r;
+ NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+
+ DEBUG(10,("cli_samr_open_domain with sid %s\n", sid_string_static(domain_sid) ));
+
+ ZERO_STRUCT(q);
+ ZERO_STRUCT(r);
+
+ /* Initialise parse structures */
+
+ prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL);
+ prs_init(&rbuf, 0, mem_ctx, UNMARSHALL);
+
+ /* Marshall data and send request */
+
+ init_samr_q_open_domain(&q, connect_pol, access_mask, domain_sid);
+
+ if (!samr_io_q_open_domain("", &q, &qbuf, 0) ||
+ !rpc_api_pipe_req(cli, SAMR_OPEN_DOMAIN, &qbuf, &rbuf))
+ goto done;
+
+ /* Unmarshall response */
+
+ if (!samr_io_r_open_domain("", &r, &rbuf, 0))
+ goto done;
+
+ /* Return output parameters */
+
+ if (NT_STATUS_IS_OK(result = r.status)) {
+ *domain_pol = r.domain_pol;
+#ifdef __INSURE__
+ domain_pol->marker = malloc(1);
+#endif
+ }
+
+ done:
+ prs_mem_free(&qbuf);
+ prs_mem_free(&rbuf);
+
+ return result;
+}
+
+/* Open handle on a user */
+
+NTSTATUS cli_samr_open_user(struct cli_state *cli, TALLOC_CTX *mem_ctx,
+ POLICY_HND *domain_pol, uint32 access_mask,
+ uint32 user_rid, POLICY_HND *user_pol)
+{
+ prs_struct qbuf, rbuf;
+ SAMR_Q_OPEN_USER q;
+ SAMR_R_OPEN_USER r;
+ NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+
+ DEBUG(10,("cli_samr_open_user with rid 0x%x\n", user_rid ));
+
+ ZERO_STRUCT(q);
+ ZERO_STRUCT(r);
+
+ /* Initialise parse structures */
+
+ prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL);
+ prs_init(&rbuf, 0, mem_ctx, UNMARSHALL);
+
+ /* Marshall data and send request */
+
+ init_samr_q_open_user(&q, domain_pol, access_mask, user_rid);
+
+ if (!samr_io_q_open_user("", &q, &qbuf, 0) ||
+ !rpc_api_pipe_req(cli, SAMR_OPEN_USER, &qbuf, &rbuf))
+ goto done;
+
+ /* Unmarshall response */
+
+ if (!samr_io_r_open_user("", &r, &rbuf, 0))
+ goto done;
+
+ /* Return output parameters */
+
+ if (NT_STATUS_IS_OK(result = r.status)) {
+ *user_pol = r.user_pol;
+#ifdef __INSURE__
+ user_pol->marker = malloc(1);
+#endif
+ }
+
+ done:
+ prs_mem_free(&qbuf);
+ prs_mem_free(&rbuf);
+
+ return result;
+}
+
+/* Open handle on a group */
+
+NTSTATUS cli_samr_open_group(struct cli_state *cli, TALLOC_CTX *mem_ctx,
+ POLICY_HND *domain_pol, uint32 access_mask,
+ uint32 group_rid, POLICY_HND *group_pol)
+{
+ prs_struct qbuf, rbuf;
+ SAMR_Q_OPEN_GROUP q;
+ SAMR_R_OPEN_GROUP r;
+ NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+
+ DEBUG(10,("cli_samr_open_group with rid 0x%x\n", group_rid ));
+
+ ZERO_STRUCT(q);
+ ZERO_STRUCT(r);
+
+ /* Initialise parse structures */
+
+ prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL);
+ prs_init(&rbuf, 0, mem_ctx, UNMARSHALL);
+
+ /* Marshall data and send request */
+
+ init_samr_q_open_group(&q, domain_pol, access_mask, group_rid);
+
+ if (!samr_io_q_open_group("", &q, &qbuf, 0) ||
+ !rpc_api_pipe_req(cli, SAMR_OPEN_GROUP, &qbuf, &rbuf))
+ goto done;
+
+ /* Unmarshall response */
+
+ if (!samr_io_r_open_group("", &r, &rbuf, 0))
+ goto done;
+
+ /* Return output parameters */
+
+ if (NT_STATUS_IS_OK(result = r.status)) {
+ *group_pol = r.pol;
+#ifdef __INSURE__
+ group_pol->marker = malloc(1);
+#endif
+ }
+
+ done:
+ prs_mem_free(&qbuf);
+ prs_mem_free(&rbuf);
+
+ return result;
+}
+
+/* Create domain group */
+
+NTSTATUS cli_samr_create_dom_group(struct cli_state *cli, TALLOC_CTX *mem_ctx,
+ POLICY_HND *domain_pol,
+ const char *group_name,
+ uint32 access_mask, POLICY_HND *group_pol)
+{
+ prs_struct qbuf, rbuf;
+ SAMR_Q_CREATE_DOM_GROUP q;
+ SAMR_R_CREATE_DOM_GROUP r;
+ NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+
+ DEBUG(10,("cli_samr_create_dom_group\n"));
+
+ ZERO_STRUCT(q);
+ ZERO_STRUCT(r);
+
+ /* Initialise parse structures */
+
+ prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL);
+ prs_init(&rbuf, 0, mem_ctx, UNMARSHALL);
+
+ /* Marshall data and send request */
+
+ init_samr_q_create_dom_group(&q, domain_pol, group_name, access_mask);
+
+ if (!samr_io_q_create_dom_group("", &q, &qbuf, 0) ||
+ !rpc_api_pipe_req(cli, SAMR_CREATE_DOM_GROUP, &qbuf, &rbuf))
+ goto done;
+
+ /* Unmarshall response */
+
+ if (!samr_io_r_create_dom_group("", &r, &rbuf, 0))
+ goto done;
+
+ /* Return output parameters */
+
+ result = r.status;
+
+ if (NT_STATUS_IS_OK(result))
+ *group_pol = r.pol;
+
+ done:
+ prs_mem_free(&qbuf);
+ prs_mem_free(&rbuf);
+
+ return result;
+}
+
+/* Add a domain group member */
+
+NTSTATUS cli_samr_add_groupmem(struct cli_state *cli, TALLOC_CTX *mem_ctx,
+ POLICY_HND *group_pol, uint32 rid)
+{
+ prs_struct qbuf, rbuf;
+ SAMR_Q_ADD_GROUPMEM q;
+ SAMR_R_ADD_GROUPMEM r;
+ NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+
+ DEBUG(10,("cli_samr_add_groupmem\n"));
+
+ ZERO_STRUCT(q);
+ ZERO_STRUCT(r);
+
+ /* Initialise parse structures */
+
+ prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL);
+ prs_init(&rbuf, 0, mem_ctx, UNMARSHALL);
+
+ /* Marshall data and send request */
+
+ init_samr_q_add_groupmem(&q, group_pol, rid);
+
+ if (!samr_io_q_add_groupmem("", &q, &qbuf, 0) ||
+ !rpc_api_pipe_req(cli, SAMR_ADD_GROUPMEM, &qbuf, &rbuf))
+ goto done;
+
+ /* Unmarshall response */
+
+ if (!samr_io_r_add_groupmem("", &r, &rbuf, 0))
+ goto done;
+
+ /* Return output parameters */
+
+ result = r.status;
+
+ done:
+ prs_mem_free(&qbuf);
+ prs_mem_free(&rbuf);
+
+ return result;
+}
+
+/* Delete a domain group member */
+
+NTSTATUS cli_samr_del_groupmem(struct cli_state *cli, TALLOC_CTX *mem_ctx,
+ POLICY_HND *group_pol, uint32 rid)
+{
+ prs_struct qbuf, rbuf;
+ SAMR_Q_DEL_GROUPMEM q;
+ SAMR_R_DEL_GROUPMEM r;
+ NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+
+ DEBUG(10,("cli_samr_del_groupmem\n"));
+
+ ZERO_STRUCT(q);
+ ZERO_STRUCT(r);
+
+ /* Initialise parse structures */
+
+ prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL);
+ prs_init(&rbuf, 0, mem_ctx, UNMARSHALL);
+
+ /* Marshall data and send request */
+
+ init_samr_q_del_groupmem(&q, group_pol, rid);
+
+ if (!samr_io_q_del_groupmem("", &q, &qbuf, 0) ||
+ !rpc_api_pipe_req(cli, SAMR_DEL_GROUPMEM, &qbuf, &rbuf))
+ goto done;
+
+ /* Unmarshall response */
+
+ if (!samr_io_r_del_groupmem("", &r, &rbuf, 0))
+ goto done;
+
+ /* Return output parameters */
+
+ result = r.status;
+
+ done:
+ prs_mem_free(&qbuf);
+ prs_mem_free(&rbuf);
+
+ return result;
+}
+
+/* Query user info */
+
+NTSTATUS cli_samr_query_userinfo(struct cli_state *cli, TALLOC_CTX *mem_ctx,
+ POLICY_HND *user_pol, uint16 switch_value,
+ SAM_USERINFO_CTR **ctr)
+{
+ prs_struct qbuf, rbuf;
+ SAMR_Q_QUERY_USERINFO q;
+ SAMR_R_QUERY_USERINFO r;
+ NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+
+ DEBUG(10,("cli_samr_query_userinfo\n"));
+
+ ZERO_STRUCT(q);
+ ZERO_STRUCT(r);
+
+ /* Initialise parse structures */
+
+ prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL);
+ prs_init(&rbuf, 0, mem_ctx, UNMARSHALL);
+
+ /* Marshall data and send request */
+
+ init_samr_q_query_userinfo(&q, user_pol, switch_value);
+
+ if (!samr_io_q_query_userinfo("", &q, &qbuf, 0) ||
+ !rpc_api_pipe_req(cli, SAMR_QUERY_USERINFO, &qbuf, &rbuf))
+ goto done;
+
+ /* Unmarshall response */
+
+ if (!samr_io_r_query_userinfo("", &r, &rbuf, 0))
+ goto done;
+
+ /* Return output parameters */
+
+ result = r.status;
+ *ctr = r.ctr;
+
+ done:
+ prs_mem_free(&qbuf);
+ prs_mem_free(&rbuf);
+
+ return result;
+}
+
+/* Set group info */
+
+NTSTATUS cli_samr_set_groupinfo(struct cli_state *cli, TALLOC_CTX *mem_ctx,
+ POLICY_HND *group_pol, GROUP_INFO_CTR *ctr)
+{
+ prs_struct qbuf, rbuf;
+ SAMR_Q_SET_GROUPINFO q;
+ SAMR_R_SET_GROUPINFO r;
+ NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+
+ DEBUG(10,("cli_samr_set_groupinfo\n"));
+
+ ZERO_STRUCT(q);
+ ZERO_STRUCT(r);
+
+ /* Initialise parse structures */
+
+ prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL);
+ prs_init(&rbuf, 0, mem_ctx, UNMARSHALL);
+
+ /* Marshall data and send request */
+
+ init_samr_q_set_groupinfo(&q, group_pol, ctr);
+
+ if (!samr_io_q_set_groupinfo("", &q, &qbuf, 0) ||
+ !rpc_api_pipe_req(cli, SAMR_SET_GROUPINFO, &qbuf, &rbuf))
+ goto done;
+
+ /* Unmarshall response */
+
+ if (!samr_io_r_set_groupinfo("", &r, &rbuf, 0))
+ goto done;
+
+ /* Return output parameters */
+
+ result = r.status;
+
+ done:
+ prs_mem_free(&qbuf);
+ prs_mem_free(&rbuf);
+
+ return result;
+}
+
+/* Query group info */
+
+NTSTATUS cli_samr_query_groupinfo(struct cli_state *cli, TALLOC_CTX *mem_ctx,
+ POLICY_HND *group_pol, uint32 info_level,
+ GROUP_INFO_CTR **ctr)
+{
+ prs_struct qbuf, rbuf;
+ SAMR_Q_QUERY_GROUPINFO q;
+ SAMR_R_QUERY_GROUPINFO r;
+ NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+
+ DEBUG(10,("cli_samr_query_groupinfo\n"));
+
+ ZERO_STRUCT(q);
+ ZERO_STRUCT(r);
+
+ /* Initialise parse structures */
+
+ prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL);
+ prs_init(&rbuf, 0, mem_ctx, UNMARSHALL);
+
+ /* Marshall data and send request */
+
+ init_samr_q_query_groupinfo(&q, group_pol, info_level);
+
+ if (!samr_io_q_query_groupinfo("", &q, &qbuf, 0) ||
+ !rpc_api_pipe_req(cli, SAMR_QUERY_GROUPINFO, &qbuf, &rbuf))
+ goto done;
+
+ /* Unmarshall response */
+
+ if (!samr_io_r_query_groupinfo("", &r, &rbuf, 0))
+ goto done;
+
+ *ctr = r.ctr;
+
+ /* Return output parameters */
+
+ result = r.status;
+
+ done:
+ prs_mem_free(&qbuf);
+ prs_mem_free(&rbuf);
+
+ return result;
+}
+
+/* Query user groups */
+
+NTSTATUS cli_samr_query_usergroups(struct cli_state *cli, TALLOC_CTX *mem_ctx,
+ POLICY_HND *user_pol, uint32 *num_groups,
+ DOM_GID **gid)
+{
+ prs_struct qbuf, rbuf;
+ SAMR_Q_QUERY_USERGROUPS q;
+ SAMR_R_QUERY_USERGROUPS r;
+ NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+
+ DEBUG(10,("cli_samr_query_usergroups\n"));
+
+ ZERO_STRUCT(q);
+ ZERO_STRUCT(r);
+
+ /* Initialise parse structures */
+
+ prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL);
+ prs_init(&rbuf, 0, mem_ctx, UNMARSHALL);
+
+ /* Marshall data and send request */
+
+ init_samr_q_query_usergroups(&q, user_pol);
+
+ if (!samr_io_q_query_usergroups("", &q, &qbuf, 0) ||
+ !rpc_api_pipe_req(cli, SAMR_QUERY_USERGROUPS, &qbuf, &rbuf))
+ goto done;
+
+ /* Unmarshall response */
+
+ if (!samr_io_r_query_usergroups("", &r, &rbuf, 0))
+ goto done;
+
+ /* Return output parameters */
+
+ if (NT_STATUS_IS_OK(result = r.status)) {
+ *num_groups = r.num_entries;
+ *gid = r.gid;
+ }
+
+ done:
+ prs_mem_free(&qbuf);
+ prs_mem_free(&rbuf);
+
+ return result;
+}
+
+/* Set alias info */
+
+NTSTATUS cli_samr_set_aliasinfo(struct cli_state *cli, TALLOC_CTX *mem_ctx,
+ POLICY_HND *alias_pol, ALIAS_INFO_CTR *ctr)
+{
+ prs_struct qbuf, rbuf;
+ SAMR_Q_SET_ALIASINFO q;
+ SAMR_R_SET_ALIASINFO r;
+ NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+
+ DEBUG(10,("cli_samr_set_aliasinfo\n"));
+
+ ZERO_STRUCT(q);
+ ZERO_STRUCT(r);
+
+ /* Initialise parse structures */
+
+ prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL);
+ prs_init(&rbuf, 0, mem_ctx, UNMARSHALL);
+
+ /* Marshall data and send request */
+
+ init_samr_q_set_aliasinfo(&q, alias_pol, ctr);
+
+ if (!samr_io_q_set_aliasinfo("", &q, &qbuf, 0) ||
+ !rpc_api_pipe_req(cli, SAMR_SET_ALIASINFO, &qbuf, &rbuf))
+ goto done;
+
+ /* Unmarshall response */
+
+ if (!samr_io_r_set_aliasinfo("", &r, &rbuf, 0))
+ goto done;
+
+ /* Return output parameters */
+
+ result = r.status;
+
+ done:
+ prs_mem_free(&qbuf);
+ prs_mem_free(&rbuf);
+
+ return result;
+}
+
+/* Query user aliases */
+
+NTSTATUS cli_samr_query_useraliases(struct cli_state *cli, TALLOC_CTX *mem_ctx,
+ POLICY_HND *user_pol, uint32 num_sids, DOM_SID2 *sid,
+ uint32 *num_aliases, uint32 **als_rids)
+{
+ prs_struct qbuf, rbuf;
+ SAMR_Q_QUERY_USERALIASES q;
+ SAMR_R_QUERY_USERALIASES r;
+ NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+ unsigned int ptr=1;
+
+ DEBUG(10,("cli_samr_query_useraliases\n"));
+
+ ZERO_STRUCT(q);
+ ZERO_STRUCT(r);
+
+ /* Initialise parse structures */
+
+ prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL);
+ prs_init(&rbuf, 0, mem_ctx, UNMARSHALL);
+
+ /* Marshall data and send request */
+
+ init_samr_q_query_useraliases(&q, user_pol, num_sids, &ptr, sid);
+
+ if (!samr_io_q_query_useraliases("", &q, &qbuf, 0) ||
+ !rpc_api_pipe_req(cli, SAMR_QUERY_USERALIASES, &qbuf, &rbuf))
+ goto done;
+
+ /* Unmarshall response */
+
+ if (!samr_io_r_query_useraliases("", &r, &rbuf, 0))
+ goto done;
+
+ /* Return output parameters */
+
+ if (NT_STATUS_IS_OK(result = r.status)) {
+ *num_aliases = r.num_entries;
+ *als_rids = r.rid;
+ }
+
+ done:
+ prs_mem_free(&qbuf);
+ prs_mem_free(&rbuf);
+
+ return result;
+}
+
+/* Query user groups */
+
+NTSTATUS cli_samr_query_groupmem(struct cli_state *cli, TALLOC_CTX *mem_ctx,
+ POLICY_HND *group_pol, uint32 *num_mem,
+ uint32 **rid, uint32 **attr)
+{
+ prs_struct qbuf, rbuf;
+ SAMR_Q_QUERY_GROUPMEM q;
+ SAMR_R_QUERY_GROUPMEM r;
+ NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+
+ DEBUG(10,("cli_samr_query_groupmem\n"));
+
+ ZERO_STRUCT(q);
+ ZERO_STRUCT(r);
+
+ /* Initialise parse structures */
+
+ prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL);
+ prs_init(&rbuf, 0, mem_ctx, UNMARSHALL);
+
+ /* Marshall data and send request */
+
+ init_samr_q_query_groupmem(&q, group_pol);
+
+ if (!samr_io_q_query_groupmem("", &q, &qbuf, 0) ||
+ !rpc_api_pipe_req(cli, SAMR_QUERY_GROUPMEM, &qbuf, &rbuf))
+ goto done;
+
+ /* Unmarshall response */
+
+ if (!samr_io_r_query_groupmem("", &r, &rbuf, 0))
+ goto done;
+
+ /* Return output parameters */
+
+ if (NT_STATUS_IS_OK(result = r.status)) {
+ *num_mem = r.num_entries;
+ *rid = r.rid;
+ *attr = r.attr;
+ }
+
+ done:
+ prs_mem_free(&qbuf);
+ prs_mem_free(&rbuf);
+
+ return result;
+}
+
+/**
+ * Enumerate domain users
+ *
+ * @param cli client state structure
+ * @param mem_ctx talloc context
+ * @param pol opened domain policy handle
+ * @param start_idx starting index of enumeration, returns context for
+ next enumeration
+ * @param acb_mask account control bit mask (to enumerate some particular
+ * kind of accounts)
+ * @param size max acceptable size of response
+ * @param dom_users returned array of domain user names
+ * @param rids returned array of domain user RIDs
+ * @param num_dom_users numer returned entries
+ *
+ * @return NTSTATUS returned in rpc response
+ **/
+NTSTATUS cli_samr_enum_dom_users(struct cli_state *cli, TALLOC_CTX *mem_ctx,
+ POLICY_HND *pol, uint32 *start_idx, uint16 acb_mask,
+ uint32 size, char ***dom_users, uint32 **rids,
+ uint32 *num_dom_users)
+{
+ prs_struct qbuf;
+ prs_struct rbuf;
+ SAMR_Q_ENUM_DOM_USERS q;
+ SAMR_R_ENUM_DOM_USERS r;
+ NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+ int i;
+
+ DEBUG(10,("cli_samr_enum_dom_users starting at index %u\n", (unsigned int)*start_idx));
+
+ ZERO_STRUCT(q);
+ ZERO_STRUCT(r);
+
+ /* always init this */
+ *num_dom_users = 0;
+
+ /* Initialise parse structures */
+
+ prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL);
+ prs_init(&rbuf, 0, mem_ctx, UNMARSHALL);
+
+ /* Fill query structure with parameters */
+
+ init_samr_q_enum_dom_users(&q, pol, *start_idx, acb_mask, 0, size);
+
+ if (!samr_io_q_enum_dom_users("", &q, &qbuf, 0) ||
+ !rpc_api_pipe_req(cli, SAMR_ENUM_DOM_USERS, &qbuf, &rbuf)) {
+ goto done;
+ }
+
+ /* unpack received stream */
+
+ if(!samr_io_r_enum_dom_users("", &r, &rbuf, 0))
+ goto done;
+
+ result = r.status;
+
+ if (!NT_STATUS_IS_OK(result) &&
+ NT_STATUS_V(result) != NT_STATUS_V(STATUS_MORE_ENTRIES))
+ goto done;
+
+ *start_idx = r.next_idx;
+ *num_dom_users = r.num_entries2;
+
+ if (r.num_entries2) {
+ /* allocate memory needed to return received data */
+ *rids = (uint32*)talloc(mem_ctx, sizeof(uint32) * r.num_entries2);
+ if (!*rids) {
+ DEBUG(0, ("Error in cli_samr_enum_dom_users(): out of memory\n"));
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ *dom_users = (char**)talloc(mem_ctx, sizeof(char*) * r.num_entries2);
+ if (!*dom_users) {
+ DEBUG(0, ("Error in cli_samr_enum_dom_users(): out of memory\n"));
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ /* fill output buffers with rpc response */
+ for (i = 0; i < r.num_entries2; i++) {
+ fstring conv_buf;
+
+ (*rids)[i] = r.sam[i].rid;
+ unistr2_to_ascii(conv_buf, &(r.uni_acct_name[i]), sizeof(conv_buf) - 1);
+ (*dom_users)[i] = talloc_strdup(mem_ctx, conv_buf);
+ }
+ }
+
+done:
+ prs_mem_free(&qbuf);
+ prs_mem_free(&rbuf);
+
+ return result;
+}
+
+/* Enumerate domain groups */
+
+NTSTATUS cli_samr_enum_dom_groups(struct cli_state *cli, TALLOC_CTX *mem_ctx,
+ POLICY_HND *pol, uint32 *start_idx,
+ uint32 size, struct acct_info **dom_groups,
+ uint32 *num_dom_groups)
+{
+ prs_struct qbuf, rbuf;
+ SAMR_Q_ENUM_DOM_GROUPS q;
+ SAMR_R_ENUM_DOM_GROUPS r;
+ NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+ uint32 name_idx, i;
+
+ DEBUG(10,("cli_samr_enum_dom_groups starting at index %u\n", (unsigned int)*start_idx));
+
+ ZERO_STRUCT(q);
+ ZERO_STRUCT(r);
+
+ /* Initialise parse structures */
+
+ prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL);
+ prs_init(&rbuf, 0, mem_ctx, UNMARSHALL);
+
+ /* Marshall data and send request */
+
+ init_samr_q_enum_dom_groups(&q, pol, *start_idx, size);
+
+ if (!samr_io_q_enum_dom_groups("", &q, &qbuf, 0) ||
+ !rpc_api_pipe_req(cli, SAMR_ENUM_DOM_GROUPS, &qbuf, &rbuf))
+ goto done;
+
+ /* Unmarshall response */
+
+ if (!samr_io_r_enum_dom_groups("", &r, &rbuf, 0))
+ goto done;
+
+ /* Return output parameters */
+
+ result = r.status;
+
+ if (!NT_STATUS_IS_OK(result) &&
+ NT_STATUS_V(result) != NT_STATUS_V(STATUS_MORE_ENTRIES))
+ goto done;
+
+ *num_dom_groups = r.num_entries2;
+
+ if (*num_dom_groups == 0)
+ goto done;
+
+ if (!((*dom_groups) = (struct acct_info *)
+ talloc(mem_ctx, sizeof(struct acct_info) * *num_dom_groups))) {
+ result = NT_STATUS_NO_MEMORY;
+ goto done;
+ }
+
+ memset(*dom_groups, 0, sizeof(struct acct_info) * (*num_dom_groups));
+
+ name_idx = 0;
+
+ for (i = 0; i < *num_dom_groups; i++) {
+
+ (*dom_groups)[i].rid = r.sam[i].rid;
+
+ if (r.sam[i].hdr_name.buffer) {
+ unistr2_to_ascii((*dom_groups)[i].acct_name,
+ &r.uni_grp_name[name_idx],
+ sizeof(fstring) - 1);
+ name_idx++;
+ }
+
+ *start_idx = r.next_idx;
+ }
+
+ done:
+ prs_mem_free(&qbuf);
+ prs_mem_free(&rbuf);
+
+ return result;
+}
+
+/* Enumerate domain groups */
+
+NTSTATUS cli_samr_enum_als_groups(struct cli_state *cli, TALLOC_CTX *mem_ctx,
+ POLICY_HND *pol, uint32 *start_idx,
+ uint32 size, struct acct_info **dom_aliases,
+ uint32 *num_dom_aliases)
+{
+ prs_struct qbuf, rbuf;
+ SAMR_Q_ENUM_DOM_ALIASES q;
+ SAMR_R_ENUM_DOM_ALIASES r;
+ NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+ uint32 name_idx, i;
+
+ DEBUG(10,("cli_samr_enum_als_groups starting at index %u\n", (unsigned int)*start_idx));
+
+ ZERO_STRUCT(q);
+ ZERO_STRUCT(r);
+
+ /* Initialise parse structures */
+
+ prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL);
+ prs_init(&rbuf, 0, mem_ctx, UNMARSHALL);
+
+ /* Marshall data and send request */
+
+ init_samr_q_enum_dom_aliases(&q, pol, *start_idx, size);
+
+ if (!samr_io_q_enum_dom_aliases("", &q, &qbuf, 0) ||
+ !rpc_api_pipe_req(cli, SAMR_ENUM_DOM_ALIASES, &qbuf, &rbuf)) {
+ goto done;
+ }
+
+ /* Unmarshall response */
+
+ if (!samr_io_r_enum_dom_aliases("", &r, &rbuf, 0)) {
+ goto done;
+ }
+
+ /* Return output parameters */
+
+ result = r.status;
+
+ if (!NT_STATUS_IS_OK(result) &&
+ NT_STATUS_V(result) != NT_STATUS_V(STATUS_MORE_ENTRIES)) {
+ goto done;
+ }
+
+ *num_dom_aliases = r.num_entries2;
+
+ if (*num_dom_aliases == 0)
+ goto done;
+
+ if (!((*dom_aliases) = (struct acct_info *)
+ talloc(mem_ctx, sizeof(struct acct_info) * *num_dom_aliases))) {
+ result = NT_STATUS_NO_MEMORY;
+ goto done;
+ }
+
+ memset(*dom_aliases, 0, sizeof(struct acct_info) * *num_dom_aliases);
+
+ name_idx = 0;
+
+ for (i = 0; i < *num_dom_aliases; i++) {
+
+ (*dom_aliases)[i].rid = r.sam[i].rid;
+
+ if (r.sam[i].hdr_name.buffer) {
+ unistr2_to_ascii((*dom_aliases)[i].acct_name,
+ &r.uni_grp_name[name_idx],
+ sizeof(fstring) - 1);
+ name_idx++;
+ }
+
+ *start_idx = r.next_idx;
+ }
+
+ done:
+ prs_mem_free(&qbuf);
+ prs_mem_free(&rbuf);
+
+ return result;
+}
+
+/* Query alias members */
+
+NTSTATUS cli_samr_query_aliasmem(struct cli_state *cli, TALLOC_CTX *mem_ctx,
+ POLICY_HND *alias_pol, uint32 *num_mem,
+ DOM_SID **sids)
+{
+ prs_struct qbuf, rbuf;
+ SAMR_Q_QUERY_ALIASMEM q;
+ SAMR_R_QUERY_ALIASMEM r;
+ NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+ uint32 i;
+
+ DEBUG(10,("cli_samr_query_aliasmem\n"));
+
+ ZERO_STRUCT(q);
+ ZERO_STRUCT(r);
+
+ /* Initialise parse structures */
+
+ prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL);
+ prs_init(&rbuf, 0, mem_ctx, UNMARSHALL);
+
+ /* Marshall data and send request */
+
+ init_samr_q_query_aliasmem(&q, alias_pol);
+
+ if (!samr_io_q_query_aliasmem("", &q, &qbuf, 0) ||
+ !rpc_api_pipe_req(cli, SAMR_QUERY_ALIASMEM, &qbuf, &rbuf)) {
+ goto done;
+ }
+
+ /* Unmarshall response */
+
+ if (!samr_io_r_query_aliasmem("", &r, &rbuf, 0)) {
+ goto done;
+ }
+
+ /* Return output parameters */
+
+ if (!NT_STATUS_IS_OK(result = r.status)) {
+ goto done;
+ }
+
+ *num_mem = r.num_sids;
+
+ if (*num_mem == 0) {
+ *sids = NULL;
+ result = NT_STATUS_OK;
+ goto done;
+ }
+
+ if (!(*sids = talloc(mem_ctx, sizeof(DOM_SID) * *num_mem))) {
+ result = NT_STATUS_UNSUCCESSFUL;
+ goto done;
+ }
+
+ for (i = 0; i < *num_mem; i++) {
+ (*sids)[i] = r.sid[i].sid;
+ }
+
+ done:
+ prs_mem_free(&qbuf);
+ prs_mem_free(&rbuf);
+
+ return result;
+}
+
+/* Open handle on an alias */
+
+NTSTATUS cli_samr_open_alias(struct cli_state *cli, TALLOC_CTX *mem_ctx,
+ POLICY_HND *domain_pol, uint32 access_mask,
+ uint32 alias_rid, POLICY_HND *alias_pol)
+{
+ prs_struct qbuf, rbuf;
+ SAMR_Q_OPEN_ALIAS q;
+ SAMR_R_OPEN_ALIAS r;
+ NTSTATUS result;
+
+ DEBUG(10,("cli_samr_open_alias with rid 0x%x\n", alias_rid));
+
+ ZERO_STRUCT(q);
+ ZERO_STRUCT(r);
+
+ /* Initialise parse structures */
+
+ prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL);
+ prs_init(&rbuf, 0, mem_ctx, UNMARSHALL);
+
+ /* Marshall data and send request */
+
+ init_samr_q_open_alias(&q, domain_pol, access_mask, alias_rid);
+
+ if (!samr_io_q_open_alias("", &q, &qbuf, 0) ||
+ !rpc_api_pipe_req(cli, SAMR_OPEN_ALIAS, &qbuf, &rbuf)) {
+ result = NT_STATUS_UNSUCCESSFUL;
+ goto done;
+ }
+
+ /* Unmarshall response */
+
+ if (!samr_io_r_open_alias("", &r, &rbuf, 0)) {
+ result = NT_STATUS_UNSUCCESSFUL;
+ goto done;
+ }
+
+ /* Return output parameters */
+
+ if (NT_STATUS_IS_OK(result = r.status)) {
+ *alias_pol = r.pol;
+#ifdef __INSURE__
+ alias_pol->marker = malloc(1);
+#endif
+ }
+
+ done:
+ prs_mem_free(&qbuf);
+ prs_mem_free(&rbuf);
+
+ return result;
+}
+
+/* Create an alias */
+
+NTSTATUS cli_samr_create_dom_alias(struct cli_state *cli, TALLOC_CTX *mem_ctx,
+ POLICY_HND *domain_pol, const char *name,
+ POLICY_HND *alias_pol)
+{
+ prs_struct qbuf, rbuf;
+ SAMR_Q_CREATE_DOM_ALIAS q;
+ SAMR_R_CREATE_DOM_ALIAS r;
+ NTSTATUS result;
+
+ DEBUG(10,("cli_samr_create_dom_alias named %s\n", name));
+
+ ZERO_STRUCT(q);
+ ZERO_STRUCT(r);
+
+ /* Initialise parse structures */
+
+ prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL);
+ prs_init(&rbuf, 0, mem_ctx, UNMARSHALL);
+
+ /* Marshall data and send request */
+
+ init_samr_q_create_dom_alias(&q, domain_pol, name);
+
+ if (!samr_io_q_create_dom_alias("", &q, &qbuf, 0) ||
+ !rpc_api_pipe_req(cli, SAMR_CREATE_DOM_ALIAS, &qbuf, &rbuf)) {
+ result = NT_STATUS_UNSUCCESSFUL;
+ goto done;
+ }
+
+ /* Unmarshall response */
+
+ if (!samr_io_r_create_dom_alias("", &r, &rbuf, 0)) {
+ result = NT_STATUS_UNSUCCESSFUL;
+ goto done;
+ }
+
+ /* Return output parameters */
+
+ if (NT_STATUS_IS_OK(result = r.status)) {
+ *alias_pol = r.alias_pol;
+ }
+
+ done:
+ prs_mem_free(&qbuf);
+ prs_mem_free(&rbuf);
+
+ return result;
+}
+
+/* Add an alias member */
+
+NTSTATUS cli_samr_add_aliasmem(struct cli_state *cli, TALLOC_CTX *mem_ctx,
+ POLICY_HND *alias_pol, DOM_SID *member)
+{
+ prs_struct qbuf, rbuf;
+ SAMR_Q_ADD_ALIASMEM q;
+ SAMR_R_ADD_ALIASMEM r;
+ NTSTATUS result;
+
+ DEBUG(10,("cli_samr_add_aliasmem"));
+
+ ZERO_STRUCT(q);
+ ZERO_STRUCT(r);
+
+ /* Initialise parse structures */
+
+ prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL);
+ prs_init(&rbuf, 0, mem_ctx, UNMARSHALL);
+
+ /* Marshall data and send request */
+
+ init_samr_q_add_aliasmem(&q, alias_pol, member);
+
+ if (!samr_io_q_add_aliasmem("", &q, &qbuf, 0) ||
+ !rpc_api_pipe_req(cli, SAMR_ADD_ALIASMEM, &qbuf, &rbuf)) {
+ result = NT_STATUS_UNSUCCESSFUL;
+ goto done;
+ }
+
+ /* Unmarshall response */
+
+ if (!samr_io_r_add_aliasmem("", &r, &rbuf, 0)) {
+ result = NT_STATUS_UNSUCCESSFUL;
+ goto done;
+ }
+
+ result = r.status;
+
+ done:
+ prs_mem_free(&qbuf);
+ prs_mem_free(&rbuf);
+
+ return result;
+}
+
+/* Delete an alias member */
+
+NTSTATUS cli_samr_del_aliasmem(struct cli_state *cli, TALLOC_CTX *mem_ctx,
+ POLICY_HND *alias_pol, DOM_SID *member)
+{
+ prs_struct qbuf, rbuf;
+ SAMR_Q_DEL_ALIASMEM q;
+ SAMR_R_DEL_ALIASMEM r;
+ NTSTATUS result;
+
+ DEBUG(10,("cli_samr_del_aliasmem"));
+
+ ZERO_STRUCT(q);
+ ZERO_STRUCT(r);
+
+ /* Initialise parse structures */
+
+ prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL);
+ prs_init(&rbuf, 0, mem_ctx, UNMARSHALL);
+
+ /* Marshall data and send request */
+
+ init_samr_q_del_aliasmem(&q, alias_pol, member);
+
+ if (!samr_io_q_del_aliasmem("", &q, &qbuf, 0) ||
+ !rpc_api_pipe_req(cli, SAMR_DEL_ALIASMEM, &qbuf, &rbuf)) {
+ result = NT_STATUS_UNSUCCESSFUL;
+ goto done;
+ }
+
+ /* Unmarshall response */
+
+ if (!samr_io_r_del_aliasmem("", &r, &rbuf, 0)) {
+ result = NT_STATUS_UNSUCCESSFUL;
+ goto done;
+ }
+
+ result = r.status;
+
+ done:
+ prs_mem_free(&qbuf);
+ prs_mem_free(&rbuf);
+
+ return result;
+}
+
+/* Query alias info */
+
+NTSTATUS cli_samr_query_alias_info(struct cli_state *cli, TALLOC_CTX *mem_ctx,
+ POLICY_HND *alias_pol, uint16 switch_value,
+ ALIAS_INFO_CTR *ctr)
+{
+ prs_struct qbuf, rbuf;
+ SAMR_Q_QUERY_ALIASINFO q;
+ SAMR_R_QUERY_ALIASINFO r;
+ NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+
+ DEBUG(10,("cli_samr_query_dom_info\n"));
+
+ ZERO_STRUCT(q);
+ ZERO_STRUCT(r);
+
+ /* Initialise parse structures */
+
+ prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL);
+ prs_init(&rbuf, 0, mem_ctx, UNMARSHALL);
+
+ /* Marshall data and send request */
+
+ init_samr_q_query_aliasinfo(&q, alias_pol, switch_value);
+
+ if (!samr_io_q_query_aliasinfo("", &q, &qbuf, 0) ||
+ !rpc_api_pipe_req(cli, SAMR_QUERY_ALIASINFO, &qbuf, &rbuf)) {
+ goto done;
+ }
+
+ /* Unmarshall response */
+
+ if (!samr_io_r_query_aliasinfo("", &r, &rbuf, 0)) {
+ goto done;
+ }
+
+ /* Return output parameters */
+
+ if (!NT_STATUS_IS_OK(result = r.status)) {
+ goto done;
+ }
+
+ *ctr = r.ctr;
+
+ done:
+ prs_mem_free(&qbuf);
+ prs_mem_free(&rbuf);
+
+ return result;
+}
+
+/* Query domain info */
+
+NTSTATUS cli_samr_query_dom_info(struct cli_state *cli, TALLOC_CTX *mem_ctx,
+ POLICY_HND *domain_pol, uint16 switch_value,
+ SAM_UNK_CTR *ctr)
+{
+ prs_struct qbuf, rbuf;
+ SAMR_Q_QUERY_DOMAIN_INFO q;
+ SAMR_R_QUERY_DOMAIN_INFO r;
+ NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+
+ DEBUG(10,("cli_samr_query_dom_info\n"));
+
+ ZERO_STRUCT(q);
+ ZERO_STRUCT(r);
+
+ /* Initialise parse structures */
+
+ prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL);
+ prs_init(&rbuf, 0, mem_ctx, UNMARSHALL);
+
+ /* Marshall data and send request */
+
+ init_samr_q_query_dom_info(&q, domain_pol, switch_value);
+
+ if (!samr_io_q_query_dom_info("", &q, &qbuf, 0) ||
+ !rpc_api_pipe_req(cli, SAMR_QUERY_DOMAIN_INFO, &qbuf, &rbuf)) {
+ goto done;
+ }
+
+ /* Unmarshall response */
+
+ r.ctr = ctr;
+
+ if (!samr_io_r_query_dom_info("", &r, &rbuf, 0)) {
+ goto done;
+ }
+
+ /* Return output parameters */
+
+ if (!NT_STATUS_IS_OK(result = r.status)) {
+ goto done;
+ }
+
+ done:
+ prs_mem_free(&qbuf);
+ prs_mem_free(&rbuf);
+
+ return result;
+}
+
+/* User change password */
+
+NTSTATUS cli_samr_chgpasswd_user(struct cli_state *cli, TALLOC_CTX *mem_ctx,
+ const char *username,
+ const char *newpassword,
+ const char *oldpassword )
+{
+ prs_struct qbuf, rbuf;
+ SAMR_Q_CHGPASSWD_USER q;
+ SAMR_R_CHGPASSWD_USER r;
+ NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+
+ uchar new_nt_password[516];
+ uchar new_lm_password[516];
+ uchar old_nt_hash[16];
+ uchar old_lanman_hash[16];
+ uchar old_nt_hash_enc[16];
+ uchar old_lanman_hash_enc[16];
+
+ uchar new_nt_hash[16];
+ uchar new_lanman_hash[16];
+
+ DEBUG(10,("cli_samr_query_dom_info\n"));
+
+ ZERO_STRUCT(q);
+ ZERO_STRUCT(r);
+
+ /* Calculate the MD4 hash (NT compatible) of the password */
+ E_md4hash(oldpassword, old_nt_hash);
+ E_md4hash(newpassword, new_nt_hash);
+
+ if (lp_client_lanman_auth()
+ && E_deshash(newpassword, new_lanman_hash)
+ && E_deshash(oldpassword, old_lanman_hash)) {
+ /* E_deshash returns false for 'long' passwords (> 14
+ DOS chars). This allows us to match Win2k, which
+ does not store a LM hash for these passwords (which
+ would reduce the effective password length to 14) */
+
+ encode_pw_buffer(new_lm_password, newpassword, STR_UNICODE);
+
+ SamOEMhash( new_lm_password, old_nt_hash, 516);
+ E_old_pw_hash( new_nt_hash, old_lanman_hash, old_lanman_hash_enc);
+ } else {
+ ZERO_STRUCT(new_lm_password);
+ ZERO_STRUCT(old_lanman_hash_enc);
+ }
+
+ encode_pw_buffer(new_nt_password, newpassword, STR_UNICODE);
+
+ SamOEMhash( new_nt_password, old_nt_hash, 516);
+ E_old_pw_hash( new_nt_hash, old_nt_hash, old_nt_hash_enc);
+
+ /* Initialise parse structures */
+
+ prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL);
+ prs_init(&rbuf, 0, mem_ctx, UNMARSHALL);
+
+ /* Marshall data and send request */
+
+ init_samr_q_chgpasswd_user(&q, cli->srv_name_slash, username,
+ new_nt_password,
+ old_nt_hash_enc,
+ new_lm_password,
+ old_lanman_hash_enc);
+
+ if (!samr_io_q_chgpasswd_user("", &q, &qbuf, 0) ||
+ !rpc_api_pipe_req(cli, SAMR_CHGPASSWD_USER, &qbuf, &rbuf)) {
+ goto done;
+ }
+
+ /* Unmarshall response */
+
+ if (!samr_io_r_chgpasswd_user("", &r, &rbuf, 0)) {
+ goto done;
+ }
+
+ /* Return output parameters */
+
+ if (!NT_STATUS_IS_OK(result = r.status)) {
+ goto done;
+ }
+
+ done:
+ prs_mem_free(&qbuf);
+ prs_mem_free(&rbuf);
+
+ return result;
+}
+
+/* This function returns the bizzare set of (max_entries, max_size) required
+ for the QueryDisplayInfo RPC to actually work against a domain controller
+ with large (10k and higher) numbers of users. These values were
+ obtained by inspection using ethereal and NT4 running User Manager. */
+
+void get_query_dispinfo_params(int loop_count, uint32 *max_entries,
+ uint32 *max_size)
+{
+ switch(loop_count) {
+ case 0:
+ *max_entries = 512;
+ *max_size = 16383;
+ break;
+ case 1:
+ *max_entries = 1024;
+ *max_size = 32766;
+ break;
+ case 2:
+ *max_entries = 2048;
+ *max_size = 65532;
+ break;
+ case 3:
+ *max_entries = 4096;
+ *max_size = 131064;
+ break;
+ default: /* loop_count >= 4 */
+ *max_entries = 4096;
+ *max_size = 131071;
+ break;
+ }
+}
+
+/* Query display info */
+
+NTSTATUS cli_samr_query_dispinfo(struct cli_state *cli, TALLOC_CTX *mem_ctx,
+ POLICY_HND *domain_pol, uint32 *start_idx,
+ uint16 switch_value, uint32 *num_entries,
+ uint32 max_entries, uint32 max_size,
+ SAM_DISPINFO_CTR *ctr)
+{
+ prs_struct qbuf, rbuf;
+ SAMR_Q_QUERY_DISPINFO q;
+ SAMR_R_QUERY_DISPINFO r;
+ NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+
+ DEBUG(10,("cli_samr_query_dispinfo for start_idx = %u\n", *start_idx));
+
+ ZERO_STRUCT(q);
+ ZERO_STRUCT(r);
+
+ *num_entries = 0;
+
+ /* Initialise parse structures */
+
+ prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL);
+ prs_init(&rbuf, 0, mem_ctx, UNMARSHALL);
+
+ /* Marshall data and send request */
+
+ init_samr_q_query_dispinfo(&q, domain_pol, switch_value,
+ *start_idx, max_entries, max_size);
+
+ if (!samr_io_q_query_dispinfo("", &q, &qbuf, 0) ||
+ !rpc_api_pipe_req(cli, SAMR_QUERY_DISPINFO, &qbuf, &rbuf)) {
+ goto done;
+ }
+
+ /* Unmarshall response */
+
+ r.ctr = ctr;
+
+ if (!samr_io_r_query_dispinfo("", &r, &rbuf, 0)) {
+ goto done;
+ }
+
+ /* Return output parameters */
+
+ result = r.status;
+
+ if (!NT_STATUS_IS_OK(result) &&
+ NT_STATUS_V(result) != NT_STATUS_V(STATUS_MORE_ENTRIES)) {
+ goto done;
+ }
+
+ *num_entries = r.num_entries;
+ *start_idx += r.num_entries; /* No next_idx in this structure! */
+
+ done:
+ prs_mem_free(&qbuf);
+ prs_mem_free(&rbuf);
+
+ return result;
+}
+
+/* Lookup rids. Note that NT4 seems to crash if more than ~1000 rids are
+ looked up in one packet. */
+
+NTSTATUS cli_samr_lookup_rids(struct cli_state *cli, TALLOC_CTX *mem_ctx,
+ POLICY_HND *domain_pol, uint32 flags,
+ uint32 num_rids, uint32 *rids,
+ uint32 *num_names, char ***names,
+ uint32 **name_types)
+{
+ prs_struct qbuf, rbuf;
+ SAMR_Q_LOOKUP_RIDS q;
+ SAMR_R_LOOKUP_RIDS r;
+ NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+ uint32 i;
+
+ DEBUG(10,("cli_samr_lookup_rids\n"));
+
+ if (num_rids > 1000) {
+ DEBUG(2, ("cli_samr_lookup_rids: warning: NT4 can crash if "
+ "more than ~1000 rids are looked up at once.\n"));
+ }
+
+ ZERO_STRUCT(q);
+ ZERO_STRUCT(r);
+
+ /* Initialise parse structures */
+
+ prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL);
+ prs_init(&rbuf, 0, mem_ctx, UNMARSHALL);
+
+ /* Marshall data and send request */
+
+ init_samr_q_lookup_rids(mem_ctx, &q, domain_pol, flags,
+ num_rids, rids);
+
+ if (!samr_io_q_lookup_rids("", &q, &qbuf, 0) ||
+ !rpc_api_pipe_req(cli, SAMR_LOOKUP_RIDS, &qbuf, &rbuf)) {
+ goto done;
+ }
+
+ /* Unmarshall response */
+
+ if (!samr_io_r_lookup_rids("", &r, &rbuf, 0)) {
+ goto done;
+ }
+
+ /* Return output parameters */
+
+ if (!NT_STATUS_IS_OK(result = r.status)) {
+ goto done;
+ }
+
+ if (r.num_names1 == 0) {
+ *num_names = 0;
+ *names = NULL;
+ goto done;
+ }
+
+ *num_names = r.num_names1;
+ *names = talloc(mem_ctx, sizeof(char *) * r.num_names1);
+ *name_types = talloc(mem_ctx, sizeof(uint32) * r.num_names1);
+
+ for (i = 0; i < r.num_names1; i++) {
+ fstring tmp;
+
+ unistr2_to_ascii(tmp, &r.uni_name[i], sizeof(tmp) - 1);
+ (*names)[i] = talloc_strdup(mem_ctx, tmp);
+ (*name_types)[i] = r.type[i];
+ }
+
+ done:
+ prs_mem_free(&qbuf);
+ prs_mem_free(&rbuf);
+
+ return result;
+}
+
+/* Lookup names */
+
+NTSTATUS cli_samr_lookup_names(struct cli_state *cli, TALLOC_CTX *mem_ctx,
+ POLICY_HND *domain_pol, uint32 flags,
+ uint32 num_names, const char **names,
+ uint32 *num_rids, uint32 **rids,
+ uint32 **rid_types)
+{
+ prs_struct qbuf, rbuf;
+ SAMR_Q_LOOKUP_NAMES q;
+ SAMR_R_LOOKUP_NAMES r;
+ NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+ uint32 i;
+
+ DEBUG(10,("cli_samr_lookup_names\n"));
+
+ ZERO_STRUCT(q);
+ ZERO_STRUCT(r);
+
+ /* Initialise parse structures */
+
+ prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL);
+ prs_init(&rbuf, 0, mem_ctx, UNMARSHALL);
+
+ /* Marshall data and send request */
+
+ init_samr_q_lookup_names(mem_ctx, &q, domain_pol, flags,
+ num_names, names);
+
+ if (!samr_io_q_lookup_names("", &q, &qbuf, 0) ||
+ !rpc_api_pipe_req(cli, SAMR_LOOKUP_NAMES, &qbuf, &rbuf)) {
+ goto done;
+ }
+
+ /* Unmarshall response */
+
+ if (!samr_io_r_lookup_names("", &r, &rbuf, 0)) {
+ goto done;
+ }
+
+ /* Return output parameters */
+
+ if (!NT_STATUS_IS_OK(result = r.status)) {
+ goto done;
+ }
+
+ if (r.num_rids1 == 0) {
+ *num_rids = 0;
+ goto done;
+ }
+
+ *num_rids = r.num_rids1;
+ *rids = talloc(mem_ctx, sizeof(uint32) * r.num_rids1);
+ *rid_types = talloc(mem_ctx, sizeof(uint32) * r.num_rids1);
+
+ for (i = 0; i < r.num_rids1; i++) {
+ (*rids)[i] = r.rids[i];
+ (*rid_types)[i] = r.types[i];
+ }
+
+ done:
+ prs_mem_free(&qbuf);
+ prs_mem_free(&rbuf);
+
+ return result;
+}
+
+/* Create a domain user */
+
+NTSTATUS cli_samr_create_dom_user(struct cli_state *cli, TALLOC_CTX *mem_ctx,
+ POLICY_HND *domain_pol, const char *acct_name,
+ uint32 acb_info, uint32 unknown,
+ POLICY_HND *user_pol, uint32 *rid)
+{
+ prs_struct qbuf, rbuf;
+ SAMR_Q_CREATE_USER q;
+ SAMR_R_CREATE_USER r;
+ NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+
+ DEBUG(10,("cli_samr_create_dom_user %s\n", acct_name));
+
+ ZERO_STRUCT(q);
+ ZERO_STRUCT(r);
+
+ /* Initialise parse structures */
+
+ prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL);
+ prs_init(&rbuf, 0, mem_ctx, UNMARSHALL);
+
+ /* Marshall data and send request */
+
+ init_samr_q_create_user(&q, domain_pol, acct_name, acb_info, unknown);
+
+ if (!samr_io_q_create_user("", &q, &qbuf, 0) ||
+ !rpc_api_pipe_req(cli, SAMR_CREATE_USER, &qbuf, &rbuf)) {
+ goto done;
+ }
+
+ /* Unmarshall response */
+
+ if (!samr_io_r_create_user("", &r, &rbuf, 0)) {
+ goto done;
+ }
+
+ /* Return output parameters */
+
+ if (!NT_STATUS_IS_OK(result = r.status)) {
+ goto done;
+ }
+
+ if (user_pol)
+ *user_pol = r.user_pol;
+
+ if (rid)
+ *rid = r.user_rid;
+
+ done:
+ prs_mem_free(&qbuf);
+ prs_mem_free(&rbuf);
+
+ return result;
+}
+
+/* Set userinfo */
+
+NTSTATUS cli_samr_set_userinfo(struct cli_state *cli, TALLOC_CTX *mem_ctx,
+ POLICY_HND *user_pol, uint16 switch_value,
+ DATA_BLOB *sess_key, SAM_USERINFO_CTR *ctr)
+{
+ prs_struct qbuf, rbuf;
+ SAMR_Q_SET_USERINFO q;
+ SAMR_R_SET_USERINFO r;
+ NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+
+ DEBUG(10,("cli_samr_set_userinfo\n"));
+
+ ZERO_STRUCT(q);
+ ZERO_STRUCT(r);
+
+ if (!sess_key->length) {
+ DEBUG(1, ("No user session key\n"));
+ return NT_STATUS_NO_USER_SESSION_KEY;
+ }
+
+ /* Initialise parse structures */
+
+ prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL);
+ prs_init(&rbuf, 0, mem_ctx, UNMARSHALL);
+
+ /* Marshall data and send request */
+
+ q.ctr = ctr;
+
+ init_samr_q_set_userinfo(&q, user_pol, sess_key, switch_value,
+ ctr->info.id);
+
+ if (!samr_io_q_set_userinfo("", &q, &qbuf, 0) ||
+ !rpc_api_pipe_req(cli, SAMR_SET_USERINFO, &qbuf, &rbuf)) {
+ goto done;
+ }
+
+ /* Unmarshall response */
+
+ if (!samr_io_r_set_userinfo("", &r, &rbuf, 0)) {
+ goto done;
+ }
+
+ /* Return output parameters */
+
+ if (!NT_STATUS_IS_OK(result = r.status)) {
+ goto done;
+ }
+
+ done:
+ prs_mem_free(&qbuf);
+ prs_mem_free(&rbuf);
+
+ return result;
+}
+
+/* Set userinfo2 */
+
+NTSTATUS cli_samr_set_userinfo2(struct cli_state *cli, TALLOC_CTX *mem_ctx,
+ POLICY_HND *user_pol, uint16 switch_value,
+ DATA_BLOB *sess_key, SAM_USERINFO_CTR *ctr)
+{
+ prs_struct qbuf, rbuf;
+ SAMR_Q_SET_USERINFO2 q;
+ SAMR_R_SET_USERINFO2 r;
+ NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+
+ DEBUG(10,("cli_samr_set_userinfo2\n"));
+
+ if (!sess_key->length) {
+ DEBUG(1, ("No user session key\n"));
+ return NT_STATUS_NO_USER_SESSION_KEY;
+ }
+
+ ZERO_STRUCT(q);
+ ZERO_STRUCT(r);
+
+ /* Initialise parse structures */
+
+ prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL);
+ prs_init(&rbuf, 0, mem_ctx, UNMARSHALL);
+
+ /* Marshall data and send request */
+
+ init_samr_q_set_userinfo2(&q, user_pol, sess_key, switch_value, ctr);
+
+ if (!samr_io_q_set_userinfo2("", &q, &qbuf, 0) ||
+ !rpc_api_pipe_req(cli, SAMR_SET_USERINFO2, &qbuf, &rbuf)) {
+ goto done;
+ }
+
+ /* Unmarshall response */
+
+ if (!samr_io_r_set_userinfo2("", &r, &rbuf, 0)) {
+ goto done;
+ }
+
+ /* Return output parameters */
+
+ if (!NT_STATUS_IS_OK(result = r.status)) {
+ goto done;
+ }
+
+ done:
+ prs_mem_free(&qbuf);
+ prs_mem_free(&rbuf);
+
+ return result;
+}
+
+/* Delete domain user */
+
+NTSTATUS cli_samr_delete_dom_user(struct cli_state *cli, TALLOC_CTX *mem_ctx,
+ POLICY_HND *user_pol)
+{
+ prs_struct qbuf, rbuf;
+ SAMR_Q_DELETE_DOM_USER q;
+ SAMR_R_DELETE_DOM_USER r;
+ NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+
+ DEBUG(10,("cli_samr_delete_dom_user\n"));
+
+ ZERO_STRUCT(q);
+ ZERO_STRUCT(r);
+
+ /* Initialise parse structures */
+
+ prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL);
+ prs_init(&rbuf, 0, mem_ctx, UNMARSHALL);
+
+ /* Marshall data and send request */
+
+ init_samr_q_delete_dom_user(&q, user_pol);
+
+ if (!samr_io_q_delete_dom_user("", &q, &qbuf, 0) ||
+ !rpc_api_pipe_req(cli, SAMR_DELETE_DOM_USER, &qbuf, &rbuf)) {
+ goto done;
+ }
+
+ /* Unmarshall response */
+
+ if (!samr_io_r_delete_dom_user("", &r, &rbuf, 0)) {
+ goto done;
+ }
+
+ /* Return output parameters */
+
+ result = r.status;
+
+ done:
+ prs_mem_free(&qbuf);
+ prs_mem_free(&rbuf);
+
+ return result;
+}
+
+/* Query user security object */
+
+NTSTATUS cli_samr_query_sec_obj(struct cli_state *cli, TALLOC_CTX *mem_ctx,
+ POLICY_HND *user_pol, uint16 switch_value,
+ TALLOC_CTX *ctx, SEC_DESC_BUF **sec_desc_buf)
+{
+ prs_struct qbuf, rbuf;
+ SAMR_Q_QUERY_SEC_OBJ q;
+ SAMR_R_QUERY_SEC_OBJ r;
+ NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+
+ DEBUG(10,("cli_samr_query_sec_obj\n"));
+
+ ZERO_STRUCT(q);
+ ZERO_STRUCT(r);
+
+ /* Initialise parse structures */
+
+ prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL);
+ prs_init(&rbuf, 0, mem_ctx, UNMARSHALL);
+
+ /* Marshall data and send request */
+
+ init_samr_q_query_sec_obj(&q, user_pol, switch_value);
+
+ if (!samr_io_q_query_sec_obj("", &q, &qbuf, 0) ||
+ !rpc_api_pipe_req(cli, SAMR_QUERY_SEC_OBJECT, &qbuf, &rbuf)) {
+ goto done;
+ }
+
+ /* Unmarshall response */
+
+ if (!samr_io_r_query_sec_obj("", &r, &rbuf, 0)) {
+ goto done;
+ }
+
+ /* Return output parameters */
+
+ result = r.status;
+ *sec_desc_buf=dup_sec_desc_buf(ctx, r.buf);
+
+ done:
+ prs_mem_free(&qbuf);
+ prs_mem_free(&rbuf);
+
+ return result;
+}
+
+/* Get domain password info */
+
+NTSTATUS cli_samr_get_dom_pwinfo(struct cli_state *cli, TALLOC_CTX *mem_ctx,
+ uint16 *unk_0, uint16 *unk_1, uint16 *unk_2)
+{
+ prs_struct qbuf, rbuf;
+ SAMR_Q_GET_DOM_PWINFO q;
+ SAMR_R_GET_DOM_PWINFO r;
+ NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+
+ DEBUG(10,("cli_samr_get_dom_pwinfo\n"));
+
+ ZERO_STRUCT(q);
+ ZERO_STRUCT(r);
+
+ /* Initialise parse structures */
+
+ prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL);
+ prs_init(&rbuf, 0, mem_ctx, UNMARSHALL);
+
+ /* Marshall data and send request */
+
+ init_samr_q_get_dom_pwinfo(&q, cli->desthost);
+
+ if (!samr_io_q_get_dom_pwinfo("", &q, &qbuf, 0) ||
+ !rpc_api_pipe_req(cli, SAMR_GET_DOM_PWINFO, &qbuf, &rbuf))
+ goto done;
+
+ /* Unmarshall response */
+
+ if (!samr_io_r_get_dom_pwinfo("", &r, &rbuf, 0))
+ goto done;
+
+ /* Return output parameters */
+
+ result = r.status;
+
+ if (NT_STATUS_IS_OK(result)) {
+ if (unk_0)
+ *unk_0 = r.unk_0;
+ if (unk_1)
+ *unk_1 = r.unk_1;
+ if (unk_2)
+ *unk_2 = r.unk_2;
+ }
+
+ done:
+ prs_mem_free(&qbuf);
+ prs_mem_free(&rbuf);
+
+ return result;
+}
+
+/* Lookup Domain Name */
+
+NTSTATUS cli_samr_lookup_domain(struct cli_state *cli, TALLOC_CTX *mem_ctx,
+ POLICY_HND *user_pol, char *domain_name,
+ DOM_SID *sid)
+{
+ prs_struct qbuf, rbuf;
+ SAMR_Q_LOOKUP_DOMAIN q;
+ SAMR_R_LOOKUP_DOMAIN r;
+ NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+
+ DEBUG(10,("cli_samr_lookup_domain\n"));
+
+ ZERO_STRUCT(q);
+ ZERO_STRUCT(r);
+
+ /* Initialise parse structures */
+
+ prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL);
+ prs_init(&rbuf, 0, mem_ctx, UNMARSHALL);
+
+ /* Marshall data and send request */
+
+ init_samr_q_lookup_domain(&q, user_pol, domain_name);
+
+ if (!samr_io_q_lookup_domain("", &q, &qbuf, 0) ||
+ !rpc_api_pipe_req(cli, SAMR_LOOKUP_DOMAIN, &qbuf, &rbuf))
+ goto done;
+
+ /* Unmarshall response */
+
+ if (!samr_io_r_lookup_domain("", &r, &rbuf, 0))
+ goto done;
+
+ /* Return output parameters */
+
+ result = r.status;
+
+ if (NT_STATUS_IS_OK(result))
+ sid_copy(sid, &r.dom_sid.sid);
+
+ done:
+ prs_mem_free(&qbuf);
+ prs_mem_free(&rbuf);
+
+ return result;
+}
diff --git a/source/rpc_client/cli_shutdown.c b/source/rpc_client/cli_shutdown.c
new file mode 100644
index 00000000000..0bf6e90ad27
--- /dev/null
+++ b/source/rpc_client/cli_shutdown.c
@@ -0,0 +1,104 @@
+/*
+ Unix SMB/CIFS implementation.
+ RPC Pipe client
+
+ Copyright (C) Andrew Tridgell 1992-1998,
+ Copyright (C) Luke Kenneth Casson Leighton 1996-1998,
+ Copyright (C) Paul Ashton 1997-1998.
+ Copyright (C) Jeremy Allison 1999,
+ Copyright (C) Simo Sorce 2001,
+ 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 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"
+
+/* Shutdown a server */
+
+NTSTATUS cli_shutdown_init(struct cli_state * cli, TALLOC_CTX *mem_ctx,
+ const char *msg, uint32 timeout, BOOL do_reboot,
+ BOOL force)
+{
+ prs_struct qbuf;
+ prs_struct rbuf;
+ SHUTDOWN_Q_INIT q_s;
+ SHUTDOWN_R_INIT r_s;
+ NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+
+ if (msg == NULL) return NT_STATUS_INVALID_PARAMETER;
+
+ ZERO_STRUCT (q_s);
+ ZERO_STRUCT (r_s);
+
+ prs_init(&qbuf , MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL);
+ prs_init(&rbuf, 0, mem_ctx, UNMARSHALL);
+
+ /* Marshall data and send request */
+
+ init_shutdown_q_init(&q_s, msg, timeout, do_reboot, force);
+
+ if (!shutdown_io_q_init("", &q_s, &qbuf, 0) ||
+ !rpc_api_pipe_req(cli, SHUTDOWN_INIT, &qbuf, &rbuf))
+ goto done;
+
+ /* Unmarshall response */
+
+ if(shutdown_io_r_init("", &r_s, &rbuf, 0))
+ result = r_s.status;
+
+done:
+ prs_mem_free(&rbuf);
+ prs_mem_free(&qbuf);
+
+ return result;
+}
+
+
+/* Abort a server shutdown */
+
+NTSTATUS cli_shutdown_abort(struct cli_state * cli, TALLOC_CTX *mem_ctx)
+{
+ prs_struct rbuf;
+ prs_struct qbuf;
+ SHUTDOWN_Q_ABORT q_s;
+ SHUTDOWN_R_ABORT r_s;
+ NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+
+ ZERO_STRUCT (q_s);
+ ZERO_STRUCT (r_s);
+
+ prs_init(&qbuf , MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL);
+ prs_init(&rbuf, 0, mem_ctx, UNMARSHALL);
+
+ /* Marshall data and send request */
+
+ init_shutdown_q_abort(&q_s);
+
+ if (!shutdown_io_q_abort("", &q_s, &qbuf, 0) ||
+ !rpc_api_pipe_req(cli, SHUTDOWN_ABORT, &qbuf, &rbuf))
+ goto done;
+
+ /* Unmarshall response */
+
+ if (shutdown_io_r_abort("", &r_s, &rbuf, 0))
+ result = r_s.status;
+
+done:
+ prs_mem_free(&rbuf);
+ prs_mem_free(&qbuf );
+
+ return result;
+}
diff --git a/source/rpc_client/cli_spoolss.c b/source/rpc_client/cli_spoolss.c
new file mode 100644
index 00000000000..8f5f2413dee
--- /dev/null
+++ b/source/rpc_client/cli_spoolss.c
@@ -0,0 +1,2466 @@
+/*
+ Unix SMB/CIFS implementation.
+ RPC pipe client
+
+ Copyright (C) Gerald Carter 2001-2002,
+ Copyright (C) Tim Potter 2000-2002,
+ Copyright (C) Andrew Tridgell 1994-2000,
+ Copyright (C) Luke Kenneth Casson Leighton 1996-2000,
+ Copyright (C) Jean-Francois Micouleau 1999-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 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"
+
+/** @defgroup spoolss SPOOLSS - NT printing routines
+ * @ingroup rpc_client
+ *
+ * @{
+ **/
+
+/**********************************************************************
+ Initialize a new spoolss buff for use by a client rpc
+**********************************************************************/
+static void init_buffer(NEW_BUFFER *buffer, uint32 size, TALLOC_CTX *ctx)
+{
+ buffer->ptr = (size != 0);
+ buffer->size = size;
+ buffer->string_at_end = size;
+ prs_init(&buffer->prs, size, ctx, MARSHALL);
+ buffer->struct_start = prs_offset(&buffer->prs);
+}
+
+/*********************************************************************
+ Decode various spoolss rpc's and info levels
+ ********************************************************************/
+
+/**********************************************************************
+**********************************************************************/
+static void decode_printer_info_0(TALLOC_CTX *mem_ctx, NEW_BUFFER *buffer,
+ uint32 returned, PRINTER_INFO_0 **info)
+{
+ uint32 i;
+ PRINTER_INFO_0 *inf;
+
+ inf=(PRINTER_INFO_0 *)talloc(mem_ctx, returned*sizeof(PRINTER_INFO_0));
+ memset(inf, 0, returned*sizeof(PRINTER_INFO_0));
+
+ prs_set_offset(&buffer->prs,0);
+
+ for (i=0; i<returned; i++) {
+ smb_io_printer_info_0("", buffer, &inf[i], 0);
+ }
+
+ *info=inf;
+}
+
+/**********************************************************************
+**********************************************************************/
+static void decode_printer_info_1(TALLOC_CTX *mem_ctx, NEW_BUFFER *buffer,
+ uint32 returned, PRINTER_INFO_1 **info)
+{
+ uint32 i;
+ PRINTER_INFO_1 *inf;
+
+ inf=(PRINTER_INFO_1 *)talloc(mem_ctx, returned*sizeof(PRINTER_INFO_1));
+ memset(inf, 0, returned*sizeof(PRINTER_INFO_1));
+
+ prs_set_offset(&buffer->prs,0);
+
+ for (i=0; i<returned; i++) {
+ smb_io_printer_info_1("", buffer, &inf[i], 0);
+ }
+
+ *info=inf;
+}
+
+/**********************************************************************
+**********************************************************************/
+static void decode_printer_info_2(TALLOC_CTX *mem_ctx, NEW_BUFFER *buffer,
+ uint32 returned, PRINTER_INFO_2 **info)
+{
+ uint32 i;
+ PRINTER_INFO_2 *inf;
+
+ inf=(PRINTER_INFO_2 *)talloc(mem_ctx, returned*sizeof(PRINTER_INFO_2));
+ memset(inf, 0, returned*sizeof(PRINTER_INFO_2));
+
+ prs_set_offset(&buffer->prs,0);
+
+ for (i=0; i<returned; i++) {
+ /* a little initialization as we go */
+ inf[i].secdesc = NULL;
+ smb_io_printer_info_2("", buffer, &inf[i], 0);
+ }
+
+ *info=inf;
+}
+
+/**********************************************************************
+**********************************************************************/
+static void decode_printer_info_3(TALLOC_CTX *mem_ctx, NEW_BUFFER *buffer,
+ uint32 returned, PRINTER_INFO_3 **info)
+{
+ uint32 i;
+ PRINTER_INFO_3 *inf;
+
+ inf=(PRINTER_INFO_3 *)talloc(mem_ctx, returned*sizeof(PRINTER_INFO_3));
+ memset(inf, 0, returned*sizeof(PRINTER_INFO_3));
+
+ prs_set_offset(&buffer->prs,0);
+
+ for (i=0; i<returned; i++) {
+ inf[i].secdesc = NULL;
+ smb_io_printer_info_3("", buffer, &inf[i], 0);
+ }
+
+ *info=inf;
+}
+
+/**********************************************************************
+**********************************************************************/
+static void decode_port_info_1(TALLOC_CTX *mem_ctx, NEW_BUFFER *buffer,
+ uint32 returned, PORT_INFO_1 **info)
+{
+ uint32 i;
+ PORT_INFO_1 *inf;
+
+ inf=(PORT_INFO_1*)talloc(mem_ctx, returned*sizeof(PORT_INFO_1));
+ memset(inf, 0, returned*sizeof(PORT_INFO_1));
+
+ prs_set_offset(&buffer->prs, 0);
+
+ for (i=0; i<returned; i++) {
+ smb_io_port_info_1("", buffer, &(inf[i]), 0);
+ }
+
+ *info=inf;
+}
+
+/**********************************************************************
+**********************************************************************/
+static void decode_port_info_2(TALLOC_CTX *mem_ctx, NEW_BUFFER *buffer,
+ uint32 returned, PORT_INFO_2 **info)
+{
+ uint32 i;
+ PORT_INFO_2 *inf;
+
+ inf=(PORT_INFO_2*)talloc(mem_ctx, returned*sizeof(PORT_INFO_2));
+ memset(inf, 0, returned*sizeof(PORT_INFO_2));
+
+ prs_set_offset(&buffer->prs, 0);
+
+ for (i=0; i<returned; i++) {
+ smb_io_port_info_2("", buffer, &(inf[i]), 0);
+ }
+
+ *info=inf;
+}
+
+/**********************************************************************
+**********************************************************************/
+static void decode_printer_driver_1(TALLOC_CTX *mem_ctx, NEW_BUFFER *buffer,
+ uint32 returned, DRIVER_INFO_1 **info)
+{
+ uint32 i;
+ DRIVER_INFO_1 *inf;
+
+ inf=(DRIVER_INFO_1 *)talloc(mem_ctx, returned*sizeof(DRIVER_INFO_1));
+ memset(inf, 0, returned*sizeof(DRIVER_INFO_1));
+
+ prs_set_offset(&buffer->prs,0);
+
+ for (i=0; i<returned; i++) {
+ smb_io_printer_driver_info_1("", buffer, &(inf[i]), 0);
+ }
+
+ *info=inf;
+}
+
+/**********************************************************************
+**********************************************************************/
+static void decode_printer_driver_2(TALLOC_CTX *mem_ctx, NEW_BUFFER *buffer,
+ uint32 returned, DRIVER_INFO_2 **info)
+{
+ uint32 i;
+ DRIVER_INFO_2 *inf;
+
+ inf=(DRIVER_INFO_2 *)talloc(mem_ctx, returned*sizeof(DRIVER_INFO_2));
+ memset(inf, 0, returned*sizeof(DRIVER_INFO_2));
+
+ prs_set_offset(&buffer->prs,0);
+
+ for (i=0; i<returned; i++) {
+ smb_io_printer_driver_info_2("", buffer, &(inf[i]), 0);
+ }
+
+ *info=inf;
+}
+
+/**********************************************************************
+**********************************************************************/
+static void decode_printer_driver_3(TALLOC_CTX *mem_ctx, NEW_BUFFER *buffer,
+ uint32 returned, DRIVER_INFO_3 **info)
+{
+ uint32 i;
+ DRIVER_INFO_3 *inf;
+
+ inf=(DRIVER_INFO_3 *)talloc(mem_ctx, returned*sizeof(DRIVER_INFO_3));
+ memset(inf, 0, returned*sizeof(DRIVER_INFO_3));
+
+ prs_set_offset(&buffer->prs,0);
+
+ for (i=0; i<returned; i++) {
+ smb_io_printer_driver_info_3("", buffer, &(inf[i]), 0);
+ }
+
+ *info=inf;
+}
+
+/**********************************************************************
+**********************************************************************/
+static void decode_printerdriverdir_1 (TALLOC_CTX *mem_ctx, NEW_BUFFER *buffer,
+ uint32 returned, DRIVER_DIRECTORY_1 **info
+)
+{
+ DRIVER_DIRECTORY_1 *inf;
+
+ inf=(DRIVER_DIRECTORY_1 *)talloc(mem_ctx, sizeof(DRIVER_DIRECTORY_1));
+ memset(inf, 0, sizeof(DRIVER_DIRECTORY_1));
+
+ prs_set_offset(&buffer->prs, 0);
+
+ smb_io_driverdir_1("", buffer, inf, 0);
+
+ *info=inf;
+}
+
+/** Return a handle to the specified printer or print server.
+ *
+ * @param cli Pointer to client state structure which is open
+ * on the SPOOLSS pipe.
+ *
+ * @param mem_ctx Pointer to an initialised talloc context.
+ *
+ * @param printername The name of the printer or print server to be
+ * opened in UNC format.
+ *
+ * @param datatype Specifies the default data type for the printer.
+ *
+ * @param access_required The access rights requested on the printer or
+ * print server.
+ *
+ * @param station The UNC name of the requesting workstation.
+ *
+ * @param username The name of the user requesting the open.
+ *
+ * @param pol Returned policy handle.
+ */
+
+/*********************************************************************************
+ Win32 API - OpenPrinter()
+ ********************************************************************************/
+
+WERROR cli_spoolss_open_printer_ex(struct cli_state *cli, TALLOC_CTX *mem_ctx,
+ const char *printername, const char *datatype, uint32 access_required,
+ const char *station, const char *username, POLICY_HND *pol)
+{
+ prs_struct qbuf, rbuf;
+ SPOOL_Q_OPEN_PRINTER_EX q;
+ SPOOL_R_OPEN_PRINTER_EX r;
+ WERROR result = W_ERROR(ERRgeneral);
+
+ ZERO_STRUCT(q);
+ ZERO_STRUCT(r);
+
+ /* Initialise parse structures */
+
+ prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL);
+ prs_init(&rbuf, 0, mem_ctx, UNMARSHALL);
+
+ /* Initialise input parameters */
+
+ make_spoolss_q_open_printer_ex(&q, printername, datatype,
+ access_required, station, username);
+
+ /* Marshall data and send request */
+
+ if (!spoolss_io_q_open_printer_ex("", &q, &qbuf, 0) ||
+ !rpc_api_pipe_req(cli, SPOOLSS_OPENPRINTEREX, &qbuf, &rbuf))
+ goto done;
+
+ /* Unmarshall response */
+
+ if (!spoolss_io_r_open_printer_ex("", &r, &rbuf, 0))
+ goto done;
+
+ /* Return output parameters */
+
+ result = r.status;
+
+ if (W_ERROR_IS_OK(result))
+ *pol = r.handle;
+
+ done:
+ prs_mem_free(&qbuf);
+ prs_mem_free(&rbuf);
+
+ return result;
+}
+
+/** Close a printer handle
+ *
+ * @param cli Pointer to client state structure which is open
+ * on the SPOOLSS pipe.
+ *
+ * @param mem_ctx Pointer to an initialised talloc context.
+ *
+ * @param pol Policy handle of printer or print server to close.
+ */
+/*********************************************************************************
+ Win32 API - ClosePrinter()
+ ********************************************************************************/
+
+WERROR cli_spoolss_close_printer(struct cli_state *cli, TALLOC_CTX *mem_ctx,
+ POLICY_HND *pol)
+{
+ prs_struct qbuf, rbuf;
+ SPOOL_Q_CLOSEPRINTER q;
+ SPOOL_R_CLOSEPRINTER r;
+ WERROR result = W_ERROR(ERRgeneral);
+
+ ZERO_STRUCT(q);
+ ZERO_STRUCT(r);
+
+ /* Initialise parse structures */
+
+ prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL);
+ prs_init(&rbuf, 0, mem_ctx, UNMARSHALL);
+
+ /* Initialise input parameters */
+
+ make_spoolss_q_closeprinter(&q, pol);
+
+ /* Marshall data and send request */
+
+ if (!spoolss_io_q_closeprinter("", &q, &qbuf, 0) ||
+ !rpc_api_pipe_req(cli, SPOOLSS_CLOSEPRINTER, &qbuf, &rbuf))
+ goto done;
+
+ /* Unmarshall response */
+
+ if (!spoolss_io_r_closeprinter("", &r, &rbuf, 0))
+ goto done;
+
+ /* Return output parameters */
+
+ result = r.status;
+
+ if (W_ERROR_IS_OK(result))
+ *pol = r.handle;
+
+ done:
+ prs_mem_free(&qbuf);
+ prs_mem_free(&rbuf);
+
+ return result;
+}
+
+/** Enumerate printers on a print server.
+ *
+ * @param cli Pointer to client state structure which is open
+ * on the SPOOLSS pipe.
+ * @param mem_ctx Pointer to an initialised talloc context.
+ *
+ * @param offered Buffer size offered in the request.
+ * @param needed Number of bytes needed to complete the request.
+ * may be NULL.
+ *
+ * @param flags Selected from PRINTER_ENUM_* flags.
+ * @param level Request information level.
+ *
+ * @param num_printers Pointer to number of printers returned. May be
+ * NULL.
+ * @param ctr Return structure for printer information. May
+ * be NULL.
+ */
+/*********************************************************************************
+ Win32 API - EnumPrinters()
+ ********************************************************************************/
+
+WERROR cli_spoolss_enum_printers(struct cli_state *cli, TALLOC_CTX *mem_ctx,
+ uint32 offered, uint32 *needed,
+ char *name, uint32 flags, uint32 level,
+ uint32 *num_printers, PRINTER_INFO_CTR *ctr)
+{
+ prs_struct qbuf, rbuf;
+ SPOOL_Q_ENUMPRINTERS q;
+ SPOOL_R_ENUMPRINTERS r;
+ NEW_BUFFER buffer;
+ WERROR result = W_ERROR(ERRgeneral);
+
+ ZERO_STRUCT(q);
+ ZERO_STRUCT(r);
+
+ /* Initialise input parameters */
+
+ init_buffer(&buffer, offered, mem_ctx);
+
+ prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL);
+ prs_init(&rbuf, 0, mem_ctx, UNMARSHALL);
+
+ make_spoolss_q_enumprinters(&q, flags, name, level, &buffer,
+ offered);
+
+ /* Marshall data and send request */
+
+ if (!spoolss_io_q_enumprinters("", &q, &qbuf, 0) ||
+ !rpc_api_pipe_req(cli, SPOOLSS_ENUMPRINTERS, &qbuf, &rbuf))
+ goto done;
+
+ /* Unmarshall response */
+
+ if (spoolss_io_r_enumprinters("", &r, &rbuf, 0)) {
+ if (needed)
+ *needed = r.needed;
+ }
+
+ result = r.status;
+
+ /* Return output parameters */
+
+ if (!W_ERROR_IS_OK(r.status))
+ goto done;
+
+ if (num_printers)
+ *num_printers = r.returned;
+
+ if (!ctr)
+ goto done;
+
+ switch (level) {
+ case 0:
+ decode_printer_info_0(mem_ctx, r.buffer, r.returned,
+ &ctr->printers_0);
+ break;
+ case 1:
+ decode_printer_info_1(mem_ctx, r.buffer, r.returned,
+ &ctr->printers_1);
+ break;
+ case 2:
+ decode_printer_info_2(mem_ctx, r.buffer, r.returned,
+ &ctr->printers_2);
+ break;
+ case 3:
+ decode_printer_info_3(mem_ctx, r.buffer, r.returned,
+ &ctr->printers_3);
+ break;
+ }
+
+ done:
+ prs_mem_free(&qbuf);
+ prs_mem_free(&rbuf);
+
+ return result;
+}
+
+/*********************************************************************************
+ Win32 API - EnumPorts()
+ ********************************************************************************/
+/** Enumerate printer ports on a print server.
+ *
+ * @param cli Pointer to client state structure which is open
+ * on the SPOOLSS pipe.
+ * @param mem_ctx Pointer to an initialised talloc context.
+ *
+ * @param offered Buffer size offered in the request.
+ * @param needed Number of bytes needed to complete the request.
+ * May be NULL.
+ *
+ * @param level Requested information level.
+ *
+ * @param num_ports Pointer to number of ports returned. May be NULL.
+ * @param ctr Pointer to structure holding port information.
+ * May be NULL.
+ */
+
+WERROR cli_spoolss_enum_ports(struct cli_state *cli, TALLOC_CTX *mem_ctx,
+ uint32 offered, uint32 *needed,
+ uint32 level, uint32 *num_ports, PORT_INFO_CTR *ctr)
+{
+ prs_struct qbuf, rbuf;
+ SPOOL_Q_ENUMPORTS q;
+ SPOOL_R_ENUMPORTS r;
+ NEW_BUFFER buffer;
+ WERROR result = W_ERROR(ERRgeneral);
+ fstring server;
+
+ ZERO_STRUCT(q);
+ ZERO_STRUCT(r);
+
+ slprintf(server, sizeof(fstring)-1, "\\\\%s", cli->desthost);
+ strupper_m(server);
+
+ /* Initialise input parameters */
+
+ init_buffer(&buffer, offered, mem_ctx);
+
+ prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL);
+ prs_init(&rbuf, 0, mem_ctx, UNMARSHALL);
+
+ make_spoolss_q_enumports(&q, server, level, &buffer, offered);
+
+ /* Marshall data and send request */
+
+ if (!spoolss_io_q_enumports("", &q, &qbuf, 0) ||
+ !rpc_api_pipe_req(cli, SPOOLSS_ENUMPORTS, &qbuf, &rbuf))
+ goto done;
+
+ /* Unmarshall response */
+
+ if (spoolss_io_r_enumports("", &r, &rbuf, 0)) {
+ if (needed)
+ *needed = r.needed;
+ }
+
+ result = r.status;
+
+ /* Return output parameters */
+
+ if (!W_ERROR_IS_OK(result))
+ goto done;
+
+ if (num_ports)
+ *num_ports = r.returned;
+
+ if (!ctr)
+ goto done;
+
+ switch (level) {
+ case 1:
+ decode_port_info_1(mem_ctx, r.buffer, r.returned,
+ &ctr->port.info_1);
+ break;
+ case 2:
+ decode_port_info_2(mem_ctx, r.buffer, r.returned,
+ &ctr->port.info_2);
+ break;
+ }
+
+ done:
+ prs_mem_free(&qbuf);
+ prs_mem_free(&rbuf);
+
+ return result;
+}
+
+/*********************************************************************************
+ Win32 API - GetPrinter()
+ ********************************************************************************/
+
+WERROR cli_spoolss_getprinter(struct cli_state *cli, TALLOC_CTX *mem_ctx,
+ uint32 offered, uint32 *needed,
+ POLICY_HND *pol, uint32 level,
+ PRINTER_INFO_CTR *ctr)
+{
+ prs_struct qbuf, rbuf;
+ SPOOL_Q_GETPRINTER q;
+ SPOOL_R_GETPRINTER r;
+ NEW_BUFFER buffer;
+ WERROR result = W_ERROR(ERRgeneral);
+
+ ZERO_STRUCT(q);
+ ZERO_STRUCT(r);
+
+ /* Initialise input parameters */
+
+ init_buffer(&buffer, offered, mem_ctx);
+
+ prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL);
+ prs_init(&rbuf, 0, mem_ctx, UNMARSHALL);
+
+ make_spoolss_q_getprinter(mem_ctx, &q, pol, level, &buffer, offered);
+
+ /* Marshall data and send request */
+
+ if (!spoolss_io_q_getprinter("", &q, &qbuf, 0) ||
+ !rpc_api_pipe_req(cli, SPOOLSS_GETPRINTER, &qbuf, &rbuf))
+ goto done;
+
+ /* Unmarshall response */
+
+ if (!spoolss_io_r_getprinter("", &r, &rbuf, 0))
+ goto done;
+
+ if (needed)
+ *needed = r.needed;
+
+ /* Return output parameters */
+
+ result = r.status;
+
+ if (W_ERROR_IS_OK(result)) {
+ switch (level) {
+ case 0:
+ decode_printer_info_0(mem_ctx, r.buffer, 1, &ctr->printers_0);
+ break;
+ case 1:
+ decode_printer_info_1(mem_ctx, r.buffer, 1, &ctr->printers_1);
+ break;
+ case 2:
+ decode_printer_info_2(mem_ctx, r.buffer, 1, &ctr->printers_2);
+ break;
+ case 3:
+ decode_printer_info_3(mem_ctx, r.buffer, 1, &ctr->printers_3);
+ break;
+ }
+ }
+
+ done:
+ prs_mem_free(&qbuf);
+ prs_mem_free(&rbuf);
+
+ return result;
+}
+
+/*********************************************************************************
+ Win32 API - SetPrinter()
+ ********************************************************************************/
+/** Set printer info
+ *
+ * @param cli Pointer to client state structure which is open
+ * on the SPOOLSS pipe.
+ * @param mem_ctx Pointer to an initialised talloc context.
+ *
+ * @param pol Policy handle on printer to set info.
+ * @param level Information level to set.
+ * @param ctr Pointer to structure holding printer information.
+ * @param command Specifies the action performed. See
+ * http://msdn.microsoft.com/library/default.asp?url=/library/en-us/gdi/prntspol_13ua.asp
+ * for details.
+ *
+ */
+
+WERROR cli_spoolss_setprinter(struct cli_state *cli, TALLOC_CTX *mem_ctx,
+ POLICY_HND *pol, uint32 level,
+ PRINTER_INFO_CTR *ctr, uint32 command)
+{
+ prs_struct qbuf, rbuf;
+ SPOOL_Q_SETPRINTER q;
+ SPOOL_R_SETPRINTER r;
+ WERROR result = W_ERROR(ERRgeneral);
+
+ ZERO_STRUCT(q);
+ ZERO_STRUCT(r);
+
+ /* Initialise input parameters */
+
+ prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL);
+ prs_init(&rbuf, 0, mem_ctx, UNMARSHALL);
+
+ if (!make_spoolss_q_setprinter(mem_ctx, &q, pol, level, ctr, command))
+ goto done;
+
+ /* Marshall data and send request */
+
+ if (!spoolss_io_q_setprinter("", &q, &qbuf, 0) ||
+ !rpc_api_pipe_req(cli, SPOOLSS_SETPRINTER, &qbuf, &rbuf))
+ goto done;
+
+ /* Unmarshall response */
+
+ if (!spoolss_io_r_setprinter("", &r, &rbuf, 0))
+ goto done;
+
+ result = r.status;
+
+done:
+ prs_mem_free(&qbuf);
+ prs_mem_free(&rbuf);
+
+ return result;
+}
+
+/*********************************************************************************
+ Win32 API - GetPrinterDriver()
+ ********************************************************************************/
+/** Get installed printer drivers for a given printer
+ *
+ * @param cli Pointer to client state structure which is open
+ * on the SPOOLSS pipe.
+ *
+ * @param mem_ctx Pointer to an initialised talloc context.
+ *
+ * @param offered Buffer size offered in the request.
+ * @param needed Number of bytes needed to complete the request.
+ * may be NULL.
+ *
+ * @param pol Pointer to an open policy handle for the printer
+ * opened with cli_spoolss_open_printer_ex().
+ * @param level Requested information level.
+ * @param env The print environment or archictecture. This is
+ * "Windows NT x86" for NT4.
+ * @param ctr Returned printer driver information.
+ */
+
+WERROR cli_spoolss_getprinterdriver(struct cli_state *cli,
+ TALLOC_CTX *mem_ctx,
+ uint32 offered, uint32 *needed,
+ POLICY_HND *pol, uint32 level,
+ const char *env, int version, PRINTER_DRIVER_CTR *ctr)
+{
+ prs_struct qbuf, rbuf;
+ SPOOL_Q_GETPRINTERDRIVER2 q;
+ SPOOL_R_GETPRINTERDRIVER2 r;
+ NEW_BUFFER buffer;
+ WERROR result = W_ERROR(ERRgeneral);
+ fstring server;
+
+ ZERO_STRUCT(q);
+ ZERO_STRUCT(r);
+
+ fstrcpy(server, cli->desthost);
+ strupper_m(server);
+
+ /* Initialise input parameters */
+
+ init_buffer(&buffer, offered, mem_ctx);
+
+ prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL);
+ prs_init(&rbuf, 0, mem_ctx, UNMARSHALL);
+
+ make_spoolss_q_getprinterdriver2(&q, pol, env, level, version, 2,
+ &buffer, offered);
+
+ /* Marshall data and send request */
+
+ if (!spoolss_io_q_getprinterdriver2 ("", &q, &qbuf, 0) ||
+ !rpc_api_pipe_req (cli, SPOOLSS_GETPRINTERDRIVER2, &qbuf, &rbuf))
+ goto done;
+
+ /* Unmarshall response */
+
+ if (spoolss_io_r_getprinterdriver2 ("", &r, &rbuf, 0)) {
+ if (needed)
+ *needed = r.needed;
+ }
+
+ result = r.status;
+
+ /* Return output parameters */
+
+ if (!W_ERROR_IS_OK(result))
+ goto done;
+
+ if (!ctr)
+ goto done;
+
+ switch (level) {
+ case 1:
+ decode_printer_driver_1(mem_ctx, r.buffer, 1, &ctr->info1);
+ break;
+ case 2:
+ decode_printer_driver_2(mem_ctx, r.buffer, 1, &ctr->info2);
+ break;
+ case 3:
+ decode_printer_driver_3(mem_ctx, r.buffer, 1, &ctr->info3);
+ break;
+ default:
+ DEBUG(10, ("cli_spoolss_getprinterdriver: unknown info level %d", level));
+ return WERR_UNKNOWN_LEVEL;
+ }
+
+ done:
+ prs_mem_free(&qbuf);
+ prs_mem_free(&rbuf);
+
+ return result;
+}
+
+/*********************************************************************************
+ Win32 API - EnumPrinterDrivers()
+ ********************************************************************************/
+/**********************************************************************
+ * Get installed printer drivers for a given printer
+ */
+WERROR cli_spoolss_enumprinterdrivers (struct cli_state *cli,
+ TALLOC_CTX *mem_ctx,
+ uint32 offered, uint32 *needed,
+ uint32 level, const char *env,
+ uint32 *num_drivers,
+ PRINTER_DRIVER_CTR *ctr)
+{
+ prs_struct qbuf, rbuf;
+ SPOOL_Q_ENUMPRINTERDRIVERS q;
+ SPOOL_R_ENUMPRINTERDRIVERS r;
+ NEW_BUFFER buffer;
+ WERROR result = W_ERROR(ERRgeneral);
+ fstring server;
+
+ ZERO_STRUCT(q);
+ ZERO_STRUCT(r);
+
+ slprintf(server, sizeof(fstring)-1, "\\\\%s", cli->desthost);
+ strupper_m(server);
+
+ /* Initialise input parameters */
+
+ init_buffer(&buffer, offered, mem_ctx);
+
+ prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL);
+ prs_init(&rbuf, 0, mem_ctx, UNMARSHALL);
+
+ /* Write the request */
+
+ make_spoolss_q_enumprinterdrivers(&q, server, env, level, &buffer,
+ offered);
+
+ /* Marshall data and send request */
+
+ if (!spoolss_io_q_enumprinterdrivers ("", &q, &qbuf, 0) ||
+ !rpc_api_pipe_req (cli, SPOOLSS_ENUMPRINTERDRIVERS, &qbuf, &rbuf))
+ goto done;
+
+ /* Unmarshall response */
+
+ if (!spoolss_io_r_enumprinterdrivers ("", &r, &rbuf, 0))
+ goto done;
+
+ if (needed)
+ *needed = r.needed;
+
+ if (num_drivers)
+ *num_drivers = r.returned;
+
+ result = r.status;
+
+ /* Return output parameters */
+
+ if (W_ERROR_IS_OK(result) && (r.returned != 0)) {
+ *num_drivers = r.returned;
+
+ switch (level) {
+ case 1:
+ decode_printer_driver_1(mem_ctx, r.buffer, r.returned, &ctr->info1);
+ break;
+ case 2:
+ decode_printer_driver_2(mem_ctx, r.buffer, r.returned, &ctr->info2);
+ break;
+ case 3:
+ decode_printer_driver_3(mem_ctx, r.buffer, r.returned, &ctr->info3);
+ break;
+ default:
+ DEBUG(10, ("cli_spoolss_enumprinterdrivers: unknown info level %d\n",
+ level));
+ return WERR_UNKNOWN_LEVEL;
+ }
+ }
+
+ done:
+ prs_mem_free(&qbuf);
+ prs_mem_free(&rbuf);
+
+ return result;
+}
+
+
+/*********************************************************************************
+ Win32 API - GetPrinterDriverDirectory()
+ ********************************************************************************/
+/**********************************************************************
+ * Get installed printer drivers for a given printer
+ */
+WERROR cli_spoolss_getprinterdriverdir (struct cli_state *cli,
+ TALLOC_CTX *mem_ctx,
+ uint32 offered, uint32 *needed,
+ uint32 level, char *env,
+ DRIVER_DIRECTORY_CTR *ctr)
+{
+ prs_struct qbuf, rbuf;
+ SPOOL_Q_GETPRINTERDRIVERDIR q;
+ SPOOL_R_GETPRINTERDRIVERDIR r;
+ NEW_BUFFER buffer;
+ WERROR result = W_ERROR(ERRgeneral);
+ fstring server;
+
+ ZERO_STRUCT(q);
+ ZERO_STRUCT(r);
+
+ slprintf(server, sizeof(fstring)-1, "\\\\%s", cli->desthost);
+ strupper_m(server);
+
+ /* Initialise input parameters */
+
+ init_buffer(&buffer, offered, mem_ctx);
+
+ prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL);
+ prs_init(&rbuf, 0, mem_ctx, UNMARSHALL);
+
+ /* Write the request */
+
+ make_spoolss_q_getprinterdriverdir(&q, server, env, level, &buffer,
+ offered);
+
+ /* Marshall data and send request */
+
+ if (!spoolss_io_q_getprinterdriverdir ("", &q, &qbuf, 0) ||
+ !rpc_api_pipe_req (cli, SPOOLSS_GETPRINTERDRIVERDIRECTORY,
+ &qbuf, &rbuf))
+ goto done;
+
+ /* Unmarshall response */
+
+ if (spoolss_io_r_getprinterdriverdir ("", &r, &rbuf, 0)) {
+ if (needed)
+ *needed = r.needed;
+ }
+
+ /* Return output parameters */
+
+ result = r.status;
+
+ if (W_ERROR_IS_OK(result)) {
+ switch (level) {
+ case 1:
+ decode_printerdriverdir_1(mem_ctx, r.buffer, 1,
+ &ctr->info1);
+ break;
+ }
+ }
+
+ done:
+ prs_mem_free(&qbuf);
+ prs_mem_free(&rbuf);
+
+ return result;
+}
+
+/*********************************************************************************
+ Win32 API - AddPrinterDriver()
+ ********************************************************************************/
+/**********************************************************************
+ * Install a printer driver
+ */
+WERROR cli_spoolss_addprinterdriver (struct cli_state *cli,
+ TALLOC_CTX *mem_ctx, uint32 level,
+ PRINTER_DRIVER_CTR *ctr)
+{
+ prs_struct qbuf, rbuf;
+ SPOOL_Q_ADDPRINTERDRIVER q;
+ SPOOL_R_ADDPRINTERDRIVER r;
+ WERROR result = W_ERROR(ERRgeneral);
+ fstring server;
+
+ ZERO_STRUCT(q);
+ ZERO_STRUCT(r);
+
+ slprintf(server, sizeof(fstring)-1, "\\\\%s", cli->desthost);
+ strupper_m(server);
+
+ /* Initialise input parameters */
+
+ prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL);
+ prs_init(&rbuf, 0, mem_ctx, UNMARSHALL);
+
+ /* Write the request */
+
+ make_spoolss_q_addprinterdriver (mem_ctx, &q, server, level, ctr);
+
+ /* Marshall data and send request */
+
+ if (!spoolss_io_q_addprinterdriver ("", &q, &qbuf, 0) ||
+ !rpc_api_pipe_req (cli, SPOOLSS_ADDPRINTERDRIVER, &qbuf, &rbuf))
+ goto done;
+
+ /* Unmarshall response */
+
+ if (!spoolss_io_r_addprinterdriver ("", &r, &rbuf, 0))
+ goto done;
+
+ /* Return output parameters */
+
+ result = r.status;
+
+done:
+ prs_mem_free(&qbuf);
+ prs_mem_free(&rbuf);
+
+ return result;
+}
+
+/*********************************************************************************
+ Win32 API - AddPrinter()
+ ********************************************************************************/
+/**********************************************************************
+ * Install a printer
+ */
+WERROR cli_spoolss_addprinterex (struct cli_state *cli, TALLOC_CTX *mem_ctx,
+ uint32 level, PRINTER_INFO_CTR*ctr)
+{
+ prs_struct qbuf, rbuf;
+ SPOOL_Q_ADDPRINTEREX q;
+ SPOOL_R_ADDPRINTEREX r;
+ WERROR result = W_ERROR(ERRgeneral);
+ fstring server,
+ client,
+ user;
+
+ ZERO_STRUCT(q);
+ ZERO_STRUCT(r);
+
+ slprintf(client, sizeof(fstring)-1, "\\\\%s", cli->desthost);
+ strupper_m(client);
+ slprintf(server, sizeof(fstring)-1, "\\\\%s", cli->desthost);
+ strupper_m(server);
+ fstrcpy (user, cli->user_name);
+
+ /* Initialise input parameters */
+
+ prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL);
+ prs_init(&rbuf, 0, mem_ctx, UNMARSHALL);
+
+ /* Write the request */
+
+ make_spoolss_q_addprinterex (mem_ctx, &q, server, client, user,
+ level, ctr);
+
+ /* Marshall data and send request */
+
+ if (!spoolss_io_q_addprinterex ("", &q, &qbuf, 0) ||
+ !rpc_api_pipe_req (cli, SPOOLSS_ADDPRINTEREX, &qbuf, &rbuf))
+ goto done;
+
+ /* Unmarshall response */
+
+ if (!spoolss_io_r_addprinterex ("", &r, &rbuf, 0))
+ goto done;
+
+ /* Return output parameters */
+
+ result = r.status;
+
+ done:
+ prs_mem_free(&qbuf);
+ prs_mem_free(&rbuf);
+
+ return result;
+}
+
+/*********************************************************************************
+ Win32 API - DeltePrinterDriver()
+ ********************************************************************************/
+/**********************************************************************
+ * Delete a Printer Driver from the server (does not remove
+ * the driver files
+ */
+WERROR cli_spoolss_deleteprinterdriver (struct cli_state *cli,
+ TALLOC_CTX *mem_ctx, const char *arch,
+ const char *driver)
+{
+ prs_struct qbuf, rbuf;
+ SPOOL_Q_DELETEPRINTERDRIVER q;
+ SPOOL_R_DELETEPRINTERDRIVER r;
+ WERROR result = W_ERROR(ERRgeneral);
+ fstring server;
+
+ ZERO_STRUCT(q);
+ ZERO_STRUCT(r);
+
+
+ /* Initialise input parameters */
+ prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL);
+ prs_init(&rbuf, 0, mem_ctx, UNMARSHALL);
+
+ slprintf(server, sizeof(fstring)-1, "\\\\%s", cli->desthost);
+ strupper_m(server);
+
+ /* Write the request */
+
+ make_spoolss_q_deleteprinterdriver(mem_ctx, &q, server, arch, driver);
+
+ /* Marshall data and send request */
+
+ if (!spoolss_io_q_deleteprinterdriver ("", &q, &qbuf, 0) ||
+ !rpc_api_pipe_req (cli,SPOOLSS_DELETEPRINTERDRIVER , &qbuf, &rbuf))
+ goto done;
+
+ /* Unmarshall response */
+
+ if (!spoolss_io_r_deleteprinterdriver ("", &r, &rbuf, 0))
+ goto done;
+
+ /* Return output parameters */
+
+ result = r.status;
+
+ done:
+ prs_mem_free(&qbuf);
+ prs_mem_free(&rbuf);
+
+ return result;
+}
+
+/*********************************************************************************
+ Win32 API - GetPrinterProcessorDirectory()
+ ********************************************************************************/
+
+WERROR cli_spoolss_getprintprocessordirectory(struct cli_state *cli,
+ TALLOC_CTX *mem_ctx,
+ uint32 offered, uint32 *needed,
+ char *name, char *environment,
+ fstring procdir)
+{
+ prs_struct qbuf, rbuf;
+ SPOOL_Q_GETPRINTPROCESSORDIRECTORY q;
+ SPOOL_R_GETPRINTPROCESSORDIRECTORY r;
+ int level = 1;
+ WERROR result = W_ERROR(ERRgeneral);
+ NEW_BUFFER buffer;
+
+ ZERO_STRUCT(q);
+ ZERO_STRUCT(r);
+
+ /* Initialise parse structures */
+
+ prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL);
+ prs_init(&rbuf, 0, mem_ctx, UNMARSHALL);
+
+ /* Initialise input parameters */
+
+ init_buffer(&buffer, offered, mem_ctx);
+
+ make_spoolss_q_getprintprocessordirectory(
+ &q, name, environment, level, &buffer, offered);
+
+ /* Marshall data and send request */
+
+ if (!spoolss_io_q_getprintprocessordirectory("", &q, &qbuf, 0) ||
+ !rpc_api_pipe_req(cli, SPOOLSS_GETPRINTPROCESSORDIRECTORY,
+ &qbuf, &rbuf))
+ goto done;
+
+ /* Unmarshall response */
+
+ if (!spoolss_io_r_getprintprocessordirectory("", &r, &rbuf, 0))
+ goto done;
+
+ /* Return output parameters */
+
+ result = r.status;
+
+ if (needed)
+ *needed = r.needed;
+
+ if (W_ERROR_IS_OK(result))
+ fstrcpy(procdir, "Not implemented!");
+
+ done:
+ prs_mem_free(&qbuf);
+ prs_mem_free(&rbuf);
+
+ return result;
+}
+
+/** Add a form to a printer.
+ *
+ * @param cli Pointer to client state structure which is open
+ * on the SPOOLSS pipe.
+ * @param mem_ctx Pointer to an initialised talloc context.
+ *
+ * @param handle Policy handle opened with cli_spoolss_open_printer_ex
+ * or cli_spoolss_addprinterex.
+ * @param level Form info level to add - should always be 1.
+ * @param form A pointer to the form to be added.
+ *
+ */
+
+WERROR cli_spoolss_addform(struct cli_state *cli, TALLOC_CTX *mem_ctx,
+ POLICY_HND *handle, uint32 level, FORM *form)
+{
+ prs_struct qbuf, rbuf;
+ SPOOL_Q_ADDFORM q;
+ SPOOL_R_ADDFORM r;
+ WERROR result = W_ERROR(ERRgeneral);
+
+ ZERO_STRUCT(q);
+ ZERO_STRUCT(r);
+
+ /* Initialise parse structures */
+
+ prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL);
+ prs_init(&rbuf, 0, mem_ctx, UNMARSHALL);
+
+ /* Initialise input parameters */
+
+ make_spoolss_q_addform(&q, handle, level, form);
+
+ /* Marshall data and send request */
+
+ if (!spoolss_io_q_addform("", &q, &qbuf, 0) ||
+ !rpc_api_pipe_req(cli, SPOOLSS_ADDFORM, &qbuf, &rbuf))
+ goto done;
+
+ /* Unmarshall response */
+
+ if (!spoolss_io_r_addform("", &r, &rbuf, 0))
+ goto done;
+
+ /* Return output parameters */
+
+ result = r.status;
+
+ done:
+ prs_mem_free(&qbuf);
+ prs_mem_free(&rbuf);
+
+ return result;
+}
+
+/** Set a form on a printer.
+ *
+ * @param cli Pointer to client state structure which is open
+ * on the SPOOLSS pipe.
+ * @param mem_ctx Pointer to an initialised talloc context.
+ *
+ * @param handle Policy handle opened with cli_spoolss_open_printer_ex
+ * or cli_spoolss_addprinterex.
+ * @param level Form info level to set - should always be 1.
+ * @param form A pointer to the form to be set.
+ *
+ */
+
+WERROR cli_spoolss_setform(struct cli_state *cli, TALLOC_CTX *mem_ctx,
+ POLICY_HND *handle, uint32 level,
+ const char *form_name, FORM *form)
+{
+ prs_struct qbuf, rbuf;
+ SPOOL_Q_SETFORM q;
+ SPOOL_R_SETFORM r;
+ WERROR result = W_ERROR(ERRgeneral);
+
+ ZERO_STRUCT(q);
+ ZERO_STRUCT(r);
+
+ /* Initialise parse structures */
+
+ prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL);
+ prs_init(&rbuf, 0, mem_ctx, UNMARSHALL);
+
+ /* Initialise input parameters */
+
+ make_spoolss_q_setform(&q, handle, level, form_name, form);
+
+ /* Marshall data and send request */
+
+ if (!spoolss_io_q_setform("", &q, &qbuf, 0) ||
+ !rpc_api_pipe_req(cli, SPOOLSS_SETFORM, &qbuf, &rbuf))
+ goto done;
+
+ /* Unmarshall response */
+
+ if (!spoolss_io_r_setform("", &r, &rbuf, 0))
+ goto done;
+
+ /* Return output parameters */
+
+ result = r.status;
+
+ if (!W_ERROR_IS_OK(result))
+ goto done;
+
+
+
+ done:
+ prs_mem_free(&qbuf);
+ prs_mem_free(&rbuf);
+
+ return result;
+}
+
+/** Get a form on a printer.
+ *
+ * @param cli Pointer to client state structure which is open
+ * on the SPOOLSS pipe.
+ * @param mem_ctx Pointer to an initialised talloc context.
+ *
+ * @param handle Policy handle opened with cli_spoolss_open_printer_ex
+ * or cli_spoolss_addprinterex.
+ * @param formname Name of the form to get
+ * @param level Form info level to get - should always be 1.
+ *
+ */
+
+WERROR cli_spoolss_getform(struct cli_state *cli, TALLOC_CTX *mem_ctx,
+ uint32 offered, uint32 *needed,
+ POLICY_HND *handle, const char *formname,
+ uint32 level, FORM_1 *form)
+{
+ prs_struct qbuf, rbuf;
+ SPOOL_Q_GETFORM q;
+ SPOOL_R_GETFORM r;
+ WERROR result = W_ERROR(ERRgeneral);
+ NEW_BUFFER buffer;
+
+ ZERO_STRUCT(q);
+ ZERO_STRUCT(r);
+
+ /* Initialise parse structures */
+
+ init_buffer(&buffer, offered, mem_ctx);
+
+ prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL);
+ prs_init(&rbuf, 0, mem_ctx, UNMARSHALL);
+
+ /* Initialise input parameters */
+
+ make_spoolss_q_getform(&q, handle, formname, level, &buffer, offered);
+
+ /* Marshall data and send request */
+
+ if (!spoolss_io_q_getform("", &q, &qbuf, 0) ||
+ !rpc_api_pipe_req(cli, SPOOLSS_GETFORM, &qbuf, &rbuf))
+ goto done;
+
+ /* Unmarshall response */
+
+ if (!spoolss_io_r_getform("", &r, &rbuf, 0))
+ goto done;
+
+ /* Return output parameters */
+
+ result = r.status;
+
+ if (needed)
+ *needed = r.needed;
+
+ if (W_ERROR_IS_OK(result)) {
+ switch(level) {
+ case 1:
+ smb_io_form_1("", r.buffer, form, 0);
+ break;
+ default:
+ DEBUG(10, ("cli_spoolss_getform: unknown info level %d", level));
+ return WERR_UNKNOWN_LEVEL;
+ }
+ }
+
+ done:
+ prs_mem_free(&qbuf);
+ prs_mem_free(&rbuf);
+
+ return result;
+}
+
+/** Delete a form on a printer.
+ *
+ * @param cli Pointer to client state structure which is open
+ * on the SPOOLSS pipe.
+ * @param mem_ctx Pointer to an initialised talloc context.
+ *
+ * @param handle Policy handle opened with cli_spoolss_open_printer_ex
+ * or cli_spoolss_addprinterex.
+ * @param form The name of the form to delete.
+ *
+ */
+
+WERROR cli_spoolss_deleteform(struct cli_state *cli, TALLOC_CTX *mem_ctx,
+ POLICY_HND *handle, const char *form_name)
+{
+ prs_struct qbuf, rbuf;
+ SPOOL_Q_DELETEFORM q;
+ SPOOL_R_DELETEFORM r;
+ WERROR result = W_ERROR(ERRgeneral);
+
+ ZERO_STRUCT(q);
+ ZERO_STRUCT(r);
+
+ /* Initialise parse structures */
+
+ prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL);
+ prs_init(&rbuf, 0, mem_ctx, UNMARSHALL);
+
+ /* Initialise input parameters */
+
+ make_spoolss_q_deleteform(&q, handle, form_name);
+
+ /* Marshall data and send request */
+
+ if (!spoolss_io_q_deleteform("", &q, &qbuf, 0) ||
+ !rpc_api_pipe_req(cli, SPOOLSS_DELETEFORM, &qbuf, &rbuf))
+ goto done;
+
+ /* Unmarshall response */
+
+ if (!spoolss_io_r_deleteform("", &r, &rbuf, 0))
+ goto done;
+
+ /* Return output parameters */
+
+ result = r.status;
+
+ done:
+ prs_mem_free(&qbuf);
+ prs_mem_free(&rbuf);
+
+ return result;
+}
+
+static void decode_forms_1(TALLOC_CTX *mem_ctx, NEW_BUFFER *buffer,
+ uint32 num_forms, FORM_1 **forms)
+{
+ int i;
+
+ *forms = (FORM_1 *)talloc(mem_ctx, num_forms * sizeof(FORM_1));
+ prs_set_offset(&buffer->prs,0);
+
+ for (i = 0; i < num_forms; i++)
+ smb_io_form_1("", buffer, &((*forms)[i]), 0);
+}
+
+/** Enumerate forms
+ *
+ * @param cli Pointer to client state structure which is open
+ * on the SPOOLSS pipe.
+ * @param mem_ctx Pointer to an initialised talloc context.
+ *
+ * @param offered Buffer size offered in the request.
+ * @param needed Number of bytes needed to complete the request.
+ * may be NULL.
+ * or cli_spoolss_addprinterex.
+ * @param level Form info level to get - should always be 1.
+ * @param handle Open policy handle
+ *
+ */
+
+WERROR cli_spoolss_enumforms(struct cli_state *cli, TALLOC_CTX *mem_ctx,
+ uint32 offered, uint32 *needed,
+ POLICY_HND *handle, int level, uint32 *num_forms,
+ FORM_1 **forms)
+{
+ prs_struct qbuf, rbuf;
+ SPOOL_Q_ENUMFORMS q;
+ SPOOL_R_ENUMFORMS r;
+ WERROR result = W_ERROR(ERRgeneral);
+ NEW_BUFFER buffer;
+
+ ZERO_STRUCT(q);
+ ZERO_STRUCT(r);
+
+ /* Initialise parse structures */
+
+ init_buffer(&buffer, offered, mem_ctx);
+
+ prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL);
+ prs_init(&rbuf, 0, mem_ctx, UNMARSHALL);
+
+ /* Initialise input parameters */
+
+ make_spoolss_q_enumforms(&q, handle, level, &buffer, offered);
+
+ /* Marshall data and send request */
+
+ if (!spoolss_io_q_enumforms("", &q, &qbuf, 0) ||
+ !rpc_api_pipe_req(cli, SPOOLSS_ENUMFORMS, &qbuf, &rbuf))
+ goto done;
+
+ /* Unmarshall response */
+
+ if (!spoolss_io_r_enumforms("", &r, &rbuf, 0))
+ goto done;
+
+ /* Return output parameters */
+
+ result = r.status;
+
+ if (needed)
+ *needed = r.needed;
+
+ if (num_forms)
+ *num_forms = r.numofforms;
+
+ decode_forms_1(mem_ctx, r.buffer, *num_forms, forms);
+
+ done:
+ prs_mem_free(&qbuf);
+ prs_mem_free(&rbuf);
+
+ return result;
+}
+
+static void decode_jobs_1(TALLOC_CTX *mem_ctx, NEW_BUFFER *buffer,
+ uint32 num_jobs, JOB_INFO_1 **jobs)
+{
+ uint32 i;
+
+ *jobs = (JOB_INFO_1 *)talloc(mem_ctx, num_jobs * sizeof(JOB_INFO_1));
+ prs_set_offset(&buffer->prs,0);
+
+ for (i = 0; i < num_jobs; i++)
+ smb_io_job_info_1("", buffer, &((*jobs)[i]), 0);
+}
+
+static void decode_jobs_2(TALLOC_CTX *mem_ctx, NEW_BUFFER *buffer,
+ uint32 num_jobs, JOB_INFO_2 **jobs)
+{
+ uint32 i;
+
+ *jobs = (JOB_INFO_2 *)talloc(mem_ctx, num_jobs * sizeof(JOB_INFO_2));
+ prs_set_offset(&buffer->prs,0);
+
+ for (i = 0; i < num_jobs; i++)
+ smb_io_job_info_2("", buffer, &((*jobs)[i]), 0);
+}
+
+/* Enumerate jobs */
+
+WERROR cli_spoolss_enumjobs(struct cli_state *cli, TALLOC_CTX *mem_ctx,
+ uint32 offered, uint32 *needed,
+ POLICY_HND *hnd, uint32 level, uint32 firstjob,
+ uint32 num_jobs, uint32 *returned, JOB_INFO_CTR *ctr)
+{
+ prs_struct qbuf, rbuf;
+ SPOOL_Q_ENUMJOBS q;
+ SPOOL_R_ENUMJOBS r;
+ WERROR result = W_ERROR(ERRgeneral);
+ NEW_BUFFER buffer;
+
+ ZERO_STRUCT(q);
+ ZERO_STRUCT(r);
+
+ /* Initialise parse structures */
+
+ init_buffer(&buffer, offered, mem_ctx);
+
+ prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL);
+ prs_init(&rbuf, 0, mem_ctx, UNMARSHALL);
+
+ /* Initialise input parameters */
+
+ make_spoolss_q_enumjobs(&q, hnd, firstjob, num_jobs, level, &buffer,
+ offered);
+
+ /* Marshall data and send request */
+
+ if (!spoolss_io_q_enumjobs("", &q, &qbuf, 0) ||
+ !rpc_api_pipe_req(cli, SPOOLSS_ENUMJOBS, &qbuf, &rbuf))
+ goto done;
+
+ /* Unmarshall response */
+
+ if (!spoolss_io_r_enumjobs("", &r, &rbuf, 0))
+ goto done;
+
+ /* Return output parameters */
+
+ result = r.status;
+
+ if (needed)
+ *needed = r.needed;
+
+ if (!W_ERROR_IS_OK(r.status))
+ goto done;
+
+ *returned = r.returned;
+
+ switch(level) {
+ case 1:
+ decode_jobs_1(mem_ctx, r.buffer, r.returned,
+ &ctr->job.job_info_1);
+ break;
+ case 2:
+ decode_jobs_2(mem_ctx, r.buffer, r.returned,
+ &ctr->job.job_info_2);
+ break;
+ default:
+ DEBUG(3, ("unsupported info level %d", level));
+ break;
+ }
+
+ done:
+ prs_mem_free(&qbuf);
+ prs_mem_free(&rbuf);
+
+ return result;
+}
+
+/* Set job */
+
+WERROR cli_spoolss_setjob(struct cli_state *cli, TALLOC_CTX *mem_ctx,
+ POLICY_HND *hnd, uint32 jobid, uint32 level,
+ uint32 command)
+{
+ prs_struct qbuf, rbuf;
+ SPOOL_Q_SETJOB q;
+ SPOOL_R_SETJOB r;
+ WERROR result = W_ERROR(ERRgeneral);
+
+ ZERO_STRUCT(q);
+ ZERO_STRUCT(r);
+
+ /* Initialise parse structures */
+
+ prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL);
+ prs_init(&rbuf, 0, mem_ctx, UNMARSHALL);
+
+ /* Initialise input parameters */
+
+ make_spoolss_q_setjob(&q, hnd, jobid, level, command);
+
+ /* Marshall data and send request */
+
+ if (!spoolss_io_q_setjob("", &q, &qbuf, 0) ||
+ !rpc_api_pipe_req(cli, SPOOLSS_SETJOB, &qbuf, &rbuf))
+ goto done;
+
+ /* Unmarshall response */
+
+ if (!spoolss_io_r_setjob("", &r, &rbuf, 0))
+ goto done;
+
+ /* Return output parameters */
+
+ result = r.status;
+
+ done:
+ prs_mem_free(&qbuf);
+ prs_mem_free(&rbuf);
+
+ return result;
+}
+
+/* Get job */
+
+WERROR cli_spoolss_getjob(struct cli_state *cli, TALLOC_CTX *mem_ctx,
+ uint32 offered, uint32 *needed,
+ POLICY_HND *hnd, uint32 jobid, uint32 level,
+ JOB_INFO_CTR *ctr)
+{
+ prs_struct qbuf, rbuf;
+ SPOOL_Q_GETJOB q;
+ SPOOL_R_GETJOB r;
+ WERROR result = W_ERROR(ERRgeneral);
+ NEW_BUFFER buffer;
+
+ ZERO_STRUCT(q);
+ ZERO_STRUCT(r);
+
+ /* Initialise parse structures */
+
+ init_buffer(&buffer, offered, mem_ctx);
+
+ prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL);
+ prs_init(&rbuf, 0, mem_ctx, UNMARSHALL);
+
+ /* Initialise input parameters */
+
+ make_spoolss_q_getjob(&q, hnd, jobid, level, &buffer, offered);
+
+ /* Marshall data and send request */
+
+ if (!spoolss_io_q_getjob("", &q, &qbuf, 0) ||
+ !rpc_api_pipe_req(cli, SPOOLSS_GETJOB, &qbuf, &rbuf))
+ goto done;
+
+ /* Unmarshall response */
+
+ if (!spoolss_io_r_getjob("", &r, &rbuf, 0))
+ goto done;
+
+ /* Return output parameters */
+
+ result = r.status;
+
+ if (needed)
+ *needed = r.needed;
+
+ if (!W_ERROR_IS_OK(r.status))
+ goto done;
+
+ switch(level) {
+ case 1:
+ decode_jobs_1(mem_ctx, r.buffer, 1, &ctr->job.job_info_1);
+ break;
+ case 2:
+ decode_jobs_2(mem_ctx, r.buffer, 1, &ctr->job.job_info_2);
+ break;
+ default:
+ DEBUG(3, ("unsupported info level %d", level));
+ break;
+ }
+
+ done:
+ prs_mem_free(&qbuf);
+ prs_mem_free(&rbuf);
+
+ return result;
+}
+
+/* Startpageprinter. Sent to notify the spooler when a page is about to be
+ sent to a printer. */
+
+WERROR cli_spoolss_startpageprinter(struct cli_state *cli, TALLOC_CTX *mem_ctx,
+ POLICY_HND *hnd)
+{
+ prs_struct qbuf, rbuf;
+ SPOOL_Q_STARTPAGEPRINTER q;
+ SPOOL_R_STARTPAGEPRINTER r;
+ WERROR result = W_ERROR(ERRgeneral);
+
+ ZERO_STRUCT(q);
+ ZERO_STRUCT(r);
+
+ /* Initialise parse structures */
+
+ prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL);
+ prs_init(&rbuf, 0, mem_ctx, UNMARSHALL);
+
+ /* Initialise input parameters */
+
+ make_spoolss_q_startpageprinter(&q, hnd);
+
+ /* Marshall data and send request */
+
+ if (!spoolss_io_q_startpageprinter("", &q, &qbuf, 0) ||
+ !rpc_api_pipe_req(cli, SPOOLSS_STARTPAGEPRINTER, &qbuf, &rbuf))
+ goto done;
+
+ /* Unmarshall response */
+
+ if (!spoolss_io_r_startpageprinter("", &r, &rbuf, 0))
+ goto done;
+
+ /* Return output parameters */
+
+ result = r.status;
+
+ done:
+ prs_mem_free(&qbuf);
+ prs_mem_free(&rbuf);
+
+ return result;
+}
+
+/* Endpageprinter. Sent to notify the spooler when a page has finished
+ being sent to a printer. */
+
+WERROR cli_spoolss_endpageprinter(struct cli_state *cli, TALLOC_CTX *mem_ctx,
+ POLICY_HND *hnd)
+{
+ prs_struct qbuf, rbuf;
+ SPOOL_Q_ENDPAGEPRINTER q;
+ SPOOL_R_ENDPAGEPRINTER r;
+ WERROR result = W_ERROR(ERRgeneral);
+
+ ZERO_STRUCT(q);
+ ZERO_STRUCT(r);
+
+ /* Initialise parse structures */
+
+ prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL);
+ prs_init(&rbuf, 0, mem_ctx, UNMARSHALL);
+
+ /* Initialise input parameters */
+
+ make_spoolss_q_endpageprinter(&q, hnd);
+
+ /* Marshall data and send request */
+
+ if (!spoolss_io_q_endpageprinter("", &q, &qbuf, 0) ||
+ !rpc_api_pipe_req(cli, SPOOLSS_ENDPAGEPRINTER, &qbuf, &rbuf))
+ goto done;
+
+ /* Unmarshall response */
+
+ if (!spoolss_io_r_endpageprinter("", &r, &rbuf, 0))
+ goto done;
+
+ /* Return output parameters */
+
+ result = r.status;
+
+ done:
+ prs_mem_free(&qbuf);
+ prs_mem_free(&rbuf);
+
+ return result;
+}
+
+/* Startdocprinter. Sent to notify the spooler that a document is about
+ to be spooled for printing. */
+
+WERROR cli_spoolss_startdocprinter(struct cli_state *cli, TALLOC_CTX *mem_ctx,
+ POLICY_HND *hnd, char *docname,
+ char *outputfile, char *datatype,
+ uint32 *jobid)
+{
+ prs_struct qbuf, rbuf;
+ SPOOL_Q_STARTDOCPRINTER q;
+ SPOOL_R_STARTDOCPRINTER r;
+ WERROR result = W_ERROR(ERRgeneral);
+ uint32 level = 1;
+
+ ZERO_STRUCT(q);
+ ZERO_STRUCT(r);
+
+ /* Initialise parse structures */
+
+ prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL);
+ prs_init(&rbuf, 0, mem_ctx, UNMARSHALL);
+
+ /* Initialise input parameters */
+
+ make_spoolss_q_startdocprinter(&q, hnd, level, docname, outputfile,
+ datatype);
+
+ /* Marshall data and send request */
+
+ if (!spoolss_io_q_startdocprinter("", &q, &qbuf, 0) ||
+ !rpc_api_pipe_req(cli, SPOOLSS_STARTDOCPRINTER, &qbuf, &rbuf))
+ goto done;
+
+ /* Unmarshall response */
+
+ if (!spoolss_io_r_startdocprinter("", &r, &rbuf, 0))
+ goto done;
+
+ /* Return output parameters */
+
+ result = r.status;
+
+ if (W_ERROR_IS_OK(result))
+ *jobid = r.jobid;
+
+ done:
+ prs_mem_free(&qbuf);
+ prs_mem_free(&rbuf);
+
+ return result;
+}
+
+/* Enddocprinter. Sent to notify the spooler that a document has finished
+ being spooled. */
+
+WERROR cli_spoolss_enddocprinter(struct cli_state *cli, TALLOC_CTX *mem_ctx,
+ POLICY_HND *hnd)
+{
+ prs_struct qbuf, rbuf;
+ SPOOL_Q_ENDDOCPRINTER q;
+ SPOOL_R_ENDDOCPRINTER r;
+ WERROR result = W_ERROR(ERRgeneral);
+
+ ZERO_STRUCT(q);
+ ZERO_STRUCT(r);
+
+ /* Initialise parse structures */
+
+ prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL);
+ prs_init(&rbuf, 0, mem_ctx, UNMARSHALL);
+
+ /* Initialise input parameters */
+
+ make_spoolss_q_enddocprinter(&q, hnd);
+
+ /* Marshall data and send request */
+
+ if (!spoolss_io_q_enddocprinter("", &q, &qbuf, 0) ||
+ !rpc_api_pipe_req(cli, SPOOLSS_ENDDOCPRINTER, &qbuf, &rbuf))
+ goto done;
+
+ /* Unmarshall response */
+
+ if (!spoolss_io_r_enddocprinter("", &r, &rbuf, 0))
+ goto done;
+
+ /* Return output parameters */
+
+ result = r.status;
+
+ done:
+ prs_mem_free(&qbuf);
+ prs_mem_free(&rbuf);
+
+ return result;
+}
+
+/* Get printer data */
+
+WERROR cli_spoolss_getprinterdata(struct cli_state *cli, TALLOC_CTX *mem_ctx,
+ uint32 offered, uint32 *needed,
+ POLICY_HND *hnd, const char *valuename,
+ REGISTRY_VALUE *value)
+{
+ prs_struct qbuf, rbuf;
+ SPOOL_Q_GETPRINTERDATA q;
+ SPOOL_R_GETPRINTERDATA r;
+ WERROR result = W_ERROR(ERRgeneral);
+
+ ZERO_STRUCT(q);
+ ZERO_STRUCT(r);
+
+ /* Initialise parse structures */
+
+ prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL);
+ prs_init(&rbuf, 0, mem_ctx, UNMARSHALL);
+
+ /* Initialise input parameters */
+
+ make_spoolss_q_getprinterdata(&q, hnd, valuename, offered);
+
+ /* Marshall data and send request */
+
+ if (!spoolss_io_q_getprinterdata("", &q, &qbuf, 0) ||
+ !rpc_api_pipe_req(cli, SPOOLSS_GETPRINTERDATA, &qbuf, &rbuf))
+ goto done;
+
+ /* Unmarshall response */
+
+ if (!spoolss_io_r_getprinterdata("", &r, &rbuf, 0))
+ goto done;
+
+ result = r.status;
+
+ if (needed)
+ *needed = r.needed;
+
+ if (!W_ERROR_IS_OK(r.status))
+ goto done;
+
+ /* Return output parameters */
+
+ value->data_p = talloc_memdup(mem_ctx, r.data, r.needed);
+ value->type = r.type;
+ value->size = r.size;
+
+ done:
+ prs_mem_free(&qbuf);
+ prs_mem_free(&rbuf);
+
+ return result;
+}
+
+WERROR cli_spoolss_getprinterdataex(struct cli_state *cli, TALLOC_CTX *mem_ctx,
+ uint32 offered, uint32 *needed,
+ POLICY_HND *hnd, const char *keyname,
+ const char *valuename,
+ REGISTRY_VALUE *value)
+{
+ prs_struct qbuf, rbuf;
+ SPOOL_Q_GETPRINTERDATAEX q;
+ SPOOL_R_GETPRINTERDATAEX r;
+ WERROR result = W_ERROR(ERRgeneral);
+
+ ZERO_STRUCT(q);
+ ZERO_STRUCT(r);
+
+ /* Initialise parse structures */
+
+ prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL);
+ prs_init(&rbuf, 0, mem_ctx, UNMARSHALL);
+
+ /* Initialise input parameters */
+
+ make_spoolss_q_getprinterdataex(&q, hnd, keyname, valuename, offered);
+
+ /* Marshall data and send request */
+
+ if (!spoolss_io_q_getprinterdataex("", &q, &qbuf, 0) ||
+ !rpc_api_pipe_req(cli, SPOOLSS_GETPRINTERDATAEX, &qbuf, &rbuf))
+ goto done;
+
+ /* Unmarshall response */
+
+ if (!spoolss_io_r_getprinterdataex("", &r, &rbuf, 0))
+ goto done;
+
+ result = r.status;
+
+ if (needed)
+ *needed = r.needed;
+
+ if (!W_ERROR_IS_OK(r.status))
+ goto done;
+
+ /* Return output parameters */
+
+ value->data_p = talloc_memdup(mem_ctx, r.data, r.needed);
+ value->type = r.type;
+ value->size = r.needed;
+
+ done:
+ prs_mem_free(&qbuf);
+ prs_mem_free(&rbuf);
+
+ return result;
+}
+
+/* Set printer data */
+
+WERROR cli_spoolss_setprinterdata(struct cli_state *cli, TALLOC_CTX *mem_ctx,
+ POLICY_HND *hnd, REGISTRY_VALUE *value)
+{
+ prs_struct qbuf, rbuf;
+ SPOOL_Q_SETPRINTERDATA q;
+ SPOOL_R_SETPRINTERDATA r;
+ WERROR result = W_ERROR(ERRgeneral);
+
+ ZERO_STRUCT(q);
+ ZERO_STRUCT(r);
+
+ /* Initialise parse structures */
+
+ prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL);
+ prs_init(&rbuf, 0, mem_ctx, UNMARSHALL);
+
+ /* Initialise input parameters */
+
+ make_spoolss_q_setprinterdata(
+ &q, hnd, value->valuename, value->type, (char *)value->data_p, value->size);
+
+ /* Marshall data and send request */
+
+ if (!spoolss_io_q_setprinterdata("", &q, &qbuf, 0) ||
+ !rpc_api_pipe_req(cli, SPOOLSS_SETPRINTERDATA, &qbuf, &rbuf))
+ goto done;
+
+ /* Unmarshall response */
+
+ if (!spoolss_io_r_setprinterdata("", &r, &rbuf, 0))
+ goto done;
+
+ result = r.status;
+
+ if (!W_ERROR_IS_OK(r.status))
+ goto done;
+
+ done:
+ prs_mem_free(&qbuf);
+ prs_mem_free(&rbuf);
+
+ return result;
+}
+
+WERROR cli_spoolss_setprinterdataex(struct cli_state *cli, TALLOC_CTX *mem_ctx,
+ POLICY_HND *hnd, char *keyname,
+ REGISTRY_VALUE *value)
+{
+ prs_struct qbuf, rbuf;
+ SPOOL_Q_SETPRINTERDATAEX q;
+ SPOOL_R_SETPRINTERDATAEX r;
+ WERROR result = W_ERROR(ERRgeneral);
+
+ ZERO_STRUCT(q);
+ ZERO_STRUCT(r);
+
+ /* Initialise parse structures */
+
+ prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL);
+ prs_init(&rbuf, 0, mem_ctx, UNMARSHALL);
+
+ /* Initialise input parameters */
+
+ make_spoolss_q_setprinterdataex(
+ &q, hnd, keyname, value->valuename, value->type, (char *)value->data_p,
+ value->size);
+
+ /* Marshall data and send request */
+
+ if (!spoolss_io_q_setprinterdataex("", &q, &qbuf, 0) ||
+ !rpc_api_pipe_req(cli, SPOOLSS_SETPRINTERDATAEX, &qbuf, &rbuf))
+ goto done;
+
+ /* Unmarshall response */
+
+ if (!spoolss_io_r_setprinterdataex("", &r, &rbuf, 0))
+ goto done;
+
+ result = r.status;
+
+ if (!W_ERROR_IS_OK(r.status))
+ goto done;
+
+ done:
+ prs_mem_free(&qbuf);
+ prs_mem_free(&rbuf);
+
+ return result;
+}
+
+/* Enum printer data */
+
+WERROR cli_spoolss_enumprinterdata(struct cli_state *cli, TALLOC_CTX *mem_ctx,
+ POLICY_HND *hnd, uint32 ndx,
+ uint32 value_offered, uint32 data_offered,
+ uint32 *value_needed, uint32 *data_needed,
+ REGISTRY_VALUE *value)
+{
+ prs_struct qbuf, rbuf;
+ SPOOL_Q_ENUMPRINTERDATA q;
+ SPOOL_R_ENUMPRINTERDATA r;
+ WERROR result = W_ERROR(ERRgeneral);
+
+ ZERO_STRUCT(q);
+ ZERO_STRUCT(r);
+
+ /* Initialise parse structures */
+
+ prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL);
+ prs_init(&rbuf, 0, mem_ctx, UNMARSHALL);
+
+ /* Initialise input parameters */
+
+ make_spoolss_q_enumprinterdata(&q, hnd, ndx, value_offered, data_offered);
+
+ /* Marshall data and send request */
+
+ if (!spoolss_io_q_enumprinterdata("", &q, &qbuf, 0) ||
+ !rpc_api_pipe_req(cli, SPOOLSS_ENUMPRINTERDATA, &qbuf, &rbuf))
+ goto done;
+
+ /* Unmarshall response */
+
+ if (!spoolss_io_r_enumprinterdata("", &r, &rbuf, 0))
+ goto done;
+
+ result = r.status;
+
+ if (!W_ERROR_IS_OK(r.status))
+ goto done;
+
+ /* Return data */
+
+ if (value_needed)
+ *value_needed = r.realvaluesize;
+
+ if (data_needed)
+ *data_needed = r.realdatasize;
+
+ if (value) {
+ rpcstr_pull(value->valuename, r.value, sizeof(value->valuename), -1,
+ STR_TERMINATE);
+ value->data_p = talloc_memdup(mem_ctx, r.data, r.realdatasize);
+ value->type = r.type;
+ value->size = r.realdatasize;
+ }
+
+ done:
+ prs_mem_free(&qbuf);
+ prs_mem_free(&rbuf);
+
+ return result;
+}
+
+WERROR cli_spoolss_enumprinterdataex(struct cli_state *cli, TALLOC_CTX *mem_ctx,
+ uint32 offered, uint32 *needed,
+ POLICY_HND *hnd, const char *keyname,
+ REGVAL_CTR *ctr)
+{
+ prs_struct qbuf, rbuf;
+ SPOOL_Q_ENUMPRINTERDATAEX q;
+ SPOOL_R_ENUMPRINTERDATAEX r;
+ WERROR result = W_ERROR(ERRgeneral);
+ int i;
+
+ ZERO_STRUCT(q);
+ ZERO_STRUCT(r);
+
+ /* Initialise parse structures */
+
+ prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL);
+ prs_init(&rbuf, 0, mem_ctx, UNMARSHALL);
+
+ /* Initialise input parameters */
+
+ make_spoolss_q_enumprinterdataex(&q, hnd, keyname, offered);
+
+ /* Marshall data and send request */
+
+ if (!spoolss_io_q_enumprinterdataex("", &q, &qbuf, 0) ||
+ !rpc_api_pipe_req(cli, SPOOLSS_ENUMPRINTERDATAEX, &qbuf, &rbuf))
+ goto done;
+
+ /* Unmarshall response */
+
+ if (!spoolss_io_r_enumprinterdataex("", &r, &rbuf, 0))
+ goto done;
+
+ result = r.status;
+
+ if (needed)
+ *needed = r.needed;
+
+ if (!W_ERROR_IS_OK(r.status))
+ goto done;
+
+ /* Return data */
+
+ ZERO_STRUCTP(ctr);
+ regval_ctr_init(ctr);
+
+ for (i = 0; i < r.returned; i++) {
+ PRINTER_ENUM_VALUES *v = &r.ctr.values[i];
+ fstring name;
+
+ rpcstr_pull(name, v->valuename.buffer, sizeof(name), -1,
+ STR_TERMINATE);
+ regval_ctr_addvalue(ctr, name, v->type, (const char *)v->data, v->data_len);
+ }
+
+ done:
+ prs_mem_free(&qbuf);
+ prs_mem_free(&rbuf);
+
+ return result;
+}
+
+/* Write data to printer */
+
+WERROR cli_spoolss_writeprinter(struct cli_state *cli, TALLOC_CTX *mem_ctx,
+ POLICY_HND *hnd, uint32 data_size, char *data,
+ uint32 *num_written)
+{
+ prs_struct qbuf, rbuf;
+ SPOOL_Q_WRITEPRINTER q;
+ SPOOL_R_WRITEPRINTER r;
+ WERROR result = W_ERROR(ERRgeneral);
+
+ ZERO_STRUCT(q);
+ ZERO_STRUCT(r);
+
+ /* Initialise parse structures */
+
+ prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL);
+ prs_init(&rbuf, 0, mem_ctx, UNMARSHALL);
+
+ /* Initialise input parameters */
+
+ make_spoolss_q_writeprinter(&q, hnd, data_size, data);
+
+ /* Marshall data and send request */
+
+ if (!spoolss_io_q_writeprinter("", &q, &qbuf, 0) ||
+ !rpc_api_pipe_req(cli, SPOOLSS_WRITEPRINTER, &qbuf, &rbuf))
+ goto done;
+
+ /* Unmarshall response */
+
+ if (!spoolss_io_r_writeprinter("", &r, &rbuf, 0))
+ goto done;
+
+ result = r.status;
+
+ if (!W_ERROR_IS_OK(r.status))
+ goto done;
+
+ if (num_written)
+ *num_written = r.buffer_written;
+
+ done:
+ prs_mem_free(&qbuf);
+ prs_mem_free(&rbuf);
+
+ return result;
+}
+
+/* Delete printer data */
+
+WERROR cli_spoolss_deleteprinterdata(struct cli_state *cli, TALLOC_CTX *mem_ctx,
+ POLICY_HND *hnd, char *valuename)
+{
+ prs_struct qbuf, rbuf;
+ SPOOL_Q_DELETEPRINTERDATA q;
+ SPOOL_R_DELETEPRINTERDATA r;
+ WERROR result = W_ERROR(ERRgeneral);
+
+ ZERO_STRUCT(q);
+ ZERO_STRUCT(r);
+
+ /* Initialise parse structures */
+
+ prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL);
+ prs_init(&rbuf, 0, mem_ctx, UNMARSHALL);
+
+ /* Initialise input parameters */
+
+ make_spoolss_q_deleteprinterdata(&q, hnd, valuename);
+
+ /* Marshall data and send request */
+
+ if (!spoolss_io_q_deleteprinterdata("", &q, &qbuf, 0) ||
+ !rpc_api_pipe_req(cli, SPOOLSS_DELETEPRINTERDATA, &qbuf, &rbuf))
+ goto done;
+
+ /* Unmarshall response */
+
+ if (!spoolss_io_r_deleteprinterdata("", &r, &rbuf, 0))
+ goto done;
+
+ result = r.status;
+
+ if (!W_ERROR_IS_OK(r.status))
+ goto done;
+
+ done:
+ prs_mem_free(&qbuf);
+ prs_mem_free(&rbuf);
+
+ return result;
+}
+
+WERROR cli_spoolss_deleteprinterdataex(struct cli_state *cli, TALLOC_CTX *mem_ctx,
+ POLICY_HND *hnd, char *keyname,
+ char *valuename)
+{
+ prs_struct qbuf, rbuf;
+ SPOOL_Q_DELETEPRINTERDATAEX q;
+ SPOOL_R_DELETEPRINTERDATAEX r;
+ WERROR result = W_ERROR(ERRgeneral);
+
+ ZERO_STRUCT(q);
+ ZERO_STRUCT(r);
+
+ /* Initialise parse structures */
+
+ prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL);
+ prs_init(&rbuf, 0, mem_ctx, UNMARSHALL);
+
+ /* Initialise input parameters */
+
+ make_spoolss_q_deleteprinterdataex(&q, hnd, keyname, valuename);
+
+ /* Marshall data and send request */
+
+ if (!spoolss_io_q_deleteprinterdataex("", &q, &qbuf, 0) ||
+ !rpc_api_pipe_req(cli, SPOOLSS_DELETEPRINTERDATAEX, &qbuf, &rbuf))
+ goto done;
+
+ /* Unmarshall response */
+
+ if (!spoolss_io_r_deleteprinterdataex("", &r, &rbuf, 0))
+ goto done;
+
+ result = r.status;
+
+ if (!W_ERROR_IS_OK(r.status))
+ goto done;
+
+ done:
+ prs_mem_free(&qbuf);
+ prs_mem_free(&rbuf);
+
+ return result;
+}
+
+WERROR cli_spoolss_enumprinterkey(struct cli_state *cli, TALLOC_CTX *mem_ctx,
+ uint32 offered, uint32 *needed,
+ POLICY_HND *hnd, const char *keyname,
+ uint16 **keylist, uint32 *len)
+{
+ prs_struct qbuf, rbuf;
+ SPOOL_Q_ENUMPRINTERKEY q;
+ SPOOL_R_ENUMPRINTERKEY r;
+ WERROR result = W_ERROR(ERRgeneral);
+
+ ZERO_STRUCT(q);
+ ZERO_STRUCT(r);
+
+ /* Initialise parse structures */
+
+ prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL);
+ prs_init(&rbuf, 0, mem_ctx, UNMARSHALL);
+
+ /* Initialise input parameters */
+
+ make_spoolss_q_enumprinterkey(&q, hnd, keyname, offered);
+
+ /* Marshall data and send request */
+
+ if (!spoolss_io_q_enumprinterkey("", &q, &qbuf, 0) ||
+ !rpc_api_pipe_req(cli, SPOOLSS_ENUMPRINTERKEY, &qbuf, &rbuf))
+ goto done;
+
+ /* Unmarshall response */
+
+ if (!spoolss_io_r_enumprinterkey("", &r, &rbuf, 0))
+ goto done;
+
+ result = r.status;
+
+ if (needed)
+ *needed = r.needed;
+
+ if (!W_ERROR_IS_OK(r.status))
+ goto done;
+
+ /* Copy results */
+
+ if (keylist) {
+ *keylist = (uint16 *)malloc(r.keys.buf_len * 2);
+ memcpy(*keylist, r.keys.buffer, r.keys.buf_len * 2);
+ if (len)
+ *len = r.keys.buf_len * 2;
+ }
+
+ done:
+ prs_mem_free(&qbuf);
+ prs_mem_free(&rbuf);
+
+ return result;
+}
+
+WERROR cli_spoolss_deleteprinterkey(struct cli_state *cli, TALLOC_CTX *mem_ctx,
+ POLICY_HND *hnd, char *keyname)
+{
+ prs_struct qbuf, rbuf;
+ SPOOL_Q_DELETEPRINTERKEY q;
+ SPOOL_R_DELETEPRINTERKEY r;
+ WERROR result = W_ERROR(ERRgeneral);
+
+ ZERO_STRUCT(q);
+ ZERO_STRUCT(r);
+
+ /* Initialise parse structures */
+
+ prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL);
+ prs_init(&rbuf, 0, mem_ctx, UNMARSHALL);
+
+ /* Initialise input parameters */
+
+ make_spoolss_q_deleteprinterkey(&q, hnd, keyname);
+
+ /* Marshall data and send request */
+
+ if (!spoolss_io_q_deleteprinterkey("", &q, &qbuf, 0) ||
+ !rpc_api_pipe_req(cli, SPOOLSS_DELETEPRINTERKEY, &qbuf, &rbuf))
+ goto done;
+
+ /* Unmarshall response */
+
+ if (!spoolss_io_r_deleteprinterkey("", &r, &rbuf, 0))
+ goto done;
+
+ result = r.status;
+
+ if (!W_ERROR_IS_OK(r.status))
+ goto done;
+
+ done:
+ prs_mem_free(&qbuf);
+ prs_mem_free(&rbuf);
+
+ return result;
+}
+
+/** @} **/
diff --git a/source/rpc_client/cli_spoolss_notify.c b/source/rpc_client/cli_spoolss_notify.c
new file mode 100644
index 00000000000..f4eda332bb1
--- /dev/null
+++ b/source/rpc_client/cli_spoolss_notify.c
@@ -0,0 +1,272 @@
+/*
+ Unix SMB/CIFS implementation.
+ RPC pipe client
+
+ Copyright (C) Gerald Carter 2001-2002,
+ Copyright (C) Tim Potter 2000-2002,
+ Copyright (C) Andrew Tridgell 1994-2000,
+ Copyright (C) Luke Kenneth Casson Leighton 1996-2000,
+ Copyright (C) Jean-Francois Micouleau 1999-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 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"
+
+/*
+ * SPOOLSS Client RPC's used by servers as the notification
+ * back channel.
+ */
+
+/* Send a ReplyOpenPrinter request. This rpc is made by the printer
+ server to the printer client in response to a rffpcnex request.
+ The rrfpcnex request names a printer and a handle (the printerlocal
+ value) and this rpc establishes a back-channel over which printer
+ notifications are performed. */
+
+WERROR cli_spoolss_reply_open_printer(struct cli_state *cli, TALLOC_CTX *mem_ctx,
+ const char *printer, uint32 printerlocal, uint32 type,
+ POLICY_HND *handle)
+{
+ prs_struct qbuf, rbuf;
+ SPOOL_Q_REPLYOPENPRINTER q;
+ SPOOL_R_REPLYOPENPRINTER r;
+ WERROR result = W_ERROR(ERRgeneral);
+
+ /* Initialise input parameters */
+
+ prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL);
+ prs_init(&rbuf, 0, mem_ctx, UNMARSHALL);
+
+ make_spoolss_q_replyopenprinter(&q, printer, printerlocal, type);
+
+ /* Marshall data and send request */
+
+ if (!spoolss_io_q_replyopenprinter("", &q, &qbuf, 0) ||
+ !rpc_api_pipe_req (cli, SPOOLSS_REPLYOPENPRINTER, &qbuf, &rbuf))
+ goto done;
+
+ /* Unmarshall response */
+
+ if (!spoolss_io_r_replyopenprinter("", &r, &rbuf, 0))
+ goto done;
+
+ /* Return result */
+
+ memcpy(handle, &r.handle, sizeof(r.handle));
+ result = r.status;
+
+done:
+ prs_mem_free(&qbuf);
+ prs_mem_free(&rbuf);
+
+ return result;
+}
+
+/* Close a back-channel notification connection */
+
+WERROR cli_spoolss_reply_close_printer(struct cli_state *cli, TALLOC_CTX *mem_ctx,
+ POLICY_HND *handle)
+{
+ prs_struct qbuf, rbuf;
+ SPOOL_Q_REPLYCLOSEPRINTER q;
+ SPOOL_R_REPLYCLOSEPRINTER r;
+ WERROR result = W_ERROR(ERRgeneral);
+
+ /* Initialise input parameters */
+
+ prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL);
+ prs_init(&rbuf, 0, mem_ctx, UNMARSHALL);
+
+ make_spoolss_q_reply_closeprinter(&q, handle);
+
+ /* Marshall data and send request */
+
+ if (!spoolss_io_q_replycloseprinter("", &q, &qbuf, 0) ||
+ !rpc_api_pipe_req (cli, SPOOLSS_REPLYCLOSEPRINTER, &qbuf, &rbuf))
+ goto done;
+
+ /* Unmarshall response */
+
+ if (!spoolss_io_r_replycloseprinter("", &r, &rbuf, 0))
+ goto done;
+
+ /* Return result */
+
+ result = r.status;
+
+done:
+ prs_mem_free(&qbuf);
+ prs_mem_free(&rbuf);
+
+ return result;
+}
+
+/*********************************************************************
+ This SPOOLSS_ROUTERREPLYPRINTER function is used to send a change
+ notification event when the registration **did not** use
+ SPOOL_NOTIFY_OPTION_TYPE structure to specify the events to monitor.
+ Also see cli_spolss_reply_rrpcn()
+ *********************************************************************/
+
+WERROR cli_spoolss_routerreplyprinter(struct cli_state *cli, TALLOC_CTX *mem_ctx,
+ POLICY_HND *pol, uint32 condition, uint32 change_id)
+{
+ prs_struct qbuf, rbuf;
+ SPOOL_Q_ROUTERREPLYPRINTER q;
+ SPOOL_R_ROUTERREPLYPRINTER r;
+ WERROR result = W_ERROR(ERRgeneral);
+
+ /* Initialise input parameters */
+
+ prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL);
+ prs_init(&rbuf, 0, mem_ctx, UNMARSHALL);
+
+ make_spoolss_q_routerreplyprinter(&q, pol, condition, change_id);
+
+ /* Marshall data and send request */
+
+ if (!spoolss_io_q_routerreplyprinter("", &q, &qbuf, 0) ||
+ !rpc_api_pipe_req (cli, SPOOLSS_ROUTERREPLYPRINTER, &qbuf, &rbuf))
+ goto done;
+
+ /* Unmarshall response */
+
+ if (!spoolss_io_r_routerreplyprinter("", &r, &rbuf, 0))
+ goto done;
+
+ /* Return output parameters */
+
+ result = r.status;
+
+done:
+ prs_mem_free(&qbuf);
+ prs_mem_free(&rbuf);
+
+ return result;
+}
+
+/*********************************************************************
+ This SPOOLSS_REPLY_RRPCN function is used to send a change
+ notification event when the registration **did** use
+ SPOOL_NOTIFY_OPTION_TYPE structure to specify the events to monitor
+ Also see cli_spoolss_routereplyprinter()
+ *********************************************************************/
+
+WERROR cli_spoolss_rrpcn(struct cli_state *cli, TALLOC_CTX *mem_ctx,
+ POLICY_HND *pol, uint32 notify_data_len,
+ SPOOL_NOTIFY_INFO_DATA *notify_data,
+ uint32 change_low, uint32 change_high)
+{
+ prs_struct qbuf, rbuf;
+ SPOOL_Q_REPLY_RRPCN q;
+ SPOOL_R_REPLY_RRPCN r;
+ WERROR result = W_ERROR(ERRgeneral);
+ SPOOL_NOTIFY_INFO notify_info;
+
+ ZERO_STRUCT(q);
+ ZERO_STRUCT(r);
+
+ /* Initialise parse structures */
+
+ prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL);
+ prs_init(&rbuf, 0, mem_ctx, UNMARSHALL);
+
+ ZERO_STRUCT(notify_info);
+
+ /* Initialise input parameters */
+
+ notify_info.version = 0x2;
+ notify_info.flags = 0x00020000; /* ?? */
+ notify_info.count = notify_data_len;
+ notify_info.data = notify_data;
+
+ /* create and send a MSRPC command with api */
+ /* store the parameters */
+
+ make_spoolss_q_reply_rrpcn(&q, pol, change_low, change_high,
+ &notify_info);
+
+ /* Marshall data and send request */
+
+ if(!spoolss_io_q_reply_rrpcn("", &q, &qbuf, 0) ||
+ !rpc_api_pipe_req(cli, SPOOLSS_RRPCN, &qbuf, &rbuf))
+ goto done;
+
+ /* Unmarshall response */
+
+ if(!spoolss_io_r_reply_rrpcn("", &r, &rbuf, 0))
+ goto done;
+
+ if (r.unknown0 == 0x00080000)
+ DEBUG(8,("cli_spoolss_reply_rrpcn: I think the spooler resonded that the notification was ignored.\n"));
+ else if ( r.unknown0 != 0x0 )
+ DEBUG(8,("cli_spoolss_reply_rrpcn: unknown0 is non-zero [0x%x]\n", r.unknown0));
+
+ result = r.status;
+
+done:
+ prs_mem_free(&qbuf);
+ prs_mem_free(&rbuf);
+
+ return result;
+}
+
+/*********************************************************************
+ *********************************************************************/
+
+WERROR cli_spoolss_rffpcnex(struct cli_state *cli, TALLOC_CTX *mem_ctx,
+ POLICY_HND *pol, uint32 flags, uint32 options,
+ const char *localmachine, uint32 printerlocal,
+ SPOOL_NOTIFY_OPTION *option)
+{
+ prs_struct qbuf, rbuf;
+ SPOOL_Q_RFFPCNEX q;
+ SPOOL_R_RFFPCNEX r;
+ WERROR result = W_ERROR(ERRgeneral);
+
+ ZERO_STRUCT(q);
+ ZERO_STRUCT(r);
+
+ /* Initialise parse structures */
+
+ prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL);
+ prs_init(&rbuf, 0, mem_ctx, UNMARSHALL);
+
+ /* Initialise input parameters */
+
+ make_spoolss_q_rffpcnex(
+ &q, pol, flags, options, localmachine, printerlocal,
+ option);
+
+ /* Marshall data and send request */
+
+ if(!spoolss_io_q_rffpcnex("", &q, &qbuf, 0) ||
+ !rpc_api_pipe_req(cli, SPOOLSS_RFFPCNEX, &qbuf, &rbuf))
+ goto done;
+
+ /* Unmarshall response */
+
+ if(!spoolss_io_r_rffpcnex("", &r, &rbuf, 0))
+ goto done;
+
+ result = r.status;
+
+done:
+ prs_mem_free(&qbuf);
+ prs_mem_free(&rbuf);
+
+ return result;
+}
diff --git a/source/rpc_client/cli_srvsvc.c b/source/rpc_client/cli_srvsvc.c
new file mode 100644
index 00000000000..555703cf4d8
--- /dev/null
+++ b/source/rpc_client/cli_srvsvc.c
@@ -0,0 +1,442 @@
+/*
+ Unix SMB/CIFS implementation.
+ NT Domain Authentication SMB / MSRPC client
+ Copyright (C) Andrew Tridgell 1994-2000
+ Copyright (C) Luke Kenneth Casson Leighton 1996-2000
+ Copyright (C) Tim Potter 2001
+ Copyright (C) Jim McDonough <jmcd@us.ibm.com> 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 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"
+
+WERROR cli_srvsvc_net_srv_get_info(struct cli_state *cli,
+ TALLOC_CTX *mem_ctx,
+ uint32 switch_value, SRV_INFO_CTR *ctr)
+{
+ prs_struct qbuf, rbuf;
+ SRV_Q_NET_SRV_GET_INFO q;
+ SRV_R_NET_SRV_GET_INFO r;
+ WERROR result = W_ERROR(ERRgeneral);
+
+ ZERO_STRUCT(q);
+ ZERO_STRUCT(r);
+
+ /* Initialise parse structures */
+
+ prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL);
+ prs_init(&rbuf, 0, mem_ctx, UNMARSHALL);
+
+ /* Initialise input parameters */
+
+ init_srv_q_net_srv_get_info(&q, cli->srv_name_slash, switch_value);
+
+ /* Marshall data and send request */
+
+ if (!srv_io_q_net_srv_get_info("", &q, &qbuf, 0) ||
+ !rpc_api_pipe_req(cli, SRV_NET_SRV_GET_INFO, &qbuf, &rbuf))
+ goto done;
+
+ /* Unmarshall response */
+
+ r.ctr = ctr;
+
+ if (!srv_io_r_net_srv_get_info("", &r, &rbuf, 0))
+ goto done;
+
+ result = r.status;
+
+ done:
+ prs_mem_free(&qbuf);
+ prs_mem_free(&rbuf);
+
+ return result;
+}
+
+WERROR cli_srvsvc_net_share_enum(struct cli_state *cli, TALLOC_CTX *mem_ctx,
+ uint32 info_level, SRV_SHARE_INFO_CTR *ctr,
+ int preferred_len, ENUM_HND *hnd)
+{
+ prs_struct qbuf, rbuf;
+ SRV_Q_NET_SHARE_ENUM q;
+ SRV_R_NET_SHARE_ENUM r;
+ WERROR result = W_ERROR(ERRgeneral);
+ int i;
+
+ ZERO_STRUCT(q);
+ ZERO_STRUCT(r);
+
+ /* Initialise parse structures */
+
+ prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL);
+ prs_init(&rbuf, 0, mem_ctx, UNMARSHALL);
+
+ /* Initialise input parameters */
+
+ init_srv_q_net_share_enum(
+ &q, cli->srv_name_slash, info_level, preferred_len, hnd);
+
+ /* Marshall data and send request */
+
+ if (!srv_io_q_net_share_enum("", &q, &qbuf, 0) ||
+ !rpc_api_pipe_req(cli, SRV_NET_SHARE_ENUM_ALL, &qbuf, &rbuf))
+ goto done;
+
+ /* Unmarshall response */
+
+ if (!srv_io_r_net_share_enum("", &r, &rbuf, 0))
+ goto done;
+
+ result = r.status;
+
+ if (!W_ERROR_IS_OK(result))
+ goto done;
+
+ /* Oh yuck yuck yuck - we have to copy all the info out of the
+ SRV_SHARE_INFO_CTR in the SRV_R_NET_SHARE_ENUM as when we do a
+ prs_mem_free() it will all be invalidated. The various share
+ info structures suck badly too. This really is gross. */
+
+ ZERO_STRUCTP(ctr);
+
+ if (!r.ctr.num_entries)
+ goto done;
+
+ ctr->info_level = info_level;
+ ctr->num_entries = r.ctr.num_entries;
+
+ switch(info_level) {
+ case 1:
+ ctr->share.info1 = (SRV_SHARE_INFO_1 *)talloc(
+ mem_ctx, sizeof(SRV_SHARE_INFO_1) * ctr->num_entries);
+
+ memset(ctr->share.info1, 0, sizeof(SRV_SHARE_INFO_1));
+
+ for (i = 0; i < ctr->num_entries; i++) {
+ SRV_SHARE_INFO_1 *info1 = &ctr->share.info1[i];
+ char *s;
+
+ /* Copy pointer crap */
+
+ memcpy(&info1->info_1, &r.ctr.share.info1[i].info_1,
+ sizeof(SH_INFO_1));
+
+ /* Duplicate strings */
+
+ s = unistr2_tdup(mem_ctx, &r.ctr.share.info1[i].info_1_str.uni_netname);
+ if (s)
+ init_unistr2(&info1->info_1_str.uni_netname, s, UNI_STR_TERMINATE);
+
+ s = unistr2_tdup(mem_ctx, &r.ctr.share.info1[i].info_1_str.uni_remark);
+ if (s)
+ init_unistr2(&info1->info_1_str.uni_remark, s, UNI_STR_TERMINATE);
+
+ }
+
+ break;
+ case 2:
+ ctr->share.info2 = (SRV_SHARE_INFO_2 *)talloc(
+ mem_ctx, sizeof(SRV_SHARE_INFO_2) * ctr->num_entries);
+
+ memset(ctr->share.info2, 0, sizeof(SRV_SHARE_INFO_2));
+
+ for (i = 0; i < ctr->num_entries; i++) {
+ SRV_SHARE_INFO_2 *info2 = &ctr->share.info2[i];
+ char *s;
+
+ /* Copy pointer crap */
+
+ memcpy(&info2->info_2, &r.ctr.share.info2[i].info_2,
+ sizeof(SH_INFO_2));
+
+ /* Duplicate strings */
+
+ s = unistr2_tdup(mem_ctx, &r.ctr.share.info2[i].info_2_str.uni_netname);
+ if (s)
+ init_unistr2(&info2->info_2_str.uni_netname, s, UNI_STR_TERMINATE);
+
+ s = unistr2_tdup(mem_ctx, &r.ctr.share.info2[i].info_2_str.uni_remark);
+ if (s)
+ init_unistr2(&info2->info_2_str.uni_remark, s, UNI_STR_TERMINATE);
+
+ s = unistr2_tdup(mem_ctx, &r.ctr.share.info2[i].info_2_str.uni_path);
+ if (s)
+ init_unistr2(&info2->info_2_str.uni_path, s, UNI_STR_TERMINATE);
+
+ s = unistr2_tdup(mem_ctx, &r.ctr.share.info2[i].info_2_str.uni_passwd);
+ if (s)
+ init_unistr2(&info2->info_2_str.uni_passwd, s, UNI_STR_TERMINATE);
+ }
+ break;
+ }
+ done:
+ prs_mem_free(&qbuf);
+ prs_mem_free(&rbuf);
+
+ return result;
+}
+
+WERROR cli_srvsvc_net_share_del(struct cli_state *cli, TALLOC_CTX *mem_ctx,
+ const char *sharename)
+{
+ prs_struct qbuf, rbuf;
+ SRV_Q_NET_SHARE_DEL q;
+ SRV_R_NET_SHARE_DEL r;
+ WERROR result = W_ERROR(ERRgeneral);
+
+ ZERO_STRUCT(q);
+ ZERO_STRUCT(r);
+
+ /* Initialise parse structures */
+
+ prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL);
+ prs_init(&rbuf, 0, mem_ctx, UNMARSHALL);
+
+ /* Initialise input parameters */
+
+ init_srv_q_net_share_del(&q, cli->srv_name_slash, sharename);
+
+ /* Marshall data and send request */
+
+ if (!srv_io_q_net_share_del("", &q, &qbuf, 0) ||
+ !rpc_api_pipe_req(cli, SRV_NET_SHARE_DEL, &qbuf, &rbuf))
+ goto done;
+
+ /* Unmarshall response */
+
+ if (!srv_io_r_net_share_del("", &r, &rbuf, 0))
+ goto done;
+
+ result = r.status;
+
+ done:
+ prs_mem_free(&qbuf);
+ prs_mem_free(&rbuf);
+
+ return result;
+}
+
+WERROR cli_srvsvc_net_share_add(struct cli_state *cli, TALLOC_CTX *mem_ctx,
+ const char *netname, uint32 type,
+ const char *remark, uint32 perms,
+ uint32 max_uses, uint32 num_uses,
+ const char *path, const char *passwd)
+{
+ prs_struct qbuf, rbuf;
+ SRV_Q_NET_SHARE_ADD q;
+ SRV_R_NET_SHARE_ADD r;
+ WERROR result = W_ERROR(ERRgeneral);
+
+ ZERO_STRUCT(q);
+ ZERO_STRUCT(r);
+
+ /* Initialise parse structures */
+
+ prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL);
+ prs_init(&rbuf, 0, mem_ctx, UNMARSHALL);
+
+ init_srv_q_net_share_add(&q,cli->srv_name_slash, netname, type, remark,
+ perms, max_uses, num_uses, path, passwd);
+
+ /* Marshall data and send request */
+
+ if (!srv_io_q_net_share_add("", &q, &qbuf, 0) ||
+ !rpc_api_pipe_req(cli, SRV_NET_SHARE_ADD, &qbuf, &rbuf))
+ goto done;
+
+ /* Unmarshall response */
+
+ if (!srv_io_r_net_share_add("", &r, &rbuf, 0))
+ goto done;
+
+ result = r.status;
+
+ done:
+ prs_mem_free(&qbuf);
+ prs_mem_free(&rbuf);
+
+ return result;
+}
+
+WERROR cli_srvsvc_net_remote_tod(struct cli_state *cli, TALLOC_CTX *mem_ctx,
+ char *server, TIME_OF_DAY_INFO *tod)
+{
+ prs_struct qbuf, rbuf;
+ SRV_Q_NET_REMOTE_TOD q;
+ SRV_R_NET_REMOTE_TOD r;
+ WERROR result = W_ERROR(ERRgeneral);
+
+ ZERO_STRUCT(q);
+ ZERO_STRUCT(r);
+
+ /* Initialise parse structures */
+
+ prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL);
+ prs_init(&rbuf, 0, mem_ctx, UNMARSHALL);
+
+ /* Initialise input parameters */
+
+ init_srv_q_net_remote_tod(&q, cli->srv_name_slash);
+
+ /* Marshall data and send request */
+
+ if (!srv_io_q_net_remote_tod("", &q, &qbuf, 0) ||
+ !rpc_api_pipe_req(cli, SRV_NET_REMOTE_TOD, &qbuf, &rbuf))
+ goto done;
+
+ /* Unmarshall response */
+
+ r.tod = tod;
+
+ if (!srv_io_r_net_remote_tod("", &r, &rbuf, 0))
+ goto done;
+
+ result = r.status;
+
+ if (!W_ERROR_IS_OK(result))
+ goto done;
+
+ done:
+ prs_mem_free(&qbuf);
+ prs_mem_free(&rbuf);
+
+ return result;
+}
+
+WERROR cli_srvsvc_net_file_enum(struct cli_state *cli, TALLOC_CTX *mem_ctx,
+ uint32 file_level, const char *user_name,
+ SRV_FILE_INFO_CTR *ctr, int preferred_len,
+ ENUM_HND *hnd)
+{
+ prs_struct qbuf, rbuf;
+ SRV_Q_NET_FILE_ENUM q;
+ SRV_R_NET_FILE_ENUM r;
+ WERROR result = W_ERROR(ERRgeneral);
+ int i;
+
+ ZERO_STRUCT(q);
+ ZERO_STRUCT(r);
+
+ /* Initialise parse structures */
+
+ prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL);
+ prs_init(&rbuf, 0, mem_ctx, UNMARSHALL);
+
+ /* Initialise input parameters */
+
+ init_srv_q_net_file_enum(&q, cli->srv_name_slash, NULL, user_name,
+ file_level, ctr, preferred_len, hnd);
+
+ /* Marshall data and send request */
+
+ if (!srv_io_q_net_file_enum("", &q, &qbuf, 0) ||
+ !rpc_api_pipe_req(cli, SRV_NET_FILE_ENUM, &qbuf, &rbuf))
+ goto done;
+
+ /* Unmarshall response */
+
+ if (!srv_io_r_net_file_enum("", &r, &rbuf, 0))
+ goto done;
+
+ result = r.status;
+
+ if (!W_ERROR_IS_OK(result))
+ goto done;
+
+ /* copy the data over to the ctr */
+
+ ZERO_STRUCTP(ctr);
+
+ ctr->switch_value = file_level;
+
+ ctr->num_entries = ctr->num_entries2 = r.ctr.num_entries;
+
+ switch(file_level) {
+ case 3:
+ ctr->file.info3 = (SRV_FILE_INFO_3 *)talloc(
+ mem_ctx, sizeof(SRV_FILE_INFO_3) * ctr->num_entries);
+
+ memset(ctr->file.info3, 0,
+ sizeof(SRV_FILE_INFO_3) * ctr->num_entries);
+
+ for (i = 0; i < r.ctr.num_entries; i++) {
+ SRV_FILE_INFO_3 *info3 = &ctr->file.info3[i];
+ char *s;
+
+ /* Copy pointer crap */
+
+ memcpy(&info3->info_3, &r.ctr.file.info3[i].info_3,
+ sizeof(FILE_INFO_3));
+
+ /* Duplicate strings */
+
+ s = unistr2_tdup(mem_ctx, &r.ctr.file.info3[i].info_3_str.uni_path_name);
+ if (s)
+ init_unistr2(&info3->info_3_str.uni_path_name, s, UNI_STR_TERMINATE);
+
+ s = unistr2_tdup(mem_ctx, &r.ctr.file.info3[i].info_3_str.uni_user_name);
+ if (s)
+ init_unistr2(&info3->info_3_str.uni_user_name, s, UNI_STR_TERMINATE);
+
+ }
+
+ break;
+ }
+
+ done:
+ prs_mem_free(&qbuf);
+ prs_mem_free(&rbuf);
+
+ return result;
+}
+
+WERROR cli_srvsvc_net_file_close(struct cli_state *cli, TALLOC_CTX *mem_ctx,
+ uint32 file_id)
+{
+ prs_struct qbuf, rbuf;
+ SRV_Q_NET_FILE_CLOSE q;
+ SRV_R_NET_FILE_CLOSE r;
+ WERROR result = W_ERROR(ERRgeneral);
+
+ ZERO_STRUCT(q);
+ ZERO_STRUCT(r);
+
+ /* Initialise parse structures */
+
+ prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL);
+ prs_init(&rbuf, 0, mem_ctx, UNMARSHALL);
+
+ /* Initialise input parameters */
+
+ init_srv_q_net_file_close(&q, cli->srv_name_slash, file_id);
+
+ /* Marshall data and send request */
+
+ if (!srv_io_q_net_file_close("", &q, &qbuf, 0) ||
+ !rpc_api_pipe_req(cli, SRV_NET_FILE_CLOSE, &qbuf, &rbuf))
+ goto done;
+
+ /* Unmarshall response */
+
+ if (!srv_io_r_net_file_close("", &r, &rbuf, 0))
+ goto done;
+
+ result = r.status;
+ done:
+ prs_mem_free(&qbuf);
+ prs_mem_free(&rbuf);
+ return result;
+}
diff --git a/source/rpc_client/cli_wkssvc.c b/source/rpc_client/cli_wkssvc.c
new file mode 100644
index 00000000000..97b948bf628
--- /dev/null
+++ b/source/rpc_client/cli_wkssvc.c
@@ -0,0 +1,93 @@
+/*
+ Unix SMB/CIFS implementation.
+ NT Domain Authentication SMB / MSRPC client
+ Copyright (C) Andrew Tridgell 1994-2000
+ Copyright (C) Luke Kenneth Casson Leighton 1996-2000
+ Copyright (C) Tim Potter 2001
+ Copytight (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 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"
+
+/**
+ * WksQueryInfo rpc call (like query for server's capabilities)
+ *
+ * @param initialised client structure with \PIPE\wkssvc opened
+ * @param mem_ctx memory context assigned to this rpc binding
+ * @param wks100 WksQueryInfo structure
+ *
+ * @return NTSTATUS of rpc call
+ */
+
+NTSTATUS cli_wks_query_info(struct cli_state *cli, TALLOC_CTX *mem_ctx,
+ WKS_INFO_100 *wks100)
+{
+ prs_struct buf;
+ prs_struct rbuf;
+ WKS_Q_QUERY_INFO q_o;
+ WKS_R_QUERY_INFO r_o;
+
+ if (cli == NULL || wks100 == NULL)
+ return NT_STATUS_UNSUCCESSFUL;
+
+ /* init rpc parse structures */
+ prs_init(&buf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL);
+ prs_init(&rbuf, 0, mem_ctx, UNMARSHALL);
+
+ DEBUG(4, ("WksQueryInfo\n"));
+
+ /* init query structure with rpc call arguments */
+ init_wks_q_query_info(&q_o, cli->desthost, 100);
+
+ /* marshall data */
+ if (!wks_io_q_query_info("", &q_o, &buf, 0)) {
+ prs_mem_free(&buf);
+ prs_mem_free(&rbuf);
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ /* actual rpc call over \PIPE\wkssvc */
+ if (!rpc_api_pipe_req(cli, WKS_QUERY_INFO, &buf, &rbuf)) {
+ prs_mem_free(&buf);
+ prs_mem_free(&rbuf);
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ prs_mem_free(&buf);
+
+ r_o.wks100 = wks100;
+
+ /* get call results from response buffer */
+ if (!wks_io_r_query_info("", &r_o, &rbuf, 0)) {
+ prs_mem_free(&rbuf);
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ /* check returnet status code */
+ if (NT_STATUS_IS_ERR(r_o.status)) {
+ /* report the error */
+ DEBUG(0,("WKS_R_QUERY_INFO: %s\n", nt_errstr(r_o.status)));
+ prs_mem_free(&rbuf);
+ return r_o.status;
+ }
+
+ /* do clean up */
+ prs_mem_free(&rbuf);
+
+ return NT_STATUS_OK;
+}
+
diff --git a/source/rpc_parse/.cvsignore b/source/rpc_parse/.cvsignore
new file mode 100644
index 00000000000..5f2a5c4cf75
--- /dev/null
+++ b/source/rpc_parse/.cvsignore
@@ -0,0 +1,2 @@
+*.po
+*.po32
diff --git a/source/rpc_parse/parse_dfs.c b/source/rpc_parse/parse_dfs.c
new file mode 100644
index 00000000000..0d0ce557b22
--- /dev/null
+++ b/source/rpc_parse/parse_dfs.c
@@ -0,0 +1,546 @@
+/*
+ * Unix SMB/CIFS implementation.
+ * MSDfs RPC Pipe client / server routines
+ * Copyright (C) Andrew Tridgell 1992-2000,
+ * Copyright (C) Luke Kenneth Casson Leighton 1996-2000,
+ * Copyright (C) Shirish Kalele 2000.
+ * Copyright (C) Jeremy Allison 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 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 "nterr.h"
+#include "rpc_parse.h"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_RPC_PARSE
+
+/*******************************************************************
+Make a DFS_Q_DFS_QUERY structure
+*******************************************************************/
+
+void init_dfs_q_dfs_exist(DFS_Q_DFS_EXIST *q_d)
+{
+ q_d->dummy = 0;
+}
+
+/*************************************************************
+ Read/write a DFS_Q_DFS_EXIST structure - dummy...
+ ************************************************************/
+
+BOOL dfs_io_q_dfs_exist(const char *desc, DFS_Q_DFS_EXIST *q_d, prs_struct *ps, int depth)
+{
+ if(q_d == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "dfs_io_q_dfs_exist");
+
+ return True;
+}
+
+/*************************************************************
+ Read/write a DFS_R_DFS_EXIST structure
+ ************************************************************/
+
+BOOL dfs_io_r_dfs_exist(const char *desc, DFS_R_DFS_EXIST *q_d, prs_struct *ps, int depth)
+{
+ if(q_d == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "dfs_io_r_dfs_exist");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("exist flag", ps, 0, &q_d->status))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+Make a DFS_Q_DFS_REMOVE structure
+*******************************************************************/
+
+BOOL init_dfs_q_dfs_remove(DFS_Q_DFS_REMOVE *q_d, const char *entrypath,
+ const char *servername, const char *sharename)
+{
+ DEBUG(5,("init_dfs_q_dfs_remove\n"));
+ init_unistr2(&q_d->DfsEntryPath, entrypath, UNI_STR_TERMINATE);
+ init_unistr2(&q_d->ServerName, servername, UNI_STR_TERMINATE);
+ init_unistr2(&q_d->ShareName, sharename, UNI_STR_TERMINATE);
+ q_d->ptr_ServerName = q_d->ptr_ShareName = 1;
+ return True;
+}
+
+/*******************************************************************
+Read/write a DFS_Q_DFS_REMOVE structure
+*******************************************************************/
+
+BOOL dfs_io_q_dfs_remove(const char *desc, DFS_Q_DFS_REMOVE *q_d, prs_struct *ps, int depth)
+{
+ if(q_d == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "dfs_io_q_dfs_remove");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!smb_io_unistr2("DfsEntryPath",&q_d->DfsEntryPath, 1, ps, depth))
+ return False;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("ptr_ServerName", ps, depth, &q_d->ptr_ServerName))
+ return False;
+ if(q_d->ptr_ServerName)
+ if (!smb_io_unistr2("ServerName",&q_d->ServerName, q_d->ptr_ServerName, ps, depth))
+ return False;
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("ptr_ShareName", ps, depth, &q_d->ptr_ShareName))
+ return False;
+ if(q_d->ptr_ShareName)
+ if (!smb_io_unistr2("ShareName",&q_d->ShareName, q_d->ptr_ShareName, ps, depth))
+ return False;
+ if(!prs_align(ps))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+Read/write a DFS_R_DFS_REMOVE structure
+*******************************************************************/
+
+BOOL dfs_io_r_dfs_remove(const char *desc, DFS_R_DFS_REMOVE *r_d, prs_struct *ps, int depth)
+{
+ if(r_d == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "dfs_io_r_dfs_remove");
+ depth++;
+
+ if(!prs_werror("status", ps, depth, &r_d->status))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+Make a DFS_Q_DFS_ADD structure
+*******************************************************************/
+
+BOOL init_dfs_q_dfs_add(DFS_Q_DFS_ADD *q_d, const char *entrypath,
+ const char *servername, const char *sharename,
+ const char *comment, uint32 flags)
+{
+ DEBUG(5,("init_dfs_q_dfs_add\n"));
+ q_d->ptr_DfsEntryPath = q_d->ptr_ServerName = q_d->ptr_ShareName = 1;
+ init_unistr2(&q_d->DfsEntryPath, entrypath, UNI_STR_TERMINATE);
+ init_unistr2(&q_d->ServerName, servername, UNI_STR_TERMINATE);
+ init_unistr2(&q_d->ShareName, sharename, UNI_STR_TERMINATE);
+ if(comment != NULL) {
+ init_unistr2(&q_d->Comment, comment,UNI_STR_TERMINATE);
+ q_d->ptr_Comment = 1;
+ } else {
+ q_d->ptr_Comment = 0;
+ }
+
+ q_d->Flags = flags;
+ return True;
+}
+
+/************************************************************
+ Read/write a DFS_Q_DFS_ADD structure
+ ************************************************************/
+
+BOOL dfs_io_q_dfs_add(const char *desc, DFS_Q_DFS_ADD *q_d, prs_struct *ps, int depth)
+{
+ if(q_d == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "dfs_io_q_dfs_add");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!smb_io_unistr2("DfsEntryPath",&q_d->DfsEntryPath, 1, ps, depth))
+ return False;
+ if(!prs_align(ps))
+ return False;
+
+ if(!smb_io_unistr2("ServerName",&q_d->ServerName, 1, ps, depth))
+ return False;
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("ptr_ShareName", ps, depth, &q_d->ptr_ShareName))
+ return False;
+ if(!smb_io_unistr2("ShareName",&q_d->ShareName, 1, ps, depth))
+ return False;
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("ptr_Comment", ps, depth, &q_d->ptr_Comment))
+ return False;
+ if(!smb_io_unistr2("",&q_d->Comment, q_d->ptr_Comment , ps, depth))
+ return False;
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("Flags", ps, depth, &q_d->Flags))
+ return True;
+
+ return True;
+}
+
+/************************************************************
+ Read/write a DFS_R_DFS_ADD structure
+ ************************************************************/
+
+BOOL dfs_io_r_dfs_add(const char *desc, DFS_R_DFS_ADD *r_d, prs_struct *ps, int depth)
+{
+ if(r_d == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "dfs_io_r_dfs_add");
+ depth++;
+
+ if(!prs_werror("status", ps, depth, &r_d->status))
+ return False;
+
+ return True;
+}
+
+BOOL init_dfs_q_dfs_get_info(DFS_Q_DFS_GET_INFO *q_d, const char *entrypath,
+ const char *servername, const char *sharename,
+ uint32 info_level)
+{
+ DEBUG(5,("init_dfs_q2_get_info\n"));
+ init_unistr2(&q_d->uni_path, entrypath, UNI_STR_TERMINATE);
+ init_unistr2(&q_d->uni_server, servername, UNI_STR_TERMINATE);
+ init_unistr2(&q_d->uni_share, sharename, UNI_STR_TERMINATE);
+ q_d->level = info_level;
+ q_d->ptr_server = q_d->ptr_share = 1;
+ return True;
+}
+
+/************************************************************
+ Read/write a DFS_Q_GET_INFO structure
+ ************************************************************/
+
+BOOL dfs_io_q_dfs_get_info(const char *desc, DFS_Q_DFS_GET_INFO* q_i, prs_struct* ps, int depth)
+{
+ if(q_i == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "dfs_io_q_dfs_get_info");
+ depth++;
+
+ if(!smb_io_unistr2("",&q_i->uni_path, 1, ps, depth))
+ return False;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("ptr_server", ps, depth, &q_i->ptr_server))
+ return False;
+
+ if(q_i->ptr_server)
+ if (!smb_io_unistr2("",&q_i->uni_server, q_i->ptr_server, ps, depth))
+ return False;
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("ptr_share", ps, depth, &q_i->ptr_share))
+ return False;
+ if(q_i->ptr_share)
+ if(!smb_io_unistr2("", &q_i->uni_share, q_i->ptr_share, ps, depth))
+ return False;
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("level", ps, depth, &q_i->level))
+ return False;
+ return True;
+}
+
+/************************************************************
+ Read/write a DFS_R_GET_INFO structure
+ ************************************************************/
+
+BOOL dfs_io_r_dfs_get_info(const char *desc, DFS_R_DFS_GET_INFO* r_i, prs_struct* ps, int depth)
+{
+ if(r_i == NULL)
+ return False;
+
+ if(!prs_uint32("level", ps, depth, &r_i->level))
+ return False;
+ if(!prs_uint32("ptr_ctr", ps, depth, &r_i->ptr_ctr))
+ return False;
+
+ if(!dfs_io_dfs_info_ctr("", &r_i->ctr, 1, r_i->level, ps, depth))
+ return False;
+ if(!prs_werror("status", ps, depth, &r_i->status))
+ return False;
+ return True;
+}
+
+/************************************************************
+ Make a DFS_Q_DFS_ENUM structure
+ ************************************************************/
+BOOL init_dfs_q_dfs_enum(DFS_Q_DFS_ENUM *q_d, uint32 level, DFS_INFO_CTR *ctr)
+{
+ q_d->level = level;
+ q_d->maxpreflen = -1;
+ q_d->ptr_buffer = 1;
+ q_d->level2 = level;
+
+ q_d->ptr_num_entries = 1;
+ q_d->num_entries = 0;
+ q_d->num_entries2 = 0;
+ q_d->reshnd.ptr_hnd = 1;
+ q_d->reshnd.handle = 0;
+ return True;
+}
+
+/************************************************************
+ Read or write the DFS_Q_DFS_ENUM structure
+ ************************************************************/
+
+BOOL dfs_io_q_dfs_enum(const char *desc, DFS_Q_DFS_ENUM *q_d, prs_struct *ps, int depth)
+{
+ if(q_d == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "dfs_io_q_dfs_enum");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("level", ps, depth, &q_d->level))
+ return False;
+ if(!prs_uint32("maxpreflen", ps, depth, &q_d->maxpreflen))
+ return False;
+ if(!prs_uint32("ptr_buffer", ps, depth, &q_d->ptr_buffer))
+ return False;
+ if(!prs_uint32("level2", ps, depth, &q_d->level2))
+ return False;
+ if(!prs_uint32("level3", ps, depth, &q_d->level2))
+ return False;
+
+ if(!prs_uint32("ptr_num_entries", ps, depth, &q_d->ptr_num_entries))
+ return False;
+ if(!prs_uint32("num_entries", ps, depth, &q_d->num_entries))
+ return False;
+ if(!prs_uint32("num_entries2", ps, depth, &q_d->num_entries2))
+ return False;
+ if(!smb_io_enum_hnd("resume_hnd",&q_d->reshnd, ps, depth))
+ return False;
+ return True;
+}
+
+/************************************************************
+ Read/write a DFS_INFO_CTR structure
+ ************************************************************/
+
+BOOL dfs_io_dfs_info_ctr(const char *desc, DFS_INFO_CTR* ctr, uint32 num_entries, uint32 level, prs_struct* ps, int depth)
+{
+ int i=0;
+
+ switch(level) {
+ case 1:
+ depth++;
+ /* should depend on whether marshalling or unmarshalling! */
+ if(UNMARSHALLING(ps)) {
+ ctr->dfs.info1 = (DFS_INFO_1 *)prs_alloc_mem(ps, sizeof(DFS_INFO_1)*num_entries);
+ if (!ctr->dfs.info1)
+ return False;
+ }
+
+ for(i=0;i<num_entries;i++) {
+ if(!prs_uint32("ptr_entrypath",ps, depth, &ctr->dfs.info1[i].ptr_entrypath))
+ return False;
+ }
+ for(i=0;i<num_entries;i++) {
+ if(!smb_io_unistr2("", &ctr->dfs.info1[i].entrypath, ctr->dfs.info1[i].ptr_entrypath, ps, depth))
+ return False;
+ if(!prs_align(ps))
+ return False;
+ }
+ depth--;
+ break;
+ case 2:
+ depth++;
+ if(UNMARSHALLING(ps)) {
+ ctr->dfs.info2 = (DFS_INFO_2 *)prs_alloc_mem(ps, num_entries*sizeof(DFS_INFO_2));
+ if (!ctr->dfs.info2)
+ return False;
+ }
+
+ for(i=0;i<num_entries;i++) {
+ if(!prs_uint32("ptr_entrypath", ps, depth, &ctr->dfs.info2[i].ptr_entrypath))
+ return False;
+ if(!prs_uint32("ptr_comment", ps, depth, &ctr->dfs.info2[i].ptr_comment))
+ return False;
+ if(!prs_uint32("state", ps, depth, &ctr->dfs.info2[i].state))
+ return False;
+ if(!prs_uint32("num_storages", ps, depth, &ctr->dfs.info2[i].num_storages))
+ return False;
+ }
+ for(i=0;i<num_entries;i++) {
+ if(!smb_io_unistr2("", &ctr->dfs.info2[i].entrypath, ctr->dfs.info2[i].ptr_entrypath, ps, depth))
+ return False;
+ if(!prs_align(ps))
+ return False;
+ if(!smb_io_unistr2("",&ctr->dfs.info2[i].comment, ctr->dfs.info2[i].ptr_comment, ps, depth))
+ return False;
+ if(!prs_align(ps))
+ return False;
+ }
+ depth--;
+ break;
+ case 3:
+ depth++;
+ if(UNMARSHALLING(ps)) {
+ ctr->dfs.info3 = (DFS_INFO_3 *)prs_alloc_mem(ps, num_entries*sizeof(DFS_INFO_3));
+ if (!ctr->dfs.info3)
+ return False;
+ }
+
+ for(i=0;i<num_entries;i++) {
+ if(!prs_uint32("ptr_entrypath", ps, depth, &ctr->dfs.info3[i].ptr_entrypath))
+ return False;
+ if(!prs_uint32("ptr_comment", ps, depth, &ctr->dfs.info3[i].ptr_comment))
+ return False;
+ if(!prs_uint32("state", ps, depth, &ctr->dfs.info3[i].state))
+ return False;
+ if(!prs_uint32("num_storages", ps, depth, &ctr->dfs.info3[i].num_storages))
+ return False;
+ if(!prs_uint32("ptr_storages", ps, depth, &ctr->dfs.info3[i].ptr_storages))
+ return False;
+ }
+ for(i=0;i<num_entries;i++) {
+ if(!smb_io_unistr2("", &ctr->dfs.info3[i].entrypath, ctr->dfs.info3[i].ptr_entrypath, ps, depth))
+ return False;
+ if(!prs_align(ps))
+ return False;
+ if(!smb_io_unistr2("", &ctr->dfs.info3[i].comment, ctr->dfs.info3[i].ptr_comment, ps, depth))
+ return False;
+ if(!prs_align(ps))
+ return False;
+ if(!prs_uint32("num_storage_infos", ps, depth, &ctr->dfs.info3[i].num_storage_infos))
+ return False;
+
+ if(!dfs_io_dfs_storage_info("storage_info", &ctr->dfs.info3[i], ps, depth))
+ return False;
+ }
+ }
+
+ return True;
+}
+
+/************************************************************
+ Read/write a DFS_R_DFS_ENUM structure
+ ************************************************************/
+
+BOOL dfs_io_r_dfs_enum(const char *desc, DFS_R_DFS_ENUM *q_d, prs_struct *ps, int depth)
+{
+ DFS_INFO_CTR *ctr;
+ if(q_d == NULL)
+ return False;
+ ctr = q_d->ctr;
+ if(ctr == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "dfs_io_r_dfs_enum");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("ptr_buffer", ps, depth, &q_d->ptr_buffer))
+ return False;
+ if(!prs_uint32("level", ps, depth, &q_d->level))
+ return False;
+ if(!prs_uint32("level2", ps, depth, &ctr->switch_value))
+ return False;
+ if(!prs_uint32("ptr_num_entries", ps, depth, &q_d->ptr_num_entries))
+ return False;
+ if(q_d->ptr_num_entries)
+ if(!prs_uint32("num_entries", ps, depth, &q_d->num_entries))
+ return False;
+ if(!prs_uint32("ptr_num_entries2", ps, depth, &q_d->ptr_num_entries2))
+ return False;
+ if(q_d->ptr_num_entries2)
+ if(!prs_uint32("num_entries2", ps, depth, &ctr->num_entries))
+ return False;
+
+ if(!dfs_io_dfs_info_ctr("", ctr, q_d->num_entries, q_d->level, ps, depth))
+ return False;
+
+ if(!smb_io_enum_hnd("resume_hnd", &q_d->reshnd, ps, depth))
+ return False;
+ if(!prs_werror("status", ps, depth, &q_d->status))
+ return False;
+ return True;
+}
+
+BOOL dfs_io_dfs_storage_info(const char *desc, DFS_INFO_3* info3, prs_struct *ps, int depth)
+{
+ int i=0;
+ if(info3 == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "smb_io_dfs_storage_info");
+ depth++;
+
+ if(UNMARSHALLING(ps)) {
+ info3->storages = (DFS_STORAGE_INFO *)prs_alloc_mem(ps, info3->num_storage_infos*sizeof(DFS_STORAGE_INFO));
+ if (!info3->storages)
+ return False;
+ }
+
+ for(i=0;i<info3->num_storage_infos;i++) {
+ if(!prs_uint32("storage_state", ps, depth, &info3->storages[i].state))
+ return False;
+ if(!prs_uint32("ptr_servername", ps, depth, &info3->storages[i].ptr_servername))
+ return False;
+ if(!prs_uint32("ptr_sharename", ps, depth, &info3->storages[i].ptr_sharename))
+ return False;
+ }
+
+ for(i=0;i<info3->num_storage_infos;i++) {
+ if(!smb_io_unistr2("servername", &info3->storages[i].servername, info3->storages[i].ptr_servername, ps, depth))
+ return False;
+ if(!prs_align(ps))
+ return False;
+ if(!smb_io_unistr2("sharename", &info3->storages[i].sharename, info3->storages[i].ptr_sharename, ps, depth))
+ return False;
+ if(!prs_align(ps))
+ return False;
+ }
+
+ return True;
+}
diff --git a/source/rpc_parse/parse_ds.c b/source/rpc_parse/parse_ds.c
new file mode 100644
index 00000000000..8d894b6c6ad
--- /dev/null
+++ b/source/rpc_parse/parse_ds.c
@@ -0,0 +1,302 @@
+/*
+ * Unix SMB/CIFS implementation.
+ * RPC Pipe client / server routines
+
+ * Copyright (C) Gerald Carter 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 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"
+
+/************************************************************************
+************************************************************************/
+
+static BOOL ds_io_dominfobasic( const char *desc, prs_struct *ps, int depth, DSROLE_PRIMARY_DOMAIN_INFO_BASIC **basic)
+{
+ DSROLE_PRIMARY_DOMAIN_INFO_BASIC *p = *basic;
+
+ if ( UNMARSHALLING(ps) )
+ p = *basic = (DSROLE_PRIMARY_DOMAIN_INFO_BASIC *)prs_alloc_mem(ps, sizeof(DSROLE_PRIMARY_DOMAIN_INFO_BASIC));
+
+ if ( !p )
+ return False;
+
+ if ( !prs_uint16("machine_role", ps, depth, &p->machine_role) )
+ return False;
+ if ( !prs_uint16("unknown", ps, depth, &p->unknown) )
+ return False;
+
+ if ( !prs_uint32("flags", ps, depth, &p->flags) )
+ return False;
+
+ if ( !prs_uint32("netbios_ptr", ps, depth, &p->netbios_ptr) )
+ return False;
+ if ( !prs_uint32("dnsname_ptr", ps, depth, &p->dnsname_ptr) )
+ return False;
+ if ( !prs_uint32("forestname_ptr", ps, depth, &p->forestname_ptr) )
+ return False;
+
+ if ( !smb_io_uuid("domain_guid", &p->domain_guid, ps, depth) )
+ return False;
+
+ if ( !smb_io_unistr2( "netbios_domain", &p->netbios_domain, p->netbios_ptr, ps, depth) )
+ return False;
+ if ( !prs_align(ps) )
+ return False;
+
+ if ( !smb_io_unistr2( "dns_domain", &p->dns_domain, p->dnsname_ptr, ps, depth) )
+ return False;
+ if ( !prs_align(ps) )
+ return False;
+
+ if ( !smb_io_unistr2( "forest_domain", &p->forest_domain, p->forestname_ptr, ps, depth) )
+ return False;
+ if ( !prs_align(ps) )
+ return False;
+
+
+ return True;
+
+}
+
+/************************************************************************
+************************************************************************/
+
+BOOL ds_io_q_getprimdominfo( const char *desc, prs_struct *ps, int depth, DS_Q_GETPRIMDOMINFO *q_u)
+{
+ prs_debug(ps, depth, desc, "ds_io_q_getprimdominfo");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if ( !prs_uint16( "level", ps, depth, &q_u->level ) )
+ return False;
+
+ return True;
+}
+
+/************************************************************************
+************************************************************************/
+
+BOOL ds_io_r_getprimdominfo( const char *desc, prs_struct *ps, int depth, DS_R_GETPRIMDOMINFO *r_u)
+{
+ prs_debug(ps, depth, desc, "ds_io_r_getprimdominfo");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if ( !prs_uint32( "ptr", ps, depth, &r_u->ptr ) )
+ return False;
+
+ if ( r_u->ptr )
+ {
+ if ( !prs_uint16( "level", ps, depth, &r_u->level ) )
+ return False;
+
+ if ( !prs_uint16( "unknown0", ps, depth, &r_u->unknown0 ) )
+ return False;
+
+ switch ( r_u->level )
+ {
+ case DsRolePrimaryDomainInfoBasic:
+ if ( !ds_io_dominfobasic( "dominfobasic", ps, depth, &r_u->info.basic ) )
+ return False;
+ break;
+ default:
+ return False;
+ }
+ }
+
+ if ( !prs_align(ps) )
+ return False;
+
+ if ( !prs_ntstatus("status", ps, depth, &r_u->status ) )
+ return False;
+
+ return True;
+}
+
+/************************************************************************
+ initialize a DS_ENUM_DOM_TRUSTS structure
+************************************************************************/
+
+BOOL init_q_ds_enum_domain_trusts( DS_Q_ENUM_DOM_TRUSTS *q, const char *server,
+ uint32 flags )
+{
+ q->flags = flags;
+
+ if ( server && *server )
+ q->server_ptr = 1;
+ else
+ q->server_ptr = 0;
+
+ init_unistr2( &q->server, server, UNI_STR_TERMINATE);
+
+ return True;
+}
+
+/************************************************************************
+************************************************************************/
+
+static BOOL ds_io_domain_trusts( const char *desc, prs_struct *ps, int depth, DS_DOMAIN_TRUSTS *trust)
+{
+ prs_debug(ps, depth, desc, "ds_io_dom_trusts_ctr");
+ depth++;
+
+ if ( !prs_uint32( "netbios_ptr", ps, depth, &trust->netbios_ptr ) )
+ return False;
+
+ if ( !prs_uint32( "dns_ptr", ps, depth, &trust->dns_ptr ) )
+ return False;
+
+ if ( !prs_uint32( "flags", ps, depth, &trust->flags ) )
+ return False;
+
+ if ( !prs_uint32( "parent_index", ps, depth, &trust->parent_index ) )
+ return False;
+
+ if ( !prs_uint32( "trust_type", ps, depth, &trust->trust_type ) )
+ return False;
+
+ if ( !prs_uint32( "trust_attributes", ps, depth, &trust->trust_attributes ) )
+ return False;
+
+ if ( !prs_uint32( "sid_ptr", ps, depth, &trust->sid_ptr ) )
+ return False;
+
+ if ( !smb_io_uuid("guid", &trust->guid, ps, depth) )
+ return False;
+
+ return True;
+}
+
+/************************************************************************
+************************************************************************/
+
+static BOOL ds_io_dom_trusts_ctr( const char *desc, prs_struct *ps, int depth, DS_DOMAIN_TRUSTS_CTR *ctr)
+{
+ int i;
+
+ prs_debug(ps, depth, desc, "ds_io_dom_trusts_ctr");
+ depth++;
+
+ if ( !prs_uint32( "ptr", ps, depth, &ctr->ptr ) )
+ return False;
+
+ if ( !prs_uint32( "max_count", ps, depth, &ctr->max_count ) )
+ return False;
+
+ /* are we done? */
+
+ if ( ctr->max_count == 0 )
+ return True;
+
+ /* allocate the domain trusts array are parse it */
+
+ ctr->trusts = (DS_DOMAIN_TRUSTS*)talloc(ps->mem_ctx, sizeof(DS_DOMAIN_TRUSTS)*ctr->max_count);
+
+ if ( !ctr->trusts )
+ return False;
+
+ /* this stinks; the static portion o fthe structure is read here and then
+ we need another loop to read the UNISTR2's and SID's */
+
+ for ( i=0; i<ctr->max_count;i++ ) {
+ if ( !ds_io_domain_trusts("domain_trusts", ps, depth, &ctr->trusts[i] ) )
+ return False;
+ }
+
+ for ( i=0; i<ctr->max_count; i++ ) {
+
+ if ( !smb_io_unistr2("netbios_domain", &ctr->trusts[i].netbios_domain, ctr->trusts[i].netbios_ptr, ps, depth) )
+ return False;
+
+ if(!prs_align(ps))
+ return False;
+
+ if ( !smb_io_unistr2("dns_domain", &ctr->trusts[i].dns_domain, ctr->trusts[i].dns_ptr, ps, depth) )
+ return False;
+
+ if(!prs_align(ps))
+ return False;
+
+ if ( ctr->trusts[i].sid_ptr ) {
+ if ( !smb_io_dom_sid2("sid", &ctr->trusts[i].sid, ps, depth ) )
+ return False;
+ }
+ }
+
+ return True;
+}
+
+/************************************************************************
+ initialize a DS_ENUM_DOM_TRUSTS request
+************************************************************************/
+
+BOOL ds_io_q_enum_domain_trusts( const char *desc, prs_struct *ps, int depth, DS_Q_ENUM_DOM_TRUSTS *q_u)
+{
+ prs_debug(ps, depth, desc, "ds_io_q_enum_domain_trusts");
+ depth++;
+
+ if ( !prs_align(ps) )
+ return False;
+
+ if ( !prs_uint32( "server_ptr", ps, depth, &q_u->server_ptr ) )
+ return False;
+
+ if ( !smb_io_unistr2("server", &q_u->server, q_u->server_ptr, ps, depth) )
+ return False;
+
+ if ( !prs_align(ps) )
+ return False;
+
+ if ( !prs_uint32( "flags", ps, depth, &q_u->flags ) )
+ return False;
+
+ return True;
+}
+
+/************************************************************************
+************************************************************************/
+
+BOOL ds_io_r_enum_domain_trusts( const char *desc, prs_struct *ps, int depth, DS_R_ENUM_DOM_TRUSTS *r_u)
+{
+ prs_debug(ps, depth, desc, "ds_io_r_enum_domain_trusts");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if ( !prs_uint32( "num_domains", ps, depth, &r_u->num_domains ) )
+ return False;
+
+ if ( r_u->num_domains ) {
+ if ( !ds_io_dom_trusts_ctr("domains", ps, depth, &r_u->domains ) )
+ return False;
+ }
+
+ if(!prs_align(ps))
+ return False;
+
+ if ( !prs_ntstatus("status", ps, depth, &r_u->status ) )
+ return False;
+
+ return True;
+}
+
+
diff --git a/source/rpc_parse/parse_echo.c b/source/rpc_parse/parse_echo.c
new file mode 100644
index 00000000000..4b1ff1f4d54
--- /dev/null
+++ b/source/rpc_parse/parse_echo.c
@@ -0,0 +1,166 @@
+/*
+ * Unix SMB/CIFS implementation.
+ *
+ * RPC Pipe client / server routines
+ *
+ * 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 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 "nterr.h"
+#include "rpc_parse.h"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_RPC_PARSE
+
+void init_echo_q_add_one(ECHO_Q_ADD_ONE *q_d, uint32 request)
+{
+ q_d->request = request;
+}
+
+BOOL echo_io_q_add_one(const char *desc, ECHO_Q_ADD_ONE *q_d,
+ prs_struct *ps, int depth)
+{
+ if (!prs_uint32("request", ps, 0, &q_d->request))
+ return False;
+
+ return True;
+}
+
+BOOL echo_io_r_add_one(const char *desc, ECHO_R_ADD_ONE *q_d,
+ prs_struct *ps, int depth)
+{
+ if (!prs_uint32("response", ps, 0, &q_d->response))
+ return False;
+
+ return True;
+}
+
+
+void init_echo_q_echo_data(ECHO_Q_ECHO_DATA *q_d, uint32 size, char *data)
+{
+ q_d->size = size;
+ q_d->data = data;
+}
+
+BOOL echo_io_q_echo_data(const char *desc, ECHO_Q_ECHO_DATA *q_d,
+ prs_struct *ps, int depth)
+{
+ if (!prs_uint32("size", ps, depth, &q_d->size))
+ return False;
+
+ if (!prs_uint32("size", ps, depth, &q_d->size))
+ return False;
+
+ if (UNMARSHALLING(ps)) {
+ q_d->data = prs_alloc_mem(ps, q_d->size);
+
+ if (!q_d->data)
+ return False;
+ }
+
+ if (!prs_uint8s(False, "data", ps, depth, (unsigned char *)q_d->data, q_d->size))
+ return False;
+
+ return True;
+}
+
+BOOL echo_io_r_echo_data(const char *desc, ECHO_R_ECHO_DATA *q_d,
+ prs_struct *ps, int depth)
+{
+ if (!prs_uint32("size", ps, 0, &q_d->size))
+ return False;
+
+ if (UNMARSHALLING(ps)) {
+ q_d->data = prs_alloc_mem(ps, q_d->size);
+
+ if (!q_d->data)
+ return False;
+ }
+
+ if (!prs_uint8s(False, "data", ps, depth, (unsigned char *)q_d->data, q_d->size))
+ return False;
+
+ return True;
+}
+
+void init_echo_q_sink_data(ECHO_Q_SINK_DATA *q_d, uint32 size, char *data)
+{
+ q_d->size = size;
+ q_d->data = data;
+}
+
+BOOL echo_io_q_sink_data(const char *desc, ECHO_Q_SINK_DATA *q_d,
+ prs_struct *ps, int depth)
+{
+ if (!prs_uint32("size", ps, depth, &q_d->size))
+ return False;
+
+ if (!prs_uint32("size", ps, depth, &q_d->size))
+ return False;
+
+ if (UNMARSHALLING(ps)) {
+ q_d->data = prs_alloc_mem(ps, q_d->size);
+
+ if (!q_d->data)
+ return False;
+ }
+
+ if (!prs_uint8s(False, "data", ps, depth, (unsigned char *)q_d->data, q_d->size))
+ return False;
+
+ return True;
+}
+
+BOOL echo_io_r_sink_data(const char *desc, ECHO_R_SINK_DATA *q_d,
+ prs_struct *ps, int depth)
+{
+ return True;
+}
+
+void init_echo_q_source_data(ECHO_Q_SOURCE_DATA *q_d, uint32 size)
+{
+ q_d->size = size;
+}
+
+BOOL echo_io_q_source_data(const char *desc, ECHO_Q_SOURCE_DATA *q_d,
+ prs_struct *ps, int depth)
+{
+ if (!prs_uint32("size", ps, depth, &q_d->size))
+ return False;
+
+ return True;
+}
+
+BOOL echo_io_r_source_data(const char *desc, ECHO_R_SOURCE_DATA *q_d,
+ prs_struct *ps, int depth)
+{
+ if (!prs_uint32("size", ps, 0, &q_d->size))
+ return False;
+
+ if (UNMARSHALLING(ps)) {
+ q_d->data = prs_alloc_mem(ps, q_d->size);
+
+ if (!q_d->data)
+ return False;
+ }
+
+ if (!prs_uint8s(False, "data", ps, depth, (unsigned char *)q_d->data, q_d->size))
+ return False;
+
+ return True;
+}
diff --git a/source/rpc_parse/parse_epmapper.c b/source/rpc_parse/parse_epmapper.c
new file mode 100644
index 00000000000..bc2cd175034
--- /dev/null
+++ b/source/rpc_parse/parse_epmapper.c
@@ -0,0 +1,482 @@
+/*
+ Unix SMB/CIFS implementation.
+ Samba end point mapper functions
+ 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 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"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_RPC_PARSE
+
+static uint32 internal_referent_id = 0;
+
+
+/*******************************************************************
+ Reads or writes a handle.
+********************************************************************/
+BOOL epm_io_handle(const char *desc, EPM_HANDLE *handle, prs_struct *ps,
+ int depth)
+{
+ if (!prs_align(ps))
+ return False;
+
+ if (!prs_uint8s(False, "data", ps, depth, handle->data,
+ sizeof(handle->data)))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ inits an EPM_FLOOR structure.
+********************************************************************/
+NTSTATUS init_epm_floor(EPM_FLOOR *efloor, uint8 protocol)
+{
+ /* handle lhs */
+ efloor->lhs.protocol = protocol;
+ efloor->lhs.length = sizeof(efloor->lhs.protocol);
+
+ switch(efloor->lhs.protocol) {
+ case EPM_FLOOR_UUID:
+ efloor->lhs.length += sizeof(efloor->lhs.uuid.uuid);
+ efloor->lhs.length += sizeof(efloor->lhs.uuid.version);
+ break;
+ default:
+ break;
+ }
+
+ /* handle rhs */
+ switch(efloor->lhs.protocol) {
+ case EPM_FLOOR_RPC:
+ case EPM_FLOOR_UUID:
+ efloor->rhs.length = sizeof(efloor->rhs.unknown);
+ break;
+ case EPM_FLOOR_TCP:
+ efloor->rhs.length = sizeof(efloor->rhs.tcp.port);
+ break;
+ case EPM_FLOOR_IP:
+ efloor->rhs.length = sizeof(efloor->rhs.ip.addr);
+ break;
+ case EPM_FLOOR_NMPIPES:
+ case EPM_FLOOR_LRPC:
+ case EPM_FLOOR_NETBIOS:
+ efloor->rhs.length = strlen(efloor->rhs.string) + 1;
+ break;
+ default:
+ break;
+ }
+
+ return NT_STATUS_OK;
+}
+
+/*******************************************************************
+ inits an EPM_FLOOR structure with a UUID
+********************************************************************/
+NTSTATUS init_epm_floor_uuid(EPM_FLOOR *efloor,
+ const struct uuid uuid, uint16 version)
+{
+ memcpy(&efloor->lhs.uuid.uuid, &uuid, sizeof(uuid));
+ efloor->lhs.uuid.version = version;
+ efloor->rhs.unknown = 0;
+ return init_epm_floor(efloor, EPM_FLOOR_UUID);
+}
+
+/*******************************************************************
+ inits an EPM_FLOOR structure for RPC
+********************************************************************/
+NTSTATUS init_epm_floor_rpc(EPM_FLOOR *efloor)
+{
+ efloor->rhs.unknown = 0;
+ return init_epm_floor(efloor, EPM_FLOOR_RPC);
+}
+
+/*******************************************************************
+ inits an EPM_FLOOR structure for TCP
+********************************************************************/
+NTSTATUS init_epm_floor_tcp(EPM_FLOOR *efloor, uint16 port)
+{
+ efloor->rhs.tcp.port = htons(port);
+ return init_epm_floor(efloor, EPM_FLOOR_TCP);
+}
+
+/*******************************************************************
+ inits an EPM_FLOOR structure for IP
+********************************************************************/
+NTSTATUS init_epm_floor_ip(EPM_FLOOR *efloor, uint8 addr[4])
+{
+ memcpy(&efloor->rhs.ip.addr, addr, sizeof(addr));
+ return init_epm_floor(efloor, EPM_FLOOR_IP);
+}
+
+/*******************************************************************
+ inits an EPM_FLOOR structure for named pipe
+********************************************************************/
+NTSTATUS init_epm_floor_np(EPM_FLOOR *efloor, const char *pipe_name)
+{
+ safe_strcpy(efloor->rhs.string, pipe_name, sizeof(efloor->rhs.string)-1);
+ return init_epm_floor(efloor, EPM_FLOOR_NMPIPES);
+}
+
+/*******************************************************************
+ inits an EPM_FLOOR structure for named pipe
+********************************************************************/
+NTSTATUS init_epm_floor_lrpc(EPM_FLOOR *efloor, const char *pipe_name)
+{
+ safe_strcpy(efloor->rhs.string, pipe_name, sizeof(efloor->rhs.string)-1);
+ return init_epm_floor(efloor, EPM_FLOOR_LRPC);
+}
+
+/*******************************************************************
+ inits an EPM_FLOOR structure for named pipe
+********************************************************************/
+NTSTATUS init_epm_floor_nb(EPM_FLOOR *efloor, char *host_name)
+{
+ safe_strcpy(efloor->rhs.string, host_name, sizeof(efloor->rhs.string)-1);
+ return init_epm_floor(efloor, EPM_FLOOR_NETBIOS);
+}
+
+/*******************************************************************
+ reads and writes EPM_FLOOR.
+********************************************************************/
+BOOL epm_io_floor(const char *desc, EPM_FLOOR *efloor,
+ prs_struct *ps, int depth)
+{
+ prs_debug(ps, depth, desc, "epm_io_floor");
+ depth++;
+
+ if (!prs_uint16("lhs_length", ps, depth, &efloor->lhs.length))
+ return False;
+ if (!prs_uint8("protocol", ps, depth, &efloor->lhs.protocol))
+ return False;
+
+ switch (efloor->lhs.protocol) {
+ case EPM_FLOOR_UUID:
+ if (!smb_io_uuid("uuid", &efloor->lhs.uuid.uuid, ps, depth))
+ return False;
+ if (!prs_uint16("version", ps, depth,
+ &efloor->lhs.uuid.version))
+ return False;
+ break;
+ }
+
+ if (!prs_uint16("rhs_length", ps, depth, &efloor->rhs.length))
+ return False;
+
+ switch (efloor->lhs.protocol) {
+ case EPM_FLOOR_UUID:
+ case EPM_FLOOR_RPC:
+ if (!prs_uint16("unknown", ps, depth, &efloor->rhs.unknown))
+ return False;
+ break;
+ case EPM_FLOOR_TCP:
+ if (!prs_uint16("tcp_port", ps, depth, &efloor->rhs.tcp.port))
+ return False;
+ break;
+ case EPM_FLOOR_IP:
+ if (!prs_uint8s(False, "ip_addr", ps, depth,
+ efloor->rhs.ip.addr,
+ sizeof(efloor->rhs.ip.addr)))
+ return False;
+ break;
+ case EPM_FLOOR_NMPIPES:
+ case EPM_FLOOR_LRPC:
+ case EPM_FLOOR_NETBIOS:
+ if (!prs_uint8s(False, "string", ps, depth,
+ efloor->rhs.string,
+ efloor->rhs.length))
+ return False;
+ break;
+ default:
+ break;
+ }
+
+ return True;
+}
+
+/*******************************************************************
+ Inits a EPM_TOWER structure.
+********************************************************************/
+NTSTATUS init_epm_tower(TALLOC_CTX *ctx, EPM_TOWER *tower,
+ const EPM_FLOOR *floors, int num_floors)
+{
+ int size = 0;
+ int i;
+
+ DEBUG(5, ("init_epm_tower\n"));
+
+ size += sizeof(uint16); /* number of floors is in tower length */
+ for (i = 0; i < num_floors; i++) {
+ size += (sizeof(uint16) * 2);
+ size += floors[i].lhs.length;
+ size += floors[i].rhs.length;
+ }
+
+ tower->max_length = tower->length = size;
+ tower->num_floors = num_floors;
+ tower->floors = talloc(ctx, sizeof(EPM_FLOOR) * num_floors);
+ if (!tower->floors) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ memcpy(tower->floors, floors, sizeof(EPM_FLOOR) * num_floors);
+ tower->unknown = 0x7e;
+
+ return NT_STATUS_OK;
+}
+
+/*******************************************************************
+ Reads or writes an EPM_TOWER structure.
+********************************************************************/
+BOOL epm_io_tower(const char *desc, EPM_TOWER *tower,
+ prs_struct *ps, int depth)
+{
+ int i;
+
+ prs_debug(ps, depth, desc, "epm_io_tower");
+ depth++;
+
+ if (!prs_align(ps))
+ return False;
+
+ if (!prs_uint32("max_length", ps, depth, &tower->max_length))
+ return False;
+ if (!prs_uint32("length", ps, depth, &tower->length))
+ return False;
+ if (!prs_uint16("num_floors", ps, depth, &tower->num_floors))
+ return False;
+
+ if (UNMARSHALLING(ps)) {
+ tower->floors = talloc(ps->mem_ctx,
+ sizeof(EPM_FLOOR) * tower->num_floors);
+ if (!tower->floors)
+ return False;
+ }
+
+ for (i = 0; i < tower->num_floors; i++) {
+ if (!epm_io_floor("floor", tower->floors + i, ps, depth))
+ return False;
+ }
+
+ return True;
+}
+
+/*******************************************************************
+ Initialize an EPM_TOWER_ARRAY structure
+********************************************************************/
+NTSTATUS init_epm_tower_array(TALLOC_CTX *ctx, EPM_TOWER_ARRAY *array,
+ const EPM_TOWER *towers, int num_towers)
+{
+ int i;
+
+ array->max_count = num_towers;
+ array->offset = 0;
+ array->count = num_towers;
+ array->tower_ref_ids = talloc(ctx, sizeof(uint32) * num_towers);
+ if (!array->tower_ref_ids) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ for (i=0;i<num_towers;i++)
+ array->tower_ref_ids[i] = ++internal_referent_id;
+
+ array->towers = talloc(ctx, sizeof(EPM_TOWER) * num_towers);
+ if (!array->towers) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ memcpy(array->towers, towers, sizeof(EPM_TOWER) * num_towers);
+
+ return NT_STATUS_OK;
+}
+
+/*******************************************************************
+ Reads or writes an EPM_TOWER_ARRAY structure.
+********************************************************************/
+BOOL epm_io_tower_array(const char *desc, EPM_TOWER_ARRAY *array,
+ prs_struct *ps, int depth)
+{
+ int i;
+
+ prs_debug(ps, depth, desc, "epm_io_tower_array");
+ depth++;
+
+ if (!prs_uint32("max_count", ps, depth, &array->max_count))
+ return False;
+ if (!prs_uint32("offset", ps, depth, &array->offset))
+ return False;
+ if (!prs_uint32("count", ps, depth, &array->count))
+ return False;
+
+
+ if (UNMARSHALLING(ps)) {
+ array->tower_ref_ids = talloc(ps->mem_ctx,
+ sizeof(uint32) * array->count);
+ if (!array->tower_ref_ids) {
+ return False;
+ }
+ }
+ for (i=0; i < array->count; i++) {
+ if (!prs_uint32("ref_id", ps, depth, &array->tower_ref_ids[i])) {
+ return False;
+ } else {
+ if (array->tower_ref_ids[i] > internal_referent_id) {
+ internal_referent_id = array->tower_ref_ids[i];
+ }
+ }
+ }
+
+
+
+ if (!prs_set_offset(ps, prs_offset(ps) + array->offset))
+ return False;
+
+ if (UNMARSHALLING(ps)) {
+ array->towers = talloc(ps->mem_ctx,
+ sizeof(EPM_TOWER) * array->count);
+ if (!array->towers) {
+ return False;
+ }
+ }
+
+ for (i = 0; i < array->count; i++) {
+ if (!epm_io_tower("tower", &array->towers[i], ps, depth))
+ return False;
+ }
+
+ return True;
+}
+
+/*******************************************************************
+ Initialize EPM_R_MAP structure
+******************************************************************/
+NTSTATUS init_epm_r_map(TALLOC_CTX *ctx, EPM_R_MAP *r_map,
+ const EPM_HANDLE *handle, const EPM_TOWER_ARRAY *array,
+ int num_elements, uint32 status)
+{
+ memcpy(&r_map->handle, handle, sizeof(*handle));
+ r_map->num_results = num_elements;
+ r_map->results = talloc(ctx, sizeof(EPM_TOWER_ARRAY) * num_elements);
+ if (!r_map->results) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ memcpy(r_map->results, array, sizeof(EPM_TOWER_ARRAY) * num_elements);
+ r_map->status = status;
+ return NT_STATUS_OK;
+}
+
+/*************************************************************************
+ Inits a EPM_Q_MAP structure.
+**************************************************************************
+* We attempt to hide the ugliness of the wire format by taking a EPM_TOWER
+* array with a defined size
+**************************************************************************/
+NTSTATUS init_epm_q_map(TALLOC_CTX *ctx, EPM_Q_MAP *q_map,
+ const EPM_TOWER *towers, int num_towers)
+{
+ static uint32 handle = 1;
+
+ ZERO_STRUCTP(q_map);
+
+ DEBUG(5, ("init_epm_q_map\n"));
+ q_map->handle.data[0] = (handle >> 0) & 0xFF;
+ q_map->handle.data[1] = (handle >> 8) & 0xFF;
+ q_map->handle.data[2] = (handle >> 16) & 0xFF;
+ q_map->handle.data[3] = (handle >> 24) & 0xFF;
+
+ q_map->tower = talloc(ctx, sizeof(EPM_TOWER) * (num_towers + 1));
+ if (!q_map->tower) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ memcpy(q_map->tower, towers, sizeof(EPM_TOWER) * num_towers);
+
+ ZERO_STRUCT(q_map->tower[num_towers]);
+
+ /* For now let's not take more than 4 towers per result */
+ q_map->max_towers = num_towers * 4;
+
+ q_map->tower_ref_id = ++internal_referent_id;
+
+ handle++;
+
+ return NT_STATUS_OK;
+}
+
+/*****************************************************************
+ epm_io_q_map - read or write EPM_Q_MAP structure
+******************************************************************/
+BOOL epm_io_q_map(const char *desc, EPM_Q_MAP *io_map, prs_struct *ps,
+ int depth)
+{
+ prs_debug(ps, depth, desc, "epm_io_q_map");
+ depth++;
+
+ if (!epm_io_handle("handle", &io_map->handle, ps, depth))
+ return False;
+
+ if (!prs_uint32("referent_id", ps, 0, &io_map->tower_ref_id))
+ return False;
+ if (io_map->tower_ref_id > internal_referent_id)
+ internal_referent_id = io_map->tower_ref_id;
+
+ /* HACK: We need a more elegant way of doing this */
+ if (UNMARSHALLING(ps)) {
+ io_map->tower = talloc(ps->mem_ctx, sizeof(EPM_TOWER));
+ if (!io_map->tower)
+ return False;
+ }
+ if (!epm_io_tower("tower", io_map->tower, ps, depth))
+ return False;
+ if (!epm_io_handle("term_handle", &io_map->term_handle, ps, depth))
+ return False;
+
+ if (!prs_uint32("max_towers", ps, 0, &io_map->max_towers))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ epm_io_r_map - Read/Write EPM_R_MAP structure
+******************************************************************/
+BOOL epm_io_r_map(const char *desc, EPM_R_MAP *io_map,
+ prs_struct *ps, int depth)
+{
+ prs_debug(ps, depth, desc, "epm_io_r_map");
+ depth++;
+
+ if (!epm_io_handle("handle", &io_map->handle, ps, depth))
+ return False;
+ if (!prs_uint32("num_results", ps, depth, &io_map->num_results))
+ return False;
+
+ if (UNMARSHALLING(ps)) {
+ io_map->results = talloc(ps->mem_ctx,
+ sizeof(EPM_TOWER_ARRAY) *
+ io_map->num_results);
+ if (!io_map->results)
+ return False;
+ }
+ if (!epm_io_tower_array("results", io_map->results, ps, depth))
+ return False;
+
+ if (!prs_align(ps))
+ return False;
+
+ if (!prs_uint32("status", ps, depth, &io_map->status))
+ return False;
+
+ return True;
+}
diff --git a/source/rpc_parse/parse_lsa.c b/source/rpc_parse/parse_lsa.c
new file mode 100644
index 00000000000..50fd3beb48e
--- /dev/null
+++ b/source/rpc_parse/parse_lsa.c
@@ -0,0 +1,2474 @@
+/*
+ * Unix SMB/CIFS implementation.
+ * RPC Pipe client / server routines
+ * Copyright (C) Andrew Tridgell 1992-1997,
+ * Copyright (C) Luke Kenneth Casson Leighton 1996-1997,
+ * Copyright (C) Paul Ashton 1997,
+ * Copyright (C) Andrew Bartlett 2002,
+ * Copyright (C) Jim McDonough <jmcd@us.ibm.com> 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 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"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_RPC_PARSE
+
+static BOOL lsa_io_trans_names(const char *desc, LSA_TRANS_NAME_ENUM *trn, prs_struct *ps, int depth);
+
+/*******************************************************************
+ Inits a LSA_TRANS_NAME structure.
+********************************************************************/
+
+void init_lsa_trans_name(LSA_TRANS_NAME *trn, UNISTR2 *uni_name,
+ uint16 sid_name_use, const char *name, uint32 idx)
+{
+ trn->sid_name_use = sid_name_use;
+ init_unistr2(uni_name, name, UNI_FLAGS_NONE);
+ init_uni_hdr(&trn->hdr_name, uni_name);
+ trn->domain_idx = idx;
+}
+
+/*******************************************************************
+ Reads or writes a LSA_TRANS_NAME structure.
+********************************************************************/
+
+static BOOL lsa_io_trans_name(const char *desc, LSA_TRANS_NAME *trn, prs_struct *ps,
+ int depth)
+{
+ prs_debug(ps, depth, desc, "lsa_io_trans_name");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint16("sid_name_use", ps, depth, &trn->sid_name_use))
+ return False;
+ if(!prs_align(ps))
+ return False;
+
+ if(!smb_io_unihdr ("hdr_name", &trn->hdr_name, ps, depth))
+ return False;
+ if(!prs_uint32("domain_idx ", ps, depth, &trn->domain_idx))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ Reads or writes a DOM_R_REF structure.
+********************************************************************/
+
+static BOOL lsa_io_dom_r_ref(const char *desc, DOM_R_REF *r_r, prs_struct *ps,
+ int depth)
+{
+ unsigned int i;
+
+ prs_debug(ps, depth, desc, "lsa_io_dom_r_ref");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("num_ref_doms_1", ps, depth, &r_r->num_ref_doms_1)) /* num referenced domains? */
+ return False;
+ if(!prs_uint32("ptr_ref_dom ", ps, depth, &r_r->ptr_ref_dom)) /* undocumented buffer pointer. */
+ return False;
+ if(!prs_uint32("max_entries ", ps, depth, &r_r->max_entries)) /* 32 - max number of entries */
+ return False;
+
+ SMB_ASSERT_ARRAY(r_r->hdr_ref_dom, r_r->num_ref_doms_1);
+
+ if (r_r->ptr_ref_dom != 0) {
+
+ if(!prs_uint32("num_ref_doms_2", ps, depth, &r_r->num_ref_doms_2)) /* 4 - num referenced domains? */
+ return False;
+
+ SMB_ASSERT_ARRAY(r_r->ref_dom, r_r->num_ref_doms_2);
+
+ for (i = 0; i < r_r->num_ref_doms_1; i++) {
+ fstring t;
+
+ slprintf(t, sizeof(t) - 1, "dom_ref[%d] ", i);
+ if(!smb_io_unihdr(t, &r_r->hdr_ref_dom[i].hdr_dom_name, ps, depth))
+ return False;
+
+ slprintf(t, sizeof(t) - 1, "sid_ptr[%d] ", i);
+ if(!prs_uint32(t, ps, depth, &r_r->hdr_ref_dom[i].ptr_dom_sid))
+ return False;
+ }
+
+ for (i = 0; i < r_r->num_ref_doms_2; i++) {
+ fstring t;
+
+ if (r_r->hdr_ref_dom[i].hdr_dom_name.buffer != 0) {
+ slprintf(t, sizeof(t) - 1, "dom_ref[%d] ", i);
+ if(!smb_io_unistr2(t, &r_r->ref_dom[i].uni_dom_name, True, ps, depth)) /* domain name unicode string */
+ return False;
+ if(!prs_align(ps))
+ return False;
+ }
+
+ if (r_r->hdr_ref_dom[i].ptr_dom_sid != 0) {
+ slprintf(t, sizeof(t) - 1, "sid_ptr[%d] ", i);
+ if(!smb_io_dom_sid2(t, &r_r->ref_dom[i].ref_dom, ps, depth)) /* referenced domain SIDs */
+ return False;
+ }
+ }
+ }
+
+ return True;
+}
+
+/*******************************************************************
+ Inits an LSA_SEC_QOS structure.
+********************************************************************/
+
+void init_lsa_sec_qos(LSA_SEC_QOS *qos, uint16 imp_lev, uint8 ctxt, uint8 eff)
+{
+ DEBUG(5, ("init_lsa_sec_qos\n"));
+
+ qos->len = 0x0c; /* length of quality of service block, in bytes */
+ qos->sec_imp_level = imp_lev;
+ qos->sec_ctxt_mode = ctxt;
+ qos->effective_only = eff;
+}
+
+/*******************************************************************
+ Reads or writes an LSA_SEC_QOS structure.
+********************************************************************/
+
+static BOOL lsa_io_sec_qos(const char *desc, LSA_SEC_QOS *qos, prs_struct *ps,
+ int depth)
+{
+ uint32 start;
+
+ prs_debug(ps, depth, desc, "lsa_io_obj_qos");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ start = prs_offset(ps);
+
+ /* these pointers had _better_ be zero, because we don't know
+ what they point to!
+ */
+ if(!prs_uint32("len ", ps, depth, &qos->len)) /* 0x18 - length (in bytes) inc. the length field. */
+ return False;
+ if(!prs_uint16("sec_imp_level ", ps, depth, &qos->sec_imp_level ))
+ return False;
+ if(!prs_uint8 ("sec_ctxt_mode ", ps, depth, &qos->sec_ctxt_mode ))
+ return False;
+ if(!prs_uint8 ("effective_only", ps, depth, &qos->effective_only))
+ return False;
+
+ if (qos->len != prs_offset(ps) - start) {
+ DEBUG(3,("lsa_io_sec_qos: length %x does not match size %x\n",
+ qos->len, prs_offset(ps) - start));
+ }
+
+ return True;
+}
+
+/*******************************************************************
+ Inits an LSA_OBJ_ATTR structure.
+********************************************************************/
+
+static void init_lsa_obj_attr(LSA_OBJ_ATTR *attr, uint32 attributes, LSA_SEC_QOS *qos)
+{
+ DEBUG(5, ("init_lsa_obj_attr\n"));
+
+ attr->len = 0x18; /* length of object attribute block, in bytes */
+ attr->ptr_root_dir = 0;
+ attr->ptr_obj_name = 0;
+ attr->attributes = attributes;
+ attr->ptr_sec_desc = 0;
+
+ if (qos != NULL) {
+ attr->ptr_sec_qos = 1;
+ attr->sec_qos = qos;
+ } else {
+ attr->ptr_sec_qos = 0;
+ attr->sec_qos = NULL;
+ }
+}
+
+/*******************************************************************
+ Reads or writes an LSA_OBJ_ATTR structure.
+********************************************************************/
+
+static BOOL lsa_io_obj_attr(const char *desc, LSA_OBJ_ATTR *attr, prs_struct *ps,
+ int depth)
+{
+ uint32 start;
+
+ prs_debug(ps, depth, desc, "lsa_io_obj_attr");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ start = prs_offset(ps);
+
+ /* these pointers had _better_ be zero, because we don't know
+ what they point to!
+ */
+ if(!prs_uint32("len ", ps, depth, &attr->len)) /* 0x18 - length (in bytes) inc. the length field. */
+ return False;
+ if(!prs_uint32("ptr_root_dir", ps, depth, &attr->ptr_root_dir)) /* 0 - root directory (pointer) */
+ return False;
+ if(!prs_uint32("ptr_obj_name", ps, depth, &attr->ptr_obj_name)) /* 0 - object name (pointer) */
+ return False;
+ if(!prs_uint32("attributes ", ps, depth, &attr->attributes)) /* 0 - attributes (undocumented) */
+ return False;
+ if(!prs_uint32("ptr_sec_desc", ps, depth, &attr->ptr_sec_desc)) /* 0 - security descriptior (pointer) */
+ return False;
+ if(!prs_uint32("ptr_sec_qos ", ps, depth, &attr->ptr_sec_qos )) /* security quality of service (pointer) */
+ return False;
+
+ /* code commented out as it's not necessary true (tested with hyena). JFM, 11/22/2001 */
+#if 0
+ if (attr->len != prs_offset(ps) - start) {
+ DEBUG(3,("lsa_io_obj_attr: length %x does not match size %x\n",
+ attr->len, prs_offset(ps) - start));
+ return False;
+ }
+#endif
+
+ if (attr->ptr_sec_qos != 0) {
+ if (UNMARSHALLING(ps))
+ if (!(attr->sec_qos = (LSA_SEC_QOS *)prs_alloc_mem(ps,sizeof(LSA_SEC_QOS))))
+ return False;
+
+ if(!lsa_io_sec_qos("sec_qos", attr->sec_qos, ps, depth))
+ return False;
+ }
+
+ return True;
+}
+
+
+/*******************************************************************
+ Inits an LSA_Q_OPEN_POL structure.
+********************************************************************/
+
+void init_q_open_pol(LSA_Q_OPEN_POL *r_q, uint16 system_name,
+ uint32 attributes, uint32 desired_access,
+ LSA_SEC_QOS *qos)
+{
+ DEBUG(5, ("init_open_pol: attr:%d da:%d\n", attributes,
+ desired_access));
+
+ r_q->ptr = 1; /* undocumented pointer */
+
+ r_q->des_access = desired_access;
+
+ r_q->system_name = system_name;
+ init_lsa_obj_attr(&r_q->attr, attributes, qos);
+}
+
+/*******************************************************************
+ Reads or writes an LSA_Q_OPEN_POL structure.
+********************************************************************/
+
+BOOL lsa_io_q_open_pol(const char *desc, LSA_Q_OPEN_POL *r_q, prs_struct *ps,
+ int depth)
+{
+ prs_debug(ps, depth, desc, "lsa_io_q_open_pol");
+ depth++;
+
+ if(!prs_uint32("ptr ", ps, depth, &r_q->ptr))
+ return False;
+ if(!prs_uint16("system_name", ps, depth, &r_q->system_name))
+ return False;
+ if(!prs_align( ps ))
+ return False;
+
+ if(!lsa_io_obj_attr("", &r_q->attr, ps, depth))
+ return False;
+
+ if(!prs_uint32("des_access", ps, depth, &r_q->des_access))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ Reads or writes an LSA_R_OPEN_POL structure.
+********************************************************************/
+
+BOOL lsa_io_r_open_pol(const char *desc, LSA_R_OPEN_POL *r_p, prs_struct *ps,
+ int depth)
+{
+ prs_debug(ps, depth, desc, "lsa_io_r_open_pol");
+ depth++;
+
+ if(!smb_io_pol_hnd("", &r_p->pol, ps, depth))
+ return False;
+
+ if(!prs_ntstatus("status", ps, depth, &r_p->status))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ Inits an LSA_Q_OPEN_POL2 structure.
+********************************************************************/
+
+void init_q_open_pol2(LSA_Q_OPEN_POL2 *r_q, const char *server_name,
+ uint32 attributes, uint32 desired_access,
+ LSA_SEC_QOS *qos)
+{
+ DEBUG(5, ("init_q_open_pol2: attr:%d da:%d\n", attributes,
+ desired_access));
+
+ r_q->ptr = 1; /* undocumented pointer */
+
+ r_q->des_access = desired_access;
+
+ init_unistr2(&r_q->uni_server_name, server_name, UNI_STR_TERMINATE);
+
+ init_lsa_obj_attr(&r_q->attr, attributes, qos);
+}
+
+/*******************************************************************
+ Reads or writes an LSA_Q_OPEN_POL2 structure.
+********************************************************************/
+
+BOOL lsa_io_q_open_pol2(const char *desc, LSA_Q_OPEN_POL2 *r_q, prs_struct *ps,
+ int depth)
+{
+ prs_debug(ps, depth, desc, "lsa_io_q_open_pol2");
+ depth++;
+
+ if(!prs_uint32("ptr ", ps, depth, &r_q->ptr))
+ return False;
+
+ if(!smb_io_unistr2 ("", &r_q->uni_server_name, r_q->ptr, ps, depth))
+ return False;
+ if(!lsa_io_obj_attr("", &r_q->attr, ps, depth))
+ return False;
+
+ if(!prs_uint32("des_access", ps, depth, &r_q->des_access))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ Reads or writes an LSA_R_OPEN_POL2 structure.
+********************************************************************/
+
+BOOL lsa_io_r_open_pol2(const char *desc, LSA_R_OPEN_POL2 *r_p, prs_struct *ps,
+ int depth)
+{
+ prs_debug(ps, depth, desc, "lsa_io_r_open_pol2");
+ depth++;
+
+ if(!smb_io_pol_hnd("", &r_p->pol, ps, depth))
+ return False;
+
+ if(!prs_ntstatus("status", ps, depth, &r_p->status))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+makes an LSA_Q_QUERY_SEC_OBJ structure.
+********************************************************************/
+
+void init_q_query_sec_obj(LSA_Q_QUERY_SEC_OBJ *q_q, const POLICY_HND *hnd,
+ uint32 sec_info)
+{
+ DEBUG(5, ("init_q_query_sec_obj\n"));
+
+ q_q->pol = *hnd;
+ q_q->sec_info = sec_info;
+
+ return;
+}
+
+/*******************************************************************
+ Reads or writes an LSA_Q_QUERY_SEC_OBJ structure.
+********************************************************************/
+
+BOOL lsa_io_q_query_sec_obj(const char *desc, LSA_Q_QUERY_SEC_OBJ *q_q,
+ prs_struct *ps, int depth)
+{
+ prs_debug(ps, depth, desc, "lsa_io_q_query_sec_obj");
+ depth++;
+
+ if (!smb_io_pol_hnd("", &q_q->pol, ps, depth))
+ return False;
+
+ if (!prs_uint32("sec_info", ps, depth, &q_q->sec_info))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ Reads or writes a LSA_R_QUERY_SEC_OBJ structure.
+********************************************************************/
+
+BOOL lsa_io_r_query_sec_obj(const char *desc, LSA_R_QUERY_SEC_OBJ *r_u,
+ prs_struct *ps, int depth)
+{
+ prs_debug(ps, depth, desc, "lsa_io_r_query_sec_obj");
+ depth++;
+
+ if (!prs_align(ps))
+ return False;
+
+ if (!prs_uint32("ptr", ps, depth, &r_u->ptr))
+ return False;
+
+ if (r_u->ptr != 0) {
+ if (!sec_io_desc_buf("sec", &r_u->buf, ps, depth))
+ return False;
+ }
+
+ if (!prs_ntstatus("status", ps, depth, &r_u->status))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ Inits an LSA_Q_QUERY_INFO structure.
+********************************************************************/
+
+void init_q_query(LSA_Q_QUERY_INFO *q_q, POLICY_HND *hnd, uint16 info_class)
+{
+ DEBUG(5, ("init_q_query\n"));
+
+ memcpy(&q_q->pol, hnd, sizeof(q_q->pol));
+
+ q_q->info_class = info_class;
+}
+
+/*******************************************************************
+ Reads or writes an LSA_Q_QUERY_INFO structure.
+********************************************************************/
+
+BOOL lsa_io_q_query(const char *desc, LSA_Q_QUERY_INFO *q_q, prs_struct *ps,
+ int depth)
+{
+ prs_debug(ps, depth, desc, "lsa_io_q_query");
+ depth++;
+
+ if(!smb_io_pol_hnd("", &q_q->pol, ps, depth))
+ return False;
+
+ if(!prs_uint16("info_class", ps, depth, &q_q->info_class))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+makes an LSA_Q_ENUM_TRUST_DOM structure.
+********************************************************************/
+BOOL init_q_enum_trust_dom(LSA_Q_ENUM_TRUST_DOM * q_e, POLICY_HND *pol,
+ uint32 enum_context, uint32 preferred_len)
+{
+ DEBUG(5, ("init_q_enum_trust_dom\n"));
+
+ q_e->pol = *pol;
+ q_e->enum_context = enum_context;
+ q_e->preferred_len = preferred_len;
+
+ return True;
+}
+
+/*******************************************************************
+ Reads or writes an LSA_Q_ENUM_TRUST_DOM structure.
+********************************************************************/
+
+BOOL lsa_io_q_enum_trust_dom(const char *desc, LSA_Q_ENUM_TRUST_DOM *q_e,
+ prs_struct *ps, int depth)
+{
+ prs_debug(ps, depth, desc, "lsa_io_q_enum_trust_dom");
+ depth++;
+
+ if(!smb_io_pol_hnd("", &q_e->pol, ps, depth))
+ return False;
+
+ if(!prs_uint32("enum_context ", ps, depth, &q_e->enum_context))
+ return False;
+ if(!prs_uint32("preferred_len", ps, depth, &q_e->preferred_len))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ Inits an LSA_R_ENUM_TRUST_DOM structure.
+********************************************************************/
+
+void init_r_enum_trust_dom(TALLOC_CTX *ctx, LSA_R_ENUM_TRUST_DOM *r_e, uint32 enum_context,
+ uint32 req_num_domains, uint32 num_domains, TRUSTDOM **td)
+{
+ unsigned int i;
+
+ DEBUG(5, ("init_r_enum_trust_dom\n"));
+
+ r_e->enum_context = enum_context;
+ r_e->num_domains = num_domains;
+ r_e->ptr_enum_domains = 0;
+ r_e->num_domains2 = num_domains;
+
+ if (num_domains != 0) {
+
+ /*
+ * allocating empty arrays of unicode headers, strings
+ * and sids of enumerated trusted domains
+ */
+ if (!(r_e->hdr_domain_name = (UNIHDR2 *)talloc(ctx,sizeof(UNIHDR2) * num_domains))) {
+ r_e->status = NT_STATUS_NO_MEMORY;
+ return;
+ }
+
+ if (!(r_e->uni_domain_name = (UNISTR2 *)talloc(ctx,sizeof(UNISTR2) * num_domains))) {
+ r_e->status = NT_STATUS_NO_MEMORY;
+ return;
+ }
+
+ if (!(r_e->domain_sid = (DOM_SID2 *)talloc(ctx,sizeof(DOM_SID2) * num_domains))) {
+ r_e->status = NT_STATUS_NO_MEMORY;
+ return;
+ }
+
+ for (i = 0; i < num_domains; i++) {
+
+ /* don't know what actually is this for */
+ r_e->ptr_enum_domains = 1;
+
+ init_dom_sid2(&r_e->domain_sid[i], &(td[i])->sid);
+
+ init_unistr2_w(ctx, &r_e->uni_domain_name[i], (td[i])->name);
+ init_uni_hdr2(&r_e->hdr_domain_name[i], &r_e->uni_domain_name[i]);
+
+ };
+ }
+
+}
+
+/*******************************************************************
+ Reads or writes an LSA_R_ENUM_TRUST_DOM structure.
+********************************************************************/
+
+BOOL lsa_io_r_enum_trust_dom(const char *desc, LSA_R_ENUM_TRUST_DOM *r_e,
+ prs_struct *ps, int depth)
+{
+ prs_debug(ps, depth, desc, "lsa_io_r_enum_trust_dom");
+ depth++;
+
+ if(!prs_uint32("enum_context ", ps, depth, &r_e->enum_context))
+ return False;
+ if(!prs_uint32("num_domains ", ps, depth, &r_e->num_domains))
+ return False;
+ if(!prs_uint32("ptr_enum_domains", ps, depth, &r_e->ptr_enum_domains))
+ return False;
+
+ if (r_e->ptr_enum_domains) {
+ int i, num_domains;
+
+ if(!prs_uint32("num_domains2", ps, depth, &r_e->num_domains2))
+ return False;
+
+ num_domains = r_e->num_domains2;
+
+ if (UNMARSHALLING(ps)) {
+ if (!(r_e->hdr_domain_name = (UNIHDR2 *)prs_alloc_mem(ps,sizeof(UNIHDR2) * num_domains)))
+ return False;
+
+ if (!(r_e->uni_domain_name = (UNISTR2 *)prs_alloc_mem(ps,sizeof(UNISTR2) * num_domains)))
+ return False;
+
+ if (!(r_e->domain_sid = (DOM_SID2 *)prs_alloc_mem(ps,sizeof(DOM_SID2) * num_domains)))
+ return False;
+ }
+
+ for (i = 0; i < num_domains; i++) {
+ if(!smb_io_unihdr2 ("", &r_e->hdr_domain_name[i], ps,
+ depth))
+ return False;
+ }
+
+ for (i = 0; i < num_domains; i++) {
+ if(!smb_io_unistr2 ("", &r_e->uni_domain_name[i],
+ r_e->hdr_domain_name[i].buffer,
+ ps, depth))
+ return False;
+ if(!smb_io_dom_sid2("", &r_e->domain_sid[i], ps,
+ depth))
+ return False;
+ }
+ }
+
+ if(!prs_ntstatus("status", ps, depth, &r_e->status))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+reads or writes a dom query structure.
+********************************************************************/
+
+static BOOL lsa_io_dom_query(const char *desc, DOM_QUERY *d_q, prs_struct *ps, int depth)
+{
+ if (d_q == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "lsa_io_dom_query");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint16("uni_dom_max_len", ps, depth, &d_q->uni_dom_max_len)) /* domain name string length * 2 */
+ return False;
+ if(!prs_uint16("uni_dom_str_len", ps, depth, &d_q->uni_dom_str_len)) /* domain name string length * 2 */
+ return False;
+
+ if(!prs_uint32("buffer_dom_name", ps, depth, &d_q->buffer_dom_name)) /* undocumented domain name string buffer pointer */
+ return False;
+ if(!prs_uint32("buffer_dom_sid ", ps, depth, &d_q->buffer_dom_sid)) /* undocumented domain SID string buffer pointer */
+ return False;
+
+ if(!smb_io_unistr2("unistr2", &d_q->uni_domain_name, d_q->buffer_dom_name, ps, depth)) /* domain name (unicode string) */
+ return False;
+
+ if(!prs_align(ps))
+ return False;
+
+ if (d_q->buffer_dom_sid != 0) {
+ if(!smb_io_dom_sid2("", &d_q->dom_sid, ps, depth)) /* domain SID */
+ return False;
+ } else {
+ memset((char *)&d_q->dom_sid, '\0', sizeof(d_q->dom_sid));
+ }
+
+ return True;
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+static BOOL lsa_io_dom_query_2(const char *desc, DOM_QUERY_2 *d_q, prs_struct *ps, int depth)
+{
+ uint32 ptr = 1;
+
+ if (d_q == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "lsa_io_dom_query_2");
+ depth++;
+
+ if (!prs_align(ps))
+ return False;
+
+ if (!prs_uint32("auditing_enabled", ps, depth, &d_q->auditing_enabled))
+ return False;
+ if (!prs_uint32("ptr ", ps, depth, &ptr))
+ return False;
+ if (!prs_uint32("count1", ps, depth, &d_q->count1))
+ return False;
+ if (!prs_uint32("count2", ps, depth, &d_q->count2))
+ return False;
+
+ if (UNMARSHALLING(ps)) {
+ d_q->auditsettings = (uint32 *)talloc_zero(ps->mem_ctx, d_q->count2 * sizeof(uint32));
+ }
+
+ if (d_q->auditsettings == NULL) {
+ DEBUG(1, ("lsa_io_dom_query_2: NULL auditsettings!\n"));
+ return False;
+ }
+
+ if (!prs_uint32s(False, "auditsettings", ps, depth, d_q->auditsettings, d_q->count2))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ Reads or writes a dom query structure.
+********************************************************************/
+
+static BOOL lsa_io_dom_query_3(const char *desc, DOM_QUERY_3 *d_q, prs_struct *ps, int depth)
+{
+ return lsa_io_dom_query("", d_q, ps, depth);
+}
+
+/*******************************************************************
+ Reads or writes a dom query structure.
+********************************************************************/
+
+static BOOL lsa_io_dom_query_5(const char *desc, DOM_QUERY_5 *d_q, prs_struct *ps, int depth)
+{
+ return lsa_io_dom_query("", d_q, ps, depth);
+}
+
+/*******************************************************************
+ Reads or writes a dom query structure.
+********************************************************************/
+
+static BOOL lsa_io_dom_query_6(const char *desc, DOM_QUERY_6 *d_q, prs_struct *ps, int depth)
+{
+ if (d_q == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "lsa_io_dom_query_6");
+ depth++;
+
+ if (!prs_uint16("server_role", ps, depth, &d_q->server_role))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ Reads or writes an LSA_R_QUERY_INFO structure.
+********************************************************************/
+
+BOOL lsa_io_r_query(const char *desc, LSA_R_QUERY_INFO *r_q, prs_struct *ps,
+ int depth)
+{
+ prs_debug(ps, depth, desc, "lsa_io_r_query");
+ depth++;
+
+ if(!prs_uint32("undoc_buffer", ps, depth, &r_q->undoc_buffer))
+ return False;
+
+ if (r_q->undoc_buffer != 0) {
+ if(!prs_uint16("info_class", ps, depth, &r_q->info_class))
+ return False;
+
+ if(!prs_align(ps))
+ return False;
+
+ switch (r_q->info_class) {
+ case 2:
+ if(!lsa_io_dom_query_2("", &r_q->dom.id2, ps, depth))
+ return False;
+ break;
+ case 3:
+ if(!lsa_io_dom_query_3("", &r_q->dom.id3, ps, depth))
+ return False;
+ break;
+ case 5:
+ if(!lsa_io_dom_query_5("", &r_q->dom.id5, ps, depth))
+ return False;
+ break;
+ case 6:
+ if(!lsa_io_dom_query_6("", &r_q->dom.id6, ps, depth))
+ return False;
+ break;
+ default:
+ /* PANIC! */
+ break;
+ }
+ }
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_ntstatus("status", ps, depth, &r_q->status))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ Inits a LSA_SID_ENUM structure.
+********************************************************************/
+
+static void init_lsa_sid_enum(TALLOC_CTX *mem_ctx, LSA_SID_ENUM *sen,
+ int num_entries, const DOM_SID *sids)
+{
+ int i;
+
+ DEBUG(5, ("init_lsa_sid_enum\n"));
+
+ sen->num_entries = num_entries;
+ sen->ptr_sid_enum = (num_entries != 0);
+ sen->num_entries2 = num_entries;
+
+ /* Allocate memory for sids and sid pointers */
+
+ if (num_entries == 0) return;
+
+ if ((sen->ptr_sid = (uint32 *)talloc_zero(mem_ctx, num_entries *
+ sizeof(uint32))) == NULL) {
+ DEBUG(3, ("init_lsa_sid_enum(): out of memory for ptr_sid\n"));
+ return;
+ }
+
+ if ((sen->sid = (DOM_SID2 *)talloc_zero(mem_ctx, num_entries *
+ sizeof(DOM_SID2))) == NULL) {
+ DEBUG(3, ("init_lsa_sid_enum(): out of memory for sids\n"));
+ return;
+ }
+
+ /* Copy across SIDs and SID pointers */
+
+ for (i = 0; i < num_entries; i++) {
+ sen->ptr_sid[i] = 1;
+ init_dom_sid2(&sen->sid[i], &sids[i]);
+ }
+}
+
+/*******************************************************************
+ Reads or writes a LSA_SID_ENUM structure.
+********************************************************************/
+
+static BOOL lsa_io_sid_enum(const char *desc, LSA_SID_ENUM *sen, prs_struct *ps,
+ int depth)
+{
+ unsigned int i;
+
+ prs_debug(ps, depth, desc, "lsa_io_sid_enum");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("num_entries ", ps, depth, &sen->num_entries))
+ return False;
+ if(!prs_uint32("ptr_sid_enum", ps, depth, &sen->ptr_sid_enum))
+ return False;
+
+ /*
+ if the ptr is NULL, leave here. checked from a real w2k trace.
+ JFM, 11/23/2001
+ */
+
+ if (sen->ptr_sid_enum==0)
+ return True;
+
+ if(!prs_uint32("num_entries2", ps, depth, &sen->num_entries2))
+ return False;
+
+ /* Mallocate memory if we're unpacking from the wire */
+
+ if (UNMARSHALLING(ps)) {
+ if ((sen->ptr_sid = (uint32 *)prs_alloc_mem( ps,
+ sen->num_entries * sizeof(uint32))) == NULL) {
+ DEBUG(3, ("init_lsa_sid_enum(): out of memory for "
+ "ptr_sid\n"));
+ return False;
+ }
+
+ if ((sen->sid = (DOM_SID2 *)prs_alloc_mem( ps,
+ sen->num_entries * sizeof(DOM_SID2))) == NULL) {
+ DEBUG(3, ("init_lsa_sid_enum(): out of memory for "
+ "sids\n"));
+ return False;
+ }
+ }
+
+ for (i = 0; i < sen->num_entries; i++) {
+ fstring temp;
+
+ slprintf(temp, sizeof(temp) - 1, "ptr_sid[%d]", i);
+ if(!prs_uint32(temp, ps, depth, &sen->ptr_sid[i])) {
+ return False;
+ }
+ }
+
+ for (i = 0; i < sen->num_entries; i++) {
+ fstring temp;
+
+ slprintf(temp, sizeof(temp) - 1, "sid[%d]", i);
+ if(!smb_io_dom_sid2(temp, &sen->sid[i], ps, depth)) {
+ return False;
+ }
+ }
+
+ return True;
+}
+
+/*******************************************************************
+ Inits an LSA_R_ENUM_TRUST_DOM structure.
+********************************************************************/
+
+void init_q_lookup_sids(TALLOC_CTX *mem_ctx, LSA_Q_LOOKUP_SIDS *q_l,
+ POLICY_HND *hnd, int num_sids, const DOM_SID *sids,
+ uint16 level)
+{
+ DEBUG(5, ("init_q_lookup_sids\n"));
+
+ ZERO_STRUCTP(q_l);
+
+ memcpy(&q_l->pol, hnd, sizeof(q_l->pol));
+ init_lsa_sid_enum(mem_ctx, &q_l->sids, num_sids, sids);
+
+ q_l->level.value = level;
+}
+
+/*******************************************************************
+ Reads or writes a LSA_Q_LOOKUP_SIDS structure.
+********************************************************************/
+
+BOOL lsa_io_q_lookup_sids(const char *desc, LSA_Q_LOOKUP_SIDS *q_s, prs_struct *ps,
+ int depth)
+{
+ prs_debug(ps, depth, desc, "lsa_io_q_lookup_sids");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!smb_io_pol_hnd("pol_hnd", &q_s->pol, ps, depth)) /* policy handle */
+ return False;
+ if(!lsa_io_sid_enum("sids ", &q_s->sids, ps, depth)) /* sids to be looked up */
+ return False;
+ if(!lsa_io_trans_names("names ", &q_s->names, ps, depth)) /* translated names */
+ return False;
+ if(!smb_io_lookup_level("switch ", &q_s->level, ps, depth)) /* lookup level */
+ return False;
+
+ if(!prs_uint32("mapped_count", ps, depth, &q_s->mapped_count))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ Reads or writes a structure.
+********************************************************************/
+
+static BOOL lsa_io_trans_names(const char *desc, LSA_TRANS_NAME_ENUM *trn,
+ prs_struct *ps, int depth)
+{
+ unsigned int i;
+
+ prs_debug(ps, depth, desc, "lsa_io_trans_names");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("num_entries ", ps, depth, &trn->num_entries))
+ return False;
+ if(!prs_uint32("ptr_trans_names", ps, depth, &trn->ptr_trans_names))
+ return False;
+
+ if (trn->ptr_trans_names != 0) {
+ if(!prs_uint32("num_entries2 ", ps, depth,
+ &trn->num_entries2))
+ return False;
+
+ if (UNMARSHALLING(ps)) {
+ if ((trn->name = (LSA_TRANS_NAME *)
+ prs_alloc_mem(ps, trn->num_entries *
+ sizeof(LSA_TRANS_NAME))) == NULL) {
+ return False;
+ }
+
+ if ((trn->uni_name = (UNISTR2 *)
+ prs_alloc_mem(ps, trn->num_entries *
+ sizeof(UNISTR2))) == NULL) {
+ return False;
+ }
+ }
+
+ for (i = 0; i < trn->num_entries2; i++) {
+ fstring t;
+ slprintf(t, sizeof(t) - 1, "name[%d] ", i);
+
+ if(!lsa_io_trans_name(t, &trn->name[i], ps, depth)) /* translated name */
+ return False;
+ }
+
+ for (i = 0; i < trn->num_entries2; i++) {
+ fstring t;
+ slprintf(t, sizeof(t) - 1, "name[%d] ", i);
+
+ if(!smb_io_unistr2(t, &trn->uni_name[i], trn->name[i].hdr_name.buffer, ps, depth))
+ return False;
+ if(!prs_align(ps))
+ return False;
+ }
+ }
+
+ return True;
+}
+
+/*******************************************************************
+ Reads or writes a structure.
+********************************************************************/
+
+BOOL lsa_io_r_lookup_sids(const char *desc, LSA_R_LOOKUP_SIDS *r_s,
+ prs_struct *ps, int depth)
+{
+ prs_debug(ps, depth, desc, "lsa_io_r_lookup_sids");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("ptr_dom_ref", ps, depth, &r_s->ptr_dom_ref))
+ return False;
+
+ if (r_s->ptr_dom_ref != 0)
+ if(!lsa_io_dom_r_ref ("dom_ref", r_s->dom_ref, ps, depth)) /* domain reference info */
+ return False;
+
+ if(!lsa_io_trans_names("names ", r_s->names, ps, depth)) /* translated names */
+ return False;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("mapped_count", ps, depth, &r_s->mapped_count))
+ return False;
+
+ if(!prs_ntstatus("status ", ps, depth, &r_s->status))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+makes a structure.
+********************************************************************/
+
+void init_q_lookup_names(TALLOC_CTX *mem_ctx, LSA_Q_LOOKUP_NAMES *q_l,
+ POLICY_HND *hnd, int num_names, const char **names)
+{
+ unsigned int i;
+
+ DEBUG(5, ("init_q_lookup_names\n"));
+
+ ZERO_STRUCTP(q_l);
+
+ q_l->pol = *hnd;
+ q_l->num_entries = num_names;
+ q_l->num_entries2 = num_names;
+ q_l->lookup_level = 1;
+
+ if ((q_l->uni_name = (UNISTR2 *)talloc_zero(
+ mem_ctx, num_names * sizeof(UNISTR2))) == NULL) {
+ DEBUG(3, ("init_q_lookup_names(): out of memory\n"));
+ return;
+ }
+
+ if ((q_l->hdr_name = (UNIHDR *)talloc_zero(
+ mem_ctx, num_names * sizeof(UNIHDR))) == NULL) {
+ DEBUG(3, ("init_q_lookup_names(): out of memory\n"));
+ return;
+ }
+
+ for (i = 0; i < num_names; i++) {
+ init_unistr2(&q_l->uni_name[i], names[i], UNI_FLAGS_NONE);
+ init_uni_hdr(&q_l->hdr_name[i], &q_l->uni_name[i]);
+ }
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+BOOL lsa_io_q_lookup_names(const char *desc, LSA_Q_LOOKUP_NAMES *q_r,
+ prs_struct *ps, int depth)
+{
+ unsigned int i;
+
+ prs_debug(ps, depth, desc, "lsa_io_q_lookup_names");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!smb_io_pol_hnd("", &q_r->pol, ps, depth)) /* policy handle */
+ return False;
+
+ if(!prs_align(ps))
+ return False;
+ if(!prs_uint32("num_entries ", ps, depth, &q_r->num_entries))
+ return False;
+ if(!prs_uint32("num_entries2 ", ps, depth, &q_r->num_entries2))
+ return False;
+
+ if (UNMARSHALLING(ps)) {
+ if (q_r->num_entries) {
+ if ((q_r->hdr_name = (UNIHDR *)prs_alloc_mem(ps,
+ q_r->num_entries * sizeof(UNIHDR))) == NULL)
+ return False;
+ if ((q_r->uni_name = (UNISTR2 *)prs_alloc_mem(ps,
+ q_r->num_entries * sizeof(UNISTR2))) == NULL)
+ return False;
+ }
+ }
+
+ for (i = 0; i < q_r->num_entries; i++) {
+ if(!prs_align(ps))
+ return False;
+ if(!smb_io_unihdr("hdr_name", &q_r->hdr_name[i], ps, depth)) /* pointer names */
+ return False;
+ }
+
+ for (i = 0; i < q_r->num_entries; i++) {
+ if(!prs_align(ps))
+ return False;
+ if(!smb_io_unistr2("dom_name", &q_r->uni_name[i], q_r->hdr_name[i].buffer, ps, depth)) /* names to be looked up */
+ return False;
+ }
+
+ if(!prs_align(ps))
+ return False;
+ if(!prs_uint32("num_trans_entries ", ps, depth, &q_r->num_trans_entries))
+ return False;
+ if(!prs_uint32("ptr_trans_sids ", ps, depth, &q_r->ptr_trans_sids))
+ return False;
+ if(!prs_uint32("lookup_level ", ps, depth, &q_r->lookup_level))
+ return False;
+ if(!prs_uint32("mapped_count ", ps, depth, &q_r->mapped_count))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+BOOL lsa_io_r_lookup_names(const char *desc, LSA_R_LOOKUP_NAMES *r_r,
+ prs_struct *ps, int depth)
+{
+ unsigned int i;
+
+ prs_debug(ps, depth, desc, "lsa_io_r_lookup_names");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("ptr_dom_ref", ps, depth, &r_r->ptr_dom_ref))
+ return False;
+
+ if (r_r->ptr_dom_ref != 0)
+ if(!lsa_io_dom_r_ref("", r_r->dom_ref, ps, depth))
+ return False;
+
+ if(!prs_uint32("num_entries", ps, depth, &r_r->num_entries))
+ return False;
+ if(!prs_uint32("ptr_entries", ps, depth, &r_r->ptr_entries))
+ return False;
+
+ if (r_r->ptr_entries != 0) {
+ if(!prs_uint32("num_entries2", ps, depth, &r_r->num_entries2))
+ return False;
+
+ if (r_r->num_entries2 != r_r->num_entries) {
+ /* RPC fault */
+ return False;
+ }
+
+ if (UNMARSHALLING(ps)) {
+ if ((r_r->dom_rid = (DOM_RID2 *)prs_alloc_mem(ps, r_r->num_entries2 * sizeof(DOM_RID2)))
+ == NULL) {
+ DEBUG(3, ("lsa_io_r_lookup_names(): out of memory\n"));
+ return False;
+ }
+ }
+
+ for (i = 0; i < r_r->num_entries2; i++)
+ if(!smb_io_dom_rid2("", &r_r->dom_rid[i], ps, depth)) /* domain RIDs being looked up */
+ return False;
+ }
+
+ if(!prs_uint32("mapped_count", ps, depth, &r_r->mapped_count))
+ return False;
+
+ if(!prs_ntstatus("status ", ps, depth, &r_r->status))
+ return False;
+
+ return True;
+}
+
+
+/*******************************************************************
+ Inits an LSA_Q_CLOSE structure.
+********************************************************************/
+
+void init_lsa_q_close(LSA_Q_CLOSE *q_c, POLICY_HND *hnd)
+{
+ DEBUG(5, ("init_lsa_q_close\n"));
+
+ memcpy(&q_c->pol, hnd, sizeof(q_c->pol));
+}
+
+/*******************************************************************
+ Reads or writes an LSA_Q_CLOSE structure.
+********************************************************************/
+
+BOOL lsa_io_q_close(const char *desc, LSA_Q_CLOSE *q_c, prs_struct *ps, int depth)
+{
+ prs_debug(ps, depth, desc, "lsa_io_q_close");
+ depth++;
+
+ if(!smb_io_pol_hnd("", &q_c->pol, ps, depth))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ Reads or writes an LSA_R_CLOSE structure.
+********************************************************************/
+
+BOOL lsa_io_r_close(const char *desc, LSA_R_CLOSE *r_c, prs_struct *ps, int depth)
+{
+ prs_debug(ps, depth, desc, "lsa_io_r_close");
+ depth++;
+
+ if(!smb_io_pol_hnd("", &r_c->pol, ps, depth))
+ return False;
+
+ if(!prs_ntstatus("status", ps, depth, &r_c->status))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ Reads or writes an LSA_Q_OPEN_SECRET structure.
+********************************************************************/
+
+BOOL lsa_io_q_open_secret(const char *desc, LSA_Q_OPEN_SECRET *q_c, prs_struct *ps, int depth)
+{
+ prs_debug(ps, depth, desc, "lsa_io_q_open_secret");
+ depth++;
+
+ /* Don't bother to read or write at present... */
+ return True;
+}
+
+/*******************************************************************
+ Reads or writes an LSA_R_OPEN_SECRET structure.
+********************************************************************/
+
+BOOL lsa_io_r_open_secret(const char *desc, LSA_R_OPEN_SECRET *r_c, prs_struct *ps, int depth)
+{
+ prs_debug(ps, depth, desc, "lsa_io_r_open_secret");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("dummy1", ps, depth, &r_c->dummy1))
+ return False;
+ if(!prs_uint32("dummy2", ps, depth, &r_c->dummy2))
+ return False;
+ if(!prs_uint32("dummy3", ps, depth, &r_c->dummy3))
+ return False;
+ if(!prs_uint32("dummy4", ps, depth, &r_c->dummy4))
+ return False;
+ if(!prs_ntstatus("status", ps, depth, &r_c->status))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ Inits an LSA_Q_ENUM_PRIVS structure.
+********************************************************************/
+
+void init_q_enum_privs(LSA_Q_ENUM_PRIVS *q_q, POLICY_HND *hnd, uint32 enum_context, uint32 pref_max_length)
+{
+ DEBUG(5, ("init_q_enum_privs\n"));
+
+ memcpy(&q_q->pol, hnd, sizeof(q_q->pol));
+
+ q_q->enum_context = enum_context;
+ q_q->pref_max_length = pref_max_length;
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+BOOL lsa_io_q_enum_privs(const char *desc, LSA_Q_ENUM_PRIVS *q_q, prs_struct *ps, int depth)
+{
+ if (q_q == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "lsa_io_q_enum_privs");
+ depth++;
+
+ if (!smb_io_pol_hnd("", &q_q->pol, ps, depth))
+ return False;
+
+ if(!prs_uint32("enum_context ", ps, depth, &q_q->enum_context))
+ return False;
+ if(!prs_uint32("pref_max_length", ps, depth, &q_q->pref_max_length))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+static BOOL lsa_io_priv_entries(const char *desc, LSA_PRIV_ENTRY *entries, uint32 count, prs_struct *ps, int depth)
+{
+ uint32 i;
+
+ if (entries == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "lsa_io_priv_entries");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ for (i = 0; i < count; i++) {
+ if (!smb_io_unihdr("", &entries[i].hdr_name, ps, depth))
+ return False;
+ if(!prs_uint32("luid_low ", ps, depth, &entries[i].luid_low))
+ return False;
+ if(!prs_uint32("luid_high", ps, depth, &entries[i].luid_high))
+ return False;
+ }
+
+ for (i = 0; i < count; i++)
+ if (!smb_io_unistr2("", &entries[i].name, entries[i].hdr_name.buffer, ps, depth))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ Inits an LSA_R_ENUM_PRIVS structure.
+********************************************************************/
+
+void init_lsa_r_enum_privs(LSA_R_ENUM_PRIVS *r_u, uint32 enum_context,
+ uint32 count, LSA_PRIV_ENTRY *entries)
+{
+ DEBUG(5, ("init_lsa_r_enum_privs\n"));
+
+ r_u->enum_context=enum_context;
+ r_u->count=count;
+
+ if (entries!=NULL) {
+ r_u->ptr=1;
+ r_u->count1=count;
+ r_u->privs=entries;
+ } else {
+ r_u->ptr=0;
+ r_u->count1=0;
+ r_u->privs=NULL;
+ }
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+BOOL lsa_io_r_enum_privs(const char *desc, LSA_R_ENUM_PRIVS *r_q, prs_struct *ps, int depth)
+{
+ if (r_q == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "lsa_io_r_enum_privs");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("enum_context", ps, depth, &r_q->enum_context))
+ return False;
+ if(!prs_uint32("count", ps, depth, &r_q->count))
+ return False;
+ if(!prs_uint32("ptr", ps, depth, &r_q->ptr))
+ return False;
+
+ if (r_q->ptr) {
+ if(!prs_uint32("count1", ps, depth, &r_q->count1))
+ return False;
+
+ if (UNMARSHALLING(ps))
+ if (!(r_q->privs = (LSA_PRIV_ENTRY *)prs_alloc_mem(ps, sizeof(LSA_PRIV_ENTRY) * r_q->count1)))
+ return False;
+
+ if (!lsa_io_priv_entries("", r_q->privs, r_q->count1, ps, depth))
+ return False;
+ }
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_ntstatus("status", ps, depth, &r_q->status))
+ return False;
+
+ return True;
+}
+
+void init_lsa_priv_get_dispname(LSA_Q_PRIV_GET_DISPNAME *trn, POLICY_HND *hnd, const char *name, uint16 lang_id, uint16 lang_id_sys)
+{
+ memcpy(&trn->pol, hnd, sizeof(trn->pol));
+
+ init_unistr2(&trn->name, name, UNI_FLAGS_NONE);
+ init_uni_hdr(&trn->hdr_name, &trn->name);
+ trn->lang_id = lang_id;
+ trn->lang_id_sys = lang_id_sys;
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+BOOL lsa_io_q_priv_get_dispname(const char *desc, LSA_Q_PRIV_GET_DISPNAME *q_q, prs_struct *ps, int depth)
+{
+ if (q_q == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "lsa_io_q_priv_get_dispname");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if (!smb_io_pol_hnd("", &q_q->pol, ps, depth))
+ return False;
+
+ if (!smb_io_unihdr("hdr_name", &q_q->hdr_name, ps, depth))
+ return False;
+
+ if (!smb_io_unistr2("name", &q_q->name, q_q->hdr_name.buffer, ps, depth))
+ return False;
+
+ if(!prs_uint16("lang_id ", ps, depth, &q_q->lang_id))
+ return False;
+ if(!prs_uint16("lang_id_sys", ps, depth, &q_q->lang_id_sys))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+BOOL lsa_io_r_priv_get_dispname(const char *desc, LSA_R_PRIV_GET_DISPNAME *r_q, prs_struct *ps, int depth)
+{
+ if (r_q == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "lsa_io_r_priv_get_dispname");
+ depth++;
+
+ if (!prs_align(ps))
+ return False;
+
+ if (!prs_uint32("ptr_info", ps, depth, &r_q->ptr_info))
+ return False;
+
+ if (r_q->ptr_info){
+ if (!smb_io_unihdr("hdr_name", &r_q->hdr_desc, ps, depth))
+ return False;
+
+ if (!smb_io_unistr2("desc", &r_q->desc, r_q->hdr_desc.buffer, ps, depth))
+ return False;
+ }
+/*
+ if(!prs_align(ps))
+ return False;
+*/
+ if(!prs_uint16("lang_id", ps, depth, &r_q->lang_id))
+ return False;
+
+ if(!prs_align(ps))
+ return False;
+ if(!prs_ntstatus("status", ps, depth, &r_q->status))
+ return False;
+
+ return True;
+}
+
+/*
+ initialise a LSA_Q_ENUM_ACCOUNTS structure
+*/
+void init_lsa_q_enum_accounts(LSA_Q_ENUM_ACCOUNTS *trn, POLICY_HND *hnd, uint32 enum_context, uint32 pref_max_length)
+{
+ memcpy(&trn->pol, hnd, sizeof(trn->pol));
+
+ trn->enum_context = enum_context;
+ trn->pref_max_length = pref_max_length;
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+BOOL lsa_io_q_enum_accounts(const char *desc, LSA_Q_ENUM_ACCOUNTS *q_q, prs_struct *ps, int depth)
+{
+ if (q_q == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "lsa_io_q_enum_accounts");
+ depth++;
+
+ if (!smb_io_pol_hnd("", &q_q->pol, ps, depth))
+ return False;
+
+ if(!prs_uint32("enum_context ", ps, depth, &q_q->enum_context))
+ return False;
+ if(!prs_uint32("pref_max_length", ps, depth, &q_q->pref_max_length))
+ return False;
+
+ return True;
+}
+
+
+/*******************************************************************
+ Inits an LSA_R_ENUM_PRIVS structure.
+********************************************************************/
+
+void init_lsa_r_enum_accounts(LSA_R_ENUM_ACCOUNTS *r_u, uint32 enum_context)
+{
+ DEBUG(5, ("init_lsa_r_enum_accounts\n"));
+
+ r_u->enum_context=enum_context;
+ if (r_u->enum_context!=0) {
+ r_u->sids.num_entries=enum_context;
+ r_u->sids.ptr_sid_enum=1;
+ r_u->sids.num_entries2=enum_context;
+ } else {
+ r_u->sids.num_entries=0;
+ r_u->sids.ptr_sid_enum=0;
+ r_u->sids.num_entries2=0;
+ }
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+BOOL lsa_io_r_enum_accounts(const char *desc, LSA_R_ENUM_ACCOUNTS *r_q, prs_struct *ps, int depth)
+{
+ if (r_q == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "lsa_io_r_enum_accounts");
+ depth++;
+
+ if (!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("enum_context", ps, depth, &r_q->enum_context))
+ return False;
+
+ if (!lsa_io_sid_enum("sids", &r_q->sids, ps, depth))
+ return False;
+
+ if (!prs_align(ps))
+ return False;
+
+ if(!prs_ntstatus("status", ps, depth, &r_q->status))
+ return False;
+
+ return True;
+}
+
+
+/*******************************************************************
+ Reads or writes an LSA_Q_UNK_GET_CONNUSER structure.
+********************************************************************/
+
+BOOL lsa_io_q_unk_get_connuser(const char *desc, LSA_Q_UNK_GET_CONNUSER *q_c, prs_struct *ps, int depth)
+{
+ prs_debug(ps, depth, desc, "lsa_io_q_unk_get_connuser");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("ptr_srvname", ps, depth, &q_c->ptr_srvname))
+ return False;
+
+ if(!smb_io_unistr2("uni2_srvname", &q_c->uni2_srvname, q_c->ptr_srvname, ps, depth)) /* server name to be looked up */
+ return False;
+
+ if (!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("unk1", ps, depth, &q_c->unk1))
+ return False;
+ if(!prs_uint32("unk2", ps, depth, &q_c->unk2))
+ return False;
+ if(!prs_uint32("unk3", ps, depth, &q_c->unk3))
+ return False;
+
+ /* Don't bother to read or write at present... */
+ return True;
+}
+
+/*******************************************************************
+ Reads or writes an LSA_R_UNK_GET_CONNUSER structure.
+********************************************************************/
+
+BOOL lsa_io_r_unk_get_connuser(const char *desc, LSA_R_UNK_GET_CONNUSER *r_c, prs_struct *ps, int depth)
+{
+ prs_debug(ps, depth, desc, "lsa_io_r_unk_get_connuser");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("ptr_user_name", ps, depth, &r_c->ptr_user_name))
+ return False;
+ if(!smb_io_unihdr("hdr_user_name", &r_c->hdr_user_name, ps, depth))
+ return False;
+ if(!smb_io_unistr2("uni2_user_name", &r_c->uni2_user_name, r_c->ptr_user_name, ps, depth))
+ return False;
+
+ if (!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("unk1", ps, depth, &r_c->unk1))
+ return False;
+
+ if(!prs_uint32("ptr_dom_name", ps, depth, &r_c->ptr_dom_name))
+ return False;
+ if(!smb_io_unihdr("hdr_dom_name", &r_c->hdr_dom_name, ps, depth))
+ return False;
+ if(!smb_io_unistr2("uni2_dom_name", &r_c->uni2_dom_name, r_c->ptr_dom_name, ps, depth))
+ return False;
+
+ if (!prs_align(ps))
+ return False;
+
+ if(!prs_ntstatus("status", ps, depth, &r_c->status))
+ return False;
+
+ return True;
+}
+
+void init_lsa_q_create_account(LSA_Q_CREATEACCOUNT *trn, POLICY_HND *hnd, DOM_SID *sid, uint32 desired_access)
+{
+ memcpy(&trn->pol, hnd, sizeof(trn->pol));
+
+ init_dom_sid2(&trn->sid, sid);
+ trn->access = desired_access;
+}
+
+
+/*******************************************************************
+ Reads or writes an LSA_Q_CREATEACCOUNT structure.
+********************************************************************/
+
+BOOL lsa_io_q_create_account(const char *desc, LSA_Q_CREATEACCOUNT *r_c, prs_struct *ps, int depth)
+{
+ prs_debug(ps, depth, desc, "lsa_io_q_create_account");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!smb_io_pol_hnd("pol", &r_c->pol, ps, depth))
+ return False;
+
+ if(!smb_io_dom_sid2("sid", &r_c->sid, ps, depth)) /* domain SID */
+ return False;
+
+ if(!prs_uint32("access", ps, depth, &r_c->access))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ Reads or writes an LSA_R_CREATEACCOUNT structure.
+********************************************************************/
+
+BOOL lsa_io_r_create_account(const char *desc, LSA_R_CREATEACCOUNT *r_c, prs_struct *ps, int depth)
+{
+ prs_debug(ps, depth, desc, "lsa_io_r_open_account");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!smb_io_pol_hnd("pol", &r_c->pol, ps, depth))
+ return False;
+
+ if(!prs_ntstatus("status", ps, depth, &r_c->status))
+ return False;
+
+ return True;
+}
+
+
+void init_lsa_q_open_account(LSA_Q_OPENACCOUNT *trn, POLICY_HND *hnd, DOM_SID *sid, uint32 desired_access)
+{
+ memcpy(&trn->pol, hnd, sizeof(trn->pol));
+
+ init_dom_sid2(&trn->sid, sid);
+ trn->access = desired_access;
+}
+
+/*******************************************************************
+ Reads or writes an LSA_Q_OPENACCOUNT structure.
+********************************************************************/
+
+BOOL lsa_io_q_open_account(const char *desc, LSA_Q_OPENACCOUNT *r_c, prs_struct *ps, int depth)
+{
+ prs_debug(ps, depth, desc, "lsa_io_q_open_account");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!smb_io_pol_hnd("pol", &r_c->pol, ps, depth))
+ return False;
+
+ if(!smb_io_dom_sid2("sid", &r_c->sid, ps, depth)) /* domain SID */
+ return False;
+
+ if(!prs_uint32("access", ps, depth, &r_c->access))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ Reads or writes an LSA_R_OPENACCOUNT structure.
+********************************************************************/
+
+BOOL lsa_io_r_open_account(const char *desc, LSA_R_OPENACCOUNT *r_c, prs_struct *ps, int depth)
+{
+ prs_debug(ps, depth, desc, "lsa_io_r_open_account");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!smb_io_pol_hnd("pol", &r_c->pol, ps, depth))
+ return False;
+
+ if(!prs_ntstatus("status", ps, depth, &r_c->status))
+ return False;
+
+ return True;
+}
+
+
+void init_lsa_q_enum_privsaccount(LSA_Q_ENUMPRIVSACCOUNT *trn, POLICY_HND *hnd)
+{
+ memcpy(&trn->pol, hnd, sizeof(trn->pol));
+
+}
+
+/*******************************************************************
+ Reads or writes an LSA_Q_ENUMPRIVSACCOUNT structure.
+********************************************************************/
+
+BOOL lsa_io_q_enum_privsaccount(const char *desc, LSA_Q_ENUMPRIVSACCOUNT *r_c, prs_struct *ps, int depth)
+{
+ prs_debug(ps, depth, desc, "lsa_io_q_enum_privsaccount");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!smb_io_pol_hnd("pol", &r_c->pol, ps, depth))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ Reads or writes an LUID structure.
+********************************************************************/
+
+static BOOL lsa_io_luid(const char *desc, LUID *r_c, prs_struct *ps, int depth)
+{
+ prs_debug(ps, depth, desc, "lsa_io_luid");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("low", ps, depth, &r_c->low))
+ return False;
+
+ if(!prs_uint32("high", ps, depth, &r_c->high))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ Reads or writes an LUID_ATTR structure.
+********************************************************************/
+
+static BOOL lsa_io_luid_attr(const char *desc, LUID_ATTR *r_c, prs_struct *ps, int depth)
+{
+ prs_debug(ps, depth, desc, "lsa_io_luid_attr");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if (!lsa_io_luid(desc, &r_c->luid, ps, depth))
+ return False;
+
+ if(!prs_uint32("attr", ps, depth, &r_c->attr))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ Reads or writes an PRIVILEGE_SET structure.
+********************************************************************/
+
+static BOOL lsa_io_privilege_set(const char *desc, PRIVILEGE_SET *r_c, prs_struct *ps, int depth)
+{
+ uint32 i;
+
+ prs_debug(ps, depth, desc, "lsa_io_privilege_set");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("count", ps, depth, &r_c->count))
+ return False;
+ if(!prs_uint32("control", ps, depth, &r_c->control))
+ return False;
+
+ for (i=0; i<r_c->count; i++) {
+ if (!lsa_io_luid_attr(desc, &r_c->set[i], ps, depth))
+ return False;
+ }
+
+ return True;
+}
+
+NTSTATUS init_lsa_r_enum_privsaccount(TALLOC_CTX *mem_ctx, LSA_R_ENUMPRIVSACCOUNT *r_u, LUID_ATTR *set, uint32 count, uint32 control)
+{
+ NTSTATUS ret = NT_STATUS_OK;
+
+ r_u->ptr = 1;
+ r_u->count = count;
+
+ if (!NT_STATUS_IS_OK(ret = init_priv_with_ctx(mem_ctx, &(r_u->set))))
+ return ret;
+
+ r_u->set->count = count;
+
+ if (!NT_STATUS_IS_OK(ret = dupalloc_luid_attr(r_u->set->mem_ctx, &(r_u->set->set), set, count)))
+ return ret;
+
+ DEBUG(10,("init_lsa_r_enum_privsaccount: %d privileges\n", r_u->count));
+
+ return ret;
+}
+
+/*******************************************************************
+ Reads or writes an LSA_R_ENUMPRIVSACCOUNT structure.
+********************************************************************/
+
+BOOL lsa_io_r_enum_privsaccount(const char *desc, LSA_R_ENUMPRIVSACCOUNT *r_c, prs_struct *ps, int depth)
+{
+ prs_debug(ps, depth, desc, "lsa_io_r_enum_privsaccount");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("ptr", ps, depth, &r_c->ptr))
+ return False;
+
+ if (r_c->ptr!=0) {
+ if(!prs_uint32("count", ps, depth, &r_c->count))
+ return False;
+
+ /* malloc memory if unmarshalling here */
+
+ if (UNMARSHALLING(ps) && r_c->count != 0) {
+ if (!NT_STATUS_IS_OK(init_priv_with_ctx(ps->mem_ctx, &(r_c->set))))
+ return False;
+
+ if (!(r_c->set->set = (LUID_ATTR *)prs_alloc_mem(ps,sizeof(LUID_ATTR) * r_c->count)))
+ return False;
+
+ }
+
+ if(!lsa_io_privilege_set(desc, r_c->set, ps, depth))
+ return False;
+ }
+
+ if(!prs_ntstatus("status", ps, depth, &r_c->status))
+ return False;
+
+ return True;
+}
+
+
+
+/*******************************************************************
+ Reads or writes an LSA_Q_GETSYSTEMACCOUNTstructure.
+********************************************************************/
+
+BOOL lsa_io_q_getsystemaccount(const char *desc, LSA_Q_GETSYSTEMACCOUNT *r_c, prs_struct *ps, int depth)
+{
+ prs_debug(ps, depth, desc, "lsa_io_q_getsystemaccount");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!smb_io_pol_hnd("pol", &r_c->pol, ps, depth))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ Reads or writes an LSA_R_GETSYSTEMACCOUNTstructure.
+********************************************************************/
+
+BOOL lsa_io_r_getsystemaccount(const char *desc, LSA_R_GETSYSTEMACCOUNT *r_c, prs_struct *ps, int depth)
+{
+ prs_debug(ps, depth, desc, "lsa_io_r_getsystemaccount");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("access", ps, depth, &r_c->access))
+ return False;
+
+ if(!prs_ntstatus("status", ps, depth, &r_c->status))
+ return False;
+
+ return True;
+}
+
+
+/*******************************************************************
+ Reads or writes an LSA_Q_SETSYSTEMACCOUNT structure.
+********************************************************************/
+
+BOOL lsa_io_q_setsystemaccount(const char *desc, LSA_Q_SETSYSTEMACCOUNT *r_c, prs_struct *ps, int depth)
+{
+ prs_debug(ps, depth, desc, "lsa_io_q_setsystemaccount");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!smb_io_pol_hnd("pol", &r_c->pol, ps, depth))
+ return False;
+
+ if(!prs_uint32("access", ps, depth, &r_c->access))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ Reads or writes an LSA_R_SETSYSTEMACCOUNT structure.
+********************************************************************/
+
+BOOL lsa_io_r_setsystemaccount(const char *desc, LSA_R_SETSYSTEMACCOUNT *r_c, prs_struct *ps, int depth)
+{
+ prs_debug(ps, depth, desc, "lsa_io_r_setsystemaccount");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_ntstatus("status", ps, depth, &r_c->status))
+ return False;
+
+ return True;
+}
+
+
+void init_lsa_q_lookupprivvalue(LSA_Q_LOOKUPPRIVVALUE *trn, POLICY_HND *hnd, const char *name)
+{
+ memcpy(&trn->pol, hnd, sizeof(trn->pol));
+ init_unistr2(&trn->uni2_right, name, UNI_FLAGS_NONE);
+ init_uni_hdr(&trn->hdr_right, &trn->uni2_right);
+}
+
+/*******************************************************************
+ Reads or writes an LSA_Q_LOOKUPPRIVVALUE structure.
+********************************************************************/
+
+BOOL lsa_io_q_lookupprivvalue(const char *desc, LSA_Q_LOOKUPPRIVVALUE *r_c, prs_struct *ps, int depth)
+{
+ prs_debug(ps, depth, desc, "lsa_io_q_lookupprivvalue");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!smb_io_pol_hnd("pol", &r_c->pol, ps, depth))
+ return False;
+ if(!smb_io_unihdr ("hdr_name", &r_c->hdr_right, ps, depth))
+ return False;
+ if(!smb_io_unistr2("uni2_right", &r_c->uni2_right, r_c->hdr_right.buffer, ps, depth))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ Reads or writes an LSA_R_LOOKUPPRIVVALUE structure.
+********************************************************************/
+
+BOOL lsa_io_r_lookupprivvalue(const char *desc, LSA_R_LOOKUPPRIVVALUE *r_c, prs_struct *ps, int depth)
+{
+ prs_debug(ps, depth, desc, "lsa_io_r_lookupprivvalue");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!lsa_io_luid("luid", &r_c->luid, ps, depth))
+ return False;
+
+ if(!prs_ntstatus("status", ps, depth, &r_c->status))
+ return False;
+
+ return True;
+}
+
+
+/*******************************************************************
+ Reads or writes an LSA_Q_ADDPRIVS structure.
+********************************************************************/
+
+BOOL lsa_io_q_addprivs(const char *desc, LSA_Q_ADDPRIVS *r_c, prs_struct *ps, int depth)
+{
+ prs_debug(ps, depth, desc, "lsa_io_q_addprivs");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!smb_io_pol_hnd("pol", &r_c->pol, ps, depth))
+ return False;
+
+ if(!prs_uint32("count", ps, depth, &r_c->count))
+ return False;
+
+ if (UNMARSHALLING(ps) && r_c->count!=0) {
+ if (!NT_STATUS_IS_OK(init_priv_with_ctx(ps->mem_ctx, &(r_c->set))))
+ return False;
+
+ if (!(r_c->set->set = (LUID_ATTR *)prs_alloc_mem(ps, sizeof(LUID_ATTR) * r_c->count)))
+ return False;
+ }
+
+ if(!lsa_io_privilege_set(desc, r_c->set, ps, depth))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ Reads or writes an LSA_R_ADDPRIVS structure.
+********************************************************************/
+
+BOOL lsa_io_r_addprivs(const char *desc, LSA_R_ADDPRIVS *r_c, prs_struct *ps, int depth)
+{
+ prs_debug(ps, depth, desc, "lsa_io_r_addprivs");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_ntstatus("status", ps, depth, &r_c->status))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ Reads or writes an LSA_Q_REMOVEPRIVS structure.
+********************************************************************/
+
+BOOL lsa_io_q_removeprivs(const char *desc, LSA_Q_REMOVEPRIVS *r_c, prs_struct *ps, int depth)
+{
+ prs_debug(ps, depth, desc, "lsa_io_q_removeprivs");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!smb_io_pol_hnd("pol", &r_c->pol, ps, depth))
+ return False;
+
+ if(!prs_uint32("allrights", ps, depth, &r_c->allrights))
+ return False;
+
+ if(!prs_uint32("ptr", ps, depth, &r_c->ptr))
+ return False;
+
+ /*
+ * JFM: I'm not sure at all if the count is inside the ptr
+ * never seen one with ptr=0
+ */
+
+ if (r_c->ptr!=0) {
+ if(!prs_uint32("count", ps, depth, &r_c->count))
+ return False;
+
+ if (UNMARSHALLING(ps) && r_c->count!=0) {
+ if (!NT_STATUS_IS_OK(init_priv_with_ctx(ps->mem_ctx, &(r_c->set))))
+ return False;
+
+ if (!(r_c->set->set = (LUID_ATTR *)prs_alloc_mem(ps, sizeof(LUID_ATTR) * r_c->count)))
+ return False;
+ }
+
+ if(!lsa_io_privilege_set(desc, r_c->set, ps, depth))
+ return False;
+ }
+
+ return True;
+}
+
+/*******************************************************************
+ Reads or writes an LSA_R_REMOVEPRIVS structure.
+********************************************************************/
+
+BOOL lsa_io_r_removeprivs(const char *desc, LSA_R_REMOVEPRIVS *r_c, prs_struct *ps, int depth)
+{
+ prs_debug(ps, depth, desc, "lsa_io_r_removeprivs");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_ntstatus("status", ps, depth, &r_c->status))
+ return False;
+
+ return True;
+}
+
+BOOL policy_handle_is_valid(const POLICY_HND *hnd)
+{
+ POLICY_HND zero_pol;
+
+ ZERO_STRUCT(zero_pol);
+ return ((memcmp(&zero_pol, hnd, sizeof(POLICY_HND)) == 0) ? False : True );
+}
+
+/*******************************************************************
+ Reads or writes an LSA_DNS_DOM_INFO structure.
+********************************************************************/
+
+BOOL lsa_io_dns_dom_info(const char *desc, LSA_DNS_DOM_INFO *info,
+ prs_struct *ps, int depth)
+{
+ prs_debug(ps, depth, desc, "lsa_io_dns_dom_info");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+ if(!smb_io_unihdr("nb_name", &info->hdr_nb_dom_name, ps, depth))
+ return False;
+ if(!smb_io_unihdr("dns_name", &info->hdr_dns_dom_name, ps, depth))
+ return False;
+ if(!smb_io_unihdr("forest", &info->hdr_forest_name, ps, depth))
+ return False;
+
+ if(!prs_align(ps))
+ return False;
+ if ( !smb_io_uuid("dom_guid", &info->dom_guid, ps, depth) )
+ return False;
+
+ if(!prs_align(ps))
+ return False;
+ if(!prs_uint32("dom_sid", ps, depth, &info->ptr_dom_sid))
+ return False;
+
+ if(!smb_io_unistr2("nb_name", &info->uni_nb_dom_name,
+ info->hdr_nb_dom_name.buffer, ps, depth))
+ return False;
+ if(!smb_io_unistr2("dns_name", &info->uni_dns_dom_name,
+ info->hdr_dns_dom_name.buffer, ps, depth))
+ return False;
+ if(!smb_io_unistr2("forest", &info->uni_forest_name,
+ info->hdr_forest_name.buffer, ps, depth))
+ return False;
+
+ if(!smb_io_dom_sid2("dom_sid", &info->dom_sid, ps, depth))
+ return False;
+
+ return True;
+
+}
+
+/*******************************************************************
+ Inits an LSA_Q_QUERY_INFO2 structure.
+********************************************************************/
+
+void init_q_query2(LSA_Q_QUERY_INFO2 *q_q, POLICY_HND *hnd, uint16 info_class)
+{
+ DEBUG(5, ("init_q_query2\n"));
+
+ memcpy(&q_q->pol, hnd, sizeof(q_q->pol));
+
+ q_q->info_class = info_class;
+}
+
+/*******************************************************************
+ Reads or writes an LSA_Q_QUERY_DNSDOMINFO structure.
+********************************************************************/
+
+BOOL lsa_io_q_query_info2(const char *desc, LSA_Q_QUERY_INFO2 *q_c,
+ prs_struct *ps, int depth)
+{
+ prs_debug(ps, depth, desc, "lsa_io_q_query_info2");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!smb_io_pol_hnd("pol", &q_c->pol, ps, depth))
+ return False;
+
+ if(!prs_uint16("info_class", ps, depth, &q_c->info_class))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ Reads or writes an LSA_R_QUERY_DNSDOMINFO structure.
+********************************************************************/
+
+BOOL lsa_io_r_query_info2(const char *desc, LSA_R_QUERY_INFO2 *r_c,
+ prs_struct *ps, int depth)
+{
+ prs_debug(ps, depth, desc, "lsa_io_r_query_info2");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("ptr", ps, depth, &r_c->ptr))
+ return False;
+ if(!prs_uint16("info_class", ps, depth, &r_c->info_class))
+ return False;
+ switch(r_c->info_class) {
+ case 0x000c:
+ if (!lsa_io_dns_dom_info("info12", &r_c->info.dns_dom_info,
+ ps, depth))
+ return False;
+ break;
+ default:
+ DEBUG(0,("lsa_io_r_query_info2: unknown info class %d\n",
+ r_c->info_class));
+ return False;
+ }
+
+ if(!prs_align(ps))
+ return False;
+ if(!prs_ntstatus("status", ps, depth, &r_c->status))
+ return False;
+
+ return True;
+}
+
+
+/*******************************************************************
+ Inits an LSA_Q_ENUM_ACCT_RIGHTS structure.
+********************************************************************/
+void init_q_enum_acct_rights(LSA_Q_ENUM_ACCT_RIGHTS *q_q,
+ POLICY_HND *hnd,
+ uint32 count,
+ DOM_SID *sid)
+{
+ DEBUG(5, ("init_q_enum_acct_rights\n"));
+
+ q_q->pol = *hnd;
+ init_dom_sid2(&q_q->sid, sid);
+}
+
+/*******************************************************************
+reads or writes a LSA_Q_ENUM_ACCT_RIGHTS structure.
+********************************************************************/
+BOOL lsa_io_q_enum_acct_rights(const char *desc, LSA_Q_ENUM_ACCT_RIGHTS *q_q, prs_struct *ps, int depth)
+{
+
+ if (q_q == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "lsa_io_q_enum_acct_rights");
+ depth++;
+
+ if (!smb_io_pol_hnd("", &q_q->pol, ps, depth))
+ return False;
+
+ if(!smb_io_dom_sid2("sid", &q_q->sid, ps, depth))
+ return False;
+
+ return True;
+}
+
+
+/*******************************************************************
+reads or writes a LSA_R_ENUM_ACCT_RIGHTS structure.
+********************************************************************/
+BOOL lsa_io_r_enum_acct_rights(const char *desc, LSA_R_ENUM_ACCT_RIGHTS *r_c, prs_struct *ps, int depth)
+{
+ prs_debug(ps, depth, desc, "lsa_io_r_enum_acct_rights");
+ depth++;
+
+ if(!prs_uint32("count ", ps, depth, &r_c->count))
+ return False;
+
+ if(!smb_io_unistr2_array("rights", &r_c->rights, ps, depth))
+ return False;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_ntstatus("status", ps, depth, &r_c->status))
+ return False;
+
+ return True;
+}
+
+
+/*******************************************************************
+ Inits an LSA_Q_ADD_ACCT_RIGHTS structure.
+********************************************************************/
+void init_q_add_acct_rights(LSA_Q_ADD_ACCT_RIGHTS *q_q,
+ POLICY_HND *hnd,
+ DOM_SID *sid,
+ uint32 count,
+ const char **rights)
+{
+ DEBUG(5, ("init_q_add_acct_rights\n"));
+
+ q_q->pol = *hnd;
+ init_dom_sid2(&q_q->sid, sid);
+ init_unistr2_array(&q_q->rights, count, rights);
+ q_q->count = 5;
+}
+
+
+/*******************************************************************
+reads or writes a LSA_Q_ADD_ACCT_RIGHTS structure.
+********************************************************************/
+BOOL lsa_io_q_add_acct_rights(const char *desc, LSA_Q_ADD_ACCT_RIGHTS *q_q, prs_struct *ps, int depth)
+{
+ prs_debug(ps, depth, desc, "lsa_io_q_add_acct_rights");
+ depth++;
+
+ if (!smb_io_pol_hnd("", &q_q->pol, ps, depth))
+ return False;
+
+ if(!smb_io_dom_sid2("sid", &q_q->sid, ps, depth))
+ return False;
+
+ if(!prs_uint32("count", ps, depth, &q_q->rights.count))
+ return False;
+
+ if(!smb_io_unistr2_array("rights", &q_q->rights, ps, depth))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+reads or writes a LSA_R_ENUM_ACCT_RIGHTS structure.
+********************************************************************/
+BOOL lsa_io_r_add_acct_rights(const char *desc, LSA_R_ADD_ACCT_RIGHTS *r_c, prs_struct *ps, int depth)
+{
+ prs_debug(ps, depth, desc, "lsa_io_r_add_acct_rights");
+ depth++;
+
+ if(!prs_ntstatus("status", ps, depth, &r_c->status))
+ return False;
+
+ return True;
+}
+
+
+/*******************************************************************
+ Inits an LSA_Q_REMOVE_ACCT_RIGHTS structure.
+********************************************************************/
+void init_q_remove_acct_rights(LSA_Q_REMOVE_ACCT_RIGHTS *q_q,
+ POLICY_HND *hnd,
+ DOM_SID *sid,
+ uint32 removeall,
+ uint32 count,
+ const char **rights)
+{
+ DEBUG(5, ("init_q_remove_acct_rights\n"));
+
+ q_q->pol = *hnd;
+ init_dom_sid2(&q_q->sid, sid);
+ q_q->removeall = removeall;
+ init_unistr2_array(&q_q->rights, count, rights);
+ q_q->count = 5;
+}
+
+
+/*******************************************************************
+reads or writes a LSA_Q_REMOVE_ACCT_RIGHTS structure.
+********************************************************************/
+BOOL lsa_io_q_remove_acct_rights(const char *desc, LSA_Q_REMOVE_ACCT_RIGHTS *q_q, prs_struct *ps, int depth)
+{
+ prs_debug(ps, depth, desc, "lsa_io_q_remove_acct_rights");
+ depth++;
+
+ if (!smb_io_pol_hnd("", &q_q->pol, ps, depth))
+ return False;
+
+ if(!smb_io_dom_sid2("sid", &q_q->sid, ps, depth))
+ return False;
+
+ if(!prs_uint32("removeall", ps, depth, &q_q->removeall))
+ return False;
+
+ if(!prs_uint32("count", ps, depth, &q_q->rights.count))
+ return False;
+
+ if(!smb_io_unistr2_array("rights", &q_q->rights, ps, depth))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+reads or writes a LSA_R_ENUM_ACCT_RIGHTS structure.
+********************************************************************/
+BOOL lsa_io_r_remove_acct_rights(const char *desc, LSA_R_REMOVE_ACCT_RIGHTS *r_c, prs_struct *ps, int depth)
+{
+ prs_debug(ps, depth, desc, "lsa_io_r_remove_acct_rights");
+ depth++;
+
+ if(!prs_ntstatus("status", ps, depth, &r_c->status))
+ return False;
+
+ return True;
+}
diff --git a/source/rpc_parse/parse_misc.c b/source/rpc_parse/parse_misc.c
new file mode 100644
index 00000000000..cea31c88a80
--- /dev/null
+++ b/source/rpc_parse/parse_misc.c
@@ -0,0 +1,1765 @@
+/*
+ * Unix SMB/CIFS implementation.
+ * RPC Pipe client / server routines
+ * 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 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"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_RPC_PARSE
+
+/****************************************************************************
+ A temporary TALLOC context for things like unistrs, that is valid for
+ the life of a complete RPC call.
+****************************************************************************/
+
+static TALLOC_CTX *current_rpc_talloc = NULL;
+
+static TALLOC_CTX *get_current_rpc_talloc(void)
+{
+ return current_rpc_talloc;
+}
+
+void set_current_rpc_talloc( TALLOC_CTX *ctx)
+{
+ current_rpc_talloc = ctx;
+}
+
+static TALLOC_CTX *main_loop_talloc = NULL;
+
+/*******************************************************************
+free up temporary memory - called from the main loop
+********************************************************************/
+
+void main_loop_talloc_free(void)
+{
+ if (!main_loop_talloc)
+ return;
+ talloc_destroy(main_loop_talloc);
+ main_loop_talloc = NULL;
+}
+
+/*******************************************************************
+ Get a talloc context that is freed in the main loop...
+********************************************************************/
+
+TALLOC_CTX *main_loop_talloc_get(void)
+{
+ if (!main_loop_talloc) {
+ main_loop_talloc = talloc_init("main loop talloc (mainly parse_misc)");
+ if (!main_loop_talloc)
+ smb_panic("main_loop_talloc: malloc fail\n");
+ }
+
+ return main_loop_talloc;
+}
+
+/*******************************************************************
+ Try and get a talloc context. Get the rpc one if possible, else
+ get the main loop one. The main loop one is more dangerous as it
+ goes away between packets, the rpc one will stay around for as long
+ as a current RPC lasts.
+********************************************************************/
+
+TALLOC_CTX *get_talloc_ctx(void)
+{
+ TALLOC_CTX *tc = get_current_rpc_talloc();
+
+ if (tc)
+ return tc;
+ return main_loop_talloc_get();
+}
+
+/*******************************************************************
+ Reads or writes a UTIME type.
+********************************************************************/
+
+static BOOL smb_io_utime(const char *desc, UTIME *t, prs_struct *ps, int depth)
+{
+ if (t == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "smb_io_utime");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32 ("time", ps, depth, &t->time))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ Reads or writes an NTTIME structure.
+********************************************************************/
+
+BOOL smb_io_time(const char *desc, NTTIME *nttime, prs_struct *ps, int depth)
+{
+ if (nttime == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "smb_io_time");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("low ", ps, depth, &nttime->low)) /* low part */
+ return False;
+ if(!prs_uint32("high", ps, depth, &nttime->high)) /* high part */
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ Reads or writes a LOOKUP_LEVEL structure.
+********************************************************************/
+
+BOOL smb_io_lookup_level(const char *desc, LOOKUP_LEVEL *level, prs_struct *ps, int depth)
+{
+ if (level == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "smb_io_lookup_level");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+ if(!prs_uint16("value", ps, depth, &level->value))
+ return False;
+ if(!prs_align(ps))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ Gets an enumeration handle from an ENUM_HND structure.
+********************************************************************/
+
+uint32 get_enum_hnd(ENUM_HND *enh)
+{
+ return (enh && enh->ptr_hnd != 0) ? enh->handle : 0;
+}
+
+/*******************************************************************
+ Inits an ENUM_HND structure.
+********************************************************************/
+
+void init_enum_hnd(ENUM_HND *enh, uint32 hnd)
+{
+ DEBUG(5,("smb_io_enum_hnd\n"));
+
+ enh->ptr_hnd = (hnd != 0) ? 1 : 0;
+ enh->handle = hnd;
+}
+
+/*******************************************************************
+ Reads or writes an ENUM_HND structure.
+********************************************************************/
+
+BOOL smb_io_enum_hnd(const char *desc, ENUM_HND *hnd, prs_struct *ps, int depth)
+{
+ if (hnd == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "smb_io_enum_hnd");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("ptr_hnd", ps, depth, &hnd->ptr_hnd)) /* pointer */
+ return False;
+
+ if (hnd->ptr_hnd != 0) {
+ if(!prs_uint32("handle ", ps, depth, &hnd->handle )) /* enum handle */
+ return False;
+ }
+
+ return True;
+}
+
+/*******************************************************************
+ Reads or writes a DOM_SID structure.
+********************************************************************/
+
+BOOL smb_io_dom_sid(const char *desc, DOM_SID *sid, prs_struct *ps, int depth)
+{
+ int i;
+
+ if (sid == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "smb_io_dom_sid");
+ depth++;
+
+ if(!prs_uint8 ("sid_rev_num", ps, depth, &sid->sid_rev_num))
+ return False;
+
+ if(!prs_uint8 ("num_auths ", ps, depth, &sid->num_auths))
+ return False;
+
+ for (i = 0; i < 6; i++)
+ {
+ fstring tmp;
+ slprintf(tmp, sizeof(tmp) - 1, "id_auth[%d] ", i);
+ if(!prs_uint8 (tmp, ps, depth, &sid->id_auth[i]))
+ return False;
+ }
+
+ /* oops! XXXX should really issue a warning here... */
+ if (sid->num_auths > MAXSUBAUTHS)
+ sid->num_auths = MAXSUBAUTHS;
+
+ if(!prs_uint32s(False, "sub_auths ", ps, depth, sid->sub_auths, sid->num_auths))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ Inits a DOM_SID structure.
+
+ BIG NOTE: this function only does SIDS where the identauth is not >= 2^32
+ identauth >= 2^32 can be detected because it will be specified in hex
+********************************************************************/
+
+void init_dom_sid(DOM_SID *sid, const char *str_sid)
+{
+ pstring domsid;
+ int identauth;
+ char *p;
+
+ if (str_sid == NULL) {
+ DEBUG(4,("netlogon domain SID: none\n"));
+ sid->sid_rev_num = 0;
+ sid->num_auths = 0;
+ return;
+ }
+
+ pstrcpy(domsid, str_sid);
+
+ DEBUG(4,("init_dom_sid %d SID: %s\n", __LINE__, domsid));
+
+ /* assume, but should check, that domsid starts "S-" */
+ p = strtok(domsid+2,"-");
+ sid->sid_rev_num = atoi(p);
+
+ /* identauth in decimal should be < 2^32 */
+ /* identauth in hex should be >= 2^32 */
+ identauth = atoi(strtok(0,"-"));
+
+ DEBUG(4,("netlogon rev %d\n", sid->sid_rev_num));
+ DEBUG(4,("netlogon %s ia %d\n", p, identauth));
+
+ sid->id_auth[0] = 0;
+ sid->id_auth[1] = 0;
+ sid->id_auth[2] = (identauth & 0xff000000) >> 24;
+ sid->id_auth[3] = (identauth & 0x00ff0000) >> 16;
+ sid->id_auth[4] = (identauth & 0x0000ff00) >> 8;
+ sid->id_auth[5] = (identauth & 0x000000ff);
+
+ sid->num_auths = 0;
+
+ while ((p = strtok(0, "-")) != NULL && sid->num_auths < MAXSUBAUTHS)
+ sid->sub_auths[sid->num_auths++] = atoi(p);
+
+ DEBUG(4,("init_dom_sid: %d SID: %s\n", __LINE__, domsid));
+}
+
+/*******************************************************************
+ Inits a DOM_SID2 structure.
+********************************************************************/
+
+void init_dom_sid2(DOM_SID2 *sid2, const DOM_SID *sid)
+{
+ sid2->sid = *sid;
+ sid2->num_auths = sid2->sid.num_auths;
+}
+
+/*******************************************************************
+ Reads or writes a DOM_SID2 structure.
+********************************************************************/
+
+BOOL smb_io_dom_sid2(const char *desc, DOM_SID2 *sid, prs_struct *ps, int depth)
+{
+ if (sid == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "smb_io_dom_sid2");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("num_auths", ps, depth, &sid->num_auths))
+ return False;
+
+ if(!smb_io_dom_sid("sid", &sid->sid, ps, depth))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ Reads or writes a struct uuid
+********************************************************************/
+
+BOOL smb_io_uuid(const char *desc, struct uuid *uuid,
+ prs_struct *ps, int depth)
+{
+ if (uuid == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "smb_io_uuid");
+ depth++;
+
+ if(!prs_uint32 ("data ", ps, depth, &uuid->time_low))
+ return False;
+ if(!prs_uint16 ("data ", ps, depth, &uuid->time_mid))
+ return False;
+ if(!prs_uint16 ("data ", ps, depth, &uuid->time_hi_and_version))
+ return False;
+
+ if(!prs_uint8s (False, "data ", ps, depth, uuid->clock_seq, sizeof(uuid->clock_seq)))
+ return False;
+ if(!prs_uint8s (False, "data ", ps, depth, uuid->node, sizeof(uuid->node)))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+creates a STRHDR structure.
+********************************************************************/
+
+void init_str_hdr(STRHDR *hdr, int max_len, int len, uint32 buffer)
+{
+ hdr->str_max_len = max_len;
+ hdr->str_str_len = len;
+ hdr->buffer = buffer;
+}
+
+/*******************************************************************
+ Reads or writes a STRHDR structure.
+********************************************************************/
+
+BOOL smb_io_strhdr(const char *desc, STRHDR *hdr, prs_struct *ps, int depth)
+{
+ if (hdr == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "smb_io_strhdr");
+ depth++;
+
+ prs_align(ps);
+
+ if(!prs_uint16("str_str_len", ps, depth, &hdr->str_str_len))
+ return False;
+ if(!prs_uint16("str_max_len", ps, depth, &hdr->str_max_len))
+ return False;
+ if(!prs_uint32("buffer ", ps, depth, &hdr->buffer))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ Inits a UNIHDR structure.
+********************************************************************/
+
+void init_uni_hdr(UNIHDR *hdr, UNISTR2 *str2)
+{
+ hdr->uni_str_len = 2 * (str2->uni_str_len);
+ hdr->uni_max_len = 2 * (str2->uni_max_len);
+ hdr->buffer = (str2->uni_str_len != 0) ? 1 : 0;
+}
+
+/*******************************************************************
+ Reads or writes a UNIHDR structure.
+********************************************************************/
+
+BOOL smb_io_unihdr(const char *desc, UNIHDR *hdr, prs_struct *ps, int depth)
+{
+ if (hdr == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "smb_io_unihdr");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint16("uni_str_len", ps, depth, &hdr->uni_str_len))
+ return False;
+ if(!prs_uint16("uni_max_len", ps, depth, &hdr->uni_max_len))
+ return False;
+ if(!prs_uint32("buffer ", ps, depth, &hdr->buffer))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ Inits a BUFHDR structure.
+********************************************************************/
+
+void init_buf_hdr(BUFHDR *hdr, int max_len, int len)
+{
+ hdr->buf_max_len = max_len;
+ hdr->buf_len = len;
+}
+
+/*******************************************************************
+ prs_uint16 wrapper. Call this and it sets up a pointer to where the
+ uint16 should be stored, or gets the size if reading.
+ ********************************************************************/
+
+BOOL smb_io_hdrbuf_pre(const char *desc, BUFHDR *hdr, prs_struct *ps, int depth, uint32 *offset)
+{
+ (*offset) = prs_offset(ps);
+ if (ps->io) {
+
+ /* reading. */
+
+ if(!smb_io_hdrbuf(desc, hdr, ps, depth))
+ return False;
+
+ } else {
+
+ /* writing. */
+
+ if(!prs_set_offset(ps, prs_offset(ps) + (sizeof(uint32) * 2)))
+ return False;
+ }
+
+ return True;
+}
+
+/*******************************************************************
+ smb_io_hdrbuf wrapper. Call this and it retrospectively stores the size.
+ Does nothing on reading, as that is already handled by ...._pre()
+ ********************************************************************/
+
+BOOL smb_io_hdrbuf_post(const char *desc, BUFHDR *hdr, prs_struct *ps, int depth,
+ uint32 ptr_hdrbuf, uint32 max_len, uint32 len)
+{
+ if (!ps->io) {
+ /* writing: go back and do a retrospective job. i hate this */
+
+ uint32 old_offset = prs_offset(ps);
+
+ init_buf_hdr(hdr, max_len, len);
+ if(!prs_set_offset(ps, ptr_hdrbuf))
+ return False;
+ if(!smb_io_hdrbuf(desc, hdr, ps, depth))
+ return False;
+
+ if(!prs_set_offset(ps, old_offset))
+ return False;
+ }
+
+ return True;
+}
+
+/*******************************************************************
+ Reads or writes a BUFHDR structure.
+********************************************************************/
+
+BOOL smb_io_hdrbuf(const char *desc, BUFHDR *hdr, prs_struct *ps, int depth)
+{
+ if (hdr == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "smb_io_hdrbuf");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("buf_max_len", ps, depth, &hdr->buf_max_len))
+ return False;
+ if(!prs_uint32("buf_len ", ps, depth, &hdr->buf_len))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+creates a UNIHDR2 structure.
+********************************************************************/
+
+void init_uni_hdr2(UNIHDR2 *hdr, UNISTR2 *str2)
+{
+ init_uni_hdr(&hdr->unihdr, str2);
+ hdr->buffer = (str2->uni_str_len > 0) ? 1 : 0;
+}
+
+/*******************************************************************
+ Reads or writes a UNIHDR2 structure.
+********************************************************************/
+
+BOOL smb_io_unihdr2(const char *desc, UNIHDR2 *hdr2, prs_struct *ps, int depth)
+{
+ if (hdr2 == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "smb_io_unihdr2");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!smb_io_unihdr("hdr", &hdr2->unihdr, ps, depth))
+ return False;
+ if(!prs_uint32("buffer", ps, depth, &hdr2->buffer))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ Inits a UNISTR structure.
+********************************************************************/
+
+void init_unistr(UNISTR *str, const char *buf)
+{
+ size_t len;
+
+ if (buf == NULL) {
+ str->buffer = NULL;
+ return;
+ }
+
+
+ len = strlen(buf) + 1;
+
+ if (len < MAX_UNISTRLEN)
+ len = MAX_UNISTRLEN;
+ len *= sizeof(uint16);
+
+ str->buffer = (uint16 *)talloc_zero(get_talloc_ctx(), len);
+ if (str->buffer == NULL)
+ smb_panic("init_unistr: malloc fail\n");
+
+ rpcstr_push(str->buffer, buf, len, STR_TERMINATE);
+}
+
+/*******************************************************************
+reads or writes a UNISTR structure.
+XXXX NOTE: UNISTR structures NEED to be null-terminated.
+********************************************************************/
+
+BOOL smb_io_unistr(const char *desc, UNISTR *uni, prs_struct *ps, int depth)
+{
+ if (uni == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "smb_io_unistr");
+ depth++;
+
+ if(!prs_unistr("unistr", ps, depth, uni))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ Allocate the BUFFER3 memory.
+********************************************************************/
+
+static void create_buffer3(BUFFER3 *str, size_t len)
+{
+ if (len < MAX_BUFFERLEN)
+ len = MAX_BUFFERLEN;
+
+ str->buffer = talloc_zero(get_talloc_ctx(), len);
+ if (str->buffer == NULL)
+ smb_panic("create_buffer3: talloc fail\n");
+
+}
+
+/*******************************************************************
+ Inits a BUFFER3 structure from a uint32
+********************************************************************/
+
+void init_buffer3_uint32(BUFFER3 *str, uint32 val)
+{
+ ZERO_STRUCTP(str);
+
+ /* set up string lengths. */
+ str->buf_max_len = sizeof(uint32);
+ str->buf_len = sizeof(uint32);
+
+ create_buffer3(str, sizeof(uint32));
+ SIVAL(str->buffer, 0, val);
+}
+
+/*******************************************************************
+ Inits a BUFFER3 structure.
+********************************************************************/
+
+void init_buffer3_str(BUFFER3 *str, const char *buf, int len)
+{
+ ZERO_STRUCTP(str);
+
+ /* set up string lengths. */
+ str->buf_max_len = len * 2;
+ str->buf_len = len * 2;
+
+ create_buffer3(str, str->buf_max_len);
+
+ rpcstr_push(str->buffer, buf, str->buf_max_len, STR_TERMINATE);
+
+}
+
+/*******************************************************************
+ Inits a BUFFER3 structure from a hex string.
+********************************************************************/
+
+void init_buffer3_hex(BUFFER3 *str, const char *buf)
+{
+ ZERO_STRUCTP(str);
+ create_buffer3(str, strlen(buf));
+ str->buf_max_len = str->buf_len = strhex_to_str((char *)str->buffer, sizeof(str->buffer), buf);
+}
+
+/*******************************************************************
+ Inits a BUFFER3 structure.
+********************************************************************/
+
+void init_buffer3_bytes(BUFFER3 *str, uint8 *buf, int len)
+{
+ ZERO_STRUCTP(str);
+
+ /* max buffer size (allocated size) */
+ str->buf_max_len = len;
+ if (buf != NULL) {
+ create_buffer3(str, len);
+ memcpy(str->buffer, buf, len);
+ }
+ str->buf_len = buf != NULL ? len : 0;
+}
+
+/*******************************************************************
+ Reads or writes a BUFFER3 structure.
+ the uni_max_len member tells you how large the buffer is.
+ the uni_str_len member tells you how much of the buffer is really used.
+********************************************************************/
+
+BOOL smb_io_buffer3(const char *desc, BUFFER3 *buf3, prs_struct *ps, int depth)
+{
+ if (buf3 == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "smb_io_buffer3");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("uni_max_len", ps, depth, &buf3->buf_max_len))
+ return False;
+
+ if (UNMARSHALLING(ps)) {
+ buf3->buffer = (unsigned char *)prs_alloc_mem(ps, buf3->buf_max_len);
+ if (buf3->buffer == NULL)
+ return False;
+ }
+
+ if(!prs_uint8s(True, "buffer ", ps, depth, buf3->buffer, buf3->buf_max_len))
+ return False;
+
+ if(!prs_uint32("buf_len ", ps, depth, &buf3->buf_len))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+reads or writes a BUFFER5 structure.
+the buf_len member tells you how large the buffer is.
+********************************************************************/
+BOOL smb_io_buffer5(const char *desc, BUFFER5 *buf5, prs_struct *ps, int depth)
+{
+ prs_debug(ps, depth, desc, "smb_io_buffer5");
+ depth++;
+
+ if (buf5 == NULL) return False;
+
+ if(!prs_align(ps))
+ return False;
+ if(!prs_uint32("buf_len", ps, depth, &buf5->buf_len))
+ return False;
+
+ if(buf5->buf_len) {
+ if(!prs_buffer5(True, "buffer" , ps, depth, buf5))
+ return False;
+ }
+
+ return True;
+}
+
+/*******************************************************************
+ Inits a BUFFER2 structure.
+********************************************************************/
+
+void init_buffer2(BUFFER2 *str, const uint8 *buf, size_t len)
+{
+ ZERO_STRUCTP(str);
+
+ /* max buffer size (allocated size) */
+ str->buf_max_len = len;
+ str->offset = 0;
+ str->buf_len = buf != NULL ? len : 0;
+
+ if (buf != NULL) {
+ if (len < MAX_BUFFERLEN)
+ len = MAX_BUFFERLEN;
+ str->buffer = talloc_zero(get_talloc_ctx(), len);
+ if (str->buffer == NULL)
+ smb_panic("init_buffer2: talloc fail\n");
+ memcpy(str->buffer, buf, MIN(str->buf_len, len));
+ }
+}
+
+/*******************************************************************
+ Reads or writes a BUFFER2 structure.
+ the uni_max_len member tells you how large the buffer is.
+ the uni_str_len member tells you how much of the buffer is really used.
+********************************************************************/
+
+BOOL smb_io_buffer2(const char *desc, BUFFER2 *buf2, uint32 buffer, prs_struct *ps, int depth)
+{
+ if (buf2 == NULL)
+ return False;
+
+ if (buffer) {
+
+ prs_debug(ps, depth, desc, "smb_io_buffer2");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("uni_max_len", ps, depth, &buf2->buf_max_len))
+ return False;
+ if(!prs_uint32("offset ", ps, depth, &buf2->offset))
+ return False;
+ if(!prs_uint32("buf_len ", ps, depth, &buf2->buf_len))
+ return False;
+
+ /* buffer advanced by indicated length of string
+ NOT by searching for null-termination */
+
+ if(!prs_buffer2(True, "buffer ", ps, depth, buf2))
+ return False;
+
+ } else {
+
+ prs_debug(ps, depth, desc, "smb_io_buffer2 - NULL");
+ depth++;
+ memset((char *)buf2, '\0', sizeof(*buf2));
+
+ }
+ return True;
+}
+
+/*******************************************************************
+creates a UNISTR2 structure: sets up the buffer, too
+********************************************************************/
+
+void init_buf_unistr2(UNISTR2 *str, uint32 *ptr, const char *buf)
+{
+ if (buf != NULL) {
+ *ptr = 1;
+ init_unistr2(str, buf, UNI_STR_TERMINATE);
+ } else {
+ *ptr = 0;
+ init_unistr2(str, NULL, UNI_FLAGS_NONE);
+
+ }
+}
+
+/*******************************************************************
+ Copies a UNISTR2 structure.
+********************************************************************/
+
+void copy_unistr2(UNISTR2 *str, const UNISTR2 *from)
+{
+ str->uni_max_len = from->uni_max_len;
+ str->offset = from->offset;
+ str->uni_str_len = from->uni_str_len;
+
+ if (from->buffer == NULL)
+ return;
+
+ /* the string buffer is allocated to the maximum size
+ (the the length of the source string) to prevent
+ reallocation of memory. */
+ if (str->buffer == NULL) {
+ size_t len = from->uni_max_len * sizeof(uint16);
+
+ if (len < MAX_UNISTRLEN)
+ len = MAX_UNISTRLEN;
+ len *= sizeof(uint16);
+
+ str->buffer = (uint16 *)talloc_zero(get_talloc_ctx(), len);
+ if ((str->buffer == NULL) && (len > 0 )) {
+ smb_panic("copy_unistr2: talloc fail\n");
+ return;
+ }
+ }
+
+ /* copy the string */
+ memcpy(str->buffer, from->buffer, from->uni_max_len*sizeof(uint16));
+}
+
+/*******************************************************************
+ Creates a STRING2 structure.
+********************************************************************/
+
+void init_string2(STRING2 *str, const char *buf, int max_len, int str_len)
+{
+ int alloc_len = 0;
+
+ /* set up string lengths. */
+ str->str_max_len = max_len;
+ str->offset = 0;
+ str->str_str_len = str_len;
+
+ /* store the string */
+ if(str_len != 0) {
+ if (str_len < MAX_STRINGLEN)
+ alloc_len = MAX_STRINGLEN;
+ str->buffer = talloc_zero(get_talloc_ctx(), alloc_len);
+ if (str->buffer == NULL)
+ smb_panic("init_string2: malloc fail\n");
+ memcpy(str->buffer, buf, str_len);
+ }
+}
+
+/*******************************************************************
+ Reads or writes a STRING2 structure.
+ XXXX NOTE: STRING2 structures need NOT be null-terminated.
+ the str_str_len member tells you how long the string is;
+ the str_max_len member tells you how large the buffer is.
+********************************************************************/
+
+BOOL smb_io_string2(const char *desc, STRING2 *str2, uint32 buffer, prs_struct *ps, int depth)
+{
+ if (str2 == NULL)
+ return False;
+
+ if (buffer) {
+
+ prs_debug(ps, depth, desc, "smb_io_string2");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("str_max_len", ps, depth, &str2->str_max_len))
+ return False;
+ if(!prs_uint32("offset ", ps, depth, &str2->offset))
+ return False;
+ if(!prs_uint32("str_str_len", ps, depth, &str2->str_str_len))
+ return False;
+
+ /* buffer advanced by indicated length of string
+ NOT by searching for null-termination */
+ if(!prs_string2(True, "buffer ", ps, depth, str2))
+ return False;
+
+ } else {
+
+ prs_debug(ps, depth, desc, "smb_io_string2 - NULL");
+ depth++;
+ memset((char *)str2, '\0', sizeof(*str2));
+
+ }
+
+ return True;
+}
+
+/*******************************************************************
+ Inits a UNISTR2 structure.
+********************************************************************/
+
+void init_unistr2(UNISTR2 *str, const char *buf, enum unistr2_term_codes flags)
+{
+ size_t len = 0;
+ uint32 num_chars = 0;
+
+ if (buf) {
+ /* We always null terminate the copy. */
+ len = strlen(buf) + 1;
+ }
+
+ if (len < MAX_UNISTRLEN)
+ len = MAX_UNISTRLEN;
+ len *= sizeof(uint16);
+
+ str->buffer = (uint16 *)talloc_zero(get_talloc_ctx(), len);
+ if ((str->buffer == NULL) && (len > 0)) {
+ smb_panic("init_unistr2: malloc fail\n");
+ return;
+ }
+
+ /*
+ * The UNISTR2 must be initialized !!!
+ * jfm, 7/7/2001.
+ */
+ if (buf) {
+ rpcstr_push((char *)str->buffer, buf, len, STR_TERMINATE);
+ num_chars = strlen_w(str->buffer);
+ if (flags == STR_TERMINATE || flags == UNI_MAXLEN_TERMINATE) {
+ num_chars++;
+ }
+ }
+
+ str->uni_max_len = num_chars;
+ str->offset = 0;
+ str->uni_str_len = num_chars;
+ if ( num_chars && ((flags == UNI_MAXLEN_TERMINATE) || (flags == UNI_BROKEN_NON_NULL)) )
+ str->uni_max_len++;
+}
+
+/**
+ * Inits a UNISTR2 structure.
+ * @param ctx talloc context to allocate string on
+ * @param str pointer to string to create
+ * @param buf UCS2 null-terminated buffer to init from
+*/
+
+void init_unistr2_w(TALLOC_CTX *ctx, UNISTR2 *str, const smb_ucs2_t *buf)
+{
+ uint32 len = strlen_w(buf);
+ uint32 max_len = len;
+ uint32 alloc_len;
+
+ ZERO_STRUCTP(str);
+
+ /* set up string lengths. */
+ str->uni_max_len = len;
+ str->offset = 0;
+ str->uni_str_len = len;
+
+ if (max_len < MAX_UNISTRLEN)
+ max_len = MAX_UNISTRLEN;
+
+ alloc_len = (max_len + 1) * sizeof(uint16);
+
+ str->buffer = (uint16 *)talloc_zero(ctx, alloc_len);
+ if ((str->buffer == NULL) && (alloc_len > 0)) {
+ smb_panic("init_unistr2_w: malloc fail\n");
+ return;
+ }
+
+ /*
+ * don't move this test above ! The UNISTR2 must be initialized !!!
+ * jfm, 7/7/2001.
+ */
+ if (buf==NULL)
+ return;
+
+ /* Yes, this is a strncpy( foo, bar, strlen(bar)) - but as
+ long as the buffer above is talloc()ed correctly then this
+ is the correct thing to do */
+ strncpy_w(str->buffer, buf, len + 1);
+}
+
+/*******************************************************************
+ Inits a UNISTR2 structure from a UNISTR
+********************************************************************/
+
+void init_unistr2_from_unistr(UNISTR2 *to, const UNISTR *from)
+{
+ uint32 i;
+
+ /* the destination UNISTR2 should never be NULL.
+ if it is it is a programming error */
+
+ /* if the source UNISTR is NULL, then zero out
+ the destination string and return */
+ ZERO_STRUCTP (to);
+ if ((from == NULL) || (from->buffer == NULL))
+ return;
+
+ /* get the length; UNISTR must be NULL terminated */
+ i = 0;
+ while ((from->buffer)[i]!='\0')
+ i++;
+ i++; /* one more to catch the terminating NULL */
+ /* is this necessary -- jerry? I need to think */
+
+ /* set up string lengths; uni_max_len is set to i+1
+ because we need to account for the final NULL termination */
+ to->uni_max_len = i;
+ to->offset = 0;
+ to->uni_str_len = i;
+
+ /* allocate the space and copy the string buffer */
+ to->buffer = (uint16 *)talloc_zero(get_talloc_ctx(), sizeof(uint16)*(to->uni_str_len));
+ if (to->buffer == NULL)
+ smb_panic("init_unistr2_from_unistr: malloc fail\n");
+ memcpy(to->buffer, from->buffer, to->uni_max_len*sizeof(uint16));
+ return;
+}
+
+/*******************************************************************
+ Inits a UNISTR2 structure from a DATA_BLOB.
+ The length of the data_blob must count the bytes of the buffer.
+ Copies the blob data.
+********************************************************************/
+
+void init_unistr2_from_datablob(UNISTR2 *str, DATA_BLOB *blob)
+{
+ /* Allocs the unistring */
+ init_unistr2(str, NULL, UNI_FLAGS_NONE);
+
+ /* Sets the values */
+ str->uni_str_len = blob->length / sizeof(uint16);
+ str->uni_max_len = str->uni_str_len;
+ str->offset = 0;
+ if (blob->length) {
+ str->buffer = (uint16 *) memdup(blob->data, blob->length);
+ } else {
+ str->buffer = NULL;
+ }
+ if ((str->buffer == NULL) && (blob->length > 0)) {
+ smb_panic("init_unistr2_from_datablob: malloc fail\n");
+ }
+}
+
+/*******************************************************************
+ Reads or writes a UNISTR2 structure.
+ XXXX NOTE: UNISTR2 structures need NOT be null-terminated.
+ the uni_str_len member tells you how long the string is;
+ the uni_max_len member tells you how large the buffer is.
+********************************************************************/
+
+BOOL smb_io_unistr2(const char *desc, UNISTR2 *uni2, uint32 buffer, prs_struct *ps, int depth)
+{
+ if (uni2 == NULL)
+ return False;
+
+ if (buffer) {
+
+ prs_debug(ps, depth, desc, "smb_io_unistr2");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("uni_max_len", ps, depth, &uni2->uni_max_len))
+ return False;
+ if(!prs_uint32("offset ", ps, depth, &uni2->offset))
+ return False;
+ if(!prs_uint32("uni_str_len", ps, depth, &uni2->uni_str_len))
+ return False;
+
+ /* buffer advanced by indicated length of string
+ NOT by searching for null-termination */
+ if(!prs_unistr2(True, "buffer ", ps, depth, uni2))
+ return False;
+
+ } else {
+
+ prs_debug(ps, depth, desc, "smb_io_unistr2 - NULL");
+ depth++;
+ memset((char *)uni2, '\0', sizeof(*uni2));
+
+ }
+
+ return True;
+}
+
+
+/*
+ initialise a UNISTR_ARRAY from a char**
+*/
+BOOL init_unistr2_array(UNISTR2_ARRAY *array,
+ uint32 count, const char **strings)
+{
+ unsigned int i;
+
+ array->count = count;
+ array->ref_id = count?1:0;
+ if (array->count == 0) {
+ return True;
+ }
+
+ array->strings = (UNISTR2_ARRAY_EL *)talloc_zero(get_talloc_ctx(), count * sizeof(UNISTR2_ARRAY_EL));
+ if (!array->strings) {
+ return False;
+ }
+
+ for (i=0;i<count;i++) {
+ init_unistr2(&array->strings[i].string, strings[i], UNI_FLAGS_NONE);
+ array->strings[i].size = array->strings[i].string.uni_max_len*2;
+ array->strings[i].length = array->strings[i].size;
+ array->strings[i].ref_id = 1;
+ }
+
+ return True;
+}
+
+/*******************************************************************
+ Reads or writes a UNISTR2_ARRAY structure.
+********************************************************************/
+BOOL smb_io_unistr2_array(const char *desc, UNISTR2_ARRAY *array, prs_struct *ps, int depth)
+{
+ unsigned int i;
+
+ prs_debug(ps, depth, desc, "smb_io_unistr2_array");
+ depth++;
+
+ if(!prs_uint32("ref_id", ps, depth, &array->ref_id))
+ return False;
+
+ if (! array->ref_id) {
+ return True;
+ }
+
+ if(!prs_uint32("count", ps, depth, &array->count))
+ return False;
+
+ if (array->count == 0) {
+ return True;
+ }
+
+ if (UNMARSHALLING(ps)) {
+ array->strings = talloc_zero(get_talloc_ctx(), array->count * sizeof(array->strings[0]));
+ }
+ if (! array->strings) {
+ return False;
+ }
+
+ for (i=0;i<array->count;i++) {
+ if(!prs_uint16("length", ps, depth, &array->strings[i].length))
+ return False;
+ if(!prs_uint16("size", ps, depth, &array->strings[i].size))
+ return False;
+ if(!prs_uint32("ref_id", ps, depth, &array->strings[i].ref_id))
+ return False;
+ }
+
+ for (i=0;i<array->count;i++) {
+ if (! smb_io_unistr2("string", &array->strings[i].string, array->strings[i].ref_id, ps, depth))
+ return False;
+ }
+
+ return True;
+}
+
+
+/*******************************************************************
+ Inits a DOM_RID2 structure.
+********************************************************************/
+
+void init_dom_rid2(DOM_RID2 *rid2, uint32 rid, uint8 type, uint32 idx)
+{
+ rid2->type = type;
+ rid2->rid = rid;
+ rid2->rid_idx = idx;
+}
+
+/*******************************************************************
+ Reads or writes a DOM_RID2 structure.
+********************************************************************/
+
+BOOL smb_io_dom_rid2(const char *desc, DOM_RID2 *rid2, prs_struct *ps, int depth)
+{
+ if (rid2 == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "smb_io_dom_rid2");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint8("type ", ps, depth, &rid2->type))
+ return False;
+ if(!prs_align(ps))
+ return False;
+ if(!prs_uint32("rid ", ps, depth, &rid2->rid))
+ return False;
+ if(!prs_uint32("rid_idx", ps, depth, &rid2->rid_idx))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+creates a DOM_RID3 structure.
+********************************************************************/
+
+void init_dom_rid3(DOM_RID3 *rid3, uint32 rid, uint8 type)
+{
+ rid3->rid = rid;
+ rid3->type1 = type;
+ rid3->ptr_type = 0x1; /* non-zero, basically. */
+ rid3->type2 = 0x1;
+ rid3->unk = type;
+}
+
+/*******************************************************************
+reads or writes a DOM_RID3 structure.
+********************************************************************/
+
+BOOL smb_io_dom_rid3(const char *desc, DOM_RID3 *rid3, prs_struct *ps, int depth)
+{
+ if (rid3 == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "smb_io_dom_rid3");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("rid ", ps, depth, &rid3->rid))
+ return False;
+ if(!prs_uint32("type1 ", ps, depth, &rid3->type1))
+ return False;
+ if(!prs_uint32("ptr_type", ps, depth, &rid3->ptr_type))
+ return False;
+ if(!prs_uint32("type2 ", ps, depth, &rid3->type2))
+ return False;
+ if(!prs_uint32("unk ", ps, depth, &rid3->unk))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ Inits a DOM_RID4 structure.
+********************************************************************/
+
+void init_dom_rid4(DOM_RID4 *rid4, uint16 unknown, uint16 attr, uint32 rid)
+{
+ rid4->unknown = unknown;
+ rid4->attr = attr;
+ rid4->rid = rid;
+}
+
+/*******************************************************************
+ Inits a DOM_CLNT_SRV structure.
+********************************************************************/
+
+static void init_clnt_srv(DOM_CLNT_SRV *logcln, const char *logon_srv, const char *comp_name)
+{
+ DEBUG(5,("init_clnt_srv: %d\n", __LINE__));
+
+ if (logon_srv != NULL) {
+ logcln->undoc_buffer = 1;
+ init_unistr2(&logcln->uni_logon_srv, logon_srv, UNI_STR_TERMINATE);
+ } else {
+ logcln->undoc_buffer = 0;
+ }
+
+ if (comp_name != NULL) {
+ logcln->undoc_buffer2 = 1;
+ init_unistr2(&logcln->uni_comp_name, comp_name, UNI_STR_TERMINATE);
+ } else {
+ logcln->undoc_buffer2 = 0;
+ }
+}
+
+/*******************************************************************
+ Inits or writes a DOM_CLNT_SRV structure.
+********************************************************************/
+
+static BOOL smb_io_clnt_srv(const char *desc, DOM_CLNT_SRV *logcln, prs_struct *ps, int depth)
+{
+ if (logcln == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "smb_io_clnt_srv");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("undoc_buffer ", ps, depth, &logcln->undoc_buffer))
+ return False;
+
+ if (logcln->undoc_buffer != 0) {
+ if(!smb_io_unistr2("unistr2", &logcln->uni_logon_srv, logcln->undoc_buffer, ps, depth))
+ return False;
+ }
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("undoc_buffer2", ps, depth, &logcln->undoc_buffer2))
+ return False;
+
+ if (logcln->undoc_buffer2 != 0) {
+ if(!smb_io_unistr2("unistr2", &logcln->uni_comp_name, logcln->undoc_buffer2, ps, depth))
+ return False;
+ }
+
+ return True;
+}
+
+/*******************************************************************
+ Inits a DOM_LOG_INFO structure.
+********************************************************************/
+
+void init_log_info(DOM_LOG_INFO *loginfo, const char *logon_srv, const char *acct_name,
+ uint16 sec_chan, const char *comp_name)
+{
+ DEBUG(5,("make_log_info %d\n", __LINE__));
+
+ loginfo->undoc_buffer = 1;
+
+ init_unistr2(&loginfo->uni_logon_srv, logon_srv, UNI_STR_TERMINATE);
+ init_unistr2(&loginfo->uni_acct_name, acct_name, UNI_STR_TERMINATE);
+
+ loginfo->sec_chan = sec_chan;
+
+ init_unistr2(&loginfo->uni_comp_name, comp_name, UNI_STR_TERMINATE);
+}
+
+/*******************************************************************
+ Reads or writes a DOM_LOG_INFO structure.
+********************************************************************/
+
+BOOL smb_io_log_info(const char *desc, DOM_LOG_INFO *loginfo, prs_struct *ps, int depth)
+{
+ if (loginfo == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "smb_io_log_info");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("undoc_buffer", ps, depth, &loginfo->undoc_buffer))
+ return False;
+
+ if(!smb_io_unistr2("unistr2", &loginfo->uni_logon_srv, True, ps, depth))
+ return False;
+ if(!smb_io_unistr2("unistr2", &loginfo->uni_acct_name, True, ps, depth))
+ return False;
+
+ if(!prs_uint16("sec_chan", ps, depth, &loginfo->sec_chan))
+ return False;
+
+ if(!smb_io_unistr2("unistr2", &loginfo->uni_comp_name, True, ps, depth))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ Reads or writes a DOM_CHAL structure.
+********************************************************************/
+
+BOOL smb_io_chal(const char *desc, DOM_CHAL *chal, prs_struct *ps, int depth)
+{
+ if (chal == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "smb_io_chal");
+ depth++;
+
+ if(!prs_uint8s (False, "data", ps, depth, chal->data, 8))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ Reads or writes a DOM_CRED structure.
+********************************************************************/
+
+BOOL smb_io_cred(const char *desc, DOM_CRED *cred, prs_struct *ps, int depth)
+{
+ if (cred == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "smb_io_cred");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!smb_io_chal ("", &cred->challenge, ps, depth))
+ return False;
+
+ if(!smb_io_utime("", &cred->timestamp, ps, depth))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ Inits a DOM_CLNT_INFO2 structure.
+********************************************************************/
+
+void init_clnt_info2(DOM_CLNT_INFO2 *clnt,
+ const char *logon_srv, const char *comp_name,
+ const DOM_CRED *clnt_cred)
+{
+ DEBUG(5,("make_clnt_info: %d\n", __LINE__));
+
+ init_clnt_srv(&clnt->login, logon_srv, comp_name);
+
+ if (clnt_cred != NULL) {
+ clnt->ptr_cred = 1;
+ memcpy(&clnt->cred, clnt_cred, sizeof(clnt->cred));
+ } else {
+ clnt->ptr_cred = 0;
+ }
+}
+
+/*******************************************************************
+ Reads or writes a DOM_CLNT_INFO2 structure.
+********************************************************************/
+
+BOOL smb_io_clnt_info2(const char *desc, DOM_CLNT_INFO2 *clnt, prs_struct *ps, int depth)
+{
+ if (clnt == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "smb_io_clnt_info2");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!smb_io_clnt_srv("", &clnt->login, ps, depth))
+ return False;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("ptr_cred", ps, depth, &clnt->ptr_cred))
+ return False;
+ if(!smb_io_cred("", &clnt->cred, ps, depth))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ Inits a DOM_CLNT_INFO structure.
+********************************************************************/
+
+void init_clnt_info(DOM_CLNT_INFO *clnt,
+ const char *logon_srv, const char *acct_name,
+ uint16 sec_chan, const char *comp_name,
+ const DOM_CRED *cred)
+{
+ DEBUG(5,("make_clnt_info\n"));
+
+ init_log_info(&clnt->login, logon_srv, acct_name, sec_chan, comp_name);
+ memcpy(&clnt->cred, cred, sizeof(clnt->cred));
+}
+
+/*******************************************************************
+ Reads or writes a DOM_CLNT_INFO structure.
+********************************************************************/
+
+BOOL smb_io_clnt_info(const char *desc, DOM_CLNT_INFO *clnt, prs_struct *ps, int depth)
+{
+ if (clnt == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "smb_io_clnt_info");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!smb_io_log_info("", &clnt->login, ps, depth))
+ return False;
+ if(!smb_io_cred("", &clnt->cred, ps, depth))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ Inits a DOM_LOGON_ID structure.
+********************************************************************/
+
+void init_logon_id(DOM_LOGON_ID *logonid, uint32 log_id_low, uint32 log_id_high)
+{
+ DEBUG(5,("make_logon_id: %d\n", __LINE__));
+
+ logonid->low = log_id_low;
+ logonid->high = log_id_high;
+}
+
+/*******************************************************************
+ Reads or writes a DOM_LOGON_ID structure.
+********************************************************************/
+
+BOOL smb_io_logon_id(const char *desc, DOM_LOGON_ID *logonid, prs_struct *ps, int depth)
+{
+ if (logonid == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "smb_io_logon_id");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("low ", ps, depth, &logonid->low ))
+ return False;
+ if(!prs_uint32("high", ps, depth, &logonid->high))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ Inits an OWF_INFO structure.
+********************************************************************/
+
+void init_owf_info(OWF_INFO *hash, const uint8 data[16])
+{
+ DEBUG(5,("init_owf_info: %d\n", __LINE__));
+
+ if (data != NULL)
+ memcpy(hash->data, data, sizeof(hash->data));
+ else
+ memset((char *)hash->data, '\0', sizeof(hash->data));
+}
+
+/*******************************************************************
+ Reads or writes an OWF_INFO structure.
+********************************************************************/
+
+BOOL smb_io_owf_info(const char *desc, OWF_INFO *hash, prs_struct *ps, int depth)
+{
+ if (hash == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "smb_io_owf_info");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint8s (False, "data", ps, depth, hash->data, 16))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ Reads or writes a DOM_GID structure.
+********************************************************************/
+
+BOOL smb_io_gid(const char *desc, DOM_GID *gid, prs_struct *ps, int depth)
+{
+ if (gid == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "smb_io_gid");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("g_rid", ps, depth, &gid->g_rid))
+ return False;
+ if(!prs_uint32("attr ", ps, depth, &gid->attr))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ Reads or writes an POLICY_HND structure.
+********************************************************************/
+
+BOOL smb_io_pol_hnd(const char *desc, POLICY_HND *pol, prs_struct *ps, int depth)
+{
+ if (pol == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "smb_io_pol_hnd");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(UNMARSHALLING(ps))
+ ZERO_STRUCTP(pol);
+
+ if (!prs_uint32("data1", ps, depth, &pol->data1))
+ return False;
+ if (!prs_uint32("data2", ps, depth, &pol->data2))
+ return False;
+ if (!prs_uint16("data3", ps, depth, &pol->data3))
+ return False;
+ if (!prs_uint16("data4", ps, depth, &pol->data4))
+ return False;
+ if(!prs_uint8s (False, "data5", ps, depth, pol->data5, sizeof(pol->data5)))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ Create a UNISTR3.
+********************************************************************/
+
+void init_unistr3(UNISTR3 *str, const char *buf)
+{
+ size_t len;
+
+ if (buf == NULL) {
+ str->uni_str_len=0;
+ str->str.buffer = NULL;
+ return;
+ }
+
+ len = strlen(buf) + 1;
+
+ str->uni_str_len=len;
+
+ if (len < MAX_UNISTRLEN)
+ len = MAX_UNISTRLEN;
+
+ len *= sizeof(uint16);
+
+ str->str.buffer = (uint16 *)talloc_zero(get_talloc_ctx(), len);
+ if (str->str.buffer == NULL)
+ smb_panic("init_unistr3: malloc fail\n");
+
+ rpcstr_push((char *)str->str.buffer, buf, len, STR_TERMINATE);
+}
+
+/*******************************************************************
+ Reads or writes a UNISTR3 structure.
+********************************************************************/
+
+BOOL smb_io_unistr3(const char *desc, UNISTR3 *name, prs_struct *ps, int depth)
+{
+ if (name == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "smb_io_unistr3");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("uni_str_len", ps, depth, &name->uni_str_len))
+ return False;
+
+ /* don't know if len is specified by uni_str_len member... */
+ /* assume unicode string is unicode-null-terminated, instead */
+
+ if(!prs_unistr3(True, "unistr", name, ps, depth))
+ return False;
+
+ return True;
+}
+
+
+/*******************************************************************
+ Stream a uint64_struct
+ ********************************************************************/
+BOOL prs_uint64(const char *name, prs_struct *ps, int depth, UINT64_S *data64)
+{
+ return prs_uint32(name, ps, depth+1, &data64->low) &&
+ prs_uint32(name, ps, depth+1, &data64->high);
+}
+
+/*******************************************************************
+reads or writes a BUFHDR2 structure.
+********************************************************************/
+BOOL smb_io_bufhdr2(const char *desc, BUFHDR2 *hdr, prs_struct *ps, int depth)
+{
+ prs_debug(ps, depth, desc, "smb_io_bufhdr2");
+ depth++;
+
+ prs_align(ps);
+ prs_uint32("info_level", ps, depth, &(hdr->info_level));
+ prs_uint32("length ", ps, depth, &(hdr->length ));
+ prs_uint32("buffer ", ps, depth, &(hdr->buffer ));
+
+ return True;
+}
+
+/*******************************************************************
+reads or writes a BUFFER4 structure.
+********************************************************************/
+BOOL smb_io_buffer4(const char *desc, BUFFER4 *buf4, uint32 buffer, prs_struct *ps, int depth)
+{
+ prs_debug(ps, depth, desc, "smb_io_buffer4");
+ depth++;
+
+ prs_align(ps);
+ prs_uint32("buf_len", ps, depth, &(buf4->buf_len));
+
+ if (buf4->buf_len > MAX_BUFFERLEN)
+ {
+ buf4->buf_len = MAX_BUFFERLEN;
+ }
+
+ prs_uint8s(True, "buffer", ps, depth, buf4->buffer, buf4->buf_len);
+
+ return True;
+}
+
+/*******************************************************************
+creates a UNIHDR structure.
+********************************************************************/
+
+BOOL make_uni_hdr(UNIHDR *hdr, int len)
+{
+ if (hdr == NULL)
+ {
+ return False;
+ }
+ hdr->uni_str_len = 2 * len;
+ hdr->uni_max_len = 2 * len;
+ hdr->buffer = len != 0 ? 1 : 0;
+
+ return True;
+}
+
+/*******************************************************************
+creates a BUFHDR2 structure.
+********************************************************************/
+BOOL make_bufhdr2(BUFHDR2 *hdr, uint32 info_level, uint32 length, uint32 buffer)
+{
+ hdr->info_level = info_level;
+ hdr->length = length;
+ hdr->buffer = buffer;
+
+ return True;
+}
diff --git a/source/rpc_parse/parse_net.c b/source/rpc_parse/parse_net.c
new file mode 100644
index 00000000000..a98738b51f0
--- /dev/null
+++ b/source/rpc_parse/parse_net.c
@@ -0,0 +1,3048 @@
+/*
+ * Unix SMB/CIFS implementation.
+ * RPC Pipe client / server routines
+ * Copyright (C) Andrew Tridgell 1992-1997,
+ * Copyright (C) Luke Kenneth Casson Leighton 1996-1997,
+ * Copyright (C) Paul Ashton 1997.
+ * Copyright (C) Jean François Micouleau 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 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"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_RPC_PARSE
+
+/*******************************************************************
+ Reads or writes a structure.
+********************************************************************/
+
+static BOOL net_io_neg_flags(const char *desc, NEG_FLAGS *neg, prs_struct *ps, int depth)
+{
+ if (neg == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "net_io_neg_flags");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("neg_flags", ps, depth, &neg->neg_flags))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ Inits a NETLOGON_INFO_3 structure.
+********************************************************************/
+
+static void init_netinfo_3(NETLOGON_INFO_3 *info, uint32 flags, uint32 logon_attempts)
+{
+ info->flags = flags;
+ info->logon_attempts = logon_attempts;
+ info->reserved_1 = 0x0;
+ info->reserved_2 = 0x0;
+ info->reserved_3 = 0x0;
+ info->reserved_4 = 0x0;
+ info->reserved_5 = 0x0;
+}
+
+/*******************************************************************
+ Reads or writes a NETLOGON_INFO_3 structure.
+********************************************************************/
+
+static BOOL net_io_netinfo_3(const char *desc, NETLOGON_INFO_3 *info, prs_struct *ps, int depth)
+{
+ if (info == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "net_io_netinfo_3");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("flags ", ps, depth, &info->flags))
+ return False;
+ if(!prs_uint32("logon_attempts", ps, depth, &info->logon_attempts))
+ return False;
+ if(!prs_uint32("reserved_1 ", ps, depth, &info->reserved_1))
+ return False;
+ if(!prs_uint32("reserved_2 ", ps, depth, &info->reserved_2))
+ return False;
+ if(!prs_uint32("reserved_3 ", ps, depth, &info->reserved_3))
+ return False;
+ if(!prs_uint32("reserved_4 ", ps, depth, &info->reserved_4))
+ return False;
+ if(!prs_uint32("reserved_5 ", ps, depth, &info->reserved_5))
+ return False;
+
+ return True;
+}
+
+
+/*******************************************************************
+ Inits a NETLOGON_INFO_1 structure.
+********************************************************************/
+
+static void init_netinfo_1(NETLOGON_INFO_1 *info, uint32 flags, uint32 pdc_status)
+{
+ info->flags = flags;
+ info->pdc_status = pdc_status;
+}
+
+/*******************************************************************
+ Reads or writes a NETLOGON_INFO_1 structure.
+********************************************************************/
+
+static BOOL net_io_netinfo_1(const char *desc, NETLOGON_INFO_1 *info, prs_struct *ps, int depth)
+{
+ if (info == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "net_io_netinfo_1");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("flags ", ps, depth, &info->flags))
+ return False;
+ if(!prs_uint32("pdc_status", ps, depth, &info->pdc_status))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ Inits a NETLOGON_INFO_2 structure.
+********************************************************************/
+
+static void init_netinfo_2(NETLOGON_INFO_2 *info, uint32 flags, uint32 pdc_status,
+ uint32 tc_status, const char *trusted_dc_name)
+{
+ info->flags = flags;
+ info->pdc_status = pdc_status;
+ info->ptr_trusted_dc_name = 1;
+ info->tc_status = tc_status;
+
+ if (trusted_dc_name != NULL)
+ init_unistr2(&info->uni_trusted_dc_name, trusted_dc_name, UNI_STR_TERMINATE);
+ else
+ init_unistr2(&info->uni_trusted_dc_name, "", UNI_STR_TERMINATE);
+}
+
+/*******************************************************************
+ Reads or writes a NETLOGON_INFO_2 structure.
+********************************************************************/
+
+static BOOL net_io_netinfo_2(const char *desc, NETLOGON_INFO_2 *info, prs_struct *ps, int depth)
+{
+ if (info == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "net_io_netinfo_2");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("flags ", ps, depth, &info->flags))
+ return False;
+ if(!prs_uint32("pdc_status ", ps, depth, &info->pdc_status))
+ return False;
+ if(!prs_uint32("ptr_trusted_dc_name", ps, depth, &info->ptr_trusted_dc_name))
+ return False;
+ if(!prs_uint32("tc_status ", ps, depth, &info->tc_status))
+ return False;
+
+ if (info->ptr_trusted_dc_name != 0) {
+ if(!smb_io_unistr2("unistr2", &info->uni_trusted_dc_name, info->ptr_trusted_dc_name, ps, depth))
+ return False;
+ }
+
+ if(!prs_align(ps))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ Reads or writes an NET_Q_LOGON_CTRL2 structure.
+********************************************************************/
+
+BOOL net_io_q_logon_ctrl2(const char *desc, NET_Q_LOGON_CTRL2 *q_l, prs_struct *ps, int depth)
+{
+ if (q_l == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "net_io_q_logon_ctrl2");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("ptr ", ps, depth, &q_l->ptr))
+ return False;
+
+ if(!smb_io_unistr2 ("", &q_l->uni_server_name, q_l->ptr, ps, depth))
+ return False;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("function_code", ps, depth, &q_l->function_code))
+ return False;
+ if(!prs_uint32("query_level ", ps, depth, &q_l->query_level))
+ return False;
+ if(!prs_uint32("switch_value ", ps, depth, &q_l->switch_value))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ Inits an NET_Q_LOGON_CTRL2 structure.
+********************************************************************/
+
+void init_net_q_logon_ctrl2(NET_Q_LOGON_CTRL2 *q_l, const char *srv_name,
+ uint32 query_level)
+{
+ DEBUG(5,("init_q_logon_ctrl2\n"));
+
+ q_l->function_code = 0x01;
+ q_l->query_level = query_level;
+ q_l->switch_value = 0x01;
+
+ init_unistr2(&q_l->uni_server_name, srv_name, UNI_STR_TERMINATE);
+}
+
+/*******************************************************************
+ Inits an NET_R_LOGON_CTRL2 structure.
+********************************************************************/
+
+void init_net_r_logon_ctrl2(NET_R_LOGON_CTRL2 *r_l, uint32 query_level,
+ uint32 flags, uint32 pdc_status,
+ uint32 logon_attempts, uint32 tc_status,
+ const char *trusted_domain_name)
+{
+ DEBUG(5,("init_r_logon_ctrl2\n"));
+
+ r_l->switch_value = query_level; /* should only be 0x1 */
+
+ switch (query_level) {
+ case 1:
+ r_l->ptr = 1; /* undocumented pointer */
+ init_netinfo_1(&r_l->logon.info1, flags, pdc_status);
+ r_l->status = NT_STATUS_OK;
+ break;
+ case 2:
+ r_l->ptr = 1; /* undocumented pointer */
+ init_netinfo_2(&r_l->logon.info2, flags, pdc_status,
+ tc_status, trusted_domain_name);
+ r_l->status = NT_STATUS_OK;
+ break;
+ case 3:
+ r_l->ptr = 1; /* undocumented pointer */
+ init_netinfo_3(&r_l->logon.info3, flags, logon_attempts);
+ r_l->status = NT_STATUS_OK;
+ break;
+ default:
+ DEBUG(2,("init_r_logon_ctrl2: unsupported switch value %d\n",
+ r_l->switch_value));
+ r_l->ptr = 0; /* undocumented pointer */
+
+ /* take a guess at an error code... */
+ r_l->status = NT_STATUS_INVALID_INFO_CLASS;
+ break;
+ }
+}
+
+/*******************************************************************
+ Reads or writes an NET_R_LOGON_CTRL2 structure.
+********************************************************************/
+
+BOOL net_io_r_logon_ctrl2(const char *desc, NET_R_LOGON_CTRL2 *r_l, prs_struct *ps, int depth)
+{
+ if (r_l == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "net_io_r_logon_ctrl2");
+ depth++;
+
+ if(!prs_uint32("switch_value ", ps, depth, &r_l->switch_value))
+ return False;
+ if(!prs_uint32("ptr ", ps, depth, &r_l->ptr))
+ return False;
+
+ if (r_l->ptr != 0) {
+ switch (r_l->switch_value) {
+ case 1:
+ if(!net_io_netinfo_1("", &r_l->logon.info1, ps, depth))
+ return False;
+ break;
+ case 2:
+ if(!net_io_netinfo_2("", &r_l->logon.info2, ps, depth))
+ return False;
+ break;
+ case 3:
+ if(!net_io_netinfo_3("", &r_l->logon.info3, ps, depth))
+ return False;
+ break;
+ default:
+ DEBUG(2,("net_io_r_logon_ctrl2: unsupported switch value %d\n",
+ r_l->switch_value));
+ break;
+ }
+ }
+
+ if(!prs_ntstatus("status ", ps, depth, &r_l->status))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ Reads or writes an NET_Q_LOGON_CTRL structure.
+********************************************************************/
+
+BOOL net_io_q_logon_ctrl(const char *desc, NET_Q_LOGON_CTRL *q_l, prs_struct *ps,
+ int depth)
+{
+ prs_debug(ps, depth, desc, "net_io_q_logon_ctrl");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("ptr ", ps, depth, &q_l->ptr))
+ return False;
+
+ if(!smb_io_unistr2 ("", &q_l->uni_server_name, q_l->ptr, ps, depth))
+ return False;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("function_code", ps, depth, &q_l->function_code))
+ return False;
+ if(!prs_uint32("query_level ", ps, depth, &q_l->query_level))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ Inits an NET_Q_LOGON_CTRL structure.
+********************************************************************/
+
+void init_net_q_logon_ctrl(NET_Q_LOGON_CTRL *q_l, const char *srv_name,
+ uint32 query_level)
+{
+ DEBUG(5,("init_q_logon_ctrl\n"));
+
+ q_l->function_code = 0x01; /* ??? */
+ q_l->query_level = query_level;
+
+ init_unistr2(&q_l->uni_server_name, srv_name, UNI_STR_TERMINATE);
+}
+
+/*******************************************************************
+ Inits an NET_R_LOGON_CTRL structure.
+********************************************************************/
+
+void init_net_r_logon_ctrl(NET_R_LOGON_CTRL *r_l, uint32 query_level,
+ uint32 flags, uint32 pdc_status)
+{
+ DEBUG(5,("init_r_logon_ctrl\n"));
+
+ r_l->switch_value = query_level; /* should only be 0x1 */
+
+ switch (query_level) {
+ case 1:
+ r_l->ptr = 1; /* undocumented pointer */
+ init_netinfo_1(&r_l->logon.info1, flags, pdc_status);
+ r_l->status = NT_STATUS_OK;
+ break;
+ default:
+ DEBUG(2,("init_r_logon_ctrl: unsupported switch value %d\n",
+ r_l->switch_value));
+ r_l->ptr = 0; /* undocumented pointer */
+
+ /* take a guess at an error code... */
+ r_l->status = NT_STATUS_INVALID_INFO_CLASS;
+ break;
+ }
+}
+
+/*******************************************************************
+ Reads or writes an NET_R_LOGON_CTRL structure.
+********************************************************************/
+
+BOOL net_io_r_logon_ctrl(const char *desc, NET_R_LOGON_CTRL *r_l, prs_struct *ps,
+ int depth)
+{
+ prs_debug(ps, depth, desc, "net_io_r_logon_ctrl");
+ depth++;
+
+ if(!prs_uint32("switch_value ", ps, depth, &r_l->switch_value))
+ return False;
+ if(!prs_uint32("ptr ", ps, depth, &r_l->ptr))
+ return False;
+
+ if (r_l->ptr != 0) {
+ switch (r_l->switch_value) {
+ case 1:
+ if(!net_io_netinfo_1("", &r_l->logon.info1, ps, depth))
+ return False;
+ break;
+ default:
+ DEBUG(2,("net_io_r_logon_ctrl: unsupported switch value %d\n",
+ r_l->switch_value));
+ break;
+ }
+ }
+
+ if(!prs_ntstatus("status ", ps, depth, &r_l->status))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ Inits an NET_R_GETDCNAME structure.
+********************************************************************/
+void init_net_q_getdcname(NET_Q_GETDCNAME *r_t, const char *logon_server,
+ const char *domainname)
+{
+ DEBUG(5,("init_r_getdcname\n"));
+
+ r_t->ptr_logon_server = (logon_server != NULL);
+ init_unistr2(&r_t->uni_logon_server, logon_server, UNI_STR_TERMINATE);
+ r_t->ptr_domainname = (domainname != NULL);
+ init_unistr2(&r_t->uni_domainname, domainname, UNI_STR_TERMINATE);
+}
+
+/*******************************************************************
+ Reads or writes an NET_Q_GETDCNAME structure.
+********************************************************************/
+
+BOOL net_io_q_getdcname(const char *desc, NET_Q_GETDCNAME *r_t, prs_struct *ps,
+ int depth)
+{
+ if (r_t == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "net_io_q_getdcname");
+ depth++;
+
+ if (!prs_uint32("ptr_logon_server", ps, depth, &r_t->ptr_logon_server))
+ return False;
+
+ if (!smb_io_unistr2("logon_server", &r_t->uni_logon_server,
+ r_t->ptr_logon_server, ps, depth))
+ return False;
+
+ if (!prs_align(ps))
+ return False;
+
+ if (!prs_uint32("ptr_domainname", ps, depth, &r_t->ptr_domainname))
+ return False;
+
+ if (!smb_io_unistr2("domainname", &r_t->uni_domainname,
+ r_t->ptr_domainname, ps, depth))
+ return False;
+
+ return True;
+}
+
+
+/*******************************************************************
+ Inits an NET_R_GETDCNAME structure.
+********************************************************************/
+void init_net_r_getdcname(NET_R_GETDCNAME *r_t, const char *dcname)
+{
+ DEBUG(5,("init_r_getdcname\n"));
+
+ init_unistr2(&r_t->uni_dcname, dcname, UNI_STR_TERMINATE);
+}
+
+/*******************************************************************
+ Reads or writes an NET_R_GETDCNAME structure.
+********************************************************************/
+
+BOOL net_io_r_getdcname(const char *desc, NET_R_GETDCNAME *r_t, prs_struct *ps,
+ int depth)
+{
+ if (r_t == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "net_io_r_getdcname");
+ depth++;
+
+ if (!prs_uint32("ptr_dcname", ps, depth, &r_t->ptr_dcname))
+ return False;
+
+ if (!smb_io_unistr2("dcname", &r_t->uni_dcname,
+ r_t->ptr_dcname, ps, depth))
+ return False;
+
+ if (!prs_ntstatus("status", ps, depth, &r_t->status))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ Inits an NET_R_TRUST_DOM_LIST structure.
+********************************************************************/
+
+void init_r_trust_dom(NET_R_TRUST_DOM_LIST *r_t,
+ uint32 num_doms, const char *dom_name)
+{
+ unsigned int i = 0;
+
+ DEBUG(5,("init_r_trust_dom\n"));
+
+ for (i = 0; i < MAX_TRUST_DOMS; i++) {
+ r_t->uni_trust_dom_name[i].uni_str_len = 0;
+ r_t->uni_trust_dom_name[i].uni_max_len = 0;
+ }
+ if (num_doms > MAX_TRUST_DOMS)
+ num_doms = MAX_TRUST_DOMS;
+
+ for (i = 0; i < num_doms; i++) {
+ fstring domain_name;
+ fstrcpy(domain_name, dom_name);
+ strupper_m(domain_name);
+ init_unistr2(&r_t->uni_trust_dom_name[i], domain_name, UNI_STR_TERMINATE);
+ /* the use of UNISTR2 here is non-standard. */
+ r_t->uni_trust_dom_name[i].offset = 0x1;
+ }
+
+ r_t->status = NT_STATUS_OK;
+}
+
+/*******************************************************************
+ Reads or writes an NET_R_TRUST_DOM_LIST structure.
+********************************************************************/
+
+BOOL net_io_r_trust_dom(const char *desc, NET_R_TRUST_DOM_LIST *r_t, prs_struct *ps, int depth)
+{
+ uint32 value;
+
+ if (r_t == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "net_io_r_trust_dom");
+ depth++;
+
+ /* temporary code to give a valid response */
+ value=2;
+ if(!prs_uint32("status", ps, depth, &value))
+ return False;
+
+ value=1;
+ if(!prs_uint32("status", ps, depth, &value))
+ return False;
+ value=2;
+ if(!prs_uint32("status", ps, depth, &value))
+ return False;
+
+ value=0;
+ if(!prs_uint32("status", ps, depth, &value))
+ return False;
+
+ value=0;
+ if(!prs_uint32("status", ps, depth, &value))
+ return False;
+
+/* old non working code */
+#if 0
+ int i;
+
+ for (i = 0; i < MAX_TRUST_DOMS; i++) {
+ if (r_t->uni_trust_dom_name[i].uni_str_len == 0)
+ break;
+ if(!smb_io_unistr2("", &r_t->uni_trust_dom_name[i], True, ps, depth))
+ return False;
+ }
+
+ if(!prs_ntstatus("status", ps, depth, &r_t->status))
+ return False;
+#endif
+ return True;
+}
+
+
+/*******************************************************************
+ Reads or writes an NET_Q_TRUST_DOM_LIST structure.
+********************************************************************/
+
+BOOL net_io_q_trust_dom(const char *desc, NET_Q_TRUST_DOM_LIST *q_l, prs_struct *ps, int depth)
+{
+ if (q_l == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "net_io_q_trust_dom");
+ depth++;
+
+ if(!prs_uint32("ptr ", ps, depth, &q_l->ptr))
+ return False;
+ if(!smb_io_unistr2 ("", &q_l->uni_server_name, q_l->ptr, ps, depth))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ Inits an NET_Q_REQ_CHAL structure.
+********************************************************************/
+
+void init_q_req_chal(NET_Q_REQ_CHAL *q_c,
+ const char *logon_srv, const char *logon_clnt,
+ DOM_CHAL *clnt_chal)
+{
+ DEBUG(5,("init_q_req_chal: %d\n", __LINE__));
+
+ q_c->undoc_buffer = 1; /* don't know what this buffer is */
+
+ init_unistr2(&q_c->uni_logon_srv, logon_srv , UNI_STR_TERMINATE);
+ init_unistr2(&q_c->uni_logon_clnt, logon_clnt, UNI_STR_TERMINATE);
+
+ memcpy(q_c->clnt_chal.data, clnt_chal->data, sizeof(clnt_chal->data));
+
+ DEBUG(5,("init_q_req_chal: %d\n", __LINE__));
+}
+
+/*******************************************************************
+ Reads or writes an NET_Q_REQ_CHAL structure.
+********************************************************************/
+
+BOOL net_io_q_req_chal(const char *desc, NET_Q_REQ_CHAL *q_c, prs_struct *ps, int depth)
+{
+ if (q_c == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "net_io_q_req_chal");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("undoc_buffer", ps, depth, &q_c->undoc_buffer))
+ return False;
+
+ if(!smb_io_unistr2("", &q_c->uni_logon_srv, True, ps, depth)) /* logon server unicode string */
+ return False;
+ if(!smb_io_unistr2("", &q_c->uni_logon_clnt, True, ps, depth)) /* logon client unicode string */
+ return False;
+
+ if(!smb_io_chal("", &q_c->clnt_chal, ps, depth))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ Reads or writes a structure.
+********************************************************************/
+
+BOOL net_io_r_req_chal(const char *desc, NET_R_REQ_CHAL *r_c, prs_struct *ps, int depth)
+{
+ if (r_c == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "net_io_r_req_chal");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!smb_io_chal("", &r_c->srv_chal, ps, depth)) /* server challenge */
+ return False;
+
+ if(!prs_ntstatus("status", ps, depth, &r_c->status))
+ return False;
+
+ return True;
+}
+
+
+/*******************************************************************
+ Reads or writes a structure.
+********************************************************************/
+
+BOOL net_io_q_auth(const char *desc, NET_Q_AUTH *q_a, prs_struct *ps, int depth)
+{
+ if (q_a == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "net_io_q_auth");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!smb_io_log_info ("", &q_a->clnt_id, ps, depth)) /* client identification info */
+ return False;
+ if(!smb_io_chal("", &q_a->clnt_chal, ps, depth))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ Reads or writes a structure.
+********************************************************************/
+
+BOOL net_io_r_auth(const char *desc, NET_R_AUTH *r_a, prs_struct *ps, int depth)
+{
+ if (r_a == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "net_io_r_auth");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!smb_io_chal("", &r_a->srv_chal, ps, depth)) /* server challenge */
+ return False;
+
+ if(!prs_ntstatus("status", ps, depth, &r_a->status))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ Inits a NET_Q_AUTH_2 struct.
+********************************************************************/
+
+void init_q_auth_2(NET_Q_AUTH_2 *q_a,
+ const char *logon_srv, const char *acct_name, uint16 sec_chan, const char *comp_name,
+ DOM_CHAL *clnt_chal, uint32 clnt_flgs)
+{
+ DEBUG(5,("init_q_auth_2: %d\n", __LINE__));
+
+ init_log_info(&q_a->clnt_id, logon_srv, acct_name, sec_chan, comp_name);
+ memcpy(q_a->clnt_chal.data, clnt_chal->data, sizeof(clnt_chal->data));
+ q_a->clnt_flgs.neg_flags = clnt_flgs;
+
+ DEBUG(5,("init_q_auth_2: %d\n", __LINE__));
+}
+
+/*******************************************************************
+ Reads or writes a structure.
+********************************************************************/
+
+BOOL net_io_q_auth_2(const char *desc, NET_Q_AUTH_2 *q_a, prs_struct *ps, int depth)
+{
+ if (q_a == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "net_io_q_auth_2");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!smb_io_log_info ("", &q_a->clnt_id, ps, depth)) /* client identification info */
+ return False;
+ if(!smb_io_chal("", &q_a->clnt_chal, ps, depth))
+ return False;
+ if(!net_io_neg_flags("", &q_a->clnt_flgs, ps, depth))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ Reads or writes a structure.
+********************************************************************/
+
+BOOL net_io_r_auth_2(const char *desc, NET_R_AUTH_2 *r_a, prs_struct *ps, int depth)
+{
+ if (r_a == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "net_io_r_auth_2");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!smb_io_chal("", &r_a->srv_chal, ps, depth)) /* server challenge */
+ return False;
+ if(!net_io_neg_flags("", &r_a->srv_flgs, ps, depth))
+ return False;
+
+ if(!prs_ntstatus("status", ps, depth, &r_a->status))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ Inits a NET_Q_AUTH_3 struct.
+********************************************************************/
+
+void init_q_auth_3(NET_Q_AUTH_3 *q_a,
+ const char *logon_srv, const char *acct_name, uint16 sec_chan, const char *comp_name,
+ DOM_CHAL *clnt_chal, uint32 clnt_flgs)
+{
+ DEBUG(5,("init_q_auth_3: %d\n", __LINE__));
+
+ init_log_info(&q_a->clnt_id, logon_srv, acct_name, sec_chan, comp_name);
+ memcpy(q_a->clnt_chal.data, clnt_chal->data, sizeof(clnt_chal->data));
+ q_a->clnt_flgs.neg_flags = clnt_flgs;
+
+ DEBUG(5,("init_q_auth_3: %d\n", __LINE__));
+}
+
+/*******************************************************************
+ Reads or writes a structure.
+********************************************************************/
+
+BOOL net_io_q_auth_3(const char *desc, NET_Q_AUTH_3 *q_a, prs_struct *ps, int depth)
+{
+ if (q_a == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "net_io_q_auth_3");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!smb_io_log_info ("", &q_a->clnt_id, ps, depth)) /* client identification info */
+ return False;
+ if(!smb_io_chal("", &q_a->clnt_chal, ps, depth))
+ return False;
+ if(!net_io_neg_flags("", &q_a->clnt_flgs, ps, depth))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ Reads or writes a structure.
+********************************************************************/
+
+BOOL net_io_r_auth_3(const char *desc, NET_R_AUTH_3 *r_a, prs_struct *ps, int depth)
+{
+ if (r_a == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "net_io_r_auth_3");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!smb_io_chal("srv_chal", &r_a->srv_chal, ps, depth)) /* server challenge */
+ return False;
+ if(!net_io_neg_flags("srv_flgs", &r_a->srv_flgs, ps, depth))
+ return False;
+ if (!prs_uint32("unknown", ps, depth, &r_a->unknown))
+ return False;
+
+ if(!prs_ntstatus("status", ps, depth, &r_a->status))
+ return False;
+
+ return True;
+}
+
+
+/*******************************************************************
+ Inits a NET_Q_SRV_PWSET.
+********************************************************************/
+
+void init_q_srv_pwset(NET_Q_SRV_PWSET *q_s,
+ const char *logon_srv, const char *sess_key, const char *acct_name,
+ uint16 sec_chan, const char *comp_name,
+ DOM_CRED *cred, uchar hashed_mach_pwd[16])
+{
+ unsigned char nt_cypher[16];
+
+ DEBUG(5,("init_q_srv_pwset\n"));
+
+ /* Process the new password. */
+ cred_hash3( nt_cypher, hashed_mach_pwd, (const unsigned char *)sess_key, 1);
+
+ init_clnt_info(&q_s->clnt_id, logon_srv, acct_name, sec_chan, comp_name, cred);
+
+ memcpy(q_s->pwd, nt_cypher, sizeof(q_s->pwd));
+}
+
+/*******************************************************************
+ Reads or writes a structure.
+********************************************************************/
+
+BOOL net_io_q_srv_pwset(const char *desc, NET_Q_SRV_PWSET *q_s, prs_struct *ps, int depth)
+{
+ if (q_s == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "net_io_q_srv_pwset");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!smb_io_clnt_info("", &q_s->clnt_id, ps, depth)) /* client identification/authentication info */
+ return False;
+ if(!prs_uint8s (False, "pwd", ps, depth, q_s->pwd, 16)) /* new password - undocumented */
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ Reads or writes a structure.
+********************************************************************/
+
+BOOL net_io_r_srv_pwset(const char *desc, NET_R_SRV_PWSET *r_s, prs_struct *ps, int depth)
+{
+ if (r_s == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "net_io_r_srv_pwset");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!smb_io_cred("", &r_s->srv_cred, ps, depth)) /* server challenge */
+ return False;
+
+ if(!prs_ntstatus("status", ps, depth, &r_s->status))
+ return False;
+
+ return True;
+}
+
+/*************************************************************************
+ Init DOM_SID2 array from a string containing multiple sids
+ *************************************************************************/
+
+static int init_dom_sid2s(TALLOC_CTX *ctx, const char *sids_str, DOM_SID2 **ppsids)
+{
+ const char *ptr;
+ pstring s2;
+ int count = 0;
+
+ DEBUG(4,("init_dom_sid2s: %s\n", sids_str ? sids_str:""));
+
+ *ppsids = NULL;
+
+ if(sids_str) {
+ int number;
+ DOM_SID2 *sids;
+
+ /* Count the number of valid SIDs. */
+ for (count = 0, ptr = sids_str; next_token(&ptr, s2, NULL, sizeof(s2)); ) {
+ DOM_SID tmpsid;
+ if (string_to_sid(&tmpsid, s2))
+ count++;
+ }
+
+ /* Now allocate space for them. */
+ *ppsids = (DOM_SID2 *)talloc_zero(ctx, count * sizeof(DOM_SID2));
+ if (*ppsids == NULL)
+ return 0;
+
+ sids = *ppsids;
+
+ for (number = 0, ptr = sids_str; next_token(&ptr, s2, NULL, sizeof(s2)); ) {
+ DOM_SID tmpsid;
+ if (string_to_sid(&tmpsid, s2)) {
+ /* count only valid sids */
+ init_dom_sid2(&sids[number], &tmpsid);
+ number++;
+ }
+ }
+ }
+
+ return count;
+}
+
+/*******************************************************************
+ Inits a NET_ID_INFO_1 structure.
+********************************************************************/
+
+void init_id_info1(NET_ID_INFO_1 *id, const char *domain_name,
+ uint32 param_ctrl, uint32 log_id_low, uint32 log_id_high,
+ const char *user_name, const char *wksta_name,
+ const char *sess_key,
+ unsigned char lm_cypher[16], unsigned char nt_cypher[16])
+{
+ unsigned char lm_owf[16];
+ unsigned char nt_owf[16];
+
+ DEBUG(5,("init_id_info1: %d\n", __LINE__));
+
+ id->ptr_id_info1 = 1;
+
+ id->param_ctrl = param_ctrl;
+ init_logon_id(&id->logon_id, log_id_low, log_id_high);
+
+
+ if (lm_cypher && nt_cypher) {
+ unsigned char key[16];
+#ifdef DEBUG_PASSWORD
+ DEBUG(100,("lm cypher:"));
+ dump_data(100, (char *)lm_cypher, 16);
+
+ DEBUG(100,("nt cypher:"));
+ dump_data(100, (char *)nt_cypher, 16);
+#endif
+
+ memset(key, 0, 16);
+ memcpy(key, sess_key, 8);
+
+ memcpy(lm_owf, lm_cypher, 16);
+ SamOEMhash(lm_owf, key, 16);
+ memcpy(nt_owf, nt_cypher, 16);
+ SamOEMhash(nt_owf, key, 16);
+
+#ifdef DEBUG_PASSWORD
+ DEBUG(100,("encrypt of lm owf password:"));
+ dump_data(100, (char *)lm_owf, 16);
+
+ DEBUG(100,("encrypt of nt owf password:"));
+ dump_data(100, (char *)nt_owf, 16);
+#endif
+ /* set up pointers to cypher blocks */
+ lm_cypher = lm_owf;
+ nt_cypher = nt_owf;
+ }
+
+ init_owf_info(&id->lm_owf, lm_cypher);
+ init_owf_info(&id->nt_owf, nt_cypher);
+
+ init_unistr2(&id->uni_domain_name, domain_name, UNI_FLAGS_NONE);
+ init_uni_hdr(&id->hdr_domain_name, &id->uni_domain_name);
+ init_unistr2(&id->uni_user_name, user_name, UNI_FLAGS_NONE);
+ init_uni_hdr(&id->hdr_user_name, &id->uni_user_name);
+ init_unistr2(&id->uni_wksta_name, wksta_name, UNI_FLAGS_NONE);
+ init_uni_hdr(&id->hdr_wksta_name, &id->uni_wksta_name);
+}
+
+/*******************************************************************
+ Reads or writes an NET_ID_INFO_1 structure.
+********************************************************************/
+
+static BOOL net_io_id_info1(const char *desc, NET_ID_INFO_1 *id, prs_struct *ps, int depth)
+{
+ if (id == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "net_io_id_info1");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("ptr_id_info1", ps, depth, &id->ptr_id_info1))
+ return False;
+
+ if (id->ptr_id_info1 != 0) {
+ if(!smb_io_unihdr("unihdr", &id->hdr_domain_name, ps, depth))
+ return False;
+
+ if(!prs_uint32("param_ctrl", ps, depth, &id->param_ctrl))
+ return False;
+ if(!smb_io_logon_id("", &id->logon_id, ps, depth))
+ return False;
+
+ if(!smb_io_unihdr("unihdr", &id->hdr_user_name, ps, depth))
+ return False;
+ if(!smb_io_unihdr("unihdr", &id->hdr_wksta_name, ps, depth))
+ return False;
+
+ if(!smb_io_owf_info("", &id->lm_owf, ps, depth))
+ return False;
+ if(!smb_io_owf_info("", &id->nt_owf, ps, depth))
+ return False;
+
+ if(!smb_io_unistr2("unistr2", &id->uni_domain_name,
+ id->hdr_domain_name.buffer, ps, depth))
+ return False;
+ if(!smb_io_unistr2("unistr2", &id->uni_user_name,
+ id->hdr_user_name.buffer, ps, depth))
+ return False;
+ if(!smb_io_unistr2("unistr2", &id->uni_wksta_name,
+ id->hdr_wksta_name.buffer, ps, depth))
+ return False;
+ }
+
+ return True;
+}
+
+/*******************************************************************
+Inits a NET_ID_INFO_2 structure.
+
+This is a network logon packet. The log_id parameters
+are what an NT server would generate for LUID once the
+user is logged on. I don't think we care about them.
+
+Note that this has no access to the NT and LM hashed passwords,
+so it forwards the challenge, and the NT and LM responses (24
+bytes each) over the secure channel to the Domain controller
+for it to say yea or nay. This is the preferred method of
+checking for a logon as it doesn't export the password
+hashes to anyone who has compromised the secure channel. JRA.
+********************************************************************/
+
+void init_id_info2(NET_ID_INFO_2 * id, const char *domain_name,
+ uint32 param_ctrl,
+ uint32 log_id_low, uint32 log_id_high,
+ const char *user_name, const char *wksta_name,
+ const uchar lm_challenge[8],
+ const uchar * lm_chal_resp, size_t lm_chal_resp_len,
+ const uchar * nt_chal_resp, size_t nt_chal_resp_len)
+{
+ unsigned char lm_owf[24];
+ unsigned char nt_owf[128];
+
+ DEBUG(5,("init_id_info2: %d\n", __LINE__));
+
+ id->ptr_id_info2 = 1;
+
+
+ id->param_ctrl = param_ctrl;
+ init_logon_id(&id->logon_id, log_id_low, log_id_high);
+
+ if (nt_chal_resp) {
+ /* oops. can only send what-ever-it-is direct */
+ memcpy(nt_owf, nt_chal_resp, MIN(sizeof(nt_owf), nt_chal_resp_len));
+ nt_chal_resp = nt_owf;
+ }
+ if (lm_chal_resp) {
+ /* oops. can only send what-ever-it-is direct */
+ memcpy(lm_owf, lm_chal_resp, MIN(sizeof(lm_owf), lm_chal_resp_len));
+ lm_chal_resp = lm_owf;
+ }
+
+ memcpy(id->lm_chal, lm_challenge, sizeof(id->lm_chal));
+ init_str_hdr(&id->hdr_nt_chal_resp, nt_chal_resp_len, nt_chal_resp_len, (nt_chal_resp != NULL) ? 1 : 0);
+ init_str_hdr(&id->hdr_lm_chal_resp, lm_chal_resp_len, lm_chal_resp_len, (lm_chal_resp != NULL) ? 1 : 0);
+
+ init_unistr2(&id->uni_domain_name, domain_name, UNI_FLAGS_NONE);
+ init_uni_hdr(&id->hdr_domain_name, &id->uni_domain_name);
+ init_unistr2(&id->uni_user_name, user_name, UNI_FLAGS_NONE);
+ init_uni_hdr(&id->hdr_user_name, &id->uni_user_name);
+ init_unistr2(&id->uni_wksta_name, wksta_name, UNI_FLAGS_NONE);
+ init_uni_hdr(&id->hdr_wksta_name, &id->uni_wksta_name);
+
+ init_string2(&id->nt_chal_resp, (const char *)nt_chal_resp, nt_chal_resp_len, nt_chal_resp_len);
+ init_string2(&id->lm_chal_resp, (const char *)lm_chal_resp, lm_chal_resp_len, lm_chal_resp_len);
+
+}
+
+/*******************************************************************
+ Reads or writes an NET_ID_INFO_2 structure.
+********************************************************************/
+
+static BOOL net_io_id_info2(const char *desc, NET_ID_INFO_2 *id, prs_struct *ps, int depth)
+{
+ if (id == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "net_io_id_info2");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("ptr_id_info2", ps, depth, &id->ptr_id_info2))
+ return False;
+
+ if (id->ptr_id_info2 != 0) {
+ if(!smb_io_unihdr("unihdr", &id->hdr_domain_name, ps, depth))
+ return False;
+
+ if(!prs_uint32("param_ctrl", ps, depth, &id->param_ctrl))
+ return False;
+ if(!smb_io_logon_id("", &id->logon_id, ps, depth))
+ return False;
+
+ if(!smb_io_unihdr("unihdr", &id->hdr_user_name, ps, depth))
+ return False;
+ if(!smb_io_unihdr("unihdr", &id->hdr_wksta_name, ps, depth))
+ return False;
+
+ if(!prs_uint8s (False, "lm_chal", ps, depth, id->lm_chal, 8)) /* lm 8 byte challenge */
+ return False;
+
+ if(!smb_io_strhdr("hdr_nt_chal_resp", &id->hdr_nt_chal_resp, ps, depth))
+ return False;
+ if(!smb_io_strhdr("hdr_lm_chal_resp", &id->hdr_lm_chal_resp, ps, depth))
+ return False;
+
+ if(!smb_io_unistr2("uni_domain_name", &id->uni_domain_name,
+ id->hdr_domain_name.buffer, ps, depth))
+ return False;
+ if(!smb_io_unistr2("uni_user_name ", &id->uni_user_name,
+ id->hdr_user_name.buffer, ps, depth))
+ return False;
+ if(!smb_io_unistr2("uni_wksta_name ", &id->uni_wksta_name,
+ id->hdr_wksta_name.buffer, ps, depth))
+ return False;
+ if(!smb_io_string2("nt_chal_resp", &id->nt_chal_resp,
+ id->hdr_nt_chal_resp.buffer, ps, depth))
+ return False;
+ if(!smb_io_string2("lm_chal_resp", &id->lm_chal_resp,
+ id->hdr_lm_chal_resp.buffer, ps, depth))
+ return False;
+ }
+
+ return True;
+}
+
+
+/*******************************************************************
+ Inits a DOM_SAM_INFO structure.
+********************************************************************/
+
+void init_sam_info(DOM_SAM_INFO *sam,
+ const char *logon_srv, const char *comp_name,
+ DOM_CRED *clnt_cred,
+ DOM_CRED *rtn_cred, uint16 logon_level,
+ NET_ID_INFO_CTR *ctr)
+{
+ DEBUG(5,("init_sam_info: %d\n", __LINE__));
+
+ init_clnt_info2(&sam->client, logon_srv, comp_name, clnt_cred);
+
+ if (rtn_cred != NULL) {
+ sam->ptr_rtn_cred = 1;
+ memcpy(&sam->rtn_cred, rtn_cred, sizeof(sam->rtn_cred));
+ } else {
+ sam->ptr_rtn_cred = 0;
+ }
+
+ sam->logon_level = logon_level;
+ sam->ctr = ctr;
+}
+
+/*******************************************************************
+ Reads or writes a DOM_SAM_INFO structure.
+********************************************************************/
+
+static BOOL net_io_id_info_ctr(const char *desc, NET_ID_INFO_CTR **pp_ctr, prs_struct *ps, int depth)
+{
+ NET_ID_INFO_CTR *ctr = *pp_ctr;
+
+ prs_debug(ps, depth, desc, "smb_io_sam_info");
+ depth++;
+
+ if (UNMARSHALLING(ps)) {
+ ctr = *pp_ctr = (NET_ID_INFO_CTR *)prs_alloc_mem(ps, sizeof(NET_ID_INFO_CTR));
+ if (ctr == NULL)
+ return False;
+ }
+
+ if (ctr == NULL)
+ return False;
+
+ /* don't 4-byte align here! */
+
+ if(!prs_uint16("switch_value ", ps, depth, &ctr->switch_value))
+ return False;
+
+ switch (ctr->switch_value) {
+ case 1:
+ if(!net_io_id_info1("", &ctr->auth.id1, ps, depth))
+ return False;
+ break;
+ case 2:
+ if(!net_io_id_info2("", &ctr->auth.id2, ps, depth))
+ return False;
+ break;
+ default:
+ /* PANIC! */
+ DEBUG(4,("smb_io_sam_info: unknown switch_value!\n"));
+ break;
+ }
+
+ return True;
+}
+
+/*******************************************************************
+ Reads or writes a DOM_SAM_INFO structure.
+ ********************************************************************/
+
+static BOOL smb_io_sam_info(const char *desc, DOM_SAM_INFO *sam, prs_struct *ps, int depth)
+{
+ if (sam == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "smb_io_sam_info");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!smb_io_clnt_info2("", &sam->client, ps, depth))
+ return False;
+
+ if(!prs_uint32("ptr_rtn_cred ", ps, depth, &sam->ptr_rtn_cred))
+ return False;
+ if(!smb_io_cred("", &sam->rtn_cred, ps, depth))
+ return False;
+
+ if(!prs_uint16("logon_level ", ps, depth, &sam->logon_level))
+ return False;
+
+ if (sam->logon_level != 0) {
+ if(!net_io_id_info_ctr("logon_info", &sam->ctr, ps, depth))
+ return False;
+ }
+
+ return True;
+}
+
+/*************************************************************************
+ Inits a NET_USER_INFO_3 structure.
+
+ This is a network logon reply packet, and contains much information about
+ the user. This information is passed as a (very long) paramater list
+ to avoid having to link in the PASSDB code to every program that deals
+ with this file.
+ *************************************************************************/
+
+void init_net_user_info3(TALLOC_CTX *ctx, NET_USER_INFO_3 *usr,
+ uint32 user_rid,
+ uint32 group_rid,
+
+ const char* user_name,
+ const char* full_name,
+ const char* home_dir,
+ const char* dir_drive,
+ const char* logon_script,
+ const char* profile_path,
+
+ time_t unix_logon_time,
+ time_t unix_logoff_time,
+ time_t unix_kickoff_time,
+ time_t unix_pass_last_set_time,
+ time_t unix_pass_can_change_time,
+ time_t unix_pass_must_change_time,
+
+ uint16 logon_count, uint16 bad_pw_count,
+ uint32 num_groups, const DOM_GID *gids,
+ uint32 user_flgs, uchar nt_session_key[16],
+ uchar lm_session_key[16],
+ const char *logon_srv, const char *logon_dom,
+ const DOM_SID *dom_sid, const char *other_sids)
+{
+ /* only cope with one "other" sid, right now. */
+ /* need to count the number of space-delimited sids */
+ unsigned int i;
+ int num_other_sids = 0;
+
+ NTTIME logon_time, logoff_time, kickoff_time,
+ pass_last_set_time, pass_can_change_time,
+ pass_must_change_time;
+
+ ZERO_STRUCTP(usr);
+
+ usr->ptr_user_info = 1; /* yes, we're bothering to put USER_INFO data here */
+
+ /* Create NTTIME structs */
+ unix_to_nt_time (&logon_time, unix_logon_time);
+ unix_to_nt_time (&logoff_time, unix_logoff_time);
+ unix_to_nt_time (&kickoff_time, unix_kickoff_time);
+ unix_to_nt_time (&pass_last_set_time, unix_pass_last_set_time);
+ unix_to_nt_time (&pass_can_change_time, unix_pass_can_change_time);
+ unix_to_nt_time (&pass_must_change_time, unix_pass_must_change_time);
+
+ usr->logon_time = logon_time;
+ usr->logoff_time = logoff_time;
+ usr->kickoff_time = kickoff_time;
+ usr->pass_last_set_time = pass_last_set_time;
+ usr->pass_can_change_time = pass_can_change_time;
+ usr->pass_must_change_time = pass_must_change_time;
+
+ usr->logon_count = logon_count;
+ usr->bad_pw_count = bad_pw_count;
+
+ usr->user_rid = user_rid;
+ usr->group_rid = group_rid;
+ usr->num_groups = num_groups;
+
+ usr->buffer_groups = 1; /* indicates fill in groups, below, even if there are none */
+ usr->user_flgs = user_flgs;
+
+ if (nt_session_key != NULL)
+ memcpy(usr->user_sess_key, nt_session_key, sizeof(usr->user_sess_key));
+ else
+ memset((char *)usr->user_sess_key, '\0', sizeof(usr->user_sess_key));
+
+ usr->buffer_dom_id = dom_sid ? 1 : 0; /* yes, we're bothering to put a domain SID in */
+
+ memset((char *)usr->padding, '\0', sizeof(usr->padding));
+
+#if 0 /* JRATEST - exchange auth test. */
+ if (lm_session_key != NULL)
+ memcpy(usr->padding, lm_session_key, sizeof(usr->user_sess_key));
+#endif
+
+ num_other_sids = init_dom_sid2s(ctx, other_sids, &usr->other_sids);
+
+ usr->num_other_sids = num_other_sids;
+ usr->buffer_other_sids = (num_other_sids != 0) ? 1 : 0;
+
+ init_unistr2(&usr->uni_user_name, user_name, UNI_FLAGS_NONE);
+ init_uni_hdr(&usr->hdr_user_name, &usr->uni_user_name);
+ init_unistr2(&usr->uni_full_name, full_name, UNI_FLAGS_NONE);
+ init_uni_hdr(&usr->hdr_full_name, &usr->uni_full_name);
+ init_unistr2(&usr->uni_logon_script, logon_script, UNI_FLAGS_NONE);
+ init_uni_hdr(&usr->hdr_logon_script, &usr->uni_logon_script);
+ init_unistr2(&usr->uni_profile_path, profile_path, UNI_FLAGS_NONE);
+ init_uni_hdr(&usr->hdr_profile_path, &usr->uni_profile_path);
+ init_unistr2(&usr->uni_home_dir, home_dir, UNI_FLAGS_NONE);
+ init_uni_hdr(&usr->hdr_home_dir, &usr->uni_home_dir);
+ init_unistr2(&usr->uni_dir_drive, dir_drive, UNI_FLAGS_NONE);
+ init_uni_hdr(&usr->hdr_dir_drive, &usr->uni_dir_drive);
+
+ usr->num_groups2 = num_groups;
+
+ usr->gids = (DOM_GID *)talloc_zero(ctx,sizeof(DOM_GID) * (num_groups));
+ if (usr->gids == NULL && num_groups>0)
+ return;
+
+ for (i = 0; i < num_groups; i++)
+ usr->gids[i] = gids[i];
+
+ init_unistr2(&usr->uni_logon_srv, logon_srv, UNI_FLAGS_NONE);
+ init_uni_hdr(&usr->hdr_logon_srv, &usr->uni_logon_srv);
+ init_unistr2(&usr->uni_logon_dom, logon_dom, UNI_FLAGS_NONE);
+ init_uni_hdr(&usr->hdr_logon_dom, &usr->uni_logon_dom);
+
+ init_dom_sid2(&usr->dom_sid, dom_sid);
+ /* "other" sids are set up above */
+}
+
+/*******************************************************************
+ This code has been modified to cope with a NET_USER_INFO_2 - which is
+ exactly the same as a NET_USER_INFO_3, minus the other sids parameters.
+ We use validation level to determine if we're marshalling a info 2 or
+ INFO_3 - be we always return an INFO_3. Based on code donated by Marc
+ Jacobsen at HP. JRA.
+********************************************************************/
+
+BOOL net_io_user_info3(const char *desc, NET_USER_INFO_3 *usr, prs_struct *ps,
+ int depth, uint16 validation_level)
+{
+ unsigned int i;
+
+ if (usr == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "net_io_user_info3");
+ depth++;
+
+ if (UNMARSHALLING(ps))
+ ZERO_STRUCTP(usr);
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("ptr_user_info ", ps, depth, &usr->ptr_user_info))
+ return False;
+
+ if (usr->ptr_user_info == 0)
+ return True;
+
+ if(!smb_io_time("logon time", &usr->logon_time, ps, depth)) /* logon time */
+ return False;
+ if(!smb_io_time("logoff time", &usr->logoff_time, ps, depth)) /* logoff time */
+ return False;
+ if(!smb_io_time("kickoff time", &usr->kickoff_time, ps, depth)) /* kickoff time */
+ return False;
+ if(!smb_io_time("last set time", &usr->pass_last_set_time, ps, depth)) /* password last set time */
+ return False;
+ if(!smb_io_time("can change time", &usr->pass_can_change_time , ps, depth)) /* password can change time */
+ return False;
+ if(!smb_io_time("must change time", &usr->pass_must_change_time, ps, depth)) /* password must change time */
+ return False;
+
+ if(!smb_io_unihdr("hdr_user_name", &usr->hdr_user_name, ps, depth)) /* username unicode string header */
+ return False;
+ if(!smb_io_unihdr("hdr_full_name", &usr->hdr_full_name, ps, depth)) /* user's full name unicode string header */
+ return False;
+ if(!smb_io_unihdr("hdr_logon_script", &usr->hdr_logon_script, ps, depth)) /* logon script unicode string header */
+ return False;
+ if(!smb_io_unihdr("hdr_profile_path", &usr->hdr_profile_path, ps, depth)) /* profile path unicode string header */
+ return False;
+ if(!smb_io_unihdr("hdr_home_dir", &usr->hdr_home_dir, ps, depth)) /* home directory unicode string header */
+ return False;
+ if(!smb_io_unihdr("hdr_dir_drive", &usr->hdr_dir_drive, ps, depth)) /* home directory drive unicode string header */
+ return False;
+
+ if(!prs_uint16("logon_count ", ps, depth, &usr->logon_count)) /* logon count */
+ return False;
+ if(!prs_uint16("bad_pw_count ", ps, depth, &usr->bad_pw_count)) /* bad password count */
+ return False;
+
+ if(!prs_uint32("user_rid ", ps, depth, &usr->user_rid)) /* User RID */
+ return False;
+ if(!prs_uint32("group_rid ", ps, depth, &usr->group_rid)) /* Group RID */
+ return False;
+ if(!prs_uint32("num_groups ", ps, depth, &usr->num_groups)) /* num groups */
+ return False;
+ if(!prs_uint32("buffer_groups ", ps, depth, &usr->buffer_groups)) /* undocumented buffer pointer to groups. */
+ return False;
+ if(!prs_uint32("user_flgs ", ps, depth, &usr->user_flgs)) /* user flags */
+ return False;
+
+ if(!prs_uint8s(False, "user_sess_key", ps, depth, usr->user_sess_key, 16)) /* user session key */
+ return False;
+
+ if(!smb_io_unihdr("hdr_logon_srv", &usr->hdr_logon_srv, ps, depth)) /* logon server unicode string header */
+ return False;
+ if(!smb_io_unihdr("hdr_logon_dom", &usr->hdr_logon_dom, ps, depth)) /* logon domain unicode string header */
+ return False;
+
+ if(!prs_uint32("buffer_dom_id ", ps, depth, &usr->buffer_dom_id)) /* undocumented logon domain id pointer */
+ return False;
+ if(!prs_uint8s (False, "padding ", ps, depth, usr->padding, 40)) /* unused padding bytes? */
+ return False;
+
+ if (validation_level == 3) {
+ if(!prs_uint32("num_other_sids", ps, depth, &usr->num_other_sids)) /* 0 - num_sids */
+ return False;
+ if(!prs_uint32("buffer_other_sids", ps, depth, &usr->buffer_other_sids)) /* NULL - undocumented pointer to SIDs. */
+ return False;
+ } else {
+ if (UNMARSHALLING(ps)) {
+ usr->num_other_sids = 0;
+ usr->buffer_other_sids = 0;
+ }
+ }
+
+ if(!smb_io_unistr2("uni_user_name", &usr->uni_user_name, usr->hdr_user_name.buffer, ps, depth)) /* username unicode string */
+ return False;
+ if(!smb_io_unistr2("uni_full_name", &usr->uni_full_name, usr->hdr_full_name.buffer, ps, depth)) /* user's full name unicode string */
+ return False;
+ if(!smb_io_unistr2("uni_logon_script", &usr->uni_logon_script, usr->hdr_logon_script.buffer, ps, depth)) /* logon script unicode string */
+ return False;
+ if(!smb_io_unistr2("uni_profile_path", &usr->uni_profile_path, usr->hdr_profile_path.buffer, ps, depth)) /* profile path unicode string */
+ return False;
+ if(!smb_io_unistr2("uni_home_dir", &usr->uni_home_dir, usr->hdr_home_dir.buffer, ps, depth)) /* home directory unicode string */
+ return False;
+ if(!smb_io_unistr2("uni_dir_drive", &usr->uni_dir_drive, usr->hdr_dir_drive.buffer, ps, depth)) /* home directory drive unicode string */
+ return False;
+
+ if(!prs_align(ps))
+ return False;
+ if(!prs_uint32("num_groups2 ", ps, depth, &usr->num_groups2)) /* num groups */
+ return False;
+
+ if (UNMARSHALLING(ps) && usr->num_groups2 > 0) {
+ usr->gids = (DOM_GID *)prs_alloc_mem(ps, sizeof(DOM_GID)*usr->num_groups2);
+ if (usr->gids == NULL)
+ return False;
+ }
+
+ for (i = 0; i < usr->num_groups2; i++) {
+ if(!smb_io_gid("", &usr->gids[i], ps, depth)) /* group info */
+ return False;
+ }
+
+ if(!smb_io_unistr2("uni_logon_srv", &usr->uni_logon_srv, usr->hdr_logon_srv.buffer, ps, depth)) /* logon server unicode string */
+ return False;
+ if(!smb_io_unistr2("uni_logon_dom", &usr->uni_logon_dom, usr->hdr_logon_srv.buffer, ps, depth)) /* logon domain unicode string */
+ return False;
+
+ if(!smb_io_dom_sid2("", &usr->dom_sid, ps, depth)) /* domain SID */
+ return False;
+
+ if (usr->num_other_sids) {
+
+ if (UNMARSHALLING(ps)) {
+ usr->other_sids = (DOM_SID2 *)prs_alloc_mem(ps, sizeof(DOM_SID2)*usr->num_other_sids);
+ if (usr->other_sids == NULL)
+ return False;
+ }
+
+ if(!prs_uint32("num_other_groups", ps, depth, &usr->num_other_groups))
+ return False;
+
+ if (UNMARSHALLING(ps) && usr->num_other_groups > 0) {
+ usr->other_gids = (DOM_GID *)prs_alloc_mem(ps, sizeof(DOM_GID)*usr->num_other_groups);
+ if (usr->other_gids == NULL)
+ return False;
+ }
+
+ for (i = 0; i < usr->num_other_groups; i++) {
+ if(!smb_io_gid("", &usr->other_gids[i], ps, depth)) /* other GIDs */
+ return False;
+ }
+ for (i = 0; i < usr->num_other_sids; i++) {
+ if(!smb_io_dom_sid2("", &usr->other_sids[i], ps, depth)) /* other domain SIDs */
+ return False;
+ }
+ }
+
+ return True;
+}
+
+/*******************************************************************
+ Reads or writes a structure.
+********************************************************************/
+
+BOOL net_io_q_sam_logon(const char *desc, NET_Q_SAM_LOGON *q_l, prs_struct *ps, int depth)
+{
+ if (q_l == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "net_io_q_sam_logon");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!smb_io_sam_info("", &q_l->sam_id, ps, depth))
+ return False;
+
+ if(!prs_align_uint16(ps))
+ return False;
+
+ if(!prs_uint16("validation_level", ps, depth, &q_l->validation_level))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ Reads or writes a structure.
+********************************************************************/
+
+BOOL net_io_r_sam_logon(const char *desc, NET_R_SAM_LOGON *r_l, prs_struct *ps, int depth)
+{
+ if (r_l == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "net_io_r_sam_logon");
+ depth++;
+
+ if(!prs_uint32("buffer_creds", ps, depth, &r_l->buffer_creds)) /* undocumented buffer pointer */
+ return False;
+ if(!smb_io_cred("", &r_l->srv_creds, ps, depth)) /* server credentials. server time stamp appears to be ignored. */
+ return False;
+
+ if(!prs_uint16("switch_value", ps, depth, &r_l->switch_value))
+ return False;
+ if(!prs_align(ps))
+ return False;
+
+#if 1 /* W2k always needs this - even for bad passwd. JRA */
+ if(!net_io_user_info3("", r_l->user, ps, depth, r_l->switch_value))
+ return False;
+#else
+ if (r_l->switch_value != 0) {
+ if(!net_io_user_info3("", r_l->user, ps, depth, r_l->switch_value))
+ return False;
+ }
+#endif
+
+ if(!prs_uint32("auth_resp ", ps, depth, &r_l->auth_resp)) /* 1 - Authoritative response; 0 - Non-Auth? */
+ return False;
+
+ if(!prs_ntstatus("status ", ps, depth, &r_l->status))
+ return False;
+
+ if(!prs_align(ps))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ Reads or writes a structure.
+********************************************************************/
+
+BOOL net_io_q_sam_logoff(const char *desc, NET_Q_SAM_LOGOFF *q_l, prs_struct *ps, int depth)
+{
+ if (q_l == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "net_io_q_sam_logoff");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!smb_io_sam_info("", &q_l->sam_id, ps, depth)) /* domain SID */
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ Reads or writes a structure.
+********************************************************************/
+
+BOOL net_io_r_sam_logoff(const char *desc, NET_R_SAM_LOGOFF *r_l, prs_struct *ps, int depth)
+{
+ if (r_l == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "net_io_r_sam_logoff");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("buffer_creds", ps, depth, &r_l->buffer_creds)) /* undocumented buffer pointer */
+ return False;
+ if(!smb_io_cred("", &r_l->srv_creds, ps, depth)) /* server credentials. server time stamp appears to be ignored. */
+ return False;
+
+ if(!prs_ntstatus("status ", ps, depth, &r_l->status))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+makes a NET_Q_SAM_SYNC structure.
+********************************************************************/
+BOOL init_net_q_sam_sync(NET_Q_SAM_SYNC * q_s, const char *srv_name,
+ const char *cli_name, DOM_CRED *cli_creds,
+ DOM_CRED *ret_creds, uint32 database_id,
+ uint32 next_rid)
+{
+ DEBUG(5, ("init_q_sam_sync\n"));
+
+ init_unistr2(&q_s->uni_srv_name, srv_name, UNI_STR_TERMINATE);
+ init_unistr2(&q_s->uni_cli_name, cli_name, UNI_STR_TERMINATE);
+
+ if (cli_creds)
+ memcpy(&q_s->cli_creds, cli_creds, sizeof(q_s->cli_creds));
+
+ if (cli_creds)
+ memcpy(&q_s->ret_creds, ret_creds, sizeof(q_s->ret_creds));
+ else
+ memset(&q_s->ret_creds, 0, sizeof(q_s->ret_creds));
+
+ q_s->database_id = database_id;
+ q_s->restart_state = 0;
+ q_s->sync_context = next_rid;
+ q_s->max_size = 0xffff;
+
+ return True;
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+BOOL net_io_q_sam_sync(const char *desc, NET_Q_SAM_SYNC * q_s, prs_struct *ps,
+ int depth)
+{
+ prs_debug(ps, depth, desc, "net_io_q_sam_sync");
+ depth++;
+
+ if (!smb_io_unistr2("", &q_s->uni_srv_name, True, ps, depth))
+ return False;
+ if (!smb_io_unistr2("", &q_s->uni_cli_name, True, ps, depth))
+ return False;
+
+ if (!smb_io_cred("", &q_s->cli_creds, ps, depth))
+ return False;
+ if (!smb_io_cred("", &q_s->ret_creds, ps, depth))
+ return False;
+
+ if (!prs_uint32("database_id ", ps, depth, &q_s->database_id))
+ return False;
+ if (!prs_uint32("restart_state", ps, depth, &q_s->restart_state))
+ return False;
+ if (!prs_uint32("sync_context ", ps, depth, &q_s->sync_context))
+ return False;
+
+ if (!prs_uint32("max_size", ps, depth, &q_s->max_size))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+static BOOL net_io_sam_delta_hdr(const char *desc, SAM_DELTA_HDR * delta,
+ prs_struct *ps, int depth)
+{
+ prs_debug(ps, depth, desc, "net_io_sam_delta_hdr");
+ depth++;
+
+ if (!prs_uint16("type", ps, depth, &delta->type))
+ return False;
+ if (!prs_uint16("type2", ps, depth, &delta->type2))
+ return False;
+ if (!prs_uint32("target_rid", ps, depth, &delta->target_rid))
+ return False;
+
+ if (!prs_uint32("type3", ps, depth, &delta->type3))
+ return False;
+
+ /* Not sure why we need this but it seems to be necessary to get
+ sam deltas working. */
+
+ if (delta->type != 0x16) {
+ if (!prs_uint32("ptr_delta", ps, depth, &delta->ptr_delta))
+ return False;
+ }
+
+ return True;
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+static BOOL net_io_sam_delta_mod_count(const char *desc, SAM_DELTA_MOD_COUNT *info,
+ prs_struct *ps, int depth)
+{
+ prs_debug(ps, depth, desc, "net_io_sam_delta_stamp");
+ depth++;
+
+ if (!prs_uint32("seqnum", ps, depth, &info->seqnum))
+ return False;
+ if (!prs_uint32("dom_mod_count_ptr", ps, depth,
+ &info->dom_mod_count_ptr))
+ return False;
+
+ if (info->dom_mod_count_ptr) {
+ if (!prs_uint64("dom_mod_count", ps, depth,
+ &info->dom_mod_count))
+ return False;
+ }
+
+ return True;
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+static BOOL net_io_sam_domain_info(const char *desc, SAM_DOMAIN_INFO * info,
+ prs_struct *ps, int depth)
+{
+ prs_debug(ps, depth, desc, "net_io_sam_domain_info");
+ depth++;
+
+ if (!smb_io_unihdr("hdr_dom_name", &info->hdr_dom_name, ps, depth))
+ return False;
+ if (!smb_io_unihdr("hdr_oem_info", &info->hdr_oem_info, ps, depth))
+ return False;
+
+ if (!prs_uint64("force_logoff", ps, depth, &info->force_logoff))
+ return False;
+ if (!prs_uint16("min_pwd_len", ps, depth, &info->min_pwd_len))
+ return False;
+ if (!prs_uint16("pwd_history_len", ps, depth, &info->pwd_history_len))
+ return False;
+ if (!prs_uint64("max_pwd_age", ps, depth, &info->max_pwd_age))
+ return False;
+ if (!prs_uint64("min_pwd_age", ps, depth, &info->min_pwd_age))
+ return False;
+ if (!prs_uint64("dom_mod_count", ps, depth, &info->dom_mod_count))
+ return False;
+ if (!smb_io_time("creation_time", &info->creation_time, ps, depth))
+ return False;
+
+ if (!smb_io_bufhdr2("hdr_sec_desc", &info->hdr_sec_desc, ps, depth))
+ return False;
+ if (!smb_io_unihdr("hdr_unknown", &info->hdr_unknown, ps, depth))
+ return False;
+
+ if (ps->data_offset + 40 > ps->buffer_size)
+ return False;
+ ps->data_offset += 40;
+
+ if (!smb_io_unistr2("uni_dom_name", &info->uni_dom_name,
+ info->hdr_dom_name.buffer, ps, depth))
+ return False;
+ if (!smb_io_unistr2("buf_oem_info", &info->buf_oem_info,
+ info->hdr_oem_info.buffer, ps, depth))
+ return False;
+
+ if (!smb_io_buffer4("buf_sec_desc", &info->buf_sec_desc,
+ info->hdr_sec_desc.buffer, ps, depth))
+ return False;
+ if (!smb_io_unistr2("buf_unknown", &info->buf_unknown,
+ info->hdr_unknown.buffer, ps, depth))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+static BOOL net_io_sam_group_info(const char *desc, SAM_GROUP_INFO * info,
+ prs_struct *ps, int depth)
+{
+ prs_debug(ps, depth, desc, "net_io_sam_group_info");
+ depth++;
+
+ if (!smb_io_unihdr("hdr_grp_name", &info->hdr_grp_name, ps, depth))
+ return False;
+ if (!smb_io_gid("gid", &info->gid, ps, depth))
+ return False;
+ if (!smb_io_unihdr("hdr_grp_desc", &info->hdr_grp_desc, ps, depth))
+ return False;
+ if (!smb_io_bufhdr2("hdr_sec_desc", &info->hdr_sec_desc, ps, depth))
+ return False;
+
+ if (ps->data_offset + 48 > ps->buffer_size)
+ return False;
+ ps->data_offset += 48;
+
+ if (!smb_io_unistr2("uni_grp_name", &info->uni_grp_name,
+ info->hdr_grp_name.buffer, ps, depth))
+ return False;
+ if (!smb_io_unistr2("uni_grp_desc", &info->uni_grp_desc,
+ info->hdr_grp_desc.buffer, ps, depth))
+ return False;
+ if (!smb_io_buffer4("buf_sec_desc", &info->buf_sec_desc,
+ info->hdr_sec_desc.buffer, ps, depth))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+static BOOL net_io_sam_passwd_info(const char *desc, SAM_PWD * pwd,
+ prs_struct *ps, int depth)
+{
+ prs_debug(ps, depth, desc, "net_io_sam_passwd_info");
+ depth++;
+
+ if (!prs_uint32("unk_0 ", ps, depth, &pwd->unk_0))
+ return False;
+
+ if (!smb_io_unihdr("hdr_lm_pwd", &pwd->hdr_lm_pwd, ps, depth))
+ return False;
+ if (!prs_uint8s(False, "buf_lm_pwd", ps, depth, pwd->buf_lm_pwd, 16))
+ return False;
+
+ if (!smb_io_unihdr("hdr_nt_pwd", &pwd->hdr_nt_pwd, ps, depth))
+ return False;
+ if (!prs_uint8s(False, "buf_nt_pwd", ps, depth, pwd->buf_nt_pwd, 16))
+ return False;
+
+ if (!smb_io_unihdr("", &pwd->hdr_empty_lm, ps, depth))
+ return False;
+ if (!smb_io_unihdr("", &pwd->hdr_empty_nt, ps, depth))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+makes a SAM_ACCOUNT_INFO structure.
+********************************************************************/
+BOOL make_sam_account_info(SAM_ACCOUNT_INFO * info,
+ const UNISTR2 *user_name,
+ const UNISTR2 *full_name,
+ uint32 user_rid, uint32 group_rid,
+ const UNISTR2 *home_dir,
+ const UNISTR2 *dir_drive,
+ const UNISTR2 *log_scr,
+ const UNISTR2 *desc,
+ uint32 acb_info,
+ const UNISTR2 *prof_path,
+ const UNISTR2 *wkstas,
+ const UNISTR2 *unk_str, const UNISTR2 *mung_dial)
+{
+ int len_user_name = user_name != NULL ? user_name->uni_str_len : 0;
+ int len_full_name = full_name != NULL ? full_name->uni_str_len : 0;
+ int len_home_dir = home_dir != NULL ? home_dir->uni_str_len : 0;
+ int len_dir_drive = dir_drive != NULL ? dir_drive->uni_str_len : 0;
+ int len_logon_script = log_scr != NULL ? log_scr->uni_str_len : 0;
+ int len_profile_path = prof_path != NULL ? prof_path->uni_str_len : 0;
+ int len_description = desc != NULL ? desc->uni_str_len : 0;
+ int len_workstations = wkstas != NULL ? wkstas->uni_str_len : 0;
+ int len_unknown_str = unk_str != NULL ? unk_str->uni_str_len : 0;
+ int len_munged_dial = mung_dial != NULL ? mung_dial->uni_str_len : 0;
+
+ DEBUG(5, ("make_sam_account_info\n"));
+
+ make_uni_hdr(&info->hdr_acct_name, len_user_name);
+ make_uni_hdr(&info->hdr_full_name, len_full_name);
+ make_uni_hdr(&info->hdr_home_dir, len_home_dir);
+ make_uni_hdr(&info->hdr_dir_drive, len_dir_drive);
+ make_uni_hdr(&info->hdr_logon_script, len_logon_script);
+ make_uni_hdr(&info->hdr_profile, len_profile_path);
+ make_uni_hdr(&info->hdr_acct_desc, len_description);
+ make_uni_hdr(&info->hdr_workstations, len_workstations);
+ make_uni_hdr(&info->hdr_comment, len_unknown_str);
+ make_uni_hdr(&info->hdr_parameters, len_munged_dial);
+
+ /* not present */
+ make_bufhdr2(&info->hdr_sec_desc, 0, 0, 0);
+
+ info->user_rid = user_rid;
+ info->group_rid = group_rid;
+
+ init_nt_time(&info->logon_time);
+ init_nt_time(&info->logoff_time);
+ init_nt_time(&info->pwd_last_set_time);
+ init_nt_time(&info->acct_expiry_time);
+
+ info->logon_divs = 0xA8;
+ info->ptr_logon_hrs = 0; /* Don't care right now */
+
+ info->bad_pwd_count = 0;
+ info->logon_count = 0;
+ info->acb_info = acb_info;
+ info->nt_pwd_present = 0;
+ info->lm_pwd_present = 0;
+ info->pwd_expired = 0;
+ info->country = 0;
+ info->codepage = 0;
+
+ info->unknown1 = 0x4EC;
+ info->unknown2 = 0;
+
+ copy_unistr2(&info->uni_acct_name, user_name);
+ copy_unistr2(&info->uni_full_name, full_name);
+ copy_unistr2(&info->uni_home_dir, home_dir);
+ copy_unistr2(&info->uni_dir_drive, dir_drive);
+ copy_unistr2(&info->uni_logon_script, log_scr);
+ copy_unistr2(&info->uni_profile, prof_path);
+ copy_unistr2(&info->uni_acct_desc, desc);
+ copy_unistr2(&info->uni_workstations, wkstas);
+ copy_unistr2(&info->uni_comment, unk_str);
+ copy_unistr2(&info->uni_parameters, mung_dial);
+
+ return True;
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+static BOOL net_io_sam_account_info(const char *desc, uint8 sess_key[16],
+ SAM_ACCOUNT_INFO * info, prs_struct *ps,
+ int depth)
+{
+ BUFHDR2 hdr_priv_data;
+ uint32 i;
+
+ prs_debug(ps, depth, desc, "net_io_sam_account_info");
+ depth++;
+
+ if (!smb_io_unihdr("hdr_acct_name", &info->hdr_acct_name, ps, depth))
+ return False;
+ if (!smb_io_unihdr("hdr_full_name", &info->hdr_full_name, ps, depth))
+ return False;
+
+ if (!prs_uint32("user_rid ", ps, depth, &info->user_rid))
+ return False;
+ if (!prs_uint32("group_rid", ps, depth, &info->group_rid))
+ return False;
+
+ if (!smb_io_unihdr("hdr_home_dir ", &info->hdr_home_dir, ps, depth))
+ return False;
+ if (!smb_io_unihdr("hdr_dir_drive", &info->hdr_dir_drive, ps, depth))
+ return False;
+ if (!smb_io_unihdr("hdr_logon_script", &info->hdr_logon_script, ps,
+ depth))
+ return False;
+
+ if (!smb_io_unihdr("hdr_acct_desc", &info->hdr_acct_desc, ps, depth))
+ return False;
+ if (!smb_io_unihdr("hdr_workstations", &info->hdr_workstations, ps,
+ depth))
+ return False;
+
+ if (!smb_io_time("logon_time", &info->logon_time, ps, depth))
+ return False;
+ if (!smb_io_time("logoff_time", &info->logoff_time, ps, depth))
+ return False;
+
+ if (!prs_uint32("logon_divs ", ps, depth, &info->logon_divs))
+ return False;
+ if (!prs_uint32("ptr_logon_hrs", ps, depth, &info->ptr_logon_hrs))
+ return False;
+
+ if (!prs_uint16("bad_pwd_count", ps, depth, &info->bad_pwd_count))
+ return False;
+ if (!prs_uint16("logon_count", ps, depth, &info->logon_count))
+ return False;
+ if (!smb_io_time("pwd_last_set_time", &info->pwd_last_set_time, ps,
+ depth))
+ return False;
+ if (!smb_io_time("acct_expiry_time", &info->acct_expiry_time, ps,
+ depth))
+ return False;
+
+ if (!prs_uint32("acb_info", ps, depth, &info->acb_info))
+ return False;
+ if (!prs_uint8s(False, "nt_pwd", ps, depth, info->nt_pwd, 16))
+ return False;
+ if (!prs_uint8s(False, "lm_pwd", ps, depth, info->lm_pwd, 16))
+ return False;
+ if (!prs_uint8("lm_pwd_present", ps, depth, &info->lm_pwd_present))
+ return False;
+ if (!prs_uint8("nt_pwd_present", ps, depth, &info->nt_pwd_present))
+ return False;
+ if (!prs_uint8("pwd_expired", ps, depth, &info->pwd_expired))
+ return False;
+
+ if (!smb_io_unihdr("hdr_comment", &info->hdr_comment, ps, depth))
+ return False;
+ if (!smb_io_unihdr("hdr_parameters", &info->hdr_parameters, ps,
+ depth))
+ return False;
+ if (!prs_uint16("country", ps, depth, &info->country))
+ return False;
+ if (!prs_uint16("codepage", ps, depth, &info->codepage))
+ return False;
+
+ if (!smb_io_bufhdr2("hdr_priv_data", &hdr_priv_data, ps, depth))
+ return False;
+ if (!smb_io_bufhdr2("hdr_sec_desc", &info->hdr_sec_desc, ps, depth))
+ return False;
+ if (!smb_io_unihdr("hdr_profile", &info->hdr_profile, ps, depth))
+ return False;
+
+ for (i = 0; i < 3; i++)
+ {
+ if (!smb_io_unihdr("hdr_reserved", &info->hdr_reserved[i],
+ ps, depth))
+ return False;
+ }
+
+ for (i = 0; i < 4; i++)
+ {
+ if (!prs_uint32("dw_reserved", ps, depth,
+ &info->dw_reserved[i]))
+ return False;
+ }
+
+ if (!smb_io_unistr2("uni_acct_name", &info->uni_acct_name,
+ info->hdr_acct_name.buffer, ps, depth))
+ return False;
+ prs_align(ps);
+ if (!smb_io_unistr2("uni_full_name", &info->uni_full_name,
+ info->hdr_full_name.buffer, ps, depth))
+ return False;
+ prs_align(ps);
+ if (!smb_io_unistr2("uni_home_dir ", &info->uni_home_dir,
+ info->hdr_home_dir.buffer, ps, depth))
+ return False;
+ prs_align(ps);
+ if (!smb_io_unistr2("uni_dir_drive", &info->uni_dir_drive,
+ info->hdr_dir_drive.buffer, ps, depth))
+ return False;
+ prs_align(ps);
+ if (!smb_io_unistr2("uni_logon_script", &info->uni_logon_script,
+ info->hdr_logon_script.buffer, ps, depth))
+ return False;
+ prs_align(ps);
+ if (!smb_io_unistr2("uni_acct_desc", &info->uni_acct_desc,
+ info->hdr_acct_desc.buffer, ps, depth))
+ return False;
+ prs_align(ps);
+ if (!smb_io_unistr2("uni_workstations", &info->uni_workstations,
+ info->hdr_workstations.buffer, ps, depth))
+ return False;
+ prs_align(ps);
+
+ if (!prs_uint32("unknown1", ps, depth, &info->unknown1))
+ return False;
+ if (!prs_uint32("unknown2", ps, depth, &info->unknown2))
+ return False;
+
+ if (!smb_io_buffer4("buf_logon_hrs", &info->buf_logon_hrs,
+ info->ptr_logon_hrs, ps, depth))
+ return False;
+ prs_align(ps);
+ if (!smb_io_unistr2("uni_comment", &info->uni_comment,
+ info->hdr_comment.buffer, ps, depth))
+ return False;
+ prs_align(ps);
+ if (!smb_io_unistr2("uni_parameters", &info->uni_parameters,
+ info->hdr_parameters.buffer, ps, depth))
+ return False;
+ prs_align(ps);
+ if (hdr_priv_data.buffer != 0)
+ {
+ int old_offset = 0;
+ uint32 len = 0x44;
+ if (!prs_uint32("pwd_len", ps, depth, &len))
+ return False;
+ old_offset = ps->data_offset;
+ if (len > 0)
+ {
+ if (ps->io)
+ {
+ /* reading */
+ if (!prs_hash1(ps, ps->data_offset, sess_key, len))
+ return False;
+ }
+ if (!net_io_sam_passwd_info("pass", &info->pass,
+ ps, depth))
+ return False;
+
+ if (!ps->io)
+ {
+ /* writing */
+ if (!prs_hash1(ps, old_offset, sess_key, len))
+ return False;
+ }
+ }
+ if (old_offset + len > ps->buffer_size)
+ return False;
+ ps->data_offset = old_offset + len;
+ }
+ if (!smb_io_buffer4("buf_sec_desc", &info->buf_sec_desc,
+ info->hdr_sec_desc.buffer, ps, depth))
+ return False;
+ prs_align(ps);
+ if (!smb_io_unistr2("uni_profile", &info->uni_profile,
+ info->hdr_profile.buffer, ps, depth))
+ return False;
+
+ prs_align(ps);
+
+ return True;
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+static BOOL net_io_sam_group_mem_info(const char *desc, SAM_GROUP_MEM_INFO * info,
+ prs_struct *ps, int depth)
+{
+ uint32 i;
+ fstring tmp;
+
+ prs_debug(ps, depth, desc, "net_io_sam_group_mem_info");
+ depth++;
+
+ prs_align(ps);
+ if (!prs_uint32("ptr_rids ", ps, depth, &info->ptr_rids))
+ return False;
+ if (!prs_uint32("ptr_attribs", ps, depth, &info->ptr_attribs))
+ return False;
+ if (!prs_uint32("num_members", ps, depth, &info->num_members))
+ return False;
+
+ if (ps->data_offset + 16 > ps->buffer_size)
+ return False;
+ ps->data_offset += 16;
+
+ if (info->ptr_rids != 0)
+ {
+ if (!prs_uint32("num_members2", ps, depth,
+ &info->num_members2))
+ return False;
+
+ if (info->num_members2 != info->num_members)
+ {
+ /* RPC fault */
+ return False;
+ }
+
+ info->rids = talloc(ps->mem_ctx, sizeof(uint32) *
+ info->num_members2);
+
+ if (info->rids == NULL) {
+ DEBUG(0, ("out of memory allocating %d rids\n",
+ info->num_members2));
+ return False;
+ }
+
+ for (i = 0; i < info->num_members2; i++)
+ {
+ slprintf(tmp, sizeof(tmp) - 1, "rids[%02d]", i);
+ if (!prs_uint32(tmp, ps, depth, &info->rids[i]))
+ return False;
+ }
+ }
+
+ if (info->ptr_attribs != 0)
+ {
+ if (!prs_uint32("num_members3", ps, depth,
+ &info->num_members3))
+ return False;
+ if (info->num_members3 != info->num_members)
+ {
+ /* RPC fault */
+ return False;
+ }
+
+ info->attribs = talloc(ps->mem_ctx, sizeof(uint32) *
+ info->num_members3);
+
+ if (info->attribs == NULL) {
+ DEBUG(0, ("out of memory allocating %d attribs\n",
+ info->num_members3));
+ return False;
+ }
+
+ for (i = 0; i < info->num_members3; i++)
+ {
+ slprintf(tmp, sizeof(tmp) - 1, "attribs[%02d]", i);
+ if (!prs_uint32(tmp, ps, depth, &info->attribs[i]))
+ return False;
+ }
+ }
+
+ return True;
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+static BOOL net_io_sam_alias_info(const char *desc, SAM_ALIAS_INFO * info,
+ prs_struct *ps, int depth)
+{
+ prs_debug(ps, depth, desc, "net_io_sam_alias_info");
+ depth++;
+
+ if (!smb_io_unihdr("hdr_als_name", &info->hdr_als_name, ps, depth))
+ return False;
+ if (!prs_uint32("als_rid", ps, depth, &info->als_rid))
+ return False;
+ if (!smb_io_bufhdr2("hdr_sec_desc", &info->hdr_sec_desc, ps, depth))
+ return False;
+ if (!smb_io_unihdr("hdr_als_desc", &info->hdr_als_desc, ps, depth))
+ return False;
+
+ if (ps->data_offset + 40 > ps->buffer_size)
+ return False;
+ ps->data_offset += 40;
+
+ if (!smb_io_unistr2("uni_als_name", &info->uni_als_name,
+ info->hdr_als_name.buffer, ps, depth))
+ return False;
+ if (!smb_io_buffer4("buf_sec_desc", &info->buf_sec_desc,
+ info->hdr_sec_desc.buffer, ps, depth))
+ return False;
+
+ if (info->hdr_als_desc.buffer != 0) {
+ if (!smb_io_unistr2("uni_als_desc", &info->uni_als_desc,
+ info->hdr_als_name.buffer, ps, depth))
+ return False;
+ }
+
+ return True;
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+static BOOL net_io_sam_alias_mem_info(const char *desc, SAM_ALIAS_MEM_INFO * info,
+ prs_struct *ps, int depth)
+{
+ uint32 i;
+ fstring tmp;
+
+ prs_debug(ps, depth, desc, "net_io_sam_alias_mem_info");
+ depth++;
+
+ prs_align(ps);
+ if (!prs_uint32("num_members", ps, depth, &info->num_members))
+ return False;
+ if (!prs_uint32("ptr_members", ps, depth, &info->ptr_members))
+ return False;
+
+ if (ps->data_offset + 16 > ps->buffer_size)
+ return False;
+ ps->data_offset += 16;
+
+ if (info->ptr_members != 0)
+ {
+ if (!prs_uint32("num_sids", ps, depth, &info->num_sids))
+ return False;
+ if (info->num_sids != info->num_members)
+ {
+ /* RPC fault */
+ return False;
+ }
+
+ info->ptr_sids = talloc(ps->mem_ctx, sizeof(uint32) *
+ info->num_sids);
+
+ if (info->ptr_sids == NULL) {
+ DEBUG(0, ("out of memory allocating %d ptr_sids\n",
+ info->num_sids));
+ return False;
+ }
+
+ for (i = 0; i < info->num_sids; i++)
+ {
+ slprintf(tmp, sizeof(tmp) - 1, "ptr_sids[%02d]", i);
+ if (!prs_uint32(tmp, ps, depth, &info->ptr_sids[i]))
+ return False;
+ }
+
+ info->sids = talloc(ps->mem_ctx, sizeof(DOM_SID2) *
+ info->num_sids);
+
+ if (info->sids == NULL) {
+ DEBUG(0, ("error allocating %d sids\n",
+ info->num_sids));
+ return False;
+ }
+
+ for (i = 0; i < info->num_sids; i++)
+ {
+ if (info->ptr_sids[i] != 0)
+ {
+ slprintf(tmp, sizeof(tmp) - 1, "sids[%02d]",
+ i);
+ if (!smb_io_dom_sid2(tmp, &info->sids[i],
+ ps, depth))
+ return False;
+ }
+ }
+ }
+
+ return True;
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+static BOOL net_io_sam_policy_info(const char *desc, SAM_DELTA_POLICY *info,
+ prs_struct *ps, int depth)
+{
+ unsigned int i;
+ prs_debug(ps, depth, desc, "net_io_sam_policy_info");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if (!prs_uint32("max_log_size", ps, depth, &info->max_log_size))
+ return False;
+ if (!prs_uint64("audit_retention_period", ps, depth,
+ &info->audit_retention_period))
+ return False;
+ if (!prs_uint32("auditing_mode", ps, depth, &info->auditing_mode))
+ return False;
+ if (!prs_uint32("num_events", ps, depth, &info->num_events))
+ return False;
+ if (!prs_uint32("ptr_events", ps, depth, &info->ptr_events))
+ return False;
+
+ if (!smb_io_unihdr("hdr_dom_name", &info->hdr_dom_name, ps, depth))
+ return False;
+
+ if (!prs_uint32("sid_ptr", ps, depth, &info->sid_ptr))
+ return False;
+
+ if (!prs_uint32("paged_pool_limit", ps, depth, &info->paged_pool_limit))
+ return False;
+ if (!prs_uint32("non_paged_pool_limit", ps, depth,
+ &info->non_paged_pool_limit))
+ return False;
+ if (!prs_uint32("min_workset_size", ps, depth, &info->min_workset_size))
+ return False;
+ if (!prs_uint32("max_workset_size", ps, depth, &info->max_workset_size))
+ return False;
+ if (!prs_uint32("page_file_limit", ps, depth, &info->page_file_limit))
+ return False;
+ if (!prs_uint64("time_limit", ps, depth, &info->time_limit))
+ return False;
+ if (!smb_io_time("modify_time", &info->modify_time, ps, depth))
+ return False;
+ if (!smb_io_time("create_time", &info->create_time, ps, depth))
+ return False;
+ if (!smb_io_bufhdr2("hdr_sec_desc", &info->hdr_sec_desc, ps, depth))
+ return False;
+
+ for (i=0; i<4; i++) {
+ UNIHDR dummy;
+ if (!smb_io_unihdr("dummy", &dummy, ps, depth))
+ return False;
+ }
+
+ for (i=0; i<4; i++) {
+ uint32 reserved;
+ if (!prs_uint32("reserved", ps, depth, &reserved))
+ return False;
+ }
+
+ if (!prs_uint32("num_event_audit_options", ps, depth,
+ &info->num_event_audit_options))
+ return False;
+
+ for (i=0; i<info->num_event_audit_options; i++)
+ if (!prs_uint32("event_audit_option", ps, depth,
+ &info->event_audit_option))
+ return False;
+
+ if (!smb_io_unistr2("domain_name", &info->domain_name, True, ps, depth))
+ return False;
+
+ if(!smb_io_dom_sid2("domain_sid", &info->domain_sid, ps, depth))
+ return False;
+
+ if (!smb_io_buffer4("buf_sec_desc", &info->buf_sec_desc,
+ info->hdr_sec_desc.buffer, ps, depth))
+
+ return False;
+
+ return True;
+}
+
+#if 0
+
+/* This function is pretty broken - see bug #334 */
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+static BOOL net_io_sam_trustdoms_info(const char *desc, SAM_DELTA_TRUSTDOMS *info,
+ prs_struct *ps, int depth)
+{
+ int i;
+
+ prs_debug(ps, depth, desc, "net_io_sam_trustdoms_info");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("buf_size", ps, depth, &info->buf_size))
+ return False;
+
+ if(!sec_io_desc("sec_desc", &info->sec_desc, ps, depth))
+ return False;
+
+ if(!smb_io_dom_sid2("sid", &info->sid, ps, depth))
+ return False;
+
+ if(!smb_io_unihdr("hdr_domain", &info->hdr_domain, ps, depth))
+ return False;
+
+ if(!prs_uint32("unknown0", ps, depth, &info->unknown0))
+ return False;
+ if(!prs_uint32("unknown1", ps, depth, &info->unknown1))
+ return False;
+ if(!prs_uint32("unknown2", ps, depth, &info->unknown2))
+ return False;
+
+ if(!prs_uint32("buf_size2", ps, depth, &info->buf_size2))
+ return False;
+ if(!prs_uint32("ptr", ps, depth, &info->ptr))
+ return False;
+
+ for (i=0; i<12; i++)
+ if(!prs_uint32("unknown3", ps, depth, &info->unknown3))
+ return False;
+
+ if (!smb_io_unistr2("domain", &info->domain, True, ps, depth))
+ return False;
+
+ return True;
+}
+
+#endif
+
+#if 0
+
+/* This function doesn't work - see bug #334 */
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+static BOOL net_io_sam_secret_info(const char *desc, SAM_DELTA_SECRET *info,
+ prs_struct *ps, int depth)
+{
+ int i;
+
+ prs_debug(ps, depth, desc, "net_io_sam_secret_info");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("buf_size", ps, depth, &info->buf_size))
+ return False;
+
+ if(!sec_io_desc("sec_desc", &info->sec_desc, ps, depth))
+ return False;
+
+ if (!smb_io_unistr2("secret", &info->secret, True, ps, depth))
+ return False;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("count1", ps, depth, &info->count1))
+ return False;
+ if(!prs_uint32("count2", ps, depth, &info->count2))
+ return False;
+ if(!prs_uint32("ptr", ps, depth, &info->ptr))
+ return False;
+
+
+ if(!smb_io_time("time1", &info->time1, ps, depth)) /* logon time */
+ return False;
+ if(!prs_uint32("count3", ps, depth, &info->count3))
+ return False;
+ if(!prs_uint32("count4", ps, depth, &info->count4))
+ return False;
+ if(!prs_uint32("ptr2", ps, depth, &info->ptr2))
+ return False;
+ if(!smb_io_time("time2", &info->time2, ps, depth)) /* logon time */
+ return False;
+ if(!prs_uint32("unknow1", ps, depth, &info->unknow1))
+ return False;
+
+
+ if(!prs_uint32("buf_size2", ps, depth, &info->buf_size2))
+ return False;
+ if(!prs_uint32("ptr3", ps, depth, &info->ptr3))
+ return False;
+ for(i=0; i<12; i++)
+ if(!prs_uint32("unknow2", ps, depth, &info->unknow2))
+ return False;
+
+ if(!prs_uint32("chal_len", ps, depth, &info->chal_len))
+ return False;
+ if(!prs_uint32("reserved1", ps, depth, &info->reserved1))
+ return False;
+ if(!prs_uint32("chal_len2", ps, depth, &info->chal_len2))
+ return False;
+
+ if(!prs_uint8s (False, "chal", ps, depth, info->chal, info->chal_len2))
+ return False;
+
+ if(!prs_uint32("key_len", ps, depth, &info->key_len))
+ return False;
+ if(!prs_uint32("reserved2", ps, depth, &info->reserved2))
+ return False;
+ if(!prs_uint32("key_len2", ps, depth, &info->key_len2))
+ return False;
+
+ if(!prs_uint8s (False, "key", ps, depth, info->key, info->key_len2))
+ return False;
+
+
+ if(!prs_uint32("buf_size3", ps, depth, &info->buf_size3))
+ return False;
+
+ if(!sec_io_desc("sec_desc2", &info->sec_desc2, ps, depth))
+ return False;
+
+
+ return True;
+}
+
+#endif
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+static BOOL net_io_sam_privs_info(const char *desc, SAM_DELTA_PRIVS *info,
+ prs_struct *ps, int depth)
+{
+ unsigned int i;
+
+ prs_debug(ps, depth, desc, "net_io_sam_privs_info");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!smb_io_dom_sid2("sid", &info->sid, ps, depth))
+ return False;
+
+ if(!prs_uint32("priv_count", ps, depth, &info->priv_count))
+ return False;
+ if(!prs_uint32("priv_control", ps, depth, &info->priv_control))
+ return False;
+
+ if(!prs_uint32("priv_attr_ptr", ps, depth, &info->priv_attr_ptr))
+ return False;
+ if(!prs_uint32("priv_name_ptr", ps, depth, &info->priv_name_ptr))
+ return False;
+
+ if (!prs_uint32("paged_pool_limit", ps, depth, &info->paged_pool_limit))
+ return False;
+ if (!prs_uint32("non_paged_pool_limit", ps, depth,
+ &info->non_paged_pool_limit))
+ return False;
+ if (!prs_uint32("min_workset_size", ps, depth, &info->min_workset_size))
+ return False;
+ if (!prs_uint32("max_workset_size", ps, depth, &info->max_workset_size))
+ return False;
+ if (!prs_uint32("page_file_limit", ps, depth, &info->page_file_limit))
+ return False;
+ if (!prs_uint64("time_limit", ps, depth, &info->time_limit))
+ return False;
+ if (!prs_uint32("system_flags", ps, depth, &info->system_flags))
+ return False;
+ if (!smb_io_bufhdr2("hdr_sec_desc", &info->hdr_sec_desc, ps, depth))
+ return False;
+
+ for (i=0; i<4; i++) {
+ UNIHDR dummy;
+ if (!smb_io_unihdr("dummy", &dummy, ps, depth))
+ return False;
+ }
+
+ for (i=0; i<4; i++) {
+ uint32 reserved;
+ if (!prs_uint32("reserved", ps, depth, &reserved))
+ return False;
+ }
+
+ if(!prs_uint32("attribute_count", ps, depth, &info->attribute_count))
+ return False;
+
+ info->attributes = talloc(ps->mem_ctx, sizeof(uint32) * info->attribute_count);
+
+ for (i=0; i<info->attribute_count; i++)
+ if(!prs_uint32("attributes", ps, depth, &info->attributes[i]))
+ return False;
+
+ if(!prs_uint32("privlist_count", ps, depth, &info->privlist_count))
+ return False;
+
+ info->hdr_privslist = talloc(ps->mem_ctx, sizeof(UNIHDR) * info->privlist_count);
+ info->uni_privslist = talloc(ps->mem_ctx, sizeof(UNISTR2) * info->privlist_count);
+
+ for (i=0; i<info->privlist_count; i++)
+ if(!smb_io_unihdr("hdr_privslist", &info->hdr_privslist[i], ps, depth))
+ return False;
+
+ for (i=0; i<info->privlist_count; i++)
+ if (!smb_io_unistr2("uni_privslist", &info->uni_privslist[i], True, ps, depth))
+ return False;
+
+ if (!smb_io_buffer4("buf_sec_desc", &info->buf_sec_desc,
+ info->hdr_sec_desc.buffer, ps, depth))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+static BOOL net_io_sam_delta_ctr(const char *desc, uint8 sess_key[16],
+ SAM_DELTA_CTR * delta, uint16 type,
+ prs_struct *ps, int depth)
+{
+ prs_debug(ps, depth, desc, "net_io_sam_delta_ctr");
+ depth++;
+
+ switch (type) {
+ /* Seen in sam deltas */
+ case SAM_DELTA_MODIFIED_COUNT:
+ if (!net_io_sam_delta_mod_count("", &delta->mod_count, ps, depth))
+ return False;
+ break;
+
+ case SAM_DELTA_DOMAIN_INFO:
+ if (!net_io_sam_domain_info("", &delta->domain_info, ps, depth))
+ return False;
+ break;
+
+ case SAM_DELTA_GROUP_INFO:
+ if (!net_io_sam_group_info("", &delta->group_info, ps, depth))
+ return False;
+ break;
+
+ case SAM_DELTA_ACCOUNT_INFO:
+ if (!net_io_sam_account_info("", sess_key, &delta->account_info, ps, depth))
+ return False;
+ break;
+
+ case SAM_DELTA_GROUP_MEM:
+ if (!net_io_sam_group_mem_info("", &delta->grp_mem_info, ps, depth))
+ return False;
+ break;
+
+ case SAM_DELTA_ALIAS_INFO:
+ if (!net_io_sam_alias_info("", &delta->alias_info, ps, depth))
+ return False;
+ break;
+
+ case SAM_DELTA_POLICY_INFO:
+ if (!net_io_sam_policy_info("", &delta->policy_info, ps, depth))
+ return False;
+ break;
+
+ case SAM_DELTA_ALIAS_MEM:
+ if (!net_io_sam_alias_mem_info("", &delta->als_mem_info, ps, depth))
+ return False;
+ break;
+
+ case SAM_DELTA_PRIVS_INFO:
+ if (!net_io_sam_privs_info("", &delta->privs_info, ps, depth))
+ return False;
+ break;
+
+ /* These guys are implemented but broken */
+
+ case SAM_DELTA_TRUST_DOMS:
+ case SAM_DELTA_SECRET_INFO:
+ break;
+
+ /* These guys are not implemented yet */
+
+ case SAM_DELTA_RENAME_GROUP:
+ case SAM_DELTA_RENAME_USER:
+ case SAM_DELTA_RENAME_ALIAS:
+ case SAM_DELTA_DELETE_GROUP:
+ case SAM_DELTA_DELETE_USER:
+ default:
+ DEBUG(0, ("Replication error: Unknown delta type 0x%x\n", type));
+ break;
+ }
+
+ return True;
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+BOOL net_io_r_sam_sync(const char *desc, uint8 sess_key[16],
+ NET_R_SAM_SYNC * r_s, prs_struct *ps, int depth)
+{
+ uint32 i;
+
+ prs_debug(ps, depth, desc, "net_io_r_sam_sync");
+ depth++;
+
+ if (!smb_io_cred("srv_creds", &r_s->srv_creds, ps, depth))
+ return False;
+ if (!prs_uint32("sync_context", ps, depth, &r_s->sync_context))
+ return False;
+
+ if (!prs_uint32("ptr_deltas", ps, depth, &r_s->ptr_deltas))
+ return False;
+ if (r_s->ptr_deltas != 0)
+ {
+ if (!prs_uint32("num_deltas ", ps, depth, &r_s->num_deltas))
+ return False;
+ if (!prs_uint32("ptr_deltas2", ps, depth, &r_s->ptr_deltas2))
+ return False;
+ if (r_s->ptr_deltas2 != 0)
+ {
+ if (!prs_uint32("num_deltas2", ps, depth,
+ &r_s->num_deltas2))
+ return False;
+
+ if (r_s->num_deltas2 != r_s->num_deltas)
+ {
+ /* RPC fault */
+ return False;
+ }
+
+ if (r_s->num_deltas2 > 0) {
+ r_s->hdr_deltas = (SAM_DELTA_HDR *)
+ talloc(ps->mem_ctx, r_s->num_deltas2 *
+ sizeof(SAM_DELTA_HDR));
+
+ if (r_s->hdr_deltas == NULL) {
+ DEBUG(0, ("error tallocating memory "
+ "for %d delta headers\n",
+ r_s->num_deltas2));
+ return False;
+ }
+ }
+
+ for (i = 0; i < r_s->num_deltas2; i++)
+ {
+ if (!net_io_sam_delta_hdr("",
+ &r_s->hdr_deltas[i],
+ ps, depth))
+ return False;
+ }
+
+ if (r_s->num_deltas2 > 0) {
+ r_s->deltas = (SAM_DELTA_CTR *)
+ talloc(ps->mem_ctx, r_s->num_deltas2 *
+ sizeof(SAM_DELTA_CTR));
+
+ if (r_s->deltas == NULL) {
+ DEBUG(0, ("error tallocating memory "
+ "for %d deltas\n",
+ r_s->num_deltas2));
+ return False;
+ }
+ }
+
+ for (i = 0; i < r_s->num_deltas2; i++)
+ {
+ if (!net_io_sam_delta_ctr(
+ "", sess_key, &r_s->deltas[i],
+ r_s->hdr_deltas[i].type3,
+ ps, depth)) {
+ DEBUG(0, ("hmm, failed on i=%d\n", i));
+ return False;
+ }
+ }
+ }
+ }
+
+ prs_align(ps);
+ if (!prs_ntstatus("status", ps, depth, &(r_s->status)))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+makes a NET_Q_SAM_DELTAS structure.
+********************************************************************/
+BOOL init_net_q_sam_deltas(NET_Q_SAM_DELTAS *q_s, const char *srv_name,
+ const char *cli_name, DOM_CRED *cli_creds,
+ uint32 database_id, UINT64_S dom_mod_count)
+{
+ DEBUG(5, ("init_net_q_sam_deltas\n"));
+
+ init_unistr2(&q_s->uni_srv_name, srv_name, UNI_STR_TERMINATE);
+ init_unistr2(&q_s->uni_cli_name, cli_name, UNI_STR_TERMINATE);
+
+ memcpy(&q_s->cli_creds, cli_creds, sizeof(q_s->cli_creds));
+ memset(&q_s->ret_creds, 0, sizeof(q_s->ret_creds));
+
+ q_s->database_id = database_id;
+ q_s->dom_mod_count.low = dom_mod_count.low;
+ q_s->dom_mod_count.high = dom_mod_count.high;
+ q_s->max_size = 0xffff;
+
+ return True;
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+BOOL net_io_q_sam_deltas(const char *desc, NET_Q_SAM_DELTAS *q_s, prs_struct *ps,
+ int depth)
+{
+ prs_debug(ps, depth, desc, "net_io_q_sam_deltas");
+ depth++;
+
+ if (!smb_io_unistr2("", &q_s->uni_srv_name, True, ps, depth))
+ return False;
+ if (!smb_io_unistr2("", &q_s->uni_cli_name, True, ps, depth))
+ return False;
+
+ if (!smb_io_cred("", &q_s->cli_creds, ps, depth))
+ return False;
+ if (!smb_io_cred("", &q_s->ret_creds, ps, depth))
+ return False;
+
+ if (!prs_uint32("database_id ", ps, depth, &q_s->database_id))
+ return False;
+ if (!prs_uint64("dom_mod_count", ps, depth, &q_s->dom_mod_count))
+ return False;
+ if (!prs_uint32("max_size", ps, depth, &q_s->max_size))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+BOOL net_io_r_sam_deltas(const char *desc, uint8 sess_key[16],
+ NET_R_SAM_DELTAS *r_s, prs_struct *ps, int depth)
+{
+ unsigned int i;
+
+ prs_debug(ps, depth, desc, "net_io_r_sam_deltas");
+ depth++;
+
+ if (!smb_io_cred("srv_creds", &r_s->srv_creds, ps, depth))
+ return False;
+ if (!prs_uint64("dom_mod_count", ps, depth, &r_s->dom_mod_count))
+ return False;
+
+ if (!prs_uint32("ptr_deltas", ps, depth, &r_s->ptr_deltas))
+ return False;
+ if (!prs_uint32("num_deltas", ps, depth, &r_s->num_deltas))
+ return False;
+ if (!prs_uint32("ptr_deltas2", ps, depth, &r_s->num_deltas2))
+ return False;
+
+ if (r_s->num_deltas2 != 0)
+ {
+ if (!prs_uint32("num_deltas2 ", ps, depth, &r_s->num_deltas2))
+ return False;
+
+ if (r_s->ptr_deltas != 0)
+ {
+ if (r_s->num_deltas > 0) {
+ r_s->hdr_deltas = (SAM_DELTA_HDR *)
+ talloc(ps->mem_ctx, r_s->num_deltas *
+ sizeof(SAM_DELTA_HDR));
+ if (r_s->hdr_deltas == NULL) {
+ DEBUG(0, ("error tallocating memory "
+ "for %d delta headers\n",
+ r_s->num_deltas));
+ return False;
+ }
+ }
+
+ for (i = 0; i < r_s->num_deltas; i++)
+ {
+ net_io_sam_delta_hdr("", &r_s->hdr_deltas[i],
+ ps, depth);
+ }
+
+ if (r_s->num_deltas > 0) {
+ r_s->deltas = (SAM_DELTA_CTR *)
+ talloc(ps->mem_ctx, r_s->num_deltas *
+ sizeof(SAM_DELTA_CTR));
+
+ if (r_s->deltas == NULL) {
+ DEBUG(0, ("error tallocating memory "
+ "for %d deltas\n",
+ r_s->num_deltas));
+ return False;
+ }
+ }
+
+ for (i = 0; i < r_s->num_deltas; i++)
+ {
+ if (!net_io_sam_delta_ctr(
+ "", sess_key,
+ &r_s->deltas[i],
+ r_s->hdr_deltas[i].type2,
+ ps, depth))
+
+ return False;
+ }
+ }
+ }
+
+ prs_align(ps);
+ if (!prs_ntstatus("status", ps, depth, &r_s->status))
+ return False;
+
+ return True;
+}
diff --git a/source/rpc_parse/parse_prs.c b/source/rpc_parse/parse_prs.c
new file mode 100644
index 00000000000..0e5a25fe8c2
--- /dev/null
+++ b/source/rpc_parse/parse_prs.c
@@ -0,0 +1,1624 @@
+/*
+ Unix SMB/CIFS implementation.
+ Samba memory buffer functions
+ Copyright (C) Andrew Tridgell 1992-1997
+ Copyright (C) Luke Kenneth Casson Leighton 1996-1997
+ Copyright (C) Jeremy Allison 1999
+ Copyright (C) Andrew Bartlett 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 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"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_RPC_PARSE
+
+/**
+ * Dump a prs to a file: from the current location through to the end.
+ **/
+void prs_dump(char *name, int v, prs_struct *ps)
+{
+ prs_dump_region(name, v, ps, ps->data_offset, ps->buffer_size);
+}
+
+
+/**
+ * Dump from the start of the prs to the current location.
+ **/
+void prs_dump_before(char *name, int v, prs_struct *ps)
+{
+ prs_dump_region(name, v, ps, 0, ps->data_offset);
+}
+
+
+/**
+ * Dump everything from the start of the prs up to the current location.
+ **/
+void prs_dump_region(char *name, int v, prs_struct *ps,
+ int from_off, int to_off)
+{
+ int fd, i;
+ pstring fname;
+ if (DEBUGLEVEL < 50) return;
+ for (i=1;i<100;i++) {
+ if (v != -1) {
+ slprintf(fname,sizeof(fname)-1, "/tmp/%s_%d.%d.prs", name, v, i);
+ } else {
+ slprintf(fname,sizeof(fname)-1, "/tmp/%s.%d.prs", name, i);
+ }
+ fd = open(fname, O_WRONLY|O_CREAT|O_EXCL, 0644);
+ if (fd != -1 || errno != EEXIST) break;
+ }
+ if (fd != -1) {
+ write(fd, ps->data_p + from_off, to_off - from_off);
+ close(fd);
+ DEBUG(0,("created %s\n", fname));
+ }
+}
+
+
+
+/*******************************************************************
+ debug output for parsing info.
+
+ XXXX side-effect of this function is to increase the debug depth XXXX
+
+ ********************************************************************/
+void prs_debug(prs_struct *ps, int depth, const char *desc, const char *fn_name)
+{
+ DEBUG(5+depth, ("%s%06x %s %s\n", tab_depth(depth), ps->data_offset, fn_name, desc));
+}
+
+
+/**
+ * Initialise an expandable parse structure.
+ *
+ * @param size Initial buffer size. If >0, a new buffer will be
+ * created with malloc().
+ *
+ * @return False if allocation fails, otherwise True.
+ **/
+BOOL prs_init(prs_struct *ps, uint32 size, TALLOC_CTX *ctx, BOOL io)
+{
+ ZERO_STRUCTP(ps);
+ ps->io = io;
+ ps->bigendian_data = RPC_LITTLE_ENDIAN;
+ ps->align = RPC_PARSE_ALIGN;
+ ps->is_dynamic = False;
+ ps->data_offset = 0;
+ ps->buffer_size = 0;
+ ps->data_p = NULL;
+ ps->mem_ctx = ctx;
+
+ if (size != 0) {
+ ps->buffer_size = size;
+ if((ps->data_p = (char *)malloc((size_t)size)) == NULL) {
+ DEBUG(0,("prs_init: malloc fail for %u bytes.\n", (unsigned int)size));
+ return False;
+ }
+ memset(ps->data_p, '\0', (size_t)size);
+ ps->is_dynamic = True; /* We own this memory. */
+ }
+
+ return True;
+}
+
+/*******************************************************************
+ Delete the memory in a parse structure - if we own it.
+ ********************************************************************/
+
+void prs_mem_free(prs_struct *ps)
+{
+ if(ps->is_dynamic)
+ SAFE_FREE(ps->data_p);
+ ps->is_dynamic = False;
+ ps->buffer_size = 0;
+ ps->data_offset = 0;
+}
+
+/*******************************************************************
+ Clear the memory in a parse structure.
+ ********************************************************************/
+
+void prs_mem_clear(prs_struct *ps)
+{
+ if (ps->buffer_size)
+ memset(ps->data_p, '\0', (size_t)ps->buffer_size);
+}
+
+/*******************************************************************
+ Allocate memory when unmarshalling... Always zero clears.
+ ********************************************************************/
+
+char *prs_alloc_mem(prs_struct *ps, size_t size)
+{
+ char *ret = NULL;
+
+ if (size) {
+ ret = talloc(ps->mem_ctx, size);
+ if (ret)
+ memset(ret, '\0', size);
+ }
+ return ret;
+}
+
+/*******************************************************************
+ Return the current talloc context we're using.
+ ********************************************************************/
+
+TALLOC_CTX *prs_get_mem_context(prs_struct *ps)
+{
+ return ps->mem_ctx;
+}
+
+/*******************************************************************
+ Hand some already allocated memory to a prs_struct.
+ ********************************************************************/
+
+void prs_give_memory(prs_struct *ps, char *buf, uint32 size, BOOL is_dynamic)
+{
+ ps->is_dynamic = is_dynamic;
+ ps->data_p = buf;
+ ps->buffer_size = size;
+}
+
+/*******************************************************************
+ Take some memory back from a prs_struct.
+ ********************************************************************/
+
+char *prs_take_memory(prs_struct *ps, uint32 *psize)
+{
+ char *ret = ps->data_p;
+ if(psize)
+ *psize = ps->buffer_size;
+ ps->is_dynamic = False;
+ prs_mem_free(ps);
+ return ret;
+}
+
+/*******************************************************************
+ Set a prs_struct to exactly a given size. Will grow or tuncate if neccessary.
+ ********************************************************************/
+
+BOOL prs_set_buffer_size(prs_struct *ps, uint32 newsize)
+{
+ if (newsize > ps->buffer_size)
+ return prs_force_grow(ps, newsize - ps->buffer_size);
+
+ if (newsize < ps->buffer_size) {
+ char *new_data_p = Realloc(ps->data_p, newsize);
+ /* if newsize is zero, Realloc acts like free() & returns NULL*/
+ if (new_data_p == NULL && newsize != 0) {
+ DEBUG(0,("prs_set_buffer_size: Realloc failure for size %u.\n",
+ (unsigned int)newsize));
+ DEBUG(0,("prs_set_buffer_size: Reason %s\n",strerror(errno)));
+ return False;
+ }
+ ps->data_p = new_data_p;
+ ps->buffer_size = newsize;
+ }
+
+ return True;
+}
+
+/*******************************************************************
+ Attempt, if needed, to grow a data buffer.
+ Also depends on the data stream mode (io).
+ ********************************************************************/
+
+BOOL prs_grow(prs_struct *ps, uint32 extra_space)
+{
+ uint32 new_size;
+ char *new_data;
+
+ ps->grow_size = MAX(ps->grow_size, ps->data_offset + extra_space);
+
+ if(ps->data_offset + extra_space <= ps->buffer_size)
+ return True;
+
+ /*
+ * We cannot grow the buffer if we're not reading
+ * into the prs_struct, or if we don't own the memory.
+ */
+
+ if(UNMARSHALLING(ps) || !ps->is_dynamic) {
+ DEBUG(0,("prs_grow: Buffer overflow - unable to expand buffer by %u bytes.\n",
+ (unsigned int)extra_space));
+ return False;
+ }
+
+ /*
+ * Decide how much extra space we really need.
+ */
+
+ extra_space -= (ps->buffer_size - ps->data_offset);
+ if(ps->buffer_size == 0) {
+ /*
+ * Ensure we have at least a PDU's length, or extra_space, whichever
+ * is greater.
+ */
+
+ new_size = MAX(MAX_PDU_FRAG_LEN,extra_space);
+
+ if((new_data = malloc(new_size)) == NULL) {
+ DEBUG(0,("prs_grow: Malloc failure for size %u.\n", (unsigned int)new_size));
+ return False;
+ }
+ memset(new_data, '\0', (size_t)new_size );
+ } else {
+ /*
+ * If the current buffer size is bigger than the space needed, just
+ * double it, else add extra_space.
+ */
+ new_size = MAX(ps->buffer_size*2, ps->buffer_size + extra_space);
+
+ if ((new_data = Realloc(ps->data_p, new_size)) == NULL) {
+ DEBUG(0,("prs_grow: Realloc failure for size %u.\n",
+ (unsigned int)new_size));
+ return False;
+ }
+
+ memset(&new_data[ps->buffer_size], '\0', (size_t)(new_size - ps->buffer_size));
+ }
+ ps->buffer_size = new_size;
+ ps->data_p = new_data;
+
+ return True;
+}
+
+/*******************************************************************
+ Attempt to force a data buffer to grow by len bytes.
+ This is only used when appending more data onto a prs_struct
+ when reading an rpc reply, before unmarshalling it.
+ ********************************************************************/
+
+BOOL prs_force_grow(prs_struct *ps, uint32 extra_space)
+{
+ uint32 new_size = ps->buffer_size + extra_space;
+ char *new_data;
+
+ if(!UNMARSHALLING(ps) || !ps->is_dynamic) {
+ DEBUG(0,("prs_force_grow: Buffer overflow - unable to expand buffer by %u bytes.\n",
+ (unsigned int)extra_space));
+ return False;
+ }
+
+ if((new_data = Realloc(ps->data_p, new_size)) == NULL) {
+ DEBUG(0,("prs_force_grow: Realloc failure for size %u.\n",
+ (unsigned int)new_size));
+ return False;
+ }
+
+ memset(&new_data[ps->buffer_size], '\0', (size_t)(new_size - ps->buffer_size));
+
+ ps->buffer_size = new_size;
+ ps->data_p = new_data;
+
+ return True;
+}
+
+/*******************************************************************
+ Get the data pointer (external interface).
+********************************************************************/
+
+char *prs_data_p(prs_struct *ps)
+{
+ return ps->data_p;
+}
+
+/*******************************************************************
+ Get the current data size (external interface).
+ ********************************************************************/
+
+uint32 prs_data_size(prs_struct *ps)
+{
+ return ps->buffer_size;
+}
+
+/*******************************************************************
+ Fetch the current offset (external interface).
+ ********************************************************************/
+
+uint32 prs_offset(prs_struct *ps)
+{
+ return ps->data_offset;
+}
+
+/*******************************************************************
+ Set the current offset (external interface).
+ ********************************************************************/
+
+BOOL prs_set_offset(prs_struct *ps, uint32 offset)
+{
+ if(offset <= ps->data_offset) {
+ ps->data_offset = offset;
+ return True;
+ }
+
+ if(!prs_grow(ps, offset - ps->data_offset))
+ return False;
+
+ ps->data_offset = offset;
+ return True;
+}
+
+/*******************************************************************
+ Append the data from one parse_struct into another.
+ ********************************************************************/
+
+BOOL prs_append_prs_data(prs_struct *dst, prs_struct *src)
+{
+ if (prs_offset(src) == 0)
+ return True;
+
+ if(!prs_grow(dst, prs_offset(src)))
+ return False;
+
+ memcpy(&dst->data_p[dst->data_offset], src->data_p, (size_t)prs_offset(src));
+ dst->data_offset += prs_offset(src);
+
+ return True;
+}
+
+/*******************************************************************
+ Append some data from one parse_struct into another.
+ ********************************************************************/
+
+BOOL prs_append_some_prs_data(prs_struct *dst, prs_struct *src, int32 start, uint32 len)
+{
+ if (len == 0)
+ return True;
+
+ if(!prs_grow(dst, len))
+ return False;
+
+ memcpy(&dst->data_p[dst->data_offset], src->data_p + start, (size_t)len);
+ dst->data_offset += len;
+
+ return True;
+}
+
+/*******************************************************************
+ Append the data from a buffer into a parse_struct.
+ ********************************************************************/
+
+BOOL prs_copy_data_in(prs_struct *dst, char *src, uint32 len)
+{
+ if (len == 0)
+ return True;
+
+ if(!prs_grow(dst, len))
+ return False;
+
+ memcpy(&dst->data_p[dst->data_offset], src, (size_t)len);
+ dst->data_offset += len;
+
+ return True;
+}
+
+/*******************************************************************
+ Copy some data from a parse_struct into a buffer.
+ ********************************************************************/
+
+BOOL prs_copy_data_out(char *dst, prs_struct *src, uint32 len)
+{
+ if (len == 0)
+ return True;
+
+ if(!prs_mem_get(src, len))
+ return False;
+
+ memcpy(dst, &src->data_p[src->data_offset], (size_t)len);
+ src->data_offset += len;
+
+ return True;
+}
+
+/*******************************************************************
+ Copy all the data from a parse_struct into a buffer.
+ ********************************************************************/
+
+BOOL prs_copy_all_data_out(char *dst, prs_struct *src)
+{
+ uint32 len = prs_offset(src);
+
+ if (!len)
+ return True;
+
+ prs_set_offset(src, 0);
+ return prs_copy_data_out(dst, src, len);
+}
+
+/*******************************************************************
+ Set the data as X-endian (external interface).
+ ********************************************************************/
+
+void prs_set_endian_data(prs_struct *ps, BOOL endian)
+{
+ ps->bigendian_data = endian;
+}
+
+/*******************************************************************
+ Align a the data_len to a multiple of align bytes - filling with
+ zeros.
+ ********************************************************************/
+
+BOOL prs_align(prs_struct *ps)
+{
+ uint32 mod = ps->data_offset & (ps->align-1);
+
+ if (ps->align != 0 && mod != 0) {
+ uint32 extra_space = (ps->align - mod);
+ if(!prs_grow(ps, extra_space))
+ return False;
+ memset(&ps->data_p[ps->data_offset], '\0', (size_t)extra_space);
+ ps->data_offset += extra_space;
+ }
+
+ return True;
+}
+
+/******************************************************************
+ Align on a 2 byte boundary
+ *****************************************************************/
+
+BOOL prs_align_uint16(prs_struct *ps)
+{
+ BOOL ret;
+ uint8 old_align = ps->align;
+
+ ps->align = 2;
+ ret = prs_align(ps);
+ ps->align = old_align;
+
+ return ret;
+}
+
+/******************************************************************
+ Align on a 8 byte boundary
+ *****************************************************************/
+
+BOOL prs_align_uint64(prs_struct *ps)
+{
+ BOOL ret;
+ uint8 old_align = ps->align;
+
+ ps->align = 8;
+ ret = prs_align(ps);
+ ps->align = old_align;
+
+ return ret;
+}
+
+/*******************************************************************
+ Align only if required (for the unistr2 string mainly)
+ ********************************************************************/
+
+BOOL prs_align_needed(prs_struct *ps, uint32 needed)
+{
+ if (needed==0)
+ return True;
+ else
+ return prs_align(ps);
+}
+
+/*******************************************************************
+ Ensure we can read/write to a given offset.
+ ********************************************************************/
+
+char *prs_mem_get(prs_struct *ps, uint32 extra_size)
+{
+ if(UNMARSHALLING(ps)) {
+ /*
+ * If reading, ensure that we can read the requested size item.
+ */
+ if (ps->data_offset + extra_size > ps->buffer_size) {
+ DEBUG(0,("prs_mem_get: reading data of size %u would overrun buffer.\n",
+ (unsigned int)extra_size ));
+ return NULL;
+ }
+ } else {
+ /*
+ * Writing - grow the buffer if needed.
+ */
+ if(!prs_grow(ps, extra_size))
+ return NULL;
+ }
+ return &ps->data_p[ps->data_offset];
+}
+
+/*******************************************************************
+ Change the struct type.
+ ********************************************************************/
+
+void prs_switch_type(prs_struct *ps, BOOL io)
+{
+ if ((ps->io ^ io) == True)
+ ps->io=io;
+}
+
+/*******************************************************************
+ Force a prs_struct to be dynamic even when it's size is 0.
+ ********************************************************************/
+
+void prs_force_dynamic(prs_struct *ps)
+{
+ ps->is_dynamic=True;
+}
+
+/*******************************************************************
+ Stream a uint8.
+ ********************************************************************/
+
+BOOL prs_uint8(const char *name, prs_struct *ps, int depth, uint8 *data8)
+{
+ char *q = prs_mem_get(ps, 1);
+ if (q == NULL)
+ return False;
+
+ if (UNMARSHALLING(ps))
+ *data8 = CVAL(q,0);
+ else
+ SCVAL(q,0,*data8);
+
+ DEBUG(5,("%s%04x %s: %02x\n", tab_depth(depth), ps->data_offset, name, *data8));
+
+ ps->data_offset += 1;
+
+ return True;
+}
+
+/*******************************************************************
+ Stream a uint16.
+ ********************************************************************/
+
+BOOL prs_uint16(const char *name, prs_struct *ps, int depth, uint16 *data16)
+{
+ char *q = prs_mem_get(ps, sizeof(uint16));
+ if (q == NULL)
+ return False;
+
+ if (UNMARSHALLING(ps)) {
+ if (ps->bigendian_data)
+ *data16 = RSVAL(q,0);
+ else
+ *data16 = SVAL(q,0);
+ } else {
+ if (ps->bigendian_data)
+ RSSVAL(q,0,*data16);
+ else
+ SSVAL(q,0,*data16);
+ }
+
+ DEBUG(5,("%s%04x %s: %04x\n", tab_depth(depth), ps->data_offset, name, *data16));
+
+ ps->data_offset += sizeof(uint16);
+
+ return True;
+}
+
+/*******************************************************************
+ Stream a uint32.
+ ********************************************************************/
+
+BOOL prs_uint32(const char *name, prs_struct *ps, int depth, uint32 *data32)
+{
+ char *q = prs_mem_get(ps, sizeof(uint32));
+ if (q == NULL)
+ return False;
+
+ if (UNMARSHALLING(ps)) {
+ if (ps->bigendian_data)
+ *data32 = RIVAL(q,0);
+ else
+ *data32 = IVAL(q,0);
+ } else {
+ if (ps->bigendian_data)
+ RSIVAL(q,0,*data32);
+ else
+ SIVAL(q,0,*data32);
+ }
+
+ DEBUG(5,("%s%04x %s: %08x\n", tab_depth(depth), ps->data_offset, name, *data32));
+
+ ps->data_offset += sizeof(uint32);
+
+ return True;
+}
+
+/*******************************************************************
+ Stream a NTSTATUS
+ ********************************************************************/
+
+BOOL prs_ntstatus(const char *name, prs_struct *ps, int depth, NTSTATUS *status)
+{
+ char *q = prs_mem_get(ps, sizeof(uint32));
+ if (q == NULL)
+ return False;
+
+ if (UNMARSHALLING(ps)) {
+ if (ps->bigendian_data)
+ *status = NT_STATUS(RIVAL(q,0));
+ else
+ *status = NT_STATUS(IVAL(q,0));
+ } else {
+ if (ps->bigendian_data)
+ RSIVAL(q,0,NT_STATUS_V(*status));
+ else
+ SIVAL(q,0,NT_STATUS_V(*status));
+ }
+
+ DEBUG(5,("%s%04x %s: %s\n", tab_depth(depth), ps->data_offset, name,
+ nt_errstr(*status)));
+
+ ps->data_offset += sizeof(uint32);
+
+ return True;
+}
+
+/*******************************************************************
+ Stream a WERROR
+ ********************************************************************/
+
+BOOL prs_werror(const char *name, prs_struct *ps, int depth, WERROR *status)
+{
+ char *q = prs_mem_get(ps, sizeof(uint32));
+ if (q == NULL)
+ return False;
+
+ if (UNMARSHALLING(ps)) {
+ if (ps->bigendian_data)
+ *status = W_ERROR(RIVAL(q,0));
+ else
+ *status = W_ERROR(IVAL(q,0));
+ } else {
+ if (ps->bigendian_data)
+ RSIVAL(q,0,W_ERROR_V(*status));
+ else
+ SIVAL(q,0,W_ERROR_V(*status));
+ }
+
+ DEBUG(5,("%s%04x %s: %s\n", tab_depth(depth), ps->data_offset, name,
+ dos_errstr(*status)));
+
+ ps->data_offset += sizeof(uint32);
+
+ return True;
+}
+
+
+/******************************************************************
+ Stream an array of uint8s. Length is number of uint8s.
+ ********************************************************************/
+
+BOOL prs_uint8s(BOOL charmode, const char *name, prs_struct *ps, int depth, uint8 *data8s, int len)
+{
+ int i;
+ char *q = prs_mem_get(ps, len);
+ if (q == NULL)
+ return False;
+
+ if (UNMARSHALLING(ps)) {
+ for (i = 0; i < len; i++)
+ data8s[i] = CVAL(q,i);
+ } else {
+ for (i = 0; i < len; i++)
+ SCVAL(q, i, data8s[i]);
+ }
+
+ DEBUG(5,("%s%04x %s: ", tab_depth(depth), ps->data_offset ,name));
+ if (charmode)
+ print_asc(5, (unsigned char*)data8s, len);
+ else {
+ for (i = 0; i < len; i++)
+ DEBUG(5,("%02x ", data8s[i]));
+ }
+ DEBUG(5,("\n"));
+
+ ps->data_offset += len;
+
+ return True;
+}
+
+/******************************************************************
+ Stream an array of uint16s. Length is number of uint16s.
+ ********************************************************************/
+
+BOOL prs_uint16s(BOOL charmode, const char *name, prs_struct *ps, int depth, uint16 *data16s, int len)
+{
+ int i;
+ char *q = prs_mem_get(ps, len * sizeof(uint16));
+ if (q == NULL)
+ return False;
+
+ if (UNMARSHALLING(ps)) {
+ if (ps->bigendian_data) {
+ for (i = 0; i < len; i++)
+ data16s[i] = RSVAL(q, 2*i);
+ } else {
+ for (i = 0; i < len; i++)
+ data16s[i] = SVAL(q, 2*i);
+ }
+ } else {
+ if (ps->bigendian_data) {
+ for (i = 0; i < len; i++)
+ RSSVAL(q, 2*i, data16s[i]);
+ } else {
+ for (i = 0; i < len; i++)
+ SSVAL(q, 2*i, data16s[i]);
+ }
+ }
+
+ DEBUG(5,("%s%04x %s: ", tab_depth(depth), ps->data_offset, name));
+ if (charmode)
+ print_asc(5, (unsigned char*)data16s, 2*len);
+ else {
+ for (i = 0; i < len; i++)
+ DEBUG(5,("%04x ", data16s[i]));
+ }
+ DEBUG(5,("\n"));
+
+ ps->data_offset += (len * sizeof(uint16));
+
+ return True;
+}
+
+/******************************************************************
+ Start using a function for streaming unicode chars. If unmarshalling,
+ output must be little-endian, if marshalling, input must be little-endian.
+ ********************************************************************/
+
+static void dbg_rw_punival(BOOL charmode, const char *name, int depth, prs_struct *ps,
+ char *in_buf, char *out_buf, int len)
+{
+ int i;
+
+ if (UNMARSHALLING(ps)) {
+ if (ps->bigendian_data) {
+ for (i = 0; i < len; i++)
+ SSVAL(out_buf,2*i,RSVAL(in_buf, 2*i));
+ } else {
+ for (i = 0; i < len; i++)
+ SSVAL(out_buf, 2*i, SVAL(in_buf, 2*i));
+ }
+ } else {
+ if (ps->bigendian_data) {
+ for (i = 0; i < len; i++)
+ RSSVAL(in_buf, 2*i, SVAL(out_buf,2*i));
+ } else {
+ for (i = 0; i < len; i++)
+ SSVAL(in_buf, 2*i, SVAL(out_buf,2*i));
+ }
+ }
+
+ DEBUG(5,("%s%04x %s: ", tab_depth(depth), ps->data_offset, name));
+ if (charmode)
+ print_asc(5, (unsigned char*)out_buf, 2*len);
+ else {
+ for (i = 0; i < len; i++)
+ DEBUG(5,("%04x ", out_buf[i]));
+ }
+ DEBUG(5,("\n"));
+}
+
+/******************************************************************
+ Stream a unistr. Always little endian.
+ ********************************************************************/
+
+BOOL prs_uint16uni(BOOL charmode, const char *name, prs_struct *ps, int depth, uint16 *data16s, int len)
+{
+ char *q = prs_mem_get(ps, len * sizeof(uint16));
+ if (q == NULL)
+ return False;
+
+ dbg_rw_punival(charmode, name, depth, ps, q, (char *)data16s, len);
+ ps->data_offset += (len * sizeof(uint16));
+
+ return True;
+}
+
+/******************************************************************
+ Stream an array of uint32s. Length is number of uint32s.
+ ********************************************************************/
+
+BOOL prs_uint32s(BOOL charmode, const char *name, prs_struct *ps, int depth, uint32 *data32s, int len)
+{
+ int i;
+ char *q = prs_mem_get(ps, len * sizeof(uint32));
+ if (q == NULL)
+ return False;
+
+ if (UNMARSHALLING(ps)) {
+ if (ps->bigendian_data) {
+ for (i = 0; i < len; i++)
+ data32s[i] = RIVAL(q, 4*i);
+ } else {
+ for (i = 0; i < len; i++)
+ data32s[i] = IVAL(q, 4*i);
+ }
+ } else {
+ if (ps->bigendian_data) {
+ for (i = 0; i < len; i++)
+ RSIVAL(q, 4*i, data32s[i]);
+ } else {
+ for (i = 0; i < len; i++)
+ SIVAL(q, 4*i, data32s[i]);
+ }
+ }
+
+ DEBUG(5,("%s%04x %s: ", tab_depth(depth), ps->data_offset, name));
+ if (charmode)
+ print_asc(5, (unsigned char*)data32s, 4*len);
+ else {
+ for (i = 0; i < len; i++)
+ DEBUG(5,("%08x ", data32s[i]));
+ }
+ DEBUG(5,("\n"));
+
+ ps->data_offset += (len * sizeof(uint32));
+
+ return True;
+}
+
+/******************************************************************
+ Stream an array of unicode string, length/buffer specified separately,
+ in uint16 chars. The unicode string is already in little-endian format.
+ ********************************************************************/
+
+BOOL prs_buffer5(BOOL charmode, const char *name, prs_struct *ps, int depth, BUFFER5 *str)
+{
+ char *p;
+ char *q = prs_mem_get(ps, str->buf_len * sizeof(uint16));
+ if (q == NULL)
+ return False;
+
+ if (UNMARSHALLING(ps)) {
+ str->buffer = (uint16 *)prs_alloc_mem(ps,str->buf_len * sizeof(uint16));
+ if (str->buffer == NULL)
+ return False;
+ }
+
+ /* If the string is empty, we don't have anything to stream */
+ if (str->buf_len==0)
+ return True;
+
+ p = (char *)str->buffer;
+
+ dbg_rw_punival(charmode, name, depth, ps, q, p, str->buf_len);
+
+ ps->data_offset += (str->buf_len * sizeof(uint16));
+
+ return True;
+}
+
+/******************************************************************
+ Stream a "not" unicode string, length/buffer specified separately,
+ in byte chars. String is in little-endian format.
+ ********************************************************************/
+
+BOOL prs_buffer2(BOOL charmode, const char *name, prs_struct *ps, int depth, BUFFER2 *str)
+{
+ char *p;
+ char *q = prs_mem_get(ps, str->buf_len);
+ if (q == NULL)
+ return False;
+
+ if (UNMARSHALLING(ps)) {
+ if ( str->buf_len ) {
+ str->buffer = (uint16 *)prs_alloc_mem(ps,str->buf_len);
+ if ( str->buffer == NULL )
+ return False;
+ }
+ }
+
+ p = (char *)str->buffer;
+
+ dbg_rw_punival(charmode, name, depth, ps, q, p, str->buf_len/2);
+ ps->data_offset += str->buf_len;
+
+ return True;
+}
+
+/******************************************************************
+ Stream a string, length/buffer specified separately,
+ in uint8 chars.
+ ********************************************************************/
+
+BOOL prs_string2(BOOL charmode, const char *name, prs_struct *ps, int depth, STRING2 *str)
+{
+ unsigned int i;
+ char *q = prs_mem_get(ps, str->str_max_len);
+ if (q == NULL)
+ return False;
+
+ if (UNMARSHALLING(ps)) {
+ str->buffer = (unsigned char *)prs_alloc_mem(ps,str->str_max_len);
+ if (str->buffer == NULL)
+ return False;
+ }
+
+ if (UNMARSHALLING(ps)) {
+ for (i = 0; i < str->str_str_len; i++)
+ str->buffer[i] = CVAL(q,i);
+ } else {
+ for (i = 0; i < str->str_str_len; i++)
+ SCVAL(q, i, str->buffer[i]);
+ }
+
+ DEBUG(5,("%s%04x %s: ", tab_depth(depth), ps->data_offset, name));
+ if (charmode)
+ print_asc(5, (unsigned char*)str->buffer, str->str_str_len);
+ else {
+ for (i = 0; i < str->str_str_len; i++)
+ DEBUG(5,("%02x ", str->buffer[i]));
+ }
+ DEBUG(5,("\n"));
+
+ ps->data_offset += str->str_str_len;
+
+ return True;
+}
+
+/******************************************************************
+ Stream a unicode string, length/buffer specified separately,
+ in uint16 chars. The unicode string is already in little-endian format.
+ ********************************************************************/
+
+BOOL prs_unistr2(BOOL charmode, const char *name, prs_struct *ps, int depth, UNISTR2 *str)
+{
+ char *p;
+ char *q = prs_mem_get(ps, str->uni_str_len * sizeof(uint16));
+ if (q == NULL)
+ return False;
+
+ /* If the string is empty, we don't have anything to stream */
+ if (str->uni_str_len==0)
+ return True;
+
+ if (UNMARSHALLING(ps)) {
+ str->buffer = (uint16 *)prs_alloc_mem(ps,str->uni_max_len * sizeof(uint16));
+ if (str->buffer == NULL)
+ return False;
+ }
+
+ p = (char *)str->buffer;
+
+ dbg_rw_punival(charmode, name, depth, ps, q, p, str->uni_str_len);
+
+ ps->data_offset += (str->uni_str_len * sizeof(uint16));
+
+ return True;
+}
+
+/******************************************************************
+ Stream a unicode string, length/buffer specified separately,
+ in uint16 chars. The unicode string is already in little-endian format.
+ ********************************************************************/
+
+BOOL prs_unistr3(BOOL charmode, const char *name, UNISTR3 *str, prs_struct *ps, int depth)
+{
+ char *p;
+ char *q = prs_mem_get(ps, str->uni_str_len * sizeof(uint16));
+ if (q == NULL)
+ return False;
+
+ if (UNMARSHALLING(ps)) {
+ str->str.buffer = (uint16 *)prs_alloc_mem(ps,str->uni_str_len * sizeof(uint16));
+ if (str->str.buffer == NULL)
+ return False;
+ }
+
+ p = (char *)str->str.buffer;
+
+ dbg_rw_punival(charmode, name, depth, ps, q, p, str->uni_str_len);
+ ps->data_offset += (str->uni_str_len * sizeof(uint16));
+
+ return True;
+}
+
+/*******************************************************************
+ Stream a unicode null-terminated string. As the string is already
+ in little-endian format then do it as a stream of bytes.
+ ********************************************************************/
+
+BOOL prs_unistr(const char *name, prs_struct *ps, int depth, UNISTR *str)
+{
+ unsigned int len = 0;
+ unsigned char *p = (unsigned char *)str->buffer;
+ uint8 *start;
+ char *q;
+ uint32 max_len;
+ uint16* ptr;
+
+ if (MARSHALLING(ps)) {
+
+ for(len = 0; str->buffer[len] != 0; len++)
+ ;
+
+ q = prs_mem_get(ps, (len+1)*2);
+ if (q == NULL)
+ return False;
+
+ start = (uint8*)q;
+
+ for(len = 0; str->buffer[len] != 0; len++)
+ {
+ if(ps->bigendian_data)
+ {
+ /* swap bytes - p is little endian, q is big endian. */
+ q[0] = (char)p[1];
+ q[1] = (char)p[0];
+ p += 2;
+ q += 2;
+ }
+ else
+ {
+ q[0] = (char)p[0];
+ q[1] = (char)p[1];
+ p += 2;
+ q += 2;
+ }
+ }
+
+ /*
+ * even if the string is 'empty' (only an \0 char)
+ * at this point the leading \0 hasn't been parsed.
+ * so parse it now
+ */
+
+ q[0] = 0;
+ q[1] = 0;
+ q += 2;
+
+ len++;
+
+ DEBUG(5,("%s%04x %s: ", tab_depth(depth), ps->data_offset, name));
+ print_asc(5, (unsigned char*)start, 2*len);
+ DEBUG(5, ("\n"));
+ }
+ else { /* unmarshalling */
+
+ uint32 alloc_len = 0;
+ q = ps->data_p + prs_offset(ps);
+
+ /*
+ * Work out how much space we need and talloc it.
+ */
+ max_len = (ps->buffer_size - ps->data_offset)/sizeof(uint16);
+
+ /* the test of the value of *ptr helps to catch the circumstance
+ where we have an emtpty (non-existent) string in the buffer */
+ for ( ptr = (uint16 *)q; *ptr && (alloc_len <= max_len); alloc_len++)
+ /* do nothing */
+ ;
+
+ /* should we allocate anything at all? */
+ str->buffer = (uint16 *)prs_alloc_mem(ps,alloc_len * sizeof(uint16));
+ if ((str->buffer == NULL) && (alloc_len > 0))
+ return False;
+
+ p = (unsigned char *)str->buffer;
+
+ len = 0;
+ /* the (len < alloc_len) test is to prevent us from overwriting
+ memory that is not ours...if we get that far, we have a non-null
+ terminated string in the buffer and have messed up somewhere */
+ while ((len < alloc_len) && (*(uint16 *)q != 0))
+ {
+ if(ps->bigendian_data)
+ {
+ /* swap bytes - q is big endian, p is little endian. */
+ p[0] = (unsigned char)q[1];
+ p[1] = (unsigned char)q[0];
+ p += 2;
+ q += 2;
+ } else {
+
+ p[0] = (unsigned char)q[0];
+ p[1] = (unsigned char)q[1];
+ p += 2;
+ q += 2;
+ }
+
+ len++;
+ }
+ if (len < alloc_len)
+ {
+ /* NULL terminate the UNISTR */
+ str->buffer[len++] = '\0';
+ }
+
+ DEBUG(5,("%s%04x %s: ", tab_depth(depth), ps->data_offset, name));
+ print_asc(5, (unsigned char*)str->buffer, 2*len);
+ DEBUG(5, ("\n"));
+ }
+
+ /* set the offset in the prs_struct; 'len' points to the
+ terminiating NULL in the UNISTR so we need to go one more
+ uint16 */
+ ps->data_offset += (len)*2;
+
+ return True;
+}
+
+
+/*******************************************************************
+ Stream a null-terminated string. len is strlen, and therefore does
+ not include the null-termination character.
+ ********************************************************************/
+
+BOOL prs_string(const char *name, prs_struct *ps, int depth, char *str, int max_buf_size)
+{
+ char *q;
+ int i;
+ int len;
+
+ if (UNMARSHALLING(ps))
+ len = strlen(&ps->data_p[ps->data_offset]);
+ else
+ len = strlen(str);
+
+ len = MIN(len, (max_buf_size-1));
+
+ q = prs_mem_get(ps, len+1);
+ if (q == NULL)
+ return False;
+
+ for(i = 0; i < len; i++) {
+ if (UNMARSHALLING(ps))
+ str[i] = q[i];
+ else
+ q[i] = str[i];
+ }
+
+ /* The terminating null. */
+ str[i] = '\0';
+
+ if (MARSHALLING(ps)) {
+ q[i] = '\0';
+ }
+
+ ps->data_offset += len+1;
+
+ dump_data(5+depth, q, len);
+
+ return True;
+}
+
+/*******************************************************************
+ prs_uint16 wrapper. Call this and it sets up a pointer to where the
+ uint16 should be stored, or gets the size if reading.
+ ********************************************************************/
+
+BOOL prs_uint16_pre(const char *name, prs_struct *ps, int depth, uint16 *data16, uint32 *offset)
+{
+ *offset = ps->data_offset;
+ if (UNMARSHALLING(ps)) {
+ /* reading. */
+ return prs_uint16(name, ps, depth, data16);
+ } else {
+ char *q = prs_mem_get(ps, sizeof(uint16));
+ if(q ==NULL)
+ return False;
+ ps->data_offset += sizeof(uint16);
+ }
+ return True;
+}
+
+/*******************************************************************
+ prs_uint16 wrapper. call this and it retrospectively stores the size.
+ does nothing on reading, as that is already handled by ...._pre()
+ ********************************************************************/
+
+BOOL prs_uint16_post(const char *name, prs_struct *ps, int depth, uint16 *data16,
+ uint32 ptr_uint16, uint32 start_offset)
+{
+ if (MARSHALLING(ps)) {
+ /*
+ * Writing - temporarily move the offset pointer.
+ */
+ uint16 data_size = ps->data_offset - start_offset;
+ uint32 old_offset = ps->data_offset;
+
+ ps->data_offset = ptr_uint16;
+ if(!prs_uint16(name, ps, depth, &data_size)) {
+ ps->data_offset = old_offset;
+ return False;
+ }
+ ps->data_offset = old_offset;
+ } else {
+ ps->data_offset = start_offset + (uint32)(*data16);
+ }
+ return True;
+}
+
+/*******************************************************************
+ prs_uint32 wrapper. Call this and it sets up a pointer to where the
+ uint32 should be stored, or gets the size if reading.
+ ********************************************************************/
+
+BOOL prs_uint32_pre(const char *name, prs_struct *ps, int depth, uint32 *data32, uint32 *offset)
+{
+ *offset = ps->data_offset;
+ if (UNMARSHALLING(ps) && (data32 != NULL)) {
+ /* reading. */
+ return prs_uint32(name, ps, depth, data32);
+ } else {
+ ps->data_offset += sizeof(uint32);
+ }
+ return True;
+}
+
+/*******************************************************************
+ prs_uint32 wrapper. call this and it retrospectively stores the size.
+ does nothing on reading, as that is already handled by ...._pre()
+ ********************************************************************/
+
+BOOL prs_uint32_post(const char *name, prs_struct *ps, int depth, uint32 *data32,
+ uint32 ptr_uint32, uint32 data_size)
+{
+ if (MARSHALLING(ps)) {
+ /*
+ * Writing - temporarily move the offset pointer.
+ */
+ uint32 old_offset = ps->data_offset;
+ ps->data_offset = ptr_uint32;
+ if(!prs_uint32(name, ps, depth, &data_size)) {
+ ps->data_offset = old_offset;
+ return False;
+ }
+ ps->data_offset = old_offset;
+ }
+ return True;
+}
+
+/* useful function to store a structure in rpc wire format */
+int tdb_prs_store(TDB_CONTEXT *tdb, char *keystr, prs_struct *ps)
+{
+ TDB_DATA kbuf, dbuf;
+ kbuf.dptr = keystr;
+ kbuf.dsize = strlen(keystr)+1;
+ dbuf.dptr = ps->data_p;
+ dbuf.dsize = prs_offset(ps);
+ return tdb_store(tdb, kbuf, dbuf, TDB_REPLACE);
+}
+
+/* useful function to fetch a structure into rpc wire format */
+int tdb_prs_fetch(TDB_CONTEXT *tdb, char *keystr, prs_struct *ps, TALLOC_CTX *mem_ctx)
+{
+ TDB_DATA kbuf, dbuf;
+ kbuf.dptr = keystr;
+ kbuf.dsize = strlen(keystr)+1;
+
+ dbuf = tdb_fetch(tdb, kbuf);
+ if (!dbuf.dptr)
+ return -1;
+
+ prs_init(ps, 0, mem_ctx, UNMARSHALL);
+ prs_give_memory(ps, dbuf.dptr, dbuf.dsize, True);
+
+ return 0;
+}
+
+/*******************************************************************
+ hash a stream.
+ ********************************************************************/
+BOOL prs_hash1(prs_struct *ps, uint32 offset, uint8 sess_key[16], int len)
+{
+ char *q;
+
+ q = ps->data_p;
+ q = &q[offset];
+
+#ifdef DEBUG_PASSWORD
+ DEBUG(100, ("prs_hash1\n"));
+ dump_data(100, sess_key, 16);
+ dump_data(100, q, len);
+#endif
+ SamOEMhash((uchar *) q, sess_key, len);
+
+#ifdef DEBUG_PASSWORD
+ dump_data(100, q, len);
+#endif
+
+ return True;
+}
+
+
+/*******************************************************************
+ Create a digest over the entire packet (including the data), and
+ MD5 it with the session key.
+ ********************************************************************/
+static void netsec_digest(struct netsec_auth_struct *a,
+ int auth_flags,
+ RPC_AUTH_NETSEC_CHK * verf,
+ char *data, size_t data_len,
+ uchar digest_final[16])
+{
+ uchar whole_packet_digest[16];
+ static uchar zeros[4];
+ struct MD5Context ctx3;
+
+ /* verfiy the signature on the packet by MD5 over various bits */
+ MD5Init(&ctx3);
+ /* use our sequence number, which ensures the packet is not
+ out of order */
+ MD5Update(&ctx3, zeros, sizeof(zeros));
+ MD5Update(&ctx3, verf->sig, sizeof(verf->sig));
+ if (auth_flags & AUTH_PIPE_SEAL) {
+ MD5Update(&ctx3, verf->confounder, sizeof(verf->confounder));
+ }
+ MD5Update(&ctx3, (const unsigned char *)data, data_len);
+ MD5Final(whole_packet_digest, &ctx3);
+ dump_data_pw("whole_packet_digest:\n", whole_packet_digest, sizeof(whole_packet_digest));
+
+ /* MD5 this result and the session key, to prove that
+ only a valid client could had produced this */
+ hmac_md5(a->sess_key, whole_packet_digest, sizeof(whole_packet_digest), digest_final);
+}
+
+/*******************************************************************
+ Calculate the key with which to encode the data payload
+ ********************************************************************/
+static void netsec_get_sealing_key(struct netsec_auth_struct *a,
+ RPC_AUTH_NETSEC_CHK *verf,
+ uchar sealing_key[16])
+{
+ static uchar zeros[4];
+ uchar digest2[16];
+ uchar sess_kf0[16];
+ int i;
+
+ for (i = 0; i < sizeof(sess_kf0); i++) {
+ sess_kf0[i] = a->sess_key[i] ^ 0xf0;
+ }
+
+ dump_data_pw("sess_kf0:\n", sess_kf0, sizeof(sess_kf0));
+
+ /* MD5 of sess_kf0 and 4 zero bytes */
+ hmac_md5(sess_kf0, zeros, 0x4, digest2);
+ dump_data_pw("digest2:\n", digest2, sizeof(digest2));
+
+ /* MD5 of the above result, plus 8 bytes of sequence number */
+ hmac_md5(digest2, verf->seq_num, sizeof(verf->seq_num), sealing_key);
+ dump_data_pw("sealing_key:\n", sealing_key, 16);
+}
+
+/*******************************************************************
+ Encode or Decode the sequence number (which is symmetric)
+ ********************************************************************/
+static void netsec_deal_with_seq_num(struct netsec_auth_struct *a,
+ RPC_AUTH_NETSEC_CHK *verf)
+{
+ static uchar zeros[4];
+ uchar sequence_key[16];
+ uchar digest1[16];
+
+ hmac_md5(a->sess_key, zeros, sizeof(zeros), digest1);
+ dump_data_pw("(sequence key) digest1:\n", digest1, sizeof(digest1));
+
+ hmac_md5(digest1, verf->packet_digest, 8, sequence_key);
+
+ dump_data_pw("sequence_key:\n", sequence_key, sizeof(sequence_key));
+
+ dump_data_pw("seq_num (before):\n", verf->seq_num, sizeof(verf->seq_num));
+ SamOEMhash(verf->seq_num, sequence_key, 8);
+ dump_data_pw("seq_num (after):\n", verf->seq_num, sizeof(verf->seq_num));
+}
+
+/*******************************************************************
+creates an RPC_AUTH_NETSEC_CHK structure.
+********************************************************************/
+static BOOL init_rpc_auth_netsec_chk(RPC_AUTH_NETSEC_CHK * chk,
+ const uchar sig[8],
+ const uchar packet_digest[8],
+ const uchar seq_num[8], const uchar confounder[8])
+{
+ if (chk == NULL)
+ return False;
+
+ memcpy(chk->sig, sig, sizeof(chk->sig));
+ memcpy(chk->packet_digest, packet_digest, sizeof(chk->packet_digest));
+ memcpy(chk->seq_num, seq_num, sizeof(chk->seq_num));
+ memcpy(chk->confounder, confounder, sizeof(chk->confounder));
+
+ return True;
+}
+
+
+/*******************************************************************
+ Encode a blob of data using the netsec (schannel) alogrithm, also produceing
+ a checksum over the original data. We currently only support
+ signing and sealing togeather - the signing-only code is close, but not
+ quite compatible with what MS does.
+ ********************************************************************/
+void netsec_encode(struct netsec_auth_struct *a, int auth_flags,
+ enum netsec_direction direction,
+ RPC_AUTH_NETSEC_CHK * verf,
+ char *data, size_t data_len)
+{
+ uchar digest_final[16];
+ uchar confounder[8];
+ uchar seq_num[8];
+ static const uchar nullbytes[8];
+
+ static const uchar netsec_seal_sig[8] = NETSEC_SEAL_SIGNATURE;
+ static const uchar netsec_sign_sig[8] = NETSEC_SIGN_SIGNATURE;
+ const uchar *netsec_sig = NULL;
+
+ DEBUG(10,("SCHANNEL: netsec_encode seq_num=%d data_len=%lu\n", a->seq_num, (unsigned long)data_len));
+
+ if (auth_flags & AUTH_PIPE_SEAL) {
+ netsec_sig = netsec_seal_sig;
+ } else if (auth_flags & AUTH_PIPE_SIGN) {
+ netsec_sig = netsec_sign_sig;
+ }
+
+ /* fill the 'confounder' with random data */
+ generate_random_buffer(confounder, sizeof(confounder), False);
+
+ dump_data_pw("a->sess_key:\n", a->sess_key, sizeof(a->sess_key));
+
+ RSIVAL(seq_num, 0, a->seq_num);
+
+ switch (direction) {
+ case SENDER_IS_INITIATOR:
+ SIVAL(seq_num, 4, 0x80);
+ break;
+ case SENDER_IS_ACCEPTOR:
+ SIVAL(seq_num, 4, 0x0);
+ break;
+ }
+
+ dump_data_pw("verf->seq_num:\n", seq_num, sizeof(verf->seq_num));
+
+ init_rpc_auth_netsec_chk(verf, netsec_sig, nullbytes,
+ seq_num, confounder);
+
+ /* produce a digest of the packet to prove it's legit (before we seal it) */
+ netsec_digest(a, auth_flags, verf, data, data_len, digest_final);
+ memcpy(verf->packet_digest, digest_final, sizeof(verf->packet_digest));
+
+ if (auth_flags & AUTH_PIPE_SEAL) {
+ uchar sealing_key[16];
+
+ /* get the key to encode the data with */
+ netsec_get_sealing_key(a, verf, sealing_key);
+
+ /* encode the verification data */
+ dump_data_pw("verf->confounder:\n", verf->confounder, sizeof(verf->confounder));
+ SamOEMhash(verf->confounder, sealing_key, 8);
+
+ dump_data_pw("verf->confounder_enc:\n", verf->confounder, sizeof(verf->confounder));
+
+ /* encode the packet payload */
+ dump_data_pw("data:\n", (const unsigned char *)data, data_len);
+ SamOEMhash((unsigned char *)data, sealing_key, data_len);
+ dump_data_pw("data_enc:\n", (const unsigned char *)data, data_len);
+ }
+
+ /* encode the sequence number (key based on packet digest) */
+ /* needs to be done after the sealing, as the original version
+ is used in the sealing stuff... */
+ netsec_deal_with_seq_num(a, verf);
+
+ return;
+}
+
+/*******************************************************************
+ Decode a blob of data using the netsec (schannel) alogrithm, also verifiying
+ a checksum over the original data. We currently can verify signed messages,
+ as well as decode sealed messages
+ ********************************************************************/
+
+BOOL netsec_decode(struct netsec_auth_struct *a, int auth_flags,
+ enum netsec_direction direction,
+ RPC_AUTH_NETSEC_CHK * verf, char *data, size_t data_len)
+{
+ uchar digest_final[16];
+
+ static const uchar netsec_seal_sig[8] = NETSEC_SEAL_SIGNATURE;
+ static const uchar netsec_sign_sig[8] = NETSEC_SIGN_SIGNATURE;
+ const uchar *netsec_sig = NULL;
+
+ uchar seq_num[8];
+
+ DEBUG(10,("SCHANNEL: netsec_encode seq_num=%d data_len=%lu\n", a->seq_num, (unsigned long)data_len));
+
+ if (auth_flags & AUTH_PIPE_SEAL) {
+ netsec_sig = netsec_seal_sig;
+ } else if (auth_flags & AUTH_PIPE_SIGN) {
+ netsec_sig = netsec_sign_sig;
+ }
+
+ /* Create the expected sequence number for comparison */
+ RSIVAL(seq_num, 0, a->seq_num);
+
+ switch (direction) {
+ case SENDER_IS_INITIATOR:
+ SIVAL(seq_num, 4, 0x80);
+ break;
+ case SENDER_IS_ACCEPTOR:
+ SIVAL(seq_num, 4, 0x0);
+ break;
+ }
+
+ DEBUG(10,("SCHANNEL: netsec_decode seq_num=%d data_len=%lu\n", a->seq_num, (unsigned long)data_len));
+ dump_data_pw("a->sess_key:\n", a->sess_key, sizeof(a->sess_key));
+
+ dump_data_pw("seq_num:\n", seq_num, sizeof(seq_num));
+
+ /* extract the sequence number (key based on supplied packet digest) */
+ /* needs to be done before the sealing, as the original version
+ is used in the sealing stuff... */
+ netsec_deal_with_seq_num(a, verf);
+
+ if (memcmp(verf->seq_num, seq_num, sizeof(seq_num))) {
+ /* don't even bother with the below if the sequence number is out */
+ /* The sequence number is MD5'ed with a key based on the whole-packet
+ digest, as supplied by the client. We check that it's a valid
+ checksum after the decode, below
+ */
+ DEBUG(2, ("netsec_decode: FAILED: packet sequence number:\n"));
+ dump_data(2, (const char*)verf->seq_num, sizeof(verf->seq_num));
+ DEBUG(2, ("should be:\n"));
+ dump_data(2, (const char*)seq_num, sizeof(seq_num));
+
+ return False;
+ }
+
+ if (memcmp(verf->sig, netsec_sig, sizeof(verf->sig))) {
+ /* Validate that the other end sent the expected header */
+ DEBUG(2, ("netsec_decode: FAILED: packet header:\n"));
+ dump_data(2, (const char*)verf->sig, sizeof(verf->sig));
+ DEBUG(2, ("should be:\n"));
+ dump_data(2, (const char*)netsec_sig, sizeof(netsec_sig));
+ return False;
+ }
+
+ if (auth_flags & AUTH_PIPE_SEAL) {
+ uchar sealing_key[16];
+
+ /* get the key to extract the data with */
+ netsec_get_sealing_key(a, verf, sealing_key);
+
+ /* extract the verification data */
+ dump_data_pw("verf->confounder:\n", verf->confounder,
+ sizeof(verf->confounder));
+ SamOEMhash(verf->confounder, sealing_key, 8);
+
+ dump_data_pw("verf->confounder_dec:\n", verf->confounder,
+ sizeof(verf->confounder));
+
+ /* extract the packet payload */
+ dump_data_pw("data :\n", (const unsigned char *)data, data_len);
+ SamOEMhash((unsigned char *)data, sealing_key, data_len);
+ dump_data_pw("datadec:\n", (const unsigned char *)data, data_len);
+ }
+
+ /* digest includes 'data' after unsealing */
+ netsec_digest(a, auth_flags, verf, data, data_len, digest_final);
+
+ dump_data_pw("Calculated digest:\n", digest_final,
+ sizeof(digest_final));
+ dump_data_pw("verf->packet_digest:\n", verf->packet_digest,
+ sizeof(verf->packet_digest));
+
+ /* compare - if the client got the same result as us, then
+ it must know the session key */
+ return (memcmp(digest_final, verf->packet_digest,
+ sizeof(verf->packet_digest)) == 0);
+}
diff --git a/source/rpc_parse/parse_reg.c b/source/rpc_parse/parse_reg.c
new file mode 100644
index 00000000000..69c0dfc7548
--- /dev/null
+++ b/source/rpc_parse/parse_reg.c
@@ -0,0 +1,1856 @@
+/*
+ * Unix SMB/CIFS implementation.
+ * RPC Pipe client / server routines
+ * Copyright (C) Andrew Tridgell 1992-1997,
+ * Copyright (C) Luke Kenneth Casson Leighton 1996-1997,
+ * Copyright (C) Paul Ashton 1997.
+ * Copyright (C) Marc Jacobsen 1999.
+ * Copyright (C) Simo Sorce 2000.
+ * Copyright (C) Gerald Carter 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 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"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_RPC_PARSE
+
+/*******************************************************************
+ Fill in a BUFFER2 for the data given a REGISTRY_VALUE
+ *******************************************************************/
+
+static uint32 reg_init_buffer2( BUFFER2 *buf2, REGISTRY_VALUE *val )
+{
+ uint32 real_size = 0;
+
+ if ( !buf2 || !val )
+ return 0;
+
+ real_size = regval_size(val);
+ init_buffer2( buf2, (unsigned char*)regval_data_p(val), real_size );
+
+ return real_size;
+}
+
+/*******************************************************************
+ Inits a structure.
+********************************************************************/
+
+void init_reg_q_open_hkcr(REG_Q_OPEN_HKCR *q_o,
+ uint16 unknown_0, uint32 level)
+{
+ q_o->ptr = 1;
+ q_o->unknown_0 = unknown_0;
+ q_o->unknown_1 = 0x0; /* random - changes */
+ q_o->level = level;
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+BOOL reg_io_q_open_hkcr(const char *desc, REG_Q_OPEN_HKCR *r_q, prs_struct *ps, int depth)
+{
+ if (r_q == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "reg_io_q_open_hkcr");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("ptr ", ps, depth, &r_q->ptr))
+ return False;
+
+ if (r_q->ptr != 0) {
+ if(!prs_uint16("unknown_0", ps, depth, &r_q->unknown_0))
+ return False;
+ if(!prs_uint16("unknown_1", ps, depth, &r_q->unknown_1))
+ return False;
+ if(!prs_uint32("level ", ps, depth, &r_q->level))
+ return False;
+ }
+
+ return True;
+}
+
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+BOOL reg_io_r_open_hkcr(const char *desc, REG_R_OPEN_HKCR *r_r, prs_struct *ps, int depth)
+{
+ if (r_r == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "reg_io_r_open_hkcr");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!smb_io_pol_hnd("", &r_r->pol, ps, depth))
+ return False;
+
+ if(!prs_ntstatus("status", ps, depth, &r_r->status))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ Inits a structure.
+********************************************************************/
+
+void init_reg_q_open_hklm(REG_Q_OPEN_HKLM * q_o,
+ uint16 unknown_0, uint32 access_mask)
+{
+ q_o->ptr = 1;
+ q_o->unknown_0 = unknown_0;
+ q_o->unknown_1 = 0x0; /* random - changes */
+ q_o->access_mask = access_mask;
+
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+BOOL reg_io_q_open_hklm(const char *desc, REG_Q_OPEN_HKLM * r_q, prs_struct *ps,
+ int depth)
+{
+ if (r_q == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "reg_io_q_open_hklm");
+ depth++;
+
+ if (!prs_align(ps))
+ return False;
+
+ if (!prs_uint32("ptr ", ps, depth, &(r_q->ptr)))
+ return False;
+ if (r_q->ptr != 0)
+ {
+ if (!prs_uint16("unknown_0", ps, depth, &(r_q->unknown_0)))
+ return False;
+ if (!prs_uint16("unknown_1", ps, depth, &(r_q->unknown_1)))
+ return False;
+ if (!prs_uint32("access_mask", ps, depth, &(r_q->access_mask)))
+ return False;
+ }
+
+ return True;
+}
+
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+BOOL reg_io_r_open_hklm(const char *desc, REG_R_OPEN_HKLM * r_r, prs_struct *ps,
+ int depth)
+{
+ if (r_r == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "reg_io_r_open_hklm");
+ depth++;
+
+ if (!prs_align(ps))
+ return False;
+
+ if (!smb_io_pol_hnd("", &r_r->pol, ps, depth))
+ return False;
+
+ if (!prs_ntstatus("status", ps, depth, &r_r->status))
+ return False;
+
+ return True;
+}
+
+
+
+
+/*******************************************************************
+ Inits a structure.
+********************************************************************/
+
+void init_reg_q_flush_key(REG_Q_FLUSH_KEY *q_u, POLICY_HND *pol)
+{
+ memcpy(&q_u->pol, pol, sizeof(q_u->pol));
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+BOOL reg_io_q_flush_key(const char *desc, REG_Q_FLUSH_KEY *r_q, prs_struct *ps, int depth)
+{
+ if (r_q == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "reg_io_q_flush_key");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!smb_io_pol_hnd("", &r_q->pol, ps, depth))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+BOOL reg_io_r_flush_key(const char *desc, REG_R_FLUSH_KEY *r_r, prs_struct *ps, int depth)
+{
+ if (r_r == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "reg_io_r_flush_key");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_ntstatus("status", ps, depth, &r_r->status))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+reads or writes SEC_DESC_BUF and SEC_DATA structures.
+********************************************************************/
+
+static BOOL reg_io_hdrbuf_sec(uint32 ptr, uint32 *ptr3, BUFHDR *hdr_sec, SEC_DESC_BUF *data, prs_struct *ps, int depth)
+{
+ if (ptr != 0) {
+ uint32 hdr_offset;
+ uint32 old_offset;
+ if(!smb_io_hdrbuf_pre("hdr_sec", hdr_sec, ps, depth, &hdr_offset))
+ return False;
+
+ old_offset = prs_offset(ps);
+
+ if (ptr3 != NULL) {
+ if(!prs_uint32("ptr3", ps, depth, ptr3))
+ return False;
+ }
+
+ if (ptr3 == NULL || *ptr3 != 0) {
+ if(!sec_io_desc_buf("data ", &data, ps, depth)) /* JRA - this line is probably wrong... */
+ return False;
+ }
+
+ if(!smb_io_hdrbuf_post("hdr_sec", hdr_sec, ps, depth, hdr_offset,
+ data->max_len, data->len))
+ return False;
+ if(!prs_set_offset(ps, old_offset + data->len + sizeof(uint32) * ((ptr3 != NULL) ? 5 : 3)))
+ return False;
+
+ if(!prs_align(ps))
+ return False;
+ }
+
+ return True;
+}
+
+/*******************************************************************
+ Inits a structure.
+********************************************************************/
+
+void init_reg_q_create_key(REG_Q_CREATE_KEY *q_c, POLICY_HND *hnd,
+ char *name, char *class, SEC_ACCESS *sam_access,
+ SEC_DESC_BUF *sec_buf)
+{
+ ZERO_STRUCTP(q_c);
+
+ memcpy(&q_c->pnt_pol, hnd, sizeof(q_c->pnt_pol));
+
+ init_unistr2(&q_c->uni_name, name, UNI_STR_TERMINATE);
+ init_uni_hdr(&q_c->hdr_name, &q_c->uni_name);
+
+ init_unistr2(&q_c->uni_class, class, UNI_STR_TERMINATE);
+ init_uni_hdr(&q_c->hdr_class, &q_c->uni_class);
+
+ q_c->reserved = 0x00000000;
+ memcpy(&q_c->sam_access, sam_access, sizeof(q_c->sam_access));
+
+ q_c->ptr1 = 1;
+ q_c->sec_info = DACL_SECURITY_INFORMATION | SACL_SECURITY_INFORMATION;
+
+ q_c->data = sec_buf;
+ q_c->ptr2 = 1;
+ init_buf_hdr(&q_c->hdr_sec, sec_buf->len, sec_buf->len);
+ q_c->ptr3 = 1;
+ q_c->unknown_2 = 0x00000000;
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+BOOL reg_io_q_create_key(const char *desc, REG_Q_CREATE_KEY *r_q, prs_struct *ps, int depth)
+{
+ if (r_q == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "reg_io_q_create_key");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!smb_io_pol_hnd("", &r_q->pnt_pol, ps, depth))
+ return False;
+
+ if(!smb_io_unihdr ("", &r_q->hdr_name, ps, depth))
+ return False;
+ if(!smb_io_unistr2("", &r_q->uni_name, r_q->hdr_name.buffer, ps, depth))
+ return False;
+ if(!prs_align(ps))
+ return False;
+
+ if(!smb_io_unihdr ("", &r_q->hdr_class, ps, depth))
+ return False;
+ if(!smb_io_unistr2("", &r_q->uni_class, r_q->hdr_class.buffer, ps, depth))
+ return False;
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("reserved", ps, depth, &r_q->reserved))
+ return False;
+ if(!sec_io_access("sam_access", &r_q->sam_access, ps, depth))
+ return False;
+
+ if(!prs_uint32("ptr1", ps, depth, &r_q->ptr1))
+ return False;
+
+ if (r_q->ptr1 != 0) {
+ if(!prs_uint32("sec_info", ps, depth, &r_q->sec_info))
+ return False;
+ }
+
+ if(!prs_uint32("ptr2", ps, depth, &r_q->ptr2))
+ return False;
+ if(!reg_io_hdrbuf_sec(r_q->ptr2, &r_q->ptr3, &r_q->hdr_sec, r_q->data, ps, depth))
+ return False;
+
+ if(!prs_uint32("unknown_2", ps, depth, &r_q->unknown_2))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+BOOL reg_io_r_create_key(const char *desc, REG_R_CREATE_KEY *r_r, prs_struct *ps, int depth)
+{
+ if (r_r == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "reg_io_r_create_key");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!smb_io_pol_hnd("", &r_r->key_pol, ps, depth))
+ return False;
+ if(!prs_uint32("unknown", ps, depth, &r_r->unknown))
+ return False;
+
+ if(!prs_ntstatus("status", ps, depth, &r_r->status))
+ return False;
+
+ return True;
+}
+
+
+/*******************************************************************
+ Inits a structure.
+********************************************************************/
+
+void init_reg_q_delete_val(REG_Q_DELETE_VALUE *q_c, POLICY_HND *hnd,
+ char *name)
+{
+ ZERO_STRUCTP(q_c);
+
+ memcpy(&q_c->pnt_pol, hnd, sizeof(q_c->pnt_pol));
+
+ init_unistr2(&q_c->uni_name, name, UNI_STR_TERMINATE);
+ init_uni_hdr(&q_c->hdr_name, &q_c->uni_name);
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+BOOL reg_io_q_delete_val(const char *desc, REG_Q_DELETE_VALUE *r_q, prs_struct *ps, int depth)
+{
+ if (r_q == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "reg_io_q_delete_val");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!smb_io_pol_hnd("", &r_q->pnt_pol, ps, depth))
+ return False;
+
+ if(!smb_io_unihdr ("", &r_q->hdr_name, ps, depth))
+ return False;
+ if(!smb_io_unistr2("", &r_q->uni_name, r_q->hdr_name.buffer, ps, depth))
+ return False;
+ if(!prs_align(ps))
+ return False;
+
+ return True;
+}
+
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+BOOL reg_io_r_delete_val(const char *desc, REG_R_DELETE_VALUE *r_r, prs_struct *ps, int depth)
+{
+ if (r_r == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "reg_io_r_delete_val");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_ntstatus("status", ps, depth, &r_r->status))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ Inits a structure.
+********************************************************************/
+
+void init_reg_q_delete_key(REG_Q_DELETE_KEY *q_c, POLICY_HND *hnd,
+ char *name)
+{
+ ZERO_STRUCTP(q_c);
+
+ memcpy(&q_c->pnt_pol, hnd, sizeof(q_c->pnt_pol));
+
+ init_unistr2(&q_c->uni_name, name, UNI_STR_TERMINATE);
+ init_uni_hdr(&q_c->hdr_name, &q_c->uni_name);
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+BOOL reg_io_q_delete_key(const char *desc, REG_Q_DELETE_KEY *r_q, prs_struct *ps, int depth)
+{
+ if (r_q == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "reg_io_q_delete_key");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!smb_io_pol_hnd("", &r_q->pnt_pol, ps, depth))
+ return False;
+
+ if(!smb_io_unihdr ("", &r_q->hdr_name, ps, depth))
+ return False;
+ if(!smb_io_unistr2("", &r_q->uni_name, r_q->hdr_name.buffer, ps, depth))
+ return False;
+ if(!prs_align(ps))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+BOOL reg_io_r_delete_key(const char *desc, REG_R_DELETE_KEY *r_r, prs_struct *ps, int depth)
+{
+ if (r_r == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "reg_io_r_delete_key");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_ntstatus("status", ps, depth, &r_r->status))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ Inits a structure.
+********************************************************************/
+
+void init_reg_q_query_key(REG_Q_QUERY_KEY *q_o, POLICY_HND *hnd, UNISTR2 *uni2)
+{
+ ZERO_STRUCTP(q_o);
+
+ memcpy(&q_o->pol, hnd, sizeof(q_o->pol));
+ init_uni_hdr(&q_o->hdr_class, uni2);
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+BOOL reg_io_q_query_key(const char *desc, REG_Q_QUERY_KEY *r_q, prs_struct *ps, int depth)
+{
+ if (r_q == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "reg_io_q_query_key");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!smb_io_pol_hnd("", &r_q->pol, ps, depth))
+ return False;
+ if(!smb_io_unihdr ("", &r_q->hdr_class, ps, depth))
+ return False;
+ if(!smb_io_unistr2("", &r_q->uni_class, r_q->hdr_class.buffer, ps, depth))
+ return False;
+
+ if(!prs_align(ps))
+ return False;
+
+ return True;
+}
+
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+BOOL reg_io_r_query_key(const char *desc, REG_R_QUERY_KEY *r_r, prs_struct *ps, int depth)
+{
+ if (r_r == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "reg_io_r_query_key");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!smb_io_unihdr ("", &r_r->hdr_class, ps, depth))
+ return False;
+ if(!smb_io_unistr2("", &r_r->uni_class, r_r->hdr_class.buffer, ps, depth))
+ return False;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("num_subkeys ", ps, depth, &r_r->num_subkeys))
+ return False;
+ if(!prs_uint32("max_subkeylen ", ps, depth, &r_r->max_subkeylen))
+ return False;
+ if(!prs_uint32("reserved ", ps, depth, &r_r->reserved))
+ return False;
+ if(!prs_uint32("num_values ", ps, depth, &r_r->num_values))
+ return False;
+ if(!prs_uint32("max_valnamelen", ps, depth, &r_r->max_valnamelen))
+ return False;
+ if(!prs_uint32("max_valbufsize", ps, depth, &r_r->max_valbufsize))
+ return False;
+ if(!prs_uint32("sec_desc ", ps, depth, &r_r->sec_desc))
+ return False;
+ if(!smb_io_time("mod_time ", &r_r->mod_time, ps, depth))
+ return False;
+
+ if(!prs_ntstatus("status", ps, depth, &r_r->status))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ Inits a structure.
+********************************************************************/
+
+void init_reg_q_unknown_1a(REG_Q_UNKNOWN_1A *q_o, POLICY_HND *hnd)
+{
+ memcpy(&q_o->pol, hnd, sizeof(q_o->pol));
+}
+
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+BOOL reg_io_q_unknown_1a(const char *desc, REG_Q_UNKNOWN_1A *r_q, prs_struct *ps, int depth)
+{
+ if (r_q == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "reg_io_q_unknown_1a");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!smb_io_pol_hnd("", &r_q->pol, ps, depth))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+BOOL reg_io_r_unknown_1a(const char *desc, REG_R_UNKNOWN_1A *r_r, prs_struct *ps, int depth)
+{
+ if (r_r == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "reg_io_r_unknown_1a");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("unknown", ps, depth, &r_r->unknown))
+ return False;
+ if(!prs_ntstatus("status" , ps, depth, &r_r->status))
+ return False;
+
+ return True;
+}
+
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+BOOL reg_io_q_save_key(const char *desc, REG_Q_SAVE_KEY *r_q, prs_struct *ps, int depth)
+{
+ if (r_q == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "reg_io_q_save_key");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!smb_io_pol_hnd("", &r_q->pol, ps, depth))
+ return False;
+
+ if(!smb_io_unihdr ("hdr_file", &r_q->hdr_file, ps, depth))
+ return False;
+ if(!smb_io_unistr2("uni_file", &r_q->uni_file, r_q->hdr_file.buffer, ps, depth))
+ return False;
+
+ if(!prs_uint32("unknown", ps, depth, &r_q->unknown))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+BOOL reg_io_r_save_key(const char *desc, REG_R_SAVE_KEY *r_r, prs_struct *ps, int depth)
+{
+ if (r_r == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "reg_io_r_save_key");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_ntstatus("status" , ps, depth, &r_r->status))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ Inits a structure.
+********************************************************************/
+
+void init_reg_q_open_hku(REG_Q_OPEN_HKU *q_o,
+ uint16 unknown_0, uint32 access_mask)
+{
+ q_o->ptr = 1;
+ q_o->unknown_0 = unknown_0;
+ q_o->unknown_1 = 0x0; /* random - changes */
+ q_o->access_mask = access_mask;
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+BOOL reg_io_q_open_hku(const char *desc, REG_Q_OPEN_HKU *r_q, prs_struct *ps, int depth)
+{
+ if (r_q == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "reg_io_q_open_hku");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("ptr ", ps, depth, &r_q->ptr))
+ return False;
+ if (r_q->ptr != 0) {
+ if(!prs_uint16("unknown_0 ", ps, depth, &r_q->unknown_0))
+ return False;
+ if(!prs_uint16("unknown_1 ", ps, depth, &r_q->unknown_1))
+ return False;
+ if(!prs_uint32("access_mask ", ps, depth, &r_q->access_mask))
+ return False;
+ }
+
+ return True;
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+BOOL reg_io_r_open_hku(const char *desc, REG_R_OPEN_HKU *r_r, prs_struct *ps, int depth)
+{
+ if (r_r == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "reg_io_r_open_hku");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!smb_io_pol_hnd("", &r_r->pol, ps, depth))
+ return False;
+
+ if(!prs_ntstatus("status", ps, depth, &r_r->status))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ Inits an REG_Q_CLOSE structure.
+********************************************************************/
+
+void init_reg_q_close(REG_Q_CLOSE *q_c, POLICY_HND *hnd)
+{
+ DEBUG(5,("init_reg_q_close\n"));
+
+ memcpy(&q_c->pol, hnd, sizeof(q_c->pol));
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+BOOL reg_io_q_close(const char *desc, REG_Q_CLOSE *q_u, prs_struct *ps, int depth)
+{
+ if (q_u == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "reg_io_q_close");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!smb_io_pol_hnd("", &q_u->pol, ps, depth))
+ return False;
+ if(!prs_align(ps))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+BOOL reg_io_r_close(const char *desc, REG_R_CLOSE *r_u, prs_struct *ps, int depth)
+{
+ if (r_u == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "reg_io_r_close");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!smb_io_pol_hnd("", &r_u->pol, ps, depth))
+ return False;
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_ntstatus("status", ps, depth, &r_u->status))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+makes a structure.
+********************************************************************/
+
+void init_reg_q_set_key_sec(REG_Q_SET_KEY_SEC *q_i, POLICY_HND *pol, SEC_DESC_BUF *sec_desc_buf)
+{
+ memcpy(&q_i->pol, pol, sizeof(q_i->pol));
+
+ q_i->sec_info = DACL_SECURITY_INFORMATION;
+
+ q_i->ptr = 1;
+ init_buf_hdr(&q_i->hdr_sec, sec_desc_buf->len, sec_desc_buf->len);
+ q_i->data = sec_desc_buf;
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+BOOL reg_io_q_set_key_sec(const char *desc, REG_Q_SET_KEY_SEC *r_q, prs_struct *ps, int depth)
+{
+ if (r_q == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "reg_io_q_set_key_sec");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!smb_io_pol_hnd("", &r_q->pol, ps, depth))
+ return False;
+
+ if(!prs_uint32("sec_info", ps, depth, &r_q->sec_info))
+ return False;
+ if(!prs_uint32("ptr ", ps, depth, &r_q->ptr))
+ return False;
+
+ if(!reg_io_hdrbuf_sec(r_q->ptr, NULL, &r_q->hdr_sec, r_q->data, ps, depth))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+BOOL reg_io_r_set_key_sec(const char *desc, REG_R_SET_KEY_SEC *r_q, prs_struct *ps, int depth)
+{
+ if (r_q == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "reg_io_r_set_key_sec");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_ntstatus("status", ps, depth, &r_q->status))
+ return False;
+
+ return True;
+}
+
+
+/*******************************************************************
+makes a structure.
+********************************************************************/
+
+void init_reg_q_get_key_sec(REG_Q_GET_KEY_SEC *q_i, POLICY_HND *pol,
+ uint32 sec_buf_size, SEC_DESC_BUF *psdb)
+{
+ memcpy(&q_i->pol, pol, sizeof(q_i->pol));
+
+ q_i->sec_info = OWNER_SECURITY_INFORMATION |
+ GROUP_SECURITY_INFORMATION |
+ DACL_SECURITY_INFORMATION;
+
+ q_i->ptr = psdb != NULL ? 1 : 0;
+ q_i->data = psdb;
+
+ init_buf_hdr(&q_i->hdr_sec, sec_buf_size, 0);
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+BOOL reg_io_q_get_key_sec(const char *desc, REG_Q_GET_KEY_SEC *r_q, prs_struct *ps, int depth)
+{
+ if (r_q == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "reg_io_q_get_key_sec");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!smb_io_pol_hnd("", &r_q->pol, ps, depth))
+ return False;
+
+ if(!prs_uint32("sec_info", ps, depth, &r_q->sec_info))
+ return False;
+ if(!prs_uint32("ptr ", ps, depth, &r_q->ptr))
+ return False;
+
+ if(!reg_io_hdrbuf_sec(r_q->ptr, NULL, &r_q->hdr_sec, r_q->data, ps, depth))
+ return False;
+
+ return True;
+}
+
+#if 0
+/*******************************************************************
+makes a structure.
+********************************************************************/
+ void init_reg_r_get_key_sec(REG_R_GET_KEY_SEC *r_i, POLICY_HND *pol,
+ uint32 buf_len, uint8 *buf,
+ NTSTATUS status)
+{
+ r_i->ptr = 1;
+ init_buf_hdr(&r_i->hdr_sec, buf_len, buf_len);
+ init_sec_desc_buf(r_i->data, buf_len, 1);
+
+ r_i->status = status; /* 0x0000 0000 or 0x0000 007a */
+}
+#endif
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+BOOL reg_io_r_get_key_sec(const char *desc, REG_R_GET_KEY_SEC *r_q, prs_struct *ps, int depth)
+{
+ if (r_q == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "reg_io_r_get_key_sec");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("ptr ", ps, depth, &r_q->ptr))
+ return False;
+
+ if (r_q->ptr != 0) {
+ if(!smb_io_hdrbuf("", &r_q->hdr_sec, ps, depth))
+ return False;
+ if(!sec_io_desc_buf("", &r_q->data, ps, depth))
+ return False;
+ if(!prs_align(ps))
+ return False;
+ }
+
+ if(!prs_ntstatus("status", ps, depth, &r_q->status))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+makes a structure.
+********************************************************************/
+
+BOOL init_reg_q_info(REG_Q_INFO *q_i, POLICY_HND *pol, char* val_name)
+{
+ if (q_i == NULL)
+ return False;
+
+ q_i->pol = *pol;
+
+ init_unistr2(&q_i->uni_type, val_name, UNI_STR_TERMINATE);
+ init_uni_hdr(&q_i->hdr_type, &q_i->uni_type);
+
+ q_i->ptr_reserved = 1;
+ q_i->ptr_buf = 1;
+
+ q_i->ptr_bufsize = 1;
+ q_i->bufsize = 0;
+ q_i->buf_unk = 0;
+
+ q_i->unk1 = 0;
+ q_i->ptr_buflen = 1;
+ q_i->buflen = 0;
+
+ q_i->ptr_buflen2 = 1;
+ q_i->buflen2 = 0;
+
+ return True;
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+BOOL reg_io_q_info(const char *desc, REG_Q_INFO *r_q, prs_struct *ps, int depth)
+{
+ if (r_q == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "reg_io_q_info");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!smb_io_pol_hnd("", &r_q->pol, ps, depth))
+ return False;
+ if(!smb_io_unihdr ("", &r_q->hdr_type, ps, depth))
+ return False;
+ if(!smb_io_unistr2("", &r_q->uni_type, r_q->hdr_type.buffer, ps, depth))
+ return False;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("ptr_reserved", ps, depth, &(r_q->ptr_reserved)))
+ return False;
+
+ if(!prs_uint32("ptr_buf", ps, depth, &(r_q->ptr_buf)))
+ return False;
+
+ if(r_q->ptr_buf) {
+ if(!prs_uint32("ptr_bufsize", ps, depth, &(r_q->ptr_bufsize)))
+ return False;
+ if(!prs_uint32("bufsize", ps, depth, &(r_q->bufsize)))
+ return False;
+ if(!prs_uint32("buf_unk", ps, depth, &(r_q->buf_unk)))
+ return False;
+ }
+
+ if(!prs_uint32("unk1", ps, depth, &(r_q->unk1)))
+ return False;
+
+ if(!prs_uint32("ptr_buflen", ps, depth, &(r_q->ptr_buflen)))
+ return False;
+
+ if (r_q->ptr_buflen) {
+ if(!prs_uint32("buflen", ps, depth, &(r_q->buflen)))
+ return False;
+ if(!prs_uint32("ptr_buflen2", ps, depth, &(r_q->ptr_buflen2)))
+ return False;
+ if(!prs_uint32("buflen2", ps, depth, &(r_q->buflen2)))
+ return False;
+ }
+
+ return True;
+}
+
+/*******************************************************************
+ Inits a structure.
+ New version to replace older init_reg_r_info()
+********************************************************************/
+
+BOOL new_init_reg_r_info(uint32 include_keyval, REG_R_INFO *r_r,
+ REGISTRY_VALUE *val, NTSTATUS status)
+{
+ uint32 buf_len = 0;
+ BUFFER2 buf2;
+
+ if(r_r == NULL)
+ return False;
+
+ if ( !val )
+ return False;
+
+ r_r->ptr_type = 1;
+ r_r->type = val->type;
+
+ /* if include_keyval is not set, don't send the key value, just
+ the buflen data. probably used by NT5 to allocate buffer space - SK */
+
+ if ( include_keyval ) {
+ r_r->ptr_uni_val = 1;
+ buf_len = reg_init_buffer2( &r_r->uni_val, val );
+
+ }
+ else {
+ /* dummy buffer used so we can get the size */
+ r_r->ptr_uni_val = 0;
+ buf_len = reg_init_buffer2( &buf2, val );
+ }
+
+ r_r->ptr_max_len = 1;
+ r_r->buf_max_len = buf_len;
+
+ r_r->ptr_len = 1;
+ r_r->buf_len = buf_len;
+
+ r_r->status = status;
+
+ return True;
+}
+
+/*******************************************************************
+ Inits a structure.
+********************************************************************/
+
+BOOL init_reg_r_info(uint32 include_keyval, REG_R_INFO *r_r,
+ BUFFER2* buf, uint32 type, NTSTATUS status)
+{
+ if(r_r == NULL)
+ return False;
+
+ r_r->ptr_type = 1;
+ r_r->type = type;
+
+ /* if include_keyval is not set, don't send the key value, just
+ the buflen data. probably used by NT5 to allocate buffer space - SK */
+
+ r_r->ptr_uni_val = include_keyval ? 1:0;
+ r_r->uni_val = *buf;
+
+ r_r->ptr_max_len = 1;
+ r_r->buf_max_len = r_r->uni_val.buf_max_len;
+
+ r_r->ptr_len = 1;
+ r_r->buf_len = r_r->uni_val.buf_len;
+
+ r_r->status = status;
+
+ return True;
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+BOOL reg_io_r_info(const char *desc, REG_R_INFO *r_r, prs_struct *ps, int depth)
+{
+ if (r_r == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "reg_io_r_info");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("ptr_type", ps, depth, &(r_r->ptr_type)))
+ return False;
+
+ if (r_r->ptr_type != 0) {
+ if(!prs_uint32("type", ps, depth, &r_r->type))
+ return False;
+ }
+
+ if(!prs_uint32("ptr_uni_val", ps, depth, &(r_r->ptr_uni_val)))
+ return False;
+
+ if(r_r->ptr_uni_val != 0) {
+ if(!smb_io_buffer2("uni_val", &r_r->uni_val, r_r->ptr_uni_val, ps, depth))
+ return False;
+ }
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("ptr_max_len", ps, depth, &(r_r->ptr_max_len)))
+ return False;
+
+ if (r_r->ptr_max_len != 0) {
+ if(!prs_uint32("buf_max_len", ps, depth, &(r_r->buf_max_len)))
+ return False;
+ }
+
+ if(!prs_uint32("ptr_len", ps, depth, &(r_r->ptr_len)))
+ return False;
+ if (r_r->ptr_len != 0) {
+ if(!prs_uint32("buf_len", ps, depth, &(r_r->buf_len)))
+ return False;
+ }
+
+ if(!prs_ntstatus("status", ps, depth, &r_r->status))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+makes a structure.
+********************************************************************/
+
+void init_reg_q_enum_val(REG_Q_ENUM_VALUE *q_i, POLICY_HND *pol,
+ uint32 val_idx, UNISTR2 *uni2,
+ uint32 max_buf_len)
+{
+ ZERO_STRUCTP(q_i);
+
+ memcpy(&q_i->pol, pol, sizeof(q_i->pol));
+
+ q_i->val_index = val_idx;
+ init_uni_hdr(&q_i->hdr_name, uni2);
+
+ q_i->ptr_type = 1;
+ q_i->type = 0x0;
+
+ q_i->ptr_value = 1;
+ q_i->buf_value.buf_max_len = max_buf_len;
+
+ q_i->ptr1 = 1;
+ q_i->len_value1 = max_buf_len;
+
+ q_i->ptr2 = 1;
+ q_i->len_value2 = 0;
+}
+
+/*******************************************************************
+makes a structure.
+********************************************************************/
+
+void init_reg_r_enum_val(REG_R_ENUM_VALUE *r_u, REGISTRY_VALUE *val )
+{
+ uint32 real_size;
+
+ DEBUG(8,("init_reg_r_enum_val: Enter\n"));
+
+ ZERO_STRUCTP(r_u);
+
+ /* value name */
+
+ DEBUG(10,("init_reg_r_enum_val: Valuename => [%s]\n", val->valuename));
+
+ init_unistr2( &r_u->uni_name, val->valuename, UNI_STR_TERMINATE);
+ init_uni_hdr( &r_u->hdr_name, &r_u->uni_name);
+
+ /* type */
+
+ r_u->ptr_type = 1;
+ r_u->type = val->type;
+
+ /* REG_SZ & REG_MULTI_SZ must be converted to UNICODE */
+
+ r_u->ptr_value = 1;
+ real_size = reg_init_buffer2( &r_u->buf_value, val );
+
+ /* lengths */
+
+ r_u->ptr1 = 1;
+ r_u->len_value1 = real_size;
+
+ r_u->ptr2 = 1;
+ r_u->len_value2 = real_size;
+
+ DEBUG(8,("init_reg_r_enum_val: Exit\n"));
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+BOOL reg_io_q_enum_val(const char *desc, REG_Q_ENUM_VALUE *q_q, prs_struct *ps, int depth)
+{
+ if (q_q == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "reg_io_q_enum_val");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!smb_io_pol_hnd("", &q_q->pol, ps, depth))
+ return False;
+
+ if(!prs_uint32("val_index", ps, depth, &q_q->val_index))
+ return False;
+
+ if(!smb_io_unihdr ("hdr_name", &q_q->hdr_name, ps, depth))
+ return False;
+ if(!smb_io_unistr2("uni_name", &q_q->uni_name, q_q->hdr_name.buffer, ps, depth))
+ return False;
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("ptr_type", ps, depth, &q_q->ptr_type))
+ return False;
+
+ if (q_q->ptr_type != 0) {
+ if(!prs_uint32("type", ps, depth, &q_q->type))
+ return False;
+ }
+
+ if(!prs_uint32("ptr_value", ps, depth, &q_q->ptr_value))
+ return False;
+ if(!smb_io_buffer2("buf_value", &q_q->buf_value, q_q->ptr_value, ps, depth))
+ return False;
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("ptr1", ps, depth, &q_q->ptr1))
+ return False;
+ if (q_q->ptr1 != 0) {
+ if(!prs_uint32("len_value1", ps, depth, &q_q->len_value1))
+ return False;
+ }
+ if(!prs_uint32("ptr2", ps, depth, &q_q->ptr2))
+ return False;
+ if (q_q->ptr2 != 0) {
+ if(!prs_uint32("len_value2", ps, depth, &q_q->len_value2))
+ return False;
+ }
+
+ return True;
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+BOOL reg_io_r_enum_val(const char *desc, REG_R_ENUM_VALUE *r_q, prs_struct *ps, int depth)
+{
+ if (r_q == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "reg_io_r_enum_val");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!smb_io_unihdr ("hdr_name", &r_q->hdr_name, ps, depth))
+ return False;
+ if(!smb_io_unistr2("uni_name", &r_q->uni_name, r_q->hdr_name.buffer, ps, depth))
+ return False;
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("ptr_type", ps, depth, &r_q->ptr_type))
+ return False;
+
+ if (r_q->ptr_type != 0) {
+ if(!prs_uint32("type", ps, depth, &r_q->type))
+ return False;
+ }
+
+ if(!prs_uint32("ptr_value", ps, depth, &r_q->ptr_value))
+ return False;
+ if(!smb_io_buffer2("buf_value", &r_q->buf_value, r_q->ptr_value, ps, depth))
+ return False;
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("ptr1", ps, depth, &r_q->ptr1))
+ return False;
+ if (r_q->ptr1 != 0) {
+ if(!prs_uint32("len_value1", ps, depth, &r_q->len_value1))
+ return False;
+ }
+
+ if(!prs_uint32("ptr2", ps, depth, &r_q->ptr2))
+ return False;
+ if (r_q->ptr2 != 0) {
+ if(!prs_uint32("len_value2", ps, depth, &r_q->len_value2))
+ return False;
+ }
+
+ if(!prs_ntstatus("status", ps, depth, &r_q->status))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+makes a structure.
+********************************************************************/
+
+void init_reg_q_create_val(REG_Q_CREATE_VALUE *q_i, POLICY_HND *pol,
+ char *val_name, uint32 type,
+ BUFFER3 *val)
+{
+ ZERO_STRUCTP(q_i);
+
+ memcpy(&q_i->pol, pol, sizeof(q_i->pol));
+
+ init_unistr2(&q_i->uni_name, val_name, UNI_STR_TERMINATE);
+ init_uni_hdr(&q_i->hdr_name, &q_i->uni_name);
+
+ q_i->type = type;
+ q_i->buf_value = val;
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+BOOL reg_io_q_create_val(const char *desc, REG_Q_CREATE_VALUE *q_q, prs_struct *ps, int depth)
+{
+ if (q_q == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "reg_io_q_create_val");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!smb_io_pol_hnd("", &q_q->pol, ps, depth))
+ return False;
+
+ if(!smb_io_unihdr ("hdr_name", &q_q->hdr_name, ps, depth))
+ return False;
+ if(!smb_io_unistr2("uni_name", &q_q->uni_name, q_q->hdr_name.buffer, ps, depth))
+ return False;
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("type", ps, depth, &q_q->type))
+ return False;
+ if(!smb_io_buffer3("buf_value", q_q->buf_value, ps, depth))
+ return False;
+ if(!prs_align(ps))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+BOOL reg_io_r_create_val(const char *desc, REG_R_CREATE_VALUE *r_q, prs_struct *ps, int depth)
+{
+ if (r_q == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "reg_io_r_create_val");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_ntstatus("status", ps, depth, &r_q->status))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+makes a structure.
+********************************************************************/
+
+void init_reg_q_enum_key(REG_Q_ENUM_KEY *q_i, POLICY_HND *pol, uint32 key_idx)
+{
+ memcpy(&q_i->pol, pol, sizeof(q_i->pol));
+
+ q_i->key_index = key_idx;
+ q_i->key_name_len = 0;
+ q_i->unknown_1 = 0x0414;
+
+ q_i->ptr1 = 1;
+ q_i->unknown_2 = 0x0000020A;
+ memset(q_i->pad1, 0, sizeof(q_i->pad1));
+
+ q_i->ptr2 = 1;
+ memset(q_i->pad2, 0, sizeof(q_i->pad2));
+
+ q_i->ptr3 = 1;
+ unix_to_nt_time(&q_i->time, 0); /* current time? */
+}
+
+/*******************************************************************
+makes a reply structure.
+********************************************************************/
+
+void init_reg_r_enum_key(REG_R_ENUM_KEY *r_u, char *subkey, uint32 unknown_1,
+ uint32 unknown_2)
+{
+ if ( !r_u )
+ return;
+
+ r_u->unknown_1 = unknown_1;
+ r_u->unknown_2 = unknown_2;
+ r_u->unknown_3 = 0x0;
+
+ r_u->key_name_len = (strlen(subkey)+1) * 2;
+ if (r_u->key_name_len)
+ r_u->ptr1 = 0x1;
+ init_unistr3( &r_u->key_name, subkey );
+
+ r_u->ptr2 = 0x1;
+ r_u->ptr3 = 0x1;
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+BOOL reg_io_q_enum_key(const char *desc, REG_Q_ENUM_KEY *q_q, prs_struct *ps, int depth)
+{
+ if (q_q == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "reg_io_q_enum_key");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!smb_io_pol_hnd("", &q_q->pol, ps, depth))
+ return False;
+
+ if(!prs_uint32("key_index", ps, depth, &q_q->key_index))
+ return False;
+ if(!prs_uint16("key_name_len", ps, depth, &q_q->key_name_len))
+ return False;
+ if(!prs_uint16("unknown_1", ps, depth, &q_q->unknown_1))
+ return False;
+
+ if(!prs_uint32("ptr1", ps, depth, &q_q->ptr1))
+ return False;
+
+ if (q_q->ptr1 != 0) {
+ if(!prs_uint32("unknown_2", ps, depth, &q_q->unknown_2))
+ return False;
+ if(!prs_uint8s(False, "pad1", ps, depth, q_q->pad1, sizeof(q_q->pad1)))
+ return False;
+ }
+
+ if(!prs_uint32("ptr2", ps, depth, &q_q->ptr2))
+ return False;
+
+ if (q_q->ptr2 != 0) {
+ if(!prs_uint8s(False, "pad2", ps, depth, q_q->pad2, sizeof(q_q->pad2)))
+ return False;
+ }
+
+ if(!prs_uint32("ptr3", ps, depth, &q_q->ptr3))
+ return False;
+
+ if (q_q->ptr3 != 0) {
+ if(!smb_io_time("", &q_q->time, ps, depth))
+ return False;
+ }
+
+ return True;
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+BOOL reg_io_r_enum_key(const char *desc, REG_R_ENUM_KEY *r_q, prs_struct *ps, int depth)
+{
+ if (r_q == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "reg_io_r_enum_key");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint16("key_name_len", ps, depth, &r_q->key_name_len))
+ return False;
+ if(!prs_uint16("unknown_1", ps, depth, &r_q->unknown_1))
+ return False;
+
+ if(!prs_uint32("ptr1", ps, depth, &r_q->ptr1))
+ return False;
+
+ if (r_q->ptr1 != 0) {
+ if(!prs_uint32("unknown_2", ps, depth, &r_q->unknown_2))
+ return False;
+ if(!prs_uint32("unknown_3", ps, depth, &r_q->unknown_3))
+ return False;
+ if(!smb_io_unistr3("key_name", &r_q->key_name, ps, depth))
+ return False;
+ if(!prs_align(ps))
+ return False;
+ }
+
+ if(!prs_uint32("ptr2", ps, depth, &r_q->ptr2))
+ return False;
+
+ if (r_q->ptr2 != 0) {
+ if(!prs_uint8s(False, "pad2", ps, depth, r_q->pad2, sizeof(r_q->pad2)))
+ return False;
+ }
+
+ if(!prs_uint32("ptr3", ps, depth, &r_q->ptr3))
+ return False;
+
+ if (r_q->ptr3 != 0) {
+ if(!smb_io_time("", &r_q->time, ps, depth))
+ return False;
+ }
+
+ if(!prs_ntstatus("status", ps, depth, &r_q->status))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+makes a structure.
+********************************************************************/
+
+void init_reg_q_open_entry(REG_Q_OPEN_ENTRY *r_q, POLICY_HND *pol,
+ char *key_name, uint32 access_desired)
+{
+ memcpy(&r_q->pol, pol, sizeof(r_q->pol));
+
+ init_unistr2(&r_q->uni_name, key_name, UNI_STR_TERMINATE);
+ init_uni_hdr(&r_q->hdr_name, &r_q->uni_name);
+
+ r_q->unknown_0 = 0x00000000;
+ r_q->access_desired = access_desired;
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+BOOL reg_io_q_open_entry(const char *desc, REG_Q_OPEN_ENTRY *r_q, prs_struct *ps, int depth)
+{
+ if (r_q == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "reg_io_q_entry");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!smb_io_pol_hnd("", &r_q->pol, ps, depth))
+ return False;
+ if(!smb_io_unihdr ("", &r_q->hdr_name, ps, depth))
+ return False;
+ if(!smb_io_unistr2("", &r_q->uni_name, r_q->hdr_name.buffer, ps, depth))
+ return False;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("unknown_0 ", ps, depth, &r_q->unknown_0))
+ return False;
+ if(!prs_uint32("access_desired ", ps, depth, &r_q->access_desired))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ Inits a structure.
+********************************************************************/
+
+void init_reg_r_open_entry(REG_R_OPEN_ENTRY *r_r,
+ POLICY_HND *pol, NTSTATUS status)
+{
+ if (NT_STATUS_IS_OK(status)) {
+ memcpy(&r_r->pol, pol, sizeof(r_r->pol));
+ } else {
+ ZERO_STRUCT(r_r->pol);
+ }
+ r_r->status = status;
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+BOOL reg_io_r_open_entry(const char *desc, REG_R_OPEN_ENTRY *r_r, prs_struct *ps, int depth)
+{
+ if (r_r == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "reg_io_r_open_entry");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!smb_io_pol_hnd("", &r_r->pol, ps, depth))
+ return False;
+
+ if(!prs_ntstatus("status", ps, depth, &r_r->status))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+Inits a structure.
+********************************************************************/
+
+void init_reg_q_shutdown(REG_Q_SHUTDOWN * q_s, const char *msg,
+ uint32 timeout, BOOL do_reboot, BOOL force)
+{
+ q_s->ptr_0 = 1;
+ q_s->ptr_1 = 1;
+ q_s->ptr_2 = 1;
+
+ init_unistr2(&q_s->uni_msg, msg, UNI_FLAGS_NONE);
+ init_uni_hdr(&q_s->hdr_msg, &q_s->uni_msg);
+
+ q_s->timeout = timeout;
+
+ q_s->reboot = do_reboot ? 1 : 0;
+ q_s->force = force ? 1 : 0;
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+BOOL reg_io_q_shutdown(const char *desc, REG_Q_SHUTDOWN * q_s, prs_struct *ps,
+ int depth)
+{
+ if (q_s == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "reg_io_q_shutdown");
+ depth++;
+
+ if (!prs_align(ps))
+ return False;
+
+ if (!prs_uint32("ptr_0", ps, depth, &(q_s->ptr_0)))
+ return False;
+ if (!prs_uint32("ptr_1", ps, depth, &(q_s->ptr_1)))
+ return False;
+ if (!prs_uint32("ptr_2", ps, depth, &(q_s->ptr_2)))
+ return False;
+
+ if (!smb_io_unihdr("hdr_msg", &(q_s->hdr_msg), ps, depth))
+ return False;
+ if (!smb_io_unistr2("uni_msg", &(q_s->uni_msg), q_s->hdr_msg.buffer, ps, depth))
+ return False;
+ if (!prs_align(ps))
+ return False;
+
+ if (!prs_uint32("timeout", ps, depth, &(q_s->timeout)))
+ return False;
+ if (!prs_uint8("force ", ps, depth, &(q_s->force)))
+ return False;
+ if (!prs_uint8("reboot ", ps, depth, &(q_s->reboot)))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+BOOL reg_io_r_shutdown(const char *desc, REG_R_SHUTDOWN * r_s, prs_struct *ps,
+ int depth)
+{
+ if (r_s == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "reg_io_r_shutdown");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_ntstatus("status", ps, depth, &r_s->status))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+Inits a structure.
+********************************************************************/
+void init_reg_q_abort_shutdown(REG_Q_ABORT_SHUTDOWN * q_s)
+{
+
+ q_s->ptr_server = 0;
+
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+BOOL reg_io_q_abort_shutdown(const char *desc, REG_Q_ABORT_SHUTDOWN * q_s,
+ prs_struct *ps, int depth)
+{
+ if (q_s == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "reg_io_q_abort_shutdown");
+ depth++;
+
+ if (!prs_align(ps))
+ return False;
+
+ if (!prs_uint32("ptr_server", ps, depth, &(q_s->ptr_server)))
+ return False;
+ if (q_s->ptr_server != 0)
+ if (!prs_uint16("server", ps, depth, &(q_s->server)))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+BOOL reg_io_r_abort_shutdown(const char *desc, REG_R_ABORT_SHUTDOWN * r_s,
+ prs_struct *ps, int depth)
+{
+ if (r_s == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "reg_io_r_abort_shutdown");
+ depth++;
+
+ if (!prs_align(ps))
+ return False;
+
+ if (!prs_ntstatus("status", ps, depth, &r_s->status))
+ return False;
+
+ return True;
+}
diff --git a/source/rpc_parse/parse_rpc.c b/source/rpc_parse/parse_rpc.c
new file mode 100644
index 00000000000..696f258e5de
--- /dev/null
+++ b/source/rpc_parse/parse_rpc.c
@@ -0,0 +1,1219 @@
+/*
+ * Unix SMB/CIFS implementation.
+ * RPC Pipe client / server routines
+ * Copyright (C) Andrew Tridgell 1992-1997,
+ * Copyright (C) Luke Kenneth Casson Leighton 1996-1997,
+ * Copyright (C) Paul Ashton 1997.
+ * Copyright (C) Jeremy Allison 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 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"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_RPC_PARSE
+
+/*******************************************************************
+interface/version dce/rpc pipe identification
+********************************************************************/
+
+#define TRANS_SYNT_V2 \
+{ \
+ { \
+ 0x8a885d04, 0x1ceb, 0x11c9, \
+ { 0x9f, 0xe8 }, \
+ { 0x08, 0x00, \
+ 0x2b, 0x10, 0x48, 0x60 } \
+ }, 0x02 \
+}
+
+#define SYNT_NETLOGON_V2 \
+{ \
+ { \
+ 0x8a885d04, 0x1ceb, 0x11c9, \
+ { 0x9f, 0xe8 }, \
+ { 0x08, 0x00, \
+ 0x2b, 0x10, 0x48, 0x60 } \
+ }, 0x02 \
+}
+
+#define SYNT_WKSSVC_V1 \
+{ \
+ { \
+ 0x6bffd098, 0xa112, 0x3610, \
+ { 0x98, 0x33 }, \
+ { 0x46, 0xc3, \
+ 0xf8, 0x7e, 0x34, 0x5a } \
+ }, 0x01 \
+}
+
+#define SYNT_SRVSVC_V3 \
+{ \
+ { \
+ 0x4b324fc8, 0x1670, 0x01d3, \
+ { 0x12, 0x78 }, \
+ { 0x5a, 0x47, \
+ 0xbf, 0x6e, 0xe1, 0x88 } \
+ }, 0x03 \
+}
+
+#define SYNT_LSARPC_V0 \
+{ \
+ { \
+ 0x12345778, 0x1234, 0xabcd, \
+ { 0xef, 0x00 }, \
+ { 0x01, 0x23, \
+ 0x45, 0x67, 0x89, 0xab } \
+ }, 0x00 \
+}
+
+#define SYNT_LSARPC_V0_DS \
+{ \
+ { \
+ 0x3919286a, 0xb10c, 0x11d0, \
+ { 0x9b, 0xa8 }, \
+ { 0x00, 0xc0, \
+ 0x4f, 0xd9, 0x2e, 0xf5 } \
+ }, 0x00 \
+}
+
+#define SYNT_SAMR_V1 \
+{ \
+ { \
+ 0x12345778, 0x1234, 0xabcd, \
+ { 0xef, 0x00 }, \
+ { 0x01, 0x23, \
+ 0x45, 0x67, 0x89, 0xac } \
+ }, 0x01 \
+}
+
+#define SYNT_NETLOGON_V1 \
+{ \
+ { \
+ 0x12345678, 0x1234, 0xabcd, \
+ { 0xef, 0x00 }, \
+ { 0x01, 0x23, \
+ 0x45, 0x67, 0xcf, 0xfb } \
+ }, 0x01 \
+}
+
+#define SYNT_WINREG_V1 \
+{ \
+ { \
+ 0x338cd001, 0x2244, 0x31f1, \
+ { 0xaa, 0xaa }, \
+ { 0x90, 0x00, \
+ 0x38, 0x00, 0x10, 0x03 } \
+ }, 0x01 \
+}
+
+#define SYNT_SPOOLSS_V1 \
+{ \
+ { \
+ 0x12345678, 0x1234, 0xabcd, \
+ { 0xef, 0x00 }, \
+ { 0x01, 0x23, \
+ 0x45, 0x67, 0x89, 0xab } \
+ }, 0x01 \
+}
+
+#define SYNT_NONE_V0 \
+{ \
+ { \
+ 0x0, 0x0, 0x0, \
+ { 0x00, 0x00 }, \
+ { 0x00, 0x00, \
+ 0x00, 0x00, 0x00, 0x00 } \
+ }, 0x00 \
+}
+
+#define SYNT_NETDFS_V3 \
+{ \
+ { \
+ 0x4fc742e0, 0x4a10, 0x11cf, \
+ { 0x82, 0x73 }, \
+ { 0x00, 0xaa, \
+ 0x00, 0x4a, 0xe6, 0x73 } \
+ }, 0x03 \
+}
+
+#define SYNT_ECHO_V1 \
+{ \
+ { \
+ 0x60a15ec5, 0x4de8, 0x11d7, \
+ { 0xa6, 0x37 }, \
+ { 0x00, 0x50, \
+ 0x56, 0xa2, 0x01, 0x82 } \
+ }, 0x01 \
+}
+
+#define SYNT_SHUTDOWN_V1 \
+{ \
+ { \
+ 0x894de0c0, 0x0d55, 0x11d3, \
+ { 0xa3, 0x22 }, \
+ { 0x00, 0xc0, \
+ 0x4f, 0xa3, 0x21, 0xa1 } \
+ }, 0x01 \
+}
+
+#define SYNT_EPM_V3 \
+{ \
+ { \
+ 0xe1af8308, 0x5d1f, 0x11c9, \
+ { 0x91, 0xa4 }, \
+ { 0x08, 0x00, \
+ 0x2b, 0x14, 0xa0, 0xfa } \
+ }, 0x03 \
+}
+
+/*
+ * IMPORTANT!! If you update this structure, make sure to
+ * update the index #defines in smb.h.
+ */
+
+const struct pipe_id_info pipe_names [] =
+{
+ /* client pipe , abstract syntax , server pipe , transfer syntax */
+ { PIPE_LSARPC , SYNT_LSARPC_V0 , PIPE_LSASS , TRANS_SYNT_V2 },
+ { PIPE_LSARPC , SYNT_LSARPC_V0_DS , PIPE_LSASS , TRANS_SYNT_V2 },
+ { PIPE_SAMR , SYNT_SAMR_V1 , PIPE_LSASS , TRANS_SYNT_V2 },
+ { PIPE_NETLOGON, SYNT_NETLOGON_V1 , PIPE_LSASS , TRANS_SYNT_V2 },
+ { PIPE_SRVSVC , SYNT_SRVSVC_V3 , PIPE_NTSVCS , TRANS_SYNT_V2 },
+ { PIPE_WKSSVC , SYNT_WKSSVC_V1 , PIPE_NTSVCS , TRANS_SYNT_V2 },
+ { PIPE_WINREG , SYNT_WINREG_V1 , PIPE_WINREG , TRANS_SYNT_V2 },
+ { PIPE_SPOOLSS , SYNT_SPOOLSS_V1 , PIPE_SPOOLSS , TRANS_SYNT_V2 },
+ { PIPE_NETDFS , SYNT_NETDFS_V3 , PIPE_NETDFS , TRANS_SYNT_V2 },
+ { PIPE_ECHO , SYNT_ECHO_V1 , PIPE_ECHO , TRANS_SYNT_V2 },
+ { PIPE_SHUTDOWN, SYNT_SHUTDOWN_V1 , PIPE_SHUTDOWN , TRANS_SYNT_V2 },
+ { PIPE_EPM , SYNT_EPM_V3 , PIPE_EPM , TRANS_SYNT_V2 },
+ { NULL , SYNT_NONE_V0 , NULL , SYNT_NONE_V0 }
+};
+
+/*******************************************************************
+ Inits an RPC_HDR structure.
+********************************************************************/
+
+void init_rpc_hdr(RPC_HDR *hdr, enum RPC_PKT_TYPE pkt_type, uint8 flags,
+ uint32 call_id, int data_len, int auth_len)
+{
+ hdr->major = 5; /* RPC version 5 */
+ hdr->minor = 0; /* minor version 0 */
+ hdr->pkt_type = pkt_type; /* RPC packet type */
+ hdr->flags = flags; /* dce/rpc flags */
+ hdr->pack_type[0] = 0x10; /* little-endian data representation */
+ hdr->pack_type[1] = 0; /* packed data representation */
+ hdr->pack_type[2] = 0; /* packed data representation */
+ hdr->pack_type[3] = 0; /* packed data representation */
+ hdr->frag_len = data_len; /* fragment length, fill in later */
+ hdr->auth_len = auth_len; /* authentication length */
+ hdr->call_id = call_id; /* call identifier - match incoming RPC */
+}
+
+/*******************************************************************
+ Reads or writes an RPC_HDR structure.
+********************************************************************/
+
+BOOL smb_io_rpc_hdr(const char *desc, RPC_HDR *rpc, prs_struct *ps, int depth)
+{
+ if (rpc == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "smb_io_rpc_hdr");
+ depth++;
+
+ if(!prs_uint8 ("major ", ps, depth, &rpc->major))
+ return False;
+
+ if(!prs_uint8 ("minor ", ps, depth, &rpc->minor))
+ return False;
+ if(!prs_uint8 ("pkt_type ", ps, depth, &rpc->pkt_type))
+ return False;
+ if(!prs_uint8 ("flags ", ps, depth, &rpc->flags))
+ return False;
+
+ /* We always marshall in little endian format. */
+ if (MARSHALLING(ps))
+ rpc->pack_type[0] = 0x10;
+
+ if(!prs_uint8("pack_type0", ps, depth, &rpc->pack_type[0]))
+ return False;
+ if(!prs_uint8("pack_type1", ps, depth, &rpc->pack_type[1]))
+ return False;
+ if(!prs_uint8("pack_type2", ps, depth, &rpc->pack_type[2]))
+ return False;
+ if(!prs_uint8("pack_type3", ps, depth, &rpc->pack_type[3]))
+ return False;
+
+ /*
+ * If reading and pack_type[0] == 0 then the data is in big-endian
+ * format. Set the flag in the prs_struct to specify reverse-endainness.
+ */
+
+ if (UNMARSHALLING(ps) && rpc->pack_type[0] == 0) {
+ DEBUG(10,("smb_io_rpc_hdr: PDU data format is big-endian. Setting flag.\n"));
+ prs_set_endian_data(ps, RPC_BIG_ENDIAN);
+ }
+
+ if(!prs_uint16("frag_len ", ps, depth, &rpc->frag_len))
+ return False;
+ if(!prs_uint16("auth_len ", ps, depth, &rpc->auth_len))
+ return False;
+ if(!prs_uint32("call_id ", ps, depth, &rpc->call_id))
+ return False;
+ return True;
+}
+
+/*******************************************************************
+ Reads or writes an RPC_IFACE structure.
+********************************************************************/
+
+static BOOL smb_io_rpc_iface(const char *desc, RPC_IFACE *ifc, prs_struct *ps, int depth)
+{
+ if (ifc == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "smb_io_rpc_iface");
+ depth++;
+
+ if (!prs_align(ps))
+ return False;
+
+ if (!smb_io_uuid( "uuid", &ifc->uuid, ps, depth))
+ return False;
+
+ if(!prs_uint32 ("version", ps, depth, &ifc->version))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ Inits an RPC_ADDR_STR structure.
+********************************************************************/
+
+static void init_rpc_addr_str(RPC_ADDR_STR *str, const char *name)
+{
+ str->len = strlen(name) + 1;
+ fstrcpy(str->str, name);
+}
+
+/*******************************************************************
+ Reads or writes an RPC_ADDR_STR structure.
+********************************************************************/
+
+static BOOL smb_io_rpc_addr_str(const char *desc, RPC_ADDR_STR *str, prs_struct *ps, int depth)
+{
+ if (str == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "smb_io_rpc_addr_str");
+ depth++;
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint16 ( "len", ps, depth, &str->len))
+ return False;
+ if(!prs_uint8s (True, "str", ps, depth, (uchar*)str->str, MIN(str->len, sizeof(str->str)) ))
+ return False;
+ return True;
+}
+
+/*******************************************************************
+ Inits an RPC_HDR_BBA structure.
+********************************************************************/
+
+static void init_rpc_hdr_bba(RPC_HDR_BBA *bba, uint16 max_tsize, uint16 max_rsize, uint32 assoc_gid)
+{
+ bba->max_tsize = max_tsize; /* maximum transmission fragment size (0x1630) */
+ bba->max_rsize = max_rsize; /* max receive fragment size (0x1630) */
+ bba->assoc_gid = assoc_gid; /* associated group id (0x0) */
+}
+
+/*******************************************************************
+ Reads or writes an RPC_HDR_BBA structure.
+********************************************************************/
+
+static BOOL smb_io_rpc_hdr_bba(const char *desc, RPC_HDR_BBA *rpc, prs_struct *ps, int depth)
+{
+ if (rpc == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "smb_io_rpc_hdr_bba");
+ depth++;
+
+ if(!prs_uint16("max_tsize", ps, depth, &rpc->max_tsize))
+ return False;
+ if(!prs_uint16("max_rsize", ps, depth, &rpc->max_rsize))
+ return False;
+ if(!prs_uint32("assoc_gid", ps, depth, &rpc->assoc_gid))
+ return False;
+ return True;
+}
+
+/*******************************************************************
+ Inits an RPC_HDR_RB structure.
+********************************************************************/
+
+void init_rpc_hdr_rb(RPC_HDR_RB *rpc,
+ uint16 max_tsize, uint16 max_rsize, uint32 assoc_gid,
+ uint32 num_elements, uint16 context_id, uint8 num_syntaxes,
+ RPC_IFACE *abstract, RPC_IFACE *transfer)
+{
+ init_rpc_hdr_bba(&rpc->bba, max_tsize, max_rsize, assoc_gid);
+
+ rpc->num_elements = num_elements ; /* the number of elements (0x1) */
+ rpc->context_id = context_id ; /* presentation context identifier (0x0) */
+ rpc->num_syntaxes = num_syntaxes ; /* the number of syntaxes (has always been 1?)(0x1) */
+
+ /* num and vers. of interface client is using */
+ rpc->abstract = *abstract;
+
+ /* num and vers. of interface to use for replies */
+ rpc->transfer = *transfer;
+}
+
+/*******************************************************************
+ Reads or writes an RPC_HDR_RB structure.
+********************************************************************/
+
+BOOL smb_io_rpc_hdr_rb(const char *desc, RPC_HDR_RB *rpc, prs_struct *ps, int depth)
+{
+ if (rpc == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "smb_io_rpc_hdr_rb");
+ depth++;
+
+ if(!smb_io_rpc_hdr_bba("", &rpc->bba, ps, depth))
+ return False;
+
+ if(!prs_uint32("num_elements", ps, depth, &rpc->num_elements))
+ return False;
+ if(!prs_uint16("context_id ", ps, depth, &rpc->context_id ))
+ return False;
+ if(!prs_uint8 ("num_syntaxes", ps, depth, &rpc->num_syntaxes))
+ return False;
+
+ if(!smb_io_rpc_iface("", &rpc->abstract, ps, depth))
+ return False;
+ if(!smb_io_rpc_iface("", &rpc->transfer, ps, depth))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ Inits an RPC_RESULTS structure.
+
+ lkclXXXX only one reason at the moment!
+********************************************************************/
+
+static void init_rpc_results(RPC_RESULTS *res,
+ uint8 num_results, uint16 result, uint16 reason)
+{
+ res->num_results = num_results; /* the number of results (0x01) */
+ res->result = result ; /* result (0x00 = accept) */
+ res->reason = reason ; /* reason (0x00 = no reason specified) */
+}
+
+/*******************************************************************
+ Reads or writes an RPC_RESULTS structure.
+
+ lkclXXXX only one reason at the moment!
+********************************************************************/
+
+static BOOL smb_io_rpc_results(const char *desc, RPC_RESULTS *res, prs_struct *ps, int depth)
+{
+ if (res == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "smb_io_rpc_results");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint8 ("num_results", ps, depth, &res->num_results))
+ return False;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint16("result ", ps, depth, &res->result))
+ return False;
+ if(!prs_uint16("reason ", ps, depth, &res->reason))
+ return False;
+ return True;
+}
+
+/*******************************************************************
+ Init an RPC_HDR_BA structure.
+
+ lkclXXXX only one reason at the moment!
+
+********************************************************************/
+
+void init_rpc_hdr_ba(RPC_HDR_BA *rpc,
+ uint16 max_tsize, uint16 max_rsize, uint32 assoc_gid,
+ const char *pipe_addr,
+ uint8 num_results, uint16 result, uint16 reason,
+ RPC_IFACE *transfer)
+{
+ init_rpc_hdr_bba (&rpc->bba, max_tsize, max_rsize, assoc_gid);
+ init_rpc_addr_str(&rpc->addr, pipe_addr);
+ init_rpc_results (&rpc->res, num_results, result, reason);
+
+ /* the transfer syntax from the request */
+ memcpy(&rpc->transfer, transfer, sizeof(rpc->transfer));
+}
+
+/*******************************************************************
+ Reads or writes an RPC_HDR_BA structure.
+********************************************************************/
+
+BOOL smb_io_rpc_hdr_ba(const char *desc, RPC_HDR_BA *rpc, prs_struct *ps, int depth)
+{
+ if (rpc == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "smb_io_rpc_hdr_ba");
+ depth++;
+
+ if(!smb_io_rpc_hdr_bba("", &rpc->bba, ps, depth))
+ return False;
+ if(!smb_io_rpc_addr_str("", &rpc->addr, ps, depth))
+ return False;
+ if(!smb_io_rpc_results("", &rpc->res, ps, depth))
+ return False;
+ if(!smb_io_rpc_iface("", &rpc->transfer, ps, depth))
+ return False;
+ return True;
+}
+
+/*******************************************************************
+ Init an RPC_HDR_REQ structure.
+********************************************************************/
+
+void init_rpc_hdr_req(RPC_HDR_REQ *hdr, uint32 alloc_hint, uint16 opnum)
+{
+ hdr->alloc_hint = alloc_hint; /* allocation hint */
+ hdr->context_id = 0; /* presentation context identifier */
+ hdr->opnum = opnum; /* opnum */
+}
+
+/*******************************************************************
+ Reads or writes an RPC_HDR_REQ structure.
+********************************************************************/
+
+BOOL smb_io_rpc_hdr_req(const char *desc, RPC_HDR_REQ *rpc, prs_struct *ps, int depth)
+{
+ if (rpc == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "smb_io_rpc_hdr_req");
+ depth++;
+
+ if(!prs_uint32("alloc_hint", ps, depth, &rpc->alloc_hint))
+ return False;
+ if(!prs_uint16("context_id", ps, depth, &rpc->context_id))
+ return False;
+ if(!prs_uint16("opnum ", ps, depth, &rpc->opnum))
+ return False;
+ return True;
+}
+
+/*******************************************************************
+ Reads or writes an RPC_HDR_RESP structure.
+********************************************************************/
+
+BOOL smb_io_rpc_hdr_resp(const char *desc, RPC_HDR_RESP *rpc, prs_struct *ps, int depth)
+{
+ if (rpc == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "smb_io_rpc_hdr_resp");
+ depth++;
+
+ if(!prs_uint32("alloc_hint", ps, depth, &rpc->alloc_hint))
+ return False;
+ if(!prs_uint16("context_id", ps, depth, &rpc->context_id))
+ return False;
+ if(!prs_uint8 ("cancel_ct ", ps, depth, &rpc->cancel_count))
+ return False;
+ if(!prs_uint8 ("reserved ", ps, depth, &rpc->reserved))
+ return False;
+ return True;
+}
+
+/*******************************************************************
+ Reads or writes an RPC_HDR_FAULT structure.
+********************************************************************/
+
+BOOL smb_io_rpc_hdr_fault(const char *desc, RPC_HDR_FAULT *rpc, prs_struct *ps, int depth)
+{
+ if (rpc == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "smb_io_rpc_hdr_fault");
+ depth++;
+
+ if(!prs_ntstatus("status ", ps, depth, &rpc->status))
+ return False;
+ if(!prs_uint32("reserved", ps, depth, &rpc->reserved))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ Init an RPC_HDR_AUTHA structure.
+********************************************************************/
+
+void init_rpc_hdr_autha(RPC_HDR_AUTHA *rai,
+ uint16 max_tsize, uint16 max_rsize,
+ uint8 auth_type, uint8 auth_level,
+ uint8 stub_type_len)
+{
+ rai->max_tsize = max_tsize; /* maximum transmission fragment size (0x1630) */
+ rai->max_rsize = max_rsize; /* max receive fragment size (0x1630) */
+
+ rai->auth_type = auth_type; /* nt lm ssp 0x0a */
+ rai->auth_level = auth_level; /* 0x06 */
+ rai->stub_type_len = stub_type_len; /* 0x00 */
+ rai->padding = 0; /* padding 0x00 */
+
+ rai->unknown = 0x0014a0c0; /* non-zero pointer to something */
+}
+
+/*******************************************************************
+ Reads or writes an RPC_HDR_AUTHA structure.
+********************************************************************/
+
+BOOL smb_io_rpc_hdr_autha(const char *desc, RPC_HDR_AUTHA *rai, prs_struct *ps, int depth)
+{
+ if (rai == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "smb_io_rpc_hdr_autha");
+ depth++;
+
+ if(!prs_uint16("max_tsize ", ps, depth, &rai->max_tsize))
+ return False;
+ if(!prs_uint16("max_rsize ", ps, depth, &rai->max_rsize))
+ return False;
+
+ if(!prs_uint8 ("auth_type ", ps, depth, &rai->auth_type)) /* 0x0a nt lm ssp */
+ return False;
+ if(!prs_uint8 ("auth_level ", ps, depth, &rai->auth_level)) /* 0x06 */
+ return False;
+ if(!prs_uint8 ("stub_type_len", ps, depth, &rai->stub_type_len))
+ return False;
+ if(!prs_uint8 ("padding ", ps, depth, &rai->padding))
+ return False;
+
+ if(!prs_uint32("unknown ", ps, depth, &rai->unknown)) /* 0x0014a0c0 */
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ Inits an RPC_HDR_AUTH structure.
+********************************************************************/
+
+void init_rpc_hdr_auth(RPC_HDR_AUTH *rai,
+ uint8 auth_type, uint8 auth_level,
+ uint8 padding,
+ uint32 ptr)
+{
+ rai->auth_type = auth_type; /* nt lm ssp 0x0a */
+ rai->auth_level = auth_level; /* 0x06 */
+ rai->padding = padding;
+ rai->reserved = 0;
+
+ rai->auth_context = ptr; /* non-zero pointer to something */
+}
+
+/*******************************************************************
+ Reads or writes an RPC_HDR_AUTH structure.
+********************************************************************/
+
+BOOL smb_io_rpc_hdr_auth(const char *desc, RPC_HDR_AUTH *rai, prs_struct *ps, int depth)
+{
+ if (rai == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "smb_io_rpc_hdr_auth");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint8 ("auth_type ", ps, depth, &rai->auth_type)) /* 0x0a nt lm ssp */
+ return False;
+ if(!prs_uint8 ("auth_level ", ps, depth, &rai->auth_level)) /* 0x06 */
+ return False;
+ if(!prs_uint8 ("padding ", ps, depth, &rai->padding))
+ return False;
+ if(!prs_uint8 ("reserved ", ps, depth, &rai->reserved))
+ return False;
+ if(!prs_uint32("auth_context ", ps, depth, &rai->auth_context))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ Checks an RPC_AUTH_VERIFIER structure.
+********************************************************************/
+
+BOOL rpc_auth_verifier_chk(RPC_AUTH_VERIFIER *rav,
+ const char *signature, uint32 msg_type)
+{
+ return (strequal(rav->signature, signature) && rav->msg_type == msg_type);
+}
+
+/*******************************************************************
+ Inits an RPC_AUTH_VERIFIER structure.
+********************************************************************/
+
+void init_rpc_auth_verifier(RPC_AUTH_VERIFIER *rav,
+ const char *signature, uint32 msg_type)
+{
+ fstrcpy(rav->signature, signature); /* "NTLMSSP" */
+ rav->msg_type = msg_type; /* NTLMSSP_MESSAGE_TYPE */
+}
+
+/*******************************************************************
+ Reads or writes an RPC_AUTH_VERIFIER structure.
+********************************************************************/
+
+BOOL smb_io_rpc_auth_verifier(const char *desc, RPC_AUTH_VERIFIER *rav, prs_struct *ps, int depth)
+{
+ if (rav == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "smb_io_rpc_auth_verifier");
+ depth++;
+
+ /* "NTLMSSP" */
+ if(!prs_string("signature", ps, depth, rav->signature,
+ sizeof(rav->signature)))
+ return False;
+ if(!prs_uint32("msg_type ", ps, depth, &rav->msg_type)) /* NTLMSSP_MESSAGE_TYPE */
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ This parses an RPC_AUTH_VERIFIER for NETLOGON schannel. I think
+ assuming "NTLMSSP" in sm_io_rpc_auth_verifier is somewhat wrong.
+ I have to look at that later...
+********************************************************************/
+
+BOOL smb_io_rpc_netsec_verifier(const char *desc, RPC_AUTH_VERIFIER *rav, prs_struct *ps, int depth)
+{
+ if (rav == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "smb_io_rpc_auth_verifier");
+ depth++;
+
+ if(!prs_string("signature", ps, depth, rav->signature, sizeof(rav->signature)))
+ return False;
+ if(!prs_uint32("msg_type ", ps, depth, &rav->msg_type))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ Inits an RPC_AUTH_NTLMSSP_NEG structure.
+********************************************************************/
+
+void init_rpc_auth_ntlmssp_neg(RPC_AUTH_NTLMSSP_NEG *neg,
+ uint32 neg_flgs,
+ const char *myname, const char *domain)
+{
+ int len_myname = strlen(myname);
+ int len_domain = strlen(domain);
+
+ neg->neg_flgs = neg_flgs ; /* 0x00b2b3 */
+
+ init_str_hdr(&neg->hdr_domain, len_domain, len_domain, 0x20 + len_myname);
+ init_str_hdr(&neg->hdr_myname, len_myname, len_myname, 0x20);
+
+ fstrcpy(neg->myname, myname);
+ fstrcpy(neg->domain, domain);
+}
+
+/*******************************************************************
+ Reads or writes an RPC_AUTH_NTLMSSP_NEG structure.
+
+ *** lkclXXXX HACK ALERT! ***
+********************************************************************/
+
+BOOL smb_io_rpc_auth_ntlmssp_neg(const char *desc, RPC_AUTH_NTLMSSP_NEG *neg, prs_struct *ps, int depth)
+{
+ uint32 start_offset = prs_offset(ps);
+ if (neg == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "smb_io_rpc_auth_ntlmssp_neg");
+ depth++;
+
+ if(!prs_uint32("neg_flgs ", ps, depth, &neg->neg_flgs))
+ return False;
+
+ if (ps->io) {
+ uint32 old_offset;
+ uint32 old_neg_flags = neg->neg_flgs;
+
+ /* reading */
+
+ ZERO_STRUCTP(neg);
+
+ neg->neg_flgs = old_neg_flags;
+
+ if(!smb_io_strhdr("hdr_domain", &neg->hdr_domain, ps, depth))
+ return False;
+ if(!smb_io_strhdr("hdr_myname", &neg->hdr_myname, ps, depth))
+ return False;
+
+ old_offset = prs_offset(ps);
+
+ if(!prs_set_offset(ps, neg->hdr_myname.buffer + start_offset - 12))
+ return False;
+
+ if(!prs_uint8s(True, "myname", ps, depth, (uint8*)neg->myname,
+ MIN(neg->hdr_myname.str_str_len, sizeof(neg->myname))))
+ return False;
+
+ old_offset += neg->hdr_myname.str_str_len;
+
+ if(!prs_set_offset(ps, neg->hdr_domain.buffer + start_offset - 12))
+ return False;
+
+ if(!prs_uint8s(True, "domain", ps, depth, (uint8*)neg->domain,
+ MIN(neg->hdr_domain.str_str_len, sizeof(neg->domain ))))
+ return False;
+
+ old_offset += neg->hdr_domain .str_str_len;
+
+ if(!prs_set_offset(ps, old_offset))
+ return False;
+ } else {
+ /* writing */
+ if(!smb_io_strhdr("hdr_domain", &neg->hdr_domain, ps, depth))
+ return False;
+ if(!smb_io_strhdr("hdr_myname", &neg->hdr_myname, ps, depth))
+ return False;
+
+ if(!prs_uint8s(True, "myname", ps, depth, (uint8*)neg->myname,
+ MIN(neg->hdr_myname.str_str_len, sizeof(neg->myname))))
+ return False;
+ if(!prs_uint8s(True, "domain", ps, depth, (uint8*)neg->domain,
+ MIN(neg->hdr_domain.str_str_len, sizeof(neg->domain ))))
+ return False;
+ }
+
+ return True;
+}
+
+/*******************************************************************
+creates an RPC_AUTH_NTLMSSP_CHAL structure.
+********************************************************************/
+
+void init_rpc_auth_ntlmssp_chal(RPC_AUTH_NTLMSSP_CHAL *chl,
+ uint32 neg_flags,
+ uint8 challenge[8])
+{
+ chl->unknown_1 = 0x0;
+ chl->unknown_2 = 0x00000028;
+ chl->neg_flags = neg_flags; /* 0x0082b1 */
+
+ memcpy(chl->challenge, challenge, sizeof(chl->challenge));
+ memset((char *)chl->reserved , '\0', sizeof(chl->reserved));
+}
+
+/*******************************************************************
+ Reads or writes an RPC_AUTH_NTLMSSP_CHAL structure.
+********************************************************************/
+
+BOOL smb_io_rpc_auth_ntlmssp_chal(const char *desc, RPC_AUTH_NTLMSSP_CHAL *chl, prs_struct *ps, int depth)
+{
+ if (chl == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "smb_io_rpc_auth_ntlmssp_chal");
+ depth++;
+
+ if(!prs_uint32("unknown_1", ps, depth, &chl->unknown_1)) /* 0x0000 0000 */
+ return False;
+ if(!prs_uint32("unknown_2", ps, depth, &chl->unknown_2)) /* 0x0000 b2b3 */
+ return False;
+ if(!prs_uint32("neg_flags", ps, depth, &chl->neg_flags)) /* 0x0000 82b1 */
+ return False;
+
+ if(!prs_uint8s (False, "challenge", ps, depth, chl->challenge, sizeof(chl->challenge)))
+ return False;
+ if(!prs_uint8s (False, "reserved ", ps, depth, chl->reserved , sizeof(chl->reserved )))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ Inits an RPC_AUTH_NTLMSSP_RESP structure.
+
+ *** lkclXXXX FUDGE! HAVE TO MANUALLY SPECIFY OFFSET HERE (0x1c bytes) ***
+ *** lkclXXXX the actual offset is at the start of the auth verifier ***
+********************************************************************/
+
+void init_rpc_auth_ntlmssp_resp(RPC_AUTH_NTLMSSP_RESP *rsp,
+ uchar lm_resp[24], uchar nt_resp[24],
+ const char *domain, const char *user, const char *wks,
+ uint32 neg_flags)
+{
+ uint32 offset;
+ int dom_len = strlen(domain);
+ int wks_len = strlen(wks);
+ int usr_len = strlen(user);
+ int lm_len = (lm_resp != NULL) ? 24 : 0;
+ int nt_len = (nt_resp != NULL) ? 24 : 0;
+
+ DEBUG(5,("make_rpc_auth_ntlmssp_resp\n"));
+
+#ifdef DEBUG_PASSWORD
+ DEBUG(100,("lm_resp\n"));
+ dump_data(100, (char *)lm_resp, 24);
+ DEBUG(100,("nt_resp\n"));
+ dump_data(100, (char *)nt_resp, 24);
+#endif
+
+ DEBUG(6,("dom: %s user: %s wks: %s neg_flgs: 0x%x\n",
+ domain, user, wks, neg_flags));
+
+ offset = 0x40;
+
+ if (neg_flags & NTLMSSP_NEGOTIATE_UNICODE) {
+ dom_len *= 2;
+ wks_len *= 2;
+ usr_len *= 2;
+ }
+
+ init_str_hdr(&rsp->hdr_domain, dom_len, dom_len, offset);
+ offset += dom_len;
+
+ init_str_hdr(&rsp->hdr_usr, usr_len, usr_len, offset);
+ offset += usr_len;
+
+ init_str_hdr(&rsp->hdr_wks, wks_len, wks_len, offset);
+ offset += wks_len;
+
+ init_str_hdr(&rsp->hdr_lm_resp, lm_len, lm_len, offset);
+ offset += lm_len;
+
+ init_str_hdr(&rsp->hdr_nt_resp, nt_len, nt_len, offset);
+ offset += nt_len;
+
+ init_str_hdr(&rsp->hdr_sess_key, 0, 0, offset);
+
+ rsp->neg_flags = neg_flags;
+
+ memcpy(rsp->lm_resp, lm_resp, 24);
+ memcpy(rsp->nt_resp, nt_resp, 24);
+
+ if (neg_flags & NTLMSSP_NEGOTIATE_UNICODE) {
+ rpcstr_push(rsp->domain, domain, sizeof(rsp->domain), 0);
+ rpcstr_push(rsp->user, user, sizeof(rsp->user), 0);
+ rpcstr_push(rsp->wks, wks, sizeof(rsp->wks), 0);
+ } else {
+ fstrcpy(rsp->domain, domain);
+ fstrcpy(rsp->user, user);
+ fstrcpy(rsp->wks, wks);
+ }
+
+ rsp->sess_key[0] = 0;
+}
+
+/*******************************************************************
+ Reads or writes an RPC_AUTH_NTLMSSP_RESP structure.
+
+ *** lkclXXXX FUDGE! HAVE TO MANUALLY SPECIFY OFFSET HERE (0x1c bytes) ***
+ *** lkclXXXX the actual offset is at the start of the auth verifier ***
+********************************************************************/
+
+BOOL smb_io_rpc_auth_ntlmssp_resp(const char *desc, RPC_AUTH_NTLMSSP_RESP *rsp, prs_struct *ps, int depth)
+{
+ if (rsp == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "smb_io_rpc_auth_ntlmssp_resp");
+ depth++;
+
+ if (ps->io) {
+ uint32 old_offset;
+
+ /* reading */
+
+ ZERO_STRUCTP(rsp);
+
+ if(!smb_io_strhdr("hdr_lm_resp ", &rsp->hdr_lm_resp, ps, depth))
+ return False;
+ if(!smb_io_strhdr("hdr_nt_resp ", &rsp->hdr_nt_resp, ps, depth))
+ return False;
+ if(!smb_io_strhdr("hdr_domain ", &rsp->hdr_domain, ps, depth))
+ return False;
+ if(!smb_io_strhdr("hdr_user ", &rsp->hdr_usr, ps, depth))
+ return False;
+ if(!smb_io_strhdr("hdr_wks ", &rsp->hdr_wks, ps, depth))
+ return False;
+ if(!smb_io_strhdr("hdr_sess_key", &rsp->hdr_sess_key, ps, depth))
+ return False;
+
+ if(!prs_uint32("neg_flags", ps, depth, &rsp->neg_flags)) /* 0x0000 82b1 */
+ return False;
+
+ old_offset = prs_offset(ps);
+
+ if(!prs_set_offset(ps, rsp->hdr_domain.buffer + 0xc))
+ return False;
+
+ if(!prs_uint8s(True , "domain ", ps, depth, (uint8*)rsp->domain,
+ MIN(rsp->hdr_domain.str_str_len, sizeof(rsp->domain))))
+ return False;
+
+ old_offset += rsp->hdr_domain.str_str_len;
+
+ if(!prs_set_offset(ps, rsp->hdr_usr.buffer + 0xc))
+ return False;
+
+ if(!prs_uint8s(True , "user ", ps, depth, (uint8*)rsp->user,
+ MIN(rsp->hdr_usr.str_str_len, sizeof(rsp->user))))
+ return False;
+
+ old_offset += rsp->hdr_usr.str_str_len;
+
+ if(!prs_set_offset(ps, rsp->hdr_wks.buffer + 0xc))
+ return False;
+
+ if(!prs_uint8s(True, "wks ", ps, depth, (uint8*)rsp->wks,
+ MIN(rsp->hdr_wks.str_str_len, sizeof(rsp->wks))))
+ return False;
+
+ old_offset += rsp->hdr_wks.str_str_len;
+
+ if(!prs_set_offset(ps, rsp->hdr_lm_resp.buffer + 0xc))
+ return False;
+
+ if(!prs_uint8s(False, "lm_resp ", ps, depth, (uint8*)rsp->lm_resp,
+ MIN(rsp->hdr_lm_resp.str_str_len, sizeof(rsp->lm_resp ))))
+ return False;
+
+ old_offset += rsp->hdr_lm_resp.str_str_len;
+
+ if(!prs_set_offset(ps, rsp->hdr_nt_resp.buffer + 0xc))
+ return False;
+
+ if(!prs_uint8s(False, "nt_resp ", ps, depth, (uint8*)rsp->nt_resp,
+ MIN(rsp->hdr_nt_resp.str_str_len, sizeof(rsp->nt_resp ))))
+ return False;
+
+ old_offset += rsp->hdr_nt_resp.str_str_len;
+
+ if (rsp->hdr_sess_key.str_str_len != 0) {
+
+ if(!prs_set_offset(ps, rsp->hdr_sess_key.buffer + 0x10))
+ return False;
+
+ old_offset += rsp->hdr_sess_key.str_str_len;
+
+ if(!prs_uint8s(False, "sess_key", ps, depth, (uint8*)rsp->sess_key,
+ MIN(rsp->hdr_sess_key.str_str_len, sizeof(rsp->sess_key))))
+ return False;
+ }
+
+ if(!prs_set_offset(ps, old_offset))
+ return False;
+ } else {
+ /* writing */
+ if(!smb_io_strhdr("hdr_lm_resp ", &rsp->hdr_lm_resp, ps, depth))
+ return False;
+ if(!smb_io_strhdr("hdr_nt_resp ", &rsp->hdr_nt_resp, ps, depth))
+ return False;
+ if(!smb_io_strhdr("hdr_domain ", &rsp->hdr_domain, ps, depth))
+ return False;
+ if(!smb_io_strhdr("hdr_user ", &rsp->hdr_usr, ps, depth))
+ return False;
+ if(!smb_io_strhdr("hdr_wks ", &rsp->hdr_wks, ps, depth))
+ return False;
+ if(!smb_io_strhdr("hdr_sess_key", &rsp->hdr_sess_key, ps, depth))
+ return False;
+
+ if(!prs_uint32("neg_flags", ps, depth, &rsp->neg_flags)) /* 0x0000 82b1 */
+ return False;
+
+ if(!prs_uint8s(True , "domain ", ps, depth, (uint8*)rsp->domain,
+ MIN(rsp->hdr_domain.str_str_len, sizeof(rsp->domain))))
+ return False;
+
+ if(!prs_uint8s(True , "user ", ps, depth, (uint8*)rsp->user,
+ MIN(rsp->hdr_usr.str_str_len, sizeof(rsp->user))))
+ return False;
+
+ if(!prs_uint8s(True , "wks ", ps, depth, (uint8*)rsp->wks,
+ MIN(rsp->hdr_wks.str_str_len, sizeof(rsp->wks))))
+ return False;
+ if(!prs_uint8s(False, "lm_resp ", ps, depth, (uint8*)rsp->lm_resp,
+ MIN(rsp->hdr_lm_resp .str_str_len, sizeof(rsp->lm_resp))))
+ return False;
+ if(!prs_uint8s(False, "nt_resp ", ps, depth, (uint8*)rsp->nt_resp,
+ MIN(rsp->hdr_nt_resp .str_str_len, sizeof(rsp->nt_resp ))))
+ return False;
+ if(!prs_uint8s(False, "sess_key", ps, depth, (uint8*)rsp->sess_key,
+ MIN(rsp->hdr_sess_key.str_str_len, sizeof(rsp->sess_key))))
+ return False;
+ }
+
+ return True;
+}
+
+/*******************************************************************
+ Checks an RPC_AUTH_NTLMSSP_CHK structure.
+********************************************************************/
+
+BOOL rpc_auth_ntlmssp_chk(RPC_AUTH_NTLMSSP_CHK *chk, uint32 crc32, uint32 seq_num)
+{
+ if (chk == NULL)
+ return False;
+
+ if (chk->crc32 != crc32 ||
+ chk->ver != NTLMSSP_SIGN_VERSION ||
+ chk->seq_num != seq_num)
+ {
+ DEBUG(5,("verify failed - crc %x ver %x seq %d\n",
+ chk->crc32, chk->ver, chk->seq_num));
+
+ DEBUG(5,("verify expect - crc %x ver %x seq %d\n",
+ crc32, NTLMSSP_SIGN_VERSION, seq_num));
+ return False;
+ }
+ return True;
+}
+
+/*******************************************************************
+ Inits an RPC_AUTH_NTLMSSP_CHK structure.
+********************************************************************/
+
+void init_rpc_auth_ntlmssp_chk(RPC_AUTH_NTLMSSP_CHK *chk,
+ uint32 ver, uint32 crc32, uint32 seq_num)
+{
+ chk->ver = ver;
+ chk->reserved = 0x0;
+ chk->crc32 = crc32;
+ chk->seq_num = seq_num;
+}
+
+/*******************************************************************
+ Reads or writes an RPC_AUTH_NTLMSSP_CHK structure.
+********************************************************************/
+
+BOOL smb_io_rpc_auth_ntlmssp_chk(const char *desc, RPC_AUTH_NTLMSSP_CHK *chk, prs_struct *ps, int depth)
+{
+ if (chk == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "smb_io_rpc_auth_ntlmssp_chk");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("ver ", ps, depth, &chk->ver))
+ return False;
+ if(!prs_uint32("reserved", ps, depth, &chk->reserved))
+ return False;
+ if(!prs_uint32("crc32 ", ps, depth, &chk->crc32))
+ return False;
+ if(!prs_uint32("seq_num ", ps, depth, &chk->seq_num))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+creates an RPC_AUTH_NETSEC_NEG structure.
+********************************************************************/
+void init_rpc_auth_netsec_neg(RPC_AUTH_NETSEC_NEG *neg,
+ const char *domain, const char *myname)
+{
+ neg->type1 = 0;
+ neg->type2 = 0x3;
+ fstrcpy(neg->domain, domain);
+ fstrcpy(neg->myname, myname);
+}
+
+/*******************************************************************
+ Reads or writes an RPC_AUTH_NETSEC_NEG structure.
+********************************************************************/
+
+BOOL smb_io_rpc_auth_netsec_neg(const char *desc, RPC_AUTH_NETSEC_NEG *neg,
+ prs_struct *ps, int depth)
+{
+ if (neg == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "smb_io_rpc_auth_netsec_neg");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("type1", ps, depth, &neg->type1))
+ return False;
+ if(!prs_uint32("type2", ps, depth, &neg->type2))
+ return False;
+ if(!prs_string("domain ", ps, depth, neg->domain, sizeof(neg->domain)))
+ return False;
+ if(!prs_string("myname ", ps, depth, neg->myname, sizeof(neg->myname)))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+reads or writes an RPC_AUTH_NETSEC_CHK structure.
+********************************************************************/
+BOOL smb_io_rpc_auth_netsec_chk(const char *desc, RPC_AUTH_NETSEC_CHK * chk,
+ prs_struct *ps, int depth)
+{
+ if (chk == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "smb_io_rpc_auth_netsec_chk");
+ depth++;
+
+ prs_uint8s(False, "sig ", ps, depth, chk->sig, sizeof(chk->sig));
+ prs_uint8s(False, "seq_num", ps, depth, chk->seq_num, sizeof(chk->seq_num));
+ prs_uint8s(False, "packet_digest", ps, depth, chk->packet_digest, sizeof(chk->packet_digest));
+ prs_uint8s(False, "confounder", ps, depth, chk->confounder, sizeof(chk->confounder));
+
+ return True;
+}
+
diff --git a/source/rpc_parse/parse_samr.c b/source/rpc_parse/parse_samr.c
new file mode 100644
index 00000000000..287dc3bd7f2
--- /dev/null
+++ b/source/rpc_parse/parse_samr.c
@@ -0,0 +1,7387 @@
+/*
+ * Unix SMB/CIFS implementation.
+ * RPC Pipe client / server routines
+ * Copyright (C) Andrew Tridgell 1992-2000,
+ * Copyright (C) Luke Kenneth Casson Leighton 1996-2000,
+ * Copyright (C) Paul Ashton 1997-2000,
+ * Copyright (C) Elrond 2000,
+ * Copyright (C) Jeremy Allison 2001,
+ * Copyright (C) Jean François Micouleau 1998-2001,
+ * Copyright (C) Jim McDonough <jmcd@us.ibm.com> 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 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 "rpc_parse.h"
+#include "nterr.h"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_RPC_PARSE
+
+/*******************************************************************
+inits a SAMR_Q_CLOSE_HND structure.
+********************************************************************/
+
+void init_samr_q_close_hnd(SAMR_Q_CLOSE_HND * q_c, POLICY_HND *hnd)
+{
+ DEBUG(5, ("init_samr_q_close_hnd\n"));
+
+ q_c->pol = *hnd;
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+BOOL samr_io_q_close_hnd(const char *desc, SAMR_Q_CLOSE_HND * q_u,
+ prs_struct *ps, int depth)
+{
+ if (q_u == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "samr_io_q_close_hnd");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ return smb_io_pol_hnd("pol", &q_u->pol, ps, depth);
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+BOOL samr_io_r_close_hnd(const char *desc, SAMR_R_CLOSE_HND * r_u,
+ prs_struct *ps, int depth)
+{
+ if (r_u == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "samr_io_r_close_hnd");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!smb_io_pol_hnd("pol", &r_u->pol, ps, depth))
+ return False;
+
+ if(!prs_ntstatus("status", ps, depth, &r_u->status))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+inits a SAMR_Q_LOOKUP_DOMAIN structure.
+********************************************************************/
+
+void init_samr_q_lookup_domain(SAMR_Q_LOOKUP_DOMAIN * q_u,
+ POLICY_HND *pol, char *dom_name)
+{
+ DEBUG(5, ("init_samr_q_lookup_domain\n"));
+
+ q_u->connect_pol = *pol;
+
+ init_unistr2(&q_u->uni_domain, dom_name, UNI_FLAGS_NONE);
+ init_uni_hdr(&q_u->hdr_domain, &q_u->uni_domain);
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+BOOL samr_io_q_lookup_domain(const char *desc, SAMR_Q_LOOKUP_DOMAIN * q_u,
+ prs_struct *ps, int depth)
+{
+ if (q_u == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "samr_io_q_lookup_domain");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!smb_io_pol_hnd("connect_pol", &q_u->connect_pol, ps, depth))
+ return False;
+
+ if(!smb_io_unihdr("hdr_domain", &q_u->hdr_domain, ps, depth))
+ return False;
+
+ if(!smb_io_unistr2("uni_domain", &q_u->uni_domain, q_u->hdr_domain.buffer, ps, depth))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+inits a SAMR_R_LOOKUP_DOMAIN structure.
+********************************************************************/
+
+void init_samr_r_lookup_domain(SAMR_R_LOOKUP_DOMAIN * r_u,
+ DOM_SID *dom_sid, NTSTATUS status)
+{
+ DEBUG(5, ("init_samr_r_lookup_domain\n"));
+
+ r_u->status = status;
+ r_u->ptr_sid = 0;
+ if (NT_STATUS_IS_OK(status)) {
+ r_u->ptr_sid = 1;
+ init_dom_sid2(&r_u->dom_sid, dom_sid);
+ }
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+BOOL samr_io_r_lookup_domain(const char *desc, SAMR_R_LOOKUP_DOMAIN * r_u,
+ prs_struct *ps, int depth)
+{
+ if (r_u == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "samr_io_r_lookup_domain");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("ptr", ps, depth, &r_u->ptr_sid))
+ return False;
+
+ if (r_u->ptr_sid != 0) {
+ if(!smb_io_dom_sid2("sid", &r_u->dom_sid, ps, depth))
+ return False;
+ if(!prs_align(ps))
+ return False;
+ }
+
+ if(!prs_ntstatus("status", ps, depth, &r_u->status))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+void init_samr_q_remove_sid_foreign_domain(SAMR_Q_REMOVE_SID_FOREIGN_DOMAIN * q_u, POLICY_HND *dom_pol, DOM_SID *sid)
+{
+ DEBUG(5, ("samr_init_samr_q_remove_sid_foreign_domain\n"));
+
+ q_u->dom_pol = *dom_pol;
+ init_dom_sid2(&q_u->sid, sid);
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+BOOL samr_io_q_remove_sid_foreign_domain(const char *desc, SAMR_Q_REMOVE_SID_FOREIGN_DOMAIN * q_u,
+ prs_struct *ps, int depth)
+{
+ if (q_u == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "samr_io_q_remove_sid_foreign_domain");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!smb_io_pol_hnd("domain_pol", &q_u->dom_pol, ps, depth))
+ return False;
+
+ if(!smb_io_dom_sid2("sid", &q_u->sid, ps, depth))
+ return False;
+
+ if(!prs_align(ps))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+BOOL samr_io_r_remove_sid_foreign_domain(const char *desc, SAMR_R_REMOVE_SID_FOREIGN_DOMAIN * r_u,
+ prs_struct *ps, int depth)
+{
+ if (r_u == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "samr_io_r_remove_sid_foreign_domain");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_ntstatus("status", ps, depth, &r_u->status))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+void init_samr_q_open_domain(SAMR_Q_OPEN_DOMAIN * q_u,
+ POLICY_HND *pol, uint32 flags,
+ const DOM_SID *sid)
+{
+ DEBUG(5, ("samr_init_samr_q_open_domain\n"));
+
+ q_u->pol = *pol;
+ q_u->flags = flags;
+ init_dom_sid2(&q_u->dom_sid, sid);
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+BOOL samr_io_q_open_domain(const char *desc, SAMR_Q_OPEN_DOMAIN * q_u,
+ prs_struct *ps, int depth)
+{
+ if (q_u == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "samr_io_q_open_domain");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!smb_io_pol_hnd("pol", &q_u->pol, ps, depth))
+ return False;
+
+ if(!prs_uint32("flags", ps, depth, &q_u->flags))
+ return False;
+
+ if(!smb_io_dom_sid2("sid", &q_u->dom_sid, ps, depth))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+BOOL samr_io_r_open_domain(const char *desc, SAMR_R_OPEN_DOMAIN * r_u,
+ prs_struct *ps, int depth)
+{
+ if (r_u == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "samr_io_r_open_domain");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!smb_io_pol_hnd("domain_pol", &r_u->domain_pol, ps, depth))
+ return False;
+
+ if(!prs_ntstatus("status", ps, depth, &r_u->status))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+void init_samr_q_get_usrdom_pwinfo(SAMR_Q_GET_USRDOM_PWINFO * q_u,
+ POLICY_HND *user_pol)
+{
+ DEBUG(5, ("samr_init_samr_q_get_usrdom_pwinfo\n"));
+
+ q_u->user_pol = *user_pol;
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+BOOL samr_io_q_get_usrdom_pwinfo(const char *desc, SAMR_Q_GET_USRDOM_PWINFO * q_u,
+ prs_struct *ps, int depth)
+{
+ if (q_u == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "samr_io_q_get_usrdom_pwinfo");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ return smb_io_pol_hnd("user_pol", &q_u->user_pol, ps, depth);
+}
+
+/*******************************************************************
+ Init.
+********************************************************************/
+
+void init_samr_r_get_usrdom_pwinfo(SAMR_R_GET_USRDOM_PWINFO *r_u, NTSTATUS status)
+{
+ DEBUG(5, ("init_samr_r_get_usrdom_pwinfo\n"));
+
+ r_u->unknown_0 = 0x0000;
+
+ /*
+ * used to be
+ * r_u->unknown_1 = 0x0015;
+ * but for trusts.
+ */
+ r_u->unknown_1 = 0x01D1;
+ r_u->unknown_1 = 0x0015;
+
+ r_u->unknown_2 = 0x00000000;
+
+ r_u->status = status;
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+BOOL samr_io_r_get_usrdom_pwinfo(const char *desc, SAMR_R_GET_USRDOM_PWINFO * r_u,
+ prs_struct *ps, int depth)
+{
+ if (r_u == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "samr_io_r_get_usrdom_pwinfo");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint16("unknown_0", ps, depth, &r_u->unknown_0))
+ return False;
+ if(!prs_uint16("unknown_1", ps, depth, &r_u->unknown_1))
+ return False;
+ if(!prs_uint32("unknown_2", ps, depth, &r_u->unknown_2))
+ return False;
+ if(!prs_ntstatus("status ", ps, depth, &r_u->status))
+ return False;
+
+ return True;
+}
+
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+BOOL samr_io_q_set_sec_obj(const char *desc, SAMR_Q_SET_SEC_OBJ * q_u,
+ prs_struct *ps, int depth)
+{
+ if (q_u == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "samr_io_q_set_sec_obj");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!smb_io_pol_hnd("pol", &q_u->pol, ps, depth))
+ return False;
+
+ if(!prs_uint32("sec_info", ps, depth, &q_u->sec_info))
+ return False;
+
+ if(!sec_io_desc_buf("sec_desc", &q_u->buf, ps, depth))
+ return False;
+
+ return True;
+}
+
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+void init_samr_q_query_sec_obj(SAMR_Q_QUERY_SEC_OBJ * q_u,
+ POLICY_HND *user_pol, uint32 sec_info)
+{
+ DEBUG(5, ("samr_init_samr_q_query_sec_obj\n"));
+
+ q_u->user_pol = *user_pol;
+ q_u->sec_info = sec_info;
+}
+
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+BOOL samr_io_q_query_sec_obj(const char *desc, SAMR_Q_QUERY_SEC_OBJ * q_u,
+ prs_struct *ps, int depth)
+{
+ if (q_u == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "samr_io_q_query_sec_obj");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!smb_io_pol_hnd("user_pol", &q_u->user_pol, ps, depth))
+ return False;
+
+ if(!prs_uint32("sec_info", ps, depth, &q_u->sec_info))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+void init_samr_q_query_dom_info(SAMR_Q_QUERY_DOMAIN_INFO * q_u,
+ POLICY_HND *domain_pol, uint16 switch_value)
+{
+ DEBUG(5, ("samr_init_samr_q_query_dom_info\n"));
+
+ q_u->domain_pol = *domain_pol;
+ q_u->switch_value = switch_value;
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+BOOL samr_io_q_query_dom_info(const char *desc, SAMR_Q_QUERY_DOMAIN_INFO * q_u,
+ prs_struct *ps, int depth)
+{
+ if (q_u == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "samr_io_q_query_dom_info");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!smb_io_pol_hnd("domain_pol", &q_u->domain_pol, ps, depth))
+ return False;
+
+ if(!prs_uint16("switch_value", ps, depth, &q_u->switch_value))
+ return False;
+
+ return True;
+}
+
+
+/*******************************************************************
+inits a structure.
+********************************************************************/
+
+void init_unk_info3(SAM_UNK_INFO_3 *u_3, NTTIME nt_logout)
+{
+ u_3->logout.low = nt_logout.low;
+ u_3->logout.high = nt_logout.high;
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+static BOOL sam_io_unk_info3(const char *desc, SAM_UNK_INFO_3 * u_3,
+ prs_struct *ps, int depth)
+{
+ if (u_3 == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "sam_io_unk_info3");
+ depth++;
+
+ if(!smb_io_time("logout", &u_3->logout, ps, depth))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+inits a structure.
+********************************************************************/
+
+void init_unk_info6(SAM_UNK_INFO_6 * u_6)
+{
+ u_6->unknown_0 = 0x00000000;
+ u_6->ptr_0 = 1;
+ memset(u_6->padding, 0, sizeof(u_6->padding)); /* 12 bytes zeros */
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+static BOOL sam_io_unk_info6(const char *desc, SAM_UNK_INFO_6 * u_6,
+ prs_struct *ps, int depth)
+{
+ if (u_6 == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "sam_io_unk_info6");
+ depth++;
+
+ if(!prs_uint32("unknown_0", ps, depth, &u_6->unknown_0)) /* 0x0000 0000 */
+ return False;
+ if(!prs_uint32("ptr_0", ps, depth, &u_6->ptr_0)) /* pointer to unknown structure */
+ return False;
+ if(!prs_uint8s(False, "padding", ps, depth, u_6->padding, sizeof(u_6->padding))) /* 12 bytes zeros */
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+inits a structure.
+********************************************************************/
+
+void init_unk_info7(SAM_UNK_INFO_7 * u_7)
+{
+ u_7->unknown_0 = 0x0003;
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+static BOOL sam_io_unk_info7(const char *desc, SAM_UNK_INFO_7 * u_7,
+ prs_struct *ps, int depth)
+{
+ if (u_7 == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "sam_io_unk_info7");
+ depth++;
+
+ if(!prs_uint16("unknown_0", ps, depth, &u_7->unknown_0)) /* 0x0003 */
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+inits a structure.
+********************************************************************/
+
+void init_unk_info12(SAM_UNK_INFO_12 * u_12, NTTIME nt_lock_duration, NTTIME nt_reset_time, uint16 lockout)
+{
+ u_12->duration.low = nt_lock_duration.low;
+ u_12->duration.high = nt_lock_duration.high;
+ u_12->reset_count.low = nt_reset_time.low;
+ u_12->reset_count.high = nt_reset_time.high;
+
+ u_12->bad_attempt_lockout = lockout;
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+static BOOL sam_io_unk_info12(const char *desc, SAM_UNK_INFO_12 * u_12,
+ prs_struct *ps, int depth)
+{
+ if (u_12 == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "sam_io_unk_info12");
+ depth++;
+
+ if(!smb_io_time("duration", &u_12->duration, ps, depth))
+ return False;
+ if(!smb_io_time("reset_count", &u_12->reset_count, ps, depth))
+ return False;
+ if(!prs_uint16("bad_attempt_lockout", ps, depth, &u_12->bad_attempt_lockout))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+inits a structure.
+********************************************************************/
+
+void init_unk_info5(SAM_UNK_INFO_5 * u_5,const char *server)
+{
+ init_unistr2(&u_5->uni_server, server, UNI_FLAGS_NONE);
+ init_uni_hdr(&u_5->hdr_server, &u_5->uni_server);
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+static BOOL sam_io_unk_info5(const char *desc, SAM_UNK_INFO_5 * u_5,
+ prs_struct *ps, int depth)
+{
+ if (u_5 == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "sam_io_unk_info5");
+ depth++;
+
+ if(!smb_io_unihdr("hdr_server", &u_5->hdr_server, ps, depth))
+ return False;
+
+ if(!smb_io_unistr2("uni_server", &u_5->uni_server, u_5->hdr_server.buffer, ps, depth))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+inits a structure.
+********************************************************************/
+
+void init_unk_info2(SAM_UNK_INFO_2 * u_2,
+ const char *domain, const char *server,
+ uint32 seq_num, uint32 num_users, uint32 num_groups, uint32 num_alias)
+{
+ u_2->unknown_0 = 0x00000000;
+ u_2->unknown_1 = 0x80000000;
+ u_2->unknown_2 = 0x00000000;
+
+ u_2->ptr_0 = 1;
+
+ u_2->seq_num = seq_num;
+ u_2->unknown_3 = 0x00000000;
+
+ u_2->unknown_4 = 0x00000001;
+ u_2->unknown_5 = 0x00000003;
+ u_2->unknown_6 = 0x00000001;
+ u_2->num_domain_usrs = num_users;
+ u_2->num_domain_grps = num_groups;
+ u_2->num_local_grps = num_alias;
+
+ memset(u_2->padding, 0, sizeof(u_2->padding)); /* 12 bytes zeros */
+
+ init_unistr2(&u_2->uni_domain, domain, UNI_FLAGS_NONE);
+ init_uni_hdr(&u_2->hdr_domain, &u_2->uni_domain);
+ init_unistr2(&u_2->uni_server, server, UNI_FLAGS_NONE);
+ init_uni_hdr(&u_2->hdr_server, &u_2->uni_server);
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+static BOOL sam_io_unk_info2(const char *desc, SAM_UNK_INFO_2 * u_2,
+ prs_struct *ps, int depth)
+{
+ if (u_2 == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "sam_io_unk_info2");
+ depth++;
+
+ if(!prs_uint32("unknown_0", ps, depth, &u_2->unknown_0)) /* 0x0000 0000 */
+ return False;
+ if(!prs_uint32("unknown_1", ps, depth, &u_2->unknown_1)) /* 0x8000 0000 */
+ return False;
+ if(!prs_uint32("unknown_2", ps, depth, &u_2->unknown_2)) /* 0x0000 0000 */
+ return False;
+
+ if(!prs_uint32("ptr_0", ps, depth, &u_2->ptr_0))
+ return False;
+ if(!smb_io_unihdr("hdr_domain", &u_2->hdr_domain, ps, depth))
+ return False;
+ if(!smb_io_unihdr("hdr_server", &u_2->hdr_server, ps, depth))
+ return False;
+
+ /* put all the data in here, at the moment, including what the above
+ pointer is referring to
+ */
+
+ if(!prs_uint32("seq_num ", ps, depth, &u_2->seq_num)) /* 0x0000 0099 or 0x1000 0000 */
+ return False;
+ if(!prs_uint32("unknown_3 ", ps, depth, &u_2->unknown_3)) /* 0x0000 0000 */
+ return False;
+
+ if(!prs_uint32("unknown_4 ", ps, depth, &u_2->unknown_4)) /* 0x0000 0001 */
+ return False;
+ if(!prs_uint32("unknown_5 ", ps, depth, &u_2->unknown_5)) /* 0x0000 0003 */
+ return False;
+ if(!prs_uint32("unknown_6 ", ps, depth, &u_2->unknown_6)) /* 0x0000 0001 */
+ return False;
+ if(!prs_uint32("num_domain_usrs ", ps, depth, &u_2->num_domain_usrs))
+ return False;
+ if(!prs_uint32("num_domain_grps", ps, depth, &u_2->num_domain_grps))
+ return False;
+ if(!prs_uint32("num_local_grps", ps, depth, &u_2->num_local_grps))
+ return False;
+
+ if (u_2->ptr_0) {
+ /* this was originally marked as 'padding'. It isn't
+ padding, it is some sort of optional 12 byte
+ structure. When it is present it contains zeros
+ !? */
+ if(!prs_uint8s(False, "unknown", ps, depth, u_2->padding,sizeof(u_2->padding)))
+ return False;
+ }
+
+ if(!smb_io_unistr2("uni_domain", &u_2->uni_domain, u_2->hdr_domain.buffer, ps, depth))
+ return False;
+ if(!smb_io_unistr2("uni_server", &u_2->uni_server, u_2->hdr_server.buffer, ps, depth))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+inits a structure.
+********************************************************************/
+
+void init_unk_info1(SAM_UNK_INFO_1 *u_1, uint16 min_pass_len, uint16 pass_hist,
+ uint32 flag, NTTIME nt_expire, NTTIME nt_min_age)
+{
+ u_1->min_length_password = min_pass_len;
+ u_1->password_history = pass_hist;
+ u_1->flag = flag;
+
+ /* password never expire */
+ u_1->expire.high = nt_expire.high;
+ u_1->expire.low = nt_expire.low;
+
+ /* can change the password now */
+ u_1->min_passwordage.high = nt_min_age.high;
+ u_1->min_passwordage.low = nt_min_age.low;
+
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+static BOOL sam_io_unk_info1(const char *desc, SAM_UNK_INFO_1 * u_1,
+ prs_struct *ps, int depth)
+{
+ if (u_1 == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "sam_io_unk_info1");
+ depth++;
+
+ if(!prs_uint16("min_length_password", ps, depth, &u_1->min_length_password))
+ return False;
+ if(!prs_uint16("password_history", ps, depth, &u_1->password_history))
+ return False;
+ if(!prs_uint32("flag", ps, depth, &u_1->flag))
+ return False;
+ if(!smb_io_time("expire", &u_1->expire, ps, depth))
+ return False;
+ if(!smb_io_time("min_passwordage", &u_1->min_passwordage, ps, depth))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+inits a SAMR_R_QUERY_DOMAIN_INFO structure.
+********************************************************************/
+
+void init_samr_r_query_dom_info(SAMR_R_QUERY_DOMAIN_INFO * r_u,
+ uint16 switch_value, SAM_UNK_CTR * ctr,
+ NTSTATUS status)
+{
+ DEBUG(5, ("init_samr_r_query_dom_info\n"));
+
+ r_u->ptr_0 = 0;
+ r_u->switch_value = 0;
+ r_u->status = status; /* return status */
+
+ if (NT_STATUS_IS_OK(status)) {
+ r_u->switch_value = switch_value;
+ r_u->ptr_0 = 1;
+ r_u->ctr = ctr;
+ }
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+BOOL samr_io_r_query_dom_info(const char *desc, SAMR_R_QUERY_DOMAIN_INFO * r_u,
+ prs_struct *ps, int depth)
+{
+ if (r_u == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "samr_io_r_query_dom_info");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("ptr_0 ", ps, depth, &r_u->ptr_0))
+ return False;
+
+ if (r_u->ptr_0 != 0 && r_u->ctr != NULL) {
+ if(!prs_uint16("switch_value", ps, depth, &r_u->switch_value))
+ return False;
+ if(!prs_align(ps))
+ return False;
+
+ switch (r_u->switch_value) {
+ case 0x0c:
+ if(!sam_io_unk_info12("unk_inf12", &r_u->ctr->info.inf12, ps, depth))
+ return False;
+ break;
+ case 0x07:
+ if(!sam_io_unk_info7("unk_inf7",&r_u->ctr->info.inf7, ps,depth))
+ return False;
+ break;
+ case 0x06:
+ if(!sam_io_unk_info6("unk_inf6",&r_u->ctr->info.inf6, ps,depth))
+ return False;
+ break;
+ case 0x05:
+ if(!sam_io_unk_info5("unk_inf5",&r_u->ctr->info.inf5, ps,depth))
+ return False;
+ break;
+ case 0x03:
+ if(!sam_io_unk_info3("unk_inf3",&r_u->ctr->info.inf3, ps,depth))
+ return False;
+ break;
+ case 0x02:
+ if(!sam_io_unk_info2("unk_inf2",&r_u->ctr->info.inf2, ps,depth))
+ return False;
+ break;
+ case 0x01:
+ if(!sam_io_unk_info1("unk_inf1",&r_u->ctr->info.inf1, ps,depth))
+ return False;
+ break;
+ default:
+ DEBUG(0, ("samr_io_r_query_dom_info: unknown switch level 0x%x\n",
+ r_u->switch_value));
+ r_u->status = NT_STATUS_INVALID_INFO_CLASS;
+ return False;
+ }
+ }
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_ntstatus("status", ps, depth, &r_u->status))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+reads or writes a SAMR_R_SET_SEC_OBJ structure.
+********************************************************************/
+
+BOOL samr_io_r_set_sec_obj(const char *desc, SAMR_R_SET_SEC_OBJ * r_u,
+ prs_struct *ps, int depth)
+{
+ if (r_u == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "samr_io_r_set_sec_obj");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_ntstatus("status", ps, depth, &r_u->status))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+reads or writes a SAMR_R_QUERY_SEC_OBJ structure.
+********************************************************************/
+
+BOOL samr_io_r_query_sec_obj(const char *desc, SAMR_R_QUERY_SEC_OBJ * r_u,
+ prs_struct *ps, int depth)
+{
+ if (r_u == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "samr_io_r_query_sec_obj");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("ptr", ps, depth, &r_u->ptr))
+ return False;
+ if (r_u->ptr != 0) {
+ if(!sec_io_desc_buf("sec", &r_u->buf, ps, depth))
+ return False;
+ }
+
+ if(!prs_ntstatus("status", ps, depth, &r_u->status))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+reads or writes a SAM_STR1 structure.
+********************************************************************/
+
+static BOOL sam_io_sam_str1(const char *desc, SAM_STR1 * sam, uint32 acct_buf,
+ uint32 name_buf, uint32 desc_buf,
+ prs_struct *ps, int depth)
+{
+ if (sam == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "sam_io_sam_str1");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+ if (!smb_io_unistr2("name", &sam->uni_acct_name, acct_buf, ps, depth))
+ return False;
+
+ if (!smb_io_unistr2("desc", &sam->uni_acct_desc, desc_buf, ps, depth))
+ return False;
+
+ if (!smb_io_unistr2("full", &sam->uni_full_name, name_buf, ps, depth))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+inits a SAM_ENTRY1 structure.
+********************************************************************/
+
+static void init_sam_entry1(SAM_ENTRY1 *sam, uint32 user_idx,
+ UNISTR2 *sam_name, UNISTR2 *sam_full,
+ UNISTR2 *sam_desc, uint32 rid_user,
+ uint16 acb_info)
+{
+ DEBUG(5, ("init_sam_entry1\n"));
+
+ ZERO_STRUCTP(sam);
+
+ sam->user_idx = user_idx;
+ sam->rid_user = rid_user;
+ sam->acb_info = acb_info;
+
+ init_uni_hdr(&sam->hdr_acct_name, sam_name);
+ init_uni_hdr(&sam->hdr_user_name, sam_full);
+ init_uni_hdr(&sam->hdr_user_desc, sam_desc);
+}
+
+/*******************************************************************
+reads or writes a SAM_ENTRY1 structure.
+********************************************************************/
+
+static BOOL sam_io_sam_entry1(const char *desc, SAM_ENTRY1 * sam,
+ prs_struct *ps, int depth)
+{
+ if (sam == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "sam_io_sam_entry1");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("user_idx ", ps, depth, &sam->user_idx))
+ return False;
+
+ if(!prs_uint32("rid_user ", ps, depth, &sam->rid_user))
+ return False;
+ if(!prs_uint16("acb_info ", ps, depth, &sam->acb_info))
+ return False;
+
+ if(!prs_align(ps))
+ return False;
+
+ if (!smb_io_unihdr("hdr_acct_name", &sam->hdr_acct_name, ps, depth))
+ return False;
+ if (!smb_io_unihdr("hdr_user_desc", &sam->hdr_user_desc, ps, depth))
+ return False;
+ if (!smb_io_unihdr("hdr_user_name", &sam->hdr_user_name, ps, depth))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+reads or writes a SAM_STR2 structure.
+********************************************************************/
+
+static BOOL sam_io_sam_str2(const char *desc, SAM_STR2 * sam, uint32 acct_buf,
+ uint32 desc_buf, prs_struct *ps, int depth)
+{
+ if (sam == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "sam_io_sam_str2");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!smb_io_unistr2("uni_srv_name", &sam->uni_srv_name, acct_buf, ps, depth)) /* account name unicode string */
+ return False;
+ if(!smb_io_unistr2("uni_srv_desc", &sam->uni_srv_desc, desc_buf, ps, depth)) /* account desc unicode string */
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+inits a SAM_ENTRY2 structure.
+********************************************************************/
+static void init_sam_entry2(SAM_ENTRY2 * sam, uint32 user_idx,
+ UNISTR2 *sam_name, UNISTR2 *sam_desc,
+ uint32 rid_user, uint16 acb_info)
+{
+ DEBUG(5, ("init_sam_entry2\n"));
+
+ sam->user_idx = user_idx;
+ sam->rid_user = rid_user;
+ sam->acb_info = acb_info;
+
+ init_uni_hdr(&sam->hdr_srv_name, sam_name);
+ init_uni_hdr(&sam->hdr_srv_desc, sam_desc);
+}
+
+/*******************************************************************
+reads or writes a SAM_ENTRY2 structure.
+********************************************************************/
+
+static BOOL sam_io_sam_entry2(const char *desc, SAM_ENTRY2 * sam,
+ prs_struct *ps, int depth)
+{
+ if (sam == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "sam_io_sam_entry2");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("user_idx ", ps, depth, &sam->user_idx))
+ return False;
+
+ if(!prs_uint32("rid_user ", ps, depth, &sam->rid_user))
+ return False;
+ if(!prs_uint16("acb_info ", ps, depth, &sam->acb_info))
+ return False;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!smb_io_unihdr("unihdr", &sam->hdr_srv_name, ps, depth)) /* account name unicode string header */
+ return False;
+ if(!smb_io_unihdr("unihdr", &sam->hdr_srv_desc, ps, depth)) /* account name unicode string header */
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+reads or writes a SAM_STR3 structure.
+********************************************************************/
+
+static BOOL sam_io_sam_str3(const char *desc, SAM_STR3 * sam, uint32 acct_buf,
+ uint32 desc_buf, prs_struct *ps, int depth)
+{
+ if (sam == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "sam_io_sam_str3");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!smb_io_unistr2("uni_grp_name", &sam->uni_grp_name, acct_buf, ps, depth)) /* account name unicode string */
+ return False;
+ if(!smb_io_unistr2("uni_grp_desc", &sam->uni_grp_desc, desc_buf, ps, depth)) /* account desc unicode string */
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+inits a SAM_ENTRY3 structure.
+********************************************************************/
+
+static void init_sam_entry3(SAM_ENTRY3 * sam, uint32 grp_idx,
+ UNISTR2 *grp_name, UNISTR2 *grp_desc,
+ uint32 rid_grp)
+{
+ DEBUG(5, ("init_sam_entry3\n"));
+
+ sam->grp_idx = grp_idx;
+ sam->rid_grp = rid_grp;
+ sam->attr = 0x07; /* group rid attributes - gets ignored by nt 4.0 */
+
+ init_uni_hdr(&sam->hdr_grp_name, grp_name);
+ init_uni_hdr(&sam->hdr_grp_desc, grp_desc);
+}
+
+/*******************************************************************
+reads or writes a SAM_ENTRY3 structure.
+********************************************************************/
+
+static BOOL sam_io_sam_entry3(const char *desc, SAM_ENTRY3 * sam,
+ prs_struct *ps, int depth)
+{
+ if (sam == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "sam_io_sam_entry3");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("grp_idx", ps, depth, &sam->grp_idx))
+ return False;
+
+ if(!prs_uint32("rid_grp", ps, depth, &sam->rid_grp))
+ return False;
+ if(!prs_uint32("attr ", ps, depth, &sam->attr))
+ return False;
+
+ if(!smb_io_unihdr("unihdr", &sam->hdr_grp_name, ps, depth)) /* account name unicode string header */
+ return False;
+ if(!smb_io_unihdr("unihdr", &sam->hdr_grp_desc, ps, depth)) /* account name unicode string header */
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+inits a SAM_ENTRY4 structure.
+********************************************************************/
+
+static void init_sam_entry4(SAM_ENTRY4 * sam, uint32 user_idx,
+ uint32 len_acct_name)
+{
+ DEBUG(5, ("init_sam_entry4\n"));
+
+ sam->user_idx = user_idx;
+ init_str_hdr(&sam->hdr_acct_name, len_acct_name+1, len_acct_name, len_acct_name != 0);
+}
+
+/*******************************************************************
+reads or writes a SAM_ENTRY4 structure.
+********************************************************************/
+
+static BOOL sam_io_sam_entry4(const char *desc, SAM_ENTRY4 * sam,
+ prs_struct *ps, int depth)
+{
+ if (sam == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "sam_io_sam_entry4");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("user_idx", ps, depth, &sam->user_idx))
+ return False;
+ if(!smb_io_strhdr("strhdr", &sam->hdr_acct_name, ps, depth))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+inits a SAM_ENTRY5 structure.
+********************************************************************/
+
+static void init_sam_entry5(SAM_ENTRY5 * sam, uint32 grp_idx,
+ uint32 len_grp_name)
+{
+ DEBUG(5, ("init_sam_entry5\n"));
+
+ sam->grp_idx = grp_idx;
+ init_str_hdr(&sam->hdr_grp_name, len_grp_name, len_grp_name,
+ len_grp_name != 0);
+}
+
+/*******************************************************************
+reads or writes a SAM_ENTRY5 structure.
+********************************************************************/
+
+static BOOL sam_io_sam_entry5(const char *desc, SAM_ENTRY5 * sam,
+ prs_struct *ps, int depth)
+{
+ if (sam == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "sam_io_sam_entry5");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("grp_idx", ps, depth, &sam->grp_idx))
+ return False;
+ if(!smb_io_strhdr("strhdr", &sam->hdr_grp_name, ps, depth))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+inits a SAM_ENTRY structure.
+********************************************************************/
+
+void init_sam_entry(SAM_ENTRY *sam, UNISTR2 *uni2, uint32 rid)
+{
+ DEBUG(10, ("init_sam_entry: %d\n", rid));
+
+ sam->rid = rid;
+ init_uni_hdr(&sam->hdr_name, uni2);
+}
+
+/*******************************************************************
+reads or writes a SAM_ENTRY structure.
+********************************************************************/
+
+static BOOL sam_io_sam_entry(const char *desc, SAM_ENTRY * sam,
+ prs_struct *ps, int depth)
+{
+ if (sam == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "sam_io_sam_entry");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+ if(!prs_uint32("rid", ps, depth, &sam->rid))
+ return False;
+ if(!smb_io_unihdr("unihdr", &sam->hdr_name, ps, depth)) /* account name unicode string header */
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+inits a SAMR_Q_ENUM_DOM_USERS structure.
+********************************************************************/
+
+void init_samr_q_enum_dom_users(SAMR_Q_ENUM_DOM_USERS * q_e, POLICY_HND *pol,
+ uint32 start_idx,
+ uint16 acb_mask, uint16 unk_1, uint32 size)
+{
+ DEBUG(5, ("init_samr_q_enum_dom_users\n"));
+
+ q_e->pol = *pol;
+
+ q_e->start_idx = start_idx; /* zero indicates lots */
+ q_e->acb_mask = acb_mask;
+ q_e->unknown_1 = unk_1;
+ q_e->max_size = size;
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+BOOL samr_io_q_enum_dom_users(const char *desc, SAMR_Q_ENUM_DOM_USERS * q_e,
+ prs_struct *ps, int depth)
+{
+ if (q_e == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "samr_io_q_enum_dom_users");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!smb_io_pol_hnd("domain_pol", &q_e->pol, ps, depth))
+ return False;
+
+ if(!prs_uint32("start_idx", ps, depth, &q_e->start_idx))
+ return False;
+ if(!prs_uint16("acb_mask ", ps, depth, &q_e->acb_mask))
+ return False;
+ if(!prs_uint16("unknown_1", ps, depth, &q_e->unknown_1))
+ return False;
+
+ if(!prs_uint32("max_size ", ps, depth, &q_e->max_size))
+ return False;
+
+ return True;
+}
+
+
+/*******************************************************************
+inits a SAMR_R_ENUM_DOM_USERS structure.
+********************************************************************/
+
+void init_samr_r_enum_dom_users(SAMR_R_ENUM_DOM_USERS * r_u,
+ uint32 next_idx, uint32 num_sam_entries)
+{
+ DEBUG(5, ("init_samr_r_enum_dom_users\n"));
+
+ r_u->next_idx = next_idx;
+
+ if (num_sam_entries != 0) {
+ r_u->ptr_entries1 = 1;
+ r_u->ptr_entries2 = 1;
+ r_u->num_entries2 = num_sam_entries;
+ r_u->num_entries3 = num_sam_entries;
+
+ r_u->num_entries4 = num_sam_entries;
+ } else {
+ r_u->ptr_entries1 = 0;
+ r_u->num_entries2 = num_sam_entries;
+ r_u->ptr_entries2 = 1;
+ }
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+BOOL samr_io_r_enum_dom_users(const char *desc, SAMR_R_ENUM_DOM_USERS * r_u,
+ prs_struct *ps, int depth)
+{
+ uint32 i;
+
+ if (r_u == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "samr_io_r_enum_dom_users");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("next_idx ", ps, depth, &r_u->next_idx))
+ return False;
+ if(!prs_uint32("ptr_entries1", ps, depth, &r_u->ptr_entries1))
+ return False;
+
+ if (r_u->ptr_entries1 != 0) {
+ if(!prs_uint32("num_entries2", ps, depth, &r_u->num_entries2))
+ return False;
+ if(!prs_uint32("ptr_entries2", ps, depth, &r_u->ptr_entries2))
+ return False;
+ if(!prs_uint32("num_entries3", ps, depth, &r_u->num_entries3))
+ return False;
+
+ if (UNMARSHALLING(ps) && (r_u->num_entries2 != 0)) {
+ r_u->sam = (SAM_ENTRY *)prs_alloc_mem(ps,sizeof(SAM_ENTRY)*r_u->num_entries2);
+ r_u->uni_acct_name = (UNISTR2 *)prs_alloc_mem(ps,sizeof(UNISTR2)*r_u->num_entries2);
+ }
+
+ if ((r_u->sam == NULL || r_u->uni_acct_name == NULL) && r_u->num_entries2 != 0) {
+ DEBUG(0,("NULL pointers in SAMR_R_ENUM_DOM_USERS\n"));
+ r_u->num_entries4 = 0;
+ r_u->status = NT_STATUS_MEMORY_NOT_ALLOCATED;
+ return False;
+ }
+
+ for (i = 0; i < r_u->num_entries2; i++) {
+ if(!sam_io_sam_entry("", &r_u->sam[i], ps, depth))
+ return False;
+ }
+
+ for (i = 0; i < r_u->num_entries2; i++) {
+ if(!smb_io_unistr2("", &r_u->uni_acct_name[i],r_u->sam[i].hdr_name.buffer, ps,depth))
+ return False;
+ }
+
+ }
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("num_entries4", ps, depth, &r_u->num_entries4))
+ return False;
+ if(!prs_ntstatus("status", ps, depth, &r_u->status))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+inits a SAMR_Q_QUERY_DISPINFO structure.
+********************************************************************/
+
+void init_samr_q_query_dispinfo(SAMR_Q_QUERY_DISPINFO * q_e, POLICY_HND *pol,
+ uint16 switch_level, uint32 start_idx,
+ uint32 max_entries, uint32 max_size)
+{
+ DEBUG(5, ("init_samr_q_query_dispinfo\n"));
+
+ q_e->domain_pol = *pol;
+
+ q_e->switch_level = switch_level;
+
+ q_e->start_idx = start_idx;
+ q_e->max_entries = max_entries;
+ q_e->max_size = max_size;
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+BOOL samr_io_q_query_dispinfo(const char *desc, SAMR_Q_QUERY_DISPINFO * q_e,
+ prs_struct *ps, int depth)
+{
+ if (q_e == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "samr_io_q_query_dispinfo");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!smb_io_pol_hnd("domain_pol", &q_e->domain_pol, ps, depth))
+ return False;
+
+ if(!prs_uint16("switch_level", ps, depth, &q_e->switch_level))
+ return False;
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("start_idx ", ps, depth, &q_e->start_idx))
+ return False;
+ if(!prs_uint32("max_entries ", ps, depth, &q_e->max_entries))
+ return False;
+ if(!prs_uint32("max_size ", ps, depth, &q_e->max_size))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+inits a SAM_DISPINFO_1 structure.
+********************************************************************/
+
+NTSTATUS init_sam_dispinfo_1(TALLOC_CTX *ctx, SAM_DISPINFO_1 *sam, uint32 num_entries,
+ uint32 start_idx, SAM_ACCOUNT *disp_user_info,
+ DOM_SID *domain_sid)
+{
+ uint32 i;
+
+ SAM_ACCOUNT *pwd = NULL;
+ ZERO_STRUCTP(sam);
+
+ DEBUG(10, ("init_sam_dispinfo_1: num_entries: %d\n", num_entries));
+
+ if (num_entries==0)
+ return NT_STATUS_OK;
+
+ sam->sam=(SAM_ENTRY1 *)talloc(ctx, num_entries*sizeof(SAM_ENTRY1));
+ if (!sam->sam)
+ return NT_STATUS_NO_MEMORY;
+
+ sam->str=(SAM_STR1 *)talloc(ctx, num_entries*sizeof(SAM_STR1));
+ if (!sam->str)
+ return NT_STATUS_NO_MEMORY;
+
+ ZERO_STRUCTP(sam->sam);
+ ZERO_STRUCTP(sam->str);
+
+ for (i = 0; i < num_entries ; i++) {
+ const char *username;
+ const char *fullname;
+ const char *acct_desc;
+ uint32 user_rid;
+ const DOM_SID *user_sid;
+ fstring user_sid_string, domain_sid_string;
+
+ DEBUG(11, ("init_sam_dispinfo_1: entry: %d\n",i));
+
+ pwd=&disp_user_info[i+start_idx];
+
+ username = pdb_get_username(pwd);
+ fullname = pdb_get_fullname(pwd);
+ acct_desc = pdb_get_acct_desc(pwd);
+
+ if (!username)
+ username = "";
+
+ if (!fullname)
+ fullname = "";
+
+ if (!acct_desc)
+ acct_desc = "";
+
+ user_sid = pdb_get_user_sid(pwd);
+
+ if (!sid_peek_check_rid(domain_sid, user_sid, &user_rid)) {
+ DEBUG(0, ("init_sam_dispinfo_1: User %s has SID %s, which conflicts with "
+ "the domain sid %s. Failing operation.\n",
+ username,
+ sid_to_string(user_sid_string, user_sid),
+ sid_to_string(domain_sid_string, domain_sid)));
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ init_unistr2(&sam->str[i].uni_acct_name, pdb_get_username(pwd), UNI_FLAGS_NONE);
+ init_unistr2(&sam->str[i].uni_full_name, pdb_get_fullname(pwd), UNI_FLAGS_NONE);
+ init_unistr2(&sam->str[i].uni_acct_desc, pdb_get_acct_desc(pwd), UNI_FLAGS_NONE);
+
+ init_sam_entry1(&sam->sam[i], start_idx + i + 1,
+ &sam->str[i].uni_acct_name, &sam->str[i].uni_full_name, &sam->str[i].uni_acct_desc,
+ user_rid, pdb_get_acct_ctrl(pwd));
+
+ }
+
+ return NT_STATUS_OK;
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+static BOOL sam_io_sam_dispinfo_1(const char *desc, SAM_DISPINFO_1 * sam,
+ uint32 num_entries,
+ prs_struct *ps, int depth)
+{
+ uint32 i;
+
+ prs_debug(ps, depth, desc, "sam_io_sam_dispinfo_1");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if (UNMARSHALLING(ps) && num_entries > 0) {
+
+ if ((sam->sam = (SAM_ENTRY1 *)
+ prs_alloc_mem(ps, sizeof(SAM_ENTRY1) *
+ num_entries)) == NULL) {
+ DEBUG(0, ("out of memory allocating SAM_ENTRY1\n"));
+ return False;
+ }
+
+ if ((sam->str = (SAM_STR1 *)
+ prs_alloc_mem(ps, sizeof(SAM_STR1) *
+ num_entries)) == NULL) {
+ DEBUG(0, ("out of memory allocating SAM_STR1\n"));
+ return False;
+ }
+ }
+
+ for (i = 0; i < num_entries; i++) {
+ if(!sam_io_sam_entry1("", &sam->sam[i], ps, depth))
+ return False;
+ }
+
+ for (i = 0; i < num_entries; i++) {
+ if(!sam_io_sam_str1("", &sam->str[i],
+ sam->sam[i].hdr_acct_name.buffer,
+ sam->sam[i].hdr_user_name.buffer,
+ sam->sam[i].hdr_user_desc.buffer, ps, depth))
+ return False;
+ }
+
+ return True;
+}
+
+/*******************************************************************
+inits a SAM_DISPINFO_2 structure.
+********************************************************************/
+
+NTSTATUS init_sam_dispinfo_2(TALLOC_CTX *ctx, SAM_DISPINFO_2 *sam, uint32 num_entries,
+ uint32 start_idx, SAM_ACCOUNT *disp_user_info,
+ DOM_SID *domain_sid )
+{
+ uint32 i;
+
+ SAM_ACCOUNT *pwd = NULL;
+ ZERO_STRUCTP(sam);
+
+ DEBUG(10, ("init_sam_dispinfo_2: num_entries: %d\n", num_entries));
+
+ if (num_entries==0)
+ return NT_STATUS_OK;
+
+ if (!(sam->sam=(SAM_ENTRY2 *)talloc(ctx, num_entries*sizeof(SAM_ENTRY2))))
+ return NT_STATUS_NO_MEMORY;
+
+ if (!(sam->str=(SAM_STR2 *)talloc(ctx, num_entries*sizeof(SAM_STR2))))
+ return NT_STATUS_NO_MEMORY;
+
+ ZERO_STRUCTP(sam->sam);
+ ZERO_STRUCTP(sam->str);
+
+ for (i = 0; i < num_entries; i++) {
+ uint32 user_rid;
+ const DOM_SID *user_sid;
+ const char *username;
+ const char *acct_desc;
+ fstring user_sid_string, domain_sid_string;
+
+ DEBUG(11, ("init_sam_dispinfo_2: entry: %d\n",i));
+ pwd=&disp_user_info[i+start_idx];
+
+ username = pdb_get_username(pwd);
+ acct_desc = pdb_get_acct_desc(pwd);
+ user_sid = pdb_get_user_sid(pwd);
+
+ if (!sid_peek_check_rid(domain_sid, user_sid, &user_rid)) {
+ DEBUG(0, ("init_sam_dispinfo_2: User %s has SID %s, which conflicts with "
+ "the domain sid %s. Failing operation.\n",
+ username,
+ sid_to_string(user_sid_string, user_sid),
+ sid_to_string(domain_sid_string, domain_sid)));
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ init_unistr2(&sam->str[i].uni_srv_name, username, UNI_FLAGS_NONE);
+ init_unistr2(&sam->str[i].uni_srv_desc, pdb_get_acct_desc(pwd), UNI_FLAGS_NONE);
+
+ init_sam_entry2(&sam->sam[i], start_idx + i + 1,
+ &sam->str[i].uni_srv_name, &sam->str[i].uni_srv_desc,
+ user_rid, pdb_get_acct_ctrl(pwd));
+ }
+
+ return NT_STATUS_OK;
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+static BOOL sam_io_sam_dispinfo_2(const char *desc, SAM_DISPINFO_2 * sam,
+ uint32 num_entries,
+ prs_struct *ps, int depth)
+{
+ uint32 i;
+
+ if (sam == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "sam_io_sam_dispinfo_2");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if (UNMARSHALLING(ps) && num_entries > 0) {
+
+ if ((sam->sam = (SAM_ENTRY2 *)
+ prs_alloc_mem(ps, sizeof(SAM_ENTRY2) *
+ num_entries)) == NULL) {
+ DEBUG(0, ("out of memory allocating SAM_ENTRY2\n"));
+ return False;
+ }
+
+ if ((sam->str = (SAM_STR2 *)
+ prs_alloc_mem(ps, sizeof(SAM_STR2) *
+ num_entries)) == NULL) {
+ DEBUG(0, ("out of memory allocating SAM_STR2\n"));
+ return False;
+ }
+ }
+
+ for (i = 0; i < num_entries; i++) {
+ if(!sam_io_sam_entry2("", &sam->sam[i], ps, depth))
+ return False;
+ }
+
+ for (i = 0; i < num_entries; i++) {
+ if(!sam_io_sam_str2("", &sam->str[i],
+ sam->sam[i].hdr_srv_name.buffer,
+ sam->sam[i].hdr_srv_desc.buffer, ps, depth))
+ return False;
+ }
+
+ return True;
+}
+
+/*******************************************************************
+inits a SAM_DISPINFO_3 structure.
+********************************************************************/
+
+NTSTATUS init_sam_dispinfo_3(TALLOC_CTX *ctx, SAM_DISPINFO_3 *sam, uint32 num_entries,
+ uint32 start_idx, DOMAIN_GRP *disp_group_info)
+{
+ uint32 i;
+
+ ZERO_STRUCTP(sam);
+
+ DEBUG(5, ("init_sam_dispinfo_3: num_entries: %d\n", num_entries));
+
+ if (num_entries==0)
+ return NT_STATUS_OK;
+
+ if (!(sam->sam=(SAM_ENTRY3 *)talloc(ctx, num_entries*sizeof(SAM_ENTRY3))))
+ return NT_STATUS_NO_MEMORY;
+
+ if (!(sam->str=(SAM_STR3 *)talloc(ctx, num_entries*sizeof(SAM_STR3))))
+ return NT_STATUS_NO_MEMORY;
+
+ ZERO_STRUCTP(sam->sam);
+ ZERO_STRUCTP(sam->str);
+
+ for (i = 0; i < num_entries; i++) {
+ DOMAIN_GRP *grp = &disp_group_info[i+start_idx];
+
+ DEBUG(11, ("init_sam_dispinfo_3: entry: %d\n",i));
+
+ init_unistr2(&sam->str[i].uni_grp_name, grp->name, UNI_FLAGS_NONE);
+ init_unistr2(&sam->str[i].uni_grp_desc, grp->comment, UNI_FLAGS_NONE);
+
+ init_sam_entry3(&sam->sam[i], start_idx + i + 1, &sam->str[i].uni_grp_name,
+ &sam->str[i].uni_grp_desc, grp->rid);
+ }
+
+ return NT_STATUS_OK;
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+static BOOL sam_io_sam_dispinfo_3(const char *desc, SAM_DISPINFO_3 * sam,
+ uint32 num_entries,
+ prs_struct *ps, int depth)
+{
+ uint32 i;
+
+ if (sam == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "sam_io_sam_dispinfo_3");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if (UNMARSHALLING(ps) && num_entries > 0) {
+
+ if ((sam->sam = (SAM_ENTRY3 *)
+ prs_alloc_mem(ps, sizeof(SAM_ENTRY3) *
+ num_entries)) == NULL) {
+ DEBUG(0, ("out of memory allocating SAM_ENTRY3\n"));
+ return False;
+ }
+
+ if ((sam->str = (SAM_STR3 *)
+ prs_alloc_mem(ps, sizeof(SAM_STR3) *
+ num_entries)) == NULL) {
+ DEBUG(0, ("out of memory allocating SAM_STR3\n"));
+ return False;
+ }
+ }
+
+ for (i = 0; i < num_entries; i++) {
+ if(!sam_io_sam_entry3("", &sam->sam[i], ps, depth))
+ return False;
+ }
+
+ for (i = 0; i < num_entries; i++) {
+ if(!sam_io_sam_str3("", &sam->str[i],
+ sam->sam[i].hdr_grp_name.buffer,
+ sam->sam[i].hdr_grp_desc.buffer, ps, depth))
+ return False;
+ }
+
+ return True;
+}
+
+/*******************************************************************
+inits a SAM_DISPINFO_4 structure.
+********************************************************************/
+
+NTSTATUS init_sam_dispinfo_4(TALLOC_CTX *ctx, SAM_DISPINFO_4 *sam, uint32 num_entries,
+ uint32 start_idx, SAM_ACCOUNT *disp_user_info)
+{
+ uint32 len_sam_name;
+ uint32 i;
+
+ SAM_ACCOUNT *pwd = NULL;
+ ZERO_STRUCTP(sam);
+
+ DEBUG(5, ("init_sam_dispinfo_4: num_entries: %d\n", num_entries));
+
+ if (num_entries==0)
+ return NT_STATUS_OK;
+
+ if (!(sam->sam=(SAM_ENTRY4 *)talloc(ctx, num_entries*sizeof(SAM_ENTRY4))))
+ return NT_STATUS_NO_MEMORY;
+
+ if (!(sam->str=(SAM_STR4 *)talloc(ctx, num_entries*sizeof(SAM_STR4))))
+ return NT_STATUS_NO_MEMORY;
+
+ ZERO_STRUCTP(sam->sam);
+ ZERO_STRUCTP(sam->str);
+
+ for (i = 0; i < num_entries; i++) {
+ DEBUG(11, ("init_sam_dispinfo_2: entry: %d\n",i));
+ pwd=&disp_user_info[i+start_idx];
+
+ len_sam_name = strlen(pdb_get_username(pwd));
+
+ init_sam_entry4(&sam->sam[i], start_idx + i + 1, len_sam_name);
+
+ init_string2(&sam->str[i].acct_name, pdb_get_username(pwd), len_sam_name+1, len_sam_name);
+ }
+
+ return NT_STATUS_OK;
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+static BOOL sam_io_sam_dispinfo_4(const char *desc, SAM_DISPINFO_4 * sam,
+ uint32 num_entries,
+ prs_struct *ps, int depth)
+{
+ uint32 i;
+
+ if (sam == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "sam_io_sam_dispinfo_4");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if (UNMARSHALLING(ps) && num_entries > 0) {
+
+ if ((sam->sam = (SAM_ENTRY4 *)
+ prs_alloc_mem(ps, sizeof(SAM_ENTRY4) *
+ num_entries)) == NULL) {
+ DEBUG(0, ("out of memory allocating SAM_ENTRY4\n"));
+ return False;
+ }
+
+ if ((sam->str = (SAM_STR4 *)
+ prs_alloc_mem(ps, sizeof(SAM_STR4) *
+ num_entries)) == NULL) {
+ DEBUG(0, ("out of memory allocating SAM_STR4\n"));
+ return False;
+ }
+ }
+
+ for (i = 0; i < num_entries; i++) {
+ if(!sam_io_sam_entry4("", &sam->sam[i], ps, depth))
+ return False;
+ }
+
+ for (i = 0; i < num_entries; i++) {
+ if(!smb_io_string2("acct_name", &sam->str[i].acct_name,
+ sam->sam[i].hdr_acct_name.buffer, ps, depth))
+ return False;
+ }
+
+ return True;
+}
+
+/*******************************************************************
+inits a SAM_DISPINFO_5 structure.
+********************************************************************/
+
+NTSTATUS init_sam_dispinfo_5(TALLOC_CTX *ctx, SAM_DISPINFO_5 *sam, uint32 num_entries,
+ uint32 start_idx, DOMAIN_GRP *disp_group_info)
+{
+ uint32 len_sam_name;
+ uint32 i;
+
+ ZERO_STRUCTP(sam);
+
+ DEBUG(5, ("init_sam_dispinfo_5: num_entries: %d\n", num_entries));
+
+ if (num_entries==0)
+ return NT_STATUS_OK;
+
+ if (!(sam->sam=(SAM_ENTRY5 *)talloc(ctx, num_entries*sizeof(SAM_ENTRY5))))
+ return NT_STATUS_NO_MEMORY;
+
+ if (!(sam->str=(SAM_STR5 *)talloc(ctx, num_entries*sizeof(SAM_STR5))))
+ return NT_STATUS_NO_MEMORY;
+
+ ZERO_STRUCTP(sam->sam);
+ ZERO_STRUCTP(sam->str);
+
+ for (i = 0; i < num_entries; i++) {
+ DOMAIN_GRP *grp = &disp_group_info[i+start_idx];
+
+ DEBUG(11, ("init_sam_dispinfo_5: entry: %d\n",i));
+
+ len_sam_name = strlen(grp->name);
+
+ init_sam_entry5(&sam->sam[i], start_idx + i + 1, len_sam_name);
+ init_string2(&sam->str[i].grp_name, grp->name, len_sam_name+1, len_sam_name);
+ }
+
+ return NT_STATUS_OK;
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+static BOOL sam_io_sam_dispinfo_5(const char *desc, SAM_DISPINFO_5 * sam,
+ uint32 num_entries,
+ prs_struct *ps, int depth)
+{
+ uint32 i;
+
+ if (sam == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "sam_io_sam_dispinfo_5");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if (UNMARSHALLING(ps) && num_entries > 0) {
+
+ if ((sam->sam = (SAM_ENTRY5 *)
+ prs_alloc_mem(ps, sizeof(SAM_ENTRY5) *
+ num_entries)) == NULL) {
+ DEBUG(0, ("out of memory allocating SAM_ENTRY5\n"));
+ return False;
+ }
+
+ if ((sam->str = (SAM_STR5 *)
+ prs_alloc_mem(ps, sizeof(SAM_STR5) *
+ num_entries)) == NULL) {
+ DEBUG(0, ("out of memory allocating SAM_STR5\n"));
+ return False;
+ }
+ }
+
+ for (i = 0; i < num_entries; i++) {
+ if(!sam_io_sam_entry5("", &sam->sam[i], ps, depth))
+ return False;
+ }
+
+ for (i = 0; i < num_entries; i++) {
+ if(!smb_io_string2("grp_name", &sam->str[i].grp_name,
+ sam->sam[i].hdr_grp_name.buffer, ps, depth))
+ return False;
+ }
+
+ return True;
+}
+
+/*******************************************************************
+inits a SAMR_R_QUERY_DISPINFO structure.
+********************************************************************/
+
+void init_samr_r_query_dispinfo(SAMR_R_QUERY_DISPINFO * r_u,
+ uint32 num_entries, uint32 total_size, uint32 data_size,
+ uint16 switch_level, SAM_DISPINFO_CTR * ctr,
+ NTSTATUS status)
+{
+ DEBUG(5, ("init_samr_r_query_dispinfo: level %d\n", switch_level));
+
+ r_u->total_size = total_size;
+
+ r_u->data_size = data_size;
+
+ r_u->switch_level = switch_level;
+ r_u->num_entries = num_entries;
+
+ if (num_entries==0)
+ r_u->ptr_entries = 0;
+ else
+ r_u->ptr_entries = 1;
+
+ r_u->num_entries2 = num_entries;
+ r_u->ctr = ctr;
+
+ r_u->status = status;
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+BOOL samr_io_r_query_dispinfo(const char *desc, SAMR_R_QUERY_DISPINFO * r_u,
+ prs_struct *ps, int depth)
+{
+ if (r_u == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "samr_io_r_query_dispinfo");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("total_size ", ps, depth, &r_u->total_size))
+ return False;
+ if(!prs_uint32("data_size ", ps, depth, &r_u->data_size))
+ return False;
+ if(!prs_uint16("switch_level", ps, depth, &r_u->switch_level))
+ return False;
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("num_entries ", ps, depth, &r_u->num_entries))
+ return False;
+ if(!prs_uint32("ptr_entries ", ps, depth, &r_u->ptr_entries))
+ return False;
+
+ if (r_u->ptr_entries==0) {
+ if(!prs_align(ps))
+ return False;
+ if(!prs_ntstatus("status", ps, depth, &r_u->status))
+ return False;
+
+ return True;
+ }
+
+ if(!prs_uint32("num_entries2", ps, depth, &r_u->num_entries2))
+ return False;
+
+ switch (r_u->switch_level) {
+ case 0x1:
+ if(!sam_io_sam_dispinfo_1("users", r_u->ctr->sam.info1,
+ r_u->num_entries, ps, depth))
+ return False;
+ break;
+ case 0x2:
+ if(!sam_io_sam_dispinfo_2("servers", r_u->ctr->sam.info2,
+ r_u->num_entries, ps, depth))
+ return False;
+ break;
+ case 0x3:
+ if(!sam_io_sam_dispinfo_3("groups", r_u->ctr->sam.info3,
+ r_u->num_entries, ps, depth))
+ return False;
+ break;
+ case 0x4:
+ if(!sam_io_sam_dispinfo_4("user list",
+ r_u->ctr->sam.info4,
+ r_u->num_entries, ps, depth))
+ return False;
+ break;
+ case 0x5:
+ if(!sam_io_sam_dispinfo_5("group list",
+ r_u->ctr->sam.info5,
+ r_u->num_entries, ps, depth))
+ return False;
+ break;
+ default:
+ DEBUG(0,("samr_io_r_query_dispinfo: unknown switch value\n"));
+ break;
+ }
+
+ if(!prs_align(ps))
+ return False;
+ if(!prs_ntstatus("status", ps, depth, &r_u->status))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+inits a SAMR_Q_OPEN_GROUP structure.
+********************************************************************/
+
+void init_samr_q_open_group(SAMR_Q_OPEN_GROUP * q_c,
+ POLICY_HND *hnd,
+ uint32 access_mask, uint32 rid)
+{
+ DEBUG(5, ("init_samr_q_open_group\n"));
+
+ q_c->domain_pol = *hnd;
+ q_c->access_mask = access_mask;
+ q_c->rid_group = rid;
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+BOOL samr_io_q_open_group(const char *desc, SAMR_Q_OPEN_GROUP * q_u,
+ prs_struct *ps, int depth)
+{
+ if (q_u == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "samr_io_q_open_group");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!smb_io_pol_hnd("domain_pol", &q_u->domain_pol, ps, depth))
+ return False;
+
+ if(!prs_uint32("access_mask", ps, depth, &q_u->access_mask))
+ return False;
+ if(!prs_uint32("rid_group", ps, depth, &q_u->rid_group))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+BOOL samr_io_r_open_group(const char *desc, SAMR_R_OPEN_GROUP * r_u,
+ prs_struct *ps, int depth)
+{
+ if (r_u == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "samr_io_r_open_group");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!smb_io_pol_hnd("pol", &r_u->pol, ps, depth))
+ return False;
+
+ if(!prs_ntstatus("status", ps, depth, &r_u->status))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+inits a GROUP_INFO1 structure.
+********************************************************************/
+
+void init_samr_group_info1(GROUP_INFO1 * gr1,
+ char *acct_name, char *acct_desc,
+ uint32 num_members)
+{
+ DEBUG(5, ("init_samr_group_info1\n"));
+
+ gr1->unknown_1 = 0x3;
+ gr1->num_members = num_members;
+
+ init_unistr2(&gr1->uni_acct_name, acct_name, UNI_FLAGS_NONE);
+ init_uni_hdr(&gr1->hdr_acct_name, &gr1->uni_acct_name);
+ init_unistr2(&gr1->uni_acct_desc, acct_desc, UNI_FLAGS_NONE);
+ init_uni_hdr(&gr1->hdr_acct_desc, &gr1->uni_acct_desc);
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+BOOL samr_io_group_info1(const char *desc, GROUP_INFO1 * gr1,
+ prs_struct *ps, int depth)
+{
+ if (gr1 == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "samr_io_group_info1");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!smb_io_unihdr("hdr_acct_name", &gr1->hdr_acct_name, ps, depth))
+ return False;
+
+ if(!prs_uint32("unknown_1", ps, depth, &gr1->unknown_1))
+ return False;
+ if(!prs_uint32("num_members", ps, depth, &gr1->num_members))
+ return False;
+
+ if(!smb_io_unihdr("hdr_acct_desc", &gr1->hdr_acct_desc, ps, depth))
+ return False;
+
+ if(!smb_io_unistr2("uni_acct_name", &gr1->uni_acct_name,
+ gr1->hdr_acct_name.buffer, ps, depth))
+ return False;
+
+ if(!smb_io_unistr2("uni_acct_desc", &gr1->uni_acct_desc,
+ gr1->hdr_acct_desc.buffer, ps, depth))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+inits a GROUP_INFO3 structure.
+********************************************************************/
+
+void init_samr_group_info3(GROUP_INFO3 *gr3)
+{
+ DEBUG(5, ("init_samr_group_info3\n"));
+
+ gr3->unknown_1 = 0x3;
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+BOOL samr_io_group_info3(const char *desc, GROUP_INFO3 *gr3, prs_struct *ps, int depth)
+{
+ if (gr3 == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "samr_io_group_info3");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("unknown_1", ps, depth, &gr3->unknown_1))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+inits a GROUP_INFO4 structure.
+********************************************************************/
+
+void init_samr_group_info4(GROUP_INFO4 * gr4, const char *acct_desc)
+{
+ DEBUG(5, ("init_samr_group_info4\n"));
+
+ gr4->level = 4;
+ init_unistr2(&gr4->uni_acct_desc, acct_desc, UNI_FLAGS_NONE);
+ init_uni_hdr(&gr4->hdr_acct_desc, &gr4->uni_acct_desc);
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+BOOL samr_io_group_info4(const char *desc, GROUP_INFO4 * gr4,
+ prs_struct *ps, int depth)
+{
+ if (gr4 == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "samr_io_group_info4");
+ depth++;
+
+ if(!prs_uint16("hdr_level", ps, depth, &gr4->level))
+ return False;
+ if(!smb_io_unihdr("hdr_acct_desc", &gr4->hdr_acct_desc, ps, depth))
+ return False;
+ if(!smb_io_unistr2("uni_acct_desc", &gr4->uni_acct_desc,
+ gr4->hdr_acct_desc.buffer, ps, depth))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+static BOOL samr_group_info_ctr(const char *desc, GROUP_INFO_CTR **ctr,
+ prs_struct *ps, int depth)
+{
+ if (UNMARSHALLING(ps))
+ *ctr = (GROUP_INFO_CTR *)prs_alloc_mem(ps,sizeof(GROUP_INFO_CTR));
+
+ if (*ctr == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "samr_group_info_ctr");
+ depth++;
+
+ if(!prs_uint16("switch_value1", ps, depth, &(*ctr)->switch_value1))
+ return False;
+
+ switch ((*ctr)->switch_value1) {
+ case 1:
+ if(!samr_io_group_info1("group_info1", &(*ctr)->group.info1, ps, depth))
+ return False;
+ break;
+ case 3:
+ if(!samr_io_group_info3("group_info3", &(*ctr)->group.info3, ps, depth))
+ return False;
+ break;
+ case 4:
+ if(!samr_io_group_info4("group_info4", &(*ctr)->group.info4, ps, depth))
+ return False;
+ break;
+ default:
+ DEBUG(0,("samr_group_info_ctr: unsupported switch level\n"));
+ break;
+ }
+
+ return True;
+}
+
+/*******************************************************************
+inits a SAMR_Q_CREATE_DOM_GROUP structure.
+********************************************************************/
+
+void init_samr_q_create_dom_group(SAMR_Q_CREATE_DOM_GROUP * q_e,
+ POLICY_HND *pol, const char *acct_desc,
+ uint32 access_mask)
+{
+ DEBUG(5, ("init_samr_q_create_dom_group\n"));
+
+ q_e->pol = *pol;
+
+ init_unistr2(&q_e->uni_acct_desc, acct_desc, UNI_FLAGS_NONE);
+ init_uni_hdr(&q_e->hdr_acct_desc, &q_e->uni_acct_desc);
+
+ q_e->access_mask = access_mask;
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+BOOL samr_io_q_create_dom_group(const char *desc, SAMR_Q_CREATE_DOM_GROUP * q_e,
+ prs_struct *ps, int depth)
+{
+ if (q_e == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "samr_io_q_create_dom_group");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!smb_io_pol_hnd("pol", &q_e->pol, ps, depth))
+ return False;
+
+ if(!smb_io_unihdr("hdr_acct_desc", &q_e->hdr_acct_desc, ps, depth))
+ return False;
+ if(!smb_io_unistr2("uni_acct_desc", &q_e->uni_acct_desc,
+ q_e->hdr_acct_desc.buffer, ps, depth))
+ return False;
+
+ if(!prs_align(ps))
+ return False;
+ if(!prs_uint32("access", ps, depth, &q_e->access_mask))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+BOOL samr_io_r_create_dom_group(const char *desc, SAMR_R_CREATE_DOM_GROUP * r_u,
+ prs_struct *ps, int depth)
+{
+ if (r_u == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "samr_io_r_create_dom_group");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!smb_io_pol_hnd("pol", &r_u->pol, ps, depth))
+ return False;
+
+ if(!prs_uint32("rid ", ps, depth, &r_u->rid))
+ return False;
+ if(!prs_ntstatus("status", ps, depth, &r_u->status))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+inits a SAMR_Q_DELETE_DOM_GROUP structure.
+********************************************************************/
+
+void init_samr_q_delete_dom_group(SAMR_Q_DELETE_DOM_GROUP * q_c,
+ POLICY_HND *hnd)
+{
+ DEBUG(5, ("init_samr_q_delete_dom_group\n"));
+
+ q_c->group_pol = *hnd;
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+BOOL samr_io_q_delete_dom_group(const char *desc, SAMR_Q_DELETE_DOM_GROUP * q_u,
+ prs_struct *ps, int depth)
+{
+ if (q_u == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "samr_io_q_delete_dom_group");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!smb_io_pol_hnd("group_pol", &q_u->group_pol, ps, depth))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+BOOL samr_io_r_delete_dom_group(const char *desc, SAMR_R_DELETE_DOM_GROUP * r_u,
+ prs_struct *ps, int depth)
+{
+ if (r_u == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "samr_io_r_delete_dom_group");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!smb_io_pol_hnd("pol", &r_u->pol, ps, depth))
+ return False;
+
+ if(!prs_ntstatus("status", ps, depth, &r_u->status))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+inits a SAMR_Q_DEL_GROUPMEM structure.
+********************************************************************/
+
+void init_samr_q_del_groupmem(SAMR_Q_DEL_GROUPMEM * q_e,
+ POLICY_HND *pol, uint32 rid)
+{
+ DEBUG(5, ("init_samr_q_del_groupmem\n"));
+
+ q_e->pol = *pol;
+ q_e->rid = rid;
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+BOOL samr_io_q_del_groupmem(const char *desc, SAMR_Q_DEL_GROUPMEM * q_e,
+ prs_struct *ps, int depth)
+{
+ if (q_e == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "samr_io_q_del_groupmem");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!smb_io_pol_hnd("pol", &q_e->pol, ps, depth))
+ return False;
+
+ if(!prs_uint32("rid", ps, depth, &q_e->rid))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+inits a SAMR_R_DEL_GROUPMEM structure.
+********************************************************************/
+
+void init_samr_r_del_groupmem(SAMR_R_DEL_GROUPMEM * r_u, POLICY_HND *pol,
+ NTSTATUS status)
+{
+ DEBUG(5, ("init_samr_r_del_groupmem\n"));
+
+ r_u->status = status;
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+BOOL samr_io_r_del_groupmem(const char *desc, SAMR_R_DEL_GROUPMEM * r_u,
+ prs_struct *ps, int depth)
+{
+ if (r_u == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "samr_io_r_del_groupmem");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_ntstatus("status", ps, depth, &r_u->status))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+inits a SAMR_Q_ADD_GROUPMEM structure.
+********************************************************************/
+
+void init_samr_q_add_groupmem(SAMR_Q_ADD_GROUPMEM * q_e,
+ POLICY_HND *pol, uint32 rid)
+{
+ DEBUG(5, ("init_samr_q_add_groupmem\n"));
+
+ q_e->pol = *pol;
+ q_e->rid = rid;
+ q_e->unknown = 0x0005;
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+BOOL samr_io_q_add_groupmem(const char *desc, SAMR_Q_ADD_GROUPMEM * q_e,
+ prs_struct *ps, int depth)
+{
+ if (q_e == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "samr_io_q_add_groupmem");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!smb_io_pol_hnd("pol", &q_e->pol, ps, depth))
+ return False;
+
+ if(!prs_uint32("rid ", ps, depth, &q_e->rid))
+ return False;
+ if(!prs_uint32("unknown", ps, depth, &q_e->unknown))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+inits a SAMR_R_ADD_GROUPMEM structure.
+********************************************************************/
+
+void init_samr_r_add_groupmem(SAMR_R_ADD_GROUPMEM * r_u, POLICY_HND *pol,
+ NTSTATUS status)
+{
+ DEBUG(5, ("init_samr_r_add_groupmem\n"));
+
+ r_u->status = status;
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+BOOL samr_io_r_add_groupmem(const char *desc, SAMR_R_ADD_GROUPMEM * r_u,
+ prs_struct *ps, int depth)
+{
+ if (r_u == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "samr_io_r_add_groupmem");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_ntstatus("status", ps, depth, &r_u->status))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+inits a SAMR_Q_SET_GROUPINFO structure.
+********************************************************************/
+
+void init_samr_q_set_groupinfo(SAMR_Q_SET_GROUPINFO * q_e,
+ POLICY_HND *pol, GROUP_INFO_CTR * ctr)
+{
+ DEBUG(5, ("init_samr_q_set_groupinfo\n"));
+
+ q_e->pol = *pol;
+ q_e->ctr = ctr;
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+BOOL samr_io_q_set_groupinfo(const char *desc, SAMR_Q_SET_GROUPINFO * q_e,
+ prs_struct *ps, int depth)
+{
+ if (q_e == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "samr_io_q_set_groupinfo");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!smb_io_pol_hnd("pol", &q_e->pol, ps, depth))
+ return False;
+
+ if(!samr_group_info_ctr("ctr", &q_e->ctr, ps, depth))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+inits a SAMR_R_SET_GROUPINFO structure.
+********************************************************************/
+
+void init_samr_r_set_groupinfo(SAMR_R_SET_GROUPINFO * r_u, NTSTATUS status)
+{
+ DEBUG(5, ("init_samr_r_set_groupinfo\n"));
+
+ r_u->status = status;
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+BOOL samr_io_r_set_groupinfo(const char *desc, SAMR_R_SET_GROUPINFO * r_u,
+ prs_struct *ps, int depth)
+{
+ if (r_u == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "samr_io_r_set_groupinfo");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_ntstatus("status", ps, depth, &r_u->status))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+inits a SAMR_Q_QUERY_GROUPINFO structure.
+********************************************************************/
+
+void init_samr_q_query_groupinfo(SAMR_Q_QUERY_GROUPINFO * q_e,
+ POLICY_HND *pol, uint16 switch_level)
+{
+ DEBUG(5, ("init_samr_q_query_groupinfo\n"));
+
+ q_e->pol = *pol;
+
+ q_e->switch_level = switch_level;
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+BOOL samr_io_q_query_groupinfo(const char *desc, SAMR_Q_QUERY_GROUPINFO * q_e,
+ prs_struct *ps, int depth)
+{
+ if (q_e == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "samr_io_q_query_groupinfo");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!smb_io_pol_hnd("pol", &q_e->pol, ps, depth))
+ return False;
+
+ if(!prs_uint16("switch_level", ps, depth, &q_e->switch_level))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+inits a SAMR_R_QUERY_GROUPINFO structure.
+********************************************************************/
+
+void init_samr_r_query_groupinfo(SAMR_R_QUERY_GROUPINFO * r_u,
+ GROUP_INFO_CTR * ctr, NTSTATUS status)
+{
+ DEBUG(5, ("init_samr_r_query_groupinfo\n"));
+
+ r_u->ptr = (NT_STATUS_IS_OK(status) && ctr != NULL) ? 1 : 0;
+ r_u->ctr = ctr;
+ r_u->status = status;
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+BOOL samr_io_r_query_groupinfo(const char *desc, SAMR_R_QUERY_GROUPINFO * r_u,
+ prs_struct *ps, int depth)
+{
+ if (r_u == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "samr_io_r_query_groupinfo");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("ptr", ps, depth, &r_u->ptr))
+ return False;
+
+ if (r_u->ptr != 0) {
+ if(!samr_group_info_ctr("ctr", &r_u->ctr, ps, depth))
+ return False;
+ }
+
+ if(!prs_align(ps))
+ return False;
+ if(!prs_ntstatus("status", ps, depth, &r_u->status))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+inits a SAMR_Q_QUERY_GROUPMEM structure.
+********************************************************************/
+
+void init_samr_q_query_groupmem(SAMR_Q_QUERY_GROUPMEM * q_c, POLICY_HND *hnd)
+{
+ DEBUG(5, ("init_samr_q_query_groupmem\n"));
+
+ q_c->group_pol = *hnd;
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+BOOL samr_io_q_query_groupmem(const char *desc, SAMR_Q_QUERY_GROUPMEM * q_u,
+ prs_struct *ps, int depth)
+{
+ if (q_u == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "samr_io_q_query_groupmem");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!smb_io_pol_hnd("group_pol", &q_u->group_pol, ps, depth))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+inits a SAMR_R_QUERY_GROUPMEM structure.
+********************************************************************/
+
+void init_samr_r_query_groupmem(SAMR_R_QUERY_GROUPMEM * r_u,
+ uint32 num_entries, uint32 *rid,
+ uint32 *attr, NTSTATUS status)
+{
+ DEBUG(5, ("init_samr_r_query_groupmem\n"));
+
+ if (NT_STATUS_IS_OK(status)) {
+ r_u->ptr = 1;
+ r_u->num_entries = num_entries;
+
+ r_u->ptr_attrs = attr != NULL ? 1 : 0;
+ r_u->ptr_rids = rid != NULL ? 1 : 0;
+
+ r_u->num_rids = num_entries;
+ r_u->rid = rid;
+
+ r_u->num_attrs = num_entries;
+ r_u->attr = attr;
+ } else {
+ r_u->ptr = 0;
+ r_u->num_entries = 0;
+ }
+
+ r_u->status = status;
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+BOOL samr_io_r_query_groupmem(const char *desc, SAMR_R_QUERY_GROUPMEM * r_u,
+ prs_struct *ps, int depth)
+{
+ uint32 i;
+
+ if (r_u == NULL)
+ return False;
+
+ if (UNMARSHALLING(ps))
+ ZERO_STRUCTP(r_u);
+
+ prs_debug(ps, depth, desc, "samr_io_r_query_groupmem");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("ptr", ps, depth, &r_u->ptr))
+ return False;
+ if(!prs_uint32("num_entries ", ps, depth, &r_u->num_entries))
+ return False;
+
+ if (r_u->ptr != 0) {
+ if(!prs_uint32("ptr_rids ", ps, depth, &r_u->ptr_rids))
+ return False;
+ if(!prs_uint32("ptr_attrs", ps, depth, &r_u->ptr_attrs))
+ return False;
+
+ if (r_u->ptr_rids != 0) {
+ if(!prs_uint32("num_rids", ps, depth, &r_u->num_rids))
+ return False;
+ if (UNMARSHALLING(ps) && r_u->num_rids != 0) {
+ r_u->rid = (uint32 *)prs_alloc_mem(ps,sizeof(r_u->rid[0])*r_u->num_rids);
+ if (r_u->rid == NULL)
+ return False;
+ }
+
+ for (i = 0; i < r_u->num_rids; i++) {
+ if(!prs_uint32("", ps, depth, &r_u->rid[i]))
+ return False;
+ }
+ }
+
+ if (r_u->ptr_attrs != 0) {
+ if(!prs_uint32("num_attrs", ps, depth, &r_u->num_attrs))
+ return False;
+
+ if (UNMARSHALLING(ps) && r_u->num_attrs != 0) {
+ r_u->attr = (uint32 *)prs_alloc_mem(ps,sizeof(r_u->attr[0])*r_u->num_attrs);
+ if (r_u->attr == NULL)
+ return False;
+ }
+
+ for (i = 0; i < r_u->num_attrs; i++) {
+ if(!prs_uint32("", ps, depth, &r_u->attr[i]))
+ return False;
+ }
+ }
+ }
+
+ if(!prs_ntstatus("status", ps, depth, &r_u->status))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+inits a SAMR_Q_QUERY_USERGROUPS structure.
+********************************************************************/
+
+void init_samr_q_query_usergroups(SAMR_Q_QUERY_USERGROUPS * q_u,
+ POLICY_HND *hnd)
+{
+ DEBUG(5, ("init_samr_q_query_usergroups\n"));
+
+ q_u->pol = *hnd;
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+BOOL samr_io_q_query_usergroups(const char *desc, SAMR_Q_QUERY_USERGROUPS * q_u,
+ prs_struct *ps, int depth)
+{
+ if (q_u == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "samr_io_q_query_usergroups");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!smb_io_pol_hnd("pol", &q_u->pol, ps, depth))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+inits a SAMR_R_QUERY_USERGROUPS structure.
+********************************************************************/
+
+void init_samr_r_query_usergroups(SAMR_R_QUERY_USERGROUPS * r_u,
+ uint32 num_gids, DOM_GID * gid,
+ NTSTATUS status)
+{
+ DEBUG(5, ("init_samr_r_query_usergroups\n"));
+
+ if (NT_STATUS_IS_OK(status)) {
+ r_u->ptr_0 = 1;
+ r_u->num_entries = num_gids;
+ r_u->ptr_1 = (num_gids != 0) ? 1 : 0;
+ r_u->num_entries2 = num_gids;
+
+ r_u->gid = gid;
+ } else {
+ r_u->ptr_0 = 0;
+ r_u->num_entries = 0;
+ r_u->ptr_1 = 0;
+ r_u->gid = NULL;
+ }
+
+ r_u->status = status;
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+BOOL samr_io_gids(const char *desc, uint32 *num_gids, DOM_GID ** gid,
+ prs_struct *ps, int depth)
+{
+ uint32 i;
+ if (gid == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "samr_io_gids");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("num_gids", ps, depth, num_gids))
+ return False;
+
+ if ((*num_gids) != 0) {
+ if (UNMARSHALLING(ps)) {
+ (*gid) = (DOM_GID *)prs_alloc_mem(ps,sizeof(DOM_GID)*(*num_gids));
+ }
+
+ if ((*gid) == NULL) {
+ return False;
+ }
+
+ for (i = 0; i < (*num_gids); i++) {
+ if(!smb_io_gid("gids", &(*gid)[i], ps, depth))
+ return False;
+ }
+ }
+
+ return True;
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+BOOL samr_io_r_query_usergroups(const char *desc, SAMR_R_QUERY_USERGROUPS * r_u,
+ prs_struct *ps, int depth)
+{
+ if (r_u == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "samr_io_r_query_usergroups");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("ptr_0 ", ps, depth, &r_u->ptr_0))
+ return False;
+
+ if (r_u->ptr_0 != 0) {
+ if(!prs_uint32("num_entries ", ps, depth, &r_u->num_entries))
+ return False;
+ if(!prs_uint32("ptr_1 ", ps, depth, &r_u->ptr_1))
+ return False;
+
+ if (r_u->num_entries != 0 && r_u->ptr_1 != 0) {
+ if(!samr_io_gids("gids", &r_u->num_entries2, &r_u->gid, ps, depth))
+ return False;
+ }
+ }
+
+ if(!prs_align(ps))
+ return False;
+ if(!prs_ntstatus("status", ps, depth, &r_u->status))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+inits a SAMR_Q_ENUM_DOMAINS structure.
+********************************************************************/
+
+void init_samr_q_enum_domains(SAMR_Q_ENUM_DOMAINS * q_e,
+ POLICY_HND *pol,
+ uint32 start_idx, uint32 size)
+{
+ DEBUG(5, ("init_samr_q_enum_domains\n"));
+
+ q_e->pol = *pol;
+
+ q_e->start_idx = start_idx;
+ q_e->max_size = size;
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+BOOL samr_io_q_enum_domains(const char *desc, SAMR_Q_ENUM_DOMAINS * q_e,
+ prs_struct *ps, int depth)
+{
+ if (q_e == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "samr_io_q_enum_domains");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!smb_io_pol_hnd("pol", &q_e->pol, ps, depth))
+ return False;
+
+ if(!prs_uint32("start_idx", ps, depth, &q_e->start_idx))
+ return False;
+ if(!prs_uint32("max_size ", ps, depth, &q_e->max_size))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+inits a SAMR_R_ENUM_DOMAINS structure.
+********************************************************************/
+
+void init_samr_r_enum_domains(SAMR_R_ENUM_DOMAINS * r_u,
+ uint32 next_idx, uint32 num_sam_entries)
+{
+ DEBUG(5, ("init_samr_r_enum_domains\n"));
+
+ r_u->next_idx = next_idx;
+
+ if (num_sam_entries != 0) {
+ r_u->ptr_entries1 = 1;
+ r_u->ptr_entries2 = 1;
+ r_u->num_entries2 = num_sam_entries;
+ r_u->num_entries3 = num_sam_entries;
+
+ r_u->num_entries4 = num_sam_entries;
+ } else {
+ r_u->ptr_entries1 = 0;
+ r_u->num_entries2 = num_sam_entries;
+ r_u->ptr_entries2 = 1;
+ }
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+BOOL samr_io_r_enum_domains(const char *desc, SAMR_R_ENUM_DOMAINS * r_u,
+ prs_struct *ps, int depth)
+{
+ uint32 i;
+
+ if (r_u == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "samr_io_r_enum_domains");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("next_idx ", ps, depth, &r_u->next_idx))
+ return False;
+ if(!prs_uint32("ptr_entries1", ps, depth, &r_u->ptr_entries1))
+ return False;
+
+ if (r_u->ptr_entries1 != 0) {
+ if(!prs_uint32("num_entries2", ps, depth, &r_u->num_entries2))
+ return False;
+ if(!prs_uint32("ptr_entries2", ps, depth, &r_u->ptr_entries2))
+ return False;
+ if(!prs_uint32("num_entries3", ps, depth, &r_u->num_entries3))
+ return False;
+
+ if (UNMARSHALLING(ps)) {
+ r_u->sam = (SAM_ENTRY *)prs_alloc_mem(ps,sizeof(SAM_ENTRY)*r_u->num_entries2);
+ r_u->uni_dom_name = (UNISTR2 *)prs_alloc_mem(ps,sizeof(UNISTR2)*r_u->num_entries2);
+ }
+
+ if ((r_u->sam == NULL || r_u->uni_dom_name == NULL) && r_u->num_entries2 != 0) {
+ DEBUG(0, ("NULL pointers in SAMR_R_ENUM_DOMAINS\n"));
+ r_u->num_entries4 = 0;
+ r_u->status = NT_STATUS_MEMORY_NOT_ALLOCATED;
+ return False;
+ }
+
+ for (i = 0; i < r_u->num_entries2; i++) {
+ fstring tmp;
+ slprintf(tmp, sizeof(tmp) - 1, "dom[%d]", i);
+ if(!sam_io_sam_entry(tmp, &r_u->sam[i], ps, depth))
+ return False;
+ }
+
+ for (i = 0; i < r_u->num_entries2; i++) {
+ fstring tmp;
+ slprintf(tmp, sizeof(tmp) - 1, "dom[%d]", i);
+ if(!smb_io_unistr2(tmp, &r_u->uni_dom_name[i],
+ r_u->sam[i].hdr_name.buffer, ps,
+ depth))
+ return False;
+ }
+
+ }
+
+ if(!prs_align(ps))
+ return False;
+ if(!prs_uint32("num_entries4", ps, depth, &r_u->num_entries4))
+ return False;
+ if(!prs_ntstatus("status", ps, depth, &r_u->status))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+inits a SAMR_Q_ENUM_DOM_GROUPS structure.
+********************************************************************/
+
+void init_samr_q_enum_dom_groups(SAMR_Q_ENUM_DOM_GROUPS * q_e,
+ POLICY_HND *pol,
+ uint32 start_idx, uint32 size)
+{
+ DEBUG(5, ("init_samr_q_enum_dom_groups\n"));
+
+ q_e->pol = *pol;
+
+ q_e->start_idx = start_idx;
+ q_e->max_size = size;
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+BOOL samr_io_q_enum_dom_groups(const char *desc, SAMR_Q_ENUM_DOM_GROUPS * q_e,
+ prs_struct *ps, int depth)
+{
+ if (q_e == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "samr_io_q_enum_dom_groups");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!smb_io_pol_hnd("pol", &(q_e->pol), ps, depth))
+ return False;
+
+ if(!prs_uint32("start_idx", ps, depth, &q_e->start_idx))
+ return False;
+ if(!prs_uint32("max_size ", ps, depth, &q_e->max_size))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+inits a SAMR_R_ENUM_DOM_GROUPS structure.
+********************************************************************/
+
+void init_samr_r_enum_dom_groups(SAMR_R_ENUM_DOM_GROUPS * r_u,
+ uint32 next_idx, uint32 num_sam_entries)
+{
+ DEBUG(5, ("init_samr_r_enum_dom_groups\n"));
+
+ r_u->next_idx = next_idx;
+
+ if (num_sam_entries != 0) {
+ r_u->ptr_entries1 = 1;
+ r_u->ptr_entries2 = 1;
+ r_u->num_entries2 = num_sam_entries;
+ r_u->num_entries3 = num_sam_entries;
+
+ r_u->num_entries4 = num_sam_entries;
+ } else {
+ r_u->ptr_entries1 = 0;
+ r_u->num_entries2 = num_sam_entries;
+ r_u->ptr_entries2 = 1;
+ }
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+BOOL samr_io_r_enum_dom_groups(const char *desc, SAMR_R_ENUM_DOM_GROUPS * r_u,
+ prs_struct *ps, int depth)
+{
+ uint32 i;
+
+ if (r_u == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "samr_io_r_enum_dom_groups");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("next_idx ", ps, depth, &r_u->next_idx))
+ return False;
+ if(!prs_uint32("ptr_entries1", ps, depth, &r_u->ptr_entries1))
+ return False;
+
+ if (r_u->ptr_entries1 != 0) {
+ if(!prs_uint32("num_entries2", ps, depth, &r_u->num_entries2))
+ return False;
+ if(!prs_uint32("ptr_entries2", ps, depth, &r_u->ptr_entries2))
+ return False;
+ if(!prs_uint32("num_entries3", ps, depth, &r_u->num_entries3))
+ return False;
+
+ if (UNMARSHALLING(ps)) {
+ r_u->sam = (SAM_ENTRY *)prs_alloc_mem(ps,sizeof(SAM_ENTRY)*r_u->num_entries2);
+ r_u->uni_grp_name = (UNISTR2 *)prs_alloc_mem(ps,sizeof(UNISTR2)*r_u->num_entries2);
+ }
+
+ if ((r_u->sam == NULL || r_u->uni_grp_name == NULL) && r_u->num_entries2 != 0) {
+ DEBUG(0,
+ ("NULL pointers in SAMR_R_ENUM_DOM_GROUPS\n"));
+ r_u->num_entries4 = 0;
+ r_u->status = NT_STATUS_MEMORY_NOT_ALLOCATED;
+ return False;
+ }
+
+ for (i = 0; i < r_u->num_entries2; i++) {
+ if(!sam_io_sam_entry("", &r_u->sam[i], ps, depth))
+ return False;
+ }
+
+ for (i = 0; i < r_u->num_entries2; i++) {
+ if(!smb_io_unistr2("", &r_u->uni_grp_name[i],
+ r_u->sam[i].hdr_name.buffer, ps, depth))
+ return False;
+ }
+ }
+
+ if(!prs_align(ps))
+ return False;
+ if(!prs_uint32("num_entries4", ps, depth, &r_u->num_entries4))
+ return False;
+ if(!prs_ntstatus("status", ps, depth, &r_u->status))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+inits a SAMR_Q_ENUM_DOM_ALIASES structure.
+********************************************************************/
+
+void init_samr_q_enum_dom_aliases(SAMR_Q_ENUM_DOM_ALIASES * q_e,
+ POLICY_HND *pol, uint32 start_idx,
+ uint32 size)
+{
+ DEBUG(5, ("init_samr_q_enum_dom_aliases\n"));
+
+ q_e->pol = *pol;
+
+ q_e->start_idx = start_idx;
+ q_e->max_size = size;
+}
+
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+BOOL samr_io_q_enum_dom_aliases(const char *desc, SAMR_Q_ENUM_DOM_ALIASES * q_e,
+ prs_struct *ps, int depth)
+{
+ if (q_e == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "samr_io_q_enum_dom_aliases");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!smb_io_pol_hnd("pol", &q_e->pol, ps, depth))
+ return False;
+
+ if(!prs_uint32("start_idx", ps, depth, &q_e->start_idx))
+ return False;
+ if(!prs_uint32("max_size ", ps, depth, &q_e->max_size))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+inits a SAMR_R_ENUM_DOM_ALIASES structure.
+********************************************************************/
+
+void init_samr_r_enum_dom_aliases(SAMR_R_ENUM_DOM_ALIASES *r_u, uint32 next_idx, uint32 num_sam_entries)
+{
+ DEBUG(5, ("init_samr_r_enum_dom_aliases\n"));
+
+ r_u->next_idx = next_idx;
+
+ if (num_sam_entries != 0) {
+ r_u->ptr_entries1 = 1;
+ r_u->ptr_entries2 = 1;
+ r_u->num_entries2 = num_sam_entries;
+ r_u->num_entries3 = num_sam_entries;
+
+ r_u->num_entries4 = num_sam_entries;
+ } else {
+ r_u->ptr_entries1 = 0;
+ r_u->num_entries2 = num_sam_entries;
+ r_u->ptr_entries2 = 1;
+ }
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+BOOL samr_io_r_enum_dom_aliases(const char *desc, SAMR_R_ENUM_DOM_ALIASES * r_u,
+ prs_struct *ps, int depth)
+{
+ uint32 i;
+
+ if (r_u == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "samr_io_r_enum_dom_aliases");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("next_idx ", ps, depth, &r_u->next_idx))
+ return False;
+ if(!prs_uint32("ptr_entries1", ps, depth, &r_u->ptr_entries1))
+ return False;
+
+ if (r_u->ptr_entries1 != 0) {
+ if(!prs_uint32("num_entries2", ps, depth, &r_u->num_entries2))
+ return False;
+ if(!prs_uint32("ptr_entries2", ps, depth, &r_u->ptr_entries2))
+ return False;
+ if(!prs_uint32("num_entries3", ps, depth, &r_u->num_entries3))
+ return False;
+
+ if (UNMARSHALLING(ps) && (r_u->num_entries2 > 0)) {
+ r_u->sam = (SAM_ENTRY *)prs_alloc_mem(ps,sizeof(SAM_ENTRY)*r_u->num_entries2);
+ r_u->uni_grp_name = (UNISTR2 *)prs_alloc_mem(ps,sizeof(UNISTR2)*r_u->num_entries2);
+ }
+
+ if (r_u->num_entries2 != 0 &&
+ (r_u->sam == NULL || r_u->uni_grp_name == NULL)) {
+ DEBUG(0,("NULL pointers in SAMR_R_ENUM_DOM_ALIASES\n"));
+ r_u->num_entries4 = 0;
+ r_u->status = NT_STATUS_MEMORY_NOT_ALLOCATED;
+ return False;
+ }
+
+ for (i = 0; i < r_u->num_entries2; i++) {
+ if(!sam_io_sam_entry("", &r_u->sam[i], ps, depth))
+ return False;
+ }
+
+ for (i = 0; i < r_u->num_entries2; i++) {
+ if(!smb_io_unistr2("", &r_u->uni_grp_name[i],
+ r_u->sam[i].hdr_name.buffer, ps,
+ depth))
+ return False;
+ }
+ }
+
+ if(!prs_align(ps))
+ return False;
+ if(!prs_uint32("num_entries4", ps, depth, &r_u->num_entries4))
+ return False;
+ if(!prs_ntstatus("status", ps, depth, &r_u->status))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+inits a ALIAS_INFO1 structure.
+********************************************************************/
+
+void init_samr_alias_info1(ALIAS_INFO1 * al1, char *acct_name, uint32 num_member, char *acct_desc)
+{
+ DEBUG(5, ("init_samr_alias_info1\n"));
+
+ init_unistr2(&al1->uni_acct_name, acct_name, UNI_FLAGS_NONE);
+ init_uni_hdr(&al1->hdr_acct_name, &al1->uni_acct_name);
+
+ al1->num_member=num_member;
+
+ init_unistr2(&al1->uni_acct_desc, acct_desc, UNI_FLAGS_NONE);
+ init_uni_hdr(&al1->hdr_acct_desc, &al1->uni_acct_name);
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+BOOL samr_io_alias_info1(const char *desc, ALIAS_INFO1 * al1,
+ prs_struct *ps, int depth)
+{
+ if (al1 == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "samr_io_alias_info1");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!smb_io_unihdr("hdr_acct_name", &al1->hdr_acct_name, ps, depth))
+ return False;
+ if(!prs_uint32("num_member", ps, depth, &al1->num_member))
+ return False;
+ if(!smb_io_unihdr("hdr_acct_desc", &al1->hdr_acct_desc, ps, depth))
+ return False;
+
+ if(!smb_io_unistr2("uni_acct_name", &al1->uni_acct_name,
+ al1->hdr_acct_name.buffer, ps, depth))
+ return False;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!smb_io_unistr2("uni_acct_desc", &al1->uni_acct_desc,
+ al1->hdr_acct_desc.buffer, ps, depth))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+inits a ALIAS_INFO3 structure.
+********************************************************************/
+
+void init_samr_alias_info3(ALIAS_INFO3 * al3, const char *acct_desc)
+{
+ DEBUG(5, ("init_samr_alias_info3\n"));
+
+ init_unistr2(&al3->uni_acct_desc, acct_desc, UNI_FLAGS_NONE);
+ init_uni_hdr(&al3->hdr_acct_desc, &al3->uni_acct_desc);
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+BOOL samr_io_alias_info3(const char *desc, ALIAS_INFO3 * al3,
+ prs_struct *ps, int depth)
+{
+ if (al3 == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "samr_io_alias_info3");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!smb_io_unihdr("hdr_acct_desc", &al3->hdr_acct_desc, ps, depth))
+ return False;
+ if(!smb_io_unistr2("uni_acct_desc", &al3->uni_acct_desc,
+ al3->hdr_acct_desc.buffer, ps, depth))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+BOOL samr_alias_info_ctr(const char *desc, ALIAS_INFO_CTR * ctr,
+ prs_struct *ps, int depth)
+{
+ if (ctr == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "samr_alias_info_ctr");
+ depth++;
+
+ if(!prs_uint16("switch_value1", ps, depth, &ctr->switch_value1))
+ return False;
+ if(!prs_uint16("switch_value2", ps, depth, &ctr->switch_value2))
+ return False;
+
+ switch (ctr->switch_value1) {
+ case 1:
+ if(!samr_io_alias_info1("alias_info1", &ctr->alias.info1, ps, depth))
+ return False;
+ break;
+ case 3:
+ if(!samr_io_alias_info3("alias_info3", &ctr->alias.info3, ps, depth))
+ return False;
+ break;
+ default:
+ DEBUG(0,("samr_alias_info_ctr: unsupported switch level\n"));
+ break;
+ }
+
+ return True;
+}
+
+/*******************************************************************
+inits a SAMR_Q_QUERY_ALIASINFO structure.
+********************************************************************/
+
+void init_samr_q_query_aliasinfo(SAMR_Q_QUERY_ALIASINFO * q_e,
+ POLICY_HND *pol, uint16 switch_level)
+{
+ DEBUG(5, ("init_samr_q_query_aliasinfo\n"));
+
+ q_e->pol = *pol;
+ q_e->switch_level = switch_level;
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+BOOL samr_io_q_query_aliasinfo(const char *desc, SAMR_Q_QUERY_ALIASINFO * q_e,
+ prs_struct *ps, int depth)
+{
+ if (q_e == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "samr_io_q_query_aliasinfo");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!smb_io_pol_hnd("pol", &(q_e->pol), ps, depth))
+ return False;
+
+ if(!prs_uint16("switch_level", ps, depth, &q_e->switch_level))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+inits a SAMR_R_QUERY_ALIASINFO structure.
+********************************************************************/
+
+void init_samr_r_query_aliasinfo(SAMR_R_QUERY_ALIASINFO * r_u,
+ ALIAS_INFO_CTR * ctr, NTSTATUS status)
+{
+ DEBUG(5, ("init_samr_r_query_aliasinfo\n"));
+
+ r_u->ptr = (NT_STATUS_IS_OK(status) && ctr != NULL) ? 1 : 0;
+ r_u->ctr = *ctr;
+ r_u->status = status;
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+BOOL samr_io_r_query_aliasinfo(const char *desc, SAMR_R_QUERY_ALIASINFO * r_u,
+ prs_struct *ps, int depth)
+{
+ if (r_u == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "samr_io_r_query_aliasinfo");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("ptr", ps, depth, &r_u->ptr))
+ return False;
+
+ if (r_u->ptr != 0) {
+ if(!samr_alias_info_ctr("ctr", &r_u->ctr, ps, depth))
+ return False;
+ }
+
+ if(!prs_align(ps))
+ return False;
+ if(!prs_ntstatus("status", ps, depth, &r_u->status))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+inits a SAMR_Q_SET_ALIASINFO structure.
+********************************************************************/
+
+void init_samr_q_set_aliasinfo(SAMR_Q_SET_ALIASINFO * q_u,
+ POLICY_HND *hnd, ALIAS_INFO_CTR * ctr)
+{
+ DEBUG(5, ("init_samr_q_set_aliasinfo\n"));
+
+ q_u->alias_pol = *hnd;
+ q_u->ctr = *ctr;
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+BOOL samr_io_q_set_aliasinfo(const char *desc, SAMR_Q_SET_ALIASINFO * q_u,
+ prs_struct *ps, int depth)
+{
+ if (q_u == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "samr_io_q_set_aliasinfo");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!smb_io_pol_hnd("alias_pol", &q_u->alias_pol, ps, depth))
+ return False;
+ if(!samr_alias_info_ctr("ctr", &q_u->ctr, ps, depth))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+BOOL samr_io_r_set_aliasinfo(const char *desc, SAMR_R_SET_ALIASINFO * r_u,
+ prs_struct *ps, int depth)
+{
+ if (r_u == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "samr_io_r_set_aliasinfo");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+ if(!prs_ntstatus("status", ps, depth, &r_u->status))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+inits a SAMR_Q_QUERY_USERALIASES structure.
+********************************************************************/
+
+void init_samr_q_query_useraliases(SAMR_Q_QUERY_USERALIASES * q_u,
+ POLICY_HND *hnd,
+ uint32 num_sids,
+ uint32 *ptr_sid, DOM_SID2 * sid)
+{
+ DEBUG(5, ("init_samr_q_query_useraliases\n"));
+
+ q_u->pol = *hnd;
+
+ q_u->num_sids1 = num_sids;
+ q_u->ptr = 1;
+ q_u->num_sids2 = num_sids;
+
+ q_u->ptr_sid = ptr_sid;
+ q_u->sid = sid;
+}
+
+/*******************************************************************
+reads or writes a SAMR_Q_QUERY_USERALIASES structure.
+********************************************************************/
+
+BOOL samr_io_q_query_useraliases(const char *desc, SAMR_Q_QUERY_USERALIASES * q_u,
+ prs_struct *ps, int depth)
+{
+ fstring tmp;
+ uint32 i;
+
+ if (q_u == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "samr_io_q_query_useraliases");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!smb_io_pol_hnd("pol", &q_u->pol, ps, depth))
+ return False;
+
+ if(!prs_uint32("num_sids1", ps, depth, &q_u->num_sids1))
+ return False;
+ if(!prs_uint32("ptr ", ps, depth, &q_u->ptr))
+ return False;
+
+ if (q_u->ptr==0)
+ return True;
+
+ if(!prs_uint32("num_sids2", ps, depth, &q_u->num_sids2))
+ return False;
+
+ if (UNMARSHALLING(ps) && (q_u->num_sids2 != 0)) {
+ q_u->ptr_sid = (uint32 *)prs_alloc_mem(ps,sizeof(q_u->ptr_sid[0])*q_u->num_sids2);
+ if (q_u->ptr_sid == NULL)
+ return False;
+
+ q_u->sid = (DOM_SID2 *)prs_alloc_mem(ps, sizeof(q_u->sid[0]) * q_u->num_sids2);
+ if (q_u->sid == NULL)
+ return False;
+ }
+
+ for (i = 0; i < q_u->num_sids2; i++) {
+ slprintf(tmp, sizeof(tmp) - 1, "ptr[%02d]", i);
+ if(!prs_uint32(tmp, ps, depth, &q_u->ptr_sid[i]))
+ return False;
+ }
+
+ for (i = 0; i < q_u->num_sids2; i++) {
+ if (q_u->ptr_sid[i] != 0) {
+ slprintf(tmp, sizeof(tmp) - 1, "sid[%02d]", i);
+ if(!smb_io_dom_sid2(tmp, &q_u->sid[i], ps, depth))
+ return False;
+ }
+ }
+
+ return True;
+}
+
+/*******************************************************************
+inits a SAMR_R_QUERY_USERALIASES structure.
+********************************************************************/
+
+void init_samr_r_query_useraliases(SAMR_R_QUERY_USERALIASES * r_u,
+ uint32 num_rids, uint32 *rid,
+ NTSTATUS status)
+{
+ DEBUG(5, ("init_samr_r_query_useraliases\n"));
+
+ if (NT_STATUS_IS_OK(status)) {
+ r_u->num_entries = num_rids;
+ r_u->ptr = 1;
+ r_u->num_entries2 = num_rids;
+
+ r_u->rid = rid;
+ } else {
+ r_u->num_entries = 0;
+ r_u->ptr = 0;
+ r_u->num_entries2 = 0;
+ }
+
+ r_u->status = status;
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+BOOL samr_io_rids(const char *desc, uint32 *num_rids, uint32 **rid,
+ prs_struct *ps, int depth)
+{
+ fstring tmp;
+ uint32 i;
+ if (rid == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "samr_io_rids");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("num_rids", ps, depth, num_rids))
+ return False;
+
+ if ((*num_rids) != 0) {
+ if (UNMARSHALLING(ps)) {
+ /* reading */
+ (*rid) = (uint32 *)prs_alloc_mem(ps,sizeof(uint32)*(*num_rids));
+ }
+ if ((*rid) == NULL)
+ return False;
+
+ for (i = 0; i < (*num_rids); i++) {
+ slprintf(tmp, sizeof(tmp) - 1, "rid[%02d]", i);
+ if(!prs_uint32(tmp, ps, depth, &((*rid)[i])))
+ return False;
+ }
+ }
+
+ return True;
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+BOOL samr_io_r_query_useraliases(const char *desc, SAMR_R_QUERY_USERALIASES * r_u,
+ prs_struct *ps, int depth)
+{
+ if (r_u == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "samr_io_r_query_useraliases");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("num_entries", ps, depth, &r_u->num_entries))
+ return False;
+ if(!prs_uint32("ptr ", ps, depth, &r_u->ptr))
+ return False;
+
+ if (r_u->ptr != 0) {
+ if(!samr_io_rids("rids", &r_u->num_entries2, &r_u->rid, ps, depth))
+ return False;
+ }
+
+ if(!prs_align(ps))
+ return False;
+ if(!prs_ntstatus("status", ps, depth, &r_u->status))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+inits a SAMR_Q_OPEN_ALIAS structure.
+********************************************************************/
+
+void init_samr_q_open_alias(SAMR_Q_OPEN_ALIAS * q_u, POLICY_HND *pol,
+ uint32 access_mask, uint32 rid)
+{
+ DEBUG(5, ("init_samr_q_open_alias\n"));
+
+ q_u->dom_pol = *pol;
+ q_u->access_mask = access_mask;
+ q_u->rid_alias = rid;
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+BOOL samr_io_q_open_alias(const char *desc, SAMR_Q_OPEN_ALIAS * q_u,
+ prs_struct *ps, int depth)
+{
+ if (q_u == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "samr_io_q_open_alias");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!smb_io_pol_hnd("domain_pol", &q_u->dom_pol, ps, depth))
+ return False;
+
+ if(!prs_uint32("access_mask", ps, depth, &q_u->access_mask))
+ return False;
+ if(!prs_uint32("rid_alias", ps, depth, &q_u->rid_alias))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+BOOL samr_io_r_open_alias(const char *desc, SAMR_R_OPEN_ALIAS * r_u,
+ prs_struct *ps, int depth)
+{
+ if (r_u == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "samr_io_r_open_alias");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!smb_io_pol_hnd("pol", &r_u->pol, ps, depth))
+ return False;
+
+ if(!prs_ntstatus("status", ps, depth, &r_u->status))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+inits a SAMR_Q_LOOKUP_RIDS structure.
+********************************************************************/
+
+void init_samr_q_lookup_rids(TALLOC_CTX *ctx, SAMR_Q_LOOKUP_RIDS * q_u,
+ POLICY_HND *pol, uint32 flags,
+ uint32 num_rids, uint32 *rid)
+{
+ DEBUG(5, ("init_samr_q_lookup_rids\n"));
+
+ q_u->pol = *pol;
+
+ q_u->num_rids1 = num_rids;
+ q_u->flags = flags;
+ q_u->ptr = 0;
+ q_u->num_rids2 = num_rids;
+ q_u->rid = (uint32 *)talloc_zero(ctx, num_rids * sizeof(q_u->rid[0]));
+ if (q_u->rid == NULL) {
+ q_u->num_rids1 = 0;
+ q_u->num_rids2 = 0;
+ } else {
+ memcpy(q_u->rid, rid, num_rids * sizeof(q_u->rid[0]));
+ }
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+BOOL samr_io_q_lookup_rids(const char *desc, SAMR_Q_LOOKUP_RIDS * q_u,
+ prs_struct *ps, int depth)
+{
+ uint32 i;
+ fstring tmp;
+
+ if (q_u == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "samr_io_q_lookup_rids");
+ depth++;
+
+ if (UNMARSHALLING(ps))
+ ZERO_STRUCTP(q_u);
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!smb_io_pol_hnd("pol", &q_u->pol, ps, depth))
+ return False;
+
+ if(!prs_uint32("num_rids1", ps, depth, &q_u->num_rids1))
+ return False;
+ if(!prs_uint32("flags ", ps, depth, &q_u->flags))
+ return False;
+ if(!prs_uint32("ptr ", ps, depth, &q_u->ptr))
+ return False;
+ if(!prs_uint32("num_rids2", ps, depth, &q_u->num_rids2))
+ return False;
+
+ if (UNMARSHALLING(ps) && (q_u->num_rids2 != 0)) {
+ q_u->rid = (uint32 *)prs_alloc_mem(ps, sizeof(q_u->rid[0])*q_u->num_rids2);
+ if (q_u->rid == NULL)
+ return False;
+ }
+
+ for (i = 0; i < q_u->num_rids2; i++) {
+ slprintf(tmp, sizeof(tmp) - 1, "rid[%02d] ", i);
+ if(!prs_uint32(tmp, ps, depth, &q_u->rid[i]))
+ return False;
+ }
+
+ return True;
+}
+
+/*******************************************************************
+inits a SAMR_R_LOOKUP_RIDS structure.
+********************************************************************/
+
+void init_samr_r_lookup_rids(SAMR_R_LOOKUP_RIDS * r_u,
+ uint32 num_names, UNIHDR * hdr_name,
+ UNISTR2 *uni_name, uint32 *type)
+{
+ DEBUG(5, ("init_samr_r_lookup_rids\n"));
+
+ r_u->hdr_name = NULL;
+ r_u->uni_name = NULL;
+ r_u->type = NULL;
+
+ if (num_names != 0) {
+ r_u->num_names1 = num_names;
+ r_u->ptr_names = 1;
+ r_u->num_names2 = num_names;
+
+ r_u->num_types1 = num_names;
+ r_u->ptr_types = 1;
+ r_u->num_types2 = num_names;
+
+ r_u->hdr_name = hdr_name;
+ r_u->uni_name = uni_name;
+ r_u->type = type;
+ } else {
+ r_u->num_names1 = num_names;
+ r_u->ptr_names = 0;
+ r_u->num_names2 = num_names;
+
+ r_u->num_types1 = num_names;
+ r_u->ptr_types = 0;
+ r_u->num_types2 = num_names;
+ }
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+BOOL samr_io_r_lookup_rids(const char *desc, SAMR_R_LOOKUP_RIDS * r_u,
+ prs_struct *ps, int depth)
+{
+ uint32 i;
+ fstring tmp;
+ if (r_u == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "samr_io_r_lookup_rids");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("num_names1", ps, depth, &r_u->num_names1))
+ return False;
+ if(!prs_uint32("ptr_names ", ps, depth, &r_u->ptr_names))
+ return False;
+
+ if (r_u->ptr_names != 0) {
+
+ if(!prs_uint32("num_names2", ps, depth, &r_u->num_names2))
+ return False;
+
+
+ if (UNMARSHALLING(ps) && (r_u->num_names2 != 0)) {
+ r_u->hdr_name = (UNIHDR *) prs_alloc_mem(ps, r_u->num_names2 * sizeof(r_u->hdr_name[0]));
+ if (r_u->hdr_name == NULL)
+ return False;
+
+ r_u->uni_name = (UNISTR2 *)prs_alloc_mem(ps, r_u->num_names2 * sizeof(r_u->uni_name[0]));
+ if (r_u->uni_name == NULL)
+ return False;
+ }
+
+ for (i = 0; i < r_u->num_names2; i++) {
+ slprintf(tmp, sizeof(tmp) - 1, "hdr[%02d] ", i);
+ if(!smb_io_unihdr("", &r_u->hdr_name[i], ps, depth))
+ return False;
+ }
+ for (i = 0; i < r_u->num_names2; i++) {
+ slprintf(tmp, sizeof(tmp) - 1, "str[%02d] ", i);
+ if(!smb_io_unistr2("", &r_u->uni_name[i], r_u->hdr_name[i].buffer, ps, depth))
+ return False;
+ }
+
+ }
+
+ if(!prs_align(ps))
+ return False;
+ if(!prs_uint32("num_types1", ps, depth, &r_u->num_types1))
+ return False;
+ if(!prs_uint32("ptr_types ", ps, depth, &r_u->ptr_types))
+ return False;
+
+ if (r_u->ptr_types != 0) {
+
+ if(!prs_uint32("num_types2", ps, depth, &r_u->num_types2))
+ return False;
+
+ if (UNMARSHALLING(ps) && (r_u->num_types2 != 0)) {
+ r_u->type = (uint32 *)prs_alloc_mem(ps, r_u->num_types2 * sizeof(r_u->type[0]));
+ if (r_u->type == NULL)
+ return False;
+ }
+
+ for (i = 0; i < r_u->num_types2; i++) {
+ slprintf(tmp, sizeof(tmp) - 1, "type[%02d] ", i);
+ if(!prs_uint32(tmp, ps, depth, &r_u->type[i]))
+ return False;
+ }
+ }
+
+ if(!prs_ntstatus("status", ps, depth, &r_u->status))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+inits a SAMR_Q_OPEN_ALIAS structure.
+********************************************************************/
+
+void init_samr_q_delete_alias(SAMR_Q_DELETE_DOM_ALIAS * q_u, POLICY_HND *hnd)
+{
+ DEBUG(5, ("init_samr_q_delete_alias\n"));
+
+ q_u->alias_pol = *hnd;
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+BOOL samr_io_q_delete_alias(const char *desc, SAMR_Q_DELETE_DOM_ALIAS * q_u,
+ prs_struct *ps, int depth)
+{
+ if (q_u == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "samr_io_q_delete_alias");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!smb_io_pol_hnd("alias_pol", &q_u->alias_pol, ps, depth))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+BOOL samr_io_r_delete_alias(const char *desc, SAMR_R_DELETE_DOM_ALIAS * r_u,
+ prs_struct *ps, int depth)
+{
+ if (r_u == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "samr_io_r_delete_alias");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!smb_io_pol_hnd("pol", &r_u->pol, ps, depth))
+ return False;
+ if(!prs_ntstatus("status", ps, depth, &r_u->status))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+inits a SAMR_Q_CREATE_DOM_ALIAS structure.
+********************************************************************/
+
+void init_samr_q_create_dom_alias(SAMR_Q_CREATE_DOM_ALIAS * q_u,
+ POLICY_HND *hnd, const char *acct_desc)
+{
+ DEBUG(5, ("init_samr_q_create_dom_alias\n"));
+
+ q_u->dom_pol = *hnd;
+
+ init_unistr2(&q_u->uni_acct_desc, acct_desc, UNI_FLAGS_NONE);
+ init_uni_hdr(&q_u->hdr_acct_desc, &q_u->uni_acct_desc);
+
+ q_u->access_mask = MAXIMUM_ALLOWED_ACCESS;
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+BOOL samr_io_q_create_dom_alias(const char *desc, SAMR_Q_CREATE_DOM_ALIAS * q_u,
+ prs_struct *ps, int depth)
+{
+ if (q_u == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "samr_io_q_create_dom_alias");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!smb_io_pol_hnd("dom_pol", &q_u->dom_pol, ps, depth))
+ return False;
+
+ if(!smb_io_unihdr("hdr_acct_desc", &q_u->hdr_acct_desc, ps, depth))
+ return False;
+ if(!smb_io_unistr2("uni_acct_desc", &q_u->uni_acct_desc,
+ q_u->hdr_acct_desc.buffer, ps, depth))
+ return False;
+
+ if(!prs_align(ps))
+ return False;
+ if(!prs_uint32("access_mask", ps, depth, &q_u->access_mask))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+BOOL samr_io_r_create_dom_alias(const char *desc, SAMR_R_CREATE_DOM_ALIAS * r_u,
+ prs_struct *ps, int depth)
+{
+ if (r_u == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "samr_io_r_create_dom_alias");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!smb_io_pol_hnd("alias_pol", &r_u->alias_pol, ps, depth))
+ return False;
+
+ if(!prs_uint32("rid", ps, depth, &r_u->rid))
+ return False;
+
+ if(!prs_ntstatus("status", ps, depth, &r_u->status))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+inits a SAMR_Q_ADD_ALIASMEM structure.
+********************************************************************/
+
+void init_samr_q_add_aliasmem(SAMR_Q_ADD_ALIASMEM * q_u, POLICY_HND *hnd,
+ DOM_SID *sid)
+{
+ DEBUG(5, ("init_samr_q_add_aliasmem\n"));
+
+ q_u->alias_pol = *hnd;
+ init_dom_sid2(&q_u->sid, sid);
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+BOOL samr_io_q_add_aliasmem(const char *desc, SAMR_Q_ADD_ALIASMEM * q_u,
+ prs_struct *ps, int depth)
+{
+ if (q_u == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "samr_io_q_add_aliasmem");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!smb_io_pol_hnd("alias_pol", &q_u->alias_pol, ps, depth))
+ return False;
+ if(!smb_io_dom_sid2("sid ", &q_u->sid, ps, depth))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+BOOL samr_io_r_add_aliasmem(const char *desc, SAMR_R_ADD_ALIASMEM * r_u,
+ prs_struct *ps, int depth)
+{
+ if (r_u == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "samr_io_r_add_aliasmem");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_ntstatus("status", ps, depth, &r_u->status))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+inits a SAMR_Q_DEL_ALIASMEM structure.
+********************************************************************/
+
+void init_samr_q_del_aliasmem(SAMR_Q_DEL_ALIASMEM * q_u, POLICY_HND *hnd,
+ DOM_SID *sid)
+{
+ DEBUG(5, ("init_samr_q_del_aliasmem\n"));
+
+ q_u->alias_pol = *hnd;
+ init_dom_sid2(&q_u->sid, sid);
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+BOOL samr_io_q_del_aliasmem(const char *desc, SAMR_Q_DEL_ALIASMEM * q_u,
+ prs_struct *ps, int depth)
+{
+ if (q_u == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "samr_io_q_del_aliasmem");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!smb_io_pol_hnd("alias_pol", &q_u->alias_pol, ps, depth))
+ return False;
+ if(!smb_io_dom_sid2("sid ", &q_u->sid, ps, depth))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+BOOL samr_io_r_del_aliasmem(const char *desc, SAMR_R_DEL_ALIASMEM * r_u,
+ prs_struct *ps, int depth)
+{
+ if (r_u == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "samr_io_r_del_aliasmem");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_ntstatus("status", ps, depth, &r_u->status))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+inits a SAMR_Q_DELETE_DOM_ALIAS structure.
+********************************************************************/
+
+void init_samr_q_delete_dom_alias(SAMR_Q_DELETE_DOM_ALIAS * q_c,
+ POLICY_HND *hnd)
+{
+ DEBUG(5, ("init_samr_q_delete_dom_alias\n"));
+
+ q_c->alias_pol = *hnd;
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+BOOL samr_io_q_delete_dom_alias(const char *desc, SAMR_Q_DELETE_DOM_ALIAS * q_u,
+ prs_struct *ps, int depth)
+{
+ if (q_u == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "samr_io_q_delete_dom_alias");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!smb_io_pol_hnd("alias_pol", &q_u->alias_pol, ps, depth))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+inits a SAMR_R_DELETE_DOM_ALIAS structure.
+********************************************************************/
+
+void init_samr_r_delete_dom_alias(SAMR_R_DELETE_DOM_ALIAS * r_u,
+ NTSTATUS status)
+{
+ DEBUG(5, ("init_samr_r_delete_dom_alias\n"));
+
+ r_u->status = status;
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+BOOL samr_io_r_delete_dom_alias(const char *desc, SAMR_R_DELETE_DOM_ALIAS * r_u,
+ prs_struct *ps, int depth)
+{
+ if (r_u == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "samr_io_r_delete_dom_alias");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_ntstatus("status", ps, depth, &r_u->status))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+inits a SAMR_Q_QUERY_ALIASMEM structure.
+********************************************************************/
+
+void init_samr_q_query_aliasmem(SAMR_Q_QUERY_ALIASMEM * q_c,
+ POLICY_HND *hnd)
+{
+ DEBUG(5, ("init_samr_q_query_aliasmem\n"));
+
+ q_c->alias_pol = *hnd;
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+BOOL samr_io_q_query_aliasmem(const char *desc, SAMR_Q_QUERY_ALIASMEM * q_u,
+ prs_struct *ps, int depth)
+{
+ if (q_u == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "samr_io_q_query_aliasmem");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!smb_io_pol_hnd("alias_pol", &q_u->alias_pol, ps, depth))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+inits a SAMR_R_QUERY_ALIASMEM structure.
+********************************************************************/
+
+void init_samr_r_query_aliasmem(SAMR_R_QUERY_ALIASMEM * r_u,
+ uint32 num_sids, DOM_SID2 * sid,
+ NTSTATUS status)
+{
+ DEBUG(5, ("init_samr_r_query_aliasmem\n"));
+
+ if (NT_STATUS_IS_OK(status)) {
+ r_u->num_sids = num_sids;
+ r_u->ptr = (num_sids != 0) ? 1 : 0;
+ r_u->num_sids1 = num_sids;
+
+ r_u->sid = sid;
+ } else {
+ r_u->ptr = 0;
+ r_u->num_sids = 0;
+ }
+
+ r_u->status = status;
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+BOOL samr_io_r_query_aliasmem(const char *desc, SAMR_R_QUERY_ALIASMEM * r_u,
+ prs_struct *ps, int depth)
+{
+ uint32 i;
+
+ if (r_u == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "samr_io_r_query_aliasmem");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("num_sids ", ps, depth, &r_u->num_sids))
+ return False;
+ if(!prs_uint32("ptr", ps, depth, &r_u->ptr))
+ return False;
+
+ if (r_u->ptr != 0 && r_u->num_sids != 0) {
+ uint32 *ptr_sid = NULL;
+
+ if(!prs_uint32("num_sids1", ps, depth, &r_u->num_sids1))
+ return False;
+
+ ptr_sid = talloc(ps->mem_ctx, sizeof(uint32) * r_u->num_sids1);
+ if (!ptr_sid) {
+ return False;
+ }
+
+ for (i = 0; i < r_u->num_sids1; i++) {
+ ptr_sid[i] = 1;
+ if(!prs_uint32("ptr_sid", ps, depth, &ptr_sid[i]))
+ return False;
+ }
+
+ if (UNMARSHALLING(ps)) {
+ r_u->sid = talloc(ps->mem_ctx, r_u->num_sids1 * sizeof(DOM_SID2));
+ }
+
+ for (i = 0; i < r_u->num_sids1; i++) {
+ if (ptr_sid[i] != 0) {
+ if(!smb_io_dom_sid2("sid", &r_u->sid[i], ps, depth))
+ return False;
+ }
+ }
+ }
+
+ if(!prs_align(ps))
+ return False;
+ if(!prs_ntstatus("status", ps, depth, &r_u->status))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+inits a SAMR_Q_LOOKUP_NAMES structure.
+********************************************************************/
+
+NTSTATUS init_samr_q_lookup_names(TALLOC_CTX *ctx, SAMR_Q_LOOKUP_NAMES * q_u,
+ POLICY_HND *pol, uint32 flags,
+ uint32 num_names, const char **name)
+{
+ uint32 i;
+
+ DEBUG(5, ("init_samr_q_lookup_names\n"));
+
+ q_u->pol = *pol;
+
+ q_u->num_names1 = num_names;
+ q_u->flags = flags;
+ q_u->ptr = 0;
+ q_u->num_names2 = num_names;
+
+ if (!(q_u->hdr_name = (UNIHDR *)talloc_zero(ctx, num_names * sizeof(UNIHDR))))
+ return NT_STATUS_NO_MEMORY;
+
+ if (!(q_u->uni_name = (UNISTR2 *)talloc_zero(ctx, num_names * sizeof(UNISTR2))))
+ return NT_STATUS_NO_MEMORY;
+
+ for (i = 0; i < num_names; i++) {
+ init_unistr2(&q_u->uni_name[i], name[i], UNI_FLAGS_NONE); /* unicode string for machine account */
+ init_uni_hdr(&q_u->hdr_name[i], &q_u->uni_name[i]); /* unicode header for user_name */
+ }
+
+ return NT_STATUS_OK;
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+BOOL samr_io_q_lookup_names(const char *desc, SAMR_Q_LOOKUP_NAMES * q_u,
+ prs_struct *ps, int depth)
+{
+ uint32 i;
+
+ if (q_u == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "samr_io_q_lookup_names");
+ depth++;
+
+ if (UNMARSHALLING(ps))
+ ZERO_STRUCTP(q_u);
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!smb_io_pol_hnd("pol", &q_u->pol, ps, depth))
+ return False;
+
+ if(!prs_uint32("num_names1", ps, depth, &q_u->num_names1))
+ return False;
+ if(!prs_uint32("flags ", ps, depth, &q_u->flags))
+ return False;
+ if(!prs_uint32("ptr ", ps, depth, &q_u->ptr))
+ return False;
+ if(!prs_uint32("num_names2", ps, depth, &q_u->num_names2))
+ return False;
+
+ if (UNMARSHALLING(ps) && (q_u->num_names2 != 0)) {
+ q_u->hdr_name = (UNIHDR *)prs_alloc_mem(ps, sizeof(UNIHDR) *
+ q_u->num_names2);
+ q_u->uni_name = (UNISTR2 *)prs_alloc_mem(ps, sizeof(UNISTR2) *
+ q_u->num_names2);
+ if (!q_u->hdr_name || !q_u->uni_name)
+ return False;
+ }
+
+ for (i = 0; i < q_u->num_names2; i++) {
+ if(!smb_io_unihdr("", &q_u->hdr_name[i], ps, depth))
+ return False;
+ }
+
+ for (i = 0; i < q_u->num_names2; i++) {
+ if(!smb_io_unistr2("", &q_u->uni_name[i], q_u->hdr_name[i].buffer, ps, depth))
+ return False;
+ }
+
+ return True;
+}
+
+/*******************************************************************
+inits a SAMR_R_LOOKUP_NAMES structure.
+********************************************************************/
+
+NTSTATUS init_samr_r_lookup_names(TALLOC_CTX *ctx, SAMR_R_LOOKUP_NAMES * r_u,
+ uint32 num_rids,
+ uint32 *rid, uint32 *type,
+ NTSTATUS status)
+{
+ DEBUG(5, ("init_samr_r_lookup_names\n"));
+
+ if (NT_STATUS_IS_OK(status) && (num_rids != 0)) {
+ uint32 i;
+
+ r_u->num_types1 = num_rids;
+ r_u->ptr_types = 1;
+ r_u->num_types2 = num_rids;
+
+ r_u->num_rids1 = num_rids;
+ r_u->ptr_rids = 1;
+ r_u->num_rids2 = num_rids;
+
+ if (!(r_u->rids = (uint32 *)talloc_zero(ctx, sizeof(uint32)*num_rids)))
+ return NT_STATUS_NO_MEMORY;
+ if (!(r_u->types = (uint32 *)talloc_zero(ctx, sizeof(uint32)*num_rids)))
+ return NT_STATUS_NO_MEMORY;
+
+ if (!r_u->rids || !r_u->types)
+ goto empty;
+
+ for (i = 0; i < num_rids; i++) {
+ r_u->rids[i] = rid[i];
+ r_u->types[i] = type[i];
+ }
+ } else {
+
+ empty:
+ r_u->num_types1 = 0;
+ r_u->ptr_types = 0;
+ r_u->num_types2 = 0;
+
+ r_u->num_rids1 = 0;
+ r_u->ptr_rids = 0;
+ r_u->num_rids2 = 0;
+
+ r_u->rids = NULL;
+ r_u->types = NULL;
+ }
+
+ r_u->status = status;
+
+ return NT_STATUS_OK;
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+BOOL samr_io_r_lookup_names(const char *desc, SAMR_R_LOOKUP_NAMES * r_u,
+ prs_struct *ps, int depth)
+{
+ uint32 i;
+ fstring tmp;
+
+ if (r_u == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "samr_io_r_lookup_names");
+ depth++;
+
+ if (UNMARSHALLING(ps))
+ ZERO_STRUCTP(r_u);
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("num_rids1", ps, depth, &r_u->num_rids1))
+ return False;
+ if(!prs_uint32("ptr_rids ", ps, depth, &r_u->ptr_rids))
+ return False;
+
+ if (r_u->ptr_rids != 0) {
+ if(!prs_uint32("num_rids2", ps, depth, &r_u->num_rids2))
+ return False;
+
+ if (r_u->num_rids2 != r_u->num_rids1) {
+ /* RPC fault */
+ return False;
+ }
+
+ if (UNMARSHALLING(ps))
+ r_u->rids = (uint32 *)prs_alloc_mem(ps, sizeof(uint32)*r_u->num_rids2);
+
+ if (!r_u->rids) {
+ DEBUG(0, ("NULL rids in samr_io_r_lookup_names\n"));
+ return False;
+ }
+
+ for (i = 0; i < r_u->num_rids2; i++) {
+ slprintf(tmp, sizeof(tmp) - 1, "rid[%02d] ", i);
+ if(!prs_uint32(tmp, ps, depth, &r_u->rids[i]))
+ return False;
+ }
+ }
+
+ if(!prs_uint32("num_types1", ps, depth, &r_u->num_types1))
+ return False;
+ if(!prs_uint32("ptr_types ", ps, depth, &r_u->ptr_types))
+ return False;
+
+ if (r_u->ptr_types != 0) {
+ if(!prs_uint32("num_types2", ps, depth, &r_u->num_types2))
+ return False;
+
+ if (r_u->num_types2 != r_u->num_types1) {
+ /* RPC fault */
+ return False;
+ }
+
+ if (UNMARSHALLING(ps))
+ r_u->types = (uint32 *)prs_alloc_mem(ps, sizeof(uint32)*r_u->num_types2);
+
+ if (!r_u->types) {
+ DEBUG(0, ("NULL types in samr_io_r_lookup_names\n"));
+ return False;
+ }
+
+ for (i = 0; i < r_u->num_types2; i++) {
+ slprintf(tmp, sizeof(tmp) - 1, "type[%02d] ", i);
+ if(!prs_uint32(tmp, ps, depth, &r_u->types[i]))
+ return False;
+ }
+ }
+
+ if(!prs_ntstatus("status", ps, depth, &r_u->status))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+inits a SAMR_Q_DELETE_DOM_USER structure.
+********************************************************************/
+
+void init_samr_q_delete_dom_user(SAMR_Q_DELETE_DOM_USER * q_c,
+ POLICY_HND *hnd)
+{
+ DEBUG(5, ("init_samr_q_delete_dom_user\n"));
+
+ q_c->user_pol = *hnd;
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+BOOL samr_io_q_delete_dom_user(const char *desc, SAMR_Q_DELETE_DOM_USER * q_u,
+ prs_struct *ps, int depth)
+{
+ if (q_u == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "samr_io_q_delete_dom_user");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!smb_io_pol_hnd("user_pol", &q_u->user_pol, ps, depth))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+BOOL samr_io_r_delete_dom_user(const char *desc, SAMR_R_DELETE_DOM_USER * r_u,
+ prs_struct *ps, int depth)
+{
+ if (r_u == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "samr_io_r_delete_dom_user");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!smb_io_pol_hnd("pol", &r_u->pol, ps, depth))
+ return False;
+ if(!prs_ntstatus("status", ps, depth, &r_u->status))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+void init_samr_q_open_user(SAMR_Q_OPEN_USER * q_u,
+ POLICY_HND *pol,
+ uint32 access_mask, uint32 rid)
+{
+ DEBUG(5, ("samr_init_samr_q_open_user\n"));
+
+ q_u->domain_pol = *pol;
+ q_u->access_mask = access_mask;
+ q_u->user_rid = rid;
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+BOOL samr_io_q_open_user(const char *desc, SAMR_Q_OPEN_USER * q_u,
+ prs_struct *ps, int depth)
+{
+ if (q_u == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "samr_io_q_open_user");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!smb_io_pol_hnd("domain_pol", &q_u->domain_pol, ps, depth))
+ return False;
+
+ if(!prs_uint32("access_mask", ps, depth, &q_u->access_mask))
+ return False;
+ if(!prs_uint32("user_rid ", ps, depth, &q_u->user_rid))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+BOOL samr_io_r_open_user(const char *desc, SAMR_R_OPEN_USER * r_u,
+ prs_struct *ps, int depth)
+{
+ if (r_u == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "samr_io_r_open_user");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!smb_io_pol_hnd("user_pol", &r_u->user_pol, ps, depth))
+ return False;
+
+ if(!prs_ntstatus("status", ps, depth, &r_u->status))
+ return False;
+
+ return True;
+}
+
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+void init_samr_q_create_user(SAMR_Q_CREATE_USER * q_u,
+ POLICY_HND *pol,
+ const char *name,
+ uint32 acb_info, uint32 access_mask)
+{
+ DEBUG(5, ("samr_init_samr_q_create_user\n"));
+
+ q_u->domain_pol = *pol;
+
+ init_unistr2(&q_u->uni_name, name, UNI_FLAGS_NONE);
+ init_uni_hdr(&q_u->hdr_name, &q_u->uni_name);
+
+ q_u->acb_info = acb_info;
+ q_u->access_mask = access_mask;
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+BOOL samr_io_q_create_user(const char *desc, SAMR_Q_CREATE_USER * q_u,
+ prs_struct *ps, int depth)
+{
+ if (q_u == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "samr_io_q_create_user");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!smb_io_pol_hnd("domain_pol", &q_u->domain_pol, ps, depth))
+ return False;
+
+ if(!smb_io_unihdr("hdr_name", &q_u->hdr_name, ps, depth))
+ return False;
+ if(!smb_io_unistr2("uni_name", &q_u->uni_name, q_u->hdr_name.buffer, ps, depth))
+ return False;
+
+ if(!prs_align(ps))
+ return False;
+ if(!prs_uint32("acb_info ", ps, depth, &q_u->acb_info))
+ return False;
+ if(!prs_uint32("access_mask", ps, depth, &q_u->access_mask))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+BOOL samr_io_r_create_user(const char *desc, SAMR_R_CREATE_USER * r_u,
+ prs_struct *ps, int depth)
+{
+ if (r_u == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "samr_io_r_create_user");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!smb_io_pol_hnd("user_pol", &r_u->user_pol, ps, depth))
+ return False;
+
+ if(!prs_uint32("access_granted", ps, depth, &r_u->access_granted))
+ return False;
+ if(!prs_uint32("user_rid ", ps, depth, &r_u->user_rid))
+ return False;
+ if(!prs_ntstatus("status", ps, depth, &r_u->status))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+inits a SAMR_Q_QUERY_USERINFO structure.
+********************************************************************/
+
+void init_samr_q_query_userinfo(SAMR_Q_QUERY_USERINFO * q_u,
+ POLICY_HND *hnd, uint16 switch_value)
+{
+ DEBUG(5, ("init_samr_q_query_userinfo\n"));
+
+ q_u->pol = *hnd;
+ q_u->switch_value = switch_value;
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+BOOL samr_io_q_query_userinfo(const char *desc, SAMR_Q_QUERY_USERINFO * q_u,
+ prs_struct *ps, int depth)
+{
+ if (q_u == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "samr_io_q_query_userinfo");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!smb_io_pol_hnd("pol", &q_u->pol, ps, depth))
+ return False;
+
+ if(!prs_uint16("switch_value", ps, depth, &q_u->switch_value)) /* 0x0015 or 0x0011 */
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+reads or writes a LOGON_HRS structure.
+********************************************************************/
+
+static BOOL sam_io_logon_hrs(const char *desc, LOGON_HRS * hrs,
+ prs_struct *ps, int depth)
+{
+ if (hrs == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "sam_io_logon_hrs");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("len ", ps, depth, &hrs->len))
+ return False;
+
+ if (hrs->len > sizeof(hrs->hours)) {
+ DEBUG(3, ("sam_io_logon_hrs: truncating length from %d\n", hrs->len));
+ hrs->len = sizeof(hrs->hours);
+ }
+
+ if(!prs_uint8s(False, "hours", ps, depth, hrs->hours, hrs->len))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+inits a SAM_USER_INFO_12 structure.
+********************************************************************/
+
+void init_sam_user_info12(SAM_USER_INFO_12 * usr,
+ const uint8 lm_pwd[16], const uint8 nt_pwd[16])
+{
+ DEBUG(5, ("init_sam_user_info12\n"));
+
+ usr->lm_pwd_active =
+ memcpy(usr->lm_pwd, lm_pwd, sizeof(usr->lm_pwd)) ? 1 : 0;
+ usr->nt_pwd_active =
+ memcpy(usr->nt_pwd, nt_pwd, sizeof(usr->nt_pwd)) ? 1 : 0;
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+static BOOL sam_io_user_info12(const char *desc, SAM_USER_INFO_12 * u,
+ prs_struct *ps, int depth)
+{
+ if (u == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "samr_io_r_user_info12");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint8s(False, "lm_pwd", ps, depth, u->lm_pwd, sizeof(u->lm_pwd)))
+ return False;
+ if(!prs_uint8s(False, "nt_pwd", ps, depth, u->nt_pwd, sizeof(u->nt_pwd)))
+ return False;
+
+ if(!prs_uint8("lm_pwd_active", ps, depth, &u->lm_pwd_active))
+ return False;
+ if(!prs_uint8("nt_pwd_active", ps, depth, &u->nt_pwd_active))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+inits a SAM_USER_INFO_10 structure.
+********************************************************************/
+
+void init_sam_user_info10(SAM_USER_INFO_10 * usr, uint32 acb_info)
+{
+ DEBUG(5, ("init_sam_user_info10\n"));
+
+ usr->acb_info = acb_info;
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+static BOOL sam_io_user_info10(const char *desc, SAM_USER_INFO_10 * usr,
+ prs_struct *ps, int depth)
+{
+ if (usr == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "samr_io_r_user_info10");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("acb_info", ps, depth, &usr->acb_info))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+inits a SAM_USER_INFO_11 structure.
+********************************************************************/
+
+void init_sam_user_info11(SAM_USER_INFO_11 * usr,
+ NTTIME * expiry,
+ char *mach_acct,
+ uint32 rid_user, uint32 rid_group, uint16 acct_ctrl)
+{
+ DEBUG(5, ("init_sam_user_info11\n"));
+
+ memcpy(&usr->expiry, expiry, sizeof(usr->expiry)); /* expiry time or something? */
+ ZERO_STRUCT(usr->padding_1); /* 0 - padding 24 bytes */
+
+ usr->padding_2 = 0; /* 0 - padding 4 bytes */
+
+ usr->ptr_1 = 1; /* pointer */
+ ZERO_STRUCT(usr->padding_3); /* 0 - padding 32 bytes */
+ usr->padding_4 = 0; /* 0 - padding 4 bytes */
+
+ usr->ptr_2 = 1; /* pointer */
+ usr->padding_5 = 0; /* 0 - padding 4 bytes */
+
+ usr->ptr_3 = 1; /* pointer */
+ ZERO_STRUCT(usr->padding_6); /* 0 - padding 32 bytes */
+
+ usr->rid_user = rid_user;
+ usr->rid_group = rid_group;
+
+ usr->acct_ctrl = acct_ctrl;
+ usr->unknown_3 = 0x0000;
+
+ usr->unknown_4 = 0x003f; /* 0x003f - 16 bit unknown */
+ usr->unknown_5 = 0x003c; /* 0x003c - 16 bit unknown */
+
+ ZERO_STRUCT(usr->padding_7); /* 0 - padding 16 bytes */
+ usr->padding_8 = 0; /* 0 - padding 4 bytes */
+
+ init_unistr2(&usr->uni_mach_acct, mach_acct, UNI_FLAGS_NONE); /* unicode string for machine account */
+ init_uni_hdr(&usr->hdr_mach_acct, &usr->uni_mach_acct); /* unicode header for machine account */
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+static BOOL sam_io_user_info11(const char *desc, SAM_USER_INFO_11 * usr,
+ prs_struct *ps, int depth)
+{
+ if (usr == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "samr_io_r_unknown_11");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint8s(False, "padding_0", ps, depth, usr->padding_0, sizeof(usr->padding_0)))
+ return False;
+
+ if(!smb_io_time("time", &usr->expiry, ps, depth))
+ return False;
+
+ if(!prs_uint8s(False, "padding_1", ps, depth, usr->padding_1, sizeof(usr->padding_1)))
+ return False;
+
+ if(!smb_io_unihdr("unihdr", &usr->hdr_mach_acct, ps, depth))
+ return False;
+
+ if(!prs_uint32("padding_2", ps, depth, &usr->padding_2))
+ return False;
+
+ if(!prs_uint32("ptr_1 ", ps, depth, &usr->ptr_1))
+ return False;
+ if(!prs_uint8s(False, "padding_3", ps, depth, usr->padding_3, sizeof(usr->padding_3)))
+ return False;
+
+ if(!prs_uint32("padding_4", ps, depth, &usr->padding_4))
+ return False;
+
+ if(!prs_uint32("ptr_2 ", ps, depth, &usr->ptr_2))
+ return False;
+ if(!prs_uint32("padding_5", ps, depth, &usr->padding_5))
+ return False;
+
+ if(!prs_uint32("ptr_3 ", ps, depth, &usr->ptr_3))
+ return False;
+ if(!prs_uint8s(False, "padding_6", ps, depth, usr->padding_6,sizeof(usr->padding_6)))
+ return False;
+
+ if(!prs_uint32("rid_user ", ps, depth, &usr->rid_user))
+ return False;
+ if(!prs_uint32("rid_group", ps, depth, &usr->rid_group))
+ return False;
+ if(!prs_uint16("acct_ctrl", ps, depth, &usr->acct_ctrl))
+ return False;
+ if(!prs_uint16("unknown_3", ps, depth, &usr->unknown_3))
+ return False;
+ if(!prs_uint16("unknown_4", ps, depth, &usr->unknown_4))
+ return False;
+ if(!prs_uint16("unknown_5", ps, depth, &usr->unknown_5))
+ return False;
+
+ if(!prs_uint8s(False, "padding_7", ps, depth, usr->padding_7, sizeof(usr->padding_7)))
+ return False;
+
+ if(!prs_uint32("padding_8", ps, depth, &(usr->padding_8)))
+ return False;
+
+ if(!smb_io_unistr2("unistr2", &usr->uni_mach_acct, True, ps, depth))
+ return False;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint8s(False, "padding_9", ps, depth, usr->padding_9, sizeof(usr->padding_9)))
+ return False;
+
+ return True;
+}
+
+/*************************************************************************
+ init_sam_user_infoa
+ *************************************************************************/
+
+void init_sam_user_info24(SAM_USER_INFO_24 * usr, char newpass[516], uint16 pw_len)
+{
+ DEBUG(10, ("init_sam_user_info24:\n"));
+ memcpy(usr->pass, newpass, sizeof(usr->pass));
+ usr->pw_len = pw_len;
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+static BOOL sam_io_user_info24(const char *desc, SAM_USER_INFO_24 * usr,
+ prs_struct *ps, int depth)
+{
+ if (usr == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "sam_io_user_info24");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint8s(False, "password", ps, depth, usr->pass,
+ sizeof(usr->pass)))
+ return False;
+
+ if (MARSHALLING(ps) && (usr->pw_len != 0)) {
+ if (!prs_uint16("pw_len", ps, depth, &usr->pw_len))
+ return False;
+ }
+ if(!prs_align(ps))
+ return False;
+
+ return True;
+}
+
+/*************************************************************************
+ init_sam_user_info23
+
+ unknown_6 = 0x0000 04ec
+
+ *************************************************************************/
+
+void init_sam_user_info23W(SAM_USER_INFO_23 * usr, NTTIME * logon_time, /* all zeros */
+ NTTIME * logoff_time, /* all zeros */
+ NTTIME * kickoff_time, /* all zeros */
+ NTTIME * pass_last_set_time, /* all zeros */
+ NTTIME * pass_can_change_time, /* all zeros */
+ NTTIME * pass_must_change_time, /* all zeros */
+ UNISTR2 *user_name,
+ UNISTR2 *full_name,
+ UNISTR2 *home_dir,
+ UNISTR2 *dir_drive,
+ UNISTR2 *log_scr,
+ UNISTR2 *prof_path,
+ UNISTR2 *desc,
+ UNISTR2 *wkstas,
+ UNISTR2 *unk_str,
+ UNISTR2 *mung_dial,
+ uint32 user_rid, /* 0x0000 0000 */
+ uint32 group_rid,
+ uint32 acb_info,
+ uint32 fields_present,
+ uint16 logon_divs,
+ LOGON_HRS * hrs,
+ uint16 bad_password_count,
+ uint16 logon_count,
+ char newpass[516], uint32 unknown_6)
+{
+ usr->logon_time = *logon_time; /* all zeros */
+ usr->logoff_time = *logoff_time; /* all zeros */
+ usr->kickoff_time = *kickoff_time; /* all zeros */
+ usr->pass_last_set_time = *pass_last_set_time; /* all zeros */
+ usr->pass_can_change_time = *pass_can_change_time; /* all zeros */
+ usr->pass_must_change_time = *pass_must_change_time; /* all zeros */
+
+ ZERO_STRUCT(usr->nt_pwd);
+ ZERO_STRUCT(usr->lm_pwd);
+
+ usr->user_rid = user_rid; /* 0x0000 0000 */
+ usr->group_rid = group_rid;
+ usr->acb_info = acb_info;
+ usr->fields_present = fields_present; /* 09f8 27fa */
+
+ usr->logon_divs = logon_divs; /* should be 168 (hours/week) */
+ usr->ptr_logon_hrs = hrs ? 1 : 0;
+
+ if (nt_time_is_zero(pass_must_change_time)) {
+ usr->passmustchange=PASS_MUST_CHANGE_AT_NEXT_LOGON;
+ } else {
+ usr->passmustchange=0;
+ }
+
+ ZERO_STRUCT(usr->padding1);
+ ZERO_STRUCT(usr->padding2);
+
+ usr->bad_password_count = bad_password_count;
+ usr->logon_count = logon_count;
+
+ memcpy(usr->pass, newpass, sizeof(usr->pass));
+
+ copy_unistr2(&usr->uni_user_name, user_name);
+ init_uni_hdr(&usr->hdr_user_name, &usr->uni_user_name);
+
+ copy_unistr2(&usr->uni_full_name, full_name);
+ init_uni_hdr(&usr->hdr_full_name, &usr->uni_full_name);
+
+ copy_unistr2(&usr->uni_home_dir, home_dir);
+ init_uni_hdr(&usr->hdr_home_dir, &usr->uni_home_dir);
+
+ copy_unistr2(&usr->uni_dir_drive, dir_drive);
+ init_uni_hdr(&usr->hdr_dir_drive, &usr->uni_dir_drive);
+
+ copy_unistr2(&usr->uni_logon_script, log_scr);
+ init_uni_hdr(&usr->hdr_logon_script, &usr->uni_logon_script);
+
+ copy_unistr2(&usr->uni_profile_path, prof_path);
+ init_uni_hdr(&usr->hdr_profile_path, &usr->uni_profile_path);
+
+ copy_unistr2(&usr->uni_acct_desc, desc);
+ init_uni_hdr(&usr->hdr_acct_desc, &usr->uni_acct_desc);
+
+ copy_unistr2(&usr->uni_workstations, wkstas);
+ init_uni_hdr(&usr->hdr_workstations, &usr->uni_workstations);
+
+ copy_unistr2(&usr->uni_unknown_str, unk_str);
+ init_uni_hdr(&usr->hdr_unknown_str, &usr->uni_unknown_str);
+
+ copy_unistr2(&usr->uni_munged_dial, mung_dial);
+ init_uni_hdr(&usr->hdr_munged_dial, &usr->uni_munged_dial);
+
+ usr->unknown_6 = unknown_6; /* 0x0000 04ec */
+ usr->padding4 = 0;
+
+ memcpy(&usr->logon_hrs, hrs, sizeof(usr->logon_hrs));
+}
+
+/*************************************************************************
+ init_sam_user_info23
+
+ unknown_6 = 0x0000 04ec
+
+ *************************************************************************/
+
+void init_sam_user_info23A(SAM_USER_INFO_23 * usr, NTTIME * logon_time, /* all zeros */
+ NTTIME * logoff_time, /* all zeros */
+ NTTIME * kickoff_time, /* all zeros */
+ NTTIME * pass_last_set_time, /* all zeros */
+ NTTIME * pass_can_change_time, /* all zeros */
+ NTTIME * pass_must_change_time, /* all zeros */
+ char *user_name, /* NULL */
+ char *full_name,
+ char *home_dir, char *dir_drive, char *log_scr,
+ char *prof_path, const char *desc, char *wkstas,
+ char *unk_str, char *mung_dial, uint32 user_rid, /* 0x0000 0000 */
+ uint32 group_rid, uint32 acb_info,
+ uint32 fields_present, uint16 logon_divs,
+ LOGON_HRS * hrs, uint16 bad_password_count, uint16 logon_count,
+ char newpass[516], uint32 unknown_6)
+{
+ DATA_BLOB blob = base64_decode_data_blob(mung_dial);
+
+ usr->logon_time = *logon_time; /* all zeros */
+ usr->logoff_time = *logoff_time; /* all zeros */
+ usr->kickoff_time = *kickoff_time; /* all zeros */
+ usr->pass_last_set_time = *pass_last_set_time; /* all zeros */
+ usr->pass_can_change_time = *pass_can_change_time; /* all zeros */
+ usr->pass_must_change_time = *pass_must_change_time; /* all zeros */
+
+ ZERO_STRUCT(usr->nt_pwd);
+ ZERO_STRUCT(usr->lm_pwd);
+
+ usr->user_rid = user_rid; /* 0x0000 0000 */
+ usr->group_rid = group_rid;
+ usr->acb_info = acb_info;
+ usr->fields_present = fields_present; /* 09f8 27fa */
+
+ usr->logon_divs = logon_divs; /* should be 168 (hours/week) */
+ usr->ptr_logon_hrs = hrs ? 1 : 0;
+
+ if (nt_time_is_zero(pass_must_change_time)) {
+ usr->passmustchange=PASS_MUST_CHANGE_AT_NEXT_LOGON;
+ } else {
+ usr->passmustchange=0;
+ }
+
+ ZERO_STRUCT(usr->padding1);
+ ZERO_STRUCT(usr->padding2);
+
+ usr->bad_password_count = bad_password_count;
+ usr->logon_count = logon_count;
+
+ memcpy(usr->pass, newpass, sizeof(usr->pass));
+
+ init_unistr2(&usr->uni_user_name, user_name, UNI_FLAGS_NONE);
+ init_uni_hdr(&usr->hdr_user_name, &usr->uni_user_name);
+
+ init_unistr2(&usr->uni_full_name, full_name, UNI_FLAGS_NONE);
+ init_uni_hdr(&usr->hdr_full_name, &usr->uni_full_name);
+
+ init_unistr2(&usr->uni_home_dir, home_dir, UNI_FLAGS_NONE);
+ init_uni_hdr(&usr->hdr_home_dir, &usr->uni_home_dir);
+
+ init_unistr2(&usr->uni_dir_drive, dir_drive, UNI_FLAGS_NONE);
+ init_uni_hdr(&usr->hdr_dir_drive, &usr->uni_dir_drive);
+
+ init_unistr2(&usr->uni_logon_script, log_scr, UNI_FLAGS_NONE);
+ init_uni_hdr(&usr->hdr_logon_script, &usr->uni_logon_script);
+
+ init_unistr2(&usr->uni_profile_path, prof_path, UNI_FLAGS_NONE);
+ init_uni_hdr(&usr->hdr_profile_path, &usr->uni_profile_path);
+
+ init_unistr2(&usr->uni_acct_desc, desc, UNI_FLAGS_NONE);
+ init_uni_hdr(&usr->hdr_acct_desc, &usr->uni_acct_desc);
+
+ init_unistr2(&usr->uni_workstations, wkstas, UNI_FLAGS_NONE);
+ init_uni_hdr(&usr->hdr_workstations, &usr->uni_workstations);
+
+ init_unistr2(&usr->uni_unknown_str, unk_str, UNI_FLAGS_NONE);
+ init_uni_hdr(&usr->hdr_unknown_str, &usr->uni_unknown_str);
+
+ init_unistr2_from_datablob(&usr->uni_munged_dial, &blob);
+ init_uni_hdr(&usr->hdr_munged_dial, &usr->uni_munged_dial);
+
+ data_blob_free(&blob);
+
+ usr->unknown_6 = unknown_6; /* 0x0000 04ec */
+ usr->padding4 = 0;
+
+ memcpy(&usr->logon_hrs, hrs, sizeof(usr->logon_hrs));
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+static BOOL sam_io_user_info23(const char *desc, SAM_USER_INFO_23 * usr,
+ prs_struct *ps, int depth)
+{
+ if (usr == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "sam_io_user_info23");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!smb_io_time("logon_time ", &usr->logon_time, ps, depth))
+ return False;
+ if(!smb_io_time("logoff_time ", &usr->logoff_time, ps, depth))
+ return False;
+ if(!smb_io_time("kickoff_time ", &usr->kickoff_time, ps, depth))
+ return False;
+ if(!smb_io_time("pass_last_set_time ", &usr->pass_last_set_time, ps, depth))
+ return False;
+ if(!smb_io_time("pass_can_change_time ", &usr->pass_can_change_time, ps, depth))
+ return False;
+ if(!smb_io_time("pass_must_change_time", &usr->pass_must_change_time, ps, depth))
+ return False;
+
+ if(!smb_io_unihdr("hdr_user_name ", &usr->hdr_user_name, ps, depth)) /* username unicode string header */
+ return False;
+ if(!smb_io_unihdr("hdr_full_name ", &usr->hdr_full_name, ps, depth)) /* user's full name unicode string header */
+ return False;
+ if(!smb_io_unihdr("hdr_home_dir ", &usr->hdr_home_dir, ps, depth)) /* home directory unicode string header */
+ return False;
+ if(!smb_io_unihdr("hdr_dir_drive ", &usr->hdr_dir_drive, ps, depth)) /* home directory drive */
+ return False;
+ if(!smb_io_unihdr("hdr_logon_script", &usr->hdr_logon_script, ps, depth)) /* logon script unicode string header */
+ return False;
+ if(!smb_io_unihdr("hdr_profile_path", &usr->hdr_profile_path, ps, depth)) /* profile path unicode string header */
+ return False;
+ if(!smb_io_unihdr("hdr_acct_desc ", &usr->hdr_acct_desc, ps, depth)) /* account desc */
+ return False;
+ if(!smb_io_unihdr("hdr_workstations", &usr->hdr_workstations, ps, depth)) /* wkstas user can log on from */
+ return False;
+ if(!smb_io_unihdr("hdr_unknown_str ", &usr->hdr_unknown_str, ps, depth)) /* unknown string */
+ return False;
+ if(!smb_io_unihdr("hdr_munged_dial ", &usr->hdr_munged_dial, ps, depth)) /* wkstas user can log on from */
+ return False;
+
+ if(!prs_uint8s(False, "lm_pwd ", ps, depth, usr->lm_pwd, sizeof(usr->lm_pwd)))
+ return False;
+ if(!prs_uint8s(False, "nt_pwd ", ps, depth, usr->nt_pwd, sizeof(usr->nt_pwd)))
+ return False;
+
+ if(!prs_uint32("user_rid ", ps, depth, &usr->user_rid)) /* User ID */
+ return False;
+ if(!prs_uint32("group_rid ", ps, depth, &usr->group_rid)) /* Group ID */
+ return False;
+ if(!prs_uint32("acb_info ", ps, depth, &usr->acb_info))
+ return False;
+
+ if(!prs_uint32("fields_present ", ps, depth, &usr->fields_present))
+ return False;
+ if(!prs_uint16("logon_divs ", ps, depth, &usr->logon_divs)) /* logon divisions per week */
+ return False;
+ if(!prs_align(ps))
+ return False;
+ if(!prs_uint32("ptr_logon_hrs ", ps, depth, &usr->ptr_logon_hrs))
+ return False;
+
+ if(!prs_uint16("bad_password_count ", ps, depth, &usr->bad_password_count))
+ return False;
+ if(!prs_uint16("logon_count ", ps, depth, &usr->logon_count))
+ return False;
+
+ if(!prs_uint8s(False, "padding1 ", ps, depth, usr->padding1, sizeof(usr->padding1)))
+ return False;
+ if(!prs_uint8("passmustchange ", ps, depth, &usr->passmustchange))
+ return False;
+ if(!prs_uint8("padding2 ", ps, depth, &usr->padding2))
+ return False;
+
+
+ if(!prs_uint8s(False, "password ", ps, depth, usr->pass, sizeof(usr->pass)))
+ return False;
+
+ /* here begins pointed-to data */
+
+ if(!smb_io_unistr2("uni_user_name ", &usr->uni_user_name, usr->hdr_user_name.buffer, ps, depth)) /* username unicode string */
+ return False;
+
+ if(!smb_io_unistr2("uni_full_name ", &usr->uni_full_name, usr->hdr_full_name.buffer, ps, depth)) /* user's full name unicode string */
+ return False;
+
+ if(!smb_io_unistr2("uni_home_dir ", &usr->uni_home_dir, usr->hdr_home_dir.buffer, ps, depth)) /* home directory unicode string */
+ return False;
+
+ if(!smb_io_unistr2("uni_dir_drive ", &usr->uni_dir_drive, usr->hdr_dir_drive.buffer, ps, depth)) /* home directory drive unicode string */
+ return False;
+
+ if(!smb_io_unistr2("uni_logon_script", &usr->uni_logon_script, usr->hdr_logon_script.buffer, ps, depth)) /* logon script unicode string */
+ return False;
+
+ if(!smb_io_unistr2("uni_profile_path", &usr->uni_profile_path, usr->hdr_profile_path.buffer, ps, depth)) /* profile path unicode string */
+ return False;
+
+ if(!smb_io_unistr2("uni_acct_desc ", &usr->uni_acct_desc, usr->hdr_acct_desc.buffer, ps, depth)) /* user desc unicode string */
+ return False;
+
+ if(!smb_io_unistr2("uni_workstations", &usr->uni_workstations, usr->hdr_workstations.buffer, ps, depth)) /* worksations user can log on from */
+ return False;
+
+ if(!smb_io_unistr2("uni_unknown_str ", &usr->uni_unknown_str, usr->hdr_unknown_str.buffer, ps, depth)) /* unknown string */
+ return False;
+
+ if(!smb_io_unistr2("uni_munged_dial ", &usr->uni_munged_dial, usr->hdr_munged_dial.buffer, ps, depth))
+ return False;
+
+ /* ok, this is only guess-work (as usual) */
+ if (usr->ptr_logon_hrs) {
+ if(!prs_uint32("unknown_6 ", ps, depth, &usr->unknown_6))
+ return False;
+ if(!prs_uint32("padding4 ", ps, depth, &usr->padding4))
+ return False;
+ if(!sam_io_logon_hrs("logon_hrs", &usr->logon_hrs, ps, depth))
+ return False;
+ } else if (UNMARSHALLING(ps)) {
+ usr->unknown_6 = 0;
+ usr->padding4 = 0;
+ }
+
+ return True;
+}
+
+/*******************************************************************
+ reads or writes a structure.
+ NB. This structure is *definately* incorrect. It's my best guess
+ currently for W2K SP2. The password field is encrypted in a different
+ way than normal... And there are definately other problems. JRA.
+********************************************************************/
+
+static BOOL sam_io_user_info25(const char *desc, SAM_USER_INFO_25 * usr, prs_struct *ps, int depth)
+{
+ if (usr == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "sam_io_user_info25");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!smb_io_time("logon_time ", &usr->logon_time, ps, depth))
+ return False;
+ if(!smb_io_time("logoff_time ", &usr->logoff_time, ps, depth))
+ return False;
+ if(!smb_io_time("kickoff_time ", &usr->kickoff_time, ps, depth))
+ return False;
+ if(!smb_io_time("pass_last_set_time ", &usr->pass_last_set_time, ps, depth))
+ return False;
+ if(!smb_io_time("pass_can_change_time ", &usr->pass_can_change_time, ps, depth))
+ return False;
+ if(!smb_io_time("pass_must_change_time", &usr->pass_must_change_time, ps, depth))
+ return False;
+
+ if(!smb_io_unihdr("hdr_user_name ", &usr->hdr_user_name, ps, depth)) /* username unicode string header */
+ return False;
+ if(!smb_io_unihdr("hdr_full_name ", &usr->hdr_full_name, ps, depth)) /* user's full name unicode string header */
+ return False;
+ if(!smb_io_unihdr("hdr_home_dir ", &usr->hdr_home_dir, ps, depth)) /* home directory unicode string header */
+ return False;
+ if(!smb_io_unihdr("hdr_dir_drive ", &usr->hdr_dir_drive, ps, depth)) /* home directory drive */
+ return False;
+ if(!smb_io_unihdr("hdr_logon_script", &usr->hdr_logon_script, ps, depth)) /* logon script unicode string header */
+ return False;
+ if(!smb_io_unihdr("hdr_profile_path", &usr->hdr_profile_path, ps, depth)) /* profile path unicode string header */
+ return False;
+ if(!smb_io_unihdr("hdr_acct_desc ", &usr->hdr_acct_desc, ps, depth)) /* account desc */
+ return False;
+ if(!smb_io_unihdr("hdr_workstations", &usr->hdr_workstations, ps, depth)) /* wkstas user can log on from */
+ return False;
+ if(!smb_io_unihdr("hdr_unknown_str ", &usr->hdr_unknown_str, ps, depth)) /* unknown string */
+ return False;
+ if(!smb_io_unihdr("hdr_munged_dial ", &usr->hdr_munged_dial, ps, depth)) /* wkstas user can log on from */
+ return False;
+
+ if(!prs_uint8s(False, "lm_pwd ", ps, depth, usr->lm_pwd, sizeof(usr->lm_pwd)))
+ return False;
+ if(!prs_uint8s(False, "nt_pwd ", ps, depth, usr->nt_pwd, sizeof(usr->nt_pwd)))
+ return False;
+
+ if(!prs_uint32("user_rid ", ps, depth, &usr->user_rid)) /* User ID */
+ return False;
+ if(!prs_uint32("group_rid ", ps, depth, &usr->group_rid)) /* Group ID */
+ return False;
+ if(!prs_uint32("acb_info ", ps, depth, &usr->acb_info))
+ return False;
+
+ if(!prs_uint32s(False, "unknown_6 ", ps, depth, usr->unknown_6, 6))
+ return False;
+
+ if(!prs_uint8s(False, "password ", ps, depth, usr->pass, sizeof(usr->pass)))
+ return False;
+
+ /* here begins pointed-to data */
+
+ if(!smb_io_unistr2("uni_user_name ", &usr->uni_user_name, usr->hdr_user_name.buffer, ps, depth)) /* username unicode string */
+ return False;
+
+ if(!smb_io_unistr2("uni_full_name ", &usr->uni_full_name, usr->hdr_full_name.buffer, ps, depth)) /* user's full name unicode string */
+ return False;
+
+ if(!smb_io_unistr2("uni_home_dir ", &usr->uni_home_dir, usr->hdr_home_dir.buffer, ps, depth)) /* home directory unicode string */
+ return False;
+
+ if(!smb_io_unistr2("uni_dir_drive ", &usr->uni_dir_drive, usr->hdr_dir_drive.buffer, ps, depth)) /* home directory drive unicode string */
+ return False;
+
+ if(!smb_io_unistr2("uni_logon_script", &usr->uni_logon_script, usr->hdr_logon_script.buffer, ps, depth)) /* logon script unicode string */
+ return False;
+
+ if(!smb_io_unistr2("uni_profile_path", &usr->uni_profile_path, usr->hdr_profile_path.buffer, ps, depth)) /* profile path unicode string */
+ return False;
+
+ if(!smb_io_unistr2("uni_acct_desc ", &usr->uni_acct_desc, usr->hdr_acct_desc.buffer, ps, depth)) /* user desc unicode string */
+ return False;
+
+ if(!smb_io_unistr2("uni_workstations", &usr->uni_workstations, usr->hdr_workstations.buffer, ps, depth)) /* worksations user can log on from */
+ return False;
+
+ if(!smb_io_unistr2("uni_unknown_str ", &usr->uni_unknown_str, usr->hdr_unknown_str.buffer, ps, depth)) /* unknown string */
+ return False;
+
+ if(!smb_io_unistr2("uni_munged_dial ", &usr->uni_munged_dial, usr->hdr_munged_dial.buffer, ps, depth))
+ return False;
+
+#if 0 /* JRA - unknown... */
+ /* ok, this is only guess-work (as usual) */
+ if (usr->ptr_logon_hrs) {
+ if(!prs_uint32("unknown_6 ", ps, depth, &usr->unknown_6))
+ return False;
+ if(!prs_uint32("padding4 ", ps, depth, &usr->padding4))
+ return False;
+ if(!sam_io_logon_hrs("logon_hrs", &usr->logon_hrs, ps, depth))
+ return False;
+ } else if (UNMARSHALLING(ps)) {
+ usr->unknown_6 = 0;
+ usr->padding4 = 0;
+ }
+#endif
+
+ return True;
+}
+
+
+/*************************************************************************
+ init_sam_user_info21W
+
+ unknown_6 = 0x0000 04ec
+
+ *************************************************************************/
+
+void init_sam_user_info21W(SAM_USER_INFO_21 * usr,
+ NTTIME * logon_time,
+ NTTIME * logoff_time,
+ NTTIME * kickoff_time,
+ NTTIME * pass_last_set_time,
+ NTTIME * pass_can_change_time,
+ NTTIME * pass_must_change_time,
+ UNISTR2 *user_name,
+ UNISTR2 *full_name,
+ UNISTR2 *home_dir,
+ UNISTR2 *dir_drive,
+ UNISTR2 *log_scr,
+ UNISTR2 *prof_path,
+ UNISTR2 *desc,
+ UNISTR2 *wkstas,
+ UNISTR2 *unk_str,
+ UNISTR2 *mung_dial,
+ uchar lm_pwd[16],
+ uchar nt_pwd[16],
+ uint32 user_rid,
+ uint32 group_rid,
+ uint32 acb_info,
+ uint32 fields_present,
+ uint16 logon_divs,
+ LOGON_HRS * hrs,
+ uint16 bad_password_count,
+ uint16 logon_count,
+ uint32 unknown_6)
+{
+ usr->logon_time = *logon_time;
+ usr->logoff_time = *logoff_time;
+ usr->kickoff_time = *kickoff_time;
+ usr->pass_last_set_time = *pass_last_set_time;
+ usr->pass_can_change_time = *pass_can_change_time;
+ usr->pass_must_change_time = *pass_must_change_time;
+
+ memcpy(usr->lm_pwd, lm_pwd, sizeof(usr->lm_pwd));
+ memcpy(usr->nt_pwd, nt_pwd, sizeof(usr->nt_pwd));
+
+ usr->user_rid = user_rid;
+ usr->group_rid = group_rid;
+ usr->acb_info = acb_info;
+ usr->fields_present = fields_present; /* 0x00ff ffff */
+
+ usr->logon_divs = logon_divs; /* should be 168 (hours/week) */
+ usr->ptr_logon_hrs = hrs ? 1 : 0;
+ usr->bad_password_count = bad_password_count;
+ usr->logon_count = logon_count;
+
+ if (nt_time_is_zero(pass_must_change_time)) {
+ usr->passmustchange=PASS_MUST_CHANGE_AT_NEXT_LOGON;
+ } else {
+ usr->passmustchange=0;
+ }
+
+ ZERO_STRUCT(usr->padding1);
+ ZERO_STRUCT(usr->padding2);
+
+ copy_unistr2(&usr->uni_user_name, user_name);
+ init_uni_hdr(&usr->hdr_user_name, &usr->uni_user_name);
+
+ copy_unistr2(&usr->uni_full_name, full_name);
+ init_uni_hdr(&usr->hdr_full_name, &usr->uni_full_name);
+
+ copy_unistr2(&usr->uni_home_dir, home_dir);
+ init_uni_hdr(&usr->hdr_home_dir, &usr->uni_home_dir);
+
+ copy_unistr2(&usr->uni_dir_drive, dir_drive);
+ init_uni_hdr(&usr->hdr_dir_drive, &usr->uni_dir_drive);
+
+ copy_unistr2(&usr->uni_logon_script, log_scr);
+ init_uni_hdr(&usr->hdr_logon_script, &usr->uni_logon_script);
+
+ copy_unistr2(&usr->uni_profile_path, prof_path);
+ init_uni_hdr(&usr->hdr_profile_path, &usr->uni_profile_path);
+
+ copy_unistr2(&usr->uni_acct_desc, desc);
+ init_uni_hdr(&usr->hdr_acct_desc, &usr->uni_acct_desc);
+
+ copy_unistr2(&usr->uni_workstations, wkstas);
+ init_uni_hdr(&usr->hdr_workstations, &usr->uni_workstations);
+
+ copy_unistr2(&usr->uni_unknown_str, unk_str);
+ init_uni_hdr(&usr->hdr_unknown_str, &usr->uni_unknown_str);
+
+ copy_unistr2(&usr->uni_munged_dial, mung_dial);
+ init_uni_hdr(&usr->hdr_munged_dial, &usr->uni_munged_dial);
+
+ usr->unknown_6 = unknown_6; /* 0x0000 04ec */
+ usr->padding4 = 0;
+
+ memcpy(&usr->logon_hrs, hrs, sizeof(usr->logon_hrs));
+}
+
+/*************************************************************************
+ init_sam_user_info21
+
+ unknown_6 = 0x0000 04ec
+
+ *************************************************************************/
+
+NTSTATUS init_sam_user_info21A(SAM_USER_INFO_21 *usr, SAM_ACCOUNT *pw, DOM_SID *domain_sid)
+{
+ NTTIME logon_time, logoff_time, kickoff_time,
+ pass_last_set_time, pass_can_change_time,
+ pass_must_change_time;
+
+ const char* user_name = pdb_get_username(pw);
+ const char* full_name = pdb_get_fullname(pw);
+ const char* home_dir = pdb_get_homedir(pw);
+ const char* dir_drive = pdb_get_dir_drive(pw);
+ const char* logon_script = pdb_get_logon_script(pw);
+ const char* profile_path = pdb_get_profile_path(pw);
+ const char* description = pdb_get_acct_desc(pw);
+ const char* workstations = pdb_get_workstations(pw);
+ const char* munged_dial = pdb_get_munged_dial(pw);
+ DATA_BLOB munged_dial_blob;
+
+ uint32 user_rid;
+ const DOM_SID *user_sid;
+
+ uint32 group_rid;
+ const DOM_SID *group_sid;
+
+ if (munged_dial) {
+ munged_dial_blob = base64_decode_data_blob(munged_dial);
+ } else {
+ munged_dial_blob = data_blob(NULL, 0);
+ }
+
+ /* Create NTTIME structs */
+ unix_to_nt_time (&logon_time, pdb_get_logon_time(pw));
+ unix_to_nt_time (&logoff_time, pdb_get_logoff_time(pw));
+ unix_to_nt_time (&kickoff_time, pdb_get_kickoff_time(pw));
+ unix_to_nt_time (&pass_last_set_time, pdb_get_pass_last_set_time(pw));
+ unix_to_nt_time (&pass_can_change_time, pdb_get_pass_can_change_time(pw));
+ unix_to_nt_time (&pass_must_change_time,pdb_get_pass_must_change_time(pw));
+
+ /* structure assignment */
+ usr->logon_time = logon_time;
+ usr->logoff_time = logoff_time;
+ usr->kickoff_time = kickoff_time;
+ usr->pass_last_set_time = pass_last_set_time;
+ usr->pass_can_change_time = pass_can_change_time;
+ usr->pass_must_change_time = pass_must_change_time;
+
+ ZERO_STRUCT(usr->nt_pwd);
+ ZERO_STRUCT(usr->lm_pwd);
+
+ user_sid = pdb_get_user_sid(pw);
+
+ if (!sid_peek_check_rid(domain_sid, user_sid, &user_rid)) {
+ fstring user_sid_string;
+ fstring domain_sid_string;
+ DEBUG(0, ("init_sam_user_info_21A: User %s has SID %s, \nwhich conflicts with "
+ "the domain sid %s. Failing operation.\n",
+ user_name,
+ sid_to_string(user_sid_string, user_sid),
+ sid_to_string(domain_sid_string, domain_sid)));
+ data_blob_free(&munged_dial_blob);
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ group_sid = pdb_get_group_sid(pw);
+
+ if (!sid_peek_check_rid(domain_sid, group_sid, &group_rid)) {
+ fstring group_sid_string;
+ fstring domain_sid_string;
+ DEBUG(0, ("init_sam_user_info_21A: User %s has Primary Group SID %s, \n"
+ "which conflicts with the domain sid %s. Failing operation.\n",
+ user_name,
+ sid_to_string(group_sid_string, group_sid),
+ sid_to_string(domain_sid_string, domain_sid)));
+ data_blob_free(&munged_dial_blob);
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ usr->user_rid = user_rid;
+ usr->group_rid = group_rid;
+ usr->acb_info = pdb_get_acct_ctrl(pw);
+
+ /*
+ Look at a user on a real NT4 PDC with usrmgr, press
+ 'ok'. Then you will see that fields_present is set to
+ 0x08f827fa. Look at the user immediately after that again,
+ and you will see that 0x00fffff is returned. This solves
+ the problem that you get access denied after having looked
+ at the user.
+ -- Volker
+ */
+ usr->fields_present = pdb_build_fields_present(pw);
+
+ usr->logon_divs = pdb_get_logon_divs(pw);
+ usr->ptr_logon_hrs = pdb_get_hours(pw) ? 1 : 0;
+ usr->bad_password_count = pdb_get_bad_password_count(pw);
+ usr->logon_count = pdb_get_logon_count(pw);
+
+ if (pdb_get_pass_must_change_time(pw) == 0) {
+ usr->passmustchange=PASS_MUST_CHANGE_AT_NEXT_LOGON;
+ } else {
+ usr->passmustchange=0;
+ }
+
+ ZERO_STRUCT(usr->padding1);
+ ZERO_STRUCT(usr->padding2);
+
+ init_unistr2(&usr->uni_user_name, user_name, UNI_STR_TERMINATE);
+ init_uni_hdr(&usr->hdr_user_name, &usr->uni_user_name);
+
+ init_unistr2(&usr->uni_full_name, full_name, UNI_STR_TERMINATE);
+ init_uni_hdr(&usr->hdr_full_name, &usr->uni_full_name);
+
+ init_unistr2(&usr->uni_home_dir, home_dir, UNI_STR_TERMINATE);
+ init_uni_hdr(&usr->hdr_home_dir, &usr->uni_home_dir);
+
+ init_unistr2(&usr->uni_dir_drive, dir_drive, UNI_STR_TERMINATE);
+ init_uni_hdr(&usr->hdr_dir_drive, &usr->uni_dir_drive);
+
+ init_unistr2(&usr->uni_logon_script, logon_script, UNI_STR_TERMINATE);
+ init_uni_hdr(&usr->hdr_logon_script, &usr->uni_logon_script);
+
+ init_unistr2(&usr->uni_profile_path, profile_path, UNI_STR_TERMINATE);
+ init_uni_hdr(&usr->hdr_profile_path, &usr->uni_profile_path);
+
+ init_unistr2(&usr->uni_acct_desc, description, UNI_STR_TERMINATE);
+ init_uni_hdr(&usr->hdr_acct_desc, &usr->uni_acct_desc);
+
+ init_unistr2(&usr->uni_workstations, workstations, UNI_STR_TERMINATE);
+ init_uni_hdr(&usr->hdr_workstations, &usr->uni_workstations);
+
+ init_unistr2(&usr->uni_unknown_str, NULL, UNI_STR_TERMINATE);
+ init_uni_hdr(&usr->hdr_unknown_str, &usr->uni_unknown_str);
+
+ init_unistr2_from_datablob(&usr->uni_munged_dial, &munged_dial_blob);
+ init_uni_hdr(&usr->hdr_munged_dial, &usr->uni_munged_dial);
+ data_blob_free(&munged_dial_blob);
+
+ usr->unknown_6 = pdb_get_unknown_6(pw);
+ usr->padding4 = 0;
+
+ if (pdb_get_hours(pw)) {
+ usr->logon_hrs.len = pdb_get_hours_len(pw);
+ memcpy(&usr->logon_hrs.hours, pdb_get_hours(pw), MAX_HOURS_LEN);
+ } else
+ memset(&usr->logon_hrs, 0xff, sizeof(usr->logon_hrs));
+
+ return NT_STATUS_OK;
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+static BOOL sam_io_user_info21(const char *desc, SAM_USER_INFO_21 * usr,
+ prs_struct *ps, int depth)
+{
+ if (usr == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "sam_io_user_info21");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!smb_io_time("logon_time ", &usr->logon_time, ps, depth))
+ return False;
+ if(!smb_io_time("logoff_time ", &usr->logoff_time, ps, depth))
+ return False;
+ if(!smb_io_time("pass_last_set_time ", &usr->pass_last_set_time, ps,depth))
+ return False;
+ if(!smb_io_time("kickoff_time ", &usr->kickoff_time, ps, depth))
+ return False;
+ if(!smb_io_time("pass_can_change_time ", &usr->pass_can_change_time, ps,depth))
+ return False;
+ if(!smb_io_time("pass_must_change_time", &usr->pass_must_change_time, ps, depth))
+ return False;
+
+ if(!smb_io_unihdr("hdr_user_name ", &usr->hdr_user_name, ps, depth)) /* username unicode string header */
+ return False;
+ if(!smb_io_unihdr("hdr_full_name ", &usr->hdr_full_name, ps, depth)) /* user's full name unicode string header */
+ return False;
+ if(!smb_io_unihdr("hdr_home_dir ", &usr->hdr_home_dir, ps, depth)) /* home directory unicode string header */
+ return False;
+ if(!smb_io_unihdr("hdr_dir_drive ", &usr->hdr_dir_drive, ps, depth)) /* home directory drive */
+ return False;
+ if(!smb_io_unihdr("hdr_logon_script", &usr->hdr_logon_script, ps, depth)) /* logon script unicode string header */
+ return False;
+ if(!smb_io_unihdr("hdr_profile_path", &usr->hdr_profile_path, ps, depth)) /* profile path unicode string header */
+ return False;
+ if(!smb_io_unihdr("hdr_acct_desc ", &usr->hdr_acct_desc, ps, depth)) /* account desc */
+ return False;
+ if(!smb_io_unihdr("hdr_workstations", &usr->hdr_workstations, ps, depth)) /* wkstas user can log on from */
+ return False;
+ if(!smb_io_unihdr("hdr_unknown_str ", &usr->hdr_unknown_str, ps, depth)) /* unknown string */
+ return False;
+ if(!smb_io_unihdr("hdr_munged_dial ", &usr->hdr_munged_dial, ps, depth)) /* wkstas user can log on from */
+ return False;
+
+ if(!prs_uint8s(False, "lm_pwd ", ps, depth, usr->lm_pwd, sizeof(usr->lm_pwd)))
+ return False;
+ if(!prs_uint8s(False, "nt_pwd ", ps, depth, usr->nt_pwd, sizeof(usr->nt_pwd)))
+ return False;
+
+ if(!prs_uint32("user_rid ", ps, depth, &usr->user_rid)) /* User ID */
+ return False;
+ if(!prs_uint32("group_rid ", ps, depth, &usr->group_rid)) /* Group ID */
+ return False;
+ if(!prs_uint32("acb_info ", ps, depth, &usr->acb_info))
+ return False;
+
+ if(!prs_uint32("fields_present ", ps, depth, &usr->fields_present))
+ return False;
+ if(!prs_uint16("logon_divs ", ps, depth, &usr->logon_divs)) /* logon divisions per week */
+ return False;
+ if(!prs_align(ps))
+ return False;
+ if(!prs_uint32("ptr_logon_hrs ", ps, depth, &usr->ptr_logon_hrs))
+ return False;
+
+ if(!prs_uint16("bad_password_count ", ps, depth, &usr->bad_password_count))
+ return False;
+ if(!prs_uint16("logon_count ", ps, depth, &usr->logon_count))
+ return False;
+
+ if(!prs_uint8s(False, "padding1 ", ps, depth, usr->padding1, sizeof(usr->padding1)))
+ return False;
+ if(!prs_uint8("passmustchange ", ps, depth, &usr->passmustchange))
+ return False;
+ if(!prs_uint8("padding2 ", ps, depth, &usr->padding2))
+ return False;
+
+ /* here begins pointed-to data */
+
+ if(!smb_io_unistr2("uni_user_name ", &usr->uni_user_name,usr->hdr_user_name.buffer, ps, depth)) /* username unicode string */
+ return False;
+ if(!smb_io_unistr2("uni_full_name ", &usr->uni_full_name, usr->hdr_full_name.buffer, ps, depth)) /* user's full name unicode string */
+ return False;
+ if(!smb_io_unistr2("uni_home_dir ", &usr->uni_home_dir, usr->hdr_home_dir.buffer, ps, depth)) /* home directory unicode string */
+ return False;
+ if(!smb_io_unistr2("uni_dir_drive ", &usr->uni_dir_drive, usr->hdr_dir_drive.buffer, ps, depth)) /* home directory drive unicode string */
+ return False;
+ if(!smb_io_unistr2("uni_logon_script", &usr->uni_logon_script, usr->hdr_logon_script.buffer, ps, depth)) /* logon script unicode string */
+ return False;
+ if(!smb_io_unistr2("uni_profile_path", &usr->uni_profile_path, usr->hdr_profile_path.buffer, ps, depth)) /* profile path unicode string */
+ return False;
+ if(!smb_io_unistr2("uni_acct_desc ", &usr->uni_acct_desc, usr->hdr_acct_desc.buffer, ps, depth)) /* user desc unicode string */
+ return False;
+ if(!smb_io_unistr2("uni_workstations", &usr->uni_workstations, usr->hdr_workstations.buffer, ps, depth)) /* worksations user can log on from */
+ return False;
+ if(!smb_io_unistr2("uni_unknown_str ", &usr->uni_unknown_str, usr->hdr_unknown_str.buffer, ps, depth)) /* unknown string */
+ return False;
+ if(!smb_io_unistr2("uni_munged_dial ", &usr->uni_munged_dial,usr->hdr_munged_dial.buffer, ps, depth)) /* worksations user can log on from */
+ return False;
+
+ /* ok, this is only guess-work (as usual) */
+ if (usr->ptr_logon_hrs) {
+ if(!prs_align(ps))
+ return False;
+ if(!prs_uint32("unknown_6 ", ps, depth, &usr->unknown_6))
+ return False;
+ if(!prs_uint32("padding4 ", ps, depth, &usr->padding4))
+ return False;
+ if(!sam_io_logon_hrs("logon_hrs", &usr->logon_hrs, ps, depth))
+ return False;
+ } else if (UNMARSHALLING(ps)) {
+ usr->unknown_6 = 0;
+ usr->padding4 = 0;
+ }
+
+ return True;
+}
+
+void init_sam_user_info20A(SAM_USER_INFO_20 *usr, SAM_ACCOUNT *pw)
+{
+ const char *munged_dial = pdb_get_munged_dial(pw);
+ DATA_BLOB blob = base64_decode_data_blob(munged_dial);
+
+ init_unistr2_from_datablob(&usr->uni_munged_dial, &blob);
+ init_uni_hdr(&usr->hdr_munged_dial, &usr->uni_munged_dial);
+ data_blob_free(&blob);
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+static BOOL sam_io_user_info20(const char *desc, SAM_USER_INFO_20 *usr,
+ prs_struct *ps, int depth)
+{
+ if (usr == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "sam_io_user_info20");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!smb_io_unihdr("hdr_munged_dial ", &usr->hdr_munged_dial, ps, depth)) /* wkstas user can log on from */
+ return False;
+
+ if(!smb_io_unistr2("uni_munged_dial ", &usr->uni_munged_dial,usr->hdr_munged_dial.buffer, ps, depth)) /* worksations user can log on from */
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+inits a SAM_USERINFO_CTR structure.
+********************************************************************/
+
+NTSTATUS make_samr_userinfo_ctr_usr21(TALLOC_CTX *ctx, SAM_USERINFO_CTR * ctr,
+ uint16 switch_value,
+ SAM_USER_INFO_21 * usr)
+{
+ DEBUG(5, ("init_samr_userinfo_ctr\n"));
+
+ ctr->switch_value = switch_value;
+ ctr->info.id = NULL;
+
+ switch (switch_value) {
+ case 0x10:
+ ctr->info.id10 = (SAM_USER_INFO_10 *)talloc_zero(ctx,sizeof(SAM_USER_INFO_10));
+ if (ctr->info.id10 == NULL)
+ return NT_STATUS_NO_MEMORY;
+
+ init_sam_user_info10(ctr->info.id10, usr->acb_info);
+ break;
+#if 0
+/* whoops - got this wrong. i think. or don't understand what's happening. */
+ case 0x11:
+ {
+ NTTIME expire;
+ info = (void *)&id11;
+
+ expire.low = 0xffffffff;
+ expire.high = 0x7fffffff;
+
+ ctr->info.id = (SAM_USER_INFO_11 *) talloc_zero(ctx,sizeof(*ctr->info.id11));
+ init_sam_user_info11(ctr->info.id11, &expire,
+ "BROOKFIELDS$", /* name */
+ 0x03ef, /* user rid */
+ 0x201, /* group rid */
+ 0x0080); /* acb info */
+
+ break;
+ }
+#endif
+ case 0x12:
+ ctr->info.id12 = (SAM_USER_INFO_12 *)talloc_zero(ctx,sizeof(SAM_USER_INFO_12));
+ if (ctr->info.id12 == NULL)
+ return NT_STATUS_NO_MEMORY;
+
+ init_sam_user_info12(ctr->info.id12, usr->lm_pwd, usr->nt_pwd);
+ break;
+ case 21:
+ {
+ SAM_USER_INFO_21 *cusr;
+ cusr = (SAM_USER_INFO_21 *)talloc_zero(ctx,sizeof(SAM_USER_INFO_21));
+ ctr->info.id21 = cusr;
+ if (ctr->info.id21 == NULL)
+ return NT_STATUS_NO_MEMORY;
+ memcpy(cusr, usr, sizeof(*usr));
+ memset(cusr->lm_pwd, 0, sizeof(cusr->lm_pwd));
+ memset(cusr->nt_pwd, 0, sizeof(cusr->nt_pwd));
+ break;
+ }
+ default:
+ DEBUG(4,("make_samr_userinfo_ctr: unsupported info\n"));
+ return NT_STATUS_INVALID_INFO_CLASS;
+ }
+
+ return NT_STATUS_OK;
+}
+
+/*******************************************************************
+inits a SAM_USERINFO_CTR structure.
+********************************************************************/
+
+static void init_samr_userinfo_ctr(SAM_USERINFO_CTR * ctr, DATA_BLOB *sess_key,
+ uint16 switch_value, void *info)
+{
+ DEBUG(5, ("init_samr_userinfo_ctr\n"));
+
+ ctr->switch_value = switch_value;
+ ctr->info.id = info;
+
+ switch (switch_value) {
+ case 0x18:
+ SamOEMhashBlob(ctr->info.id24->pass, 516, sess_key);
+ dump_data(100, (char *)sess_key->data, sess_key->length);
+ dump_data(100, (char *)ctr->info.id24->pass, 516);
+ break;
+ case 0x17:
+ SamOEMhashBlob(ctr->info.id23->pass, 516, sess_key);
+ dump_data(100, (char *)sess_key->data, sess_key->length);
+ dump_data(100, (char *)ctr->info.id23->pass, 516);
+ break;
+ default:
+ DEBUG(4,("init_samr_userinfo_ctr: unsupported switch level\n"));
+ }
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+static BOOL samr_io_userinfo_ctr(const char *desc, SAM_USERINFO_CTR **ppctr,
+ prs_struct *ps, int depth)
+{
+ BOOL ret;
+ SAM_USERINFO_CTR *ctr;
+
+ prs_debug(ps, depth, desc, "samr_io_userinfo_ctr");
+ depth++;
+
+ if (UNMARSHALLING(ps)) {
+ ctr = (SAM_USERINFO_CTR *)prs_alloc_mem(ps,sizeof(SAM_USERINFO_CTR));
+ if (ctr == NULL)
+ return False;
+ *ppctr = ctr;
+ } else {
+ ctr = *ppctr;
+ }
+
+ /* lkclXXXX DO NOT ALIGN BEFORE READING SWITCH VALUE! */
+
+ if(!prs_uint16("switch_value", ps, depth, &ctr->switch_value))
+ return False;
+ if(!prs_align(ps))
+ return False;
+
+ ret = False;
+
+ switch (ctr->switch_value) {
+ case 0x10:
+ if (UNMARSHALLING(ps))
+ ctr->info.id10 = (SAM_USER_INFO_10 *)prs_alloc_mem(ps,sizeof(SAM_USER_INFO_10));
+ if (ctr->info.id10 == NULL) {
+ DEBUG(2,("samr_io_userinfo_ctr: info pointer not initialised\n"));
+ return False;
+ }
+ ret = sam_io_user_info10("", ctr->info.id10, ps, depth);
+ break;
+ case 0x11:
+ if (UNMARSHALLING(ps))
+ ctr->info.id11 = (SAM_USER_INFO_11 *)prs_alloc_mem(ps,sizeof(SAM_USER_INFO_11));
+
+ if (ctr->info.id11 == NULL) {
+ DEBUG(2,("samr_io_userinfo_ctr: info pointer not initialised\n"));
+ return False;
+ }
+ ret = sam_io_user_info11("", ctr->info.id11, ps, depth);
+ break;
+ case 0x12:
+ if (UNMARSHALLING(ps))
+ ctr->info.id12 = (SAM_USER_INFO_12 *)prs_alloc_mem(ps,sizeof(SAM_USER_INFO_12));
+
+ if (ctr->info.id12 == NULL) {
+ DEBUG(2,("samr_io_userinfo_ctr: info pointer not initialised\n"));
+ return False;
+ }
+ ret = sam_io_user_info12("", ctr->info.id12, ps, depth);
+ break;
+ case 20:
+ if (UNMARSHALLING(ps))
+ ctr->info.id20 = (SAM_USER_INFO_20 *)prs_alloc_mem(ps,sizeof(SAM_USER_INFO_20));
+
+ if (ctr->info.id20 == NULL) {
+ DEBUG(2,("samr_io_userinfo_ctr: info pointer not initialised\n"));
+ return False;
+ }
+ ret = sam_io_user_info20("", ctr->info.id20, ps, depth);
+ break;
+ case 21:
+ if (UNMARSHALLING(ps))
+ ctr->info.id21 = (SAM_USER_INFO_21 *)prs_alloc_mem(ps,sizeof(SAM_USER_INFO_21));
+
+ if (ctr->info.id21 == NULL) {
+ DEBUG(2,("samr_io_userinfo_ctr: info pointer not initialised\n"));
+ return False;
+ }
+ ret = sam_io_user_info21("", ctr->info.id21, ps, depth);
+ break;
+ case 23:
+ if (UNMARSHALLING(ps))
+ ctr->info.id23 = (SAM_USER_INFO_23 *)prs_alloc_mem(ps,sizeof(SAM_USER_INFO_23));
+
+ if (ctr->info.id23 == NULL) {
+ DEBUG(2,("samr_io_userinfo_ctr: info pointer not initialised\n"));
+ return False;
+ }
+ ret = sam_io_user_info23("", ctr->info.id23, ps, depth);
+ break;
+ case 24:
+ if (UNMARSHALLING(ps))
+ ctr->info.id24 = (SAM_USER_INFO_24 *)prs_alloc_mem(ps,sizeof(SAM_USER_INFO_24));
+
+ if (ctr->info.id24 == NULL) {
+ DEBUG(2,("samr_io_userinfo_ctr: info pointer not initialised\n"));
+ return False;
+ }
+ ret = sam_io_user_info24("", ctr->info.id24, ps, depth);
+ break;
+ case 25:
+ if (UNMARSHALLING(ps))
+ ctr->info.id25 = (SAM_USER_INFO_25 *)prs_alloc_mem(ps,sizeof(SAM_USER_INFO_25));
+
+ if (ctr->info.id25 == NULL) {
+ DEBUG(2,("samr_io_userinfo_ctr: info pointer not initialised\n"));
+ return False;
+ }
+ ret = sam_io_user_info25("", ctr->info.id25, ps, depth);
+ break;
+ default:
+ DEBUG(2, ("samr_io_userinfo_ctr: unknown switch level 0x%x\n", ctr->switch_value));
+ ret = False;
+ break;
+ }
+
+ return ret;
+}
+
+/*******************************************************************
+inits a SAMR_R_QUERY_USERINFO structure.
+********************************************************************/
+
+void init_samr_r_query_userinfo(SAMR_R_QUERY_USERINFO * r_u,
+ SAM_USERINFO_CTR * ctr, NTSTATUS status)
+{
+ DEBUG(5, ("init_samr_r_query_userinfo\n"));
+
+ r_u->ptr = 0;
+ r_u->ctr = NULL;
+
+ if (NT_STATUS_IS_OK(status)) {
+ r_u->ptr = 1;
+ r_u->ctr = ctr;
+ }
+
+ r_u->status = status; /* return status */
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+BOOL samr_io_r_query_userinfo(const char *desc, SAMR_R_QUERY_USERINFO * r_u,
+ prs_struct *ps, int depth)
+{
+ if (r_u == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "samr_io_r_query_userinfo");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("ptr", ps, depth, &r_u->ptr))
+ return False;
+
+ if (r_u->ptr != 0) {
+ if(!samr_io_userinfo_ctr("ctr", &r_u->ctr, ps, depth))
+ return False;
+ }
+
+ if(!prs_align(ps))
+ return False;
+ if(!prs_ntstatus("status", ps, depth, &r_u->status))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+inits a SAMR_Q_SET_USERINFO structure.
+********************************************************************/
+
+void init_samr_q_set_userinfo(SAMR_Q_SET_USERINFO * q_u,
+ POLICY_HND *hnd, DATA_BLOB *sess_key,
+ uint16 switch_value, void *info)
+{
+ DEBUG(5, ("init_samr_q_set_userinfo\n"));
+
+ q_u->pol = *hnd;
+ q_u->switch_value = switch_value;
+ init_samr_userinfo_ctr(q_u->ctr, sess_key, switch_value, info);
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+BOOL samr_io_q_set_userinfo(const char *desc, SAMR_Q_SET_USERINFO * q_u,
+ prs_struct *ps, int depth)
+{
+ if (q_u == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "samr_io_q_set_userinfo");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ smb_io_pol_hnd("pol", &(q_u->pol), ps, depth);
+
+ if(!prs_uint16("switch_value", ps, depth, &q_u->switch_value))
+ return False;
+ if(!samr_io_userinfo_ctr("ctr", &q_u->ctr, ps, depth))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+inits a SAMR_R_SET_USERINFO structure.
+********************************************************************/
+
+void init_samr_r_set_userinfo(SAMR_R_SET_USERINFO * r_u, NTSTATUS status)
+{
+ DEBUG(5, ("init_samr_r_set_userinfo\n"));
+
+ r_u->status = status; /* return status */
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+BOOL samr_io_r_set_userinfo(const char *desc, SAMR_R_SET_USERINFO * r_u,
+ prs_struct *ps, int depth)
+{
+ if (r_u == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "samr_io_r_set_userinfo");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_ntstatus("status", ps, depth, &r_u->status))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+inits a SAMR_Q_SET_USERINFO2 structure.
+********************************************************************/
+
+void init_samr_q_set_userinfo2(SAMR_Q_SET_USERINFO2 * q_u,
+ POLICY_HND *hnd, DATA_BLOB *sess_key,
+ uint16 switch_value, SAM_USERINFO_CTR * ctr)
+{
+ DEBUG(5, ("init_samr_q_set_userinfo2\n"));
+
+ q_u->pol = *hnd;
+ q_u->switch_value = switch_value;
+ q_u->ctr = ctr;
+
+ if (q_u->ctr != NULL)
+ q_u->ctr->switch_value = switch_value;
+
+ switch (switch_value) {
+ case 0x12:
+ SamOEMhashBlob(ctr->info.id12->lm_pwd, 16, sess_key);
+ SamOEMhashBlob(ctr->info.id12->nt_pwd, 16, sess_key);
+ dump_data(100, (char *)sess_key->data, sess_key->length);
+ dump_data(100, (char *)ctr->info.id12->lm_pwd, 16);
+ dump_data(100, (char *)ctr->info.id12->nt_pwd, 16);
+ break;
+ }
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+BOOL samr_io_q_set_userinfo2(const char *desc, SAMR_Q_SET_USERINFO2 * q_u,
+ prs_struct *ps, int depth)
+{
+ if (q_u == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "samr_io_q_set_userinfo2");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!smb_io_pol_hnd("pol", &q_u->pol, ps, depth))
+ return False;
+
+ if(!prs_uint16("switch_value", ps, depth, &q_u->switch_value))
+ return False;
+ if(!samr_io_userinfo_ctr("ctr", &q_u->ctr, ps, depth))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+inits a SAMR_R_SET_USERINFO2 structure.
+********************************************************************/
+
+void init_samr_r_set_userinfo2(SAMR_R_SET_USERINFO2 * r_u, NTSTATUS status)
+{
+ DEBUG(5, ("init_samr_r_set_userinfo2\n"));
+
+ r_u->status = status; /* return status */
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+BOOL samr_io_r_set_userinfo2(const char *desc, SAMR_R_SET_USERINFO2 * r_u,
+ prs_struct *ps, int depth)
+{
+ if (r_u == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "samr_io_r_set_userinfo2");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_ntstatus("status", ps, depth, &r_u->status))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+inits a SAMR_Q_CONNECT structure.
+********************************************************************/
+
+void init_samr_q_connect(SAMR_Q_CONNECT * q_u,
+ char *srv_name, uint32 access_mask)
+{
+ DEBUG(5, ("init_samr_q_connect\n"));
+
+ /* make PDC server name \\server */
+ q_u->ptr_srv_name = (srv_name != NULL && *srv_name) ? 1 : 0;
+ init_unistr2(&q_u->uni_srv_name, srv_name, UNI_STR_TERMINATE);
+
+ /* example values: 0x0000 0002 */
+ q_u->access_mask = access_mask;
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+BOOL samr_io_q_connect(const char *desc, SAMR_Q_CONNECT * q_u,
+ prs_struct *ps, int depth)
+{
+ if (q_u == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "samr_io_q_connect");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("ptr_srv_name", ps, depth, &q_u->ptr_srv_name))
+ return False;
+ if(!smb_io_unistr2("", &q_u->uni_srv_name, q_u->ptr_srv_name, ps, depth))
+ return False;
+
+ if(!prs_align(ps))
+ return False;
+ if(!prs_uint32("access_mask", ps, depth, &q_u->access_mask))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+BOOL samr_io_r_connect(const char *desc, SAMR_R_CONNECT * r_u,
+ prs_struct *ps, int depth)
+{
+ if (r_u == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "samr_io_r_connect");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!smb_io_pol_hnd("connect_pol", &r_u->connect_pol, ps, depth))
+ return False;
+
+ if(!prs_ntstatus("status", ps, depth, &r_u->status))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+inits a SAMR_Q_CONNECT4 structure.
+********************************************************************/
+
+void init_samr_q_connect4(SAMR_Q_CONNECT4 * q_u,
+ char *srv_name, uint32 access_mask)
+{
+ DEBUG(5, ("init_samr_q_connect\n"));
+
+ /* make PDC server name \\server */
+ q_u->ptr_srv_name = (srv_name != NULL && *srv_name) ? 1 : 0;
+ init_unistr2(&q_u->uni_srv_name, srv_name, UNI_STR_TERMINATE);
+
+ /* Only value we've seen, possibly an address type ? */
+ q_u->unk_0 = 2;
+
+ /* example values: 0x0000 0002 */
+ q_u->access_mask = access_mask;
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+BOOL samr_io_q_connect4(const char *desc, SAMR_Q_CONNECT4 * q_u,
+ prs_struct *ps, int depth)
+{
+ if (q_u == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "samr_io_q_connect4");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("ptr_srv_name", ps, depth, &q_u->ptr_srv_name))
+ return False;
+ if(!smb_io_unistr2("", &q_u->uni_srv_name, q_u->ptr_srv_name, ps, depth))
+ return False;
+
+ if(!prs_align(ps))
+ return False;
+ if(!prs_uint32("unk_0", ps, depth, &q_u->unk_0))
+ return False;
+ if(!prs_uint32("access_mask", ps, depth, &q_u->access_mask))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+BOOL samr_io_r_connect4(const char *desc, SAMR_R_CONNECT4 * r_u,
+ prs_struct *ps, int depth)
+{
+ if (r_u == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "samr_io_r_connect4");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!smb_io_pol_hnd("connect_pol", &r_u->connect_pol, ps, depth))
+ return False;
+
+ if(!prs_ntstatus("status", ps, depth, &r_u->status))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+inits a SAMR_Q_CONNECT_ANON structure.
+********************************************************************/
+
+void init_samr_q_connect_anon(SAMR_Q_CONNECT_ANON * q_u)
+{
+ DEBUG(5, ("init_samr_q_connect_anon\n"));
+
+ q_u->ptr = 1;
+ q_u->unknown_0 = 0x5c; /* server name (?!!) */
+ q_u->unknown_1 = 0x01;
+ q_u->access_mask = 0x20;
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+BOOL samr_io_q_connect_anon(const char *desc, SAMR_Q_CONNECT_ANON * q_u,
+ prs_struct *ps, int depth)
+{
+ if (q_u == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "samr_io_q_connect_anon");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("ptr ", ps, depth, &q_u->ptr))
+ return False;
+ if(!prs_uint16("unknown_0", ps, depth, &q_u->unknown_0))
+ return False;
+ if(!prs_uint16("unknown_1", ps, depth, &q_u->unknown_1))
+ return False;
+ if(!prs_uint32("access_mask", ps, depth, &q_u->access_mask))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+BOOL samr_io_r_connect_anon(const char *desc, SAMR_R_CONNECT_ANON * r_u,
+ prs_struct *ps, int depth)
+{
+ if (r_u == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "samr_io_r_connect_anon");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!smb_io_pol_hnd("connect_pol", &r_u->connect_pol, ps, depth))
+ return False;
+
+ if(!prs_ntstatus("status", ps, depth, &r_u->status))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+inits a SAMR_Q_GET_DOM_PWINFO structure.
+********************************************************************/
+
+void init_samr_q_get_dom_pwinfo(SAMR_Q_GET_DOM_PWINFO * q_u,
+ char *srv_name)
+{
+ DEBUG(5, ("init_samr_q_get_dom_pwinfo\n"));
+
+ q_u->ptr = 1;
+ init_unistr2(&q_u->uni_srv_name, srv_name, UNI_FLAGS_NONE);
+ init_uni_hdr(&q_u->hdr_srv_name, &q_u->uni_srv_name);
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+BOOL samr_io_q_get_dom_pwinfo(const char *desc, SAMR_Q_GET_DOM_PWINFO * q_u,
+ prs_struct *ps, int depth)
+{
+ if (q_u == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "samr_io_q_get_dom_pwinfo");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("ptr", ps, depth, &q_u->ptr))
+ return False;
+ if (q_u->ptr != 0) {
+ if(!smb_io_unihdr("", &q_u->hdr_srv_name, ps, depth))
+ return False;
+ if(!smb_io_unistr2("", &q_u->uni_srv_name, q_u->hdr_srv_name.buffer, ps, depth))
+ return False;
+ }
+
+ return True;
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+BOOL samr_io_r_get_dom_pwinfo(const char *desc, SAMR_R_GET_DOM_PWINFO * r_u,
+ prs_struct *ps, int depth)
+{
+ if (r_u == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "samr_io_r_get_dom_pwinfo");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ /*
+ * We need 16 bytes here according to tests. Don't know
+ * what they are, but the length is important for the singing
+ */
+
+ if(!prs_uint32("unk_0", ps, depth, &r_u->unk_0))
+ return False;
+ if(!prs_uint32("unk_1", ps, depth, &r_u->unk_1))
+ return False;
+ if(!prs_uint32("unk_2", ps, depth, &r_u->unk_2))
+ return False;
+
+ if(!prs_ntstatus("status", ps, depth, &r_u->status))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+make a SAMR_ENC_PASSWD structure.
+********************************************************************/
+
+void init_enc_passwd(SAMR_ENC_PASSWD * pwd, const char pass[512])
+{
+ ZERO_STRUCTP(pwd);
+
+ if (pass == NULL) {
+ pwd->ptr = 0;
+ } else {
+ pwd->ptr = 1;
+ memcpy(pwd->pass, pass, sizeof(pwd->pass));
+ }
+}
+
+/*******************************************************************
+reads or writes a SAMR_ENC_PASSWD structure.
+********************************************************************/
+
+BOOL samr_io_enc_passwd(const char *desc, SAMR_ENC_PASSWD * pwd,
+ prs_struct *ps, int depth)
+{
+ if (pwd == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "samr_io_enc_passwd");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("ptr", ps, depth, &pwd->ptr))
+ return False;
+
+ if (pwd->ptr != 0) {
+ if(!prs_uint8s(False, "pwd", ps, depth, pwd->pass, sizeof(pwd->pass)))
+ return False;
+ }
+
+ return True;
+}
+
+/*******************************************************************
+inits a SAMR_ENC_HASH structure.
+********************************************************************/
+
+void init_enc_hash(SAMR_ENC_HASH * hsh, const uchar hash[16])
+{
+ ZERO_STRUCTP(hsh);
+
+ if (hash == NULL) {
+ hsh->ptr = 0;
+ } else {
+ hsh->ptr = 1;
+ memcpy(hsh->hash, hash, sizeof(hsh->hash));
+ }
+}
+
+/*******************************************************************
+reads or writes a SAMR_ENC_HASH structure.
+********************************************************************/
+
+BOOL samr_io_enc_hash(const char *desc, SAMR_ENC_HASH * hsh,
+ prs_struct *ps, int depth)
+{
+ if (hsh == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "samr_io_enc_hash");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("ptr ", ps, depth, &hsh->ptr))
+ return False;
+ if (hsh->ptr != 0) {
+ if(!prs_uint8s(False, "hash", ps, depth, hsh->hash,sizeof(hsh->hash)))
+ return False;
+ }
+
+ return True;
+}
+
+/*******************************************************************
+inits a SAMR_R_GET_DOM_PWINFO structure.
+********************************************************************/
+
+void init_samr_q_chgpasswd_user(SAMR_Q_CHGPASSWD_USER * q_u,
+ const char *dest_host, const char *user_name,
+ const char nt_newpass[516],
+ const uchar nt_oldhash[16],
+ const char lm_newpass[516],
+ const uchar lm_oldhash[16])
+{
+ DEBUG(5, ("init_samr_q_chgpasswd_user\n"));
+
+ q_u->ptr_0 = 1;
+ init_unistr2(&q_u->uni_dest_host, dest_host, UNI_FLAGS_NONE);
+ init_uni_hdr(&q_u->hdr_dest_host, &q_u->uni_dest_host);
+
+ init_unistr2(&q_u->uni_user_name, user_name, UNI_FLAGS_NONE);
+ init_uni_hdr(&q_u->hdr_user_name, &q_u->uni_user_name);
+
+ init_enc_passwd(&q_u->nt_newpass, nt_newpass);
+ init_enc_hash(&q_u->nt_oldhash, nt_oldhash);
+
+ q_u->unknown = 0x01;
+
+ init_enc_passwd(&q_u->lm_newpass, lm_newpass);
+ init_enc_hash(&q_u->lm_oldhash, lm_oldhash);
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+BOOL samr_io_q_chgpasswd_user(const char *desc, SAMR_Q_CHGPASSWD_USER * q_u,
+ prs_struct *ps, int depth)
+{
+ if (q_u == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "samr_io_q_chgpasswd_user");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("ptr_0", ps, depth, &q_u->ptr_0))
+ return False;
+
+ if(!smb_io_unihdr("", &q_u->hdr_dest_host, ps, depth))
+ return False;
+ if(!smb_io_unistr2("", &q_u->uni_dest_host, q_u->hdr_dest_host.buffer, ps, depth))
+ return False;
+
+ if(!prs_align(ps))
+ return False;
+ if(!smb_io_unihdr("", &q_u->hdr_user_name, ps, depth))
+ return False;
+ if(!smb_io_unistr2("", &q_u->uni_user_name, q_u->hdr_user_name.buffer,ps, depth))
+ return False;
+
+ if(!samr_io_enc_passwd("nt_newpass", &q_u->nt_newpass, ps, depth))
+ return False;
+ if(!samr_io_enc_hash("nt_oldhash", &q_u->nt_oldhash, ps, depth))
+ return False;
+
+ if(!prs_uint32("unknown", ps, depth, &q_u->unknown))
+ return False;
+
+ if(!samr_io_enc_passwd("lm_newpass", &q_u->lm_newpass, ps, depth))
+ return False;
+ if(!samr_io_enc_hash("lm_oldhash", &q_u->lm_oldhash, ps, depth))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+inits a SAMR_R_CHGPASSWD_USER structure.
+********************************************************************/
+
+void init_samr_r_chgpasswd_user(SAMR_R_CHGPASSWD_USER * r_u, NTSTATUS status)
+{
+ DEBUG(5, ("init_r_chgpasswd_user\n"));
+
+ r_u->status = status;
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+BOOL samr_io_r_chgpasswd_user(const char *desc, SAMR_R_CHGPASSWD_USER * r_u,
+ prs_struct *ps, int depth)
+{
+ if (r_u == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "samr_io_r_chgpasswd_user");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_ntstatus("status", ps, depth, &r_u->status))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+void init_samr_q_unknown_2e(SAMR_Q_UNKNOWN_2E *q_u,
+ POLICY_HND *domain_pol, uint16 switch_value)
+{
+ DEBUG(5, ("init_samr_q_unknown_2e\n"));
+
+ q_u->domain_pol = *domain_pol;
+ q_u->switch_value = switch_value;
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+BOOL samr_io_q_unknown_2e(const char *desc, SAMR_Q_UNKNOWN_2E *q_u,
+ prs_struct *ps, int depth)
+{
+ if (q_u == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "samr_io_q_unknown_2e");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!smb_io_pol_hnd("domain_pol", &q_u->domain_pol, ps, depth))
+ return False;
+
+ if(!prs_uint16("switch_value", ps, depth, &q_u->switch_value))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+inits a SAMR_R_QUERY_DOMAIN_INFO structure.
+********************************************************************/
+
+void init_samr_r_samr_unknown_2e(SAMR_R_UNKNOWN_2E * r_u,
+ uint16 switch_value, SAM_UNK_CTR * ctr,
+ NTSTATUS status)
+{
+ DEBUG(5, ("init_samr_r_samr_unknown_2e\n"));
+
+ r_u->ptr_0 = 0;
+ r_u->switch_value = 0;
+ r_u->status = status; /* return status */
+
+ if (NT_STATUS_IS_OK(status)) {
+ r_u->switch_value = switch_value;
+ r_u->ptr_0 = 1;
+ r_u->ctr = ctr;
+ }
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+BOOL samr_io_r_samr_unknown_2e(const char *desc, SAMR_R_UNKNOWN_2E * r_u,
+ prs_struct *ps, int depth)
+{
+ if (r_u == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "samr_io_r_samr_unknown_2e");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("ptr_0 ", ps, depth, &r_u->ptr_0))
+ return False;
+
+ if (r_u->ptr_0 != 0 && r_u->ctr != NULL) {
+ if(!prs_uint16("switch_value", ps, depth, &r_u->switch_value))
+ return False;
+ if(!prs_align(ps))
+ return False;
+
+ switch (r_u->switch_value) {
+ case 0x0c:
+ if(!sam_io_unk_info12("unk_inf12", &r_u->ctr->info.inf12, ps, depth))
+ return False;
+ break;
+ case 0x07:
+ if(!sam_io_unk_info7("unk_inf7",&r_u->ctr->info.inf7, ps,depth))
+ return False;
+ break;
+ case 0x06:
+ if(!sam_io_unk_info6("unk_inf6",&r_u->ctr->info.inf6, ps,depth))
+ return False;
+ break;
+ case 0x05:
+ if(!sam_io_unk_info5("unk_inf5",&r_u->ctr->info.inf5, ps,depth))
+ return False;
+ break;
+ case 0x03:
+ if(!sam_io_unk_info3("unk_inf3",&r_u->ctr->info.inf3, ps,depth))
+ return False;
+ break;
+ case 0x02:
+ if(!sam_io_unk_info2("unk_inf2",&r_u->ctr->info.inf2, ps,depth))
+ return False;
+ break;
+ case 0x01:
+ if(!sam_io_unk_info1("unk_inf1",&r_u->ctr->info.inf1, ps,depth))
+ return False;
+ break;
+ default:
+ DEBUG(0, ("samr_io_r_samr_unknown_2e: unknown switch level 0x%x\n",
+ r_u->switch_value));
+ r_u->status = NT_STATUS_INVALID_INFO_CLASS;
+ return False;
+ }
+ }
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_ntstatus("status", ps, depth, &r_u->status))
+ return False;
+
+ return True;
+}
+
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+void init_samr_q_set_domain_info(SAMR_Q_SET_DOMAIN_INFO *q_u,
+ POLICY_HND *domain_pol, uint16 switch_value, SAM_UNK_CTR *ctr)
+{
+ DEBUG(5, ("init_samr_q_set_domain_info\n"));
+
+ q_u->domain_pol = *domain_pol;
+ q_u->switch_value0 = switch_value;
+
+ q_u->switch_value = switch_value;
+ q_u->ctr = ctr;
+
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+BOOL samr_io_q_set_domain_info(const char *desc, SAMR_Q_SET_DOMAIN_INFO *q_u,
+ prs_struct *ps, int depth)
+{
+ if (q_u == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "samr_io_q_set_domain_info");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!smb_io_pol_hnd("domain_pol", &q_u->domain_pol, ps, depth))
+ return False;
+
+ if(!prs_uint16("switch_value0", ps, depth, &q_u->switch_value0))
+ return False;
+
+ if(!prs_uint16("switch_value", ps, depth, &q_u->switch_value))
+ return False;
+
+ if(!prs_align(ps))
+ return False;
+
+ if ((q_u->ctr = (SAM_UNK_CTR *)prs_alloc_mem(ps, sizeof(SAM_UNK_CTR))) == NULL)
+ return False;
+
+ switch (q_u->switch_value) {
+
+ case 0x0c:
+ if(!sam_io_unk_info12("unk_inf12", &q_u->ctr->info.inf12, ps, depth))
+ return False;
+ break;
+ case 0x07:
+ if(!sam_io_unk_info7("unk_inf7",&q_u->ctr->info.inf7, ps,depth))
+ return False;
+ break;
+ case 0x06:
+ if(!sam_io_unk_info6("unk_inf6",&q_u->ctr->info.inf6, ps,depth))
+ return False;
+ break;
+ case 0x05:
+ if(!sam_io_unk_info5("unk_inf5",&q_u->ctr->info.inf5, ps,depth))
+ return False;
+ break;
+ case 0x03:
+ if(!sam_io_unk_info3("unk_inf3",&q_u->ctr->info.inf3, ps,depth))
+ return False;
+ break;
+ case 0x02:
+ if(!sam_io_unk_info2("unk_inf2",&q_u->ctr->info.inf2, ps,depth))
+ return False;
+ break;
+ case 0x01:
+ if(!sam_io_unk_info1("unk_inf1",&q_u->ctr->info.inf1, ps,depth))
+ return False;
+ break;
+ default:
+ DEBUG(0, ("samr_io_r_samr_unknown_2e: unknown switch level 0x%x\n",
+ q_u->switch_value));
+ return False;
+ }
+
+ return True;
+}
+
+/*******************************************************************
+inits a SAMR_R_QUERY_DOMAIN_INFO structure.
+********************************************************************/
+
+void init_samr_r_set_domain_info(SAMR_R_SET_DOMAIN_INFO * r_u, NTSTATUS status)
+{
+ DEBUG(5, ("init_samr_r_set_domain_info\n"));
+
+ r_u->status = status; /* return status */
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+BOOL samr_io_r_set_domain_info(const char *desc, SAMR_R_SET_DOMAIN_INFO * r_u,
+ prs_struct *ps, int depth)
+{
+ if (r_u == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "samr_io_r_samr_unknown_2e");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_ntstatus("status", ps, depth, &r_u->status))
+ return False;
+
+ return True;
+}
diff --git a/source/rpc_parse/parse_sec.c b/source/rpc_parse/parse_sec.c
new file mode 100644
index 00000000000..a78627650ad
--- /dev/null
+++ b/source/rpc_parse/parse_sec.c
@@ -0,0 +1,366 @@
+/*
+ * Unix SMB/Netbios implementation.
+ * Version 1.9.
+ * RPC Pipe client / server routines
+ * Copyright (C) Andrew Tridgell 1992-1998,
+ * Copyright (C) Jeremy R. Allison 1995-2003.
+ * Copyright (C) Luke Kenneth Casson Leighton 1996-1998,
+ * Copyright (C) Paul Ashton 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 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"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_RPC_PARSE
+
+/*******************************************************************
+ Reads or writes a SEC_ACCESS structure.
+********************************************************************/
+
+BOOL sec_io_access(const char *desc, SEC_ACCESS *t, prs_struct *ps, int depth)
+{
+ if (t == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "sec_io_access");
+ depth++;
+
+ if(!prs_uint32("mask", ps, depth, &t->mask))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ Reads or writes a SEC_ACE structure.
+********************************************************************/
+
+BOOL sec_io_ace(const char *desc, SEC_ACE *psa, prs_struct *ps, int depth)
+{
+ uint32 old_offset;
+ uint32 offset_ace_size;
+
+ if (psa == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "sec_io_ace");
+ depth++;
+
+ old_offset = prs_offset(ps);
+
+ if(!prs_uint8("type ", ps, depth, &psa->type))
+ return False;
+
+ if(!prs_uint8("flags", ps, depth, &psa->flags))
+ return False;
+
+ if(!prs_uint16_pre("size ", ps, depth, &psa->size, &offset_ace_size))
+ return False;
+
+ if(!sec_io_access("info ", &psa->info, ps, depth))
+ return False;
+
+ /* check whether object access is present */
+ if (!sec_ace_object(psa->type)) {
+ if (!smb_io_dom_sid("trustee ", &psa->trustee , ps, depth))
+ return False;
+ } else {
+ if (!prs_uint32("obj_flags", ps, depth, &psa->obj_flags))
+ return False;
+
+ if (psa->obj_flags & SEC_ACE_OBJECT_PRESENT)
+ if (!smb_io_uuid("obj_guid", &psa->obj_guid, ps,depth))
+ return False;
+
+ if (psa->obj_flags & SEC_ACE_OBJECT_INHERITED_PRESENT)
+ if (!smb_io_uuid("inh_guid", &psa->inh_guid, ps,depth))
+ return False;
+
+ if(!smb_io_dom_sid("trustee ", &psa->trustee , ps, depth))
+ return False;
+ }
+
+ if(!prs_uint16_post("size ", ps, depth, &psa->size, offset_ace_size, old_offset))
+ return False;
+ return True;
+}
+
+/*******************************************************************
+ Reads or writes a SEC_ACL structure.
+
+ First of the xx_io_xx functions that allocates its data structures
+ for you as it reads them.
+********************************************************************/
+
+BOOL sec_io_acl(const char *desc, SEC_ACL **ppsa, prs_struct *ps, int depth)
+{
+ unsigned int i;
+ uint32 old_offset;
+ uint32 offset_acl_size;
+ SEC_ACL *psa;
+
+ /*
+ * Note that the size is always a multiple of 4 bytes due to the
+ * nature of the data structure. Therefore the prs_align() calls
+ * have been removed as they through us off when doing two-layer
+ * marshalling such as in the printing code (NEW_BUFFER). --jerry
+ */
+
+ if (ppsa == NULL)
+ return False;
+
+ psa = *ppsa;
+
+ if(UNMARSHALLING(ps) && psa == NULL) {
+ /*
+ * This is a read and we must allocate the stuct to read into.
+ */
+ if((psa = (SEC_ACL *)prs_alloc_mem(ps, sizeof(SEC_ACL))) == NULL)
+ return False;
+ *ppsa = psa;
+ }
+
+ prs_debug(ps, depth, desc, "sec_io_acl");
+ depth++;
+
+ old_offset = prs_offset(ps);
+
+ if(!prs_uint16("revision", ps, depth, &psa->revision))
+ return False;
+
+ if(!prs_uint16_pre("size ", ps, depth, &psa->size, &offset_acl_size))
+ return False;
+
+ if(!prs_uint32("num_aces ", ps, depth, &psa->num_aces))
+ return False;
+
+ if (UNMARSHALLING(ps)) {
+ /*
+ * Even if the num_aces is zero, allocate memory as there's a difference
+ * between a non-present DACL (allow all access) and a DACL with no ACE's
+ * (allow no access).
+ */
+ if((psa->ace = (SEC_ACE *)prs_alloc_mem(ps,sizeof(psa->ace[0]) * (psa->num_aces+1))) == NULL)
+ return False;
+ }
+
+ for (i = 0; i < psa->num_aces; i++) {
+ fstring tmp;
+ slprintf(tmp, sizeof(tmp)-1, "ace_list[%02d]: ", i);
+ if(!sec_io_ace(tmp, &psa->ace[i], ps, depth))
+ return False;
+ }
+
+ if(!prs_uint16_post("size ", ps, depth, &psa->size, offset_acl_size, old_offset))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ Reads or writes a SEC_DESC structure.
+ If reading and the *ppsd = NULL, allocates the structure.
+********************************************************************/
+
+BOOL sec_io_desc(const char *desc, SEC_DESC **ppsd, prs_struct *ps, int depth)
+{
+ uint32 old_offset;
+ uint32 max_offset = 0; /* after we're done, move offset to end */
+ uint32 tmp_offset = 0;
+
+ SEC_DESC *psd;
+
+ if (ppsd == NULL)
+ return False;
+
+ psd = *ppsd;
+
+ if (psd == NULL) {
+ if(UNMARSHALLING(ps)) {
+ if((psd = (SEC_DESC *)prs_alloc_mem(ps,sizeof(SEC_DESC))) == NULL)
+ return False;
+ *ppsd = psd;
+ } else {
+ /* Marshalling - just ignore. */
+ return True;
+ }
+ }
+
+ prs_debug(ps, depth, desc, "sec_io_desc");
+ depth++;
+
+#if 0
+ /*
+ * if alignment is needed, should be done by the the
+ * caller. Not here. This caused me problems when marshalling
+ * printer info into a buffer. --jerry
+ */
+ if(!prs_align(ps))
+ return False;
+#endif
+
+ /* start of security descriptor stored for back-calc offset purposes */
+ old_offset = prs_offset(ps);
+
+ if(!prs_uint16("revision ", ps, depth, &psd->revision))
+ return False;
+
+ if(!prs_uint16("type ", ps, depth, &psd->type))
+ return False;
+
+ if(!prs_uint32("off_owner_sid", ps, depth, &psd->off_owner_sid))
+ return False;
+
+ if(!prs_uint32("off_grp_sid ", ps, depth, &psd->off_grp_sid))
+ return False;
+
+ if(!prs_uint32("off_sacl ", ps, depth, &psd->off_sacl))
+ return False;
+
+ if(!prs_uint32("off_dacl ", ps, depth, &psd->off_dacl))
+ return False;
+
+ max_offset = MAX(max_offset, prs_offset(ps));
+
+ if (psd->off_owner_sid != 0) {
+
+ tmp_offset = prs_offset(ps);
+ if(!prs_set_offset(ps, old_offset + psd->off_owner_sid))
+ return False;
+
+ if (UNMARSHALLING(ps)) {
+ /* reading */
+ if((psd->owner_sid = (DOM_SID *)prs_alloc_mem(ps,sizeof(*psd->owner_sid))) == NULL)
+ return False;
+ }
+
+ if(!smb_io_dom_sid("owner_sid ", psd->owner_sid , ps, depth))
+ return False;
+
+ max_offset = MAX(max_offset, prs_offset(ps));
+
+ if (!prs_set_offset(ps,tmp_offset))
+ return False;
+ }
+
+ if (psd->off_grp_sid != 0) {
+
+ tmp_offset = prs_offset(ps);
+ if(!prs_set_offset(ps, old_offset + psd->off_grp_sid))
+ return False;
+
+ if (UNMARSHALLING(ps)) {
+ /* reading */
+ if((psd->grp_sid = (DOM_SID *)prs_alloc_mem(ps,sizeof(*psd->grp_sid))) == NULL)
+ return False;
+ }
+
+ if(!smb_io_dom_sid("grp_sid", psd->grp_sid, ps, depth))
+ return False;
+
+ max_offset = MAX(max_offset, prs_offset(ps));
+
+ if (!prs_set_offset(ps,tmp_offset))
+ return False;
+ }
+
+ if ((psd->type & SEC_DESC_SACL_PRESENT) && psd->off_sacl) {
+ tmp_offset = prs_offset(ps);
+ if(!prs_set_offset(ps, old_offset + psd->off_sacl))
+ return False;
+ if(!sec_io_acl("sacl", &psd->sacl, ps, depth))
+ return False;
+ max_offset = MAX(max_offset, prs_offset(ps));
+ if (!prs_set_offset(ps,tmp_offset))
+ return False;
+ }
+
+
+ if ((psd->type & SEC_DESC_DACL_PRESENT) && psd->off_dacl != 0) {
+ tmp_offset = prs_offset(ps);
+ if(!prs_set_offset(ps, old_offset + psd->off_dacl))
+ return False;
+ if(!sec_io_acl("dacl", &psd->dacl, ps, depth))
+ return False;
+ max_offset = MAX(max_offset, prs_offset(ps));
+ if (!prs_set_offset(ps,tmp_offset))
+ return False;
+ }
+
+ if(!prs_set_offset(ps, max_offset))
+ return False;
+ return True;
+}
+
+/*******************************************************************
+ Reads or writes a SEC_DESC_BUF structure.
+********************************************************************/
+
+BOOL sec_io_desc_buf(const char *desc, SEC_DESC_BUF **ppsdb, prs_struct *ps, int depth)
+{
+ uint32 off_len;
+ uint32 off_max_len;
+ uint32 old_offset;
+ uint32 size;
+ SEC_DESC_BUF *psdb;
+
+ if (ppsdb == NULL)
+ return False;
+
+ psdb = *ppsdb;
+
+ if (UNMARSHALLING(ps) && psdb == NULL) {
+ if((psdb = (SEC_DESC_BUF *)prs_alloc_mem(ps,sizeof(SEC_DESC_BUF))) == NULL)
+ return False;
+ *ppsdb = psdb;
+ }
+
+ prs_debug(ps, depth, desc, "sec_io_desc_buf");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32_pre("max_len", ps, depth, &psdb->max_len, &off_max_len))
+ return False;
+
+ if(!prs_uint32 ("ptr ", ps, depth, &psdb->ptr))
+ return False;
+
+ if(!prs_uint32_pre("len ", ps, depth, &psdb->len, &off_len))
+ return False;
+
+ old_offset = prs_offset(ps);
+
+ /* reading, length is non-zero; writing, descriptor is non-NULL */
+ if ((UNMARSHALLING(ps) && psdb->len != 0) || (MARSHALLING(ps) && psdb->sec != NULL)) {
+ if(!sec_io_desc("sec ", &psdb->sec, ps, depth))
+ return False;
+ }
+
+ if(!prs_align(ps))
+ return False;
+
+ size = prs_offset(ps) - old_offset;
+ if(!prs_uint32_post("max_len", ps, depth, &psdb->max_len, off_max_len, size == 0 ? psdb->max_len : size))
+ return False;
+
+ if(!prs_uint32_post("len ", ps, depth, &psdb->len, off_len, size))
+ return False;
+
+ return True;
+}
diff --git a/source/rpc_parse/parse_shutdown.c b/source/rpc_parse/parse_shutdown.c
new file mode 100644
index 00000000000..ad2d6e1a028
--- /dev/null
+++ b/source/rpc_parse/parse_shutdown.c
@@ -0,0 +1,163 @@
+/*
+ * Unix SMB/CIFS implementation.
+ * RPC Pipe client / server routines
+ * 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 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"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_RPC_PARSE
+
+/*******************************************************************
+Inits a structure.
+********************************************************************/
+
+void init_shutdown_q_init(SHUTDOWN_Q_INIT *q_s, const char *msg,
+ uint32 timeout, BOOL do_reboot, BOOL force)
+{
+ q_s->ptr_server = 1;
+ q_s->server = 1;
+ q_s->ptr_msg = 1;
+
+ init_unistr2(&q_s->uni_msg, msg, UNI_FLAGS_NONE);
+ init_uni_hdr(&q_s->hdr_msg, &q_s->uni_msg);
+
+ q_s->timeout = timeout;
+
+ q_s->reboot = do_reboot ? 1 : 0;
+ q_s->force = force ? 1 : 0;
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+BOOL shutdown_io_q_init(const char *desc, SHUTDOWN_Q_INIT *q_s, prs_struct *ps,
+ int depth)
+{
+ if (q_s == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "shutdown_io_q_init");
+ depth++;
+
+ if (!prs_align(ps))
+ return False;
+
+ if (!prs_uint32("ptr_server", ps, depth, &(q_s->ptr_server)))
+ return False;
+ if (!prs_uint16("server", ps, depth, &(q_s->server)))
+ return False;
+
+ if (!prs_align(ps))
+ return False;
+ if (!prs_uint32("ptr_msg", ps, depth, &(q_s->ptr_msg)))
+ return False;
+
+ if (!smb_io_unihdr("hdr_msg", &(q_s->hdr_msg), ps, depth))
+ return False;
+ if (!smb_io_unistr2("uni_msg", &(q_s->uni_msg), q_s->hdr_msg.buffer, ps, depth))
+ return False;
+ if (!prs_align(ps))
+ return False;
+
+ if (!prs_uint32("timeout", ps, depth, &(q_s->timeout)))
+ return False;
+ if (!prs_uint8("force ", ps, depth, &(q_s->force)))
+ return False;
+ if (!prs_uint8("reboot ", ps, depth, &(q_s->reboot)))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+BOOL shutdown_io_r_init(const char *desc, SHUTDOWN_R_INIT* r_s, prs_struct *ps,
+ int depth)
+{
+ if (r_s == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "shutdown_io_r_init");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_ntstatus("status", ps, depth, &r_s->status))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+Inits a structure.
+********************************************************************/
+void init_shutdown_q_abort(SHUTDOWN_Q_ABORT *q_s)
+{
+
+ q_s->ptr_server = 0;
+
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+BOOL shutdown_io_q_abort(const char *desc, SHUTDOWN_Q_ABORT *q_s,
+ prs_struct *ps, int depth)
+{
+ if (q_s == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "shutdown_io_q_abort");
+ depth++;
+
+ if (!prs_align(ps))
+ return False;
+
+ if (!prs_uint32("ptr_server", ps, depth, &(q_s->ptr_server)))
+ return False;
+ if (q_s->ptr_server != 0)
+ if (!prs_uint16("server", ps, depth, &(q_s->server)))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+BOOL shutdown_io_r_abort(const char *desc, SHUTDOWN_R_ABORT *r_s,
+ prs_struct *ps, int depth)
+{
+ if (r_s == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "shutdown_io_r_abort");
+ depth++;
+
+ if (!prs_align(ps))
+ return False;
+
+ if (!prs_ntstatus("status", ps, depth, &r_s->status))
+ return False;
+
+ return True;
+}
diff --git a/source/rpc_parse/parse_spoolss.c b/source/rpc_parse/parse_spoolss.c
new file mode 100644
index 00000000000..ae087c7f774
--- /dev/null
+++ b/source/rpc_parse/parse_spoolss.c
@@ -0,0 +1,7741 @@
+/*
+ * Unix SMB/CIFS implementation.
+ * RPC Pipe client / server routines
+ * Copyright (C) Andrew Tridgell 1992-2000,
+ * Copyright (C) Luke Kenneth Casson Leighton 1996-2000,
+ * Copyright (C) Jean François Micouleau 1998-2000,
+ * Copyright (C) Gerald Carter 2000-2002,
+ * Copyright (C) Tim Potter 2001-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 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"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_RPC_PARSE
+
+/*******************************************************************
+return the length of a UNISTR string.
+********************************************************************/
+
+static uint32 str_len_uni(UNISTR *source)
+{
+ uint32 i=0;
+
+ if (!source->buffer)
+ return 0;
+
+ while (source->buffer[i])
+ i++;
+
+ return i;
+}
+
+/*******************************************************************
+This should be moved in a more generic lib.
+********************************************************************/
+
+BOOL spoolss_io_system_time(const char *desc, prs_struct *ps, int depth, SYSTEMTIME *systime)
+{
+ if(!prs_uint16("year", ps, depth, &systime->year))
+ return False;
+ if(!prs_uint16("month", ps, depth, &systime->month))
+ return False;
+ if(!prs_uint16("dayofweek", ps, depth, &systime->dayofweek))
+ return False;
+ if(!prs_uint16("day", ps, depth, &systime->day))
+ return False;
+ if(!prs_uint16("hour", ps, depth, &systime->hour))
+ return False;
+ if(!prs_uint16("minute", ps, depth, &systime->minute))
+ return False;
+ if(!prs_uint16("second", ps, depth, &systime->second))
+ return False;
+ if(!prs_uint16("milliseconds", ps, depth, &systime->milliseconds))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+********************************************************************/
+
+BOOL make_systemtime(SYSTEMTIME *systime, struct tm *unixtime)
+{
+ systime->year=unixtime->tm_year+1900;
+ systime->month=unixtime->tm_mon+1;
+ systime->dayofweek=unixtime->tm_wday;
+ systime->day=unixtime->tm_mday;
+ systime->hour=unixtime->tm_hour;
+ systime->minute=unixtime->tm_min;
+ systime->second=unixtime->tm_sec;
+ systime->milliseconds=0;
+
+ return True;
+}
+
+/*******************************************************************
+reads or writes an DOC_INFO structure.
+********************************************************************/
+
+static BOOL smb_io_doc_info_1(const char *desc, DOC_INFO_1 *info_1, prs_struct *ps, int depth)
+{
+ if (info_1 == NULL) return False;
+
+ prs_debug(ps, depth, desc, "smb_io_doc_info_1");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("p_docname", ps, depth, &info_1->p_docname))
+ return False;
+ if(!prs_uint32("p_outputfile", ps, depth, &info_1->p_outputfile))
+ return False;
+ if(!prs_uint32("p_datatype", ps, depth, &info_1->p_datatype))
+ return False;
+
+ if(!smb_io_unistr2("", &info_1->docname, info_1->p_docname, ps, depth))
+ return False;
+ if(!smb_io_unistr2("", &info_1->outputfile, info_1->p_outputfile, ps, depth))
+ return False;
+ if(!smb_io_unistr2("", &info_1->datatype, info_1->p_datatype, ps, depth))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+reads or writes an DOC_INFO structure.
+********************************************************************/
+
+static BOOL smb_io_doc_info(const char *desc, DOC_INFO *info, prs_struct *ps, int depth)
+{
+ uint32 useless_ptr=0;
+
+ if (info == NULL) return False;
+
+ prs_debug(ps, depth, desc, "smb_io_doc_info");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("switch_value", ps, depth, &info->switch_value))
+ return False;
+
+ if(!prs_uint32("doc_info_X ptr", ps, depth, &useless_ptr))
+ return False;
+
+ switch (info->switch_value)
+ {
+ case 1:
+ if(!smb_io_doc_info_1("",&info->doc_info_1, ps, depth))
+ return False;
+ break;
+ case 2:
+ /*
+ this is just a placeholder
+
+ MSDN July 1998 says doc_info_2 is only on
+ Windows 95, and as Win95 doesn't do RPC to print
+ this case is nearly impossible
+
+ Maybe one day with Windows for dishwasher 2037 ...
+
+ */
+ /* smb_io_doc_info_2("",&info->doc_info_2, ps, depth); */
+ break;
+ default:
+ DEBUG(0,("Something is obviously wrong somewhere !\n"));
+ break;
+ }
+
+ return True;
+}
+
+/*******************************************************************
+reads or writes an DOC_INFO_CONTAINER structure.
+********************************************************************/
+
+static BOOL smb_io_doc_info_container(const char *desc, DOC_INFO_CONTAINER *cont, prs_struct *ps, int depth)
+{
+ if (cont == NULL) return False;
+
+ prs_debug(ps, depth, desc, "smb_io_doc_info_container");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("level", ps, depth, &cont->level))
+ return False;
+
+ if(!smb_io_doc_info("",&cont->docinfo, ps, depth))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+reads or writes an NOTIFY OPTION TYPE structure.
+********************************************************************/
+
+/* NOTIFY_OPTION_TYPE and NOTIFY_OPTION_TYPE_DATA are really one
+ structure. The _TYPE structure is really the deferred referrants (i.e
+ the notify fields array) of the _TYPE structure. -tpot */
+
+static BOOL smb_io_notify_option_type(const char *desc, SPOOL_NOTIFY_OPTION_TYPE *type, prs_struct *ps, int depth)
+{
+ prs_debug(ps, depth, desc, "smb_io_notify_option_type");
+ depth++;
+
+ if (!prs_align(ps))
+ return False;
+
+ if(!prs_uint16("type", ps, depth, &type->type))
+ return False;
+ if(!prs_uint16("reserved0", ps, depth, &type->reserved0))
+ return False;
+ if(!prs_uint32("reserved1", ps, depth, &type->reserved1))
+ return False;
+ if(!prs_uint32("reserved2", ps, depth, &type->reserved2))
+ return False;
+ if(!prs_uint32("count", ps, depth, &type->count))
+ return False;
+ if(!prs_uint32("fields_ptr", ps, depth, &type->fields_ptr))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+reads or writes an NOTIFY OPTION TYPE DATA.
+********************************************************************/
+
+static BOOL smb_io_notify_option_type_data(const char *desc, SPOOL_NOTIFY_OPTION_TYPE *type, prs_struct *ps, int depth)
+{
+ int i;
+
+ prs_debug(ps, depth, desc, "smb_io_notify_option_type_data");
+ depth++;
+
+ /* if there are no fields just return */
+ if (type->fields_ptr==0)
+ return True;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("count2", ps, depth, &type->count2))
+ return False;
+
+ if (type->count2 != type->count)
+ DEBUG(4,("What a mess, count was %x now is %x !\n", type->count, type->count2));
+
+ /* parse the option type data */
+ for(i=0;i<type->count2;i++)
+ if(!prs_uint16("fields",ps,depth,&type->fields[i]))
+ return False;
+ return True;
+}
+
+/*******************************************************************
+reads or writes an NOTIFY OPTION structure.
+********************************************************************/
+
+static BOOL smb_io_notify_option_type_ctr(const char *desc, SPOOL_NOTIFY_OPTION_TYPE_CTR *ctr , prs_struct *ps, int depth)
+{
+ int i;
+
+ prs_debug(ps, depth, desc, "smb_io_notify_option_type_ctr");
+ depth++;
+
+ if(!prs_uint32("count", ps, depth, &ctr->count))
+ return False;
+
+ /* reading */
+ if (UNMARSHALLING(ps))
+ if((ctr->type=(SPOOL_NOTIFY_OPTION_TYPE *)prs_alloc_mem(ps,ctr->count*sizeof(SPOOL_NOTIFY_OPTION_TYPE))) == NULL)
+ return False;
+
+ /* the option type struct */
+ for(i=0;i<ctr->count;i++)
+ if(!smb_io_notify_option_type("", &ctr->type[i] , ps, depth))
+ return False;
+
+ /* the type associated with the option type struct */
+ for(i=0;i<ctr->count;i++)
+ if(!smb_io_notify_option_type_data("", &ctr->type[i] , ps, depth))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+reads or writes an NOTIFY OPTION structure.
+********************************************************************/
+
+static BOOL smb_io_notify_option(const char *desc, SPOOL_NOTIFY_OPTION *option, prs_struct *ps, int depth)
+{
+ prs_debug(ps, depth, desc, "smb_io_notify_option");
+ depth++;
+
+ if(!prs_uint32("version", ps, depth, &option->version))
+ return False;
+ if(!prs_uint32("flags", ps, depth, &option->flags))
+ return False;
+ if(!prs_uint32("count", ps, depth, &option->count))
+ return False;
+ if(!prs_uint32("option_type_ptr", ps, depth, &option->option_type_ptr))
+ return False;
+
+ /* marshalling or unmarshalling, that would work */
+ if (option->option_type_ptr!=0) {
+ if(!smb_io_notify_option_type_ctr("", &option->ctr ,ps, depth))
+ return False;
+ }
+ else {
+ option->ctr.type=NULL;
+ option->ctr.count=0;
+ }
+
+ return True;
+}
+
+/*******************************************************************
+reads or writes an NOTIFY INFO DATA structure.
+********************************************************************/
+
+static BOOL smb_io_notify_info_data(const char *desc,SPOOL_NOTIFY_INFO_DATA *data, prs_struct *ps, int depth)
+{
+ uint32 useless_ptr=0x0FF0ADDE;
+
+ prs_debug(ps, depth, desc, "smb_io_notify_info_data");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+ if(!prs_uint16("type", ps, depth, &data->type))
+ return False;
+ if(!prs_uint16("field", ps, depth, &data->field))
+ return False;
+
+ if(!prs_uint32("how many words", ps, depth, &data->size))
+ return False;
+ if(!prs_uint32("id", ps, depth, &data->id))
+ return False;
+ if(!prs_uint32("how many words", ps, depth, &data->size))
+ return False;
+
+ switch (data->enc_type) {
+
+ /* One and two value data has two uint32 values */
+
+ case NOTIFY_ONE_VALUE:
+ case NOTIFY_TWO_VALUE:
+
+ if(!prs_uint32("value[0]", ps, depth, &data->notify_data.value[0]))
+ return False;
+ if(!prs_uint32("value[1]", ps, depth, &data->notify_data.value[1]))
+ return False;
+ break;
+
+ /* Pointers and strings have a string length and a
+ pointer. For a string the length is expressed as
+ the number of uint16 characters plus a trailing
+ \0\0. */
+
+ case NOTIFY_POINTER:
+
+ if(!prs_uint32("string length", ps, depth, &data->notify_data.data.length ))
+ return False;
+ if(!prs_uint32("pointer", ps, depth, &useless_ptr))
+ return False;
+
+ break;
+
+ case NOTIFY_STRING:
+
+ if(!prs_uint32("string length", ps, depth, &data->notify_data.data.length))
+ return False;
+
+ if(!prs_uint32("pointer", ps, depth, &useless_ptr))
+ return False;
+
+ break;
+
+ case NOTIFY_SECDESC:
+ if( !prs_uint32( "sd size", ps, depth, &data->notify_data.sd.size ) )
+ return False;
+ if( !prs_uint32( "pointer", ps, depth, &useless_ptr ) )
+ return False;
+
+ break;
+
+ default:
+ DEBUG(3, ("invalid enc_type %d for smb_io_notify_info_data\n",
+ data->enc_type));
+ break;
+ }
+
+ return True;
+}
+
+/*******************************************************************
+reads or writes an NOTIFY INFO DATA structure.
+********************************************************************/
+
+BOOL smb_io_notify_info_data_strings(const char *desc,SPOOL_NOTIFY_INFO_DATA *data,
+ prs_struct *ps, int depth)
+{
+ prs_debug(ps, depth, desc, "smb_io_notify_info_data_strings");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ switch(data->enc_type) {
+
+ /* No data for values */
+
+ case NOTIFY_ONE_VALUE:
+ case NOTIFY_TWO_VALUE:
+
+ break;
+
+ /* Strings start with a length in uint16s */
+
+ case NOTIFY_STRING:
+
+ if (UNMARSHALLING(ps)) {
+ data->notify_data.data.string =
+ (uint16 *)prs_alloc_mem(ps, data->notify_data.data.length);
+
+ if (!data->notify_data.data.string)
+ return False;
+ }
+
+ if (MARSHALLING(ps))
+ data->notify_data.data.length /= 2;
+
+ if(!prs_uint32("string length", ps, depth, &data->notify_data.data.length))
+ return False;
+
+ if (!prs_uint16uni(True, "string", ps, depth, data->notify_data.data.string,
+ data->notify_data.data.length))
+ return False;
+
+ if (MARSHALLING(ps))
+ data->notify_data.data.length *= 2;
+
+ break;
+
+ case NOTIFY_POINTER:
+
+ if (UNMARSHALLING(ps)) {
+ data->notify_data.data.string =
+ (uint16 *)prs_alloc_mem(ps, data->notify_data.data.length);
+
+ if (!data->notify_data.data.string)
+ return False;
+ }
+
+ if(!prs_uint8s(True,"buffer",ps,depth,(uint8*)data->notify_data.data.string,data->notify_data.data.length))
+ return False;
+
+ break;
+
+ case NOTIFY_SECDESC:
+ if( !prs_uint32("secdesc size ", ps, depth, &data->notify_data.sd.size ) )
+ return False;
+ if ( !sec_io_desc( "sec_desc", &data->notify_data.sd.desc, ps, depth ) )
+ return False;
+ break;
+
+ default:
+ DEBUG(3, ("invalid enc_type %d for smb_io_notify_info_data_strings\n",
+ data->enc_type));
+ break;
+ }
+
+#if 0
+ if (isvalue==False) {
+
+ /* length of string in unicode include \0 */
+ x=data->notify_data.data.length+1;
+
+ if (data->field != 16)
+ if(!prs_uint32("string length", ps, depth, &x ))
+ return False;
+
+ if (MARSHALLING(ps)) {
+ /* These are already in little endian format. Don't byte swap. */
+ if (x == 1) {
+
+ /* No memory allocated for this string
+ therefore following the data.string
+ pointer is a bad idea. Use a pointer to
+ the uint32 length union member to
+ provide a source for a unicode NULL */
+
+ if(!prs_uint8s(True,"string",ps,depth, (uint8 *)&data->notify_data.data.length,x*2))
+ return False;
+ } else {
+
+ if (data->field == 16)
+ x /= 2;
+
+ if(!prs_uint16uni(True,"string",ps,depth,data->notify_data.data.string,x))
+ return False;
+ }
+ } else {
+
+ /* Tallocate memory for string */
+
+ data->notify_data.data.string = (uint16 *)prs_alloc_mem(ps, x * 2);
+ if (!data->notify_data.data.string)
+ return False;
+
+ if(!prs_uint16uni(True,"string",ps,depth,data->notify_data.data.string,x))
+ return False;
+ }
+ }
+
+#endif
+
+#if 0 /* JERRY */
+ /* Win2k does not seem to put this parse align here */
+ if(!prs_align(ps))
+ return False;
+#endif
+
+ return True;
+}
+
+/*******************************************************************
+reads or writes an NOTIFY INFO structure.
+********************************************************************/
+
+static BOOL smb_io_notify_info(const char *desc, SPOOL_NOTIFY_INFO *info, prs_struct *ps, int depth)
+{
+ int i;
+
+ prs_debug(ps, depth, desc, "smb_io_notify_info");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("count", ps, depth, &info->count))
+ return False;
+ if(!prs_uint32("version", ps, depth, &info->version))
+ return False;
+ if(!prs_uint32("flags", ps, depth, &info->flags))
+ return False;
+ if(!prs_uint32("count", ps, depth, &info->count))
+ return False;
+
+ for (i=0;i<info->count;i++) {
+ if(!smb_io_notify_info_data(desc, &info->data[i], ps, depth))
+ return False;
+ }
+
+ /* now do the strings at the end of the stream */
+ for (i=0;i<info->count;i++) {
+ if(!smb_io_notify_info_data_strings(desc, &info->data[i], ps, depth))
+ return False;
+ }
+
+ return True;
+}
+
+/*******************************************************************
+********************************************************************/
+
+static BOOL spool_io_user_level_1(const char *desc, SPOOL_USER_1 *q_u, prs_struct *ps, int depth)
+{
+ prs_debug(ps, depth, desc, "");
+ depth++;
+
+ /* reading */
+ if (UNMARSHALLING(ps))
+ ZERO_STRUCTP(q_u);
+
+ if (!prs_align(ps))
+ return False;
+ if (!prs_uint32("size", ps, depth, &q_u->size))
+ return False;
+ if (!prs_uint32("client_name_ptr", ps, depth, &q_u->client_name_ptr))
+ return False;
+ if (!prs_uint32("user_name_ptr", ps, depth, &q_u->user_name_ptr))
+ return False;
+ if (!prs_uint32("build", ps, depth, &q_u->build))
+ return False;
+ if (!prs_uint32("major", ps, depth, &q_u->major))
+ return False;
+ if (!prs_uint32("minor", ps, depth, &q_u->minor))
+ return False;
+ if (!prs_uint32("processor", ps, depth, &q_u->processor))
+ return False;
+
+ if (!smb_io_unistr2("", &q_u->client_name, q_u->client_name_ptr, ps, depth))
+ return False;
+ if (!prs_align(ps))
+ return False;
+ if (!smb_io_unistr2("", &q_u->user_name, q_u->user_name_ptr, ps, depth))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+********************************************************************/
+
+static BOOL spool_io_user_level(const char *desc, SPOOL_USER_CTR *q_u, prs_struct *ps, int depth)
+{
+ if (q_u==NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "spool_io_user_level");
+ depth++;
+
+ if (!prs_align(ps))
+ return False;
+
+ /* From looking at many captures in ethereal, it looks like
+ the level and ptr fields should be transposed. -tpot */
+
+ if (!prs_uint32("level", ps, depth, &q_u->level))
+ return False;
+ if (!prs_uint32("ptr", ps, depth, &q_u->ptr))
+ return False;
+
+ switch (q_u->level) {
+ case 1:
+ if (!spool_io_user_level_1("", &q_u->user1, ps, depth))
+ return False;
+ break;
+ default:
+ return False;
+ }
+
+ return True;
+}
+
+/*******************************************************************
+ * read or write a DEVICEMODE struct.
+ * on reading allocate memory for the private member
+ ********************************************************************/
+
+#define DM_NUM_OPTIONAL_FIELDS 8
+
+BOOL spoolss_io_devmode(const char *desc, prs_struct *ps, int depth, DEVICEMODE *devmode)
+{
+ int available_space; /* size of the device mode left to parse */
+ /* only important on unmarshalling */
+ int i = 0;
+
+ struct optional_fields {
+ fstring name;
+ uint32* field;
+ } opt_fields[DM_NUM_OPTIONAL_FIELDS] = {
+ { "icmmethod", NULL },
+ { "icmintent", NULL },
+ { "mediatype", NULL },
+ { "dithertype", NULL },
+ { "reserved1", NULL },
+ { "reserved2", NULL },
+ { "panningwidth", NULL },
+ { "panningheight", NULL }
+ };
+
+ /* assign at run time to keep non-gcc compilers happy */
+
+ opt_fields[0].field = &devmode->icmmethod;
+ opt_fields[1].field = &devmode->icmintent;
+ opt_fields[2].field = &devmode->mediatype;
+ opt_fields[3].field = &devmode->dithertype;
+ opt_fields[4].field = &devmode->reserved1;
+ opt_fields[5].field = &devmode->reserved2;
+ opt_fields[6].field = &devmode->panningwidth;
+ opt_fields[7].field = &devmode->panningheight;
+
+
+ prs_debug(ps, depth, desc, "spoolss_io_devmode");
+ depth++;
+
+ if (UNMARSHALLING(ps)) {
+ devmode->devicename.buffer = (uint16 *)prs_alloc_mem(ps, 32 * sizeof(uint16) );
+ if (devmode->devicename.buffer == NULL)
+ return False;
+ }
+
+ if (!prs_uint16uni(True,"devicename", ps, depth, devmode->devicename.buffer, MAXDEVICENAME))
+ return False;
+
+ if (!prs_uint16("specversion", ps, depth, &devmode->specversion))
+ return False;
+
+ /* Sanity Check - look for unknown specversions, but don't fail if we see one.
+ Let the size determine that */
+
+ switch (devmode->specversion) {
+ /* list of observed spec version's */
+ case 0x0320:
+ case 0x0400:
+ case 0x0401:
+ case 0x040d:
+ break;
+
+ default:
+ DEBUG(0,("spoolss_io_devmode: Unknown specversion in devicemode [0x%x]\n",
+ devmode->specversion));
+ DEBUG(0,("spoolss_io_devmode: please report to samba-technical@samba.org!\n"));
+ break;
+ }
+
+
+ if (!prs_uint16("driverversion", ps, depth, &devmode->driverversion))
+ return False;
+ if (!prs_uint16("size", ps, depth, &devmode->size))
+ return False;
+ if (!prs_uint16("driverextra", ps, depth, &devmode->driverextra))
+ return False;
+ if (!prs_uint32("fields", ps, depth, &devmode->fields))
+ return False;
+ if (!prs_uint16("orientation", ps, depth, &devmode->orientation))
+ return False;
+ if (!prs_uint16("papersize", ps, depth, &devmode->papersize))
+ return False;
+ if (!prs_uint16("paperlength", ps, depth, &devmode->paperlength))
+ return False;
+ if (!prs_uint16("paperwidth", ps, depth, &devmode->paperwidth))
+ return False;
+ if (!prs_uint16("scale", ps, depth, &devmode->scale))
+ return False;
+ if (!prs_uint16("copies", ps, depth, &devmode->copies))
+ return False;
+ if (!prs_uint16("defaultsource", ps, depth, &devmode->defaultsource))
+ return False;
+ if (!prs_uint16("printquality", ps, depth, &devmode->printquality))
+ return False;
+ if (!prs_uint16("color", ps, depth, &devmode->color))
+ return False;
+ if (!prs_uint16("duplex", ps, depth, &devmode->duplex))
+ return False;
+ if (!prs_uint16("yresolution", ps, depth, &devmode->yresolution))
+ return False;
+ if (!prs_uint16("ttoption", ps, depth, &devmode->ttoption))
+ return False;
+ if (!prs_uint16("collate", ps, depth, &devmode->collate))
+ return False;
+
+ if (UNMARSHALLING(ps)) {
+ devmode->formname.buffer = (uint16 *)prs_alloc_mem(ps, 32 * sizeof(uint16) );
+ if (devmode->formname.buffer == NULL)
+ return False;
+ }
+
+ if (!prs_uint16uni(True, "formname", ps, depth, devmode->formname.buffer, 32))
+ return False;
+ if (!prs_uint16("logpixels", ps, depth, &devmode->logpixels))
+ return False;
+ if (!prs_uint32("bitsperpel", ps, depth, &devmode->bitsperpel))
+ return False;
+ if (!prs_uint32("pelswidth", ps, depth, &devmode->pelswidth))
+ return False;
+ if (!prs_uint32("pelsheight", ps, depth, &devmode->pelsheight))
+ return False;
+ if (!prs_uint32("displayflags", ps, depth, &devmode->displayflags))
+ return False;
+ if (!prs_uint32("displayfrequency", ps, depth, &devmode->displayfrequency))
+ return False;
+ /*
+ * every device mode I've ever seen on the wire at least has up
+ * to the displayfrequency field. --jerry (05-09-2002)
+ */
+
+ /* add uint32's + uint16's + two UNICODE strings */
+
+ available_space = devmode->size - (sizeof(uint32)*6 + sizeof(uint16)*18 + sizeof(uint16)*64);
+
+ /* Sanity check - we only have uint32's left tp parse */
+
+ if ( available_space && ((available_space % sizeof(uint32)) != 0) ) {
+ DEBUG(0,("spoolss_io_devmode: available_space [%d] no in multiple of 4 bytes (size = %d)!\n",
+ available_space, devmode->size));
+ DEBUG(0,("spoolss_io_devmode: please report to samba-technical@samba.org!\n"));
+ return False;
+ }
+
+ /*
+ * Conditional parsing. Assume that the DeviceMode has been
+ * zero'd by the caller.
+ */
+
+ while ((available_space > 0) && (i < DM_NUM_OPTIONAL_FIELDS))
+ {
+ DEBUG(11, ("spoolss_io_devmode: [%d] bytes left to parse in devmode\n", available_space));
+ if (!prs_uint32(opt_fields[i].name, ps, depth, opt_fields[i].field))
+ return False;
+ available_space -= sizeof(uint32);
+ i++;
+ }
+
+ /* Sanity Check - we should no available space at this point unless
+ MS changes the device mode structure */
+
+ if (available_space) {
+ DEBUG(0,("spoolss_io_devmode: I've parsed all I know and there is still stuff left|\n"));
+ DEBUG(0,("spoolss_io_devmode: available_space = [%d], devmode_size = [%d]!\n",
+ available_space, devmode->size));
+ DEBUG(0,("spoolss_io_devmode: please report to samba-technical@samba.org!\n"));
+ return False;
+ }
+
+
+ if (devmode->driverextra!=0) {
+ if (UNMARSHALLING(ps)) {
+ devmode->private=(uint8 *)prs_alloc_mem(ps, devmode->driverextra*sizeof(uint8));
+ if(devmode->private == NULL)
+ return False;
+ DEBUG(7,("spoolss_io_devmode: allocated memory [%d] for private\n",devmode->driverextra));
+ }
+
+ DEBUG(7,("spoolss_io_devmode: parsing [%d] bytes of private\n",devmode->driverextra));
+ if (!prs_uint8s(False, "private", ps, depth,
+ devmode->private, devmode->driverextra))
+ return False;
+ }
+
+ return True;
+}
+
+/*******************************************************************
+ Read or write a DEVICEMODE container
+********************************************************************/
+
+static BOOL spoolss_io_devmode_cont(const char *desc, DEVMODE_CTR *dm_c, prs_struct *ps, int depth)
+{
+ if (dm_c==NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "spoolss_io_devmode_cont");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if (!prs_uint32("size", ps, depth, &dm_c->size))
+ return False;
+
+ if (!prs_uint32("devmode_ptr", ps, depth, &dm_c->devmode_ptr))
+ return False;
+
+ if (dm_c->size==0 || dm_c->devmode_ptr==0) {
+ if (UNMARSHALLING(ps))
+ /* if while reading there is no DEVMODE ... */
+ dm_c->devmode=NULL;
+ return True;
+ }
+
+ /* so we have a DEVICEMODE to follow */
+ if (UNMARSHALLING(ps)) {
+ DEBUG(9,("Allocating memory for spoolss_io_devmode\n"));
+ dm_c->devmode=(DEVICEMODE *)prs_alloc_mem(ps,sizeof(DEVICEMODE));
+ if(dm_c->devmode == NULL)
+ return False;
+ }
+
+ /* this is bad code, shouldn't be there */
+ if (!prs_uint32("size", ps, depth, &dm_c->size))
+ return False;
+
+ if (!spoolss_io_devmode(desc, ps, depth, dm_c->devmode))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+********************************************************************/
+
+static BOOL spoolss_io_printer_default(const char *desc, PRINTER_DEFAULT *pd, prs_struct *ps, int depth)
+{
+ if (pd==NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "spoolss_io_printer_default");
+ depth++;
+
+ if (!prs_uint32("datatype_ptr", ps, depth, &pd->datatype_ptr))
+ return False;
+
+ if (!smb_io_unistr2("datatype", &pd->datatype, pd->datatype_ptr, ps,depth))
+ return False;
+
+ if (!prs_align(ps))
+ return False;
+
+ if (!spoolss_io_devmode_cont("", &pd->devmode_cont, ps, depth))
+ return False;
+
+ if (!prs_align(ps))
+ return False;
+
+ if (!prs_uint32("access_required", ps, depth, &pd->access_required))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ * init a structure.
+ ********************************************************************/
+
+BOOL make_spoolss_q_open_printer_ex(SPOOL_Q_OPEN_PRINTER_EX *q_u,
+ const fstring printername,
+ const fstring datatype,
+ uint32 access_required,
+ const fstring clientname,
+ const fstring user_name)
+{
+ DEBUG(5,("make_spoolss_q_open_printer_ex\n"));
+ q_u->printername_ptr = (printername!=NULL)?1:0;
+ init_unistr2(&q_u->printername, printername, UNI_STR_TERMINATE);
+
+ q_u->printer_default.datatype_ptr = 0;
+/*
+ q_u->printer_default.datatype_ptr = (datatype!=NULL)?1:0;
+ init_unistr2(&q_u->printer_default.datatype, datatype, UNI_FLAGS_NONE);
+*/
+ q_u->printer_default.devmode_cont.size=0;
+ q_u->printer_default.devmode_cont.devmode_ptr=0;
+ q_u->printer_default.devmode_cont.devmode=NULL;
+ q_u->printer_default.access_required=access_required;
+ q_u->user_switch=1;
+ q_u->user_ctr.level=1;
+ q_u->user_ctr.ptr=1;
+ q_u->user_ctr.user1.size=strlen(clientname)+strlen(user_name)+10;
+ q_u->user_ctr.user1.client_name_ptr = (clientname!=NULL)?1:0;
+ q_u->user_ctr.user1.user_name_ptr = (user_name!=NULL)?1:0;
+ q_u->user_ctr.user1.build=1381;
+ q_u->user_ctr.user1.major=2;
+ q_u->user_ctr.user1.minor=0;
+ q_u->user_ctr.user1.processor=0;
+ init_unistr2(&q_u->user_ctr.user1.client_name, clientname, UNI_STR_TERMINATE);
+ init_unistr2(&q_u->user_ctr.user1.user_name, user_name, UNI_STR_TERMINATE);
+
+ return True;
+}
+
+/*******************************************************************
+ * init a structure.
+ ********************************************************************/
+
+BOOL make_spoolss_q_addprinterex(
+ TALLOC_CTX *mem_ctx,
+ SPOOL_Q_ADDPRINTEREX *q_u,
+ const char *srv_name,
+ const char* clientname,
+ const char* user_name,
+ uint32 level,
+ PRINTER_INFO_CTR *ctr)
+{
+ DEBUG(5,("make_spoolss_q_addprinterex\n"));
+
+ if (!ctr) return False;
+
+ ZERO_STRUCTP(q_u);
+
+ q_u->server_name_ptr = (srv_name!=NULL)?1:0;
+ init_unistr2(&q_u->server_name, srv_name, UNI_FLAGS_NONE);
+
+ q_u->level = level;
+
+ q_u->info.level = level;
+ q_u->info.info_ptr = (ctr->printers_2!=NULL)?1:0;
+ switch (level) {
+ case 2:
+ /* init q_u->info.info2 from *info */
+ if (!make_spoolss_printer_info_2(mem_ctx, &q_u->info.info_2, ctr->printers_2)) {
+ DEBUG(0,("make_spoolss_q_addprinterex: Unable to fill SPOOL_Q_ADDPRINTEREX struct!\n"));
+ return False;
+ }
+ break;
+ default :
+ break;
+ }
+
+ q_u->user_switch=1;
+
+ q_u->user_ctr.level=1;
+ q_u->user_ctr.ptr=1;
+ q_u->user_ctr.user1.client_name_ptr = (clientname!=NULL)?1:0;
+ q_u->user_ctr.user1.user_name_ptr = (user_name!=NULL)?1:0;
+ q_u->user_ctr.user1.build=1381;
+ q_u->user_ctr.user1.major=2;
+ q_u->user_ctr.user1.minor=0;
+ q_u->user_ctr.user1.processor=0;
+ init_unistr2(&q_u->user_ctr.user1.client_name, clientname, UNI_STR_TERMINATE);
+ init_unistr2(&q_u->user_ctr.user1.user_name, user_name, UNI_STR_TERMINATE);
+ q_u->user_ctr.user1.size=q_u->user_ctr.user1.user_name.uni_str_len +
+ q_u->user_ctr.user1.client_name.uni_str_len + 2;
+
+ return True;
+}
+
+/*******************************************************************
+create a SPOOL_PRINTER_INFO_2 stuct from a PRINTER_INFO_2 struct
+*******************************************************************/
+
+BOOL make_spoolss_printer_info_2(TALLOC_CTX *mem_ctx, SPOOL_PRINTER_INFO_LEVEL_2 **spool_info2,
+ PRINTER_INFO_2 *info)
+{
+
+ SPOOL_PRINTER_INFO_LEVEL_2 *inf;
+
+ /* allocate the necessary memory */
+ if (!(inf=(SPOOL_PRINTER_INFO_LEVEL_2*)talloc(mem_ctx, sizeof(SPOOL_PRINTER_INFO_LEVEL_2)))) {
+ DEBUG(0,("make_spoolss_printer_info_2: Unable to allocate SPOOL_PRINTER_INFO_LEVEL_2 sruct!\n"));
+ return False;
+ }
+
+ inf->servername_ptr = (info->servername.buffer!=NULL)?1:0;
+ inf->printername_ptr = (info->printername.buffer!=NULL)?1:0;
+ inf->sharename_ptr = (info->sharename.buffer!=NULL)?1:0;
+ inf->portname_ptr = (info->portname.buffer!=NULL)?1:0;
+ inf->drivername_ptr = (info->drivername.buffer!=NULL)?1:0;
+ inf->comment_ptr = (info->comment.buffer!=NULL)?1:0;
+ inf->location_ptr = (info->location.buffer!=NULL)?1:0;
+ inf->devmode_ptr = (info->devmode!=NULL)?1:0;
+ inf->sepfile_ptr = (info->sepfile.buffer!=NULL)?1:0;
+ inf->printprocessor_ptr = (info->printprocessor.buffer!=NULL)?1:0;
+ inf->datatype_ptr = (info->datatype.buffer!=NULL)?1:0;
+ inf->parameters_ptr = (info->parameters.buffer!=NULL)?1:0;
+ inf->secdesc_ptr = (info->secdesc!=NULL)?1:0;
+ inf->attributes = info->attributes;
+ inf->priority = info->priority;
+ inf->default_priority = info->defaultpriority;
+ inf->starttime = info->starttime;
+ inf->untiltime = info->untiltime;
+ inf->cjobs = info->cjobs;
+ inf->averageppm = info->averageppm;
+ init_unistr2_from_unistr(&inf->servername, &info->servername);
+ init_unistr2_from_unistr(&inf->printername, &info->printername);
+ init_unistr2_from_unistr(&inf->sharename, &info->sharename);
+ init_unistr2_from_unistr(&inf->portname, &info->portname);
+ init_unistr2_from_unistr(&inf->drivername, &info->drivername);
+ init_unistr2_from_unistr(&inf->comment, &info->comment);
+ init_unistr2_from_unistr(&inf->location, &info->location);
+ init_unistr2_from_unistr(&inf->sepfile, &info->sepfile);
+ init_unistr2_from_unistr(&inf->printprocessor, &info->printprocessor);
+ init_unistr2_from_unistr(&inf->datatype, &info->datatype);
+ init_unistr2_from_unistr(&inf->parameters, &info->parameters);
+ init_unistr2_from_unistr(&inf->datatype, &info->datatype);
+
+ *spool_info2 = inf;
+
+ return True;
+}
+
+
+/*******************************************************************
+ * read a structure.
+ * called from spoolss_q_open_printer_ex (srv_spoolss.c)
+ ********************************************************************/
+
+BOOL spoolss_io_q_open_printer(const char *desc, SPOOL_Q_OPEN_PRINTER *q_u, prs_struct *ps, int depth)
+{
+ if (q_u == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "spoolss_io_q_open_printer");
+ depth++;
+
+ if (!prs_align(ps))
+ return False;
+
+ if (!prs_uint32("printername_ptr", ps, depth, &q_u->printername_ptr))
+ return False;
+ if (!smb_io_unistr2("", &q_u->printername, q_u->printername_ptr, ps,depth))
+ return False;
+
+ if (!prs_align(ps))
+ return False;
+
+ if (!spoolss_io_printer_default("", &q_u->printer_default, ps, depth))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ * write a structure.
+ * called from static spoolss_r_open_printer_ex (srv_spoolss.c)
+ * called from spoolss_open_printer_ex (cli_spoolss.c)
+ ********************************************************************/
+
+BOOL spoolss_io_r_open_printer(const char *desc, SPOOL_R_OPEN_PRINTER *r_u, prs_struct *ps, int depth)
+{
+ if (r_u == NULL) return False;
+
+ prs_debug(ps, depth, desc, "spoolss_io_r_open_printer");
+ depth++;
+
+ if (!prs_align(ps))
+ return False;
+
+ if (!smb_io_pol_hnd("printer handle",&(r_u->handle),ps,depth))
+ return False;
+
+ if (!prs_werror("status code", ps, depth, &(r_u->status)))
+ return False;
+
+ return True;
+}
+
+
+/*******************************************************************
+ * read a structure.
+ * called from spoolss_q_open_printer_ex (srv_spoolss.c)
+ ********************************************************************/
+
+BOOL spoolss_io_q_open_printer_ex(const char *desc, SPOOL_Q_OPEN_PRINTER_EX *q_u, prs_struct *ps, int depth)
+{
+ if (q_u == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "spoolss_io_q_open_printer_ex");
+ depth++;
+
+ if (!prs_align(ps))
+ return False;
+
+ if (!prs_uint32("printername_ptr", ps, depth, &q_u->printername_ptr))
+ return False;
+ if (!smb_io_unistr2("", &q_u->printername, q_u->printername_ptr, ps,depth))
+ return False;
+
+ if (!prs_align(ps))
+ return False;
+
+ if (!spoolss_io_printer_default("", &q_u->printer_default, ps, depth))
+ return False;
+
+ if (!prs_uint32("user_switch", ps, depth, &q_u->user_switch))
+ return False;
+ if (!spool_io_user_level("", &q_u->user_ctr, ps, depth))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ * write a structure.
+ * called from static spoolss_r_open_printer_ex (srv_spoolss.c)
+ * called from spoolss_open_printer_ex (cli_spoolss.c)
+ ********************************************************************/
+
+BOOL spoolss_io_r_open_printer_ex(const char *desc, SPOOL_R_OPEN_PRINTER_EX *r_u, prs_struct *ps, int depth)
+{
+ if (r_u == NULL) return False;
+
+ prs_debug(ps, depth, desc, "spoolss_io_r_open_printer_ex");
+ depth++;
+
+ if (!prs_align(ps))
+ return False;
+
+ if (!smb_io_pol_hnd("printer handle",&(r_u->handle),ps,depth))
+ return False;
+
+ if (!prs_werror("status code", ps, depth, &(r_u->status)))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ * init a structure.
+ ********************************************************************/
+BOOL make_spoolss_q_deleteprinterdriver(
+ TALLOC_CTX *mem_ctx,
+ SPOOL_Q_DELETEPRINTERDRIVER *q_u,
+ const char *server,
+ const char* arch,
+ const char* driver
+)
+{
+ DEBUG(5,("make_spoolss_q_deleteprinterdriver\n"));
+
+ q_u->server_ptr = (server!=NULL)?1:0;
+
+ /* these must be NULL terminated or else NT4 will
+ complain about invalid parameters --jerry */
+ init_unistr2(&q_u->server, server, UNI_STR_TERMINATE);
+ init_unistr2(&q_u->arch, arch, UNI_STR_TERMINATE);
+ init_unistr2(&q_u->driver, driver, UNI_STR_TERMINATE);
+
+ return True;
+}
+
+/*******************************************************************
+ * make a structure.
+ ********************************************************************/
+
+BOOL make_spoolss_q_getprinterdata(SPOOL_Q_GETPRINTERDATA *q_u,
+ const POLICY_HND *handle,
+ const char *valuename, uint32 size)
+{
+ if (q_u == NULL) return False;
+
+ DEBUG(5,("make_spoolss_q_getprinterdata\n"));
+
+ q_u->handle = *handle;
+ init_unistr2(&q_u->valuename, valuename, UNI_STR_TERMINATE);
+ q_u->size = size;
+
+ return True;
+}
+
+/*******************************************************************
+ * make a structure.
+ ********************************************************************/
+
+BOOL make_spoolss_q_getprinterdataex(SPOOL_Q_GETPRINTERDATAEX *q_u,
+ const POLICY_HND *handle,
+ const char *keyname,
+ const char *valuename, uint32 size)
+{
+ if (q_u == NULL) return False;
+
+ DEBUG(5,("make_spoolss_q_getprinterdataex\n"));
+
+ q_u->handle = *handle;
+ init_unistr2(&q_u->valuename, valuename, UNI_STR_TERMINATE);
+ init_unistr2(&q_u->keyname, keyname, UNI_STR_TERMINATE);
+ q_u->size = size;
+
+ return True;
+}
+
+/*******************************************************************
+ * read a structure.
+ * called from spoolss_q_getprinterdata (srv_spoolss.c)
+ ********************************************************************/
+
+BOOL spoolss_io_q_getprinterdata(const char *desc, SPOOL_Q_GETPRINTERDATA *q_u, prs_struct *ps, int depth)
+{
+ if (q_u == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "spoolss_io_q_getprinterdata");
+ depth++;
+
+ if (!prs_align(ps))
+ return False;
+ if (!smb_io_pol_hnd("printer handle",&q_u->handle,ps,depth))
+ return False;
+ if (!prs_align(ps))
+ return False;
+ if (!smb_io_unistr2("valuename", &q_u->valuename,True,ps,depth))
+ return False;
+ if (!prs_align(ps))
+ return False;
+ if (!prs_uint32("size", ps, depth, &q_u->size))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ * read a structure.
+ * called from spoolss_q_deleteprinterdata (srv_spoolss.c)
+ ********************************************************************/
+
+BOOL spoolss_io_q_deleteprinterdata(const char *desc, SPOOL_Q_DELETEPRINTERDATA *q_u, prs_struct *ps, int depth)
+{
+ if (q_u == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "spoolss_io_q_deleteprinterdata");
+ depth++;
+
+ if (!prs_align(ps))
+ return False;
+ if (!smb_io_pol_hnd("printer handle",&q_u->handle,ps,depth))
+ return False;
+ if (!prs_align(ps))
+ return False;
+ if (!smb_io_unistr2("valuename", &q_u->valuename,True,ps,depth))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ * write a structure.
+ * called from spoolss_r_deleteprinterdata (srv_spoolss.c)
+ ********************************************************************/
+
+BOOL spoolss_io_r_deleteprinterdata(const char *desc, SPOOL_R_DELETEPRINTERDATA *r_u, prs_struct *ps, int depth)
+{
+ prs_debug(ps, depth, desc, "spoolss_io_r_deleteprinterdata");
+ depth++;
+ if(!prs_werror("status", ps, depth, &r_u->status))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ * read a structure.
+ * called from spoolss_q_deleteprinterdataex (srv_spoolss.c)
+ ********************************************************************/
+
+BOOL spoolss_io_q_deleteprinterdataex(const char *desc, SPOOL_Q_DELETEPRINTERDATAEX *q_u, prs_struct *ps, int depth)
+{
+ if (q_u == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "spoolss_io_q_deleteprinterdataex");
+ depth++;
+
+ if (!prs_align(ps))
+ return False;
+ if (!smb_io_pol_hnd("printer handle", &q_u->handle, ps, depth))
+ return False;
+
+ if (!smb_io_unistr2("keyname ", &q_u->keyname, True, ps, depth))
+ return False;
+ if (!smb_io_unistr2("valuename", &q_u->valuename, True, ps, depth))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ * write a structure.
+ * called from spoolss_r_deleteprinterdataex (srv_spoolss.c)
+ ********************************************************************/
+
+BOOL spoolss_io_r_deleteprinterdataex(const char *desc, SPOOL_R_DELETEPRINTERDATAEX *r_u, prs_struct *ps, int depth)
+{
+ prs_debug(ps, depth, desc, "spoolss_io_r_deleteprinterdataex");
+ depth++;
+
+ if(!prs_werror("status", ps, depth, &r_u->status))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ * write a structure.
+ * called from spoolss_r_getprinterdata (srv_spoolss.c)
+ ********************************************************************/
+
+BOOL spoolss_io_r_getprinterdata(const char *desc, SPOOL_R_GETPRINTERDATA *r_u, prs_struct *ps, int depth)
+{
+ if (r_u == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "spoolss_io_r_getprinterdata");
+ depth++;
+
+ if (!prs_align(ps))
+ return False;
+ if (!prs_uint32("type", ps, depth, &r_u->type))
+ return False;
+ if (!prs_uint32("size", ps, depth, &r_u->size))
+ return False;
+
+ if (UNMARSHALLING(ps) && r_u->size) {
+ r_u->data = (unsigned char *)prs_alloc_mem(ps, r_u->size);
+ if(!r_u->data)
+ return False;
+ }
+
+ if (!prs_uint8s( False, "data", ps, depth, r_u->data, r_u->size ))
+ return False;
+
+ if (!prs_align(ps))
+ return False;
+
+ if (!prs_uint32("needed", ps, depth, &r_u->needed))
+ return False;
+ if (!prs_werror("status", ps, depth, &r_u->status))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ * make a structure.
+ ********************************************************************/
+
+BOOL make_spoolss_q_closeprinter(SPOOL_Q_CLOSEPRINTER *q_u, POLICY_HND *hnd)
+{
+ if (q_u == NULL) return False;
+
+ DEBUG(5,("make_spoolss_q_closeprinter\n"));
+
+ memcpy(&q_u->handle, hnd, sizeof(q_u->handle));
+
+ return True;
+}
+
+/*******************************************************************
+ * read a structure.
+ * called from static spoolss_q_abortprinter (srv_spoolss.c)
+ * called from spoolss_abortprinter (cli_spoolss.c)
+ ********************************************************************/
+
+BOOL spoolss_io_q_abortprinter(const char *desc, SPOOL_Q_ABORTPRINTER *q_u, prs_struct *ps, int depth)
+{
+ if (q_u == NULL) return False;
+
+ prs_debug(ps, depth, desc, "spoolss_io_q_abortprinter");
+ depth++;
+
+ if (!prs_align(ps))
+ return False;
+
+ if (!smb_io_pol_hnd("printer handle",&q_u->handle,ps,depth))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ * write a structure.
+ * called from spoolss_r_abortprinter (srv_spoolss.c)
+ ********************************************************************/
+
+BOOL spoolss_io_r_abortprinter(const char *desc, SPOOL_R_ABORTPRINTER *r_u, prs_struct *ps, int depth)
+{
+ prs_debug(ps, depth, desc, "spoolss_io_r_abortprinter");
+ depth++;
+ if(!prs_werror("status", ps, depth, &r_u->status))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ * read a structure.
+ * called from static spoolss_q_deleteprinter (srv_spoolss.c)
+ * called from spoolss_deleteprinter (cli_spoolss.c)
+ ********************************************************************/
+
+BOOL spoolss_io_q_deleteprinter(const char *desc, SPOOL_Q_DELETEPRINTER *q_u, prs_struct *ps, int depth)
+{
+ if (q_u == NULL) return False;
+
+ prs_debug(ps, depth, desc, "spoolss_io_q_deleteprinter");
+ depth++;
+
+ if (!prs_align(ps))
+ return False;
+
+ if (!smb_io_pol_hnd("printer handle",&q_u->handle,ps,depth))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ * write a structure.
+ * called from static spoolss_r_deleteprinter (srv_spoolss.c)
+ * called from spoolss_deleteprinter (cli_spoolss.c)
+ ********************************************************************/
+
+BOOL spoolss_io_r_deleteprinter(const char *desc, SPOOL_R_DELETEPRINTER *r_u, prs_struct *ps, int depth)
+{
+ prs_debug(ps, depth, desc, "spoolss_io_r_deleteprinter");
+ depth++;
+
+ if (!prs_align(ps))
+ return False;
+
+ if (!smb_io_pol_hnd("printer handle",&r_u->handle,ps,depth))
+ return False;
+ if (!prs_werror("status", ps, depth, &r_u->status))
+ return False;
+
+ return True;
+}
+
+
+/*******************************************************************
+ * read a structure.
+ * called from api_spoolss_deleteprinterdriver (srv_spoolss.c)
+ * called from spoolss_deleteprinterdriver (cli_spoolss.c)
+ ********************************************************************/
+
+BOOL spoolss_io_q_deleteprinterdriver(const char *desc, SPOOL_Q_DELETEPRINTERDRIVER *q_u, prs_struct *ps, int depth)
+{
+ if (q_u == NULL) return False;
+
+ prs_debug(ps, depth, desc, "spoolss_io_q_deleteprinterdriver");
+ depth++;
+
+ if (!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("server_ptr", ps, depth, &q_u->server_ptr))
+ return False;
+ if(!smb_io_unistr2("server", &q_u->server, q_u->server_ptr, ps, depth))
+ return False;
+ if(!smb_io_unistr2("arch", &q_u->arch, True, ps, depth))
+ return False;
+ if(!smb_io_unistr2("driver", &q_u->driver, True, ps, depth))
+ return False;
+
+
+ return True;
+}
+
+
+/*******************************************************************
+ * write a structure.
+ ********************************************************************/
+BOOL spoolss_io_r_deleteprinterdriver(const char *desc, SPOOL_R_DELETEPRINTERDRIVER *r_u, prs_struct *ps, int depth)
+{
+ if (r_u == NULL) return False;
+
+ prs_debug(ps, depth, desc, "spoolss_io_r_deleteprinterdriver");
+ depth++;
+
+ if (!prs_align(ps))
+ return False;
+
+ if (!prs_werror("status", ps, depth, &r_u->status))
+ return False;
+
+ return True;
+}
+
+
+/*******************************************************************
+ * read a structure.
+ * called from api_spoolss_deleteprinterdriver (srv_spoolss.c)
+ * called from spoolss_deleteprinterdriver (cli_spoolss.c)
+ ********************************************************************/
+
+BOOL spoolss_io_q_deleteprinterdriverex(const char *desc, SPOOL_Q_DELETEPRINTERDRIVEREX *q_u, prs_struct *ps, int depth)
+{
+ if (q_u == NULL) return False;
+
+ prs_debug(ps, depth, desc, "spoolss_io_q_deleteprinterdriverex");
+ depth++;
+
+ if (!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("server_ptr", ps, depth, &q_u->server_ptr))
+ return False;
+ if(!smb_io_unistr2("server", &q_u->server, q_u->server_ptr, ps, depth))
+ return False;
+ if(!smb_io_unistr2("arch", &q_u->arch, True, ps, depth))
+ return False;
+ if(!smb_io_unistr2("driver", &q_u->driver, True, ps, depth))
+ return False;
+
+ if (!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("delete_flags ", ps, depth, &q_u->delete_flags))
+ return False;
+ if(!prs_uint32("version ", ps, depth, &q_u->version))
+ return False;
+
+
+ return True;
+}
+
+
+/*******************************************************************
+ * write a structure.
+ ********************************************************************/
+BOOL spoolss_io_r_deleteprinterdriverex(const char *desc, SPOOL_R_DELETEPRINTERDRIVEREX *r_u, prs_struct *ps, int depth)
+{
+ if (r_u == NULL) return False;
+
+ prs_debug(ps, depth, desc, "spoolss_io_r_deleteprinterdriverex");
+ depth++;
+
+ if (!prs_align(ps))
+ return False;
+
+ if (!prs_werror("status", ps, depth, &r_u->status))
+ return False;
+
+ return True;
+}
+
+
+
+/*******************************************************************
+ * read a structure.
+ * called from static spoolss_q_closeprinter (srv_spoolss.c)
+ * called from spoolss_closeprinter (cli_spoolss.c)
+ ********************************************************************/
+
+BOOL spoolss_io_q_closeprinter(const char *desc, SPOOL_Q_CLOSEPRINTER *q_u, prs_struct *ps, int depth)
+{
+ if (q_u == NULL) return False;
+
+ prs_debug(ps, depth, desc, "spoolss_io_q_closeprinter");
+ depth++;
+
+ if (!prs_align(ps))
+ return False;
+
+ if (!smb_io_pol_hnd("printer handle",&q_u->handle,ps,depth))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ * write a structure.
+ * called from static spoolss_r_closeprinter (srv_spoolss.c)
+ * called from spoolss_closeprinter (cli_spoolss.c)
+ ********************************************************************/
+
+BOOL spoolss_io_r_closeprinter(const char *desc, SPOOL_R_CLOSEPRINTER *r_u, prs_struct *ps, int depth)
+{
+ prs_debug(ps, depth, desc, "spoolss_io_r_closeprinter");
+ depth++;
+
+ if (!prs_align(ps))
+ return False;
+
+ if (!smb_io_pol_hnd("printer handle",&r_u->handle,ps,depth))
+ return False;
+ if (!prs_werror("status", ps, depth, &r_u->status))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ * read a structure.
+ * called from spoolss_q_startdocprinter (srv_spoolss.c)
+ ********************************************************************/
+
+BOOL spoolss_io_q_startdocprinter(const char *desc, SPOOL_Q_STARTDOCPRINTER *q_u, prs_struct *ps, int depth)
+{
+ if (q_u == NULL) return False;
+
+ prs_debug(ps, depth, desc, "spoolss_io_q_startdocprinter");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!smb_io_pol_hnd("printer handle",&q_u->handle,ps,depth))
+ return False;
+
+ if(!smb_io_doc_info_container("",&q_u->doc_info_container, ps, depth))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ * write a structure.
+ * called from spoolss_r_startdocprinter (srv_spoolss.c)
+ ********************************************************************/
+
+BOOL spoolss_io_r_startdocprinter(const char *desc, SPOOL_R_STARTDOCPRINTER *r_u, prs_struct *ps, int depth)
+{
+ prs_debug(ps, depth, desc, "spoolss_io_r_startdocprinter");
+ depth++;
+ if(!prs_uint32("jobid", ps, depth, &r_u->jobid))
+ return False;
+ if(!prs_werror("status", ps, depth, &r_u->status))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ * read a structure.
+ * called from spoolss_q_enddocprinter (srv_spoolss.c)
+ ********************************************************************/
+
+BOOL spoolss_io_q_enddocprinter(const char *desc, SPOOL_Q_ENDDOCPRINTER *q_u, prs_struct *ps, int depth)
+{
+ if (q_u == NULL) return False;
+
+ prs_debug(ps, depth, desc, "spoolss_io_q_enddocprinter");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!smb_io_pol_hnd("printer handle",&q_u->handle,ps,depth))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ * write a structure.
+ * called from spoolss_r_enddocprinter (srv_spoolss.c)
+ ********************************************************************/
+
+BOOL spoolss_io_r_enddocprinter(const char *desc, SPOOL_R_ENDDOCPRINTER *r_u, prs_struct *ps, int depth)
+{
+ prs_debug(ps, depth, desc, "spoolss_io_r_enddocprinter");
+ depth++;
+ if(!prs_werror("status", ps, depth, &r_u->status))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ * read a structure.
+ * called from spoolss_q_startpageprinter (srv_spoolss.c)
+ ********************************************************************/
+
+BOOL spoolss_io_q_startpageprinter(const char *desc, SPOOL_Q_STARTPAGEPRINTER *q_u, prs_struct *ps, int depth)
+{
+ if (q_u == NULL) return False;
+
+ prs_debug(ps, depth, desc, "spoolss_io_q_startpageprinter");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!smb_io_pol_hnd("printer handle",&q_u->handle,ps,depth))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ * write a structure.
+ * called from spoolss_r_startpageprinter (srv_spoolss.c)
+ ********************************************************************/
+
+BOOL spoolss_io_r_startpageprinter(const char *desc, SPOOL_R_STARTPAGEPRINTER *r_u, prs_struct *ps, int depth)
+{
+ prs_debug(ps, depth, desc, "spoolss_io_r_startpageprinter");
+ depth++;
+ if(!prs_werror("status", ps, depth, &r_u->status))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ * read a structure.
+ * called from spoolss_q_endpageprinter (srv_spoolss.c)
+ ********************************************************************/
+
+BOOL spoolss_io_q_endpageprinter(const char *desc, SPOOL_Q_ENDPAGEPRINTER *q_u, prs_struct *ps, int depth)
+{
+ if (q_u == NULL) return False;
+
+ prs_debug(ps, depth, desc, "spoolss_io_q_endpageprinter");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!smb_io_pol_hnd("printer handle",&q_u->handle,ps,depth))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ * write a structure.
+ * called from spoolss_r_endpageprinter (srv_spoolss.c)
+ ********************************************************************/
+
+BOOL spoolss_io_r_endpageprinter(const char *desc, SPOOL_R_ENDPAGEPRINTER *r_u, prs_struct *ps, int depth)
+{
+ prs_debug(ps, depth, desc, "spoolss_io_r_endpageprinter");
+ depth++;
+ if(!prs_werror("status", ps, depth, &r_u->status))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ * read a structure.
+ * called from spoolss_q_writeprinter (srv_spoolss.c)
+ ********************************************************************/
+
+BOOL spoolss_io_q_writeprinter(const char *desc, SPOOL_Q_WRITEPRINTER *q_u, prs_struct *ps, int depth)
+{
+ if (q_u == NULL) return False;
+
+ prs_debug(ps, depth, desc, "spoolss_io_q_writeprinter");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!smb_io_pol_hnd("printer handle",&q_u->handle,ps,depth))
+ return False;
+ if(!prs_uint32("buffer_size", ps, depth, &q_u->buffer_size))
+ return False;
+
+ if (q_u->buffer_size!=0)
+ {
+ if (UNMARSHALLING(ps))
+ q_u->buffer=(uint8 *)prs_alloc_mem(ps,q_u->buffer_size*sizeof(uint8));
+ if(q_u->buffer == NULL)
+ return False;
+ if(!prs_uint8s(True, "buffer", ps, depth, q_u->buffer, q_u->buffer_size))
+ return False;
+ }
+ if(!prs_align(ps))
+ return False;
+ if(!prs_uint32("buffer_size2", ps, depth, &q_u->buffer_size2))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ * write a structure.
+ * called from spoolss_r_writeprinter (srv_spoolss.c)
+ ********************************************************************/
+
+BOOL spoolss_io_r_writeprinter(const char *desc, SPOOL_R_WRITEPRINTER *r_u, prs_struct *ps, int depth)
+{
+ prs_debug(ps, depth, desc, "spoolss_io_r_writeprinter");
+ depth++;
+ if(!prs_uint32("buffer_written", ps, depth, &r_u->buffer_written))
+ return False;
+ if(!prs_werror("status", ps, depth, &r_u->status))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ * read a structure.
+ * called from spoolss_q_rffpcnex (srv_spoolss.c)
+ ********************************************************************/
+
+BOOL spoolss_io_q_rffpcnex(const char *desc, SPOOL_Q_RFFPCNEX *q_u, prs_struct *ps, int depth)
+{
+ prs_debug(ps, depth, desc, "spoolss_io_q_rffpcnex");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!smb_io_pol_hnd("printer handle", &q_u->handle, ps, depth))
+ return False;
+ if(!prs_uint32("flags", ps, depth, &q_u->flags))
+ return False;
+ if(!prs_uint32("options", ps, depth, &q_u->options))
+ return False;
+ if(!prs_uint32("localmachine_ptr", ps, depth, &q_u->localmachine_ptr))
+ return False;
+ if(!smb_io_unistr2("localmachine", &q_u->localmachine, q_u->localmachine_ptr, ps, depth))
+ return False;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("printerlocal", ps, depth, &q_u->printerlocal))
+ return False;
+
+ if(!prs_uint32("option_ptr", ps, depth, &q_u->option_ptr))
+ return False;
+
+ if (q_u->option_ptr!=0) {
+
+ if (UNMARSHALLING(ps))
+ if((q_u->option=(SPOOL_NOTIFY_OPTION *)prs_alloc_mem(ps,sizeof(SPOOL_NOTIFY_OPTION))) == NULL)
+ return False;
+
+ if(!smb_io_notify_option("notify option", q_u->option, ps, depth))
+ return False;
+ }
+
+ return True;
+}
+
+/*******************************************************************
+ * write a structure.
+ * called from spoolss_r_rffpcnex (srv_spoolss.c)
+ ********************************************************************/
+
+BOOL spoolss_io_r_rffpcnex(const char *desc, SPOOL_R_RFFPCNEX *r_u, prs_struct *ps, int depth)
+{
+ prs_debug(ps, depth, desc, "spoolss_io_r_rffpcnex");
+ depth++;
+
+ if(!prs_werror("status", ps, depth, &r_u->status))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ * read a structure.
+ * called from spoolss_q_rfnpcnex (srv_spoolss.c)
+ ********************************************************************/
+
+BOOL spoolss_io_q_rfnpcnex(const char *desc, SPOOL_Q_RFNPCNEX *q_u, prs_struct *ps, int depth)
+{
+ prs_debug(ps, depth, desc, "spoolss_io_q_rfnpcnex");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!smb_io_pol_hnd("printer handle",&q_u->handle,ps,depth))
+ return False;
+
+ if(!prs_uint32("change", ps, depth, &q_u->change))
+ return False;
+
+ if(!prs_uint32("option_ptr", ps, depth, &q_u->option_ptr))
+ return False;
+
+ if (q_u->option_ptr!=0) {
+
+ if (UNMARSHALLING(ps))
+ if((q_u->option=(SPOOL_NOTIFY_OPTION *)prs_alloc_mem(ps,sizeof(SPOOL_NOTIFY_OPTION))) == NULL)
+ return False;
+
+ if(!smb_io_notify_option("notify option", q_u->option, ps, depth))
+ return False;
+ }
+
+ return True;
+}
+
+/*******************************************************************
+ * write a structure.
+ * called from spoolss_r_rfnpcnex (srv_spoolss.c)
+ ********************************************************************/
+
+BOOL spoolss_io_r_rfnpcnex(const char *desc, SPOOL_R_RFNPCNEX *r_u, prs_struct *ps, int depth)
+{
+ prs_debug(ps, depth, desc, "spoolss_io_r_rfnpcnex");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if (!prs_uint32("info_ptr", ps, depth, &r_u->info_ptr))
+ return False;
+
+ if(!smb_io_notify_info("notify info", &r_u->info ,ps,depth))
+ return False;
+
+ if(!prs_align(ps))
+ return False;
+ if(!prs_werror("status", ps, depth, &r_u->status))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ * return the length of a uint16 (obvious, but the code is clean)
+ ********************************************************************/
+
+static uint32 size_of_uint16(uint16 *value)
+{
+ return (sizeof(*value));
+}
+
+/*******************************************************************
+ * return the length of a uint32 (obvious, but the code is clean)
+ ********************************************************************/
+
+static uint32 size_of_uint32(uint32 *value)
+{
+ return (sizeof(*value));
+}
+
+/*******************************************************************
+ * return the length of a NTTIME (obvious, but the code is clean)
+ ********************************************************************/
+
+static uint32 size_of_nttime(NTTIME *value)
+{
+ return (sizeof(*value));
+}
+
+/*******************************************************************
+ * return the length of a UNICODE string in number of char, includes:
+ * - the leading zero
+ * - the relative pointer size
+ ********************************************************************/
+
+static uint32 size_of_relative_string(UNISTR *string)
+{
+ uint32 size=0;
+
+ size=str_len_uni(string); /* the string length */
+ size=size+1; /* add the trailing zero */
+ size=size*2; /* convert in char */
+ size=size+4; /* add the size of the ptr */
+
+#if 0 /* JERRY */
+ /*
+ * Do not include alignment as Win2k does not align relative
+ * strings within a buffer --jerry
+ */
+ /* Ensure size is 4 byte multiple (prs_align is being called...). */
+ /* size += ((4 - (size & 3)) & 3); */
+#endif
+
+ return size;
+}
+
+/*******************************************************************
+ * return the length of a uint32 (obvious, but the code is clean)
+ ********************************************************************/
+
+static uint32 size_of_device_mode(DEVICEMODE *devmode)
+{
+ if (devmode==NULL)
+ return (4);
+ else
+ return (4+devmode->size+devmode->driverextra);
+}
+
+/*******************************************************************
+ * return the length of a uint32 (obvious, but the code is clean)
+ ********************************************************************/
+
+static uint32 size_of_systemtime(SYSTEMTIME *systime)
+{
+ if (systime==NULL)
+ return (4);
+ else
+ return (sizeof(SYSTEMTIME) +4);
+}
+
+/*******************************************************************
+ * write a UNICODE string and its relative pointer.
+ * used by all the RPC structs passing a buffer
+ *
+ * As I'm a nice guy, I'm forcing myself to explain this code.
+ * MS did a good job in the overall spoolss code except in some
+ * functions where they are passing the API buffer directly in the
+ * RPC request/reply. That's to maintain compatiility at the API level.
+ * They could have done it the good way the first time.
+ *
+ * So what happen is: the strings are written at the buffer's end,
+ * in the reverse order of the original structure. Some pointers to
+ * the strings are also in the buffer. Those are relative to the
+ * buffer's start.
+ *
+ * If you don't understand or want to change that function,
+ * first get in touch with me: jfm@samba.org
+ *
+ ********************************************************************/
+
+static BOOL smb_io_relstr(const char *desc, NEW_BUFFER *buffer, int depth, UNISTR *string)
+{
+ prs_struct *ps=&buffer->prs;
+
+ if (MARSHALLING(ps)) {
+ uint32 struct_offset = prs_offset(ps);
+ uint32 relative_offset;
+
+ buffer->string_at_end -= (size_of_relative_string(string) - 4);
+ if(!prs_set_offset(ps, buffer->string_at_end))
+ return False;
+#if 0 /* JERRY */
+ /*
+ * Win2k does not align strings in a buffer
+ * Tested against WinNT 4.0 SP 6a & 2k SP2 --jerry
+ */
+ if (!prs_align(ps))
+ return False;
+#endif
+ buffer->string_at_end = prs_offset(ps);
+
+ /* write the string */
+ if (!smb_io_unistr(desc, string, ps, depth))
+ return False;
+
+ if(!prs_set_offset(ps, struct_offset))
+ return False;
+
+ relative_offset=buffer->string_at_end - buffer->struct_start;
+ /* write its offset */
+ if (!prs_uint32("offset", ps, depth, &relative_offset))
+ return False;
+ }
+ else {
+ uint32 old_offset;
+
+ /* read the offset */
+ if (!prs_uint32("offset", ps, depth, &(buffer->string_at_end)))
+ return False;
+
+ if (buffer->string_at_end == 0)
+ return True;
+
+ old_offset = prs_offset(ps);
+ if(!prs_set_offset(ps, buffer->string_at_end+buffer->struct_start))
+ return False;
+
+ /* read the string */
+ if (!smb_io_unistr(desc, string, ps, depth))
+ return False;
+
+ if(!prs_set_offset(ps, old_offset))
+ return False;
+ }
+ return True;
+}
+
+/*******************************************************************
+ * write a array of UNICODE strings and its relative pointer.
+ * used by 2 RPC structs
+ ********************************************************************/
+
+static BOOL smb_io_relarraystr(const char *desc, NEW_BUFFER *buffer, int depth, uint16 **string)
+{
+ UNISTR chaine;
+
+ prs_struct *ps=&buffer->prs;
+
+ if (MARSHALLING(ps)) {
+ uint32 struct_offset = prs_offset(ps);
+ uint32 relative_offset;
+ uint16 *p;
+ uint16 *q;
+ uint16 zero=0;
+ p=*string;
+ q=*string;
+
+ /* first write the last 0 */
+ buffer->string_at_end -= 2;
+ if(!prs_set_offset(ps, buffer->string_at_end))
+ return False;
+
+ if(!prs_uint16("leading zero", ps, depth, &zero))
+ return False;
+
+ while (p && (*p!=0)) {
+ while (*q!=0)
+ q++;
+
+ /* Yes this should be malloc not talloc. Don't change. */
+
+ chaine.buffer = malloc((q-p+1)*sizeof(uint16));
+ if (chaine.buffer == NULL)
+ return False;
+
+ memcpy(chaine.buffer, p, (q-p+1)*sizeof(uint16));
+
+ buffer->string_at_end -= (q-p+1)*sizeof(uint16);
+
+ if(!prs_set_offset(ps, buffer->string_at_end)) {
+ SAFE_FREE(chaine.buffer);
+ return False;
+ }
+
+ /* write the string */
+ if (!smb_io_unistr(desc, &chaine, ps, depth)) {
+ SAFE_FREE(chaine.buffer);
+ return False;
+ }
+ q++;
+ p=q;
+
+ SAFE_FREE(chaine.buffer);
+ }
+
+ if(!prs_set_offset(ps, struct_offset))
+ return False;
+
+ relative_offset=buffer->string_at_end - buffer->struct_start;
+ /* write its offset */
+ if (!prs_uint32("offset", ps, depth, &relative_offset))
+ return False;
+
+ } else {
+
+ /* UNMARSHALLING */
+
+ uint32 old_offset;
+ uint16 *chaine2=NULL;
+ int l_chaine=0;
+ int l_chaine2=0;
+ size_t realloc_size = 0;
+
+ *string=NULL;
+
+ /* read the offset */
+ if (!prs_uint32("offset", ps, depth, &buffer->string_at_end))
+ return False;
+
+ old_offset = prs_offset(ps);
+ if(!prs_set_offset(ps, buffer->string_at_end + buffer->struct_start))
+ return False;
+
+ do {
+ if (!smb_io_unistr(desc, &chaine, ps, depth))
+ return False;
+
+ l_chaine=str_len_uni(&chaine);
+
+ /* we're going to add two more bytes here in case this
+ is the last string in the array and we need to add
+ an extra NULL for termination */
+ if (l_chaine > 0)
+ {
+ uint16 *tc2;
+
+ realloc_size = (l_chaine2+l_chaine+2)*sizeof(uint16);
+
+ /* Yes this should be realloc - it's freed below. JRA */
+
+ if((tc2=(uint16 *)Realloc(chaine2, realloc_size)) == NULL) {
+ SAFE_FREE(chaine2);
+ return False;
+ }
+ else chaine2 = tc2;
+ memcpy(chaine2+l_chaine2, chaine.buffer, (l_chaine+1)*sizeof(uint16));
+ l_chaine2+=l_chaine+1;
+ }
+
+ } while(l_chaine!=0);
+
+ /* the end should be bould NULL terminated so add
+ the second one here */
+ if (chaine2)
+ {
+ chaine2[l_chaine2] = '\0';
+ *string=(uint16 *)talloc_memdup(prs_get_mem_context(ps),chaine2,realloc_size);
+ SAFE_FREE(chaine2);
+ }
+
+ if(!prs_set_offset(ps, old_offset))
+ return False;
+ }
+ return True;
+}
+
+/*******************************************************************
+ Parse a DEVMODE structure and its relative pointer.
+********************************************************************/
+
+static BOOL smb_io_relsecdesc(const char *desc, NEW_BUFFER *buffer, int depth, SEC_DESC **secdesc)
+{
+ prs_struct *ps= &buffer->prs;
+
+ prs_debug(ps, depth, desc, "smb_io_relsecdesc");
+ depth++;
+
+ if (MARSHALLING(ps)) {
+ uint32 struct_offset = prs_offset(ps);
+ uint32 relative_offset;
+
+ if (! *secdesc) {
+ relative_offset = 0;
+ if (!prs_uint32("offset", ps, depth, &relative_offset))
+ return False;
+ return True;
+ }
+
+ if (*secdesc != NULL) {
+ buffer->string_at_end -= sec_desc_size(*secdesc);
+
+ if(!prs_set_offset(ps, buffer->string_at_end))
+ return False;
+ /* write the secdesc */
+ if (!sec_io_desc(desc, secdesc, ps, depth))
+ return False;
+
+ if(!prs_set_offset(ps, struct_offset))
+ return False;
+ }
+
+ relative_offset=buffer->string_at_end - buffer->struct_start;
+ /* write its offset */
+
+ if (!prs_uint32("offset", ps, depth, &relative_offset))
+ return False;
+ } else {
+ uint32 old_offset;
+
+ /* read the offset */
+ if (!prs_uint32("offset", ps, depth, &buffer->string_at_end))
+ return False;
+
+ old_offset = prs_offset(ps);
+ if(!prs_set_offset(ps, buffer->string_at_end + buffer->struct_start))
+ return False;
+
+ /* read the sd */
+ if (!sec_io_desc(desc, secdesc, ps, depth))
+ return False;
+
+ if(!prs_set_offset(ps, old_offset))
+ return False;
+ }
+ return True;
+}
+
+/*******************************************************************
+ Parse a DEVMODE structure and its relative pointer.
+********************************************************************/
+
+static BOOL smb_io_reldevmode(const char *desc, NEW_BUFFER *buffer, int depth, DEVICEMODE **devmode)
+{
+ prs_struct *ps=&buffer->prs;
+
+ prs_debug(ps, depth, desc, "smb_io_reldevmode");
+ depth++;
+
+ if (MARSHALLING(ps)) {
+ uint32 struct_offset = prs_offset(ps);
+ uint32 relative_offset;
+
+ if (*devmode == NULL) {
+ relative_offset=0;
+ if (!prs_uint32("offset", ps, depth, &relative_offset))
+ return False;
+ DEBUG(8, ("boing, the devmode was NULL\n"));
+
+ return True;
+ }
+
+ buffer->string_at_end -= ((*devmode)->size + (*devmode)->driverextra);
+
+ if(!prs_set_offset(ps, buffer->string_at_end))
+ return False;
+
+ /* write the DEVMODE */
+ if (!spoolss_io_devmode(desc, ps, depth, *devmode))
+ return False;
+
+ if(!prs_set_offset(ps, struct_offset))
+ return False;
+
+ relative_offset=buffer->string_at_end - buffer->struct_start;
+ /* write its offset */
+ if (!prs_uint32("offset", ps, depth, &relative_offset))
+ return False;
+ }
+ else {
+ uint32 old_offset;
+
+ /* read the offset */
+ if (!prs_uint32("offset", ps, depth, &buffer->string_at_end))
+ return False;
+ if (buffer->string_at_end == 0) {
+ *devmode = NULL;
+ return True;
+ }
+
+ old_offset = prs_offset(ps);
+ if(!prs_set_offset(ps, buffer->string_at_end + buffer->struct_start))
+ return False;
+
+ /* read the string */
+ if((*devmode=(DEVICEMODE *)prs_alloc_mem(ps,sizeof(DEVICEMODE))) == NULL)
+ return False;
+ if (!spoolss_io_devmode(desc, ps, depth, *devmode))
+ return False;
+
+ if(!prs_set_offset(ps, old_offset))
+ return False;
+ }
+ return True;
+}
+
+/*******************************************************************
+ Parse a PRINTER_INFO_0 structure.
+********************************************************************/
+
+BOOL smb_io_printer_info_0(const char *desc, NEW_BUFFER *buffer, PRINTER_INFO_0 *info, int depth)
+{
+ prs_struct *ps=&buffer->prs;
+
+ prs_debug(ps, depth, desc, "smb_io_printer_info_0");
+ depth++;
+
+ buffer->struct_start=prs_offset(ps);
+
+ if (!smb_io_relstr("printername", buffer, depth, &info->printername))
+ return False;
+ if (!smb_io_relstr("servername", buffer, depth, &info->servername))
+ return False;
+
+ if(!prs_uint32("cjobs", ps, depth, &info->cjobs))
+ return False;
+ if(!prs_uint32("total_jobs", ps, depth, &info->total_jobs))
+ return False;
+ if(!prs_uint32("total_bytes", ps, depth, &info->total_bytes))
+ return False;
+
+ if(!prs_uint16("year", ps, depth, &info->year))
+ return False;
+ if(!prs_uint16("month", ps, depth, &info->month))
+ return False;
+ if(!prs_uint16("dayofweek", ps, depth, &info->dayofweek))
+ return False;
+ if(!prs_uint16("day", ps, depth, &info->day))
+ return False;
+ if(!prs_uint16("hour", ps, depth, &info->hour))
+ return False;
+ if(!prs_uint16("minute", ps, depth, &info->minute))
+ return False;
+ if(!prs_uint16("second", ps, depth, &info->second))
+ return False;
+ if(!prs_uint16("milliseconds", ps, depth, &info->milliseconds))
+ return False;
+
+ if(!prs_uint32("global_counter", ps, depth, &info->global_counter))
+ return False;
+ if(!prs_uint32("total_pages", ps, depth, &info->total_pages))
+ return False;
+
+ if(!prs_uint16("major_version", ps, depth, &info->major_version))
+ return False;
+ if(!prs_uint16("build_version", ps, depth, &info->build_version))
+ return False;
+ if(!prs_uint32("unknown7", ps, depth, &info->unknown7))
+ return False;
+ if(!prs_uint32("unknown8", ps, depth, &info->unknown8))
+ return False;
+ if(!prs_uint32("unknown9", ps, depth, &info->unknown9))
+ return False;
+ if(!prs_uint32("session_counter", ps, depth, &info->session_counter))
+ return False;
+ if(!prs_uint32("unknown11", ps, depth, &info->unknown11))
+ return False;
+ if(!prs_uint32("printer_errors", ps, depth, &info->printer_errors))
+ return False;
+ if(!prs_uint32("unknown13", ps, depth, &info->unknown13))
+ return False;
+ if(!prs_uint32("unknown14", ps, depth, &info->unknown14))
+ return False;
+ if(!prs_uint32("unknown15", ps, depth, &info->unknown15))
+ return False;
+ if(!prs_uint32("unknown16", ps, depth, &info->unknown16))
+ return False;
+ if(!prs_uint32("change_id", ps, depth, &info->change_id))
+ return False;
+ if(!prs_uint32("unknown18", ps, depth, &info->unknown18))
+ return False;
+ if(!prs_uint32("status" , ps, depth, &info->status))
+ return False;
+ if(!prs_uint32("unknown20", ps, depth, &info->unknown20))
+ return False;
+ if(!prs_uint32("c_setprinter", ps, depth, &info->c_setprinter))
+ return False;
+ if(!prs_uint16("unknown22", ps, depth, &info->unknown22))
+ return False;
+ if(!prs_uint16("unknown23", ps, depth, &info->unknown23))
+ return False;
+ if(!prs_uint16("unknown24", ps, depth, &info->unknown24))
+ return False;
+ if(!prs_uint16("unknown25", ps, depth, &info->unknown25))
+ return False;
+ if(!prs_uint16("unknown26", ps, depth, &info->unknown26))
+ return False;
+ if(!prs_uint16("unknown27", ps, depth, &info->unknown27))
+ return False;
+ if(!prs_uint16("unknown28", ps, depth, &info->unknown28))
+ return False;
+ if(!prs_uint16("unknown29", ps, depth, &info->unknown29))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ Parse a PRINTER_INFO_1 structure.
+********************************************************************/
+
+BOOL smb_io_printer_info_1(const char *desc, NEW_BUFFER *buffer, PRINTER_INFO_1 *info, int depth)
+{
+ prs_struct *ps=&buffer->prs;
+
+ prs_debug(ps, depth, desc, "smb_io_printer_info_1");
+ depth++;
+
+ buffer->struct_start=prs_offset(ps);
+
+ if (!prs_uint32("flags", ps, depth, &info->flags))
+ return False;
+ if (!smb_io_relstr("description", buffer, depth, &info->description))
+ return False;
+ if (!smb_io_relstr("name", buffer, depth, &info->name))
+ return False;
+ if (!smb_io_relstr("comment", buffer, depth, &info->comment))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ Parse a PRINTER_INFO_2 structure.
+********************************************************************/
+
+BOOL smb_io_printer_info_2(const char *desc, NEW_BUFFER *buffer, PRINTER_INFO_2 *info, int depth)
+{
+ prs_struct *ps=&buffer->prs;
+ uint32 dm_offset, sd_offset, current_offset;
+ uint32 dummy_value = 0, has_secdesc = 0;
+
+ prs_debug(ps, depth, desc, "smb_io_printer_info_2");
+ depth++;
+
+ buffer->struct_start=prs_offset(ps);
+
+ if (!smb_io_relstr("servername", buffer, depth, &info->servername))
+ return False;
+ if (!smb_io_relstr("printername", buffer, depth, &info->printername))
+ return False;
+ if (!smb_io_relstr("sharename", buffer, depth, &info->sharename))
+ return False;
+ if (!smb_io_relstr("portname", buffer, depth, &info->portname))
+ return False;
+ if (!smb_io_relstr("drivername", buffer, depth, &info->drivername))
+ return False;
+ if (!smb_io_relstr("comment", buffer, depth, &info->comment))
+ return False;
+ if (!smb_io_relstr("location", buffer, depth, &info->location))
+ return False;
+
+ /* save current offset and wind forwared by a uint32 */
+ dm_offset = prs_offset(ps);
+ if (!prs_uint32("devmode", ps, depth, &dummy_value))
+ return False;
+
+ if (!smb_io_relstr("sepfile", buffer, depth, &info->sepfile))
+ return False;
+ if (!smb_io_relstr("printprocessor", buffer, depth, &info->printprocessor))
+ return False;
+ if (!smb_io_relstr("datatype", buffer, depth, &info->datatype))
+ return False;
+ if (!smb_io_relstr("parameters", buffer, depth, &info->parameters))
+ return False;
+
+ /* save current offset for the sec_desc */
+ sd_offset = prs_offset(ps);
+ if (!prs_uint32("sec_desc", ps, depth, &has_secdesc))
+ return False;
+
+
+ /* save current location so we can pick back up here */
+ current_offset = prs_offset(ps);
+
+ /* parse the devmode */
+ if (!prs_set_offset(ps, dm_offset))
+ return False;
+ if (!smb_io_reldevmode("devmode", buffer, depth, &info->devmode))
+ return False;
+
+ /* parse the sec_desc */
+ if (info->secdesc) {
+ if (!prs_set_offset(ps, sd_offset))
+ return False;
+ if (!smb_io_relsecdesc("secdesc", buffer, depth, &info->secdesc))
+ return False;
+ }
+
+ /* pick up where we left off */
+ if (!prs_set_offset(ps, current_offset))
+ return False;
+
+ if (!prs_uint32("attributes", ps, depth, &info->attributes))
+ return False;
+ if (!prs_uint32("priority", ps, depth, &info->priority))
+ return False;
+ if (!prs_uint32("defpriority", ps, depth, &info->defaultpriority))
+ return False;
+ if (!prs_uint32("starttime", ps, depth, &info->starttime))
+ return False;
+ if (!prs_uint32("untiltime", ps, depth, &info->untiltime))
+ return False;
+ if (!prs_uint32("status", ps, depth, &info->status))
+ return False;
+ if (!prs_uint32("jobs", ps, depth, &info->cjobs))
+ return False;
+ if (!prs_uint32("averageppm", ps, depth, &info->averageppm))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ Parse a PRINTER_INFO_3 structure.
+********************************************************************/
+
+BOOL smb_io_printer_info_3(const char *desc, NEW_BUFFER *buffer, PRINTER_INFO_3 *info, int depth)
+{
+ prs_struct *ps=&buffer->prs;
+
+ prs_debug(ps, depth, desc, "smb_io_printer_info_3");
+ depth++;
+
+ buffer->struct_start=prs_offset(ps);
+
+ if (!prs_uint32("flags", ps, depth, &info->flags))
+ return False;
+ if (!sec_io_desc("sec_desc", &info->secdesc, ps, depth))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ Parse a PRINTER_INFO_4 structure.
+********************************************************************/
+
+BOOL smb_io_printer_info_4(const char *desc, NEW_BUFFER *buffer, PRINTER_INFO_4 *info, int depth)
+{
+ prs_struct *ps=&buffer->prs;
+
+ prs_debug(ps, depth, desc, "smb_io_printer_info_4");
+ depth++;
+
+ buffer->struct_start=prs_offset(ps);
+
+ if (!smb_io_relstr("printername", buffer, depth, &info->printername))
+ return False;
+ if (!smb_io_relstr("servername", buffer, depth, &info->servername))
+ return False;
+ if (!prs_uint32("attributes", ps, depth, &info->attributes))
+ return False;
+ return True;
+}
+
+/*******************************************************************
+ Parse a PRINTER_INFO_5 structure.
+********************************************************************/
+
+BOOL smb_io_printer_info_5(const char *desc, NEW_BUFFER *buffer, PRINTER_INFO_5 *info, int depth)
+{
+ prs_struct *ps=&buffer->prs;
+
+ prs_debug(ps, depth, desc, "smb_io_printer_info_5");
+ depth++;
+
+ buffer->struct_start=prs_offset(ps);
+
+ if (!smb_io_relstr("printername", buffer, depth, &info->printername))
+ return False;
+ if (!smb_io_relstr("portname", buffer, depth, &info->portname))
+ return False;
+ if (!prs_uint32("attributes", ps, depth, &info->attributes))
+ return False;
+ if (!prs_uint32("device_not_selected_timeout", ps, depth, &info->device_not_selected_timeout))
+ return False;
+ if (!prs_uint32("transmission_retry_timeout", ps, depth, &info->transmission_retry_timeout))
+ return False;
+ return True;
+}
+
+/*******************************************************************
+ Parse a PRINTER_INFO_7 structure.
+********************************************************************/
+
+BOOL smb_io_printer_info_7(const char *desc, NEW_BUFFER *buffer, PRINTER_INFO_7 *info, int depth)
+{
+ prs_struct *ps=&buffer->prs;
+
+ prs_debug(ps, depth, desc, "smb_io_printer_info_7");
+ depth++;
+
+ buffer->struct_start=prs_offset(ps);
+
+ if (!smb_io_relstr("guid", buffer, depth, &info->guid))
+ return False;
+ if (!prs_uint32("action", ps, depth, &info->action))
+ return False;
+ return True;
+}
+
+/*******************************************************************
+ Parse a PORT_INFO_1 structure.
+********************************************************************/
+
+BOOL smb_io_port_info_1(const char *desc, NEW_BUFFER *buffer, PORT_INFO_1 *info, int depth)
+{
+ prs_struct *ps=&buffer->prs;
+
+ prs_debug(ps, depth, desc, "smb_io_port_info_1");
+ depth++;
+
+ buffer->struct_start=prs_offset(ps);
+
+ if (!smb_io_relstr("port_name", buffer, depth, &info->port_name))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ Parse a PORT_INFO_2 structure.
+********************************************************************/
+
+BOOL smb_io_port_info_2(const char *desc, NEW_BUFFER *buffer, PORT_INFO_2 *info, int depth)
+{
+ prs_struct *ps=&buffer->prs;
+
+ prs_debug(ps, depth, desc, "smb_io_port_info_2");
+ depth++;
+
+ buffer->struct_start=prs_offset(ps);
+
+ if (!smb_io_relstr("port_name", buffer, depth, &info->port_name))
+ return False;
+ if (!smb_io_relstr("monitor_name", buffer, depth, &info->monitor_name))
+ return False;
+ if (!smb_io_relstr("description", buffer, depth, &info->description))
+ return False;
+ if (!prs_uint32("port_type", ps, depth, &info->port_type))
+ return False;
+ if (!prs_uint32("reserved", ps, depth, &info->reserved))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ Parse a DRIVER_INFO_1 structure.
+********************************************************************/
+
+BOOL smb_io_printer_driver_info_1(const char *desc, NEW_BUFFER *buffer, DRIVER_INFO_1 *info, int depth)
+{
+ prs_struct *ps=&buffer->prs;
+
+ prs_debug(ps, depth, desc, "smb_io_printer_driver_info_1");
+ depth++;
+
+ buffer->struct_start=prs_offset(ps);
+
+ if (!smb_io_relstr("name", buffer, depth, &info->name))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ Parse a DRIVER_INFO_2 structure.
+********************************************************************/
+
+BOOL smb_io_printer_driver_info_2(const char *desc, NEW_BUFFER *buffer, DRIVER_INFO_2 *info, int depth)
+{
+ prs_struct *ps=&buffer->prs;
+
+ prs_debug(ps, depth, desc, "smb_io_printer_driver_info_2");
+ depth++;
+
+ buffer->struct_start=prs_offset(ps);
+
+ if (!prs_uint32("version", ps, depth, &info->version))
+ return False;
+ if (!smb_io_relstr("name", buffer, depth, &info->name))
+ return False;
+ if (!smb_io_relstr("architecture", buffer, depth, &info->architecture))
+ return False;
+ if (!smb_io_relstr("driverpath", buffer, depth, &info->driverpath))
+ return False;
+ if (!smb_io_relstr("datafile", buffer, depth, &info->datafile))
+ return False;
+ if (!smb_io_relstr("configfile", buffer, depth, &info->configfile))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ Parse a DRIVER_INFO_3 structure.
+********************************************************************/
+
+BOOL smb_io_printer_driver_info_3(const char *desc, NEW_BUFFER *buffer, DRIVER_INFO_3 *info, int depth)
+{
+ prs_struct *ps=&buffer->prs;
+
+ prs_debug(ps, depth, desc, "smb_io_printer_driver_info_3");
+ depth++;
+
+ buffer->struct_start=prs_offset(ps);
+
+ if (!prs_uint32("version", ps, depth, &info->version))
+ return False;
+ if (!smb_io_relstr("name", buffer, depth, &info->name))
+ return False;
+ if (!smb_io_relstr("architecture", buffer, depth, &info->architecture))
+ return False;
+ if (!smb_io_relstr("driverpath", buffer, depth, &info->driverpath))
+ return False;
+ if (!smb_io_relstr("datafile", buffer, depth, &info->datafile))
+ return False;
+ if (!smb_io_relstr("configfile", buffer, depth, &info->configfile))
+ return False;
+ if (!smb_io_relstr("helpfile", buffer, depth, &info->helpfile))
+ return False;
+
+ if (!smb_io_relarraystr("dependentfiles", buffer, depth, &info->dependentfiles))
+ return False;
+
+ if (!smb_io_relstr("monitorname", buffer, depth, &info->monitorname))
+ return False;
+ if (!smb_io_relstr("defaultdatatype", buffer, depth, &info->defaultdatatype))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ Parse a DRIVER_INFO_6 structure.
+********************************************************************/
+
+BOOL smb_io_printer_driver_info_6(const char *desc, NEW_BUFFER *buffer, DRIVER_INFO_6 *info, int depth)
+{
+ prs_struct *ps=&buffer->prs;
+
+ prs_debug(ps, depth, desc, "smb_io_printer_driver_info_6");
+ depth++;
+
+ buffer->struct_start=prs_offset(ps);
+
+ if (!prs_uint32("version", ps, depth, &info->version))
+ return False;
+ if (!smb_io_relstr("name", buffer, depth, &info->name))
+ return False;
+ if (!smb_io_relstr("architecture", buffer, depth, &info->architecture))
+ return False;
+ if (!smb_io_relstr("driverpath", buffer, depth, &info->driverpath))
+ return False;
+ if (!smb_io_relstr("datafile", buffer, depth, &info->datafile))
+ return False;
+ if (!smb_io_relstr("configfile", buffer, depth, &info->configfile))
+ return False;
+ if (!smb_io_relstr("helpfile", buffer, depth, &info->helpfile))
+ return False;
+
+ if (!smb_io_relarraystr("dependentfiles", buffer, depth, &info->dependentfiles))
+ return False;
+
+ if (!smb_io_relstr("monitorname", buffer, depth, &info->monitorname))
+ return False;
+ if (!smb_io_relstr("defaultdatatype", buffer, depth, &info->defaultdatatype))
+ return False;
+
+ if (!smb_io_relarraystr("previousdrivernames", buffer, depth, &info->previousdrivernames))
+ return False;
+
+ if (!prs_uint32("date.low", ps, depth, &info->driver_date.low))
+ return False;
+ if (!prs_uint32("date.high", ps, depth, &info->driver_date.high))
+ return False;
+
+ if (!prs_uint32("padding", ps, depth, &info->padding))
+ return False;
+
+ if (!prs_uint32("driver_version_low", ps, depth, &info->driver_version_low))
+ return False;
+
+ if (!prs_uint32("driver_version_high", ps, depth, &info->driver_version_high))
+ return False;
+
+ if (!smb_io_relstr("mfgname", buffer, depth, &info->mfgname))
+ return False;
+ if (!smb_io_relstr("oem_url", buffer, depth, &info->oem_url))
+ return False;
+ if (!smb_io_relstr("hardware_id", buffer, depth, &info->hardware_id))
+ return False;
+ if (!smb_io_relstr("provider", buffer, depth, &info->provider))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ Parse a JOB_INFO_1 structure.
+********************************************************************/
+
+BOOL smb_io_job_info_1(const char *desc, NEW_BUFFER *buffer, JOB_INFO_1 *info, int depth)
+{
+ prs_struct *ps=&buffer->prs;
+
+ prs_debug(ps, depth, desc, "smb_io_job_info_1");
+ depth++;
+
+ buffer->struct_start=prs_offset(ps);
+
+ if (!prs_uint32("jobid", ps, depth, &info->jobid))
+ return False;
+ if (!smb_io_relstr("printername", buffer, depth, &info->printername))
+ return False;
+ if (!smb_io_relstr("machinename", buffer, depth, &info->machinename))
+ return False;
+ if (!smb_io_relstr("username", buffer, depth, &info->username))
+ return False;
+ if (!smb_io_relstr("document", buffer, depth, &info->document))
+ return False;
+ if (!smb_io_relstr("datatype", buffer, depth, &info->datatype))
+ return False;
+ if (!smb_io_relstr("text_status", buffer, depth, &info->text_status))
+ return False;
+ if (!prs_uint32("status", ps, depth, &info->status))
+ return False;
+ if (!prs_uint32("priority", ps, depth, &info->priority))
+ return False;
+ if (!prs_uint32("position", ps, depth, &info->position))
+ return False;
+ if (!prs_uint32("totalpages", ps, depth, &info->totalpages))
+ return False;
+ if (!prs_uint32("pagesprinted", ps, depth, &info->pagesprinted))
+ return False;
+ if (!spoolss_io_system_time("submitted", ps, depth, &info->submitted))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ Parse a JOB_INFO_2 structure.
+********************************************************************/
+
+BOOL smb_io_job_info_2(const char *desc, NEW_BUFFER *buffer, JOB_INFO_2 *info, int depth)
+{
+ uint32 pipo=0;
+ prs_struct *ps=&buffer->prs;
+
+ prs_debug(ps, depth, desc, "smb_io_job_info_2");
+ depth++;
+
+ buffer->struct_start=prs_offset(ps);
+
+ if (!prs_uint32("jobid",ps, depth, &info->jobid))
+ return False;
+ if (!smb_io_relstr("printername", buffer, depth, &info->printername))
+ return False;
+ if (!smb_io_relstr("machinename", buffer, depth, &info->machinename))
+ return False;
+ if (!smb_io_relstr("username", buffer, depth, &info->username))
+ return False;
+ if (!smb_io_relstr("document", buffer, depth, &info->document))
+ return False;
+ if (!smb_io_relstr("notifyname", buffer, depth, &info->notifyname))
+ return False;
+ if (!smb_io_relstr("datatype", buffer, depth, &info->datatype))
+ return False;
+
+ if (!smb_io_relstr("printprocessor", buffer, depth, &info->printprocessor))
+ return False;
+ if (!smb_io_relstr("parameters", buffer, depth, &info->parameters))
+ return False;
+ if (!smb_io_relstr("drivername", buffer, depth, &info->drivername))
+ return False;
+ if (!smb_io_reldevmode("devmode", buffer, depth, &info->devmode))
+ return False;
+ if (!smb_io_relstr("text_status", buffer, depth, &info->text_status))
+ return False;
+
+/* SEC_DESC sec_desc;*/
+ if (!prs_uint32("Hack! sec desc", ps, depth, &pipo))
+ return False;
+
+ if (!prs_uint32("status",ps, depth, &info->status))
+ return False;
+ if (!prs_uint32("priority",ps, depth, &info->priority))
+ return False;
+ if (!prs_uint32("position",ps, depth, &info->position))
+ return False;
+ if (!prs_uint32("starttime",ps, depth, &info->starttime))
+ return False;
+ if (!prs_uint32("untiltime",ps, depth, &info->untiltime))
+ return False;
+ if (!prs_uint32("totalpages",ps, depth, &info->totalpages))
+ return False;
+ if (!prs_uint32("size",ps, depth, &info->size))
+ return False;
+ if (!spoolss_io_system_time("submitted", ps, depth, &info->submitted) )
+ return False;
+ if (!prs_uint32("timeelapsed",ps, depth, &info->timeelapsed))
+ return False;
+ if (!prs_uint32("pagesprinted",ps, depth, &info->pagesprinted))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+********************************************************************/
+
+BOOL smb_io_form_1(const char *desc, NEW_BUFFER *buffer, FORM_1 *info, int depth)
+{
+ prs_struct *ps=&buffer->prs;
+
+ prs_debug(ps, depth, desc, "smb_io_form_1");
+ depth++;
+
+ buffer->struct_start=prs_offset(ps);
+
+ if (!prs_uint32("flag", ps, depth, &info->flag))
+ return False;
+
+ if (!smb_io_relstr("name", buffer, depth, &info->name))
+ return False;
+
+ if (!prs_uint32("width", ps, depth, &info->width))
+ return False;
+ if (!prs_uint32("length", ps, depth, &info->length))
+ return False;
+ if (!prs_uint32("left", ps, depth, &info->left))
+ return False;
+ if (!prs_uint32("top", ps, depth, &info->top))
+ return False;
+ if (!prs_uint32("right", ps, depth, &info->right))
+ return False;
+ if (!prs_uint32("bottom", ps, depth, &info->bottom))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ Read/write a BUFFER struct.
+********************************************************************/
+
+static BOOL spoolss_io_buffer(const char *desc, prs_struct *ps, int depth, NEW_BUFFER **pp_buffer)
+{
+ NEW_BUFFER *buffer = *pp_buffer;
+
+ prs_debug(ps, depth, desc, "spoolss_io_buffer");
+ depth++;
+
+ if (UNMARSHALLING(ps))
+ buffer = *pp_buffer = (NEW_BUFFER *)prs_alloc_mem(ps, sizeof(NEW_BUFFER));
+
+ if (buffer == NULL)
+ return False;
+
+ if (!prs_uint32("ptr", ps, depth, &buffer->ptr))
+ return False;
+
+ /* reading */
+ if (UNMARSHALLING(ps)) {
+ buffer->size=0;
+ buffer->string_at_end=0;
+
+ if (buffer->ptr==0) {
+ /*
+ * JRA. I'm not sure if the data in here is in big-endian format if
+ * the client is big-endian. Leave as default (little endian) for now.
+ */
+
+ if (!prs_init(&buffer->prs, 0, prs_get_mem_context(ps), UNMARSHALL))
+ return False;
+ return True;
+ }
+
+ if (!prs_uint32("size", ps, depth, &buffer->size))
+ return False;
+
+ /*
+ * JRA. I'm not sure if the data in here is in big-endian format if
+ * the client is big-endian. Leave as default (little endian) for now.
+ */
+
+ if (!prs_init(&buffer->prs, buffer->size, prs_get_mem_context(ps), UNMARSHALL))
+ return False;
+
+ if (!prs_append_some_prs_data(&buffer->prs, ps, prs_offset(ps), buffer->size))
+ return False;
+
+ if (!prs_set_offset(&buffer->prs, 0))
+ return False;
+
+ if (!prs_set_offset(ps, buffer->size+prs_offset(ps)))
+ return False;
+
+ buffer->string_at_end=buffer->size;
+
+ return True;
+ }
+ else {
+ BOOL ret = False;
+
+ /* writing */
+ if (buffer->ptr==0) {
+ /* We have finished with the data in buffer->prs - free it. */
+ prs_mem_free(&buffer->prs);
+ return True;
+ }
+
+ if (!prs_uint32("size", ps, depth, &buffer->size))
+ goto out;
+
+ if (!prs_append_some_prs_data(ps, &buffer->prs, 0, buffer->size))
+ goto out;
+
+ ret = True;
+ out:
+
+ /* We have finished with the data in buffer->prs - free it. */
+ prs_mem_free(&buffer->prs);
+
+ return ret;
+ }
+}
+
+/*******************************************************************
+ move a BUFFER from the query to the reply.
+ As the data pointers in NEW_BUFFER are malloc'ed, not talloc'ed,
+ this is ok. This is an OPTIMIZATION and is not strictly neccessary.
+ Clears the memory to zero also.
+********************************************************************/
+
+void spoolss_move_buffer(NEW_BUFFER *src, NEW_BUFFER **dest)
+{
+ prs_switch_type(&src->prs, MARSHALL);
+ if(!prs_set_offset(&src->prs, 0))
+ return;
+ prs_force_dynamic(&src->prs);
+ prs_mem_clear(&src->prs);
+ *dest=src;
+}
+
+/*******************************************************************
+ Get the size of a BUFFER struct.
+********************************************************************/
+
+uint32 new_get_buffer_size(NEW_BUFFER *buffer)
+{
+ return (buffer->size);
+}
+
+/*******************************************************************
+ Parse a DRIVER_DIRECTORY_1 structure.
+********************************************************************/
+
+BOOL smb_io_driverdir_1(const char *desc, NEW_BUFFER *buffer, DRIVER_DIRECTORY_1 *info, int depth)
+{
+ prs_struct *ps=&buffer->prs;
+
+ prs_debug(ps, depth, desc, "smb_io_driverdir_1");
+ depth++;
+
+ buffer->struct_start=prs_offset(ps);
+
+ if (!smb_io_unistr(desc, &info->name, ps, depth))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ Parse a PORT_INFO_1 structure.
+********************************************************************/
+
+BOOL smb_io_port_1(const char *desc, NEW_BUFFER *buffer, PORT_INFO_1 *info, int depth)
+{
+ prs_struct *ps=&buffer->prs;
+
+ prs_debug(ps, depth, desc, "smb_io_port_1");
+ depth++;
+
+ buffer->struct_start=prs_offset(ps);
+
+ if(!smb_io_relstr("port_name", buffer, depth, &info->port_name))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ Parse a PORT_INFO_2 structure.
+********************************************************************/
+
+BOOL smb_io_port_2(const char *desc, NEW_BUFFER *buffer, PORT_INFO_2 *info, int depth)
+{
+ prs_struct *ps=&buffer->prs;
+
+ prs_debug(ps, depth, desc, "smb_io_port_2");
+ depth++;
+
+ buffer->struct_start=prs_offset(ps);
+
+ if(!smb_io_relstr("port_name", buffer, depth, &info->port_name))
+ return False;
+ if(!smb_io_relstr("monitor_name", buffer, depth, &info->monitor_name))
+ return False;
+ if(!smb_io_relstr("description", buffer, depth, &info->description))
+ return False;
+ if(!prs_uint32("port_type", ps, depth, &info->port_type))
+ return False;
+ if(!prs_uint32("reserved", ps, depth, &info->reserved))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+********************************************************************/
+
+BOOL smb_io_printprocessor_info_1(const char *desc, NEW_BUFFER *buffer, PRINTPROCESSOR_1 *info, int depth)
+{
+ prs_struct *ps=&buffer->prs;
+
+ prs_debug(ps, depth, desc, "smb_io_printprocessor_info_1");
+ depth++;
+
+ buffer->struct_start=prs_offset(ps);
+
+ if (smb_io_relstr("name", buffer, depth, &info->name))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+********************************************************************/
+
+BOOL smb_io_printprocdatatype_info_1(const char *desc, NEW_BUFFER *buffer, PRINTPROCDATATYPE_1 *info, int depth)
+{
+ prs_struct *ps=&buffer->prs;
+
+ prs_debug(ps, depth, desc, "smb_io_printprocdatatype_info_1");
+ depth++;
+
+ buffer->struct_start=prs_offset(ps);
+
+ if (smb_io_relstr("name", buffer, depth, &info->name))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+********************************************************************/
+
+BOOL smb_io_printmonitor_info_1(const char *desc, NEW_BUFFER *buffer, PRINTMONITOR_1 *info, int depth)
+{
+ prs_struct *ps=&buffer->prs;
+
+ prs_debug(ps, depth, desc, "smb_io_printmonitor_info_1");
+ depth++;
+
+ buffer->struct_start=prs_offset(ps);
+
+ if (!smb_io_relstr("name", buffer, depth, &info->name))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+********************************************************************/
+
+BOOL smb_io_printmonitor_info_2(const char *desc, NEW_BUFFER *buffer, PRINTMONITOR_2 *info, int depth)
+{
+ prs_struct *ps=&buffer->prs;
+
+ prs_debug(ps, depth, desc, "smb_io_printmonitor_info_2");
+ depth++;
+
+ buffer->struct_start=prs_offset(ps);
+
+ if (!smb_io_relstr("name", buffer, depth, &info->name))
+ return False;
+ if (!smb_io_relstr("environment", buffer, depth, &info->environment))
+ return False;
+ if (!smb_io_relstr("dll_name", buffer, depth, &info->dll_name))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+return the size required by a struct in the stream
+********************************************************************/
+
+uint32 spoolss_size_printer_info_0(PRINTER_INFO_0 *info)
+{
+ int size=0;
+
+ size+=size_of_relative_string( &info->printername );
+ size+=size_of_relative_string( &info->servername );
+
+ size+=size_of_uint32( &info->cjobs);
+ size+=size_of_uint32( &info->total_jobs);
+ size+=size_of_uint32( &info->total_bytes);
+
+ size+=size_of_uint16( &info->year);
+ size+=size_of_uint16( &info->month);
+ size+=size_of_uint16( &info->dayofweek);
+ size+=size_of_uint16( &info->day);
+ size+=size_of_uint16( &info->hour);
+ size+=size_of_uint16( &info->minute);
+ size+=size_of_uint16( &info->second);
+ size+=size_of_uint16( &info->milliseconds);
+
+ size+=size_of_uint32( &info->global_counter);
+ size+=size_of_uint32( &info->total_pages);
+
+ size+=size_of_uint16( &info->major_version);
+ size+=size_of_uint16( &info->build_version);
+
+ size+=size_of_uint32( &info->unknown7);
+ size+=size_of_uint32( &info->unknown8);
+ size+=size_of_uint32( &info->unknown9);
+ size+=size_of_uint32( &info->session_counter);
+ size+=size_of_uint32( &info->unknown11);
+ size+=size_of_uint32( &info->printer_errors);
+ size+=size_of_uint32( &info->unknown13);
+ size+=size_of_uint32( &info->unknown14);
+ size+=size_of_uint32( &info->unknown15);
+ size+=size_of_uint32( &info->unknown16);
+ size+=size_of_uint32( &info->change_id);
+ size+=size_of_uint32( &info->unknown18);
+ size+=size_of_uint32( &info->status);
+ size+=size_of_uint32( &info->unknown20);
+ size+=size_of_uint32( &info->c_setprinter);
+
+ size+=size_of_uint16( &info->unknown22);
+ size+=size_of_uint16( &info->unknown23);
+ size+=size_of_uint16( &info->unknown24);
+ size+=size_of_uint16( &info->unknown25);
+ size+=size_of_uint16( &info->unknown26);
+ size+=size_of_uint16( &info->unknown27);
+ size+=size_of_uint16( &info->unknown28);
+ size+=size_of_uint16( &info->unknown29);
+
+ return size;
+}
+
+/*******************************************************************
+return the size required by a struct in the stream
+********************************************************************/
+
+uint32 spoolss_size_printer_info_1(PRINTER_INFO_1 *info)
+{
+ int size=0;
+
+ size+=size_of_uint32( &info->flags );
+ size+=size_of_relative_string( &info->description );
+ size+=size_of_relative_string( &info->name );
+ size+=size_of_relative_string( &info->comment );
+
+ return size;
+}
+
+/*******************************************************************
+return the size required by a struct in the stream
+********************************************************************/
+
+uint32 spoolss_size_printer_info_2(PRINTER_INFO_2 *info)
+{
+ uint32 size=0;
+
+ size += 4;
+
+ size += sec_desc_size( info->secdesc );
+
+ size+=size_of_device_mode( info->devmode );
+
+ size+=size_of_relative_string( &info->servername );
+ size+=size_of_relative_string( &info->printername );
+ size+=size_of_relative_string( &info->sharename );
+ size+=size_of_relative_string( &info->portname );
+ size+=size_of_relative_string( &info->drivername );
+ size+=size_of_relative_string( &info->comment );
+ size+=size_of_relative_string( &info->location );
+
+ size+=size_of_relative_string( &info->sepfile );
+ size+=size_of_relative_string( &info->printprocessor );
+ size+=size_of_relative_string( &info->datatype );
+ size+=size_of_relative_string( &info->parameters );
+
+ size+=size_of_uint32( &info->attributes );
+ size+=size_of_uint32( &info->priority );
+ size+=size_of_uint32( &info->defaultpriority );
+ size+=size_of_uint32( &info->starttime );
+ size+=size_of_uint32( &info->untiltime );
+ size+=size_of_uint32( &info->status );
+ size+=size_of_uint32( &info->cjobs );
+ size+=size_of_uint32( &info->averageppm );
+
+ /*
+ * add any adjustments for alignment. This is
+ * not optimal since we could be calling this
+ * function from a loop (e.g. enumprinters), but
+ * it is easier to maintain the calculation here and
+ * not place the burden on the caller to remember. --jerry
+ */
+ if ((size % 4) != 0)
+ size += 4 - (size % 4);
+
+ return size;
+}
+
+/*******************************************************************
+return the size required by a struct in the stream
+********************************************************************/
+
+uint32 spoolss_size_printer_info_4(PRINTER_INFO_4 *info)
+{
+ uint32 size=0;
+
+ size+=size_of_relative_string( &info->printername );
+ size+=size_of_relative_string( &info->servername );
+
+ size+=size_of_uint32( &info->attributes );
+ return size;
+}
+
+/*******************************************************************
+return the size required by a struct in the stream
+********************************************************************/
+
+uint32 spoolss_size_printer_info_5(PRINTER_INFO_5 *info)
+{
+ uint32 size=0;
+
+ size+=size_of_relative_string( &info->printername );
+ size+=size_of_relative_string( &info->portname );
+
+ size+=size_of_uint32( &info->attributes );
+ size+=size_of_uint32( &info->device_not_selected_timeout );
+ size+=size_of_uint32( &info->transmission_retry_timeout );
+ return size;
+}
+
+
+/*******************************************************************
+return the size required by a struct in the stream
+********************************************************************/
+
+uint32 spoolss_size_printer_info_3(PRINTER_INFO_3 *info)
+{
+ /* The 4 is for the self relative pointer.. */
+ /* JRA !!!! TESTME - WHAT ABOUT prs_align.... !!! */
+ return 4 + (uint32)sec_desc_size( info->secdesc );
+}
+
+/*******************************************************************
+return the size required by a struct in the stream
+********************************************************************/
+
+uint32 spoolss_size_printer_info_7(PRINTER_INFO_7 *info)
+{
+ uint32 size=0;
+
+ size+=size_of_relative_string( &info->guid );
+ size+=size_of_uint32( &info->action );
+ return size;
+}
+
+/*******************************************************************
+return the size required by a struct in the stream
+********************************************************************/
+
+uint32 spoolss_size_printer_driver_info_1(DRIVER_INFO_1 *info)
+{
+ int size=0;
+ size+=size_of_relative_string( &info->name );
+
+ return size;
+}
+
+/*******************************************************************
+return the size required by a struct in the stream
+********************************************************************/
+
+uint32 spoolss_size_printer_driver_info_2(DRIVER_INFO_2 *info)
+{
+ int size=0;
+ size+=size_of_uint32( &info->version );
+ size+=size_of_relative_string( &info->name );
+ size+=size_of_relative_string( &info->architecture );
+ size+=size_of_relative_string( &info->driverpath );
+ size+=size_of_relative_string( &info->datafile );
+ size+=size_of_relative_string( &info->configfile );
+
+ return size;
+}
+
+/*******************************************************************
+return the size required by a string array.
+********************************************************************/
+
+uint32 spoolss_size_string_array(uint16 *string)
+{
+ uint32 i = 0;
+
+ if (string) {
+ for (i=0; (string[i]!=0x0000) || (string[i+1]!=0x0000); i++);
+ }
+ i=i+2; /* to count all chars including the leading zero */
+ i=2*i; /* because we need the value in bytes */
+ i=i+4; /* the offset pointer size */
+
+ return i;
+}
+
+/*******************************************************************
+return the size required by a struct in the stream
+********************************************************************/
+
+uint32 spoolss_size_printer_driver_info_3(DRIVER_INFO_3 *info)
+{
+ int size=0;
+
+ size+=size_of_uint32( &info->version );
+ size+=size_of_relative_string( &info->name );
+ size+=size_of_relative_string( &info->architecture );
+ size+=size_of_relative_string( &info->driverpath );
+ size+=size_of_relative_string( &info->datafile );
+ size+=size_of_relative_string( &info->configfile );
+ size+=size_of_relative_string( &info->helpfile );
+ size+=size_of_relative_string( &info->monitorname );
+ size+=size_of_relative_string( &info->defaultdatatype );
+
+ size+=spoolss_size_string_array(info->dependentfiles);
+
+ return size;
+}
+
+/*******************************************************************
+return the size required by a struct in the stream
+********************************************************************/
+
+uint32 spoolss_size_printer_driver_info_6(DRIVER_INFO_6 *info)
+{
+ uint32 size=0;
+
+ size+=size_of_uint32( &info->version );
+ size+=size_of_relative_string( &info->name );
+ size+=size_of_relative_string( &info->architecture );
+ size+=size_of_relative_string( &info->driverpath );
+ size+=size_of_relative_string( &info->datafile );
+ size+=size_of_relative_string( &info->configfile );
+ size+=size_of_relative_string( &info->helpfile );
+
+ size+=spoolss_size_string_array(info->dependentfiles);
+
+ size+=size_of_relative_string( &info->monitorname );
+ size+=size_of_relative_string( &info->defaultdatatype );
+
+ size+=spoolss_size_string_array(info->previousdrivernames);
+
+ size+=size_of_nttime(&info->driver_date);
+ size+=size_of_uint32( &info->padding );
+ size+=size_of_uint32( &info->driver_version_low );
+ size+=size_of_uint32( &info->driver_version_high );
+ size+=size_of_relative_string( &info->mfgname );
+ size+=size_of_relative_string( &info->oem_url );
+ size+=size_of_relative_string( &info->hardware_id );
+ size+=size_of_relative_string( &info->provider );
+
+ return size;
+}
+
+/*******************************************************************
+return the size required by a struct in the stream
+********************************************************************/
+
+uint32 spoolss_size_job_info_1(JOB_INFO_1 *info)
+{
+ int size=0;
+ size+=size_of_uint32( &info->jobid );
+ size+=size_of_relative_string( &info->printername );
+ size+=size_of_relative_string( &info->machinename );
+ size+=size_of_relative_string( &info->username );
+ size+=size_of_relative_string( &info->document );
+ size+=size_of_relative_string( &info->datatype );
+ size+=size_of_relative_string( &info->text_status );
+ size+=size_of_uint32( &info->status );
+ size+=size_of_uint32( &info->priority );
+ size+=size_of_uint32( &info->position );
+ size+=size_of_uint32( &info->totalpages );
+ size+=size_of_uint32( &info->pagesprinted );
+ size+=size_of_systemtime( &info->submitted );
+
+ return size;
+}
+
+/*******************************************************************
+return the size required by a struct in the stream
+********************************************************************/
+
+uint32 spoolss_size_job_info_2(JOB_INFO_2 *info)
+{
+ int size=0;
+
+ size+=4; /* size of sec desc ptr */
+
+ size+=size_of_uint32( &info->jobid );
+ size+=size_of_relative_string( &info->printername );
+ size+=size_of_relative_string( &info->machinename );
+ size+=size_of_relative_string( &info->username );
+ size+=size_of_relative_string( &info->document );
+ size+=size_of_relative_string( &info->notifyname );
+ size+=size_of_relative_string( &info->datatype );
+ size+=size_of_relative_string( &info->printprocessor );
+ size+=size_of_relative_string( &info->parameters );
+ size+=size_of_relative_string( &info->drivername );
+ size+=size_of_device_mode( info->devmode );
+ size+=size_of_relative_string( &info->text_status );
+/* SEC_DESC sec_desc;*/
+ size+=size_of_uint32( &info->status );
+ size+=size_of_uint32( &info->priority );
+ size+=size_of_uint32( &info->position );
+ size+=size_of_uint32( &info->starttime );
+ size+=size_of_uint32( &info->untiltime );
+ size+=size_of_uint32( &info->totalpages );
+ size+=size_of_uint32( &info->size );
+ size+=size_of_systemtime( &info->submitted );
+ size+=size_of_uint32( &info->timeelapsed );
+ size+=size_of_uint32( &info->pagesprinted );
+
+ return size;
+}
+
+/*******************************************************************
+return the size required by a struct in the stream
+********************************************************************/
+
+uint32 spoolss_size_form_1(FORM_1 *info)
+{
+ int size=0;
+
+ size+=size_of_uint32( &info->flag );
+ size+=size_of_relative_string( &info->name );
+ size+=size_of_uint32( &info->width );
+ size+=size_of_uint32( &info->length );
+ size+=size_of_uint32( &info->left );
+ size+=size_of_uint32( &info->top );
+ size+=size_of_uint32( &info->right );
+ size+=size_of_uint32( &info->bottom );
+
+ return size;
+}
+
+/*******************************************************************
+return the size required by a struct in the stream
+********************************************************************/
+
+uint32 spoolss_size_port_info_1(PORT_INFO_1 *info)
+{
+ int size=0;
+
+ size+=size_of_relative_string( &info->port_name );
+
+ return size;
+}
+
+/*******************************************************************
+return the size required by a struct in the stream
+********************************************************************/
+
+uint32 spoolss_size_driverdir_info_1(DRIVER_DIRECTORY_1 *info)
+{
+ int size=0;
+
+ size=str_len_uni(&info->name); /* the string length */
+ size=size+1; /* add the leading zero */
+ size=size*2; /* convert in char */
+
+ return size;
+}
+
+/*******************************************************************
+return the size required by a struct in the stream
+********************************************************************/
+
+uint32 spoolss_size_printprocessordirectory_info_1(PRINTPROCESSOR_DIRECTORY_1 *info)
+{
+ int size=0;
+
+ size=str_len_uni(&info->name); /* the string length */
+ size=size+1; /* add the leading zero */
+ size=size*2; /* convert in char */
+
+ return size;
+}
+
+/*******************************************************************
+return the size required by a struct in the stream
+********************************************************************/
+
+uint32 spoolss_size_port_info_2(PORT_INFO_2 *info)
+{
+ int size=0;
+
+ size+=size_of_relative_string( &info->port_name );
+ size+=size_of_relative_string( &info->monitor_name );
+ size+=size_of_relative_string( &info->description );
+
+ size+=size_of_uint32( &info->port_type );
+ size+=size_of_uint32( &info->reserved );
+
+ return size;
+}
+
+/*******************************************************************
+return the size required by a struct in the stream
+********************************************************************/
+
+uint32 spoolss_size_printprocessor_info_1(PRINTPROCESSOR_1 *info)
+{
+ int size=0;
+ size+=size_of_relative_string( &info->name );
+
+ return size;
+}
+
+/*******************************************************************
+return the size required by a struct in the stream
+********************************************************************/
+
+uint32 spoolss_size_printprocdatatype_info_1(PRINTPROCDATATYPE_1 *info)
+{
+ int size=0;
+ size+=size_of_relative_string( &info->name );
+
+ return size;
+}
+
+/*******************************************************************
+return the size required by a struct in the stream
+********************************************************************/
+uint32 spoolss_size_printer_enum_values(PRINTER_ENUM_VALUES *p)
+{
+ uint32 size = 0;
+
+ if (!p)
+ return 0;
+
+ /* uint32(offset) + uint32(length) + length) */
+ size += (size_of_uint32(&p->value_len)*2) + p->value_len;
+ size += (size_of_uint32(&p->data_len)*2) + p->data_len + (p->data_len%2) ;
+
+ size += size_of_uint32(&p->type);
+
+ return size;
+}
+
+/*******************************************************************
+return the size required by a struct in the stream
+********************************************************************/
+
+uint32 spoolss_size_printmonitor_info_1(PRINTMONITOR_1 *info)
+{
+ int size=0;
+ size+=size_of_relative_string( &info->name );
+
+ return size;
+}
+
+/*******************************************************************
+return the size required by a struct in the stream
+********************************************************************/
+
+uint32 spoolss_size_printmonitor_info_2(PRINTMONITOR_2 *info)
+{
+ int size=0;
+ size+=size_of_relative_string( &info->name);
+ size+=size_of_relative_string( &info->environment);
+ size+=size_of_relative_string( &info->dll_name);
+
+ return size;
+}
+
+/*******************************************************************
+ * init a structure.
+ ********************************************************************/
+
+BOOL make_spoolss_q_getprinterdriver2(SPOOL_Q_GETPRINTERDRIVER2 *q_u,
+ const POLICY_HND *hnd,
+ const fstring architecture,
+ uint32 level, uint32 clientmajor, uint32 clientminor,
+ NEW_BUFFER *buffer, uint32 offered)
+{
+ if (q_u == NULL)
+ return False;
+
+ memcpy(&q_u->handle, hnd, sizeof(q_u->handle));
+
+ init_buf_unistr2(&q_u->architecture, &q_u->architecture_ptr, architecture);
+
+ q_u->level=level;
+ q_u->clientmajorversion=clientmajor;
+ q_u->clientminorversion=clientminor;
+
+ q_u->buffer=buffer;
+ q_u->offered=offered;
+
+ return True;
+}
+
+/*******************************************************************
+ * read a structure.
+ * called from spoolss_getprinterdriver2 (srv_spoolss.c)
+ ********************************************************************/
+
+BOOL spoolss_io_q_getprinterdriver2(const char *desc, SPOOL_Q_GETPRINTERDRIVER2 *q_u, prs_struct *ps, int depth)
+{
+ prs_debug(ps, depth, desc, "spoolss_io_q_getprinterdriver2");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!smb_io_pol_hnd("printer handle", &q_u->handle, ps, depth))
+ return False;
+ if(!prs_uint32("architecture_ptr", ps, depth, &q_u->architecture_ptr))
+ return False;
+ if(!smb_io_unistr2("architecture", &q_u->architecture, q_u->architecture_ptr, ps, depth))
+ return False;
+
+ if(!prs_align(ps))
+ return False;
+ if(!prs_uint32("level", ps, depth, &q_u->level))
+ return False;
+
+ if(!spoolss_io_buffer("", ps, depth, &q_u->buffer))
+ return False;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("offered", ps, depth, &q_u->offered))
+ return False;
+
+ if(!prs_uint32("clientmajorversion", ps, depth, &q_u->clientmajorversion))
+ return False;
+ if(!prs_uint32("clientminorversion", ps, depth, &q_u->clientminorversion))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ * read a structure.
+ * called from spoolss_getprinterdriver2 (srv_spoolss.c)
+ ********************************************************************/
+
+BOOL spoolss_io_r_getprinterdriver2(const char *desc, SPOOL_R_GETPRINTERDRIVER2 *r_u, prs_struct *ps, int depth)
+{
+ prs_debug(ps, depth, desc, "spoolss_io_r_getprinterdriver2");
+ depth++;
+
+ if (!prs_align(ps))
+ return False;
+
+ if (!spoolss_io_buffer("", ps, depth, &r_u->buffer))
+ return False;
+
+ if (!prs_align(ps))
+ return False;
+ if (!prs_uint32("needed", ps, depth, &r_u->needed))
+ return False;
+ if (!prs_uint32("servermajorversion", ps, depth, &r_u->servermajorversion))
+ return False;
+ if (!prs_uint32("serverminorversion", ps, depth, &r_u->serverminorversion))
+ return False;
+ if (!prs_werror("status", ps, depth, &r_u->status))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ * init a structure.
+ ********************************************************************/
+
+BOOL make_spoolss_q_enumprinters(
+ SPOOL_Q_ENUMPRINTERS *q_u,
+ uint32 flags,
+ char *servername,
+ uint32 level,
+ NEW_BUFFER *buffer,
+ uint32 offered
+)
+{
+ q_u->flags=flags;
+
+ q_u->servername_ptr = (servername != NULL) ? 1 : 0;
+ init_buf_unistr2(&q_u->servername, &q_u->servername_ptr, servername);
+
+ q_u->level=level;
+ q_u->buffer=buffer;
+ q_u->offered=offered;
+
+ return True;
+}
+
+/*******************************************************************
+ * init a structure.
+ ********************************************************************/
+
+BOOL make_spoolss_q_enumports(SPOOL_Q_ENUMPORTS *q_u,
+ fstring servername, uint32 level,
+ NEW_BUFFER *buffer, uint32 offered)
+{
+ q_u->name_ptr = (servername != NULL) ? 1 : 0;
+ init_buf_unistr2(&q_u->name, &q_u->name_ptr, servername);
+
+ q_u->level=level;
+ q_u->buffer=buffer;
+ q_u->offered=offered;
+
+ return True;
+}
+
+/*******************************************************************
+ * read a structure.
+ * called from spoolss_enumprinters (srv_spoolss.c)
+ ********************************************************************/
+
+BOOL spoolss_io_q_enumprinters(const char *desc, SPOOL_Q_ENUMPRINTERS *q_u, prs_struct *ps, int depth)
+{
+ prs_debug(ps, depth, desc, "spoolss_io_q_enumprinters");
+ depth++;
+
+ if (!prs_align(ps))
+ return False;
+
+ if (!prs_uint32("flags", ps, depth, &q_u->flags))
+ return False;
+ if (!prs_uint32("servername_ptr", ps, depth, &q_u->servername_ptr))
+ return False;
+
+ if (!smb_io_unistr2("", &q_u->servername, q_u->servername_ptr, ps, depth))
+ return False;
+
+ if (!prs_align(ps))
+ return False;
+ if (!prs_uint32("level", ps, depth, &q_u->level))
+ return False;
+
+ if (!spoolss_io_buffer("", ps, depth, &q_u->buffer))
+ return False;
+
+ if (!prs_align(ps))
+ return False;
+ if (!prs_uint32("offered", ps, depth, &q_u->offered))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ Parse a SPOOL_R_ENUMPRINTERS structure.
+ ********************************************************************/
+
+BOOL spoolss_io_r_enumprinters(const char *desc, SPOOL_R_ENUMPRINTERS *r_u, prs_struct *ps, int depth)
+{
+ prs_debug(ps, depth, desc, "spoolss_io_r_enumprinters");
+ depth++;
+
+ if (!prs_align(ps))
+ return False;
+
+ if (!spoolss_io_buffer("", ps, depth, &r_u->buffer))
+ return False;
+
+ if (!prs_align(ps))
+ return False;
+
+ if (!prs_uint32("needed", ps, depth, &r_u->needed))
+ return False;
+
+ if (!prs_uint32("returned", ps, depth, &r_u->returned))
+ return False;
+
+ if (!prs_werror("status", ps, depth, &r_u->status))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ * write a structure.
+ * called from spoolss_r_enum_printers (srv_spoolss.c)
+ *
+ ********************************************************************/
+
+BOOL spoolss_io_r_getprinter(const char *desc, SPOOL_R_GETPRINTER *r_u, prs_struct *ps, int depth)
+{
+ prs_debug(ps, depth, desc, "spoolss_io_r_getprinter");
+ depth++;
+
+ if (!prs_align(ps))
+ return False;
+
+ if (!spoolss_io_buffer("", ps, depth, &r_u->buffer))
+ return False;
+
+ if (!prs_align(ps))
+ return False;
+
+ if (!prs_uint32("needed", ps, depth, &r_u->needed))
+ return False;
+
+ if (!prs_werror("status", ps, depth, &r_u->status))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ * read a structure.
+ * called from spoolss_getprinter (srv_spoolss.c)
+ ********************************************************************/
+
+BOOL spoolss_io_q_getprinter(const char *desc, SPOOL_Q_GETPRINTER *q_u, prs_struct *ps, int depth)
+{
+ prs_debug(ps, depth, desc, "spoolss_io_q_getprinter");
+ depth++;
+
+ if (!prs_align(ps))
+ return False;
+
+ if(!smb_io_pol_hnd("printer handle", &q_u->handle, ps, depth))
+ return False;
+ if (!prs_uint32("level", ps, depth, &q_u->level))
+ return False;
+
+ if (!spoolss_io_buffer("", ps, depth, &q_u->buffer))
+ return False;
+
+ if (!prs_align(ps))
+ return False;
+ if (!prs_uint32("offered", ps, depth, &q_u->offered))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ * init a structure.
+ ********************************************************************/
+
+BOOL make_spoolss_q_getprinter(
+ TALLOC_CTX *mem_ctx,
+ SPOOL_Q_GETPRINTER *q_u,
+ const POLICY_HND *hnd,
+ uint32 level,
+ NEW_BUFFER *buffer,
+ uint32 offered
+)
+{
+ if (q_u == NULL)
+ {
+ return False;
+ }
+ memcpy(&q_u->handle, hnd, sizeof(q_u->handle));
+
+ q_u->level=level;
+ q_u->buffer=buffer;
+ q_u->offered=offered;
+
+ return True;
+}
+
+/*******************************************************************
+ * init a structure.
+ ********************************************************************/
+BOOL make_spoolss_q_setprinter(TALLOC_CTX *mem_ctx, SPOOL_Q_SETPRINTER *q_u,
+ const POLICY_HND *hnd, uint32 level, PRINTER_INFO_CTR *info,
+ uint32 command)
+{
+ SEC_DESC *secdesc;
+ DEVICEMODE *devmode;
+
+ if (q_u == NULL)
+ return False;
+
+ memcpy(&q_u->handle, hnd, sizeof(q_u->handle));
+
+ q_u->level = level;
+ q_u->info.level = level;
+ q_u->info.info_ptr = (info != NULL) ? 1 : 0;
+ switch (level) {
+
+ /* There's no such thing as a setprinter level 1 */
+
+ case 2:
+ secdesc = info->printers_2->secdesc;
+ devmode = info->printers_2->devmode;
+
+ make_spoolss_printer_info_2 (mem_ctx, &q_u->info.info_2, info->printers_2);
+#if 1 /* JERRY TEST */
+ q_u->secdesc_ctr = (SEC_DESC_BUF*)malloc(sizeof(SEC_DESC_BUF));
+ if (!q_u->secdesc_ctr)
+ return False;
+ q_u->secdesc_ctr->ptr = (secdesc != NULL) ? 1: 0;
+ q_u->secdesc_ctr->max_len = (secdesc) ? sizeof(SEC_DESC) + (2*sizeof(uint32)) : 0;
+ q_u->secdesc_ctr->len = (secdesc) ? sizeof(SEC_DESC) + (2*sizeof(uint32)) : 0;
+ q_u->secdesc_ctr->sec = secdesc;
+
+ q_u->devmode_ctr.devmode_ptr = (devmode != NULL) ? 1 : 0;
+ q_u->devmode_ctr.size = (devmode != NULL) ? sizeof(DEVICEMODE) + (3*sizeof(uint32)) : 0;
+ q_u->devmode_ctr.devmode = devmode;
+#else
+ q_u->secdesc_ctr = NULL;
+
+ q_u->devmode_ctr.devmode_ptr = 0;
+ q_u->devmode_ctr.size = 0;
+ q_u->devmode_ctr.devmode = NULL;
+#endif
+ break;
+ default:
+ DEBUG(0,("make_spoolss_q_setprinter: Unknown info level [%d]\n", level));
+ break;
+ }
+
+
+ q_u->command = command;
+
+ return True;
+}
+
+
+/*******************************************************************
+********************************************************************/
+
+BOOL spoolss_io_r_setprinter(const char *desc, SPOOL_R_SETPRINTER *r_u, prs_struct *ps, int depth)
+{
+ prs_debug(ps, depth, desc, "spoolss_io_r_setprinter");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_werror("status", ps, depth, &r_u->status))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ Marshall/unmarshall a SPOOL_Q_SETPRINTER struct.
+********************************************************************/
+
+BOOL spoolss_io_q_setprinter(const char *desc, SPOOL_Q_SETPRINTER *q_u, prs_struct *ps, int depth)
+{
+ uint32 ptr_sec_desc = 0;
+
+ prs_debug(ps, depth, desc, "spoolss_io_q_setprinter");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!smb_io_pol_hnd("printer handle", &q_u->handle ,ps, depth))
+ return False;
+ if(!prs_uint32("level", ps, depth, &q_u->level))
+ return False;
+
+ if(!spool_io_printer_info_level("", &q_u->info, ps, depth))
+ return False;
+
+ if (!spoolss_io_devmode_cont(desc, &q_u->devmode_ctr, ps, depth))
+ return False;
+
+ if(!prs_align(ps))
+ return False;
+
+ switch (q_u->level)
+ {
+ case 2:
+ {
+ ptr_sec_desc = q_u->info.info_2->secdesc_ptr;
+ break;
+ }
+ case 3:
+ {
+ ptr_sec_desc = q_u->info.info_3->secdesc_ptr;
+ break;
+ }
+ }
+ if (ptr_sec_desc)
+ {
+ if (!sec_io_desc_buf(desc, &q_u->secdesc_ctr, ps, depth))
+ return False;
+ } else {
+ uint32 dummy = 0;
+
+ /* Parse a NULL security descriptor. This should really
+ happen inside the sec_io_desc_buf() function. */
+
+ prs_debug(ps, depth, "", "sec_io_desc_buf");
+ if (!prs_uint32("size", ps, depth + 1, &dummy))
+ return False;
+ if (!prs_uint32("ptr", ps, depth + 1, &dummy)) return
+ False;
+ }
+
+ if(!prs_uint32("command", ps, depth, &q_u->command))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+********************************************************************/
+
+BOOL spoolss_io_r_fcpn(const char *desc, SPOOL_R_FCPN *r_u, prs_struct *ps, int depth)
+{
+ prs_debug(ps, depth, desc, "spoolss_io_r_fcpn");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_werror("status", ps, depth, &r_u->status))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+********************************************************************/
+
+BOOL spoolss_io_q_fcpn(const char *desc, SPOOL_Q_FCPN *q_u, prs_struct *ps, int depth)
+{
+
+ prs_debug(ps, depth, desc, "spoolss_io_q_fcpn");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!smb_io_pol_hnd("printer handle",&q_u->handle,ps,depth))
+ return False;
+
+ return True;
+}
+
+
+/*******************************************************************
+********************************************************************/
+
+BOOL spoolss_io_r_addjob(const char *desc, SPOOL_R_ADDJOB *r_u, prs_struct *ps, int depth)
+{
+ prs_debug(ps, depth, desc, "");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!spoolss_io_buffer("", ps, depth, &r_u->buffer))
+ return False;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("needed", ps, depth, &r_u->needed))
+ return False;
+
+ if(!prs_werror("status", ps, depth, &r_u->status))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+********************************************************************/
+
+BOOL spoolss_io_q_addjob(const char *desc, SPOOL_Q_ADDJOB *q_u, prs_struct *ps, int depth)
+{
+ prs_debug(ps, depth, desc, "");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!smb_io_pol_hnd("printer handle", &q_u->handle, ps, depth))
+ return False;
+ if(!prs_uint32("level", ps, depth, &q_u->level))
+ return False;
+
+ if(!spoolss_io_buffer("", ps, depth, &q_u->buffer))
+ return False;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("offered", ps, depth, &q_u->offered))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+********************************************************************/
+
+BOOL spoolss_io_r_enumjobs(const char *desc, SPOOL_R_ENUMJOBS *r_u, prs_struct *ps, int depth)
+{
+ prs_debug(ps, depth, desc, "spoolss_io_r_enumjobs");
+ depth++;
+
+ if (!prs_align(ps))
+ return False;
+
+ if (!spoolss_io_buffer("", ps, depth, &r_u->buffer))
+ return False;
+
+ if (!prs_align(ps))
+ return False;
+
+ if (!prs_uint32("needed", ps, depth, &r_u->needed))
+ return False;
+
+ if (!prs_uint32("returned", ps, depth, &r_u->returned))
+ return False;
+
+ if (!prs_werror("status", ps, depth, &r_u->status))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+********************************************************************/
+
+BOOL make_spoolss_q_enumjobs(SPOOL_Q_ENUMJOBS *q_u, const POLICY_HND *hnd,
+ uint32 firstjob,
+ uint32 numofjobs,
+ uint32 level,
+ NEW_BUFFER *buffer,
+ uint32 offered)
+{
+ if (q_u == NULL)
+ {
+ return False;
+ }
+ memcpy(&q_u->handle, hnd, sizeof(q_u->handle));
+ q_u->firstjob = firstjob;
+ q_u->numofjobs = numofjobs;
+ q_u->level = level;
+ q_u->buffer= buffer;
+ q_u->offered = offered;
+ return True;
+}
+
+/*******************************************************************
+********************************************************************/
+
+BOOL spoolss_io_q_enumjobs(const char *desc, SPOOL_Q_ENUMJOBS *q_u, prs_struct *ps, int depth)
+{
+ prs_debug(ps, depth, desc, "spoolss_io_q_enumjobs");
+ depth++;
+
+ if (!prs_align(ps))
+ return False;
+
+ if (!smb_io_pol_hnd("printer handle",&q_u->handle, ps, depth))
+ return False;
+
+ if (!prs_uint32("firstjob", ps, depth, &q_u->firstjob))
+ return False;
+ if (!prs_uint32("numofjobs", ps, depth, &q_u->numofjobs))
+ return False;
+ if (!prs_uint32("level", ps, depth, &q_u->level))
+ return False;
+
+ if (!spoolss_io_buffer("", ps, depth, &q_u->buffer))
+ return False;
+
+ if(!prs_align(ps))
+ return False;
+
+ if (!prs_uint32("offered", ps, depth, &q_u->offered))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+********************************************************************/
+
+BOOL spoolss_io_r_schedulejob(const char *desc, SPOOL_R_SCHEDULEJOB *r_u, prs_struct *ps, int depth)
+{
+ prs_debug(ps, depth, desc, "spoolss_io_r_schedulejob");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_werror("status", ps, depth, &r_u->status))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+********************************************************************/
+
+BOOL spoolss_io_q_schedulejob(const char *desc, SPOOL_Q_SCHEDULEJOB *q_u, prs_struct *ps, int depth)
+{
+ prs_debug(ps, depth, desc, "spoolss_io_q_schedulejob");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!smb_io_pol_hnd("printer handle",&q_u->handle,ps,depth))
+ return False;
+ if(!prs_uint32("jobid", ps, depth, &q_u->jobid))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+********************************************************************/
+
+BOOL spoolss_io_r_setjob(const char *desc, SPOOL_R_SETJOB *r_u, prs_struct *ps, int depth)
+{
+ prs_debug(ps, depth, desc, "spoolss_io_r_setjob");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_werror("status", ps, depth, &r_u->status))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+********************************************************************/
+
+BOOL spoolss_io_q_setjob(const char *desc, SPOOL_Q_SETJOB *q_u, prs_struct *ps, int depth)
+{
+ prs_debug(ps, depth, desc, "spoolss_io_q_setjob");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!smb_io_pol_hnd("printer handle",&q_u->handle,ps,depth))
+ return False;
+ if(!prs_uint32("jobid", ps, depth, &q_u->jobid))
+ return False;
+ /*
+ * level is usually 0. If (level!=0) then I'm in trouble !
+ * I will try to generate setjob command with level!=0, one day.
+ */
+ if(!prs_uint32("level", ps, depth, &q_u->level))
+ return False;
+ if(!prs_uint32("command", ps, depth, &q_u->command))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ Parse a SPOOL_R_ENUMPRINTERDRIVERS structure.
+********************************************************************/
+
+BOOL spoolss_io_r_enumprinterdrivers(const char *desc, SPOOL_R_ENUMPRINTERDRIVERS *r_u, prs_struct *ps, int depth)
+{
+ prs_debug(ps, depth, desc, "spoolss_io_r_enumprinterdrivers");
+ depth++;
+
+ if (!prs_align(ps))
+ return False;
+
+ if (!spoolss_io_buffer("", ps, depth, &r_u->buffer))
+ return False;
+
+ if (!prs_align(ps))
+ return False;
+
+ if (!prs_uint32("needed", ps, depth, &r_u->needed))
+ return False;
+
+ if (!prs_uint32("returned", ps, depth, &r_u->returned))
+ return False;
+
+ if (!prs_werror("status", ps, depth, &r_u->status))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ * init a structure.
+ ********************************************************************/
+
+BOOL make_spoolss_q_enumprinterdrivers(SPOOL_Q_ENUMPRINTERDRIVERS *q_u,
+ const char *name,
+ const char *environment,
+ uint32 level,
+ NEW_BUFFER *buffer, uint32 offered)
+{
+ init_buf_unistr2(&q_u->name, &q_u->name_ptr, name);
+ init_buf_unistr2(&q_u->environment, &q_u->environment_ptr, environment);
+
+ q_u->level=level;
+ q_u->buffer=buffer;
+ q_u->offered=offered;
+
+ return True;
+}
+
+/*******************************************************************
+ Parse a SPOOL_Q_ENUMPRINTERDRIVERS structure.
+********************************************************************/
+
+BOOL spoolss_io_q_enumprinterdrivers(const char *desc, SPOOL_Q_ENUMPRINTERDRIVERS *q_u, prs_struct *ps, int depth)
+{
+
+ prs_debug(ps, depth, desc, "spoolss_io_q_enumprinterdrivers");
+ depth++;
+
+ if (!prs_align(ps))
+ return False;
+
+ if (!prs_uint32("name_ptr", ps, depth, &q_u->name_ptr))
+ return False;
+ if (!smb_io_unistr2("", &q_u->name, q_u->name_ptr,ps, depth))
+ return False;
+
+ if (!prs_align(ps))
+ return False;
+ if (!prs_uint32("environment_ptr", ps, depth, &q_u->environment_ptr))
+ return False;
+ if (!smb_io_unistr2("", &q_u->environment, q_u->environment_ptr, ps, depth))
+ return False;
+
+ if (!prs_align(ps))
+ return False;
+ if (!prs_uint32("level", ps, depth, &q_u->level))
+ return False;
+
+ if (!spoolss_io_buffer("", ps, depth, &q_u->buffer))
+ return False;
+
+ if (!prs_align(ps))
+ return False;
+
+ if (!prs_uint32("offered", ps, depth, &q_u->offered))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+********************************************************************/
+
+BOOL spoolss_io_q_enumforms(const char *desc, SPOOL_Q_ENUMFORMS *q_u, prs_struct *ps, int depth)
+{
+
+ prs_debug(ps, depth, desc, "spoolss_io_q_enumforms");
+ depth++;
+
+ if (!prs_align(ps))
+ return False;
+ if (!smb_io_pol_hnd("printer handle",&q_u->handle,ps,depth))
+ return False;
+ if (!prs_uint32("level", ps, depth, &q_u->level))
+ return False;
+
+ if (!spoolss_io_buffer("", ps, depth, &q_u->buffer))
+ return False;
+
+ if (!prs_align(ps))
+ return False;
+ if (!prs_uint32("offered", ps, depth, &q_u->offered))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+********************************************************************/
+
+BOOL spoolss_io_r_enumforms(const char *desc, SPOOL_R_ENUMFORMS *r_u, prs_struct *ps, int depth)
+{
+ prs_debug(ps, depth, desc, "spoolss_io_r_enumforms");
+ depth++;
+
+ if (!prs_align(ps))
+ return False;
+
+ if (!spoolss_io_buffer("", ps, depth, &r_u->buffer))
+ return False;
+
+ if (!prs_align(ps))
+ return False;
+
+ if (!prs_uint32("size of buffer needed", ps, depth, &r_u->needed))
+ return False;
+
+ if (!prs_uint32("numofforms", ps, depth, &r_u->numofforms))
+ return False;
+
+ if (!prs_werror("status", ps, depth, &r_u->status))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+********************************************************************/
+
+BOOL spoolss_io_q_getform(const char *desc, SPOOL_Q_GETFORM *q_u, prs_struct *ps, int depth)
+{
+
+ prs_debug(ps, depth, desc, "spoolss_io_q_getform");
+ depth++;
+
+ if (!prs_align(ps))
+ return False;
+ if (!smb_io_pol_hnd("printer handle",&q_u->handle,ps,depth))
+ return False;
+ if (!smb_io_unistr2("", &q_u->formname,True,ps,depth))
+ return False;
+
+ if (!prs_align(ps))
+ return False;
+
+ if (!prs_uint32("level", ps, depth, &q_u->level))
+ return False;
+
+ if (!spoolss_io_buffer("", ps, depth, &q_u->buffer))
+ return False;
+
+ if (!prs_align(ps))
+ return False;
+ if (!prs_uint32("offered", ps, depth, &q_u->offered))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+********************************************************************/
+
+BOOL spoolss_io_r_getform(const char *desc, SPOOL_R_GETFORM *r_u, prs_struct *ps, int depth)
+{
+ prs_debug(ps, depth, desc, "spoolss_io_r_getform");
+ depth++;
+
+ if (!prs_align(ps))
+ return False;
+
+ if (!spoolss_io_buffer("", ps, depth, &r_u->buffer))
+ return False;
+
+ if (!prs_align(ps))
+ return False;
+
+ if (!prs_uint32("size of buffer needed", ps, depth, &r_u->needed))
+ return False;
+
+ if (!prs_werror("status", ps, depth, &r_u->status))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ Parse a SPOOL_R_ENUMPORTS structure.
+********************************************************************/
+
+BOOL spoolss_io_r_enumports(const char *desc, SPOOL_R_ENUMPORTS *r_u, prs_struct *ps, int depth)
+{
+ prs_debug(ps, depth, desc, "spoolss_io_r_enumports");
+ depth++;
+
+ if (!prs_align(ps))
+ return False;
+
+ if (!spoolss_io_buffer("", ps, depth, &r_u->buffer))
+ return False;
+
+ if (!prs_align(ps))
+ return False;
+
+ if (!prs_uint32("needed", ps, depth, &r_u->needed))
+ return False;
+
+ if (!prs_uint32("returned", ps, depth, &r_u->returned))
+ return False;
+
+ if (!prs_werror("status", ps, depth, &r_u->status))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+********************************************************************/
+
+BOOL spoolss_io_q_enumports(const char *desc, SPOOL_Q_ENUMPORTS *q_u, prs_struct *ps, int depth)
+{
+ prs_debug(ps, depth, desc, "");
+ depth++;
+
+ if (!prs_align(ps))
+ return False;
+
+ if (!prs_uint32("", ps, depth, &q_u->name_ptr))
+ return False;
+ if (!smb_io_unistr2("", &q_u->name,True,ps,depth))
+ return False;
+
+ if (!prs_align(ps))
+ return False;
+ if (!prs_uint32("level", ps, depth, &q_u->level))
+ return False;
+
+ if (!spoolss_io_buffer("", ps, depth, &q_u->buffer))
+ return False;
+
+ if (!prs_align(ps))
+ return False;
+ if (!prs_uint32("offered", ps, depth, &q_u->offered))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ Parse a SPOOL_PRINTER_INFO_LEVEL_1 structure.
+********************************************************************/
+
+BOOL spool_io_printer_info_level_1(const char *desc, SPOOL_PRINTER_INFO_LEVEL_1 *il, prs_struct *ps, int depth)
+{
+ prs_debug(ps, depth, desc, "spool_io_printer_info_level_1");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("flags", ps, depth, &il->flags))
+ return False;
+ if(!prs_uint32("description_ptr", ps, depth, &il->description_ptr))
+ return False;
+ if(!prs_uint32("name_ptr", ps, depth, &il->name_ptr))
+ return False;
+ if(!prs_uint32("comment_ptr", ps, depth, &il->comment_ptr))
+ return False;
+
+ if(!smb_io_unistr2("description", &il->description, il->description_ptr, ps, depth))
+ return False;
+ if(!smb_io_unistr2("name", &il->name, il->name_ptr, ps, depth))
+ return False;
+ if(!smb_io_unistr2("comment", &il->comment, il->comment_ptr, ps, depth))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ Parse a SPOOL_PRINTER_INFO_LEVEL_3 structure.
+********************************************************************/
+
+BOOL spool_io_printer_info_level_3(const char *desc, SPOOL_PRINTER_INFO_LEVEL_3 *il, prs_struct *ps, int depth)
+{
+ prs_debug(ps, depth, desc, "spool_io_printer_info_level_3");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("secdesc_ptr", ps, depth, &il->secdesc_ptr))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ Parse a SPOOL_PRINTER_INFO_LEVEL_2 structure.
+********************************************************************/
+
+BOOL spool_io_printer_info_level_2(const char *desc, SPOOL_PRINTER_INFO_LEVEL_2 *il, prs_struct *ps, int depth)
+{
+ prs_debug(ps, depth, desc, "spool_io_printer_info_level_2");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("servername_ptr", ps, depth, &il->servername_ptr))
+ return False;
+ if(!prs_uint32("printername_ptr", ps, depth, &il->printername_ptr))
+ return False;
+ if(!prs_uint32("sharename_ptr", ps, depth, &il->sharename_ptr))
+ return False;
+ if(!prs_uint32("portname_ptr", ps, depth, &il->portname_ptr))
+ return False;
+
+ if(!prs_uint32("drivername_ptr", ps, depth, &il->drivername_ptr))
+ return False;
+ if(!prs_uint32("comment_ptr", ps, depth, &il->comment_ptr))
+ return False;
+ if(!prs_uint32("location_ptr", ps, depth, &il->location_ptr))
+ return False;
+ if(!prs_uint32("devmode_ptr", ps, depth, &il->devmode_ptr))
+ return False;
+ if(!prs_uint32("sepfile_ptr", ps, depth, &il->sepfile_ptr))
+ return False;
+ if(!prs_uint32("printprocessor_ptr", ps, depth, &il->printprocessor_ptr))
+ return False;
+ if(!prs_uint32("datatype_ptr", ps, depth, &il->datatype_ptr))
+ return False;
+ if(!prs_uint32("parameters_ptr", ps, depth, &il->parameters_ptr))
+ return False;
+ if(!prs_uint32("secdesc_ptr", ps, depth, &il->secdesc_ptr))
+ return False;
+
+ if(!prs_uint32("attributes", ps, depth, &il->attributes))
+ return False;
+ if(!prs_uint32("priority", ps, depth, &il->priority))
+ return False;
+ if(!prs_uint32("default_priority", ps, depth, &il->default_priority))
+ return False;
+ if(!prs_uint32("starttime", ps, depth, &il->starttime))
+ return False;
+ if(!prs_uint32("untiltime", ps, depth, &il->untiltime))
+ return False;
+ if(!prs_uint32("status", ps, depth, &il->status))
+ return False;
+ if(!prs_uint32("cjobs", ps, depth, &il->cjobs))
+ return False;
+ if(!prs_uint32("averageppm", ps, depth, &il->averageppm))
+ return False;
+
+ if(!smb_io_unistr2("servername", &il->servername, il->servername_ptr, ps, depth))
+ return False;
+ if(!smb_io_unistr2("printername", &il->printername, il->printername_ptr, ps, depth))
+ return False;
+ if(!smb_io_unistr2("sharename", &il->sharename, il->sharename_ptr, ps, depth))
+ return False;
+ if(!smb_io_unistr2("portname", &il->portname, il->portname_ptr, ps, depth))
+ return False;
+ if(!smb_io_unistr2("drivername", &il->drivername, il->drivername_ptr, ps, depth))
+ return False;
+ if(!smb_io_unistr2("comment", &il->comment, il->comment_ptr, ps, depth))
+ return False;
+ if(!smb_io_unistr2("location", &il->location, il->location_ptr, ps, depth))
+ return False;
+ if(!smb_io_unistr2("sepfile", &il->sepfile, il->sepfile_ptr, ps, depth))
+ return False;
+ if(!smb_io_unistr2("printprocessor", &il->printprocessor, il->printprocessor_ptr, ps, depth))
+ return False;
+ if(!smb_io_unistr2("datatype", &il->datatype, il->datatype_ptr, ps, depth))
+ return False;
+ if(!smb_io_unistr2("parameters", &il->parameters, il->parameters_ptr, ps, depth))
+ return False;
+
+ return True;
+}
+
+BOOL spool_io_printer_info_level_7(const char *desc, SPOOL_PRINTER_INFO_LEVEL_7 *il, prs_struct *ps, int depth)
+{
+ prs_debug(ps, depth, desc, "spool_io_printer_info_level_7");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("guid_ptr", ps, depth, &il->guid_ptr))
+ return False;
+ if(!prs_uint32("action", ps, depth, &il->action))
+ return False;
+
+ if(!smb_io_unistr2("servername", &il->guid, il->guid_ptr, ps, depth))
+ return False;
+ return True;
+}
+
+/*******************************************************************
+********************************************************************/
+
+BOOL spool_io_printer_info_level(const char *desc, SPOOL_PRINTER_INFO_LEVEL *il, prs_struct *ps, int depth)
+{
+ prs_debug(ps, depth, desc, "spool_io_printer_info_level");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+ if(!prs_uint32("level", ps, depth, &il->level))
+ return False;
+ if(!prs_uint32("info_ptr", ps, depth, &il->info_ptr))
+ return False;
+
+ /* if no struct inside just return */
+ if (il->info_ptr==0) {
+ if (UNMARSHALLING(ps)) {
+ il->info_1=NULL;
+ il->info_2=NULL;
+ }
+ return True;
+ }
+
+ switch (il->level) {
+ /*
+ * level 0 is used by setprinter when managing the queue
+ * (hold, stop, start a queue)
+ */
+ case 0:
+ break;
+ /* DOCUMENT ME!!! What is level 1 used for? */
+ case 1:
+ {
+ if (UNMARSHALLING(ps)) {
+ if ((il->info_1=(SPOOL_PRINTER_INFO_LEVEL_1 *)prs_alloc_mem(ps,sizeof(SPOOL_PRINTER_INFO_LEVEL_1))) == NULL)
+ return False;
+ }
+ if (!spool_io_printer_info_level_1("", il->info_1, ps, depth))
+ return False;
+ break;
+ }
+ /*
+ * level 2 is used by addprinter
+ * and by setprinter when updating printer's info
+ */
+ case 2:
+ if (UNMARSHALLING(ps)) {
+ if ((il->info_2=(SPOOL_PRINTER_INFO_LEVEL_2 *)prs_alloc_mem(ps,sizeof(SPOOL_PRINTER_INFO_LEVEL_2))) == NULL)
+ return False;
+ }
+ if (!spool_io_printer_info_level_2("", il->info_2, ps, depth))
+ return False;
+ break;
+ /* DOCUMENT ME!!! What is level 3 used for? */
+ case 3:
+ {
+ if (UNMARSHALLING(ps)) {
+ if ((il->info_3=(SPOOL_PRINTER_INFO_LEVEL_3 *)prs_alloc_mem(ps,sizeof(SPOOL_PRINTER_INFO_LEVEL_3))) == NULL)
+ return False;
+ }
+ if (!spool_io_printer_info_level_3("", il->info_3, ps, depth))
+ return False;
+ break;
+ }
+ case 7:
+ if (UNMARSHALLING(ps))
+ if ((il->info_7=(SPOOL_PRINTER_INFO_LEVEL_7 *)prs_alloc_mem(ps,sizeof(SPOOL_PRINTER_INFO_LEVEL_7))) == NULL)
+ return False;
+ if (!spool_io_printer_info_level_7("", il->info_7, ps, depth))
+ return False;
+ break;
+ }
+
+ return True;
+}
+
+/*******************************************************************
+********************************************************************/
+
+BOOL spoolss_io_q_addprinterex(const char *desc, SPOOL_Q_ADDPRINTEREX *q_u, prs_struct *ps, int depth)
+{
+ uint32 ptr_sec_desc = 0;
+
+ prs_debug(ps, depth, desc, "spoolss_io_q_addprinterex");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+ if(!prs_uint32("", ps, depth, &q_u->server_name_ptr))
+ return False;
+ if(!smb_io_unistr2("", &q_u->server_name, q_u->server_name_ptr, ps, depth))
+ return False;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("info_level", ps, depth, &q_u->level))
+ return False;
+
+ if(!spool_io_printer_info_level("", &q_u->info, ps, depth))
+ return False;
+
+ if (!spoolss_io_devmode_cont(desc, &q_u->devmode_ctr, ps, depth))
+ return False;
+
+ if(!prs_align(ps))
+ return False;
+
+ switch (q_u->level) {
+ case 2:
+ ptr_sec_desc = q_u->info.info_2->secdesc_ptr;
+ break;
+ case 3:
+ ptr_sec_desc = q_u->info.info_3->secdesc_ptr;
+ break;
+ }
+ if (ptr_sec_desc) {
+ if (!sec_io_desc_buf(desc, &q_u->secdesc_ctr, ps, depth))
+ return False;
+ } else {
+ uint32 dummy;
+
+ /* Parse a NULL security descriptor. This should really
+ happen inside the sec_io_desc_buf() function. */
+
+ prs_debug(ps, depth, "", "sec_io_desc_buf");
+ if (!prs_uint32("size", ps, depth + 1, &dummy))
+ return False;
+ if (!prs_uint32("ptr", ps, depth + 1, &dummy))
+ return False;
+ }
+
+ if(!prs_uint32("user_switch", ps, depth, &q_u->user_switch))
+ return False;
+ if(!spool_io_user_level("", &q_u->user_ctr, ps, depth))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+********************************************************************/
+
+BOOL spoolss_io_r_addprinterex(const char *desc, SPOOL_R_ADDPRINTEREX *r_u,
+ prs_struct *ps, int depth)
+{
+ prs_debug(ps, depth, desc, "spoolss_io_r_addprinterex");
+ depth++;
+
+ if(!smb_io_pol_hnd("printer handle",&r_u->handle,ps,depth))
+ return False;
+
+ if(!prs_werror("status", ps, depth, &r_u->status))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+********************************************************************/
+
+BOOL spool_io_printer_driver_info_level_3(const char *desc, SPOOL_PRINTER_DRIVER_INFO_LEVEL_3 **q_u,
+ prs_struct *ps, int depth)
+{
+ SPOOL_PRINTER_DRIVER_INFO_LEVEL_3 *il;
+
+ prs_debug(ps, depth, desc, "spool_io_printer_driver_info_level_3");
+ depth++;
+
+ /* reading */
+ if (UNMARSHALLING(ps)) {
+ il=(SPOOL_PRINTER_DRIVER_INFO_LEVEL_3 *)prs_alloc_mem(ps,sizeof(SPOOL_PRINTER_DRIVER_INFO_LEVEL_3));
+ if(il == NULL)
+ return False;
+ *q_u=il;
+ }
+ else {
+ il=*q_u;
+ }
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("cversion", ps, depth, &il->cversion))
+ return False;
+ if(!prs_uint32("name", ps, depth, &il->name_ptr))
+ return False;
+ if(!prs_uint32("environment", ps, depth, &il->environment_ptr))
+ return False;
+ if(!prs_uint32("driverpath", ps, depth, &il->driverpath_ptr))
+ return False;
+ if(!prs_uint32("datafile", ps, depth, &il->datafile_ptr))
+ return False;
+ if(!prs_uint32("configfile", ps, depth, &il->configfile_ptr))
+ return False;
+ if(!prs_uint32("helpfile", ps, depth, &il->helpfile_ptr))
+ return False;
+ if(!prs_uint32("monitorname", ps, depth, &il->monitorname_ptr))
+ return False;
+ if(!prs_uint32("defaultdatatype", ps, depth, &il->defaultdatatype_ptr))
+ return False;
+ if(!prs_uint32("dependentfilessize", ps, depth, &il->dependentfilessize))
+ return False;
+ if(!prs_uint32("dependentfiles", ps, depth, &il->dependentfiles_ptr))
+ return False;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!smb_io_unistr2("name", &il->name, il->name_ptr, ps, depth))
+ return False;
+ if(!smb_io_unistr2("environment", &il->environment, il->environment_ptr, ps, depth))
+ return False;
+ if(!smb_io_unistr2("driverpath", &il->driverpath, il->driverpath_ptr, ps, depth))
+ return False;
+ if(!smb_io_unistr2("datafile", &il->datafile, il->datafile_ptr, ps, depth))
+ return False;
+ if(!smb_io_unistr2("configfile", &il->configfile, il->configfile_ptr, ps, depth))
+ return False;
+ if(!smb_io_unistr2("helpfile", &il->helpfile, il->helpfile_ptr, ps, depth))
+ return False;
+ if(!smb_io_unistr2("monitorname", &il->monitorname, il->monitorname_ptr, ps, depth))
+ return False;
+ if(!smb_io_unistr2("defaultdatatype", &il->defaultdatatype, il->defaultdatatype_ptr, ps, depth))
+ return False;
+
+ if(!prs_align(ps))
+ return False;
+
+ if (il->dependentfiles_ptr)
+ smb_io_buffer5("", &il->dependentfiles, ps, depth);
+
+ return True;
+}
+
+/*******************************************************************
+parse a SPOOL_PRINTER_DRIVER_INFO_LEVEL_6 structure
+********************************************************************/
+
+BOOL spool_io_printer_driver_info_level_6(const char *desc, SPOOL_PRINTER_DRIVER_INFO_LEVEL_6 **q_u,
+ prs_struct *ps, int depth)
+{
+ SPOOL_PRINTER_DRIVER_INFO_LEVEL_6 *il;
+
+ prs_debug(ps, depth, desc, "spool_io_printer_driver_info_level_6");
+ depth++;
+
+ /* reading */
+ if (UNMARSHALLING(ps)) {
+ il=(SPOOL_PRINTER_DRIVER_INFO_LEVEL_6 *)prs_alloc_mem(ps,sizeof(SPOOL_PRINTER_DRIVER_INFO_LEVEL_6));
+ if(il == NULL)
+ return False;
+ *q_u=il;
+ }
+ else {
+ il=*q_u;
+ }
+
+ if(!prs_align(ps))
+ return False;
+
+ /*
+ * I know this seems weird, but I have no other explanation.
+ * This is observed behavior on both NT4 and 2K servers.
+ * --jerry
+ */
+
+ if (!prs_align_uint64(ps))
+ return False;
+
+ /* parse the main elements the packet */
+
+ if(!prs_uint32("cversion ", ps, depth, &il->version))
+ return False;
+ if(!prs_uint32("name ", ps, depth, &il->name_ptr))
+ return False;
+ if(!prs_uint32("environment ", ps, depth, &il->environment_ptr))
+ return False;
+ if(!prs_uint32("driverpath ", ps, depth, &il->driverpath_ptr))
+ return False;
+ if(!prs_uint32("datafile ", ps, depth, &il->datafile_ptr))
+ return False;
+ if(!prs_uint32("configfile ", ps, depth, &il->configfile_ptr))
+ return False;
+ if(!prs_uint32("helpfile ", ps, depth, &il->helpfile_ptr))
+ return False;
+ if(!prs_uint32("monitorname ", ps, depth, &il->monitorname_ptr))
+ return False;
+ if(!prs_uint32("defaultdatatype", ps, depth, &il->defaultdatatype_ptr))
+ return False;
+ if(!prs_uint32("dependentfiles ", ps, depth, &il->dependentfiles_len))
+ return False;
+ if(!prs_uint32("dependentfiles ", ps, depth, &il->dependentfiles_ptr))
+ return False;
+ if(!prs_uint32("previousnames ", ps, depth, &il->previousnames_len))
+ return False;
+ if(!prs_uint32("previousnames ", ps, depth, &il->previousnames_ptr))
+ return False;
+ if(!smb_io_time("driverdate ", &il->driverdate, ps, depth))
+ return False;
+ if(!prs_uint32("dummy4 ", ps, depth, &il->dummy4))
+ return False;
+ if(!prs_uint64("driverversion ", ps, depth, &il->driverversion))
+ return False;
+ if(!prs_uint32("mfgname ", ps, depth, &il->mfgname_ptr))
+ return False;
+ if(!prs_uint32("oemurl ", ps, depth, &il->oemurl_ptr))
+ return False;
+ if(!prs_uint32("hardwareid ", ps, depth, &il->hardwareid_ptr))
+ return False;
+ if(!prs_uint32("provider ", ps, depth, &il->provider_ptr))
+ return False;
+
+ /* parse the structures in the packet */
+
+ if(!smb_io_unistr2("name", &il->name, il->name_ptr, ps, depth))
+ return False;
+ if(!prs_align(ps))
+ return False;
+
+ if(!smb_io_unistr2("environment", &il->environment, il->environment_ptr, ps, depth))
+ return False;
+ if(!prs_align(ps))
+ return False;
+
+ if(!smb_io_unistr2("driverpath", &il->driverpath, il->driverpath_ptr, ps, depth))
+ return False;
+ if(!prs_align(ps))
+ return False;
+
+ if(!smb_io_unistr2("datafile", &il->datafile, il->datafile_ptr, ps, depth))
+ return False;
+ if(!prs_align(ps))
+ return False;
+
+ if(!smb_io_unistr2("configfile", &il->configfile, il->configfile_ptr, ps, depth))
+ return False;
+ if(!prs_align(ps))
+ return False;
+
+ if(!smb_io_unistr2("helpfile", &il->helpfile, il->helpfile_ptr, ps, depth))
+ return False;
+ if(!prs_align(ps))
+ return False;
+
+ if(!smb_io_unistr2("monitorname", &il->monitorname, il->monitorname_ptr, ps, depth))
+ return False;
+ if(!prs_align(ps))
+ return False;
+
+ if(!smb_io_unistr2("defaultdatatype", &il->defaultdatatype, il->defaultdatatype_ptr, ps, depth))
+ return False;
+ if(!prs_align(ps))
+ return False;
+ if (il->dependentfiles_ptr) {
+ if(!smb_io_buffer5("dependentfiles", &il->dependentfiles, ps, depth))
+ return False;
+ if(!prs_align(ps))
+ return False;
+ }
+ if (il->previousnames_ptr) {
+ if(!smb_io_buffer5("previousnames", &il->previousnames, ps, depth))
+ return False;
+ if(!prs_align(ps))
+ return False;
+ }
+ if(!smb_io_unistr2("mfgname", &il->mfgname, il->mfgname_ptr, ps, depth))
+ return False;
+ if(!prs_align(ps))
+ return False;
+ if(!smb_io_unistr2("oemurl", &il->oemurl, il->oemurl_ptr, ps, depth))
+ return False;
+ if(!prs_align(ps))
+ return False;
+ if(!smb_io_unistr2("hardwareid", &il->hardwareid, il->hardwareid_ptr, ps, depth))
+ return False;
+ if(!prs_align(ps))
+ return False;
+ if(!smb_io_unistr2("provider", &il->provider, il->provider_ptr, ps, depth))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ convert a buffer of UNICODE strings null terminated
+ the buffer is terminated by a NULL
+
+ convert to an dos codepage array (null terminated)
+
+ dynamically allocate memory
+
+********************************************************************/
+static BOOL uniarray_2_dosarray(BUFFER5 *buf5, fstring **ar)
+{
+ fstring f, *tar;
+ int n = 0;
+ char *src;
+
+ if (buf5==NULL)
+ return False;
+
+ src = (char *)buf5->buffer;
+ *ar = NULL;
+
+ while (src < ((char *)buf5->buffer) + buf5->buf_len*2) {
+ rpcstr_pull(f, src, sizeof(f)-1, -1, STR_TERMINATE);
+ src = skip_unibuf(src, 2*buf5->buf_len - PTR_DIFF(src,buf5->buffer));
+ tar = (fstring *)Realloc(*ar, sizeof(fstring)*(n+2));
+ if (!tar)
+ return False;
+ else
+ *ar = tar;
+ fstrcpy((*ar)[n], f);
+ n++;
+ }
+ fstrcpy((*ar)[n], "");
+
+ return True;
+}
+
+
+
+
+/*******************************************************************
+ read a UNICODE array with null terminated strings
+ and null terminated array
+ and size of array at beginning
+********************************************************************/
+
+BOOL smb_io_unibuffer(const char *desc, UNISTR2 *buffer, prs_struct *ps, int depth)
+{
+ if (buffer==NULL) return False;
+
+ buffer->offset=0;
+ buffer->uni_str_len=buffer->uni_max_len;
+
+ if(!prs_uint32("buffer_size", ps, depth, &buffer->uni_max_len))
+ return False;
+
+ if(!prs_unistr2(True, "buffer ", ps, depth, buffer))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+********************************************************************/
+
+BOOL spool_io_printer_driver_info_level(const char *desc, SPOOL_PRINTER_DRIVER_INFO_LEVEL *il, prs_struct *ps, int depth)
+{
+ prs_debug(ps, depth, desc, "spool_io_printer_driver_info_level");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+ if(!prs_uint32("level", ps, depth, &il->level))
+ return False;
+ if(!prs_uint32("ptr", ps, depth, &il->ptr))
+ return False;
+
+ if (il->ptr==0)
+ return True;
+
+ switch (il->level) {
+ case 3:
+ if(!spool_io_printer_driver_info_level_3("", &il->info_3, ps, depth))
+ return False;
+ break;
+ case 6:
+ if(!spool_io_printer_driver_info_level_6("", &il->info_6, ps, depth))
+ return False;
+ break;
+ default:
+ return False;
+ }
+
+ return True;
+}
+
+/*******************************************************************
+ init a SPOOL_Q_ADDPRINTERDRIVER struct
+ ******************************************************************/
+
+BOOL make_spoolss_q_addprinterdriver(TALLOC_CTX *mem_ctx,
+ SPOOL_Q_ADDPRINTERDRIVER *q_u, const char* srv_name,
+ uint32 level, PRINTER_DRIVER_CTR *info)
+{
+ DEBUG(5,("make_spoolss_q_addprinterdriver\n"));
+
+ q_u->server_name_ptr = (srv_name!=NULL)?1:0;
+ init_unistr2(&q_u->server_name, srv_name, UNI_STR_TERMINATE);
+
+ q_u->level = level;
+
+ q_u->info.level = level;
+ q_u->info.ptr = (info!=NULL)?1:0;
+ switch (level)
+ {
+ /* info level 3 is supported by Windows 95/98, WinNT and Win2k */
+ case 3 :
+ make_spoolss_driver_info_3(mem_ctx, &q_u->info.info_3, info->info3);
+ break;
+
+ default:
+ DEBUG(0,("make_spoolss_q_addprinterdriver: Unknown info level [%d]\n", level));
+ break;
+ }
+
+ return True;
+}
+
+BOOL make_spoolss_driver_info_3(TALLOC_CTX *mem_ctx,
+ SPOOL_PRINTER_DRIVER_INFO_LEVEL_3 **spool_drv_info,
+ DRIVER_INFO_3 *info3)
+{
+ uint32 len = 0;
+ uint16 *ptr = info3->dependentfiles;
+ BOOL done = False;
+ BOOL null_char = False;
+ SPOOL_PRINTER_DRIVER_INFO_LEVEL_3 *inf;
+
+ if (!(inf=(SPOOL_PRINTER_DRIVER_INFO_LEVEL_3*)talloc_zero(mem_ctx, sizeof(SPOOL_PRINTER_DRIVER_INFO_LEVEL_3))))
+ return False;
+
+ inf->cversion = info3->version;
+ inf->name_ptr = (info3->name.buffer!=NULL)?1:0;
+ inf->environment_ptr = (info3->architecture.buffer!=NULL)?1:0;
+ inf->driverpath_ptr = (info3->driverpath.buffer!=NULL)?1:0;
+ inf->datafile_ptr = (info3->datafile.buffer!=NULL)?1:0;
+ inf->configfile_ptr = (info3->configfile.buffer!=NULL)?1:0;
+ inf->helpfile_ptr = (info3->helpfile.buffer!=NULL)?1:0;
+ inf->monitorname_ptr = (info3->monitorname.buffer!=NULL)?1:0;
+ inf->defaultdatatype_ptr = (info3->defaultdatatype.buffer!=NULL)?1:0;
+
+ init_unistr2_from_unistr(&inf->name, &info3->name);
+ init_unistr2_from_unistr(&inf->environment, &info3->architecture);
+ init_unistr2_from_unistr(&inf->driverpath, &info3->driverpath);
+ init_unistr2_from_unistr(&inf->datafile, &info3->datafile);
+ init_unistr2_from_unistr(&inf->configfile, &info3->configfile);
+ init_unistr2_from_unistr(&inf->helpfile, &info3->helpfile);
+ init_unistr2_from_unistr(&inf->monitorname, &info3->monitorname);
+ init_unistr2_from_unistr(&inf->defaultdatatype, &info3->defaultdatatype);
+
+ while (!done)
+ {
+ switch (*ptr)
+ {
+ case 0:
+ /* the null_char BOOL is used to help locate
+ two '\0's back to back */
+ if (null_char)
+ done = True;
+ else
+ null_char = True;
+ break;
+
+ default:
+ null_char = False;
+ ;;
+ break;
+ }
+ len++;
+ ptr++;
+ }
+ inf->dependentfiles_ptr = (info3->dependentfiles != NULL) ? 1 : 0;
+ inf->dependentfilessize = len;
+ if(!make_spoolss_buffer5(mem_ctx, &inf->dependentfiles, len, info3->dependentfiles))
+ {
+ SAFE_FREE(inf);
+ return False;
+ }
+
+ *spool_drv_info = inf;
+
+ return True;
+}
+
+/*******************************************************************
+ make a BUFFER5 struct from a uint16*
+ ******************************************************************/
+BOOL make_spoolss_buffer5(TALLOC_CTX *mem_ctx, BUFFER5 *buf5, uint32 len, uint16 *src)
+{
+
+ buf5->buf_len = len;
+ if((buf5->buffer=(uint16*)talloc_memdup(mem_ctx, src, sizeof(uint16)*len)) == NULL)
+ {
+ DEBUG(0,("make_spoolss_buffer5: Unable to malloc memory for buffer!\n"));
+ return False;
+ }
+
+ return True;
+}
+
+/*******************************************************************
+ fill in the prs_struct for a ADDPRINTERDRIVER request PDU
+ ********************************************************************/
+
+BOOL spoolss_io_q_addprinterdriver(const char *desc, SPOOL_Q_ADDPRINTERDRIVER *q_u, prs_struct *ps, int depth)
+{
+ prs_debug(ps, depth, desc, "spoolss_io_q_addprinterdriver");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("server_name_ptr", ps, depth, &q_u->server_name_ptr))
+ return False;
+ if(!smb_io_unistr2("server_name", &q_u->server_name, q_u->server_name_ptr, ps, depth))
+ return False;
+
+ if(!prs_align(ps))
+ return False;
+ if(!prs_uint32("info_level", ps, depth, &q_u->level))
+ return False;
+
+ if(!spool_io_printer_driver_info_level("", &q_u->info, ps, depth))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+********************************************************************/
+
+BOOL spoolss_io_r_addprinterdriver(const char *desc, SPOOL_R_ADDPRINTERDRIVER *q_u, prs_struct *ps, int depth)
+{
+ prs_debug(ps, depth, desc, "spoolss_io_r_addprinterdriver");
+ depth++;
+
+ if(!prs_werror("status", ps, depth, &q_u->status))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ fill in the prs_struct for a ADDPRINTERDRIVER request PDU
+ ********************************************************************/
+
+BOOL spoolss_io_q_addprinterdriverex(const char *desc, SPOOL_Q_ADDPRINTERDRIVEREX *q_u, prs_struct *ps, int depth)
+{
+ prs_debug(ps, depth, desc, "spoolss_io_q_addprinterdriverex");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("server_name_ptr", ps, depth, &q_u->server_name_ptr))
+ return False;
+ if(!smb_io_unistr2("server_name", &q_u->server_name, q_u->server_name_ptr, ps, depth))
+ return False;
+
+ if(!prs_align(ps))
+ return False;
+ if(!prs_uint32("info_level", ps, depth, &q_u->level))
+ return False;
+
+ if(!spool_io_printer_driver_info_level("", &q_u->info, ps, depth))
+ return False;
+
+ if(!prs_align(ps))
+ return False;
+ if(!prs_uint32("copy flags", ps, depth, &q_u->copy_flags))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+********************************************************************/
+
+BOOL spoolss_io_r_addprinterdriverex(const char *desc, SPOOL_R_ADDPRINTERDRIVEREX *q_u, prs_struct *ps, int depth)
+{
+ prs_debug(ps, depth, desc, "spoolss_io_r_addprinterdriverex");
+ depth++;
+
+ if(!prs_werror("status", ps, depth, &q_u->status))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+********************************************************************/
+
+BOOL uni_2_asc_printer_driver_3(SPOOL_PRINTER_DRIVER_INFO_LEVEL_3 *uni,
+ NT_PRINTER_DRIVER_INFO_LEVEL_3 **asc)
+{
+ NT_PRINTER_DRIVER_INFO_LEVEL_3 *d;
+
+ DEBUG(7,("uni_2_asc_printer_driver_3: Converting from UNICODE to ASCII\n"));
+
+ if (*asc==NULL)
+ {
+ *asc=(NT_PRINTER_DRIVER_INFO_LEVEL_3 *)malloc(sizeof(NT_PRINTER_DRIVER_INFO_LEVEL_3));
+ if(*asc == NULL)
+ return False;
+ ZERO_STRUCTP(*asc);
+ }
+
+ d=*asc;
+
+ d->cversion=uni->cversion;
+
+ unistr2_to_ascii(d->name, &uni->name, sizeof(d->name)-1);
+ unistr2_to_ascii(d->environment, &uni->environment, sizeof(d->environment)-1);
+ unistr2_to_ascii(d->driverpath, &uni->driverpath, sizeof(d->driverpath)-1);
+ unistr2_to_ascii(d->datafile, &uni->datafile, sizeof(d->datafile)-1);
+ unistr2_to_ascii(d->configfile, &uni->configfile, sizeof(d->configfile)-1);
+ unistr2_to_ascii(d->helpfile, &uni->helpfile, sizeof(d->helpfile)-1);
+ unistr2_to_ascii(d->monitorname, &uni->monitorname, sizeof(d->monitorname)-1);
+ unistr2_to_ascii(d->defaultdatatype, &uni->defaultdatatype, sizeof(d->defaultdatatype)-1);
+
+ DEBUGADD(8,( "version: %d\n", d->cversion));
+ DEBUGADD(8,( "name: %s\n", d->name));
+ DEBUGADD(8,( "environment: %s\n", d->environment));
+ DEBUGADD(8,( "driverpath: %s\n", d->driverpath));
+ DEBUGADD(8,( "datafile: %s\n", d->datafile));
+ DEBUGADD(8,( "configfile: %s\n", d->configfile));
+ DEBUGADD(8,( "helpfile: %s\n", d->helpfile));
+ DEBUGADD(8,( "monitorname: %s\n", d->monitorname));
+ DEBUGADD(8,( "defaultdatatype: %s\n", d->defaultdatatype));
+
+ if (uniarray_2_dosarray(&uni->dependentfiles, &d->dependentfiles ))
+ return True;
+
+ SAFE_FREE(*asc);
+ return False;
+}
+
+/*******************************************************************
+********************************************************************/
+BOOL uni_2_asc_printer_driver_6(SPOOL_PRINTER_DRIVER_INFO_LEVEL_6 *uni,
+ NT_PRINTER_DRIVER_INFO_LEVEL_6 **asc)
+{
+ NT_PRINTER_DRIVER_INFO_LEVEL_6 *d;
+
+ DEBUG(7,("uni_2_asc_printer_driver_6: Converting from UNICODE to ASCII\n"));
+
+ if (*asc==NULL)
+ {
+ *asc=(NT_PRINTER_DRIVER_INFO_LEVEL_6 *)malloc(sizeof(NT_PRINTER_DRIVER_INFO_LEVEL_6));
+ if(*asc == NULL)
+ return False;
+ ZERO_STRUCTP(*asc);
+ }
+
+ d=*asc;
+
+ d->version=uni->version;
+
+ unistr2_to_ascii(d->name, &uni->name, sizeof(d->name)-1);
+ unistr2_to_ascii(d->environment, &uni->environment, sizeof(d->environment)-1);
+ unistr2_to_ascii(d->driverpath, &uni->driverpath, sizeof(d->driverpath)-1);
+ unistr2_to_ascii(d->datafile, &uni->datafile, sizeof(d->datafile)-1);
+ unistr2_to_ascii(d->configfile, &uni->configfile, sizeof(d->configfile)-1);
+ unistr2_to_ascii(d->helpfile, &uni->helpfile, sizeof(d->helpfile)-1);
+ unistr2_to_ascii(d->monitorname, &uni->monitorname, sizeof(d->monitorname)-1);
+ unistr2_to_ascii(d->defaultdatatype, &uni->defaultdatatype, sizeof(d->defaultdatatype)-1);
+
+ DEBUGADD(8,( "version: %d\n", d->version));
+ DEBUGADD(8,( "name: %s\n", d->name));
+ DEBUGADD(8,( "environment: %s\n", d->environment));
+ DEBUGADD(8,( "driverpath: %s\n", d->driverpath));
+ DEBUGADD(8,( "datafile: %s\n", d->datafile));
+ DEBUGADD(8,( "configfile: %s\n", d->configfile));
+ DEBUGADD(8,( "helpfile: %s\n", d->helpfile));
+ DEBUGADD(8,( "monitorname: %s\n", d->monitorname));
+ DEBUGADD(8,( "defaultdatatype: %s\n", d->defaultdatatype));
+
+ if (!uniarray_2_dosarray(&uni->dependentfiles, &d->dependentfiles ))
+ goto error;
+ if (!uniarray_2_dosarray(&uni->previousnames, &d->previousnames ))
+ goto error;
+
+ return True;
+
+error:
+ SAFE_FREE(*asc);
+ return False;
+}
+
+BOOL uni_2_asc_printer_info_2(const SPOOL_PRINTER_INFO_LEVEL_2 *uni,
+ NT_PRINTER_INFO_LEVEL_2 **asc)
+{
+ NT_PRINTER_INFO_LEVEL_2 *d;
+ time_t time_unix;
+
+ DEBUG(7,("Converting from UNICODE to ASCII\n"));
+ time_unix=time(NULL);
+
+ if (*asc==NULL) {
+ DEBUGADD(8,("allocating memory\n"));
+
+ *asc=(NT_PRINTER_INFO_LEVEL_2 *)malloc(sizeof(NT_PRINTER_INFO_LEVEL_2));
+ if(*asc == NULL)
+ return False;
+ ZERO_STRUCTP(*asc);
+
+ /* we allocate memory iff called from
+ * addprinter(ex) so we can do one time stuff here.
+ */
+ (*asc)->setuptime=time_unix;
+
+ }
+ DEBUGADD(8,("start converting\n"));
+
+ d=*asc;
+
+ d->attributes=uni->attributes;
+ d->priority=uni->priority;
+ d->default_priority=uni->default_priority;
+ d->starttime=uni->starttime;
+ d->untiltime=uni->untiltime;
+ d->status=uni->status;
+ d->cjobs=uni->cjobs;
+
+ unistr2_to_ascii(d->servername, &uni->servername, sizeof(d->servername)-1);
+ unistr2_to_ascii(d->printername, &uni->printername, sizeof(d->printername)-1);
+ unistr2_to_ascii(d->sharename, &uni->sharename, sizeof(d->sharename)-1);
+ unistr2_to_ascii(d->portname, &uni->portname, sizeof(d->portname)-1);
+ unistr2_to_ascii(d->drivername, &uni->drivername, sizeof(d->drivername)-1);
+ unistr2_to_ascii(d->comment, &uni->comment, sizeof(d->comment)-1);
+ unistr2_to_ascii(d->location, &uni->location, sizeof(d->location)-1);
+ unistr2_to_ascii(d->sepfile, &uni->sepfile, sizeof(d->sepfile)-1);
+ unistr2_to_ascii(d->printprocessor, &uni->printprocessor, sizeof(d->printprocessor)-1);
+ unistr2_to_ascii(d->datatype, &uni->datatype, sizeof(d->datatype)-1);
+ unistr2_to_ascii(d->parameters, &uni->parameters, sizeof(d->parameters)-1);
+
+ return True;
+}
+
+/*******************************************************************
+ * init a structure.
+ ********************************************************************/
+
+BOOL make_spoolss_q_getprinterdriverdir(SPOOL_Q_GETPRINTERDRIVERDIR *q_u,
+ fstring servername, fstring env_name, uint32 level,
+ NEW_BUFFER *buffer, uint32 offered)
+{
+ init_buf_unistr2(&q_u->name, &q_u->name_ptr, servername);
+ init_buf_unistr2(&q_u->environment, &q_u->environment_ptr, env_name);
+
+ q_u->level=level;
+ q_u->buffer=buffer;
+ q_u->offered=offered;
+
+ return True;
+}
+
+/*******************************************************************
+ Parse a SPOOL_Q_GETPRINTERDRIVERDIR structure.
+********************************************************************/
+
+BOOL spoolss_io_q_getprinterdriverdir(const char *desc, SPOOL_Q_GETPRINTERDRIVERDIR *q_u, prs_struct *ps, int depth)
+{
+ prs_debug(ps, depth, desc, "spoolss_io_q_getprinterdriverdir");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+ if(!prs_uint32("name_ptr", ps, depth, &q_u->name_ptr))
+ return False;
+ if(!smb_io_unistr2("", &q_u->name, q_u->name_ptr, ps, depth))
+ return False;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("", ps, depth, &q_u->environment_ptr))
+ return False;
+ if(!smb_io_unistr2("", &q_u->environment, q_u->environment_ptr, ps, depth))
+ return False;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("level", ps, depth, &q_u->level))
+ return False;
+
+ if(!spoolss_io_buffer("", ps, depth, &q_u->buffer))
+ return False;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("offered", ps, depth, &q_u->offered))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ Parse a SPOOL_R_GETPRINTERDRIVERDIR structure.
+********************************************************************/
+
+BOOL spoolss_io_r_getprinterdriverdir(const char *desc, SPOOL_R_GETPRINTERDRIVERDIR *r_u, prs_struct *ps, int depth)
+{
+ prs_debug(ps, depth, desc, "spoolss_io_r_getprinterdriverdir");
+ depth++;
+
+ if (!prs_align(ps))
+ return False;
+
+ if (!spoolss_io_buffer("", ps, depth, &r_u->buffer))
+ return False;
+
+ if (!prs_align(ps))
+ return False;
+
+ if (!prs_uint32("needed", ps, depth, &r_u->needed))
+ return False;
+
+ if (!prs_werror("status", ps, depth, &r_u->status))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+********************************************************************/
+
+BOOL spoolss_io_r_enumprintprocessors(const char *desc, SPOOL_R_ENUMPRINTPROCESSORS *r_u, prs_struct *ps, int depth)
+{
+ prs_debug(ps, depth, desc, "spoolss_io_r_enumprintprocessors");
+ depth++;
+
+ if (!prs_align(ps))
+ return False;
+
+ if (!spoolss_io_buffer("", ps, depth, &r_u->buffer))
+ return False;
+
+ if (!prs_align(ps))
+ return False;
+
+ if (!prs_uint32("needed", ps, depth, &r_u->needed))
+ return False;
+
+ if (!prs_uint32("returned", ps, depth, &r_u->returned))
+ return False;
+
+ if (!prs_werror("status", ps, depth, &r_u->status))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+********************************************************************/
+
+BOOL spoolss_io_q_enumprintprocessors(const char *desc, SPOOL_Q_ENUMPRINTPROCESSORS *q_u, prs_struct *ps, int depth)
+{
+ prs_debug(ps, depth, desc, "spoolss_io_q_enumprintprocessors");
+ depth++;
+
+ if (!prs_align(ps))
+ return False;
+
+ if (!prs_uint32("name_ptr", ps, depth, &q_u->name_ptr))
+ return False;
+ if (!smb_io_unistr2("name", &q_u->name, True, ps, depth))
+ return False;
+
+ if (!prs_align(ps))
+ return False;
+
+ if (!prs_uint32("", ps, depth, &q_u->environment_ptr))
+ return False;
+ if (!smb_io_unistr2("", &q_u->environment, q_u->environment_ptr, ps, depth))
+ return False;
+
+ if (!prs_align(ps))
+ return False;
+
+ if (!prs_uint32("level", ps, depth, &q_u->level))
+ return False;
+
+ if(!spoolss_io_buffer("", ps, depth, &q_u->buffer))
+ return False;
+
+ if (!prs_align(ps))
+ return False;
+
+ if (!prs_uint32("offered", ps, depth, &q_u->offered))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+********************************************************************/
+
+BOOL spoolss_io_q_addprintprocessor(const char *desc, SPOOL_Q_ADDPRINTPROCESSOR *q_u, prs_struct *ps, int depth)
+{
+ prs_debug(ps, depth, desc, "spoolss_io_q_addprintprocessor");
+ depth++;
+
+ if (!prs_align(ps))
+ return False;
+
+ if (!prs_uint32("server_ptr", ps, depth, &q_u->server_ptr))
+ return False;
+ if (!smb_io_unistr2("server", &q_u->server, q_u->server_ptr, ps, depth))
+ return False;
+
+ if (!prs_align(ps))
+ return False;
+ if (!smb_io_unistr2("environment", &q_u->environment, True, ps, depth))
+ return False;
+
+ if (!prs_align(ps))
+ return False;
+ if (!smb_io_unistr2("path", &q_u->path, True, ps, depth))
+ return False;
+
+ if (!prs_align(ps))
+ return False;
+ if (!smb_io_unistr2("name", &q_u->name, True, ps, depth))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+********************************************************************/
+
+BOOL spoolss_io_r_addprintprocessor(const char *desc, SPOOL_R_ADDPRINTPROCESSOR *r_u, prs_struct *ps, int depth)
+{
+ prs_debug(ps, depth, desc, "spoolss_io_r_addprintproicessor");
+ depth++;
+
+ if (!prs_align(ps))
+ return False;
+
+ if (!prs_werror("status", ps, depth, &r_u->status))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+********************************************************************/
+
+BOOL spoolss_io_r_enumprintprocdatatypes(const char *desc, SPOOL_R_ENUMPRINTPROCDATATYPES *r_u, prs_struct *ps, int depth)
+{
+ prs_debug(ps, depth, desc, "spoolss_io_r_enumprintprocdatatypes");
+ depth++;
+
+ if (!prs_align(ps))
+ return False;
+
+ if (!spoolss_io_buffer("", ps, depth, &r_u->buffer))
+ return False;
+
+ if (!prs_align(ps))
+ return False;
+
+ if (!prs_uint32("needed", ps, depth, &r_u->needed))
+ return False;
+
+ if (!prs_uint32("returned", ps, depth, &r_u->returned))
+ return False;
+
+ if (!prs_werror("status", ps, depth, &r_u->status))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+********************************************************************/
+
+BOOL spoolss_io_q_enumprintprocdatatypes(const char *desc, SPOOL_Q_ENUMPRINTPROCDATATYPES *q_u, prs_struct *ps, int depth)
+{
+ prs_debug(ps, depth, desc, "spoolss_io_q_enumprintprocdatatypes");
+ depth++;
+
+ if (!prs_align(ps))
+ return False;
+
+ if (!prs_uint32("name_ptr", ps, depth, &q_u->name_ptr))
+ return False;
+ if (!smb_io_unistr2("name", &q_u->name, True, ps, depth))
+ return False;
+
+ if (!prs_align(ps))
+ return False;
+
+ if (!prs_uint32("processor_ptr", ps, depth, &q_u->processor_ptr))
+ return False;
+ if (!smb_io_unistr2("processor", &q_u->processor, q_u->processor_ptr, ps, depth))
+ return False;
+
+ if (!prs_align(ps))
+ return False;
+
+ if (!prs_uint32("level", ps, depth, &q_u->level))
+ return False;
+
+ if(!spoolss_io_buffer("buffer", ps, depth, &q_u->buffer))
+ return False;
+
+ if (!prs_align(ps))
+ return False;
+
+ if (!prs_uint32("offered", ps, depth, &q_u->offered))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ Parse a SPOOL_Q_ENUMPRINTMONITORS structure.
+********************************************************************/
+
+BOOL spoolss_io_q_enumprintmonitors(const char *desc, SPOOL_Q_ENUMPRINTMONITORS *q_u, prs_struct *ps, int depth)
+{
+ prs_debug(ps, depth, desc, "spoolss_io_q_enumprintmonitors");
+ depth++;
+
+ if (!prs_align(ps))
+ return False;
+
+ if (!prs_uint32("name_ptr", ps, depth, &q_u->name_ptr))
+ return False;
+ if (!smb_io_unistr2("name", &q_u->name, True, ps, depth))
+ return False;
+
+ if (!prs_align(ps))
+ return False;
+
+ if (!prs_uint32("level", ps, depth, &q_u->level))
+ return False;
+
+ if(!spoolss_io_buffer("", ps, depth, &q_u->buffer))
+ return False;
+
+ if (!prs_align(ps))
+ return False;
+
+ if (!prs_uint32("offered", ps, depth, &q_u->offered))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+********************************************************************/
+
+BOOL spoolss_io_r_enumprintmonitors(const char *desc, SPOOL_R_ENUMPRINTMONITORS *r_u, prs_struct *ps, int depth)
+{
+ prs_debug(ps, depth, desc, "spoolss_io_r_enumprintmonitors");
+ depth++;
+
+ if (!prs_align(ps))
+ return False;
+
+ if (!spoolss_io_buffer("", ps, depth, &r_u->buffer))
+ return False;
+
+ if (!prs_align(ps))
+ return False;
+
+ if (!prs_uint32("needed", ps, depth, &r_u->needed))
+ return False;
+
+ if (!prs_uint32("returned", ps, depth, &r_u->returned))
+ return False;
+
+ if (!prs_werror("status", ps, depth, &r_u->status))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+********************************************************************/
+
+BOOL spoolss_io_r_enumprinterdata(const char *desc, SPOOL_R_ENUMPRINTERDATA *r_u, prs_struct *ps, int depth)
+{
+ prs_debug(ps, depth, desc, "spoolss_io_r_enumprinterdata");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+ if(!prs_uint32("valuesize", ps, depth, &r_u->valuesize))
+ return False;
+
+ if (UNMARSHALLING(ps) && r_u->valuesize) {
+ r_u->value = (uint16 *)prs_alloc_mem(ps, r_u->valuesize * 2);
+ if (!r_u->value) {
+ DEBUG(0, ("spoolss_io_r_enumprinterdata: out of memory for printerdata value\n"));
+ return False;
+ }
+ }
+
+ if(!prs_uint16uni(False, "value", ps, depth, r_u->value, r_u->valuesize ))
+ return False;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("realvaluesize", ps, depth, &r_u->realvaluesize))
+ return False;
+
+ if(!prs_uint32("type", ps, depth, &r_u->type))
+ return False;
+
+ if(!prs_uint32("datasize", ps, depth, &r_u->datasize))
+ return False;
+
+ if (UNMARSHALLING(ps) && r_u->datasize) {
+ r_u->data = (uint8 *)prs_alloc_mem(ps, r_u->datasize);
+ if (!r_u->data) {
+ DEBUG(0, ("spoolss_io_r_enumprinterdata: out of memory for printerdata data\n"));
+ return False;
+ }
+ }
+
+ if(!prs_uint8s(False, "data", ps, depth, r_u->data, r_u->datasize))
+ return False;
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("realdatasize", ps, depth, &r_u->realdatasize))
+ return False;
+ if(!prs_werror("status", ps, depth, &r_u->status))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+********************************************************************/
+
+BOOL spoolss_io_q_enumprinterdata(const char *desc, SPOOL_Q_ENUMPRINTERDATA *q_u, prs_struct *ps, int depth)
+{
+ prs_debug(ps, depth, desc, "spoolss_io_q_enumprinterdata");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+ if(!smb_io_pol_hnd("printer handle",&q_u->handle,ps,depth))
+ return False;
+ if(!prs_uint32("index", ps, depth, &q_u->index))
+ return False;
+ if(!prs_uint32("valuesize", ps, depth, &q_u->valuesize))
+ return False;
+ if(!prs_uint32("datasize", ps, depth, &q_u->datasize))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+********************************************************************/
+
+BOOL make_spoolss_q_enumprinterdata(SPOOL_Q_ENUMPRINTERDATA *q_u,
+ const POLICY_HND *hnd,
+ uint32 idx, uint32 valuelen, uint32 datalen)
+{
+ memcpy(&q_u->handle, hnd, sizeof(q_u->handle));
+ q_u->index=idx;
+ q_u->valuesize=valuelen;
+ q_u->datasize=datalen;
+
+ return True;
+}
+
+/*******************************************************************
+********************************************************************/
+
+BOOL make_spoolss_q_enumprinterdataex(SPOOL_Q_ENUMPRINTERDATAEX *q_u,
+ const POLICY_HND *hnd, const char *key,
+ uint32 size)
+{
+ memcpy(&q_u->handle, hnd, sizeof(q_u->handle));
+ init_unistr2(&q_u->key, key, UNI_STR_TERMINATE);
+ q_u->size = size;
+
+ return True;
+}
+
+/*******************************************************************
+********************************************************************/
+BOOL make_spoolss_q_setprinterdata(SPOOL_Q_SETPRINTERDATA *q_u, const POLICY_HND *hnd,
+ char* value, uint32 data_type, char* data, uint32 data_size)
+{
+ memcpy(&q_u->handle, hnd, sizeof(q_u->handle));
+ q_u->type = data_type;
+ init_unistr2(&q_u->value, value, UNI_STR_TERMINATE);
+
+ q_u->max_len = q_u->real_len = data_size;
+ q_u->data = (unsigned char *)data;
+
+ return True;
+}
+
+/*******************************************************************
+********************************************************************/
+BOOL make_spoolss_q_setprinterdataex(SPOOL_Q_SETPRINTERDATAEX *q_u, const POLICY_HND *hnd,
+ char *key, char* value, uint32 data_type, char* data,
+ uint32 data_size)
+{
+ memcpy(&q_u->handle, hnd, sizeof(q_u->handle));
+ q_u->type = data_type;
+ init_unistr2(&q_u->value, value, UNI_STR_TERMINATE);
+ init_unistr2(&q_u->key, key, UNI_STR_TERMINATE);
+
+ q_u->max_len = q_u->real_len = data_size;
+ q_u->data = (unsigned char *)data;
+
+ return True;
+}
+
+/*******************************************************************
+********************************************************************/
+
+BOOL spoolss_io_q_setprinterdata(const char *desc, SPOOL_Q_SETPRINTERDATA *q_u, prs_struct *ps, int depth)
+{
+ prs_debug(ps, depth, desc, "spoolss_io_q_setprinterdata");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+ if(!smb_io_pol_hnd("printer handle", &q_u->handle, ps, depth))
+ return False;
+ if(!smb_io_unistr2("", &q_u->value, True, ps, depth))
+ return False;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("type", ps, depth, &q_u->type))
+ return False;
+
+ if(!prs_uint32("max_len", ps, depth, &q_u->max_len))
+ return False;
+
+ switch (q_u->type)
+ {
+ case REG_SZ:
+ case REG_BINARY:
+ case REG_DWORD:
+ case REG_MULTI_SZ:
+ if (q_u->max_len) {
+ if (UNMARSHALLING(ps))
+ q_u->data=(uint8 *)prs_alloc_mem(ps, q_u->max_len * sizeof(uint8));
+ if(q_u->data == NULL)
+ return False;
+ if(!prs_uint8s(False,"data", ps, depth, q_u->data, q_u->max_len))
+ return False;
+ }
+ if(!prs_align(ps))
+ return False;
+ break;
+ }
+
+ if(!prs_uint32("real_len", ps, depth, &q_u->real_len))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+********************************************************************/
+
+BOOL spoolss_io_r_setprinterdata(const char *desc, SPOOL_R_SETPRINTERDATA *r_u, prs_struct *ps, int depth)
+{
+ prs_debug(ps, depth, desc, "spoolss_io_r_setprinterdata");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+ if(!prs_werror("status", ps, depth, &r_u->status))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+********************************************************************/
+BOOL spoolss_io_q_resetprinter(const char *desc, SPOOL_Q_RESETPRINTER *q_u, prs_struct *ps, int depth)
+{
+ prs_debug(ps, depth, desc, "spoolss_io_q_resetprinter");
+ depth++;
+
+ if (!prs_align(ps))
+ return False;
+ if (!smb_io_pol_hnd("printer handle", &q_u->handle, ps, depth))
+ return False;
+
+ if (!prs_uint32("datatype_ptr", ps, depth, &q_u->datatype_ptr))
+ return False;
+
+ if (q_u->datatype_ptr) {
+ if (!smb_io_unistr2("datatype", &q_u->datatype, q_u->datatype_ptr?True:False, ps, depth))
+ return False;
+ }
+
+ if (!spoolss_io_devmode_cont(desc, &q_u->devmode_ctr, ps, depth))
+ return False;
+
+ return True;
+}
+
+
+/*******************************************************************
+********************************************************************/
+BOOL spoolss_io_r_resetprinter(const char *desc, SPOOL_R_RESETPRINTER *r_u, prs_struct *ps, int depth)
+{
+ prs_debug(ps, depth, desc, "spoolss_io_r_resetprinter");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+ if(!prs_werror("status", ps, depth, &r_u->status))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+********************************************************************/
+
+static BOOL spoolss_io_addform(const char *desc, FORM *f, uint32 ptr, prs_struct *ps, int depth)
+{
+ prs_debug(ps, depth, desc, "spoolss_io_addform");
+ depth++;
+ if(!prs_align(ps))
+ return False;
+
+ if (ptr!=0)
+ {
+ if(!prs_uint32("flags", ps, depth, &f->flags))
+ return False;
+ if(!prs_uint32("name_ptr", ps, depth, &f->name_ptr))
+ return False;
+ if(!prs_uint32("size_x", ps, depth, &f->size_x))
+ return False;
+ if(!prs_uint32("size_y", ps, depth, &f->size_y))
+ return False;
+ if(!prs_uint32("left", ps, depth, &f->left))
+ return False;
+ if(!prs_uint32("top", ps, depth, &f->top))
+ return False;
+ if(!prs_uint32("right", ps, depth, &f->right))
+ return False;
+ if(!prs_uint32("bottom", ps, depth, &f->bottom))
+ return False;
+
+ if(!smb_io_unistr2("", &f->name, f->name_ptr, ps, depth))
+ return False;
+ }
+
+ return True;
+}
+
+/*******************************************************************
+********************************************************************/
+
+BOOL spoolss_io_q_deleteform(const char *desc, SPOOL_Q_DELETEFORM *q_u, prs_struct *ps, int depth)
+{
+ prs_debug(ps, depth, desc, "spoolss_io_q_deleteform");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+ if(!smb_io_pol_hnd("printer handle", &q_u->handle, ps, depth))
+ return False;
+ if(!smb_io_unistr2("form name", &q_u->name, True, ps, depth))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+********************************************************************/
+
+BOOL spoolss_io_r_deleteform(const char *desc, SPOOL_R_DELETEFORM *r_u, prs_struct *ps, int depth)
+{
+ prs_debug(ps, depth, desc, "spoolss_io_r_deleteform");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+ if(!prs_werror("status", ps, depth, &r_u->status))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+********************************************************************/
+
+BOOL spoolss_io_q_addform(const char *desc, SPOOL_Q_ADDFORM *q_u, prs_struct *ps, int depth)
+{
+ uint32 useless_ptr=1;
+ prs_debug(ps, depth, desc, "spoolss_io_q_addform");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+ if(!smb_io_pol_hnd("printer handle", &q_u->handle, ps, depth))
+ return False;
+ if(!prs_uint32("level", ps, depth, &q_u->level))
+ return False;
+ if(!prs_uint32("level2", ps, depth, &q_u->level2))
+ return False;
+
+ if (q_u->level==1)
+ {
+ if(!prs_uint32("useless_ptr", ps, depth, &useless_ptr))
+ return False;
+ if(!spoolss_io_addform("", &q_u->form, useless_ptr, ps, depth))
+ return False;
+ }
+
+ return True;
+}
+
+/*******************************************************************
+********************************************************************/
+
+BOOL spoolss_io_r_addform(const char *desc, SPOOL_R_ADDFORM *r_u, prs_struct *ps, int depth)
+{
+ prs_debug(ps, depth, desc, "spoolss_io_r_addform");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+ if(!prs_werror("status", ps, depth, &r_u->status))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+********************************************************************/
+
+BOOL spoolss_io_q_setform(const char *desc, SPOOL_Q_SETFORM *q_u, prs_struct *ps, int depth)
+{
+ uint32 useless_ptr=1;
+ prs_debug(ps, depth, desc, "spoolss_io_q_setform");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+ if(!smb_io_pol_hnd("printer handle", &q_u->handle, ps, depth))
+ return False;
+ if(!smb_io_unistr2("", &q_u->name, True, ps, depth))
+ return False;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("level", ps, depth, &q_u->level))
+ return False;
+ if(!prs_uint32("level2", ps, depth, &q_u->level2))
+ return False;
+
+ if (q_u->level==1)
+ {
+ if(!prs_uint32("useless_ptr", ps, depth, &useless_ptr))
+ return False;
+ if(!spoolss_io_addform("", &q_u->form, useless_ptr, ps, depth))
+ return False;
+ }
+
+ return True;
+}
+
+/*******************************************************************
+********************************************************************/
+
+BOOL spoolss_io_r_setform(const char *desc, SPOOL_R_SETFORM *r_u, prs_struct *ps, int depth)
+{
+ prs_debug(ps, depth, desc, "spoolss_io_r_setform");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+ if(!prs_werror("status", ps, depth, &r_u->status))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ Parse a SPOOL_R_GETJOB structure.
+********************************************************************/
+
+BOOL spoolss_io_r_getjob(const char *desc, SPOOL_R_GETJOB *r_u, prs_struct *ps, int depth)
+{
+ prs_debug(ps, depth, desc, "spoolss_io_r_getjob");
+ depth++;
+
+ if (!prs_align(ps))
+ return False;
+
+ if (!spoolss_io_buffer("", ps, depth, &r_u->buffer))
+ return False;
+
+ if (!prs_align(ps))
+ return False;
+
+ if (!prs_uint32("needed", ps, depth, &r_u->needed))
+ return False;
+
+ if (!prs_werror("status", ps, depth, &r_u->status))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ Parse a SPOOL_Q_GETJOB structure.
+********************************************************************/
+
+BOOL spoolss_io_q_getjob(const char *desc, SPOOL_Q_GETJOB *q_u, prs_struct *ps, int depth)
+{
+ prs_debug(ps, depth, desc, "");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!smb_io_pol_hnd("printer handle",&q_u->handle,ps,depth))
+ return False;
+ if(!prs_uint32("jobid", ps, depth, &q_u->jobid))
+ return False;
+ if(!prs_uint32("level", ps, depth, &q_u->level))
+ return False;
+
+ if(!spoolss_io_buffer("", ps, depth, &q_u->buffer))
+ return False;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("offered", ps, depth, &q_u->offered))
+ return False;
+
+ return True;
+}
+
+void free_devmode(DEVICEMODE *devmode)
+{
+ if (devmode!=NULL) {
+ SAFE_FREE(devmode->private);
+ SAFE_FREE(devmode);
+ }
+}
+
+void free_printer_info_1(PRINTER_INFO_1 *printer)
+{
+ SAFE_FREE(printer);
+}
+
+void free_printer_info_2(PRINTER_INFO_2 *printer)
+{
+ if (printer!=NULL) {
+ free_devmode(printer->devmode);
+ printer->devmode = NULL;
+ SAFE_FREE(printer);
+ }
+}
+
+void free_printer_info_3(PRINTER_INFO_3 *printer)
+{
+ SAFE_FREE(printer);
+}
+
+void free_printer_info_4(PRINTER_INFO_4 *printer)
+{
+ SAFE_FREE(printer);
+}
+
+void free_printer_info_5(PRINTER_INFO_5 *printer)
+{
+ SAFE_FREE(printer);
+}
+
+void free_printer_info_7(PRINTER_INFO_7 *printer)
+{
+ SAFE_FREE(printer);
+}
+
+void free_job_info_2(JOB_INFO_2 *job)
+{
+ if (job!=NULL)
+ free_devmode(job->devmode);
+}
+
+/*******************************************************************
+ * init a structure.
+ ********************************************************************/
+
+BOOL make_spoolss_q_replyopenprinter(SPOOL_Q_REPLYOPENPRINTER *q_u,
+ const fstring string, uint32 printer, uint32 type)
+{
+ if (q_u == NULL)
+ return False;
+
+ init_unistr2(&q_u->string, string, UNI_STR_TERMINATE);
+
+ q_u->printer=printer;
+ q_u->type=type;
+
+ q_u->unknown0=0x0;
+ q_u->unknown1=0x0;
+
+ return True;
+}
+
+/*******************************************************************
+ Parse a SPOOL_Q_REPLYOPENPRINTER structure.
+********************************************************************/
+
+BOOL spoolss_io_q_replyopenprinter(const char *desc, SPOOL_Q_REPLYOPENPRINTER *q_u, prs_struct *ps, int depth)
+{
+ prs_debug(ps, depth, desc, "spoolss_io_q_replyopenprinter");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!smb_io_unistr2("", &q_u->string, True, ps, depth))
+ return False;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("printer", ps, depth, &q_u->printer))
+ return False;
+ if(!prs_uint32("type", ps, depth, &q_u->type))
+ return False;
+
+ if(!prs_uint32("unknown0", ps, depth, &q_u->unknown0))
+ return False;
+ if(!prs_uint32("unknown1", ps, depth, &q_u->unknown1))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ Parse a SPOOL_R_REPLYOPENPRINTER structure.
+********************************************************************/
+
+BOOL spoolss_io_r_replyopenprinter(const char *desc, SPOOL_R_REPLYOPENPRINTER *r_u, prs_struct *ps, int depth)
+{
+ prs_debug(ps, depth, desc, "spoolss_io_r_replyopenprinter");
+ depth++;
+
+ if (!prs_align(ps))
+ return False;
+
+ if(!smb_io_pol_hnd("printer handle",&r_u->handle,ps,depth))
+ return False;
+
+ if (!prs_werror("status", ps, depth, &r_u->status))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ * init a structure.
+ ********************************************************************/
+BOOL make_spoolss_q_routerreplyprinter(SPOOL_Q_ROUTERREPLYPRINTER *q_u, POLICY_HND *hnd,
+ uint32 condition, uint32 change_id)
+{
+
+ memcpy(&q_u->handle, hnd, sizeof(q_u->handle));
+
+ q_u->condition = condition;
+ q_u->change_id = change_id;
+
+ /* magic values */
+ q_u->unknown1 = 0x1;
+ memset(q_u->unknown2, 0x0, 5);
+ q_u->unknown2[0] = 0x1;
+
+ return True;
+}
+
+/*******************************************************************
+ Parse a SPOOL_Q_ROUTERREPLYPRINTER structure.
+********************************************************************/
+BOOL spoolss_io_q_routerreplyprinter (const char *desc, SPOOL_Q_ROUTERREPLYPRINTER *q_u, prs_struct *ps, int depth)
+{
+
+ prs_debug(ps, depth, desc, "spoolss_io_q_routerreplyprinter");
+ depth++;
+
+ if (!prs_align(ps))
+ return False;
+
+ if(!smb_io_pol_hnd("printer handle",&q_u->handle,ps,depth))
+ return False;
+
+ if (!prs_uint32("condition", ps, depth, &q_u->condition))
+ return False;
+
+ if (!prs_uint32("unknown1", ps, depth, &q_u->unknown1))
+ return False;
+
+ if (!prs_uint32("change_id", ps, depth, &q_u->change_id))
+ return False;
+
+ if (!prs_uint8s(False, "private", ps, depth, q_u->unknown2, 5))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ Parse a SPOOL_R_ROUTERREPLYPRINTER structure.
+********************************************************************/
+BOOL spoolss_io_r_routerreplyprinter (const char *desc, SPOOL_R_ROUTERREPLYPRINTER *r_u, prs_struct *ps, int depth)
+{
+ prs_debug(ps, depth, desc, "spoolss_io_r_routerreplyprinter");
+ depth++;
+
+ if (!prs_align(ps))
+ return False;
+
+ if (!prs_werror("status", ps, depth, &r_u->status))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ * init a structure.
+ ********************************************************************/
+
+BOOL make_spoolss_q_reply_closeprinter(SPOOL_Q_REPLYCLOSEPRINTER *q_u, POLICY_HND *hnd)
+{
+ if (q_u == NULL)
+ return False;
+
+ memcpy(&q_u->handle, hnd, sizeof(q_u->handle));
+
+ return True;
+}
+
+/*******************************************************************
+ Parse a SPOOL_Q_REPLYCLOSEPRINTER structure.
+********************************************************************/
+
+BOOL spoolss_io_q_replycloseprinter(const char *desc, SPOOL_Q_REPLYCLOSEPRINTER *q_u, prs_struct *ps, int depth)
+{
+ prs_debug(ps, depth, desc, "spoolss_io_q_replycloseprinter");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!smb_io_pol_hnd("printer handle",&q_u->handle,ps,depth))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ Parse a SPOOL_R_REPLYCLOSEPRINTER structure.
+********************************************************************/
+
+BOOL spoolss_io_r_replycloseprinter(const char *desc, SPOOL_R_REPLYCLOSEPRINTER *r_u, prs_struct *ps, int depth)
+{
+ prs_debug(ps, depth, desc, "spoolss_io_r_replycloseprinter");
+ depth++;
+
+ if (!prs_align(ps))
+ return False;
+
+ if(!smb_io_pol_hnd("printer handle",&r_u->handle,ps,depth))
+ return False;
+
+ if (!prs_werror("status", ps, depth, &r_u->status))
+ return False;
+
+ return True;
+}
+
+#if 0 /* JERRY - not currently used but could be :-) */
+
+/*******************************************************************
+ Deep copy a SPOOL_NOTIFY_INFO_DATA structure
+ ******************************************************************/
+static BOOL copy_spool_notify_info_data(SPOOL_NOTIFY_INFO_DATA *dst,
+ SPOOL_NOTIFY_INFO_DATA *src, int n)
+{
+ int i;
+
+ memcpy(dst, src, sizeof(SPOOL_NOTIFY_INFO_DATA)*n);
+
+ for (i=0; i<n; i++) {
+ int len;
+ uint16 *s = NULL;
+
+ if (src->size != POINTER)
+ continue;
+ len = src->notify_data.data.length;
+ s = malloc(sizeof(uint16)*len);
+ if (s == NULL) {
+ DEBUG(0,("copy_spool_notify_info_data: malloc() failed!\n"));
+ return False;
+ }
+
+ memcpy(s, src->notify_data.data.string, len*2);
+ dst->notify_data.data.string = s;
+ }
+
+ return True;
+}
+
+/*******************************************************************
+ Deep copy a SPOOL_NOTIFY_INFO structure
+ ******************************************************************/
+static BOOL copy_spool_notify_info(SPOOL_NOTIFY_INFO *dst, SPOOL_NOTIFY_INFO *src)
+{
+ if (!dst) {
+ DEBUG(0,("copy_spool_notify_info: NULL destination pointer!\n"));
+ return False;
+ }
+
+ dst->version = src->version;
+ dst->flags = src->flags;
+ dst->count = src->count;
+
+ if (dst->count)
+ {
+ dst->data = malloc(dst->count * sizeof(SPOOL_NOTIFY_INFO_DATA));
+
+ DEBUG(10,("copy_spool_notify_info: allocating space for [%d] PRINTER_NOTIFY_INFO_DATA entries\n",
+ dst->count));
+
+ if (dst->data == NULL) {
+ DEBUG(0,("copy_spool_notify_info: malloc() failed for [%d] entries!\n",
+ dst->count));
+ return False;
+ }
+
+ return (copy_spool_notify_info_data(dst->data, src->data, src->count));
+ }
+
+ return True;
+}
+#endif /* JERRY */
+
+/*******************************************************************
+ * init a structure.
+ ********************************************************************/
+
+BOOL make_spoolss_q_reply_rrpcn(SPOOL_Q_REPLY_RRPCN *q_u, POLICY_HND *hnd,
+ uint32 change_low, uint32 change_high,
+ SPOOL_NOTIFY_INFO *info)
+{
+ if (q_u == NULL)
+ return False;
+
+ memcpy(&q_u->handle, hnd, sizeof(q_u->handle));
+
+ q_u->change_low=change_low;
+ q_u->change_high=change_high;
+
+ q_u->unknown0=0x0;
+ q_u->unknown1=0x0;
+
+ q_u->info_ptr=0x0FF0ADDE;
+
+ q_u->info.version=2;
+
+ if (info->count) {
+ DEBUG(10,("make_spoolss_q_reply_rrpcn: [%d] PRINTER_NOTIFY_INFO_DATA\n",
+ info->count));
+ q_u->info.version = info->version;
+ q_u->info.flags = info->flags;
+ q_u->info.count = info->count;
+ /* pointer field - be careful! */
+ q_u->info.data = info->data;
+ }
+ else {
+ q_u->info.flags=PRINTER_NOTIFY_INFO_DISCARDED;
+ q_u->info.count=0;
+ }
+
+ return True;
+}
+
+/*******************************************************************
+ Parse a SPOOL_Q_REPLY_RRPCN structure.
+********************************************************************/
+
+BOOL spoolss_io_q_reply_rrpcn(const char *desc, SPOOL_Q_REPLY_RRPCN *q_u, prs_struct *ps, int depth)
+{
+ prs_debug(ps, depth, desc, "spoolss_io_q_reply_rrpcn");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!smb_io_pol_hnd("printer handle",&q_u->handle,ps,depth))
+ return False;
+
+ if (!prs_uint32("change_low", ps, depth, &q_u->change_low))
+ return False;
+
+ if (!prs_uint32("change_high", ps, depth, &q_u->change_high))
+ return False;
+
+ if (!prs_uint32("unknown0", ps, depth, &q_u->unknown0))
+ return False;
+
+ if (!prs_uint32("unknown1", ps, depth, &q_u->unknown1))
+ return False;
+
+ if (!prs_uint32("info_ptr", ps, depth, &q_u->info_ptr))
+ return False;
+
+ if(q_u->info_ptr!=0)
+ if(!smb_io_notify_info(desc, &q_u->info, ps, depth))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ Parse a SPOOL_R_REPLY_RRPCN structure.
+********************************************************************/
+
+BOOL spoolss_io_r_reply_rrpcn(const char *desc, SPOOL_R_REPLY_RRPCN *r_u, prs_struct *ps, int depth)
+{
+ prs_debug(ps, depth, desc, "spoolss_io_r_reply_rrpcn");
+ depth++;
+
+ if (!prs_align(ps))
+ return False;
+
+ if (!prs_uint32("unknown0", ps, depth, &r_u->unknown0))
+ return False;
+
+ if (!prs_werror("status", ps, depth, &r_u->status))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ * read a structure.
+ * called from spoolss_q_getprinterdataex (srv_spoolss.c)
+ ********************************************************************/
+
+BOOL spoolss_io_q_getprinterdataex(const char *desc, SPOOL_Q_GETPRINTERDATAEX *q_u, prs_struct *ps, int depth)
+{
+ if (q_u == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "spoolss_io_q_getprinterdataex");
+ depth++;
+
+ if (!prs_align(ps))
+ return False;
+ if (!smb_io_pol_hnd("printer handle",&q_u->handle,ps,depth))
+ return False;
+ if (!prs_align(ps))
+ return False;
+ if (!smb_io_unistr2("keyname", &q_u->keyname,True,ps,depth))
+ return False;
+ if (!prs_align(ps))
+ return False;
+ if (!smb_io_unistr2("valuename", &q_u->valuename,True,ps,depth))
+ return False;
+ if (!prs_align(ps))
+ return False;
+ if (!prs_uint32("size", ps, depth, &q_u->size))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ * write a structure.
+ * called from spoolss_r_getprinterdataex (srv_spoolss.c)
+ ********************************************************************/
+
+BOOL spoolss_io_r_getprinterdataex(const char *desc, SPOOL_R_GETPRINTERDATAEX *r_u, prs_struct *ps, int depth)
+{
+ if (r_u == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "spoolss_io_r_getprinterdataex");
+ depth++;
+
+ if (!prs_align(ps))
+ return False;
+ if (!prs_uint32("type", ps, depth, &r_u->type))
+ return False;
+ if (!prs_uint32("size", ps, depth, &r_u->size))
+ return False;
+
+ if (UNMARSHALLING(ps) && r_u->size) {
+ r_u->data = (unsigned char *)prs_alloc_mem(ps, r_u->size);
+ if(!r_u->data)
+ return False;
+ }
+
+ if (!prs_uint8s(False,"data", ps, depth, r_u->data, r_u->size))
+ return False;
+
+ if (!prs_align(ps))
+ return False;
+
+ if (!prs_uint32("needed", ps, depth, &r_u->needed))
+ return False;
+ if (!prs_werror("status", ps, depth, &r_u->status))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ * read a structure.
+ ********************************************************************/
+
+BOOL spoolss_io_q_setprinterdataex(const char *desc, SPOOL_Q_SETPRINTERDATAEX *q_u, prs_struct *ps, int depth)
+{
+ prs_debug(ps, depth, desc, "spoolss_io_q_setprinterdataex");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+ if(!smb_io_pol_hnd("printer handle", &q_u->handle, ps, depth))
+ return False;
+ if(!smb_io_unistr2("", &q_u->key, True, ps, depth))
+ return False;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!smb_io_unistr2("", &q_u->value, True, ps, depth))
+ return False;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("type", ps, depth, &q_u->type))
+ return False;
+
+ if(!prs_uint32("max_len", ps, depth, &q_u->max_len))
+ return False;
+
+ switch (q_u->type)
+ {
+ case 0x1:
+ case 0x3:
+ case 0x4:
+ case 0x7:
+ if (q_u->max_len) {
+ if (UNMARSHALLING(ps))
+ q_u->data=(uint8 *)prs_alloc_mem(ps, q_u->max_len * sizeof(uint8));
+ if(q_u->data == NULL)
+ return False;
+ if(!prs_uint8s(False,"data", ps, depth, q_u->data, q_u->max_len))
+ return False;
+ }
+ if(!prs_align(ps))
+ return False;
+ break;
+ }
+
+ if(!prs_uint32("real_len", ps, depth, &q_u->real_len))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ * write a structure.
+ ********************************************************************/
+
+BOOL spoolss_io_r_setprinterdataex(const char *desc, SPOOL_R_SETPRINTERDATAEX *r_u, prs_struct *ps, int depth)
+{
+ prs_debug(ps, depth, desc, "spoolss_io_r_setprinterdataex");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+ if(!prs_werror("status", ps, depth, &r_u->status))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ * read a structure.
+ ********************************************************************/
+BOOL make_spoolss_q_enumprinterkey(SPOOL_Q_ENUMPRINTERKEY *q_u,
+ POLICY_HND *hnd, const char *key,
+ uint32 size)
+{
+ DEBUG(5,("make_spoolss_q_enumprinterkey\n"));
+
+ memcpy(&q_u->handle, hnd, sizeof(q_u->handle));
+ init_unistr2(&q_u->key, key, UNI_STR_TERMINATE);
+ q_u->size = size;
+
+ return True;
+}
+
+/*******************************************************************
+ * read a structure.
+ ********************************************************************/
+
+BOOL spoolss_io_q_enumprinterkey(const char *desc, SPOOL_Q_ENUMPRINTERKEY *q_u, prs_struct *ps, int depth)
+{
+ prs_debug(ps, depth, desc, "spoolss_io_q_enumprinterkey");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+ if(!smb_io_pol_hnd("printer handle", &q_u->handle, ps, depth))
+ return False;
+
+ if(!smb_io_unistr2("", &q_u->key, True, ps, depth))
+ return False;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("size", ps, depth, &q_u->size))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ * write a structure.
+ ********************************************************************/
+
+BOOL spoolss_io_r_enumprinterkey(const char *desc, SPOOL_R_ENUMPRINTERKEY *r_u, prs_struct *ps, int depth)
+{
+ prs_debug(ps, depth, desc, "spoolss_io_r_enumprinterkey");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if (!smb_io_buffer5("", &r_u->keys, ps, depth))
+ return False;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("needed", ps, depth, &r_u->needed))
+ return False;
+
+ if(!prs_werror("status", ps, depth, &r_u->status))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ * read a structure.
+ ********************************************************************/
+
+BOOL make_spoolss_q_deleteprinterkey(SPOOL_Q_DELETEPRINTERKEY *q_u,
+ POLICY_HND *hnd, char *keyname)
+{
+ DEBUG(5,("make_spoolss_q_deleteprinterkey\n"));
+
+ memcpy(&q_u->handle, hnd, sizeof(q_u->handle));
+ init_unistr2(&q_u->keyname, keyname, UNI_STR_TERMINATE);
+
+ return True;
+}
+
+/*******************************************************************
+ * read a structure.
+ ********************************************************************/
+
+BOOL spoolss_io_q_deleteprinterkey(const char *desc, SPOOL_Q_DELETEPRINTERKEY *q_u, prs_struct *ps, int depth)
+{
+ prs_debug(ps, depth, desc, "spoolss_io_q_deleteprinterkey");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+ if(!smb_io_pol_hnd("printer handle", &q_u->handle, ps, depth))
+ return False;
+
+ if(!smb_io_unistr2("", &q_u->keyname, True, ps, depth))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ * write a structure.
+ ********************************************************************/
+
+BOOL spoolss_io_r_deleteprinterkey(const char *desc, SPOOL_R_DELETEPRINTERKEY *r_u, prs_struct *ps, int depth)
+{
+ prs_debug(ps, depth, desc, "spoolss_io_r_deleteprinterkey");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_werror("status", ps, depth, &r_u->status))
+ return False;
+
+ return True;
+}
+
+
+/*******************************************************************
+ * read a structure.
+ ********************************************************************/
+
+BOOL spoolss_io_q_enumprinterdataex(const char *desc, SPOOL_Q_ENUMPRINTERDATAEX *q_u, prs_struct *ps, int depth)
+{
+ prs_debug(ps, depth, desc, "spoolss_io_q_enumprinterdataex");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+ if(!smb_io_pol_hnd("printer handle", &q_u->handle, ps, depth))
+ return False;
+
+ if(!smb_io_unistr2("", &q_u->key, True, ps, depth))
+ return False;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("size", ps, depth, &q_u->size))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+********************************************************************/
+
+static BOOL spoolss_io_printer_enum_values_ctr(const char *desc, prs_struct *ps,
+ PRINTER_ENUM_VALUES_CTR *ctr, int depth)
+{
+ int i;
+ uint32 valuename_offset,
+ data_offset,
+ current_offset;
+ const uint32 basic_unit = 20; /* size of static portion of enum_values */
+
+ prs_debug(ps, depth, desc, "spoolss_io_printer_enum_values_ctr");
+ depth++;
+
+ /*
+ * offset data begins at 20 bytes per structure * size_of_array.
+ * Don't forget the uint32 at the beginning
+ * */
+
+ current_offset = basic_unit * ctr->size_of_array;
+
+ /* first loop to write basic enum_value information */
+
+ if (UNMARSHALLING(ps)) {
+ ctr->values = (PRINTER_ENUM_VALUES *)prs_alloc_mem(
+ ps, ctr->size_of_array * sizeof(PRINTER_ENUM_VALUES));
+ if (!ctr->values)
+ return False;
+ }
+
+ for (i=0; i<ctr->size_of_array; i++) {
+ valuename_offset = current_offset;
+ if (!prs_uint32("valuename_offset", ps, depth, &valuename_offset))
+ return False;
+
+ if (!prs_uint32("value_len", ps, depth, &ctr->values[i].value_len))
+ return False;
+
+ if (!prs_uint32("type", ps, depth, &ctr->values[i].type))
+ return False;
+
+ data_offset = ctr->values[i].value_len + valuename_offset;
+
+ if (!prs_uint32("data_offset", ps, depth, &data_offset))
+ return False;
+
+ if (!prs_uint32("data_len", ps, depth, &ctr->values[i].data_len))
+ return False;
+
+ current_offset = data_offset + ctr->values[i].data_len - basic_unit;
+ /* account for 2 byte alignment */
+ current_offset += (current_offset % 2);
+ }
+
+ /*
+ * loop #2 for writing the dynamically size objects; pay
+ * attention to 2-byte alignment here....
+ */
+
+ for (i=0; i<ctr->size_of_array; i++) {
+
+ if (!prs_unistr("valuename", ps, depth, &ctr->values[i].valuename))
+ return False;
+
+ if ( ctr->values[i].data_len ) {
+ if ( UNMARSHALLING(ps) ) {
+ ctr->values[i].data = (uint8 *)prs_alloc_mem(
+ ps, ctr->values[i].data_len);
+ if (!ctr->values[i].data)
+ return False;
+ }
+ if (!prs_uint8s(False, "data", ps, depth, ctr->values[i].data, ctr->values[i].data_len))
+ return False;
+ }
+
+ if ( !prs_align_uint16(ps) )
+ return False;
+ }
+
+ return True;
+}
+
+/*******************************************************************
+ * write a structure.
+ ********************************************************************/
+
+BOOL spoolss_io_r_enumprinterdataex(const char *desc, SPOOL_R_ENUMPRINTERDATAEX *r_u, prs_struct *ps, int depth)
+{
+ uint32 data_offset, end_offset;
+ prs_debug(ps, depth, desc, "spoolss_io_r_enumprinterdataex");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if (!prs_uint32("size", ps, depth, &r_u->ctr.size))
+ return False;
+
+ data_offset = prs_offset(ps);
+
+ if (!prs_set_offset(ps, data_offset + r_u->ctr.size))
+ return False;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("needed", ps, depth, &r_u->needed))
+ return False;
+
+ if(!prs_uint32("returned", ps, depth, &r_u->returned))
+ return False;
+
+ if(!prs_werror("status", ps, depth, &r_u->status))
+ return False;
+
+ r_u->ctr.size_of_array = r_u->returned;
+
+ end_offset = prs_offset(ps);
+
+ if (!prs_set_offset(ps, data_offset))
+ return False;
+
+ if (r_u->ctr.size)
+ if (!spoolss_io_printer_enum_values_ctr("", ps, &r_u->ctr, depth ))
+ return False;
+
+ if (!prs_set_offset(ps, end_offset))
+ return False;
+ return True;
+}
+
+/*******************************************************************
+ * write a structure.
+ ********************************************************************/
+
+/*
+ uint32 GetPrintProcessorDirectory(
+ [in] unistr2 *name,
+ [in] unistr2 *environment,
+ [in] uint32 level,
+ [in,out] NEW_BUFFER buffer,
+ [in] uint32 offered,
+ [out] uint32 needed,
+ [out] uint32 returned
+ );
+
+*/
+
+BOOL make_spoolss_q_getprintprocessordirectory(SPOOL_Q_GETPRINTPROCESSORDIRECTORY *q_u, const char *name, char *environment, int level, NEW_BUFFER *buffer, uint32 offered)
+{
+ DEBUG(5,("make_spoolss_q_getprintprocessordirectory\n"));
+
+ init_unistr2(&q_u->name, name, UNI_STR_TERMINATE);
+ init_unistr2(&q_u->environment, environment, UNI_STR_TERMINATE);
+
+ q_u->level = level;
+
+ q_u->buffer = buffer;
+ q_u->offered = offered;
+
+ return True;
+}
+
+BOOL spoolss_io_q_getprintprocessordirectory(const char *desc, SPOOL_Q_GETPRINTPROCESSORDIRECTORY *q_u, prs_struct *ps, int depth)
+{
+ uint32 ptr;
+
+ prs_debug(ps, depth, desc, "spoolss_io_q_getprintprocessordirectory");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if (!prs_uint32("ptr", ps, depth, &ptr))
+ return False;
+
+ if (ptr) {
+ if(!smb_io_unistr2("name", &q_u->name, True, ps, depth))
+ return False;
+ }
+
+ if (!prs_align(ps))
+ return False;
+
+ if (!prs_uint32("ptr", ps, depth, &ptr))
+ return False;
+
+ if (ptr) {
+ if(!smb_io_unistr2("environment", &q_u->environment, True,
+ ps, depth))
+ return False;
+ }
+
+ if (!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("level", ps, depth, &q_u->level))
+ return False;
+
+ if(!spoolss_io_buffer("", ps, depth, &q_u->buffer))
+ return False;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("offered", ps, depth, &q_u->offered))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ * write a structure.
+ ********************************************************************/
+
+BOOL spoolss_io_r_getprintprocessordirectory(const char *desc, SPOOL_R_GETPRINTPROCESSORDIRECTORY *r_u, prs_struct *ps, int depth)
+{
+ prs_debug(ps, depth, desc, "spoolss_io_r_getprintprocessordirectory");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!spoolss_io_buffer("", ps, depth, &r_u->buffer))
+ return False;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("needed", ps, depth, &r_u->needed))
+ return False;
+
+ if(!prs_werror("status", ps, depth, &r_u->status))
+ return False;
+
+ return True;
+}
+
+BOOL smb_io_printprocessordirectory_1(const char *desc, NEW_BUFFER *buffer, PRINTPROCESSOR_DIRECTORY_1 *info, int depth)
+{
+ prs_struct *ps=&buffer->prs;
+
+ prs_debug(ps, depth, desc, "smb_io_printprocessordirectory_1");
+ depth++;
+
+ buffer->struct_start=prs_offset(ps);
+
+ if (!smb_io_unistr(desc, &info->name, ps, depth))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ * init a structure.
+ ********************************************************************/
+
+BOOL make_spoolss_q_addform(SPOOL_Q_ADDFORM *q_u, POLICY_HND *handle,
+ int level, FORM *form)
+{
+ memcpy(&q_u->handle, handle, sizeof(POLICY_HND));
+ q_u->level = level;
+ q_u->level2 = level;
+ memcpy(&q_u->form, form, sizeof(FORM));
+
+ return True;
+}
+
+/*******************************************************************
+ * init a structure.
+ ********************************************************************/
+
+BOOL make_spoolss_q_setform(SPOOL_Q_SETFORM *q_u, POLICY_HND *handle,
+ int level, const char *form_name, FORM *form)
+{
+ memcpy(&q_u->handle, handle, sizeof(POLICY_HND));
+ q_u->level = level;
+ q_u->level2 = level;
+ memcpy(&q_u->form, form, sizeof(FORM));
+ init_unistr2(&q_u->name, form_name, UNI_STR_TERMINATE);
+
+ return True;
+}
+
+/*******************************************************************
+ * init a structure.
+ ********************************************************************/
+
+BOOL make_spoolss_q_deleteform(SPOOL_Q_DELETEFORM *q_u, POLICY_HND *handle,
+ const char *form)
+{
+ memcpy(&q_u->handle, handle, sizeof(POLICY_HND));
+ init_unistr2(&q_u->name, form, UNI_STR_TERMINATE);
+ return True;
+}
+
+/*******************************************************************
+ * init a structure.
+ ********************************************************************/
+
+BOOL make_spoolss_q_getform(SPOOL_Q_GETFORM *q_u, POLICY_HND *handle,
+ const char *formname, uint32 level,
+ NEW_BUFFER *buffer, uint32 offered)
+{
+ memcpy(&q_u->handle, handle, sizeof(POLICY_HND));
+ q_u->level = level;
+ init_unistr2(&q_u->formname, formname, UNI_STR_TERMINATE);
+ q_u->buffer=buffer;
+ q_u->offered=offered;
+
+ return True;
+}
+
+/*******************************************************************
+ * init a structure.
+ ********************************************************************/
+
+BOOL make_spoolss_q_enumforms(SPOOL_Q_ENUMFORMS *q_u, POLICY_HND *handle,
+ uint32 level, NEW_BUFFER *buffer,
+ uint32 offered)
+{
+ memcpy(&q_u->handle, handle, sizeof(POLICY_HND));
+ q_u->level = level;
+ q_u->buffer=buffer;
+ q_u->offered=offered;
+
+ return True;
+}
+
+/*******************************************************************
+ * init a structure.
+ ********************************************************************/
+
+BOOL make_spoolss_q_setjob(SPOOL_Q_SETJOB *q_u, POLICY_HND *handle,
+ uint32 jobid, uint32 level, uint32 command)
+{
+ memcpy(&q_u->handle, handle, sizeof(POLICY_HND));
+ q_u->jobid = jobid;
+ q_u->level = level;
+
+ /* Hmm - the SPOOL_Q_SETJOB structure has a JOB_INFO ctr in it but
+ the server side code has it marked as unused. */
+
+ q_u->command = command;
+
+ return True;
+}
+
+/*******************************************************************
+ * init a structure.
+ ********************************************************************/
+
+BOOL make_spoolss_q_getjob(SPOOL_Q_GETJOB *q_u, POLICY_HND *handle,
+ uint32 jobid, uint32 level, NEW_BUFFER *buffer,
+ uint32 offered)
+{
+ memcpy(&q_u->handle, handle, sizeof(POLICY_HND));
+ q_u->jobid = jobid;
+ q_u->level = level;
+ q_u->buffer = buffer;
+ q_u->offered = offered;
+
+ return True;
+}
+
+/*******************************************************************
+ * init a structure.
+ ********************************************************************/
+
+BOOL make_spoolss_q_startpageprinter(SPOOL_Q_STARTPAGEPRINTER *q_u,
+ POLICY_HND *handle)
+{
+ memcpy(&q_u->handle, handle, sizeof(POLICY_HND));
+
+ return True;
+}
+
+/*******************************************************************
+ * init a structure.
+ ********************************************************************/
+
+BOOL make_spoolss_q_endpageprinter(SPOOL_Q_ENDPAGEPRINTER *q_u,
+ POLICY_HND *handle)
+{
+ memcpy(&q_u->handle, handle, sizeof(POLICY_HND));
+
+ return True;
+}
+
+/*******************************************************************
+ * init a structure.
+ ********************************************************************/
+
+BOOL make_spoolss_q_startdocprinter(SPOOL_Q_STARTDOCPRINTER *q_u,
+ POLICY_HND *handle, uint32 level,
+ char *docname, char *outputfile,
+ char *datatype)
+{
+ DOC_INFO_CONTAINER *ctr = &q_u->doc_info_container;
+
+ memcpy(&q_u->handle, handle, sizeof(POLICY_HND));
+
+ ctr->level = level;
+
+ switch (level) {
+ case 1:
+ ctr->docinfo.switch_value = level;
+
+ ctr->docinfo.doc_info_1.p_docname = docname ? 1 : 0;
+ ctr->docinfo.doc_info_1.p_outputfile = outputfile ? 1 : 0;
+ ctr->docinfo.doc_info_1.p_datatype = datatype ? 1 : 0;
+
+ init_unistr2(&ctr->docinfo.doc_info_1.docname, docname, UNI_STR_TERMINATE);
+ init_unistr2(&ctr->docinfo.doc_info_1.outputfile, outputfile, UNI_STR_TERMINATE);
+ init_unistr2(&ctr->docinfo.doc_info_1.datatype, datatype, UNI_STR_TERMINATE);
+
+ break;
+ case 2:
+ /* DOC_INFO_2 is only used by Windows 9x and since it
+ doesn't do printing over RPC we don't have to worry
+ about it. */
+ default:
+ DEBUG(3, ("unsupported info level %d\n", level));
+ return False;
+ }
+
+ return True;
+}
+
+/*******************************************************************
+ * init a structure.
+ ********************************************************************/
+
+BOOL make_spoolss_q_enddocprinter(SPOOL_Q_ENDDOCPRINTER *q_u,
+ POLICY_HND *handle)
+{
+ memcpy(&q_u->handle, handle, sizeof(POLICY_HND));
+
+ return True;
+}
+
+/*******************************************************************
+ * init a structure.
+ ********************************************************************/
+
+BOOL make_spoolss_q_writeprinter(SPOOL_Q_WRITEPRINTER *q_u,
+ POLICY_HND *handle, uint32 data_size,
+ char *data)
+{
+ memcpy(&q_u->handle, handle, sizeof(POLICY_HND));
+ q_u->buffer_size = q_u->buffer_size2 = data_size;
+ q_u->buffer = (unsigned char *)data;
+ return True;
+}
+
+/*******************************************************************
+ * init a structure.
+ ********************************************************************/
+
+BOOL make_spoolss_q_deleteprinterdata(SPOOL_Q_DELETEPRINTERDATA *q_u,
+ POLICY_HND *handle, char *valuename)
+{
+ memcpy(&q_u->handle, handle, sizeof(POLICY_HND));
+ init_unistr2(&q_u->valuename, valuename, UNI_STR_TERMINATE);
+
+ return True;
+}
+
+/*******************************************************************
+ * init a structure.
+ ********************************************************************/
+
+BOOL make_spoolss_q_deleteprinterdataex(SPOOL_Q_DELETEPRINTERDATAEX *q_u,
+ POLICY_HND *handle, char *key,
+ char *value)
+{
+ memcpy(&q_u->handle, handle, sizeof(POLICY_HND));
+ init_unistr2(&q_u->valuename, value, UNI_STR_TERMINATE);
+ init_unistr2(&q_u->keyname, key, UNI_STR_TERMINATE);
+
+ return True;
+}
+
+/*******************************************************************
+ * init a structure.
+ ********************************************************************/
+
+BOOL make_spoolss_q_rffpcnex(SPOOL_Q_RFFPCNEX *q_u, POLICY_HND *handle,
+ uint32 flags, uint32 options, const char *localmachine,
+ uint32 printerlocal, SPOOL_NOTIFY_OPTION *option)
+{
+ memcpy(&q_u->handle, handle, sizeof(POLICY_HND));
+
+ q_u->flags = flags;
+ q_u->options = options;
+
+ q_u->localmachine_ptr = 1;
+
+ init_unistr2(&q_u->localmachine, localmachine, UNI_STR_TERMINATE);
+
+ q_u->printerlocal = printerlocal;
+
+ if (option)
+ q_u->option_ptr = 1;
+
+ q_u->option = option;
+
+ return True;
+}
diff --git a/source/rpc_parse/parse_srv.c b/source/rpc_parse/parse_srv.c
new file mode 100644
index 00000000000..6349fc16325
--- /dev/null
+++ b/source/rpc_parse/parse_srv.c
@@ -0,0 +1,3575 @@
+/*
+ * Unix SMB/CIFS implementation.
+ * RPC Pipe client / server routines
+ * Copyright (C) Andrew Tridgell 1992-1997,
+ * Copyright (C) Luke Kenneth Casson Leighton 1996-1997,
+ * Copyright (C) Paul Ashton 1997,
+ * Copyright (C) Jeremy Allison 1999,
+ * Copyright (C) Nigel Williams 2001,
+ * Copyright (C) Jim McDonough (jmcd@us.ibm.com) 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 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"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_RPC_PARSE
+
+/*******************************************************************
+ Inits a SH_INFO_0_STR structure
+********************************************************************/
+
+void init_srv_share_info0_str(SH_INFO_0_STR *sh0, const char *net_name)
+{
+ DEBUG(5,("init_srv_share_info0_str\n"));
+
+ init_unistr2(&sh0->uni_netname, net_name, UNI_STR_TERMINATE);
+}
+
+/*******************************************************************
+ Reads or writes a structure.
+********************************************************************/
+
+static BOOL srv_io_share_info0_str(const char *desc, SH_INFO_0_STR *sh0, prs_struct *ps, int depth)
+{
+ if (sh0 == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "srv_io_share_info0_str");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+ if(sh0->ptrs->ptr_netname)
+ if(!smb_io_unistr2("", &sh0->uni_netname, True, ps, depth))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ makes a SH_INFO_0 structure
+********************************************************************/
+
+void init_srv_share_info0(SH_INFO_0 *sh0, const char *net_name)
+{
+ DEBUG(5,("init_srv_share_info0: %s\n", net_name));
+
+ sh0->ptr_netname = (net_name != NULL) ? 1 : 0;
+}
+
+/*******************************************************************
+ Reads or writes a structure.
+********************************************************************/
+
+static BOOL srv_io_share_info0(const char *desc, SH_INFO_0 *sh0, prs_struct *ps, int depth)
+{
+ if (sh0 == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "srv_io_share_info0");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("ptr_netname", ps, depth, &sh0->ptr_netname))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ Inits a SH_INFO_1_STR structure
+********************************************************************/
+
+void init_srv_share_info1_str(SH_INFO_1_STR *sh1, const char *net_name, const char *remark)
+{
+ DEBUG(5,("init_srv_share_info1_str\n"));
+
+ init_unistr2(&sh1->uni_netname, net_name, UNI_STR_TERMINATE);
+ init_unistr2(&sh1->uni_remark, remark, UNI_STR_TERMINATE);
+}
+
+/*******************************************************************
+ Reads or writes a structure.
+********************************************************************/
+
+static BOOL srv_io_share_info1_str(const char *desc, SH_INFO_1_STR *sh1, prs_struct *ps, int depth)
+{
+ if (sh1 == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "srv_io_share_info1_str");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(sh1->ptrs->ptr_netname)
+ if(!smb_io_unistr2("", &sh1->uni_netname, True, ps, depth))
+ return False;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(sh1->ptrs->ptr_remark)
+ if(!smb_io_unistr2("", &sh1->uni_remark, True, ps, depth))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ makes a SH_INFO_1 structure
+********************************************************************/
+
+void init_srv_share_info1(SH_INFO_1 *sh1, const char *net_name, uint32 type, const char *remark)
+{
+ DEBUG(5,("init_srv_share_info1: %s %8x %s\n", net_name, type, remark));
+
+ sh1->ptr_netname = (net_name != NULL) ? 1 : 0;
+ sh1->type = type;
+ sh1->ptr_remark = (remark != NULL) ? 1 : 0;
+}
+
+/*******************************************************************
+ Reads or writes a structure.
+********************************************************************/
+
+static BOOL srv_io_share_info1(const char *desc, SH_INFO_1 *sh1, prs_struct *ps, int depth)
+{
+ if (sh1 == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "srv_io_share_info1");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("ptr_netname", ps, depth, &sh1->ptr_netname))
+ return False;
+ if(!prs_uint32("type ", ps, depth, &sh1->type))
+ return False;
+ if(!prs_uint32("ptr_remark ", ps, depth, &sh1->ptr_remark))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ Inits a SH_INFO_2_STR structure
+********************************************************************/
+
+void init_srv_share_info2_str(SH_INFO_2_STR *sh2,
+ const char *net_name, const char *remark,
+ const char *path, const char *passwd)
+{
+ DEBUG(5,("init_srv_share_info2_str\n"));
+
+ init_unistr2(&sh2->uni_netname, net_name, UNI_STR_TERMINATE);
+ init_unistr2(&sh2->uni_remark, remark, UNI_STR_TERMINATE);
+ init_unistr2(&sh2->uni_path, path, UNI_STR_TERMINATE);
+ init_unistr2(&sh2->uni_passwd, passwd, UNI_STR_TERMINATE);
+}
+
+/*******************************************************************
+ Reads or writes a structure.
+********************************************************************/
+
+static BOOL srv_io_share_info2_str(const char *desc, SH_INFO_2 *sh, SH_INFO_2_STR *sh2, prs_struct *ps, int depth)
+{
+ if (sh2 == NULL)
+ return False;
+
+ if (UNMARSHALLING(ps))
+ ZERO_STRUCTP(sh2);
+
+ prs_debug(ps, depth, desc, "srv_io_share_info2_str");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if (sh->ptr_netname)
+ if(!smb_io_unistr2("", &sh2->uni_netname, True, ps, depth))
+ return False;
+
+ if (sh->ptr_remark)
+ if(!smb_io_unistr2("", &sh2->uni_remark, True, ps, depth))
+ return False;
+
+ if (sh->ptr_netname)
+ if(!smb_io_unistr2("", &sh2->uni_path, True, ps, depth))
+ return False;
+
+ if (sh->ptr_passwd)
+ if(!smb_io_unistr2("", &sh2->uni_passwd, True, ps, depth))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ Inits a SH_INFO_2 structure
+********************************************************************/
+
+void init_srv_share_info2(SH_INFO_2 *sh2,
+ const char *net_name, uint32 type, const char *remark,
+ uint32 perms, uint32 max_uses, uint32 num_uses,
+ const char *path, const char *passwd)
+{
+ DEBUG(5,("init_srv_share_info2: %s %8x %s\n", net_name, type, remark));
+
+ sh2->ptr_netname = (net_name != NULL) ? 1 : 0;
+ sh2->type = type;
+ sh2->ptr_remark = (remark != NULL) ? 1 : 0;
+ sh2->perms = perms;
+ sh2->max_uses = max_uses;
+ sh2->num_uses = num_uses;
+ sh2->ptr_path = (path != NULL) ? 1 : 0;
+ sh2->ptr_passwd = (passwd != NULL) ? 1 : 0;
+}
+
+/*******************************************************************
+ Reads or writes a structure.
+********************************************************************/
+
+static BOOL srv_io_share_info2(const char *desc, SH_INFO_2 *sh2, prs_struct *ps, int depth)
+{
+ if (sh2 == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "srv_io_share_info2");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("ptr_netname", ps, depth, &sh2->ptr_netname))
+ return False;
+ if(!prs_uint32("type ", ps, depth, &sh2->type))
+ return False;
+ if(!prs_uint32("ptr_remark ", ps, depth, &sh2->ptr_remark))
+ return False;
+ if(!prs_uint32("perms ", ps, depth, &sh2->perms))
+ return False;
+ if(!prs_uint32("max_uses ", ps, depth, &sh2->max_uses))
+ return False;
+ if(!prs_uint32("num_uses ", ps, depth, &sh2->num_uses))
+ return False;
+ if(!prs_uint32("ptr_path ", ps, depth, &sh2->ptr_path))
+ return False;
+ if(!prs_uint32("ptr_passwd ", ps, depth, &sh2->ptr_passwd))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ Inits a SH_INFO_501_STR structure
+********************************************************************/
+
+void init_srv_share_info501_str(SH_INFO_501_STR *sh501,
+ const char *net_name, const char *remark)
+{
+ DEBUG(5,("init_srv_share_info501_str\n"));
+
+ init_unistr2(&sh501->uni_netname, net_name, UNI_STR_TERMINATE);
+ init_unistr2(&sh501->uni_remark, remark, UNI_STR_TERMINATE);
+}
+
+/*******************************************************************
+ Inits a SH_INFO_2 structure
+*******************************************************************/
+
+void init_srv_share_info501(SH_INFO_501 *sh501, const char *net_name, uint32 type, const char *remark, uint32 csc_policy)
+{
+ DEBUG(5,("init_srv_share_info501: %s %8x %s %08x\n", net_name, type,
+ remark, csc_policy));
+
+ ZERO_STRUCTP(sh501);
+
+ sh501->ptr_netname = (net_name != NULL) ? 1 : 0;
+ sh501->type = type;
+ sh501->ptr_remark = (remark != NULL) ? 1 : 0;
+ sh501->csc_policy = csc_policy;
+}
+
+/*******************************************************************
+ Reads of writes a structure.
+*******************************************************************/
+
+static BOOL srv_io_share_info501(const char *desc, SH_INFO_501 *sh501, prs_struct *ps, int depth)
+{
+ if (sh501 == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "srv_io_share_info501");
+ depth++;
+
+ if (!prs_align(ps))
+ return False;
+
+ if (!prs_uint32("ptr_netname", ps, depth, &sh501->ptr_netname))
+ return False;
+ if (!prs_uint32("type ", ps, depth, &sh501->type))
+ return False;
+ if (!prs_uint32("ptr_remark ", ps, depth, &sh501->ptr_remark))
+ return False;
+ if (!prs_uint32("csc_policy ", ps, depth, &sh501->csc_policy))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ Reads or writes a structure.
+********************************************************************/
+
+static BOOL srv_io_share_info501_str(const char *desc, SH_INFO_501_STR *sh501, prs_struct *ps, int depth)
+{
+ if (sh501 == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "srv_io_share_info501_str");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+ if(!smb_io_unistr2("", &sh501->uni_netname, True, ps, depth))
+ return False;
+
+ if(!prs_align(ps))
+ return False;
+ if(!smb_io_unistr2("", &sh501->uni_remark, True, ps, depth))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ Inits a SH_INFO_502 structure
+********************************************************************/
+
+void init_srv_share_info502(SH_INFO_502 *sh502,
+ const char *net_name, uint32 type, const char *remark,
+ uint32 perms, uint32 max_uses, uint32 num_uses,
+ const char *path, const char *passwd, SEC_DESC *psd, size_t sd_size)
+{
+ DEBUG(5,("init_srv_share_info502: %s %8x %s\n", net_name, type, remark));
+
+ ZERO_STRUCTP(sh502);
+
+ sh502->ptr_netname = (net_name != NULL) ? 1 : 0;
+ sh502->type = type;
+ sh502->ptr_remark = (remark != NULL) ? 1 : 0;
+ sh502->perms = perms;
+ sh502->max_uses = max_uses;
+ sh502->num_uses = num_uses;
+ sh502->ptr_path = (path != NULL) ? 1 : 0;
+ sh502->ptr_passwd = (passwd != NULL) ? 1 : 0;
+ sh502->reserved = 0; /* actual size within rpc */
+ sh502->sd_size = (uint32)sd_size;
+ sh502->ptr_sd = (psd != NULL) ? 1 : 0;
+}
+
+/*******************************************************************
+ Reads or writes a structure.
+********************************************************************/
+
+static BOOL srv_io_share_info502(const char *desc, SH_INFO_502 *sh502, prs_struct *ps, int depth)
+{
+ if (sh502 == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "srv_io_share_info502");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("ptr_netname", ps, depth, &sh502->ptr_netname))
+ return False;
+ if(!prs_uint32("type ", ps, depth, &sh502->type))
+ return False;
+ if(!prs_uint32("ptr_remark ", ps, depth, &sh502->ptr_remark))
+ return False;
+ if(!prs_uint32("perms ", ps, depth, &sh502->perms))
+ return False;
+ if(!prs_uint32("max_uses ", ps, depth, &sh502->max_uses))
+ return False;
+ if(!prs_uint32("num_uses ", ps, depth, &sh502->num_uses))
+ return False;
+ if(!prs_uint32("ptr_path ", ps, depth, &sh502->ptr_path))
+ return False;
+ if(!prs_uint32("ptr_passwd ", ps, depth, &sh502->ptr_passwd))
+ return False;
+ if(!prs_uint32_pre("reserved ", ps, depth, &sh502->reserved, &sh502->reserved_offset))
+ return False;
+ if(!prs_uint32("ptr_sd ", ps, depth, &sh502->ptr_sd))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ Inits a SH_INFO_502_STR structure
+********************************************************************/
+
+void init_srv_share_info502_str(SH_INFO_502_STR *sh502str,
+ const char *net_name, const char *remark,
+ const char *path, const char *passwd, SEC_DESC *psd, size_t sd_size)
+{
+ DEBUG(5,("init_srv_share_info502_str\n"));
+
+ init_unistr2(&sh502str->uni_netname, net_name, UNI_STR_TERMINATE);
+ init_unistr2(&sh502str->uni_remark, remark, UNI_STR_TERMINATE);
+ init_unistr2(&sh502str->uni_path, path, UNI_STR_TERMINATE);
+ init_unistr2(&sh502str->uni_passwd, passwd, UNI_STR_TERMINATE);
+ sh502str->sd = psd;
+ sh502str->reserved = 0;
+ sh502str->sd_size = sd_size;
+}
+
+/*******************************************************************
+ Reads or writes a structure.
+********************************************************************/
+
+static BOOL srv_io_share_info502_str(const char *desc, SH_INFO_502_STR *sh502, prs_struct *ps, int depth)
+{
+ if (sh502 == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "srv_io_share_info502_str");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(sh502->ptrs->ptr_netname) {
+ if(!smb_io_unistr2("", &sh502->uni_netname, True, ps, depth))
+ return False;
+ }
+
+ if(!prs_align(ps))
+ return False;
+
+ if(sh502->ptrs->ptr_remark) {
+ if(!smb_io_unistr2("", &sh502->uni_remark, True, ps, depth))
+ return False;
+ }
+
+ if(!prs_align(ps))
+ return False;
+
+ if(sh502->ptrs->ptr_path) {
+ if(!smb_io_unistr2("", &sh502->uni_path, True, ps, depth))
+ return False;
+ }
+
+ if(!prs_align(ps))
+ return False;
+
+ if(sh502->ptrs->ptr_passwd) {
+ if(!smb_io_unistr2("", &sh502->uni_passwd, True, ps, depth))
+ return False;
+ }
+
+ if(!prs_align(ps))
+ return False;
+
+ if(sh502->ptrs->ptr_sd) {
+ uint32 old_offset;
+ uint32 reserved_offset;
+
+ if(!prs_uint32_pre("reserved ", ps, depth, &sh502->reserved, &reserved_offset))
+ return False;
+
+ old_offset = prs_offset(ps);
+
+ if (!sec_io_desc(desc, &sh502->sd, ps, depth))
+ return False;
+
+ if(UNMARSHALLING(ps)) {
+
+ sh502->ptrs->sd_size = sh502->sd_size = sec_desc_size(sh502->sd);
+
+ prs_set_offset(ps, old_offset + sh502->reserved);
+ }
+
+ prs_align(ps);
+
+ if(MARSHALLING(ps)) {
+
+ sh502->ptrs->reserved = sh502->reserved = prs_offset(ps) - old_offset;
+ }
+
+ if(!prs_uint32_post("reserved ", ps, depth,
+ &sh502->reserved, reserved_offset, sh502->reserved))
+ return False;
+ if(!prs_uint32_post("reserved ", ps, depth,
+ &sh502->ptrs->reserved, sh502->ptrs->reserved_offset, sh502->ptrs->reserved))
+ return False;
+ }
+
+ return True;
+}
+
+/*******************************************************************
+ Inits a SH_INFO_1004_STR structure
+********************************************************************/
+
+void init_srv_share_info1004_str(SH_INFO_1004_STR *sh1004, const char *remark)
+{
+ DEBUG(5,("init_srv_share_info1004_str\n"));
+
+ init_unistr2(&sh1004->uni_remark, remark, UNI_STR_TERMINATE);
+}
+
+/*******************************************************************
+ Reads or writes a structure.
+********************************************************************/
+
+static BOOL srv_io_share_info1004_str(const char *desc, SH_INFO_1004_STR *sh1004, prs_struct *ps, int depth)
+{
+ if (sh1004 == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "srv_io_share_info1004_str");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+ if(sh1004->ptrs->ptr_remark)
+ if(!smb_io_unistr2("", &sh1004->uni_remark, True, ps, depth))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ makes a SH_INFO_1004 structure
+********************************************************************/
+
+void init_srv_share_info1004(SH_INFO_1004 *sh1004, const char *remark)
+{
+ DEBUG(5,("init_srv_share_info1004: %s\n", remark));
+
+ sh1004->ptr_remark = (remark != NULL) ? 1 : 0;
+}
+
+/*******************************************************************
+ Reads or writes a structure.
+********************************************************************/
+
+static BOOL srv_io_share_info1004(const char *desc, SH_INFO_1004 *sh1004, prs_struct *ps, int depth)
+{
+ if (sh1004 == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "srv_io_share_info1004");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("ptr_remark", ps, depth, &sh1004->ptr_remark))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ Reads or writes a structure.
+********************************************************************/
+
+static BOOL srv_io_share_info1005(const char* desc, SRV_SHARE_INFO_1005* sh1005, prs_struct* ps, int depth)
+{
+ if(sh1005 == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "srv_io_share_info1005");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("share_info_flags", ps, depth,
+ &sh1005->share_info_flags))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ Reads or writes a structure.
+********************************************************************/
+
+static BOOL srv_io_share_info1006(const char* desc, SRV_SHARE_INFO_1006* sh1006, prs_struct* ps, int depth)
+{
+ if(sh1006 == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "srv_io_share_info1006");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("max uses ", ps, depth, &sh1006->max_uses))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ Inits a SH_INFO_1007_STR structure
+********************************************************************/
+
+void init_srv_share_info1007_str(SH_INFO_1007_STR *sh1007, const char *alternate_directory_name)
+{
+ DEBUG(5,("init_srv_share_info1007_str\n"));
+
+ init_unistr2(&sh1007->uni_AlternateDirectoryName, alternate_directory_name, UNI_STR_TERMINATE);
+}
+
+/*******************************************************************
+ Reads or writes a structure.
+********************************************************************/
+
+static BOOL srv_io_share_info1007_str(const char *desc, SH_INFO_1007_STR *sh1007, prs_struct *ps, int depth)
+{
+ if (sh1007 == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "srv_io_share_info1007_str");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+ if(sh1007->ptrs->ptr_AlternateDirectoryName)
+ if(!smb_io_unistr2("", &sh1007->uni_AlternateDirectoryName, True, ps, depth))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ makes a SH_INFO_1007 structure
+********************************************************************/
+
+void init_srv_share_info1007(SH_INFO_1007 *sh1007, uint32 flags, const char *alternate_directory_name)
+{
+ DEBUG(5,("init_srv_share_info1007: %s\n", alternate_directory_name));
+
+ sh1007->flags = flags;
+ sh1007->ptr_AlternateDirectoryName = (alternate_directory_name != NULL) ? 1 : 0;
+}
+
+/*******************************************************************
+ Reads or writes a structure.
+********************************************************************/
+
+static BOOL srv_io_share_info1007(const char *desc, SH_INFO_1007 *sh1007, prs_struct *ps, int depth)
+{
+ if (sh1007 == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "srv_io_share_info1007");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("flags ", ps, depth, &sh1007->flags))
+ return False;
+ if(!prs_uint32("ptr_Alter..", ps, depth, &sh1007->ptr_AlternateDirectoryName))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ Reads or writes a structure.
+********************************************************************/
+
+static BOOL srv_io_share_info1501(const char* desc, SRV_SHARE_INFO_1501* sh1501,
+ prs_struct* ps, int depth)
+{
+ if(sh1501 == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "srv_io_share_info1501");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if (!sec_io_desc_buf(desc, &sh1501->sdb, ps, depth))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ Reads or writes a structure.
+********************************************************************/
+
+static BOOL srv_io_srv_share_ctr(const char *desc, SRV_SHARE_INFO_CTR *ctr, prs_struct *ps, int depth)
+{
+ if (ctr == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "srv_io_srv_share_ctr");
+ depth++;
+
+ if (UNMARSHALLING(ps)) {
+ memset(ctr, '\0', sizeof(SRV_SHARE_INFO_CTR));
+ }
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("info_level", ps, depth, &ctr->info_level))
+ return False;
+
+ if(!prs_uint32("switch_value", ps, depth, &ctr->switch_value))
+ return False;
+ if(!prs_uint32("ptr_share_info", ps, depth, &ctr->ptr_share_info))
+ return False;
+
+ if (ctr->ptr_share_info == 0)
+ return True;
+
+ if(!prs_uint32("num_entries", ps, depth, &ctr->num_entries))
+ return False;
+ if(!prs_uint32("ptr_entries", ps, depth, &ctr->ptr_entries))
+ return False;
+
+ if (ctr->ptr_entries == 0) {
+ if (ctr->num_entries == 0)
+ return True;
+ else
+ return False;
+ }
+
+ if(!prs_uint32("num_entries2", ps, depth, &ctr->num_entries2))
+ return False;
+
+ if (ctr->num_entries2 != ctr->num_entries)
+ return False;
+
+ switch (ctr->switch_value) {
+
+ case 0:
+ {
+ SRV_SHARE_INFO_0 *info0 = ctr->share.info0;
+ int num_entries = ctr->num_entries;
+ int i;
+
+ if (UNMARSHALLING(ps)) {
+ if (!(info0 = (SRV_SHARE_INFO_0 *)prs_alloc_mem(ps, num_entries * sizeof(SRV_SHARE_INFO_0))))
+ return False;
+ ctr->share.info0 = info0;
+ }
+
+ for (i = 0; i < num_entries; i++) {
+ if(!srv_io_share_info0("", &info0[i].info_0, ps, depth))
+ return False;
+ }
+
+ for (i = 0; i < num_entries; i++) {
+ info0[i].info_0_str.ptrs = &info0[i].info_0;
+ if(!srv_io_share_info0_str("", &info0[i].info_0_str, ps, depth))
+ return False;
+ }
+
+ break;
+ }
+
+ case 1:
+ {
+ SRV_SHARE_INFO_1 *info1 = ctr->share.info1;
+ int num_entries = ctr->num_entries;
+ int i;
+
+ if (UNMARSHALLING(ps)) {
+ if (!(info1 = (SRV_SHARE_INFO_1 *)prs_alloc_mem(ps, num_entries * sizeof(SRV_SHARE_INFO_1))))
+ return False;
+ ctr->share.info1 = info1;
+ }
+
+ for (i = 0; i < num_entries; i++) {
+ if(!srv_io_share_info1("", &info1[i].info_1, ps, depth))
+ return False;
+ }
+
+ for (i = 0; i < num_entries; i++) {
+ info1[i].info_1_str.ptrs = &info1[i].info_1;
+ if(!srv_io_share_info1_str("", &info1[i].info_1_str, ps, depth))
+ return False;
+ }
+
+ break;
+ }
+
+ case 2:
+ {
+ SRV_SHARE_INFO_2 *info2 = ctr->share.info2;
+ int num_entries = ctr->num_entries;
+ int i;
+
+ if (UNMARSHALLING(ps)) {
+ if (!(info2 = (SRV_SHARE_INFO_2 *)prs_alloc_mem(ps,num_entries * sizeof(SRV_SHARE_INFO_2))))
+ return False;
+ ctr->share.info2 = info2;
+ }
+
+ for (i = 0; i < num_entries; i++) {
+ if(!srv_io_share_info2("", &info2[i].info_2, ps, depth))
+ return False;
+ }
+
+ for (i = 0; i < num_entries; i++) {
+ if(!srv_io_share_info2_str("", &info2[i].info_2, &info2[i].info_2_str, ps, depth))
+ return False;
+ }
+
+ break;
+ }
+
+ case 501:
+ {
+ SRV_SHARE_INFO_501 *info501 = ctr->share.info501;
+ int num_entries = ctr->num_entries;
+ int i;
+
+ if (UNMARSHALLING(ps)) {
+ if (!(info501 = (SRV_SHARE_INFO_501 *) prs_alloc_mem(ps, num_entries *
+ sizeof (SRV_SHARE_INFO_501))))
+ return False;
+ ctr->share.info501 = info501;
+ }
+
+ for (i = 0; i < num_entries; i++) {
+ if (!srv_io_share_info501("", &info501[i].info_501, ps, depth))
+ return False;
+ }
+
+ for (i = 0; i < num_entries; i++) {
+ if (!srv_io_share_info501_str("", &info501[i].info_501_str, ps, depth))
+ return False;
+ }
+
+ break;
+ }
+
+ case 502:
+ {
+ SRV_SHARE_INFO_502 *info502 = ctr->share.info502;
+ int num_entries = ctr->num_entries;
+ int i;
+
+ if (UNMARSHALLING(ps)) {
+ if (!(info502 = (SRV_SHARE_INFO_502 *)prs_alloc_mem(ps,num_entries * sizeof(SRV_SHARE_INFO_502))))
+ return False;
+ ctr->share.info502 = info502;
+ }
+
+ for (i = 0; i < num_entries; i++) {
+ if(!srv_io_share_info502("", &info502[i].info_502, ps, depth))
+ return False;
+ }
+
+ for (i = 0; i < num_entries; i++) {
+ info502[i].info_502_str.ptrs = &info502[i].info_502;
+ if(!srv_io_share_info502_str("", &info502[i].info_502_str, ps, depth))
+ return False;
+ }
+
+ break;
+ }
+
+ case 1004:
+ {
+ SRV_SHARE_INFO_1004 *info1004 = ctr->share.info1004;
+ int num_entries = ctr->num_entries;
+ int i;
+
+ if (UNMARSHALLING(ps)) {
+ if (!(info1004 = (SRV_SHARE_INFO_1004 *)prs_alloc_mem(ps,num_entries * sizeof(SRV_SHARE_INFO_1004))))
+ return False;
+ ctr->share.info1004 = info1004;
+ }
+
+ for (i = 0; i < num_entries; i++) {
+ if(!srv_io_share_info1004("", &info1004[i].info_1004, ps, depth))
+ return False;
+ }
+
+ for (i = 0; i < num_entries; i++) {
+ info1004[i].info_1004_str.ptrs = &info1004[i].info_1004;
+ if(!srv_io_share_info1004_str("", &info1004[i].info_1004_str, ps, depth))
+ return False;
+ }
+
+ break;
+ }
+
+ case 1005:
+ {
+ SRV_SHARE_INFO_1005 *info1005 = ctr->share.info1005;
+ int num_entries = ctr->num_entries;
+ int i;
+
+ if (UNMARSHALLING(ps)) {
+ if (!(info1005 = (SRV_SHARE_INFO_1005 *)prs_alloc_mem(ps,num_entries * sizeof(SRV_SHARE_INFO_1005))))
+ return False;
+ ctr->share.info1005 = info1005;
+ }
+
+ for (i = 0; i < num_entries; i++) {
+ if(!srv_io_share_info1005("", &info1005[i], ps, depth))
+ return False;
+ }
+
+ break;
+ }
+
+ case 1006:
+ {
+ SRV_SHARE_INFO_1006 *info1006 = ctr->share.info1006;
+ int num_entries = ctr->num_entries;
+ int i;
+
+ if (UNMARSHALLING(ps)) {
+ if (!(info1006 = (SRV_SHARE_INFO_1006 *)prs_alloc_mem(ps,num_entries * sizeof(SRV_SHARE_INFO_1006))))
+ return False;
+ ctr->share.info1006 = info1006;
+ }
+
+ for (i = 0; i < num_entries; i++) {
+ if(!srv_io_share_info1006("", &info1006[i], ps, depth))
+ return False;
+ }
+
+ break;
+ }
+
+ case 1007:
+ {
+ SRV_SHARE_INFO_1007 *info1007 = ctr->share.info1007;
+ int num_entries = ctr->num_entries;
+ int i;
+
+ if (UNMARSHALLING(ps)) {
+ if (!(info1007 = (SRV_SHARE_INFO_1007 *)prs_alloc_mem(ps,num_entries * sizeof(SRV_SHARE_INFO_1007))))
+ return False;
+ ctr->share.info1007 = info1007;
+ }
+
+ for (i = 0; i < num_entries; i++) {
+ if(!srv_io_share_info1007("", &info1007[i].info_1007, ps, depth))
+ return False;
+ }
+
+ for (i = 0; i < num_entries; i++) {
+ info1007[i].info_1007_str.ptrs = &info1007[i].info_1007;
+ if(!srv_io_share_info1007_str("", &info1007[i].info_1007_str, ps, depth))
+ return False;
+ }
+
+ break;
+ }
+
+ case 1501:
+ {
+ SRV_SHARE_INFO_1501 *info1501 = ctr->share.info1501;
+ int num_entries = ctr->num_entries;
+ int i;
+
+ if (UNMARSHALLING(ps)) {
+ if (!(info1501 = (SRV_SHARE_INFO_1501 *)prs_alloc_mem(ps,num_entries * sizeof(SRV_SHARE_INFO_1501))))
+ return False;
+ ctr->share.info1501 = info1501;
+ }
+
+ for (i = 0; i < num_entries; i++) {
+ if(!srv_io_share_info1501("", &info1501[i], ps, depth))
+ return False;
+ }
+
+ break;
+ }
+
+ default:
+ DEBUG(5,("%s no share info at switch_value %d\n",
+ tab_depth(depth), ctr->switch_value));
+ break;
+ }
+
+ return True;
+}
+
+/*******************************************************************
+ Inits a SRV_Q_NET_SHARE_ENUM structure.
+********************************************************************/
+
+void init_srv_q_net_share_enum(SRV_Q_NET_SHARE_ENUM *q_n,
+ const char *srv_name, uint32 info_level,
+ uint32 preferred_len, ENUM_HND *hnd)
+{
+
+ DEBUG(5,("init_q_net_share_enum\n"));
+
+ init_buf_unistr2(&q_n->uni_srv_name, &q_n->ptr_srv_name, srv_name);
+
+ q_n->ctr.info_level = q_n->ctr.switch_value = info_level;
+ q_n->ctr.ptr_share_info = 1;
+ q_n->ctr.num_entries = 0;
+ q_n->ctr.ptr_entries = 0;
+ q_n->ctr.num_entries2 = 0;
+ q_n->preferred_len = preferred_len;
+
+ memcpy(&q_n->enum_hnd, hnd, sizeof(*hnd));
+}
+
+/*******************************************************************
+ Reads or writes a structure.
+********************************************************************/
+
+BOOL srv_io_q_net_share_enum(const char *desc, SRV_Q_NET_SHARE_ENUM *q_n, prs_struct *ps, int depth)
+{
+ if (q_n == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "srv_io_q_net_share_enum");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("ptr_srv_name", ps, depth, &q_n->ptr_srv_name))
+ return False;
+ if(!smb_io_unistr2("", &q_n->uni_srv_name, True, ps, depth))
+ return False;
+
+ if(!srv_io_srv_share_ctr("share_ctr", &q_n->ctr, ps, depth))
+ return False;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("preferred_len", ps, depth, &q_n->preferred_len))
+ return False;
+
+ if(!smb_io_enum_hnd("enum_hnd", &q_n->enum_hnd, ps, depth))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ Reads or writes a structure.
+********************************************************************/
+
+BOOL srv_io_r_net_share_enum(const char *desc, SRV_R_NET_SHARE_ENUM *r_n, prs_struct *ps, int depth)
+{
+ if (r_n == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "srv_io_r_net_share_enum");
+ depth++;
+
+ if(!srv_io_srv_share_ctr("share_ctr", &r_n->ctr, ps, depth))
+ return False;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("total_entries", ps, depth, &r_n->total_entries))
+ return False;
+
+ if(!smb_io_enum_hnd("enum_hnd", &r_n->enum_hnd, ps, depth))
+ return False;
+
+ if(!prs_werror("status", ps, depth, &r_n->status))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ initialises a structure.
+********************************************************************/
+
+BOOL init_srv_q_net_share_get_info(SRV_Q_NET_SHARE_GET_INFO *q_n, const char *srv_name, const char *share_name, uint32 info_level)
+{
+
+ uint32 ptr_share_name;
+
+ DEBUG(5,("init_srv_q_net_share_get_info\n"));
+
+ init_buf_unistr2(&q_n->uni_srv_name, &q_n->ptr_srv_name, srv_name);
+ init_buf_unistr2(&q_n->uni_share_name, &ptr_share_name, share_name);
+
+ q_n->info_level = info_level;
+
+ return True;
+}
+
+/*******************************************************************
+ Reads or writes a structure.
+********************************************************************/
+
+BOOL srv_io_q_net_share_get_info(const char *desc, SRV_Q_NET_SHARE_GET_INFO *q_n, prs_struct *ps, int depth)
+{
+ if (q_n == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "srv_io_q_net_share_get_info");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("ptr_srv_name", ps, depth, &q_n->ptr_srv_name))
+ return False;
+ if(!smb_io_unistr2("", &q_n->uni_srv_name, True, ps, depth))
+ return False;
+
+ if(!smb_io_unistr2("", &q_n->uni_share_name, True, ps, depth))
+ return False;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("info_level", ps, depth, &q_n->info_level))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ Reads or writes a structure.
+********************************************************************/
+
+static BOOL srv_io_srv_share_info(const char *desc, prs_struct *ps, int depth, SRV_SHARE_INFO *r_n)
+{
+ if (r_n == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "srv_io_srv_share_info");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("switch_value ", ps, depth, &r_n->switch_value ))
+ return False;
+
+ if(!prs_uint32("ptr_share_ctr", ps, depth, &r_n->ptr_share_ctr))
+ return False;
+
+ if (r_n->ptr_share_ctr != 0) {
+ switch (r_n->switch_value) {
+ case 0:
+ if(!srv_io_share_info0("", &r_n->share.info0.info_0, ps, depth))
+ return False;
+
+ /* allow access to pointers in the str part. */
+ r_n->share.info0.info_0_str.ptrs = &r_n->share.info0.info_0;
+
+ if(!srv_io_share_info0_str("", &r_n->share.info0.info_0_str, ps, depth))
+ return False;
+
+ break;
+ case 1:
+ if(!srv_io_share_info1("", &r_n->share.info1.info_1, ps, depth))
+ return False;
+
+ /* allow access to pointers in the str part. */
+ r_n->share.info1.info_1_str.ptrs = &r_n->share.info1.info_1;
+
+ if(!srv_io_share_info1_str("", &r_n->share.info1.info_1_str, ps, depth))
+ return False;
+
+ break;
+ case 2:
+ if(!srv_io_share_info2("", &r_n->share.info2.info_2, ps, depth))
+ return False;
+
+ if(!srv_io_share_info2_str("", &r_n->share.info2.info_2, &r_n->share.info2.info_2_str, ps, depth))
+ return False;
+
+ break;
+ case 501:
+ if (!srv_io_share_info501("", &r_n->share.info501.info_501, ps, depth))
+ return False;
+ if (!srv_io_share_info501_str("", &r_n->share.info501.info_501_str, ps, depth))
+ return False;
+ break;
+
+ case 502:
+ if(!srv_io_share_info502("", &r_n->share.info502.info_502, ps, depth))
+ return False;
+
+ /* allow access to pointers in the str part. */
+ r_n->share.info502.info_502_str.ptrs = &r_n->share.info502.info_502;
+
+ if(!srv_io_share_info502_str("", &r_n->share.info502.info_502_str, ps, depth))
+ return False;
+ break;
+ case 1004:
+ if(!srv_io_share_info1004("", &r_n->share.info1004.info_1004, ps, depth))
+ return False;
+
+ /* allow access to pointers in the str part. */
+ r_n->share.info1004.info_1004_str.ptrs = &r_n->share.info1004.info_1004;
+
+ if(!srv_io_share_info1004_str("", &r_n->share.info1004.info_1004_str, ps, depth))
+ return False;
+ break;
+ case 1005:
+ if(!srv_io_share_info1005("", &r_n->share.info1005, ps, depth))
+ return False;
+ break;
+ case 1006:
+ if(!srv_io_share_info1006("", &r_n->share.info1006, ps, depth))
+ return False;
+ break;
+ case 1007:
+ if(!srv_io_share_info1007("", &r_n->share.info1007.info_1007, ps, depth))
+ return False;
+
+ /* allow access to pointers in the str part. */
+ r_n->share.info1007.info_1007_str.ptrs = &r_n->share.info1007.info_1007;
+
+ if(!srv_io_share_info1007_str("", &r_n->share.info1007.info_1007_str, ps, depth))
+ return False;
+ break;
+ case 1501:
+ if (!srv_io_share_info1501("", &r_n->share.info1501, ps, depth))
+ return False;
+ default:
+ DEBUG(5,("%s no share info at switch_value %d\n",
+ tab_depth(depth), r_n->switch_value));
+ break;
+ }
+ }
+
+ return True;
+}
+
+/*******************************************************************
+ Reads or writes a structure.
+********************************************************************/
+
+BOOL srv_io_r_net_share_get_info(const char *desc, SRV_R_NET_SHARE_GET_INFO *r_n, prs_struct *ps, int depth)
+{
+ if (r_n == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "srv_io_r_net_share_get_info");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!srv_io_srv_share_info("info ", ps, depth, &r_n->info))
+ return False;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_werror("status", ps, depth, &r_n->status))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ intialises a structure.
+********************************************************************/
+
+BOOL init_srv_q_net_share_set_info(SRV_Q_NET_SHARE_SET_INFO *q_n,
+ const char *srv_name,
+ const char *share_name,
+ uint32 info_level,
+ const SRV_SHARE_INFO *info)
+{
+
+ uint32 ptr_share_name;
+
+ DEBUG(5,("init_srv_q_net_share_set_info\n"));
+
+ init_buf_unistr2(&q_n->uni_srv_name, &q_n->ptr_srv_name, srv_name);
+ init_buf_unistr2(&q_n->uni_share_name, &ptr_share_name, share_name);
+
+ q_n->info_level = info_level;
+
+ q_n->info = *info;
+
+ q_n->ptr_parm_error = 1;
+ q_n->parm_error = 0;
+
+ return True;
+}
+
+/*******************************************************************
+ Reads or writes a structure.
+********************************************************************/
+
+BOOL srv_io_q_net_share_set_info(const char *desc, SRV_Q_NET_SHARE_SET_INFO *q_n, prs_struct *ps, int depth)
+{
+ if (q_n == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "srv_io_q_net_share_set_info");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("ptr_srv_name", ps, depth, &q_n->ptr_srv_name))
+ return False;
+ if(!smb_io_unistr2("", &q_n->uni_srv_name, True, ps, depth))
+ return False;
+
+ if(!smb_io_unistr2("", &q_n->uni_share_name, True, ps, depth))
+ return False;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("info_level", ps, depth, &q_n->info_level))
+ return False;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!srv_io_srv_share_info("info ", ps, depth, &q_n->info))
+ return False;
+
+ if(!prs_align(ps))
+ return False;
+ if(!prs_uint32("ptr_parm_error", ps, depth, &q_n->ptr_parm_error))
+ return False;
+ if(q_n->ptr_parm_error!=0) {
+ if(!prs_uint32("parm_error", ps, depth, &q_n->parm_error))
+ return False;
+ }
+
+ return True;
+}
+
+/*******************************************************************
+ Reads or writes a structure.
+********************************************************************/
+
+BOOL srv_io_r_net_share_set_info(const char *desc, SRV_R_NET_SHARE_SET_INFO *r_n, prs_struct *ps, int depth)
+{
+ if (r_n == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "srv_io_r_net_share_set_info");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("ptr_parm_error ", ps, depth, &r_n->ptr_parm_error))
+ return False;
+
+ if(r_n->ptr_parm_error) {
+
+ if(!prs_uint32("parm_error ", ps, depth, &r_n->parm_error))
+ return False;
+ }
+
+ if(!prs_werror("status", ps, depth, &r_n->status))
+ return False;
+
+ return True;
+}
+
+
+/*******************************************************************
+ Reads or writes a structure.
+********************************************************************/
+
+BOOL srv_io_q_net_share_add(const char *desc, SRV_Q_NET_SHARE_ADD *q_n, prs_struct *ps, int depth)
+{
+ if (q_n == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "srv_io_q_net_share_add");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("ptr_srv_name", ps, depth, &q_n->ptr_srv_name))
+ return False;
+ if(!smb_io_unistr2("", &q_n->uni_srv_name, True, ps, depth))
+ return False;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("info_level", ps, depth, &q_n->info_level))
+ return False;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!srv_io_srv_share_info("info ", ps, depth, &q_n->info))
+ return False;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("ptr_err_index", ps, depth, &q_n->ptr_err_index))
+ return False;
+ if (q_n->ptr_err_index)
+ if (!prs_uint32("err_index", ps, depth, &q_n->err_index))
+ return False;
+
+ return True;
+}
+
+void init_srv_q_net_share_add(SRV_Q_NET_SHARE_ADD *q, const char *srvname,
+ const char *netname, uint32 type, const char *remark,
+ uint32 perms, uint32 max_uses, uint32 num_uses,
+ const char *path, const char *passwd)
+{
+ q->ptr_srv_name = 1;
+ init_unistr2(&q->uni_srv_name, srvname, UNI_STR_TERMINATE);
+ q->info.switch_value = q->info_level = 2;
+
+ q->info.ptr_share_ctr = 1;
+ init_srv_share_info2(&q->info.share.info2.info_2, netname, type,
+ remark, perms, max_uses, num_uses, path, passwd);
+ init_srv_share_info2_str(&q->info.share.info2.info_2_str, netname,
+ remark, path, passwd);
+ q->ptr_err_index = 1;
+ q->err_index = 0;
+}
+
+
+/*******************************************************************
+ Reads or writes a structure.
+********************************************************************/
+
+BOOL srv_io_r_net_share_add(const char *desc, SRV_R_NET_SHARE_ADD *r_n, prs_struct *ps, int depth)
+{
+ if (r_n == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "srv_io_r_net_share_add");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("ptr_parm_error", ps, depth, &r_n->ptr_parm_error))
+ return False;
+
+ if(r_n->ptr_parm_error) {
+
+ if(!prs_uint32("parm_error", ps, depth, &r_n->parm_error))
+ return False;
+ }
+
+ if(!prs_werror("status", ps, depth, &r_n->status))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ initialises a structure.
+********************************************************************/
+
+void init_srv_q_net_share_del(SRV_Q_NET_SHARE_DEL *del, const char *srvname,
+ const char *sharename)
+{
+ del->ptr_srv_name = 1;
+ init_unistr2(&del->uni_srv_name, srvname, UNI_STR_TERMINATE);
+ init_unistr2(&del->uni_share_name, sharename, UNI_STR_TERMINATE);
+}
+
+/*******************************************************************
+ Reads or writes a structure.
+********************************************************************/
+
+BOOL srv_io_q_net_share_del(const char *desc, SRV_Q_NET_SHARE_DEL *q_n, prs_struct *ps, int depth)
+{
+ if (q_n == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "srv_io_q_net_share_del");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("ptr_srv_name", ps, depth, &q_n->ptr_srv_name))
+ return False;
+ if(!smb_io_unistr2("", &q_n->uni_srv_name, True, ps, depth))
+ return False;
+
+ if(!smb_io_unistr2("", &q_n->uni_share_name, True, ps, depth))
+ return False;
+
+ if(!prs_align(ps))
+ return False;
+ if(!prs_uint32("reserved", ps, depth, &q_n->reserved))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ Reads or writes a structure.
+********************************************************************/
+
+BOOL srv_io_r_net_share_del(const char *desc, SRV_R_NET_SHARE_DEL *q_n, prs_struct *ps, int depth)
+{
+ if (q_n == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "srv_io_r_net_share_del");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_werror("status", ps, depth, &q_n->status))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ Inits a SESS_INFO_0_STR structure
+********************************************************************/
+
+void init_srv_sess_info0_str(SESS_INFO_0_STR *ss0, const char *name)
+{
+ DEBUG(5,("init_srv_sess_info0_str\n"));
+
+ init_unistr2(&ss0->uni_name, name, UNI_STR_TERMINATE);
+}
+
+/*******************************************************************
+ Reads or writes a structure.
+********************************************************************/
+
+static BOOL srv_io_sess_info0_str(const char *desc, SESS_INFO_0_STR *ss0, prs_struct *ps, int depth)
+{
+ if (ss0 == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "srv_io_sess_info0_str");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!smb_io_unistr2("", &ss0->uni_name, True, ps, depth))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ Inits a SESS_INFO_0 structure
+********************************************************************/
+
+void init_srv_sess_info0(SESS_INFO_0 *ss0, const char *name)
+{
+ DEBUG(5,("init_srv_sess_info0: %s\n", name));
+
+ ss0->ptr_name = (name != NULL) ? 1 : 0;
+}
+
+/*******************************************************************
+ Reads or writes a structure.
+********************************************************************/
+
+static BOOL srv_io_sess_info0(const char *desc, SESS_INFO_0 *ss0, prs_struct *ps, int depth)
+{
+ if (ss0 == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "srv_io_sess_info0");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("ptr_name", ps, depth, &ss0->ptr_name))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ Reads or writes a structure.
+********************************************************************/
+
+static BOOL srv_io_srv_sess_info_0(const char *desc, SRV_SESS_INFO_0 *ss0, prs_struct *ps, int depth)
+{
+ if (ss0 == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "srv_io_srv_sess_info_0");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("num_entries_read", ps, depth, &ss0->num_entries_read))
+ return False;
+ if(!prs_uint32("ptr_sess_info", ps, depth, &ss0->ptr_sess_info))
+ return False;
+
+ if (ss0->ptr_sess_info != 0) {
+ uint32 i;
+ uint32 num_entries = ss0->num_entries_read;
+
+ if (num_entries > MAX_SESS_ENTRIES) {
+ num_entries = MAX_SESS_ENTRIES; /* report this! */
+ }
+
+ if(!prs_uint32("num_entries_read2", ps, depth, &ss0->num_entries_read2))
+ return False;
+
+ SMB_ASSERT_ARRAY(ss0->info_0, num_entries);
+
+ for (i = 0; i < num_entries; i++) {
+ if(!srv_io_sess_info0("", &ss0->info_0[i], ps, depth))
+ return False;
+ }
+
+ for (i = 0; i < num_entries; i++) {
+ if(!srv_io_sess_info0_str("", &ss0->info_0_str[i], ps, depth))
+ return False;
+ }
+
+ if(!prs_align(ps))
+ return False;
+ }
+
+ return True;
+}
+
+/*******************************************************************
+ Inits a SESS_INFO_1_STR structure
+********************************************************************/
+
+void init_srv_sess_info1_str(SESS_INFO_1_STR *ss1, const char *name, const char *user)
+{
+ DEBUG(5,("init_srv_sess_info1_str\n"));
+
+ init_unistr2(&ss1->uni_name, name, UNI_STR_TERMINATE);
+ init_unistr2(&ss1->uni_user, user, UNI_STR_TERMINATE);
+}
+
+/*******************************************************************
+ Reads or writes a structure.
+********************************************************************/
+
+static BOOL srv_io_sess_info1_str(const char *desc, SESS_INFO_1_STR *ss1, prs_struct *ps, int depth)
+{
+ if (ss1 == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "srv_io_sess_info1_str");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!smb_io_unistr2("", &ss1->uni_name, True, ps, depth))
+ return False;
+ if(!smb_io_unistr2("", &(ss1->uni_user), True, ps, depth))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ Inits a SESS_INFO_1 structure
+********************************************************************/
+
+void init_srv_sess_info1(SESS_INFO_1 *ss1,
+ const char *name, const char *user,
+ uint32 num_opens, uint32 open_time, uint32 idle_time,
+ uint32 user_flags)
+{
+ DEBUG(5,("init_srv_sess_info1: %s\n", name));
+
+ ss1->ptr_name = (name != NULL) ? 1 : 0;
+ ss1->ptr_user = (user != NULL) ? 1 : 0;
+
+ ss1->num_opens = num_opens;
+ ss1->open_time = open_time;
+ ss1->idle_time = idle_time;
+ ss1->user_flags = user_flags;
+}
+
+/*******************************************************************
+reads or writes a structure.
+********************************************************************/
+
+static BOOL srv_io_sess_info1(const char *desc, SESS_INFO_1 *ss1, prs_struct *ps, int depth)
+{
+ if (ss1 == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "srv_io_sess_info1");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("ptr_name ", ps, depth, &ss1->ptr_name))
+ return False;
+ if(!prs_uint32("ptr_user ", ps, depth, &ss1->ptr_user))
+ return False;
+
+ if(!prs_uint32("num_opens ", ps, depth, &ss1->num_opens))
+ return False;
+ if(!prs_uint32("open_time ", ps, depth, &ss1->open_time))
+ return False;
+ if(!prs_uint32("idle_time ", ps, depth, &ss1->idle_time))
+ return False;
+ if(!prs_uint32("user_flags", ps, depth, &ss1->user_flags))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ Reads or writes a structure.
+********************************************************************/
+
+static BOOL srv_io_srv_sess_info_1(const char *desc, SRV_SESS_INFO_1 *ss1, prs_struct *ps, int depth)
+{
+ if (ss1 == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "srv_io_srv_sess_info_1");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("num_entries_read", ps, depth, &ss1->num_entries_read))
+ return False;
+ if(!prs_uint32("ptr_sess_info", ps, depth, &ss1->ptr_sess_info))
+ return False;
+
+ if (ss1->ptr_sess_info != 0) {
+ uint32 i;
+ uint32 num_entries = ss1->num_entries_read;
+
+ if (num_entries > MAX_SESS_ENTRIES) {
+ num_entries = MAX_SESS_ENTRIES; /* report this! */
+ }
+
+ if(!prs_uint32("num_entries_read2", ps, depth, &ss1->num_entries_read2))
+ return False;
+
+ SMB_ASSERT_ARRAY(ss1->info_1, num_entries);
+
+ for (i = 0; i < num_entries; i++) {
+ if(!srv_io_sess_info1("", &ss1->info_1[i], ps, depth))
+ return False;
+ }
+
+ for (i = 0; i < num_entries; i++) {
+ if(!srv_io_sess_info1_str("", &ss1->info_1_str[i], ps, depth))
+ return False;
+ }
+
+ if(!prs_align(ps))
+ return False;
+ }
+
+ return True;
+}
+
+/*******************************************************************
+ Reads or writes a structure.
+********************************************************************/
+
+static BOOL srv_io_srv_sess_ctr(const char *desc, SRV_SESS_INFO_CTR **pp_ctr, prs_struct *ps, int depth)
+{
+ SRV_SESS_INFO_CTR *ctr = *pp_ctr;
+
+ prs_debug(ps, depth, desc, "srv_io_srv_sess_ctr");
+ depth++;
+
+ if(UNMARSHALLING(ps)) {
+ ctr = *pp_ctr = (SRV_SESS_INFO_CTR *)prs_alloc_mem(ps, sizeof(SRV_SESS_INFO_CTR));
+ if (ctr == NULL)
+ return False;
+ }
+
+ if (ctr == NULL)
+ return False;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("switch_value", ps, depth, &ctr->switch_value))
+ return False;
+ if(!prs_uint32("ptr_sess_ctr", ps, depth, &ctr->ptr_sess_ctr))
+ return False;
+
+ if (ctr->ptr_sess_ctr != 0) {
+ switch (ctr->switch_value) {
+ case 0:
+ if(!srv_io_srv_sess_info_0("", &ctr->sess.info0, ps, depth))
+ return False;
+ break;
+ case 1:
+ if(!srv_io_srv_sess_info_1("", &ctr->sess.info1, ps, depth))
+ return False;
+ break;
+ default:
+ DEBUG(5,("%s no session info at switch_value %d\n",
+ tab_depth(depth), ctr->switch_value));
+ break;
+ }
+ }
+
+ return True;
+}
+
+/*******************************************************************
+ Inits a SRV_Q_NET_SESS_ENUM structure.
+********************************************************************/
+
+void init_srv_q_net_sess_enum(SRV_Q_NET_SESS_ENUM *q_n,
+ const char *srv_name, const char *qual_name,
+ const char *user_name, uint32 sess_level,
+ SRV_SESS_INFO_CTR *ctr, uint32 preferred_len,
+ ENUM_HND *hnd)
+{
+ q_n->ctr = ctr;
+
+ DEBUG(5,("init_q_net_sess_enum\n"));
+
+ init_buf_unistr2(&q_n->uni_srv_name, &q_n->ptr_srv_name, srv_name);
+ init_buf_unistr2(&q_n->uni_qual_name, &q_n->ptr_qual_name, qual_name);
+ init_buf_unistr2(&q_n->uni_user_name, &q_n->ptr_user_name, user_name);
+
+ q_n->sess_level = sess_level;
+ q_n->preferred_len = preferred_len;
+
+ memcpy(&q_n->enum_hnd, hnd, sizeof(*hnd));
+}
+
+/*******************************************************************
+ Reads or writes a structure.
+********************************************************************/
+
+BOOL srv_io_q_net_sess_enum(const char *desc, SRV_Q_NET_SESS_ENUM *q_n, prs_struct *ps, int depth)
+{
+ if (q_n == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "srv_io_q_net_sess_enum");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("ptr_srv_name", ps, depth, &q_n->ptr_srv_name))
+ return False;
+ if(!smb_io_unistr2("", &q_n->uni_srv_name, True, ps, depth))
+ return False;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("ptr_qual_name", ps, depth, &q_n->ptr_qual_name))
+ return False;
+ if(!smb_io_unistr2("", &q_n->uni_qual_name, q_n->ptr_qual_name, ps, depth))
+ return False;
+
+ if(!prs_align(ps))
+ return False;
+ if(!prs_uint32("ptr_user_name", ps, depth, &q_n->ptr_user_name))
+ return False;
+ if(!smb_io_unistr2("", &q_n->uni_user_name, q_n->ptr_user_name, ps, depth))
+ return False;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("sess_level", ps, depth, &q_n->sess_level))
+ return False;
+
+ if (q_n->sess_level != (uint32)-1) {
+ if(!srv_io_srv_sess_ctr("sess_ctr", &q_n->ctr, ps, depth))
+ return False;
+ }
+
+ if(!prs_uint32("preferred_len", ps, depth, &q_n->preferred_len))
+ return False;
+
+ if(!smb_io_enum_hnd("enum_hnd", &q_n->enum_hnd, ps, depth))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ Reads or writes a structure.
+********************************************************************/
+
+BOOL srv_io_r_net_sess_enum(const char *desc, SRV_R_NET_SESS_ENUM *r_n, prs_struct *ps, int depth)
+{
+ if (r_n == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "srv_io_r_net_sess_enum");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("sess_level", ps, depth, &r_n->sess_level))
+ return False;
+
+ if (r_n->sess_level != (uint32)-1) {
+ if(!srv_io_srv_sess_ctr("sess_ctr", &r_n->ctr, ps, depth))
+ return False;
+ }
+
+ if(!prs_uint32("total_entries", ps, depth, &r_n->total_entries))
+ return False;
+ if(!smb_io_enum_hnd("enum_hnd", &r_n->enum_hnd, ps, depth))
+ return False;
+ if(!prs_werror("status", ps, depth, &r_n->status))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ Inits a CONN_INFO_0 structure
+********************************************************************/
+
+void init_srv_conn_info0(CONN_INFO_0 *ss0, uint32 id)
+{
+ DEBUG(5,("init_srv_conn_info0\n"));
+
+ ss0->id = id;
+}
+
+/*******************************************************************
+ Reads or writes a structure.
+********************************************************************/
+
+static BOOL srv_io_conn_info0(const char *desc, CONN_INFO_0 *ss0, prs_struct *ps, int depth)
+{
+ if (ss0 == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "srv_io_conn_info0");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("id", ps, depth, &ss0->id))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ Reads or writes a structure.
+********************************************************************/
+
+static BOOL srv_io_srv_conn_info_0(const char *desc, SRV_CONN_INFO_0 *ss0, prs_struct *ps, int depth)
+{
+ if (ss0 == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "srv_io_srv_conn_info_0");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("num_entries_read", ps, depth, &ss0->num_entries_read))
+ return False;
+ if(!prs_uint32("ptr_conn_info", ps, depth, &ss0->ptr_conn_info))
+ return False;
+
+ if (ss0->ptr_conn_info != 0) {
+ int i;
+ int num_entries = ss0->num_entries_read;
+
+ if (num_entries > MAX_CONN_ENTRIES) {
+ num_entries = MAX_CONN_ENTRIES; /* report this! */
+ }
+
+ if(!prs_uint32("num_entries_read2", ps, depth, &ss0->num_entries_read2))
+ return False;
+
+ for (i = 0; i < num_entries; i++) {
+ if(!srv_io_conn_info0("", &ss0->info_0[i], ps, depth))
+ return False;
+ }
+
+ if(!prs_align(ps))
+ return False;
+ }
+
+ return True;
+}
+
+/*******************************************************************
+ Inits a CONN_INFO_1_STR structure
+********************************************************************/
+
+void init_srv_conn_info1_str(CONN_INFO_1_STR *ss1, const char *usr_name, const char *net_name)
+{
+ DEBUG(5,("init_srv_conn_info1_str\n"));
+
+ init_unistr2(&ss1->uni_usr_name, usr_name, UNI_STR_TERMINATE);
+ init_unistr2(&ss1->uni_net_name, net_name, UNI_STR_TERMINATE);
+}
+
+/*******************************************************************
+ Reads or writes a structure.
+********************************************************************/
+
+static BOOL srv_io_conn_info1_str(const char *desc, CONN_INFO_1_STR *ss1, prs_struct *ps, int depth)
+{
+ if (ss1 == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "srv_io_conn_info1_str");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!smb_io_unistr2("", &ss1->uni_usr_name, True, ps, depth))
+ return False;
+ if(!smb_io_unistr2("", &ss1->uni_net_name, True, ps, depth))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ Inits a CONN_INFO_1 structure
+********************************************************************/
+
+void init_srv_conn_info1(CONN_INFO_1 *ss1,
+ uint32 id, uint32 type,
+ uint32 num_opens, uint32 num_users, uint32 open_time,
+ const char *usr_name, const char *net_name)
+{
+ DEBUG(5,("init_srv_conn_info1: %s %s\n", usr_name, net_name));
+
+ ss1->id = id ;
+ ss1->type = type ;
+ ss1->num_opens = num_opens ;
+ ss1->num_users = num_users;
+ ss1->open_time = open_time;
+
+ ss1->ptr_usr_name = (usr_name != NULL) ? 1 : 0;
+ ss1->ptr_net_name = (net_name != NULL) ? 1 : 0;
+}
+
+/*******************************************************************
+ Reads or writes a structure.
+********************************************************************/
+
+static BOOL srv_io_conn_info1(const char *desc, CONN_INFO_1 *ss1, prs_struct *ps, int depth)
+{
+ if (ss1 == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "srv_io_conn_info1");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("id ", ps, depth, &ss1->id))
+ return False;
+ if(!prs_uint32("type ", ps, depth, &ss1->type))
+ return False;
+ if(!prs_uint32("num_opens ", ps, depth, &ss1->num_opens))
+ return False;
+ if(!prs_uint32("num_users ", ps, depth, &ss1->num_users))
+ return False;
+ if(!prs_uint32("open_time ", ps, depth, &ss1->open_time))
+ return False;
+
+ if(!prs_uint32("ptr_usr_name", ps, depth, &ss1->ptr_usr_name))
+ return False;
+ if(!prs_uint32("ptr_net_name", ps, depth, &ss1->ptr_net_name))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ Reads or writes a structure.
+********************************************************************/
+
+static BOOL srv_io_srv_conn_info_1(const char *desc, SRV_CONN_INFO_1 *ss1, prs_struct *ps, int depth)
+{
+ if (ss1 == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "srv_io_srv_conn_info_1");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("num_entries_read", ps, depth, &ss1->num_entries_read))
+ return False;
+ if(!prs_uint32("ptr_conn_info", ps, depth, &ss1->ptr_conn_info))
+ return False;
+
+ if (ss1->ptr_conn_info != 0) {
+ int i;
+ int num_entries = ss1->num_entries_read;
+
+ if (num_entries > MAX_CONN_ENTRIES) {
+ num_entries = MAX_CONN_ENTRIES; /* report this! */
+ }
+
+ if(!prs_uint32("num_entries_read2", ps, depth, &ss1->num_entries_read2))
+ return False;
+
+ for (i = 0; i < num_entries; i++) {
+ if(!srv_io_conn_info1("", &ss1->info_1[i], ps, depth))
+ return False;
+ }
+
+ for (i = 0; i < num_entries; i++) {
+ if(!srv_io_conn_info1_str("", &ss1->info_1_str[i], ps, depth))
+ return False;
+ }
+
+ if(!prs_align(ps))
+ return False;
+ }
+
+ return True;
+}
+
+/*******************************************************************
+ Reads or writes a structure.
+********************************************************************/
+
+static BOOL srv_io_srv_conn_ctr(const char *desc, SRV_CONN_INFO_CTR **pp_ctr, prs_struct *ps, int depth)
+{
+ SRV_CONN_INFO_CTR *ctr = *pp_ctr;
+
+ prs_debug(ps, depth, desc, "srv_io_srv_conn_ctr");
+ depth++;
+
+ if (UNMARSHALLING(ps)) {
+ ctr = *pp_ctr = (SRV_CONN_INFO_CTR *)prs_alloc_mem(ps, sizeof(SRV_CONN_INFO_CTR));
+ if (ctr == NULL)
+ return False;
+ }
+
+ if (ctr == NULL)
+ return False;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("switch_value", ps, depth, &ctr->switch_value))
+ return False;
+ if(!prs_uint32("ptr_conn_ctr", ps, depth, &ctr->ptr_conn_ctr))
+ return False;
+
+ if (ctr->ptr_conn_ctr != 0) {
+ switch (ctr->switch_value) {
+ case 0:
+ if(!srv_io_srv_conn_info_0("", &ctr->conn.info0, ps, depth))
+ return False;
+ break;
+ case 1:
+ if(!srv_io_srv_conn_info_1("", &ctr->conn.info1, ps, depth))
+ return False;
+ break;
+ default:
+ DEBUG(5,("%s no connection info at switch_value %d\n",
+ tab_depth(depth), ctr->switch_value));
+ break;
+ }
+ }
+
+ return True;
+}
+
+/*******************************************************************
+ Reads or writes a structure.
+********************************************************************/
+
+void init_srv_q_net_conn_enum(SRV_Q_NET_CONN_ENUM *q_n,
+ const char *srv_name, const char *qual_name,
+ uint32 conn_level, SRV_CONN_INFO_CTR *ctr,
+ uint32 preferred_len,
+ ENUM_HND *hnd)
+{
+ DEBUG(5,("init_q_net_conn_enum\n"));
+
+ q_n->ctr = ctr;
+
+ init_buf_unistr2(&q_n->uni_srv_name, &q_n->ptr_srv_name, srv_name );
+ init_buf_unistr2(&q_n->uni_qual_name, &q_n->ptr_qual_name, qual_name);
+
+ q_n->conn_level = conn_level;
+ q_n->preferred_len = preferred_len;
+
+ memcpy(&q_n->enum_hnd, hnd, sizeof(*hnd));
+}
+
+/*******************************************************************
+ Reads or writes a structure.
+********************************************************************/
+
+BOOL srv_io_q_net_conn_enum(const char *desc, SRV_Q_NET_CONN_ENUM *q_n, prs_struct *ps, int depth)
+{
+ if (q_n == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "srv_io_q_net_conn_enum");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("ptr_srv_name ", ps, depth, &q_n->ptr_srv_name))
+ return False;
+ if(!smb_io_unistr2("", &q_n->uni_srv_name, q_n->ptr_srv_name, ps, depth))
+ return False;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("ptr_qual_name", ps, depth, &q_n->ptr_qual_name))
+ return False;
+ if(!smb_io_unistr2("", &q_n->uni_qual_name, q_n->ptr_qual_name, ps, depth))
+ return False;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("conn_level", ps, depth, &q_n->conn_level))
+ return False;
+
+ if (q_n->conn_level != (uint32)-1) {
+ if(!srv_io_srv_conn_ctr("conn_ctr", &q_n->ctr, ps, depth))
+ return False;
+ }
+
+ if(!prs_uint32("preferred_len", ps, depth, &q_n->preferred_len))
+ return False;
+
+ if(!smb_io_enum_hnd("enum_hnd", &q_n->enum_hnd, ps, depth))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ Reads or writes a structure.
+********************************************************************/
+
+BOOL srv_io_r_net_conn_enum(const char *desc, SRV_R_NET_CONN_ENUM *r_n, prs_struct *ps, int depth)
+{
+ if (r_n == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "srv_io_r_net_conn_enum");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("conn_level", ps, depth, &r_n->conn_level))
+ return False;
+
+ if (r_n->conn_level != (uint32)-1) {
+ if(!srv_io_srv_conn_ctr("conn_ctr", &r_n->ctr, ps, depth))
+ return False;
+ }
+
+ if(!prs_uint32("total_entries", ps, depth, &r_n->total_entries))
+ return False;
+ if(!smb_io_enum_hnd("enum_hnd", &r_n->enum_hnd, ps, depth))
+ return False;
+ if(!prs_werror("status", ps, depth, &r_n->status))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ Inits a FILE_INFO_3_STR structure
+********************************************************************/
+
+void init_srv_file_info3_str(FILE_INFO_3_STR *fi3, const char *user_name, const char *path_name)
+{
+ DEBUG(5,("init_srv_file_info3_str\n"));
+
+ init_unistr2(&fi3->uni_path_name, path_name, UNI_STR_TERMINATE);
+ init_unistr2(&fi3->uni_user_name, user_name, UNI_STR_TERMINATE);
+}
+
+/*******************************************************************
+ Reads or writes a structure.
+********************************************************************/
+
+static BOOL srv_io_file_info3_str(const char *desc, FILE_INFO_3_STR *sh1, prs_struct *ps, int depth)
+{
+ if (sh1 == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "srv_io_file_info3_str");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!smb_io_unistr2("", &sh1->uni_path_name, True, ps, depth))
+ return False;
+ if(!smb_io_unistr2("", &sh1->uni_user_name, True, ps, depth))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ Inits a FILE_INFO_3 structure
+********************************************************************/
+
+void init_srv_file_info3(FILE_INFO_3 *fl3,
+ uint32 id, uint32 perms, uint32 num_locks,
+ const char *path_name, const char *user_name)
+{
+ DEBUG(5,("init_srv_file_info3: %s %s\n", path_name, user_name));
+
+ fl3->id = id;
+ fl3->perms = perms;
+ fl3->num_locks = num_locks;
+
+ fl3->ptr_path_name = (path_name != NULL) ? 1 : 0;
+ fl3->ptr_user_name = (user_name != NULL) ? 1 : 0;
+}
+
+/*******************************************************************
+ Reads or writes a structure.
+********************************************************************/
+
+static BOOL srv_io_file_info3(const char *desc, FILE_INFO_3 *fl3, prs_struct *ps, int depth)
+{
+ if (fl3 == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "srv_io_file_info3");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("id ", ps, depth, &fl3->id))
+ return False;
+ if(!prs_uint32("perms ", ps, depth, &fl3->perms))
+ return False;
+ if(!prs_uint32("num_locks ", ps, depth, &fl3->num_locks))
+ return False;
+ if(!prs_uint32("ptr_path_name", ps, depth, &fl3->ptr_path_name))
+ return False;
+ if(!prs_uint32("ptr_user_name", ps, depth, &fl3->ptr_user_name))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ Reads or writes a structure.
+********************************************************************/
+
+static BOOL srv_io_srv_file_ctr(const char *desc, SRV_FILE_INFO_CTR *ctr, prs_struct *ps, int depth)
+{
+ if (ctr == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "srv_io_srv_file_ctr");
+ depth++;
+
+ if (UNMARSHALLING(ps)) {
+ memset(ctr, '\0', sizeof(SRV_FILE_INFO_CTR));
+ }
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("switch_value", ps, depth, &ctr->switch_value))
+ return False;
+ if (ctr->switch_value != 3) {
+ DEBUG(5,("%s File info %d level not supported\n",
+ tab_depth(depth), ctr->switch_value));
+ }
+ if(!prs_uint32("ptr_file_info", ps, depth, &ctr->ptr_file_info))
+ return False;
+ if(!prs_uint32("num_entries", ps, depth, &ctr->num_entries))
+ return False;
+ if(!prs_uint32("ptr_entries", ps, depth, &ctr->ptr_entries))
+ return False;
+ if (ctr->ptr_entries == 0)
+ return True;
+ if(!prs_uint32("num_entries2", ps, depth,
+ &ctr->num_entries2))
+ return False;
+
+ switch (ctr->switch_value) {
+ case 3: {
+ SRV_FILE_INFO_3 *info3 = ctr->file.info3;
+ int num_entries = ctr->num_entries;
+ int i;
+
+ if (UNMARSHALLING(ps)) {
+ if (!(info3 = (SRV_FILE_INFO_3 *)prs_alloc_mem(ps, num_entries * sizeof(SRV_FILE_INFO_3))))
+ return False;
+ ctr->file.info3 = info3;
+ }
+
+ for (i = 0; i < num_entries; i++) {
+ if(!srv_io_file_info3("", &ctr->file.info3[i].info_3, ps, depth))
+ return False;
+ }
+ for (i = 0; i < num_entries; i++) {
+ if(!srv_io_file_info3_str("", &ctr->file.info3[i].info_3_str, ps, depth))
+ return False;
+ }
+ break;
+ }
+ default:
+ DEBUG(5,("%s no file info at switch_value %d\n",
+ tab_depth(depth), ctr->switch_value));
+ break;
+ }
+
+ return True;
+}
+
+/*******************************************************************
+ Inits a SRV_Q_NET_FILE_ENUM structure.
+********************************************************************/
+
+void init_srv_q_net_file_enum(SRV_Q_NET_FILE_ENUM *q_n,
+ const char *srv_name, const char *qual_name,
+ const char *user_name,
+ uint32 file_level, SRV_FILE_INFO_CTR *ctr,
+ uint32 preferred_len,
+ ENUM_HND *hnd)
+{
+ DEBUG(5,("init_q_net_file_enum\n"));
+
+ init_buf_unistr2(&q_n->uni_srv_name, &q_n->ptr_srv_name, srv_name);
+ init_buf_unistr2(&q_n->uni_qual_name, &q_n->ptr_qual_name, qual_name);
+ init_buf_unistr2(&q_n->uni_user_name, &q_n->ptr_user_name, user_name);
+
+ q_n->file_level = q_n->ctr.switch_value = file_level;
+ q_n->preferred_len = preferred_len;
+ q_n->ctr.ptr_file_info = 1;
+ q_n->ctr.num_entries = 0;
+ q_n->ctr.num_entries2 = 0;
+
+ memcpy(&q_n->enum_hnd, hnd, sizeof(*hnd));
+}
+
+/*******************************************************************
+ Reads or writes a structure.
+********************************************************************/
+
+BOOL srv_io_q_net_file_enum(const char *desc, SRV_Q_NET_FILE_ENUM *q_n, prs_struct *ps, int depth)
+{
+ if (q_n == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "srv_io_q_net_file_enum");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("ptr_srv_name", ps, depth, &q_n->ptr_srv_name))
+ return False;
+ if(!smb_io_unistr2("", &q_n->uni_srv_name, True, ps, depth))
+ return False;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("ptr_qual_name", ps, depth, &q_n->ptr_qual_name))
+ return False;
+ if(!smb_io_unistr2("", &q_n->uni_qual_name, q_n->ptr_qual_name, ps, depth))
+ return False;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("ptr_user_name", ps, depth, &q_n->ptr_user_name))
+ return False;
+ if(!smb_io_unistr2("", &q_n->uni_user_name, q_n->ptr_user_name, ps, depth))
+ return False;
+
+ if(!prs_align(ps))
+ return False;
+ if(!prs_uint32("file_level", ps, depth, &q_n->file_level))
+ return False;
+
+ if (q_n->file_level != (uint32)-1) {
+ if(!srv_io_srv_file_ctr("file_ctr", &q_n->ctr, ps, depth))
+ return False;
+ }
+
+ if(!prs_uint32("preferred_len", ps, depth, &q_n->preferred_len))
+ return False;
+
+ if(!smb_io_enum_hnd("enum_hnd", &q_n->enum_hnd, ps, depth))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ Reads or writes a structure.
+********************************************************************/
+
+BOOL srv_io_r_net_file_enum(const char *desc, SRV_R_NET_FILE_ENUM *r_n, prs_struct *ps, int depth)
+{
+ if (r_n == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "srv_io_r_net_file_enum");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("file_level", ps, depth, &r_n->file_level))
+ return False;
+
+ if (r_n->file_level != 0) {
+ if(!srv_io_srv_file_ctr("file_ctr", &r_n->ctr, ps, depth))
+ return False;
+ }
+
+ if(!prs_uint32("total_entries", ps, depth, &r_n->total_entries))
+ return False;
+ if(!smb_io_enum_hnd("enum_hnd", &r_n->enum_hnd, ps, depth))
+ return False;
+ if(!prs_werror("status", ps, depth, &r_n->status))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ Initialize a net file close request
+********************************************************************/
+void init_srv_q_net_file_close(SRV_Q_NET_FILE_CLOSE *q_n, const char *server,
+ uint32 file_id)
+{
+ q_n->ptr_srv_name = 1;
+ init_unistr2(&q_n->uni_srv_name, server, UNI_STR_TERMINATE);
+ q_n->file_id = file_id;
+}
+
+/*******************************************************************
+ Reads or writes a structure.
+********************************************************************/
+BOOL srv_io_q_net_file_close(const char *desc, SRV_Q_NET_FILE_CLOSE *q_n,
+ prs_struct *ps, int depth)
+{
+ if (q_n == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "srv_io_q_net_file_close");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("ptr_srv_name", ps, depth, &q_n->ptr_srv_name))
+ return False;
+ if(!smb_io_unistr2("", &q_n->uni_srv_name, True, ps, depth))
+ return False;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("file_id", ps, depth, &q_n->file_id))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ Reads or writes a structure.
+********************************************************************/
+
+BOOL srv_io_r_net_file_close(const char *desc, SRV_R_NET_FILE_CLOSE *q_n,
+ prs_struct *ps, int depth)
+{
+ if (q_n == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "srv_io_r_net_file_close");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_werror("status", ps, depth, &q_n->status))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ Inits a SRV_INFO_100 structure.
+ ********************************************************************/
+
+void init_srv_info_100(SRV_INFO_100 *sv100, uint32 platform_id, const char *name)
+{
+ DEBUG(5,("init_srv_info_100\n"));
+
+ sv100->platform_id = platform_id;
+ init_buf_unistr2(&sv100->uni_name, &sv100->ptr_name, name);
+}
+
+/*******************************************************************
+ Reads or writes a SRV_INFO_101 structure.
+ ********************************************************************/
+
+static BOOL srv_io_info_100(const char *desc, SRV_INFO_100 *sv100, prs_struct *ps, int depth)
+{
+ if (sv100 == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "srv_io_info_100");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("platform_id ", ps, depth, &sv100->platform_id))
+ return False;
+ if(!prs_uint32("ptr_name ", ps, depth, &sv100->ptr_name))
+ return False;
+
+ if(!smb_io_unistr2("uni_name ", &sv100->uni_name, True, ps, depth))
+ return False;
+
+ return True;
+}
+
+
+/*******************************************************************
+ Inits a SRV_INFO_101 structure.
+ ********************************************************************/
+
+void init_srv_info_101(SRV_INFO_101 *sv101, uint32 platform_id, const char *name,
+ uint32 ver_major, uint32 ver_minor,
+ uint32 srv_type, const char *comment)
+{
+ DEBUG(5,("init_srv_info_101\n"));
+
+ sv101->platform_id = platform_id;
+ init_buf_unistr2(&sv101->uni_name, &sv101->ptr_name, name);
+ sv101->ver_major = ver_major;
+ sv101->ver_minor = ver_minor;
+ sv101->srv_type = srv_type;
+ init_buf_unistr2(&sv101->uni_comment, &sv101->ptr_comment, comment);
+}
+
+/*******************************************************************
+ Reads or writes a SRV_INFO_101 structure.
+ ********************************************************************/
+
+static BOOL srv_io_info_101(const char *desc, SRV_INFO_101 *sv101, prs_struct *ps, int depth)
+{
+ if (sv101 == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "srv_io_info_101");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("platform_id ", ps, depth, &sv101->platform_id))
+ return False;
+ if(!prs_uint32("ptr_name ", ps, depth, &sv101->ptr_name))
+ return False;
+ if(!prs_uint32("ver_major ", ps, depth, &sv101->ver_major))
+ return False;
+ if(!prs_uint32("ver_minor ", ps, depth, &sv101->ver_minor))
+ return False;
+ if(!prs_uint32("srv_type ", ps, depth, &sv101->srv_type))
+ return False;
+ if(!prs_uint32("ptr_comment ", ps, depth, &sv101->ptr_comment))
+ return False;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!smb_io_unistr2("uni_name ", &sv101->uni_name, True, ps, depth))
+ return False;
+ if(!smb_io_unistr2("uni_comment ", &sv101->uni_comment, True, ps, depth))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ Inits a SRV_INFO_102 structure.
+ ********************************************************************/
+
+void init_srv_info_102(SRV_INFO_102 *sv102, uint32 platform_id, const char *name,
+ const char *comment, uint32 ver_major, uint32 ver_minor,
+ uint32 srv_type, uint32 users, uint32 disc, uint32 hidden,
+ uint32 announce, uint32 ann_delta, uint32 licenses,
+ const char *usr_path)
+{
+ DEBUG(5,("init_srv_info_102\n"));
+
+ sv102->platform_id = platform_id;
+ init_buf_unistr2(&sv102->uni_name, &sv102->ptr_name, name);
+ sv102->ver_major = ver_major;
+ sv102->ver_minor = ver_minor;
+ sv102->srv_type = srv_type;
+ init_buf_unistr2(&sv102->uni_comment, &sv102->ptr_comment, comment);
+
+ /* same as 101 up to here */
+
+ sv102->users = users;
+ sv102->disc = disc;
+ sv102->hidden = hidden;
+ sv102->announce = announce;
+ sv102->ann_delta = ann_delta;
+ sv102->licenses = licenses;
+ init_buf_unistr2(&sv102->uni_usr_path, &sv102->ptr_usr_path, usr_path);
+}
+
+
+/*******************************************************************
+ Reads or writes a SRV_INFO_102 structure.
+ ********************************************************************/
+
+static BOOL srv_io_info_102(const char *desc, SRV_INFO_102 *sv102, prs_struct *ps, int depth)
+{
+ if (sv102 == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "srv_io_info102");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("platform_id ", ps, depth, &sv102->platform_id))
+ return False;
+ if(!prs_uint32("ptr_name ", ps, depth, &sv102->ptr_name))
+ return False;
+ if(!prs_uint32("ver_major ", ps, depth, &sv102->ver_major))
+ return False;
+ if(!prs_uint32("ver_minor ", ps, depth, &sv102->ver_minor))
+ return False;
+ if(!prs_uint32("srv_type ", ps, depth, &sv102->srv_type))
+ return False;
+ if(!prs_uint32("ptr_comment ", ps, depth, &sv102->ptr_comment))
+ return False;
+
+ /* same as 101 up to here */
+
+ if(!prs_uint32("users ", ps, depth, &sv102->users))
+ return False;
+ if(!prs_uint32("disc ", ps, depth, &sv102->disc))
+ return False;
+ if(!prs_uint32("hidden ", ps, depth, &sv102->hidden))
+ return False;
+ if(!prs_uint32("announce ", ps, depth, &sv102->announce))
+ return False;
+ if(!prs_uint32("ann_delta ", ps, depth, &sv102->ann_delta))
+ return False;
+ if(!prs_uint32("licenses ", ps, depth, &sv102->licenses))
+ return False;
+ if(!prs_uint32("ptr_usr_path", ps, depth, &sv102->ptr_usr_path))
+ return False;
+
+ if(!smb_io_unistr2("uni_name ", &sv102->uni_name, True, ps, depth))
+ return False;
+ if(!prs_align(ps))
+ return False;
+ if(!smb_io_unistr2("uni_comment ", &sv102->uni_comment, True, ps, depth))
+ return False;
+ if(!prs_align(ps))
+ return False;
+ if(!smb_io_unistr2("uni_usr_path", &sv102->uni_usr_path, True, ps, depth))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ Reads or writes a SRV_INFO_102 structure.
+ ********************************************************************/
+
+static BOOL srv_io_info_ctr(const char *desc, SRV_INFO_CTR *ctr, prs_struct *ps, int depth)
+{
+ if (ctr == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "srv_io_info_ctr");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("switch_value", ps, depth, &ctr->switch_value))
+ return False;
+ if(!prs_uint32("ptr_srv_ctr ", ps, depth, &ctr->ptr_srv_ctr))
+ return False;
+
+ if (ctr->ptr_srv_ctr != 0 && ctr->switch_value != 0 && ctr != NULL) {
+ switch (ctr->switch_value) {
+ case 100:
+ if(!srv_io_info_100("sv100", &ctr->srv.sv100, ps, depth))
+ return False;
+ break;
+ case 101:
+ if(!srv_io_info_101("sv101", &ctr->srv.sv101, ps, depth))
+ return False;
+ break;
+ case 102:
+ if(!srv_io_info_102("sv102", &ctr->srv.sv102, ps, depth))
+ return False;
+ break;
+ default:
+ DEBUG(5,("%s no server info at switch_value %d\n",
+ tab_depth(depth), ctr->switch_value));
+ break;
+ }
+ if(!prs_align(ps))
+ return False;
+ }
+
+ return True;
+}
+
+/*******************************************************************
+ Inits a SRV_Q_NET_SRV_GET_INFO structure.
+ ********************************************************************/
+
+void init_srv_q_net_srv_get_info(SRV_Q_NET_SRV_GET_INFO *srv,
+ const char *server_name, uint32 switch_value)
+{
+ DEBUG(5,("init_srv_q_net_srv_get_info\n"));
+
+ init_buf_unistr2(&srv->uni_srv_name, &srv->ptr_srv_name, server_name);
+
+ srv->switch_value = switch_value;
+}
+
+/*******************************************************************
+ Reads or writes a structure.
+********************************************************************/
+
+BOOL srv_io_q_net_srv_get_info(const char *desc, SRV_Q_NET_SRV_GET_INFO *q_n, prs_struct *ps, int depth)
+{
+ if (q_n == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "srv_io_q_net_srv_get_info");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("ptr_srv_name ", ps, depth, &q_n->ptr_srv_name))
+ return False;
+ if(!smb_io_unistr2("", &q_n->uni_srv_name, True, ps, depth))
+ return False;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("switch_value ", ps, depth, &q_n->switch_value))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ Inits a SRV_R_NET_SRV_GET_INFO structure.
+ ********************************************************************/
+
+void init_srv_r_net_srv_get_info(SRV_R_NET_SRV_GET_INFO *srv,
+ uint32 switch_value, SRV_INFO_CTR *ctr, WERROR status)
+{
+ DEBUG(5,("init_srv_r_net_srv_get_info\n"));
+
+ srv->ctr = ctr;
+
+ if (W_ERROR_IS_OK(status)) {
+ srv->ctr->switch_value = switch_value;
+ srv->ctr->ptr_srv_ctr = 1;
+ } else {
+ srv->ctr->switch_value = 0;
+ srv->ctr->ptr_srv_ctr = 0;
+ }
+
+ srv->status = status;
+}
+
+/*******************************************************************
+ Inits a SRV_R_NET_SRV_SET_INFO structure.
+ ********************************************************************/
+
+void init_srv_r_net_srv_set_info(SRV_R_NET_SRV_SET_INFO *srv,
+ uint32 switch_value, WERROR status)
+{
+ DEBUG(5,("init_srv_r_net_srv_set_info\n"));
+
+ srv->switch_value = switch_value;
+ srv->status = status;
+}
+
+/*******************************************************************
+ Reads or writes a structure.
+********************************************************************/
+
+BOOL srv_io_q_net_srv_set_info(const char *desc, SRV_Q_NET_SRV_SET_INFO *q_n,
+ prs_struct *ps, int depth)
+{
+ prs_debug(ps, depth, desc, "srv_io_q_net_srv_set_info");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("ptr_srv_name ", ps, depth, &q_n->ptr_srv_name))
+ return False;
+ if(!smb_io_unistr2("", &q_n->uni_srv_name, True, ps, depth))
+ return False;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("switch_value ", ps, depth, &q_n->switch_value))
+ return False;
+
+ if (UNMARSHALLING(ps)) {
+ q_n->ctr = (SRV_INFO_CTR *)
+ prs_alloc_mem(ps, sizeof(SRV_INFO_CTR));
+
+ if (!q_n->ctr)
+ return False;
+ }
+
+ if(!srv_io_info_ctr("ctr", q_n->ctr, ps, depth))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ Reads or writes a structure.
+ ********************************************************************/
+
+BOOL srv_io_r_net_srv_get_info(const char *desc, SRV_R_NET_SRV_GET_INFO *r_n, prs_struct *ps, int depth)
+{
+ if (r_n == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "srv_io_r_net_srv_get_info");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!srv_io_info_ctr("ctr", r_n->ctr, ps, depth))
+ return False;
+
+ if(!prs_werror("status", ps, depth, &r_n->status))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ Reads or writes a structure.
+ ********************************************************************/
+
+BOOL srv_io_r_net_srv_set_info(const char *desc, SRV_R_NET_SRV_SET_INFO *r_n,
+ prs_struct *ps, int depth)
+{
+ prs_debug(ps, depth, desc, "srv_io_r_net_srv_set_info");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("switch value ", ps, depth, &r_n->switch_value))
+ return False;
+
+ if(!prs_werror("status", ps, depth, &r_n->status))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ Reads or writes a structure.
+ ********************************************************************/
+
+BOOL srv_io_q_net_remote_tod(const char *desc, SRV_Q_NET_REMOTE_TOD *q_n, prs_struct *ps, int depth)
+{
+ if (q_n == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "srv_io_q_net_remote_tod");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("ptr_srv_name ", ps, depth, &q_n->ptr_srv_name))
+ return False;
+ if(!smb_io_unistr2("", &q_n->uni_srv_name, True, ps, depth))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ Reads or writes a TIME_OF_DAY_INFO structure.
+ ********************************************************************/
+
+static BOOL srv_io_time_of_day_info(const char *desc, TIME_OF_DAY_INFO *tod, prs_struct *ps, int depth)
+{
+ if (tod == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "srv_io_time_of_day_info");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("elapsedt ", ps, depth, &tod->elapsedt))
+ return False;
+ if(!prs_uint32("msecs ", ps, depth, &tod->msecs))
+ return False;
+ if(!prs_uint32("hours ", ps, depth, &tod->hours))
+ return False;
+ if(!prs_uint32("mins ", ps, depth, &tod->mins))
+ return False;
+ if(!prs_uint32("secs ", ps, depth, &tod->secs))
+ return False;
+ if(!prs_uint32("hunds ", ps, depth, &tod->hunds))
+ return False;
+ if(!prs_uint32("timezone ", ps, depth, &tod->zone))
+ return False;
+ if(!prs_uint32("tintervals ", ps, depth, &tod->tintervals))
+ return False;
+ if(!prs_uint32("day ", ps, depth, &tod->day))
+ return False;
+ if(!prs_uint32("month ", ps, depth, &tod->month))
+ return False;
+ if(!prs_uint32("year ", ps, depth, &tod->year))
+ return False;
+ if(!prs_uint32("weekday ", ps, depth, &tod->weekday))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ Inits a TIME_OF_DAY_INFO structure.
+ ********************************************************************/
+
+void init_time_of_day_info(TIME_OF_DAY_INFO *tod, uint32 elapsedt, uint32 msecs,
+ uint32 hours, uint32 mins, uint32 secs, uint32 hunds,
+ uint32 zone, uint32 tintervals, uint32 day,
+ uint32 month, uint32 year, uint32 weekday)
+{
+ DEBUG(5,("init_time_of_day_info\n"));
+
+ tod->elapsedt = elapsedt;
+ tod->msecs = msecs;
+ tod->hours = hours;
+ tod->mins = mins;
+ tod->secs = secs;
+ tod->hunds = hunds;
+ tod->zone = zone;
+ tod->tintervals = tintervals;
+ tod->day = day;
+ tod->month = month;
+ tod->year = year;
+ tod->weekday = weekday;
+}
+
+
+/*******************************************************************
+ Reads or writes a structure.
+ ********************************************************************/
+
+BOOL srv_io_r_net_remote_tod(const char *desc, SRV_R_NET_REMOTE_TOD *r_n, prs_struct *ps, int depth)
+{
+ if (r_n == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "srv_io_r_net_remote_tod");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("ptr_srv_tod ", ps, depth, &r_n->ptr_srv_tod))
+ return False;
+
+ if(!srv_io_time_of_day_info("tod", r_n->tod, ps, depth))
+ return False;
+
+ if(!prs_werror("status", ps, depth, &r_n->status))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ initialises a structure.
+ ********************************************************************/
+
+BOOL init_srv_q_net_disk_enum(SRV_Q_NET_DISK_ENUM *q_n,
+ const char *srv_name,
+ uint32 preferred_len,
+ ENUM_HND *enum_hnd
+ )
+{
+
+
+ DEBUG(5,("init_srv_q_net_srv_disk_enum\n"));
+
+ init_buf_unistr2(&q_n->uni_srv_name, &q_n->ptr_srv_name, srv_name);
+
+ q_n->disk_enum_ctr.level = 0;
+ q_n->disk_enum_ctr.disk_info_ptr = 0;
+
+ q_n->preferred_len = preferred_len;
+ memcpy(&q_n->enum_hnd, enum_hnd, sizeof(*enum_hnd));
+
+ return True;
+}
+
+/*******************************************************************
+ Reads or writes a structure.
+ ********************************************************************/
+
+BOOL srv_io_q_net_disk_enum(const char *desc, SRV_Q_NET_DISK_ENUM *q_n, prs_struct *ps, int depth)
+{
+ if (q_n == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "srv_io_q_net_disk_enum");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("ptr_srv_name", ps, depth, &q_n->ptr_srv_name))
+ return False;
+
+ if(!smb_io_unistr2("", &q_n->uni_srv_name, True, ps, depth))
+ return False;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("level", ps, depth, &q_n->disk_enum_ctr.level))
+ return False;
+
+ if(!prs_uint32("entries_read", ps, depth, &q_n->disk_enum_ctr.entries_read))
+ return False;
+
+ if(!prs_uint32("buffer", ps, depth, &q_n->disk_enum_ctr.disk_info_ptr))
+ return False;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("preferred_len", ps, depth, &q_n->preferred_len))
+ return False;
+ if(!smb_io_enum_hnd("enum_hnd", &q_n->enum_hnd, ps, depth))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ Reads or writes a structure.
+ ********************************************************************/
+
+BOOL srv_io_r_net_disk_enum(const char *desc, SRV_R_NET_DISK_ENUM *r_n, prs_struct *ps, int depth)
+{
+
+ unsigned int i;
+ uint32 entries_read, entries_read2, entries_read3;
+
+ if (r_n == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "srv_io_r_net_disk_enum");
+ depth++;
+
+ entries_read = entries_read2 = entries_read3 = r_n->disk_enum_ctr.entries_read;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("entries_read", ps, depth, &entries_read))
+ return False;
+ if(!prs_uint32("ptr_disk_info", ps, depth, &r_n->disk_enum_ctr.disk_info_ptr))
+ return False;
+
+ /*this may be max, unknown, actual?*/
+
+ if(!prs_uint32("max_elements", ps, depth, &entries_read2))
+ return False;
+ if(!prs_uint32("unknown", ps, depth, &r_n->disk_enum_ctr.unknown))
+ return False;
+ if(!prs_uint32("actual_elements", ps, depth, &entries_read3))
+ return False;
+
+ r_n->disk_enum_ctr.entries_read = entries_read3;
+
+ if(UNMARSHALLING(ps)) {
+
+ DISK_INFO *dinfo;
+
+ if(!(dinfo = (DISK_INFO *)prs_alloc_mem(ps, sizeof(*dinfo) * entries_read3)))
+ return False;
+ r_n->disk_enum_ctr.disk_info = dinfo;
+ }
+
+ for(i=0; i < r_n->disk_enum_ctr.entries_read; i++) {
+
+ if(!prs_uint32("unknown", ps, depth, &r_n->disk_enum_ctr.disk_info[i].unknown))
+ return False;
+
+ if(!smb_io_unistr3("disk_name", &r_n->disk_enum_ctr.disk_info[i].disk_name, ps, depth))
+ return False;
+
+ if(!prs_align(ps))
+ return False;
+ }
+
+ if(!prs_uint32("total_entries", ps, depth, &r_n->total_entries))
+ return False;
+
+ if(!smb_io_enum_hnd("enum_hnd", &r_n->enum_hnd, ps, depth))
+ return False;
+
+ if(!prs_werror("status", ps, depth, &r_n->status))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ initialises a structure.
+ ********************************************************************/
+
+BOOL init_srv_q_net_name_validate(SRV_Q_NET_NAME_VALIDATE *q_n, const char *srv_name, const char *share_name, int type)
+{
+ uint32 ptr_share_name;
+
+ DEBUG(5,("init_srv_q_net_name_validate\n"));
+
+ init_buf_unistr2(&q_n->uni_srv_name, &q_n->ptr_srv_name, srv_name);
+ init_buf_unistr2(&q_n->uni_name, &ptr_share_name, share_name);
+
+ q_n->type = type;
+ q_n->flags = 0;
+
+ return True;
+}
+
+/*******************************************************************
+ Reads or writes a structure.
+ ********************************************************************/
+
+BOOL srv_io_q_net_name_validate(const char *desc, SRV_Q_NET_NAME_VALIDATE *q_n, prs_struct *ps, int depth)
+{
+ if (q_n == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "srv_io_q_net_name_validate");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("ptr_srv_name", ps, depth, &q_n->ptr_srv_name))
+ return False;
+
+ if(!smb_io_unistr2("", &q_n->uni_srv_name, True, ps, depth))
+ return False;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!smb_io_unistr2("", &q_n->uni_name, True, ps, depth))
+ return False;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("type", ps, depth, &q_n->type))
+ return False;
+
+ if(!prs_uint32("flags", ps, depth, &q_n->flags))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ Reads or writes a structure.
+ ********************************************************************/
+
+BOOL srv_io_r_net_name_validate(const char *desc, SRV_R_NET_NAME_VALIDATE *r_n, prs_struct *ps, int depth)
+{
+ if (r_n == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "srv_io_r_net_name_validate");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_werror("status", ps, depth, &r_n->status))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ Reads or writes a structure.
+********************************************************************/
+
+BOOL srv_io_q_net_file_query_secdesc(const char *desc, SRV_Q_NET_FILE_QUERY_SECDESC *q_n, prs_struct *ps, int depth)
+{
+ if (q_n == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "srv_io_q_net_file_query_secdesc");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("ptr_srv_name", ps, depth, &q_n->ptr_srv_name))
+ return False;
+
+ if(!smb_io_unistr2("", &q_n->uni_srv_name, True, ps, depth))
+ return False;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("ptr_qual_name", ps, depth, &q_n->ptr_qual_name))
+ return False;
+
+ if(!smb_io_unistr2("", &q_n->uni_qual_name, True, ps, depth))
+ return False;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!smb_io_unistr2("", &q_n->uni_file_name, True, ps, depth))
+ return False;
+
+ if(!prs_uint32("unknown1", ps, depth, &q_n->unknown1))
+ return False;
+
+ if(!prs_uint32("unknown2", ps, depth, &q_n->unknown2))
+ return False;
+
+ if(!prs_uint32("unknown3", ps, depth, &q_n->unknown3))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ Reads or writes a structure.
+********************************************************************/
+
+BOOL srv_io_r_net_file_query_secdesc(const char *desc, SRV_R_NET_FILE_QUERY_SECDESC *r_n, prs_struct *ps, int depth)
+{
+ if (r_n == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "srv_io_r_net_file_query_secdesc");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("ptr_response", ps, depth, &r_n->ptr_response))
+ return False;
+
+ if(!prs_uint32("size_response", ps, depth, &r_n->size_response))
+ return False;
+
+ if(!prs_uint32("ptr_secdesc", ps, depth, &r_n->ptr_secdesc))
+ return False;
+
+ if(!prs_uint32("size_secdesc", ps, depth, &r_n->size_secdesc))
+ return False;
+
+ if(!sec_io_desc("sec_desc", &r_n->sec_desc, ps, depth))
+ return False;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_werror("status", ps, depth, &r_n->status))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ Reads or writes a structure.
+********************************************************************/
+
+BOOL srv_io_q_net_file_set_secdesc(const char *desc, SRV_Q_NET_FILE_SET_SECDESC *q_n, prs_struct *ps, int depth)
+{
+ if (q_n == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "srv_io_q_net_file_set_secdesc");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("ptr_srv_name", ps, depth, &q_n->ptr_srv_name))
+ return False;
+
+ if(!smb_io_unistr2("", &q_n->uni_srv_name, True, ps, depth))
+ return False;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("ptr_qual_name", ps, depth, &q_n->ptr_qual_name))
+ return False;
+
+ if(!smb_io_unistr2("", &q_n->uni_qual_name, True, ps, depth))
+ return False;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!smb_io_unistr2("", &q_n->uni_file_name, True, ps, depth))
+ return False;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("sec_info", ps, depth, &q_n->sec_info))
+ return False;
+
+ if(!prs_uint32("size_set", ps, depth, &q_n->size_set))
+ return False;
+
+ if(!prs_uint32("ptr_secdesc", ps, depth, &q_n->ptr_secdesc))
+ return False;
+
+ if(!prs_uint32("size_secdesc", ps, depth, &q_n->size_secdesc))
+ return False;
+
+ if(!sec_io_desc("sec_desc", &q_n->sec_desc, ps, depth))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ Reads or writes a structure.
+********************************************************************/
+
+BOOL srv_io_r_net_file_set_secdesc(const char *desc, SRV_R_NET_FILE_SET_SECDESC *r_n, prs_struct *ps, int depth)
+{
+ if (r_n == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "srv_io_r_net_file_set_secdesc");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_werror("status", ps, depth, &r_n->status))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ Inits a structure
+********************************************************************/
+
+void init_srv_q_net_remote_tod(SRV_Q_NET_REMOTE_TOD *q_u, const char *server)
+{
+ q_u->ptr_srv_name = 1;
+ init_unistr2(&q_u->uni_srv_name, server, UNI_STR_TERMINATE);
+}
diff --git a/source/rpc_parse/parse_wks.c b/source/rpc_parse/parse_wks.c
new file mode 100644
index 00000000000..b6de058652b
--- /dev/null
+++ b/source/rpc_parse/parse_wks.c
@@ -0,0 +1,178 @@
+/*
+ * Unix SMB/CIFS implementation.
+ * RPC Pipe client / server routines
+ * 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 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"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_RPC_PARSE
+
+/*******************************************************************
+ Init
+ ********************************************************************/
+
+void init_wks_q_query_info(WKS_Q_QUERY_INFO *q_u,
+ char *server, uint16 switch_value)
+{
+ DEBUG(5,("init_wks_q_query_info\n"));
+
+ init_buf_unistr2(&q_u->uni_srv_name, &q_u->ptr_srv_name, server);
+ q_u->switch_value = switch_value;
+}
+
+/*******************************************************************
+ Reads or writes a WKS_Q_QUERY_INFO structure.
+********************************************************************/
+
+BOOL wks_io_q_query_info(const char *desc, WKS_Q_QUERY_INFO *q_u, prs_struct *ps, int depth)
+{
+ if (q_u == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "wks_io_q_query_info");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("ptr_srv_name", ps, depth, &q_u->ptr_srv_name))
+ return False;
+ if(!smb_io_unistr2("", &q_u->uni_srv_name, q_u->ptr_srv_name, ps, depth))
+ return False;
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint16("switch_value", ps, depth, &q_u->switch_value))
+ return False;
+ if(!prs_align(ps))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ wks_info_100
+ ********************************************************************/
+
+void init_wks_info_100(WKS_INFO_100 *inf,
+ uint32 platform_id, uint32 ver_major, uint32 ver_minor,
+ char *my_name, char *domain_name)
+{
+ DEBUG(5,("Init WKS_INFO_100: %d\n", __LINE__));
+
+ inf->platform_id = platform_id; /* 0x0000 01f4 - unknown */
+ inf->ver_major = ver_major; /* os major version */
+ inf->ver_minor = ver_minor; /* os minor version */
+
+ init_buf_unistr2(&inf->uni_compname, &inf->ptr_compname, my_name );
+ init_buf_unistr2(&inf->uni_lan_grp, &inf->ptr_lan_grp, domain_name);
+}
+
+/*******************************************************************
+ Reads or writes a WKS_INFO_100 structure.
+********************************************************************/
+
+static BOOL wks_io_wks_info_100(const char *desc, WKS_INFO_100 *inf, prs_struct *ps, int depth)
+{
+ if (inf == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "wks_io_wks_info_100");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("platform_id ", ps, depth, &inf->platform_id)) /* 0x0000 01f4 - unknown */
+ return False;
+ if(!prs_uint32("ptr_compname", ps, depth, &inf->ptr_compname)) /* pointer to computer name */
+ return False;
+ if(!prs_uint32("ptr_lan_grp ", ps, depth, &inf->ptr_lan_grp)) /* pointer to LAN group name */
+ return False;
+ if(!prs_uint32("ver_major ", ps, depth, &inf->ver_major)) /* 4 - major os version */
+ return False;
+ if(!prs_uint32("ver_minor ", ps, depth, &inf->ver_minor)) /* 0 - minor os version */
+ return False;
+
+ if(!smb_io_unistr2("", &inf->uni_compname, inf->ptr_compname, ps, depth))
+ return False;
+ if(!prs_align(ps))
+ return False;
+
+ if(!smb_io_unistr2("", &inf->uni_lan_grp, inf->ptr_lan_grp , ps, depth))
+ return False;
+ if(!prs_align(ps))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ Inits WKS_R_QUERY_INFO.
+
+ only supports info level 100 at the moment.
+
+ ********************************************************************/
+
+void init_wks_r_query_info(WKS_R_QUERY_INFO *r_u,
+ uint32 switch_value, WKS_INFO_100 *wks100,
+ NTSTATUS status)
+{
+ DEBUG(5,("init_wks_r_unknown_0: %d\n", __LINE__));
+
+ r_u->switch_value = switch_value; /* same as in request */
+
+ r_u->ptr_1 = 1; /* pointer 1 */
+ r_u->wks100 = wks100;
+
+ r_u->status = status;
+}
+
+/*******************************************************************
+ Reads or writes a structure.
+********************************************************************/
+
+BOOL wks_io_r_query_info(const char *desc, WKS_R_QUERY_INFO *r_u, prs_struct *ps, int depth)
+{
+ if (r_u == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "wks_io_r_query_info");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint16("switch_value", ps, depth, &r_u->switch_value)) /* level 100 (0x64) */
+ return False;
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("ptr_1 ", ps, depth, &r_u->ptr_1)) /* pointer 1 */
+ return False;
+ if(!wks_io_wks_info_100("inf", r_u->wks100, ps, depth))
+ return False;
+
+ if(!prs_ntstatus("status ", ps, depth, &r_u->status))
+ return False;
+
+ return True;
+}
diff --git a/source/rpc_server/.cvsignore b/source/rpc_server/.cvsignore
new file mode 100644
index 00000000000..e69de29bb2d
--- /dev/null
+++ b/source/rpc_server/.cvsignore
diff --git a/source/rpc_server/srv_dfs.c b/source/rpc_server/srv_dfs.c
new file mode 100644
index 00000000000..6c35917e618
--- /dev/null
+++ b/source/rpc_server/srv_dfs.c
@@ -0,0 +1,179 @@
+/*
+ * Unix SMB/CIFS implementation.
+ * RPC Pipe client / server routines for Dfs
+ * Copyright (C) Andrew Tridgell 1992-1997,
+ * Copyright (C) Luke Kenneth Casson Leighton 1996-1997,
+ * Copyright (C) Shirish Kalele 2000,
+ * Copyright (C) Jeremy Allison 2001,
+ * 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 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.
+ */
+
+/* This is the interface to the dfs pipe. */
+
+#include "includes.h"
+#include "nterr.h"
+
+#define MAX_MSDFS_JUNCTIONS 256
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_RPC_SRV
+
+/**********************************************************************
+ api_dfs_exist
+ **********************************************************************/
+
+static BOOL api_dfs_exist(pipes_struct *p)
+{
+ DFS_Q_DFS_EXIST q_u;
+ DFS_R_DFS_EXIST r_u;
+ prs_struct *data = &p->in_data.data;
+ prs_struct *rdata = &p->out_data.rdata;
+
+ if(!dfs_io_q_dfs_exist("", &q_u, data, 0))
+ return False;
+
+ r_u.status = _dfs_exist(p, &q_u, &r_u);
+
+ if (!dfs_io_r_dfs_exist("", &r_u, rdata, 0))
+ return False;
+
+ return True;
+}
+
+/*****************************************************************
+ api_dfs_add
+ *****************************************************************/
+
+static BOOL api_dfs_add(pipes_struct *p)
+{
+ DFS_Q_DFS_ADD q_u;
+ DFS_R_DFS_ADD r_u;
+ prs_struct *data = &p->in_data.data;
+ prs_struct *rdata = &p->out_data.rdata;
+
+ ZERO_STRUCT(q_u);
+ ZERO_STRUCT(r_u);
+
+ if(!dfs_io_q_dfs_add("", &q_u, data, 0))
+ return False;
+
+ r_u.status = _dfs_add(p, &q_u, &r_u);
+
+ if (!dfs_io_r_dfs_add("", &r_u, rdata, 0))
+ return False;
+
+ return True;
+}
+
+/*****************************************************************
+ api_dfs_remove
+ *****************************************************************/
+
+static BOOL api_dfs_remove(pipes_struct *p)
+{
+ DFS_Q_DFS_REMOVE q_u;
+ DFS_R_DFS_REMOVE r_u;
+ prs_struct *data = &p->in_data.data;
+ prs_struct *rdata = &p->out_data.rdata;
+
+ ZERO_STRUCT(q_u);
+ ZERO_STRUCT(r_u);
+
+ if(!dfs_io_q_dfs_remove("", &q_u, data, 0))
+ return False;
+
+ r_u.status = _dfs_remove(p, &q_u, &r_u);
+
+ if (!dfs_io_r_dfs_remove("", &r_u, rdata, 0))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ api_dfs_get_info
+ *******************************************************************/
+
+static BOOL api_dfs_get_info(pipes_struct *p)
+{
+ DFS_Q_DFS_GET_INFO q_u;
+ DFS_R_DFS_GET_INFO r_u;
+ prs_struct *data = &p->in_data.data;
+ prs_struct *rdata = &p->out_data.rdata;
+
+ ZERO_STRUCT(q_u);
+ ZERO_STRUCT(r_u);
+
+ if(!dfs_io_q_dfs_get_info("", &q_u, data, 0))
+ return False;
+
+ r_u.status = _dfs_get_info(p, &q_u, &r_u);
+
+ if(!dfs_io_r_dfs_get_info("", &r_u, rdata, 0))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ api_dfs_enum
+ *******************************************************************/
+
+static BOOL api_dfs_enum(pipes_struct *p)
+{
+ DFS_Q_DFS_ENUM q_u;
+ DFS_R_DFS_ENUM r_u;
+ prs_struct *data = &p->in_data.data;
+ prs_struct *rdata = &p->out_data.rdata;
+
+ ZERO_STRUCT(q_u);
+ ZERO_STRUCT(r_u);
+
+ if(!dfs_io_q_dfs_enum("", &q_u, data, 0))
+ return False;
+
+ r_u.status = _dfs_enum(p, &q_u, &r_u);
+
+ if(!dfs_io_r_dfs_enum("", &r_u, rdata, 0))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+\pipe\netdfs commands
+********************************************************************/
+static struct api_struct api_netdfs_cmds[] =
+{
+ {"DFS_EXIST", DFS_EXIST, api_dfs_exist },
+ {"DFS_ADD", DFS_ADD, api_dfs_add },
+ {"DFS_REMOVE", DFS_REMOVE, api_dfs_remove },
+ {"DFS_GET_INFO", DFS_GET_INFO, api_dfs_get_info },
+ {"DFS_ENUM", DFS_ENUM, api_dfs_enum }
+};
+
+void netdfs_get_pipe_fns( struct api_struct **fns, int *n_fns )
+{
+ *fns = api_netdfs_cmds;
+ *n_fns = sizeof(api_netdfs_cmds) / sizeof(struct api_struct);
+}
+
+NTSTATUS rpc_dfs_init(void)
+{
+ return rpc_pipe_register_commands(SMB_RPC_INTERFACE_VERSION, "netdfs", "netdfs", api_netdfs_cmds,
+ sizeof(api_netdfs_cmds) / sizeof(struct api_struct));
+}
diff --git a/source/rpc_server/srv_dfs_nt.c b/source/rpc_server/srv_dfs_nt.c
new file mode 100644
index 00000000000..a3b06bb6e10
--- /dev/null
+++ b/source/rpc_server/srv_dfs_nt.c
@@ -0,0 +1,377 @@
+/*
+ * Unix SMB/CIFS implementation.
+ * RPC Pipe client / server routines for Dfs
+ * Copyright (C) Andrew Tridgell 1992-1997,
+ * Copyright (C) Luke Kenneth Casson Leighton 1996-1997,
+ * Copyright (C) Shirish Kalele 2000.
+ * Copyright (C) Jeremy Allison 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 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.
+ */
+
+/* This is the implementation of the dfs pipe. */
+
+#include "includes.h"
+#include "nterr.h"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_RPC_SRV
+
+#define MAX_MSDFS_JUNCTIONS 256
+
+/* This function does not return a WERROR or NTSTATUS code but rather 1 if
+ dfs exists, or 0 otherwise. */
+
+uint32 _dfs_exist(pipes_struct *p, DFS_Q_DFS_EXIST *q_u, DFS_R_DFS_EXIST *r_u)
+{
+ if(lp_host_msdfs())
+ return 1;
+ else
+ return 0;
+}
+
+WERROR _dfs_add(pipes_struct *p, DFS_Q_DFS_ADD* q_u, DFS_R_DFS_ADD *r_u)
+{
+ struct current_user user;
+ struct junction_map jn;
+ struct referral* old_referral_list = NULL;
+ BOOL exists = False;
+
+ pstring dfspath, servername, sharename;
+ pstring altpath;
+
+ get_current_user(&user,p);
+
+ if (user.uid != 0) {
+ DEBUG(10,("_dfs_add: uid != 0. Access denied.\n"));
+ return WERR_ACCESS_DENIED;
+ }
+
+ unistr2_to_ascii(dfspath, &q_u->DfsEntryPath, sizeof(dfspath)-1);
+ unistr2_to_ascii(servername, &q_u->ServerName, sizeof(servername)-1);
+ unistr2_to_ascii(sharename, &q_u->ShareName, sizeof(sharename)-1);
+
+ DEBUG(5,("init_reply_dfs_add: Request to add %s -> %s\\%s.\n",
+ dfspath, servername, sharename));
+
+ pstrcpy(altpath, servername);
+ pstrcat(altpath, "\\");
+ pstrcat(altpath, sharename);
+
+ /* The following call can change the cwd. */
+ if(get_referred_path(dfspath, &jn, NULL, NULL)) {
+ exists = True;
+ jn.referral_count += 1;
+ old_referral_list = jn.referral_list;
+ } else {
+ jn.referral_count = 1;
+ }
+
+ vfs_ChDir(p->conn,p->conn->connectpath);
+
+ jn.referral_list = (struct referral*) talloc(p->mem_ctx, jn.referral_count
+ * sizeof(struct referral));
+
+ if(jn.referral_list == NULL) {
+ DEBUG(0,("init_reply_dfs_add: talloc failed for referral list!\n"));
+ return WERR_DFS_INTERNAL_ERROR;
+ }
+
+ if(old_referral_list) {
+ memcpy(jn.referral_list, old_referral_list, sizeof(struct referral)*jn.referral_count-1);
+ SAFE_FREE(old_referral_list);
+ }
+
+ jn.referral_list[jn.referral_count-1].proximity = 0;
+ jn.referral_list[jn.referral_count-1].ttl = REFERRAL_TTL;
+
+ pstrcpy(jn.referral_list[jn.referral_count-1].alternate_path, altpath);
+
+ if(!create_msdfs_link(&jn, exists)) {
+ vfs_ChDir(p->conn,p->conn->connectpath);
+ return WERR_DFS_CANT_CREATE_JUNCT;
+ }
+ vfs_ChDir(p->conn,p->conn->connectpath);
+
+ return WERR_OK;
+}
+
+WERROR _dfs_remove(pipes_struct *p, DFS_Q_DFS_REMOVE *q_u,
+ DFS_R_DFS_REMOVE *r_u)
+{
+ struct current_user user;
+ struct junction_map jn;
+ BOOL found = False;
+
+ pstring dfspath, servername, sharename;
+ pstring altpath;
+
+ get_current_user(&user,p);
+
+ if (user.uid != 0) {
+ DEBUG(10,("_dfs_remove: uid != 0. Access denied.\n"));
+ return WERR_ACCESS_DENIED;
+ }
+
+ unistr2_to_ascii(dfspath, &q_u->DfsEntryPath, sizeof(dfspath)-1);
+ if(q_u->ptr_ServerName) {
+ unistr2_to_ascii(servername, &q_u->ServerName, sizeof(servername)-1);
+ }
+
+ if(q_u->ptr_ShareName) {
+ unistr2_to_ascii(sharename, &q_u->ShareName, sizeof(sharename)-1);
+ }
+
+ if(q_u->ptr_ServerName && q_u->ptr_ShareName) {
+ pstrcpy(altpath, servername);
+ pstrcat(altpath, "\\");
+ pstrcat(altpath, sharename);
+ strlower_m(altpath);
+ }
+
+ DEBUG(5,("init_reply_dfs_remove: Request to remove %s -> %s\\%s.\n",
+ dfspath, servername, sharename));
+
+ if(!get_referred_path(dfspath, &jn, NULL, NULL)) {
+ return WERR_DFS_NO_SUCH_VOL;
+ }
+
+ /* if no server-share pair given, remove the msdfs link completely */
+ if(!q_u->ptr_ServerName && !q_u->ptr_ShareName) {
+ if(!remove_msdfs_link(&jn)) {
+ vfs_ChDir(p->conn,p->conn->connectpath);
+ return WERR_DFS_NO_SUCH_VOL;
+ }
+ vfs_ChDir(p->conn,p->conn->connectpath);
+ } else {
+ int i=0;
+ /* compare each referral in the list with the one to remove */
+ DEBUG(10,("altpath: .%s. refcnt: %d\n", altpath, jn.referral_count));
+ for(i=0;i<jn.referral_count;i++) {
+ pstring refpath;
+ pstrcpy(refpath,jn.referral_list[i].alternate_path);
+ trim_char(refpath, '\\', '\\');
+ DEBUG(10,("_dfs_remove: refpath: .%s.\n", refpath));
+ if(strequal(refpath, altpath)) {
+ *(jn.referral_list[i].alternate_path)='\0';
+ DEBUG(10,("_dfs_remove: Removal request matches referral %s\n",
+ refpath));
+ found = True;
+ }
+ }
+
+ if(!found) {
+ return WERR_DFS_NO_SUCH_SHARE;
+ }
+
+ /* Only one referral, remove it */
+ if(jn.referral_count == 1) {
+ if(!remove_msdfs_link(&jn)) {
+ vfs_ChDir(p->conn,p->conn->connectpath);
+ return WERR_DFS_NO_SUCH_VOL;
+ }
+ } else {
+ if(!create_msdfs_link(&jn, True)) {
+ vfs_ChDir(p->conn,p->conn->connectpath);
+ return WERR_DFS_CANT_CREATE_JUNCT;
+ }
+ }
+ vfs_ChDir(p->conn,p->conn->connectpath);
+ }
+
+ return WERR_OK;
+}
+
+static BOOL init_reply_dfs_info_1(struct junction_map* j, DFS_INFO_1* dfs1, int num_j)
+{
+ int i=0;
+ for(i=0;i<num_j;i++) {
+ pstring str;
+ dfs1[i].ptr_entrypath = 1;
+ slprintf(str, sizeof(pstring)-1, "\\\\%s\\%s\\%s", global_myname(),
+ j[i].service_name, j[i].volume_name);
+ DEBUG(5,("init_reply_dfs_info_1: %d) initing entrypath: %s\n",i,str));
+ init_unistr2(&dfs1[i].entrypath,str,UNI_STR_TERMINATE);
+ }
+ return True;
+}
+
+static BOOL init_reply_dfs_info_2(struct junction_map* j, DFS_INFO_2* dfs2, int num_j)
+{
+ int i=0;
+ for(i=0;i<num_j;i++) {
+ pstring str;
+ dfs2[i].ptr_entrypath = 1;
+ slprintf(str, sizeof(pstring)-1, "\\\\%s\\%s\\%s", global_myname(),
+ j[i].service_name, j[i].volume_name);
+ init_unistr2(&dfs2[i].entrypath, str, UNI_STR_TERMINATE);
+ dfs2[i].ptr_comment = 0;
+ dfs2[i].state = 1; /* set up state of dfs junction as OK */
+ dfs2[i].num_storages = j[i].referral_count;
+ }
+ return True;
+}
+
+static BOOL init_reply_dfs_info_3(TALLOC_CTX *ctx, struct junction_map* j, DFS_INFO_3* dfs3, int num_j)
+{
+ int i=0,ii=0;
+ for(i=0;i<num_j;i++) {
+ pstring str;
+ dfs3[i].ptr_entrypath = 1;
+ if (j[i].volume_name[0] == '\0')
+ slprintf(str, sizeof(pstring)-1, "\\\\%s\\%s",
+ global_myname(), j[i].service_name);
+ else
+ slprintf(str, sizeof(pstring)-1, "\\\\%s\\%s\\%s", global_myname(),
+ j[i].service_name, j[i].volume_name);
+
+ init_unistr2(&dfs3[i].entrypath, str, UNI_STR_TERMINATE);
+ dfs3[i].ptr_comment = 1;
+ init_unistr2(&dfs3[i].comment, "", UNI_STR_TERMINATE);
+ dfs3[i].state = 1;
+ dfs3[i].num_storages = dfs3[i].num_storage_infos = j[i].referral_count;
+ dfs3[i].ptr_storages = 1;
+
+ /* also enumerate the storages */
+ dfs3[i].storages = (DFS_STORAGE_INFO*) talloc(ctx, j[i].referral_count *
+ sizeof(DFS_STORAGE_INFO));
+ if (!dfs3[i].storages)
+ return False;
+
+ memset(dfs3[i].storages, '\0', j[i].referral_count * sizeof(DFS_STORAGE_INFO));
+
+ for(ii=0;ii<j[i].referral_count;ii++) {
+ char* p;
+ pstring path;
+ DFS_STORAGE_INFO* stor = &(dfs3[i].storages[ii]);
+ struct referral* ref = &(j[i].referral_list[ii]);
+
+ pstrcpy(path, ref->alternate_path);
+ trim_char(path,'\\','\0');
+ p = strrchr_m(path,'\\');
+ if(p==NULL) {
+ DEBUG(4,("init_reply_dfs_info_3: invalid path: no \\ found in %s\n",path));
+ continue;
+ }
+ *p = '\0';
+ DEBUG(5,("storage %d: %s.%s\n",ii,path,p+1));
+ stor->state = 2; /* set all storages as ONLINE */
+ init_unistr2(&stor->servername, path, UNI_STR_TERMINATE);
+ init_unistr2(&stor->sharename, p+1, UNI_STR_TERMINATE);
+ stor->ptr_servername = stor->ptr_sharename = 1;
+ }
+ }
+ return True;
+}
+
+static WERROR init_reply_dfs_ctr(TALLOC_CTX *ctx, uint32 level,
+ DFS_INFO_CTR* ctr, struct junction_map* jn,
+ int num_jn)
+{
+ /* do the levels */
+ switch(level) {
+ case 1:
+ {
+ DFS_INFO_1* dfs1;
+ dfs1 = (DFS_INFO_1*) talloc(ctx, num_jn * sizeof(DFS_INFO_1));
+ if (!dfs1)
+ return WERR_NOMEM;
+ init_reply_dfs_info_1(jn, dfs1, num_jn);
+ ctr->dfs.info1 = dfs1;
+ break;
+ }
+ case 2:
+ {
+ DFS_INFO_2* dfs2;
+ dfs2 = (DFS_INFO_2*) talloc(ctx, num_jn * sizeof(DFS_INFO_2));
+ if (!dfs2)
+ return WERR_NOMEM;
+ init_reply_dfs_info_2(jn, dfs2, num_jn);
+ ctr->dfs.info2 = dfs2;
+ break;
+ }
+ case 3:
+ {
+ DFS_INFO_3* dfs3;
+ dfs3 = (DFS_INFO_3*) talloc(ctx, num_jn * sizeof(DFS_INFO_3));
+ if (!dfs3)
+ return WERR_NOMEM;
+ init_reply_dfs_info_3(ctx, jn, dfs3, num_jn);
+ ctr->dfs.info3 = dfs3;
+ break;
+ }
+ default:
+ return WERR_INVALID_PARAM;
+ }
+ return WERR_OK;
+}
+
+WERROR _dfs_enum(pipes_struct *p, DFS_Q_DFS_ENUM *q_u, DFS_R_DFS_ENUM *r_u)
+{
+ uint32 level = q_u->level;
+ struct junction_map jn[MAX_MSDFS_JUNCTIONS];
+ int num_jn = 0;
+
+ num_jn = enum_msdfs_links(jn);
+ vfs_ChDir(p->conn,p->conn->connectpath);
+
+ DEBUG(5,("make_reply_dfs_enum: %d junctions found in Dfs, doing level %d\n", num_jn, level));
+
+ r_u->ptr_buffer = level;
+ r_u->level = r_u->level2 = level;
+ r_u->ptr_num_entries = r_u->ptr_num_entries2 = 1;
+ r_u->num_entries = r_u->num_entries2 = num_jn;
+ r_u->reshnd.ptr_hnd = 1;
+ r_u->reshnd.handle = num_jn;
+
+ r_u->ctr = (DFS_INFO_CTR*)talloc(p->mem_ctx, sizeof(DFS_INFO_CTR));
+ if (!r_u->ctr)
+ return WERR_NOMEM;
+ ZERO_STRUCTP(r_u->ctr);
+ r_u->ctr->switch_value = level;
+ r_u->ctr->num_entries = num_jn;
+ r_u->ctr->ptr_dfs_ctr = 1;
+
+ r_u->status = init_reply_dfs_ctr(p->mem_ctx, level, r_u->ctr, jn, num_jn);
+
+ return r_u->status;
+}
+
+WERROR _dfs_get_info(pipes_struct *p, DFS_Q_DFS_GET_INFO *q_u,
+ DFS_R_DFS_GET_INFO *r_u)
+{
+ UNISTR2* uni_path = &q_u->uni_path;
+ uint32 level = q_u->level;
+ int consumedcnt = sizeof(pstring);
+ pstring path;
+ struct junction_map jn;
+
+ unistr2_to_ascii(path, uni_path, sizeof(path)-1);
+ if(!create_junction(path, &jn))
+ return WERR_DFS_NO_SUCH_SERVER;
+
+ /* The following call can change the cwd. */
+ if(!get_referred_path(path, &jn, &consumedcnt, NULL) || consumedcnt < strlen(path)) {
+ vfs_ChDir(p->conn,p->conn->connectpath);
+ return WERR_DFS_NO_SUCH_VOL;
+ }
+
+ vfs_ChDir(p->conn,p->conn->connectpath);
+ r_u->level = level;
+ r_u->ptr_ctr = 1;
+ r_u->status = init_reply_dfs_ctr(p->mem_ctx, level, &r_u->ctr, &jn, 1);
+
+ return r_u->status;
+}
diff --git a/source/rpc_server/srv_echo.c b/source/rpc_server/srv_echo.c
new file mode 100644
index 00000000000..c6cfde07c15
--- /dev/null
+++ b/source/rpc_server/srv_echo.c
@@ -0,0 +1,150 @@
+/*
+ * Unix SMB/CIFS implementation.
+ * RPC Pipe client / server routines for rpcecho
+ * 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 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.
+ */
+
+/* This is the interface to the rpcecho pipe. */
+
+#include "includes.h"
+#include "nterr.h"
+
+#ifdef DEVELOPER
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_RPC_SRV
+
+static BOOL api_add_one(pipes_struct *p)
+{
+ ECHO_Q_ADD_ONE q_u;
+ ECHO_R_ADD_ONE r_u;
+
+ prs_struct *data = &p->in_data.data;
+ prs_struct *rdata = &p->out_data.rdata;
+
+ ZERO_STRUCT(q_u);
+ ZERO_STRUCT(r_u);
+
+ if(!echo_io_q_add_one("", &q_u, data, 0))
+ return False;
+
+ _echo_add_one(p, &q_u, &r_u);
+
+ if(!echo_io_r_add_one("", &r_u, rdata, 0))
+ return False;
+
+ return True;
+}
+
+static BOOL api_echo_data(pipes_struct *p)
+{
+ ECHO_Q_ECHO_DATA q_u;
+ ECHO_R_ECHO_DATA r_u;
+
+ prs_struct *data = &p->in_data.data;
+ prs_struct *rdata = &p->out_data.rdata;
+
+ ZERO_STRUCT(q_u);
+ ZERO_STRUCT(r_u);
+
+ if(!echo_io_q_echo_data("", &q_u, data, 0))
+ return False;
+
+ _echo_data(p, &q_u, &r_u);
+
+ if(!echo_io_r_echo_data("", &r_u, rdata, 0))
+ return False;
+
+ return True;
+}
+
+static BOOL api_source_data(pipes_struct *p)
+{
+ ECHO_Q_SOURCE_DATA q_u;
+ ECHO_R_SOURCE_DATA r_u;
+
+ prs_struct *data = &p->in_data.data;
+ prs_struct *rdata = &p->out_data.rdata;
+
+ ZERO_STRUCT(q_u);
+ ZERO_STRUCT(r_u);
+
+ if(!echo_io_q_source_data("", &q_u, data, 0))
+ return False;
+
+ _source_data(p, &q_u, &r_u);
+
+ if(!echo_io_r_source_data("", &r_u, rdata, 0))
+ return False;
+
+ return True;
+}
+
+static BOOL api_sink_data(pipes_struct *p)
+{
+ ECHO_Q_SINK_DATA q_u;
+ ECHO_R_SINK_DATA r_u;
+
+ prs_struct *data = &p->in_data.data;
+ prs_struct *rdata = &p->out_data.rdata;
+
+ ZERO_STRUCT(q_u);
+ ZERO_STRUCT(r_u);
+
+ if(!echo_io_q_sink_data("", &q_u, data, 0))
+ return False;
+
+ _sink_data(p, &q_u, &r_u);
+
+ if(!echo_io_r_sink_data("", &r_u, rdata, 0))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+\pipe\rpcecho commands
+********************************************************************/
+
+struct api_struct api_echo_cmds[] = {
+ {"ADD_ONE", ECHO_ADD_ONE, api_add_one },
+ {"ECHO_DATA", ECHO_DATA, api_echo_data },
+ {"SOURCE_DATA", ECHO_SOURCE_DATA, api_source_data },
+ {"SINK_DATA", ECHO_SINK_DATA, api_sink_data },
+};
+
+
+void echo_get_pipe_fns( struct api_struct **fns, int *n_fns )
+{
+ *fns = api_echo_cmds;
+ *n_fns = sizeof(api_echo_cmds) / sizeof(struct api_struct);
+}
+
+NTSTATUS rpc_echo_init(void)
+{
+ return rpc_pipe_register_commands(SMB_RPC_INTERFACE_VERSION,
+ "rpcecho", "rpcecho", api_echo_cmds,
+ sizeof(api_echo_cmds) / sizeof(struct api_struct));
+}
+
+#else /* DEVELOPER */
+
+NTSTATUS rpc_echo_init(void)
+{
+ return NT_STATUS_OK;
+}
+#endif /* DEVELOPER */
diff --git a/source/rpc_server/srv_echo_nt.c b/source/rpc_server/srv_echo_nt.c
new file mode 100644
index 00000000000..ddb76b3a21e
--- /dev/null
+++ b/source/rpc_server/srv_echo_nt.c
@@ -0,0 +1,78 @@
+/*
+ * Unix SMB/CIFS implementation.
+ * RPC Pipe client / server routines for rpcecho
+ * 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 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.
+ */
+
+/* This is the interface to the rpcecho pipe. */
+
+#include "includes.h"
+#include "nterr.h"
+
+#ifdef DEVELOPER
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_RPC_SRV
+
+/* Add one to the input and return it */
+
+void _echo_add_one(pipes_struct *p, ECHO_Q_ADD_ONE *q_u, ECHO_R_ADD_ONE *r_u)
+{
+ DEBUG(10, ("_echo_add_one\n"));
+
+ r_u->response = q_u->request + 1;
+}
+
+/* Echo back an array of data */
+
+void _echo_data(pipes_struct *p, ECHO_Q_ECHO_DATA *q_u,
+ ECHO_R_ECHO_DATA *r_u)
+{
+ DEBUG(10, ("_echo_data\n"));
+
+ r_u->data = talloc(p->mem_ctx, q_u->size);
+ r_u->size = q_u->size;
+ memcpy(r_u->data, q_u->data, q_u->size);
+}
+
+/* Sink an array of data */
+
+void _sink_data(pipes_struct *p, ECHO_Q_SINK_DATA *q_u,
+ ECHO_R_SINK_DATA *r_u)
+{
+ DEBUG(10, ("_sink_data\n"));
+
+ /* My that was some yummy data! */
+}
+
+/* Source an array of data */
+
+void _source_data(pipes_struct *p, ECHO_Q_SOURCE_DATA *q_u,
+ ECHO_R_SOURCE_DATA *r_u)
+{
+ uint32 i;
+
+ DEBUG(10, ("_source_data\n"));
+
+ r_u->data = talloc(p->mem_ctx, q_u->size);
+ r_u->size = q_u->size;
+
+ for (i = 0; i < r_u->size; i++)
+ r_u->data[i] = i & 0xff;
+}
+
+#endif /* DEVELOPER */
diff --git a/source/rpc_server/srv_epmapper.c b/source/rpc_server/srv_epmapper.c
new file mode 100644
index 00000000000..70de092850b
--- /dev/null
+++ b/source/rpc_server/srv_epmapper.c
@@ -0,0 +1,88 @@
+
+/*
+ Unix SMB/CIFS implementation.
+ Samba end point mapper utility and mapping functions
+ 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 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"
+
+/*****************************************************************
+ api_handle_map_req - handles standard epm mapping request
+******************************************************************/
+static BOOL api_handle_map_req(pipes_struct * p)
+{
+
+ EPM_Q_MAP q_in;
+ EPM_R_MAP q_out;
+
+ prs_struct *in_data = &p->in_data.data;
+ prs_struct *ret_data = &p->out_data.rdata;
+
+ ZERO_STRUCT(q_in);
+ ZERO_STRUCT(q_out);
+
+ /* process input request and parse packet */
+
+ if (!epm_io_q_map("", &q_in, in_data, 0)) {
+ DEBUG(0,
+ ("api_handle_map_request: unable to unmarshall EPMD_MAP\n"));
+ return False;
+ }
+
+ _epm_map(p, &q_in, &q_out);
+
+ if (!epm_io_r_map("", &q_out, ret_data, 0)) {
+ DEBUG(0,
+ ("api_handle_map_req: unable to marshall EPMD_MAP\n"));
+ return False;
+ }
+
+ return True;
+}
+
+/*******************************************************************/
+/* \pipe\epmapper commands */
+/*******************************************************************/
+/* opnum is 3 on map request */
+
+struct api_struct api_epmapper_cmds[] = {
+ {"MAP_PIPE_NAME", EPM_MAP_PIPE_NAME, api_handle_map_req},
+};
+
+/*******************************************************************/
+/* */
+/*******************************************************************/
+
+void epm_get_pipe_fns(struct api_struct **funcs, int *n_funcs)
+{
+ *funcs = api_epmapper_cmds;
+ *n_funcs = sizeof(api_epmapper_cmds) / sizeof(struct api_struct);
+}
+
+/*******************************************************************/
+/* */
+/*******************************************************************/
+
+NTSTATUS rpc_epmapper_init(void)
+{
+ return rpc_pipe_register_commands(SMB_RPC_INTERFACE_VERSION,
+ EPM_PIPE_NM, EPM_PIPE_NM,
+ api_epmapper_cmds,
+ sizeof(api_epmapper_cmds) /
+ sizeof(struct api_struct));
+}
diff --git a/source/rpc_server/srv_epmapper_nt.c b/source/rpc_server/srv_epmapper_nt.c
new file mode 100644
index 00000000000..e82484af4af
--- /dev/null
+++ b/source/rpc_server/srv_epmapper_nt.c
@@ -0,0 +1,70 @@
+
+/*
+ Unix SMB/CIFS implementation.
+ Samba end point mapper utility and mapping functions
+ 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 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"
+
+/*******************************************************************/
+/* _epm_map - fill out mapping on input and output structs */
+/*******************************************************************/
+void _epm_map(pipes_struct *ps, const EPM_Q_MAP *q_u, EPM_R_MAP *r_u)
+{
+ int i;
+ uint8 target_address[] = { 9, 53, 95, 27 };
+ EPM_FLOOR *floors = talloc(ps->mem_ctx, sizeof(EPM_FLOOR) *
+ q_u->tower->num_floors);
+ EPM_TOWER *towers = talloc(ps->mem_ctx,
+ sizeof(EPM_TOWER) * MAX_TOWERS);
+ EPM_TOWER_ARRAY array;
+
+ if (!floors || !towers) {
+ DEBUG(0, ("_epm_map: talloc failed!\n"));
+ return;
+ }
+
+ for (i = 0; i < q_u->tower->num_floors; i++) {
+ switch (q_u->tower->floors[i].lhs.protocol) {
+ case EPM_FLOOR_UUID:
+ init_epm_floor_uuid(&floors[i],
+ q_u->tower->floors[i].
+ lhs.uuid.uuid,
+ q_u->tower->floors[i].
+ lhs.uuid.version);
+ break;
+ case EPM_FLOOR_RPC:
+ init_epm_floor_rpc(&floors[i]);
+ break;
+ case EPM_FLOOR_TCP:
+ /* for now map all requests to port 135 */
+ init_epm_floor_tcp(&floors[i], 135);
+ break;
+ case EPM_FLOOR_IP:
+ init_epm_floor_ip(&floors[i], target_address);
+ break;
+ }
+ }
+
+ init_epm_tower(ps->mem_ctx, &towers[0], floors, 5);
+ init_epm_tower_array(ps->mem_ctx, &array, towers, 1);
+ init_epm_r_map(ps->mem_ctx, r_u, &q_u->term_handle, &array, 1, 0);
+
+ return;
+
+}
diff --git a/source/rpc_server/srv_lsa.c b/source/rpc_server/srv_lsa.c
new file mode 100644
index 00000000000..63e74ec8911
--- /dev/null
+++ b/source/rpc_server/srv_lsa.c
@@ -0,0 +1,734 @@
+/*
+ * Unix SMB/CIFS implementation.
+ * RPC Pipe client / server routines
+ * Copyright (C) Andrew Tridgell 1992-1997,
+ * Copyright (C) Luke Kenneth Casson Leighton 1996-1997,
+ * Copyright (C) Paul Ashton 1997,
+ * Copyright (C) Jeremy Allison 2001,
+ * Copyright (C) Jim McDonough <jmcd@us.ibm.com> 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 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.
+ */
+
+/* This is the interface to the lsa server code. */
+
+#include "includes.h"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_RPC_SRV
+
+/***************************************************************************
+ api_lsa_open_policy2
+ ***************************************************************************/
+
+static BOOL api_lsa_open_policy2(pipes_struct *p)
+{
+ LSA_Q_OPEN_POL2 q_u;
+ LSA_R_OPEN_POL2 r_u;
+ prs_struct *data = &p->in_data.data;
+ prs_struct *rdata = &p->out_data.rdata;
+
+ ZERO_STRUCT(q_u);
+ ZERO_STRUCT(r_u);
+
+ /* grab the server, object attributes and desired access flag...*/
+ if(!lsa_io_q_open_pol2("", &q_u, data, 0)) {
+ DEBUG(0,("api_lsa_open_policy2: unable to unmarshall LSA_Q_OPEN_POL2.\n"));
+ return False;
+ }
+
+ r_u.status = _lsa_open_policy2(p, &q_u, &r_u);
+
+ /* store the response in the SMB stream */
+ if(!lsa_io_r_open_pol2("", &r_u, rdata, 0)) {
+ DEBUG(0,("api_lsa_open_policy2: unable to marshall LSA_R_OPEN_POL2.\n"));
+ return False;
+ }
+
+ return True;
+}
+
+/***************************************************************************
+api_lsa_open_policy
+ ***************************************************************************/
+
+static BOOL api_lsa_open_policy(pipes_struct *p)
+{
+ LSA_Q_OPEN_POL q_u;
+ LSA_R_OPEN_POL r_u;
+ prs_struct *data = &p->in_data.data;
+ prs_struct *rdata = &p->out_data.rdata;
+
+ ZERO_STRUCT(q_u);
+ ZERO_STRUCT(r_u);
+
+ /* grab the server, object attributes and desired access flag...*/
+ if(!lsa_io_q_open_pol("", &q_u, data, 0)) {
+ DEBUG(0,("api_lsa_open_policy: unable to unmarshall LSA_Q_OPEN_POL.\n"));
+ return False;
+ }
+
+ r_u.status = _lsa_open_policy(p, &q_u, &r_u);
+
+ /* store the response in the SMB stream */
+ if(!lsa_io_r_open_pol("", &r_u, rdata, 0)) {
+ DEBUG(0,("api_lsa_open_policy: unable to marshall LSA_R_OPEN_POL.\n"));
+ return False;
+ }
+
+ return True;
+}
+
+/***************************************************************************
+ api_lsa_enum_trust_dom
+ ***************************************************************************/
+
+static BOOL api_lsa_enum_trust_dom(pipes_struct *p)
+{
+ LSA_Q_ENUM_TRUST_DOM q_u;
+ LSA_R_ENUM_TRUST_DOM r_u;
+ prs_struct *data = &p->in_data.data;
+ prs_struct *rdata = &p->out_data.rdata;
+
+ ZERO_STRUCT(q_u);
+ ZERO_STRUCT(r_u);
+
+ /* grab the enum trust domain context etc. */
+ if(!lsa_io_q_enum_trust_dom("", &q_u, data, 0))
+ return False;
+
+ /* get required trusted domains information */
+ r_u.status = _lsa_enum_trust_dom(p, &q_u, &r_u);
+
+ /* prepare the response */
+ if(!lsa_io_r_enum_trust_dom("", &r_u, rdata, 0))
+ return False;
+
+ return True;
+}
+
+/***************************************************************************
+ api_lsa_query_info
+ ***************************************************************************/
+
+static BOOL api_lsa_query_info(pipes_struct *p)
+{
+ LSA_Q_QUERY_INFO q_u;
+ LSA_R_QUERY_INFO r_u;
+ prs_struct *data = &p->in_data.data;
+ prs_struct *rdata = &p->out_data.rdata;
+
+ ZERO_STRUCT(q_u);
+ ZERO_STRUCT(r_u);
+
+ /* grab the info class and policy handle */
+ if(!lsa_io_q_query("", &q_u, data, 0)) {
+ DEBUG(0,("api_lsa_query_info: failed to unmarshall LSA_Q_QUERY_INFO.\n"));
+ return False;
+ }
+
+ r_u.status = _lsa_query_info(p, &q_u, &r_u);
+
+ /* store the response in the SMB stream */
+ if(!lsa_io_r_query("", &r_u, rdata, 0)) {
+ DEBUG(0,("api_lsa_query_info: failed to marshall LSA_R_QUERY_INFO.\n"));
+ return False;
+ }
+
+ return True;
+}
+
+/***************************************************************************
+ api_lsa_lookup_sids
+ ***************************************************************************/
+
+static BOOL api_lsa_lookup_sids(pipes_struct *p)
+{
+ LSA_Q_LOOKUP_SIDS q_u;
+ LSA_R_LOOKUP_SIDS r_u;
+ prs_struct *data = &p->in_data.data;
+ prs_struct *rdata = &p->out_data.rdata;
+
+ ZERO_STRUCT(q_u);
+ ZERO_STRUCT(r_u);
+
+ /* grab the info class and policy handle */
+ if(!lsa_io_q_lookup_sids("", &q_u, data, 0)) {
+ DEBUG(0,("api_lsa_lookup_sids: failed to unmarshall LSA_Q_LOOKUP_SIDS.\n"));
+ return False;
+ }
+
+ r_u.status = _lsa_lookup_sids(p, &q_u, &r_u);
+
+ if(!lsa_io_r_lookup_sids("", &r_u, rdata, 0)) {
+ DEBUG(0,("api_lsa_lookup_sids: Failed to marshall LSA_R_LOOKUP_SIDS.\n"));
+ return False;
+ }
+
+ return True;
+}
+
+/***************************************************************************
+ api_lsa_lookup_names
+ ***************************************************************************/
+
+static BOOL api_lsa_lookup_names(pipes_struct *p)
+{
+ LSA_Q_LOOKUP_NAMES q_u;
+ LSA_R_LOOKUP_NAMES r_u;
+ prs_struct *data = &p->in_data.data;
+ prs_struct *rdata = &p->out_data.rdata;
+
+ ZERO_STRUCT(q_u);
+ ZERO_STRUCT(r_u);
+
+ /* grab the info class and policy handle */
+ if(!lsa_io_q_lookup_names("", &q_u, data, 0)) {
+ DEBUG(0,("api_lsa_lookup_names: failed to unmarshall LSA_Q_LOOKUP_NAMES.\n"));
+ return False;
+ }
+
+ r_u.status = _lsa_lookup_names(p, &q_u, &r_u);
+
+ /* store the response in the SMB stream */
+ if(!lsa_io_r_lookup_names("", &r_u, rdata, 0)) {
+ DEBUG(0,("api_lsa_lookup_names: Failed to marshall LSA_R_LOOKUP_NAMES.\n"));
+ return False;
+ }
+
+ return True;
+}
+
+/***************************************************************************
+ api_lsa_close.
+ ***************************************************************************/
+
+static BOOL api_lsa_close(pipes_struct *p)
+{
+ LSA_Q_CLOSE q_u;
+ LSA_R_CLOSE r_u;
+ prs_struct *data = &p->in_data.data;
+ prs_struct *rdata = &p->out_data.rdata;
+
+ ZERO_STRUCT(q_u);
+ ZERO_STRUCT(r_u);
+
+ if (!lsa_io_q_close("", &q_u, data, 0)) {
+ DEBUG(0,("api_lsa_close: lsa_io_q_close failed.\n"));
+ return False;
+ }
+
+ r_u.status = _lsa_close(p, &q_u, &r_u);
+
+ /* store the response in the SMB stream */
+ if (!lsa_io_r_close("", &r_u, rdata, 0)) {
+ DEBUG(0,("api_lsa_close: lsa_io_r_close failed.\n"));
+ return False;
+ }
+
+ return True;
+}
+
+/***************************************************************************
+ api_lsa_open_secret.
+ ***************************************************************************/
+
+static BOOL api_lsa_open_secret(pipes_struct *p)
+{
+ LSA_Q_OPEN_SECRET q_u;
+ LSA_R_OPEN_SECRET r_u;
+ prs_struct *data = &p->in_data.data;
+ prs_struct *rdata = &p->out_data.rdata;
+
+ ZERO_STRUCT(q_u);
+ ZERO_STRUCT(r_u);
+
+ if(!lsa_io_q_open_secret("", &q_u, data, 0)) {
+ DEBUG(0,("api_lsa_open_secret: failed to unmarshall LSA_Q_OPEN_SECRET.\n"));
+ return False;
+ }
+
+ r_u.status = _lsa_open_secret(p, &q_u, &r_u);
+
+ /* store the response in the SMB stream */
+ if(!lsa_io_r_open_secret("", &r_u, rdata, 0)) {
+ DEBUG(0,("api_lsa_open_secret: Failed to marshall LSA_R_OPEN_SECRET.\n"));
+ return False;
+ }
+
+ return True;
+}
+
+/***************************************************************************
+ api_lsa_open_secret.
+ ***************************************************************************/
+
+static BOOL api_lsa_enum_privs(pipes_struct *p)
+{
+ LSA_Q_ENUM_PRIVS q_u;
+ LSA_R_ENUM_PRIVS r_u;
+ prs_struct *data = &p->in_data.data;
+ prs_struct *rdata = &p->out_data.rdata;
+
+ ZERO_STRUCT(q_u);
+ ZERO_STRUCT(r_u);
+
+ if(!lsa_io_q_enum_privs("", &q_u, data, 0)) {
+ DEBUG(0,("api_lsa_enum_privs: failed to unmarshall LSA_Q_ENUM_PRIVS.\n"));
+ return False;
+ }
+
+ r_u.status = _lsa_enum_privs(p, &q_u, &r_u);
+
+ /* store the response in the SMB stream */
+ if(!lsa_io_r_enum_privs("", &r_u, rdata, 0)) {
+ DEBUG(0,("api_lsa_enum_privs: Failed to marshall LSA_R_ENUM_PRIVS.\n"));
+ return False;
+ }
+
+ return True;
+}
+
+/***************************************************************************
+ api_lsa_open_secret.
+ ***************************************************************************/
+
+static BOOL api_lsa_priv_get_dispname(pipes_struct *p)
+{
+ LSA_Q_PRIV_GET_DISPNAME q_u;
+ LSA_R_PRIV_GET_DISPNAME r_u;
+ prs_struct *data = &p->in_data.data;
+ prs_struct *rdata = &p->out_data.rdata;
+
+ ZERO_STRUCT(q_u);
+ ZERO_STRUCT(r_u);
+
+ if(!lsa_io_q_priv_get_dispname("", &q_u, data, 0)) {
+ DEBUG(0,("api_lsa_priv_get_dispname: failed to unmarshall LSA_Q_PRIV_GET_DISPNAME.\n"));
+ return False;
+ }
+
+ r_u.status = _lsa_priv_get_dispname(p, &q_u, &r_u);
+
+ /* store the response in the SMB stream */
+ if(!lsa_io_r_priv_get_dispname("", &r_u, rdata, 0)) {
+ DEBUG(0,("api_lsa_priv_get_dispname: Failed to marshall LSA_R_PRIV_GET_DISPNAME.\n"));
+ return False;
+ }
+
+ return True;
+}
+
+/***************************************************************************
+ api_lsa_open_secret.
+ ***************************************************************************/
+
+static BOOL api_lsa_enum_accounts(pipes_struct *p)
+{
+ LSA_Q_ENUM_ACCOUNTS q_u;
+ LSA_R_ENUM_ACCOUNTS r_u;
+ prs_struct *data = &p->in_data.data;
+ prs_struct *rdata = &p->out_data.rdata;
+
+ ZERO_STRUCT(q_u);
+ ZERO_STRUCT(r_u);
+
+ if(!lsa_io_q_enum_accounts("", &q_u, data, 0)) {
+ DEBUG(0,("api_lsa_enum_accounts: failed to unmarshall LSA_Q_ENUM_ACCOUNTS.\n"));
+ return False;
+ }
+
+ r_u.status = _lsa_enum_accounts(p, &q_u, &r_u);
+
+ /* store the response in the SMB stream */
+ if(!lsa_io_r_enum_accounts("", &r_u, rdata, 0)) {
+ DEBUG(0,("api_lsa_enum_accounts: Failed to marshall LSA_R_ENUM_ACCOUNTS.\n"));
+ return False;
+ }
+
+ return True;
+}
+
+/***************************************************************************
+ api_lsa_UNK_GET_CONNUSER
+ ***************************************************************************/
+
+static BOOL api_lsa_unk_get_connuser(pipes_struct *p)
+{
+ LSA_Q_UNK_GET_CONNUSER q_u;
+ LSA_R_UNK_GET_CONNUSER r_u;
+
+ prs_struct *data = &p->in_data.data;
+ prs_struct *rdata = &p->out_data.rdata;
+
+ ZERO_STRUCT(q_u);
+ ZERO_STRUCT(r_u);
+
+ if(!lsa_io_q_unk_get_connuser("", &q_u, data, 0)) {
+ DEBUG(0,("api_lsa_unk_get_connuser: failed to unmarshall LSA_Q_UNK_GET_CONNUSER.\n"));
+ return False;
+ }
+
+ r_u.status = _lsa_unk_get_connuser(p, &q_u, &r_u);
+
+ /* store the response in the SMB stream */
+ if(!lsa_io_r_unk_get_connuser("", &r_u, rdata, 0)) {
+ DEBUG(0,("api_lsa_unk_get_connuser: Failed to marshall LSA_R_UNK_GET_CONNUSER.\n"));
+ return False;
+ }
+
+ return True;
+}
+
+/***************************************************************************
+ api_lsa_create_user
+ ***************************************************************************/
+
+static BOOL api_lsa_create_account(pipes_struct *p)
+{
+ LSA_Q_CREATEACCOUNT q_u;
+ LSA_R_CREATEACCOUNT r_u;
+
+ prs_struct *data = &p->in_data.data;
+ prs_struct *rdata = &p->out_data.rdata;
+
+ ZERO_STRUCT(q_u);
+ ZERO_STRUCT(r_u);
+
+ if(!lsa_io_q_create_account("", &q_u, data, 0)) {
+ DEBUG(0,("api_lsa_create_account: failed to unmarshall LSA_Q_CREATEACCOUNT.\n"));
+ return False;
+ }
+
+ r_u.status = _lsa_create_account(p, &q_u, &r_u);
+
+ /* store the response in the SMB stream */
+ if(!lsa_io_r_create_account("", &r_u, rdata, 0)) {
+ DEBUG(0,("api_lsa_create_account: Failed to marshall LSA_R_CREATEACCOUNT.\n"));
+ return False;
+ }
+
+ return True;
+}
+
+/***************************************************************************
+ api_lsa_open_user
+ ***************************************************************************/
+
+static BOOL api_lsa_open_account(pipes_struct *p)
+{
+ LSA_Q_OPENACCOUNT q_u;
+ LSA_R_OPENACCOUNT r_u;
+
+ prs_struct *data = &p->in_data.data;
+ prs_struct *rdata = &p->out_data.rdata;
+
+ ZERO_STRUCT(q_u);
+ ZERO_STRUCT(r_u);
+
+ if(!lsa_io_q_open_account("", &q_u, data, 0)) {
+ DEBUG(0,("api_lsa_open_account: failed to unmarshall LSA_Q_OPENACCOUNT.\n"));
+ return False;
+ }
+
+ r_u.status = _lsa_open_account(p, &q_u, &r_u);
+
+ /* store the response in the SMB stream */
+ if(!lsa_io_r_open_account("", &r_u, rdata, 0)) {
+ DEBUG(0,("api_lsa_open_account: Failed to marshall LSA_R_OPENACCOUNT.\n"));
+ return False;
+ }
+
+ return True;
+}
+
+/***************************************************************************
+ api_lsa_get_privs
+ ***************************************************************************/
+
+static BOOL api_lsa_enum_privsaccount(pipes_struct *p)
+{
+ LSA_Q_ENUMPRIVSACCOUNT q_u;
+ LSA_R_ENUMPRIVSACCOUNT r_u;
+
+ prs_struct *data = &p->in_data.data;
+ prs_struct *rdata = &p->out_data.rdata;
+
+ ZERO_STRUCT(q_u);
+ ZERO_STRUCT(r_u);
+
+ if(!lsa_io_q_enum_privsaccount("", &q_u, data, 0)) {
+ DEBUG(0,("api_lsa_enum_privsaccount: failed to unmarshall LSA_Q_ENUMPRIVSACCOUNT.\n"));
+ return False;
+ }
+
+ r_u.status = _lsa_enum_privsaccount(p, rdata, &q_u, &r_u);
+
+ /* store the response in the SMB stream */
+ if(!lsa_io_r_enum_privsaccount("", &r_u, rdata, 0)) {
+ DEBUG(0,("api_lsa_enum_privsaccount: Failed to marshall LSA_R_ENUMPRIVSACCOUNT.\n"));
+ return False;
+ }
+
+ return True;
+}
+
+/***************************************************************************
+ api_lsa_getsystemaccount
+ ***************************************************************************/
+
+static BOOL api_lsa_getsystemaccount(pipes_struct *p)
+{
+ LSA_Q_GETSYSTEMACCOUNT q_u;
+ LSA_R_GETSYSTEMACCOUNT r_u;
+
+ prs_struct *data = &p->in_data.data;
+ prs_struct *rdata = &p->out_data.rdata;
+
+ ZERO_STRUCT(q_u);
+ ZERO_STRUCT(r_u);
+
+ if(!lsa_io_q_getsystemaccount("", &q_u, data, 0)) {
+ DEBUG(0,("api_lsa_getsystemaccount: failed to unmarshall LSA_Q_GETSYSTEMACCOUNT.\n"));
+ return False;
+ }
+
+ r_u.status = _lsa_getsystemaccount(p, &q_u, &r_u);
+
+ /* store the response in the SMB stream */
+ if(!lsa_io_r_getsystemaccount("", &r_u, rdata, 0)) {
+ DEBUG(0,("api_lsa_getsystemaccount: Failed to marshall LSA_R_GETSYSTEMACCOUNT.\n"));
+ return False;
+ }
+
+ return True;
+}
+
+
+/***************************************************************************
+ api_lsa_setsystemaccount
+ ***************************************************************************/
+
+static BOOL api_lsa_setsystemaccount(pipes_struct *p)
+{
+ LSA_Q_SETSYSTEMACCOUNT q_u;
+ LSA_R_SETSYSTEMACCOUNT r_u;
+
+ prs_struct *data = &p->in_data.data;
+ prs_struct *rdata = &p->out_data.rdata;
+
+ ZERO_STRUCT(q_u);
+ ZERO_STRUCT(r_u);
+
+ if(!lsa_io_q_setsystemaccount("", &q_u, data, 0)) {
+ DEBUG(0,("api_lsa_setsystemaccount: failed to unmarshall LSA_Q_SETSYSTEMACCOUNT.\n"));
+ return False;
+ }
+
+ r_u.status = _lsa_setsystemaccount(p, &q_u, &r_u);
+
+ /* store the response in the SMB stream */
+ if(!lsa_io_r_setsystemaccount("", &r_u, rdata, 0)) {
+ DEBUG(0,("api_lsa_setsystemaccount: Failed to marshall LSA_R_SETSYSTEMACCOUNT.\n"));
+ return False;
+ }
+
+ return True;
+}
+
+/***************************************************************************
+ api_lsa_addprivs
+ ***************************************************************************/
+
+static BOOL api_lsa_addprivs(pipes_struct *p)
+{
+ LSA_Q_ADDPRIVS q_u;
+ LSA_R_ADDPRIVS r_u;
+
+ prs_struct *data = &p->in_data.data;
+ prs_struct *rdata = &p->out_data.rdata;
+
+ ZERO_STRUCT(q_u);
+ ZERO_STRUCT(r_u);
+
+ if(!lsa_io_q_addprivs("", &q_u, data, 0)) {
+ DEBUG(0,("api_lsa_addprivs: failed to unmarshall LSA_Q_ADDPRIVS.\n"));
+ return False;
+ }
+
+ r_u.status = _lsa_addprivs(p, &q_u, &r_u);
+
+ /* store the response in the SMB stream */
+ if(!lsa_io_r_addprivs("", &r_u, rdata, 0)) {
+ DEBUG(0,("api_lsa_addprivs: Failed to marshall LSA_R_ADDPRIVS.\n"));
+ return False;
+ }
+
+ return True;
+}
+
+/***************************************************************************
+ api_lsa_removeprivs
+ ***************************************************************************/
+
+static BOOL api_lsa_removeprivs(pipes_struct *p)
+{
+ LSA_Q_REMOVEPRIVS q_u;
+ LSA_R_REMOVEPRIVS r_u;
+
+ prs_struct *data = &p->in_data.data;
+ prs_struct *rdata = &p->out_data.rdata;
+
+ ZERO_STRUCT(q_u);
+ ZERO_STRUCT(r_u);
+
+ if(!lsa_io_q_removeprivs("", &q_u, data, 0)) {
+ DEBUG(0,("api_lsa_removeprivs: failed to unmarshall LSA_Q_REMOVEPRIVS.\n"));
+ return False;
+ }
+
+ r_u.status = _lsa_removeprivs(p, &q_u, &r_u);
+
+ /* store the response in the SMB stream */
+ if(!lsa_io_r_removeprivs("", &r_u, rdata, 0)) {
+ DEBUG(0,("api_lsa_removeprivs: Failed to marshall LSA_R_REMOVEPRIVS.\n"));
+ return False;
+ }
+
+ return True;
+}
+
+/***************************************************************************
+ api_lsa_query_secobj
+ ***************************************************************************/
+
+static BOOL api_lsa_query_secobj(pipes_struct *p)
+{
+ LSA_Q_QUERY_SEC_OBJ q_u;
+ LSA_R_QUERY_SEC_OBJ r_u;
+
+ prs_struct *data = &p->in_data.data;
+ prs_struct *rdata = &p->out_data.rdata;
+
+ ZERO_STRUCT(q_u);
+ ZERO_STRUCT(r_u);
+
+ if(!lsa_io_q_query_sec_obj("", &q_u, data, 0)) {
+ DEBUG(0,("api_lsa_query_secobj: failed to unmarshall LSA_Q_QUERY_SEC_OBJ.\n"));
+ return False;
+ }
+
+ r_u.status = _lsa_query_secobj(p, &q_u, &r_u);
+
+ /* store the response in the SMB stream */
+ if(!lsa_io_r_query_sec_obj("", &r_u, rdata, 0)) {
+ DEBUG(0,("api_lsa_query_secobj: Failed to marshall LSA_R_QUERY_SEC_OBJ.\n"));
+ return False;
+ }
+
+ return True;
+}
+
+/***************************************************************************
+ api_lsa_query_dnsdomainfo
+ ***************************************************************************/
+
+static BOOL api_lsa_query_info2(pipes_struct *p)
+{
+ LSA_Q_QUERY_INFO2 q_u;
+ LSA_R_QUERY_INFO2 r_u;
+
+ prs_struct *data = &p->in_data.data;
+ prs_struct *rdata = &p->out_data.rdata;
+
+ ZERO_STRUCT(q_u);
+ ZERO_STRUCT(r_u);
+
+ if(!lsa_io_q_query_info2("", &q_u, data, 0)) {
+ DEBUG(0,("api_lsa_query_info2: failed to unmarshall LSA_Q_QUERY_INFO2.\n"));
+ return False;
+ }
+
+ r_u.status = _lsa_query_info2(p, &q_u, &r_u);
+
+ if (!lsa_io_r_query_info2("", &r_u, rdata, 0)) {
+ DEBUG(0,("api_lsa_query_info2: failed to marshall LSA_R_QUERY_INFO2.\n"));
+ return False;
+ }
+
+ return True;
+}
+
+
+/***************************************************************************
+ \PIPE\ntlsa commands
+ ***************************************************************************/
+
+static struct api_struct api_lsa_cmds[] =
+{
+ { "LSA_OPENPOLICY2" , LSA_OPENPOLICY2 , api_lsa_open_policy2 },
+ { "LSA_OPENPOLICY" , LSA_OPENPOLICY , api_lsa_open_policy },
+ { "LSA_QUERYINFOPOLICY" , LSA_QUERYINFOPOLICY , api_lsa_query_info },
+ { "LSA_ENUMTRUSTDOM" , LSA_ENUMTRUSTDOM , api_lsa_enum_trust_dom },
+ { "LSA_CLOSE" , LSA_CLOSE , api_lsa_close },
+ { "LSA_OPENSECRET" , LSA_OPENSECRET , api_lsa_open_secret },
+ { "LSA_LOOKUPSIDS" , LSA_LOOKUPSIDS , api_lsa_lookup_sids },
+ { "LSA_LOOKUPNAMES" , LSA_LOOKUPNAMES , api_lsa_lookup_names },
+ { "LSA_ENUM_PRIVS" , LSA_ENUM_PRIVS , api_lsa_enum_privs },
+ { "LSA_PRIV_GET_DISPNAME",LSA_PRIV_GET_DISPNAME,api_lsa_priv_get_dispname},
+ { "LSA_ENUM_ACCOUNTS" , LSA_ENUM_ACCOUNTS , api_lsa_enum_accounts },
+ { "LSA_UNK_GET_CONNUSER", LSA_UNK_GET_CONNUSER, api_lsa_unk_get_connuser },
+ { "LSA_CREATEACCOUNT" , LSA_CREATEACCOUNT , api_lsa_create_account },
+ { "LSA_OPENACCOUNT" , LSA_OPENACCOUNT , api_lsa_open_account },
+ { "LSA_ENUMPRIVSACCOUNT", LSA_ENUMPRIVSACCOUNT, api_lsa_enum_privsaccount},
+ { "LSA_GETSYSTEMACCOUNT", LSA_GETSYSTEMACCOUNT, api_lsa_getsystemaccount },
+ { "LSA_SETSYSTEMACCOUNT", LSA_SETSYSTEMACCOUNT, api_lsa_setsystemaccount },
+ { "LSA_ADDPRIVS" , LSA_ADDPRIVS , api_lsa_addprivs },
+ { "LSA_REMOVEPRIVS" , LSA_REMOVEPRIVS , api_lsa_removeprivs },
+ { "LSA_QUERYSECOBJ" , LSA_QUERYSECOBJ , api_lsa_query_secobj },
+ /* be careful of the adding of new RPC's. See commentrs below about
+ ADS DC capabilities */
+ { "LSA_QUERYINFO2" , LSA_QUERYINFO2 , api_lsa_query_info2 }
+};
+
+static int count_fns(void)
+{
+ int funcs = sizeof(api_lsa_cmds) / sizeof(struct api_struct);
+
+ /*
+ * NOTE: Certain calls can not be enabled if we aren't an ADS DC. Make sure
+ * these calls are always last and that you decrement by the amount of calls
+ * to disable.
+ */
+ if (!(SEC_ADS == lp_security() && ROLE_DOMAIN_PDC == lp_server_role())) {
+ funcs -= 1;
+ }
+
+ return funcs;
+}
+void lsa_get_pipe_fns( struct api_struct **fns, int *n_fns )
+{
+ *fns = api_lsa_cmds;
+ *n_fns = count_fns();
+}
+
+
+NTSTATUS rpc_lsa_init(void)
+{
+ int funcs = count_fns();
+
+ return rpc_pipe_register_commands(SMB_RPC_INTERFACE_VERSION, "lsarpc", "lsass", api_lsa_cmds,
+ funcs);
+}
diff --git a/source/rpc_server/srv_lsa_ds.c b/source/rpc_server/srv_lsa_ds.c
new file mode 100644
index 00000000000..1e75175c2cd
--- /dev/null
+++ b/source/rpc_server/srv_lsa_ds.c
@@ -0,0 +1,93 @@
+/*
+ * Unix SMB/CIFS implementation.
+ * RPC Pipe client / server routines
+ * Copyright (C) Gerald Carter 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 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.
+ */
+
+/* This is the interface for the registry functions. */
+
+#include "includes.h"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_RPC_SRV
+
+#if 0 /* disabled */
+/*******************************************************************
+ api_reg_open_entry
+ ********************************************************************/
+
+static BOOL api_dsrole_get_primary_dominfo(pipes_struct *p)
+{
+ DS_Q_GETPRIMDOMINFO q_u;
+ DS_R_GETPRIMDOMINFO r_u;
+ prs_struct *data = &p->in_data.data;
+ prs_struct *rdata = &p->out_data.rdata;
+
+ ZERO_STRUCT(q_u);
+ ZERO_STRUCT(r_u);
+
+ /* grab the request */
+ if ( !ds_io_q_getprimdominfo("", data, 0, &q_u) )
+ return False;
+
+ /* construct reply. */
+ r_u.status = _dsrole_get_primary_dominfo( p, &q_u, &r_u );
+
+ if ( !ds_io_r_getprimdominfo("", rdata, 0, &r_u) )
+ return False;
+
+ return True;
+}
+#endif
+
+/*******************************************************************
+ stub functions for unimplemented RPC
+*******************************************************************/
+
+static BOOL api_dsrole_stub( pipes_struct *p )
+{
+ DEBUG(0,("api_dsrole_stub: Hmmm....didn't know this RPC existed...\n"));
+
+ return False;
+}
+
+
+/*******************************************************************
+ array of \PIPE\lsass (new windows 2000 UUID) operations
+********************************************************************/
+static struct api_struct api_lsa_ds_cmds[] = {
+ { "DS_NOP", DS_NOP, api_dsrole_stub }
+
+#if 0 /* disabled due to breakage with viewing domain users and groups
+ on a Samba PDC from win2k clients --jerry CIFS 2003 */
+ { "DS_GETPRIMDOMINFO", DS_GETPRIMDOMINFO, api_dsrole_get_primary_dominfo }
+#endif
+
+};
+
+void lsa_ds_get_pipe_fns( struct api_struct **fns, int *n_fns )
+{
+ *fns = api_lsa_ds_cmds;
+ *n_fns = sizeof(api_lsa_ds_cmds) / sizeof(struct api_struct);
+}
+
+
+NTSTATUS rpc_lsa_ds_init(void)
+{
+ return rpc_pipe_register_commands(SMB_RPC_INTERFACE_VERSION, "lsa_ds", "lsa_ds", api_lsa_ds_cmds,
+ sizeof(api_lsa_ds_cmds) / sizeof(struct api_struct));
+}
diff --git a/source/rpc_server/srv_lsa_ds_nt.c b/source/rpc_server/srv_lsa_ds_nt.c
new file mode 100644
index 00000000000..f6e8eed9a97
--- /dev/null
+++ b/source/rpc_server/srv_lsa_ds_nt.c
@@ -0,0 +1,127 @@
+/*
+ * Unix SMB/CIFS implementation.
+ * RPC Pipe client / server routines
+ * Copyright (C) Andrew Tridgell 1992-1997.
+ * Copyright (C) Luke Kenneth Casson Leighton 1996-1997.
+ * Copyright (C) Paul Ashton 1997.
+ * Copyright (C) Jeremy Allison 2001.
+ * Copyright (C) Gerald Carter 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 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.
+ */
+
+/* Implementation of registry functions. */
+
+#include "includes.h"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_RPC_SRV
+
+/********************************************************************
+ Fill in a DS_DOMINFO_CTR structure
+ ********************************************************************/
+
+static NTSTATUS fill_dsrole_dominfo_basic(TALLOC_CTX *ctx, DSROLE_PRIMARY_DOMAIN_INFO_BASIC **info)
+{
+ DSROLE_PRIMARY_DOMAIN_INFO_BASIC *basic;
+ const char *netbios_domain;
+ fstring dnsdomain;
+
+ DEBUG(10,("fill_dsrole_dominfo_basic: enter\n"));
+
+ if ( !(basic = talloc_zero(ctx, sizeof(DSROLE_PRIMARY_DOMAIN_INFO_BASIC))) ) {
+ DEBUG(0,("fill_dsrole_dominfo_basic: FATAL error! talloc_xero() failed\n"));
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ switch ( lp_server_role() ) {
+ case ROLE_STANDALONE:
+ basic->machine_role = DSROLE_STANDALONE_SRV;
+ break;
+ case ROLE_DOMAIN_MEMBER:
+ basic->machine_role = DSROLE_DOMAIN_MEMBER_SRV;
+ break;
+ case ROLE_DOMAIN_BDC:
+ basic->machine_role = DSROLE_BDC;
+ basic->flags = DSROLE_PRIMARY_DS_RUNNING|DSROLE_PRIMARY_DS_MIXED_MODE;
+ if ( secrets_fetch_domain_guid( lp_workgroup(), &basic->domain_guid ) )
+ basic->flags |= DSROLE_PRIMARY_DOMAIN_GUID_PRESENT;
+ get_mydnsdomname(dnsdomain);
+ strlower_m(dnsdomain);
+ break;
+ case ROLE_DOMAIN_PDC:
+ basic->machine_role = DSROLE_PDC;
+ basic->flags = DSROLE_PRIMARY_DS_RUNNING|DSROLE_PRIMARY_DS_MIXED_MODE;
+ if ( secrets_fetch_domain_guid( lp_workgroup(), &basic->domain_guid ) )
+ basic->flags |= DSROLE_PRIMARY_DOMAIN_GUID_PRESENT;
+ get_mydnsdomname(dnsdomain);
+ strlower_m(dnsdomain);
+ break;
+ }
+
+ basic->unknown = 0x6173; /* seen on the wire; maybe padding */
+
+ /* always set netbios name */
+
+ basic->netbios_ptr = 1;
+ netbios_domain = get_global_sam_name();
+ init_unistr2( &basic->netbios_domain, netbios_domain, UNI_FLAGS_NONE);
+
+ basic->dnsname_ptr = 1;
+ init_unistr2( &basic->dns_domain, dnsdomain, UNI_FLAGS_NONE);
+ basic->forestname_ptr = 1;
+ init_unistr2( &basic->forest_domain, dnsdomain, UNI_FLAGS_NONE);
+
+
+ /* fill in some additional fields if we are a member of an AD domain */
+
+ if ( lp_security() == SEC_ADS ) {
+ /* TODO */
+ ;;
+ }
+
+ *info = basic;
+
+ return NT_STATUS_OK;
+}
+
+/********************************************************************
+ Implement the DsroleGetPrimaryDomainInfo() call
+ ********************************************************************/
+
+NTSTATUS _dsrole_get_primary_dominfo(pipes_struct *p, DS_Q_GETPRIMDOMINFO *q_u, DS_R_GETPRIMDOMINFO *r_u)
+{
+ NTSTATUS result = NT_STATUS_OK;
+ uint32 level = q_u->level;
+
+ switch ( level ) {
+
+ case DsRolePrimaryDomainInfoBasic:
+ r_u->level = DsRolePrimaryDomainInfoBasic;
+ r_u->ptr = 1;
+ result = fill_dsrole_dominfo_basic( p->mem_ctx, &r_u->info.basic );
+ break;
+
+ default:
+ DEBUG(0,("_dsrole_get_primary_dominfo: Unsupported info level [%d]!\n",
+ level));
+ result = NT_STATUS_INVALID_LEVEL;
+ }
+
+ return result;
+}
+
+
+
diff --git a/source/rpc_server/srv_lsa_hnd.c b/source/rpc_server/srv_lsa_hnd.c
new file mode 100644
index 00000000000..2ec62e2c57a
--- /dev/null
+++ b/source/rpc_server/srv_lsa_hnd.c
@@ -0,0 +1,265 @@
+/*
+ * Unix SMB/CIFS implementation.
+ * RPC Pipe client / server routines
+ * Copyright (C) Andrew Tridgell 1992-1997,
+ * Copyright (C) Luke Kenneth Casson Leighton 1996-1997,
+ * Copyright (C) Jeremy Allison 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 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"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_RPC_SRV
+
+/* This is the max handles across all instances of a pipe name. */
+#ifndef MAX_OPEN_POLS
+#define MAX_OPEN_POLS 1024
+#endif
+
+/****************************************************************************
+ Hack as handles need to be persisant over lsa pipe closes so long as a samr
+ pipe is open. JRA.
+****************************************************************************/
+
+static BOOL is_samr_lsa_pipe(const char *pipe_name)
+{
+ return (strstr(pipe_name, "samr") || strstr(pipe_name, "lsa"));
+}
+
+/****************************************************************************
+ Initialise a policy handle list on a pipe. Handle list is shared between all
+ pipes of the same name.
+****************************************************************************/
+
+BOOL init_pipe_handle_list(pipes_struct *p, char *pipe_name)
+{
+ pipes_struct *plist = get_first_internal_pipe();
+ struct handle_list *hl = NULL;
+
+ for (plist = get_first_internal_pipe(); plist; plist = get_next_internal_pipe(plist)) {
+ if (strequal( plist->name, pipe_name) ||
+ (is_samr_lsa_pipe(plist->name) && is_samr_lsa_pipe(pipe_name))) {
+ if (!plist->pipe_handles) {
+ pstring msg;
+ slprintf(msg, sizeof(msg)-1, "init_pipe_handles: NULL pipe_handle pointer in pipe %s",
+ pipe_name );
+ smb_panic(msg);
+ }
+ hl = plist->pipe_handles;
+ break;
+ }
+ }
+
+ if (!hl) {
+ /*
+ * No handle list for this pipe (first open of pipe).
+ * Create list.
+ */
+
+ if ((hl = (struct handle_list *)malloc(sizeof(struct handle_list))) == NULL)
+ return False;
+ ZERO_STRUCTP(hl);
+
+ DEBUG(10,("init_pipe_handles: created handle list for pipe %s\n", pipe_name ));
+ }
+
+ /*
+ * One more pipe is using this list.
+ */
+
+ hl->pipe_ref_count++;
+
+ /*
+ * Point this pipe at this list.
+ */
+
+ p->pipe_handles = hl;
+
+ DEBUG(10,("init_pipe_handles: pipe_handles ref count = %lu for pipe %s\n",
+ (unsigned long)p->pipe_handles->pipe_ref_count, pipe_name ));
+
+ return True;
+}
+
+/****************************************************************************
+ find first available policy slot. creates a policy handle for you.
+****************************************************************************/
+
+BOOL create_policy_hnd(pipes_struct *p, POLICY_HND *hnd, void (*free_fn)(void *), void *data_ptr)
+{
+ static uint32 pol_hnd_low = 0;
+ static uint32 pol_hnd_high = 0;
+
+ struct policy *pol;
+
+ if (p->pipe_handles->count > MAX_OPEN_POLS) {
+ DEBUG(0,("create_policy_hnd: ERROR: too many handles (%d) on this pipe.\n",
+ (int)p->pipe_handles->count));
+ return False;
+ }
+
+ pol = (struct policy *)malloc(sizeof(*p));
+ if (!pol) {
+ DEBUG(0,("create_policy_hnd: ERROR: out of memory!\n"));
+ return False;
+ }
+
+ ZERO_STRUCTP(pol);
+
+ pol->data_ptr = data_ptr;
+ pol->free_fn = free_fn;
+
+ pol_hnd_low++;
+ if (pol_hnd_low == 0)
+ (pol_hnd_high)++;
+
+ SIVAL(&pol->pol_hnd.data1, 0 , 0); /* first bit must be null */
+ SIVAL(&pol->pol_hnd.data2, 0 , pol_hnd_low ); /* second bit is incrementing */
+ SSVAL(&pol->pol_hnd.data3, 0 , pol_hnd_high); /* second bit is incrementing */
+ SSVAL(&pol->pol_hnd.data4, 0 , (pol_hnd_high>>16)); /* second bit is incrementing */
+ SIVAL(pol->pol_hnd.data5, 0, time(NULL)); /* something random */
+ SIVAL(pol->pol_hnd.data5, 4, sys_getpid()); /* something more random */
+
+ DLIST_ADD(p->pipe_handles->Policy, pol);
+ p->pipe_handles->count++;
+
+ *hnd = pol->pol_hnd;
+
+ DEBUG(4,("Opened policy hnd[%d] ", (int)p->pipe_handles->count));
+ dump_data(4, (char *)hnd, sizeof(*hnd));
+
+ return True;
+}
+
+/****************************************************************************
+ find policy by handle - internal version.
+****************************************************************************/
+
+static struct policy *find_policy_by_hnd_internal(pipes_struct *p, POLICY_HND *hnd, void **data_p)
+{
+ struct policy *pol;
+ size_t i;
+
+ if (data_p)
+ *data_p = NULL;
+
+ for (i = 0, pol=p->pipe_handles->Policy;pol;pol=pol->next, i++) {
+ if (memcmp(&pol->pol_hnd, hnd, sizeof(*hnd)) == 0) {
+ DEBUG(4,("Found policy hnd[%d] ", (int)i));
+ dump_data(4, (char *)hnd, sizeof(*hnd));
+ if (data_p)
+ *data_p = pol->data_ptr;
+ return pol;
+ }
+ }
+
+ DEBUG(4,("Policy not found: "));
+ dump_data(4, (char *)hnd, sizeof(*hnd));
+
+ p->bad_handle_fault_state = True;
+
+ return NULL;
+}
+
+/****************************************************************************
+ find policy by handle
+****************************************************************************/
+
+BOOL find_policy_by_hnd(pipes_struct *p, POLICY_HND *hnd, void **data_p)
+{
+ return find_policy_by_hnd_internal(p, hnd, data_p) == NULL ? False : True;
+}
+
+/****************************************************************************
+ Close a policy.
+****************************************************************************/
+
+BOOL close_policy_hnd(pipes_struct *p, POLICY_HND *hnd)
+{
+ struct policy *pol = find_policy_by_hnd_internal(p, hnd, NULL);
+
+ if (!pol) {
+ DEBUG(3,("Error closing policy\n"));
+ return False;
+ }
+
+ DEBUG(3,("Closed policy\n"));
+
+ if (pol->free_fn && pol->data_ptr)
+ (*pol->free_fn)(pol->data_ptr);
+
+ p->pipe_handles->count--;
+
+ DLIST_REMOVE(p->pipe_handles->Policy, pol);
+
+ ZERO_STRUCTP(pol);
+
+ SAFE_FREE(pol);
+
+ return True;
+}
+
+/****************************************************************************
+ Close a pipe - free the handle list if it was the last pipe reference.
+****************************************************************************/
+
+void close_policy_by_pipe(pipes_struct *p)
+{
+ p->pipe_handles->pipe_ref_count--;
+
+ if (p->pipe_handles->pipe_ref_count == 0) {
+ /*
+ * Last pipe open on this list - free the list.
+ */
+ while (p->pipe_handles->Policy)
+ close_policy_hnd(p, &p->pipe_handles->Policy->pol_hnd);
+
+ p->pipe_handles->Policy = NULL;
+ p->pipe_handles->count = 0;
+
+ SAFE_FREE(p->pipe_handles);
+ DEBUG(10,("close_policy_by_pipe: deleted handle list for pipe %s\n", p->name ));
+ }
+}
+
+/*******************************************************************
+Shall we allow access to this rpc? Currently this function
+implements the 'restrict anonymous' setting by denying access to
+anonymous users if the restrict anonymous level is > 0. Further work
+will be checking a security descriptor to determine whether a user
+token has enough access to access the pipe.
+********************************************************************/
+
+BOOL pipe_access_check(pipes_struct *p)
+{
+ /* Don't let anonymous users access this RPC if restrict
+ anonymous > 0 */
+
+ if (lp_restrict_anonymous() > 0) {
+ user_struct *user = get_valid_user_struct(p->vuid);
+
+ if (!user) {
+ DEBUG(3, ("invalid vuid %d\n", p->vuid));
+ return False;
+ }
+
+ if (user->guest)
+ return False;
+ }
+
+ return True;
+}
diff --git a/source/rpc_server/srv_lsa_nt.c b/source/rpc_server/srv_lsa_nt.c
new file mode 100644
index 00000000000..f2fe3235a60
--- /dev/null
+++ b/source/rpc_server/srv_lsa_nt.c
@@ -0,0 +1,1342 @@
+/*
+ * Unix SMB/CIFS implementation.
+ * RPC Pipe client / server routines
+ * Copyright (C) Andrew Tridgell 1992-1997,
+ * Copyright (C) Luke Kenneth Casson Leighton 1996-1997,
+ * Copyright (C) Paul Ashton 1997,
+ * Copyright (C) Jeremy Allison 2001,
+ * Copyright (C) Rafal Szczesniak 2002,
+ * Copyright (C) Jim McDonough <jmcd@us.ibm.com> 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 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.
+ */
+
+/* This is the implementation of the lsa server code. */
+
+#include "includes.h"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_RPC_SRV
+
+extern PRIVS privs[];
+
+struct lsa_info {
+ DOM_SID sid;
+ uint32 access;
+};
+
+struct generic_mapping lsa_generic_mapping = {
+ POLICY_READ,
+ POLICY_WRITE,
+ POLICY_EXECUTE,
+ POLICY_ALL_ACCESS
+};
+
+/*******************************************************************
+ Function to free the per handle data.
+ ********************************************************************/
+
+static void free_lsa_info(void *ptr)
+{
+ struct lsa_info *lsa = (struct lsa_info *)ptr;
+
+ SAFE_FREE(lsa);
+}
+
+/***************************************************************************
+Init dom_query
+ ***************************************************************************/
+
+static void init_dom_query(DOM_QUERY *d_q, const char *dom_name, DOM_SID *dom_sid)
+{
+ d_q->buffer_dom_name = (dom_name != NULL) ? 1 : 0; /* domain buffer pointer */
+ d_q->buffer_dom_sid = (dom_sid != NULL) ? 1 : 0; /* domain sid pointer */
+
+ /* this string is supposed to be non-null terminated. */
+ /* But the maxlen in this UNISTR2 must include the terminating null. */
+ init_unistr2(&d_q->uni_domain_name, dom_name, UNI_BROKEN_NON_NULL);
+
+ /*
+ * I'm not sure why this really odd combination of length
+ * values works, but it does appear to. I need to look at
+ * this *much* more closely - but at the moment leave alone
+ * until it's understood. This allows a W2k client to join
+ * a domain with both odd and even length names... JRA.
+ */
+
+ /*
+ * IMPORTANT NOTE !!!!
+ * The two fields below probably are reversed in meaning, ie.
+ * the first field is probably the str_len, the second the max
+ * len. Both are measured in bytes anyway.
+ */
+
+ d_q->uni_dom_str_len = d_q->uni_domain_name.uni_max_len * 2;
+ d_q->uni_dom_max_len = d_q->uni_domain_name.uni_str_len * 2;
+
+ if (dom_sid != NULL)
+ init_dom_sid2(&d_q->dom_sid, dom_sid);
+}
+
+/***************************************************************************
+ init_dom_ref - adds a domain if it's not already in, returns the index.
+***************************************************************************/
+
+static int init_dom_ref(DOM_R_REF *ref, char *dom_name, DOM_SID *dom_sid)
+{
+ int num = 0;
+
+ if (dom_name != NULL) {
+ for (num = 0; num < ref->num_ref_doms_1; num++) {
+ fstring domname;
+ rpcstr_pull(domname, &ref->ref_dom[num].uni_dom_name, sizeof(domname), -1, 0);
+ if (strequal(domname, dom_name))
+ return num;
+ }
+ } else {
+ num = ref->num_ref_doms_1;
+ }
+
+ if (num >= MAX_REF_DOMAINS) {
+ /* index not found, already at maximum domain limit */
+ return -1;
+ }
+
+ ref->num_ref_doms_1 = num+1;
+ ref->ptr_ref_dom = 1;
+ ref->max_entries = MAX_REF_DOMAINS;
+ ref->num_ref_doms_2 = num+1;
+
+ ref->hdr_ref_dom[num].ptr_dom_sid = dom_sid != NULL ? 1 : 0;
+
+ init_unistr2(&ref->ref_dom[num].uni_dom_name, dom_name, UNI_FLAGS_NONE);
+ init_uni_hdr(&ref->hdr_ref_dom[num].hdr_dom_name, &ref->ref_dom[num].uni_dom_name);
+
+ init_dom_sid2(&ref->ref_dom[num].ref_dom, dom_sid );
+
+ return num;
+}
+
+/***************************************************************************
+ init_lsa_rid2s
+ ***************************************************************************/
+
+static void init_lsa_rid2s(DOM_R_REF *ref, DOM_RID2 *rid2,
+ int num_entries, UNISTR2 *name,
+ uint32 *mapped_count, BOOL endian)
+{
+ int i;
+ int total = 0;
+ *mapped_count = 0;
+
+ SMB_ASSERT(num_entries <= MAX_LOOKUP_SIDS);
+
+ become_root(); /* lookup_name can require root privs */
+
+ for (i = 0; i < num_entries; i++) {
+ BOOL status = False;
+ DOM_SID sid;
+ uint32 rid = 0xffffffff;
+ int dom_idx = -1;
+ pstring full_name;
+ fstring dom_name, user;
+ enum SID_NAME_USE name_type = SID_NAME_UNKNOWN;
+
+ /* Split name into domain and user component */
+
+ unistr2_to_ascii(full_name, &name[i], sizeof(full_name));
+ split_domain_name(full_name, dom_name, user);
+
+ /* Lookup name */
+
+ DEBUG(5, ("init_lsa_rid2s: looking up name %s\n", full_name));
+
+ status = lookup_name(dom_name, user, &sid, &name_type);
+
+ if (name_type == SID_NAME_WKN_GRP) {
+ /* BUILTIN aliases are still aliases :-) */
+ name_type = SID_NAME_ALIAS;
+ }
+
+ DEBUG(5, ("init_lsa_rid2s: %s\n", status ? "found" :
+ "not found"));
+
+ if (status && name_type != SID_NAME_UNKNOWN) {
+ sid_split_rid(&sid, &rid);
+ dom_idx = init_dom_ref(ref, dom_name, &sid);
+ (*mapped_count)++;
+ } else {
+ dom_idx = -1;
+ rid = 0;
+ name_type = SID_NAME_UNKNOWN;
+ }
+
+ init_dom_rid2(&rid2[total], rid, name_type, dom_idx);
+ total++;
+ }
+
+ unbecome_root();
+}
+
+/***************************************************************************
+ init_reply_lookup_names
+ ***************************************************************************/
+
+static void init_reply_lookup_names(LSA_R_LOOKUP_NAMES *r_l,
+ DOM_R_REF *ref, uint32 num_entries,
+ DOM_RID2 *rid2, uint32 mapped_count)
+{
+ r_l->ptr_dom_ref = 1;
+ r_l->dom_ref = ref;
+
+ r_l->num_entries = num_entries;
+ r_l->ptr_entries = 1;
+ r_l->num_entries2 = num_entries;
+ r_l->dom_rid = rid2;
+
+ r_l->mapped_count = mapped_count;
+}
+
+/***************************************************************************
+ Init lsa_trans_names.
+ ***************************************************************************/
+
+static void init_lsa_trans_names(TALLOC_CTX *ctx, DOM_R_REF *ref, LSA_TRANS_NAME_ENUM *trn,
+ int num_entries, DOM_SID2 *sid,
+ uint32 *mapped_count)
+{
+ int i;
+ int total = 0;
+ *mapped_count = 0;
+
+ /* Allocate memory for list of names */
+
+ if (num_entries > 0) {
+ if (!(trn->name = (LSA_TRANS_NAME *)talloc(ctx, sizeof(LSA_TRANS_NAME) *
+ num_entries))) {
+ DEBUG(0, ("init_lsa_trans_names(): out of memory\n"));
+ return;
+ }
+
+ if (!(trn->uni_name = (UNISTR2 *)talloc(ctx, sizeof(UNISTR2) *
+ num_entries))) {
+ DEBUG(0, ("init_lsa_trans_names(): out of memory\n"));
+ return;
+ }
+ }
+
+ become_root(); /* Need root to get to passdb to for local sids */
+
+ for (i = 0; i < num_entries; i++) {
+ BOOL status = False;
+ DOM_SID find_sid = sid[i].sid;
+ uint32 rid = 0xffffffff;
+ int dom_idx = -1;
+ fstring name, dom_name;
+ enum SID_NAME_USE sid_name_use = (enum SID_NAME_USE)0;
+
+ sid_to_string(name, &find_sid);
+ DEBUG(5, ("init_lsa_trans_names: looking up sid %s\n", name));
+
+ /* Lookup sid from winbindd */
+
+ status = lookup_sid(&find_sid, dom_name, name, &sid_name_use);
+
+ DEBUG(5, ("init_lsa_trans_names: %s\n", status ? "found" :
+ "not found"));
+
+ if (!status) {
+ sid_name_use = SID_NAME_UNKNOWN;
+ memset(dom_name, '\0', sizeof(dom_name));
+ sid_to_string(name, &find_sid);
+ dom_idx = -1;
+
+ DEBUG(10,("init_lsa_trans_names: added unknown user '%s' to "
+ "referenced list.\n", name ));
+ } else {
+ (*mapped_count)++;
+ /* Store domain sid in ref array */
+ if (find_sid.num_auths == 5) {
+ sid_split_rid(&find_sid, &rid);
+ }
+ dom_idx = init_dom_ref(ref, dom_name, &find_sid);
+
+ DEBUG(10,("init_lsa_trans_names: added user '%s\\%s' to "
+ "referenced list.\n", dom_name, name ));
+
+ }
+
+ init_lsa_trans_name(&trn->name[total], &trn->uni_name[total],
+ sid_name_use, name, dom_idx);
+ total++;
+ }
+
+ unbecome_root();
+
+ trn->num_entries = total;
+ trn->ptr_trans_names = 1;
+ trn->num_entries2 = total;
+}
+
+/***************************************************************************
+ Init_reply_lookup_sids.
+ ***************************************************************************/
+
+static void init_reply_lookup_sids(LSA_R_LOOKUP_SIDS *r_l,
+ DOM_R_REF *ref, LSA_TRANS_NAME_ENUM *names,
+ uint32 mapped_count)
+{
+ r_l->ptr_dom_ref = 1;
+ r_l->dom_ref = ref;
+ r_l->names = names;
+ r_l->mapped_count = mapped_count;
+}
+
+static NTSTATUS lsa_get_generic_sd(TALLOC_CTX *mem_ctx, SEC_DESC **sd, size_t *sd_size)
+{
+ extern DOM_SID global_sid_World;
+ extern DOM_SID global_sid_Builtin;
+ DOM_SID local_adm_sid;
+ DOM_SID adm_sid;
+
+ SEC_ACE ace[3];
+ SEC_ACCESS mask;
+
+ SEC_ACL *psa = NULL;
+
+ init_sec_access(&mask, POLICY_EXECUTE);
+ init_sec_ace(&ace[0], &global_sid_World, SEC_ACE_TYPE_ACCESS_ALLOWED, mask, 0);
+
+ sid_copy(&adm_sid, get_global_sam_sid());
+ sid_append_rid(&adm_sid, DOMAIN_GROUP_RID_ADMINS);
+ init_sec_access(&mask, POLICY_ALL_ACCESS);
+ init_sec_ace(&ace[1], &adm_sid, SEC_ACE_TYPE_ACCESS_ALLOWED, mask, 0);
+
+ sid_copy(&local_adm_sid, &global_sid_Builtin);
+ sid_append_rid(&local_adm_sid, BUILTIN_ALIAS_RID_ADMINS);
+ init_sec_access(&mask, POLICY_ALL_ACCESS);
+ init_sec_ace(&ace[2], &local_adm_sid, SEC_ACE_TYPE_ACCESS_ALLOWED, mask, 0);
+
+ if((psa = make_sec_acl(mem_ctx, NT4_ACL_REVISION, 3, ace)) == NULL)
+ return NT_STATUS_NO_MEMORY;
+
+ if((*sd = make_sec_desc(mem_ctx, SEC_DESC_REVISION, SEC_DESC_SELF_RELATIVE, &adm_sid, NULL, NULL, psa, sd_size)) == NULL)
+ return NT_STATUS_NO_MEMORY;
+
+ return NT_STATUS_OK;
+}
+
+/***************************************************************************
+ Init_dns_dom_info.
+***************************************************************************/
+
+static void init_dns_dom_info(LSA_DNS_DOM_INFO *r_l, const char *nb_name,
+ const char *dns_name, const char *forest_name,
+ struct uuid *dom_guid, DOM_SID *dom_sid)
+{
+ if (nb_name && *nb_name) {
+ init_unistr2(&r_l->uni_nb_dom_name, nb_name, UNI_FLAGS_NONE);
+ init_uni_hdr(&r_l->hdr_nb_dom_name, &r_l->uni_nb_dom_name);
+ r_l->hdr_nb_dom_name.uni_max_len += 2;
+ r_l->uni_nb_dom_name.uni_max_len += 1;
+ }
+
+ if (dns_name && *dns_name) {
+ init_unistr2(&r_l->uni_dns_dom_name, dns_name, UNI_FLAGS_NONE);
+ init_uni_hdr(&r_l->hdr_dns_dom_name, &r_l->uni_dns_dom_name);
+ r_l->hdr_dns_dom_name.uni_max_len += 2;
+ r_l->uni_dns_dom_name.uni_max_len += 1;
+ }
+
+ if (forest_name && *forest_name) {
+ init_unistr2(&r_l->uni_forest_name, forest_name, UNI_FLAGS_NONE);
+ init_uni_hdr(&r_l->hdr_forest_name, &r_l->uni_forest_name);
+ r_l->hdr_forest_name.uni_max_len += 2;
+ r_l->uni_forest_name.uni_max_len += 1;
+ }
+
+ /* how do we init the guid ? probably should write an init fn */
+ if (dom_guid) {
+ memcpy(&r_l->dom_guid, dom_guid, sizeof(struct uuid));
+ }
+
+ if (dom_sid) {
+ r_l->ptr_dom_sid = 1;
+ init_dom_sid2(&r_l->dom_sid, dom_sid);
+ }
+}
+
+/***************************************************************************
+ _lsa_open_policy2.
+ ***************************************************************************/
+
+NTSTATUS _lsa_open_policy2(pipes_struct *p, LSA_Q_OPEN_POL2 *q_u, LSA_R_OPEN_POL2 *r_u)
+{
+ struct lsa_info *info;
+ SEC_DESC *psd = NULL;
+ size_t sd_size;
+ uint32 des_access=q_u->des_access;
+ uint32 acc_granted;
+ NTSTATUS status;
+
+
+ /* map the generic bits to the lsa policy ones */
+ se_map_generic(&des_access, &lsa_generic_mapping);
+
+ /* get the generic lsa policy SD until we store it */
+ lsa_get_generic_sd(p->mem_ctx, &psd, &sd_size);
+
+ if(!se_access_check(psd, p->pipe_user.nt_user_token, des_access, &acc_granted, &status)) {
+ if (geteuid() != 0) {
+ return status;
+ }
+ DEBUG(4,("ACCESS should be DENIED (granted: %#010x; required: %#010x)\n",
+ acc_granted, des_access));
+ DEBUGADD(4,("but overwritten by euid == 0\n"));
+ }
+
+ /* This is needed for lsa_open_account and rpcclient .... :-) */
+
+ if (geteuid() == 0)
+ acc_granted = POLICY_ALL_ACCESS;
+
+ /* associate the domain SID with the (unique) handle. */
+ if ((info = (struct lsa_info *)malloc(sizeof(struct lsa_info))) == NULL)
+ return NT_STATUS_NO_MEMORY;
+
+ ZERO_STRUCTP(info);
+ sid_copy(&info->sid,get_global_sam_sid());
+ info->access = acc_granted;
+
+ /* set up the LSA QUERY INFO response */
+ if (!create_policy_hnd(p, &r_u->pol, free_lsa_info, (void *)info))
+ return NT_STATUS_OBJECT_NAME_NOT_FOUND;
+
+ return NT_STATUS_OK;
+}
+
+/***************************************************************************
+ _lsa_open_policy
+ ***************************************************************************/
+
+NTSTATUS _lsa_open_policy(pipes_struct *p, LSA_Q_OPEN_POL *q_u, LSA_R_OPEN_POL *r_u)
+{
+ struct lsa_info *info;
+ SEC_DESC *psd = NULL;
+ size_t sd_size;
+ uint32 des_access=q_u->des_access;
+ uint32 acc_granted;
+ NTSTATUS status;
+
+
+ /* map the generic bits to the lsa policy ones */
+ se_map_generic(&des_access, &lsa_generic_mapping);
+
+ /* get the generic lsa policy SD until we store it */
+ lsa_get_generic_sd(p->mem_ctx, &psd, &sd_size);
+
+ if(!se_access_check(psd, p->pipe_user.nt_user_token, des_access, &acc_granted, &status)) {
+ if (geteuid() != 0) {
+ return status;
+ }
+ DEBUG(4,("ACCESS should be DENIED (granted: %#010x; required: %#010x)\n",
+ acc_granted, des_access));
+ DEBUGADD(4,("but overwritten by euid == 0\n"));
+ acc_granted = des_access;
+ }
+
+ /* associate the domain SID with the (unique) handle. */
+ if ((info = (struct lsa_info *)malloc(sizeof(struct lsa_info))) == NULL)
+ return NT_STATUS_NO_MEMORY;
+
+ ZERO_STRUCTP(info);
+ sid_copy(&info->sid,get_global_sam_sid());
+ info->access = acc_granted;
+
+ /* set up the LSA QUERY INFO response */
+ if (!create_policy_hnd(p, &r_u->pol, free_lsa_info, (void *)info))
+ return NT_STATUS_OBJECT_NAME_NOT_FOUND;
+
+ return NT_STATUS_OK;
+}
+
+/***************************************************************************
+ _lsa_enum_trust_dom - this needs fixing to do more than return NULL ! JRA.
+ ufff, done :) mimir
+ ***************************************************************************/
+
+NTSTATUS _lsa_enum_trust_dom(pipes_struct *p, LSA_Q_ENUM_TRUST_DOM *q_u, LSA_R_ENUM_TRUST_DOM *r_u)
+{
+ struct lsa_info *info;
+ uint32 enum_context = q_u->enum_context;
+
+ /*
+ * preferred length is set to 5 as a "our" preferred length
+ * nt sets this parameter to 2
+ * update (20.08.2002): it's not preferred length, but preferred size!
+ * it needs further investigation how to optimally choose this value
+ */
+ uint32 max_num_domains = q_u->preferred_len < 5 ? q_u->preferred_len : 10;
+ TRUSTDOM **trust_doms;
+ uint32 num_domains;
+ NTSTATUS nt_status;
+
+ if (!find_policy_by_hnd(p, &q_u->pol, (void **)&info))
+ return NT_STATUS_INVALID_HANDLE;
+
+ /* check if the user have enough rights */
+ if (!(info->access & POLICY_VIEW_LOCAL_INFORMATION))
+ return NT_STATUS_ACCESS_DENIED;
+
+ nt_status = secrets_get_trusted_domains(p->mem_ctx, (int *)&enum_context, max_num_domains, (int *)&num_domains, &trust_doms);
+
+ if (!NT_STATUS_IS_OK(nt_status) &&
+ !NT_STATUS_EQUAL(nt_status, STATUS_MORE_ENTRIES) &&
+ !NT_STATUS_EQUAL(nt_status, NT_STATUS_NO_MORE_ENTRIES)) {
+ return nt_status;
+ } else {
+ r_u->status = nt_status;
+ }
+
+ /* set up the lsa_enum_trust_dom response */
+ init_r_enum_trust_dom(p->mem_ctx, r_u, enum_context, max_num_domains, num_domains, trust_doms);
+
+ return r_u->status;
+}
+
+/***************************************************************************
+ _lsa_query_info. See the POLICY_INFOMATION_CLASS docs at msdn.
+ ***************************************************************************/
+
+NTSTATUS _lsa_query_info(pipes_struct *p, LSA_Q_QUERY_INFO *q_u, LSA_R_QUERY_INFO *r_u)
+{
+ struct lsa_info *handle;
+ LSA_INFO_UNION *info = &r_u->dom;
+ DOM_SID domain_sid;
+ const char *name;
+ DOM_SID *sid = NULL;
+
+ r_u->status = NT_STATUS_OK;
+
+ if (!find_policy_by_hnd(p, &q_u->pol, (void **)&handle))
+ return NT_STATUS_INVALID_HANDLE;
+
+ switch (q_u->info_class) {
+ case 0x02:
+ {
+ unsigned int i;
+ /* check if the user have enough rights */
+ if (!(handle->access & POLICY_VIEW_AUDIT_INFORMATION))
+ return NT_STATUS_ACCESS_DENIED;
+
+ /* fake info: We audit everything. ;) */
+ info->id2.auditing_enabled = 1;
+ info->id2.count1 = 7;
+ info->id2.count2 = 7;
+ if ((info->id2.auditsettings = (uint32 *)talloc(p->mem_ctx,7*sizeof(uint32))) == NULL)
+ return NT_STATUS_NO_MEMORY;
+ for (i = 0; i < 7; i++)
+ info->id2.auditsettings[i] = 3;
+ break;
+ }
+ case 0x03:
+ /* check if the user have enough rights */
+ if (!(handle->access & POLICY_VIEW_LOCAL_INFORMATION))
+ return NT_STATUS_ACCESS_DENIED;
+
+ /* Request PolicyPrimaryDomainInformation. */
+ switch (lp_server_role()) {
+ case ROLE_DOMAIN_PDC:
+ case ROLE_DOMAIN_BDC:
+ name = get_global_sam_name();
+ sid = get_global_sam_sid();
+ break;
+ case ROLE_DOMAIN_MEMBER:
+ name = lp_workgroup();
+ /* We need to return the Domain SID here. */
+ if (secrets_fetch_domain_sid(lp_workgroup(), &domain_sid))
+ sid = &domain_sid;
+ else
+ return NT_STATUS_CANT_ACCESS_DOMAIN_INFO;
+ break;
+ case ROLE_STANDALONE:
+ name = lp_workgroup();
+ sid = NULL;
+ break;
+ default:
+ return NT_STATUS_CANT_ACCESS_DOMAIN_INFO;
+ }
+ init_dom_query(&r_u->dom.id3, name, sid);
+ break;
+ case 0x05:
+ /* check if the user have enough rights */
+ if (!(handle->access & POLICY_VIEW_LOCAL_INFORMATION))
+ return NT_STATUS_ACCESS_DENIED;
+
+ /* Request PolicyAccountDomainInformation. */
+ name = get_global_sam_name();
+ sid = get_global_sam_sid();
+ init_dom_query(&r_u->dom.id5, name, sid);
+ break;
+ case 0x06:
+ /* check if the user have enough rights */
+ if (!(handle->access & POLICY_VIEW_LOCAL_INFORMATION))
+ return NT_STATUS_ACCESS_DENIED;
+
+ switch (lp_server_role()) {
+ case ROLE_DOMAIN_BDC:
+ /*
+ * only a BDC is a backup controller
+ * of the domain, it controls.
+ */
+ info->id6.server_role = 2;
+ break;
+ default:
+ /*
+ * any other role is a primary
+ * of the domain, it controls.
+ */
+ info->id6.server_role = 3;
+ break;
+ }
+ break;
+ default:
+ DEBUG(0,("_lsa_query_info: unknown info level in Lsa Query: %d\n", q_u->info_class));
+ r_u->status = NT_STATUS_INVALID_INFO_CLASS;
+ break;
+ }
+
+ if (NT_STATUS_IS_OK(r_u->status)) {
+ r_u->undoc_buffer = 0x22000000; /* bizarre */
+ r_u->info_class = q_u->info_class;
+ }
+
+ return r_u->status;
+}
+
+/***************************************************************************
+ _lsa_lookup_sids
+ ***************************************************************************/
+
+NTSTATUS _lsa_lookup_sids(pipes_struct *p, LSA_Q_LOOKUP_SIDS *q_u, LSA_R_LOOKUP_SIDS *r_u)
+{
+ struct lsa_info *handle;
+ DOM_SID2 *sid = q_u->sids.sid;
+ int num_entries = q_u->sids.num_entries;
+ DOM_R_REF *ref = NULL;
+ LSA_TRANS_NAME_ENUM *names = NULL;
+ uint32 mapped_count = 0;
+
+ if (num_entries > MAX_LOOKUP_SIDS) {
+ num_entries = MAX_LOOKUP_SIDS;
+ DEBUG(5,("_lsa_lookup_sids: truncating SID lookup list to %d\n", num_entries));
+ }
+
+ ref = (DOM_R_REF *)talloc_zero(p->mem_ctx, sizeof(DOM_R_REF));
+ names = (LSA_TRANS_NAME_ENUM *)talloc_zero(p->mem_ctx, sizeof(LSA_TRANS_NAME_ENUM));
+
+ if (!find_policy_by_hnd(p, &q_u->pol, (void **)&handle)) {
+ r_u->status = NT_STATUS_INVALID_HANDLE;
+ goto done;
+ }
+
+ /* check if the user have enough rights */
+ if (!(handle->access & POLICY_LOOKUP_NAMES)) {
+ r_u->status = NT_STATUS_ACCESS_DENIED;
+ goto done;
+ }
+ if (!ref || !names)
+ return NT_STATUS_NO_MEMORY;
+
+done:
+
+ /* set up the LSA Lookup SIDs response */
+ init_lsa_trans_names(p->mem_ctx, ref, names, num_entries, sid, &mapped_count);
+ if (mapped_count == 0)
+ r_u->status = NT_STATUS_NONE_MAPPED;
+ else if (mapped_count != num_entries)
+ r_u->status = STATUS_SOME_UNMAPPED;
+ else
+ r_u->status = NT_STATUS_OK;
+ init_reply_lookup_sids(r_u, ref, names, mapped_count);
+
+ return r_u->status;
+}
+
+/***************************************************************************
+lsa_reply_lookup_names
+ ***************************************************************************/
+
+NTSTATUS _lsa_lookup_names(pipes_struct *p,LSA_Q_LOOKUP_NAMES *q_u, LSA_R_LOOKUP_NAMES *r_u)
+{
+ struct lsa_info *handle;
+ UNISTR2 *names = q_u->uni_name;
+ int num_entries = q_u->num_entries;
+ DOM_R_REF *ref;
+ DOM_RID2 *rids;
+ uint32 mapped_count = 0;
+
+ if (num_entries > MAX_LOOKUP_SIDS) {
+ num_entries = MAX_LOOKUP_SIDS;
+ DEBUG(5,("_lsa_lookup_names: truncating name lookup list to %d\n", num_entries));
+ }
+
+ ref = (DOM_R_REF *)talloc_zero(p->mem_ctx, sizeof(DOM_R_REF));
+ rids = (DOM_RID2 *)talloc_zero(p->mem_ctx, sizeof(DOM_RID2)*num_entries);
+
+ if (!find_policy_by_hnd(p, &q_u->pol, (void **)&handle)) {
+ r_u->status = NT_STATUS_INVALID_HANDLE;
+ goto done;
+ }
+
+ /* check if the user have enough rights */
+ if (!(handle->access & POLICY_LOOKUP_NAMES)) {
+ r_u->status = NT_STATUS_ACCESS_DENIED;
+ goto done;
+ }
+
+ if (!ref || !rids)
+ return NT_STATUS_NO_MEMORY;
+
+done:
+
+ /* set up the LSA Lookup RIDs response */
+ init_lsa_rid2s(ref, rids, num_entries, names, &mapped_count, p->endian);
+ if (mapped_count == 0)
+ r_u->status = NT_STATUS_NONE_MAPPED;
+ else if (mapped_count != num_entries)
+ r_u->status = STATUS_SOME_UNMAPPED;
+ else
+ r_u->status = NT_STATUS_OK;
+ init_reply_lookup_names(r_u, ref, num_entries, rids, mapped_count);
+
+ return r_u->status;
+}
+
+/***************************************************************************
+ _lsa_close. Also weird - needs to check if lsa handle is correct. JRA.
+ ***************************************************************************/
+
+NTSTATUS _lsa_close(pipes_struct *p, LSA_Q_CLOSE *q_u, LSA_R_CLOSE *r_u)
+{
+ if (!find_policy_by_hnd(p, &q_u->pol, NULL))
+ return NT_STATUS_INVALID_HANDLE;
+
+ close_policy_hnd(p, &q_u->pol);
+ return NT_STATUS_OK;
+}
+
+/***************************************************************************
+ "No more secrets Marty...." :-).
+ ***************************************************************************/
+
+NTSTATUS _lsa_open_secret(pipes_struct *p, LSA_Q_OPEN_SECRET *q_u, LSA_R_OPEN_SECRET *r_u)
+{
+ return NT_STATUS_OBJECT_NAME_NOT_FOUND;
+}
+
+/***************************************************************************
+_lsa_enum_privs.
+ ***************************************************************************/
+
+NTSTATUS _lsa_enum_privs(pipes_struct *p, LSA_Q_ENUM_PRIVS *q_u, LSA_R_ENUM_PRIVS *r_u)
+{
+ struct lsa_info *handle;
+ uint32 i;
+
+ uint32 enum_context=q_u->enum_context;
+ LSA_PRIV_ENTRY *entry;
+ LSA_PRIV_ENTRY *entries=NULL;
+
+ if (enum_context >= PRIV_ALL_INDEX-2)
+ return NT_STATUS_NO_MORE_ENTRIES;
+
+ entries = (LSA_PRIV_ENTRY *)talloc_zero(p->mem_ctx, sizeof(LSA_PRIV_ENTRY) * (PRIV_ALL_INDEX));
+ if (entries==NULL)
+ return NT_STATUS_NO_MEMORY;
+
+ if (!find_policy_by_hnd(p, &q_u->pol, (void **)&handle))
+ return NT_STATUS_INVALID_HANDLE;
+
+ /* check if the user have enough rights */
+
+ /*
+ * I don't know if it's the right one. not documented.
+ */
+ if (!(handle->access & POLICY_VIEW_LOCAL_INFORMATION))
+ return NT_STATUS_ACCESS_DENIED;
+
+ entry = entries;
+
+ DEBUG(10,("_lsa_enum_privs: enum_context:%d total entries:%d\n", enum_context, PRIV_ALL_INDEX));
+
+ for (i = 1; i < PRIV_ALL_INDEX-1; i++, entry++) {
+ if( i<enum_context) {
+ init_unistr2(&entry->name, NULL, UNI_FLAGS_NONE);
+ init_uni_hdr(&entry->hdr_name, &entry->name);
+ entry->luid_low = 0;
+ entry->luid_high = 0;
+ } else {
+ init_unistr2(&entry->name, privs[i].priv, UNI_FLAGS_NONE);
+ init_uni_hdr(&entry->hdr_name, &entry->name);
+ entry->luid_low = privs[i].se_priv;
+ entry->luid_high = 0;
+ }
+ }
+
+ enum_context = PRIV_ALL_INDEX-2;
+ init_lsa_r_enum_privs(r_u, enum_context, PRIV_ALL_INDEX-2, entries);
+
+ return NT_STATUS_OK;
+}
+
+/***************************************************************************
+_lsa_priv_get_dispname.
+ ***************************************************************************/
+
+NTSTATUS _lsa_priv_get_dispname(pipes_struct *p, LSA_Q_PRIV_GET_DISPNAME *q_u, LSA_R_PRIV_GET_DISPNAME *r_u)
+{
+ struct lsa_info *handle;
+ fstring name_asc;
+ int i=1;
+
+ if (!find_policy_by_hnd(p, &q_u->pol, (void **)&handle))
+ return NT_STATUS_INVALID_HANDLE;
+
+ /* check if the user have enough rights */
+
+ /*
+ * I don't know if it's the right one. not documented.
+ */
+ if (!(handle->access & POLICY_VIEW_LOCAL_INFORMATION))
+ return NT_STATUS_ACCESS_DENIED;
+
+ unistr2_to_ascii(name_asc, &q_u->name, sizeof(name_asc));
+
+ DEBUG(10,("_lsa_priv_get_dispname: %s", name_asc));
+
+ while (privs[i].se_priv!=SE_ALL_PRIVS && strcmp(name_asc, privs[i].priv))
+ i++;
+
+ if (privs[i].se_priv!=SE_ALL_PRIVS) {
+ DEBUG(10,(": %s\n", privs[i].description));
+ init_unistr2(&r_u->desc, privs[i].description, UNI_FLAGS_NONE);
+ init_uni_hdr(&r_u->hdr_desc, &r_u->desc);
+
+ r_u->ptr_info=0xdeadbeef;
+ r_u->lang_id=q_u->lang_id;
+ return NT_STATUS_OK;
+ } else {
+ DEBUG(10,("_lsa_priv_get_dispname: doesn't exist\n"));
+ r_u->ptr_info=0;
+ return NT_STATUS_NO_SUCH_PRIVILEGE;
+ }
+}
+
+/***************************************************************************
+_lsa_enum_accounts.
+ ***************************************************************************/
+
+NTSTATUS _lsa_enum_accounts(pipes_struct *p, LSA_Q_ENUM_ACCOUNTS *q_u, LSA_R_ENUM_ACCOUNTS *r_u)
+{
+ struct lsa_info *handle;
+ DOM_SID *sid_list;
+ int i, j, num_entries;
+ LSA_SID_ENUM *sids=&r_u->sids;
+
+ if (!find_policy_by_hnd(p, &q_u->pol, (void **)&handle))
+ return NT_STATUS_INVALID_HANDLE;
+
+ if (!(handle->access & POLICY_VIEW_LOCAL_INFORMATION))
+ return NT_STATUS_ACCESS_DENIED;
+
+ sid_list = NULL;
+ num_entries = 0;
+
+ /* The only way we can currently find out all the SIDs that have been
+ privileged is to scan all privileges */
+
+ for (i=1; i<PRIV_ALL_INDEX-1; i++) {
+ DOM_SID *priv_sids = NULL;
+ int num_priv_sids = 0;
+
+ if (!get_sids_from_priv(privs[i].priv, &priv_sids,
+ &num_priv_sids))
+ continue;
+
+ for (j=0; j<num_priv_sids; j++) {
+ add_sid_to_array_unique(&priv_sids[j], &sid_list,
+ &num_entries);
+ }
+ SAFE_FREE(priv_sids);
+ }
+
+ if (q_u->enum_context >= num_entries)
+ return NT_STATUS_NO_MORE_ENTRIES;
+
+ sids->ptr_sid = (uint32 *)talloc_zero(p->mem_ctx, (num_entries-q_u->enum_context)*sizeof(uint32));
+ sids->sid = (DOM_SID2 *)talloc_zero(p->mem_ctx, (num_entries-q_u->enum_context)*sizeof(DOM_SID2));
+
+ if (sids->ptr_sid==NULL || sids->sid==NULL) {
+ SAFE_FREE(sid_list);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ for (i=q_u->enum_context, j=0; i<num_entries; i++) {
+ init_dom_sid2( &(*sids).sid[j], &sid_list[i]);
+ (*sids).ptr_sid[j]=1;
+ j++;
+ }
+
+ SAFE_FREE(sid_list);
+
+ init_lsa_r_enum_accounts(r_u, num_entries);
+
+ return NT_STATUS_OK;
+}
+
+
+NTSTATUS _lsa_unk_get_connuser(pipes_struct *p, LSA_Q_UNK_GET_CONNUSER *q_u, LSA_R_UNK_GET_CONNUSER *r_u)
+{
+ fstring username, domname;
+ user_struct *vuser = get_valid_user_struct(p->vuid);
+
+ if (vuser == NULL)
+ return NT_STATUS_CANT_ACCESS_DOMAIN_INFO;
+
+ fstrcpy(username, vuser->user.smb_name);
+ fstrcpy(domname, vuser->user.domain);
+
+ r_u->ptr_user_name = 1;
+ init_unistr2(&r_u->uni2_user_name, username, UNI_STR_TERMINATE);
+ init_uni_hdr(&r_u->hdr_user_name, &r_u->uni2_user_name);
+
+ r_u->unk1 = 1;
+
+ r_u->ptr_dom_name = 1;
+ init_unistr2(&r_u->uni2_dom_name, domname, UNI_STR_TERMINATE);
+ init_uni_hdr(&r_u->hdr_dom_name, &r_u->uni2_dom_name);
+
+ r_u->status = NT_STATUS_OK;
+
+ return r_u->status;
+}
+
+/***************************************************************************
+ Lsa Create Account
+
+ FIXME: Actually the code is just a copy of lsa_open_account
+ TODO: Check and code what this function should exactly do
+ ***************************************************************************/
+
+NTSTATUS _lsa_create_account(pipes_struct *p, LSA_Q_CREATEACCOUNT *q_u, LSA_R_CREATEACCOUNT *r_u)
+{
+ struct lsa_info *handle;
+ struct lsa_info *info;
+
+ r_u->status = NT_STATUS_OK;
+
+ /* find the connection policy handle. */
+ if (!find_policy_by_hnd(p, &q_u->pol, (void **)&handle))
+ return NT_STATUS_INVALID_HANDLE;
+
+ /* check if the user have enough rights */
+
+ /*
+ * I don't know if it's the right one. not documented.
+ * but guessed with rpcclient.
+ */
+ if (!(handle->access & POLICY_GET_PRIVATE_INFORMATION))
+ return NT_STATUS_ACCESS_DENIED;
+
+ /* associate the user/group SID with the (unique) handle. */
+ if ((info = (struct lsa_info *)malloc(sizeof(struct lsa_info))) == NULL)
+ return NT_STATUS_NO_MEMORY;
+
+ ZERO_STRUCTP(info);
+ info->sid = q_u->sid.sid;
+ info->access = q_u->access;
+
+ /* get a (unique) handle. open a policy on it. */
+ if (!create_policy_hnd(p, &r_u->pol, free_lsa_info, (void *)info))
+ return NT_STATUS_OBJECT_NAME_NOT_FOUND;
+
+ return r_u->status;
+}
+
+
+/***************************************************************************
+ Lsa Open Account
+ ***************************************************************************/
+
+NTSTATUS _lsa_open_account(pipes_struct *p, LSA_Q_OPENACCOUNT *q_u, LSA_R_OPENACCOUNT *r_u)
+{
+ struct lsa_info *handle;
+ struct lsa_info *info;
+
+ r_u->status = NT_STATUS_OK;
+
+ /* find the connection policy handle. */
+ if (!find_policy_by_hnd(p, &q_u->pol, (void **)&handle))
+ return NT_STATUS_INVALID_HANDLE;
+
+ /* check if the user have enough rights */
+
+ /*
+ * I don't know if it's the right one. not documented.
+ * but guessed with rpcclient.
+ */
+ if (!(handle->access & POLICY_GET_PRIVATE_INFORMATION))
+ return NT_STATUS_ACCESS_DENIED;
+
+ /* associate the user/group SID with the (unique) handle. */
+ if ((info = (struct lsa_info *)malloc(sizeof(struct lsa_info))) == NULL)
+ return NT_STATUS_NO_MEMORY;
+
+ ZERO_STRUCTP(info);
+ info->sid = q_u->sid.sid;
+ info->access = q_u->access;
+
+ /* get a (unique) handle. open a policy on it. */
+ if (!create_policy_hnd(p, &r_u->pol, free_lsa_info, (void *)info))
+ return NT_STATUS_OBJECT_NAME_NOT_FOUND;
+
+ return r_u->status;
+}
+
+/***************************************************************************
+ For a given SID, enumerate all the privilege this account has.
+ ***************************************************************************/
+
+NTSTATUS _lsa_enum_privsaccount(pipes_struct *p, prs_struct *ps, LSA_Q_ENUMPRIVSACCOUNT *q_u, LSA_R_ENUMPRIVSACCOUNT *r_u)
+{
+ struct lsa_info *info=NULL;
+ LUID_ATTR *set=NULL;
+ PRIVILEGE_SET *priv;
+
+ r_u->status = NT_STATUS_OK;
+
+ /* find the connection policy handle. */
+ if (!find_policy_by_hnd(p, &q_u->pol, (void **)&info))
+ return NT_STATUS_INVALID_HANDLE;
+
+ init_privilege(&priv);
+
+ if (!get_priv_for_sid(&info->sid, priv)) {
+ /* This is probably wrong... */
+ return NT_STATUS_INVALID_HANDLE;
+ }
+
+ DEBUG(10,("_lsa_enum_privsaccount: %d privileges\n", priv->count));
+
+ if (priv->count > 0) {
+ int i;
+ set=(LUID_ATTR *)talloc(ps->mem_ctx,
+ priv->count*sizeof(LUID_ATTR));
+ if (set == NULL) {
+ destroy_privilege(&priv);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ for (i = 0; i < priv->count; i++) {
+ set[i].luid.low = priv->set[i].luid.low;
+ set[i].luid.high = priv->set[i].luid.high;
+ set[i].attr = priv->set[i].attr;
+ DEBUG(10,("_lsa_enum_privsaccount: %d: %d:%d:%d\n", i,
+ set[i].luid.high, set[i].luid.low,
+ set[i].attr));
+ }
+ }
+
+ init_lsa_r_enum_privsaccount(ps->mem_ctx, r_u, set, priv->count, 0);
+ destroy_privilege(&priv);
+
+ return r_u->status;
+}
+
+/***************************************************************************
+
+ ***************************************************************************/
+
+NTSTATUS _lsa_getsystemaccount(pipes_struct *p, LSA_Q_GETSYSTEMACCOUNT *q_u, LSA_R_GETSYSTEMACCOUNT *r_u)
+{
+ struct lsa_info *info=NULL;
+ r_u->status = NT_STATUS_OK;
+ fstring name, dom_name;
+ enum SID_NAME_USE type;
+
+ /* find the connection policy handle. */
+ if (!find_policy_by_hnd(p, &q_u->pol, (void **)&info))
+ return NT_STATUS_INVALID_HANDLE;
+
+ if (!lookup_sid(&info->sid, dom_name, name, &type))
+ return NT_STATUS_INVALID_HANDLE;
+
+ /*
+ 0x01 -> Log on locally
+ 0x02 -> Access this computer from network
+ 0x04 -> Log on as a batch job
+ 0x10 -> Log on as a service
+
+ they can be ORed together
+ */
+
+ r_u->access = PR_LOG_ON_LOCALLY | PR_ACCESS_FROM_NETWORK;
+
+ return r_u->status;
+}
+
+/***************************************************************************
+ update the systemaccount information
+ ***************************************************************************/
+
+NTSTATUS _lsa_setsystemaccount(pipes_struct *p, LSA_Q_SETSYSTEMACCOUNT *q_u, LSA_R_SETSYSTEMACCOUNT *r_u)
+{
+ struct lsa_info *info=NULL;
+ GROUP_MAP map;
+ r_u->status = NT_STATUS_OK;
+
+ /* find the connection policy handle. */
+ if (!find_policy_by_hnd(p, &q_u->pol, (void **)&info))
+ return NT_STATUS_INVALID_HANDLE;
+
+ if (!pdb_getgrsid(&map, info->sid))
+ return NT_STATUS_NO_SUCH_GROUP;
+
+ if(!pdb_update_group_mapping_entry(&map))
+ return NT_STATUS_NO_SUCH_GROUP;
+
+ return r_u->status;
+}
+
+/***************************************************************************
+ For a given SID, add some privileges.
+ ***************************************************************************/
+
+NTSTATUS _lsa_addprivs(pipes_struct *p, LSA_Q_ADDPRIVS *q_u, LSA_R_ADDPRIVS *r_u)
+{
+#if 0
+ struct lsa_info *info = NULL;
+ GROUP_MAP map;
+ int i = 0;
+ LUID_ATTR *luid_attr = NULL;
+ PRIVILEGE_SET *set = NULL;
+#endif
+
+ r_u->status = NT_STATUS_OK;
+
+#if 0 /* privileges are not implemented */
+ /* find the connection policy handle. */
+ if (!find_policy_by_hnd(p, &q_u->pol, (void **)&info))
+ return NT_STATUS_INVALID_HANDLE;
+
+ if (!pdb_getgrsid(&map, info->sid))
+ return NT_STATUS_NO_SUCH_GROUP;
+
+ set = &q_u->set;
+
+ for (i = 0; i < set->count; i++) {
+ luid_attr = &set->set[i];
+
+ /* check if the privilege is already there */
+ if (check_priv_in_privilege(map.priv_set, *luid_attr)){
+ destroy_privilege(&map.priv_set);
+ return NT_STATUS_NO_SUCH_PRIVILEGE;
+ }
+
+ add_privilege(map.priv_set, *luid_attr);
+ }
+
+ if(!pdb_update_group_mapping_entry(&map))
+ return NT_STATUS_NO_SUCH_GROUP;
+
+ destroy_privilege(&map.priv_set);
+
+#endif
+ return r_u->status;
+}
+
+/***************************************************************************
+ For a given SID, remove some privileges.
+ ***************************************************************************/
+
+NTSTATUS _lsa_removeprivs(pipes_struct *p, LSA_Q_REMOVEPRIVS *q_u, LSA_R_REMOVEPRIVS *r_u)
+{
+#if 0
+ struct lsa_info *info = NULL;
+ GROUP_MAP map;
+ int i=0;
+ LUID_ATTR *luid_attr = NULL;
+ PRIVILEGE_SET *set = NULL;
+#endif
+
+ r_u->status = NT_STATUS_OK;
+
+#if 0 /* privileges are not implemented */
+ /* find the connection policy handle. */
+ if (!find_policy_by_hnd(p, &q_u->pol, (void **)&info))
+ return NT_STATUS_INVALID_HANDLE;
+
+ if (!pdb_getgrsid(&map, info->sid))
+ return NT_STATUS_NO_SUCH_GROUP;
+
+ if (q_u->allrights != 0) {
+ /* log it and return, until I see one myself don't do anything */
+ DEBUG(5,("_lsa_removeprivs: trying to remove all privileges ?\n"));
+ return NT_STATUS_OK;
+ }
+
+ if (q_u->ptr == 0) {
+ /* log it and return, until I see one myself don't do anything */
+ DEBUG(5,("_lsa_removeprivs: no privileges to remove ?\n"));
+ return NT_STATUS_OK;
+ }
+
+ set = &q_u->set;
+
+ for (i = 0; i < set->count; i++) {
+ luid_attr = &set->set[i];
+
+ /* if we don't have the privilege, we're trying to remove, give up */
+ /* what else can we do ??? JFM. */
+ if (!check_priv_in_privilege(map.priv_set, *luid_attr)){
+ destroy_privilege(&map.priv_set);
+ return NT_STATUS_NO_SUCH_PRIVILEGE;
+ }
+
+ remove_privilege(map.priv_set, *luid_attr);
+ }
+
+ if(!pdb_update_group_mapping_entry(&map))
+ return NT_STATUS_NO_SUCH_GROUP;
+
+ destroy_privilege(&map.priv_set);
+#endif
+ return r_u->status;
+}
+
+/***************************************************************************
+ For a given SID, remove some privileges.
+ ***************************************************************************/
+
+NTSTATUS _lsa_query_secobj(pipes_struct *p, LSA_Q_QUERY_SEC_OBJ *q_u, LSA_R_QUERY_SEC_OBJ *r_u)
+{
+ struct lsa_info *handle=NULL;
+ SEC_DESC *psd = NULL;
+ size_t sd_size;
+ NTSTATUS status;
+
+ r_u->status = NT_STATUS_OK;
+
+ /* find the connection policy handle. */
+ if (!find_policy_by_hnd(p, &q_u->pol, (void **)&handle))
+ return NT_STATUS_INVALID_HANDLE;
+
+ /* check if the user have enough rights */
+ if (!(handle->access & POLICY_VIEW_LOCAL_INFORMATION))
+ return NT_STATUS_ACCESS_DENIED;
+
+
+ switch (q_u->sec_info) {
+ case 1:
+ /* SD contains only the owner */
+
+ status=lsa_get_generic_sd(p->mem_ctx, &psd, &sd_size);
+ if(!NT_STATUS_IS_OK(status))
+ return NT_STATUS_NO_MEMORY;
+
+
+ if((r_u->buf = make_sec_desc_buf(p->mem_ctx, sd_size, psd)) == NULL)
+ return NT_STATUS_NO_MEMORY;
+ break;
+ case 4:
+ /* SD contains only the ACL */
+
+ status=lsa_get_generic_sd(p->mem_ctx, &psd, &sd_size);
+ if(!NT_STATUS_IS_OK(status))
+ return NT_STATUS_NO_MEMORY;
+
+ if((r_u->buf = make_sec_desc_buf(p->mem_ctx, sd_size, psd)) == NULL)
+ return NT_STATUS_NO_MEMORY;
+ break;
+ default:
+ return NT_STATUS_INVALID_LEVEL;
+ }
+
+ r_u->ptr=1;
+
+ return r_u->status;
+}
+
+
+NTSTATUS _lsa_query_info2(pipes_struct *p, LSA_Q_QUERY_INFO2 *q_u, LSA_R_QUERY_INFO2 *r_u)
+{
+ struct lsa_info *handle;
+ const char *nb_name;
+ char *dns_name = NULL;
+ char *forest_name = NULL;
+ DOM_SID *sid = NULL;
+ struct uuid guid;
+ fstring dnsdomname;
+
+ ZERO_STRUCT(guid);
+ r_u->status = NT_STATUS_OK;
+
+ if (!find_policy_by_hnd(p, &q_u->pol, (void **)&handle))
+ return NT_STATUS_INVALID_HANDLE;
+
+ switch (q_u->info_class) {
+ case 0x0c:
+ /* check if the user have enough rights */
+ if (!(handle->access & POLICY_VIEW_LOCAL_INFORMATION))
+ return NT_STATUS_ACCESS_DENIED;
+
+ /* Request PolicyPrimaryDomainInformation. */
+ switch (lp_server_role()) {
+ case ROLE_DOMAIN_PDC:
+ case ROLE_DOMAIN_BDC:
+ nb_name = get_global_sam_name();
+ /* ugly temp hack for these next two */
+
+ /* This should be a 'netbios domain -> DNS domain' mapping */
+ dnsdomname[0] = '\0';
+ get_mydnsdomname(dnsdomname);
+ strlower_m(dnsdomname);
+
+ dns_name = dnsdomname;
+ forest_name = dnsdomname;
+
+ sid = get_global_sam_sid();
+ secrets_fetch_domain_guid(lp_workgroup(), &guid);
+ break;
+ default:
+ return NT_STATUS_CANT_ACCESS_DOMAIN_INFO;
+ }
+ init_dns_dom_info(&r_u->info.dns_dom_info, nb_name, dns_name,
+ forest_name,&guid,sid);
+ break;
+ default:
+ DEBUG(0,("_lsa_query_info2: unknown info level in Lsa Query: %d\n", q_u->info_class));
+ r_u->status = NT_STATUS_INVALID_INFO_CLASS;
+ break;
+ }
+
+ if (NT_STATUS_IS_OK(r_u->status)) {
+ r_u->ptr = 0x1;
+ r_u->info_class = q_u->info_class;
+ }
+
+ return r_u->status;
+}
diff --git a/source/rpc_server/srv_netlog.c b/source/rpc_server/srv_netlog.c
new file mode 100644
index 00000000000..f06a2002e3c
--- /dev/null
+++ b/source/rpc_server/srv_netlog.c
@@ -0,0 +1,385 @@
+/*
+ * Unix SMB/CIFS implementation.
+ * RPC Pipe client / server routines
+ * Copyright (C) Andrew Tridgell 1992-1997,
+ * Copyright (C) Luke Kenneth Casson Leighton 1996-1997,
+ * Copyright (C) Paul Ashton 1997,
+ * Copyright (C) Jeremy Allison 1998-2001,
+ * 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 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.
+ */
+
+/* This is the interface to the netlogon pipe. */
+
+#include "includes.h"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_RPC_SRV
+
+/*************************************************************************
+ api_net_req_chal:
+ *************************************************************************/
+
+static BOOL api_net_req_chal(pipes_struct *p)
+{
+ NET_Q_REQ_CHAL q_u;
+ NET_R_REQ_CHAL r_u;
+ prs_struct *data = &p->in_data.data;
+ prs_struct *rdata = &p->out_data.rdata;
+
+ ZERO_STRUCT(q_u);
+ ZERO_STRUCT(r_u);
+
+ /* grab the challenge... */
+ if(!net_io_q_req_chal("", &q_u, data, 0)) {
+ DEBUG(0,("api_net_req_chal: Failed to unmarshall NET_Q_REQ_CHAL.\n"));
+ return False;
+ }
+
+ r_u.status = _net_req_chal(p, &q_u, &r_u);
+
+ /* store the response in the SMB stream */
+ if(!net_io_r_req_chal("", &r_u, rdata, 0)) {
+ DEBUG(0,("api_net_req_chal: Failed to marshall NET_R_REQ_CHAL.\n"));
+ return False;
+ }
+
+ return True;
+}
+
+/*************************************************************************
+ api_net_auth:
+ *************************************************************************/
+
+static BOOL api_net_auth(pipes_struct *p)
+{
+ NET_Q_AUTH q_u;
+ NET_R_AUTH r_u;
+ prs_struct *data = &p->in_data.data;
+ prs_struct *rdata = &p->out_data.rdata;
+
+ ZERO_STRUCT(q_u);
+ ZERO_STRUCT(r_u);
+
+ /* grab the challenge... */
+ if(!net_io_q_auth("", &q_u, data, 0)) {
+ DEBUG(0,("api_net_auth: Failed to unmarshall NET_Q_AUTH.\n"));
+ return False;
+ }
+
+ r_u.status = _net_auth(p, &q_u, &r_u);
+
+ /* store the response in the SMB stream */
+ if(!net_io_r_auth("", &r_u, rdata, 0)) {
+ DEBUG(0,("api_net_auth: Failed to marshall NET_R_AUTH.\n"));
+ return False;
+ }
+
+ return True;
+}
+
+/*************************************************************************
+ api_net_auth_2:
+ *************************************************************************/
+
+static BOOL api_net_auth_2(pipes_struct *p)
+{
+ NET_Q_AUTH_2 q_u;
+ NET_R_AUTH_2 r_u;
+ prs_struct *data = &p->in_data.data;
+ prs_struct *rdata = &p->out_data.rdata;
+
+ ZERO_STRUCT(q_u);
+ ZERO_STRUCT(r_u);
+
+ /* grab the challenge... */
+ if(!net_io_q_auth_2("", &q_u, data, 0)) {
+ DEBUG(0,("api_net_auth_2: Failed to unmarshall NET_Q_AUTH_2.\n"));
+ return False;
+ }
+
+ r_u.status = _net_auth_2(p, &q_u, &r_u);
+
+ /* store the response in the SMB stream */
+ if(!net_io_r_auth_2("", &r_u, rdata, 0)) {
+ DEBUG(0,("api_net_auth_2: Failed to marshall NET_R_AUTH_2.\n"));
+ return False;
+ }
+
+ return True;
+}
+
+/*************************************************************************
+ api_net_srv_pwset:
+ *************************************************************************/
+
+static BOOL api_net_srv_pwset(pipes_struct *p)
+{
+ NET_Q_SRV_PWSET q_u;
+ NET_R_SRV_PWSET r_u;
+ prs_struct *data = &p->in_data.data;
+ prs_struct *rdata = &p->out_data.rdata;
+
+ ZERO_STRUCT(q_u);
+ ZERO_STRUCT(r_u);
+
+ /* grab the challenge and encrypted password ... */
+ if(!net_io_q_srv_pwset("", &q_u, data, 0)) {
+ DEBUG(0,("api_net_srv_pwset: Failed to unmarshall NET_Q_SRV_PWSET.\n"));
+ return False;
+ }
+
+ r_u.status = _net_srv_pwset(p, &q_u, &r_u);
+
+ /* store the response in the SMB stream */
+ if(!net_io_r_srv_pwset("", &r_u, rdata, 0)) {
+ DEBUG(0,("api_net_srv_pwset: Failed to marshall NET_R_SRV_PWSET.\n"));
+ return False;
+ }
+
+ return True;
+}
+
+/*************************************************************************
+ api_net_sam_logoff:
+ *************************************************************************/
+
+static BOOL api_net_sam_logoff(pipes_struct *p)
+{
+ NET_Q_SAM_LOGOFF q_u;
+ NET_R_SAM_LOGOFF r_u;
+ prs_struct *data = &p->in_data.data;
+ prs_struct *rdata = &p->out_data.rdata;
+
+ ZERO_STRUCT(q_u);
+ ZERO_STRUCT(r_u);
+
+ if(!net_io_q_sam_logoff("", &q_u, data, 0)) {
+ DEBUG(0,("api_net_sam_logoff: Failed to unmarshall NET_Q_SAM_LOGOFF.\n"));
+ return False;
+ }
+
+ r_u.status = _net_sam_logoff(p, &q_u, &r_u);
+
+ /* store the response in the SMB stream */
+ if(!net_io_r_sam_logoff("", &r_u, rdata, 0)) {
+ DEBUG(0,("api_net_sam_logoff: Failed to marshall NET_R_SAM_LOGOFF.\n"));
+ return False;
+ }
+
+ return True;
+}
+
+/*************************************************************************
+ api_net_sam_logon:
+ *************************************************************************/
+
+static BOOL api_net_sam_logon(pipes_struct *p)
+{
+ NET_Q_SAM_LOGON q_u;
+ NET_R_SAM_LOGON r_u;
+ prs_struct *data = &p->in_data.data;
+ prs_struct *rdata = &p->out_data.rdata;
+
+ ZERO_STRUCT(q_u);
+ ZERO_STRUCT(r_u);
+
+ if(!net_io_q_sam_logon("", &q_u, data, 0)) {
+ DEBUG(0, ("api_net_sam_logon: Failed to unmarshall NET_Q_SAM_LOGON.\n"));
+ return False;
+ }
+
+ r_u.status = _net_sam_logon(p, &q_u, &r_u);
+
+ /* store the response in the SMB stream */
+ if(!net_io_r_sam_logon("", &r_u, rdata, 0)) {
+ DEBUG(0,("api_net_sam_logon: Failed to marshall NET_R_SAM_LOGON.\n"));
+ return False;
+ }
+
+ return True;
+}
+
+/*************************************************************************
+ api_net_trust_dom_list:
+ *************************************************************************/
+
+static BOOL api_net_trust_dom_list(pipes_struct *p)
+{
+ NET_Q_TRUST_DOM_LIST q_u;
+ NET_R_TRUST_DOM_LIST r_u;
+ prs_struct *data = &p->in_data.data;
+ prs_struct *rdata = &p->out_data.rdata;
+
+ ZERO_STRUCT(q_u);
+ ZERO_STRUCT(r_u);
+
+ DEBUG(6,("api_net_trust_dom_list: %d\n", __LINE__));
+
+ /* grab the lsa trusted domain list query... */
+ if(!net_io_q_trust_dom("", &q_u, data, 0)) {
+ DEBUG(0,("api_net_trust_dom_list: Failed to unmarshall NET_Q_TRUST_DOM_LIST.\n"));
+ return False;
+ }
+
+ /* construct reply. */
+ r_u.status = _net_trust_dom_list(p, &q_u, &r_u);
+
+ /* store the response in the SMB stream */
+ if(!net_io_r_trust_dom("", &r_u, rdata, 0)) {
+ DEBUG(0,("net_reply_trust_dom_list: Failed to marshall NET_R_TRUST_DOM_LIST.\n"));
+ return False;
+ }
+
+ DEBUG(6,("api_net_trust_dom_list: %d\n", __LINE__));
+
+ return True;
+}
+
+/*************************************************************************
+ api_net_logon_ctrl2:
+ *************************************************************************/
+
+static BOOL api_net_logon_ctrl2(pipes_struct *p)
+{
+ NET_Q_LOGON_CTRL2 q_u;
+ NET_R_LOGON_CTRL2 r_u;
+ prs_struct *data = &p->in_data.data;
+ prs_struct *rdata = &p->out_data.rdata;
+
+ ZERO_STRUCT(q_u);
+ ZERO_STRUCT(r_u);
+
+ DEBUG(6,("api_net_logon_ctrl2: %d\n", __LINE__));
+
+ /* grab the lsa netlogon ctrl2 query... */
+ if(!net_io_q_logon_ctrl2("", &q_u, data, 0)) {
+ DEBUG(0,("api_net_logon_ctrl2: Failed to unmarshall NET_Q_LOGON_CTRL2.\n"));
+ return False;
+ }
+
+ r_u.status = _net_logon_ctrl2(p, &q_u, &r_u);
+
+ if(!net_io_r_logon_ctrl2("", &r_u, rdata, 0)) {
+ DEBUG(0,("net_reply_logon_ctrl2: Failed to marshall NET_R_LOGON_CTRL2.\n"));
+ return False;
+ }
+
+ DEBUG(6,("api_net_logon_ctrl2: %d\n", __LINE__));
+
+ return True;
+}
+
+/*************************************************************************
+ api_net_logon_ctrl:
+ *************************************************************************/
+
+static BOOL api_net_logon_ctrl(pipes_struct *p)
+{
+ NET_Q_LOGON_CTRL q_u;
+ NET_R_LOGON_CTRL r_u;
+ prs_struct *data = &p->in_data.data;
+ prs_struct *rdata = &p->out_data.rdata;
+
+ ZERO_STRUCT(q_u);
+ ZERO_STRUCT(r_u);
+
+ DEBUG(6,("api_net_logon_ctrl: %d\n", __LINE__));
+
+ /* grab the lsa netlogon ctrl query... */
+ if(!net_io_q_logon_ctrl("", &q_u, data, 0)) {
+ DEBUG(0,("api_net_logon_ctrl: Failed to unmarshall NET_Q_LOGON_CTRL.\n"));
+ return False;
+ }
+
+ r_u.status = _net_logon_ctrl(p, &q_u, &r_u);
+
+ if(!net_io_r_logon_ctrl("", &r_u, rdata, 0)) {
+ DEBUG(0,("net_reply_logon_ctrl2: Failed to marshall NET_R_LOGON_CTRL2.\n"));
+ return False;
+ }
+
+ DEBUG(6,("api_net_logon_ctrl2: %d\n", __LINE__));
+
+ return True;
+}
+
+/*************************************************************************
+ api_ds_enum_dom_trusts:
+ *************************************************************************/
+
+#if 0 /* JERRY */
+static BOOL api_ds_enum_dom_trusts(pipes_struct *p)
+{
+ DS_Q_ENUM_DOM_TRUSTS q_u;
+ DS_R_ENUM_DOM_TRUSTS r_u;
+
+ prs_struct *data = &p->in_data.data;
+ prs_struct *rdata = &p->out_data.rdata;
+
+ ZERO_STRUCT(q_u);
+ ZERO_STRUCT(r_u);
+
+ DEBUG(6,("api_ds_enum_dom_trusts\n"));
+
+ if ( !ds_io_q_enum_domain_trusts("", data, 0, &q_u) ) {
+ DEBUG(0,("api_ds_enum_domain_trusts: Failed to unmarshall DS_Q_ENUM_DOM_TRUSTS.\n"));
+ return False;
+ }
+
+ r_u.status = _ds_enum_dom_trusts(p, &q_u, &r_u);
+
+ if ( !ds_io_r_enum_domain_trusts("", rdata, 0, &r_u) ) {
+ DEBUG(0,("api_ds_enum_domain_trusts: Failed to marshall DS_R_ENUM_DOM_TRUSTS.\n"));
+ return False;
+ }
+
+ DEBUG(6,("api_ds_enum_dom_trusts\n"));
+
+ return True;
+}
+#endif /* JERRY */
+
+/*******************************************************************
+ array of \PIPE\NETLOGON operations
+ ********************************************************************/
+static struct api_struct api_net_cmds [] =
+ {
+ { "NET_REQCHAL" , NET_REQCHAL , api_net_req_chal },
+ { "NET_AUTH" , NET_AUTH , api_net_auth },
+ { "NET_AUTH2" , NET_AUTH2 , api_net_auth_2 },
+ { "NET_SRVPWSET" , NET_SRVPWSET , api_net_srv_pwset },
+ { "NET_SAMLOGON" , NET_SAMLOGON , api_net_sam_logon },
+ { "NET_SAMLOGOFF" , NET_SAMLOGOFF , api_net_sam_logoff },
+ { "NET_LOGON_CTRL2" , NET_LOGON_CTRL2 , api_net_logon_ctrl2 },
+ { "NET_TRUST_DOM_LIST", NET_TRUST_DOM_LIST, api_net_trust_dom_list },
+ { "NET_LOGON_CTRL" , NET_LOGON_CTRL , api_net_logon_ctrl },
+#if 0 /* JERRY */
+ { "DS_ENUM_DOM_TRUSTS", DS_ENUM_DOM_TRUSTS, api_ds_enum_dom_trusts }
+#endif /* JERRY */
+ };
+
+void netlog_get_pipe_fns( struct api_struct **fns, int *n_fns )
+{
+ *fns = api_net_cmds;
+ *n_fns = sizeof(api_net_cmds) / sizeof(struct api_struct);
+}
+
+NTSTATUS rpc_net_init(void)
+{
+ return rpc_pipe_register_commands(SMB_RPC_INTERFACE_VERSION, "NETLOGON", "lsass", api_net_cmds,
+ sizeof(api_net_cmds) / sizeof(struct api_struct));
+}
diff --git a/source/rpc_server/srv_netlog_nt.c b/source/rpc_server/srv_netlog_nt.c
new file mode 100644
index 00000000000..51ed79980c7
--- /dev/null
+++ b/source/rpc_server/srv_netlog_nt.c
@@ -0,0 +1,795 @@
+/*
+ * Unix SMB/CIFS implementation.
+ * RPC Pipe client / server routines
+ * Copyright (C) Andrew Tridgell 1992-1997,
+ * Copyright (C) Luke Kenneth Casson Leighton 1996-1997,
+ * Copyright (C) Paul Ashton 1997.
+ * Copyright (C) Jeremy Allison 1998-2001.
+ * Copyright (C) Andrew Bartlett 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 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.
+ */
+
+/* This is the implementation of the netlogon pipe. */
+
+#include "includes.h"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_RPC_SRV
+
+/*************************************************************************
+ init_net_r_req_chal:
+ *************************************************************************/
+
+static void init_net_r_req_chal(NET_R_REQ_CHAL *r_c,
+ DOM_CHAL *srv_chal, NTSTATUS status)
+{
+ DEBUG(6,("init_net_r_req_chal: %d\n", __LINE__));
+ memcpy(r_c->srv_chal.data, srv_chal->data, sizeof(srv_chal->data));
+ r_c->status = status;
+}
+
+/*************************************************************************
+ error messages cropping up when using nltest.exe...
+ *************************************************************************/
+
+#define ERROR_NO_SUCH_DOMAIN 0x54b
+#define ERROR_NO_LOGON_SERVERS 0x51f
+
+/*************************************************************************
+ net_reply_logon_ctrl:
+ *************************************************************************/
+
+/* Some flag values reverse engineered from NLTEST.EXE */
+
+#define LOGON_CTRL_IN_SYNC 0x00
+#define LOGON_CTRL_REPL_NEEDED 0x01
+#define LOGON_CTRL_REPL_IN_PROGRESS 0x02
+
+NTSTATUS _net_logon_ctrl(pipes_struct *p, NET_Q_LOGON_CTRL *q_u,
+ NET_R_LOGON_CTRL *r_u)
+{
+ uint32 flags = 0x0;
+ uint32 pdc_connection_status = 0x00; /* Maybe a win32 error code? */
+
+ /* Setup the Logon Control response */
+
+ init_net_r_logon_ctrl(r_u, q_u->query_level, flags,
+ pdc_connection_status);
+
+ return r_u->status;
+}
+
+/****************************************************************************
+Send a message to smbd to do a sam synchronisation
+**************************************************************************/
+static void send_sync_message(void)
+{
+ TDB_CONTEXT *tdb;
+
+ tdb = tdb_open_log(lock_path("connections.tdb"), 0,
+ TDB_DEFAULT, O_RDONLY, 0);
+
+ if (!tdb) {
+ DEBUG(3, ("send_sync_message(): failed to open connections "
+ "database\n"));
+ return;
+ }
+
+ DEBUG(3, ("sending sam synchronisation message\n"));
+
+ message_send_all(tdb, MSG_SMB_SAM_SYNC, NULL, 0, False, NULL);
+
+ tdb_close(tdb);
+}
+
+/*************************************************************************
+ net_reply_logon_ctrl2:
+ *************************************************************************/
+
+NTSTATUS _net_logon_ctrl2(pipes_struct *p, NET_Q_LOGON_CTRL2 *q_u, NET_R_LOGON_CTRL2 *r_u)
+{
+ uint32 flags = 0x0;
+ uint32 pdc_connection_status = 0x0;
+ uint32 logon_attempts = 0x0;
+ uint32 tc_status = ERROR_NO_LOGON_SERVERS;
+ const char *trusted_domain = "test_domain";
+
+ DEBUG(0, ("*** net long ctrl2 %d, %d, %d\n",
+ q_u->function_code, q_u->query_level, q_u->switch_value));
+
+ DEBUG(6,("_net_logon_ctrl2: %d\n", __LINE__));
+
+
+ /* set up the Logon Control2 response */
+ init_net_r_logon_ctrl2(r_u, q_u->query_level,
+ flags, pdc_connection_status, logon_attempts,
+ tc_status, trusted_domain);
+
+ if (lp_server_role() == ROLE_DOMAIN_BDC)
+ send_sync_message();
+
+ DEBUG(6,("_net_logon_ctrl2: %d\n", __LINE__));
+
+ return r_u->status;
+}
+
+/*************************************************************************
+ net_reply_trust_dom_list:
+ *************************************************************************/
+
+NTSTATUS _net_trust_dom_list(pipes_struct *p, NET_Q_TRUST_DOM_LIST *q_u, NET_R_TRUST_DOM_LIST *r_u)
+{
+ const char *trusted_domain = "test_domain";
+ uint32 num_trust_domains = 1;
+
+ DEBUG(6,("_net_trust_dom_list: %d\n", __LINE__));
+
+ /* set up the Trusted Domain List response */
+ init_r_trust_dom(r_u, num_trust_domains, trusted_domain);
+
+ DEBUG(6,("_net_trust_dom_list: %d\n", __LINE__));
+
+ return r_u->status;
+}
+
+/***********************************************************************************
+ init_net_r_srv_pwset:
+ ***********************************************************************************/
+
+static void init_net_r_srv_pwset(NET_R_SRV_PWSET *r_s,
+ DOM_CRED *srv_cred, NTSTATUS status)
+{
+ DEBUG(5,("init_net_r_srv_pwset: %d\n", __LINE__));
+
+ memcpy(&r_s->srv_cred, srv_cred, sizeof(r_s->srv_cred));
+ r_s->status = status;
+
+ DEBUG(5,("init_net_r_srv_pwset: %d\n", __LINE__));
+}
+
+/******************************************************************
+ gets a machine password entry. checks access rights of the host.
+ ******************************************************************/
+
+static BOOL get_md4pw(char *md4pw, char *mach_acct)
+{
+ SAM_ACCOUNT *sampass = NULL;
+ const uint8 *pass;
+ BOOL ret;
+ uint32 acct_ctrl;
+
+#if 0
+ /*
+ * Currently this code is redundent as we already have a filter
+ * by hostname list. What this code really needs to do is to
+ * get a hosts allowed/hosts denied list from the SAM database
+ * on a per user basis, and make the access decision there.
+ * I will leave this code here for now as a reminder to implement
+ * this at a later date. JRA.
+ */
+
+ if (!allow_access(lp_domain_hostsdeny(), lp_domain_hostsallow(),
+ client_name(), client_addr()))
+ {
+ DEBUG(0,("get_md4pw: Workstation %s denied access to domain\n", mach_acct));
+ return False;
+ }
+#endif /* 0 */
+
+ if(!NT_STATUS_IS_OK(pdb_init_sam(&sampass)))
+ return False;
+
+ /* JRA. This is ok as it is only used for generating the challenge. */
+ become_root();
+ ret=pdb_getsampwnam(sampass, mach_acct);
+ unbecome_root();
+
+ if (ret==False) {
+ DEBUG(0,("get_md4pw: Workstation %s: no account in domain\n", mach_acct));
+ pdb_free_sam(&sampass);
+ return False;
+ }
+
+ acct_ctrl = pdb_get_acct_ctrl(sampass);
+ if (!(acct_ctrl & ACB_DISABLED) &&
+ ((acct_ctrl & ACB_DOMTRUST) ||
+ (acct_ctrl & ACB_WSTRUST) ||
+ (acct_ctrl & ACB_SVRTRUST)) &&
+ ((pass=pdb_get_nt_passwd(sampass)) != NULL)) {
+ memcpy(md4pw, pass, 16);
+ dump_data(5, md4pw, 16);
+ pdb_free_sam(&sampass);
+ return True;
+ }
+
+ DEBUG(0,("get_md4pw: Workstation %s: no account in domain\n", mach_acct));
+ pdb_free_sam(&sampass);
+ return False;
+
+}
+
+/*************************************************************************
+ _net_req_chal
+ *************************************************************************/
+
+NTSTATUS _net_req_chal(pipes_struct *p, NET_Q_REQ_CHAL *q_u, NET_R_REQ_CHAL *r_u)
+{
+ NTSTATUS status = NT_STATUS_OK;
+
+ rpcstr_pull(p->dc.remote_machine,q_u->uni_logon_clnt.buffer,sizeof(fstring),q_u->uni_logon_clnt.uni_str_len*2,0);
+
+ /* create a server challenge for the client */
+ /* Set these to random values. */
+ generate_random_buffer(p->dc.srv_chal.data, 8, False);
+
+ memcpy(p->dc.srv_cred.challenge.data, p->dc.srv_chal.data, 8);
+
+ memcpy(p->dc.clnt_chal.data , q_u->clnt_chal.data, sizeof(q_u->clnt_chal.data));
+ memcpy(p->dc.clnt_cred.challenge.data, q_u->clnt_chal.data, sizeof(q_u->clnt_chal.data));
+
+ memset((char *)p->dc.sess_key, '\0', sizeof(p->dc.sess_key));
+
+ p->dc.challenge_sent = True;
+ /* set up the LSA REQUEST CHALLENGE response */
+ init_net_r_req_chal(r_u, &p->dc.srv_chal, status);
+
+ return status;
+}
+
+/*************************************************************************
+ init_net_r_auth:
+ *************************************************************************/
+
+static void init_net_r_auth(NET_R_AUTH *r_a, DOM_CHAL *resp_cred, NTSTATUS status)
+{
+ memcpy(r_a->srv_chal.data, resp_cred->data, sizeof(resp_cred->data));
+ r_a->status = status;
+}
+
+/*************************************************************************
+ _net_auth
+ *************************************************************************/
+
+NTSTATUS _net_auth(pipes_struct *p, NET_Q_AUTH *q_u, NET_R_AUTH *r_u)
+{
+ NTSTATUS status = NT_STATUS_OK;
+ DOM_CHAL srv_cred;
+ UTIME srv_time;
+ fstring mach_acct;
+
+ srv_time.time = 0;
+
+ rpcstr_pull(mach_acct, q_u->clnt_id.uni_acct_name.buffer,sizeof(fstring),q_u->clnt_id.uni_acct_name.uni_str_len*2,0);
+
+ if (p->dc.challenge_sent && get_md4pw((char *)p->dc.md4pw, mach_acct)) {
+
+ /* from client / server challenges and md4 password, generate sess key */
+ cred_session_key(&p->dc.clnt_chal, &p->dc.srv_chal,
+ p->dc.md4pw, p->dc.sess_key);
+
+ /* check that the client credentials are valid */
+ if (cred_assert(&q_u->clnt_chal, p->dc.sess_key, &p->dc.clnt_cred.challenge, srv_time)) {
+
+ /* create server challenge for inclusion in the reply */
+ cred_create(p->dc.sess_key, &p->dc.srv_cred.challenge, srv_time, &srv_cred);
+
+ /* copy the received client credentials for use next time */
+ memcpy(p->dc.clnt_cred.challenge.data, q_u->clnt_chal.data, sizeof(q_u->clnt_chal.data));
+ memcpy(p->dc.srv_cred .challenge.data, q_u->clnt_chal.data, sizeof(q_u->clnt_chal.data));
+
+ /* Save the machine account name. */
+ fstrcpy(p->dc.mach_acct, mach_acct);
+
+ p->dc.authenticated = True;
+
+ } else {
+ status = NT_STATUS_ACCESS_DENIED;
+ }
+ } else {
+ status = NT_STATUS_ACCESS_DENIED;
+ }
+
+ /* set up the LSA AUTH response */
+ init_net_r_auth(r_u, &srv_cred, status);
+
+ return r_u->status;
+}
+
+/*************************************************************************
+ init_net_r_auth_2:
+ *************************************************************************/
+
+static void init_net_r_auth_2(NET_R_AUTH_2 *r_a,
+ DOM_CHAL *resp_cred, NEG_FLAGS *flgs, NTSTATUS status)
+{
+ memcpy(r_a->srv_chal.data, resp_cred->data, sizeof(resp_cred->data));
+ memcpy(&r_a->srv_flgs, flgs, sizeof(r_a->srv_flgs));
+ r_a->status = status;
+}
+
+/*************************************************************************
+ _net_auth_2
+ *************************************************************************/
+
+NTSTATUS _net_auth_2(pipes_struct *p, NET_Q_AUTH_2 *q_u, NET_R_AUTH_2 *r_u)
+{
+ NTSTATUS status = NT_STATUS_OK;
+ DOM_CHAL srv_cred;
+ UTIME srv_time;
+ NEG_FLAGS srv_flgs;
+ fstring mach_acct;
+
+ srv_time.time = 0;
+
+ if ( (lp_server_schannel() == True) &&
+ ((q_u->clnt_flgs.neg_flags & NETLOGON_NEG_SCHANNEL) == 0) ) {
+
+ /* schannel must be used, but client did not offer it. */
+ status = NT_STATUS_ACCESS_DENIED;
+ }
+
+ rpcstr_pull(mach_acct, q_u->clnt_id.uni_acct_name.buffer,sizeof(fstring),q_u->clnt_id.uni_acct_name.uni_str_len*2,0);
+
+ if (p->dc.challenge_sent && get_md4pw((char *)p->dc.md4pw, mach_acct)) {
+
+ /* from client / server challenges and md4 password, generate sess key */
+ cred_session_key(&p->dc.clnt_chal, &p->dc.srv_chal,
+ p->dc.md4pw, p->dc.sess_key);
+
+ /* check that the client credentials are valid */
+ if (cred_assert(&q_u->clnt_chal, p->dc.sess_key, &p->dc.clnt_cred.challenge, srv_time)) {
+
+ /* create server challenge for inclusion in the reply */
+ cred_create(p->dc.sess_key, &p->dc.srv_cred.challenge, srv_time, &srv_cred);
+
+ /* copy the received client credentials for use next time */
+ memcpy(p->dc.clnt_cred.challenge.data, q_u->clnt_chal.data, sizeof(q_u->clnt_chal.data));
+ memcpy(p->dc.srv_cred .challenge.data, q_u->clnt_chal.data, sizeof(q_u->clnt_chal.data));
+
+ /* Save the machine account name. */
+ fstrcpy(p->dc.mach_acct, mach_acct);
+
+ p->dc.authenticated = True;
+
+ } else {
+ status = NT_STATUS_ACCESS_DENIED;
+ }
+ } else {
+ status = NT_STATUS_ACCESS_DENIED;
+ }
+
+ srv_flgs.neg_flags = 0x000001ff;
+
+ if (lp_server_schannel() != False) {
+ srv_flgs.neg_flags |= NETLOGON_NEG_SCHANNEL;
+ }
+
+ /* set up the LSA AUTH 2 response */
+ init_net_r_auth_2(r_u, &srv_cred, &srv_flgs, status);
+
+ if (NT_STATUS_IS_OK(status)) {
+ extern struct dcinfo last_dcinfo;
+ last_dcinfo = p->dc;
+ }
+
+ return r_u->status;
+}
+
+/*************************************************************************
+ _net_srv_pwset
+ *************************************************************************/
+
+NTSTATUS _net_srv_pwset(pipes_struct *p, NET_Q_SRV_PWSET *q_u, NET_R_SRV_PWSET *r_u)
+{
+ NTSTATUS status = NT_STATUS_ACCESS_DENIED;
+ DOM_CRED srv_cred;
+ pstring workstation;
+ SAM_ACCOUNT *sampass=NULL;
+ BOOL ret = False;
+ unsigned char pwd[16];
+ int i;
+ uint32 acct_ctrl;
+
+ /* checks and updates credentials. creates reply credentials */
+ if (!(p->dc.authenticated && deal_with_creds(p->dc.sess_key, &p->dc.clnt_cred, &q_u->clnt_id.cred, &srv_cred)))
+ return NT_STATUS_INVALID_HANDLE;
+
+ memcpy(&p->dc.srv_cred, &p->dc.clnt_cred, sizeof(p->dc.clnt_cred));
+
+ DEBUG(5,("_net_srv_pwset: %d\n", __LINE__));
+
+ rpcstr_pull(workstation,q_u->clnt_id.login.uni_comp_name.buffer,
+ sizeof(workstation),q_u->clnt_id.login.uni_comp_name.uni_str_len*2,0);
+
+ DEBUG(3,("Server Password Set by Wksta:[%s] on account [%s]\n", workstation, p->dc.mach_acct));
+
+ pdb_init_sam(&sampass);
+
+ become_root();
+ ret=pdb_getsampwnam(sampass, p->dc.mach_acct);
+ unbecome_root();
+
+ /* Ensure the account exists and is a machine account. */
+
+ acct_ctrl = pdb_get_acct_ctrl(sampass);
+
+ if (!(ret
+ && (acct_ctrl & ACB_WSTRUST ||
+ acct_ctrl & ACB_SVRTRUST ||
+ acct_ctrl & ACB_DOMTRUST))) {
+ pdb_free_sam(&sampass);
+ return NT_STATUS_NO_SUCH_USER;
+ }
+
+ if (pdb_get_acct_ctrl(sampass) & ACB_DISABLED) {
+ pdb_free_sam(&sampass);
+ return NT_STATUS_ACCOUNT_DISABLED;
+ }
+
+ DEBUG(100,("Server password set : new given value was :\n"));
+ for(i = 0; i < 16; i++)
+ DEBUG(100,("%02X ", q_u->pwd[i]));
+ DEBUG(100,("\n"));
+
+ cred_hash3( pwd, q_u->pwd, p->dc.sess_key, 0);
+
+ /* lies! nt and lm passwords are _not_ the same: don't care */
+ if (!pdb_set_lanman_passwd (sampass, pwd, PDB_CHANGED)) {
+ pdb_free_sam(&sampass);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ if (!pdb_set_nt_passwd (sampass, pwd, PDB_CHANGED)) {
+ pdb_free_sam(&sampass);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ if (!pdb_set_pass_changed_now (sampass)) {
+ pdb_free_sam(&sampass);
+ /* Not quite sure what this one qualifies as, but this will do */
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ become_root();
+ ret = pdb_update_sam_account (sampass);
+ unbecome_root();
+
+ if (ret)
+ status = NT_STATUS_OK;
+
+ /* set up the LSA Server Password Set response */
+ init_net_r_srv_pwset(r_u, &srv_cred, status);
+
+ pdb_free_sam(&sampass);
+ return r_u->status;
+}
+
+
+/*************************************************************************
+ _net_sam_logoff:
+ *************************************************************************/
+
+NTSTATUS _net_sam_logoff(pipes_struct *p, NET_Q_SAM_LOGOFF *q_u, NET_R_SAM_LOGOFF *r_u)
+{
+ DOM_CRED srv_cred;
+
+ if (!get_valid_user_struct(p->vuid))
+ return NT_STATUS_NO_SUCH_USER;
+
+ /* checks and updates credentials. creates reply credentials */
+ if (!(p->dc.authenticated && deal_with_creds(p->dc.sess_key, &p->dc.clnt_cred,
+ &q_u->sam_id.client.cred, &srv_cred)))
+ return NT_STATUS_INVALID_HANDLE;
+
+ memcpy(&p->dc.srv_cred, &p->dc.clnt_cred, sizeof(p->dc.clnt_cred));
+
+ /* XXXX maybe we want to say 'no', reject the client's credentials */
+ r_u->buffer_creds = 1; /* yes, we have valid server credentials */
+ memcpy(&r_u->srv_creds, &srv_cred, sizeof(r_u->srv_creds));
+
+ r_u->status = NT_STATUS_OK;
+
+ return r_u->status;
+}
+
+
+/*************************************************************************
+ _net_sam_logon
+ *************************************************************************/
+
+NTSTATUS _net_sam_logon(pipes_struct *p, NET_Q_SAM_LOGON *q_u, NET_R_SAM_LOGON *r_u)
+{
+ NTSTATUS status = NT_STATUS_OK;
+ NET_USER_INFO_3 *usr_info = NULL;
+ NET_ID_INFO_CTR *ctr = q_u->sam_id.ctr;
+ DOM_CRED srv_cred;
+ UNISTR2 *uni_samlogon_user = NULL;
+ UNISTR2 *uni_samlogon_domain = NULL;
+ UNISTR2 *uni_samlogon_workstation = NULL;
+ fstring nt_username, nt_domain, nt_workstation;
+ auth_usersupplied_info *user_info = NULL;
+ auth_serversupplied_info *server_info = NULL;
+ extern userdom_struct current_user_info;
+ SAM_ACCOUNT *sampw;
+ struct auth_context *auth_context = NULL;
+
+ usr_info = (NET_USER_INFO_3 *)talloc(p->mem_ctx, sizeof(NET_USER_INFO_3));
+ if (!usr_info)
+ return NT_STATUS_NO_MEMORY;
+
+ ZERO_STRUCTP(usr_info);
+
+ /* store the user information, if there is any. */
+ r_u->user = usr_info;
+ r_u->switch_value = 0; /* indicates no info */
+ r_u->auth_resp = 1; /* authoritative response */
+ r_u->switch_value = 3; /* indicates type of validation user info */
+
+ if (!get_valid_user_struct(p->vuid))
+ return NT_STATUS_NO_SUCH_USER;
+
+
+ if ( (lp_server_schannel() == True) && (!p->netsec_auth_validated) ) {
+ /* 'server schannel = yes' should enforce use of
+ schannel, the client did offer it in auth2, but
+ obviously did not use it. */
+ return NT_STATUS_ACCESS_DENIED;
+ }
+
+ /* checks and updates credentials. creates reply credentials */
+ if (!(p->dc.authenticated && deal_with_creds(p->dc.sess_key, &p->dc.clnt_cred, &q_u->sam_id.client.cred, &srv_cred)))
+ return NT_STATUS_INVALID_HANDLE;
+
+ memcpy(&p->dc.srv_cred, &p->dc.clnt_cred, sizeof(p->dc.clnt_cred));
+
+ r_u->buffer_creds = 1; /* yes, we have valid server credentials */
+ memcpy(&r_u->srv_creds, &srv_cred, sizeof(r_u->srv_creds));
+
+ /* find the username */
+
+ switch (q_u->sam_id.logon_level) {
+ case INTERACTIVE_LOGON_TYPE:
+ uni_samlogon_user = &ctr->auth.id1.uni_user_name;
+ uni_samlogon_domain = &ctr->auth.id1.uni_domain_name;
+
+ uni_samlogon_workstation = &ctr->auth.id1.uni_wksta_name;
+
+ DEBUG(3,("SAM Logon (Interactive). Domain:[%s]. ", lp_workgroup()));
+ break;
+ case NET_LOGON_TYPE:
+ uni_samlogon_user = &ctr->auth.id2.uni_user_name;
+ uni_samlogon_domain = &ctr->auth.id2.uni_domain_name;
+ uni_samlogon_workstation = &ctr->auth.id2.uni_wksta_name;
+
+ DEBUG(3,("SAM Logon (Network). Domain:[%s]. ", lp_workgroup()));
+ break;
+ default:
+ DEBUG(2,("SAM Logon: unsupported switch value\n"));
+ return NT_STATUS_INVALID_INFO_CLASS;
+ } /* end switch */
+
+ rpcstr_pull(nt_username,uni_samlogon_user->buffer,sizeof(nt_username),uni_samlogon_user->uni_str_len*2,0);
+ rpcstr_pull(nt_domain,uni_samlogon_domain->buffer,sizeof(nt_domain),uni_samlogon_domain->uni_str_len*2,0);
+ rpcstr_pull(nt_workstation,uni_samlogon_workstation->buffer,sizeof(nt_workstation),uni_samlogon_workstation->uni_str_len*2,0);
+
+ DEBUG(3,("User:[%s@%s] Requested Domain:[%s]\n", nt_username,
+ nt_workstation, nt_domain));
+
+ fstrcpy(current_user_info.smb_name, nt_username);
+ sub_set_smb_name(nt_username);
+
+ DEBUG(5,("Attempting validation level %d for unmapped username %s.\n", q_u->sam_id.ctr->switch_value, nt_username));
+
+ status = NT_STATUS_OK;
+
+ switch (ctr->switch_value) {
+ case NET_LOGON_TYPE:
+ {
+ const char *wksname = nt_workstation;
+
+ if (!NT_STATUS_IS_OK(status = make_auth_context_fixed(&auth_context, ctr->auth.id2.lm_chal))) {
+ return status;
+ }
+
+ /* For a network logon, the workstation name comes in with two
+ * backslashes in the front. Strip them if they are there. */
+
+ if (*wksname == '\\') wksname++;
+ if (*wksname == '\\') wksname++;
+
+ /* Standard challenge/response authenticaion */
+ if (!make_user_info_netlogon_network(&user_info,
+ nt_username, nt_domain,
+ wksname,
+ ctr->auth.id2.lm_chal_resp.buffer,
+ ctr->auth.id2.lm_chal_resp.str_str_len,
+ ctr->auth.id2.nt_chal_resp.buffer,
+ ctr->auth.id2.nt_chal_resp.str_str_len)) {
+ status = NT_STATUS_NO_MEMORY;
+ }
+ break;
+ }
+ case INTERACTIVE_LOGON_TYPE:
+ /* 'Interactive' autheticaion, supplies the password in its
+ MD4 form, encrypted with the session key. We will
+ convert this to chellange/responce for the auth
+ subsystem to chew on */
+ {
+ const uint8 *chal;
+
+ if (!NT_STATUS_IS_OK(status = make_auth_context_subsystem(&auth_context))) {
+ return status;
+ }
+
+ chal = auth_context->get_ntlm_challenge(auth_context);
+
+ if (!make_user_info_netlogon_interactive(&user_info,
+ nt_username, nt_domain,
+ nt_workstation, chal,
+ ctr->auth.id1.lm_owf.data,
+ ctr->auth.id1.nt_owf.data,
+ p->dc.sess_key)) {
+ status = NT_STATUS_NO_MEMORY;
+ }
+ break;
+ }
+ default:
+ DEBUG(2,("SAM Logon: unsupported switch value\n"));
+ return NT_STATUS_INVALID_INFO_CLASS;
+ } /* end switch */
+
+ if ( NT_STATUS_IS_OK(status) ) {
+ status = auth_context->check_ntlm_password(auth_context,
+ user_info, &server_info);
+ }
+
+ (auth_context->free)(&auth_context);
+ free_user_info(&user_info);
+
+ DEBUG(5, ("_net_sam_logon: check_password returned status %s\n",
+ nt_errstr(status)));
+
+ /* Check account and password */
+
+ if (!NT_STATUS_IS_OK(status)) {
+ free_server_info(&server_info);
+ return status;
+ }
+
+ if (server_info->guest) {
+ /* We don't like guest domain logons... */
+ DEBUG(5,("_net_sam_logon: Attempted domain logon as GUEST denied.\n"));
+ free_server_info(&server_info);
+ return NT_STATUS_LOGON_FAILURE;
+ }
+
+ /* This is the point at which, if the login was successful, that
+ the SAM Local Security Authority should record that the user is
+ logged in to the domain. */
+
+ {
+ DOM_GID *gids = NULL;
+ const DOM_SID *user_sid = NULL;
+ const DOM_SID *group_sid = NULL;
+ DOM_SID domain_sid;
+ uint32 user_rid, group_rid;
+
+ int num_gids = 0;
+ pstring my_name;
+ fstring user_sid_string;
+ fstring group_sid_string;
+ uchar nt_session_key[16];
+ uchar lm_session_key[16];
+ uchar netlogon_sess_key[16];
+
+ sampw = server_info->sam_account;
+
+ /* set up pointer indicating user/password failed to be found */
+ usr_info->ptr_user_info = 0;
+
+ user_sid = pdb_get_user_sid(sampw);
+ group_sid = pdb_get_group_sid(sampw);
+
+ sid_copy(&domain_sid, user_sid);
+ sid_split_rid(&domain_sid, &user_rid);
+
+ if (!sid_peek_check_rid(&domain_sid, group_sid, &group_rid)) {
+ DEBUG(1, ("_net_sam_logon: user %s\\%s has user sid %s\n but group sid %s.\nThe conflicting domain portions are not supported for NETLOGON calls\n",
+ pdb_get_domain(sampw), pdb_get_username(sampw),
+ sid_to_string(user_sid_string, user_sid),
+ sid_to_string(group_sid_string, group_sid)));
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ pstrcpy(my_name, global_myname());
+
+ if (!NT_STATUS_IS_OK(status
+ = nt_token_to_group_list(p->mem_ctx,
+ &domain_sid,
+ server_info->ptok,
+ &num_gids,
+ &gids))) {
+ return status;
+ }
+
+ ZERO_STRUCT(netlogon_sess_key);
+ memcpy(netlogon_sess_key, p->dc.sess_key, 8);
+ if (server_info->nt_session_key.length) {
+ memcpy(nt_session_key, server_info->nt_session_key.data,
+ MIN(sizeof(nt_session_key), server_info->nt_session_key.length));
+ SamOEMhash(nt_session_key, netlogon_sess_key, 16);
+ }
+ if (server_info->lm_session_key.length) {
+ memcpy(lm_session_key, server_info->lm_session_key.data,
+ MIN(sizeof(lm_session_key), server_info->lm_session_key.length));
+ SamOEMhash(lm_session_key, netlogon_sess_key, 16);
+ }
+ ZERO_STRUCT(netlogon_sess_key);
+
+ init_net_user_info3(p->mem_ctx, usr_info,
+ user_rid,
+ group_rid,
+ pdb_get_username(sampw),
+ pdb_get_fullname(sampw),
+ pdb_get_homedir(sampw),
+ pdb_get_dir_drive(sampw),
+ pdb_get_logon_script(sampw),
+ pdb_get_profile_path(sampw),
+ pdb_get_logon_time(sampw),
+ get_time_t_max(),
+ get_time_t_max(),
+ pdb_get_pass_last_set_time(sampw),
+ pdb_get_pass_can_change_time(sampw),
+ pdb_get_pass_must_change_time(sampw),
+
+ 0, /* logon_count */
+ 0, /* bad_pw_count */
+ num_gids, /* uint32 num_groups */
+ gids , /* DOM_GID *gids */
+ 0x20 , /* uint32 user_flgs (?) */
+ server_info->nt_session_key.length ? nt_session_key : NULL,
+ server_info->lm_session_key.length ? lm_session_key : NULL,
+ my_name , /* char *logon_srv */
+ pdb_get_domain(sampw),
+ &domain_sid, /* DOM_SID *dom_sid */
+ /* Should be users domain sid, not servers - for trusted domains */
+
+ NULL); /* char *other_sids */
+ ZERO_STRUCT(nt_session_key);
+ ZERO_STRUCT(lm_session_key);
+ }
+ free_server_info(&server_info);
+ return status;
+}
+
+/*************************************************************************
+ _ds_enum_dom_trusts
+ *************************************************************************/
+#if 0 /* JERRY -- not correct */
+NTSTATUS _ds_enum_dom_trusts(pipes_struct *p, DS_Q_ENUM_DOM_TRUSTS *q_u,
+ DS_R_ENUM_DOM_TRUSTS *r_u)
+{
+ NTSTATUS status = NT_STATUS_OK;
+
+ /* TODO: According to MSDN, the can only be executed against a
+ DC or domain member running Windows 2000 or later. Need
+ to test against a standalone 2k server and see what it
+ does. A windows 2000 DC includes its own domain in the
+ list. --jerry */
+
+ return status;
+}
+#endif /* JERRY */
diff --git a/source/rpc_server/srv_pipe.c b/source/rpc_server/srv_pipe.c
new file mode 100644
index 00000000000..90c20a97fa6
--- /dev/null
+++ b/source/rpc_server/srv_pipe.c
@@ -0,0 +1,1625 @@
+/*
+ * Unix SMB/CIFS implementation.
+ * RPC Pipe client / server routines
+ * Copyright (C) Andrew Tridgell 1992-1998
+ * Copyright (C) Luke Kenneth Casson Leighton 1996-1998,
+ * Copyright (C) Paul Ashton 1997-1998,
+ * Copyright (C) Jeremy Allison 1999,
+ * 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 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.
+ */
+
+/* this module apparently provides an implementation of DCE/RPC over a
+ * named pipe (IPC$ connection using SMBtrans). details of DCE/RPC
+ * documentation are available (in on-line form) from the X-Open group.
+ *
+ * this module should provide a level of abstraction between SMB
+ * and DCE/RPC, while minimising the amount of mallocs, unnecessary
+ * data copies, and network traffic.
+ *
+ * in this version, which takes a "let's learn what's going on and
+ * get something running" approach, there is additional network
+ * traffic generated, but the code should be easier to understand...
+ *
+ * ... if you read the docs. or stare at packets for weeks on end.
+ *
+ */
+
+#include "includes.h"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_RPC_SRV
+
+/*************************************************************
+ HACK Alert!
+ We need to transfer the session key from one rpc bind to the
+ next. This is the way the netlogon schannel works.
+**************************************************************/
+struct dcinfo last_dcinfo;
+
+static void NTLMSSPcalc_p( pipes_struct *p, unsigned char *data, int len)
+{
+ unsigned char *hash = p->ntlmssp_hash;
+ unsigned char index_i = hash[256];
+ unsigned char index_j = hash[257];
+ int ind;
+
+ for( ind = 0; ind < len; ind++) {
+ unsigned char tc;
+ unsigned char t;
+
+ index_i++;
+ index_j += hash[index_i];
+
+ tc = hash[index_i];
+ hash[index_i] = hash[index_j];
+ hash[index_j] = tc;
+
+ t = hash[index_i] + hash[index_j];
+ data[ind] = data[ind] ^ hash[t];
+ }
+
+ hash[256] = index_i;
+ hash[257] = index_j;
+}
+
+/*******************************************************************
+ Generate the next PDU to be returned from the data in p->rdata.
+ We cheat here as this function doesn't handle the special auth
+ footers of the authenticated bind response reply.
+ ********************************************************************/
+
+BOOL create_next_pdu(pipes_struct *p)
+{
+ RPC_HDR_RESP hdr_resp;
+ BOOL auth_verify = ((p->ntlmssp_chal_flags & NTLMSSP_NEGOTIATE_SIGN) != 0);
+ BOOL auth_seal = ((p->ntlmssp_chal_flags & NTLMSSP_NEGOTIATE_SEAL) != 0);
+ uint32 data_len;
+ uint32 data_space_available;
+ uint32 data_len_left;
+ prs_struct outgoing_pdu;
+ uint32 data_pos;
+
+ /*
+ * If we're in the fault state, keep returning fault PDU's until
+ * the pipe gets closed. JRA.
+ */
+
+ if(p->fault_state) {
+ setup_fault_pdu(p, NT_STATUS(0x1c010002));
+ return True;
+ }
+
+ memset((char *)&hdr_resp, '\0', sizeof(hdr_resp));
+
+ /* Change the incoming request header to a response. */
+ p->hdr.pkt_type = RPC_RESPONSE;
+
+ /* Set up rpc header flags. */
+ if (p->out_data.data_sent_length == 0)
+ p->hdr.flags = RPC_FLG_FIRST;
+ else
+ p->hdr.flags = 0;
+
+ /*
+ * Work out how much we can fit in a single PDU.
+ */
+
+ data_space_available = sizeof(p->out_data.current_pdu) - RPC_HEADER_LEN - RPC_HDR_RESP_LEN;
+ if(p->ntlmssp_auth_validated)
+ data_space_available -= (RPC_HDR_AUTH_LEN + RPC_AUTH_NTLMSSP_CHK_LEN);
+
+ if(p->netsec_auth_validated)
+ data_space_available -= (RPC_HDR_AUTH_LEN + RPC_AUTH_NETSEC_CHK_LEN);
+
+ /*
+ * The amount we send is the minimum of the available
+ * space and the amount left to send.
+ */
+
+ data_len_left = prs_offset(&p->out_data.rdata) - p->out_data.data_sent_length;
+
+ /*
+ * Ensure there really is data left to send.
+ */
+
+ if(!data_len_left) {
+ DEBUG(0,("create_next_pdu: no data left to send !\n"));
+ return False;
+ }
+
+ data_len = MIN(data_len_left, data_space_available);
+
+ /*
+ * Set up the alloc hint. This should be the data left to
+ * send.
+ */
+
+ hdr_resp.alloc_hint = data_len_left;
+
+ /*
+ * Set up the header lengths.
+ */
+
+ if (p->ntlmssp_auth_validated) {
+ p->hdr.frag_len = RPC_HEADER_LEN + RPC_HDR_RESP_LEN + data_len +
+ RPC_HDR_AUTH_LEN + RPC_AUTH_NTLMSSP_CHK_LEN;
+ p->hdr.auth_len = RPC_AUTH_NTLMSSP_CHK_LEN;
+ } else if (p->netsec_auth_validated) {
+ p->hdr.frag_len = RPC_HEADER_LEN + RPC_HDR_RESP_LEN + data_len +
+ RPC_HDR_AUTH_LEN + RPC_AUTH_NETSEC_CHK_LEN;
+ p->hdr.auth_len = RPC_AUTH_NETSEC_CHK_LEN;
+ } else {
+ p->hdr.frag_len = RPC_HEADER_LEN + RPC_HDR_RESP_LEN + data_len;
+ p->hdr.auth_len = 0;
+ }
+
+ /*
+ * Work out if this PDU will be the last.
+ */
+
+ if(p->out_data.data_sent_length + data_len >= prs_offset(&p->out_data.rdata))
+ p->hdr.flags |= RPC_FLG_LAST;
+
+ /*
+ * Init the parse struct to point at the outgoing
+ * data.
+ */
+
+ prs_init( &outgoing_pdu, 0, p->mem_ctx, MARSHALL);
+ prs_give_memory( &outgoing_pdu, (char *)p->out_data.current_pdu, sizeof(p->out_data.current_pdu), False);
+
+ /* Store the header in the data stream. */
+ if(!smb_io_rpc_hdr("hdr", &p->hdr, &outgoing_pdu, 0)) {
+ DEBUG(0,("create_next_pdu: failed to marshall RPC_HDR.\n"));
+ prs_mem_free(&outgoing_pdu);
+ return False;
+ }
+
+ if(!smb_io_rpc_hdr_resp("resp", &hdr_resp, &outgoing_pdu, 0)) {
+ DEBUG(0,("create_next_pdu: failed to marshall RPC_HDR_RESP.\n"));
+ prs_mem_free(&outgoing_pdu);
+ return False;
+ }
+
+ /* Store the current offset. */
+ data_pos = prs_offset(&outgoing_pdu);
+
+ /* Copy the data into the PDU. */
+
+ if(!prs_append_some_prs_data(&outgoing_pdu, &p->out_data.rdata, p->out_data.data_sent_length, data_len)) {
+ DEBUG(0,("create_next_pdu: failed to copy %u bytes of data.\n", (unsigned int)data_len));
+ prs_mem_free(&outgoing_pdu);
+ return False;
+ }
+
+ if (p->ntlmssp_auth_validated) {
+ uint32 crc32 = 0;
+ char *data;
+
+ DEBUG(5,("create_next_pdu: sign: %s seal: %s data %d auth %d\n",
+ BOOLSTR(auth_verify), BOOLSTR(auth_seal), data_len, p->hdr.auth_len));
+
+ /*
+ * Set data to point to where we copied the data into.
+ */
+
+ data = prs_data_p(&outgoing_pdu) + data_pos;
+
+ if (auth_seal) {
+ crc32 = crc32_calc_buffer(data, data_len);
+ NTLMSSPcalc_p(p, (uchar*)data, data_len);
+ }
+
+ if (auth_seal || auth_verify) {
+ RPC_HDR_AUTH auth_info;
+
+ init_rpc_hdr_auth(&auth_info, NTLMSSP_AUTH_TYPE, RPC_PIPE_AUTH_SEAL_LEVEL,
+ (auth_verify ? RPC_HDR_AUTH_LEN : 0), (auth_verify ? 1 : 0));
+ if(!smb_io_rpc_hdr_auth("hdr_auth", &auth_info, &outgoing_pdu, 0)) {
+ DEBUG(0,("create_next_pdu: failed to marshall RPC_HDR_AUTH.\n"));
+ prs_mem_free(&outgoing_pdu);
+ return False;
+ }
+ }
+
+ if (auth_verify) {
+ RPC_AUTH_NTLMSSP_CHK ntlmssp_chk;
+ char *auth_data = prs_data_p(&outgoing_pdu);
+
+ p->ntlmssp_seq_num++;
+ init_rpc_auth_ntlmssp_chk(&ntlmssp_chk, NTLMSSP_SIGN_VERSION,
+ crc32, p->ntlmssp_seq_num++);
+ auth_data = prs_data_p(&outgoing_pdu) + prs_offset(&outgoing_pdu) + 4;
+ if(!smb_io_rpc_auth_ntlmssp_chk("auth_sign", &ntlmssp_chk, &outgoing_pdu, 0)) {
+ DEBUG(0,("create_next_pdu: failed to marshall RPC_AUTH_NTLMSSP_CHK.\n"));
+ prs_mem_free(&outgoing_pdu);
+ return False;
+ }
+ NTLMSSPcalc_p(p, (uchar*)auth_data, RPC_AUTH_NTLMSSP_CHK_LEN - 4);
+ }
+ }
+
+ if (p->netsec_auth_validated) {
+ int auth_type, auth_level;
+ char *data;
+ RPC_HDR_AUTH auth_info;
+
+ RPC_AUTH_NETSEC_CHK verf;
+ prs_struct rverf;
+ prs_struct rauth;
+
+ data = prs_data_p(&outgoing_pdu) + data_pos;
+ /* Check it's the type of reply we were expecting to decode */
+
+ get_auth_type_level(p->netsec_auth.auth_flags, &auth_type, &auth_level);
+ init_rpc_hdr_auth(&auth_info, auth_type, auth_level,
+ RPC_HDR_AUTH_LEN, 1);
+
+ if(!smb_io_rpc_hdr_auth("hdr_auth", &auth_info, &outgoing_pdu, 0)) {
+ DEBUG(0,("create_next_pdu: failed to marshall RPC_HDR_AUTH.\n"));
+ prs_mem_free(&outgoing_pdu);
+ return False;
+ }
+
+ prs_init(&rverf, 0, p->mem_ctx, MARSHALL);
+ prs_init(&rauth, 0, p->mem_ctx, MARSHALL);
+
+ netsec_encode(&p->netsec_auth,
+ p->netsec_auth.auth_flags,
+ SENDER_IS_ACCEPTOR,
+ &verf, data, data_len);
+
+ smb_io_rpc_auth_netsec_chk("", &verf, &outgoing_pdu, 0);
+
+ p->netsec_auth.seq_num++;
+ }
+
+ /*
+ * Setup the counts for this PDU.
+ */
+
+ p->out_data.data_sent_length += data_len;
+ p->out_data.current_pdu_len = p->hdr.frag_len;
+ p->out_data.current_pdu_sent = 0;
+
+ prs_mem_free(&outgoing_pdu);
+ return True;
+}
+
+/*******************************************************************
+ Process an NTLMSSP authentication response.
+ If this function succeeds, the user has been authenticated
+ and their domain, name and calling workstation stored in
+ the pipe struct.
+ The initial challenge is stored in p->challenge.
+ *******************************************************************/
+
+static BOOL api_pipe_ntlmssp_verify(pipes_struct *p, RPC_AUTH_NTLMSSP_RESP *ntlmssp_resp)
+{
+ uchar lm_owf[24];
+ uchar nt_owf[128];
+ int nt_pw_len;
+ int lm_pw_len;
+ fstring user_name;
+ fstring domain;
+ fstring wks;
+
+ NTSTATUS nt_status;
+
+ struct auth_context *auth_context = NULL;
+ auth_usersupplied_info *user_info = NULL;
+ auth_serversupplied_info *server_info = NULL;
+
+ DEBUG(5,("api_pipe_ntlmssp_verify: checking user details\n"));
+
+ memset(p->user_name, '\0', sizeof(p->user_name));
+ memset(p->pipe_user_name, '\0', sizeof(p->pipe_user_name));
+ memset(p->domain, '\0', sizeof(p->domain));
+ memset(p->wks, '\0', sizeof(p->wks));
+
+ /* Set up for non-authenticated user. */
+ delete_nt_token(&p->pipe_user.nt_user_token);
+ p->pipe_user.ngroups = 0;
+ SAFE_FREE( p->pipe_user.groups);
+
+ /*
+ * Setup an empty password for a guest user.
+ */
+
+ /*
+ * We always negotiate UNICODE.
+ */
+
+ if (p->ntlmssp_chal_flags & NTLMSSP_NEGOTIATE_UNICODE) {
+ rpcstr_pull(user_name, ntlmssp_resp->user, sizeof(fstring), ntlmssp_resp->hdr_usr.str_str_len*2, 0 );
+ rpcstr_pull(domain, ntlmssp_resp->domain, sizeof(fstring), ntlmssp_resp->hdr_domain.str_str_len*2, 0);
+ rpcstr_pull(wks, ntlmssp_resp->wks, sizeof(fstring), ntlmssp_resp->hdr_wks.str_str_len*2, 0);
+ } else {
+ pull_ascii_fstring(user_name, ntlmssp_resp->user);
+ pull_ascii_fstring(domain, ntlmssp_resp->domain);
+ pull_ascii_fstring(wks, ntlmssp_resp->wks);
+ }
+
+ DEBUG(5,("user: %s domain: %s wks: %s\n", user_name, domain, wks));
+
+ nt_pw_len = MIN(sizeof(nt_owf), ntlmssp_resp->hdr_nt_resp.str_str_len);
+ lm_pw_len = MIN(sizeof(lm_owf), ntlmssp_resp->hdr_lm_resp.str_str_len);
+
+ memcpy(lm_owf, ntlmssp_resp->lm_resp, sizeof(lm_owf));
+ memcpy(nt_owf, ntlmssp_resp->nt_resp, nt_pw_len);
+
+#ifdef DEBUG_PASSWORD
+ DEBUG(100,("lm, nt owfs, chal\n"));
+ dump_data(100, (char *)lm_owf, sizeof(lm_owf));
+ dump_data(100, (char *)nt_owf, nt_pw_len);
+ dump_data(100, (char *)p->challenge, 8);
+#endif
+
+ /*
+ * Allow guest access. Patch from Shirish Kalele <kalele@veritas.com>.
+ */
+
+ if (*user_name) {
+
+ /*
+ * Do the length checking only if user is not NULL.
+ */
+
+ if (ntlmssp_resp->hdr_lm_resp.str_str_len == 0)
+ return False;
+ if (ntlmssp_resp->hdr_nt_resp.str_str_len == 0)
+ return False;
+ if (ntlmssp_resp->hdr_usr.str_str_len == 0)
+ return False;
+ if (ntlmssp_resp->hdr_domain.str_str_len == 0)
+ return False;
+ if (ntlmssp_resp->hdr_wks.str_str_len == 0)
+ return False;
+
+ }
+
+ make_auth_context_fixed(&auth_context, (uchar*)p->challenge);
+
+ if (!make_user_info_netlogon_network(&user_info,
+ user_name, domain, wks,
+ lm_owf, lm_pw_len,
+ nt_owf, nt_pw_len)) {
+ DEBUG(0,("make_user_info_netlogon_network failed! Failing authenticaion.\n"));
+ return False;
+ }
+
+ nt_status = auth_context->check_ntlm_password(auth_context, user_info, &server_info);
+
+ (auth_context->free)(&auth_context);
+ free_user_info(&user_info);
+
+ p->ntlmssp_auth_validated = NT_STATUS_IS_OK(nt_status);
+
+ if (!p->ntlmssp_auth_validated) {
+ DEBUG(1,("api_pipe_ntlmssp_verify: User [%s]\\[%s] from machine %s \
+failed authentication on named pipe %s.\n", domain, user_name, wks, p->name ));
+ free_server_info(&server_info);
+ return False;
+ }
+
+ /*
+ * Set up the sign/seal data.
+ */
+
+ if (server_info->lm_session_key.length != 16) {
+ DEBUG(1,("api_pipe_ntlmssp_verify: User [%s]\\[%s] from machine %s \
+succeeded authentication on named pipe %s, but session key was of incorrect length [%u].\n",
+ domain, user_name, wks, p->name, server_info->lm_session_key.length));
+ free_server_info(&server_info);
+ return False;
+ } else {
+ uchar p24[24];
+ NTLMSSPOWFencrypt(server_info->lm_session_key.data, lm_owf, p24);
+ {
+ unsigned char j = 0;
+ int ind;
+
+ unsigned char k2[8];
+
+ memcpy(k2, p24, 5);
+ k2[5] = 0xe5;
+ k2[6] = 0x38;
+ k2[7] = 0xb0;
+
+ for (ind = 0; ind < 256; ind++)
+ p->ntlmssp_hash[ind] = (unsigned char)ind;
+
+ for( ind = 0; ind < 256; ind++) {
+ unsigned char tc;
+
+ j += (p->ntlmssp_hash[ind] + k2[ind%8]);
+
+ tc = p->ntlmssp_hash[ind];
+ p->ntlmssp_hash[ind] = p->ntlmssp_hash[j];
+ p->ntlmssp_hash[j] = tc;
+ }
+
+ p->ntlmssp_hash[256] = 0;
+ p->ntlmssp_hash[257] = 0;
+ }
+
+ dump_data_pw("NTLMSSP hash (v1)\n", p->ntlmssp_hash,
+ sizeof(p->ntlmssp_hash));
+
+/* NTLMSSPhash(p->ntlmssp_hash, p24); */
+ p->ntlmssp_seq_num = 0;
+
+ }
+
+ fstrcpy(p->user_name, user_name);
+ fstrcpy(p->pipe_user_name, server_info->unix_name);
+ fstrcpy(p->domain, domain);
+ fstrcpy(p->wks, wks);
+
+ /*
+ * Store the UNIX credential data (uid/gid pair) in the pipe structure.
+ */
+
+ p->session_key = data_blob(server_info->lm_session_key.data, server_info->lm_session_key.length);
+
+ p->pipe_user.uid = server_info->uid;
+ p->pipe_user.gid = server_info->gid;
+
+ p->pipe_user.ngroups = server_info->n_groups;
+ if (p->pipe_user.ngroups) {
+ if (!(p->pipe_user.groups = memdup(server_info->groups, sizeof(gid_t) * p->pipe_user.ngroups))) {
+ DEBUG(0,("failed to memdup group list to p->pipe_user.groups\n"));
+ free_server_info(&server_info);
+ return False;
+ }
+ }
+
+ if (server_info->ptok)
+ p->pipe_user.nt_user_token = dup_nt_token(server_info->ptok);
+ else {
+ DEBUG(1,("Error: Authmodule failed to provide nt_user_token\n"));
+ p->pipe_user.nt_user_token = NULL;
+ free_server_info(&server_info);
+ return False;
+ }
+
+ p->ntlmssp_auth_validated = True;
+
+ free_server_info(&server_info);
+ return True;
+}
+
+/*******************************************************************
+ The switch table for the pipe names and the functions to handle them.
+ *******************************************************************/
+
+struct rpc_table
+{
+ struct
+ {
+ const char *clnt;
+ const char *srv;
+ } pipe;
+ struct api_struct *cmds;
+ int n_cmds;
+};
+
+static struct rpc_table *rpc_lookup;
+static int rpc_lookup_size;
+
+/*******************************************************************
+ This is the client reply to our challenge for an authenticated
+ bind request. The challenge we sent is in p->challenge.
+*******************************************************************/
+
+BOOL api_pipe_bind_auth_resp(pipes_struct *p, prs_struct *rpc_in_p)
+{
+ RPC_HDR_AUTHA autha_info;
+ RPC_AUTH_VERIFIER auth_verifier;
+ RPC_AUTH_NTLMSSP_RESP ntlmssp_resp;
+
+ DEBUG(5,("api_pipe_bind_auth_resp: decode request. %d\n", __LINE__));
+
+ if (p->hdr.auth_len == 0) {
+ DEBUG(0,("api_pipe_bind_auth_resp: No auth field sent !\n"));
+ return False;
+ }
+
+ /*
+ * Decode the authentication verifier response.
+ */
+
+ if(!smb_io_rpc_hdr_autha("", &autha_info, rpc_in_p, 0)) {
+ DEBUG(0,("api_pipe_bind_auth_resp: unmarshall of RPC_HDR_AUTHA failed.\n"));
+ return False;
+ }
+
+ if (autha_info.auth_type != NTLMSSP_AUTH_TYPE || autha_info.auth_level != RPC_PIPE_AUTH_SEAL_LEVEL) {
+ DEBUG(0,("api_pipe_bind_auth_resp: incorrect auth type (%d) or level (%d).\n",
+ (int)autha_info.auth_type, (int)autha_info.auth_level ));
+ return False;
+ }
+
+ if(!smb_io_rpc_auth_verifier("", &auth_verifier, rpc_in_p, 0)) {
+ DEBUG(0,("api_pipe_bind_auth_resp: unmarshall of RPC_AUTH_VERIFIER failed.\n"));
+ return False;
+ }
+
+ /*
+ * Ensure this is a NTLMSSP_AUTH packet type.
+ */
+
+ if (!rpc_auth_verifier_chk(&auth_verifier, "NTLMSSP", NTLMSSP_AUTH)) {
+ DEBUG(0,("api_pipe_bind_auth_resp: rpc_auth_verifier_chk failed.\n"));
+ return False;
+ }
+
+ if(!smb_io_rpc_auth_ntlmssp_resp("", &ntlmssp_resp, rpc_in_p, 0)) {
+ DEBUG(0,("api_pipe_bind_auth_resp: Failed to unmarshall RPC_AUTH_NTLMSSP_RESP.\n"));
+ return False;
+ }
+
+ /*
+ * The following call actually checks the challenge/response data.
+ * for correctness against the given DOMAIN\user name.
+ */
+
+ if (!api_pipe_ntlmssp_verify(p, &ntlmssp_resp))
+ return False;
+
+ p->pipe_bound = True
+;
+ return True;
+}
+
+/*******************************************************************
+ Marshall a bind_nak pdu.
+*******************************************************************/
+
+static BOOL setup_bind_nak(pipes_struct *p)
+{
+ prs_struct outgoing_rpc;
+ RPC_HDR nak_hdr;
+ uint16 zero = 0;
+
+ /* Free any memory in the current return data buffer. */
+ prs_mem_free(&p->out_data.rdata);
+
+ /*
+ * Marshall directly into the outgoing PDU space. We
+ * must do this as we need to set to the bind response
+ * header and are never sending more than one PDU here.
+ */
+
+ prs_init( &outgoing_rpc, 0, p->mem_ctx, MARSHALL);
+ prs_give_memory( &outgoing_rpc, (char *)p->out_data.current_pdu, sizeof(p->out_data.current_pdu), False);
+
+
+ /*
+ * Initialize a bind_nak header.
+ */
+
+ init_rpc_hdr(&nak_hdr, RPC_BINDNACK, RPC_FLG_FIRST | RPC_FLG_LAST,
+ p->hdr.call_id, RPC_HEADER_LEN + sizeof(uint16), 0);
+
+ /*
+ * Marshall the header into the outgoing PDU.
+ */
+
+ if(!smb_io_rpc_hdr("", &nak_hdr, &outgoing_rpc, 0)) {
+ DEBUG(0,("setup_bind_nak: marshalling of RPC_HDR failed.\n"));
+ prs_mem_free(&outgoing_rpc);
+ return False;
+ }
+
+ /*
+ * Now add the reject reason.
+ */
+
+ if(!prs_uint16("reject code", &outgoing_rpc, 0, &zero)) {
+ prs_mem_free(&outgoing_rpc);
+ return False;
+ }
+
+ p->out_data.data_sent_length = 0;
+ p->out_data.current_pdu_len = prs_offset(&outgoing_rpc);
+ p->out_data.current_pdu_sent = 0;
+
+ p->pipe_bound = False;
+
+ return True;
+}
+
+/*******************************************************************
+ Marshall a fault pdu.
+*******************************************************************/
+
+BOOL setup_fault_pdu(pipes_struct *p, NTSTATUS status)
+{
+ prs_struct outgoing_pdu;
+ RPC_HDR fault_hdr;
+ RPC_HDR_RESP hdr_resp;
+ RPC_HDR_FAULT fault_resp;
+
+ /* Free any memory in the current return data buffer. */
+ prs_mem_free(&p->out_data.rdata);
+
+ /*
+ * Marshall directly into the outgoing PDU space. We
+ * must do this as we need to set to the bind response
+ * header and are never sending more than one PDU here.
+ */
+
+ prs_init( &outgoing_pdu, 0, p->mem_ctx, MARSHALL);
+ prs_give_memory( &outgoing_pdu, (char *)p->out_data.current_pdu, sizeof(p->out_data.current_pdu), False);
+
+ /*
+ * Initialize a fault header.
+ */
+
+ init_rpc_hdr(&fault_hdr, RPC_FAULT, RPC_FLG_FIRST | RPC_FLG_LAST | RPC_FLG_NOCALL,
+ p->hdr.call_id, RPC_HEADER_LEN + RPC_HDR_RESP_LEN + RPC_HDR_FAULT_LEN, 0);
+
+ /*
+ * Initialize the HDR_RESP and FAULT parts of the PDU.
+ */
+
+ memset((char *)&hdr_resp, '\0', sizeof(hdr_resp));
+
+ fault_resp.status = status;
+ fault_resp.reserved = 0;
+
+ /*
+ * Marshall the header into the outgoing PDU.
+ */
+
+ if(!smb_io_rpc_hdr("", &fault_hdr, &outgoing_pdu, 0)) {
+ DEBUG(0,("setup_fault_pdu: marshalling of RPC_HDR failed.\n"));
+ prs_mem_free(&outgoing_pdu);
+ return False;
+ }
+
+ if(!smb_io_rpc_hdr_resp("resp", &hdr_resp, &outgoing_pdu, 0)) {
+ DEBUG(0,("setup_fault_pdu: failed to marshall RPC_HDR_RESP.\n"));
+ prs_mem_free(&outgoing_pdu);
+ return False;
+ }
+
+ if(!smb_io_rpc_hdr_fault("fault", &fault_resp, &outgoing_pdu, 0)) {
+ DEBUG(0,("setup_fault_pdu: failed to marshall RPC_HDR_FAULT.\n"));
+ prs_mem_free(&outgoing_pdu);
+ return False;
+ }
+
+ p->out_data.data_sent_length = 0;
+ p->out_data.current_pdu_len = prs_offset(&outgoing_pdu);
+ p->out_data.current_pdu_sent = 0;
+
+ prs_mem_free(&outgoing_pdu);
+ return True;
+}
+
+/*******************************************************************
+ Ensure a bind request has the correct abstract & transfer interface.
+ Used to reject unknown binds from Win2k.
+*******************************************************************/
+
+BOOL check_bind_req(struct pipes_struct *p, RPC_IFACE* abstract,
+ RPC_IFACE* transfer, uint32 context_id)
+{
+ extern struct pipe_id_info pipe_names[];
+ char *pipe_name = p->name;
+ int i=0;
+ fstring pname;
+
+ fstrcpy(pname,"\\PIPE\\");
+ fstrcat(pname,pipe_name);
+
+ DEBUG(3,("check_bind_req for %s\n", pname));
+
+ /* we have to check all now since win2k introduced a new UUID on the lsaprpc pipe */
+
+ for ( i=0; pipe_names[i].client_pipe; i++ )
+ {
+ if ( strequal(pipe_names[i].client_pipe, pname)
+ && (abstract->version == pipe_names[i].abstr_syntax.version)
+ && (memcmp(&abstract->uuid, &pipe_names[i].abstr_syntax.uuid, sizeof(struct uuid)) == 0)
+ && (transfer->version == pipe_names[i].trans_syntax.version)
+ && (memcmp(&transfer->uuid, &pipe_names[i].trans_syntax.uuid, sizeof(struct uuid)) == 0) )
+ {
+ struct api_struct *fns = NULL;
+ int n_fns = 0;
+ PIPE_RPC_FNS *context_fns;
+
+ if ( !(context_fns = malloc(sizeof(PIPE_RPC_FNS))) ) {
+ DEBUG(0,("check_bind_req: malloc() failed!\n"));
+ return False;
+ }
+
+ /* save the RPC function table associated with this bind */
+
+ get_pipe_fns(i, &fns, &n_fns);
+
+ context_fns->cmds = fns;
+ context_fns->n_cmds = n_fns;
+ context_fns->context_id = context_id;
+
+ /* add to the list of open contexts */
+
+ DLIST_ADD( p->contexts, context_fns );
+
+ break;
+ }
+ }
+
+ if(pipe_names[i].client_pipe == NULL)
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ Register commands to an RPC pipe
+*******************************************************************/
+NTSTATUS rpc_pipe_register_commands(int version, const char *clnt, const char *srv, const struct api_struct *cmds, int size)
+{
+ struct rpc_table *rpc_entry;
+
+ if (!clnt || !srv || !cmds) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ if (version != SMB_RPC_INTERFACE_VERSION) {
+ DEBUG(0,("Can't register rpc commands!\n"
+ "You tried to register a rpc module with SMB_RPC_INTERFACE_VERSION %d"
+ ", while this version of samba uses version %d!\n",
+ version,SMB_RPC_INTERFACE_VERSION));
+ return NT_STATUS_OBJECT_TYPE_MISMATCH;
+ }
+
+ /* TODO:
+ *
+ * we still need to make sure that don't register the same commands twice!!!
+ *
+ * --metze
+ */
+
+ /* We use a temporary variable because this call can fail and
+ rpc_lookup will still be valid afterwards. It could then succeed if
+ called again later */
+ rpc_entry = realloc(rpc_lookup,
+ ++rpc_lookup_size*sizeof(struct rpc_table));
+ if (NULL == rpc_entry) {
+ rpc_lookup_size--;
+ DEBUG(0, ("rpc_pipe_register_commands: memory allocation failed\n"));
+ return NT_STATUS_NO_MEMORY;
+ } else {
+ rpc_lookup = rpc_entry;
+ }
+
+ rpc_entry = rpc_lookup + (rpc_lookup_size - 1);
+ ZERO_STRUCTP(rpc_entry);
+ rpc_entry->pipe.clnt = strdup(clnt);
+ rpc_entry->pipe.srv = strdup(srv);
+ rpc_entry->cmds = realloc(rpc_entry->cmds,
+ (rpc_entry->n_cmds + size) *
+ sizeof(struct api_struct));
+ memcpy(rpc_entry->cmds + rpc_entry->n_cmds, cmds,
+ size * sizeof(struct api_struct));
+ rpc_entry->n_cmds += size;
+
+ return NT_STATUS_OK;
+}
+
+/*******************************************************************
+ Respond to a pipe bind request.
+*******************************************************************/
+
+BOOL api_pipe_bind_req(pipes_struct *p, prs_struct *rpc_in_p)
+{
+ RPC_HDR_BA hdr_ba;
+ RPC_HDR_RB hdr_rb;
+ RPC_HDR_AUTH auth_info;
+ uint16 assoc_gid;
+ fstring ack_pipe_name;
+ prs_struct out_hdr_ba;
+ prs_struct out_auth;
+ prs_struct outgoing_rpc;
+ int i = 0;
+ int auth_len = 0;
+ enum RPC_PKT_TYPE reply_pkt_type;
+
+ p->ntlmssp_auth_requested = False;
+ p->netsec_auth_validated = False;
+
+ DEBUG(5,("api_pipe_bind_req: decode request. %d\n", __LINE__));
+
+ /*
+ * Try and find the correct pipe name to ensure
+ * that this is a pipe name we support.
+ */
+
+
+ for (i = 0; i < rpc_lookup_size; i++) {
+ if (strequal(rpc_lookup[i].pipe.clnt, p->name)) {
+ DEBUG(3, ("api_pipe_bind_req: \\PIPE\\%s -> \\PIPE\\%s\n",
+ rpc_lookup[i].pipe.clnt, rpc_lookup[i].pipe.srv));
+ fstrcpy(p->pipe_srv_name, rpc_lookup[i].pipe.srv);
+ break;
+ }
+ }
+
+ if (i == rpc_lookup_size) {
+ if (NT_STATUS_IS_ERR(smb_probe_module("rpc", p->name))) {
+ DEBUG(3,("api_pipe_bind_req: Unknown pipe name %s in bind request.\n",
+ p->name ));
+ if(!setup_bind_nak(p))
+ return False;
+ return True;
+ }
+
+ for (i = 0; i < rpc_lookup_size; i++) {
+ if (strequal(rpc_lookup[i].pipe.clnt, p->name)) {
+ DEBUG(3, ("api_pipe_bind_req: \\PIPE\\%s -> \\PIPE\\%s\n",
+ rpc_lookup[i].pipe.clnt, rpc_lookup[i].pipe.srv));
+ fstrcpy(p->pipe_srv_name, rpc_lookup[i].pipe.srv);
+ break;
+ }
+ }
+
+ if (i == rpc_lookup_size) {
+ DEBUG(0, ("module %s doesn't provide functions for pipe %s!\n", p->name, p->name));
+ return False;
+ }
+ }
+
+ /* decode the bind request */
+ if(!smb_io_rpc_hdr_rb("", &hdr_rb, rpc_in_p, 0)) {
+ DEBUG(0,("api_pipe_bind_req: unable to unmarshall RPC_HDR_RB struct.\n"));
+ return False;
+ }
+
+ /*
+ * Check if this is an authenticated request.
+ */
+
+ if (p->hdr.auth_len != 0) {
+ RPC_AUTH_VERIFIER auth_verifier;
+ RPC_AUTH_NTLMSSP_NEG ntlmssp_neg;
+
+ /*
+ * Decode the authentication verifier.
+ */
+
+ if(!smb_io_rpc_hdr_auth("", &auth_info, rpc_in_p, 0)) {
+ DEBUG(0,("api_pipe_bind_req: unable to unmarshall RPC_HDR_AUTH struct.\n"));
+ return False;
+ }
+
+ if(auth_info.auth_type == NTLMSSP_AUTH_TYPE) {
+
+ if(!smb_io_rpc_auth_verifier("", &auth_verifier, rpc_in_p, 0)) {
+ DEBUG(0,("api_pipe_bind_req: unable to "
+ "unmarshall RPC_HDR_AUTH struct.\n"));
+ return False;
+ }
+
+ if(!strequal(auth_verifier.signature, "NTLMSSP")) {
+ DEBUG(0,("api_pipe_bind_req: "
+ "auth_verifier.signature != NTLMSSP\n"));
+ return False;
+ }
+
+ if(auth_verifier.msg_type != NTLMSSP_NEGOTIATE) {
+ DEBUG(0,("api_pipe_bind_req: "
+ "auth_verifier.msg_type (%d) != NTLMSSP_NEGOTIATE\n",
+ auth_verifier.msg_type));
+ return False;
+ }
+
+ if(!smb_io_rpc_auth_ntlmssp_neg("", &ntlmssp_neg, rpc_in_p, 0)) {
+ DEBUG(0,("api_pipe_bind_req: "
+ "Failed to unmarshall RPC_AUTH_NTLMSSP_NEG.\n"));
+ return False;
+ }
+
+ p->ntlmssp_chal_flags = SMBD_NTLMSSP_NEG_FLAGS;
+ p->ntlmssp_auth_requested = True;
+
+ } else if (auth_info.auth_type == NETSEC_AUTH_TYPE) {
+
+ RPC_AUTH_NETSEC_NEG neg;
+ struct netsec_auth_struct *a = &(p->netsec_auth);
+
+ if (!smb_io_rpc_auth_netsec_neg("", &neg, rpc_in_p, 0)) {
+ DEBUG(0,("api_pipe_bind_req: "
+ "Could not unmarshal SCHANNEL auth neg\n"));
+ return False;
+ }
+
+ p->netsec_auth_validated = True;
+
+ memset(a->sess_key, 0, sizeof(a->sess_key));
+ memcpy(a->sess_key, last_dcinfo.sess_key, sizeof(last_dcinfo.sess_key));
+
+ a->seq_num = 0;
+
+ DEBUG(10,("schannel auth: domain [%s] myname [%s]\n",
+ neg.domain, neg.myname));
+
+ } else {
+ DEBUG(0,("api_pipe_bind_req: unknown auth type %x requested.\n",
+ auth_info.auth_type ));
+ return False;
+ }
+ }
+
+ switch(p->hdr.pkt_type) {
+ case RPC_BIND:
+ /* name has to be \PIPE\xxxxx */
+ fstrcpy(ack_pipe_name, "\\PIPE\\");
+ fstrcat(ack_pipe_name, p->pipe_srv_name);
+ reply_pkt_type = RPC_BINDACK;
+ break;
+ case RPC_ALTCONT:
+ /* secondary address CAN be NULL
+ * as the specs say it's ignored.
+ * It MUST NULL to have the spoolss working.
+ */
+ fstrcpy(ack_pipe_name,"");
+ reply_pkt_type = RPC_ALTCONTRESP;
+ break;
+ default:
+ return False;
+ }
+
+ DEBUG(5,("api_pipe_bind_req: make response. %d\n", __LINE__));
+
+ /*
+ * Marshall directly into the outgoing PDU space. We
+ * must do this as we need to set to the bind response
+ * header and are never sending more than one PDU here.
+ */
+
+ prs_init( &outgoing_rpc, 0, p->mem_ctx, MARSHALL);
+ prs_give_memory( &outgoing_rpc, (char *)p->out_data.current_pdu, sizeof(p->out_data.current_pdu), False);
+
+ /*
+ * Setup the memory to marshall the ba header, and the
+ * auth footers.
+ */
+
+ if(!prs_init(&out_hdr_ba, 1024, p->mem_ctx, MARSHALL)) {
+ DEBUG(0,("api_pipe_bind_req: malloc out_hdr_ba failed.\n"));
+ prs_mem_free(&outgoing_rpc);
+ return False;
+ }
+
+ if(!prs_init(&out_auth, 1024, p->mem_ctx, MARSHALL)) {
+ DEBUG(0,("pi_pipe_bind_req: malloc out_auth failed.\n"));
+ prs_mem_free(&outgoing_rpc);
+ prs_mem_free(&out_hdr_ba);
+ return False;
+ }
+
+ if (p->ntlmssp_auth_requested)
+ assoc_gid = 0x7a77;
+ else
+ assoc_gid = hdr_rb.bba.assoc_gid ? hdr_rb.bba.assoc_gid : 0x53f0;
+
+ /*
+ * Create the bind response struct.
+ */
+
+ /* If the requested abstract synt uuid doesn't match our client pipe,
+ reject the bind_ack & set the transfer interface synt to all 0's,
+ ver 0 (observed when NT5 attempts to bind to abstract interfaces
+ unknown to NT4)
+ Needed when adding entries to a DACL from NT5 - SK */
+
+ if(check_bind_req(p, &hdr_rb.abstract, &hdr_rb.transfer, hdr_rb.context_id ))
+ {
+ init_rpc_hdr_ba(&hdr_ba,
+ MAX_PDU_FRAG_LEN,
+ MAX_PDU_FRAG_LEN,
+ assoc_gid,
+ ack_pipe_name,
+ 0x1, 0x0, 0x0,
+ &hdr_rb.transfer);
+ } else {
+ RPC_IFACE null_interface;
+ ZERO_STRUCT(null_interface);
+ /* Rejection reason: abstract syntax not supported */
+ init_rpc_hdr_ba(&hdr_ba, MAX_PDU_FRAG_LEN,
+ MAX_PDU_FRAG_LEN, assoc_gid,
+ ack_pipe_name, 0x1, 0x2, 0x1,
+ &null_interface);
+ }
+
+ /*
+ * and marshall it.
+ */
+
+ if(!smb_io_rpc_hdr_ba("", &hdr_ba, &out_hdr_ba, 0)) {
+ DEBUG(0,("api_pipe_bind_req: marshalling of RPC_HDR_BA failed.\n"));
+ goto err_exit;
+ }
+
+ /*
+ * Now the authentication.
+ */
+
+ if (p->ntlmssp_auth_requested) {
+ RPC_AUTH_VERIFIER auth_verifier;
+ RPC_AUTH_NTLMSSP_CHAL ntlmssp_chal;
+
+ generate_random_buffer(p->challenge, 8, False);
+
+ /*** Authentication info ***/
+
+ init_rpc_hdr_auth(&auth_info, NTLMSSP_AUTH_TYPE, RPC_PIPE_AUTH_SEAL_LEVEL, RPC_HDR_AUTH_LEN, 1);
+ if(!smb_io_rpc_hdr_auth("", &auth_info, &out_auth, 0)) {
+ DEBUG(0,("api_pipe_bind_req: marshalling of RPC_HDR_AUTH failed.\n"));
+ goto err_exit;
+ }
+
+ /*** NTLMSSP verifier ***/
+
+ init_rpc_auth_verifier(&auth_verifier, "NTLMSSP", NTLMSSP_CHALLENGE);
+ if(!smb_io_rpc_auth_verifier("", &auth_verifier, &out_auth, 0)) {
+ DEBUG(0,("api_pipe_bind_req: marshalling of RPC_AUTH_VERIFIER failed.\n"));
+ goto err_exit;
+ }
+
+ /* NTLMSSP challenge ***/
+
+ init_rpc_auth_ntlmssp_chal(&ntlmssp_chal, p->ntlmssp_chal_flags, p->challenge);
+ if(!smb_io_rpc_auth_ntlmssp_chal("", &ntlmssp_chal, &out_auth, 0)) {
+ DEBUG(0,("api_pipe_bind_req: marshalling of RPC_AUTH_NTLMSSP_CHAL failed.\n"));
+ goto err_exit;
+ }
+
+ /* Auth len in the rpc header doesn't include auth_header. */
+ auth_len = prs_offset(&out_auth) - RPC_HDR_AUTH_LEN;
+ }
+
+ if (p->netsec_auth_validated) {
+ RPC_AUTH_VERIFIER auth_verifier;
+ uint32 flags;
+
+ /* The client opens a second RPC NETLOGON pipe without
+ doing a auth2. The credentials for the schannel are
+ re-used from the auth2 the client did before. */
+ p->dc = last_dcinfo;
+
+ init_rpc_hdr_auth(&auth_info, NETSEC_AUTH_TYPE, RPC_PIPE_AUTH_SEAL_LEVEL, RPC_HDR_AUTH_LEN, 1);
+ if(!smb_io_rpc_hdr_auth("", &auth_info, &out_auth, 0)) {
+ DEBUG(0,("api_pipe_bind_req: marshalling of RPC_HDR_AUTH failed.\n"));
+ goto err_exit;
+ }
+
+ /*** NETSEC verifier ***/
+
+ init_rpc_auth_verifier(&auth_verifier, "\001", 0x0);
+ if(!smb_io_rpc_netsec_verifier("", &auth_verifier, &out_auth, 0)) {
+ DEBUG(0,("api_pipe_bind_req: marshalling of RPC_AUTH_VERIFIER failed.\n"));
+ goto err_exit;
+ }
+
+ prs_align(&out_auth);
+
+ flags = 5;
+ if(!prs_uint32("flags ", &out_auth, 0, &flags))
+ goto err_exit;
+
+ auth_len = prs_offset(&out_auth) - RPC_HDR_AUTH_LEN;
+ }
+
+ /*
+ * Create the header, now we know the length.
+ */
+
+ init_rpc_hdr(&p->hdr, reply_pkt_type, RPC_FLG_FIRST | RPC_FLG_LAST,
+ p->hdr.call_id,
+ RPC_HEADER_LEN + prs_offset(&out_hdr_ba) + prs_offset(&out_auth),
+ auth_len);
+
+ /*
+ * Marshall the header into the outgoing PDU.
+ */
+
+ if(!smb_io_rpc_hdr("", &p->hdr, &outgoing_rpc, 0)) {
+ DEBUG(0,("pi_pipe_bind_req: marshalling of RPC_HDR failed.\n"));
+ goto err_exit;
+ }
+
+ /*
+ * Now add the RPC_HDR_BA and any auth needed.
+ */
+
+ if(!prs_append_prs_data( &outgoing_rpc, &out_hdr_ba)) {
+ DEBUG(0,("api_pipe_bind_req: append of RPC_HDR_BA failed.\n"));
+ goto err_exit;
+ }
+
+ if((p->ntlmssp_auth_requested|p->netsec_auth_validated) &&
+ !prs_append_prs_data( &outgoing_rpc, &out_auth)) {
+ DEBUG(0,("api_pipe_bind_req: append of auth info failed.\n"));
+ goto err_exit;
+ }
+
+ if(!p->ntlmssp_auth_requested)
+ p->pipe_bound = True;
+
+ /*
+ * Setup the lengths for the initial reply.
+ */
+
+ p->out_data.data_sent_length = 0;
+ p->out_data.current_pdu_len = prs_offset(&outgoing_rpc);
+ p->out_data.current_pdu_sent = 0;
+
+ prs_mem_free(&out_hdr_ba);
+ prs_mem_free(&out_auth);
+
+ return True;
+
+ err_exit:
+
+ prs_mem_free(&outgoing_rpc);
+ prs_mem_free(&out_hdr_ba);
+ prs_mem_free(&out_auth);
+ return False;
+}
+
+/****************************************************************************
+ Deal with sign & seal processing on an RPC request.
+****************************************************************************/
+
+BOOL api_pipe_auth_process(pipes_struct *p, prs_struct *rpc_in)
+{
+ /*
+ * We always negotiate the following two bits....
+ */
+ BOOL auth_verify = ((p->ntlmssp_chal_flags & NTLMSSP_NEGOTIATE_SIGN) != 0);
+ BOOL auth_seal = ((p->ntlmssp_chal_flags & NTLMSSP_NEGOTIATE_SEAL) != 0);
+ int data_len;
+ int auth_len;
+ uint32 old_offset;
+ uint32 crc32 = 0;
+
+ auth_len = p->hdr.auth_len;
+
+ if ((auth_len != RPC_AUTH_NTLMSSP_CHK_LEN) && auth_verify) {
+ DEBUG(0,("api_pipe_auth_process: Incorrect auth_len %d.\n", auth_len ));
+ return False;
+ }
+
+ /*
+ * The following is that length of the data we must verify or unseal.
+ * This doesn't include the RPC headers or the auth_len or the RPC_HDR_AUTH_LEN
+ * preceeding the auth_data.
+ */
+
+ data_len = p->hdr.frag_len - RPC_HEADER_LEN - RPC_HDR_REQ_LEN -
+ (auth_verify ? RPC_HDR_AUTH_LEN : 0) - auth_len;
+
+ DEBUG(5,("api_pipe_auth_process: sign: %s seal: %s data %d auth %d\n",
+ BOOLSTR(auth_verify), BOOLSTR(auth_seal), data_len, auth_len));
+
+ if (auth_seal) {
+ /*
+ * The data in rpc_in doesn't contain the RPC_HEADER as this
+ * has already been consumed.
+ */
+ char *data = prs_data_p(rpc_in) + RPC_HDR_REQ_LEN;
+ dump_data_pw("NTLMSSP hash (v1)\n", p->ntlmssp_hash,
+ sizeof(p->ntlmssp_hash));
+
+ dump_data_pw("Incoming RPC PDU (NTLMSSP sealed)\n",
+ (const unsigned char *)data, data_len);
+ NTLMSSPcalc_p(p, (uchar*)data, data_len);
+ dump_data_pw("Incoming RPC PDU (NTLMSSP unsealed)\n",
+ (const unsigned char *)data, data_len);
+ crc32 = crc32_calc_buffer(data, data_len);
+ }
+
+ old_offset = prs_offset(rpc_in);
+
+ if (auth_seal || auth_verify) {
+ RPC_HDR_AUTH auth_info;
+
+ if(!prs_set_offset(rpc_in, old_offset + data_len)) {
+ DEBUG(0,("api_pipe_auth_process: cannot move offset to %u.\n",
+ (unsigned int)old_offset + data_len ));
+ return False;
+ }
+
+ if(!smb_io_rpc_hdr_auth("hdr_auth", &auth_info, rpc_in, 0)) {
+ DEBUG(0,("api_pipe_auth_process: failed to unmarshall RPC_HDR_AUTH.\n"));
+ return False;
+ }
+ }
+
+ if (auth_verify) {
+ RPC_AUTH_NTLMSSP_CHK ntlmssp_chk;
+ char *req_data = prs_data_p(rpc_in) + prs_offset(rpc_in) + 4;
+
+ DEBUG(5,("api_pipe_auth_process: auth %d\n", prs_offset(rpc_in) + 4));
+
+ /*
+ * Ensure we have RPC_AUTH_NTLMSSP_CHK_LEN - 4 more bytes in the
+ * incoming buffer.
+ */
+ if(prs_mem_get(rpc_in, RPC_AUTH_NTLMSSP_CHK_LEN - 4) == NULL) {
+ DEBUG(0,("api_pipe_auth_process: missing %d bytes in buffer.\n",
+ RPC_AUTH_NTLMSSP_CHK_LEN - 4 ));
+ return False;
+ }
+
+ NTLMSSPcalc_p(p, (uchar*)req_data, RPC_AUTH_NTLMSSP_CHK_LEN - 4);
+ if(!smb_io_rpc_auth_ntlmssp_chk("auth_sign", &ntlmssp_chk, rpc_in, 0)) {
+ DEBUG(0,("api_pipe_auth_process: failed to unmarshall RPC_AUTH_NTLMSSP_CHK.\n"));
+ return False;
+ }
+
+ if (!rpc_auth_ntlmssp_chk(&ntlmssp_chk, crc32, p->ntlmssp_seq_num)) {
+ DEBUG(0,("api_pipe_auth_process: NTLMSSP check failed.\n"));
+ return False;
+ }
+ }
+
+ /*
+ * Return the current pointer to the data offset.
+ */
+
+ if(!prs_set_offset(rpc_in, old_offset)) {
+ DEBUG(0,("api_pipe_auth_process: failed to set offset back to %u\n",
+ (unsigned int)old_offset ));
+ return False;
+ }
+
+ return True;
+}
+
+/****************************************************************************
+ Deal with schannel processing on an RPC request.
+****************************************************************************/
+BOOL api_pipe_netsec_process(pipes_struct *p, prs_struct *rpc_in)
+{
+ /*
+ * We always negotiate the following two bits....
+ */
+ int data_len;
+ int auth_len;
+ uint32 old_offset;
+ RPC_HDR_AUTH auth_info;
+ RPC_AUTH_NETSEC_CHK netsec_chk;
+
+
+ auth_len = p->hdr.auth_len;
+
+ if (auth_len != RPC_AUTH_NETSEC_CHK_LEN) {
+ DEBUG(0,("Incorrect auth_len %d.\n", auth_len ));
+ return False;
+ }
+
+ /*
+ * The following is that length of the data we must verify or unseal.
+ * This doesn't include the RPC headers or the auth_len or the RPC_HDR_AUTH_LEN
+ * preceeding the auth_data.
+ */
+
+ data_len = p->hdr.frag_len - RPC_HEADER_LEN - RPC_HDR_REQ_LEN -
+ RPC_HDR_AUTH_LEN - auth_len;
+
+ DEBUG(5,("data %d auth %d\n", data_len, auth_len));
+
+ old_offset = prs_offset(rpc_in);
+
+ if(!prs_set_offset(rpc_in, old_offset + data_len)) {
+ DEBUG(0,("cannot move offset to %u.\n",
+ (unsigned int)old_offset + data_len ));
+ return False;
+ }
+
+ if(!smb_io_rpc_hdr_auth("hdr_auth", &auth_info, rpc_in, 0)) {
+ DEBUG(0,("failed to unmarshall RPC_HDR_AUTH.\n"));
+ return False;
+ }
+
+ if (auth_info.auth_type != NETSEC_AUTH_TYPE) {
+ DEBUG(0,("Invalid auth info %d on schannel\n",
+ auth_info.auth_type));
+ return False;
+ }
+
+ if (auth_info.auth_level == RPC_PIPE_AUTH_SEAL_LEVEL) {
+ p->netsec_auth.auth_flags = AUTH_PIPE_NETSEC|AUTH_PIPE_SIGN|AUTH_PIPE_SEAL;
+ } else if (auth_info.auth_level == RPC_PIPE_AUTH_SIGN_LEVEL) {
+ p->netsec_auth.auth_flags = AUTH_PIPE_NETSEC|AUTH_PIPE_SIGN;
+ } else {
+ DEBUG(0,("Invalid auth level %d on schannel\n",
+ auth_info.auth_level));
+ return False;
+ }
+
+ if(!smb_io_rpc_auth_netsec_chk("", &netsec_chk, rpc_in, 0)) {
+ DEBUG(0,("failed to unmarshal RPC_AUTH_NETSEC_CHK.\n"));
+ return False;
+ }
+
+ if (!netsec_decode(&p->netsec_auth,
+ p->netsec_auth.auth_flags,
+ SENDER_IS_INITIATOR,
+ &netsec_chk,
+ prs_data_p(rpc_in)+old_offset, data_len)) {
+ DEBUG(0,("failed to decode PDU\n"));
+ return False;
+ }
+
+ /*
+ * Return the current pointer to the data offset.
+ */
+
+ if(!prs_set_offset(rpc_in, old_offset)) {
+ DEBUG(0,("failed to set offset back to %u\n",
+ (unsigned int)old_offset ));
+ return False;
+ }
+
+ /* The sequence number gets incremented on both send and receive. */
+ p->netsec_auth.seq_num++;
+
+ return True;
+}
+
+/****************************************************************************
+ Return a user struct for a pipe user.
+****************************************************************************/
+
+struct current_user *get_current_user(struct current_user *user, pipes_struct *p)
+{
+ if (p->ntlmssp_auth_validated) {
+ memcpy(user, &p->pipe_user, sizeof(struct current_user));
+ } else {
+ extern struct current_user current_user;
+ memcpy(user, &current_user, sizeof(struct current_user));
+ }
+
+ return user;
+}
+
+/****************************************************************************
+ Find the set of RPC functions associated with this context_id
+****************************************************************************/
+
+static PIPE_RPC_FNS* find_pipe_fns_by_context( PIPE_RPC_FNS *list, uint32 context_id )
+{
+ PIPE_RPC_FNS *fns = NULL;
+ PIPE_RPC_FNS *tmp = NULL;
+
+ if ( !list ) {
+ DEBUG(0,("find_pipe_fns_by_context: ERROR! No context list for pipe!\n"));
+ return NULL;
+ }
+
+ for (tmp=list; tmp; tmp=tmp->next ) {
+ if ( tmp->context_id == context_id )
+ break;
+ }
+
+ fns = tmp;
+
+ return fns;
+}
+
+/****************************************************************************
+ memory cleanup
+****************************************************************************/
+
+void free_pipe_rpc_context( PIPE_RPC_FNS *list )
+{
+ PIPE_RPC_FNS *tmp = list;
+ PIPE_RPC_FNS *tmp2;
+
+ while (tmp) {
+ tmp2 = tmp->next;
+ SAFE_FREE(tmp);
+ tmp = tmp2;
+ }
+
+ return;
+}
+
+/****************************************************************************
+ Find the correct RPC function to call for this request.
+ If the pipe is authenticated then become the correct UNIX user
+ before doing the call.
+****************************************************************************/
+
+BOOL api_pipe_request(pipes_struct *p)
+{
+ BOOL ret = False;
+ PIPE_RPC_FNS *pipe_fns;
+
+ if (p->ntlmssp_auth_validated) {
+
+ if(!become_authenticated_pipe_user(p)) {
+ prs_mem_free(&p->out_data.rdata);
+ return False;
+ }
+ }
+
+ DEBUG(5, ("Requested \\PIPE\\%s\n", p->name));
+
+ /* get the set of RPC functions for this context */
+
+ pipe_fns = find_pipe_fns_by_context(p->contexts, p->hdr_req.context_id);
+
+ if ( pipe_fns ) {
+ set_current_rpc_talloc(p->mem_ctx);
+ ret = api_rpcTNP(p, p->name, pipe_fns->cmds, pipe_fns->n_cmds);
+ set_current_rpc_talloc(NULL);
+ }
+ else {
+ DEBUG(0,("api_pipe_request: No rpc function table associated with context [%d] on pipe [%s]\n",
+ p->hdr_req.context_id, p->name));
+ }
+
+ if(p->ntlmssp_auth_validated)
+ unbecome_authenticated_pipe_user();
+
+ return ret;
+}
+
+/*******************************************************************
+ Calls the underlying RPC function for a named pipe.
+ ********************************************************************/
+
+BOOL api_rpcTNP(pipes_struct *p, const char *rpc_name,
+ const struct api_struct *api_rpc_cmds, int n_cmds)
+{
+ int fn_num;
+ fstring name;
+ uint32 offset1, offset2;
+
+ /* interpret the command */
+ DEBUG(4,("api_rpcTNP: %s op 0x%x - ", rpc_name, p->hdr_req.opnum));
+
+ slprintf(name, sizeof(name)-1, "in_%s", rpc_name);
+ prs_dump(name, p->hdr_req.opnum, &p->in_data.data);
+
+ for (fn_num = 0; fn_num < n_cmds; fn_num++) {
+ if (api_rpc_cmds[fn_num].opnum == p->hdr_req.opnum && api_rpc_cmds[fn_num].fn != NULL) {
+ DEBUG(3,("api_rpcTNP: rpc command: %s\n", api_rpc_cmds[fn_num].name));
+ break;
+ }
+ }
+
+ if (fn_num == n_cmds) {
+ /*
+ * For an unknown RPC just return a fault PDU but
+ * return True to allow RPC's on the pipe to continue
+ * and not put the pipe into fault state. JRA.
+ */
+ DEBUG(4, ("unknown\n"));
+ setup_fault_pdu(p, NT_STATUS(0x1c010002));
+ return True;
+ }
+
+ offset1 = prs_offset(&p->out_data.rdata);
+
+ DEBUG(6, ("api_rpc_cmds[%d].fn == %p\n",
+ fn_num, api_rpc_cmds[fn_num].fn));
+ /* do the actual command */
+ if(!api_rpc_cmds[fn_num].fn(p)) {
+ DEBUG(0,("api_rpcTNP: %s: %s failed.\n", rpc_name, api_rpc_cmds[fn_num].name));
+ prs_mem_free(&p->out_data.rdata);
+ return False;
+ }
+
+ if (p->bad_handle_fault_state) {
+ DEBUG(4,("api_rpcTNP: bad handle fault return.\n"));
+ p->bad_handle_fault_state = False;
+ setup_fault_pdu(p, NT_STATUS(0x1C00001A));
+ return True;
+ }
+
+ slprintf(name, sizeof(name)-1, "out_%s", rpc_name);
+ offset2 = prs_offset(&p->out_data.rdata);
+ prs_set_offset(&p->out_data.rdata, offset1);
+ prs_dump(name, p->hdr_req.opnum, &p->out_data.rdata);
+ prs_set_offset(&p->out_data.rdata, offset2);
+
+ DEBUG(5,("api_rpcTNP: called %s successfully\n", rpc_name));
+
+ /* Check for buffer underflow in rpc parsing */
+
+ if ((DEBUGLEVEL >= 10) &&
+ (prs_offset(&p->in_data.data) != prs_data_size(&p->in_data.data))) {
+ size_t data_len = prs_data_size(&p->in_data.data) - prs_offset(&p->in_data.data);
+ char *data;
+
+ data = malloc(data_len);
+
+ DEBUG(10, ("api_rpcTNP: rpc input buffer underflow (parse error?)\n"));
+ if (data) {
+ prs_uint8s(False, "", &p->in_data.data, 0, (unsigned char *)data, (uint32)data_len);
+ SAFE_FREE(data);
+ }
+
+ }
+
+ return True;
+}
+
+/*******************************************************************
+*******************************************************************/
+
+void get_pipe_fns( int idx, struct api_struct **fns, int *n_fns )
+{
+ struct api_struct *cmds = NULL;
+ int n_cmds = 0;
+
+ switch ( idx ) {
+ case PI_LSARPC:
+ lsa_get_pipe_fns( &cmds, &n_cmds );
+ break;
+ case PI_LSARPC_DS:
+ lsa_ds_get_pipe_fns( &cmds, &n_cmds );
+ break;
+ case PI_SAMR:
+ samr_get_pipe_fns( &cmds, &n_cmds );
+ break;
+ case PI_NETLOGON:
+ netlog_get_pipe_fns( &cmds, &n_cmds );
+ break;
+ case PI_SRVSVC:
+ srvsvc_get_pipe_fns( &cmds, &n_cmds );
+ break;
+ case PI_WKSSVC:
+ wkssvc_get_pipe_fns( &cmds, &n_cmds );
+ break;
+ case PI_WINREG:
+ reg_get_pipe_fns( &cmds, &n_cmds );
+ break;
+ case PI_SPOOLSS:
+ spoolss_get_pipe_fns( &cmds, &n_cmds );
+ break;
+ case PI_NETDFS:
+ netdfs_get_pipe_fns( &cmds, &n_cmds );
+ break;
+#ifdef DEVELOPER
+ case PI_ECHO:
+ echo_get_pipe_fns( &cmds, &n_cmds );
+ break;
+#endif
+ case PI_EPM:
+ epm_get_pipe_fns( &cmds, &n_cmds );
+ break;
+ default:
+ DEBUG(0,("get_pipe_fns: Unknown pipe index! [%d]\n", idx));
+ }
+
+ *fns = cmds;
+ *n_fns = n_cmds;
+
+ return;
+}
+
+
diff --git a/source/rpc_server/srv_pipe_hnd.c b/source/rpc_server/srv_pipe_hnd.c
new file mode 100644
index 00000000000..64ca8388d77
--- /dev/null
+++ b/source/rpc_server/srv_pipe_hnd.c
@@ -0,0 +1,1169 @@
+/*
+ * Unix SMB/CIFS implementation.
+ * RPC Pipe client / server routines
+ * Copyright (C) Andrew Tridgell 1992-1998,
+ * Copyright (C) Luke Kenneth Casson Leighton 1996-1998,
+ * Copyright (C) Jeremy Allison 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 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"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_RPC_SRV
+
+#define PIPE "\\PIPE\\"
+#define PIPELEN strlen(PIPE)
+
+static smb_np_struct *chain_p;
+static int pipes_open;
+
+/*
+ * Sometimes I can't decide if I hate Windows printer driver
+ * writers more than I hate the Windows spooler service driver
+ * writers. This gets around a combination of bugs in the spooler
+ * and the HP 8500 PCL driver that causes a spooler spin. JRA.
+ *
+ * bumped up from 20 -> 64 after viewing traffic from WordPerfect
+ * 2002 running on NT 4.- SP6
+ * bumped up from 64 -> 256 after viewing traffic from con2prt
+ * for lots of printers on a WinNT 4.x SP6 box.
+ */
+
+#ifndef MAX_OPEN_SPOOLSS_PIPES
+#define MAX_OPEN_SPOOLSS_PIPES 256
+#endif
+static int current_spoolss_pipes_open;
+
+static smb_np_struct *Pipes;
+static pipes_struct *InternalPipes;
+static struct bitmap *bmap;
+
+/* TODO
+ * the following prototypes are declared here to avoid
+ * code being moved about too much for a patch to be
+ * disrupted / less obvious.
+ *
+ * these functions, and associated functions that they
+ * call, should be moved behind a .so module-loading
+ * system _anyway_. so that's the next step...
+ */
+
+static ssize_t read_from_internal_pipe(void *np_conn, char *data, size_t n,
+ BOOL *is_data_outstanding);
+static ssize_t write_to_internal_pipe(void *np_conn, char *data, size_t n);
+static BOOL close_internal_rpc_pipe_hnd(void *np_conn);
+static void *make_internal_rpc_pipe_p(char *pipe_name,
+ connection_struct *conn, uint16 vuid);
+
+/****************************************************************************
+ Pipe iterator functions.
+****************************************************************************/
+
+smb_np_struct *get_first_pipe(void)
+{
+ return Pipes;
+}
+
+smb_np_struct *get_next_pipe(smb_np_struct *p)
+{
+ return p->next;
+}
+
+/****************************************************************************
+ Internal Pipe iterator functions.
+****************************************************************************/
+
+pipes_struct *get_first_internal_pipe(void)
+{
+ return InternalPipes;
+}
+
+pipes_struct *get_next_internal_pipe(pipes_struct *p)
+{
+ return p->next;
+}
+
+/* this must be larger than the sum of the open files and directories */
+static int pipe_handle_offset;
+
+/****************************************************************************
+ Set the pipe_handle_offset. Called from smbd/files.c
+****************************************************************************/
+
+void set_pipe_handle_offset(int max_open_files)
+{
+ if(max_open_files < 0x7000)
+ pipe_handle_offset = 0x7000;
+ else
+ pipe_handle_offset = max_open_files + 10; /* For safety. :-) */
+}
+
+/****************************************************************************
+ Reset pipe chain handle number.
+****************************************************************************/
+
+void reset_chain_p(void)
+{
+ chain_p = NULL;
+}
+
+/****************************************************************************
+ Initialise pipe handle states.
+****************************************************************************/
+
+void init_rpc_pipe_hnd(void)
+{
+ bmap = bitmap_allocate(MAX_OPEN_PIPES);
+ if (!bmap)
+ exit_server("out of memory in init_rpc_pipe_hnd");
+}
+
+/****************************************************************************
+ Initialise an outgoing packet.
+****************************************************************************/
+
+static BOOL pipe_init_outgoing_data(pipes_struct *p)
+{
+ output_data *o_data = &p->out_data;
+
+ /* Reset the offset counters. */
+ o_data->data_sent_length = 0;
+ o_data->current_pdu_len = 0;
+ o_data->current_pdu_sent = 0;
+
+ memset(o_data->current_pdu, '\0', sizeof(o_data->current_pdu));
+
+ /* Free any memory in the current return data buffer. */
+ prs_mem_free(&o_data->rdata);
+
+ /*
+ * Initialize the outgoing RPC data buffer.
+ * we will use this as the raw data area for replying to rpc requests.
+ */
+ if(!prs_init(&o_data->rdata, MAX_PDU_FRAG_LEN, p->mem_ctx, MARSHALL)) {
+ DEBUG(0,("pipe_init_outgoing_data: malloc fail.\n"));
+ return False;
+ }
+
+ return True;
+}
+
+/****************************************************************************
+ Find first available pipe slot.
+****************************************************************************/
+
+smb_np_struct *open_rpc_pipe_p(char *pipe_name,
+ connection_struct *conn, uint16 vuid)
+{
+ int i;
+ smb_np_struct *p, *p_it;
+ static int next_pipe;
+ BOOL is_spoolss_pipe = False;
+
+ DEBUG(4,("Open pipe requested %s (pipes_open=%d)\n",
+ pipe_name, pipes_open));
+
+ if (strstr(pipe_name, "spoolss"))
+ is_spoolss_pipe = True;
+
+ if (is_spoolss_pipe && current_spoolss_pipes_open >= MAX_OPEN_SPOOLSS_PIPES) {
+ DEBUG(10,("open_rpc_pipe_p: spooler bug workaround. Denying open on pipe %s\n",
+ pipe_name ));
+ return NULL;
+ }
+
+ /* not repeating pipe numbers makes it easier to track things in
+ log files and prevents client bugs where pipe numbers are reused
+ over connection restarts */
+ if (next_pipe == 0)
+ next_pipe = (sys_getpid() ^ time(NULL)) % MAX_OPEN_PIPES;
+
+ i = bitmap_find(bmap, next_pipe);
+
+ if (i == -1) {
+ DEBUG(0,("ERROR! Out of pipe structures\n"));
+ return NULL;
+ }
+
+ next_pipe = (i+1) % MAX_OPEN_PIPES;
+
+ for (p = Pipes; p; p = p->next)
+ DEBUG(5,("open_rpc_pipe_p: name %s pnum=%x\n", p->name, p->pnum));
+
+ p = (smb_np_struct *)malloc(sizeof(*p));
+
+ if (!p) {
+ DEBUG(0,("ERROR! no memory for pipes_struct!\n"));
+ return NULL;
+ }
+
+ ZERO_STRUCTP(p);
+
+ /* add a dso mechanism instead of this, here */
+
+ p->namedpipe_create = make_internal_rpc_pipe_p;
+ p->namedpipe_read = read_from_internal_pipe;
+ p->namedpipe_write = write_to_internal_pipe;
+ p->namedpipe_close = close_internal_rpc_pipe_hnd;
+
+ p->np_state = p->namedpipe_create(pipe_name, conn, vuid);
+
+ if (p->np_state == NULL) {
+ DEBUG(0,("open_rpc_pipe_p: make_internal_rpc_pipe_p failed.\n"));
+ SAFE_FREE(p);
+ return NULL;
+ }
+
+ DLIST_ADD(Pipes, p);
+
+ /*
+ * Initialize the incoming RPC data buffer with one PDU worth of memory.
+ * We cheat here and say we're marshalling, as we intend to add incoming
+ * data directly into the prs_struct and we want it to auto grow. We will
+ * change the type to UNMARSALLING before processing the stream.
+ */
+
+ bitmap_set(bmap, i);
+ i += pipe_handle_offset;
+
+ pipes_open++;
+
+ p->pnum = i;
+
+ p->open = True;
+ p->device_state = 0;
+ p->priority = 0;
+ p->conn = conn;
+ p->vuid = vuid;
+
+ p->max_trans_reply = 0;
+
+ fstrcpy(p->name, pipe_name);
+
+ DEBUG(4,("Opened pipe %s with handle %x (pipes_open=%d)\n",
+ pipe_name, i, pipes_open));
+
+ chain_p = p;
+
+ /* Iterate over p_it as a temp variable, to display all open pipes */
+ for (p_it = Pipes; p_it; p_it = p_it->next)
+ DEBUG(5,("open pipes: name %s pnum=%x\n", p_it->name, p_it->pnum));
+
+ return chain_p;
+}
+
+/****************************************************************************
+ Make an internal namedpipes structure
+****************************************************************************/
+
+static void *make_internal_rpc_pipe_p(char *pipe_name,
+ connection_struct *conn, uint16 vuid)
+{
+ pipes_struct *p;
+ user_struct *vuser = get_valid_user_struct(vuid);
+
+ DEBUG(4,("Create pipe requested %s\n", pipe_name));
+
+ if (!vuser && vuid != UID_FIELD_INVALID) {
+ DEBUG(0,("ERROR! vuid %d did not map to a valid vuser struct!\n", vuid));
+ return NULL;
+ }
+
+ p = (pipes_struct *)malloc(sizeof(*p));
+
+ if (!p)
+ {
+ DEBUG(0,("ERROR! no memory for pipes_struct!\n"));
+ return NULL;
+ }
+
+ ZERO_STRUCTP(p);
+
+ if ((p->mem_ctx = talloc_init("pipe %s %p", pipe_name, p)) == NULL) {
+ DEBUG(0,("open_rpc_pipe_p: talloc_init failed.\n"));
+ SAFE_FREE(p);
+ return NULL;
+ }
+
+ if (!init_pipe_handle_list(p, pipe_name)) {
+ DEBUG(0,("open_rpc_pipe_p: init_pipe_handles failed.\n"));
+ talloc_destroy(p->mem_ctx);
+ SAFE_FREE(p);
+ return NULL;
+ }
+
+ /*
+ * Initialize the incoming RPC data buffer with one PDU worth of memory.
+ * We cheat here and say we're marshalling, as we intend to add incoming
+ * data directly into the prs_struct and we want it to auto grow. We will
+ * change the type to UNMARSALLING before processing the stream.
+ */
+
+ if(!prs_init(&p->in_data.data, MAX_PDU_FRAG_LEN, p->mem_ctx, MARSHALL)) {
+ DEBUG(0,("open_rpc_pipe_p: malloc fail for in_data struct.\n"));
+ return NULL;
+ }
+
+ DLIST_ADD(InternalPipes, p);
+
+ p->conn = conn;
+
+ /* Ensure the connection isn't idled whilst this pipe is open. */
+ p->conn->num_files_open++;
+
+ p->vuid = vuid;
+
+ p->ntlmssp_chal_flags = 0;
+ p->ntlmssp_auth_validated = False;
+ p->ntlmssp_auth_requested = False;
+
+ p->pipe_bound = False;
+ p->fault_state = False;
+ p->endian = RPC_LITTLE_ENDIAN;
+
+ ZERO_STRUCT(p->pipe_user);
+
+ p->pipe_user.uid = (uid_t)-1;
+ p->pipe_user.gid = (gid_t)-1;
+
+ /* Store the session key and NT_TOKEN */
+ if (vuser) {
+ p->session_key = data_blob(vuser->session_key.data, vuser->session_key.length);
+ p->pipe_user.nt_user_token = dup_nt_token(vuser->nt_user_token);
+ init_privilege(&p->pipe_user.privs);
+ dup_priv_set(p->pipe_user.privs, vuser->privs);
+ }
+
+ /*
+ * Initialize the incoming RPC struct.
+ */
+
+ p->in_data.pdu_needed_len = 0;
+ p->in_data.pdu_received_len = 0;
+
+ /*
+ * Initialize the outgoing RPC struct.
+ */
+
+ p->out_data.current_pdu_len = 0;
+ p->out_data.current_pdu_sent = 0;
+ p->out_data.data_sent_length = 0;
+
+ /*
+ * Initialize the outgoing RPC data buffer with no memory.
+ */
+ prs_init(&p->out_data.rdata, 0, p->mem_ctx, MARSHALL);
+
+ fstrcpy(p->name, pipe_name);
+
+ DEBUG(4,("Created internal pipe %s (pipes_open=%d)\n",
+ pipe_name, pipes_open));
+
+ return (void*)p;
+}
+
+/****************************************************************************
+ Sets the fault state on incoming packets.
+****************************************************************************/
+
+static void set_incoming_fault(pipes_struct *p)
+{
+ prs_mem_free(&p->in_data.data);
+ p->in_data.pdu_needed_len = 0;
+ p->in_data.pdu_received_len = 0;
+ p->fault_state = True;
+ DEBUG(10,("set_incoming_fault: Setting fault state on pipe %s : vuid = 0x%x\n",
+ p->name, p->vuid ));
+}
+
+/****************************************************************************
+ Ensures we have at least RPC_HEADER_LEN amount of data in the incoming buffer.
+****************************************************************************/
+
+static ssize_t fill_rpc_header(pipes_struct *p, char *data, size_t data_to_copy)
+{
+ size_t len_needed_to_complete_hdr = MIN(data_to_copy, RPC_HEADER_LEN - p->in_data.pdu_received_len);
+
+ DEBUG(10,("fill_rpc_header: data_to_copy = %u, len_needed_to_complete_hdr = %u, receive_len = %u\n",
+ (unsigned int)data_to_copy, (unsigned int)len_needed_to_complete_hdr,
+ (unsigned int)p->in_data.pdu_received_len ));
+
+ memcpy((char *)&p->in_data.current_in_pdu[p->in_data.pdu_received_len], data, len_needed_to_complete_hdr);
+ p->in_data.pdu_received_len += len_needed_to_complete_hdr;
+
+ return (ssize_t)len_needed_to_complete_hdr;
+}
+
+/****************************************************************************
+ Unmarshalls a new PDU header. Assumes the raw header data is in current_in_pdu.
+****************************************************************************/
+
+static ssize_t unmarshall_rpc_header(pipes_struct *p)
+{
+ /*
+ * Unmarshall the header to determine the needed length.
+ */
+
+ prs_struct rpc_in;
+
+ if(p->in_data.pdu_received_len != RPC_HEADER_LEN) {
+ DEBUG(0,("unmarshall_rpc_header: assert on rpc header length failed.\n"));
+ set_incoming_fault(p);
+ return -1;
+ }
+
+ prs_init( &rpc_in, 0, p->mem_ctx, UNMARSHALL);
+ prs_set_endian_data( &rpc_in, p->endian);
+
+ prs_give_memory( &rpc_in, (char *)&p->in_data.current_in_pdu[0],
+ p->in_data.pdu_received_len, False);
+
+ /*
+ * Unmarshall the header as this will tell us how much
+ * data we need to read to get the complete pdu.
+ * This also sets the endian flag in rpc_in.
+ */
+
+ if(!smb_io_rpc_hdr("", &p->hdr, &rpc_in, 0)) {
+ DEBUG(0,("unmarshall_rpc_header: failed to unmarshall RPC_HDR.\n"));
+ set_incoming_fault(p);
+ prs_mem_free(&rpc_in);
+ return -1;
+ }
+
+ /*
+ * Validate the RPC header.
+ */
+
+ if(p->hdr.major != 5 && p->hdr.minor != 0) {
+ DEBUG(0,("unmarshall_rpc_header: invalid major/minor numbers in RPC_HDR.\n"));
+ set_incoming_fault(p);
+ prs_mem_free(&rpc_in);
+ return -1;
+ }
+
+ /*
+ * If there's not data in the incoming buffer this should be the start of a new RPC.
+ */
+
+ if(prs_offset(&p->in_data.data) == 0) {
+
+ /*
+ * AS/U doesn't set FIRST flag in a BIND packet it seems.
+ */
+
+ if ((p->hdr.pkt_type == RPC_REQUEST) && !(p->hdr.flags & RPC_FLG_FIRST)) {
+ /*
+ * Ensure that the FIRST flag is set. If not then we have
+ * a stream missmatch.
+ */
+
+ DEBUG(0,("unmarshall_rpc_header: FIRST flag not set in first PDU !\n"));
+ set_incoming_fault(p);
+ prs_mem_free(&rpc_in);
+ return -1;
+ }
+
+ /*
+ * If this is the first PDU then set the endianness
+ * flag in the pipe. We will need this when parsing all
+ * data in this RPC.
+ */
+
+ p->endian = rpc_in.bigendian_data;
+
+ DEBUG(5,("unmarshall_rpc_header: using %sendian RPC\n",
+ p->endian == RPC_LITTLE_ENDIAN ? "little-" : "big-" ));
+
+ } else {
+
+ /*
+ * If this is *NOT* the first PDU then check the endianness
+ * flag in the pipe is the same as that in the PDU.
+ */
+
+ if (p->endian != rpc_in.bigendian_data) {
+ DEBUG(0,("unmarshall_rpc_header: FIRST endianness flag (%d) different in next PDU !\n", (int)p->endian));
+ set_incoming_fault(p);
+ prs_mem_free(&rpc_in);
+ return -1;
+ }
+ }
+
+ /*
+ * Ensure that the pdu length is sane.
+ */
+
+ if((p->hdr.frag_len < RPC_HEADER_LEN) || (p->hdr.frag_len > MAX_PDU_FRAG_LEN)) {
+ DEBUG(0,("unmarshall_rpc_header: assert on frag length failed.\n"));
+ set_incoming_fault(p);
+ prs_mem_free(&rpc_in);
+ return -1;
+ }
+
+ DEBUG(10,("unmarshall_rpc_header: type = %u, flags = %u\n", (unsigned int)p->hdr.pkt_type,
+ (unsigned int)p->hdr.flags ));
+
+ /*
+ * Adjust for the header we just ate.
+ */
+ p->in_data.pdu_received_len = 0;
+ p->in_data.pdu_needed_len = (uint32)p->hdr.frag_len - RPC_HEADER_LEN;
+
+ /*
+ * Null the data we just ate.
+ */
+
+ memset((char *)&p->in_data.current_in_pdu[0], '\0', RPC_HEADER_LEN);
+
+ prs_mem_free(&rpc_in);
+
+ return 0; /* No extra data processed. */
+}
+
+/****************************************************************************
+ Call this to free any talloc'ed memory. Do this before and after processing
+ a complete PDU.
+****************************************************************************/
+
+void free_pipe_context(pipes_struct *p)
+{
+ if (p->mem_ctx) {
+ DEBUG(3,("free_pipe_context: destroying talloc pool of size %lu\n", (unsigned long)talloc_pool_size(p->mem_ctx) ));
+ talloc_destroy_pool(p->mem_ctx);
+ } else {
+ p->mem_ctx = talloc_init("pipe %s %p", p->name, p);
+ if (p->mem_ctx == NULL)
+ p->fault_state = True;
+ }
+}
+
+/****************************************************************************
+ Processes a request pdu. This will do auth processing if needed, and
+ appends the data into the complete stream if the LAST flag is not set.
+****************************************************************************/
+
+static BOOL process_request_pdu(pipes_struct *p, prs_struct *rpc_in_p)
+{
+ BOOL auth_verify = ((p->ntlmssp_chal_flags & NTLMSSP_NEGOTIATE_SIGN) != 0);
+ size_t data_len = p->hdr.frag_len - RPC_HEADER_LEN - RPC_HDR_REQ_LEN -
+ (auth_verify ? RPC_HDR_AUTH_LEN : 0) - p->hdr.auth_len;
+
+ if(!p->pipe_bound) {
+ DEBUG(0,("process_request_pdu: rpc request with no bind.\n"));
+ set_incoming_fault(p);
+ return False;
+ }
+
+ /*
+ * Check if we need to do authentication processing.
+ * This is only done on requests, not binds.
+ */
+
+ /*
+ * Read the RPC request header.
+ */
+
+ if(!smb_io_rpc_hdr_req("req", &p->hdr_req, rpc_in_p, 0)) {
+ DEBUG(0,("process_request_pdu: failed to unmarshall RPC_HDR_REQ.\n"));
+ set_incoming_fault(p);
+ return False;
+ }
+
+ if(p->ntlmssp_auth_validated && !api_pipe_auth_process(p, rpc_in_p)) {
+ DEBUG(0,("process_request_pdu: failed to do auth processing.\n"));
+ set_incoming_fault(p);
+ return False;
+ }
+
+ if (p->ntlmssp_auth_requested && !p->ntlmssp_auth_validated) {
+
+ /*
+ * Authentication _was_ requested and it already failed.
+ */
+
+ DEBUG(0,("process_request_pdu: RPC request received on pipe %s "
+ "where authentication failed. Denying the request.\n",
+ p->name));
+ set_incoming_fault(p);
+ return False;
+ }
+
+ if (p->netsec_auth_validated && !api_pipe_netsec_process(p, rpc_in_p)) {
+ DEBUG(0,("process_request_pdu: failed to do schannel processing.\n"));
+ set_incoming_fault(p);
+ return False;
+ }
+
+ /*
+ * Check the data length doesn't go over the 15Mb limit.
+ * increased after observing a bug in the Windows NT 4.0 SP6a
+ * spoolsv.exe when the response to a GETPRINTERDRIVER2 RPC
+ * will not fit in the initial buffer of size 0x1068 --jerry 22/01/2002
+ */
+
+ if(prs_offset(&p->in_data.data) + data_len > 15*1024*1024) {
+ DEBUG(0,("process_request_pdu: rpc data buffer too large (%u) + (%u)\n",
+ (unsigned int)prs_data_size(&p->in_data.data), (unsigned int)data_len ));
+ set_incoming_fault(p);
+ return False;
+ }
+
+ /*
+ * Append the data portion into the buffer and return.
+ */
+
+ if(!prs_append_some_prs_data(&p->in_data.data, rpc_in_p, prs_offset(rpc_in_p), data_len)) {
+ DEBUG(0,("process_request_pdu: Unable to append data size %u to parse buffer of size %u.\n",
+ (unsigned int)data_len, (unsigned int)prs_data_size(&p->in_data.data) ));
+ set_incoming_fault(p);
+ return False;
+ }
+
+ if(p->hdr.flags & RPC_FLG_LAST) {
+ BOOL ret = False;
+ /*
+ * Ok - we finally have a complete RPC stream.
+ * Call the rpc command to process it.
+ */
+
+ /*
+ * Ensure the internal prs buffer size is *exactly* the same
+ * size as the current offset.
+ */
+
+ if(!prs_set_buffer_size(&p->in_data.data, prs_offset(&p->in_data.data)))
+ {
+ DEBUG(0,("process_request_pdu: Call to prs_set_buffer_size failed!\n"));
+ set_incoming_fault(p);
+ return False;
+ }
+
+ /*
+ * Set the parse offset to the start of the data and set the
+ * prs_struct to UNMARSHALL.
+ */
+
+ prs_set_offset(&p->in_data.data, 0);
+ prs_switch_type(&p->in_data.data, UNMARSHALL);
+
+ /*
+ * Process the complete data stream here.
+ */
+
+ free_pipe_context(p);
+
+ if(pipe_init_outgoing_data(p))
+ ret = api_pipe_request(p);
+
+ free_pipe_context(p);
+
+ /*
+ * We have consumed the whole data stream. Set back to
+ * marshalling and set the offset back to the start of
+ * the buffer to re-use it (we could also do a prs_mem_free()
+ * and then re_init on the next start of PDU. Not sure which
+ * is best here.... JRA.
+ */
+
+ prs_switch_type(&p->in_data.data, MARSHALL);
+ prs_set_offset(&p->in_data.data, 0);
+ return ret;
+ }
+
+ return True;
+}
+
+/****************************************************************************
+ Processes a finished PDU stored in current_in_pdu. The RPC_HEADER has
+ already been parsed and stored in p->hdr.
+****************************************************************************/
+
+static ssize_t process_complete_pdu(pipes_struct *p)
+{
+ prs_struct rpc_in;
+ size_t data_len = p->in_data.pdu_received_len;
+ char *data_p = (char *)&p->in_data.current_in_pdu[0];
+ BOOL reply = False;
+
+ if(p->fault_state) {
+ DEBUG(10,("process_complete_pdu: pipe %s in fault state.\n",
+ p->name ));
+ set_incoming_fault(p);
+ setup_fault_pdu(p, NT_STATUS(0x1c010002));
+ return (ssize_t)data_len;
+ }
+
+ prs_init( &rpc_in, 0, p->mem_ctx, UNMARSHALL);
+
+ /*
+ * Ensure we're using the corrent endianness for both the
+ * RPC header flags and the raw data we will be reading from.
+ */
+
+ prs_set_endian_data( &rpc_in, p->endian);
+ prs_set_endian_data( &p->in_data.data, p->endian);
+
+ prs_give_memory( &rpc_in, data_p, (uint32)data_len, False);
+
+ DEBUG(10,("process_complete_pdu: processing packet type %u\n",
+ (unsigned int)p->hdr.pkt_type ));
+
+ switch (p->hdr.pkt_type) {
+ case RPC_BIND:
+ case RPC_ALTCONT:
+ /*
+ * We assume that a pipe bind is only in one pdu.
+ */
+ if(pipe_init_outgoing_data(p))
+ reply = api_pipe_bind_req(p, &rpc_in);
+ break;
+ case RPC_BINDRESP:
+ /*
+ * We assume that a pipe bind_resp is only in one pdu.
+ */
+ if(pipe_init_outgoing_data(p))
+ reply = api_pipe_bind_auth_resp(p, &rpc_in);
+ break;
+ case RPC_REQUEST:
+ reply = process_request_pdu(p, &rpc_in);
+ break;
+ default:
+ DEBUG(0,("process_complete_pdu: Unknown rpc type = %u received.\n", (unsigned int)p->hdr.pkt_type ));
+ break;
+ }
+
+ /* Reset to little endian. Probably don't need this but it won't hurt. */
+ prs_set_endian_data( &p->in_data.data, RPC_LITTLE_ENDIAN);
+
+ if (!reply) {
+ DEBUG(3,("process_complete_pdu: DCE/RPC fault sent on pipe %s\n", p->pipe_srv_name));
+ set_incoming_fault(p);
+ setup_fault_pdu(p, NT_STATUS(0x1c010002));
+ prs_mem_free(&rpc_in);
+ } else {
+ /*
+ * Reset the lengths. We're ready for a new pdu.
+ */
+ p->in_data.pdu_needed_len = 0;
+ p->in_data.pdu_received_len = 0;
+ }
+
+ prs_mem_free(&rpc_in);
+ return (ssize_t)data_len;
+}
+
+/****************************************************************************
+ Accepts incoming data on an rpc pipe. Processes the data in pdu sized units.
+****************************************************************************/
+
+static ssize_t process_incoming_data(pipes_struct *p, char *data, size_t n)
+{
+ size_t data_to_copy = MIN(n, MAX_PDU_FRAG_LEN - p->in_data.pdu_received_len);
+ size_t old_pdu_received_len = p->in_data.pdu_received_len;
+
+ DEBUG(10,("process_incoming_data: Start: pdu_received_len = %u, pdu_needed_len = %u, incoming data = %u\n",
+ (unsigned int)p->in_data.pdu_received_len, (unsigned int)p->in_data.pdu_needed_len,
+ (unsigned int)n ));
+
+ if(data_to_copy == 0) {
+ /*
+ * This is an error - data is being received and there is no
+ * space in the PDU. Free the received data and go into the fault state.
+ */
+ DEBUG(0,("process_incoming_data: No space in incoming pdu buffer. Current size = %u \
+incoming data size = %u\n", (unsigned int)p->in_data.pdu_received_len, (unsigned int)n ));
+ set_incoming_fault(p);
+ return -1;
+ }
+
+ /*
+ * If we have no data already, wait until we get at least a RPC_HEADER_LEN
+ * number of bytes before we can do anything.
+ */
+
+ if((p->in_data.pdu_needed_len == 0) && (p->in_data.pdu_received_len < RPC_HEADER_LEN)) {
+ /*
+ * Always return here. If we have more data then the RPC_HEADER
+ * will be processed the next time around the loop.
+ */
+ return fill_rpc_header(p, data, data_to_copy);
+ }
+
+ /*
+ * At this point we know we have at least an RPC_HEADER_LEN amount of data
+ * stored in current_in_pdu.
+ */
+
+ /*
+ * If pdu_needed_len is zero this is a new pdu.
+ * Unmarshall the header so we know how much more
+ * data we need, then loop again.
+ */
+
+ if(p->in_data.pdu_needed_len == 0)
+ return unmarshall_rpc_header(p);
+
+ /*
+ * Ok - at this point we have a valid RPC_HEADER in p->hdr.
+ * Keep reading until we have a full pdu.
+ */
+
+ data_to_copy = MIN(data_to_copy, p->in_data.pdu_needed_len);
+
+ /*
+ * Copy as much of the data as we need into the current_in_pdu buffer.
+ */
+
+ memcpy( (char *)&p->in_data.current_in_pdu[p->in_data.pdu_received_len], data, data_to_copy);
+ p->in_data.pdu_received_len += data_to_copy;
+
+ /*
+ * Do we have a complete PDU ?
+ * (return the nym of bytes handled in the call)
+ */
+
+ if(p->in_data.pdu_received_len == p->in_data.pdu_needed_len)
+ return process_complete_pdu(p) - old_pdu_received_len;
+
+ DEBUG(10,("process_incoming_data: not a complete PDU yet. pdu_received_len = %u, pdu_needed_len = %u\n",
+ (unsigned int)p->in_data.pdu_received_len, (unsigned int)p->in_data.pdu_needed_len ));
+
+ return (ssize_t)data_to_copy;
+
+}
+
+/****************************************************************************
+ Accepts incoming data on an rpc pipe.
+****************************************************************************/
+
+ssize_t write_to_pipe(smb_np_struct *p, char *data, size_t n)
+{
+ DEBUG(6,("write_to_pipe: %x", p->pnum));
+
+ DEBUG(6,(" name: %s open: %s len: %d\n",
+ p->name, BOOLSTR(p->open), (int)n));
+
+ dump_data(50, data, n);
+
+ return p->namedpipe_write(p->np_state, data, n);
+}
+
+/****************************************************************************
+ Accepts incoming data on an internal rpc pipe.
+****************************************************************************/
+
+static ssize_t write_to_internal_pipe(void *np_conn, char *data, size_t n)
+{
+ pipes_struct *p = (pipes_struct*)np_conn;
+ size_t data_left = n;
+
+ while(data_left) {
+ ssize_t data_used;
+
+ DEBUG(10,("write_to_pipe: data_left = %u\n", (unsigned int)data_left ));
+
+ data_used = process_incoming_data(p, data, data_left);
+
+ DEBUG(10,("write_to_pipe: data_used = %d\n", (int)data_used ));
+
+ if(data_used < 0)
+ return -1;
+
+ data_left -= data_used;
+ data += data_used;
+ }
+
+ return n;
+}
+
+/****************************************************************************
+ Replies to a request to read data from a pipe.
+
+ Headers are interspersed with the data at PDU intervals. By the time
+ this function is called, the start of the data could possibly have been
+ read by an SMBtrans (file_offset != 0).
+
+ Calling create_rpc_reply() here is a hack. The data should already
+ have been prepared into arrays of headers + data stream sections.
+****************************************************************************/
+
+ssize_t read_from_pipe(smb_np_struct *p, char *data, size_t n,
+ BOOL *is_data_outstanding)
+{
+ if (!p || !p->open) {
+ DEBUG(0,("read_from_pipe: pipe not open\n"));
+ return -1;
+ }
+
+ DEBUG(6,("read_from_pipe: %x", p->pnum));
+
+ return p->namedpipe_read(p->np_state, data, n, is_data_outstanding);
+}
+
+/****************************************************************************
+ Replies to a request to read data from a pipe.
+
+ Headers are interspersed with the data at PDU intervals. By the time
+ this function is called, the start of the data could possibly have been
+ read by an SMBtrans (file_offset != 0).
+
+ Calling create_rpc_reply() here is a hack. The data should already
+ have been prepared into arrays of headers + data stream sections.
+****************************************************************************/
+
+static ssize_t read_from_internal_pipe(void *np_conn, char *data, size_t n,
+ BOOL *is_data_outstanding)
+{
+ pipes_struct *p = (pipes_struct*)np_conn;
+ uint32 pdu_remaining = 0;
+ ssize_t data_returned = 0;
+
+ if (!p) {
+ DEBUG(0,("read_from_pipe: pipe not open\n"));
+ return -1;
+ }
+
+ DEBUG(6,(" name: %s len: %u\n", p->name, (unsigned int)n));
+
+ /*
+ * We cannot return more than one PDU length per
+ * read request.
+ */
+
+ /*
+ * This condition should result in the connection being closed.
+ * Netapp filers seem to set it to 0xffff which results in domain
+ * authentications failing. Just ignore it so things work.
+ */
+
+ if(n > MAX_PDU_FRAG_LEN) {
+ DEBUG(5,("read_from_pipe: too large read (%u) requested on \
+pipe %s. We can only service %d sized reads.\n", (unsigned int)n, p->name, MAX_PDU_FRAG_LEN ));
+ }
+
+ /*
+ * Determine if there is still data to send in the
+ * pipe PDU buffer. Always send this first. Never
+ * send more than is left in the current PDU. The
+ * client should send a new read request for a new
+ * PDU.
+ */
+
+ if((pdu_remaining = p->out_data.current_pdu_len - p->out_data.current_pdu_sent) > 0) {
+ data_returned = (ssize_t)MIN(n, pdu_remaining);
+
+ DEBUG(10,("read_from_pipe: %s: current_pdu_len = %u, current_pdu_sent = %u \
+returning %d bytes.\n", p->name, (unsigned int)p->out_data.current_pdu_len,
+ (unsigned int)p->out_data.current_pdu_sent, (int)data_returned));
+
+ memcpy( data, &p->out_data.current_pdu[p->out_data.current_pdu_sent], (size_t)data_returned);
+ p->out_data.current_pdu_sent += (uint32)data_returned;
+ goto out;
+ }
+
+ /*
+ * At this point p->current_pdu_len == p->current_pdu_sent (which
+ * may of course be zero if this is the first return fragment.
+ */
+
+ DEBUG(10,("read_from_pipe: %s: fault_state = %d : data_sent_length \
+= %u, prs_offset(&p->out_data.rdata) = %u.\n",
+ p->name, (int)p->fault_state, (unsigned int)p->out_data.data_sent_length, (unsigned int)prs_offset(&p->out_data.rdata) ));
+
+ if(p->out_data.data_sent_length >= prs_offset(&p->out_data.rdata)) {
+ /*
+ * We have sent all possible data, return 0.
+ */
+ data_returned = 0;
+ goto out;
+ }
+
+ /*
+ * We need to create a new PDU from the data left in p->rdata.
+ * Create the header/data/footers. This also sets up the fields
+ * p->current_pdu_len, p->current_pdu_sent, p->data_sent_length
+ * and stores the outgoing PDU in p->current_pdu.
+ */
+
+ if(!create_next_pdu(p)) {
+ DEBUG(0,("read_from_pipe: %s: create_next_pdu failed.\n", p->name));
+ return -1;
+ }
+
+ data_returned = MIN(n, p->out_data.current_pdu_len);
+
+ memcpy( data, p->out_data.current_pdu, (size_t)data_returned);
+ p->out_data.current_pdu_sent += (uint32)data_returned;
+
+ out:
+
+ (*is_data_outstanding) = p->out_data.current_pdu_len > n;
+ return data_returned;
+}
+
+/****************************************************************************
+ Wait device state on a pipe. Exactly what this is for is unknown...
+****************************************************************************/
+
+BOOL wait_rpc_pipe_hnd_state(smb_np_struct *p, uint16 priority)
+{
+ if (p == NULL)
+ return False;
+
+ if (p->open) {
+ DEBUG(3,("wait_rpc_pipe_hnd_state: Setting pipe wait state priority=%x on pipe (name=%s)\n",
+ priority, p->name));
+
+ p->priority = priority;
+
+ return True;
+ }
+
+ DEBUG(3,("wait_rpc_pipe_hnd_state: Error setting pipe wait state priority=%x (name=%s)\n",
+ priority, p->name));
+ return False;
+}
+
+
+/****************************************************************************
+ Set device state on a pipe. Exactly what this is for is unknown...
+****************************************************************************/
+
+BOOL set_rpc_pipe_hnd_state(smb_np_struct *p, uint16 device_state)
+{
+ if (p == NULL)
+ return False;
+
+ if (p->open) {
+ DEBUG(3,("set_rpc_pipe_hnd_state: Setting pipe device state=%x on pipe (name=%s)\n",
+ device_state, p->name));
+
+ p->device_state = device_state;
+
+ return True;
+ }
+
+ DEBUG(3,("set_rpc_pipe_hnd_state: Error setting pipe device state=%x (name=%s)\n",
+ device_state, p->name));
+ return False;
+}
+
+
+/****************************************************************************
+ Close an rpc pipe.
+****************************************************************************/
+
+BOOL close_rpc_pipe_hnd(smb_np_struct *p)
+{
+ if (!p) {
+ DEBUG(0,("Invalid pipe in close_rpc_pipe_hnd\n"));
+ return False;
+ }
+
+ p->namedpipe_close(p->np_state);
+
+ bitmap_clear(bmap, p->pnum - pipe_handle_offset);
+
+ pipes_open--;
+
+ DEBUG(4,("closed pipe name %s pnum=%x (pipes_open=%d)\n",
+ p->name, p->pnum, pipes_open));
+
+ DLIST_REMOVE(Pipes, p);
+
+ ZERO_STRUCTP(p);
+
+ SAFE_FREE(p);
+
+ return True;
+}
+
+/****************************************************************************
+ Close an rpc pipe.
+****************************************************************************/
+
+static BOOL close_internal_rpc_pipe_hnd(void *np_conn)
+{
+ pipes_struct *p = (pipes_struct *)np_conn;
+ if (!p) {
+ DEBUG(0,("Invalid pipe in close_internal_rpc_pipe_hnd\n"));
+ return False;
+ }
+
+ prs_mem_free(&p->out_data.rdata);
+ prs_mem_free(&p->in_data.data);
+
+ if (p->mem_ctx)
+ talloc_destroy(p->mem_ctx);
+
+ free_pipe_rpc_context( p->contexts );
+
+ /* Free the handles database. */
+ close_policy_by_pipe(p);
+
+ delete_nt_token(&p->pipe_user.nt_user_token);
+ SAFE_FREE(p->pipe_user.groups);
+
+ DLIST_REMOVE(InternalPipes, p);
+
+ p->conn->num_files_open--;
+
+ ZERO_STRUCTP(p);
+
+ SAFE_FREE(p);
+
+ return True;
+}
+
+/****************************************************************************
+ Find an rpc pipe given a pipe handle in a buffer and an offset.
+****************************************************************************/
+
+smb_np_struct *get_rpc_pipe_p(char *buf, int where)
+{
+ int pnum = SVAL(buf,where);
+
+ if (chain_p)
+ return chain_p;
+
+ return get_rpc_pipe(pnum);
+}
+
+/****************************************************************************
+ Find an rpc pipe given a pipe handle.
+****************************************************************************/
+
+smb_np_struct *get_rpc_pipe(int pnum)
+{
+ smb_np_struct *p;
+
+ DEBUG(4,("search for pipe pnum=%x\n", pnum));
+
+ for (p=Pipes;p;p=p->next)
+ DEBUG(5,("pipe name %s pnum=%x (pipes_open=%d)\n",
+ p->name, p->pnum, pipes_open));
+
+ for (p=Pipes;p;p=p->next) {
+ if (p->pnum == pnum) {
+ chain_p = p;
+ return p;
+ }
+ }
+
+ return NULL;
+}
diff --git a/source/rpc_server/srv_reg.c b/source/rpc_server/srv_reg.c
new file mode 100644
index 00000000000..b780be0aff3
--- /dev/null
+++ b/source/rpc_server/srv_reg.c
@@ -0,0 +1,402 @@
+/*
+ * Unix SMB/CIFS implementation.
+ * RPC Pipe client / server routines
+ * Copyright (C) Andrew Tridgell 1992-1997,
+ * Copyright (C) Luke Kenneth Casson Leighton 1996-1997,
+ * Copyright (C) Paul Ashton 1997,
+ * Copyright (C) Marc Jacobsen 2000,
+ * Copyright (C) Jeremy Allison 2001,
+ * Copyright (C) Gerald Carter 2002,
+ * 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 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.
+ */
+
+/* This is the interface for the registry functions. */
+
+#include "includes.h"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_RPC_SRV
+
+/*******************************************************************
+ api_reg_close
+ ********************************************************************/
+
+static BOOL api_reg_close(pipes_struct *p)
+{
+ REG_Q_CLOSE q_u;
+ REG_R_CLOSE r_u;
+ prs_struct *data = &p->in_data.data;
+ prs_struct *rdata = &p->out_data.rdata;
+
+ ZERO_STRUCT(q_u);
+ ZERO_STRUCT(r_u);
+
+ /* grab the reg unknown 1 */
+ if(!reg_io_q_close("", &q_u, data, 0))
+ return False;
+
+ r_u.status = _reg_close(p, &q_u, &r_u);
+
+ if(!reg_io_r_close("", &r_u, rdata, 0))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ api_reg_open_khlm
+ ********************************************************************/
+
+static BOOL api_reg_open_hklm(pipes_struct *p)
+{
+ REG_Q_OPEN_HKLM q_u;
+ REG_R_OPEN_HKLM r_u;
+ prs_struct *data = &p->in_data.data;
+ prs_struct *rdata = &p->out_data.rdata;
+
+ ZERO_STRUCT(q_u);
+ ZERO_STRUCT(r_u);
+
+ /* grab the reg open */
+ if(!reg_io_q_open_hklm("", &q_u, data, 0))
+ return False;
+
+ r_u.status = _reg_open_hklm(p, &q_u, &r_u);
+
+ if(!reg_io_r_open_hklm("", &r_u, rdata, 0))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ api_reg_open_khu
+ ********************************************************************/
+
+static BOOL api_reg_open_hku(pipes_struct *p)
+{
+ REG_Q_OPEN_HKU q_u;
+ REG_R_OPEN_HKU r_u;
+ prs_struct *data = &p->in_data.data;
+ prs_struct *rdata = &p->out_data.rdata;
+
+ ZERO_STRUCT(q_u);
+ ZERO_STRUCT(r_u);
+
+ /* grab the reg open */
+ if(!reg_io_q_open_hku("", &q_u, data, 0))
+ return False;
+
+ r_u.status = _reg_open_hku(p, &q_u, &r_u);
+
+ if(!reg_io_r_open_hku("", &r_u, rdata, 0))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ api_reg_open_khcr
+ ********************************************************************/
+
+static BOOL api_reg_open_hkcr(pipes_struct *p)
+{
+ REG_Q_OPEN_HKCR q_u;
+ REG_R_OPEN_HKCR r_u;
+ prs_struct *data = &p->in_data.data;
+ prs_struct *rdata = &p->out_data.rdata;
+
+ ZERO_STRUCT(q_u);
+ ZERO_STRUCT(r_u);
+
+ /* grab the reg open */
+ if(!reg_io_q_open_hkcr("", &q_u, data, 0))
+ return False;
+
+ r_u.status = _reg_open_hkcr(p, &q_u, &r_u);
+
+ if(!reg_io_r_open_hkcr("", &r_u, rdata, 0))
+ return False;
+
+ return True;
+}
+
+
+/*******************************************************************
+ api_reg_open_entry
+ ********************************************************************/
+
+static BOOL api_reg_open_entry(pipes_struct *p)
+{
+ REG_Q_OPEN_ENTRY q_u;
+ REG_R_OPEN_ENTRY r_u;
+ prs_struct *data = &p->in_data.data;
+ prs_struct *rdata = &p->out_data.rdata;
+
+ ZERO_STRUCT(q_u);
+ ZERO_STRUCT(r_u);
+
+ /* grab the reg open entry */
+ if(!reg_io_q_open_entry("", &q_u, data, 0))
+ return False;
+
+ /* construct reply. */
+ r_u.status = _reg_open_entry(p, &q_u, &r_u);
+
+ if(!reg_io_r_open_entry("", &r_u, rdata, 0))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ api_reg_info
+ ********************************************************************/
+
+static BOOL api_reg_info(pipes_struct *p)
+{
+ REG_Q_INFO q_u;
+ REG_R_INFO r_u;
+ prs_struct *data = &p->in_data.data;
+ prs_struct *rdata = &p->out_data.rdata;
+
+ ZERO_STRUCT(q_u);
+ ZERO_STRUCT(r_u);
+
+ /* grab the reg unknown 0x11*/
+ if(!reg_io_q_info("", &q_u, data, 0))
+ return False;
+
+ r_u.status = _reg_info(p, &q_u, &r_u);
+
+ if(!reg_io_r_info("", &r_u, rdata, 0))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ api_reg_shutdown
+ ********************************************************************/
+
+static BOOL api_reg_shutdown(pipes_struct *p)
+{
+ REG_Q_SHUTDOWN q_u;
+ REG_R_SHUTDOWN r_u;
+ prs_struct *data = &p->in_data.data;
+ prs_struct *rdata = &p->out_data.rdata;
+
+ ZERO_STRUCT(q_u);
+ ZERO_STRUCT(r_u);
+
+ /* grab the reg shutdown */
+ if(!reg_io_q_shutdown("", &q_u, data, 0))
+ return False;
+
+ r_u.status = _reg_shutdown(p, &q_u, &r_u);
+
+ if(!reg_io_r_shutdown("", &r_u, rdata, 0))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ api_reg_abort_shutdown
+ ********************************************************************/
+
+static BOOL api_reg_abort_shutdown(pipes_struct *p)
+{
+ REG_Q_ABORT_SHUTDOWN q_u;
+ REG_R_ABORT_SHUTDOWN r_u;
+ prs_struct *data = &p->in_data.data;
+ prs_struct *rdata = &p->out_data.rdata;
+
+ ZERO_STRUCT(q_u);
+ ZERO_STRUCT(r_u);
+
+ /* grab the reg shutdown */
+ if(!reg_io_q_abort_shutdown("", &q_u, data, 0))
+ return False;
+
+ r_u.status = _reg_abort_shutdown(p, &q_u, &r_u);
+
+ if(!reg_io_r_abort_shutdown("", &r_u, rdata, 0))
+ return False;
+
+ return True;
+}
+
+
+/*******************************************************************
+ api_reg_query_key
+ ********************************************************************/
+
+static BOOL api_reg_query_key(pipes_struct *p)
+{
+ REG_Q_QUERY_KEY q_u;
+ REG_R_QUERY_KEY r_u;
+ prs_struct *data = &p->in_data.data;
+ prs_struct *rdata = &p->out_data.rdata;
+
+ ZERO_STRUCT(q_u);
+ ZERO_STRUCT(r_u);
+
+ if(!reg_io_q_query_key("", &q_u, data, 0))
+ return False;
+
+ r_u.status = _reg_query_key(p, &q_u, &r_u);
+
+ if(!reg_io_r_query_key("", &r_u, rdata, 0))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ api_reg_unknown_1a
+ ********************************************************************/
+
+static BOOL api_reg_unknown_1a(pipes_struct *p)
+{
+ REG_Q_UNKNOWN_1A q_u;
+ REG_R_UNKNOWN_1A r_u;
+ prs_struct *data = &p->in_data.data;
+ prs_struct *rdata = &p->out_data.rdata;
+
+ ZERO_STRUCT(q_u);
+ ZERO_STRUCT(r_u);
+
+ if(!reg_io_q_unknown_1a("", &q_u, data, 0))
+ return False;
+
+ r_u.status = _reg_unknown_1a(p, &q_u, &r_u);
+
+ if(!reg_io_r_unknown_1a("", &r_u, rdata, 0))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ api_reg_enum_key
+ ********************************************************************/
+
+static BOOL api_reg_enum_key(pipes_struct *p)
+{
+ REG_Q_ENUM_KEY q_u;
+ REG_R_ENUM_KEY r_u;
+ prs_struct *data = &p->in_data.data;
+ prs_struct *rdata = &p->out_data.rdata;
+
+ ZERO_STRUCT(q_u);
+ ZERO_STRUCT(r_u);
+
+ if(!reg_io_q_enum_key("", &q_u, data, 0))
+ return False;
+
+ r_u.status = _reg_enum_key(p, &q_u, &r_u);
+
+ if(!reg_io_r_enum_key("", &r_u, rdata, 0))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ api_reg_enum_value
+ ********************************************************************/
+
+static BOOL api_reg_enum_value(pipes_struct *p)
+{
+ REG_Q_ENUM_VALUE q_u;
+ REG_R_ENUM_VALUE r_u;
+ prs_struct *data = &p->in_data.data;
+ prs_struct *rdata = &p->out_data.rdata;
+
+ ZERO_STRUCT(q_u);
+ ZERO_STRUCT(r_u);
+
+ if(!reg_io_q_enum_val("", &q_u, data, 0))
+ return False;
+
+ r_u.status = _reg_enum_value(p, &q_u, &r_u);
+
+ if(!reg_io_r_enum_val("", &r_u, rdata, 0))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ api_reg_save_key
+ ********************************************************************/
+
+static BOOL api_reg_save_key(pipes_struct *p)
+{
+ REG_Q_SAVE_KEY q_u;
+ REG_R_SAVE_KEY r_u;
+ prs_struct *data = &p->in_data.data;
+ prs_struct *rdata = &p->out_data.rdata;
+
+ ZERO_STRUCT(q_u);
+ ZERO_STRUCT(r_u);
+
+ if(!reg_io_q_save_key("", &q_u, data, 0))
+ return False;
+
+ r_u.status = _reg_save_key(p, &q_u, &r_u);
+
+ if(!reg_io_r_save_key("", &r_u, rdata, 0))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ array of \PIPE\reg operations
+ ********************************************************************/
+
+static struct api_struct api_reg_cmds[] =
+{
+ { "REG_CLOSE" , REG_CLOSE , api_reg_close },
+ { "REG_OPEN_ENTRY" , REG_OPEN_ENTRY , api_reg_open_entry },
+ { "REG_OPEN_HKCR" , REG_OPEN_HKCR , api_reg_open_hkcr },
+ { "REG_OPEN_HKLM" , REG_OPEN_HKLM , api_reg_open_hklm },
+ { "REG_OPEN_HKU" , REG_OPEN_HKU , api_reg_open_hku },
+ { "REG_ENUM_KEY" , REG_ENUM_KEY , api_reg_enum_key },
+ { "REG_ENUM_VALUE" , REG_ENUM_VALUE , api_reg_enum_value },
+ { "REG_QUERY_KEY" , REG_QUERY_KEY , api_reg_query_key },
+ { "REG_INFO" , REG_INFO , api_reg_info },
+ { "REG_SHUTDOWN" , REG_SHUTDOWN , api_reg_shutdown },
+ { "REG_ABORT_SHUTDOWN" , REG_ABORT_SHUTDOWN , api_reg_abort_shutdown },
+ { "REG_UNKNOWN_1A" , REG_UNKNOWN_1A , api_reg_unknown_1a },
+ { "REG_SAVE_KEY" , REG_SAVE_KEY , api_reg_save_key }
+};
+
+void reg_get_pipe_fns( struct api_struct **fns, int *n_fns )
+{
+ *fns = api_reg_cmds;
+ *n_fns = sizeof(api_reg_cmds) / sizeof(struct api_struct);
+}
+
+NTSTATUS rpc_reg_init(void)
+{
+
+ return rpc_pipe_register_commands(SMB_RPC_INTERFACE_VERSION, "winreg", "winreg", api_reg_cmds,
+ sizeof(api_reg_cmds) / sizeof(struct api_struct));
+}
diff --git a/source/rpc_server/srv_reg_nt.c b/source/rpc_server/srv_reg_nt.c
new file mode 100644
index 00000000000..a4e3638be60
--- /dev/null
+++ b/source/rpc_server/srv_reg_nt.c
@@ -0,0 +1,664 @@
+/*
+ * Unix SMB/CIFS implementation.
+ * RPC Pipe client / server routines
+ * Copyright (C) Andrew Tridgell 1992-1997.
+ * Copyright (C) Luke Kenneth Casson Leighton 1996-1997.
+ * Copyright (C) Paul Ashton 1997.
+ * Copyright (C) Jeremy Allison 2001.
+ * Copyright (C) Gerald Carter 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 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.
+ */
+
+/* Implementation of registry functions. */
+
+#include "includes.h"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_RPC_SRV
+
+#define REGSTR_PRODUCTTYPE "ProductType"
+#define REG_PT_WINNT "WinNT"
+#define REG_PT_LANMANNT "LanmanNT"
+#define REG_PT_SERVERNT "ServerNT"
+
+#define OUR_HANDLE(hnd) (((hnd)==NULL)?"NULL":(IVAL((hnd)->data5,4)==(uint32)sys_getpid()?"OURS":"OTHER")), \
+((unsigned int)IVAL((hnd)->data5,4)),((unsigned int)sys_getpid())
+
+
+static REGISTRY_KEY *regkeys_list;
+
+
+/******************************************************************
+ free() function for REGISTRY_KEY
+ *****************************************************************/
+
+static void free_regkey_info(void *ptr)
+{
+ REGISTRY_KEY *info = (REGISTRY_KEY*)ptr;
+
+ DLIST_REMOVE(regkeys_list, info);
+
+ SAFE_FREE(info);
+}
+
+/******************************************************************
+ Find a registry key handle and return a REGISTRY_KEY
+ *****************************************************************/
+
+static REGISTRY_KEY *find_regkey_index_by_hnd(pipes_struct *p, POLICY_HND *hnd)
+{
+ REGISTRY_KEY *regkey = NULL;
+
+ if(!find_policy_by_hnd(p,hnd,(void **)&regkey)) {
+ DEBUG(2,("find_regkey_index_by_hnd: Registry Key not found: "));
+ return NULL;
+ }
+
+ return regkey;
+}
+
+
+/*******************************************************************
+ Function for open a new registry handle and creating a handle
+ Note that P should be valid & hnd should already have space
+
+ When we open a key, we store the full path to the key as
+ HK[LM|U]\<key>\<key>\...
+ *******************************************************************/
+
+static NTSTATUS open_registry_key(pipes_struct *p, POLICY_HND *hnd, REGISTRY_KEY *parent,
+ const char *subkeyname, uint32 access_granted )
+{
+ REGISTRY_KEY *regkey = NULL;
+ NTSTATUS result = NT_STATUS_OK;
+ REGSUBKEY_CTR subkeys;
+ pstring subkeyname2;
+ int subkey_len;
+
+ DEBUG(7,("open_registry_key: name = [%s][%s]\n",
+ parent ? parent->name : "NULL", subkeyname));
+
+ /* strip any trailing '\'s */
+ pstrcpy( subkeyname2, subkeyname );
+ subkey_len = strlen ( subkeyname2 );
+ if ( subkey_len && subkeyname2[subkey_len-1] == '\\' )
+ subkeyname2[subkey_len-1] = '\0';
+
+ if ((regkey=(REGISTRY_KEY*)malloc(sizeof(REGISTRY_KEY))) == NULL)
+ return NT_STATUS_NO_MEMORY;
+
+ ZERO_STRUCTP( regkey );
+
+ /*
+ * very crazy, but regedit.exe on Win2k will attempt to call
+ * REG_OPEN_ENTRY with a keyname of "". We should return a new
+ * (second) handle here on the key->name. regedt32.exe does
+ * not do this stupidity. --jerry
+ */
+
+ if ( !subkey_len ) {
+ pstrcpy( regkey->name, parent->name );
+ }
+ else {
+ pstrcpy( regkey->name, "" );
+ if ( parent ) {
+ pstrcat( regkey->name, parent->name );
+ pstrcat( regkey->name, "\\" );
+ }
+ pstrcat( regkey->name, subkeyname2 );
+ }
+
+ /* Look up the table of registry I/O operations */
+
+ if ( !(regkey->hook = reghook_cache_find( regkey->name )) ) {
+ DEBUG(0,("open_registry_key: Failed to assigned a REGISTRY_HOOK to [%s]\n",
+ regkey->name ));
+ return NT_STATUS_OBJECT_PATH_NOT_FOUND;
+ }
+
+ /* check if the path really exists; failed is indicated by -1 */
+ /* if the subkey count failed, bail out */
+
+ ZERO_STRUCTP( &subkeys );
+
+ regsubkey_ctr_init( &subkeys );
+
+ if ( fetch_reg_keys( regkey, &subkeys ) == -1 ) {
+
+ /* don't really know what to return here */
+ result = NT_STATUS_NO_SUCH_FILE;
+ }
+ else {
+ /*
+ * This would previously return NT_STATUS_TOO_MANY_SECRETS
+ * that doesn't sound quite right to me --jerry
+ */
+
+ if ( !create_policy_hnd( p, hnd, free_regkey_info, regkey ) )
+ result = NT_STATUS_OBJECT_NAME_NOT_FOUND;
+ }
+
+ /* clean up */
+
+ regsubkey_ctr_destroy( &subkeys );
+
+ if ( ! NT_STATUS_IS_OK(result) )
+ SAFE_FREE( regkey );
+ else
+ DLIST_ADD( regkeys_list, regkey );
+
+
+ DEBUG(7,("open_registry_key: exit\n"));
+
+ return result;
+}
+
+/*******************************************************************
+ Function for open a new registry handle and creating a handle
+ Note that P should be valid & hnd should already have space
+ *******************************************************************/
+
+static BOOL close_registry_key(pipes_struct *p, POLICY_HND *hnd)
+{
+ REGISTRY_KEY *regkey = find_regkey_index_by_hnd(p, hnd);
+
+ if ( !regkey ) {
+ DEBUG(2,("close_registry_key: Invalid handle (%s:%u:%u)\n", OUR_HANDLE(hnd)));
+ return False;
+ }
+
+ close_policy_hnd(p, hnd);
+
+ return True;
+}
+
+/********************************************************************
+ retrieve information about the subkeys
+ *******************************************************************/
+
+static BOOL get_subkey_information( REGISTRY_KEY *key, uint32 *maxnum, uint32 *maxlen )
+{
+ int num_subkeys, i;
+ uint32 max_len;
+ REGSUBKEY_CTR subkeys;
+ uint32 len;
+
+ if ( !key )
+ return False;
+
+ ZERO_STRUCTP( &subkeys );
+
+ regsubkey_ctr_init( &subkeys );
+
+ if ( fetch_reg_keys( key, &subkeys ) == -1 )
+ return False;
+
+ /* find the longest string */
+
+ max_len = 0;
+ num_subkeys = regsubkey_ctr_numkeys( &subkeys );
+
+ for ( i=0; i<num_subkeys; i++ ) {
+ len = strlen( regsubkey_ctr_specific_key(&subkeys, i) );
+ max_len = MAX(max_len, len);
+ }
+
+ *maxnum = num_subkeys;
+ *maxlen = max_len*2;
+
+ regsubkey_ctr_destroy( &subkeys );
+
+ return True;
+}
+
+/********************************************************************
+ retrieve information about the values. We don't store values
+ here. The registry tdb is intended to be a frontend to oether
+ Samba tdb's (such as ntdrivers.tdb).
+ *******************************************************************/
+
+static BOOL get_value_information( REGISTRY_KEY *key, uint32 *maxnum,
+ uint32 *maxlen, uint32 *maxsize )
+{
+ REGVAL_CTR values;
+ REGISTRY_VALUE *val;
+ uint32 sizemax, lenmax;
+ int i, num_values;
+
+ if ( !key )
+ return False;
+
+
+ ZERO_STRUCTP( &values );
+
+ regval_ctr_init( &values );
+
+ if ( fetch_reg_values( key, &values ) == -1 )
+ return False;
+
+ lenmax = sizemax = 0;
+ num_values = regval_ctr_numvals( &values );
+
+ val = regval_ctr_specific_value( &values, 0 );
+
+ for ( i=0; i<num_values && val; i++ )
+ {
+ lenmax = MAX(lenmax, strlen(val->valuename)+1 );
+ sizemax = MAX(sizemax, val->size );
+
+ val = regval_ctr_specific_value( &values, i );
+ }
+
+ *maxnum = num_values;
+ *maxlen = lenmax;
+ *maxsize = sizemax;
+
+ regval_ctr_destroy( &values );
+
+ return True;
+}
+
+
+/********************************************************************
+ reg_close
+ ********************************************************************/
+
+NTSTATUS _reg_close(pipes_struct *p, REG_Q_CLOSE *q_u, REG_R_CLOSE *r_u)
+{
+ /* set up the REG unknown_1 response */
+ ZERO_STRUCT(r_u->pol);
+
+ /* close the policy handle */
+ if (!close_registry_key(p, &q_u->pol))
+ return NT_STATUS_OBJECT_NAME_INVALID;
+
+ return NT_STATUS_OK;
+}
+
+/*******************************************************************
+ ********************************************************************/
+
+NTSTATUS _reg_open_hklm(pipes_struct *p, REG_Q_OPEN_HKLM *q_u, REG_R_OPEN_HKLM *r_u)
+{
+ return open_registry_key( p, &r_u->pol, NULL, KEY_HKLM, 0x0 );
+}
+
+/*******************************************************************
+ ********************************************************************/
+
+NTSTATUS _reg_open_hkcr(pipes_struct *p, REG_Q_OPEN_HKCR *q_u, REG_R_OPEN_HKCR *r_u)
+{
+ return open_registry_key( p, &r_u->pol, NULL, KEY_HKCR, 0x0 );
+}
+
+/*******************************************************************
+ ********************************************************************/
+
+NTSTATUS _reg_open_hku(pipes_struct *p, REG_Q_OPEN_HKU *q_u, REG_R_OPEN_HKU *r_u)
+{
+ return open_registry_key( p, &r_u->pol, NULL, KEY_HKU, 0x0 );
+}
+
+/*******************************************************************
+ reg_reply_open_entry
+ ********************************************************************/
+
+NTSTATUS _reg_open_entry(pipes_struct *p, REG_Q_OPEN_ENTRY *q_u, REG_R_OPEN_ENTRY *r_u)
+{
+ POLICY_HND pol;
+ fstring name;
+ REGISTRY_KEY *key = find_regkey_index_by_hnd(p, &q_u->pol);
+ NTSTATUS result;
+
+ DEBUG(5,("reg_open_entry: Enter\n"));
+
+ if ( !key )
+ return NT_STATUS_INVALID_HANDLE;
+
+ rpcstr_pull(name,q_u->uni_name.buffer,sizeof(name),q_u->uni_name.uni_str_len*2,0);
+
+ result = open_registry_key( p, &pol, key, name, 0x0 );
+
+ init_reg_r_open_entry( r_u, &pol, result );
+
+ DEBUG(5,("reg_open_entry: Exit\n"));
+
+ return r_u->status;
+}
+
+/*******************************************************************
+ reg_reply_info
+ ********************************************************************/
+
+NTSTATUS _reg_info(pipes_struct *p, REG_Q_INFO *q_u, REG_R_INFO *r_u)
+{
+ NTSTATUS status = NT_STATUS_NO_SUCH_FILE;
+ fstring name;
+ const char *value_ascii = "";
+ fstring value;
+ int value_length;
+ REGISTRY_KEY *regkey = find_regkey_index_by_hnd( p, &q_u->pol );
+ REGISTRY_VALUE *val = NULL;
+ REGVAL_CTR regvals;
+ int i;
+
+ DEBUG(5,("_reg_info: Enter\n"));
+
+ if ( !regkey )
+ return NT_STATUS_INVALID_HANDLE;
+
+ DEBUG(7,("_reg_info: policy key name = [%s]\n", regkey->name));
+
+ rpcstr_pull(name, q_u->uni_type.buffer, sizeof(name), q_u->uni_type.uni_str_len*2, 0);
+
+ DEBUG(5,("reg_info: looking up value: [%s]\n", name));
+
+ ZERO_STRUCTP( &regvals );
+
+ regval_ctr_init( &regvals );
+
+ /* couple of hard coded registry values */
+
+ if ( strequal(name, "RefusePasswordChange") ) {
+ if ( (val = (REGISTRY_VALUE*)malloc(sizeof(REGISTRY_VALUE))) == NULL ) {
+ DEBUG(0,("_reg_info: malloc() failed!\n"));
+ return NT_STATUS_NO_MEMORY;
+ }
+ ZERO_STRUCTP( val );
+
+ goto out;
+ }
+
+ if ( strequal(name, REGSTR_PRODUCTTYPE) ) {
+ /* This makes the server look like a member server to clients */
+ /* which tells clients that we have our own local user and */
+ /* group databases and helps with ACL support. */
+
+ switch (lp_server_role()) {
+ case ROLE_DOMAIN_PDC:
+ case ROLE_DOMAIN_BDC:
+ value_ascii = REG_PT_LANMANNT;
+ break;
+ case ROLE_STANDALONE:
+ value_ascii = REG_PT_SERVERNT;
+ break;
+ case ROLE_DOMAIN_MEMBER:
+ value_ascii = REG_PT_WINNT;
+ break;
+ }
+ value_length = push_ucs2(value, value, value_ascii,
+ sizeof(value),
+ STR_TERMINATE|STR_NOALIGN);
+ regval_ctr_addvalue(&regvals, REGSTR_PRODUCTTYPE, REG_SZ,
+ value, value_length);
+
+ val = dup_registry_value( regval_ctr_specific_value( &regvals, 0 ) );
+
+ status = NT_STATUS_OK;
+
+ goto out;
+ }
+
+ /* else fall back to actually looking up the value */
+
+ for ( i=0; fetch_reg_values_specific(regkey, &val, i); i++ )
+ {
+ DEBUG(10,("_reg_info: Testing value [%s]\n", val->valuename));
+ if ( StrCaseCmp( val->valuename, name ) == 0 ) {
+ DEBUG(10,("_reg_info: Found match for value [%s]\n", name));
+ status = NT_STATUS_OK;
+ break;
+ }
+
+ free_registry_value( val );
+ }
+
+
+out:
+ new_init_reg_r_info(q_u->ptr_buf, r_u, val, status);
+
+ regval_ctr_destroy( &regvals );
+ free_registry_value( val );
+
+ DEBUG(5,("_reg_info: Exit\n"));
+
+ return status;
+}
+
+
+/*****************************************************************************
+ Implementation of REG_QUERY_KEY
+ ****************************************************************************/
+
+NTSTATUS _reg_query_key(pipes_struct *p, REG_Q_QUERY_KEY *q_u, REG_R_QUERY_KEY *r_u)
+{
+ NTSTATUS status = NT_STATUS_OK;
+ REGISTRY_KEY *regkey = find_regkey_index_by_hnd( p, &q_u->pol );
+
+ DEBUG(5,("_reg_query_key: Enter\n"));
+
+ if ( !regkey )
+ return NT_STATUS_INVALID_HANDLE;
+
+ if ( !get_subkey_information( regkey, &r_u->num_subkeys, &r_u->max_subkeylen ) )
+ return NT_STATUS_ACCESS_DENIED;
+
+ if ( !get_value_information( regkey, &r_u->num_values, &r_u->max_valnamelen, &r_u->max_valbufsize ) )
+ return NT_STATUS_ACCESS_DENIED;
+
+
+ r_u->sec_desc = 0x00000078; /* size for key's sec_desc */
+
+ /* Win9x set this to 0x0 since it does not keep timestamps.
+ Doing the same here for simplicity --jerry */
+
+ ZERO_STRUCT(r_u->mod_time);
+
+ DEBUG(5,("_reg_query_key: Exit\n"));
+
+ return status;
+}
+
+
+/*****************************************************************************
+ Implementation of REG_UNKNOWN_1A
+ ****************************************************************************/
+
+NTSTATUS _reg_unknown_1a(pipes_struct *p, REG_Q_UNKNOWN_1A *q_u, REG_R_UNKNOWN_1A *r_u)
+{
+ NTSTATUS status = NT_STATUS_OK;
+ REGISTRY_KEY *regkey = find_regkey_index_by_hnd( p, &q_u->pol );
+
+ DEBUG(5,("_reg_unknown_1a: Enter\n"));
+
+ if ( !regkey )
+ return NT_STATUS_INVALID_HANDLE;
+
+ r_u->unknown = 0x00000005; /* seems to be consistent...no idea what it means */
+
+ DEBUG(5,("_reg_unknown_1a: Exit\n"));
+
+ return status;
+}
+
+
+/*****************************************************************************
+ Implementation of REG_ENUM_KEY
+ ****************************************************************************/
+
+NTSTATUS _reg_enum_key(pipes_struct *p, REG_Q_ENUM_KEY *q_u, REG_R_ENUM_KEY *r_u)
+{
+ NTSTATUS status = NT_STATUS_OK;
+ REGISTRY_KEY *regkey = find_regkey_index_by_hnd( p, &q_u->pol );
+ char *subkey = NULL;
+
+
+ DEBUG(5,("_reg_enum_key: Enter\n"));
+
+ if ( !regkey )
+ return NT_STATUS_INVALID_HANDLE;
+
+ DEBUG(8,("_reg_enum_key: enumerating key [%s]\n", regkey->name));
+
+ if ( !fetch_reg_keys_specific( regkey, &subkey, q_u->key_index ) )
+ {
+ status = NT_STATUS_NO_MORE_ENTRIES;
+ goto done;
+ }
+
+ DEBUG(10,("_reg_enum_key: retrieved subkey named [%s]\n", subkey));
+
+ /* subkey has the string name now */
+
+ init_reg_r_enum_key( r_u, subkey, q_u->unknown_1, q_u->unknown_2 );
+
+ DEBUG(5,("_reg_enum_key: Exit\n"));
+
+done:
+ SAFE_FREE( subkey );
+ return status;
+}
+
+/*****************************************************************************
+ Implementation of REG_ENUM_VALUE
+ ****************************************************************************/
+
+NTSTATUS _reg_enum_value(pipes_struct *p, REG_Q_ENUM_VALUE *q_u, REG_R_ENUM_VALUE *r_u)
+{
+ NTSTATUS status = NT_STATUS_OK;
+ REGISTRY_KEY *regkey = find_regkey_index_by_hnd( p, &q_u->pol );
+ REGISTRY_VALUE *val;
+
+
+ DEBUG(5,("_reg_enum_value: Enter\n"));
+
+ if ( !regkey )
+ return NT_STATUS_INVALID_HANDLE;
+
+ DEBUG(8,("_reg_enum_key: enumerating values for key [%s]\n", regkey->name));
+
+ if ( !fetch_reg_values_specific( regkey, &val, q_u->val_index ) )
+ {
+ status = NT_STATUS_NO_MORE_ENTRIES;
+ goto done;
+ }
+
+ DEBUG(10,("_reg_enum_value: retrieved value named [%s]\n", val->valuename));
+
+ /* subkey has the string name now */
+
+ init_reg_r_enum_val( r_u, val );
+
+
+ DEBUG(5,("_reg_enum_value: Exit\n"));
+
+done:
+ free_registry_value( val );
+
+ return status;
+}
+
+
+/*******************************************************************
+ reg_shutdwon
+ ********************************************************************/
+
+#define SHUTDOWN_R_STRING "-r"
+#define SHUTDOWN_F_STRING "-f"
+
+
+NTSTATUS _reg_shutdown(pipes_struct *p, REG_Q_SHUTDOWN *q_u, REG_R_SHUTDOWN *r_u)
+{
+ NTSTATUS status = NT_STATUS_OK;
+ pstring shutdown_script;
+ UNISTR2 unimsg = q_u->uni_msg;
+ pstring message;
+ pstring chkmsg;
+ fstring timeout;
+ fstring r;
+ fstring f;
+
+ /* message */
+ rpcstr_pull (message, unimsg.buffer, sizeof(message), unimsg.uni_str_len*2,0);
+ /* security check */
+ alpha_strcpy (chkmsg, message, NULL, sizeof(message));
+ /* timeout */
+ fstr_sprintf(timeout, "%d", q_u->timeout);
+ /* reboot */
+ fstr_sprintf(r, (q_u->reboot) ? SHUTDOWN_R_STRING : "");
+ /* force */
+ fstr_sprintf(f, (q_u->force) ? SHUTDOWN_F_STRING : "");
+
+ pstrcpy(shutdown_script, lp_shutdown_script());
+
+ if(*shutdown_script) {
+ int shutdown_ret;
+ all_string_sub(shutdown_script, "%m", chkmsg, sizeof(shutdown_script));
+ all_string_sub(shutdown_script, "%t", timeout, sizeof(shutdown_script));
+ all_string_sub(shutdown_script, "%r", r, sizeof(shutdown_script));
+ all_string_sub(shutdown_script, "%f", f, sizeof(shutdown_script));
+ shutdown_ret = smbrun(shutdown_script,NULL);
+ DEBUG(3,("_reg_shutdown: Running the command `%s' gave %d\n",shutdown_script,shutdown_ret));
+ }
+
+ return status;
+}
+
+/*******************************************************************
+ reg_abort_shutdwon
+ ********************************************************************/
+
+NTSTATUS _reg_abort_shutdown(pipes_struct *p, REG_Q_ABORT_SHUTDOWN *q_u, REG_R_ABORT_SHUTDOWN *r_u)
+{
+ NTSTATUS status = NT_STATUS_OK;
+ pstring abort_shutdown_script;
+
+ pstrcpy(abort_shutdown_script, lp_abort_shutdown_script());
+
+ if(*abort_shutdown_script) {
+ int abort_shutdown_ret;
+ abort_shutdown_ret = smbrun(abort_shutdown_script,NULL);
+ DEBUG(3,("_reg_abort_shutdown: Running the command `%s' gave %d\n",abort_shutdown_script,abort_shutdown_ret));
+ }
+
+ return status;
+}
+
+/*******************************************************************
+ REG_SAVE_KEY (0x14)
+ ********************************************************************/
+
+NTSTATUS _reg_save_key(pipes_struct *p, REG_Q_SAVE_KEY *q_u, REG_R_SAVE_KEY *r_u)
+{
+ REGISTRY_KEY *regkey = find_regkey_index_by_hnd( p, &q_u->pol );
+
+ DEBUG(5,("_reg_save_key: Enter\n"));
+
+ /*
+ * basically this is a no op function which just gverifies
+ * that the client gave us a valid registry key handle
+ */
+
+ if ( !regkey )
+ return NT_STATUS_INVALID_HANDLE;
+
+ DEBUG(8,("_reg_save_key: berifying backup of key [%s]\n", regkey->name));
+
+
+ return NT_STATUS_OK;
+}
+
+
diff --git a/source/rpc_server/srv_samr.c b/source/rpc_server/srv_samr.c
new file mode 100644
index 00000000000..971f5ed40ce
--- /dev/null
+++ b/source/rpc_server/srv_samr.c
@@ -0,0 +1,1508 @@
+/*
+ * Unix SMB/CIFS implementation.
+ * RPC Pipe client / server routines
+ * Copyright (C) Andrew Tridgell 1992-1997,
+ * Copyright (C) Luke Kenneth Casson Leighton 1996-1997,
+ * Copyright (C) Paul Ashton 1997,
+ * Copyright (C) Marc Jacobsen 1999,
+ * Copyright (C) Jean François Micouleau 1998-2001,
+ * Copyright (C) Jim McDonough <jmcd@us.ibm.com> 2002-2003.
+ *
+ * Split into interface and implementation modules by,
+ *
+ * Copyright (C) Jeremy Allison 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 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.
+ */
+
+/*
+ * This is the interface to the SAMR code.
+ */
+
+#include "includes.h"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_RPC_SRV
+
+/*******************************************************************
+ api_samr_close_hnd
+ ********************************************************************/
+
+static BOOL api_samr_close_hnd(pipes_struct *p)
+{
+ SAMR_Q_CLOSE_HND q_u;
+ SAMR_R_CLOSE_HND r_u;
+ prs_struct *data = &p->in_data.data;
+ prs_struct *rdata = &p->out_data.rdata;
+
+ ZERO_STRUCT(q_u);
+ ZERO_STRUCT(r_u);
+
+ if(!samr_io_q_close_hnd("", &q_u, data, 0)) {
+ DEBUG(0,("api_samr_close_hnd: unable to unmarshall SAMR_Q_CLOSE_HND.\n"));
+ return False;
+ }
+
+ r_u.status = _samr_close_hnd(p, &q_u, &r_u);
+
+ /* store the response in the SMB stream */
+ if(!samr_io_r_close_hnd("", &r_u, rdata, 0)) {
+ DEBUG(0,("api_samr_close_hnd: unable to marshall SAMR_R_CLOSE_HND.\n"));
+ return False;
+ }
+
+ return True;
+}
+
+/*******************************************************************
+ api_samr_open_domain
+ ********************************************************************/
+
+static BOOL api_samr_open_domain(pipes_struct *p)
+{
+ SAMR_Q_OPEN_DOMAIN q_u;
+ SAMR_R_OPEN_DOMAIN r_u;
+
+ prs_struct *data = &p->in_data.data;
+ prs_struct *rdata = &p->out_data.rdata;
+
+ ZERO_STRUCT(q_u);
+ ZERO_STRUCT(r_u);
+
+ if(!samr_io_q_open_domain("", &q_u, data, 0)) {
+ DEBUG(0,("api_samr_open_domain: unable to unmarshall SAMR_Q_OPEN_DOMAIN.\n"));
+ return False;
+ }
+
+ r_u.status = _samr_open_domain(p, &q_u, &r_u);
+
+ /* store the response in the SMB stream */
+ if(!samr_io_r_open_domain("", &r_u, rdata, 0)) {
+ DEBUG(0,("api_samr_open_domain: unable to marshall SAMR_R_OPEN_DOMAIN.\n"));
+ return False;
+ }
+
+ return True;
+}
+
+/*******************************************************************
+ api_samr_get_usrdom_pwinfo
+ ********************************************************************/
+
+static BOOL api_samr_get_usrdom_pwinfo(pipes_struct *p)
+{
+ SAMR_Q_GET_USRDOM_PWINFO q_u;
+ SAMR_R_GET_USRDOM_PWINFO r_u;
+
+ prs_struct *data = &p->in_data.data;
+ prs_struct *rdata = &p->out_data.rdata;
+
+ ZERO_STRUCT(q_u);
+ ZERO_STRUCT(r_u);
+
+ if(!samr_io_q_get_usrdom_pwinfo("", &q_u, data, 0)) {
+ DEBUG(0,("api_samr_get_usrdom_pwinfo: unable to unmarshall SAMR_Q_GET_USRDOM_PWINFO.\n"));
+ return False;
+ }
+
+ r_u.status = _samr_get_usrdom_pwinfo(p, &q_u, &r_u);
+
+ if(!samr_io_r_get_usrdom_pwinfo("", &r_u, rdata, 0)) {
+ DEBUG(0,("api_samr_get_usrdom_pwinfo: unable to marshall SAMR_R_GET_USRDOM_PWINFO.\n"));
+ return False;
+ }
+
+ return True;
+}
+
+/*******************************************************************
+ api_samr_set_sec_obj
+ ********************************************************************/
+
+static BOOL api_samr_set_sec_obj(pipes_struct *p)
+{
+ SAMR_Q_SET_SEC_OBJ q_u;
+ SAMR_R_SET_SEC_OBJ r_u;
+
+ prs_struct *data = &p->in_data.data;
+ prs_struct *rdata = &p->out_data.rdata;
+
+ ZERO_STRUCT(q_u);
+ ZERO_STRUCT(r_u);
+
+ if(!samr_io_q_set_sec_obj("", &q_u, data, 0)) {
+ DEBUG(0,("api_samr_set_sec_obj: unable to unmarshall SAMR_Q_SET_SEC_OBJ.\n"));
+ return False;
+ }
+
+ r_u.status = _samr_set_sec_obj(p, &q_u, &r_u);
+
+ if(!samr_io_r_set_sec_obj("", &r_u, rdata, 0)) {
+ DEBUG(0,("api_samr_set_sec_obj: unable to marshall SAMR_R_SET_SEC_OBJ.\n"));
+ return False;
+ }
+
+ return True;
+}
+
+/*******************************************************************
+ api_samr_query_sec_obj
+ ********************************************************************/
+
+static BOOL api_samr_query_sec_obj(pipes_struct *p)
+{
+ SAMR_Q_QUERY_SEC_OBJ q_u;
+ SAMR_R_QUERY_SEC_OBJ r_u;
+
+ prs_struct *data = &p->in_data.data;
+ prs_struct *rdata = &p->out_data.rdata;
+
+ ZERO_STRUCT(q_u);
+ ZERO_STRUCT(r_u);
+
+ if(!samr_io_q_query_sec_obj("", &q_u, data, 0)) {
+ DEBUG(0,("api_samr_query_sec_obj: unable to unmarshall SAMR_Q_QUERY_SEC_OBJ.\n"));
+ return False;
+ }
+
+ r_u.status = _samr_query_sec_obj(p, &q_u, &r_u);
+
+ if(!samr_io_r_query_sec_obj("", &r_u, rdata, 0)) {
+ DEBUG(0,("api_samr_query_sec_obj: unable to marshall SAMR_R_QUERY_SEC_OBJ.\n"));
+ return False;
+ }
+
+ return True;
+}
+
+/*******************************************************************
+ api_samr_enum_dom_users
+ ********************************************************************/
+
+static BOOL api_samr_enum_dom_users(pipes_struct *p)
+{
+ SAMR_Q_ENUM_DOM_USERS q_u;
+ SAMR_R_ENUM_DOM_USERS r_u;
+ prs_struct *data = &p->in_data.data;
+ prs_struct *rdata = &p->out_data.rdata;
+
+ ZERO_STRUCT(q_u);
+ ZERO_STRUCT(r_u);
+
+ /* grab the samr open */
+ if(!samr_io_q_enum_dom_users("", &q_u, data, 0)) {
+ DEBUG(0,("api_samr_enum_dom_users: unable to unmarshall SAMR_Q_ENUM_DOM_USERS.\n"));
+ return False;
+ }
+
+ r_u.status = _samr_enum_dom_users(p, &q_u, &r_u);
+
+ /* store the response in the SMB stream */
+ if(!samr_io_r_enum_dom_users("", &r_u, rdata, 0)) {
+ DEBUG(0,("api_samr_enum_dom_users: unable to marshall SAMR_R_ENUM_DOM_USERS.\n"));
+ return False;
+ }
+
+ return True;
+}
+
+/*******************************************************************
+ api_samr_enum_dom_groups
+ ********************************************************************/
+
+static BOOL api_samr_enum_dom_groups(pipes_struct *p)
+{
+ SAMR_Q_ENUM_DOM_GROUPS q_u;
+ SAMR_R_ENUM_DOM_GROUPS r_u;
+ prs_struct *data = &p->in_data.data;
+ prs_struct *rdata = &p->out_data.rdata;
+
+ ZERO_STRUCT(q_u);
+ ZERO_STRUCT(r_u);
+
+ /* grab the samr open */
+ if(!samr_io_q_enum_dom_groups("", &q_u, data, 0)) {
+ DEBUG(0,("api_samr_enum_dom_groups: unable to unmarshall SAMR_Q_ENUM_DOM_GROUPS.\n"));
+ return False;
+ }
+
+ r_u.status = _samr_enum_dom_groups(p, &q_u, &r_u);
+
+ /* store the response in the SMB stream */
+ if(!samr_io_r_enum_dom_groups("", &r_u, rdata, 0)) {
+ DEBUG(0,("api_samr_enum_dom_groups: unable to marshall SAMR_R_ENUM_DOM_GROUPS.\n"));
+ return False;
+ }
+
+ return True;
+}
+
+/*******************************************************************
+ api_samr_enum_dom_aliases
+ ********************************************************************/
+
+static BOOL api_samr_enum_dom_aliases(pipes_struct *p)
+{
+ SAMR_Q_ENUM_DOM_ALIASES q_u;
+ SAMR_R_ENUM_DOM_ALIASES r_u;
+ prs_struct *data = &p->in_data.data;
+ prs_struct *rdata = &p->out_data.rdata;
+
+ ZERO_STRUCT(q_u);
+ ZERO_STRUCT(r_u);
+
+ /* grab the samr open */
+ if(!samr_io_q_enum_dom_aliases("", &q_u, data, 0)) {
+ DEBUG(0,("api_samr_enum_dom_aliases: unable to unmarshall SAMR_Q_ENUM_DOM_ALIASES.\n"));
+ return False;
+ }
+
+ r_u.status = _samr_enum_dom_aliases(p, &q_u, &r_u);
+
+ /* store the response in the SMB stream */
+ if(!samr_io_r_enum_dom_aliases("", &r_u, rdata, 0)) {
+ DEBUG(0,("api_samr_enum_dom_aliases: unable to marshall SAMR_R_ENUM_DOM_ALIASES.\n"));
+ return False;
+ }
+
+ return True;
+}
+
+/*******************************************************************
+ api_samr_query_dispinfo
+ ********************************************************************/
+
+static BOOL api_samr_query_dispinfo(pipes_struct *p)
+{
+ SAMR_Q_QUERY_DISPINFO q_u;
+ SAMR_R_QUERY_DISPINFO r_u;
+ prs_struct *data = &p->in_data.data;
+ prs_struct *rdata = &p->out_data.rdata;
+
+ ZERO_STRUCT(q_u);
+ ZERO_STRUCT(r_u);
+
+ if(!samr_io_q_query_dispinfo("", &q_u, data, 0)) {
+ DEBUG(0,("api_samr_query_dispinfo: unable to unmarshall SAMR_Q_QUERY_DISPINFO.\n"));
+ return False;
+ }
+
+ r_u.status = _samr_query_dispinfo(p, &q_u, &r_u);
+
+ /* store the response in the SMB stream */
+ if(!samr_io_r_query_dispinfo("", &r_u, rdata, 0)) {
+ DEBUG(0,("api_samr_query_dispinfo: unable to marshall SAMR_R_QUERY_DISPINFO.\n"));
+ return False;
+ }
+
+ return True;
+}
+
+/*******************************************************************
+ api_samr_query_aliasinfo
+ ********************************************************************/
+
+static BOOL api_samr_query_aliasinfo(pipes_struct *p)
+{
+ SAMR_Q_QUERY_ALIASINFO q_u;
+ SAMR_R_QUERY_ALIASINFO r_u;
+ prs_struct *data = &p->in_data.data;
+ prs_struct *rdata = &p->out_data.rdata;
+
+ ZERO_STRUCT(q_u);
+ ZERO_STRUCT(r_u);
+
+ /* grab the samr open */
+ if(!samr_io_q_query_aliasinfo("", &q_u, data, 0)) {
+ DEBUG(0,("api_samr_query_aliasinfo: unable to unmarshall SAMR_Q_QUERY_ALIASINFO.\n"));
+ return False;
+ }
+
+ r_u.status = _samr_query_aliasinfo(p, &q_u, &r_u);
+
+ /* store the response in the SMB stream */
+ if(!samr_io_r_query_aliasinfo("", &r_u, rdata, 0)) {
+ DEBUG(0,("api_samr_query_aliasinfo: unable to marshall SAMR_R_QUERY_ALIASINFO.\n"));
+ return False;
+ }
+
+ return True;
+}
+
+/*******************************************************************
+ api_samr_lookup_names
+ ********************************************************************/
+
+static BOOL api_samr_lookup_names(pipes_struct *p)
+{
+ SAMR_Q_LOOKUP_NAMES q_u;
+ SAMR_R_LOOKUP_NAMES r_u;
+ prs_struct *data = &p->in_data.data;
+ prs_struct *rdata = &p->out_data.rdata;
+
+ ZERO_STRUCT(q_u);
+ ZERO_STRUCT(r_u);
+
+ /* grab the samr lookup names */
+ if(!samr_io_q_lookup_names("", &q_u, data, 0)) {
+ DEBUG(0,("api_samr_lookup_names: unable to unmarshall SAMR_Q_LOOKUP_NAMES.\n"));
+ return False;
+ }
+
+ r_u.status = _samr_lookup_names(p, &q_u, &r_u);
+
+ /* store the response in the SMB stream */
+ if(!samr_io_r_lookup_names("", &r_u, rdata, 0)) {
+ DEBUG(0,("api_samr_lookup_names: unable to marshall SAMR_R_LOOKUP_NAMES.\n"));
+ return False;
+ }
+
+ return True;
+}
+
+/*******************************************************************
+ api_samr_chgpasswd_user
+ ********************************************************************/
+
+static BOOL api_samr_chgpasswd_user(pipes_struct *p)
+{
+ SAMR_Q_CHGPASSWD_USER q_u;
+ SAMR_R_CHGPASSWD_USER r_u;
+ prs_struct *data = &p->in_data.data;
+ prs_struct *rdata = &p->out_data.rdata;
+
+ ZERO_STRUCT(q_u);
+ ZERO_STRUCT(r_u);
+
+ /* change password request */
+ if (!samr_io_q_chgpasswd_user("", &q_u, data, 0)) {
+ DEBUG(0,("api_samr_chgpasswd_user: Failed to unmarshall SAMR_Q_CHGPASSWD_USER.\n"));
+ return False;
+ }
+
+ r_u.status = _samr_chgpasswd_user(p, &q_u, &r_u);
+
+ /* store the response in the SMB stream */
+ if(!samr_io_r_chgpasswd_user("", &r_u, rdata, 0)) {
+ DEBUG(0,("api_samr_chgpasswd_user: Failed to marshall SAMR_R_CHGPASSWD_USER.\n" ));
+ return False;
+ }
+
+ return True;
+}
+
+/*******************************************************************
+ api_samr_lookup_rids
+ ********************************************************************/
+
+static BOOL api_samr_lookup_rids(pipes_struct *p)
+{
+ SAMR_Q_LOOKUP_RIDS q_u;
+ SAMR_R_LOOKUP_RIDS r_u;
+ prs_struct *data = &p->in_data.data;
+ prs_struct *rdata = &p->out_data.rdata;
+
+ ZERO_STRUCT(q_u);
+ ZERO_STRUCT(r_u);
+
+ /* grab the samr lookup names */
+ if(!samr_io_q_lookup_rids("", &q_u, data, 0)) {
+ DEBUG(0,("api_samr_lookup_rids: unable to unmarshall SAMR_Q_LOOKUP_RIDS.\n"));
+ return False;
+ }
+
+ r_u.status = _samr_lookup_rids(p, &q_u, &r_u);
+
+ /* store the response in the SMB stream */
+ if(!samr_io_r_lookup_rids("", &r_u, rdata, 0)) {
+ DEBUG(0,("api_samr_lookup_rids: unable to marshall SAMR_R_LOOKUP_RIDS.\n"));
+ return False;
+ }
+
+ return True;
+}
+
+/*******************************************************************
+ api_samr_open_user
+ ********************************************************************/
+
+static BOOL api_samr_open_user(pipes_struct *p)
+{
+ SAMR_Q_OPEN_USER q_u;
+ SAMR_R_OPEN_USER r_u;
+ prs_struct *data = &p->in_data.data;
+ prs_struct *rdata = &p->out_data.rdata;
+
+ ZERO_STRUCT(q_u);
+ ZERO_STRUCT(r_u);
+
+ if(!samr_io_q_open_user("", &q_u, data, 0)) {
+ DEBUG(0,("api_samr_open_user: unable to unmarshall SAMR_Q_OPEN_USER.\n"));
+ return False;
+ }
+
+ r_u.status = _samr_open_user(p, &q_u, &r_u);
+
+ /* store the response in the SMB stream */
+ if(!samr_io_r_open_user("", &r_u, rdata, 0)) {
+ DEBUG(0,("api_samr_open_user: unable to marshall SAMR_R_OPEN_USER.\n"));
+ return False;
+ }
+
+ return True;
+}
+
+/*******************************************************************
+ api_samr_query_userinfo
+ ********************************************************************/
+
+static BOOL api_samr_query_userinfo(pipes_struct *p)
+{
+ SAMR_Q_QUERY_USERINFO q_u;
+ SAMR_R_QUERY_USERINFO r_u;
+ prs_struct *data = &p->in_data.data;
+ prs_struct *rdata = &p->out_data.rdata;
+
+ ZERO_STRUCT(q_u);
+ ZERO_STRUCT(r_u);
+
+ if(!samr_io_q_query_userinfo("", &q_u, data, 0)){
+ DEBUG(0,("api_samr_query_userinfo: unable to unmarshall SAMR_Q_QUERY_USERINFO.\n"));
+ return False;
+ }
+
+ r_u.status = _samr_query_userinfo(p, &q_u, &r_u);
+
+ /* store the response in the SMB stream */
+ if(!samr_io_r_query_userinfo("", &r_u, rdata, 0)) {
+ DEBUG(0,("api_samr_query_userinfo: unable to marshall SAMR_R_QUERY_USERINFO.\n"));
+ return False;
+ }
+
+ return True;
+}
+
+/*******************************************************************
+ api_samr_query_usergroups
+ ********************************************************************/
+
+static BOOL api_samr_query_usergroups(pipes_struct *p)
+{
+ SAMR_Q_QUERY_USERGROUPS q_u;
+ SAMR_R_QUERY_USERGROUPS r_u;
+ prs_struct *data = &p->in_data.data;
+ prs_struct *rdata = &p->out_data.rdata;
+
+ ZERO_STRUCT(q_u);
+ ZERO_STRUCT(r_u);
+
+ if(!samr_io_q_query_usergroups("", &q_u, data, 0)) {
+ DEBUG(0,("api_samr_query_usergroups: unable to unmarshall SAMR_Q_QUERY_USERGROUPS.\n"));
+ return False;
+ }
+
+ r_u.status = _samr_query_usergroups(p, &q_u, &r_u);
+
+ /* store the response in the SMB stream */
+ if(!samr_io_r_query_usergroups("", &r_u, rdata, 0)) {
+ DEBUG(0,("api_samr_query_usergroups: unable to marshall SAMR_R_QUERY_USERGROUPS.\n"));
+ return False;
+ }
+
+ return True;
+}
+
+/*******************************************************************
+ api_samr_query_dom_info
+ ********************************************************************/
+
+static BOOL api_samr_query_dom_info(pipes_struct *p)
+{
+ SAMR_Q_QUERY_DOMAIN_INFO q_u;
+ SAMR_R_QUERY_DOMAIN_INFO r_u;
+ prs_struct *data = &p->in_data.data;
+ prs_struct *rdata = &p->out_data.rdata;
+
+ ZERO_STRUCT(q_u);
+ ZERO_STRUCT(r_u);
+
+ if(!samr_io_q_query_dom_info("", &q_u, data, 0)) {
+ DEBUG(0,("api_samr_query_dom_info: unable to unmarshall SAMR_Q_QUERY_DOMAIN_INFO.\n"));
+ return False;
+ }
+
+ r_u.status = _samr_query_dom_info(p, &q_u, &r_u);
+
+ /* store the response in the SMB stream */
+ if(!samr_io_r_query_dom_info("", &r_u, rdata, 0)) {
+ DEBUG(0,("api_samr_query_dom_info: unable to marshall SAMR_R_QUERY_DOMAIN_INFO.\n"));
+ return False;
+ }
+
+ return True;
+}
+
+/*******************************************************************
+ api_samr_create_user
+ ********************************************************************/
+
+static BOOL api_samr_create_user(pipes_struct *p)
+{
+ prs_struct *data = &p->in_data.data;
+ prs_struct *rdata = &p->out_data.rdata;
+
+ SAMR_Q_CREATE_USER q_u;
+ SAMR_R_CREATE_USER r_u;
+
+ ZERO_STRUCT(q_u);
+ ZERO_STRUCT(r_u);
+
+ /* grab the samr create user */
+ if (!samr_io_q_create_user("", &q_u, data, 0)) {
+ DEBUG(0,("api_samr_create_user: Unable to unmarshall SAMR_Q_CREATE_USER.\n"));
+ return False;
+ }
+
+ r_u.status=_samr_create_user(p, &q_u, &r_u);
+
+ /* store the response in the SMB stream */
+ if(!samr_io_r_create_user("", &r_u, rdata, 0)) {
+ DEBUG(0,("api_samr_create_user: Unable to marshall SAMR_R_CREATE_USER.\n"));
+ return False;
+ }
+
+ return True;
+}
+
+/*******************************************************************
+ api_samr_connect_anon
+ ********************************************************************/
+
+static BOOL api_samr_connect_anon(pipes_struct *p)
+{
+ SAMR_Q_CONNECT_ANON q_u;
+ SAMR_R_CONNECT_ANON r_u;
+ prs_struct *data = &p->in_data.data;
+ prs_struct *rdata = &p->out_data.rdata;
+
+ ZERO_STRUCT(q_u);
+ ZERO_STRUCT(r_u);
+
+ /* grab the samr open policy */
+ if(!samr_io_q_connect_anon("", &q_u, data, 0)) {
+ DEBUG(0,("api_samr_connect_anon: unable to unmarshall SAMR_Q_CONNECT_ANON.\n"));
+ return False;
+ }
+
+ r_u.status = _samr_connect_anon(p, &q_u, &r_u);
+
+ /* store the response in the SMB stream */
+ if(!samr_io_r_connect_anon("", &r_u, rdata, 0)) {
+ DEBUG(0,("api_samr_connect_anon: unable to marshall SAMR_R_CONNECT_ANON.\n"));
+ return False;
+ }
+
+ return True;
+}
+
+/*******************************************************************
+ api_samr_connect
+ ********************************************************************/
+
+static BOOL api_samr_connect(pipes_struct *p)
+{
+ SAMR_Q_CONNECT q_u;
+ SAMR_R_CONNECT r_u;
+ prs_struct *data = &p->in_data.data;
+ prs_struct *rdata = &p->out_data.rdata;
+
+ ZERO_STRUCT(q_u);
+ ZERO_STRUCT(r_u);
+
+ /* grab the samr open policy */
+ if(!samr_io_q_connect("", &q_u, data, 0)) {
+ DEBUG(0,("api_samr_connect: unable to unmarshall SAMR_Q_CONNECT.\n"));
+ return False;
+ }
+
+ r_u.status = _samr_connect(p, &q_u, &r_u);
+
+ /* store the response in the SMB stream */
+ if(!samr_io_r_connect("", &r_u, rdata, 0)) {
+ DEBUG(0,("api_samr_connect: unable to marshall SAMR_R_CONNECT.\n"));
+ return False;
+ }
+
+ return True;
+}
+
+/*******************************************************************
+ api_samr_connect4
+ ********************************************************************/
+
+static BOOL api_samr_connect4(pipes_struct *p)
+{
+ SAMR_Q_CONNECT4 q_u;
+ SAMR_R_CONNECT4 r_u;
+ prs_struct *data = &p->in_data.data;
+ prs_struct *rdata = &p->out_data.rdata;
+
+ ZERO_STRUCT(q_u);
+ ZERO_STRUCT(r_u);
+
+ /* grab the samr open policy */
+ if(!samr_io_q_connect4("", &q_u, data, 0)) {
+ DEBUG(0,("api_samr_connect4: unable to unmarshall SAMR_Q_CONNECT4.\n"));
+ return False;
+ }
+
+ r_u.status = _samr_connect4(p, &q_u, &r_u);
+
+ /* store the response in the SMB stream */
+ if(!samr_io_r_connect4("", &r_u, rdata, 0)) {
+ DEBUG(0,("api_samr_connect4: unable to marshall SAMR_R_CONNECT4.\n"));
+ return False;
+ }
+
+ return True;
+}
+
+/**********************************************************************
+ api_samr_lookup_domain
+ **********************************************************************/
+
+static BOOL api_samr_lookup_domain(pipes_struct *p)
+{
+ SAMR_Q_LOOKUP_DOMAIN q_u;
+ SAMR_R_LOOKUP_DOMAIN r_u;
+ prs_struct *data = &p->in_data.data;
+ prs_struct *rdata = &p->out_data.rdata;
+
+ ZERO_STRUCT(q_u);
+ ZERO_STRUCT(r_u);
+
+ if(!samr_io_q_lookup_domain("", &q_u, data, 0)) {
+ DEBUG(0,("api_samr_lookup_domain: Unable to unmarshall SAMR_Q_LOOKUP_DOMAIN.\n"));
+ return False;
+ }
+
+ r_u.status = _samr_lookup_domain(p, &q_u, &r_u);
+
+ if(!samr_io_r_lookup_domain("", &r_u, rdata, 0)){
+ DEBUG(0,("api_samr_lookup_domain: Unable to marshall SAMR_R_LOOKUP_DOMAIN.\n"));
+ return False;
+ }
+
+ return True;
+}
+
+/**********************************************************************
+ api_samr_enum_domains
+ **********************************************************************/
+
+static BOOL api_samr_enum_domains(pipes_struct *p)
+{
+ SAMR_Q_ENUM_DOMAINS q_u;
+ SAMR_R_ENUM_DOMAINS r_u;
+ prs_struct *data = &p->in_data.data;
+ prs_struct *rdata = &p->out_data.rdata;
+
+ ZERO_STRUCT(q_u);
+ ZERO_STRUCT(r_u);
+
+ if(!samr_io_q_enum_domains("", &q_u, data, 0)) {
+ DEBUG(0,("api_samr_enum_domains: Unable to unmarshall SAMR_Q_ENUM_DOMAINS.\n"));
+ return False;
+ }
+
+ r_u.status = _samr_enum_domains(p, &q_u, &r_u);
+
+ if(!samr_io_r_enum_domains("", &r_u, rdata, 0)) {
+ DEBUG(0,("api_samr_enum_domains: Unable to marshall SAMR_R_ENUM_DOMAINS.\n"));
+ return False;
+ }
+
+ return True;
+}
+
+/*******************************************************************
+ api_samr_open_alias
+ ********************************************************************/
+
+static BOOL api_samr_open_alias(pipes_struct *p)
+{
+ SAMR_Q_OPEN_ALIAS q_u;
+ SAMR_R_OPEN_ALIAS r_u;
+ prs_struct *data = &p->in_data.data;
+ prs_struct *rdata = &p->out_data.rdata;
+
+ ZERO_STRUCT(q_u);
+ ZERO_STRUCT(r_u);
+
+ /* grab the samr open policy */
+ if(!samr_io_q_open_alias("", &q_u, data, 0)) {
+ DEBUG(0,("api_samr_open_alias: Unable to unmarshall SAMR_Q_OPEN_ALIAS.\n"));
+ return False;
+ }
+
+ r_u.status=_samr_open_alias(p, &q_u, &r_u);
+
+ /* store the response in the SMB stream */
+ if(!samr_io_r_open_alias("", &r_u, rdata, 0)) {
+ DEBUG(0,("api_samr_open_alias: Unable to marshall SAMR_R_OPEN_ALIAS.\n"));
+ return False;
+ }
+
+ return True;
+}
+
+/*******************************************************************
+ api_samr_set_userinfo
+ ********************************************************************/
+
+static BOOL api_samr_set_userinfo(pipes_struct *p)
+{
+ SAMR_Q_SET_USERINFO q_u;
+ SAMR_R_SET_USERINFO r_u;
+ prs_struct *data = &p->in_data.data;
+ prs_struct *rdata = &p->out_data.rdata;
+
+ ZERO_STRUCT(q_u);
+ ZERO_STRUCT(r_u);
+
+ if (!samr_io_q_set_userinfo("", &q_u, data, 0)) {
+ DEBUG(0,("api_samr_set_userinfo: Unable to unmarshall SAMR_Q_SET_USERINFO.\n"));
+ /* Fix for W2K SP2 */
+ if (q_u.switch_value == 0x1a) {
+ setup_fault_pdu(p, NT_STATUS(0x1c000006));
+ return True;
+ }
+ return False;
+ }
+
+ r_u.status = _samr_set_userinfo(p, &q_u, &r_u);
+
+ if(!samr_io_r_set_userinfo("", &r_u, rdata, 0)) {
+ DEBUG(0,("api_samr_set_userinfo: Unable to marshall SAMR_R_SET_USERINFO.\n"));
+ return False;
+ }
+
+ return True;
+}
+
+/*******************************************************************
+ api_samr_set_userinfo2
+ ********************************************************************/
+
+static BOOL api_samr_set_userinfo2(pipes_struct *p)
+{
+ SAMR_Q_SET_USERINFO2 q_u;
+ SAMR_R_SET_USERINFO2 r_u;
+
+ prs_struct *data = &p->in_data.data;
+ prs_struct *rdata = &p->out_data.rdata;
+
+ ZERO_STRUCT(q_u);
+ ZERO_STRUCT(r_u);
+
+ if (!samr_io_q_set_userinfo2("", &q_u, data, 0)) {
+ DEBUG(0,("api_samr_set_userinfo2: Unable to unmarshall SAMR_Q_SET_USERINFO2.\n"));
+ return False;
+ }
+
+ r_u.status = _samr_set_userinfo2(p, &q_u, &r_u);
+
+ if(!samr_io_r_set_userinfo2("", &r_u, rdata, 0)) {
+ DEBUG(0,("api_samr_set_userinfo2: Unable to marshall SAMR_R_SET_USERINFO2.\n"));
+ return False;
+ }
+
+ return True;
+}
+
+/*******************************************************************
+ api_samr_query_useraliases
+ ********************************************************************/
+
+static BOOL api_samr_query_useraliases(pipes_struct *p)
+{
+ SAMR_Q_QUERY_USERALIASES q_u;
+ SAMR_R_QUERY_USERALIASES r_u;
+
+ prs_struct *data = &p->in_data.data;
+ prs_struct *rdata = &p->out_data.rdata;
+
+ ZERO_STRUCT(q_u);
+ ZERO_STRUCT(r_u);
+
+ if (!samr_io_q_query_useraliases("", &q_u, data, 0)) {
+ DEBUG(0,("api_samr_query_useraliases: Unable to unmarshall SAMR_Q_QUERY_USERALIASES.\n"));
+ return False;
+ }
+
+ r_u.status = _samr_query_useraliases(p, &q_u, &r_u);
+
+ if (! samr_io_r_query_useraliases("", &r_u, rdata, 0)) {
+ DEBUG(0,("api_samr_query_useraliases: Unable to nmarshall SAMR_R_QUERY_USERALIASES.\n"));
+ return False;
+ }
+
+ return True;
+}
+
+/*******************************************************************
+ api_samr_query_aliasmem
+ ********************************************************************/
+
+static BOOL api_samr_query_aliasmem(pipes_struct *p)
+{
+ SAMR_Q_QUERY_ALIASMEM q_u;
+ SAMR_R_QUERY_ALIASMEM r_u;
+
+ prs_struct *data = &p->in_data.data;
+ prs_struct *rdata = &p->out_data.rdata;
+
+ ZERO_STRUCT(q_u);
+ ZERO_STRUCT(r_u);
+
+ if (!samr_io_q_query_aliasmem("", &q_u, data, 0)) {
+ DEBUG(0,("api_samr_query_aliasmem: unable to unmarshall SAMR_Q_QUERY_ALIASMEM.\n"));
+ return False;
+ }
+
+ r_u.status = _samr_query_aliasmem(p, &q_u, &r_u);
+
+ if (!samr_io_r_query_aliasmem("", &r_u, rdata, 0)) {
+ DEBUG(0,("api_samr_query_aliasmem: unable to marshall SAMR_R_QUERY_ALIASMEM.\n"));
+ return False;
+ }
+
+ return True;
+}
+
+/*******************************************************************
+ api_samr_query_groupmem
+ ********************************************************************/
+
+static BOOL api_samr_query_groupmem(pipes_struct *p)
+{
+ SAMR_Q_QUERY_GROUPMEM q_u;
+ SAMR_R_QUERY_GROUPMEM r_u;
+
+ prs_struct *data = &p->in_data.data;
+ prs_struct *rdata = &p->out_data.rdata;
+
+ ZERO_STRUCT(q_u);
+ ZERO_STRUCT(r_u);
+
+ if (!samr_io_q_query_groupmem("", &q_u, data, 0)) {
+ DEBUG(0,("api_samr_query_groupmem: unable to unmarshall SAMR_Q_QUERY_GROUPMEM.\n"));
+ return False;
+ }
+
+ r_u.status = _samr_query_groupmem(p, &q_u, &r_u);
+
+ if (!samr_io_r_query_groupmem("", &r_u, rdata, 0)) {
+ DEBUG(0,("api_samr_query_groupmem: unable to marshall SAMR_R_QUERY_GROUPMEM.\n"));
+ return False;
+ }
+
+ return True;
+}
+
+/*******************************************************************
+ api_samr_add_aliasmem
+ ********************************************************************/
+
+static BOOL api_samr_add_aliasmem(pipes_struct *p)
+{
+ SAMR_Q_ADD_ALIASMEM q_u;
+ SAMR_R_ADD_ALIASMEM r_u;
+
+ prs_struct *data = &p->in_data.data;
+ prs_struct *rdata = &p->out_data.rdata;
+
+ ZERO_STRUCT(q_u);
+ ZERO_STRUCT(r_u);
+
+ if (!samr_io_q_add_aliasmem("", &q_u, data, 0)) {
+ DEBUG(0,("api_samr_add_aliasmem: unable to unmarshall SAMR_Q_ADD_ALIASMEM.\n"));
+ return False;
+ }
+
+ r_u.status = _samr_add_aliasmem(p, &q_u, &r_u);
+
+ if (!samr_io_r_add_aliasmem("", &r_u, rdata, 0)) {
+ DEBUG(0,("api_samr_add_aliasmem: unable to marshall SAMR_R_ADD_ALIASMEM.\n"));
+ return False;
+ }
+
+ return True;
+}
+
+/*******************************************************************
+ api_samr_del_aliasmem
+ ********************************************************************/
+
+static BOOL api_samr_del_aliasmem(pipes_struct *p)
+{
+ SAMR_Q_DEL_ALIASMEM q_u;
+ SAMR_R_DEL_ALIASMEM r_u;
+
+ prs_struct *data = &p->in_data.data;
+ prs_struct *rdata = &p->out_data.rdata;
+
+ ZERO_STRUCT(q_u);
+ ZERO_STRUCT(r_u);
+
+ if (!samr_io_q_del_aliasmem("", &q_u, data, 0)) {
+ DEBUG(0,("api_samr_del_aliasmem: unable to unmarshall SAMR_Q_DEL_ALIASMEM.\n"));
+ return False;
+ }
+
+ r_u.status = _samr_del_aliasmem(p, &q_u, &r_u);
+
+ if (!samr_io_r_del_aliasmem("", &r_u, rdata, 0)) {
+ DEBUG(0,("api_samr_del_aliasmem: unable to marshall SAMR_R_DEL_ALIASMEM.\n"));
+ return False;
+ }
+
+ return True;
+}
+
+/*******************************************************************
+ api_samr_add_groupmem
+ ********************************************************************/
+
+static BOOL api_samr_add_groupmem(pipes_struct *p)
+{
+ SAMR_Q_ADD_GROUPMEM q_u;
+ SAMR_R_ADD_GROUPMEM r_u;
+
+ prs_struct *data = &p->in_data.data;
+ prs_struct *rdata = &p->out_data.rdata;
+
+ ZERO_STRUCT(q_u);
+ ZERO_STRUCT(r_u);
+
+ if (!samr_io_q_add_groupmem("", &q_u, data, 0)) {
+ DEBUG(0,("api_samr_add_groupmem: unable to unmarshall SAMR_Q_ADD_GROUPMEM.\n"));
+ return False;
+ }
+
+ r_u.status = _samr_add_groupmem(p, &q_u, &r_u);
+
+ if (!samr_io_r_add_groupmem("", &r_u, rdata, 0)) {
+ DEBUG(0,("api_samr_add_groupmem: unable to marshall SAMR_R_ADD_GROUPMEM.\n"));
+ return False;
+ }
+
+ return True;
+}
+
+/*******************************************************************
+ api_samr_del_groupmem
+ ********************************************************************/
+
+static BOOL api_samr_del_groupmem(pipes_struct *p)
+{
+ SAMR_Q_DEL_GROUPMEM q_u;
+ SAMR_R_DEL_GROUPMEM r_u;
+
+ prs_struct *data = &p->in_data.data;
+ prs_struct *rdata = &p->out_data.rdata;
+
+ ZERO_STRUCT(q_u);
+ ZERO_STRUCT(r_u);
+
+ if (!samr_io_q_del_groupmem("", &q_u, data, 0)) {
+ DEBUG(0,("api_samr_del_groupmem: unable to unmarshall SAMR_Q_DEL_GROUPMEM.\n"));
+ return False;
+ }
+
+ r_u.status = _samr_del_groupmem(p, &q_u, &r_u);
+
+ if (!samr_io_r_del_groupmem("", &r_u, rdata, 0)) {
+ DEBUG(0,("api_samr_del_groupmem: unable to marshall SAMR_R_DEL_GROUPMEM.\n"));
+ return False;
+ }
+
+ return True;
+}
+
+/*******************************************************************
+ api_samr_delete_dom_user
+ ********************************************************************/
+
+static BOOL api_samr_delete_dom_user(pipes_struct *p)
+{
+ SAMR_Q_DELETE_DOM_USER q_u;
+ SAMR_R_DELETE_DOM_USER r_u;
+
+ prs_struct *data = &p->in_data.data;
+ prs_struct *rdata = &p->out_data.rdata;
+
+ ZERO_STRUCT(q_u);
+ ZERO_STRUCT(r_u);
+
+ if (!samr_io_q_delete_dom_user("", &q_u, data, 0)) {
+ DEBUG(0,("api_samr_delete_dom_user: unable to unmarshall SAMR_Q_DELETE_DOM_USER.\n"));
+ return False;
+ }
+
+ r_u.status = _samr_delete_dom_user(p, &q_u, &r_u);
+
+ if (!samr_io_r_delete_dom_user("", &r_u, rdata, 0)) {
+ DEBUG(0,("api_samr_delete_dom_user: unable to marshall SAMR_R_DELETE_DOM_USER.\n"));
+ return False;
+ }
+
+ return True;
+}
+
+/*******************************************************************
+ api_samr_delete_dom_group
+ ********************************************************************/
+
+static BOOL api_samr_delete_dom_group(pipes_struct *p)
+{
+ SAMR_Q_DELETE_DOM_GROUP q_u;
+ SAMR_R_DELETE_DOM_GROUP r_u;
+
+ prs_struct *data = &p->in_data.data;
+ prs_struct *rdata = &p->out_data.rdata;
+
+ ZERO_STRUCT(q_u);
+ ZERO_STRUCT(r_u);
+
+ if (!samr_io_q_delete_dom_group("", &q_u, data, 0)) {
+ DEBUG(0,("api_samr_delete_dom_group: unable to unmarshall SAMR_Q_DELETE_DOM_GROUP.\n"));
+ return False;
+ }
+
+ r_u.status = _samr_delete_dom_group(p, &q_u, &r_u);
+
+ if (!samr_io_r_delete_dom_group("", &r_u, rdata, 0)) {
+ DEBUG(0,("api_samr_delete_dom_group: unable to marshall SAMR_R_DELETE_DOM_GROUP.\n"));
+ return False;
+ }
+
+ return True;
+}
+
+/*******************************************************************
+ api_samr_delete_dom_alias
+ ********************************************************************/
+
+static BOOL api_samr_delete_dom_alias(pipes_struct *p)
+{
+ SAMR_Q_DELETE_DOM_ALIAS q_u;
+ SAMR_R_DELETE_DOM_ALIAS r_u;
+
+ prs_struct *data = &p->in_data.data;
+ prs_struct *rdata = &p->out_data.rdata;
+
+ ZERO_STRUCT(q_u);
+ ZERO_STRUCT(r_u);
+
+ if (!samr_io_q_delete_dom_alias("", &q_u, data, 0)) {
+ DEBUG(0,("api_samr_delete_dom_alias: unable to unmarshall SAMR_Q_DELETE_DOM_ALIAS.\n"));
+ return False;
+ }
+
+ r_u.status = _samr_delete_dom_alias(p, &q_u, &r_u);
+
+ if (!samr_io_r_delete_dom_alias("", &r_u, rdata, 0)) {
+ DEBUG(0,("api_samr_delete_dom_alias: unable to marshall SAMR_R_DELETE_DOM_ALIAS.\n"));
+ return False;
+ }
+
+ return True;
+}
+
+/*******************************************************************
+ api_samr_create_dom_group
+ ********************************************************************/
+
+static BOOL api_samr_create_dom_group(pipes_struct *p)
+{
+ SAMR_Q_CREATE_DOM_GROUP q_u;
+ SAMR_R_CREATE_DOM_GROUP r_u;
+
+ prs_struct *data = &p->in_data.data;
+ prs_struct *rdata = &p->out_data.rdata;
+
+ ZERO_STRUCT(q_u);
+ ZERO_STRUCT(r_u);
+
+ if (!samr_io_q_create_dom_group("", &q_u, data, 0)) {
+ DEBUG(0,("api_samr_create_dom_group: unable to unmarshall SAMR_Q_CREATE_DOM_GROUP.\n"));
+ return False;
+ }
+
+ r_u.status = _samr_create_dom_group(p, &q_u, &r_u);
+
+ if (!samr_io_r_create_dom_group("", &r_u, rdata, 0)) {
+ DEBUG(0,("api_samr_create_dom_group: unable to marshall SAMR_R_CREATE_DOM_GROUP.\n"));
+ return False;
+ }
+
+ return True;
+}
+
+/*******************************************************************
+ api_samr_create_dom_alias
+ ********************************************************************/
+
+static BOOL api_samr_create_dom_alias(pipes_struct *p)
+{
+ SAMR_Q_CREATE_DOM_ALIAS q_u;
+ SAMR_R_CREATE_DOM_ALIAS r_u;
+
+ prs_struct *data = &p->in_data.data;
+ prs_struct *rdata = &p->out_data.rdata;
+
+ ZERO_STRUCT(q_u);
+ ZERO_STRUCT(r_u);
+
+ if (!samr_io_q_create_dom_alias("", &q_u, data, 0)) {
+ DEBUG(0,("api_samr_create_dom_alias: unable to unmarshall SAMR_Q_CREATE_DOM_ALIAS.\n"));
+ return False;
+ }
+
+ r_u.status = _samr_create_dom_alias(p, &q_u, &r_u);
+
+ if (!samr_io_r_create_dom_alias("", &r_u, rdata, 0)) {
+ DEBUG(0,("api_samr_create_dom_alias: unable to marshall SAMR_R_CREATE_DOM_ALIAS.\n"));
+ return False;
+ }
+
+ return True;
+}
+
+/*******************************************************************
+ api_samr_query_groupinfo
+ ********************************************************************/
+
+static BOOL api_samr_query_groupinfo(pipes_struct *p)
+{
+ SAMR_Q_QUERY_GROUPINFO q_u;
+ SAMR_R_QUERY_GROUPINFO r_u;
+
+ prs_struct *data = &p->in_data.data;
+ prs_struct *rdata = &p->out_data.rdata;
+
+ ZERO_STRUCT(q_u);
+ ZERO_STRUCT(r_u);
+
+ if (!samr_io_q_query_groupinfo("", &q_u, data, 0)) {
+ DEBUG(0,("api_samr_query_groupinfo: unable to unmarshall SAMR_Q_QUERY_GROUPINFO.\n"));
+ return False;
+ }
+
+ r_u.status = _samr_query_groupinfo(p, &q_u, &r_u);
+
+ if (!samr_io_r_query_groupinfo("", &r_u, rdata, 0)) {
+ DEBUG(0,("api_samr_query_groupinfo: unable to marshall SAMR_R_QUERY_GROUPINFO.\n"));
+ return False;
+ }
+
+ return True;
+}
+
+/*******************************************************************
+ api_samr_set_groupinfo
+ ********************************************************************/
+
+static BOOL api_samr_set_groupinfo(pipes_struct *p)
+{
+ SAMR_Q_SET_GROUPINFO q_u;
+ SAMR_R_SET_GROUPINFO r_u;
+
+ prs_struct *data = &p->in_data.data;
+ prs_struct *rdata = &p->out_data.rdata;
+
+ ZERO_STRUCT(q_u);
+ ZERO_STRUCT(r_u);
+
+ if (!samr_io_q_set_groupinfo("", &q_u, data, 0)) {
+ DEBUG(0,("api_samr_set_groupinfo: unable to unmarshall SAMR_Q_SET_GROUPINFO.\n"));
+ return False;
+ }
+
+ r_u.status = _samr_set_groupinfo(p, &q_u, &r_u);
+
+ if (!samr_io_r_set_groupinfo("", &r_u, rdata, 0)) {
+ DEBUG(0,("api_samr_set_groupinfo: unable to marshall SAMR_R_SET_GROUPINFO.\n"));
+ return False;
+ }
+
+ return True;
+}
+
+/*******************************************************************
+ api_samr_set_aliasinfo
+ ********************************************************************/
+
+static BOOL api_samr_set_aliasinfo(pipes_struct *p)
+{
+ SAMR_Q_SET_ALIASINFO q_u;
+ SAMR_R_SET_ALIASINFO r_u;
+
+ prs_struct *data = &p->in_data.data;
+ prs_struct *rdata = &p->out_data.rdata;
+
+ ZERO_STRUCT(q_u);
+ ZERO_STRUCT(r_u);
+
+ if (!samr_io_q_set_aliasinfo("", &q_u, data, 0)) {
+ DEBUG(0,("api_samr_set_aliasinfo: unable to unmarshall SAMR_Q_SET_ALIASINFO.\n"));
+ return False;
+ }
+
+ r_u.status = _samr_set_aliasinfo(p, &q_u, &r_u);
+
+ if (!samr_io_r_set_aliasinfo("", &r_u, rdata, 0)) {
+ DEBUG(0,("api_samr_set_aliasinfo: unable to marshall SAMR_R_SET_ALIASINFO.\n"));
+ return False;
+ }
+
+ return True;
+}
+
+/*******************************************************************
+ api_samr_get_dom_pwinfo
+ ********************************************************************/
+
+static BOOL api_samr_get_dom_pwinfo(pipes_struct *p)
+{
+ SAMR_Q_GET_DOM_PWINFO q_u;
+ SAMR_R_GET_DOM_PWINFO r_u;
+
+ prs_struct *data = &p->in_data.data;
+ prs_struct *rdata = &p->out_data.rdata;
+
+ ZERO_STRUCT(q_u);
+ ZERO_STRUCT(r_u);
+
+ if (!samr_io_q_get_dom_pwinfo("", &q_u, data, 0)) {
+ DEBUG(0,("api_samr_get_dom_pwinfo: unable to unmarshall SAMR_Q_GET_DOM_PWINFO.\n"));
+ return False;
+ }
+
+ r_u.status = _samr_get_dom_pwinfo(p, &q_u, &r_u);
+
+ if (!samr_io_r_get_dom_pwinfo("", &r_u, rdata, 0)) {
+ DEBUG(0,("api_samr_get_dom_pwinfo: unable to marshall SAMR_R_GET_DOM_PWINFO.\n"));
+ return False;
+ }
+
+ return True;
+}
+
+/*******************************************************************
+ api_samr_open_group
+ ********************************************************************/
+
+static BOOL api_samr_open_group(pipes_struct *p)
+{
+ SAMR_Q_OPEN_GROUP q_u;
+ SAMR_R_OPEN_GROUP r_u;
+
+ prs_struct *data = &p->in_data.data;
+ prs_struct *rdata = &p->out_data.rdata;
+
+ ZERO_STRUCT(q_u);
+ ZERO_STRUCT(r_u);
+
+ if (!samr_io_q_open_group("", &q_u, data, 0)) {
+ DEBUG(0,("api_samr_open_group: unable to unmarshall SAMR_Q_OPEN_GROUP.\n"));
+ return False;
+ }
+
+ r_u.status = _samr_open_group(p, &q_u, &r_u);
+
+ if (!samr_io_r_open_group("", &r_u, rdata, 0)) {
+ DEBUG(0,("api_samr_open_group: unable to marshall SAMR_R_OPEN_GROUP.\n"));
+ return False;
+ }
+
+ return True;
+}
+
+/*******************************************************************
+ api_samr_remove_sid_foreign_domain
+ ********************************************************************/
+
+static BOOL api_samr_remove_sid_foreign_domain(pipes_struct *p)
+{
+ SAMR_Q_REMOVE_SID_FOREIGN_DOMAIN q_u;
+ SAMR_R_REMOVE_SID_FOREIGN_DOMAIN r_u;
+
+ prs_struct *data = &p->in_data.data;
+ prs_struct *rdata = &p->out_data.rdata;
+
+ ZERO_STRUCT(q_u);
+ ZERO_STRUCT(r_u);
+
+ if (!samr_io_q_remove_sid_foreign_domain("", &q_u, data, 0)) {
+ DEBUG(0,("api_samr_remove_sid_foreign_domain: unable to unmarshall SAMR_Q_REMOVE_SID_FOREIGN_DOMAIN.\n"));
+ return False;
+ }
+
+ r_u.status = _samr_remove_sid_foreign_domain(p, &q_u, &r_u);
+
+ if (!samr_io_r_remove_sid_foreign_domain("", &r_u, rdata, 0)) {
+ DEBUG(0,("api_samr_remove_sid_foreign_domain: unable to marshall SAMR_R_REMOVE_SID_FOREIGN_DOMAIN.\n"));
+ return False;
+ }
+
+ return True;
+}
+
+/*******************************************************************
+ api_samr_query_dom_info
+ ********************************************************************/
+
+static BOOL api_samr_unknown_2e(pipes_struct *p)
+{
+ SAMR_Q_UNKNOWN_2E q_u;
+ SAMR_R_UNKNOWN_2E r_u;
+ prs_struct *data = &p->in_data.data;
+ prs_struct *rdata = &p->out_data.rdata;
+
+ ZERO_STRUCT(q_u);
+ ZERO_STRUCT(r_u);
+
+ /* grab the samr unknown 8 command */
+ if(!samr_io_q_unknown_2e("", &q_u, data, 0)) {
+ DEBUG(0,("api_samr_unknown_2e: unable to unmarshall SAMR_Q_UNKNOWN_2E.\n"));
+ return False;
+ }
+
+ r_u.status = _samr_unknown_2e(p, &q_u, &r_u);
+
+ /* store the response in the SMB stream */
+ if(!samr_io_r_samr_unknown_2e("", &r_u, rdata, 0)) {
+ DEBUG(0,("api_samr_unknown_2e: unable to marshall SAMR_R_UNKNOWN_2E.\n"));
+ return False;
+ }
+
+ return True;
+}
+
+/*******************************************************************
+ api_samr_set_dom_info
+ ********************************************************************/
+
+static BOOL api_samr_set_dom_info(pipes_struct *p)
+{
+ SAMR_Q_SET_DOMAIN_INFO q_u;
+ SAMR_R_SET_DOMAIN_INFO r_u;
+ prs_struct *data = &p->in_data.data;
+ prs_struct *rdata = &p->out_data.rdata;
+
+ ZERO_STRUCT(q_u);
+ ZERO_STRUCT(r_u);
+
+ if(!samr_io_q_set_domain_info("", &q_u, data, 0)) {
+ DEBUG(0,("api_samr_set_dom_info: unable to unmarshall SAMR_Q_SET_DOMAIN_INFO.\n"));
+ return False;
+ }
+
+ r_u.status = _samr_set_dom_info(p, &q_u, &r_u);
+
+ /* store the response in the SMB stream */
+ if(!samr_io_r_set_domain_info("", &r_u, rdata, 0)) {
+ DEBUG(0,("api_samr_set_dom_info: unable to marshall SAMR_R_SET_DOMAIN_INFO.\n"));
+ return False;
+ }
+
+ return True;
+}
+
+/*******************************************************************
+ array of \PIPE\samr operations
+ ********************************************************************/
+
+static struct api_struct api_samr_cmds [] =
+{
+ {"SAMR_CLOSE_HND" , SAMR_CLOSE_HND , api_samr_close_hnd },
+ {"SAMR_CONNECT" , SAMR_CONNECT , api_samr_connect },
+ {"SAMR_CONNECT_ANON" , SAMR_CONNECT_ANON , api_samr_connect_anon },
+ {"SAMR_ENUM_DOMAINS" , SAMR_ENUM_DOMAINS , api_samr_enum_domains },
+ {"SAMR_ENUM_DOM_USERS" , SAMR_ENUM_DOM_USERS , api_samr_enum_dom_users },
+
+ {"SAMR_ENUM_DOM_GROUPS" , SAMR_ENUM_DOM_GROUPS , api_samr_enum_dom_groups },
+ {"SAMR_ENUM_DOM_ALIASES" , SAMR_ENUM_DOM_ALIASES , api_samr_enum_dom_aliases },
+ {"SAMR_QUERY_USERALIASES" , SAMR_QUERY_USERALIASES, api_samr_query_useraliases},
+ {"SAMR_QUERY_ALIASMEM" , SAMR_QUERY_ALIASMEM , api_samr_query_aliasmem },
+ {"SAMR_QUERY_GROUPMEM" , SAMR_QUERY_GROUPMEM , api_samr_query_groupmem },
+ {"SAMR_ADD_ALIASMEM" , SAMR_ADD_ALIASMEM , api_samr_add_aliasmem },
+ {"SAMR_DEL_ALIASMEM" , SAMR_DEL_ALIASMEM , api_samr_del_aliasmem },
+ {"SAMR_ADD_GROUPMEM" , SAMR_ADD_GROUPMEM , api_samr_add_groupmem },
+ {"SAMR_DEL_GROUPMEM" , SAMR_DEL_GROUPMEM , api_samr_del_groupmem },
+
+ {"SAMR_DELETE_DOM_USER" , SAMR_DELETE_DOM_USER , api_samr_delete_dom_user },
+ {"SAMR_DELETE_DOM_GROUP" , SAMR_DELETE_DOM_GROUP , api_samr_delete_dom_group },
+ {"SAMR_DELETE_DOM_ALIAS" , SAMR_DELETE_DOM_ALIAS , api_samr_delete_dom_alias },
+ {"SAMR_CREATE_DOM_GROUP" , SAMR_CREATE_DOM_GROUP , api_samr_create_dom_group },
+ {"SAMR_CREATE_DOM_ALIAS" , SAMR_CREATE_DOM_ALIAS , api_samr_create_dom_alias },
+ {"SAMR_LOOKUP_NAMES" , SAMR_LOOKUP_NAMES , api_samr_lookup_names },
+ {"SAMR_OPEN_USER" , SAMR_OPEN_USER , api_samr_open_user },
+ {"SAMR_QUERY_USERINFO" , SAMR_QUERY_USERINFO , api_samr_query_userinfo },
+ {"SAMR_SET_USERINFO" , SAMR_SET_USERINFO , api_samr_set_userinfo },
+ {"SAMR_SET_USERINFO2" , SAMR_SET_USERINFO2 , api_samr_set_userinfo2 },
+
+ {"SAMR_QUERY_DOMAIN_INFO" , SAMR_QUERY_DOMAIN_INFO, api_samr_query_dom_info },
+ {"SAMR_QUERY_USERGROUPS" , SAMR_QUERY_USERGROUPS , api_samr_query_usergroups },
+ {"SAMR_QUERY_DISPINFO" , SAMR_QUERY_DISPINFO , api_samr_query_dispinfo },
+ {"SAMR_QUERY_DISPINFO3" , SAMR_QUERY_DISPINFO3 , api_samr_query_dispinfo },
+ {"SAMR_QUERY_DISPINFO4" , SAMR_QUERY_DISPINFO4 , api_samr_query_dispinfo },
+
+ {"SAMR_QUERY_ALIASINFO" , SAMR_QUERY_ALIASINFO , api_samr_query_aliasinfo },
+ {"SAMR_QUERY_GROUPINFO" , SAMR_QUERY_GROUPINFO , api_samr_query_groupinfo },
+ {"SAMR_SET_GROUPINFO" , SAMR_SET_GROUPINFO , api_samr_set_groupinfo },
+ {"SAMR_SET_ALIASINFO" , SAMR_SET_ALIASINFO , api_samr_set_aliasinfo },
+ {"SAMR_CREATE_USER" , SAMR_CREATE_USER , api_samr_create_user },
+ {"SAMR_LOOKUP_RIDS" , SAMR_LOOKUP_RIDS , api_samr_lookup_rids },
+ {"SAMR_GET_DOM_PWINFO" , SAMR_GET_DOM_PWINFO , api_samr_get_dom_pwinfo },
+ {"SAMR_CHGPASSWD_USER" , SAMR_CHGPASSWD_USER , api_samr_chgpasswd_user },
+ {"SAMR_OPEN_ALIAS" , SAMR_OPEN_ALIAS , api_samr_open_alias },
+ {"SAMR_OPEN_GROUP" , SAMR_OPEN_GROUP , api_samr_open_group },
+ {"SAMR_OPEN_DOMAIN" , SAMR_OPEN_DOMAIN , api_samr_open_domain },
+ {"SAMR_REMOVE_SID_FOREIGN_DOMAIN" , SAMR_REMOVE_SID_FOREIGN_DOMAIN , api_samr_remove_sid_foreign_domain },
+ {"SAMR_LOOKUP_DOMAIN" , SAMR_LOOKUP_DOMAIN , api_samr_lookup_domain },
+
+ {"SAMR_QUERY_SEC_OBJECT" , SAMR_QUERY_SEC_OBJECT , api_samr_query_sec_obj },
+ {"SAMR_SET_SEC_OBJECT" , SAMR_SET_SEC_OBJECT , api_samr_set_sec_obj },
+ {"SAMR_GET_USRDOM_PWINFO" , SAMR_GET_USRDOM_PWINFO, api_samr_get_usrdom_pwinfo},
+ {"SAMR_UNKNOWN_2E" , SAMR_UNKNOWN_2E , api_samr_unknown_2e },
+ {"SAMR_SET_DOMAIN_INFO" , SAMR_SET_DOMAIN_INFO , api_samr_set_dom_info },
+ {"SAMR_CONNECT4" , SAMR_CONNECT4 , api_samr_connect4 }
+};
+
+void samr_get_pipe_fns( struct api_struct **fns, int *n_fns )
+{
+ *fns = api_samr_cmds;
+ *n_fns = sizeof(api_samr_cmds) / sizeof(struct api_struct);
+}
+
+
+NTSTATUS rpc_samr_init(void)
+{
+ return rpc_pipe_register_commands(SMB_RPC_INTERFACE_VERSION, "samr", "lsass", api_samr_cmds,
+ sizeof(api_samr_cmds) / sizeof(struct api_struct));
+}
diff --git a/source/rpc_server/srv_samr_nt.c b/source/rpc_server/srv_samr_nt.c
new file mode 100644
index 00000000000..642c10e26fe
--- /dev/null
+++ b/source/rpc_server/srv_samr_nt.c
@@ -0,0 +1,4476 @@
+/*
+ * Unix SMB/CIFS implementation.
+ * RPC Pipe client / server routines
+ * Copyright (C) Andrew Tridgell 1992-1997,
+ * Copyright (C) Luke Kenneth Casson Leighton 1996-1997,
+ * Copyright (C) Paul Ashton 1997,
+ * Copyright (C) Marc Jacobsen 1999,
+ * Copyright (C) Jeremy Allison 2001-2002,
+ * Copyright (C) Jean Fran§ois Micouleau 1998-2001,
+ * Copyright (C) Jim McDonough <jmcd@us.ibm.com> 2002.
+ * Copyright (C) Gerald (Jerry) Carter 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 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.
+ */
+
+/*
+ * This is the implementation of the SAMR code.
+ */
+
+#include "includes.h"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_RPC_SRV
+
+extern DOM_SID global_sid_Builtin;
+
+extern rid_name domain_group_rids[];
+extern rid_name domain_alias_rids[];
+extern rid_name builtin_alias_rids[];
+
+extern PRIVS privs[];
+
+typedef struct _disp_info {
+ BOOL user_dbloaded;
+ uint32 num_user_account;
+ SAM_ACCOUNT *disp_user_info;
+ BOOL group_dbloaded;
+ uint32 num_group_account;
+ DOMAIN_GRP *disp_group_info;
+} DISP_INFO;
+
+struct samr_info {
+ /* for use by the \PIPE\samr policy */
+ DOM_SID sid;
+ uint32 status; /* some sort of flag. best to record it. comes from opnum 0x39 */
+ uint32 acc_granted;
+ uint16 acb_mask;
+ BOOL all_machines;
+ DISP_INFO disp_info;
+
+ TALLOC_CTX *mem_ctx;
+};
+
+struct generic_mapping sam_generic_mapping = {GENERIC_RIGHTS_SAM_READ, GENERIC_RIGHTS_SAM_WRITE, GENERIC_RIGHTS_SAM_EXECUTE, GENERIC_RIGHTS_SAM_ALL_ACCESS};
+struct generic_mapping dom_generic_mapping = {GENERIC_RIGHTS_DOMAIN_READ, GENERIC_RIGHTS_DOMAIN_WRITE, GENERIC_RIGHTS_DOMAIN_EXECUTE, GENERIC_RIGHTS_DOMAIN_ALL_ACCESS};
+struct generic_mapping usr_generic_mapping = {GENERIC_RIGHTS_USER_READ, GENERIC_RIGHTS_USER_WRITE, GENERIC_RIGHTS_USER_EXECUTE, GENERIC_RIGHTS_USER_ALL_ACCESS};
+struct generic_mapping grp_generic_mapping = {GENERIC_RIGHTS_GROUP_READ, GENERIC_RIGHTS_GROUP_WRITE, GENERIC_RIGHTS_GROUP_EXECUTE, GENERIC_RIGHTS_GROUP_ALL_ACCESS};
+struct generic_mapping ali_generic_mapping = {GENERIC_RIGHTS_ALIAS_READ, GENERIC_RIGHTS_ALIAS_WRITE, GENERIC_RIGHTS_ALIAS_EXECUTE, GENERIC_RIGHTS_ALIAS_ALL_ACCESS};
+
+static NTSTATUS samr_make_dom_obj_sd(TALLOC_CTX *ctx, SEC_DESC **psd, size_t *sd_size);
+
+/*******************************************************************
+ Checks if access to an object should be granted, and returns that
+ level of access for further checks.
+********************************************************************/
+
+NTSTATUS access_check_samr_object(SEC_DESC *psd, pipes_struct *p, uint32 des_access,
+ uint32 *acc_granted, uint32 *priv_list, const char *debug)
+{
+ NTSTATUS status = NT_STATUS_ACCESS_DENIED;
+ NT_USER_TOKEN *nt_user_token = p->pipe_user.nt_user_token;
+ int i;
+
+ if (!se_access_check(psd, nt_user_token, des_access, acc_granted, &status)) {
+ *acc_granted = des_access;
+ if (geteuid() == sec_initial_uid()) {
+ DEBUG(4,("%s: ACCESS should be DENIED (requested: %#010x)\n",
+ debug, des_access));
+ DEBUGADD(4,("but overritten by euid == sec_initial_uid()\n"));
+ return NT_STATUS_OK;
+ }
+ if (priv_list != NULL) {
+ for (i = 0; priv_list[i] != SE_NONE; i++) {
+ if (NT_STATUS_IS_OK(user_has_privilege(&(p->pipe_user), priv_list[i]))) {
+ DEBUG(3, ("%s: User should be denied access but was overridden by %s\n", debug, privs[priv_list[i]].priv));
+ return NT_STATUS_OK;
+ }
+ }
+ }
+
+ DEBUG(2,("%s: ACCESS DENIED (requested: %#010x)\n", debug, des_access));
+ }
+ return status;
+}
+
+/*******************************************************************
+ Checks if access to a function can be granted
+********************************************************************/
+
+NTSTATUS access_check_samr_function(pipes_struct *p, uint32 acc_granted, uint32 acc_required, uint32 *priv_list, const char *debug)
+{
+ int i;
+
+ DEBUG(5,("%s: access check ((granted: %#010x; required: %#010x)\n",
+ debug, acc_granted, acc_required));
+ if ((acc_granted & acc_required) != acc_required) {
+ if (geteuid() == sec_initial_uid()) {
+ DEBUG(4,("%s: ACCESS should be DENIED (granted: %#010x; required: %#010x)\n",
+ debug, acc_granted, acc_required));
+ DEBUGADD(4,("but overwritten by euid == 0\n"));
+ return NT_STATUS_OK;
+ }
+ if (priv_list != NULL) {
+ for (i = 0; priv_list[i] != SE_NONE; i++) {
+ if (NT_STATUS_IS_OK(user_has_privilege(&(p->pipe_user), priv_list[i]))) {
+ DEBUG(3, ("%s: User should be denied access but was overridden by %s\n", debug, privs[priv_list[i]].priv));
+ return NT_STATUS_OK;
+ }
+ }
+ }
+
+ DEBUG(2,("%s: ACCESS DENIED (granted: %#010x; required: %#010x)\n",
+ debug, acc_granted, acc_required));
+ return NT_STATUS_ACCESS_DENIED;
+ }
+ return NT_STATUS_OK;
+}
+
+
+/*******************************************************************
+ Create a samr_info struct.
+********************************************************************/
+
+static struct samr_info *get_samr_info_by_sid(DOM_SID *psid)
+{
+ struct samr_info *info;
+ fstring sid_str;
+ TALLOC_CTX *mem_ctx;
+
+ if (psid) {
+ sid_to_string(sid_str, psid);
+ } else {
+ fstrcpy(sid_str,"(NULL)");
+ }
+
+ mem_ctx = talloc_init("samr_info for domain sid %s", sid_str);
+
+ if ((info = (struct samr_info *)talloc(mem_ctx, sizeof(struct samr_info))) == NULL)
+ return NULL;
+
+ ZERO_STRUCTP(info);
+ DEBUG(10,("get_samr_info_by_sid: created new info for sid %s\n", sid_str));
+ if (psid) {
+ sid_copy( &info->sid, psid);
+ } else {
+ DEBUG(10,("get_samr_info_by_sid: created new info for NULL sid.\n"));
+ }
+ info->mem_ctx = mem_ctx;
+ return info;
+}
+
+/*******************************************************************
+ Function to free the per handle data.
+ ********************************************************************/
+
+static void free_samr_users(struct samr_info *info)
+{
+ int i;
+
+ if (info->disp_info.user_dbloaded){
+ for (i=0; i<info->disp_info.num_user_account; i++) {
+ SAM_ACCOUNT *sam = &info->disp_info.disp_user_info[i];
+ /* Not really a free, actually a 'clear' */
+ pdb_free_sam(&sam);
+ }
+ }
+ info->disp_info.user_dbloaded=False;
+ info->disp_info.num_user_account=0;
+}
+
+/*******************************************************************
+ Function to free the per handle data.
+ ********************************************************************/
+
+static void free_samr_db(struct samr_info *info)
+{
+ /* Groups are talloced */
+
+ free_samr_users(info);
+
+ info->disp_info.group_dbloaded=False;
+ info->disp_info.num_group_account=0;
+}
+
+static void free_samr_info(void *ptr)
+{
+ struct samr_info *info=(struct samr_info *) ptr;
+
+ free_samr_db(info);
+ talloc_destroy(info->mem_ctx);
+}
+
+/*******************************************************************
+ Ensure password info is never given out. Paranioa... JRA.
+ ********************************************************************/
+
+static void samr_clear_sam_passwd(SAM_ACCOUNT *sam_pass)
+{
+
+ if (!sam_pass)
+ return;
+
+ /* These now zero out the old password */
+
+ pdb_set_lanman_passwd(sam_pass, NULL, PDB_DEFAULT);
+ pdb_set_nt_passwd(sam_pass, NULL, PDB_DEFAULT);
+}
+
+
+static NTSTATUS load_sampwd_entries(struct samr_info *info, uint16 acb_mask, BOOL all_machines)
+{
+ SAM_ACCOUNT *pwd = NULL;
+ SAM_ACCOUNT *pwd_array = NULL;
+ NTSTATUS nt_status = NT_STATUS_OK;
+ TALLOC_CTX *mem_ctx = info->mem_ctx;
+
+ DEBUG(10,("load_sampwd_entries\n"));
+
+ /* if the snapshoot is already loaded, return */
+ if ((info->disp_info.user_dbloaded==True)
+ && (info->acb_mask == acb_mask)
+ && (info->all_machines == all_machines)) {
+ DEBUG(10,("load_sampwd_entries: already in memory\n"));
+ return NT_STATUS_OK;
+ }
+
+ free_samr_users(info);
+
+ if (!pdb_setsampwent(False)) {
+ DEBUG(0, ("load_sampwd_entries: Unable to open passdb.\n"));
+ return NT_STATUS_ACCESS_DENIED;
+ }
+
+ for (; (NT_STATUS_IS_OK(nt_status = pdb_init_sam_talloc(mem_ctx, &pwd)))
+ && pdb_getsampwent(pwd) == True; pwd=NULL) {
+
+ if (all_machines) {
+ if (!((pdb_get_acct_ctrl(pwd) & ACB_WSTRUST)
+ || (pdb_get_acct_ctrl(pwd) & ACB_SVRTRUST))) {
+ DEBUG(5,("load_sampwd_entries: '%s' is not a machine account - ACB: %x - skipping\n", pdb_get_username(pwd), acb_mask));
+ pdb_free_sam(&pwd);
+ continue;
+ }
+ } else {
+ if (acb_mask != 0 && !(pdb_get_acct_ctrl(pwd) & acb_mask)) {
+ pdb_free_sam(&pwd);
+ DEBUG(5,(" acb_mask %x reject\n", acb_mask));
+ continue;
+ }
+ }
+
+ /* Realloc some memory for the array of ptr to the SAM_ACCOUNT structs */
+ if (info->disp_info.num_user_account % MAX_SAM_ENTRIES == 0) {
+
+ DEBUG(10,("load_sampwd_entries: allocating more memory\n"));
+ pwd_array=(SAM_ACCOUNT *)talloc_realloc(mem_ctx, info->disp_info.disp_user_info,
+ (info->disp_info.num_user_account+MAX_SAM_ENTRIES)*sizeof(SAM_ACCOUNT));
+
+ if (pwd_array==NULL)
+ return NT_STATUS_NO_MEMORY;
+
+ info->disp_info.disp_user_info=pwd_array;
+ }
+
+ /* Copy the SAM_ACCOUNT into the array */
+ info->disp_info.disp_user_info[info->disp_info.num_user_account]=*pwd;
+
+ DEBUG(10,("load_sampwd_entries: entry: %d\n", info->disp_info.num_user_account));
+
+ info->disp_info.num_user_account++;
+ }
+
+ pdb_endsampwent();
+
+ /* the snapshoot is in memory, we're ready to enumerate fast */
+
+ info->acb_mask = acb_mask;
+ info->all_machines = all_machines;
+ info->disp_info.user_dbloaded=True;
+
+ DEBUG(10,("load_sampwd_entries: done\n"));
+
+ return nt_status;
+}
+
+static NTSTATUS load_group_domain_entries(struct samr_info *info, DOM_SID *sid)
+{
+ GROUP_MAP *map=NULL;
+ DOMAIN_GRP *grp_array = NULL;
+ uint32 group_entries = 0;
+ uint32 i;
+ TALLOC_CTX *mem_ctx = info->mem_ctx;
+ BOOL ret;
+
+ DEBUG(10,("load_group_domain_entries\n"));
+
+ /* if the snapshoot is already loaded, return */
+ if (info->disp_info.group_dbloaded==True) {
+ DEBUG(10,("load_group_domain_entries: already in memory\n"));
+ return NT_STATUS_OK;
+ }
+
+
+ become_root();
+ ret = pdb_enum_group_mapping(SID_NAME_DOM_GRP, &map, (int *)&group_entries, ENUM_ONLY_MAPPED);
+ unbecome_root();
+
+ if ( !ret ) {
+ DEBUG(1, ("load_group_domain_entries: pdb_enum_group_mapping() failed!\n"));
+ return NT_STATUS_NO_MEMORY;
+ }
+
+
+ info->disp_info.num_group_account=group_entries;
+
+ grp_array=(DOMAIN_GRP *)talloc(mem_ctx, info->disp_info.num_group_account*sizeof(DOMAIN_GRP));
+ if (group_entries!=0 && grp_array==NULL) {
+ DEBUG(1, ("load_group_domain_entries: talloc() failed for grp_array!\n"));
+ SAFE_FREE(map);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ info->disp_info.disp_group_info=grp_array;
+
+ for (i=0; i<group_entries; i++) {
+ fstrcpy(grp_array[i].name, map[i].nt_name);
+ fstrcpy(grp_array[i].comment, map[i].comment);
+ sid_split_rid(&map[i].sid, &grp_array[i].rid);
+ grp_array[i].attr=SID_NAME_DOM_GRP;
+ }
+
+ SAFE_FREE(map);
+
+ /* the snapshoot is in memory, we're ready to enumerate fast */
+
+ info->disp_info.group_dbloaded=True;
+
+ DEBUG(10,("load_group_domain_entries: done\n"));
+
+ return NT_STATUS_OK;
+}
+
+
+/*******************************************************************
+ _samr_close_hnd
+ ********************************************************************/
+
+NTSTATUS _samr_close_hnd(pipes_struct *p, SAMR_Q_CLOSE_HND *q_u, SAMR_R_CLOSE_HND *r_u)
+{
+ r_u->status = NT_STATUS_OK;
+
+ /* close the policy handle */
+ if (!close_policy_hnd(p, &q_u->pol))
+ return NT_STATUS_OBJECT_NAME_INVALID;
+
+ DEBUG(5,("samr_reply_close_hnd: %d\n", __LINE__));
+
+ return r_u->status;
+}
+
+/*******************************************************************
+ samr_reply_open_domain
+ ********************************************************************/
+
+NTSTATUS _samr_open_domain(pipes_struct *p, SAMR_Q_OPEN_DOMAIN *q_u, SAMR_R_OPEN_DOMAIN *r_u)
+{
+ struct samr_info *info;
+ SEC_DESC *psd = NULL;
+ uint32 acc_granted;
+ uint32 des_access = q_u->flags;
+ size_t sd_size;
+ NTSTATUS status;
+ uint32 priv_list[3] = {SE_MACHINE_ACCOUNT, SE_NONE};
+
+ r_u->status = NT_STATUS_OK;
+
+ /* find the connection policy handle. */
+ if (!find_policy_by_hnd(p, &q_u->pol, (void**)&info))
+ return NT_STATUS_INVALID_HANDLE;
+
+ if (!NT_STATUS_IS_OK(status = access_check_samr_function(p, info->acc_granted, SA_RIGHT_SAM_OPEN_DOMAIN, priv_list, "_samr_open_domain"))) {
+ return status;
+ }
+
+ /*check if access can be granted as requested by client. */
+ samr_make_dom_obj_sd(p->mem_ctx, &psd, &sd_size);
+ se_map_generic(&des_access,&dom_generic_mapping);
+
+ if (!NT_STATUS_IS_OK(status =
+ access_check_samr_object(psd, p, des_access, &acc_granted,
+ priv_list, "_samr_open_domain"))) {
+ return status;
+ }
+
+ /* associate the domain SID with the (unique) handle. */
+ if ((info = get_samr_info_by_sid(&q_u->dom_sid.sid))==NULL)
+ return NT_STATUS_NO_MEMORY;
+ info->acc_granted = acc_granted;
+
+ /* get a (unique) handle. open a policy on it. */
+ if (!create_policy_hnd(p, &r_u->domain_pol, free_samr_info, (void *)info))
+ return NT_STATUS_OBJECT_NAME_NOT_FOUND;
+
+ DEBUG(5,("samr_open_domain: %d\n", __LINE__));
+
+ return r_u->status;
+}
+
+/*******************************************************************
+ _samr_get_usrdom_pwinfo
+ ********************************************************************/
+
+NTSTATUS _samr_get_usrdom_pwinfo(pipes_struct *p, SAMR_Q_GET_USRDOM_PWINFO *q_u, SAMR_R_GET_USRDOM_PWINFO *r_u)
+{
+ struct samr_info *info = NULL;
+
+ r_u->status = NT_STATUS_OK;
+
+ /* find the policy handle. open a policy on it. */
+ if (!find_policy_by_hnd(p, &q_u->user_pol, (void **)&info))
+ return NT_STATUS_INVALID_HANDLE;
+
+ if (!sid_check_is_in_our_domain(&info->sid))
+ return NT_STATUS_OBJECT_TYPE_MISMATCH;
+
+ init_samr_r_get_usrdom_pwinfo(r_u, NT_STATUS_OK);
+
+ DEBUG(5,("_samr_get_usrdom_pwinfo: %d\n", __LINE__));
+
+ /*
+ * NT sometimes return NT_STATUS_ACCESS_DENIED
+ * I don't know yet why.
+ */
+
+ return r_u->status;
+}
+
+/*******************************************************************
+ samr_make_dom_obj_sd
+ ********************************************************************/
+
+static NTSTATUS samr_make_dom_obj_sd(TALLOC_CTX *ctx, SEC_DESC **psd, size_t *sd_size)
+{
+ extern DOM_SID global_sid_World;
+ DOM_SID adm_sid;
+ DOM_SID act_sid;
+
+ SEC_ACE ace[3];
+ SEC_ACCESS mask;
+
+ SEC_ACL *psa = NULL;
+
+ sid_copy(&adm_sid, &global_sid_Builtin);
+ sid_append_rid(&adm_sid, BUILTIN_ALIAS_RID_ADMINS);
+
+ sid_copy(&act_sid, &global_sid_Builtin);
+ sid_append_rid(&act_sid, BUILTIN_ALIAS_RID_ACCOUNT_OPS);
+
+ /*basic access for every one*/
+ init_sec_access(&mask, GENERIC_RIGHTS_DOMAIN_EXECUTE | GENERIC_RIGHTS_DOMAIN_READ);
+ init_sec_ace(&ace[0], &global_sid_World, SEC_ACE_TYPE_ACCESS_ALLOWED, mask, 0);
+
+ /*full access for builtin aliases Administrators and Account Operators*/
+ init_sec_access(&mask, GENERIC_RIGHTS_DOMAIN_ALL_ACCESS);
+ init_sec_ace(&ace[1], &adm_sid, SEC_ACE_TYPE_ACCESS_ALLOWED, mask, 0);
+ init_sec_ace(&ace[2], &act_sid, SEC_ACE_TYPE_ACCESS_ALLOWED, mask, 0);
+
+ if ((psa = make_sec_acl(ctx, NT4_ACL_REVISION, 3, ace)) == NULL)
+ return NT_STATUS_NO_MEMORY;
+
+ if ((*psd = make_sec_desc(ctx, SEC_DESC_REVISION, SEC_DESC_SELF_RELATIVE, NULL, NULL, NULL, psa, sd_size)) == NULL)
+ return NT_STATUS_NO_MEMORY;
+
+ return NT_STATUS_OK;
+}
+
+/*******************************************************************
+ samr_make_usr_obj_sd
+ ********************************************************************/
+
+static NTSTATUS samr_make_usr_obj_sd(TALLOC_CTX *ctx, SEC_DESC **psd, size_t *sd_size, DOM_SID *usr_sid)
+{
+ extern DOM_SID global_sid_World;
+ DOM_SID adm_sid;
+ DOM_SID act_sid;
+
+ SEC_ACE ace[4];
+ SEC_ACCESS mask;
+
+ SEC_ACL *psa = NULL;
+
+ sid_copy(&adm_sid, &global_sid_Builtin);
+ sid_append_rid(&adm_sid, BUILTIN_ALIAS_RID_ADMINS);
+
+ sid_copy(&act_sid, &global_sid_Builtin);
+ sid_append_rid(&act_sid, BUILTIN_ALIAS_RID_ACCOUNT_OPS);
+
+ /*basic access for every one*/
+ init_sec_access(&mask, GENERIC_RIGHTS_USER_EXECUTE | GENERIC_RIGHTS_USER_READ);
+ init_sec_ace(&ace[0], &global_sid_World, SEC_ACE_TYPE_ACCESS_ALLOWED, mask, 0);
+
+ /*full access for builtin aliases Administrators and Account Operators*/
+ init_sec_access(&mask, GENERIC_RIGHTS_USER_ALL_ACCESS);
+ init_sec_ace(&ace[1], &adm_sid, SEC_ACE_TYPE_ACCESS_ALLOWED, mask, 0);
+ init_sec_ace(&ace[2], &act_sid, SEC_ACE_TYPE_ACCESS_ALLOWED, mask, 0);
+
+ /*extended access for the user*/
+ init_sec_access(&mask,READ_CONTROL_ACCESS | SA_RIGHT_USER_CHANGE_PASSWORD | SA_RIGHT_USER_SET_LOC_COM);
+ init_sec_ace(&ace[3], usr_sid, SEC_ACE_TYPE_ACCESS_ALLOWED, mask, 0);
+
+ if ((psa = make_sec_acl(ctx, NT4_ACL_REVISION, 4, ace)) == NULL)
+ return NT_STATUS_NO_MEMORY;
+
+ if ((*psd = make_sec_desc(ctx, SEC_DESC_REVISION, SEC_DESC_SELF_RELATIVE, NULL, NULL, NULL, psa, sd_size)) == NULL)
+ return NT_STATUS_NO_MEMORY;
+
+ return NT_STATUS_OK;
+}
+
+/*******************************************************************
+ samr_make_grp_obj_sd
+ ********************************************************************/
+
+static NTSTATUS samr_make_grp_obj_sd(TALLOC_CTX *ctx, SEC_DESC **psd, size_t *sd_size)
+{
+ extern DOM_SID global_sid_World;
+ DOM_SID adm_sid;
+ DOM_SID act_sid;
+
+ SEC_ACE ace[3];
+ SEC_ACCESS mask;
+
+ SEC_ACL *psa = NULL;
+
+ sid_copy(&adm_sid, &global_sid_Builtin);
+ sid_append_rid(&adm_sid, BUILTIN_ALIAS_RID_ADMINS);
+
+ sid_copy(&act_sid, &global_sid_Builtin);
+ sid_append_rid(&act_sid, BUILTIN_ALIAS_RID_ACCOUNT_OPS);
+
+ /*basic access for every one*/
+ init_sec_access(&mask, GENERIC_RIGHTS_GROUP_EXECUTE | GENERIC_RIGHTS_GROUP_READ);
+ init_sec_ace(&ace[0], &global_sid_World, SEC_ACE_TYPE_ACCESS_ALLOWED, mask, 0);
+
+ /*full access for builtin aliases Administrators and Account Operators*/
+ init_sec_access(&mask, GENERIC_RIGHTS_GROUP_ALL_ACCESS);
+ init_sec_ace(&ace[1], &adm_sid, SEC_ACE_TYPE_ACCESS_ALLOWED, mask, 0);
+ init_sec_ace(&ace[2], &act_sid, SEC_ACE_TYPE_ACCESS_ALLOWED, mask, 0);
+
+ if ((psa = make_sec_acl(ctx, NT4_ACL_REVISION, 3, ace)) == NULL)
+ return NT_STATUS_NO_MEMORY;
+
+ if ((*psd = make_sec_desc(ctx, SEC_DESC_REVISION, SEC_DESC_SELF_RELATIVE, NULL, NULL, NULL, psa, sd_size)) == NULL)
+ return NT_STATUS_NO_MEMORY;
+
+ return NT_STATUS_OK;
+}
+
+/*******************************************************************
+ samr_make_ali_obj_sd
+ ********************************************************************/
+
+static NTSTATUS samr_make_ali_obj_sd(TALLOC_CTX *ctx, SEC_DESC **psd, size_t *sd_size)
+{
+ extern DOM_SID global_sid_World;
+ DOM_SID adm_sid;
+ DOM_SID act_sid;
+
+ SEC_ACE ace[3];
+ SEC_ACCESS mask;
+
+ SEC_ACL *psa = NULL;
+
+ sid_copy(&adm_sid, &global_sid_Builtin);
+ sid_append_rid(&adm_sid, BUILTIN_ALIAS_RID_ADMINS);
+
+ sid_copy(&act_sid, &global_sid_Builtin);
+ sid_append_rid(&act_sid, BUILTIN_ALIAS_RID_ACCOUNT_OPS);
+
+ /*basic access for every one*/
+ init_sec_access(&mask, GENERIC_RIGHTS_ALIAS_EXECUTE | GENERIC_RIGHTS_ALIAS_READ);
+ init_sec_ace(&ace[0], &global_sid_World, SEC_ACE_TYPE_ACCESS_ALLOWED, mask, 0);
+
+ /*full access for builtin aliases Administrators and Account Operators*/
+ init_sec_access(&mask, GENERIC_RIGHTS_ALIAS_ALL_ACCESS);
+ init_sec_ace(&ace[1], &adm_sid, SEC_ACE_TYPE_ACCESS_ALLOWED, mask, 0);
+ init_sec_ace(&ace[2], &act_sid, SEC_ACE_TYPE_ACCESS_ALLOWED, mask, 0);
+
+ if ((psa = make_sec_acl(ctx, NT4_ACL_REVISION, 3, ace)) == NULL)
+ return NT_STATUS_NO_MEMORY;
+
+ if ((*psd = make_sec_desc(ctx, SEC_DESC_REVISION, SEC_DESC_SELF_RELATIVE, NULL, NULL, NULL, psa, sd_size)) == NULL)
+ return NT_STATUS_NO_MEMORY;
+
+ return NT_STATUS_OK;
+}
+
+static BOOL get_lsa_policy_samr_sid(pipes_struct *p, POLICY_HND *pol, DOM_SID *sid, uint32 *acc_granted)
+{
+ struct samr_info *info = NULL;
+
+ /* find the policy handle. open a policy on it. */
+ if (!find_policy_by_hnd(p, pol, (void **)&info))
+ return False;
+
+ if (!info)
+ return False;
+
+ *sid = info->sid;
+ *acc_granted = info->acc_granted;
+ return True;
+}
+
+/*******************************************************************
+ _samr_set_sec_obj
+ ********************************************************************/
+
+NTSTATUS _samr_set_sec_obj(pipes_struct *p, SAMR_Q_SET_SEC_OBJ *q_u, SAMR_R_SET_SEC_OBJ *r_u)
+{
+ DEBUG(0,("_samr_set_sec_obj: Not yet implemented!\n"));
+ return NT_STATUS_NOT_IMPLEMENTED;
+}
+
+
+/*******************************************************************
+ _samr_query_sec_obj
+ ********************************************************************/
+
+NTSTATUS _samr_query_sec_obj(pipes_struct *p, SAMR_Q_QUERY_SEC_OBJ *q_u, SAMR_R_QUERY_SEC_OBJ *r_u)
+{
+ DOM_SID pol_sid;
+ fstring str_sid;
+ SEC_DESC * psd = NULL;
+ size_t sd_size;
+ uint32 acc_granted;
+
+ r_u->status = NT_STATUS_OK;
+
+ /* Get the SID. */
+ if (!get_lsa_policy_samr_sid(p, &q_u->user_pol, &pol_sid, &acc_granted))
+ return NT_STATUS_INVALID_HANDLE;
+
+
+
+ DEBUG(10,("_samr_query_sec_obj: querying security on SID: %s\n", sid_to_string(str_sid, &pol_sid)));
+
+ /* Check what typ of SID is beeing queried (e.g Domain SID, User SID, Group SID) */
+
+ /* To query the security of the SAM it self an invalid SID with S-0-0 is passed to this function */
+ if (pol_sid.sid_rev_num == 0)
+ {
+ DEBUG(5,("_samr_query_sec_obj: querying security on SAM\n"));
+ r_u->status = samr_make_sam_obj_sd(p->mem_ctx, &psd, &sd_size);
+ }
+ else if (sid_equal(&pol_sid,get_global_sam_sid())) /* check if it is our domain SID */
+
+ {
+ DEBUG(5,("_samr_query_sec_obj: querying security on Domain with SID: %s\n", sid_to_string(str_sid, &pol_sid)));
+ r_u->status = samr_make_dom_obj_sd(p->mem_ctx, &psd, &sd_size);
+ }
+ else if (sid_equal(&pol_sid,&global_sid_Builtin)) /* check if it is the Builtin Domain */
+ {
+ /* TODO: Builtin probably needs a different SD with restricted write access*/
+ DEBUG(5,("_samr_query_sec_obj: querying security on Builtin Domain with SID: %s\n", sid_to_string(str_sid, &pol_sid)));
+ r_u->status = samr_make_dom_obj_sd(p->mem_ctx, &psd, &sd_size);
+ }
+ else if (sid_check_is_in_our_domain(&pol_sid) ||
+ sid_check_is_in_builtin(&pol_sid))
+ {
+ /* TODO: different SDs have to be generated for aliases groups and users.
+ Currently all three get a default user SD */
+ DEBUG(10,("_samr_query_sec_obj: querying security on Object with SID: %s\n", sid_to_string(str_sid, &pol_sid)));
+ r_u->status = samr_make_usr_obj_sd(p->mem_ctx, &psd,&sd_size, &pol_sid);
+ }
+ else return NT_STATUS_OBJECT_TYPE_MISMATCH;
+
+ if ((r_u->buf = make_sec_desc_buf(p->mem_ctx, sd_size, psd)) == NULL)
+ return NT_STATUS_NO_MEMORY;
+
+ if (NT_STATUS_IS_OK(r_u->status))
+ r_u->ptr = 1;
+
+ return r_u->status;
+}
+
+/*******************************************************************
+makes a SAM_ENTRY / UNISTR2* structure from a user list.
+********************************************************************/
+
+static NTSTATUS make_user_sam_entry_list(TALLOC_CTX *ctx, SAM_ENTRY **sam_pp, UNISTR2 **uni_name_pp,
+ uint32 num_entries, uint32 start_idx, SAM_ACCOUNT *disp_user_info,
+ DOM_SID *domain_sid)
+{
+ uint32 i;
+ SAM_ENTRY *sam;
+ UNISTR2 *uni_name;
+ SAM_ACCOUNT *pwd = NULL;
+ UNISTR2 uni_temp_name;
+ const char *temp_name;
+ const DOM_SID *user_sid;
+ uint32 user_rid;
+ fstring user_sid_string;
+ fstring domain_sid_string;
+
+ *sam_pp = NULL;
+ *uni_name_pp = NULL;
+
+ if (num_entries == 0)
+ return NT_STATUS_OK;
+
+ sam = (SAM_ENTRY *)talloc_zero(ctx, sizeof(SAM_ENTRY)*num_entries);
+
+ uni_name = (UNISTR2 *)talloc_zero(ctx, sizeof(UNISTR2)*num_entries);
+
+ if (sam == NULL || uni_name == NULL) {
+ DEBUG(0, ("make_user_sam_entry_list: talloc_zero failed!\n"));
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ for (i = 0; i < num_entries; i++) {
+ pwd = &disp_user_info[i+start_idx];
+ temp_name = pdb_get_username(pwd);
+ init_unistr2(&uni_temp_name, temp_name, UNI_STR_TERMINATE);
+ user_sid = pdb_get_user_sid(pwd);
+
+ if (!sid_peek_check_rid(domain_sid, user_sid, &user_rid)) {
+ DEBUG(0, ("make_user_sam_entry_list: User %s has SID %s, which conflicts with "
+ "the domain sid %s. Failing operation.\n",
+ temp_name,
+ sid_to_string(user_sid_string, user_sid),
+ sid_to_string(domain_sid_string, domain_sid)));
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ init_sam_entry(&sam[i], &uni_temp_name, user_rid);
+ copy_unistr2(&uni_name[i], &uni_temp_name);
+ }
+
+ *sam_pp = sam;
+ *uni_name_pp = uni_name;
+ return NT_STATUS_OK;
+}
+
+/*******************************************************************
+ samr_reply_enum_dom_users
+ ********************************************************************/
+
+NTSTATUS _samr_enum_dom_users(pipes_struct *p, SAMR_Q_ENUM_DOM_USERS *q_u,
+ SAMR_R_ENUM_DOM_USERS *r_u)
+{
+ struct samr_info *info = NULL;
+ uint32 struct_size=0x20; /* W2K always reply that, client doesn't care */
+ int num_account;
+ uint32 enum_context=q_u->start_idx;
+ uint32 max_size=q_u->max_size;
+ uint32 temp_size;
+ enum remote_arch_types ra_type = get_remote_arch();
+ int max_sam_entries = (ra_type == RA_WIN95) ? MAX_SAM_ENTRIES_W95 : MAX_SAM_ENTRIES_W2K;
+ uint32 max_entries = max_sam_entries;
+ DOM_SID domain_sid;
+
+ r_u->status = NT_STATUS_OK;
+
+ /* find the policy handle. open a policy on it. */
+ if (!find_policy_by_hnd(p, &q_u->pol, (void **)&info))
+ return NT_STATUS_INVALID_HANDLE;
+
+ domain_sid = info->sid;
+
+ if (!NT_STATUS_IS_OK(r_u->status = access_check_samr_function(p, info->acc_granted,
+ SA_RIGHT_DOMAIN_ENUM_ACCOUNTS, NULL,
+ "_samr_enum_dom_users"))) {
+ return r_u->status;
+ }
+
+ DEBUG(5,("_samr_enum_dom_users: %d\n", __LINE__));
+
+ become_root();
+ r_u->status=load_sampwd_entries(info, q_u->acb_mask, False);
+ unbecome_root();
+
+ if (!NT_STATUS_IS_OK(r_u->status))
+ return r_u->status;
+
+ num_account = info->disp_info.num_user_account;
+
+ if (enum_context > num_account) {
+ DEBUG(5, ("_samr_enum_dom_users: enumeration handle over total entries\n"));
+ return NT_STATUS_OK;
+ }
+
+ /* verify we won't overflow */
+ if (max_entries > num_account-enum_context) {
+ max_entries = num_account-enum_context;
+ DEBUG(5, ("_samr_enum_dom_users: only %d entries to return\n", max_entries));
+ }
+
+ /* calculate the size and limit on the number of entries we will return */
+ temp_size=max_entries*struct_size;
+
+ if (temp_size>max_size) {
+ max_entries=MIN((max_size/struct_size),max_entries);;
+ DEBUG(5, ("_samr_enum_dom_users: buffer size limits to only %d entries\n", max_entries));
+ }
+
+ /*
+ * Note from JRA. total_entries is not being used here. Currently if there is a
+ * large user base then it looks like NT will enumerate until get_sampwd_entries
+ * returns False due to num_entries being zero. This will cause an access denied
+ * return. I don't think this is right and needs further investigation. Note that
+ * this is also the same in the TNG code (I don't think that has been tested with
+ * a very large user list as MAX_SAM_ENTRIES is set to 600).
+ *
+ * I also think that one of the 'num_entries' return parameters is probably
+ * the "max entries" parameter - but in the TNG code they're all currently set to the same
+ * value (again I think this is wrong).
+ */
+
+ r_u->status = make_user_sam_entry_list(p->mem_ctx, &r_u->sam, &r_u->uni_acct_name,
+ max_entries, enum_context,
+ info->disp_info.disp_user_info,
+ &domain_sid);
+
+ if (!NT_STATUS_IS_OK(r_u->status))
+ return r_u->status;
+
+ if (enum_context+max_entries < num_account)
+ r_u->status = STATUS_MORE_ENTRIES;
+
+ DEBUG(5, ("_samr_enum_dom_users: %d\n", __LINE__));
+
+ init_samr_r_enum_dom_users(r_u, q_u->start_idx + max_entries, max_entries);
+
+ DEBUG(5,("_samr_enum_dom_users: %d\n", __LINE__));
+
+ return r_u->status;
+}
+
+/*******************************************************************
+makes a SAM_ENTRY / UNISTR2* structure from a group list.
+********************************************************************/
+
+static void make_group_sam_entry_list(TALLOC_CTX *ctx, SAM_ENTRY **sam_pp, UNISTR2 **uni_name_pp,
+ uint32 num_sam_entries, DOMAIN_GRP *grp)
+{
+ uint32 i;
+ SAM_ENTRY *sam;
+ UNISTR2 *uni_name;
+
+ *sam_pp = NULL;
+ *uni_name_pp = NULL;
+
+ if (num_sam_entries == 0)
+ return;
+
+ sam = (SAM_ENTRY *)talloc_zero(ctx, sizeof(SAM_ENTRY)*num_sam_entries);
+
+ uni_name = (UNISTR2 *)talloc_zero(ctx, sizeof(UNISTR2)*num_sam_entries);
+
+ if (sam == NULL || uni_name == NULL) {
+ DEBUG(0, ("NULL pointers in SAMR_R_QUERY_DISPINFO\n"));
+ return;
+ }
+
+ for (i = 0; i < num_sam_entries; i++) {
+ /*
+ * JRA. I think this should include the null. TNG does not.
+ */
+ init_unistr2(&uni_name[i], grp[i].name, UNI_STR_TERMINATE);
+ init_sam_entry(&sam[i], &uni_name[i], grp[i].rid);
+ }
+
+ *sam_pp = sam;
+ *uni_name_pp = uni_name;
+}
+
+/*******************************************************************
+ Get the group entries - similar to get_sampwd_entries().
+ ******************************************************************/
+
+static NTSTATUS get_group_domain_entries( TALLOC_CTX *ctx,
+ DOMAIN_GRP **d_grp, DOM_SID *sid, uint32 start_idx,
+ uint32 *p_num_entries, uint32 max_entries )
+{
+ GROUP_MAP *map=NULL;
+ int i;
+ uint32 group_entries = 0;
+ uint32 num_entries = 0;
+
+ *p_num_entries = 0;
+
+ /* access checks for the users were performed higher up. become/unbecome_root()
+ needed for some passdb backends to enumerate groups */
+
+ become_root();
+ pdb_enum_group_mapping(SID_NAME_DOM_GRP, &map, (int *)&group_entries,
+ ENUM_ONLY_MAPPED);
+ unbecome_root();
+
+ num_entries=group_entries-start_idx;
+
+ /* limit the number of entries */
+ if (num_entries>max_entries) {
+ DEBUG(5,("Limiting to %d entries\n", max_entries));
+ num_entries=max_entries;
+ }
+
+ *d_grp=(DOMAIN_GRP *)talloc_zero(ctx, num_entries*sizeof(DOMAIN_GRP));
+ if (num_entries!=0 && *d_grp==NULL){
+ SAFE_FREE(map);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ for (i=0; i<num_entries; i++) {
+ fstrcpy((*d_grp)[i].name, map[i+start_idx].nt_name);
+ fstrcpy((*d_grp)[i].comment, map[i+start_idx].comment);
+ sid_split_rid(&map[i+start_idx].sid, &(*d_grp)[i].rid);
+ (*d_grp)[i].attr=SID_NAME_DOM_GRP;
+ }
+
+ SAFE_FREE(map);
+
+ *p_num_entries = num_entries;
+
+ DEBUG(10,("get_group_domain_entries: returning %d entries\n",
+ *p_num_entries));
+
+ return NT_STATUS_OK;
+}
+
+/*******************************************************************
+ Wrapper for enumerating local groups
+ ******************************************************************/
+
+static NTSTATUS get_alias_entries( TALLOC_CTX *ctx, DOMAIN_GRP **d_grp,
+ const DOM_SID *sid, uint32 start_idx,
+ uint32 *p_num_entries, uint32 max_entries )
+{
+ struct acct_info *info;
+ int i;
+ BOOL res;
+
+ become_root();
+ res = pdb_enum_aliases(sid, start_idx, max_entries,
+ p_num_entries, &info);
+ unbecome_root();
+
+ if (!res)
+ return NT_STATUS_ACCESS_DENIED;
+
+ if (*p_num_entries == 0)
+ return NT_STATUS_OK;
+
+ *d_grp = talloc(ctx, sizeof(DOMAIN_GRP) * (*p_num_entries));
+
+ if (*d_grp == NULL) {
+ SAFE_FREE(info);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ for (i=0; i<*p_num_entries; i++) {
+ fstrcpy((*d_grp)[i].name, info[i].acct_name);
+ fstrcpy((*d_grp)[i].comment, info[i].acct_desc);
+ (*d_grp)[i].rid = info[i].rid;
+ (*d_grp)[i].attr = SID_NAME_ALIAS;
+ }
+
+ SAFE_FREE(info);
+ return NT_STATUS_OK;
+}
+
+/*******************************************************************
+ samr_reply_enum_dom_groups
+ ********************************************************************/
+
+NTSTATUS _samr_enum_dom_groups(pipes_struct *p, SAMR_Q_ENUM_DOM_GROUPS *q_u, SAMR_R_ENUM_DOM_GROUPS *r_u)
+{
+ DOMAIN_GRP *grp=NULL;
+ uint32 num_entries;
+ DOM_SID sid;
+ uint32 acc_granted;
+
+ r_u->status = NT_STATUS_OK;
+
+ if (!get_lsa_policy_samr_sid(p, &q_u->pol, &sid, &acc_granted))
+ return NT_STATUS_INVALID_HANDLE;
+
+ if (!NT_STATUS_IS_OK(r_u->status = access_check_samr_function(p, acc_granted, SA_RIGHT_DOMAIN_ENUM_ACCOUNTS, NULL, "_samr_enum_dom_groups"))) {
+ return r_u->status;
+ }
+
+ DEBUG(5,("samr_reply_enum_dom_groups: %d\n", __LINE__));
+
+ /* the domain group array is being allocated in the function below */
+ if (!NT_STATUS_IS_OK(r_u->status = get_group_domain_entries(p->mem_ctx, &grp, &sid, q_u->start_idx, &num_entries, MAX_SAM_ENTRIES))) {
+ return r_u->status;
+ }
+
+ make_group_sam_entry_list(p->mem_ctx, &r_u->sam, &r_u->uni_grp_name, num_entries, grp);
+
+ init_samr_r_enum_dom_groups(r_u, q_u->start_idx, num_entries);
+
+ DEBUG(5,("samr_enum_dom_groups: %d\n", __LINE__));
+
+ return r_u->status;
+}
+
+
+/*******************************************************************
+ samr_reply_enum_dom_aliases
+ ********************************************************************/
+
+NTSTATUS _samr_enum_dom_aliases(pipes_struct *p, SAMR_Q_ENUM_DOM_ALIASES *q_u, SAMR_R_ENUM_DOM_ALIASES *r_u)
+{
+ DOMAIN_GRP *grp=NULL;
+ uint32 num_entries = 0;
+ fstring sid_str;
+ DOM_SID sid;
+ NTSTATUS status;
+ uint32 acc_granted;
+
+ r_u->status = NT_STATUS_OK;
+
+ if (!get_lsa_policy_samr_sid(p, &q_u->pol, &sid, &acc_granted))
+ return NT_STATUS_INVALID_HANDLE;
+
+ if (!NT_STATUS_IS_OK(r_u->status = access_check_samr_function(p, acc_granted, SA_RIGHT_DOMAIN_ENUM_ACCOUNTS, NULL, "_samr_enum_dom_aliases"))) {
+ return r_u->status;
+ }
+
+ sid_to_string(sid_str, &sid);
+ DEBUG(5,("samr_reply_enum_dom_aliases: sid %s\n", sid_str));
+
+ status = get_alias_entries(p->mem_ctx, &grp, &sid, q_u->start_idx,
+ &num_entries, MAX_SAM_ENTRIES);
+ if (!NT_STATUS_IS_OK(status)) return status;
+
+ make_group_sam_entry_list(p->mem_ctx, &r_u->sam, &r_u->uni_grp_name, num_entries, grp);
+
+ /*safe_free(grp);*/
+
+ init_samr_r_enum_dom_aliases(r_u, q_u->start_idx + num_entries, num_entries);
+
+ DEBUG(5,("samr_enum_dom_aliases: %d\n", __LINE__));
+
+ return r_u->status;
+}
+
+/*******************************************************************
+ samr_reply_query_dispinfo
+ ********************************************************************/
+
+NTSTATUS _samr_query_dispinfo(pipes_struct *p, SAMR_Q_QUERY_DISPINFO *q_u,
+ SAMR_R_QUERY_DISPINFO *r_u)
+{
+ struct samr_info *info = NULL;
+ uint32 struct_size=0x20; /* W2K always reply that, client doesn't care */
+
+ uint32 max_entries=q_u->max_entries;
+ uint32 enum_context=q_u->start_idx;
+ uint32 max_size=q_u->max_size;
+
+ SAM_DISPINFO_CTR *ctr;
+ uint32 temp_size=0, total_data_size=0;
+ NTSTATUS disp_ret;
+ uint32 num_account = 0;
+ enum remote_arch_types ra_type = get_remote_arch();
+ int max_sam_entries = (ra_type == RA_WIN95) ? MAX_SAM_ENTRIES_W95 : MAX_SAM_ENTRIES_W2K;
+ DOM_SID domain_sid;
+
+ DEBUG(5, ("samr_reply_query_dispinfo: %d\n", __LINE__));
+ r_u->status = NT_STATUS_OK;
+
+ /* find the policy handle. open a policy on it. */
+ if (!find_policy_by_hnd(p, &q_u->domain_pol, (void **)&info))
+ return NT_STATUS_INVALID_HANDLE;
+
+ domain_sid = info->sid;
+
+ /*
+ * calculate how many entries we will return.
+ * based on
+ * - the number of entries the client asked
+ * - our limit on that
+ * - the starting point (enumeration context)
+ * - the buffer size the client will accept
+ */
+
+ /*
+ * We are a lot more like W2K. Instead of reading the SAM
+ * each time to find the records we need to send back,
+ * we read it once and link that copy to the sam handle.
+ * For large user list (over the MAX_SAM_ENTRIES)
+ * it's a definitive win.
+ * second point to notice: between enumerations
+ * our sam is now the same as it's a snapshoot.
+ * third point: got rid of the static SAM_USER_21 struct
+ * no more intermediate.
+ * con: it uses much more memory, as a full copy is stored
+ * in memory.
+ *
+ * If you want to change it, think twice and think
+ * of the second point , that's really important.
+ *
+ * JFM, 12/20/2001
+ */
+
+ /* Get what we need from the password database */
+ switch (q_u->switch_level) {
+ case 0x1:
+ /* When playing with usrmgr, this is necessary
+ if you want immediate refresh after editing
+ a user. I would like to do this after the
+ setuserinfo2, but we do not have access to
+ the domain handle in that call, only to the
+ user handle. Where else does this hurt?
+ -- Volker
+ */
+#if 0
+ /* We cannot do this here - it kills performace. JRA. */
+ free_samr_users(info);
+#endif
+ case 0x2:
+ case 0x4:
+ become_root();
+ /* Level 2 is for all machines, otherwise only 'normal' users */
+ r_u->status=load_sampwd_entries(info, ACB_NORMAL, q_u->switch_level==2);
+ unbecome_root();
+ if (!NT_STATUS_IS_OK(r_u->status)) {
+ DEBUG(5, ("_samr_query_dispinfo: load_sampwd_entries failed\n"));
+ return r_u->status;
+ }
+ num_account = info->disp_info.num_user_account;
+ break;
+ case 0x3:
+ case 0x5:
+ r_u->status = load_group_domain_entries(info, &info->sid);
+ if (!NT_STATUS_IS_OK(r_u->status))
+ return r_u->status;
+ num_account = info->disp_info.num_group_account;
+ break;
+ default:
+ DEBUG(0,("_samr_query_dispinfo: Unknown info level (%u)\n", (unsigned int)q_u->switch_level ));
+ return NT_STATUS_INVALID_INFO_CLASS;
+ }
+
+ /* first limit the number of entries we will return */
+ if(max_entries > max_sam_entries) {
+ DEBUG(5, ("samr_reply_query_dispinfo: client requested %d entries, limiting to %d\n", max_entries, max_sam_entries));
+ max_entries = max_sam_entries;
+ }
+
+ if (enum_context > num_account) {
+ DEBUG(5, ("samr_reply_query_dispinfo: enumeration handle over total entries\n"));
+ return NT_STATUS_NO_MORE_ENTRIES;
+ }
+
+ /* verify we won't overflow */
+ if (max_entries > num_account-enum_context) {
+ max_entries = num_account-enum_context;
+ DEBUG(5, ("samr_reply_query_dispinfo: only %d entries to return\n", max_entries));
+ }
+
+ /* calculate the size and limit on the number of entries we will return */
+ temp_size=max_entries*struct_size;
+
+ if (temp_size>max_size) {
+ max_entries=MIN((max_size/struct_size),max_entries);;
+ DEBUG(5, ("samr_reply_query_dispinfo: buffer size limits to only %d entries\n", max_entries));
+ }
+
+ if (!(ctr = (SAM_DISPINFO_CTR *)talloc_zero(p->mem_ctx,sizeof(SAM_DISPINFO_CTR))))
+ return NT_STATUS_NO_MEMORY;
+
+ ZERO_STRUCTP(ctr);
+
+ /* Now create reply structure */
+ switch (q_u->switch_level) {
+ case 0x1:
+ if (max_entries) {
+ if (!(ctr->sam.info1 = (SAM_DISPINFO_1 *)talloc_zero(p->mem_ctx,max_entries*sizeof(SAM_DISPINFO_1))))
+ return NT_STATUS_NO_MEMORY;
+ }
+ disp_ret = init_sam_dispinfo_1(p->mem_ctx, ctr->sam.info1, max_entries, enum_context,
+ info->disp_info.disp_user_info, &domain_sid);
+ if (!NT_STATUS_IS_OK(disp_ret))
+ return disp_ret;
+ break;
+ case 0x2:
+ if (max_entries) {
+ if (!(ctr->sam.info2 = (SAM_DISPINFO_2 *)talloc_zero(p->mem_ctx,max_entries*sizeof(SAM_DISPINFO_2))))
+ return NT_STATUS_NO_MEMORY;
+ }
+ disp_ret = init_sam_dispinfo_2(p->mem_ctx, ctr->sam.info2, max_entries, enum_context,
+ info->disp_info.disp_user_info, &domain_sid);
+ if (!NT_STATUS_IS_OK(disp_ret))
+ return disp_ret;
+ break;
+ case 0x3:
+ if (max_entries) {
+ if (!(ctr->sam.info3 = (SAM_DISPINFO_3 *)talloc_zero(p->mem_ctx,max_entries*sizeof(SAM_DISPINFO_3))))
+ return NT_STATUS_NO_MEMORY;
+ }
+ disp_ret = init_sam_dispinfo_3(p->mem_ctx, ctr->sam.info3, max_entries, enum_context, info->disp_info.disp_group_info);
+ if (!NT_STATUS_IS_OK(disp_ret))
+ return disp_ret;
+ break;
+ case 0x4:
+ if (max_entries) {
+ if (!(ctr->sam.info4 = (SAM_DISPINFO_4 *)talloc_zero(p->mem_ctx,max_entries*sizeof(SAM_DISPINFO_4))))
+ return NT_STATUS_NO_MEMORY;
+ }
+ disp_ret = init_sam_dispinfo_4(p->mem_ctx, ctr->sam.info4, max_entries, enum_context, info->disp_info.disp_user_info);
+ if (!NT_STATUS_IS_OK(disp_ret))
+ return disp_ret;
+ break;
+ case 0x5:
+ if (max_entries) {
+ if (!(ctr->sam.info5 = (SAM_DISPINFO_5 *)talloc_zero(p->mem_ctx,max_entries*sizeof(SAM_DISPINFO_5))))
+ return NT_STATUS_NO_MEMORY;
+ }
+ disp_ret = init_sam_dispinfo_5(p->mem_ctx, ctr->sam.info5, max_entries, enum_context, info->disp_info.disp_group_info);
+ if (!NT_STATUS_IS_OK(disp_ret))
+ return disp_ret;
+ break;
+
+ default:
+ ctr->sam.info = NULL;
+ return NT_STATUS_INVALID_INFO_CLASS;
+ }
+
+ /* calculate the total size */
+ total_data_size=num_account*struct_size;
+
+ if (enum_context+max_entries < num_account)
+ r_u->status = STATUS_MORE_ENTRIES;
+
+ DEBUG(5, ("_samr_query_dispinfo: %d\n", __LINE__));
+
+ init_samr_r_query_dispinfo(r_u, max_entries, total_data_size, temp_size, q_u->switch_level, ctr, r_u->status);
+
+ return r_u->status;
+
+}
+
+/*******************************************************************
+ samr_reply_query_aliasinfo
+ ********************************************************************/
+
+NTSTATUS _samr_query_aliasinfo(pipes_struct *p, SAMR_Q_QUERY_ALIASINFO *q_u, SAMR_R_QUERY_ALIASINFO *r_u)
+{
+ DOM_SID sid;
+ struct acct_info info;
+ uint32 acc_granted;
+ BOOL ret;
+
+ r_u->status = NT_STATUS_OK;
+
+ DEBUG(5,("_samr_query_aliasinfo: %d\n", __LINE__));
+
+ /* find the policy handle. open a policy on it. */
+ if (!get_lsa_policy_samr_sid(p, &q_u->pol, &sid, &acc_granted))
+ return NT_STATUS_INVALID_HANDLE;
+ if (!NT_STATUS_IS_OK(r_u->status = access_check_samr_function(p, acc_granted, SA_RIGHT_ALIAS_LOOKUP_INFO, NULL, "_samr_query_aliasinfo"))) {
+ return r_u->status;
+ }
+
+ become_root();
+ ret = pdb_get_aliasinfo(&sid, &info);
+ unbecome_root();
+
+ if ( !ret )
+ return NT_STATUS_NO_SUCH_ALIAS;
+
+ switch (q_u->switch_level) {
+ case 1:
+ r_u->ptr = 1;
+ r_u->ctr.switch_value1 = 1;
+ init_samr_alias_info1(&r_u->ctr.alias.info1,
+ info.acct_name, 1, info.acct_desc);
+ break;
+ case 3:
+ r_u->ptr = 1;
+ r_u->ctr.switch_value1 = 3;
+ init_samr_alias_info3(&r_u->ctr.alias.info3, info.acct_desc);
+ break;
+ default:
+ return NT_STATUS_INVALID_INFO_CLASS;
+ }
+
+ DEBUG(5,("_samr_query_aliasinfo: %d\n", __LINE__));
+
+ return r_u->status;
+}
+
+#if 0
+/*******************************************************************
+ samr_reply_lookup_ids
+ ********************************************************************/
+
+ uint32 _samr_lookup_ids(pipes_struct *p, SAMR_Q_LOOKUP_IDS *q_u, SAMR_R_LOOKUP_IDS *r_u)
+{
+ uint32 rid[MAX_SAM_ENTRIES];
+ int num_rids = q_u->num_sids1;
+
+ r_u->status = NT_STATUS_OK;
+
+ DEBUG(5,("_samr_lookup_ids: %d\n", __LINE__));
+
+ if (num_rids > MAX_SAM_ENTRIES) {
+ num_rids = MAX_SAM_ENTRIES;
+ DEBUG(5,("_samr_lookup_ids: truncating entries to %d\n", num_rids));
+ }
+
+#if 0
+ int i;
+ SMB_ASSERT_ARRAY(q_u->uni_user_name, num_rids);
+
+ for (i = 0; i < num_rids && status == 0; i++)
+ {
+ struct sam_passwd *sam_pass;
+ fstring user_name;
+
+
+ fstrcpy(user_name, unistrn2(q_u->uni_user_name[i].buffer,
+ q_u->uni_user_name[i].uni_str_len));
+
+ /* find the user account */
+ become_root();
+ sam_pass = get_smb21pwd_entry(user_name, 0);
+ unbecome_root();
+
+ if (sam_pass == NULL)
+ {
+ status = 0xC0000000 | NT_STATUS_NO_SUCH_USER;
+ rid[i] = 0;
+ }
+ else
+ {
+ rid[i] = sam_pass->user_rid;
+ }
+ }
+#endif
+
+ num_rids = 1;
+ rid[0] = BUILTIN_ALIAS_RID_USERS;
+
+ init_samr_r_lookup_ids(&r_u, num_rids, rid, NT_STATUS_OK);
+
+ DEBUG(5,("_samr_lookup_ids: %d\n", __LINE__));
+
+ return r_u->status;
+}
+#endif
+
+/*******************************************************************
+ _samr_lookup_names
+ ********************************************************************/
+
+NTSTATUS _samr_lookup_names(pipes_struct *p, SAMR_Q_LOOKUP_NAMES *q_u, SAMR_R_LOOKUP_NAMES *r_u)
+{
+ uint32 rid[MAX_SAM_ENTRIES];
+ uint32 local_rid;
+ enum SID_NAME_USE type[MAX_SAM_ENTRIES];
+ enum SID_NAME_USE local_type;
+ int i;
+ int num_rids = q_u->num_names2;
+ DOM_SID pol_sid;
+ fstring sid_str;
+ uint32 acc_granted;
+
+ r_u->status = NT_STATUS_OK;
+
+ DEBUG(5,("_samr_lookup_names: %d\n", __LINE__));
+
+ ZERO_ARRAY(rid);
+ ZERO_ARRAY(type);
+
+ if (!get_lsa_policy_samr_sid(p, &q_u->pol, &pol_sid, &acc_granted)) {
+ init_samr_r_lookup_names(p->mem_ctx, r_u, 0, NULL, NULL, NT_STATUS_OBJECT_TYPE_MISMATCH);
+ return r_u->status;
+ }
+
+ if (!NT_STATUS_IS_OK(r_u->status = access_check_samr_function(p, acc_granted, 0, NULL, "_samr_lookup_names"))) { /* Don't know the acc_bits yet */
+ return r_u->status;
+ }
+
+ if (num_rids > MAX_SAM_ENTRIES) {
+ num_rids = MAX_SAM_ENTRIES;
+ DEBUG(5,("_samr_lookup_names: truncating entries to %d\n", num_rids));
+ }
+
+ DEBUG(5,("_samr_lookup_names: looking name on SID %s\n", sid_to_string(sid_str, &pol_sid)));
+
+ for (i = 0; i < num_rids; i++) {
+ fstring name;
+ DOM_SID sid;
+ int ret;
+
+ r_u->status = NT_STATUS_NONE_MAPPED;
+
+ rid [i] = 0xffffffff;
+ type[i] = SID_NAME_UNKNOWN;
+
+ ret = rpcstr_pull(name, q_u->uni_name[i].buffer, sizeof(name), q_u->uni_name[i].uni_str_len*2, 0);
+
+ /*
+ * we are only looking for a name
+ * the SID we get back can be outside
+ * the scope of the pol_sid
+ *
+ * in clear: it prevents to reply to domain\group: yes
+ * when only builtin\group exists.
+ *
+ * a cleaner code is to add the sid of the domain we're looking in
+ * to the local_lookup_name function.
+ */
+
+ if ((ret > 0) && local_lookup_name(name, &sid, &local_type)) {
+ sid_split_rid(&sid, &local_rid);
+
+ if (sid_equal(&sid, &pol_sid)) {
+ rid[i]=local_rid;
+
+ /* Windows does not return WKN_GRP here, even
+ * on lookups in builtin */
+ type[i] = (local_type == SID_NAME_WKN_GRP) ?
+ SID_NAME_ALIAS : local_type;
+
+ r_u->status = NT_STATUS_OK;
+ }
+ }
+ }
+
+ init_samr_r_lookup_names(p->mem_ctx, r_u, num_rids, rid, (uint32 *)type, r_u->status);
+
+ DEBUG(5,("_samr_lookup_names: %d\n", __LINE__));
+
+ return r_u->status;
+}
+
+/*******************************************************************
+ _samr_chgpasswd_user
+ ********************************************************************/
+
+NTSTATUS _samr_chgpasswd_user(pipes_struct *p, SAMR_Q_CHGPASSWD_USER *q_u, SAMR_R_CHGPASSWD_USER *r_u)
+{
+ fstring user_name;
+ fstring wks;
+
+ DEBUG(5,("_samr_chgpasswd_user: %d\n", __LINE__));
+
+ r_u->status = NT_STATUS_OK;
+
+ rpcstr_pull(user_name, q_u->uni_user_name.buffer, sizeof(user_name), q_u->uni_user_name.uni_str_len*2, 0);
+ rpcstr_pull(wks, q_u->uni_dest_host.buffer, sizeof(wks), q_u->uni_dest_host.uni_str_len*2,0);
+
+ DEBUG(5,("samr_chgpasswd_user: user: %s wks: %s\n", user_name, wks));
+
+ /*
+ * Pass the user through the NT -> unix user mapping
+ * function.
+ */
+
+ (void)map_username(user_name);
+
+ /*
+ * UNIX username case mangling not required, pass_oem_change
+ * is case insensitive.
+ */
+
+ r_u->status = pass_oem_change(user_name, q_u->lm_newpass.pass, q_u->lm_oldhash.hash,
+ q_u->nt_newpass.pass, q_u->nt_oldhash.hash);
+
+ init_samr_r_chgpasswd_user(r_u, r_u->status);
+
+ DEBUG(5,("_samr_chgpasswd_user: %d\n", __LINE__));
+
+ return r_u->status;
+}
+
+/*******************************************************************
+makes a SAMR_R_LOOKUP_RIDS structure.
+********************************************************************/
+
+static BOOL make_samr_lookup_rids(TALLOC_CTX *ctx, uint32 num_names, fstring names[],
+ UNIHDR **pp_hdr_name, UNISTR2 **pp_uni_name)
+{
+ uint32 i;
+ UNIHDR *hdr_name=NULL;
+ UNISTR2 *uni_name=NULL;
+
+ *pp_uni_name = NULL;
+ *pp_hdr_name = NULL;
+
+ if (num_names != 0) {
+ hdr_name = (UNIHDR *)talloc_zero(ctx, sizeof(UNIHDR)*num_names);
+ if (hdr_name == NULL)
+ return False;
+
+ uni_name = (UNISTR2 *)talloc_zero(ctx,sizeof(UNISTR2)*num_names);
+ if (uni_name == NULL)
+ return False;
+ }
+
+ for (i = 0; i < num_names; i++) {
+ DEBUG(10, ("names[%d]:%s\n", i, names[i] ? names[i] : ""));
+ init_unistr2(&uni_name[i], names[i], UNI_FLAGS_NONE);
+ init_uni_hdr(&hdr_name[i], &uni_name[i]);
+ }
+
+ *pp_uni_name = uni_name;
+ *pp_hdr_name = hdr_name;
+
+ return True;
+}
+
+/*******************************************************************
+ _samr_lookup_rids
+ ********************************************************************/
+
+NTSTATUS _samr_lookup_rids(pipes_struct *p, SAMR_Q_LOOKUP_RIDS *q_u, SAMR_R_LOOKUP_RIDS *r_u)
+{
+ fstring group_names[MAX_SAM_ENTRIES];
+ uint32 *group_attrs = NULL;
+ UNIHDR *hdr_name = NULL;
+ UNISTR2 *uni_name = NULL;
+ DOM_SID pol_sid;
+ int num_rids = q_u->num_rids1;
+ int i;
+ uint32 acc_granted;
+
+ r_u->status = NT_STATUS_OK;
+
+ DEBUG(5,("_samr_lookup_rids: %d\n", __LINE__));
+
+ /* find the policy handle. open a policy on it. */
+ if (!get_lsa_policy_samr_sid(p, &q_u->pol, &pol_sid, &acc_granted))
+ return NT_STATUS_INVALID_HANDLE;
+
+ if (num_rids > MAX_SAM_ENTRIES) {
+ num_rids = MAX_SAM_ENTRIES;
+ DEBUG(5,("_samr_lookup_rids: truncating entries to %d\n", num_rids));
+ }
+
+ if (num_rids) {
+ if ((group_attrs = (uint32 *)talloc_zero(p->mem_ctx, num_rids * sizeof(uint32))) == NULL)
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ r_u->status = NT_STATUS_NONE_MAPPED;
+
+ become_root(); /* lookup_sid can require root privs */
+
+ for (i = 0; i < num_rids; i++) {
+ fstring tmpname;
+ fstring domname;
+ DOM_SID sid;
+ enum SID_NAME_USE type;
+
+ group_attrs[i] = SID_NAME_UNKNOWN;
+ *group_names[i] = '\0';
+
+ if (sid_equal(&pol_sid, get_global_sam_sid())) {
+ sid_copy(&sid, &pol_sid);
+ sid_append_rid(&sid, q_u->rid[i]);
+
+ if (lookup_sid(&sid, domname, tmpname, &type)) {
+ r_u->status = NT_STATUS_OK;
+ group_attrs[i] = (uint32)type;
+ fstrcpy(group_names[i],tmpname);
+ DEBUG(5,("_samr_lookup_rids: %s:%d\n", group_names[i], group_attrs[i]));
+ }
+ }
+ }
+
+ unbecome_root();
+
+ if(!make_samr_lookup_rids(p->mem_ctx, num_rids, group_names, &hdr_name, &uni_name))
+ return NT_STATUS_NO_MEMORY;
+
+ init_samr_r_lookup_rids(r_u, num_rids, hdr_name, uni_name, group_attrs);
+
+ DEBUG(5,("_samr_lookup_rids: %d\n", __LINE__));
+
+ return r_u->status;
+}
+
+/*******************************************************************
+ _samr_open_user. Safe - gives out no passwd info.
+ ********************************************************************/
+
+NTSTATUS _samr_open_user(pipes_struct *p, SAMR_Q_OPEN_USER *q_u, SAMR_R_OPEN_USER *r_u)
+{
+ SAM_ACCOUNT *sampass=NULL;
+ DOM_SID sid;
+ POLICY_HND domain_pol = q_u->domain_pol;
+ POLICY_HND *user_pol = &r_u->user_pol;
+ struct samr_info *info = NULL;
+ SEC_DESC *psd = NULL;
+ uint32 acc_granted;
+ uint32 des_access = q_u->access_mask;
+ size_t sd_size;
+ BOOL ret;
+ NTSTATUS nt_status;
+ uint32 priv_list[3] = {SE_MACHINE_ACCOUNT, SE_ADD_USERS, SE_NONE};
+
+ r_u->status = NT_STATUS_OK;
+
+ /* find the domain policy handle and get domain SID / access bits in the domain policy. */
+ if (!get_lsa_policy_samr_sid(p, &domain_pol, &sid, &acc_granted))
+ return NT_STATUS_INVALID_HANDLE;
+
+ if (!NT_STATUS_IS_OK(nt_status = access_check_samr_function(p, acc_granted, SA_RIGHT_DOMAIN_OPEN_ACCOUNT, priv_list, "_samr_open_user"))) {
+ return nt_status;
+ }
+
+ nt_status = pdb_init_sam_talloc(p->mem_ctx, &sampass);
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ return nt_status;
+ }
+
+ /* append the user's RID to it */
+ if (!sid_append_rid(&sid, q_u->user_rid))
+ return NT_STATUS_NO_SUCH_USER;
+
+ /* check if access can be granted as requested by client. */
+ samr_make_usr_obj_sd(p->mem_ctx, &psd, &sd_size, &sid);
+ se_map_generic(&des_access, &usr_generic_mapping);
+ if (!NT_STATUS_IS_OK(nt_status =
+ access_check_samr_object(psd, p, des_access, &acc_granted,
+ priv_list, "_samr_open_user"))) {
+ return nt_status;
+ }
+
+ become_root();
+ ret=pdb_getsampwsid(sampass, &sid);
+ unbecome_root();
+
+ /* check that the SID exists in our domain. */
+ if (ret == False) {
+ return NT_STATUS_NO_SUCH_USER;
+ }
+
+ pdb_free_sam(&sampass);
+
+ /* associate the user's SID and access bits with the new handle. */
+ if ((info = get_samr_info_by_sid(&sid)) == NULL)
+ return NT_STATUS_NO_MEMORY;
+ info->acc_granted = acc_granted;
+
+ /* get a (unique) handle. open a policy on it. */
+ if (!create_policy_hnd(p, user_pol, free_samr_info, (void *)info))
+ return NT_STATUS_OBJECT_NAME_NOT_FOUND;
+
+ return r_u->status;
+}
+
+/*************************************************************************
+ get_user_info_10. Safe. Only gives out acb bits.
+ *************************************************************************/
+
+static NTSTATUS get_user_info_10(TALLOC_CTX *mem_ctx, SAM_USER_INFO_10 *id10, DOM_SID *user_sid)
+{
+ SAM_ACCOUNT *smbpass=NULL;
+ BOOL ret;
+ NTSTATUS nt_status;
+
+ nt_status = pdb_init_sam_talloc(mem_ctx, &smbpass);
+
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ return nt_status;
+ }
+
+ become_root();
+ ret = pdb_getsampwsid(smbpass, user_sid);
+ unbecome_root();
+
+ if (ret==False) {
+ DEBUG(4,("User %s not found\n", sid_string_static(user_sid)));
+ return NT_STATUS_NO_SUCH_USER;
+ }
+
+ DEBUG(3,("User:[%s]\n", pdb_get_username(smbpass) ));
+
+ ZERO_STRUCTP(id10);
+ init_sam_user_info10(id10, pdb_get_acct_ctrl(smbpass) );
+
+ pdb_free_sam(&smbpass);
+
+ return NT_STATUS_OK;
+}
+
+/*************************************************************************
+ get_user_info_12. OK - this is the killer as it gives out password info.
+ Ensure that this is only allowed on an encrypted connection with a root
+ user. JRA.
+ *************************************************************************/
+
+static NTSTATUS get_user_info_12(pipes_struct *p, TALLOC_CTX *mem_ctx, SAM_USER_INFO_12 * id12, DOM_SID *user_sid)
+{
+ SAM_ACCOUNT *smbpass=NULL;
+ BOOL ret;
+ NTSTATUS nt_status;
+
+ if (!p->ntlmssp_auth_validated)
+ return NT_STATUS_ACCESS_DENIED;
+
+ if (!(p->ntlmssp_chal_flags & NTLMSSP_NEGOTIATE_SIGN) || !(p->ntlmssp_chal_flags & NTLMSSP_NEGOTIATE_SEAL))
+ return NT_STATUS_ACCESS_DENIED;
+
+ /*
+ * Do *NOT* do become_root()/unbecome_root() here ! JRA.
+ */
+
+ nt_status = pdb_init_sam_talloc(mem_ctx, &smbpass);
+
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ return nt_status;
+ }
+
+ ret = pdb_getsampwsid(smbpass, user_sid);
+
+ if (ret == False) {
+ DEBUG(4, ("User %s not found\n", sid_string_static(user_sid)));
+ pdb_free_sam(&smbpass);
+ return (geteuid() == (uid_t)0) ? NT_STATUS_NO_SUCH_USER : NT_STATUS_ACCESS_DENIED;
+ }
+
+ DEBUG(3,("User:[%s] 0x%x\n", pdb_get_username(smbpass), pdb_get_acct_ctrl(smbpass) ));
+
+ if ( pdb_get_acct_ctrl(smbpass) & ACB_DISABLED) {
+ pdb_free_sam(&smbpass);
+ return NT_STATUS_ACCOUNT_DISABLED;
+ }
+
+ ZERO_STRUCTP(id12);
+ init_sam_user_info12(id12, pdb_get_lanman_passwd(smbpass), pdb_get_nt_passwd(smbpass));
+
+ pdb_free_sam(&smbpass);
+
+ return NT_STATUS_OK;
+}
+
+/*************************************************************************
+ get_user_info_20
+ *************************************************************************/
+
+static NTSTATUS get_user_info_20(TALLOC_CTX *mem_ctx, SAM_USER_INFO_20 *id20, DOM_SID *user_sid)
+{
+ SAM_ACCOUNT *sampass=NULL;
+ BOOL ret;
+
+ pdb_init_sam_talloc(mem_ctx, &sampass);
+
+ become_root();
+ ret = pdb_getsampwsid(sampass, user_sid);
+ unbecome_root();
+
+ if (ret == False) {
+ DEBUG(4,("User %s not found\n", sid_string_static(user_sid)));
+ return NT_STATUS_NO_SUCH_USER;
+ }
+
+ samr_clear_sam_passwd(sampass);
+
+ DEBUG(3,("User:[%s]\n", pdb_get_username(sampass) ));
+
+ ZERO_STRUCTP(id20);
+ init_sam_user_info20A(id20, sampass);
+
+ pdb_free_sam(&sampass);
+
+ return NT_STATUS_OK;
+}
+
+/*************************************************************************
+ get_user_info_21
+ *************************************************************************/
+
+static NTSTATUS get_user_info_21(TALLOC_CTX *mem_ctx, SAM_USER_INFO_21 *id21,
+ DOM_SID *user_sid, DOM_SID *domain_sid)
+{
+ SAM_ACCOUNT *sampass=NULL;
+ BOOL ret;
+ NTSTATUS nt_status;
+
+ nt_status = pdb_init_sam_talloc(mem_ctx, &sampass);
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ return nt_status;
+ }
+
+ become_root();
+ ret = pdb_getsampwsid(sampass, user_sid);
+ unbecome_root();
+
+ if (ret == False) {
+ DEBUG(4,("User %s not found\n", sid_string_static(user_sid)));
+ return NT_STATUS_NO_SUCH_USER;
+ }
+
+ samr_clear_sam_passwd(sampass);
+
+ DEBUG(3,("User:[%s]\n", pdb_get_username(sampass) ));
+
+ ZERO_STRUCTP(id21);
+ nt_status = init_sam_user_info21A(id21, sampass, domain_sid);
+
+ pdb_free_sam(&sampass);
+
+ return NT_STATUS_OK;
+}
+
+/*******************************************************************
+ _samr_query_userinfo
+ ********************************************************************/
+
+NTSTATUS _samr_query_userinfo(pipes_struct *p, SAMR_Q_QUERY_USERINFO *q_u, SAMR_R_QUERY_USERINFO *r_u)
+{
+ SAM_USERINFO_CTR *ctr;
+ struct samr_info *info = NULL;
+ DOM_SID domain_sid;
+ uint32 rid;
+
+ r_u->status=NT_STATUS_OK;
+
+ /* search for the handle */
+ if (!find_policy_by_hnd(p, &q_u->pol, (void **)&info))
+ return NT_STATUS_INVALID_HANDLE;
+
+ domain_sid = info->sid;
+
+ sid_split_rid(&domain_sid, &rid);
+
+ if (!sid_check_is_in_our_domain(&info->sid))
+ return NT_STATUS_OBJECT_TYPE_MISMATCH;
+
+ DEBUG(5,("_samr_query_userinfo: sid:%s\n", sid_string_static(&info->sid)));
+
+ ctr = (SAM_USERINFO_CTR *)talloc_zero(p->mem_ctx, sizeof(SAM_USERINFO_CTR));
+ if (!ctr)
+ return NT_STATUS_NO_MEMORY;
+
+ ZERO_STRUCTP(ctr);
+
+ /* ok! user info levels (lots: see MSDEV help), off we go... */
+ ctr->switch_value = q_u->switch_value;
+
+ switch (q_u->switch_value) {
+ case 0x10:
+ ctr->info.id10 = (SAM_USER_INFO_10 *)talloc_zero(p->mem_ctx, sizeof(SAM_USER_INFO_10));
+ if (ctr->info.id10 == NULL)
+ return NT_STATUS_NO_MEMORY;
+
+ if (!NT_STATUS_IS_OK(r_u->status = get_user_info_10(p->mem_ctx, ctr->info.id10, &info->sid)))
+ return r_u->status;
+ break;
+
+#if 0
+/* whoops - got this wrong. i think. or don't understand what's happening. */
+ case 0x11:
+ {
+ NTTIME expire;
+ info = (void *)&id11;
+
+ expire.low = 0xffffffff;
+ expire.high = 0x7fffffff;
+
+ ctr->info.id = (SAM_USER_INFO_11 *)talloc_zero(p->mem_ctx,
+ sizeof
+ (*ctr->
+ info.
+ id11));
+ ZERO_STRUCTP(ctr->info.id11);
+ init_sam_user_info11(ctr->info.id11, &expire,
+ "BROOKFIELDS$", /* name */
+ 0x03ef, /* user rid */
+ 0x201, /* group rid */
+ 0x0080); /* acb info */
+
+ break;
+ }
+#endif
+
+ case 0x12:
+ ctr->info.id12 = (SAM_USER_INFO_12 *)talloc_zero(p->mem_ctx, sizeof(SAM_USER_INFO_12));
+ if (ctr->info.id12 == NULL)
+ return NT_STATUS_NO_MEMORY;
+
+ if (!NT_STATUS_IS_OK(r_u->status = get_user_info_12(p, p->mem_ctx, ctr->info.id12, &info->sid)))
+ return r_u->status;
+ break;
+
+ case 20:
+ ctr->info.id20 = (SAM_USER_INFO_20 *)talloc_zero(p->mem_ctx,sizeof(SAM_USER_INFO_20));
+ if (ctr->info.id20 == NULL)
+ return NT_STATUS_NO_MEMORY;
+ if (!NT_STATUS_IS_OK(r_u->status = get_user_info_20(p->mem_ctx, ctr->info.id20, &info->sid)))
+ return r_u->status;
+ break;
+
+ case 21:
+ ctr->info.id21 = (SAM_USER_INFO_21 *)talloc_zero(p->mem_ctx,sizeof(SAM_USER_INFO_21));
+ if (ctr->info.id21 == NULL)
+ return NT_STATUS_NO_MEMORY;
+ if (!NT_STATUS_IS_OK(r_u->status = get_user_info_21(p->mem_ctx, ctr->info.id21,
+ &info->sid, &domain_sid)))
+ return r_u->status;
+ break;
+
+ default:
+ return NT_STATUS_INVALID_INFO_CLASS;
+ }
+
+ init_samr_r_query_userinfo(r_u, ctr, r_u->status);
+
+ DEBUG(5,("_samr_query_userinfo: %d\n", __LINE__));
+
+ return r_u->status;
+}
+
+/*******************************************************************
+ samr_reply_query_usergroups
+ ********************************************************************/
+
+NTSTATUS _samr_query_usergroups(pipes_struct *p, SAMR_Q_QUERY_USERGROUPS *q_u, SAMR_R_QUERY_USERGROUPS *r_u)
+{
+ SAM_ACCOUNT *sam_pass=NULL;
+ DOM_SID sid;
+ DOM_GID *gids = NULL;
+ int num_groups = 0;
+ uint32 acc_granted;
+ BOOL ret;
+
+ /*
+ * from the SID in the request:
+ * we should send back the list of DOMAIN GROUPS
+ * the user is a member of
+ *
+ * and only the DOMAIN GROUPS
+ * no ALIASES !!! neither aliases of the domain
+ * nor aliases of the builtin SID
+ *
+ * JFM, 12/2/2001
+ */
+
+ r_u->status = NT_STATUS_OK;
+
+ DEBUG(5,("_samr_query_usergroups: %d\n", __LINE__));
+
+ /* find the policy handle. open a policy on it. */
+ if (!get_lsa_policy_samr_sid(p, &q_u->pol, &sid, &acc_granted))
+ return NT_STATUS_INVALID_HANDLE;
+
+ if (!NT_STATUS_IS_OK(r_u->status = access_check_samr_function(p, acc_granted, SA_RIGHT_USER_GET_GROUPS, NULL, "_samr_query_usergroups"))) {
+ return r_u->status;
+ }
+
+ if (!sid_check_is_in_our_domain(&sid))
+ return NT_STATUS_OBJECT_TYPE_MISMATCH;
+
+ pdb_init_sam(&sam_pass);
+
+ become_root();
+ ret = pdb_getsampwsid(sam_pass, &sid);
+ unbecome_root();
+
+ if (ret == False) {
+ pdb_free_sam(&sam_pass);
+ return NT_STATUS_NO_SUCH_USER;
+ }
+
+ if(!get_domain_user_groups(p->mem_ctx, &num_groups, &gids, sam_pass)) {
+ pdb_free_sam(&sam_pass);
+ return NT_STATUS_NO_SUCH_GROUP;
+ }
+
+ /* construct the response. lkclXXXX: gids are not copied! */
+ init_samr_r_query_usergroups(r_u, num_groups, gids, r_u->status);
+
+ DEBUG(5,("_samr_query_usergroups: %d\n", __LINE__));
+
+ pdb_free_sam(&sam_pass);
+
+ return r_u->status;
+}
+
+/*******************************************************************
+ _samr_query_dom_info
+ ********************************************************************/
+
+NTSTATUS _samr_query_dom_info(pipes_struct *p, SAMR_Q_QUERY_DOMAIN_INFO *q_u, SAMR_R_QUERY_DOMAIN_INFO *r_u)
+{
+ struct samr_info *info = NULL;
+ SAM_UNK_CTR *ctr;
+ uint32 min_pass_len,pass_hist,flag;
+ time_t u_expire, u_min_age;
+ NTTIME nt_expire, nt_min_age;
+
+ time_t u_lock_duration, u_reset_time;
+ NTTIME nt_lock_duration, nt_reset_time;
+ uint32 lockout;
+
+ time_t u_logout;
+ NTTIME nt_logout;
+
+ uint32 account_policy_temp;
+
+ uint32 num_users=0, num_groups=0, num_aliases=0;
+
+ if ((ctr = (SAM_UNK_CTR *)talloc_zero(p->mem_ctx, sizeof(SAM_UNK_CTR))) == NULL)
+ return NT_STATUS_NO_MEMORY;
+
+ ZERO_STRUCTP(ctr);
+
+ r_u->status = NT_STATUS_OK;
+
+ DEBUG(5,("_samr_query_dom_info: %d\n", __LINE__));
+
+ /* find the policy handle. open a policy on it. */
+ if (!find_policy_by_hnd(p, &q_u->domain_pol, (void **)&info))
+ return NT_STATUS_INVALID_HANDLE;
+
+ switch (q_u->switch_value) {
+ case 0x01:
+
+ account_policy_get(AP_MIN_PASSWORD_LEN, &account_policy_temp);
+ min_pass_len = account_policy_temp;
+
+ account_policy_get(AP_PASSWORD_HISTORY, &account_policy_temp);
+ pass_hist = account_policy_temp;
+
+ account_policy_get(AP_USER_MUST_LOGON_TO_CHG_PASS, &account_policy_temp);
+ flag = account_policy_temp;
+
+ account_policy_get(AP_MAX_PASSWORD_AGE, &account_policy_temp);
+ u_expire = account_policy_temp;
+
+ account_policy_get(AP_MIN_PASSWORD_AGE, &account_policy_temp);
+ u_min_age = account_policy_temp;
+
+ unix_to_nt_time_abs(&nt_expire, u_expire);
+ unix_to_nt_time_abs(&nt_min_age, u_min_age);
+
+ init_unk_info1(&ctr->info.inf1, (uint16)min_pass_len, (uint16)pass_hist,
+ flag, nt_expire, nt_min_age);
+ break;
+ case 0x02:
+ become_root();
+ r_u->status=load_sampwd_entries(info, ACB_NORMAL, False);
+ unbecome_root();
+ if (!NT_STATUS_IS_OK(r_u->status)) {
+ DEBUG(5, ("_samr_query_dispinfo: load_sampwd_entries failed\n"));
+ return r_u->status;
+ }
+ num_users=info->disp_info.num_user_account;
+ free_samr_db(info);
+
+ r_u->status=load_group_domain_entries(info, get_global_sam_sid());
+ if (!NT_STATUS_IS_OK(r_u->status)) {
+ DEBUG(5, ("_samr_query_dispinfo: load_group_domain_entries failed\n"));
+ return r_u->status;
+ }
+ num_groups=info->disp_info.num_group_account;
+ free_samr_db(info);
+
+ /* The time call below is to get a sequence number for the sam. FIXME !!! JRA. */
+ init_unk_info2(&ctr->info.inf2, lp_workgroup(), global_myname(), (uint32) time(NULL),
+ num_users, num_groups, num_aliases);
+ break;
+ case 0x03:
+ account_policy_get(AP_TIME_TO_LOGOUT, (unsigned int *)&u_logout);
+ unix_to_nt_time_abs(&nt_logout, u_logout);
+
+ init_unk_info3(&ctr->info.inf3, nt_logout);
+ break;
+ case 0x05:
+ init_unk_info5(&ctr->info.inf5, global_myname());
+ break;
+ case 0x06:
+ init_unk_info6(&ctr->info.inf6);
+ break;
+ case 0x07:
+ init_unk_info7(&ctr->info.inf7);
+ break;
+ case 0x0c:
+ account_policy_get(AP_LOCK_ACCOUNT_DURATION, &account_policy_temp);
+ u_lock_duration = account_policy_temp * 60;
+
+ account_policy_get(AP_RESET_COUNT_TIME, &account_policy_temp);
+ u_reset_time = account_policy_temp * 60;
+
+ account_policy_get(AP_BAD_ATTEMPT_LOCKOUT, &account_policy_temp);
+ lockout = account_policy_temp;
+
+ unix_to_nt_time_abs(&nt_lock_duration, u_lock_duration);
+ unix_to_nt_time_abs(&nt_reset_time, u_reset_time);
+
+ init_unk_info12(&ctr->info.inf12, nt_lock_duration, nt_reset_time, (uint16)lockout);
+ break;
+ default:
+ return NT_STATUS_INVALID_INFO_CLASS;
+ }
+
+ init_samr_r_query_dom_info(r_u, q_u->switch_value, ctr, NT_STATUS_OK);
+
+ DEBUG(5,("_samr_query_dom_info: %d\n", __LINE__));
+
+ return r_u->status;
+}
+
+/*******************************************************************
+ _samr_create_user
+ Create an account, can be either a normal user or a machine.
+ This funcion will need to be updated for bdc/domain trusts.
+ ********************************************************************/
+
+NTSTATUS _samr_create_user(pipes_struct *p, SAMR_Q_CREATE_USER *q_u, SAMR_R_CREATE_USER *r_u)
+{
+ SAM_ACCOUNT *sam_pass=NULL;
+ fstring account;
+ DOM_SID sid;
+ pstring add_script;
+ POLICY_HND dom_pol = q_u->domain_pol;
+ UNISTR2 user_account = q_u->uni_name;
+ uint16 acb_info = q_u->acb_info;
+ POLICY_HND *user_pol = &r_u->user_pol;
+ struct samr_info *info = NULL;
+ BOOL ret;
+ NTSTATUS nt_status;
+ struct passwd *pw;
+ uint32 acc_granted;
+ SEC_DESC *psd;
+ size_t sd_size;
+ uint32 new_rid = 0;
+ /* check this, when giving away 'add computer to domain' privs */
+ uint32 des_access = GENERIC_RIGHTS_USER_ALL_ACCESS;
+ uint32 priv_list[3] = {SE_MACHINE_ACCOUNT, SE_ADD_USERS, SE_NONE};
+
+ /* Get the domain SID stored in the domain policy */
+ if (!get_lsa_policy_samr_sid(p, &dom_pol, &sid, &acc_granted))
+ return NT_STATUS_INVALID_HANDLE;
+
+ if (!NT_STATUS_IS_OK(nt_status = access_check_samr_function(p, acc_granted, SA_RIGHT_DOMAIN_CREATE_USER, priv_list, "_samr_create_user"))) {
+ return nt_status;
+ }
+
+ if (!(acb_info == ACB_NORMAL || acb_info == ACB_DOMTRUST || acb_info == ACB_WSTRUST || acb_info == ACB_SVRTRUST)) {
+ /* Match Win2k, and return NT_STATUS_INVALID_PARAMETER if
+ this parameter is not an account type */
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ /* find the account: tell the caller if it exists.
+ lkclXXXX i have *no* idea if this is a problem or not
+ or even if you are supposed to construct a different
+ reply if the account already exists...
+ */
+
+ rpcstr_pull(account, user_account.buffer, sizeof(account), user_account.uni_str_len*2, 0);
+ strlower_m(account);
+
+ pdb_init_sam(&sam_pass);
+
+ become_root();
+ ret = pdb_getsampwnam(sam_pass, account);
+ unbecome_root();
+ if (ret == True) {
+ /* this account exists: say so */
+ pdb_free_sam(&sam_pass);
+ return NT_STATUS_USER_EXISTS;
+ }
+
+ pdb_free_sam(&sam_pass);
+
+ /*
+ * NB. VERY IMPORTANT ! This call must be done as the current pipe user,
+ * *NOT* surrounded by a become_root()/unbecome_root() call. This ensures
+ * that only people with write access to the smbpasswd file will be able
+ * to create a user. JRA.
+ */
+
+ /*
+ * add the user in the /etc/passwd file or the unix authority system.
+ * We don't check if the smb_create_user() function succed or not for 2 reasons:
+ * a) local_password_change() checks for us if the /etc/passwd account really exists
+ * b) smb_create_user() would return an error if the account already exists
+ * and as it could return an error also if it can't create the account, it would be tricky.
+ *
+ * So we go the easy way, only check after if the account exists.
+ * JFM (2/3/2001), to clear any possible bad understanding (-:
+ *
+ * We now have seperate script paramaters for adding users/machines so we
+ * now have some sainity-checking to match.
+ */
+
+ DEBUG(10,("checking account %s at pos %lu for $ termination\n",account, (unsigned long)strlen(account)-1));
+
+ /*
+ * we used to have code here that made sure the acb_info flags
+ * matched with the users named (e.g. an account flags as a machine
+ * trust account ended in '$'). It has been ifdef'd out for a long
+ * time, so I replaced it with this comment. --jerry
+ */
+
+ /* the passdb lookup has failed; check to see if we need to run the
+ add user/machine script */
+
+ /*
+ * we can't check both the ending $ and the acb_info.
+ *
+ * UserManager creates trust accounts (ending in $,
+ * normal that hidden accounts) with the acb_info equals to ACB_NORMAL.
+ * JFM, 11/29/2001
+ */
+ if (account[strlen(account)-1] == '$') {
+ if (NT_STATUS_IS_OK(user_has_privilege(&(p->pipe_user), SE_MACHINE_ACCOUNT)) || geteuid() == 0) {
+ DEBUG(3, ("user [%s] has been granted Add Machines privilege!\n", p->user_name));
+ become_root();
+ pstrcpy(add_script, lp_addmachine_script());
+ } else {
+ DEBUG(3, ("user [%s] doesn't have Add Machines privilege!\n", p->user_name));
+ return NT_STATUS_ACCESS_DENIED;
+ }
+ } else {
+ if (NT_STATUS_IS_OK(user_has_privilege(&(p->pipe_user), SE_ADD_USERS)) || geteuid() == 0) {
+ DEBUG(3, ("user [%s] has been granted Add Users privilege!\n", p->user_name));
+ become_root();
+ pstrcpy(add_script, lp_adduser_script());
+ } else {
+ DEBUG(3, ("user [%s] doesn't have Add Users privilege!\n", p->user_name));
+ return NT_STATUS_ACCESS_DENIED;
+ }
+ }
+
+ pw = Get_Pwnam(account);
+
+ /*********************************************************************
+ * HEADS UP! If we have to create a new user account, we have to get
+ * a new RID from somewhere. This used to be done by the passdb
+ * backend. It has been moved into idmap now. Since idmap is now
+ * wrapped up behind winbind, this means you have to run winbindd if you
+ * want new accounts to get a new RID when "enable rid algorithm = no".
+ * Tough. We now have a uniform way of allocating RIDs regardless
+ * of what ever passdb backend people may use.
+ * --jerry (2003-07-10)
+ *********************************************************************/
+
+ if ( !pw ) {
+
+ if (add_script[0] != '\0') {
+ int add_ret;
+ all_string_sub(add_script, "%u", account, sizeof(account));
+ add_ret = smbrun(add_script,NULL);
+ }
+ else /* no add user script -- ask winbindd to do it */
+ {
+ DEBUG(0, ("_samr_create_user: lp_adduser_script() = %s add_script = %s\n", lp_adduser_script(), add_script));
+ if (!winbind_create_user(account, &new_rid)) {
+ DEBUG(3,("_samr_create_user: winbind_create_user(%s) failed\n",
+ account));
+ }
+ }
+
+ }
+
+ /* implicit call to getpwnam() next. we have a valid SID coming out of this call */
+
+ if ( !NT_STATUS_IS_OK(nt_status = pdb_init_sam_new(&sam_pass, account, new_rid)) )
+ goto done;
+
+ pdb_set_acct_ctrl(sam_pass, acb_info, PDB_CHANGED);
+
+ if (!pdb_add_sam_account(sam_pass)) {
+ pdb_free_sam(&sam_pass);
+ DEBUG(0, ("could not add user/computer %s to passdb !?\n",
+ account));
+ nt_status = NT_STATUS_ACCESS_DENIED;
+ goto done;
+ }
+
+ /* Get the user's SID */
+ sid_copy(&sid, pdb_get_user_sid(sam_pass));
+
+ samr_make_usr_obj_sd(p->mem_ctx, &psd, &sd_size, &sid);
+ se_map_generic(&des_access, &usr_generic_mapping);
+ if (!NT_STATUS_IS_OK(nt_status =
+ access_check_samr_object(psd, p, des_access, &acc_granted,
+ priv_list, "_samr_create_user"))) {
+ goto done;
+ }
+
+ /* associate the user's SID with the new handle. */
+ if ((info = get_samr_info_by_sid(&sid)) == NULL) {
+ pdb_free_sam(&sam_pass);
+ nt_status = NT_STATUS_NO_MEMORY;
+ goto done;
+ }
+
+ ZERO_STRUCTP(info);
+ info->sid = sid;
+ info->acc_granted = acc_granted;
+
+ /* get a (unique) handle. open a policy on it. */
+ if (!create_policy_hnd(p, user_pol, free_samr_info, (void *)info)) {
+ pdb_free_sam(&sam_pass);
+ nt_status = NT_STATUS_OBJECT_NAME_NOT_FOUND;
+ goto done;
+ }
+
+ r_u->user_rid=pdb_get_user_rid(sam_pass);
+
+ r_u->access_granted = acc_granted;
+
+ pdb_free_sam(&sam_pass);
+
+ nt_status = NT_STATUS_OK;
+
+done:
+ unbecome_root();
+ return nt_status;
+}
+
+/*******************************************************************
+ samr_reply_connect_anon
+ ********************************************************************/
+
+NTSTATUS _samr_connect_anon(pipes_struct *p, SAMR_Q_CONNECT_ANON *q_u, SAMR_R_CONNECT_ANON *r_u)
+{
+ struct samr_info *info = NULL;
+ uint32 des_access = q_u->access_mask;
+
+ /* Access check */
+
+ if (!pipe_access_check(p)) {
+ DEBUG(3, ("access denied to samr_connect_anon\n"));
+ r_u->status = NT_STATUS_ACCESS_DENIED;
+ return r_u->status;
+ }
+
+ /* set up the SAMR connect_anon response */
+
+ r_u->status = NT_STATUS_OK;
+
+ /* associate the user's SID with the new handle. */
+ if ((info = get_samr_info_by_sid(NULL)) == NULL)
+ return NT_STATUS_NO_MEMORY;
+
+ /* don't give away the farm but this is probably ok. The SA_RIGHT_SAM_ENUM_DOMAINS
+ was observed from a win98 client trying to enumerate users (when configured
+ user level access control on shares) --jerry */
+
+ se_map_generic( &des_access, &sam_generic_mapping );
+ info->acc_granted = des_access & (SA_RIGHT_SAM_ENUM_DOMAINS|SA_RIGHT_SAM_OPEN_DOMAIN);
+
+ info->status = q_u->unknown_0;
+
+ /* get a (unique) handle. open a policy on it. */
+ if (!create_policy_hnd(p, &r_u->connect_pol, free_samr_info, (void *)info))
+ return NT_STATUS_OBJECT_NAME_NOT_FOUND;
+
+ return r_u->status;
+}
+
+/*******************************************************************
+ samr_reply_connect
+ ********************************************************************/
+
+NTSTATUS _samr_connect(pipes_struct *p, SAMR_Q_CONNECT *q_u, SAMR_R_CONNECT *r_u)
+{
+ struct samr_info *info = NULL;
+ SEC_DESC *psd = NULL;
+ uint32 acc_granted;
+ uint32 des_access = q_u->access_mask;
+ size_t sd_size;
+ NTSTATUS nt_status;
+
+
+ DEBUG(5,("_samr_connect: %d\n", __LINE__));
+
+ /* Access check */
+
+ if (!pipe_access_check(p)) {
+ DEBUG(3, ("access denied to samr_connect\n"));
+ r_u->status = NT_STATUS_ACCESS_DENIED;
+ return r_u->status;
+ }
+
+ samr_make_sam_obj_sd(p->mem_ctx, &psd, &sd_size);
+ se_map_generic(&des_access, &sam_generic_mapping);
+ if (!NT_STATUS_IS_OK(nt_status =
+ access_check_samr_object(psd, p, des_access, &acc_granted,
+ NULL, "_samr_connect"))) {
+ return nt_status;
+ }
+
+ r_u->status = NT_STATUS_OK;
+
+ /* associate the user's SID and access granted with the new handle. */
+ if ((info = get_samr_info_by_sid(NULL)) == NULL)
+ return NT_STATUS_NO_MEMORY;
+
+ info->acc_granted = acc_granted;
+ info->status = q_u->access_mask;
+
+ /* get a (unique) handle. open a policy on it. */
+ if (!create_policy_hnd(p, &r_u->connect_pol, free_samr_info, (void *)info))
+ return NT_STATUS_OBJECT_NAME_NOT_FOUND;
+
+ DEBUG(5,("_samr_connect: %d\n", __LINE__));
+
+ return r_u->status;
+}
+
+/*******************************************************************
+ samr_connect4
+ ********************************************************************/
+
+NTSTATUS _samr_connect4(pipes_struct *p, SAMR_Q_CONNECT4 *q_u, SAMR_R_CONNECT4 *r_u)
+{
+ struct samr_info *info = NULL;
+ SEC_DESC *psd = NULL;
+ uint32 acc_granted;
+ uint32 des_access = q_u->access_mask;
+ size_t sd_size;
+ NTSTATUS nt_status;
+
+
+ DEBUG(5,("_samr_connect4: %d\n", __LINE__));
+
+ /* Access check */
+
+ if (!pipe_access_check(p)) {
+ DEBUG(3, ("access denied to samr_connect4\n"));
+ r_u->status = NT_STATUS_ACCESS_DENIED;
+ return r_u->status;
+ }
+
+ samr_make_sam_obj_sd(p->mem_ctx, &psd, &sd_size);
+ se_map_generic(&des_access, &sam_generic_mapping);
+ if (!NT_STATUS_IS_OK(nt_status =
+ access_check_samr_object(psd, p, des_access, &acc_granted,
+ NULL, "_samr_connect"))) {
+ return nt_status;
+ }
+
+ r_u->status = NT_STATUS_OK;
+
+ /* associate the user's SID and access granted with the new handle. */
+ if ((info = get_samr_info_by_sid(NULL)) == NULL)
+ return NT_STATUS_NO_MEMORY;
+
+ info->acc_granted = acc_granted;
+ info->status = q_u->access_mask;
+
+ /* get a (unique) handle. open a policy on it. */
+ if (!create_policy_hnd(p, &r_u->connect_pol, free_samr_info, (void *)info))
+ return NT_STATUS_OBJECT_NAME_NOT_FOUND;
+
+ DEBUG(5,("_samr_connect: %d\n", __LINE__));
+
+ return r_u->status;
+}
+
+/**********************************************************************
+ api_samr_lookup_domain
+ **********************************************************************/
+
+NTSTATUS _samr_lookup_domain(pipes_struct *p, SAMR_Q_LOOKUP_DOMAIN *q_u, SAMR_R_LOOKUP_DOMAIN *r_u)
+{
+ struct samr_info *info;
+ fstring domain_name;
+ DOM_SID sid;
+
+ r_u->status = NT_STATUS_OK;
+
+ if (!find_policy_by_hnd(p, &q_u->connect_pol, (void**)&info))
+ return NT_STATUS_INVALID_HANDLE;
+
+ if (!NT_STATUS_IS_OK(r_u->status = access_check_samr_function(p, info->acc_granted,
+ SA_RIGHT_SAM_ENUM_DOMAINS, NULL, "_samr_lookup_domain")))
+ {
+ return r_u->status;
+ }
+
+ rpcstr_pull(domain_name, q_u->uni_domain.buffer, sizeof(domain_name), q_u->uni_domain.uni_str_len*2, 0);
+
+ ZERO_STRUCT(sid);
+
+ if (!secrets_fetch_domain_sid(domain_name, &sid)) {
+ r_u->status = NT_STATUS_NO_SUCH_DOMAIN;
+ }
+
+ DEBUG(2,("Returning domain sid for domain %s -> %s\n", domain_name, sid_string_static(&sid)));
+
+ init_samr_r_lookup_domain(r_u, &sid, r_u->status);
+
+ return r_u->status;
+}
+
+/******************************************************************
+makes a SAMR_R_ENUM_DOMAINS structure.
+********************************************************************/
+
+static BOOL make_enum_domains(TALLOC_CTX *ctx, SAM_ENTRY **pp_sam,
+ UNISTR2 **pp_uni_name, uint32 num_sam_entries, fstring doms[])
+{
+ uint32 i;
+ SAM_ENTRY *sam;
+ UNISTR2 *uni_name;
+
+ DEBUG(5, ("make_enum_domains\n"));
+
+ *pp_sam = NULL;
+ *pp_uni_name = NULL;
+
+ if (num_sam_entries == 0)
+ return True;
+
+ sam = (SAM_ENTRY *)talloc_zero(ctx, sizeof(SAM_ENTRY)*num_sam_entries);
+ uni_name = (UNISTR2 *)talloc_zero(ctx, sizeof(UNISTR2)*num_sam_entries);
+
+ if (sam == NULL || uni_name == NULL)
+ return False;
+
+ for (i = 0; i < num_sam_entries; i++) {
+ init_unistr2(&uni_name[i], doms[i], UNI_FLAGS_NONE);
+ init_sam_entry(&sam[i], &uni_name[i], 0);
+ }
+
+ *pp_sam = sam;
+ *pp_uni_name = uni_name;
+
+ return True;
+}
+
+/**********************************************************************
+ api_samr_enum_domains
+ **********************************************************************/
+
+NTSTATUS _samr_enum_domains(pipes_struct *p, SAMR_Q_ENUM_DOMAINS *q_u, SAMR_R_ENUM_DOMAINS *r_u)
+{
+ struct samr_info *info;
+ uint32 num_entries = 2;
+ fstring dom[2];
+ const char *name;
+
+ r_u->status = NT_STATUS_OK;
+
+ if (!find_policy_by_hnd(p, &q_u->pol, (void**)&info))
+ return NT_STATUS_INVALID_HANDLE;
+
+ if (!NT_STATUS_IS_OK(r_u->status = access_check_samr_function(p, info->acc_granted, SA_RIGHT_SAM_ENUM_DOMAINS, NULL, "_samr_enum_domains"))) {
+ return r_u->status;
+ }
+
+ name = get_global_sam_name();
+
+ fstrcpy(dom[0],name);
+ strupper_m(dom[0]);
+ fstrcpy(dom[1],"Builtin");
+
+ if (!make_enum_domains(p->mem_ctx, &r_u->sam, &r_u->uni_dom_name, num_entries, dom))
+ return NT_STATUS_NO_MEMORY;
+
+ init_samr_r_enum_domains(r_u, q_u->start_idx + num_entries, num_entries);
+
+ return r_u->status;
+}
+
+/*******************************************************************
+ api_samr_open_alias
+ ********************************************************************/
+
+NTSTATUS _samr_open_alias(pipes_struct *p, SAMR_Q_OPEN_ALIAS *q_u, SAMR_R_OPEN_ALIAS *r_u)
+{
+ DOM_SID sid;
+ POLICY_HND domain_pol = q_u->dom_pol;
+ uint32 alias_rid = q_u->rid_alias;
+ POLICY_HND *alias_pol = &r_u->pol;
+ struct samr_info *info = NULL;
+ SEC_DESC *psd = NULL;
+ uint32 acc_granted;
+ uint32 des_access = q_u->access_mask;
+ size_t sd_size;
+ NTSTATUS status;
+
+ r_u->status = NT_STATUS_OK;
+
+ /* find the domain policy and get the SID / access bits stored in the domain policy */
+ if (!get_lsa_policy_samr_sid(p, &domain_pol, &sid, &acc_granted))
+ return NT_STATUS_INVALID_HANDLE;
+
+ if (!NT_STATUS_IS_OK(status = access_check_samr_function(p, acc_granted, SA_RIGHT_DOMAIN_OPEN_ACCOUNT, NULL, "_samr_open_alias"))) {
+ return status;
+ }
+
+ /* append the alias' RID to it */
+ if (!sid_append_rid(&sid, alias_rid))
+ return NT_STATUS_NO_SUCH_USER;
+
+ /*check if access can be granted as requested by client. */
+ samr_make_ali_obj_sd(p->mem_ctx, &psd, &sd_size);
+ se_map_generic(&des_access,&ali_generic_mapping);
+ if (!NT_STATUS_IS_OK(status =
+ access_check_samr_object(psd, p, des_access, &acc_granted,
+ NULL, "_samr_open_alias"))) {
+ return status;
+ }
+
+ /*
+ * we should check if the rid really exist !!!
+ * JFM.
+ */
+
+ /* associate the user's SID with the new handle. */
+ if ((info = get_samr_info_by_sid(&sid)) == NULL)
+ return NT_STATUS_NO_MEMORY;
+
+ info->acc_granted = acc_granted;
+
+ /* get a (unique) handle. open a policy on it. */
+ if (!create_policy_hnd(p, alias_pol, free_samr_info, (void *)info))
+ return NT_STATUS_OBJECT_NAME_NOT_FOUND;
+
+ return r_u->status;
+}
+
+/*******************************************************************
+ set_user_info_10
+ ********************************************************************/
+
+static BOOL set_user_info_10(const SAM_USER_INFO_10 *id10, DOM_SID *sid)
+{
+ SAM_ACCOUNT *pwd =NULL;
+ BOOL ret;
+
+ pdb_init_sam(&pwd);
+
+ ret = pdb_getsampwsid(pwd, sid);
+
+ if(ret==False) {
+ pdb_free_sam(&pwd);
+ return False;
+ }
+
+ if (id10 == NULL) {
+ DEBUG(5, ("set_user_info_10: NULL id10\n"));
+ pdb_free_sam(&pwd);
+ return False;
+ }
+
+ /* FIX ME: check if the value is really changed --metze */
+ if (!pdb_set_acct_ctrl(pwd, id10->acb_info, PDB_CHANGED)) {
+ pdb_free_sam(&pwd);
+ return False;
+ }
+
+ if(!pdb_update_sam_account(pwd)) {
+ pdb_free_sam(&pwd);
+ return False;
+ }
+
+ pdb_free_sam(&pwd);
+
+ return True;
+}
+
+/*******************************************************************
+ set_user_info_12
+ ********************************************************************/
+
+static BOOL set_user_info_12(SAM_USER_INFO_12 *id12, DOM_SID *sid)
+{
+ SAM_ACCOUNT *pwd = NULL;
+
+ pdb_init_sam(&pwd);
+
+ if(!pdb_getsampwsid(pwd, sid)) {
+ pdb_free_sam(&pwd);
+ return False;
+ }
+
+ if (id12 == NULL) {
+ DEBUG(2, ("set_user_info_12: id12 is NULL\n"));
+ pdb_free_sam(&pwd);
+ return False;
+ }
+
+ if (!pdb_set_lanman_passwd (pwd, id12->lm_pwd, PDB_CHANGED)) {
+ pdb_free_sam(&pwd);
+ return False;
+ }
+ if (!pdb_set_nt_passwd (pwd, id12->nt_pwd, PDB_CHANGED)) {
+ pdb_free_sam(&pwd);
+ return False;
+ }
+ if (!pdb_set_pass_changed_now (pwd)) {
+ pdb_free_sam(&pwd);
+ return False;
+ }
+
+ if(!pdb_update_sam_account(pwd)) {
+ pdb_free_sam(&pwd);
+ return False;
+ }
+
+ pdb_free_sam(&pwd);
+ return True;
+}
+
+/*******************************************************************
+ The GROUPSID field in the SAM_ACCOUNT changed. Try to tell unix.
+ ********************************************************************/
+static BOOL set_unix_primary_group(SAM_ACCOUNT *sampass)
+{
+ struct group *grp;
+ gid_t gid;
+
+ if (!NT_STATUS_IS_OK(sid_to_gid(pdb_get_group_sid(sampass),
+ &gid))) {
+ DEBUG(2,("Could not get gid for primary group of "
+ "user %s\n", pdb_get_username(sampass)));
+ return False;
+ }
+
+ grp = getgrgid(gid);
+
+ if (grp == NULL) {
+ DEBUG(2,("Could not find primary group %lu for "
+ "user %s\n", (unsigned long)gid,
+ pdb_get_username(sampass)));
+ return False;
+ }
+
+ if (smb_set_primary_group(grp->gr_name,
+ pdb_get_username(sampass)) != 0) {
+ DEBUG(2,("Could not set primary group for user %s to "
+ "%s\n",
+ pdb_get_username(sampass), grp->gr_name));
+ return False;
+ }
+
+ return True;
+}
+
+
+/*******************************************************************
+ set_user_info_20
+ ********************************************************************/
+
+static BOOL set_user_info_20(SAM_USER_INFO_20 *id20, DOM_SID *sid)
+{
+ SAM_ACCOUNT *pwd = NULL;
+
+ if (id20 == NULL) {
+ DEBUG(5, ("set_user_info_20: NULL id20\n"));
+ return False;
+ }
+
+ pdb_init_sam(&pwd);
+
+ if (!pdb_getsampwsid(pwd, sid)) {
+ pdb_free_sam(&pwd);
+ return False;
+ }
+
+ copy_id20_to_sam_passwd(pwd, id20);
+
+ /* write the change out */
+ if(!pdb_update_sam_account(pwd)) {
+ pdb_free_sam(&pwd);
+ return False;
+ }
+
+ pdb_free_sam(&pwd);
+
+ return True;
+}
+/*******************************************************************
+ set_user_info_21
+ ********************************************************************/
+
+static BOOL set_user_info_21(SAM_USER_INFO_21 *id21, DOM_SID *sid)
+{
+ SAM_ACCOUNT *pwd = NULL;
+
+ if (id21 == NULL) {
+ DEBUG(5, ("set_user_info_21: NULL id21\n"));
+ return False;
+ }
+
+ pdb_init_sam(&pwd);
+
+ if (!pdb_getsampwsid(pwd, sid)) {
+ pdb_free_sam(&pwd);
+ return False;
+ }
+
+ copy_id21_to_sam_passwd(pwd, id21);
+
+ /*
+ * The funny part about the previous two calls is
+ * that pwd still has the password hashes from the
+ * passdb entry. These have not been updated from
+ * id21. I don't know if they need to be set. --jerry
+ */
+
+ if (IS_SAM_CHANGED(pwd, PDB_GROUPSID))
+ set_unix_primary_group(pwd);
+
+ /* write the change out */
+ if(!pdb_update_sam_account(pwd)) {
+ pdb_free_sam(&pwd);
+ return False;
+ }
+
+ pdb_free_sam(&pwd);
+
+ return True;
+}
+
+/*******************************************************************
+ set_user_info_23
+ ********************************************************************/
+
+static BOOL set_user_info_23(SAM_USER_INFO_23 *id23, DOM_SID *sid)
+{
+ SAM_ACCOUNT *pwd = NULL;
+ pstring plaintext_buf;
+ uint32 len;
+ uint16 acct_ctrl;
+
+ if (id23 == NULL) {
+ DEBUG(5, ("set_user_info_23: NULL id23\n"));
+ return False;
+ }
+
+ pdb_init_sam(&pwd);
+
+ if (!pdb_getsampwsid(pwd, sid)) {
+ pdb_free_sam(&pwd);
+ return False;
+ }
+
+ DEBUG(5, ("Attempting administrator password change (level 23) for user %s\n",
+ pdb_get_username(pwd)));
+
+ acct_ctrl = pdb_get_acct_ctrl(pwd);
+
+ if (!decode_pw_buffer((char*)id23->pass, plaintext_buf, 256, &len, STR_UNICODE)) {
+ pdb_free_sam(&pwd);
+ return False;
+ }
+
+ if (!pdb_set_plaintext_passwd (pwd, plaintext_buf)) {
+ pdb_free_sam(&pwd);
+ return False;
+ }
+
+ copy_id23_to_sam_passwd(pwd, id23);
+
+ /* if it's a trust account, don't update /etc/passwd */
+ if ( ( (acct_ctrl & ACB_DOMTRUST) == ACB_DOMTRUST ) ||
+ ( (acct_ctrl & ACB_WSTRUST) == ACB_WSTRUST) ||
+ ( (acct_ctrl & ACB_SVRTRUST) == ACB_SVRTRUST) ) {
+ DEBUG(5, ("Changing trust account or non-unix-user password, not updating /etc/passwd\n"));
+ } else {
+ /* update the UNIX password */
+ if (lp_unix_password_sync() ) {
+ struct passwd *passwd = Get_Pwnam(pdb_get_username(pwd));
+ if (!passwd) {
+ DEBUG(1, ("chgpasswd: Username does not exist in system !?!\n"));
+ }
+
+ if(!chgpasswd(pdb_get_username(pwd), passwd, "", plaintext_buf, True)) {
+ pdb_free_sam(&pwd);
+ return False;
+ }
+ }
+ }
+
+ ZERO_STRUCT(plaintext_buf);
+
+ if (IS_SAM_CHANGED(pwd, PDB_GROUPSID))
+ set_unix_primary_group(pwd);
+
+ if(!pdb_update_sam_account(pwd)) {
+ pdb_free_sam(&pwd);
+ return False;
+ }
+
+ pdb_free_sam(&pwd);
+
+ return True;
+}
+
+/*******************************************************************
+ set_user_info_pw
+ ********************************************************************/
+
+static BOOL set_user_info_pw(char *pass, DOM_SID *sid)
+{
+ SAM_ACCOUNT *pwd = NULL;
+ uint32 len;
+ pstring plaintext_buf;
+ uint16 acct_ctrl;
+
+ pdb_init_sam(&pwd);
+
+ if (!pdb_getsampwsid(pwd, sid)) {
+ pdb_free_sam(&pwd);
+ return False;
+ }
+
+ DEBUG(5, ("Attempting administrator password change for user %s\n",
+ pdb_get_username(pwd)));
+
+ acct_ctrl = pdb_get_acct_ctrl(pwd);
+
+ ZERO_STRUCT(plaintext_buf);
+
+ if (!decode_pw_buffer(pass, plaintext_buf, 256, &len, STR_UNICODE)) {
+ pdb_free_sam(&pwd);
+ return False;
+ }
+
+ if (!pdb_set_plaintext_passwd (pwd, plaintext_buf)) {
+ pdb_free_sam(&pwd);
+ return False;
+ }
+
+ /* if it's a trust account, don't update /etc/passwd */
+ if ( ( (acct_ctrl & ACB_DOMTRUST) == ACB_DOMTRUST ) ||
+ ( (acct_ctrl & ACB_WSTRUST) == ACB_WSTRUST) ||
+ ( (acct_ctrl & ACB_SVRTRUST) == ACB_SVRTRUST) ) {
+ DEBUG(5, ("Changing trust account or non-unix-user password, not updating /etc/passwd\n"));
+ } else {
+ /* update the UNIX password */
+ if (lp_unix_password_sync()) {
+ struct passwd *passwd = Get_Pwnam(pdb_get_username(pwd));
+ if (!passwd) {
+ DEBUG(1, ("chgpasswd: Username does not exist in system !?!\n"));
+ }
+
+ if(!chgpasswd(pdb_get_username(pwd), passwd, "", plaintext_buf, True)) {
+ pdb_free_sam(&pwd);
+ return False;
+ }
+ }
+ }
+
+ ZERO_STRUCT(plaintext_buf);
+
+ DEBUG(5,("set_user_info_pw: pdb_update_pwd()\n"));
+
+ /* update the SAMBA password */
+ if(!pdb_update_sam_account(pwd)) {
+ pdb_free_sam(&pwd);
+ return False;
+ }
+
+ pdb_free_sam(&pwd);
+
+ return True;
+}
+
+/*******************************************************************
+ samr_reply_set_userinfo
+ ********************************************************************/
+
+NTSTATUS _samr_set_userinfo(pipes_struct *p, SAMR_Q_SET_USERINFO *q_u, SAMR_R_SET_USERINFO *r_u)
+{
+ DOM_SID sid;
+ POLICY_HND *pol = &q_u->pol;
+ uint16 switch_value = q_u->switch_value;
+ SAM_USERINFO_CTR *ctr = q_u->ctr;
+ uint32 acc_granted;
+ uint32 acc_required;
+ uint32 priv_list[3] = {SE_MACHINE_ACCOUNT, SE_ADD_USERS, SE_NONE};
+ BOOL priv_to_root = False;
+
+ DEBUG(5, ("_samr_set_userinfo: %d\n", __LINE__));
+
+ r_u->status = NT_STATUS_OK;
+
+ /* find the policy handle. open a policy on it. */
+ if (!get_lsa_policy_samr_sid(p, pol, &sid, &acc_granted))
+ return NT_STATUS_INVALID_HANDLE;
+
+ acc_required = SA_RIGHT_USER_SET_LOC_COM | SA_RIGHT_USER_SET_ATTRIBUTES; /* This is probably wrong */
+ if (!NT_STATUS_IS_OK(r_u->status = access_check_samr_function(p, acc_granted, acc_required, priv_list, "_samr_set_userinfo"))) {
+ return r_u->status;
+ }
+
+ if (geteuid() != sec_initial_uid()) {
+ SAM_ACCOUNT *pwd = NULL;
+
+ pdb_init_sam(&pwd);
+
+ become_root();
+ if (!pdb_getsampwsid(pwd, &sid)) {
+ unbecome_root();
+ pdb_free_sam(&pwd);
+ return NT_STATUS_ACCESS_DENIED;
+ }
+
+ if (NT_STATUS_IS_OK(user_has_privilege(&(p->pipe_user), SE_ADD_USERS))) {
+ priv_to_root = True;
+
+ } else if (NT_STATUS_IS_OK(user_has_privilege(&(p->pipe_user), SE_MACHINE_ACCOUNT))) {
+ if (pdb_get_acct_ctrl(pwd) & ACB_WSTRUST) {
+ priv_to_root = True;
+ }
+ } else {
+ unbecome_root();
+ return NT_STATUS_ACCESS_DENIED;
+ }
+ }
+
+ DEBUG(5, ("_samr_set_userinfo: sid:%s, level:%d\n", sid_string_static(&sid), switch_value));
+
+ if (ctr == NULL) {
+ DEBUG(5, ("_samr_set_userinfo: NULL info level\n"));
+ if (priv_to_root) unbecome_root();
+ return NT_STATUS_INVALID_INFO_CLASS;
+ }
+
+ /* ok! user info levels (lots: see MSDEV help), off we go... */
+ switch (switch_value) {
+ case 0x12:
+ if (!set_user_info_12(ctr->info.id12, &sid)) {
+ if (priv_to_root) unbecome_root();
+ return NT_STATUS_ACCESS_DENIED;
+ }
+ break;
+
+ case 24:
+ if (!p->session_key.length) {
+ if (priv_to_root) unbecome_root();
+ return NT_STATUS_NO_USER_SESSION_KEY;
+ }
+ SamOEMhashBlob(ctr->info.id24->pass, 516, &p->session_key);
+
+ dump_data(100, (char *)ctr->info.id24->pass, 516);
+
+ if (!set_user_info_pw((char *)ctr->info.id24->pass, &sid)) {
+ if (priv_to_root) unbecome_root();
+ return NT_STATUS_ACCESS_DENIED;
+ }
+ break;
+
+ case 25:
+#if 0
+ /*
+ * Currently we don't really know how to unmarshall
+ * the level 25 struct, and the password encryption
+ * is different. This is a placeholder for when we
+ * do understand it. In the meantime just return INVALID
+ * info level and W2K SP2 drops down to level 23... JRA.
+ */
+
+ if (!p->session_key.length) {
+ return NT_STATUS_NO_USER_SESSION_KEY;
+ }
+ SamOEMhashBlob(ctr->info.id25->pass, 532, &p->session_key);
+
+ dump_data(100, (char *)ctr->info.id25->pass, 532);
+
+ if (!set_user_info_pw(ctr->info.id25->pass, &sid))
+ return NT_STATUS_ACCESS_DENIED;
+ break;
+#endif
+ if (priv_to_root) unbecome_root();
+ return NT_STATUS_INVALID_INFO_CLASS;
+
+ case 23:
+ if (!p->session_key.length) {
+ if (priv_to_root) unbecome_root();
+ return NT_STATUS_NO_USER_SESSION_KEY;
+ }
+ SamOEMhashBlob(ctr->info.id23->pass, 516, &p->session_key);
+
+ dump_data(100, (char *)ctr->info.id23->pass, 516);
+
+ if (!set_user_info_23(ctr->info.id23, &sid)) {
+ if (priv_to_root) unbecome_root();
+ return NT_STATUS_ACCESS_DENIED;
+ }
+ break;
+
+ default:
+ if (priv_to_root) unbecome_root();
+ return NT_STATUS_INVALID_INFO_CLASS;
+ }
+
+ if (priv_to_root) unbecome_root();
+ return r_u->status;
+}
+
+/*******************************************************************
+ samr_reply_set_userinfo2
+ ********************************************************************/
+
+NTSTATUS _samr_set_userinfo2(pipes_struct *p, SAMR_Q_SET_USERINFO2 *q_u, SAMR_R_SET_USERINFO2 *r_u)
+{
+ DOM_SID sid;
+ SAM_USERINFO_CTR *ctr = q_u->ctr;
+ POLICY_HND *pol = &q_u->pol;
+ uint16 switch_value = q_u->switch_value;
+ uint32 acc_granted;
+ uint32 acc_required;
+ uint32 priv_list[3] = {SE_MACHINE_ACCOUNT, SE_ADD_USERS, SE_NONE};
+ BOOL priv_to_root = False;
+
+ DEBUG(5, ("samr_reply_set_userinfo2: %d\n", __LINE__));
+
+ r_u->status = NT_STATUS_OK;
+
+ /* find the policy handle. open a policy on it. */
+ if (!get_lsa_policy_samr_sid(p, pol, &sid, &acc_granted))
+ return NT_STATUS_INVALID_HANDLE;
+
+ acc_required = SA_RIGHT_USER_SET_LOC_COM | SA_RIGHT_USER_SET_ATTRIBUTES; /* This is probably wrong */
+ if (!NT_STATUS_IS_OK(r_u->status = access_check_samr_function(p, acc_granted, acc_required, priv_list, "_samr_set_userinfo2"))) {
+ return r_u->status;
+ }
+
+ if (geteuid() != sec_initial_uid()) {
+ SAM_ACCOUNT *pwd = NULL;
+
+ pdb_init_sam(&pwd);
+
+ become_root();
+ if (!pdb_getsampwsid(pwd, &sid)) {
+ unbecome_root();
+ pdb_free_sam(&pwd);
+ return NT_STATUS_ACCESS_DENIED;
+ }
+
+ if (NT_STATUS_IS_OK(user_has_privilege(&(p->pipe_user), SE_ADD_USERS))) {
+ priv_to_root = True;
+
+ } else if (NT_STATUS_IS_OK(user_has_privilege(&(p->pipe_user), SE_MACHINE_ACCOUNT))) {
+ if (pdb_get_acct_ctrl(pwd) & ACB_WSTRUST) {
+ priv_to_root = True;
+ }
+ } else {
+ unbecome_root();
+ return NT_STATUS_ACCESS_DENIED;
+ }
+ }
+
+ DEBUG(5, ("samr_reply_set_userinfo2: sid:%s\n", sid_string_static(&sid)));
+
+ if (ctr == NULL) {
+ DEBUG(5, ("samr_reply_set_userinfo2: NULL info level\n"));
+ if (priv_to_root) unbecome_root();
+ return NT_STATUS_INVALID_INFO_CLASS;
+ }
+
+ switch_value=ctr->switch_value;
+
+ /* ok! user info levels (lots: see MSDEV help), off we go... */
+ switch (switch_value) {
+ case 21:
+ if (!set_user_info_21(ctr->info.id21, &sid)) {
+ if (priv_to_root) unbecome_root();
+ return NT_STATUS_ACCESS_DENIED;
+ }
+ break;
+ case 20:
+ if (!set_user_info_20(ctr->info.id20, &sid)) {
+ if (priv_to_root) unbecome_root();
+ return NT_STATUS_ACCESS_DENIED;
+ }
+ break;
+ case 16:
+ if (!set_user_info_10(ctr->info.id10, &sid)) {
+ if (priv_to_root) unbecome_root();
+ return NT_STATUS_ACCESS_DENIED;
+ }
+ break;
+ case 18:
+ /* Used by AS/U JRA. */
+ if (!set_user_info_12(ctr->info.id12, &sid)) {
+ if (priv_to_root) unbecome_root();
+ return NT_STATUS_ACCESS_DENIED;
+ }
+ break;
+ default:
+ if (priv_to_root) unbecome_root();
+ return NT_STATUS_INVALID_INFO_CLASS;
+ }
+
+ if (priv_to_root) unbecome_root();
+ return r_u->status;
+}
+
+/*********************************************************************
+ _samr_query_aliasmem
+*********************************************************************/
+
+NTSTATUS _samr_query_useraliases(pipes_struct *p, SAMR_Q_QUERY_USERALIASES *q_u, SAMR_R_QUERY_USERALIASES *r_u)
+{
+ int num_groups = 0, tmp_num_groups=0;
+ uint32 *rids=NULL, *new_rids=NULL, *tmp_rids=NULL;
+ struct samr_info *info = NULL;
+ int i,j;
+
+ NTSTATUS ntstatus1;
+ NTSTATUS ntstatus2;
+
+ /* until i see a real useraliases query, we fack one up */
+
+ /* I have seen one, JFM 2/12/2001 */
+ /*
+ * Explanation of what this call does:
+ * for all the SID given in the request:
+ * return a list of alias (local groups)
+ * that have those SID as members.
+ *
+ * and that's the alias in the domain specified
+ * in the policy_handle
+ *
+ * if the policy handle is on an incorrect sid
+ * for example a user's sid
+ * we should reply NT_STATUS_OBJECT_TYPE_MISMATCH
+ */
+
+ r_u->status = NT_STATUS_OK;
+
+ DEBUG(5,("_samr_query_useraliases: %d\n", __LINE__));
+
+ /* find the policy handle. open a policy on it. */
+ if (!find_policy_by_hnd(p, &q_u->pol, (void **)&info))
+ return NT_STATUS_INVALID_HANDLE;
+
+ ntstatus1 = access_check_samr_function(p, info->acc_granted, SA_RIGHT_DOMAIN_LOOKUP_ALIAS_BY_MEM, NULL, "_samr_query_useraliases");
+ ntstatus2 = access_check_samr_function(p, info->acc_granted, SA_RIGHT_DOMAIN_OPEN_ACCOUNT, NULL, "_samr_query_useraliases");
+
+ if (!NT_STATUS_IS_OK(ntstatus1) || !NT_STATUS_IS_OK(ntstatus2)) {
+ if (!(NT_STATUS_EQUAL(ntstatus1,NT_STATUS_ACCESS_DENIED) && NT_STATUS_IS_OK(ntstatus2)) &&
+ !(NT_STATUS_EQUAL(ntstatus1,NT_STATUS_ACCESS_DENIED) && NT_STATUS_IS_OK(ntstatus1))) {
+ return (NT_STATUS_IS_OK(ntstatus1)) ? ntstatus2 : ntstatus1;
+ }
+ }
+
+ if (!sid_check_is_domain(&info->sid) &&
+ !sid_check_is_builtin(&info->sid))
+ return NT_STATUS_OBJECT_TYPE_MISMATCH;
+
+
+ for (i=0; i<q_u->num_sids1; i++) {
+
+ r_u->status=get_alias_user_groups(p->mem_ctx, &info->sid, &tmp_num_groups, &tmp_rids, &(q_u->sid[i].sid));
+
+ /*
+ * if there is an error, we just continue as
+ * it can be an unfound user or group
+ */
+ if (!NT_STATUS_IS_OK(r_u->status)) {
+ DEBUG(10,("_samr_query_useraliases: an error occured while getting groups\n"));
+ continue;
+ }
+
+ if (tmp_num_groups==0) {
+ DEBUG(10,("_samr_query_useraliases: no groups found\n"));
+ continue;
+ }
+
+ new_rids=(uint32 *)talloc_realloc(p->mem_ctx, rids, (num_groups+tmp_num_groups)*sizeof(uint32));
+ if (new_rids==NULL) {
+ DEBUG(0,("_samr_query_useraliases: could not realloc memory\n"));
+ return NT_STATUS_NO_MEMORY;
+ }
+ rids=new_rids;
+
+ for (j=0; j<tmp_num_groups; j++)
+ rids[j+num_groups]=tmp_rids[j];
+
+ safe_free(tmp_rids);
+
+ num_groups+=tmp_num_groups;
+ }
+
+ init_samr_r_query_useraliases(r_u, num_groups, rids, NT_STATUS_OK);
+ return NT_STATUS_OK;
+}
+
+/*********************************************************************
+ _samr_query_aliasmem
+*********************************************************************/
+
+NTSTATUS _samr_query_aliasmem(pipes_struct *p, SAMR_Q_QUERY_ALIASMEM *q_u, SAMR_R_QUERY_ALIASMEM *r_u)
+{
+ int i;
+
+ int num_sids = 0;
+ DOM_SID2 *sid;
+ DOM_SID *sids=NULL;
+
+ DOM_SID alias_sid;
+
+ uint32 acc_granted;
+
+ /* find the policy handle. open a policy on it. */
+ if (!get_lsa_policy_samr_sid(p, &q_u->alias_pol, &alias_sid, &acc_granted))
+ return NT_STATUS_INVALID_HANDLE;
+
+ if (!NT_STATUS_IS_OK(r_u->status =
+ access_check_samr_function(p, acc_granted, SA_RIGHT_ALIAS_GET_MEMBERS, NULL, "_samr_query_aliasmem"))) {
+ return r_u->status;
+ }
+
+ DEBUG(10, ("sid is %s\n", sid_string_static(&alias_sid)));
+
+ if (!pdb_enum_aliasmem(&alias_sid, &sids, &num_sids))
+ return NT_STATUS_NO_SUCH_ALIAS;
+
+ sid = (DOM_SID2 *)talloc_zero(p->mem_ctx, sizeof(DOM_SID2) * num_sids);
+ if (num_sids!=0 && sid == NULL) {
+ SAFE_FREE(sids);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ for (i = 0; i < num_sids; i++) {
+ init_dom_sid2(&sid[i], &sids[i]);
+ }
+
+ init_samr_r_query_aliasmem(r_u, num_sids, sid, NT_STATUS_OK);
+
+ SAFE_FREE(sids);
+
+ return NT_STATUS_OK;
+}
+
+static void add_uid_to_array_unique(uid_t uid, uid_t **uids, int *num)
+{
+ int i;
+
+ if ((*num) >= groups_max())
+ return;
+
+ for (i=0; i<*num; i++) {
+ if ((*uids)[i] == uid)
+ return;
+ }
+
+ *uids = Realloc(*uids, (*num+1) * sizeof(uid_t));
+
+ if (*uids == NULL)
+ return;
+
+ (*uids)[*num] = uid;
+ *num += 1;
+}
+
+
+static BOOL get_memberuids(gid_t gid, uid_t **uids, int *num)
+{
+ struct group *grp;
+ char **gr;
+ struct sys_pwent *userlist, *user;
+
+ *uids = NULL;
+ *num = 0;
+
+ /* We only look at our own sam, so don't care about imported stuff */
+
+ winbind_off();
+
+ if ((grp = getgrgid(gid)) == NULL) {
+ winbind_on();
+ return False;
+ }
+
+ /* Primary group members */
+
+ userlist = getpwent_list();
+
+ for (user = userlist; user != NULL; user = user->next) {
+ if (user->pw_gid != gid)
+ continue;
+ add_uid_to_array_unique(user->pw_uid, uids, num);
+ }
+
+ pwent_free(userlist);
+
+ /* Secondary group members */
+
+ gr = grp->gr_mem;
+ while ((*gr != NULL) && ((*gr)[0] != '\0')) {
+ struct passwd *pw = getpwnam(*gr);
+
+ if (pw == NULL)
+ continue;
+
+ add_uid_to_array_unique(pw->pw_uid, uids, num);
+
+ gr += 1;
+ }
+
+ winbind_on();
+
+ return True;
+}
+
+/*********************************************************************
+ _samr_query_groupmem
+*********************************************************************/
+
+NTSTATUS _samr_query_groupmem(pipes_struct *p, SAMR_Q_QUERY_GROUPMEM *q_u, SAMR_R_QUERY_GROUPMEM *r_u)
+{
+ int final_num_rids, i;
+ DOM_SID group_sid;
+ fstring group_sid_str;
+ uid_t *uids;
+ int num;
+ gid_t gid;
+
+ uint32 *rid=NULL;
+ uint32 *attr=NULL;
+
+ uint32 acc_granted;
+
+ /* find the policy handle. open a policy on it. */
+ if (!get_lsa_policy_samr_sid(p, &q_u->group_pol, &group_sid, &acc_granted))
+ return NT_STATUS_INVALID_HANDLE;
+
+ if (!NT_STATUS_IS_OK(r_u->status = access_check_samr_function(p, acc_granted, SA_RIGHT_GROUP_GET_MEMBERS, NULL, "_samr_query_groupmem"))) {
+ return r_u->status;
+ }
+
+ sid_to_string(group_sid_str, &group_sid);
+ DEBUG(10, ("sid is %s\n", group_sid_str));
+
+ if (!sid_check_is_in_our_domain(&group_sid)) {
+ DEBUG(3, ("sid %s is not in our domain\n", group_sid_str));
+ return NT_STATUS_NO_SUCH_GROUP;
+ }
+
+ DEBUG(10, ("lookup on Domain SID\n"));
+
+ if (!NT_STATUS_IS_OK(sid_to_gid(&group_sid, &gid)))
+ return NT_STATUS_NO_SUCH_GROUP;
+
+ if(!get_memberuids(gid, &uids, &num))
+ return NT_STATUS_NO_SUCH_GROUP;
+
+ rid=talloc_zero(p->mem_ctx, sizeof(uint32)*num);
+ attr=talloc_zero(p->mem_ctx, sizeof(uint32)*num);
+
+ if (num!=0 && (rid==NULL || attr==NULL))
+ return NT_STATUS_NO_MEMORY;
+
+ final_num_rids = 0;
+
+ for (i=0; i<num; i++) {
+ DOM_SID sid;
+
+ if (!NT_STATUS_IS_OK(uid_to_sid(&sid, uids[i]))) {
+ DEBUG(1, ("Could not map member uid to SID\n"));
+ continue;
+ }
+
+ if (!sid_check_is_in_our_domain(&sid)) {
+ DEBUG(1, ("Inconsistent SAM -- group member uid not "
+ "in our domain\n"));
+ continue;
+ }
+
+ sid_peek_rid(&sid, &rid[final_num_rids]);
+
+ /* Hmm. In a trace I got the constant 7 here from NT. */
+ attr[final_num_rids] = SID_NAME_USER;
+
+ final_num_rids += 1;
+ }
+
+ SAFE_FREE(uids);
+
+ init_samr_r_query_groupmem(r_u, final_num_rids, rid, attr,
+ NT_STATUS_OK);
+
+ return NT_STATUS_OK;
+}
+
+/*********************************************************************
+ _samr_add_aliasmem
+*********************************************************************/
+
+NTSTATUS _samr_add_aliasmem(pipes_struct *p, SAMR_Q_ADD_ALIASMEM *q_u, SAMR_R_ADD_ALIASMEM *r_u)
+{
+ DOM_SID alias_sid;
+ uint32 acc_granted;
+
+ /* Find the policy handle. Open a policy on it. */
+ if (!get_lsa_policy_samr_sid(p, &q_u->alias_pol, &alias_sid, &acc_granted))
+ return NT_STATUS_INVALID_HANDLE;
+
+ if (!NT_STATUS_IS_OK(r_u->status = access_check_samr_function(p, acc_granted, SA_RIGHT_ALIAS_ADD_MEMBER, NULL, "_samr_add_aliasmem"))) {
+ return r_u->status;
+ }
+
+ DEBUG(10, ("sid is %s\n", sid_string_static(&alias_sid)));
+
+ if (!pdb_add_aliasmem(&alias_sid, &q_u->sid.sid))
+ return NT_STATUS_ACCESS_DENIED;
+
+ return NT_STATUS_OK;
+}
+
+/*********************************************************************
+ _samr_del_aliasmem
+*********************************************************************/
+
+NTSTATUS _samr_del_aliasmem(pipes_struct *p, SAMR_Q_DEL_ALIASMEM *q_u, SAMR_R_DEL_ALIASMEM *r_u)
+{
+ DOM_SID alias_sid;
+ uint32 acc_granted;
+
+ /* Find the policy handle. Open a policy on it. */
+ if (!get_lsa_policy_samr_sid(p, &q_u->alias_pol, &alias_sid, &acc_granted))
+ return NT_STATUS_INVALID_HANDLE;
+
+ if (!NT_STATUS_IS_OK(r_u->status = access_check_samr_function(p, acc_granted, SA_RIGHT_ALIAS_REMOVE_MEMBER, NULL, "_samr_del_aliasmem"))) {
+ return r_u->status;
+ }
+
+ DEBUG(10, ("_samr_del_aliasmem:sid is %s\n",
+ sid_string_static(&alias_sid)));
+
+ if (!pdb_del_aliasmem(&alias_sid, &q_u->sid.sid))
+ return NT_STATUS_ACCESS_DENIED;
+
+ return NT_STATUS_OK;
+}
+
+/*********************************************************************
+ _samr_add_groupmem
+*********************************************************************/
+
+NTSTATUS _samr_add_groupmem(pipes_struct *p, SAMR_Q_ADD_GROUPMEM *q_u, SAMR_R_ADD_GROUPMEM *r_u)
+{
+ DOM_SID group_sid;
+ DOM_SID user_sid;
+ fstring grp_name;
+ fstring usr_name;
+ uint32 acc_granted;
+
+ /* Find the policy handle. Open a policy on it. */
+ if (!get_lsa_policy_samr_sid(p, &q_u->pol, &group_sid, &acc_granted))
+ return NT_STATUS_INVALID_HANDLE;
+
+ if (!NT_STATUS_IS_OK(r_u->status = access_check_samr_function(p, acc_granted, SA_RIGHT_GROUP_ADD_MEMBER, NULL, "_samr_add_groupmem"))) {
+ return r_u->status;
+ }
+
+ if (!sid_to_local_dom_grp_name(&group_sid, grp_name)) {
+ DEBUG(1, ("Could not find group for SID %s\n",
+ sid_string_static(&group_sid)));
+ return NT_STATUS_NO_SUCH_GROUP;
+ }
+
+ sid_copy(&user_sid, get_global_sam_sid());
+ sid_append_rid(&user_sid, q_u->rid);
+
+ if (!sid_to_local_user_name(&user_sid, usr_name)) {
+ DEBUG(1, ("Could not find user for SID %s\n",
+ sid_string_static(&user_sid)));
+ return NT_STATUS_NO_SUCH_USER;
+ }
+
+ /* if the user is already in the group */
+ if(user_in_unix_group_list(usr_name, grp_name)) {
+ return NT_STATUS_MEMBER_IN_GROUP;
+ }
+
+ /*
+ * ok, the group exist, the user exist, the user is not in the group,
+ *
+ * we can (finally) add it to the group !
+ */
+
+ smb_add_user_group(grp_name, usr_name);
+
+ /* check if the user has been added then ... */
+ if(!user_in_unix_group_list(usr_name, grp_name)) {
+ return NT_STATUS_ACCESS_DENIED;
+ }
+
+ return NT_STATUS_OK;
+}
+
+/*********************************************************************
+ _samr_del_groupmem
+*********************************************************************/
+
+NTSTATUS _samr_del_groupmem(pipes_struct *p, SAMR_Q_DEL_GROUPMEM *q_u, SAMR_R_DEL_GROUPMEM *r_u)
+{
+ DOM_SID group_sid;
+ DOM_SID user_sid;
+ fstring grp_name;
+ fstring usr_name;
+ uint32 acc_granted;
+
+ /*
+ * delete the group member named q_u->rid
+ * who is a member of the sid associated with the handle
+ * the rid is a user's rid as the group is a domain group.
+ */
+
+ /* Find the policy handle. Open a policy on it. */
+ if (!get_lsa_policy_samr_sid(p, &q_u->pol, &group_sid, &acc_granted))
+ return NT_STATUS_INVALID_HANDLE;
+
+ if (!NT_STATUS_IS_OK(r_u->status = access_check_samr_function(p, acc_granted, SA_RIGHT_GROUP_REMOVE_MEMBER, NULL, "_samr_del_groupmem"))) {
+ return r_u->status;
+ }
+
+ if (!sid_to_local_dom_grp_name(&group_sid, grp_name)) {
+ DEBUG(1, ("Could not find group for SID %s\n",
+ sid_string_static(&group_sid)));
+ return NT_STATUS_NO_SUCH_GROUP;
+ }
+
+ sid_copy(&user_sid, get_global_sam_sid());
+ sid_append_rid(&user_sid, q_u->rid);
+
+ if (!sid_to_local_user_name(&user_sid, usr_name)) {
+ DEBUG(1, ("Could not find user for SID %s\n",
+ sid_string_static(&user_sid)));
+ return NT_STATUS_NO_SUCH_USER;
+ }
+
+ /* if the user is not in the group */
+ if (!user_in_unix_group_list(usr_name, grp_name)) {
+ return NT_STATUS_MEMBER_NOT_IN_GROUP;
+ }
+
+ smb_delete_user_group(grp_name, usr_name);
+
+ /* check if the user has been removed then ... */
+ if(user_in_unix_group_list(usr_name, grp_name)) {
+ /* don't know what to reply else */
+ return NT_STATUS_ACCESS_DENIED;
+ }
+
+ return NT_STATUS_OK;
+
+}
+
+/****************************************************************************
+ Delete a UNIX user on demand.
+****************************************************************************/
+
+static int smb_delete_user(const char *unix_user)
+{
+ pstring del_script;
+ int ret;
+
+ /* try winbindd first since it is impossible to determine where
+ a user came from via NSS. Try the delete user script if this fails
+ meaning the user did not exist in winbindd's list of accounts */
+
+ if ( winbind_delete_user( unix_user ) ) {
+ DEBUG(3,("winbind_delete_user: removed user (%s)\n", unix_user));
+ return 0;
+ }
+
+
+ /* fall back to 'delete user script' */
+
+ pstrcpy(del_script, lp_deluser_script());
+ if (! *del_script)
+ return -1;
+ all_string_sub(del_script, "%u", unix_user, sizeof(pstring));
+ ret = smbrun(del_script,NULL);
+ DEBUG(3,("smb_delete_user: Running the command `%s' gave %d\n",del_script,ret));
+
+ return ret;
+}
+
+/*********************************************************************
+ _samr_delete_dom_user
+*********************************************************************/
+
+NTSTATUS _samr_delete_dom_user(pipes_struct *p, SAMR_Q_DELETE_DOM_USER *q_u, SAMR_R_DELETE_DOM_USER *r_u )
+{
+ DOM_SID user_sid;
+ SAM_ACCOUNT *sam_pass=NULL;
+ uint32 acc_granted;
+
+ DEBUG(5, ("_samr_delete_dom_user: %d\n", __LINE__));
+
+ /* Find the policy handle. Open a policy on it. */
+ if (!get_lsa_policy_samr_sid(p, &q_u->user_pol, &user_sid, &acc_granted))
+ return NT_STATUS_INVALID_HANDLE;
+
+ if (!NT_STATUS_IS_OK(r_u->status = access_check_samr_function(p, acc_granted, STD_RIGHT_DELETE_ACCESS, NULL, "_samr_delete_dom_user"))) {
+ return r_u->status;
+ }
+
+ if (!sid_check_is_in_our_domain(&user_sid))
+ return NT_STATUS_CANNOT_DELETE;
+
+ /* check if the user exists before trying to delete */
+ pdb_init_sam(&sam_pass);
+ if(!pdb_getsampwsid(sam_pass, &user_sid)) {
+ DEBUG(5,("_samr_delete_dom_user:User %s doesn't exist.\n",
+ sid_string_static(&user_sid)));
+ pdb_free_sam(&sam_pass);
+ return NT_STATUS_NO_SUCH_USER;
+ }
+
+ /* delete the unix side */
+ /*
+ * note: we don't check if the delete really happened
+ * as the script is not necessary present
+ * and maybe the sysadmin doesn't want to delete the unix side
+ */
+ smb_delete_user(pdb_get_username(sam_pass));
+
+ /* and delete the samba side */
+ if (!pdb_delete_sam_account(sam_pass)) {
+ DEBUG(5,("_samr_delete_dom_user:Failed to delete entry for user %s.\n", pdb_get_username(sam_pass)));
+ pdb_free_sam(&sam_pass);
+ return NT_STATUS_CANNOT_DELETE;
+ }
+
+ pdb_free_sam(&sam_pass);
+
+ if (!close_policy_hnd(p, &q_u->user_pol))
+ return NT_STATUS_OBJECT_NAME_INVALID;
+
+ return NT_STATUS_OK;
+}
+
+/*********************************************************************
+ _samr_delete_dom_group
+*********************************************************************/
+
+NTSTATUS _samr_delete_dom_group(pipes_struct *p, SAMR_Q_DELETE_DOM_GROUP *q_u, SAMR_R_DELETE_DOM_GROUP *r_u)
+{
+ DOM_SID group_sid;
+ fstring grp_name;
+ struct group *grp;
+ uint32 acc_granted;
+
+ DEBUG(5, ("samr_delete_dom_group: %d\n", __LINE__));
+
+ /* Find the policy handle. Open a policy on it. */
+ if (!get_lsa_policy_samr_sid(p, &q_u->group_pol, &group_sid, &acc_granted))
+ return NT_STATUS_INVALID_HANDLE;
+
+ if (!NT_STATUS_IS_OK(r_u->status = access_check_samr_function(p, acc_granted, STD_RIGHT_DELETE_ACCESS, NULL, "_samr_delete_dom_group"))) {
+ return r_u->status;
+ }
+
+ if (!sid_to_local_dom_grp_name(&group_sid, grp_name)) {
+ DEBUG(1, ("Could not find group for SID %s\n",
+ sid_string_static(&group_sid)));
+ return NT_STATUS_NO_SUCH_GROUP;
+ }
+
+ /* we can delete the UNIX group */
+ smb_delete_group(grp_name);
+
+ /* check if the group has been successfully deleted */
+ if ( (grp=getgrnam(grp_name)) != NULL)
+ return NT_STATUS_ACCESS_DENIED;
+
+ if (!close_policy_hnd(p, &q_u->group_pol))
+ return NT_STATUS_OBJECT_NAME_INVALID;
+
+ return NT_STATUS_OK;
+}
+
+/*********************************************************************
+ _samr_delete_dom_alias
+*********************************************************************/
+
+NTSTATUS _samr_delete_dom_alias(pipes_struct *p, SAMR_Q_DELETE_DOM_ALIAS *q_u, SAMR_R_DELETE_DOM_ALIAS *r_u)
+{
+ DOM_SID alias_sid;
+ uint32 acc_granted;
+
+ DEBUG(5, ("_samr_delete_dom_alias: %d\n", __LINE__));
+
+ /* Find the policy handle. Open a policy on it. */
+ if (!get_lsa_policy_samr_sid(p, &q_u->alias_pol, &alias_sid, &acc_granted))
+ return NT_STATUS_INVALID_HANDLE;
+
+ if (!NT_STATUS_IS_OK(r_u->status = access_check_samr_function(p, acc_granted, STD_RIGHT_DELETE_ACCESS, NULL, "_samr_delete_dom_alias"))) {
+ return r_u->status;
+ }
+
+ DEBUG(10, ("sid is %s\n", sid_string_static(&alias_sid)));
+
+ if (!sid_check_is_in_our_domain(&alias_sid))
+ return NT_STATUS_NO_SUCH_ALIAS;
+
+ DEBUG(10, ("lookup on Local SID\n"));
+
+ /* Have passdb delete the alias */
+ if (!pdb_delete_alias(&alias_sid))
+ return NT_STATUS_ACCESS_DENIED;
+
+ if (!close_policy_hnd(p, &q_u->alias_pol))
+ return NT_STATUS_OBJECT_NAME_INVALID;
+
+ return NT_STATUS_OK;
+}
+
+/*********************************************************************
+ _samr_create_dom_group
+*********************************************************************/
+
+NTSTATUS _samr_create_dom_group(pipes_struct *p, SAMR_Q_CREATE_DOM_GROUP *q_u, SAMR_R_CREATE_DOM_GROUP *r_u)
+{
+ DOM_SID dom_sid;
+ DOM_SID info_sid;
+ fstring name;
+ fstring sid_string;
+ struct group *grp;
+ struct samr_info *info;
+ uint32 acc_granted;
+ gid_t gid;
+
+ /* Find the policy handle. Open a policy on it. */
+ if (!get_lsa_policy_samr_sid(p, &q_u->pol, &dom_sid, &acc_granted))
+ return NT_STATUS_INVALID_HANDLE;
+
+ if (!NT_STATUS_IS_OK(r_u->status = access_check_samr_function(p, acc_granted, SA_RIGHT_DOMAIN_CREATE_GROUP, NULL, "_samr_create_dom_group"))) {
+ return r_u->status;
+ }
+
+ if (!sid_equal(&dom_sid, get_global_sam_sid()))
+ return NT_STATUS_ACCESS_DENIED;
+
+ /* TODO: check if allowed to create group and add a become_root/unbecome_root pair.*/
+
+ unistr2_to_ascii(name, &q_u->uni_acct_desc, sizeof(name)-1);
+
+ /* check if group already exist */
+ if ((grp=getgrnam(name)) != NULL)
+ return NT_STATUS_GROUP_EXISTS;
+
+ /* we can create the UNIX group */
+ if (smb_create_group(name, &gid) != 0)
+ return NT_STATUS_ACCESS_DENIED;
+
+ /* check if the group has been successfully created */
+ if ((grp=getgrgid(gid)) == NULL)
+ return NT_STATUS_ACCESS_DENIED;
+
+ r_u->rid=pdb_gid_to_group_rid(grp->gr_gid);
+
+ /* add the group to the mapping table */
+ sid_copy(&info_sid, get_global_sam_sid());
+ sid_append_rid(&info_sid, r_u->rid);
+ sid_to_string(sid_string, &info_sid);
+
+ if(!add_initial_entry(grp->gr_gid, sid_string, SID_NAME_DOM_GRP, name, NULL))
+ return NT_STATUS_ACCESS_DENIED;
+
+ if ((info = get_samr_info_by_sid(&info_sid)) == NULL)
+ return NT_STATUS_NO_MEMORY;
+
+ /* get a (unique) handle. open a policy on it. */
+ if (!create_policy_hnd(p, &r_u->pol, free_samr_info, (void *)info))
+ return NT_STATUS_OBJECT_NAME_NOT_FOUND;
+
+ return NT_STATUS_OK;
+}
+
+/*********************************************************************
+ _samr_create_dom_alias
+*********************************************************************/
+
+NTSTATUS _samr_create_dom_alias(pipes_struct *p, SAMR_Q_CREATE_DOM_ALIAS *q_u, SAMR_R_CREATE_DOM_ALIAS *r_u)
+{
+ DOM_SID dom_sid;
+ DOM_SID info_sid;
+ fstring name;
+ struct group *grp;
+ struct samr_info *info;
+ uint32 acc_granted;
+ gid_t gid;
+
+ /* Find the policy handle. Open a policy on it. */
+ if (!get_lsa_policy_samr_sid(p, &q_u->dom_pol, &dom_sid, &acc_granted))
+ return NT_STATUS_INVALID_HANDLE;
+
+ if (!NT_STATUS_IS_OK(r_u->status = access_check_samr_function(p, acc_granted, SA_RIGHT_DOMAIN_CREATE_ALIAS, NULL, "_samr_create_alias"))) {
+ return r_u->status;
+ }
+
+ if (!sid_equal(&dom_sid, get_global_sam_sid()))
+ return NT_STATUS_ACCESS_DENIED;
+
+ /* TODO: check if allowed to create group and add a become_root/unbecome_root pair.*/
+
+ unistr2_to_ascii(name, &q_u->uni_acct_desc, sizeof(name)-1);
+
+ /* Have passdb create the alias */
+ if (!pdb_create_alias(name, &r_u->rid))
+ return NT_STATUS_ACCESS_DENIED;
+
+ sid_copy(&info_sid, get_global_sam_sid());
+ sid_append_rid(&info_sid, r_u->rid);
+
+ if (!NT_STATUS_IS_OK(sid_to_gid(&info_sid, &gid)))
+ return NT_STATUS_ACCESS_DENIED;
+
+ /* check if the group has been successfully created */
+ if ((grp=getgrgid(gid)) == NULL)
+ return NT_STATUS_ACCESS_DENIED;
+
+ if ((info = get_samr_info_by_sid(&info_sid)) == NULL)
+ return NT_STATUS_NO_MEMORY;
+
+ /* get a (unique) handle. open a policy on it. */
+ if (!create_policy_hnd(p, &r_u->alias_pol, free_samr_info, (void *)info))
+ return NT_STATUS_OBJECT_NAME_NOT_FOUND;
+
+ return NT_STATUS_OK;
+}
+
+/*********************************************************************
+ _samr_query_groupinfo
+
+sends the name/comment pair of a domain group
+level 1 send also the number of users of that group
+*********************************************************************/
+
+NTSTATUS _samr_query_groupinfo(pipes_struct *p, SAMR_Q_QUERY_GROUPINFO *q_u, SAMR_R_QUERY_GROUPINFO *r_u)
+{
+ DOM_SID group_sid;
+ gid_t gid;
+ uid_t *uids;
+ int num=0;
+ GROUP_INFO_CTR *ctr;
+ uint32 acc_granted;
+ struct acct_info info;
+
+ if (!get_lsa_policy_samr_sid(p, &q_u->pol, &group_sid, &acc_granted))
+ return NT_STATUS_INVALID_HANDLE;
+
+ if (!NT_STATUS_IS_OK(r_u->status = access_check_samr_function(p, acc_granted, SA_RIGHT_GROUP_LOOKUP_INFO, NULL, "_samr_query_groupinfo"))) {
+ return r_u->status;
+ }
+
+ if (!pdb_get_dom_grp_info(&group_sid, &info))
+ return NT_STATUS_NO_SUCH_GROUP;
+
+ if (!NT_STATUS_IS_OK(sid_to_gid(&group_sid, &gid)))
+ return NT_STATUS_INVALID_HANDLE;
+
+ ctr=(GROUP_INFO_CTR *)talloc_zero(p->mem_ctx, sizeof(GROUP_INFO_CTR));
+ if (ctr==NULL)
+ return NT_STATUS_NO_MEMORY;
+
+ switch (q_u->switch_level) {
+ case 1:
+ ctr->switch_value1 = 1;
+ if(!get_memberuids(gid, &uids, &num))
+ return NT_STATUS_NO_SUCH_GROUP;
+ SAFE_FREE(uids);
+ init_samr_group_info1(&ctr->group.info1,
+ info.acct_name, info.acct_desc,
+ num);
+ break;
+ case 3:
+ ctr->switch_value1 = 3;
+ init_samr_group_info3(&ctr->group.info3);
+ break;
+ case 4:
+ ctr->switch_value1 = 4;
+ init_samr_group_info4(&ctr->group.info4,
+ info.acct_desc);
+ break;
+ default:
+ return NT_STATUS_INVALID_INFO_CLASS;
+ }
+
+ init_samr_r_query_groupinfo(r_u, ctr, NT_STATUS_OK);
+
+ return NT_STATUS_OK;
+}
+
+/*********************************************************************
+ _samr_set_groupinfo
+
+ update a domain group's comment.
+*********************************************************************/
+
+NTSTATUS _samr_set_groupinfo(pipes_struct *p, SAMR_Q_SET_GROUPINFO *q_u, SAMR_R_SET_GROUPINFO *r_u)
+{
+ DOM_SID group_sid;
+ GROUP_INFO_CTR *ctr;
+ uint32 acc_granted;
+ struct acct_info info;
+
+ if (!get_lsa_policy_samr_sid(p, &q_u->pol, &group_sid, &acc_granted))
+ return NT_STATUS_INVALID_HANDLE;
+
+ if (!NT_STATUS_IS_OK(r_u->status = access_check_samr_function(p, acc_granted, SA_RIGHT_GROUP_SET_INFO, NULL, "_samr_set_groupinfo"))) {
+ return r_u->status;
+ }
+
+ if (!pdb_get_dom_grp_info(&group_sid, &info))
+ return NT_STATUS_INVALID_HANDLE;
+
+ ctr=q_u->ctr;
+
+ switch (ctr->switch_value1) {
+ case 1:
+ unistr2_to_ascii(info.acct_desc,
+ &(ctr->group.info1.uni_acct_desc),
+ sizeof(info.acct_desc)-1);
+ break;
+ case 4:
+ unistr2_to_ascii(info.acct_desc,
+ &(ctr->group.info4.uni_acct_desc),
+ sizeof(info.acct_desc)-1);
+ break;
+ default:
+ return NT_STATUS_INVALID_INFO_CLASS;
+ }
+
+ if (!pdb_set_dom_grp_info(&group_sid, &info))
+ return NT_STATUS_ACCESS_DENIED;
+
+ return NT_STATUS_OK;
+}
+
+/*********************************************************************
+ _samr_set_aliasinfo
+
+ update an alias's comment.
+*********************************************************************/
+
+NTSTATUS _samr_set_aliasinfo(pipes_struct *p, SAMR_Q_SET_ALIASINFO *q_u, SAMR_R_SET_ALIASINFO *r_u)
+{
+ DOM_SID group_sid;
+ struct acct_info info;
+ ALIAS_INFO_CTR *ctr;
+ uint32 acc_granted;
+
+ if (!get_lsa_policy_samr_sid(p, &q_u->alias_pol, &group_sid, &acc_granted))
+ return NT_STATUS_INVALID_HANDLE;
+
+ if (!NT_STATUS_IS_OK(r_u->status = access_check_samr_function(p, acc_granted, SA_RIGHT_ALIAS_SET_INFO, NULL, "_samr_set_aliasinfo"))) {
+ return r_u->status;
+ }
+
+ ctr=&q_u->ctr;
+
+ switch (ctr->switch_value1) {
+ case 3:
+ unistr2_to_ascii(info.acct_desc,
+ &(ctr->alias.info3.uni_acct_desc),
+ sizeof(info.acct_desc)-1);
+ break;
+ default:
+ return NT_STATUS_INVALID_INFO_CLASS;
+ }
+
+ if(!pdb_set_aliasinfo(&group_sid, &info)) {
+ return NT_STATUS_ACCESS_DENIED;
+ }
+
+ return NT_STATUS_OK;
+}
+
+/*********************************************************************
+ _samr_get_dom_pwinfo
+*********************************************************************/
+
+NTSTATUS _samr_get_dom_pwinfo(pipes_struct *p, SAMR_Q_GET_DOM_PWINFO *q_u, SAMR_R_GET_DOM_PWINFO *r_u)
+{
+ /* Perform access check. Since this rpc does not require a
+ policy handle it will not be caught by the access checks on
+ SAMR_CONNECT or SAMR_CONNECT_ANON. */
+
+ if (!pipe_access_check(p)) {
+ DEBUG(3, ("access denied to samr_get_dom_pwinfo\n"));
+ r_u->status = NT_STATUS_ACCESS_DENIED;
+ return r_u->status;
+ }
+
+ /* Actually, returning zeros here works quite well :-). */
+
+ return NT_STATUS_OK;
+}
+
+/*********************************************************************
+ _samr_open_group
+*********************************************************************/
+
+NTSTATUS _samr_open_group(pipes_struct *p, SAMR_Q_OPEN_GROUP *q_u, SAMR_R_OPEN_GROUP *r_u)
+{
+ DOM_SID sid;
+ DOM_SID info_sid;
+ fstring grp_name;
+ struct samr_info *info;
+ SEC_DESC *psd = NULL;
+ uint32 acc_granted;
+ uint32 des_access = q_u->access_mask;
+ size_t sd_size;
+ NTSTATUS status;
+ fstring sid_string;
+
+ if (!get_lsa_policy_samr_sid(p, &q_u->domain_pol, &sid, &acc_granted))
+ return NT_STATUS_INVALID_HANDLE;
+
+ if (!NT_STATUS_IS_OK(status = access_check_samr_function(p, acc_granted, SA_RIGHT_DOMAIN_OPEN_ACCOUNT, NULL, "_samr_open_group"))) {
+ return status;
+ }
+
+ /*check if access can be granted as requested by client. */
+ samr_make_grp_obj_sd(p->mem_ctx, &psd, &sd_size);
+ se_map_generic(&des_access,&grp_generic_mapping);
+ if (!NT_STATUS_IS_OK(status =
+ access_check_samr_object(psd, p, des_access, &acc_granted,
+ NULL, "_samr_open_group"))) {
+ return status;
+ }
+
+
+ /* this should not be hard-coded like this */
+ if (!sid_equal(&sid, get_global_sam_sid()))
+ return NT_STATUS_ACCESS_DENIED;
+
+ sid_copy(&info_sid, get_global_sam_sid());
+ sid_append_rid(&info_sid, q_u->rid_group);
+ sid_to_string(sid_string, &info_sid);
+
+ if ((info = get_samr_info_by_sid(&info_sid)) == NULL)
+ return NT_STATUS_NO_MEMORY;
+
+ info->acc_granted = acc_granted;
+
+ DEBUG(10, ("_samr_open_group:Opening SID: %s\n", sid_string));
+
+ if (!sid_to_local_dom_grp_name(&info->sid, grp_name))
+ return NT_STATUS_NO_SUCH_GROUP;
+
+ /* get a (unique) handle. open a policy on it. */
+ if (!create_policy_hnd(p, &r_u->pol, free_samr_info, (void *)info))
+ return NT_STATUS_OBJECT_NAME_NOT_FOUND;
+
+ return NT_STATUS_OK;
+}
+
+/*********************************************************************
+ _samr_remove_sid_foreign_domain
+*********************************************************************/
+
+NTSTATUS _samr_remove_sid_foreign_domain(pipes_struct *p,
+ SAMR_Q_REMOVE_SID_FOREIGN_DOMAIN *q_u,
+ SAMR_R_REMOVE_SID_FOREIGN_DOMAIN *r_u)
+{
+ DOM_SID delete_sid, alias_sid;
+ SAM_ACCOUNT *sam_pass=NULL;
+ uint32 acc_granted;
+ GROUP_MAP map;
+ BOOL is_user = False;
+ NTSTATUS result;
+ enum SID_NAME_USE type = SID_NAME_UNKNOWN;
+
+ sid_copy( &delete_sid, &q_u->sid.sid );
+
+ DEBUG(5,("_samr_remove_sid_foreign_domain: removing SID [%s]\n",
+ sid_string_static(&delete_sid)));
+
+ /* Find the policy handle. Open a policy on it. */
+
+ if (!get_lsa_policy_samr_sid(p, &q_u->dom_pol, &alias_sid, &acc_granted))
+ return NT_STATUS_INVALID_HANDLE;
+
+ result = access_check_samr_function(p, acc_granted, STD_RIGHT_DELETE_ACCESS,
+ NULL, "_samr_remove_sid_foreign_domain");
+
+ if (!NT_STATUS_IS_OK(result))
+ return result;
+
+ DEBUG(8, ("_samr_remove_sid_foreign_domain:sid is %s\n",
+ sid_string_static(&alias_sid)));
+
+ /* make sure we can handle this */
+
+ if ( sid_check_is_domain(&alias_sid) )
+ type = SID_NAME_DOM_GRP;
+ else if ( sid_check_is_builtin(&alias_sid) )
+ type = SID_NAME_ALIAS;
+
+ if ( type == SID_NAME_UNKNOWN ) {
+ DEBUG(10, ("_samr_remove_sid_foreign_domain: can't operate on what we don't own!\n"));
+ return NT_STATUS_OK;
+ }
+
+ /* check if the user exists before trying to delete */
+
+ pdb_init_sam(&sam_pass);
+
+ if ( pdb_getsampwsid(sam_pass, &delete_sid) ) {
+ is_user = True;
+ } else {
+ /* maybe it is a group */
+ if( !pdb_getgrsid(&map, delete_sid) ) {
+ DEBUG(3,("_samr_remove_sid_foreign_domain: %s is not a user or a group!\n",
+ sid_string_static(&delete_sid)));
+ result = NT_STATUS_INVALID_SID;
+ goto done;
+ }
+ }
+
+ /* we can only delete a user from a group since we don't have
+ nested groups anyways. So in the latter case, just say OK */
+
+ if ( is_user ) {
+ GROUP_MAP *mappings = NULL;
+ int num_groups, i;
+ struct group *grp2;
+
+ if ( pdb_enum_group_mapping(type, &mappings, &num_groups, False) && num_groups>0 ) {
+
+ /* interate over the groups */
+ for ( i=0; i<num_groups; i++ ) {
+
+ grp2 = getgrgid(mappings[i].gid);
+
+ if ( !grp2 ) {
+ DEBUG(0,("_samr_remove_sid_foreign_domain: group mapping without UNIX group!\n"));
+ continue;
+ }
+
+ if ( !user_in_unix_group_list(pdb_get_username(sam_pass), grp2->gr_name) )
+ continue;
+
+ smb_delete_user_group(grp2->gr_name, pdb_get_username(sam_pass));
+
+ if ( user_in_unix_group_list(pdb_get_username(sam_pass), grp2->gr_name) ) {
+ /* should we fail here ? */
+ DEBUG(0,("_samr_remove_sid_foreign_domain: Delete user [%s] from group [%s] failed!\n",
+ pdb_get_username(sam_pass), grp2->gr_name ));
+ continue;
+ }
+
+ DEBUG(10,("_samr_remove_sid_foreign_domain: Removed user [%s] from group [%s]!\n",
+ pdb_get_username(sam_pass), grp2->gr_name ));
+ }
+
+ SAFE_FREE(mappings);
+ }
+ }
+
+ result = NT_STATUS_OK;
+done:
+
+ pdb_free_sam(&sam_pass);
+
+ return result;
+}
+
+/*******************************************************************
+ _samr_unknown_2e
+ ********************************************************************/
+
+NTSTATUS _samr_unknown_2e(pipes_struct *p, SAMR_Q_UNKNOWN_2E *q_u, SAMR_R_UNKNOWN_2E *r_u)
+{
+ struct samr_info *info = NULL;
+ SAM_UNK_CTR *ctr;
+ uint32 min_pass_len,pass_hist,flag;
+ time_t u_expire, u_min_age;
+ NTTIME nt_expire, nt_min_age;
+
+ time_t u_lock_duration, u_reset_time;
+ NTTIME nt_lock_duration, nt_reset_time;
+ uint32 lockout;
+
+ time_t u_logout;
+ NTTIME nt_logout;
+
+ uint32 num_users=0, num_groups=0, num_aliases=0;
+
+ uint32 account_policy_temp;
+
+ if ((ctr = (SAM_UNK_CTR *)talloc_zero(p->mem_ctx, sizeof(SAM_UNK_CTR))) == NULL)
+ return NT_STATUS_NO_MEMORY;
+
+ ZERO_STRUCTP(ctr);
+
+ r_u->status = NT_STATUS_OK;
+
+ DEBUG(5,("_samr_unknown_2e: %d\n", __LINE__));
+
+ /* find the policy handle. open a policy on it. */
+ if (!find_policy_by_hnd(p, &q_u->domain_pol, (void **)&info))
+ return NT_STATUS_INVALID_HANDLE;
+
+ switch (q_u->switch_value) {
+ case 0x01:
+ account_policy_get(AP_MIN_PASSWORD_LEN, &account_policy_temp);
+ min_pass_len = account_policy_temp;
+
+ account_policy_get(AP_PASSWORD_HISTORY, &account_policy_temp);
+ pass_hist = account_policy_temp;
+
+ account_policy_get(AP_USER_MUST_LOGON_TO_CHG_PASS, &account_policy_temp);
+ flag = account_policy_temp;
+
+ account_policy_get(AP_MAX_PASSWORD_AGE, &account_policy_temp);
+ u_expire = account_policy_temp;
+
+ account_policy_get(AP_MIN_PASSWORD_AGE, &account_policy_temp);
+ u_min_age = account_policy_temp;
+
+ unix_to_nt_time_abs(&nt_expire, u_expire);
+ unix_to_nt_time_abs(&nt_min_age, u_min_age);
+
+ init_unk_info1(&ctr->info.inf1, (uint16)min_pass_len, (uint16)pass_hist,
+ flag, nt_expire, nt_min_age);
+ break;
+ case 0x02:
+ become_root();
+ r_u->status=load_sampwd_entries(info, ACB_NORMAL, False);
+ unbecome_root();
+ if (!NT_STATUS_IS_OK(r_u->status)) {
+ DEBUG(5, ("_samr_unknown_2e: load_sampwd_entries failed\n"));
+ return r_u->status;
+ }
+ num_users=info->disp_info.num_user_account;
+ free_samr_db(info);
+
+ r_u->status=load_group_domain_entries(info, get_global_sam_sid());
+ if (NT_STATUS_IS_ERR(r_u->status)) {
+ DEBUG(5, ("_samr_unknown_2e: load_group_domain_entries failed\n"));
+ return r_u->status;
+ }
+ num_groups=info->disp_info.num_group_account;
+ free_samr_db(info);
+
+ /* The time call below is to get a sequence number for the sam. FIXME !!! JRA. */
+ init_unk_info2(&ctr->info.inf2, lp_workgroup(), global_myname(), (uint32) time(NULL),
+ num_users, num_groups, num_aliases);
+ break;
+ case 0x03:
+ account_policy_get(AP_TIME_TO_LOGOUT, &account_policy_temp);
+ u_logout = account_policy_temp;
+
+ unix_to_nt_time_abs(&nt_logout, u_logout);
+
+ init_unk_info3(&ctr->info.inf3, nt_logout);
+ break;
+ case 0x05:
+ init_unk_info5(&ctr->info.inf5, global_myname());
+ break;
+ case 0x06:
+ init_unk_info6(&ctr->info.inf6);
+ break;
+ case 0x07:
+ init_unk_info7(&ctr->info.inf7);
+ break;
+ case 0x0c:
+ account_policy_get(AP_LOCK_ACCOUNT_DURATION, &account_policy_temp);
+ u_lock_duration = account_policy_temp * 60;
+
+ account_policy_get(AP_RESET_COUNT_TIME, &account_policy_temp);
+ u_reset_time = account_policy_temp * 60;
+
+ account_policy_get(AP_BAD_ATTEMPT_LOCKOUT, &account_policy_temp);
+ lockout = account_policy_temp;
+
+ unix_to_nt_time_abs(&nt_lock_duration, u_lock_duration);
+ unix_to_nt_time_abs(&nt_reset_time, u_reset_time);
+
+ init_unk_info12(&ctr->info.inf12, nt_lock_duration, nt_reset_time, (uint16)lockout);
+ break;
+ default:
+ return NT_STATUS_INVALID_INFO_CLASS;
+ }
+
+ init_samr_r_samr_unknown_2e(r_u, q_u->switch_value, ctr, NT_STATUS_OK);
+
+ DEBUG(5,("_samr_unknown_2e: %d\n", __LINE__));
+
+ return r_u->status;
+}
+
+/*******************************************************************
+ _samr_
+ ********************************************************************/
+
+NTSTATUS _samr_set_dom_info(pipes_struct *p, SAMR_Q_SET_DOMAIN_INFO *q_u, SAMR_R_SET_DOMAIN_INFO *r_u)
+{
+ time_t u_expire, u_min_age;
+ time_t u_logout;
+ time_t u_lock_duration, u_reset_time;
+
+ r_u->status = NT_STATUS_OK;
+
+ DEBUG(5,("_samr_set_dom_info: %d\n", __LINE__));
+
+ /* find the policy handle. open a policy on it. */
+ if (!find_policy_by_hnd(p, &q_u->domain_pol, NULL))
+ return NT_STATUS_INVALID_HANDLE;
+
+ DEBUG(5,("_samr_set_dom_info: switch_value: %d\n", q_u->switch_value));
+
+ switch (q_u->switch_value) {
+ case 0x01:
+ u_expire=nt_time_to_unix_abs(&q_u->ctr->info.inf1.expire);
+ u_min_age=nt_time_to_unix_abs(&q_u->ctr->info.inf1.min_passwordage);
+
+ account_policy_set(AP_MIN_PASSWORD_LEN, (uint32)q_u->ctr->info.inf1.min_length_password);
+ account_policy_set(AP_PASSWORD_HISTORY, (uint32)q_u->ctr->info.inf1.password_history);
+ account_policy_set(AP_USER_MUST_LOGON_TO_CHG_PASS, (uint32)q_u->ctr->info.inf1.flag);
+ account_policy_set(AP_MAX_PASSWORD_AGE, (int)u_expire);
+ account_policy_set(AP_MIN_PASSWORD_AGE, (int)u_min_age);
+ break;
+ case 0x02:
+ break;
+ case 0x03:
+ u_logout=nt_time_to_unix_abs(&q_u->ctr->info.inf3.logout);
+ account_policy_set(AP_TIME_TO_LOGOUT, (int)u_logout);
+ break;
+ case 0x05:
+ break;
+ case 0x06:
+ break;
+ case 0x07:
+ break;
+ case 0x0c:
+ u_lock_duration=nt_time_to_unix_abs(&q_u->ctr->info.inf12.duration)/60;
+ u_reset_time=nt_time_to_unix_abs(&q_u->ctr->info.inf12.reset_count)/60;
+
+ account_policy_set(AP_LOCK_ACCOUNT_DURATION, (int)u_lock_duration);
+ account_policy_set(AP_RESET_COUNT_TIME, (int)u_reset_time);
+ account_policy_set(AP_BAD_ATTEMPT_LOCKOUT, (uint32)q_u->ctr->info.inf12.bad_attempt_lockout);
+ break;
+ default:
+ return NT_STATUS_INVALID_INFO_CLASS;
+ }
+
+ init_samr_r_set_domain_info(r_u, NT_STATUS_OK);
+
+ DEBUG(5,("_samr_set_dom_info: %d\n", __LINE__));
+
+ return r_u->status;
+}
diff --git a/source/rpc_server/srv_samr_util.c b/source/rpc_server/srv_samr_util.c
new file mode 100644
index 00000000000..ae0fe84e029
--- /dev/null
+++ b/source/rpc_server/srv_samr_util.c
@@ -0,0 +1,554 @@
+/*
+ Unix SMB/CIFS implementation.
+ SAMR Pipe utility functions.
+
+ Copyright (C) Luke Kenneth Casson Leighton 1996-1998
+ Copyright (C) Gerald (Jerry) Carter 2000-2001
+ Copyright (C) Andrew Bartlett 2001-2002
+ Copyright (C) Stefan (metze) Metzmacher 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 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"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_RPC_SRV
+
+#define STRING_CHANGED (old_string && !new_string) ||\
+ (!old_string && new_string) ||\
+ (old_string && new_string && (strcmp(old_string, new_string) != 0))
+
+#define STRING_CHANGED_NC(s1,s2) ((s1) && !(s2)) ||\
+ (!(s1) && (s2)) ||\
+ ((s1) && (s2) && (strcmp((s1), (s2)) != 0))
+
+/*************************************************************
+ Copies a SAM_USER_INFO_20 to a SAM_ACCOUNT
+**************************************************************/
+
+void copy_id20_to_sam_passwd(SAM_ACCOUNT *to, SAM_USER_INFO_20 *from)
+{
+ const char *old_string;
+ char *new_string;
+ DATA_BLOB mung;
+
+ if (from == NULL || to == NULL)
+ return;
+
+ if (from->hdr_munged_dial.buffer) {
+ old_string = pdb_get_munged_dial(to);
+ mung.length = from->hdr_munged_dial.uni_str_len;
+ mung.data = (uint8 *) from->uni_munged_dial.buffer;
+ new_string = base64_encode_data_blob(mung);
+ DEBUG(10,("INFO_20 UNI_MUNGED_DIAL: %s -> %s\n",old_string, new_string));
+ if (STRING_CHANGED_NC(old_string,new_string))
+ pdb_set_munged_dial(to , new_string, PDB_CHANGED);
+
+ SAFE_FREE(new_string);
+ }
+}
+
+/*************************************************************
+ Copies a SAM_USER_INFO_21 to a SAM_ACCOUNT
+**************************************************************/
+
+void copy_id21_to_sam_passwd(SAM_ACCOUNT *to, SAM_USER_INFO_21 *from)
+{
+ time_t unix_time, stored_time;
+ const char *old_string, *new_string;
+ DATA_BLOB mung;
+
+ if (from == NULL || to == NULL)
+ return;
+
+ if (from->fields_present & ACCT_LAST_LOGON) {
+ unix_time=nt_time_to_unix(&from->logon_time);
+ stored_time = pdb_get_logon_time(to);
+ DEBUG(10,("INFO_21 LOGON_TIME: %lu -> %lu\n",(long unsigned int)stored_time, (long unsigned int)unix_time));
+ if (stored_time != unix_time)
+ pdb_set_logon_time(to, unix_time, PDB_CHANGED);
+ }
+
+ if (from->fields_present & ACCT_LAST_LOGOFF) {
+ unix_time=nt_time_to_unix(&from->logoff_time);
+ stored_time = pdb_get_logoff_time(to);
+ DEBUG(10,("INFO_21 LOGOFF_TIME: %lu -> %lu\n",(long unsigned int)stored_time, (long unsigned int)unix_time));
+ if (stored_time != unix_time)
+ pdb_set_logoff_time(to, unix_time, PDB_CHANGED);
+ }
+
+ if (from->fields_present & ACCT_EXPIRY) {
+ unix_time=nt_time_to_unix(&from->kickoff_time);
+ stored_time = pdb_get_kickoff_time(to);
+ DEBUG(10,("INFO_21 KICKOFF_TIME: %lu -> %lu\n",(long unsigned int)stored_time, (long unsigned int)unix_time));
+ if (stored_time != unix_time)
+ pdb_set_kickoff_time(to, unix_time , PDB_CHANGED);
+ }
+
+ if (from->fields_present & ACCT_ALLOW_PWD_CHANGE) {
+ unix_time=nt_time_to_unix(&from->pass_can_change_time);
+ stored_time = pdb_get_pass_can_change_time(to);
+ DEBUG(10,("INFO_21 PASS_CAN_CH: %lu -> %lu\n",(long unsigned int)stored_time, (long unsigned int)unix_time));
+ if (stored_time != unix_time)
+ pdb_set_pass_can_change_time(to, unix_time, PDB_CHANGED);
+ }
+
+ if (from->fields_present & ACCT_LAST_PWD_CHANGE) {
+ unix_time=nt_time_to_unix(&from->pass_last_set_time);
+ stored_time = pdb_get_pass_last_set_time(to);
+ DEBUG(10,("INFO_21 PASS_LAST_SET: %lu -> %lu\n",(long unsigned int)stored_time, (long unsigned int)unix_time));
+ if (stored_time != unix_time)
+ pdb_set_pass_last_set_time(to, unix_time, PDB_CHANGED);
+ }
+
+ if (from->fields_present & ACCT_FORCE_PWD_CHANGE) {
+ unix_time=nt_time_to_unix(&from->pass_must_change_time);
+ stored_time=pdb_get_pass_must_change_time(to);
+ DEBUG(10,("INFO_21 PASS_MUST_CH: %lu -> %lu\n",(long unsigned int)stored_time, (long unsigned int)unix_time));
+ if (stored_time != unix_time)
+ pdb_set_pass_must_change_time(to, unix_time, PDB_CHANGED);
+ }
+
+ if ((from->fields_present & ACCT_USERNAME) &&
+ (from->hdr_user_name.buffer)) {
+ old_string = pdb_get_username(to);
+ new_string = unistr2_static(&from->uni_user_name);
+ DEBUG(10,("INFO_21 UNI_USER_NAME: %s -> %s\n", old_string, new_string));
+ if (STRING_CHANGED)
+ pdb_set_username(to , new_string, PDB_CHANGED);
+ }
+
+ if ((from->fields_present & ACCT_FULL_NAME) &&
+ (from->hdr_full_name.buffer)) {
+ old_string = pdb_get_fullname(to);
+ new_string = unistr2_static(&from->uni_full_name);
+ DEBUG(10,("INFO_21 UNI_FULL_NAME: %s -> %s\n",old_string, new_string));
+ if (STRING_CHANGED)
+ pdb_set_fullname(to , new_string, PDB_CHANGED);
+ }
+
+ if ((from->fields_present & ACCT_HOME_DIR) &&
+ (from->hdr_home_dir.buffer)) {
+ old_string = pdb_get_homedir(to);
+ new_string = unistr2_static(&from->uni_home_dir);
+ DEBUG(10,("INFO_21 UNI_HOME_DIR: %s -> %s\n",old_string,new_string));
+ if (STRING_CHANGED)
+ pdb_set_homedir(to , new_string, PDB_CHANGED);
+ }
+
+ if ((from->fields_present & ACCT_HOME_DRIVE) &&
+ (from->hdr_dir_drive.buffer)) {
+ old_string = pdb_get_dir_drive(to);
+ new_string = unistr2_static(&from->uni_dir_drive);
+ DEBUG(10,("INFO_21 UNI_DIR_DRIVE: %s -> %s\n",old_string,new_string));
+ if (STRING_CHANGED)
+ pdb_set_dir_drive(to , new_string, PDB_CHANGED);
+ }
+
+ if ((from->fields_present & ACCT_LOGON_SCRIPT) &&
+ (from->hdr_logon_script.buffer)) {
+ old_string = pdb_get_logon_script(to);
+ new_string = unistr2_static(&from->uni_logon_script);
+ DEBUG(10,("INFO_21 UNI_LOGON_SCRIPT: %s -> %s\n",old_string,new_string));
+ if (STRING_CHANGED)
+ pdb_set_logon_script(to , new_string, PDB_CHANGED);
+ }
+
+ if ((from->fields_present & ACCT_PROFILE) &&
+ (from->hdr_profile_path.buffer)) {
+ old_string = pdb_get_profile_path(to);
+ new_string = unistr2_static(&from->uni_profile_path);
+ DEBUG(10,("INFO_21 UNI_PROFILE_PATH: %s -> %s\n",old_string, new_string));
+ if (STRING_CHANGED)
+ pdb_set_profile_path(to , new_string, PDB_CHANGED);
+ }
+
+ if ((from->fields_present & ACCT_DESCRIPTION) &&
+ (from->hdr_acct_desc.buffer)) {
+ old_string = pdb_get_acct_desc(to);
+ new_string = unistr2_static(&from->uni_acct_desc);
+ DEBUG(10,("INFO_21 UNI_ACCT_DESC: %s -> %s\n",old_string,new_string));
+ if (STRING_CHANGED)
+ pdb_set_acct_desc(to , new_string, PDB_CHANGED);
+ }
+
+ if ((from->fields_present & ACCT_WORKSTATIONS) &&
+ (from->hdr_workstations.buffer)) {
+ old_string = pdb_get_workstations(to);
+ new_string = unistr2_static(&from->uni_workstations);
+ DEBUG(10,("INFO_21 UNI_WORKSTATIONS: %s -> %s\n",old_string, new_string));
+ if (STRING_CHANGED)
+ pdb_set_workstations(to , new_string, PDB_CHANGED);
+ }
+
+ /* is this right? */
+ if ((from->fields_present & ACCT_ADMIN_DESC) &&
+ (from->hdr_unknown_str.buffer)) {
+ old_string = pdb_get_unknown_str(to);
+ new_string = unistr2_static(&from->uni_unknown_str);
+ DEBUG(10,("INFO_21 UNI_UNKNOWN_STR: %s -> %s\n",old_string, new_string));
+ if (STRING_CHANGED)
+ pdb_set_unknown_str(to , new_string, PDB_CHANGED);
+ }
+
+ if ((from->fields_present & ACCT_CALLBACK) &&
+ (from->hdr_munged_dial.buffer)) {
+ char *newstr;
+ old_string = pdb_get_munged_dial(to);
+ mung.length = from->hdr_munged_dial.uni_str_len;
+ mung.data = (uint8 *) from->uni_munged_dial.buffer;
+ newstr = base64_encode_data_blob(mung);
+ DEBUG(10,("INFO_21 UNI_MUNGED_DIAL: %s -> %s\n",old_string, newstr));
+ if (STRING_CHANGED_NC(old_string,newstr))
+ pdb_set_munged_dial(to , newstr, PDB_CHANGED);
+
+ SAFE_FREE(newstr);
+ }
+
+ if (from->fields_present & ACCT_RID) {
+ if (from->user_rid == 0) {
+ DEBUG(10, ("INFO_21: Asked to set User RID to 0 !? Skipping change!\n"));
+ } else if (from->user_rid != pdb_get_user_rid(to)) {
+ DEBUG(10,("INFO_21 USER_RID: %u -> %u NOT UPDATED!\n",pdb_get_user_rid(to),from->user_rid));
+ }
+ }
+
+ if (from->fields_present & ACCT_PRIMARY_GID) {
+ if (from->group_rid == 0) {
+ DEBUG(10, ("INFO_21: Asked to set Group RID to 0 !? Skipping change!\n"));
+ } else if (from->group_rid != pdb_get_group_rid(to)) {
+ DEBUG(10,("INFO_21 GROUP_RID: %u -> %u\n",pdb_get_group_rid(to),from->group_rid));
+ pdb_set_group_sid_from_rid(to, from->group_rid, PDB_CHANGED);
+ }
+ }
+
+ if (from->fields_present & ACCT_FLAGS) {
+ DEBUG(10,("INFO_21 ACCT_CTRL: %08X -> %08X\n",pdb_get_acct_ctrl(to),from->acb_info));
+ if (from->acb_info != pdb_get_acct_ctrl(to)) {
+ pdb_set_acct_ctrl(to, from->acb_info, PDB_CHANGED);
+ }
+ }
+
+ if (from->fields_present & ACCT_LOGON_HOURS) {
+ DEBUG(15,("INFO_21 LOGON_DIVS: %08X -> %08X\n",pdb_get_logon_divs(to),from->logon_divs));
+ if (from->logon_divs != pdb_get_logon_divs(to)) {
+ pdb_set_logon_divs(to, from->logon_divs, PDB_CHANGED);
+ }
+
+ DEBUG(15,("INFO_21 LOGON_HRS.LEN: %08X -> %08X\n",pdb_get_hours_len(to),from->logon_hrs.len));
+ if (from->logon_hrs.len != pdb_get_hours_len(to)) {
+ pdb_set_hours_len(to, from->logon_hrs.len, PDB_CHANGED);
+ }
+
+ DEBUG(15,("INFO_21 LOGON_HRS.HOURS: %s -> %s\n",pdb_get_hours(to),from->logon_hrs.hours));
+ /* Fix me: only update if it changes --metze */
+ pdb_set_hours(to, from->logon_hrs.hours, PDB_CHANGED);
+
+ /* This is max logon hours */
+ DEBUG(10,("INFO_21 UNKNOWN_6: %08X -> %08X\n",pdb_get_unknown_6(to),from->unknown_6));
+ if (from->unknown_6 != pdb_get_unknown_6(to)) {
+ pdb_set_unknown_6(to, from->unknown_6, PDB_CHANGED);
+ }
+ }
+
+ if (from->fields_present & ACCT_BAD_PWD_COUNT) {
+ DEBUG(10,("INFO_21 BAD_PASSWORD_COUNT: %08X -> %08X\n",pdb_get_bad_password_count(to),from->bad_password_count));
+ if (from->bad_password_count != pdb_get_bad_password_count(to)) {
+ pdb_set_bad_password_count(to, from->bad_password_count, PDB_CHANGED);
+ }
+ }
+
+ if (from->fields_present & ACCT_NUM_LOGONS) {
+ DEBUG(10,("INFO_21 LOGON_COUNT: %08X -> %08X\n",pdb_get_logon_count(to),from->logon_count));
+ if (from->logon_count != pdb_get_logon_count(to)) {
+ pdb_set_logon_count(to, from->logon_count, PDB_CHANGED);
+ }
+ }
+
+ DEBUG(10,("INFO_21 PASS_MUST_CHANGE_AT_NEXT_LOGON: %02X\n",from->passmustchange));
+ if (from->passmustchange==PASS_MUST_CHANGE_AT_NEXT_LOGON) {
+ pdb_set_pass_must_change_time(to,0, PDB_CHANGED);
+ } else {
+ uint32 expire;
+ time_t new_time;
+ if (pdb_get_pass_must_change_time(to) == 0) {
+ if (!account_policy_get(AP_MAX_PASSWORD_AGE, &expire)
+ || expire == (uint32)-1) {
+ new_time = get_time_t_max();
+ } else {
+ time_t old_time = pdb_get_pass_last_set_time(to);
+ new_time = old_time + expire;
+ if ((new_time) < time(0)) {
+ new_time = time(0) + expire;
+ }
+ }
+ if (!pdb_set_pass_must_change_time (to, new_time, PDB_CHANGED)) {
+ DEBUG (0, ("pdb_set_pass_must_change_time failed!\n"));
+ }
+ }
+ }
+
+ DEBUG(10,("INFO_21 PADDING_2: %02X\n",from->padding2));
+
+ DEBUG(10,("INFO_21 PADDING_4: %08X\n",from->padding4));
+}
+
+
+/*************************************************************
+ Copies a SAM_USER_INFO_23 to a SAM_ACCOUNT
+**************************************************************/
+
+void copy_id23_to_sam_passwd(SAM_ACCOUNT *to, SAM_USER_INFO_23 *from)
+{
+ time_t unix_time, stored_time;
+ const char *old_string, *new_string;
+ DATA_BLOB mung;
+
+ if (from == NULL || to == NULL)
+ return;
+
+ if (from->fields_present & ACCT_LAST_LOGON) {
+ unix_time=nt_time_to_unix(&from->logon_time);
+ stored_time = pdb_get_logon_time(to);
+ DEBUG(10,("INFO_23 LOGON_TIME: %lu -> %lu\n",(long unsigned int)stored_time, (long unsigned int)unix_time));
+ if (stored_time != unix_time)
+ pdb_set_logon_time(to, unix_time, PDB_CHANGED);
+ }
+
+ if (from->fields_present & ACCT_LAST_LOGOFF) {
+ unix_time=nt_time_to_unix(&from->logoff_time);
+ stored_time = pdb_get_logoff_time(to);
+ DEBUG(10,("INFO_23 LOGOFF_TIME: %lu -> %lu\n",(long unsigned int)stored_time, (long unsigned int)unix_time));
+ if (stored_time != unix_time)
+ pdb_set_logoff_time(to, unix_time, PDB_CHANGED);
+ }
+
+ if (from->fields_present & ACCT_EXPIRY) {
+ unix_time=nt_time_to_unix(&from->kickoff_time);
+ stored_time = pdb_get_kickoff_time(to);
+ DEBUG(10,("INFO_23 KICKOFF_TIME: %lu -> %lu\n",(long unsigned int)stored_time, (long unsigned int)unix_time));
+ if (stored_time != unix_time)
+ pdb_set_kickoff_time(to, unix_time , PDB_CHANGED);
+ }
+
+ if (from->fields_present & ACCT_ALLOW_PWD_CHANGE) {
+ unix_time=nt_time_to_unix(&from->pass_can_change_time);
+ stored_time = pdb_get_pass_can_change_time(to);
+ DEBUG(10,("INFO_23 PASS_CAN_CH: %lu -> %lu\n",(long unsigned int)stored_time, (long unsigned int)unix_time));
+ if (stored_time != unix_time)
+ pdb_set_pass_can_change_time(to, unix_time, PDB_CHANGED);
+ }
+
+ if (from->fields_present & ACCT_LAST_PWD_CHANGE) {
+ unix_time=nt_time_to_unix(&from->pass_last_set_time);
+ stored_time = pdb_get_pass_last_set_time(to);
+ DEBUG(10,("INFO_23 PASS_LAST_SET: %lu -> %lu\n",(long unsigned int)stored_time, (long unsigned int)unix_time));
+ if (stored_time != unix_time)
+ pdb_set_pass_last_set_time(to, unix_time, PDB_CHANGED);
+ }
+
+ if (from->fields_present & ACCT_FORCE_PWD_CHANGE) {
+ unix_time=nt_time_to_unix(&from->pass_must_change_time);
+ stored_time=pdb_get_pass_must_change_time(to);
+ DEBUG(10,("INFO_23 PASS_MUST_CH: %lu -> %lu\n",(long unsigned int)stored_time, (long unsigned int)unix_time));
+ if (stored_time != unix_time)
+ pdb_set_pass_must_change_time(to, unix_time, PDB_CHANGED);
+ }
+
+ /* Backend should check this for sanity */
+ if ((from->fields_present & ACCT_USERNAME) &&
+ (from->hdr_user_name.buffer)) {
+ old_string = pdb_get_username(to);
+ new_string = unistr2_static(&from->uni_user_name);
+ DEBUG(10,("INFO_23 UNI_USER_NAME: %s -> %s\n", old_string, new_string));
+ if (STRING_CHANGED)
+ pdb_set_username(to , new_string, PDB_CHANGED);
+ }
+
+ if ((from->fields_present & ACCT_FULL_NAME) &&
+ (from->hdr_full_name.buffer)) {
+ old_string = pdb_get_fullname(to);
+ new_string = unistr2_static(&from->uni_full_name);
+ DEBUG(10,("INFO_23 UNI_FULL_NAME: %s -> %s\n",old_string, new_string));
+ if (STRING_CHANGED)
+ pdb_set_fullname(to , new_string, PDB_CHANGED);
+ }
+
+ if ((from->fields_present & ACCT_HOME_DIR) &&
+ (from->hdr_home_dir.buffer)) {
+ old_string = pdb_get_homedir(to);
+ new_string = unistr2_static(&from->uni_home_dir);
+ DEBUG(10,("INFO_23 UNI_HOME_DIR: %s -> %s\n",old_string,new_string));
+ if (STRING_CHANGED)
+ pdb_set_homedir(to , new_string, PDB_CHANGED);
+ }
+
+ if ((from->fields_present & ACCT_HOME_DRIVE) &&
+ (from->hdr_dir_drive.buffer)) {
+ old_string = pdb_get_dir_drive(to);
+ new_string = unistr2_static(&from->uni_dir_drive);
+ DEBUG(10,("INFO_23 UNI_DIR_DRIVE: %s -> %s\n",old_string,new_string));
+ if (STRING_CHANGED)
+ pdb_set_dir_drive(to , new_string, PDB_CHANGED);
+ }
+
+ if ((from->fields_present & ACCT_LOGON_SCRIPT) &&
+ (from->hdr_logon_script.buffer)) {
+ old_string = pdb_get_logon_script(to);
+ new_string = unistr2_static(&from->uni_logon_script);
+ DEBUG(10,("INFO_23 UNI_LOGON_SCRIPT: %s -> %s\n",old_string,new_string));
+ if (STRING_CHANGED)
+ pdb_set_logon_script(to , new_string, PDB_CHANGED);
+ }
+
+ if ((from->fields_present & ACCT_PROFILE) &&
+ (from->hdr_profile_path.buffer)) {
+ old_string = pdb_get_profile_path(to);
+ new_string = unistr2_static(&from->uni_profile_path);
+ DEBUG(10,("INFO_23 UNI_PROFILE_PATH: %s -> %s\n",old_string, new_string));
+ if (STRING_CHANGED)
+ pdb_set_profile_path(to , new_string, PDB_CHANGED);
+ }
+
+ if ((from->fields_present & ACCT_DESCRIPTION) &&
+ (from->hdr_acct_desc.buffer)) {
+ old_string = pdb_get_acct_desc(to);
+ new_string = unistr2_static(&from->uni_acct_desc);
+ DEBUG(10,("INFO_23 UNI_ACCT_DESC: %s -> %s\n",old_string,new_string));
+ if (STRING_CHANGED)
+ pdb_set_acct_desc(to , new_string, PDB_CHANGED);
+ }
+
+ if ((from->fields_present & ACCT_WORKSTATIONS) &&
+ (from->hdr_workstations.buffer)) {
+ old_string = pdb_get_workstations(to);
+ new_string = unistr2_static(&from->uni_workstations);
+ DEBUG(10,("INFO_23 UNI_WORKSTATIONS: %s -> %s\n",old_string, new_string));
+ if (STRING_CHANGED)
+ pdb_set_workstations(to , new_string, PDB_CHANGED);
+ }
+
+ /* is this right? */
+ if ((from->fields_present & ACCT_ADMIN_DESC) &&
+ (from->hdr_unknown_str.buffer)) {
+ old_string = pdb_get_unknown_str(to);
+ new_string = unistr2_static(&from->uni_unknown_str);
+ DEBUG(10,("INFO_23 UNI_UNKNOWN_STR: %s -> %s\n",old_string, new_string));
+ if (STRING_CHANGED)
+ pdb_set_unknown_str(to , new_string, PDB_CHANGED);
+ }
+
+ if ((from->fields_present & ACCT_CALLBACK) &&
+ (from->hdr_munged_dial.buffer)) {
+ char *newstr;
+ old_string = pdb_get_munged_dial(to);
+ mung.length = from->hdr_munged_dial.uni_str_len;
+ mung.data = (uint8 *) from->uni_munged_dial.buffer;
+ newstr = base64_encode_data_blob(mung);
+ DEBUG(10,("INFO_23 UNI_MUNGED_DIAL: %s -> %s\n",old_string, newstr));
+ if (STRING_CHANGED_NC(old_string, newstr))
+ pdb_set_munged_dial(to , newstr, PDB_CHANGED);
+
+ SAFE_FREE(newstr);
+ }
+
+ if (from->fields_present & ACCT_RID) {
+ if (from->user_rid == 0) {
+ DEBUG(10, ("INFO_23: Asked to set User RID to 0 !? Skipping change!\n"));
+ } else if (from->user_rid != pdb_get_user_rid(to)) {
+ DEBUG(10,("INFO_23 USER_RID: %u -> %u NOT UPDATED!\n",pdb_get_user_rid(to),from->user_rid));
+ }
+ }
+
+ if (from->fields_present & ACCT_PRIMARY_GID) {
+ if (from->group_rid == 0) {
+ DEBUG(10, ("INFO_23: Asked to set Group RID to 0 !? Skipping change!\n"));
+ } else if (from->group_rid != pdb_get_group_rid(to)) {
+ DEBUG(10,("INFO_23 GROUP_RID: %u -> %u\n",pdb_get_group_rid(to),from->group_rid));
+ pdb_set_group_sid_from_rid(to, from->group_rid, PDB_CHANGED);
+ }
+ }
+
+ if (from->fields_present & ACCT_FLAGS) {
+ DEBUG(10,("INFO_23 ACCT_CTRL: %08X -> %08X\n",pdb_get_acct_ctrl(to),from->acb_info));
+ if (from->acb_info != pdb_get_acct_ctrl(to)) {
+ pdb_set_acct_ctrl(to, from->acb_info, PDB_CHANGED);
+ }
+ }
+
+ if (from->fields_present & ACCT_LOGON_HOURS) {
+ DEBUG(15,("INFO_23 LOGON_DIVS: %08X -> %08X\n",pdb_get_logon_divs(to),from->logon_divs));
+ if (from->logon_divs != pdb_get_logon_divs(to)) {
+ pdb_set_logon_divs(to, from->logon_divs, PDB_CHANGED);
+ }
+
+ DEBUG(15,("INFO_23 LOGON_HRS.LEN: %08X -> %08X\n",pdb_get_hours_len(to),from->logon_hrs.len));
+ if (from->logon_hrs.len != pdb_get_hours_len(to)) {
+ pdb_set_hours_len(to, from->logon_hrs.len, PDB_CHANGED);
+ }
+
+ DEBUG(15,("INFO_23 LOGON_HRS.HOURS: %s -> %s\n",pdb_get_hours(to),from->logon_hrs.hours));
+ /* Fix me: only update if it changes --metze */
+ pdb_set_hours(to, from->logon_hrs.hours, PDB_CHANGED);
+
+ /* This is max logon hours */
+ DEBUG(10,("INFO_23 UNKOWN_6: %08X -> %08X\n",pdb_get_unknown_6(to),from->unknown_6));
+ if (from->unknown_6 != pdb_get_unknown_6(to)) {
+ pdb_set_unknown_6(to, from->unknown_6, PDB_CHANGED);
+ }
+ }
+
+ if (from->fields_present & ACCT_BAD_PWD_COUNT) {
+ DEBUG(10,("INFO_23 BAD_PASSWORD_COUNT: %08X -> %08X\n",pdb_get_bad_password_count(to),from->bad_password_count));
+ if (from->bad_password_count != pdb_get_bad_password_count(to)) {
+ pdb_set_bad_password_count(to, from->bad_password_count, PDB_CHANGED);
+ }
+ }
+
+ if (from->fields_present & ACCT_NUM_LOGONS) {
+ DEBUG(10,("INFO_23 LOGON_COUNT: %08X -> %08X\n",pdb_get_logon_count(to),from->logon_count));
+ if (from->logon_count != pdb_get_logon_count(to)) {
+ pdb_set_logon_count(to, from->logon_count, PDB_CHANGED);
+ }
+ }
+
+ DEBUG(10,("INFO_23 PASS_MUST_CHANGE_AT_NEXT_LOGON: %02X\n",from->passmustchange));
+ if (from->passmustchange==PASS_MUST_CHANGE_AT_NEXT_LOGON) {
+ pdb_set_pass_must_change_time(to,0, PDB_CHANGED);
+ } else {
+ uint32 expire;
+ time_t new_time;
+ if (pdb_get_pass_must_change_time(to) == 0) {
+ if (!account_policy_get(AP_MAX_PASSWORD_AGE, &expire)
+ || expire == (uint32)-1) {
+ new_time = get_time_t_max();
+ } else {
+ time_t old_time = pdb_get_pass_last_set_time(to);
+ new_time = old_time + expire;
+ if ((new_time) < time(0)) {
+ new_time = time(0) + expire;
+ }
+ }
+ if (!pdb_set_pass_must_change_time (to, new_time, PDB_CHANGED)) {
+ DEBUG (0, ("pdb_set_pass_must_change_time failed!\n"));
+ }
+ }
+ }
+
+ DEBUG(10,("INFO_23 PADDING_2: %02X\n",from->padding2));
+
+ DEBUG(10,("INFO_23 PADDING_4: %08X\n",from->padding4));
+}
diff --git a/source/rpc_server/srv_spoolss.c b/source/rpc_server/srv_spoolss.c
new file mode 100755
index 00000000000..f846813a40b
--- /dev/null
+++ b/source/rpc_server/srv_spoolss.c
@@ -0,0 +1,1652 @@
+/*
+ * Unix SMB/CIFS implementation.
+ * RPC Pipe client / server routines
+ * Copyright (C) Andrew Tridgell 1992-2000,
+ * Copyright (C) Luke Kenneth Casson Leighton 1996-2000,
+ * Copyright (C) Jean François Micouleau 1998-2000,
+ * Copyright (C) Jeremy Allison 2001,
+ * Copyright (C) Gerald Carter 2001-2002,
+ * 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 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"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_RPC_SRV
+
+/********************************************************************
+ * api_spoolss_open_printer_ex (rarely seen - older call)
+ ********************************************************************/
+
+static BOOL api_spoolss_open_printer(pipes_struct *p)
+{
+ SPOOL_Q_OPEN_PRINTER q_u;
+ SPOOL_R_OPEN_PRINTER r_u;
+ prs_struct *data = &p->in_data.data;
+ prs_struct *rdata = &p->out_data.rdata;
+
+ ZERO_STRUCT(q_u);
+ ZERO_STRUCT(r_u);
+
+ if (!spoolss_io_q_open_printer("", &q_u, data, 0)) {
+ DEBUG(0,("spoolss_io_q_open_printer: unable to unmarshall SPOOL_Q_OPEN_PRINTER.\n"));
+ return False;
+ }
+
+ r_u.status = _spoolss_open_printer( p, &q_u, &r_u);
+
+ if (!spoolss_io_r_open_printer("",&r_u,rdata,0)){
+ DEBUG(0,("spoolss_io_r_open_printer: unable to marshall SPOOL_R_OPEN_PRINTER.\n"));
+ return False;
+ }
+
+ return True;
+}
+
+
+/********************************************************************
+ * api_spoolss_open_printer_ex
+ ********************************************************************/
+
+static BOOL api_spoolss_open_printer_ex(pipes_struct *p)
+{
+ SPOOL_Q_OPEN_PRINTER_EX q_u;
+ SPOOL_R_OPEN_PRINTER_EX r_u;
+ prs_struct *data = &p->in_data.data;
+ prs_struct *rdata = &p->out_data.rdata;
+
+ ZERO_STRUCT(q_u);
+ ZERO_STRUCT(r_u);
+
+ if (!spoolss_io_q_open_printer_ex("", &q_u, data, 0)) {
+ DEBUG(0,("spoolss_io_q_open_printer_ex: unable to unmarshall SPOOL_Q_OPEN_PRINTER_EX.\n"));
+ return False;
+ }
+
+ r_u.status = _spoolss_open_printer_ex( p, &q_u, &r_u);
+
+ if (!spoolss_io_r_open_printer_ex("",&r_u,rdata,0)){
+ DEBUG(0,("spoolss_io_r_open_printer_ex: unable to marshall SPOOL_R_OPEN_PRINTER_EX.\n"));
+ return False;
+ }
+
+ return True;
+}
+
+/********************************************************************
+ * api_spoolss_getprinterdata
+ *
+ * called from the spoolss dispatcher
+ ********************************************************************/
+
+static BOOL api_spoolss_getprinterdata(pipes_struct *p)
+{
+ SPOOL_Q_GETPRINTERDATA q_u;
+ SPOOL_R_GETPRINTERDATA r_u;
+ prs_struct *data = &p->in_data.data;
+ prs_struct *rdata = &p->out_data.rdata;
+
+ ZERO_STRUCT(q_u);
+ ZERO_STRUCT(r_u);
+
+ /* read the stream and fill the struct */
+ if (!spoolss_io_q_getprinterdata("", &q_u, data, 0)) {
+ DEBUG(0,("spoolss_io_q_getprinterdata: unable to unmarshall SPOOL_Q_GETPRINTERDATA.\n"));
+ return False;
+ }
+
+ r_u.status = _spoolss_getprinterdata( p, &q_u, &r_u);
+
+ if (!spoolss_io_r_getprinterdata("", &r_u, rdata, 0)) {
+ DEBUG(0,("spoolss_io_r_getprinterdata: unable to marshall SPOOL_R_GETPRINTERDATA.\n"));
+ return False;
+ }
+
+ return True;
+}
+
+/********************************************************************
+ * api_spoolss_deleteprinterdata
+ *
+ * called from the spoolss dispatcher
+ ********************************************************************/
+
+static BOOL api_spoolss_deleteprinterdata(pipes_struct *p)
+{
+ SPOOL_Q_DELETEPRINTERDATA q_u;
+ SPOOL_R_DELETEPRINTERDATA r_u;
+ prs_struct *data = &p->in_data.data;
+ prs_struct *rdata = &p->out_data.rdata;
+
+ ZERO_STRUCT(q_u);
+ ZERO_STRUCT(r_u);
+
+ /* read the stream and fill the struct */
+ if (!spoolss_io_q_deleteprinterdata("", &q_u, data, 0)) {
+ DEBUG(0,("spoolss_io_q_deleteprinterdata: unable to unmarshall SPOOL_Q_DELETEPRINTERDATA.\n"));
+ return False;
+ }
+
+ r_u.status = _spoolss_deleteprinterdata( p, &q_u, &r_u );
+
+ if (!spoolss_io_r_deleteprinterdata("", &r_u, rdata, 0)) {
+ DEBUG(0,("spoolss_io_r_deleteprinterdata: unable to marshall SPOOL_R_DELETEPRINTERDATA.\n"));
+ return False;
+ }
+
+ return True;
+}
+
+/********************************************************************
+ * api_spoolss_closeprinter
+ *
+ * called from the spoolss dispatcher
+ ********************************************************************/
+
+static BOOL api_spoolss_closeprinter(pipes_struct *p)
+{
+ SPOOL_Q_CLOSEPRINTER q_u;
+ SPOOL_R_CLOSEPRINTER r_u;
+ prs_struct *data = &p->in_data.data;
+ prs_struct *rdata = &p->out_data.rdata;
+
+ ZERO_STRUCT(q_u);
+ ZERO_STRUCT(r_u);
+
+ if (!spoolss_io_q_closeprinter("", &q_u, data, 0)) {
+ DEBUG(0,("spoolss_io_q_closeprinter: unable to unmarshall SPOOL_Q_CLOSEPRINTER.\n"));
+ return False;
+ }
+
+ r_u.status = _spoolss_closeprinter(p, &q_u, &r_u);
+
+ if (!spoolss_io_r_closeprinter("",&r_u,rdata,0)) {
+ DEBUG(0,("spoolss_io_r_closeprinter: unable to marshall SPOOL_R_CLOSEPRINTER.\n"));
+ return False;
+ }
+
+ return True;
+}
+
+/********************************************************************
+ * api_spoolss_abortprinter
+ *
+ * called from the spoolss dispatcher
+ ********************************************************************/
+
+static BOOL api_spoolss_abortprinter(pipes_struct *p)
+{
+ SPOOL_Q_ABORTPRINTER q_u;
+ SPOOL_R_ABORTPRINTER r_u;
+ prs_struct *data = &p->in_data.data;
+ prs_struct *rdata = &p->out_data.rdata;
+
+ ZERO_STRUCT(q_u);
+ ZERO_STRUCT(r_u);
+
+ if (!spoolss_io_q_abortprinter("", &q_u, data, 0)) {
+ DEBUG(0,("spoolss_io_q_abortprinter: unable to unmarshall SPOOL_Q_ABORTPRINTER.\n"));
+ return False;
+ }
+
+ r_u.status = _spoolss_abortprinter(p, &q_u, &r_u);
+
+ if (!spoolss_io_r_abortprinter("",&r_u,rdata,0)) {
+ DEBUG(0,("spoolss_io_r_abortprinter: unable to marshall SPOOL_R_ABORTPRINTER.\n"));
+ return False;
+ }
+
+ return True;
+}
+
+/********************************************************************
+ * api_spoolss_deleteprinter
+ *
+ * called from the spoolss dispatcher
+ ********************************************************************/
+
+static BOOL api_spoolss_deleteprinter(pipes_struct *p)
+{
+ SPOOL_Q_DELETEPRINTER q_u;
+ SPOOL_R_DELETEPRINTER r_u;
+ prs_struct *data = &p->in_data.data;
+ prs_struct *rdata = &p->out_data.rdata;
+
+ ZERO_STRUCT(q_u);
+ ZERO_STRUCT(r_u);
+
+ if (!spoolss_io_q_deleteprinter("", &q_u, data, 0)) {
+ DEBUG(0,("spoolss_io_q_deleteprinter: unable to unmarshall SPOOL_Q_DELETEPRINTER.\n"));
+ return False;
+ }
+
+ r_u.status = _spoolss_deleteprinter(p, &q_u, &r_u);
+
+ if (!spoolss_io_r_deleteprinter("",&r_u,rdata,0)) {
+ DEBUG(0,("spoolss_io_r_deleteprinter: unable to marshall SPOOL_R_DELETEPRINTER.\n"));
+ return False;
+ }
+
+ return True;
+}
+
+
+/********************************************************************
+ * api_spoolss_deleteprinterdriver
+ *
+ * called from the spoolss dispatcher
+ ********************************************************************/
+
+static BOOL api_spoolss_deleteprinterdriver(pipes_struct *p)
+{
+ SPOOL_Q_DELETEPRINTERDRIVER q_u;
+ SPOOL_R_DELETEPRINTERDRIVER r_u;
+ prs_struct *data = &p->in_data.data;
+ prs_struct *rdata = &p->out_data.rdata;
+
+ ZERO_STRUCT(q_u);
+ ZERO_STRUCT(r_u);
+
+ if (!spoolss_io_q_deleteprinterdriver("", &q_u, data, 0)) {
+ DEBUG(0,("spoolss_io_q_deleteprinterdriver: unable to unmarshall SPOOL_Q_DELETEPRINTERDRIVER.\n"));
+ return False;
+ }
+
+ r_u.status = _spoolss_deleteprinterdriver(p, &q_u, &r_u);
+
+ if (!spoolss_io_r_deleteprinterdriver("",&r_u,rdata,0)) {
+ DEBUG(0,("spoolss_io_r_deleteprinter: unable to marshall SPOOL_R_DELETEPRINTER.\n"));
+ return False;
+ }
+
+ return True;
+}
+
+
+/********************************************************************
+ * api_spoolss_rffpcnex
+ * ReplyFindFirstPrinterChangeNotifyEx
+ ********************************************************************/
+
+static BOOL api_spoolss_rffpcnex(pipes_struct *p)
+{
+ SPOOL_Q_RFFPCNEX q_u;
+ SPOOL_R_RFFPCNEX r_u;
+ prs_struct *data = &p->in_data.data;
+ prs_struct *rdata = &p->out_data.rdata;
+
+ ZERO_STRUCT(q_u);
+ ZERO_STRUCT(r_u);
+
+ if (!spoolss_io_q_rffpcnex("", &q_u, data, 0)) {
+ DEBUG(0,("spoolss_io_q_rffpcnex: unable to unmarshall SPOOL_Q_RFFPCNEX.\n"));
+ return False;
+ }
+
+ r_u.status = _spoolss_rffpcnex(p, &q_u, &r_u);
+
+ if (!spoolss_io_r_rffpcnex("", &r_u, rdata, 0)) {
+ DEBUG(0,("spoolss_io_r_rffpcnex: unable to marshall SPOOL_R_RFFPCNEX.\n"));
+ return False;
+ }
+
+ return True;
+}
+
+
+/********************************************************************
+ * api_spoolss_rfnpcnex
+ * ReplyFindNextPrinterChangeNotifyEx
+ * called from the spoolss dispatcher
+
+ * Note - this is the *ONLY* function that breaks the RPC call
+ * symmetry in all the other calls. We need to do this to fix
+ * the massive memory allocation problem with thousands of jobs...
+ * JRA.
+ ********************************************************************/
+
+static BOOL api_spoolss_rfnpcnex(pipes_struct *p)
+{
+ SPOOL_Q_RFNPCNEX q_u;
+ SPOOL_R_RFNPCNEX r_u;
+ prs_struct *data = &p->in_data.data;
+ prs_struct *rdata = &p->out_data.rdata;
+
+ ZERO_STRUCT(q_u);
+ ZERO_STRUCT(r_u);
+
+ if (!spoolss_io_q_rfnpcnex("", &q_u, data, 0)) {
+ DEBUG(0,("spoolss_io_q_rfnpcnex: unable to unmarshall SPOOL_Q_RFNPCNEX.\n"));
+ return False;
+ }
+
+ r_u.status = _spoolss_rfnpcnex(p, &q_u, &r_u);
+
+ if (!spoolss_io_r_rfnpcnex("", &r_u, rdata, 0)) {
+ SAFE_FREE(r_u.info.data);
+ DEBUG(0,("spoolss_io_r_rfnpcnex: unable to marshall SPOOL_R_RFNPCNEX.\n"));
+ return False;
+ }
+
+ SAFE_FREE(r_u.info.data);
+
+ return True;
+}
+
+
+/********************************************************************
+ * api_spoolss_enumprinters
+ * called from the spoolss dispatcher
+ *
+ ********************************************************************/
+
+static BOOL api_spoolss_enumprinters(pipes_struct *p)
+{
+ SPOOL_Q_ENUMPRINTERS q_u;
+ SPOOL_R_ENUMPRINTERS r_u;
+ prs_struct *data = &p->in_data.data;
+ prs_struct *rdata = &p->out_data.rdata;
+
+ ZERO_STRUCT(q_u);
+ ZERO_STRUCT(r_u);
+
+ if (!spoolss_io_q_enumprinters("", &q_u, data, 0)) {
+ DEBUG(0,("spoolss_io_q_enumprinters: unable to unmarshall SPOOL_Q_ENUMPRINTERS.\n"));
+ return False;
+ }
+
+ r_u.status = _spoolss_enumprinters( p, &q_u, &r_u);
+
+ if (!spoolss_io_r_enumprinters("", &r_u, rdata, 0)) {
+ DEBUG(0,("spoolss_io_r_enumprinters: unable to marshall SPOOL_R_ENUMPRINTERS.\n"));
+ return False;
+ }
+
+ return True;
+}
+
+/********************************************************************
+ * api_spoolss_getprinter
+ * called from the spoolss dispatcher
+ *
+ ********************************************************************/
+
+static BOOL api_spoolss_getprinter(pipes_struct *p)
+{
+ SPOOL_Q_GETPRINTER q_u;
+ SPOOL_R_GETPRINTER r_u;
+ prs_struct *data = &p->in_data.data;
+ prs_struct *rdata = &p->out_data.rdata;
+
+ ZERO_STRUCT(q_u);
+ ZERO_STRUCT(r_u);
+
+ if(!spoolss_io_q_getprinter("", &q_u, data, 0)) {
+ DEBUG(0,("spoolss_io_q_getprinter: unable to unmarshall SPOOL_Q_GETPRINTER.\n"));
+ return False;
+ }
+
+ r_u.status = _spoolss_getprinter(p, &q_u, &r_u);
+
+ if(!spoolss_io_r_getprinter("",&r_u,rdata,0)) {
+ DEBUG(0,("spoolss_io_r_getprinter: unable to marshall SPOOL_R_GETPRINTER.\n"));
+ return False;
+ }
+
+ return True;
+}
+
+/********************************************************************
+ * api_spoolss_getprinter
+ * called from the spoolss dispatcher
+ *
+ ********************************************************************/
+
+static BOOL api_spoolss_getprinterdriver2(pipes_struct *p)
+{
+ SPOOL_Q_GETPRINTERDRIVER2 q_u;
+ SPOOL_R_GETPRINTERDRIVER2 r_u;
+ prs_struct *data = &p->in_data.data;
+ prs_struct *rdata = &p->out_data.rdata;
+
+ ZERO_STRUCT(q_u);
+ ZERO_STRUCT(r_u);
+
+ if(!spoolss_io_q_getprinterdriver2("", &q_u, data, 0)) {
+ DEBUG(0,("spoolss_io_q_getprinterdriver2: unable to unmarshall SPOOL_Q_GETPRINTERDRIVER2.\n"));
+ return False;
+ }
+
+ r_u.status = _spoolss_getprinterdriver2(p, &q_u, &r_u);
+
+ if(!spoolss_io_r_getprinterdriver2("",&r_u,rdata,0)) {
+ DEBUG(0,("spoolss_io_r_getprinterdriver2: unable to marshall SPOOL_R_GETPRINTERDRIVER2.\n"));
+ return False;
+ }
+
+ return True;
+}
+
+/********************************************************************
+ * api_spoolss_getprinter
+ * called from the spoolss dispatcher
+ *
+ ********************************************************************/
+
+static BOOL api_spoolss_startpageprinter(pipes_struct *p)
+{
+ SPOOL_Q_STARTPAGEPRINTER q_u;
+ SPOOL_R_STARTPAGEPRINTER r_u;
+ prs_struct *data = &p->in_data.data;
+ prs_struct *rdata = &p->out_data.rdata;
+
+ ZERO_STRUCT(q_u);
+ ZERO_STRUCT(r_u);
+
+ if(!spoolss_io_q_startpageprinter("", &q_u, data, 0)) {
+ DEBUG(0,("spoolss_io_q_startpageprinter: unable to unmarshall SPOOL_Q_STARTPAGEPRINTER.\n"));
+ return False;
+ }
+
+ r_u.status = _spoolss_startpageprinter(p, &q_u, &r_u);
+
+ if(!spoolss_io_r_startpageprinter("",&r_u,rdata,0)) {
+ DEBUG(0,("spoolss_io_r_startpageprinter: unable to marshall SPOOL_R_STARTPAGEPRINTER.\n"));
+ return False;
+ }
+
+ return True;
+}
+
+/********************************************************************
+ * api_spoolss_getprinter
+ * called from the spoolss dispatcher
+ *
+ ********************************************************************/
+
+static BOOL api_spoolss_endpageprinter(pipes_struct *p)
+{
+ SPOOL_Q_ENDPAGEPRINTER q_u;
+ SPOOL_R_ENDPAGEPRINTER r_u;
+ prs_struct *data = &p->in_data.data;
+ prs_struct *rdata = &p->out_data.rdata;
+
+ ZERO_STRUCT(q_u);
+ ZERO_STRUCT(r_u);
+
+ if(!spoolss_io_q_endpageprinter("", &q_u, data, 0)) {
+ DEBUG(0,("spoolss_io_q_endpageprinter: unable to unmarshall SPOOL_Q_ENDPAGEPRINTER.\n"));
+ return False;
+ }
+
+ r_u.status = _spoolss_endpageprinter(p, &q_u, &r_u);
+
+ if(!spoolss_io_r_endpageprinter("",&r_u,rdata,0)) {
+ DEBUG(0,("spoolss_io_r_endpageprinter: unable to marshall SPOOL_R_ENDPAGEPRINTER.\n"));
+ return False;
+ }
+
+ return True;
+}
+
+/********************************************************************
+********************************************************************/
+
+static BOOL api_spoolss_startdocprinter(pipes_struct *p)
+{
+ SPOOL_Q_STARTDOCPRINTER q_u;
+ SPOOL_R_STARTDOCPRINTER r_u;
+ prs_struct *data = &p->in_data.data;
+ prs_struct *rdata = &p->out_data.rdata;
+
+ ZERO_STRUCT(q_u);
+ ZERO_STRUCT(r_u);
+
+ if(!spoolss_io_q_startdocprinter("", &q_u, data, 0)) {
+ DEBUG(0,("spoolss_io_q_startdocprinter: unable to unmarshall SPOOL_Q_STARTDOCPRINTER.\n"));
+ return False;
+ }
+
+ r_u.status = _spoolss_startdocprinter(p, &q_u, &r_u);
+
+ if(!spoolss_io_r_startdocprinter("",&r_u,rdata,0)) {
+ DEBUG(0,("spoolss_io_r_startdocprinter: unable to marshall SPOOL_R_STARTDOCPRINTER.\n"));
+ return False;
+ }
+
+ return True;
+}
+
+/********************************************************************
+********************************************************************/
+
+static BOOL api_spoolss_enddocprinter(pipes_struct *p)
+{
+ SPOOL_Q_ENDDOCPRINTER q_u;
+ SPOOL_R_ENDDOCPRINTER r_u;
+ prs_struct *data = &p->in_data.data;
+ prs_struct *rdata = &p->out_data.rdata;
+
+ ZERO_STRUCT(q_u);
+ ZERO_STRUCT(r_u);
+
+ if(!spoolss_io_q_enddocprinter("", &q_u, data, 0)) {
+ DEBUG(0,("spoolss_io_q_enddocprinter: unable to unmarshall SPOOL_Q_ENDDOCPRINTER.\n"));
+ return False;
+ }
+
+ r_u.status = _spoolss_enddocprinter(p, &q_u, &r_u);
+
+ if(!spoolss_io_r_enddocprinter("",&r_u,rdata,0)) {
+ DEBUG(0,("spoolss_io_r_enddocprinter: unable to marshall SPOOL_R_ENDDOCPRINTER.\n"));
+ return False;
+ }
+
+ return True;
+}
+
+/********************************************************************
+********************************************************************/
+
+static BOOL api_spoolss_writeprinter(pipes_struct *p)
+{
+ SPOOL_Q_WRITEPRINTER q_u;
+ SPOOL_R_WRITEPRINTER r_u;
+ prs_struct *data = &p->in_data.data;
+ prs_struct *rdata = &p->out_data.rdata;
+
+ ZERO_STRUCT(q_u);
+ ZERO_STRUCT(r_u);
+
+ if(!spoolss_io_q_writeprinter("", &q_u, data, 0)) {
+ DEBUG(0,("spoolss_io_q_writeprinter: unable to unmarshall SPOOL_Q_WRITEPRINTER.\n"));
+ return False;
+ }
+
+ r_u.status = _spoolss_writeprinter(p, &q_u, &r_u);
+
+ if(!spoolss_io_r_writeprinter("",&r_u,rdata,0)) {
+ DEBUG(0,("spoolss_io_r_writeprinter: unable to marshall SPOOL_R_WRITEPRINTER.\n"));
+ return False;
+ }
+
+ return True;
+}
+
+/****************************************************************************
+
+****************************************************************************/
+
+static BOOL api_spoolss_setprinter(pipes_struct *p)
+{
+ SPOOL_Q_SETPRINTER q_u;
+ SPOOL_R_SETPRINTER r_u;
+ prs_struct *data = &p->in_data.data;
+ prs_struct *rdata = &p->out_data.rdata;
+
+ ZERO_STRUCT(q_u);
+ ZERO_STRUCT(r_u);
+
+ if(!spoolss_io_q_setprinter("", &q_u, data, 0)) {
+ DEBUG(0,("spoolss_io_q_setprinter: unable to unmarshall SPOOL_Q_SETPRINTER.\n"));
+ return False;
+ }
+
+ r_u.status = _spoolss_setprinter(p, &q_u, &r_u);
+
+ if(!spoolss_io_r_setprinter("",&r_u,rdata,0)) {
+ DEBUG(0,("spoolss_io_r_setprinter: unable to marshall SPOOL_R_SETPRINTER.\n"));
+ return False;
+ }
+
+ return True;
+}
+
+/****************************************************************************
+****************************************************************************/
+
+static BOOL api_spoolss_fcpn(pipes_struct *p)
+{
+ SPOOL_Q_FCPN q_u;
+ SPOOL_R_FCPN r_u;
+ prs_struct *data = &p->in_data.data;
+ prs_struct *rdata = &p->out_data.rdata;
+
+ ZERO_STRUCT(q_u);
+ ZERO_STRUCT(r_u);
+
+ if(!spoolss_io_q_fcpn("", &q_u, data, 0)) {
+ DEBUG(0,("spoolss_io_q_fcpn: unable to unmarshall SPOOL_Q_FCPN.\n"));
+ return False;
+ }
+
+ r_u.status = _spoolss_fcpn(p, &q_u, &r_u);
+
+ if(!spoolss_io_r_fcpn("",&r_u,rdata,0)) {
+ DEBUG(0,("spoolss_io_r_fcpn: unable to marshall SPOOL_R_FCPN.\n"));
+ return False;
+ }
+
+ return True;
+}
+
+/****************************************************************************
+****************************************************************************/
+
+static BOOL api_spoolss_addjob(pipes_struct *p)
+{
+ SPOOL_Q_ADDJOB q_u;
+ SPOOL_R_ADDJOB r_u;
+ prs_struct *data = &p->in_data.data;
+ prs_struct *rdata = &p->out_data.rdata;
+
+ ZERO_STRUCT(q_u);
+ ZERO_STRUCT(r_u);
+
+ if(!spoolss_io_q_addjob("", &q_u, data, 0)) {
+ DEBUG(0,("spoolss_io_q_addjob: unable to unmarshall SPOOL_Q_ADDJOB.\n"));
+ return False;
+ }
+
+ r_u.status = _spoolss_addjob(p, &q_u, &r_u);
+
+ if(!spoolss_io_r_addjob("",&r_u,rdata,0)) {
+ DEBUG(0,("spoolss_io_r_addjob: unable to marshall SPOOL_R_ADDJOB.\n"));
+ return False;
+ }
+
+ return True;
+}
+
+/****************************************************************************
+****************************************************************************/
+
+static BOOL api_spoolss_enumjobs(pipes_struct *p)
+{
+ SPOOL_Q_ENUMJOBS q_u;
+ SPOOL_R_ENUMJOBS r_u;
+ prs_struct *data = &p->in_data.data;
+ prs_struct *rdata = &p->out_data.rdata;
+
+ ZERO_STRUCT(q_u);
+ ZERO_STRUCT(r_u);
+
+ if (!spoolss_io_q_enumjobs("", &q_u, data, 0)) {
+ DEBUG(0,("spoolss_io_q_enumjobs: unable to unmarshall SPOOL_Q_ENUMJOBS.\n"));
+ return False;
+ }
+
+ r_u.status = _spoolss_enumjobs(p, &q_u, &r_u);
+
+ if (!spoolss_io_r_enumjobs("",&r_u,rdata,0)) {
+ DEBUG(0,("spoolss_io_r_enumjobs: unable to marshall SPOOL_R_ENUMJOBS.\n"));
+ return False;
+ }
+
+ return True;
+}
+
+/****************************************************************************
+****************************************************************************/
+
+static BOOL api_spoolss_schedulejob(pipes_struct *p)
+{
+ SPOOL_Q_SCHEDULEJOB q_u;
+ SPOOL_R_SCHEDULEJOB r_u;
+ prs_struct *data = &p->in_data.data;
+ prs_struct *rdata = &p->out_data.rdata;
+
+ ZERO_STRUCT(q_u);
+ ZERO_STRUCT(r_u);
+
+ if(!spoolss_io_q_schedulejob("", &q_u, data, 0)) {
+ DEBUG(0,("spoolss_io_q_schedulejob: unable to unmarshall SPOOL_Q_SCHEDULEJOB.\n"));
+ return False;
+ }
+
+ r_u.status = _spoolss_schedulejob(p, &q_u, &r_u);
+
+ if(!spoolss_io_r_schedulejob("",&r_u,rdata,0)) {
+ DEBUG(0,("spoolss_io_r_schedulejob: unable to marshall SPOOL_R_SCHEDULEJOB.\n"));
+ return False;
+ }
+
+ return True;
+}
+
+/****************************************************************************
+****************************************************************************/
+
+static BOOL api_spoolss_setjob(pipes_struct *p)
+{
+ SPOOL_Q_SETJOB q_u;
+ SPOOL_R_SETJOB r_u;
+ prs_struct *data = &p->in_data.data;
+ prs_struct *rdata = &p->out_data.rdata;
+
+ ZERO_STRUCT(q_u);
+ ZERO_STRUCT(r_u);
+
+ if(!spoolss_io_q_setjob("", &q_u, data, 0)) {
+ DEBUG(0,("spoolss_io_q_setjob: unable to unmarshall SPOOL_Q_SETJOB.\n"));
+ return False;
+ }
+
+ r_u.status = _spoolss_setjob(p, &q_u, &r_u);
+
+ if(!spoolss_io_r_setjob("",&r_u,rdata,0)) {
+ DEBUG(0,("spoolss_io_r_setjob: unable to marshall SPOOL_R_SETJOB.\n"));
+ return False;
+ }
+
+ return True;
+}
+
+/****************************************************************************
+****************************************************************************/
+
+static BOOL api_spoolss_enumprinterdrivers(pipes_struct *p)
+{
+ SPOOL_Q_ENUMPRINTERDRIVERS q_u;
+ SPOOL_R_ENUMPRINTERDRIVERS r_u;
+ prs_struct *data = &p->in_data.data;
+ prs_struct *rdata = &p->out_data.rdata;
+
+ ZERO_STRUCT(q_u);
+ ZERO_STRUCT(r_u);
+
+ if (!spoolss_io_q_enumprinterdrivers("", &q_u, data, 0)) {
+ DEBUG(0,("spoolss_io_q_enumprinterdrivers: unable to unmarshall SPOOL_Q_ENUMPRINTERDRIVERS.\n"));
+ return False;
+ }
+
+ r_u.status = _spoolss_enumprinterdrivers(p, &q_u, &r_u);
+
+ if (!spoolss_io_r_enumprinterdrivers("",&r_u,rdata,0)) {
+ DEBUG(0,("spoolss_io_r_enumprinterdrivers: unable to marshall SPOOL_R_ENUMPRINTERDRIVERS.\n"));
+ return False;
+ }
+
+ return True;
+}
+
+/****************************************************************************
+****************************************************************************/
+
+static BOOL api_spoolss_getform(pipes_struct *p)
+{
+ SPOOL_Q_GETFORM q_u;
+ SPOOL_R_GETFORM r_u;
+ prs_struct *data = &p->in_data.data;
+ prs_struct *rdata = &p->out_data.rdata;
+
+ ZERO_STRUCT(q_u);
+ ZERO_STRUCT(r_u);
+
+ if (!spoolss_io_q_getform("", &q_u, data, 0)) {
+ DEBUG(0,("spoolss_io_q_getform: unable to unmarshall SPOOL_Q_GETFORM.\n"));
+ return False;
+ }
+
+ r_u.status = _spoolss_getform(p, &q_u, &r_u);
+
+ if (!spoolss_io_r_getform("",&r_u,rdata,0)) {
+ DEBUG(0,("spoolss_io_r_getform: unable to marshall SPOOL_R_GETFORM.\n"));
+ return False;
+ }
+
+ return True;
+}
+
+/****************************************************************************
+****************************************************************************/
+
+static BOOL api_spoolss_enumforms(pipes_struct *p)
+{
+ SPOOL_Q_ENUMFORMS q_u;
+ SPOOL_R_ENUMFORMS r_u;
+ prs_struct *data = &p->in_data.data;
+ prs_struct *rdata = &p->out_data.rdata;
+
+ ZERO_STRUCT(q_u);
+ ZERO_STRUCT(r_u);
+
+ if (!spoolss_io_q_enumforms("", &q_u, data, 0)) {
+ DEBUG(0,("spoolss_io_q_enumforms: unable to unmarshall SPOOL_Q_ENUMFORMS.\n"));
+ return False;
+ }
+
+ r_u.status = _spoolss_enumforms(p, &q_u, &r_u);
+
+ if (!spoolss_io_r_enumforms("",&r_u,rdata,0)) {
+ DEBUG(0,("spoolss_io_r_enumforms: unable to marshall SPOOL_R_ENUMFORMS.\n"));
+ return False;
+ }
+
+ return True;
+}
+
+/****************************************************************************
+****************************************************************************/
+
+static BOOL api_spoolss_enumports(pipes_struct *p)
+{
+ SPOOL_Q_ENUMPORTS q_u;
+ SPOOL_R_ENUMPORTS r_u;
+ prs_struct *data = &p->in_data.data;
+ prs_struct *rdata = &p->out_data.rdata;
+
+ ZERO_STRUCT(q_u);
+ ZERO_STRUCT(r_u);
+
+ if(!spoolss_io_q_enumports("", &q_u, data, 0)) {
+ DEBUG(0,("spoolss_io_q_enumports: unable to unmarshall SPOOL_Q_ENUMPORTS.\n"));
+ return False;
+ }
+
+ r_u.status = _spoolss_enumports(p, &q_u, &r_u);
+
+ if (!spoolss_io_r_enumports("",&r_u,rdata,0)) {
+ DEBUG(0,("spoolss_io_r_enumports: unable to marshall SPOOL_R_ENUMPORTS.\n"));
+ return False;
+ }
+
+ return True;
+}
+
+/****************************************************************************
+****************************************************************************/
+
+static BOOL api_spoolss_addprinterex(pipes_struct *p)
+{
+ SPOOL_Q_ADDPRINTEREX q_u;
+ SPOOL_R_ADDPRINTEREX r_u;
+ prs_struct *data = &p->in_data.data;
+ prs_struct *rdata = &p->out_data.rdata;
+
+ ZERO_STRUCT(q_u);
+ ZERO_STRUCT(r_u);
+
+ if(!spoolss_io_q_addprinterex("", &q_u, data, 0)) {
+ DEBUG(0,("spoolss_io_q_addprinterex: unable to unmarshall SPOOL_Q_ADDPRINTEREX.\n"));
+ return False;
+ }
+
+ r_u.status = _spoolss_addprinterex(p, &q_u, &r_u);
+
+ if(!spoolss_io_r_addprinterex("", &r_u, rdata, 0)) {
+ DEBUG(0,("spoolss_io_r_addprinterex: unable to marshall SPOOL_R_ADDPRINTEREX.\n"));
+ return False;
+ }
+
+ return True;
+}
+
+/****************************************************************************
+****************************************************************************/
+
+static BOOL api_spoolss_addprinterdriver(pipes_struct *p)
+{
+ SPOOL_Q_ADDPRINTERDRIVER q_u;
+ SPOOL_R_ADDPRINTERDRIVER r_u;
+ prs_struct *data = &p->in_data.data;
+ prs_struct *rdata = &p->out_data.rdata;
+
+ ZERO_STRUCT(q_u);
+ ZERO_STRUCT(r_u);
+
+ if(!spoolss_io_q_addprinterdriver("", &q_u, data, 0)) {
+ DEBUG(0,("spoolss_io_q_addprinterdriver: unable to unmarshall SPOOL_Q_ADDPRINTERDRIVER.\n"));
+ return False;
+ }
+
+ r_u.status = _spoolss_addprinterdriver(p, &q_u, &r_u);
+
+ if(!spoolss_io_r_addprinterdriver("", &r_u, rdata, 0)) {
+ DEBUG(0,("spoolss_io_r_addprinterdriver: unable to marshall SPOOL_R_ADDPRINTERDRIVER.\n"));
+ return False;
+ }
+
+ return True;
+}
+
+/****************************************************************************
+****************************************************************************/
+
+static BOOL api_spoolss_getprinterdriverdirectory(pipes_struct *p)
+{
+ SPOOL_Q_GETPRINTERDRIVERDIR q_u;
+ SPOOL_R_GETPRINTERDRIVERDIR r_u;
+ prs_struct *data = &p->in_data.data;
+ prs_struct *rdata = &p->out_data.rdata;
+
+ ZERO_STRUCT(q_u);
+ ZERO_STRUCT(r_u);
+
+ if(!spoolss_io_q_getprinterdriverdir("", &q_u, data, 0)) {
+ DEBUG(0,("spoolss_io_q_getprinterdriverdir: unable to unmarshall SPOOL_Q_GETPRINTERDRIVERDIR.\n"));
+ return False;
+ }
+
+ r_u.status = _spoolss_getprinterdriverdirectory(p, &q_u, &r_u);
+
+ if(!spoolss_io_r_getprinterdriverdir("", &r_u, rdata, 0)) {
+ DEBUG(0,("spoolss_io_r_getprinterdriverdir: unable to marshall SPOOL_R_GETPRINTERDRIVERDIR.\n"));
+ return False;
+ }
+
+ return True;
+}
+
+/****************************************************************************
+****************************************************************************/
+
+static BOOL api_spoolss_enumprinterdata(pipes_struct *p)
+{
+ SPOOL_Q_ENUMPRINTERDATA q_u;
+ SPOOL_R_ENUMPRINTERDATA r_u;
+ prs_struct *data = &p->in_data.data;
+ prs_struct *rdata = &p->out_data.rdata;
+
+ ZERO_STRUCT(q_u);
+ ZERO_STRUCT(r_u);
+
+ if(!spoolss_io_q_enumprinterdata("", &q_u, data, 0)) {
+ DEBUG(0,("spoolss_io_q_enumprinterdata: unable to unmarshall SPOOL_Q_ENUMPRINTERDATA.\n"));
+ return False;
+ }
+
+ r_u.status = _spoolss_enumprinterdata(p, &q_u, &r_u);
+
+ if(!spoolss_io_r_enumprinterdata("", &r_u, rdata, 0)) {
+ DEBUG(0,("spoolss_io_r_enumprinterdata: unable to marshall SPOOL_R_ENUMPRINTERDATA.\n"));
+ return False;
+ }
+
+ return True;
+}
+
+/****************************************************************************
+****************************************************************************/
+
+static BOOL api_spoolss_setprinterdata(pipes_struct *p)
+{
+ SPOOL_Q_SETPRINTERDATA q_u;
+ SPOOL_R_SETPRINTERDATA r_u;
+ prs_struct *data = &p->in_data.data;
+ prs_struct *rdata = &p->out_data.rdata;
+
+ ZERO_STRUCT(q_u);
+ ZERO_STRUCT(r_u);
+
+ if(!spoolss_io_q_setprinterdata("", &q_u, data, 0)) {
+ DEBUG(0,("spoolss_io_q_setprinterdata: unable to unmarshall SPOOL_Q_SETPRINTERDATA.\n"));
+ return False;
+ }
+
+ r_u.status = _spoolss_setprinterdata(p, &q_u, &r_u);
+
+ if(!spoolss_io_r_setprinterdata("", &r_u, rdata, 0)) {
+ DEBUG(0,("spoolss_io_r_setprinterdata: unable to marshall SPOOL_R_SETPRINTERDATA.\n"));
+ return False;
+ }
+
+ return True;
+}
+
+/****************************************************************************
+****************************************************************************/
+static BOOL api_spoolss_reset_printer(pipes_struct *p)
+{
+ SPOOL_Q_RESETPRINTER q_u;
+ SPOOL_R_RESETPRINTER r_u;
+ prs_struct *data = &p->in_data.data;
+ prs_struct *rdata = &p->out_data.rdata;
+
+ ZERO_STRUCT(q_u);
+ ZERO_STRUCT(r_u);
+
+ if(!spoolss_io_q_resetprinter("", &q_u, data, 0)) {
+ DEBUG(0,("spoolss_io_q_setprinterdata: unable to unmarshall SPOOL_Q_SETPRINTERDATA.\n"));
+ return False;
+ }
+
+ r_u.status = _spoolss_resetprinter(p, &q_u, &r_u);
+
+ if(!spoolss_io_r_resetprinter("", &r_u, rdata, 0)) {
+ DEBUG(0,("spoolss_io_r_setprinterdata: unable to marshall SPOOL_R_RESETPRINTER.\n"));
+ return False;
+ }
+
+ return True;
+}
+
+/****************************************************************************
+****************************************************************************/
+static BOOL api_spoolss_addform(pipes_struct *p)
+{
+ SPOOL_Q_ADDFORM q_u;
+ SPOOL_R_ADDFORM r_u;
+ prs_struct *data = &p->in_data.data;
+ prs_struct *rdata = &p->out_data.rdata;
+
+ ZERO_STRUCT(q_u);
+ ZERO_STRUCT(r_u);
+
+ if(!spoolss_io_q_addform("", &q_u, data, 0)) {
+ DEBUG(0,("spoolss_io_q_addform: unable to unmarshall SPOOL_Q_ADDFORM.\n"));
+ return False;
+ }
+
+ r_u.status = _spoolss_addform(p, &q_u, &r_u);
+
+ if(!spoolss_io_r_addform("", &r_u, rdata, 0)) {
+ DEBUG(0,("spoolss_io_r_addform: unable to marshall SPOOL_R_ADDFORM.\n"));
+ return False;
+ }
+
+ return True;
+}
+
+/****************************************************************************
+****************************************************************************/
+
+static BOOL api_spoolss_deleteform(pipes_struct *p)
+{
+ SPOOL_Q_DELETEFORM q_u;
+ SPOOL_R_DELETEFORM r_u;
+ prs_struct *data = &p->in_data.data;
+ prs_struct *rdata = &p->out_data.rdata;
+
+ ZERO_STRUCT(q_u);
+ ZERO_STRUCT(r_u);
+
+ if(!spoolss_io_q_deleteform("", &q_u, data, 0)) {
+ DEBUG(0,("spoolss_io_q_deleteform: unable to unmarshall SPOOL_Q_DELETEFORM.\n"));
+ return False;
+ }
+
+ r_u.status = _spoolss_deleteform(p, &q_u, &r_u);
+
+ if(!spoolss_io_r_deleteform("", &r_u, rdata, 0)) {
+ DEBUG(0,("spoolss_io_r_deleteform: unable to marshall SPOOL_R_DELETEFORM.\n"));
+ return False;
+ }
+
+ return True;
+}
+
+/****************************************************************************
+****************************************************************************/
+
+static BOOL api_spoolss_setform(pipes_struct *p)
+{
+ SPOOL_Q_SETFORM q_u;
+ SPOOL_R_SETFORM r_u;
+ prs_struct *data = &p->in_data.data;
+ prs_struct *rdata = &p->out_data.rdata;
+
+ ZERO_STRUCT(q_u);
+ ZERO_STRUCT(r_u);
+
+ if(!spoolss_io_q_setform("", &q_u, data, 0)) {
+ DEBUG(0,("spoolss_io_q_setform: unable to unmarshall SPOOL_Q_SETFORM.\n"));
+ return False;
+ }
+
+ r_u.status = _spoolss_setform(p, &q_u, &r_u);
+
+ if(!spoolss_io_r_setform("", &r_u, rdata, 0)) {
+ DEBUG(0,("spoolss_io_r_setform: unable to marshall SPOOL_R_SETFORM.\n"));
+ return False;
+ }
+
+ return True;
+}
+
+/****************************************************************************
+****************************************************************************/
+
+static BOOL api_spoolss_enumprintprocessors(pipes_struct *p)
+{
+ SPOOL_Q_ENUMPRINTPROCESSORS q_u;
+ SPOOL_R_ENUMPRINTPROCESSORS r_u;
+ prs_struct *data = &p->in_data.data;
+ prs_struct *rdata = &p->out_data.rdata;
+
+ ZERO_STRUCT(q_u);
+ ZERO_STRUCT(r_u);
+
+ if(!spoolss_io_q_enumprintprocessors("", &q_u, data, 0)) {
+ DEBUG(0,("spoolss_io_q_enumprintprocessors: unable to unmarshall SPOOL_Q_ENUMPRINTPROCESSORS.\n"));
+ return False;
+ }
+
+ r_u.status = _spoolss_enumprintprocessors(p, &q_u, &r_u);
+
+ if(!spoolss_io_r_enumprintprocessors("", &r_u, rdata, 0)) {
+ DEBUG(0,("spoolss_io_r_enumprintprocessors: unable to marshall SPOOL_R_ENUMPRINTPROCESSORS.\n"));
+ return False;
+ }
+
+ return True;
+}
+
+/****************************************************************************
+****************************************************************************/
+
+static BOOL api_spoolss_addprintprocessor(pipes_struct *p)
+{
+ SPOOL_Q_ADDPRINTPROCESSOR q_u;
+ SPOOL_R_ADDPRINTPROCESSOR r_u;
+ prs_struct *data = &p->in_data.data;
+ prs_struct *rdata = &p->out_data.rdata;
+
+ ZERO_STRUCT(q_u);
+ ZERO_STRUCT(r_u);
+
+ if(!spoolss_io_q_addprintprocessor("", &q_u, data, 0)) {
+ DEBUG(0,("spoolss_io_q_addprintprocessor: unable to unmarshall SPOOL_Q_ADDPRINTPROCESSOR.\n"));
+ return False;
+ }
+
+ /* for now, just indicate success and ignore the add. We'll
+ automatically set the winprint processor for printer
+ entries later. Used to debug the LexMark Optra S 1855 PCL
+ driver --jerry */
+ r_u.status = WERR_OK;
+
+ if(!spoolss_io_r_addprintprocessor("", &r_u, rdata, 0)) {
+ DEBUG(0,("spoolss_io_r_addprintprocessor: unable to marshall SPOOL_R_ADDPRINTPROCESSOR.\n"));
+ return False;
+ }
+
+ return True;
+}
+
+/****************************************************************************
+****************************************************************************/
+
+static BOOL api_spoolss_enumprintprocdatatypes(pipes_struct *p)
+{
+ SPOOL_Q_ENUMPRINTPROCDATATYPES q_u;
+ SPOOL_R_ENUMPRINTPROCDATATYPES r_u;
+ prs_struct *data = &p->in_data.data;
+ prs_struct *rdata = &p->out_data.rdata;
+
+ ZERO_STRUCT(q_u);
+ ZERO_STRUCT(r_u);
+
+ if(!spoolss_io_q_enumprintprocdatatypes("", &q_u, data, 0)) {
+ DEBUG(0,("spoolss_io_q_enumprintprocdatatypes: unable to unmarshall SPOOL_Q_ENUMPRINTPROCDATATYPES.\n"));
+ return False;
+ }
+
+ r_u.status = _spoolss_enumprintprocdatatypes(p, &q_u, &r_u);
+
+ if(!spoolss_io_r_enumprintprocdatatypes("", &r_u, rdata, 0)) {
+ DEBUG(0,("spoolss_io_r_enumprintprocdatatypes: unable to marshall SPOOL_R_ENUMPRINTPROCDATATYPES.\n"));
+ return False;
+ }
+
+ return True;
+}
+
+/****************************************************************************
+****************************************************************************/
+
+static BOOL api_spoolss_enumprintmonitors(pipes_struct *p)
+{
+ SPOOL_Q_ENUMPRINTMONITORS q_u;
+ SPOOL_R_ENUMPRINTMONITORS r_u;
+ prs_struct *data = &p->in_data.data;
+ prs_struct *rdata = &p->out_data.rdata;
+
+ ZERO_STRUCT(q_u);
+ ZERO_STRUCT(r_u);
+
+ if (!spoolss_io_q_enumprintmonitors("", &q_u, data, 0)) {
+ DEBUG(0,("spoolss_io_q_enumprintmonitors: unable to unmarshall SPOOL_Q_ENUMPRINTMONITORS.\n"));
+ return False;
+ }
+
+ r_u.status = _spoolss_enumprintmonitors(p, &q_u, &r_u);
+
+ if (!spoolss_io_r_enumprintmonitors("", &r_u, rdata, 0)) {
+ DEBUG(0,("spoolss_io_r_enumprintmonitors: unable to marshall SPOOL_R_ENUMPRINTMONITORS.\n"));
+ return False;
+ }
+
+ return True;
+}
+
+/****************************************************************************
+****************************************************************************/
+
+static BOOL api_spoolss_getjob(pipes_struct *p)
+{
+ SPOOL_Q_GETJOB q_u;
+ SPOOL_R_GETJOB r_u;
+ prs_struct *data = &p->in_data.data;
+ prs_struct *rdata = &p->out_data.rdata;
+
+ if(!spoolss_io_q_getjob("", &q_u, data, 0)) {
+ DEBUG(0,("spoolss_io_q_getjob: unable to unmarshall SPOOL_Q_GETJOB.\n"));
+ return False;
+ }
+
+ r_u.status = _spoolss_getjob(p, &q_u, &r_u);
+
+ if(!spoolss_io_r_getjob("",&r_u,rdata,0)) {
+ DEBUG(0,("spoolss_io_r_getjob: unable to marshall SPOOL_R_GETJOB.\n"));
+ return False;
+ }
+
+ return True;
+}
+
+/********************************************************************
+ * api_spoolss_getprinterdataex
+ *
+ * called from the spoolss dispatcher
+ ********************************************************************/
+
+static BOOL api_spoolss_getprinterdataex(pipes_struct *p)
+{
+ SPOOL_Q_GETPRINTERDATAEX q_u;
+ SPOOL_R_GETPRINTERDATAEX r_u;
+ prs_struct *data = &p->in_data.data;
+ prs_struct *rdata = &p->out_data.rdata;
+
+ ZERO_STRUCT(q_u);
+ ZERO_STRUCT(r_u);
+
+ /* read the stream and fill the struct */
+ if (!spoolss_io_q_getprinterdataex("", &q_u, data, 0)) {
+ DEBUG(0,("spoolss_io_q_getprinterdataex: unable to unmarshall SPOOL_Q_GETPRINTERDATAEX.\n"));
+ return False;
+ }
+
+ r_u.status = _spoolss_getprinterdataex( p, &q_u, &r_u);
+
+ if (!spoolss_io_r_getprinterdataex("", &r_u, rdata, 0)) {
+ DEBUG(0,("spoolss_io_r_getprinterdataex: unable to marshall SPOOL_R_GETPRINTERDATAEX.\n"));
+ return False;
+ }
+
+ return True;
+}
+
+/****************************************************************************
+****************************************************************************/
+
+static BOOL api_spoolss_setprinterdataex(pipes_struct *p)
+{
+ SPOOL_Q_SETPRINTERDATAEX q_u;
+ SPOOL_R_SETPRINTERDATAEX r_u;
+ prs_struct *data = &p->in_data.data;
+ prs_struct *rdata = &p->out_data.rdata;
+
+ ZERO_STRUCT(q_u);
+ ZERO_STRUCT(r_u);
+
+ if(!spoolss_io_q_setprinterdataex("", &q_u, data, 0)) {
+ DEBUG(0,("spoolss_io_q_setprinterdataex: unable to unmarshall SPOOL_Q_SETPRINTERDATAEX.\n"));
+ return False;
+ }
+
+ r_u.status = _spoolss_setprinterdataex(p, &q_u, &r_u);
+
+ if(!spoolss_io_r_setprinterdataex("", &r_u, rdata, 0)) {
+ DEBUG(0,("spoolss_io_r_setprinterdataex: unable to marshall SPOOL_R_SETPRINTERDATAEX.\n"));
+ return False;
+ }
+
+ return True;
+}
+
+
+/****************************************************************************
+****************************************************************************/
+
+static BOOL api_spoolss_enumprinterkey(pipes_struct *p)
+{
+ SPOOL_Q_ENUMPRINTERKEY q_u;
+ SPOOL_R_ENUMPRINTERKEY r_u;
+ prs_struct *data = &p->in_data.data;
+ prs_struct *rdata = &p->out_data.rdata;
+
+ ZERO_STRUCT(q_u);
+ ZERO_STRUCT(r_u);
+
+ if(!spoolss_io_q_enumprinterkey("", &q_u, data, 0)) {
+ DEBUG(0,("spoolss_io_q_setprinterkey: unable to unmarshall SPOOL_Q_ENUMPRINTERKEY.\n"));
+ return False;
+ }
+
+ r_u.status = _spoolss_enumprinterkey(p, &q_u, &r_u);
+
+ if(!spoolss_io_r_enumprinterkey("", &r_u, rdata, 0)) {
+ DEBUG(0,("spoolss_io_r_enumprinterkey: unable to marshall SPOOL_R_ENUMPRINTERKEY.\n"));
+ return False;
+ }
+
+ return True;
+}
+
+/****************************************************************************
+****************************************************************************/
+
+static BOOL api_spoolss_enumprinterdataex(pipes_struct *p)
+{
+ SPOOL_Q_ENUMPRINTERDATAEX q_u;
+ SPOOL_R_ENUMPRINTERDATAEX r_u;
+ prs_struct *data = &p->in_data.data;
+ prs_struct *rdata = &p->out_data.rdata;
+
+ ZERO_STRUCT(q_u);
+ ZERO_STRUCT(r_u);
+
+ if(!spoolss_io_q_enumprinterdataex("", &q_u, data, 0)) {
+ DEBUG(0,("spoolss_io_q_enumprinterdataex: unable to unmarshall SPOOL_Q_ENUMPRINTERDATAEX.\n"));
+ return False;
+ }
+
+ r_u.status = _spoolss_enumprinterdataex(p, &q_u, &r_u);
+
+ if(!spoolss_io_r_enumprinterdataex("", &r_u, rdata, 0)) {
+ DEBUG(0,("spoolss_io_r_enumprinterdataex: unable to marshall SPOOL_R_ENUMPRINTERDATAEX.\n"));
+ return False;
+ }
+
+ return True;
+}
+
+/****************************************************************************
+****************************************************************************/
+
+static BOOL api_spoolss_getprintprocessordirectory(pipes_struct *p)
+{
+ SPOOL_Q_GETPRINTPROCESSORDIRECTORY q_u;
+ SPOOL_R_GETPRINTPROCESSORDIRECTORY r_u;
+ prs_struct *data = &p->in_data.data;
+ prs_struct *rdata = &p->out_data.rdata;
+
+ ZERO_STRUCT(q_u);
+ ZERO_STRUCT(r_u);
+
+ if(!spoolss_io_q_getprintprocessordirectory("", &q_u, data, 0)) {
+ DEBUG(0,("spoolss_io_q_getprintprocessordirectory: unable to unmarshall SPOOL_Q_GETPRINTPROCESSORDIRECTORY.\n"));
+ return False;
+ }
+
+ r_u.status = _spoolss_getprintprocessordirectory(p, &q_u, &r_u);
+
+ if(!spoolss_io_r_getprintprocessordirectory("", &r_u, rdata, 0)) {
+ DEBUG(0,("spoolss_io_r_getprintprocessordirectory: unable to marshall SPOOL_R_GETPRINTPROCESSORDIRECTORY.\n"));
+ return False;
+ }
+
+ return True;
+}
+
+/****************************************************************************
+****************************************************************************/
+
+static BOOL api_spoolss_deleteprinterdataex(pipes_struct *p)
+{
+ SPOOL_Q_DELETEPRINTERDATAEX q_u;
+ SPOOL_R_DELETEPRINTERDATAEX r_u;
+ prs_struct *data = &p->in_data.data;
+ prs_struct *rdata = &p->out_data.rdata;
+
+ ZERO_STRUCT(q_u);
+ ZERO_STRUCT(r_u);
+
+ if(!spoolss_io_q_deleteprinterdataex("", &q_u, data, 0)) {
+ DEBUG(0,("spoolss_io_q_deleteprinterdataex: unable to unmarshall SPOOL_Q_DELETEPRINTERDATAEX.\n"));
+ return False;
+ }
+
+ r_u.status = _spoolss_deleteprinterdataex(p, &q_u, &r_u);
+
+ if(!spoolss_io_r_deleteprinterdataex("", &r_u, rdata, 0)) {
+ DEBUG(0,("spoolss_io_r_deleteprinterdataex: unable to marshall SPOOL_R_DELETEPRINTERDATAEX.\n"));
+ return False;
+ }
+
+ return True;
+}
+
+/****************************************************************************
+****************************************************************************/
+
+static BOOL api_spoolss_deleteprinterkey(pipes_struct *p)
+{
+ SPOOL_Q_DELETEPRINTERKEY q_u;
+ SPOOL_R_DELETEPRINTERKEY r_u;
+ prs_struct *data = &p->in_data.data;
+ prs_struct *rdata = &p->out_data.rdata;
+
+ ZERO_STRUCT(q_u);
+ ZERO_STRUCT(r_u);
+
+ if(!spoolss_io_q_deleteprinterkey("", &q_u, data, 0)) {
+ DEBUG(0,("spoolss_io_q_deleteprinterkey: unable to unmarshall SPOOL_Q_DELETEPRINTERKEY.\n"));
+ return False;
+ }
+
+ r_u.status = _spoolss_deleteprinterkey(p, &q_u, &r_u);
+
+ if(!spoolss_io_r_deleteprinterkey("", &r_u, rdata, 0)) {
+ DEBUG(0,("spoolss_io_r_deleteprinterkey: unable to marshall SPOOL_R_DELETEPRINTERKEY.\n"));
+ return False;
+ }
+
+ return True;
+}
+
+/****************************************************************************
+****************************************************************************/
+
+static BOOL api_spoolss_addprinterdriverex(pipes_struct *p)
+{
+ SPOOL_Q_ADDPRINTERDRIVEREX q_u;
+ SPOOL_R_ADDPRINTERDRIVEREX r_u;
+ prs_struct *data = &p->in_data.data;
+ prs_struct *rdata = &p->out_data.rdata;
+
+ ZERO_STRUCT(q_u);
+ ZERO_STRUCT(r_u);
+
+ if(!spoolss_io_q_addprinterdriverex("", &q_u, data, 0)) {
+ DEBUG(0,("spoolss_io_q_addprinterdriverex: unable to unmarshall SPOOL_Q_ADDPRINTERDRIVEREX.\n"));
+ return False;
+ }
+
+ r_u.status = _spoolss_addprinterdriverex(p, &q_u, &r_u);
+
+ if(!spoolss_io_r_addprinterdriverex("", &r_u, rdata, 0)) {
+ DEBUG(0,("spoolss_io_r_addprinterdriverex: unable to marshall SPOOL_R_ADDPRINTERDRIVEREX.\n"));
+ return False;
+ }
+
+ return True;
+}
+
+/****************************************************************************
+****************************************************************************/
+
+static BOOL api_spoolss_deleteprinterdriverex(pipes_struct *p)
+{
+ SPOOL_Q_DELETEPRINTERDRIVEREX q_u;
+ SPOOL_R_DELETEPRINTERDRIVEREX r_u;
+ prs_struct *data = &p->in_data.data;
+ prs_struct *rdata = &p->out_data.rdata;
+
+ ZERO_STRUCT(q_u);
+ ZERO_STRUCT(r_u);
+
+ if(!spoolss_io_q_deleteprinterdriverex("", &q_u, data, 0)) {
+ DEBUG(0,("spoolss_io_q_deleteprinterdriverex: unable to unmarshall SPOOL_Q_DELETEPRINTERDRIVEREX.\n"));
+ return False;
+ }
+
+ r_u.status = _spoolss_deleteprinterdriverex(p, &q_u, &r_u);
+
+ if(!spoolss_io_r_deleteprinterdriverex("", &r_u, rdata, 0)) {
+ DEBUG(0,("spoolss_io_r_deleteprinterdriverex: unable to marshall SPOOL_R_DELETEPRINTERDRIVEREX.\n"));
+ return False;
+ }
+
+ return True;
+}
+
+#if 0
+
+/****************************************************************************
+****************************************************************************/
+
+static BOOL api_spoolss_replyopenprinter(pipes_struct *p)
+{
+ SPOOL_Q_REPLYOPENPRINTER q_u;
+ SPOOL_R_REPLYOPENPRINTER r_u;
+ prs_struct *data = &p->in_data.data;
+ prs_struct *rdata = &p->out_data.rdata;
+
+ ZERO_STRUCT(q_u);
+ ZERO_STRUCT(r_u);
+
+ if(!spoolss_io_q_replyopenprinter("", &q_u, data, 0)) {
+ DEBUG(0,("spoolss_io_q_replyopenprinter: unable to unmarshall SPOOL_Q_REPLYOPENPRINTER.\n"));
+ return False;
+ }
+
+ r_u.status = _spoolss_replyopenprinter(p, &q_u, &r_u);
+
+ if(!spoolss_io_r_replyopenprinter("", &r_u, rdata, 0)) {
+ DEBUG(0,("spoolss_io_r_replyopenprinter: unable to marshall SPOOL_R_REPLYOPENPRINTER.\n"));
+ return False;
+ }
+
+ return True;
+}
+
+/****************************************************************************
+****************************************************************************/
+
+static BOOL api_spoolss_replycloseprinter(pipes_struct *p)
+{
+ SPOOL_Q_REPLYCLOSEPRINTER q_u;
+ SPOOL_R_REPLYCLOSEPRINTER r_u;
+ prs_struct *data = &p->in_data.data;
+ prs_struct *rdata = &p->out_data.rdata;
+
+ ZERO_STRUCT(q_u);
+ ZERO_STRUCT(r_u);
+
+ if(!spoolss_io_q_replycloseprinter("", &q_u, data, 0)) {
+ DEBUG(0,("spoolss_io_q_replycloseprinter: unable to unmarshall SPOOL_Q_REPLYCLOSEPRINTER.\n"));
+ return False;
+ }
+
+ r_u.status = _spoolss_replycloseprinter(p, &q_u, &r_u);
+
+ if(!spoolss_io_r_replycloseprinter("", &r_u, rdata, 0)) {
+ DEBUG(0,("spoolss_io_r_replycloseprinter: unable to marshall SPOOL_R_REPLYCLOSEPRINTER.\n"));
+ return False;
+ }
+
+ return True;
+}
+
+#endif
+
+/*******************************************************************
+\pipe\spoolss commands
+********************************************************************/
+
+ struct api_struct api_spoolss_cmds[] =
+ {
+ {"SPOOLSS_OPENPRINTER", SPOOLSS_OPENPRINTER, api_spoolss_open_printer },
+ {"SPOOLSS_OPENPRINTEREX", SPOOLSS_OPENPRINTEREX, api_spoolss_open_printer_ex },
+ {"SPOOLSS_GETPRINTERDATA", SPOOLSS_GETPRINTERDATA, api_spoolss_getprinterdata },
+ {"SPOOLSS_CLOSEPRINTER", SPOOLSS_CLOSEPRINTER, api_spoolss_closeprinter },
+ {"SPOOLSS_DELETEPRINTER", SPOOLSS_DELETEPRINTER, api_spoolss_deleteprinter },
+ {"SPOOLSS_ABORTPRINTER", SPOOLSS_ABORTPRINTER, api_spoolss_abortprinter },
+ {"SPOOLSS_RFFPCNEX", SPOOLSS_RFFPCNEX, api_spoolss_rffpcnex },
+ {"SPOOLSS_RFNPCNEX", SPOOLSS_RFNPCNEX, api_spoolss_rfnpcnex },
+ {"SPOOLSS_ENUMPRINTERS", SPOOLSS_ENUMPRINTERS, api_spoolss_enumprinters },
+ {"SPOOLSS_GETPRINTER", SPOOLSS_GETPRINTER, api_spoolss_getprinter },
+ {"SPOOLSS_GETPRINTERDRIVER2", SPOOLSS_GETPRINTERDRIVER2, api_spoolss_getprinterdriver2 },
+ {"SPOOLSS_STARTPAGEPRINTER", SPOOLSS_STARTPAGEPRINTER, api_spoolss_startpageprinter },
+ {"SPOOLSS_ENDPAGEPRINTER", SPOOLSS_ENDPAGEPRINTER, api_spoolss_endpageprinter },
+ {"SPOOLSS_STARTDOCPRINTER", SPOOLSS_STARTDOCPRINTER, api_spoolss_startdocprinter },
+ {"SPOOLSS_ENDDOCPRINTER", SPOOLSS_ENDDOCPRINTER, api_spoolss_enddocprinter },
+ {"SPOOLSS_WRITEPRINTER", SPOOLSS_WRITEPRINTER, api_spoolss_writeprinter },
+ {"SPOOLSS_SETPRINTER", SPOOLSS_SETPRINTER, api_spoolss_setprinter },
+ {"SPOOLSS_FCPN", SPOOLSS_FCPN, api_spoolss_fcpn },
+ {"SPOOLSS_ADDJOB", SPOOLSS_ADDJOB, api_spoolss_addjob },
+ {"SPOOLSS_ENUMJOBS", SPOOLSS_ENUMJOBS, api_spoolss_enumjobs },
+ {"SPOOLSS_SCHEDULEJOB", SPOOLSS_SCHEDULEJOB, api_spoolss_schedulejob },
+ {"SPOOLSS_SETJOB", SPOOLSS_SETJOB, api_spoolss_setjob },
+ {"SPOOLSS_ENUMFORMS", SPOOLSS_ENUMFORMS, api_spoolss_enumforms },
+ {"SPOOLSS_ENUMPORTS", SPOOLSS_ENUMPORTS, api_spoolss_enumports },
+ {"SPOOLSS_ENUMPRINTERDRIVERS", SPOOLSS_ENUMPRINTERDRIVERS, api_spoolss_enumprinterdrivers },
+ {"SPOOLSS_ADDPRINTEREX", SPOOLSS_ADDPRINTEREX, api_spoolss_addprinterex },
+ {"SPOOLSS_ADDPRINTERDRIVER", SPOOLSS_ADDPRINTERDRIVER, api_spoolss_addprinterdriver },
+ {"SPOOLSS_DELETEPRINTERDRIVER", SPOOLSS_DELETEPRINTERDRIVER, api_spoolss_deleteprinterdriver },
+ {"SPOOLSS_GETPRINTERDRIVERDIRECTORY", SPOOLSS_GETPRINTERDRIVERDIRECTORY, api_spoolss_getprinterdriverdirectory },
+ {"SPOOLSS_ENUMPRINTERDATA", SPOOLSS_ENUMPRINTERDATA, api_spoolss_enumprinterdata },
+ {"SPOOLSS_SETPRINTERDATA", SPOOLSS_SETPRINTERDATA, api_spoolss_setprinterdata },
+ {"SPOOLSS_RESETPRINTER", SPOOLSS_RESETPRINTER, api_spoolss_reset_printer },
+ {"SPOOLSS_DELETEPRINTERDATA", SPOOLSS_DELETEPRINTERDATA, api_spoolss_deleteprinterdata },
+ {"SPOOLSS_ADDFORM", SPOOLSS_ADDFORM, api_spoolss_addform },
+ {"SPOOLSS_DELETEFORM", SPOOLSS_DELETEFORM, api_spoolss_deleteform },
+ {"SPOOLSS_GETFORM", SPOOLSS_GETFORM, api_spoolss_getform },
+ {"SPOOLSS_SETFORM", SPOOLSS_SETFORM, api_spoolss_setform },
+ {"SPOOLSS_ADDPRINTPROCESSOR", SPOOLSS_ADDPRINTPROCESSOR, api_spoolss_addprintprocessor },
+ {"SPOOLSS_ENUMPRINTPROCESSORS", SPOOLSS_ENUMPRINTPROCESSORS, api_spoolss_enumprintprocessors },
+ {"SPOOLSS_ENUMMONITORS", SPOOLSS_ENUMMONITORS, api_spoolss_enumprintmonitors },
+ {"SPOOLSS_GETJOB", SPOOLSS_GETJOB, api_spoolss_getjob },
+ {"SPOOLSS_ENUMPRINTPROCDATATYPES", SPOOLSS_ENUMPRINTPROCDATATYPES, api_spoolss_enumprintprocdatatypes },
+ {"SPOOLSS_GETPRINTERDATAEX", SPOOLSS_GETPRINTERDATAEX, api_spoolss_getprinterdataex },
+ {"SPOOLSS_SETPRINTERDATAEX", SPOOLSS_SETPRINTERDATAEX, api_spoolss_setprinterdataex },
+ {"SPOOLSS_DELETEPRINTERDATAEX", SPOOLSS_DELETEPRINTERDATAEX, api_spoolss_deleteprinterdataex },
+ {"SPOOLSS_ENUMPRINTERDATAEX", SPOOLSS_ENUMPRINTERDATAEX, api_spoolss_enumprinterdataex },
+ {"SPOOLSS_ENUMPRINTERKEY", SPOOLSS_ENUMPRINTERKEY, api_spoolss_enumprinterkey },
+ {"SPOOLSS_DELETEPRINTERKEY", SPOOLSS_DELETEPRINTERKEY, api_spoolss_deleteprinterkey },
+ {"SPOOLSS_GETPRINTPROCESSORDIRECTORY",SPOOLSS_GETPRINTPROCESSORDIRECTORY,api_spoolss_getprintprocessordirectory},
+ {"SPOOLSS_ADDPRINTERDRIVEREX", SPOOLSS_ADDPRINTERDRIVEREX, api_spoolss_addprinterdriverex },
+ {"SPOOLSS_DELETEPRINTERDRIVEREX", SPOOLSS_DELETEPRINTERDRIVEREX, api_spoolss_deleteprinterdriverex },
+#if 0
+ {"SPOOLSS_REPLYOPENPRINTER", SPOOLSS_REPLYOPENPRINTER, api_spoolss_replyopenprinter },
+ {"SPOOLSS_REPLYCLOSEPRINTER", SPOOLSS_REPLYCLOSEPRINTER, api_spoolss_replycloseprinter }
+#endif
+ };
+
+void spoolss_get_pipe_fns( struct api_struct **fns, int *n_fns )
+{
+ *fns = api_spoolss_cmds;
+ *n_fns = sizeof(api_spoolss_cmds) / sizeof(struct api_struct);
+}
+
+NTSTATUS rpc_spoolss_init(void)
+{
+ return rpc_pipe_register_commands(SMB_RPC_INTERFACE_VERSION, "spoolss", "spoolss", api_spoolss_cmds,
+ sizeof(api_spoolss_cmds) / sizeof(struct api_struct));
+}
diff --git a/source/rpc_server/srv_spoolss_nt.c b/source/rpc_server/srv_spoolss_nt.c
new file mode 100644
index 00000000000..edd62fa8f62
--- /dev/null
+++ b/source/rpc_server/srv_spoolss_nt.c
@@ -0,0 +1,9188 @@
+/*
+ * Unix SMB/CIFS implementation.
+ * RPC Pipe client / server routines
+ * Copyright (C) Andrew Tridgell 1992-2000,
+ * Copyright (C) Luke Kenneth Casson Leighton 1996-2000,
+ * Copyright (C) Jean François Micouleau 1998-2000,
+ * Copyright (C) Jeremy Allison 2001-2002,
+ * Copyright (C) Gerald Carter 2000-2003,
+ * Copyright (C) Tim Potter 2001-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 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.
+ */
+
+/* Since the SPOOLSS rpc routines are basically DOS 16-bit calls wrapped
+ up, all the errors returned are DOS errors, not NT status codes. */
+
+#include "includes.h"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_RPC_SRV
+
+#ifndef MAX_OPEN_PRINTER_EXS
+#define MAX_OPEN_PRINTER_EXS 50
+#endif
+
+#define MAGIC_DISPLAY_FREQUENCY 0xfade2bad
+#define PHANTOM_DEVMODE_KEY "_p_f_a_n_t_0_m_"
+
+
+/* Table to map the driver version */
+/* to OS */
+static const char * drv_ver_to_os[] = {
+ "WIN9X", /* driver version/cversion 0 */
+ "", /* unused ? */
+ "WINNT", /* driver version/cversion 2 */
+ "WIN2K", /* driver version/cversion 3 */
+};
+
+static const char *get_drv_ver_to_os(int ver)
+{
+ if (ver < 0 || ver > 3)
+ return "";
+ return drv_ver_to_os[ver];
+}
+
+struct table_node {
+ const char *long_archi;
+ const char *short_archi;
+ int version;
+};
+
+static Printer_entry *printers_list;
+
+typedef struct _counter_printer_0 {
+ ubi_dlNode Next;
+ ubi_dlNode Prev;
+
+ int snum;
+ uint32 counter;
+} counter_printer_0;
+
+static ubi_dlList counter_list;
+
+static struct cli_state notify_cli; /* print notify back-channel */
+static uint32 smb_connections=0;
+
+
+/* in printing/nt_printing.c */
+
+extern STANDARD_MAPPING printer_std_mapping, printserver_std_mapping;
+
+#define OUR_HANDLE(hnd) (((hnd)==NULL)?"NULL":(IVAL((hnd)->data5,4)==(uint32)sys_getpid()?"OURS":"OTHER")), \
+((unsigned int)IVAL((hnd)->data5,4)),((unsigned int)sys_getpid())
+
+/* translate between internal status numbers and NT status numbers */
+static int nt_printj_status(int v)
+{
+ switch (v) {
+ case LPQ_QUEUED:
+ return 0;
+ case LPQ_PAUSED:
+ return JOB_STATUS_PAUSED;
+ case LPQ_SPOOLING:
+ return JOB_STATUS_SPOOLING;
+ case LPQ_PRINTING:
+ return JOB_STATUS_PRINTING;
+ case LPQ_ERROR:
+ return JOB_STATUS_ERROR;
+ case LPQ_DELETING:
+ return JOB_STATUS_DELETING;
+ case LPQ_OFFLINE:
+ return JOB_STATUS_OFFLINE;
+ case LPQ_PAPEROUT:
+ return JOB_STATUS_PAPEROUT;
+ case LPQ_PRINTED:
+ return JOB_STATUS_PRINTED;
+ case LPQ_DELETED:
+ return JOB_STATUS_DELETED;
+ case LPQ_BLOCKED:
+ return JOB_STATUS_BLOCKED;
+ case LPQ_USER_INTERVENTION:
+ return JOB_STATUS_USER_INTERVENTION;
+ }
+ return 0;
+}
+
+static int nt_printq_status(int v)
+{
+ switch (v) {
+ case LPQ_PAUSED:
+ return PRINTER_STATUS_PAUSED;
+ case LPQ_QUEUED:
+ case LPQ_SPOOLING:
+ case LPQ_PRINTING:
+ return 0;
+ }
+ return 0;
+}
+
+/****************************************************************************
+ Functions to handle SPOOL_NOTIFY_OPTION struct stored in Printer_entry.
+****************************************************************************/
+
+static void free_spool_notify_option(SPOOL_NOTIFY_OPTION **pp)
+{
+ if (*pp == NULL)
+ return;
+
+ SAFE_FREE((*pp)->ctr.type);
+ SAFE_FREE(*pp);
+}
+
+/***************************************************************************
+ Disconnect from the client
+****************************************************************************/
+
+static void srv_spoolss_replycloseprinter(int snum, POLICY_HND *handle)
+{
+ WERROR result;
+
+ /*
+ * Tell the specific printing tdb we no longer want messages for this printer
+ * by deregistering our PID.
+ */
+
+ if (!print_notify_deregister_pid(snum))
+ DEBUG(0,("print_notify_register_pid: Failed to register our pid for printer %s\n", lp_const_servicename(snum) ));
+
+ /* weird if the test succeds !!! */
+ if (smb_connections==0) {
+ DEBUG(0,("srv_spoolss_replycloseprinter:Trying to close non-existant notify backchannel !\n"));
+ return;
+ }
+
+ result = cli_spoolss_reply_close_printer(&notify_cli, notify_cli.mem_ctx, handle);
+
+ if (!W_ERROR_IS_OK(result))
+ DEBUG(0,("srv_spoolss_replycloseprinter: reply_close_printer failed [%s].\n",
+ dos_errstr(result)));
+
+ /* if it's the last connection, deconnect the IPC$ share */
+ if (smb_connections==1) {
+ cli_nt_session_close(&notify_cli);
+ cli_ulogoff(&notify_cli);
+ cli_shutdown(&notify_cli);
+ message_deregister(MSG_PRINTER_NOTIFY2);
+
+ /* Tell the connections db we're no longer interested in
+ * printer notify messages. */
+
+ register_message_flags( False, FLAG_MSG_PRINTING );
+ }
+
+ smb_connections--;
+}
+
+/****************************************************************************
+ Functions to free a printer entry datastruct.
+****************************************************************************/
+
+static void free_printer_entry(void *ptr)
+{
+ Printer_entry *Printer = (Printer_entry *)ptr;
+
+ if (Printer->notify.client_connected==True) {
+ int snum = -1;
+
+ if ( Printer->printer_type == PRINTER_HANDLE_IS_PRINTSERVER) {
+ snum = -1;
+ srv_spoolss_replycloseprinter(snum, &Printer->notify.client_hnd);
+ } else if (Printer->printer_type == PRINTER_HANDLE_IS_PRINTER) {
+ snum = print_queue_snum(Printer->dev.handlename);
+ if (snum != -1)
+ srv_spoolss_replycloseprinter(snum,
+ &Printer->notify.client_hnd);
+ }
+ }
+
+ Printer->notify.flags=0;
+ Printer->notify.options=0;
+ Printer->notify.localmachine[0]='\0';
+ Printer->notify.printerlocal=0;
+ free_spool_notify_option(&Printer->notify.option);
+ Printer->notify.option=NULL;
+ Printer->notify.client_connected=False;
+
+ free_nt_devicemode( &Printer->nt_devmode );
+ free_a_printer( &Printer->printer_info, 2 );
+
+ talloc_destroy( Printer->ctx );
+
+ /* Remove from the internal list. */
+ DLIST_REMOVE(printers_list, Printer);
+
+ SAFE_FREE(Printer);
+}
+
+/****************************************************************************
+ Functions to duplicate a SPOOL_NOTIFY_OPTION struct stored in Printer_entry.
+****************************************************************************/
+
+static SPOOL_NOTIFY_OPTION *dup_spool_notify_option(SPOOL_NOTIFY_OPTION *sp)
+{
+ SPOOL_NOTIFY_OPTION *new_sp = NULL;
+
+ if (!sp)
+ return NULL;
+
+ new_sp = (SPOOL_NOTIFY_OPTION *)malloc(sizeof(SPOOL_NOTIFY_OPTION));
+ if (!new_sp)
+ return NULL;
+
+ *new_sp = *sp;
+
+ if (sp->ctr.count) {
+ new_sp->ctr.type = (SPOOL_NOTIFY_OPTION_TYPE *)memdup(sp->ctr.type, sizeof(SPOOL_NOTIFY_OPTION_TYPE) * sp->ctr.count);
+
+ if (!new_sp->ctr.type) {
+ SAFE_FREE(new_sp);
+ return NULL;
+ }
+ }
+
+ return new_sp;
+}
+
+/****************************************************************************
+ find printer index by handle
+****************************************************************************/
+
+static Printer_entry *find_printer_index_by_hnd(pipes_struct *p, POLICY_HND *hnd)
+{
+ Printer_entry *find_printer = NULL;
+
+ if(!find_policy_by_hnd(p,hnd,(void **)&find_printer)) {
+ DEBUG(2,("find_printer_index_by_hnd: Printer handle not found: "));
+ return NULL;
+ }
+
+ return find_printer;
+}
+
+/****************************************************************************
+ look for a printer object cached on an open printer handle
+****************************************************************************/
+
+WERROR find_printer_in_print_hnd_cache( TALLOC_CTX *ctx, NT_PRINTER_INFO_LEVEL_2 **info2,
+ const char *printername )
+{
+ Printer_entry *p;
+
+ DEBUG(10,("find_printer_in_print_hnd_cache: printer [%s]\n", printername));
+
+ for ( p=printers_list; p; p=p->next )
+ {
+ if ( p->printer_type==PRINTER_HANDLE_IS_PRINTER
+ && p->printer_info
+ && StrCaseCmp(p->dev.handlename, printername) == 0 )
+ {
+ DEBUG(10,("Found printer\n"));
+ *info2 = dup_printer_2( ctx, p->printer_info->info_2 );
+ if ( *info2 )
+ return WERR_OK;
+ }
+ }
+
+ return WERR_INVALID_PRINTER_NAME;
+}
+
+/****************************************************************************
+ destroy any cached printer_info_2 structures on open handles
+****************************************************************************/
+
+void invalidate_printer_hnd_cache( char *printername )
+{
+ Printer_entry *p;
+
+ DEBUG(10,("invalidate_printer_hnd_cache: printer [%s]\n", printername));
+
+ for ( p=printers_list; p; p=p->next )
+ {
+ if ( p->printer_type==PRINTER_HANDLE_IS_PRINTER
+ && StrCaseCmp(p->dev.handlename, printername)==0)
+ {
+ DEBUG(10,("invalidating printer_info cache for handl:\n"));
+ free_a_printer( &p->printer_info, 2 );
+ p->printer_info = NULL;
+ }
+ }
+
+ return;
+}
+/****************************************************************************
+ Close printer index by handle.
+****************************************************************************/
+
+static BOOL close_printer_handle(pipes_struct *p, POLICY_HND *hnd)
+{
+ Printer_entry *Printer = find_printer_index_by_hnd(p, hnd);
+
+ if (!Printer) {
+ DEBUG(2,("close_printer_handle: Invalid handle (%s:%u:%u)\n", OUR_HANDLE(hnd)));
+ return False;
+ }
+
+ close_policy_hnd(p, hnd);
+
+ return True;
+}
+
+/****************************************************************************
+ Delete a printer given a handle.
+****************************************************************************/
+
+static WERROR delete_printer_handle(pipes_struct *p, POLICY_HND *hnd)
+{
+ Printer_entry *Printer = find_printer_index_by_hnd(p, hnd);
+
+ if (!Printer) {
+ DEBUG(2,("delete_printer_handle: Invalid handle (%s:%u:%u)\n", OUR_HANDLE(hnd)));
+ return WERR_BADFID;
+ }
+
+ /*
+ * It turns out that Windows allows delete printer on a handle
+ * opened by an admin user, then used on a pipe handle created
+ * by an anonymous user..... but they're working on security.... riiight !
+ * JRA.
+ */
+
+ if (Printer->access_granted != PRINTER_ACCESS_ADMINISTER) {
+ DEBUG(3, ("delete_printer_handle: denied by handle\n"));
+ return WERR_ACCESS_DENIED;
+ }
+
+#if 0
+ /* Check calling user has permission to delete printer. Note that
+ since we set the snum parameter to -1 only administrators can
+ delete the printer. This stops people with the Full Control
+ permission from deleting the printer. */
+
+ if (!print_access_check(NULL, -1, PRINTER_ACCESS_ADMINISTER)) {
+ DEBUG(3, ("printer delete denied by security descriptor\n"));
+ return WERR_ACCESS_DENIED;
+ }
+#endif
+
+ if (del_a_printer(Printer->dev.handlename) != 0) {
+ DEBUG(3,("Error deleting printer %s\n", Printer->dev.handlename));
+ return WERR_BADFID;
+ }
+
+ if (*lp_deleteprinter_cmd()) {
+
+ char *cmd = lp_deleteprinter_cmd();
+ pstring command;
+ int ret;
+
+ /* Printer->dev.handlename equals portname equals sharename */
+ slprintf(command, sizeof(command)-1, "%s \"%s\"", cmd,
+ Printer->dev.handlename);
+
+ DEBUG(10,("Running [%s]\n", command));
+ ret = smbrun(command, NULL);
+ if (ret != 0) {
+ return WERR_BADFID; /* What to return here? */
+ }
+ DEBUGADD(10,("returned [%d]\n", ret));
+
+ /* Send SIGHUP to process group... is there a better way? */
+ kill(0, SIGHUP);
+
+ /* go ahead and re-read the services immediately */
+ reload_services( False );
+
+ if ( lp_servicenumber( Printer->dev.handlename ) < 0 )
+ return WERR_ACCESS_DENIED;
+ }
+
+ return WERR_OK;
+}
+
+/****************************************************************************
+ Return the snum of a printer corresponding to an handle.
+****************************************************************************/
+
+static BOOL get_printer_snum(pipes_struct *p, POLICY_HND *hnd, int *number)
+{
+ Printer_entry *Printer = find_printer_index_by_hnd(p, hnd);
+
+ if (!Printer) {
+ DEBUG(2,("get_printer_snum: Invalid handle (%s:%u:%u)\n", OUR_HANDLE(hnd)));
+ return False;
+ }
+
+ switch (Printer->printer_type) {
+ case PRINTER_HANDLE_IS_PRINTER:
+ DEBUG(4,("short name:%s\n", Printer->dev.handlename));
+ *number = print_queue_snum(Printer->dev.handlename);
+ return (*number != -1);
+ case PRINTER_HANDLE_IS_PRINTSERVER:
+ return False;
+ default:
+ return False;
+ }
+}
+
+/****************************************************************************
+ Set printer handle type.
+ Check if it's \\server or \\server\printer
+****************************************************************************/
+
+static BOOL set_printer_hnd_printertype(Printer_entry *Printer, char *handlename)
+{
+ DEBUG(3,("Setting printer type=%s\n", handlename));
+
+ if ( strlen(handlename) < 3 ) {
+ DEBUGADD(4,("A print server must have at least 1 char ! %s\n", handlename));
+ return False;
+ }
+
+ /* it's a print server */
+ if (*handlename=='\\' && *(handlename+1)=='\\' && !strchr_m(handlename+2, '\\')) {
+ DEBUGADD(4,("Printer is a print server\n"));
+ Printer->printer_type = PRINTER_HANDLE_IS_PRINTSERVER;
+ }
+ /* it's a printer */
+ else {
+ DEBUGADD(4,("Printer is a printer\n"));
+ Printer->printer_type = PRINTER_HANDLE_IS_PRINTER;
+ }
+
+ return True;
+}
+
+/****************************************************************************
+ Set printer handle name.
+****************************************************************************/
+
+static BOOL set_printer_hnd_name(Printer_entry *Printer, char *handlename)
+{
+ int snum;
+ int n_services=lp_numservices();
+ char *aprinter;
+ fstring sname;
+ BOOL found=False;
+
+ DEBUG(4,("Setting printer name=%s (len=%lu)\n", handlename, (unsigned long)strlen(handlename)));
+
+ if (Printer->printer_type==PRINTER_HANDLE_IS_PRINTSERVER) {
+ ZERO_STRUCT(Printer->dev.printerservername);
+ strncpy(Printer->dev.printerservername, handlename, strlen(handlename));
+ return True;
+ }
+
+ if (Printer->printer_type!=PRINTER_HANDLE_IS_PRINTER)
+ return False;
+
+ if (*handlename=='\\') {
+ aprinter=strchr_m(handlename+2, '\\');
+ aprinter++;
+ }
+ else {
+ aprinter=handlename;
+ }
+
+ DEBUGADD(5,("searching for [%s] (len=%lu)\n", aprinter, (unsigned long)strlen(aprinter)));
+
+ /*
+ * The original code allowed smbd to store a printer name that
+ * was different from the share name. This is not possible
+ * anymore, so I've simplified this loop greatly. Here
+ * we are just verifying that the printer name is a valid
+ * printer service defined in smb.conf
+ * --jerry [Fri Feb 15 11:17:46 CST 2002]
+ */
+
+ for (snum=0; snum<n_services; snum++) {
+
+ if ( !(lp_snum_ok(snum) && lp_print_ok(snum) ) )
+ continue;
+
+ fstrcpy(sname, lp_servicename(snum));
+
+ DEBUGADD(5,("share:%s\n",sname));
+
+ if (! StrCaseCmp(sname, aprinter)) {
+ found = True;
+ break;
+ }
+
+ }
+
+
+ if (!found) {
+ DEBUGADD(4,("Printer not found\n"));
+ return False;
+ }
+
+ DEBUGADD(4,("set_printer_hnd_name: Printer found: %s -> %s\n", aprinter, sname));
+
+ ZERO_STRUCT(Printer->dev.handlename);
+ fstrcpy(Printer->dev.handlename, sname);
+
+ return True;
+}
+
+/****************************************************************************
+ Find first available printer slot. creates a printer handle for you.
+ ****************************************************************************/
+
+static BOOL open_printer_hnd(pipes_struct *p, POLICY_HND *hnd, char *name, uint32 access_granted)
+{
+ Printer_entry *new_printer;
+
+ DEBUG(10,("open_printer_hnd: name [%s]\n", name));
+
+ if((new_printer=(Printer_entry *)malloc(sizeof(Printer_entry))) == NULL)
+ return False;
+
+ ZERO_STRUCTP(new_printer);
+
+ if (!create_policy_hnd(p, hnd, free_printer_entry, new_printer)) {
+ SAFE_FREE(new_printer);
+ return False;
+ }
+
+ /* Add to the internal list. */
+ DLIST_ADD(printers_list, new_printer);
+
+ new_printer->notify.option=NULL;
+
+ if ( !(new_printer->ctx = talloc_init("Printer Entry [%p]", hnd)) ) {
+ DEBUG(0,("open_printer_hnd: talloc_init() failed!\n"));
+ close_printer_handle(p, hnd);
+ return False;
+ }
+
+ if (!set_printer_hnd_printertype(new_printer, name)) {
+ close_printer_handle(p, hnd);
+ return False;
+ }
+
+ if (!set_printer_hnd_name(new_printer, name)) {
+ close_printer_handle(p, hnd);
+ return False;
+ }
+
+ new_printer->access_granted = access_granted;
+
+ DEBUG(5, ("%d printer handles active\n", (int)p->pipe_handles->count ));
+
+ return True;
+}
+
+/****************************************************************************
+ Allocate more memory for a BUFFER.
+****************************************************************************/
+
+static BOOL alloc_buffer_size(NEW_BUFFER *buffer, uint32 buffer_size)
+{
+ prs_struct *ps;
+ uint32 extra_space;
+ uint32 old_offset;
+
+ ps= &buffer->prs;
+
+ /* damn, I'm doing the reverse operation of prs_grow() :) */
+ if (buffer_size < prs_data_size(ps))
+ extra_space=0;
+ else
+ extra_space = buffer_size - prs_data_size(ps);
+
+ /*
+ * save the offset and move to the end of the buffer
+ * prs_grow() checks the extra_space against the offset
+ */
+ old_offset=prs_offset(ps);
+ prs_set_offset(ps, prs_data_size(ps));
+
+ if (!prs_grow(ps, extra_space))
+ return False;
+
+ prs_set_offset(ps, old_offset);
+
+ buffer->string_at_end=prs_data_size(ps);
+
+ return True;
+}
+
+/***************************************************************************
+ check to see if the client motify handle is monitoring the notification
+ given by (notify_type, notify_field).
+ **************************************************************************/
+
+static BOOL is_monitoring_event_flags(uint32 flags, uint16 notify_type,
+ uint16 notify_field)
+{
+ return True;
+}
+
+static BOOL is_monitoring_event(Printer_entry *p, uint16 notify_type,
+ uint16 notify_field)
+{
+ SPOOL_NOTIFY_OPTION *option = p->notify.option;
+ uint32 i, j;
+
+ /*
+ * Flags should always be zero when the change notify
+ * is registered by the client's spooler. A user Win32 app
+ * might use the flags though instead of the NOTIFY_OPTION_INFO
+ * --jerry
+ */
+
+ if (p->notify.flags)
+ return is_monitoring_event_flags(
+ p->notify.flags, notify_type, notify_field);
+
+ for (i = 0; i < option->count; i++) {
+
+ /* Check match for notify_type */
+
+ if (option->ctr.type[i].type != notify_type)
+ continue;
+
+ /* Check match for field */
+
+ for (j = 0; j < option->ctr.type[i].count; j++) {
+ if (option->ctr.type[i].fields[j] == notify_field) {
+ return True;
+ }
+ }
+ }
+
+ DEBUG(10, ("%s is not monitoring 0x%02x/0x%02x\n",
+ (p->printer_type == PRINTER_HANDLE_IS_PRINTER) ?
+ p->dev.handlename : p->dev.printerservername,
+ notify_type, notify_field));
+
+ return False;
+}
+
+/* Convert a notification message to a SPOOL_NOTIFY_INFO_DATA struct */
+
+static void notify_one_value(struct spoolss_notify_msg *msg,
+ SPOOL_NOTIFY_INFO_DATA *data,
+ TALLOC_CTX *mem_ctx)
+{
+ data->notify_data.value[0] = msg->notify.value[0];
+ data->notify_data.value[1] = 0;
+}
+
+static void notify_string(struct spoolss_notify_msg *msg,
+ SPOOL_NOTIFY_INFO_DATA *data,
+ TALLOC_CTX *mem_ctx)
+{
+ UNISTR2 unistr;
+
+ /* The length of the message includes the trailing \0 */
+
+ init_unistr2(&unistr, msg->notify.data, UNI_STR_TERMINATE);
+
+ data->notify_data.data.length = msg->len * 2;
+ data->notify_data.data.string = (uint16 *)talloc(mem_ctx, msg->len * 2);
+
+ if (!data->notify_data.data.string) {
+ data->notify_data.data.length = 0;
+ return;
+ }
+
+ memcpy(data->notify_data.data.string, unistr.buffer, msg->len * 2);
+}
+
+static void notify_system_time(struct spoolss_notify_msg *msg,
+ SPOOL_NOTIFY_INFO_DATA *data,
+ TALLOC_CTX *mem_ctx)
+{
+ SYSTEMTIME systime;
+ prs_struct ps;
+
+ if (msg->len != sizeof(time_t)) {
+ DEBUG(5, ("notify_system_time: received wrong sized message (%d)\n",
+ msg->len));
+ return;
+ }
+
+ if (!prs_init(&ps, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL)) {
+ DEBUG(5, ("notify_system_time: prs_init() failed\n"));
+ return;
+ }
+
+ if (!make_systemtime(&systime, gmtime((time_t *)msg->notify.data))) {
+ DEBUG(5, ("notify_system_time: unable to make systemtime\n"));
+ return;
+ }
+
+ if (!spoolss_io_system_time("", &ps, 0, &systime))
+ return;
+
+ data->notify_data.data.length = prs_offset(&ps);
+ data->notify_data.data.string = talloc(mem_ctx, prs_offset(&ps));
+
+ prs_copy_all_data_out((char *)data->notify_data.data.string, &ps);
+
+ prs_mem_free(&ps);
+}
+
+struct notify2_message_table {
+ const char *name;
+ void (*fn)(struct spoolss_notify_msg *msg,
+ SPOOL_NOTIFY_INFO_DATA *data, TALLOC_CTX *mem_ctx);
+};
+
+static struct notify2_message_table printer_notify_table[] = {
+ /* 0x00 */ { "PRINTER_NOTIFY_SERVER_NAME", notify_string },
+ /* 0x01 */ { "PRINTER_NOTIFY_PRINTER_NAME", notify_string },
+ /* 0x02 */ { "PRINTER_NOTIFY_SHARE_NAME", notify_string },
+ /* 0x03 */ { "PRINTER_NOTIFY_PORT_NAME", notify_string },
+ /* 0x04 */ { "PRINTER_NOTIFY_DRIVER_NAME", notify_string },
+ /* 0x05 */ { "PRINTER_NOTIFY_COMMENT", notify_string },
+ /* 0x06 */ { "PRINTER_NOTIFY_LOCATION", notify_string },
+ /* 0x07 */ { "PRINTER_NOTIFY_DEVMODE", NULL },
+ /* 0x08 */ { "PRINTER_NOTIFY_SEPFILE", notify_string },
+ /* 0x09 */ { "PRINTER_NOTIFY_PRINT_PROCESSOR", notify_string },
+ /* 0x0a */ { "PRINTER_NOTIFY_PARAMETERS", NULL },
+ /* 0x0b */ { "PRINTER_NOTIFY_DATATYPE", notify_string },
+ /* 0x0c */ { "PRINTER_NOTIFY_SECURITY_DESCRIPTOR", NULL },
+ /* 0x0d */ { "PRINTER_NOTIFY_ATTRIBUTES", notify_one_value },
+ /* 0x0e */ { "PRINTER_NOTIFY_PRIORITY", notify_one_value },
+ /* 0x0f */ { "PRINTER_NOTIFY_DEFAULT_PRIORITY", NULL },
+ /* 0x10 */ { "PRINTER_NOTIFY_START_TIME", NULL },
+ /* 0x11 */ { "PRINTER_NOTIFY_UNTIL_TIME", NULL },
+ /* 0x12 */ { "PRINTER_NOTIFY_STATUS", notify_one_value },
+};
+
+static struct notify2_message_table job_notify_table[] = {
+ /* 0x00 */ { "JOB_NOTIFY_PRINTER_NAME", NULL },
+ /* 0x01 */ { "JOB_NOTIFY_MACHINE_NAME", NULL },
+ /* 0x02 */ { "JOB_NOTIFY_PORT_NAME", NULL },
+ /* 0x03 */ { "JOB_NOTIFY_USER_NAME", notify_string },
+ /* 0x04 */ { "JOB_NOTIFY_NOTIFY_NAME", NULL },
+ /* 0x05 */ { "JOB_NOTIFY_DATATYPE", NULL },
+ /* 0x06 */ { "JOB_NOTIFY_PRINT_PROCESSOR", NULL },
+ /* 0x07 */ { "JOB_NOTIFY_PARAMETERS", NULL },
+ /* 0x08 */ { "JOB_NOTIFY_DRIVER_NAME", NULL },
+ /* 0x09 */ { "JOB_NOTIFY_DEVMODE", NULL },
+ /* 0x0a */ { "JOB_NOTIFY_STATUS", notify_one_value },
+ /* 0x0b */ { "JOB_NOTIFY_STATUS_STRING", NULL },
+ /* 0x0c */ { "JOB_NOTIFY_SECURITY_DESCRIPTOR", NULL },
+ /* 0x0d */ { "JOB_NOTIFY_DOCUMENT", notify_string },
+ /* 0x0e */ { "JOB_NOTIFY_PRIORITY", NULL },
+ /* 0x0f */ { "JOB_NOTIFY_POSITION", NULL },
+ /* 0x10 */ { "JOB_NOTIFY_SUBMITTED", notify_system_time },
+ /* 0x11 */ { "JOB_NOTIFY_START_TIME", NULL },
+ /* 0x12 */ { "JOB_NOTIFY_UNTIL_TIME", NULL },
+ /* 0x13 */ { "JOB_NOTIFY_TIME", NULL },
+ /* 0x14 */ { "JOB_NOTIFY_TOTAL_PAGES", notify_one_value },
+ /* 0x15 */ { "JOB_NOTIFY_PAGES_PRINTED", NULL },
+ /* 0x16 */ { "JOB_NOTIFY_TOTAL_BYTES", notify_one_value },
+ /* 0x17 */ { "JOB_NOTIFY_BYTES_PRINTED", NULL },
+};
+
+
+/***********************************************************************
+ Allocate talloc context for container object
+ **********************************************************************/
+
+static void notify_msg_ctr_init( SPOOLSS_NOTIFY_MSG_CTR *ctr )
+{
+ if ( !ctr )
+ return;
+
+ ctr->ctx = talloc_init("notify_msg_ctr_init %p", ctr);
+
+ return;
+}
+
+/***********************************************************************
+ release all allocated memory and zero out structure
+ **********************************************************************/
+
+static void notify_msg_ctr_destroy( SPOOLSS_NOTIFY_MSG_CTR *ctr )
+{
+ if ( !ctr )
+ return;
+
+ if ( ctr->ctx )
+ talloc_destroy(ctr->ctx);
+
+ ZERO_STRUCTP(ctr);
+
+ return;
+}
+
+/***********************************************************************
+ **********************************************************************/
+
+static TALLOC_CTX* notify_ctr_getctx( SPOOLSS_NOTIFY_MSG_CTR *ctr )
+{
+ if ( !ctr )
+ return NULL;
+
+ return ctr->ctx;
+}
+
+/***********************************************************************
+ **********************************************************************/
+
+static SPOOLSS_NOTIFY_MSG_GROUP* notify_ctr_getgroup( SPOOLSS_NOTIFY_MSG_CTR *ctr, uint32 idx )
+{
+ if ( !ctr || !ctr->msg_groups )
+ return NULL;
+
+ if ( idx >= ctr->num_groups )
+ return NULL;
+
+ return &ctr->msg_groups[idx];
+
+}
+
+/***********************************************************************
+ How many groups of change messages do we have ?
+ **********************************************************************/
+
+static int notify_msg_ctr_numgroups( SPOOLSS_NOTIFY_MSG_CTR *ctr )
+{
+ if ( !ctr )
+ return 0;
+
+ return ctr->num_groups;
+}
+
+/***********************************************************************
+ Add a SPOOLSS_NOTIFY_MSG_CTR to the correct group
+ **********************************************************************/
+
+static int notify_msg_ctr_addmsg( SPOOLSS_NOTIFY_MSG_CTR *ctr, SPOOLSS_NOTIFY_MSG *msg )
+{
+ SPOOLSS_NOTIFY_MSG_GROUP *groups = NULL;
+ SPOOLSS_NOTIFY_MSG_GROUP *msg_grp = NULL;
+ SPOOLSS_NOTIFY_MSG *msg_list = NULL;
+ int i, new_slot;
+
+ if ( !ctr || !msg )
+ return 0;
+
+ /* loop over all groups looking for a matching printer name */
+
+ for ( i=0; i<ctr->num_groups; i++ ) {
+ if ( strcmp(ctr->msg_groups[i].printername, msg->printer) == 0 )
+ break;
+ }
+
+ /* add a new group? */
+
+ if ( i == ctr->num_groups ) {
+ ctr->num_groups++;
+
+ if ( !(groups = talloc_realloc( ctr->ctx, ctr->msg_groups, sizeof(SPOOLSS_NOTIFY_MSG_GROUP)*ctr->num_groups)) ) {
+ DEBUG(0,("notify_msg_ctr_addmsg: talloc_realloc() failed!\n"));
+ return 0;
+ }
+ ctr->msg_groups = groups;
+
+ /* clear the new entry and set the printer name */
+
+ ZERO_STRUCT( ctr->msg_groups[ctr->num_groups-1] );
+ fstrcpy( ctr->msg_groups[ctr->num_groups-1].printername, msg->printer );
+ }
+
+ /* add the change messages; 'i' is the correct index now regardless */
+
+ msg_grp = &ctr->msg_groups[i];
+
+ msg_grp->num_msgs++;
+
+ if ( !(msg_list = talloc_realloc( ctr->ctx, msg_grp->msgs, sizeof(SPOOLSS_NOTIFY_MSG)*msg_grp->num_msgs )) ) {
+ DEBUG(0,("notify_msg_ctr_addmsg: talloc_realloc() failed for new message [%d]!\n", msg_grp->num_msgs));
+ return 0;
+ }
+ msg_grp->msgs = msg_list;
+
+ new_slot = msg_grp->num_msgs-1;
+ memcpy( &msg_grp->msgs[new_slot], msg, sizeof(SPOOLSS_NOTIFY_MSG) );
+
+ /* need to allocate own copy of data */
+
+ if ( msg->len != 0 )
+ msg_grp->msgs[new_slot].notify.data = talloc_memdup( ctr->ctx, msg->notify.data, msg->len );
+
+ return ctr->num_groups;
+}
+
+/***********************************************************************
+ Send a change notication message on all handles which have a call
+ back registered
+ **********************************************************************/
+
+static void send_notify2_changes( SPOOLSS_NOTIFY_MSG_CTR *ctr, uint32 idx )
+{
+ Printer_entry *p;
+ TALLOC_CTX *mem_ctx = notify_ctr_getctx( ctr );
+ SPOOLSS_NOTIFY_MSG_GROUP *msg_group = notify_ctr_getgroup( ctr, idx );
+ SPOOLSS_NOTIFY_MSG *messages;
+ int sending_msg_count;
+
+ if ( !msg_group ) {
+ DEBUG(5,("send_notify2_changes() called with no msg group!\n"));
+ return;
+ }
+
+ messages = msg_group->msgs;
+
+ if ( !messages ) {
+ DEBUG(5,("send_notify2_changes() called with no messages!\n"));
+ return;
+ }
+
+ DEBUG(8,("send_notify2_changes: Enter...[%s]\n", msg_group->printername));
+
+ /* loop over all printers */
+
+ for (p = printers_list; p; p = p->next) {
+ SPOOL_NOTIFY_INFO_DATA *data;
+ uint32 data_len = 0;
+ uint32 id;
+ int i;
+
+ /* Is there notification on this handle? */
+
+ if ( !p->notify.client_connected )
+ continue;
+
+ DEBUG(10,("Client connected! [%s]\n", p->dev.handlename));
+
+ /* For this printer? Print servers always receive
+ notifications. */
+
+ if ( ( p->printer_type == PRINTER_HANDLE_IS_PRINTER ) &&
+ ( !strequal(msg_group->printername, p->dev.handlename) ) )
+ continue;
+
+ DEBUG(10,("Our printer\n"));
+
+ /* allocate the max entries possible */
+
+ data = talloc( mem_ctx, msg_group->num_msgs*sizeof(SPOOL_NOTIFY_INFO_DATA) );
+ ZERO_STRUCTP(data);
+
+ /* build the array of change notifications */
+
+ sending_msg_count = 0;
+
+ for ( i=0; i<msg_group->num_msgs; i++ ) {
+ SPOOLSS_NOTIFY_MSG *msg = &messages[i];
+
+ /* Are we monitoring this event? */
+
+ if (!is_monitoring_event(p, msg->type, msg->field))
+ continue;
+
+ sending_msg_count++;
+
+
+ DEBUG(10,("process_notify2_message: Sending message type [%x] field [%x] for printer [%s]\n",
+ msg->type, msg->field, p->dev.handlename));
+
+ /*
+ * if the is a printer notification handle and not a job notification
+ * type, then set the id to 0. Other wise just use what was specified
+ * in the message.
+ *
+ * When registering change notification on a print server handle
+ * we always need to send back the id (snum) matching the printer
+ * for which the change took place. For change notify registered
+ * on a printer handle, this does not matter and the id should be 0.
+ *
+ * --jerry
+ */
+
+ if ( ( p->printer_type == PRINTER_HANDLE_IS_PRINTER ) && ( msg->type == PRINTER_NOTIFY_TYPE ) )
+ id = 0;
+ else
+ id = msg->id;
+
+
+ /* Convert unix jobid to smb jobid */
+
+ if (msg->flags & SPOOLSS_NOTIFY_MSG_UNIX_JOBID) {
+ id = sysjob_to_jobid(msg->id);
+
+ if (id == -1) {
+ DEBUG(3, ("no such unix jobid %d\n", msg->id));
+ goto done;
+ }
+ }
+
+ construct_info_data( &data[data_len], msg->type, msg->field, id );
+
+ switch(msg->type) {
+ case PRINTER_NOTIFY_TYPE:
+ if ( printer_notify_table[msg->field].fn )
+ printer_notify_table[msg->field].fn(msg, &data[data_len], mem_ctx);
+ break;
+
+ case JOB_NOTIFY_TYPE:
+ if ( job_notify_table[msg->field].fn )
+ job_notify_table[msg->field].fn(msg, &data[data_len], mem_ctx);
+ break;
+
+ default:
+ DEBUG(5, ("Unknown notification type %d\n", msg->type));
+ goto done;
+ }
+
+ data_len++;
+ }
+
+ if ( sending_msg_count ) {
+ cli_spoolss_rrpcn( &notify_cli, mem_ctx, &p->notify.client_hnd,
+ data_len, data, p->notify.change, 0 );
+ }
+ }
+
+done:
+ DEBUG(8,("send_notify2_changes: Exit...\n"));
+ return;
+}
+
+/***********************************************************************
+ **********************************************************************/
+
+static BOOL notify2_unpack_msg( SPOOLSS_NOTIFY_MSG *msg, struct timeval *tv, void *buf, size_t len )
+{
+
+ uint32 tv_sec, tv_usec;
+ size_t offset = 0;
+
+ /* Unpack message */
+
+ offset += tdb_unpack((char *)buf + offset, len - offset, "f",
+ msg->printer);
+
+ offset += tdb_unpack((char *)buf + offset, len - offset, "ddddddd",
+ &tv_sec, &tv_usec,
+ &msg->type, &msg->field, &msg->id, &msg->len, &msg->flags);
+
+ if (msg->len == 0)
+ tdb_unpack((char *)buf + offset, len - offset, "dd",
+ &msg->notify.value[0], &msg->notify.value[1]);
+ else
+ tdb_unpack((char *)buf + offset, len - offset, "B",
+ &msg->len, &msg->notify.data);
+
+ DEBUG(3, ("notify2_unpack_msg: got NOTIFY2 message for printer %s, jobid %u type %d, field 0x%02x, flags 0x%04x\n",
+ msg->printer, (unsigned int)msg->id, msg->type, msg->field, msg->flags));
+
+ tv->tv_sec = tv_sec;
+ tv->tv_usec = tv_usec;
+
+ if (msg->len == 0)
+ DEBUG(3, ("notify2_unpack_msg: value1 = %d, value2 = %d\n", msg->notify.value[0],
+ msg->notify.value[1]));
+ else
+ dump_data(3, msg->notify.data, msg->len);
+
+ return True;
+}
+
+/********************************************************************
+ Receive a notify2 message list
+ ********************************************************************/
+
+static void receive_notify2_message_list(int msg_type, pid_t src, void *msg, size_t len)
+{
+ size_t msg_count, i;
+ char *buf = (char *)msg;
+ char *msg_ptr;
+ size_t msg_len;
+ SPOOLSS_NOTIFY_MSG notify;
+ SPOOLSS_NOTIFY_MSG_CTR messages;
+ int num_groups;
+
+ if (len < 4) {
+ DEBUG(0,("receive_notify2_message_list: bad message format (len < 4)!\n"));
+ return;
+ }
+
+ msg_count = IVAL(buf, 0);
+ msg_ptr = buf + 4;
+
+ DEBUG(5, ("receive_notify2_message_list: got %lu messages in list\n", (unsigned long)msg_count));
+
+ if (msg_count == 0) {
+ DEBUG(0,("receive_notify2_message_list: bad message format (msg_count == 0) !\n"));
+ return;
+ }
+
+ /* initialize the container */
+
+ ZERO_STRUCT( messages );
+ notify_msg_ctr_init( &messages );
+
+ /*
+ * build message groups for each printer identified
+ * in a change_notify msg. Remember that a PCN message
+ * includes the handle returned for the srv_spoolss_replyopenprinter()
+ * call. Therefore messages are grouped according to printer handle.
+ */
+
+ for ( i=0; i<msg_count; i++ ) {
+ struct timeval msg_tv;
+
+ if (msg_ptr + 4 - buf > len) {
+ DEBUG(0,("receive_notify2_message_list: bad message format (len > buf_size) !\n"));
+ return;
+ }
+
+ msg_len = IVAL(msg_ptr,0);
+ msg_ptr += 4;
+
+ if (msg_ptr + msg_len - buf > len) {
+ DEBUG(0,("receive_notify2_message_list: bad message format (bad len) !\n"));
+ return;
+ }
+
+ /* unpack messages */
+
+ ZERO_STRUCT( notify );
+ notify2_unpack_msg( &notify, &msg_tv, msg_ptr, msg_len );
+ msg_ptr += msg_len;
+
+ /* add to correct list in container */
+
+ notify_msg_ctr_addmsg( &messages, &notify );
+
+ /* free memory that might have been allocated by notify2_unpack_msg() */
+
+ if ( notify.len != 0 )
+ SAFE_FREE( notify.notify.data );
+ }
+
+ /* process each group of messages */
+
+ num_groups = notify_msg_ctr_numgroups( &messages );
+ for ( i=0; i<num_groups; i++ )
+ send_notify2_changes( &messages, i );
+
+
+ /* cleanup */
+
+ DEBUG(10,("receive_notify2_message_list: processed %u messages\n", (uint32)msg_count ));
+
+ notify_msg_ctr_destroy( &messages );
+
+ return;
+}
+
+/********************************************************************
+ Send a message to ourself about new driver being installed
+ so we can upgrade the information for each printer bound to this
+ driver
+ ********************************************************************/
+
+static BOOL srv_spoolss_drv_upgrade_printer(char* drivername)
+{
+ int len = strlen(drivername);
+
+ if (!len)
+ return False;
+
+ DEBUG(10,("srv_spoolss_drv_upgrade_printer: Sending message about driver upgrade [%s]\n",
+ drivername));
+
+ message_send_pid(sys_getpid(), MSG_PRINTER_DRVUPGRADE, drivername, len+1, False);
+
+ return True;
+}
+
+/**********************************************************************
+ callback to receive a MSG_PRINTER_DRVUPGRADE message and interate
+ over all printers, upgrading ones as necessary
+ **********************************************************************/
+
+void do_drv_upgrade_printer(int msg_type, pid_t src, void *buf, size_t len)
+{
+ fstring drivername;
+ int snum;
+ int n_services = lp_numservices();
+
+ len = MIN(len,sizeof(drivername)-1);
+ strncpy(drivername, buf, len);
+
+ DEBUG(10,("do_drv_upgrade_printer: Got message for new driver [%s]\n", drivername ));
+
+ /* Iterate the printer list */
+
+ for (snum=0; snum<n_services; snum++)
+ {
+ if (lp_snum_ok(snum) && lp_print_ok(snum) )
+ {
+ WERROR result;
+ NT_PRINTER_INFO_LEVEL *printer = NULL;
+
+ result = get_a_printer(NULL, &printer, 2, lp_const_servicename(snum));
+ if (!W_ERROR_IS_OK(result))
+ continue;
+
+ if (printer && printer->info_2 && !strcmp(drivername, printer->info_2->drivername))
+ {
+ DEBUG(6,("Updating printer [%s]\n", printer->info_2->printername));
+
+ /* all we care about currently is the change_id */
+
+ result = mod_a_printer(*printer, 2);
+ if (!W_ERROR_IS_OK(result)) {
+ DEBUG(3,("do_drv_upgrade_printer: mod_a_printer() failed with status [%s]\n",
+ dos_errstr(result)));
+ }
+ }
+
+ free_a_printer(&printer, 2);
+ }
+ }
+
+ /* all done */
+}
+
+/********************************************************************
+ Update the cache for all printq's with a registered client
+ connection
+ ********************************************************************/
+
+void update_monitored_printq_cache( void )
+{
+ Printer_entry *printer = printers_list;
+ int snum;
+
+ /* loop through all printers and update the cache where
+ client_connected == True */
+ while ( printer )
+ {
+ if ( (printer->printer_type == PRINTER_HANDLE_IS_PRINTER)
+ && printer->notify.client_connected )
+ {
+ snum = print_queue_snum(printer->dev.handlename);
+ print_queue_status( snum, NULL, NULL );
+ }
+
+ printer = printer->next;
+ }
+
+ return;
+}
+/********************************************************************
+ Send a message to ourself about new driver being installed
+ so we can upgrade the information for each printer bound to this
+ driver
+ ********************************************************************/
+
+static BOOL srv_spoolss_reset_printerdata(char* drivername)
+{
+ int len = strlen(drivername);
+
+ if (!len)
+ return False;
+
+ DEBUG(10,("srv_spoolss_reset_printerdata: Sending message about resetting printerdata [%s]\n",
+ drivername));
+
+ message_send_pid(sys_getpid(), MSG_PRINTERDATA_INIT_RESET, drivername, len+1, False);
+
+ return True;
+}
+
+/**********************************************************************
+ callback to receive a MSG_PRINTERDATA_INIT_RESET message and interate
+ over all printers, resetting printer data as neessary
+ **********************************************************************/
+
+void reset_all_printerdata(int msg_type, pid_t src, void *buf, size_t len)
+{
+ fstring drivername;
+ int snum;
+ int n_services = lp_numservices();
+
+ len = MIN( len, sizeof(drivername)-1 );
+ strncpy( drivername, buf, len );
+
+ DEBUG(10,("reset_all_printerdata: Got message for new driver [%s]\n", drivername ));
+
+ /* Iterate the printer list */
+
+ for ( snum=0; snum<n_services; snum++ )
+ {
+ if ( lp_snum_ok(snum) && lp_print_ok(snum) )
+ {
+ WERROR result;
+ NT_PRINTER_INFO_LEVEL *printer = NULL;
+
+ result = get_a_printer( NULL, &printer, 2, lp_const_servicename(snum) );
+ if ( !W_ERROR_IS_OK(result) )
+ continue;
+
+ /*
+ * if the printer is bound to the driver,
+ * then reset to the new driver initdata
+ */
+
+ if ( printer && printer->info_2 && !strcmp(drivername, printer->info_2->drivername) )
+ {
+ DEBUG(6,("reset_all_printerdata: Updating printer [%s]\n", printer->info_2->printername));
+
+ if ( !set_driver_init(printer, 2) ) {
+ DEBUG(5,("reset_all_printerdata: Error resetting printer data for printer [%s], driver [%s]!\n",
+ printer->info_2->printername, printer->info_2->drivername));
+ }
+
+ result = mod_a_printer( *printer, 2 );
+ if ( !W_ERROR_IS_OK(result) ) {
+ DEBUG(3,("reset_all_printerdata: mod_a_printer() failed! (%s)\n",
+ get_dos_error_msg(result)));
+ }
+ }
+
+ free_a_printer( &printer, 2 );
+ }
+ }
+
+ /* all done */
+
+ return;
+}
+
+/********************************************************************
+ Copy routines used by convert_to_openprinterex()
+ *******************************************************************/
+
+static DEVICEMODE* dup_devicemode(TALLOC_CTX *ctx, DEVICEMODE *devmode)
+{
+ DEVICEMODE *d;
+ int len;
+
+ if (!devmode)
+ return NULL;
+
+ DEBUG (8,("dup_devmode\n"));
+
+ /* bulk copy first */
+
+ d = talloc_memdup(ctx, devmode, sizeof(DEVICEMODE));
+ if (!d)
+ return NULL;
+
+ /* dup the pointer members separately */
+
+ len = unistrlen(devmode->devicename.buffer);
+ if (len != -1) {
+ d->devicename.buffer = talloc(ctx, len*2);
+ if (unistrcpy(d->devicename.buffer, devmode->devicename.buffer) != len)
+ return NULL;
+ }
+
+
+ len = unistrlen(devmode->formname.buffer);
+ if (len != -1) {
+ d->devicename.buffer = talloc(ctx, len*2);
+ if (unistrcpy(d->formname.buffer, devmode->formname.buffer) != len)
+ return NULL;
+ }
+
+ d->private = talloc_memdup(ctx, devmode->private, devmode->driverextra);
+
+ return d;
+}
+
+static void copy_devmode_ctr(TALLOC_CTX *ctx, DEVMODE_CTR *new_ctr, DEVMODE_CTR *ctr)
+{
+ if (!new_ctr || !ctr)
+ return;
+
+ DEBUG(8,("copy_devmode_ctr\n"));
+
+ new_ctr->size = ctr->size;
+ new_ctr->devmode_ptr = ctr->devmode_ptr;
+
+ if(ctr->devmode_ptr)
+ new_ctr->devmode = dup_devicemode(ctx, ctr->devmode);
+}
+
+static void copy_printer_default(TALLOC_CTX *ctx, PRINTER_DEFAULT *new_def, PRINTER_DEFAULT *def)
+{
+ if (!new_def || !def)
+ return;
+
+ DEBUG(8,("copy_printer_defaults\n"));
+
+ new_def->datatype_ptr = def->datatype_ptr;
+
+ if (def->datatype_ptr)
+ copy_unistr2(&new_def->datatype, &def->datatype);
+
+ copy_devmode_ctr(ctx, &new_def->devmode_cont, &def->devmode_cont);
+
+ new_def->access_required = def->access_required;
+}
+
+/********************************************************************
+ * Convert a SPOOL_Q_OPEN_PRINTER structure to a
+ * SPOOL_Q_OPEN_PRINTER_EX structure
+ ********************************************************************/
+
+static void convert_to_openprinterex(TALLOC_CTX *ctx, SPOOL_Q_OPEN_PRINTER_EX *q_u_ex, SPOOL_Q_OPEN_PRINTER *q_u)
+{
+ if (!q_u_ex || !q_u)
+ return;
+
+ DEBUG(8,("convert_to_openprinterex\n"));
+
+ q_u_ex->printername_ptr = q_u->printername_ptr;
+
+ if (q_u->printername_ptr)
+ copy_unistr2(&q_u_ex->printername, &q_u->printername);
+
+ copy_printer_default(ctx, &q_u_ex->printer_default, &q_u->printer_default);
+}
+
+/********************************************************************
+ * spoolss_open_printer
+ *
+ * called from the spoolss dispatcher
+ ********************************************************************/
+
+WERROR _spoolss_open_printer(pipes_struct *p, SPOOL_Q_OPEN_PRINTER *q_u, SPOOL_R_OPEN_PRINTER *r_u)
+{
+ SPOOL_Q_OPEN_PRINTER_EX q_u_ex;
+ SPOOL_R_OPEN_PRINTER_EX r_u_ex;
+
+ if (!q_u || !r_u)
+ return WERR_NOMEM;
+
+ ZERO_STRUCT(q_u_ex);
+ ZERO_STRUCT(r_u_ex);
+
+ /* convert the OpenPrinter() call to OpenPrinterEx() */
+
+ convert_to_openprinterex(p->mem_ctx, &q_u_ex, q_u);
+
+ r_u_ex.status = _spoolss_open_printer_ex(p, &q_u_ex, &r_u_ex);
+
+ /* convert back to OpenPrinter() */
+
+ memcpy(r_u, &r_u_ex, sizeof(*r_u));
+
+ return r_u->status;
+}
+
+/********************************************************************
+ * spoolss_open_printer
+ *
+ * If the openprinterex rpc call contains a devmode,
+ * it's a per-user one. This per-user devmode is derivated
+ * from the global devmode. Openprinterex() contains a per-user
+ * devmode for when you do EMF printing and spooling.
+ * In the EMF case, the NT workstation is only doing half the job
+ * of rendering the page. The other half is done by running the printer
+ * driver on the server.
+ * The EMF file doesn't contain the page description (paper size, orientation, ...).
+ * The EMF file only contains what is to be printed on the page.
+ * So in order for the server to know how to print, the NT client sends
+ * a devicemode attached to the openprinterex call.
+ * But this devicemode is short lived, it's only valid for the current print job.
+ *
+ * If Samba would have supported EMF spooling, this devicemode would
+ * have been attached to the handle, to sent it to the driver to correctly
+ * rasterize the EMF file.
+ *
+ * As Samba only supports RAW spooling, we only receive a ready-to-print file,
+ * we just act as a pass-thru between windows and the printer.
+ *
+ * In order to know that Samba supports only RAW spooling, NT has to call
+ * getprinter() at level 2 (attribute field) or NT has to call startdoc()
+ * and until NT sends a RAW job, we refuse it.
+ *
+ * But to call getprinter() or startdoc(), you first need a valid handle,
+ * and to get an handle you have to call openprintex(). Hence why you have
+ * a devicemode in the openprinterex() call.
+ *
+ *
+ * Differences between NT4 and NT 2000.
+ * NT4:
+ * ---
+ * On NT4, you only have a global devicemode. This global devicemode can be changed
+ * by the administrator (or by a user with enough privs). Everytime a user
+ * wants to print, the devicemode is resetted to the default. In Word, everytime
+ * you print, the printer's characteristics are always reset to the global devicemode.
+ *
+ * NT 2000:
+ * -------
+ * In W2K, there is the notion of per-user devicemode. The first time you use
+ * a printer, a per-user devicemode is build from the global devicemode.
+ * If you change your per-user devicemode, it is saved in the registry, under the
+ * H_KEY_CURRENT_KEY sub_tree. So that everytime you print, you have your default
+ * printer preferences available.
+ *
+ * To change the per-user devicemode: it's the "Printing Preferences ..." button
+ * on the General Tab of the printer properties windows.
+ *
+ * To change the global devicemode: it's the "Printing Defaults..." button
+ * on the Advanced Tab of the printer properties window.
+ *
+ * JFM.
+ ********************************************************************/
+
+WERROR _spoolss_open_printer_ex( pipes_struct *p, SPOOL_Q_OPEN_PRINTER_EX *q_u, SPOOL_R_OPEN_PRINTER_EX *r_u)
+{
+ UNISTR2 *printername = NULL;
+ PRINTER_DEFAULT *printer_default = &q_u->printer_default;
+ POLICY_HND *handle = &r_u->handle;
+
+ fstring name;
+ int snum;
+ struct current_user user;
+ Printer_entry *Printer=NULL;
+
+ if (q_u->printername_ptr != 0)
+ printername = &q_u->printername;
+
+ if (printername == NULL)
+ return WERR_INVALID_PRINTER_NAME;
+
+ /* some sanity check because you can open a printer or a print server */
+ /* aka: \\server\printer or \\server */
+ unistr2_to_ascii(name, printername, sizeof(name)-1);
+
+ DEBUGADD(3,("checking name: %s\n",name));
+
+ if (!open_printer_hnd(p, handle, name, 0))
+ return WERR_INVALID_PRINTER_NAME;
+
+ Printer=find_printer_index_by_hnd(p, handle);
+ if (!Printer) {
+ DEBUG(0,(" _spoolss_open_printer_ex: logic error. \
+Can't find printer handle we created for printer %s\n", name ));
+ close_printer_handle(p,handle);
+ return WERR_INVALID_PRINTER_NAME;
+ }
+
+ get_current_user(&user, p);
+
+ /*
+ * First case: the user is opening the print server:
+ *
+ * Disallow MS AddPrinterWizard if parameter disables it. A Win2k
+ * client 1st tries an OpenPrinterEx with access==0, MUST be allowed.
+ *
+ * Then both Win2k and WinNT clients try an OpenPrinterEx with
+ * SERVER_ALL_ACCESS, which we allow only if the user is root (uid=0)
+ * or if the user is listed in the smb.conf printer admin parameter.
+ *
+ * Then they try OpenPrinterEx with SERVER_READ which we allow. This lets the
+ * client view printer folder, but does not show the MSAPW.
+ *
+ * Note: this test needs code to check access rights here too. Jeremy
+ * could you look at this?
+ *
+ * Second case: the user is opening a printer:
+ * NT doesn't let us connect to a printer if the connecting user
+ * doesn't have print permission.
+ */
+
+ if (Printer->printer_type == PRINTER_HANDLE_IS_PRINTSERVER)
+ {
+ /* Printserver handles use global struct... */
+
+ snum = -1;
+
+ /* Map standard access rights to object specific access rights */
+
+ se_map_standard(&printer_default->access_required,
+ &printserver_std_mapping);
+
+ /* Deny any object specific bits that don't apply to print
+ servers (i.e printer and job specific bits) */
+
+ printer_default->access_required &= SPECIFIC_RIGHTS_MASK;
+
+ if (printer_default->access_required &
+ ~(SERVER_ACCESS_ADMINISTER | SERVER_ACCESS_ENUMERATE)) {
+ DEBUG(3, ("access DENIED for non-printserver bits"));
+ close_printer_handle(p, handle);
+ return WERR_ACCESS_DENIED;
+ }
+
+ /* Allow admin access */
+
+ if ( printer_default->access_required & SERVER_ACCESS_ADMINISTER )
+ {
+ if (!lp_ms_add_printer_wizard()) {
+ close_printer_handle(p, handle);
+ return WERR_ACCESS_DENIED;
+ }
+
+ /* if the user is not root and not a printer admin, then fail */
+
+ if ( user.uid != 0
+ && !user_in_list(uidtoname(user.uid), lp_printer_admin(snum), user.groups, user.ngroups) )
+ {
+ close_printer_handle(p, handle);
+ return WERR_ACCESS_DENIED;
+ }
+
+ printer_default->access_required = SERVER_ACCESS_ADMINISTER;
+ }
+ else
+ {
+ printer_default->access_required = SERVER_ACCESS_ENUMERATE;
+ }
+
+ DEBUG(4,("Setting print server access = %s\n", (printer_default->access_required == SERVER_ACCESS_ADMINISTER)
+ ? "SERVER_ACCESS_ADMINISTER" : "SERVER_ACCESS_ENUMERATE" ));
+
+ /* We fall through to return WERR_OK */
+
+ }
+ else
+ {
+ /* NT doesn't let us connect to a printer if the connecting user
+ doesn't have print permission. */
+
+ if (!get_printer_snum(p, handle, &snum)) {
+ close_printer_handle(p, handle);
+ return WERR_BADFID;
+ }
+
+ se_map_standard(&printer_default->access_required, &printer_std_mapping);
+
+ /* map an empty access mask to the minimum access mask */
+ if (printer_default->access_required == 0x0)
+ printer_default->access_required = PRINTER_ACCESS_USE;
+
+ /*
+ * If we are not serving the printer driver for this printer,
+ * map PRINTER_ACCESS_ADMINISTER to PRINTER_ACCESS_USE. This
+ * will keep NT clients happy --jerry
+ */
+
+ if (lp_use_client_driver(snum)
+ && (printer_default->access_required & PRINTER_ACCESS_ADMINISTER))
+ {
+ printer_default->access_required = PRINTER_ACCESS_USE;
+ }
+
+ /* check smb.conf parameters and the the sec_desc */
+
+ if (!user_ok(uidtoname(user.uid), snum, user.groups, user.ngroups) || !print_access_check(&user, snum, printer_default->access_required)) {
+ DEBUG(3, ("access DENIED for printer open\n"));
+ close_printer_handle(p, handle);
+ return WERR_ACCESS_DENIED;
+ }
+
+ if ((printer_default->access_required & SPECIFIC_RIGHTS_MASK)& ~(PRINTER_ACCESS_ADMINISTER|PRINTER_ACCESS_USE)) {
+ DEBUG(3, ("access DENIED for printer open - unknown bits\n"));
+ close_printer_handle(p, handle);
+ return WERR_ACCESS_DENIED;
+ }
+
+ if (printer_default->access_required & PRINTER_ACCESS_ADMINISTER)
+ printer_default->access_required = PRINTER_ACCESS_ADMINISTER;
+ else
+ printer_default->access_required = PRINTER_ACCESS_USE;
+
+ DEBUG(4,("Setting printer access = %s\n", (printer_default->access_required == PRINTER_ACCESS_ADMINISTER)
+ ? "PRINTER_ACCESS_ADMINISTER" : "PRINTER_ACCESS_USE" ));
+
+ }
+
+ Printer->access_granted = printer_default->access_required;
+
+ /*
+ * If the client sent a devmode in the OpenPrinter() call, then
+ * save it here in case we get a job submission on this handle
+ */
+
+ if ( (Printer->printer_type != PRINTER_HANDLE_IS_PRINTSERVER)
+ && q_u->printer_default.devmode_cont.devmode_ptr )
+ {
+ convert_devicemode( Printer->dev.handlename, q_u->printer_default.devmode_cont.devmode,
+ &Printer->nt_devmode );
+ }
+
+ /* HACK ALERT!!! Sleep for 1/3 of a second to try trigger a LAN/WAN
+ optimization in Windows 2000 clients --jerry */
+
+ if ( (printer_default->access_required == PRINTER_ACCESS_ADMINISTER)
+ && (RA_WIN2K == get_remote_arch()) )
+ {
+ DEBUG(10,("_spoolss_open_printer_ex: Enabling LAN/WAN hack for Win2k clients.\n"));
+ sys_usleep( 500000 );
+ }
+
+ return WERR_OK;
+}
+
+/****************************************************************************
+****************************************************************************/
+
+static BOOL convert_printer_info(const SPOOL_PRINTER_INFO_LEVEL *uni,
+ NT_PRINTER_INFO_LEVEL *printer, uint32 level)
+{
+ BOOL ret = True;
+
+ switch (level) {
+ case 2:
+ ret = uni_2_asc_printer_info_2(uni->info_2, &printer->info_2);
+ break;
+ default:
+ break;
+ }
+
+ return ret;
+}
+
+static BOOL convert_printer_driver_info(const SPOOL_PRINTER_DRIVER_INFO_LEVEL *uni,
+ NT_PRINTER_DRIVER_INFO_LEVEL *printer, uint32 level)
+{
+ BOOL result = True;
+
+ switch (level) {
+ case 3:
+ printer->info_3=NULL;
+ if (!uni_2_asc_printer_driver_3(uni->info_3, &printer->info_3))
+ result = False;
+ break;
+ case 6:
+ printer->info_6=NULL;
+ if (!uni_2_asc_printer_driver_6(uni->info_6, &printer->info_6))
+ result = False;
+ break;
+ default:
+ break;
+ }
+
+ return result;
+}
+
+BOOL convert_devicemode(const char *printername, const DEVICEMODE *devmode,
+ NT_DEVICEMODE **pp_nt_devmode)
+{
+ NT_DEVICEMODE *nt_devmode = *pp_nt_devmode;
+
+ /*
+ * Ensure nt_devmode is a valid pointer
+ * as we will be overwriting it.
+ */
+
+ if (nt_devmode == NULL) {
+ DEBUG(5, ("convert_devicemode: allocating a generic devmode\n"));
+ if ((nt_devmode = construct_nt_devicemode(printername)) == NULL)
+ return False;
+ }
+
+ rpcstr_pull(nt_devmode->devicename,devmode->devicename.buffer, 31, -1, 0);
+ rpcstr_pull(nt_devmode->formname,devmode->formname.buffer, 31, -1, 0);
+
+ nt_devmode->specversion=devmode->specversion;
+ nt_devmode->driverversion=devmode->driverversion;
+ nt_devmode->size=devmode->size;
+ nt_devmode->fields=devmode->fields;
+ nt_devmode->orientation=devmode->orientation;
+ nt_devmode->papersize=devmode->papersize;
+ nt_devmode->paperlength=devmode->paperlength;
+ nt_devmode->paperwidth=devmode->paperwidth;
+ nt_devmode->scale=devmode->scale;
+ nt_devmode->copies=devmode->copies;
+ nt_devmode->defaultsource=devmode->defaultsource;
+ nt_devmode->printquality=devmode->printquality;
+ nt_devmode->color=devmode->color;
+ nt_devmode->duplex=devmode->duplex;
+ nt_devmode->yresolution=devmode->yresolution;
+ nt_devmode->ttoption=devmode->ttoption;
+ nt_devmode->collate=devmode->collate;
+
+ nt_devmode->logpixels=devmode->logpixels;
+ nt_devmode->bitsperpel=devmode->bitsperpel;
+ nt_devmode->pelswidth=devmode->pelswidth;
+ nt_devmode->pelsheight=devmode->pelsheight;
+ nt_devmode->displayflags=devmode->displayflags;
+ nt_devmode->displayfrequency=devmode->displayfrequency;
+ nt_devmode->icmmethod=devmode->icmmethod;
+ nt_devmode->icmintent=devmode->icmintent;
+ nt_devmode->mediatype=devmode->mediatype;
+ nt_devmode->dithertype=devmode->dithertype;
+ nt_devmode->reserved1=devmode->reserved1;
+ nt_devmode->reserved2=devmode->reserved2;
+ nt_devmode->panningwidth=devmode->panningwidth;
+ nt_devmode->panningheight=devmode->panningheight;
+
+ /*
+ * Only change private and driverextra if the incoming devmode
+ * has a new one. JRA.
+ */
+
+ if ((devmode->driverextra != 0) && (devmode->private != NULL)) {
+ SAFE_FREE(nt_devmode->private);
+ nt_devmode->driverextra=devmode->driverextra;
+ if((nt_devmode->private=(uint8 *)malloc(nt_devmode->driverextra * sizeof(uint8))) == NULL)
+ return False;
+ memcpy(nt_devmode->private, devmode->private, nt_devmode->driverextra);
+ }
+
+ *pp_nt_devmode = nt_devmode;
+
+ return True;
+}
+
+/********************************************************************
+ * _spoolss_enddocprinter_internal.
+ ********************************************************************/
+
+static WERROR _spoolss_enddocprinter_internal(pipes_struct *p, POLICY_HND *handle)
+{
+ Printer_entry *Printer=find_printer_index_by_hnd(p, handle);
+ int snum;
+
+ if (!Printer) {
+ DEBUG(2,("_spoolss_enddocprinter_internal: Invalid handle (%s:%u:%u)\n", OUR_HANDLE(handle)));
+ return WERR_BADFID;
+ }
+
+ if (!get_printer_snum(p, handle, &snum))
+ return WERR_BADFID;
+
+ Printer->document_started=False;
+ print_job_end(snum, Printer->jobid,True);
+ /* error codes unhandled so far ... */
+
+ return WERR_OK;
+}
+
+/********************************************************************
+ * api_spoolss_closeprinter
+ ********************************************************************/
+
+WERROR _spoolss_closeprinter(pipes_struct *p, SPOOL_Q_CLOSEPRINTER *q_u, SPOOL_R_CLOSEPRINTER *r_u)
+{
+ POLICY_HND *handle = &q_u->handle;
+
+ Printer_entry *Printer=find_printer_index_by_hnd(p, handle);
+
+ if (Printer && Printer->document_started)
+ _spoolss_enddocprinter_internal(p, handle); /* print job was not closed */
+
+ if (!close_printer_handle(p, handle))
+ return WERR_BADFID;
+
+ /* clear the returned printer handle. Observed behavior
+ from Win2k server. Don't think this really matters.
+ Previous code just copied the value of the closed
+ handle. --jerry */
+
+ memset(&r_u->handle, '\0', sizeof(r_u->handle));
+
+ return WERR_OK;
+}
+
+/********************************************************************
+ * api_spoolss_deleteprinter
+
+ ********************************************************************/
+
+WERROR _spoolss_deleteprinter(pipes_struct *p, SPOOL_Q_DELETEPRINTER *q_u, SPOOL_R_DELETEPRINTER *r_u)
+{
+ POLICY_HND *handle = &q_u->handle;
+ Printer_entry *Printer=find_printer_index_by_hnd(p, handle);
+ WERROR result;
+
+ if (Printer && Printer->document_started)
+ _spoolss_enddocprinter_internal(p, handle); /* print job was not closed */
+
+ memcpy(&r_u->handle, &q_u->handle, sizeof(r_u->handle));
+
+ result = delete_printer_handle(p, handle);
+
+ update_c_setprinter(False);
+
+ return result;
+}
+
+/*******************************************************************
+ * static function to lookup the version id corresponding to an
+ * long architecture string
+ ******************************************************************/
+
+static int get_version_id (char * arch)
+{
+ int i;
+ struct table_node archi_table[]= {
+
+ {"Windows 4.0", "WIN40", 0 },
+ {"Windows NT x86", "W32X86", 2 },
+ {"Windows NT R4000", "W32MIPS", 2 },
+ {"Windows NT Alpha_AXP", "W32ALPHA", 2 },
+ {"Windows NT PowerPC", "W32PPC", 2 },
+ {NULL, "", -1 }
+ };
+
+ for (i=0; archi_table[i].long_archi != NULL; i++)
+ {
+ if (strcmp(arch, archi_table[i].long_archi) == 0)
+ return (archi_table[i].version);
+ }
+
+ return -1;
+}
+
+/********************************************************************
+ * _spoolss_deleteprinterdriver
+ ********************************************************************/
+
+WERROR _spoolss_deleteprinterdriver(pipes_struct *p, SPOOL_Q_DELETEPRINTERDRIVER *q_u, SPOOL_R_DELETEPRINTERDRIVER *r_u)
+{
+ fstring driver;
+ fstring arch;
+ NT_PRINTER_DRIVER_INFO_LEVEL info;
+ NT_PRINTER_DRIVER_INFO_LEVEL info_win2k;
+ int version;
+ struct current_user user;
+ WERROR status;
+ WERROR status_win2k = WERR_ACCESS_DENIED;
+
+ get_current_user(&user, p);
+
+ unistr2_to_ascii(driver, &q_u->driver, sizeof(driver)-1 );
+ unistr2_to_ascii(arch, &q_u->arch, sizeof(arch)-1 );
+
+ /* check that we have a valid driver name first */
+
+ if ((version=get_version_id(arch)) == -1)
+ return WERR_INVALID_ENVIRONMENT;
+
+ ZERO_STRUCT(info);
+ ZERO_STRUCT(info_win2k);
+
+ if (!W_ERROR_IS_OK(get_a_printer_driver(&info, 3, driver, arch, version)))
+ {
+ /* try for Win2k driver if "Windows NT x86" */
+
+ if ( version == 2 ) {
+ version = 3;
+ if (!W_ERROR_IS_OK(get_a_printer_driver(&info, 3, driver, arch, version))) {
+ status = WERR_UNKNOWN_PRINTER_DRIVER;
+ goto done;
+ }
+ }
+ /* otherwise it was a failure */
+ else {
+ status = WERR_UNKNOWN_PRINTER_DRIVER;
+ goto done;
+ }
+
+ }
+
+ if (printer_driver_in_use(info.info_3)) {
+ status = WERR_PRINTER_DRIVER_IN_USE;
+ goto done;
+ }
+
+ if ( version == 2 )
+ {
+ if (W_ERROR_IS_OK(get_a_printer_driver(&info_win2k, 3, driver, arch, 3)))
+ {
+ /* if we get to here, we now have 2 driver info structures to remove */
+ /* remove the Win2k driver first*/
+
+ status_win2k = delete_printer_driver(info_win2k.info_3, &user, 3, False );
+ free_a_printer_driver( info_win2k, 3 );
+
+ /* this should not have failed---if it did, report to client */
+ if ( !W_ERROR_IS_OK(status_win2k) )
+ goto done;
+ }
+ }
+
+ status = delete_printer_driver(info.info_3, &user, version, False);
+
+ /* if at least one of the deletes succeeded return OK */
+
+ if ( W_ERROR_IS_OK(status) || W_ERROR_IS_OK(status_win2k) )
+ status = WERR_OK;
+
+done:
+ free_a_printer_driver( info, 3 );
+
+ return status;
+}
+
+/********************************************************************
+ * spoolss_deleteprinterdriverex
+ ********************************************************************/
+
+WERROR _spoolss_deleteprinterdriverex(pipes_struct *p, SPOOL_Q_DELETEPRINTERDRIVEREX *q_u, SPOOL_R_DELETEPRINTERDRIVEREX *r_u)
+{
+ fstring driver;
+ fstring arch;
+ NT_PRINTER_DRIVER_INFO_LEVEL info;
+ NT_PRINTER_DRIVER_INFO_LEVEL info_win2k;
+ int version;
+ uint32 flags = q_u->delete_flags;
+ BOOL delete_files;
+ struct current_user user;
+ WERROR status;
+ WERROR status_win2k = WERR_ACCESS_DENIED;
+
+ get_current_user(&user, p);
+
+ unistr2_to_ascii(driver, &q_u->driver, sizeof(driver)-1 );
+ unistr2_to_ascii(arch, &q_u->arch, sizeof(arch)-1 );
+
+ /* check that we have a valid driver name first */
+ if ((version=get_version_id(arch)) == -1) {
+ /* this is what NT returns */
+ return WERR_INVALID_ENVIRONMENT;
+ }
+
+ if ( flags & DPD_DELETE_SPECIFIC_VERSION )
+ version = q_u->version;
+
+ ZERO_STRUCT(info);
+ ZERO_STRUCT(info_win2k);
+
+ status = get_a_printer_driver(&info, 3, driver, arch, version);
+
+ if ( !W_ERROR_IS_OK(status) )
+ {
+ /*
+ * if the client asked for a specific version,
+ * or this is something other than Windows NT x86,
+ * then we've failed
+ */
+
+ if ( (flags&DPD_DELETE_SPECIFIC_VERSION) || (version !=2) )
+ goto done;
+
+ /* try for Win2k driver if "Windows NT x86" */
+
+ version = 3;
+ if (!W_ERROR_IS_OK(get_a_printer_driver(&info, 3, driver, arch, version))) {
+ status = WERR_UNKNOWN_PRINTER_DRIVER;
+ goto done;
+ }
+ }
+
+ if ( printer_driver_in_use(info.info_3) ) {
+ status = WERR_PRINTER_DRIVER_IN_USE;
+ goto done;
+ }
+
+ /*
+ * we have a couple of cases to consider.
+ * (1) Are any files in use? If so and DPD_DELTE_ALL_FILE is set,
+ * then the delete should fail if **any** files overlap with
+ * other drivers
+ * (2) If DPD_DELTE_UNUSED_FILES is sert, then delete all
+ * non-overlapping files
+ * (3) If neither DPD_DELTE_ALL_FILE nor DPD_DELTE_ALL_FILES
+ * is set, the do not delete any files
+ * Refer to MSDN docs on DeletePrinterDriverEx() for details.
+ */
+
+ delete_files = flags & (DPD_DELETE_ALL_FILES|DPD_DELETE_UNUSED_FILES);
+
+ /* fail if any files are in use and DPD_DELETE_ALL_FILES is set */
+
+ if ( delete_files && printer_driver_files_in_use(info.info_3) & (flags&DPD_DELETE_ALL_FILES) ) {
+ /* no idea of the correct error here */
+ status = WERR_ACCESS_DENIED;
+ goto done;
+ }
+
+
+ /* also check for W32X86/3 if necessary; maybe we already have? */
+
+ if ( (version == 2) && ((flags&DPD_DELETE_SPECIFIC_VERSION) != DPD_DELETE_SPECIFIC_VERSION) ) {
+ if (W_ERROR_IS_OK(get_a_printer_driver(&info_win2k, 3, driver, arch, 3)))
+ {
+
+ if ( delete_files && printer_driver_files_in_use(info_win2k.info_3) & (flags&DPD_DELETE_ALL_FILES) ) {
+ /* no idea of the correct error here */
+ free_a_printer_driver( info_win2k, 3 );
+ status = WERR_ACCESS_DENIED;
+ goto done;
+ }
+
+ /* if we get to here, we now have 2 driver info structures to remove */
+ /* remove the Win2k driver first*/
+
+ status_win2k = delete_printer_driver(info_win2k.info_3, &user, 3, delete_files);
+ free_a_printer_driver( info_win2k, 3 );
+
+ /* this should not have failed---if it did, report to client */
+
+ if ( !W_ERROR_IS_OK(status_win2k) )
+ goto done;
+ }
+ }
+
+ status = delete_printer_driver(info.info_3, &user, version, delete_files);
+
+ if ( W_ERROR_IS_OK(status) || W_ERROR_IS_OK(status_win2k) )
+ status = WERR_OK;
+done:
+ free_a_printer_driver( info, 3 );
+
+ return status;
+}
+
+
+/****************************************************************************
+ Internal routine for retreiving printerdata
+ ***************************************************************************/
+
+static WERROR get_printer_dataex( TALLOC_CTX *ctx, NT_PRINTER_INFO_LEVEL *printer,
+ const char *key, const char *value, uint32 *type, uint8 **data,
+ uint32 *needed, uint32 in_size )
+{
+ REGISTRY_VALUE *val;
+ int size, data_len;
+
+ if ( !(val = get_printer_data( printer->info_2, key, value)) )
+ return WERR_BADFILE;
+
+ *type = regval_type( val );
+
+ DEBUG(5,("get_printer_dataex: allocating %d\n", in_size));
+
+ size = regval_size( val );
+
+ /* copy the min(in_size, len) */
+
+ if ( in_size ) {
+ data_len = (size > in_size) ? in_size : size*sizeof(uint8);
+
+ /* special case for 0 length values */
+ if ( data_len ) {
+ if ( (*data = (uint8 *)talloc_memdup(ctx, regval_data_p(val), data_len)) == NULL )
+ return WERR_NOMEM;
+ }
+ else {
+ if ( (*data = (uint8 *)talloc_zero(ctx, in_size)) == NULL )
+ return WERR_NOMEM;
+ }
+ }
+ else
+ *data = NULL;
+
+ *needed = size;
+
+ DEBUG(5,("get_printer_dataex: copy done\n"));
+
+ return WERR_OK;
+}
+
+/****************************************************************************
+ Internal routine for removing printerdata
+ ***************************************************************************/
+
+static WERROR delete_printer_dataex( NT_PRINTER_INFO_LEVEL *printer, const char *key, const char *value )
+{
+ return delete_printer_data( printer->info_2, key, value );
+}
+
+/****************************************************************************
+ Internal routine for storing printerdata
+ ***************************************************************************/
+
+static WERROR set_printer_dataex( NT_PRINTER_INFO_LEVEL *printer, const char *key, const char *value,
+ uint32 type, uint8 *data, int real_len )
+{
+ delete_printer_data( printer->info_2, key, value );
+
+ return add_printer_data( printer->info_2, key, value, type, data, real_len );
+}
+
+/********************************************************************
+ GetPrinterData on a printer server Handle.
+********************************************************************/
+
+static WERROR getprinterdata_printer_server(TALLOC_CTX *ctx, fstring value, uint32 *type, uint8 **data, uint32 *needed, uint32 in_size)
+{
+ int i;
+
+ DEBUG(8,("getprinterdata_printer_server:%s\n", value));
+
+ if (!StrCaseCmp(value, "W3SvcInstalled")) {
+ *type = 0x4;
+ if((*data = (uint8 *)talloc_zero(ctx, 4*sizeof(uint8) )) == NULL)
+ return WERR_NOMEM;
+ *needed = 0x4;
+ return WERR_OK;
+ }
+
+ if (!StrCaseCmp(value, "BeepEnabled")) {
+ *type = 0x4;
+ if((*data = (uint8 *)talloc(ctx, 4*sizeof(uint8) )) == NULL)
+ return WERR_NOMEM;
+ SIVAL(*data, 0, 0x00);
+ *needed = 0x4;
+ return WERR_OK;
+ }
+
+ if (!StrCaseCmp(value, "EventLog")) {
+ *type = 0x4;
+ if((*data = (uint8 *)talloc(ctx, 4*sizeof(uint8) )) == NULL)
+ return WERR_NOMEM;
+ /* formally was 0x1b */
+ SIVAL(*data, 0, 0x0);
+ *needed = 0x4;
+ return WERR_OK;
+ }
+
+ if (!StrCaseCmp(value, "NetPopup")) {
+ *type = 0x4;
+ if((*data = (uint8 *)talloc(ctx, 4*sizeof(uint8) )) == NULL)
+ return WERR_NOMEM;
+ SIVAL(*data, 0, 0x00);
+ *needed = 0x4;
+ return WERR_OK;
+ }
+
+ if (!StrCaseCmp(value, "MajorVersion")) {
+ *type = 0x4;
+ if((*data = (uint8 *)talloc(ctx, 4*sizeof(uint8) )) == NULL)
+ return WERR_NOMEM;
+
+ /* Windows NT 4.0 seems to not allow uploading of drivers
+ to a server that reports 0x3 as the MajorVersion.
+ need to investigate more how Win2k gets around this .
+ -- jerry */
+
+ if ( RA_WINNT == get_remote_arch() )
+ SIVAL(*data, 0, 2);
+ else
+ SIVAL(*data, 0, 3);
+
+ *needed = 0x4;
+ return WERR_OK;
+ }
+
+ if (!StrCaseCmp(value, "MinorVersion")) {
+ *type = 0x4;
+ if((*data = (uint8 *)talloc(ctx, 4*sizeof(uint8) )) == NULL)
+ return WERR_NOMEM;
+ SIVAL(*data, 0, 0);
+ *needed = 0x4;
+ return WERR_OK;
+ }
+
+ /* REG_BINARY
+ * uint32 size = 0x114
+ * uint32 major = 5
+ * uint32 minor = [0|1]
+ * uint32 build = [2195|2600]
+ * extra unicode string = e.g. "Service Pack 3"
+ */
+ if (!StrCaseCmp(value, "OSVersion")) {
+ *type = 0x3;
+ *needed = 0x114;
+
+ if((*data = (uint8 *)talloc(ctx, (*needed)*sizeof(uint8) )) == NULL)
+ return WERR_NOMEM;
+ ZERO_STRUCTP( *data );
+
+ SIVAL(*data, 0, *needed); /* size */
+ SIVAL(*data, 4, 5); /* Windows 2000 == 5.0 */
+ SIVAL(*data, 8, 0);
+ SIVAL(*data, 12, 2195); /* build */
+
+ /* leave extra string empty */
+
+ return WERR_OK;
+ }
+
+
+ if (!StrCaseCmp(value, "DefaultSpoolDirectory")) {
+ const char *string="C:\\PRINTERS";
+ *type = 0x1;
+ *needed = 2*(strlen(string)+1);
+ if((*data = (uint8 *)talloc(ctx, ((*needed > in_size) ? *needed:in_size) *sizeof(uint8))) == NULL)
+ return WERR_NOMEM;
+ memset(*data, 0, (*needed > in_size) ? *needed:in_size);
+
+ /* it's done by hand ready to go on the wire */
+ for (i=0; i<strlen(string); i++) {
+ (*data)[2*i]=string[i];
+ (*data)[2*i+1]='\0';
+ }
+ return WERR_OK;
+ }
+
+ if (!StrCaseCmp(value, "Architecture")) {
+ const char *string="Windows NT x86";
+ *type = 0x1;
+ *needed = 2*(strlen(string)+1);
+ if((*data = (uint8 *)talloc(ctx, ((*needed > in_size) ? *needed:in_size) *sizeof(uint8))) == NULL)
+ return WERR_NOMEM;
+ memset(*data, 0, (*needed > in_size) ? *needed:in_size);
+ for (i=0; i<strlen(string); i++) {
+ (*data)[2*i]=string[i];
+ (*data)[2*i+1]='\0';
+ }
+ return WERR_OK;
+ }
+
+ if (!StrCaseCmp(value, "DsPresent")) {
+ *type = 0x4;
+ if((*data = (uint8 *)talloc(ctx, 4*sizeof(uint8) )) == NULL)
+ return WERR_NOMEM;
+ SIVAL(*data, 0, 0x01);
+ *needed = 0x4;
+ return WERR_OK;
+ }
+
+ if (!StrCaseCmp(value, "DNSMachineName")) {
+ pstring hostname;
+
+ if (!get_mydnsfullname(hostname))
+ return WERR_BADFILE;
+ *type = 0x1;
+ *needed = 2*(strlen(hostname)+1);
+ if((*data = (uint8 *)talloc(ctx, ((*needed > in_size) ? *needed:in_size) *sizeof(uint8))) == NULL)
+ return WERR_NOMEM;
+ memset(*data, 0, (*needed > in_size) ? *needed:in_size);
+ for (i=0; i<strlen(hostname); i++) {
+ (*data)[2*i]=hostname[i];
+ (*data)[2*i+1]='\0';
+ }
+ return WERR_OK;
+ }
+
+
+ return WERR_BADFILE;
+}
+
+/********************************************************************
+ * spoolss_getprinterdata
+ ********************************************************************/
+
+WERROR _spoolss_getprinterdata(pipes_struct *p, SPOOL_Q_GETPRINTERDATA *q_u, SPOOL_R_GETPRINTERDATA *r_u)
+{
+ POLICY_HND *handle = &q_u->handle;
+ UNISTR2 *valuename = &q_u->valuename;
+ uint32 in_size = q_u->size;
+ uint32 *type = &r_u->type;
+ uint32 *out_size = &r_u->size;
+ uint8 **data = &r_u->data;
+ uint32 *needed = &r_u->needed;
+ WERROR status;
+ fstring value;
+ Printer_entry *Printer = find_printer_index_by_hnd(p, handle);
+ NT_PRINTER_INFO_LEVEL *printer = NULL;
+ int snum = 0;
+
+ /*
+ * Reminder: when it's a string, the length is in BYTES
+ * even if UNICODE is negociated.
+ *
+ * JFM, 4/19/1999
+ */
+
+ *out_size = in_size;
+
+ /* in case of problem, return some default values */
+
+ *needed = 0;
+ *type = 0;
+
+ DEBUG(4,("_spoolss_getprinterdata\n"));
+
+ if ( !Printer ) {
+ DEBUG(2,("_spoolss_getprinterdata: Invalid handle (%s:%u:%u).\n", OUR_HANDLE(handle)));
+ status = WERR_BADFID;
+ goto done;
+ }
+
+ unistr2_to_ascii(value, valuename, sizeof(value)-1);
+
+ if ( Printer->printer_type == PRINTER_HANDLE_IS_PRINTSERVER )
+ status = getprinterdata_printer_server( p->mem_ctx, value, type, data, needed, *out_size );
+ else
+ {
+ if ( !get_printer_snum(p,handle, &snum) ) {
+ status = WERR_BADFID;
+ goto done;
+ }
+
+ status = get_a_printer(Printer, &printer, 2, lp_servicename(snum));
+ if ( !W_ERROR_IS_OK(status) )
+ goto done;
+
+ /* XP sends this and wants to change id value from the PRINTER_INFO_0 */
+
+ if ( strequal(value, "ChangeId") ) {
+ *type = REG_DWORD;
+ *needed = sizeof(uint32);
+ if ( (*data = (uint8*)talloc(p->mem_ctx, sizeof(uint32))) == NULL) {
+ status = WERR_NOMEM;
+ goto done;
+ }
+ SIVAL( *data, 0, printer->info_2->changeid );
+ status = WERR_OK;
+ }
+ else
+ status = get_printer_dataex( p->mem_ctx, printer, SPOOL_PRINTERDATA_KEY, value, type, data, needed, *out_size );
+ }
+
+ if (*needed > *out_size)
+ status = WERR_MORE_DATA;
+
+done:
+ if ( !W_ERROR_IS_OK(status) )
+ {
+ DEBUG(5, ("error %d: allocating %d\n", W_ERROR_V(status),*out_size));
+
+ /* reply this param doesn't exist */
+
+ if ( *out_size ) {
+ if((*data=(uint8 *)talloc_zero(p->mem_ctx, *out_size*sizeof(uint8))) == NULL) {
+ if ( printer )
+ free_a_printer( &printer, 2 );
+ return WERR_NOMEM;
+ }
+ }
+ else {
+ *data = NULL;
+ }
+ }
+
+ /* cleanup & exit */
+
+ if ( printer )
+ free_a_printer( &printer, 2 );
+
+ return status;
+}
+
+/*********************************************************
+ Connect to the client machine.
+**********************************************************/
+
+static BOOL spoolss_connect_to_client(struct cli_state *the_cli,
+ struct in_addr *client_ip, const char *remote_machine)
+{
+ ZERO_STRUCTP(the_cli);
+
+ if(cli_initialise(the_cli) == NULL) {
+ DEBUG(0,("spoolss_connect_to_client: unable to initialize client connection.\n"));
+ return False;
+ }
+
+ if ( is_zero_ip(*client_ip) ) {
+ if(!resolve_name( remote_machine, &the_cli->dest_ip, 0x20)) {
+ DEBUG(0,("spoolss_connect_to_client: Can't resolve address for %s\n", remote_machine));
+ cli_shutdown(the_cli);
+ return False;
+ }
+
+ if (ismyip(the_cli->dest_ip)) {
+ DEBUG(0,("spoolss_connect_to_client: Machine %s is one of our addresses. Cannot add to ourselves.\n", remote_machine));
+ cli_shutdown(the_cli);
+ return False;
+ }
+ }
+ else {
+ the_cli->dest_ip.s_addr = client_ip->s_addr;
+ DEBUG(5,("spoolss_connect_to_client: Using address %s (no name resolution necessary)\n",
+ inet_ntoa(*client_ip) ));
+ }
+
+ if (!cli_connect(the_cli, remote_machine, &the_cli->dest_ip)) {
+ DEBUG(0,("spoolss_connect_to_client: unable to connect to SMB server on machine %s. Error was : %s.\n", remote_machine, cli_errstr(the_cli) ));
+ cli_shutdown(the_cli);
+ return False;
+ }
+
+ if (!attempt_netbios_session_request(the_cli, global_myname(), remote_machine, &the_cli->dest_ip)) {
+ DEBUG(0,("spoolss_connect_to_client: machine %s rejected the NetBIOS session request.\n",
+ remote_machine));
+ cli_shutdown(the_cli);
+ return False;
+ }
+
+ the_cli->protocol = PROTOCOL_NT1;
+ cli_setup_signing_state(the_cli, lp_client_signing());
+
+ if (!cli_negprot(the_cli)) {
+ DEBUG(0,("spoolss_connect_to_client: machine %s rejected the negotiate protocol. Error was : %s.\n", remote_machine, cli_errstr(the_cli) ));
+ cli_shutdown(the_cli);
+ return False;
+ }
+
+ if (the_cli->protocol != PROTOCOL_NT1) {
+ DEBUG(0,("spoolss_connect_to_client: machine %s didn't negotiate NT protocol.\n", remote_machine));
+ cli_shutdown(the_cli);
+ return False;
+ }
+
+ /*
+ * Do an anonymous session setup.
+ */
+
+ if (!cli_session_setup(the_cli, "", "", 0, "", 0, "")) {
+ DEBUG(0,("spoolss_connect_to_client: machine %s rejected the session setup. Error was : %s.\n", remote_machine, cli_errstr(the_cli) ));
+ cli_shutdown(the_cli);
+ return False;
+ }
+
+ if (!(the_cli->sec_mode & 1)) {
+ DEBUG(0,("spoolss_connect_to_client: machine %s isn't in user level security mode\n", remote_machine));
+ cli_shutdown(the_cli);
+ return False;
+ }
+
+ if (!cli_send_tconX(the_cli, "IPC$", "IPC", "", 1)) {
+ DEBUG(0,("spoolss_connect_to_client: machine %s rejected the tconX on the IPC$ share. Error was : %s.\n", remote_machine, cli_errstr(the_cli) ));
+ cli_shutdown(the_cli);
+ return False;
+ }
+
+ /*
+ * Ok - we have an anonymous connection to the IPC$ share.
+ * Now start the NT Domain stuff :-).
+ */
+
+ if(cli_nt_session_open(the_cli, PI_SPOOLSS) == False) {
+ DEBUG(0,("spoolss_connect_to_client: unable to open the domain client session to machine %s. Error was : %s.\n", remote_machine, cli_errstr(the_cli)));
+ cli_nt_session_close(the_cli);
+ cli_ulogoff(the_cli);
+ cli_shutdown(the_cli);
+ return False;
+ }
+
+ return True;
+}
+
+/***************************************************************************
+ Connect to the client.
+****************************************************************************/
+
+static BOOL srv_spoolss_replyopenprinter(int snum, const char *printer,
+ uint32 localprinter, uint32 type,
+ POLICY_HND *handle, struct in_addr *client_ip)
+{
+ WERROR result;
+
+ /*
+ * If it's the first connection, contact the client
+ * and connect to the IPC$ share anonymously
+ */
+ if (smb_connections==0) {
+ fstring unix_printer;
+
+ fstrcpy(unix_printer, printer+2); /* the +2 is to strip the leading 2 backslashs */
+
+ ZERO_STRUCT(notify_cli);
+
+ if(!spoolss_connect_to_client(&notify_cli, client_ip, unix_printer))
+ return False;
+
+ message_register(MSG_PRINTER_NOTIFY2, receive_notify2_message_list);
+ /* Tell the connections db we're now interested in printer
+ * notify messages. */
+ register_message_flags( True, FLAG_MSG_PRINTING );
+ }
+
+ /*
+ * Tell the specific printing tdb we want messages for this printer
+ * by registering our PID.
+ */
+
+ if (!print_notify_register_pid(snum))
+ DEBUG(0,("print_notify_register_pid: Failed to register our pid for printer %s\n", printer ));
+
+ smb_connections++;
+
+ result = cli_spoolss_reply_open_printer(&notify_cli, notify_cli.mem_ctx, printer, localprinter,
+ type, handle);
+
+ if (!W_ERROR_IS_OK(result))
+ DEBUG(5,("srv_spoolss_reply_open_printer: Client RPC returned [%s]\n",
+ dos_errstr(result)));
+
+ return (W_ERROR_IS_OK(result));
+}
+
+/********************************************************************
+ * _spoolss_rffpcnex
+ * ReplyFindFirstPrinterChangeNotifyEx
+ *
+ * before replying OK: status=0 a rpc call is made to the workstation
+ * asking ReplyOpenPrinter
+ *
+ * in fact ReplyOpenPrinter is the changenotify equivalent on the spoolss pipe
+ * called from api_spoolss_rffpcnex
+ ********************************************************************/
+
+WERROR _spoolss_rffpcnex(pipes_struct *p, SPOOL_Q_RFFPCNEX *q_u, SPOOL_R_RFFPCNEX *r_u)
+{
+ POLICY_HND *handle = &q_u->handle;
+ uint32 flags = q_u->flags;
+ uint32 options = q_u->options;
+ UNISTR2 *localmachine = &q_u->localmachine;
+ uint32 printerlocal = q_u->printerlocal;
+ int snum = -1;
+ SPOOL_NOTIFY_OPTION *option = q_u->option;
+ struct in_addr client_ip;
+
+ /* store the notify value in the printer struct */
+
+ Printer_entry *Printer=find_printer_index_by_hnd(p, handle);
+
+ if (!Printer) {
+ DEBUG(2,("_spoolss_rffpcnex: Invalid handle (%s:%u:%u).\n", OUR_HANDLE(handle)));
+ return WERR_BADFID;
+ }
+
+ Printer->notify.flags=flags;
+ Printer->notify.options=options;
+ Printer->notify.printerlocal=printerlocal;
+
+ if (Printer->notify.option)
+ free_spool_notify_option(&Printer->notify.option);
+
+ Printer->notify.option=dup_spool_notify_option(option);
+
+ unistr2_to_ascii(Printer->notify.localmachine, localmachine,
+ sizeof(Printer->notify.localmachine)-1);
+
+ /* Connect to the client machine and send a ReplyOpenPrinter */
+
+ if ( Printer->printer_type == PRINTER_HANDLE_IS_PRINTSERVER)
+ snum = -1;
+ else if ( (Printer->printer_type == PRINTER_HANDLE_IS_PRINTER) &&
+ !get_printer_snum(p, handle, &snum) )
+ return WERR_BADFID;
+
+ client_ip.s_addr = inet_addr(p->conn->client_address);
+
+ if(!srv_spoolss_replyopenprinter(snum, Printer->notify.localmachine,
+ Printer->notify.printerlocal, 1,
+ &Printer->notify.client_hnd, &client_ip))
+ return WERR_SERVER_UNAVAILABLE;
+
+ Printer->notify.client_connected=True;
+
+ return WERR_OK;
+}
+
+/*******************************************************************
+ * fill a notify_info_data with the servername
+ ********************************************************************/
+
+void spoolss_notify_server_name(int snum,
+ SPOOL_NOTIFY_INFO_DATA *data,
+ print_queue_struct *queue,
+ NT_PRINTER_INFO_LEVEL *printer,
+ TALLOC_CTX *mem_ctx)
+{
+ pstring temp_name, temp;
+ uint32 len;
+
+ slprintf(temp_name, sizeof(temp_name)-1, "\\\\%s", get_called_name());
+
+ len = rpcstr_push(temp, temp_name, sizeof(temp)-2, STR_TERMINATE);
+
+ data->notify_data.data.length = len;
+ data->notify_data.data.string = (uint16 *)talloc(mem_ctx, len);
+
+ if (!data->notify_data.data.string) {
+ data->notify_data.data.length = 0;
+ return;
+ }
+
+ memcpy(data->notify_data.data.string, temp, len);
+}
+
+/*******************************************************************
+ * fill a notify_info_data with the printername (not including the servername).
+ ********************************************************************/
+
+void spoolss_notify_printer_name(int snum,
+ SPOOL_NOTIFY_INFO_DATA *data,
+ print_queue_struct *queue,
+ NT_PRINTER_INFO_LEVEL *printer,
+ TALLOC_CTX *mem_ctx)
+{
+ pstring temp;
+ uint32 len;
+
+ /* the notify name should not contain the \\server\ part */
+ char *p = strrchr(printer->info_2->printername, '\\');
+
+ if (!p) {
+ p = printer->info_2->printername;
+ } else {
+ p++;
+ }
+
+ len = rpcstr_push(temp, p, sizeof(temp)-2, STR_TERMINATE);
+
+ data->notify_data.data.length = len;
+ data->notify_data.data.string = (uint16 *)talloc(mem_ctx, len);
+
+ if (!data->notify_data.data.string) {
+ data->notify_data.data.length = 0;
+ return;
+ }
+
+ memcpy(data->notify_data.data.string, temp, len);
+}
+
+/*******************************************************************
+ * fill a notify_info_data with the servicename
+ ********************************************************************/
+
+void spoolss_notify_share_name(int snum,
+ SPOOL_NOTIFY_INFO_DATA *data,
+ print_queue_struct *queue,
+ NT_PRINTER_INFO_LEVEL *printer,
+ TALLOC_CTX *mem_ctx)
+{
+ pstring temp;
+ uint32 len;
+
+ len = rpcstr_push(temp, lp_servicename(snum), sizeof(temp)-2, STR_TERMINATE);
+
+ data->notify_data.data.length = len;
+ data->notify_data.data.string = (uint16 *)talloc(mem_ctx, len);
+
+ if (!data->notify_data.data.string) {
+ data->notify_data.data.length = 0;
+ return;
+ }
+
+ memcpy(data->notify_data.data.string, temp, len);
+}
+
+/*******************************************************************
+ * fill a notify_info_data with the port name
+ ********************************************************************/
+
+void spoolss_notify_port_name(int snum,
+ SPOOL_NOTIFY_INFO_DATA *data,
+ print_queue_struct *queue,
+ NT_PRINTER_INFO_LEVEL *printer,
+ TALLOC_CTX *mem_ctx)
+{
+ pstring temp;
+ uint32 len;
+
+ /* even if it's strange, that's consistant in all the code */
+
+ len = rpcstr_push(temp, printer->info_2->portname, sizeof(temp)-2, STR_TERMINATE);
+
+ data->notify_data.data.length = len;
+ data->notify_data.data.string = (uint16 *)talloc(mem_ctx, len);
+
+ if (!data->notify_data.data.string) {
+ data->notify_data.data.length = 0;
+ return;
+ }
+
+ memcpy(data->notify_data.data.string, temp, len);
+}
+
+/*******************************************************************
+ * fill a notify_info_data with the printername
+ * but it doesn't exist, have to see what to do
+ ********************************************************************/
+
+void spoolss_notify_driver_name(int snum,
+ SPOOL_NOTIFY_INFO_DATA *data,
+ print_queue_struct *queue,
+ NT_PRINTER_INFO_LEVEL *printer,
+ TALLOC_CTX *mem_ctx)
+{
+ pstring temp;
+ uint32 len;
+
+ len = rpcstr_push(temp, printer->info_2->drivername, sizeof(temp)-2, STR_TERMINATE);
+
+ data->notify_data.data.length = len;
+ data->notify_data.data.string = (uint16 *)talloc(mem_ctx, len);
+
+ if (!data->notify_data.data.string) {
+ data->notify_data.data.length = 0;
+ return;
+ }
+
+ memcpy(data->notify_data.data.string, temp, len);
+}
+
+/*******************************************************************
+ * fill a notify_info_data with the comment
+ ********************************************************************/
+
+void spoolss_notify_comment(int snum,
+ SPOOL_NOTIFY_INFO_DATA *data,
+ print_queue_struct *queue,
+ NT_PRINTER_INFO_LEVEL *printer,
+ TALLOC_CTX *mem_ctx)
+{
+ pstring temp;
+ uint32 len;
+
+ if (*printer->info_2->comment == '\0')
+ len = rpcstr_push(temp, lp_comment(snum), sizeof(temp)-2, STR_TERMINATE);
+ else
+ len = rpcstr_push(temp, printer->info_2->comment, sizeof(temp)-2, STR_TERMINATE);
+
+ data->notify_data.data.length = len;
+ data->notify_data.data.string = (uint16 *)talloc(mem_ctx, len);
+
+ if (!data->notify_data.data.string) {
+ data->notify_data.data.length = 0;
+ return;
+ }
+
+ memcpy(data->notify_data.data.string, temp, len);
+}
+
+/*******************************************************************
+ * fill a notify_info_data with the comment
+ * location = "Room 1, floor 2, building 3"
+ ********************************************************************/
+
+void spoolss_notify_location(int snum,
+ SPOOL_NOTIFY_INFO_DATA *data,
+ print_queue_struct *queue,
+ NT_PRINTER_INFO_LEVEL *printer,
+ TALLOC_CTX *mem_ctx)
+{
+ pstring temp;
+ uint32 len;
+
+ len = rpcstr_push(temp, printer->info_2->location,sizeof(temp)-2, STR_TERMINATE);
+
+ data->notify_data.data.length = len;
+ data->notify_data.data.string = (uint16 *)talloc(mem_ctx, len);
+
+ if (!data->notify_data.data.string) {
+ data->notify_data.data.length = 0;
+ return;
+ }
+
+ memcpy(data->notify_data.data.string, temp, len);
+}
+
+/*******************************************************************
+ * fill a notify_info_data with the device mode
+ * jfm:xxxx don't to it for know but that's a real problem !!!
+ ********************************************************************/
+
+static void spoolss_notify_devmode(int snum,
+ SPOOL_NOTIFY_INFO_DATA *data,
+ print_queue_struct *queue,
+ NT_PRINTER_INFO_LEVEL *printer,
+ TALLOC_CTX *mem_ctx)
+{
+}
+
+/*******************************************************************
+ * fill a notify_info_data with the separator file name
+ ********************************************************************/
+
+void spoolss_notify_sepfile(int snum,
+ SPOOL_NOTIFY_INFO_DATA *data,
+ print_queue_struct *queue,
+ NT_PRINTER_INFO_LEVEL *printer,
+ TALLOC_CTX *mem_ctx)
+{
+ pstring temp;
+ uint32 len;
+
+ len = rpcstr_push(temp, printer->info_2->sepfile, sizeof(temp)-2, STR_TERMINATE);
+
+ data->notify_data.data.length = len;
+ data->notify_data.data.string = (uint16 *)talloc(mem_ctx, len);
+
+ if (!data->notify_data.data.string) {
+ data->notify_data.data.length = 0;
+ return;
+ }
+
+ memcpy(data->notify_data.data.string, temp, len);
+}
+
+/*******************************************************************
+ * fill a notify_info_data with the print processor
+ * jfm:xxxx return always winprint to indicate we don't do anything to it
+ ********************************************************************/
+
+void spoolss_notify_print_processor(int snum,
+ SPOOL_NOTIFY_INFO_DATA *data,
+ print_queue_struct *queue,
+ NT_PRINTER_INFO_LEVEL *printer,
+ TALLOC_CTX *mem_ctx)
+{
+ pstring temp;
+ uint32 len;
+
+ len = rpcstr_push(temp, printer->info_2->printprocessor, sizeof(temp)-2, STR_TERMINATE);
+
+ data->notify_data.data.length = len;
+ data->notify_data.data.string = (uint16 *)talloc(mem_ctx, len);
+
+ if (!data->notify_data.data.string) {
+ data->notify_data.data.length = 0;
+ return;
+ }
+
+ memcpy(data->notify_data.data.string, temp, len);
+}
+
+/*******************************************************************
+ * fill a notify_info_data with the print processor options
+ * jfm:xxxx send an empty string
+ ********************************************************************/
+
+void spoolss_notify_parameters(int snum,
+ SPOOL_NOTIFY_INFO_DATA *data,
+ print_queue_struct *queue,
+ NT_PRINTER_INFO_LEVEL *printer,
+ TALLOC_CTX *mem_ctx)
+{
+ pstring temp;
+ uint32 len;
+
+ len = rpcstr_push(temp, printer->info_2->parameters, sizeof(temp)-2, STR_TERMINATE);
+
+ data->notify_data.data.length = len;
+ data->notify_data.data.string = (uint16 *)talloc(mem_ctx, len);
+
+ if (!data->notify_data.data.string) {
+ data->notify_data.data.length = 0;
+ return;
+ }
+
+ memcpy(data->notify_data.data.string, temp, len);
+}
+
+/*******************************************************************
+ * fill a notify_info_data with the data type
+ * jfm:xxxx always send RAW as data type
+ ********************************************************************/
+
+void spoolss_notify_datatype(int snum,
+ SPOOL_NOTIFY_INFO_DATA *data,
+ print_queue_struct *queue,
+ NT_PRINTER_INFO_LEVEL *printer,
+ TALLOC_CTX *mem_ctx)
+{
+ pstring temp;
+ uint32 len;
+
+ len = rpcstr_push(temp, printer->info_2->datatype, sizeof(pstring)-2, STR_TERMINATE);
+
+ data->notify_data.data.length = len;
+ data->notify_data.data.string = (uint16 *)talloc(mem_ctx, len);
+
+ if (!data->notify_data.data.string) {
+ data->notify_data.data.length = 0;
+ return;
+ }
+
+ memcpy(data->notify_data.data.string, temp, len);
+}
+
+/*******************************************************************
+ * fill a notify_info_data with the security descriptor
+ * jfm:xxxx send an null pointer to say no security desc
+ * have to implement security before !
+ ********************************************************************/
+
+static void spoolss_notify_security_desc(int snum,
+ SPOOL_NOTIFY_INFO_DATA *data,
+ print_queue_struct *queue,
+ NT_PRINTER_INFO_LEVEL *printer,
+ TALLOC_CTX *mem_ctx)
+{
+ data->notify_data.sd.size = printer->info_2->secdesc_buf->len;
+ data->notify_data.sd.desc = dup_sec_desc( mem_ctx, printer->info_2->secdesc_buf->sec ) ;
+}
+
+/*******************************************************************
+ * fill a notify_info_data with the attributes
+ * jfm:xxxx a samba printer is always shared
+ ********************************************************************/
+
+void spoolss_notify_attributes(int snum,
+ SPOOL_NOTIFY_INFO_DATA *data,
+ print_queue_struct *queue,
+ NT_PRINTER_INFO_LEVEL *printer,
+ TALLOC_CTX *mem_ctx)
+{
+ data->notify_data.value[0] = printer->info_2->attributes;
+ data->notify_data.value[1] = 0;
+}
+
+/*******************************************************************
+ * fill a notify_info_data with the priority
+ ********************************************************************/
+
+static void spoolss_notify_priority(int snum,
+ SPOOL_NOTIFY_INFO_DATA *data,
+ print_queue_struct *queue,
+ NT_PRINTER_INFO_LEVEL *printer,
+ TALLOC_CTX *mem_ctx)
+{
+ data->notify_data.value[0] = printer->info_2->priority;
+ data->notify_data.value[1] = 0;
+}
+
+/*******************************************************************
+ * fill a notify_info_data with the default priority
+ ********************************************************************/
+
+static void spoolss_notify_default_priority(int snum,
+ SPOOL_NOTIFY_INFO_DATA *data,
+ print_queue_struct *queue,
+ NT_PRINTER_INFO_LEVEL *printer,
+ TALLOC_CTX *mem_ctx)
+{
+ data->notify_data.value[0] = printer->info_2->default_priority;
+ data->notify_data.value[1] = 0;
+}
+
+/*******************************************************************
+ * fill a notify_info_data with the start time
+ ********************************************************************/
+
+static void spoolss_notify_start_time(int snum,
+ SPOOL_NOTIFY_INFO_DATA *data,
+ print_queue_struct *queue,
+ NT_PRINTER_INFO_LEVEL *printer,
+ TALLOC_CTX *mem_ctx)
+{
+ data->notify_data.value[0] = printer->info_2->starttime;
+ data->notify_data.value[1] = 0;
+}
+
+/*******************************************************************
+ * fill a notify_info_data with the until time
+ ********************************************************************/
+
+static void spoolss_notify_until_time(int snum,
+ SPOOL_NOTIFY_INFO_DATA *data,
+ print_queue_struct *queue,
+ NT_PRINTER_INFO_LEVEL *printer,
+ TALLOC_CTX *mem_ctx)
+{
+ data->notify_data.value[0] = printer->info_2->untiltime;
+ data->notify_data.value[1] = 0;
+}
+
+/*******************************************************************
+ * fill a notify_info_data with the status
+ ********************************************************************/
+
+static void spoolss_notify_status(int snum,
+ SPOOL_NOTIFY_INFO_DATA *data,
+ print_queue_struct *queue,
+ NT_PRINTER_INFO_LEVEL *printer,
+ TALLOC_CTX *mem_ctx)
+{
+ print_status_struct status;
+
+ print_queue_length(snum, &status);
+ data->notify_data.value[0]=(uint32) status.status;
+ data->notify_data.value[1] = 0;
+}
+
+/*******************************************************************
+ * fill a notify_info_data with the number of jobs queued
+ ********************************************************************/
+
+void spoolss_notify_cjobs(int snum,
+ SPOOL_NOTIFY_INFO_DATA *data,
+ print_queue_struct *queue,
+ NT_PRINTER_INFO_LEVEL *printer,
+ TALLOC_CTX *mem_ctx)
+{
+ data->notify_data.value[0] = print_queue_length(snum, NULL);
+ data->notify_data.value[1] = 0;
+}
+
+/*******************************************************************
+ * fill a notify_info_data with the average ppm
+ ********************************************************************/
+
+static void spoolss_notify_average_ppm(int snum,
+ SPOOL_NOTIFY_INFO_DATA *data,
+ print_queue_struct *queue,
+ NT_PRINTER_INFO_LEVEL *printer,
+ TALLOC_CTX *mem_ctx)
+{
+ /* always respond 8 pages per minutes */
+ /* a little hard ! */
+ data->notify_data.value[0] = printer->info_2->averageppm;
+ data->notify_data.value[1] = 0;
+}
+
+/*******************************************************************
+ * fill a notify_info_data with username
+ ********************************************************************/
+
+static void spoolss_notify_username(int snum,
+ SPOOL_NOTIFY_INFO_DATA *data,
+ print_queue_struct *queue,
+ NT_PRINTER_INFO_LEVEL *printer,
+ TALLOC_CTX *mem_ctx)
+{
+ pstring temp;
+ uint32 len;
+
+ len = rpcstr_push(temp, queue->fs_user, sizeof(temp)-2, STR_TERMINATE);
+
+ data->notify_data.data.length = len;
+ data->notify_data.data.string = (uint16 *)talloc(mem_ctx, len);
+
+ if (!data->notify_data.data.string) {
+ data->notify_data.data.length = 0;
+ return;
+ }
+
+ memcpy(data->notify_data.data.string, temp, len);
+}
+
+/*******************************************************************
+ * fill a notify_info_data with job status
+ ********************************************************************/
+
+static void spoolss_notify_job_status(int snum,
+ SPOOL_NOTIFY_INFO_DATA *data,
+ print_queue_struct *queue,
+ NT_PRINTER_INFO_LEVEL *printer,
+ TALLOC_CTX *mem_ctx)
+{
+ data->notify_data.value[0]=nt_printj_status(queue->status);
+ data->notify_data.value[1] = 0;
+}
+
+/*******************************************************************
+ * fill a notify_info_data with job name
+ ********************************************************************/
+
+static void spoolss_notify_job_name(int snum,
+ SPOOL_NOTIFY_INFO_DATA *data,
+ print_queue_struct *queue,
+ NT_PRINTER_INFO_LEVEL *printer,
+ TALLOC_CTX *mem_ctx)
+{
+ pstring temp;
+ uint32 len;
+
+ len = rpcstr_push(temp, queue->fs_file, sizeof(temp)-2, STR_TERMINATE);
+
+ data->notify_data.data.length = len;
+ data->notify_data.data.string = (uint16 *)talloc(mem_ctx, len);
+
+ if (!data->notify_data.data.string) {
+ data->notify_data.data.length = 0;
+ return;
+ }
+
+ memcpy(data->notify_data.data.string, temp, len);
+}
+
+/*******************************************************************
+ * fill a notify_info_data with job status
+ ********************************************************************/
+
+static void spoolss_notify_job_status_string(int snum,
+ SPOOL_NOTIFY_INFO_DATA *data,
+ print_queue_struct *queue,
+ NT_PRINTER_INFO_LEVEL *printer,
+ TALLOC_CTX *mem_ctx)
+{
+ /*
+ * Now we're returning job status codes we just return a "" here. JRA.
+ */
+
+ const char *p = "";
+ pstring temp;
+ uint32 len;
+
+#if 0 /* NO LONGER NEEDED - JRA. 02/22/2001 */
+ p = "unknown";
+
+ switch (queue->status) {
+ case LPQ_QUEUED:
+ p = "Queued";
+ break;
+ case LPQ_PAUSED:
+ p = ""; /* NT provides the paused string */
+ break;
+ case LPQ_SPOOLING:
+ p = "Spooling";
+ break;
+ case LPQ_PRINTING:
+ p = "Printing";
+ break;
+ }
+#endif /* NO LONGER NEEDED. */
+
+ len = rpcstr_push(temp, p, sizeof(temp) - 2, STR_TERMINATE);
+
+ data->notify_data.data.length = len;
+ data->notify_data.data.string = (uint16 *)talloc(mem_ctx, len);
+
+ if (!data->notify_data.data.string) {
+ data->notify_data.data.length = 0;
+ return;
+ }
+
+ memcpy(data->notify_data.data.string, temp, len);
+}
+
+/*******************************************************************
+ * fill a notify_info_data with job time
+ ********************************************************************/
+
+static void spoolss_notify_job_time(int snum,
+ SPOOL_NOTIFY_INFO_DATA *data,
+ print_queue_struct *queue,
+ NT_PRINTER_INFO_LEVEL *printer,
+ TALLOC_CTX *mem_ctx)
+{
+ data->notify_data.value[0]=0x0;
+ data->notify_data.value[1]=0;
+}
+
+/*******************************************************************
+ * fill a notify_info_data with job size
+ ********************************************************************/
+
+static void spoolss_notify_job_size(int snum,
+ SPOOL_NOTIFY_INFO_DATA *data,
+ print_queue_struct *queue,
+ NT_PRINTER_INFO_LEVEL *printer,
+ TALLOC_CTX *mem_ctx)
+{
+ data->notify_data.value[0]=queue->size;
+ data->notify_data.value[1]=0;
+}
+
+/*******************************************************************
+ * fill a notify_info_data with page info
+ ********************************************************************/
+static void spoolss_notify_total_pages(int snum,
+ SPOOL_NOTIFY_INFO_DATA *data,
+ print_queue_struct *queue,
+ NT_PRINTER_INFO_LEVEL *printer,
+ TALLOC_CTX *mem_ctx)
+{
+ data->notify_data.value[0]=queue->page_count;
+ data->notify_data.value[1]=0;
+}
+
+/*******************************************************************
+ * fill a notify_info_data with pages printed info.
+ ********************************************************************/
+static void spoolss_notify_pages_printed(int snum,
+ SPOOL_NOTIFY_INFO_DATA *data,
+ print_queue_struct *queue,
+ NT_PRINTER_INFO_LEVEL *printer,
+ TALLOC_CTX *mem_ctx)
+{
+ data->notify_data.value[0]=0; /* Add code when back-end tracks this */
+ data->notify_data.value[1]=0;
+}
+
+/*******************************************************************
+ Fill a notify_info_data with job position.
+ ********************************************************************/
+
+static void spoolss_notify_job_position(int snum,
+ SPOOL_NOTIFY_INFO_DATA *data,
+ print_queue_struct *queue,
+ NT_PRINTER_INFO_LEVEL *printer,
+ TALLOC_CTX *mem_ctx)
+{
+ data->notify_data.value[0]=queue->job;
+ data->notify_data.value[1]=0;
+}
+
+/*******************************************************************
+ Fill a notify_info_data with submitted time.
+ ********************************************************************/
+
+static void spoolss_notify_submitted_time(int snum,
+ SPOOL_NOTIFY_INFO_DATA *data,
+ print_queue_struct *queue,
+ NT_PRINTER_INFO_LEVEL *printer,
+ TALLOC_CTX *mem_ctx)
+{
+ struct tm *t;
+ uint32 len;
+ SYSTEMTIME st;
+ char *p;
+
+ t=gmtime(&queue->time);
+
+ len = sizeof(SYSTEMTIME);
+
+ data->notify_data.data.length = len;
+ data->notify_data.data.string = (uint16 *)talloc(mem_ctx, len);
+
+ if (!data->notify_data.data.string) {
+ data->notify_data.data.length = 0;
+ return;
+ }
+
+ make_systemtime(&st, t);
+
+ /*
+ * Systemtime must be linearized as a set of UINT16's.
+ * Fix from Benjamin (Bj) Kuit bj@it.uts.edu.au
+ */
+
+ p = (char *)data->notify_data.data.string;
+ SSVAL(p, 0, st.year);
+ SSVAL(p, 2, st.month);
+ SSVAL(p, 4, st.dayofweek);
+ SSVAL(p, 6, st.day);
+ SSVAL(p, 8, st.hour);
+ SSVAL(p, 10, st.minute);
+ SSVAL(p, 12, st.second);
+ SSVAL(p, 14, st.milliseconds);
+}
+
+struct s_notify_info_data_table
+{
+ uint16 type;
+ uint16 field;
+ const char *name;
+ uint32 size;
+ void (*fn) (int snum, SPOOL_NOTIFY_INFO_DATA *data,
+ print_queue_struct *queue,
+ NT_PRINTER_INFO_LEVEL *printer, TALLOC_CTX *mem_ctx);
+};
+
+/* A table describing the various print notification constants and
+ whether the notification data is a pointer to a variable sized
+ buffer, a one value uint32 or a two value uint32. */
+
+static const struct s_notify_info_data_table notify_info_data_table[] =
+{
+{ PRINTER_NOTIFY_TYPE, PRINTER_NOTIFY_SERVER_NAME, "PRINTER_NOTIFY_SERVER_NAME", NOTIFY_STRING, spoolss_notify_server_name },
+{ PRINTER_NOTIFY_TYPE, PRINTER_NOTIFY_PRINTER_NAME, "PRINTER_NOTIFY_PRINTER_NAME", NOTIFY_STRING, spoolss_notify_printer_name },
+{ PRINTER_NOTIFY_TYPE, PRINTER_NOTIFY_SHARE_NAME, "PRINTER_NOTIFY_SHARE_NAME", NOTIFY_STRING, spoolss_notify_share_name },
+{ PRINTER_NOTIFY_TYPE, PRINTER_NOTIFY_PORT_NAME, "PRINTER_NOTIFY_PORT_NAME", NOTIFY_STRING, spoolss_notify_port_name },
+{ PRINTER_NOTIFY_TYPE, PRINTER_NOTIFY_DRIVER_NAME, "PRINTER_NOTIFY_DRIVER_NAME", NOTIFY_STRING, spoolss_notify_driver_name },
+{ PRINTER_NOTIFY_TYPE, PRINTER_NOTIFY_COMMENT, "PRINTER_NOTIFY_COMMENT", NOTIFY_STRING, spoolss_notify_comment },
+{ PRINTER_NOTIFY_TYPE, PRINTER_NOTIFY_LOCATION, "PRINTER_NOTIFY_LOCATION", NOTIFY_STRING, spoolss_notify_location },
+{ PRINTER_NOTIFY_TYPE, PRINTER_NOTIFY_DEVMODE, "PRINTER_NOTIFY_DEVMODE", NOTIFY_POINTER, spoolss_notify_devmode },
+{ PRINTER_NOTIFY_TYPE, PRINTER_NOTIFY_SEPFILE, "PRINTER_NOTIFY_SEPFILE", NOTIFY_STRING, spoolss_notify_sepfile },
+{ PRINTER_NOTIFY_TYPE, PRINTER_NOTIFY_PRINT_PROCESSOR, "PRINTER_NOTIFY_PRINT_PROCESSOR", NOTIFY_STRING, spoolss_notify_print_processor },
+{ PRINTER_NOTIFY_TYPE, PRINTER_NOTIFY_PARAMETERS, "PRINTER_NOTIFY_PARAMETERS", NOTIFY_STRING, spoolss_notify_parameters },
+{ PRINTER_NOTIFY_TYPE, PRINTER_NOTIFY_DATATYPE, "PRINTER_NOTIFY_DATATYPE", NOTIFY_STRING, spoolss_notify_datatype },
+{ PRINTER_NOTIFY_TYPE, PRINTER_NOTIFY_SECURITY_DESCRIPTOR, "PRINTER_NOTIFY_SECURITY_DESCRIPTOR", NOTIFY_SECDESC, spoolss_notify_security_desc },
+{ PRINTER_NOTIFY_TYPE, PRINTER_NOTIFY_ATTRIBUTES, "PRINTER_NOTIFY_ATTRIBUTES", NOTIFY_ONE_VALUE, spoolss_notify_attributes },
+{ PRINTER_NOTIFY_TYPE, PRINTER_NOTIFY_PRIORITY, "PRINTER_NOTIFY_PRIORITY", NOTIFY_ONE_VALUE, spoolss_notify_priority },
+{ PRINTER_NOTIFY_TYPE, PRINTER_NOTIFY_DEFAULT_PRIORITY, "PRINTER_NOTIFY_DEFAULT_PRIORITY", NOTIFY_ONE_VALUE, spoolss_notify_default_priority },
+{ PRINTER_NOTIFY_TYPE, PRINTER_NOTIFY_START_TIME, "PRINTER_NOTIFY_START_TIME", NOTIFY_ONE_VALUE, spoolss_notify_start_time },
+{ PRINTER_NOTIFY_TYPE, PRINTER_NOTIFY_UNTIL_TIME, "PRINTER_NOTIFY_UNTIL_TIME", NOTIFY_ONE_VALUE, spoolss_notify_until_time },
+{ PRINTER_NOTIFY_TYPE, PRINTER_NOTIFY_STATUS, "PRINTER_NOTIFY_STATUS", NOTIFY_ONE_VALUE, spoolss_notify_status },
+{ PRINTER_NOTIFY_TYPE, PRINTER_NOTIFY_STATUS_STRING, "PRINTER_NOTIFY_STATUS_STRING", NOTIFY_POINTER, NULL },
+{ PRINTER_NOTIFY_TYPE, PRINTER_NOTIFY_CJOBS, "PRINTER_NOTIFY_CJOBS", NOTIFY_ONE_VALUE, spoolss_notify_cjobs },
+{ PRINTER_NOTIFY_TYPE, PRINTER_NOTIFY_AVERAGE_PPM, "PRINTER_NOTIFY_AVERAGE_PPM", NOTIFY_ONE_VALUE, spoolss_notify_average_ppm },
+{ PRINTER_NOTIFY_TYPE, PRINTER_NOTIFY_TOTAL_PAGES, "PRINTER_NOTIFY_TOTAL_PAGES", NOTIFY_POINTER, NULL },
+{ PRINTER_NOTIFY_TYPE, PRINTER_NOTIFY_PAGES_PRINTED, "PRINTER_NOTIFY_PAGES_PRINTED", NOTIFY_POINTER, NULL },
+{ PRINTER_NOTIFY_TYPE, PRINTER_NOTIFY_TOTAL_BYTES, "PRINTER_NOTIFY_TOTAL_BYTES", NOTIFY_POINTER, NULL },
+{ PRINTER_NOTIFY_TYPE, PRINTER_NOTIFY_BYTES_PRINTED, "PRINTER_NOTIFY_BYTES_PRINTED", NOTIFY_POINTER, NULL },
+{ JOB_NOTIFY_TYPE, JOB_NOTIFY_PRINTER_NAME, "JOB_NOTIFY_PRINTER_NAME", NOTIFY_STRING, spoolss_notify_printer_name },
+{ JOB_NOTIFY_TYPE, JOB_NOTIFY_MACHINE_NAME, "JOB_NOTIFY_MACHINE_NAME", NOTIFY_STRING, spoolss_notify_server_name },
+{ JOB_NOTIFY_TYPE, JOB_NOTIFY_PORT_NAME, "JOB_NOTIFY_PORT_NAME", NOTIFY_STRING, spoolss_notify_port_name },
+{ JOB_NOTIFY_TYPE, JOB_NOTIFY_USER_NAME, "JOB_NOTIFY_USER_NAME", NOTIFY_STRING, spoolss_notify_username },
+{ JOB_NOTIFY_TYPE, JOB_NOTIFY_NOTIFY_NAME, "JOB_NOTIFY_NOTIFY_NAME", NOTIFY_STRING, spoolss_notify_username },
+{ JOB_NOTIFY_TYPE, JOB_NOTIFY_DATATYPE, "JOB_NOTIFY_DATATYPE", NOTIFY_STRING, spoolss_notify_datatype },
+{ JOB_NOTIFY_TYPE, JOB_NOTIFY_PRINT_PROCESSOR, "JOB_NOTIFY_PRINT_PROCESSOR", NOTIFY_STRING, spoolss_notify_print_processor },
+{ JOB_NOTIFY_TYPE, JOB_NOTIFY_PARAMETERS, "JOB_NOTIFY_PARAMETERS", NOTIFY_STRING, spoolss_notify_parameters },
+{ JOB_NOTIFY_TYPE, JOB_NOTIFY_DRIVER_NAME, "JOB_NOTIFY_DRIVER_NAME", NOTIFY_STRING, spoolss_notify_driver_name },
+{ JOB_NOTIFY_TYPE, JOB_NOTIFY_DEVMODE, "JOB_NOTIFY_DEVMODE", NOTIFY_POINTER, spoolss_notify_devmode },
+{ JOB_NOTIFY_TYPE, JOB_NOTIFY_STATUS, "JOB_NOTIFY_STATUS", NOTIFY_ONE_VALUE, spoolss_notify_job_status },
+{ JOB_NOTIFY_TYPE, JOB_NOTIFY_STATUS_STRING, "JOB_NOTIFY_STATUS_STRING", NOTIFY_STRING, spoolss_notify_job_status_string },
+{ JOB_NOTIFY_TYPE, JOB_NOTIFY_SECURITY_DESCRIPTOR, "JOB_NOTIFY_SECURITY_DESCRIPTOR", NOTIFY_POINTER, NULL },
+{ JOB_NOTIFY_TYPE, JOB_NOTIFY_DOCUMENT, "JOB_NOTIFY_DOCUMENT", NOTIFY_STRING, spoolss_notify_job_name },
+{ JOB_NOTIFY_TYPE, JOB_NOTIFY_PRIORITY, "JOB_NOTIFY_PRIORITY", NOTIFY_ONE_VALUE, spoolss_notify_priority },
+{ JOB_NOTIFY_TYPE, JOB_NOTIFY_POSITION, "JOB_NOTIFY_POSITION", NOTIFY_ONE_VALUE, spoolss_notify_job_position },
+{ JOB_NOTIFY_TYPE, JOB_NOTIFY_SUBMITTED, "JOB_NOTIFY_SUBMITTED", NOTIFY_POINTER, spoolss_notify_submitted_time },
+{ JOB_NOTIFY_TYPE, JOB_NOTIFY_START_TIME, "JOB_NOTIFY_START_TIME", NOTIFY_ONE_VALUE, spoolss_notify_start_time },
+{ JOB_NOTIFY_TYPE, JOB_NOTIFY_UNTIL_TIME, "JOB_NOTIFY_UNTIL_TIME", NOTIFY_ONE_VALUE, spoolss_notify_until_time },
+{ JOB_NOTIFY_TYPE, JOB_NOTIFY_TIME, "JOB_NOTIFY_TIME", NOTIFY_ONE_VALUE, spoolss_notify_job_time },
+{ JOB_NOTIFY_TYPE, JOB_NOTIFY_TOTAL_PAGES, "JOB_NOTIFY_TOTAL_PAGES", NOTIFY_ONE_VALUE, spoolss_notify_total_pages },
+{ JOB_NOTIFY_TYPE, JOB_NOTIFY_PAGES_PRINTED, "JOB_NOTIFY_PAGES_PRINTED", NOTIFY_ONE_VALUE, spoolss_notify_pages_printed },
+{ JOB_NOTIFY_TYPE, JOB_NOTIFY_TOTAL_BYTES, "JOB_NOTIFY_TOTAL_BYTES", NOTIFY_ONE_VALUE, spoolss_notify_job_size },
+};
+
+/*******************************************************************
+ Return the size of info_data structure.
+********************************************************************/
+
+static uint32 size_of_notify_info_data(uint16 type, uint16 field)
+{
+ int i=0;
+
+ for (i = 0; i < sizeof(notify_info_data_table); i++)
+ {
+ if ( (notify_info_data_table[i].type == type)
+ && (notify_info_data_table[i].field == field) )
+ {
+ switch(notify_info_data_table[i].size)
+ {
+ case NOTIFY_ONE_VALUE:
+ case NOTIFY_TWO_VALUE:
+ return 1;
+ case NOTIFY_STRING:
+ return 2;
+
+ /* The only pointer notify data I have seen on
+ the wire is the submitted time and this has
+ the notify size set to 4. -tpot */
+
+ case NOTIFY_POINTER:
+ return 4;
+
+ case NOTIFY_SECDESC:
+ return 5;
+ }
+ }
+ }
+
+ DEBUG(5, ("invalid notify data type %d/%d\n", type, field));
+
+ return 0;
+}
+
+/*******************************************************************
+ Return the type of notify_info_data.
+********************************************************************/
+
+static int type_of_notify_info_data(uint16 type, uint16 field)
+{
+ int i=0;
+
+ for (i = 0; i < sizeof(notify_info_data_table); i++) {
+ if (notify_info_data_table[i].type == type &&
+ notify_info_data_table[i].field == field)
+ return notify_info_data_table[i].size;
+ }
+
+ return False;
+}
+
+/****************************************************************************
+****************************************************************************/
+
+static int search_notify(uint16 type, uint16 field, int *value)
+{
+ int i;
+
+ for (i = 0; i < sizeof(notify_info_data_table); i++) {
+ if (notify_info_data_table[i].type == type &&
+ notify_info_data_table[i].field == field &&
+ notify_info_data_table[i].fn != NULL) {
+ *value = i;
+ return True;
+ }
+ }
+
+ return False;
+}
+
+/****************************************************************************
+****************************************************************************/
+
+void construct_info_data(SPOOL_NOTIFY_INFO_DATA *info_data, uint16 type, uint16 field, int id)
+{
+ info_data->type = type;
+ info_data->field = field;
+ info_data->reserved = 0;
+
+ info_data->size = size_of_notify_info_data(type, field);
+ info_data->enc_type = type_of_notify_info_data(type, field);
+
+ info_data->id = id;
+
+}
+
+
+/*******************************************************************
+ *
+ * fill a notify_info struct with info asked
+ *
+ ********************************************************************/
+
+static BOOL construct_notify_printer_info(Printer_entry *print_hnd, SPOOL_NOTIFY_INFO *info, int
+ snum, SPOOL_NOTIFY_OPTION_TYPE
+ *option_type, uint32 id,
+ TALLOC_CTX *mem_ctx)
+{
+ int field_num,j;
+ uint16 type;
+ uint16 field;
+
+ SPOOL_NOTIFY_INFO_DATA *current_data, *tid;
+ NT_PRINTER_INFO_LEVEL *printer = NULL;
+ print_queue_struct *queue=NULL;
+
+ type=option_type->type;
+
+ DEBUG(4,("construct_notify_printer_info: Notify type: [%s], number of notify info: [%d] on printer: [%s]\n",
+ (option_type->type==PRINTER_NOTIFY_TYPE?"PRINTER_NOTIFY_TYPE":"JOB_NOTIFY_TYPE"),
+ option_type->count, lp_servicename(snum)));
+
+ if (!W_ERROR_IS_OK(get_a_printer(print_hnd, &printer, 2, lp_const_servicename(snum))))
+ return False;
+
+ for(field_num=0; field_num<option_type->count; field_num++) {
+ field = option_type->fields[field_num];
+
+ DEBUG(4,("construct_notify_printer_info: notify [%d]: type [%x], field [%x]\n", field_num, type, field));
+
+ if (!search_notify(type, field, &j) )
+ continue;
+
+ if((tid=(SPOOL_NOTIFY_INFO_DATA *)Realloc(info->data, (info->count+1)*sizeof(SPOOL_NOTIFY_INFO_DATA))) == NULL) {
+ DEBUG(2,("construct_notify_printer_info: failed to enlarge buffer info->data!\n"));
+ return False;
+ } else
+ info->data = tid;
+
+ current_data = &info->data[info->count];
+
+ construct_info_data(current_data, type, field, id);
+
+ DEBUG(10,("construct_notify_printer_info: calling [%s] snum=%d printername=[%s])\n",
+ notify_info_data_table[j].name, snum, printer->info_2->printername ));
+
+ notify_info_data_table[j].fn(snum, current_data, queue,
+ printer, mem_ctx);
+
+ info->count++;
+ }
+
+ free_a_printer(&printer, 2);
+ return True;
+}
+
+/*******************************************************************
+ *
+ * fill a notify_info struct with info asked
+ *
+ ********************************************************************/
+
+static BOOL construct_notify_jobs_info(print_queue_struct *queue,
+ SPOOL_NOTIFY_INFO *info,
+ NT_PRINTER_INFO_LEVEL *printer,
+ int snum, SPOOL_NOTIFY_OPTION_TYPE
+ *option_type, uint32 id,
+ TALLOC_CTX *mem_ctx)
+{
+ int field_num,j;
+ uint16 type;
+ uint16 field;
+
+ SPOOL_NOTIFY_INFO_DATA *current_data, *tid;
+
+ DEBUG(4,("construct_notify_jobs_info\n"));
+
+ type = option_type->type;
+
+ DEBUGADD(4,("Notify type: [%s], number of notify info: [%d]\n",
+ (option_type->type==PRINTER_NOTIFY_TYPE?"PRINTER_NOTIFY_TYPE":"JOB_NOTIFY_TYPE"),
+ option_type->count));
+
+ for(field_num=0; field_num<option_type->count; field_num++) {
+ field = option_type->fields[field_num];
+
+ if (!search_notify(type, field, &j) )
+ continue;
+
+ if((tid=Realloc(info->data, (info->count+1)*sizeof(SPOOL_NOTIFY_INFO_DATA))) == NULL) {
+ DEBUG(2,("construct_notify_jobs_info: failed to enlarg buffer info->data!\n"));
+ return False;
+ }
+ else info->data = tid;
+
+ current_data=&(info->data[info->count]);
+
+ construct_info_data(current_data, type, field, id);
+ notify_info_data_table[j].fn(snum, current_data, queue,
+ printer, mem_ctx);
+ info->count++;
+ }
+
+ return True;
+}
+
+/*
+ * JFM: The enumeration is not that simple, it's even non obvious.
+ *
+ * let's take an example: I want to monitor the PRINTER SERVER for
+ * the printer's name and the number of jobs currently queued.
+ * So in the NOTIFY_OPTION, I have one NOTIFY_OPTION_TYPE structure.
+ * Its type is PRINTER_NOTIFY_TYPE and it has 2 fields NAME and CJOBS.
+ *
+ * I have 3 printers on the back of my server.
+ *
+ * Now the response is a NOTIFY_INFO structure, with 6 NOTIFY_INFO_DATA
+ * structures.
+ * Number Data Id
+ * 1 printer 1 name 1
+ * 2 printer 1 cjob 1
+ * 3 printer 2 name 2
+ * 4 printer 2 cjob 2
+ * 5 printer 3 name 3
+ * 6 printer 3 name 3
+ *
+ * that's the print server case, the printer case is even worse.
+ */
+
+/*******************************************************************
+ *
+ * enumerate all printers on the printserver
+ * fill a notify_info struct with info asked
+ *
+ ********************************************************************/
+
+static WERROR printserver_notify_info(pipes_struct *p, POLICY_HND *hnd,
+ SPOOL_NOTIFY_INFO *info,
+ TALLOC_CTX *mem_ctx)
+{
+ int snum;
+ Printer_entry *Printer=find_printer_index_by_hnd(p, hnd);
+ int n_services=lp_numservices();
+ int i;
+ SPOOL_NOTIFY_OPTION *option;
+ SPOOL_NOTIFY_OPTION_TYPE *option_type;
+
+ DEBUG(4,("printserver_notify_info\n"));
+
+ if (!Printer)
+ return WERR_BADFID;
+
+ option=Printer->notify.option;
+ info->version=2;
+ info->data=NULL;
+ info->count=0;
+
+ for (i=0; i<option->count; i++) {
+ option_type=&(option->ctr.type[i]);
+
+ if (option_type->type!=PRINTER_NOTIFY_TYPE)
+ continue;
+
+ for (snum=0; snum<n_services; snum++)
+ {
+ if ( lp_browseable(snum) && lp_snum_ok(snum) && lp_print_ok(snum) )
+ construct_notify_printer_info ( Printer, info, snum, option_type, snum, mem_ctx );
+ }
+ }
+
+#if 0
+ /*
+ * Debugging information, don't delete.
+ */
+
+ DEBUG(1,("dumping the NOTIFY_INFO\n"));
+ DEBUGADD(1,("info->version:[%d], info->flags:[%d], info->count:[%d]\n", info->version, info->flags, info->count));
+ DEBUGADD(1,("num\ttype\tfield\tres\tid\tsize\tenc_type\n"));
+
+ for (i=0; i<info->count; i++) {
+ DEBUGADD(1,("[%d]\t[%d]\t[%d]\t[%d]\t[%d]\t[%d]\t[%d]\n",
+ i, info->data[i].type, info->data[i].field, info->data[i].reserved,
+ info->data[i].id, info->data[i].size, info->data[i].enc_type));
+ }
+#endif
+
+ return WERR_OK;
+}
+
+/*******************************************************************
+ *
+ * fill a notify_info struct with info asked
+ *
+ ********************************************************************/
+
+static WERROR printer_notify_info(pipes_struct *p, POLICY_HND *hnd, SPOOL_NOTIFY_INFO *info,
+ TALLOC_CTX *mem_ctx)
+{
+ int snum;
+ Printer_entry *Printer=find_printer_index_by_hnd(p, hnd);
+ int i;
+ uint32 id;
+ SPOOL_NOTIFY_OPTION *option;
+ SPOOL_NOTIFY_OPTION_TYPE *option_type;
+ int count,j;
+ print_queue_struct *queue=NULL;
+ print_status_struct status;
+
+ DEBUG(4,("printer_notify_info\n"));
+
+ if (!Printer)
+ return WERR_BADFID;
+
+ option=Printer->notify.option;
+ id = 0x0;
+ info->version=2;
+ info->data=NULL;
+ info->count=0;
+
+ get_printer_snum(p, hnd, &snum);
+
+ for (i=0; i<option->count; i++) {
+ option_type=&option->ctr.type[i];
+
+ switch ( option_type->type ) {
+ case PRINTER_NOTIFY_TYPE:
+ if(construct_notify_printer_info(Printer, info, snum,
+ option_type, id,
+ mem_ctx))
+ id--;
+ break;
+
+ case JOB_NOTIFY_TYPE: {
+ NT_PRINTER_INFO_LEVEL *printer = NULL;
+
+ count = print_queue_status(snum, &queue, &status);
+
+ if (!W_ERROR_IS_OK(get_a_printer(Printer, &printer, 2, lp_const_servicename(snum))))
+ goto done;
+
+ for (j=0; j<count; j++) {
+ construct_notify_jobs_info(&queue[j], info,
+ printer, snum,
+ option_type,
+ queue[j].job,
+ mem_ctx);
+ }
+
+ free_a_printer(&printer, 2);
+
+ done:
+ SAFE_FREE(queue);
+ break;
+ }
+ }
+ }
+
+ /*
+ * Debugging information, don't delete.
+ */
+ /*
+ DEBUG(1,("dumping the NOTIFY_INFO\n"));
+ DEBUGADD(1,("info->version:[%d], info->flags:[%d], info->count:[%d]\n", info->version, info->flags, info->count));
+ DEBUGADD(1,("num\ttype\tfield\tres\tid\tsize\tenc_type\n"));
+
+ for (i=0; i<info->count; i++) {
+ DEBUGADD(1,("[%d]\t[%d]\t[%d]\t[%d]\t[%d]\t[%d]\t[%d]\n",
+ i, info->data[i].type, info->data[i].field, info->data[i].reserved,
+ info->data[i].id, info->data[i].size, info->data[i].enc_type));
+ }
+ */
+ return WERR_OK;
+}
+
+/********************************************************************
+ * spoolss_rfnpcnex
+ ********************************************************************/
+
+WERROR _spoolss_rfnpcnex( pipes_struct *p, SPOOL_Q_RFNPCNEX *q_u, SPOOL_R_RFNPCNEX *r_u)
+{
+ POLICY_HND *handle = &q_u->handle;
+ SPOOL_NOTIFY_INFO *info = &r_u->info;
+
+ Printer_entry *Printer=find_printer_index_by_hnd(p, handle);
+ WERROR result = WERR_BADFID;
+
+ /* we always have a NOTIFY_INFO struct */
+ r_u->info_ptr=0x1;
+
+ if (!Printer) {
+ DEBUG(2,("_spoolss_rfnpcnex: Invalid handle (%s:%u:%u).\n",
+ OUR_HANDLE(handle)));
+ goto done;
+ }
+
+ DEBUG(4,("Printer type %x\n",Printer->printer_type));
+
+ /*
+ * We are now using the change value, and
+ * I should check for PRINTER_NOTIFY_OPTIONS_REFRESH but as
+ * I don't have a global notification system, I'm sending back all the
+ * informations even when _NOTHING_ has changed.
+ */
+
+ /* We need to keep track of the change value to send back in
+ RRPCN replies otherwise our updates are ignored. */
+
+ Printer->notify.fnpcn = True;
+
+ if (Printer->notify.client_connected) {
+ DEBUG(10,("_spoolss_rfnpcnex: Saving change value in request [%x]\n", q_u->change));
+ Printer->notify.change = q_u->change;
+ }
+
+ /* just ignore the SPOOL_NOTIFY_OPTION */
+
+ switch (Printer->printer_type) {
+ case PRINTER_HANDLE_IS_PRINTSERVER:
+ result = printserver_notify_info(p, handle, info, p->mem_ctx);
+ break;
+
+ case PRINTER_HANDLE_IS_PRINTER:
+ result = printer_notify_info(p, handle, info, p->mem_ctx);
+ break;
+ }
+
+ Printer->notify.fnpcn = False;
+
+done:
+ return result;
+}
+
+/********************************************************************
+ * construct_printer_info_0
+ * fill a printer_info_0 struct
+ ********************************************************************/
+
+static BOOL construct_printer_info_0(Printer_entry *print_hnd, PRINTER_INFO_0 *printer, int snum)
+{
+ pstring chaine;
+ int count;
+ NT_PRINTER_INFO_LEVEL *ntprinter = NULL;
+ counter_printer_0 *session_counter;
+ uint32 global_counter;
+ struct tm *t;
+ time_t setuptime;
+ print_status_struct status;
+
+ if (!W_ERROR_IS_OK(get_a_printer(print_hnd, &ntprinter, 2, lp_const_servicename(snum))))
+ return False;
+
+ count = print_queue_length(snum, &status);
+
+ /* check if we already have a counter for this printer */
+ session_counter = (counter_printer_0 *)ubi_dlFirst(&counter_list);
+
+ for(; session_counter; session_counter = (counter_printer_0 *)ubi_dlNext(session_counter)) {
+ if (session_counter->snum == snum)
+ break;
+ }
+
+ /* it's the first time, add it to the list */
+ if (session_counter==NULL) {
+ if((session_counter=(counter_printer_0 *)malloc(sizeof(counter_printer_0))) == NULL) {
+ free_a_printer(&ntprinter, 2);
+ return False;
+ }
+ ZERO_STRUCTP(session_counter);
+ session_counter->snum=snum;
+ session_counter->counter=0;
+ ubi_dlAddHead( &counter_list, (ubi_dlNode *)session_counter);
+ }
+
+ /* increment it */
+ session_counter->counter++;
+
+ /* JFM:
+ * the global_counter should be stored in a TDB as it's common to all the clients
+ * and should be zeroed on samba startup
+ */
+ global_counter=session_counter->counter;
+
+ pstrcpy(chaine,ntprinter->info_2->printername);
+
+ init_unistr(&printer->printername, chaine);
+
+ slprintf(chaine,sizeof(chaine)-1,"\\\\%s", get_called_name());
+ init_unistr(&printer->servername, chaine);
+
+ printer->cjobs = count;
+ printer->total_jobs = 0;
+ printer->total_bytes = 0;
+
+ setuptime = (time_t)ntprinter->info_2->setuptime;
+ t=gmtime(&setuptime);
+
+ printer->year = t->tm_year+1900;
+ printer->month = t->tm_mon+1;
+ printer->dayofweek = t->tm_wday;
+ printer->day = t->tm_mday;
+ printer->hour = t->tm_hour;
+ printer->minute = t->tm_min;
+ printer->second = t->tm_sec;
+ printer->milliseconds = 0;
+
+ printer->global_counter = global_counter;
+ printer->total_pages = 0;
+
+ /* in 2.2 we reported ourselves as 0x0004 and 0x0565 */
+ printer->major_version = 0x0005; /* NT 5 */
+ printer->build_version = 0x0893; /* build 2195 */
+
+ printer->unknown7 = 0x1;
+ printer->unknown8 = 0x0;
+ printer->unknown9 = 0x0;
+ printer->session_counter = session_counter->counter;
+ printer->unknown11 = 0x0;
+ printer->printer_errors = 0x0; /* number of print failure */
+ printer->unknown13 = 0x0;
+ printer->unknown14 = 0x1;
+ printer->unknown15 = 0x024a; /* 586 Pentium ? */
+ printer->unknown16 = 0x0;
+ printer->change_id = ntprinter->info_2->changeid; /* ChangeID in milliseconds*/
+ printer->unknown18 = 0x0;
+ printer->status = nt_printq_status(status.status);
+ printer->unknown20 = 0x0;
+ printer->c_setprinter = get_c_setprinter(); /* monotonically increasing sum of delta printer counts */
+ printer->unknown22 = 0x0;
+ printer->unknown23 = 0x6; /* 6 ???*/
+ printer->unknown24 = 0; /* unknown 24 to 26 are always 0 */
+ printer->unknown25 = 0;
+ printer->unknown26 = 0;
+ printer->unknown27 = 0;
+ printer->unknown28 = 0;
+ printer->unknown29 = 0;
+
+ free_a_printer(&ntprinter,2);
+ return (True);
+}
+
+/********************************************************************
+ * construct_printer_info_1
+ * fill a printer_info_1 struct
+ ********************************************************************/
+static BOOL construct_printer_info_1(Printer_entry *print_hnd, uint32 flags, PRINTER_INFO_1 *printer, int snum)
+{
+ pstring chaine;
+ pstring chaine2;
+ NT_PRINTER_INFO_LEVEL *ntprinter = NULL;
+
+ if (!W_ERROR_IS_OK(get_a_printer(print_hnd, &ntprinter, 2, lp_const_servicename(snum))))
+ return False;
+
+ printer->flags=flags;
+
+ if (*ntprinter->info_2->comment == '\0') {
+ init_unistr(&printer->comment, lp_comment(snum));
+ slprintf(chaine,sizeof(chaine)-1,"%s,%s,%s", ntprinter->info_2->printername,
+ ntprinter->info_2->drivername, lp_comment(snum));
+ }
+ else {
+ init_unistr(&printer->comment, ntprinter->info_2->comment); /* saved comment. */
+ slprintf(chaine,sizeof(chaine)-1,"%s,%s,%s", ntprinter->info_2->printername,
+ ntprinter->info_2->drivername, ntprinter->info_2->comment);
+ }
+
+ slprintf(chaine2,sizeof(chaine)-1,"%s", ntprinter->info_2->printername);
+
+ init_unistr(&printer->description, chaine);
+ init_unistr(&printer->name, chaine2);
+
+ free_a_printer(&ntprinter,2);
+
+ return True;
+}
+
+/****************************************************************************
+ Free a DEVMODE struct.
+****************************************************************************/
+
+static void free_dev_mode(DEVICEMODE *dev)
+{
+ if (dev == NULL)
+ return;
+
+ SAFE_FREE(dev->private);
+ SAFE_FREE(dev);
+}
+
+
+/****************************************************************************
+ Convert an NT_DEVICEMODE to a DEVICEMODE structure. Both pointers
+ should be valid upon entry
+****************************************************************************/
+
+static BOOL convert_nt_devicemode( DEVICEMODE *devmode, NT_DEVICEMODE *ntdevmode )
+{
+ if ( !devmode || !ntdevmode )
+ return False;
+
+ init_unistr(&devmode->devicename, ntdevmode->devicename);
+
+ init_unistr(&devmode->formname, ntdevmode->formname);
+
+ devmode->specversion = ntdevmode->specversion;
+ devmode->driverversion = ntdevmode->driverversion;
+ devmode->size = ntdevmode->size;
+ devmode->driverextra = ntdevmode->driverextra;
+ devmode->fields = ntdevmode->fields;
+
+ devmode->orientation = ntdevmode->orientation;
+ devmode->papersize = ntdevmode->papersize;
+ devmode->paperlength = ntdevmode->paperlength;
+ devmode->paperwidth = ntdevmode->paperwidth;
+ devmode->scale = ntdevmode->scale;
+ devmode->copies = ntdevmode->copies;
+ devmode->defaultsource = ntdevmode->defaultsource;
+ devmode->printquality = ntdevmode->printquality;
+ devmode->color = ntdevmode->color;
+ devmode->duplex = ntdevmode->duplex;
+ devmode->yresolution = ntdevmode->yresolution;
+ devmode->ttoption = ntdevmode->ttoption;
+ devmode->collate = ntdevmode->collate;
+ devmode->icmmethod = ntdevmode->icmmethod;
+ devmode->icmintent = ntdevmode->icmintent;
+ devmode->mediatype = ntdevmode->mediatype;
+ devmode->dithertype = ntdevmode->dithertype;
+
+ if (ntdevmode->private != NULL) {
+ if ((devmode->private=(uint8 *)memdup(ntdevmode->private, ntdevmode->driverextra)) == NULL)
+ return False;
+ }
+
+ return True;
+}
+
+/****************************************************************************
+ Create a DEVMODE struct. Returns malloced memory.
+****************************************************************************/
+
+DEVICEMODE *construct_dev_mode(int snum)
+{
+ NT_PRINTER_INFO_LEVEL *printer = NULL;
+ DEVICEMODE *devmode = NULL;
+
+ DEBUG(7,("construct_dev_mode\n"));
+
+ DEBUGADD(8,("getting printer characteristics\n"));
+
+ if (!W_ERROR_IS_OK(get_a_printer(NULL, &printer, 2, lp_const_servicename(snum))))
+ return NULL;
+
+ if ( !printer->info_2->devmode ) {
+ DEBUG(5, ("BONG! There was no device mode!\n"));
+ goto done;
+ }
+
+ if ((devmode = (DEVICEMODE *)malloc(sizeof(DEVICEMODE))) == NULL) {
+ DEBUG(2,("construct_dev_mode: malloc fail.\n"));
+ goto done;
+ }
+
+ ZERO_STRUCTP(devmode);
+
+ DEBUGADD(8,("loading DEVICEMODE\n"));
+
+ if ( !convert_nt_devicemode( devmode, printer->info_2->devmode ) ) {
+ free_dev_mode( devmode );
+ devmode = NULL;
+ }
+
+done:
+ free_a_printer(&printer,2);
+
+ return devmode;
+}
+
+/********************************************************************
+ * construct_printer_info_2
+ * fill a printer_info_2 struct
+ ********************************************************************/
+
+static BOOL construct_printer_info_2(Printer_entry *print_hnd, PRINTER_INFO_2 *printer, int snum)
+{
+ int count;
+ NT_PRINTER_INFO_LEVEL *ntprinter = NULL;
+
+ print_status_struct status;
+
+ if (!W_ERROR_IS_OK(get_a_printer(print_hnd, &ntprinter, 2, lp_const_servicename(snum))))
+ return False;
+
+ count = print_queue_length(snum, &status);
+
+ init_unistr(&printer->servername, ntprinter->info_2->servername); /* servername*/
+ init_unistr(&printer->printername, ntprinter->info_2->printername); /* printername*/
+ init_unistr(&printer->sharename, lp_servicename(snum)); /* sharename */
+ init_unistr(&printer->portname, ntprinter->info_2->portname); /* port */
+ init_unistr(&printer->drivername, ntprinter->info_2->drivername); /* drivername */
+
+ if (*ntprinter->info_2->comment == '\0')
+ init_unistr(&printer->comment, lp_comment(snum)); /* comment */
+ else
+ init_unistr(&printer->comment, ntprinter->info_2->comment); /* saved comment. */
+
+ init_unistr(&printer->location, ntprinter->info_2->location); /* location */
+ init_unistr(&printer->sepfile, ntprinter->info_2->sepfile); /* separator file */
+ init_unistr(&printer->printprocessor, ntprinter->info_2->printprocessor);/* print processor */
+ init_unistr(&printer->datatype, ntprinter->info_2->datatype); /* datatype */
+ init_unistr(&printer->parameters, ntprinter->info_2->parameters); /* parameters (of print processor) */
+
+ printer->attributes = ntprinter->info_2->attributes;
+
+ printer->priority = ntprinter->info_2->priority; /* priority */
+ printer->defaultpriority = ntprinter->info_2->default_priority; /* default priority */
+ printer->starttime = ntprinter->info_2->starttime; /* starttime */
+ printer->untiltime = ntprinter->info_2->untiltime; /* untiltime */
+ printer->status = nt_printq_status(status.status); /* status */
+ printer->cjobs = count; /* jobs */
+ printer->averageppm = ntprinter->info_2->averageppm; /* average pages per minute */
+
+ if((printer->devmode = construct_dev_mode(snum)) == NULL) {
+ DEBUG(8, ("Returning NULL Devicemode!\n"));
+ }
+
+ if (ntprinter->info_2->secdesc_buf && ntprinter->info_2->secdesc_buf->len != 0) {
+ /* steal the printer info sec_desc structure. [badly done]. */
+ printer->secdesc = ntprinter->info_2->secdesc_buf->sec;
+ ntprinter->info_2->secdesc_buf->sec = NULL; /* Stolen memory. */
+ ntprinter->info_2->secdesc_buf->len = 0; /* Stolen memory. */
+ ntprinter->info_2->secdesc_buf->max_len = 0; /* Stolen memory. */
+ }
+ else {
+ printer->secdesc = NULL;
+ }
+
+ free_a_printer(&ntprinter, 2);
+ return True;
+}
+
+/********************************************************************
+ * construct_printer_info_3
+ * fill a printer_info_3 struct
+ ********************************************************************/
+
+static BOOL construct_printer_info_3(Printer_entry *print_hnd, PRINTER_INFO_3 **pp_printer, int snum)
+{
+ NT_PRINTER_INFO_LEVEL *ntprinter = NULL;
+ PRINTER_INFO_3 *printer = NULL;
+
+ if (!W_ERROR_IS_OK(get_a_printer(print_hnd, &ntprinter, 2, lp_const_servicename(snum))))
+ return False;
+
+ *pp_printer = NULL;
+ if ((printer = (PRINTER_INFO_3 *)malloc(sizeof(PRINTER_INFO_3))) == NULL) {
+ DEBUG(2,("construct_printer_info_3: malloc fail.\n"));
+ return False;
+ }
+
+ ZERO_STRUCTP(printer);
+
+ printer->flags = 4; /* These are the components of the SD we are returning. */
+ if (ntprinter->info_2->secdesc_buf && ntprinter->info_2->secdesc_buf->len != 0) {
+ /* steal the printer info sec_desc structure. [badly done]. */
+ printer->secdesc = ntprinter->info_2->secdesc_buf->sec;
+
+#if 0
+ /*
+ * Set the flags for the components we are returning.
+ */
+
+ if (printer->secdesc->owner_sid)
+ printer->flags |= OWNER_SECURITY_INFORMATION;
+
+ if (printer->secdesc->grp_sid)
+ printer->flags |= GROUP_SECURITY_INFORMATION;
+
+ if (printer->secdesc->dacl)
+ printer->flags |= DACL_SECURITY_INFORMATION;
+
+ if (printer->secdesc->sacl)
+ printer->flags |= SACL_SECURITY_INFORMATION;
+#endif
+
+ ntprinter->info_2->secdesc_buf->sec = NULL; /* Stolen the malloced memory. */
+ ntprinter->info_2->secdesc_buf->len = 0; /* Stolen the malloced memory. */
+ ntprinter->info_2->secdesc_buf->max_len = 0; /* Stolen the malloced memory. */
+ }
+
+ free_a_printer(&ntprinter, 2);
+
+ *pp_printer = printer;
+ return True;
+}
+
+/********************************************************************
+ * construct_printer_info_4
+ * fill a printer_info_4 struct
+ ********************************************************************/
+
+static BOOL construct_printer_info_4(Printer_entry *print_hnd, PRINTER_INFO_4 *printer, int snum)
+{
+ NT_PRINTER_INFO_LEVEL *ntprinter = NULL;
+
+ if (!W_ERROR_IS_OK(get_a_printer(print_hnd, &ntprinter, 2, lp_const_servicename(snum))))
+ return False;
+
+ init_unistr(&printer->printername, ntprinter->info_2->printername); /* printername*/
+ init_unistr(&printer->servername, ntprinter->info_2->servername); /* servername*/
+ printer->attributes = ntprinter->info_2->attributes;
+
+ free_a_printer(&ntprinter, 2);
+ return True;
+}
+
+/********************************************************************
+ * construct_printer_info_5
+ * fill a printer_info_5 struct
+ ********************************************************************/
+
+static BOOL construct_printer_info_5(Printer_entry *print_hnd, PRINTER_INFO_5 *printer, int snum)
+{
+ NT_PRINTER_INFO_LEVEL *ntprinter = NULL;
+
+ if (!W_ERROR_IS_OK(get_a_printer(print_hnd, &ntprinter, 2, lp_const_servicename(snum))))
+ return False;
+
+ init_unistr(&printer->printername, ntprinter->info_2->printername);
+ init_unistr(&printer->portname, ntprinter->info_2->portname);
+ printer->attributes = ntprinter->info_2->attributes;
+
+ /* these two are not used by NT+ according to MSDN */
+
+ printer->device_not_selected_timeout = 0x0; /* have seen 0x3a98 */
+ printer->transmission_retry_timeout = 0x0; /* have seen 0xafc8 */
+
+ free_a_printer(&ntprinter, 2);
+
+ return True;
+}
+
+/********************************************************************
+ * construct_printer_info_7
+ * fill a printer_info_7 struct
+ ********************************************************************/
+
+static BOOL construct_printer_info_7(Printer_entry *print_hnd, PRINTER_INFO_7 *printer, int snum)
+{
+ char *guid_str = NULL;
+ UUID_FLAT guid;
+
+ if (is_printer_published(print_hnd, snum, &guid)) {
+ asprintf(&guid_str, "{%s}",
+ smb_uuid_string_static(smb_uuid_unpack_static(guid)));
+ strupper_m(guid_str);
+ init_unistr(&printer->guid, guid_str);
+ printer->action = SPOOL_DS_PUBLISH;
+ } else {
+ init_unistr(&printer->guid, "");
+ printer->action = SPOOL_DS_UNPUBLISH;
+ }
+
+ return True;
+}
+
+/********************************************************************
+ Spoolss_enumprinters.
+********************************************************************/
+
+static WERROR enum_all_printers_info_1(uint32 flags, NEW_BUFFER *buffer, uint32 offered, uint32 *needed, uint32 *returned)
+{
+ int snum;
+ int i;
+ int n_services=lp_numservices();
+ PRINTER_INFO_1 *tp, *printers=NULL;
+ PRINTER_INFO_1 current_prt;
+
+ DEBUG(4,("enum_all_printers_info_1\n"));
+
+ for (snum=0; snum<n_services; snum++) {
+ if (lp_browseable(snum) && lp_snum_ok(snum) && lp_print_ok(snum) ) {
+ DEBUG(4,("Found a printer in smb.conf: %s[%x]\n", lp_servicename(snum), snum));
+
+ if (construct_printer_info_1(NULL, flags, &current_prt, snum)) {
+ if((tp=Realloc(printers, (*returned +1)*sizeof(PRINTER_INFO_1))) == NULL) {
+ DEBUG(2,("enum_all_printers_info_1: failed to enlarge printers buffer!\n"));
+ SAFE_FREE(printers);
+ *returned=0;
+ return WERR_NOMEM;
+ }
+ else printers = tp;
+ DEBUG(4,("ReAlloced memory for [%d] PRINTER_INFO_1\n", *returned));
+
+ memcpy(&printers[*returned], &current_prt, sizeof(PRINTER_INFO_1));
+ (*returned)++;
+ }
+ }
+ }
+
+ /* check the required size. */
+ for (i=0; i<*returned; i++)
+ (*needed) += spoolss_size_printer_info_1(&printers[i]);
+
+ if (!alloc_buffer_size(buffer, *needed))
+ return WERR_INSUFFICIENT_BUFFER;
+
+ /* fill the buffer with the structures */
+ for (i=0; i<*returned; i++)
+ smb_io_printer_info_1("", buffer, &printers[i], 0);
+
+ /* clear memory */
+ SAFE_FREE(printers);
+
+ if (*needed > offered) {
+ *returned=0;
+ return WERR_INSUFFICIENT_BUFFER;
+ }
+ else
+ return WERR_OK;
+}
+
+/********************************************************************
+ enum_all_printers_info_1_local.
+*********************************************************************/
+
+static WERROR enum_all_printers_info_1_local(NEW_BUFFER *buffer, uint32 offered, uint32 *needed, uint32 *returned)
+{
+ DEBUG(4,("enum_all_printers_info_1_local\n"));
+
+ return enum_all_printers_info_1(PRINTER_ENUM_ICON8, buffer, offered, needed, returned);
+}
+
+/********************************************************************
+ enum_all_printers_info_1_name.
+*********************************************************************/
+
+static WERROR enum_all_printers_info_1_name(fstring name, NEW_BUFFER *buffer, uint32 offered, uint32 *needed, uint32 *returned)
+{
+ char *s = name;
+
+ DEBUG(4,("enum_all_printers_info_1_name\n"));
+
+ if ((name[0] == '\\') && (name[1] == '\\'))
+ s = name + 2;
+
+ if (is_myname_or_ipaddr(s)) {
+ return enum_all_printers_info_1(PRINTER_ENUM_ICON8, buffer, offered, needed, returned);
+ }
+ else
+ return WERR_INVALID_NAME;
+}
+
+/********************************************************************
+ enum_all_printers_info_1_remote.
+*********************************************************************/
+
+static WERROR enum_all_printers_info_1_remote(fstring name, NEW_BUFFER *buffer, uint32 offered, uint32 *needed, uint32 *returned)
+{
+ PRINTER_INFO_1 *printer;
+ fstring printername;
+ fstring desc;
+ fstring comment;
+ DEBUG(4,("enum_all_printers_info_1_remote\n"));
+
+ /* JFM: currently it's more a place holder than anything else.
+ * In the spooler world there is a notion of server registration.
+ * the print servers are registring (sp ?) on the PDC (in the same domain)
+ *
+ * We should have a TDB here. The registration is done thru an undocumented RPC call.
+ */
+
+ if((printer=(PRINTER_INFO_1 *)malloc(sizeof(PRINTER_INFO_1))) == NULL)
+ return WERR_NOMEM;
+
+ *returned=1;
+
+ slprintf(printername, sizeof(printername)-1,"Windows NT Remote Printers!!\\\\%s", get_called_name());
+ slprintf(desc, sizeof(desc)-1,"%s", get_called_name());
+ slprintf(comment, sizeof(comment)-1, "Logged on Domain");
+
+ init_unistr(&printer->description, desc);
+ init_unistr(&printer->name, printername);
+ init_unistr(&printer->comment, comment);
+ printer->flags=PRINTER_ENUM_ICON3|PRINTER_ENUM_CONTAINER;
+
+ /* check the required size. */
+ *needed += spoolss_size_printer_info_1(printer);
+
+ if (!alloc_buffer_size(buffer, *needed)) {
+ SAFE_FREE(printer);
+ return WERR_INSUFFICIENT_BUFFER;
+ }
+
+ /* fill the buffer with the structures */
+ smb_io_printer_info_1("", buffer, printer, 0);
+
+ /* clear memory */
+ SAFE_FREE(printer);
+
+ if (*needed > offered) {
+ *returned=0;
+ return WERR_INSUFFICIENT_BUFFER;
+ }
+ else
+ return WERR_OK;
+}
+
+/********************************************************************
+ enum_all_printers_info_1_network.
+*********************************************************************/
+
+static WERROR enum_all_printers_info_1_network(fstring name, NEW_BUFFER *buffer, uint32 offered, uint32 *needed, uint32 *returned)
+{
+ char *s = name;
+
+ DEBUG(4,("enum_all_printers_info_1_network\n"));
+
+ /* If we respond to a enum_printers level 1 on our name with flags
+ set to PRINTER_ENUM_REMOTE with a list of printers then these
+ printers incorrectly appear in the APW browse list.
+ Specifically the printers for the server appear at the workgroup
+ level where all the other servers in the domain are
+ listed. Windows responds to this call with a
+ WERR_CAN_NOT_COMPLETE so we should do the same. */
+
+ if (name[0] == '\\' && name[1] == '\\')
+ s = name + 2;
+
+ if (is_myname_or_ipaddr(s))
+ return WERR_CAN_NOT_COMPLETE;
+
+ return enum_all_printers_info_1(PRINTER_ENUM_UNKNOWN_8, buffer, offered, needed, returned);
+}
+
+/********************************************************************
+ * api_spoolss_enumprinters
+ *
+ * called from api_spoolss_enumprinters (see this to understand)
+ ********************************************************************/
+
+static WERROR enum_all_printers_info_2(NEW_BUFFER *buffer, uint32 offered, uint32 *needed, uint32 *returned)
+{
+ int snum;
+ int i;
+ int n_services=lp_numservices();
+ PRINTER_INFO_2 *tp, *printers=NULL;
+ PRINTER_INFO_2 current_prt;
+
+ for (snum=0; snum<n_services; snum++) {
+ if (lp_browseable(snum) && lp_snum_ok(snum) && lp_print_ok(snum) ) {
+ DEBUG(4,("Found a printer in smb.conf: %s[%x]\n", lp_servicename(snum), snum));
+
+ if (construct_printer_info_2(NULL, &current_prt, snum)) {
+ if((tp=Realloc(printers, (*returned +1)*sizeof(PRINTER_INFO_2))) == NULL) {
+ DEBUG(2,("enum_all_printers_info_2: failed to enlarge printers buffer!\n"));
+ SAFE_FREE(printers);
+ *returned = 0;
+ return WERR_NOMEM;
+ }
+ else printers = tp;
+ DEBUG(4,("ReAlloced memory for [%d] PRINTER_INFO_2\n", *returned));
+ memcpy(&printers[*returned], &current_prt, sizeof(PRINTER_INFO_2));
+ (*returned)++;
+ }
+ }
+ }
+
+ /* check the required size. */
+ for (i=0; i<*returned; i++)
+ (*needed) += spoolss_size_printer_info_2(&printers[i]);
+
+ if (!alloc_buffer_size(buffer, *needed)) {
+ for (i=0; i<*returned; i++) {
+ free_devmode(printers[i].devmode);
+ }
+ SAFE_FREE(printers);
+ return WERR_INSUFFICIENT_BUFFER;
+ }
+
+ /* fill the buffer with the structures */
+ for (i=0; i<*returned; i++)
+ smb_io_printer_info_2("", buffer, &(printers[i]), 0);
+
+ /* clear memory */
+ for (i=0; i<*returned; i++) {
+ free_devmode(printers[i].devmode);
+ }
+ SAFE_FREE(printers);
+
+ if (*needed > offered) {
+ *returned=0;
+ return WERR_INSUFFICIENT_BUFFER;
+ }
+ else
+ return WERR_OK;
+}
+
+/********************************************************************
+ * handle enumeration of printers at level 1
+ ********************************************************************/
+
+static WERROR enumprinters_level1( uint32 flags, fstring name,
+ NEW_BUFFER *buffer, uint32 offered,
+ uint32 *needed, uint32 *returned)
+{
+ /* Not all the flags are equals */
+
+ if (flags & PRINTER_ENUM_LOCAL)
+ return enum_all_printers_info_1_local(buffer, offered, needed, returned);
+
+ if (flags & PRINTER_ENUM_NAME)
+ return enum_all_printers_info_1_name(name, buffer, offered, needed, returned);
+
+ if (flags & PRINTER_ENUM_REMOTE)
+ return enum_all_printers_info_1_remote(name, buffer, offered, needed, returned);
+
+ if (flags & PRINTER_ENUM_NETWORK)
+ return enum_all_printers_info_1_network(name, buffer, offered, needed, returned);
+
+ return WERR_OK; /* NT4sp5 does that */
+}
+
+/********************************************************************
+ * handle enumeration of printers at level 2
+ ********************************************************************/
+
+static WERROR enumprinters_level2( uint32 flags, fstring servername,
+ NEW_BUFFER *buffer, uint32 offered,
+ uint32 *needed, uint32 *returned)
+{
+ char *s = servername;
+
+ if (flags & PRINTER_ENUM_LOCAL) {
+ return enum_all_printers_info_2(buffer, offered, needed, returned);
+ }
+
+ if (flags & PRINTER_ENUM_NAME) {
+ if ((servername[0] == '\\') && (servername[1] == '\\'))
+ s = servername + 2;
+ if (is_myname_or_ipaddr(s))
+ return enum_all_printers_info_2(buffer, offered, needed, returned);
+ else
+ return WERR_INVALID_NAME;
+ }
+
+ if (flags & PRINTER_ENUM_REMOTE)
+ return WERR_UNKNOWN_LEVEL;
+
+ return WERR_OK;
+}
+
+/********************************************************************
+ * handle enumeration of printers at level 5
+ ********************************************************************/
+
+static WERROR enumprinters_level5( uint32 flags, fstring servername,
+ NEW_BUFFER *buffer, uint32 offered,
+ uint32 *needed, uint32 *returned)
+{
+/* return enum_all_printers_info_5(buffer, offered, needed, returned);*/
+ return WERR_OK;
+}
+
+/********************************************************************
+ * api_spoolss_enumprinters
+ *
+ * called from api_spoolss_enumprinters (see this to understand)
+ ********************************************************************/
+
+WERROR _spoolss_enumprinters( pipes_struct *p, SPOOL_Q_ENUMPRINTERS *q_u, SPOOL_R_ENUMPRINTERS *r_u)
+{
+ uint32 flags = q_u->flags;
+ UNISTR2 *servername = &q_u->servername;
+ uint32 level = q_u->level;
+ NEW_BUFFER *buffer = NULL;
+ uint32 offered = q_u->offered;
+ uint32 *needed = &r_u->needed;
+ uint32 *returned = &r_u->returned;
+
+ fstring name;
+
+ /* that's an [in out] buffer */
+ spoolss_move_buffer(q_u->buffer, &r_u->buffer);
+ buffer = r_u->buffer;
+
+ DEBUG(4,("_spoolss_enumprinters\n"));
+
+ *needed=0;
+ *returned=0;
+
+ /*
+ * Level 1:
+ * flags==PRINTER_ENUM_NAME
+ * if name=="" then enumerates all printers
+ * if name!="" then enumerate the printer
+ * flags==PRINTER_ENUM_REMOTE
+ * name is NULL, enumerate printers
+ * Level 2: name!="" enumerates printers, name can't be NULL
+ * Level 3: doesn't exist
+ * Level 4: does a local registry lookup
+ * Level 5: same as Level 2
+ */
+
+ unistr2_to_ascii(name, servername, sizeof(name)-1);
+ strupper_m(name);
+
+ switch (level) {
+ case 1:
+ return enumprinters_level1(flags, name, buffer, offered, needed, returned);
+ case 2:
+ return enumprinters_level2(flags, name, buffer, offered, needed, returned);
+ case 5:
+ return enumprinters_level5(flags, name, buffer, offered, needed, returned);
+ case 3:
+ case 4:
+ break;
+ }
+ return WERR_UNKNOWN_LEVEL;
+}
+
+/****************************************************************************
+****************************************************************************/
+
+static WERROR getprinter_level_0(Printer_entry *print_hnd, int snum, NEW_BUFFER *buffer, uint32 offered, uint32 *needed)
+{
+ PRINTER_INFO_0 *printer=NULL;
+
+ if((printer=(PRINTER_INFO_0*)malloc(sizeof(PRINTER_INFO_0))) == NULL)
+ return WERR_NOMEM;
+
+ construct_printer_info_0(print_hnd, printer, snum);
+
+ /* check the required size. */
+ *needed += spoolss_size_printer_info_0(printer);
+
+ if (!alloc_buffer_size(buffer, *needed)) {
+ SAFE_FREE(printer);
+ return WERR_INSUFFICIENT_BUFFER;
+ }
+
+ /* fill the buffer with the structures */
+ smb_io_printer_info_0("", buffer, printer, 0);
+
+ /* clear memory */
+ SAFE_FREE(printer);
+
+ if (*needed > offered) {
+ return WERR_INSUFFICIENT_BUFFER;
+ }
+
+ return WERR_OK;
+}
+
+/****************************************************************************
+****************************************************************************/
+
+static WERROR getprinter_level_1(Printer_entry *print_hnd, int snum, NEW_BUFFER *buffer, uint32 offered, uint32 *needed)
+{
+ PRINTER_INFO_1 *printer=NULL;
+
+ if((printer=(PRINTER_INFO_1*)malloc(sizeof(PRINTER_INFO_1))) == NULL)
+ return WERR_NOMEM;
+
+ construct_printer_info_1(print_hnd, PRINTER_ENUM_ICON8, printer, snum);
+
+ /* check the required size. */
+ *needed += spoolss_size_printer_info_1(printer);
+
+ if (!alloc_buffer_size(buffer, *needed)) {
+ SAFE_FREE(printer);
+ return WERR_INSUFFICIENT_BUFFER;
+ }
+
+ /* fill the buffer with the structures */
+ smb_io_printer_info_1("", buffer, printer, 0);
+
+ /* clear memory */
+ SAFE_FREE(printer);
+
+ if (*needed > offered) {
+ return WERR_INSUFFICIENT_BUFFER;
+ }
+
+ return WERR_OK;
+}
+
+/****************************************************************************
+****************************************************************************/
+
+static WERROR getprinter_level_2(Printer_entry *print_hnd, int snum, NEW_BUFFER *buffer, uint32 offered, uint32 *needed)
+{
+ PRINTER_INFO_2 *printer=NULL;
+
+ if((printer=(PRINTER_INFO_2*)malloc(sizeof(PRINTER_INFO_2)))==NULL)
+ return WERR_NOMEM;
+
+ construct_printer_info_2(print_hnd, printer, snum);
+
+ /* check the required size. */
+ *needed += spoolss_size_printer_info_2(printer);
+
+ if (!alloc_buffer_size(buffer, *needed)) {
+ free_printer_info_2(printer);
+ return WERR_INSUFFICIENT_BUFFER;
+ }
+
+ /* fill the buffer with the structures */
+ if (!smb_io_printer_info_2("", buffer, printer, 0)) {
+ free_printer_info_2(printer);
+ return WERR_NOMEM;
+ }
+
+ /* clear memory */
+ free_printer_info_2(printer);
+
+ if (*needed > offered) {
+ return WERR_INSUFFICIENT_BUFFER;
+ }
+
+ return WERR_OK;
+}
+
+/****************************************************************************
+****************************************************************************/
+
+static WERROR getprinter_level_3(Printer_entry *print_hnd, int snum, NEW_BUFFER *buffer, uint32 offered, uint32 *needed)
+{
+ PRINTER_INFO_3 *printer=NULL;
+
+ if (!construct_printer_info_3(print_hnd, &printer, snum))
+ return WERR_NOMEM;
+
+ /* check the required size. */
+ *needed += spoolss_size_printer_info_3(printer);
+
+ if (!alloc_buffer_size(buffer, *needed)) {
+ free_printer_info_3(printer);
+ return WERR_INSUFFICIENT_BUFFER;
+ }
+
+ /* fill the buffer with the structures */
+ smb_io_printer_info_3("", buffer, printer, 0);
+
+ /* clear memory */
+ free_printer_info_3(printer);
+
+ if (*needed > offered) {
+ return WERR_INSUFFICIENT_BUFFER;
+ }
+
+ return WERR_OK;
+}
+
+/****************************************************************************
+****************************************************************************/
+
+static WERROR getprinter_level_4(Printer_entry *print_hnd, int snum, NEW_BUFFER *buffer, uint32 offered, uint32 *needed)
+{
+ PRINTER_INFO_4 *printer=NULL;
+
+ if((printer=(PRINTER_INFO_4*)malloc(sizeof(PRINTER_INFO_4)))==NULL)
+ return WERR_NOMEM;
+
+ if (!construct_printer_info_4(print_hnd, printer, snum))
+ return WERR_NOMEM;
+
+ /* check the required size. */
+ *needed += spoolss_size_printer_info_4(printer);
+
+ if (!alloc_buffer_size(buffer, *needed)) {
+ free_printer_info_4(printer);
+ return WERR_INSUFFICIENT_BUFFER;
+ }
+
+ /* fill the buffer with the structures */
+ smb_io_printer_info_4("", buffer, printer, 0);
+
+ /* clear memory */
+ free_printer_info_4(printer);
+
+ if (*needed > offered) {
+ return WERR_INSUFFICIENT_BUFFER;
+ }
+
+ return WERR_OK;
+}
+
+/****************************************************************************
+****************************************************************************/
+
+static WERROR getprinter_level_5(Printer_entry *print_hnd, int snum, NEW_BUFFER *buffer, uint32 offered, uint32 *needed)
+{
+ PRINTER_INFO_5 *printer=NULL;
+
+ if((printer=(PRINTER_INFO_5*)malloc(sizeof(PRINTER_INFO_5)))==NULL)
+ return WERR_NOMEM;
+
+ if (!construct_printer_info_5(print_hnd, printer, snum))
+ return WERR_NOMEM;
+
+ /* check the required size. */
+ *needed += spoolss_size_printer_info_5(printer);
+
+ if (!alloc_buffer_size(buffer, *needed)) {
+ free_printer_info_5(printer);
+ return WERR_INSUFFICIENT_BUFFER;
+ }
+
+ /* fill the buffer with the structures */
+ smb_io_printer_info_5("", buffer, printer, 0);
+
+ /* clear memory */
+ free_printer_info_5(printer);
+
+ if (*needed > offered) {
+ return WERR_INSUFFICIENT_BUFFER;
+ }
+
+ return WERR_OK;
+}
+
+static WERROR getprinter_level_7(Printer_entry *print_hnd, int snum, NEW_BUFFER *buffer, uint32 offered, uint32 *needed)
+{
+ PRINTER_INFO_7 *printer=NULL;
+
+ if((printer=(PRINTER_INFO_7*)malloc(sizeof(PRINTER_INFO_7)))==NULL)
+ return WERR_NOMEM;
+
+ if (!construct_printer_info_7(print_hnd, printer, snum))
+ return WERR_NOMEM;
+
+ /* check the required size. */
+ *needed += spoolss_size_printer_info_7(printer);
+
+ if (!alloc_buffer_size(buffer, *needed)) {
+ free_printer_info_7(printer);
+ return WERR_INSUFFICIENT_BUFFER;
+ }
+
+ /* fill the buffer with the structures */
+ smb_io_printer_info_7("", buffer, printer, 0);
+
+ /* clear memory */
+ free_printer_info_7(printer);
+
+ if (*needed > offered) {
+ return WERR_INSUFFICIENT_BUFFER;
+ }
+
+ return WERR_OK;
+}
+
+/****************************************************************************
+****************************************************************************/
+
+WERROR _spoolss_getprinter(pipes_struct *p, SPOOL_Q_GETPRINTER *q_u, SPOOL_R_GETPRINTER *r_u)
+{
+ POLICY_HND *handle = &q_u->handle;
+ uint32 level = q_u->level;
+ NEW_BUFFER *buffer = NULL;
+ uint32 offered = q_u->offered;
+ uint32 *needed = &r_u->needed;
+ Printer_entry *Printer=find_printer_index_by_hnd(p, handle);
+
+ int snum;
+
+ /* that's an [in out] buffer */
+ spoolss_move_buffer(q_u->buffer, &r_u->buffer);
+ buffer = r_u->buffer;
+
+ *needed=0;
+
+ if (!get_printer_snum(p, handle, &snum))
+ return WERR_BADFID;
+
+ switch (level) {
+ case 0:
+ return getprinter_level_0(Printer, snum, buffer, offered, needed);
+ case 1:
+ return getprinter_level_1(Printer, snum, buffer, offered, needed);
+ case 2:
+ return getprinter_level_2(Printer, snum, buffer, offered, needed);
+ case 3:
+ return getprinter_level_3(Printer, snum, buffer, offered, needed);
+ case 4:
+ return getprinter_level_4(Printer, snum, buffer, offered, needed);
+ case 5:
+ return getprinter_level_5(Printer, snum, buffer, offered, needed);
+ case 7:
+ return getprinter_level_7(Printer, snum, buffer, offered, needed);
+ }
+ return WERR_UNKNOWN_LEVEL;
+}
+
+/********************************************************************
+ * fill a DRIVER_INFO_1 struct
+ ********************************************************************/
+
+static void fill_printer_driver_info_1(DRIVER_INFO_1 *info, NT_PRINTER_DRIVER_INFO_LEVEL driver, fstring servername, fstring architecture)
+{
+ init_unistr( &info->name, driver.info_3->name);
+}
+
+/********************************************************************
+ * construct_printer_driver_info_1
+ ********************************************************************/
+
+static WERROR construct_printer_driver_info_1(DRIVER_INFO_1 *info, int snum, fstring servername, fstring architecture, uint32 version)
+{
+ NT_PRINTER_INFO_LEVEL *printer = NULL;
+ NT_PRINTER_DRIVER_INFO_LEVEL driver;
+
+ ZERO_STRUCT(driver);
+
+ if (!W_ERROR_IS_OK(get_a_printer(NULL, &printer, 2, lp_const_servicename(snum))))
+ return WERR_INVALID_PRINTER_NAME;
+
+ if (!W_ERROR_IS_OK(get_a_printer_driver(&driver, 3, printer->info_2->drivername, architecture, version)))
+ return WERR_UNKNOWN_PRINTER_DRIVER;
+
+ fill_printer_driver_info_1(info, driver, servername, architecture);
+
+ free_a_printer(&printer,2);
+
+ return WERR_OK;
+}
+
+/********************************************************************
+ * construct_printer_driver_info_2
+ * fill a printer_info_2 struct
+ ********************************************************************/
+
+static void fill_printer_driver_info_2(DRIVER_INFO_2 *info, NT_PRINTER_DRIVER_INFO_LEVEL driver, fstring servername)
+{
+ pstring temp;
+
+ info->version=driver.info_3->cversion;
+
+ init_unistr( &info->name, driver.info_3->name );
+ init_unistr( &info->architecture, driver.info_3->environment );
+
+
+ if (strlen(driver.info_3->driverpath)) {
+ slprintf(temp, sizeof(temp)-1, "\\\\%s%s", servername, driver.info_3->driverpath);
+ init_unistr( &info->driverpath, temp );
+ } else
+ init_unistr( &info->driverpath, "" );
+
+ if (strlen(driver.info_3->datafile)) {
+ slprintf(temp, sizeof(temp)-1, "\\\\%s%s", servername, driver.info_3->datafile);
+ init_unistr( &info->datafile, temp );
+ } else
+ init_unistr( &info->datafile, "" );
+
+ if (strlen(driver.info_3->configfile)) {
+ slprintf(temp, sizeof(temp)-1, "\\\\%s%s", servername, driver.info_3->configfile);
+ init_unistr( &info->configfile, temp );
+ } else
+ init_unistr( &info->configfile, "" );
+}
+
+/********************************************************************
+ * construct_printer_driver_info_2
+ * fill a printer_info_2 struct
+ ********************************************************************/
+
+static WERROR construct_printer_driver_info_2(DRIVER_INFO_2 *info, int snum, fstring servername, fstring architecture, uint32 version)
+{
+ NT_PRINTER_INFO_LEVEL *printer = NULL;
+ NT_PRINTER_DRIVER_INFO_LEVEL driver;
+
+ ZERO_STRUCT(printer);
+ ZERO_STRUCT(driver);
+
+ if (!W_ERROR_IS_OK(get_a_printer(NULL, &printer, 2, lp_const_servicename(snum))))
+ return WERR_INVALID_PRINTER_NAME;
+
+ if (!W_ERROR_IS_OK(get_a_printer_driver(&driver, 3, printer->info_2->drivername, architecture, version)))
+ return WERR_UNKNOWN_PRINTER_DRIVER;
+
+ fill_printer_driver_info_2(info, driver, servername);
+
+ free_a_printer(&printer,2);
+
+ return WERR_OK;
+}
+
+/********************************************************************
+ * copy a strings array and convert to UNICODE
+ *
+ * convert an array of ascii string to a UNICODE string
+ ********************************************************************/
+
+static uint32 init_unistr_array(uint16 **uni_array, fstring *char_array, const char *servername)
+{
+ int i=0;
+ int j=0;
+ const char *v;
+ pstring line;
+ uint16 *tuary;
+
+ DEBUG(6,("init_unistr_array\n"));
+ *uni_array=NULL;
+
+ while (True)
+ {
+ if ( !char_array )
+ v = "";
+ else
+ {
+ v = char_array[i];
+ if (!v)
+ v = ""; /* hack to handle null lists */
+ }
+
+ /* hack to allow this to be used in places other than when generating
+ the list of dependent files */
+
+ if ( servername )
+ slprintf( line, sizeof(line)-1, "\\\\%s%s", servername, v );
+ else
+ pstrcpy( line, v );
+
+ DEBUGADD(6,("%d:%s:%lu\n", i, line, (unsigned long)strlen(line)));
+
+ /* add one extra unit16 for the second terminating NULL */
+
+ if ( (tuary=Realloc(*uni_array, (j+1+strlen(line)+2)*sizeof(uint16))) == NULL ) {
+ DEBUG(2,("init_unistr_array: Realloc error\n" ));
+ return 0;
+ } else
+ *uni_array = tuary;
+
+ if ( !strlen(v) )
+ break;
+
+ j += (rpcstr_push((*uni_array+j), line, sizeof(uint16)*strlen(line)+2, STR_TERMINATE) / sizeof(uint16));
+ i++;
+ }
+
+ if (*uni_array) {
+ /* special case for ""; we need to add both NULL's here */
+ if (!j)
+ (*uni_array)[j++]=0x0000;
+ (*uni_array)[j]=0x0000;
+ }
+
+ DEBUGADD(6,("last one:done\n"));
+
+ /* return size of array in uint16's */
+
+ return j+1;
+}
+
+/********************************************************************
+ * construct_printer_info_3
+ * fill a printer_info_3 struct
+ ********************************************************************/
+
+static void fill_printer_driver_info_3(DRIVER_INFO_3 *info, NT_PRINTER_DRIVER_INFO_LEVEL driver, fstring servername)
+{
+ pstring temp;
+
+ ZERO_STRUCTP(info);
+
+ info->version=driver.info_3->cversion;
+
+ init_unistr( &info->name, driver.info_3->name );
+ init_unistr( &info->architecture, driver.info_3->environment );
+
+ if (strlen(driver.info_3->driverpath)) {
+ slprintf(temp, sizeof(temp)-1, "\\\\%s%s", servername, driver.info_3->driverpath);
+ init_unistr( &info->driverpath, temp );
+ } else
+ init_unistr( &info->driverpath, "" );
+
+ if (strlen(driver.info_3->datafile)) {
+ slprintf(temp, sizeof(temp)-1, "\\\\%s%s", servername, driver.info_3->datafile);
+ init_unistr( &info->datafile, temp );
+ } else
+ init_unistr( &info->datafile, "" );
+
+ if (strlen(driver.info_3->configfile)) {
+ slprintf(temp, sizeof(temp)-1, "\\\\%s%s", servername, driver.info_3->configfile);
+ init_unistr( &info->configfile, temp );
+ } else
+ init_unistr( &info->configfile, "" );
+
+ if (strlen(driver.info_3->helpfile)) {
+ slprintf(temp, sizeof(temp)-1, "\\\\%s%s", servername, driver.info_3->helpfile);
+ init_unistr( &info->helpfile, temp );
+ } else
+ init_unistr( &info->helpfile, "" );
+
+ init_unistr( &info->monitorname, driver.info_3->monitorname );
+ init_unistr( &info->defaultdatatype, driver.info_3->defaultdatatype );
+
+ info->dependentfiles=NULL;
+ init_unistr_array(&info->dependentfiles, driver.info_3->dependentfiles, servername);
+}
+
+/********************************************************************
+ * construct_printer_info_3
+ * fill a printer_info_3 struct
+ ********************************************************************/
+
+static WERROR construct_printer_driver_info_3(DRIVER_INFO_3 *info, int snum, fstring servername, fstring architecture, uint32 version)
+{
+ NT_PRINTER_INFO_LEVEL *printer = NULL;
+ NT_PRINTER_DRIVER_INFO_LEVEL driver;
+ WERROR status;
+ ZERO_STRUCT(driver);
+
+ status=get_a_printer(NULL, &printer, 2, lp_const_servicename(snum) );
+ DEBUG(8,("construct_printer_driver_info_3: status: %s\n", dos_errstr(status)));
+ if (!W_ERROR_IS_OK(status))
+ return WERR_INVALID_PRINTER_NAME;
+
+ status=get_a_printer_driver(&driver, 3, printer->info_2->drivername, architecture, version);
+ DEBUG(8,("construct_printer_driver_info_3: status: %s\n", dos_errstr(status)));
+
+#if 0 /* JERRY */
+
+ /*
+ * I put this code in during testing. Helpful when commenting out the
+ * support for DRIVER_INFO_6 in regards to win2k. Not needed in general
+ * as win2k always queries the driver using an infor level of 6.
+ * I've left it in (but ifdef'd out) because I'll probably
+ * use it in experimentation again in the future. --jerry 22/01/2002
+ */
+
+ if (!W_ERROR_IS_OK(status)) {
+ /*
+ * Is this a W2k client ?
+ */
+ if (version == 3) {
+ /* Yes - try again with a WinNT driver. */
+ version = 2;
+ status=get_a_printer_driver(&driver, 3, printer->info_2->drivername, architecture, version);
+ DEBUG(8,("construct_printer_driver_info_3: status: %s\n", dos_errstr(status)));
+ }
+#endif
+
+ if (!W_ERROR_IS_OK(status)) {
+ free_a_printer(&printer,2);
+ return WERR_UNKNOWN_PRINTER_DRIVER;
+ }
+
+#if 0 /* JERRY */
+ }
+#endif
+
+
+ fill_printer_driver_info_3(info, driver, servername);
+
+ free_a_printer(&printer,2);
+
+ return WERR_OK;
+}
+
+/********************************************************************
+ * construct_printer_info_6
+ * fill a printer_info_6 struct - we know that driver is really level 3. This sucks. JRA.
+ ********************************************************************/
+
+static void fill_printer_driver_info_6(DRIVER_INFO_6 *info, NT_PRINTER_DRIVER_INFO_LEVEL driver, fstring servername)
+{
+ pstring temp;
+ fstring nullstr;
+
+ ZERO_STRUCTP(info);
+ memset(&nullstr, '\0', sizeof(fstring));
+
+ info->version=driver.info_3->cversion;
+
+ init_unistr( &info->name, driver.info_3->name );
+ init_unistr( &info->architecture, driver.info_3->environment );
+
+ if (strlen(driver.info_3->driverpath)) {
+ slprintf(temp, sizeof(temp)-1, "\\\\%s%s", servername, driver.info_3->driverpath);
+ init_unistr( &info->driverpath, temp );
+ } else
+ init_unistr( &info->driverpath, "" );
+
+ if (strlen(driver.info_3->datafile)) {
+ slprintf(temp, sizeof(temp)-1, "\\\\%s%s", servername, driver.info_3->datafile);
+ init_unistr( &info->datafile, temp );
+ } else
+ init_unistr( &info->datafile, "" );
+
+ if (strlen(driver.info_3->configfile)) {
+ slprintf(temp, sizeof(temp)-1, "\\\\%s%s", servername, driver.info_3->configfile);
+ init_unistr( &info->configfile, temp );
+ } else
+ init_unistr( &info->configfile, "" );
+
+ if (strlen(driver.info_3->helpfile)) {
+ slprintf(temp, sizeof(temp)-1, "\\\\%s%s", servername, driver.info_3->helpfile);
+ init_unistr( &info->helpfile, temp );
+ } else
+ init_unistr( &info->helpfile, "" );
+
+ init_unistr( &info->monitorname, driver.info_3->monitorname );
+ init_unistr( &info->defaultdatatype, driver.info_3->defaultdatatype );
+
+ info->dependentfiles = NULL;
+ init_unistr_array( &info->dependentfiles, driver.info_3->dependentfiles, servername );
+
+ info->previousdrivernames=NULL;
+ init_unistr_array(&info->previousdrivernames, &nullstr, servername);
+
+ info->driver_date.low=0;
+ info->driver_date.high=0;
+
+ info->padding=0;
+ info->driver_version_low=0;
+ info->driver_version_high=0;
+
+ init_unistr( &info->mfgname, "");
+ init_unistr( &info->oem_url, "");
+ init_unistr( &info->hardware_id, "");
+ init_unistr( &info->provider, "");
+}
+
+/********************************************************************
+ * construct_printer_info_6
+ * fill a printer_info_6 struct
+ ********************************************************************/
+
+static WERROR construct_printer_driver_info_6(DRIVER_INFO_6 *info, int snum,
+ fstring servername, fstring architecture, uint32 version)
+{
+ NT_PRINTER_INFO_LEVEL *printer = NULL;
+ NT_PRINTER_DRIVER_INFO_LEVEL driver;
+ WERROR status;
+
+ ZERO_STRUCT(driver);
+
+ status=get_a_printer(NULL, &printer, 2, lp_const_servicename(snum) );
+
+ DEBUG(8,("construct_printer_driver_info_6: status: %s\n", dos_errstr(status)));
+
+ if (!W_ERROR_IS_OK(status))
+ return WERR_INVALID_PRINTER_NAME;
+
+ status = get_a_printer_driver(&driver, 3, printer->info_2->drivername, architecture, version);
+
+ DEBUG(8,("construct_printer_driver_info_6: status: %s\n", dos_errstr(status)));
+
+ if (!W_ERROR_IS_OK(status))
+ {
+ /*
+ * Is this a W2k client ?
+ */
+
+ if (version < 3) {
+ free_a_printer(&printer,2);
+ return WERR_UNKNOWN_PRINTER_DRIVER;
+ }
+
+ /* Yes - try again with a WinNT driver. */
+ version = 2;
+ status=get_a_printer_driver(&driver, 3, printer->info_2->drivername, architecture, version);
+ DEBUG(8,("construct_printer_driver_info_6: status: %s\n", dos_errstr(status)));
+ if (!W_ERROR_IS_OK(status)) {
+ free_a_printer(&printer,2);
+ return WERR_UNKNOWN_PRINTER_DRIVER;
+ }
+ }
+
+ fill_printer_driver_info_6(info, driver, servername);
+
+ free_a_printer(&printer,2);
+ free_a_printer_driver(driver, 3);
+
+ return WERR_OK;
+}
+
+/****************************************************************************
+****************************************************************************/
+
+static void free_printer_driver_info_3(DRIVER_INFO_3 *info)
+{
+ SAFE_FREE(info->dependentfiles);
+}
+
+/****************************************************************************
+****************************************************************************/
+
+static void free_printer_driver_info_6(DRIVER_INFO_6 *info)
+{
+ SAFE_FREE(info->dependentfiles);
+
+}
+
+/****************************************************************************
+****************************************************************************/
+
+static WERROR getprinterdriver2_level1(fstring servername, fstring architecture, uint32 version, int snum, NEW_BUFFER *buffer, uint32 offered, uint32 *needed)
+{
+ DRIVER_INFO_1 *info=NULL;
+ WERROR status;
+
+ if((info=(DRIVER_INFO_1 *)malloc(sizeof(DRIVER_INFO_1))) == NULL)
+ return WERR_NOMEM;
+
+ status=construct_printer_driver_info_1(info, snum, servername, architecture, version);
+ if (!W_ERROR_IS_OK(status)) {
+ SAFE_FREE(info);
+ return status;
+ }
+
+ /* check the required size. */
+ *needed += spoolss_size_printer_driver_info_1(info);
+
+ if (!alloc_buffer_size(buffer, *needed)) {
+ SAFE_FREE(info);
+ return WERR_INSUFFICIENT_BUFFER;
+ }
+
+ /* fill the buffer with the structures */
+ smb_io_printer_driver_info_1("", buffer, info, 0);
+
+ /* clear memory */
+ SAFE_FREE(info);
+
+ if (*needed > offered)
+ return WERR_INSUFFICIENT_BUFFER;
+
+ return WERR_OK;
+}
+
+/****************************************************************************
+****************************************************************************/
+
+static WERROR getprinterdriver2_level2(fstring servername, fstring architecture, uint32 version, int snum, NEW_BUFFER *buffer, uint32 offered, uint32 *needed)
+{
+ DRIVER_INFO_2 *info=NULL;
+ WERROR status;
+
+ if((info=(DRIVER_INFO_2 *)malloc(sizeof(DRIVER_INFO_2))) == NULL)
+ return WERR_NOMEM;
+
+ status=construct_printer_driver_info_2(info, snum, servername, architecture, version);
+ if (!W_ERROR_IS_OK(status)) {
+ SAFE_FREE(info);
+ return status;
+ }
+
+ /* check the required size. */
+ *needed += spoolss_size_printer_driver_info_2(info);
+
+ if (!alloc_buffer_size(buffer, *needed)) {
+ SAFE_FREE(info);
+ return WERR_INSUFFICIENT_BUFFER;
+ }
+
+ /* fill the buffer with the structures */
+ smb_io_printer_driver_info_2("", buffer, info, 0);
+
+ /* clear memory */
+ SAFE_FREE(info);
+
+ if (*needed > offered)
+ return WERR_INSUFFICIENT_BUFFER;
+
+ return WERR_OK;
+}
+
+/****************************************************************************
+****************************************************************************/
+
+static WERROR getprinterdriver2_level3(fstring servername, fstring architecture, uint32 version, int snum, NEW_BUFFER *buffer, uint32 offered, uint32 *needed)
+{
+ DRIVER_INFO_3 info;
+ WERROR status;
+
+ ZERO_STRUCT(info);
+
+ status=construct_printer_driver_info_3(&info, snum, servername, architecture, version);
+ if (!W_ERROR_IS_OK(status)) {
+ return status;
+ }
+
+ /* check the required size. */
+ *needed += spoolss_size_printer_driver_info_3(&info);
+
+ if (!alloc_buffer_size(buffer, *needed)) {
+ free_printer_driver_info_3(&info);
+ return WERR_INSUFFICIENT_BUFFER;
+ }
+
+ /* fill the buffer with the structures */
+ smb_io_printer_driver_info_3("", buffer, &info, 0);
+
+ free_printer_driver_info_3(&info);
+
+ if (*needed > offered)
+ return WERR_INSUFFICIENT_BUFFER;
+
+ return WERR_OK;
+}
+
+/****************************************************************************
+****************************************************************************/
+
+static WERROR getprinterdriver2_level6(fstring servername, fstring architecture, uint32 version, int snum, NEW_BUFFER *buffer, uint32 offered, uint32 *needed)
+{
+ DRIVER_INFO_6 info;
+ WERROR status;
+
+ ZERO_STRUCT(info);
+
+ status=construct_printer_driver_info_6(&info, snum, servername, architecture, version);
+ if (!W_ERROR_IS_OK(status)) {
+ return status;
+ }
+
+ /* check the required size. */
+ *needed += spoolss_size_printer_driver_info_6(&info);
+
+ if (!alloc_buffer_size(buffer, *needed)) {
+ free_printer_driver_info_6(&info);
+ return WERR_INSUFFICIENT_BUFFER;
+ }
+
+ /* fill the buffer with the structures */
+ smb_io_printer_driver_info_6("", buffer, &info, 0);
+
+ free_printer_driver_info_6(&info);
+
+ if (*needed > offered)
+ return WERR_INSUFFICIENT_BUFFER;
+
+ return WERR_OK;
+}
+
+/****************************************************************************
+****************************************************************************/
+
+WERROR _spoolss_getprinterdriver2(pipes_struct *p, SPOOL_Q_GETPRINTERDRIVER2 *q_u, SPOOL_R_GETPRINTERDRIVER2 *r_u)
+{
+ POLICY_HND *handle = &q_u->handle;
+ UNISTR2 *uni_arch = &q_u->architecture;
+ uint32 level = q_u->level;
+ uint32 clientmajorversion = q_u->clientmajorversion;
+ NEW_BUFFER *buffer = NULL;
+ uint32 offered = q_u->offered;
+ uint32 *needed = &r_u->needed;
+ uint32 *servermajorversion = &r_u->servermajorversion;
+ uint32 *serverminorversion = &r_u->serverminorversion;
+
+ fstring servername;
+ fstring architecture;
+ int snum;
+
+ /* that's an [in out] buffer */
+ spoolss_move_buffer(q_u->buffer, &r_u->buffer);
+ buffer = r_u->buffer;
+
+ DEBUG(4,("_spoolss_getprinterdriver2\n"));
+
+ *needed = 0;
+ *servermajorversion = 0;
+ *serverminorversion = 0;
+
+ fstrcpy(servername, get_called_name());
+ unistr2_to_ascii(architecture, uni_arch, sizeof(architecture)-1);
+
+ if (!get_printer_snum(p, handle, &snum))
+ return WERR_BADFID;
+
+ switch (level) {
+ case 1:
+ return getprinterdriver2_level1(servername, architecture, clientmajorversion, snum, buffer, offered, needed);
+ case 2:
+ return getprinterdriver2_level2(servername, architecture, clientmajorversion, snum, buffer, offered, needed);
+ case 3:
+ return getprinterdriver2_level3(servername, architecture, clientmajorversion, snum, buffer, offered, needed);
+ case 6:
+ return getprinterdriver2_level6(servername, architecture, clientmajorversion, snum, buffer, offered, needed);
+ }
+
+ return WERR_UNKNOWN_LEVEL;
+}
+
+/****************************************************************************
+****************************************************************************/
+
+WERROR _spoolss_startpageprinter(pipes_struct *p, SPOOL_Q_STARTPAGEPRINTER *q_u, SPOOL_R_STARTPAGEPRINTER *r_u)
+{
+ POLICY_HND *handle = &q_u->handle;
+
+ Printer_entry *Printer = find_printer_index_by_hnd(p, handle);
+
+ if (!Printer) {
+ DEBUG(3,("Error in startpageprinter printer handle\n"));
+ return WERR_BADFID;
+ }
+
+ Printer->page_started=True;
+ return WERR_OK;
+}
+
+/****************************************************************************
+****************************************************************************/
+
+WERROR _spoolss_endpageprinter(pipes_struct *p, SPOOL_Q_ENDPAGEPRINTER *q_u, SPOOL_R_ENDPAGEPRINTER *r_u)
+{
+ POLICY_HND *handle = &q_u->handle;
+ int snum;
+
+ Printer_entry *Printer = find_printer_index_by_hnd(p, handle);
+
+ if (!Printer) {
+ DEBUG(2,("_spoolss_endpageprinter: Invalid handle (%s:%u:%u).\n",OUR_HANDLE(handle)));
+ return WERR_BADFID;
+ }
+
+ if (!get_printer_snum(p, handle, &snum))
+ return WERR_BADFID;
+
+ Printer->page_started=False;
+ print_job_endpage(snum, Printer->jobid);
+
+ return WERR_OK;
+}
+
+/********************************************************************
+ * api_spoolss_getprinter
+ * called from the spoolss dispatcher
+ *
+ ********************************************************************/
+
+WERROR _spoolss_startdocprinter(pipes_struct *p, SPOOL_Q_STARTDOCPRINTER *q_u, SPOOL_R_STARTDOCPRINTER *r_u)
+{
+ POLICY_HND *handle = &q_u->handle;
+ DOC_INFO *docinfo = &q_u->doc_info_container.docinfo;
+ uint32 *jobid = &r_u->jobid;
+
+ DOC_INFO_1 *info_1 = &docinfo->doc_info_1;
+ int snum;
+ pstring jobname;
+ fstring datatype;
+ Printer_entry *Printer = find_printer_index_by_hnd(p, handle);
+ struct current_user user;
+
+ if (!Printer) {
+ DEBUG(2,("_spoolss_startdocprinter: Invalid handle (%s:%u:%u)\n", OUR_HANDLE(handle)));
+ return WERR_BADFID;
+ }
+
+ get_current_user(&user, p);
+
+ /*
+ * a nice thing with NT is it doesn't listen to what you tell it.
+ * when asked to send _only_ RAW datas, it tries to send datas
+ * in EMF format.
+ *
+ * So I add checks like in NT Server ...
+ */
+
+ if (info_1->p_datatype != 0) {
+ unistr2_to_ascii(datatype, &info_1->datatype, sizeof(datatype));
+ if (strcmp(datatype, "RAW") != 0) {
+ (*jobid)=0;
+ return WERR_INVALID_DATATYPE;
+ }
+ }
+
+ /* get the share number of the printer */
+ if (!get_printer_snum(p, handle, &snum)) {
+ return WERR_BADFID;
+ }
+
+ unistr2_to_ascii(jobname, &info_1->docname, sizeof(jobname));
+
+ Printer->jobid = print_job_start(&user, snum, jobname, Printer->nt_devmode);
+
+ /* An error occured in print_job_start() so return an appropriate
+ NT error code. */
+
+ if (Printer->jobid == -1) {
+ return map_werror_from_unix(errno);
+ }
+
+ Printer->document_started=True;
+ (*jobid) = Printer->jobid;
+
+ return WERR_OK;
+}
+
+/********************************************************************
+ * api_spoolss_getprinter
+ * called from the spoolss dispatcher
+ *
+ ********************************************************************/
+
+WERROR _spoolss_enddocprinter(pipes_struct *p, SPOOL_Q_ENDDOCPRINTER *q_u, SPOOL_R_ENDDOCPRINTER *r_u)
+{
+ POLICY_HND *handle = &q_u->handle;
+
+ return _spoolss_enddocprinter_internal(p, handle);
+}
+
+/****************************************************************************
+****************************************************************************/
+
+WERROR _spoolss_writeprinter(pipes_struct *p, SPOOL_Q_WRITEPRINTER *q_u, SPOOL_R_WRITEPRINTER *r_u)
+{
+ POLICY_HND *handle = &q_u->handle;
+ uint32 buffer_size = q_u->buffer_size;
+ uint8 *buffer = q_u->buffer;
+ uint32 *buffer_written = &q_u->buffer_size2;
+ int snum;
+ Printer_entry *Printer = find_printer_index_by_hnd(p, handle);
+
+ if (!Printer) {
+ DEBUG(2,("_spoolss_writeprinter: Invalid handle (%s:%u:%u)\n",OUR_HANDLE(handle)));
+ r_u->buffer_written = q_u->buffer_size2;
+ return WERR_BADFID;
+ }
+
+ if (!get_printer_snum(p, handle, &snum))
+ return WERR_BADFID;
+
+ (*buffer_written) = print_job_write(snum, Printer->jobid, (char *)buffer, buffer_size);
+ if (*buffer_written == -1) {
+ r_u->buffer_written = 0;
+ if (errno == ENOSPC)
+ return WERR_NO_SPOOL_SPACE;
+ else
+ return WERR_ACCESS_DENIED;
+ }
+
+ r_u->buffer_written = q_u->buffer_size2;
+
+ return WERR_OK;
+}
+
+/********************************************************************
+ * api_spoolss_getprinter
+ * called from the spoolss dispatcher
+ *
+ ********************************************************************/
+
+static WERROR control_printer(POLICY_HND *handle, uint32 command,
+ pipes_struct *p)
+{
+ struct current_user user;
+ int snum;
+ WERROR errcode = WERR_BADFUNC;
+ Printer_entry *Printer = find_printer_index_by_hnd(p, handle);
+
+ get_current_user(&user, p);
+
+ if (!Printer) {
+ DEBUG(2,("control_printer: Invalid handle (%s:%u:%u)\n", OUR_HANDLE(handle)));
+ return WERR_BADFID;
+ }
+
+ if (!get_printer_snum(p, handle, &snum))
+ return WERR_BADFID;
+
+ switch (command) {
+ case PRINTER_CONTROL_PAUSE:
+ if (print_queue_pause(&user, snum, &errcode)) {
+ errcode = WERR_OK;
+ }
+ break;
+ case PRINTER_CONTROL_RESUME:
+ case PRINTER_CONTROL_UNPAUSE:
+ if (print_queue_resume(&user, snum, &errcode)) {
+ errcode = WERR_OK;
+ }
+ break;
+ case PRINTER_CONTROL_PURGE:
+ if (print_queue_purge(&user, snum, &errcode)) {
+ errcode = WERR_OK;
+ }
+ break;
+ default:
+ return WERR_UNKNOWN_LEVEL;
+ }
+
+ return errcode;
+}
+
+/********************************************************************
+ * api_spoolss_abortprinter
+ * From MSDN: "Deletes printer's spool file if printer is configured
+ * for spooling"
+ ********************************************************************/
+
+WERROR _spoolss_abortprinter(pipes_struct *p, SPOOL_Q_ABORTPRINTER *q_u, SPOOL_R_ABORTPRINTER *r_u)
+{
+ POLICY_HND *handle = &q_u->handle;
+ Printer_entry *Printer = find_printer_index_by_hnd(p, handle);
+ int snum;
+ struct current_user user;
+ WERROR errcode = WERR_OK;
+
+ if (!Printer) {
+ DEBUG(2,("_spoolss_abortprinter: Invalid handle (%s:%u:%u)\n",OUR_HANDLE(handle)));
+ return WERR_BADFID;
+ }
+
+ if (!get_printer_snum(p, handle, &snum))
+ return WERR_BADFID;
+
+ get_current_user( &user, p );
+
+ print_job_delete( &user, snum, Printer->jobid, &errcode );
+
+ return errcode;
+}
+
+/********************************************************************
+ * called by spoolss_api_setprinter
+ * when updating a printer description
+ ********************************************************************/
+
+static WERROR update_printer_sec(POLICY_HND *handle, uint32 level,
+ const SPOOL_PRINTER_INFO_LEVEL *info,
+ pipes_struct *p, SEC_DESC_BUF *secdesc_ctr)
+{
+ SEC_DESC_BUF *new_secdesc_ctr = NULL, *old_secdesc_ctr = NULL;
+ struct current_user user;
+ WERROR result;
+ int snum;
+
+ Printer_entry *Printer = find_printer_index_by_hnd(p, handle);
+
+ if (!Printer || !get_printer_snum(p, handle, &snum)) {
+ DEBUG(2,("update_printer_sec: Invalid handle (%s:%u:%u)\n",
+ OUR_HANDLE(handle)));
+
+ result = WERR_BADFID;
+ goto done;
+ }
+
+ /* NT seems to like setting the security descriptor even though
+ nothing may have actually changed. This causes annoying
+ dialog boxes when the user doesn't have permission to change
+ the security descriptor. */
+
+ nt_printing_getsec(p->mem_ctx, Printer->dev.handlename, &old_secdesc_ctr);
+
+ if (DEBUGLEVEL >= 10) {
+ SEC_ACL *the_acl;
+ int i;
+
+ the_acl = old_secdesc_ctr->sec->dacl;
+ DEBUG(10, ("old_secdesc_ctr for %s has %d aces:\n",
+ PRINTERNAME(snum), the_acl->num_aces));
+
+ for (i = 0; i < the_acl->num_aces; i++) {
+ fstring sid_str;
+
+ sid_to_string(sid_str, &the_acl->ace[i].trustee);
+
+ DEBUG(10, ("%s 0x%08x\n", sid_str,
+ the_acl->ace[i].info.mask));
+ }
+
+ the_acl = secdesc_ctr->sec->dacl;
+
+ if (the_acl) {
+ DEBUG(10, ("secdesc_ctr for %s has %d aces:\n",
+ PRINTERNAME(snum), the_acl->num_aces));
+
+ for (i = 0; i < the_acl->num_aces; i++) {
+ fstring sid_str;
+
+ sid_to_string(sid_str, &the_acl->ace[i].trustee);
+
+ DEBUG(10, ("%s 0x%08x\n", sid_str,
+ the_acl->ace[i].info.mask));
+ }
+ } else {
+ DEBUG(10, ("dacl for secdesc_ctr is NULL\n"));
+ }
+ }
+
+ new_secdesc_ctr = sec_desc_merge(p->mem_ctx, secdesc_ctr, old_secdesc_ctr);
+
+ if (sec_desc_equal(new_secdesc_ctr->sec, old_secdesc_ctr->sec)) {
+ result = WERR_OK;
+ goto done;
+ }
+
+ /* Work out which user is performing the operation */
+
+ get_current_user(&user, p);
+
+ /* Check the user has permissions to change the security
+ descriptor. By experimentation with two NT machines, the user
+ requires Full Access to the printer to change security
+ information. */
+
+ if (!print_access_check(&user, snum, PRINTER_ACCESS_ADMINISTER)) {
+ result = WERR_ACCESS_DENIED;
+ goto done;
+ }
+
+ result = nt_printing_setsec(Printer->dev.handlename, new_secdesc_ctr);
+
+ done:
+
+ return result;
+}
+
+/********************************************************************
+ Do Samba sanity checks on a printer info struct.
+ this has changed purpose: it now "canonicalises" printer
+ info from a client rather than just checking it is correct
+ ********************************************************************/
+
+static BOOL check_printer_ok(NT_PRINTER_INFO_LEVEL_2 *info, int snum)
+{
+ DEBUG(5,("check_printer_ok: servername=%s printername=%s sharename=%s portname=%s drivername=%s comment=%s location=%s\n",
+ info->servername, info->printername, info->sharename, info->portname, info->drivername, info->comment, info->location));
+
+ /* we force some elements to "correct" values */
+ slprintf(info->servername, sizeof(info->servername)-1, "\\\\%s", get_called_name());
+ fstrcpy(info->sharename, lp_servicename(snum));
+ slprintf(info->printername, sizeof(info->printername)-1, "\\\\%s\\%s",
+ get_called_name(), info->sharename);
+ info->attributes = PRINTER_ATTRIBUTE_SAMBA;
+
+
+ return True;
+}
+
+/****************************************************************************
+****************************************************************************/
+
+static BOOL add_printer_hook(NT_PRINTER_INFO_LEVEL *printer)
+{
+ extern userdom_struct current_user_info;
+ char *cmd = lp_addprinter_cmd();
+ char **qlines;
+ pstring command;
+ int numlines;
+ int ret;
+ int fd;
+ fstring remote_machine = "%m";
+
+ standard_sub_basic(current_user_info.smb_name, remote_machine,sizeof(remote_machine));
+
+ slprintf(command, sizeof(command)-1, "%s \"%s\" \"%s\" \"%s\" \"%s\" \"%s\" \"%s\" \"%s\"",
+ cmd, printer->info_2->printername, printer->info_2->sharename,
+ printer->info_2->portname, printer->info_2->drivername,
+ printer->info_2->location, printer->info_2->comment, remote_machine);
+
+ DEBUG(10,("Running [%s]\n", command));
+ ret = smbrun(command, &fd);
+ DEBUGADD(10,("returned [%d]\n", ret));
+
+ if ( ret != 0 ) {
+ if (fd != -1)
+ close(fd);
+ return False;
+ }
+
+ numlines = 0;
+ /* Get lines and convert them back to dos-codepage */
+ qlines = fd_lines_load(fd, &numlines);
+ DEBUGADD(10,("Lines returned = [%d]\n", numlines));
+ close(fd);
+
+ if(numlines) {
+ /* Set the portname to what the script says the portname should be. */
+ strncpy(printer->info_2->portname, qlines[0], sizeof(printer->info_2->portname));
+ DEBUGADD(6,("Line[0] = [%s]\n", qlines[0]));
+
+ /* Send SIGHUP to process group... is there a better way? */
+ kill(0, SIGHUP);
+
+ /* reload our services immediately */
+ reload_services( False );
+ }
+
+ file_lines_free(qlines);
+ return True;
+}
+
+/********************************************************************
+ * Called by spoolss_api_setprinter
+ * when updating a printer description.
+ ********************************************************************/
+
+static WERROR update_printer(pipes_struct *p, POLICY_HND *handle, uint32 level,
+ const SPOOL_PRINTER_INFO_LEVEL *info,
+ DEVICEMODE *devmode)
+{
+ int snum;
+ NT_PRINTER_INFO_LEVEL *printer = NULL, *old_printer = NULL;
+ Printer_entry *Printer = find_printer_index_by_hnd(p, handle);
+ WERROR result;
+ UNISTR2 buffer;
+ fstring asc_buffer;
+
+ DEBUG(8,("update_printer\n"));
+
+ result = WERR_OK;
+
+ if (!Printer) {
+ result = WERR_BADFID;
+ goto done;
+ }
+
+ if (!get_printer_snum(p, handle, &snum)) {
+ result = WERR_BADFID;
+ goto done;
+ }
+
+ if (!W_ERROR_IS_OK(get_a_printer(Printer, &printer, 2, lp_const_servicename(snum))) ||
+ (!W_ERROR_IS_OK(get_a_printer(Printer, &old_printer, 2, lp_const_servicename(snum))))) {
+ result = WERR_BADFID;
+ goto done;
+ }
+
+ DEBUGADD(8,("Converting info_2 struct\n"));
+
+ /*
+ * convert_printer_info converts the incoming
+ * info from the client and overwrites the info
+ * just read from the tdb in the pointer 'printer'.
+ */
+
+ if (!convert_printer_info(info, printer, level)) {
+ result = WERR_NOMEM;
+ goto done;
+ }
+
+ if (devmode) {
+ /* we have a valid devmode
+ convert it and link it*/
+
+ DEBUGADD(8,("update_printer: Converting the devicemode struct\n"));
+ if (!convert_devicemode(printer->info_2->printername, devmode,
+ &printer->info_2->devmode)) {
+ result = WERR_NOMEM;
+ goto done;
+ }
+ }
+
+ /* Do sanity check on the requested changes for Samba */
+
+ if (!check_printer_ok(printer->info_2, snum)) {
+ result = WERR_INVALID_PARAM;
+ goto done;
+ }
+
+ /* FIXME!!! If the driver has changed we really should verify that
+ it is installed before doing much else --jerry */
+
+ /* Check calling user has permission to update printer description */
+
+ if (Printer->access_granted != PRINTER_ACCESS_ADMINISTER) {
+ DEBUG(3, ("update_printer: printer property change denied by handle\n"));
+ result = WERR_ACCESS_DENIED;
+ goto done;
+ }
+
+ /* Call addprinter hook */
+ /* Check changes to see if this is really needed */
+
+ if ( *lp_addprinter_cmd()
+ && (!strequal(printer->info_2->drivername, old_printer->info_2->drivername)
+ || !strequal(printer->info_2->comment, old_printer->info_2->comment)
+ || !strequal(printer->info_2->portname, old_printer->info_2->portname)
+ || !strequal(printer->info_2->location, old_printer->info_2->location)) )
+ {
+ if ( !add_printer_hook(printer) ) {
+ result = WERR_ACCESS_DENIED;
+ goto done;
+ }
+
+ /*
+ * make sure we actually reload the services after
+ * this as smb.conf could have a new section in it
+ * .... shouldn't .... but could
+ */
+ reload_services(False);
+ }
+
+ /*
+ * When a *new* driver is bound to a printer, the drivername is used to
+ * lookup previously saved driver initialization info, which is then
+ * bound to the printer, simulating what happens in the Windows arch.
+ */
+ if (!strequal(printer->info_2->drivername, old_printer->info_2->drivername))
+ {
+ if (!set_driver_init(printer, 2))
+ {
+ DEBUG(5,("update_printer: Error restoring driver initialization data for driver [%s]!\n",
+ printer->info_2->drivername));
+ }
+
+ DEBUG(10,("update_printer: changing driver [%s]! Sending event!\n",
+ printer->info_2->drivername));
+
+ notify_printer_driver(snum, printer->info_2->drivername);
+ }
+
+ /*
+ * flag which changes actually occured. This is a small subset of
+ * all the possible changes. We also have to update things in the
+ * DsSpooler key.
+ */
+
+ if (!strequal(printer->info_2->comment, old_printer->info_2->comment)) {
+ init_unistr2( &buffer, printer->info_2->comment, UNI_STR_TERMINATE);
+ set_printer_dataex( printer, SPOOL_DSSPOOLER_KEY, "description",
+ REG_SZ, (uint8*)buffer.buffer, buffer.uni_str_len*2 );
+
+ notify_printer_comment(snum, printer->info_2->comment);
+ }
+
+ if (!strequal(printer->info_2->sharename, old_printer->info_2->sharename)) {
+ init_unistr2( &buffer, printer->info_2->sharename, UNI_STR_TERMINATE);
+ set_printer_dataex( printer, SPOOL_DSSPOOLER_KEY, "printerName",
+ REG_SZ, (uint8*)buffer.buffer, buffer.uni_str_len*2 );
+ set_printer_dataex( printer, SPOOL_DSSPOOLER_KEY, "shareName",
+ REG_SZ, (uint8*)buffer.buffer, buffer.uni_str_len*2 );
+
+ notify_printer_sharename(snum, printer->info_2->sharename);
+ }
+
+ if (!strequal(printer->info_2->portname, old_printer->info_2->portname)) {
+ init_unistr2( &buffer, printer->info_2->portname, UNI_STR_TERMINATE);
+ set_printer_dataex( printer, SPOOL_DSSPOOLER_KEY, "portName",
+ REG_SZ, (uint8*)buffer.buffer, buffer.uni_str_len*2 );
+
+ notify_printer_port(snum, printer->info_2->portname);
+ }
+
+ if (!strequal(printer->info_2->location, old_printer->info_2->location)) {
+ init_unistr2( &buffer, printer->info_2->location, UNI_STR_TERMINATE);
+ set_printer_dataex( printer, SPOOL_DSSPOOLER_KEY, "location",
+ REG_SZ, (uint8*)buffer.buffer, buffer.uni_str_len*2 );
+
+ notify_printer_location(snum, printer->info_2->location);
+ }
+
+ /* here we need to update some more DsSpooler keys */
+ /* uNCName, serverName, shortServerName */
+
+ init_unistr2( &buffer, global_myname(), UNI_STR_TERMINATE);
+ set_printer_dataex( printer, SPOOL_DSSPOOLER_KEY, "serverName",
+ REG_SZ, (uint8*)buffer.buffer, buffer.uni_str_len*2 );
+ set_printer_dataex( printer, SPOOL_DSSPOOLER_KEY, "shortServerName",
+ REG_SZ, (uint8*)buffer.buffer, buffer.uni_str_len*2 );
+
+ slprintf( asc_buffer, sizeof(asc_buffer)-1, "\\\\%s\\%s",
+ global_myname(), printer->info_2->sharename );
+ init_unistr2( &buffer, asc_buffer, UNI_STR_TERMINATE);
+ set_printer_dataex( printer, SPOOL_DSSPOOLER_KEY, "uNCName",
+ REG_SZ, (uint8*)buffer.buffer, buffer.uni_str_len*2 );
+
+ /* Update printer info */
+ result = mod_a_printer(*printer, 2);
+
+done:
+ free_a_printer(&printer, 2);
+ free_a_printer(&old_printer, 2);
+
+
+ return result;
+}
+
+/****************************************************************************
+****************************************************************************/
+static WERROR publish_or_unpublish_printer(pipes_struct *p, POLICY_HND *handle,
+ const SPOOL_PRINTER_INFO_LEVEL *info)
+{
+#ifdef HAVE_ADS
+ SPOOL_PRINTER_INFO_LEVEL_7 *info7 = info->info_7;
+ int snum;
+ Printer_entry *Printer = find_printer_index_by_hnd(p, handle);
+
+ DEBUG(5,("publish_or_unpublish_printer, action = %d\n",info7->action));
+
+ if (!Printer)
+ return WERR_BADFID;
+
+ if (!get_printer_snum(p, handle, &snum))
+ return WERR_BADFID;
+
+ nt_printer_publish(Printer, snum, info7->action);
+
+ return WERR_OK;
+#else
+ return WERR_UNKNOWN_LEVEL;
+#endif
+}
+/****************************************************************************
+****************************************************************************/
+
+WERROR _spoolss_setprinter(pipes_struct *p, SPOOL_Q_SETPRINTER *q_u, SPOOL_R_SETPRINTER *r_u)
+{
+ POLICY_HND *handle = &q_u->handle;
+ uint32 level = q_u->level;
+ SPOOL_PRINTER_INFO_LEVEL *info = &q_u->info;
+ DEVMODE_CTR devmode_ctr = q_u->devmode_ctr;
+ SEC_DESC_BUF *secdesc_ctr = q_u->secdesc_ctr;
+ uint32 command = q_u->command;
+
+ Printer_entry *Printer = find_printer_index_by_hnd(p, handle);
+
+ if (!Printer) {
+ DEBUG(2,("_spoolss_setprinter: Invalid handle (%s:%u:%u)\n", OUR_HANDLE(handle)));
+ return WERR_BADFID;
+ }
+
+ /* check the level */
+ switch (level) {
+ case 0:
+ return control_printer(handle, command, p);
+ case 2:
+ return update_printer(p, handle, level, info, devmode_ctr.devmode);
+ case 3:
+ return update_printer_sec(handle, level, info, p,
+ secdesc_ctr);
+ case 7:
+ return publish_or_unpublish_printer(p, handle, info);
+ default:
+ return WERR_UNKNOWN_LEVEL;
+ }
+}
+
+/****************************************************************************
+****************************************************************************/
+
+WERROR _spoolss_fcpn(pipes_struct *p, SPOOL_Q_FCPN *q_u, SPOOL_R_FCPN *r_u)
+{
+ POLICY_HND *handle = &q_u->handle;
+ Printer_entry *Printer= find_printer_index_by_hnd(p, handle);
+
+ if (!Printer) {
+ DEBUG(2,("_spoolss_fcpn: Invalid handle (%s:%u:%u)\n", OUR_HANDLE(handle)));
+ return WERR_BADFID;
+ }
+
+ if (Printer->notify.client_connected==True) {
+ int snum = -1;
+
+ if ( Printer->printer_type == PRINTER_HANDLE_IS_PRINTSERVER)
+ snum = -1;
+ else if ( (Printer->printer_type == PRINTER_HANDLE_IS_PRINTER) &&
+ !get_printer_snum(p, handle, &snum) )
+ return WERR_BADFID;
+
+ srv_spoolss_replycloseprinter(snum, &Printer->notify.client_hnd);
+ }
+
+ Printer->notify.flags=0;
+ Printer->notify.options=0;
+ Printer->notify.localmachine[0]='\0';
+ Printer->notify.printerlocal=0;
+ if (Printer->notify.option)
+ free_spool_notify_option(&Printer->notify.option);
+ Printer->notify.client_connected=False;
+
+ return WERR_OK;
+}
+
+/****************************************************************************
+****************************************************************************/
+
+WERROR _spoolss_addjob(pipes_struct *p, SPOOL_Q_ADDJOB *q_u, SPOOL_R_ADDJOB *r_u)
+{
+ /* that's an [in out] buffer (despite appearences to the contrary) */
+ spoolss_move_buffer(q_u->buffer, &r_u->buffer);
+
+ r_u->needed = 0;
+ return WERR_INVALID_PARAM; /* this is what a NT server
+ returns for AddJob. AddJob
+ must fail on non-local
+ printers */
+}
+
+/****************************************************************************
+****************************************************************************/
+
+static void fill_job_info_1(JOB_INFO_1 *job_info, print_queue_struct *queue,
+ int position, int snum)
+{
+ pstring temp_name;
+
+ struct tm *t;
+
+ t=gmtime(&queue->time);
+ slprintf(temp_name, sizeof(temp_name)-1, "\\\\%s", get_called_name());
+
+ job_info->jobid=queue->job;
+ init_unistr(&job_info->printername, lp_servicename(snum));
+ init_unistr(&job_info->machinename, temp_name);
+ init_unistr(&job_info->username, queue->fs_user);
+ init_unistr(&job_info->document, queue->fs_file);
+ init_unistr(&job_info->datatype, "RAW");
+ init_unistr(&job_info->text_status, "");
+ job_info->status=nt_printj_status(queue->status);
+ job_info->priority=queue->priority;
+ job_info->position=position;
+ job_info->totalpages=queue->page_count;
+ job_info->pagesprinted=0;
+
+ make_systemtime(&job_info->submitted, t);
+}
+
+/****************************************************************************
+****************************************************************************/
+
+static BOOL fill_job_info_2(JOB_INFO_2 *job_info, print_queue_struct *queue,
+ int position, int snum,
+ NT_PRINTER_INFO_LEVEL *ntprinter,
+ DEVICEMODE *devmode)
+{
+ pstring temp_name;
+ struct tm *t;
+
+ t=gmtime(&queue->time);
+ slprintf(temp_name, sizeof(temp_name)-1, "\\\\%s", get_called_name());
+
+ job_info->jobid=queue->job;
+
+ init_unistr(&job_info->printername, ntprinter->info_2->printername);
+
+ init_unistr(&job_info->machinename, temp_name);
+ init_unistr(&job_info->username, queue->fs_user);
+ init_unistr(&job_info->document, queue->fs_file);
+ init_unistr(&job_info->notifyname, queue->fs_user);
+ init_unistr(&job_info->datatype, "RAW");
+ init_unistr(&job_info->printprocessor, "winprint");
+ init_unistr(&job_info->parameters, "");
+ init_unistr(&job_info->drivername, ntprinter->info_2->drivername);
+ init_unistr(&job_info->text_status, "");
+
+/* and here the security descriptor */
+
+ job_info->status=nt_printj_status(queue->status);
+ job_info->priority=queue->priority;
+ job_info->position=position;
+ job_info->starttime=0;
+ job_info->untiltime=0;
+ job_info->totalpages=queue->page_count;
+ job_info->size=queue->size;
+ make_systemtime(&(job_info->submitted), t);
+ job_info->timeelapsed=0;
+ job_info->pagesprinted=0;
+
+ job_info->devmode = devmode;
+
+ return (True);
+}
+
+/****************************************************************************
+ Enumjobs at level 1.
+****************************************************************************/
+
+static WERROR enumjobs_level1(print_queue_struct *queue, int snum,
+ NEW_BUFFER *buffer, uint32 offered,
+ uint32 *needed, uint32 *returned)
+{
+ JOB_INFO_1 *info;
+ int i;
+
+ info=(JOB_INFO_1 *)malloc(*returned*sizeof(JOB_INFO_1));
+ if (info==NULL) {
+ SAFE_FREE(queue);
+ *returned=0;
+ return WERR_NOMEM;
+ }
+
+ for (i=0; i<*returned; i++)
+ fill_job_info_1(&info[i], &queue[i], i, snum);
+
+ SAFE_FREE(queue);
+
+ /* check the required size. */
+ for (i=0; i<*returned; i++)
+ (*needed) += spoolss_size_job_info_1(&info[i]);
+
+ if (!alloc_buffer_size(buffer, *needed)) {
+ SAFE_FREE(info);
+ return WERR_INSUFFICIENT_BUFFER;
+ }
+
+ /* fill the buffer with the structures */
+ for (i=0; i<*returned; i++)
+ smb_io_job_info_1("", buffer, &info[i], 0);
+
+ /* clear memory */
+ SAFE_FREE(info);
+
+ if (*needed > offered) {
+ *returned=0;
+ return WERR_INSUFFICIENT_BUFFER;
+ }
+
+ return WERR_OK;
+}
+
+/****************************************************************************
+ Enumjobs at level 2.
+****************************************************************************/
+
+static WERROR enumjobs_level2(print_queue_struct *queue, int snum,
+ NEW_BUFFER *buffer, uint32 offered,
+ uint32 *needed, uint32 *returned)
+{
+ NT_PRINTER_INFO_LEVEL *ntprinter = NULL;
+ JOB_INFO_2 *info = NULL;
+ int i;
+ WERROR result;
+ DEVICEMODE *devmode = NULL;
+
+ info=(JOB_INFO_2 *)malloc(*returned*sizeof(JOB_INFO_2));
+ if (info==NULL) {
+ *returned=0;
+ result = WERR_NOMEM;
+ goto done;
+ }
+
+ result = get_a_printer(NULL, &ntprinter, 2, lp_servicename(snum));
+ if (!W_ERROR_IS_OK(result)) {
+ *returned = 0;
+ goto done;
+ }
+
+ /* this should not be a failure condition if the devmode is NULL */
+
+ devmode = construct_dev_mode(snum);
+
+ for (i=0; i<*returned; i++)
+ fill_job_info_2(&(info[i]), &queue[i], i, snum, ntprinter,
+ devmode);
+
+ free_a_printer(&ntprinter, 2);
+ SAFE_FREE(queue);
+
+ /* check the required size. */
+ for (i=0; i<*returned; i++)
+ (*needed) += spoolss_size_job_info_2(&info[i]);
+
+ if (*needed > offered) {
+ *returned=0;
+ result = WERR_INSUFFICIENT_BUFFER;
+ goto done;
+ }
+
+ if (!alloc_buffer_size(buffer, *needed)) {
+ SAFE_FREE(info);
+ result = WERR_INSUFFICIENT_BUFFER;
+ goto done;
+ }
+
+ /* fill the buffer with the structures */
+ for (i=0; i<*returned; i++)
+ smb_io_job_info_2("", buffer, &info[i], 0);
+
+ result = WERR_OK;
+
+ done:
+ free_a_printer(&ntprinter, 2);
+ free_devmode(devmode);
+ SAFE_FREE(queue);
+ SAFE_FREE(info);
+
+ return result;
+
+}
+
+/****************************************************************************
+ Enumjobs.
+****************************************************************************/
+
+WERROR _spoolss_enumjobs( pipes_struct *p, SPOOL_Q_ENUMJOBS *q_u, SPOOL_R_ENUMJOBS *r_u)
+{
+ POLICY_HND *handle = &q_u->handle;
+ uint32 level = q_u->level;
+ NEW_BUFFER *buffer = NULL;
+ uint32 offered = q_u->offered;
+ uint32 *needed = &r_u->needed;
+ uint32 *returned = &r_u->returned;
+ WERROR wret;
+
+ int snum;
+ print_status_struct prt_status;
+ print_queue_struct *queue=NULL;
+
+ /* that's an [in out] buffer */
+ spoolss_move_buffer(q_u->buffer, &r_u->buffer);
+ buffer = r_u->buffer;
+
+ DEBUG(4,("_spoolss_enumjobs\n"));
+
+ *needed=0;
+ *returned=0;
+
+ if (!get_printer_snum(p, handle, &snum))
+ return WERR_BADFID;
+
+ *returned = print_queue_status(snum, &queue, &prt_status);
+ DEBUGADD(4,("count:[%d], status:[%d], [%s]\n", *returned, prt_status.status, prt_status.message));
+
+ if (*returned == 0) {
+ SAFE_FREE(queue);
+ return WERR_OK;
+ }
+
+ switch (level) {
+ case 1:
+ wret = enumjobs_level1(queue, snum, buffer, offered, needed, returned);
+ return wret;
+ case 2:
+ wret = enumjobs_level2(queue, snum, buffer, offered, needed, returned);
+ return wret;
+ default:
+ SAFE_FREE(queue);
+ *returned=0;
+ return WERR_UNKNOWN_LEVEL;
+ }
+}
+
+/****************************************************************************
+****************************************************************************/
+
+WERROR _spoolss_schedulejob( pipes_struct *p, SPOOL_Q_SCHEDULEJOB *q_u, SPOOL_R_SCHEDULEJOB *r_u)
+{
+ return WERR_OK;
+}
+
+/****************************************************************************
+****************************************************************************/
+
+WERROR _spoolss_setjob(pipes_struct *p, SPOOL_Q_SETJOB *q_u, SPOOL_R_SETJOB *r_u)
+{
+ POLICY_HND *handle = &q_u->handle;
+ uint32 jobid = q_u->jobid;
+ uint32 command = q_u->command;
+
+ struct current_user user;
+ int snum;
+ WERROR errcode = WERR_BADFUNC;
+
+ if (!get_printer_snum(p, handle, &snum)) {
+ return WERR_BADFID;
+ }
+
+ if (!print_job_exists(snum, jobid)) {
+ return WERR_INVALID_PRINTER_NAME;
+ }
+
+ get_current_user(&user, p);
+
+ switch (command) {
+ case JOB_CONTROL_CANCEL:
+ case JOB_CONTROL_DELETE:
+ if (print_job_delete(&user, snum, jobid, &errcode)) {
+ errcode = WERR_OK;
+ }
+ break;
+ case JOB_CONTROL_PAUSE:
+ if (print_job_pause(&user, snum, jobid, &errcode)) {
+ errcode = WERR_OK;
+ }
+ break;
+ case JOB_CONTROL_RESTART:
+ case JOB_CONTROL_RESUME:
+ if (print_job_resume(&user, snum, jobid, &errcode)) {
+ errcode = WERR_OK;
+ }
+ break;
+ default:
+ return WERR_UNKNOWN_LEVEL;
+ }
+
+ return errcode;
+}
+
+/****************************************************************************
+ Enumerates all printer drivers at level 1.
+****************************************************************************/
+
+static WERROR enumprinterdrivers_level1(fstring servername, fstring architecture, NEW_BUFFER *buffer, uint32 offered, uint32 *needed, uint32 *returned)
+{
+ int i;
+ int ndrivers;
+ uint32 version;
+ fstring *list = NULL;
+
+ NT_PRINTER_DRIVER_INFO_LEVEL driver;
+ DRIVER_INFO_1 *tdi1, *driver_info_1=NULL;
+
+ *returned=0;
+
+ for (version=0; version<DRIVER_MAX_VERSION; version++) {
+ list=NULL;
+ ndrivers=get_ntdrivers(&list, architecture, version);
+ DEBUGADD(4,("we have:[%d] drivers in environment [%s] and version [%d]\n", ndrivers, architecture, version));
+
+ if(ndrivers == -1)
+ return WERR_NOMEM;
+
+ if(ndrivers != 0) {
+ if((tdi1=(DRIVER_INFO_1 *)Realloc(driver_info_1, (*returned+ndrivers) * sizeof(DRIVER_INFO_1))) == NULL) {
+ DEBUG(0,("enumprinterdrivers_level1: failed to enlarge driver info buffer!\n"));
+ SAFE_FREE(driver_info_1);
+ SAFE_FREE(list);
+ return WERR_NOMEM;
+ }
+ else driver_info_1 = tdi1;
+ }
+
+ for (i=0; i<ndrivers; i++) {
+ WERROR status;
+ DEBUGADD(5,("\tdriver: [%s]\n", list[i]));
+ ZERO_STRUCT(driver);
+ status = get_a_printer_driver(&driver, 3, list[i],
+ architecture, version);
+ if (!W_ERROR_IS_OK(status)) {
+ SAFE_FREE(list);
+ return status;
+ }
+ fill_printer_driver_info_1(&driver_info_1[*returned+i], driver, servername, architecture );
+ free_a_printer_driver(driver, 3);
+ }
+
+ *returned+=ndrivers;
+ SAFE_FREE(list);
+ }
+
+ /* check the required size. */
+ for (i=0; i<*returned; i++) {
+ DEBUGADD(6,("adding driver [%d]'s size\n",i));
+ *needed += spoolss_size_printer_driver_info_1(&driver_info_1[i]);
+ }
+
+ if (!alloc_buffer_size(buffer, *needed)) {
+ SAFE_FREE(driver_info_1);
+ return WERR_INSUFFICIENT_BUFFER;
+ }
+
+ /* fill the buffer with the driver structures */
+ for (i=0; i<*returned; i++) {
+ DEBUGADD(6,("adding driver [%d] to buffer\n",i));
+ smb_io_printer_driver_info_1("", buffer, &driver_info_1[i], 0);
+ }
+
+ SAFE_FREE(driver_info_1);
+
+ if (*needed > offered) {
+ *returned=0;
+ return WERR_INSUFFICIENT_BUFFER;
+ }
+
+ return WERR_OK;
+}
+
+/****************************************************************************
+ Enumerates all printer drivers at level 2.
+****************************************************************************/
+
+static WERROR enumprinterdrivers_level2(fstring servername, fstring architecture, NEW_BUFFER *buffer, uint32 offered, uint32 *needed, uint32 *returned)
+{
+ int i;
+ int ndrivers;
+ uint32 version;
+ fstring *list = NULL;
+
+ NT_PRINTER_DRIVER_INFO_LEVEL driver;
+ DRIVER_INFO_2 *tdi2, *driver_info_2=NULL;
+
+ *returned=0;
+
+ for (version=0; version<DRIVER_MAX_VERSION; version++) {
+ list=NULL;
+ ndrivers=get_ntdrivers(&list, architecture, version);
+ DEBUGADD(4,("we have:[%d] drivers in environment [%s] and version [%d]\n", ndrivers, architecture, version));
+
+ if(ndrivers == -1)
+ return WERR_NOMEM;
+
+ if(ndrivers != 0) {
+ if((tdi2=(DRIVER_INFO_2 *)Realloc(driver_info_2, (*returned+ndrivers) * sizeof(DRIVER_INFO_2))) == NULL) {
+ DEBUG(0,("enumprinterdrivers_level2: failed to enlarge driver info buffer!\n"));
+ SAFE_FREE(driver_info_2);
+ SAFE_FREE(list);
+ return WERR_NOMEM;
+ }
+ else driver_info_2 = tdi2;
+ }
+
+ for (i=0; i<ndrivers; i++) {
+ WERROR status;
+
+ DEBUGADD(5,("\tdriver: [%s]\n", list[i]));
+ ZERO_STRUCT(driver);
+ status = get_a_printer_driver(&driver, 3, list[i],
+ architecture, version);
+ if (!W_ERROR_IS_OK(status)) {
+ SAFE_FREE(list);
+ return status;
+ }
+ fill_printer_driver_info_2(&driver_info_2[*returned+i], driver, servername);
+ free_a_printer_driver(driver, 3);
+ }
+
+ *returned+=ndrivers;
+ SAFE_FREE(list);
+ }
+
+ /* check the required size. */
+ for (i=0; i<*returned; i++) {
+ DEBUGADD(6,("adding driver [%d]'s size\n",i));
+ *needed += spoolss_size_printer_driver_info_2(&(driver_info_2[i]));
+ }
+
+ if (!alloc_buffer_size(buffer, *needed)) {
+ SAFE_FREE(driver_info_2);
+ return WERR_INSUFFICIENT_BUFFER;
+ }
+
+ /* fill the buffer with the form structures */
+ for (i=0; i<*returned; i++) {
+ DEBUGADD(6,("adding driver [%d] to buffer\n",i));
+ smb_io_printer_driver_info_2("", buffer, &(driver_info_2[i]), 0);
+ }
+
+ SAFE_FREE(driver_info_2);
+
+ if (*needed > offered) {
+ *returned=0;
+ return WERR_INSUFFICIENT_BUFFER;
+ }
+
+ return WERR_OK;
+}
+
+/****************************************************************************
+ Enumerates all printer drivers at level 3.
+****************************************************************************/
+
+static WERROR enumprinterdrivers_level3(fstring servername, fstring architecture, NEW_BUFFER *buffer, uint32 offered, uint32 *needed, uint32 *returned)
+{
+ int i;
+ int ndrivers;
+ uint32 version;
+ fstring *list = NULL;
+
+ NT_PRINTER_DRIVER_INFO_LEVEL driver;
+ DRIVER_INFO_3 *tdi3, *driver_info_3=NULL;
+
+ *returned=0;
+
+ for (version=0; version<DRIVER_MAX_VERSION; version++) {
+ list=NULL;
+ ndrivers=get_ntdrivers(&list, architecture, version);
+ DEBUGADD(4,("we have:[%d] drivers in environment [%s] and version [%d]\n", ndrivers, architecture, version));
+
+ if(ndrivers == -1)
+ return WERR_NOMEM;
+
+ if(ndrivers != 0) {
+ if((tdi3=(DRIVER_INFO_3 *)Realloc(driver_info_3, (*returned+ndrivers) * sizeof(DRIVER_INFO_3))) == NULL) {
+ DEBUG(0,("enumprinterdrivers_level3: failed to enlarge driver info buffer!\n"));
+ SAFE_FREE(driver_info_3);
+ SAFE_FREE(list);
+ return WERR_NOMEM;
+ }
+ else driver_info_3 = tdi3;
+ }
+
+ for (i=0; i<ndrivers; i++) {
+ WERROR status;
+
+ DEBUGADD(5,("\tdriver: [%s]\n", list[i]));
+ ZERO_STRUCT(driver);
+ status = get_a_printer_driver(&driver, 3, list[i],
+ architecture, version);
+ if (!W_ERROR_IS_OK(status)) {
+ SAFE_FREE(list);
+ return status;
+ }
+ fill_printer_driver_info_3(&driver_info_3[*returned+i], driver, servername);
+ free_a_printer_driver(driver, 3);
+ }
+
+ *returned+=ndrivers;
+ SAFE_FREE(list);
+ }
+
+ /* check the required size. */
+ for (i=0; i<*returned; i++) {
+ DEBUGADD(6,("adding driver [%d]'s size\n",i));
+ *needed += spoolss_size_printer_driver_info_3(&driver_info_3[i]);
+ }
+
+ if (!alloc_buffer_size(buffer, *needed)) {
+ SAFE_FREE(driver_info_3);
+ return WERR_INSUFFICIENT_BUFFER;
+ }
+
+ /* fill the buffer with the driver structures */
+ for (i=0; i<*returned; i++) {
+ DEBUGADD(6,("adding driver [%d] to buffer\n",i));
+ smb_io_printer_driver_info_3("", buffer, &driver_info_3[i], 0);
+ }
+
+ for (i=0; i<*returned; i++)
+ SAFE_FREE(driver_info_3[i].dependentfiles);
+
+ SAFE_FREE(driver_info_3);
+
+ if (*needed > offered) {
+ *returned=0;
+ return WERR_INSUFFICIENT_BUFFER;
+ }
+
+ return WERR_OK;
+}
+
+/****************************************************************************
+ Enumerates all printer drivers.
+****************************************************************************/
+
+WERROR _spoolss_enumprinterdrivers( pipes_struct *p, SPOOL_Q_ENUMPRINTERDRIVERS *q_u, SPOOL_R_ENUMPRINTERDRIVERS *r_u)
+{
+ UNISTR2 *environment = &q_u->environment;
+ uint32 level = q_u->level;
+ NEW_BUFFER *buffer = NULL;
+ uint32 offered = q_u->offered;
+ uint32 *needed = &r_u->needed;
+ uint32 *returned = &r_u->returned;
+
+ fstring *list = NULL;
+ fstring servername;
+ fstring architecture;
+
+ /* that's an [in out] buffer */
+ spoolss_move_buffer(q_u->buffer, &r_u->buffer);
+ buffer = r_u->buffer;
+
+ DEBUG(4,("_spoolss_enumprinterdrivers\n"));
+ fstrcpy(servername, get_called_name());
+ *needed=0;
+ *returned=0;
+
+ unistr2_to_ascii(architecture, environment, sizeof(architecture)-1);
+
+ switch (level) {
+ case 1:
+ return enumprinterdrivers_level1(servername, architecture, buffer, offered, needed, returned);
+ case 2:
+ return enumprinterdrivers_level2(servername, architecture, buffer, offered, needed, returned);
+ case 3:
+ return enumprinterdrivers_level3(servername, architecture, buffer, offered, needed, returned);
+ default:
+ *returned=0;
+ SAFE_FREE(list);
+ return WERR_UNKNOWN_LEVEL;
+ }
+}
+
+/****************************************************************************
+****************************************************************************/
+
+static void fill_form_1(FORM_1 *form, nt_forms_struct *list)
+{
+ form->flag=list->flag;
+ init_unistr(&form->name, list->name);
+ form->width=list->width;
+ form->length=list->length;
+ form->left=list->left;
+ form->top=list->top;
+ form->right=list->right;
+ form->bottom=list->bottom;
+}
+
+/****************************************************************************
+****************************************************************************/
+
+WERROR _spoolss_enumforms(pipes_struct *p, SPOOL_Q_ENUMFORMS *q_u, SPOOL_R_ENUMFORMS *r_u)
+{
+ uint32 level = q_u->level;
+ NEW_BUFFER *buffer = NULL;
+ uint32 offered = q_u->offered;
+ uint32 *needed = &r_u->needed;
+ uint32 *numofforms = &r_u->numofforms;
+ uint32 numbuiltinforms;
+
+ nt_forms_struct *list=NULL;
+ nt_forms_struct *builtinlist=NULL;
+ FORM_1 *forms_1;
+ int buffer_size=0;
+ int i;
+
+ /* that's an [in out] buffer */
+ spoolss_move_buffer(q_u->buffer, &r_u->buffer);
+ buffer = r_u->buffer;
+
+ DEBUG(4,("_spoolss_enumforms\n"));
+ DEBUGADD(5,("Offered buffer size [%d]\n", offered));
+ DEBUGADD(5,("Info level [%d]\n", level));
+
+ numbuiltinforms = get_builtin_ntforms(&builtinlist);
+ DEBUGADD(5,("Number of builtin forms [%d]\n", numbuiltinforms));
+ *numofforms = get_ntforms(&list);
+ DEBUGADD(5,("Number of user forms [%d]\n", *numofforms));
+ *numofforms += numbuiltinforms;
+
+ if (*numofforms == 0) return WERR_NO_MORE_ITEMS;
+
+ switch (level) {
+ case 1:
+ if ((forms_1=(FORM_1 *)malloc(*numofforms * sizeof(FORM_1))) == NULL) {
+ *numofforms=0;
+ return WERR_NOMEM;
+ }
+
+ /* construct the list of form structures */
+ for (i=0; i<numbuiltinforms; i++) {
+ DEBUGADD(6,("Filling form number [%d]\n",i));
+ fill_form_1(&forms_1[i], &builtinlist[i]);
+ }
+
+ SAFE_FREE(builtinlist);
+
+ for (; i<*numofforms; i++) {
+ DEBUGADD(6,("Filling form number [%d]\n",i));
+ fill_form_1(&forms_1[i], &list[i-numbuiltinforms]);
+ }
+
+ SAFE_FREE(list);
+
+ /* check the required size. */
+ for (i=0; i<numbuiltinforms; i++) {
+ DEBUGADD(6,("adding form [%d]'s size\n",i));
+ buffer_size += spoolss_size_form_1(&forms_1[i]);
+ }
+ for (; i<*numofforms; i++) {
+ DEBUGADD(6,("adding form [%d]'s size\n",i));
+ buffer_size += spoolss_size_form_1(&forms_1[i]);
+ }
+
+ *needed=buffer_size;
+
+ if (!alloc_buffer_size(buffer, buffer_size)){
+ SAFE_FREE(forms_1);
+ return WERR_INSUFFICIENT_BUFFER;
+ }
+
+ /* fill the buffer with the form structures */
+ for (i=0; i<numbuiltinforms; i++) {
+ DEBUGADD(6,("adding form [%d] to buffer\n",i));
+ smb_io_form_1("", buffer, &forms_1[i], 0);
+ }
+ for (; i<*numofforms; i++) {
+ DEBUGADD(6,("adding form [%d] to buffer\n",i));
+ smb_io_form_1("", buffer, &forms_1[i], 0);
+ }
+
+ SAFE_FREE(forms_1);
+
+ if (*needed > offered) {
+ *numofforms=0;
+ return WERR_INSUFFICIENT_BUFFER;
+ }
+ else
+ return WERR_OK;
+
+ default:
+ SAFE_FREE(list);
+ SAFE_FREE(builtinlist);
+ return WERR_UNKNOWN_LEVEL;
+ }
+
+}
+
+/****************************************************************************
+****************************************************************************/
+
+WERROR _spoolss_getform(pipes_struct *p, SPOOL_Q_GETFORM *q_u, SPOOL_R_GETFORM *r_u)
+{
+ uint32 level = q_u->level;
+ UNISTR2 *uni_formname = &q_u->formname;
+ NEW_BUFFER *buffer = NULL;
+ uint32 offered = q_u->offered;
+ uint32 *needed = &r_u->needed;
+
+ nt_forms_struct *list=NULL;
+ nt_forms_struct builtin_form;
+ BOOL foundBuiltin;
+ FORM_1 form_1;
+ fstring form_name;
+ int buffer_size=0;
+ int numofforms=0, i=0;
+
+ /* that's an [in out] buffer */
+ spoolss_move_buffer(q_u->buffer, &r_u->buffer);
+ buffer = r_u->buffer;
+
+ unistr2_to_ascii(form_name, uni_formname, sizeof(form_name)-1);
+
+ DEBUG(4,("_spoolss_getform\n"));
+ DEBUGADD(5,("Offered buffer size [%d]\n", offered));
+ DEBUGADD(5,("Info level [%d]\n", level));
+
+ foundBuiltin = get_a_builtin_ntform(uni_formname,&builtin_form);
+ if (!foundBuiltin) {
+ numofforms = get_ntforms(&list);
+ DEBUGADD(5,("Number of forms [%d]\n", numofforms));
+
+ if (numofforms == 0)
+ return WERR_BADFID;
+ }
+
+ switch (level) {
+ case 1:
+ if (foundBuiltin) {
+ fill_form_1(&form_1, &builtin_form);
+ } else {
+
+ /* Check if the requested name is in the list of form structures */
+ for (i=0; i<numofforms; i++) {
+
+ DEBUG(4,("_spoolss_getform: checking form %s (want %s)\n", list[i].name, form_name));
+
+ if (strequal(form_name, list[i].name)) {
+ DEBUGADD(6,("Found form %s number [%d]\n", form_name, i));
+ fill_form_1(&form_1, &list[i]);
+ break;
+ }
+ }
+
+ SAFE_FREE(list);
+ if (i == numofforms) {
+ return WERR_BADFID;
+ }
+ }
+ /* check the required size. */
+
+ *needed=spoolss_size_form_1(&form_1);
+
+ if (!alloc_buffer_size(buffer, buffer_size)){
+ return WERR_INSUFFICIENT_BUFFER;
+ }
+
+ if (*needed > offered) {
+ return WERR_INSUFFICIENT_BUFFER;
+ }
+
+ /* fill the buffer with the form structures */
+ DEBUGADD(6,("adding form %s [%d] to buffer\n", form_name, i));
+ smb_io_form_1("", buffer, &form_1, 0);
+
+ return WERR_OK;
+
+ default:
+ SAFE_FREE(list);
+ return WERR_UNKNOWN_LEVEL;
+ }
+}
+
+/****************************************************************************
+****************************************************************************/
+
+static void fill_port_1(PORT_INFO_1 *port, const char *name)
+{
+ init_unistr(&port->port_name, name);
+}
+
+/****************************************************************************
+****************************************************************************/
+
+static void fill_port_2(PORT_INFO_2 *port, const char *name)
+{
+ init_unistr(&port->port_name, name);
+ init_unistr(&port->monitor_name, "Local Monitor");
+ init_unistr(&port->description, "Local Port");
+ port->port_type=PORT_TYPE_WRITE;
+ port->reserved=0x0;
+}
+
+/****************************************************************************
+ enumports level 1.
+****************************************************************************/
+
+static WERROR enumports_level_1(NEW_BUFFER *buffer, uint32 offered, uint32 *needed, uint32 *returned)
+{
+ PORT_INFO_1 *ports=NULL;
+ int i=0;
+
+ if (*lp_enumports_cmd()) {
+ char *cmd = lp_enumports_cmd();
+ char **qlines;
+ pstring command;
+ int numlines;
+ int ret;
+ int fd;
+
+ slprintf(command, sizeof(command)-1, "%s \"%d\"", cmd, 1);
+
+ DEBUG(10,("Running [%s]\n", command));
+ ret = smbrun(command, &fd);
+ DEBUG(10,("Returned [%d]\n", ret));
+ if (ret != 0) {
+ if (fd != -1)
+ close(fd);
+ /* Is this the best error to return here? */
+ return WERR_ACCESS_DENIED;
+ }
+
+ numlines = 0;
+ qlines = fd_lines_load(fd, &numlines);
+ DEBUGADD(10,("Lines returned = [%d]\n", numlines));
+ close(fd);
+
+ if(numlines) {
+ if((ports=(PORT_INFO_1 *)malloc( numlines * sizeof(PORT_INFO_1) )) == NULL) {
+ DEBUG(10,("Returning WERR_NOMEM [%s]\n",
+ dos_errstr(WERR_NOMEM)));
+ file_lines_free(qlines);
+ return WERR_NOMEM;
+ }
+
+ for (i=0; i<numlines; i++) {
+ DEBUG(6,("Filling port number [%d] with port [%s]\n", i, qlines[i]));
+ fill_port_1(&ports[i], qlines[i]);
+ }
+
+ file_lines_free(qlines);
+ }
+
+ *returned = numlines;
+
+ } else {
+ *returned = 1; /* Sole Samba port returned. */
+
+ if((ports=(PORT_INFO_1 *)malloc( sizeof(PORT_INFO_1) )) == NULL)
+ return WERR_NOMEM;
+
+ DEBUG(10,("enumports_level_1: port name %s\n", SAMBA_PRINTER_PORT_NAME));
+
+ fill_port_1(&ports[0], SAMBA_PRINTER_PORT_NAME);
+ }
+
+ /* check the required size. */
+ for (i=0; i<*returned; i++) {
+ DEBUGADD(6,("adding port [%d]'s size\n", i));
+ *needed += spoolss_size_port_info_1(&ports[i]);
+ }
+
+ if (!alloc_buffer_size(buffer, *needed)) {
+ SAFE_FREE(ports);
+ return WERR_INSUFFICIENT_BUFFER;
+ }
+
+ /* fill the buffer with the ports structures */
+ for (i=0; i<*returned; i++) {
+ DEBUGADD(6,("adding port [%d] to buffer\n", i));
+ smb_io_port_1("", buffer, &ports[i], 0);
+ }
+
+ SAFE_FREE(ports);
+
+ if (*needed > offered) {
+ *returned=0;
+ return WERR_INSUFFICIENT_BUFFER;
+ }
+
+ return WERR_OK;
+}
+
+/****************************************************************************
+ enumports level 2.
+****************************************************************************/
+
+static WERROR enumports_level_2(NEW_BUFFER *buffer, uint32 offered, uint32 *needed, uint32 *returned)
+{
+ PORT_INFO_2 *ports=NULL;
+ int i=0;
+
+ if (*lp_enumports_cmd()) {
+ char *cmd = lp_enumports_cmd();
+ char *path;
+ char **qlines;
+ pstring tmp_file;
+ pstring command;
+ int numlines;
+ int ret;
+ int fd;
+
+ if (*lp_pathname(lp_servicenumber(PRINTERS_NAME)))
+ path = lp_pathname(lp_servicenumber(PRINTERS_NAME));
+ else
+ path = lp_lockdir();
+
+ slprintf(tmp_file, sizeof(tmp_file)-1, "%s/smbcmd.%u.", path, (unsigned int)sys_getpid());
+ slprintf(command, sizeof(command)-1, "%s \"%d\"", cmd, 2);
+
+ unlink(tmp_file);
+ DEBUG(10,("Running [%s > %s]\n", command,tmp_file));
+ ret = smbrun(command, &fd);
+ DEBUGADD(10,("returned [%d]\n", ret));
+ if (ret != 0) {
+ if (fd != -1)
+ close(fd);
+ /* Is this the best error to return here? */
+ return WERR_ACCESS_DENIED;
+ }
+
+ numlines = 0;
+ qlines = fd_lines_load(fd, &numlines);
+ DEBUGADD(10,("Lines returned = [%d]\n", numlines));
+ close(fd);
+
+ if(numlines) {
+ if((ports=(PORT_INFO_2 *)malloc( numlines * sizeof(PORT_INFO_2) )) == NULL) {
+ file_lines_free(qlines);
+ return WERR_NOMEM;
+ }
+
+ for (i=0; i<numlines; i++) {
+ DEBUG(6,("Filling port number [%d] with port [%s]\n", i, qlines[i]));
+ fill_port_2(&(ports[i]), qlines[i]);
+ }
+
+ file_lines_free(qlines);
+ }
+
+ *returned = numlines;
+
+ } else {
+
+ *returned = 1;
+
+ if((ports=(PORT_INFO_2 *)malloc( sizeof(PORT_INFO_2) )) == NULL)
+ return WERR_NOMEM;
+
+ DEBUG(10,("enumports_level_2: port name %s\n", SAMBA_PRINTER_PORT_NAME));
+
+ fill_port_2(&ports[0], SAMBA_PRINTER_PORT_NAME);
+ }
+
+ /* check the required size. */
+ for (i=0; i<*returned; i++) {
+ DEBUGADD(6,("adding port [%d]'s size\n", i));
+ *needed += spoolss_size_port_info_2(&ports[i]);
+ }
+
+ if (!alloc_buffer_size(buffer, *needed)) {
+ SAFE_FREE(ports);
+ return WERR_INSUFFICIENT_BUFFER;
+ }
+
+ /* fill the buffer with the ports structures */
+ for (i=0; i<*returned; i++) {
+ DEBUGADD(6,("adding port [%d] to buffer\n", i));
+ smb_io_port_2("", buffer, &ports[i], 0);
+ }
+
+ SAFE_FREE(ports);
+
+ if (*needed > offered) {
+ *returned=0;
+ return WERR_INSUFFICIENT_BUFFER;
+ }
+
+ return WERR_OK;
+}
+
+/****************************************************************************
+ enumports.
+****************************************************************************/
+
+WERROR _spoolss_enumports( pipes_struct *p, SPOOL_Q_ENUMPORTS *q_u, SPOOL_R_ENUMPORTS *r_u)
+{
+ uint32 level = q_u->level;
+ NEW_BUFFER *buffer = NULL;
+ uint32 offered = q_u->offered;
+ uint32 *needed = &r_u->needed;
+ uint32 *returned = &r_u->returned;
+
+ /* that's an [in out] buffer */
+ spoolss_move_buffer(q_u->buffer, &r_u->buffer);
+ buffer = r_u->buffer;
+
+ DEBUG(4,("_spoolss_enumports\n"));
+
+ *returned=0;
+ *needed=0;
+
+ switch (level) {
+ case 1:
+ return enumports_level_1(buffer, offered, needed, returned);
+ case 2:
+ return enumports_level_2(buffer, offered, needed, returned);
+ default:
+ return WERR_UNKNOWN_LEVEL;
+ }
+}
+
+/****************************************************************************
+****************************************************************************/
+
+static WERROR spoolss_addprinterex_level_2( pipes_struct *p, const UNISTR2 *uni_srv_name,
+ const SPOOL_PRINTER_INFO_LEVEL *info,
+ DEVICEMODE *devmode, SEC_DESC_BUF *sec_desc_buf,
+ uint32 user_switch, const SPOOL_USER_CTR *user,
+ POLICY_HND *handle)
+{
+ NT_PRINTER_INFO_LEVEL *printer = NULL;
+ fstring name;
+ int snum;
+ WERROR err = WERR_OK;
+
+ if ((printer = (NT_PRINTER_INFO_LEVEL *)malloc(sizeof(NT_PRINTER_INFO_LEVEL))) == NULL) {
+ DEBUG(0,("spoolss_addprinterex_level_2: malloc fail.\n"));
+ return WERR_NOMEM;
+ }
+
+ ZERO_STRUCTP(printer);
+
+ /* convert from UNICODE to ASCII - this allocates the info_2 struct inside *printer.*/
+ if (!convert_printer_info(info, printer, 2)) {
+ free_a_printer(&printer, 2);
+ return WERR_NOMEM;
+ }
+
+ /* check to see if the printer already exists */
+
+ if ((snum = print_queue_snum(printer->info_2->sharename)) != -1) {
+ DEBUG(5, ("_spoolss_addprinterex: Attempted to add a printer named [%s] when one already existed!\n",
+ printer->info_2->sharename));
+ free_a_printer(&printer, 2);
+ return WERR_PRINTER_ALREADY_EXISTS;
+ }
+
+ /* FIXME!!! smbd should check to see if the driver is installed before
+ trying to add a printer like this --jerry */
+
+ if (*lp_addprinter_cmd() ) {
+ if ( !add_printer_hook(printer) ) {
+ free_a_printer(&printer,2);
+ return WERR_ACCESS_DENIED;
+ }
+ }
+
+ slprintf(name, sizeof(name)-1, "\\\\%s\\%s", get_called_name(),
+ printer->info_2->sharename);
+
+
+ if ((snum = print_queue_snum(printer->info_2->sharename)) == -1) {
+ free_a_printer(&printer,2);
+ return WERR_ACCESS_DENIED;
+ }
+
+ /* you must be a printer admin to add a new printer */
+ if (!print_access_check(NULL, snum, PRINTER_ACCESS_ADMINISTER)) {
+ free_a_printer(&printer,2);
+ return WERR_ACCESS_DENIED;
+ }
+
+ /*
+ * Do sanity check on the requested changes for Samba.
+ */
+
+ if (!check_printer_ok(printer->info_2, snum)) {
+ free_a_printer(&printer,2);
+ return WERR_INVALID_PARAM;
+ }
+
+ /*
+ * When a printer is created, the drivername bound to the printer is used
+ * to lookup previously saved driver initialization info, which is then
+ * bound to the new printer, simulating what happens in the Windows arch.
+ */
+
+ if (!devmode)
+ {
+ set_driver_init(printer, 2);
+ }
+ else
+ {
+ /* A valid devmode was included, convert and link it
+ */
+ DEBUGADD(10, ("spoolss_addprinterex_level_2: devmode included, converting\n"));
+
+ if (!convert_devicemode(printer->info_2->printername, devmode,
+ &printer->info_2->devmode))
+ return WERR_NOMEM;
+ }
+
+ /* write the ASCII on disk */
+ err = mod_a_printer(*printer, 2);
+ if (!W_ERROR_IS_OK(err)) {
+ free_a_printer(&printer,2);
+ return err;
+ }
+
+ if (!open_printer_hnd(p, handle, name, PRINTER_ACCESS_ADMINISTER)) {
+ /* Handle open failed - remove addition. */
+ del_a_printer(printer->info_2->sharename);
+ free_a_printer(&printer,2);
+ return WERR_ACCESS_DENIED;
+ }
+
+ update_c_setprinter(False);
+ free_a_printer(&printer,2);
+
+ return WERR_OK;
+}
+
+/****************************************************************************
+****************************************************************************/
+
+WERROR _spoolss_addprinterex( pipes_struct *p, SPOOL_Q_ADDPRINTEREX *q_u, SPOOL_R_ADDPRINTEREX *r_u)
+{
+ UNISTR2 *uni_srv_name = &q_u->server_name;
+ uint32 level = q_u->level;
+ SPOOL_PRINTER_INFO_LEVEL *info = &q_u->info;
+ DEVICEMODE *devmode = q_u->devmode_ctr.devmode;
+ SEC_DESC_BUF *sdb = q_u->secdesc_ctr;
+ uint32 user_switch = q_u->user_switch;
+ SPOOL_USER_CTR *user = &q_u->user_ctr;
+ POLICY_HND *handle = &r_u->handle;
+
+ switch (level) {
+ case 1:
+ /* we don't handle yet */
+ /* but I know what to do ... */
+ return WERR_UNKNOWN_LEVEL;
+ case 2:
+ return spoolss_addprinterex_level_2(p, uni_srv_name, info,
+ devmode, sdb,
+ user_switch, user, handle);
+ default:
+ return WERR_UNKNOWN_LEVEL;
+ }
+}
+
+/****************************************************************************
+****************************************************************************/
+
+WERROR _spoolss_addprinterdriver(pipes_struct *p, SPOOL_Q_ADDPRINTERDRIVER *q_u, SPOOL_R_ADDPRINTERDRIVER *r_u)
+{
+ uint32 level = q_u->level;
+ SPOOL_PRINTER_DRIVER_INFO_LEVEL *info = &q_u->info;
+ WERROR err = WERR_OK;
+ NT_PRINTER_DRIVER_INFO_LEVEL driver;
+ struct current_user user;
+ fstring driver_name;
+ uint32 version;
+
+ ZERO_STRUCT(driver);
+
+ get_current_user(&user, p);
+
+ if (!convert_printer_driver_info(info, &driver, level)) {
+ err = WERR_NOMEM;
+ goto done;
+ }
+
+ DEBUG(5,("Cleaning driver's information\n"));
+ err = clean_up_driver_struct(driver, level, &user);
+ if (!W_ERROR_IS_OK(err))
+ goto done;
+
+ DEBUG(5,("Moving driver to final destination\n"));
+ if(!move_driver_to_download_area(driver, level, &user, &err)) {
+ if (W_ERROR_IS_OK(err))
+ err = WERR_ACCESS_DENIED;
+ goto done;
+ }
+
+ if (add_a_printer_driver(driver, level)!=0) {
+ err = WERR_ACCESS_DENIED;
+ goto done;
+ }
+
+ /* BEGIN_ADMIN_LOG */
+ switch(level) {
+ case 3:
+ fstrcpy(driver_name, driver.info_3->name ? driver.info_3->name : "");
+ sys_adminlog(LOG_INFO,"Added printer driver. Print driver name: %s. Print driver OS: %s. Administrator name: %s.",
+ driver_name, get_drv_ver_to_os(driver.info_3->cversion),uidtoname(user.uid));
+ break;
+ case 6:
+ fstrcpy(driver_name, driver.info_6->name ? driver.info_6->name : "");
+ sys_adminlog(LOG_INFO,"Added printer driver. Print driver name: %s. Print driver OS: %s. Administrator name: %s.",
+ driver_name, get_drv_ver_to_os(driver.info_6->version),uidtoname(user.uid));
+ break;
+ }
+ /* END_ADMIN_LOG */
+
+ /*
+ * I think this is where he DrvUpgradePrinter() hook would be
+ * be called in a driver's interface DLL on a Windows NT 4.0/2k
+ * server. Right now, we just need to send ourselves a message
+ * to update each printer bound to this driver. --jerry
+ */
+
+ if (!srv_spoolss_drv_upgrade_printer(driver_name)) {
+ DEBUG(0,("_spoolss_addprinterdriver: Failed to send message about upgrading driver [%s]!\n",
+ driver_name));
+ }
+
+ /*
+ * Based on the version (e.g. driver destination dir: 0=9x,2=Nt/2k,3=2k/Xp),
+ * decide if the driver init data should be deleted. The rules are:
+ * 1) never delete init data if it is a 9x driver, they don't use it anyway
+ * 2) delete init data only if there is no 2k/Xp driver
+ * 3) always delete init data
+ * The generalized rule is always use init data from the highest order driver.
+ * It is necessary to follow the driver install by an initialization step to
+ * finish off this process.
+ */
+ if (level == 3)
+ version = driver.info_3->cversion;
+ else if (level == 6)
+ version = driver.info_6->version;
+ else
+ version = -1;
+ switch (version) {
+ /*
+ * 9x printer driver - never delete init data
+ */
+ case 0:
+ DEBUG(10,("_spoolss_addprinterdriver: init data not deleted for 9x driver [%s]\n",
+ driver_name));
+ break;
+
+ /*
+ * Nt or 2k (compatiblity mode) printer driver - only delete init data if
+ * there is no 2k/Xp driver init data for this driver name.
+ */
+ case 2:
+ {
+ NT_PRINTER_DRIVER_INFO_LEVEL driver1;
+
+ if (!W_ERROR_IS_OK(get_a_printer_driver(&driver1, 3, driver_name, "Windows NT x86", 3))) {
+ /*
+ * No 2k/Xp driver found, delete init data (if any) for the new Nt driver.
+ */
+ if (!del_driver_init(driver_name))
+ DEBUG(6,("_spoolss_addprinterdriver: del_driver_init(%s) Nt failed!\n", driver_name));
+ } else {
+ /*
+ * a 2k/Xp driver was found, don't delete init data because Nt driver will use it.
+ */
+ free_a_printer_driver(driver1,3);
+ DEBUG(10,("_spoolss_addprinterdriver: init data not deleted for Nt driver [%s]\n",
+ driver_name));
+ }
+ }
+ break;
+
+ /*
+ * 2k or Xp printer driver - always delete init data
+ */
+ case 3:
+ if (!del_driver_init(driver_name))
+ DEBUG(6,("_spoolss_addprinterdriver: del_driver_init(%s) 2k/Xp failed!\n", driver_name));
+ break;
+
+ default:
+ DEBUG(0,("_spoolss_addprinterdriver: invalid level=%d\n", level));
+ break;
+ }
+
+
+done:
+ free_a_printer_driver(driver, level);
+ return err;
+}
+
+/********************************************************************
+ * spoolss_addprinterdriverex
+ ********************************************************************/
+
+WERROR _spoolss_addprinterdriverex(pipes_struct *p, SPOOL_Q_ADDPRINTERDRIVEREX *q_u, SPOOL_R_ADDPRINTERDRIVEREX *r_u)
+{
+ SPOOL_Q_ADDPRINTERDRIVER q_u_local;
+ SPOOL_R_ADDPRINTERDRIVER r_u_local;
+
+ /*
+ * we only support the semantics of AddPrinterDriver()
+ * i.e. only copy files that are newer than existing ones
+ */
+
+ if ( q_u->copy_flags != APD_COPY_NEW_FILES )
+ return WERR_ACCESS_DENIED;
+
+ ZERO_STRUCT(q_u_local);
+ ZERO_STRUCT(r_u_local);
+
+ /* just pass the information off to _spoolss_addprinterdriver() */
+ q_u_local.server_name_ptr = q_u->server_name_ptr;
+ copy_unistr2(&q_u_local.server_name, &q_u->server_name);
+ q_u_local.level = q_u->level;
+ memcpy( &q_u_local.info, &q_u->info, sizeof(SPOOL_PRINTER_DRIVER_INFO_LEVEL) );
+
+ return _spoolss_addprinterdriver( p, &q_u_local, &r_u_local );
+}
+
+/****************************************************************************
+****************************************************************************/
+
+static void fill_driverdir_1(DRIVER_DIRECTORY_1 *info, char *name)
+{
+ init_unistr(&info->name, name);
+}
+
+/****************************************************************************
+****************************************************************************/
+
+static WERROR getprinterdriverdir_level_1(UNISTR2 *name, UNISTR2 *uni_environment, NEW_BUFFER *buffer, uint32 offered, uint32 *needed)
+{
+ pstring path;
+ pstring long_archi;
+ const char *short_archi;
+ DRIVER_DIRECTORY_1 *info=NULL;
+
+ unistr2_to_ascii(long_archi, uni_environment, sizeof(long_archi)-1);
+
+ if (!(short_archi = get_short_archi(long_archi)))
+ return WERR_INVALID_ENVIRONMENT;
+
+ if((info=(DRIVER_DIRECTORY_1 *)malloc(sizeof(DRIVER_DIRECTORY_1))) == NULL)
+ return WERR_NOMEM;
+
+ slprintf(path, sizeof(path)-1, "\\\\%s\\print$\\%s", get_called_name(), short_archi);
+
+ DEBUG(4,("printer driver directory: [%s]\n", path));
+
+ fill_driverdir_1(info, path);
+
+ *needed += spoolss_size_driverdir_info_1(info);
+
+ if (!alloc_buffer_size(buffer, *needed)) {
+ SAFE_FREE(info);
+ return WERR_INSUFFICIENT_BUFFER;
+ }
+
+ smb_io_driverdir_1("", buffer, info, 0);
+
+ SAFE_FREE(info);
+
+ if (*needed > offered)
+ return WERR_INSUFFICIENT_BUFFER;
+
+ return WERR_OK;
+}
+
+/****************************************************************************
+****************************************************************************/
+
+WERROR _spoolss_getprinterdriverdirectory(pipes_struct *p, SPOOL_Q_GETPRINTERDRIVERDIR *q_u, SPOOL_R_GETPRINTERDRIVERDIR *r_u)
+{
+ UNISTR2 *name = &q_u->name;
+ UNISTR2 *uni_environment = &q_u->environment;
+ uint32 level = q_u->level;
+ NEW_BUFFER *buffer = NULL;
+ uint32 offered = q_u->offered;
+ uint32 *needed = &r_u->needed;
+
+ /* that's an [in out] buffer */
+ spoolss_move_buffer(q_u->buffer, &r_u->buffer);
+ buffer = r_u->buffer;
+
+ DEBUG(4,("_spoolss_getprinterdriverdirectory\n"));
+
+ *needed=0;
+
+ switch(level) {
+ case 1:
+ return getprinterdriverdir_level_1(name, uni_environment, buffer, offered, needed);
+ default:
+ return WERR_UNKNOWN_LEVEL;
+ }
+}
+
+/****************************************************************************
+****************************************************************************/
+
+WERROR _spoolss_enumprinterdata(pipes_struct *p, SPOOL_Q_ENUMPRINTERDATA *q_u, SPOOL_R_ENUMPRINTERDATA *r_u)
+{
+ POLICY_HND *handle = &q_u->handle;
+ uint32 idx = q_u->index;
+ uint32 in_value_len = q_u->valuesize;
+ uint32 in_data_len = q_u->datasize;
+ uint32 *out_max_value_len = &r_u->valuesize;
+ uint16 **out_value = &r_u->value;
+ uint32 *out_value_len = &r_u->realvaluesize;
+ uint32 *out_type = &r_u->type;
+ uint32 *out_max_data_len = &r_u->datasize;
+ uint8 **data_out = &r_u->data;
+ uint32 *out_data_len = &r_u->realdatasize;
+
+ NT_PRINTER_INFO_LEVEL *printer = NULL;
+
+ uint32 biggest_valuesize;
+ uint32 biggest_datasize;
+ uint32 data_len;
+ Printer_entry *Printer = find_printer_index_by_hnd(p, handle);
+ int snum;
+ WERROR result;
+ REGISTRY_VALUE *val = NULL;
+ NT_PRINTER_DATA *p_data;
+ int i, key_index, num_values;
+ int name_length;
+
+ ZERO_STRUCT( printer );
+
+ *out_type = 0;
+
+ *out_max_data_len = 0;
+ *data_out = NULL;
+ *out_data_len = 0;
+
+ DEBUG(5,("spoolss_enumprinterdata\n"));
+
+ if (!Printer) {
+ DEBUG(2,("_spoolss_enumprinterdata: Invalid handle (%s:%u:%u).\n", OUR_HANDLE(handle)));
+ return WERR_BADFID;
+ }
+
+ if (!get_printer_snum(p,handle, &snum))
+ return WERR_BADFID;
+
+ result = get_a_printer(Printer, &printer, 2, lp_const_servicename(snum));
+ if (!W_ERROR_IS_OK(result))
+ return result;
+
+ p_data = &printer->info_2->data;
+ key_index = lookup_printerkey( p_data, SPOOL_PRINTERDATA_KEY );
+
+ result = WERR_OK;
+
+ /*
+ * The NT machine wants to know the biggest size of value and data
+ *
+ * cf: MSDN EnumPrinterData remark section
+ */
+
+ if ( !in_value_len && !in_data_len && (key_index != -1) )
+ {
+ DEBUGADD(6,("Activating NT mega-hack to find sizes\n"));
+
+ biggest_valuesize = 0;
+ biggest_datasize = 0;
+
+ num_values = regval_ctr_numvals( &p_data->keys[key_index].values );
+
+ for ( i=0; i<num_values; i++ )
+ {
+ val = regval_ctr_specific_value( &p_data->keys[key_index].values, i );
+
+ name_length = strlen(val->valuename);
+ if ( strlen(val->valuename) > biggest_valuesize )
+ biggest_valuesize = name_length;
+
+ if ( val->size > biggest_datasize )
+ biggest_datasize = val->size;
+
+ DEBUG(6,("current values: [%d], [%d]\n", biggest_valuesize,
+ biggest_datasize));
+ }
+
+ /* the value is an UNICODE string but real_value_size is the length
+ in bytes including the trailing 0 */
+
+ *out_value_len = 2 * (1+biggest_valuesize);
+ *out_data_len = biggest_datasize;
+
+ DEBUG(6,("final values: [%d], [%d]\n", *out_value_len, *out_data_len));
+
+ goto done;
+ }
+
+ /*
+ * the value len is wrong in NT sp3
+ * that's the number of bytes not the number of unicode chars
+ */
+
+ if ( key_index != -1 )
+ val = regval_ctr_specific_value( &p_data->keys[key_index].values, idx );
+
+ if ( !val )
+ {
+
+ /* out_value should default to "" or else NT4 has
+ problems unmarshalling the response */
+
+ *out_max_value_len=(in_value_len/sizeof(uint16));
+
+ if((*out_value=(uint16 *)talloc_zero(p->mem_ctx, in_value_len*sizeof(uint8))) == NULL)
+ {
+ result = WERR_NOMEM;
+ goto done;
+ }
+
+ *out_value_len = (uint32)rpcstr_push((char *)*out_value, "", in_value_len, 0);
+
+ /* the data is counted in bytes */
+
+ *out_max_data_len = in_data_len;
+ *out_data_len = in_data_len;
+
+ /* only allocate when given a non-zero data_len */
+
+ if ( in_data_len && ((*data_out=(uint8 *)talloc_zero(p->mem_ctx, in_data_len*sizeof(uint8))) == NULL) )
+ {
+ result = WERR_NOMEM;
+ goto done;
+ }
+
+ result = WERR_NO_MORE_ITEMS;
+ }
+ else
+ {
+ /*
+ * the value is:
+ * - counted in bytes in the request
+ * - counted in UNICODE chars in the max reply
+ * - counted in bytes in the real size
+ *
+ * take a pause *before* coding not *during* coding
+ */
+
+ /* name */
+ *out_max_value_len=(in_value_len/sizeof(uint16));
+ if ( (*out_value = (uint16 *)talloc_zero(p->mem_ctx, in_value_len*sizeof(uint8))) == NULL )
+ {
+ result = WERR_NOMEM;
+ goto done;
+ }
+
+ *out_value_len = (uint32)rpcstr_push((char *)*out_value, regval_name(val), in_value_len, 0);
+
+ /* type */
+
+ *out_type = regval_type( val );
+
+ /* data - counted in bytes */
+
+ *out_max_data_len = in_data_len;
+ if ( (*data_out = (uint8 *)talloc_zero(p->mem_ctx, in_data_len*sizeof(uint8))) == NULL)
+ {
+ result = WERR_NOMEM;
+ goto done;
+ }
+ data_len = (size_t)regval_size(val);
+ memcpy( *data_out, regval_data_p(val), data_len );
+ *out_data_len = data_len;
+ }
+
+done:
+ free_a_printer(&printer, 2);
+ return result;
+}
+
+/****************************************************************************
+****************************************************************************/
+
+WERROR _spoolss_setprinterdata( pipes_struct *p, SPOOL_Q_SETPRINTERDATA *q_u, SPOOL_R_SETPRINTERDATA *r_u)
+{
+ POLICY_HND *handle = &q_u->handle;
+ UNISTR2 *value = &q_u->value;
+ uint32 type = q_u->type;
+ uint8 *data = q_u->data;
+ uint32 real_len = q_u->real_len;
+
+ NT_PRINTER_INFO_LEVEL *printer = NULL;
+ int snum=0;
+ WERROR status = WERR_OK;
+ Printer_entry *Printer=find_printer_index_by_hnd(p, handle);
+ fstring valuename;
+
+ DEBUG(5,("spoolss_setprinterdata\n"));
+
+ if (!Printer) {
+ DEBUG(2,("_spoolss_setprinterdata: Invalid handle (%s:%u:%u).\n", OUR_HANDLE(handle)));
+ return WERR_BADFID;
+ }
+
+ if ( Printer->printer_type == PRINTER_HANDLE_IS_PRINTSERVER ) {
+ DEBUG(10,("_spoolss_setprinterdata: Not implemented for server handles yet\n"));
+ return WERR_INVALID_PARAM;
+ }
+
+ if (!get_printer_snum(p,handle, &snum))
+ return WERR_BADFID;
+
+ /*
+ * Access check : NT returns "access denied" if you make a
+ * SetPrinterData call without the necessary privildge.
+ * we were originally returning OK if nothing changed
+ * which made Win2k issue **a lot** of SetPrinterData
+ * when connecting to a printer --jerry
+ */
+
+ if (Printer->access_granted != PRINTER_ACCESS_ADMINISTER)
+ {
+ DEBUG(3, ("_spoolss_setprinterdata: change denied by handle access permissions\n"));
+ status = WERR_ACCESS_DENIED;
+ goto done;
+ }
+
+ status = get_a_printer(Printer, &printer, 2, lp_const_servicename(snum));
+ if (!W_ERROR_IS_OK(status))
+ return status;
+
+ unistr2_to_ascii( valuename, value, sizeof(valuename)-1 );
+
+ /*
+ * When client side code sets a magic printer data key, detect it and save
+ * the current printer data and the magic key's data (its the DEVMODE) for
+ * future printer/driver initializations.
+ */
+ if ( (type == REG_BINARY) && strequal( valuename, PHANTOM_DEVMODE_KEY))
+ {
+ /* Set devmode and printer initialization info */
+ status = save_driver_init( printer, 2, data, real_len );
+
+ srv_spoolss_reset_printerdata( printer->info_2->drivername );
+ }
+ else
+ {
+ status = set_printer_dataex( printer, SPOOL_PRINTERDATA_KEY, valuename,
+ type, data, real_len );
+ if ( W_ERROR_IS_OK(status) )
+ status = mod_a_printer(*printer, 2);
+ }
+
+done:
+ free_a_printer(&printer, 2);
+
+ return status;
+}
+
+/****************************************************************************
+****************************************************************************/
+
+WERROR _spoolss_resetprinter(pipes_struct *p, SPOOL_Q_RESETPRINTER *q_u, SPOOL_R_RESETPRINTER *r_u)
+{
+ POLICY_HND *handle = &q_u->handle;
+ Printer_entry *Printer=find_printer_index_by_hnd(p, handle);
+ int snum;
+
+ DEBUG(5,("_spoolss_resetprinter\n"));
+
+ /*
+ * All we do is to check to see if the handle and queue is valid.
+ * This call really doesn't mean anything to us because we only
+ * support RAW printing. --jerry
+ */
+
+ if (!Printer) {
+ DEBUG(2,("_spoolss_resetprinter: Invalid handle (%s:%u:%u).\n", OUR_HANDLE(handle)));
+ return WERR_BADFID;
+ }
+
+ if (!get_printer_snum(p,handle, &snum))
+ return WERR_BADFID;
+
+
+ /* blindly return success */
+ return WERR_OK;
+}
+
+
+/****************************************************************************
+****************************************************************************/
+
+WERROR _spoolss_deleteprinterdata(pipes_struct *p, SPOOL_Q_DELETEPRINTERDATA *q_u, SPOOL_R_DELETEPRINTERDATA *r_u)
+{
+ POLICY_HND *handle = &q_u->handle;
+ UNISTR2 *value = &q_u->valuename;
+
+ NT_PRINTER_INFO_LEVEL *printer = NULL;
+ int snum=0;
+ WERROR status = WERR_OK;
+ Printer_entry *Printer=find_printer_index_by_hnd(p, handle);
+ pstring valuename;
+
+ DEBUG(5,("spoolss_deleteprinterdata\n"));
+
+ if (!Printer) {
+ DEBUG(2,("_spoolss_deleteprinterdata: Invalid handle (%s:%u:%u).\n", OUR_HANDLE(handle)));
+ return WERR_BADFID;
+ }
+
+ if (!get_printer_snum(p, handle, &snum))
+ return WERR_BADFID;
+
+ if (Printer->access_granted != PRINTER_ACCESS_ADMINISTER) {
+ DEBUG(3, ("_spoolss_deleteprinterdata: printer properties change denied by handle\n"));
+ return WERR_ACCESS_DENIED;
+ }
+
+ status = get_a_printer(Printer, &printer, 2, lp_const_servicename(snum));
+ if (!W_ERROR_IS_OK(status))
+ return status;
+
+ unistr2_to_ascii( valuename, value, sizeof(valuename)-1 );
+
+ status = delete_printer_dataex( printer, SPOOL_PRINTERDATA_KEY, valuename );
+
+ if ( W_ERROR_IS_OK(status) )
+ mod_a_printer( *printer, 2 );
+
+ free_a_printer(&printer, 2);
+
+ return status;
+}
+
+/****************************************************************************
+****************************************************************************/
+
+WERROR _spoolss_addform( pipes_struct *p, SPOOL_Q_ADDFORM *q_u, SPOOL_R_ADDFORM *r_u)
+{
+ POLICY_HND *handle = &q_u->handle;
+ FORM *form = &q_u->form;
+ nt_forms_struct tmpForm;
+ int snum;
+ WERROR status = WERR_OK;
+ NT_PRINTER_INFO_LEVEL *printer = NULL;
+
+ int count=0;
+ nt_forms_struct *list=NULL;
+ Printer_entry *Printer = find_printer_index_by_hnd(p, handle);
+
+ DEBUG(5,("spoolss_addform\n"));
+
+ if (!Printer) {
+ DEBUG(2,("_spoolss_addform: Invalid handle (%s:%u:%u).\n", OUR_HANDLE(handle)));
+ return WERR_BADFID;
+ }
+
+
+ /* forms can be added on printer of on the print server handle */
+
+ if ( Printer->printer_type == PRINTER_HANDLE_IS_PRINTER )
+ {
+ if (!get_printer_snum(p,handle, &snum))
+ return WERR_BADFID;
+
+ status = get_a_printer(Printer, &printer, 2, lp_const_servicename(snum));
+ if (!W_ERROR_IS_OK(status))
+ goto done;
+ }
+
+ if ( !(Printer->access_granted & (PRINTER_ACCESS_ADMINISTER|SERVER_ACCESS_ADMINISTER)) ) {
+ DEBUG(2,("_spoolss_addform: denied by handle permissions.\n"));
+ status = WERR_ACCESS_DENIED;
+ goto done;
+ }
+
+ /* can't add if builtin */
+
+ if (get_a_builtin_ntform(&form->name,&tmpForm)) {
+ status = WERR_ALREADY_EXISTS;
+ goto done;
+ }
+
+ count = get_ntforms(&list);
+
+ if(!add_a_form(&list, form, &count)) {
+ status = WERR_NOMEM;
+ goto done;
+ }
+
+ write_ntforms(&list, count);
+
+ /*
+ * ChangeID must always be set if this is a printer
+ */
+
+ if ( Printer->printer_type == PRINTER_HANDLE_IS_PRINTER )
+ status = mod_a_printer(*printer, 2);
+
+done:
+ if ( printer )
+ free_a_printer(&printer, 2);
+ SAFE_FREE(list);
+
+ return status;
+}
+
+/****************************************************************************
+****************************************************************************/
+
+WERROR _spoolss_deleteform( pipes_struct *p, SPOOL_Q_DELETEFORM *q_u, SPOOL_R_DELETEFORM *r_u)
+{
+ POLICY_HND *handle = &q_u->handle;
+ UNISTR2 *form_name = &q_u->name;
+ nt_forms_struct tmpForm;
+ int count=0;
+ nt_forms_struct *list=NULL;
+ Printer_entry *Printer = find_printer_index_by_hnd(p, handle);
+ int snum;
+ WERROR status = WERR_OK;
+ NT_PRINTER_INFO_LEVEL *printer = NULL;
+
+ DEBUG(5,("spoolss_deleteform\n"));
+
+ if (!Printer) {
+ DEBUG(2,("_spoolss_deleteform: Invalid handle (%s:%u:%u).\n", OUR_HANDLE(handle)));
+ return WERR_BADFID;
+ }
+
+ /* forms can be deleted on printer of on the print server handle */
+
+ if ( Printer->printer_type == PRINTER_HANDLE_IS_PRINTER )
+ {
+ if (!get_printer_snum(p,handle, &snum))
+ return WERR_BADFID;
+
+ status = get_a_printer(Printer, &printer, 2, lp_const_servicename(snum));
+ if (!W_ERROR_IS_OK(status))
+ goto done;
+ }
+
+ if ( !(Printer->access_granted & (PRINTER_ACCESS_ADMINISTER|SERVER_ACCESS_ADMINISTER)) ) {
+ DEBUG(2,("_spoolss_deleteform: denied by handle permissions.\n"));
+ status = WERR_ACCESS_DENIED;
+ goto done;
+ }
+
+ /* can't delete if builtin */
+
+ if (get_a_builtin_ntform(form_name,&tmpForm)) {
+ status = WERR_INVALID_PARAM;
+ goto done;
+ }
+
+ count = get_ntforms(&list);
+
+ if ( !delete_a_form(&list, form_name, &count, &status ))
+ goto done;
+
+ /*
+ * ChangeID must always be set if this is a printer
+ */
+
+ if ( Printer->printer_type == PRINTER_HANDLE_IS_PRINTER )
+ status = mod_a_printer(*printer, 2);
+
+done:
+ if ( printer )
+ free_a_printer(&printer, 2);
+ SAFE_FREE(list);
+
+ return status;
+}
+
+/****************************************************************************
+****************************************************************************/
+
+WERROR _spoolss_setform(pipes_struct *p, SPOOL_Q_SETFORM *q_u, SPOOL_R_SETFORM *r_u)
+{
+ POLICY_HND *handle = &q_u->handle;
+ FORM *form = &q_u->form;
+ nt_forms_struct tmpForm;
+ int snum;
+ WERROR status = WERR_OK;
+ NT_PRINTER_INFO_LEVEL *printer = NULL;
+
+ int count=0;
+ nt_forms_struct *list=NULL;
+ Printer_entry *Printer = find_printer_index_by_hnd(p, handle);
+
+ DEBUG(5,("spoolss_setform\n"));
+
+ if (!Printer) {
+ DEBUG(2,("_spoolss_setform: Invalid handle (%s:%u:%u).\n", OUR_HANDLE(handle)));
+ return WERR_BADFID;
+ }
+
+ /* forms can be modified on printer of on the print server handle */
+
+ if ( Printer->printer_type == PRINTER_HANDLE_IS_PRINTER )
+ {
+ if (!get_printer_snum(p,handle, &snum))
+ return WERR_BADFID;
+
+ status = get_a_printer(Printer, &printer, 2, lp_const_servicename(snum));
+ if (!W_ERROR_IS_OK(status))
+ goto done;
+ }
+
+ if ( !(Printer->access_granted & (PRINTER_ACCESS_ADMINISTER|SERVER_ACCESS_ADMINISTER)) ) {
+ DEBUG(2,("_spoolss_setform: denied by handle permissions\n"));
+ status = WERR_ACCESS_DENIED;
+ goto done;
+ }
+
+ /* can't set if builtin */
+ if (get_a_builtin_ntform(&form->name,&tmpForm)) {
+ status = WERR_INVALID_PARAM;
+ goto done;
+ }
+
+ count = get_ntforms(&list);
+ update_a_form(&list, form, count);
+ write_ntforms(&list, count);
+
+ /*
+ * ChangeID must always be set if this is a printer
+ */
+
+ if ( Printer->printer_type == PRINTER_HANDLE_IS_PRINTER )
+ status = mod_a_printer(*printer, 2);
+
+
+done:
+ if ( printer )
+ free_a_printer(&printer, 2);
+ SAFE_FREE(list);
+
+ return status;
+}
+
+/****************************************************************************
+ enumprintprocessors level 1.
+****************************************************************************/
+
+static WERROR enumprintprocessors_level_1(NEW_BUFFER *buffer, uint32 offered, uint32 *needed, uint32 *returned)
+{
+ PRINTPROCESSOR_1 *info_1=NULL;
+
+ if((info_1 = (PRINTPROCESSOR_1 *)malloc(sizeof(PRINTPROCESSOR_1))) == NULL)
+ return WERR_NOMEM;
+
+ (*returned) = 0x1;
+
+ init_unistr(&info_1->name, "winprint");
+
+ *needed += spoolss_size_printprocessor_info_1(info_1);
+
+ if (!alloc_buffer_size(buffer, *needed))
+ return WERR_INSUFFICIENT_BUFFER;
+
+ smb_io_printprocessor_info_1("", buffer, info_1, 0);
+
+ SAFE_FREE(info_1);
+
+ if (*needed > offered) {
+ *returned=0;
+ return WERR_INSUFFICIENT_BUFFER;
+ }
+
+ return WERR_OK;
+}
+
+/****************************************************************************
+****************************************************************************/
+
+WERROR _spoolss_enumprintprocessors(pipes_struct *p, SPOOL_Q_ENUMPRINTPROCESSORS *q_u, SPOOL_R_ENUMPRINTPROCESSORS *r_u)
+{
+ uint32 level = q_u->level;
+ NEW_BUFFER *buffer = NULL;
+ uint32 offered = q_u->offered;
+ uint32 *needed = &r_u->needed;
+ uint32 *returned = &r_u->returned;
+
+ /* that's an [in out] buffer */
+ spoolss_move_buffer(q_u->buffer, &r_u->buffer);
+ buffer = r_u->buffer;
+
+ DEBUG(5,("spoolss_enumprintprocessors\n"));
+
+ /*
+ * Enumerate the print processors ...
+ *
+ * Just reply with "winprint", to keep NT happy
+ * and I can use my nice printer checker.
+ */
+
+ *returned=0;
+ *needed=0;
+
+ switch (level) {
+ case 1:
+ return enumprintprocessors_level_1(buffer, offered, needed, returned);
+ default:
+ return WERR_UNKNOWN_LEVEL;
+ }
+}
+
+/****************************************************************************
+ enumprintprocdatatypes level 1.
+****************************************************************************/
+
+static WERROR enumprintprocdatatypes_level_1(NEW_BUFFER *buffer, uint32 offered, uint32 *needed, uint32 *returned)
+{
+ PRINTPROCDATATYPE_1 *info_1=NULL;
+
+ if((info_1 = (PRINTPROCDATATYPE_1 *)malloc(sizeof(PRINTPROCDATATYPE_1))) == NULL)
+ return WERR_NOMEM;
+
+ (*returned) = 0x1;
+
+ init_unistr(&info_1->name, "RAW");
+
+ *needed += spoolss_size_printprocdatatype_info_1(info_1);
+
+ if (!alloc_buffer_size(buffer, *needed))
+ return WERR_INSUFFICIENT_BUFFER;
+
+ smb_io_printprocdatatype_info_1("", buffer, info_1, 0);
+
+ SAFE_FREE(info_1);
+
+ if (*needed > offered) {
+ *returned=0;
+ return WERR_INSUFFICIENT_BUFFER;
+ }
+
+ return WERR_OK;
+}
+
+/****************************************************************************
+****************************************************************************/
+
+WERROR _spoolss_enumprintprocdatatypes(pipes_struct *p, SPOOL_Q_ENUMPRINTPROCDATATYPES *q_u, SPOOL_R_ENUMPRINTPROCDATATYPES *r_u)
+{
+ uint32 level = q_u->level;
+ NEW_BUFFER *buffer = NULL;
+ uint32 offered = q_u->offered;
+ uint32 *needed = &r_u->needed;
+ uint32 *returned = &r_u->returned;
+
+ /* that's an [in out] buffer */
+ spoolss_move_buffer(q_u->buffer, &r_u->buffer);
+ buffer = r_u->buffer;
+
+ DEBUG(5,("_spoolss_enumprintprocdatatypes\n"));
+
+ *returned=0;
+ *needed=0;
+
+ switch (level) {
+ case 1:
+ return enumprintprocdatatypes_level_1(buffer, offered, needed, returned);
+ default:
+ return WERR_UNKNOWN_LEVEL;
+ }
+}
+
+/****************************************************************************
+ enumprintmonitors level 1.
+****************************************************************************/
+
+static WERROR enumprintmonitors_level_1(NEW_BUFFER *buffer, uint32 offered, uint32 *needed, uint32 *returned)
+{
+ PRINTMONITOR_1 *info_1=NULL;
+
+ if((info_1 = (PRINTMONITOR_1 *)malloc(sizeof(PRINTMONITOR_1))) == NULL)
+ return WERR_NOMEM;
+
+ (*returned) = 0x1;
+
+ init_unistr(&info_1->name, "Local Port");
+
+ *needed += spoolss_size_printmonitor_info_1(info_1);
+
+ if (!alloc_buffer_size(buffer, *needed))
+ return WERR_INSUFFICIENT_BUFFER;
+
+ smb_io_printmonitor_info_1("", buffer, info_1, 0);
+
+ SAFE_FREE(info_1);
+
+ if (*needed > offered) {
+ *returned=0;
+ return WERR_INSUFFICIENT_BUFFER;
+ }
+
+ return WERR_OK;
+}
+
+/****************************************************************************
+ enumprintmonitors level 2.
+****************************************************************************/
+
+static WERROR enumprintmonitors_level_2(NEW_BUFFER *buffer, uint32 offered, uint32 *needed, uint32 *returned)
+{
+ PRINTMONITOR_2 *info_2=NULL;
+
+ if((info_2 = (PRINTMONITOR_2 *)malloc(sizeof(PRINTMONITOR_2))) == NULL)
+ return WERR_NOMEM;
+
+ (*returned) = 0x1;
+
+ init_unistr(&info_2->name, "Local Port");
+ init_unistr(&info_2->environment, "Windows NT X86");
+ init_unistr(&info_2->dll_name, "localmon.dll");
+
+ *needed += spoolss_size_printmonitor_info_2(info_2);
+
+ if (!alloc_buffer_size(buffer, *needed))
+ return WERR_INSUFFICIENT_BUFFER;
+
+ smb_io_printmonitor_info_2("", buffer, info_2, 0);
+
+ SAFE_FREE(info_2);
+
+ if (*needed > offered) {
+ *returned=0;
+ return WERR_INSUFFICIENT_BUFFER;
+ }
+
+ return WERR_OK;
+}
+
+/****************************************************************************
+****************************************************************************/
+
+WERROR _spoolss_enumprintmonitors(pipes_struct *p, SPOOL_Q_ENUMPRINTMONITORS *q_u, SPOOL_R_ENUMPRINTMONITORS *r_u)
+{
+ uint32 level = q_u->level;
+ NEW_BUFFER *buffer = NULL;
+ uint32 offered = q_u->offered;
+ uint32 *needed = &r_u->needed;
+ uint32 *returned = &r_u->returned;
+
+ /* that's an [in out] buffer */
+ spoolss_move_buffer(q_u->buffer, &r_u->buffer);
+ buffer = r_u->buffer;
+
+ DEBUG(5,("spoolss_enumprintmonitors\n"));
+
+ /*
+ * Enumerate the print monitors ...
+ *
+ * Just reply with "Local Port", to keep NT happy
+ * and I can use my nice printer checker.
+ */
+
+ *returned=0;
+ *needed=0;
+
+ switch (level) {
+ case 1:
+ return enumprintmonitors_level_1(buffer, offered, needed, returned);
+ case 2:
+ return enumprintmonitors_level_2(buffer, offered, needed, returned);
+ default:
+ return WERR_UNKNOWN_LEVEL;
+ }
+}
+
+/****************************************************************************
+****************************************************************************/
+
+static WERROR getjob_level_1(print_queue_struct **queue, int count, int snum, uint32 jobid, NEW_BUFFER *buffer, uint32 offered, uint32 *needed)
+{
+ int i=0;
+ BOOL found=False;
+ JOB_INFO_1 *info_1=NULL;
+
+ info_1=(JOB_INFO_1 *)malloc(sizeof(JOB_INFO_1));
+
+ if (info_1 == NULL) {
+ return WERR_NOMEM;
+ }
+
+ for (i=0; i<count && found==False; i++) {
+ if ((*queue)[i].job==(int)jobid)
+ found=True;
+ }
+
+ if (found==False) {
+ SAFE_FREE(info_1);
+ /* NT treats not found as bad param... yet another bad choice */
+ return WERR_INVALID_PARAM;
+ }
+
+ fill_job_info_1(info_1, &((*queue)[i-1]), i, snum);
+
+ *needed += spoolss_size_job_info_1(info_1);
+
+ if (!alloc_buffer_size(buffer, *needed)) {
+ SAFE_FREE(info_1);
+ return WERR_INSUFFICIENT_BUFFER;
+ }
+
+ smb_io_job_info_1("", buffer, info_1, 0);
+
+ SAFE_FREE(info_1);
+
+ if (*needed > offered)
+ return WERR_INSUFFICIENT_BUFFER;
+
+ return WERR_OK;
+}
+
+/****************************************************************************
+****************************************************************************/
+
+static WERROR getjob_level_2(print_queue_struct **queue, int count, int snum, uint32 jobid, NEW_BUFFER *buffer, uint32 offered, uint32 *needed)
+{
+ int i = 0;
+ BOOL found = False;
+ JOB_INFO_2 *info_2;
+ NT_PRINTER_INFO_LEVEL *ntprinter = NULL;
+ WERROR ret;
+ DEVICEMODE *devmode = NULL;
+ NT_DEVICEMODE *nt_devmode = NULL;
+
+ info_2=(JOB_INFO_2 *)malloc(sizeof(JOB_INFO_2));
+
+ ZERO_STRUCTP(info_2);
+
+ if (info_2 == NULL) {
+ ret = WERR_NOMEM;
+ goto done;
+ }
+
+ for ( i=0; i<count && found==False; i++ )
+ {
+ if ((*queue)[i].job == (int)jobid)
+ found = True;
+ }
+
+ if ( !found )
+ {
+ /* NT treats not found as bad param... yet another bad
+ choice */
+ ret = WERR_INVALID_PARAM;
+ goto done;
+ }
+
+ ret = get_a_printer(NULL, &ntprinter, 2, lp_const_servicename(snum));
+ if (!W_ERROR_IS_OK(ret))
+ goto done;
+
+ /*
+ * if the print job does not have a DEVMODE associated with it,
+ * just use the one for the printer. A NULL devicemode is not
+ * a failure condition
+ */
+
+ if ( !(nt_devmode=print_job_devmode( snum, jobid )) )
+ devmode = construct_dev_mode(snum);
+ else {
+ if ((devmode = (DEVICEMODE *)malloc(sizeof(DEVICEMODE))) != NULL) {
+ ZERO_STRUCTP( devmode );
+ convert_nt_devicemode( devmode, nt_devmode );
+ }
+ }
+
+ fill_job_info_2(info_2, &((*queue)[i-1]), i, snum, ntprinter, devmode);
+
+ *needed += spoolss_size_job_info_2(info_2);
+
+ if (!alloc_buffer_size(buffer, *needed)) {
+ ret = WERR_INSUFFICIENT_BUFFER;
+ goto done;
+ }
+
+ smb_io_job_info_2("", buffer, info_2, 0);
+
+ if (*needed > offered) {
+ ret = WERR_INSUFFICIENT_BUFFER;
+ goto done;
+ }
+
+ ret = WERR_OK;
+
+ done:
+ /* Cleanup allocated memory */
+
+ free_job_info_2(info_2); /* Also frees devmode */
+ SAFE_FREE(info_2);
+ free_a_printer(&ntprinter, 2);
+
+ return ret;
+}
+
+/****************************************************************************
+****************************************************************************/
+
+WERROR _spoolss_getjob( pipes_struct *p, SPOOL_Q_GETJOB *q_u, SPOOL_R_GETJOB *r_u)
+{
+ POLICY_HND *handle = &q_u->handle;
+ uint32 jobid = q_u->jobid;
+ uint32 level = q_u->level;
+ NEW_BUFFER *buffer = NULL;
+ uint32 offered = q_u->offered;
+ uint32 *needed = &r_u->needed;
+ WERROR wstatus = WERR_OK;
+
+ int snum;
+ int count;
+ print_queue_struct *queue = NULL;
+ print_status_struct prt_status;
+
+ /* that's an [in out] buffer */
+ spoolss_move_buffer(q_u->buffer, &r_u->buffer);
+ buffer = r_u->buffer;
+
+ DEBUG(5,("spoolss_getjob\n"));
+
+ *needed = 0;
+
+ if (!get_printer_snum(p, handle, &snum))
+ return WERR_BADFID;
+
+ count = print_queue_status(snum, &queue, &prt_status);
+
+ DEBUGADD(4,("count:[%d], prt_status:[%d], [%s]\n",
+ count, prt_status.status, prt_status.message));
+
+ switch ( level ) {
+ case 1:
+ wstatus = getjob_level_1(&queue, count, snum, jobid,
+ buffer, offered, needed);
+ break;
+ case 2:
+ wstatus = getjob_level_2(&queue, count, snum, jobid,
+ buffer, offered, needed);
+ break;
+ default:
+ wstatus = WERR_UNKNOWN_LEVEL;
+ break;
+ }
+
+ SAFE_FREE(queue);
+ return wstatus;
+}
+
+/********************************************************************
+ spoolss_getprinterdataex
+
+ From MSDN documentation of GetPrinterDataEx: pass request
+ to GetPrinterData if key is "PrinterDriverData".
+ ********************************************************************/
+
+WERROR _spoolss_getprinterdataex(pipes_struct *p, SPOOL_Q_GETPRINTERDATAEX *q_u, SPOOL_R_GETPRINTERDATAEX *r_u)
+{
+ POLICY_HND *handle = &q_u->handle;
+ uint32 in_size = q_u->size;
+ uint32 *type = &r_u->type;
+ uint32 *out_size = &r_u->size;
+ uint8 **data = &r_u->data;
+ uint32 *needed = &r_u->needed;
+ fstring keyname, valuename;
+
+ Printer_entry *Printer = find_printer_index_by_hnd(p, handle);
+
+ NT_PRINTER_INFO_LEVEL *printer = NULL;
+ int snum = 0;
+ WERROR status = WERR_OK;
+
+ DEBUG(4,("_spoolss_getprinterdataex\n"));
+
+ unistr2_to_ascii(keyname, &q_u->keyname, sizeof(keyname) - 1);
+ unistr2_to_ascii(valuename, &q_u->valuename, sizeof(valuename) - 1);
+
+ DEBUG(10, ("_spoolss_getprinterdataex: key => [%s], value => [%s]\n",
+ keyname, valuename));
+
+ /* in case of problem, return some default values */
+
+ *needed = 0;
+ *type = 0;
+ *out_size = in_size;
+
+ if (!Printer) {
+ DEBUG(2,("_spoolss_getprinterdataex: Invalid handle (%s:%u:%u).\n", OUR_HANDLE(handle)));
+ status = WERR_BADFID;
+ goto done;
+ }
+
+ /* Is the handle to a printer or to the server? */
+
+ if (Printer->printer_type == PRINTER_HANDLE_IS_PRINTSERVER) {
+ DEBUG(10,("_spoolss_getprinterdataex: Not implemented for server handles yet\n"));
+ status = WERR_INVALID_PARAM;
+ goto done;
+ }
+
+ if ( !get_printer_snum(p,handle, &snum) )
+ return WERR_BADFID;
+
+ status = get_a_printer(Printer, &printer, 2, lp_servicename(snum));
+ if ( !W_ERROR_IS_OK(status) )
+ goto done;
+
+ /* check to see if the keyname is valid */
+ if ( !strlen(keyname) ) {
+ status = WERR_INVALID_PARAM;
+ goto done;
+ }
+
+ if ( lookup_printerkey( &printer->info_2->data, keyname ) == -1 ) {
+ DEBUG(4,("_spoolss_getprinterdataex: Invalid keyname [%s]\n", keyname ));
+ free_a_printer( &printer, 2 );
+ status = WERR_BADFILE;
+ goto done;
+ }
+
+ /* When given a new keyname, we should just create it */
+
+ status = get_printer_dataex( p->mem_ctx, printer, keyname, valuename, type, data, needed, in_size );
+
+ if (*needed > *out_size)
+ status = WERR_MORE_DATA;
+
+done:
+ if ( !W_ERROR_IS_OK(status) )
+ {
+ DEBUG(5, ("error: allocating %d\n", *out_size));
+
+ /* reply this param doesn't exist */
+
+ if ( *out_size )
+ {
+ if( (*data=(uint8 *)talloc_zero(p->mem_ctx, *out_size*sizeof(uint8))) == NULL ) {
+ status = WERR_NOMEM;
+ goto done;
+ }
+ }
+ else {
+ *data = NULL;
+ }
+ }
+
+ if ( printer )
+ free_a_printer( &printer, 2 );
+
+ return status;
+}
+
+/********************************************************************
+ * spoolss_setprinterdataex
+ ********************************************************************/
+
+WERROR _spoolss_setprinterdataex(pipes_struct *p, SPOOL_Q_SETPRINTERDATAEX *q_u, SPOOL_R_SETPRINTERDATAEX *r_u)
+{
+ POLICY_HND *handle = &q_u->handle;
+ uint32 type = q_u->type;
+ uint8 *data = q_u->data;
+ uint32 real_len = q_u->real_len;
+
+ NT_PRINTER_INFO_LEVEL *printer = NULL;
+ int snum = 0;
+ WERROR status = WERR_OK;
+ Printer_entry *Printer = find_printer_index_by_hnd(p, handle);
+ fstring valuename;
+ fstring keyname;
+ char *oid_string;
+
+ DEBUG(4,("_spoolss_setprinterdataex\n"));
+
+ /* From MSDN documentation of SetPrinterDataEx: pass request to
+ SetPrinterData if key is "PrinterDriverData" */
+
+ if (!Printer) {
+ DEBUG(2,("_spoolss_setprinterdataex: Invalid handle (%s:%u:%u).\n", OUR_HANDLE(handle)));
+ return WERR_BADFID;
+ }
+
+ if ( Printer->printer_type == PRINTER_HANDLE_IS_PRINTSERVER ) {
+ DEBUG(10,("_spoolss_setprinterdataex: Not implemented for server handles yet\n"));
+ return WERR_INVALID_PARAM;
+ }
+
+ if ( !get_printer_snum(p,handle, &snum) )
+ return WERR_BADFID;
+
+ /*
+ * Access check : NT returns "access denied" if you make a
+ * SetPrinterData call without the necessary privildge.
+ * we were originally returning OK if nothing changed
+ * which made Win2k issue **a lot** of SetPrinterData
+ * when connecting to a printer --jerry
+ */
+
+ if (Printer->access_granted != PRINTER_ACCESS_ADMINISTER)
+ {
+ DEBUG(3, ("_spoolss_setprinterdataex: change denied by handle access permissions\n"));
+ return WERR_ACCESS_DENIED;
+ }
+
+ status = get_a_printer(Printer, &printer, 2, lp_servicename(snum));
+ if (!W_ERROR_IS_OK(status))
+ return status;
+
+ unistr2_to_ascii( valuename, &q_u->value, sizeof(valuename) - 1);
+ unistr2_to_ascii( keyname, &q_u->key, sizeof(keyname) - 1);
+
+ /* check for OID in valuename */
+
+ if ( (oid_string = strchr( valuename, ',' )) != NULL )
+ {
+ *oid_string = '\0';
+ oid_string++;
+ }
+
+ /* save the registry data */
+
+ status = set_printer_dataex( printer, keyname, valuename, type, data, real_len );
+
+ if ( W_ERROR_IS_OK(status) )
+ {
+ /* save the OID if one was specified */
+ if ( oid_string ) {
+ fstrcat( keyname, "\\" );
+ fstrcat( keyname, SPOOL_OID_KEY );
+
+ /*
+ * I'm not checking the status here on purpose. Don't know
+ * if this is right, but I'm returning the status from the
+ * previous set_printer_dataex() call. I have no idea if
+ * this is right. --jerry
+ */
+
+ set_printer_dataex( printer, keyname, valuename,
+ REG_SZ, (void*)oid_string, strlen(oid_string)+1 );
+ }
+
+ status = mod_a_printer(*printer, 2);
+ }
+
+ free_a_printer(&printer, 2);
+
+ return status;
+}
+
+
+/********************************************************************
+ * spoolss_deleteprinterdataex
+ ********************************************************************/
+
+WERROR _spoolss_deleteprinterdataex(pipes_struct *p, SPOOL_Q_DELETEPRINTERDATAEX *q_u, SPOOL_R_DELETEPRINTERDATAEX *r_u)
+{
+ POLICY_HND *handle = &q_u->handle;
+ UNISTR2 *value = &q_u->valuename;
+ UNISTR2 *key = &q_u->keyname;
+
+ NT_PRINTER_INFO_LEVEL *printer = NULL;
+ int snum=0;
+ WERROR status = WERR_OK;
+ Printer_entry *Printer=find_printer_index_by_hnd(p, handle);
+ pstring valuename, keyname;
+
+ DEBUG(5,("spoolss_deleteprinterdataex\n"));
+
+ if (!Printer) {
+ DEBUG(2,("_spoolss_deleteprinterdata: Invalid handle (%s:%u:%u).\n", OUR_HANDLE(handle)));
+ return WERR_BADFID;
+ }
+
+ if (!get_printer_snum(p, handle, &snum))
+ return WERR_BADFID;
+
+ if (Printer->access_granted != PRINTER_ACCESS_ADMINISTER) {
+ DEBUG(3, ("_spoolss_deleteprinterdataex: printer properties change denied by handle\n"));
+ return WERR_ACCESS_DENIED;
+ }
+
+ status = get_a_printer(Printer, &printer, 2, lp_const_servicename(snum));
+ if (!W_ERROR_IS_OK(status))
+ return status;
+
+ unistr2_to_ascii( valuename, value, sizeof(valuename)-1 );
+ unistr2_to_ascii( keyname, key, sizeof(keyname)-1 );
+
+ status = delete_printer_dataex( printer, keyname, valuename );
+
+ if ( W_ERROR_IS_OK(status) )
+ mod_a_printer( *printer, 2 );
+
+ free_a_printer(&printer, 2);
+
+ return status;
+}
+
+/********************************************************************
+ * spoolss_enumprinterkey
+ ********************************************************************/
+
+
+WERROR _spoolss_enumprinterkey(pipes_struct *p, SPOOL_Q_ENUMPRINTERKEY *q_u, SPOOL_R_ENUMPRINTERKEY *r_u)
+{
+ fstring key;
+ fstring *keynames = NULL;
+ uint16 *enumkeys = NULL;
+ int num_keys;
+ int printerkey_len;
+ POLICY_HND *handle = &q_u->handle;
+ Printer_entry *Printer = find_printer_index_by_hnd(p, handle);
+ NT_PRINTER_DATA *data;
+ NT_PRINTER_INFO_LEVEL *printer = NULL;
+ int snum = 0;
+ WERROR status = WERR_BADFILE;
+
+
+ DEBUG(4,("_spoolss_enumprinterkey\n"));
+
+ if (!Printer) {
+ DEBUG(2,("_spoolss_enumprinterkey: Invalid handle (%s:%u:%u).\n", OUR_HANDLE(handle)));
+ return WERR_BADFID;
+ }
+
+ if ( !get_printer_snum(p,handle, &snum) )
+ return WERR_BADFID;
+
+ status = get_a_printer(Printer, &printer, 2, lp_const_servicename(snum));
+ if (!W_ERROR_IS_OK(status))
+ return status;
+
+ /* get the list of subkey names */
+
+ unistr2_to_ascii( key, &q_u->key, sizeof(key)-1 );
+ data = &printer->info_2->data;
+
+ num_keys = get_printer_subkeys( data, key, &keynames );
+
+ if ( num_keys == -1 ) {
+ status = WERR_BADFILE;
+ goto done;
+ }
+
+ printerkey_len = init_unistr_array( &enumkeys, keynames, NULL );
+
+ r_u->needed = printerkey_len*2;
+
+ if ( q_u->size < r_u->needed ) {
+ status = WERR_MORE_DATA;
+ goto done;
+ }
+
+ if (!make_spoolss_buffer5(p->mem_ctx, &r_u->keys, printerkey_len, enumkeys)) {
+ status = WERR_NOMEM;
+ goto done;
+ }
+
+ status = WERR_OK;
+
+ if ( q_u->size < r_u->needed )
+ status = WERR_MORE_DATA;
+
+done:
+ free_a_printer( &printer, 2 );
+ SAFE_FREE( keynames );
+
+ return status;
+}
+
+/********************************************************************
+ * spoolss_deleteprinterkey
+ ********************************************************************/
+
+WERROR _spoolss_deleteprinterkey(pipes_struct *p, SPOOL_Q_DELETEPRINTERKEY *q_u, SPOOL_R_DELETEPRINTERKEY *r_u)
+{
+ POLICY_HND *handle = &q_u->handle;
+ Printer_entry *Printer = find_printer_index_by_hnd(p, &q_u->handle);
+ fstring key;
+ NT_PRINTER_INFO_LEVEL *printer = NULL;
+ int snum=0;
+ WERROR status;
+
+ DEBUG(5,("spoolss_deleteprinterkey\n"));
+
+ if (!Printer) {
+ DEBUG(2,("_spoolss_deleteprinterkey: Invalid handle (%s:%u:%u).\n", OUR_HANDLE(handle)));
+ return WERR_BADFID;
+ }
+
+ /* if keyname == NULL, return error */
+
+ if ( !q_u->keyname.buffer )
+ return WERR_INVALID_PARAM;
+
+ if (!get_printer_snum(p, handle, &snum))
+ return WERR_BADFID;
+
+ if (Printer->access_granted != PRINTER_ACCESS_ADMINISTER) {
+ DEBUG(3, ("_spoolss_deleteprinterkey: printer properties change denied by handle\n"));
+ return WERR_ACCESS_DENIED;
+ }
+
+ status = get_a_printer(Printer, &printer, 2, lp_const_servicename(snum));
+ if (!W_ERROR_IS_OK(status))
+ return status;
+
+ /* delete the key and all subneys */
+
+ unistr2_to_ascii(key, &q_u->keyname, sizeof(key) - 1);
+
+ status = delete_all_printer_data( printer->info_2, key );
+
+ if ( W_ERROR_IS_OK(status) )
+ status = mod_a_printer(*printer, 2);
+
+ free_a_printer( &printer, 2 );
+
+ return status;
+}
+
+
+/********************************************************************
+ * spoolss_enumprinterdataex
+ ********************************************************************/
+
+WERROR _spoolss_enumprinterdataex(pipes_struct *p, SPOOL_Q_ENUMPRINTERDATAEX *q_u, SPOOL_R_ENUMPRINTERDATAEX *r_u)
+{
+ POLICY_HND *handle = &q_u->handle;
+ uint32 in_size = q_u->size;
+ uint32 num_entries,
+ needed;
+ NT_PRINTER_INFO_LEVEL *printer = NULL;
+ PRINTER_ENUM_VALUES *enum_values = NULL;
+ NT_PRINTER_DATA *p_data;
+ fstring key;
+ Printer_entry *Printer = find_printer_index_by_hnd(p, handle);
+ int snum;
+ WERROR result;
+ int key_index;
+ int i;
+ REGISTRY_VALUE *val;
+ char *value_name;
+ int data_len;
+
+
+ DEBUG(4,("_spoolss_enumprinterdataex\n"));
+
+ if (!Printer) {
+ DEBUG(2,("_spoolss_enumprinterdataex: Invalid handle (%s:%u:%u1<).\n", OUR_HANDLE(handle)));
+ return WERR_BADFID;
+ }
+
+ /*
+ * first check for a keyname of NULL or "". Win2k seems to send
+ * this a lot and we should send back WERR_INVALID_PARAM
+ * no need to spend time looking up the printer in this case.
+ * --jerry
+ */
+
+ unistr2_to_ascii(key, &q_u->key, sizeof(key) - 1);
+ if ( !strlen(key) ) {
+ result = WERR_INVALID_PARAM;
+ goto done;
+ }
+
+ /* get the printer off of disk */
+
+ if (!get_printer_snum(p,handle, &snum))
+ return WERR_BADFID;
+
+ ZERO_STRUCT(printer);
+ result = get_a_printer(Printer, &printer, 2, lp_const_servicename(snum));
+ if (!W_ERROR_IS_OK(result))
+ return result;
+
+ /* now look for a match on the key name */
+
+ p_data = &printer->info_2->data;
+
+ unistr2_to_ascii(key, &q_u->key, sizeof(key) - 1);
+ if ( (key_index = lookup_printerkey( p_data, key)) == -1 )
+ {
+ DEBUG(10,("_spoolss_enumprinterdataex: Unknown keyname [%s]\n", key));
+ result = WERR_INVALID_PARAM;
+ goto done;
+ }
+
+ result = WERR_OK;
+ needed = 0;
+
+ /* allocate the memory for the array of pointers -- if necessary */
+
+ num_entries = regval_ctr_numvals( &p_data->keys[key_index].values );
+ if ( num_entries )
+ {
+ if ( (enum_values=talloc(p->mem_ctx, num_entries*sizeof(PRINTER_ENUM_VALUES))) == NULL )
+ {
+ DEBUG(0,("_spoolss_enumprinterdataex: talloc() failed to allocate memory for [%lu] bytes!\n",
+ (unsigned long)num_entries*sizeof(PRINTER_ENUM_VALUES)));
+ result = WERR_NOMEM;
+ goto done;
+ }
+
+ memset( enum_values, 0x0, num_entries*sizeof(PRINTER_ENUM_VALUES) );
+ }
+
+ /*
+ * loop through all params and build the array to pass
+ * back to the client
+ */
+
+ for ( i=0; i<num_entries; i++ )
+ {
+ /* lookup the registry value */
+
+ val = regval_ctr_specific_value( &p_data->keys[key_index].values, i );
+ DEBUG(10,("retrieved value number [%d] [%s]\n", i, regval_name(val) ));
+
+ /* copy the data */
+
+ value_name = regval_name( val );
+ init_unistr( &enum_values[i].valuename, value_name );
+ enum_values[i].value_len = (strlen(value_name)+1) * 2;
+ enum_values[i].type = regval_type( val );
+
+ data_len = regval_size( val );
+ if ( data_len ) {
+ if ( !(enum_values[i].data = talloc_memdup(p->mem_ctx, regval_data_p(val), data_len)) )
+ {
+ DEBUG(0,("talloc_memdup failed to allocate memory [data_len=%d] for data!\n",
+ data_len ));
+ result = WERR_NOMEM;
+ goto done;
+ }
+ }
+ enum_values[i].data_len = data_len;
+
+ /* keep track of the size of the array in bytes */
+
+ needed += spoolss_size_printer_enum_values(&enum_values[i]);
+ }
+
+ /* housekeeping information in the reply */
+
+ r_u->needed = needed;
+ r_u->returned = num_entries;
+
+ if (needed > in_size) {
+ result = WERR_MORE_DATA;
+ goto done;
+ }
+
+ /* copy data into the reply */
+
+ r_u->ctr.size = r_u->needed;
+ r_u->ctr.size_of_array = r_u->returned;
+ r_u->ctr.values = enum_values;
+
+
+
+done:
+ if ( printer )
+ free_a_printer(&printer, 2);
+
+ return result;
+}
+
+/****************************************************************************
+****************************************************************************/
+
+static void fill_printprocessordirectory_1(PRINTPROCESSOR_DIRECTORY_1 *info, char *name)
+{
+ init_unistr(&info->name, name);
+}
+
+static WERROR getprintprocessordirectory_level_1(UNISTR2 *name,
+ UNISTR2 *environment,
+ NEW_BUFFER *buffer,
+ uint32 offered,
+ uint32 *needed)
+{
+ pstring path;
+ pstring long_archi;
+ PRINTPROCESSOR_DIRECTORY_1 *info=NULL;
+
+ unistr2_to_ascii(long_archi, environment, sizeof(long_archi)-1);
+
+ if (!get_short_archi(long_archi))
+ return WERR_INVALID_ENVIRONMENT;
+
+ if((info=(PRINTPROCESSOR_DIRECTORY_1 *)malloc(sizeof(PRINTPROCESSOR_DIRECTORY_1))) == NULL)
+ return WERR_NOMEM;
+
+ pstrcpy(path, "C:\\WINNT\\System32\\spool\\PRTPROCS\\W32X86");
+
+ fill_printprocessordirectory_1(info, path);
+
+ *needed += spoolss_size_printprocessordirectory_info_1(info);
+
+ if (!alloc_buffer_size(buffer, *needed)) {
+ safe_free(info);
+ return WERR_INSUFFICIENT_BUFFER;
+ }
+
+ smb_io_printprocessordirectory_1("", buffer, info, 0);
+
+ safe_free(info);
+
+ if (*needed > offered)
+ return WERR_INSUFFICIENT_BUFFER;
+ else
+ return WERR_OK;
+}
+
+WERROR _spoolss_getprintprocessordirectory(pipes_struct *p, SPOOL_Q_GETPRINTPROCESSORDIRECTORY *q_u, SPOOL_R_GETPRINTPROCESSORDIRECTORY *r_u)
+{
+ uint32 level = q_u->level;
+ NEW_BUFFER *buffer = NULL;
+ uint32 offered = q_u->offered;
+ uint32 *needed = &r_u->needed;
+ WERROR result;
+
+ /* that's an [in out] buffer */
+ spoolss_move_buffer(q_u->buffer, &r_u->buffer);
+ buffer = r_u->buffer;
+
+ DEBUG(5,("_spoolss_getprintprocessordirectory\n"));
+
+ *needed=0;
+
+ switch(level) {
+ case 1:
+ result = getprintprocessordirectory_level_1
+ (&q_u->name, &q_u->environment, buffer, offered, needed);
+ break;
+ default:
+ result = WERR_UNKNOWN_LEVEL;
+ }
+
+ return result;
+}
+
+#if 0
+
+WERROR _spoolss_replyopenprinter(pipes_struct *p, SPOOL_Q_REPLYOPENPRINTER *q_u,
+ SPOOL_R_REPLYOPENPRINTER *r_u)
+{
+ DEBUG(5,("_spoolss_replyopenprinter\n"));
+
+ DEBUG(10, ("replyopenprinter for localprinter %d\n", q_u->printer));
+
+ return WERR_OK;
+}
+
+WERROR _spoolss_replycloseprinter(pipes_struct *p, SPOOL_Q_REPLYCLOSEPRINTER *q_u,
+ SPOOL_R_REPLYCLOSEPRINTER *r_u)
+{
+ DEBUG(5,("_spoolss_replycloseprinter\n"));
+ return WERR_OK;
+}
+
+#endif
diff --git a/source/rpc_server/srv_srvsvc.c b/source/rpc_server/srv_srvsvc.c
new file mode 100644
index 00000000000..9d85088e568
--- /dev/null
+++ b/source/rpc_server/srv_srvsvc.c
@@ -0,0 +1,561 @@
+/*
+ * Unix SMB/CIFS implementation.
+ * RPC Pipe client / server routines
+ * Copyright (C) Andrew Tridgell 1992-1997,
+ * Copyright (C) Luke Kenneth Casson Leighton 1996-1997,
+ * Copyright (C) Paul Ashton 1997,
+ * Copyright (C) Jeremy Allison 2001,
+ * 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 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.
+ */
+
+/* This is the interface to the srvsvc pipe. */
+
+#include "includes.h"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_RPC_SRV
+
+/*******************************************************************
+ api_srv_net_srv_get_info
+********************************************************************/
+
+static BOOL api_srv_net_srv_get_info(pipes_struct *p)
+{
+ SRV_Q_NET_SRV_GET_INFO q_u;
+ SRV_R_NET_SRV_GET_INFO r_u;
+ prs_struct *data = &p->in_data.data;
+ prs_struct *rdata = &p->out_data.rdata;
+
+ ZERO_STRUCT(q_u);
+ ZERO_STRUCT(r_u);
+
+ /* grab the net server get info */
+ if (!srv_io_q_net_srv_get_info("", &q_u, data, 0))
+ return False;
+
+ r_u.status = _srv_net_srv_get_info(p, &q_u, &r_u);
+
+ /* store the response in the SMB stream */
+ if (!srv_io_r_net_srv_get_info("", &r_u, rdata, 0))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ api_srv_net_srv_get_info
+********************************************************************/
+
+static BOOL api_srv_net_srv_set_info(pipes_struct *p)
+{
+ SRV_Q_NET_SRV_SET_INFO q_u;
+ SRV_R_NET_SRV_SET_INFO r_u;
+ prs_struct *data = &p->in_data.data;
+ prs_struct *rdata = &p->out_data.rdata;
+
+ ZERO_STRUCT(q_u);
+ ZERO_STRUCT(r_u);
+
+ /* grab the net server set info */
+ if (!srv_io_q_net_srv_set_info("", &q_u, data, 0))
+ return False;
+
+ r_u.status = _srv_net_srv_set_info(p, &q_u, &r_u);
+
+ /* store the response in the SMB stream */
+ if (!srv_io_r_net_srv_set_info("", &r_u, rdata, 0))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ api_srv_net_file_enum
+********************************************************************/
+
+static BOOL api_srv_net_file_enum(pipes_struct *p)
+{
+ SRV_Q_NET_FILE_ENUM q_u;
+ SRV_R_NET_FILE_ENUM r_u;
+ prs_struct *data = &p->in_data.data;
+ prs_struct *rdata = &p->out_data.rdata;
+
+ ZERO_STRUCT(q_u);
+ ZERO_STRUCT(r_u);
+
+ /* grab the net file enum */
+ if (!srv_io_q_net_file_enum("", &q_u, data, 0))
+ return False;
+
+ r_u.status = _srv_net_file_enum(p, &q_u, &r_u);
+
+ /* store the response in the SMB stream */
+ if(!srv_io_r_net_file_enum("", &r_u, rdata, 0))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ api_srv_net_conn_enum
+********************************************************************/
+
+static BOOL api_srv_net_conn_enum(pipes_struct *p)
+{
+ SRV_Q_NET_CONN_ENUM q_u;
+ SRV_R_NET_CONN_ENUM r_u;
+ prs_struct *data = &p->in_data.data;
+ prs_struct *rdata = &p->out_data.rdata;
+
+ ZERO_STRUCT(q_u);
+ ZERO_STRUCT(r_u);
+
+ /* grab the net server get enum */
+ if (!srv_io_q_net_conn_enum("", &q_u, data, 0))
+ return False;
+
+ r_u.status = _srv_net_conn_enum(p, &q_u, &r_u);
+
+ /* store the response in the SMB stream */
+ if (!srv_io_r_net_conn_enum("", &r_u, rdata, 0))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ Enumerate sessions.
+********************************************************************/
+
+static BOOL api_srv_net_sess_enum(pipes_struct *p)
+{
+ SRV_Q_NET_SESS_ENUM q_u;
+ SRV_R_NET_SESS_ENUM r_u;
+ prs_struct *data = &p->in_data.data;
+ prs_struct *rdata = &p->out_data.rdata;
+
+ ZERO_STRUCT(q_u);
+ ZERO_STRUCT(r_u);
+
+ /* grab the net server get enum */
+ if (!srv_io_q_net_sess_enum("", &q_u, data, 0))
+ return False;
+
+ /* construct reply. always indicate success */
+ r_u.status = _srv_net_sess_enum(p, &q_u, &r_u);
+
+ /* store the response in the SMB stream */
+ if (!srv_io_r_net_sess_enum("", &r_u, rdata, 0))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ RPC to enumerate shares.
+********************************************************************/
+
+static BOOL api_srv_net_share_enum_all(pipes_struct *p)
+{
+ SRV_Q_NET_SHARE_ENUM q_u;
+ SRV_R_NET_SHARE_ENUM r_u;
+ prs_struct *data = &p->in_data.data;
+ prs_struct *rdata = &p->out_data.rdata;
+
+ ZERO_STRUCT(q_u);
+ ZERO_STRUCT(r_u);
+
+ /* Unmarshall the net server get enum. */
+ if(!srv_io_q_net_share_enum("", &q_u, data, 0)) {
+ DEBUG(0,("api_srv_net_share_enum_all: Failed to unmarshall SRV_Q_NET_SHARE_ENUM.\n"));
+ return False;
+ }
+
+ r_u.status = _srv_net_share_enum_all(p, &q_u, &r_u);
+
+ if (!srv_io_r_net_share_enum("", &r_u, rdata, 0)) {
+ DEBUG(0,("api_srv_net_share_enum_all: Failed to marshall SRV_R_NET_SHARE_ENUM.\n"));
+ return False;
+ }
+
+ return True;
+}
+
+/*******************************************************************
+ RPC to enumerate shares.
+********************************************************************/
+
+static BOOL api_srv_net_share_enum(pipes_struct *p)
+{
+ SRV_Q_NET_SHARE_ENUM q_u;
+ SRV_R_NET_SHARE_ENUM r_u;
+ prs_struct *data = &p->in_data.data;
+ prs_struct *rdata = &p->out_data.rdata;
+
+ ZERO_STRUCT(q_u);
+ ZERO_STRUCT(r_u);
+
+ /* Unmarshall the net server get enum. */
+ if(!srv_io_q_net_share_enum("", &q_u, data, 0)) {
+ DEBUG(0,("api_srv_net_share_enum: Failed to unmarshall SRV_Q_NET_SHARE_ENUM.\n"));
+ return False;
+ }
+
+ r_u.status = _srv_net_share_enum(p, &q_u, &r_u);
+
+ if (!srv_io_r_net_share_enum("", &r_u, rdata, 0)) {
+ DEBUG(0,("api_srv_net_share_enum: Failed to marshall SRV_R_NET_SHARE_ENUM.\n"));
+ return False;
+ }
+
+ return True;
+}
+
+/*******************************************************************
+ RPC to return share information.
+********************************************************************/
+
+static BOOL api_srv_net_share_get_info(pipes_struct *p)
+{
+ SRV_Q_NET_SHARE_GET_INFO q_u;
+ SRV_R_NET_SHARE_GET_INFO r_u;
+ prs_struct *data = &p->in_data.data;
+ prs_struct *rdata = &p->out_data.rdata;
+
+ ZERO_STRUCT(q_u);
+ ZERO_STRUCT(r_u);
+
+ /* Unmarshall the net server get info. */
+ if(!srv_io_q_net_share_get_info("", &q_u, data, 0)) {
+ DEBUG(0,("api_srv_net_share_get_info: Failed to unmarshall SRV_Q_NET_SHARE_GET_INFO.\n"));
+ return False;
+ }
+
+ r_u.status = _srv_net_share_get_info(p, &q_u, &r_u);
+
+ if(!srv_io_r_net_share_get_info("", &r_u, rdata, 0)) {
+ DEBUG(0,("api_srv_net_share_get_info: Failed to marshall SRV_R_NET_SHARE_GET_INFO.\n"));
+ return False;
+ }
+
+ return True;
+}
+
+/*******************************************************************
+ RPC to set share information.
+********************************************************************/
+
+static BOOL api_srv_net_share_set_info(pipes_struct *p)
+{
+ SRV_Q_NET_SHARE_SET_INFO q_u;
+ SRV_R_NET_SHARE_SET_INFO r_u;
+ prs_struct *data = &p->in_data.data;
+ prs_struct *rdata = &p->out_data.rdata;
+
+ ZERO_STRUCT(q_u);
+ ZERO_STRUCT(r_u);
+
+ /* Unmarshall the net server set info. */
+ if(!srv_io_q_net_share_set_info("", &q_u, data, 0)) {
+ DEBUG(0,("api_srv_net_share_set_info: Failed to unmarshall SRV_Q_NET_SHARE_SET_INFO.\n"));
+ return False;
+ }
+
+ r_u.status = _srv_net_share_set_info(p, &q_u, &r_u);
+
+ if(!srv_io_r_net_share_set_info("", &r_u, rdata, 0)) {
+ DEBUG(0,("api_srv_net_share_set_info: Failed to marshall SRV_R_NET_SHARE_SET_INFO.\n"));
+ return False;
+ }
+
+ return True;
+}
+
+/*******************************************************************
+ RPC to add share information.
+********************************************************************/
+
+static BOOL api_srv_net_share_add(pipes_struct *p)
+{
+ SRV_Q_NET_SHARE_ADD q_u;
+ SRV_R_NET_SHARE_ADD r_u;
+ prs_struct *data = &p->in_data.data;
+ prs_struct *rdata = &p->out_data.rdata;
+
+ ZERO_STRUCT(q_u);
+ ZERO_STRUCT(r_u);
+
+ /* Unmarshall the net server add info. */
+ if(!srv_io_q_net_share_add("", &q_u, data, 0)) {
+ DEBUG(0,("api_srv_net_share_add: Failed to unmarshall SRV_Q_NET_SHARE_ADD.\n"));
+ return False;
+ }
+
+ r_u.status = _srv_net_share_add(p, &q_u, &r_u);
+
+ if(!srv_io_r_net_share_add("", &r_u, rdata, 0)) {
+ DEBUG(0,("api_srv_net_share_add: Failed to marshall SRV_R_NET_SHARE_ADD.\n"));
+ return False;
+ }
+
+ return True;
+}
+
+/*******************************************************************
+ RPC to delete share information.
+********************************************************************/
+
+static BOOL api_srv_net_share_del(pipes_struct *p)
+{
+ SRV_Q_NET_SHARE_DEL q_u;
+ SRV_R_NET_SHARE_DEL r_u;
+ prs_struct *data = &p->in_data.data;
+ prs_struct *rdata = &p->out_data.rdata;
+
+ ZERO_STRUCT(q_u);
+ ZERO_STRUCT(r_u);
+
+ /* Unmarshall the net server del info. */
+ if(!srv_io_q_net_share_del("", &q_u, data, 0)) {
+ DEBUG(0,("api_srv_net_share_del: Failed to unmarshall SRV_Q_NET_SHARE_DEL.\n"));
+ return False;
+ }
+
+ r_u.status = _srv_net_share_del(p, &q_u, &r_u);
+
+ if(!srv_io_r_net_share_del("", &r_u, rdata, 0)) {
+ DEBUG(0,("api_srv_net_share_del: Failed to marshall SRV_R_NET_SHARE_DEL.\n"));
+ return False;
+ }
+
+ return True;
+}
+
+/*******************************************************************
+ RPC to delete share information.
+********************************************************************/
+
+static BOOL api_srv_net_share_del_sticky(pipes_struct *p)
+{
+ SRV_Q_NET_SHARE_DEL q_u;
+ SRV_R_NET_SHARE_DEL r_u;
+ prs_struct *data = &p->in_data.data;
+ prs_struct *rdata = &p->out_data.rdata;
+
+ ZERO_STRUCT(q_u);
+ ZERO_STRUCT(r_u);
+
+ /* Unmarshall the net server del info. */
+ if(!srv_io_q_net_share_del("", &q_u, data, 0)) {
+ DEBUG(0,("api_srv_net_share_del_sticky: Failed to unmarshall SRV_Q_NET_SHARE_DEL.\n"));
+ return False;
+ }
+
+ r_u.status = _srv_net_share_del_sticky(p, &q_u, &r_u);
+
+ if(!srv_io_r_net_share_del("", &r_u, rdata, 0)) {
+ DEBUG(0,("api_srv_net_share_del_sticky: Failed to marshall SRV_R_NET_SHARE_DEL.\n"));
+ return False;
+ }
+
+ return True;
+}
+
+/*******************************************************************
+ api_srv_net_remote_tod
+********************************************************************/
+
+static BOOL api_srv_net_remote_tod(pipes_struct *p)
+{
+ SRV_Q_NET_REMOTE_TOD q_u;
+ SRV_R_NET_REMOTE_TOD r_u;
+ prs_struct *data = &p->in_data.data;
+ prs_struct *rdata = &p->out_data.rdata;
+
+ ZERO_STRUCT(q_u);
+ ZERO_STRUCT(r_u);
+
+ /* grab the net server get enum */
+ if(!srv_io_q_net_remote_tod("", &q_u, data, 0))
+ return False;
+
+ r_u.status = _srv_net_remote_tod(p, &q_u, &r_u);
+
+ /* store the response in the SMB stream */
+ if(!srv_io_r_net_remote_tod("", &r_u, rdata, 0))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ RPC to enumerate disks available on a server e.g. C:, D: ...
+*******************************************************************/
+
+static BOOL api_srv_net_disk_enum(pipes_struct *p)
+{
+ SRV_Q_NET_DISK_ENUM q_u;
+ SRV_R_NET_DISK_ENUM r_u;
+ prs_struct *data = &p->in_data.data;
+ prs_struct *rdata = &p->out_data.rdata;
+
+ ZERO_STRUCT(q_u);
+ ZERO_STRUCT(r_u);
+
+ /* Unmarshall the net server disk enum. */
+ if(!srv_io_q_net_disk_enum("", &q_u, data, 0)) {
+ DEBUG(0,("api_srv_net_disk_enum: Failed to unmarshall SRV_Q_NET_DISK_ENUM.\n"));
+ return False;
+ }
+
+ r_u.status = _srv_net_disk_enum(p, &q_u, &r_u);
+
+ if(!srv_io_r_net_disk_enum("", &r_u, rdata, 0)) {
+ DEBUG(0,("api_srv_net_disk_enum: Failed to marshall SRV_R_NET_DISK_ENUM.\n"));
+ return False;
+ }
+
+ return True;
+}
+
+/*******************************************************************
+ NetValidateName (opnum 0x21)
+*******************************************************************/
+
+static BOOL api_srv_net_name_validate(pipes_struct *p)
+{
+ SRV_Q_NET_NAME_VALIDATE q_u;
+ SRV_R_NET_NAME_VALIDATE r_u;
+ prs_struct *data = &p->in_data.data;
+ prs_struct *rdata = &p->out_data.rdata;
+
+ ZERO_STRUCT(q_u);
+ ZERO_STRUCT(r_u);
+
+ /* Unmarshall the net server disk enum. */
+ if(!srv_io_q_net_name_validate("", &q_u, data, 0)) {
+ DEBUG(0,("api_srv_net_name_validate: Failed to unmarshall SRV_Q_NET_NAME_VALIDATE.\n"));
+ return False;
+ }
+
+ r_u.status = _srv_net_name_validate(p, &q_u, &r_u);
+
+ if(!srv_io_r_net_name_validate("", &r_u, rdata, 0)) {
+ DEBUG(0,("api_srv_net_name_validate: Failed to marshall SRV_R_NET_NAME_VALIDATE.\n"));
+ return False;
+ }
+
+ return True;
+}
+
+/*******************************************************************
+ NetFileQuerySecdesc (opnum 0x27)
+*******************************************************************/
+
+static BOOL api_srv_net_file_query_secdesc(pipes_struct *p)
+{
+ SRV_Q_NET_FILE_QUERY_SECDESC q_u;
+ SRV_R_NET_FILE_QUERY_SECDESC r_u;
+ prs_struct *data = &p->in_data.data;
+ prs_struct *rdata = &p->out_data.rdata;
+
+ ZERO_STRUCT(q_u);
+ ZERO_STRUCT(r_u);
+
+ /* Unmarshall the net file get info from Win9x */
+ if(!srv_io_q_net_file_query_secdesc("", &q_u, data, 0)) {
+ DEBUG(0,("api_srv_net_file_query_secdesc: Failed to unmarshall SRV_Q_NET_FILE_QUERY_SECDESC.\n"));
+ return False;
+ }
+
+ r_u.status = _srv_net_file_query_secdesc(p, &q_u, &r_u);
+
+ if(!srv_io_r_net_file_query_secdesc("", &r_u, rdata, 0)) {
+ DEBUG(0,("api_srv_net_file_query_secdesc: Failed to marshall SRV_R_NET_FILE_QUERY_SECDESC.\n"));
+ return False;
+ }
+
+ return True;
+}
+
+/*******************************************************************
+ NetFileSetSecdesc (opnum 0x28)
+*******************************************************************/
+
+static BOOL api_srv_net_file_set_secdesc(pipes_struct *p)
+{
+ SRV_Q_NET_FILE_SET_SECDESC q_u;
+ SRV_R_NET_FILE_SET_SECDESC r_u;
+ prs_struct *data = &p->in_data.data;
+ prs_struct *rdata = &p->out_data.rdata;
+
+ ZERO_STRUCT(q_u);
+ ZERO_STRUCT(r_u);
+
+ /* Unmarshall the net file set info from Win9x */
+ if(!srv_io_q_net_file_set_secdesc("", &q_u, data, 0)) {
+ DEBUG(0,("api_srv_net_file_set_secdesc: Failed to unmarshall SRV_Q_NET_FILE_SET_SECDESC.\n"));
+ return False;
+ }
+
+ r_u.status = _srv_net_file_set_secdesc(p, &q_u, &r_u);
+
+ if(!srv_io_r_net_file_set_secdesc("", &r_u, rdata, 0)) {
+ DEBUG(0,("api_srv_net_file_set_secdesc: Failed to marshall SRV_R_NET_FILE_SET_SECDESC.\n"));
+ return False;
+ }
+
+ return True;
+}
+
+/*******************************************************************
+\PIPE\srvsvc commands
+********************************************************************/
+
+static struct api_struct api_srv_cmds[] =
+{
+ { "SRV_NET_CONN_ENUM" , SRV_NET_CONN_ENUM , api_srv_net_conn_enum },
+ { "SRV_NET_SESS_ENUM" , SRV_NET_SESS_ENUM , api_srv_net_sess_enum },
+ { "SRV_NET_SHARE_ENUM_ALL" , SRV_NET_SHARE_ENUM_ALL , api_srv_net_share_enum_all },
+ { "SRV_NET_SHARE_ENUM" , SRV_NET_SHARE_ENUM , api_srv_net_share_enum },
+ { "SRV_NET_SHARE_ADD" , SRV_NET_SHARE_ADD , api_srv_net_share_add },
+ { "SRV_NET_SHARE_DEL" , SRV_NET_SHARE_DEL , api_srv_net_share_del },
+ { "SRV_NET_SHARE_DEL_STICKY" , SRV_NET_SHARE_DEL_STICKY , api_srv_net_share_del_sticky },
+ { "SRV_NET_SHARE_GET_INFO" , SRV_NET_SHARE_GET_INFO , api_srv_net_share_get_info },
+ { "SRV_NET_SHARE_SET_INFO" , SRV_NET_SHARE_SET_INFO , api_srv_net_share_set_info },
+ { "SRV_NET_FILE_ENUM" , SRV_NET_FILE_ENUM , api_srv_net_file_enum },
+ { "SRV_NET_SRV_GET_INFO" , SRV_NET_SRV_GET_INFO , api_srv_net_srv_get_info },
+ { "SRV_NET_SRV_SET_INFO" , SRV_NET_SRV_SET_INFO , api_srv_net_srv_set_info },
+ { "SRV_NET_REMOTE_TOD" , SRV_NET_REMOTE_TOD , api_srv_net_remote_tod },
+ { "SRV_NET_DISK_ENUM" , SRV_NET_DISK_ENUM , api_srv_net_disk_enum },
+ { "SRV_NET_NAME_VALIDATE" , SRV_NET_NAME_VALIDATE , api_srv_net_name_validate },
+ { "SRV_NET_FILE_QUERY_SECDESC", SRV_NET_FILE_QUERY_SECDESC, api_srv_net_file_query_secdesc },
+ { "SRV_NET_FILE_SET_SECDESC" , SRV_NET_FILE_SET_SECDESC , api_srv_net_file_set_secdesc }
+};
+
+void srvsvc_get_pipe_fns( struct api_struct **fns, int *n_fns )
+{
+ *fns = api_srv_cmds;
+ *n_fns = sizeof(api_srv_cmds) / sizeof(struct api_struct);
+}
+
+
+NTSTATUS rpc_srv_init(void)
+{
+ return rpc_pipe_register_commands(SMB_RPC_INTERFACE_VERSION, "srvsvc", "ntsvcs", api_srv_cmds,
+ sizeof(api_srv_cmds) / sizeof(struct api_struct));
+}
diff --git a/source/rpc_server/srv_srvsvc_nt.c b/source/rpc_server/srv_srvsvc_nt.c
new file mode 100644
index 00000000000..9d56e1b3858
--- /dev/null
+++ b/source/rpc_server/srv_srvsvc_nt.c
@@ -0,0 +1,2195 @@
+/*
+ * Unix SMB/CIFS implementation.
+ * RPC Pipe client / server routines
+ * Copyright (C) Andrew Tridgell 1992-1997,
+ * Copyright (C) Jeremy Allison 2001.
+ * Copyright (C) Nigel Williams 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 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.
+ */
+
+/* This is the implementation of the srvsvc pipe. */
+
+#include "includes.h"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_RPC_SRV
+
+/*******************************************************************
+ Utility function to get the 'type' of a share from an snum.
+ ********************************************************************/
+static uint32 get_share_type(int snum)
+{
+ char *net_name = lp_servicename(snum);
+ int len_net_name = strlen(net_name);
+
+ /* work out the share type */
+ uint32 type = STYPE_DISKTREE;
+
+ if (lp_print_ok(snum))
+ type = STYPE_PRINTQ;
+ if (strequal(lp_fstype(snum), "IPC"))
+ type = STYPE_IPC;
+ if (net_name[len_net_name] == '$')
+ type |= STYPE_HIDDEN;
+
+ return type;
+}
+
+/*******************************************************************
+ Fill in a share info level 0 structure.
+ ********************************************************************/
+
+static void init_srv_share_info_0(pipes_struct *p, SRV_SHARE_INFO_0 *sh0, int snum)
+{
+ pstring net_name;
+
+ pstrcpy(net_name, lp_servicename(snum));
+
+ init_srv_share_info0(&sh0->info_0, net_name);
+ init_srv_share_info0_str(&sh0->info_0_str, net_name);
+}
+
+/*******************************************************************
+ Fill in a share info level 1 structure.
+ ********************************************************************/
+
+static void init_srv_share_info_1(pipes_struct *p, SRV_SHARE_INFO_1 *sh1, int snum)
+{
+ pstring remark;
+
+ char *net_name = lp_servicename(snum);
+ pstrcpy(remark, lp_comment(snum));
+ standard_sub_conn(p->conn, remark,sizeof(remark));
+
+ init_srv_share_info1(&sh1->info_1, net_name, get_share_type(snum), remark);
+ init_srv_share_info1_str(&sh1->info_1_str, net_name, remark);
+}
+
+/*******************************************************************
+ Fill in a share info level 2 structure.
+ ********************************************************************/
+
+static void init_srv_share_info_2(pipes_struct *p, SRV_SHARE_INFO_2 *sh2, int snum)
+{
+ pstring remark;
+ pstring path;
+ pstring passwd;
+
+ char *net_name = lp_servicename(snum);
+ pstrcpy(remark, lp_comment(snum));
+ standard_sub_conn(p->conn, remark,sizeof(remark));
+ pstrcpy(path, "C:");
+ pstrcat(path, lp_pathname(snum));
+
+ /*
+ * Change / to \\ so that win2k will see it as a valid path. This was added to
+ * enable use of browsing in win2k add share dialog.
+ */
+
+ string_replace(path, '/', '\\');
+
+ pstrcpy(passwd, "");
+
+ init_srv_share_info2(&sh2->info_2, net_name, get_share_type(snum), remark, 0, 0xffffffff, 1, path, passwd);
+ init_srv_share_info2_str(&sh2->info_2_str, net_name, remark, path, passwd);
+}
+
+/*******************************************************************
+ What to do when smb.conf is updated.
+ ********************************************************************/
+
+static void smb_conf_updated(int msg_type, pid_t src, void *buf, size_t len)
+{
+ DEBUG(10,("smb_conf_updated: Got message saying smb.conf was updated. Reloading.\n"));
+ reload_services(False);
+}
+
+/*******************************************************************
+ Create the share security tdb.
+ ********************************************************************/
+
+static TDB_CONTEXT *share_tdb; /* used for share security descriptors */
+#define SHARE_DATABASE_VERSION_V1 1
+#define SHARE_DATABASE_VERSION_V2 2 /* version id in little endian. */
+
+BOOL share_info_db_init(void)
+{
+ static pid_t local_pid;
+ const char *vstring = "INFO/version";
+ int32 vers_id;
+
+ if (share_tdb && local_pid == sys_getpid())
+ return True;
+ share_tdb = tdb_open_log(lock_path("share_info.tdb"), 0, TDB_DEFAULT, O_RDWR|O_CREAT, 0600);
+ if (!share_tdb) {
+ DEBUG(0,("Failed to open share info database %s (%s)\n",
+ lock_path("share_info.tdb"), strerror(errno) ));
+ return False;
+ }
+
+ local_pid = sys_getpid();
+
+ /* handle a Samba upgrade */
+ tdb_lock_bystring(share_tdb, vstring, 0);
+
+ /* Cope with byte-reversed older versions of the db. */
+ vers_id = tdb_fetch_int32(share_tdb, vstring);
+ if ((vers_id == SHARE_DATABASE_VERSION_V1) || (IREV(vers_id) == SHARE_DATABASE_VERSION_V1)) {
+ /* Written on a bigendian machine with old fetch_int code. Save as le. */
+ tdb_store_int32(share_tdb, vstring, SHARE_DATABASE_VERSION_V2);
+ vers_id = SHARE_DATABASE_VERSION_V2;
+ }
+
+ if (vers_id != SHARE_DATABASE_VERSION_V2) {
+ tdb_traverse(share_tdb, tdb_traverse_delete_fn, NULL);
+ tdb_store_int32(share_tdb, vstring, SHARE_DATABASE_VERSION_V2);
+ }
+ tdb_unlock_bystring(share_tdb, vstring);
+
+ message_register(MSG_SMB_CONF_UPDATED, smb_conf_updated);
+
+ return True;
+}
+
+/*******************************************************************
+ Fake up a Everyone, full access as a default.
+ ********************************************************************/
+
+static SEC_DESC *get_share_security_default( TALLOC_CTX *ctx, int snum, size_t *psize)
+{
+ extern DOM_SID global_sid_World;
+ extern struct generic_mapping file_generic_mapping;
+ SEC_ACCESS sa;
+ SEC_ACE ace;
+ SEC_ACL *psa = NULL;
+ SEC_DESC *psd = NULL;
+ uint32 def_access = GENERIC_ALL_ACCESS;
+
+ se_map_generic(&def_access, &file_generic_mapping);
+
+ init_sec_access(&sa, GENERIC_ALL_ACCESS | def_access );
+ init_sec_ace(&ace, &global_sid_World, SEC_ACE_TYPE_ACCESS_ALLOWED, sa, 0);
+
+ if ((psa = make_sec_acl(ctx, NT4_ACL_REVISION, 1, &ace)) != NULL) {
+ psd = make_sec_desc(ctx, SEC_DESC_REVISION, SEC_DESC_SELF_RELATIVE, NULL, NULL, NULL, psa, psize);
+ }
+
+ if (!psd) {
+ DEBUG(0,("get_share_security: Failed to make SEC_DESC.\n"));
+ return NULL;
+ }
+
+ return psd;
+}
+
+/*******************************************************************
+ Pull a security descriptor from the share tdb.
+ ********************************************************************/
+
+static SEC_DESC *get_share_security( TALLOC_CTX *ctx, int snum, size_t *psize)
+{
+ prs_struct ps;
+ fstring key;
+ SEC_DESC *psd = NULL;
+
+ *psize = 0;
+
+ /* Fetch security descriptor from tdb */
+
+ slprintf(key, sizeof(key)-1, "SECDESC/%s", lp_servicename(snum));
+
+ if (tdb_prs_fetch(share_tdb, key, &ps, ctx)!=0 ||
+ !sec_io_desc("get_share_security", &psd, &ps, 1)) {
+
+ DEBUG(4,("get_share_security: using default secdesc for %s\n", lp_servicename(snum) ));
+
+ return get_share_security_default(ctx, snum, psize);
+ }
+
+ if (psd)
+ *psize = sec_desc_size(psd);
+
+ prs_mem_free(&ps);
+ return psd;
+}
+
+/*******************************************************************
+ Store a security descriptor in the share db.
+ ********************************************************************/
+
+static BOOL set_share_security(TALLOC_CTX *ctx, const char *share_name, SEC_DESC *psd)
+{
+ prs_struct ps;
+ TALLOC_CTX *mem_ctx = NULL;
+ fstring key;
+ BOOL ret = False;
+
+ mem_ctx = talloc_init("set_share_security");
+ if (mem_ctx == NULL)
+ return False;
+
+ prs_init(&ps, (uint32)sec_desc_size(psd), mem_ctx, MARSHALL);
+
+ if (!sec_io_desc("share_security", &psd, &ps, 1))
+ goto out;
+
+ slprintf(key, sizeof(key)-1, "SECDESC/%s", share_name);
+
+ if (tdb_prs_store(share_tdb, key, &ps)==0) {
+ ret = True;
+ DEBUG(5,("set_share_security: stored secdesc for %s\n", share_name ));
+ } else {
+ DEBUG(1,("set_share_security: Failed to store secdesc for %s\n", share_name ));
+ }
+
+ /* Free malloc'ed memory */
+
+out:
+
+ prs_mem_free(&ps);
+ if (mem_ctx)
+ talloc_destroy(mem_ctx);
+ return ret;
+}
+
+/*******************************************************************
+ Delete a security descriptor.
+********************************************************************/
+
+static BOOL delete_share_security(int snum)
+{
+ TDB_DATA kbuf;
+ fstring key;
+
+ slprintf(key, sizeof(key)-1, "SECDESC/%s", lp_servicename(snum));
+ kbuf.dptr = key;
+ kbuf.dsize = strlen(key)+1;
+
+ if (tdb_delete(share_tdb, kbuf) != 0) {
+ DEBUG(0,("delete_share_security: Failed to delete entry for share %s\n",
+ lp_servicename(snum) ));
+ return False;
+ }
+
+ return True;
+}
+
+/*******************************************************************
+ Map any generic bits to file specific bits.
+********************************************************************/
+
+void map_generic_share_sd_bits(SEC_DESC *psd)
+{
+ extern struct generic_mapping file_generic_mapping;
+ int i;
+ SEC_ACL *ps_dacl = NULL;
+
+ if (!psd)
+ return;
+
+ ps_dacl = psd->dacl;
+ if (!ps_dacl)
+ return;
+
+ for (i = 0; i < ps_dacl->num_aces; i++) {
+ SEC_ACE *psa = &ps_dacl->ace[i];
+ uint32 orig_mask = psa->info.mask;
+
+ se_map_generic(&psa->info.mask, &file_generic_mapping);
+ psa->info.mask |= orig_mask;
+ }
+}
+
+/*******************************************************************
+ Can this user access with share with the required permissions ?
+********************************************************************/
+
+BOOL share_access_check(connection_struct *conn, int snum, user_struct *vuser, uint32 desired_access)
+{
+ uint32 granted;
+ NTSTATUS status;
+ TALLOC_CTX *mem_ctx = NULL;
+ SEC_DESC *psd = NULL;
+ size_t sd_size;
+ NT_USER_TOKEN *token = NULL;
+ BOOL ret = True;
+
+ mem_ctx = talloc_init("share_access_check");
+ if (mem_ctx == NULL)
+ return False;
+
+ psd = get_share_security(mem_ctx, snum, &sd_size);
+
+ if (!psd)
+ goto out;
+
+ if (conn->nt_user_token)
+ token = conn->nt_user_token;
+ else
+ token = vuser->nt_user_token;
+
+ ret = se_access_check(psd, token, desired_access, &granted, &status);
+
+out:
+
+ talloc_destroy(mem_ctx);
+
+ return ret;
+}
+
+/*******************************************************************
+ Fill in a share info level 501 structure.
+********************************************************************/
+
+static void init_srv_share_info_501(pipes_struct *p, SRV_SHARE_INFO_501 *sh501, int snum)
+{
+ int len_net_name;
+ pstring remark;
+
+ char *net_name = lp_servicename(snum);
+ pstrcpy(remark, lp_comment(snum));
+ standard_sub_conn(p->conn, remark, sizeof(remark));
+
+ len_net_name = strlen(net_name);
+
+ init_srv_share_info501(&sh501->info_501, net_name, get_share_type(snum), remark, (lp_csc_policy(snum) << 4));
+ init_srv_share_info501_str(&sh501->info_501_str, net_name, remark);
+}
+
+/*******************************************************************
+ Fill in a share info level 502 structure.
+ ********************************************************************/
+
+static void init_srv_share_info_502(pipes_struct *p, SRV_SHARE_INFO_502 *sh502, int snum)
+{
+ int len_net_name;
+ pstring net_name;
+ pstring remark;
+ pstring path;
+ pstring passwd;
+ SEC_DESC *sd;
+ size_t sd_size;
+ TALLOC_CTX *ctx = p->mem_ctx;
+
+
+ ZERO_STRUCTP(sh502);
+
+ pstrcpy(net_name, lp_servicename(snum));
+ pstrcpy(remark, lp_comment(snum));
+ standard_sub_conn(p->conn, remark,sizeof(remark));
+ pstrcpy(path, "C:");
+ pstrcat(path, lp_pathname(snum));
+
+ /*
+ * Change / to \\ so that win2k will see it as a valid path. This was added to
+ * enable use of browsing in win2k add share dialog.
+ */
+
+ string_replace(path, '/', '\\');
+
+ pstrcpy(passwd, "");
+ len_net_name = strlen(net_name);
+
+ sd = get_share_security(ctx, snum, &sd_size);
+
+ init_srv_share_info502(&sh502->info_502, net_name, get_share_type(snum), remark, 0, 0xffffffff, 1, path, passwd, sd, sd_size);
+ init_srv_share_info502_str(&sh502->info_502_str, net_name, remark, path, passwd, sd, sd_size);
+}
+
+/***************************************************************************
+ Fill in a share info level 1004 structure.
+ ***************************************************************************/
+
+static void init_srv_share_info_1004(pipes_struct *p, SRV_SHARE_INFO_1004* sh1004, int snum)
+{
+ pstring remark;
+
+ pstrcpy(remark, lp_comment(snum));
+ standard_sub_conn(p->conn, remark, sizeof(remark));
+
+ ZERO_STRUCTP(sh1004);
+
+ init_srv_share_info1004(&sh1004->info_1004, remark);
+ init_srv_share_info1004_str(&sh1004->info_1004_str, remark);
+}
+
+/***************************************************************************
+ Fill in a share info level 1005 structure.
+ ***************************************************************************/
+
+static void init_srv_share_info_1005(pipes_struct *p, SRV_SHARE_INFO_1005* sh1005, int snum)
+{
+ sh1005->share_info_flags = 0;
+
+ if(lp_host_msdfs() && lp_msdfs_root(snum))
+ sh1005->share_info_flags |=
+ SHARE_1005_IN_DFS | SHARE_1005_DFS_ROOT;
+ sh1005->share_info_flags |=
+ lp_csc_policy(snum) << SHARE_1005_CSC_POLICY_SHIFT;
+}
+/***************************************************************************
+ Fill in a share info level 1006 structure.
+ ***************************************************************************/
+
+static void init_srv_share_info_1006(pipes_struct *p, SRV_SHARE_INFO_1006* sh1006, int snum)
+{
+ sh1006->max_uses = -1;
+}
+
+/***************************************************************************
+ Fill in a share info level 1007 structure.
+ ***************************************************************************/
+
+static void init_srv_share_info_1007(pipes_struct *p, SRV_SHARE_INFO_1007* sh1007, int snum)
+{
+ pstring alternate_directory_name = "";
+ uint32 flags = 0;
+
+ ZERO_STRUCTP(sh1007);
+
+ init_srv_share_info1007(&sh1007->info_1007, flags, alternate_directory_name);
+ init_srv_share_info1007_str(&sh1007->info_1007_str, alternate_directory_name);
+}
+
+/*******************************************************************
+ Fill in a share info level 1501 structure.
+ ********************************************************************/
+
+static void init_srv_share_info_1501(pipes_struct *p, SRV_SHARE_INFO_1501 *sh1501, int snum)
+{
+ SEC_DESC *sd;
+ size_t sd_size;
+ TALLOC_CTX *ctx = p->mem_ctx;
+
+ ZERO_STRUCTP(sh1501);
+
+ sd = get_share_security(ctx, snum, &sd_size);
+
+ sh1501->sdb = make_sec_desc_buf(p->mem_ctx, sd_size, sd);
+}
+
+/*******************************************************************
+ True if it ends in '$'.
+ ********************************************************************/
+
+static BOOL is_hidden_share(int snum)
+{
+ const char *net_name = lp_servicename(snum);
+
+ return (net_name[strlen(net_name) - 1] == '$') ? True : False;
+}
+
+/*******************************************************************
+ Fill in a share info structure.
+ ********************************************************************/
+
+static BOOL init_srv_share_info_ctr(pipes_struct *p, SRV_SHARE_INFO_CTR *ctr,
+ uint32 info_level, uint32 *resume_hnd, uint32 *total_entries, BOOL all_shares)
+{
+ int num_entries = 0;
+ int num_services = lp_numservices();
+ int snum;
+ TALLOC_CTX *ctx = p->mem_ctx;
+
+ DEBUG(5,("init_srv_share_info_ctr\n"));
+
+ ZERO_STRUCTPN(ctr);
+
+ ctr->info_level = ctr->switch_value = info_level;
+ *resume_hnd = 0;
+
+ /* Count the number of entries. */
+ for (snum = 0; snum < num_services; snum++) {
+ if (lp_browseable(snum) && lp_snum_ok(snum) && (all_shares || !is_hidden_share(snum)) )
+ num_entries++;
+ }
+
+ *total_entries = num_entries;
+ ctr->num_entries2 = ctr->num_entries = num_entries;
+ ctr->ptr_share_info = ctr->ptr_entries = 1;
+
+ if (!num_entries)
+ return True;
+
+ switch (info_level) {
+ case 0:
+ {
+ SRV_SHARE_INFO_0 *info0;
+ int i = 0;
+
+ info0 = talloc(ctx, num_entries * sizeof(SRV_SHARE_INFO_0));
+
+ for (snum = *resume_hnd; snum < num_services; snum++) {
+ if (lp_browseable(snum) && lp_snum_ok(snum) && (all_shares || !is_hidden_share(snum)) ) {
+ init_srv_share_info_0(p, &info0[i++], snum);
+ }
+ }
+
+ ctr->share.info0 = info0;
+ break;
+
+ }
+
+ case 1:
+ {
+ SRV_SHARE_INFO_1 *info1;
+ int i = 0;
+
+ info1 = talloc(ctx, num_entries * sizeof(SRV_SHARE_INFO_1));
+
+ for (snum = *resume_hnd; snum < num_services; snum++) {
+ if (lp_browseable(snum) && lp_snum_ok(snum) && (all_shares || !is_hidden_share(snum)) ) {
+ init_srv_share_info_1(p, &info1[i++], snum);
+ }
+ }
+
+ ctr->share.info1 = info1;
+ break;
+ }
+
+ case 2:
+ {
+ SRV_SHARE_INFO_2 *info2;
+ int i = 0;
+
+ info2 = talloc(ctx, num_entries * sizeof(SRV_SHARE_INFO_2));
+
+ for (snum = *resume_hnd; snum < num_services; snum++) {
+ if (lp_browseable(snum) && lp_snum_ok(snum) && (all_shares || !is_hidden_share(snum)) ) {
+ init_srv_share_info_2(p, &info2[i++], snum);
+ }
+ }
+
+ ctr->share.info2 = info2;
+ break;
+ }
+
+ case 501:
+ {
+ SRV_SHARE_INFO_501 *info501;
+ int i = 0;
+
+ info501 = talloc(ctx, num_entries * sizeof(SRV_SHARE_INFO_501));
+
+ for (snum = *resume_hnd; snum < num_services; snum++) {
+ if (lp_browseable(snum) && lp_snum_ok(snum) && (all_shares || !is_hidden_share(snum)) ) {
+ init_srv_share_info_501(p, &info501[i++], snum);
+ }
+ }
+
+ ctr->share.info501 = info501;
+ break;
+ }
+
+ case 502:
+ {
+ SRV_SHARE_INFO_502 *info502;
+ int i = 0;
+
+ info502 = talloc(ctx, num_entries * sizeof(SRV_SHARE_INFO_502));
+
+ for (snum = *resume_hnd; snum < num_services; snum++) {
+ if (lp_browseable(snum) && lp_snum_ok(snum) && (all_shares || !is_hidden_share(snum)) ) {
+ init_srv_share_info_502(p, &info502[i++], snum);
+ }
+ }
+
+ ctr->share.info502 = info502;
+ break;
+ }
+
+ /* here for completeness but not currently used with enum (1004 - 1501)*/
+
+ case 1004:
+ {
+ SRV_SHARE_INFO_1004 *info1004;
+ int i = 0;
+
+ info1004 = talloc(ctx, num_entries * sizeof(SRV_SHARE_INFO_1004));
+
+ for (snum = *resume_hnd; snum < num_services; snum++) {
+ if (lp_browseable(snum) && lp_snum_ok(snum) && (all_shares || !is_hidden_share(snum)) ) {
+ init_srv_share_info_1004(p, &info1004[i++], snum);
+ }
+ }
+
+ ctr->share.info1004 = info1004;
+ break;
+ }
+
+ case 1005:
+ {
+ SRV_SHARE_INFO_1005 *info1005;
+ int i = 0;
+
+ info1005 = talloc(ctx, num_entries * sizeof(SRV_SHARE_INFO_1005));
+
+ for (snum = *resume_hnd; snum < num_services; snum++) {
+ if (lp_browseable(snum) && lp_snum_ok(snum) && (all_shares || !is_hidden_share(snum)) ) {
+ init_srv_share_info_1005(p, &info1005[i++], snum);
+ }
+ }
+
+ ctr->share.info1005 = info1005;
+ break;
+ }
+
+ case 1006:
+ {
+ SRV_SHARE_INFO_1006 *info1006;
+ int i = 0;
+
+ info1006 = talloc(ctx, num_entries * sizeof(SRV_SHARE_INFO_1006));
+
+ for (snum = *resume_hnd; snum < num_services; snum++) {
+ if (lp_browseable(snum) && lp_snum_ok(snum) && (all_shares || !is_hidden_share(snum)) ) {
+ init_srv_share_info_1006(p, &info1006[i++], snum);
+ }
+ }
+
+ ctr->share.info1006 = info1006;
+ break;
+ }
+
+ case 1007:
+ {
+ SRV_SHARE_INFO_1007 *info1007;
+ int i = 0;
+
+ info1007 = talloc(ctx, num_entries * sizeof(SRV_SHARE_INFO_1007));
+
+ for (snum = *resume_hnd; snum < num_services; snum++) {
+ if (lp_browseable(snum) && lp_snum_ok(snum) && (all_shares || !is_hidden_share(snum)) ) {
+ init_srv_share_info_1007(p, &info1007[i++], snum);
+ }
+ }
+
+ ctr->share.info1007 = info1007;
+ break;
+ }
+
+ case 1501:
+ {
+ SRV_SHARE_INFO_1501 *info1501;
+ int i = 0;
+
+ info1501 = talloc(ctx, num_entries * sizeof(SRV_SHARE_INFO_1501));
+
+ for (snum = *resume_hnd; snum < num_services; snum++) {
+ if (lp_browseable(snum) && lp_snum_ok(snum) && (all_shares || !is_hidden_share(snum)) ) {
+ init_srv_share_info_1501(p, &info1501[i++], snum);
+ }
+ }
+
+ ctr->share.info1501 = info1501;
+ break;
+ }
+ default:
+ DEBUG(5,("init_srv_share_info_ctr: unsupported switch value %d\n", info_level));
+ return False;
+ }
+
+ return True;
+}
+
+/*******************************************************************
+ Inits a SRV_R_NET_SHARE_ENUM structure.
+********************************************************************/
+
+static void init_srv_r_net_share_enum(pipes_struct *p, SRV_R_NET_SHARE_ENUM *r_n,
+ uint32 info_level, uint32 resume_hnd, BOOL all)
+{
+ DEBUG(5,("init_srv_r_net_share_enum: %d\n", __LINE__));
+
+ if (init_srv_share_info_ctr(p, &r_n->ctr, info_level,
+ &resume_hnd, &r_n->total_entries, all)) {
+ r_n->status = WERR_OK;
+ } else {
+ r_n->status = WERR_UNKNOWN_LEVEL;
+ }
+
+ init_enum_hnd(&r_n->enum_hnd, resume_hnd);
+}
+
+/*******************************************************************
+ Inits a SRV_R_NET_SHARE_GET_INFO structure.
+********************************************************************/
+
+static void init_srv_r_net_share_get_info(pipes_struct *p, SRV_R_NET_SHARE_GET_INFO *r_n,
+ char *share_name, uint32 info_level)
+{
+ WERROR status = WERR_OK;
+ int snum;
+
+ DEBUG(5,("init_srv_r_net_share_get_info: %d\n", __LINE__));
+
+ r_n->info.switch_value = info_level;
+
+ snum = find_service(share_name);
+
+ if (snum >= 0) {
+ switch (info_level) {
+ case 0:
+ init_srv_share_info_0(p, &r_n->info.share.info0, snum);
+ break;
+ case 1:
+ init_srv_share_info_1(p, &r_n->info.share.info1, snum);
+ break;
+ case 2:
+ init_srv_share_info_2(p, &r_n->info.share.info2, snum);
+ break;
+ case 501:
+ init_srv_share_info_501(p, &r_n->info.share.info501, snum);
+ break;
+ case 502:
+ init_srv_share_info_502(p, &r_n->info.share.info502, snum);
+ break;
+
+ /* here for completeness */
+ case 1004:
+ init_srv_share_info_1004(p, &r_n->info.share.info1004, snum);
+ break;
+ case 1005:
+ init_srv_share_info_1005(p, &r_n->info.share.info1005, snum);
+ break;
+
+ /* here for completeness 1006 - 1501 */
+ case 1006:
+ init_srv_share_info_1006(p, &r_n->info.share.info1006, snum);
+ break;
+ case 1007:
+ init_srv_share_info_1007(p, &r_n->info.share.info1007, snum);
+ break;
+ case 1501:
+ init_srv_share_info_1501(p, &r_n->info.share.info1501, snum);
+ break;
+ default:
+ DEBUG(5,("init_srv_net_share_get_info: unsupported switch value %d\n", info_level));
+ status = WERR_UNKNOWN_LEVEL;
+ break;
+ }
+ } else {
+ status = WERR_INVALID_NAME;
+ }
+
+ r_n->info.ptr_share_ctr = W_ERROR_IS_OK(status) ? 1 : 0;
+ r_n->status = status;
+}
+
+/*******************************************************************
+ fill in a sess info level 1 structure.
+ ********************************************************************/
+
+static void init_srv_sess_0_info(SESS_INFO_0 *se0, SESS_INFO_0_STR *str0, char *name)
+{
+ init_srv_sess_info0(se0, name);
+ init_srv_sess_info0_str(str0, name);
+}
+
+/*******************************************************************
+ fill in a sess info level 0 structure.
+ ********************************************************************/
+
+static void init_srv_sess_info_0(SRV_SESS_INFO_0 *ss0, uint32 *snum, uint32 *stot)
+{
+ struct sessionid *session_list;
+ uint32 num_entries = 0;
+ (*stot) = list_sessions(&session_list);
+
+ if (ss0 == NULL) {
+ (*snum) = 0;
+ SAFE_FREE(session_list);
+ return;
+ }
+
+ DEBUG(5,("init_srv_sess_0_ss0\n"));
+
+ if (snum) {
+ for (; (*snum) < (*stot) && num_entries < MAX_SESS_ENTRIES; (*snum)++) {
+ init_srv_sess_0_info(&ss0->info_0[num_entries],
+ &ss0->info_0_str[num_entries], session_list[(*snum)].remote_machine);
+
+ /* move on to creating next session */
+ /* move on to creating next sess */
+ num_entries++;
+ }
+
+ ss0->num_entries_read = num_entries;
+ ss0->ptr_sess_info = num_entries > 0 ? 1 : 0;
+ ss0->num_entries_read2 = num_entries;
+
+ if ((*snum) >= (*stot)) {
+ (*snum) = 0;
+ }
+
+ } else {
+ ss0->num_entries_read = 0;
+ ss0->ptr_sess_info = 0;
+ ss0->num_entries_read2 = 0;
+ }
+ SAFE_FREE(session_list);
+}
+
+/*******************************************************************
+ fill in a sess info level 1 structure.
+ ********************************************************************/
+
+static void init_srv_sess_1_info(SESS_INFO_1 *se1, SESS_INFO_1_STR *str1,
+ char *name, char *user,
+ uint32 num_opens,
+ uint32 open_time, uint32 idle_time,
+ uint32 usr_flgs)
+{
+ init_srv_sess_info1(se1 , name, user, num_opens, open_time, idle_time, usr_flgs);
+ init_srv_sess_info1_str(str1, name, user);
+}
+
+/*******************************************************************
+ fill in a sess info level 1 structure.
+ ********************************************************************/
+
+static void init_srv_sess_info_1(SRV_SESS_INFO_1 *ss1, uint32 *snum, uint32 *stot)
+{
+ struct sessionid *session_list;
+ uint32 num_entries = 0;
+ (*stot) = list_sessions(&session_list);
+
+ if (ss1 == NULL) {
+ (*snum) = 0;
+ SAFE_FREE(session_list);
+ return;
+ }
+
+ DEBUG(5,("init_srv_sess_1_ss1\n"));
+
+ if (snum) {
+ for (; (*snum) < (*stot) && num_entries < MAX_SESS_ENTRIES; (*snum)++) {
+ init_srv_sess_1_info(&ss1->info_1[num_entries],
+ &ss1->info_1_str[num_entries],
+ session_list[*snum].remote_machine,
+ session_list[*snum].username,
+ 1, 10, 5, 0);
+
+ /* move on to creating next session */
+ /* move on to creating next sess */
+ num_entries++;
+ }
+
+ ss1->num_entries_read = num_entries;
+ ss1->ptr_sess_info = num_entries > 0 ? 1 : 0;
+ ss1->num_entries_read2 = num_entries;
+
+ if ((*snum) >= (*stot)) {
+ (*snum) = 0;
+ }
+
+ } else {
+ ss1->num_entries_read = 0;
+ ss1->ptr_sess_info = 0;
+ ss1->num_entries_read2 = 0;
+
+ (*stot) = 0;
+ }
+}
+
+/*******************************************************************
+ makes a SRV_R_NET_SESS_ENUM structure.
+********************************************************************/
+
+static WERROR init_srv_sess_info_ctr(SRV_SESS_INFO_CTR *ctr,
+ int switch_value, uint32 *resume_hnd, uint32 *total_entries)
+{
+ WERROR status = WERR_OK;
+ DEBUG(5,("init_srv_sess_info_ctr: %d\n", __LINE__));
+
+ ctr->switch_value = switch_value;
+
+ switch (switch_value) {
+ case 0:
+ init_srv_sess_info_0(&(ctr->sess.info0), resume_hnd, total_entries);
+ ctr->ptr_sess_ctr = 1;
+ break;
+ case 1:
+ init_srv_sess_info_1(&(ctr->sess.info1), resume_hnd, total_entries);
+ ctr->ptr_sess_ctr = 1;
+ break;
+ default:
+ DEBUG(5,("init_srv_sess_info_ctr: unsupported switch value %d\n", switch_value));
+ (*resume_hnd) = 0;
+ (*total_entries) = 0;
+ ctr->ptr_sess_ctr = 0;
+ status = WERR_UNKNOWN_LEVEL;
+ break;
+ }
+
+ return status;
+}
+
+/*******************************************************************
+ makes a SRV_R_NET_SESS_ENUM structure.
+********************************************************************/
+
+static void init_srv_r_net_sess_enum(SRV_R_NET_SESS_ENUM *r_n,
+ uint32 resume_hnd, int sess_level, int switch_value)
+{
+ DEBUG(5,("init_srv_r_net_sess_enum: %d\n", __LINE__));
+
+ r_n->sess_level = sess_level;
+
+ if (sess_level == -1)
+ r_n->status = WERR_UNKNOWN_LEVEL;
+ else
+ r_n->status = init_srv_sess_info_ctr(r_n->ctr, switch_value, &resume_hnd, &r_n->total_entries);
+
+ if (!W_ERROR_IS_OK(r_n->status))
+ resume_hnd = 0;
+
+ init_enum_hnd(&r_n->enum_hnd, resume_hnd);
+}
+
+/*******************************************************************
+ fill in a conn info level 0 structure.
+ ********************************************************************/
+
+static void init_srv_conn_info_0(SRV_CONN_INFO_0 *ss0, uint32 *snum, uint32 *stot)
+{
+ uint32 num_entries = 0;
+ (*stot) = 1;
+
+ if (ss0 == NULL) {
+ (*snum) = 0;
+ return;
+ }
+
+ DEBUG(5,("init_srv_conn_0_ss0\n"));
+
+ if (snum) {
+ for (; (*snum) < (*stot) && num_entries < MAX_CONN_ENTRIES; (*snum)++) {
+
+ init_srv_conn_info0(&ss0->info_0[num_entries], (*stot));
+
+ /* move on to creating next connection */
+ /* move on to creating next conn */
+ num_entries++;
+ }
+
+ ss0->num_entries_read = num_entries;
+ ss0->ptr_conn_info = num_entries > 0 ? 1 : 0;
+ ss0->num_entries_read2 = num_entries;
+
+ if ((*snum) >= (*stot)) {
+ (*snum) = 0;
+ }
+
+ } else {
+ ss0->num_entries_read = 0;
+ ss0->ptr_conn_info = 0;
+ ss0->num_entries_read2 = 0;
+
+ (*stot) = 0;
+ }
+}
+
+/*******************************************************************
+ fill in a conn info level 1 structure.
+ ********************************************************************/
+
+static void init_srv_conn_1_info(CONN_INFO_1 *se1, CONN_INFO_1_STR *str1,
+ uint32 id, uint32 type,
+ uint32 num_opens, uint32 num_users, uint32 open_time,
+ const char *usr_name, const char *net_name)
+{
+ init_srv_conn_info1(se1 , id, type, num_opens, num_users, open_time, usr_name, net_name);
+ init_srv_conn_info1_str(str1, usr_name, net_name);
+}
+
+/*******************************************************************
+ fill in a conn info level 1 structure.
+ ********************************************************************/
+
+static void init_srv_conn_info_1(SRV_CONN_INFO_1 *ss1, uint32 *snum, uint32 *stot)
+{
+ uint32 num_entries = 0;
+ (*stot) = 1;
+
+ if (ss1 == NULL) {
+ (*snum) = 0;
+ return;
+ }
+
+ DEBUG(5,("init_srv_conn_1_ss1\n"));
+
+ if (snum) {
+ for (; (*snum) < (*stot) && num_entries < MAX_CONN_ENTRIES; (*snum)++) {
+ init_srv_conn_1_info(&ss1->info_1[num_entries],
+ &ss1->info_1_str[num_entries],
+ (*stot), 0x3, 1, 1, 3,"dummy_user", "IPC$");
+
+ /* move on to creating next connection */
+ /* move on to creating next conn */
+ num_entries++;
+ }
+
+ ss1->num_entries_read = num_entries;
+ ss1->ptr_conn_info = num_entries > 0 ? 1 : 0;
+ ss1->num_entries_read2 = num_entries;
+
+
+ if ((*snum) >= (*stot)) {
+ (*snum) = 0;
+ }
+
+ } else {
+ ss1->num_entries_read = 0;
+ ss1->ptr_conn_info = 0;
+ ss1->num_entries_read2 = 0;
+
+ (*stot) = 0;
+ }
+}
+
+/*******************************************************************
+ makes a SRV_R_NET_CONN_ENUM structure.
+********************************************************************/
+
+static WERROR init_srv_conn_info_ctr(SRV_CONN_INFO_CTR *ctr,
+ int switch_value, uint32 *resume_hnd, uint32 *total_entries)
+{
+ WERROR status = WERR_OK;
+ DEBUG(5,("init_srv_conn_info_ctr: %d\n", __LINE__));
+
+ ctr->switch_value = switch_value;
+
+ switch (switch_value) {
+ case 0:
+ init_srv_conn_info_0(&ctr->conn.info0, resume_hnd, total_entries);
+ ctr->ptr_conn_ctr = 1;
+ break;
+ case 1:
+ init_srv_conn_info_1(&ctr->conn.info1, resume_hnd, total_entries);
+ ctr->ptr_conn_ctr = 1;
+ break;
+ default:
+ DEBUG(5,("init_srv_conn_info_ctr: unsupported switch value %d\n", switch_value));
+ (*resume_hnd = 0);
+ (*total_entries) = 0;
+ ctr->ptr_conn_ctr = 0;
+ status = WERR_UNKNOWN_LEVEL;
+ break;
+ }
+
+ return status;
+}
+
+/*******************************************************************
+ makes a SRV_R_NET_CONN_ENUM structure.
+********************************************************************/
+
+static void init_srv_r_net_conn_enum(SRV_R_NET_CONN_ENUM *r_n,
+ uint32 resume_hnd, int conn_level, int switch_value)
+{
+ DEBUG(5,("init_srv_r_net_conn_enum: %d\n", __LINE__));
+
+ r_n->conn_level = conn_level;
+ if (conn_level == -1)
+ r_n->status = WERR_UNKNOWN_LEVEL;
+ else
+ r_n->status = init_srv_conn_info_ctr(r_n->ctr, switch_value, &resume_hnd, &r_n->total_entries);
+
+ if (!W_ERROR_IS_OK(r_n->status))
+ resume_hnd = 0;
+
+ init_enum_hnd(&r_n->enum_hnd, resume_hnd);
+}
+
+/*******************************************************************
+ makes a SRV_R_NET_FILE_ENUM structure.
+********************************************************************/
+
+static WERROR init_srv_file_info_ctr(pipes_struct *p, SRV_FILE_INFO_CTR *ctr,
+ int switch_value, uint32 *resume_hnd,
+ uint32 *total_entries)
+{
+ WERROR status = WERR_OK;
+ TALLOC_CTX *ctx = p->mem_ctx;
+ DEBUG(5,("init_srv_file_info_ctr: %d\n", __LINE__));
+ *total_entries = 1; /* dummy entries only, for */
+
+ ctr->switch_value = switch_value;
+ ctr->num_entries = *total_entries - *resume_hnd;
+ ctr->num_entries2 = ctr->num_entries;
+
+ switch (switch_value) {
+ case 3: {
+ int i;
+ if (*total_entries > 0) {
+ ctr->ptr_entries = 1;
+ ctr->file.info3 = talloc(ctx, ctr->num_entries *
+ sizeof(SRV_FILE_INFO_3));
+ }
+ for (i=0 ;i<ctr->num_entries;i++) {
+ init_srv_file_info3(&ctr->file.info3[i].info_3, i+*resume_hnd, 0x35, 0, "\\PIPE\\samr", "dummy user");
+ init_srv_file_info3_str(&ctr->file.info3[i].info_3_str, "\\PIPE\\samr", "dummy user");
+
+ }
+ ctr->ptr_file_info = 1;
+ *resume_hnd = 0;
+ break;
+ }
+ default:
+ DEBUG(5,("init_srv_file_info_ctr: unsupported switch value %d\n", switch_value));
+ (*resume_hnd = 0);
+ (*total_entries) = 0;
+ ctr->ptr_entries = 0;
+ status = WERR_UNKNOWN_LEVEL;
+ break;
+ }
+
+ return status;
+}
+
+/*******************************************************************
+ makes a SRV_R_NET_FILE_ENUM structure.
+********************************************************************/
+
+static void init_srv_r_net_file_enum(pipes_struct *p, SRV_R_NET_FILE_ENUM *r_n,
+ uint32 resume_hnd, int file_level, int switch_value)
+{
+ DEBUG(5,("init_srv_r_net_file_enum: %d\n", __LINE__));
+
+ r_n->file_level = file_level;
+ if (file_level == 0)
+ r_n->status = WERR_UNKNOWN_LEVEL;
+ else
+ r_n->status = init_srv_file_info_ctr(p, &r_n->ctr, switch_value, &resume_hnd, &(r_n->total_entries));
+
+ if (!W_ERROR_IS_OK(r_n->status))
+ resume_hnd = 0;
+
+ init_enum_hnd(&r_n->enum_hnd, resume_hnd);
+}
+
+/*******************************************************************
+net server get info
+********************************************************************/
+
+WERROR _srv_net_srv_get_info(pipes_struct *p, SRV_Q_NET_SRV_GET_INFO *q_u, SRV_R_NET_SRV_GET_INFO *r_u)
+{
+ WERROR status = WERR_OK;
+ SRV_INFO_CTR *ctr = (SRV_INFO_CTR *)talloc(p->mem_ctx, sizeof(SRV_INFO_CTR));
+
+ if (!ctr)
+ return WERR_NOMEM;
+
+ ZERO_STRUCTP(ctr);
+
+ DEBUG(5,("srv_net_srv_get_info: %d\n", __LINE__));
+
+ if (!pipe_access_check(p)) {
+ DEBUG(3, ("access denied to srv_net_srv_get_info\n"));
+ return WERR_ACCESS_DENIED;
+ }
+
+ switch (q_u->switch_value) {
+
+ /* Technically level 102 should only be available to
+ Administrators but there isn't anything super-secret
+ here, as most of it is made up. */
+
+ case 102:
+ init_srv_info_102(&ctr->srv.sv102,
+ 500, global_myname(),
+ string_truncate(lp_serverstring(), MAX_SERVER_STRING_LENGTH),
+ lp_major_announce_version(), lp_minor_announce_version(),
+ lp_default_server_announce(),
+ 0xffffffff, /* users */
+ 0xf, /* disc */
+ 0, /* hidden */
+ 240, /* announce */
+ 3000, /* announce delta */
+ 100000, /* licenses */
+ "c:\\"); /* user path */
+ break;
+ case 101:
+ init_srv_info_101(&ctr->srv.sv101,
+ 500, global_myname(),
+ lp_major_announce_version(), lp_minor_announce_version(),
+ lp_default_server_announce(),
+ string_truncate(lp_serverstring(), MAX_SERVER_STRING_LENGTH));
+ break;
+ case 100:
+ init_srv_info_100(&ctr->srv.sv100, 500, global_myname());
+ break;
+ default:
+ status = WERR_UNKNOWN_LEVEL;
+ break;
+ }
+
+ /* set up the net server get info structure */
+ init_srv_r_net_srv_get_info(r_u, q_u->switch_value, ctr, status);
+
+ DEBUG(5,("srv_net_srv_get_info: %d\n", __LINE__));
+
+ return r_u->status;
+}
+
+/*******************************************************************
+net server set info
+********************************************************************/
+
+WERROR _srv_net_srv_set_info(pipes_struct *p, SRV_Q_NET_SRV_SET_INFO *q_u, SRV_R_NET_SRV_SET_INFO *r_u)
+{
+ WERROR status = WERR_OK;
+
+ DEBUG(5,("srv_net_srv_set_info: %d\n", __LINE__));
+
+ /* Set up the net server set info structure. */
+
+ init_srv_r_net_srv_set_info(r_u, 0x0, status);
+
+ DEBUG(5,("srv_net_srv_set_info: %d\n", __LINE__));
+
+ return r_u->status;
+}
+
+/*******************************************************************
+net file enum
+********************************************************************/
+
+WERROR _srv_net_file_enum(pipes_struct *p, SRV_Q_NET_FILE_ENUM *q_u, SRV_R_NET_FILE_ENUM *r_u)
+{
+ DEBUG(5,("srv_net_file_enum: %d\n", __LINE__));
+
+ /* set up the */
+ init_srv_r_net_file_enum(p, r_u,
+ get_enum_hnd(&q_u->enum_hnd),
+ q_u->file_level,
+ q_u->ctr.switch_value);
+
+ DEBUG(5,("srv_net_file_enum: %d\n", __LINE__));
+
+ return r_u->status;
+}
+
+/*******************************************************************
+net conn enum
+********************************************************************/
+
+WERROR _srv_net_conn_enum(pipes_struct *p, SRV_Q_NET_CONN_ENUM *q_u, SRV_R_NET_CONN_ENUM *r_u)
+{
+ DEBUG(5,("srv_net_conn_enum: %d\n", __LINE__));
+
+ r_u->ctr = (SRV_CONN_INFO_CTR *)talloc(p->mem_ctx, sizeof(SRV_CONN_INFO_CTR));
+ if (!r_u->ctr)
+ return WERR_NOMEM;
+
+ ZERO_STRUCTP(r_u->ctr);
+
+ /* set up the */
+ init_srv_r_net_conn_enum(r_u,
+ get_enum_hnd(&q_u->enum_hnd),
+ q_u->conn_level,
+ q_u->ctr->switch_value);
+
+ DEBUG(5,("srv_net_conn_enum: %d\n", __LINE__));
+
+ return r_u->status;
+}
+
+/*******************************************************************
+net sess enum
+********************************************************************/
+
+WERROR _srv_net_sess_enum(pipes_struct *p, SRV_Q_NET_SESS_ENUM *q_u, SRV_R_NET_SESS_ENUM *r_u)
+{
+ DEBUG(5,("_srv_net_sess_enum: %d\n", __LINE__));
+
+ r_u->ctr = (SRV_SESS_INFO_CTR *)talloc(p->mem_ctx, sizeof(SRV_SESS_INFO_CTR));
+ if (!r_u->ctr)
+ return WERR_NOMEM;
+
+ ZERO_STRUCTP(r_u->ctr);
+
+ /* set up the */
+ init_srv_r_net_sess_enum(r_u,
+ get_enum_hnd(&q_u->enum_hnd),
+ q_u->sess_level,
+ q_u->ctr->switch_value);
+
+ DEBUG(5,("_srv_net_sess_enum: %d\n", __LINE__));
+
+ return r_u->status;
+}
+
+/*******************************************************************
+ Net share enum all.
+********************************************************************/
+
+WERROR _srv_net_share_enum_all(pipes_struct *p, SRV_Q_NET_SHARE_ENUM *q_u, SRV_R_NET_SHARE_ENUM *r_u)
+{
+ DEBUG(5,("_srv_net_share_enum: %d\n", __LINE__));
+
+ if (!pipe_access_check(p)) {
+ DEBUG(3, ("access denied to srv_net_share_enum_all\n"));
+ return WERR_ACCESS_DENIED;
+ }
+
+ /* Create the list of shares for the response. */
+ init_srv_r_net_share_enum(p, r_u,
+ q_u->ctr.info_level,
+ get_enum_hnd(&q_u->enum_hnd), True);
+
+ DEBUG(5,("_srv_net_share_enum: %d\n", __LINE__));
+
+ return r_u->status;
+}
+
+/*******************************************************************
+ Net share enum.
+********************************************************************/
+
+WERROR _srv_net_share_enum(pipes_struct *p, SRV_Q_NET_SHARE_ENUM *q_u, SRV_R_NET_SHARE_ENUM *r_u)
+{
+ DEBUG(5,("_srv_net_share_enum: %d\n", __LINE__));
+
+ if (!pipe_access_check(p)) {
+ DEBUG(3, ("access denied to srv_net_share_enum\n"));
+ return WERR_ACCESS_DENIED;
+ }
+
+ /* Create the list of shares for the response. */
+ init_srv_r_net_share_enum(p, r_u,
+ q_u->ctr.info_level,
+ get_enum_hnd(&q_u->enum_hnd), False);
+
+ DEBUG(5,("_srv_net_share_enum: %d\n", __LINE__));
+
+ return r_u->status;
+}
+
+/*******************************************************************
+ Net share get info.
+********************************************************************/
+
+WERROR _srv_net_share_get_info(pipes_struct *p, SRV_Q_NET_SHARE_GET_INFO *q_u, SRV_R_NET_SHARE_GET_INFO *r_u)
+{
+ fstring share_name;
+
+ DEBUG(5,("_srv_net_share_get_info: %d\n", __LINE__));
+
+ /* Create the list of shares for the response. */
+ unistr2_to_ascii(share_name, &q_u->uni_share_name, sizeof(share_name));
+ init_srv_r_net_share_get_info(p, r_u, share_name, q_u->info_level);
+
+ DEBUG(5,("_srv_net_share_get_info: %d\n", __LINE__));
+
+ return r_u->status;
+}
+
+/*******************************************************************
+ Check a given DOS pathname is valid for a share.
+********************************************************************/
+
+static char *valid_share_pathname(char *dos_pathname)
+{
+ char *ptr;
+
+ /* Convert any '\' paths to '/' */
+ unix_format(dos_pathname);
+ unix_clean_name(dos_pathname);
+
+ /* NT is braindead - it wants a C: prefix to a pathname ! So strip it. */
+ ptr = dos_pathname;
+ if (strlen(dos_pathname) > 2 && ptr[1] == ':' && ptr[0] != '/')
+ ptr += 2;
+
+ /* Only abolute paths allowed. */
+ if (*ptr != '/')
+ return NULL;
+
+ return ptr;
+}
+
+static BOOL exist_share_pathname(char *unix_pathname)
+{
+ pstring saved_pathname;
+ int ret;
+
+ /* Can we cd to it ? */
+
+ /* First save our current directory. */
+ if (getcwd(saved_pathname, sizeof(saved_pathname)) == NULL)
+ return False;
+
+ ret = chdir(unix_pathname);
+
+ /* We *MUST* be able to chdir back. Abort if we can't. */
+ if (chdir(saved_pathname) == -1)
+ smb_panic("valid_share_pathname: Unable to restore current directory.\n");
+
+ if (ret == -1) return False;
+
+ return True;
+}
+
+/*******************************************************************
+ Net share set info. Modify share details.
+********************************************************************/
+
+WERROR _srv_net_share_set_info(pipes_struct *p, SRV_Q_NET_SHARE_SET_INFO *q_u, SRV_R_NET_SHARE_SET_INFO *r_u)
+{
+ struct current_user user;
+ pstring command;
+ fstring share_name;
+ fstring comment;
+ pstring pathname;
+ int type;
+ int snum;
+ int ret;
+ char *path;
+ SEC_DESC *psd = NULL;
+
+ DEBUG(5,("_srv_net_share_set_info: %d\n", __LINE__));
+
+ unistr2_to_ascii(share_name, &q_u->uni_share_name, sizeof(share_name));
+
+ r_u->parm_error = 0;
+
+ if (strequal(share_name,"IPC$") || strequal(share_name,"ADMIN$") || strequal(share_name,"global"))
+ return WERR_ACCESS_DENIED;
+
+ snum = find_service(share_name);
+
+ /* Does this share exist ? */
+ if (snum < 0)
+ return WERR_INVALID_NAME;
+
+ /* No change to printer shares. */
+ if (lp_print_ok(snum))
+ return WERR_ACCESS_DENIED;
+
+ get_current_user(&user,p);
+
+ if (user.uid != sec_initial_uid())
+ return WERR_ACCESS_DENIED;
+
+ switch (q_u->info_level) {
+ case 1:
+ pstrcpy(pathname, lp_pathname(snum));
+ unistr2_to_ascii(comment, &q_u->info.share.info2.info_2_str.uni_remark, sizeof(comment));
+ type = q_u->info.share.info2.info_2.type;
+ psd = NULL;
+ break;
+ case 2:
+ unistr2_to_ascii(comment, &q_u->info.share.info2.info_2_str.uni_remark, sizeof(comment));
+ unistr2_to_ascii(pathname, &q_u->info.share.info2.info_2_str.uni_path, sizeof(pathname));
+ type = q_u->info.share.info2.info_2.type;
+ psd = NULL;
+ break;
+#if 0
+ /* not supported on set but here for completeness */
+ case 501:
+ unistr2_to_ascii(comment, &q_u->info.share.info501.info_501_str.uni_remark, sizeof(comment));
+ type = q_u->info.share.info501.info_501.type;
+ psd = NULL;
+ break;
+#endif
+ case 502:
+ unistr2_to_ascii(comment, &q_u->info.share.info502.info_502_str.uni_remark, sizeof(comment));
+ unistr2_to_ascii(pathname, &q_u->info.share.info502.info_502_str.uni_path, sizeof(pathname));
+ type = q_u->info.share.info502.info_502.type;
+ psd = q_u->info.share.info502.info_502_str.sd;
+ map_generic_share_sd_bits(psd);
+ break;
+ case 1004:
+ pstrcpy(pathname, lp_pathname(snum));
+ unistr2_to_ascii(comment, &q_u->info.share.info1004.info_1004_str.uni_remark, sizeof(comment));
+ type = STYPE_DISKTREE;
+ break;
+ case 1005:
+ /* XP re-sets the csc policy even if it wasn't changed by the
+ user, so we must compare it to see if it's what is set in
+ smb.conf, so that we can contine other ops like setting
+ ACLs on a share */
+ if (((q_u->info.share.info1005.share_info_flags &
+ SHARE_1005_CSC_POLICY_MASK) >>
+ SHARE_1005_CSC_POLICY_SHIFT) == lp_csc_policy(snum))
+ return WERR_OK;
+ else {
+ DEBUG(3, ("_srv_net_share_set_info: client is trying to change csc policy from the network; must be done with smb.conf\n"));
+ return WERR_ACCESS_DENIED;
+ }
+ break;
+ case 1006:
+ case 1007:
+ return WERR_ACCESS_DENIED;
+ break;
+ case 1501:
+ pstrcpy(pathname, lp_pathname(snum));
+ fstrcpy(comment, lp_comment(snum));
+ psd = q_u->info.share.info1501.sdb->sec;
+ map_generic_share_sd_bits(psd);
+ type = STYPE_DISKTREE;
+ break;
+ default:
+ DEBUG(5,("_srv_net_share_set_info: unsupported switch value %d\n", q_u->info_level));
+ return WERR_UNKNOWN_LEVEL;
+ }
+
+ /* We can only modify disk shares. */
+ if (type != STYPE_DISKTREE)
+ return WERR_ACCESS_DENIED;
+
+ /* Check if the pathname is valid. */
+ if (!(path = valid_share_pathname( pathname )))
+ return WERR_OBJECT_PATH_INVALID;
+
+ /* Ensure share name, pathname and comment don't contain '"' characters. */
+ string_replace(share_name, '"', ' ');
+ string_replace(path, '"', ' ');
+ string_replace(comment, '"', ' ');
+
+ DEBUG(10,("_srv_net_share_set_info: change share command = %s\n",
+ lp_change_share_cmd() ? lp_change_share_cmd() : "NULL" ));
+
+ /* Only call modify function if something changed. */
+
+ if (strcmp(path, lp_pathname(snum)) || strcmp(comment, lp_comment(snum)) ) {
+ if (!lp_change_share_cmd() || !*lp_change_share_cmd())
+ return WERR_ACCESS_DENIED;
+
+ slprintf(command, sizeof(command)-1, "%s \"%s\" \"%s\" \"%s\" \"%s\"",
+ lp_change_share_cmd(), dyn_CONFIGFILE, share_name, path, comment);
+
+ DEBUG(10,("_srv_net_share_set_info: Running [%s]\n", command ));
+ if ((ret = smbrun(command, NULL)) != 0) {
+ DEBUG(0,("_srv_net_share_set_info: Running [%s] returned (%d)\n", command, ret ));
+ return WERR_ACCESS_DENIED;
+ }
+
+ /* Check if the new share pathname exist, if not return an error */
+ if (!exist_share_pathname(path)) {
+ DEBUG(1, ("_srv_net_share_set_info: change share command was ok but path (%s) has not been created!\n", path));
+ return WERR_OBJECT_PATH_INVALID;
+ }
+
+ /* Tell everyone we updated smb.conf. */
+ message_send_all(conn_tdb_ctx(), MSG_SMB_CONF_UPDATED, NULL, 0, False, NULL);
+
+ } else {
+ DEBUG(10,("_srv_net_share_set_info: No change to share name (%s)\n", share_name ));
+ }
+
+ /* Replace SD if changed. */
+ if (psd) {
+ SEC_DESC *old_sd;
+ size_t sd_size;
+
+ old_sd = get_share_security(p->mem_ctx, snum, &sd_size);
+
+ if (old_sd && !sec_desc_equal(old_sd, psd)) {
+ if (!set_share_security(p->mem_ctx, share_name, psd))
+ DEBUG(0,("_srv_net_share_set_info: Failed to change security info in share %s.\n",
+ share_name ));
+ }
+ }
+
+ DEBUG(5,("_srv_net_share_set_info: %d\n", __LINE__));
+
+ return WERR_OK;
+}
+
+/*******************************************************************
+ Net share add. Call 'add_share_command "sharename" "pathname" "comment" "read only = xxx"'
+********************************************************************/
+
+WERROR _srv_net_share_add(pipes_struct *p, SRV_Q_NET_SHARE_ADD *q_u, SRV_R_NET_SHARE_ADD *r_u)
+{
+ struct current_user user;
+ pstring command;
+ fstring share_name;
+ fstring comment;
+ pstring pathname;
+ int type;
+ int snum;
+ int ret;
+ char *path;
+ SEC_DESC *psd = NULL;
+
+ DEBUG(5,("_srv_net_share_add: %d\n", __LINE__));
+
+ r_u->parm_error = 0;
+
+ get_current_user(&user,p);
+
+ if (user.uid != sec_initial_uid()) {
+ DEBUG(10,("_srv_net_share_add: uid != sec_initial_uid(). Access denied.\n"));
+ return WERR_ACCESS_DENIED;
+ }
+
+ if (!lp_add_share_cmd() || !*lp_add_share_cmd()) {
+ DEBUG(10,("_srv_net_share_add: No add share command\n"));
+ return WERR_ACCESS_DENIED;
+ }
+
+ switch (q_u->info_level) {
+ case 0:
+ /* No path. Not enough info in a level 0 to do anything. */
+ return WERR_ACCESS_DENIED;
+ case 1:
+ /* Not enough info in a level 1 to do anything. */
+ return WERR_ACCESS_DENIED;
+ case 2:
+ unistr2_to_ascii(share_name, &q_u->info.share.info2.info_2_str.uni_netname, sizeof(share_name));
+ unistr2_to_ascii(comment, &q_u->info.share.info2.info_2_str.uni_remark, sizeof(share_name));
+ unistr2_to_ascii(pathname, &q_u->info.share.info2.info_2_str.uni_path, sizeof(share_name));
+ type = q_u->info.share.info2.info_2.type;
+ break;
+ case 501:
+ /* No path. Not enough info in a level 501 to do anything. */
+ return WERR_ACCESS_DENIED;
+ case 502:
+ unistr2_to_ascii(share_name, &q_u->info.share.info502.info_502_str.uni_netname, sizeof(share_name));
+ unistr2_to_ascii(comment, &q_u->info.share.info502.info_502_str.uni_remark, sizeof(share_name));
+ unistr2_to_ascii(pathname, &q_u->info.share.info502.info_502_str.uni_path, sizeof(share_name));
+ type = q_u->info.share.info502.info_502.type;
+ psd = q_u->info.share.info502.info_502_str.sd;
+ map_generic_share_sd_bits(psd);
+ break;
+
+ /* none of the following contain share names. NetShareAdd does not have a separate parameter for the share name */
+
+ case 1004:
+ case 1005:
+ case 1006:
+ case 1007:
+ return WERR_ACCESS_DENIED;
+ break;
+ case 1501:
+ /* DFS only level. */
+ return WERR_ACCESS_DENIED;
+ default:
+ DEBUG(5,("_srv_net_share_add: unsupported switch value %d\n", q_u->info_level));
+ return WERR_UNKNOWN_LEVEL;
+ }
+
+ if (strequal(share_name,"IPC$") || strequal(share_name,"ADMIN$") || strequal(share_name,"global"))
+ return WERR_ACCESS_DENIED;
+
+ snum = find_service(share_name);
+
+ /* Share already exists. */
+ if (snum >= 0)
+ return WERR_ALREADY_EXISTS;
+
+ /* We can only add disk shares. */
+ if (type != STYPE_DISKTREE)
+ return WERR_ACCESS_DENIED;
+
+ /* Check if the pathname is valid. */
+ if (!(path = valid_share_pathname( pathname )))
+ return WERR_OBJECT_PATH_INVALID;
+
+ /* Ensure share name, pathname and comment don't contain '"' characters. */
+ string_replace(share_name, '"', ' ');
+ string_replace(path, '"', ' ');
+ string_replace(comment, '"', ' ');
+
+ slprintf(command, sizeof(command)-1, "%s \"%s\" \"%s\" \"%s\" \"%s\"",
+ lp_add_share_cmd(), dyn_CONFIGFILE, share_name, path, comment);
+
+ DEBUG(10,("_srv_net_share_add: Running [%s]\n", command ));
+ if ((ret = smbrun(command, NULL)) != 0) {
+ DEBUG(0,("_srv_net_share_add: Running [%s] returned (%d)\n", command, ret ));
+ return WERR_ACCESS_DENIED;
+ }
+
+ /* Check if the new share pathname exist, if not try to delete the
+ * share and return an error */
+ if (!exist_share_pathname(path)) {
+ DEBUG(1, ("_srv_net_share_add: add share command was ok but path (%s) has not been created!\n", path));
+ DEBUG(1, ("_srv_net_share_add: trying to rollback and delete the share\n"));
+
+ if (!lp_delete_share_cmd() || !*lp_delete_share_cmd()) {
+ DEBUG(1, ("_srv_net_share_add: Error! delete share command is not defined! Please check share (%s) in the config file\n", share_name));
+ return WERR_OBJECT_PATH_INVALID;
+ }
+
+ slprintf(command, sizeof(command)-1, "%s \"%s\" \"%s\"",
+ lp_delete_share_cmd(), dyn_CONFIGFILE, share_name);
+
+ DEBUG(10,("_srv_net_share_add: Running [%s]\n", command ));
+ if ((ret = smbrun(command, NULL)) != 0) {
+ DEBUG(0,("_srv_net_share_add: Running [%s] returned (%d)\n", command, ret ));
+ DEBUG(1, ("_srv_net_share_add: Error! delete share command failed! Please check share (%s) in the config file\n", share_name));
+ }
+
+ return WERR_OBJECT_PATH_INVALID;
+ }
+
+ if (psd) {
+ if (!set_share_security(p->mem_ctx, share_name, psd)) {
+ DEBUG(0,("_srv_net_share_add: Failed to add security info to share %s.\n", share_name ));
+ }
+ }
+
+ /* Tell everyone we updated smb.conf. */
+ message_send_all(conn_tdb_ctx(), MSG_SMB_CONF_UPDATED, NULL, 0, False, NULL);
+
+ /*
+ * We don't call reload_services() here, the message will
+ * cause this to be done before the next packet is read
+ * from the client. JRA.
+ */
+
+ DEBUG(5,("_srv_net_share_add: %d\n", __LINE__));
+
+ return WERR_OK;
+}
+
+/*******************************************************************
+ Net share delete. Call "delete share command" with the share name as
+ a parameter.
+********************************************************************/
+
+WERROR _srv_net_share_del(pipes_struct *p, SRV_Q_NET_SHARE_DEL *q_u, SRV_R_NET_SHARE_DEL *r_u)
+{
+ struct current_user user;
+ pstring command;
+ fstring share_name;
+ int ret;
+ int snum;
+
+ DEBUG(5,("_srv_net_share_del: %d\n", __LINE__));
+
+ unistr2_to_ascii(share_name, &q_u->uni_share_name, sizeof(share_name));
+
+ if (strequal(share_name,"IPC$") || strequal(share_name,"ADMIN$") || strequal(share_name,"global"))
+ return WERR_ACCESS_DENIED;
+
+ snum = find_service(share_name);
+
+ if (snum < 0)
+ return WERR_NO_SUCH_SHARE;
+
+ /* No change to printer shares. */
+ if (lp_print_ok(snum))
+ return WERR_ACCESS_DENIED;
+
+ get_current_user(&user,p);
+
+ if (user.uid != sec_initial_uid())
+ return WERR_ACCESS_DENIED;
+
+ if (!lp_delete_share_cmd() || !*lp_delete_share_cmd())
+ return WERR_ACCESS_DENIED;
+
+ slprintf(command, sizeof(command)-1, "%s \"%s\" \"%s\"",
+ lp_delete_share_cmd(), dyn_CONFIGFILE, lp_servicename(snum));
+
+ DEBUG(10,("_srv_net_share_del: Running [%s]\n", command ));
+ if ((ret = smbrun(command, NULL)) != 0) {
+ DEBUG(0,("_srv_net_share_del: Running [%s] returned (%d)\n", command, ret ));
+ return WERR_ACCESS_DENIED;
+ }
+
+ /* Delete the SD in the database. */
+ delete_share_security(snum);
+
+ /* Tell everyone we updated smb.conf. */
+ message_send_all(conn_tdb_ctx(), MSG_SMB_CONF_UPDATED, NULL, 0, False, NULL);
+
+ lp_killservice(snum);
+
+ return WERR_OK;
+}
+
+WERROR _srv_net_share_del_sticky(pipes_struct *p, SRV_Q_NET_SHARE_DEL *q_u, SRV_R_NET_SHARE_DEL *r_u)
+{
+ DEBUG(5,("_srv_net_share_del_stick: %d\n", __LINE__));
+
+ return _srv_net_share_del(p, q_u, r_u);
+}
+
+/*******************************************************************
+time of day
+********************************************************************/
+
+WERROR _srv_net_remote_tod(pipes_struct *p, SRV_Q_NET_REMOTE_TOD *q_u, SRV_R_NET_REMOTE_TOD *r_u)
+{
+ TIME_OF_DAY_INFO *tod;
+ struct tm *t;
+ time_t unixdate = time(NULL);
+
+ tod = (TIME_OF_DAY_INFO *)talloc(p->mem_ctx, sizeof(TIME_OF_DAY_INFO));
+ if (!tod)
+ return WERR_NOMEM;
+
+ ZERO_STRUCTP(tod);
+
+ r_u->tod = tod;
+ r_u->ptr_srv_tod = 0x1;
+ r_u->status = WERR_OK;
+
+ DEBUG(5,("_srv_net_remote_tod: %d\n", __LINE__));
+
+ t = gmtime(&unixdate);
+
+ /* set up the */
+ init_time_of_day_info(tod,
+ unixdate,
+ 0,
+ t->tm_hour,
+ t->tm_min,
+ t->tm_sec,
+ 0,
+ TimeDiff(unixdate)/60,
+ 10000,
+ t->tm_mday,
+ t->tm_mon + 1,
+ 1900+t->tm_year,
+ t->tm_wday);
+
+ DEBUG(5,("_srv_net_remote_tod: %d\n", __LINE__));
+
+ return r_u->status;
+}
+
+/***********************************************************************************
+ Win9x NT tools get security descriptor.
+***********************************************************************************/
+
+WERROR _srv_net_file_query_secdesc(pipes_struct *p, SRV_Q_NET_FILE_QUERY_SECDESC *q_u,
+ SRV_R_NET_FILE_QUERY_SECDESC *r_u)
+{
+ SEC_DESC *psd = NULL;
+ size_t sd_size;
+ DATA_BLOB null_pw;
+ pstring filename;
+ pstring qualname;
+ files_struct *fsp = NULL;
+ SMB_STRUCT_STAT st;
+ BOOL bad_path;
+ int access_mode;
+ int action;
+ NTSTATUS nt_status;
+ struct current_user user;
+ connection_struct *conn = NULL;
+ BOOL became_user = False;
+
+ ZERO_STRUCT(st);
+
+ r_u->status = WERR_OK;
+
+ unistr2_to_ascii(qualname, &q_u->uni_qual_name, sizeof(qualname));
+
+ /* Null password is ok - we are already an authenticated user... */
+ null_pw = data_blob(NULL, 0);
+
+ get_current_user(&user, p);
+
+ become_root();
+ conn = make_connection(qualname, null_pw, "A:", user.vuid, &nt_status);
+ unbecome_root();
+
+ if (conn == NULL) {
+ DEBUG(3,("_srv_net_file_query_secdesc: Unable to connect to %s\n", qualname));
+ r_u->status = ntstatus_to_werror(nt_status);
+ goto error_exit;
+ }
+
+ if (!become_user(conn, conn->vuid)) {
+ DEBUG(0,("_srv_net_file_query_secdesc: Can't become connected user!\n"));
+ r_u->status = WERR_ACCESS_DENIED;
+ goto error_exit;
+ }
+ became_user = True;
+
+ unistr2_to_ascii(filename, &q_u->uni_file_name, sizeof(filename));
+ unix_convert(filename, conn, NULL, &bad_path, &st);
+ fsp = open_file_shared(conn, filename, &st, SET_OPEN_MODE(DOS_OPEN_RDONLY),
+ (FILE_FAIL_IF_NOT_EXIST|FILE_EXISTS_OPEN), FILE_ATTRIBUTE_NORMAL, 0, &access_mode, &action);
+
+ if (!fsp) {
+ /* Perhaps it is a directory */
+ if (errno == EISDIR)
+ fsp = open_directory(conn, filename, &st,FILE_READ_ATTRIBUTES,0,
+ (FILE_FAIL_IF_NOT_EXIST|FILE_EXISTS_OPEN), &action);
+
+ if (!fsp) {
+ DEBUG(3,("_srv_net_file_query_secdesc: Unable to open file %s\n", filename));
+ r_u->status = WERR_ACCESS_DENIED;
+ goto error_exit;
+ }
+ }
+
+ sd_size = SMB_VFS_GET_NT_ACL(fsp, fsp->fsp_name, (OWNER_SECURITY_INFORMATION|GROUP_SECURITY_INFORMATION|DACL_SECURITY_INFORMATION), &psd);
+
+ if (sd_size == 0) {
+ DEBUG(3,("_srv_net_file_query_secdesc: Unable to get NT ACL for file %s\n", filename));
+ r_u->status = WERR_ACCESS_DENIED;
+ goto error_exit;
+ }
+
+ r_u->ptr_response = 1;
+ r_u->size_response = sd_size;
+ r_u->ptr_secdesc = 1;
+ r_u->size_secdesc = sd_size;
+ r_u->sec_desc = psd;
+
+ psd->dacl->revision = (uint16) NT4_ACL_REVISION;
+
+ close_file(fsp, True);
+ unbecome_user();
+ close_cnum(conn, user.vuid);
+ return r_u->status;
+
+error_exit:
+
+ if(fsp) {
+ close_file(fsp, True);
+ }
+
+ if (became_user)
+ unbecome_user();
+
+ if (conn)
+ close_cnum(conn, user.vuid);
+
+ return r_u->status;
+}
+
+/***********************************************************************************
+ Win9x NT tools set security descriptor.
+***********************************************************************************/
+
+WERROR _srv_net_file_set_secdesc(pipes_struct *p, SRV_Q_NET_FILE_SET_SECDESC *q_u,
+ SRV_R_NET_FILE_SET_SECDESC *r_u)
+{
+ BOOL ret;
+ pstring filename;
+ pstring qualname;
+ DATA_BLOB null_pw;
+ files_struct *fsp = NULL;
+ SMB_STRUCT_STAT st;
+ BOOL bad_path;
+ int access_mode;
+ int action;
+ NTSTATUS nt_status;
+ struct current_user user;
+ connection_struct *conn = NULL;
+ BOOL became_user = False;
+
+ ZERO_STRUCT(st);
+
+ r_u->status = WERR_OK;
+
+ unistr2_to_ascii(qualname, &q_u->uni_qual_name, sizeof(qualname));
+
+ /* Null password is ok - we are already an authenticated user... */
+ null_pw = data_blob(NULL, 0);
+
+ get_current_user(&user, p);
+
+ become_root();
+ conn = make_connection(qualname, null_pw, "A:", user.vuid, &nt_status);
+ unbecome_root();
+
+ if (conn == NULL) {
+ DEBUG(3,("_srv_net_file_set_secdesc: Unable to connect to %s\n", qualname));
+ r_u->status = ntstatus_to_werror(nt_status);
+ goto error_exit;
+ }
+
+ if (!become_user(conn, conn->vuid)) {
+ DEBUG(0,("_srv_net_file_set_secdesc: Can't become connected user!\n"));
+ r_u->status = WERR_ACCESS_DENIED;
+ goto error_exit;
+ }
+ became_user = True;
+
+ unistr2_to_ascii(filename, &q_u->uni_file_name, sizeof(filename));
+ unix_convert(filename, conn, NULL, &bad_path, &st);
+
+ fsp = open_file_shared(conn, filename, &st, SET_OPEN_MODE(DOS_OPEN_RDWR),
+ (FILE_FAIL_IF_NOT_EXIST|FILE_EXISTS_OPEN), FILE_ATTRIBUTE_NORMAL, 0, &access_mode, &action);
+
+ if (!fsp) {
+ /* Perhaps it is a directory */
+ if (errno == EISDIR)
+ fsp = open_directory(conn, filename, &st,FILE_READ_ATTRIBUTES,0,
+ (FILE_FAIL_IF_NOT_EXIST|FILE_EXISTS_OPEN), &action);
+
+ if (!fsp) {
+ DEBUG(3,("_srv_net_file_set_secdesc: Unable to open file %s\n", filename));
+ r_u->status = WERR_ACCESS_DENIED;
+ goto error_exit;
+ }
+ }
+
+ ret = SMB_VFS_SET_NT_ACL(fsp, fsp->fsp_name, q_u->sec_info, q_u->sec_desc);
+
+ if (ret == False) {
+ DEBUG(3,("_srv_net_file_set_secdesc: Unable to set NT ACL on file %s\n", filename));
+ r_u->status = WERR_ACCESS_DENIED;
+ goto error_exit;
+ }
+
+ close_file(fsp, True);
+ unbecome_user();
+ close_cnum(conn, user.vuid);
+ return r_u->status;
+
+error_exit:
+
+ if(fsp) {
+ close_file(fsp, True);
+ }
+
+ if (became_user)
+ unbecome_user();
+
+ if (conn)
+ close_cnum(conn, user.vuid);
+
+ return r_u->status;
+}
+
+/***********************************************************************************
+ It may be that we want to limit users to creating shares on certain areas of the UNIX file area.
+ We could define areas by mapping Windows style disks to points on the UNIX directory hierarchy.
+ These disks would the disks listed by this function.
+ Users could then create shares relative to these disks. Watch out for moving these disks around.
+ "Nigel Williams" <nigel@veritas.com>.
+***********************************************************************************/
+
+static const char *server_disks[] = {"C:"};
+
+static uint32 get_server_disk_count(void)
+{
+ return sizeof(server_disks)/sizeof(server_disks[0]);
+}
+
+static uint32 init_server_disk_enum(uint32 *resume)
+{
+ uint32 server_disk_count = get_server_disk_count();
+
+ /*resume can be an offset into the list for now*/
+
+ if(*resume & 0x80000000)
+ *resume = 0;
+
+ if(*resume > server_disk_count)
+ *resume = server_disk_count;
+
+ return server_disk_count - *resume;
+}
+
+static const char *next_server_disk_enum(uint32 *resume)
+{
+ const char *disk;
+
+ if(init_server_disk_enum(resume) == 0)
+ return NULL;
+
+ disk = server_disks[*resume];
+
+ (*resume)++;
+
+ DEBUG(10, ("next_server_disk_enum: reporting disk %s. resume handle %d.\n", disk, *resume));
+
+ return disk;
+}
+
+WERROR _srv_net_disk_enum(pipes_struct *p, SRV_Q_NET_DISK_ENUM *q_u, SRV_R_NET_DISK_ENUM *r_u)
+{
+ uint32 i;
+ const char *disk_name;
+ TALLOC_CTX *ctx = p->mem_ctx;
+ uint32 resume=get_enum_hnd(&q_u->enum_hnd);
+
+ r_u->status=WERR_OK;
+
+ r_u->total_entries = init_server_disk_enum(&resume);
+
+ r_u->disk_enum_ctr.unknown = 0;
+
+ {
+ DISK_INFO *dinfo;
+
+ int dinfo_size = MAX_SERVER_DISK_ENTRIES * sizeof(*dinfo);
+
+ if(!(dinfo = talloc(ctx, dinfo_size))) {
+ return WERR_NOMEM;
+ }
+
+ r_u->disk_enum_ctr.disk_info = dinfo;
+ }
+
+ r_u->disk_enum_ctr.disk_info_ptr = r_u->disk_enum_ctr.disk_info ? 1 : 0;
+
+ /*allow one DISK_INFO for null terminator*/
+
+ for(i = 0; i < MAX_SERVER_DISK_ENTRIES -1 && (disk_name = next_server_disk_enum(&resume)); i++) {
+
+ r_u->disk_enum_ctr.entries_read++;
+
+ /*copy disk name into a unicode string*/
+
+ init_unistr3(&r_u->disk_enum_ctr.disk_info[i].disk_name, disk_name);
+ }
+
+ /* add a terminating null string. Is this there if there is more data to come? */
+
+ r_u->disk_enum_ctr.entries_read++;
+
+ init_unistr3(&r_u->disk_enum_ctr.disk_info[i].disk_name, "");
+
+ init_enum_hnd(&r_u->enum_hnd, resume);
+
+ return r_u->status;
+}
+
+WERROR _srv_net_name_validate(pipes_struct *p, SRV_Q_NET_NAME_VALIDATE *q_u, SRV_R_NET_NAME_VALIDATE *r_u)
+{
+ int snum;
+ fstring share_name;
+
+ r_u->status=WERR_OK;
+
+ switch(q_u->type) {
+
+ case 0x9:
+
+ /*check if share name is ok*/
+ /*also check if we already have a share with this name*/
+
+ unistr2_to_ascii(share_name, &q_u->uni_name, sizeof(share_name));
+ snum = find_service(share_name);
+
+ /* Share already exists. */
+ if (snum >= 0)
+ r_u->status = WERR_ALREADY_EXISTS;
+ break;
+
+ default:
+ /*unsupported type*/
+ r_u->status = WERR_UNKNOWN_LEVEL;
+ break;
+ }
+
+ return r_u->status;
+}
diff --git a/source/rpc_server/srv_util.c b/source/rpc_server/srv_util.c
new file mode 100644
index 00000000000..5bb8db4e062
--- /dev/null
+++ b/source/rpc_server/srv_util.c
@@ -0,0 +1,580 @@
+/*
+ * Unix SMB/CIFS implementation.
+ * RPC Pipe client / server routines
+ * Copyright (C) Andrew Tridgell 1992-1998
+ * Copyright (C) Luke Kenneth Casson Leighton 1996-1998,
+ * Copyright (C) Paul Ashton 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 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.
+ */
+
+/* this module apparently provides an implementation of DCE/RPC over a
+ * named pipe (IPC$ connection using SMBtrans). details of DCE/RPC
+ * documentation are available (in on-line form) from the X-Open group.
+ *
+ * this module should provide a level of abstraction between SMB
+ * and DCE/RPC, while minimising the amount of mallocs, unnecessary
+ * data copies, and network traffic.
+ *
+ * in this version, which takes a "let's learn what's going on and
+ * get something running" approach, there is additional network
+ * traffic generated, but the code should be easier to understand...
+ *
+ * ... if you read the docs. or stare at packets for weeks on end.
+ *
+ */
+
+#include "includes.h"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_RPC_SRV
+
+/*
+ * A list of the rids of well known BUILTIN and Domain users
+ * and groups.
+ */
+
+rid_name builtin_alias_rids[] =
+{
+ { BUILTIN_ALIAS_RID_ADMINS , "Administrators" },
+ { BUILTIN_ALIAS_RID_USERS , "Users" },
+ { BUILTIN_ALIAS_RID_GUESTS , "Guests" },
+ { BUILTIN_ALIAS_RID_POWER_USERS , "Power Users" },
+
+ { BUILTIN_ALIAS_RID_ACCOUNT_OPS , "Account Operators" },
+ { BUILTIN_ALIAS_RID_SYSTEM_OPS , "System Operators" },
+ { BUILTIN_ALIAS_RID_PRINT_OPS , "Print Operators" },
+ { BUILTIN_ALIAS_RID_BACKUP_OPS , "Backup Operators" },
+ { BUILTIN_ALIAS_RID_REPLICATOR , "Replicator" },
+ { 0 , NULL }
+};
+
+/* array lookup of well-known Domain RID users. */
+rid_name domain_user_rids[] =
+{
+ { DOMAIN_USER_RID_ADMIN , "Administrator" },
+ { DOMAIN_USER_RID_GUEST , "Guest" },
+ { 0 , NULL }
+};
+
+/* array lookup of well-known Domain RID groups. */
+rid_name domain_group_rids[] =
+{
+ { DOMAIN_GROUP_RID_ADMINS , "Domain Admins" },
+ { DOMAIN_GROUP_RID_USERS , "Domain Users" },
+ { DOMAIN_GROUP_RID_GUESTS , "Domain Guests" },
+ { 0 , NULL }
+};
+
+/*******************************************************************
+ gets a domain user's groups
+ ********************************************************************/
+NTSTATUS get_alias_user_groups(TALLOC_CTX *ctx, DOM_SID *sid, int *numgroups, uint32 **prids, DOM_SID *q_sid)
+{
+ SAM_ACCOUNT *sam_pass=NULL;
+ int i, cur_rid=0;
+ gid_t gid;
+ gid_t *groups = NULL;
+ int num_groups;
+ GROUP_MAP map;
+ DOM_SID tmp_sid;
+ fstring user_name;
+ fstring str_domsid, str_qsid;
+ uint32 rid,grid;
+ uint32 *rids=NULL, *new_rids=NULL;
+ gid_t winbind_gid_low, winbind_gid_high;
+ BOOL ret;
+ BOOL winbind_groups_exist;
+
+ /*
+ * this code is far from perfect.
+ * first it enumerates the full /etc/group and that can be slow.
+ * second, it works only with users' SIDs
+ * whereas the day we support nested groups, it will have to
+ * support both users's SIDs and domain groups' SIDs
+ *
+ * having our own ldap backend would be so much faster !
+ * we're far from that, but hope one day ;-) JFM.
+ */
+
+ *prids=NULL;
+ *numgroups=0;
+
+ winbind_groups_exist = lp_idmap_gid(&winbind_gid_low, &winbind_gid_high);
+
+
+ DEBUG(10,("get_alias_user_groups: looking if SID %s is a member of groups in the SID domain %s\n",
+ sid_to_string(str_qsid, q_sid), sid_to_string(str_domsid, sid)));
+
+ pdb_init_sam(&sam_pass);
+ become_root();
+ ret = pdb_getsampwsid(sam_pass, q_sid);
+ unbecome_root();
+ if (ret == False) {
+ pdb_free_sam(&sam_pass);
+ return NT_STATUS_NO_SUCH_USER;
+ }
+
+ fstrcpy(user_name, pdb_get_username(sam_pass));
+ grid=pdb_get_group_rid(sam_pass);
+ if (!NT_STATUS_IS_OK(sid_to_gid(pdb_get_group_sid(sam_pass), &gid))) {
+ /* this should never happen */
+ DEBUG(2,("get_alias_user_groups: sid_to_gid failed!\n"));
+ pdb_free_sam(&sam_pass);
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ become_root();
+ /* on some systems this must run as root */
+ num_groups = getgroups_user(user_name, &groups);
+ unbecome_root();
+ if (num_groups == -1) {
+ /* this should never happen */
+ DEBUG(2,("get_alias_user_groups: getgroups_user failed\n"));
+ pdb_free_sam(&sam_pass);
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ for (i=0;i<num_groups;i++) {
+
+ become_root();
+ ret = get_group_from_gid(groups[i], &map);
+ unbecome_root();
+
+ if ( !ret ) {
+ DEBUG(10,("get_alias_user_groups: gid %d. not found\n", (int)groups[i]));
+ continue;
+ }
+
+ /* if it's not an alias, continue */
+ if (map.sid_name_use != SID_NAME_ALIAS) {
+ DEBUG(10,("get_alias_user_groups: not returing %s, not an ALIAS group.\n", map.nt_name));
+ continue;
+ }
+
+ sid_copy(&tmp_sid, &map.sid);
+ sid_split_rid(&tmp_sid, &rid);
+
+ /* if the sid is not in the correct domain, continue */
+ if (!sid_equal(&tmp_sid, sid)) {
+ DEBUG(10,("get_alias_user_groups: not returing %s, not in the domain SID.\n", map.nt_name));
+ continue;
+ }
+
+ /* Don't return winbind groups as they are not local! */
+ if (winbind_groups_exist && (groups[i] >= winbind_gid_low) && (groups[i] <= winbind_gid_high)) {
+ DEBUG(10,("get_alias_user_groups: not returing %s, not local.\n", map.nt_name));
+ continue;
+ }
+
+ /* Don't return user private groups... */
+ if (Get_Pwnam(map.nt_name) != 0) {
+ DEBUG(10,("get_alias_user_groups: not returing %s, clashes with user.\n", map.nt_name));
+ continue;
+ }
+
+ new_rids=(uint32 *)Realloc(rids, sizeof(uint32)*(cur_rid+1));
+ if (new_rids==NULL) {
+ DEBUG(10,("get_alias_user_groups: could not realloc memory\n"));
+ pdb_free_sam(&sam_pass);
+ free(groups);
+ return NT_STATUS_NO_MEMORY;
+ }
+ rids=new_rids;
+
+ sid_peek_rid(&map.sid, &(rids[cur_rid]));
+ cur_rid++;
+ break;
+ }
+
+ if(num_groups)
+ free(groups);
+
+ /* now check for the user's gid (the primary group rid) */
+ for (i=0; i<cur_rid && grid!=rids[i]; i++)
+ ;
+
+ /* the user's gid is already there */
+ if (i!=cur_rid) {
+ DEBUG(10,("get_alias_user_groups: user is already in the list. good.\n"));
+ goto done;
+ }
+
+ DEBUG(10,("get_alias_user_groups: looking for gid %d of user %s\n", (int)gid, user_name));
+
+ if(!get_group_from_gid(gid, &map)) {
+ DEBUG(0,("get_alias_user_groups: gid of user %s doesn't exist. Check your "
+ "/etc/passwd and /etc/group files\n", user_name));
+ goto done;
+ }
+
+ /* the primary group isn't an alias */
+ if (map.sid_name_use!=SID_NAME_ALIAS) {
+ DEBUG(10,("get_alias_user_groups: not returing %s, not an ALIAS group.\n", map.nt_name));
+ goto done;
+ }
+
+ sid_copy(&tmp_sid, &map.sid);
+ sid_split_rid(&tmp_sid, &rid);
+
+ /* if the sid is not in the correct domain, continue */
+ if (!sid_equal(&tmp_sid, sid)) {
+ DEBUG(10,("get_alias_user_groups: not returing %s, not in the domain SID.\n", map.nt_name));
+ goto done;
+ }
+
+ /* Don't return winbind groups as they are not local! */
+ if (winbind_groups_exist && (gid >= winbind_gid_low) && (gid <= winbind_gid_high)) {
+ DEBUG(10,("get_alias_user_groups: not returing %s, not local.\n", map.nt_name ));
+ goto done;
+ }
+
+ /* Don't return user private groups... */
+ if (Get_Pwnam(map.nt_name) != 0) {
+ DEBUG(10,("get_alias_user_groups: not returing %s, clashes with user.\n", map.nt_name ));
+ goto done;
+ }
+
+ new_rids=(uint32 *)Realloc(rids, sizeof(uint32)*(cur_rid+1));
+ if (new_rids==NULL) {
+ DEBUG(10,("get_alias_user_groups: could not realloc memory\n"));
+ pdb_free_sam(&sam_pass);
+ return NT_STATUS_NO_MEMORY;
+ }
+ rids=new_rids;
+
+ sid_peek_rid(&map.sid, &(rids[cur_rid]));
+ cur_rid++;
+
+done:
+ *prids=rids;
+ *numgroups=cur_rid;
+ pdb_free_sam(&sam_pass);
+
+ return NT_STATUS_OK;
+}
+
+
+/*******************************************************************
+ gets a domain user's groups
+ ********************************************************************/
+BOOL get_domain_user_groups(TALLOC_CTX *ctx, int *numgroups, DOM_GID **pgids, SAM_ACCOUNT *sam_pass)
+{
+ GROUP_MAP *map=NULL;
+ int i, num, num_entries, cur_gid=0;
+ struct group *grp;
+ DOM_GID *gids;
+ fstring user_name;
+ uint32 grid;
+ uint32 tmp_rid;
+ BOOL ret;
+
+ *numgroups= 0;
+
+ fstrcpy(user_name, pdb_get_username(sam_pass));
+ grid=pdb_get_group_rid(sam_pass);
+
+ DEBUG(10,("get_domain_user_groups: searching domain groups [%s] is a member of\n", user_name));
+
+ /* we must wrap this is become/unbecome root for ldap backends */
+
+ become_root();
+ /* first get the list of the domain groups */
+ ret = pdb_enum_group_mapping(SID_NAME_DOM_GRP, &map, &num_entries, ENUM_ONLY_MAPPED);
+
+ unbecome_root();
+
+ /* end wrapper for group enumeration */
+
+
+ if ( !ret )
+ return False;
+
+ DEBUG(10,("get_domain_user_groups: there are %d mapped groups\n", num_entries));
+
+
+ /*
+ * alloc memory. In the worse case, we alloc memory for nothing.
+ * but I prefer to alloc for nothing
+ * than reallocing everytime.
+ */
+ gids = (DOM_GID *)talloc(ctx, sizeof(DOM_GID) * num_entries);
+
+ /* for each group, check if the user is a member of. Only include groups
+ from this domain */
+
+ for(i=0; i<num_entries; i++) {
+
+ if ( !sid_check_is_in_our_domain(&map[i].sid) ) {
+ DEBUG(10,("get_domain_user_groups: skipping check of %s since it is not in our domain\n",
+ map[i].nt_name));
+ continue;
+ }
+
+ if ((grp=getgrgid(map[i].gid)) == NULL) {
+ /* very weird !!! */
+ DEBUG(5,("get_domain_user_groups: gid %d doesn't exist anymore !\n", (int)map[i].gid));
+ continue;
+ }
+
+ for(num=0; grp->gr_mem[num]!=NULL; num++) {
+ if(strcmp(grp->gr_mem[num], user_name)==0) {
+ /* we found the user, add the group to the list */
+ sid_peek_rid(&map[i].sid, &(gids[cur_gid].g_rid));
+ gids[cur_gid].attr=7;
+ DEBUG(10,("get_domain_user_groups: user found in group %s\n", map[i].nt_name));
+ cur_gid++;
+ break;
+ }
+ }
+ }
+
+ /* we have checked the groups */
+ /* we must now check the gid of the user or the primary group rid, that's the same */
+ for (i=0; i<cur_gid && grid!=gids[i].g_rid; i++)
+ ;
+
+ /* the user's gid is already there */
+ if (i!=cur_gid) {
+ /*
+ * the primary group of the user but be the first one in the list
+ * don't ask ! JFM.
+ */
+ gids[i].g_rid=gids[0].g_rid;
+ gids[0].g_rid=grid;
+ goto done;
+ }
+
+ for(i=0; i<num_entries; i++) {
+ sid_peek_rid(&map[i].sid, &tmp_rid);
+ if (tmp_rid==grid) {
+ /*
+ * the primary group of the user but be the first one in the list
+ * don't ask ! JFM.
+ */
+ gids[cur_gid].g_rid=gids[0].g_rid;
+ gids[0].g_rid=tmp_rid;
+ gids[cur_gid].attr=7;
+ DEBUG(10,("get_domain_user_groups: primary gid of user found in group %s\n", map[i].nt_name));
+ cur_gid++;
+ goto done; /* leave the loop early */
+ }
+ }
+
+ DEBUG(0,("get_domain_user_groups: primary gid of user [%s] is not a Domain group !\n", user_name));
+ DEBUGADD(0,("get_domain_user_groups: You should fix it, NT doesn't like that\n"));
+
+
+ done:
+ *pgids=gids;
+ *numgroups=cur_gid;
+ SAFE_FREE(map);
+
+ return True;
+}
+
+/*******************************************************************
+ gets a domain user's groups from their already-calculated NT_USER_TOKEN
+ ********************************************************************/
+NTSTATUS nt_token_to_group_list(TALLOC_CTX *mem_ctx, const DOM_SID *domain_sid,
+ const NT_USER_TOKEN *nt_token,
+ int *numgroups, DOM_GID **pgids)
+{
+ DOM_GID *gids;
+ int i;
+
+ gids = (DOM_GID *)talloc(mem_ctx, sizeof(*gids) * nt_token->num_sids);
+
+ if (!gids) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ *numgroups=0;
+
+ for (i=PRIMARY_GROUP_SID_INDEX; i < nt_token->num_sids; i++) {
+ if (sid_compare_domain(domain_sid, &nt_token->user_sids[i])==0) {
+ sid_peek_rid(&nt_token->user_sids[i], &(gids[*numgroups].g_rid));
+ gids[*numgroups].attr=7;
+ (*numgroups)++;
+ }
+ }
+ *pgids = gids;
+ return NT_STATUS_OK;
+}
+
+/*******************************************************************
+ Look up a local (domain) rid and return a name and type.
+ ********************************************************************/
+NTSTATUS local_lookup_group_name(uint32 rid, char *group_name, uint32 *type)
+{
+ int i = 0;
+ (*type) = SID_NAME_DOM_GRP;
+
+ DEBUG(5,("lookup_group_name: rid: %d", rid));
+
+ while (domain_group_rids[i].rid != rid && domain_group_rids[i].rid != 0)
+ {
+ i++;
+ }
+
+ if (domain_group_rids[i].rid != 0)
+ {
+ fstrcpy(group_name, domain_group_rids[i].name);
+ DEBUG(5,(" = %s\n", group_name));
+ return NT_STATUS_OK;
+ }
+
+ DEBUG(5,(" none mapped\n"));
+ return NT_STATUS_NONE_MAPPED;
+}
+
+/*******************************************************************
+ Look up a local alias rid and return a name and type.
+ ********************************************************************/
+NTSTATUS local_lookup_alias_name(uint32 rid, char *alias_name, uint32 *type)
+{
+ int i = 0;
+ (*type) = SID_NAME_WKN_GRP;
+
+ DEBUG(5,("lookup_alias_name: rid: %d", rid));
+
+ while (builtin_alias_rids[i].rid != rid && builtin_alias_rids[i].rid != 0)
+ {
+ i++;
+ }
+
+ if (builtin_alias_rids[i].rid != 0)
+ {
+ fstrcpy(alias_name, builtin_alias_rids[i].name);
+ DEBUG(5,(" = %s\n", alias_name));
+ return NT_STATUS_OK;
+ }
+
+ DEBUG(5,(" none mapped\n"));
+ return NT_STATUS_NONE_MAPPED;
+}
+
+
+#if 0 /*Nobody uses this function just now*/
+/*******************************************************************
+ Look up a local user rid and return a name and type.
+ ********************************************************************/
+NTSTATUS local_lookup_user_name(uint32 rid, char *user_name, uint32 *type)
+{
+ SAM_ACCOUNT *sampwd=NULL;
+ int i = 0;
+ BOOL ret;
+
+ (*type) = SID_NAME_USER;
+
+ DEBUG(5,("lookup_user_name: rid: %d", rid));
+
+ /* look up the well-known domain user rids first */
+ while (domain_user_rids[i].rid != rid && domain_user_rids[i].rid != 0)
+ {
+ i++;
+ }
+
+ if (domain_user_rids[i].rid != 0) {
+ fstrcpy(user_name, domain_user_rids[i].name);
+ DEBUG(5,(" = %s\n", user_name));
+ return NT_STATUS_OK;
+ }
+
+ pdb_init_sam(&sampwd);
+
+ /* ok, it's a user. find the user account */
+ become_root();
+ ret = pdb_getsampwrid(sampwd, rid);
+ unbecome_root();
+
+ if (ret == True) {
+ fstrcpy(user_name, pdb_get_username(sampwd) );
+ DEBUG(5,(" = %s\n", user_name));
+ pdb_free_sam(&sampwd);
+ return NT_STATUS_OK;
+ }
+
+ DEBUG(5,(" none mapped\n"));
+ pdb_free_sam(&sampwd);
+ return NT_STATUS_NONE_MAPPED;
+}
+
+#endif
+
+/*******************************************************************
+ Look up a local (domain) group name and return a rid
+ ********************************************************************/
+NTSTATUS local_lookup_group_rid(char *group_name, uint32 *rid)
+{
+ const char *grp_name;
+ int i = -1; /* start do loop at -1 */
+
+ do /* find, if it exists, a group rid for the group name*/
+ {
+ i++;
+ (*rid) = domain_group_rids[i].rid;
+ grp_name = domain_group_rids[i].name;
+
+ } while (grp_name != NULL && !strequal(grp_name, group_name));
+
+ return (grp_name != NULL) ? NT_STATUS_OK : NT_STATUS_NONE_MAPPED;
+}
+
+/*******************************************************************
+ Look up a local (BUILTIN) alias name and return a rid
+ ********************************************************************/
+NTSTATUS local_lookup_alias_rid(const char *alias_name, uint32 *rid)
+{
+ const char *als_name;
+ int i = -1; /* start do loop at -1 */
+
+ do /* find, if it exists, a alias rid for the alias name*/
+ {
+ i++;
+ (*rid) = builtin_alias_rids[i].rid;
+ als_name = builtin_alias_rids[i].name;
+
+ } while (als_name != NULL && !strequal(als_name, alias_name));
+
+ return (als_name != NULL) ? NT_STATUS_OK : NT_STATUS_NONE_MAPPED;
+}
+
+/*******************************************************************
+ Look up a local user name and return a rid
+ ********************************************************************/
+NTSTATUS local_lookup_user_rid(char *user_name, uint32 *rid)
+{
+ SAM_ACCOUNT *sampass=NULL;
+ BOOL ret;
+
+ (*rid) = 0;
+
+ pdb_init_sam(&sampass);
+
+ /* find the user account */
+ become_root();
+ ret = pdb_getsampwnam(sampass, user_name);
+ unbecome_root();
+
+ if (ret == True) {
+ (*rid) = pdb_get_user_rid(sampass);
+ pdb_free_sam(&sampass);
+ return NT_STATUS_OK;
+ }
+
+ pdb_free_sam(&sampass);
+ return NT_STATUS_NONE_MAPPED;
+}
diff --git a/source/rpc_server/srv_wkssvc.c b/source/rpc_server/srv_wkssvc.c
new file mode 100644
index 00000000000..b5c1af34d9d
--- /dev/null
+++ b/source/rpc_server/srv_wkssvc.c
@@ -0,0 +1,78 @@
+/*
+ * Unix SMB/CIFS implementation.
+ * RPC Pipe client / server routines
+ * Copyright (C) Andrew Tridgell 1992-1997,
+ * Copyright (C) Luke Kenneth Casson Leighton 1996-1997,
+ * Copyright (C) Paul Ashton 1997,
+ * 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 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.
+ */
+
+/* This is the interface to the wks pipe. */
+
+#include "includes.h"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_RPC_SRV
+
+/*******************************************************************
+ api_wks_query_info
+ ********************************************************************/
+
+static BOOL api_wks_query_info(pipes_struct *p)
+{
+ WKS_Q_QUERY_INFO q_u;
+ WKS_R_QUERY_INFO r_u;
+ prs_struct *data = &p->in_data.data;
+ prs_struct *rdata = &p->out_data.rdata;
+
+ ZERO_STRUCT(q_u);
+ ZERO_STRUCT(r_u);
+
+ /* grab the net share enum */
+ if(!wks_io_q_query_info("", &q_u, data, 0))
+ return False;
+
+ r_u.status = _wks_query_info(p, &q_u, &r_u);
+
+ /* store the response in the SMB stream */
+ if(!wks_io_r_query_info("", &r_u, rdata, 0))
+ return False;
+
+ return True;
+}
+
+
+/*******************************************************************
+ \PIPE\wkssvc commands
+ ********************************************************************/
+
+static struct api_struct api_wks_cmds[] =
+{
+ { "WKS_Q_QUERY_INFO", WKS_QUERY_INFO, api_wks_query_info }
+};
+
+void wkssvc_get_pipe_fns( struct api_struct **fns, int *n_fns )
+{
+ *fns = api_wks_cmds;
+ *n_fns = sizeof(api_wks_cmds) / sizeof(struct api_struct);
+}
+
+NTSTATUS rpc_wks_init(void)
+{
+ return rpc_pipe_register_commands(SMB_RPC_INTERFACE_VERSION, "wkssvc", "ntsvcs", api_wks_cmds,
+ sizeof(api_wks_cmds) / sizeof(struct api_struct));
+}
diff --git a/source/rpc_server/srv_wkssvc_nt.c b/source/rpc_server/srv_wkssvc_nt.c
new file mode 100644
index 00000000000..25fa029237d
--- /dev/null
+++ b/source/rpc_server/srv_wkssvc_nt.c
@@ -0,0 +1,79 @@
+/*
+ * Unix SMB/CIFS implementation.
+ * RPC Pipe client / server routines
+ * Copyright (C) Andrew Tridgell 1992-1997,
+ * Copyright (C) Luke Kenneth Casson Leighton 1996-1997,
+ * Copyright (C) Paul Ashton 1997.
+ * Copyright (C) Jeremy Allison 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 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.
+ */
+
+/* This is the implementation of the wks interface. */
+
+#include "includes.h"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_RPC_SRV
+
+/*******************************************************************
+ create_wks_info_100
+ ********************************************************************/
+
+static void create_wks_info_100(WKS_INFO_100 *inf)
+{
+ pstring my_name;
+ pstring domain;
+
+ DEBUG(5,("create_wks_info_100: %d\n", __LINE__));
+
+ pstrcpy (my_name, global_myname());
+ strupper_m(my_name);
+
+ pstrcpy (domain, lp_workgroup());
+ strupper_m(domain);
+
+ init_wks_info_100(inf,
+ 0x000001f4, /* platform id info */
+ lp_major_announce_version(),
+ lp_minor_announce_version(),
+ my_name, domain);
+}
+
+/*******************************************************************
+ wks_reply_query_info
+
+ only supports info level 100 at the moment.
+
+ ********************************************************************/
+
+NTSTATUS _wks_query_info(pipes_struct *p, WKS_Q_QUERY_INFO *q_u, WKS_R_QUERY_INFO *r_u)
+{
+ WKS_INFO_100 *wks100 = NULL;
+
+ DEBUG(5,("_wks_query_info: %d\n", __LINE__));
+
+ wks100 = (WKS_INFO_100 *)talloc_zero(p->mem_ctx, sizeof(WKS_INFO_100));
+
+ if (!wks100)
+ return NT_STATUS_NO_MEMORY;
+
+ create_wks_info_100(wks100);
+ init_wks_r_query_info(r_u, q_u->switch_value, wks100, NT_STATUS_OK);
+
+ DEBUG(5,("_wks_query_info: %d\n", __LINE__));
+
+ return r_u->status;
+}
diff --git a/source/rpcclient/cmd_dfs.c b/source/rpcclient/cmd_dfs.c
new file mode 100644
index 00000000000..44e97f9881e
--- /dev/null
+++ b/source/rpcclient/cmd_dfs.c
@@ -0,0 +1,237 @@
+/*
+ Unix SMB/CIFS implementation.
+ RPC pipe client
+
+ 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 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 "rpcclient.h"
+
+/* Check DFS is supported by the remote server */
+
+static NTSTATUS cmd_dfs_exist(struct cli_state *cli, TALLOC_CTX *mem_ctx,
+ int argc, const char **argv)
+{
+ BOOL dfs_exists;
+ NTSTATUS result;
+
+ if (argc != 1) {
+ printf("Usage: %s\n", argv[0]);
+ return NT_STATUS_OK;
+ }
+
+ result = cli_dfs_exist(cli, mem_ctx, &dfs_exists);
+
+ if (NT_STATUS_IS_OK(result))
+ printf("dfs is %spresent\n", dfs_exists ? "" : "not ");
+
+ return result;
+}
+
+static NTSTATUS cmd_dfs_add(struct cli_state *cli, TALLOC_CTX *mem_ctx,
+ int argc, const char **argv)
+{
+ NTSTATUS result;
+ const char *entrypath, *servername, *sharename, *comment;
+ uint32 flags = 0;
+
+ if (argc != 5) {
+ printf("Usage: %s entrypath servername sharename comment\n",
+ argv[0]);
+ return NT_STATUS_OK;
+ }
+
+ entrypath = argv[1];
+ servername = argv[2];
+ sharename = argv[3];
+ comment = argv[4];
+
+ result = cli_dfs_add(cli, mem_ctx, entrypath, servername,
+ sharename, comment, flags);
+
+ return result;
+}
+
+static NTSTATUS cmd_dfs_remove(struct cli_state *cli, TALLOC_CTX *mem_ctx,
+ int argc, const char **argv)
+{
+ NTSTATUS result;
+ const char *entrypath, *servername, *sharename;
+
+ if (argc != 4) {
+ printf("Usage: %s entrypath servername sharename\n", argv[0]);
+ return NT_STATUS_OK;
+ }
+
+ entrypath = argv[1];
+ servername = argv[2];
+ sharename = argv[3];
+
+ result = cli_dfs_remove(cli, mem_ctx, entrypath, servername,
+ sharename);
+
+ return result;
+}
+
+/* Display a DFS_INFO_1 structure */
+
+static void display_dfs_info_1(DFS_INFO_1 *info1)
+{
+ fstring temp;
+
+ unistr2_to_ascii(temp, &info1->entrypath, sizeof(temp) - 1);
+ printf("entrypath: %s\n", temp);
+}
+
+/* Display a DFS_INFO_2 structure */
+
+static void display_dfs_info_2(DFS_INFO_2 *info2)
+{
+ fstring temp;
+
+ unistr2_to_ascii(temp, &info2->entrypath, sizeof(temp) - 1);
+ printf("entrypath: %s\n", temp);
+
+ unistr2_to_ascii(temp, &info2->comment, sizeof(temp) - 1);
+ printf("\tcomment: %s\n", temp);
+
+ printf("\tstate: %d\n", info2->state);
+ printf("\tnum_storages: %d\n", info2->num_storages);
+}
+
+/* Display a DFS_INFO_3 structure */
+
+static void display_dfs_info_3(DFS_INFO_3 *info3)
+{
+ fstring temp;
+ int i;
+
+ unistr2_to_ascii(temp, &info3->entrypath, sizeof(temp) - 1);
+ printf("entrypath: %s\n", temp);
+
+ unistr2_to_ascii(temp, &info3->comment, sizeof(temp) - 1);
+ printf("\tcomment: %s\n", temp);
+
+ printf("\tstate: %d\n", info3->state);
+ printf("\tnum_storages: %d\n", info3->num_storages);
+
+ for (i = 0; i < info3->num_storages; i++) {
+ DFS_STORAGE_INFO *dsi = &info3->storages[i];
+
+ unistr2_to_ascii(temp, &dsi->servername, sizeof(temp) - 1);
+ printf("\t\tstorage[%d] servername: %s\n", i, temp);
+
+ unistr2_to_ascii(temp, &dsi->sharename, sizeof(temp) - 1);
+ printf("\t\tstorage[%d] sharename: %s\n", i, temp);
+ }
+}
+
+/* Display a DFS_INFO_CTR structure */
+
+static void display_dfs_info_ctr(DFS_INFO_CTR *ctr)
+{
+ int i;
+
+ for (i = 0; i < ctr->num_entries; i++) {
+ switch (ctr->switch_value) {
+ case 0x01:
+ display_dfs_info_1(&ctr->dfs.info1[i]);
+ break;
+ case 0x02:
+ display_dfs_info_2(&ctr->dfs.info2[i]);
+ break;
+ case 0x03:
+ display_dfs_info_3(&ctr->dfs.info3[i]);
+ break;
+ default:
+ printf("unsupported info level %d\n",
+ ctr->switch_value);
+ break;
+ }
+ }
+}
+
+/* Enumerate dfs shares */
+
+static NTSTATUS cmd_dfs_enum(struct cli_state *cli, TALLOC_CTX *mem_ctx,
+ int argc, const char **argv)
+{
+ DFS_INFO_CTR ctr;
+ NTSTATUS result;
+ uint32 info_level = 1;
+
+ if (argc > 2) {
+ printf("Usage: %s [info_level]\n", argv[0]);
+ return NT_STATUS_OK;
+ }
+
+ if (argc == 2)
+ info_level = atoi(argv[1]);
+
+ result = cli_dfs_enum(cli, mem_ctx, info_level, &ctr);
+
+ if (NT_STATUS_IS_OK(result))
+ display_dfs_info_ctr(&ctr);
+
+ return result;
+}
+
+static NTSTATUS cmd_dfs_getinfo(struct cli_state *cli, TALLOC_CTX *mem_ctx,
+ int argc, const char **argv)
+{
+ NTSTATUS result;
+ const char *entrypath, *servername, *sharename;
+ uint32 info_level = 1;
+ DFS_INFO_CTR ctr;
+
+ if (argc < 4 || argc > 5) {
+ printf("Usage: %s entrypath servername sharename "
+ "[info_level]\n", argv[0]);
+ return NT_STATUS_OK;
+ }
+
+ entrypath = argv[1];
+ servername = argv[2];
+ sharename = argv[3];
+
+ if (argc == 5)
+ info_level = atoi(argv[4]);
+
+ result = cli_dfs_get_info(cli, mem_ctx, entrypath, servername,
+ sharename, info_level, &ctr);
+
+ if (NT_STATUS_IS_OK(result))
+ display_dfs_info_ctr(&ctr);
+
+ return result;
+}
+
+/* List of commands exported by this module */
+
+struct cmd_set dfs_commands[] = {
+
+ { "DFS" },
+
+ { "dfsexist", RPC_RTYPE_NTSTATUS, cmd_dfs_exist, NULL, PI_NETDFS, "Query DFS support", "" },
+ { "dfsadd", RPC_RTYPE_NTSTATUS, cmd_dfs_add, NULL, PI_NETDFS, "Add a DFS share", "" },
+ { "dfsremove", RPC_RTYPE_NTSTATUS, cmd_dfs_remove, NULL, PI_NETDFS, "Remove a DFS share", "" },
+ { "dfsgetinfo",RPC_RTYPE_NTSTATUS, cmd_dfs_getinfo, NULL, PI_NETDFS, "Query DFS share info", "" },
+ { "dfsenum", RPC_RTYPE_NTSTATUS, cmd_dfs_enum, NULL, PI_NETDFS, "Enumerate dfs shares", "" },
+
+ { NULL }
+};
diff --git a/source/rpcclient/cmd_ds.c b/source/rpcclient/cmd_ds.c
new file mode 100644
index 00000000000..c5b12ed1503
--- /dev/null
+++ b/source/rpcclient/cmd_ds.c
@@ -0,0 +1,79 @@
+/*
+ Unix SMB/CIFS implementation.
+ RPC pipe client
+
+ Copyright (C) Gerald Carter 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 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 "rpcclient.h"
+
+/* Look up domain related information on a remote host */
+
+static NTSTATUS cmd_ds_dsrole_getprimarydominfo(struct cli_state *cli,
+ TALLOC_CTX *mem_ctx, int argc,
+ const char **argv)
+{
+ NTSTATUS result;
+ DS_DOMINFO_CTR ctr;
+
+ result = cli_ds_getprimarydominfo( cli, mem_ctx, DsRolePrimaryDomainInfoBasic, &ctr );
+ if ( NT_STATUS_IS_OK(result) )
+ {
+ printf ("Machine Role = [%d]\n", ctr.basic->machine_role);
+
+ if ( ctr.basic->flags & DSROLE_PRIMARY_DS_RUNNING ) {
+ printf( "Directory Service is running.\n");
+ printf( "Domain is in %s mode.\n", (ctr.basic->flags & DSROLE_PRIMARY_DS_MIXED_MODE) ? "mixed" : "native" );
+ }
+ else
+ printf( "Directory Service not running on server\n");
+ }
+
+ return result;
+}
+
+static NTSTATUS cmd_ds_enum_domain_trusts(struct cli_state *cli,
+ TALLOC_CTX *mem_ctx, int argc,
+ const char **argv)
+{
+ NTSTATUS result;
+ uint32 flags = 0x1;
+ struct ds_domain_trust *trusts = NULL;
+ unsigned int num_domains = 0;
+
+ result = cli_ds_enum_domain_trusts( cli, mem_ctx, cli->desthost, flags,
+ &trusts, &num_domains );
+
+ printf( "%d domains returned\n", num_domains );
+
+ SAFE_FREE( trusts );
+
+ return result;
+}
+
+/* List of commands exported by this module */
+
+struct cmd_set ds_commands[] = {
+
+ { "LSARPC-DS" },
+
+ { "dsroledominfo", RPC_RTYPE_NTSTATUS, cmd_ds_dsrole_getprimarydominfo, NULL, PI_LSARPC_DS, "Get Primary Domain Information", "" },
+ { "dsenumdomtrusts", RPC_RTYPE_NTSTATUS, cmd_ds_enum_domain_trusts, NULL, PI_NETLOGON, "Enumerate all trusted domains in an AD forest", "" },
+
+ { NULL }
+};
diff --git a/source/rpcclient/cmd_echo.c b/source/rpcclient/cmd_echo.c
new file mode 100644
index 00000000000..fa4e6916633
--- /dev/null
+++ b/source/rpcclient/cmd_echo.c
@@ -0,0 +1,159 @@
+/*
+ Unix SMB/CIFS implementation.
+ RPC pipe client
+
+ 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 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 "rpcclient.h"
+
+static NTSTATUS cmd_echo_add_one(struct cli_state *cli, TALLOC_CTX *mem_ctx,
+ int argc, const char **argv)
+{
+ uint32 request = 1, response;
+ NTSTATUS result;
+
+ if (argc > 2) {
+ printf("Usage: %s [num]\n", argv[0]);
+ return NT_STATUS_OK;
+ }
+
+ if (argc == 2)
+ request = atoi(argv[1]);
+
+ result = cli_echo_add_one(cli, mem_ctx, request, &response);
+
+ if (!NT_STATUS_IS_OK(result))
+ goto done;
+
+ printf("%d + 1 = %d\n", request, response);
+
+done:
+ return result;
+}
+
+static NTSTATUS cmd_echo_data(struct cli_state *cli, TALLOC_CTX *mem_ctx,
+ int argc, const char **argv)
+{
+ uint32 size, i;
+ NTSTATUS result;
+ char *in_data = NULL, *out_data = NULL;
+
+ if (argc != 2) {
+ printf("Usage: %s num\n", argv[0]);
+ return NT_STATUS_OK;
+ }
+
+ size = atoi(argv[1]);
+ in_data = malloc(size);
+
+ for (i = 0; i < size; i++)
+ in_data[i] = i & 0xff;
+
+ result = cli_echo_data(cli, mem_ctx, size, in_data, &out_data);
+
+ if (!NT_STATUS_IS_OK(result))
+ goto done;
+
+ for (i = 0; i < size; i++) {
+ if (in_data[i] != out_data[i]) {
+ printf("mismatch at offset %d, %d != %d\n",
+ i, in_data[i], out_data[i]);
+ result = NT_STATUS_UNSUCCESSFUL;
+ }
+ }
+
+done:
+ SAFE_FREE(in_data);
+
+ return result;
+}
+
+static NTSTATUS cmd_echo_source_data(struct cli_state *cli,
+ TALLOC_CTX *mem_ctx, int argc,
+ const char **argv)
+{
+ uint32 size, i;
+ NTSTATUS result;
+ char *out_data = NULL;
+
+ if (argc != 2) {
+ printf("Usage: %s num\n", argv[0]);
+ return NT_STATUS_OK;
+ }
+
+ size = atoi(argv[1]);
+
+ result = cli_echo_source_data(cli, mem_ctx, size, &out_data);
+
+ if (!NT_STATUS_IS_OK(result))
+ goto done;
+
+ for (i = 0; i < size; i++) {
+ if (out_data && out_data[i] != (i & 0xff)) {
+ printf("mismatch at offset %d, %d != %d\n",
+ i, out_data[i], i & 0xff);
+ result = NT_STATUS_UNSUCCESSFUL;
+ }
+ }
+
+done:
+ return result;
+}
+
+static NTSTATUS cmd_echo_sink_data(struct cli_state *cli, TALLOC_CTX *mem_ctx,
+ int argc, const char **argv)
+{
+ uint32 size, i;
+ NTSTATUS result;
+ char *in_data = NULL;
+
+ if (argc != 2) {
+ printf("Usage: %s num\n", argv[0]);
+ return NT_STATUS_OK;
+ }
+
+ size = atoi(argv[1]);
+ in_data = malloc(size);
+
+ for (i = 0; i < size; i++)
+ in_data[i] = i & 0xff;
+
+ result = cli_echo_sink_data(cli, mem_ctx, size, in_data);
+
+ if (!NT_STATUS_IS_OK(result))
+ goto done;
+
+done:
+ SAFE_FREE(in_data);
+
+ return result;
+}
+
+/* List of commands exported by this module */
+
+struct cmd_set echo_commands[] = {
+
+ { "ECHO" },
+
+ { "echoaddone", RPC_RTYPE_NTSTATUS, cmd_echo_add_one, NULL, PI_ECHO, "Add one to a number", "" },
+ { "echodata", RPC_RTYPE_NTSTATUS, cmd_echo_data, NULL, PI_ECHO, "Echo data", "" },
+ { "sinkdata", RPC_RTYPE_NTSTATUS, cmd_echo_sink_data, NULL, PI_ECHO, "Sink data", "" },
+ { "sourcedata", RPC_RTYPE_NTSTATUS, cmd_echo_source_data, NULL, PI_ECHO, "Source data", "" },
+ { NULL }
+};
diff --git a/source/rpcclient/cmd_epmapper.c b/source/rpcclient/cmd_epmapper.c
new file mode 100644
index 00000000000..4998286194c
--- /dev/null
+++ b/source/rpcclient/cmd_epmapper.c
@@ -0,0 +1,76 @@
+/*
+ Unix SMB/CIFS implementation.
+ RPC pipe client
+
+ 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 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 "rpcclient.h"
+
+
+static NTSTATUS cmd_epm_map(struct cli_state *cli,
+ TALLOC_CTX *mem_ctx,
+ int argc, const char **argv)
+{
+ EPM_HANDLE handle, entry_handle;
+ EPM_TOWER *towers;
+ EPM_FLOOR floors[5];
+ uint8 addr[4] = {0,0,0,0};
+ uint32 numtowers;
+ /* need to allow all this stuff to be passed in, but
+ for now, it demonstrates the call */
+ struct uuid if_uuid = {0xe3514235, 0x4b06, 0x11d1, \
+ { 0xab, 0x04 }, \
+ { 0x00, 0xc0, \
+ 0x4f, 0xc2, 0xdc, 0xd2 } },
+ syn_uuid = {0x8a885d04, 0x1ceb, 0x11c9, \
+ { 0x9f, 0xe8 }, \
+ { 0x08, 0x00, \
+ 0x2b, 0x10, 0x48, 0x60 } };
+
+ NTSTATUS result;
+
+ ZERO_STRUCT(handle);
+ numtowers = 1;
+ init_epm_floor_uuid(&floors[0], if_uuid, 4);
+ init_epm_floor_uuid(&floors[1], syn_uuid, 2);
+ init_epm_floor_rpc(&floors[2]);
+
+ /* sample for netbios named pipe query
+ init_epm_floor_np(&floors[3], "\\PIPE\\lsass");
+ init_epm_floor_nb(&floors[4], "\\\\psflinux");
+ */
+ init_epm_floor_tcp(&floors[3], 135);
+ init_epm_floor_ip(&floors[4], addr);
+ towers = talloc(mem_ctx, sizeof(EPM_TOWER));
+ init_epm_tower(mem_ctx, towers, floors, 5);
+
+ result = cli_epm_map(cli, mem_ctx, &handle, &towers, &entry_handle, &numtowers);
+
+ return result;
+}
+
+struct cmd_set epm_commands[] = {
+
+ { "EPMAPPER" },
+
+ { "map", RPC_RTYPE_NTSTATUS, cmd_epm_map, NULL, PI_EPM, "map endpoint", "" },
+ { NULL }
+};
+
+
diff --git a/source/rpcclient/cmd_lsarpc.c b/source/rpcclient/cmd_lsarpc.c
new file mode 100644
index 00000000000..5a646a10460
--- /dev/null
+++ b/source/rpcclient/cmd_lsarpc.c
@@ -0,0 +1,762 @@
+/*
+ Unix SMB/CIFS implementation.
+ RPC pipe client
+
+ Copyright (C) Tim Potter 2000
+ 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 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 "rpcclient.h"
+
+
+/* useful function to allow entering a name instead of a SID and
+ * looking it up automatically */
+static NTSTATUS name_to_sid(struct cli_state *cli,
+ TALLOC_CTX *mem_ctx,
+ DOM_SID *sid, const char *name)
+{
+ POLICY_HND pol;
+ uint32 *sid_types;
+ NTSTATUS result;
+ DOM_SID *sids;
+
+ /* maybe its a raw SID */
+ if (strncmp(name, "S-", 2) == 0 &&
+ string_to_sid(sid, name)) {
+ return NT_STATUS_OK;
+ }
+
+ result = cli_lsa_open_policy(cli, mem_ctx, True,
+ SEC_RIGHTS_MAXIMUM_ALLOWED,
+ &pol);
+ if (!NT_STATUS_IS_OK(result))
+ goto done;
+
+ result = cli_lsa_lookup_names(cli, mem_ctx, &pol, 1, &name, &sids, &sid_types);
+ if (!NT_STATUS_IS_OK(result))
+ goto done;
+
+ cli_lsa_close(cli, mem_ctx, &pol);
+
+ *sid = sids[0];
+
+done:
+ return result;
+}
+
+
+/* Look up domain related information on a remote host */
+
+static NTSTATUS cmd_lsa_query_info_policy(struct cli_state *cli,
+ TALLOC_CTX *mem_ctx, int argc,
+ const char **argv)
+{
+ POLICY_HND pol;
+ NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+ DOM_SID *dom_sid;
+ struct uuid *dom_guid;
+ fstring sid_str;
+ char *domain_name = NULL;
+ char *dns_name = NULL;
+ char *forest_name = NULL;
+
+ uint32 info_class = 3;
+
+ if (argc > 2) {
+ printf("Usage: %s [info_class]\n", argv[0]);
+ return NT_STATUS_OK;
+ }
+
+ if (argc == 2)
+ info_class = atoi(argv[1]);
+
+ /* Lookup info policy */
+ switch (info_class) {
+ case 12:
+ result = cli_lsa_open_policy2(cli, mem_ctx, True,
+ SEC_RIGHTS_MAXIMUM_ALLOWED,
+ &pol);
+
+ if (!NT_STATUS_IS_OK(result))
+ goto done;
+ result = cli_lsa_query_info_policy2(cli, mem_ctx, &pol,
+ info_class, &domain_name,
+ &dns_name, &forest_name,
+ &dom_guid, &dom_sid);
+ break;
+ default:
+ result = cli_lsa_open_policy(cli, mem_ctx, True,
+ SEC_RIGHTS_MAXIMUM_ALLOWED,
+ &pol);
+
+ if (!NT_STATUS_IS_OK(result))
+ goto done;
+ result = cli_lsa_query_info_policy(cli, mem_ctx, &pol,
+ info_class, &domain_name,
+ &dom_sid);
+ }
+
+ if (!NT_STATUS_IS_OK(result))
+ goto done;
+
+ sid_to_string(sid_str, dom_sid);
+
+ if (domain_name)
+ printf("domain %s has sid %s\n", domain_name, sid_str);
+ else
+ printf("could not query info for level %d\n", info_class);
+
+ if (dns_name)
+ printf("domain dns name is %s\n", dns_name);
+ if (forest_name)
+ printf("forest name is %s\n", forest_name);
+
+ if (info_class == 12) {
+ printf("domain GUID is ");
+ smb_uuid_string_static(*dom_guid);
+ }
+ done:
+ return result;
+}
+
+/* Resolve a list of names to a list of sids */
+
+static NTSTATUS cmd_lsa_lookup_names(struct cli_state *cli,
+ TALLOC_CTX *mem_ctx, int argc,
+ const char **argv)
+{
+ POLICY_HND pol;
+ NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+ DOM_SID *sids;
+ uint32 *types;
+ int i;
+
+ if (argc == 1) {
+ printf("Usage: %s [name1 [name2 [...]]]\n", argv[0]);
+ return NT_STATUS_OK;
+ }
+
+ result = cli_lsa_open_policy(cli, mem_ctx, True,
+ SEC_RIGHTS_MAXIMUM_ALLOWED,
+ &pol);
+
+ if (!NT_STATUS_IS_OK(result))
+ goto done;
+
+ result = cli_lsa_lookup_names(cli, mem_ctx, &pol, argc - 1,
+ (const char**)(argv + 1), &sids, &types);
+
+ if (!NT_STATUS_IS_OK(result) && NT_STATUS_V(result) !=
+ NT_STATUS_V(STATUS_SOME_UNMAPPED))
+ goto done;
+
+ result = NT_STATUS_OK;
+
+ /* Print results */
+
+ for (i = 0; i < (argc - 1); i++) {
+ fstring sid_str;
+ sid_to_string(sid_str, &sids[i]);
+ printf("%s %s (%s: %d)\n", argv[i + 1], sid_str,
+ sid_type_lookup(types[i]), types[i]);
+ }
+
+ done:
+ return result;
+}
+
+/* Resolve a list of SIDs to a list of names */
+
+static NTSTATUS cmd_lsa_lookup_sids(struct cli_state *cli, TALLOC_CTX *mem_ctx,
+ int argc, const char **argv)
+{
+ POLICY_HND pol;
+ NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+ DOM_SID *sids;
+ char **domains;
+ char **names;
+ uint32 *types;
+ int i;
+
+ if (argc == 1) {
+ printf("Usage: %s [sid1 [sid2 [...]]]\n", argv[0]);
+ return NT_STATUS_OK;
+ }
+
+ result = cli_lsa_open_policy(cli, mem_ctx, True,
+ SEC_RIGHTS_MAXIMUM_ALLOWED,
+ &pol);
+
+ if (!NT_STATUS_IS_OK(result))
+ goto done;
+
+ /* Convert arguments to sids */
+
+ sids = (DOM_SID *)talloc(mem_ctx, sizeof(DOM_SID) * (argc - 1));
+
+ if (!sids) {
+ printf("could not allocate memory for %d sids\n", argc - 1);
+ goto done;
+ }
+
+ for (i = 0; i < argc - 1; i++)
+ if (!string_to_sid(&sids[i], argv[i + 1])) {
+ result = NT_STATUS_INVALID_SID;
+ goto done;
+ }
+
+ /* Lookup the SIDs */
+
+ result = cli_lsa_lookup_sids(cli, mem_ctx, &pol, argc - 1, sids,
+ &domains, &names, &types);
+
+ if (!NT_STATUS_IS_OK(result) && NT_STATUS_V(result) !=
+ NT_STATUS_V(STATUS_SOME_UNMAPPED))
+ goto done;
+
+ result = NT_STATUS_OK;
+
+ /* Print results */
+
+ for (i = 0; i < (argc - 1); i++) {
+ fstring sid_str;
+
+ sid_to_string(sid_str, &sids[i]);
+ printf("%s %s\\%s (%d)\n", sid_str,
+ domains[i] ? domains[i] : "*unknown*",
+ names[i] ? names[i] : "*unknown*", types[i]);
+ }
+
+ done:
+ return result;
+}
+
+/* Enumerate list of trusted domains */
+
+static NTSTATUS cmd_lsa_enum_trust_dom(struct cli_state *cli,
+ TALLOC_CTX *mem_ctx, int argc,
+ const char **argv)
+{
+ POLICY_HND pol;
+ NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+ DOM_SID *domain_sids;
+ char **domain_names;
+
+ /* defaults, but may be changed using params */
+ uint32 enum_ctx = 0;
+ uint32 num_domains = 0;
+ int i;
+
+ if (argc > 2) {
+ printf("Usage: %s [enum context (0)]\n", argv[0]);
+ return NT_STATUS_OK;
+ }
+
+ if (argc == 2 && argv[1]) {
+ enum_ctx = atoi(argv[2]);
+ }
+
+ result = cli_lsa_open_policy(cli, mem_ctx, True,
+ POLICY_VIEW_LOCAL_INFORMATION,
+ &pol);
+
+ if (!NT_STATUS_IS_OK(result))
+ goto done;
+
+ /* Lookup list of trusted domains */
+
+ result = cli_lsa_enum_trust_dom(cli, mem_ctx, &pol, &enum_ctx,
+ &num_domains,
+ &domain_names, &domain_sids);
+ if (!NT_STATUS_IS_OK(result) &&
+ !NT_STATUS_EQUAL(result, NT_STATUS_NO_MORE_ENTRIES) &&
+ !NT_STATUS_EQUAL(result, STATUS_MORE_ENTRIES))
+ goto done;
+
+ /* Print results: list of names and sids returned in this response. */
+ for (i = 0; i < num_domains; i++) {
+ fstring sid_str;
+
+ sid_to_string(sid_str, &domain_sids[i]);
+ printf("%s %s\n", domain_names[i] ? domain_names[i] :
+ "*unknown*", sid_str);
+ }
+
+ done:
+ return result;
+}
+
+/* Enumerates privileges */
+
+static NTSTATUS cmd_lsa_enum_privilege(struct cli_state *cli,
+ TALLOC_CTX *mem_ctx, int argc,
+ const char **argv)
+{
+ POLICY_HND pol;
+ NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+
+ uint32 enum_context=0;
+ uint32 pref_max_length=0x1000;
+ uint32 count=0;
+ char **privs_name;
+ uint32 *privs_high;
+ uint32 *privs_low;
+ int i;
+
+ if (argc > 3) {
+ printf("Usage: %s [enum context] [max length]\n", argv[0]);
+ return NT_STATUS_OK;
+ }
+
+ if (argc>=2)
+ enum_context=atoi(argv[1]);
+
+ if (argc==3)
+ pref_max_length=atoi(argv[2]);
+
+ result = cli_lsa_open_policy(cli, mem_ctx, True,
+ SEC_RIGHTS_MAXIMUM_ALLOWED,
+ &pol);
+
+ if (!NT_STATUS_IS_OK(result))
+ goto done;
+
+ result = cli_lsa_enum_privilege(cli, mem_ctx, &pol, &enum_context, pref_max_length,
+ &count, &privs_name, &privs_high, &privs_low);
+
+ if (!NT_STATUS_IS_OK(result))
+ goto done;
+
+ /* Print results */
+ printf("found %d privileges\n\n", count);
+
+ for (i = 0; i < count; i++) {
+ printf("%s \t\t%d:%d (0x%x:0x%x)\n", privs_name[i] ? privs_name[i] : "*unknown*",
+ privs_high[i], privs_low[i], privs_high[i], privs_low[i]);
+ }
+
+ done:
+ return result;
+}
+
+/* Get privilege name */
+
+static NTSTATUS cmd_lsa_get_dispname(struct cli_state *cli,
+ TALLOC_CTX *mem_ctx, int argc,
+ const char **argv)
+{
+ POLICY_HND pol;
+ NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+
+ uint16 lang_id=0;
+ uint16 lang_id_sys=0;
+ uint16 lang_id_desc;
+ fstring description;
+
+ if (argc != 2) {
+ printf("Usage: %s privilege name\n", argv[0]);
+ return NT_STATUS_OK;
+ }
+
+ result = cli_lsa_open_policy(cli, mem_ctx, True,
+ SEC_RIGHTS_MAXIMUM_ALLOWED,
+ &pol);
+
+ if (!NT_STATUS_IS_OK(result))
+ goto done;
+
+ result = cli_lsa_get_dispname(cli, mem_ctx, &pol, argv[1], lang_id, lang_id_sys, description, &lang_id_desc);
+
+ if (!NT_STATUS_IS_OK(result))
+ goto done;
+
+ /* Print results */
+ printf("%s -> %s (language: 0x%x)\n", argv[1], description, lang_id_desc);
+
+ done:
+ return result;
+}
+
+/* Enumerate the LSA SIDS */
+
+static NTSTATUS cmd_lsa_enum_sids(struct cli_state *cli,
+ TALLOC_CTX *mem_ctx, int argc,
+ const char **argv)
+{
+ POLICY_HND pol;
+ NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+
+ uint32 enum_context=0;
+ uint32 pref_max_length=0x1000;
+ DOM_SID *sids;
+ uint32 count=0;
+ int i;
+
+ if (argc > 3) {
+ printf("Usage: %s [enum context] [max length]\n", argv[0]);
+ return NT_STATUS_OK;
+ }
+
+ if (argc>=2)
+ enum_context=atoi(argv[1]);
+
+ if (argc==3)
+ pref_max_length=atoi(argv[2]);
+
+ result = cli_lsa_open_policy(cli, mem_ctx, True,
+ SEC_RIGHTS_MAXIMUM_ALLOWED,
+ &pol);
+
+ if (!NT_STATUS_IS_OK(result))
+ goto done;
+
+ result = cli_lsa_enum_sids(cli, mem_ctx, &pol, &enum_context, pref_max_length,
+ &count, &sids);
+
+ if (!NT_STATUS_IS_OK(result))
+ goto done;
+
+ /* Print results */
+ printf("found %d SIDs\n\n", count);
+
+ for (i = 0; i < count; i++) {
+ fstring sid_str;
+
+ sid_to_string(sid_str, &sids[i]);
+ printf("%s\n", sid_str);
+ }
+
+ done:
+ return result;
+}
+
+/* Create a new account */
+
+static NTSTATUS cmd_lsa_create_account(struct cli_state *cli,
+ TALLOC_CTX *mem_ctx, int argc,
+ const char **argv)
+{
+ POLICY_HND dom_pol;
+ POLICY_HND user_pol;
+ NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+ uint32 des_access = 0x000f000f;
+
+ DOM_SID sid;
+
+ if (argc != 2 ) {
+ printf("Usage: %s SID\n", argv[0]);
+ return NT_STATUS_OK;
+ }
+
+ result = name_to_sid(cli, mem_ctx, &sid, argv[1]);
+ if (!NT_STATUS_IS_OK(result))
+ goto done;
+
+ result = cli_lsa_open_policy2(cli, mem_ctx, True,
+ SEC_RIGHTS_MAXIMUM_ALLOWED,
+ &dom_pol);
+
+ if (!NT_STATUS_IS_OK(result))
+ goto done;
+
+ result = cli_lsa_create_account(cli, mem_ctx, &dom_pol, &sid, des_access, &user_pol);
+
+ if (!NT_STATUS_IS_OK(result))
+ goto done;
+
+ printf("Account for SID %s successfully created\n\n", argv[1]);
+ result = NT_STATUS_OK;
+
+ done:
+ return result;
+}
+
+
+/* Enumerate the privileges of an SID */
+
+static NTSTATUS cmd_lsa_enum_privsaccounts(struct cli_state *cli,
+ TALLOC_CTX *mem_ctx, int argc,
+ const char **argv)
+{
+ POLICY_HND dom_pol;
+ POLICY_HND user_pol;
+ NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+ uint32 access_desired = 0x000f000f;
+
+ DOM_SID sid;
+ uint32 count=0;
+ LUID_ATTR *set;
+ int i;
+
+ if (argc != 2 ) {
+ printf("Usage: %s SID\n", argv[0]);
+ return NT_STATUS_OK;
+ }
+
+ result = name_to_sid(cli, mem_ctx, &sid, argv[1]);
+ if (!NT_STATUS_IS_OK(result))
+ goto done;
+
+ result = cli_lsa_open_policy2(cli, mem_ctx, True,
+ SEC_RIGHTS_MAXIMUM_ALLOWED,
+ &dom_pol);
+
+ if (!NT_STATUS_IS_OK(result))
+ goto done;
+
+ result = cli_lsa_open_account(cli, mem_ctx, &dom_pol, &sid, access_desired, &user_pol);
+
+ if (!NT_STATUS_IS_OK(result))
+ goto done;
+
+ result = cli_lsa_enum_privsaccount(cli, mem_ctx, &user_pol, &count, &set);
+
+ if (!NT_STATUS_IS_OK(result))
+ goto done;
+
+ /* Print results */
+ printf("found %d privileges for SID %s\n\n", count, argv[1]);
+ printf("high\tlow\tattribute\n");
+
+ for (i = 0; i < count; i++) {
+ printf("%u\t%u\t%u\n", set[i].luid.high, set[i].luid.low, set[i].attr);
+ }
+
+ done:
+ return result;
+}
+
+
+/* Enumerate the privileges of an SID via LsaEnumerateAccountRights */
+
+static NTSTATUS cmd_lsa_enum_acct_rights(struct cli_state *cli,
+ TALLOC_CTX *mem_ctx, int argc,
+ const char **argv)
+{
+ POLICY_HND dom_pol;
+ NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+
+ DOM_SID sid;
+ uint32 count;
+ char **rights;
+
+ int i;
+
+ if (argc != 2 ) {
+ printf("Usage: %s SID\n", argv[0]);
+ return NT_STATUS_OK;
+ }
+
+ result = name_to_sid(cli, mem_ctx, &sid, argv[1]);
+ if (!NT_STATUS_IS_OK(result))
+ goto done;
+
+ result = cli_lsa_open_policy2(cli, mem_ctx, True,
+ SEC_RIGHTS_MAXIMUM_ALLOWED,
+ &dom_pol);
+
+ if (!NT_STATUS_IS_OK(result))
+ goto done;
+
+ result = cli_lsa_enum_account_rights(cli, mem_ctx, &dom_pol, sid, &count, &rights);
+
+ if (!NT_STATUS_IS_OK(result))
+ goto done;
+
+ printf("found %d privileges for SID %s\n", count, sid_string_static(&sid));
+
+ for (i = 0; i < count; i++) {
+ printf("\t%s\n", rights[i]);
+ }
+
+ done:
+ return result;
+}
+
+
+/* add some privileges to a SID via LsaAddAccountRights */
+
+static NTSTATUS cmd_lsa_add_acct_rights(struct cli_state *cli,
+ TALLOC_CTX *mem_ctx, int argc,
+ const char **argv)
+{
+ POLICY_HND dom_pol;
+ NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+
+ DOM_SID sid;
+
+ if (argc < 3 ) {
+ printf("Usage: %s SID [rights...]\n", argv[0]);
+ return NT_STATUS_OK;
+ }
+
+ result = name_to_sid(cli, mem_ctx, &sid, argv[1]);
+ if (!NT_STATUS_IS_OK(result))
+ goto done;
+
+ result = cli_lsa_open_policy2(cli, mem_ctx, True,
+ SEC_RIGHTS_MAXIMUM_ALLOWED,
+ &dom_pol);
+
+ if (!NT_STATUS_IS_OK(result))
+ goto done;
+
+ result = cli_lsa_add_account_rights(cli, mem_ctx, &dom_pol, sid,
+ argc-2, argv+2);
+
+ if (!NT_STATUS_IS_OK(result))
+ goto done;
+
+ done:
+ return result;
+}
+
+
+/* remove some privileges to a SID via LsaRemoveAccountRights */
+
+static NTSTATUS cmd_lsa_remove_acct_rights(struct cli_state *cli,
+ TALLOC_CTX *mem_ctx, int argc,
+ const char **argv)
+{
+ POLICY_HND dom_pol;
+ NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+
+ DOM_SID sid;
+
+ if (argc < 3 ) {
+ printf("Usage: %s SID [rights...]\n", argv[0]);
+ return NT_STATUS_OK;
+ }
+
+ result = name_to_sid(cli, mem_ctx, &sid, argv[1]);
+ if (!NT_STATUS_IS_OK(result))
+ goto done;
+
+ result = cli_lsa_open_policy2(cli, mem_ctx, True,
+ SEC_RIGHTS_MAXIMUM_ALLOWED,
+ &dom_pol);
+
+ if (!NT_STATUS_IS_OK(result))
+ goto done;
+
+ result = cli_lsa_remove_account_rights(cli, mem_ctx, &dom_pol, sid,
+ False, argc-2, argv+2);
+
+ if (!NT_STATUS_IS_OK(result))
+ goto done;
+
+ done:
+ return result;
+}
+
+
+/* Get a privilege value given its name */
+
+static NTSTATUS cmd_lsa_lookupprivvalue(struct cli_state *cli,
+ TALLOC_CTX *mem_ctx, int argc,
+ const char **argv)
+{
+ POLICY_HND pol;
+ NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+ LUID luid;
+
+ if (argc != 2 ) {
+ printf("Usage: %s name\n", argv[0]);
+ return NT_STATUS_OK;
+ }
+
+ result = cli_lsa_open_policy2(cli, mem_ctx, True,
+ SEC_RIGHTS_MAXIMUM_ALLOWED,
+ &pol);
+
+ if (!NT_STATUS_IS_OK(result))
+ goto done;
+
+ result = cli_lsa_lookupprivvalue(cli, mem_ctx, &pol, argv[1], &luid);
+
+ if (!NT_STATUS_IS_OK(result))
+ goto done;
+
+ /* Print results */
+
+ printf("%u:%u (0x%x:0x%x)\n", luid.high, luid.low, luid.high, luid.low);
+
+ done:
+ return result;
+}
+
+/* Query LSA security object */
+
+static NTSTATUS cmd_lsa_query_secobj(struct cli_state *cli,
+ TALLOC_CTX *mem_ctx, int argc,
+ const char **argv)
+{
+ POLICY_HND pol;
+ NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+ SEC_DESC_BUF *sdb;
+ uint32 sec_info = 0x00000004; /* ??? */
+
+ if (argc != 1 ) {
+ printf("Usage: %s\n", argv[0]);
+ return NT_STATUS_OK;
+ }
+
+ result = cli_lsa_open_policy2(cli, mem_ctx, True,
+ SEC_RIGHTS_MAXIMUM_ALLOWED,
+ &pol);
+
+ if (!NT_STATUS_IS_OK(result))
+ goto done;
+
+ result = cli_lsa_query_secobj(cli, mem_ctx, &pol, sec_info, &sdb);
+
+ if (!NT_STATUS_IS_OK(result))
+ goto done;
+
+ /* Print results */
+
+ display_sec_desc(sdb->sec);
+
+ done:
+ return result;
+}
+
+
+/* List of commands exported by this module */
+
+struct cmd_set lsarpc_commands[] = {
+
+ { "LSARPC" },
+
+ { "lsaquery", RPC_RTYPE_NTSTATUS, cmd_lsa_query_info_policy, NULL, PI_LSARPC, "Query info policy", "" },
+ { "lookupsids", RPC_RTYPE_NTSTATUS, cmd_lsa_lookup_sids, NULL, PI_LSARPC, "Convert SIDs to names", "" },
+ { "lookupnames", RPC_RTYPE_NTSTATUS, cmd_lsa_lookup_names, NULL, PI_LSARPC, "Convert names to SIDs", "" },
+ { "enumtrust", RPC_RTYPE_NTSTATUS, cmd_lsa_enum_trust_dom, NULL, PI_LSARPC, "Enumerate trusted domains", "Usage: [preferred max number] [enum context (0)]" },
+ { "enumprivs", RPC_RTYPE_NTSTATUS, cmd_lsa_enum_privilege, NULL, PI_LSARPC, "Enumerate privileges", "" },
+ { "getdispname", RPC_RTYPE_NTSTATUS, cmd_lsa_get_dispname, NULL, PI_LSARPC, "Get the privilege name", "" },
+ { "lsaenumsid", RPC_RTYPE_NTSTATUS, cmd_lsa_enum_sids, NULL, PI_LSARPC, "Enumerate the LSA SIDS", "" },
+ { "lsacreateaccount", RPC_RTYPE_NTSTATUS, cmd_lsa_create_account, NULL, PI_LSARPC, "Create a new lsa account", "" },
+ { "lsaenumprivsaccount", RPC_RTYPE_NTSTATUS, cmd_lsa_enum_privsaccounts, NULL, PI_LSARPC, "Enumerate the privileges of an SID", "" },
+ { "lsaenumacctrights", RPC_RTYPE_NTSTATUS, cmd_lsa_enum_acct_rights, NULL, PI_LSARPC, "Enumerate the rights of an SID", "" },
+ { "lsaaddacctrights", RPC_RTYPE_NTSTATUS, cmd_lsa_add_acct_rights, NULL, PI_LSARPC, "Add rights to an account", "" },
+ { "lsaremoveacctrights", RPC_RTYPE_NTSTATUS, cmd_lsa_remove_acct_rights, NULL, PI_LSARPC, "Remove rights from an account", "" },
+ { "lsalookupprivvalue", RPC_RTYPE_NTSTATUS, cmd_lsa_lookupprivvalue, NULL, PI_LSARPC, "Get a privilege value given its name", "" },
+ { "lsaquerysecobj", RPC_RTYPE_NTSTATUS, cmd_lsa_query_secobj, NULL, PI_LSARPC, "Query LSA security object", "" },
+
+ { NULL }
+};
diff --git a/source/rpcclient/cmd_netlogon.c b/source/rpcclient/cmd_netlogon.c
new file mode 100644
index 00000000000..9e281fefce4
--- /dev/null
+++ b/source/rpcclient/cmd_netlogon.c
@@ -0,0 +1,345 @@
+/*
+ Unix SMB/CIFS implementation.
+ RPC pipe client
+
+ 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 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 "rpcclient.h"
+
+static NTSTATUS cmd_netlogon_logon_ctrl2(struct cli_state *cli,
+ TALLOC_CTX *mem_ctx, int argc,
+ const char **argv)
+{
+ uint32 query_level = 1;
+ NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+
+ if (argc > 1) {
+ fprintf(stderr, "Usage: %s\n", argv[0]);
+ return NT_STATUS_OK;
+ }
+
+ result = cli_netlogon_logon_ctrl2(cli, mem_ctx, query_level);
+
+ if (!NT_STATUS_IS_OK(result))
+ goto done;
+
+ /* Display results */
+
+ done:
+ return result;
+}
+
+static NTSTATUS cmd_netlogon_getdcname(struct cli_state *cli,
+ TALLOC_CTX *mem_ctx, int argc,
+ const char **argv)
+{
+ fstring dcname;
+ NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+
+ if (argc != 2) {
+ fprintf(stderr, "Usage: %s domainname\n", argv[0]);
+ return NT_STATUS_OK;
+ }
+
+ result = cli_netlogon_getdcname(cli, mem_ctx, argv[1], dcname);
+
+ if (!NT_STATUS_IS_OK(result))
+ goto done;
+
+ /* Display results */
+
+ printf("%s\n", dcname);
+
+ done:
+ return result;
+}
+
+static NTSTATUS cmd_netlogon_logon_ctrl(struct cli_state *cli,
+ TALLOC_CTX *mem_ctx, int argc,
+ const char **argv)
+{
+#if 0
+ uint32 query_level = 1;
+#endif
+ NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+
+ if (argc > 1) {
+ fprintf(stderr, "Usage: %s\n", argv[0]);
+ return NT_STATUS_OK;
+ }
+
+#if 0
+ result = cli_netlogon_logon_ctrl(cli, mem_ctx, query_level);
+ if (!NT_STATUS_IS_OK(result)) {
+ goto done;
+ }
+#endif
+
+ /* Display results */
+
+ return result;
+}
+
+/* Display sam synchronisation information */
+
+static void display_sam_sync(uint32 num_deltas, SAM_DELTA_HDR *hdr_deltas,
+ SAM_DELTA_CTR *deltas)
+{
+ fstring name;
+ uint32 i, j;
+
+ for (i = 0; i < num_deltas; i++) {
+ switch (hdr_deltas[i].type) {
+ case SAM_DELTA_DOMAIN_INFO:
+ unistr2_to_ascii(name,
+ &deltas[i].domain_info.uni_dom_name,
+ sizeof(name) - 1);
+ printf("Domain: %s\n", name);
+ break;
+ case SAM_DELTA_GROUP_INFO:
+ unistr2_to_ascii(name,
+ &deltas[i].group_info.uni_grp_name,
+ sizeof(name) - 1);
+ printf("Group: %s\n", name);
+ break;
+ case SAM_DELTA_ACCOUNT_INFO:
+ unistr2_to_ascii(name,
+ &deltas[i].account_info.uni_acct_name,
+ sizeof(name) - 1);
+ printf("Account: %s\n", name);
+ break;
+ case SAM_DELTA_ALIAS_INFO:
+ unistr2_to_ascii(name,
+ &deltas[i].alias_info.uni_als_name,
+ sizeof(name) - 1);
+ printf("Alias: %s\n", name);
+ break;
+ case SAM_DELTA_ALIAS_MEM: {
+ SAM_ALIAS_MEM_INFO *alias = &deltas[i].als_mem_info;
+
+ for (j = 0; j < alias->num_members; j++) {
+ fstring sid_str;
+
+ sid_to_string(sid_str, &alias->sids[j].sid);
+
+ printf("%s\n", sid_str);
+ }
+ break;
+ }
+ case SAM_DELTA_GROUP_MEM: {
+ SAM_GROUP_MEM_INFO *group = &deltas[i].grp_mem_info;
+
+ for (j = 0; j < group->num_members; j++)
+ printf("rid 0x%x, attrib 0x%08x\n",
+ group->rids[j], group->attribs[j]);
+ break;
+ }
+ case SAM_DELTA_MODIFIED_COUNT: {
+ SAM_DELTA_MOD_COUNT *mc = &deltas[i].mod_count;
+
+ printf("sam sequence update: 0x%04x\n", mc->seqnum);
+ break;
+ }
+ default:
+ printf("unknown delta type 0x%02x\n",
+ hdr_deltas[i].type);
+ break;
+ }
+ }
+}
+
+/* Perform sam synchronisation */
+
+static NTSTATUS cmd_netlogon_sam_sync(struct cli_state *cli,
+ TALLOC_CTX *mem_ctx, int argc,
+ const char **argv)
+{
+ NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+ uint32 database_id = 0, num_deltas;
+ SAM_DELTA_HDR *hdr_deltas;
+ SAM_DELTA_CTR *deltas;
+ DOM_CRED ret_creds;
+
+ if (argc > 2) {
+ fprintf(stderr, "Usage: %s [database_id]\n", argv[0]);
+ return NT_STATUS_OK;
+ }
+
+ if (argc == 2)
+ database_id = atoi(argv[1]);
+
+ /* on first call the returnAuthenticator is empty */
+ memset(&ret_creds, 0, sizeof(ret_creds));
+
+ /* Synchronise sam database */
+
+ result = cli_netlogon_sam_sync(cli, mem_ctx, &ret_creds, database_id,
+ 0, &num_deltas, &hdr_deltas, &deltas);
+
+ if (!NT_STATUS_IS_OK(result))
+ goto done;
+
+ /* Display results */
+
+ display_sam_sync(num_deltas, hdr_deltas, deltas);
+
+ done:
+ return result;
+}
+
+/* Perform sam delta synchronisation */
+
+static NTSTATUS cmd_netlogon_sam_deltas(struct cli_state *cli,
+ TALLOC_CTX *mem_ctx, int argc,
+ const char **argv)
+{
+ NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+ uint32 database_id, num_deltas, tmp;
+ SAM_DELTA_HDR *hdr_deltas;
+ SAM_DELTA_CTR *deltas;
+ UINT64_S seqnum;
+
+ if (argc != 3) {
+ fprintf(stderr, "Usage: %s database_id seqnum\n", argv[0]);
+ return NT_STATUS_OK;
+ }
+
+ database_id = atoi(argv[1]);
+ tmp = atoi(argv[2]);
+
+ seqnum.low = tmp & 0xffff;
+ seqnum.high = 0;
+
+ result = cli_netlogon_sam_deltas(cli, mem_ctx, database_id,
+ seqnum, &num_deltas,
+ &hdr_deltas, &deltas);
+
+ if (!NT_STATUS_IS_OK(result))
+ goto done;
+
+ /* Display results */
+
+ display_sam_sync(num_deltas, hdr_deltas, deltas);
+
+ done:
+ return result;
+}
+
+/* Log on a domain user */
+
+static NTSTATUS cmd_netlogon_sam_logon(struct cli_state *cli,
+ TALLOC_CTX *mem_ctx, int argc,
+ const char **argv)
+{
+ NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+ int logon_type = NET_LOGON_TYPE;
+ const char *username, *password;
+ uint32 neg_flags = 0x000001ff;
+ int auth_level = 2;
+ DOM_CRED ret_creds;
+
+ /* Check arguments */
+
+ if (argc < 3 || argc > 6) {
+ fprintf(stderr, "Usage: samlogon <username> <password> "
+ "[logon_type] [neg flags] [auth level (2 or 3)]\n"
+ "neg flags being 0x000001ff or 0x6007ffff\n");
+ return NT_STATUS_OK;
+ }
+
+ username = argv[1];
+ password = argv[2];
+
+ if (argc == 4)
+ sscanf(argv[3], "%i", &logon_type);
+
+ if (argc == 5)
+ sscanf(argv[4], "%i", &neg_flags);
+
+ if (argc == 6)
+ sscanf(argv[5], "%i", &auth_level);
+
+ /* Perform the sam logon */
+
+ ZERO_STRUCT(ret_creds);
+
+ result = cli_netlogon_sam_logon(cli, mem_ctx, &ret_creds, username, password, logon_type);
+
+ clnt_deal_with_creds(cli->sess_key, &(cli->clnt_cred), &ret_creds);
+
+ result = cli_netlogon_sam_logon(cli, mem_ctx, &ret_creds, username, password, logon_type);
+
+ clnt_deal_with_creds(cli->sess_key, &(cli->clnt_cred), &ret_creds);
+
+ if (!NT_STATUS_IS_OK(result))
+ goto done;
+
+ done:
+ return result;
+}
+
+/* Change the trust account password */
+
+static NTSTATUS cmd_netlogon_change_trust_pw(struct cli_state *cli,
+ TALLOC_CTX *mem_ctx, int argc,
+ const char **argv)
+{
+ NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+ DOM_CRED ret_creds;
+
+ /* Check arguments */
+
+ if (argc > 1) {
+ fprintf(stderr, "Usage: change_trust_pw");
+ return NT_STATUS_OK;
+ }
+
+ /* Perform the sam logon */
+
+ ZERO_STRUCT(ret_creds);
+
+ result = trust_pw_find_change_and_store_it(cli, mem_ctx,
+ lp_workgroup());
+
+ clnt_deal_with_creds(cli->sess_key, &(cli->clnt_cred), &ret_creds);
+
+ if (!NT_STATUS_IS_OK(result))
+ goto done;
+
+ done:
+ return result;
+}
+
+
+/* List of commands exported by this module */
+
+struct cmd_set netlogon_commands[] = {
+
+ { "NETLOGON" },
+
+ { "logonctrl2", RPC_RTYPE_NTSTATUS, cmd_netlogon_logon_ctrl2, NULL, PI_NETLOGON, "Logon Control 2", "" },
+ { "getdcname", RPC_RTYPE_NTSTATUS, cmd_netlogon_getdcname, NULL, PI_NETLOGON, "Get trusted DC name", "" },
+ { "logonctrl", RPC_RTYPE_NTSTATUS, cmd_netlogon_logon_ctrl, NULL, PI_NETLOGON, "Logon Control", "" },
+ { "samsync", RPC_RTYPE_NTSTATUS, cmd_netlogon_sam_sync, NULL, PI_NETLOGON, "Sam Synchronisation", "" },
+ { "samdeltas", RPC_RTYPE_NTSTATUS, cmd_netlogon_sam_deltas, NULL, PI_NETLOGON, "Query Sam Deltas", "" },
+ { "samlogon", RPC_RTYPE_NTSTATUS, cmd_netlogon_sam_logon, NULL, PI_NETLOGON, "Sam Logon", "" },
+ { "samlogon", RPC_RTYPE_NTSTATUS, cmd_netlogon_change_trust_pw, NULL, PI_NETLOGON, "Change Trust Account Password", "" },
+
+ { NULL }
+};
diff --git a/source/rpcclient/cmd_reg.c b/source/rpcclient/cmd_reg.c
new file mode 100644
index 00000000000..bf85d217160
--- /dev/null
+++ b/source/rpcclient/cmd_reg.c
@@ -0,0 +1,1007 @@
+/*
+ Unix SMB/CIFS implementation.
+ NT Domain Authentication SMB / MSRPC client
+ Copyright (C) Andrew Tridgell 1994-1997
+ Copyright (C) Luke Kenneth Casson Leighton 1996-1997
+ Copyright (C) Simo Sorce 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 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 "rpcclient.h"
+
+/*
+ * keys. of the form:
+ * ----
+ *
+ * [HKLM]|[HKU]\[parent_keyname_components]\[subkey]|[value]
+ *
+ * reg_getsubkey() splits this down into:
+ * [HKLM]|[HKU]\[parent_keyname_components] and [subkey]|[value]
+ *
+ * do_reg_connect() splits the left side down further into:
+ * [HKLM]|[HKU] and [parent_keyname_components].
+ *
+ * HKLM is short for HKEY_LOCAL_MACHINE
+ * HKU is short for HKEY_USERS
+ *
+ * oh, and HKEY stands for "Hive Key".
+ *
+ */
+
+#if 0 /* Simo: reg functions need to be updated to the new cmd interface */
+
+/****************************************************************************
+nt registry enum
+****************************************************************************/
+static void cmd_reg_enum(struct client_info *info)
+{
+ BOOL res = True;
+ BOOL res1 = True;
+ BOOL res2 = True;
+ int i;
+
+ POLICY_HND key_pol;
+ fstring full_keyname;
+ fstring key_name;
+
+ /*
+ * query key info
+ */
+
+ fstring key_class;
+ uint32 max_class_len = 0;
+ uint32 num_subkeys;
+ uint32 max_subkeylen;
+ uint32 max_subkeysize;
+ uint32 num_values;
+ uint32 max_valnamelen;
+ uint32 max_valbufsize;
+ uint32 sec_desc;
+ NTTIME mod_time;
+
+ /*
+ * unknown 0x1a request
+ */
+
+ uint32 unk_1a_response;
+
+ DEBUG(5, ("cmd_reg_enum: smb_cli->fd:%d\n", smb_cli->fd));
+
+ if (!next_token_nr(NULL, full_keyname, NULL, sizeof(full_keyname)))
+ {
+ fprintf(out_hnd, "regenum <key_name>\n");
+ return;
+ }
+
+ /* open WINREG session. */
+ res = res ? cli_nt_session_open(smb_cli, PI_WINREG) : False;
+
+ /* open registry receive a policy handle */
+ res = res ? do_reg_connect(smb_cli, full_keyname, key_name,
+ &info->dom.reg_pol_connect) : False;
+
+ if ((*key_name) != 0)
+ {
+ /* open an entry */
+ res1 = res ? do_reg_open_entry(smb_cli, &info->dom.reg_pol_connect,
+ key_name, 0x02000000, &key_pol) : False;
+ }
+ else
+ {
+ memcpy(&key_pol, &info->dom.reg_pol_connect, sizeof(key_pol));
+ }
+
+ res1 = res1 ? do_reg_query_key(smb_cli,
+ &key_pol,
+ key_class, &max_class_len,
+ &num_subkeys, &max_subkeylen, &max_subkeysize,
+ &num_values, &max_valnamelen, &max_valbufsize,
+ &sec_desc, &mod_time) : False;
+
+ if (res1 && num_subkeys > 0)
+ {
+ fprintf(out_hnd,"Subkeys\n");
+ fprintf(out_hnd,"-------\n");
+ }
+
+ for (i = 0; i < num_subkeys; i++)
+ {
+ /*
+ * enumerate key
+ */
+
+ fstring enum_name;
+ uint32 enum_unk1;
+ uint32 enum_unk2;
+ time_t key_mod_time;
+
+ /* unknown 1a it */
+ res2 = res1 ? do_reg_unknown_1a(smb_cli, &key_pol,
+ &unk_1a_response) : False;
+
+ if (res2 && unk_1a_response != 5)
+ {
+ fprintf(out_hnd,"Unknown 1a response: %x\n", unk_1a_response);
+ }
+
+ /* enum key */
+ res2 = res2 ? do_reg_enum_key(smb_cli, &key_pol,
+ i, enum_name,
+ &enum_unk1, &enum_unk2,
+ &key_mod_time) : False;
+
+ if (res2)
+ {
+ display_reg_key_info(out_hnd, ACTION_HEADER , enum_name, key_mod_time);
+ display_reg_key_info(out_hnd, ACTION_ENUMERATE, enum_name, key_mod_time);
+ display_reg_key_info(out_hnd, ACTION_FOOTER , enum_name, key_mod_time);
+ }
+
+ }
+
+ if (num_values > 0)
+ {
+ fprintf(out_hnd,"Key Values\n");
+ fprintf(out_hnd,"----------\n");
+ }
+
+ for (i = 0; i < num_values; i++)
+ {
+ /*
+ * enumerate key
+ */
+
+ uint32 val_type;
+ BUFFER2 value;
+ fstring val_name;
+
+ /* unknown 1a it */
+ res2 = res1 ? do_reg_unknown_1a(smb_cli, &key_pol,
+ &unk_1a_response) : False;
+
+ if (res2 && unk_1a_response != 5)
+ {
+ fprintf(out_hnd,"Unknown 1a response: %x\n", unk_1a_response);
+ }
+
+ /* enum key */
+ res2 = res2 ? do_reg_enum_val(smb_cli, &key_pol,
+ i, max_valnamelen, max_valbufsize,
+ val_name, &val_type, &value) : False;
+
+ if (res2)
+ {
+ display_reg_value_info(out_hnd, ACTION_HEADER , val_name, val_type, &value);
+ display_reg_value_info(out_hnd, ACTION_ENUMERATE, val_name, val_type, &value);
+ display_reg_value_info(out_hnd, ACTION_FOOTER , val_name, val_type, &value);
+ }
+ }
+
+ /* close the handles */
+ if ((*key_name) != 0)
+ {
+ res1 = res1 ? do_reg_close(smb_cli, &key_pol) : False;
+ }
+ res = res ? do_reg_close(smb_cli, &info->dom.reg_pol_connect) : False;
+
+ /* close the session */
+ cli_nt_session_close(smb_cli);
+
+ if (res && res1 && res2)
+ {
+ DEBUG(5,("cmd_reg_enum: query succeeded\n"));
+ }
+ else
+ {
+ DEBUG(5,("cmd_reg_enum: query failed\n"));
+ }
+}
+
+/****************************************************************************
+nt registry query key
+****************************************************************************/
+static void cmd_reg_query_key(struct client_info *info)
+{
+ BOOL res = True;
+ BOOL res1 = True;
+
+ POLICY_HND key_pol;
+ fstring full_keyname;
+ fstring key_name;
+
+ /*
+ * query key info
+ */
+
+ fstring key_class;
+ uint32 key_class_len = 0;
+ uint32 num_subkeys;
+ uint32 max_subkeylen;
+ uint32 max_subkeysize;
+ uint32 num_values;
+ uint32 max_valnamelen;
+ uint32 max_valbufsize;
+ uint32 sec_desc;
+ NTTIME mod_time;
+
+ DEBUG(5, ("cmd_reg_enum: smb_cli->fd:%d\n", smb_cli->fd));
+
+ if (!next_token_nr(NULL, full_keyname, NULL, sizeof(full_keyname)))
+ {
+ fprintf(out_hnd, "regquery key_name\n");
+ return;
+ }
+
+ /* open WINREG session. */
+ res = res ? cli_nt_session_open(smb_cli, PI_WINREG) : False;
+
+ /* open registry receive a policy handle */
+ res = res ? do_reg_connect(smb_cli, full_keyname, key_name,
+ &info->dom.reg_pol_connect) : False;
+
+ if ((*key_name) != 0)
+ {
+ /* open an entry */
+ res1 = res ? do_reg_open_entry(smb_cli, &info->dom.reg_pol_connect,
+ key_name, 0x02000000, &key_pol) : False;
+ }
+ else
+ {
+ memcpy(&key_pol, &info->dom.reg_pol_connect, sizeof(key_pol));
+ }
+
+ res1 = res1 ? do_reg_query_key(smb_cli,
+ &key_pol,
+ key_class, &key_class_len,
+ &num_subkeys, &max_subkeylen, &max_subkeysize,
+ &num_values, &max_valnamelen, &max_valbufsize,
+ &sec_desc, &mod_time) : False;
+
+ if (res1 && key_class_len != 0)
+ {
+ res1 = res1 ? do_reg_query_key(smb_cli,
+ &key_pol,
+ key_class, &key_class_len,
+ &num_subkeys, &max_subkeylen, &max_subkeysize,
+ &num_values, &max_valnamelen, &max_valbufsize,
+ &sec_desc, &mod_time) : False;
+ }
+
+ if (res1)
+ {
+ fprintf(out_hnd,"Registry Query Info Key\n");
+ fprintf(out_hnd,"key class: %s\n", key_class);
+ fprintf(out_hnd,"subkeys, max_len, max_size: %d %d %d\n", num_subkeys, max_subkeylen, max_subkeysize);
+ fprintf(out_hnd,"vals, max_len, max_size: 0x%x 0x%x 0x%x\n", num_values, max_valnamelen, max_valbufsize);
+ fprintf(out_hnd,"sec desc: 0x%x\n", sec_desc);
+ fprintf(out_hnd,"mod time: %s\n", http_timestring(nt_time_to_unix(&mod_time)));
+ }
+
+ /* close the handles */
+ if ((*key_name) != 0)
+ {
+ res1 = res1 ? do_reg_close(smb_cli, &key_pol) : False;
+ }
+ res = res ? do_reg_close(smb_cli, &info->dom.reg_pol_connect) : False;
+
+ /* close the session */
+ cli_nt_session_close(smb_cli);
+
+ if (res && res1)
+ {
+ DEBUG(5,("cmd_reg_query: query succeeded\n"));
+ }
+ else
+ {
+ DEBUG(5,("cmd_reg_query: query failed\n"));
+ }
+}
+
+/****************************************************************************
+nt registry create value
+****************************************************************************/
+static void cmd_reg_create_val(struct client_info *info)
+{
+ BOOL res = True;
+ BOOL res3 = True;
+ BOOL res4 = True;
+
+ POLICY_HND parent_pol;
+ fstring full_keyname;
+ fstring keyname;
+ fstring parent_name;
+ fstring val_name;
+ fstring tmp;
+ uint32 val_type;
+ BUFFER3 value;
+
+#if 0
+ uint32 unk_0;
+ uint32 unk_1;
+ /* query it */
+ res1 = res1 ? do_reg_query_info(smb_cli, &val_pol,
+ val_name, *val_type) : False;
+#endif
+
+ DEBUG(5, ("cmd_reg_create_val: smb_cli->fd:%d\n", smb_cli->fd));
+
+ if (!next_token_nr(NULL, full_keyname, NULL, sizeof(full_keyname)))
+ {
+ fprintf(out_hnd, "regcreate <val_name> <val_type> <val>\n");
+ return;
+ }
+
+ reg_get_subkey(full_keyname, keyname, val_name);
+
+ if (keyname[0] == 0 || val_name[0] == 0)
+ {
+ fprintf(out_hnd, "invalid key name\n");
+ return;
+ }
+
+ if (!next_token_nr(NULL, tmp, NULL, sizeof(tmp)))
+ {
+ fprintf(out_hnd, "regcreate <val_name> <val_type (1|4)> <val>\n");
+ return;
+ }
+
+ val_type = atoi(tmp);
+
+ if (val_type != 1 && val_type != 3 && val_type != 4)
+ {
+ fprintf(out_hnd, "val_type 1=UNISTR, 3=BYTES, 4=DWORD supported\n");
+ return;
+ }
+
+ if (!next_token_nr(NULL, tmp, NULL, sizeof(tmp)))
+ {
+ fprintf(out_hnd, "regcreate <val_name> <val_type (1|4)> <val>\n");
+ return;
+ }
+
+ switch (val_type)
+ {
+ case 0x01: /* UNISTR */
+ {
+ init_buffer3_str(&value, tmp, strlen(tmp)+1);
+ break;
+ }
+ case 0x03: /* BYTES */
+ {
+ init_buffer3_hex(&value, tmp);
+ break;
+ }
+ case 0x04: /* DWORD */
+ {
+ uint32 tmp_val;
+ if (strnequal(tmp, "0x", 2))
+ {
+ tmp_val = strtol(tmp, (char**)NULL, 16);
+ }
+ else
+ {
+ tmp_val = strtol(tmp, (char**)NULL, 10);
+ }
+ init_buffer3_uint32(&value, tmp_val);
+ break;
+ }
+ default:
+ {
+ fprintf(out_hnd, "i told you i only deal with UNISTR, DWORD and BYTES!\n");
+ return;
+ }
+ }
+
+ DEBUG(10,("key data:\n"));
+ dump_data(10, (char *)value.buffer, value.buf_len);
+
+ /* open WINREG session. */
+ res = res ? cli_nt_session_open(smb_cli, PI_WINREG) : False;
+
+ /* open registry receive a policy handle */
+ res = res ? do_reg_connect(smb_cli, keyname, parent_name,
+ &info->dom.reg_pol_connect) : False;
+
+ if ((*val_name) != 0)
+ {
+ /* open an entry */
+ res3 = res ? do_reg_open_entry(smb_cli, &info->dom.reg_pol_connect,
+ parent_name, 0x02000000, &parent_pol) : False;
+ }
+ else
+ {
+ memcpy(&parent_pol, &info->dom.reg_pol_connect, sizeof(parent_pol));
+ }
+
+ /* create an entry */
+ res4 = res3 ? do_reg_create_val(smb_cli, &parent_pol,
+ val_name, val_type, &value) : False;
+
+ /* flush the modified key */
+ res4 = res4 ? do_reg_flush_key(smb_cli, &parent_pol) : False;
+
+ /* close the val handle */
+ if ((*val_name) != 0)
+ {
+ res3 = res3 ? do_reg_close(smb_cli, &parent_pol) : False;
+ }
+
+ /* close the registry handles */
+ res = res ? do_reg_close(smb_cli, &info->dom.reg_pol_connect) : False;
+
+ /* close the session */
+ cli_nt_session_close(smb_cli);
+
+ if (res && res3 && res4)
+ {
+ DEBUG(5,("cmd_reg_create_val: query succeeded\n"));
+ fprintf(out_hnd,"OK\n");
+ }
+ else
+ {
+ DEBUG(5,("cmd_reg_create_val: query failed\n"));
+ }
+}
+
+/****************************************************************************
+nt registry delete value
+****************************************************************************/
+static void cmd_reg_delete_val(struct client_info *info)
+{
+ BOOL res = True;
+ BOOL res3 = True;
+ BOOL res4 = True;
+
+ POLICY_HND parent_pol;
+ fstring full_keyname;
+ fstring keyname;
+ fstring parent_name;
+ fstring val_name;
+
+ DEBUG(5, ("cmd_reg_delete_val: smb_cli->fd:%d\n", smb_cli->fd));
+
+ if (!next_token_nr(NULL, full_keyname, NULL, sizeof(full_keyname)))
+ {
+ fprintf(out_hnd, "regdelete <val_name>\n");
+ return;
+ }
+
+ reg_get_subkey(full_keyname, keyname, val_name);
+
+ if (keyname[0] == 0 || val_name[0] == 0)
+ {
+ fprintf(out_hnd, "invalid key name\n");
+ return;
+ }
+
+ /* open WINREG session. */
+ res = res ? cli_nt_session_open(smb_cli, PI_WINREG) : False;
+
+ /* open registry receive a policy handle */
+ res = res ? do_reg_connect(smb_cli, keyname, parent_name,
+ &info->dom.reg_pol_connect) : False;
+
+ if ((*val_name) != 0)
+ {
+ /* open an entry */
+ res3 = res ? do_reg_open_entry(smb_cli, &info->dom.reg_pol_connect,
+ parent_name, 0x02000000, &parent_pol) : False;
+ }
+ else
+ {
+ memcpy(&parent_pol, &info->dom.reg_pol_connect, sizeof(parent_pol));
+ }
+
+ /* delete an entry */
+ res4 = res3 ? do_reg_delete_val(smb_cli, &parent_pol, val_name) : False;
+
+ /* flush the modified key */
+ res4 = res4 ? do_reg_flush_key(smb_cli, &parent_pol) : False;
+
+ /* close the key handle */
+ res3 = res3 ? do_reg_close(smb_cli, &parent_pol) : False;
+
+ /* close the registry handles */
+ res = res ? do_reg_close(smb_cli, &info->dom.reg_pol_connect) : False;
+
+ /* close the session */
+ cli_nt_session_close(smb_cli);
+
+ if (res && res3 && res4)
+ {
+ DEBUG(5,("cmd_reg_delete_val: query succeeded\n"));
+ fprintf(out_hnd,"OK\n");
+ }
+ else
+ {
+ DEBUG(5,("cmd_reg_delete_val: query failed\n"));
+ }
+}
+
+/****************************************************************************
+nt registry delete key
+****************************************************************************/
+static void cmd_reg_delete_key(struct client_info *info)
+{
+ BOOL res = True;
+ BOOL res3 = True;
+ BOOL res4 = True;
+
+ POLICY_HND parent_pol;
+ fstring full_keyname;
+ fstring parent_name;
+ fstring key_name;
+ fstring subkey_name;
+
+ DEBUG(5, ("cmd_reg_delete_key: smb_cli->fd:%d\n", smb_cli->fd));
+
+ if (!next_token_nr(NULL, full_keyname, NULL, sizeof(full_keyname)))
+ {
+ fprintf(out_hnd, "regdeletekey <key_name>\n");
+ return;
+ }
+
+ reg_get_subkey(full_keyname, parent_name, subkey_name);
+
+ if (parent_name[0] == 0 || subkey_name[0] == 0)
+ {
+ fprintf(out_hnd, "invalid key name\n");
+ return;
+ }
+
+ /* open WINREG session. */
+ res = res ? cli_nt_session_open(smb_cli, PI_WINREG) : False;
+
+ /* open registry receive a policy handle */
+ res = res ? do_reg_connect(smb_cli, parent_name, key_name,
+ &info->dom.reg_pol_connect) : False;
+
+ if ((*key_name) != 0)
+ {
+ /* open an entry */
+ res3 = res ? do_reg_open_entry(smb_cli, &info->dom.reg_pol_connect,
+ key_name, 0x02000000, &parent_pol) : False;
+ }
+ else
+ {
+ memcpy(&parent_pol, &info->dom.reg_pol_connect, sizeof(parent_pol));
+ }
+
+ /* create an entry */
+ res4 = res3 ? do_reg_delete_key(smb_cli, &parent_pol, subkey_name) : False;
+
+ /* flush the modified key */
+ res4 = res4 ? do_reg_flush_key(smb_cli, &parent_pol) : False;
+
+ /* close the key handle */
+ if ((*key_name) != 0)
+ {
+ res3 = res3 ? do_reg_close(smb_cli, &parent_pol) : False;
+ }
+
+ /* close the registry handles */
+ res = res ? do_reg_close(smb_cli, &info->dom.reg_pol_connect) : False;
+
+ /* close the session */
+ cli_nt_session_close(smb_cli);
+
+ if (res && res3 && res4)
+ {
+ DEBUG(5,("cmd_reg_delete_key: query succeeded\n"));
+ fprintf(out_hnd,"OK\n");
+ }
+ else
+ {
+ DEBUG(5,("cmd_reg_delete_key: query failed\n"));
+ }
+}
+
+/****************************************************************************
+nt registry create key
+****************************************************************************/
+static void cmd_reg_create_key(struct client_info *info)
+{
+ BOOL res = True;
+ BOOL res3 = True;
+ BOOL res4 = True;
+
+ POLICY_HND parent_pol;
+ POLICY_HND key_pol;
+ fstring full_keyname;
+ fstring parent_key;
+ fstring parent_name;
+ fstring key_name;
+ fstring key_class;
+ SEC_ACCESS sam_access;
+
+ DEBUG(5, ("cmd_reg_create_key: smb_cli->fd:%d\n", smb_cli->fd));
+
+ if (!next_token_nr(NULL, full_keyname, NULL, sizeof(full_keyname)))
+ {
+ fprintf(out_hnd, "regcreate <key_name> [key_class]\n");
+ return;
+ }
+
+ reg_get_subkey(full_keyname, parent_key, key_name);
+
+ if (parent_key[0] == 0 || key_name[0] == 0)
+ {
+ fprintf(out_hnd, "invalid key name\n");
+ return;
+ }
+
+ if (!next_token_nr(NULL, key_class, NULL, sizeof(key_class)))
+ {
+ memset(key_class, 0, sizeof(key_class));
+ }
+
+ /* set access permissions */
+ sam_access.mask = SEC_RIGHTS_READ;
+
+ /* open WINREG session. */
+ res = res ? cli_nt_session_open(smb_cli, PI_WINREG) : False;
+
+ /* open registry receive a policy handle */
+ res = res ? do_reg_connect(smb_cli, parent_key, parent_name,
+ &info->dom.reg_pol_connect) : False;
+
+ if ((*parent_name) != 0)
+ {
+ /* open an entry */
+ res3 = res ? do_reg_open_entry(smb_cli, &info->dom.reg_pol_connect,
+ parent_name, 0x02000000, &parent_pol) : False;
+ }
+ else
+ {
+ memcpy(&parent_pol, &info->dom.reg_pol_connect, sizeof(parent_pol));
+ }
+
+ /* create an entry */
+ res4 = res3 ? do_reg_create_key(smb_cli, &parent_pol,
+ key_name, key_class, &sam_access, &key_pol) : False;
+
+ /* flush the modified key */
+ res4 = res4 ? do_reg_flush_key(smb_cli, &parent_pol) : False;
+
+ /* close the key handle */
+ res4 = res4 ? do_reg_close(smb_cli, &key_pol) : False;
+
+ /* close the key handle */
+ if ((*parent_name) != 0)
+ {
+ res3 = res3 ? do_reg_close(smb_cli, &parent_pol) : False;
+ }
+
+ /* close the registry handles */
+ res = res ? do_reg_close(smb_cli, &info->dom.reg_pol_connect) : False;
+
+ /* close the session */
+ cli_nt_session_close(smb_cli);
+
+ if (res && res3 && res4)
+ {
+ DEBUG(5,("cmd_reg_create_key: query succeeded\n"));
+ fprintf(out_hnd,"OK\n");
+ }
+ else
+ {
+ DEBUG(5,("cmd_reg_create_key: query failed\n"));
+ }
+}
+
+/****************************************************************************
+nt registry security info
+****************************************************************************/
+static void cmd_reg_test_key_sec(struct client_info *info)
+{
+ BOOL res = True;
+ BOOL res3 = True;
+ BOOL res4 = True;
+
+ POLICY_HND key_pol;
+ fstring full_keyname;
+ fstring key_name;
+
+ /*
+ * security info
+ */
+
+ uint32 sec_buf_size;
+ SEC_DESC_BUF *psdb;
+
+ DEBUG(5, ("cmd_reg_get_key_sec: smb_cli->fd:%d\n", smb_cli->fd));
+
+ if (!next_token_nr(NULL, full_keyname, NULL, sizeof(full_keyname)))
+ {
+ fprintf(out_hnd, "reggetsec <key_name>\n");
+ return;
+ }
+
+ /* open WINREG session. */
+ res = res ? cli_nt_session_open(smb_cli, PI_WINREG) : False;
+
+ /* open registry receive a policy handle */
+ res = res ? do_reg_connect(smb_cli, full_keyname, key_name,
+ &info->dom.reg_pol_connect) : False;
+
+ if ((*key_name) != 0)
+ {
+ /* open an entry */
+ res3 = res ? do_reg_open_entry(smb_cli, &info->dom.reg_pol_connect,
+ key_name, 0x02000000, &key_pol) : False;
+ }
+ else
+ {
+ memcpy(&key_pol, &info->dom.reg_pol_connect, sizeof(key_pol));
+ }
+
+ /* open an entry */
+ res3 = res ? do_reg_open_entry(smb_cli, &info->dom.reg_pol_connect,
+ key_name, 0x02000000, &key_pol) : False;
+
+ /* query key sec info. first call sets sec_buf_size. */
+
+ sec_buf_size = 0;
+ res4 = res3 ? do_reg_get_key_sec(smb_cli, &key_pol,
+ &sec_buf_size, &psdb) : False;
+
+ free_sec_desc_buf(&psdb);
+
+ res4 = res4 ? do_reg_get_key_sec(smb_cli, &key_pol,
+ &sec_buf_size, &psdb) : False;
+
+ if (res4 && psdb->len > 0 && psdb->sec != NULL)
+ {
+ display_sec_desc(out_hnd, ACTION_HEADER , psdb->sec);
+ display_sec_desc(out_hnd, ACTION_ENUMERATE, psdb->sec);
+ display_sec_desc(out_hnd, ACTION_FOOTER , psdb->sec);
+
+ res4 = res4 ? do_reg_set_key_sec(smb_cli, &key_pol, psdb) : False;
+ }
+
+ free_sec_desc_buf(&psdb);
+
+ /* close the key handle */
+ if ((*key_name) != 0)
+ {
+ res3 = res3 ? do_reg_close(smb_cli, &key_pol) : False;
+ }
+
+ /* close the registry handles */
+ res = res ? do_reg_close(smb_cli, &info->dom.reg_pol_connect) : False;
+
+ /* close the session */
+ cli_nt_session_close(smb_cli);
+
+ if (res && res3 && res4)
+ {
+ DEBUG(5,("cmd_reg_test2: query succeeded\n"));
+ fprintf(out_hnd,"Registry Test2\n");
+ }
+ else
+ {
+ DEBUG(5,("cmd_reg_test2: query failed\n"));
+ }
+}
+
+/****************************************************************************
+nt registry security info
+****************************************************************************/
+static void cmd_reg_get_key_sec(struct client_info *info)
+{
+ BOOL res = True;
+ BOOL res3 = True;
+ BOOL res4 = True;
+
+ POLICY_HND key_pol;
+ fstring full_keyname;
+ fstring key_name;
+
+ /*
+ * security info
+ */
+
+ uint32 sec_buf_size;
+ SEC_DESC_BUF *psdb;
+
+ DEBUG(5, ("cmd_reg_get_key_sec: smb_cli->fd:%d\n", smb_cli->fd));
+
+ if (!next_token_nr(NULL, full_keyname, NULL, sizeof(full_keyname)))
+ {
+ fprintf(out_hnd, "reggetsec <key_name>\n");
+ return;
+ }
+
+ /* open WINREG session. */
+ res = res ? cli_nt_session_open(smb_cli, PI_WINREG) : False;
+
+ /* open registry receive a policy handle */
+ res = res ? do_reg_connect(smb_cli, full_keyname, key_name,
+ &info->dom.reg_pol_connect) : False;
+
+ if ((*key_name) != 0)
+ {
+ /* open an entry */
+ res3 = res ? do_reg_open_entry(smb_cli, &info->dom.reg_pol_connect,
+ key_name, 0x02000000, &key_pol) : False;
+ }
+ else
+ {
+ memcpy(&key_pol, &info->dom.reg_pol_connect, sizeof(key_pol));
+ }
+
+ /* open an entry */
+ res3 = res ? do_reg_open_entry(smb_cli, &info->dom.reg_pol_connect,
+ key_name, 0x02000000, &key_pol) : False;
+
+ /* Get the size. */
+ sec_buf_size = 0;
+ res4 = res3 ? do_reg_get_key_sec(smb_cli, &key_pol,
+ &sec_buf_size, &psdb) : False;
+
+ free_sec_desc_buf(&psdb);
+
+ res4 = res4 ? do_reg_get_key_sec(smb_cli, &key_pol,
+ &sec_buf_size, &psdb) : False;
+
+ if (res4 && psdb->len > 0 && psdb->sec != NULL)
+ {
+ display_sec_desc(out_hnd, ACTION_HEADER , psdb->sec);
+ display_sec_desc(out_hnd, ACTION_ENUMERATE, psdb->sec);
+ display_sec_desc(out_hnd, ACTION_FOOTER , psdb->sec);
+ }
+
+ free_sec_desc_buf(&psdb);
+
+ /* close the key handle */
+ if ((*key_name) != 0)
+ {
+ res3 = res3 ? do_reg_close(smb_cli, &key_pol) : False;
+ }
+
+ /* close the registry handles */
+ res = res ? do_reg_close(smb_cli, &info->dom.reg_pol_connect) : False;
+
+ /* close the session */
+ cli_nt_session_close(smb_cli);
+
+ if (res && res3 && res4)
+ {
+ DEBUG(5,("cmd_reg_get_key_sec: query succeeded\n"));
+ }
+ else
+ {
+ DEBUG(5,("cmd_reg_get_key_sec: query failed\n"));
+ }
+}
+
+#endif /* 0 */
+
+/****************************************************************************
+nt registry shutdown
+****************************************************************************/
+static NTSTATUS cmd_reg_shutdown(struct cli_state *cli, TALLOC_CTX *mem_ctx,
+ int argc, const char **argv)
+{
+ NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+ fstring msg;
+ uint32 timeout = 20;
+ BOOL force = False;
+ BOOL reboot = False;
+ int opt;
+
+ *msg = 0;
+ optind = 0; /* TODO: test if this hack works on other systems too --simo */
+
+ while ((opt = getopt(argc, argv, "m:t:rf")) != EOF)
+ {
+ /*fprintf (stderr, "[%s]\n", argv[argc-1]);*/
+
+ switch (opt)
+ {
+ case 'm':
+ fstrcpy(msg, optarg);
+ /*fprintf (stderr, "[%s|%s]\n", optarg, msg);*/
+ break;
+
+ case 't':
+ timeout = atoi(optarg);
+ /*fprintf (stderr, "[%s|%d]\n", optarg, timeout);*/
+ break;
+
+ case 'r':
+ reboot = True;
+ break;
+
+ case 'f':
+ force = True;
+ break;
+
+ }
+ }
+
+ /* create an entry */
+ result = cli_reg_shutdown(cli, mem_ctx, msg, timeout, reboot, force);
+
+ if (NT_STATUS_IS_OK(result))
+ DEBUG(5,("cmd_reg_shutdown: query succeeded\n"));
+ else
+ DEBUG(5,("cmd_reg_shutdown: query failed\n"));
+
+ return result;
+}
+
+/****************************************************************************
+abort a shutdown
+****************************************************************************/
+static NTSTATUS cmd_reg_abort_shutdown(struct cli_state *cli,
+ TALLOC_CTX *mem_ctx, int argc,
+ const char **argv)
+{
+ NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+
+ result = cli_reg_abort_shutdown(cli, mem_ctx);
+
+ if (NT_STATUS_IS_OK(result))
+ DEBUG(5,("cmd_reg_abort_shutdown: query succeeded\n"));
+ else
+ DEBUG(5,("cmd_reg_abort_shutdown: query failed\n"));
+
+ return result;
+}
+
+
+/* List of commands exported by this module */
+struct cmd_set reg_commands[] = {
+
+ { "REG" },
+
+ { "shutdown", RPC_RTYPE_NTSTATUS, cmd_reg_shutdown, NULL, PI_WINREG, "Remote Shutdown",
+ "syntax: shutdown [-m message] [-t timeout] [-r] [-h] [-f] (-r == reboot, -h == halt, -f == force)" },
+
+ { "abortshutdown", RPC_RTYPE_NTSTATUS, cmd_reg_abort_shutdown, NULL, PI_WINREG, "Abort Shutdown",
+ "syntax: abortshutdown" },
+/*
+ { "regenum", cmd_reg_enum, "Registry Enumeration",
+ "<keyname>" },
+
+ { "regdeletekey", cmd_reg_delete_key, "Registry Key Delete",
+ "<keyname>" },
+
+ { "regcreatekey", cmd_reg_create_key, "Registry Key Create",
+ "<keyname> [keyclass]" },
+
+ { "regqueryval", cmd_reg_query_info, "Registry Value Query",
+ "<valname>" },
+
+ { "regquerykey", cmd_reg_query_key, "Registry Key Query",
+ "<keyname>" },
+
+ { "regdeleteval", cmd_reg_delete_val, "Registry Value Delete",
+ "<valname>" },
+
+ { "regcreateval", cmd_reg_create_val, "Registry Key Create",
+ "<valname> <valtype> <value>" },
+
+ { "reggetsec", cmd_reg_get_key_sec, "Registry Key Security",
+ "<keyname>" },
+
+ { "regtestsec", cmd_reg_test_key_sec, "Test Registry Key Security",
+ "<keyname>" },
+*/
+ { NULL }
+};
diff --git a/source/rpcclient/cmd_samr.c b/source/rpcclient/cmd_samr.c
new file mode 100644
index 00000000000..ed06a5a4a07
--- /dev/null
+++ b/source/rpcclient/cmd_samr.c
@@ -0,0 +1,1562 @@
+/*
+ Unix SMB/CIFS implementation.
+ RPC pipe client
+
+ Copyright (C) Andrew Tridgell 1992-2000,
+ Copyright (C) Luke Kenneth Casson Leighton 1996-2000,
+ Copyright (C) Elrond 2000,
+ 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 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 "rpcclient.h"
+
+extern DOM_SID domain_sid;
+
+/****************************************************************************
+ display sam_user_info_21 structure
+ ****************************************************************************/
+static void display_sam_user_info_21(SAM_USER_INFO_21 *usr)
+{
+ fstring temp;
+
+ unistr2_to_ascii(temp, &usr->uni_user_name, sizeof(temp)-1);
+ printf("\tUser Name :\t%s\n", temp);
+
+ unistr2_to_ascii(temp, &usr->uni_full_name, sizeof(temp)-1);
+ printf("\tFull Name :\t%s\n", temp);
+
+ unistr2_to_ascii(temp, &usr->uni_home_dir, sizeof(temp)-1);
+ printf("\tHome Drive :\t%s\n", temp);
+
+ unistr2_to_ascii(temp, &usr->uni_dir_drive, sizeof(temp)-1);
+ printf("\tDir Drive :\t%s\n", temp);
+
+ unistr2_to_ascii(temp, &usr->uni_profile_path, sizeof(temp)-1);
+ printf("\tProfile Path:\t%s\n", temp);
+
+ unistr2_to_ascii(temp, &usr->uni_logon_script, sizeof(temp)-1);
+ printf("\tLogon Script:\t%s\n", temp);
+
+ unistr2_to_ascii(temp, &usr->uni_acct_desc, sizeof(temp)-1);
+ printf("\tDescription :\t%s\n", temp);
+
+ unistr2_to_ascii(temp, &usr->uni_workstations, sizeof(temp)-1);
+ printf("\tWorkstations:\t%s\n", temp);
+
+ unistr2_to_ascii(temp, &usr->uni_unknown_str, sizeof(temp)-1);
+ printf("\tUnknown Str :\t%s\n", temp);
+
+ unistr2_to_ascii(temp, &usr->uni_munged_dial, sizeof(temp)-1);
+ printf("\tRemote Dial :\t%s\n", temp);
+
+ printf("\tLogon Time :\t%s\n",
+ http_timestring(nt_time_to_unix(&usr->logon_time)));
+ printf("\tLogoff Time :\t%s\n",
+ http_timestring(nt_time_to_unix(&usr->logoff_time)));
+ printf("\tKickoff Time :\t%s\n",
+ http_timestring(nt_time_to_unix(&usr->kickoff_time)));
+ printf("\tPassword last set Time :\t%s\n",
+ http_timestring(nt_time_to_unix(&usr->pass_last_set_time)));
+ printf("\tPassword can change Time :\t%s\n",
+ http_timestring(nt_time_to_unix(&usr->pass_can_change_time)));
+ printf("\tPassword must change Time:\t%s\n",
+ http_timestring(nt_time_to_unix(&usr->pass_must_change_time)));
+
+ printf("\tunknown_2[0..31]...\n"); /* user passwords? */
+
+ printf("\tuser_rid :\t0x%x\n" , usr->user_rid ); /* User ID */
+ printf("\tgroup_rid:\t0x%x\n" , usr->group_rid); /* Group ID */
+ printf("\tacb_info :\t0x%04x\n", usr->acb_info ); /* Account Control Info */
+
+ printf("\tfields_present:\t0x%08x\n", usr->fields_present); /* 0x00ff ffff */
+ printf("\tlogon_divs:\t%d\n", usr->logon_divs); /* 0x0000 00a8 which is 168 which is num hrs in a week */
+ printf("\tbad_password_count:\t0x%08x\n", usr->bad_password_count);
+ printf("\tlogon_count:\t0x%08x\n", usr->logon_count);
+
+ printf("\tpadding1[0..7]...\n");
+
+ if (usr->ptr_logon_hrs) {
+ printf("\tlogon_hrs[0..%d]...\n", usr->logon_hrs.len);
+ }
+}
+
+static const char *display_time(NTTIME nttime)
+{
+ static fstring string;
+
+ float high;
+ float low;
+ int sec;
+ int days, hours, mins, secs;
+
+ if (nttime.high==0 && nttime.low==0)
+ return "Now";
+
+ if (nttime.high==0x80000000 && nttime.low==0)
+ return "Never";
+
+ high = 65536;
+ high = high/10000;
+ high = high*65536;
+ high = high/1000;
+ high = high * (~nttime.high);
+
+ low = ~nttime.low;
+ low = low/(1000*1000*10);
+
+ sec=high+low;
+
+ days=sec/(60*60*24);
+ hours=(sec - (days*60*60*24)) / (60*60);
+ mins=(sec - (days*60*60*24) - (hours*60*60) ) / 60;
+ secs=sec - (days*60*60*24) - (hours*60*60) - (mins*60);
+
+ fstr_sprintf(string, "%u days, %u hours, %u minutes, %u seconds", days, hours, mins, secs);
+ return (string);
+}
+
+static void display_sam_unk_info_1(SAM_UNK_INFO_1 *info1)
+{
+
+ printf("Minimum password length: %d\n", info1->min_length_password);
+ printf("Password uniqueness (remember x passwords): %d\n", info1->password_history);
+ printf("flag: ");
+ if(info1->flag&&2==2) printf("users must open a session to change password ");
+ printf("\n");
+
+ printf("password expire in: %s\n", display_time(info1->expire));
+ printf("Min password age (allow changing in x days): %s\n", display_time(info1->min_passwordage));
+}
+
+static void display_sam_unk_info_2(SAM_UNK_INFO_2 *info2)
+{
+ fstring name;
+
+ unistr2_to_ascii(name, &info2->uni_domain, sizeof(name) - 1);
+ printf("Domain:\t%s\n", name);
+
+ unistr2_to_ascii(name, &info2->uni_server, sizeof(name) - 1);
+ printf("Server:\t%s\n", name);
+
+ printf("Total Users:\t%d\n", info2->num_domain_usrs);
+ printf("Total Groups:\t%d\n", info2->num_domain_grps);
+ printf("Total Aliases:\t%d\n", info2->num_local_grps);
+
+ printf("Sequence No:\t%d\n", info2->seq_num);
+
+ printf("Unknown 0:\t0x%x\n", info2->unknown_0);
+ printf("Unknown 1:\t0x%x\n", info2->unknown_1);
+ printf("Unknown 2:\t0x%x\n", info2->unknown_2);
+ printf("Unknown 3:\t0x%x\n", info2->unknown_3);
+ printf("Unknown 4:\t0x%x\n", info2->unknown_4);
+ printf("Unknown 5:\t0x%x\n", info2->unknown_5);
+ printf("Unknown 6:\t0x%x\n", info2->unknown_6);
+}
+
+static void display_sam_info_1(SAM_ENTRY1 *e1, SAM_STR1 *s1)
+{
+ fstring tmp;
+
+ printf("index: 0x%x ", e1->user_idx);
+ printf("RID: 0x%x ", e1->rid_user);
+ printf("acb: 0x%x ", e1->acb_info);
+
+ unistr2_to_ascii(tmp, &s1->uni_acct_name, sizeof(tmp)-1);
+ printf("Account: %s\t", tmp);
+
+ unistr2_to_ascii(tmp, &s1->uni_full_name, sizeof(tmp)-1);
+ printf("Name: %s\t", tmp);
+
+ unistr2_to_ascii(tmp, &s1->uni_acct_desc, sizeof(tmp)-1);
+ printf("Desc: %s\n", tmp);
+}
+
+static void display_sam_info_2(SAM_ENTRY2 *e2, SAM_STR2 *s2)
+{
+ fstring tmp;
+
+ printf("index: 0x%x ", e2->user_idx);
+ printf("RID: 0x%x ", e2->rid_user);
+ printf("acb: 0x%x ", e2->acb_info);
+
+ unistr2_to_ascii(tmp, &s2->uni_srv_name, sizeof(tmp)-1);
+ printf("Account: %s\t", tmp);
+
+ unistr2_to_ascii(tmp, &s2->uni_srv_desc, sizeof(tmp)-1);
+ printf("Name: %s\n", tmp);
+
+}
+
+static void display_sam_info_3(SAM_ENTRY3 *e3, SAM_STR3 *s3)
+{
+ fstring tmp;
+
+ printf("index: 0x%x ", e3->grp_idx);
+ printf("RID: 0x%x ", e3->rid_grp);
+ printf("attr: 0x%x ", e3->attr);
+
+ unistr2_to_ascii(tmp, &s3->uni_grp_name, sizeof(tmp)-1);
+ printf("Account: %s\t", tmp);
+
+ unistr2_to_ascii(tmp, &s3->uni_grp_desc, sizeof(tmp)-1);
+ printf("Name: %s\n", tmp);
+
+}
+
+static void display_sam_info_4(SAM_ENTRY4 *e4, SAM_STR4 *s4)
+{
+ int i;
+
+ printf("index: %d ", e4->user_idx);
+
+ printf("Account: ");
+ for (i=0; i<s4->acct_name.str_str_len; i++)
+ printf("%c", s4->acct_name.buffer[i]);
+ printf("\n");
+
+}
+
+static void display_sam_info_5(SAM_ENTRY5 *e5, SAM_STR5 *s5)
+{
+ int i;
+
+ printf("index: 0x%x ", e5->grp_idx);
+
+ printf("Account: ");
+ for (i=0; i<s5->grp_name.str_str_len; i++)
+ printf("%c", s5->grp_name.buffer[i]);
+ printf("\n");
+
+}
+
+/****************************************************************************
+ Try samr_connect4 first, then samr_conenct if it fails
+ ****************************************************************************/
+static NTSTATUS try_samr_connects(struct cli_state *cli, TALLOC_CTX *mem_ctx,
+ uint32 access_mask, POLICY_HND *connect_pol)
+{
+ NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+
+ result = cli_samr_connect4(cli, mem_ctx, access_mask, connect_pol);
+ if (!NT_STATUS_IS_OK(result)) {
+ result = cli_samr_connect(cli, mem_ctx, access_mask,
+ connect_pol);
+ }
+ return result;
+}
+
+/**********************************************************************
+ * Query user information
+ */
+static NTSTATUS cmd_samr_query_user(struct cli_state *cli,
+ TALLOC_CTX *mem_ctx,
+ int argc, const char **argv)
+{
+ POLICY_HND connect_pol, domain_pol, user_pol;
+ NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+ uint32 info_level = 21;
+ uint32 access_mask = MAXIMUM_ALLOWED_ACCESS;
+ SAM_USERINFO_CTR *user_ctr;
+ fstring server;
+ uint32 user_rid;
+
+ if ((argc < 2) || (argc > 4)) {
+ printf("Usage: %s rid [info level] [access mask] \n", argv[0]);
+ return NT_STATUS_OK;
+ }
+
+ sscanf(argv[1], "%i", &user_rid);
+
+ if (argc > 2)
+ sscanf(argv[2], "%i", &info_level);
+
+ if (argc > 3)
+ sscanf(argv[3], "%x", &access_mask);
+
+
+ slprintf(server, sizeof(fstring)-1, "\\\\%s", cli->desthost);
+ strupper_m(server);
+
+ result = try_samr_connects(cli, mem_ctx, MAXIMUM_ALLOWED_ACCESS,
+ &connect_pol);
+
+ if (!NT_STATUS_IS_OK(result))
+ goto done;
+
+ result = cli_samr_open_domain(cli, mem_ctx, &connect_pol,
+ MAXIMUM_ALLOWED_ACCESS,
+ &domain_sid, &domain_pol);
+
+ if (!NT_STATUS_IS_OK(result))
+ goto done;
+
+ result = cli_samr_open_user(cli, mem_ctx, &domain_pol,
+ access_mask,
+ user_rid, &user_pol);
+
+ if (!NT_STATUS_IS_OK(result))
+ goto done;
+
+ ZERO_STRUCT(user_ctr);
+
+ result = cli_samr_query_userinfo(cli, mem_ctx, &user_pol,
+ info_level, &user_ctr);
+
+ if (!NT_STATUS_IS_OK(result))
+ goto done;
+
+ display_sam_user_info_21(user_ctr->info.id21);
+
+done:
+ return result;
+}
+
+/****************************************************************************
+ display group info
+ ****************************************************************************/
+static void display_group_info1(GROUP_INFO1 *info1)
+{
+ fstring temp;
+
+ unistr2_to_ascii(temp, &info1->uni_acct_name, sizeof(temp)-1);
+ printf("\tGroup Name:\t%s\n", temp);
+ unistr2_to_ascii(temp, &info1->uni_acct_desc, sizeof(temp)-1);
+ printf("\tDescription:\t%s\n", temp);
+ printf("\tunk1:%d\n", info1->unknown_1);
+ printf("\tNum Members:%d\n", info1->num_members);
+}
+
+/****************************************************************************
+ display group info
+ ****************************************************************************/
+static void display_group_info4(GROUP_INFO4 *info4)
+{
+ fstring desc;
+
+ unistr2_to_ascii(desc, &info4->uni_acct_desc, sizeof(desc)-1);
+ printf("\tGroup Description:%s\n", desc);
+}
+
+/****************************************************************************
+ display sam sync structure
+ ****************************************************************************/
+static void display_group_info_ctr(GROUP_INFO_CTR *ctr)
+{
+ switch (ctr->switch_value1) {
+ case 1: {
+ display_group_info1(&ctr->group.info1);
+ break;
+ }
+ case 4: {
+ display_group_info4(&ctr->group.info4);
+ break;
+ }
+ }
+}
+
+/***********************************************************************
+ * Query group information
+ */
+static NTSTATUS cmd_samr_query_group(struct cli_state *cli,
+ TALLOC_CTX *mem_ctx,
+ int argc, const char **argv)
+{
+ POLICY_HND connect_pol, domain_pol, group_pol;
+ NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+ uint32 info_level = 1;
+ uint32 access_mask = MAXIMUM_ALLOWED_ACCESS;
+ GROUP_INFO_CTR *group_ctr;
+ fstring server;
+ uint32 group_rid;
+
+ if ((argc < 2) || (argc > 4)) {
+ printf("Usage: %s rid [info level] [access mask]\n", argv[0]);
+ return NT_STATUS_OK;
+ }
+
+ sscanf(argv[1], "%i", &group_rid);
+
+ if (argc > 2)
+ sscanf(argv[2], "%i", &info_level);
+
+ if (argc > 3)
+ sscanf(argv[3], "%x", &access_mask);
+
+ slprintf(server, sizeof(fstring)-1, "\\\\%s", cli->desthost);
+ strupper_m(server);
+
+ result = try_samr_connects(cli, mem_ctx, MAXIMUM_ALLOWED_ACCESS,
+ &connect_pol);
+
+ if (!NT_STATUS_IS_OK(result))
+ goto done;
+
+ result = cli_samr_open_domain(cli, mem_ctx, &connect_pol,
+ MAXIMUM_ALLOWED_ACCESS,
+ &domain_sid, &domain_pol);
+
+ if (!NT_STATUS_IS_OK(result))
+ goto done;
+
+ result = cli_samr_open_group(cli, mem_ctx, &domain_pol,
+ access_mask,
+ group_rid, &group_pol);
+
+ if (!NT_STATUS_IS_OK(result))
+ goto done;
+
+ result = cli_samr_query_groupinfo(cli, mem_ctx, &group_pol,
+ info_level, &group_ctr);
+ if (!NT_STATUS_IS_OK(result)) {
+ goto done;
+ }
+
+ display_group_info_ctr(group_ctr);
+
+done:
+ return result;
+}
+
+/* Query groups a user is a member of */
+
+static NTSTATUS cmd_samr_query_usergroups(struct cli_state *cli,
+ TALLOC_CTX *mem_ctx,
+ int argc, const char **argv)
+{
+ POLICY_HND connect_pol,
+ domain_pol,
+ user_pol;
+ NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+ uint32 num_groups,
+ user_rid;
+ uint32 access_mask = MAXIMUM_ALLOWED_ACCESS;
+ DOM_GID *user_gids;
+ int i;
+ fstring server;
+
+ if ((argc < 2) || (argc > 3)) {
+ printf("Usage: %s rid [access mask]\n", argv[0]);
+ return NT_STATUS_OK;
+ }
+
+ sscanf(argv[1], "%i", &user_rid);
+
+ if (argc > 2)
+ sscanf(argv[2], "%x", &access_mask);
+
+ slprintf(server, sizeof(fstring)-1, "\\\\%s", cli->desthost);
+ strupper_m(server);
+
+ result = try_samr_connects(cli, mem_ctx, MAXIMUM_ALLOWED_ACCESS,
+ &connect_pol);
+
+ if (!NT_STATUS_IS_OK(result))
+ goto done;
+
+ result = cli_samr_open_domain(cli, mem_ctx, &connect_pol,
+ MAXIMUM_ALLOWED_ACCESS,
+ &domain_sid, &domain_pol);
+
+ if (!NT_STATUS_IS_OK(result))
+ goto done;
+
+ result = cli_samr_open_user(cli, mem_ctx, &domain_pol,
+ access_mask,
+ user_rid, &user_pol);
+
+ if (!NT_STATUS_IS_OK(result))
+ goto done;
+
+ result = cli_samr_query_usergroups(cli, mem_ctx, &user_pol,
+ &num_groups, &user_gids);
+
+ if (!NT_STATUS_IS_OK(result))
+ goto done;
+
+ for (i = 0; i < num_groups; i++) {
+ printf("\tgroup rid:[0x%x] attr:[0x%x]\n",
+ user_gids[i].g_rid, user_gids[i].attr);
+ }
+
+ done:
+ return result;
+}
+
+/* Query aliases a user is a member of */
+
+static NTSTATUS cmd_samr_query_useraliases(struct cli_state *cli,
+ TALLOC_CTX *mem_ctx,
+ int argc, const char **argv)
+{
+ POLICY_HND connect_pol, domain_pol;
+ NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+ uint32 user_rid, num_aliases, *alias_rids;
+ uint32 access_mask = MAXIMUM_ALLOWED_ACCESS;
+ int i;
+ fstring server;
+ DOM_SID tmp_sid;
+ DOM_SID2 sid;
+ DOM_SID global_sid_Builtin;
+
+ string_to_sid(&global_sid_Builtin, "S-1-5-32");
+
+ if ((argc < 3) || (argc > 4)) {
+ printf("Usage: %s builtin|domain rid [access mask]\n", argv[0]);
+ return NT_STATUS_OK;
+ }
+
+ sscanf(argv[2], "%i", &user_rid);
+
+ if (argc > 3)
+ sscanf(argv[3], "%x", &access_mask);
+
+ slprintf(server, sizeof(fstring)-1, "\\\\%s", cli->desthost);
+ strupper_m(server);
+
+ result = try_samr_connects(cli, mem_ctx, MAXIMUM_ALLOWED_ACCESS,
+ &connect_pol);
+
+ if (!NT_STATUS_IS_OK(result))
+ goto done;
+
+ if (StrCaseCmp(argv[1], "domain")==0)
+ result = cli_samr_open_domain(cli, mem_ctx, &connect_pol,
+ access_mask,
+ &domain_sid, &domain_pol);
+ else if (StrCaseCmp(argv[1], "builtin")==0)
+ result = cli_samr_open_domain(cli, mem_ctx, &connect_pol,
+ access_mask,
+ &global_sid_Builtin, &domain_pol);
+ else
+ return NT_STATUS_OK;
+
+ if (!NT_STATUS_IS_OK(result))
+ goto done;
+
+ sid_copy(&tmp_sid, &domain_sid);
+ sid_append_rid(&tmp_sid, user_rid);
+ init_dom_sid2(&sid, &tmp_sid);
+
+ result = cli_samr_query_useraliases(cli, mem_ctx, &domain_pol, 1, &sid, &num_aliases, &alias_rids);
+
+ if (!NT_STATUS_IS_OK(result))
+ goto done;
+
+ for (i = 0; i < num_aliases; i++) {
+ printf("\tgroup rid:[0x%x]\n", alias_rids[i]);
+ }
+
+ done:
+ return result;
+}
+
+/* Query members of a group */
+
+static NTSTATUS cmd_samr_query_groupmem(struct cli_state *cli,
+ TALLOC_CTX *mem_ctx,
+ int argc, const char **argv)
+{
+ POLICY_HND connect_pol, domain_pol, group_pol;
+ NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+ uint32 num_members, *group_rids, *group_attrs, group_rid;
+ uint32 access_mask = MAXIMUM_ALLOWED_ACCESS;
+ int i;
+ fstring server;
+
+ if ((argc < 2) || (argc > 3)) {
+ printf("Usage: %s rid [access mask]\n", argv[0]);
+ return NT_STATUS_OK;
+ }
+
+ sscanf(argv[1], "%i", &group_rid);
+
+ if (argc > 2)
+ sscanf(argv[2], "%x", &access_mask);
+
+ slprintf(server, sizeof(fstring)-1, "\\\\%s", cli->desthost);
+ strupper_m(server);
+
+ result = try_samr_connects(cli, mem_ctx, MAXIMUM_ALLOWED_ACCESS,
+ &connect_pol);
+
+ if (!NT_STATUS_IS_OK(result))
+ goto done;
+
+ result = cli_samr_open_domain(cli, mem_ctx, &connect_pol,
+ MAXIMUM_ALLOWED_ACCESS,
+ &domain_sid, &domain_pol);
+
+ if (!NT_STATUS_IS_OK(result))
+ goto done;
+
+ result = cli_samr_open_group(cli, mem_ctx, &domain_pol,
+ access_mask,
+ group_rid, &group_pol);
+
+ if (!NT_STATUS_IS_OK(result))
+ goto done;
+
+ result = cli_samr_query_groupmem(cli, mem_ctx, &group_pol,
+ &num_members, &group_rids,
+ &group_attrs);
+
+ if (!NT_STATUS_IS_OK(result))
+ goto done;
+
+ for (i = 0; i < num_members; i++) {
+ printf("\trid:[0x%x] attr:[0x%x]\n", group_rids[i],
+ group_attrs[i]);
+ }
+
+ done:
+ return result;
+}
+
+/* Enumerate domain users */
+
+static NTSTATUS cmd_samr_enum_dom_users(struct cli_state *cli,
+ TALLOC_CTX *mem_ctx,
+ int argc, const char **argv)
+{
+ POLICY_HND connect_pol, domain_pol;
+ NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+ uint32 start_idx, size, num_dom_users, i;
+ char **dom_users;
+ uint32 *dom_rids;
+ uint32 access_mask = MAXIMUM_ALLOWED_ACCESS;
+ uint16 acb_mask = ACB_NORMAL;
+ BOOL got_connect_pol = False, got_domain_pol = False;
+
+ if ((argc < 1) || (argc > 2)) {
+ printf("Usage: %s [access_mask]\n", argv[0]);
+ return NT_STATUS_OK;
+ }
+
+ if (argc > 1)
+ sscanf(argv[1], "%x", &access_mask);
+
+ /* Get sam policy handle */
+
+ result = try_samr_connects(cli, mem_ctx, MAXIMUM_ALLOWED_ACCESS,
+ &connect_pol);
+
+ if (!NT_STATUS_IS_OK(result))
+ goto done;
+
+ got_connect_pol = True;
+
+ /* Get domain policy handle */
+
+ result = cli_samr_open_domain(cli, mem_ctx, &connect_pol,
+ access_mask,
+ &domain_sid, &domain_pol);
+
+ if (!NT_STATUS_IS_OK(result))
+ goto done;
+
+ got_domain_pol = True;
+
+ /* Enumerate domain users */
+
+ start_idx = 0;
+ size = 0xffff;
+
+ do {
+ result = cli_samr_enum_dom_users(
+ cli, mem_ctx, &domain_pol, &start_idx, acb_mask,
+ size, &dom_users, &dom_rids, &num_dom_users);
+
+ if (NT_STATUS_IS_OK(result) ||
+ NT_STATUS_V(result) == NT_STATUS_V(STATUS_MORE_ENTRIES)) {
+
+ for (i = 0; i < num_dom_users; i++)
+ printf("user:[%s] rid:[0x%x]\n",
+ dom_users[i], dom_rids[i]);
+ }
+
+ } while (NT_STATUS_V(result) == NT_STATUS_V(STATUS_MORE_ENTRIES));
+
+ done:
+ if (got_domain_pol)
+ cli_samr_close(cli, mem_ctx, &domain_pol);
+
+ if (got_connect_pol)
+ cli_samr_close(cli, mem_ctx, &connect_pol);
+
+ return result;
+}
+
+/* Enumerate domain groups */
+
+static NTSTATUS cmd_samr_enum_dom_groups(struct cli_state *cli,
+ TALLOC_CTX *mem_ctx,
+ int argc, const char **argv)
+{
+ POLICY_HND connect_pol, domain_pol;
+ NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+ uint32 start_idx, size, num_dom_groups, i;
+ uint32 access_mask = MAXIMUM_ALLOWED_ACCESS;
+ struct acct_info *dom_groups;
+ BOOL got_connect_pol = False, got_domain_pol = False;
+
+ if ((argc < 1) || (argc > 2)) {
+ printf("Usage: %s [access_mask]\n", argv[0]);
+ return NT_STATUS_OK;
+ }
+
+ if (argc > 1)
+ sscanf(argv[1], "%x", &access_mask);
+
+ /* Get sam policy handle */
+
+ result = try_samr_connects(cli, mem_ctx, MAXIMUM_ALLOWED_ACCESS,
+ &connect_pol);
+
+ if (!NT_STATUS_IS_OK(result))
+ goto done;
+
+ got_connect_pol = True;
+
+ /* Get domain policy handle */
+
+ result = cli_samr_open_domain(cli, mem_ctx, &connect_pol,
+ access_mask,
+ &domain_sid, &domain_pol);
+
+ if (!NT_STATUS_IS_OK(result))
+ goto done;
+
+ got_domain_pol = True;
+
+ /* Enumerate domain groups */
+
+ start_idx = 0;
+ size = 0xffff;
+
+ do {
+ result = cli_samr_enum_dom_groups(
+ cli, mem_ctx, &domain_pol, &start_idx, size,
+ &dom_groups, &num_dom_groups);
+
+ if (NT_STATUS_IS_OK(result) ||
+ NT_STATUS_V(result) == NT_STATUS_V(STATUS_MORE_ENTRIES)) {
+
+ for (i = 0; i < num_dom_groups; i++)
+ printf("group:[%s] rid:[0x%x]\n",
+ dom_groups[i].acct_name,
+ dom_groups[i].rid);
+ }
+
+ } while (NT_STATUS_V(result) == NT_STATUS_V(STATUS_MORE_ENTRIES));
+
+ done:
+ if (got_domain_pol)
+ cli_samr_close(cli, mem_ctx, &domain_pol);
+
+ if (got_connect_pol)
+ cli_samr_close(cli, mem_ctx, &connect_pol);
+
+ return result;
+}
+
+/* Enumerate alias groups */
+
+static NTSTATUS cmd_samr_enum_als_groups(struct cli_state *cli,
+ TALLOC_CTX *mem_ctx,
+ int argc, const char **argv)
+{
+ POLICY_HND connect_pol, domain_pol;
+ NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+ uint32 start_idx, size, num_als_groups, i;
+ uint32 access_mask = MAXIMUM_ALLOWED_ACCESS;
+ struct acct_info *als_groups;
+ DOM_SID global_sid_Builtin;
+ BOOL got_connect_pol = False, got_domain_pol = False;
+
+ string_to_sid(&global_sid_Builtin, "S-1-5-32");
+
+ if ((argc < 2) || (argc > 3)) {
+ printf("Usage: %s builtin|domain [access mask]\n", argv[0]);
+ return NT_STATUS_OK;
+ }
+
+ if (argc > 2)
+ sscanf(argv[2], "%x", &access_mask);
+
+ /* Get sam policy handle */
+
+ result = try_samr_connects(cli, mem_ctx, MAXIMUM_ALLOWED_ACCESS,
+ &connect_pol);
+
+ if (!NT_STATUS_IS_OK(result))
+ goto done;
+
+ got_connect_pol = True;
+
+ /* Get domain policy handle */
+
+ if (StrCaseCmp(argv[1], "domain")==0)
+ result = cli_samr_open_domain(cli, mem_ctx, &connect_pol,
+ access_mask,
+ &domain_sid, &domain_pol);
+ else if (StrCaseCmp(argv[1], "builtin")==0)
+ result = cli_samr_open_domain(cli, mem_ctx, &connect_pol,
+ access_mask,
+ &global_sid_Builtin, &domain_pol);
+ else
+ return NT_STATUS_OK;
+
+ if (!NT_STATUS_IS_OK(result))
+ goto done;
+
+ got_domain_pol = True;
+
+ /* Enumerate alias groups */
+
+ start_idx = 0;
+ size = 0xffff; /* Number of groups to retrieve */
+
+ do {
+ result = cli_samr_enum_als_groups(
+ cli, mem_ctx, &domain_pol, &start_idx, size,
+ &als_groups, &num_als_groups);
+
+ if (NT_STATUS_IS_OK(result) ||
+ NT_STATUS_V(result) == NT_STATUS_V(STATUS_MORE_ENTRIES)) {
+
+ for (i = 0; i < num_als_groups; i++)
+ printf("group:[%s] rid:[0x%x]\n",
+ als_groups[i].acct_name,
+ als_groups[i].rid);
+ }
+ } while (NT_STATUS_V(result) == NT_STATUS_V(STATUS_MORE_ENTRIES));
+
+ done:
+ if (got_domain_pol)
+ cli_samr_close(cli, mem_ctx, &domain_pol);
+
+ if (got_connect_pol)
+ cli_samr_close(cli, mem_ctx, &connect_pol);
+
+ return result;
+}
+
+/* Query alias membership */
+
+static NTSTATUS cmd_samr_query_aliasmem(struct cli_state *cli,
+ TALLOC_CTX *mem_ctx,
+ int argc, const char **argv)
+{
+ POLICY_HND connect_pol, domain_pol, alias_pol;
+ NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+ uint32 alias_rid, num_members, i;
+ uint32 access_mask = MAXIMUM_ALLOWED_ACCESS;
+ DOM_SID *alias_sids;
+ DOM_SID global_sid_Builtin;
+
+ string_to_sid(&global_sid_Builtin, "S-1-5-32");
+
+ if ((argc < 3) || (argc > 4)) {
+ printf("Usage: %s builtin|domain rid [access mask]\n", argv[0]);
+ return NT_STATUS_OK;
+ }
+
+ sscanf(argv[2], "%i", &alias_rid);
+
+ if (argc > 3)
+ sscanf(argv[3], "%x", &access_mask);
+
+ /* Open SAMR handle */
+
+ result = try_samr_connects(cli, mem_ctx, MAXIMUM_ALLOWED_ACCESS,
+ &connect_pol);
+
+ if (!NT_STATUS_IS_OK(result))
+ goto done;
+
+ /* Open handle on domain */
+
+ if (StrCaseCmp(argv[1], "domain")==0)
+ result = cli_samr_open_domain(cli, mem_ctx, &connect_pol,
+ MAXIMUM_ALLOWED_ACCESS,
+ &domain_sid, &domain_pol);
+ else if (StrCaseCmp(argv[1], "builtin")==0)
+ result = cli_samr_open_domain(cli, mem_ctx, &connect_pol,
+ MAXIMUM_ALLOWED_ACCESS,
+ &global_sid_Builtin, &domain_pol);
+ else
+ return NT_STATUS_OK;
+
+ if (!NT_STATUS_IS_OK(result))
+ goto done;
+
+ /* Open handle on alias */
+
+ result = cli_samr_open_alias(cli, mem_ctx, &domain_pol,
+ access_mask,
+ alias_rid, &alias_pol);
+ if (!NT_STATUS_IS_OK(result))
+ goto done;
+
+ result = cli_samr_query_aliasmem(cli, mem_ctx, &alias_pol,
+ &num_members, &alias_sids);
+
+ if (!NT_STATUS_IS_OK(result))
+ goto done;
+
+ for (i = 0; i < num_members; i++) {
+ fstring sid_str;
+
+ sid_to_string(sid_str, &alias_sids[i]);
+ printf("\tsid:[%s]\n", sid_str);
+ }
+
+ done:
+ return result;
+}
+
+/* Query display info */
+
+static NTSTATUS cmd_samr_query_dispinfo(struct cli_state *cli,
+ TALLOC_CTX *mem_ctx,
+ int argc, const char **argv)
+{
+ POLICY_HND connect_pol, domain_pol;
+ NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+ uint32 start_idx=0, max_entries=250, max_size = 0xffff, num_entries, i;
+ uint32 access_mask = MAXIMUM_ALLOWED_ACCESS;
+ uint32 info_level = 1;
+ SAM_DISPINFO_CTR ctr;
+ SAM_DISPINFO_1 info1;
+ SAM_DISPINFO_2 info2;
+ SAM_DISPINFO_3 info3;
+ SAM_DISPINFO_4 info4;
+ SAM_DISPINFO_5 info5;
+ int loop_count = 0;
+ BOOL got_params = False; /* Use get_query_dispinfo_params() or not? */
+
+ if (argc > 5) {
+ printf("Usage: %s [info level] [start index] [max entries] [max size] [access mask]\n", argv[0]);
+ return NT_STATUS_OK;
+ }
+
+ if (argc >= 2)
+ sscanf(argv[1], "%i", &info_level);
+
+ if (argc >= 3)
+ sscanf(argv[2], "%i", &start_idx);
+
+ if (argc >= 4) {
+ sscanf(argv[3], "%i", &max_entries);
+ got_params = True;
+ }
+
+ if (argc >= 5) {
+ sscanf(argv[4], "%i", &max_size);
+ got_params = True;
+ }
+
+ if (argc >= 6)
+ sscanf(argv[5], "%x", &access_mask);
+
+ /* Get sam policy handle */
+
+ result = try_samr_connects(cli, mem_ctx, MAXIMUM_ALLOWED_ACCESS,
+ &connect_pol);
+
+ if (!NT_STATUS_IS_OK(result))
+ goto done;
+
+ /* Get domain policy handle */
+
+ result = cli_samr_open_domain(cli, mem_ctx, &connect_pol,
+ access_mask,
+ &domain_sid, &domain_pol);
+
+ if (!NT_STATUS_IS_OK(result))
+ goto done;
+
+ /* Query display info */
+
+ ZERO_STRUCT(ctr);
+ ZERO_STRUCT(info1);
+
+ switch (info_level) {
+ case 1:
+ ZERO_STRUCT(info1);
+ ctr.sam.info1 = &info1;
+ break;
+ case 2:
+ ZERO_STRUCT(info2);
+ ctr.sam.info2 = &info2;
+ break;
+ case 3:
+ ZERO_STRUCT(info3);
+ ctr.sam.info3 = &info3;
+ break;
+ case 4:
+ ZERO_STRUCT(info4);
+ ctr.sam.info4 = &info4;
+ break;
+ case 5:
+ ZERO_STRUCT(info5);
+ ctr.sam.info5 = &info5;
+ break;
+ }
+
+
+ while(1) {
+
+ if (!got_params)
+ get_query_dispinfo_params(
+ loop_count, &max_entries, &max_size);
+
+ result = cli_samr_query_dispinfo(cli, mem_ctx, &domain_pol,
+ &start_idx, info_level,
+ &num_entries, max_entries,
+ max_size, &ctr);
+
+ loop_count++;
+
+ if (!NT_STATUS_IS_OK(result) && !NT_STATUS_EQUAL(result, STATUS_MORE_ENTRIES))
+ break;
+
+ if (num_entries == 0)
+ break;
+
+ for (i = 0; i < num_entries; i++) {
+ switch (info_level) {
+ case 1:
+ display_sam_info_1(&ctr.sam.info1->sam[i], &ctr.sam.info1->str[i]);
+ break;
+ case 2:
+ display_sam_info_2(&ctr.sam.info2->sam[i], &ctr.sam.info2->str[i]);
+ break;
+ case 3:
+ display_sam_info_3(&ctr.sam.info3->sam[i], &ctr.sam.info3->str[i]);
+ break;
+ case 4:
+ display_sam_info_4(&ctr.sam.info4->sam[i], &ctr.sam.info4->str[i]);
+ break;
+ case 5:
+ display_sam_info_5(&ctr.sam.info5->sam[i], &ctr.sam.info5->str[i]);
+ break;
+ }
+ }
+ }
+
+ done:
+ return result;
+}
+
+/* Query domain info */
+
+static NTSTATUS cmd_samr_query_dominfo(struct cli_state *cli,
+ TALLOC_CTX *mem_ctx,
+ int argc, const char **argv)
+{
+ POLICY_HND connect_pol, domain_pol;
+ NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+ uint32 switch_level = 2;
+ uint32 access_mask = MAXIMUM_ALLOWED_ACCESS;
+ SAM_UNK_CTR ctr;
+
+ if (argc > 2) {
+ printf("Usage: %s [info level] [access mask]\n", argv[0]);
+ return NT_STATUS_OK;
+ }
+
+ if (argc > 1)
+ sscanf(argv[1], "%i", &switch_level);
+
+ if (argc > 2)
+ sscanf(argv[2], "%x", &access_mask);
+
+ /* Get sam policy handle */
+
+ result = try_samr_connects(cli, mem_ctx, MAXIMUM_ALLOWED_ACCESS,
+ &connect_pol);
+
+ if (!NT_STATUS_IS_OK(result))
+ goto done;
+
+ /* Get domain policy handle */
+
+ result = cli_samr_open_domain(cli, mem_ctx, &connect_pol,
+ access_mask,
+ &domain_sid, &domain_pol);
+
+ if (!NT_STATUS_IS_OK(result))
+ goto done;
+
+ /* Query domain info */
+
+ result = cli_samr_query_dom_info(cli, mem_ctx, &domain_pol,
+ switch_level, &ctr);
+
+ if (!NT_STATUS_IS_OK(result))
+ goto done;
+
+ /* Display domain info */
+
+ switch (switch_level) {
+ case 1:
+ display_sam_unk_info_1(&ctr.info.inf1);
+ break;
+ case 2:
+ display_sam_unk_info_2(&ctr.info.inf2);
+ break;
+ default:
+ printf("cannot display domain info for switch value %d\n",
+ switch_level);
+ break;
+ }
+
+ done:
+
+ cli_samr_close(cli, mem_ctx, &domain_pol);
+ cli_samr_close(cli, mem_ctx, &connect_pol);
+ return result;
+}
+
+/* Create domain user */
+
+static NTSTATUS cmd_samr_create_dom_user(struct cli_state *cli,
+ TALLOC_CTX *mem_ctx,
+ int argc, const char **argv)
+{
+ POLICY_HND connect_pol, domain_pol, user_pol;
+ NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+ const char *acct_name;
+ uint16 acb_info;
+ uint32 unknown, user_rid;
+ uint32 access_mask = MAXIMUM_ALLOWED_ACCESS;
+
+ if ((argc < 2) || (argc > 3)) {
+ printf("Usage: %s username [access mask]\n", argv[0]);
+ return NT_STATUS_OK;
+ }
+
+ acct_name = argv[1];
+
+ if (argc > 2)
+ sscanf(argv[2], "%x", &access_mask);
+
+ /* Get sam policy handle */
+
+ result = try_samr_connects(cli, mem_ctx, MAXIMUM_ALLOWED_ACCESS,
+ &connect_pol);
+
+ if (!NT_STATUS_IS_OK(result))
+ goto done;
+
+ /* Get domain policy handle */
+
+ result = cli_samr_open_domain(cli, mem_ctx, &connect_pol,
+ access_mask,
+ &domain_sid, &domain_pol);
+
+ if (!NT_STATUS_IS_OK(result))
+ goto done;
+
+ /* Create domain user */
+
+ acb_info = ACB_NORMAL;
+ unknown = 0xe005000b; /* No idea what this is - a permission mask? */
+
+ result = cli_samr_create_dom_user(cli, mem_ctx, &domain_pol,
+ acct_name, acb_info, unknown,
+ &user_pol, &user_rid);
+
+ if (!NT_STATUS_IS_OK(result))
+ goto done;
+
+ done:
+ return result;
+}
+
+/* Lookup sam names */
+
+static NTSTATUS cmd_samr_lookup_names(struct cli_state *cli,
+ TALLOC_CTX *mem_ctx,
+ int argc, const char **argv)
+{
+ NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+ POLICY_HND connect_pol, domain_pol;
+ uint32 flags = 0x000003e8; /* Unknown */
+ uint32 num_rids, num_names, *name_types, *rids;
+ const char **names;
+ int i;
+ DOM_SID global_sid_Builtin;
+
+ string_to_sid(&global_sid_Builtin, "S-1-5-32");
+
+ if (argc < 3) {
+ printf("Usage: %s domain|builtin name1 [name2 [name3] [...]]\n", argv[0]);
+ printf("check on the domain SID: S-1-5-21-x-y-z\n");
+ printf("or check on the builtin SID: S-1-5-32\n");
+ return NT_STATUS_OK;
+ }
+
+ /* Get sam policy and domain handles */
+
+ result = try_samr_connects(cli, mem_ctx, MAXIMUM_ALLOWED_ACCESS,
+ &connect_pol);
+
+ if (!NT_STATUS_IS_OK(result))
+ goto done;
+
+ if (StrCaseCmp(argv[1], "domain")==0)
+ result = cli_samr_open_domain(cli, mem_ctx, &connect_pol,
+ MAXIMUM_ALLOWED_ACCESS,
+ &domain_sid, &domain_pol);
+ else if (StrCaseCmp(argv[1], "builtin")==0)
+ result = cli_samr_open_domain(cli, mem_ctx, &connect_pol,
+ MAXIMUM_ALLOWED_ACCESS,
+ &global_sid_Builtin, &domain_pol);
+ else
+ return NT_STATUS_OK;
+
+ if (!NT_STATUS_IS_OK(result))
+ goto done;
+
+ /* Look up names */
+
+ num_names = argc - 2;
+ names = (const char **)talloc(mem_ctx, sizeof(char *) * num_names);
+
+ for (i = 0; i < argc - 2; i++)
+ names[i] = argv[i + 2];
+
+ result = cli_samr_lookup_names(cli, mem_ctx, &domain_pol,
+ flags, num_names, names,
+ &num_rids, &rids, &name_types);
+
+ if (!NT_STATUS_IS_OK(result))
+ goto done;
+
+ /* Display results */
+
+ for (i = 0; i < num_names; i++)
+ printf("name %s: 0x%x (%d)\n", names[i], rids[i],
+ name_types[i]);
+
+ done:
+ return result;
+}
+
+/* Lookup sam rids */
+
+static NTSTATUS cmd_samr_lookup_rids(struct cli_state *cli,
+ TALLOC_CTX *mem_ctx,
+ int argc, const char **argv)
+{
+ NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+ POLICY_HND connect_pol, domain_pol;
+ uint32 flags = 0x000003e8; /* Unknown */
+ uint32 num_rids, num_names, *rids, *name_types;
+ char **names;
+ int i;
+
+ if (argc < 2) {
+ printf("Usage: %s rid1 [rid2 [rid3] [...]]\n", argv[0]);
+ return NT_STATUS_OK;
+ }
+
+ /* Get sam policy and domain handles */
+
+ result = try_samr_connects(cli, mem_ctx, MAXIMUM_ALLOWED_ACCESS,
+ &connect_pol);
+
+ if (!NT_STATUS_IS_OK(result))
+ goto done;
+
+ result = cli_samr_open_domain(cli, mem_ctx, &connect_pol,
+ MAXIMUM_ALLOWED_ACCESS,
+ &domain_sid, &domain_pol);
+
+ if (!NT_STATUS_IS_OK(result))
+ goto done;
+
+ /* Look up rids */
+
+ num_rids = argc - 1;
+ rids = (uint32 *)talloc(mem_ctx, sizeof(uint32) * num_rids);
+
+ for (i = 0; i < argc - 1; i++)
+ sscanf(argv[i + 1], "%i", &rids[i]);
+
+ result = cli_samr_lookup_rids(cli, mem_ctx, &domain_pol,
+ flags, num_rids, rids,
+ &num_names, &names, &name_types);
+
+ if (!NT_STATUS_IS_OK(result))
+ goto done;
+
+ /* Display results */
+
+ for (i = 0; i < num_names; i++)
+ printf("rid 0x%x: %s (%d)\n", rids[i], names[i], name_types[i]);
+
+ done:
+ return result;
+}
+
+/* Delete domain user */
+
+static NTSTATUS cmd_samr_delete_dom_user(struct cli_state *cli,
+ TALLOC_CTX *mem_ctx,
+ int argc, const char **argv)
+{
+ NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+ POLICY_HND connect_pol, domain_pol, user_pol;
+ uint32 access_mask = MAXIMUM_ALLOWED_ACCESS;
+
+ if ((argc < 2) || (argc > 3)) {
+ printf("Usage: %s username\n", argv[0]);
+ return NT_STATUS_OK;
+ }
+
+ if (argc > 2)
+ sscanf(argv[2], "%x", &access_mask);
+
+ /* Get sam policy and domain handles */
+
+ result = try_samr_connects(cli, mem_ctx, MAXIMUM_ALLOWED_ACCESS,
+ &connect_pol);
+
+ if (!NT_STATUS_IS_OK(result))
+ goto done;
+
+ result = cli_samr_open_domain(cli, mem_ctx, &connect_pol,
+ MAXIMUM_ALLOWED_ACCESS,
+ &domain_sid, &domain_pol);
+
+ if (!NT_STATUS_IS_OK(result))
+ goto done;
+
+ /* Get handle on user */
+
+ {
+ uint32 *user_rids, num_rids, *name_types;
+ uint32 flags = 0x000003e8; /* Unknown */
+
+ result = cli_samr_lookup_names(cli, mem_ctx, &domain_pol,
+ flags, 1, (const char **)&argv[1],
+ &num_rids, &user_rids,
+ &name_types);
+
+ if (!NT_STATUS_IS_OK(result))
+ goto done;
+
+ result = cli_samr_open_user(cli, mem_ctx, &domain_pol,
+ access_mask,
+ user_rids[0], &user_pol);
+
+ if (!NT_STATUS_IS_OK(result))
+ goto done;
+ }
+
+ /* Delete user */
+
+ result = cli_samr_delete_dom_user(cli, mem_ctx, &user_pol);
+
+ if (!NT_STATUS_IS_OK(result))
+ goto done;
+
+ /* Display results */
+
+ done:
+ return result;
+}
+
+/**********************************************************************
+ * Query user security object
+ */
+static NTSTATUS cmd_samr_query_sec_obj(struct cli_state *cli,
+ TALLOC_CTX *mem_ctx,
+ int argc, const char **argv)
+{
+ POLICY_HND connect_pol, domain_pol, user_pol, *pol;
+ NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+ uint32 info_level = 4;
+ fstring server;
+ uint32 user_rid = 0;
+ TALLOC_CTX *ctx = NULL;
+ SEC_DESC_BUF *sec_desc_buf=NULL;
+ BOOL domain = False;
+
+ ctx=talloc_init("cmd_samr_query_sec_obj");
+
+ if ((argc < 1) || (argc > 2)) {
+ printf("Usage: %s [rid|-d]\n", argv[0]);
+ printf("\tSpecify rid for security on user, -d for security on domain\n");
+ return NT_STATUS_OK;
+ }
+
+ if (argc > 1) {
+ if (strcmp(argv[1], "-d") == 0)
+ domain = True;
+ else
+ sscanf(argv[1], "%i", &user_rid);
+ }
+
+ slprintf(server, sizeof(fstring)-1, "\\\\%s", cli->desthost);
+ strupper_m(server);
+ result = try_samr_connects(cli, mem_ctx, MAXIMUM_ALLOWED_ACCESS,
+ &connect_pol);
+
+ if (!NT_STATUS_IS_OK(result))
+ goto done;
+
+ if (domain || user_rid)
+ result = cli_samr_open_domain(cli, mem_ctx, &connect_pol,
+ MAXIMUM_ALLOWED_ACCESS,
+ &domain_sid, &domain_pol);
+
+ if (!NT_STATUS_IS_OK(result))
+ goto done;
+
+ if (user_rid)
+ result = cli_samr_open_user(cli, mem_ctx, &domain_pol,
+ MAXIMUM_ALLOWED_ACCESS,
+ user_rid, &user_pol);
+
+ if (!NT_STATUS_IS_OK(result))
+ goto done;
+
+ /* Pick which query pol to use */
+
+ pol = &connect_pol;
+
+ if (domain)
+ pol = &domain_pol;
+
+ if (user_rid)
+ pol = &user_pol;
+
+ /* Query SAM security object */
+
+ result = cli_samr_query_sec_obj(cli, mem_ctx, pol, info_level, ctx,
+ &sec_desc_buf);
+
+ if (!NT_STATUS_IS_OK(result))
+ goto done;
+
+ display_sec_desc(sec_desc_buf->sec);
+
+done:
+ talloc_destroy(ctx);
+ return result;
+}
+
+static NTSTATUS cmd_samr_get_dom_pwinfo(struct cli_state *cli,
+ TALLOC_CTX *mem_ctx,
+ int argc, const char **argv)
+{
+ NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+ uint16 unk_0, unk_1, unk_2;
+
+ if (argc != 1) {
+ printf("Usage: %s\n", argv[0]);
+ return NT_STATUS_OK;
+ }
+
+ result = cli_samr_get_dom_pwinfo(cli, mem_ctx, &unk_0, &unk_1, &unk_2);
+
+ if (NT_STATUS_IS_OK(result)) {
+ printf("unk_0 = 0x%08x\n", unk_0);
+ printf("unk_1 = 0x%08x\n", unk_1);
+ printf("unk_2 = 0x%08x\n", unk_2);
+ }
+
+ return result;
+}
+
+/* Look up domain name */
+
+static NTSTATUS cmd_samr_lookup_domain(struct cli_state *cli,
+ TALLOC_CTX *mem_ctx,
+ int argc, const char **argv)
+{
+ POLICY_HND connect_pol, domain_pol;
+ NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+ uint32 access_mask = MAXIMUM_ALLOWED_ACCESS;
+ fstring domain_name,sid_string;
+ DOM_SID sid;
+
+ if (argc != 2) {
+ printf("Usage: %s domain_name\n", argv[0]);
+ return NT_STATUS_OK;
+ }
+
+ sscanf(argv[1], "%s", domain_name);
+
+ result = try_samr_connects(cli, mem_ctx, access_mask, &connect_pol);
+
+ if (!NT_STATUS_IS_OK(result))
+ goto done;
+
+ result = cli_samr_open_domain(cli, mem_ctx, &connect_pol,
+ access_mask, &domain_sid, &domain_pol);
+
+ if (!NT_STATUS_IS_OK(result))
+ goto done;
+
+ result = cli_samr_lookup_domain(
+ cli, mem_ctx, &connect_pol, domain_name, &sid);
+
+ sid_to_string(sid_string,&sid);
+
+ if (NT_STATUS_IS_OK(result))
+ printf("SAMR_LOOKUP_DOMAIN: Domain Name: %s Domain SID: %s\n",
+ domain_name,sid_string);
+
+done:
+ return result;
+}
+
+
+/* List of commands exported by this module */
+
+struct cmd_set samr_commands[] = {
+
+ { "SAMR" },
+
+ { "queryuser", RPC_RTYPE_NTSTATUS, cmd_samr_query_user, NULL, PI_SAMR, "Query user info", "" },
+ { "querygroup", RPC_RTYPE_NTSTATUS, cmd_samr_query_group, NULL, PI_SAMR, "Query group info", "" },
+ { "queryusergroups", RPC_RTYPE_NTSTATUS, cmd_samr_query_usergroups, NULL, PI_SAMR, "Query user groups", "" },
+ { "queryuseraliases", RPC_RTYPE_NTSTATUS, cmd_samr_query_useraliases, NULL, PI_SAMR, "Query user aliases", "" },
+ { "querygroupmem", RPC_RTYPE_NTSTATUS, cmd_samr_query_groupmem, NULL, PI_SAMR, "Query group membership", "" },
+ { "queryaliasmem", RPC_RTYPE_NTSTATUS, cmd_samr_query_aliasmem, NULL, PI_SAMR, "Query alias membership", "" },
+ { "querydispinfo", RPC_RTYPE_NTSTATUS, cmd_samr_query_dispinfo, NULL, PI_SAMR, "Query display info", "" },
+ { "querydominfo", RPC_RTYPE_NTSTATUS, cmd_samr_query_dominfo, NULL, PI_SAMR, "Query domain info", "" },
+ { "enumdomusers", RPC_RTYPE_NTSTATUS, cmd_samr_enum_dom_users, NULL, PI_SAMR, "Enumerate domain users", "" },
+ { "enumdomgroups", RPC_RTYPE_NTSTATUS, cmd_samr_enum_dom_groups, NULL, PI_SAMR, "Enumerate domain groups", "" },
+ { "enumalsgroups", RPC_RTYPE_NTSTATUS, cmd_samr_enum_als_groups, NULL, PI_SAMR, "Enumerate alias groups", "" },
+
+ { "createdomuser", RPC_RTYPE_NTSTATUS, cmd_samr_create_dom_user, NULL, PI_SAMR, "Create domain user", "" },
+ { "samlookupnames", RPC_RTYPE_NTSTATUS, cmd_samr_lookup_names, NULL, PI_SAMR, "Look up names", "" },
+ { "samlookuprids", RPC_RTYPE_NTSTATUS, cmd_samr_lookup_rids, NULL, PI_SAMR, "Look up names", "" },
+ { "deletedomuser", RPC_RTYPE_NTSTATUS, cmd_samr_delete_dom_user, NULL, PI_SAMR, "Delete domain user", "" },
+ { "samquerysecobj", RPC_RTYPE_NTSTATUS, cmd_samr_query_sec_obj, NULL, PI_SAMR, "Query SAMR security object", "" },
+ { "getdompwinfo", RPC_RTYPE_NTSTATUS, cmd_samr_get_dom_pwinfo, NULL, PI_SAMR, "Retrieve domain password info", "" },
+
+ { "lookupdomain", RPC_RTYPE_NTSTATUS, cmd_samr_lookup_domain, NULL, PI_SAMR, "Lookup Domain Name", "" },
+ { NULL }
+};
diff --git a/source/rpcclient/cmd_shutdown.c b/source/rpcclient/cmd_shutdown.c
new file mode 100644
index 00000000000..e42ec30ac13
--- /dev/null
+++ b/source/rpcclient/cmd_shutdown.c
@@ -0,0 +1,112 @@
+/*
+ Unix SMB/CIFS implementation.
+ NT Domain Authentication SMB / MSRPC client
+ Copyright (C) Andrew Tridgell 1994-1997,
+ Copyright (C) Luke Kenneth Casson Leighton 1996-1997,
+ Copyright (C) Simo Sorce 2001,
+ 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 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 "rpcclient.h"
+
+/****************************************************************************
+nt shutdown init
+****************************************************************************/
+static NTSTATUS cmd_shutdown_init(struct cli_state *cli, TALLOC_CTX *mem_ctx,
+ int argc, const char **argv)
+{
+ NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+ fstring msg;
+ uint32 timeout = 20;
+ BOOL force = False;
+ BOOL reboot = False;
+ int opt;
+
+ *msg = 0;
+ optind = 0; /* TODO: test if this hack works on other systems too --simo */
+
+ while ((opt = getopt(argc, argv, "m:t:rf")) != EOF)
+ {
+ /*fprintf (stderr, "[%s]\n", argv[argc-1]);*/
+
+ switch (opt)
+ {
+ case 'm':
+ fstrcpy(msg, optarg);
+ /*fprintf (stderr, "[%s|%s]\n", optarg, msg);*/
+ break;
+
+ case 't':
+ timeout = atoi(optarg);
+ /*fprintf (stderr, "[%s|%d]\n", optarg, timeout);*/
+ break;
+
+ case 'r':
+ reboot = True;
+ break;
+
+ case 'f':
+ force = True;
+ break;
+
+ }
+ }
+
+ /* create an entry */
+ result = cli_shutdown_init(cli, mem_ctx, msg, timeout, reboot, force);
+
+ if (NT_STATUS_IS_OK(result))
+ DEBUG(5,("cmd_shutdown_init: query succeeded\n"));
+ else
+ DEBUG(5,("cmd_shutdown_init: query failed\n"));
+
+ return result;
+}
+
+/****************************************************************************
+abort a shutdown
+****************************************************************************/
+static NTSTATUS cmd_shutdown_abort(struct cli_state *cli,
+ TALLOC_CTX *mem_ctx, int argc,
+ const char **argv)
+{
+ NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+
+ result = cli_shutdown_abort(cli, mem_ctx);
+
+ if (NT_STATUS_IS_OK(result))
+ DEBUG(5,("cmd_shutdown_abort: query succeeded\n"));
+ else
+ DEBUG(5,("cmd_shutdown_abort: query failed\n"));
+
+ return result;
+}
+
+
+/* List of commands exported by this module */
+struct cmd_set shutdown_commands[] = {
+
+ { "SHUTDOWN" },
+
+ { "shutdowninit", RPC_RTYPE_NTSTATUS, cmd_shutdown_init, NULL, PI_SHUTDOWN, "Remote Shutdown (over shutdown pipe)",
+ "syntax: shutdown [-m message] [-t timeout] [-r] [-h] [-f] (-r == reboot, -h == halt, -f == force)" },
+
+ { "shutdownabort", RPC_RTYPE_NTSTATUS, cmd_shutdown_abort, NULL, PI_SHUTDOWN, "Abort Shutdown (over shutdown pipe)",
+ "syntax: shutdownabort" },
+ { NULL }
+};
diff --git a/source/rpcclient/cmd_spoolss.c b/source/rpcclient/cmd_spoolss.c
new file mode 100644
index 00000000000..f5a440c024c
--- /dev/null
+++ b/source/rpcclient/cmd_spoolss.c
@@ -0,0 +1,2338 @@
+/*
+ Unix SMB/CIFS implementation.
+ RPC pipe client
+
+ Copyright (C) Gerald Carter 2001
+ Copyright (C) Tim Potter 2000
+ Copyright (C) Andrew Tridgell 1992-1999
+ Copyright (C) Luke Kenneth Casson Leighton 1996-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 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 "rpcclient.h"
+
+struct table_node {
+ const char *long_archi;
+ const char *short_archi;
+ int version;
+};
+
+static const struct table_node archi_table[]= {
+
+ {"Windows 4.0", "WIN40", 0 },
+ {"Windows NT x86", "W32X86", 2 },
+ {"Windows NT x86", "W32X86", 3 },
+ {"Windows NT R4000", "W32MIPS", 2 },
+ {"Windows NT Alpha_AXP", "W32ALPHA", 2 },
+ {"Windows NT PowerPC", "W32PPC", 2 },
+ {NULL, "", -1 }
+};
+
+/**
+ * @file
+ *
+ * rpcclient module for SPOOLSS rpc pipe.
+ *
+ * This generally just parses and checks command lines, and then calls
+ * a cli_spoolss function.
+ **/
+
+/****************************************************************************
+function to do the mapping between the long architecture name and
+the short one.
+****************************************************************************/
+static const char *cmd_spoolss_get_short_archi(const char *long_archi)
+{
+ int i=-1;
+
+ DEBUG(107,("Getting architecture dependant directory\n"));
+ do {
+ i++;
+ } while ( (archi_table[i].long_archi!=NULL ) &&
+ StrCaseCmp(long_archi, archi_table[i].long_archi) );
+
+ if (archi_table[i].long_archi==NULL) {
+ DEBUGADD(10,("Unknown architecture [%s] !\n", long_archi));
+ return NULL;
+ }
+
+ /* this might be client code - but shouldn't this be an fstrcpy etc? */
+
+
+ DEBUGADD(108,("index: [%d]\n", i));
+ DEBUGADD(108,("long architecture: [%s]\n", archi_table[i].long_archi));
+ DEBUGADD(108,("short architecture: [%s]\n", archi_table[i].short_archi));
+
+ return archi_table[i].short_archi;
+}
+
+#if 0
+/**********************************************************************
+ * dummy function -- placeholder
+ */
+static WERROR cmd_spoolss_not_implemented(struct cli_state *cli,
+ TALLOC_CTX *mem_ctx,
+ int argc, const char **argv)
+{
+ printf ("(*) This command is not currently implemented.\n");
+ return WERR_OK;
+}
+#endif
+
+/***********************************************************************
+ * Get printer information
+ */
+static WERROR cmd_spoolss_open_printer_ex(struct cli_state *cli,
+ TALLOC_CTX *mem_ctx,
+ int argc, const char **argv)
+{
+ WERROR werror;
+ fstring printername;
+ fstring servername, user;
+ POLICY_HND hnd;
+
+ if (argc != 2) {
+ printf("Usage: %s <printername>\n", argv[0]);
+ return WERR_OK;
+ }
+
+ if (!cli)
+ return WERR_GENERAL_FAILURE;
+
+ slprintf(servername, sizeof(servername)-1, "\\\\%s", cli->desthost);
+ strupper_m(servername);
+ fstrcpy(user, cli->user_name);
+ fstrcpy(printername, argv[1]);
+
+ /* Open the printer handle */
+
+ werror = cli_spoolss_open_printer_ex(cli, mem_ctx, printername,
+ "", PRINTER_ALL_ACCESS,
+ servername, user, &hnd);
+
+ if (W_ERROR_IS_OK(werror)) {
+ printf("Printer %s opened successfully\n", printername);
+ werror = cli_spoolss_close_printer(cli, mem_ctx, &hnd);
+
+ if (!W_ERROR_IS_OK(werror)) {
+ printf("Error closing printer handle! (%s)\n",
+ get_dos_error_msg(werror));
+ }
+ }
+
+ return werror;
+}
+
+
+/****************************************************************************
+printer info level 0 display function
+****************************************************************************/
+static void display_print_info_0(PRINTER_INFO_0 *i0)
+{
+ fstring name = "";
+ fstring servername = "";
+
+ if (!i0)
+ return;
+
+ rpcstr_pull(name, i0->printername.buffer, sizeof(name), -1, STR_TERMINATE);
+
+ rpcstr_pull(servername, i0->servername.buffer, sizeof(servername), -1,STR_TERMINATE);
+
+ printf("\tprintername:[%s]\n", name);
+ printf("\tservername:[%s]\n", servername);
+ printf("\tcjobs:[0x%x]\n", i0->cjobs);
+ printf("\ttotal_jobs:[0x%x]\n", i0->total_jobs);
+
+ printf("\t:date: [%d]-[%d]-[%d] (%d)\n", i0->year, i0->month,
+ i0->day, i0->dayofweek);
+ printf("\t:time: [%d]-[%d]-[%d]-[%d]\n", i0->hour, i0->minute,
+ i0->second, i0->milliseconds);
+
+ printf("\tglobal_counter:[0x%x]\n", i0->global_counter);
+ printf("\ttotal_pages:[0x%x]\n", i0->total_pages);
+
+ printf("\tmajorversion:[0x%x]\n", i0->major_version);
+ printf("\tbuildversion:[0x%x]\n", i0->build_version);
+
+ printf("\tunknown7:[0x%x]\n", i0->unknown7);
+ printf("\tunknown8:[0x%x]\n", i0->unknown8);
+ printf("\tunknown9:[0x%x]\n", i0->unknown9);
+ printf("\tsession_counter:[0x%x]\n", i0->session_counter);
+ printf("\tunknown11:[0x%x]\n", i0->unknown11);
+ printf("\tprinter_errors:[0x%x]\n", i0->printer_errors);
+ printf("\tunknown13:[0x%x]\n", i0->unknown13);
+ printf("\tunknown14:[0x%x]\n", i0->unknown14);
+ printf("\tunknown15:[0x%x]\n", i0->unknown15);
+ printf("\tunknown16:[0x%x]\n", i0->unknown16);
+ printf("\tchange_id:[0x%x]\n", i0->change_id);
+ printf("\tunknown18:[0x%x]\n", i0->unknown18);
+ printf("\tstatus:[0x%x]\n", i0->status);
+ printf("\tunknown20:[0x%x]\n", i0->unknown20);
+ printf("\tc_setprinter:[0x%x]\n", i0->c_setprinter);
+ printf("\tunknown22:[0x%x]\n", i0->unknown22);
+ printf("\tunknown23:[0x%x]\n", i0->unknown23);
+ printf("\tunknown24:[0x%x]\n", i0->unknown24);
+ printf("\tunknown25:[0x%x]\n", i0->unknown25);
+ printf("\tunknown26:[0x%x]\n", i0->unknown26);
+ printf("\tunknown27:[0x%x]\n", i0->unknown27);
+ printf("\tunknown28:[0x%x]\n", i0->unknown28);
+ printf("\tunknown29:[0x%x]\n", i0->unknown29);
+
+ printf("\n");
+}
+
+/****************************************************************************
+printer info level 1 display function
+****************************************************************************/
+static void display_print_info_1(PRINTER_INFO_1 *i1)
+{
+ fstring desc = "";
+ fstring name = "";
+ fstring comm = "";
+
+ rpcstr_pull(desc, i1->description.buffer, sizeof(desc), -1,
+ STR_TERMINATE);
+
+ rpcstr_pull(name, i1->name.buffer, sizeof(name), -1, STR_TERMINATE);
+ rpcstr_pull(comm, i1->comment.buffer, sizeof(comm), -1, STR_TERMINATE);
+
+ printf("\tflags:[0x%x]\n", i1->flags);
+ printf("\tname:[%s]\n", name);
+ printf("\tdescription:[%s]\n", desc);
+ printf("\tcomment:[%s]\n", comm);
+
+ printf("\n");
+}
+
+/****************************************************************************
+printer info level 2 display function
+****************************************************************************/
+static void display_print_info_2(PRINTER_INFO_2 *i2)
+{
+ fstring servername = "";
+ fstring printername = "";
+ fstring sharename = "";
+ fstring portname = "";
+ fstring drivername = "";
+ fstring comment = "";
+ fstring location = "";
+ fstring sepfile = "";
+ fstring printprocessor = "";
+ fstring datatype = "";
+ fstring parameters = "";
+
+ rpcstr_pull(servername, i2->servername.buffer,sizeof(servername), -1, STR_TERMINATE);
+
+ rpcstr_pull(printername, i2->printername.buffer,sizeof(printername), -1, STR_TERMINATE);
+
+ rpcstr_pull(sharename, i2->sharename.buffer,sizeof(sharename), -1, STR_TERMINATE);
+
+ rpcstr_pull(portname, i2->portname.buffer,sizeof(portname), -1, STR_TERMINATE);
+
+ rpcstr_pull(drivername, i2->drivername.buffer,sizeof(drivername), -1, STR_TERMINATE);
+
+ rpcstr_pull(comment, i2->comment.buffer,sizeof(comment), -1, STR_TERMINATE);
+
+ rpcstr_pull(location, i2->location.buffer,sizeof(location), -1, STR_TERMINATE);
+
+ rpcstr_pull(sepfile, i2->sepfile.buffer,sizeof(sepfile), -1, STR_TERMINATE);
+
+ rpcstr_pull(printprocessor, i2->printprocessor.buffer,sizeof(printprocessor), -1, STR_TERMINATE);
+
+ rpcstr_pull(datatype, i2->datatype.buffer,sizeof(datatype), -1, STR_TERMINATE);
+
+ rpcstr_pull(parameters, i2->parameters.buffer,sizeof(parameters), -1, STR_TERMINATE);
+
+ printf("\tservername:[%s]\n", servername);
+ printf("\tprintername:[%s]\n", printername);
+ printf("\tsharename:[%s]\n", sharename);
+ printf("\tportname:[%s]\n", portname);
+ printf("\tdrivername:[%s]\n", drivername);
+ printf("\tcomment:[%s]\n", comment);
+ printf("\tlocation:[%s]\n", location);
+ printf("\tsepfile:[%s]\n", sepfile);
+ printf("\tprintprocessor:[%s]\n", printprocessor);
+ printf("\tdatatype:[%s]\n", datatype);
+ printf("\tparameters:[%s]\n", parameters);
+ printf("\tattributes:[0x%x]\n", i2->attributes);
+ printf("\tpriority:[0x%x]\n", i2->priority);
+ printf("\tdefaultpriority:[0x%x]\n", i2->defaultpriority);
+ printf("\tstarttime:[0x%x]\n", i2->starttime);
+ printf("\tuntiltime:[0x%x]\n", i2->untiltime);
+ printf("\tstatus:[0x%x]\n", i2->status);
+ printf("\tcjobs:[0x%x]\n", i2->cjobs);
+ printf("\taverageppm:[0x%x]\n", i2->averageppm);
+
+ if (i2->secdesc)
+ display_sec_desc(i2->secdesc);
+
+ printf("\n");
+}
+
+/****************************************************************************
+printer info level 3 display function
+****************************************************************************/
+static void display_print_info_3(PRINTER_INFO_3 *i3)
+{
+ printf("\tflags:[0x%x]\n", i3->flags);
+
+ display_sec_desc(i3->secdesc);
+
+ printf("\n");
+}
+
+/* Enumerate printers */
+
+static WERROR cmd_spoolss_enum_printers(struct cli_state *cli,
+ TALLOC_CTX *mem_ctx,
+ int argc, const char **argv)
+{
+ WERROR result;
+ uint32 info_level = 1;
+ PRINTER_INFO_CTR ctr;
+ uint32 i = 0, num_printers, needed;
+ fstring name;
+
+ if (argc > 3)
+ {
+ printf("Usage: %s [level] [name]\n", argv[0]);
+ return WERR_OK;
+ }
+
+ if (argc == 2)
+ info_level = atoi(argv[1]);
+
+ if (argc == 3)
+ fstrcpy(name, argv[2]);
+ else {
+ slprintf(name, sizeof(name)-1, "\\\\%s", cli->desthost);
+ strupper_m(name);
+ }
+
+ /* Enumerate printers -- Should we enumerate types other
+ than PRINTER_ENUM_LOCAL? Maybe accept as a parameter? --jerry */
+
+ ZERO_STRUCT(ctr);
+
+ result = cli_spoolss_enum_printers(
+ cli, mem_ctx, 0, &needed, name, PRINTER_ENUM_LOCAL,
+ info_level, &num_printers, &ctr);
+
+ if (W_ERROR_V(result) == ERRinsufficientbuffer)
+ result = cli_spoolss_enum_printers(
+ cli, mem_ctx, needed, NULL, name, PRINTER_ENUM_LOCAL,
+ info_level, &num_printers, &ctr);
+
+ if (W_ERROR_IS_OK(result)) {
+
+ if (!num_printers) {
+ printf ("No printers returned.\n");
+ goto done;
+ }
+
+ for (i = 0; i < num_printers; i++) {
+ switch(info_level) {
+ case 0:
+ display_print_info_0(&ctr.printers_0[i]);
+ break;
+ case 1:
+ display_print_info_1(&ctr.printers_1[i]);
+ break;
+ case 2:
+ display_print_info_2(&ctr.printers_2[i]);
+ break;
+ case 3:
+ display_print_info_3(&ctr.printers_3[i]);
+ break;
+ default:
+ printf("unknown info level %d\n", info_level);
+ goto done;
+ }
+ }
+ }
+ done:
+
+ return result;
+}
+
+/****************************************************************************
+port info level 1 display function
+****************************************************************************/
+static void display_port_info_1(PORT_INFO_1 *i1)
+{
+ fstring buffer;
+
+ rpcstr_pull(buffer, i1->port_name.buffer, sizeof(buffer), -1, STR_TERMINATE);
+ printf("\tPort Name:\t[%s]\n", buffer);
+}
+
+/****************************************************************************
+port info level 2 display function
+****************************************************************************/
+static void display_port_info_2(PORT_INFO_2 *i2)
+{
+ fstring buffer;
+
+ rpcstr_pull(buffer, i2->port_name.buffer, sizeof(buffer), -1, STR_TERMINATE);
+ printf("\tPort Name:\t[%s]\n", buffer);
+ rpcstr_pull(buffer, i2->monitor_name.buffer, sizeof(buffer), -1, STR_TERMINATE);
+
+ printf("\tMonitor Name:\t[%s]\n", buffer);
+ rpcstr_pull(buffer, i2->description.buffer, sizeof(buffer), -1, STR_TERMINATE);
+
+ printf("\tDescription:\t[%s]\n", buffer);
+ printf("\tPort Type:\t" );
+ if ( i2->port_type ) {
+ int comma = 0; /* hack */
+ printf( "[" );
+ if ( i2->port_type & PORT_TYPE_READ ) {
+ printf( "Read" );
+ comma = 1;
+ }
+ if ( i2->port_type & PORT_TYPE_WRITE ) {
+ printf( "%sWrite", comma ? ", " : "" );
+ comma = 1;
+ }
+ /* These two have slightly different interpretations
+ on 95/98/ME but I'm disregarding that for now */
+ if ( i2->port_type & PORT_TYPE_REDIRECTED ) {
+ printf( "%sRedirected", comma ? ", " : "" );
+ comma = 1;
+ }
+ if ( i2->port_type & PORT_TYPE_NET_ATTACHED ) {
+ printf( "%sNet-Attached", comma ? ", " : "" );
+ }
+ printf( "]\n" );
+ } else {
+ printf( "[Unset]\n" );
+ }
+ printf("\tReserved:\t[%d]\n", i2->reserved);
+ printf("\n");
+}
+
+/* Enumerate ports */
+
+static WERROR cmd_spoolss_enum_ports(struct cli_state *cli,
+ TALLOC_CTX *mem_ctx, int argc,
+ const char **argv)
+{
+ WERROR result;
+ uint32 needed, info_level = 1;
+ PORT_INFO_CTR ctr;
+ int returned;
+
+ if (argc > 2) {
+ printf("Usage: %s [level]\n", argv[0]);
+ return WERR_OK;
+ }
+
+ if (argc == 2)
+ info_level = atoi(argv[1]);
+
+ /* Enumerate ports */
+
+ ZERO_STRUCT(ctr);
+
+ result = cli_spoolss_enum_ports(cli, mem_ctx, 0, &needed, info_level,
+ &returned, &ctr);
+
+ if (W_ERROR_V(result) == ERRinsufficientbuffer)
+ result = cli_spoolss_enum_ports(cli, mem_ctx, needed, NULL,
+ info_level, &returned, &ctr);
+
+ if (W_ERROR_IS_OK(result)) {
+ int i;
+
+ for (i = 0; i < returned; i++) {
+ switch (info_level) {
+ case 1:
+ display_port_info_1(&ctr.port.info_1[i]);
+ break;
+ case 2:
+ display_port_info_2(&ctr.port.info_2[i]);
+ break;
+ default:
+ printf("unknown info level %d\n", info_level);
+ break;
+ }
+ }
+ }
+
+ return result;
+}
+
+/***********************************************************************
+ * Set printer comment - use a level2 set.
+ */
+static WERROR cmd_spoolss_setprinter(struct cli_state *cli,
+ TALLOC_CTX *mem_ctx,
+ int argc, const char **argv)
+{
+ POLICY_HND pol;
+ WERROR result;
+ uint32 needed;
+ uint32 info_level = 2;
+ BOOL opened_hnd = False;
+ PRINTER_INFO_CTR ctr;
+ fstring printername,
+ servername,
+ user,
+ comment;
+
+ if (argc == 1 || argc > 3) {
+ printf("Usage: %s printername comment\n", argv[0]);
+
+ return WERR_OK;
+ }
+
+ /* Open a printer handle */
+ if (argc == 3) {
+ fstrcpy(comment, argv[2]);
+ }
+
+ slprintf(servername, sizeof(servername)-1, "\\\\%s", cli->desthost);
+ strupper_m(servername);
+ fstrcpy(printername, argv[1]);
+ fstrcpy(user, cli->user_name);
+
+ /* get a printer handle */
+ result = cli_spoolss_open_printer_ex(cli, mem_ctx, printername, "",
+ PRINTER_ALL_ACCESS, servername,
+ user, &pol);
+
+ if (!W_ERROR_IS_OK(result))
+ goto done;
+
+ opened_hnd = True;
+
+ /* Get printer info */
+ result = cli_spoolss_getprinter(cli, mem_ctx, 0, &needed, &pol, info_level, &ctr);
+
+ if (W_ERROR_V(result) == ERRinsufficientbuffer)
+ result = cli_spoolss_getprinter(cli, mem_ctx, needed, NULL, &pol, info_level, &ctr);
+
+ if (!W_ERROR_IS_OK(result))
+ goto done;
+
+
+ /* Modify the comment. */
+ init_unistr(&ctr.printers_2->comment, comment);
+ ctr.printers_2->devmode = NULL;
+ ctr.printers_2->secdesc = NULL;
+
+ result = cli_spoolss_setprinter(cli, mem_ctx, &pol, info_level, &ctr, 0);
+ if (W_ERROR_IS_OK(result))
+ printf("Success in setting comment.\n");
+
+ done:
+ if (opened_hnd)
+ cli_spoolss_close_printer(cli, mem_ctx, &pol);
+
+ return result;
+}
+
+/***********************************************************************
+ * Get printer information
+ */
+static WERROR cmd_spoolss_getprinter(struct cli_state *cli,
+ TALLOC_CTX *mem_ctx,
+ int argc, const char **argv)
+{
+ POLICY_HND pol;
+ WERROR result;
+ uint32 info_level = 1;
+ BOOL opened_hnd = False;
+ PRINTER_INFO_CTR ctr;
+ fstring printername,
+ servername,
+ user;
+ uint32 needed;
+
+ if (argc == 1 || argc > 3) {
+ printf("Usage: %s <printername> [level]\n", argv[0]);
+ return WERR_OK;
+ }
+
+ /* Open a printer handle */
+ if (argc == 3) {
+ info_level = atoi(argv[2]);
+ }
+
+ slprintf(servername, sizeof(servername)-1, "\\\\%s", cli->desthost);
+ strupper_m(servername);
+ slprintf(printername, sizeof(printername)-1, "%s\\%s", servername, argv[1]);
+ fstrcpy(user, cli->user_name);
+
+ /* get a printer handle */
+
+ result = cli_spoolss_open_printer_ex(cli, mem_ctx, printername,
+ "", MAXIMUM_ALLOWED_ACCESS,
+ servername, user, &pol);
+
+ if (!W_ERROR_IS_OK(result))
+ goto done;
+
+ opened_hnd = True;
+
+ /* Get printer info */
+
+ result = cli_spoolss_getprinter(cli, mem_ctx, 0, &needed,
+ &pol, info_level, &ctr);
+
+ if (W_ERROR_V(result) == ERRinsufficientbuffer)
+ result = cli_spoolss_getprinter(
+ cli, mem_ctx, needed, NULL, &pol, info_level, &ctr);
+
+ if (!W_ERROR_IS_OK(result))
+ goto done;
+
+ /* Display printer info */
+
+ switch (info_level) {
+ case 0:
+ display_print_info_0(ctr.printers_0);
+ break;
+ case 1:
+ display_print_info_1(ctr.printers_1);
+ break;
+ case 2:
+ display_print_info_2(ctr.printers_2);
+ break;
+ case 3:
+ display_print_info_3(ctr.printers_3);
+ break;
+ default:
+ printf("unknown info level %d\n", info_level);
+ break;
+ }
+
+ done:
+ if (opened_hnd)
+ cli_spoolss_close_printer(cli, mem_ctx, &pol);
+
+ return result;
+}
+
+static void display_reg_value(REGISTRY_VALUE value)
+{
+ pstring text;
+
+ switch(value.type) {
+ case REG_DWORD:
+ printf("%s: REG_DWORD: 0x%08x\n", value.valuename,
+ *((uint32 *) value.data_p));
+ break;
+ case REG_SZ:
+ rpcstr_pull(text, value.data_p, sizeof(text), value.size,
+ STR_TERMINATE);
+ printf("%s: REG_SZ: %s\n", value.valuename, text);
+ break;
+ case REG_BINARY:
+ printf("%s: REG_BINARY: unknown length value not displayed\n",
+ value.valuename);
+ break;
+ case REG_MULTI_SZ: {
+ uint16 *curstr = (uint16 *) value.data_p;
+ uint8 *start = value.data_p;
+ printf("%s: REG_MULTI_SZ:\n", value.valuename);
+ while ((*curstr != 0) &&
+ ((uint8 *) curstr < start + value.size)) {
+ rpcstr_pull(text, curstr, sizeof(text), -1,
+ STR_TERMINATE);
+ printf(" %s\n", text);
+ curstr += strlen(text) + 1;
+ }
+ }
+ break;
+ default:
+ printf("%s: unknown type %d\n", value.valuename, value.type);
+ }
+
+}
+
+/***********************************************************************
+ * Get printer data
+ */
+static WERROR cmd_spoolss_getprinterdata(struct cli_state *cli,
+ TALLOC_CTX *mem_ctx,
+ int argc, const char **argv)
+{
+ POLICY_HND pol;
+ WERROR result;
+ BOOL opened_hnd = False;
+ fstring printername,
+ servername,
+ user;
+ uint32 needed;
+ const char *valuename;
+ REGISTRY_VALUE value;
+
+ if (argc != 3) {
+ printf("Usage: %s <printername> <valuename>\n", argv[0]);
+ printf("<printername> of . queries print server\n");
+ return WERR_OK;
+ }
+ valuename = argv[2];
+
+ /* Open a printer handle */
+
+ slprintf(servername, sizeof(servername)-1, "\\\\%s", cli->desthost);
+ strupper_m(servername);
+ if (strncmp(argv[1], ".", sizeof(".")) == 0)
+ fstrcpy(printername, servername);
+ else
+ slprintf(printername, sizeof(servername)-1, "%s\\%s",
+ servername, argv[1]);
+ fstrcpy(user, cli->user_name);
+
+ /* get a printer handle */
+
+ result = cli_spoolss_open_printer_ex(cli, mem_ctx, printername,
+ "", MAXIMUM_ALLOWED_ACCESS,
+ servername, user, &pol);
+
+ if (!W_ERROR_IS_OK(result))
+ goto done;
+
+ opened_hnd = True;
+
+ /* Get printer info */
+
+ result = cli_spoolss_getprinterdata(cli, mem_ctx, 0, &needed,
+ &pol, valuename, &value);
+
+ if (W_ERROR_V(result) == ERRmoredata)
+ result = cli_spoolss_getprinterdata(
+ cli, mem_ctx, needed, NULL, &pol, valuename, &value);
+
+ if (!W_ERROR_IS_OK(result))
+ goto done;
+
+ /* Display printer data */
+
+ fstrcpy(value.valuename, valuename);
+ display_reg_value(value);
+
+
+ done:
+ if (opened_hnd)
+ cli_spoolss_close_printer(cli, mem_ctx, &pol);
+
+ return result;
+}
+
+/***********************************************************************
+ * Get printer data
+ */
+static WERROR cmd_spoolss_getprinterdataex(struct cli_state *cli,
+ TALLOC_CTX *mem_ctx,
+ int argc, const char **argv)
+{
+ POLICY_HND pol;
+ WERROR result;
+ BOOL opened_hnd = False;
+ fstring printername,
+ servername,
+ user;
+ uint32 needed;
+ const char *valuename, *keyname;
+ REGISTRY_VALUE value;
+
+ if (argc != 4) {
+ printf("Usage: %s <printername> <keyname> <valuename>\n",
+ argv[0]);
+ printf("<printername> of . queries print server\n");
+ return WERR_OK;
+ }
+ valuename = argv[3];
+ keyname = argv[2];
+
+ /* Open a printer handle */
+
+ slprintf(servername, sizeof(servername)-1, "\\\\%s", cli->desthost);
+ strupper_m(servername);
+ if (strncmp(argv[1], ".", sizeof(".")) == 0)
+ fstrcpy(printername, servername);
+ else
+ slprintf(printername, sizeof(printername)-1, "%s\\%s",
+ servername, argv[1]);
+ fstrcpy(user, cli->user_name);
+
+ /* get a printer handle */
+
+ result = cli_spoolss_open_printer_ex(cli, mem_ctx, printername,
+ "", MAXIMUM_ALLOWED_ACCESS,
+ servername, user, &pol);
+
+ if (!W_ERROR_IS_OK(result))
+ goto done;
+
+ opened_hnd = True;
+
+ /* Get printer info */
+
+ result = cli_spoolss_getprinterdataex(cli, mem_ctx, 0, &needed,
+ &pol, keyname, valuename,
+ &value);
+
+ if (W_ERROR_V(result) == ERRmoredata)
+ result = cli_spoolss_getprinterdataex(cli, mem_ctx, needed,
+ NULL, &pol, keyname,
+ valuename, &value);
+
+ if (!W_ERROR_IS_OK(result))
+ goto done;
+
+ /* Display printer data */
+
+ fstrcpy(value.valuename, valuename);
+ display_reg_value(value);
+
+
+ done:
+ if (opened_hnd)
+ cli_spoolss_close_printer(cli, mem_ctx, &pol);
+
+ return result;
+}
+
+/****************************************************************************
+printer info level 0 display function
+****************************************************************************/
+static void display_print_driver_1(DRIVER_INFO_1 *i1)
+{
+ fstring name;
+ if (i1 == NULL)
+ return;
+
+ rpcstr_pull(name, i1->name.buffer, sizeof(name), -1, STR_TERMINATE);
+
+ printf ("Printer Driver Info 1:\n");
+ printf ("\tDriver Name: [%s]\n\n", name);
+
+ return;
+}
+
+/****************************************************************************
+printer info level 1 display function
+****************************************************************************/
+static void display_print_driver_2(DRIVER_INFO_2 *i1)
+{
+ fstring name;
+ fstring architecture;
+ fstring driverpath;
+ fstring datafile;
+ fstring configfile;
+ if (i1 == NULL)
+ return;
+
+ rpcstr_pull(name, i1->name.buffer, sizeof(name), -1, STR_TERMINATE);
+ rpcstr_pull(architecture, i1->architecture.buffer, sizeof(architecture), -1, STR_TERMINATE);
+ rpcstr_pull(driverpath, i1->driverpath.buffer, sizeof(driverpath), -1, STR_TERMINATE);
+ rpcstr_pull(datafile, i1->datafile.buffer, sizeof(datafile), -1, STR_TERMINATE);
+ rpcstr_pull(configfile, i1->configfile.buffer, sizeof(configfile), -1, STR_TERMINATE);
+
+ printf ("Printer Driver Info 2:\n");
+ printf ("\tVersion: [%x]\n", i1->version);
+ printf ("\tDriver Name: [%s]\n", name);
+ printf ("\tArchitecture: [%s]\n", architecture);
+ printf ("\tDriver Path: [%s]\n", driverpath);
+ printf ("\tDatafile: [%s]\n", datafile);
+ printf ("\tConfigfile: [%s]\n\n", configfile);
+
+ return;
+}
+
+/****************************************************************************
+printer info level 2 display function
+****************************************************************************/
+static void display_print_driver_3(DRIVER_INFO_3 *i1)
+{
+ fstring name = "";
+ fstring architecture = "";
+ fstring driverpath = "";
+ fstring datafile = "";
+ fstring configfile = "";
+ fstring helpfile = "";
+ fstring dependentfiles = "";
+ fstring monitorname = "";
+ fstring defaultdatatype = "";
+
+ int length=0;
+ BOOL valid = True;
+
+ if (i1 == NULL)
+ return;
+
+ rpcstr_pull(name, i1->name.buffer, sizeof(name), -1, STR_TERMINATE);
+ rpcstr_pull(architecture, i1->architecture.buffer, sizeof(architecture), -1, STR_TERMINATE);
+ rpcstr_pull(driverpath, i1->driverpath.buffer, sizeof(driverpath), -1, STR_TERMINATE);
+ rpcstr_pull(datafile, i1->datafile.buffer, sizeof(datafile), -1, STR_TERMINATE);
+ rpcstr_pull(configfile, i1->configfile.buffer, sizeof(configfile), -1, STR_TERMINATE);
+ rpcstr_pull(helpfile, i1->helpfile.buffer, sizeof(helpfile), -1, STR_TERMINATE);
+ rpcstr_pull(monitorname, i1->monitorname.buffer, sizeof(monitorname), -1, STR_TERMINATE);
+ rpcstr_pull(defaultdatatype, i1->defaultdatatype.buffer, sizeof(defaultdatatype), -1, STR_TERMINATE);
+
+ printf ("Printer Driver Info 3:\n");
+ printf ("\tVersion: [%x]\n", i1->version);
+ printf ("\tDriver Name: [%s]\n",name);
+ printf ("\tArchitecture: [%s]\n", architecture);
+ printf ("\tDriver Path: [%s]\n", driverpath);
+ printf ("\tDatafile: [%s]\n", datafile);
+ printf ("\tConfigfile: [%s]\n", configfile);
+ printf ("\tHelpfile: [%s]\n\n", helpfile);
+
+ while (valid)
+ {
+ rpcstr_pull(dependentfiles, i1->dependentfiles+length, sizeof(dependentfiles), -1, STR_TERMINATE);
+
+ length+=strlen(dependentfiles)+1;
+
+ if (strlen(dependentfiles) > 0)
+ {
+ printf ("\tDependentfiles: [%s]\n", dependentfiles);
+ }
+ else
+ {
+ valid = False;
+ }
+ }
+
+ printf ("\n");
+
+ printf ("\tMonitorname: [%s]\n", monitorname);
+ printf ("\tDefaultdatatype: [%s]\n\n", defaultdatatype);
+
+ return;
+}
+
+/***********************************************************************
+ * Get printer information
+ */
+static WERROR cmd_spoolss_getdriver(struct cli_state *cli,
+ TALLOC_CTX *mem_ctx,
+ int argc, const char **argv)
+{
+ POLICY_HND pol;
+ WERROR werror;
+ uint32 info_level = 3;
+ BOOL opened_hnd = False;
+ PRINTER_DRIVER_CTR ctr;
+ fstring printername,
+ servername,
+ user;
+ uint32 i;
+ BOOL success = False;
+
+ if ((argc == 1) || (argc > 3))
+ {
+ printf("Usage: %s <printername> [level]\n", argv[0]);
+ return WERR_OK;
+ }
+
+ /* get the arguments need to open the printer handle */
+ slprintf(servername, sizeof(servername)-1, "\\\\%s", cli->desthost);
+ strupper_m(servername);
+ fstrcpy(user, cli->user_name);
+ fstrcpy(printername, argv[1]);
+ if (argc == 3)
+ info_level = atoi(argv[2]);
+
+ /* Open a printer handle */
+
+ werror = cli_spoolss_open_printer_ex(cli, mem_ctx, printername, "",
+ PRINTER_ACCESS_USE,
+ servername, user, &pol);
+
+ if (!W_ERROR_IS_OK(werror)) {
+ printf("Error opening printer handle for %s!\n", printername);
+ return werror;
+ }
+
+ opened_hnd = True;
+
+ /* loop through and print driver info level for each architecture */
+
+ for (i=0; archi_table[i].long_archi!=NULL; i++) {
+ uint32 needed;
+
+ werror = cli_spoolss_getprinterdriver(
+ cli, mem_ctx, 0, &needed, &pol, info_level,
+ archi_table[i].long_archi, archi_table[i].version,
+ &ctr);
+
+ if (W_ERROR_V(werror) == ERRinsufficientbuffer) {
+ werror = cli_spoolss_getprinterdriver(
+ cli, mem_ctx, needed, NULL, &pol, info_level,
+ archi_table[i].long_archi, archi_table[i].version,
+ &ctr);
+ }
+
+ if (!W_ERROR_IS_OK(werror))
+ continue;
+
+ /* need at least one success */
+
+ success = True;
+
+ printf ("\n[%s]\n", archi_table[i].long_archi);
+
+ switch (info_level) {
+ case 1:
+ display_print_driver_1 (ctr.info1);
+ break;
+ case 2:
+ display_print_driver_2 (ctr.info2);
+ break;
+ case 3:
+ display_print_driver_3 (ctr.info3);
+ break;
+ default:
+ printf("unknown info level %d\n", info_level);
+ break;
+ }
+ }
+
+ /* Cleanup */
+
+ if (opened_hnd)
+ cli_spoolss_close_printer (cli, mem_ctx, &pol);
+
+ if ( success )
+ werror = WERR_OK;
+
+ return werror;
+}
+
+/***********************************************************************
+ * Get printer information
+ */
+static WERROR cmd_spoolss_enum_drivers(struct cli_state *cli,
+ TALLOC_CTX *mem_ctx,
+ int argc, const char **argv)
+{
+ WERROR werror;
+ uint32 info_level = 1;
+ PRINTER_DRIVER_CTR ctr;
+ uint32 i, j,
+ returned;
+
+ if (argc > 2)
+ {
+ printf("Usage: enumdrivers [level]\n");
+ return WERR_OK;
+ }
+
+ if (argc == 2)
+ info_level = atoi(argv[1]);
+
+
+ /* loop through and print driver info level for each architecture */
+ for (i=0; archi_table[i].long_archi!=NULL; i++)
+ {
+ uint32 needed;
+
+ werror = cli_spoolss_enumprinterdrivers(
+ cli, mem_ctx, 0, &needed, info_level,
+ archi_table[i].long_archi, &returned, &ctr);
+
+ if (W_ERROR_V(werror) == ERRinsufficientbuffer)
+ werror = cli_spoolss_enumprinterdrivers(
+ cli, mem_ctx, needed, NULL, info_level,
+ archi_table[i].long_archi, &returned, &ctr);
+
+ if (returned == 0)
+ continue;
+
+ if (!W_ERROR_IS_OK(werror)) {
+ printf ("Error getting driver for environment [%s] - %d\n",
+ archi_table[i].long_archi, W_ERROR_V(werror));
+ continue;
+ }
+
+ printf ("\n[%s]\n", archi_table[i].long_archi);
+ switch (info_level)
+ {
+
+ case 1:
+ for (j=0; j < returned; j++) {
+ display_print_driver_1 (&(ctr.info1[j]));
+ }
+ break;
+ case 2:
+ for (j=0; j < returned; j++) {
+ display_print_driver_2 (&(ctr.info2[j]));
+ }
+ break;
+ case 3:
+ for (j=0; j < returned; j++) {
+ display_print_driver_3 (&(ctr.info3[j]));
+ }
+ break;
+ default:
+ printf("unknown info level %d\n", info_level);
+ break;
+ }
+ }
+
+ return werror;
+}
+
+/****************************************************************************
+printer info level 1 display function
+****************************************************************************/
+static void display_printdriverdir_1(DRIVER_DIRECTORY_1 *i1)
+{
+ fstring name;
+ if (i1 == NULL)
+ return;
+
+ rpcstr_pull(name, i1->name.buffer, sizeof(name), -1, STR_TERMINATE);
+
+ printf ("\tDirectory Name:[%s]\n", name);
+}
+
+/***********************************************************************
+ * Get printer driver directory information
+ */
+static WERROR cmd_spoolss_getdriverdir(struct cli_state *cli,
+ TALLOC_CTX *mem_ctx,
+ int argc, const char **argv)
+{
+ WERROR result;
+ fstring env;
+ DRIVER_DIRECTORY_CTR ctr;
+ uint32 needed;
+
+ if (argc > 2) {
+ printf("Usage: %s [environment]\n", argv[0]);
+ return WERR_OK;
+ }
+
+ /* Get the arguments need to open the printer handle */
+
+ if (argc == 2)
+ fstrcpy (env, argv[1]);
+ else
+ fstrcpy (env, "Windows NT x86");
+
+ /* Get the directory. Only use Info level 1 */
+
+ result = cli_spoolss_getprinterdriverdir(
+ cli, mem_ctx, 0, &needed, 1, env, &ctr);
+
+ if (W_ERROR_V(result) == ERRinsufficientbuffer)
+ result = cli_spoolss_getprinterdriverdir(
+ cli, mem_ctx, needed, NULL, 1, env, &ctr);
+
+ if (W_ERROR_IS_OK(result))
+ display_printdriverdir_1(ctr.info1);
+
+ return result;
+}
+
+/*******************************************************************************
+ set the version and environment fields of a DRIVER_INFO_3 struct
+ ******************************************************************************/
+void set_drv_info_3_env (DRIVER_INFO_3 *info, const char *arch)
+{
+
+ int i;
+
+ for (i=0; archi_table[i].long_archi != NULL; i++)
+ {
+ if (strcmp(arch, archi_table[i].short_archi) == 0)
+ {
+ info->version = archi_table[i].version;
+ init_unistr (&info->architecture, archi_table[i].long_archi);
+ break;
+ }
+ }
+
+ if (archi_table[i].long_archi == NULL)
+ {
+ DEBUG(0, ("set_drv_info_3_env: Unknown arch [%s]\n", arch));
+ }
+
+ return;
+}
+
+
+/**************************************************************************
+ wrapper for strtok to get the next parameter from a delimited list.
+ Needed to handle the empty parameter string denoted by "NULL"
+ *************************************************************************/
+static char* get_driver_3_param (const char* str, const char* delim, UNISTR* dest)
+{
+ char *ptr;
+
+ /* get the next token */
+ ptr = strtok(str, delim);
+
+ /* a string of 'NULL' is used to represent an empty
+ parameter because two consecutive delimiters
+ will not return an empty string. See man strtok(3)
+ for details */
+ if (ptr && (StrCaseCmp(ptr, "NULL") == 0))
+ ptr = NULL;
+
+ if (dest != NULL)
+ init_unistr(dest, ptr);
+
+ return ptr;
+}
+
+/********************************************************************************
+ fill in the members of a DRIVER_INFO_3 struct using a character
+ string in the form of
+ <Long Printer Name>:<Driver File Name>:<Data File Name>:\
+ <Config File Name>:<Help File Name>:<Language Monitor Name>:\
+ <Default Data Type>:<Comma Separated list of Files>
+ *******************************************************************************/
+static BOOL init_drv_info_3_members (
+ TALLOC_CTX *mem_ctx,
+ DRIVER_INFO_3 *info,
+ const char *args
+)
+{
+ char *str, *str2;
+ uint32 len, i;
+
+ /* fill in the UNISTR fields */
+ str = get_driver_3_param (args, ":", &info->name);
+ str = get_driver_3_param (NULL, ":", &info->driverpath);
+ str = get_driver_3_param (NULL, ":", &info->datafile);
+ str = get_driver_3_param (NULL, ":", &info->configfile);
+ str = get_driver_3_param (NULL, ":", &info->helpfile);
+ str = get_driver_3_param (NULL, ":", &info->monitorname);
+ str = get_driver_3_param (NULL, ":", &info->defaultdatatype);
+
+ /* <Comma Separated List of Dependent Files> */
+ str2 = get_driver_3_param (NULL, ":", NULL); /* save the beginning of the string */
+ str = str2;
+
+ /* begin to strip out each filename */
+ str = strtok(str, ",");
+ len = 0;
+ while (str != NULL)
+ {
+ /* keep a cumlative count of the str lengths */
+ len += strlen(str)+1;
+ str = strtok(NULL, ",");
+ }
+
+ /* allocate the space; add one extra slot for a terminating NULL.
+ Each filename is NULL terminated and the end contains a double
+ NULL */
+ if ((info->dependentfiles=(uint16*)talloc(mem_ctx, (len+1)*sizeof(uint16))) == NULL)
+ {
+ DEBUG(0,("init_drv_info_3_members: Unable to malloc memory for dependenfiles\n"));
+ return False;
+ }
+ for (i=0; i<len; i++)
+ {
+ SSVAL(&info->dependentfiles[i], 0, str2[i]);
+ }
+ info->dependentfiles[len] = '\0';
+
+ return True;
+}
+
+
+static WERROR cmd_spoolss_addprinterdriver(struct cli_state *cli,
+ TALLOC_CTX *mem_ctx,
+ int argc, const char **argv)
+{
+ WERROR result;
+ uint32 level = 3;
+ PRINTER_DRIVER_CTR ctr;
+ DRIVER_INFO_3 info3;
+ const char *arch;
+ fstring driver_name;
+
+ /* parse the command arguements */
+ if (argc != 3 && argc != 4)
+ {
+ printf ("Usage: %s <Environment> \\\n", argv[0]);
+ printf ("\t<Long Printer Name>:<Driver File Name>:<Data File Name>:\\\n");
+ printf ("\t<Config File Name>:<Help File Name>:<Language Monitor Name>:\\\n");
+ printf ("\t<Default Data Type>:<Comma Separated list of Files> \\\n");
+ printf ("\t[version]\n");
+
+ return WERR_OK;
+ }
+
+ /* Fill in the DRIVER_INFO_3 struct */
+ ZERO_STRUCT(info3);
+ if (!(arch = cmd_spoolss_get_short_archi(argv[1])))
+ {
+ printf ("Error Unknown architechture [%s]\n", argv[1]);
+ return WERR_INVALID_PARAM;
+ }
+ else
+ set_drv_info_3_env(&info3, arch);
+
+ if (!init_drv_info_3_members(mem_ctx, &info3, argv[2]))
+ {
+ printf ("Error Invalid parameter list - %s.\n", argv[2]);
+ return WERR_INVALID_PARAM;
+ }
+
+ /* if printer driver version specified, override the default version
+ * used by the architecture. This allows installation of Windows
+ * 2000 (version 3) printer drivers. */
+ if (argc == 4)
+ {
+ info3.version = atoi(argv[3]);
+ }
+
+
+ ctr.info3 = &info3;
+ result = cli_spoolss_addprinterdriver (cli, mem_ctx, level, &ctr);
+
+ if (W_ERROR_IS_OK(result)) {
+ rpcstr_pull(driver_name, info3.name.buffer,
+ sizeof(driver_name), -1, STR_TERMINATE);
+ printf ("Printer Driver %s successfully installed.\n",
+ driver_name);
+ }
+
+ return result;
+}
+
+
+static WERROR cmd_spoolss_addprinterex(struct cli_state *cli,
+ TALLOC_CTX *mem_ctx,
+ int argc, const char **argv)
+{
+ WERROR result;
+ uint32 level = 2;
+ PRINTER_INFO_CTR ctr;
+ PRINTER_INFO_2 info2;
+ fstring servername;
+
+ /* parse the command arguements */
+ if (argc != 5)
+ {
+ printf ("Usage: %s <name> <shared name> <driver> <port>\n", argv[0]);
+ return WERR_OK;
+ }
+
+ slprintf(servername, sizeof(servername)-1, "\\\\%s", cli->desthost);
+ strupper_m(servername);
+
+ /* Fill in the DRIVER_INFO_2 struct */
+ ZERO_STRUCT(info2);
+#if 0 /* JERRY */
+ init_unistr( &info2.servername, servername);
+#endif
+ init_unistr( &info2.printername, argv[1]);
+ init_unistr( &info2.sharename, argv[2]);
+ init_unistr( &info2.drivername, argv[3]);
+ init_unistr( &info2.portname, argv[4]);
+ init_unistr( &info2.comment, "Created by rpcclient");
+ init_unistr( &info2.printprocessor, "winprint");
+ init_unistr( &info2.datatype, "RAW");
+ info2.devmode = NULL;
+ info2.secdesc = NULL;
+ info2.attributes = PRINTER_ATTRIBUTE_SHARED;
+ info2.priority = 0;
+ info2.defaultpriority = 0;
+ info2.starttime = 0;
+ info2.untiltime = 0;
+
+ /* These three fields must not be used by AddPrinter()
+ as defined in the MS Platform SDK documentation..
+ --jerry
+ info2.status = 0;
+ info2.cjobs = 0;
+ info2.averageppm = 0;
+ */
+
+ ctr.printers_2 = &info2;
+ result = cli_spoolss_addprinterex (cli, mem_ctx, level, &ctr);
+
+ if (W_ERROR_IS_OK(result))
+ printf ("Printer %s successfully installed.\n", argv[1]);
+
+ return result;
+}
+
+static WERROR cmd_spoolss_setdriver(struct cli_state *cli,
+ TALLOC_CTX *mem_ctx,
+ int argc, const char **argv)
+{
+ POLICY_HND pol;
+ WERROR result;
+ uint32 level = 2;
+ BOOL opened_hnd = False;
+ PRINTER_INFO_CTR ctr;
+ PRINTER_INFO_2 info2;
+ fstring servername,
+ printername,
+ user;
+ uint32 needed;
+
+ /* parse the command arguements */
+ if (argc != 3)
+ {
+ printf ("Usage: %s <printer> <driver>\n", argv[0]);
+ return WERR_OK;
+ }
+
+ slprintf(servername, sizeof(servername)-1, "\\\\%s", cli->desthost);
+ strupper_m(servername);
+ slprintf(printername, sizeof(printername)-1, "%s\\%s", servername, argv[1]);
+ fstrcpy(user, cli->user_name);
+
+ /* Get a printer handle */
+
+ result = cli_spoolss_open_printer_ex(cli, mem_ctx, printername, "",
+ PRINTER_ALL_ACCESS,
+ servername, user, &pol);
+
+ if (!W_ERROR_IS_OK(result))
+ goto done;
+
+ opened_hnd = True;
+
+ /* Get printer info */
+
+ ZERO_STRUCT (info2);
+ ctr.printers_2 = &info2;
+
+ result = cli_spoolss_getprinter(cli, mem_ctx, 0, &needed,
+ &pol, level, &ctr);
+
+ if (W_ERROR_V(result) == ERRinsufficientbuffer)
+ result = cli_spoolss_getprinter(
+ cli, mem_ctx, needed, NULL, &pol, level, &ctr);
+
+ if (!W_ERROR_IS_OK(result)) {
+ printf ("Unable to retrieve printer information!\n");
+ goto done;
+ }
+
+ /* Set the printer driver */
+
+ init_unistr(&ctr.printers_2->drivername, argv[2]);
+
+ result = cli_spoolss_setprinter(cli, mem_ctx, &pol, level, &ctr, 0);
+
+ if (!W_ERROR_IS_OK(result)) {
+ printf("SetPrinter call failed!\n");
+ goto done;;
+ }
+
+ printf("Succesfully set %s to driver %s.\n", argv[1], argv[2]);
+
+done:
+ /* Cleanup */
+
+ if (opened_hnd)
+ cli_spoolss_close_printer(cli, mem_ctx, &pol);
+
+ return result;
+}
+
+
+static WERROR cmd_spoolss_deletedriver(struct cli_state *cli,
+ TALLOC_CTX *mem_ctx,
+ int argc, const char **argv)
+{
+ WERROR result;
+ fstring servername;
+ int i;
+
+ /* parse the command arguements */
+ if (argc != 2)
+ {
+ printf ("Usage: %s <driver>\n", argv[0]);
+ return WERR_OK;
+ }
+
+ slprintf(servername, sizeof(servername)-1, "\\\\%s", cli->desthost);
+ strupper_m(servername);
+
+ /* delete the driver for all architectures */
+ for (i=0; archi_table[i].long_archi; i++)
+ {
+ /* make the call to remove the driver */
+ result = cli_spoolss_deleteprinterdriver(
+ cli, mem_ctx, archi_table[i].long_archi, argv[1]);
+
+ if ( !W_ERROR_IS_OK(result) ) {
+ if ( !W_ERROR_EQUAL(result, WERR_UNKNOWN_PRINTER_DRIVER) ) {
+ printf ("Failed to remove driver %s for arch [%s] - error 0x%x!\n",
+ argv[1], archi_table[i].long_archi,
+ W_ERROR_V(result));
+ }
+ }
+ else
+ {
+ printf ("Driver %s removed for arch [%s].\n", argv[1],
+ archi_table[i].long_archi);
+ }
+ }
+
+ return result;
+}
+
+static WERROR cmd_spoolss_getprintprocdir(struct cli_state *cli,
+ TALLOC_CTX *mem_ctx,
+ int argc, const char **argv)
+{
+ WERROR result;
+ char *servername = NULL, *environment = NULL;
+ fstring procdir;
+ uint32 needed;
+
+ /* parse the command arguements */
+ if (argc > 2) {
+ printf ("Usage: %s [environment]\n", argv[0]);
+ return WERR_OK;
+ }
+
+ if (asprintf(&servername, "\\\\%s", cli->desthost) < 0)
+ return WERR_NOMEM;
+ strupper_m(servername);
+
+ if (asprintf(&environment, "%s", (argc == 2) ? argv[1] :
+ PRINTER_DRIVER_ARCHITECTURE) < 0) {
+ SAFE_FREE(servername);
+ return WERR_NOMEM;
+ }
+
+ result = cli_spoolss_getprintprocessordirectory(
+ cli, mem_ctx, 0, &needed, servername, environment, procdir);
+
+ if (W_ERROR_V(result) == ERRinsufficientbuffer)
+ result = cli_spoolss_getprintprocessordirectory(
+ cli, mem_ctx, needed, NULL, servername, environment,
+ procdir);
+
+ if (W_ERROR_IS_OK(result))
+ printf("%s\n", procdir);
+
+ SAFE_FREE(servername);
+ SAFE_FREE(environment);
+
+ return result;
+}
+
+/* Add a form */
+
+static WERROR cmd_spoolss_addform(struct cli_state *cli, TALLOC_CTX *mem_ctx,
+ int argc, const char **argv)
+{
+ POLICY_HND handle;
+ WERROR werror;
+ char *servername = NULL, *printername = NULL;
+ FORM form;
+ BOOL got_handle = False;
+
+ /* Parse the command arguements */
+
+ if (argc != 3) {
+ printf ("Usage: %s <printer> <formname>\n", argv[0]);
+ return WERR_OK;
+ }
+
+ /* Get a printer handle */
+
+ asprintf(&servername, "\\\\%s", cli->desthost);
+ strupper_m(servername);
+ asprintf(&printername, "%s\\%s", servername, argv[1]);
+
+ werror = cli_spoolss_open_printer_ex(cli, mem_ctx, printername, "",
+ MAXIMUM_ALLOWED_ACCESS,
+ servername, cli->user_name, &handle);
+
+ if (!W_ERROR_IS_OK(werror))
+ goto done;
+
+ got_handle = True;
+
+ /* Dummy up some values for the form data */
+
+ form.flags = FORM_USER;
+ form.size_x = form.size_y = 100;
+ form.left = 0;
+ form.top = 10;
+ form.right = 20;
+ form.bottom = 30;
+
+ init_unistr2(&form.name, argv[2], UNI_STR_TERMINATE);
+
+ /* Add the form */
+
+
+ werror = cli_spoolss_addform(cli, mem_ctx, &handle, 1, &form);
+
+ done:
+ if (got_handle)
+ cli_spoolss_close_printer(cli, mem_ctx, &handle);
+
+ SAFE_FREE(servername);
+ SAFE_FREE(printername);
+
+ return werror;
+}
+
+/* Set a form */
+
+static WERROR cmd_spoolss_setform(struct cli_state *cli, TALLOC_CTX *mem_ctx,
+ int argc, const char **argv)
+{
+ POLICY_HND handle;
+ WERROR werror;
+ char *servername = NULL, *printername = NULL;
+ FORM form;
+ BOOL got_handle = False;
+
+ /* Parse the command arguements */
+
+ if (argc != 3) {
+ printf ("Usage: %s <printer> <formname>\n", argv[0]);
+ return WERR_OK;
+ }
+
+ /* Get a printer handle */
+
+ asprintf(&servername, "\\\\%s", cli->desthost);
+ strupper_m(servername);
+ asprintf(&printername, "%s\\%s", servername, argv[1]);
+
+ werror = cli_spoolss_open_printer_ex(
+ cli, mem_ctx, printername, "", MAXIMUM_ALLOWED_ACCESS,
+ servername, cli->user_name, &handle);
+
+ if (!W_ERROR_IS_OK(werror))
+ goto done;
+
+ got_handle = True;
+
+ /* Dummy up some values for the form data */
+
+ form.flags = FORM_PRINTER;
+ form.size_x = form.size_y = 100;
+ form.left = 0;
+ form.top = 1000;
+ form.right = 2000;
+ form.bottom = 3000;
+
+ init_unistr2(&form.name, argv[2], UNI_STR_TERMINATE);
+
+ /* Set the form */
+
+ werror = cli_spoolss_setform(cli, mem_ctx, &handle, 1, argv[2], &form);
+
+ done:
+ if (got_handle)
+ cli_spoolss_close_printer(cli, mem_ctx, &handle);
+
+ SAFE_FREE(servername);
+ SAFE_FREE(printername);
+
+ return werror;
+}
+
+/* Get a form */
+
+static WERROR cmd_spoolss_getform(struct cli_state *cli, TALLOC_CTX *mem_ctx,
+ int argc, const char **argv)
+{
+ POLICY_HND handle;
+ WERROR werror;
+ char *servername = NULL, *printername = NULL;
+ FORM_1 form;
+ BOOL got_handle = False;
+ uint32 needed;
+
+ /* Parse the command arguements */
+
+ if (argc != 3) {
+ printf ("Usage: %s <printer> <formname>\n", argv[0]);
+ return WERR_OK;
+ }
+
+ /* Get a printer handle */
+
+ asprintf(&servername, "\\\\%s", cli->desthost);
+ strupper_m(servername);
+ asprintf(&printername, "%s\\%s", servername, argv[1]);
+
+ werror = cli_spoolss_open_printer_ex(
+ cli, mem_ctx, printername, "", MAXIMUM_ALLOWED_ACCESS,
+ servername, cli->user_name, &handle);
+
+ if (!W_ERROR_IS_OK(werror))
+ goto done;
+
+ got_handle = True;
+
+ /* Set the form */
+
+ werror = cli_spoolss_getform(cli, mem_ctx, 0, &needed,
+ &handle, argv[2], 1, &form);
+
+ if (W_ERROR_V(werror) == ERRinsufficientbuffer)
+ werror = cli_spoolss_getform(cli, mem_ctx, needed, NULL,
+ &handle, argv[2], 1, &form);
+
+ if (!W_ERROR_IS_OK(werror))
+ goto done;
+
+ printf("width: %d\n", form.width);
+ printf("length: %d\n", form.length);
+ printf("left: %d\n", form.left);
+ printf("top: %d\n", form.top);
+ printf("right: %d\n", form.right);
+ printf("bottom: %d\n", form.bottom);
+
+ done:
+ if (got_handle)
+ cli_spoolss_close_printer(cli, mem_ctx, &handle);
+
+ SAFE_FREE(servername);
+ SAFE_FREE(printername);
+
+ return werror;
+}
+
+/* Delete a form */
+
+static WERROR cmd_spoolss_deleteform(struct cli_state *cli,
+ TALLOC_CTX *mem_ctx, int argc,
+ const char **argv)
+{
+ POLICY_HND handle;
+ WERROR werror;
+ char *servername = NULL, *printername = NULL;
+ BOOL got_handle = False;
+
+ /* Parse the command arguements */
+
+ if (argc != 3) {
+ printf ("Usage: %s <printer> <formname>\n", argv[0]);
+ return WERR_OK;
+ }
+
+ /* Get a printer handle */
+
+ asprintf(&servername, "\\\\%s", cli->desthost);
+ strupper_m(servername);
+ asprintf(&printername, "%s\\%s", servername, argv[1]);
+
+ werror = cli_spoolss_open_printer_ex(
+ cli, mem_ctx, printername, "", MAXIMUM_ALLOWED_ACCESS,
+ servername, cli->user_name, &handle);
+
+ if (!W_ERROR_IS_OK(werror))
+ goto done;
+
+ got_handle = True;
+
+ /* Delete the form */
+
+ werror = cli_spoolss_deleteform(cli, mem_ctx, &handle, argv[2]);
+
+ done:
+ if (got_handle)
+ cli_spoolss_close_printer(cli, mem_ctx, &handle);
+
+ SAFE_FREE(servername);
+ SAFE_FREE(printername);
+
+ return werror;
+}
+
+/* Enumerate forms */
+
+static WERROR cmd_spoolss_enum_forms(struct cli_state *cli,
+ TALLOC_CTX *mem_ctx, int argc,
+ const char **argv)
+{
+ POLICY_HND handle;
+ WERROR werror;
+ char *servername = NULL, *printername = NULL;
+ BOOL got_handle = False;
+ uint32 needed, num_forms, level = 1, i;
+ FORM_1 *forms;
+
+ /* Parse the command arguements */
+
+ if (argc != 2) {
+ printf ("Usage: %s <printer>\n", argv[0]);
+ return WERR_OK;
+ }
+
+ /* Get a printer handle */
+
+ asprintf(&servername, "\\\\%s", cli->desthost);
+ strupper_m(servername);
+ asprintf(&printername, "%s\\%s", servername, argv[1]);
+
+ werror = cli_spoolss_open_printer_ex(
+ cli, mem_ctx, printername, "", MAXIMUM_ALLOWED_ACCESS,
+ servername, cli->user_name, &handle);
+
+ if (!W_ERROR_IS_OK(werror))
+ goto done;
+
+ got_handle = True;
+
+ /* Enumerate forms */
+
+ werror = cli_spoolss_enumforms(
+ cli, mem_ctx, 0, &needed, &handle, level, &num_forms, &forms);
+
+ if (W_ERROR_V(werror) == ERRinsufficientbuffer)
+ werror = cli_spoolss_enumforms(
+ cli, mem_ctx, needed, NULL, &handle, level,
+ &num_forms, &forms);
+
+ if (!W_ERROR_IS_OK(werror))
+ goto done;
+
+ /* Display output */
+
+ for (i = 0; i < num_forms; i++) {
+ fstring form_name;
+
+ if (forms[i].name.buffer)
+ rpcstr_pull(form_name, forms[i].name.buffer,
+ sizeof(form_name), -1, STR_TERMINATE);
+
+ printf("%s\n", form_name);
+ }
+
+ done:
+ if (got_handle)
+ cli_spoolss_close_printer(cli, mem_ctx, &handle);
+
+ SAFE_FREE(servername);
+ SAFE_FREE(printername);
+
+ return werror;
+}
+
+static WERROR cmd_spoolss_setprinterdata(struct cli_state *cli,
+ TALLOC_CTX *mem_ctx,
+ int argc, const char **argv)
+{
+ WERROR result;
+ uint32 needed;
+ fstring servername, printername, user;
+ POLICY_HND pol;
+ BOOL opened_hnd = False;
+ PRINTER_INFO_CTR ctr;
+ PRINTER_INFO_0 info;
+ REGISTRY_VALUE value;
+
+ /* parse the command arguements */
+ if (argc != 4) {
+ printf ("Usage: %s <printer> <value> <data>\n", argv[0]);
+ return WERR_OK;
+ }
+
+ slprintf(servername, sizeof(servername)-1, "\\\\%s", cli->desthost);
+ strupper_m(servername);
+ slprintf(printername, sizeof(servername)-1, "%s\\%s", servername, argv[1]);
+ fstrcpy(user, cli->user_name);
+
+ /* get a printer handle */
+ result = cli_spoolss_open_printer_ex(cli, mem_ctx, printername, "",
+ MAXIMUM_ALLOWED_ACCESS, servername,
+ user, &pol);
+ if (!W_ERROR_IS_OK(result))
+ goto done;
+
+ opened_hnd = True;
+
+ ctr.printers_0 = &info;
+
+ result = cli_spoolss_getprinter(cli, mem_ctx, 0, &needed,
+ &pol, 0, &ctr);
+
+ if (W_ERROR_V(result) == ERRinsufficientbuffer)
+ result = cli_spoolss_getprinter(cli, mem_ctx, needed, NULL, &pol, 0, &ctr);
+
+ if (!W_ERROR_IS_OK(result))
+ goto done;
+
+ printf("%s\n", timestring(True));
+ printf("\tchange_id (before set)\t:[0x%x]\n", info.change_id);
+
+ /* Set the printer data */
+
+ fstrcpy(value.valuename, argv[2]);
+ value.type = REG_SZ;
+ value.size = strlen(argv[3]) + 1;
+ value.data_p = talloc_memdup(mem_ctx, argv[3], value.size);
+
+ result = cli_spoolss_setprinterdata(cli, mem_ctx, &pol, &value);
+
+ if (!W_ERROR_IS_OK(result)) {
+ printf ("Unable to set [%s=%s]!\n", argv[2], argv[3]);
+ goto done;
+ }
+ printf("\tSetPrinterData succeeded [%s: %s]\n", argv[2], argv[3]);
+
+ result = cli_spoolss_getprinter(cli, mem_ctx, 0, &needed, &pol, 0, &ctr);
+
+ if (W_ERROR_V(result) == ERRinsufficientbuffer)
+ result = cli_spoolss_getprinter(cli, mem_ctx, needed, NULL, &pol, 0, &ctr);
+
+ if (!W_ERROR_IS_OK(result))
+ goto done;
+
+ printf("%s\n", timestring(True));
+ printf("\tchange_id (after set)\t:[0x%x]\n", info.change_id);
+
+done:
+ /* cleanup */
+ if (opened_hnd)
+ cli_spoolss_close_printer(cli, mem_ctx, &pol);
+
+ return result;
+}
+
+static void display_job_info_1(JOB_INFO_1 *job)
+{
+ fstring username = "", document = "", text_status = "";
+
+ rpcstr_pull(username, job->username.buffer,
+ sizeof(username), -1, STR_TERMINATE);
+
+ rpcstr_pull(document, job->document.buffer,
+ sizeof(document), -1, STR_TERMINATE);
+
+ rpcstr_pull(text_status, job->text_status.buffer,
+ sizeof(text_status), -1, STR_TERMINATE);
+
+ printf("%d: jobid[%d]: %s %s %s %d/%d pages\n", job->position, job->jobid,
+ username, document, text_status, job->pagesprinted,
+ job->totalpages);
+}
+
+static void display_job_info_2(JOB_INFO_2 *job)
+{
+ fstring username = "", document = "", text_status = "";
+
+ rpcstr_pull(username, job->username.buffer,
+ sizeof(username), -1, STR_TERMINATE);
+
+ rpcstr_pull(document, job->document.buffer,
+ sizeof(document), -1, STR_TERMINATE);
+
+ rpcstr_pull(text_status, job->text_status.buffer,
+ sizeof(text_status), -1, STR_TERMINATE);
+
+ printf("%d: jobid[%d]: %s %s %s %d/%d pages, %d bytes\n", job->position, job->jobid,
+ username, document, text_status, job->pagesprinted,
+ job->totalpages, job->size);
+}
+
+/* Enumerate jobs */
+
+static WERROR cmd_spoolss_enum_jobs(struct cli_state *cli,
+ TALLOC_CTX *mem_ctx, int argc,
+ const char **argv)
+{
+ WERROR result;
+ uint32 needed, level = 1, num_jobs, i;
+ BOOL got_hnd = False;
+ pstring printername;
+ fstring servername, user;
+ POLICY_HND hnd;
+ JOB_INFO_CTR ctr;
+
+ if (argc < 2 || argc > 3) {
+ printf("Usage: %s printername [level]\n", argv[0]);
+ return WERR_OK;
+ }
+
+ if (argc == 3)
+ level = atoi(argv[2]);
+
+ /* Open printer handle */
+
+ slprintf(servername, sizeof(servername)-1, "\\\\%s", cli->desthost);
+ strupper_m(servername);
+ fstrcpy(user, cli->user_name);
+ slprintf(printername, sizeof(servername)-1, "\\\\%s\\", cli->desthost);
+ strupper_m(printername);
+ pstrcat(printername, argv[1]);
+
+ result = cli_spoolss_open_printer_ex(cli, mem_ctx, printername,
+ "", MAXIMUM_ALLOWED_ACCESS,
+ servername, user, &hnd);
+
+ if (!W_ERROR_IS_OK(result))
+ goto done;
+
+ got_hnd = True;
+
+ /* Enumerate ports */
+
+ result = cli_spoolss_enumjobs(
+ cli, mem_ctx, 0, &needed, &hnd, level, 0, 1000,
+ &num_jobs, &ctr);
+
+ if (W_ERROR_V(result) == ERRinsufficientbuffer)
+ result = cli_spoolss_enumjobs(
+ cli, mem_ctx, needed, NULL, &hnd, level, 0,
+ 1000, &num_jobs, &ctr);
+
+ if (!W_ERROR_IS_OK(result))
+ goto done;
+
+ for (i = 0; i < num_jobs; i++) {
+ switch(level) {
+ case 1:
+ display_job_info_1(&ctr.job.job_info_1[i]);
+ break;
+ case 2:
+ display_job_info_2(&ctr.job.job_info_2[i]);
+ break;
+ default:
+ d_printf("unknown info level %d\n", level);
+ break;
+ }
+ }
+
+done:
+ if (got_hnd)
+ cli_spoolss_close_printer(cli, mem_ctx, &hnd);
+
+ return result;
+}
+
+/* enumerate data */
+
+static WERROR cmd_spoolss_enum_data( struct cli_state *cli,
+ TALLOC_CTX *mem_ctx, int argc,
+ const char **argv)
+{
+ WERROR result;
+ uint32 i=0, val_needed, data_needed;
+ BOOL got_hnd = False;
+ pstring printername;
+ fstring servername, user;
+ POLICY_HND hnd;
+
+ if (argc != 2) {
+ printf("Usage: %s printername\n", argv[0]);
+ return WERR_OK;
+ }
+
+ /* Open printer handle */
+
+ slprintf(servername, sizeof(servername)-1, "\\\\%s", cli->desthost);
+ strupper_m(servername);
+ fstrcpy(user, cli->user_name);
+ slprintf(printername, sizeof(printername)-1, "\\\\%s\\", cli->desthost);
+ strupper_m(printername);
+ pstrcat(printername, argv[1]);
+
+ result = cli_spoolss_open_printer_ex(cli, mem_ctx, printername,
+ "", MAXIMUM_ALLOWED_ACCESS,
+ servername, user, &hnd);
+
+ if (!W_ERROR_IS_OK(result))
+ goto done;
+
+ got_hnd = True;
+
+ /* Enumerate data */
+
+ result = cli_spoolss_enumprinterdata(cli, mem_ctx, &hnd, i, 0, 0,
+ &val_needed, &data_needed,
+ NULL);
+ while (W_ERROR_IS_OK(result)) {
+ REGISTRY_VALUE value;
+ result = cli_spoolss_enumprinterdata(
+ cli, mem_ctx, &hnd, i++, val_needed,
+ data_needed, 0, 0, &value);
+ if (W_ERROR_IS_OK(result))
+ display_reg_value(value);
+ }
+ if (W_ERROR_V(result) == ERRnomoreitems)
+ result = W_ERROR(ERRsuccess);
+
+done:
+ if (got_hnd)
+ cli_spoolss_close_printer(cli, mem_ctx, &hnd);
+
+ return result;
+}
+
+/* enumerate data for a given key */
+
+static WERROR cmd_spoolss_enum_data_ex( struct cli_state *cli,
+ TALLOC_CTX *mem_ctx, int argc,
+ const char **argv)
+{
+ WERROR result;
+ uint32 needed, i;
+ BOOL got_hnd = False;
+ pstring printername;
+ fstring servername, user;
+ const char *keyname = NULL;
+ POLICY_HND hnd;
+ REGVAL_CTR ctr;
+
+ if (argc != 3) {
+ printf("Usage: %s printername <keyname>\n", argv[0]);
+ return WERR_OK;
+ }
+
+ keyname = argv[2];
+
+ /* Open printer handle */
+
+ slprintf(servername, sizeof(servername)-1, "\\\\%s", cli->desthost);
+ strupper_m(servername);
+ fstrcpy(user, cli->user_name);
+ slprintf(printername, sizeof(printername)-1, "\\\\%s\\", cli->desthost);
+ strupper_m(printername);
+ pstrcat(printername, argv[1]);
+
+ result = cli_spoolss_open_printer_ex(cli, mem_ctx, printername,
+ "", MAXIMUM_ALLOWED_ACCESS,
+ servername, user, &hnd);
+
+ if (!W_ERROR_IS_OK(result))
+ goto done;
+
+ got_hnd = True;
+
+ /* Enumerate subkeys */
+
+ result = cli_spoolss_enumprinterdataex(
+ cli, mem_ctx, 0, &needed, &hnd, keyname, NULL);
+
+ if (W_ERROR_V(result) == ERRmoredata)
+ result = cli_spoolss_enumprinterdataex(
+ cli, mem_ctx, needed, NULL, &hnd, keyname, &ctr);
+
+ if (!W_ERROR_IS_OK(result))
+ goto done;
+
+ for (i=0; i < ctr.num_values; i++) {
+ display_reg_value(*(ctr.values[i]));
+ }
+
+ regval_ctr_destroy(&ctr);
+
+done:
+ if (got_hnd)
+ cli_spoolss_close_printer(cli, mem_ctx, &hnd);
+
+ return result;
+}
+
+/* enumerate subkeys */
+
+static WERROR cmd_spoolss_enum_printerkey( struct cli_state *cli,
+ TALLOC_CTX *mem_ctx, int argc,
+ const char **argv)
+{
+ WERROR result;
+ uint32 needed, returned;
+ BOOL got_hnd = False;
+ pstring printername;
+ fstring servername, user;
+ const char *keyname = NULL;
+ POLICY_HND hnd;
+ uint16 *keylist = NULL, *curkey;
+
+ if (argc < 2 || argc > 3) {
+ printf("Usage: %s printername [keyname]\n", argv[0]);
+ return WERR_OK;
+ }
+
+ if (argc == 3)
+ keyname = argv[2];
+ else
+ keyname = "";
+
+ /* Open printer handle */
+
+ slprintf(servername, sizeof(servername)-1, "\\\\%s", cli->desthost);
+ strupper_m(servername);
+ fstrcpy(user, cli->user_name);
+ slprintf(printername, sizeof(printername)-1, "\\\\%s\\", cli->desthost);
+ strupper_m(printername);
+ pstrcat(printername, argv[1]);
+
+ result = cli_spoolss_open_printer_ex(cli, mem_ctx, printername,
+ "", MAXIMUM_ALLOWED_ACCESS,
+ servername, user, &hnd);
+
+ if (!W_ERROR_IS_OK(result))
+ goto done;
+
+ got_hnd = True;
+
+ /* Enumerate subkeys */
+
+ result = cli_spoolss_enumprinterkey(
+ cli, mem_ctx, 0, &needed, &hnd, keyname, NULL, NULL);
+
+ if (W_ERROR_V(result) == ERRmoredata)
+ result = cli_spoolss_enumprinterkey(
+ cli, mem_ctx, needed, NULL, &hnd, keyname, &keylist,
+ &returned);
+
+ if (!W_ERROR_IS_OK(result))
+ goto done;
+
+ curkey = keylist;
+ while (*curkey != 0) {
+ pstring subkey;
+ rpcstr_pull(subkey, curkey, sizeof(subkey), -1,
+ STR_TERMINATE);
+ printf("%s\n", subkey);
+ curkey += strlen(subkey) + 1;
+ }
+
+ safe_free(keylist);
+
+done:
+ if (got_hnd)
+ cli_spoolss_close_printer(cli, mem_ctx, &hnd);
+
+ return result;
+}
+
+static WERROR cmd_spoolss_rffpcnex(struct cli_state *cli,
+ TALLOC_CTX *mem_ctx, int argc,
+ const char **argv)
+{
+ fstring servername, printername;
+ POLICY_HND hnd;
+ BOOL got_hnd = False;
+ WERROR result;
+ SPOOL_NOTIFY_OPTION option;
+
+ if (argc != 2) {
+ printf("Usage: %s printername\n", argv[0]);
+ result = WERR_OK;
+ goto done;
+ }
+
+ /* Open printer */
+
+ slprintf(servername, sizeof(servername) - 1, "\\\\%s", cli->desthost);
+ strupper_m(servername);
+
+ slprintf(printername, sizeof(printername) - 1, "\\\\%s\\%s", cli->desthost,
+ argv[1]);
+ strupper_m(printername);
+
+ result = cli_spoolss_open_printer_ex(
+ cli, mem_ctx, printername, "", MAXIMUM_ALLOWED_ACCESS,
+ servername, cli->user_name, &hnd);
+
+ if (!W_ERROR_IS_OK(result)) {
+ printf("Error opening %s\n", argv[1]);
+ goto done;
+ }
+
+ got_hnd = True;
+
+ /* Create spool options */
+
+ ZERO_STRUCT(option);
+
+ option.version = 2;
+ option.option_type_ptr = 1;
+ option.count = option.ctr.count = 2;
+
+ option.ctr.type = (SPOOL_NOTIFY_OPTION_TYPE *)talloc(
+ mem_ctx, sizeof(SPOOL_NOTIFY_OPTION_TYPE) * 2);
+
+ ZERO_STRUCT(option.ctr.type[0]);
+ option.ctr.type[0].type = PRINTER_NOTIFY_TYPE;
+ option.ctr.type[0].count = option.ctr.type[0].count2 = 1;
+ option.ctr.type[0].fields_ptr = 1;
+ option.ctr.type[0].fields[0] = PRINTER_NOTIFY_SERVER_NAME;
+
+ ZERO_STRUCT(option.ctr.type[1]);
+ option.ctr.type[1].type = JOB_NOTIFY_TYPE;
+ option.ctr.type[1].count = option.ctr.type[1].count2 = 1;
+ option.ctr.type[1].fields_ptr = 1;
+ option.ctr.type[1].fields[0] = JOB_NOTIFY_PRINTER_NAME;
+
+ /* Send rffpcnex */
+
+ slprintf(servername, sizeof(servername) - 1, "\\\\%s", myhostname());
+ strupper_m(servername);
+
+ result = cli_spoolss_rffpcnex(
+ cli, mem_ctx, &hnd, 0, 0, servername, 123, &option);
+
+ if (!W_ERROR_IS_OK(result)) {
+ printf("Error rffpcnex %s\n", argv[1]);
+ goto done;
+ }
+
+done:
+ if (got_hnd)
+ cli_spoolss_close_printer(cli, mem_ctx, &hnd);
+
+ return result;
+}
+
+/* List of commands exported by this module */
+struct cmd_set spoolss_commands[] = {
+
+ { "SPOOLSS" },
+
+ { "adddriver", RPC_RTYPE_WERROR, NULL, cmd_spoolss_addprinterdriver, PI_SPOOLSS, "Add a print driver", "" },
+ { "addprinter", RPC_RTYPE_WERROR, NULL, cmd_spoolss_addprinterex, PI_SPOOLSS, "Add a printer", "" },
+ { "deldriver", RPC_RTYPE_WERROR, NULL, cmd_spoolss_deletedriver, PI_SPOOLSS, "Delete a printer driver", "" },
+ { "enumdata", RPC_RTYPE_WERROR, NULL, cmd_spoolss_enum_data, PI_SPOOLSS, "Enumerate printer data", "" },
+ { "enumdataex", RPC_RTYPE_WERROR, NULL, cmd_spoolss_enum_data_ex, PI_SPOOLSS, "Enumerate printer data for a key", "" },
+ { "enumkey", RPC_RTYPE_WERROR, NULL, cmd_spoolss_enum_printerkey, PI_SPOOLSS, "Enumerate printer keys", "" },
+ { "enumjobs", RPC_RTYPE_WERROR, NULL, cmd_spoolss_enum_jobs, PI_SPOOLSS, "Enumerate print jobs", "" },
+ { "enumports", RPC_RTYPE_WERROR, NULL, cmd_spoolss_enum_ports, PI_SPOOLSS, "Enumerate printer ports", "" },
+ { "enumdrivers", RPC_RTYPE_WERROR, NULL, cmd_spoolss_enum_drivers, PI_SPOOLSS, "Enumerate installed printer drivers", "" },
+ { "enumprinters", RPC_RTYPE_WERROR, NULL, cmd_spoolss_enum_printers, PI_SPOOLSS, "Enumerate printers", "" },
+ { "getdata", RPC_RTYPE_WERROR, NULL, cmd_spoolss_getprinterdata, PI_SPOOLSS, "Get print driver data", "" },
+ { "getdataex", RPC_RTYPE_WERROR, NULL, cmd_spoolss_getprinterdataex, PI_SPOOLSS, "Get printer driver data with keyname", ""},
+ { "getdriver", RPC_RTYPE_WERROR, NULL, cmd_spoolss_getdriver, PI_SPOOLSS, "Get print driver information", "" },
+ { "getdriverdir", RPC_RTYPE_WERROR, NULL, cmd_spoolss_getdriverdir, PI_SPOOLSS, "Get print driver upload directory", "" },
+ { "getprinter", RPC_RTYPE_WERROR, NULL, cmd_spoolss_getprinter, PI_SPOOLSS, "Get printer info", "" },
+ { "getprintprocdir",RPC_RTYPE_WERROR, NULL, cmd_spoolss_getprintprocdir, PI_SPOOLSS, "Get print processor directory", "" },
+ { "openprinter", RPC_RTYPE_WERROR, NULL, cmd_spoolss_open_printer_ex, PI_SPOOLSS, "Open printer handle", "" },
+ { "setdriver", RPC_RTYPE_WERROR, NULL, cmd_spoolss_setdriver, PI_SPOOLSS, "Set printer driver", "" },
+ { "getprintprocdir", RPC_RTYPE_WERROR, NULL, cmd_spoolss_getprintprocdir, PI_SPOOLSS, "Get print processor directory", "" },
+ { "addform", RPC_RTYPE_WERROR, NULL, cmd_spoolss_addform, PI_SPOOLSS, "Add form", "" },
+ { "setform", RPC_RTYPE_WERROR, NULL, cmd_spoolss_setform, PI_SPOOLSS, "Set form", "" },
+ { "getform", RPC_RTYPE_WERROR, NULL, cmd_spoolss_getform, PI_SPOOLSS, "Get form", "" },
+ { "deleteform", RPC_RTYPE_WERROR, NULL, cmd_spoolss_deleteform, PI_SPOOLSS, "Delete form", "" },
+ { "enumforms", RPC_RTYPE_WERROR, NULL, cmd_spoolss_enum_forms, PI_SPOOLSS, "Enumerate forms", "" },
+ { "setprinter", RPC_RTYPE_WERROR, NULL, cmd_spoolss_setprinter, PI_SPOOLSS, "Set printer comment", "" },
+ { "setprinterdata", RPC_RTYPE_WERROR, NULL, cmd_spoolss_setprinterdata, PI_SPOOLSS, "Set REG_SZ printer data", "" },
+ { "rffpcnex", RPC_RTYPE_WERROR, NULL, cmd_spoolss_rffpcnex, PI_SPOOLSS, "Rffpcnex test", "" },
+
+ { NULL }
+};
diff --git a/source/rpcclient/cmd_srvsvc.c b/source/rpcclient/cmd_srvsvc.c
new file mode 100644
index 00000000000..3e569f51cea
--- /dev/null
+++ b/source/rpcclient/cmd_srvsvc.c
@@ -0,0 +1,361 @@
+/*
+ Unix SMB/CIFS implementation.
+ RPC pipe client
+
+ Copyright (C) Andrew Tridgell 1992-1999
+ Copyright (C) Luke Kenneth Casson Leighton 1996 - 1999
+ Copyright (C) Tim Potter 2000,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 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 "rpcclient.h"
+
+/* Display server query info */
+
+static char *get_server_type_str(uint32 type)
+{
+ static fstring typestr;
+ int i;
+
+ if (type == SV_TYPE_ALL) {
+ fstrcpy(typestr, "All");
+ return typestr;
+ }
+
+ typestr[0] = 0;
+
+ for (i = 0; i < 32; i++) {
+ if (type & (1 << i)) {
+ switch (1 << i) {
+ case SV_TYPE_WORKSTATION:
+ fstrcat(typestr, "Wk ");
+ break;
+ case SV_TYPE_SERVER:
+ fstrcat(typestr, "Sv ");
+ break;
+ case SV_TYPE_SQLSERVER:
+ fstrcat(typestr, "Sql ");
+ break;
+ case SV_TYPE_DOMAIN_CTRL:
+ fstrcat(typestr, "PDC ");
+ break;
+ case SV_TYPE_DOMAIN_BAKCTRL:
+ fstrcat(typestr, "BDC ");
+ break;
+ case SV_TYPE_TIME_SOURCE:
+ fstrcat(typestr, "Tim ");
+ break;
+ case SV_TYPE_AFP:
+ fstrcat(typestr, "AFP ");
+ break;
+ case SV_TYPE_NOVELL:
+ fstrcat(typestr, "Nov ");
+ break;
+ case SV_TYPE_DOMAIN_MEMBER:
+ fstrcat(typestr, "Dom ");
+ break;
+ case SV_TYPE_PRINTQ_SERVER:
+ fstrcat(typestr, "PrQ ");
+ break;
+ case SV_TYPE_DIALIN_SERVER:
+ fstrcat(typestr, "Din ");
+ break;
+ case SV_TYPE_SERVER_UNIX:
+ fstrcat(typestr, "Unx ");
+ break;
+ case SV_TYPE_NT:
+ fstrcat(typestr, "NT ");
+ break;
+ case SV_TYPE_WFW:
+ fstrcat(typestr, "Wfw ");
+ break;
+ case SV_TYPE_SERVER_MFPN:
+ fstrcat(typestr, "Mfp ");
+ break;
+ case SV_TYPE_SERVER_NT:
+ fstrcat(typestr, "SNT ");
+ break;
+ case SV_TYPE_POTENTIAL_BROWSER:
+ fstrcat(typestr, "PtB ");
+ break;
+ case SV_TYPE_BACKUP_BROWSER:
+ fstrcat(typestr, "BMB ");
+ break;
+ case SV_TYPE_MASTER_BROWSER:
+ fstrcat(typestr, "LMB ");
+ break;
+ case SV_TYPE_DOMAIN_MASTER:
+ fstrcat(typestr, "DMB ");
+ break;
+ case SV_TYPE_SERVER_OSF:
+ fstrcat(typestr, "OSF ");
+ break;
+ case SV_TYPE_SERVER_VMS:
+ fstrcat(typestr, "VMS ");
+ break;
+ case SV_TYPE_WIN95_PLUS:
+ fstrcat(typestr, "W95 ");
+ break;
+ case SV_TYPE_ALTERNATE_XPORT:
+ fstrcat(typestr, "Xpt ");
+ break;
+ case SV_TYPE_LOCAL_LIST_ONLY:
+ fstrcat(typestr, "Dom ");
+ break;
+ case SV_TYPE_DOMAIN_ENUM:
+ fstrcat(typestr, "Loc ");
+ break;
+ }
+ }
+ }
+
+ i = strlen(typestr) - 1;
+
+ if (typestr[i] == ' ')
+ typestr[i] = 0;
+
+ return typestr;
+}
+
+static void display_server(char *sname, uint32 type, const char *comment)
+{
+ printf("\t%-15.15s%-20s %s\n", sname, get_server_type_str(type),
+ comment);
+}
+
+static void display_srv_info_101(SRV_INFO_101 *sv101)
+{
+ fstring name;
+ fstring comment;
+
+ unistr2_to_ascii(name, &sv101->uni_name, sizeof(name) - 1);
+ unistr2_to_ascii(comment, &sv101->uni_comment, sizeof(comment) - 1);
+
+ display_server(name, sv101->srv_type, comment);
+
+ printf("\tplatform_id :\t%d\n", sv101->platform_id);
+ printf("\tos version :\t%d.%d\n", sv101->ver_major,
+ sv101->ver_minor);
+
+ printf("\tserver type :\t0x%x\n", sv101->srv_type);
+}
+
+static void display_srv_info_102(SRV_INFO_102 *sv102)
+{
+ fstring name;
+ fstring comment;
+ fstring usr_path;
+
+ unistr2_to_ascii(name, &sv102->uni_name, sizeof(name) - 1);
+ unistr2_to_ascii(comment, &sv102->uni_comment, sizeof(comment) - 1);
+ unistr2_to_ascii(usr_path, &sv102->uni_usr_path, sizeof(usr_path) - 1);
+
+ display_server(name, sv102->srv_type, comment);
+
+ printf("\tplatform_id :\t%d\n", sv102->platform_id);
+ printf("\tos version :\t%d.%d\n", sv102->ver_major,
+ sv102->ver_minor);
+
+ printf("\tusers :\t%x\n", sv102->users);
+ printf("\tdisc, hidden :\t%x, %x\n", sv102->disc, sv102->hidden);
+ printf("\tannounce, delta :\t%d, %d\n", sv102->announce,
+ sv102->ann_delta);
+ printf("\tlicenses :\t%d\n", sv102->licenses);
+ printf("\tuser path :\t%s\n", usr_path);
+}
+
+/* Server query info */
+static WERROR cmd_srvsvc_srv_query_info(struct cli_state *cli,
+ TALLOC_CTX *mem_ctx,
+ int argc, const char **argv)
+{
+ uint32 info_level = 101;
+ SRV_INFO_CTR ctr;
+ WERROR result;
+
+ if (argc > 2) {
+ printf("Usage: %s [infolevel]\n", argv[0]);
+ return WERR_OK;
+ }
+
+ if (argc == 2)
+ info_level = atoi(argv[1]);
+
+ result = cli_srvsvc_net_srv_get_info(cli, mem_ctx, info_level,
+ &ctr);
+
+ if (!W_ERROR_IS_OK(result)) {
+ goto done;
+ }
+
+ /* Display results */
+
+ switch (info_level) {
+ case 101:
+ display_srv_info_101(&ctr.srv.sv101);
+ break;
+ case 102:
+ display_srv_info_102(&ctr.srv.sv102);
+ break;
+ default:
+ printf("unsupported info level %d\n", info_level);
+ break;
+ }
+
+ done:
+ return result;
+}
+
+static void display_share_info_1(SRV_SHARE_INFO_1 *info1)
+{
+ fstring netname = "", remark = "";
+
+ rpcstr_pull_unistr2_fstring(netname, &info1->info_1_str.uni_netname);
+ rpcstr_pull_unistr2_fstring(remark, &info1->info_1_str.uni_remark);
+
+ printf("netname: %s\n", netname);
+ printf("\tremark:\t%s\n", remark);
+}
+
+static void display_share_info_2(SRV_SHARE_INFO_2 *info2)
+{
+ fstring netname = "", remark = "", path = "", passwd = "";
+
+ rpcstr_pull_unistr2_fstring(netname, &info2->info_2_str.uni_netname);
+ rpcstr_pull_unistr2_fstring(remark, &info2->info_2_str.uni_remark);
+ rpcstr_pull_unistr2_fstring(path, &info2->info_2_str.uni_path);
+ rpcstr_pull_unistr2_fstring(passwd, &info2->info_2_str.uni_passwd);
+
+ printf("netname: %s\n", netname);
+ printf("\tremark:\t%s\n", remark);
+ printf("\tpath:\t%s\n", path);
+ printf("\tpassword:\t%s\n", passwd);
+}
+
+static WERROR cmd_srvsvc_net_share_enum(struct cli_state *cli,
+ TALLOC_CTX *mem_ctx,
+ int argc, const char **argv)
+{
+ uint32 info_level = 2;
+ SRV_SHARE_INFO_CTR ctr;
+ WERROR result;
+ ENUM_HND hnd;
+ uint32 preferred_len = 0xffffffff, i;
+
+ if (argc > 2) {
+ printf("Usage: %s [infolevel]\n", argv[0]);
+ return WERR_OK;
+ }
+
+ if (argc == 2)
+ info_level = atoi(argv[1]);
+
+ init_enum_hnd(&hnd, 0);
+
+ result = cli_srvsvc_net_share_enum(
+ cli, mem_ctx, info_level, &ctr, preferred_len, &hnd);
+
+ if (!W_ERROR_IS_OK(result) || !ctr.num_entries)
+ goto done;
+
+ /* Display results */
+
+ switch (info_level) {
+ case 1:
+ for (i = 0; i < ctr.num_entries; i++)
+ display_share_info_1(&ctr.share.info1[i]);
+ break;
+ case 2:
+ for (i = 0; i < ctr.num_entries; i++)
+ display_share_info_2(&ctr.share.info2[i]);
+ break;
+ default:
+ printf("unsupported info level %d\n", info_level);
+ break;
+ }
+
+ done:
+ return result;
+}
+
+static WERROR cmd_srvsvc_net_remote_tod(struct cli_state *cli,
+ TALLOC_CTX *mem_ctx,
+ int argc, const char **argv)
+{
+ TIME_OF_DAY_INFO tod;
+ WERROR result;
+
+ if (argc > 1) {
+ printf("Usage: %s\n", argv[0]);
+ return WERR_OK;
+ }
+
+ result = cli_srvsvc_net_remote_tod(
+ cli, mem_ctx, cli->srv_name_slash, &tod);
+
+ if (!W_ERROR_IS_OK(result))
+ goto done;
+
+ done:
+ return result;
+}
+
+static WERROR cmd_srvsvc_net_file_enum(struct cli_state *cli,
+ TALLOC_CTX *mem_ctx,
+ int argc, const char **argv)
+{
+ uint32 info_level = 3;
+ SRV_FILE_INFO_CTR ctr;
+ WERROR result;
+ ENUM_HND hnd;
+ uint32 preferred_len = 0;
+
+ if (argc > 2) {
+ printf("Usage: %s [infolevel]\n", argv[0]);
+ return WERR_OK;
+ }
+
+ if (argc == 2)
+ info_level = atoi(argv[1]);
+
+ init_enum_hnd(&hnd, 0);
+
+ ZERO_STRUCT(ctr);
+
+ result = cli_srvsvc_net_file_enum(
+ cli, mem_ctx, info_level, NULL, &ctr, preferred_len, &hnd);
+
+ if (!W_ERROR_IS_OK(result))
+ goto done;
+
+ done:
+ return result;
+}
+
+/* List of commands exported by this module */
+
+struct cmd_set srvsvc_commands[] = {
+
+ { "SRVSVC" },
+
+ { "srvinfo", RPC_RTYPE_WERROR, NULL, cmd_srvsvc_srv_query_info, PI_SRVSVC, "Server query info", "" },
+ { "netshareenum",RPC_RTYPE_WERROR, NULL, cmd_srvsvc_net_share_enum, PI_SRVSVC, "Enumerate shares", "" },
+ { "netfileenum", RPC_RTYPE_WERROR, NULL, cmd_srvsvc_net_file_enum, PI_SRVSVC, "Enumerate open files", "" },
+ { "netremotetod",RPC_RTYPE_WERROR, NULL, cmd_srvsvc_net_remote_tod, PI_SRVSVC, "Fetch remote time of day", "" },
+
+ { NULL }
+};
diff --git a/source/rpcclient/cmd_wkssvc.c b/source/rpcclient/cmd_wkssvc.c
new file mode 100644
index 00000000000..137ff3bdae9
--- /dev/null
+++ b/source/rpcclient/cmd_wkssvc.c
@@ -0,0 +1,84 @@
+/*
+ Unix SMB/CIFS implementation.
+ NT Domain Authentication SMB / MSRPC client
+ Copyright (C) Andrew Tridgell 1994-1997
+ Copyright (C) Luke Kenneth Casson Leighton 1996-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 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"
+
+#define DEBUG_TESTING
+
+extern struct cli_state *smb_cli;
+
+extern FILE* out_hnd;
+
+
+/****************************************************************************
+workstation get info query
+****************************************************************************/
+void cmd_wks_query_info(struct client_info *info)
+{
+ fstring dest_wks;
+ fstring tmp;
+ WKS_INFO_100 ctr;
+ uint32 info_level = 100;
+
+ BOOL res = True;
+
+ memset((char *)&ctr, '\0', sizeof(ctr));
+
+ fstrcpy(dest_wks, "\\\\");
+ fstrcat(dest_wks, info->dest_host);
+ strupper_m(dest_wks);
+
+ if (next_token_nr(NULL, tmp, NULL, sizeof(tmp)))
+ {
+ info_level = (uint32)strtol(tmp, (char**)NULL, 10);
+ }
+
+ DEBUG(4,("cmd_wks_query_info: server:%s info level: %d\n",
+ dest_wks, info_level));
+
+ DEBUG(5, ("cmd_wks_query_info: smb_cli->fd:%d\n", smb_cli->fd));
+
+ /* open LSARPC session. */
+ res = res ? cli_nt_session_open(smb_cli, PI_WKSSVC) : False;
+
+ /* send info level: receive requested info. hopefully. */
+ res = res ? do_wks_query_info(smb_cli,
+ dest_wks, info_level, &ctr) : False;
+
+ /* close the session */
+ cli_nt_session_close(smb_cli);
+
+ if (res)
+ {
+ DEBUG(5,("cmd_wks_query_info: query succeeded\n"));
+
+#if 0
+ display_wks_info_100(out_hnd, ACTION_HEADER , &ctr);
+ display_wks_info_100(out_hnd, ACTION_ENUMERATE, &ctr);
+ display_wks_info_100(out_hnd, ACTION_FOOTER , &ctr);
+#endif
+
+ }
+ else
+ {
+ DEBUG(5,("cmd_wks_query_info: query failed\n"));
+ }
+}
diff --git a/source/rpcclient/display_sec.c b/source/rpcclient/display_sec.c
new file mode 100644
index 00000000000..2a93c915f1a
--- /dev/null
+++ b/source/rpcclient/display_sec.c
@@ -0,0 +1,144 @@
+/*
+ Unix SMB/CIFS implementation.
+ Samba utility functions
+ Copyright (C) Andrew Tridgell 1992-1999
+ Copyright (C) Luke Kenneth Casson Leighton 1996 - 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 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 "rpcclient.h"
+
+/****************************************************************************
+convert a security permissions into a string
+****************************************************************************/
+char *get_sec_mask_str(uint32 type)
+{
+ static fstring typestr="";
+
+ typestr[0] = 0;
+
+ if (type & GENERIC_ALL_ACCESS)
+ fstrcat(typestr, "Generic all access ");
+ if (type & GENERIC_EXECUTE_ACCESS)
+ fstrcat(typestr, "Generic execute access ");
+ if (type & GENERIC_WRITE_ACCESS)
+ fstrcat(typestr, "Generic write access ");
+ if (type & GENERIC_READ_ACCESS)
+ fstrcat(typestr, "Generic read access ");
+ if (type & MAXIMUM_ALLOWED_ACCESS)
+ fstrcat(typestr, "MAXIMUM_ALLOWED_ACCESS ");
+ if (type & SYSTEM_SECURITY_ACCESS)
+ fstrcat(typestr, "SYSTEM_SECURITY_ACCESS ");
+ if (type & SYNCHRONIZE_ACCESS)
+ fstrcat(typestr, "SYNCHRONIZE_ACCESS ");
+ if (type & WRITE_OWNER_ACCESS)
+ fstrcat(typestr, "WRITE_OWNER_ACCESS ");
+ if (type & WRITE_DAC_ACCESS)
+ fstrcat(typestr, "WRITE_DAC_ACCESS ");
+ if (type & READ_CONTROL_ACCESS)
+ fstrcat(typestr, "READ_CONTROL_ACCESS ");
+ if (type & DELETE_ACCESS)
+ fstrcat(typestr, "DELETE_ACCESS ");
+
+ printf("\t\tSpecific bits: 0x%lx\n", (unsigned long)type&SPECIFIC_RIGHTS_MASK);
+
+ return typestr;
+}
+
+/****************************************************************************
+ display sec_access structure
+ ****************************************************************************/
+void display_sec_access(SEC_ACCESS *info)
+{
+ printf("\t\tPermissions: 0x%x: %s\n", info->mask, get_sec_mask_str(info->mask));
+}
+
+/****************************************************************************
+ display sec_ace structure
+ ****************************************************************************/
+void display_sec_ace(SEC_ACE *ace)
+{
+ fstring sid_str;
+
+ printf("\tACE\n\t\ttype: ");
+ switch (ace->type) {
+ case SEC_ACE_TYPE_ACCESS_ALLOWED:
+ printf("ACCESS ALLOWED");
+ break;
+ case SEC_ACE_TYPE_ACCESS_DENIED:
+ printf("ACCESS DENIED");
+ break;
+ case SEC_ACE_TYPE_SYSTEM_AUDIT:
+ printf("SYSTEM AUDIT");
+ break;
+ case SEC_ACE_TYPE_SYSTEM_ALARM:
+ printf("SYSTEM ALARM");
+ break;
+ default:
+ printf("????");
+ break;
+ }
+ printf(" (%d) flags: %d\n", ace->type, ace->flags);
+ display_sec_access(&ace->info);
+ sid_to_string(sid_str, &ace->trustee);
+ printf("\t\tSID: %s\n\n", sid_str);
+}
+
+/****************************************************************************
+ display sec_acl structure
+ ****************************************************************************/
+void display_sec_acl(SEC_ACL *sec_acl)
+{
+ int i;
+
+ printf("\tACL\tNum ACEs:\t%d\trevision:\t%x\n",
+ sec_acl->num_aces, sec_acl->revision);
+ printf("\t---\n");
+
+ if (sec_acl->size != 0 && sec_acl->num_aces != 0)
+ for (i = 0; i < sec_acl->num_aces; i++)
+ display_sec_ace(&sec_acl->ace[i]);
+
+}
+
+/****************************************************************************
+ display sec_desc structure
+ ****************************************************************************/
+void display_sec_desc(SEC_DESC *sec)
+{
+ fstring sid_str;
+
+ if (sec->sacl) {
+ printf("SACL\n");
+ display_sec_acl(sec->sacl);
+ }
+
+ if (sec->dacl) {
+ printf("DACL\n");
+ display_sec_acl(sec->dacl);
+ }
+
+ if (sec->owner_sid) {
+ sid_to_string(sid_str, sec->owner_sid);
+ printf("\tOwner SID:\t%s\n", sid_str);
+ }
+
+ if (sec->grp_sid) {
+ sid_to_string(sid_str, sec->grp_sid);
+ printf("\tParent SID:\t%s\n", sid_str);
+ }
+}
diff --git a/source/rpcclient/rpcclient.c b/source/rpcclient/rpcclient.c
new file mode 100644
index 00000000000..8372b75b4bd
--- /dev/null
+++ b/source/rpcclient/rpcclient.c
@@ -0,0 +1,803 @@
+/*
+ Unix SMB/CIFS implementation.
+ RPC pipe client
+
+ Copyright (C) Tim Potter 2000-2001
+ Copyright (C) Martin Pool 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 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 "rpcclient.h"
+
+DOM_SID domain_sid;
+
+
+/* List to hold groups of commands.
+ *
+ * Commands are defined in a list of arrays: arrays are easy to
+ * statically declare, and lists are easier to dynamically extend.
+ */
+
+static struct cmd_list {
+ struct cmd_list *prev, *next;
+ struct cmd_set *cmd_set;
+} *cmd_list;
+
+/****************************************************************************
+handle completion of commands for readline
+****************************************************************************/
+static char **completion_fn(const char *text, int start, int end)
+{
+#define MAX_COMPLETIONS 100
+ char **matches;
+ int i, count=0;
+ struct cmd_list *commands = cmd_list;
+
+#if 0 /* JERRY */
+ /* FIXME!!! -- what to do when completing argument? */
+ /* for words not at the start of the line fallback
+ to filename completion */
+ if (start)
+ return NULL;
+#endif
+
+ /* make sure we have a list of valid commands */
+ if (!commands)
+ return NULL;
+
+ matches = (char **)malloc(sizeof(matches[0])*MAX_COMPLETIONS);
+ if (!matches) return NULL;
+
+ matches[count++] = strdup(text);
+ if (!matches[0]) return NULL;
+
+ while (commands && count < MAX_COMPLETIONS-1)
+ {
+ if (!commands->cmd_set)
+ break;
+
+ for (i=0; commands->cmd_set[i].name; i++)
+ {
+ if ((strncmp(text, commands->cmd_set[i].name, strlen(text)) == 0) &&
+ (( commands->cmd_set[i].returntype == RPC_RTYPE_NTSTATUS &&
+ commands->cmd_set[i].ntfn ) ||
+ ( commands->cmd_set[i].returntype == RPC_RTYPE_WERROR &&
+ commands->cmd_set[i].wfn)))
+ {
+ matches[count] = strdup(commands->cmd_set[i].name);
+ if (!matches[count])
+ return NULL;
+ count++;
+ }
+ }
+
+ commands = commands->next;
+
+ }
+
+ if (count == 2) {
+ SAFE_FREE(matches[0]);
+ matches[0] = strdup(matches[1]);
+ }
+ matches[count] = NULL;
+ return matches;
+}
+
+static char* next_command (char** cmdstr)
+{
+ static pstring command;
+ char *p;
+
+ if (!cmdstr || !(*cmdstr))
+ return NULL;
+
+ p = strchr_m(*cmdstr, ';');
+ if (p)
+ *p = '\0';
+ pstrcpy(command, *cmdstr);
+ if (p)
+ *cmdstr = p + 1;
+ else
+ *cmdstr = NULL;
+
+ return command;
+}
+
+/* Fetch the SID for this computer */
+
+static void fetch_machine_sid(struct cli_state *cli)
+{
+ POLICY_HND pol;
+ NTSTATUS result = NT_STATUS_OK;
+ uint32 info_class = 5;
+ char *domain_name = NULL;
+ static BOOL got_domain_sid;
+ TALLOC_CTX *mem_ctx;
+ DOM_SID *dom_sid = NULL;
+
+ if (got_domain_sid) return;
+
+ if (!(mem_ctx=talloc_init("fetch_machine_sid")))
+ {
+ DEBUG(0,("fetch_machine_sid: talloc_init returned NULL!\n"));
+ goto error;
+ }
+
+
+ if (!cli_nt_session_open (cli, PI_LSARPC)) {
+ fprintf(stderr, "could not initialise lsa pipe\n");
+ goto error;
+ }
+
+ result = cli_lsa_open_policy(cli, mem_ctx, True,
+ SEC_RIGHTS_MAXIMUM_ALLOWED,
+ &pol);
+ if (!NT_STATUS_IS_OK(result)) {
+ goto error;
+ }
+
+ result = cli_lsa_query_info_policy(cli, mem_ctx, &pol, info_class,
+ &domain_name, &dom_sid);
+ if (!NT_STATUS_IS_OK(result)) {
+ goto error;
+ }
+
+ got_domain_sid = True;
+ sid_copy( &domain_sid, dom_sid );
+
+ cli_lsa_close(cli, mem_ctx, &pol);
+ cli_nt_session_close(cli);
+ talloc_destroy(mem_ctx);
+
+ return;
+
+ error:
+ fprintf(stderr, "could not obtain sid for domain %s\n", cli->domain);
+
+ if (!NT_STATUS_IS_OK(result)) {
+ fprintf(stderr, "error: %s\n", nt_errstr(result));
+ }
+
+ exit(1);
+}
+
+/* List the available commands on a given pipe */
+
+static NTSTATUS cmd_listcommands(struct cli_state *cli, TALLOC_CTX *mem_ctx,
+ int argc, const char **argv)
+{
+ struct cmd_list *tmp;
+ struct cmd_set *tmp_set;
+ int i;
+
+ /* Usage */
+
+ if (argc != 2) {
+ printf("Usage: %s <pipe>\n", argv[0]);
+ return NT_STATUS_OK;
+ }
+
+ /* Help on one command */
+
+ for (tmp = cmd_list; tmp; tmp = tmp->next)
+ {
+ tmp_set = tmp->cmd_set;
+
+ if (!StrCaseCmp(argv[1], tmp_set->name))
+ {
+ printf("Available commands on the %s pipe:\n\n", tmp_set->name);
+
+ i = 0;
+ tmp_set++;
+ while(tmp_set->name) {
+ printf("%20s", tmp_set->name);
+ tmp_set++;
+ i++;
+ if (i%4 == 0)
+ printf("\n");
+ }
+
+ /* drop out of the loop */
+ break;
+ }
+ }
+ printf("\n\n");
+
+ return NT_STATUS_OK;
+}
+
+/* Display help on commands */
+
+static NTSTATUS cmd_help(struct cli_state *cli, TALLOC_CTX *mem_ctx,
+ int argc, const char **argv)
+{
+ struct cmd_list *tmp;
+ struct cmd_set *tmp_set;
+
+ /* Usage */
+
+ if (argc > 2) {
+ printf("Usage: %s [command]\n", argv[0]);
+ return NT_STATUS_OK;
+ }
+
+ /* Help on one command */
+
+ if (argc == 2) {
+ for (tmp = cmd_list; tmp; tmp = tmp->next) {
+
+ tmp_set = tmp->cmd_set;
+
+ while(tmp_set->name) {
+ if (strequal(argv[1], tmp_set->name)) {
+ if (tmp_set->usage &&
+ tmp_set->usage[0])
+ printf("%s\n", tmp_set->usage);
+ else
+ printf("No help for %s\n", tmp_set->name);
+
+ return NT_STATUS_OK;
+ }
+
+ tmp_set++;
+ }
+ }
+
+ printf("No such command: %s\n", argv[1]);
+ return NT_STATUS_OK;
+ }
+
+ /* List all commands */
+
+ for (tmp = cmd_list; tmp; tmp = tmp->next) {
+
+ tmp_set = tmp->cmd_set;
+
+ while(tmp_set->name) {
+
+ printf("%15s\t\t%s\n", tmp_set->name,
+ tmp_set->description ? tmp_set->description:
+ "");
+
+ tmp_set++;
+ }
+ }
+
+ return NT_STATUS_OK;
+}
+
+/* Change the debug level */
+
+static NTSTATUS cmd_debuglevel(struct cli_state *cli, TALLOC_CTX *mem_ctx,
+ int argc, const char **argv)
+{
+ if (argc > 2) {
+ printf("Usage: %s [debuglevel]\n", argv[0]);
+ return NT_STATUS_OK;
+ }
+
+ if (argc == 2) {
+ DEBUGLEVEL = atoi(argv[1]);
+ }
+
+ printf("debuglevel is %d\n", DEBUGLEVEL);
+
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS cmd_quit(struct cli_state *cli, TALLOC_CTX *mem_ctx,
+ int argc, const char **argv)
+{
+ exit(0);
+ return NT_STATUS_OK; /* NOTREACHED */
+}
+
+static NTSTATUS cmd_sign(struct cli_state *cli, TALLOC_CTX *mem_ctx,
+ int argc, const char **argv)
+{
+ if (cli->pipe_auth_flags == (AUTH_PIPE_NTLMSSP|AUTH_PIPE_SIGN)) {
+ return NT_STATUS_OK;
+ } else {
+ /* still have session, just need to use it again */
+ cli->pipe_auth_flags = AUTH_PIPE_NTLMSSP;
+ cli->pipe_auth_flags |= AUTH_PIPE_SIGN;
+ if (cli->nt_pipe_fnum != 0)
+ cli_nt_session_close(cli);
+ }
+
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS cmd_seal(struct cli_state *cli, TALLOC_CTX *mem_ctx,
+ int argc, const char **argv)
+{
+ if (cli->pipe_auth_flags == (AUTH_PIPE_NTLMSSP|AUTH_PIPE_SIGN|AUTH_PIPE_SEAL)) {
+ return NT_STATUS_OK;
+ } else {
+ /* still have session, just need to use it again */
+ cli->pipe_auth_flags = AUTH_PIPE_NTLMSSP;
+ cli->pipe_auth_flags |= AUTH_PIPE_SIGN;
+ cli->pipe_auth_flags |= AUTH_PIPE_SEAL;
+ if (cli->nt_pipe_fnum != 0)
+ cli_nt_session_close(cli);
+ }
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS cmd_none(struct cli_state *cli, TALLOC_CTX *mem_ctx,
+ int argc, const char **argv)
+{
+ if (cli->pipe_auth_flags == 0) {
+ return NT_STATUS_OK;
+ } else {
+ /* still have session, just need to use it again */
+ cli->pipe_auth_flags = 0;
+ if (cli->nt_pipe_fnum != 0)
+ cli_nt_session_close(cli);
+ }
+ cli->pipe_auth_flags = 0;
+
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS setup_schannel(struct cli_state *cli, int pipe_auth_flags,
+ int argc, const char **argv)
+{
+ NTSTATUS ret;
+ static uchar zeros[16];
+ uchar trust_password[16];
+ uint32 sec_channel_type;
+ if (argc == 2) {
+ strhex_to_str((char *)cli->auth_info.sess_key,
+ strlen(argv[1]),
+ argv[1]);
+ memcpy(cli->sess_key, cli->auth_info.sess_key, sizeof(cli->sess_key));
+
+ cli->pipe_auth_flags = pipe_auth_flags;
+ return NT_STATUS_OK;
+ }
+
+ /* Cleanup */
+
+ if ((memcmp(cli->auth_info.sess_key, zeros, sizeof(cli->auth_info.sess_key)) != 0)) {
+ if (cli->pipe_auth_flags == pipe_auth_flags) {
+ /* already in this mode nothing to do */
+ return NT_STATUS_OK;
+ } else {
+ /* schannel is setup, just need to use it again with new flags */
+ cli->pipe_auth_flags = pipe_auth_flags;
+
+ if (cli->nt_pipe_fnum != 0)
+ cli_nt_session_close(cli);
+ return NT_STATUS_OK;
+ }
+ }
+
+ if (cli->nt_pipe_fnum != 0)
+ cli_nt_session_close(cli);
+
+ if (!secrets_fetch_trust_account_password(lp_workgroup(),
+ trust_password,
+ NULL, &sec_channel_type)) {
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ ret = cli_nt_setup_netsec(cli, sec_channel_type, pipe_auth_flags, trust_password);
+ if (NT_STATUS_IS_OK(ret)) {
+ char *hex_session_key;
+ hex_encode(cli->auth_info.sess_key,
+ sizeof(cli->auth_info.sess_key),
+ &hex_session_key);
+ printf("Got Session key: %s\n", hex_session_key);
+ SAFE_FREE(hex_session_key);
+ }
+ return ret;
+}
+
+
+static NTSTATUS cmd_schannel(struct cli_state *cli, TALLOC_CTX *mem_ctx,
+ int argc, const char **argv)
+{
+ d_printf("Setting schannel - sign and seal\n");
+ return setup_schannel(cli, AUTH_PIPE_NETSEC | AUTH_PIPE_SIGN | AUTH_PIPE_SEAL,
+ argc, argv);
+}
+
+static NTSTATUS cmd_schannel_sign(struct cli_state *cli, TALLOC_CTX *mem_ctx,
+ int argc, const char **argv)
+{
+ d_printf("Setting schannel - sign only\n");
+ return setup_schannel(cli, AUTH_PIPE_NETSEC | AUTH_PIPE_SIGN,
+ argc, argv);
+}
+
+
+/* Built in rpcclient commands */
+
+static struct cmd_set rpcclient_commands[] = {
+
+ { "GENERAL OPTIONS" },
+
+ { "help", RPC_RTYPE_NTSTATUS, cmd_help, NULL, -1, "Get help on commands", "[command]" },
+ { "?", RPC_RTYPE_NTSTATUS, cmd_help, NULL, -1, "Get help on commands", "[command]" },
+ { "debuglevel", RPC_RTYPE_NTSTATUS, cmd_debuglevel, NULL, -1, "Set debug level", "level" },
+ { "list", RPC_RTYPE_NTSTATUS, cmd_listcommands, NULL, -1, "List available commands on <pipe>", "pipe" },
+ { "exit", RPC_RTYPE_NTSTATUS, cmd_quit, NULL, -1, "Exit program", "" },
+ { "quit", RPC_RTYPE_NTSTATUS, cmd_quit, NULL, -1, "Exit program", "" },
+ { "sign", RPC_RTYPE_NTSTATUS, cmd_sign, NULL, -1, "Force RPC pipe connections to be signed", "" },
+ { "seal", RPC_RTYPE_NTSTATUS, cmd_seal, NULL, -1, "Force RPC pipe connections to be sealed", "" },
+ { "schannel", RPC_RTYPE_NTSTATUS, cmd_schannel, NULL, -1, "Force RPC pipe connections to be sealed with 'schannel' (NETSEC). Assumes valid machine account to this domain controller.", "" },
+ { "schannelsign", RPC_RTYPE_NTSTATUS, cmd_schannel_sign, NULL, -1, "Force RPC pipe connections to be signed (not sealed) with 'schannel' (NETSEC). Assumes valid machine account to this domain controller.", "" },
+ { "none", RPC_RTYPE_NTSTATUS, cmd_none, NULL, -1, "Force RPC pipe connections to have no special properties", "" },
+
+ { NULL }
+};
+
+static struct cmd_set separator_command[] = {
+ { "---------------", MAX_RPC_RETURN_TYPE, NULL, NULL, -1, "----------------------" },
+ { NULL }
+};
+
+
+/* Various pipe commands */
+
+extern struct cmd_set lsarpc_commands[];
+extern struct cmd_set samr_commands[];
+extern struct cmd_set spoolss_commands[];
+extern struct cmd_set netlogon_commands[];
+extern struct cmd_set srvsvc_commands[];
+extern struct cmd_set dfs_commands[];
+extern struct cmd_set reg_commands[];
+extern struct cmd_set ds_commands[];
+extern struct cmd_set echo_commands[];
+extern struct cmd_set shutdown_commands[];
+extern struct cmd_set epm_commands[];
+
+static struct cmd_set *rpcclient_command_list[] = {
+ rpcclient_commands,
+ lsarpc_commands,
+ ds_commands,
+ samr_commands,
+ spoolss_commands,
+ netlogon_commands,
+ srvsvc_commands,
+ dfs_commands,
+ reg_commands,
+ echo_commands,
+ shutdown_commands,
+ epm_commands,
+ NULL
+};
+
+static void add_command_set(struct cmd_set *cmd_set)
+{
+ struct cmd_list *entry;
+
+ if (!(entry = (struct cmd_list *)malloc(sizeof(struct cmd_list)))) {
+ DEBUG(0, ("out of memory\n"));
+ return;
+ }
+
+ ZERO_STRUCTP(entry);
+
+ entry->cmd_set = cmd_set;
+ DLIST_ADD(cmd_list, entry);
+}
+
+
+/**
+ * Call an rpcclient function, passing an argv array.
+ *
+ * @param cmd Command to run, as a single string.
+ **/
+static NTSTATUS do_cmd(struct cli_state *cli,
+ struct cmd_set *cmd_entry,
+ int argc, char **argv)
+{
+ NTSTATUS ntresult;
+ WERROR wresult;
+ uchar trust_password[16];
+
+ TALLOC_CTX *mem_ctx;
+
+ /* Create mem_ctx */
+
+ if (!(mem_ctx = talloc_init("do_cmd"))) {
+ DEBUG(0, ("talloc_init() failed\n"));
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ /* Open pipe */
+
+ if (cmd_entry->pipe_idx != -1
+ && cmd_entry->pipe_idx != cli->pipe_idx) {
+ if (cli->nt_pipe_fnum != 0)
+ cli_nt_session_close(cli);
+
+ if (!cli_nt_session_open(cli, cmd_entry->pipe_idx)) {
+ DEBUG(0, ("Could not initialise %s\n",
+ get_pipe_name_from_index(cmd_entry->pipe_idx)));
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+ }
+
+ /* some of the DsXXX commands use the netlogon pipe */
+
+ if (lp_client_schannel() && (cmd_entry->pipe_idx == PI_NETLOGON) && !(cli->pipe_auth_flags & AUTH_PIPE_NETSEC)) {
+ uint32 neg_flags = NETLOGON_NEG_AUTH2_FLAGS;
+ uint32 sec_channel_type;
+
+ if (!secrets_fetch_trust_account_password(lp_workgroup(),
+ trust_password,
+ NULL, &sec_channel_type)) {
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ ntresult = cli_nt_setup_creds(cli, sec_channel_type,
+ trust_password,
+ &neg_flags, 2);
+ if (!NT_STATUS_IS_OK(ntresult)) {
+ ZERO_STRUCT(cli->auth_info.sess_key);
+ printf("nt_setup_creds failed with %s\n", nt_errstr(ntresult));
+ return ntresult;
+ }
+
+ }
+
+ /* Run command */
+
+ if ( cmd_entry->returntype == RPC_RTYPE_NTSTATUS ) {
+ ntresult = cmd_entry->ntfn(cli, mem_ctx, argc, (const char **) argv);
+ if (!NT_STATUS_IS_OK(ntresult)) {
+ printf("result was %s\n", nt_errstr(ntresult));
+ }
+ } else {
+ wresult = cmd_entry->wfn( cli, mem_ctx, argc, (const char **) argv);
+ /* print out the DOS error */
+ if (!W_ERROR_IS_OK(wresult)) {
+ printf( "result was %s\n", dos_errstr(wresult));
+ }
+ ntresult = W_ERROR_IS_OK(wresult)?NT_STATUS_OK:NT_STATUS_UNSUCCESSFUL;
+ }
+
+
+ /* Cleanup */
+
+ talloc_destroy(mem_ctx);
+
+ return ntresult;
+}
+
+
+/**
+ * Process a command entered at the prompt or as part of -c
+ *
+ * @returns The NTSTATUS from running the command.
+ **/
+static NTSTATUS process_cmd(struct cli_state *cli, char *cmd)
+{
+ struct cmd_list *temp_list;
+ NTSTATUS result = NT_STATUS_OK;
+ int ret;
+ int argc;
+ char **argv = NULL;
+
+ if ((ret = poptParseArgvString(cmd, &argc, (const char ***) &argv)) != 0) {
+ fprintf(stderr, "rpcclient: %s\n", poptStrerror(ret));
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+
+ /* Walk through a dlist of arrays of commands. */
+ for (temp_list = cmd_list; temp_list; temp_list = temp_list->next) {
+ struct cmd_set *temp_set = temp_list->cmd_set;
+
+ while (temp_set->name) {
+ if (strequal(argv[0], temp_set->name)) {
+ if (!(temp_set->returntype == RPC_RTYPE_NTSTATUS && temp_set->ntfn ) &&
+ !(temp_set->returntype == RPC_RTYPE_WERROR && temp_set->wfn )) {
+ fprintf (stderr, "Invalid command\n");
+ goto out_free;
+ }
+
+ result = do_cmd(cli, temp_set, argc, argv);
+
+ goto out_free;
+ }
+ temp_set++;
+ }
+ }
+
+ if (argv[0]) {
+ printf("command not found: %s\n", argv[0]);
+ }
+
+out_free:
+/* moved to do_cmd()
+ if (!NT_STATUS_IS_OK(result)) {
+ printf("result was %s\n", nt_errstr(result));
+ }
+*/
+
+ if (argv) {
+ /* NOTE: popt allocates the whole argv, including the
+ * strings, as a single block. So a single free is
+ * enough to release it -- we don't free the
+ * individual strings. rtfm. */
+ free(argv);
+ }
+
+ return result;
+}
+
+
+/* Main function */
+
+ int main(int argc, char *argv[])
+{
+ BOOL interactive = True;
+ int opt;
+ static char *cmdstr = NULL;
+ const char *server;
+ struct cli_state *cli;
+ static char *opt_ipaddr=NULL;
+ struct cmd_set **cmd_set;
+ struct in_addr server_ip;
+ NTSTATUS nt_status;
+
+ /* 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
+ {"command", 'c', POPT_ARG_STRING, &cmdstr, 'c', "Execute semicolon separated cmds", "COMMANDS"},
+ {"dest-ip", 'I', POPT_ARG_STRING, &opt_ipaddr, 'I', "Specify destination IP address", "IP"},
+ POPT_COMMON_SAMBA
+ POPT_COMMON_CONNECTION
+ POPT_COMMON_CREDENTIALS
+ POPT_TABLEEND
+ };
+
+ ZERO_STRUCT(server_ip);
+
+ setlinebuf(stdout);
+
+ /* the following functions are part of the Samba debugging
+ facilities. See lib/debug.c */
+ setup_logging("rpcclient", interactive);
+ if (!interactive)
+ reopen_logs();
+
+ /* Load smb.conf file */
+
+ if (!lp_load(dyn_CONFIGFILE,True,False,False))
+ fprintf(stderr, "Can't load %s\n", dyn_CONFIGFILE);
+
+ /* Parse options */
+
+ pc = poptGetContext("rpcclient", argc, (const char **) argv,
+ long_options, 0);
+
+ if (argc == 1) {
+ poptPrintHelp(pc, stderr, 0);
+ return 0;
+ }
+
+ while((opt = poptGetNextOpt(pc)) != -1) {
+ switch (opt) {
+
+ case 'I':
+ if ( (server_ip.s_addr=inet_addr(opt_ipaddr)) == INADDR_NONE ) {
+ fprintf(stderr, "%s not a valid IP address\n",
+ opt_ipaddr);
+ return 1;
+ }
+ }
+ }
+
+ /* Get server as remaining unparsed argument. Print usage if more
+ than one unparsed argument is present. */
+
+ server = poptGetArg(pc);
+
+ if (!server || poptGetArg(pc)) {
+ poptPrintHelp(pc, stderr, 0);
+ return 1;
+ }
+
+ poptFreeContext(pc);
+
+ load_interfaces();
+
+ if (!init_names())
+ return 1;
+
+ /*
+ * Get password
+ * from stdin if necessary
+ */
+
+ if (!cmdline_auth_info.got_pass) {
+ char *pass = getpass("Password:");
+ if (pass) {
+ pstrcpy(cmdline_auth_info.password, pass);
+ }
+ }
+
+ nt_status = cli_full_connection(&cli, global_myname(), server,
+ opt_ipaddr ? &server_ip : NULL, 0,
+ "IPC$", "IPC",
+ cmdline_auth_info.username,
+ lp_workgroup(),
+ cmdline_auth_info.password,
+ cmdline_auth_info.use_kerberos ? CLI_FULL_CONNECTION_USE_KERBEROS : 0,
+ cmdline_auth_info.signing_state,NULL);
+
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ DEBUG(0,("Cannot connect to server. Error was %s\n", nt_errstr(nt_status)));
+ return 1;
+ }
+
+ memset(cmdline_auth_info.password,'X',sizeof(cmdline_auth_info.password));
+
+ /* Load command lists */
+
+ cmd_set = rpcclient_command_list;
+
+ while(*cmd_set) {
+ add_command_set(*cmd_set);
+ add_command_set(separator_command);
+ cmd_set++;
+ }
+
+ fetch_machine_sid(cli);
+
+ /* Do anything specified with -c */
+ if (cmdstr && cmdstr[0]) {
+ char *cmd;
+ char *p = cmdstr;
+ int result = 0;
+
+ while((cmd=next_command(&p)) != NULL) {
+ NTSTATUS cmd_result = process_cmd(cli, cmd);
+ result = NT_STATUS_IS_ERR(cmd_result);
+ }
+
+ cli_shutdown(cli);
+ return result;
+ }
+
+ /* Loop around accepting commands */
+
+ while(1) {
+ pstring prompt;
+ char *line;
+
+ slprintf(prompt, sizeof(prompt) - 1, "rpcclient $> ");
+
+ line = smb_readline(prompt, NULL, completion_fn);
+
+ if (line == NULL)
+ break;
+
+ if (line[0] != '\n')
+ process_cmd(cli, line);
+ }
+
+ cli_shutdown(cli);
+ return 0;
+}
diff --git a/source/rpcclient/rpcclient.h b/source/rpcclient/rpcclient.h
new file mode 100644
index 00000000000..e1e61dc43d5
--- /dev/null
+++ b/source/rpcclient/rpcclient.h
@@ -0,0 +1,42 @@
+/*
+ Unix SMB/CIFS implementation.
+ RPC pipe client
+
+ 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 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 RPCCLIENT_H
+#define RPCCLIENT_H
+
+typedef enum {
+ RPC_RTYPE_NTSTATUS = 0,
+ RPC_RTYPE_WERROR,
+ MAX_RPC_RETURN_TYPE
+} RPC_RETURN_TYPE;
+
+struct cmd_set {
+ const char *name;
+ RPC_RETURN_TYPE returntype;
+ NTSTATUS (*ntfn)(struct cli_state *cli, TALLOC_CTX *mem_ctx, int argc,
+ const char **argv);
+ WERROR (*wfn)(struct cli_state *cli, TALLOC_CTX *mem_ctx, int argc, const char **argv);
+ int pipe_idx;
+ const char *description;
+ const char *usage;
+};
+
+#endif /* RPCCLIENT_H */
diff --git a/source/sam/account.c b/source/sam/account.c
new file mode 100644
index 00000000000..b8336146cda
--- /dev/null
+++ b/source/sam/account.c
@@ -0,0 +1,305 @@
+/*
+ Unix SMB/CIFS implementation.
+ Password and authentication handling
+ Copyright (C) Jeremy Allison 1996-2001
+ Copyright (C) Luke Kenneth Casson Leighton 1996-1998
+ Copyright (C) Gerald (Jerry) Carter 2000-2001
+ Copyright (C) Andrew Bartlett 2001-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 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"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_SAM
+
+/************************************************************
+ Fill the SAM_ACCOUNT_HANDLE with default values.
+ ***********************************************************/
+
+static void sam_fill_default_account(SAM_ACCOUNT_HANDLE *account)
+{
+ ZERO_STRUCT(account->private); /* Don't touch the talloc context */
+
+ /* Don't change these timestamp settings without a good reason.
+ They are important for NT member server compatibility. */
+
+ /* FIXME: We should actually call get_nt_time_max() or sthng
+ * here */
+ unix_to_nt_time(&(account->private.logoff_time),get_time_t_max());
+ unix_to_nt_time(&(account->private.kickoff_time),get_time_t_max());
+ unix_to_nt_time(&(account->private.pass_must_change_time),get_time_t_max());
+ account->private.unknown_1 = 0x00ffffff; /* don't know */
+ account->private.logon_divs = 168; /* hours per week */
+ account->private.hours_len = 21; /* 21 times 8 bits = 168 */
+ memset(account->private.hours, 0xff, account->private.hours_len); /* available at all hours */
+ account->private.unknown_2 = 0x00000000; /* don't know */
+ account->private.unknown_3 = 0x000004ec; /* don't know */
+}
+
+static void destroy_sam_talloc(SAM_ACCOUNT_HANDLE **account)
+{
+ if (*account) {
+ data_blob_clear_free(&((*account)->private.lm_pw));
+ data_blob_clear_free(&((*account)->private.nt_pw));
+ if((*account)->private.plaintext_pw!=NULL)
+ memset((*account)->private.plaintext_pw,'\0',strlen((*account)->private.plaintext_pw));
+
+ talloc_destroy((*account)->mem_ctx);
+ *account = NULL;
+ }
+}
+
+
+/**********************************************************************
+ Alloc memory and initialises a SAM_ACCOUNT_HANDLE on supplied mem_ctx.
+***********************************************************************/
+
+NTSTATUS sam_init_account_talloc(TALLOC_CTX *mem_ctx, SAM_ACCOUNT_HANDLE **account)
+{
+ SMB_ASSERT(*account != NULL);
+
+ if (!mem_ctx) {
+ DEBUG(0,("sam_init_account_talloc: mem_ctx was NULL!\n"));
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ *account=(SAM_ACCOUNT_HANDLE *)talloc(mem_ctx, sizeof(SAM_ACCOUNT_HANDLE));
+
+ if (*account==NULL) {
+ DEBUG(0,("sam_init_account_talloc: error while allocating memory\n"));
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ (*account)->mem_ctx = mem_ctx;
+
+ (*account)->free_fn = NULL;
+
+ sam_fill_default_account(*account);
+
+ return NT_STATUS_OK;
+}
+
+
+/*************************************************************
+ Alloc memory and initialises a struct sam_passwd.
+ ************************************************************/
+
+NTSTATUS sam_init_account(SAM_ACCOUNT_HANDLE **account)
+{
+ TALLOC_CTX *mem_ctx;
+ NTSTATUS nt_status;
+
+ mem_ctx = talloc_init("sam internal SAM_ACCOUNT_HANDLE allocation");
+
+ if (!mem_ctx) {
+ DEBUG(0,("sam_init_account: error while doing talloc_init()\n"));
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ if (!NT_STATUS_IS_OK(nt_status = sam_init_account_talloc(mem_ctx, account))) {
+ talloc_destroy(mem_ctx);
+ return nt_status;
+ }
+
+ (*account)->free_fn = destroy_sam_talloc;
+
+ return NT_STATUS_OK;
+}
+
+/**
+ * Free the contents of the SAM_ACCOUNT_HANDLE, but not the structure.
+ *
+ * Also wipes the LM and NT hashes and plaintext password from
+ * memory.
+ *
+ * @param account SAM_ACCOUNT_HANDLE to free members of.
+ **/
+
+static void sam_free_account_contents(SAM_ACCOUNT_HANDLE *account)
+{
+
+ /* Kill off sensitive data. Free()ed by the
+ talloc mechinism */
+
+ data_blob_clear_free(&(account->private.lm_pw));
+ data_blob_clear_free(&(account->private.nt_pw));
+ if (account->private.plaintext_pw)
+ memset(account->private.plaintext_pw,'\0',strlen(account->private.plaintext_pw));
+}
+
+
+/************************************************************
+ Reset the SAM_ACCOUNT_HANDLE and free the NT/LM hashes.
+ ***********************************************************/
+
+NTSTATUS sam_reset_sam(SAM_ACCOUNT_HANDLE *account)
+{
+ SMB_ASSERT(account != NULL);
+
+ sam_free_account_contents(account);
+
+ sam_fill_default_account(account);
+
+ return NT_STATUS_OK;
+}
+
+
+/************************************************************
+ Free the SAM_ACCOUNT_HANDLE and the member pointers.
+ ***********************************************************/
+
+NTSTATUS sam_free_account(SAM_ACCOUNT_HANDLE **account)
+{
+ SMB_ASSERT(*account != NULL);
+
+ sam_free_account_contents(*account);
+
+ if ((*account)->free_fn) {
+ (*account)->free_fn(account);
+ }
+
+ return NT_STATUS_OK;
+}
+
+
+/**********************************************************
+ Encode the account control bits into a string.
+ length = length of string to encode into (including terminating
+ null). length *MUST BE MORE THAN 2* !
+ **********************************************************/
+
+char *sam_encode_acct_ctrl(uint16 acct_ctrl, size_t length)
+{
+ static fstring acct_str;
+ size_t i = 0;
+
+ acct_str[i++] = '[';
+
+ if (acct_ctrl & ACB_PWNOTREQ ) acct_str[i++] = 'N';
+ if (acct_ctrl & ACB_DISABLED ) acct_str[i++] = 'D';
+ if (acct_ctrl & ACB_HOMDIRREQ) acct_str[i++] = 'H';
+ if (acct_ctrl & ACB_TEMPDUP ) acct_str[i++] = 'T';
+ if (acct_ctrl & ACB_NORMAL ) acct_str[i++] = 'U';
+ if (acct_ctrl & ACB_MNS ) acct_str[i++] = 'M';
+ if (acct_ctrl & ACB_WSTRUST ) acct_str[i++] = 'W';
+ if (acct_ctrl & ACB_SVRTRUST ) acct_str[i++] = 'S';
+ if (acct_ctrl & ACB_AUTOLOCK ) acct_str[i++] = 'L';
+ if (acct_ctrl & ACB_PWNOEXP ) acct_str[i++] = 'X';
+ if (acct_ctrl & ACB_DOMTRUST ) acct_str[i++] = 'I';
+
+ for ( ; i < length - 2 ; i++ )
+ acct_str[i] = ' ';
+
+ i = length - 2;
+ acct_str[i++] = ']';
+ acct_str[i++] = '\0';
+
+ return acct_str;
+}
+
+/**********************************************************
+ Decode the account control bits from a string.
+ **********************************************************/
+
+uint16 sam_decode_acct_ctrl(const char *p)
+{
+ uint16 acct_ctrl = 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': { acct_ctrl |= ACB_PWNOTREQ ; break; /* 'N'o password. */ }
+ case 'D': { acct_ctrl |= ACB_DISABLED ; break; /* 'D'isabled. */ }
+ case 'H': { acct_ctrl |= ACB_HOMDIRREQ; break; /* 'H'omedir required. */ }
+ case 'T': { acct_ctrl |= ACB_TEMPDUP ; break; /* 'T'emp account. */ }
+ case 'U': { acct_ctrl |= ACB_NORMAL ; break; /* 'U'ser account (normal). */ }
+ case 'M': { acct_ctrl |= ACB_MNS ; break; /* 'M'NS logon user account. What is this ? */ }
+ case 'W': { acct_ctrl |= ACB_WSTRUST ; break; /* 'W'orkstation account. */ }
+ case 'S': { acct_ctrl |= ACB_SVRTRUST ; break; /* 'S'erver account. */ }
+ case 'L': { acct_ctrl |= ACB_AUTOLOCK ; break; /* 'L'ocked account. */ }
+ case 'X': { acct_ctrl |= ACB_PWNOEXP ; break; /* No 'X'piry on password */ }
+ case 'I': { acct_ctrl |= ACB_DOMTRUST ; break; /* 'I'nterdomain trust account. */ }
+ case ' ': { break; }
+ case ':':
+ case '\n':
+ case '\0':
+ case ']':
+ default: { finished = True; }
+ }
+ }
+
+ return acct_ctrl;
+}
+
+/*************************************************************
+ Routine to set 32 hex password characters from a 16 byte array.
+**************************************************************/
+
+void sam_sethexpwd(char *p, const unsigned char *pwd, uint16 acct_ctrl)
+{
+ if (pwd != NULL) {
+ int i;
+ for (i = 0; i < 16; i++)
+ slprintf(&p[i*2], 3, "%02X", pwd[i]);
+ } else {
+ if (acct_ctrl & ACB_PWNOTREQ)
+ safe_strcpy(p, "NO PASSWORDXXXXXXXXXXXXXXXXXXXXX", 33);
+ else
+ safe_strcpy(p, "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX", 33);
+ }
+}
+
+/*************************************************************
+ Routine to get the 32 hex characters and turn them
+ into a 16 byte array.
+**************************************************************/
+
+BOOL sam_gethexpwd(const char *p, unsigned char *pwd)
+{
+ int i;
+ unsigned char lonybble, hinybble;
+ char *hexchars = "0123456789ABCDEF";
+ char *p1, *p2;
+
+ if (!p)
+ return (False);
+
+ for (i = 0; i < 32; i += 2) {
+ hinybble = toupper(p[i]);
+ lonybble = toupper(p[i + 1]);
+
+ p1 = strchr(hexchars, hinybble);
+ p2 = strchr(hexchars, lonybble);
+
+ if (!p1 || !p2)
+ return (False);
+
+ hinybble = PTR_DIFF(p1, hexchars);
+ lonybble = PTR_DIFF(p2, hexchars);
+
+ pwd[i / 2] = (hinybble << 4) | lonybble;
+ }
+ return (True);
+}
diff --git a/source/sam/group.c b/source/sam/group.c
new file mode 100644
index 00000000000..101e3dd7ce1
--- /dev/null
+++ b/source/sam/group.c
@@ -0,0 +1,193 @@
+/*
+ Unix SMB/CIFS implementation.
+ SAM_GROUP_HANDLE /SAM_GROUP_ENUM helpers
+
+ Copyright (C) Stefan (metze) Metzmacher 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 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"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_SAM
+
+/************************************************************
+ Fill the SAM_GROUP_HANDLE with default values.
+ ***********************************************************/
+
+static void sam_fill_default_group(SAM_GROUP_HANDLE *group)
+{
+ ZERO_STRUCT(group->private); /* Don't touch the talloc context */
+
+}
+
+static void destroy_sam_group_handle_talloc(SAM_GROUP_HANDLE **group)
+{
+ if (*group) {
+
+ talloc_destroy((*group)->mem_ctx);
+ *group = NULL;
+ }
+}
+
+
+/**********************************************************************
+ Alloc memory and initialises a SAM_GROUP_HANDLE on supplied mem_ctx.
+***********************************************************************/
+
+NTSTATUS sam_init_group_talloc(TALLOC_CTX *mem_ctx, SAM_GROUP_HANDLE **group)
+{
+ SMB_ASSERT(*group != NULL);
+
+ if (!mem_ctx) {
+ DEBUG(0,("sam_init_group_talloc: mem_ctx was NULL!\n"));
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ *group=(SAM_GROUP_HANDLE *)talloc(mem_ctx, sizeof(SAM_GROUP_HANDLE));
+
+ if (*group==NULL) {
+ DEBUG(0,("sam_init_group_talloc: error while allocating memory\n"));
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ (*group)->mem_ctx = mem_ctx;
+
+ (*group)->free_fn = NULL;
+
+ sam_fill_default_group(*group);
+
+ return NT_STATUS_OK;
+}
+
+
+/*************************************************************
+ Alloc memory and initialises a struct SAM_GROUP_HANDLE.
+ ************************************************************/
+
+NTSTATUS sam_init_group(SAM_GROUP_HANDLE **group)
+{
+ TALLOC_CTX *mem_ctx;
+ NTSTATUS nt_status;
+
+ mem_ctx = talloc_init("sam internal SAM_GROUP_HANDLE allocation");
+
+ if (!mem_ctx) {
+ DEBUG(0,("sam_init_group: error while doing talloc_init()\n"));
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ if (!NT_STATUS_IS_OK(nt_status = sam_init_group_talloc(mem_ctx, group))) {
+ talloc_destroy(mem_ctx);
+ return nt_status;
+ }
+
+ (*group)->free_fn = destroy_sam_group_handle_talloc;
+
+ return NT_STATUS_OK;
+}
+
+
+/************************************************************
+ Reset the SAM_GROUP_HANDLE.
+ ***********************************************************/
+
+NTSTATUS sam_reset_group(SAM_GROUP_HANDLE *group)
+{
+ SMB_ASSERT(group != NULL);
+
+ sam_fill_default_group(group);
+
+ return NT_STATUS_OK;
+}
+
+
+/************************************************************
+ Free the SAM_GROUP_HANDLE and the member pointers.
+ ***********************************************************/
+
+NTSTATUS sam_free_group(SAM_ACCOUNT_HANDLE **group)
+{
+ SMB_ASSERT(*group != NULL);
+
+ if ((*group)->free_fn) {
+ (*group)->free_fn(group);
+ }
+
+ return NT_STATUS_OK;
+}
+
+
+/**********************************************************
+ Encode the group control bits into a string.
+ length = length of string to encode into (including terminating
+ null). length *MUST BE MORE THAN 2* !
+ **********************************************************/
+
+char *sam_encode_acct_ctrl(uint16 group_ctrl, size_t length)
+{
+ static fstring group_str;
+ size_t i = 0;
+
+ group_str[i++] = '[';
+
+ if (group_ctrl & GCB_LOCAL_GROUP ) group_str[i++] = 'L';
+ if (group_ctrl & GCB_GLOBAL_GROUP ) group_str[i++] = 'G';
+
+ for ( ; i < length - 2 ; i++ )
+ group_str[i] = ' ';
+
+ i = length - 2;
+ group_str[i++] = ']';
+ group_str[i++] = '\0';
+
+ return group_str;
+}
+
+/**********************************************************
+ Decode the group control bits from a string.
+ **********************************************************/
+
+uint16 sam_decode_group_ctrl(const char *p)
+{
+ uint16 group_ctrl = 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 'L': { group_ctrl |= GCB_LOCAL_GROUP; break; /* 'L'ocal Aliases Group. */ }
+ case 'G': { group_ctrl |= GCB_GLOBAL_GROUP; break; /* 'G'lobal Domain Group. */ }
+
+ case ' ': { break; }
+ case ':':
+ case '\n':
+ case '\0':
+ case ']':
+ default: { finished = True; }
+ }
+ }
+
+ return group_ctrl;
+}
+
diff --git a/source/sam/gums.c b/source/sam/gums.c
new file mode 100644
index 00000000000..b7191535845
--- /dev/null
+++ b/source/sam/gums.c
@@ -0,0 +1,173 @@
+/*
+ Unix SMB/CIFS implementation.
+ Grops and Users Management System initializations.
+ Copyright (C) Simo Sorce 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 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"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_SAM
+
+#define GMV_MAJOR 0
+#define GMV_MINOR 1
+
+static GUMS_FUNCTIONS *gums_backend = NULL;
+
+static struct gums_init_function_entry *backends = NULL;
+
+static void lazy_initialize_gums(void)
+{
+ static BOOL initialized = False;
+
+ if (initialized)
+ return;
+
+ static_init_gums;
+ initialized = True;
+}
+
+static struct gums_init_function_entry *gums_find_backend_entry(const char *name);
+
+NTSTATUS gums_register_module(int version, const char *name, gums_init_function init_fn)
+{
+ struct gums_init_function_entry *entry = backends;
+
+ if (version != GUMS_INTERFACE_VERSION) {
+ DEBUG(0,("Can't register gums backend!\n"
+ "You tried to register a gums module with"
+ "GUMS_INTERFACE_VERSION %d, while this version"
+ "of samba uses version %d\n", version,
+ GUMS_INTERFACE_VERSION));
+
+ return NT_STATUS_OBJECT_TYPE_MISMATCH;
+ }
+
+ if (!name || !init_fn) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ DEBUG(5,("Attempting to register gums backend %s\n", name));
+
+ /* Check for duplicates */
+ if (gums_find_backend_entry(name)) {
+ DEBUG(0,("There already is a gums backend registered"
+ "with the name %s!\n", name));
+ return NT_STATUS_OBJECT_NAME_COLLISION;
+ }
+
+ entry = smb_xmalloc(sizeof(struct gums_init_function_entry));
+ entry->name = smb_xstrdup(name);
+ entry->init_fn = init_fn;
+
+ DLIST_ADD(backends, entry);
+ DEBUG(5,("Successfully added gums backend '%s'\n", name));
+ return NT_STATUS_OK;
+}
+
+static struct gums_init_function_entry *gums_find_backend_entry(const char *name)
+{
+ struct gums_init_function_entry *entry = backends;
+
+ while (entry) {
+ if (strcmp(entry->name, name) == 0)
+ return entry;
+ entry = entry->next;
+ }
+
+ return NULL;
+}
+
+NTSTATUS gums_setup_backend(const char *backend)
+{
+
+ TALLOC_CTX *mem_ctx;
+ char *module_name = smb_xstrdup(backend);
+ char *p, *module_data = NULL;
+ struct gums_init_function_entry *entry;
+ NTSTATUS ret = NT_STATUS_UNSUCCESSFUL;
+
+ lazy_initialize_gums();
+
+ p = strchr(module_name, ':');
+ if (p) {
+ *p = 0;
+ module_data = p+1;
+ trim_string(module_data, " ", " ");
+ }
+
+ trim_string(module_name, " ", " ");
+
+ DEBUG(5,("Attempting to find a gums backend to match %s (%s)\n", backend, module_name));
+
+ entry = gums_find_backend_entry(module_name);
+
+ /* Try to find a module that contains this module */
+ if (!entry) {
+ DEBUG(2,("No builtin backend found, trying to load plugin\n"));
+ if(NT_STATUS_IS_OK(smb_probe_module("gums", module_name)) && !(entry = gums_find_backend_entry(module_name))) {
+ DEBUG(0,("Plugin is available, but doesn't register gums backend %s\n", module_name));
+ SAFE_FREE(module_name);
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+ }
+
+ /* No such backend found */
+ if(!entry) {
+ DEBUG(0,("No builtin nor plugin backend for %s found\n", module_name));
+ SAFE_FREE(module_name);
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ DEBUG(5,("Found gums backend %s\n", module_name));
+
+ /* free current functions structure if any */
+ if (gums_backend) {
+ gums_backend->free_private_data(gums_backend->private_data);
+ talloc_destroy(gums_backend->mem_ctx);
+ gums_backend = NULL;
+ }
+
+ /* allocate a new GUMS_FUNCTIONS structure and memory context */
+ mem_ctx = talloc_init("gums_backend (%s)", module_name);
+ if (!mem_ctx)
+ return NT_STATUS_NO_MEMORY;
+ gums_backend = talloc(mem_ctx, sizeof(GUMS_FUNCTIONS));
+ if (!gums_backend)
+ return NT_STATUS_NO_MEMORY;
+ gums_backend->mem_ctx = mem_ctx;
+
+ /* init the requested backend module */
+ if (NT_STATUS_IS_OK(ret = entry->init_fn(gums_backend, module_data))) {
+ DEBUG(5,("gums backend %s has a valid init\n", backend));
+ } else {
+ DEBUG(0,("gums backend %s did not correctly init (error was %s)\n", backend, nt_errstr(ret)));
+ }
+ SAFE_FREE(module_name);
+ return ret;
+}
+
+NTSTATUS get_gums_fns(GUMS_FUNCTIONS **fns)
+{
+ if (gums_backend != NULL) {
+ *fns = gums_backend;
+ return NT_STATUS_OK;
+ }
+
+ DEBUG(2, ("get_gums_fns: unable to get gums functions! backend uninitialized?\n"));
+ return NT_STATUS_UNSUCCESSFUL;
+}
diff --git a/source/sam/gums_api.c b/source/sam/gums_api.c
new file mode 100644
index 00000000000..5aafa7695f6
--- /dev/null
+++ b/source/sam/gums_api.c
@@ -0,0 +1,1426 @@
+/*
+ Unix SMB/CIFS implementation.
+ GUMS structures
+ Copyright (C) Simo Sorce 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 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"
+
+/* Functions to get/set info from a GUMS object */
+
+NTSTATUS gums_create_object(GUMS_OBJECT **obj, uint32 type)
+{
+ TALLOC_CTX *mem_ctx;
+ GUMS_OBJECT *go;
+ NTSTATUS ret;
+
+ mem_ctx = talloc_init("gums_create_object");
+ if (!mem_ctx) {
+ DEBUG(0, ("gums_create_object: Out of memory!\n"));
+ *obj = NULL;
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ go = talloc_zero(mem_ctx, sizeof(GUMS_OBJECT));
+ if (!go) {
+ DEBUG(0, ("gums_create_object: Out of memory!\n"));
+ talloc_destroy(mem_ctx);
+ *obj = NULL;
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ go->mem_ctx = mem_ctx;
+ go->type = type;
+ go->version = GUMS_OBJECT_VERSION;
+
+ switch(type) {
+ case GUMS_OBJ_DOMAIN:
+ go->domain = (GUMS_DOMAIN *)talloc_zero(mem_ctx, sizeof(GUMS_DOMAIN));
+ if (!(go->domain)) {
+ ret = NT_STATUS_NO_MEMORY;
+ DEBUG(0, ("gums_create_object: Out of memory!\n"));
+ goto error;
+ }
+
+ break;
+
+/*
+ case GUMS_OBJ_WORKSTATION_TRUST:
+ case GUMS_OBJ_SERVER_TRUST:
+ case GUMS_OBJ_DOMAIN_TRUST:
+*/
+ case GUMS_OBJ_NORMAL_USER:
+ go->user = (GUMS_USER *)talloc_zero(mem_ctx, sizeof(GUMS_USER));
+ if (!(go->user)) {
+ ret = NT_STATUS_NO_MEMORY;
+ DEBUG(0, ("gums_create_object: Out of memory!\n"));
+ goto error;
+ }
+ gums_set_user_acct_ctrl(go, ACB_NORMAL);
+ gums_set_user_hours(go, 0, NULL);
+
+ break;
+
+ case GUMS_OBJ_GROUP:
+ case GUMS_OBJ_ALIAS:
+ go->group = (GUMS_GROUP *)talloc_zero(mem_ctx, sizeof(GUMS_GROUP));
+ if (!(go->group)) {
+ ret = NT_STATUS_NO_MEMORY;
+ DEBUG(0, ("gums_create_object: Out of memory!\n"));
+ goto error;
+ }
+
+ break;
+
+ default:
+ /* TODO: throw error */
+ ret = NT_STATUS_OBJECT_TYPE_MISMATCH;
+ goto error;
+ }
+
+ *obj = go;
+ return NT_STATUS_OK;
+
+error:
+ talloc_destroy(go->mem_ctx);
+ *obj = NULL;
+ return ret;
+}
+
+NTSTATUS gums_create_privilege(GUMS_PRIVILEGE **priv)
+{
+ TALLOC_CTX *mem_ctx;
+ GUMS_PRIVILEGE *pri;
+
+ mem_ctx = talloc_init("gums_create_privilege");
+ if (!mem_ctx) {
+ DEBUG(0, ("gums_create_privilege: Out of memory!\n"));
+ *priv = NULL;
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ pri = talloc_zero(mem_ctx, sizeof(GUMS_PRIVILEGE));
+ if (!pri) {
+ DEBUG(0, ("gums_create_privilege: Out of memory!\n"));
+ talloc_destroy(mem_ctx);
+ *priv = NULL;
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ pri->mem_ctx = mem_ctx;
+ pri->version = GUMS_PRIVILEGE_VERSION;
+
+ *priv = pri;
+ return NT_STATUS_OK;
+}
+
+NTSTATUS gums_destroy_object(GUMS_OBJECT **obj)
+{
+ if (!obj || !(*obj))
+ return NT_STATUS_INVALID_PARAMETER;
+
+ if ((*obj)->mem_ctx)
+ talloc_destroy((*obj)->mem_ctx);
+ *obj = NULL;
+
+ return NT_STATUS_OK;
+}
+
+NTSTATUS gums_destroy_privilege(GUMS_PRIVILEGE **priv)
+{
+ if (!priv || !(*priv))
+ return NT_STATUS_INVALID_PARAMETER;
+
+ if ((*priv)->mem_ctx)
+ talloc_destroy((*priv)->mem_ctx);
+ *priv = NULL;
+
+ return NT_STATUS_OK;
+}
+
+void gums_reset_object(GUMS_OBJECT *go)
+{
+ go->seq_num = 0;
+ go->sid = NULL;
+ go->name = NULL;
+ go->description = NULL;
+
+ switch(go->type) {
+ case GUMS_OBJ_DOMAIN:
+ memset(go->domain, 0, sizeof(GUMS_DOMAIN));
+ break;
+
+/*
+ case GUMS_OBJ_WORKSTATION_TRUST:
+ case GUMS_OBJ_SERVER_TRUST:
+ case GUMS_OBJ_DOMAIN_TRUST:
+*/
+ case GUMS_OBJ_NORMAL_USER:
+ memset(go->user, 0, sizeof(GUMS_USER));
+ gums_set_user_acct_ctrl(go, ACB_NORMAL);
+ break;
+
+ case GUMS_OBJ_GROUP:
+ case GUMS_OBJ_ALIAS:
+ memset(go->group, 0, sizeof(GUMS_GROUP));
+ break;
+
+ default:
+ return;
+ }
+}
+
+uint32 gums_get_object_type(const GUMS_OBJECT *obj)
+{
+ if (!obj)
+ return 0;
+
+ return obj->type;
+}
+
+uint32 gums_get_object_seq_num(const GUMS_OBJECT *obj)
+{
+ if (!obj)
+ return 0;
+
+ return obj->seq_num;
+}
+
+uint32 gums_get_object_version(const GUMS_OBJECT *obj)
+{
+ if (!obj)
+ return 0;
+
+ return obj->version;
+}
+
+const SEC_DESC *gums_get_sec_desc(const GUMS_OBJECT *obj)
+{
+ if (!obj)
+ return NULL;
+
+ return obj->sec_desc;
+}
+
+const DOM_SID *gums_get_object_sid(const GUMS_OBJECT *obj)
+{
+ if (!obj)
+ return NULL;
+
+ return obj->sid;
+}
+
+const char *gums_get_object_name(const GUMS_OBJECT *obj)
+{
+ if (!obj)
+ return NULL;
+
+ return obj->name;
+}
+
+const char *gums_get_object_description(const GUMS_OBJECT *obj)
+{
+ if (!obj)
+ return NULL;
+
+ return obj->description;
+}
+
+NTSTATUS gums_set_object_seq_num(GUMS_OBJECT *obj, uint32 seq_num)
+{
+ if (!obj)
+ return NT_STATUS_INVALID_PARAMETER;
+
+ obj->seq_num = seq_num;
+ return NT_STATUS_OK;
+}
+
+NTSTATUS gums_set_object_version(GUMS_OBJECT *obj, uint32 version)
+{
+ if (!obj)
+ return NT_STATUS_INVALID_PARAMETER;
+
+ obj->version = version;
+ return NT_STATUS_OK;
+}
+
+NTSTATUS gums_set_sec_desc(GUMS_OBJECT *obj, const SEC_DESC *sec_desc)
+{
+ if (!obj || !sec_desc)
+ return NT_STATUS_INVALID_PARAMETER;
+
+ obj->sec_desc = dup_sec_desc(obj->mem_ctx, sec_desc);
+ if (!(obj->sec_desc)) return NT_STATUS_UNSUCCESSFUL;
+ return NT_STATUS_OK;
+}
+
+NTSTATUS gums_set_object_sid(GUMS_OBJECT *obj, const DOM_SID *sid)
+{
+ if (!obj || !sid)
+ return NT_STATUS_INVALID_PARAMETER;
+
+ obj->sid = sid_dup_talloc(obj->mem_ctx, sid);
+ if (!(obj->sid)) return NT_STATUS_UNSUCCESSFUL;
+ return NT_STATUS_OK;
+}
+
+NTSTATUS gums_set_object_name(GUMS_OBJECT *obj, const char *name)
+{
+ if (!obj || !name)
+ return NT_STATUS_INVALID_PARAMETER;
+
+ obj->name = (char *)talloc_strdup(obj->mem_ctx, name);
+ if (!(obj->name)) return NT_STATUS_UNSUCCESSFUL;
+ return NT_STATUS_OK;
+}
+
+NTSTATUS gums_set_object_description(GUMS_OBJECT *obj, const char *description)
+{
+ if (!obj || !description)
+ return NT_STATUS_INVALID_PARAMETER;
+
+ obj->description = (char *)talloc_strdup(obj->mem_ctx, description);
+ if (!(obj->description)) return NT_STATUS_UNSUCCESSFUL;
+ return NT_STATUS_OK;
+}
+
+/*
+NTSTATUS gums_get_object_privileges(PRIVILEGE_SET **priv_set, const GUMS_OBJECT *obj)
+{
+ if (!priv_set)
+ return NT_STATUS_INVALID_PARAMETER;
+
+ *priv_set = obj->priv_set;
+ return NT_STATUS_OK;
+}
+*/
+
+uint32 gums_get_domain_next_rid(const GUMS_OBJECT *obj)
+{
+ if (obj->type != GUMS_OBJ_DOMAIN)
+ return -1;
+
+ return obj->domain->next_rid;
+}
+
+NTSTATUS gums_set_domain_next_rid(GUMS_OBJECT *obj, uint32 rid)
+{
+ if (!obj)
+ return NT_STATUS_INVALID_PARAMETER;
+
+ if (obj->type != GUMS_OBJ_DOMAIN)
+ return NT_STATUS_OBJECT_TYPE_MISMATCH;
+
+ obj->domain->next_rid = rid;
+ return NT_STATUS_OK;
+}
+
+/* User specific functions */
+
+const DOM_SID *gums_get_user_pri_group(const GUMS_OBJECT *obj)
+{
+ if (!obj || obj->type != GUMS_OBJ_NORMAL_USER)
+ return NULL;
+
+ return obj->user->group_sid;
+}
+
+const DATA_BLOB gums_get_user_nt_pwd(const GUMS_OBJECT *obj)
+{
+ fstring p;
+
+ if (!obj || obj->type != GUMS_OBJ_NORMAL_USER)
+ return data_blob(NULL, 0);
+
+ pdb_sethexpwd(p, (unsigned char *)(obj->user->nt_pw.data), 0);
+ DEBUG(100, ("Reading NT Password=[%s]\n", p));
+
+ return obj->user->nt_pw;
+}
+
+const DATA_BLOB gums_get_user_lm_pwd(const GUMS_OBJECT *obj)
+{
+ fstring p;
+
+ if (!obj || obj->type != GUMS_OBJ_NORMAL_USER)
+ return data_blob(NULL, 0);
+
+ pdb_sethexpwd(p, (unsigned char *)(obj->user->lm_pw.data), 0);
+ DEBUG(100, ("Reading LM Password=[%s]\n", p));
+
+ return obj->user->lm_pw;
+}
+
+const char *gums_get_user_fullname(const GUMS_OBJECT *obj)
+{
+ if (!obj || obj->type != GUMS_OBJ_NORMAL_USER)
+ return NULL;
+
+ return obj->user->full_name;
+}
+
+const char *gums_get_user_homedir(const GUMS_OBJECT *obj)
+{
+ if (!obj || obj->type != GUMS_OBJ_NORMAL_USER)
+ return NULL;
+
+ return obj->user->home_dir;
+}
+
+const char *gums_get_user_dir_drive(const GUMS_OBJECT *obj)
+{
+ if (!obj || obj->type != GUMS_OBJ_NORMAL_USER)
+ return NULL;
+
+ return obj->user->dir_drive;
+}
+
+const char *gums_get_user_profile_path(const GUMS_OBJECT *obj)
+{
+ if (!obj || obj->type != GUMS_OBJ_NORMAL_USER)
+ return NULL;
+
+ return obj->user->profile_path;
+}
+
+const char *gums_get_user_logon_script(const GUMS_OBJECT *obj)
+{
+ if (!obj || obj->type != GUMS_OBJ_NORMAL_USER)
+ return NULL;
+
+ return obj->user->logon_script;
+}
+
+const char *gums_get_user_workstations(const GUMS_OBJECT *obj)
+{
+ if (!obj || obj->type != GUMS_OBJ_NORMAL_USER)
+ return NULL;
+
+ return obj->user->workstations;
+}
+
+const char *gums_get_user_unknown_str(const GUMS_OBJECT *obj)
+{
+ if (!obj || obj->type != GUMS_OBJ_NORMAL_USER)
+ return NULL;
+
+ return obj->user->unknown_str;
+}
+
+const char *gums_get_user_munged_dial(const GUMS_OBJECT *obj)
+{
+ if (!obj || obj->type != GUMS_OBJ_NORMAL_USER)
+ return NULL;
+
+ return obj->user->munged_dial;
+}
+
+NTTIME gums_get_user_logon_time(const GUMS_OBJECT *obj)
+{
+ if (!obj || obj->type != GUMS_OBJ_NORMAL_USER) {
+ NTTIME null_time;
+ init_nt_time(&null_time);
+ return null_time;
+ }
+
+ return obj->user->logon_time;
+}
+
+NTTIME gums_get_user_logoff_time(const GUMS_OBJECT *obj)
+{
+ if (!obj || obj->type != GUMS_OBJ_NORMAL_USER) {
+ NTTIME null_time;
+ init_nt_time(&null_time);
+ return null_time;
+ }
+
+ return obj->user->logoff_time;
+}
+
+NTTIME gums_get_user_kickoff_time(const GUMS_OBJECT *obj)
+{
+ if (!obj || obj->type != GUMS_OBJ_NORMAL_USER) {
+ NTTIME null_time;
+ init_nt_time(&null_time);
+ return null_time;
+ }
+
+ return obj->user->kickoff_time;
+}
+
+NTTIME gums_get_user_pass_last_set_time(const GUMS_OBJECT *obj)
+{
+ if (!obj || obj->type != GUMS_OBJ_NORMAL_USER) {
+ NTTIME null_time;
+ init_nt_time(&null_time);
+ return null_time;
+ }
+
+ return obj->user->pass_last_set_time;
+}
+
+NTTIME gums_get_user_pass_can_change_time(const GUMS_OBJECT *obj)
+{
+ if (!obj || obj->type != GUMS_OBJ_NORMAL_USER) {
+ NTTIME null_time;
+ init_nt_time(&null_time);
+ return null_time;
+ }
+
+ return obj->user->pass_can_change_time;
+}
+
+NTTIME gums_get_user_pass_must_change_time(const GUMS_OBJECT *obj)
+{
+ if (!obj || obj->type != GUMS_OBJ_NORMAL_USER) {
+ NTTIME null_time;
+ init_nt_time(&null_time);
+ return null_time;
+ }
+
+ return obj->user->pass_must_change_time;
+}
+
+uint16 gums_get_user_acct_ctrl(const GUMS_OBJECT *obj)
+{
+ if (!obj || obj->type != GUMS_OBJ_NORMAL_USER)
+ return 0;
+
+ return obj->user->acct_ctrl;
+}
+
+uint16 gums_get_user_logon_divs(const GUMS_OBJECT *obj)
+{
+ if (!obj || obj->type != GUMS_OBJ_NORMAL_USER)
+ return 0;
+
+ return obj->user->logon_divs;
+}
+
+uint32 gums_get_user_hours_len(const GUMS_OBJECT *obj)
+{
+ if (!obj || obj->type != GUMS_OBJ_NORMAL_USER)
+ return 0;
+
+ return obj->user->hours_len;
+}
+
+const uint8 *gums_get_user_hours(const GUMS_OBJECT *obj)
+{
+ if (!obj || obj->type != GUMS_OBJ_NORMAL_USER)
+ return NULL;
+
+ return obj->user->hours;
+}
+
+uint32 gums_get_user_unknown_3(const GUMS_OBJECT *obj)
+{
+ if (!obj || obj->type != GUMS_OBJ_NORMAL_USER)
+ return 0;
+
+ return obj->user->unknown_3;
+}
+
+uint16 gums_get_user_bad_password_count(const GUMS_OBJECT *obj)
+{
+ if (!obj || obj->type != GUMS_OBJ_NORMAL_USER)
+ return 0;
+
+ return obj->user->bad_password_count;
+}
+
+uint16 gums_get_user_logon_count(const GUMS_OBJECT *obj)
+{
+ if (!obj || obj->type != GUMS_OBJ_NORMAL_USER)
+ return 0;
+
+ return obj->user->logon_count;
+}
+
+uint32 gums_get_user_unknown_6(const GUMS_OBJECT *obj)
+{
+ if (!obj || obj->type != GUMS_OBJ_NORMAL_USER)
+ return 0;
+
+ return obj->user->unknown_6;
+}
+
+NTSTATUS gums_set_user_pri_group(GUMS_OBJECT *obj, const DOM_SID *sid)
+{
+ if (!obj || !sid)
+ return NT_STATUS_INVALID_PARAMETER;
+
+ if (obj->type != GUMS_OBJ_NORMAL_USER)
+ return NT_STATUS_OBJECT_TYPE_MISMATCH;
+
+ obj->user->group_sid = sid_dup_talloc(obj->mem_ctx, sid);
+ if (!(obj->user->group_sid)) return NT_STATUS_NO_MEMORY;
+ return NT_STATUS_OK;
+}
+
+NTSTATUS gums_set_user_nt_pwd(GUMS_OBJECT *obj, const DATA_BLOB nt_pwd)
+{
+ fstring p;
+ unsigned char r[16];
+
+ if (!obj)
+ return NT_STATUS_INVALID_PARAMETER;
+
+ if (obj->type != GUMS_OBJ_NORMAL_USER)
+ return NT_STATUS_OBJECT_TYPE_MISMATCH;
+
+ obj->user->nt_pw = data_blob_talloc(obj->mem_ctx, nt_pwd.data, nt_pwd.length);
+
+ memcpy(r, nt_pwd.data, 16);
+ pdb_sethexpwd(p, r, 0);
+ DEBUG(100, ("Setting NT Password=[%s]\n", p));
+
+ return NT_STATUS_OK;
+}
+
+NTSTATUS gums_set_user_lm_pwd(GUMS_OBJECT *obj, const DATA_BLOB lm_pwd)
+{
+ fstring p;
+ unsigned char r[16];
+
+ if (!obj)
+ return NT_STATUS_INVALID_PARAMETER;
+
+ if (obj->type != GUMS_OBJ_NORMAL_USER)
+ return NT_STATUS_OBJECT_TYPE_MISMATCH;
+
+ obj->user->lm_pw = data_blob_talloc(obj->mem_ctx, lm_pwd.data, lm_pwd.length);
+
+ memcpy(r, lm_pwd.data, 16);
+ pdb_sethexpwd(p, r, 0);
+ DEBUG(100, ("Setting LM Password=[%s]\n", p));
+
+ return NT_STATUS_OK;
+}
+
+NTSTATUS gums_set_user_fullname(GUMS_OBJECT *obj, const char *fullname)
+{
+ if (!obj || !fullname)
+ return NT_STATUS_INVALID_PARAMETER;
+
+ if (obj->type != GUMS_OBJ_NORMAL_USER)
+ return NT_STATUS_OBJECT_TYPE_MISMATCH;
+
+ obj->user->full_name = (char *)talloc_strdup(obj->mem_ctx, fullname);
+ if (!(obj->user->full_name)) return NT_STATUS_NO_MEMORY;
+ return NT_STATUS_OK;
+}
+
+NTSTATUS gums_set_user_homedir(GUMS_OBJECT *obj, const char *homedir)
+{
+ if (!obj || !homedir)
+ return NT_STATUS_INVALID_PARAMETER;
+
+ if (obj->type != GUMS_OBJ_NORMAL_USER)
+ return NT_STATUS_OBJECT_TYPE_MISMATCH;
+
+ obj->user->home_dir = (char *)talloc_strdup(obj->mem_ctx, homedir);
+ if (!(obj->user->home_dir)) return NT_STATUS_NO_MEMORY;
+ return NT_STATUS_OK;
+}
+
+NTSTATUS gums_set_user_dir_drive(GUMS_OBJECT *obj, const char *dir_drive)
+{
+ if (!obj || !dir_drive)
+ return NT_STATUS_INVALID_PARAMETER;
+
+ if (obj->type != GUMS_OBJ_NORMAL_USER)
+ return NT_STATUS_OBJECT_TYPE_MISMATCH;
+
+ obj->user->dir_drive = (char *)talloc_strdup(obj->mem_ctx, dir_drive);
+ if (!(obj->user->dir_drive)) return NT_STATUS_NO_MEMORY;
+ return NT_STATUS_OK;
+}
+
+NTSTATUS gums_set_user_logon_script(GUMS_OBJECT *obj, const char *logon_script)
+{
+ if (!obj || !logon_script)
+ return NT_STATUS_INVALID_PARAMETER;
+
+ if (obj->type != GUMS_OBJ_NORMAL_USER)
+ return NT_STATUS_OBJECT_TYPE_MISMATCH;
+
+ obj->user->logon_script = (char *)talloc_strdup(obj->mem_ctx, logon_script);
+ if (!(obj->user->logon_script)) return NT_STATUS_NO_MEMORY;
+ return NT_STATUS_OK;
+}
+
+NTSTATUS gums_set_user_profile_path(GUMS_OBJECT *obj, const char *profile_path)
+{
+ if (!obj || !profile_path)
+ return NT_STATUS_INVALID_PARAMETER;
+
+ if (obj->type != GUMS_OBJ_NORMAL_USER)
+ return NT_STATUS_OBJECT_TYPE_MISMATCH;
+
+ obj->user->profile_path = (char *)talloc_strdup(obj->mem_ctx, profile_path);
+ if (!(obj->user->profile_path)) return NT_STATUS_NO_MEMORY;
+ return NT_STATUS_OK;
+}
+
+NTSTATUS gums_set_user_workstations(GUMS_OBJECT *obj, const char *workstations)
+{
+ if (!obj || !workstations)
+ return NT_STATUS_INVALID_PARAMETER;
+
+ if (obj->type != GUMS_OBJ_NORMAL_USER)
+ return NT_STATUS_OBJECT_TYPE_MISMATCH;
+
+ obj->user->workstations = (char *)talloc_strdup(obj->mem_ctx, workstations);
+ if (!(obj->user->workstations)) return NT_STATUS_NO_MEMORY;
+ return NT_STATUS_OK;
+}
+
+NTSTATUS gums_set_user_unknown_str(GUMS_OBJECT *obj, const char *unknown_str)
+{
+ if (!obj || !unknown_str)
+ return NT_STATUS_INVALID_PARAMETER;
+
+ if (obj->type != GUMS_OBJ_NORMAL_USER)
+ return NT_STATUS_OBJECT_TYPE_MISMATCH;
+
+ obj->user->unknown_str = (char *)talloc_strdup(obj->mem_ctx, unknown_str);
+ if (!(obj->user->unknown_str)) return NT_STATUS_NO_MEMORY;
+ return NT_STATUS_OK;
+}
+
+NTSTATUS gums_set_user_munged_dial(GUMS_OBJECT *obj, const char *munged_dial)
+{
+ if (!obj || !munged_dial)
+ return NT_STATUS_INVALID_PARAMETER;
+
+ if (obj->type != GUMS_OBJ_NORMAL_USER)
+ return NT_STATUS_OBJECT_TYPE_MISMATCH;
+
+ obj->user->munged_dial = (char *)talloc_strdup(obj->mem_ctx, munged_dial);
+ if (!(obj->user->munged_dial)) return NT_STATUS_NO_MEMORY;
+ return NT_STATUS_OK;
+}
+
+NTSTATUS gums_set_user_logon_time(GUMS_OBJECT *obj, NTTIME logon_time)
+{
+ if (!obj)
+ return NT_STATUS_INVALID_PARAMETER;
+
+ if (obj->type != GUMS_OBJ_NORMAL_USER)
+ return NT_STATUS_OBJECT_TYPE_MISMATCH;
+
+ obj->user->logon_time = logon_time;
+ return NT_STATUS_OK;
+}
+
+NTSTATUS gums_set_user_logoff_time(GUMS_OBJECT *obj, NTTIME logoff_time)
+{
+ if (!obj)
+ return NT_STATUS_INVALID_PARAMETER;
+
+ if (obj->type != GUMS_OBJ_NORMAL_USER)
+ return NT_STATUS_OBJECT_TYPE_MISMATCH;
+
+ obj->user->logoff_time = logoff_time;
+ return NT_STATUS_OK;
+}
+
+NTSTATUS gums_set_user_kickoff_time(GUMS_OBJECT *obj, NTTIME kickoff_time)
+{
+ if (!obj)
+ return NT_STATUS_INVALID_PARAMETER;
+
+ if (obj->type != GUMS_OBJ_NORMAL_USER)
+ return NT_STATUS_OBJECT_TYPE_MISMATCH;
+
+ obj->user->kickoff_time = kickoff_time;
+ return NT_STATUS_OK;
+}
+
+NTSTATUS gums_set_user_pass_last_set_time(GUMS_OBJECT *obj, NTTIME pass_last_set_time)
+{
+ if (!obj)
+ return NT_STATUS_INVALID_PARAMETER;
+
+ if (obj->type != GUMS_OBJ_NORMAL_USER)
+ return NT_STATUS_OBJECT_TYPE_MISMATCH;
+
+ obj->user->pass_last_set_time = pass_last_set_time;
+ return NT_STATUS_OK;
+}
+
+NTSTATUS gums_set_user_pass_can_change_time(GUMS_OBJECT *obj, NTTIME pass_can_change_time)
+{
+ if (!obj)
+ return NT_STATUS_INVALID_PARAMETER;
+
+ if (obj->type != GUMS_OBJ_NORMAL_USER)
+ return NT_STATUS_OBJECT_TYPE_MISMATCH;
+
+ obj->user->pass_can_change_time = pass_can_change_time;
+ return NT_STATUS_OK;
+}
+
+NTSTATUS gums_set_user_pass_must_change_time(GUMS_OBJECT *obj, NTTIME pass_must_change_time)
+{
+ if (!obj)
+ return NT_STATUS_INVALID_PARAMETER;
+
+ if (obj->type != GUMS_OBJ_NORMAL_USER)
+ return NT_STATUS_OBJECT_TYPE_MISMATCH;
+
+ obj->user->pass_must_change_time = pass_must_change_time;
+ return NT_STATUS_OK;
+}
+
+NTSTATUS gums_set_user_acct_ctrl(GUMS_OBJECT *obj, uint16 acct_ctrl)
+{
+ if (!obj)
+ return NT_STATUS_INVALID_PARAMETER;
+
+ if (obj->type != GUMS_OBJ_NORMAL_USER)
+ return NT_STATUS_OBJECT_TYPE_MISMATCH;
+
+ obj->user->acct_ctrl = acct_ctrl;
+ return NT_STATUS_OK;
+}
+
+NTSTATUS gums_set_user_logon_divs(GUMS_OBJECT *obj, uint16 logon_divs)
+{
+ if (!obj)
+ return NT_STATUS_INVALID_PARAMETER;
+
+ if (obj->type != GUMS_OBJ_NORMAL_USER)
+ return NT_STATUS_OBJECT_TYPE_MISMATCH;
+
+ obj->user->logon_divs = logon_divs;
+ return NT_STATUS_OK;
+}
+
+NTSTATUS gums_set_user_hours(GUMS_OBJECT *obj, uint32 hours_len, const uint8 *hours)
+{
+ if (!obj || !hours)
+ return NT_STATUS_INVALID_PARAMETER;
+
+ if (obj->type != GUMS_OBJ_NORMAL_USER)
+ return NT_STATUS_OBJECT_TYPE_MISMATCH;
+
+ obj->user->hours_len = hours_len;
+ if (hours_len == 0)
+ DEBUG(10, ("gums_set_user_hours: Warning, hours_len is zero!\n"));
+
+ obj->user->hours = (uint8 *)talloc(obj->mem_ctx, MAX_HOURS_LEN);
+ if (!(obj->user->hours))
+ return NT_STATUS_NO_MEMORY;
+ if (hours_len)
+ memcpy(obj->user->hours, hours, hours_len);
+
+ return NT_STATUS_OK;
+}
+
+NTSTATUS gums_set_user_unknown_3(GUMS_OBJECT *obj, uint32 unknown_3)
+{
+ if (!obj)
+ return NT_STATUS_INVALID_PARAMETER;
+
+ if (obj->type != GUMS_OBJ_NORMAL_USER)
+ return NT_STATUS_OBJECT_TYPE_MISMATCH;
+
+ obj->user->unknown_3 = unknown_3;
+ return NT_STATUS_OK;
+}
+
+NTSTATUS gums_set_user_bad_password_count(GUMS_OBJECT *obj, uint16 bad_password_count)
+{
+ if (!obj)
+ return NT_STATUS_INVALID_PARAMETER;
+
+ if (obj->type != GUMS_OBJ_NORMAL_USER)
+ return NT_STATUS_OBJECT_TYPE_MISMATCH;
+
+ obj->user->bad_password_count = bad_password_count;
+ return NT_STATUS_OK;
+}
+
+NTSTATUS gums_set_user_logon_count(GUMS_OBJECT *obj, uint16 logon_count)
+{
+ if (!obj)
+ return NT_STATUS_INVALID_PARAMETER;
+
+ if (obj->type != GUMS_OBJ_NORMAL_USER)
+ return NT_STATUS_OBJECT_TYPE_MISMATCH;
+
+ obj->user->logon_count = logon_count;
+ return NT_STATUS_OK;
+}
+
+NTSTATUS gums_set_user_unknown_6(GUMS_OBJECT *obj, uint32 unknown_6)
+{
+ if (!obj)
+ return NT_STATUS_INVALID_PARAMETER;
+
+ if (obj->type != GUMS_OBJ_NORMAL_USER)
+ return NT_STATUS_OBJECT_TYPE_MISMATCH;
+
+ obj->user->unknown_6 = unknown_6;
+ return NT_STATUS_OK;
+}
+
+/* Group specific functions */
+
+const DOM_SID *gums_get_group_members(int *count, const GUMS_OBJECT *obj)
+{
+ if (!count || !obj || !(obj->type == GUMS_OBJ_GROUP || obj->type == GUMS_OBJ_ALIAS)) {
+ *count = -1;
+ return NULL;
+ }
+
+ *count = obj->group->count;
+ return obj->group->members;
+}
+
+NTSTATUS gums_set_group_members(GUMS_OBJECT *obj, uint32 count, DOM_SID *members)
+{
+ uint32 n;
+
+ if (!obj || ((count > 0) && !members))
+ return NT_STATUS_INVALID_PARAMETER;
+
+ if (obj->type != GUMS_OBJ_GROUP &&
+ obj->type != GUMS_OBJ_ALIAS)
+ return NT_STATUS_OBJECT_TYPE_MISMATCH;
+
+ obj->group->count = count;
+
+ if (count) {
+ obj->group->members = (DOM_SID *)talloc(obj->mem_ctx, count * sizeof(DOM_SID));
+ if (!(obj->group->members)) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+
+ n = 0;
+ do {
+ sid_copy(&(obj->group->members[n]), &(members[n]));
+ n++;
+ } while (n < count);
+ } else {
+ obj->group->members = 0;
+ }
+
+ return NT_STATUS_OK;
+}
+
+/* Privilege specific functions */
+
+const LUID_ATTR *gums_get_priv_luid_attr(const GUMS_PRIVILEGE *priv)
+{
+ if (!priv) {
+ return NULL;
+ }
+
+ return priv->privilege;
+}
+
+const DOM_SID *gums_get_priv_members(int *count, const GUMS_PRIVILEGE *priv)
+{
+ if (!count || !priv) {
+ *count = -1;
+ return NULL;
+ }
+
+ *count = priv->count;
+ return priv->members;
+}
+
+NTSTATUS gums_set_priv_luid_attr(GUMS_PRIVILEGE *priv, LUID_ATTR *luid_attr)
+{
+ if (!luid_attr || !priv)
+ return NT_STATUS_INVALID_PARAMETER;
+
+ priv->privilege = (LUID_ATTR *)talloc_memdup(priv->mem_ctx, luid_attr, sizeof(LUID_ATTR));
+ if (!(priv->privilege)) return NT_STATUS_NO_MEMORY;
+ return NT_STATUS_OK;
+}
+
+NTSTATUS gums_set_priv_members(GUMS_PRIVILEGE *priv, uint32 count, DOM_SID *members)
+{
+ uint32 n;
+
+ if (!priv || !members || !members)
+ return NT_STATUS_INVALID_PARAMETER;
+
+ priv->count = count;
+ priv->members = (DOM_SID *)talloc(priv->mem_ctx, count * sizeof(DOM_SID));
+ if (!(priv->members))
+ return NT_STATUS_NO_MEMORY;
+
+ n = 0;
+ do {
+ sid_copy(&(priv->members[n]), &(members[n]));
+ n++;
+ } while (n < count);
+
+ return NT_STATUS_OK;
+}
+
+/* data_store set functions */
+
+NTSTATUS gums_create_commit_set(GUMS_COMMIT_SET **com_set, DOM_SID *sid, uint32 type)
+{
+ TALLOC_CTX *mem_ctx;
+
+ mem_ctx = talloc_init("commit_set");
+ if (mem_ctx == NULL)
+ return NT_STATUS_NO_MEMORY;
+
+ *com_set = (GUMS_COMMIT_SET *)talloc_zero(mem_ctx, sizeof(GUMS_COMMIT_SET));
+ if (*com_set == NULL) {
+ talloc_destroy(mem_ctx);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ (*com_set)->mem_ctx = mem_ctx;
+ (*com_set)->type = type;
+ sid_copy(&((*com_set)->sid), sid);
+
+ return NT_STATUS_OK;
+}
+
+NTSTATUS gums_cs_grow_data_set(GUMS_COMMIT_SET *com_set, int size)
+{
+ GUMS_DATA_SET *data_set;
+
+ com_set->count = com_set->count + size;
+ if (com_set->count == size) { /* data set is empty*/
+ data_set = (GUMS_DATA_SET *)talloc_zero(com_set->mem_ctx, sizeof(GUMS_DATA_SET));
+ } else {
+ data_set = (GUMS_DATA_SET *)talloc_realloc(com_set->mem_ctx, com_set->data, sizeof(GUMS_DATA_SET) * com_set->count);
+ }
+ if (data_set == NULL)
+ return NT_STATUS_NO_MEMORY;
+
+ com_set->data = data_set;
+
+ return NT_STATUS_OK;
+}
+
+NTSTATUS gums_cs_set_sec_desc(GUMS_COMMIT_SET *com_set, SEC_DESC *sec_desc)
+{
+ NTSTATUS ret;
+ GUMS_DATA_SET *data_set;
+ SEC_DESC *new_sec_desc;
+
+ if (!com_set || !sec_desc)
+ return NT_STATUS_INVALID_PARAMETER;
+
+ if (!NT_STATUS_IS_OK(ret = gums_cs_grow_data_set(com_set, 1)))
+ return ret;
+
+ data_set = &((com_set->data)[com_set->count - 1]);
+
+ data_set->type = GUMS_SET_SEC_DESC;
+ new_sec_desc = dup_sec_desc(com_set->mem_ctx, sec_desc);
+ if (new_sec_desc == NULL)
+ return NT_STATUS_NO_MEMORY;
+
+ (SEC_DESC *)(data_set->data) = new_sec_desc;
+
+ return NT_STATUS_OK;
+}
+
+/*
+NTSTATUS gums_cs_add_privilege(GUMS_PRIV_COMMIT_SET *com_set, LUID_ATTR priv)
+{
+ NTSTATUS ret;
+ GUMS_DATA_SET *data_set;
+ LUID_ATTR *new_priv;
+
+ if (!com_set)
+ return NT_STATUS_INVALID_PARAMETER;
+
+ if (!NT_STATUS_OK(ret = gums_pcs_grow_data_set(com_set, 1)))
+ return ret;
+
+ data_set = ((com_set->data)[com_set->count - 1]);
+
+ data_set->type = GUMS_ADD_PRIVILEGE;
+ if (!NT_STATUS_IS_OK(ret = dupalloc_luid_attr(com_set->mem_ctx, &new_priv, priv)))
+ return ret;
+
+ (SEC_DESC *)(data_set->data) = new_priv;
+
+ return NT_STATUS_OK;
+}
+
+NTSTATUS gums_cs_del_privilege(GUMS_PRIV_COMMIT_SET *com_set, LUID_ATTR priv)
+{
+ NTSTATUS ret;
+ GUMS_DATA_SET *data_set;
+ LUID_ATTR *new_priv;
+
+ if (!com_set)
+ return NT_STATUS_INVALID_PARAMETER;
+
+ if (!NT_STATUS_OK(ret = gums_pcs_grow_data_set(com_set, 1)))
+ return ret;
+
+ data_set = ((com_set->data)[com_set->count - 1]);
+
+ data_set->type = GUMS_DEL_PRIVILEGE;
+ if (!NT_STATUS_IS_OK(ret = dupalloc_luid_attr(com_set->mem_ctx, &new_priv, priv)))
+ return ret;
+
+ (SEC_DESC *)(data_set->data) = new_priv;
+
+ return NT_STATUS_OK;
+}
+
+NTSTATUS gums_cs_set_privilege_set(GUMS_PRIV_COMMIT_SET *com_set, PRIVILEGE_SET *priv_set)
+{
+ NTSTATUS ret;
+ GUMS_DATA_SET *data_set;
+ PRIVILEGE_SET *new_priv_set;
+
+ if (!com_set || !priv_set)
+ return NT_STATUS_INVALID_PARAMETER;
+
+ if (!NT_STATUS_OK(ret = gums_pcs_grow_data_set(com_set, 1)))
+ return ret;
+
+ data_set = ((com_set->data)[com_set->count - 1]);
+
+ data_set->type = GUMS_SET_PRIVILEGE;
+ if (!NT_STATUS_IS_OK(ret = init_priv_set_with_ctx(com_set->mem_ctx, &new_priv_set)))
+ return ret;
+
+ if (!NT_STATUS_IS_OK(ret = dup_priv_set(new_priv_set, priv_set)))
+ return ret;
+
+ (SEC_DESC *)(data_set->data) = new_priv_set;
+
+ return NT_STATUS_OK;
+}
+*/
+
+NTSTATUS gums_cs_set_string(GUMS_COMMIT_SET *com_set, uint32 type, char *str)
+{
+ NTSTATUS ret;
+ GUMS_DATA_SET *data_set;
+ char *new_str;
+
+ if (!com_set || !str || type < GUMS_SET_NAME || type > GUMS_SET_MUNGED_DIAL)
+ return NT_STATUS_INVALID_PARAMETER;
+
+ if (!NT_STATUS_IS_OK(ret = gums_cs_grow_data_set(com_set, 1)))
+ return ret;
+
+ data_set = &((com_set->data)[com_set->count - 1]);
+
+ data_set->type = type;
+ new_str = talloc_strdup(com_set->mem_ctx, str);
+ if (new_str == NULL)
+ return NT_STATUS_NO_MEMORY;
+
+ (char *)(data_set->data) = new_str;
+
+ return NT_STATUS_OK;
+}
+
+NTSTATUS gums_cs_set_name(GUMS_COMMIT_SET *com_set, char *name)
+{
+ return gums_cs_set_string(com_set, GUMS_SET_NAME, name);
+}
+
+NTSTATUS gums_cs_set_description(GUMS_COMMIT_SET *com_set, char *desc)
+{
+ return gums_cs_set_string(com_set, GUMS_SET_DESCRIPTION, desc);
+}
+
+NTSTATUS gums_cs_set_full_name(GUMS_COMMIT_SET *com_set, char *full_name)
+{
+ if (com_set->type != GUMS_OBJ_NORMAL_USER)
+ return NT_STATUS_INVALID_PARAMETER;
+
+ return gums_cs_set_string(com_set, GUMS_SET_NAME, full_name);
+}
+
+NTSTATUS gums_cs_set_home_directory(GUMS_COMMIT_SET *com_set, char *home_dir)
+{
+ if (com_set->type != GUMS_OBJ_NORMAL_USER)
+ return NT_STATUS_INVALID_PARAMETER;
+
+ return gums_cs_set_string(com_set, GUMS_SET_NAME, home_dir);
+}
+
+NTSTATUS gums_cs_set_drive(GUMS_COMMIT_SET *com_set, char *drive)
+{
+ if (com_set->type != GUMS_OBJ_NORMAL_USER)
+ return NT_STATUS_INVALID_PARAMETER;
+
+ return gums_cs_set_string(com_set, GUMS_SET_NAME, drive);
+}
+
+NTSTATUS gums_cs_set_logon_script(GUMS_COMMIT_SET *com_set, char *logon_script)
+{
+ if (com_set->type != GUMS_OBJ_NORMAL_USER)
+ return NT_STATUS_INVALID_PARAMETER;
+
+ return gums_cs_set_string(com_set, GUMS_SET_NAME, logon_script);
+}
+
+NTSTATUS gums_cs_set_profile_path(GUMS_COMMIT_SET *com_set, char *prof_path)
+{
+ if (com_set->type != GUMS_OBJ_NORMAL_USER)
+ return NT_STATUS_INVALID_PARAMETER;
+
+ return gums_cs_set_string(com_set, GUMS_SET_NAME, prof_path);
+}
+
+NTSTATUS gums_cs_set_workstations(GUMS_COMMIT_SET *com_set, char *wks)
+{
+ if (com_set->type != GUMS_OBJ_NORMAL_USER)
+ return NT_STATUS_INVALID_PARAMETER;
+
+ return gums_cs_set_string(com_set, GUMS_SET_NAME, wks);
+}
+
+NTSTATUS gums_cs_set_unknown_string(GUMS_COMMIT_SET *com_set, char *unkn_str)
+{
+ if (com_set->type != GUMS_OBJ_NORMAL_USER)
+ return NT_STATUS_INVALID_PARAMETER;
+
+ return gums_cs_set_string(com_set, GUMS_SET_NAME, unkn_str);
+}
+
+NTSTATUS gums_cs_set_munged_dial(GUMS_COMMIT_SET *com_set, char *munged_dial)
+{
+ if (com_set->type != GUMS_OBJ_NORMAL_USER)
+ return NT_STATUS_INVALID_PARAMETER;
+
+ return gums_cs_set_string(com_set, GUMS_SET_NAME, munged_dial);
+}
+
+NTSTATUS gums_cs_set_nttime(GUMS_COMMIT_SET *com_set, uint32 type, NTTIME *nttime)
+{
+ NTSTATUS ret;
+ GUMS_DATA_SET *data_set;
+ NTTIME *new_time;
+
+ if (!com_set || !nttime || type < GUMS_SET_LOGON_TIME || type > GUMS_SET_PASS_MUST_CHANGE_TIME)
+ return NT_STATUS_INVALID_PARAMETER;
+
+ if (!NT_STATUS_IS_OK(ret = gums_cs_grow_data_set(com_set, 1)))
+ return ret;
+
+ data_set = &((com_set->data)[com_set->count - 1]);
+
+ data_set->type = type;
+ new_time = talloc(com_set->mem_ctx, sizeof(NTTIME));
+ if (new_time == NULL)
+ return NT_STATUS_NO_MEMORY;
+
+ new_time->low = nttime->low;
+ new_time->high = nttime->high;
+ (char *)(data_set->data) = new_time;
+
+ return NT_STATUS_OK;
+}
+
+NTSTATUS gums_cs_set_logon_time(GUMS_COMMIT_SET *com_set, NTTIME *logon_time)
+{
+ if (com_set->type != GUMS_OBJ_NORMAL_USER)
+ return NT_STATUS_INVALID_PARAMETER;
+
+ return gums_cs_set_nttime(com_set, GUMS_SET_LOGON_TIME, logon_time);
+}
+
+NTSTATUS gums_cs_set_logoff_time(GUMS_COMMIT_SET *com_set, NTTIME *logoff_time)
+{
+ if (com_set->type != GUMS_OBJ_NORMAL_USER)
+ return NT_STATUS_INVALID_PARAMETER;
+
+ return gums_cs_set_nttime(com_set, GUMS_SET_LOGOFF_TIME, logoff_time);
+}
+
+NTSTATUS gums_cs_set_kickoff_time(GUMS_COMMIT_SET *com_set, NTTIME *kickoff_time)
+{
+ if (com_set->type != GUMS_OBJ_NORMAL_USER)
+ return NT_STATUS_INVALID_PARAMETER;
+
+ return gums_cs_set_nttime(com_set, GUMS_SET_KICKOFF_TIME, kickoff_time);
+}
+
+NTSTATUS gums_cs_set_pass_last_set_time(GUMS_COMMIT_SET *com_set, NTTIME *pls_time)
+{
+ if (com_set->type != GUMS_OBJ_NORMAL_USER)
+ return NT_STATUS_INVALID_PARAMETER;
+
+ return gums_cs_set_nttime(com_set, GUMS_SET_LOGON_TIME, pls_time);
+}
+
+NTSTATUS gums_cs_set_pass_can_change_time(GUMS_COMMIT_SET *com_set, NTTIME *pcc_time)
+{
+ if (com_set->type != GUMS_OBJ_NORMAL_USER)
+ return NT_STATUS_INVALID_PARAMETER;
+
+ return gums_cs_set_nttime(com_set, GUMS_SET_LOGON_TIME, pcc_time);
+}
+
+NTSTATUS gums_cs_set_pass_must_change_time(GUMS_COMMIT_SET *com_set, NTTIME *pmc_time)
+{
+ if (com_set->type != GUMS_OBJ_NORMAL_USER)
+ return NT_STATUS_INVALID_PARAMETER;
+
+ return gums_cs_set_nttime(com_set, GUMS_SET_LOGON_TIME, pmc_time);
+}
+
+NTSTATUS gums_cs_add_sids_to_group(GUMS_COMMIT_SET *com_set, const DOM_SID **sids, const uint32 count)
+{
+ NTSTATUS ret;
+ GUMS_DATA_SET *data_set;
+ DOM_SID **new_sids;
+ int i;
+
+ if (!com_set || !sids)
+ return NT_STATUS_INVALID_PARAMETER;
+
+ if (!NT_STATUS_IS_OK(ret = gums_cs_grow_data_set(com_set, 1)))
+ return ret;
+
+ data_set = &((com_set->data)[com_set->count - 1]);
+
+ data_set->type = GUMS_ADD_SID_LIST;
+ new_sids = (DOM_SID **)talloc(com_set->mem_ctx, (sizeof(void *) * count));
+ if (new_sids == NULL)
+ return NT_STATUS_NO_MEMORY;
+ for (i = 0; i < count; i++) {
+ new_sids[i] = sid_dup_talloc(com_set->mem_ctx, sids[i]);
+ if (new_sids[i] == NULL)
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ (SEC_DESC *)(data_set->data) = new_sids;
+
+ return NT_STATUS_OK;
+}
+
+NTSTATUS gums_cs_add_users_to_group(GUMS_COMMIT_SET *com_set, const DOM_SID **sids, const uint32 count)
+{
+ if (!com_set || !sids)
+ return NT_STATUS_INVALID_PARAMETER;
+ if (com_set->type != GUMS_OBJ_GROUP || com_set->type != GUMS_OBJ_ALIAS)
+ return NT_STATUS_INVALID_PARAMETER;
+
+ return gums_cs_add_sids_to_group(com_set, sids, count);
+}
+
+NTSTATUS gums_cs_add_groups_to_group(GUMS_COMMIT_SET *com_set, const DOM_SID **sids, const uint32 count)
+{
+ if (!com_set || !sids)
+ return NT_STATUS_INVALID_PARAMETER;
+ if (com_set->type != GUMS_OBJ_ALIAS)
+ return NT_STATUS_INVALID_PARAMETER;
+
+ return gums_cs_add_sids_to_group(com_set, sids, count);
+}
+
+NTSTATUS gums_cs_del_sids_from_group(GUMS_COMMIT_SET *com_set, const DOM_SID **sids, const uint32 count)
+{
+ NTSTATUS ret;
+ GUMS_DATA_SET *data_set;
+ DOM_SID **new_sids;
+ int i;
+
+ if (!com_set || !sids)
+ return NT_STATUS_INVALID_PARAMETER;
+ if (com_set->type != GUMS_OBJ_GROUP || com_set->type != GUMS_OBJ_ALIAS)
+ return NT_STATUS_INVALID_PARAMETER;
+
+ if (!NT_STATUS_IS_OK(ret = gums_cs_grow_data_set(com_set, 1)))
+ return ret;
+
+ data_set = &((com_set->data)[com_set->count - 1]);
+
+ data_set->type = GUMS_DEL_SID_LIST;
+ new_sids = (DOM_SID **)talloc(com_set->mem_ctx, (sizeof(void *) * count));
+ if (new_sids == NULL)
+ return NT_STATUS_NO_MEMORY;
+ for (i = 0; i < count; i++) {
+ new_sids[i] = sid_dup_talloc(com_set->mem_ctx, sids[i]);
+ if (new_sids[i] == NULL)
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ (SEC_DESC *)(data_set->data) = new_sids;
+
+ return NT_STATUS_OK;
+}
+
+NTSTATUS gums_ds_set_sids_in_group(GUMS_COMMIT_SET *com_set, const DOM_SID **sids, const uint32 count)
+{
+ NTSTATUS ret;
+ GUMS_DATA_SET *data_set;
+ DOM_SID **new_sids;
+ int i;
+
+ if (!com_set || !sids)
+ return NT_STATUS_INVALID_PARAMETER;
+ if (com_set->type != GUMS_OBJ_GROUP || com_set->type != GUMS_OBJ_ALIAS)
+ return NT_STATUS_INVALID_PARAMETER;
+
+ if (!NT_STATUS_IS_OK(ret = gums_cs_grow_data_set(com_set, 1)))
+ return ret;
+
+ data_set = &((com_set->data)[com_set->count - 1]);
+
+ data_set->type = GUMS_SET_SID_LIST;
+ new_sids = (DOM_SID **)talloc(com_set->mem_ctx, (sizeof(void *) * count));
+ if (new_sids == NULL)
+ return NT_STATUS_NO_MEMORY;
+ for (i = 0; i < count; i++) {
+ new_sids[i] = sid_dup_talloc(com_set->mem_ctx, sids[i]);
+ if (new_sids[i] == NULL)
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ (SEC_DESC *)(data_set->data) = new_sids;
+
+ return NT_STATUS_OK;
+}
+
+NTSTATUS gums_commit_data(GUMS_COMMIT_SET *set)
+{
+ NTSTATUS ret;
+ GUMS_FUNCTIONS *fns;
+
+ if (!NT_STATUS_IS_OK(ret = get_gums_fns(&fns))) {
+ DEBUG(0, ("gums_commit_data: unable to get gums functions! backend uninitialized?\n"));
+ return ret;
+ }
+ return fns->set_object_values(&(set->sid), set->count, set->data);
+}
+
+NTSTATUS gums_destroy_commit_set(GUMS_COMMIT_SET **com_set)
+{
+ talloc_destroy((*com_set)->mem_ctx);
+ *com_set = NULL;
+
+ return NT_STATUS_OK;
+}
+
diff --git a/source/sam/gums_helper.c b/source/sam/gums_helper.c
new file mode 100644
index 00000000000..fcb9366cda8
--- /dev/null
+++ b/source/sam/gums_helper.c
@@ -0,0 +1,383 @@
+/*
+ Unix SMB/CIFS implementation.
+ GUMS backends helper functions
+ Copyright (C) Simo Sorce 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 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"
+
+extern DOM_SID global_sid_World;
+extern DOM_SID global_sid_Builtin;
+extern DOM_SID global_sid_Builtin_Administrators;
+extern DOM_SID global_sid_Builtin_Power_Users;
+extern DOM_SID global_sid_Builtin_Account_Operators;
+extern DOM_SID global_sid_Builtin_Server_Operators;
+extern DOM_SID global_sid_Builtin_Print_Operators;
+extern DOM_SID global_sid_Builtin_Backup_Operators;
+extern DOM_SID global_sid_Builtin_Replicator;
+extern DOM_SID global_sid_Builtin_Users;
+extern DOM_SID global_sid_Builtin_Guests;
+
+
+/* defines */
+
+#define ALLOC_CHECK(str, ptr, err, label) do { if ((ptr) == NULL) { DEBUG(0, ("%s: out of memory!\n", str)); err = NT_STATUS_NO_MEMORY; goto label; } } while(0)
+#define NTSTATUS_CHECK(err, label, str1, str2) do { if (NT_STATUS_IS_ERR(err)) { DEBUG(0, ("%s: %s\n", str1, str2)); } } while(0)
+
+/****************************************************************************
+ Check if a user is a mapped group.
+
+ This function will check if the group SID is mapped onto a
+ system managed gid or onto a winbind manged sid.
+ In the first case it will be threated like a mapped group
+ and the backend should take the member list with a getgrgid
+ and ignore any user that have been possibly set into the group
+ object.
+
+ In the second case, the group is a fully SAM managed group
+ served back to the system through winbind. In this case the
+ members of a Local group are "unrolled" to cope with the fact
+ that unix cannot contain groups inside groups.
+ The backend MUST never call any getgr* / getpw* function or
+ loops with winbind may happen.
+ ****************************************************************************/
+
+#if 0
+NTSTATUS is_mapped_group(BOOL *mapped, const DOM_SID *sid)
+{
+ NTSTATUS result;
+ gid_t id;
+
+ /* look if mapping exist, do not make idmap alloc an uid if SID is not found */
+ result = idmap_get_gid_from_sid(&id, sid, False);
+ if (NT_STATUS_IS_OK(result)) {
+ *mapped = gid_is_in_winbind_range(id);
+ } else {
+ *mapped = False;
+ }
+
+ return result;
+}
+#endif
+
+#define ALIAS_DEFAULT_SACL_SA_RIGHTS 0x01050013
+#define ALIAS_DEFAULT_DACL_SA_RIGHTS \
+ (READ_CONTROL_ACCESS | \
+ SA_RIGHT_ALIAS_LOOKUP_INFO | \
+ SA_RIGHT_ALIAS_GET_MEMBERS) /* 0x0002000c */
+
+#define ALIAS_DEFAULT_SACL_SEC_ACE_FLAG (SEC_ACE_FLAG_FAILED_ACCESS | SEC_ACE_FLAG_SUCCESSFUL_ACCESS) /* 0xc0 */
+
+
+NTSTATUS create_builtin_alias_default_sec_desc(SEC_DESC **sec_desc, TALLOC_CTX *ctx)
+{
+ DOM_SID *world = &global_sid_World;
+ DOM_SID *admins = &global_sid_Builtin_Administrators;
+ SEC_ACCESS sa;
+ SEC_ACE sacl_ace;
+ SEC_ACE dacl_aces[2];
+ SEC_ACL *sacl = NULL;
+ SEC_ACL *dacl = NULL;
+ size_t psize;
+
+ init_sec_access(&sa, ALIAS_DEFAULT_SACL_SA_RIGHTS);
+ init_sec_ace(&sacl_ace, world, SEC_ACE_TYPE_SYSTEM_AUDIT, sa, ALIAS_DEFAULT_SACL_SEC_ACE_FLAG);
+
+ sacl = make_sec_acl(ctx, NT4_ACL_REVISION, 1, &sacl_ace);
+ if (!sacl) {
+ DEBUG(0, ("build_init_sec_desc: Failed to make SEC_ACL.\n"));
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ init_sec_access(&sa, ALIAS_DEFAULT_DACL_SA_RIGHTS);
+ init_sec_ace(&(dacl_aces[0]), world, SEC_ACE_TYPE_ACCESS_ALLOWED, sa, 0);
+ init_sec_access(&sa, SA_RIGHT_ALIAS_ALL_ACCESS);
+ init_sec_ace(&(dacl_aces[1]), admins, SEC_ACE_TYPE_ACCESS_ALLOWED, sa, 0);
+
+ dacl = make_sec_acl(ctx, NT4_ACL_REVISION, 2, dacl_aces);
+ if (!sacl) {
+ DEBUG(0, ("build_init_sec_desc: Failed to make SEC_ACL.\n"));
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ *sec_desc = make_sec_desc(ctx, SEC_DESC_REVISION, SEC_DESC_SELF_RELATIVE, admins, admins, sacl, dacl, &psize);
+ if (!(*sec_desc)) {
+ DEBUG(0,("get_share_security: Failed to make SEC_DESC.\n"));
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ return NT_STATUS_OK;
+}
+
+NTSTATUS sec_desc_add_ace_to_dacl(SEC_DESC *sec_desc, TALLOC_CTX *ctx, DOM_SID *sid, uint32 mask)
+{
+ NTSTATUS result;
+ SEC_ACE *new_aces;
+ unsigned num_aces;
+ int i;
+
+ num_aces = sec_desc->dacl->num_aces + 1;
+ result = sec_ace_add_sid(ctx, &new_aces, sec_desc->dacl->ace, &num_aces, sid, mask);
+ if (NT_STATUS_IS_OK(result)) {
+ sec_desc->dacl->ace = new_aces;
+ sec_desc->dacl->num_aces = num_aces;
+ sec_desc->dacl->size = SEC_ACL_HEADER_SIZE;
+ for (i = 0; i < num_aces; i++) {
+ sec_desc->dacl->size += sec_desc->dacl->ace[i].size;
+ }
+ }
+ return result;
+}
+
+NTSTATUS gums_make_domain(DOM_SID *sid, const char *name, const char *description)
+{
+ NTSTATUS ret;
+ GUMS_OBJECT *go;
+ GUMS_FUNCTIONS *fns;
+
+ if (!NT_STATUS_IS_OK(ret = get_gums_fns(&fns)))
+ return ret;
+
+ if (!NT_STATUS_IS_OK(ret = gums_create_object(&go, GUMS_OBJ_DOMAIN)))
+ return ret;
+
+ ret = gums_set_object_sid(go, sid);
+ NTSTATUS_CHECK(ret, done, "gums_make_alias", "unable to set sid!");
+
+ ret = gums_set_object_name(go, name);
+ NTSTATUS_CHECK(ret, done, "gums_make_alias", "unable to set name!");
+
+ if (description) {
+ ret = gums_set_object_description(go, description);
+ NTSTATUS_CHECK(ret, done, "gums_make_alias", "unable to set description!");
+ }
+
+ /* make security descriptor * /
+ ret = create_builtin_alias_default_sec_desc(&((*go).sec_desc), (*go).mem_ctx);
+ NTSTATUS_CHECK(ret, error, "gums_init_backend", "create_builtin_alias_default_sec_desc");
+ */
+
+ ret = fns->set_object(go);
+
+ gums_destroy_object(&go);
+ return ret;
+}
+
+NTSTATUS gums_make_alias(DOM_SID *sid, const char *name, const char *description)
+{
+ NTSTATUS ret;
+ GUMS_OBJECT *go;
+ GUMS_FUNCTIONS *fns;
+
+ if (!NT_STATUS_IS_OK(ret = get_gums_fns(&fns)))
+ return ret;
+
+ if (!NT_STATUS_IS_OK(ret = gums_create_object(&go, GUMS_OBJ_ALIAS)))
+ return ret;
+
+ ret = gums_set_object_sid(go, sid);
+ NTSTATUS_CHECK(ret, done, "gums_make_alias", "unable to set sid!");
+
+ ret = gums_set_object_name(go, name);
+ NTSTATUS_CHECK(ret, done, "gums_make_alias", "unable to set name!");
+
+ if (description) {
+ ret = gums_set_object_description(go, description);
+ NTSTATUS_CHECK(ret, done, "gums_make_alias", "unable to set description!");
+ }
+
+ /* make security descriptor * /
+ ret = create_builtin_alias_default_sec_desc(&((*go).sec_desc), (*go).mem_ctx);
+ NTSTATUS_CHECK(ret, error, "gums_init_backend", "create_builtin_alias_default_sec_desc");
+ */
+
+ ret = fns->set_object(go);
+
+ gums_destroy_object(&go);
+ return ret;
+}
+
+NTSTATUS gums_init_domain(DOM_SID *sid, const char *name, const char * description)
+{
+ NTSTATUS ret;
+
+ /* Add the weelknown Builtin Domain */
+ if (!NT_STATUS_IS_OK(ret = gums_make_domain(
+ sid,
+ name,
+ description
+ ))) {
+ return ret;
+ }
+
+ /* Add default users and groups */
+ /* Administrator
+ Guest
+ Domain Administrators
+ Domain Users
+ Domain Guests
+ */
+
+ return ret;
+}
+
+NTSTATUS gums_init_builtin_domain(void)
+{
+ NTSTATUS ret;
+
+ generate_wellknown_sids();
+
+ /* Add the weelknown Builtin Domain */
+ if (!NT_STATUS_IS_OK(ret = gums_make_domain(
+ &global_sid_Builtin,
+ "BUILTIN",
+ "Builtin Domain"
+ ))) {
+ return ret;
+ }
+
+ /* Add the well known Builtin Local Groups */
+
+ /* Administrators */
+ if (!NT_STATUS_IS_OK(ret = gums_make_alias(
+ &global_sid_Builtin_Administrators,
+ "Administrators",
+ "Members can fully administer the computer/domain"
+ ))) {
+ return ret;
+ }
+ /* Administrator privilege set */
+ /* From BDC join trace:
+ SeSecurityPrivilege, SeBackupPrivilege, SeRestorePrivilege,
+ SeSystemtimePrivilege, SeShutdownPrivilege,
+ SeRemoteShutdownPrivilege, SeTakeOwnershipPrivilege,
+ SeDebugPrivilege, SeSystemEnvironmentPrivilege,
+ SeSystemProfilePrivilege, SeProfileSingleProcessPrivilege,
+ SeIncreaseBasePriorityPrivilege, SeLocalDriverPrivilege,
+ SeCreatePagefilePrivilege, SeIncreaseQuotaPrivilege
+ */
+
+ /* Power Users */
+ /* Domain Controllers Does NOT have Power Users (?) */
+ if (!NT_STATUS_IS_OK(ret = gums_make_alias(
+ &global_sid_Builtin_Power_Users,
+ "Power Users",
+ "Power Users"
+ ))) {
+ return ret;
+ }
+
+ /* Power Users privilege set */
+ /* (?) */
+
+ /* Account Operators */
+ if (!NT_STATUS_IS_OK(ret = gums_make_alias(
+ &global_sid_Builtin_Account_Operators,
+ "Account Operators",
+ "Members can administer domain user and group accounts"
+ ))) {
+ return ret;
+ }
+
+ /* make privilege set */
+ /* From BDC join trace:
+ SeShutdownPrivilege
+ */
+
+ /* Server Operators */
+ if (!NT_STATUS_IS_OK(ret = gums_make_alias(
+ &global_sid_Builtin_Server_Operators,
+ "Server Operators",
+ "Members can administer domain servers"
+ ))) {
+ return ret;
+ }
+
+ /* make privilege set */
+ /* From BDC join trace:
+ SeBackupPrivilege, SeRestorePrivilege, SeSystemtimePrivilege,
+ SeShutdownPrivilege, SeRemoteShutdownPrivilege
+ */
+
+ /* Print Operators */
+ if (!NT_STATUS_IS_OK(ret = gums_make_alias(
+ &global_sid_Builtin_Print_Operators,
+ "Print Operators",
+ "Members can administer domain printers"
+ ))) {
+ return ret;
+ }
+
+ /* make privilege set */
+ /* From BDC join trace:
+ SeShutdownPrivilege
+ */
+
+ /* Backup Operators */
+ if (!NT_STATUS_IS_OK(ret = gums_make_alias(
+ &global_sid_Builtin_Backup_Operators,
+ "Backup Operators",
+ "Members can bypass file security to backup files"
+ ))) {
+ return ret;
+ }
+
+ /* make privilege set */
+ /* From BDC join trace:
+ SeBackupPrivilege, SeRestorePrivilege, SeShutdownPrivilege
+ */
+
+ /* Replicator */
+ if (!NT_STATUS_IS_OK(ret = gums_make_alias(
+ &global_sid_Builtin_Replicator,
+ "Replicator",
+ "Supports file replication in a domain"
+ ))) {
+ return ret;
+ }
+
+ /* make privilege set */
+ /* From BDC join trace:
+ SeBackupPrivilege, SeRestorePrivilege, SeShutdownPrivilege
+ */
+
+ /* Users */
+ if (!NT_STATUS_IS_OK(ret = gums_make_alias(
+ &global_sid_Builtin_Users,
+ "Users",
+ "Ordinary users"
+ ))) {
+ return ret;
+ }
+
+ /* Users specific ACEs * /
+ sec_desc_add_ace_to_dacl(go->sec_desc, go->mem_ctx, &global_sid_Builtin_Account_Operators, ALIAS_DEFAULT_DACL_SA_RIGHTS);
+ sec_desc_add_ace_to_dacl(go->sec_desc, go->mem_ctx, &global_sid_Builtin_Power_Users, ALIAS_DEFAULT_DACL_SA_RIGHTS);
+ */
+
+ /* Guests */
+ if (!NT_STATUS_IS_OK(ret = gums_make_alias(
+ &global_sid_Builtin_Guests,
+ "Guests",
+ "Users granted guest access to the computer/domain"
+ ))) {
+ return ret;
+ }
+
+ return ret;
+}
+
diff --git a/source/sam/gums_tdbsam2.c b/source/sam/gums_tdbsam2.c
new file mode 100644
index 00000000000..7fb9a1a997f
--- /dev/null
+++ b/source/sam/gums_tdbsam2.c
@@ -0,0 +1,1220 @@
+/*
+ * Unix SMB/CIFS implementation.
+ * tdbsam2 - sam backend
+ * Copyright (C) Simo Sorce 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 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 "tdbsam2_parse_info.h"
+
+#if 0
+static int gums_tdbsam2_debug_class = DBGC_ALL;
+#endif
+/*
+#undef DBGC_CLASS
+#define DBGC_CLASS gums_tdbsam2_debug_class
+*/
+
+#define TDBSAM_VERSION 20021215
+#define TDB_FILE_NAME "tdbsam2.tdb"
+#define NAMEPREFIX "NAME_"
+#define SIDPREFIX "SID_"
+#define PRIVILEGEPREFIX "PRIV_"
+
+#define TDB_BASIC_OBJ_STRING "ddd"
+#define TDB_FORMAT_STRING "dddB"
+#define TDB_PRIV_FORMAT_STRING "ddB"
+
+#define TALLOC_CHECK(ptr, err, label) do { if ((ptr) == NULL) { DEBUG(0, ("%s: Out of memory!\n", FUNCTION_MACRO)); err = NT_STATUS_NO_MEMORY; goto label; } } while(0)
+#define SET_OR_FAIL(func, label) do { if (!NT_STATUS_IS_OK(func)) { DEBUG(0, ("%s: Setting gums object data failed!\n", FUNCTION_MACRO)); goto label; } } while(0)
+
+
+
+struct tdbsam2_enum_objs {
+ uint32 type;
+ DOM_SID *dom_sid;
+ TDB_CONTEXT *db;
+ TDB_DATA key;
+ struct tdbsam2_enum_objs *next;
+};
+
+struct tdbsam2_private_data {
+
+ const char *storage;
+ struct tdbsam2_enum_objs *teo_handlers;
+};
+
+static struct tdbsam2_private_data *ts2_privs;
+
+static NTSTATUS init_object_from_buffer(GUMS_OBJECT **go, char *buffer, int size)
+{
+
+ NTSTATUS ret = NT_STATUS_UNSUCCESSFUL;
+ TALLOC_CTX *mem_ctx;
+ int iret;
+ char *obj_data = NULL;
+ int data_size = 0;
+ int version, type, seqnum;
+ int len;
+
+ mem_ctx = talloc_init("init_object_from_buffer");
+ if (!mem_ctx) {
+ DEBUG(0, ("init_object_from_buffer: Out of memory!\n"));
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ len = tdb_unpack (buffer, size, TDB_FORMAT_STRING,
+ &version,
+ &type,
+ &seqnum,
+ &data_size, &obj_data);
+
+ if (len == -1 || data_size <= 0)
+ goto done;
+
+ /* version is checked inside this function so that backward
+ compatibility code can be called eventually.
+ This way we can easily handle database format upgrades */
+ if (version != TDBSAM_VERSION) {
+ DEBUG(3,("init_object_from_buffer: Error, db object has wrong tdbsam version!\n"));
+ goto done;
+ }
+
+ /* be sure the string is terminated before trying to parse it */
+ if (obj_data[data_size - 1] != '\0')
+ obj_data[data_size - 1] = '\0';
+
+ *go = (GUMS_OBJECT *)talloc_zero(mem_ctx, sizeof(GUMS_OBJECT));
+ TALLOC_CHECK(*go, ret, done);
+
+ switch (type) {
+
+ case GUMS_OBJ_DOMAIN:
+ iret = gen_parse(mem_ctx, pinfo_gums_domain, (char *)(*go), obj_data);
+ break;
+
+ case GUMS_OBJ_GROUP:
+ case GUMS_OBJ_ALIAS:
+ iret = gen_parse(mem_ctx, pinfo_gums_group, (char *)(*go), obj_data);
+ break;
+
+ case GUMS_OBJ_NORMAL_USER:
+ iret = gen_parse(mem_ctx, pinfo_gums_user, (char *)(*go), obj_data);
+ break;
+
+ default:
+ DEBUG(3,("init_object_from_buffer: Error, wrong object type number!\n"));
+ goto done;
+ }
+
+ if (iret != 0) {
+ DEBUG(0, ("init_object_from_buffer: Fatal Error! Unable to parse object!\n"));
+ DEBUG(0, ("init_object_from_buffer: DB Corrupt ?"));
+ goto done;
+ }
+
+ (*go)->mem_ctx = mem_ctx;
+
+ ret = NT_STATUS_OK;
+done:
+ SAFE_FREE(obj_data);
+ return ret;
+}
+
+static NTSTATUS init_privilege_from_buffer(GUMS_PRIVILEGE **priv, char *buffer, int size)
+{
+
+ NTSTATUS ret = NT_STATUS_UNSUCCESSFUL;
+ TALLOC_CTX *mem_ctx;
+ int iret;
+ char *obj_data = NULL;
+ int data_size = 0;
+ int version, seqnum;
+ int len;
+
+ mem_ctx = talloc_init("init_privilege_from_buffer");
+ if (!mem_ctx) {
+ DEBUG(0, ("init_privilege_from_buffer: Out of memory!\n"));
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ len = tdb_unpack (buffer, size, TDB_PRIV_FORMAT_STRING,
+ &version,
+ &seqnum,
+ &data_size, &obj_data);
+
+ if (len == -1 || data_size <= 0)
+ goto done;
+
+ /* version is checked inside this function so that backward
+ compatibility code can be called eventually.
+ This way we can easily handle database format upgrades */
+ if (version != TDBSAM_VERSION) {
+ DEBUG(3,("init_privilege_from_buffer: Error, db object has wrong tdbsam version!\n"));
+ goto done;
+ }
+
+ /* be sure the string is terminated before trying to parse it */
+ if (obj_data[data_size - 1] != '\0')
+ obj_data[data_size - 1] = '\0';
+
+ *priv = (GUMS_PRIVILEGE *)talloc_zero(mem_ctx, sizeof(GUMS_PRIVILEGE));
+ TALLOC_CHECK(*priv, ret, done);
+
+ iret = gen_parse(mem_ctx, pinfo_gums_privilege, (char *)(*priv), obj_data);
+
+ if (iret != 0) {
+ DEBUG(0, ("init_privilege_from_buffer: Fatal Error! Unable to parse object!\n"));
+ DEBUG(0, ("init_privilege_from_buffer: DB Corrupt ?"));
+ goto done;
+ }
+
+ (*priv)->mem_ctx = mem_ctx;
+
+ ret = NT_STATUS_OK;
+done:
+ SAFE_FREE(obj_data);
+ return ret;
+}
+
+static NTSTATUS init_buffer_from_object(char **buffer, size_t *len, TALLOC_CTX *mem_ctx, GUMS_OBJECT *object)
+{
+
+ NTSTATUS ret;
+ char *genbuf = NULL;
+ size_t buflen;
+
+ if (!buffer)
+ return NT_STATUS_INVALID_PARAMETER;
+
+ switch (gums_get_object_type(object)) {
+
+ case GUMS_OBJ_DOMAIN:
+ genbuf = gen_dump(mem_ctx, pinfo_gums_domain, (char *)object, 0);
+ break;
+
+ case GUMS_OBJ_GROUP:
+ case GUMS_OBJ_ALIAS:
+ genbuf = gen_dump(mem_ctx, pinfo_gums_group, (char *)object, 0);
+ break;
+
+ case GUMS_OBJ_NORMAL_USER:
+ genbuf = gen_dump(mem_ctx, pinfo_gums_user, (char *)object, 0);
+ break;
+
+ default:
+ DEBUG(3,("init_buffer_from_object: Error, wrong object type number!\n"));
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ if (genbuf == NULL) {
+ DEBUG(0, ("init_buffer_from_object: Fatal Error! Unable to dump object!\n"));
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ buflen = tdb_pack(NULL, 0, TDB_FORMAT_STRING,
+ TDBSAM_VERSION,
+ object->type,
+ object->seq_num,
+ strlen(genbuf) + 1, genbuf);
+
+ *buffer = talloc(mem_ctx, buflen);
+ TALLOC_CHECK(*buffer, ret, done);
+
+ *len = tdb_pack(*buffer, buflen, TDB_FORMAT_STRING,
+ TDBSAM_VERSION,
+ object->type,
+ object->seq_num,
+ strlen(genbuf) + 1, genbuf);
+
+ if (*len != buflen) {
+ DEBUG(0, ("init_buffer_from_object: something odd is going on here: bufflen (%d) != len (%d) in tdb_pack operations!\n",
+ buflen, *len));
+ *buffer = NULL;
+ ret = NT_STATUS_UNSUCCESSFUL;
+ goto done;
+ }
+
+ ret = NT_STATUS_OK;
+done:
+ return ret;
+}
+
+static NTSTATUS init_buffer_from_privilege(char **buffer, size_t *len, TALLOC_CTX *mem_ctx, GUMS_PRIVILEGE *priv)
+{
+
+ NTSTATUS ret;
+ char *genbuf = NULL;
+ size_t buflen;
+
+ if (!buffer || !len || !mem_ctx || !priv)
+ return NT_STATUS_INVALID_PARAMETER;
+
+ genbuf = gen_dump(mem_ctx, pinfo_gums_privilege, (char *)priv, 0);
+
+ if (genbuf == NULL) {
+ DEBUG(0, ("init_buffer_from_privilege: Fatal Error! Unable to dump object!\n"));
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ buflen = tdb_pack(NULL, 0, TDB_PRIV_FORMAT_STRING,
+ TDBSAM_VERSION,
+ priv->seq_num,
+ strlen(genbuf) + 1, genbuf);
+
+ *buffer = talloc(mem_ctx, buflen);
+ TALLOC_CHECK(*buffer, ret, done);
+
+ *len = tdb_pack(*buffer, buflen, TDB_PRIV_FORMAT_STRING,
+ TDBSAM_VERSION,
+ priv->seq_num,
+ strlen(genbuf) + 1, genbuf);
+
+ if (*len != buflen) {
+ DEBUG(0, ("init_buffer_from_privilege: something odd is going on here: bufflen (%d) != len (%d) in tdb_pack operations!\n",
+ buflen, *len));
+ *buffer = NULL;
+ ret = NT_STATUS_UNSUCCESSFUL;
+ goto done;
+ }
+
+ ret = NT_STATUS_OK;
+done:
+ return ret;
+}
+
+static NTSTATUS opentdb(TDB_CONTEXT **tdb, BOOL readonly)
+{
+ if (!tdb)
+ return NT_STATUS_INVALID_PARAMETER;
+
+ *tdb = tdb_open_log(ts2_privs->storage, 0, TDB_DEFAULT, readonly?(O_RDONLY):(O_RDWR | O_CREAT), 0600);
+ if (!(*tdb))
+ {
+ DEBUG(0, ("opentdb: Unable to open database (%s)!\n", ts2_privs->storage));
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS get_object_by_sid(TDB_CONTEXT *tdb, GUMS_OBJECT **obj, const DOM_SID *sid)
+{
+ NTSTATUS ret;
+ TDB_DATA data, key;
+ fstring keystr;
+
+ if (!obj || !sid)
+ return NT_STATUS_INVALID_PARAMETER;
+
+ slprintf(keystr, sizeof(keystr)-1, "%s%s", SIDPREFIX, sid_string_static(sid));
+ key.dptr = keystr;
+ key.dsize = strlen(keystr) + 1;
+
+ data = tdb_fetch(tdb, key);
+ if (!data.dptr) {
+ DEBUG(5, ("get_object_by_sid: Entry not found!\n"));
+ DEBUGADD(5, (" Error: %s\n", tdb_errorstr(tdb)));
+ DEBUGADD(5, (" Key: %s\n", keystr));
+ ret = NT_STATUS_NOT_FOUND;
+ goto done;
+ }
+
+ if (!NT_STATUS_IS_OK(init_object_from_buffer(obj, data.dptr, data.dsize))) {
+ DEBUG(0, ("get_object_by_sid: Error fetching database, malformed entry!\n"));
+ ret = NT_STATUS_UNSUCCESSFUL;
+ goto done;
+ }
+
+ ret = NT_STATUS_OK;
+
+done:
+ SAFE_FREE(data.dptr);
+ return ret;
+}
+
+static NTSTATUS make_full_object_name(TDB_CONTEXT *tdb, fstring objname, GUMS_OBJECT *object)
+{
+ NTSTATUS ret;
+
+ objname[0] = '\0';
+
+ if (gums_get_object_type(object) == GUMS_OBJ_DOMAIN) {
+
+ fstrcpy(objname, gums_get_object_name(object));
+
+ } else {
+ GUMS_OBJECT *domain_object;
+ DOM_SID domain_sid;
+ uint32 *discard_rid;
+
+ sid_copy(&domain_sid, gums_get_object_sid(object));
+ sid_split_rid(&domain_sid, discard_rid);
+
+ if (!NT_STATUS_IS_OK(get_object_by_sid(tdb,
+ &domain_object,
+ &domain_sid))) {
+
+ DEBUG(3, ("Object's domain not found!\n"));
+ ret = NT_STATUS_UNSUCCESSFUL;
+ goto done;
+ }
+
+ fstrcpy(objname, gums_get_object_name(domain_object));
+ fstrcat(objname, "\\");
+ fstrcat(objname, gums_get_object_name(object));
+ }
+
+ ret = NT_STATUS_OK;
+
+done:
+ return ret;
+}
+
+/* name should be in DOMAIN\NAME format */
+static NTSTATUS get_object_by_name(TDB_CONTEXT *tdb, GUMS_OBJECT **obj, const char *fullname)
+{
+
+ NTSTATUS ret = NT_STATUS_OK;
+ TDB_DATA data, key;
+ fstring keystr;
+ fstring objname;
+ DOM_SID sid;
+ fstring sidstr;
+ int sidstr_len;
+
+ if (!obj || !fullname)
+ return NT_STATUS_INVALID_PARAMETER;
+
+ /* Data is stored in all lower-case */
+ fstrcpy(objname, fullname);
+ strlower_m(objname);
+
+ slprintf(keystr, sizeof(keystr)-1, "%s%s", NAMEPREFIX, objname);
+
+ key.dptr = keystr;
+ key.dsize = strlen(keystr) + 1;
+
+ data = tdb_fetch(tdb, key);
+ if (!data.dptr) {
+ DEBUG(5, ("get_object_by_name: Entry not found!\n"));
+ DEBUGADD(5, (" Error: %s\n", tdb_errorstr(tdb)));
+ DEBUGADD(5, (" Key: %s\n", keystr));
+ ret = NT_STATUS_NOT_FOUND;
+ goto done;
+ }
+
+ fstrcpy(sidstr, data.dptr);
+ sidstr_len = data.dsize;
+
+ SAFE_FREE(data.dptr);
+
+ if (sidstr_len <= 0) {
+ DEBUG(5, ("get_object_by_name: Error unpacking database object!\n"));
+ ret = NT_STATUS_UNSUCCESSFUL;
+ goto done;
+ }
+
+ if (!string_to_sid(&sid, sidstr)) {
+ DEBUG(5, ("get_object_by_name: Error invalid sid string found in database object!\n"));
+ ret = NT_STATUS_UNSUCCESSFUL;
+ goto done;
+ }
+
+done:
+ if (NT_STATUS_IS_OK(ret))
+ return get_object_by_sid(tdb, obj, &sid);
+ return ret;
+}
+
+/* Get object's sequence number */
+
+static NTSTATUS get_object_seq_num(TDB_CONTEXT *tdb, GUMS_OBJECT *object, int *seq_num)
+{
+
+ NTSTATUS ret = NT_STATUS_UNSUCCESSFUL;
+ TDB_DATA data, key;
+ fstring keystr;
+ fstring sidstr;
+ int version, type, seqnum;
+
+ if (!object || !seq_num)
+ return NT_STATUS_INVALID_PARAMETER;
+
+ fstrcpy(sidstr, sid_string_static(gums_get_object_sid(object)));
+ slprintf(keystr, sizeof(keystr)-1, "%s%s", SIDPREFIX, sidstr);
+
+ key.dptr = keystr;
+ key.dsize = strlen(keystr) + 1;
+
+ data = tdb_fetch(tdb, key);
+ if (!data.dptr) {
+ DEBUG(5, ("get_object_seq_num: Entry not found!\n"));
+ DEBUGADD(5, (" Error: %s\n", tdb_errorstr(tdb)));
+ DEBUGADD(5, (" Key: %s\n", keystr));
+ ret = NT_STATUS_NOT_FOUND;
+ goto done;
+ }
+
+ if (tdb_unpack (data.dptr, data.dsize, TDB_BASIC_OBJ_STRING, &version, &type, &seqnum) == -1)
+ goto done;
+
+ *seq_num = seqnum;
+ ret = NT_STATUS_OK;
+
+done:
+ SAFE_FREE(data.dptr);
+ return ret;
+}
+
+/* store a gums object
+ * flag: TDB_REPLACE or TDB_MODIFY or TDB_INSERT
+ */
+
+static NTSTATUS store_object(TDB_CONTEXT *tdb, GUMS_OBJECT *object, int flag)
+{
+ NTSTATUS ret = NT_STATUS_OK;
+ TDB_DATA data, data2, key, key2;
+ TALLOC_CTX *mem_ctx;
+ fstring keystr;
+ fstring sidstr;
+ fstring namestr;
+ fstring objname;
+ int r;
+
+ /* TODO: on object renaming/replacing this function should
+ * check name->sid record and delete the old one
+ */
+
+ mem_ctx = talloc_init("store_object");
+ if (!mem_ctx) {
+ DEBUG(0, ("store_object: Out of memory!\n"));
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ make_full_object_name(tdb, objname, object);
+
+ /* Data is stored in all lower-case */
+ strlower_m(objname);
+
+ if (flag == TDB_MODIFY) {
+ if (!NT_STATUS_IS_OK(ret = get_object_seq_num(tdb, object, &(object->seq_num))))
+ goto done;
+ object->seq_num += 1;
+ }
+
+ if (!NT_STATUS_IS_OK(ret = init_buffer_from_object(&(data.dptr), &(data.dsize), mem_ctx, object)))
+ goto done;
+
+ fstrcpy(sidstr, sid_string_static(gums_get_object_sid(object)));
+ slprintf(keystr, sizeof(keystr) - 1, "%s%s", SIDPREFIX, sidstr);
+ slprintf(namestr, sizeof(namestr) - 1, "%s%s", NAMEPREFIX, objname);
+
+ key.dptr = keystr;
+ key.dsize = strlen(keystr) + 1;
+
+ if ((r = tdb_store(tdb, key, data, flag)) != TDB_SUCCESS) {
+ DEBUG(0, ("store_object: Unable to modify TDBSAM!\n"));
+ DEBUGADD(0, (" Error: %s", tdb_errorstr(tdb)));
+ DEBUGADD(0, (" occured while storing sid record (%s)\n", keystr));
+ if (r == TDB_ERR_EXISTS)
+ ret = NT_STATUS_UNSUCCESSFUL;
+ else
+ ret = NT_STATUS_INTERNAL_DB_ERROR;
+ goto done;
+ }
+
+ data2.dptr = sidstr;
+ data2.dsize = strlen(sidstr) + 1;
+ key2.dptr = namestr;
+ key2.dsize = strlen(namestr) + 1;
+
+ if ((r = tdb_store(tdb, key2, data2, flag)) != TDB_SUCCESS) {
+ DEBUG(0, ("store_object: Unable to modify TDBSAM!\n"));
+ DEBUGADD(0, (" Error: %s", tdb_errorstr(tdb)));
+ DEBUGADD(0, (" occured while storing name record (%s)\n", keystr));
+ DEBUGADD(0, (" attempting rollback operation.\n"));
+ if ((tdb_delete(tdb, key)) != TDB_SUCCESS) {
+ DEBUG(0, ("store_object: Unable to rollback! Check database consitency!\n"));
+ }
+ if (r == TDB_ERR_EXISTS)
+ ret = NT_STATUS_UNSUCCESSFUL;
+ else
+ ret = NT_STATUS_INTERNAL_DB_ERROR;
+ goto done;
+ }
+
+/* TODO: update the general database counter */
+/* TODO: update this entry counter too */
+
+done:
+ talloc_destroy(mem_ctx);
+ return ret;
+}
+
+/* GUMM object functions */
+
+static NTSTATUS tdbsam2_get_domain_sid(DOM_SID *sid, const char* name)
+{
+
+ NTSTATUS ret;
+ TDB_CONTEXT *tdb;
+ GUMS_OBJECT *go;
+ fstring domname;
+
+ if (!sid || !name)
+ return NT_STATUS_INVALID_PARAMETER;
+
+ if (!NT_STATUS_IS_OK(ret = opentdb(&tdb, True))) {
+ return ret;
+ }
+
+ /* Data is stored in all lower-case */
+ fstrcpy(domname, name);
+ strlower_m(domname);
+
+ if (!NT_STATUS_IS_OK(ret = get_object_by_name(tdb, &go, domname))) {
+ go = NULL;
+ DEBUG(0, ("tdbsam2_get_domain_sid: Error fetching database!\n"));
+ goto done;
+ }
+
+ if (gums_get_object_type(go) != GUMS_OBJ_DOMAIN) {
+ DEBUG(5, ("tdbsam2_get_domain_sid: Requested object is not a domain!\n"));
+ ret = NT_STATUS_OBJECT_TYPE_MISMATCH;
+ goto done;
+ }
+
+ sid_copy(sid, gums_get_object_sid(go));
+
+ ret = NT_STATUS_OK;
+
+done:
+ if (go)
+ gums_destroy_object(&go);
+ tdb_close(tdb);
+ return ret;
+}
+
+static NTSTATUS get_next_sid(TDB_CONTEXT *tdb, DOM_SID *sid)
+{
+ NTSTATUS ret;
+ GUMS_OBJECT *go;
+ DOM_SID dom_sid;
+ TDB_DATA dom_sid_key;
+ fstring dom_sid_str;
+ uint32 new_rid;
+
+ /* Find the domain SID */
+ if (!NT_STATUS_IS_OK(tdbsam2_get_domain_sid(&dom_sid, global_myname()))) {
+ DEBUG(0, ("get_next_sid: cannot found the domain sid!!\n"));
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ /* Lock the domain record */
+ sid_to_string(dom_sid_str, &dom_sid);
+ dom_sid_key.dptr = dom_sid_str;
+ dom_sid_key.dsize = strlen(dom_sid_key.dptr) + 1;
+
+ if(tdb_chainlock(tdb, dom_sid_key) != 0) {
+ DEBUG(0, ("get_next_sid: unable to lock domain record!\n"));
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ /* Get the domain object */
+ ret = get_object_by_sid(tdb, &go, &dom_sid);
+ if (!NT_STATUS_IS_OK(ret)) {
+ DEBUG(0, ("get_next_sid: unable to get root Domain object!\n"));
+ ret = NT_STATUS_INTERNAL_DB_ERROR;
+ goto done;
+ }
+
+ new_rid = gums_get_domain_next_rid(go);
+
+ /* Increment the RID Counter */
+ gums_set_domain_next_rid(go, new_rid+1);
+
+ /* Store back Domain object */
+ ret = store_object(tdb, go, TDB_MODIFY);
+ if (!NT_STATUS_IS_OK(ret)) {
+ DEBUG(0, ("get_next_sid: unable to update root Domain object!\n"));
+ ret = NT_STATUS_INTERNAL_DB_ERROR;
+ goto done;
+ }
+
+ /* Build the Domain SID to return */
+ sid_copy(sid, &dom_sid);
+
+ if (!sid_append_rid(sid, new_rid)) {
+ DEBUG(0, ("get_next_sid: unable to build new SID !?!\n"));
+ ret = NT_STATUS_UNSUCCESSFUL;
+ goto done;
+ }
+
+ ret = NT_STATUS_OK;
+
+done:
+ /* Unlock the Domain object */
+ tdb_chainunlock(tdb, dom_sid_key);
+
+ return ret;
+}
+
+/* TODO */
+ NTSTATUS (*get_sequence_number) (void);
+
+
+extern DOM_SID global_sid_NULL;
+
+static NTSTATUS tdbsam2_new_object(DOM_SID *sid, const char *name, const int obj_type)
+{
+
+ NTSTATUS ret = NT_STATUS_OK;
+ TDB_CONTEXT *tdb;
+ GUMS_OBJECT *go;
+ NTTIME null_time;
+ DATA_BLOB pw;
+ const char *defpw = "NOPASSWORDXXXXXX";
+ uint8 defhours[21] = {255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255};
+
+ if (!name) {
+ DEBUG(0, ("tdbsam2_new_object: no NULL pointers are accepted here!\n"));
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ if (!NT_STATUS_IS_OK(ret = opentdb(&tdb, False))) {
+ return ret;
+ }
+
+ if (!NT_STATUS_IS_OK(ret = gums_create_object(&go, obj_type))) {
+ go = NULL;
+ goto done;
+ }
+
+ if (obj_type == GUMS_OBJ_DOMAIN) {
+ sid_copy(sid, get_global_sam_sid());
+ } else {
+ if (!NT_STATUS_IS_OK(ret = get_next_sid(tdb, sid)))
+ goto done;
+ }
+
+ gums_set_object_sid(go, sid);
+ gums_set_object_name(go, name);
+ gums_set_object_seq_num(go, 1);
+
+ /*obj.domain->sec_desc*/
+
+ switch (obj_type) {
+ case GUMS_OBJ_NORMAL_USER:
+
+ init_nt_time(&null_time);
+
+ gums_set_user_logon_time(go, null_time);
+ gums_set_user_logoff_time(go, null_time);
+ gums_set_user_kickoff_time(go, null_time);
+ gums_set_user_pass_last_set_time(go, null_time);
+ gums_set_user_pass_can_change_time(go, null_time);
+ gums_set_user_pass_must_change_time(go, null_time);
+
+ pw = data_blob(defpw, NT_HASH_LEN);
+ gums_set_user_nt_pwd(go, pw);
+ gums_set_user_lm_pwd(go, pw);
+ data_blob_free(&pw);
+
+ gums_set_user_logon_divs(go, 168);
+ gums_set_user_hours(go, 21, defhours);
+
+ gums_set_user_bad_password_count(go, 0);
+ gums_set_user_logon_count(go, 0);
+ gums_set_user_unknown_6(go, 0x000004ec);
+ break;
+
+ case GUMS_OBJ_GROUP:
+ case GUMS_OBJ_ALIAS:
+
+ break;
+
+ case GUMS_OBJ_DOMAIN:
+
+ gums_set_domain_next_rid(go, 0x3e9);
+
+ break;
+
+ default:
+ ret = NT_STATUS_OBJECT_TYPE_MISMATCH;
+ goto done;
+ }
+
+ ret = store_object(tdb, go, TDB_INSERT);
+
+done:
+ if (go)
+ gums_destroy_object(&go);
+ tdb_close(tdb);
+ return ret;
+}
+
+/* TODO: handle privileges objects */
+
+static NTSTATUS tdbsam2_delete_object(const DOM_SID *sid)
+{
+ /* TODO: need to address privilege deletion */
+ NTSTATUS ret = NT_STATUS_OK;
+ TDB_CONTEXT *tdb;
+ GUMS_OBJECT *go;
+ TDB_DATA data, key;
+ fstring keystr;
+
+ if (!sid) {
+ DEBUG(0, ("tdbsam2_delete_object: no NULL pointers are accepted here!\n"));
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ if (!NT_STATUS_IS_OK(ret = opentdb(&tdb, False))) {
+ return ret;
+ }
+
+ slprintf(keystr, sizeof(keystr) - 1, "%s%s", SIDPREFIX, sid_string_static(sid));
+ key.dptr = keystr;
+ key.dsize = strlen(keystr) + 1;
+
+ data = tdb_fetch(tdb, key);
+ if (!data.dptr) {
+ DEBUG(5, ("tdbsam2_delete_object: Error fetching database, SID entry not found!\n"));
+ DEBUGADD(5, (" Error: %s\n", tdb_errorstr(tdb)));
+ DEBUGADD(5, (" Key: %s\n", keystr));
+ ret = NT_STATUS_UNSUCCESSFUL;
+ goto done;
+ }
+
+ if (tdb_delete(tdb, key) != TDB_SUCCESS) {
+ DEBUG(5, ("tdbsam2_delete_object: Error deleting object!\n"));
+ DEBUGADD(5, (" Error: %s\n", tdb_errorstr(tdb)));
+ DEBUGADD(5, (" Key: %s\n", keystr));
+ ret = NT_STATUS_UNSUCCESSFUL;
+ goto done;
+ }
+
+ if (!NT_STATUS_IS_OK(init_object_from_buffer(&go, data.dptr, data.dsize))) {
+ DEBUG(0, ("tdbsam2_delete_object: Error fetching database, malformed entry!\n"));
+ ret = NT_STATUS_UNSUCCESSFUL;
+ goto done;
+ }
+
+ slprintf(keystr, sizeof(keystr) - 1, "%s%s", NAMEPREFIX, gums_get_object_name(go));
+
+ key.dptr = keystr;
+ key.dsize = strlen(keystr) + 1;
+
+ if (tdb_delete(tdb, key) != TDB_SUCCESS) {
+ DEBUG(5, ("tdbsam2_delete_object: Error deleting object!\n"));
+ DEBUGADD(5, (" Error: %s\n", tdb_errorstr(tdb)));
+ DEBUGADD(5, (" Key: %s\n", keystr));
+ ret = NT_STATUS_UNSUCCESSFUL;
+ goto done;
+ }
+
+/* TODO: update the general database counter */
+
+done:
+ gums_destroy_object(&go);
+ SAFE_FREE(data.dptr);
+ return ret;
+}
+
+static NTSTATUS tdbsam2_get_object_from_sid(GUMS_OBJECT **object, const DOM_SID *sid, const int obj_type)
+{
+ NTSTATUS ret;
+ TDB_CONTEXT *tdb;
+
+ if (!object || !sid) {
+ DEBUG(0, ("tdbsam2_get_object_from_sid: no NULL pointers are accepted here!\n"));
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ if (!NT_STATUS_IS_OK(ret = opentdb(&tdb, True))) {
+ return ret;
+ }
+
+ ret = get_object_by_sid(tdb, object, sid);
+ if (!NT_STATUS_IS_OK(ret)) {
+ DEBUG(0, ("tdbsam2_get_object_from_sid: %s\n", nt_errstr(ret)));
+ goto error;
+ }
+ if (obj_type && gums_get_object_type(*object) != obj_type) {
+ DEBUG(0, ("tdbsam2_get_object_from_sid: the object is not of the rerquested type!\n"));
+ goto error;
+ }
+
+ tdb_close(tdb);
+ return NT_STATUS_OK;
+
+error:
+ gums_destroy_object(object);
+ tdb_close(tdb);
+ return ret;
+}
+
+static NTSTATUS tdbsam2_get_object_from_name(GUMS_OBJECT **object, const char *domain, const char *name, const int obj_type)
+{
+ NTSTATUS ret;
+ TDB_CONTEXT *tdb;
+ fstring objname;
+
+ if (!object || !name) {
+ DEBUG(0, ("tdbsam2_get_object_from_name: no NULL pointers are accepted here!\n"));
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ if (!NT_STATUS_IS_OK(ret = opentdb(&tdb, True))) {
+ return ret;
+ }
+
+ if (obj_type == GUMS_OBJ_DOMAIN) {
+ fstrcpy(objname, name);
+ } else {
+ if (!domain) {
+ domain = global_myname();
+ }
+ fstrcpy(objname, domain);
+ fstrcat(objname, "\\");
+ fstrcat(objname, name);
+ }
+
+ *object = NULL;
+ ret = get_object_by_name(tdb, object, name);
+ if (!NT_STATUS_IS_OK(ret)) {
+ DEBUG(0, ("tdbsam2_get_object_from_name: %s\n", nt_errstr(ret)));
+ goto error;
+ }
+ if (obj_type && gums_get_object_type(*object) != obj_type) {
+ DEBUG(0, ("tdbsam2_get_object_from_name: the object is not of the rerquested type!\n"));
+ goto error;
+ }
+
+ tdb_close(tdb);
+ return NT_STATUS_OK;
+
+error:
+ gums_destroy_object(object);
+ tdb_close(tdb);
+ return ret;
+}
+
+ /* This function is used to get the list of all objects changed since base_time, it is
+ used to support PDC<->BDC synchronization */
+ NTSTATUS (*get_updated_objects) (GUMS_OBJECT **objects, const NTTIME base_time);
+
+static NTSTATUS tdbsam2_enumerate_objects_start(void **handle, const DOM_SID *sid, const int obj_type)
+{
+ struct tdbsam2_enum_objs *teo, *t;
+
+ teo = (struct tdbsam2_enum_objs *)malloc(sizeof(struct tdbsam2_enum_objs));
+ if (!teo) {
+ DEBUG(0, ("tdbsam2_enumerate_objects_start: Out of Memory!\n"));
+ return NT_STATUS_NO_MEMORY;
+ }
+ memset(teo, 0, sizeof(struct tdbsam2_enum_objs));
+
+ teo->type = obj_type;
+ if (sid) {
+ teo->dom_sid = (DOM_SID *)malloc(sizeof(DOM_SID));
+ if (!teo->dom_sid) {
+ DEBUG(0, ("tdbsam2_enumerate_objects_start: Out of Memory!\n"));
+ return NT_STATUS_NO_MEMORY;
+ }
+ sid_copy(teo->dom_sid, sid);
+ }
+
+ if (!NT_STATUS_IS_OK(opentdb(&(teo->db), True)))
+ {
+ DEBUG(0, ("tdbsam2_enumerate_objects_start: Unable to open database (%s)!\n", ts2_privs->storage));
+ SAFE_FREE(teo);
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ if (!ts2_privs->teo_handlers) {
+ ts2_privs->teo_handlers = teo;
+ } else {
+ t = ts2_privs->teo_handlers;
+ while (t->next) {
+ t = t->next;
+ }
+ t->next = teo;
+ }
+
+ *handle = teo;
+
+ teo->key = tdb_firstkey(teo->db);
+
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS tdbsam2_enumerate_objects_get_next(GUMS_OBJECT **object, void *handle)
+{
+ NTSTATUS ret;
+ TDB_DATA data;
+ struct tdbsam2_enum_objs *teo;
+ const char *prefix = SIDPREFIX;
+ const int preflen = strlen(prefix);
+ fstring dom_sid_str;
+ int dom_sid_str_len = 0;
+
+ if (!object || !handle) {
+ DEBUG(0, ("tdbsam2_get_object_from_sid: no NULL pointers are accepted here!\n"));
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ teo = (struct tdbsam2_enum_objs *)handle;
+
+ if (teo->dom_sid) {
+ sid_to_string(dom_sid_str, teo->dom_sid);
+ dom_sid_str_len = strlen(dom_sid_str);
+ }
+
+ while ((teo->key.dptr != NULL)) {
+ int len, version, type, size, seqnum;
+ char *ptr;
+
+ if (strncmp(teo->key.dptr, prefix, preflen)) {
+ teo->key = tdb_nextkey(teo->db, teo->key);
+ continue;
+ }
+
+ if (dom_sid_str_len != 0) {
+ if (strncmp(&(teo->key.dptr[preflen]), dom_sid_str, dom_sid_str_len)) {
+ teo->key = tdb_nextkey(teo->db, teo->key);
+ continue;
+ }
+ }
+
+ data = tdb_fetch(teo->db, teo->key);
+ if (!data.dptr) {
+ DEBUG(5, ("tdbsam2_enumerate_objects_get_next: Error fetching database, SID entry not found!\n"));
+ DEBUGADD(5, (" Error: %s\n", tdb_errorstr(teo->db)));
+ DEBUGADD(5, (" Key: %s\n", teo->key.dptr));
+ ret = NT_STATUS_UNSUCCESSFUL;
+ goto done;
+ }
+
+ len = tdb_unpack (data.dptr, data.dsize, TDB_FORMAT_STRING,
+ &version,
+ &type,
+ &seqnum,
+ &size, &ptr);
+
+ if (len == -1) {
+ DEBUG(5, ("tdbsam2_enumerate_objects_get_next: Error unable to unpack data!\n"));
+ ret = NT_STATUS_UNSUCCESSFUL;
+ goto done;
+ }
+ SAFE_FREE(ptr);
+
+ if (teo->type && type != teo->type) {
+ SAFE_FREE(data.dptr);
+ data.dsize = 0;
+ teo->key = tdb_nextkey(teo->db, teo->key);
+ continue;
+ }
+
+ break;
+ }
+
+ if (teo->key.dptr == NULL) { /* no more objs */
+ ret = NT_STATUS_NO_MORE_ENTRIES;
+ goto done;
+ }
+
+ if (!NT_STATUS_IS_OK(ret = init_object_from_buffer(object, data.dptr, data.dsize))) {
+ SAFE_FREE(data.dptr);
+ DEBUG(0, ("tdbsam2_enumerate_objects_get_next: Error fetching database, malformed entry!\n"));
+ ret = NT_STATUS_UNSUCCESSFUL;
+ goto done;
+ }
+ SAFE_FREE(data.dptr);
+
+ /* prepare next run */
+ teo->key = tdb_nextkey(teo->db, teo->key);
+
+done:
+ return ret;
+}
+
+static NTSTATUS tdbsam2_enumerate_objects_stop(void *handle)
+{
+ struct tdbsam2_enum_objs *teo, *t, *p;
+
+ teo = (struct tdbsam2_enum_objs *)handle;
+
+ if (ts2_privs->teo_handlers == teo) {
+ ts2_privs->teo_handlers = teo->next;
+ } else {
+ t = ts2_privs->teo_handlers;
+ while (t != teo) {
+ p = t;
+ t = t->next;
+ if (t == NULL) {
+ DEBUG(0, ("tdbsam2_enumerate_objects_stop: Error, handle not found!\n"));
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+ }
+ p = t->next;
+ }
+
+ tdb_close(teo->db);
+ SAFE_FREE(teo->dom_sid);
+ SAFE_FREE(teo);
+
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS tdbsam2_set_object(GUMS_OBJECT *go)
+{
+ NTSTATUS ret;
+ TDB_CONTEXT *tdb;
+
+ if (!go)
+ return NT_STATUS_INVALID_PARAMETER;
+
+ if (!NT_STATUS_IS_OK(ret = opentdb(&tdb, False))) {
+ return ret;
+ }
+
+ ret = store_object(tdb, go, TDB_REPLACE);
+
+ tdb_close(tdb);
+ return ret;
+}
+
+#if 0
+ /* set object values function */
+static NTSTATUS (*set_object_values) (DOM_SID *sid, uint32 count, GUMS_DATA_SET *data_set);
+
+ /* Group related functions */
+static NTSTATUS (*add_memberss_to_group) (const DOM_SID *group, const DOM_SID **members);
+ NTSTATUS (*delete_members_from_group) (const DOM_SID *group, const DOM_SID **members);
+static NTSTATUS (*enumerate_group_members) (DOM_SID **members, const DOM_SID *sid, const int type);
+
+static NTSTATUS (*get_sid_groups) (DOM_SID **groups, const DOM_SID *sid);
+
+static NTSTATUS (*lock_sid) (const DOM_SID *sid);
+static NTSTATUS (*unlock_sid) (const DOM_SID *sid);
+
+ /* privileges related functions */
+
+static NTSTATUS (*get_privilege) (GUMS_OBJECT **object, const char *name);
+static NTSTATUS (*add_members_to_privilege) (const char *name, const DOM_SID **members);
+static NTSTATUS (*delete_members_from_privilege) (const char *name, const DOM_SID **members);
+static NTSTATUS (*enumerate_privilege_members) (const char *name, DOM_SID **members);
+static NTSTATUS (*get_sid_privileges) (const DOM_SID *sid, const char **privs);
+
+ /* warning!: set_privilege will overwrite a prior existing privilege if such exist */
+static NTSTATUS (*set_privilege) (GUMS_PRIVILEGE *priv);
+#endif
+
+static void free_tdbsam2_private_data(void **vp)
+{
+ struct tdbsam2_private_data **tdb_privs = (struct tdbsam2_private_data **)vp;
+ while (ts2_privs->teo_handlers)
+ tdbsam2_enumerate_objects_stop(ts2_privs->teo_handlers);
+ *tdb_privs = NULL;
+ /* No need to free any further, as it is talloc()ed */
+}
+
+static NTSTATUS init_tdbsam2(GUMS_FUNCTIONS *fns, const char *storage)
+{
+ NTSTATUS ret;
+ TDB_CONTEXT *tdb;
+ DOM_SID dom_sid;
+
+ fns->name = talloc_strdup(fns->mem_ctx, "tdbsam2");
+
+ fns->get_domain_sid = tdbsam2_get_domain_sid;
+ /* fns->get_sequence_number = tdbsam2_get_sequence_number; */
+ fns->new_object = tdbsam2_new_object;
+ fns->delete_object = tdbsam2_delete_object;
+ fns->get_object_from_sid = tdbsam2_get_object_from_sid;
+ fns->get_object_from_name = tdbsam2_get_object_from_name;
+ /* fns->get_updated_objects = tdbsam2_get_updated_objects; */
+ fns->enumerate_objects_start = tdbsam2_enumerate_objects_start;
+ fns->enumerate_objects_get_next = tdbsam2_enumerate_objects_get_next;
+ fns->enumerate_objects_stop = tdbsam2_enumerate_objects_stop;
+ fns->set_object = tdbsam2_set_object;
+ /* fns->set_object_values = tdbsam2_set_object_values;
+ fns->add_members_to_group = tdbsam2_add_members_to_group;
+ fns->delete_members_from_group = tdbsam2_delete_members_from_group;
+ fns->enumerate_group_members = tdbsam2_enumerate_group_members;
+ fns->get_sid_groups = tdbsam2_get_sid_groups;
+ fns->lock_sid = tdbsam2_lock_sid;
+ fns->unlock_sid = tdbsam2_unlock_sid;
+ fns->get_privilege = tdbsam2_get_privilege;
+ fns->add_members_to_privilege = tdbsam2_add_members_to_privilege;
+ fns->delete_members_from_privilege = tdbsam2_delete_members_from_privilege;
+ fns->enumerate_privilege_members = tdbsam2_enumerate_privilege_members;
+ fns->get_sid_privileges = tdbsam2_get_sid_privileges;
+ fns->set_privilege = tdbsam2_set_privilege; */
+
+ ts2_privs = talloc_zero(fns->mem_ctx, sizeof(struct tdbsam2_private_data));
+ if (!ts2_privs) {
+ DEBUG(0, ("talloc() failed for tdbsam2 private_data!\n"));
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ if (storage) {
+ ts2_privs->storage = talloc_strdup(fns->mem_ctx, storage);
+ } else {
+ pstring tdbfile;
+ get_private_directory(tdbfile);
+ pstrcat(tdbfile, "/");
+ pstrcat(tdbfile, TDB_FILE_NAME);
+ ts2_privs->storage = talloc_strdup(fns->mem_ctx, tdbfile);
+ }
+
+ /* check tdb exist (or create it) */
+
+ /* Find the domain SID */
+ if (!NT_STATUS_IS_OK(tdbsam2_get_domain_sid(&dom_sid, global_myname()))) {
+ /* db file does not exist or it is not inited */
+ /* make the tdb file */
+ if (!NT_STATUS_IS_OK(ret = opentdb(&tdb, False))) {
+ return ret;
+ }
+ tdb_close(tdb);
+
+ if (!NT_STATUS_IS_OK(tdbsam2_get_domain_sid(&dom_sid, "BUILTIN"))) {
+ gums_init_builtin_domain();
+ }
+
+ gums_init_domain(get_global_sam_sid(), global_myname(), "The Domain");
+ }
+
+ fns->private_data = &ts2_privs;
+ fns->free_private_data = free_tdbsam2_private_data;
+
+ return NT_STATUS_OK;
+}
+
+NTSTATUS gums_tdbsam2_init(void)
+{
+ /*
+ if ((gums_tdbsam2_debug_class = debug_add_class("gums_tdbsam2")) == -1) {
+ DEBUG(0, ("gums_tdbsam2: unable to register my own debug class! going on ...\n"));
+ gums_tdbsam2_debug_class = DBGC_ALL;
+ }
+ */
+ return gums_register_module(GUMS_INTERFACE_VERSION, "tdbsam2", init_tdbsam2);
+}
diff --git a/source/sam/idmap.c b/source/sam/idmap.c
new file mode 100644
index 00000000000..4d8b768c2fa
--- /dev/null
+++ b/source/sam/idmap.c
@@ -0,0 +1,317 @@
+/*
+ Unix SMB/CIFS implementation.
+ ID Mapping
+ Copyright (C) Tim Potter 2000
+ Copyright (C) Jim McDonough <jmcd@us.ibm.com> 2003
+ Copyright (C) Simo Sorce 2003
+ Copyright (C) Jeremy Allison 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 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"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_IDMAP
+
+struct idmap_function_entry {
+ const char *name;
+ struct idmap_methods *methods;
+ struct idmap_function_entry *prev,*next;
+};
+
+static struct idmap_function_entry *backends = NULL;
+
+static struct idmap_methods *cache_map;
+static struct idmap_methods *remote_map;
+
+/**********************************************************************
+ Get idmap methods. Don't allow tdb to be a remote method.
+**********************************************************************/
+
+static struct idmap_methods *get_methods(const char *name, BOOL cache_method)
+{
+ struct idmap_function_entry *entry = backends;
+
+ for(entry = backends; entry; entry = entry->next) {
+ if (!cache_method && strequal(entry->name, "tdb"))
+ continue; /* tdb is only cache method. */
+ if (strequal(entry->name, name))
+ return entry->methods;
+ }
+
+ return NULL;
+}
+
+/**********************************************************************
+ Allow a module to register itself as a method.
+**********************************************************************/
+
+NTSTATUS smb_register_idmap(int version, const char *name, struct idmap_methods *methods)
+{
+ struct idmap_function_entry *entry;
+
+ if ((version != SMB_IDMAP_INTERFACE_VERSION)) {
+ DEBUG(0, ("smb_register_idmap: Failed to register idmap module.\n"
+ "The module was compiled against SMB_IDMAP_INTERFACE_VERSION %d,\n"
+ "current SMB_IDMAP_INTERFACE_VERSION is %d.\n"
+ "Please recompile against the current version of samba!\n",
+ version, SMB_IDMAP_INTERFACE_VERSION));
+ return NT_STATUS_OBJECT_TYPE_MISMATCH;
+ }
+
+ if (!name || !name[0] || !methods) {
+ DEBUG(0,("smb_register_idmap: called with NULL pointer or empty name!\n"));
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ if (get_methods(name, False)) {
+ DEBUG(0,("smb_register_idmap: idmap module %s already registered!\n", name));
+ return NT_STATUS_OBJECT_NAME_COLLISION;
+ }
+
+ entry = smb_xmalloc(sizeof(struct idmap_function_entry));
+ entry->name = smb_xstrdup(name);
+ entry->methods = methods;
+
+ DLIST_ADD(backends, entry);
+ DEBUG(5, ("smb_register_idmap: Successfully added idmap backend '%s'\n", name));
+ return NT_STATUS_OK;
+}
+
+/**********************************************************************
+ Initialise idmap cache and a remote backend (if configured).
+**********************************************************************/
+
+BOOL idmap_init(const char *remote_backend)
+{
+ if (!backends)
+ static_init_idmap;
+
+ if (!cache_map) {
+ cache_map = get_methods("tdb", True);
+
+ if (!cache_map) {
+ DEBUG(0, ("idmap_init: could not find tdb cache backend!\n"));
+ return False;
+ }
+
+ if (!NT_STATUS_IS_OK(cache_map->init( NULL ))) {
+ DEBUG(0, ("idmap_init: could not initialise tdb cache backend!\n"));
+ return False;
+ }
+ }
+
+ if (!remote_map && remote_backend && *remote_backend != 0) {
+ char *rem_backend = smb_xstrdup(remote_backend);
+ fstring params = "";
+ char *pparams;
+
+ /* get any mode parameters passed in */
+
+ if ( (pparams = strchr( rem_backend, ':' )) != NULL ) {
+ *pparams = '\0';
+ pparams++;
+ fstrcpy( params, pparams );
+ }
+
+ DEBUG(3, ("idmap_init: using '%s' as remote backend\n", rem_backend));
+
+ if((remote_map = get_methods(rem_backend, False)) ||
+ (NT_STATUS_IS_OK(smb_probe_module("idmap", rem_backend)) &&
+ (remote_map = get_methods(rem_backend, False)))) {
+ remote_map->init(params);
+ } else {
+ DEBUG(0, ("idmap_init: could not load remote backend '%s'\n", rem_backend));
+ SAFE_FREE(rem_backend);
+ return False;
+ }
+ SAFE_FREE(rem_backend);
+ }
+
+ return True;
+}
+
+/**************************************************************************
+ This is a rare operation, designed to allow an explicit mapping to be
+ set up for a sid to a POSIX id.
+**************************************************************************/
+
+NTSTATUS idmap_set_mapping(const DOM_SID *sid, unid_t id, int id_type)
+{
+ struct idmap_methods *map = remote_map;
+ DOM_SID tmp_sid;
+
+ DEBUG(10, ("idmap_set_mapping: Set %s to %s %lu\n",
+ sid_string_static(sid),
+ ((id_type & ID_TYPEMASK) == ID_USERID) ? "UID" : "GID",
+ ((id_type & ID_TYPEMASK) == ID_USERID) ? (unsigned long)id.uid :
+ (unsigned long)id.gid));
+
+ if ( (NT_STATUS_IS_OK(cache_map->
+ get_sid_from_id(&tmp_sid, id,
+ id_type | ID_QUERY_ONLY))) &&
+ sid_equal(sid, &tmp_sid) ) {
+ /* Nothing to do, we already have that mapping */
+ DEBUG(10, ("idmap_set_mapping: Mapping already there\n"));
+ return NT_STATUS_OK;
+ }
+
+ if (map == NULL) {
+ /* Ok, we don't have a authoritative remote
+ mapping. So update our local cache only. */
+ map = cache_map;
+ }
+
+ return map->set_mapping(sid, id, id_type);
+}
+
+/**************************************************************************
+ Get ID from SID. This can create a mapping for a SID to a POSIX id.
+**************************************************************************/
+
+NTSTATUS idmap_get_id_from_sid(unid_t *id, int *id_type, const DOM_SID *sid)
+{
+ NTSTATUS ret;
+ int loc_type;
+
+ loc_type = *id_type;
+
+ if (remote_map) {
+ /* We have a central remote idmap so only look in
+ cache, don't allocate */
+ loc_type |= ID_QUERY_ONLY;
+ }
+
+ ret = cache_map->get_id_from_sid(id, &loc_type, sid);
+
+ if (NT_STATUS_IS_OK(ret)) {
+ *id_type = loc_type & ID_TYPEMASK;
+ return NT_STATUS_OK;
+ }
+
+ if (remote_map == NULL) {
+ return ret;
+ }
+
+ /* Ok, the mapping was not in the cache, give the remote map a
+ second try. */
+
+ ret = remote_map->get_id_from_sid(id, id_type, sid);
+
+ if (NT_STATUS_IS_OK(ret)) {
+ /* The remote backend gave us a valid mapping, cache it. */
+ ret = cache_map->set_mapping(sid, *id, *id_type);
+ }
+
+ return ret;
+}
+
+/**************************************************************************
+ Get SID from ID. This must have been created before.
+**************************************************************************/
+
+NTSTATUS idmap_get_sid_from_id(DOM_SID *sid, unid_t id, int id_type)
+{
+ NTSTATUS ret;
+ int loc_type;
+
+ loc_type = id_type;
+ if (remote_map) {
+ loc_type = id_type | ID_QUERY_ONLY;
+ }
+
+ ret = cache_map->get_sid_from_id(sid, id, loc_type);
+
+ if (NT_STATUS_IS_OK(ret))
+ return ret;
+
+ if (remote_map == NULL)
+ return ret;
+
+ /* We have a second chance, ask our authoritative backend */
+
+ ret = remote_map->get_sid_from_id(sid, id, id_type);
+
+ if (NT_STATUS_IS_OK(ret)) {
+ /* The remote backend gave us a valid mapping, cache it. */
+ ret = cache_map->set_mapping(sid, id, id_type);
+ }
+
+ return ret;
+}
+
+/**************************************************************************
+ Alloocate a new UNIX uid/gid
+**************************************************************************/
+
+NTSTATUS idmap_allocate_id(unid_t *id, int id_type)
+{
+ /* we have to allocate from the authoritative backend */
+
+ if ( remote_map )
+ return remote_map->allocate_id( id, id_type );
+
+ return cache_map->allocate_id( id, id_type );
+}
+
+/**************************************************************************
+ Alloocate a new RID
+**************************************************************************/
+
+NTSTATUS idmap_allocate_rid(uint32 *rid, int type)
+{
+ /* we have to allocate from the authoritative backend */
+
+ if ( remote_map )
+ return remote_map->allocate_rid( rid, type );
+
+ return cache_map->allocate_rid( rid, type );
+}
+
+/**************************************************************************
+ Shutdown maps.
+**************************************************************************/
+
+NTSTATUS idmap_close(void)
+{
+ NTSTATUS ret;
+
+ ret = cache_map->close();
+ if (!NT_STATUS_IS_OK(ret)) {
+ DEBUG(3, ("idmap_close: failed to close local tdb cache!\n"));
+ }
+ cache_map = NULL;
+
+ if (remote_map) {
+ ret = remote_map->close();
+ if (!NT_STATUS_IS_OK(ret)) {
+ DEBUG(3, ("idmap_close: failed to close remote idmap repository!\n"));
+ }
+ remote_map = NULL;
+ }
+
+ return ret;
+}
+
+/**************************************************************************
+ Dump backend status.
+**************************************************************************/
+
+void idmap_status(void)
+{
+ cache_map->status();
+ if (remote_map)
+ remote_map->status();
+}
diff --git a/source/sam/idmap_ldap.c b/source/sam/idmap_ldap.c
new file mode 100644
index 00000000000..2124fb68793
--- /dev/null
+++ b/source/sam/idmap_ldap.c
@@ -0,0 +1,790 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ idmap LDAP backend
+
+ Copyright (C) Tim Potter 2000
+ Copyright (C) Jim McDonough <jmcd@us.ibm.com> 2003
+ Copyright (C) Simo Sorce 2003
+ Copyright (C) Gerald Carter 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 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"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_IDMAP
+
+
+#include <lber.h>
+#include <ldap.h>
+
+#include "smbldap.h"
+
+struct ldap_idmap_state {
+ struct smbldap_state *smbldap_state;
+ TALLOC_CTX *mem_ctx;
+};
+
+static struct ldap_idmap_state ldap_state;
+
+/* number tries while allocating new id */
+#define LDAP_MAX_ALLOC_ID 128
+
+
+/***********************************************************************
+ This function cannot be called to modify a mapping, only set a new one
+***********************************************************************/
+
+static NTSTATUS ldap_set_mapping(const DOM_SID *sid, unid_t id, int id_type)
+{
+ pstring dn;
+ pstring id_str;
+ fstring type;
+ LDAPMod **mods = NULL;
+ int rc = -1;
+ int ldap_op;
+ fstring sid_string;
+ LDAPMessage *entry = NULL;
+
+ sid_to_string( sid_string, sid );
+
+ ldap_op = LDAP_MOD_ADD;
+ pstr_sprintf(dn, "%s=%s,%s", get_attr_key2string( sidmap_attr_list, LDAP_ATTR_SID),
+ sid_string, lp_ldap_idmap_suffix());
+
+ if ( id_type & ID_USERID )
+ fstrcpy( type, get_attr_key2string( sidmap_attr_list, LDAP_ATTR_UIDNUMBER ) );
+ else
+ fstrcpy( type, get_attr_key2string( sidmap_attr_list, LDAP_ATTR_GIDNUMBER ) );
+
+ pstr_sprintf(id_str, "%lu", ((id_type & ID_USERID) ? (unsigned long)id.uid :
+ (unsigned long)id.gid));
+
+ smbldap_set_mod( &mods, LDAP_MOD_ADD, "objectClass", LDAP_OBJ_IDMAP_ENTRY );
+
+ smbldap_make_mod( ldap_state.smbldap_state->ldap_struct,
+ entry, &mods, type, id_str );
+
+ smbldap_make_mod( ldap_state.smbldap_state->ldap_struct,
+ entry, &mods,
+ get_attr_key2string(sidmap_attr_list, LDAP_ATTR_SID),
+ sid_string );
+
+ /* There may well be nothing at all to do */
+
+ if (mods) {
+ smbldap_set_mod( &mods, LDAP_MOD_ADD, "objectClass", LDAP_OBJ_SID_ENTRY );
+ rc = smbldap_add(ldap_state.smbldap_state, dn, mods);
+ ldap_mods_free( mods, True );
+ } else {
+ rc = LDAP_SUCCESS;
+ }
+
+ if (rc != LDAP_SUCCESS) {
+ char *ld_error = NULL;
+ ldap_get_option(ldap_state.smbldap_state->ldap_struct, LDAP_OPT_ERROR_STRING,
+ &ld_error);
+ DEBUG(0,("ldap_set_mapping_internals: Failed to %s mapping from %s to %lu [%s]\n",
+ (ldap_op == LDAP_MOD_ADD) ? "add" : "replace",
+ sid_string, (unsigned long)((id_type & ID_USERID) ? id.uid : id.gid), type));
+ DEBUG(0, ("ldap_set_mapping_internals: Error was: %s (%s)\n",
+ ld_error ? ld_error : "(NULL)", ldap_err2string (rc)));
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ DEBUG(10,("ldap_set_mapping: Successfully created mapping from %s to %lu [%s]\n",
+ sid_string, ((id_type & ID_USERID) ? (unsigned long)id.uid :
+ (unsigned long)id.gid), type));
+
+ return NT_STATUS_OK;
+}
+
+/**********************************************************************
+ Even if the sambaDomain attribute in LDAP tells us that this RID is
+ safe to use, always check before use.
+*********************************************************************/
+
+static BOOL sid_in_use(struct ldap_idmap_state *state,
+ const DOM_SID *sid, int *error)
+{
+ fstring filter;
+ fstring sid_string;
+ LDAPMessage *result = NULL;
+ int count;
+ int rc;
+ char *sid_attr[] = {LDAP_ATTRIBUTE_SID, NULL};
+
+ slprintf(filter, sizeof(filter)-1, "(%s=%s)", LDAP_ATTRIBUTE_SID, sid_to_string(sid_string, sid));
+
+ rc = smbldap_search_suffix(state->smbldap_state,
+ filter, sid_attr, &result);
+
+ if (rc != LDAP_SUCCESS) {
+ char *ld_error = NULL;
+ ldap_get_option(state->smbldap_state->ldap_struct, LDAP_OPT_ERROR_STRING, &ld_error);
+ DEBUG(2, ("Failed to check if sid %s is alredy in use: %s\n",
+ sid_string, ld_error));
+ SAFE_FREE(ld_error);
+
+ *error = rc;
+ return True;
+ }
+
+ if ((count = ldap_count_entries(state->smbldap_state->ldap_struct, result)) > 0) {
+ DEBUG(3, ("Sid %s already in use - trying next RID\n",
+ sid_string));
+ ldap_msgfree(result);
+ return True;
+ }
+
+ ldap_msgfree(result);
+
+ /* good, sid is not in use */
+ return False;
+}
+
+/**********************************************************************
+ Set the new nextRid attribute, and return one we can use.
+
+ This also checks that this RID is actually free - in case the admin
+ manually stole it :-).
+*********************************************************************/
+
+static NTSTATUS ldap_next_rid(struct ldap_idmap_state *state, uint32 *rid,
+ int rid_type)
+{
+ NTSTATUS ret = NT_STATUS_UNSUCCESSFUL;
+ int rc;
+ LDAPMessage *domain_result = NULL;
+ LDAPMessage *entry = NULL;
+ char *dn;
+ LDAPMod **mods = NULL;
+ fstring old_rid_string;
+ fstring next_rid_string;
+ fstring algorithmic_rid_base_string;
+ uint32 next_rid;
+ uint32 alg_rid_base;
+ int attempts = 0;
+ char *ld_error = NULL;
+
+ while (attempts < 10) {
+ if (!NT_STATUS_IS_OK(ret = smbldap_search_domain_info(state->smbldap_state,
+ &domain_result, get_global_sam_name(), True))) {
+ return ret;
+ }
+
+ entry = ldap_first_entry(state->smbldap_state->ldap_struct, domain_result);
+ if (!entry) {
+ DEBUG(0, ("Could not get domain info entry\n"));
+ ldap_msgfree(domain_result);
+ return ret;
+ }
+
+ if ((dn = smbldap_get_dn(state->smbldap_state->ldap_struct, entry)) == NULL) {
+ DEBUG(0, ("Could not get domain info DN\n"));
+ ldap_msgfree(domain_result);
+ return ret;
+ }
+
+ /* yes, we keep 3 seperate counters, one for rids between 1000 (BASE_RID) and
+ algorithmic_rid_base. The other two are to avoid stomping on the
+ different sets of algorithmic RIDs */
+
+ if (smbldap_get_single_pstring(state->smbldap_state->ldap_struct, entry,
+ get_attr_key2string(dominfo_attr_list, LDAP_ATTR_ALGORITHMIC_RID_BASE),
+ algorithmic_rid_base_string)) {
+
+ alg_rid_base = (uint32)atol(algorithmic_rid_base_string);
+ } else {
+ alg_rid_base = algorithmic_rid_base();
+ /* Try to make the modification atomically by enforcing the
+ old value in the delete mod. */
+ slprintf(algorithmic_rid_base_string, sizeof(algorithmic_rid_base_string)-1, "%d", alg_rid_base);
+ smbldap_make_mod(state->smbldap_state->ldap_struct, entry, &mods,
+ get_attr_key2string(dominfo_attr_list, LDAP_ATTR_ALGORITHMIC_RID_BASE),
+ algorithmic_rid_base_string);
+ }
+
+ next_rid = 0;
+
+ if (alg_rid_base > BASE_RID) {
+ /* we have a non-default 'algorithmic rid base', so we have 'low' rids that we
+ can allocate to new users */
+ if (smbldap_get_single_pstring(state->smbldap_state->ldap_struct, entry,
+ get_attr_key2string(dominfo_attr_list, LDAP_ATTR_NEXT_RID),
+ old_rid_string)) {
+ *rid = (uint32)atol(old_rid_string);
+ } else {
+ *rid = BASE_RID;
+ }
+
+ next_rid = *rid+1;
+ if (next_rid >= alg_rid_base) {
+ ldap_msgfree(domain_result);
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ slprintf(next_rid_string, sizeof(next_rid_string)-1, "%d", next_rid);
+
+ /* Try to make the modification atomically by enforcing the
+ old value in the delete mod. */
+ smbldap_make_mod(state->smbldap_state->ldap_struct, entry, &mods,
+ get_attr_key2string(dominfo_attr_list, LDAP_ATTR_NEXT_RID),
+ next_rid_string);
+ }
+
+ if (!next_rid) { /* not got one already */
+ switch (rid_type) {
+ case USER_RID_TYPE:
+ if (smbldap_get_single_pstring(state->smbldap_state->ldap_struct, entry,
+ get_attr_key2string(dominfo_attr_list, LDAP_ATTR_NEXT_USERRID),
+ old_rid_string)) {
+ *rid = (uint32)atol(old_rid_string);
+ }
+ break;
+ case GROUP_RID_TYPE:
+ if (smbldap_get_single_pstring(state->smbldap_state->ldap_struct, entry,
+ get_attr_key2string(dominfo_attr_list, LDAP_ATTR_NEXT_GROUPRID),
+ old_rid_string)) {
+ *rid = (uint32)atol(old_rid_string);
+ }
+ break;
+ }
+
+ /* This is the core of the whole routine. If we had
+ scheme-style closures, there would be a *lot* less code
+ duplication... */
+
+ next_rid = *rid+RID_MULTIPLIER;
+ slprintf(next_rid_string, sizeof(next_rid_string)-1, "%d", next_rid);
+
+ switch (rid_type) {
+ case USER_RID_TYPE:
+ /* Try to make the modification atomically by enforcing the
+ old value in the delete mod. */
+ smbldap_make_mod(state->smbldap_state->ldap_struct, entry, &mods,
+ get_attr_key2string(dominfo_attr_list, LDAP_ATTR_NEXT_USERRID),
+ next_rid_string);
+ break;
+
+ case GROUP_RID_TYPE:
+ /* Try to make the modification atomically by enforcing the
+ old value in the delete mod. */
+ smbldap_make_mod(state->smbldap_state->ldap_struct, entry, &mods,
+ get_attr_key2string(dominfo_attr_list, LDAP_ATTR_NEXT_GROUPRID),
+ next_rid_string);
+ break;
+ }
+ }
+
+ if ((rc = smbldap_modify(state->smbldap_state, dn, mods)) == LDAP_SUCCESS) {
+ DOM_SID dom_sid;
+ DOM_SID sid;
+ pstring domain_sid_string;
+ int error = 0;
+
+ if (!smbldap_get_single_pstring(state->smbldap_state->ldap_struct, domain_result,
+ get_attr_key2string(dominfo_attr_list, LDAP_ATTR_DOM_SID),
+ domain_sid_string)) {
+ ldap_mods_free(mods, True);
+ SAFE_FREE(dn);
+ ldap_msgfree(domain_result);
+ return ret;
+ }
+
+ if (!string_to_sid(&dom_sid, domain_sid_string)) {
+ ldap_mods_free(mods, True);
+ SAFE_FREE(dn);
+ ldap_msgfree(domain_result);
+ return ret;
+ }
+
+ ldap_mods_free(mods, True);
+ mods = NULL;
+ SAFE_FREE(dn);
+ ldap_msgfree(domain_result);
+
+ sid_copy(&sid, &dom_sid);
+ sid_append_rid(&sid, *rid);
+
+ /* check RID is not in use */
+ if (sid_in_use(state, &sid, &error)) {
+ if (error) {
+ return ret;
+ }
+ continue;
+ }
+
+ return NT_STATUS_OK;
+ }
+
+ ld_error = NULL;
+ ldap_get_option(state->smbldap_state->ldap_struct, LDAP_OPT_ERROR_STRING, &ld_error);
+ DEBUG(2, ("Failed to modify rid: %s\n", ld_error ? ld_error : "(NULL"));
+ SAFE_FREE(ld_error);
+
+ ldap_mods_free(mods, True);
+ mods = NULL;
+
+ SAFE_FREE(dn);
+
+ ldap_msgfree(domain_result);
+ domain_result = NULL;
+
+ {
+ /* Sleep for a random timeout */
+ unsigned sleeptime = (sys_random()*sys_getpid()*attempts);
+ attempts += 1;
+
+ sleeptime %= 100;
+ smb_msleep(sleeptime);
+ }
+ }
+
+ DEBUG(0, ("Failed to set new RID\n"));
+ return ret;
+}
+
+
+/*****************************************************************************
+ Allocate a new RID
+*****************************************************************************/
+
+static NTSTATUS ldap_allocate_rid(uint32 *rid, int rid_type)
+{
+ return ldap_next_rid( &ldap_state, rid, rid_type );
+}
+
+/*****************************************************************************
+ Allocate a new uid or gid
+*****************************************************************************/
+
+static NTSTATUS ldap_allocate_id(unid_t *id, int id_type)
+{
+ NTSTATUS ret = NT_STATUS_UNSUCCESSFUL;
+ int rc = LDAP_SERVER_DOWN;
+ int count = 0;
+ LDAPMessage *result = NULL;
+ LDAPMessage *entry = NULL;
+ pstring id_str, new_id_str;
+ LDAPMod **mods = NULL;
+ const char *type;
+ char *dn = NULL;
+ char **attr_list;
+ pstring filter;
+ uid_t luid, huid;
+ gid_t lgid, hgid;
+
+
+ type = (id_type & ID_USERID) ?
+ get_attr_key2string( idpool_attr_list, LDAP_ATTR_UIDNUMBER ) :
+ get_attr_key2string( idpool_attr_list, LDAP_ATTR_GIDNUMBER );
+
+ pstr_sprintf(filter, "(objectClass=%s)", LDAP_OBJ_IDPOOL);
+
+ attr_list = get_attr_list( idpool_attr_list );
+
+ rc = smbldap_search(ldap_state.smbldap_state, lp_ldap_idmap_suffix(),
+ LDAP_SCOPE_SUBTREE, filter,
+ attr_list, 0, &result);
+ free_attr_list( attr_list );
+
+ if (rc != LDAP_SUCCESS) {
+ DEBUG(0,("ldap_allocate_id: %s object not found\n", LDAP_OBJ_IDPOOL));
+ goto out;
+ }
+
+ count = ldap_count_entries(ldap_state.smbldap_state->ldap_struct, result);
+ if (count != 1) {
+ DEBUG(0,("ldap_allocate_id: single %s object not found\n", LDAP_OBJ_IDPOOL));
+ goto out;
+ }
+
+ dn = smbldap_get_dn(ldap_state.smbldap_state->ldap_struct, result);
+ if (!dn) {
+ goto out;
+ }
+ entry = ldap_first_entry(ldap_state.smbldap_state->ldap_struct, result);
+
+ if (!smbldap_get_single_pstring(ldap_state.smbldap_state->ldap_struct, entry, type, id_str)) {
+ DEBUG(0,("ldap_allocate_id: %s attribute not found\n",
+ type));
+ goto out;
+ }
+
+ /* this must succeed or else we wouldn't have initialized */
+
+ lp_idmap_uid( &luid, &huid);
+ lp_idmap_gid( &lgid, &hgid);
+
+ /* make sure we still have room to grow */
+
+ if (id_type & ID_USERID) {
+ id->uid = strtoul(id_str, NULL, 10);
+ if (id->uid > huid ) {
+ DEBUG(0,("ldap_allocate_id: Cannot allocate uid above %lu!\n",
+ (unsigned long)huid));
+ goto out;
+ }
+ }
+ else {
+ id->gid = strtoul(id_str, NULL, 10);
+ if (id->gid > hgid ) {
+ DEBUG(0,("ldap_allocate_id: Cannot allocate gid above %lu!\n",
+ (unsigned long)hgid));
+ goto out;
+ }
+ }
+
+ pstr_sprintf(new_id_str, "%lu",
+ ((id_type & ID_USERID) ? (unsigned long)id->uid :
+ (unsigned long)id->gid) + 1);
+
+ smbldap_set_mod( &mods, LDAP_MOD_DELETE, type, id_str );
+ smbldap_set_mod( &mods, LDAP_MOD_ADD, type, new_id_str );
+
+ if (mods == NULL) {
+ DEBUG(0,("ldap_allocate_id: smbldap_set_mod() failed.\n"));
+ goto out;
+ }
+
+ rc = smbldap_modify(ldap_state.smbldap_state, dn, mods);
+
+ ldap_mods_free( mods, True );
+ if (rc != LDAP_SUCCESS) {
+ DEBUG(0,("ldap_allocate_id: Failed to allocate new %s. ldap_modify() failed.\n",
+ type));
+ goto out;
+ }
+
+ ret = NT_STATUS_OK;
+out:
+ SAFE_FREE(dn);
+ if (result != NULL)
+ ldap_msgfree(result);
+
+ return ret;
+}
+
+/*****************************************************************************
+ get a sid from an id
+*****************************************************************************/
+
+static NTSTATUS ldap_get_sid_from_id(DOM_SID *sid, unid_t id, int id_type)
+{
+ LDAPMessage *result = NULL;
+ LDAPMessage *entry = NULL;
+ pstring sid_str;
+ pstring filter;
+ pstring suffix;
+ const char *type;
+ int rc;
+ int count;
+ NTSTATUS ret = NT_STATUS_UNSUCCESSFUL;
+ char **attr_list;
+
+ if ( id_type & ID_USERID )
+ type = get_attr_key2string( idpool_attr_list, LDAP_ATTR_UIDNUMBER );
+ else
+ type = get_attr_key2string( idpool_attr_list, LDAP_ATTR_GIDNUMBER );
+
+ pstrcpy( suffix, lp_ldap_idmap_suffix() );
+ pstr_sprintf(filter, "(&(objectClass=%s)(%s=%lu))",
+ LDAP_OBJ_IDMAP_ENTRY, type,
+ ((id_type & ID_USERID) ? (unsigned long)id.uid : (unsigned long)id.gid));
+
+ attr_list = get_attr_list( sidmap_attr_list );
+ rc = smbldap_search(ldap_state.smbldap_state, suffix, LDAP_SCOPE_SUBTREE,
+ filter, attr_list, 0, &result);
+
+ if (rc != LDAP_SUCCESS) {
+ DEBUG(3,("ldap_get_isd_from_id: Failure looking up entry (%s)\n",
+ ldap_err2string(rc) ));
+ goto out;
+ }
+
+ count = ldap_count_entries(ldap_state.smbldap_state->ldap_struct, result);
+
+ if (count != 1) {
+ DEBUG(0,("ldap_get_sid_from_id: mapping not found for %s: %lu\n",
+ type, ((id_type & ID_USERID) ? (unsigned long)id.uid :
+ (unsigned long)id.gid)));
+ goto out;
+ }
+
+ entry = ldap_first_entry(ldap_state.smbldap_state->ldap_struct, result);
+
+ if ( !smbldap_get_single_pstring(ldap_state.smbldap_state->ldap_struct, entry, LDAP_ATTRIBUTE_SID, sid_str) )
+ goto out;
+
+ if (!string_to_sid(sid, sid_str))
+ goto out;
+
+ ret = NT_STATUS_OK;
+out:
+ free_attr_list( attr_list );
+
+ if (result)
+ ldap_msgfree(result);
+
+ return ret;
+}
+
+/***********************************************************************
+ Get an id from a sid
+***********************************************************************/
+
+static NTSTATUS ldap_get_id_from_sid(unid_t *id, int *id_type, const DOM_SID *sid)
+{
+ LDAPMessage *result = NULL;
+ LDAPMessage *entry = NULL;
+ pstring sid_str;
+ pstring filter;
+ pstring id_str;
+ const char *suffix;
+ const char *type;
+ int rc;
+ int count;
+ char **attr_list;
+ char *dn = NULL;
+ NTSTATUS ret = NT_STATUS_UNSUCCESSFUL;
+
+ sid_to_string(sid_str, sid);
+
+ DEBUG(8,("ldap_get_id_from_sid: %s (%s)\n", sid_str,
+ (*id_type & ID_GROUPID ? "group" : "user") ));
+
+ suffix = lp_ldap_idmap_suffix();
+ pstr_sprintf(filter, "(&(objectClass=%s)(%s=%s))",
+ LDAP_OBJ_IDMAP_ENTRY, LDAP_ATTRIBUTE_SID, sid_str);
+
+ if ( *id_type & ID_GROUPID )
+ type = get_attr_key2string( sidmap_attr_list, LDAP_ATTR_GIDNUMBER );
+ else
+ type = get_attr_key2string( sidmap_attr_list, LDAP_ATTR_UIDNUMBER );
+
+ /* do the search and check for errors */
+
+ attr_list = get_attr_list( sidmap_attr_list );
+ rc = smbldap_search(ldap_state.smbldap_state, suffix, LDAP_SCOPE_SUBTREE,
+ filter, attr_list, 0, &result);
+
+ if (rc != LDAP_SUCCESS) {
+ DEBUG(3,("ldap_get_id_from_sid: Failure looking up idmap entry (%s)\n",
+ ldap_err2string(rc) ));
+ goto out;
+ }
+
+ /* check for the number of entries returned */
+
+ count = ldap_count_entries(ldap_state.smbldap_state->ldap_struct, result);
+
+ if ( count > 1 ) {
+ DEBUG(0, ("ldap_get_id_from_sid: (2nd) search %s returned [%d] entries!\n",
+ filter, count));
+ goto out;
+ }
+
+ /* try to allocate a new id if we still haven't found one */
+
+ if ( !count ) {
+ int i;
+
+ if (*id_type & ID_QUERY_ONLY) {
+ DEBUG(5,("ldap_get_id_from_sid: No matching entry found and QUERY_ONLY flag set\n"));
+ goto out;
+ }
+
+ DEBUG(8,("ldap_get_id_from_sid: Allocating new id\n"));
+
+ for (i = 0; i < LDAP_MAX_ALLOC_ID; i++) {
+ ret = ldap_allocate_id(id, *id_type);
+ if ( NT_STATUS_IS_OK(ret) )
+ break;
+ }
+
+ if ( !NT_STATUS_IS_OK(ret) ) {
+ DEBUG(0,("ldap_allocate_id: cannot acquire id lock!\n"));
+ goto out;
+ }
+
+ DEBUG(10,("ldap_get_id_from_sid: Allocated new %cid [%ul]\n",
+ (*id_type & ID_GROUPID ? 'g' : 'u'), (uint32)id->uid ));
+
+ ret = ldap_set_mapping(sid, *id, *id_type);
+
+ /* all done */
+
+ goto out;
+ }
+
+ DEBUG(10,("ldap_get_id_from_sid: success\n"));
+
+ entry = ldap_first_entry(ldap_state.smbldap_state->ldap_struct, result);
+
+ dn = smbldap_get_dn(ldap_state.smbldap_state->ldap_struct, result);
+ if (!dn)
+ goto out;
+
+ DEBUG(10, ("Found mapping entry at dn=%s, looking for %s\n", dn, type));
+
+ if ( smbldap_get_single_pstring(ldap_state.smbldap_state->ldap_struct, entry, type, id_str) ) {
+ if ( (*id_type & ID_USERID) )
+ id->uid = strtoul(id_str, NULL, 10);
+ else
+ id->gid = strtoul(id_str, NULL, 10);
+
+ ret = NT_STATUS_OK;
+ goto out;
+ }
+
+out:
+ free_attr_list( attr_list );
+ if (result)
+ ldap_msgfree(result);
+ SAFE_FREE(dn);
+
+ return ret;
+}
+
+/**********************************************************************
+ Verify the sambaUnixIdPool entry in the directiry.
+**********************************************************************/
+
+static NTSTATUS verify_idpool( void )
+{
+ fstring filter;
+ int rc;
+ char **attr_list;
+ LDAPMessage *result = NULL;
+ LDAPMod **mods = NULL;
+ int count;
+
+ fstr_sprintf( filter, "(objectclass=%s)", LDAP_OBJ_IDPOOL );
+
+ attr_list = get_attr_list( idpool_attr_list );
+ rc = smbldap_search(ldap_state.smbldap_state, lp_ldap_idmap_suffix(),
+ LDAP_SCOPE_SUBTREE, filter, attr_list, 0, &result);
+ free_attr_list ( attr_list );
+
+ if (rc != LDAP_SUCCESS)
+ return NT_STATUS_UNSUCCESSFUL;
+
+ count = ldap_count_entries(ldap_state.smbldap_state->ldap_struct, result);
+
+ ldap_msgfree(result);
+
+ if ( count > 1 ) {
+ DEBUG(0,("ldap_idmap_init: multiple entries returned from %s (base == %s)\n",
+ filter, lp_ldap_idmap_suffix() ));
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+ else if (count == 0) {
+ uid_t luid, huid;
+ gid_t lgid, hgid;
+ fstring uid_str, gid_str;
+
+ if ( !lp_idmap_uid(&luid, &huid) || !lp_idmap_gid( &lgid, &hgid ) ) {
+ DEBUG(0,("ldap_idmap_init: idmap uid/gid parameters not specified\n"));
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ fstr_sprintf( uid_str, "%lu", (unsigned long)luid );
+ fstr_sprintf( gid_str, "%lu", (unsigned long)lgid );
+
+ smbldap_set_mod( &mods, LDAP_MOD_ADD, "objectClass", LDAP_OBJ_IDPOOL );
+ smbldap_set_mod( &mods, LDAP_MOD_ADD,
+ get_attr_key2string(idpool_attr_list, LDAP_ATTR_UIDNUMBER), uid_str );
+ smbldap_set_mod( &mods, LDAP_MOD_ADD,
+ get_attr_key2string(idpool_attr_list, LDAP_ATTR_GIDNUMBER), gid_str );
+
+ rc = smbldap_modify(ldap_state.smbldap_state, lp_ldap_idmap_suffix(), mods);
+ }
+
+ return ( rc==LDAP_SUCCESS ? NT_STATUS_OK : NT_STATUS_UNSUCCESSFUL );
+}
+
+/*****************************************************************************
+ Initialise idmap database.
+*****************************************************************************/
+
+static NTSTATUS ldap_idmap_init( char *params )
+{
+ NTSTATUS nt_status;
+
+ ldap_state.mem_ctx = talloc_init("idmap_ldap");
+ if (!ldap_state.mem_ctx) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ /* assume location is the only parameter */
+ if (!NT_STATUS_IS_OK(nt_status =
+ smbldap_init(ldap_state.mem_ctx, params,
+ &ldap_state.smbldap_state))) {
+ talloc_destroy(ldap_state.mem_ctx);
+ return nt_status;
+ }
+
+ /* see if the idmap suffix and sub entries exists */
+
+ nt_status = verify_idpool();
+ if ( !NT_STATUS_IS_OK(nt_status) )
+ return nt_status;
+
+ return NT_STATUS_OK;
+}
+
+/*****************************************************************************
+ End the LDAP session
+*****************************************************************************/
+
+static NTSTATUS ldap_idmap_close(void)
+{
+
+ smbldap_free_struct(&(ldap_state).smbldap_state);
+ talloc_destroy(ldap_state.mem_ctx);
+
+ DEBUG(5,("The connection to the LDAP server was closed\n"));
+ /* maybe free the results here --metze */
+
+ return NT_STATUS_OK;
+}
+
+
+/* This function doesn't make as much sense in an LDAP world since the calling
+ node doesn't really control the ID ranges */
+static void ldap_idmap_status(void)
+{
+ DEBUG(0, ("LDAP IDMAP Status not available\n"));
+}
+
+static struct idmap_methods ldap_methods = {
+ ldap_idmap_init,
+ ldap_allocate_rid,
+ ldap_allocate_id,
+ ldap_get_sid_from_id,
+ ldap_get_id_from_sid,
+ ldap_set_mapping,
+ ldap_idmap_close,
+ ldap_idmap_status
+
+};
+
+NTSTATUS idmap_ldap_init(void)
+{
+ return smb_register_idmap(SMB_IDMAP_INTERFACE_VERSION, "ldap", &ldap_methods);
+}
diff --git a/source/sam/idmap_tdb.c b/source/sam/idmap_tdb.c
new file mode 100644
index 00000000000..8ab8ec84770
--- /dev/null
+++ b/source/sam/idmap_tdb.c
@@ -0,0 +1,676 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ idmap TDB backend
+
+ Copyright (C) Tim Potter 2000
+ Copyright (C) Jim McDonough <jmcd@us.ibm.com> 2003
+ Copyright (C) Simo Sorce 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 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"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_IDMAP
+
+/* High water mark keys */
+#define HWM_GROUP "GROUP HWM"
+#define HWM_USER "USER HWM"
+
+/* idmap version determines auto-conversion */
+#define IDMAP_VERSION 2
+
+/* Globals */
+static TDB_CONTEXT *idmap_tdb;
+
+static struct idmap_state {
+
+ /* User and group id pool */
+
+ uid_t uid_low, uid_high; /* Range of uids to allocate */
+ gid_t gid_low, gid_high; /* Range of gids to allocate */
+} idmap_state;
+
+/**********************************************************************
+ allocate a new RID; We don't care if is a user or group
+**********************************************************************/
+
+static NTSTATUS db_allocate_rid(uint32 *rid, int rid_type)
+{
+ uint32 lowrid, highrid;
+ uint32 tmp_rid;
+
+ /* can't handle group rids right now. This is such a mess.... */
+
+ if ( rid_type == GROUP_RID_TYPE )
+ return NT_STATUS_UNSUCCESSFUL;
+
+ /* cannot fail since idmap is only called winbindd */
+
+ get_free_rid_range( &lowrid, &highrid );
+
+ tmp_rid = lowrid;
+
+ if ( !tdb_change_uint32_atomic(idmap_tdb, "RID_COUNTER", &tmp_rid, RID_MULTIPLIER) ) {
+ DEBUG(3,("db_allocate_rid: Failed to locate next rid record in idmap db\n"));
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ if ( tmp_rid > highrid ) {
+ DEBUG(0, ("db_allocate_rid: no RIDs available!\n"));
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ *rid = tmp_rid;
+
+ return NT_STATUS_OK;
+}
+
+/**********************************************************************
+ Allocate either a user or group id from the pool
+**********************************************************************/
+
+static NTSTATUS db_allocate_id(unid_t *id, int id_type)
+{
+ BOOL ret;
+ int hwm;
+
+ if (!id)
+ return NT_STATUS_INVALID_PARAMETER;
+
+ /* Get current high water mark */
+ switch (id_type & ID_TYPEMASK) {
+ case ID_USERID:
+
+ if ((hwm = tdb_fetch_int32(idmap_tdb, HWM_USER)) == -1) {
+ return NT_STATUS_INTERNAL_DB_ERROR;
+ }
+
+ /* check it is in the range */
+ if (hwm > idmap_state.uid_high) {
+ DEBUG(0, ("idmap Fatal Error: UID range full!! (max: %lu)\n",
+ (unsigned long)idmap_state.uid_high));
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ /* fetch a new id and increment it */
+ ret = tdb_change_uint32_atomic(idmap_tdb, HWM_USER, (unsigned int *)&hwm, 1);
+ if (!ret) {
+ DEBUG(0, ("idmap_tdb: Fatal error while fetching a new id\n!"));
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ /* recheck it is in the range */
+ if (hwm > idmap_state.uid_high) {
+ DEBUG(0, ("idmap Fatal Error: UID range full!! (max: %lu)\n",
+ (unsigned long)idmap_state.uid_high));
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ (*id).uid = hwm;
+ DEBUG(10,("db_allocate_id: ID_USERID (*id).uid = %d\n", (unsigned int)hwm));
+
+ break;
+ case ID_GROUPID:
+ if ((hwm = tdb_fetch_int32(idmap_tdb, HWM_GROUP)) == -1) {
+ return NT_STATUS_INTERNAL_DB_ERROR;
+ }
+
+ /* check it is in the range */
+ if (hwm > idmap_state.gid_high) {
+ DEBUG(0, ("idmap Fatal Error: GID range full!! (max: %lu)\n",
+ (unsigned long)idmap_state.gid_high));
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ /* fetch a new id and increment it */
+ ret = tdb_change_uint32_atomic(idmap_tdb, HWM_GROUP, (unsigned int *)&hwm, 1);
+
+ if (!ret) {
+ DEBUG(0, ("idmap_tdb: Fatal error while fetching a new id\n!"));
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ /* recheck it is in the range */
+ if (hwm > idmap_state.gid_high) {
+ DEBUG(0, ("idmap Fatal Error: GID range full!! (max: %lu)\n",
+ (unsigned long)idmap_state.gid_high));
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ (*id).gid = hwm;
+ DEBUG(10,("db_allocate_id: ID_GROUPID (*id).gid = %d\n", (unsigned int)hwm));
+
+ break;
+ default:
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ return NT_STATUS_OK;
+}
+
+/* Get a sid from an id */
+static NTSTATUS internal_get_sid_from_id(DOM_SID *sid, unid_t id, int id_type)
+{
+ TDB_DATA key, data;
+ fstring keystr;
+ NTSTATUS ret = NT_STATUS_UNSUCCESSFUL;
+
+ if (!sid)
+ return NT_STATUS_INVALID_PARAMETER;
+
+ switch (id_type & ID_TYPEMASK) {
+ case ID_USERID:
+ slprintf(keystr, sizeof(keystr), "UID %lu", (unsigned long)id.uid);
+ break;
+ case ID_GROUPID:
+ slprintf(keystr, sizeof(keystr), "GID %lu", (unsigned long)id.gid);
+ break;
+ default:
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ key.dptr = keystr;
+ key.dsize = strlen(keystr) + 1;
+
+ DEBUG(10,("internal_get_sid_from_id: fetching record %s\n", keystr ));
+
+ data = tdb_fetch(idmap_tdb, key);
+
+ if (data.dptr) {
+ if (string_to_sid(sid, data.dptr)) {
+ DEBUG(10,("internal_get_sid_from_id: fetching record %s -> %s\n", keystr, data.dptr ));
+ ret = NT_STATUS_OK;
+ }
+ SAFE_FREE(data.dptr);
+ }
+
+ return ret;
+}
+
+/* Error codes for get_id_from_sid */
+enum getidfromsiderr { GET_ID_FROM_SID_OK = 0, GET_ID_FROM_SID_NOTFOUND, GET_ID_FROM_SID_WRONG_TYPE, GET_ID_FROM_SID_ERR };
+
+static enum getidfromsiderr internal_get_id_from_sid(unid_t *id, int *id_type, const DOM_SID *sid)
+{
+ enum getidfromsiderr ret = GET_ID_FROM_SID_ERR;
+ fstring keystr;
+ TDB_DATA key, data;
+ int type = *id_type & ID_TYPEMASK;
+
+ /* Check if sid is present in database */
+ sid_to_string(keystr, sid);
+
+ key.dptr = keystr;
+ key.dsize = strlen(keystr) + 1;
+
+ DEBUG(10,("internal_get_id_from_sid: fetching record %s of type 0x%x\n", keystr, type ));
+
+ data = tdb_fetch(idmap_tdb, key);
+ if (!data.dptr) {
+ DEBUG(10,("internal_get_id_from_sid: record %s not found\n", keystr ));
+ return GET_ID_FROM_SID_NOTFOUND;
+ } else {
+ DEBUG(10,("internal_get_id_from_sid: record %s -> %s\n", keystr, data.dptr ));
+ }
+
+ if (type == ID_EMPTY || type == ID_USERID) {
+ fstring scanstr;
+ /* Parse and return existing uid */
+ fstrcpy(scanstr, "UID %d");
+
+ if (sscanf(data.dptr, scanstr, &((*id).uid)) == 1) {
+ /* uid ok? */
+ if (type == ID_EMPTY) {
+ *id_type = ID_USERID;
+ }
+ DEBUG(10,("internal_get_id_from_sid: %s fetching record %s -> %s \n",
+ (type == ID_EMPTY) ? "ID_EMPTY" : "ID_USERID",
+ keystr, data.dptr ));
+ ret = GET_ID_FROM_SID_OK;
+ } else {
+ ret = GET_ID_FROM_SID_WRONG_TYPE;
+ }
+ }
+
+ if ((ret != GET_ID_FROM_SID_OK) && (type == ID_EMPTY || type == ID_GROUPID)) {
+ fstring scanstr;
+ /* Parse and return existing gid */
+ fstrcpy(scanstr, "GID %d");
+
+ if (sscanf(data.dptr, scanstr, &((*id).gid)) == 1) {
+ /* gid ok? */
+ if (type == ID_EMPTY) {
+ *id_type = ID_GROUPID;
+ }
+ DEBUG(10,("internal_get_id_from_sid: %s fetching record %s -> %s \n",
+ (type == ID_EMPTY) ? "ID_EMPTY" : "ID_GROUPID",
+ keystr, data.dptr ));
+ ret = GET_ID_FROM_SID_OK;
+ } else {
+ ret = GET_ID_FROM_SID_WRONG_TYPE;
+ }
+ }
+
+ SAFE_FREE(data.dptr);
+
+ return ret;
+}
+
+/* Get a sid from an id */
+static NTSTATUS db_get_sid_from_id(DOM_SID *sid, unid_t id, int id_type_in)
+{
+ NTSTATUS ret = NT_STATUS_UNSUCCESSFUL;
+ enum getidfromsiderr iderr;
+ int id_type = id_type_in & ID_TYPEMASK;
+ unid_t id_tmp = id;
+ int id_type_tmp = id_type;
+
+ DEBUG(10,("db_get_sid_from_id: id_type_in = 0x%x\n", id_type_in));
+
+ ret = internal_get_sid_from_id(sid, id, id_type);
+ if (!NT_STATUS_IS_OK(ret)) {
+ return ret;
+ }
+
+ iderr = internal_get_id_from_sid(&id_tmp, &id_type_tmp, sid);
+ if (iderr != GET_ID_FROM_SID_OK) {
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+ if (id_type_tmp != id_type) {
+ return NT_STATUS_UNSUCCESSFUL;
+ } else if (id_type == ID_USERID) {
+ if (id_tmp.uid != id.uid) {
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+ } else if (id_type == ID_GROUPID) {
+ if (id_tmp.gid != id.gid) {
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+ } else {
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+ return ret;
+}
+/* Get an id from a sid */
+static NTSTATUS db_get_id_from_sid(unid_t *id, int *id_type, const DOM_SID *sid)
+{
+ NTSTATUS ret = NT_STATUS_UNSUCCESSFUL;
+ enum getidfromsiderr iderr;
+
+ DEBUG(10,("db_get_id_from_sid\n"));
+
+ if (!sid || !id || !id_type)
+ return NT_STATUS_INVALID_PARAMETER;
+
+ iderr = internal_get_id_from_sid(id, id_type, sid);
+ if (iderr == GET_ID_FROM_SID_OK) {
+ DOM_SID sid_tmp;
+ ret = internal_get_sid_from_id(&sid_tmp, *id, *id_type);
+ if (NT_STATUS_IS_OK(ret)) {
+ if (!sid_equal(&sid_tmp, sid)) {
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+ }
+ } else if (iderr == GET_ID_FROM_SID_WRONG_TYPE) {
+ /* We found a record but not the type we wanted.
+ * This is an error, not an opportunity to overwrite...
+ * JRA.
+ */
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ if (!(*id_type & ID_QUERY_ONLY) && (iderr != GET_ID_FROM_SID_OK) &&
+ (((*id_type & ID_TYPEMASK) == ID_USERID)
+ || (*id_type & ID_TYPEMASK) == ID_GROUPID)) {
+ TDB_DATA sid_data;
+ TDB_DATA ugid_data;
+ fstring sid_string;
+
+ sid_to_string(sid_string, sid);
+
+ sid_data.dptr = sid_string;
+ sid_data.dsize = strlen(sid_string)+1;
+
+ /* Lock the record for this SID. */
+ if (tdb_chainlock(idmap_tdb, sid_data) != 0) {
+ DEBUG(10,("db_get_id_from_sid: failed to lock record %s. Error %s\n",
+ sid_string, tdb_errorstr(idmap_tdb) ));
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ do {
+ fstring ugid_str;
+
+ /* Allocate a new id for this sid */
+ ret = db_allocate_id(id, *id_type);
+ if (!NT_STATUS_IS_OK(ret))
+ break;
+
+ /* Store the UID side */
+ /* Store new id */
+ if (*id_type & ID_USERID) {
+ slprintf(ugid_str, sizeof(ugid_str), "UID %lu",
+ (unsigned long)((*id).uid));
+ } else {
+ slprintf(ugid_str, sizeof(ugid_str), "GID %lu",
+ (unsigned long)((*id).gid));
+ }
+
+ ugid_data.dptr = ugid_str;
+ ugid_data.dsize = strlen(ugid_str) + 1;
+
+ DEBUG(10,("db_get_id_from_sid: storing %s -> %s\n",
+ ugid_data.dptr, sid_data.dptr ));
+
+ if (tdb_store(idmap_tdb, ugid_data, sid_data, TDB_INSERT) != -1) {
+ ret = NT_STATUS_OK;
+ break;
+ }
+ if (tdb_error(idmap_tdb) != TDB_ERR_EXISTS)
+ DEBUG(10,("db_get_id_from_sid: error %s\n", tdb_errorstr(idmap_tdb) ));
+ ret = NT_STATUS_UNSUCCESSFUL;
+ } while (tdb_error(idmap_tdb) == TDB_ERR_EXISTS);
+
+ if (NT_STATUS_IS_OK(ret)) {
+
+ DEBUG(10,("db_get_id_from_sid: storing %s -> %s\n",
+ sid_data.dptr, ugid_data.dptr ));
+
+ if (tdb_store(idmap_tdb, sid_data, ugid_data, TDB_REPLACE) == -1) {
+ DEBUG(10,("db_get_id_from_sid: error %s\n", tdb_errorstr(idmap_tdb) ));
+ /* TODO: print tdb error !! */
+ tdb_chainunlock(idmap_tdb, sid_data);
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+ }
+
+ tdb_chainunlock(idmap_tdb, sid_data);
+ }
+
+ return ret;
+}
+
+static NTSTATUS db_set_mapping(const DOM_SID *sid, unid_t id, int id_type)
+{
+ TDB_DATA ksid, kid, data;
+ fstring ksidstr;
+ fstring kidstr;
+
+ DEBUG(10,("db_set_mapping: id_type = 0x%x\n", id_type));
+
+ if (!sid)
+ return NT_STATUS_INVALID_PARAMETER;
+
+ sid_to_string(ksidstr, sid);
+
+ ksid.dptr = ksidstr;
+ ksid.dsize = strlen(ksidstr) + 1;
+
+ if (id_type & ID_USERID) {
+ slprintf(kidstr, sizeof(kidstr), "UID %lu", (unsigned long)id.uid);
+ } else if (id_type & ID_GROUPID) {
+ slprintf(kidstr, sizeof(kidstr), "GID %lu", (unsigned long)id.gid);
+ } else {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ kid.dptr = kidstr;
+ kid.dsize = strlen(kidstr) + 1;
+
+ /* *DELETE* prevoius mappings if any.
+ * This is done both SID and [U|G]ID passed in */
+
+ /* Lock the record for this SID. */
+ if (tdb_chainlock(idmap_tdb, ksid) != 0) {
+ DEBUG(10,("db_set_mapping: failed to lock record %s. Error %s\n",
+ ksidstr, tdb_errorstr(idmap_tdb) ));
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ DEBUG(10,("db_set_mapping: fetching %s\n", ksid.dptr));
+
+ data = tdb_fetch(idmap_tdb, ksid);
+ if (data.dptr) {
+ DEBUG(10,("db_set_mapping: deleting %s and %s\n", data.dptr, ksid.dptr ));
+ tdb_delete(idmap_tdb, data);
+ tdb_delete(idmap_tdb, ksid);
+ SAFE_FREE(data.dptr);
+ }
+ data = tdb_fetch(idmap_tdb, kid);
+ if (data.dptr) {
+ DEBUG(10,("db_set_mapping: deleting %s and %s\n", data.dptr, kid.dptr ));
+ tdb_delete(idmap_tdb, data);
+ tdb_delete(idmap_tdb, kid);
+ SAFE_FREE(data.dptr);
+ }
+
+ if (tdb_store(idmap_tdb, ksid, kid, TDB_INSERT) == -1) {
+ DEBUG(0, ("idb_set_mapping: tdb_store 1 error: %s\n", tdb_errorstr(idmap_tdb)));
+ tdb_chainunlock(idmap_tdb, ksid);
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+ if (tdb_store(idmap_tdb, kid, ksid, TDB_INSERT) == -1) {
+ DEBUG(0, ("idb_set_mapping: tdb_store 2 error: %s\n", tdb_errorstr(idmap_tdb)));
+ tdb_chainunlock(idmap_tdb, ksid);
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ tdb_chainunlock(idmap_tdb, ksid);
+ DEBUG(10,("db_set_mapping: stored %s -> %s and %s -> %s\n", ksid.dptr, kid.dptr, kid.dptr, ksid.dptr ));
+ return NT_STATUS_OK;
+}
+
+/*****************************************************************************
+ Initialise idmap database.
+*****************************************************************************/
+
+static NTSTATUS db_idmap_init( char *params )
+{
+ SMB_STRUCT_STAT stbuf;
+ char *tdbfile = NULL;
+ int32 version;
+ BOOL tdb_is_new = False;
+
+ /* use the old database if present */
+ tdbfile = strdup(lock_path("winbindd_idmap.tdb"));
+ if (!tdbfile) {
+ DEBUG(0, ("idmap_init: out of memory!\n"));
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ if (!file_exist(tdbfile, &stbuf)) {
+ tdb_is_new = True;
+ }
+
+ DEBUG(10,("db_idmap_init: Opening tdbfile %s\n", tdbfile ));
+
+ /* Open idmap repository */
+ if (!(idmap_tdb = tdb_open_log(tdbfile, 0,
+ TDB_DEFAULT, O_RDWR | O_CREAT,
+ 0644))) {
+ DEBUG(0, ("idmap_init: Unable to open idmap database\n"));
+ SAFE_FREE(tdbfile);
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ SAFE_FREE(tdbfile);
+
+ if (tdb_is_new) {
+ /* the file didn't existed before opening it, let's
+ * store idmap version as nobody else yet opened and
+ * stored it. I do not like this method but didn't
+ * found a way to understand if an opened tdb have
+ * been just created or not --- SSS */
+ tdb_store_int32(idmap_tdb, "IDMAP_VERSION", IDMAP_VERSION);
+ }
+
+ /* check against earlier versions */
+ version = tdb_fetch_int32(idmap_tdb, "IDMAP_VERSION");
+ if (version != IDMAP_VERSION) {
+ DEBUG(0, ("idmap_init: Unable to open idmap database, it's in an old format!\n"));
+ return NT_STATUS_INTERNAL_DB_ERROR;
+ }
+
+ /* Create high water marks for group and user id */
+ if (!lp_idmap_uid(&idmap_state.uid_low, &idmap_state.uid_high)) {
+ DEBUG(1, ("idmap uid range missing or invalid\n"));
+ DEBUGADD(1, ("idmap will be unable to map foreign SIDs\n"));
+ } else {
+ if (tdb_fetch_int32(idmap_tdb, HWM_USER) == -1) {
+ if (tdb_store_int32(idmap_tdb, HWM_USER, idmap_state.uid_low) == -1) {
+ DEBUG(0, ("idmap_init: Unable to initialise user hwm in idmap database\n"));
+ return NT_STATUS_INTERNAL_DB_ERROR;
+ }
+ }
+ }
+
+ if (!lp_idmap_gid(&idmap_state.gid_low, &idmap_state.gid_high)) {
+ DEBUG(1, ("idmap gid range missing or invalid\n"));
+ DEBUGADD(1, ("idmap will be unable to map foreign SIDs\n"));
+ } else {
+ if (tdb_fetch_int32(idmap_tdb, HWM_GROUP) == -1) {
+ if (tdb_store_int32(idmap_tdb, HWM_GROUP, idmap_state.gid_low) == -1) {
+ DEBUG(0, ("idmap_init: Unable to initialise group hwm in idmap database\n"));
+ return NT_STATUS_INTERNAL_DB_ERROR;
+ }
+ }
+ }
+
+ return NT_STATUS_OK;
+}
+
+/* Close the tdb */
+static NTSTATUS db_idmap_close(void)
+{
+ if (idmap_tdb) {
+ if (tdb_close(idmap_tdb) == 0) {
+ return NT_STATUS_OK;
+ } else {
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+ }
+ return NT_STATUS_OK;
+}
+
+
+/* Dump status information to log file. Display different stuff based on
+ the debug level:
+
+ Debug Level Information Displayed
+ =================================================================
+ 0 Percentage of [ug]id range allocated
+ 0 High water marks (next allocated ids)
+*/
+
+#define DUMP_INFO 0
+
+static void db_idmap_status(void)
+{
+ int user_hwm, group_hwm;
+
+ DEBUG(0, ("winbindd idmap status:\n"));
+
+ /* Get current high water marks */
+
+ if ((user_hwm = tdb_fetch_int32(idmap_tdb, HWM_USER)) == -1) {
+ DEBUG(DUMP_INFO,
+ ("\tCould not get userid high water mark!\n"));
+ }
+
+ if ((group_hwm = tdb_fetch_int32(idmap_tdb, HWM_GROUP)) == -1) {
+ DEBUG(DUMP_INFO,
+ ("\tCould not get groupid high water mark!\n"));
+ }
+
+ /* Display next ids to allocate */
+
+ if (user_hwm != -1) {
+ DEBUG(DUMP_INFO,
+ ("\tNext userid to allocate is %d\n", user_hwm));
+ }
+
+ if (group_hwm != -1) {
+ DEBUG(DUMP_INFO,
+ ("\tNext groupid to allocate is %d\n", group_hwm));
+ }
+
+ /* Display percentage of id range already allocated. */
+
+ if (user_hwm != -1) {
+ int num_users = user_hwm - idmap_state.uid_low;
+ int total_users =
+ idmap_state.uid_high - idmap_state.uid_low;
+
+ DEBUG(DUMP_INFO,
+ ("\tUser id range is %d%% full (%d of %d)\n",
+ num_users * 100 / total_users, num_users,
+ total_users));
+ }
+
+ if (group_hwm != -1) {
+ int num_groups = group_hwm - idmap_state.gid_low;
+ int total_groups =
+ idmap_state.gid_high - idmap_state.gid_low;
+
+ DEBUG(DUMP_INFO,
+ ("\tGroup id range is %d%% full (%d of %d)\n",
+ num_groups * 100 / total_groups, num_groups,
+ total_groups));
+ }
+
+ /* Display complete mapping of users and groups to rids */
+}
+
+/**********************************************************************
+ Return the TDB_CONTEXT* for winbindd_idmap. I **really** feel
+ dirty doing this, but not so dirty that I want to create another
+ tdb
+***********************************************************************/
+
+TDB_CONTEXT *idmap_tdb_handle( void )
+{
+ if ( idmap_tdb )
+ return idmap_tdb;
+
+ /* go ahead an open it; db_idmap_init() doesn't use any params
+ right now */
+
+ db_idmap_init( NULL );
+ if ( idmap_tdb )
+ return idmap_tdb;
+
+ return NULL;
+}
+
+static struct idmap_methods db_methods = {
+
+ db_idmap_init,
+ db_allocate_rid,
+ db_allocate_id,
+ db_get_sid_from_id,
+ db_get_id_from_sid,
+ db_set_mapping,
+ db_idmap_close,
+ db_idmap_status
+
+};
+
+NTSTATUS idmap_tdb_init(void)
+{
+ return smb_register_idmap(SMB_IDMAP_INTERFACE_VERSION, "tdb", &db_methods);
+}
diff --git a/source/sam/idmap_util.c b/source/sam/idmap_util.c
new file mode 100644
index 00000000000..f28e11cde74
--- /dev/null
+++ b/source/sam/idmap_util.c
@@ -0,0 +1,295 @@
+/*
+ Unix SMB/CIFS implementation.
+ ID Mapping
+ Copyright (C) Simo Sorce 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 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"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_IDMAP
+
+#if 0 /* NOT USED */
+
+/**********************************************************************
+ Get the free RID base if idmap is configured, otherwise return 0
+**********************************************************************/
+
+uint32 idmap_get_free_rid_base(void)
+{
+ uint32 low, high;
+ if (idmap_get_free_rid_range(&low, &high)) {
+ return low;
+ }
+ return 0;
+}
+
+/**********************************************************************
+**********************************************************************/
+
+BOOL idmap_check_ugid_is_in_free_range(uint32 id)
+{
+ uint32 low, high;
+
+ if (!idmap_get_free_ugid_range(&low, &high)) {
+ return False;
+ }
+ if (id < low || id > high) {
+ return False;
+ }
+ return True;
+}
+
+/**********************************************************************
+**********************************************************************/
+
+BOOL idmap_check_rid_is_in_free_range(uint32 rid)
+{
+ uint32 low, high;
+
+ if (!idmap_get_free_rid_range(&low, &high)) {
+ return False;
+ }
+ if (rid < algorithmic_rid_base()) {
+ return True;
+ }
+
+ if (rid < low || rid > high) {
+ return False;
+ }
+
+ return True;
+}
+
+/**********************************************************************
+ if it is a foreign SID or if the SID is in the free range, return true
+**********************************************************************/
+
+BOOL idmap_check_sid_is_in_free_range(const DOM_SID *sid)
+{
+ if (sid_compare_domain(get_global_sam_sid(), sid) == 0) {
+
+ uint32 rid;
+
+ if (sid_peek_rid(sid, &rid)) {
+ return idmap_check_rid_is_in_free_range(rid);
+ }
+
+ return False;
+ }
+
+ return True;
+}
+
+#endif /* NOT USED */
+
+/*****************************************************************
+ Returns SID pointer.
+*****************************************************************/
+
+NTSTATUS idmap_uid_to_sid(DOM_SID *sid, uid_t uid)
+{
+ unid_t id;
+ int flags;
+
+ DEBUG(10,("idmap_uid_to_sid: uid = [%lu]\n", (unsigned long)uid));
+
+ flags = ID_USERID;
+ id.uid = uid;
+
+ return idmap_get_sid_from_id(sid, id, flags);
+}
+
+/*****************************************************************
+ Group mapping is used for gids that maps to Wellknown SIDs
+ Returns SID pointer.
+*****************************************************************/
+
+NTSTATUS idmap_gid_to_sid(DOM_SID *sid, gid_t gid)
+{
+ unid_t id;
+ int flags;
+
+ DEBUG(10,("idmap_gid_to_sid: gid = [%lu]\n", (unsigned long)gid));
+
+ flags = ID_GROUPID;
+#if 0 /* JERRY */
+ if (!idmap_check_ugid_is_in_free_range(gid)) {
+ flags |= ID_QUERY_ONLY;
+ }
+#endif
+ id.gid = gid;
+ return idmap_get_sid_from_id(sid, id, flags);
+}
+
+/*****************************************************************
+ if it is a foreign sid or it is in idmap rid range check idmap,
+ otherwise falls back to the legacy algorithmic mapping.
+ Returns True if this name is a user sid and the conversion
+ was done correctly, False if not.
+*****************************************************************/
+
+NTSTATUS idmap_sid_to_uid(const DOM_SID *sid, uid_t *uid, uint32 flags)
+{
+ NTSTATUS ret = NT_STATUS_UNSUCCESSFUL;
+ unid_t id;
+
+ DEBUG(10,("idmap_sid_to_uid: sid = [%s]\n", sid_string_static(sid)));
+
+ flags |= ID_USERID;
+
+ ret = idmap_get_id_from_sid(&id, (int *)&flags, sid);
+
+ if ( NT_STATUS_IS_OK(ret) ) {
+ DEBUG(10,("idmap_sid_to_uid: uid = [%lu]\n", (unsigned long)id.uid));
+ *uid = id.uid;
+ }
+
+ return ret;
+
+}
+
+/*****************************************************************
+ *THE CANONICAL* convert SID to gid function.
+ if it is a foreign sid or it is in idmap rid range check idmap,
+ otherwise falls back to the legacy algorithmic mapping.
+ Group mapping is used for gids that maps to Wellknown SIDs
+ Returns True if this name is a user sid and the conversion
+ was done correctly, False if not.
+*****************************************************************/
+
+NTSTATUS idmap_sid_to_gid(const DOM_SID *sid, gid_t *gid, uint32 flags)
+{
+ NTSTATUS ret = NT_STATUS_UNSUCCESSFUL;
+ unid_t id;
+
+ DEBUG(10,("sid_to_gid: sid = [%s]\n", sid_string_static(sid)));
+
+ flags |= ID_GROUPID;
+
+ ret = idmap_get_id_from_sid(&id, (int *)&flags, sid);
+
+ if ( NT_STATUS_IS_OK(ret) )
+ {
+ DEBUG(10,("idmap_sid_to_gid: gid = [%lu]\n", (unsigned long)id.gid));
+ *gid = id.gid;
+ }
+
+ return ret;
+}
+
+
+/***************************************************************************
+ Check first, call set_mapping if it doesn't already exist.
+***************************************************************************/
+
+static NTSTATUS wellknown_id_init(DOM_SID *sid, unid_t id, int flags)
+{
+ unid_t storedid;
+ int qflags = flags | ID_QUERY_ONLY;
+
+ if (!NT_STATUS_IS_OK(idmap_get_id_from_sid(&storedid, &qflags, sid))) {
+ return idmap_set_mapping(sid, id, flags);
+ } else {
+ if (flags == ID_USERID && id.uid != storedid.uid) {
+ DEBUG(0,("wellknown_id_init: WARNING ! Stored uid %u for SID %s is not the same as the requested uid %u\n",
+ (unsigned int)storedid.uid, sid_string_static(sid), (unsigned int)id.uid ));
+ DEBUG(0,("wellknown_id_init: Attempting to overwrite old mapping with new.\n"));
+ return idmap_set_mapping(sid, id, flags);
+ } else if (flags == ID_GROUPID && id.gid != storedid.gid) {
+ DEBUG(0,("wellknown_id_init: WARNING ! Stored gid %u for SID %s is not the same as the requested gid %u\n",
+ (unsigned int)storedid.gid, sid_string_static(sid), (unsigned int)id.gid ));
+ DEBUG(0,("wellknown_id_init: Attempting to overwrite old mapping with new.\n"));
+ return idmap_set_mapping(sid, id, flags);
+ }
+ }
+ return NT_STATUS_OK;
+}
+
+/***************************************************************************
+ Initialize idmap withWellknown SIDs like Guest, that are necessary
+ to make samba run properly.
+***************************************************************************/
+
+BOOL idmap_init_wellknown_sids(void)
+{
+ const char *guest_account = lp_guestaccount();
+ struct passwd *pass;
+ GROUP_MAP *map=NULL;
+ int num_entries=0;
+ DOM_SID sid;
+ unid_t id;
+ fstring sid_string;
+
+ if (!(guest_account && *guest_account)) {
+ DEBUG(1, ("NULL guest account!?!?\n"));
+ return False;
+ }
+
+ pass = getpwnam_alloc(guest_account);
+ if (!pass) {
+ return False;
+ }
+
+ /* Fill in the SID for the guest account. */
+ id.uid = pass->pw_uid;
+ sid_copy(&sid, get_global_sam_sid());
+ sid_append_rid(&sid, DOMAIN_USER_RID_GUEST);
+
+ if (!NT_STATUS_IS_OK(wellknown_id_init(&sid, id, ID_USERID))) {
+ DEBUG(0, ("Failed to setup UID mapping for GUEST (%s) to (%u)\n",
+ sid_to_string(sid_string, &sid), (unsigned int)id.uid));
+ passwd_free(&pass);
+ return False;
+ }
+
+ /* check if DOMAIN_GROUP_RID_GUESTS SID is set, if not store the
+ * guest account gid as mapping */
+ id.gid = pass->pw_gid;
+ sid_copy(&sid, get_global_sam_sid());
+ sid_append_rid(&sid, DOMAIN_GROUP_RID_GUESTS);
+ if (!NT_STATUS_IS_OK(wellknown_id_init(&sid, id, ID_GROUPID))) {
+ DEBUG(0, ("Failed to setup GID mapping for Group DOMAIN GUESTS (%s) to (%u)\n",
+ sid_to_string(sid_string, &sid), (unsigned int)id.gid));
+ passwd_free(&pass);
+ return False;
+ }
+
+ passwd_free(&pass);
+ /* now fill in group mappings */
+ if(pdb_enum_group_mapping(SID_NAME_UNKNOWN, &map, &num_entries, ENUM_ONLY_MAPPED)) {
+ int i;
+
+ for (i = 0; i < num_entries; i++) {
+ id.gid = map[i].gid;
+ wellknown_id_init(&map[i].sid, id, ID_GROUPID);
+ }
+ SAFE_FREE(map);
+ }
+
+ /* Fill in the SID for the administrator account. */
+ id.uid = 0;
+ sid_copy(&sid, get_global_sam_sid());
+ sid_append_rid(&sid, DOMAIN_USER_RID_ADMIN);
+
+ if (!NT_STATUS_IS_OK(wellknown_id_init(&sid, id, ID_USERID))) {
+ DEBUG(0, ("Failed to setup UID mapping for ADMINISTRATOR (%s) to (%u)\n",
+ sid_to_string(sid_string, &sid), (unsigned int)id.uid));
+ return False;
+ }
+
+ return True;
+}
diff --git a/source/sam/interface.c b/source/sam/interface.c
new file mode 100644
index 00000000000..51ae561999c
--- /dev/null
+++ b/source/sam/interface.c
@@ -0,0 +1,1338 @@
+/*
+ Unix SMB/CIFS implementation.
+ Password and authentication handling
+ Copyright (C) Andrew Bartlett 2002
+ Copyright (C) Jelmer Vernooij 2002
+ Copyright (C) Stefan (metze) Metzmacher 2002
+ Copyright (C) Kai Krüger 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 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"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_SAM
+
+extern DOM_SID global_sid_Builtin;
+
+/** List of various built-in sam modules */
+
+const struct sam_init_function_entry builtin_sam_init_functions[] = {
+ { "plugin", sam_init_plugin },
+#ifdef HAVE_LDAP
+ { "ads", sam_init_ads },
+#endif
+ { "skel", sam_init_skel },
+ { NULL, NULL}
+};
+
+
+static NTSTATUS sam_get_methods_by_sid(const SAM_CONTEXT *context, SAM_METHODS **sam_method, const DOM_SID *domainsid)
+{
+ SAM_METHODS *tmp_methods;
+
+ DEBUG(5,("sam_get_methods_by_sid: %d\n", __LINE__));
+
+ /* invalid sam_context specified */
+ SAM_ASSERT(context && context->methods);
+
+ tmp_methods = context->methods;
+
+ while (tmp_methods) {
+ if (sid_equal(domainsid, &(tmp_methods->domain_sid)))
+ {
+ (*sam_method) = tmp_methods;
+ return NT_STATUS_OK;
+ }
+ tmp_methods = tmp_methods->next;
+ }
+
+ DEBUG(3,("sam_get_methods_by_sid: There is no backend specified for domain %s\n", sid_string_static(domainsid)));
+
+ return NT_STATUS_NO_SUCH_DOMAIN;
+}
+
+static NTSTATUS sam_get_methods_by_name(const SAM_CONTEXT *context, SAM_METHODS **sam_method, const char *domainname)
+{
+ SAM_METHODS *tmp_methods;
+
+ DEBUG(5,("sam_get_methods_by_name: %d\n", __LINE__));
+
+ /* invalid sam_context specified */
+ SAM_ASSERT(context && context->methods);
+
+ tmp_methods = context->methods;
+
+ while (tmp_methods) {
+ if (strequal(domainname, tmp_methods->domain_name))
+ {
+ (*sam_method) = tmp_methods;
+ return NT_STATUS_OK;
+ }
+ tmp_methods = tmp_methods->next;
+ }
+
+ DEBUG(3,("sam_get_methods_by_sid: There is no backend specified for domain %s\n", domainname));
+
+ return NT_STATUS_NO_SUCH_DOMAIN;
+}
+
+static NTSTATUS make_sam_methods(TALLOC_CTX *mem_ctx, SAM_METHODS **methods)
+{
+ *methods = talloc(mem_ctx, sizeof(SAM_METHODS));
+
+ if (!*methods) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ ZERO_STRUCTP(*methods);
+
+ return NT_STATUS_OK;
+}
+
+/******************************************************************
+ Free and cleanup a sam context, any associated data and anything
+ that the attached modules might have associated.
+ *******************************************************************/
+
+void free_sam_context(SAM_CONTEXT **context)
+{
+ SAM_METHODS *sam_selected = (*context)->methods;
+
+ while (sam_selected) {
+ if (sam_selected->free_private_data) {
+ sam_selected->free_private_data(&(sam_selected->private_data));
+ }
+ sam_selected = sam_selected->next;
+ }
+
+ talloc_destroy((*context)->mem_ctx);
+ *context = NULL;
+}
+
+/******************************************************************
+ Make a backend_entry from scratch
+ *******************************************************************/
+
+static NTSTATUS make_backend_entry(SAM_BACKEND_ENTRY *backend_entry, char *sam_backend_string)
+{
+ char *tmp = NULL;
+ char *tmp_string = sam_backend_string;
+
+ DEBUG(5,("make_backend_entry: %d\n", __LINE__));
+
+ SAM_ASSERT(sam_backend_string && backend_entry);
+
+ backend_entry->module_name = sam_backend_string;
+
+ DEBUG(5,("makeing backend_entry for %s\n", backend_entry->module_name));
+
+ if ((tmp = strrchr(tmp_string, '|')) != NULL) {
+ DEBUGADD(20,("a domain name has been specified\n"));
+ *tmp = 0;
+ backend_entry->domain_name = smb_xstrdup(tmp + 1);
+ tmp_string = tmp + 1;
+ }
+
+ if ((tmp = strchr(tmp_string, ':')) != NULL) {
+ DEBUG(20,("options for the backend have been specified\n"));
+ *tmp = 0;
+ backend_entry->module_params = smb_xstrdup(tmp + 1);
+ tmp_string = tmp + 1;
+ }
+
+ if (backend_entry->domain_name == NULL) {
+ DEBUG(10,("make_backend_entry: no domain was specified for sam module %s. Using default domain %s\n",
+ backend_entry->module_name, lp_workgroup()));
+ backend_entry->domain_name = smb_xstrdup(lp_workgroup());
+ }
+
+ if ((backend_entry->domain_sid = (DOM_SID *)malloc(sizeof(DOM_SID))) == NULL) {
+ DEBUG(0,("make_backend_entry: failed to malloc domain_sid\n"));
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ DEBUG(10,("looking up sid for domain %s\n", backend_entry->domain_name));
+
+ if (!secrets_fetch_domain_sid(backend_entry->domain_name, backend_entry->domain_sid)) {
+ DEBUG(2,("make_backend_entry: There is no SID stored for domain %s. Creating a new one.\n",
+ backend_entry->domain_name));
+ DEBUG(0, ("FIXME in %s:%d\n", __FILE__, __LINE__));
+ ZERO_STRUCTP(backend_entry->domain_sid);
+ }
+
+ DEBUG(5,("make_backend_entry: module name: %s, module parameters: %s, domain name: %s, domain sid: %s\n",
+ backend_entry->module_name, backend_entry->module_params, backend_entry->domain_name, sid_string_static(backend_entry->domain_sid)));
+
+ return NT_STATUS_OK;
+}
+
+/******************************************************************
+ create sam_methods struct based on sam_backend_entry
+ *****************************************************************/
+
+static NTSTATUS make_sam_methods_backend_entry(SAM_CONTEXT *context, SAM_METHODS **methods_ptr, SAM_BACKEND_ENTRY *backend_entry)
+{
+ NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
+ SAM_METHODS *methods;
+ int i;
+
+ DEBUG(5,("make_sam_methods_backend_entry: %d\n", __LINE__));
+
+ if (!NT_STATUS_IS_OK(nt_status = make_sam_methods(context->mem_ctx, methods_ptr))) {
+ return nt_status;
+ }
+
+ methods = *methods_ptr;
+ methods->backendname = talloc_strdup(context->mem_ctx, backend_entry->module_name);
+ methods->domain_name = talloc_strdup(context->mem_ctx, backend_entry->domain_name);
+ sid_copy(&methods->domain_sid, backend_entry->domain_sid);
+ methods->parent = context;
+
+ DEBUG(5,("Attempting to find sam backend %s\n", backend_entry->module_name));
+ for (i = 0; builtin_sam_init_functions[i].module_name; i++)
+ {
+ if (strequal(builtin_sam_init_functions[i].module_name, backend_entry->module_name))
+ {
+ DEBUG(5,("Found sam backend %s (at pos %d)\n", backend_entry->module_name, i));
+ DEBUGADD(5,("initialising it with options=%s for domain %s\n", backend_entry->module_params, sid_string_static(backend_entry->domain_sid)));
+ nt_status = builtin_sam_init_functions[i].init(methods, backend_entry->module_params);
+ if (NT_STATUS_IS_OK(nt_status)) {
+ DEBUG(5,("sam backend %s has a valid init\n", backend_entry->module_name));
+ } else {
+ DEBUG(2,("sam backend %s did not correctly init (error was %s)\n",
+ backend_entry->module_name, nt_errstr(nt_status)));
+ }
+ return nt_status;
+ }
+ }
+
+ DEBUG(2,("could not find backend %s\n", backend_entry->module_name));
+
+ return NT_STATUS_INVALID_PARAMETER;
+}
+
+static NTSTATUS sam_context_check_default_backends(SAM_CONTEXT *context)
+{
+ SAM_BACKEND_ENTRY entry;
+ DOM_SID *global_sam_sid = get_global_sam_sid(); /* lp_workgroup doesn't play nicely with multiple domains */
+ SAM_METHODS *methods, *tmpmethods;
+ NTSTATUS ntstatus;
+
+ DEBUG(5,("sam_context_check_default_backends: %d\n", __LINE__));
+
+ /* Make sure domain lp_workgroup() is available */
+
+ ntstatus = sam_get_methods_by_sid(context, &methods, &global_sid_Builtin);
+
+ if (NT_STATUS_EQUAL(ntstatus, NT_STATUS_NO_SUCH_DOMAIN)) {
+ DEBUG(4,("There was no backend specified for domain %s(%s); using %s\n",
+ lp_workgroup(), sid_string_static(global_sam_sid), SAM_DEFAULT_BACKEND));
+
+ SAM_ASSERT(global_sam_sid);
+
+ entry.module_name = SAM_DEFAULT_BACKEND;
+ entry.module_params = NULL;
+ entry.domain_name = lp_workgroup();
+ entry.domain_sid = (DOM_SID *)malloc(sizeof(DOM_SID));
+ sid_copy(entry.domain_sid, global_sam_sid);
+
+ if (!NT_STATUS_IS_OK(ntstatus = make_sam_methods_backend_entry(context, &methods, &entry))) {
+ DEBUG(4,("make_sam_methods_backend_entry failed\n"));
+ return ntstatus;
+ }
+
+ DLIST_ADD_END(context->methods, methods, tmpmethods);
+
+ } else if (!NT_STATUS_IS_OK(ntstatus)) {
+ DEBUG(2, ("sam_get_methods_by_sid failed for %s\n", lp_workgroup()));
+ return ntstatus;
+ }
+
+ /* Make sure the BUILTIN domain is available */
+
+ ntstatus = sam_get_methods_by_sid(context, &methods, global_sam_sid);
+
+ if (NT_STATUS_EQUAL(ntstatus, NT_STATUS_NO_SUCH_DOMAIN)) {
+ DEBUG(4,("There was no backend specified for domain BUILTIN; using %s\n",
+ SAM_DEFAULT_BACKEND));
+ entry.module_name = SAM_DEFAULT_BACKEND;
+ entry.module_params = NULL;
+ entry.domain_name = "BUILTIN";
+ entry.domain_sid = (DOM_SID *)malloc(sizeof(DOM_SID));
+ sid_copy(entry.domain_sid, &global_sid_Builtin);
+
+ if (!NT_STATUS_IS_OK(ntstatus = make_sam_methods_backend_entry(context, &methods, &entry))) {
+ DEBUG(4,("make_sam_methods_backend_entry failed\n"));
+ return ntstatus;
+ }
+
+ DLIST_ADD_END(context->methods, methods, tmpmethods);
+ } else if (!NT_STATUS_IS_OK(ntstatus)) {
+ DEBUG(2, ("sam_get_methods_by_sid failed for BUILTIN\n"));
+ return ntstatus;
+ }
+
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS check_duplicate_backend_entries(SAM_BACKEND_ENTRY **backend_entries, int *nBackends)
+{
+ int i, j;
+
+ DEBUG(5,("check_duplicate_backend_entries: %d\n", __LINE__));
+
+ for (i = 0; i < *nBackends; i++) {
+ for (j = i + 1; j < *nBackends; j++) {
+ if (sid_equal((*backend_entries)[i].domain_sid, (*backend_entries)[j].domain_sid)) {
+ DEBUG(0,("two backend modules claim the same domain %s\n",
+ sid_string_static((*backend_entries)[j].domain_sid)));
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+ }
+ }
+
+ return NT_STATUS_OK;
+}
+
+NTSTATUS make_sam_context_list(SAM_CONTEXT **context, char **sam_backends_param)
+{
+ int i = 0, j = 0;
+ SAM_METHODS *curmethods, *tmpmethods;
+ int nBackends = 0;
+ SAM_BACKEND_ENTRY *backends = NULL;
+ NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
+
+ DEBUG(5,("make_sam_context_from_conf: %d\n", __LINE__));
+
+ if (!sam_backends_param) {
+ DEBUG(1, ("no SAM backeds specified!\n"));
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ if (!NT_STATUS_IS_OK(nt_status = make_sam_context(context))) {
+ DEBUG(4,("make_sam_context failed\n"));
+ return nt_status;
+ }
+
+ while (sam_backends_param[nBackends])
+ nBackends++;
+
+ DEBUG(6,("There are %d domains listed with their backends\n", nBackends));
+
+ if ((backends = (SAM_BACKEND_ENTRY *)malloc(sizeof(*backends)*nBackends)) == NULL) {
+ DEBUG(0,("make_sam_context_list: failed to allocate backends\n"));
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ memset(backends, '\0', sizeof(*backends)*nBackends);
+
+ for (i = 0; i < nBackends; i++) {
+ DEBUG(8,("processing %s\n",sam_backends_param[i]));
+ if (!NT_STATUS_IS_OK(nt_status = make_backend_entry(&backends[i], sam_backends_param[i]))) {
+ DEBUG(4,("make_backend_entry failed\n"));
+ for (j = 0; j < nBackends; j++) SAFE_FREE(backends[j].domain_sid);
+ SAFE_FREE(backends);
+ free_sam_context(context);
+ return nt_status;
+ }
+ }
+
+ if (!NT_STATUS_IS_OK(nt_status = check_duplicate_backend_entries(&backends, &nBackends))) {
+ DEBUG(4,("check_duplicate_backend_entries failed\n"));
+ for (j = 0; j < nBackends; j++) SAFE_FREE(backends[j].domain_sid);
+ SAFE_FREE(backends);
+ free_sam_context(context);
+ return nt_status;
+ }
+
+ for (i = 0; i < nBackends; i++) {
+ if (!NT_STATUS_IS_OK(nt_status = make_sam_methods_backend_entry(*context, &curmethods, &backends[i]))) {
+ DEBUG(4,("make_sam_methods_backend_entry failed\n"));
+ for (j = 0; j < nBackends; j++) SAFE_FREE(backends[j].domain_sid);
+ SAFE_FREE(backends);
+ free_sam_context(context);
+ return nt_status;
+ }
+ DLIST_ADD_END((*context)->methods, curmethods, tmpmethods);
+ }
+
+ for (i = 0; i < nBackends; i++) SAFE_FREE(backends[i].domain_sid);
+
+ SAFE_FREE(backends);
+ return NT_STATUS_OK;
+}
+
+/******************************************************************
+ Make a sam_context from scratch.
+ *******************************************************************/
+
+NTSTATUS make_sam_context(SAM_CONTEXT **context)
+{
+ TALLOC_CTX *mem_ctx;
+
+ mem_ctx = talloc_init("sam_context internal allocation context");
+
+ if (!mem_ctx) {
+ DEBUG(0, ("make_sam_context: talloc init failed!\n"));
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ *context = talloc(mem_ctx, sizeof(**context));
+ if (!*context) {
+ DEBUG(0, ("make_sam_context: talloc failed!\n"));
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ ZERO_STRUCTP(*context);
+
+ (*context)->mem_ctx = mem_ctx;
+
+ (*context)->free_fn = free_sam_context;
+
+ return NT_STATUS_OK;
+}
+
+/******************************************************************
+ Return an already initialised sam_context, to facilitate backward
+ compatibility (see functions below).
+ *******************************************************************/
+
+static struct sam_context *sam_get_static_context(BOOL reload)
+{
+ static SAM_CONTEXT *sam_context = NULL;
+
+ if ((sam_context) && (reload)) {
+ sam_context->free_fn(&sam_context);
+ sam_context = NULL;
+ }
+
+ if (!sam_context) {
+ if (!NT_STATUS_IS_OK(make_sam_context_list(&sam_context, lp_sam_backend()))) {
+ DEBUG(4,("make_sam_context_list failed\n"));
+ return NULL;
+ }
+
+ /* Make sure the required domains (default domain, builtin) are available */
+ if (!NT_STATUS_IS_OK(sam_context_check_default_backends(sam_context))) {
+ DEBUG(4,("sam_context_check_default_backends failed\n"));
+ return NULL;
+ }
+ }
+
+ return sam_context;
+}
+
+/***************************************************************
+ Initialize the static context (at smbd startup etc).
+
+ If uninitialised, context will auto-init on first use.
+ ***************************************************************/
+
+BOOL initialize_sam(BOOL reload)
+{
+ return (sam_get_static_context(reload) != NULL);
+}
+
+
+/**************************************************************
+ External API. This is what the rest of the world calls...
+***************************************************************/
+
+/******************************************************************
+ sam_* functions are used to link the external SAM interface
+ with the internal backends. These functions lookup the appropriate
+ backends for the domain and pass on to the function in sam_methods
+ in the selected backend
+
+ When the context parmater is NULL, the default is used.
+ *******************************************************************/
+
+#define SAM_SETUP_CONTEXT if (!context) \
+ context = sam_get_static_context(False);\
+ if (!context) {\
+ return NT_STATUS_UNSUCCESSFUL; \
+ }\
+
+
+
+NTSTATUS sam_get_sec_desc(const SAM_CONTEXT *context, const NT_USER_TOKEN *access_token, const DOM_SID *sid, SEC_DESC **sd)
+{
+ SAM_METHODS *tmp_methods;
+ NTSTATUS nt_status;
+
+ DEBUG(5,("sam_get_sec_desc: %d\n", __LINE__));
+
+ SAM_SETUP_CONTEXT;
+
+ if (!NT_STATUS_IS_OK(nt_status = sam_get_methods_by_sid(context, &tmp_methods, sid))) {
+ DEBUG(4,("sam_get_methods_by_sid failed\n"));
+ return nt_status;
+ }
+
+ if (!tmp_methods->sam_get_sec_desc) {
+ DEBUG(3, ("sam_get_sec_desc: sam_methods of the domain did not specify sam_get_sec_desc\n"));
+ return NT_STATUS_NOT_IMPLEMENTED;
+ }
+
+ if (!NT_STATUS_IS_OK(nt_status = tmp_methods->sam_get_sec_desc(tmp_methods, access_token, sid, sd))) {
+ DEBUG(4,("sam_get_sec_desc for %s in backend %s failed\n", sid_string_static(sid), tmp_methods->backendname));
+ return nt_status;
+ }
+
+ return NT_STATUS_OK;
+}
+
+NTSTATUS sam_set_sec_desc(const SAM_CONTEXT *context, const NT_USER_TOKEN *access_token, const DOM_SID *sid, const SEC_DESC *sd)
+{
+ SAM_METHODS *tmp_methods;
+ NTSTATUS nt_status;
+
+ DEBUG(5,("sam_set_sec_desc: %d\n", __LINE__));
+
+ SAM_SETUP_CONTEXT;
+
+ if (!NT_STATUS_IS_OK(nt_status = sam_get_methods_by_sid(context, &tmp_methods, sid))) {
+ DEBUG(4,("sam_get_methods_by_sid failed\n"));
+ return nt_status;
+ }
+
+ if (!tmp_methods->sam_set_sec_desc) {
+ DEBUG(3, ("sam_set_sec_desc: sam_methods of the domain did not specify sam_set_sec_desc\n"));
+ return NT_STATUS_NOT_IMPLEMENTED;
+ }
+
+ if (!NT_STATUS_IS_OK(nt_status = tmp_methods->sam_set_sec_desc(tmp_methods, access_token, sid, sd))) {
+ DEBUG(4,("sam_set_sec_desc for %s in backend %s failed\n", sid_string_static(sid), tmp_methods->backendname));
+ return nt_status;
+ }
+
+ return NT_STATUS_OK;
+}
+
+
+NTSTATUS sam_lookup_name(const SAM_CONTEXT *context, const NT_USER_TOKEN *access_token, const char *domain, const char *name, DOM_SID *sid, uint32 *type)
+{
+ SAM_METHODS *tmp_methods;
+ NTSTATUS nt_status;
+
+ DEBUG(5,("sam_lookup_name: %d\n", __LINE__));
+
+ SAM_SETUP_CONTEXT;
+
+ if (!NT_STATUS_IS_OK(nt_status = sam_get_methods_by_name(context, &tmp_methods, domain))) {
+ DEBUG(4,("sam_get_methods_by_name failed\n"));
+ return nt_status;
+ }
+
+ if (!tmp_methods->sam_lookup_name) {
+ DEBUG(3, ("sam_lookup_name: sam_methods of the domain did not specify sam_lookup_name\n"));
+ return NT_STATUS_NOT_IMPLEMENTED;
+ }
+
+ if (!NT_STATUS_IS_OK(nt_status = tmp_methods->sam_lookup_name(tmp_methods, access_token, name, sid, type))) {
+ DEBUG(4,("sam_lookup_name for %s\\%s in backend %s failed\n",
+ tmp_methods->domain_name, name, tmp_methods->backendname));
+ return nt_status;
+ }
+
+ return NT_STATUS_OK;
+}
+
+NTSTATUS sam_lookup_sid(const SAM_CONTEXT *context, const NT_USER_TOKEN *access_token, TALLOC_CTX *mem_ctx, const DOM_SID *sid, char **name, uint32 *type)
+{
+ SAM_METHODS *tmp_methods;
+ uint32 rid;
+ NTSTATUS nt_status;
+ DOM_SID domainsid;
+
+ DEBUG(5,("sam_lookup_sid: %d\n", __LINE__));
+
+ SAM_SETUP_CONTEXT;
+
+ sid_copy(&domainsid, sid);
+ if (!sid_split_rid(&domainsid, &rid)) {
+ DEBUG(3,("sam_lookup_sid: failed to split the sid\n"));
+ return NT_STATUS_INVALID_SID;
+ }
+
+ if (!NT_STATUS_IS_OK(nt_status = sam_get_methods_by_sid(context, &tmp_methods, &domainsid))) {
+ DEBUG(4,("sam_get_methods_by_sid failed\n"));
+ return nt_status;
+ }
+
+ if (!tmp_methods->sam_lookup_sid) {
+ DEBUG(3, ("sam_lookup_sid: sam_methods of the domain did not specify sam_lookup_sid\n"));
+ return NT_STATUS_NOT_IMPLEMENTED;
+ }
+
+ if (!NT_STATUS_IS_OK(nt_status = tmp_methods->sam_lookup_sid(tmp_methods, access_token, mem_ctx, sid, name, type))) {
+ DEBUG(4,("sam_lookup_name for %s in backend %s failed\n",
+ sid_string_static(sid), tmp_methods->backendname));
+ return nt_status;
+ }
+
+ return NT_STATUS_OK;
+}
+
+
+NTSTATUS sam_update_domain(const SAM_CONTEXT *context, const SAM_DOMAIN_HANDLE *domain)
+{
+ const SAM_METHODS *tmp_methods;
+ NTSTATUS nt_status;
+
+ DEBUG(5,("sam_update_domain: %d\n", __LINE__));
+
+ SAM_SETUP_CONTEXT;
+
+ /* invalid domain specified */
+ SAM_ASSERT(domain && domain->current_sam_methods);
+
+ tmp_methods = domain->current_sam_methods;
+
+ if (!tmp_methods->sam_update_domain) {
+ DEBUG(3, ("sam_update_domain: sam_methods of the domain did not specify sam_update_domain\n"));
+ return NT_STATUS_NOT_IMPLEMENTED;
+ }
+
+ if (!NT_STATUS_IS_OK(nt_status = tmp_methods->sam_update_domain(tmp_methods, domain))){
+ DEBUG(4,("sam_update_domain in backend %s failed\n",
+ tmp_methods->backendname));
+ return nt_status;
+ }
+
+ return NT_STATUS_OK;
+}
+
+NTSTATUS sam_enum_domains(const SAM_CONTEXT *context, const NT_USER_TOKEN *access_token, int32 *domain_count, DOM_SID **domains, char ***domain_names)
+{
+ SAM_METHODS *tmp_methods;
+ NTSTATUS nt_status;
+
+ SEC_DESC *sd;
+ size_t sd_size;
+ uint32 acc_granted;
+ int i = 0;
+
+ DEBUG(5,("sam_enum_domains: %d\n", __LINE__));
+
+ SAM_SETUP_CONTEXT;
+
+ /* invalid parmaters specified */
+ SAM_ASSERT(domain_count && domains && domain_names);
+
+ if (!NT_STATUS_IS_OK(nt_status = samr_make_sam_obj_sd(context->mem_ctx, &sd, &sd_size))) {
+ DEBUG(4,("samr_make_sam_obj_sd failed\n"));
+ return nt_status;
+ }
+
+ if (!se_access_check(sd, access_token, SA_RIGHT_SAM_ENUM_DOMAINS, &acc_granted, &nt_status)) {
+ DEBUG(3,("sam_enum_domains: ACCESS DENIED\n"));
+ return nt_status;
+ }
+
+ tmp_methods= context->methods;
+ *domain_count = 0;
+
+ while (tmp_methods) {
+ (*domain_count)++;
+ tmp_methods= tmp_methods->next;
+ }
+
+ DEBUG(6,("sam_enum_domains: enumerating %d domains\n", (*domain_count)));
+
+ tmp_methods = context->methods;
+
+ if (((*domains) = malloc( sizeof(DOM_SID) * (*domain_count))) == NULL) {
+ DEBUG(0,("sam_enum_domains: Out of memory allocating domain SID list\n"));
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ if (((*domain_names) = malloc( sizeof(char*) * (*domain_count))) == NULL) {
+ DEBUG(0,("sam_enum_domains: Out of memory allocating domain name list\n"));
+ SAFE_FREE((*domains));
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ while (tmp_methods) {
+ DEBUGADD(7,(" [%d] %s: %s\n", i, tmp_methods->domain_name, sid_string_static(&tmp_methods->domain_sid)));
+ sid_copy(domains[i],&tmp_methods->domain_sid);
+ *domain_names[i] = smb_xstrdup(tmp_methods->domain_name);
+ i++;
+ tmp_methods= tmp_methods->next;
+ }
+
+ return NT_STATUS_OK;
+}
+
+NTSTATUS sam_lookup_domain(const SAM_CONTEXT *context, const NT_USER_TOKEN *access_token, const char *domain, DOM_SID **domainsid)
+{
+ SAM_METHODS *tmp_methods;
+ NTSTATUS nt_status;
+
+ SEC_DESC *sd;
+ size_t sd_size;
+ uint32 acc_granted;
+
+ DEBUG(5,("sam_lookup_domain: %d\n", __LINE__));
+
+ SAM_SETUP_CONTEXT;
+
+ /* invalid paramters */
+ SAM_ASSERT(access_token && domain && domainsid);
+
+ if (!NT_STATUS_IS_OK(nt_status = samr_make_sam_obj_sd(context->mem_ctx, &sd, &sd_size))) {
+ DEBUG(4,("samr_make_sam_obj_sd failed\n"));
+ return nt_status;
+ }
+
+ if (!se_access_check(sd, access_token, SA_RIGHT_SAM_OPEN_DOMAIN, &acc_granted, &nt_status)) {
+ DEBUG(3,("sam_lookup_domain: ACCESS DENIED\n"));
+ return nt_status;
+ }
+
+ tmp_methods= context->methods;
+
+ while (tmp_methods) {
+ if (strcmp(domain, tmp_methods->domain_name) == 0) {
+ (*domainsid) = (DOM_SID *)malloc(sizeof(DOM_SID));
+ sid_copy((*domainsid), &tmp_methods->domain_sid);
+ return NT_STATUS_OK;
+ }
+ tmp_methods= tmp_methods->next;
+ }
+
+ return NT_STATUS_NO_SUCH_DOMAIN;
+}
+
+
+NTSTATUS sam_get_domain_by_sid(const SAM_CONTEXT *context, const NT_USER_TOKEN *access_token, uint32 access_desired, const DOM_SID *domainsid, SAM_DOMAIN_HANDLE **domain)
+{
+ SAM_METHODS *tmp_methods;
+ NTSTATUS nt_status;
+
+ DEBUG(5,("sam_get_domain_by_sid: %d\n", __LINE__));
+
+ SAM_SETUP_CONTEXT;
+
+ SAM_ASSERT(access_token && domainsid && domain);
+
+ if (!NT_STATUS_IS_OK(nt_status = sam_get_methods_by_sid(context, &tmp_methods, domainsid))) {
+ DEBUG(4,("sam_get_methods_by_sid failed\n"));
+ return nt_status;
+ }
+
+ if (!tmp_methods->sam_get_domain_handle) {
+ DEBUG(3, ("sam_get_domain_by_sid: sam_methods of the domain did not specify sam_get_domain_handle\n"));
+ return NT_STATUS_NOT_IMPLEMENTED;
+ }
+
+ if (!NT_STATUS_IS_OK(nt_status = tmp_methods->sam_get_domain_handle(tmp_methods, access_token, access_desired, domain))) {
+ DEBUG(4,("sam_get_domain_handle for %s in backend %s failed\n",
+ sid_string_static(domainsid), tmp_methods->backendname));
+ return nt_status;
+ }
+
+ return NT_STATUS_OK;
+}
+
+NTSTATUS sam_create_account(const SAM_CONTEXT *context, const NT_USER_TOKEN *access_token, uint32 access_desired, const DOM_SID *domainsid, const char *account_name, uint16 acct_ctrl, SAM_ACCOUNT_HANDLE **account)
+{
+ SAM_METHODS *tmp_methods;
+ NTSTATUS nt_status;
+
+ DEBUG(5,("sam_create_account: %d\n", __LINE__));
+
+ SAM_SETUP_CONTEXT;
+
+ /* invalid parmaters */
+ SAM_ASSERT(access_token && domainsid && account_name && account);
+
+ if (!NT_STATUS_IS_OK(nt_status = sam_get_methods_by_sid(context, &tmp_methods, domainsid))) {
+ DEBUG(4,("sam_get_methods_by_sid failed\n"));
+ return nt_status;
+ }
+
+ if (!tmp_methods->sam_create_account) {
+ DEBUG(3, ("sam_create_account: sam_methods of the domain did not specify sam_create_account\n"));
+ return NT_STATUS_NOT_IMPLEMENTED;
+ }
+
+ if (!NT_STATUS_IS_OK(nt_status = tmp_methods->sam_create_account(tmp_methods, access_token, access_desired, account_name, acct_ctrl, account))) {
+ DEBUG(4,("sam_create_account in backend %s failed\n",
+ tmp_methods->backendname));
+ return nt_status;
+ }
+
+ return NT_STATUS_OK;
+}
+
+NTSTATUS sam_add_account(const SAM_CONTEXT *context, const SAM_ACCOUNT_HANDLE *account)
+{
+ DOM_SID domainsid;
+ const DOM_SID *accountsid;
+ SAM_METHODS *tmp_methods;
+ uint32 rid;
+ NTSTATUS nt_status;
+
+ DEBUG(5,("sam_add_account: %d\n", __LINE__));
+
+ SAM_SETUP_CONTEXT;
+
+ /* invalid parmaters */
+ SAM_ASSERT(account);
+
+ if (!NT_STATUS_IS_OK(nt_status = sam_get_account_sid(account, &accountsid))) {
+ DEBUG(0,("Can't get account SID\n"));
+ return nt_status;
+ }
+
+ sid_copy(&domainsid, accountsid);
+ if (!sid_split_rid(&domainsid, &rid)) {
+ DEBUG(3,("sam_get_account_by_sid: failed to split the sid\n"));
+ return NT_STATUS_INVALID_SID;
+ }
+
+ if (!NT_STATUS_IS_OK(nt_status = sam_get_methods_by_sid(context, &tmp_methods, &domainsid))) {
+ DEBUG(4,("sam_get_methods_by_sid failed\n"));
+ return nt_status;
+ }
+
+ if (!tmp_methods->sam_add_account) {
+ DEBUG(3, ("sam_add_account: sam_methods of the domain did not specify sam_add_account\n"));
+ return NT_STATUS_NOT_IMPLEMENTED;
+ }
+
+ if (!NT_STATUS_IS_OK(nt_status = tmp_methods->sam_add_account(tmp_methods, account))){
+ DEBUG(4,("sam_add_account in backend %s failed\n",
+ tmp_methods->backendname));
+ return nt_status;
+ }
+
+ return NT_STATUS_OK;
+}
+
+NTSTATUS sam_update_account(const SAM_CONTEXT *context, const SAM_ACCOUNT_HANDLE *account)
+{
+ const SAM_METHODS *tmp_methods;
+ NTSTATUS nt_status;
+
+ DEBUG(5,("sam_update_account: %d\n", __LINE__));
+
+ SAM_SETUP_CONTEXT;
+
+ /* invalid account specified */
+ SAM_ASSERT(account && account->current_sam_methods);
+
+ tmp_methods = account->current_sam_methods;
+
+ if (!tmp_methods->sam_update_account) {
+ DEBUG(3, ("sam_update_account: sam_methods of the domain did not specify sam_update_account\n"));
+ return NT_STATUS_NOT_IMPLEMENTED;
+ }
+
+ if (!NT_STATUS_IS_OK(nt_status = tmp_methods->sam_update_account(tmp_methods, account))){
+ DEBUG(4,("sam_update_account in backend %s failed\n",
+ tmp_methods->backendname));
+ return nt_status;
+ }
+
+ return NT_STATUS_OK;
+}
+
+NTSTATUS sam_delete_account(const SAM_CONTEXT *context, const SAM_ACCOUNT_HANDLE *account)
+{
+ const SAM_METHODS *tmp_methods;
+ NTSTATUS nt_status;
+
+ DEBUG(5,("sam_delete_account: %d\n", __LINE__));
+
+ SAM_SETUP_CONTEXT;
+
+ /* invalid account specified */
+ SAM_ASSERT(account && account->current_sam_methods);
+
+ tmp_methods = account->current_sam_methods;
+
+ if (!tmp_methods->sam_delete_account) {
+ DEBUG(3, ("sam_delete_account: sam_methods of the domain did not specify sam_delete_account\n"));
+ return NT_STATUS_NOT_IMPLEMENTED;
+ }
+
+ if (!NT_STATUS_IS_OK(nt_status = tmp_methods->sam_delete_account(tmp_methods, account))){
+ DEBUG(4,("sam_delete_account in backend %s failed\n",
+ tmp_methods->backendname));
+ return nt_status;
+ }
+
+ return NT_STATUS_OK;
+}
+
+NTSTATUS sam_enum_accounts(const SAM_CONTEXT *context, const NT_USER_TOKEN *access_token, const DOM_SID *domainsid, uint16 acct_ctrl, int32 *account_count, SAM_ACCOUNT_ENUM **accounts)
+{
+ SAM_METHODS *tmp_methods;
+ NTSTATUS nt_status;
+
+ DEBUG(5,("sam_enum_accounts: %d\n", __LINE__));
+
+ SAM_SETUP_CONTEXT;
+
+ SAM_ASSERT(access_token && domainsid && account_count && accounts);
+
+ if (!NT_STATUS_IS_OK(nt_status = sam_get_methods_by_sid(context, &tmp_methods, domainsid))) {
+ DEBUG(4,("sam_get_methods_by_sid failed\n"));
+ return nt_status;
+ }
+
+ if (!tmp_methods->sam_enum_accounts) {
+ DEBUG(3, ("sam_enum_accounts: sam_methods of the domain did not specify sam_enum_accounts\n"));
+ return NT_STATUS_NOT_IMPLEMENTED;
+ }
+
+ if (!NT_STATUS_IS_OK(nt_status = tmp_methods->sam_enum_accounts(tmp_methods, access_token, acct_ctrl, account_count, accounts))) {
+ DEBUG(4,("sam_enum_accounts for domain %s in backend %s failed\n",
+ tmp_methods->domain_name, tmp_methods->backendname));
+ return nt_status;
+ }
+
+ return NT_STATUS_OK;
+}
+
+
+NTSTATUS sam_get_account_by_sid(const SAM_CONTEXT *context, const NT_USER_TOKEN *access_token, uint32 access_desired, const DOM_SID *accountsid, SAM_ACCOUNT_HANDLE **account)
+{
+ SAM_METHODS *tmp_methods;
+ uint32 rid;
+ DOM_SID domainsid;
+ NTSTATUS nt_status;
+
+ DEBUG(5,("sam_get_account_by_sid: %d\n", __LINE__));
+
+ SAM_SETUP_CONTEXT;
+
+ SAM_ASSERT(access_token && accountsid && account);
+
+ sid_copy(&domainsid, accountsid);
+ if (!sid_split_rid(&domainsid, &rid)) {
+ DEBUG(3,("sam_get_account_by_sid: failed to split the sid\n"));
+ return NT_STATUS_INVALID_SID;
+ }
+
+
+ if (!NT_STATUS_IS_OK(nt_status = sam_get_methods_by_sid(context, &tmp_methods, &domainsid))) {
+ DEBUG(4,("sam_get_methods_by_sid failed\n"));
+ return nt_status;
+ }
+
+ if (!tmp_methods->sam_get_account_by_sid) {
+ DEBUG(3, ("sam_get_account_by_sid: sam_methods of the domain did not specify sam_get_account_by_sid\n"));
+ return NT_STATUS_NOT_IMPLEMENTED;
+ }
+
+ if (!NT_STATUS_IS_OK(nt_status = tmp_methods->sam_get_account_by_sid(tmp_methods, access_token, access_desired, accountsid, account))) {
+ DEBUG(4,("sam_get_account_by_sid for %s in backend %s failed\n",
+ sid_string_static(accountsid), tmp_methods->backendname));
+ return nt_status;
+ }
+
+ return NT_STATUS_OK;
+}
+
+NTSTATUS sam_get_account_by_name(const SAM_CONTEXT *context, const NT_USER_TOKEN *access_token, uint32 access_desired, const char *domain, const char *name, SAM_ACCOUNT_HANDLE **account)
+{
+ SAM_METHODS *tmp_methods;
+ NTSTATUS nt_status;
+
+ DEBUG(5,("sam_get_account_by_name: %d\n", __LINE__));
+
+ SAM_SETUP_CONTEXT;
+
+ SAM_ASSERT(access_token && domain && name && account);
+
+ if (!NT_STATUS_IS_OK(nt_status = sam_get_methods_by_name(context, &tmp_methods, domain))) {
+ DEBUG(4,("sam_get_methods_by_name failed\n"));
+ return nt_status;
+ }
+
+ if (!tmp_methods->sam_get_account_by_name) {
+ DEBUG(3, ("sam_get_account_by_name: sam_methods of the domain did not specify sam_get_account_by_name\n"));
+ return NT_STATUS_NOT_IMPLEMENTED;
+ }
+
+ if (!NT_STATUS_IS_OK(nt_status = tmp_methods->sam_get_account_by_name(tmp_methods, access_token, access_desired, name, account))) {
+ DEBUG(4,("sam_get_account_by_name for %s\\%s in backend %s failed\n",
+ domain, name, tmp_methods->backendname));
+ return nt_status;
+ }
+
+ return NT_STATUS_OK;
+}
+
+NTSTATUS sam_create_group(const SAM_CONTEXT *context, const NT_USER_TOKEN *access_token, uint32 access_desired, const DOM_SID *domainsid, const char *group_name, uint16 group_ctrl, SAM_GROUP_HANDLE **group)
+{
+ SAM_METHODS *tmp_methods;
+ NTSTATUS nt_status;
+
+ DEBUG(5,("sam_create_group: %d\n", __LINE__));
+
+ SAM_SETUP_CONTEXT;
+
+ SAM_ASSERT(access_token && domainsid && group_name && group);
+
+ if (!NT_STATUS_IS_OK(nt_status = sam_get_methods_by_sid(context, &tmp_methods, domainsid))) {
+ DEBUG(4,("sam_get_methods_by_sid failed\n"));
+ return nt_status;
+ }
+
+ if (!tmp_methods->sam_create_group) {
+ DEBUG(3, ("sam_create_group: sam_methods of the domain did not specify sam_create_group\n"));
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ if (!NT_STATUS_IS_OK(nt_status = tmp_methods->sam_create_group(tmp_methods, access_token, access_desired, group_name, group_ctrl, group))) {
+ DEBUG(4,("sam_create_group in backend %s failed\n",
+ tmp_methods->backendname));
+ return nt_status;
+ }
+
+ return NT_STATUS_OK;
+}
+
+NTSTATUS sam_add_group(const SAM_CONTEXT *context, const SAM_GROUP_HANDLE *group)
+{
+ DOM_SID domainsid;
+ const DOM_SID *groupsid;
+ SAM_METHODS *tmp_methods;
+ uint32 rid;
+ NTSTATUS nt_status;
+
+ DEBUG(5,("sam_add_group: %d\n", __LINE__));
+
+ SAM_SETUP_CONTEXT;
+
+ SAM_ASSERT(group);
+
+ if (!NT_STATUS_IS_OK(nt_status = sam_get_group_sid(group, &groupsid))) {
+ DEBUG(0,("Can't get group SID\n"));
+ return nt_status;
+ }
+
+ sid_copy(&domainsid, groupsid);
+ if (!sid_split_rid(&domainsid, &rid)) {
+ DEBUG(3,("sam_get_group_by_sid: failed to split the sid\n"));
+ return NT_STATUS_INVALID_SID;
+ }
+
+ if (!NT_STATUS_IS_OK(nt_status = sam_get_methods_by_sid(context, &tmp_methods, &domainsid))) {
+ DEBUG(4,("sam_get_methods_by_sid failed\n"));
+ return nt_status;
+ }
+
+ if (!tmp_methods->sam_add_group) {
+ DEBUG(3, ("sam_add_group: sam_methods of the domain did not specify sam_add_group\n"));
+ return NT_STATUS_NOT_IMPLEMENTED;
+ }
+
+ if (!NT_STATUS_IS_OK(nt_status = tmp_methods->sam_add_group(tmp_methods, group))){
+ DEBUG(4,("sam_add_group in backend %s failed\n",
+ tmp_methods->backendname));
+ return nt_status;
+ }
+
+ return NT_STATUS_OK;
+}
+
+NTSTATUS sam_update_group(const SAM_CONTEXT *context, const SAM_GROUP_HANDLE *group)
+{
+ const SAM_METHODS *tmp_methods;
+ NTSTATUS nt_status;
+
+ DEBUG(5,("sam_update_group: %d\n", __LINE__));
+
+ SAM_SETUP_CONTEXT;
+
+ /* invalid group specified */
+ SAM_ASSERT(group && group->current_sam_methods);
+
+ tmp_methods = group->current_sam_methods;
+
+ if (!tmp_methods->sam_update_group) {
+ DEBUG(3, ("sam_update_group: sam_methods of the domain did not specify sam_update_group\n"));
+ return NT_STATUS_NOT_IMPLEMENTED;
+ }
+
+ if (!NT_STATUS_IS_OK(nt_status = tmp_methods->sam_update_group(tmp_methods, group))){
+ DEBUG(4,("sam_update_group in backend %s failed\n",
+ tmp_methods->backendname));
+ return nt_status;
+ }
+
+ return NT_STATUS_OK;
+}
+
+NTSTATUS sam_delete_group(const SAM_CONTEXT *context, const SAM_GROUP_HANDLE *group)
+{
+ const SAM_METHODS *tmp_methods;
+ NTSTATUS nt_status;
+
+ DEBUG(5,("sam_delete_group: %d\n", __LINE__));
+
+ SAM_SETUP_CONTEXT;
+
+ /* invalid group specified */
+ SAM_ASSERT(group && group->current_sam_methods);
+
+ tmp_methods = group->current_sam_methods;
+
+ if (!tmp_methods->sam_delete_group) {
+ DEBUG(3, ("sam_delete_group: sam_methods of the domain did not specify sam_delete_group\n"));
+ return NT_STATUS_NOT_IMPLEMENTED;
+ }
+
+ if (!NT_STATUS_IS_OK(nt_status = tmp_methods->sam_delete_group(tmp_methods, group))){
+ DEBUG(4,("sam_delete_group in backend %s failed\n",
+ tmp_methods->backendname));
+ return nt_status;
+ }
+
+ return NT_STATUS_OK;
+}
+
+NTSTATUS sam_enum_groups(const SAM_CONTEXT *context, const NT_USER_TOKEN *access_token, const DOM_SID *domainsid, uint16 group_ctrl, uint32 *groups_count, SAM_GROUP_ENUM **groups)
+{
+ SAM_METHODS *tmp_methods;
+ NTSTATUS nt_status;
+
+ DEBUG(5,("sam_enum_groups: %d\n", __LINE__));
+
+ SAM_SETUP_CONTEXT;
+
+ SAM_ASSERT(access_token && domainsid && groups_count && groups);
+
+ if (!NT_STATUS_IS_OK(nt_status = sam_get_methods_by_sid(context, &tmp_methods, domainsid))) {
+ DEBUG(4,("sam_get_methods_by_sid failed\n"));
+ return nt_status;
+ }
+
+ if (!tmp_methods->sam_enum_accounts) {
+ DEBUG(3, ("sam_enum_groups: sam_methods of the domain did not specify sam_enum_groups\n"));
+ return NT_STATUS_NOT_IMPLEMENTED;
+ }
+
+ if (!NT_STATUS_IS_OK(nt_status = tmp_methods->sam_enum_groups(tmp_methods, access_token, group_ctrl, groups_count, groups))) {
+ DEBUG(4,("sam_enum_groups for domain %s in backend %s failed\n",
+ tmp_methods->domain_name, tmp_methods->backendname));
+ return nt_status;
+ }
+
+ return NT_STATUS_OK;
+}
+
+NTSTATUS sam_get_group_by_sid(const SAM_CONTEXT *context, const NT_USER_TOKEN *access_token, uint32 access_desired, const DOM_SID *groupsid, SAM_GROUP_HANDLE **group)
+{
+ SAM_METHODS *tmp_methods;
+ uint32 rid;
+ NTSTATUS nt_status;
+ DOM_SID domainsid;
+
+ DEBUG(5,("sam_get_group_by_sid: %d\n", __LINE__));
+
+ SAM_SETUP_CONTEXT;
+
+ SAM_ASSERT(access_token && groupsid && group);
+
+ sid_copy(&domainsid, groupsid);
+ if (!sid_split_rid(&domainsid, &rid)) {
+ DEBUG(3,("sam_get_group_by_sid: failed to split the sid\n"));
+ return NT_STATUS_INVALID_SID;
+ }
+
+
+ if (!NT_STATUS_IS_OK(nt_status = sam_get_methods_by_sid(context, &tmp_methods, &domainsid))) {
+ DEBUG(4,("sam_get_methods_by_sid failed\n"));
+ return nt_status;
+ }
+
+ if (!tmp_methods->sam_get_group_by_sid) {
+ DEBUG(3, ("sam_get_group_by_sid: sam_methods of the domain did not specify sam_get_group_by_sid\n"));
+ return NT_STATUS_NOT_IMPLEMENTED;
+ }
+
+ if (!NT_STATUS_IS_OK(nt_status = tmp_methods->sam_get_group_by_sid(tmp_methods, access_token, access_desired, groupsid, group))) {
+ DEBUG(4,("sam_get_group_by_sid for %s in backend %s failed\n",
+ sid_string_static(groupsid), tmp_methods->backendname));
+ return nt_status;
+ }
+
+ return NT_STATUS_OK;
+}
+
+NTSTATUS sam_get_group_by_name(const SAM_CONTEXT *context, const NT_USER_TOKEN *access_token, uint32 access_desired, const char *domain, const char *name, SAM_GROUP_HANDLE **group)
+{
+ SAM_METHODS *tmp_methods;
+ NTSTATUS nt_status;
+
+ DEBUG(5,("sam_get_group_by_name: %d\n", __LINE__));
+
+ SAM_SETUP_CONTEXT;
+
+ SAM_ASSERT(access_token && domain && name && group);
+
+ if (!NT_STATUS_IS_OK(nt_status = sam_get_methods_by_name(context, &tmp_methods, domain))) {
+ DEBUG(4,("sam_get_methods_by_name failed\n"));
+ return nt_status;
+ }
+
+ if (!tmp_methods->sam_get_group_by_name) {
+ DEBUG(3, ("sam_get_group_by_name: sam_methods of the domain did not specify sam_get_group_by_name\n"));
+ return NT_STATUS_NOT_IMPLEMENTED;
+ }
+
+ if (!NT_STATUS_IS_OK(nt_status = tmp_methods->sam_get_group_by_name(tmp_methods, access_token, access_desired, name, group))) {
+ DEBUG(4,("sam_get_group_by_name for %s\\%s in backend %s failed\n",
+ domain, name, tmp_methods->backendname));
+ return nt_status;
+ }
+
+ return NT_STATUS_OK;
+}
+
+NTSTATUS sam_add_member_to_group(const SAM_CONTEXT *context, const SAM_GROUP_HANDLE *group, const SAM_GROUP_MEMBER *member)
+{
+ const SAM_METHODS *tmp_methods;
+ NTSTATUS nt_status;
+
+ SAM_SETUP_CONTEXT;
+
+ /* invalid group or member specified */
+ SAM_ASSERT(group && group->current_sam_methods && member);
+
+ tmp_methods = group->current_sam_methods;
+
+ if (!tmp_methods->sam_add_member_to_group) {
+ DEBUG(3, ("sam_add_member_to_group: sam_methods of the domain did not specify sam_add_member_to_group\n"));
+ return NT_STATUS_NOT_IMPLEMENTED;
+ }
+
+ if (!NT_STATUS_IS_OK(nt_status = tmp_methods->sam_add_member_to_group(tmp_methods, group, member))) {
+ DEBUG(4,("sam_add_member_to_group in backend %s failed\n", tmp_methods->backendname));
+ return nt_status;
+ }
+
+ return NT_STATUS_OK;
+
+}
+
+NTSTATUS sam_delete_member_from_group(const SAM_CONTEXT *context, const SAM_GROUP_HANDLE *group, const SAM_GROUP_MEMBER *member)
+{
+ const SAM_METHODS *tmp_methods;
+ NTSTATUS nt_status;
+
+ SAM_SETUP_CONTEXT;
+
+ /* invalid group or member specified */
+ SAM_ASSERT(group && group->current_sam_methods && member);
+
+ tmp_methods = group->current_sam_methods;
+
+ if (!tmp_methods->sam_delete_member_from_group) {
+ DEBUG(3, ("sam_delete_member_from_group: sam_methods of the domain did not specify sam_delete_member_from_group\n"));
+ return NT_STATUS_NOT_IMPLEMENTED;
+ }
+
+ if (!NT_STATUS_IS_OK(nt_status = tmp_methods->sam_delete_member_from_group(tmp_methods, group, member))) {
+ DEBUG(4,("sam_delete_member_from_group in backend %s failed\n", tmp_methods->backendname));
+ return nt_status;
+ }
+
+ return NT_STATUS_OK;
+}
+
+NTSTATUS sam_enum_groupmembers(const SAM_CONTEXT *context, const SAM_GROUP_HANDLE *group, uint32 *members_count, SAM_GROUP_MEMBER **members)
+{
+ const SAM_METHODS *tmp_methods;
+ NTSTATUS nt_status;
+
+ SAM_SETUP_CONTEXT;
+
+ /* invalid group specified */
+ SAM_ASSERT(group && group->current_sam_methods && members_count && members);
+
+ tmp_methods = group->current_sam_methods;
+
+ if (!tmp_methods->sam_enum_groupmembers) {
+ DEBUG(3, ("sam_enum_groupmembers: sam_methods of the domain did not specify sam_enum_group_members\n"));
+ return NT_STATUS_NOT_IMPLEMENTED;
+ }
+
+ if (!NT_STATUS_IS_OK(nt_status = tmp_methods->sam_enum_groupmembers(tmp_methods, group, members_count, members))) {
+ DEBUG(4,("sam_enum_groupmembers in backend %s failed\n", tmp_methods->backendname));
+ return nt_status;
+ }
+
+ return NT_STATUS_OK;
+}
+
+NTSTATUS sam_get_groups_of_sid(const SAM_CONTEXT *context, const NT_USER_TOKEN *access_token, const DOM_SID **sids, uint16 group_ctrl, uint32 *group_count, SAM_GROUP_ENUM **groups)
+{
+ SAM_METHODS *tmp_methods;
+ NTSTATUS nt_status;
+
+ uint32 tmp_group_count;
+ SAM_GROUP_ENUM *tmp_groups;
+
+ DEBUG(5,("sam_get_groups_of_sid: %d\n", __LINE__));
+
+ SAM_SETUP_CONTEXT;
+
+ /* invalid sam_context specified */
+ SAM_ASSERT(access_token && sids && context && context->methods);
+
+ *group_count = 0;
+
+ *groups = NULL;
+
+ tmp_methods= context->methods;
+
+ while (tmp_methods) {
+ DEBUG(5,("getting groups from domain \n"));
+ if (!tmp_methods->sam_get_groups_of_sid) {
+ DEBUG(3, ("sam_get_groups_of_sid: sam_methods of domain did not specify sam_get_groups_of_sid\n"));
+ SAFE_FREE(*groups);
+ return NT_STATUS_NOT_IMPLEMENTED;
+ }
+
+ if (!NT_STATUS_IS_OK(nt_status = tmp_methods->sam_get_groups_of_sid(tmp_methods, access_token, sids, group_ctrl, &tmp_group_count, &tmp_groups))) {
+ DEBUG(4,("sam_get_groups_of_sid in backend %s failed\n", tmp_methods->backendname));
+ SAFE_FREE(*groups);
+ return nt_status;
+ }
+
+ *groups = Realloc(*groups, ((*group_count) + tmp_group_count) * sizeof(SAM_GROUP_ENUM));
+
+ memcpy(&(*groups)[*group_count], tmp_groups, tmp_group_count);
+
+ SAFE_FREE(tmp_groups);
+
+ *group_count += tmp_group_count;
+
+ tmp_methods = tmp_methods->next;
+ }
+
+ return NT_STATUS_OK;
+}
+
+
diff --git a/source/script/.cvsignore b/source/script/.cvsignore
new file mode 100644
index 00000000000..0464ca23351
--- /dev/null
+++ b/source/script/.cvsignore
@@ -0,0 +1,2 @@
+findsmb
+gen-8bit-gap.sh
diff --git a/source/script/build_env.sh b/source/script/build_env.sh
new file mode 100755
index 00000000000..eb54f37aeda
--- /dev/null
+++ b/source/script/build_env.sh
@@ -0,0 +1,41 @@
+#!/bin/sh
+
+if [ $# -lt 3 ]
+then
+ echo "Usage: $0 srcdir builddir compiler"
+ exit 1
+fi
+
+uname=`uname -a`
+date=`date`
+srcdir=$1
+builddir=$2
+compiler=$3
+
+if [ ! "x$USER" = "x" ]; then
+ whoami=$USER
+else
+ if [ ! "x$LOGNAME" = "x" ]; then
+ whoami=$LOGNAME
+ else
+ whoami=`whoami || id -un`
+ fi
+fi
+
+host=`hostname`
+
+cat <<EOF
+/* This file is automatically generated with "make include/build_env.h". DO NOT EDIT */
+
+#ifndef _BUILD_ENV_H
+#define _BUILD_ENV_H
+
+#define BUILD_ENV_UNAME "${uname}"
+#define BUILD_ENV_DATE "${date}"
+#define BUILD_ENV_SRCDIR "${srcdir}"
+#define BUILD_ENV_BUILDDIR "${builddir}"
+#define BUILD_ENV_USER "${whoami}"
+#define BUILD_ENV_HOST "${host}"
+#define BUILD_ENV_COMPILER "${compiler}"
+#endif /* _BUILD_ENV_H */
+EOF
diff --git a/source/script/creategroup b/source/script/creategroup
new file mode 100755
index 00000000000..01fb0659444
--- /dev/null
+++ b/source/script/creategroup
@@ -0,0 +1,27 @@
+#!/bin/sh
+
+# Example script for 'add group command'. Handle weird NT group
+# names. First attempt to create the group directly, if that fails
+# then create a random group and print the numeric group id.
+#
+# Note that this is only an example and assumes /dev/urandom.
+#
+# Volker
+
+GROUPNAME="$1"
+ITERS=0
+
+while ! /usr/sbin/groupadd "$GROUPNAME" > /dev/null 2>&1
+do
+ # we had difficulties creating that group. Maybe the name was
+ # too weird, or it already existed. Create a random name.
+ GROUPNAME=nt-$(dd if=/dev/urandom bs=16 count=1 2>/dev/null | md5sum | cut -b 1-5)
+ ITERS=$(expr "$ITERS" + 1)
+ if [ "$ITERS" -gt 10 ]
+ then
+ # Too many attempts
+ exit 1
+ fi
+done
+
+getent group | grep ^"$GROUPNAME": | cut -d : -f 3
diff --git a/source/script/extract_allparms.sh b/source/script/extract_allparms.sh
new file mode 100755
index 00000000000..f16068b3fdc
--- /dev/null
+++ b/source/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/source/script/findsmb.in b/source/script/findsmb.in
new file mode 100755
index 00000000000..546cf8ce7b4
--- /dev/null
+++ b/source/script/findsmb.in
@@ -0,0 +1,161 @@
+#!@PERL@
+#
+# Prints info on all smb responding machines on a subnet.
+# This script needs to be run on a machine without nmbd running and be
+# run as root to get correct info from WIN95 clients.
+#
+# syntax:
+# findsmb [-d|-D] [-r] [subnet broadcast address]
+#
+# with no agrument it will list machines on the current subnet
+#
+# There will be a "+" in front of the workgroup name for machines that are
+# local master browsers for that workgroup. There will be an "*" in front
+# of the workgroup name for machines that are the domain master browser for
+# that workgroup.
+#
+# Options:
+#
+# -d|-D enable debug
+# -r add -r option to nmblookup when finding netbios name
+#
+
+$SAMBABIN = "@prefix@/bin";
+
+for ($i = 0; $i < 2; $i++) { # test for -d and -r options
+ $_ = shift;
+ if (m/-d|-D/) {
+ $DEBUG = 1;
+ } elsif (m/-r/) {
+ $R_OPTION = "-r";
+ }
+}
+
+if ($_) { # set broadcast address if it was specified
+ $BCAST = "-B $_";
+}
+
+
+######################################################################
+# do numeric sort on last field of IP address
+sub ipsort
+{
+ @t1 = split(/\./,$a);
+ @t2 = split(/\./,$b);
+ @t1[3] <=> @t2[3];
+}
+######################################################################
+
+# look for all machines that respond to a name lookup
+
+open(NMBLOOKUP,"$SAMBABIN/nmblookup $BCAST '*' --debuglevel=0|") ||
+ die("Can't run nmblookup '*'.\n");
+
+# get rid of all lines that are not a response IP address,
+# strip everything but IP address and sort by last field in address
+
+@ipaddrs = sort ipsort grep(s/ \*<00>.*$//,<NMBLOOKUP>);
+
+# print header info
+print "\n *=DMB\n";
+print " +=LMB\n";
+print "IP ADDR NETBIOS NAME WORKGROUP/OS/VERSION $BCAST\n";
+print "---------------------------------------------------------------------\n";
+
+foreach $ip (@ipaddrs) # loop through each IP address found
+{
+ $ip =~ s/\n//; # strip newline from IP address
+
+ # find the netbios names registered by each machine
+
+ open(NMBLOOKUP,"$SAMBABIN/nmblookup $R_OPTION -A $ip --debuglevel=0|") ||
+ die("Can't get nmb name list.\n");
+ @nmblookup = <NMBLOOKUP>;
+ close NMBLOOKUP;
+
+ # get the first <00> name
+
+ @name = grep(/<00>/,@nmblookup);
+ $_ = @name[0];
+
+ if ($_) { # we have a netbios name
+ if (/GROUP/) { # is it a group name
+ ($name, $aliases, $type, $length, @addresses) =
+ gethostbyaddr(pack('C4',split('\.',$ip)),2);
+ if (! $name) { # could not get name
+ $name = "unknown nis name";
+ }
+ } else {
+ # The Netbios name can contain lot of characters also '<' '>'
+ # and spaces. The follwing cure inside name space but not
+ # names starting or ending with spaces
+ /(.{1,15})\s+<00>\s+/;
+ $name = $1;
+ $name =~ s/^\s+//g;
+ }
+
+ # do an smbclient command on the netbios name.
+
+ if ( "$name" ) {
+ open(SMB,"$SAMBABIN/smbclient -L $name -I $ip -N --debuglevel=1 2>&1 |") ||
+ die("Can't do smbclient command.\n");
+ @smb = <SMB>;
+ close SMB;
+
+ if ($DEBUG) { # if -d flag print results of nmblookup and smbclient
+ print "===============================================================\n";
+ print @nmblookup;
+ print @smb;
+ }
+
+ # look for the OS= string
+
+ @info = grep(/OS=/,@smb);
+ $_ = @info[0];
+ if ($_) { # we found response
+ s/Domain=|OS=|Server=|\n//g; # strip out descriptions to make line shorter
+
+ } else { # no OS= string in response (WIN95 client)
+
+ # for WIN95 clients get workgroup name from nmblookup response
+ @name = grep(/<00> - <GROUP>/,@nmblookup);
+ $_ = @name[0];
+ if ($_) {
+ # Same as before for space and characters
+ /(.{1,15})\s+<00>\s+/;
+ $_ = "[$1]";
+ } else {
+ $_ = "Unknown Workgroup";
+ }
+ }
+ }
+
+ # see if machine registered a local master browser name
+ if (grep(/<1d>/,@nmblookup)) {
+ $master = '+'; # indicate local master browser
+ if (grep(/<1b>/,@nmblookup)) { # how about domain master browser?
+ $master = '*'; # indicate domain master browser
+ }
+ } else {
+ $master = ' '; # not a browse master
+ }
+
+ # line up info in 3 columns
+
+ print "$ip".' 'x(16-length($ip))."$name".' 'x(14-length($name))."$master"."$_\n";
+
+ } else { # no netbios name found
+ # try getting the host name
+ ($name, $aliases, $type, $length, @addresses) =
+ gethostbyaddr(pack('C4',split('\.',$ip)),2);
+ if (! $name) { # could not get name
+ $name = "unknown nis name";
+ }
+ if ($DEBUG) { # if -d flag print results of nmblookup
+ print "===============================================================\n";
+ print @nmblookup;
+ }
+ print "$ip".' 'x(16-length($ip))."$name\n";
+ }
+}
+
diff --git a/source/script/findstatic.pl b/source/script/findstatic.pl
new file mode 100755
index 00000000000..43a4916435d
--- /dev/null
+++ b/source/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/source/script/gap.awk b/source/script/gap.awk
new file mode 100644
index 00000000000..11680d10f9e
--- /dev/null
+++ b/source/script/gap.awk
@@ -0,0 +1,39 @@
+BEGIN { hv["0"] = 0; hv["1"] = 1; hv["2"] = 2; hv["3"] = 3;
+ hv["4"] = 4; hv["5"] = 5; hv["6"] = 6; hv["7"] = 7;
+ hv["8"] = 8; hv["9"] = 9; hv["A"] = 10; hv["B"] = 11;
+ hv["C"] = 12; hv["D"] = 13; hv["E"] = 14; hv["F"] = 15;
+ hv["a"] = 10; hv["b"] = 11; hv["c"] = 12; hv["d"] = 13;
+ hv["e"] = 14; hv["f"] = 15;
+
+ first = 0; last = 0; idx = 0;
+}
+
+function tonum(str)
+{
+ num=0;
+ cnt=1;
+ while (cnt <= length(str)) {
+ num *= 16;
+ num += hv[substr(str,cnt,1)];
+ ++cnt;
+ }
+ return num;
+}
+
+{
+ u = tonum($1);
+ if (u - last > 6)
+ {
+ if (last)
+ {
+ printf (" { 0x%04x, 0x%04x, %5d },\n",
+ first, last, idx);
+ idx -= u - last - 1;
+ }
+ first = u;
+ }
+ last = u;
+}
+
+END { printf (" { 0x%04x, 0x%04x, %5d },\n",
+ first, last, idx); }
diff --git a/source/script/gaptab.awk b/source/script/gaptab.awk
new file mode 100644
index 00000000000..a309089cd5b
--- /dev/null
+++ b/source/script/gaptab.awk
@@ -0,0 +1,48 @@
+BEGIN { hv["0"] = 0; hv["1"] = 1; hv["2"] = 2; hv["3"] = 3;
+ hv["4"] = 4; hv["5"] = 5; hv["6"] = 6; hv["7"] = 7;
+ hv["8"] = 8; hv["9"] = 9; hv["A"] = 10; hv["B"] = 11;
+ hv["C"] = 12; hv["D"] = 13; hv["E"] = 14; hv["F"] = 15;
+ hv["a"] = 10; hv["b"] = 11; hv["c"] = 12; hv["d"] = 13;
+ hv["e"] = 14; hv["f"] = 15;
+
+ first = 0; last = 0; idx = 0; f = 0;
+}
+
+function tonum(str)
+{
+ num=0;
+ cnt=1;
+ while (cnt <= length(str)) {
+ num *= 16;
+ num += hv[substr(str,cnt,1)];
+ ++cnt;
+ }
+ return num;
+}
+
+function fmt(val)
+{
+ if (f++ % 8 == 0)
+ { printf ("\n 0x%02x,", val); }
+ else
+ { printf (" 0x%02x,", val); }
+}
+
+{
+ u = tonum($1); c = tonum($2);
+
+ if (u - last > 6)
+ {
+ if (last) { idx += last - first + 1; }
+ first = u;
+ }
+ else
+ {
+ for (m = last+1; m < u; m++) { fmt(0); }
+ }
+
+ fmt(c);
+ last = u;
+}
+
+END { print "" }
diff --git a/source/script/gen-8bit-gap.awk b/source/script/gen-8bit-gap.awk
new file mode 100644
index 00000000000..59a1a23be07
--- /dev/null
+++ b/source/script/gen-8bit-gap.awk
@@ -0,0 +1,18 @@
+BEGIN {
+ for (i=0; i<256; i++) {
+ tbl[sprintf("%02x",i)] = "0x0000";
+ }
+}
+
+/^<U([[:xdigit:]][[:xdigit:]][[:xdigit:]][[:xdigit:]])>[[:space:]]*.x([[:xdigit:]][[:xdigit:]])[:space:]*.*$/ {
+ tbl[substr($2,3,2)]=sprintf("0x%s",substr($1,3,4));
+}
+
+END {
+ for(i=0; i<32; i++) {
+ for(j=0; j<8; j++) {
+ printf(" %s,", tbl[sprintf("%02x",i*8+j)]);
+ }
+ printf "\n"
+ }
+} \ No newline at end of file
diff --git a/source/script/gen-8bit-gap.sh.in b/source/script/gen-8bit-gap.sh.in
new file mode 100755
index 00000000000..bcf64a4464f
--- /dev/null
+++ b/source/script/gen-8bit-gap.sh.in
@@ -0,0 +1,49 @@
+#!/bin/sh
+if test $# -ne 2 ; then
+ echo "Usage: $0 <charmap file> <CHARSET NAME>"
+ exit 1
+fi
+
+CHARMAP=$1
+CHARSETNAME=$2
+
+echo "/* "
+echo " * Conversion table for $CHARSETNAME charset "
+echo " * "
+echo " * Conversion tables are generated using $CHARMAP table "
+echo " * and source/script/gen-8bit-gap.sh script "
+echo " * "
+echo " * This program is free software; you can redistribute it and/or modify "
+echo " * it under the terms of the GNU General Public License as published by "
+echo " * the Free Software Foundation; either version 2 of the License, or "
+echo " * (at your option) any later version. "
+echo " * "
+echo " * This program is distributed in the hope that it will be useful,"
+echo " * but WITHOUT ANY WARRANTY; without even the implied warranty of "
+echo " * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the "
+echo " * GNU General Public License for more details. "
+echo " * "
+echo " * You should have received a copy of the GNU General Public License "
+echo " * along with this program; if not, write to the Free Software "
+echo " * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. "
+echo " */"
+
+echo '#include "includes.h"'
+echo
+echo "static const uint16 to_ucs2[256] = {"
+cat "$CHARMAP" | @AWK@ -f @srcdir@/script/gen-8bit-gap.awk
+echo "};"
+echo
+echo "static const struct charset_gap_table from_idx[] = {"
+sed -ne 's/^<U\(....\).*/\1/p' \
+ "$CHARMAP" | sort -u | @AWK@ -f @srcdir@/script/gap.awk
+echo " { 0xffff, 0xffff, 0 }"
+echo "};"
+echo
+echo "static const unsigned char from_ucs2[] = {"
+sed -ne 's/^<U\(....\)>[[:space:]]*.x\(..\).*/\1 \2/p' \
+ "$CHARMAP" | sort -u | @AWK@ -f @srcdir@/script/gaptab.awk
+echo "};"
+echo
+echo "SMB_GENERATE_CHARSET_MODULE_8_BIT_GAP($CHARSETNAME)"
+echo
diff --git a/source/script/genstruct.pl b/source/script/genstruct.pl
new file mode 100755
index 00000000000..a6abd718c95
--- /dev/null
+++ b/source/script/genstruct.pl
@@ -0,0 +1,299 @@
+#!/usr/bin/perl -w
+# a simple system for generating C parse info
+# this can be used to write generic C structer load/save routines
+# Copyright 2002 Andrew Tridgell <genstruct@tridgell.net>
+# released under the GNU General Public License v2 or later
+
+use strict;
+
+my(%enum_done) = ();
+my(%struct_done) = ();
+
+###################################################
+# general handler
+sub handle_general($$$$$$$$)
+{
+ my($name) = shift;
+ my($ptr_count) = shift;
+ my($size) = shift;
+ my($element) = shift;
+ my($flags) = shift;
+ my($dump_fn) = shift;
+ my($parse_fn) = shift;
+ my($tflags) = shift;
+ my($array_len) = 0;
+ my($dynamic_len) = "NULL";
+
+ # handle arrays, currently treat multidimensional arrays as 1 dimensional
+ while ($element =~ /(.*)\[(.*?)\]$/) {
+ $element = $1;
+ if ($array_len == 0) {
+ $array_len = $2;
+ } else {
+ $array_len = "$2 * $array_len";
+ }
+ }
+
+ if ($flags =~ /_LEN\((\w*?)\)/) {
+ $dynamic_len = "\"$1\"";
+ }
+
+ if ($flags =~ /_NULLTERM/) {
+ $tflags = "FLAG_NULLTERM";
+ }
+
+ print OFILE "{\"$element\", $ptr_count, $size, offsetof(struct $name, $element), $array_len, $dynamic_len, $tflags, $dump_fn, $parse_fn},\n";
+}
+
+
+####################################################
+# parse one element
+sub parse_one($$$$)
+{
+ my($name) = shift;
+ my($type) = shift;
+ my($element) = shift;
+ my($flags) = shift;
+ my($ptr_count) = 0;
+ my($size) = "sizeof($type)";
+ my($tflags) = "0";
+
+ # enums get the FLAG_ALWAYS flag
+ if ($type =~ /^enum /) {
+ $tflags = "FLAG_ALWAYS";
+ }
+
+
+ # make the pointer part of the base type
+ while ($element =~ /^\*(.*)/) {
+ $ptr_count++;
+ $element = $1;
+ }
+
+ # convert spaces to _
+ $type =~ s/ /_/g;
+
+ my($dump_fn) = "gen_dump_$type";
+ my($parse_fn) = "gen_parse_$type";
+
+ handle_general($name, $ptr_count, $size, $element, $flags, $dump_fn, $parse_fn, $tflags);
+}
+
+####################################################
+# parse one element
+sub parse_element($$$)
+{
+ my($name) = shift;
+ my($element) = shift;
+ my($flags) = shift;
+ my($type);
+ my($data);
+
+ # pull the base type
+ if ($element =~ /^struct (\S*) (.*)/) {
+ $type = "struct $1";
+ $data = $2;
+ } elsif ($element =~ /^enum (\S*) (.*)/) {
+ $type = "enum $1";
+ $data = $2;
+ } elsif ($element =~ /^unsigned (\S*) (.*)/) {
+ $type = "unsigned $1";
+ $data = $2;
+ } elsif ($element =~ /^(\S*) (.*)/) {
+ $type = $1;
+ $data = $2;
+ } else {
+ die "Can't parse element '$element'";
+ }
+
+ # handle comma separated lists
+ while ($data =~ /(\S*),[\s]?(.*)/) {
+ parse_one($name, $type, $1, $flags);
+ $data = $2;
+ }
+ parse_one($name, $type, $data, $flags);
+}
+
+
+my($first_struct) = 1;
+
+####################################################
+# parse the elements of one structure
+sub parse_elements($$)
+{
+ my($name) = shift;
+ my($elements) = shift;
+
+ if ($first_struct) {
+ $first_struct = 0;
+ print "Parsing structs: $name";
+ } else {
+ print ", $name";
+ }
+
+ print OFILE "int gen_dump_struct_$name(TALLOC_CTX *mem_ctx, struct parse_string *, const char *, unsigned);\n";
+ print OFILE "int gen_parse_struct_$name(TALLOC_CTX *mem_ctx, char *, const char *);\n";
+
+ print OFILE "static const struct parse_struct pinfo_" . $name . "[] = {\n";
+
+
+ while ($elements =~ /^.*?([a-z].*?);\s*?(\S*?)\s*?$(.*)/msi) {
+ my($element) = $1;
+ my($flags) = $2;
+ $elements = $3;
+ parse_element($name, $element, $flags);
+ }
+
+ print OFILE "{NULL, 0, 0, 0, 0, NULL, 0, NULL, NULL}};\n";
+
+ print OFILE "
+int gen_dump_struct_$name(TALLOC_CTX *mem_ctx, struct parse_string *p, const char *ptr, unsigned indent) {
+ return gen_dump_struct(mem_ctx, pinfo_$name, p, ptr, indent);
+}
+int gen_parse_struct_$name(TALLOC_CTX *mem_ctx, char *ptr, const char *str) {
+ return gen_parse_struct(mem_ctx, pinfo_$name, ptr, str);
+}
+
+";
+}
+
+my($first_enum) = 1;
+
+####################################################
+# parse out the enum declarations
+sub parse_enum_elements($$)
+{
+ my($name) = shift;
+ my($elements) = shift;
+
+ if ($first_enum) {
+ $first_enum = 0;
+ print "Parsing enums: $name";
+ } else {
+ print ", $name";
+ }
+
+ print OFILE "static const struct enum_struct einfo_" . $name . "[] = {\n";
+
+ my(@enums) = split(/,/s, $elements);
+ for (my($i)=0; $i <= $#{@enums}; $i++) {
+ my($enum) = $enums[$i];
+ if ($enum =~ /\s*(\w*)/) {
+ my($e) = $1;
+ print OFILE "{\"$e\", $e},\n";
+ }
+ }
+
+ print OFILE "{NULL, 0}};\n";
+
+ print OFILE "
+int gen_dump_enum_$name(struct parse_string *p, const char *ptr, unsigned indent) {
+ return gen_dump_enum(einfo_$name, p, ptr, indent);
+}
+
+int gen_parse_enum_$name(char *ptr, const char *str) {
+ return gen_parse_enum(einfo_$name, ptr, str);
+}
+
+";
+}
+
+####################################################
+# parse out the enum declarations
+sub parse_enums($)
+{
+ my($data) = shift;
+
+ while ($data =~ /^GENSTRUCT\s+enum\s+(\w*?)\s*{(.*?)}\s*;(.*)/ms) {
+ my($name) = $1;
+ my($elements) = $2;
+ $data = $3;
+
+ if (!defined($enum_done{$name})) {
+ $enum_done{$name} = 1;
+ parse_enum_elements($name, $elements);
+ }
+ }
+
+ if (! $first_enum) {
+ print "\n";
+ }
+}
+
+####################################################
+# parse all the structures
+sub parse_structs($)
+{
+ my($data) = shift;
+
+ # parse into structures
+ while ($data =~ /^GENSTRUCT\s+struct\s+(\w+?)\s*{\s*(.*?)\s*}\s*;(.*)/ms) {
+ my($name) = $1;
+ my($elements) = $2;
+ $data = $3;
+ if (!defined($struct_done{$name})) {
+ $struct_done{$name} = 1;
+ parse_elements($name, $elements);
+ }
+ }
+
+ if (! $first_struct) {
+ print "\n";
+ } else {
+ print "No GENSTRUCT structures found?\n";
+ }
+}
+
+
+####################################################
+# parse a header file, generating a dumper structure
+sub parse_data($)
+{
+ my($data) = shift;
+
+ # collapse spaces
+ $data =~ s/[\t ]+/ /sg;
+ $data =~ s/\s*\n\s+/\n/sg;
+ # strip debug lines
+ $data =~ s/^\#.*?\n//smg;
+
+ parse_enums($data);
+ parse_structs($data);
+}
+
+
+#########################################
+# display help text
+sub ShowHelp()
+{
+ print "
+generator for C structure dumpers
+Copyright Andrew Tridgell <genstruct\@tridgell.net>
+
+Sample usage:
+ genstruct -o output.h gcc -E -O2 -g test.h
+
+Options:
+ --help this help page
+ -o OUTPUT place output in OUTPUT
+";
+ exit(0);
+}
+
+########################################
+# main program
+if ($ARGV[0] ne "-o" || $#ARGV < 2) {
+ ShowHelp();
+}
+
+shift;
+my($opt_ofile)=shift;
+
+print "creating $opt_ofile\n";
+
+open(OFILE, ">$opt_ofile") || die "can't open $opt_ofile";
+
+print OFILE "/* This is an automatically generated file - DO NOT EDIT! */\n\n";
+
+parse_data(`@ARGV -DGENSTRUCT=GENSTRUCT`);
+exit(0);
diff --git a/source/script/installbin.sh b/source/script/installbin.sh
new file mode 100755
index 00000000000..f9fd5298c09
--- /dev/null
+++ b/source/script/installbin.sh
@@ -0,0 +1,45 @@
+#!/bin/sh
+
+INSTALLPERMS=$1
+DESTDIR=$2
+BASEDIR=`echo $3 | sed 's/\/\//\//g'`
+BINDIR=`echo $4 | sed 's/\/\//\//g'`
+LIBDIR=`echo $5 | sed 's/\/\//\//g'`
+VARDIR=`echo $6 | sed 's/\/\//\//g'`
+shift
+shift
+shift
+shift
+shift
+shift
+
+for p in $*; do
+ p2=`basename $p`
+ echo Installing $p as $BINDIR/$p2
+ if [ -f $BINDIR/$p2 ]; then
+ rm -f $BINDIR/$p2.old
+ mv $BINDIR/$p2 $BINDIR/$p2.old
+ fi
+ cp $p $BINDIR/
+ chmod $INSTALLPERMS $BINDIR/$p2
+
+ # this is a special case, mount needs this in a specific location
+ if [ $p2 = smbmount ]; then
+ if [ ! -d $DESTDIR/sbin ]; then
+ mkdir $DESTDIR/sbin
+ fi
+ ln -sf $BINDIR/$p2 $DESTDIR/sbin/mount.smbfs
+ fi
+done
+
+
+cat << EOF
+======================================================================
+The binaries are installed. You may restore the old binaries (if there
+were any) using the command "make revert". You may uninstall the binaries
+using the command "make uninstallbin" or "make uninstall" to uninstall
+binaries, man pages and shell scripts.
+======================================================================
+EOF
+
+exit 0
diff --git a/source/script/installdat.sh b/source/script/installdat.sh
new file mode 100755
index 00000000000..4a5b1de5dc8
--- /dev/null
+++ b/source/script/installdat.sh
@@ -0,0 +1,23 @@
+#!/bin/sh
+#fist version March 2002, Herb Lewis
+
+DATDIR=`echo $1 | sed 's/\/\//\//g'`
+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/source/script/installdirs.sh b/source/script/installdirs.sh
new file mode 100755
index 00000000000..1db46b82ff2
--- /dev/null
+++ b/source/script/installdirs.sh
@@ -0,0 +1,19 @@
+#!/bin/sh
+
+while ( test -n "$1" ); do
+
+ DIRNAME=`echo $1 | sed 's/\/\//\//g'`
+ if [ ! -d $DIRNAME ]; then
+ mkdir -p $DIRNAME
+ fi
+
+ if [ ! -d $DIRNAME ]; then
+ echo Failed to make directory $1
+ exit 1
+ fi
+
+ shift;
+done
+
+
+
diff --git a/source/script/installman.sh b/source/script/installman.sh
new file mode 100755
index 00000000000..c7a8f450951
--- /dev/null
+++ b/source/script/installman.sh
@@ -0,0 +1,71 @@
+#!/bin/sh
+#5 July 96 Dan.Shearer@unisa.edu.au removed hardcoded values
+#
+# 13 Aug 2001 Rafal Szczesniak <mimir@spin.ict.pwr.wroc.pl>
+# modified to accomodate international man pages (inspired
+# by Japanese edition's approach)
+
+MANDIR=`echo $1 | sed 's/\/\//\//g'`
+SRCDIR=$2/
+langs=$3
+
+if [ $# -ge 4 ] ; then
+ GROFF=$4 # sh cmd line, including options
+fi
+
+
+for lang in $langs; do
+ if [ "X$lang" = Xen ]; then
+ echo Installing default man pages in $MANDIR/
+ lang=.
+ else
+ echo Installing \"$lang\" man pages in $MANDIR/lang/$lang
+ fi
+
+ langdir=$MANDIR/$lang
+ for d in $MANDIR $langdir $langdir/man1 $langdir/man5 $langdir/man7 $langdir/man8; do
+ if [ ! -d $d ]; then
+ mkdir $d
+ if [ ! -d $d ]; then
+ echo Failed to make directory $d, does $USER have privileges?
+ exit 1
+ fi
+ fi
+ done
+
+ for sect in 1 5 7 8 ; do
+ for m in $langdir/man$sect ; do
+ for s in $SRCDIR../docs/manpages/$lang/*$sect; do
+ FNAME=$m/`basename $s`
+
+ # Test for writability. Involves
+ # blowing away existing files.
+
+ if (rm -f $FNAME && touch $FNAME); then
+ rm $FNAME
+ if [ "x$GROFF" = x ] ; then
+ cp $s $m # Copy raw nroff
+ else
+ echo "\t$FNAME" # groff'ing can be slow, give the user
+ # a warm fuzzy.
+ $GROFF $s > $FNAME # Process nroff, because man(1) (on
+ # this system) doesn't .
+ fi
+ chmod 0644 $FNAME
+ else
+ echo Cannot create $FNAME... does $USER have privileges?
+ fi
+ done
+ done
+ done
+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/source/script/installmodules.sh b/source/script/installmodules.sh
new file mode 100755
index 00000000000..f7c74733381
--- /dev/null
+++ b/source/script/installmodules.sh
@@ -0,0 +1,27 @@
+#!/bin/sh
+
+INSTALLPERMS=$1
+BASEDIR=`echo $2 | sed 's/\/\//\//g'`
+LIBDIR=`echo $3 | sed 's/\/\//\//g'`
+shift
+shift
+shift
+
+for d in $BASEDIR $LIBDIR; do
+if [ ! -d $d ]; then
+mkdir $d
+if [ ! -d $d ]; then
+ echo Failed to make directory $d
+ exit 1
+fi
+fi
+done
+
+for p in $*; do
+ p2=`basename $p`
+ echo Installing $p as $LIBDIR/$p2
+ cp -f $p $LIBDIR/
+ chmod $INSTALLPERMS $LIBDIR/$p2
+done
+
+exit 0
diff --git a/source/script/installmsg.sh b/source/script/installmsg.sh
new file mode 100644
index 00000000000..5a41fe1ca8d
--- /dev/null
+++ b/source/script/installmsg.sh
@@ -0,0 +1,23 @@
+#!/bin/sh
+# first version (Sept 2003) written by Shiro Yamada <shiro@miraclelinux.com>
+# based on the first verion (March 2002) of installdat.sh written by Herb Lewis
+
+MSGDIR=`echo $1 | sed 's/\/\//\//g'`
+SRCDIR=$2/
+
+echo Installing msg files in $MSGDIR
+
+for f in $SRCDIR/po/*.msg; do
+ FNAME=$MSGDIR/`basename $f`
+ echo $FNAME
+ cp $f $FNAME || echo Cannot install $FNAME. Does $USER have privileges?
+ chmod 0644 $FNAME
+done
+
+cat << EOF
+======================================================================
+The msg files have been installed.
+======================================================================
+EOF
+
+exit 0
diff --git a/source/script/installscripts.sh b/source/script/installscripts.sh
new file mode 100755
index 00000000000..81608c3682c
--- /dev/null
+++ b/source/script/installscripts.sh
@@ -0,0 +1,47 @@
+#!/bin/sh
+# this script courtesy of James_K._Foote.PARC@xerox.com
+# 5 July 96 Dan.Shearer@UniSA.Edu.Au Don't hardcode script names, get from Make
+
+INSTALLPERMS=$1
+BINDIR=`echo $2 | sed 's/\/\//\//g'`
+
+shift
+shift
+
+echo Installing scripts in $BINDIR
+
+for d in $BINDIR; do
+ if [ ! -d $d ]; then
+ mkdir $d
+ if [ ! -d $d ]; then
+ echo Failed to make directory $d
+ echo Have you run installbin first?
+ exit 1
+ fi
+ fi
+done
+
+for p in $*; do
+ p2=`basename $p`
+ echo Installing $BINDIR/$p2
+ if [ -f $BINDIR/$p2 ]; then
+ rm -f $BINDIR/$p2.old
+ mv $BINDIR/$p2 $BINDIR/$p2.old
+ fi
+ cp $p $BINDIR/
+ chmod $INSTALLPERMS $BINDIR/$p2
+ if [ ! -f $BINDIR/$p2 ]; then
+ echo Cannot copy $p2... does $USER have privileges?
+ fi
+done
+
+cat << EOF
+======================================================================
+The scripts have been installed. You may uninstall them using
+the command "make uninstallscripts" or "make install" to install binaries,
+man pages and shell scripts. You may recover the previous version (if any
+by "make revert".
+======================================================================
+EOF
+
+exit 0
diff --git a/source/script/installswat.sh b/source/script/installswat.sh
new file mode 100755
index 00000000000..495386e0b7a
--- /dev/null
+++ b/source/script/installswat.sh
@@ -0,0 +1,153 @@
+#!/bin/sh
+#first version March 1998, Andrew Tridgell
+
+SWATDIR=`echo $1 | sed 's/\/\//\//g'`
+SRCDIR=$2/
+BOOKDIR=$SWATDIR/using_samba
+
+echo Installing SWAT in $SWATDIR
+echo Installing the Samba Web Administration Tool
+
+LANGS=". `cd $SRCDIR../swat/; /bin/echo lang/??`"
+echo Installing langs are `cd $SRCDIR../swat/lang/; /bin/echo ??`
+
+for ln in $LANGS; do
+ SWATLANGDIR=$SWATDIR/$ln
+ for d in $SWATLANGDIR $SWATLANGDIR/help $SWATLANGDIR/images \
+ $SWATLANGDIR/include; do
+ if [ ! -d $d ]; then
+ mkdir -p $d
+ if [ ! -d $d ]; then
+ echo Failed to make directory $d, does $USER have privileges?
+ exit 1
+ fi
+ fi
+ done
+done
+
+# Install images
+for ln in $LANGS; do
+
+ for f in $SRCDIR../swat/$ln/images/*.gif; do
+ if [ ! -f $f ] ; then
+ continue
+ fi
+ FNAME=$SWATDIR/$ln/images/`basename $f`
+ echo $FNAME
+ cp $f $FNAME || echo Cannot install $FNAME. Does $USER have privileges?
+ chmod 0644 $FNAME
+ done
+
+ # Install html help
+
+ for f in $SRCDIR../swat/$ln/help/*.html; do
+ if [ ! -f $f ] ; then
+ continue
+ fi
+ FNAME=$SWATDIR/$ln/help/`basename $f`
+ echo $FNAME
+ if [ "x$BOOKDIR" = "x" ]; then
+ cat $f | sed 's/@BOOKDIR@.*$//' > $f.tmp
+ else
+ cat $f | sed 's/@BOOKDIR@//' > $f.tmp
+ fi
+ f=$f.tmp
+ cp $f $FNAME || echo Cannot install $FNAME. Does $USER have privileges?
+ rm -f $f
+ chmod 0644 $FNAME
+ done
+
+ # Install "server-side" includes
+
+ for f in $SRCDIR../swat/$ln/include/*.html; do
+ if [ ! -f $f ] ; then
+ continue
+ fi
+ FNAME=$SWATDIR/$ln/include/`basename $f`
+ echo $FNAME
+ cp $f $FNAME || echo Cannot install $FNAME. Does $USER have privileges?
+ chmod 0644 $FNAME
+ done
+
+done
+
+# Install html documentation (if html documentation tree is here)
+
+if [ -d $SRCDIR../docs/htmldocs/ ]; then
+
+ for f in $SRCDIR../docs/htmldocs/*.html; do
+ FNAME=$SWATDIR/help/`basename $f`
+ echo $FNAME
+ cp $f $FNAME || echo Cannot install $FNAME. Does $USER have privileges?
+ chmod 0644 $FNAME
+ done
+
+ if [ -d $SRCDIR../docs/htmldocs/images/ ]; then
+ if [ ! -d $SWATDIR/help/images/ ]; then
+ mkdir $SWATDIR/help/images
+ if [ ! -d $SWATDIR/help/images/ ]; then
+ echo Failed to make directory $SWATDIR/help/images, does $USER have privileges?
+ exit 1
+ fi
+ fi
+ for f in $SRCDIR../docs/htmldocs/images/*.png; do
+ FNAME=$SWATDIR/help/images/`basename $f`
+ echo $FNAME
+ cp $f $FNAME || echo Cannot install $FNAME. Does $USER have privileges?
+ chmod 0644 $FNAME
+ done
+ fi
+fi
+
+# Install Using Samba book (but only if it is there)
+
+if [ "x$BOOKDIR" != "x" -a -f $SRCDIR../docs/htmldocs/using_samba/toc.html ]; then
+
+ # Create directories
+
+ for d in $BOOKDIR $BOOKDIR/figs ; do
+ if [ ! -d $d ]; then
+ mkdir $d
+ if [ ! -d $d ]; then
+ echo Failed to make directory $d, does $USER have privileges?
+ exit 1
+ fi
+ fi
+ done
+
+ # HTML files
+
+ for f in $SRCDIR../docs/htmldocs/using_samba/*.html; do
+ FNAME=$BOOKDIR/`basename $f`
+ echo $FNAME
+ cp $f $FNAME || echo Cannot install $FNAME. Does $USER have privileges?
+ chmod 0644 $FNAME
+ done
+
+ for f in $SRCDIR../docs/htmldocs/using_samba/*.gif; do
+ FNAME=$BOOKDIR/`basename $f`
+ echo $FNAME
+ cp $f $FNAME || echo Cannot install $FNAME. Does $USER have privileges?
+ chmod 0644 $FNAME
+ done
+
+ # Figures
+
+ for f in $SRCDIR../docs/htmldocs/using_samba/figs/*.gif; do
+ FNAME=$BOOKDIR/figs/`basename $f`
+ echo $FNAME
+ cp $f $FNAME || echo Cannot install $FNAME. Does $USER have privileges?
+ chmod 0644 $FNAME
+ done
+
+fi
+
+cat << EOF
+======================================================================
+The SWAT files have been installed. Remember to read the swat/README
+for information on enabling and using SWAT
+======================================================================
+EOF
+
+exit 0
+
diff --git a/source/script/linkmodules.sh b/source/script/linkmodules.sh
new file mode 100755
index 00000000000..16a04cc064b
--- /dev/null
+++ b/source/script/linkmodules.sh
@@ -0,0 +1,12 @@
+#!/bin/sh
+
+cd "$1"
+test -f "$2" || exit 0
+
+for I in $3 $4 $5 $6 $7 $8
+do
+ echo "Linking $I to $2"
+ ln -s $2 $I
+done
+
+exit 0
diff --git a/source/script/makeunicodecasemap.awk b/source/script/makeunicodecasemap.awk
new file mode 100644
index 00000000000..8424b6c6725
--- /dev/null
+++ b/source/script/makeunicodecasemap.awk
@@ -0,0 +1,59 @@
+function reset_vals() {
+ upperstr = "";
+ lowerstr = "";
+ flagstr = "0";
+}
+
+function print_val() {
+ upperstr = $13;
+ lowerstr = $14;
+ if ( upperstr == "" )
+ upperstr = strval;
+ if ( lowerstr == "" )
+ lowerstr = strval;
+
+ if ( $3 == "Lu" )
+ flagstr = sprintf("%s|%s", flagstr, "UNI_UPPER");
+ if ( $3 == "Ll" )
+ flagstr = sprintf("%s|%s", flagstr, "UNI_LOWER");
+ if ( val >= 48 && val <= 57)
+ flagstr = sprintf("%s|%s", flagstr, "UNI_DIGIT");
+ if ((val >= 48 && val <= 57) || (val >= 65 && val <= 70) || (val >=97 && val <= 102))
+ flagstr = sprintf("%s|%s", flagstr, "UNI_XDIGIT");
+ if ( val == 32 || (val >=9 && val <= 13))
+ flagstr = sprintf("%s|%s", flagstr, "UNI_SPACE");
+ if( index(flagstr, "0|") == 1)
+ flagstr = substr(flagstr, 3, length(flagstr) - 2);
+ printf("{ 0x%s, 0x%s, %s }, \t\t\t/* %s %s */\n", lowerstr, upperstr, flagstr, strval, $2);
+ val++;
+ strval=sprintf("%04X", val);
+ reset_vals();
+}
+
+BEGIN {
+ val=0
+ FS=";"
+ strval=sprintf("%04X", val);
+ reset_vals();
+}
+
+{
+ if ( $1 == strval ) {
+ print_val();
+ } else {
+ while ( $1 != strval) {
+ printf("{ 0x%04X, 0x%04X, 0 }, \t\t\t/* %s NOMAP */\n", val, val, strval);
+ val++;
+ strval=sprintf("%04X", val);
+ }
+ print_val();
+ }
+}
+
+END {
+ while ( val < 65536 ) {
+ printf("{ 0x%04X, 0x%04X, 0 }, \t\t\t/* %s NOMAP */\n", val, val, strval);
+ val++;
+ strval=sprintf("%04X", val);
+ }
+}
diff --git a/source/script/mkbuildoptions.awk b/source/script/mkbuildoptions.awk
new file mode 100644
index 00000000000..9c226623109
--- /dev/null
+++ b/source/script/mkbuildoptions.awk
@@ -0,0 +1,262 @@
+BEGIN {
+ print "/* ";
+ print " Unix SMB/CIFS implementation.";
+ print " Build Options for Samba Suite";
+ print " Copyright (C) Vance Lankhaar <vlankhaar@linux.ca> 2003";
+ print " Copyright (C) Andrew Bartlett <abartlet@samba.org> 2001";
+ print " ";
+ print " This program is free software; you can redistribute it and/or modify";
+ print " it under the terms of the GNU General Public License as published by";
+ print " the Free Software Foundation; either version 2 of the License, or";
+ print " (at your option) any later version.";
+ print " ";
+ print " This program is distributed in the hope that it will be useful,";
+ print " but WITHOUT ANY WARRANTY; without even the implied warranty of";
+ print " MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the";
+ print " GNU General Public License for more details.";
+ print " ";
+ print " You should have received a copy of the GNU General Public License";
+ print " along with this program; if not, write to the Free Software";
+ print " Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.";
+ print "*/";
+ print "";
+ print "#include \"includes.h\"";
+ print "#include \"build_env.h\"";
+ print "#include \"dynconfig.h\"";
+ print "";
+ print "static void output(BOOL screen, const char *format, ...) PRINTF_ATTRIBUTE(2,3);";
+ print "";
+ print "";
+ print "/****************************************************************************";
+ print "helper function for build_options";
+ print "****************************************************************************/";
+ print "static void output(BOOL screen, const char *format, ...)";
+ print "{";
+ print " char *ptr;";
+ print " va_list ap;";
+ print " ";
+ print " va_start(ap, format);";
+ print " vasprintf(&ptr,format,ap);";
+ print " va_end(ap);";
+ print "";
+ print " if (screen) {";
+ print " d_printf(\"%s\", ptr);";
+ print " } else {";
+ print " DEBUG(4,(\"%s\", ptr));";
+ print " }";
+ print " ";
+ print " SAFE_FREE(ptr);";
+ print "}";
+ print "";
+ print "/****************************************************************************";
+ print "options set at build time for the samba suite";
+ print "****************************************************************************/";
+ print "void build_options(BOOL screen)";
+ print "{";
+ print " if ((DEBUGLEVEL < 4) && (!screen)) {";
+ print " return;";
+ print " }";
+ print "";
+ print "#ifdef _BUILD_ENV_H";
+ print " /* Output information about the build environment */";
+ print " output(screen,\"Build environment:\\n\");";
+ print " output(screen,\" Built by: %s@%s\\n\",BUILD_ENV_USER,BUILD_ENV_HOST);";
+ print " output(screen,\" Built on: %s\\n\",BUILD_ENV_DATE);";
+ print "";
+ print " output(screen,\" Built using: %s\\n\",BUILD_ENV_COMPILER);";
+ print " output(screen,\" Build host: %s\\n\",BUILD_ENV_UNAME);";
+ print " output(screen,\" SRCDIR: %s\\n\",BUILD_ENV_SRCDIR);";
+ print " output(screen,\" BUILDDIR: %s\\n\",BUILD_ENV_BUILDDIR);";
+ print "";
+ print " ";
+ print "#endif";
+ print "";
+
+ print " /* Output various paths to files and directories */";
+ print " output(screen,\"\\nPaths:\\n\");";
+
+ print " output(screen,\" SBINDIR: %s\\n\", dyn_SBINDIR);";
+ print " output(screen,\" BINDIR: %s\\n\", dyn_BINDIR);";
+ print " output(screen,\" SWATDIR: %s\\n\", dyn_SWATDIR);";
+
+ print " output(screen,\" CONFIGFILE: %s\\n\", dyn_CONFIGFILE);";
+ print " output(screen,\" LOGFILEBASE: %s\\n\", dyn_LOGFILEBASE);";
+ print " output(screen,\" LMHOSTSFILE: %s\\n\",dyn_LMHOSTSFILE);";
+
+ print " output(screen,\" LIBDIR: %s\\n\",dyn_LIBDIR);";
+ print " output(screen,\" SHLIBEXT: %s\\n\",dyn_SHLIBEXT);";
+
+ print " output(screen,\" LOCKDIR: %s\\n\",dyn_LOCKDIR);";
+ print " output(screen,\" PIDDIR: %s\\n\", dyn_PIDDIR);";
+
+ print " output(screen,\" SMB_PASSWD_FILE: %s\\n\",dyn_SMB_PASSWD_FILE);";
+ print " output(screen,\" PRIVATE_DIR: %s\\n\",dyn_PRIVATE_DIR);";
+ print "";
+
+
+##################################################
+# predefine first element of *_ary
+# predefine *_i (num of elements in *_ary)
+ with_ary[0]="";
+ with_i=0;
+ have_ary[0]="";
+ have_i=0;
+ utmp_ary[0]="";
+ utmp_i=0;
+ misc_ary[0]="";
+ misc_i=0;
+ sys_ary[0]="";
+ sys_i=0;
+ headers_ary[0]="";
+ headers_i=0;
+ in_comment = 0;
+}
+
+# capture single line comments
+/^\/\* (.*?)\*\// {
+ last_comment = $0;
+ next;
+}
+
+# end capture multi-line comments
+/(.*?)\*\// {
+ last_comment = last_comment $0;
+ in_comment = 0;
+ next;
+}
+
+# capture middle lines of multi-line comments
+in_comment {
+ last_comment = last_comment $0;
+ next;
+}
+
+# begin capture multi-line comments
+/^\/\* (.*?)/ {
+ last_comment = $0;
+ in_comment = 1;
+ next
+}
+
+##################################################
+# if we have an #undef and a last_comment, store it
+/^\#undef/ {
+ split($0,a);
+ comments_ary[a[2]] = last_comment;
+ last_comment = "";
+}
+
+##################################################
+# for each line, sort into appropriate section
+# then move on
+
+/^\#undef WITH/ {
+ with_ary[with_i++] = a[2];
+ # we want (I think) to allow --with to show up in more than one place, so no next
+}
+
+
+/^\#undef HAVE_UT_UT_/ || /^\#undef .*UTMP/ {
+ utmp_ary[utmp_i++] = a[2];
+ next;
+}
+
+/^\#undef HAVE_SYS_.*?_H$/ {
+ sys_ary[sys_i++] = a[2];
+ next;
+}
+
+/^\#undef HAVE_.*?_H$/ {
+ headers_ary[headers_i++] = a[2];
+ next;
+}
+
+/^\#undef HAVE_/ {
+ have_ary[have_i++] = a[2];
+ next;
+}
+
+/^\#undef/ {
+ misc_ary[misc_i++] = a[2];
+ next;
+}
+
+
+##################################################
+# simple sort function
+function sort(ARRAY, ELEMENTS) {
+ for (i = 1; i <= ELEMENTS; ++i) {
+ for (j = i; (j-1) in ARRAY && (j) in ARRAY && ARRAY[j-1] > ARRAY[j]; --j) {
+ temp = ARRAY[j];
+ ARRAY[j] = ARRAY[j-1];
+ ARRAY[j-1] = temp;
+ }
+ }
+ return;
+}
+
+
+##################################################
+# output code from list of defined
+# expects: ARRAY an array of things defined
+# ELEMENTS number of elements in ARRAY
+# TITLE title for section
+# returns: nothing
+function output(ARRAY, ELEMENTS, TITLE) {
+
+ # add section header
+ print "\n\t/* Show " TITLE " */";
+ print "\toutput(screen, \"\\n " TITLE ":\\n\");\n";
+
+
+ # sort element using bubble sort (slow, but easy)
+ sort(ARRAY, ELEMENTS);
+
+ # loop through array of defines, outputting code
+ for (i = 0; i < ELEMENTS; i++) {
+ print "#ifdef " ARRAY[i];
+
+ # I don't know which one to use....
+
+ print "\toutput(screen, \" " ARRAY[i] "\\n\");";
+ #printf "\toutput(screen, \" %s\\n %s\\n\\n\");\n", comments_ary[ARRAY[i]], ARRAY[i];
+ #printf "\toutput(screen, \" %-35s %s\\n\");\n", ARRAY[i], comments_ary[ARRAY[i]];
+
+ print "#endif";
+ }
+ return;
+}
+
+END {
+ ##################################################
+ # add code to show various options
+ print "/* Output various other options (as gleaned from include/config.h.in) */";
+ output(sys_ary, sys_i, "System Headers");
+ output(headers_ary, headers_i, "Headers");
+ output(utmp_ary, utmp_i, "UTMP Options");
+ output(have_ary, have_i, "HAVE_* Defines");
+ output(with_ary, with_i, "--with Options");
+ output(misc_ary, misc_i, "Build Options");
+
+ ##################################################
+ # add code to display the various type sizes
+ print " /* Output the sizes of the various types */";
+ print " output(screen, \"\\nType sizes:\\n\");";
+ print " output(screen, \" sizeof(char): %lu\\n\",(unsigned long)sizeof(char));";
+ print " output(screen, \" sizeof(int): %lu\\n\",(unsigned long)sizeof(int));";
+ print " output(screen, \" sizeof(long): %lu\\n\",(unsigned long)sizeof(long));";
+ print " output(screen, \" sizeof(uint8): %lu\\n\",(unsigned long)sizeof(uint8));";
+ print " output(screen, \" sizeof(uint16): %lu\\n\",(unsigned long)sizeof(uint16));";
+ print " output(screen, \" sizeof(uint32): %lu\\n\",(unsigned long)sizeof(uint32));";
+ print " output(screen, \" sizeof(short): %lu\\n\",(unsigned long)sizeof(short));";
+ print " output(screen, \" sizeof(void*): %lu\\n\",(unsigned long)sizeof(void*));";
+
+ ##################################################
+ # add code to give information about modules
+ print " output(screen, \"\\nBuiltin modules:\\n\");";
+ print " output(screen, \" %s\\n\", STRING_STATIC_MODULES);";
+
+ print "}";
+
+}
+
diff --git a/source/script/mkinstalldirs b/source/script/mkinstalldirs
new file mode 100755
index 00000000000..f945dbf2bce
--- /dev/null
+++ b/source/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/source/script/mknissmbpasswd.sh b/source/script/mknissmbpasswd.sh
new file mode 100755
index 00000000000..a94c963bdce
--- /dev/null
+++ b/source/script/mknissmbpasswd.sh
@@ -0,0 +1,31 @@
+#!/bin/sh
+#
+# Copyright (C) 1998 Benny Holmgren
+#
+# Script to import smbpasswd file into the smbpasswd NIS+ table. Reads
+# from stdin the smbpasswd file.
+#
+while true
+do
+ read row
+ if [ -z "$row" ]
+ then
+ break
+ fi
+
+ if [ "`echo $row | cut -c1`" = "#" ]
+ then
+ continue
+ fi
+
+ nistbladm -a \
+ name=\"`echo $row | cut -d: -f1`\" \
+ uid=\"`echo $row | cut -d: -f2`\" \
+ lmpwd=\"`echo $row | cut -d: -f3`\" \
+ ntpwd=\"`echo $row | cut -d: -f4`\" \
+ acb=\"`echo $row | cut -d: -f5`\" \
+ pwdlset_t=\"`echo $row | cut -d: -f6`\" \
+ gcos=\"`echo $row | cut -d: -f7`\" \
+ home=\"`echo $row | cut -d: -f8`\" \
+ shell=\"`echo $row | cut -d: -f9`\" smbpasswd.org_dir.`nisdefaults -d`
+done
diff --git a/source/script/mknissmbpwdtbl.sh b/source/script/mknissmbpwdtbl.sh
new file mode 100755
index 00000000000..a9b34ff9a75
--- /dev/null
+++ b/source/script/mknissmbpwdtbl.sh
@@ -0,0 +1,42 @@
+#!/bin/sh
+#
+# Copyright (C) 1998 Benny Holmgren
+#
+# Creates smbpasswd table and smb group in NIS+
+#
+
+nistbladm \
+ -D access=og=rmcd,nw= -c \
+ -s : smbpasswd_tbl \
+ name=S,nogw=r \
+ uid=S,nogw=r \
+ user_rid=S,nogw=r \
+ smb_grpid=,nw+r \
+ group_rid=,nw+r \
+ acb=,nw+r \
+ \
+ lmpwd=C,nw=,g=r,o=rm \
+ ntpwd=C,nw=,g=r,o=rm \
+ \
+ logon_t=,nw+r \
+ logoff_t=,nw+r \
+ kick_t=,nw+r \
+ pwdlset_t=,nw+r \
+ pwdlchg_t=,nw+r \
+ pwdmchg_t=,nw+r \
+ \
+ full_name=,nw+r \
+ home_dir=,nw+r \
+ dir_drive=,nw+r \
+ logon_script=,nw+r \
+ profile_path=,nw+r \
+ acct_desc=,nw+r \
+ workstations=,nw+r \
+ \
+ hours=,nw+r \
+ smbpasswd.org_dir.`nisdefaults -d`
+
+nisgrpadm -c smb.`nisdefaults -d`
+
+nischgrp smb.`nisdefaults -d` smbpasswd.org_dir.`nisdefaults -d`
+
diff --git a/source/script/mkproto.awk b/source/script/mkproto.awk
new file mode 100644
index 00000000000..b38f405e0da
--- /dev/null
+++ b/source/script/mkproto.awk
@@ -0,0 +1,155 @@
+BEGIN {
+ inheader=0;
+# use_ldap_define = 0;
+ current_file="";
+ if (headername=="") {
+ headername="_PROTO_H_";
+ }
+
+ print "#ifndef",headername
+ print "#define",headername
+ print ""
+ print "/* This file is automatically generated with \"make proto\". DO NOT EDIT */"
+ print ""
+}
+
+END {
+ print ""
+ print "#endif /* ",headername," */"
+}
+
+{
+ if (FILENAME!=current_file) {
+# if (use_ldap_define)
+# {
+# print "#endif /* USE_LDAP */"
+# use_ldap_define = 0;
+# }
+ print ""
+ print "/* The following definitions come from",FILENAME," */"
+ print ""
+ current_file=FILENAME
+ }
+ if (inheader) {
+ if (match($0,"[)][ \t]*$")) {
+ inheader = 0;
+ printf "%s;\n",$0;
+ } else {
+ printf "%s\n",$0;
+ }
+ next;
+ }
+}
+
+# we handle the loadparm.c fns separately
+
+/^FN_LOCAL_BOOL/ {
+ split($0,a,"[,()]")
+ printf "BOOL %s(int );\n", a[2]
+}
+
+/^FN_LOCAL_LIST/ {
+ split($0,a,"[,()]")
+ printf "const char **%s(int );\n", a[2]
+}
+
+/^FN_LOCAL_STRING/ {
+ split($0,a,"[,()]")
+ printf "char *%s(int );\n", a[2]
+}
+
+/^FN_LOCAL_CONST_STRING/ {
+ split($0,a,"[,()]")
+ printf "const char *%s(int );\n", a[2]
+}
+
+/^FN_LOCAL_INT/ {
+ split($0,a,"[,()]")
+ printf "int %s(int );\n", a[2]
+}
+
+/^FN_LOCAL_CHAR/ {
+ split($0,a,"[,()]")
+ printf "char %s(int );\n", a[2]
+}
+
+/^FN_GLOBAL_BOOL/ {
+ split($0,a,"[,()]")
+ printf "BOOL %s(void);\n", a[2]
+}
+
+/^FN_GLOBAL_LIST/ {
+ split($0,a,"[,()]")
+ printf "const char **%s(void);\n", a[2]
+}
+
+/^FN_GLOBAL_STRING/ {
+ split($0,a,"[,()]")
+ printf "char *%s(void);\n", a[2]
+}
+
+/^FN_GLOBAL_CONST_STRING/ {
+ split($0,a,"[,()]")
+ printf "const char *%s(void);\n", a[2]
+}
+
+/^FN_GLOBAL_INT/ {
+ split($0,a,"[,()]")
+ printf "int %s(void);\n", a[2]
+}
+
+/^static|^extern/ || !/^[a-zA-Z]/ || /[;]/ {
+ next;
+}
+
+#
+# We have to split up the start
+# matching as we now have so many start
+# types that it can cause some versions
+# of nawk/awk to choke and fail on
+# the full match. JRA.
+#
+
+{
+ gotstart = 0;
+ if( $0 ~ /^const|^connection_struct|^pipes_struct|^smb_np_struct|^file_fd_struct|^files_struct|^connection_struct|^uid_t|^gid_t|^unsigned|^mode_t|^DIR|^user|^int|^pid_t|^ino_t|^off_t|^double/ ) {
+ gotstart = 1;
+ }
+
+ if( $0 ~ /^vuser_key|^UNISTR2|^LOCAL_GRP|^DOMAIN_GRP|^SMB_STRUCT_DIRENT|^SEC_ACL|^SEC_DESC|^SEC_DESC_BUF|^DOM_SID|^RPC_HND_NODE|^BYTE/ ) {
+ gotstart = 1;
+ }
+
+ if( $0 ~ /^ADS_STRUCT|^ADS_STATUS|^DATA_BLOB|^ASN1_DATA|^TDB_CONTEXT|^TDB_DATA|^smb_ucs2_t|^TALLOC_CTX|^hash_element|^NT_DEVICEMODE|^enum.*\(|^NT_USER_TOKEN|^SAM_ACCOUNT|^NTTIME/ ) {
+ gotstart = 1;
+ }
+
+ if( $0 ~ /^smb_iconv_t|^long|^char|^uint|^NTSTATUS|^WERROR|^CLI_POLICY_HND|^struct|^BOOL|^void|^time|^smb_shm_offset_t|^shm_offset_t|^FILE|^XFILE|^SMB_OFF_T|^size_t|^ssize_t|^SMB_BIG_UINT/ ) {
+ gotstart = 1;
+ }
+
+ if( $0 ~ /^SAM_ACCT_INFO_NODE|^SMB_ACL_T|^ADS_MODLIST|^PyObject|^SORTED_TREE|^REGISTRY_HOOK|^REGISTRY_VALUE|^DEVICEMODE|^PAC_DATA|^NET_USER_INFO_3|^smb_event_id_t/ ) {
+ gotstart = 1;
+ }
+
+ if( $0 ~ /^WINBINDD_PW|^WINBINDD_GR|^NT_PRINTER_INFO_LEVEL_2|^LOGIN_CACHE/ ) {
+ gotstart = 1;
+ }
+
+ if(!gotstart) {
+ next;
+ }
+}
+
+
+/[(].*[)][ \t]*$/ {
+ printf "%s;\n",$0;
+ next;
+}
+
+/[(]/ {
+ inheader=1;
+ printf "%s\n",$0;
+ next;
+}
+
diff --git a/source/script/mkproto.sh b/source/script/mkproto.sh
new file mode 100755
index 00000000000..62041c7e331
--- /dev/null
+++ b/source/script/mkproto.sh
@@ -0,0 +1,43 @@
+#! /bin/sh
+
+LANG=C; export LANG
+LC_ALL=C; export LC_ALL
+LC_COLLATE=C; export LC_COLLATE
+
+if [ $# -lt 3 ]
+then
+ echo "Usage: $0 awk [-h headerdefine] outputheader proto_obj"
+ exit 1
+fi
+
+awk="$1"
+shift
+
+if [ x"$1" = x-h ]
+then
+ headeropt="-v headername=$2"
+ shift; shift;
+else
+ headeropt=""
+fi
+
+header="$1"
+shift
+headertmp="$header.$$.tmp~"
+
+proto_src="`echo $@ | tr ' ' '\n' | sed -e 's/\.o/\.c/g' | sort | uniq | egrep -v 'ubiqx/|wrapped|modules/getdate'`"
+
+echo creating $header
+
+mkdir -p `dirname $header`
+
+${awk} $headeropt \
+ -f script/mkproto.awk $proto_src > $headertmp
+
+if cmp -s $header $headertmp 2>/dev/null
+then
+ echo "$header unchanged"
+ rm $headertmp
+else
+ mv $headertmp $header
+fi
diff --git a/source/script/mksmbpasswd.sh b/source/script/mksmbpasswd.sh
new file mode 100755
index 00000000000..119a55611ec
--- /dev/null
+++ b/source/script/mksmbpasswd.sh
@@ -0,0 +1,6 @@
+#!/bin/sh
+awk 'BEGIN {FS=":"
+ printf("#\n# SMB password file.\n#\n")
+ }
+{ printf( "%s:%s:XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX:XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX:[UD ]:LCT-00000000:%s\n", $1, $3, $5) }
+'
diff --git a/source/script/mkversion.sh b/source/script/mkversion.sh
new file mode 100755
index 00000000000..ca392979408
--- /dev/null
+++ b/source/script/mkversion.sh
@@ -0,0 +1,87 @@
+#!/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="include/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_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_BETA_RELEASE=`sed -n 's/^SAMBA_VERSION_BETA_RELEASE=//p' $SOURCE_DIR$VERSION_FILE`
+
+SAMBA_VERSION_ALPHA_RELEASE=`sed -n 's/^SAMBA_VERSION_ALPHA_RELEASE=//p' $SOURCE_DIR$VERSION_FILE`
+
+SAMBA_VERSION_TEST_RELEASE=`sed -n 's/^SAMBA_VERSION_TEST_RELEASE=//p' $SOURCE_DIR$VERSION_FILE`
+
+SAMBA_VERSION_IS_CVS_SNAPSHOT=`sed -n 's/^SAMBA_VERSION_IS_CVS_SNAPSHOT=//p' $SOURCE_DIR$VERSION_FILE`
+
+SAMBA_VERSION_VENDOR_SUFFIX=`sed -n 's/^SAMBA_VERSION_VENDOR_SUFFIX=//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
+
+
+SAMBA_VERSION_STRING="${SAMBA_VERSION_MAJOR}.${SAMBA_VERSION_MINOR}.${SAMBA_VERSION_RELEASE}"
+
+
+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_PRE_RELEASE}";then
+ 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
+elif test -n "${SAMBA_VERSION_BETA_RELEASE}";then
+ SAMBA_VERSION_STRING="${SAMBA_VERSION_STRING}beta${SAMBA_VERSION_BETA_RELEASE}"
+ echo "#define SAMBA_VERSION_BETA_RELEASE ${SAMBA_VERSION_BETA_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_TEST_RELEASE}";then
+ SAMBA_VERSION_STRING="${SAMBA_VERSION_STRING}test${SAMBA_VERSION_TEST_RELEASE}"
+ echo "#define SAMBA_VERSION_TEST_RELEASE ${SAMBA_VERSION_TEST_RELEASE}" >> $OUTPUT_FILE
+fi
+
+
+if test x"${SAMBA_VERSION_IS_CVS_SNAPSHOT}" = x"yes";then
+ SAMBA_VERSION_STRING="CVS ${SAMBA_VERSION_STRING}"
+ echo "#define SAMBA_VERSION_IS_CVS_SNAPSHOT 1" >> $OUTPUT_FILE
+fi
+
+if test -n "${SAMBA_VERSION_VENDOR_SUFFIX}";then
+ echo "#define SAMBA_VERSION_VENDOR_SUFFIX ${SAMBA_VERSION_VENDOR_SUFFIX}" >> $OUTPUT_FILE
+fi
+
+echo "#define SAMBA_VERSION_OFFICIAL_STRING \"${SAMBA_VERSION_STRING}\"" >> $OUTPUT_FILE
+
+echo "#define SAMBA_VERSION_STRING samba_version_string()" >> $OUTPUT_FILE
+
+echo "$0: 'include/version.h' created for Samba(\"${SAMBA_VERSION_STRING}\")"
+
+if test -n "${SAMBA_VERSION_VENDOR_SUFFIX}";then
+ echo "$0: with VENDOR_SUFFIX = ${SAMBA_VERSION_VENDOR_SUFFIX}"
+fi
+
+exit 0
diff --git a/source/script/revert.sh b/source/script/revert.sh
new file mode 100755
index 00000000000..8df5fd2fbde
--- /dev/null
+++ b/source/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/source/script/scancvslog.pl b/source/script/scancvslog.pl
new file mode 100755
index 00000000000..c39f9111c10
--- /dev/null
+++ b/source/script/scancvslog.pl
@@ -0,0 +1,112 @@
+#!/usr/bin/perl
+require"timelocal.pl";
+
+#
+# usage scancvslog.pl logfile starttime tag
+#
+# this will extract all entries from the specified cvs log file
+# that have a date later than or equal to starttime and a tag
+# value of tag. If starttime is not specified, all entries are
+# extracted. If tag is not specified then entries for all
+# branches are extracted. starttime must be specified as
+# "monthname day, year"
+#
+# Example to extract all entries for SAMBA_2_2 branch from the
+# log file named cvs.log
+#
+# scancvslog.pl cvs.log "" SAMBA_2_2
+#
+#
+# To extract all log entries after Jan 10, 1999 (Note month name
+# must be spelled out completely).
+#
+# scancvslog.pl cvs.log "January 10, 1999"
+#
+
+open(INFILE,@ARGV[0]) || die "Unable to open @ARGV[0]\n";
+
+%Monthnum = (
+ "January", 0,
+ "February", 1,
+ "March", 2,
+ "April", 3,
+ "May", 4,
+ "June", 5,
+ "July", 6,
+ "August", 7,
+ "September", 8,
+ "October", 9,
+ "November", 10,
+ "December", 11,
+ "Jan", 0,
+ "Feb", 1,
+ "Mar", 2,
+ "Apr", 3,
+ "May", 4,
+ "Jun", 5,
+ "Jul", 6,
+ "Aug", 7,
+ "Sep", 8,
+ "Oct", 9,
+ "Nov", 10,
+ "Dec", 11
+);
+
+$Starttime = (@ARGV[1]) ? &make_time(@ARGV[1]) : 0;
+$Tagvalue = @ARGV[2];
+
+while (&get_entry) {
+ $_=$Entry[0];
+# get rid of extra white space
+ s/\s+/ /g;
+# get rid of any time string in date
+ s/ \d\d:\d\d:\d\d/,/;
+ s/^Date:\s*\w*\s*(\w*)\s*(\w*),\s*(\w*).*/$1 $2 $3/;
+ $Testtime = &make_time($_);
+ $Testtag = &get_tag;
+ if (($Testtime >= $Starttime) && ($Tagvalue eq $Testtag)) {
+ print join("\n",@Entry),"\n";
+ }
+}
+close(INFILE);
+
+sub make_time {
+ $_ = @_[0];
+ s/,//;
+ ($month, $day, $year) = split(" ",$_);
+ if (($year < 1900)||($day < 1)||($day > 31)||not length($Monthnum{$month})) {
+ print "Bad date format @_[0]\n";
+ print "Date needs to be specified as \"Monthname day, year\"\n";
+ print "eg: \"January 10, 1999\"\n";
+ exit 1;
+ }
+ $year = ($year == 19100) ? 2000 : $year;
+ $month = $Monthnum{$month};
+ $Mytime=&timelocal((0,0,0,$day,$month,$year));
+}
+
+sub get_tag {
+ @Mytag = grep (/Tag:/,@Entry);
+ $_ = @Mytag[0];
+ s/^.*Tag:\s*(\w*).*/$1/;
+ return $_;
+}
+
+sub get_entry {
+ @Entry=();
+ if (not eof(INFILE)) {
+ while (not eof(INFILE)) {
+ $_ = <INFILE>;
+ chomp $_;
+ next if (not ($_));
+ if (/^\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*/) {
+ next if ($#Entry == -1);
+ push(Entry,$_);
+ return @Entry;
+ } else {
+ push(Entry,$_);
+ }
+ }
+ }
+ return @Entry;
+}
diff --git a/source/script/smbtar b/source/script/smbtar
new file mode 100644
index 00000000000..67a794553cf
--- /dev/null
+++ b/source/script/smbtar
@@ -0,0 +1,165 @@
+#!/bin/sh
+#
+# smbtar script - front end to smbclient
+#
+# Authors: Martin.Kraemer <Martin.Kraemer@mch.sni.de>
+# and Ricky Poulten (ricky@logcam.co.uk)
+#
+# (May need to change shell to ksh for HPUX or OSF for better getopts)
+#
+# sandy nov 3 '98 added -a flag
+#
+# Richard Sharpe, added -c 'tarmode full' so that we back up all files to
+# fix a bug in clitar when a patch was added to stop system and hidden files
+# being backed up.
+
+case $0 in
+ # when called by absolute path, assume smbclient is in the same directory
+ /*)
+ SMBCLIENT="`dirname $0`/smbclient";;
+ *) # you may need to edit this to show where your smbclient is
+ SMBCLIENT="smbclient";;
+esac
+
+# These are the default values. You could fill them in if you know what
+# you're doing, but beware: better not store a plain text password!
+server=""
+service="backup" # Default: a service called "backup"
+password=""
+username=$LOGNAME # Default: same user name as in *nix
+verbose="2>/dev/null" # Default: no echo to stdout
+log="-d 2"
+newer=""
+newerarg=""
+blocksize=""
+blocksizearg=""
+clientargs="-c 'tarmode full'"
+tarcmd="c"
+tarargs=""
+cdcmd="\\"
+tapefile=${TAPE-tar.out}
+
+Usage(){
+ ex=$1
+ shift
+echo >&2 "Usage: `basename $0` [<options>] [<include/exclude files>]
+Function: backup/restore a Windows PC directories to a local tape file
+Options: (Description) (Default)
+ -r Restore from tape file to PC Save from PC to tapefile
+ -i Incremental mode Full backup mode
+ -a Reset archive bit mode Don't reset archive bit
+ -v Verbose mode: echo command Don't echo anything
+ -s <server> Specify PC Server $server
+ -p <password> Specify PC Password $password
+ -x <share> Specify PC Share $service
+ -X Exclude mode Include
+ -N <newer> File for date comparison `set -- $newer; echo $2`
+ -b <blocksize> Specify tape's blocksize `set -- $blocksize; echo $2`
+ -d <dir> Specify a directory in share $cdcmd
+ -l <log> Specify a Samba Log Level `set -- $log; echo $2`
+ -u <user> Specify User Name $username
+ -t <tape> Specify Tape device $tapefile
+"
+ echo >&2 "$@"
+ exit $ex
+}
+
+# echo Params count: $#
+
+# DEC OSF AKA Digital UNIX does not seem to return a value in OPTIND if
+# there are no command line params, so protect us against that ...
+if [ $# = 0 ]; then
+
+ Usage 2 "Please enter a command line parameter!"
+
+fi
+
+while getopts riavl:b:d:N:s:p:x:u:Xt: c; do
+ case $c in
+ r) # [r]estore to Windows (instead of the default "Save from Windows")
+ tarcmd="x"
+ ;;
+ i) # [i]ncremental
+ tarargs=${tarargs}ga
+ clientargs="-c 'tarmode inc'"
+ ;;
+ a) # [a]rchive
+ tarargs=${tarargs}a
+ ;;
+ l) # specify [l]og file
+ log="-d $OPTARG"
+ case "$OPTARG" in
+ [0-9]*) ;;
+ *) echo >&2 "$0: Error, log level not numeric: -l $OPTARG"
+ exit 1
+ esac
+ ;;
+ d) # specify [d]irectory to change to in server's share
+ cdcmd="$OPTARG"
+ ;;
+ N) # compare with a file, test if [n]ewer
+ if [ -f $OPTARG ]; then
+ newer=$OPTARG
+ newerarg="N"
+ else
+ echo >&2 $0: Warning, $OPTARG not found
+ fi
+ ;;
+ X) # Add exclude flag
+ tarargs=${tarargs}X
+ ;;
+ s) # specify [s]erver's share to connect to - this MUST be given.
+ server="$OPTARG"
+ ;;
+ b) # specify [b]locksize
+ blocksize="$OPTARG"
+ case "$OPTARG" in
+ [0-9]*) ;;
+ *) echo >&2 "$0: Error, block size not numeric: -b $OPTARG"
+ exit 1
+ esac
+ blocksizearg="b"
+ ;;
+ p) # specify [p]assword to use
+ password="$OPTARG"
+ ;;
+ x) # specify windows [s]hare to use
+ service="$OPTARG"
+ ;;
+ t) # specify [t]apefile on local host
+ tapefile="$OPTARG"
+ ;;
+ u) # specify [u]sername for connection
+ username="$OPTARG"
+ ;;
+ v) # be [v]erbose and display what's going on
+ verbose=""
+ ;;
+ '?') # any other switch
+ Usage 2 "Invalid switch specified - abort."
+ ;;
+ esac
+done
+
+shift `expr $OPTIND - 1`
+
+if [ "$server" = "" ] || [ "$service" = "" ]; then
+ Usage 1 "No server or no service specified - abort."
+fi
+
+# if the -v switch is set, the echo the current parameters
+if [ -z "$verbose" ]; then
+ echo "server is $server"
+# echo "share is $service"
+ echo "share is $service\\$cdcmd"
+ echo "tar args is $tarargs"
+# echo "password is $password" # passwords should never be sent to screen
+ echo "tape is $tapefile"
+ echo "blocksize is $blocksize"
+fi
+
+tarargs=${tarargs}${blocksizearg}${newerarg}
+
+eval $SMBCLIENT "'\\\\$server\\$service'" "'$password'" -U "'$username'" \
+-E $log -D "'$cdcmd'" ${clientargs} \
+-T${tarcmd}${tarargs} $blocksize $newer $tapefile '${1+"$@"}' $verbose
diff --git a/source/script/uninstallbin.sh b/source/script/uninstallbin.sh
new file mode 100755
index 00000000000..5de936fccfd
--- /dev/null
+++ b/source/script/uninstallbin.sh
@@ -0,0 +1,42 @@
+#!/bin/sh
+#4 July 96 Dan.Shearer@UniSA.edu.au
+
+INSTALLPERMS=$1
+BASEDIR=`echo $2 | sed 's/\/\//\//g'`
+BINDIR=`echo $3 | sed 's/\/\//\//g'`
+LIBDIR=`echo $4 | sed 's/\/\//\//g'`
+VARDIR=`echo $5 | sed 's/\/\//\//g'`
+shift
+shift
+shift
+shift
+shift
+
+if [ ! -d $BINDIR ]; then
+ echo Directory $BINDIR does not exist!
+ echo Do a "make installbin" or "make install" first.
+ exit 1
+fi
+
+for p in $*; do
+ p2=`basename $p`
+ if [ -f $BINDIR/$p2 ]; then
+ echo Removing $BINDIR/$p2
+ rm -f $BINDIR/$p2
+ if [ -f $BINDIR/$p2 ]; then
+ echo Cannot remove $BINDIR/$p2 ... does $USER have privileges?
+ fi
+ fi
+done
+
+
+cat << EOF
+======================================================================
+The binaries have been uninstalled. You may restore the binaries using
+the command "make installbin" or "make install" to install binaries,
+man pages, modules and shell scripts. You can restore a previous
+version of the binaries (if there were any) using "make revert".
+======================================================================
+EOF
+
+exit 0
diff --git a/source/script/uninstallman.sh b/source/script/uninstallman.sh
new file mode 100755
index 00000000000..0fea11cd1b2
--- /dev/null
+++ b/source/script/uninstallman.sh
@@ -0,0 +1,37 @@
+#!/bin/sh
+#4 July 96 Dan.Shearer@UniSA.edu.au
+#
+# 13 Aug 2001 Rafal Szczesniak <mimir@spin.ict.pwr.wroc.pl>
+# modified to accomodate international man pages (inspired
+# by Japanese edition's approach)
+
+
+MANDIR=`echo $1 | sed 's/\/\//\//g'`
+SRCDIR=$2
+langs=$3
+
+for lang in $langs; do
+ echo Uninstalling \"$lang\" man pages from $MANDIR/$lang
+
+ for sect in 1 5 7 8 ; do
+ for m in $MANDIR/$lang/man$sect ; do
+ for s in $SRCDIR/../docs/manpages/$lang/*$sect; do
+ FNAME=$m/`basename $s`
+ if test -f $FNAME; then
+ echo Deleting $FNAME
+ rm -f $FNAME
+ test -f $FNAME && echo Cannot remove $FNAME... does $USER have privileges?
+ fi
+ done
+ done
+ done
+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/source/script/uninstallmodules.sh b/source/script/uninstallmodules.sh
new file mode 100755
index 00000000000..ac83af3dc90
--- /dev/null
+++ b/source/script/uninstallmodules.sh
@@ -0,0 +1,37 @@
+#!/bin/sh
+#4 July 96 Dan.Shearer@UniSA.edu.au
+
+INSTALLPERMS=$1
+BASEDIR=`echo $2 | sed 's/\/\//\//g'`
+LIBDIR=`echo $3 | sed 's/\/\//\//g'`
+shift
+shift
+shift
+
+if [ ! -d $LIBDIR ]; then
+ echo Directory $LIBDIR does not exist!
+ echo Do a "make installmodules" 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 modules have been uninstalled. You may restore the modules using
+the command "make installmodules" or "make install" to install
+binaries, modules, man pages and shell scripts.
+======================================================================
+EOF
+
+exit 0
diff --git a/source/script/uninstallscripts.sh b/source/script/uninstallscripts.sh
new file mode 100755
index 00000000000..cf7fd719993
--- /dev/null
+++ b/source/script/uninstallscripts.sh
@@ -0,0 +1,36 @@
+#!/bin/sh
+# 5 July 96 Dan.Shearer@UniSA.Edu.Au - almost identical to uninstallbin.sh
+
+INSTALLPERMS=$1
+BINDIR=`echo $2 | sed 's/\/\//\//g'`
+
+shift
+shift
+
+if [ ! -d $BINDIR ]; then
+ echo Directory $BINDIR does not exist!
+ echo Do a "make installscripts" or "make install" first.
+ exit 1
+fi
+
+for p in $*; do
+ p2=`basename $p`
+ if [ -f $BINDIR/$p2 ]; then
+ echo Removing $BINDIR/$p2
+ rm -f $BINDIR/$p2
+ if [ -f $BINDIR/$p2 ]; then
+ echo Cannot remove $BINDIR/$p2 ... does $USER have privileges?
+ fi
+ fi
+done
+
+cat << EOF
+======================================================================
+The scripts have been uninstalled. You may reinstall them using
+the command "make installscripts" or "make install" to install binaries,
+man pages and shell scripts. You may recover a previous version (if any
+with "make revert".
+======================================================================
+EOF
+
+exit 0
diff --git a/source/script/updatesmbpasswd.sh b/source/script/updatesmbpasswd.sh
new file mode 100755
index 00000000000..1d7e0d7332f
--- /dev/null
+++ b/source/script/updatesmbpasswd.sh
@@ -0,0 +1,14 @@
+#!/bin/sh
+nawk 'BEGIN {FS=":"}
+{
+ if( $0 ~ "^#" ) {
+ print $0
+ } else if( (length($4) == 32) && (($4 ~ "^[0-9A-F]*$") || ($4 ~ "^[X]*$") || ( $4 ~ "^[*]*$"))) {
+ print $0
+ } else {
+ printf( "%s:%s:%s:XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX:", $1, $2, $3);
+ for(i = 4; i <= NF; i++)
+ printf("%s:", $i)
+ printf("\n")
+ }
+}'
diff --git a/source/smbadduser.in b/source/smbadduser.in
new file mode 100644
index 00000000000..05da7de08ee
--- /dev/null
+++ b/source/smbadduser.in
@@ -0,0 +1,79 @@
+#!/bin/csh
+#
+# smbadduser - Written by Mike Zakharoff
+#
+
+prefix=@prefix@
+exec_prefix=@exec_prefix@
+LIBDIR=@libdir@
+PRIVATEDIR=@privatedir@
+CONFIGDIR=@configdir@
+
+unalias *
+set path = ($path /usr/local/samba/bin)
+
+set smbpasswd = $PRIVATEDIR/smbpasswd
+set user_map = $CONFIGDIR/users.map
+
+#
+# Set to site specific passwd command
+#
+set passwd = "cat /etc/passwd"
+#set passwd = "niscat passwd.org_dir"
+#set passwd = "ypcat passwd"
+
+set line = "----------------------------------------------------------"
+if ($#argv == 0) then
+ echo $line
+ echo "Written: Mike Zakharoff email: michael.j.zakharoff@boeing.com"
+ echo ""
+ echo " 1) Updates $smbpasswd"
+ echo " 2) Updates $user_map"
+ echo " 3) Executes smbpasswd for each new user"
+ echo ""
+ echo "smbadduser unixid:ntid unixid:ntid ..."
+ echo ""
+ echo "Example: smbadduser zak:zakharoffm johns:smithj"
+ echo $line
+ exit 1
+endif
+
+touch $smbpasswd $user_map
+set new = ()
+foreach one ($argv)
+ echo $one | grep ':' >& /dev/null
+ if ($status != 0) then
+ echo "ERROR: Must use unixid:ntid like -> zak:zakharoffm"
+ continue
+ endif
+ set unix = `echo $one | awk -F: '{print $1}'`
+ set ntid = `echo $one | awk -F: '{print $2}'`
+
+ set usr = `eval $passwd | awk -F: '$1==USR {print $1}' USR=$unix`
+ if ($#usr != 1) then
+ echo "ERROR: $unix Not in passwd database SKIPPING..."
+ continue
+ endif
+ set tmp = `cat $smbpasswd | awk -F: '$1==USR {print $1}' USR=$unix`
+ if ($#tmp != 0) then
+ echo "ERROR: $unix is already in $smbpasswd SKIPPING..."
+ continue
+ endif
+
+ echo "Adding: $unix to $smbpasswd"
+ /usr/bin/smbpasswd -a -n $unix
+ if ($unix != $ntid) then
+ echo "Adding: {$unix = $ntid} to $user_map"
+ echo "$unix = $ntid" >> $user_map
+ endif
+ set new = ($new $unix)
+end
+
+#
+# Enter password for new users
+#
+foreach one ($new)
+ echo $line
+ echo "ENTER password for $one"
+ smbpasswd $one
+end
diff --git a/source/smbd/.cvsignore b/source/smbd/.cvsignore
new file mode 100644
index 00000000000..d2b1fd5b2ee
--- /dev/null
+++ b/source/smbd/.cvsignore
@@ -0,0 +1,3 @@
+*.po
+*.po32
+build_options.c
diff --git a/source/smbd/blocking.c b/source/smbd/blocking.c
new file mode 100644
index 00000000000..c0512d5539b
--- /dev/null
+++ b/source/smbd/blocking.c
@@ -0,0 +1,726 @@
+/*
+ Unix SMB/CIFS implementation.
+ Blocking Locking functions
+ Copyright (C) Jeremy Allison 1998-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 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"
+
+extern char *OutBuffer;
+
+/****************************************************************************
+ This is the structure to queue to implement blocking locks.
+ notify. It consists of the requesting SMB and the expiry time.
+*****************************************************************************/
+
+typedef struct {
+ ubi_slNode msg_next;
+ int com_type;
+ files_struct *fsp;
+ time_t expire_time;
+ int lock_num;
+ SMB_BIG_UINT offset;
+ SMB_BIG_UINT count;
+ uint16 lock_pid;
+ char *inbuf;
+ int length;
+} blocking_lock_record;
+
+static ubi_slList blocking_lock_queue = { NULL, (ubi_slNodePtr)&blocking_lock_queue, 0};
+
+/****************************************************************************
+ Destructor for the above structure.
+****************************************************************************/
+
+static void free_blocking_lock_record(blocking_lock_record *blr)
+{
+ SAFE_FREE(blr->inbuf);
+ SAFE_FREE(blr);
+}
+
+/****************************************************************************
+ Get the files_struct given a particular queued SMB.
+*****************************************************************************/
+
+static files_struct *get_fsp_from_pkt(char *inbuf)
+{
+ switch(CVAL(inbuf,smb_com)) {
+ case SMBlock:
+ case SMBlockread:
+ return file_fsp(inbuf,smb_vwv0);
+ case SMBlockingX:
+ return file_fsp(inbuf,smb_vwv2);
+ default:
+ DEBUG(0,("get_fsp_from_pkt: PANIC - unknown type on blocking lock queue - exiting.!\n"));
+ exit_server("PANIC - unknown type on blocking lock queue");
+ }
+ return NULL; /* Keep compiler happy. */
+}
+
+/****************************************************************************
+ Determine if this is a secondary element of a chained SMB.
+ **************************************************************************/
+
+static BOOL in_chained_smb(void)
+{
+ return (chain_size != 0);
+}
+
+static void received_unlock_msg(int msg_type, pid_t src, void *buf, size_t len);
+
+/****************************************************************************
+ Function to push a blocking lock request onto the lock queue.
+****************************************************************************/
+
+BOOL push_blocking_lock_request( char *inbuf, int length, int lock_timeout,
+ int lock_num, uint16 lock_pid, SMB_BIG_UINT offset, SMB_BIG_UINT count)
+{
+ static BOOL set_lock_msg;
+ blocking_lock_record *blr;
+ BOOL my_lock_ctx = False;
+ NTSTATUS status;
+
+ if(in_chained_smb() ) {
+ DEBUG(0,("push_blocking_lock_request: cannot queue a chained request (currently).\n"));
+ return False;
+ }
+
+ /*
+ * Now queue an entry on the blocking lock queue. We setup
+ * the expiration time here.
+ */
+
+ if((blr = (blocking_lock_record *)malloc(sizeof(blocking_lock_record))) == NULL) {
+ DEBUG(0,("push_blocking_lock_request: Malloc fail !\n" ));
+ return False;
+ }
+
+ if((blr->inbuf = (char *)malloc(length)) == NULL) {
+ DEBUG(0,("push_blocking_lock_request: Malloc fail (2)!\n" ));
+ SAFE_FREE(blr);
+ return False;
+ }
+
+ blr->com_type = CVAL(inbuf,smb_com);
+ blr->fsp = get_fsp_from_pkt(inbuf);
+ blr->expire_time = (lock_timeout == -1) ? (time_t)-1 : time(NULL) + (time_t)lock_timeout;
+ blr->lock_num = lock_num;
+ blr->lock_pid = lock_pid;
+ blr->offset = offset;
+ blr->count = count;
+ memcpy(blr->inbuf, inbuf, length);
+ blr->length = length;
+
+ /* Add a pending lock record for this. */
+ status = brl_lock(blr->fsp->dev, blr->fsp->inode, blr->fsp->fnum,
+ lock_pid, sys_getpid(), blr->fsp->conn->cnum,
+ offset, count, PENDING_LOCK, &my_lock_ctx);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(0,("push_blocking_lock_request: failed to add PENDING_LOCK record.\n"));
+ free_blocking_lock_record(blr);
+ return False;
+ }
+
+ ubi_slAddTail(&blocking_lock_queue, blr);
+
+ /* Ensure we'll receive messages when this is unlocked. */
+ if (!set_lock_msg) {
+ message_register(MSG_SMB_UNLOCK, received_unlock_msg);
+ set_lock_msg = True;
+ }
+
+ DEBUG(3,("push_blocking_lock_request: lock request length=%d blocked with expiry time %d (+%d) \
+for fnum = %d, name = %s\n", length, (int)blr->expire_time, lock_timeout,
+ blr->fsp->fnum, blr->fsp->fsp_name ));
+
+ /* Push the MID of this packet on the signing queue. */
+ srv_defer_sign_response(SVAL(inbuf,smb_mid));
+
+ return True;
+}
+
+/****************************************************************************
+ Return a smd with a given size.
+*****************************************************************************/
+
+static void send_blocking_reply(char *outbuf, int outsize)
+{
+ if(outsize > 4)
+ smb_setlen(outbuf,outsize - 4);
+
+ if (!send_smb(smbd_server_fd(),outbuf))
+ exit_server("send_blocking_reply: send_smb failed.");
+}
+
+/****************************************************************************
+ Return a lockingX success SMB.
+*****************************************************************************/
+
+static void reply_lockingX_success(blocking_lock_record *blr)
+{
+ char *outbuf = OutBuffer;
+ int bufsize = BUFFER_SIZE;
+ char *inbuf = blr->inbuf;
+ int outsize = 0;
+
+ construct_reply_common(inbuf, outbuf);
+ set_message(outbuf,2,0,True);
+
+ /*
+ * As this message is a lockingX call we must handle
+ * any following chained message correctly.
+ * This is normally handled in construct_reply(),
+ * but as that calls switch_message, we can't use
+ * that here and must set up the chain info manually.
+ */
+
+ outsize = chain_reply(inbuf,outbuf,blr->length,bufsize);
+
+ outsize += chain_size;
+
+ send_blocking_reply(outbuf,outsize);
+}
+
+/****************************************************************************
+ Return a generic lock fail error blocking call.
+*****************************************************************************/
+
+static void generic_blocking_lock_error(blocking_lock_record *blr, NTSTATUS status)
+{
+ char *outbuf = OutBuffer;
+ char *inbuf = blr->inbuf;
+ construct_reply_common(inbuf, outbuf);
+
+ /* whenever a timeout is given w2k maps LOCK_NOT_GRANTED to
+ FILE_LOCK_CONFLICT! (tridge) */
+ if (NT_STATUS_EQUAL(status, NT_STATUS_LOCK_NOT_GRANTED)) {
+ status = NT_STATUS_FILE_LOCK_CONFLICT;
+ }
+
+ ERROR_NT(status);
+ if (!send_smb(smbd_server_fd(),outbuf))
+ exit_server("generic_blocking_lock_error: send_smb failed.");
+}
+
+/****************************************************************************
+ Return a lock fail error for a lockingX call. Undo all the locks we have
+ obtained first.
+*****************************************************************************/
+
+static void reply_lockingX_error(blocking_lock_record *blr, NTSTATUS status)
+{
+ char *inbuf = blr->inbuf;
+ files_struct *fsp = blr->fsp;
+ connection_struct *conn = conn_find(SVAL(inbuf,smb_tid));
+ uint16 num_ulocks = SVAL(inbuf,smb_vwv6);
+ SMB_BIG_UINT count = (SMB_BIG_UINT)0, offset = (SMB_BIG_UINT) 0;
+ uint16 lock_pid;
+ unsigned char locktype = CVAL(inbuf,smb_vwv3);
+ BOOL large_file_format = (locktype & LOCKING_ANDX_LARGE_FILES);
+ char *data;
+ int i;
+
+ data = smb_buf(inbuf) + ((large_file_format ? 20 : 10)*num_ulocks);
+
+ /*
+ * Data now points at the beginning of the list
+ * of smb_lkrng structs.
+ */
+
+ /*
+ * Ensure we don't do a remove on the lock that just failed,
+ * as under POSIX rules, if we have a lock already there, we
+ * will delete it (and we shouldn't) .....
+ */
+
+ for(i = blr->lock_num - 1; i >= 0; i--) {
+ BOOL err;
+
+ lock_pid = get_lock_pid( data, i, large_file_format);
+ count = get_lock_count( data, i, large_file_format);
+ offset = get_lock_offset( data, i, large_file_format, &err);
+
+ /*
+ * We know err cannot be set as if it was the lock
+ * request would never have been queued. JRA.
+ */
+
+ do_unlock(fsp,conn,lock_pid,count,offset);
+ }
+
+ generic_blocking_lock_error(blr, status);
+}
+
+/****************************************************************************
+ Return a lock fail error.
+*****************************************************************************/
+
+static void blocking_lock_reply_error(blocking_lock_record *blr, NTSTATUS status)
+{
+ switch(blr->com_type) {
+ case SMBlock:
+ case SMBlockread:
+ generic_blocking_lock_error(blr, status);
+ break;
+ case SMBlockingX:
+ reply_lockingX_error(blr, status);
+ break;
+ default:
+ DEBUG(0,("blocking_lock_reply_error: PANIC - unknown type on blocking lock queue - exiting.!\n"));
+ exit_server("PANIC - unknown type on blocking lock queue");
+ }
+}
+
+/****************************************************************************
+ Attempt to finish off getting all pending blocking locks for a lockread call.
+ Returns True if we want to be removed from the list.
+*****************************************************************************/
+
+static BOOL process_lockread(blocking_lock_record *blr)
+{
+ char *outbuf = OutBuffer;
+ char *inbuf = blr->inbuf;
+ ssize_t nread = -1;
+ char *data, *p;
+ int outsize = 0;
+ SMB_BIG_UINT startpos;
+ size_t numtoread;
+ NTSTATUS status;
+ connection_struct *conn = conn_find(SVAL(inbuf,smb_tid));
+ files_struct *fsp = blr->fsp;
+ BOOL my_lock_ctx = False;
+
+ numtoread = SVAL(inbuf,smb_vwv1);
+ startpos = (SMB_BIG_UINT)IVAL(inbuf,smb_vwv2);
+
+ numtoread = MIN(BUFFER_SIZE-outsize,numtoread);
+ data = smb_buf(outbuf) + 3;
+
+ status = do_lock_spin( fsp, conn, SVAL(inbuf,smb_pid), (SMB_BIG_UINT)numtoread, startpos, READ_LOCK, &my_lock_ctx);
+ if (NT_STATUS_V(status)) {
+ if (!NT_STATUS_EQUAL(status,NT_STATUS_LOCK_NOT_GRANTED) &&
+ !NT_STATUS_EQUAL(status,NT_STATUS_FILE_LOCK_CONFLICT)) {
+ /*
+ * We have other than a "can't get lock"
+ * error. Send an error.
+ * Return True so we get dequeued.
+ */
+ generic_blocking_lock_error(blr, status);
+ return True;
+ }
+
+ /*
+ * Still waiting for lock....
+ */
+
+ DEBUG(10,("process_lockread: failed to get lock for file = %s. Still waiting....\n",
+ fsp->fsp_name));
+ return False;
+ }
+
+ nread = read_file(fsp,data,startpos,numtoread);
+
+ if (nread < 0) {
+ generic_blocking_lock_error(blr,NT_STATUS_ACCESS_DENIED);
+ return True;
+ }
+
+ construct_reply_common(inbuf, outbuf);
+ outsize = set_message(outbuf,5,0,True);
+
+ outsize += nread;
+ SSVAL(outbuf,smb_vwv0,nread);
+ SSVAL(outbuf,smb_vwv5,nread+3);
+ p = smb_buf(outbuf);
+ *p++ = 1;
+ SSVAL(p,0,nread); p += 2;
+ set_message_end(outbuf, p+nread);
+
+ DEBUG(3, ( "process_lockread file = %s, fnum=%d num=%d nread=%d\n",
+ fsp->fsp_name, fsp->fnum, (int)numtoread, (int)nread ) );
+
+ send_blocking_reply(outbuf,outsize);
+ return True;
+}
+
+/****************************************************************************
+ Attempt to finish off getting all pending blocking locks for a lock call.
+ Returns True if we want to be removed from the list.
+*****************************************************************************/
+
+static BOOL process_lock(blocking_lock_record *blr)
+{
+ char *outbuf = OutBuffer;
+ char *inbuf = blr->inbuf;
+ int outsize;
+ SMB_BIG_UINT count = (SMB_BIG_UINT)0, offset = (SMB_BIG_UINT)0;
+ NTSTATUS status;
+ connection_struct *conn = conn_find(SVAL(inbuf,smb_tid));
+ files_struct *fsp = blr->fsp;
+ BOOL my_lock_ctx = False;
+
+ count = IVAL_TO_SMB_OFF_T(inbuf,smb_vwv1);
+ offset = IVAL_TO_SMB_OFF_T(inbuf,smb_vwv3);
+
+ errno = 0;
+ status = do_lock_spin(fsp, conn, SVAL(inbuf,smb_pid), count, offset, WRITE_LOCK, &my_lock_ctx);
+ if (NT_STATUS_IS_ERR(status)) {
+ if (!NT_STATUS_EQUAL(status,NT_STATUS_LOCK_NOT_GRANTED) &&
+ !NT_STATUS_EQUAL(status,NT_STATUS_FILE_LOCK_CONFLICT)) {
+ /*
+ * We have other than a "can't get lock"
+ * error. Send an error.
+ * Return True so we get dequeued.
+ */
+
+ blocking_lock_reply_error(blr, status);
+ return True;
+ }
+ /*
+ * Still can't get the lock - keep waiting.
+ */
+ DEBUG(10,("process_lock: failed to get lock for file = %s. Still waiting....\n",
+ fsp->fsp_name));
+ return False;
+ }
+
+ /*
+ * Success - we got the lock.
+ */
+
+ DEBUG(3,("process_lock : file=%s fnum=%d offset=%.0f count=%.0f\n",
+ fsp->fsp_name, fsp->fnum, (double)offset, (double)count));
+
+ construct_reply_common(inbuf, outbuf);
+ outsize = set_message(outbuf,0,0,True);
+ send_blocking_reply(outbuf,outsize);
+ return True;
+}
+
+/****************************************************************************
+ Attempt to finish off getting all pending blocking locks for a lockingX call.
+ Returns True if we want to be removed from the list.
+*****************************************************************************/
+
+static BOOL process_lockingX(blocking_lock_record *blr)
+{
+ char *inbuf = blr->inbuf;
+ unsigned char locktype = CVAL(inbuf,smb_vwv3);
+ files_struct *fsp = blr->fsp;
+ connection_struct *conn = conn_find(SVAL(inbuf,smb_tid));
+ uint16 num_ulocks = SVAL(inbuf,smb_vwv6);
+ uint16 num_locks = SVAL(inbuf,smb_vwv7);
+ SMB_BIG_UINT count = (SMB_BIG_UINT)0, offset = (SMB_BIG_UINT)0;
+ uint16 lock_pid;
+ BOOL large_file_format = (locktype & LOCKING_ANDX_LARGE_FILES);
+ char *data;
+ BOOL my_lock_ctx = False;
+ NTSTATUS status = NT_STATUS_OK;
+
+ data = smb_buf(inbuf) + ((large_file_format ? 20 : 10)*num_ulocks);
+
+ /*
+ * Data now points at the beginning of the list
+ * of smb_lkrng structs.
+ */
+
+ for(; blr->lock_num < num_locks; blr->lock_num++) {
+ BOOL err;
+
+ lock_pid = get_lock_pid( data, blr->lock_num, large_file_format);
+ count = get_lock_count( data, blr->lock_num, large_file_format);
+ offset = get_lock_offset( data, blr->lock_num, large_file_format, &err);
+
+ /*
+ * We know err cannot be set as if it was the lock
+ * request would never have been queued. JRA.
+ */
+ errno = 0;
+ status = do_lock_spin(fsp,conn,lock_pid,count,offset,
+ ((locktype & 1) ? READ_LOCK : WRITE_LOCK), &my_lock_ctx);
+ if (NT_STATUS_IS_ERR(status)) break;
+ }
+
+ if(blr->lock_num == num_locks) {
+ /*
+ * Success - we got all the locks.
+ */
+
+ DEBUG(3,("process_lockingX file = %s, fnum=%d type=%d num_locks=%d\n",
+ fsp->fsp_name, fsp->fnum, (unsigned int)locktype, num_locks) );
+
+ reply_lockingX_success(blr);
+ return True;
+ } else if (!NT_STATUS_EQUAL(status,NT_STATUS_LOCK_NOT_GRANTED) &&
+ !NT_STATUS_EQUAL(status,NT_STATUS_FILE_LOCK_CONFLICT)) {
+ /*
+ * We have other than a "can't get lock"
+ * error. Free any locks we had and return an error.
+ * Return True so we get dequeued.
+ */
+
+ blocking_lock_reply_error(blr, status);
+ return True;
+ }
+
+ /*
+ * Still can't get all the locks - keep waiting.
+ */
+
+ DEBUG(10,("process_lockingX: only got %d locks of %d needed for file %s, fnum = %d. \
+Waiting....\n",
+ blr->lock_num, num_locks, fsp->fsp_name, fsp->fnum));
+
+ return False;
+}
+
+/****************************************************************************
+ Process a blocking lock SMB.
+ Returns True if we want to be removed from the list.
+*****************************************************************************/
+
+static BOOL blocking_lock_record_process(blocking_lock_record *blr)
+{
+ switch(blr->com_type) {
+ case SMBlock:
+ return process_lock(blr);
+ case SMBlockread:
+ return process_lockread(blr);
+ case SMBlockingX:
+ return process_lockingX(blr);
+ default:
+ DEBUG(0,("blocking_lock_record_process: PANIC - unknown type on blocking lock queue - exiting.!\n"));
+ exit_server("PANIC - unknown type on blocking lock queue");
+ }
+ return False; /* Keep compiler happy. */
+}
+
+/****************************************************************************
+ Delete entries by fnum from the blocking lock pending queue.
+*****************************************************************************/
+
+void remove_pending_lock_requests_by_fid(files_struct *fsp)
+{
+ blocking_lock_record *blr = (blocking_lock_record *)ubi_slFirst( &blocking_lock_queue );
+ blocking_lock_record *prev = NULL;
+
+ while(blr != NULL) {
+ if(blr->fsp->fnum == fsp->fnum) {
+
+ DEBUG(10,("remove_pending_lock_requests_by_fid - removing request type %d for \
+file %s fnum = %d\n", blr->com_type, fsp->fsp_name, fsp->fnum ));
+
+ brl_unlock(blr->fsp->dev, blr->fsp->inode, blr->fsp->fnum,
+ blr->lock_pid, sys_getpid(), blr->fsp->conn->cnum,
+ blr->offset, blr->count, True, NULL, NULL);
+
+ free_blocking_lock_record((blocking_lock_record *)ubi_slRemNext( &blocking_lock_queue, prev));
+ blr = (blocking_lock_record *)(prev ? ubi_slNext(prev) : ubi_slFirst(&blocking_lock_queue));
+ continue;
+ }
+
+ prev = blr;
+ blr = (blocking_lock_record *)ubi_slNext(blr);
+ }
+}
+
+/****************************************************************************
+ Delete entries by mid from the blocking lock pending queue. Always send reply.
+*****************************************************************************/
+
+void remove_pending_lock_requests_by_mid(int mid)
+{
+ blocking_lock_record *blr = (blocking_lock_record *)ubi_slFirst( &blocking_lock_queue );
+ blocking_lock_record *prev = NULL;
+
+ while(blr != NULL) {
+ if(SVAL(blr->inbuf,smb_mid) == mid) {
+ files_struct *fsp = blr->fsp;
+
+ DEBUG(10,("remove_pending_lock_requests_by_mid - removing request type %d for \
+file %s fnum = %d\n", blr->com_type, fsp->fsp_name, fsp->fnum ));
+
+ blocking_lock_reply_error(blr,NT_STATUS_FILE_LOCK_CONFLICT);
+ brl_unlock(blr->fsp->dev, blr->fsp->inode, blr->fsp->fnum,
+ blr->lock_pid, sys_getpid(), blr->fsp->conn->cnum,
+ blr->offset, blr->count, True, NULL, NULL);
+ free_blocking_lock_record((blocking_lock_record *)ubi_slRemNext( &blocking_lock_queue, prev));
+ blr = (blocking_lock_record *)(prev ? ubi_slNext(prev) : ubi_slFirst(&blocking_lock_queue));
+ continue;
+ }
+
+ prev = blr;
+ blr = (blocking_lock_record *)ubi_slNext(blr);
+ }
+}
+
+/****************************************************************************
+ Set a flag as an unlock request affects one of our pending locks.
+*****************************************************************************/
+
+static void received_unlock_msg(int msg_type, pid_t src, void *buf, size_t len)
+{
+ DEBUG(10,("received_unlock_msg\n"));
+ process_blocking_lock_queue(time(NULL));
+}
+
+/****************************************************************************
+ Return the number of seconds to the next blocking locks timeout, or default_timeout
+*****************************************************************************/
+
+unsigned blocking_locks_timeout(unsigned default_timeout)
+{
+ unsigned timeout = default_timeout;
+ time_t t;
+ blocking_lock_record *blr = (blocking_lock_record *)ubi_slFirst(&blocking_lock_queue);
+
+ /* note that we avoid the time() syscall if there are no blocking locks */
+ if (!blr)
+ return timeout;
+
+ t = time(NULL);
+
+ while (blr) {
+ if ((blr->expire_time != (time_t)-1) &&
+ (timeout > (blr->expire_time - t))) {
+ timeout = blr->expire_time - t;
+ }
+ blr = (blocking_lock_record *)ubi_slNext(blr);
+ }
+
+ if (timeout < 1)
+ timeout = 1;
+
+ return timeout;
+}
+
+/****************************************************************************
+ Process the blocking lock queue. Note that this is only called as root.
+*****************************************************************************/
+
+void process_blocking_lock_queue(time_t t)
+{
+ blocking_lock_record *blr = (blocking_lock_record *)ubi_slFirst( &blocking_lock_queue );
+ blocking_lock_record *prev = NULL;
+
+ if(blr == NULL)
+ return;
+
+ /*
+ * Go through the queue and see if we can get any of the locks.
+ */
+
+ while(blr != NULL) {
+ connection_struct *conn = NULL;
+ uint16 vuid;
+ files_struct *fsp = NULL;
+
+ /*
+ * Ensure we don't have any old chain_fsp values
+ * sitting around....
+ */
+ chain_size = 0;
+ file_chain_reset();
+ fsp = blr->fsp;
+
+ conn = conn_find(SVAL(blr->inbuf,smb_tid));
+ vuid = (lp_security() == SEC_SHARE) ? UID_FIELD_INVALID :
+ SVAL(blr->inbuf,smb_uid);
+
+ DEBUG(5,("process_blocking_lock_queue: examining pending lock fnum = %d for file %s\n",
+ fsp->fnum, fsp->fsp_name ));
+
+ if((blr->expire_time != -1) && (blr->expire_time <= t)) {
+ /*
+ * Lock expired - throw away all previously
+ * obtained locks and return lock error.
+ */
+ DEBUG(5,("process_blocking_lock_queue: pending lock fnum = %d for file %s timed out.\n",
+ fsp->fnum, fsp->fsp_name ));
+
+ brl_unlock(fsp->dev, fsp->inode, fsp->fnum,
+ blr->lock_pid, sys_getpid(), conn->cnum,
+ blr->offset, blr->count, True, NULL, NULL);
+
+ blocking_lock_reply_error(blr,NT_STATUS_FILE_LOCK_CONFLICT);
+ free_blocking_lock_record((blocking_lock_record *)ubi_slRemNext( &blocking_lock_queue, prev));
+ blr = (blocking_lock_record *)(prev ? ubi_slNext(prev) : ubi_slFirst(&blocking_lock_queue));
+ continue;
+ }
+
+ if(!change_to_user(conn,vuid)) {
+ DEBUG(0,("process_blocking_lock_queue: Unable to become user vuid=%d.\n",
+ vuid ));
+ /*
+ * Remove the entry and return an error to the client.
+ */
+ blocking_lock_reply_error(blr,NT_STATUS_ACCESS_DENIED);
+
+ brl_unlock(fsp->dev, fsp->inode, fsp->fnum,
+ blr->lock_pid, sys_getpid(), conn->cnum,
+ blr->offset, blr->count, True, NULL, NULL);
+
+ free_blocking_lock_record((blocking_lock_record *)ubi_slRemNext( &blocking_lock_queue, prev));
+ blr = (blocking_lock_record *)(prev ? ubi_slNext(prev) : ubi_slFirst(&blocking_lock_queue));
+ continue;
+ }
+
+ if(!set_current_service(conn,True)) {
+ DEBUG(0,("process_blocking_lock_queue: Unable to become service Error was %s.\n", strerror(errno) ));
+ /*
+ * Remove the entry and return an error to the client.
+ */
+ blocking_lock_reply_error(blr,NT_STATUS_ACCESS_DENIED);
+
+ brl_unlock(fsp->dev, fsp->inode, fsp->fnum,
+ blr->lock_pid, sys_getpid(), conn->cnum,
+ blr->offset, blr->count, True, NULL, NULL);
+
+ free_blocking_lock_record((blocking_lock_record *)ubi_slRemNext( &blocking_lock_queue, prev));
+ blr = (blocking_lock_record *)(prev ? ubi_slNext(prev) : ubi_slFirst(&blocking_lock_queue));
+ change_to_root_user();
+ continue;
+ }
+
+ /*
+ * Go through the remaining locks and try and obtain them.
+ * The call returns True if all locks were obtained successfully
+ * and False if we still need to wait.
+ */
+
+ if(blocking_lock_record_process(blr)) {
+
+ brl_unlock(fsp->dev, fsp->inode, fsp->fnum,
+ blr->lock_pid, sys_getpid(), conn->cnum,
+ blr->offset, blr->count, True, NULL, NULL);
+
+ free_blocking_lock_record((blocking_lock_record *)ubi_slRemNext( &blocking_lock_queue, prev));
+ blr = (blocking_lock_record *)(prev ? ubi_slNext(prev) : ubi_slFirst(&blocking_lock_queue));
+ change_to_root_user();
+ continue;
+ }
+
+ change_to_root_user();
+
+ /*
+ * Move to the next in the list.
+ */
+ prev = blr;
+ blr = (blocking_lock_record *)ubi_slNext(blr);
+ }
+}
diff --git a/source/smbd/change_trust_pw.c b/source/smbd/change_trust_pw.c
new file mode 100644
index 00000000000..1178400e4db
--- /dev/null
+++ b/source/smbd/change_trust_pw.c
@@ -0,0 +1,99 @@
+/*
+ * Unix SMB/CIFS implementation.
+ * Periodic Trust account password changing.
+ * Copyright (C) Andrew Tridgell 1992-1997,
+ * Copyright (C) Luke Kenneth Casson Leighton 1996-1997,
+ * Copyright (C) Paul Ashton 1997.
+ * Copyright (C) Jeremy Allison 1998.
+ * Copyright (C) Andrew Bartlett 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 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"
+
+/************************************************************************
+ Change the trust account password for a domain.
+************************************************************************/
+
+NTSTATUS change_trust_account_password( const char *domain, const char *remote_machine)
+{
+ NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
+ struct in_addr pdc_ip;
+ fstring dc_name;
+ struct cli_state *cli;
+
+ DEBUG(5,("change_trust_account_password: Attempting to change trust account password in domain %s....\n",
+ domain));
+
+ if (remote_machine == NULL || !strcmp(remote_machine, "*")) {
+ /* Use the PDC *only* for this */
+
+ if ( !get_pdc_ip(domain, &pdc_ip) ) {
+ DEBUG(0,("Can't get IP for PDC for domain %s\n", domain));
+ goto failed;
+ }
+
+ if ( !name_status_find( domain, 0x1b, 0x20, pdc_ip, dc_name) )
+ goto failed;
+ } else {
+ /* supoport old deprecated "smbpasswd -j DOMAIN -r MACHINE" behavior */
+ fstrcpy( dc_name, remote_machine );
+ }
+
+ /* if this next call fails, then give up. We can't do
+ password changes on BDC's --jerry */
+
+ if (!NT_STATUS_IS_OK(cli_full_connection(&cli, global_myname(), dc_name,
+ NULL, 0,
+ "IPC$", "IPC",
+ "", "",
+ "", 0, Undefined, NULL))) {
+ DEBUG(0,("modify_trust_password: Connection to %s failed!\n", dc_name));
+ nt_status = NT_STATUS_UNSUCCESSFUL;
+ goto failed;
+ }
+
+ /*
+ * Ok - we have an anonymous connection to the IPC$ share.
+ * Now start the NT Domain stuff :-).
+ */
+
+ if(cli_nt_session_open(cli, PI_NETLOGON) == False) {
+ DEBUG(0,("modify_trust_password: unable to open the domain client session to machine %s. Error was : %s.\n",
+ dc_name, cli_errstr(cli)));
+ cli_nt_session_close(cli);
+ cli_ulogoff(cli);
+ cli_shutdown(cli);
+ nt_status = NT_STATUS_UNSUCCESSFUL;
+ goto failed;
+ }
+
+ nt_status = trust_pw_find_change_and_store_it(cli, cli->mem_ctx, domain);
+
+ cli_nt_session_close(cli);
+ cli_ulogoff(cli);
+ cli_shutdown(cli);
+
+failed:
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ DEBUG(0,("%s : change_trust_account_password: Failed to change password for domain %s.\n",
+ timestring(False), domain));
+ }
+ else
+ DEBUG(5,("change_trust_account_password: sucess!\n"));
+
+ return nt_status;
+}
diff --git a/source/smbd/chgpasswd.c b/source/smbd/chgpasswd.c
new file mode 100644
index 00000000000..4192cc3a239
--- /dev/null
+++ b/source/smbd/chgpasswd.c
@@ -0,0 +1,1054 @@
+/*
+ Unix SMB/CIFS implementation.
+ Samba utility functions
+ Copyright (C) Andrew Tridgell 1992-1998
+ Copyright (C) Andrew Bartlett 2001-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 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.
+*/
+
+/* These comments regard the code to change the user's unix password: */
+
+/* fork a child process to exec passwd and write to its
+ * tty to change a users password. This is running as the
+ * user who is attempting to change the password.
+ */
+
+/*
+ * This code was copied/borrowed and stolen from various sources.
+ * The primary source was the poppasswd.c from the authors of POPMail. This software
+ * was included as a client to change passwords using the 'passwd' program
+ * on the remote machine.
+ *
+ * This code has been hacked by Bob Nance (nance@niehs.nih.gov) and Evan Patterson
+ * (patters2@niehs.nih.gov) at the National Institute of Environmental Health Sciences
+ * and rights to modify, distribute or incorporate this change to the CAP suite or
+ * using it for any other reason are granted, so long as this disclaimer is left intact.
+ */
+
+/*
+ This code was hacked considerably for inclusion in Samba, primarily
+ by Andrew.Tridgell@anu.edu.au. The biggest change was the addition
+ of the "password chat" option, which allows the easy runtime
+ specification of the expected sequence of events to change a
+ password.
+ */
+
+#include "includes.h"
+
+#ifdef HAVE_WORKING_CRACKLIB
+#include <crack.h>
+
+#ifndef HAVE_CRACKLIB_DICTPATH
+#ifndef CRACKLIB_DICTPATH
+#define CRACKLIB_DICTPATH SAMBA_CRACKLIB_DICTPATH
+#endif
+#endif
+#endif
+
+extern struct passdb_ops pdb_ops;
+
+static NTSTATUS check_oem_password(const char *user,
+ uchar password_encrypted_with_lm_hash[516],
+ const uchar old_lm_hash_encrypted[16],
+ uchar password_encrypted_with_nt_hash[516],
+ const uchar old_nt_hash_encrypted[16],
+ SAM_ACCOUNT **hnd, char *new_passwd,
+ int new_passwd_size);
+
+#if ALLOW_CHANGE_PASSWORD
+
+static int findpty(char **slave)
+{
+ int master;
+ static fstring line;
+ DIR *dirp;
+ const char *dpname;
+
+#if defined(HAVE_GRANTPT)
+ /* Try to open /dev/ptmx. If that fails, fall through to old method. */
+ if ((master = sys_open("/dev/ptmx", O_RDWR, 0)) >= 0)
+ {
+ grantpt(master);
+ unlockpt(master);
+ *slave = (char *)ptsname(master);
+ if (*slave == NULL)
+ {
+ DEBUG(0,
+ ("findpty: Unable to create master/slave pty pair.\n"));
+ /* Stop fd leak on error. */
+ close(master);
+ return -1;
+ }
+ else
+ {
+ DEBUG(10,
+ ("findpty: Allocated slave pty %s\n", *slave));
+ return (master);
+ }
+ }
+#endif /* HAVE_GRANTPT */
+
+ fstrcpy(line, "/dev/ptyXX");
+
+ dirp = opendir("/dev");
+ if (!dirp)
+ return (-1);
+ while ((dpname = readdirname(dirp)) != NULL)
+ {
+ if (strncmp(dpname, "pty", 3) == 0 && strlen(dpname) == 5)
+ {
+ DEBUG(3,
+ ("pty: try to open %s, line was %s\n", dpname,
+ line));
+ line[8] = dpname[3];
+ line[9] = dpname[4];
+ if ((master = sys_open(line, O_RDWR, 0)) >= 0)
+ {
+ DEBUG(3, ("pty: opened %s\n", line));
+ line[5] = 't';
+ *slave = line;
+ closedir(dirp);
+ return (master);
+ }
+ }
+ }
+ closedir(dirp);
+ return (-1);
+}
+
+static int dochild(int master, const char *slavedev, const struct passwd *pass,
+ const char *passwordprogram, BOOL as_root)
+{
+ int slave;
+ struct termios stermios;
+ gid_t gid;
+ uid_t uid;
+
+ if (pass == NULL)
+ {
+ DEBUG(0,
+ ("dochild: user doesn't exist in the UNIX password database.\n"));
+ return False;
+ }
+
+ gid = pass->pw_gid;
+ uid = pass->pw_uid;
+
+ gain_root_privilege();
+
+ /* Start new session - gets rid of controlling terminal. */
+ if (setsid() < 0)
+ {
+ DEBUG(3,
+ ("Weirdness, couldn't let go of controlling terminal\n"));
+ return (False);
+ }
+
+ /* Open slave pty and acquire as new controlling terminal. */
+ if ((slave = sys_open(slavedev, O_RDWR, 0)) < 0)
+ {
+ DEBUG(3, ("More weirdness, could not open %s\n", slavedev));
+ return (False);
+ }
+#ifdef I_PUSH
+ ioctl(slave, I_PUSH, "ptem");
+ ioctl(slave, I_PUSH, "ldterm");
+#elif defined(TIOCSCTTY)
+ if (ioctl(slave, TIOCSCTTY, 0) < 0)
+ {
+ DEBUG(3, ("Error in ioctl call for slave pty\n"));
+ /* return(False); */
+ }
+#endif
+
+ /* Close master. */
+ close(master);
+
+ /* Make slave stdin/out/err of child. */
+
+ if (sys_dup2(slave, STDIN_FILENO) != STDIN_FILENO)
+ {
+ DEBUG(3, ("Could not re-direct stdin\n"));
+ return (False);
+ }
+ if (sys_dup2(slave, STDOUT_FILENO) != STDOUT_FILENO)
+ {
+ DEBUG(3, ("Could not re-direct stdout\n"));
+ return (False);
+ }
+ if (sys_dup2(slave, STDERR_FILENO) != STDERR_FILENO)
+ {
+ DEBUG(3, ("Could not re-direct stderr\n"));
+ return (False);
+ }
+ if (slave > 2)
+ close(slave);
+
+ /* Set proper terminal attributes - no echo, canonical input processing,
+ no map NL to CR/NL on output. */
+
+ if (tcgetattr(0, &stermios) < 0)
+ {
+ DEBUG(3,
+ ("could not read default terminal attributes on pty\n"));
+ return (False);
+ }
+ stermios.c_lflag &= ~(ECHO | ECHOE | ECHOK | ECHONL);
+ stermios.c_lflag |= ICANON;
+#ifdef ONLCR
+ stermios.c_oflag &= ~(ONLCR);
+#endif
+ if (tcsetattr(0, TCSANOW, &stermios) < 0)
+ {
+ DEBUG(3, ("could not set attributes of pty\n"));
+ return (False);
+ }
+
+ /* make us completely into the right uid */
+ if (!as_root)
+ {
+ become_user_permanently(uid, gid);
+ }
+
+ DEBUG(10,
+ ("Invoking '%s' as password change program.\n",
+ passwordprogram));
+
+ /* execl() password-change application */
+ if (execl("/bin/sh", "sh", "-c", passwordprogram, NULL) < 0)
+ {
+ DEBUG(3, ("Bad status returned from %s\n", passwordprogram));
+ return (False);
+ }
+ return (True);
+}
+
+static int expect(int master, char *issue, char *expected)
+{
+ pstring buffer;
+ int attempts, timeout, nread, len;
+ BOOL match = False;
+
+ for (attempts = 0; attempts < 2; attempts++) {
+ if (!strequal(issue, ".")) {
+ if (lp_passwd_chat_debug())
+ DEBUG(100, ("expect: sending [%s]\n", issue));
+
+ if ((len = write(master, issue, strlen(issue))) != strlen(issue)) {
+ DEBUG(2,("expect: (short) write returned %d\n", len ));
+ return False;
+ }
+ }
+
+ if (strequal(expected, "."))
+ return True;
+
+ /* Initial timeout. */
+ timeout = lp_passwd_chat_timeout() * 1000;
+ nread = 0;
+ buffer[nread] = 0;
+
+ while ((len = read_socket_with_timeout(master, buffer + nread, 1,
+ sizeof(buffer) - nread - 1,
+ timeout)) > 0) {
+ nread += len;
+ buffer[nread] = 0;
+
+ {
+ /* Eat leading/trailing whitespace before match. */
+ pstring str;
+ pstrcpy( str, buffer);
+ trim_char( str, ' ', ' ');
+
+ if ((match = (unix_wild_match(expected, str) == 0))) {
+ /* Now data has started to return, lower timeout. */
+ timeout = lp_passwd_chat_timeout() * 100;
+ }
+ }
+ }
+
+ if (lp_passwd_chat_debug())
+ DEBUG(100, ("expect: expected [%s] received [%s] match %s\n",
+ expected, buffer, match ? "yes" : "no" ));
+
+ if (match)
+ break;
+
+ if (len < 0) {
+ DEBUG(2, ("expect: %s\n", strerror(errno)));
+ return False;
+ }
+ }
+
+ DEBUG(10,("expect: returning %s\n", match ? "True" : "False" ));
+ return match;
+}
+
+static void pwd_sub(char *buf)
+{
+ all_string_sub(buf, "\\n", "\n", 0);
+ all_string_sub(buf, "\\r", "\r", 0);
+ all_string_sub(buf, "\\s", " ", 0);
+ all_string_sub(buf, "\\t", "\t", 0);
+}
+
+static int talktochild(int master, const char *seq)
+{
+ int count = 0;
+ fstring issue, expected;
+
+ fstrcpy(issue, ".");
+
+ while (next_token(&seq, expected, NULL, sizeof(expected)))
+ {
+ pwd_sub(expected);
+ count++;
+
+ if (!expect(master, issue, expected))
+ {
+ DEBUG(3, ("Response %d incorrect\n", count));
+ return False;
+ }
+
+ if (!next_token(&seq, issue, NULL, sizeof(issue)))
+ fstrcpy(issue, ".");
+
+ pwd_sub(issue);
+ }
+ if (!strequal(issue, ".")) {
+ /* we have one final issue to send */
+ fstrcpy(expected, ".");
+ if (!expect(master, issue, expected))
+ return False;
+ }
+
+ return (count > 0);
+}
+
+static BOOL chat_with_program(char *passwordprogram, struct passwd *pass,
+ char *chatsequence, BOOL as_root)
+{
+ char *slavedev;
+ int master;
+ pid_t pid, wpid;
+ int wstat;
+ BOOL chstat = False;
+
+ if (pass == NULL) {
+ DEBUG(0, ("chat_with_program: user doesn't exist in the UNIX password database.\n"));
+ return False;
+ }
+
+ /* allocate a pseudo-terminal device */
+ if ((master = findpty(&slavedev)) < 0) {
+ DEBUG(3, ("chat_with_program: Cannot Allocate pty for password change: %s\n", pass->pw_name));
+ return (False);
+ }
+
+ /*
+ * We need to temporarily stop CatchChild from eating
+ * SIGCLD signals as it also eats the exit status code. JRA.
+ */
+
+ CatchChildLeaveStatus();
+
+ if ((pid = sys_fork()) < 0) {
+ DEBUG(3, ("chat_with_program: Cannot fork() child for password change: %s\n", pass->pw_name));
+ close(master);
+ CatchChild();
+ return (False);
+ }
+
+ /* we now have a pty */
+ if (pid > 0) { /* This is the parent process */
+ if ((chstat = talktochild(master, chatsequence)) == False) {
+ DEBUG(3, ("chat_with_program: Child failed to change password: %s\n", pass->pw_name));
+ kill(pid, SIGKILL); /* be sure to end this process */
+ }
+
+ while ((wpid = sys_waitpid(pid, &wstat, 0)) < 0) {
+ if (errno == EINTR) {
+ errno = 0;
+ continue;
+ }
+ break;
+ }
+
+ if (wpid < 0) {
+ DEBUG(3, ("chat_with_program: The process is no longer waiting!\n\n"));
+ close(master);
+ CatchChild();
+ return (False);
+ }
+
+ /*
+ * Go back to ignoring children.
+ */
+ CatchChild();
+
+ close(master);
+
+ if (pid != wpid) {
+ DEBUG(3, ("chat_with_program: We were waiting for the wrong process ID\n"));
+ return (False);
+ }
+ if (WIFEXITED(wstat) && (WEXITSTATUS(wstat) != 0)) {
+ DEBUG(3, ("chat_with_program: The process exited with status %d \
+while we were waiting\n", WEXITSTATUS(wstat)));
+ return (False);
+ }
+#if defined(WIFSIGNALLED) && defined(WTERMSIG)
+ else if (WIFSIGNALLED(wstat)) {
+ DEBUG(3, ("chat_with_program: The process was killed by signal %d \
+while we were waiting\n", WTERMSIG(wstat)));
+ return (False);
+ }
+#endif
+ } else {
+ /* CHILD */
+
+ /*
+ * Lose any oplock capabilities.
+ */
+ oplock_set_capability(False, False);
+
+ /* make sure it doesn't freeze */
+ alarm(20);
+
+ if (as_root)
+ become_root();
+
+ DEBUG(3, ("chat_with_program: Dochild for user %s (uid=%d,gid=%d) (as_root = %s)\n", pass->pw_name,
+ (int)getuid(), (int)getgid(), BOOLSTR(as_root) ));
+ chstat = dochild(master, slavedev, pass, passwordprogram, as_root);
+
+ if (as_root)
+ unbecome_root();
+
+ /*
+ * The child should never return from dochild() ....
+ */
+
+ DEBUG(0, ("chat_with_program: Error: dochild() returned %d\n", chstat));
+ exit(1);
+ }
+
+ if (chstat)
+ DEBUG(3, ("chat_with_program: Password change %ssuccessful for user %s\n",
+ (chstat ? "" : "un"), pass->pw_name));
+ return (chstat);
+}
+
+BOOL chgpasswd(const char *name, const struct passwd *pass,
+ const char *oldpass, const char *newpass, BOOL as_root)
+{
+ pstring passwordprogram;
+ pstring chatsequence;
+ size_t i;
+ size_t len;
+
+ if (!oldpass) {
+ oldpass = "";
+ }
+
+ DEBUG(3, ("chgpasswd: Password change (as_root=%s) for user: %s\n", BOOLSTR(as_root), name));
+
+#if DEBUG_PASSWORD
+ DEBUG(100, ("chgpasswd: Passwords: old=%s new=%s\n", oldpass, newpass));
+#endif
+
+ /* Take the passed information and test it for minimum criteria */
+
+ /* Password is same as old password */
+ if (strcmp(oldpass, newpass) == 0) {
+ /* don't allow same password */
+ DEBUG(2, ("chgpasswd: Password Change: %s, New password is same as old\n", name)); /* log the attempt */
+ return (False); /* inform the user */
+ }
+
+ /*
+ * Check the old and new passwords don't contain any control
+ * characters.
+ */
+
+ len = strlen(oldpass);
+ for (i = 0; i < len; i++) {
+ if (iscntrl((int)oldpass[i])) {
+ DEBUG(0, ("chgpasswd: oldpass contains control characters (disallowed).\n"));
+ return False;
+ }
+ }
+
+ len = strlen(newpass);
+ for (i = 0; i < len; i++) {
+ if (iscntrl((int)newpass[i])) {
+ DEBUG(0, ("chgpasswd: newpass contains control characters (disallowed).\n"));
+ return False;
+ }
+ }
+
+#ifdef WITH_PAM
+ if (lp_pam_password_change()) {
+ BOOL ret;
+
+ if (as_root)
+ become_root();
+
+ if (pass) {
+ ret = smb_pam_passchange(pass->pw_name, oldpass, newpass);
+ } else {
+ ret = smb_pam_passchange(name, oldpass, newpass);
+ }
+
+ if (as_root)
+ unbecome_root();
+
+ return ret;
+ }
+#endif
+
+ /* A non-PAM password change just doen't make sense without a valid local user */
+
+ if (pass == NULL) {
+ DEBUG(0, ("chgpasswd: user %s doesn't exist in the UNIX password database.\n", name));
+ return False;
+ }
+
+ pstrcpy(passwordprogram, lp_passwd_program());
+ pstrcpy(chatsequence, lp_passwd_chat());
+
+ if (!*chatsequence) {
+ DEBUG(2, ("chgpasswd: Null chat sequence - no password changing\n"));
+ return (False);
+ }
+
+ if (!*passwordprogram) {
+ DEBUG(2, ("chgpasswd: Null password program - no password changing\n"));
+ return (False);
+ }
+
+ if (as_root) {
+ /* The password program *must* contain the user name to work. Fail if not. */
+ if (strstr_m(passwordprogram, "%u") == NULL) {
+ DEBUG(0,("chgpasswd: Running as root the 'passwd program' parameter *MUST* contain \
+the string %%u, and the given string %s does not.\n", passwordprogram ));
+ return False;
+ }
+ }
+
+ pstring_sub(passwordprogram, "%u", name);
+ /* note that we do NOT substitute the %o and %n in the password program
+ as this would open up a security hole where the user could use
+ a new password containing shell escape characters */
+
+ pstring_sub(chatsequence, "%u", name);
+ all_string_sub(chatsequence, "%o", oldpass, sizeof(pstring));
+ all_string_sub(chatsequence, "%n", newpass, sizeof(pstring));
+ return (chat_with_program
+ (passwordprogram, pass, chatsequence, as_root));
+}
+
+#else /* ALLOW_CHANGE_PASSWORD */
+
+BOOL chgpasswd(const char *name, const struct passwd *pass,
+ const char *oldpass, const char *newpass, BOOL as_root)
+{
+ DEBUG(0, ("chgpasswd: Unix Password changing not compiled in (user=%s)\n", name));
+ return (False);
+}
+#endif /* ALLOW_CHANGE_PASSWORD */
+
+/***********************************************************
+ Code to check the lanman hashed password.
+************************************************************/
+
+BOOL check_lanman_password(char *user, uchar * pass1,
+ uchar * pass2, SAM_ACCOUNT **hnd)
+{
+ uchar unenc_new_pw[16];
+ uchar unenc_old_pw[16];
+ SAM_ACCOUNT *sampass = NULL;
+ uint16 acct_ctrl;
+ const uint8 *lanman_pw;
+ BOOL ret;
+
+ become_root();
+ ret = pdb_getsampwnam(sampass, user);
+ unbecome_root();
+
+ if (ret == False) {
+ DEBUG(0,("check_lanman_password: getsampwnam returned NULL\n"));
+ pdb_free_sam(&sampass);
+ return False;
+ }
+
+ acct_ctrl = pdb_get_acct_ctrl (sampass);
+ lanman_pw = pdb_get_lanman_passwd (sampass);
+
+ if (acct_ctrl & ACB_DISABLED) {
+ DEBUG(0,("check_lanman_password: account %s disabled.\n", user));
+ pdb_free_sam(&sampass);
+ return False;
+ }
+
+ if (lanman_pw == NULL) {
+ if (acct_ctrl & ACB_PWNOTREQ) {
+ /* this saves the pointer for the caller */
+ *hnd = sampass;
+ return True;
+ } else {
+ DEBUG(0, ("check_lanman_password: no lanman password !\n"));
+ pdb_free_sam(&sampass);
+ return False;
+ }
+ }
+
+ /* Get the new lanman hash. */
+ D_P16(lanman_pw, pass2, unenc_new_pw);
+
+ /* Use this to get the old lanman hash. */
+ D_P16(unenc_new_pw, pass1, unenc_old_pw);
+
+ /* Check that the two old passwords match. */
+ if (memcmp(lanman_pw, unenc_old_pw, 16)) {
+ DEBUG(0,("check_lanman_password: old password doesn't match.\n"));
+ pdb_free_sam(&sampass);
+ return False;
+ }
+
+ /* this saves the pointer for the caller */
+ *hnd = sampass;
+ return True;
+}
+
+/***********************************************************
+ Code to change the lanman hashed password.
+ It nulls out the NT hashed password as it will
+ no longer be valid.
+ NOTE this function is designed to be called as root. Check the old password
+ is correct before calling. JRA.
+************************************************************/
+
+BOOL change_lanman_password(SAM_ACCOUNT *sampass, uchar *pass2)
+{
+ static uchar null_pw[16];
+ uchar unenc_new_pw[16];
+ BOOL ret;
+ uint16 acct_ctrl;
+ const uint8 *pwd;
+
+ if (sampass == NULL) {
+ DEBUG(0,("change_lanman_password: no smb password entry.\n"));
+ return False;
+ }
+
+ acct_ctrl = pdb_get_acct_ctrl(sampass);
+ pwd = pdb_get_lanman_passwd(sampass);
+
+ if (acct_ctrl & ACB_DISABLED) {
+ DEBUG(0,("change_lanman_password: account %s disabled.\n",
+ pdb_get_username(sampass)));
+ return False;
+ }
+
+ if (pwd == NULL) {
+ if (acct_ctrl & ACB_PWNOTREQ) {
+ uchar no_pw[14];
+ memset(no_pw, '\0', 14);
+ E_P16(no_pw, null_pw);
+
+ /* Get the new lanman hash. */
+ D_P16(null_pw, pass2, unenc_new_pw);
+ } else {
+ DEBUG(0,("change_lanman_password: no lanman password !\n"));
+ return False;
+ }
+ } else {
+ /* Get the new lanman hash. */
+ D_P16(pwd, pass2, unenc_new_pw);
+ }
+
+ if (!pdb_set_lanman_passwd(sampass, unenc_new_pw, PDB_CHANGED)) {
+ return False;
+ }
+
+ if (!pdb_set_nt_passwd (sampass, NULL, PDB_CHANGED)) {
+ return False; /* We lose the NT hash. Sorry. */
+ }
+
+ if (!pdb_set_pass_changed_now (sampass)) {
+ pdb_free_sam(&sampass);
+ /* Not quite sure what this one qualifies as, but this will do */
+ return False;
+ }
+
+ /* Now flush the sam_passwd struct to persistent storage */
+ ret = pdb_update_sam_account (sampass);
+
+ return ret;
+}
+
+/***********************************************************
+ Code to check and change the OEM hashed password.
+************************************************************/
+
+NTSTATUS pass_oem_change(char *user,
+ uchar password_encrypted_with_lm_hash[516],
+ const uchar old_lm_hash_encrypted[16],
+ uchar password_encrypted_with_nt_hash[516],
+ const uchar old_nt_hash_encrypted[16])
+{
+ pstring new_passwd;
+ SAM_ACCOUNT *sampass = NULL;
+ NTSTATUS nt_status = check_oem_password(user, password_encrypted_with_lm_hash,
+ old_lm_hash_encrypted,
+ password_encrypted_with_nt_hash,
+ old_nt_hash_encrypted,
+ &sampass, new_passwd, sizeof(new_passwd));
+
+ if (!NT_STATUS_IS_OK(nt_status))
+ return nt_status;
+
+ /* We've already checked the old password here.... */
+ become_root();
+ nt_status = change_oem_password(sampass, NULL, new_passwd, True);
+ unbecome_root();
+
+ memset(new_passwd, 0, sizeof(new_passwd));
+
+ pdb_free_sam(&sampass);
+
+ return nt_status;
+}
+
+/***********************************************************
+ Decrypt and verify a user password change.
+
+ The 516 byte long buffers are encrypted with the old NT and
+ old LM passwords, and if the NT passwords are present, both
+ buffers contain a unicode string.
+
+ After decrypting the buffers, check the password is correct by
+ matching the old hashed passwords with the passwords in the passdb.
+
+************************************************************/
+
+static NTSTATUS check_oem_password(const char *user,
+ uchar password_encrypted_with_lm_hash[516],
+ const uchar old_lm_hash_encrypted[16],
+ uchar password_encrypted_with_nt_hash[516],
+ const uchar old_nt_hash_encrypted[16],
+ SAM_ACCOUNT **hnd, char *new_passwd,
+ int new_passwd_size)
+{
+ static uchar null_pw[16];
+ static uchar null_ntpw[16];
+ SAM_ACCOUNT *sampass = NULL;
+ char *password_encrypted;
+ const char *encryption_key;
+ const uint8 *lanman_pw, *nt_pw;
+ uint16 acct_ctrl;
+ uint32 new_pw_len;
+ uchar new_nt_hash[16];
+ uchar old_nt_hash_plain[16];
+ uchar new_lm_hash[16];
+ uchar old_lm_hash_plain[16];
+ char no_pw[2];
+ BOOL ret;
+
+ BOOL nt_pass_set = (password_encrypted_with_nt_hash && old_nt_hash_encrypted);
+ BOOL lm_pass_set = (password_encrypted_with_lm_hash && old_lm_hash_encrypted);
+
+ *hnd = NULL;
+
+ pdb_init_sam(&sampass);
+
+ become_root();
+ ret = pdb_getsampwnam(sampass, user);
+ unbecome_root();
+
+ if (ret == False) {
+ DEBUG(0, ("check_oem_password: getsmbpwnam returned NULL\n"));
+ pdb_free_sam(&sampass);
+ return NT_STATUS_NO_SUCH_USER;
+ }
+
+ acct_ctrl = pdb_get_acct_ctrl(sampass);
+
+ if (acct_ctrl & ACB_DISABLED) {
+ DEBUG(2,("check_lanman_password: account %s disabled.\n", user));
+ pdb_free_sam(&sampass);
+ return NT_STATUS_ACCOUNT_DISABLED;
+ }
+
+ if (acct_ctrl & ACB_PWNOTREQ && lp_null_passwords()) {
+ /* construct a null password (in case one is needed */
+ no_pw[0] = 0;
+ no_pw[1] = 0;
+ nt_lm_owf_gen(no_pw, null_ntpw, null_pw);
+ lanman_pw = null_pw;
+ nt_pw = null_pw;
+
+ } else {
+ /* save pointers to passwords so we don't have to keep looking them up */
+ if (lp_lanman_auth()) {
+ lanman_pw = pdb_get_lanman_passwd(sampass);
+ } else {
+ lanman_pw = NULL;
+ }
+ nt_pw = pdb_get_nt_passwd(sampass);
+ }
+
+ if (nt_pw && nt_pass_set) {
+ /* IDEAL Case: passwords are in unicode, and we can
+ * read use the password encrypted with the NT hash
+ */
+ password_encrypted = password_encrypted_with_nt_hash;
+ encryption_key = nt_pw;
+ } else if (lanman_pw && lm_pass_set) {
+ /* password may still be in unicode, but use LM hash version */
+ password_encrypted = password_encrypted_with_lm_hash;
+ encryption_key = lanman_pw;
+ } else if (nt_pass_set) {
+ DEBUG(1, ("NT password change supplied for user %s, but we have no NT password to check it with\n",
+ user));
+ pdb_free_sam(&sampass);
+ return NT_STATUS_WRONG_PASSWORD;
+ } else if (lm_pass_set) {
+ DEBUG(1, ("LM password change supplied for user %s, but we have no LanMan password to check it with\n",
+ user));
+ pdb_free_sam(&sampass);
+ return NT_STATUS_WRONG_PASSWORD;
+ } else {
+ DEBUG(1, ("password change requested for user %s, but no password supplied!\n",
+ user));
+ pdb_free_sam(&sampass);
+ return NT_STATUS_WRONG_PASSWORD;
+ }
+
+ /*
+ * Decrypt the password with the key
+ */
+ SamOEMhash( password_encrypted, encryption_key, 516);
+
+ if ( !decode_pw_buffer(password_encrypted, new_passwd, new_passwd_size, &new_pw_len,
+ nt_pass_set ? STR_UNICODE : STR_ASCII)) {
+ pdb_free_sam(&sampass);
+ return NT_STATUS_WRONG_PASSWORD;
+ }
+
+ /*
+ * To ensure we got the correct new password, hash it and
+ * use it as a key to test the passed old password.
+ */
+
+ if (nt_pass_set) {
+ /* NT passwords, verify the NT hash. */
+
+ /* Calculate the MD4 hash (NT compatible) of the password */
+ memset(new_nt_hash, '\0', 16);
+ E_md4hash(new_passwd, new_nt_hash);
+
+ if (nt_pw) {
+ /*
+ * Now use new_nt_hash as the key to see if the old
+ * password matches.
+ */
+ D_P16(new_nt_hash, old_nt_hash_encrypted, old_nt_hash_plain);
+
+ if (memcmp(nt_pw, old_nt_hash_plain, 16)) {
+ DEBUG(0,("check_oem_password: old lm password doesn't match.\n"));
+ pdb_free_sam(&sampass);
+ return NT_STATUS_WRONG_PASSWORD;
+ }
+
+ /* We could check the LM password here, but there is
+ * little point, we already know the password is
+ * correct, and the LM password might not even be
+ * present. */
+
+ /* Further, LM hash generation algorithms
+ * differ with charset, so we could
+ * incorrectly fail a perfectly valid password
+ * change */
+#ifdef DEBUG_PASSWORD
+ DEBUG(100,
+ ("check_oem_password: password %s ok\n", new_passwd));
+#endif
+ *hnd = sampass;
+ return NT_STATUS_OK;
+ }
+
+ if (lanman_pw) {
+ /*
+ * Now use new_nt_hash as the key to see if the old
+ * LM password matches.
+ */
+ D_P16(new_nt_hash, old_lm_hash_encrypted, old_lm_hash_plain);
+
+ if (memcmp(lanman_pw, old_lm_hash_plain, 16)) {
+ DEBUG(0,("check_oem_password: old lm password doesn't match.\n"));
+ pdb_free_sam(&sampass);
+ return NT_STATUS_WRONG_PASSWORD;
+ }
+#ifdef DEBUG_PASSWORD
+ DEBUG(100,
+ ("check_oem_password: password %s ok\n", new_passwd));
+#endif
+ *hnd = sampass;
+ return NT_STATUS_OK;
+ }
+ }
+
+ if (lanman_pw && lm_pass_set) {
+
+ E_deshash(new_passwd, new_lm_hash);
+
+ /*
+ * Now use new_lm_hash as the key to see if the old
+ * password matches.
+ */
+ D_P16(new_lm_hash, old_lm_hash_encrypted, old_lm_hash_plain);
+
+ if (memcmp(lanman_pw, old_lm_hash_plain, 16)) {
+ DEBUG(0,("check_oem_password: old lm password doesn't match.\n"));
+ pdb_free_sam(&sampass);
+ return NT_STATUS_WRONG_PASSWORD;
+ }
+
+#ifdef DEBUG_PASSWORD
+ DEBUG(100,
+ ("check_oem_password: password %s ok\n", new_passwd));
+#endif
+ *hnd = sampass;
+ return NT_STATUS_OK;
+ }
+
+ /* should not be reached */
+ pdb_free_sam(&sampass);
+ return NT_STATUS_WRONG_PASSWORD;
+}
+
+/***********************************************************
+ Code to change the oem password. Changes both the lanman
+ and NT hashes. Old_passwd is almost always NULL.
+ NOTE this function is designed to be called as root. Check the old password
+ is correct before calling. JRA.
+************************************************************/
+
+NTSTATUS change_oem_password(SAM_ACCOUNT *hnd, char *old_passwd, char *new_passwd, BOOL as_root)
+{
+ struct passwd *pass;
+
+ BOOL ret;
+ uint32 min_len;
+
+ if (time(NULL) < pdb_get_pass_can_change_time(hnd)) {
+ DEBUG(1, ("user %s cannot change password now, must wait until %s\n",
+ pdb_get_username(hnd), http_timestring(pdb_get_pass_can_change_time(hnd))));
+ return NT_STATUS_PASSWORD_RESTRICTION;
+ }
+
+ if (account_policy_get(AP_MIN_PASSWORD_LEN, &min_len) && (strlen(new_passwd) < min_len)) {
+ DEBUG(1, ("user %s cannot change password - password too short\n",
+ pdb_get_username(hnd)));
+ DEBUGADD(1, (" account policy min password len = %d\n", min_len));
+ return NT_STATUS_PASSWORD_RESTRICTION;
+/* return NT_STATUS_PWD_TOO_SHORT; */
+ }
+
+ /* Take the passed information and test it for minimum criteria */
+ /* Minimum password length */
+ if (strlen(new_passwd) < lp_min_passwd_length()) {
+ /* too short, must be at least MINPASSWDLENGTH */
+ DEBUG(1, ("Password Change: user %s, New password is shorter than minimum password length = %d\n",
+ pdb_get_username(hnd), lp_min_passwd_length()));
+ return NT_STATUS_PASSWORD_RESTRICTION;
+/* return NT_STATUS_PWD_TOO_SHORT; */
+ }
+
+ pass = Get_Pwnam(pdb_get_username(hnd));
+ if (!pass) {
+ DEBUG(1, ("check_oem_password: Username does not exist in system !?!\n"));
+ }
+
+#ifdef HAVE_WORKING_CRACKLIB
+ if (pass) {
+ /* if we can, become the user to overcome internal cracklib sillyness */
+ if (!push_sec_ctx())
+ return NT_STATUS_UNSUCCESSFUL;
+
+ set_sec_ctx(pass->pw_uid, pass->pw_gid, 0, NULL, NULL, NULL);
+ set_re_uid();
+ }
+
+ if (lp_use_cracklib()) {
+ const char *crack_check_reason;
+ DEBUG(4, ("change_oem_password: Checking password for user [%s]"
+ " against cracklib. \n", pdb_get_username(hnd)));
+ DEBUGADD(4, ("If this is your last message, then something is "
+ "wrong with cracklib, it might be missing it's "
+ "dictionaries at %s\n",
+ CRACKLIB_DICTPATH));
+ dbgflush();
+
+ crack_check_reason = FascistCheck(new_passwd, (char *)CRACKLIB_DICTPATH);
+ if (crack_check_reason) {
+ DEBUG(1, ("Password Change: user [%s], "
+ "New password failed cracklib test - %s\n",
+ pdb_get_username(hnd), crack_check_reason));
+
+ /* get back to where we should be */
+ if (pass)
+ pop_sec_ctx();
+ return NT_STATUS_PASSWORD_RESTRICTION;
+ }
+ }
+
+ if (pass)
+ pop_sec_ctx();
+#endif
+
+ /*
+ * If unix password sync was requested, attempt to change
+ * the /etc/passwd database first. Return failure if this cannot
+ * be done.
+ *
+ * This occurs before the oem change, because we don't want to
+ * update it if chgpasswd failed.
+ *
+ * Conditional on lp_unix_password_sync() because we don't want
+ * to touch the unix db unless we have admin permission.
+ */
+
+ if(lp_unix_password_sync() &&
+ !chgpasswd(pdb_get_username(hnd), pass, old_passwd, new_passwd, as_root)) {
+ return NT_STATUS_ACCESS_DENIED;
+ }
+
+ if (!pdb_set_plaintext_passwd (hnd, new_passwd)) {
+ return NT_STATUS_ACCESS_DENIED;
+ }
+
+ /* Now write it into the file. */
+ ret = pdb_update_sam_account (hnd);
+
+ if (!ret) {
+ return NT_STATUS_ACCESS_DENIED;
+ }
+
+ return NT_STATUS_OK;
+}
diff --git a/source/smbd/close.c b/source/smbd/close.c
new file mode 100644
index 00000000000..0700aeaa0a6
--- /dev/null
+++ b/source/smbd/close.c
@@ -0,0 +1,311 @@
+/*
+ Unix SMB/CIFS implementation.
+ file closing
+ 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 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"
+
+/****************************************************************************
+ Run a file if it is a magic script.
+****************************************************************************/
+
+static void check_magic(files_struct *fsp,connection_struct *conn)
+{
+ if (!*lp_magicscript(SNUM(conn)))
+ return;
+
+ DEBUG(5,("checking magic for %s\n",fsp->fsp_name));
+
+ {
+ char *p;
+ if (!(p = strrchr_m(fsp->fsp_name,'/')))
+ p = fsp->fsp_name;
+ else
+ p++;
+
+ if (!strequal(lp_magicscript(SNUM(conn)),p))
+ return;
+ }
+
+ {
+ int ret;
+ pstring magic_output;
+ pstring fname;
+ SMB_STRUCT_STAT st;
+ int tmp_fd, outfd;
+
+ pstrcpy(fname,fsp->fsp_name);
+ if (*lp_magicoutput(SNUM(conn)))
+ pstrcpy(magic_output,lp_magicoutput(SNUM(conn)));
+ else
+ slprintf(magic_output,sizeof(fname)-1, "%s.out",fname);
+
+ chmod(fname,0755);
+ ret = smbrun(fname,&tmp_fd);
+ DEBUG(3,("Invoking magic command %s gave %d\n",fname,ret));
+ unlink(fname);
+ if (ret != 0 || tmp_fd == -1) {
+ if (tmp_fd != -1)
+ close(tmp_fd);
+ return;
+ }
+ outfd = open(magic_output, O_CREAT|O_EXCL|O_RDWR, 0600);
+ if (outfd == -1) {
+ close(tmp_fd);
+ return;
+ }
+
+ if (sys_fstat(tmp_fd,&st) == -1) {
+ close(tmp_fd);
+ close(outfd);
+ return;
+ }
+
+ transfer_file(tmp_fd,outfd,(SMB_OFF_T)st.st_size);
+ close(tmp_fd);
+ close(outfd);
+ }
+}
+
+/****************************************************************************
+ Common code to close a file or a directory.
+****************************************************************************/
+
+static int close_filestruct(files_struct *fsp)
+{
+ connection_struct *conn = fsp->conn;
+ int ret = 0;
+
+ if (fsp->fd != -1) {
+ if(flush_write_cache(fsp, CLOSE_FLUSH) == -1)
+ ret = -1;
+
+ delete_write_cache(fsp);
+ }
+
+ conn->num_files_open--;
+ SAFE_FREE(fsp->wbmpx_ptr);
+
+ return ret;
+}
+
+/****************************************************************************
+ Close a file.
+
+ If normal_close is 1 then this came from a normal SMBclose (or equivalent)
+ operation otherwise it came as the result of some other operation such as
+ the closing of the connection. In the latter case printing and
+ magic scripts are not run.
+****************************************************************************/
+
+static int close_normal_file(files_struct *fsp, BOOL normal_close)
+{
+ share_mode_entry *share_entry = NULL;
+ size_t share_entry_count = 0;
+ BOOL delete_on_close = False;
+ connection_struct *conn = fsp->conn;
+ int err = 0;
+ int err1 = 0;
+
+ remove_pending_lock_requests_by_fid(fsp);
+
+ /*
+ * If we're flushing on a close we can get a write
+ * error here, we must remember this.
+ */
+
+ if (close_filestruct(fsp) == -1)
+ err1 = -1;
+
+ if (fsp->print_file) {
+ print_fsp_end(fsp, normal_close);
+ file_free(fsp);
+ return 0;
+ }
+
+ /*
+ * Lock the share entries, and determine if we should delete
+ * on close. If so delete whilst the lock is still in effect.
+ * This prevents race conditions with the file being created. JRA.
+ */
+
+ lock_share_entry_fsp(fsp);
+
+ if (fsp->delete_on_close) {
+
+ /*
+ * Modify the share mode entry for all files open
+ * on this device and inode to tell other smbds we have
+ * changed the delete on close flag. The last closer will delete the file
+ * if flag is set.
+ */
+
+ NTSTATUS status =set_delete_on_close_over_all(fsp, fsp->delete_on_close);
+ if (NT_STATUS_V(status) != NT_STATUS_V(NT_STATUS_OK))
+ DEBUG(0,("close_normal_file: failed to change delete on close flag for file %s\n",
+ fsp->fsp_name ));
+ }
+
+ share_entry_count = del_share_mode(fsp, &share_entry);
+
+ DEBUG(10,("close_normal_file: share_entry_count = %lu for file %s\n",
+ (unsigned long)share_entry_count, fsp->fsp_name ));
+
+ /*
+ * We delete on close if it's the last open, and the
+ * delete on close flag was set in the entry we just deleted.
+ */
+
+ if ((share_entry_count == 0) && share_entry &&
+ GET_DELETE_ON_CLOSE_FLAG(share_entry->share_mode) )
+ delete_on_close = True;
+
+ SAFE_FREE(share_entry);
+
+ /*
+ * NT can set delete_on_close of the last open
+ * reference to a file.
+ */
+
+ if (normal_close && delete_on_close) {
+ DEBUG(5,("close_file: file %s. Delete on close was set - deleting file.\n",
+ fsp->fsp_name));
+ if(SMB_VFS_UNLINK(conn,fsp->fsp_name) != 0) {
+ /*
+ * This call can potentially fail as another smbd may have
+ * had the file open with delete on close set and deleted
+ * it when its last reference to this file went away. Hence
+ * we log this but not at debug level zero.
+ */
+
+ DEBUG(5,("close_file: file %s. Delete on close was set and unlink failed \
+with error %s\n", fsp->fsp_name, strerror(errno) ));
+ }
+ process_pending_change_notify_queue((time_t)0);
+ }
+
+ unlock_share_entry_fsp(fsp);
+
+ if(fsp->oplock_type)
+ release_file_oplock(fsp);
+
+ locking_close_file(fsp);
+
+ err = fd_close(conn, fsp);
+
+ /* check for magic scripts */
+ if (normal_close) {
+ check_magic(fsp,conn);
+ }
+
+ /*
+ * Ensure pending modtime is set after close.
+ */
+
+ if(fsp->pending_modtime) {
+ int saved_errno = errno;
+ set_filetime(conn, fsp->fsp_name, fsp->pending_modtime);
+ errno = saved_errno;
+ }
+
+ DEBUG(2,("%s closed file %s (numopen=%d) %s\n",
+ conn->user,fsp->fsp_name,
+ conn->num_files_open, err ? strerror(err) : ""));
+
+ if (fsp->fsp_name)
+ string_free(&fsp->fsp_name);
+
+ file_free(fsp);
+
+ if (err == -1 || err1 == -1)
+ return -1;
+ else
+ return 0;
+}
+
+/****************************************************************************
+ Close a directory opened by an NT SMB call.
+****************************************************************************/
+
+static int close_directory(files_struct *fsp, BOOL normal_close)
+{
+ remove_pending_change_notify_requests_by_fid(fsp);
+
+ /*
+ * NT can set delete_on_close of the last open
+ * reference to a directory also.
+ */
+
+ if (normal_close && fsp->directory_delete_on_close) {
+ BOOL ok = rmdir_internals(fsp->conn, fsp->fsp_name);
+ DEBUG(5,("close_directory: %s. Delete on close was set - deleting directory %s.\n",
+ fsp->fsp_name, ok ? "succeeded" : "failed" ));
+
+ /*
+ * Ensure we remove any change notify requests that would
+ * now fail as the directory has been deleted.
+ */
+
+ if(ok)
+ remove_pending_change_notify_requests_by_filename(fsp);
+ process_pending_change_notify_queue((time_t)0);
+ }
+
+ /*
+ * Do the code common to files and directories.
+ */
+ close_filestruct(fsp);
+
+ if (fsp->fsp_name)
+ string_free(&fsp->fsp_name);
+
+ file_free(fsp);
+ return 0;
+}
+
+/****************************************************************************
+ Close a 'stat file' opened internally.
+****************************************************************************/
+
+static int close_stat(files_struct *fsp)
+{
+ /*
+ * Do the code common to files and directories.
+ */
+ close_filestruct(fsp);
+
+ if (fsp->fsp_name)
+ string_free(&fsp->fsp_name);
+
+ file_free(fsp);
+ return 0;
+}
+
+/****************************************************************************
+ Close a files_struct.
+****************************************************************************/
+
+int close_file(files_struct *fsp, BOOL normal_close)
+{
+ if(fsp->is_directory)
+ return close_directory(fsp, normal_close);
+ else if (fsp->is_stat)
+ return close_stat(fsp);
+ else
+ return close_normal_file(fsp, normal_close);
+}
diff --git a/source/smbd/conn.c b/source/smbd/conn.c
new file mode 100644
index 00000000000..0805f8e6902
--- /dev/null
+++ b/source/smbd/conn.c
@@ -0,0 +1,305 @@
+/*
+ Unix SMB/CIFS implementation.
+ Manage connections_struct structures
+ Copyright (C) Andrew Tridgell 1998
+ Copyright (C) Alexander Bokovoy 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 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"
+
+/* The connections bitmap is expanded in increments of BITMAP_BLOCK_SZ. The
+ * maximum size of the bitmap is the largest positive integer, but you will hit
+ * the "max connections" limit, looong before that.
+ */
+#define BITMAP_BLOCK_SZ 128
+
+static connection_struct *Connections;
+
+/* number of open connections */
+static struct bitmap *bmap;
+static int num_open;
+
+/****************************************************************************
+init the conn structures
+****************************************************************************/
+void conn_init(void)
+{
+ bmap = bitmap_allocate(BITMAP_BLOCK_SZ);
+}
+
+/****************************************************************************
+return the number of open connections
+****************************************************************************/
+int conn_num_open(void)
+{
+ return num_open;
+}
+
+
+/****************************************************************************
+check if a snum is in use
+****************************************************************************/
+BOOL conn_snum_used(int snum)
+{
+ connection_struct *conn;
+ for (conn=Connections;conn;conn=conn->next) {
+ if (conn->service == snum) {
+ return(True);
+ }
+ }
+ return(False);
+}
+
+
+/****************************************************************************
+find a conn given a cnum
+****************************************************************************/
+connection_struct *conn_find(unsigned cnum)
+{
+ int count=0;
+ connection_struct *conn;
+
+ for (conn=Connections;conn;conn=conn->next,count++) {
+ if (conn->cnum == cnum) {
+ if (count > 10) {
+ DLIST_PROMOTE(Connections, conn);
+ }
+ return conn;
+ }
+ }
+
+ return NULL;
+}
+
+
+/****************************************************************************
+ find first available connection slot, starting from a random position.
+The randomisation stops problems with the server dieing and clients
+thinking the server is still available.
+****************************************************************************/
+connection_struct *conn_new(void)
+{
+ TALLOC_CTX *mem_ctx;
+ connection_struct *conn;
+ int i;
+ int find_offset = 1;
+
+find_again:
+ i = bitmap_find(bmap, find_offset);
+
+ if (i == -1) {
+ /* Expand the connections bitmap. */
+ int oldsz = bmap->n;
+ int newsz = bmap->n + BITMAP_BLOCK_SZ;
+ struct bitmap * nbmap;
+
+ if (newsz <= 0) {
+ /* Integer wrap. */
+ DEBUG(0,("ERROR! Out of connection structures\n"));
+ return NULL;
+ }
+
+ DEBUG(4,("resizing connections bitmap from %d to %d\n",
+ oldsz, newsz));
+
+ nbmap = bitmap_allocate(newsz);
+
+ bitmap_copy(nbmap, bmap);
+ bitmap_free(bmap);
+
+ bmap = nbmap;
+ find_offset = oldsz; /* Start next search in the new portion. */
+
+ goto find_again;
+ }
+
+ if ((mem_ctx=talloc_init("connection_struct"))==NULL) {
+ DEBUG(0,("talloc_init(connection_struct) failed!\n"));
+ return NULL;
+ }
+
+ if ((conn=(connection_struct *)talloc_zero(mem_ctx, sizeof(*conn)))==NULL) {
+ DEBUG(0,("talloc_zero() failed!\n"));
+ return NULL;
+ }
+ conn->mem_ctx = mem_ctx;
+ conn->cnum = i;
+
+ bitmap_set(bmap, i);
+
+ num_open++;
+
+ string_set(&conn->user,"");
+ string_set(&conn->dirpath,"");
+ string_set(&conn->connectpath,"");
+ string_set(&conn->origpath,"");
+
+ DLIST_ADD(Connections, conn);
+
+ return conn;
+}
+
+/****************************************************************************
+close all conn structures
+****************************************************************************/
+void conn_close_all(void)
+{
+ connection_struct *conn, *next;
+ for (conn=Connections;conn;conn=next) {
+ next=conn->next;
+ close_cnum(conn, conn->vuid);
+ }
+}
+
+/****************************************************************************
+ Idle inactive connections.
+****************************************************************************/
+
+BOOL conn_idle_all(time_t t, int deadtime)
+{
+ pipes_struct *plist = NULL;
+ BOOL allidle = True;
+ connection_struct *conn, *next;
+
+ for (conn=Connections;conn;conn=next) {
+ next=conn->next;
+ /* close dirptrs on connections that are idle */
+ if ((t-conn->lastused) > DPTR_IDLE_TIMEOUT)
+ dptr_idlecnum(conn);
+
+ if (conn->num_files_open > 0 ||
+ (t-conn->lastused)<deadtime)
+ allidle = False;
+ }
+
+ /*
+ * Check all pipes for any open handles. We cannot
+ * idle with a handle open.
+ */
+
+ for (plist = get_first_internal_pipe(); plist; plist = get_next_internal_pipe(plist))
+ if (plist->pipe_handles && plist->pipe_handles->count)
+ allidle = False;
+
+ return allidle;
+}
+
+/****************************************************************************
+ Clear a vuid out of the validity cache, and as the 'owner' of a connection.
+****************************************************************************/
+
+void conn_clear_vuid_cache(uint16 vuid)
+{
+ connection_struct *conn;
+ unsigned int i;
+
+ for (conn=Connections;conn;conn=conn->next) {
+ if (conn->vuid == vuid) {
+ conn->vuid = UID_FIELD_INVALID;
+ }
+
+ for (i=0;i<conn->vuid_cache.entries && i< VUID_CACHE_SIZE;i++) {
+ if (conn->vuid_cache.array[i].vuid == vuid) {
+ struct vuid_cache_entry *ent = &conn->vuid_cache.array[i];
+ ent->vuid = UID_FIELD_INVALID;
+ ent->read_only = False;
+ ent->admin_user = False;
+ }
+ }
+ }
+}
+
+/****************************************************************************
+ Free a conn structure.
+****************************************************************************/
+
+void conn_free(connection_struct *conn)
+{
+ vfs_handle_struct *handle = NULL, *thandle = NULL;
+ TALLOC_CTX *mem_ctx = NULL;
+
+ /* Free vfs_connection_struct */
+ handle = conn->vfs_handles;
+ while(handle) {
+ DLIST_REMOVE(conn->vfs_handles, handle);
+ thandle = handle->next;
+ if (handle->free_data)
+ handle->free_data(&handle->data);
+ handle = thandle;
+ }
+
+ DLIST_REMOVE(Connections, conn);
+
+ if (conn->ngroups && conn->groups) {
+ SAFE_FREE(conn->groups);
+ conn->ngroups = 0;
+ }
+
+ if (conn->nt_user_token) {
+ delete_nt_token(&(conn->nt_user_token));
+ }
+
+ if (conn->privs) {
+ destroy_privilege(&(conn->privs));
+ }
+
+ free_namearray(conn->veto_list);
+ free_namearray(conn->hide_list);
+ free_namearray(conn->veto_oplock_list);
+
+ string_free(&conn->user);
+ string_free(&conn->dirpath);
+ string_free(&conn->connectpath);
+ string_free(&conn->origpath);
+
+ bitmap_clear(bmap, conn->cnum);
+ num_open--;
+
+ mem_ctx = conn->mem_ctx;
+ ZERO_STRUCTP(conn);
+ talloc_destroy(mem_ctx);
+}
+
+
+/****************************************************************************
+receive a smbcontrol message to forcibly unmount a share
+the message contains just a share name and all instances of that
+share are unmounted
+the special sharename '*' forces unmount of all shares
+****************************************************************************/
+void msg_force_tdis(int msg_type, pid_t pid, void *buf, size_t len)
+{
+ connection_struct *conn, *next;
+ fstring sharename;
+
+ fstrcpy(sharename, buf);
+
+ if (strcmp(sharename, "*") == 0) {
+ DEBUG(1,("Forcing close of all shares\n"));
+ conn_close_all();
+ return;
+ }
+
+ for (conn=Connections;conn;conn=next) {
+ next=conn->next;
+ if (strequal(lp_servicename(conn->service), sharename)) {
+ DEBUG(1,("Forcing close of share %s cnum=%d\n",
+ sharename, conn->cnum));
+ close_cnum(conn, (uint16)-1);
+ }
+ }
+}
diff --git a/source/smbd/connection.c b/source/smbd/connection.c
new file mode 100644
index 00000000000..a9ab1424615
--- /dev/null
+++ b/source/smbd/connection.c
@@ -0,0 +1,243 @@
+/*
+ Unix SMB/CIFS implementation.
+ connection claim routines
+ 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 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"
+
+static TDB_CONTEXT *tdb;
+
+/****************************************************************************
+ Return the connection tdb context (used for message send all).
+****************************************************************************/
+
+TDB_CONTEXT *conn_tdb_ctx(void)
+{
+ if (!tdb)
+ tdb = tdb_open_ex(lock_path("connections.tdb"), 0, TDB_CLEAR_IF_FIRST|TDB_DEFAULT,
+ O_RDWR | O_CREAT, 0644, smbd_tdb_log);
+
+ return tdb;
+}
+
+static void make_conn_key(connection_struct *conn, const char *name, TDB_DATA *pkbuf, struct connections_key *pkey)
+{
+ ZERO_STRUCTP(pkey);
+ pkey->pid = sys_getpid();
+ pkey->cnum = conn?conn->cnum:-1;
+ fstrcpy(pkey->name, name);
+#ifdef DEVELOPER
+ /* valgrind fixer... */
+ {
+ size_t sl = strlen(pkey->name);
+ if (sizeof(fstring)-sl)
+ memset(&pkey->name[sl], '\0', sizeof(fstring)-sl);
+ }
+#endif
+
+ pkbuf->dptr = (char *)pkey;
+ pkbuf->dsize = sizeof(*pkey);
+}
+
+/****************************************************************************
+ Delete a connection record.
+****************************************************************************/
+
+BOOL yield_connection(connection_struct *conn, const char *name)
+{
+ struct connections_key key;
+ TDB_DATA kbuf;
+
+ if (!tdb)
+ return False;
+
+ DEBUG(3,("Yielding connection to %s\n",name));
+
+ make_conn_key(conn, name, &kbuf, &key);
+
+ if (tdb_delete(tdb, kbuf) != 0) {
+ int dbg_lvl = (!conn && (tdb_error(tdb) == TDB_ERR_NOEXIST)) ? 3 : 0;
+ DEBUG(dbg_lvl,("yield_connection: tdb_delete for name %s failed with error %s.\n",
+ name, tdb_errorstr(tdb) ));
+ return (False);
+ }
+
+ return(True);
+}
+
+struct count_stat {
+ pid_t mypid;
+ int curr_connections;
+ char *name;
+ BOOL Clear;
+};
+
+/****************************************************************************
+ Count the entries belonging to a service in the connection db.
+****************************************************************************/
+
+static int count_fn( TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf, void *udp)
+{
+ struct connections_data crec;
+ struct count_stat *cs = (struct count_stat *)udp;
+
+ if (dbuf.dsize != sizeof(crec))
+ return 0;
+
+ memcpy(&crec, dbuf.dptr, sizeof(crec));
+
+ if (crec.cnum == -1)
+ return 0;
+
+ /* If the pid was not found delete the entry from connections.tdb */
+
+ if (cs->Clear && !process_exists(crec.pid) && (errno == ESRCH)) {
+ DEBUG(2,("pid %u doesn't exist - deleting connections %d [%s]\n",
+ (unsigned int)crec.pid, crec.cnum, crec.name));
+ if (tdb_delete(the_tdb, kbuf) != 0)
+ DEBUG(0,("count_fn: tdb_delete failed with error %s\n", tdb_errorstr(tdb) ));
+ return 0;
+ }
+
+ if (strequal(crec.name, cs->name))
+ cs->curr_connections++;
+
+ return 0;
+}
+
+/****************************************************************************
+ Claim an entry in the connections database.
+****************************************************************************/
+
+BOOL claim_connection(connection_struct *conn, const char *name,int max_connections,BOOL Clear, uint32 msg_flags)
+{
+ struct connections_key key;
+ struct connections_data crec;
+ TDB_DATA kbuf, dbuf;
+
+ if (!tdb)
+ tdb = tdb_open_ex(lock_path("connections.tdb"), 0, TDB_CLEAR_IF_FIRST|TDB_DEFAULT,
+ O_RDWR | O_CREAT, 0644, smbd_tdb_log);
+
+ if (!tdb)
+ return False;
+
+ /*
+ * Enforce the max connections parameter.
+ */
+
+ if (max_connections > 0) {
+ struct count_stat cs;
+
+ cs.mypid = sys_getpid();
+ cs.curr_connections = 0;
+ cs.name = lp_servicename(SNUM(conn));
+ cs.Clear = Clear;
+
+ /*
+ * This has a race condition, but locking the chain before hand is worse
+ * as it leads to deadlock.
+ */
+
+ if (tdb_traverse(tdb, count_fn, &cs) == -1) {
+ DEBUG(0,("claim_connection: traverse of connections.tdb failed with error %s.\n",
+ tdb_errorstr(tdb) ));
+ return False;
+ }
+
+ if (cs.curr_connections >= max_connections) {
+ DEBUG(1,("claim_connection: Max connections (%d) exceeded for %s\n",
+ max_connections, name ));
+ return False;
+ }
+ }
+
+ DEBUG(5,("claiming %s %d\n",name,max_connections));
+
+ make_conn_key(conn, name, &kbuf, &key);
+
+ /* fill in the crec */
+ ZERO_STRUCT(crec);
+ crec.magic = 0x280267;
+ crec.pid = sys_getpid();
+ crec.cnum = conn?conn->cnum:-1;
+ if (conn) {
+ crec.uid = conn->uid;
+ crec.gid = conn->gid;
+ safe_strcpy(crec.name,
+ lp_servicename(SNUM(conn)),sizeof(crec.name)-1);
+ }
+ crec.start = time(NULL);
+ crec.bcast_msg_flags = msg_flags;
+
+ safe_strcpy(crec.machine,get_remote_machine_name(),sizeof(crec.machine)-1);
+ safe_strcpy(crec.addr,conn?conn->client_address:client_addr(),sizeof(crec.addr)-1);
+
+ dbuf.dptr = (char *)&crec;
+ dbuf.dsize = sizeof(crec);
+
+ if (tdb_store(tdb, kbuf, dbuf, TDB_REPLACE) != 0) {
+ DEBUG(0,("claim_connection: tdb_store failed with error %s.\n",
+ tdb_errorstr(tdb) ));
+ return False;
+ }
+
+ return True;
+}
+
+BOOL register_message_flags(BOOL doreg, uint32 msg_flags)
+{
+ struct connections_key key;
+ struct connections_data *pcrec;
+ TDB_DATA kbuf, dbuf;
+
+ if (!tdb)
+ return False;
+
+ DEBUG(10,("register_message_flags: %s flags 0x%x\n",
+ doreg ? "adding" : "removing",
+ (unsigned int)msg_flags ));
+
+ make_conn_key(NULL, "", &kbuf, &key);
+
+ dbuf = tdb_fetch(tdb, kbuf);
+ if (!dbuf.dptr) {
+ DEBUG(0,("register_message_flags: tdb_fetch failed\n"));
+ return False;
+ }
+
+ pcrec = (struct connections_data *)dbuf.dptr;
+ pcrec->bcast_msg_flags = msg_flags;
+ if (doreg)
+ pcrec->bcast_msg_flags |= msg_flags;
+ else
+ pcrec->bcast_msg_flags &= ~msg_flags;
+
+ if (tdb_store(tdb, kbuf, dbuf, TDB_REPLACE) != 0) {
+ DEBUG(0,("register_message_flags: tdb_store failed with error %s.\n",
+ tdb_errorstr(tdb) ));
+ SAFE_FREE(dbuf.dptr);
+ return False;
+ }
+
+ DEBUG(10,("register_message_flags: new flags 0x%x\n",
+ (unsigned int)pcrec->bcast_msg_flags ));
+
+ SAFE_FREE(dbuf.dptr);
+ return True;
+}
diff --git a/source/smbd/dfree.c b/source/smbd/dfree.c
new file mode 100644
index 00000000000..f93cdf3791e
--- /dev/null
+++ b/source/smbd/dfree.c
@@ -0,0 +1,164 @@
+/*
+ Unix SMB/CIFS implementation.
+ functions to calculate the free disk space
+ 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 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"
+
+/****************************************************************************
+normalise for DOS usage
+****************************************************************************/
+static void disk_norm(BOOL small_query, SMB_BIG_UINT *bsize,SMB_BIG_UINT *dfree,SMB_BIG_UINT *dsize)
+{
+ /* check if the disk is beyond the max disk size */
+ SMB_BIG_UINT maxdisksize = lp_maxdisksize();
+ if (maxdisksize) {
+ /* convert to blocks - and don't overflow */
+ maxdisksize = ((maxdisksize*1024)/(*bsize))*1024;
+ if (*dsize > maxdisksize) *dsize = maxdisksize;
+ if (*dfree > maxdisksize) *dfree = maxdisksize-1;
+ /* the -1 should stop applications getting div by 0
+ errors */
+ }
+
+ while (*dfree > WORDMAX || *dsize > WORDMAX || *bsize < 512) {
+ *dfree /= 2;
+ *dsize /= 2;
+ *bsize *= 2;
+ if(small_query) {
+ /*
+ * Force max to fit in 16 bit fields.
+ */
+ if (*bsize > (WORDMAX*512)) {
+ *bsize = (WORDMAX*512);
+ if (*dsize > WORDMAX)
+ *dsize = WORDMAX;
+ if (*dfree > WORDMAX)
+ *dfree = WORDMAX;
+ break;
+ }
+ }
+ }
+}
+
+
+
+/****************************************************************************
+ return number of 1K blocks available on a path and total number
+****************************************************************************/
+
+static SMB_BIG_UINT disk_free(const char *path, BOOL small_query,
+ SMB_BIG_UINT *bsize,SMB_BIG_UINT *dfree,SMB_BIG_UINT *dsize)
+{
+ int dfree_retval;
+ SMB_BIG_UINT dfree_q = 0;
+ SMB_BIG_UINT bsize_q = 0;
+ SMB_BIG_UINT dsize_q = 0;
+ char *dfree_command;
+
+ (*dfree) = (*dsize) = 0;
+ (*bsize) = 512;
+
+ /*
+ * If external disk calculation specified, use it.
+ */
+
+ dfree_command = lp_dfree_command();
+ if (dfree_command && *dfree_command) {
+ const char *p;
+ char **lines;
+ pstring syscmd;
+
+ slprintf(syscmd, sizeof(syscmd)-1, "%s %s", dfree_command, path);
+ DEBUG (3, ("disk_free: Running command %s\n", syscmd));
+
+ lines = file_lines_pload(syscmd, NULL);
+ if (lines) {
+ char *line = lines[0];
+
+ DEBUG (3, ("Read input from dfree, \"%s\"\n", line));
+
+ *dsize = STR_TO_SMB_BIG_UINT(line, &p);
+ while (p && *p && isspace(*p))
+ p++;
+ if (p && *p)
+ *dfree = STR_TO_SMB_BIG_UINT(p, &p);
+ while (p && *p && isspace(*p))
+ p++;
+ if (p && *p)
+ *bsize = STR_TO_SMB_BIG_UINT(p, NULL);
+ else
+ *bsize = 1024;
+ file_lines_free(lines);
+ DEBUG (3, ("Parsed output of dfree, dsize=%u, dfree=%u, bsize=%u\n",
+ (unsigned int)*dsize, (unsigned int)*dfree, (unsigned int)*bsize));
+
+ if (!*dsize)
+ *dsize = 2048;
+ if (!*dfree)
+ *dfree = 1024;
+ } else {
+ DEBUG (0, ("disk_free: sys_popen() failed for command %s. Error was : %s\n",
+ syscmd, strerror(errno) ));
+ sys_fsusage(path, dfree, dsize);
+ }
+ } else
+ sys_fsusage(path, dfree, dsize);
+
+ if (disk_quotas(path, &bsize_q, &dfree_q, &dsize_q)) {
+ (*bsize) = bsize_q;
+ (*dfree) = MIN(*dfree,dfree_q);
+ (*dsize) = MIN(*dsize,dsize_q);
+ }
+
+ /* FIXME : Any reason for this assumption ? */
+ if (*bsize < 256) {
+ DEBUG(5,("disk_free:Warning: bsize == %d < 256 . Changing to assumed correct bsize = 512\n",(int)*bsize));
+ *bsize = 512;
+ }
+
+ if ((*dsize)<1) {
+ static int done;
+ if (!done) {
+ DEBUG(0,("WARNING: dfree is broken on this system\n"));
+ done=1;
+ }
+ *dsize = 20*1024*1024/(*bsize);
+ *dfree = MAX(1,*dfree);
+ }
+
+ disk_norm(small_query,bsize,dfree,dsize);
+
+ if ((*bsize) < 1024) {
+ dfree_retval = (*dfree)/(1024/(*bsize));
+ } else {
+ dfree_retval = ((*bsize)/1024)*(*dfree);
+ }
+
+ return(dfree_retval);
+}
+
+
+/****************************************************************************
+wrap it to get filenames right
+****************************************************************************/
+SMB_BIG_UINT sys_disk_free(const char *path, BOOL small_query,
+ SMB_BIG_UINT *bsize,SMB_BIG_UINT *dfree,SMB_BIG_UINT *dsize)
+{
+ return disk_free(path,small_query, bsize,dfree,dsize);
+}
diff --git a/source/smbd/dir.c b/source/smbd/dir.c
new file mode 100644
index 00000000000..06ef23ab8cd
--- /dev/null
+++ b/source/smbd/dir.c
@@ -0,0 +1,1096 @@
+/*
+ Unix SMB/CIFS implementation.
+ Directory handling routines
+ 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 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"
+
+/*
+ This module implements directory related functions for Samba.
+*/
+
+typedef struct _dptr_struct {
+ struct _dptr_struct *next, *prev;
+ int dnum;
+ uint16 spid;
+ connection_struct *conn;
+ void *ptr;
+ BOOL expect_close;
+ char *wcard; /* Field only used for trans2_ searches */
+ uint16 attr; /* Field only used for trans2_ searches */
+ char *path;
+} dptr_struct;
+
+static struct bitmap *dptr_bmap;
+static dptr_struct *dirptrs;
+
+static int dptrs_open = 0;
+
+#define INVALID_DPTR_KEY (-3)
+
+/****************************************************************************
+ Initialise the dir bitmap.
+****************************************************************************/
+
+void init_dptrs(void)
+{
+ static BOOL dptrs_init=False;
+
+ if (dptrs_init)
+ return;
+
+ dptr_bmap = bitmap_allocate(MAX_DIRECTORY_HANDLES);
+
+ if (!dptr_bmap)
+ exit_server("out of memory in init_dptrs");
+
+ dptrs_init = True;
+}
+
+/****************************************************************************
+ Idle a dptr - the directory is closed but the control info is kept.
+****************************************************************************/
+
+static void dptr_idle(dptr_struct *dptr)
+{
+ if (dptr->ptr) {
+ DEBUG(4,("Idling dptr dnum %d\n",dptr->dnum));
+ dptrs_open--;
+ CloseDir(dptr->ptr);
+ dptr->ptr = NULL;
+ }
+}
+
+/****************************************************************************
+ Idle the oldest dptr.
+****************************************************************************/
+
+static void dptr_idleoldest(void)
+{
+ dptr_struct *dptr;
+
+ /*
+ * Go to the end of the list.
+ */
+ for(dptr = dirptrs; dptr && dptr->next; dptr = dptr->next)
+ ;
+
+ if(!dptr) {
+ DEBUG(0,("No dptrs available to idle ?\n"));
+ return;
+ }
+
+ /*
+ * Idle the oldest pointer.
+ */
+
+ for(; dptr; dptr = dptr->prev) {
+ if (dptr->ptr) {
+ dptr_idle(dptr);
+ return;
+ }
+ }
+}
+
+/****************************************************************************
+ Get the dptr_struct for a dir index.
+****************************************************************************/
+
+static dptr_struct *dptr_get(int key, BOOL forclose)
+{
+ dptr_struct *dptr;
+
+ for(dptr = dirptrs; dptr; dptr = dptr->next) {
+ if(dptr->dnum == key) {
+ if (!forclose && !dptr->ptr) {
+ if (dptrs_open >= MAX_OPEN_DIRECTORIES)
+ dptr_idleoldest();
+ DEBUG(4,("Reopening dptr key %d\n",key));
+ if ((dptr->ptr = OpenDir(dptr->conn, dptr->path, True)))
+ dptrs_open++;
+ }
+ DLIST_PROMOTE(dirptrs,dptr);
+ return dptr;
+ }
+ }
+ return(NULL);
+}
+
+/****************************************************************************
+ Get the dptr ptr for a dir index.
+****************************************************************************/
+
+static void *dptr_ptr(int key)
+{
+ dptr_struct *dptr = dptr_get(key, False);
+
+ if (dptr)
+ return(dptr->ptr);
+ return(NULL);
+}
+
+/****************************************************************************
+ Get the dir path for a dir index.
+****************************************************************************/
+
+char *dptr_path(int key)
+{
+ dptr_struct *dptr = dptr_get(key, False);
+
+ if (dptr)
+ return(dptr->path);
+ return(NULL);
+}
+
+/****************************************************************************
+ Get the dir wcard for a dir index (lanman2 specific).
+****************************************************************************/
+
+char *dptr_wcard(int key)
+{
+ dptr_struct *dptr = dptr_get(key, False);
+
+ if (dptr)
+ return(dptr->wcard);
+ return(NULL);
+}
+
+/****************************************************************************
+ Set the dir wcard for a dir index (lanman2 specific).
+ Returns 0 on ok, 1 on fail.
+****************************************************************************/
+
+BOOL dptr_set_wcard(int key, char *wcard)
+{
+ dptr_struct *dptr = dptr_get(key, False);
+
+ if (dptr) {
+ dptr->wcard = wcard;
+ return True;
+ }
+ return False;
+}
+
+/****************************************************************************
+ Set the dir attrib for a dir index (lanman2 specific).
+ Returns 0 on ok, 1 on fail.
+****************************************************************************/
+
+BOOL dptr_set_attr(int key, uint16 attr)
+{
+ dptr_struct *dptr = dptr_get(key, False);
+
+ if (dptr) {
+ dptr->attr = attr;
+ return True;
+ }
+ return False;
+}
+
+/****************************************************************************
+ Get the dir attrib for a dir index (lanman2 specific)
+****************************************************************************/
+
+uint16 dptr_attr(int key)
+{
+ dptr_struct *dptr = dptr_get(key, False);
+
+ if (dptr)
+ return(dptr->attr);
+ return(0);
+}
+
+/****************************************************************************
+ Close a dptr (internal func).
+****************************************************************************/
+
+static void dptr_close_internal(dptr_struct *dptr)
+{
+ DEBUG(4,("closing dptr key %d\n",dptr->dnum));
+
+ DLIST_REMOVE(dirptrs, dptr);
+
+ /*
+ * Free the dnum in the bitmap. Remember the dnum value is always
+ * biased by one with respect to the bitmap.
+ */
+
+ if(bitmap_query( dptr_bmap, dptr->dnum - 1) != True) {
+ DEBUG(0,("dptr_close_internal : Error - closing dnum = %d and bitmap not set !\n",
+ dptr->dnum ));
+ }
+
+ bitmap_clear(dptr_bmap, dptr->dnum - 1);
+
+ if (dptr->ptr) {
+ CloseDir(dptr->ptr);
+ dptrs_open--;
+ }
+
+ /* Lanman 2 specific code */
+ SAFE_FREE(dptr->wcard);
+ string_set(&dptr->path,"");
+ SAFE_FREE(dptr);
+}
+
+/****************************************************************************
+ Close a dptr given a key.
+****************************************************************************/
+
+void dptr_close(int *key)
+{
+ dptr_struct *dptr;
+
+ if(*key == INVALID_DPTR_KEY)
+ return;
+
+ /* OS/2 seems to use -1 to indicate "close all directories" */
+ if (*key == -1) {
+ dptr_struct *next;
+ for(dptr = dirptrs; dptr; dptr = next) {
+ next = dptr->next;
+ dptr_close_internal(dptr);
+ }
+ *key = INVALID_DPTR_KEY;
+ return;
+ }
+
+ dptr = dptr_get(*key, True);
+
+ if (!dptr) {
+ DEBUG(0,("Invalid key %d given to dptr_close\n", *key));
+ return;
+ }
+
+ dptr_close_internal(dptr);
+
+ *key = INVALID_DPTR_KEY;
+}
+
+/****************************************************************************
+ Close all dptrs for a cnum.
+****************************************************************************/
+
+void dptr_closecnum(connection_struct *conn)
+{
+ dptr_struct *dptr, *next;
+ for(dptr = dirptrs; dptr; dptr = next) {
+ next = dptr->next;
+ if (dptr->conn == conn)
+ dptr_close_internal(dptr);
+ }
+}
+
+/****************************************************************************
+ Idle all dptrs for a cnum.
+****************************************************************************/
+
+void dptr_idlecnum(connection_struct *conn)
+{
+ dptr_struct *dptr;
+ for(dptr = dirptrs; dptr; dptr = dptr->next) {
+ if (dptr->conn == conn && dptr->ptr)
+ dptr_idle(dptr);
+ }
+}
+
+/****************************************************************************
+ Close a dptr that matches a given path, only if it matches the spid also.
+****************************************************************************/
+
+void dptr_closepath(char *path,uint16 spid)
+{
+ dptr_struct *dptr, *next;
+ for(dptr = dirptrs; dptr; dptr = next) {
+ next = dptr->next;
+ if (spid == dptr->spid && strequal(dptr->path,path))
+ dptr_close_internal(dptr);
+ }
+}
+
+/****************************************************************************
+ Start a directory listing.
+****************************************************************************/
+
+static BOOL start_dir(connection_struct *conn, pstring directory)
+{
+ const char *dir2;
+
+ DEBUG(5,("start_dir dir=%s\n",directory));
+
+ if (!check_name(directory,conn))
+ return(False);
+
+ /* use a const pointer from here on */
+ dir2 = directory;
+
+ if (! *dir2)
+ dir2 = ".";
+
+ conn->dirptr = OpenDir(conn, directory, True);
+ if (conn->dirptr) {
+ dptrs_open++;
+ string_set(&conn->dirpath,directory);
+ return(True);
+ }
+
+ return(False);
+}
+
+/****************************************************************************
+ Try and close the oldest handle not marked for
+ expect close in the hope that the client has
+ finished with that one.
+****************************************************************************/
+
+static void dptr_close_oldest(BOOL old)
+{
+ dptr_struct *dptr;
+
+ /*
+ * Go to the end of the list.
+ */
+ for(dptr = dirptrs; dptr && dptr->next; dptr = dptr->next)
+ ;
+
+ if(!dptr) {
+ DEBUG(0,("No old dptrs available to close oldest ?\n"));
+ return;
+ }
+
+ /*
+ * If 'old' is true, close the oldest oldhandle dnum (ie. 1 < dnum < 256) that
+ * does not have expect_close set. If 'old' is false, close
+ * one of the new dnum handles.
+ */
+
+ for(; dptr; dptr = dptr->prev) {
+ if ((old && (dptr->dnum < 256) && !dptr->expect_close) ||
+ (!old && (dptr->dnum > 255))) {
+ dptr_close_internal(dptr);
+ return;
+ }
+ }
+}
+
+/****************************************************************************
+ Create a new dir ptr. If the flag old_handle is true then we must allocate
+ from the bitmap range 0 - 255 as old SMBsearch directory handles are only
+ one byte long. If old_handle is false we allocate from the range
+ 256 - MAX_DIRECTORY_HANDLES. We bias the number we return by 1 to ensure
+ a directory handle is never zero. All the above is folklore taught to
+ me at Andrew's knee.... :-) :-). JRA.
+****************************************************************************/
+
+int dptr_create(connection_struct *conn, pstring path, BOOL old_handle, BOOL expect_close,uint16 spid)
+{
+ dptr_struct *dptr;
+
+ if (!start_dir(conn,path))
+ return(-2); /* Code to say use a unix error return code. */
+
+ if (dptrs_open >= MAX_OPEN_DIRECTORIES)
+ dptr_idleoldest();
+
+ dptr = (dptr_struct *)malloc(sizeof(dptr_struct));
+ if(!dptr) {
+ DEBUG(0,("malloc fail in dptr_create.\n"));
+ return -1;
+ }
+
+ ZERO_STRUCTP(dptr);
+
+ if(old_handle) {
+
+ /*
+ * This is an old-style SMBsearch request. Ensure the
+ * value we return will fit in the range 1-255.
+ */
+
+ dptr->dnum = bitmap_find(dptr_bmap, 0);
+
+ if(dptr->dnum == -1 || dptr->dnum > 254) {
+
+ /*
+ * Try and close the oldest handle not marked for
+ * expect close in the hope that the client has
+ * finished with that one.
+ */
+
+ dptr_close_oldest(True);
+
+ /* Now try again... */
+ dptr->dnum = bitmap_find(dptr_bmap, 0);
+ if(dptr->dnum == -1 || dptr->dnum > 254) {
+ DEBUG(0,("dptr_create: returned %d: Error - all old dirptrs in use ?\n", dptr->dnum));
+ SAFE_FREE(dptr);
+ return -1;
+ }
+ }
+ } else {
+
+ /*
+ * This is a new-style trans2 request. Allocate from
+ * a range that will return 256 - MAX_DIRECTORY_HANDLES.
+ */
+
+ dptr->dnum = bitmap_find(dptr_bmap, 255);
+
+ if(dptr->dnum == -1 || dptr->dnum < 255) {
+
+ /*
+ * Try and close the oldest handle close in the hope that
+ * the client has finished with that one. This will only
+ * happen in the case of the Win98 client bug where it leaks
+ * directory handles.
+ */
+
+ dptr_close_oldest(False);
+
+ /* Now try again... */
+ dptr->dnum = bitmap_find(dptr_bmap, 255);
+
+ if(dptr->dnum == -1 || dptr->dnum < 255) {
+ DEBUG(0,("dptr_create: returned %d: Error - all new dirptrs in use ?\n", dptr->dnum));
+ SAFE_FREE(dptr);
+ return -1;
+ }
+ }
+ }
+
+ bitmap_set(dptr_bmap, dptr->dnum);
+
+ dptr->dnum += 1; /* Always bias the dnum by one - no zero dnums allowed. */
+
+ dptr->ptr = conn->dirptr;
+ string_set(&dptr->path,path);
+ dptr->conn = conn;
+ dptr->spid = spid;
+ dptr->expect_close = expect_close;
+ dptr->wcard = NULL; /* Only used in lanman2 searches */
+ dptr->attr = 0; /* Only used in lanman2 searches */
+
+ DLIST_ADD(dirptrs, dptr);
+
+ DEBUG(3,("creating new dirptr %d for path %s, expect_close = %d\n",
+ dptr->dnum,path,expect_close));
+
+ return(dptr->dnum);
+}
+
+/****************************************************************************
+ Fill the 5 byte server reserved dptr field.
+****************************************************************************/
+
+BOOL dptr_fill(char *buf1,unsigned int key)
+{
+ unsigned char *buf = (unsigned char *)buf1;
+ void *p = dptr_ptr(key);
+ uint32 offset;
+ if (!p) {
+ DEBUG(1,("filling null dirptr %d\n",key));
+ return(False);
+ }
+ offset = TellDir(p);
+ DEBUG(6,("fill on key %u dirptr 0x%lx now at %d\n",key,
+ (long)p,(int)offset));
+ buf[0] = key;
+ SIVAL(buf,1,offset | DPTR_MASK);
+ return(True);
+}
+
+/****************************************************************************
+ Fetch the dir ptr and seek it given the 5 byte server field.
+****************************************************************************/
+
+void *dptr_fetch(char *buf,int *num)
+{
+ unsigned int key = *(unsigned char *)buf;
+ void *p = dptr_ptr(key);
+ uint32 offset;
+
+ if (!p) {
+ DEBUG(3,("fetched null dirptr %d\n",key));
+ return(NULL);
+ }
+ *num = key;
+ offset = IVAL(buf,1)&~DPTR_MASK;
+ SeekDir(p,offset);
+ DEBUG(3,("fetching dirptr %d for path %s at offset %d\n",
+ key,dptr_path(key),offset));
+ return(p);
+}
+
+/****************************************************************************
+ Fetch the dir ptr.
+****************************************************************************/
+
+void *dptr_fetch_lanman2(int dptr_num)
+{
+ void *p = dptr_ptr(dptr_num);
+
+ if (!p) {
+ DEBUG(3,("fetched null dirptr %d\n",dptr_num));
+ return(NULL);
+ }
+ DEBUG(3,("fetching dirptr %d for path %s\n",dptr_num,dptr_path(dptr_num)));
+ return(p);
+}
+
+/****************************************************************************
+ Check a filetype for being valid.
+****************************************************************************/
+
+BOOL dir_check_ftype(connection_struct *conn,int mode,SMB_STRUCT_STAT *st,int dirtype)
+{
+ int mask;
+
+ /* Check the "may have" search bits. */
+ if (((mode & ~dirtype) & (aHIDDEN | aSYSTEM | aDIR)) != 0)
+ return False;
+
+ /* Check the "must have" bits, which are the may have bits shifted eight */
+ /* If must have bit is set, the file/dir can not be returned in search unless the matching
+ file attribute is set */
+ mask = ((dirtype >> 8) & (aDIR|aARCH|aRONLY|aHIDDEN|aSYSTEM)); /* & 0x37 */
+ if(mask) {
+ if((mask & (mode & (aDIR|aARCH|aRONLY|aHIDDEN|aSYSTEM))) == mask) /* check if matching attribute present */
+ return True;
+ else
+ return False;
+ }
+
+ return True;
+}
+
+static BOOL mangle_mask_match(connection_struct *conn, fstring filename, char *mask)
+{
+ mangle_map(filename,True,False,SNUM(conn));
+ return mask_match(filename,mask,False);
+}
+
+/****************************************************************************
+ Get an 8.3 directory entry.
+****************************************************************************/
+
+BOOL get_dir_entry(connection_struct *conn,char *mask,int dirtype, pstring fname,
+ SMB_OFF_T *size,int *mode,time_t *date,BOOL check_descend)
+{
+ const char *dname;
+ BOOL found = False;
+ SMB_STRUCT_STAT sbuf;
+ pstring path;
+ pstring pathreal;
+ BOOL isrootdir;
+ pstring filename;
+ BOOL needslash;
+
+ *path = *pathreal = *filename = 0;
+
+ isrootdir = (strequal(conn->dirpath,"./") ||
+ strequal(conn->dirpath,".") ||
+ strequal(conn->dirpath,"/"));
+
+ needslash = ( conn->dirpath[strlen(conn->dirpath) -1] != '/');
+
+ if (!conn->dirptr)
+ return(False);
+
+ while (!found) {
+ dname = ReadDirName(conn->dirptr);
+
+ DEBUG(6,("readdir on dirptr 0x%lx now at offset %d\n",
+ (long)conn->dirptr,TellDir(conn->dirptr)));
+
+ if (dname == NULL)
+ return(False);
+
+ pstrcpy(filename,dname);
+
+ /* notice the special *.* handling. This appears to be the only difference
+ between the wildcard handling in this routine and in the trans2 routines.
+ see masktest for a demo
+ */
+ if ((strcmp(mask,"*.*") == 0) ||
+ mask_match(filename,mask,False) ||
+ mangle_mask_match(conn,filename,mask)) {
+ if (isrootdir && (strequal(filename,"..") || strequal(filename,".")))
+ continue;
+
+ if (!mangle_is_8_3(filename, False))
+ mangle_map(filename,True,False,SNUM(conn));
+
+ pstrcpy(fname,filename);
+ *path = 0;
+ pstrcpy(path,conn->dirpath);
+ if(needslash)
+ pstrcat(path,"/");
+ pstrcpy(pathreal,path);
+ pstrcat(path,fname);
+ pstrcat(pathreal,dname);
+ if (SMB_VFS_STAT(conn, pathreal, &sbuf) != 0) {
+ DEBUG(5,("Couldn't stat 1 [%s]. Error = %s\n",path, strerror(errno) ));
+ continue;
+ }
+
+ *mode = dos_mode(conn,pathreal,&sbuf);
+
+ if (!dir_check_ftype(conn,*mode,&sbuf,dirtype)) {
+ DEBUG(5,("[%s] attribs didn't match %x\n",filename,dirtype));
+ continue;
+ }
+
+ *size = sbuf.st_size;
+ *date = sbuf.st_mtime;
+
+ DEBUG(3,("get_dir_entry mask=[%s] found %s fname=%s\n",mask, pathreal,fname));
+
+ found = True;
+ }
+ }
+
+ return(found);
+}
+
+typedef struct {
+ int pos;
+ int numentries;
+ int mallocsize;
+ char *data;
+ char *current;
+} Dir;
+
+/*******************************************************************
+ Check to see if a user can read a file. This is only approximate,
+ it is used as part of the "hide unreadable" option. Don't
+ use it for anything security sensitive.
+********************************************************************/
+
+static BOOL user_can_read_file(connection_struct *conn, char *name, SMB_STRUCT_STAT *pst)
+{
+ extern struct current_user current_user;
+ SEC_DESC *psd = NULL;
+ size_t sd_size;
+ files_struct *fsp;
+ int smb_action;
+ NTSTATUS status;
+ uint32 access_granted;
+
+ /*
+ * If user is a member of the Admin group
+ * we never hide files from them.
+ */
+
+ if (conn->admin_user)
+ return True;
+
+ /* If we can't stat it does not show it */
+ if (!VALID_STAT(*pst) && (SMB_VFS_STAT(conn, name, pst) != 0))
+ return False;
+
+ /* Pseudo-open the file (note - no fd's created). */
+
+ if(S_ISDIR(pst->st_mode))
+ fsp = open_directory(conn, name, pst, 0, SET_DENY_MODE(DENY_NONE), (FILE_FAIL_IF_NOT_EXIST|FILE_EXISTS_OPEN),
+ &smb_action);
+ else
+ fsp = open_file_stat(conn, name, pst);
+
+ if (!fsp)
+ return False;
+
+ /* Get NT ACL -allocated in main loop talloc context. No free needed here. */
+ sd_size = SMB_VFS_FGET_NT_ACL(fsp, fsp->fd,
+ (OWNER_SECURITY_INFORMATION|GROUP_SECURITY_INFORMATION|DACL_SECURITY_INFORMATION), &psd);
+ close_file(fsp, True);
+
+ /* No access if SD get failed. */
+ if (!sd_size)
+ return False;
+
+ return se_access_check(psd, current_user.nt_user_token, FILE_READ_DATA,
+ &access_granted, &status);
+}
+
+/*******************************************************************
+ Check to see if a user can write a file (and only files, we do not
+ check dirs on this one). This is only approximate,
+ it is used as part of the "hide unwriteable" option. Don't
+ use it for anything security sensitive.
+********************************************************************/
+
+static BOOL user_can_write_file(connection_struct *conn, char *name, SMB_STRUCT_STAT *pst)
+{
+ extern struct current_user current_user;
+ SEC_DESC *psd = NULL;
+ size_t sd_size;
+ files_struct *fsp;
+ int smb_action;
+ int access_mode;
+ NTSTATUS status;
+ uint32 access_granted;
+
+ /*
+ * If user is a member of the Admin group
+ * we never hide files from them.
+ */
+
+ if (conn->admin_user)
+ return True;
+
+ /* If we can't stat it does not show it */
+ if (!VALID_STAT(*pst) && (SMB_VFS_STAT(conn, name, pst) != 0))
+ return False;
+
+ /* Pseudo-open the file (note - no fd's created). */
+
+ if(S_ISDIR(pst->st_mode))
+ return True;
+ else
+ fsp = open_file_shared1(conn, name, pst, FILE_WRITE_ATTRIBUTES, SET_DENY_MODE(DENY_NONE),
+ (FILE_FAIL_IF_NOT_EXIST|FILE_EXISTS_OPEN), FILE_ATTRIBUTE_NORMAL, 0, &access_mode, &smb_action);
+
+ if (!fsp)
+ return False;
+
+ /* Get NT ACL -allocated in main loop talloc context. No free needed here. */
+ sd_size = SMB_VFS_FGET_NT_ACL(fsp, fsp->fd,
+ (OWNER_SECURITY_INFORMATION|GROUP_SECURITY_INFORMATION|DACL_SECURITY_INFORMATION), &psd);
+ close_file(fsp, False);
+
+ /* No access if SD get failed. */
+ if (!sd_size)
+ return False;
+
+ return se_access_check(psd, current_user.nt_user_token, FILE_WRITE_DATA,
+ &access_granted, &status);
+}
+
+/*******************************************************************
+ Is a file a "special" type ?
+********************************************************************/
+
+static BOOL file_is_special(connection_struct *conn, char *name, SMB_STRUCT_STAT *pst)
+{
+ /*
+ * If user is a member of the Admin group
+ * we never hide files from them.
+ */
+
+ if (conn->admin_user)
+ return False;
+
+ /* If we can't stat it does not show it */
+ if (!VALID_STAT(*pst) && (SMB_VFS_STAT(conn, name, pst) != 0))
+ return True;
+
+ if (S_ISREG(pst->st_mode) || S_ISDIR(pst->st_mode) || S_ISLNK(pst->st_mode))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ Open a directory.
+********************************************************************/
+
+void *OpenDir(connection_struct *conn, const char *name, BOOL use_veto)
+{
+ Dir *dirp;
+ const char *n;
+ DIR *p = SMB_VFS_OPENDIR(conn,name);
+ int used=0;
+
+ if (!p)
+ return(NULL);
+ dirp = (Dir *)malloc(sizeof(Dir));
+ if (!dirp) {
+ DEBUG(0,("Out of memory in OpenDir\n"));
+ SMB_VFS_CLOSEDIR(conn,p);
+ return(NULL);
+ }
+ dirp->pos = dirp->numentries = dirp->mallocsize = 0;
+ dirp->data = dirp->current = NULL;
+
+ while (True) {
+ int l;
+ BOOL normal_entry = True;
+ SMB_STRUCT_STAT st;
+ char *entry = NULL;
+
+ if (used == 0) {
+ n = ".";
+ normal_entry = False;
+ } else if (used == 2) {
+ n = "..";
+ normal_entry = False;
+ } else {
+ n = vfs_readdirname(conn, p);
+ if (n == NULL)
+ break;
+ if ((strcmp(".",n) == 0) ||(strcmp("..",n) == 0))
+ continue;
+ normal_entry = True;
+ }
+
+ ZERO_STRUCT(st);
+ l = strlen(n)+1;
+
+ /* If it's a vetoed file, pretend it doesn't even exist */
+ if (normal_entry && use_veto && conn && IS_VETO_PATH(conn, n))
+ continue;
+
+ /* Honour _hide unreadable_ option */
+ if (normal_entry && conn && lp_hideunreadable(SNUM(conn))) {
+ int ret=0;
+
+ if (entry || asprintf(&entry, "%s/%s/%s", conn->origpath, name, n) > 0) {
+ ret = user_can_read_file(conn, entry, &st);
+ }
+ if (!ret) {
+ SAFE_FREE(entry);
+ continue;
+ }
+ }
+
+ /* Honour _hide unwriteable_ option */
+ if (normal_entry && conn && lp_hideunwriteable_files(SNUM(conn))) {
+ int ret=0;
+
+ if (entry || asprintf(&entry, "%s/%s/%s", conn->origpath, name, n) > 0) {
+ ret = user_can_write_file(conn, entry, &st);
+ }
+ if (!ret) {
+ SAFE_FREE(entry);
+ continue;
+ }
+ }
+
+ /* Honour _hide_special_ option */
+ if (normal_entry && conn && lp_hide_special_files(SNUM(conn))) {
+ int ret=0;
+
+ if (entry || asprintf(&entry, "%s/%s/%s", conn->origpath, name, n) > 0) {
+ ret = file_is_special(conn, entry, &st);
+ }
+ if (ret) {
+ SAFE_FREE(entry);
+ continue;
+ }
+ }
+
+ SAFE_FREE(entry);
+
+ if (used + l > dirp->mallocsize) {
+ int s = MAX(used+l,used+2000);
+ char *r;
+ r = (char *)Realloc(dirp->data,s);
+ if (!r) {
+ DEBUG(0,("Out of memory in OpenDir\n"));
+ break;
+ }
+ dirp->data = r;
+ dirp->mallocsize = s;
+ dirp->current = dirp->data;
+ }
+
+ safe_strcpy_base(dirp->data+used,n, dirp->data, dirp->mallocsize);
+ used += l;
+ dirp->numentries++;
+ }
+
+ SMB_VFS_CLOSEDIR(conn,p);
+ return((void *)dirp);
+}
+
+
+/*******************************************************************
+ Close a directory.
+********************************************************************/
+
+void CloseDir(void *p)
+{
+ if (!p)
+ return;
+ SAFE_FREE(((Dir *)p)->data);
+ SAFE_FREE(p);
+}
+
+/*******************************************************************
+ Read from a directory.
+********************************************************************/
+
+const char *ReadDirName(void *p)
+{
+ char *ret;
+ Dir *dirp = (Dir *)p;
+
+ if (!dirp || !dirp->current || dirp->pos >= dirp->numentries)
+ return(NULL);
+
+ ret = dirp->current;
+ dirp->current = skip_string(dirp->current,1);
+ dirp->pos++;
+
+ return(ret);
+}
+
+/*******************************************************************
+ Seek a dir.
+********************************************************************/
+
+BOOL SeekDir(void *p,int pos)
+{
+ Dir *dirp = (Dir *)p;
+
+ if (!dirp)
+ return(False);
+
+ if (pos < dirp->pos) {
+ dirp->current = dirp->data;
+ dirp->pos = 0;
+ }
+
+ while (dirp->pos < pos && ReadDirName(p))
+ ;
+
+ return (dirp->pos == pos);
+}
+
+/*******************************************************************
+ Tell a dir position.
+********************************************************************/
+
+int TellDir(void *p)
+{
+ Dir *dirp = (Dir *)p;
+
+ if (!dirp)
+ return(-1);
+
+ return(dirp->pos);
+}
+
+/*******************************************************************************
+ This section manages a global directory cache.
+ (It should probably be split into a separate module. crh)
+********************************************************************************/
+
+typedef struct {
+ ubi_dlNode node;
+ char *path;
+ char *name;
+ char *dname;
+ int snum;
+} dir_cache_entry;
+
+static ubi_dlNewList( dir_cache );
+
+/*****************************************************************************
+ Add an entry to the directory cache.
+ Input: path -
+ name -
+ dname -
+ snum -
+ Output: None.
+*****************************************************************************/
+
+void DirCacheAdd( const char *path, const char *name, const char *dname, int snum )
+{
+ int pathlen;
+ int namelen;
+ dir_cache_entry *entry;
+
+ /*
+ * Allocate the structure & string space in one go so that it can be freed
+ * in one call to free().
+ */
+ pathlen = strlen(path) + 1; /* Bytes required to store path (with nul). */
+ namelen = strlen(name) + 1; /* Bytes required to store name (with nul). */
+ entry = (dir_cache_entry *)malloc( sizeof( dir_cache_entry )
+ + pathlen
+ + namelen
+ + strlen( dname ) +1 );
+ if( NULL == entry ) /* Not adding to the cache is not fatal, */
+ return; /* so just return as if nothing happened. */
+
+ /* Set pointers correctly and load values. */
+ entry->path = memcpy( (char *)&entry[1], path, strlen(path)+1 );
+ entry->name = memcpy( &(entry->path[pathlen]), name, strlen(name)+1 );
+ entry->dname = memcpy( &(entry->name[namelen]), dname, strlen(dname)+1 );
+ entry->snum = snum;
+
+ /* Add the new entry to the linked list. */
+ (void)ubi_dlAddHead( dir_cache, entry );
+ DEBUG( 4, ("Added dir cache entry %s %s -> %s\n", path, name, dname ) );
+
+ /* Free excess cache entries. */
+ while( DIRCACHESIZE < dir_cache->count )
+ safe_free( ubi_dlRemTail( dir_cache ) );
+}
+
+/*****************************************************************************
+ Search for an entry to the directory cache.
+ Input: path -
+ name -
+ snum -
+ Output: The dname string of the located entry, or NULL if the entry was
+ not found.
+
+ Notes: This uses a linear search, which is is okay because of
+ the small size of the cache. Use a splay tree or hash
+ for large caches.
+*****************************************************************************/
+
+char *DirCacheCheck( const char *path, const char *name, int snum )
+{
+ dir_cache_entry *entry;
+
+ for( entry = (dir_cache_entry *)ubi_dlFirst( dir_cache );
+ NULL != entry;
+ entry = (dir_cache_entry *)ubi_dlNext( entry ) ) {
+ if( entry->snum == snum
+ && entry->name && 0 == strcmp( name, entry->name )
+ && entry->path && 0 == strcmp( path, entry->path ) ) {
+ DEBUG(4, ("Got dir cache hit on %s %s -> %s\n",path,name,entry->dname));
+ return( entry->dname );
+ }
+ }
+
+ return(NULL);
+}
+
+/*****************************************************************************
+ Remove all cache entries which have an snum that matches the input.
+ Input: snum -
+ Output: None.
+*****************************************************************************/
+
+void DirCacheFlush(int snum)
+{
+ dir_cache_entry *entry;
+ ubi_dlNodePtr next;
+
+ for(entry = (dir_cache_entry *)ubi_dlFirst( dir_cache );
+ NULL != entry; ) {
+ next = ubi_dlNext( entry );
+ if( entry->snum == snum )
+ safe_free( ubi_dlRemThis( dir_cache, entry ) );
+ entry = (dir_cache_entry *)next;
+ }
+}
diff --git a/source/smbd/dosmode.c b/source/smbd/dosmode.c
new file mode 100644
index 00000000000..d7dc63bb2fd
--- /dev/null
+++ b/source/smbd/dosmode.c
@@ -0,0 +1,483 @@
+/*
+ Unix SMB/CIFS implementation.
+ dos mode handling functions
+ 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 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"
+
+/****************************************************************************
+ Change a dos mode to a unix mode.
+ Base permission for files:
+ if inheriting
+ apply read/write bits from parent directory.
+ else
+ everybody gets read bit set
+ dos readonly is represented in unix by removing everyone's write bit
+ dos archive is represented in unix by the user's execute bit
+ dos system is represented in unix by the group's execute bit
+ dos hidden is represented in unix by the other's execute bit
+ if !inheriting {
+ Then apply create mask,
+ then add force bits.
+ }
+ Base permission for directories:
+ dos directory is represented in unix by unix's dir bit and the exec bit
+ if !inheriting {
+ Then apply create mask,
+ then add force bits.
+ }
+****************************************************************************/
+
+mode_t unix_mode(connection_struct *conn, int dosmode, const char *fname)
+{
+ mode_t result = (S_IRUSR | S_IRGRP | S_IROTH | S_IWUSR | S_IWGRP | S_IWOTH);
+ mode_t dir_mode = 0; /* Mode of the parent directory if inheriting. */
+
+ if (!lp_store_dos_attributes(SNUM(conn)) && IS_DOS_READONLY(dosmode)) {
+ result &= ~(S_IWUSR | S_IWGRP | S_IWOTH);
+ }
+
+ if (fname && lp_inherit_perms(SNUM(conn))) {
+ char *dname;
+ SMB_STRUCT_STAT sbuf;
+
+ dname = parent_dirname(fname);
+ DEBUG(2,("unix_mode(%s) inheriting from %s\n",fname,dname));
+ if (SMB_VFS_STAT(conn,dname,&sbuf) != 0) {
+ DEBUG(4,("unix_mode(%s) failed, [dir %s]: %s\n",fname,dname,strerror(errno)));
+ return(0); /* *** shouldn't happen! *** */
+ }
+
+ /* Save for later - but explicitly remove setuid bit for safety. */
+ dir_mode = sbuf.st_mode & ~S_ISUID;
+ DEBUG(2,("unix_mode(%s) inherit mode %o\n",fname,(int)dir_mode));
+ /* Clear "result" */
+ result = 0;
+ }
+
+ if (IS_DOS_DIR(dosmode)) {
+ /* We never make directories read only for the owner as under DOS a user
+ can always create a file in a read-only directory. */
+ result |= (S_IFDIR | S_IWUSR);
+
+ if (dir_mode) {
+ /* Inherit mode of parent directory. */
+ result |= dir_mode;
+ } else {
+ /* Provisionally add all 'x' bits */
+ result |= (S_IXUSR | S_IXGRP | S_IXOTH);
+
+ /* Apply directory mask */
+ result &= lp_dir_mask(SNUM(conn));
+ /* Add in force bits */
+ result |= lp_force_dir_mode(SNUM(conn));
+ }
+ } else {
+ if (lp_map_archive(SNUM(conn)) && IS_DOS_ARCHIVE(dosmode))
+ result |= S_IXUSR;
+
+ if (lp_map_system(SNUM(conn)) && IS_DOS_SYSTEM(dosmode))
+ result |= S_IXGRP;
+
+ if (lp_map_hidden(SNUM(conn)) && IS_DOS_HIDDEN(dosmode))
+ result |= S_IXOTH;
+
+ if (dir_mode) {
+ /* Inherit 666 component of parent directory mode */
+ result |= dir_mode & (S_IRUSR | S_IRGRP | S_IROTH | S_IWUSR | S_IWGRP | S_IWOTH);
+ } else {
+ /* Apply mode mask */
+ result &= lp_create_mask(SNUM(conn));
+ /* Add in force bits */
+ result |= lp_force_create_mode(SNUM(conn));
+ }
+ }
+
+ DEBUG(3,("unix_mode(%s) returning 0%o\n",fname,(int)result ));
+ return(result);
+}
+
+/****************************************************************************
+ Change a unix mode to a dos mode.
+****************************************************************************/
+
+uint32 dos_mode_from_sbuf(connection_struct *conn, SMB_STRUCT_STAT *sbuf)
+{
+ int result = 0;
+
+ if ((sbuf->st_mode & S_IWUSR) == 0)
+ result |= aRONLY;
+
+ if (MAP_ARCHIVE(conn) && ((sbuf->st_mode & S_IXUSR) != 0))
+ result |= aARCH;
+
+ if (MAP_SYSTEM(conn) && ((sbuf->st_mode & S_IXGRP) != 0))
+ result |= aSYSTEM;
+
+ if (MAP_HIDDEN(conn) && ((sbuf->st_mode & S_IXOTH) != 0))
+ result |= aHIDDEN;
+
+ if (S_ISDIR(sbuf->st_mode))
+ result = aDIR | (result & aRONLY);
+
+#if defined (HAVE_STAT_ST_BLOCKS) && defined (HAVE_STAT_ST_BLKSIZE)
+ if (sbuf->st_size > sbuf->st_blocks * (SMB_OFF_T)sbuf->st_blksize) {
+ result |= FILE_ATTRIBUTE_SPARSE;
+ }
+#endif
+
+#ifdef S_ISLNK
+#if LINKS_READ_ONLY
+ if (S_ISLNK(sbuf->st_mode) && S_ISDIR(sbuf->st_mode))
+ result |= aRONLY;
+#endif
+#endif
+
+ DEBUG(8,("dos_mode_from_sbuf returning "));
+
+ if (result & aHIDDEN) DEBUG(8, ("h"));
+ if (result & aRONLY ) DEBUG(8, ("r"));
+ if (result & aSYSTEM) DEBUG(8, ("s"));
+ if (result & aDIR ) DEBUG(8, ("d"));
+ if (result & aARCH ) DEBUG(8, ("a"));
+
+ DEBUG(8,("\n"));
+ return result;
+}
+
+/****************************************************************************
+ Get DOS attributes from an EA.
+****************************************************************************/
+
+static BOOL get_ea_dos_attribute(connection_struct *conn, const char *path,SMB_STRUCT_STAT *sbuf, uint32 *pattr)
+{
+ ssize_t sizeret;
+ fstring attrstr;
+ unsigned int dosattr;
+
+ if (!lp_store_dos_attributes(SNUM(conn))) {
+ return False;
+ }
+
+ *pattr = 0;
+
+ sizeret = SMB_VFS_GETXATTR(conn, path, SAMBA_XATTR_DOS_ATTRIB, attrstr, sizeof(attrstr));
+ if (sizeret == -1) {
+#if defined(ENOTSUP) && defined(ENOATTR)
+ if ((errno != ENOTSUP) && (errno != ENOATTR) && (errno != EACCES)) {
+ DEBUG(1,("get_ea_dos_attributes: Cannot get attribute from EA on file %s: Error = %s\n",
+ path, strerror(errno) ));
+ }
+#endif
+ return False;
+ }
+ /* Null terminate string. */
+ attrstr[sizeret] = 0;
+ DEBUG(10,("get_ea_dos_attribute: %s attrstr = %s\n", path, attrstr));
+
+ if (sizeret < 2 || attrstr[0] != '0' || attrstr[1] != 'x' ||
+ sscanf(attrstr, "%x", &dosattr) != 1) {
+ DEBUG(1,("get_ea_dos_attributes: Badly formed DOSATTRIB on file %s - %s\n", path, attrstr));
+ return False;
+ }
+
+ if (S_ISDIR(sbuf->st_mode)) {
+ dosattr |= aDIR;
+ }
+ *pattr = (uint32)(dosattr & SAMBA_ATTRIBUTES_MASK);
+
+ DEBUG(8,("get_ea_dos_attribute returning (0x%x)", dosattr));
+
+ if (dosattr & aHIDDEN) DEBUG(8, ("h"));
+ if (dosattr & aRONLY ) DEBUG(8, ("r"));
+ if (dosattr & aSYSTEM) DEBUG(8, ("s"));
+ if (dosattr & aDIR ) DEBUG(8, ("d"));
+ if (dosattr & aARCH ) DEBUG(8, ("a"));
+
+ DEBUG(8,("\n"));
+
+ return True;
+}
+
+/****************************************************************************
+ Set DOS attributes in an EA.
+****************************************************************************/
+
+static BOOL set_ea_dos_attribute(connection_struct *conn, const char *path, SMB_STRUCT_STAT *sbuf, uint32 dosmode)
+{
+ fstring attrstr;
+ files_struct *fsp = NULL;
+ BOOL ret = False;
+
+ snprintf(attrstr, sizeof(attrstr)-1, "0x%x", dosmode & SAMBA_ATTRIBUTES_MASK);
+ if (SMB_VFS_SETXATTR(conn, path, SAMBA_XATTR_DOS_ATTRIB, attrstr, strlen(attrstr), 0) == -1) {
+ if((errno != EPERM) && (errno != EACCES)) {
+ return False;
+ }
+
+ /* We want DOS semantics, ie allow non owner with write permission to change the
+ bits on a file. Just like file_utime below.
+ */
+
+ /* Check if we have write access. */
+ if(!CAN_WRITE(conn) || !lp_dos_filemode(SNUM(conn)))
+ return False;
+
+ /*
+ * We need to open the file with write access whilst
+ * still in our current user context. This ensures we
+ * are not violating security in doing the setxattr.
+ */
+
+ fsp = open_file_fchmod(conn,path,sbuf);
+ if (!fsp)
+ return ret;
+ become_root();
+ if (SMB_VFS_SETXATTR(conn, path, SAMBA_XATTR_DOS_ATTRIB, attrstr, strlen(attrstr), 0) == 0) {
+ ret = True;
+ }
+ unbecome_root();
+ close_file_fchmod(fsp);
+ return ret;
+ }
+ DEBUG(10,("set_ea_dos_attribute: set EA %s on file %s\n", attrstr, path));
+ return True;
+}
+
+/****************************************************************************
+ Change a unix mode to a dos mode.
+****************************************************************************/
+
+uint32 dos_mode(connection_struct *conn, const char *path,SMB_STRUCT_STAT *sbuf)
+{
+ uint32 result = 0;
+
+ DEBUG(8,("dos_mode: %s\n", path));
+
+ if (!VALID_STAT(*sbuf)) {
+ return 0;
+ }
+
+ /* Get the DOS attributes from an EA by preference. */
+ if (get_ea_dos_attribute(conn, path, sbuf, &result)) {
+ return result;
+ }
+
+ result = dos_mode_from_sbuf(conn, sbuf);
+
+ /* Now do any modifications that depend on the path name. */
+ /* hide files with a name starting with a . */
+ if (lp_hide_dot_files(SNUM(conn))) {
+ const char *p = strrchr_m(path,'/');
+ if (p)
+ p++;
+ else
+ p = path;
+
+ if (p[0] == '.' && p[1] != '.' && p[1] != 0)
+ result |= aHIDDEN;
+ }
+
+ /* Optimization : Only call is_hidden_path if it's not already
+ hidden. */
+ if (!(result & aHIDDEN) && IS_HIDDEN_PATH(conn,path)) {
+ result |= aHIDDEN;
+ }
+
+ DEBUG(8,("dos_mode returning "));
+
+ if (result & aHIDDEN) DEBUG(8, ("h"));
+ if (result & aRONLY ) DEBUG(8, ("r"));
+ if (result & aSYSTEM) DEBUG(8, ("s"));
+ if (result & aDIR ) DEBUG(8, ("d"));
+ if (result & aARCH ) DEBUG(8, ("a"));
+
+ DEBUG(8,("\n"));
+
+ return(result);
+}
+
+/*******************************************************************
+ chmod a file - but preserve some bits.
+********************************************************************/
+
+int file_set_dosmode(connection_struct *conn, const char *fname, uint32 dosmode, SMB_STRUCT_STAT *st)
+{
+ SMB_STRUCT_STAT st1;
+ int mask=0;
+ mode_t tmp;
+ mode_t unixmode;
+ int ret = -1;
+
+ DEBUG(10,("file_set_dosmode: setting dos mode 0x%x on file %s\n", dosmode, fname));
+ if (!st) {
+ st = &st1;
+ if (SMB_VFS_STAT(conn,fname,st))
+ return(-1);
+ }
+
+ get_acl_group_bits(conn, fname, &st->st_mode);
+
+ if (S_ISDIR(st->st_mode))
+ dosmode |= aDIR;
+ else
+ dosmode &= ~aDIR;
+
+ if (dos_mode(conn,fname,st) == dosmode)
+ return(0);
+
+ /* Store the DOS attributes in an EA by preference. */
+ if (set_ea_dos_attribute(conn, fname, st, dosmode)) {
+ return 0;
+ }
+
+ unixmode = unix_mode(conn,dosmode,fname);
+
+ /* preserve the s bits */
+ mask |= (S_ISUID | S_ISGID);
+
+ /* preserve the t bit */
+#ifdef S_ISVTX
+ mask |= S_ISVTX;
+#endif
+
+ /* possibly preserve the x bits */
+ if (!MAP_ARCHIVE(conn))
+ mask |= S_IXUSR;
+ if (!MAP_SYSTEM(conn))
+ mask |= S_IXGRP;
+ if (!MAP_HIDDEN(conn))
+ mask |= S_IXOTH;
+
+ unixmode |= (st->st_mode & mask);
+
+ /* if we previously had any r bits set then leave them alone */
+ if ((tmp = st->st_mode & (S_IRUSR|S_IRGRP|S_IROTH))) {
+ unixmode &= ~(S_IRUSR|S_IRGRP|S_IROTH);
+ unixmode |= tmp;
+ }
+
+ /* if we previously had any w bits set then leave them alone
+ whilst adding in the new w bits, if the new mode is not rdonly */
+ if (!IS_DOS_READONLY(dosmode)) {
+ unixmode |= (st->st_mode & (S_IWUSR|S_IWGRP|S_IWOTH));
+ }
+
+ if ((ret = SMB_VFS_CHMOD(conn,fname,unixmode)) == 0)
+ return 0;
+
+ if((errno != EPERM) && (errno != EACCES))
+ return -1;
+
+ if(!lp_dos_filemode(SNUM(conn)))
+ return -1;
+
+ /* We want DOS semantics, ie allow non owner with write permission to change the
+ bits on a file. Just like file_utime below.
+ */
+
+ /* Check if we have write access. */
+ if (CAN_WRITE(conn)) {
+ /*
+ * We need to open the file with write access whilst
+ * still in our current user context. This ensures we
+ * are not violating security in doing the fchmod.
+ * This file open does *not* break any oplocks we are
+ * holding. We need to review this.... may need to
+ * break batch oplocks open by others. JRA.
+ */
+ files_struct *fsp = open_file_fchmod(conn,fname,st);
+ if (!fsp)
+ return -1;
+ become_root();
+ ret = SMB_VFS_FCHMOD(fsp, fsp->fd, unixmode);
+ unbecome_root();
+ close_file_fchmod(fsp);
+ }
+
+ return( ret );
+}
+
+/*******************************************************************
+ Wrapper around dos_utime that possibly allows DOS semantics rather
+ than POSIX.
+*******************************************************************/
+
+int file_utime(connection_struct *conn, char *fname, struct utimbuf *times)
+{
+ extern struct current_user current_user;
+ SMB_STRUCT_STAT sb;
+ int ret = -1;
+
+ errno = 0;
+
+ if(SMB_VFS_UTIME(conn,fname, times) == 0)
+ return 0;
+
+ if((errno != EPERM) && (errno != EACCES))
+ return -1;
+
+ if(!lp_dos_filetimes(SNUM(conn)))
+ return -1;
+
+ /* We have permission (given by the Samba admin) to
+ break POSIX semantics and allow a user to change
+ the time on a file they don't own but can write to
+ (as DOS does).
+ */
+
+ if(SMB_VFS_STAT(conn,fname,&sb) != 0)
+ return -1;
+
+ /* Check if we have write access. */
+ if (CAN_WRITE(conn)) {
+ if (((sb.st_mode & S_IWOTH) || conn->admin_user ||
+ ((sb.st_mode & S_IWUSR) && current_user.uid==sb.st_uid) ||
+ ((sb.st_mode & S_IWGRP) &&
+ in_group(sb.st_gid,current_user.gid,
+ current_user.ngroups,current_user.groups)))) {
+ /* We are allowed to become root and change the filetime. */
+ become_root();
+ ret = SMB_VFS_UTIME(conn,fname, times);
+ unbecome_root();
+ }
+ }
+
+ return ret;
+}
+
+/*******************************************************************
+ Change a filetime - possibly allowing DOS semantics.
+*******************************************************************/
+
+BOOL set_filetime(connection_struct *conn, char *fname, time_t mtime)
+{
+ struct utimbuf times;
+
+ if (null_mtime(mtime))
+ return(True);
+
+ times.modtime = times.actime = mtime;
+
+ if (file_utime(conn, fname, &times)) {
+ DEBUG(4,("set_filetime(%s) failed: %s\n",fname,strerror(errno)));
+ return False;
+ }
+
+ return(True);
+}
diff --git a/source/smbd/error.c b/source/smbd/error.c
new file mode 100644
index 00000000000..795bf0949cc
--- /dev/null
+++ b/source/smbd/error.c
@@ -0,0 +1,137 @@
+/*
+ Unix SMB/CIFS implementation.
+ error packet handling
+ 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 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"
+
+/* these can be set by some functions to override the error codes */
+int unix_ERR_class=SMB_SUCCESS;
+int unix_ERR_code=0;
+NTSTATUS unix_ERR_ntstatus = NT_STATUS_OK;
+
+/* From lib/error.c */
+extern struct unix_error_map unix_dos_nt_errmap[];
+
+/****************************************************************************
+ Create an error packet from a cached error.
+****************************************************************************/
+
+int cached_error_packet(char *outbuf,files_struct *fsp,int line,const char *file)
+{
+ write_bmpx_struct *wbmpx = fsp->wbmpx_ptr;
+
+ int32 eclass = wbmpx->wr_errclass;
+ int32 err = wbmpx->wr_error;
+
+ /* We can now delete the auxiliary struct */
+ free((char *)wbmpx);
+ fsp->wbmpx_ptr = NULL;
+ return error_packet(outbuf,NT_STATUS_OK,eclass,err,line,file);
+}
+
+/****************************************************************************
+ Create an error packet from errno.
+****************************************************************************/
+
+int unix_error_packet(char *outbuf,int def_class,uint32 def_code,
+ int line, const char *file)
+{
+ int eclass=def_class;
+ int ecode=def_code;
+ NTSTATUS ntstatus = NT_STATUS_OK;
+ int i=0;
+
+ if (unix_ERR_class != SMB_SUCCESS) {
+ eclass = unix_ERR_class;
+ ecode = unix_ERR_code;
+ ntstatus = unix_ERR_ntstatus;
+ unix_ERR_class = SMB_SUCCESS;
+ unix_ERR_code = 0;
+ unix_ERR_ntstatus = NT_STATUS_OK;
+ } else {
+ while (unix_dos_nt_errmap[i].dos_class != 0) {
+ if (unix_dos_nt_errmap[i].unix_error == errno) {
+ eclass = unix_dos_nt_errmap[i].dos_class;
+ ecode = unix_dos_nt_errmap[i].dos_code;
+ ntstatus = unix_dos_nt_errmap[i].nt_error;
+ break;
+ }
+ i++;
+ }
+ }
+
+ return error_packet(outbuf,ntstatus,eclass,ecode,line,file);
+}
+
+
+/****************************************************************************
+ Create an error packet. Normally called using the ERROR() macro.
+****************************************************************************/
+
+int error_packet(char *outbuf,NTSTATUS ntstatus,
+ uint8 eclass,uint32 ecode,int line, const char *file)
+{
+ int outsize = set_message(outbuf,0,0,True);
+ extern uint32 global_client_caps;
+
+ if (errno != 0)
+ DEBUG(3,("error string = %s\n",strerror(errno)));
+
+#if defined(DEVELOPER)
+ if (unix_ERR_class != SMB_SUCCESS || unix_ERR_code != 0 || !NT_STATUS_IS_OK(unix_ERR_ntstatus))
+ smb_panic("logic error in error processing");
+#endif
+
+ /*
+ * We can explicitly force 32 bit error codes even when the
+ * parameter "nt status" is set to no by pre-setting the
+ * FLAGS2_32_BIT_ERROR_CODES bit in the smb_flg2 outbuf.
+ * This is to allow work arounds for client bugs that are needed
+ * when talking with clients that normally expect nt status codes. JRA.
+ */
+
+ if ((lp_nt_status_support() || (SVAL(outbuf,smb_flg2) & FLAGS2_32_BIT_ERROR_CODES)) && (global_client_caps & CAP_STATUS32)) {
+ if (NT_STATUS_V(ntstatus) == 0 && eclass)
+ ntstatus = dos_to_ntstatus(eclass, ecode);
+ SIVAL(outbuf,smb_rcls,NT_STATUS_V(ntstatus));
+ SSVAL(outbuf,smb_flg2, SVAL(outbuf,smb_flg2)|FLAGS2_32_BIT_ERROR_CODES);
+ DEBUG(3,("error packet at %s(%d) cmd=%d (%s) %s\n",
+ file, line,
+ (int)CVAL(outbuf,smb_com),
+ smb_fn_name(CVAL(outbuf,smb_com)),
+ nt_errstr(ntstatus)));
+ return outsize;
+ }
+
+ if (eclass == 0 && NT_STATUS_V(ntstatus))
+ ntstatus_to_dos(ntstatus, &eclass, &ecode);
+
+ SSVAL(outbuf,smb_flg2, SVAL(outbuf,smb_flg2)&~FLAGS2_32_BIT_ERROR_CODES);
+ SSVAL(outbuf,smb_rcls,eclass);
+ SSVAL(outbuf,smb_err,ecode);
+
+ DEBUG(3,("error packet at %s(%d) cmd=%d (%s) eclass=%d ecode=%d\n",
+ file, line,
+ (int)CVAL(outbuf,smb_com),
+ smb_fn_name(CVAL(outbuf,smb_com)),
+ eclass,
+ ecode));
+
+ return outsize;
+}
diff --git a/source/smbd/fake_file.c b/source/smbd/fake_file.c
new file mode 100644
index 00000000000..5ccb548ba5b
--- /dev/null
+++ b/source/smbd/fake_file.c
@@ -0,0 +1,166 @@
+/*
+ Unix SMB/CIFS implementation.
+ FAKE FILE suppport, for faking up special files windows want access to
+ 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 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"
+
+/****************************************************************************
+ Open a file with a share mode.
+****************************************************************************/
+files_struct *open_fake_file_shared1(enum FAKE_FILE_TYPE fake_file_type, connection_struct *conn,char *fname,
+ SMB_STRUCT_STAT *psbuf,
+ uint32 desired_access,
+ int share_mode,int ofun, uint32 new_dos_attr, int oplock_request,
+ int *Access,int *action)
+{
+ extern struct current_user current_user;
+ int flags=0;
+ files_struct *fsp = NULL;
+
+ if (fake_file_type == 0) {
+ return open_file_shared1(conn,fname,psbuf,desired_access,
+ share_mode,ofun,new_dos_attr,
+ oplock_request,Access,action);
+ }
+
+ /* access check */
+ if (conn->admin_user != True) {
+ DEBUG(1,("access_denied to service[%s] file[%s] user[%s]\n",
+ lp_servicename(SNUM(conn)),fname,conn->user));
+ errno = EACCES;
+ return NULL;
+ }
+
+ fsp = file_new(conn);
+ if(!fsp)
+ return NULL;
+
+ DEBUG(5,("open_fake_file_shared1: fname = %s, FID = %d, share_mode = %x, ofun = %x, oplock request = %d\n",
+ fname, fsp->fnum, share_mode, ofun, oplock_request ));
+
+ if (!check_name(fname,conn)) {
+ file_free(fsp);
+ return NULL;
+ }
+
+ fsp->fd = -1;
+ fsp->mode = psbuf->st_mode;
+ fsp->inode = psbuf->st_ino;
+ fsp->dev = psbuf->st_dev;
+ fsp->vuid = current_user.vuid;
+ fsp->size = psbuf->st_size;
+ fsp->pos = -1;
+ fsp->can_lock = True;
+ fsp->can_read = ((flags & O_WRONLY)==0);
+ fsp->can_write = ((flags & (O_WRONLY|O_RDWR))!=0);
+ fsp->share_mode = 0;
+ fsp->desired_access = desired_access;
+ fsp->print_file = False;
+ fsp->modified = False;
+ fsp->oplock_type = NO_OPLOCK;
+ fsp->sent_oplock_break = NO_BREAK_SENT;
+ fsp->is_directory = False;
+ fsp->is_stat = False;
+ fsp->directory_delete_on_close = False;
+ fsp->conn = conn;
+ string_set(&fsp->fsp_name,fname);
+ fsp->wcp = NULL; /* Write cache pointer. */
+
+ fsp->fake_file_handle = init_fake_file_handle(fake_file_type);
+
+ if (fsp->fake_file_handle==NULL) {
+ file_free(fsp);
+ return NULL;
+ }
+
+ conn->num_files_open++;
+ return fsp;
+}
+
+static FAKE_FILE fake_files[] = {
+#ifdef WITH_QUOTAS
+ {FAKE_FILE_NAME_QUOTA, FAKE_FILE_TYPE_QUOTA, init_quota_handle, destroy_quota_handle},
+#endif /* WITH_QUOTAS */
+ {NULL, FAKE_FILE_TYPE_NONE, NULL, NULL }
+};
+
+int is_fake_file(char *fname)
+{
+ int i;
+
+ if (!fname)
+ return 0;
+
+ for (i=0;fake_files[i].name!=NULL;i++) {
+ if (strncmp(fname,fake_files[i].name,strlen(fake_files[i].name))==0) {
+ DEBUG(5,("is_fake_file: [%s] is a fake file\n",fname));
+ return fake_files[i].type;
+ }
+ }
+
+ return FAKE_FILE_TYPE_NONE;
+}
+
+struct _FAKE_FILE_HANDLE *init_fake_file_handle(enum FAKE_FILE_TYPE type)
+{
+ TALLOC_CTX *mem_ctx = NULL;
+ FAKE_FILE_HANDLE *fh = NULL;
+ int i;
+
+ for (i=0;fake_files[i].name!=NULL;i++) {
+ if (fake_files[i].type==type) {
+ DEBUG(5,("init_fake_file_handle: for [%s]\n",fake_files[i].name));
+
+ if ((mem_ctx=talloc_init("fake_file_handle"))==NULL) {
+ DEBUG(0,("talloc_init(fake_file_handle) failed.\n"));
+ return NULL;
+ }
+
+ if ((fh =(FAKE_FILE_HANDLE *)talloc_zero(mem_ctx, sizeof(FAKE_FILE_HANDLE)))==NULL) {
+ DEBUG(0,("talloc_zero() failed.\n"));
+ talloc_destroy(mem_ctx);
+ return NULL;
+ }
+
+ fh->type = type;
+ fh->mem_ctx = mem_ctx;
+
+ if (fake_files[i].init_pd)
+ fh->pd = fake_files[i].init_pd(fh->mem_ctx);
+
+ fh->free_pd = fake_files[i].free_pd;
+
+ return fh;
+ }
+ }
+
+ return NULL;
+}
+
+void destroy_fake_file_handle(FAKE_FILE_HANDLE **fh)
+{
+ if (!fh||!(*fh))
+ return ;
+
+ if ((*fh)->free_pd)
+ (*fh)->free_pd(&(*fh)->pd);
+
+ talloc_destroy((*fh)->mem_ctx);
+ (*fh) = NULL;
+}
diff --git a/source/smbd/fileio.c b/source/smbd/fileio.c
new file mode 100644
index 00000000000..c2fb6e34566
--- /dev/null
+++ b/source/smbd/fileio.c
@@ -0,0 +1,773 @@
+/*
+ Unix SMB/Netbios implementation.
+ Version 1.9.
+ read/write to a files_struct
+ Copyright (C) Andrew Tridgell 1992-1998
+ Copyright (C) Jeremy Allison 2000-2002. - write cache.
+
+ This program is free software; you can 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"
+
+static BOOL setup_write_cache(files_struct *, SMB_OFF_T);
+
+/****************************************************************************
+ Read from write cache if we can.
+****************************************************************************/
+
+
+static BOOL read_from_write_cache(files_struct *fsp,char *data,SMB_OFF_T pos,size_t n)
+{
+ write_cache *wcp = fsp->wcp;
+
+ if(!wcp)
+ return False;
+
+ if(n > wcp->data_size || pos < wcp->offset || pos + n > wcp->offset + wcp->data_size)
+ return False;
+
+ memcpy(data, wcp->data + (pos - wcp->offset), n);
+
+ DO_PROFILE_INC(writecache_read_hits);
+
+ return True;
+}
+
+/****************************************************************************
+ Read from a file.
+****************************************************************************/
+
+ssize_t read_file(files_struct *fsp,char *data,SMB_OFF_T pos,size_t n)
+{
+ ssize_t ret=0,readret;
+
+ /* you can't read from print files */
+ if (fsp->print_file)
+ return -1;
+
+ /*
+ * Serve from write cache if we can.
+ */
+
+ if(read_from_write_cache(fsp, data, pos, n)) {
+ fsp->pos = pos + n;
+ fsp->position_information = fsp->pos;
+ return n;
+ }
+
+ flush_write_cache(fsp, READ_FLUSH);
+
+ fsp->pos = pos;
+
+ if (n > 0) {
+#ifdef DMF_FIX
+ int numretries = 3;
+tryagain:
+ readret = SMB_VFS_PREAD(fsp,fsp->fd,data,n,pos);
+
+ if (readret == -1) {
+ if ((errno == EAGAIN) && numretries) {
+ DEBUG(3,("read_file EAGAIN retry in 10 seconds\n"));
+ (void)sleep(10);
+ --numretries;
+ goto tryagain;
+ }
+ return -1;
+ }
+#else /* NO DMF fix. */
+ readret = SMB_VFS_PREAD(fsp,fsp->fd,data,n,pos);
+
+ if (readret == -1)
+ return -1;
+#endif
+ if (readret > 0)
+ ret += readret;
+ }
+
+ DEBUG(10,("read_file (%s): pos = %.0f, size = %lu, returned %lu\n",
+ fsp->fsp_name, (double)pos, (unsigned long)n, (long)ret ));
+
+ fsp->pos += ret;
+ fsp->position_information = fsp->pos;
+
+ return(ret);
+}
+
+/* how many write cache buffers have been allocated */
+static unsigned int allocated_write_caches;
+
+/****************************************************************************
+ *Really* write to a file.
+****************************************************************************/
+
+static ssize_t real_write_file(files_struct *fsp,char *data,SMB_OFF_T pos, size_t n)
+{
+ ssize_t ret;
+
+ if (pos == -1)
+ ret = vfs_write_data(fsp, data, n);
+ else {
+ fsp->pos = pos;
+ ret = vfs_pwrite_data(fsp, data, n, pos);
+ }
+
+ DEBUG(10,("real_write_file (%s): pos = %.0f, size = %lu, returned %ld\n",
+ fsp->fsp_name, (double)pos, (unsigned long)n, (long)ret ));
+
+ if (ret != -1) {
+ fsp->pos += ret;
+
+/* Yes - this is correct - writes don't update this. JRA. */
+/* Found by Samba4 tests. */
+#if 0
+ fsp->position_information = fsp->pos;
+#endif
+ }
+
+ return ret;
+}
+
+/****************************************************************************
+write to a file
+****************************************************************************/
+
+ssize_t write_file(files_struct *fsp, char *data, SMB_OFF_T pos, size_t n)
+{
+ write_cache *wcp = fsp->wcp;
+ ssize_t total_written = 0;
+ int write_path = -1;
+
+ if (fsp->print_file) {
+ int snum;
+ uint32 jobid;
+
+ if (!rap_to_pjobid(fsp->rap_print_jobid, &snum, &jobid)) {
+ DEBUG(3,("write_file: Unable to map RAP jobid %u to jobid.\n",
+ (unsigned int)fsp->rap_print_jobid ));
+ errno = EBADF;
+ return -1;
+ }
+
+ return print_job_write(SNUM(fsp->conn), jobid, data, n);
+ }
+
+ if (!fsp->can_write) {
+ errno = EPERM;
+ return(0);
+ }
+
+ if (!fsp->modified) {
+ SMB_STRUCT_STAT st;
+ fsp->modified = True;
+
+ if (SMB_VFS_FSTAT(fsp,fsp->fd,&st) == 0) {
+ int dosmode = dos_mode(fsp->conn,fsp->fsp_name,&st);
+ fsp->size = (SMB_BIG_UINT)st.st_size;
+ if ((lp_store_dos_attributes(SNUM(fsp->conn)) || MAP_ARCHIVE(fsp->conn)) && !IS_DOS_ARCHIVE(dosmode)) {
+ file_set_dosmode(fsp->conn,fsp->fsp_name,dosmode | aARCH,&st);
+ }
+
+ /*
+ * If this is the first write and we have an exclusive oplock then setup
+ * the write cache.
+ */
+
+ if (EXCLUSIVE_OPLOCK_TYPE(fsp->oplock_type) && !wcp) {
+ setup_write_cache(fsp, st.st_size);
+ wcp = fsp->wcp;
+ }
+ }
+ }
+
+#ifdef WITH_PROFILE
+ DO_PROFILE_INC(writecache_total_writes);
+ if (!fsp->oplock_type) {
+ DO_PROFILE_INC(writecache_non_oplock_writes);
+ }
+#endif
+
+ /*
+ * If this file is level II oplocked then we need
+ * to grab the shared memory lock and inform all
+ * other files with a level II lock that they need
+ * to flush their read caches. We keep the lock over
+ * the shared memory area whilst doing this.
+ */
+
+ release_level_2_oplocks_on_change(fsp);
+
+#ifdef WITH_PROFILE
+ if (profile_p && profile_p->writecache_total_writes % 500 == 0) {
+ DEBUG(3,("WRITECACHE: initwrites=%u abutted=%u total=%u \
+nonop=%u allocated=%u active=%u direct=%u perfect=%u readhits=%u\n",
+ profile_p->writecache_init_writes,
+ profile_p->writecache_abutted_writes,
+ profile_p->writecache_total_writes,
+ profile_p->writecache_non_oplock_writes,
+ profile_p->writecache_allocated_write_caches,
+ profile_p->writecache_num_write_caches,
+ profile_p->writecache_direct_writes,
+ profile_p->writecache_num_perfect_writes,
+ profile_p->writecache_read_hits ));
+
+ DEBUG(3,("WRITECACHE: Flushes SEEK=%d, READ=%d, WRITE=%d, READRAW=%d, OPLOCK=%d, CLOSE=%d, SYNC=%d\n",
+ profile_p->writecache_flushed_writes[SEEK_FLUSH],
+ profile_p->writecache_flushed_writes[READ_FLUSH],
+ profile_p->writecache_flushed_writes[WRITE_FLUSH],
+ profile_p->writecache_flushed_writes[READRAW_FLUSH],
+ profile_p->writecache_flushed_writes[OPLOCK_RELEASE_FLUSH],
+ profile_p->writecache_flushed_writes[CLOSE_FLUSH],
+ profile_p->writecache_flushed_writes[SYNC_FLUSH] ));
+ }
+#endif
+
+ if(!wcp) {
+ DO_PROFILE_INC(writecache_direct_writes);
+ total_written = real_write_file(fsp, data, pos, n);
+ if ((total_written != -1) && (pos + total_written > (SMB_OFF_T)fsp->size))
+ fsp->size = (SMB_BIG_UINT)(pos + total_written);
+ return total_written;
+ }
+
+ DEBUG(9,("write_file (%s)(fd=%d pos=%.0f size=%u) wcp->offset=%.0f wcp->data_size=%u\n",
+ fsp->fsp_name, fsp->fd, (double)pos, (unsigned int)n, (double)wcp->offset, (unsigned int)wcp->data_size));
+
+ fsp->pos = pos + n;
+
+ /*
+ * If we have active cache and it isn't contiguous then we flush.
+ * NOTE: There is a small problem with running out of disk ....
+ */
+
+ if (wcp->data_size) {
+
+ BOOL cache_flush_needed = False;
+
+ if ((pos >= wcp->offset) && (pos <= wcp->offset + wcp->data_size)) {
+
+ /* ASCII art.... JRA.
+
+ +--------------+-----
+ | Cached data | Rest of allocated cache buffer....
+ +--------------+-----
+
+ +-------------------+
+ | Data to write |
+ +-------------------+
+
+ */
+
+ /*
+ * Start of write overlaps or abutts the existing data.
+ */
+
+ size_t data_used = MIN((wcp->alloc_size - (pos - wcp->offset)), n);
+
+ memcpy(wcp->data + (pos - wcp->offset), data, data_used);
+
+ /*
+ * Update the current buffer size with the new data.
+ */
+
+ if(pos + data_used > wcp->offset + wcp->data_size)
+ wcp->data_size = pos + data_used - wcp->offset;
+
+ /*
+ * Update the file size if changed.
+ */
+
+ if (wcp->offset + wcp->data_size > wcp->file_size) {
+ wcp->file_size = wcp->offset + wcp->data_size;
+ fsp->size = (SMB_BIG_UINT)wcp->file_size;
+ }
+
+ /*
+ * If we used all the data then
+ * return here.
+ */
+
+ if(n == data_used)
+ return n;
+ else
+ cache_flush_needed = True;
+
+ /*
+ * Move the start of data forward by the amount used,
+ * cut down the amount left by the same amount.
+ */
+
+ data += data_used;
+ pos += data_used;
+ n -= data_used;
+
+ DO_PROFILE_INC(writecache_abutted_writes);
+ total_written = data_used;
+
+ write_path = 1;
+
+ } else if ((pos < wcp->offset) && (pos + n > wcp->offset) &&
+ (pos + n <= wcp->offset + wcp->alloc_size)) {
+
+ /* ASCII art.... JRA.
+
+ +---------------+
+ | Cache buffer |
+ +---------------+
+
+ +-------------------+
+ | Data to write |
+ +-------------------+
+
+ */
+
+ /*
+ * End of write overlaps the existing data.
+ */
+
+ size_t data_used = pos + n - wcp->offset;
+
+ memcpy(wcp->data, data + n - data_used, data_used);
+
+ /*
+ * Update the current buffer size with the new data.
+ */
+
+ if(pos + n > wcp->offset + wcp->data_size)
+ wcp->data_size = pos + n - wcp->offset;
+
+ /*
+ * Update the file size if changed.
+ */
+
+ if (wcp->offset + wcp->data_size > wcp->file_size) {
+ wcp->file_size = wcp->offset + wcp->data_size;
+ fsp->size = (SMB_BIG_UINT)wcp->file_size;
+ }
+
+ /*
+ * We don't need to move the start of data, but we
+ * cut down the amount left by the amount used.
+ */
+
+ n -= data_used;
+
+ /*
+ * We cannot have used all the data here.
+ */
+
+ cache_flush_needed = True;
+
+ DO_PROFILE_INC(writecache_abutted_writes);
+ total_written = data_used;
+
+ write_path = 2;
+
+ } else if ( (pos >= wcp->file_size) &&
+ (wcp->offset + wcp->data_size == wcp->file_size) &&
+ (pos > wcp->offset + wcp->data_size) &&
+ (pos < wcp->offset + wcp->alloc_size) ) {
+
+ /* ASCII art.... JRA.
+
+ End of file ---->|
+
+ +---------------+---------------+
+ | Cached data | Cache buffer |
+ +---------------+---------------+
+
+ +-------------------+
+ | Data to write |
+ +-------------------+
+
+ */
+
+ /*
+ * Non-contiguous write part of which fits within
+ * the cache buffer and is extending the file
+ * and the cache contents reflect the current
+ * data up to the current end of the file.
+ */
+
+ size_t data_used;
+
+ if(pos + n <= wcp->offset + wcp->alloc_size)
+ data_used = n;
+ else
+ data_used = wcp->offset + wcp->alloc_size - pos;
+
+ /*
+ * Fill in the non-continuous area with zeros.
+ */
+
+ memset(wcp->data + wcp->data_size, '\0',
+ pos - (wcp->offset + wcp->data_size) );
+
+ memcpy(wcp->data + (pos - wcp->offset), data, data_used);
+
+ /*
+ * Update the current buffer size with the new data.
+ */
+
+ if(pos + data_used > wcp->offset + wcp->data_size)
+ wcp->data_size = pos + data_used - wcp->offset;
+
+ /*
+ * Update the file size if changed.
+ */
+
+ if (wcp->offset + wcp->data_size > wcp->file_size) {
+ wcp->file_size = wcp->offset + wcp->data_size;
+ fsp->size = (SMB_BIG_UINT)wcp->file_size;
+ }
+
+ /*
+ * If we used all the data then
+ * return here.
+ */
+
+ if(n == data_used)
+ return n;
+ else
+ cache_flush_needed = True;
+
+ /*
+ * Move the start of data forward by the amount used,
+ * cut down the amount left by the same amount.
+ */
+
+ data += data_used;
+ pos += data_used;
+ n -= data_used;
+
+ DO_PROFILE_INC(writecache_abutted_writes);
+ total_written = data_used;
+
+ write_path = 3;
+
+ } else {
+
+ /* ASCII art..... JRA.
+
+ Case 1).
+
+ +---------------+---------------+
+ | Cached data | Cache buffer |
+ +---------------+---------------+
+
+ +-------------------+
+ | Data to write |
+ +-------------------+
+
+ Case 2).
+
+ +---------------+---------------+
+ | Cached data | Cache buffer |
+ +---------------+---------------+
+
+ +-------------------+
+ | Data to write |
+ +-------------------+
+
+ Case 3).
+
+ +---------------+---------------+
+ | Cached data | Cache buffer |
+ +---------------+---------------+
+
+ +-----------------------------------------------------+
+ | Data to write |
+ +-----------------------------------------------------+
+
+ */
+
+ /*
+ * Write is bigger than buffer, or there is no overlap on the
+ * low or high ends.
+ */
+
+ DEBUG(9,("write_file: non cacheable write : fd = %d, pos = %.0f, len = %u, current cache pos = %.0f \
+len = %u\n",fsp->fd, (double)pos, (unsigned int)n, (double)wcp->offset, (unsigned int)wcp->data_size ));
+
+ /*
+ * Update the file size if needed.
+ */
+
+ if(pos + n > wcp->file_size) {
+ wcp->file_size = pos + n;
+ fsp->size = (SMB_BIG_UINT)wcp->file_size;
+ }
+
+ /*
+ * If write would fit in the cache, and is larger than
+ * the data already in the cache, flush the cache and
+ * preferentially copy the data new data into it. Otherwise
+ * just write the data directly.
+ */
+
+ if ( n <= wcp->alloc_size && n > wcp->data_size) {
+ cache_flush_needed = True;
+ } else {
+ ssize_t ret = real_write_file(fsp, data, pos, n);
+
+ /*
+ * If the write overlaps the entire cache, then
+ * discard the current contents of the cache.
+ * Fix from Rasmus Borup Hansen rbh@math.ku.dk.
+ */
+
+ if ((pos <= wcp->offset) &&
+ (pos + n >= wcp->offset + wcp->data_size) ) {
+ DEBUG(9,("write_file: discarding overwritten write \
+cache: fd = %d, off=%.0f, size=%u\n", fsp->fd, (double)wcp->offset, (unsigned int)wcp->data_size ));
+ wcp->data_size = 0;
+ }
+
+ DO_PROFILE_INC(writecache_direct_writes);
+ if (ret == -1)
+ return ret;
+
+ if (pos + ret > wcp->file_size) {
+ wcp->file_size = pos + ret;
+ fsp->size = (SMB_BIG_UINT)wcp->file_size;
+ }
+
+ return ret;
+ }
+
+ write_path = 4;
+
+ }
+
+ if(wcp->data_size > wcp->file_size) {
+ wcp->file_size = wcp->data_size;
+ fsp->size = (SMB_BIG_UINT)wcp->file_size;
+ }
+
+ if (cache_flush_needed) {
+ DEBUG(3,("WRITE_FLUSH:%d: due to noncontinuous write: fd = %d, size = %.0f, pos = %.0f, \
+n = %u, wcp->offset=%.0f, wcp->data_size=%u\n",
+ write_path, fsp->fd, (double)wcp->file_size, (double)pos, (unsigned int)n,
+ (double)wcp->offset, (unsigned int)wcp->data_size ));
+
+ flush_write_cache(fsp, WRITE_FLUSH);
+ }
+ }
+
+ /*
+ * If the write request is bigger than the cache
+ * size, write it all out.
+ */
+
+ if (n > wcp->alloc_size ) {
+ ssize_t ret = real_write_file(fsp, data, pos, n);
+ if (ret == -1)
+ return -1;
+
+ if (pos + ret > wcp->file_size) {
+ wcp->file_size = pos + n;
+ fsp->size = (SMB_BIG_UINT)wcp->file_size;
+ }
+
+ DO_PROFILE_INC(writecache_direct_writes);
+ return total_written + n;
+ }
+
+ /*
+ * If there's any data left, cache it.
+ */
+
+ if (n) {
+#ifdef WITH_PROFILE
+ if (wcp->data_size) {
+ DO_PROFILE_INC(writecache_abutted_writes);
+ } else {
+ DO_PROFILE_INC(writecache_init_writes);
+ }
+#endif
+ memcpy(wcp->data+wcp->data_size, data, n);
+ if (wcp->data_size == 0) {
+ wcp->offset = pos;
+ DO_PROFILE_INC(writecache_num_write_caches);
+ }
+ wcp->data_size += n;
+
+ /*
+ * Update the file size if changed.
+ */
+
+ if (wcp->offset + wcp->data_size > wcp->file_size) {
+ wcp->file_size = wcp->offset + wcp->data_size;
+ fsp->size = (SMB_BIG_UINT)wcp->file_size;
+ }
+ DEBUG(9,("wcp->offset = %.0f wcp->data_size = %u cache return %u\n",
+ (double)wcp->offset, (unsigned int)wcp->data_size, (unsigned int)n));
+
+ total_written += n;
+ return total_written; /* .... that's a write :) */
+ }
+
+ return total_written;
+}
+
+/****************************************************************************
+ Delete the write cache structure.
+****************************************************************************/
+
+void delete_write_cache(files_struct *fsp)
+{
+ write_cache *wcp;
+
+ if(!fsp)
+ return;
+
+ if(!(wcp = fsp->wcp))
+ return;
+
+ DO_PROFILE_DEC(writecache_allocated_write_caches);
+ allocated_write_caches--;
+
+ SMB_ASSERT(wcp->data_size == 0);
+
+ SAFE_FREE(wcp->data);
+ SAFE_FREE(fsp->wcp);
+
+ DEBUG(10,("delete_write_cache: File %s deleted write cache\n", fsp->fsp_name ));
+}
+
+/****************************************************************************
+ Setup the write cache structure.
+****************************************************************************/
+
+static BOOL setup_write_cache(files_struct *fsp, SMB_OFF_T file_size)
+{
+ ssize_t alloc_size = lp_write_cache_size(SNUM(fsp->conn));
+ write_cache *wcp;
+
+ if (allocated_write_caches >= MAX_WRITE_CACHES)
+ return False;
+
+ if(alloc_size == 0 || fsp->wcp)
+ return False;
+
+ if((wcp = (write_cache *)malloc(sizeof(write_cache))) == NULL) {
+ DEBUG(0,("setup_write_cache: malloc fail.\n"));
+ return False;
+ }
+
+ wcp->file_size = file_size;
+ wcp->offset = 0;
+ wcp->alloc_size = alloc_size;
+ wcp->data_size = 0;
+ if((wcp->data = malloc(wcp->alloc_size)) == NULL) {
+ DEBUG(0,("setup_write_cache: malloc fail for buffer size %u.\n",
+ (unsigned int)wcp->alloc_size ));
+ SAFE_FREE(wcp);
+ return False;
+ }
+
+ memset(wcp->data, '\0', wcp->alloc_size );
+
+ fsp->wcp = wcp;
+ DO_PROFILE_INC(writecache_allocated_write_caches);
+ allocated_write_caches++;
+
+ DEBUG(10,("setup_write_cache: File %s allocated write cache size %lu\n",
+ fsp->fsp_name, (unsigned long)wcp->alloc_size ));
+
+ return True;
+}
+
+/****************************************************************************
+ Cope with a size change.
+****************************************************************************/
+
+void set_filelen_write_cache(files_struct *fsp, SMB_OFF_T file_size)
+{
+ fsp->size = (SMB_BIG_UINT)file_size;
+ if(fsp->wcp) {
+ /* The cache *must* have been flushed before we do this. */
+ if (fsp->wcp->data_size != 0) {
+ pstring msg;
+ slprintf(msg, sizeof(msg)-1, "set_filelen_write_cache: size change \
+on file %s with write cache size = %lu\n", fsp->fsp_name, (unsigned long)fsp->wcp->data_size );
+ smb_panic(msg);
+ }
+ fsp->wcp->file_size = file_size;
+ }
+}
+
+/*******************************************************************
+ Flush a write cache struct to disk.
+********************************************************************/
+
+ssize_t flush_write_cache(files_struct *fsp, enum flush_reason_enum reason)
+{
+ write_cache *wcp = fsp->wcp;
+ size_t data_size;
+ ssize_t ret;
+
+ if(!wcp || !wcp->data_size)
+ return 0;
+
+ data_size = wcp->data_size;
+ wcp->data_size = 0;
+
+ DO_PROFILE_DEC_INC(writecache_num_write_caches,writecache_flushed_writes[reason]);
+
+ DEBUG(9,("flushing write cache: fd = %d, off=%.0f, size=%u\n",
+ fsp->fd, (double)wcp->offset, (unsigned int)data_size));
+
+#ifdef WITH_PROFILE
+ if(data_size == wcp->alloc_size)
+ DO_PROFILE_INC(writecache_num_perfect_writes);
+#endif
+
+ ret = real_write_file(fsp, wcp->data, wcp->offset, data_size);
+
+ /*
+ * Ensure file size if kept up to date if write extends file.
+ */
+
+ if ((ret != -1) && (wcp->offset + ret > wcp->file_size))
+ wcp->file_size = wcp->offset + ret;
+
+ return ret;
+}
+
+/*******************************************************************
+sync a file
+********************************************************************/
+
+void sync_file(connection_struct *conn, files_struct *fsp)
+{
+ if(lp_strict_sync(SNUM(conn)) && fsp->fd != -1) {
+ flush_write_cache(fsp, SYNC_FLUSH);
+ SMB_VFS_FSYNC(fsp,fsp->fd);
+ }
+}
+
+
+/************************************************************
+ Perform a stat whether a valid fd or not.
+************************************************************/
+
+int fsp_stat(files_struct *fsp, SMB_STRUCT_STAT *pst)
+{
+ if (fsp->fd == -1)
+ return SMB_VFS_STAT(fsp->conn, fsp->fsp_name, pst);
+ else
+ return SMB_VFS_FSTAT(fsp,fsp->fd, pst);
+}
diff --git a/source/smbd/filename.c b/source/smbd/filename.c
new file mode 100644
index 00000000000..805af9c494a
--- /dev/null
+++ b/source/smbd/filename.c
@@ -0,0 +1,497 @@
+/*
+ Unix SMB/CIFS implementation.
+ filename handling routines
+ Copyright (C) Andrew Tridgell 1992-1998
+ Copyright (C) Jeremy Allison 1999-2004
+ Copyright (C) Ying Chen 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 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.
+*/
+
+/*
+ * New hash table stat cache code added by Ying Chen.
+ */
+
+#include "includes.h"
+
+extern BOOL case_sensitive;
+extern BOOL case_preserve;
+extern BOOL short_case_preserve;
+extern BOOL use_mangled_map;
+
+static BOOL scan_directory(const char *path, char *name,size_t maxlength,
+ connection_struct *conn,BOOL docache);
+
+/****************************************************************************
+ Check if two filenames are equal.
+ This needs to be careful about whether we are case sensitive.
+****************************************************************************/
+
+static BOOL fname_equal(const char *name1, const char *name2)
+{
+ /* Normal filename handling */
+ if (case_sensitive)
+ return(strcmp(name1,name2) == 0);
+
+ return(strequal(name1,name2));
+}
+
+/****************************************************************************
+ Mangle the 2nd name and check if it is then equal to the first name.
+****************************************************************************/
+
+static BOOL mangled_equal(const char *name1, const char *name2, int snum)
+{
+ pstring tmpname;
+
+ pstrcpy(tmpname, name2);
+ mangle_map(tmpname, True, False, snum);
+ return strequal(name1, tmpname);
+}
+
+/****************************************************************************
+This routine is called to convert names from the dos namespace to unix
+namespace. It needs to handle any case conversions, mangling, format
+changes etc.
+
+We assume that we have already done a chdir() to the right "root" directory
+for this service.
+
+The function will return False if some part of the name except for the last
+part cannot be resolved
+
+If the saved_last_component != 0, then the unmodified last component
+of the pathname is returned there. This is used in an exceptional
+case in reply_mv (so far). If saved_last_component == 0 then nothing
+is returned there.
+
+The bad_path arg is set to True if the filename walk failed. This is
+used to pick the correct error code to return between ENOENT and ENOTDIR
+as Windows applications depend on ERRbadpath being returned if a component
+of a pathname does not exist.
+
+On exit from unix_convert, if *pst was not null, then the file stat
+struct will be returned if the file exists and was found, if not this
+stat struct will be filled with zeros (and this can be detected by checking
+for nlinks = 0, which can never be true for any file).
+****************************************************************************/
+
+BOOL unix_convert(pstring name,connection_struct *conn,char *saved_last_component,
+ BOOL *bad_path, SMB_STRUCT_STAT *pst)
+{
+ SMB_STRUCT_STAT st;
+ char *start, *end;
+ pstring dirpath;
+ pstring orig_path;
+ BOOL component_was_mangled = False;
+ BOOL name_has_wildcard = False;
+
+ ZERO_STRUCTP(pst);
+
+ *dirpath = 0;
+ *bad_path = False;
+ if(saved_last_component)
+ *saved_last_component = 0;
+
+ if (conn->printer) {
+ /* we don't ever use the filenames on a printer share as a
+ filename - so don't convert them */
+ return True;
+ }
+
+ DEBUG(5, ("unix_convert called on file \"%s\"\n", name));
+
+ /*
+ * Conversion to basic unix format is already done in check_path_syntax().
+ */
+
+ /*
+ * Names must be relative to the root of the service - any leading /.
+ * and trailing /'s should have been trimmed by check_path_syntax().
+ */
+
+#ifdef DEVELOPER
+ SMB_ASSERT(*name != '/');
+#endif
+
+ /*
+ * If we trimmed down to a single '\0' character
+ * then we should use the "." directory to avoid
+ * searching the cache, but not if we are in a
+ * printing share.
+ * As we know this is valid we can return true here.
+ */
+
+ if (!*name) {
+ name[0] = '.';
+ name[1] = '\0';
+ return(True);
+ }
+
+ /*
+ * Ensure saved_last_component is valid even if file exists.
+ */
+
+ if(saved_last_component) {
+ end = strrchr_m(name, '/');
+ if(end)
+ pstrcpy(saved_last_component, end + 1);
+ else
+ pstrcpy(saved_last_component, name);
+ }
+
+ if (!case_sensitive && (!case_preserve || (mangle_is_8_3(name, False) && !short_case_preserve)))
+ strnorm(name);
+
+ start = name;
+ pstrcpy(orig_path, name);
+
+ if(!case_sensitive && stat_cache_lookup(conn, name, dirpath, &start, &st)) {
+ *pst = st;
+ return True;
+ }
+
+ /*
+ * stat the name - if it exists then we are all done!
+ */
+
+ if (SMB_VFS_STAT(conn,name,&st) == 0) {
+ stat_cache_add(orig_path, name);
+ DEBUG(5,("conversion finished %s -> %s\n",orig_path, name));
+ *pst = st;
+ return(True);
+ }
+
+ DEBUG(5,("unix_convert begin: name = %s, dirpath = %s, start = %s\n", name, dirpath, start));
+
+ /*
+ * A special case - if we don't have any mangling chars and are case
+ * sensitive then searching won't help.
+ */
+
+ if (case_sensitive && !mangle_is_mangled(name) && !use_mangled_map)
+ return(False);
+
+ name_has_wildcard = ms_has_wild(start);
+
+ /*
+ * is_mangled() was changed to look at an entire pathname, not
+ * just a component. JRA.
+ */
+
+ if (mangle_is_mangled(start))
+ component_was_mangled = True;
+
+ /*
+ * Now we need to recursively match the name against the real
+ * directory structure.
+ */
+
+ /*
+ * Match each part of the path name separately, trying the names
+ * as is first, then trying to scan the directory for matching names.
+ */
+
+ for (; start ; start = (end?end+1:(char *)NULL)) {
+ /*
+ * Pinpoint the end of this section of the filename.
+ */
+ end = strchr_m(start, '/');
+
+ /*
+ * Chop the name at this point.
+ */
+ if (end)
+ *end = 0;
+
+ if(saved_last_component != 0)
+ pstrcpy(saved_last_component, end ? end + 1 : start);
+
+ /*
+ * Check if the name exists up to this point.
+ */
+
+ if (SMB_VFS_STAT(conn,name, &st) == 0) {
+ /*
+ * It exists. it must either be a directory or this must be
+ * the last part of the path for it to be OK.
+ */
+ if (end && !(st.st_mode & S_IFDIR)) {
+ /*
+ * An intermediate part of the name isn't a directory.
+ */
+ DEBUG(5,("Not a dir %s\n",start));
+ *end = '/';
+ return(False);
+ }
+
+ if (!end) {
+ /*
+ * We just scanned for, and found the end of the path.
+ * We must return the valid stat struct.
+ * JRA.
+ */
+
+ *pst = st;
+ }
+
+ } else {
+ pstring rest;
+
+ /* Stat failed - ensure we don't use it. */
+ ZERO_STRUCT(st);
+ *rest = 0;
+
+ /*
+ * Remember the rest of the pathname so it can be restored
+ * later.
+ */
+
+ if (end)
+ pstrcpy(rest,end+1);
+
+ /*
+ * Try to find this part of the path in the directory.
+ */
+
+ if (ms_has_wild(start) ||
+ !scan_directory(dirpath, start,
+ sizeof(pstring) - 1 - (start - name),
+ conn,
+ end?True:False)) {
+ if (end) {
+ /*
+ * An intermediate part of the name can't be found.
+ */
+ DEBUG(5,("Intermediate not found %s\n",start));
+ *end = '/';
+
+ /*
+ * We need to return the fact that the intermediate
+ * name resolution failed. This is used to return an
+ * error of ERRbadpath rather than ERRbadfile. Some
+ * Windows applications depend on the difference between
+ * these two errors.
+ */
+ *bad_path = True;
+ return(False);
+ }
+
+ /*
+ * Just the last part of the name doesn't exist.
+ * We may need to strupper() or strlower() it in case
+ * this conversion is being used for file creation
+ * purposes. If the filename is of mixed case then
+ * don't normalise it.
+ */
+
+ if (!case_preserve && (!strhasupper(start) || !strhaslower(start)))
+ strnorm(start);
+
+ /*
+ * check on the mangled stack to see if we can recover the
+ * base of the filename.
+ */
+
+ if (mangle_is_mangled(start)) {
+ mangle_check_cache( start );
+ }
+
+ DEBUG(5,("New file %s\n",start));
+ return(True);
+ }
+
+ /*
+ * Restore the rest of the string. If the string was mangled the size
+ * may have changed.
+ */
+ if (end) {
+ end = start + strlen(start);
+ if (!safe_strcat(start, "/", sizeof(pstring) - 1 - (start - name)) ||
+ !safe_strcat(start, rest, sizeof(pstring) - 1 - (start - name))) {
+ return False;
+ }
+ *end = '\0';
+ } else {
+ /*
+ * We just scanned for, and found the end of the path.
+ * We must return a valid stat struct if it exists.
+ * JRA.
+ */
+
+ if (SMB_VFS_STAT(conn,name, &st) == 0) {
+ *pst = st;
+ } else {
+ ZERO_STRUCT(st);
+ }
+ }
+ } /* end else */
+
+ /*
+ * Add to the dirpath that we have resolved so far.
+ */
+ if (*dirpath)
+ pstrcat(dirpath,"/");
+
+ pstrcat(dirpath,start);
+
+ /*
+ * Don't cache a name with mangled or wildcard components
+ * as this can change the size.
+ */
+
+ if(!component_was_mangled && !name_has_wildcard)
+ stat_cache_add(orig_path, dirpath);
+
+ /*
+ * Restore the / that we wiped out earlier.
+ */
+ if (end)
+ *end = '/';
+ }
+
+ /*
+ * Don't cache a name with mangled or wildcard components
+ * as this can change the size.
+ */
+
+ if(!component_was_mangled && !name_has_wildcard)
+ stat_cache_add(orig_path, name);
+
+ /*
+ * The name has been resolved.
+ */
+
+ DEBUG(5,("conversion finished %s -> %s\n",orig_path, name));
+ return(True);
+}
+
+/****************************************************************************
+ Check a filename - possibly caling reducename.
+ This is called by every routine before it allows an operation on a filename.
+ It does any final confirmation necessary to ensure that the filename is
+ a valid one for the user to access.
+****************************************************************************/
+
+BOOL check_name(pstring name,connection_struct *conn)
+{
+ BOOL ret = True;
+
+ errno = 0;
+
+ if (IS_VETO_PATH(conn, name)) {
+ /* Is it not dot or dot dot. */
+ if (!((name[0] == '.') && (!name[1] || (name[1] == '.' && !name[2])))) {
+ DEBUG(5,("file path name %s vetoed\n",name));
+ return False;
+ }
+ }
+
+ if (!lp_widelinks(SNUM(conn))) {
+ ret = reduce_name(conn,name,conn->connectpath);
+ }
+
+ /* Check if we are allowing users to follow symlinks */
+ /* Patch from David Clerc <David.Clerc@cui.unige.ch>
+ University of Geneva */
+
+#ifdef S_ISLNK
+ if (!lp_symlinks(SNUM(conn))) {
+ SMB_STRUCT_STAT statbuf;
+ if ( (SMB_VFS_LSTAT(conn,name,&statbuf) != -1) &&
+ (S_ISLNK(statbuf.st_mode)) ) {
+ DEBUG(3,("check_name: denied: file path name %s is a symlink\n",name));
+ ret = False;
+ }
+ }
+#endif
+
+ if (!ret)
+ DEBUG(5,("check_name on %s failed\n",name));
+
+ return(ret);
+}
+
+/****************************************************************************
+ Scan a directory to find a filename, matching without case sensitivity.
+ If the name looks like a mangled name then try via the mangling functions
+****************************************************************************/
+
+static BOOL scan_directory(const char *path, char *name, size_t maxlength,
+ connection_struct *conn,BOOL docache)
+{
+ void *cur_dir;
+ const char *dname;
+ BOOL mangled;
+
+ mangled = mangle_is_mangled(name);
+
+ /* handle null paths */
+ if (*path == 0)
+ path = ".";
+
+ if (docache && (dname = DirCacheCheck(path,name,SNUM(conn)))) {
+ safe_strcpy(name, dname, maxlength);
+ return(True);
+ }
+
+ /*
+ * The incoming name can be mangled, and if we de-mangle it
+ * here it will not compare correctly against the filename (name2)
+ * read from the directory and then mangled by the mangle_map()
+ * call. We need to mangle both names or neither.
+ * (JRA).
+ */
+ if (mangled)
+ mangled = !mangle_check_cache( name );
+
+ /* open the directory */
+ if (!(cur_dir = OpenDir(conn, path, True))) {
+ DEBUG(3,("scan dir didn't open dir [%s]\n",path));
+ return(False);
+ }
+
+ /* now scan for matching names */
+ while ((dname = ReadDirName(cur_dir))) {
+
+ /* Is it dot or dot dot. */
+ if ((dname[0] == '.') && (!dname[1] || (dname[1] == '.' && !dname[2]))) {
+ continue;
+ }
+
+ /*
+ * At this point dname is the unmangled name.
+ * name is either mangled or not, depending on the state of the "mangled"
+ * variable. JRA.
+ */
+
+ /*
+ * Check mangled name against mangled name, or unmangled name
+ * against unmangled name.
+ */
+
+ if ((mangled && mangled_equal(name,dname,SNUM(conn))) || fname_equal(name, dname)) {
+ /* we've found the file, change it's name and return */
+ if (docache)
+ DirCacheAdd(path,name,dname,SNUM(conn));
+ safe_strcpy(name, dname, maxlength);
+ CloseDir(cur_dir);
+ return(True);
+ }
+ }
+
+ CloseDir(cur_dir);
+ return(False);
+}
diff --git a/source/smbd/files.c b/source/smbd/files.c
new file mode 100644
index 00000000000..80544c9a309
--- /dev/null
+++ b/source/smbd/files.c
@@ -0,0 +1,449 @@
+/*
+ Unix SMB/CIFS implementation.
+ Files[] structure 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 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"
+
+static int real_max_open_files;
+
+#define VALID_FNUM(fnum) (((fnum) >= 0) && ((fnum) < real_max_open_files))
+
+#define FILE_HANDLE_OFFSET 0x1000
+
+static struct bitmap *file_bmap;
+
+static files_struct *Files;
+
+/* a fsp to use when chaining */
+static files_struct *chain_fsp = NULL;
+/* a fsp to use to save when breaking an oplock. */
+static files_struct *oplock_save_chain_fsp = NULL;
+
+static int files_used;
+
+/****************************************************************************
+ Return a unique number identifying this fsp over the life of this pid.
+****************************************************************************/
+
+static unsigned long get_gen_count(void)
+{
+ static unsigned long file_gen_counter;
+
+ if ((++file_gen_counter) == 0)
+ return ++file_gen_counter;
+ return file_gen_counter;
+}
+
+/****************************************************************************
+ Find first available file slot.
+****************************************************************************/
+
+files_struct *file_new(connection_struct *conn)
+{
+ int i;
+ static int first_file;
+ files_struct *fsp, *next;
+
+ /* we want to give out file handles differently on each new
+ connection because of a common bug in MS clients where they try to
+ reuse a file descriptor from an earlier smb connection. This code
+ increases the chance that the errant client will get an error rather
+ than causing corruption */
+ if (first_file == 0) {
+ first_file = (sys_getpid() ^ (int)time(NULL)) % real_max_open_files;
+ }
+
+ i = bitmap_find(file_bmap, first_file);
+ if (i == -1) {
+ /*
+ * Before we give up, go through the open files
+ * and see if there are any files opened with a
+ * batch oplock. If so break the oplock and then
+ * re-use that entry (if it becomes closed).
+ * This may help as NT/95 clients tend to keep
+ * files batch oplocked for quite a long time
+ * after they have finished with them.
+ */
+ for (fsp=Files;fsp;fsp=next) {
+ next=fsp->next;
+ if (attempt_close_oplocked_file(fsp)) {
+ return file_new(conn);
+ }
+ }
+
+ DEBUG(0,("ERROR! Out of file structures\n"));
+ unix_ERR_class = ERRSRV;
+ unix_ERR_code = ERRnofids;
+ return NULL;
+ }
+
+ fsp = (files_struct *)malloc(sizeof(*fsp));
+ if (!fsp) {
+ unix_ERR_class = ERRSRV;
+ unix_ERR_code = ERRnofids;
+ return NULL;
+ }
+
+ ZERO_STRUCTP(fsp);
+ fsp->fd = -1;
+ fsp->conn = conn;
+ fsp->file_id = get_gen_count();
+ GetTimeOfDay(&fsp->open_time);
+
+ first_file = (i+1) % real_max_open_files;
+
+ bitmap_set(file_bmap, i);
+ files_used++;
+
+ fsp->fnum = i + FILE_HANDLE_OFFSET;
+ SMB_ASSERT(fsp->fnum < 65536);
+
+ string_set(&fsp->fsp_name,"");
+
+ DLIST_ADD(Files, fsp);
+
+ DEBUG(5,("allocated file structure %d, fnum = %d (%d used)\n",
+ i, fsp->fnum, files_used));
+
+ chain_fsp = fsp;
+
+ return fsp;
+}
+
+/****************************************************************************
+ Close all open files for a connection.
+****************************************************************************/
+
+void file_close_conn(connection_struct *conn)
+{
+ files_struct *fsp, *next;
+
+ for (fsp=Files;fsp;fsp=next) {
+ next = fsp->next;
+ if (fsp->conn == conn) {
+ close_file(fsp,False);
+ }
+ }
+}
+
+/****************************************************************************
+ Close all open files for a pid.
+****************************************************************************/
+
+void file_close_pid(uint16 smbpid)
+{
+ files_struct *fsp, *next;
+
+ for (fsp=Files;fsp;fsp=next) {
+ next = fsp->next;
+ if (fsp->file_pid == smbpid) {
+ close_file(fsp,False);
+ }
+ }
+}
+
+/****************************************************************************
+ Initialise file structures.
+****************************************************************************/
+
+#define MAX_OPEN_FUDGEFACTOR 20
+
+void file_init(void)
+{
+ int request_max_open_files = lp_max_open_files();
+ int real_lim;
+
+ /*
+ * Set the max_open files to be the requested
+ * max plus a fudgefactor to allow for the extra
+ * fd's we need such as log files etc...
+ */
+ real_lim = set_maxfiles(request_max_open_files + MAX_OPEN_FUDGEFACTOR);
+
+ real_max_open_files = real_lim - MAX_OPEN_FUDGEFACTOR;
+
+ if (real_max_open_files + FILE_HANDLE_OFFSET + MAX_OPEN_PIPES > 65536)
+ real_max_open_files = 65536 - FILE_HANDLE_OFFSET - MAX_OPEN_PIPES;
+
+ if(real_max_open_files != request_max_open_files) {
+ DEBUG(1,("file_init: Information only: requested %d \
+open files, %d are available.\n", request_max_open_files, real_max_open_files));
+ }
+
+ SMB_ASSERT(real_max_open_files > 100);
+
+ file_bmap = bitmap_allocate(real_max_open_files);
+
+ if (!file_bmap) {
+ exit_server("out of memory in file_init");
+ }
+
+ /*
+ * Ensure that pipe_handle_oppset is set correctly.
+ */
+ set_pipe_handle_offset(real_max_open_files);
+}
+
+/****************************************************************************
+ Close files open by a specified vuid.
+****************************************************************************/
+
+void file_close_user(int vuid)
+{
+ files_struct *fsp, *next;
+
+ for (fsp=Files;fsp;fsp=next) {
+ next=fsp->next;
+ if (fsp->vuid == vuid) {
+ close_file(fsp,False);
+ }
+ }
+}
+
+void file_dump_open_table(void)
+{
+ int count=0;
+ files_struct *fsp;
+
+ for (fsp=Files;fsp;fsp=fsp->next,count++) {
+ DEBUG(10,("Files[%d], fnum = %d, name %s, fd = %d, fileid = %lu, dev = %x, inode = %.0f\n",
+ count, fsp->fnum, fsp->fsp_name, fsp->fd, (unsigned long)fsp->file_id,
+ (unsigned int)fsp->dev, (double)fsp->inode ));
+ }
+}
+
+/****************************************************************************
+ Find a fsp given a file descriptor.
+****************************************************************************/
+
+files_struct *file_find_fd(int fd)
+{
+ int count=0;
+ files_struct *fsp;
+
+ for (fsp=Files;fsp;fsp=fsp->next,count++) {
+ if (fsp->fd == fd) {
+ if (count > 10) {
+ DLIST_PROMOTE(Files, fsp);
+ }
+ return fsp;
+ }
+ }
+
+ return NULL;
+}
+
+/****************************************************************************
+ Find a fsp given a device, inode and file_id.
+****************************************************************************/
+
+files_struct *file_find_dif(SMB_DEV_T dev, SMB_INO_T inode, unsigned long file_id)
+{
+ int count=0;
+ files_struct *fsp;
+
+ for (fsp=Files;fsp;fsp=fsp->next,count++) {
+ /* We can have a fsp->fd == -1 here as it could be a stat open. */
+ if (fsp->dev == dev &&
+ fsp->inode == inode &&
+ fsp->file_id == file_id ) {
+ if (count > 10) {
+ DLIST_PROMOTE(Files, fsp);
+ }
+ /* Paranoia check. */
+ if (fsp->fd == -1 && fsp->oplock_type != NO_OPLOCK) {
+ DEBUG(0,("file_find_dif: file %s dev = %x, inode = %.0f, file_id = %u \
+oplock_type = %u is a stat open with oplock type !\n", fsp->fsp_name, (unsigned int)fsp->dev,
+ (double)fsp->inode, (unsigned int)fsp->file_id,
+ (unsigned int)fsp->oplock_type ));
+ smb_panic("file_find_dif\n");
+ }
+ return fsp;
+ }
+ }
+
+ return NULL;
+}
+
+/****************************************************************************
+ Check if an fsp still exists.
+****************************************************************************/
+
+files_struct *file_find_fsp(files_struct *orig_fsp)
+{
+ files_struct *fsp;
+
+ for (fsp=Files;fsp;fsp=fsp->next) {
+ if (fsp == orig_fsp)
+ return fsp;
+ }
+
+ return NULL;
+}
+
+/****************************************************************************
+ Find the first fsp given a device and inode.
+****************************************************************************/
+
+files_struct *file_find_di_first(SMB_DEV_T dev, SMB_INO_T inode)
+{
+ files_struct *fsp;
+
+ for (fsp=Files;fsp;fsp=fsp->next) {
+ if ( fsp->fd != -1 &&
+ fsp->dev == dev &&
+ fsp->inode == inode )
+ return fsp;
+ }
+
+ return NULL;
+}
+
+/****************************************************************************
+ Find the next fsp having the same device and inode.
+****************************************************************************/
+
+files_struct *file_find_di_next(files_struct *start_fsp)
+{
+ files_struct *fsp;
+
+ for (fsp = start_fsp->next;fsp;fsp=fsp->next) {
+ if ( fsp->fd != -1 &&
+ fsp->dev == start_fsp->dev &&
+ fsp->inode == start_fsp->inode )
+ return fsp;
+ }
+
+ return NULL;
+}
+
+/****************************************************************************
+ Find a fsp that is open for printing.
+****************************************************************************/
+
+files_struct *file_find_print(void)
+{
+ files_struct *fsp;
+
+ for (fsp=Files;fsp;fsp=fsp->next) {
+ if (fsp->print_file) return fsp;
+ }
+
+ return NULL;
+}
+
+/****************************************************************************
+ Sync open files on a connection.
+****************************************************************************/
+
+void file_sync_all(connection_struct *conn)
+{
+ files_struct *fsp, *next;
+
+ for (fsp=Files;fsp;fsp=next) {
+ next=fsp->next;
+ if ((conn == fsp->conn) && (fsp->fd != -1)) {
+ sync_file(conn,fsp);
+ }
+ }
+}
+
+/****************************************************************************
+ Free up a fsp.
+****************************************************************************/
+
+void file_free(files_struct *fsp)
+{
+ DLIST_REMOVE(Files, fsp);
+
+ string_free(&fsp->fsp_name);
+
+ if (fsp->fake_file_handle) {
+ destroy_fake_file_handle(&fsp->fake_file_handle);
+ }
+
+ bitmap_clear(file_bmap, fsp->fnum - FILE_HANDLE_OFFSET);
+ files_used--;
+
+ DEBUG(5,("freed files structure %d (%d used)\n",
+ fsp->fnum, files_used));
+
+ /* this is paranoia, just in case someone tries to reuse the
+ information */
+ ZERO_STRUCTP(fsp);
+
+ if (fsp == chain_fsp) chain_fsp = NULL;
+
+ SAFE_FREE(fsp);
+}
+
+/****************************************************************************
+ Get a fsp from a packet given the offset of a 16 bit fnum.
+****************************************************************************/
+
+files_struct *file_fsp(char *buf, int where)
+{
+ int fnum, count=0;
+ files_struct *fsp;
+
+ if (chain_fsp)
+ return chain_fsp;
+
+ if (!buf)
+ return NULL;
+ fnum = SVAL(buf, where);
+
+ for (fsp=Files;fsp;fsp=fsp->next, count++) {
+ if (fsp->fnum == fnum) {
+ chain_fsp = fsp;
+ if (count > 10) {
+ DLIST_PROMOTE(Files, fsp);
+ }
+ return fsp;
+ }
+ }
+ return NULL;
+}
+
+/****************************************************************************
+ Reset the chained fsp - done at the start of a packet reply.
+****************************************************************************/
+
+void file_chain_reset(void)
+{
+ chain_fsp = NULL;
+}
+
+/****************************************************************************
+Save the chained fsp - done when about to do an oplock break.
+****************************************************************************/
+
+void file_chain_save(void)
+{
+ oplock_save_chain_fsp = chain_fsp;
+}
+
+/****************************************************************************
+Restore the chained fsp - done after an oplock break.
+****************************************************************************/
+
+void file_chain_restore(void)
+{
+ chain_fsp = oplock_save_chain_fsp;
+}
diff --git a/source/smbd/ipc.c b/source/smbd/ipc.c
new file mode 100644
index 00000000000..e5465b902c8
--- /dev/null
+++ b/source/smbd/ipc.c
@@ -0,0 +1,599 @@
+/*
+ Unix SMB/CIFS implementation.
+ Inter-process communication and named pipe handling
+ Copyright (C) Andrew Tridgell 1992-1998
+
+ SMB Version handling
+ Copyright (C) John H Terpstra 1995-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 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.
+ */
+/*
+ This file handles the named pipe and mailslot calls
+ in the SMBtrans protocol
+ */
+
+#include "includes.h"
+
+extern int max_send;
+
+extern fstring local_machine;
+
+#define NERR_notsupported 50
+
+extern int smb_read_error;
+
+/*******************************************************************
+ copies parameters and data, as needed, into the smb buffer
+
+ *both* the data and params sections should be aligned. this
+ is fudged in the rpc pipes by
+ at present, only the data section is. this may be a possible
+ cause of some of the ipc problems being experienced. lkcl26dec97
+
+ ******************************************************************/
+
+static void copy_trans_params_and_data(char *outbuf, int align,
+ char *rparam, int param_offset, int param_len,
+ char *rdata, int data_offset, int data_len)
+{
+ char *copy_into = smb_buf(outbuf)+1;
+
+ if(param_len < 0)
+ param_len = 0;
+
+ if(data_len < 0)
+ data_len = 0;
+
+ DEBUG(5,("copy_trans_params_and_data: params[%d..%d] data[%d..%d]\n",
+ param_offset, param_offset + param_len,
+ data_offset , data_offset + data_len));
+
+ if (param_len)
+ memcpy(copy_into, &rparam[param_offset], param_len);
+
+ copy_into += param_len + align;
+
+ if (data_len )
+ memcpy(copy_into, &rdata[data_offset], data_len);
+}
+
+/****************************************************************************
+ Send a trans reply.
+ ****************************************************************************/
+
+void send_trans_reply(char *outbuf,
+ char *rparam, int rparam_len,
+ char *rdata, int rdata_len,
+ BOOL buffer_too_large)
+{
+ int this_ldata,this_lparam;
+ int tot_data_sent = 0;
+ int tot_param_sent = 0;
+ int align;
+
+ int ldata = rdata ? rdata_len : 0;
+ int lparam = rparam ? rparam_len : 0;
+
+ if (buffer_too_large)
+ DEBUG(5,("send_trans_reply: buffer %d too large\n", ldata ));
+
+ this_lparam = MIN(lparam,max_send - 500); /* hack */
+ this_ldata = MIN(ldata,max_send - (500+this_lparam));
+
+ align = ((this_lparam)%4);
+
+ if (buffer_too_large) {
+ ERROR_BOTH(STATUS_BUFFER_OVERFLOW,ERRDOS,ERRmoredata);
+ }
+
+ set_message(outbuf,10,1+align+this_ldata+this_lparam,True);
+
+ copy_trans_params_and_data(outbuf, align,
+ rparam, tot_param_sent, this_lparam,
+ rdata, tot_data_sent, this_ldata);
+
+ SSVAL(outbuf,smb_vwv0,lparam);
+ SSVAL(outbuf,smb_vwv1,ldata);
+ SSVAL(outbuf,smb_vwv3,this_lparam);
+ SSVAL(outbuf,smb_vwv4,smb_offset(smb_buf(outbuf)+1,outbuf));
+ SSVAL(outbuf,smb_vwv5,0);
+ SSVAL(outbuf,smb_vwv6,this_ldata);
+ SSVAL(outbuf,smb_vwv7,smb_offset(smb_buf(outbuf)+1+this_lparam+align,outbuf));
+ SSVAL(outbuf,smb_vwv8,0);
+ SSVAL(outbuf,smb_vwv9,0);
+
+ show_msg(outbuf);
+ if (!send_smb(smbd_server_fd(),outbuf))
+ exit_server("send_trans_reply: send_smb failed.");
+
+ tot_data_sent = this_ldata;
+ tot_param_sent = this_lparam;
+
+ while (tot_data_sent < ldata || tot_param_sent < lparam)
+ {
+ this_lparam = MIN(lparam-tot_param_sent, max_send - 500); /* hack */
+ this_ldata = MIN(ldata -tot_data_sent, max_send - (500+this_lparam));
+
+ if(this_lparam < 0)
+ this_lparam = 0;
+
+ if(this_ldata < 0)
+ this_ldata = 0;
+
+ align = (this_lparam%4);
+
+ set_message(outbuf,10,1+this_ldata+this_lparam+align,False);
+
+ copy_trans_params_and_data(outbuf, align,
+ rparam, tot_param_sent, this_lparam,
+ rdata, tot_data_sent, this_ldata);
+
+ SSVAL(outbuf,smb_vwv3,this_lparam);
+ SSVAL(outbuf,smb_vwv4,smb_offset(smb_buf(outbuf)+1,outbuf));
+ SSVAL(outbuf,smb_vwv5,tot_param_sent);
+ SSVAL(outbuf,smb_vwv6,this_ldata);
+ SSVAL(outbuf,smb_vwv7,smb_offset(smb_buf(outbuf)+1+this_lparam+align,outbuf));
+ SSVAL(outbuf,smb_vwv8,tot_data_sent);
+ SSVAL(outbuf,smb_vwv9,0);
+
+ show_msg(outbuf);
+ if (!send_smb(smbd_server_fd(),outbuf))
+ exit_server("send_trans_reply: send_smb failed.");
+
+ tot_data_sent += this_ldata;
+ tot_param_sent += this_lparam;
+ }
+}
+
+/****************************************************************************
+ Start the first part of an RPC reply which began with an SMBtrans request.
+****************************************************************************/
+
+static BOOL api_rpc_trans_reply(char *outbuf, smb_np_struct *p)
+{
+ BOOL is_data_outstanding;
+ char *rdata = malloc(p->max_trans_reply);
+ int data_len;
+
+ if(rdata == NULL) {
+ DEBUG(0,("api_rpc_trans_reply: malloc fail.\n"));
+ return False;
+ }
+
+ if((data_len = read_from_pipe( p, rdata, p->max_trans_reply,
+ &is_data_outstanding)) < 0) {
+ SAFE_FREE(rdata);
+ return False;
+ }
+
+ send_trans_reply(outbuf, NULL, 0, rdata, data_len, is_data_outstanding);
+
+ SAFE_FREE(rdata);
+ return True;
+}
+
+/****************************************************************************
+ WaitNamedPipeHandleState
+****************************************************************************/
+
+static BOOL api_WNPHS(char *outbuf, smb_np_struct *p, char *param, int param_len)
+{
+ uint16 priority;
+
+ if (!param || param_len < 2)
+ return False;
+
+ priority = SVAL(param,0);
+ DEBUG(4,("WaitNamedPipeHandleState priority %x\n", priority));
+
+ if (wait_rpc_pipe_hnd_state(p, priority)) {
+ /* now send the reply */
+ send_trans_reply(outbuf, NULL, 0, NULL, 0, False);
+ return True;
+ }
+ return False;
+}
+
+
+/****************************************************************************
+ SetNamedPipeHandleState
+****************************************************************************/
+
+static BOOL api_SNPHS(char *outbuf, smb_np_struct *p, char *param, int param_len)
+{
+ uint16 id;
+
+ if (!param || param_len < 2)
+ return False;
+
+ id = SVAL(param,0);
+ DEBUG(4,("SetNamedPipeHandleState to code %x\n", id));
+
+ if (set_rpc_pipe_hnd_state(p, id)) {
+ /* now send the reply */
+ send_trans_reply(outbuf, NULL, 0, NULL, 0, False);
+ return True;
+ }
+ return False;
+}
+
+
+/****************************************************************************
+ When no reply is generated, indicate unsupported.
+ ****************************************************************************/
+
+static BOOL api_no_reply(char *outbuf, int max_rdata_len)
+{
+ char rparam[4];
+
+ /* unsupported */
+ SSVAL(rparam,0,NERR_notsupported);
+ SSVAL(rparam,2,0); /* converter word */
+
+ DEBUG(3,("Unsupported API fd command\n"));
+
+ /* now send the reply */
+ send_trans_reply(outbuf, rparam, 4, NULL, 0, False);
+
+ return -1;
+}
+
+/****************************************************************************
+ Handle remote api calls delivered to a named pipe already opened.
+ ****************************************************************************/
+
+static int api_fd_reply(connection_struct *conn,uint16 vuid,char *outbuf,
+ uint16 *setup,char *data,char *params,
+ int suwcnt,int tdscnt,int tpscnt,int mdrcnt,int mprcnt)
+{
+ BOOL reply = False;
+ smb_np_struct *p = NULL;
+ int pnum;
+ int subcommand;
+
+ DEBUG(5,("api_fd_reply\n"));
+
+ /* First find out the name of this file. */
+ if (suwcnt != 2) {
+ DEBUG(0,("Unexpected named pipe transaction.\n"));
+ return(-1);
+ }
+
+ /* Get the file handle and hence the file name. */
+ /*
+ * NB. The setup array has already been transformed
+ * via SVAL and so is in gost byte order.
+ */
+ pnum = ((int)setup[1]) & 0xFFFF;
+ subcommand = ((int)setup[0]) & 0xFFFF;
+
+ if(!(p = get_rpc_pipe(pnum))) {
+ if (subcommand == TRANSACT_WAITNAMEDPIPEHANDLESTATE) {
+ /* Win9x does this call with a unicode pipe name, not a pnum. */
+ /* Just return success for now... */
+ DEBUG(3,("Got TRANSACT_WAITNAMEDPIPEHANDLESTATE on text pipe name\n"));
+ send_trans_reply(outbuf, NULL, 0, NULL, 0, False);
+ return -1;
+ }
+
+ DEBUG(1,("api_fd_reply: INVALID PIPE HANDLE: %x\n", pnum));
+ return api_no_reply(outbuf, mdrcnt);
+ }
+
+ DEBUG(3,("Got API command 0x%x on pipe \"%s\" (pnum %x)\n", subcommand, p->name, pnum));
+
+ /* record maximum data length that can be transmitted in an SMBtrans */
+ p->max_trans_reply = mdrcnt;
+
+ DEBUG(10,("api_fd_reply: p:%p max_trans_reply: %d\n", p, p->max_trans_reply));
+
+ switch (subcommand) {
+ case TRANSACT_DCERPCCMD:
+ /* dce/rpc command */
+ reply = write_to_pipe(p, data, tdscnt);
+ if (reply)
+ reply = api_rpc_trans_reply(outbuf, p);
+ break;
+ case TRANSACT_WAITNAMEDPIPEHANDLESTATE:
+ /* Wait Named Pipe Handle state */
+ reply = api_WNPHS(outbuf, p, params, tpscnt);
+ break;
+ case TRANSACT_SETNAMEDPIPEHANDLESTATE:
+ /* Set Named Pipe Handle state */
+ reply = api_SNPHS(outbuf, p, params, tpscnt);
+ break;
+ }
+
+ if (!reply)
+ return api_no_reply(outbuf, mdrcnt);
+
+ return -1;
+}
+
+/****************************************************************************
+ handle named pipe commands
+ ****************************************************************************/
+static int named_pipe(connection_struct *conn,uint16 vuid, char *outbuf,char *name,
+ uint16 *setup,char *data,char *params,
+ int suwcnt,int tdscnt,int tpscnt,
+ int msrcnt,int mdrcnt,int mprcnt)
+{
+ DEBUG(3,("named pipe command on <%s> name\n", name));
+
+ if (strequal(name,"LANMAN"))
+ return api_reply(conn,vuid,outbuf,data,params,tdscnt,tpscnt,mdrcnt,mprcnt);
+
+ if (strequal(name,"WKSSVC") ||
+ strequal(name,"SRVSVC") ||
+ strequal(name,"WINREG") ||
+ strequal(name,"SAMR") ||
+ strequal(name,"LSARPC"))
+ {
+ DEBUG(4,("named pipe command from Win95 (wow!)\n"));
+ return api_fd_reply(conn,vuid,outbuf,setup,data,params,suwcnt,tdscnt,tpscnt,mdrcnt,mprcnt);
+ }
+
+ if (strlen(name) < 1)
+ return api_fd_reply(conn,vuid,outbuf,setup,data,params,suwcnt,tdscnt,tpscnt,mdrcnt,mprcnt);
+
+ if (setup)
+ DEBUG(3,("unknown named pipe: setup 0x%X setup1=%d\n", (int)setup[0],(int)setup[1]));
+
+ return 0;
+}
+
+
+/****************************************************************************
+ Reply to a SMBtrans.
+ ****************************************************************************/
+
+int reply_trans(connection_struct *conn, char *inbuf,char *outbuf, int size, int bufsize)
+{
+ fstring name;
+ int name_offset = 0;
+ char *data=NULL,*params=NULL;
+ uint16 *setup=NULL;
+ int outsize = 0;
+ uint16 vuid = SVAL(inbuf,smb_uid);
+ unsigned int tpscnt = SVAL(inbuf,smb_vwv0);
+ unsigned int tdscnt = SVAL(inbuf,smb_vwv1);
+ unsigned int mprcnt = SVAL(inbuf,smb_vwv2);
+ unsigned int mdrcnt = SVAL(inbuf,smb_vwv3);
+ unsigned int msrcnt = CVAL(inbuf,smb_vwv4);
+ BOOL close_on_completion = BITSETW(inbuf+smb_vwv5,0);
+ BOOL one_way = BITSETW(inbuf+smb_vwv5,1);
+ unsigned int pscnt = SVAL(inbuf,smb_vwv9);
+ unsigned int psoff = SVAL(inbuf,smb_vwv10);
+ unsigned int dscnt = SVAL(inbuf,smb_vwv11);
+ unsigned int dsoff = SVAL(inbuf,smb_vwv12);
+ unsigned int suwcnt = CVAL(inbuf,smb_vwv13);
+ START_PROFILE(SMBtrans);
+
+ memset(name, '\0',sizeof(name));
+ srvstr_pull_buf(inbuf, name, smb_buf(inbuf), sizeof(name), STR_TERMINATE);
+
+ if (dscnt > tdscnt || pscnt > tpscnt)
+ goto bad_param;
+
+ if (tdscnt) {
+ if((data = (char *)malloc(tdscnt)) == NULL) {
+ DEBUG(0,("reply_trans: data malloc fail for %u bytes !\n", tdscnt));
+ END_PROFILE(SMBtrans);
+ return(ERROR_DOS(ERRDOS,ERRnomem));
+ }
+ if ((dsoff+dscnt < dsoff) || (dsoff+dscnt < dscnt))
+ goto bad_param;
+ if ((smb_base(inbuf)+dsoff+dscnt > inbuf + size) ||
+ (smb_base(inbuf)+dsoff+dscnt < smb_base(inbuf)))
+ goto bad_param;
+
+ memcpy(data,smb_base(inbuf)+dsoff,dscnt);
+ }
+
+ if (tpscnt) {
+ if((params = (char *)malloc(tpscnt)) == NULL) {
+ DEBUG(0,("reply_trans: param malloc fail for %u bytes !\n", tpscnt));
+ SAFE_FREE(data);
+ END_PROFILE(SMBtrans);
+ return(ERROR_DOS(ERRDOS,ERRnomem));
+ }
+ if ((psoff+pscnt < psoff) || (psoff+pscnt < pscnt))
+ goto bad_param;
+ if ((smb_base(inbuf)+psoff+pscnt > inbuf + size) ||
+ (smb_base(inbuf)+psoff+pscnt < smb_base(inbuf)))
+ goto bad_param;
+
+ memcpy(params,smb_base(inbuf)+psoff,pscnt);
+ }
+
+ if (suwcnt) {
+ unsigned int i;
+ if((setup = (uint16 *)malloc(suwcnt*sizeof(uint16))) == NULL) {
+ DEBUG(0,("reply_trans: setup malloc fail for %u bytes !\n", (unsigned int)(suwcnt * sizeof(uint16))));
+ SAFE_FREE(data);
+ SAFE_FREE(params);
+ END_PROFILE(SMBtrans);
+ return(ERROR_DOS(ERRDOS,ERRnomem));
+ }
+ if (inbuf+smb_vwv14+(suwcnt*SIZEOFWORD) > inbuf + size)
+ goto bad_param;
+ if ((smb_vwv14+(suwcnt*SIZEOFWORD) < smb_vwv14) || (smb_vwv14+(suwcnt*SIZEOFWORD) < (suwcnt*SIZEOFWORD)))
+ goto bad_param;
+
+ for (i=0;i<suwcnt;i++)
+ setup[i] = SVAL(inbuf,smb_vwv14+i*SIZEOFWORD);
+ }
+
+
+ srv_signing_trans_start(SVAL(inbuf,smb_mid));
+
+ if (pscnt < tpscnt || dscnt < tdscnt) {
+ /* We need to send an interim response then receive the rest
+ of the parameter/data bytes */
+ outsize = set_message(outbuf,0,0,True);
+ show_msg(outbuf);
+ srv_signing_trans_stop();
+ if (!send_smb(smbd_server_fd(),outbuf))
+ exit_server("reply_trans: send_smb failed.");
+ }
+
+ /* receive the rest of the trans packet */
+ while (pscnt < tpscnt || dscnt < tdscnt) {
+ BOOL ret;
+ unsigned int pcnt,poff,dcnt,doff,pdisp,ddisp;
+
+ ret = receive_next_smb(inbuf,bufsize,SMB_SECONDARY_WAIT);
+
+ /*
+ * The sequence number for the trans reply is always
+ * based on the last secondary received.
+ */
+
+ srv_signing_trans_start(SVAL(inbuf,smb_mid));
+
+ if ((ret && (CVAL(inbuf, smb_com) != SMBtranss)) || !ret) {
+ if(ret) {
+ DEBUG(0,("reply_trans: Invalid secondary trans packet\n"));
+ } else {
+ DEBUG(0,("reply_trans: %s in getting secondary trans response.\n",
+ (smb_read_error == READ_ERROR) ? "error" : "timeout" ));
+ }
+ SAFE_FREE(params);
+ SAFE_FREE(data);
+ SAFE_FREE(setup);
+ END_PROFILE(SMBtrans);
+ srv_signing_trans_stop();
+ return(ERROR_DOS(ERRSRV,ERRerror));
+ }
+
+ show_msg(inbuf);
+
+ /* Revise total_params and total_data in case they have changed downwards */
+ if (SVAL(inbuf,smb_vwv0) < tpscnt)
+ tpscnt = SVAL(inbuf,smb_vwv0);
+ if (SVAL(inbuf,smb_vwv1) < tdscnt)
+ tdscnt = SVAL(inbuf,smb_vwv1);
+
+ pcnt = SVAL(inbuf,smb_vwv2);
+ poff = SVAL(inbuf,smb_vwv3);
+ pdisp = SVAL(inbuf,smb_vwv4);
+
+ dcnt = SVAL(inbuf,smb_vwv5);
+ doff = SVAL(inbuf,smb_vwv6);
+ ddisp = SVAL(inbuf,smb_vwv7);
+
+ pscnt += pcnt;
+ dscnt += dcnt;
+
+ if (dscnt > tdscnt || pscnt > tpscnt)
+ goto bad_param;
+
+ if (pcnt) {
+ if (pdisp+pcnt >= tpscnt)
+ goto bad_param;
+ if ((pdisp+pcnt < pdisp) || (pdisp+pcnt < pcnt))
+ goto bad_param;
+ if (pdisp > tpscnt)
+ goto bad_param;
+ if ((smb_base(inbuf) + poff + pcnt >= inbuf + bufsize) ||
+ (smb_base(inbuf) + poff + pcnt < smb_base(inbuf)))
+ goto bad_param;
+ if (params + pdisp < params)
+ goto bad_param;
+
+ memcpy(params+pdisp,smb_base(inbuf)+poff,pcnt);
+ }
+
+ if (dcnt) {
+ if (ddisp+dcnt >= tdscnt)
+ goto bad_param;
+ if ((ddisp+dcnt < ddisp) || (ddisp+dcnt < dcnt))
+ goto bad_param;
+ if (ddisp > tdscnt)
+ goto bad_param;
+ if ((smb_base(inbuf) + doff + dcnt >= inbuf + bufsize) ||
+ (smb_base(inbuf) + doff + dcnt < smb_base(inbuf)))
+ goto bad_param;
+ if (data + ddisp < data)
+ goto bad_param;
+
+ memcpy(data+ddisp,smb_base(inbuf)+doff,dcnt);
+ }
+ }
+
+ DEBUG(3,("trans <%s> data=%u params=%u setup=%u\n",
+ name,tdscnt,tpscnt,suwcnt));
+
+ /*
+ * WinCE wierdness....
+ */
+
+ if (name[0] == '\\' && (StrnCaseCmp(&name[1],local_machine, strlen(local_machine)) == 0) &&
+ (name[strlen(local_machine)+1] == '\\'))
+ name_offset = strlen(local_machine)+1;
+
+ if (strnequal(&name[name_offset], "\\PIPE", strlen("\\PIPE"))) {
+ name_offset += strlen("\\PIPE");
+
+ /* Win9x weirdness. When talking to a unicode server Win9x
+ only sends \PIPE instead of \PIPE\ */
+
+ if (name[name_offset] == '\\')
+ name_offset++;
+
+ DEBUG(5,("calling named_pipe\n"));
+ outsize = named_pipe(conn,vuid,outbuf,
+ name+name_offset,setup,data,params,
+ suwcnt,tdscnt,tpscnt,msrcnt,mdrcnt,mprcnt);
+ } else {
+ DEBUG(3,("invalid pipe name\n"));
+ outsize = 0;
+ }
+
+
+ SAFE_FREE(data);
+ SAFE_FREE(params);
+ SAFE_FREE(setup);
+
+ srv_signing_trans_stop();
+
+ if (close_on_completion)
+ close_cnum(conn,vuid);
+
+ if (one_way) {
+ END_PROFILE(SMBtrans);
+ return(-1);
+ }
+
+ if (outsize == 0) {
+ END_PROFILE(SMBtrans);
+ return(ERROR_DOS(ERRSRV,ERRnosupport));
+ }
+
+ END_PROFILE(SMBtrans);
+ return(outsize);
+
+
+ bad_param:
+
+ srv_signing_trans_stop();
+ DEBUG(0,("reply_trans: invalid trans parameters\n"));
+ SAFE_FREE(data);
+ SAFE_FREE(params);
+ SAFE_FREE(setup);
+ END_PROFILE(SMBtrans);
+ return ERROR_NT(NT_STATUS_INVALID_PARAMETER);
+}
diff --git a/source/smbd/lanman.c b/source/smbd/lanman.c
new file mode 100644
index 00000000000..d715ab4ddc3
--- /dev/null
+++ b/source/smbd/lanman.c
@@ -0,0 +1,3619 @@
+/*
+ Unix SMB/CIFS implementation.
+ Inter-process communication and named pipe handling
+ Copyright (C) Andrew Tridgell 1992-1998
+
+ SMB Version handling
+ Copyright (C) John H Terpstra 1995-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 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.
+ */
+/*
+ This file handles the named pipe and mailslot calls
+ in the SMBtrans protocol
+ */
+
+#include "includes.h"
+
+#ifdef CHECK_TYPES
+#undef CHECK_TYPES
+#endif
+#define CHECK_TYPES 0
+
+extern fstring local_machine;
+
+#define NERR_Success 0
+#define NERR_badpass 86
+#define NERR_notsupported 50
+
+#define NERR_BASE (2100)
+#define NERR_BufTooSmall (NERR_BASE+23)
+#define NERR_JobNotFound (NERR_BASE+51)
+#define NERR_DestNotFound (NERR_BASE+52)
+
+#define ACCESS_READ 0x01
+#define ACCESS_WRITE 0x02
+#define ACCESS_CREATE 0x04
+
+#define SHPWLEN 8 /* share password length */
+
+static BOOL api_Unsupported(connection_struct *conn,uint16 vuid, char *param,char *data,
+ int mdrcnt,int mprcnt,
+ char **rdata,char **rparam,
+ int *rdata_len,int *rparam_len);
+static BOOL api_TooSmall(connection_struct *conn,uint16 vuid, char *param,char *data,
+ int mdrcnt,int mprcnt,
+ char **rdata,char **rparam,
+ int *rdata_len,int *rparam_len);
+
+
+static int CopyExpanded(connection_struct *conn,
+ int snum, char** dst, char* src, int* n)
+{
+ pstring buf;
+ int l;
+
+ if (!src || !dst || !n || !(*dst)) return(0);
+
+ StrnCpy(buf,src,sizeof(buf)/2);
+ pstring_sub(buf,"%S",lp_servicename(snum));
+ standard_sub_conn(conn,buf,sizeof(buf));
+ l = push_ascii(*dst,buf,*n, STR_TERMINATE);
+ (*dst) += l;
+ (*n) -= l;
+ return l;
+}
+
+static int CopyAndAdvance(char** dst, char* src, int* n)
+{
+ int l;
+ if (!src || !dst || !n || !(*dst)) return(0);
+ l = push_ascii(*dst,src,*n, STR_TERMINATE);
+ (*dst) += l;
+ (*n) -= l;
+ return l;
+}
+
+static int StrlenExpanded(connection_struct *conn, int snum, char* s)
+{
+ pstring buf;
+ if (!s) return(0);
+ StrnCpy(buf,s,sizeof(buf)/2);
+ pstring_sub(buf,"%S",lp_servicename(snum));
+ standard_sub_conn(conn,buf,sizeof(buf));
+ return strlen(buf) + 1;
+}
+
+static char* Expand(connection_struct *conn, int snum, char* s)
+{
+ static pstring buf;
+ if (!s) return(NULL);
+ StrnCpy(buf,s,sizeof(buf)/2);
+ pstring_sub(buf,"%S",lp_servicename(snum));
+ standard_sub_conn(conn,buf,sizeof(buf));
+ return &buf[0];
+}
+
+/*******************************************************************
+ check a API string for validity when we only need to check the prefix
+ ******************************************************************/
+static BOOL prefix_ok(const char *str, const char *prefix)
+{
+ return(strncmp(str,prefix,strlen(prefix)) == 0);
+}
+
+struct pack_desc {
+ const char* format; /* formatstring for structure */
+ const char* subformat; /* subformat for structure */
+ char* base; /* baseaddress of buffer */
+ int buflen; /* remaining size for fixed part; on init: length of base */
+ int subcount; /* count of substructures */
+ char* structbuf; /* pointer into buffer for remaining fixed part */
+ int stringlen; /* remaining size for variable part */
+ char* stringbuf; /* pointer into buffer for remaining variable part */
+ int neededlen; /* total needed size */
+ int usedlen; /* total used size (usedlen <= neededlen and usedlen <= buflen) */
+ const char* curpos; /* current position; pointer into format or subformat */
+ int errcode;
+};
+
+static int get_counter(const char** p)
+{
+ int i, n;
+ if (!p || !(*p)) return(1);
+ if (!isdigit((int)**p)) return 1;
+ for (n = 0;;) {
+ i = **p;
+ if (isdigit(i))
+ n = 10 * n + (i - '0');
+ else
+ return n;
+ (*p)++;
+ }
+}
+
+static int getlen(const char* p)
+{
+ int n = 0;
+ if (!p) return(0);
+ while (*p) {
+ switch( *p++ ) {
+ case 'W': /* word (2 byte) */
+ n += 2;
+ break;
+ case 'K': /* status word? (2 byte) */
+ n += 2;
+ break;
+ case 'N': /* count of substructures (word) at end */
+ n += 2;
+ break;
+ case 'D': /* double word (4 byte) */
+ case 'z': /* offset to zero terminated string (4 byte) */
+ case 'l': /* offset to user data (4 byte) */
+ n += 4;
+ break;
+ case 'b': /* offset to data (with counter) (4 byte) */
+ n += 4;
+ get_counter(&p);
+ break;
+ case 'B': /* byte (with optional counter) */
+ n += get_counter(&p);
+ break;
+ }
+ }
+ return n;
+}
+
+static BOOL init_package(struct pack_desc* p, int count, int subcount)
+{
+ int n = p->buflen;
+ int i;
+
+ if (!p->format || !p->base) return(False);
+
+ i = count * getlen(p->format);
+ if (p->subformat) i += subcount * getlen(p->subformat);
+ p->structbuf = p->base;
+ p->neededlen = 0;
+ p->usedlen = 0;
+ p->subcount = 0;
+ p->curpos = p->format;
+ if (i > n) {
+ p->neededlen = i;
+ i = n = 0;
+#if 0
+ /*
+ * This is the old error code we used. Aparently
+ * WinNT/2k systems return ERRbuftoosmall (2123) and
+ * OS/2 needs this. I'm leaving this here so we can revert
+ * if needed. JRA.
+ */
+ p->errcode = ERRmoredata;
+#else
+ p->errcode = ERRbuftoosmall;
+#endif
+ }
+ else
+ p->errcode = NERR_Success;
+ p->buflen = i;
+ n -= i;
+ p->stringbuf = p->base + i;
+ p->stringlen = n;
+ return(p->errcode == NERR_Success);
+}
+
+static int package(struct pack_desc* p, ...)
+{
+ va_list args;
+ int needed=0, stringneeded;
+ const char* str=NULL;
+ int is_string=0, stringused;
+ int32 temp;
+
+ va_start(args,p);
+
+ if (!*p->curpos) {
+ if (!p->subcount)
+ p->curpos = p->format;
+ else {
+ p->curpos = p->subformat;
+ p->subcount--;
+ }
+ }
+#if CHECK_TYPES
+ str = va_arg(args,char*);
+ SMB_ASSERT(strncmp(str,p->curpos,strlen(str)) == 0);
+#endif
+ stringneeded = -1;
+
+ if (!p->curpos) {
+ va_end(args);
+ return(0);
+ }
+
+ switch( *p->curpos++ ) {
+ case 'W': /* word (2 byte) */
+ needed = 2;
+ temp = va_arg(args,int);
+ if (p->buflen >= needed) SSVAL(p->structbuf,0,temp);
+ break;
+ case 'K': /* status word? (2 byte) */
+ needed = 2;
+ temp = va_arg(args,int);
+ if (p->buflen >= needed) SSVAL(p->structbuf,0,temp);
+ break;
+ case 'N': /* count of substructures (word) at end */
+ needed = 2;
+ p->subcount = va_arg(args,int);
+ if (p->buflen >= needed) SSVAL(p->structbuf,0,p->subcount);
+ break;
+ case 'D': /* double word (4 byte) */
+ needed = 4;
+ temp = va_arg(args,int);
+ if (p->buflen >= needed) SIVAL(p->structbuf,0,temp);
+ break;
+ case 'B': /* byte (with optional counter) */
+ needed = get_counter(&p->curpos);
+ {
+ char *s = va_arg(args,char*);
+ if (p->buflen >= needed) StrnCpy(p->structbuf,s?s:"",needed-1);
+ }
+ break;
+ case 'z': /* offset to zero terminated string (4 byte) */
+ str = va_arg(args,char*);
+ stringneeded = (str ? strlen(str)+1 : 0);
+ is_string = 1;
+ break;
+ case 'l': /* offset to user data (4 byte) */
+ str = va_arg(args,char*);
+ stringneeded = va_arg(args,int);
+ is_string = 0;
+ break;
+ case 'b': /* offset to data (with counter) (4 byte) */
+ str = va_arg(args,char*);
+ stringneeded = get_counter(&p->curpos);
+ is_string = 0;
+ break;
+ }
+ va_end(args);
+ if (stringneeded >= 0) {
+ needed = 4;
+ if (p->buflen >= needed) {
+ stringused = stringneeded;
+ if (stringused > p->stringlen) {
+ stringused = (is_string ? p->stringlen : 0);
+ if (p->errcode == NERR_Success) p->errcode = ERRmoredata;
+ }
+ if (!stringused)
+ SIVAL(p->structbuf,0,0);
+ else {
+ SIVAL(p->structbuf,0,PTR_DIFF(p->stringbuf,p->base));
+ memcpy(p->stringbuf,str?str:"",stringused);
+ if (is_string) p->stringbuf[stringused-1] = '\0';
+ p->stringbuf += stringused;
+ p->stringlen -= stringused;
+ p->usedlen += stringused;
+ }
+ }
+ p->neededlen += stringneeded;
+ }
+ p->neededlen += needed;
+ if (p->buflen >= needed) {
+ p->structbuf += needed;
+ p->buflen -= needed;
+ p->usedlen += needed;
+ }
+ else {
+ if (p->errcode == NERR_Success) p->errcode = ERRmoredata;
+ }
+ return 1;
+}
+
+#if CHECK_TYPES
+#define PACK(desc,t,v) package(desc,t,v,0,0,0,0)
+#define PACKl(desc,t,v,l) package(desc,t,v,l,0,0,0,0)
+#else
+#define PACK(desc,t,v) package(desc,v)
+#define PACKl(desc,t,v,l) package(desc,v,l)
+#endif
+
+static void PACKI(struct pack_desc* desc, const char *t,int v)
+{
+ PACK(desc,t,v);
+}
+
+static void PACKS(struct pack_desc* desc,const char *t,const char *v)
+{
+ PACK(desc,t,v);
+}
+
+
+/****************************************************************************
+ get a print queue
+ ****************************************************************************/
+static void PackDriverData(struct pack_desc* desc)
+{
+ char drivdata[4+4+32];
+ SIVAL(drivdata,0,sizeof drivdata); /* cb */
+ SIVAL(drivdata,4,1000); /* lVersion */
+ memset(drivdata+8,0,32); /* szDeviceName */
+ push_ascii(drivdata+8,"NULL",-1, STR_TERMINATE);
+ PACKl(desc,"l",drivdata,sizeof drivdata); /* pDriverData */
+}
+
+static int check_printq_info(struct pack_desc* desc,
+ int uLevel, char *id1, char *id2)
+{
+ desc->subformat = NULL;
+ switch( uLevel ) {
+ case 0:
+ desc->format = "B13";
+ break;
+ case 1:
+ desc->format = "B13BWWWzzzzzWW";
+ break;
+ case 2:
+ desc->format = "B13BWWWzzzzzWN";
+ desc->subformat = "WB21BB16B10zWWzDDz";
+ break;
+ case 3:
+ desc->format = "zWWWWzzzzWWzzl";
+ break;
+ case 4:
+ desc->format = "zWWWWzzzzWNzzl";
+ desc->subformat = "WWzWWDDzz";
+ break;
+ case 5:
+ desc->format = "z";
+ break;
+ case 51:
+ desc->format = "K";
+ break;
+ case 52:
+ desc->format = "WzzzzzzzzN";
+ desc->subformat = "z";
+ break;
+ default: return False;
+ }
+ if (strcmp(desc->format,id1) != 0) return False;
+ if (desc->subformat && strcmp(desc->subformat,id2) != 0) return False;
+ return True;
+}
+
+
+#define RAP_JOB_STATUS_QUEUED 0
+#define RAP_JOB_STATUS_PAUSED 1
+#define RAP_JOB_STATUS_SPOOLING 2
+#define RAP_JOB_STATUS_PRINTING 3
+#define RAP_JOB_STATUS_PRINTED 4
+
+#define RAP_QUEUE_STATUS_PAUSED 1
+#define RAP_QUEUE_STATUS_ERROR 2
+
+/* turn a print job status into a on the wire status
+*/
+static int printj_status(int v)
+{
+ switch (v) {
+ case LPQ_QUEUED:
+ return RAP_JOB_STATUS_QUEUED;
+ case LPQ_PAUSED:
+ return RAP_JOB_STATUS_PAUSED;
+ case LPQ_SPOOLING:
+ return RAP_JOB_STATUS_SPOOLING;
+ case LPQ_PRINTING:
+ return RAP_JOB_STATUS_PRINTING;
+ }
+ return 0;
+}
+
+/* turn a print queue status into a on the wire status
+*/
+static int printq_status(int v)
+{
+ switch (v) {
+ case LPQ_QUEUED:
+ return 0;
+ case LPQ_PAUSED:
+ return RAP_QUEUE_STATUS_PAUSED;
+ }
+ return RAP_QUEUE_STATUS_ERROR;
+}
+
+static void fill_printjob_info(connection_struct *conn, int snum, int uLevel,
+ struct pack_desc* desc,
+ print_queue_struct* queue, int n)
+{
+ time_t t = queue->time;
+
+ /* the client expects localtime */
+ t -= TimeDiff(t);
+
+ PACKI(desc,"W",pjobid_to_rap(snum,queue->job)); /* uJobId */
+ if (uLevel == 1) {
+ PACKS(desc,"B21",queue->fs_user); /* szUserName */
+ PACKS(desc,"B",""); /* pad */
+ PACKS(desc,"B16",""); /* szNotifyName */
+ PACKS(desc,"B10","PM_Q_RAW"); /* szDataType */
+ PACKS(desc,"z",""); /* pszParms */
+ PACKI(desc,"W",n+1); /* uPosition */
+ PACKI(desc,"W",printj_status(queue->status)); /* fsStatus */
+ PACKS(desc,"z",""); /* pszStatus */
+ PACKI(desc,"D",t); /* ulSubmitted */
+ PACKI(desc,"D",queue->size); /* ulSize */
+ PACKS(desc,"z",queue->fs_file); /* pszComment */
+ }
+ if (uLevel == 2 || uLevel == 3 || uLevel == 4) {
+ PACKI(desc,"W",queue->priority); /* uPriority */
+ PACKS(desc,"z",queue->fs_user); /* pszUserName */
+ PACKI(desc,"W",n+1); /* uPosition */
+ PACKI(desc,"W",printj_status(queue->status)); /* fsStatus */
+ PACKI(desc,"D",t); /* ulSubmitted */
+ PACKI(desc,"D",queue->size); /* ulSize */
+ PACKS(desc,"z","Samba"); /* pszComment */
+ PACKS(desc,"z",queue->fs_file); /* pszDocument */
+ if (uLevel == 3) {
+ PACKS(desc,"z",""); /* pszNotifyName */
+ PACKS(desc,"z","PM_Q_RAW"); /* pszDataType */
+ PACKS(desc,"z",""); /* pszParms */
+ PACKS(desc,"z",""); /* pszStatus */
+ PACKS(desc,"z",SERVICE(snum)); /* pszQueue */
+ PACKS(desc,"z","lpd"); /* pszQProcName */
+ PACKS(desc,"z",""); /* pszQProcParms */
+ PACKS(desc,"z","NULL"); /* pszDriverName */
+ PackDriverData(desc); /* pDriverData */
+ PACKS(desc,"z",""); /* pszPrinterName */
+ } else if (uLevel == 4) { /* OS2 */
+ PACKS(desc,"z",""); /* pszSpoolFileName */
+ PACKS(desc,"z",""); /* pszPortName */
+ PACKS(desc,"z",""); /* pszStatus */
+ PACKI(desc,"D",0); /* ulPagesSpooled */
+ PACKI(desc,"D",0); /* ulPagesSent */
+ PACKI(desc,"D",0); /* ulPagesPrinted */
+ PACKI(desc,"D",0); /* ulTimePrinted */
+ PACKI(desc,"D",0); /* ulExtendJobStatus */
+ PACKI(desc,"D",0); /* ulStartPage */
+ PACKI(desc,"D",0); /* ulEndPage */
+ }
+ }
+}
+
+/********************************************************************
+ Return a driver name given an snum.
+ Returns True if from tdb, False otherwise.
+ ********************************************************************/
+
+static BOOL get_driver_name(int snum, pstring drivername)
+{
+ NT_PRINTER_INFO_LEVEL *info = NULL;
+ BOOL in_tdb = False;
+
+ get_a_printer (NULL, &info, 2, lp_servicename(snum));
+ if (info != NULL) {
+ pstrcpy( drivername, info->info_2->drivername);
+ in_tdb = True;
+ free_a_printer(&info, 2);
+ }
+
+ return in_tdb;
+}
+
+/********************************************************************
+ Respond to the DosPrintQInfo command with a level of 52
+ This is used to get printer driver information for Win9x clients
+ ********************************************************************/
+static void fill_printq_info_52(connection_struct *conn, int snum,
+ struct pack_desc* desc, int count )
+{
+ int i;
+ fstring location;
+ NT_PRINTER_DRIVER_INFO_LEVEL driver;
+ NT_PRINTER_INFO_LEVEL *printer = NULL;
+
+ ZERO_STRUCT(driver);
+
+ if ( !W_ERROR_IS_OK(get_a_printer( NULL, &printer, 2, lp_servicename(snum))) ) {
+ DEBUG(3,("fill_printq_info_52: Failed to lookup printer [%s]\n",
+ lp_servicename(snum)));
+ goto err;
+ }
+
+ if ( !W_ERROR_IS_OK(get_a_printer_driver(&driver, 3, printer->info_2->drivername,
+ "Windows 4.0", 0)) )
+ {
+ DEBUG(3,("fill_printq_info_52: Failed to lookup driver [%s]\n",
+ printer->info_2->drivername));
+ goto err;
+ }
+
+ trim_string(driver.info_3->driverpath, "\\print$\\WIN40\\0\\", 0);
+ trim_string(driver.info_3->datafile, "\\print$\\WIN40\\0\\", 0);
+ trim_string(driver.info_3->helpfile, "\\print$\\WIN40\\0\\", 0);
+
+ PACKI(desc, "W", 0x0400); /* don't know */
+ PACKS(desc, "z", driver.info_3->name); /* long printer name */
+ PACKS(desc, "z", driver.info_3->driverpath); /* Driverfile Name */
+ PACKS(desc, "z", driver.info_3->datafile); /* Datafile name */
+ PACKS(desc, "z", driver.info_3->monitorname); /* language monitor */
+
+ fstrcpy(location, "\\\\");
+ fstrcat(location, get_called_name());
+ fstrcat(location, "\\print$\\WIN40\\0");
+ PACKS(desc,"z", location); /* share to retrieve files */
+
+ PACKS(desc,"z", driver.info_3->defaultdatatype); /* default data type */
+ PACKS(desc,"z", driver.info_3->helpfile); /* helpfile name */
+ PACKS(desc,"z", driver.info_3->driverpath); /* driver name */
+
+ DEBUG(3,("Printer Driver Name: %s:\n",driver.info_3->name));
+ DEBUG(3,("Driver: %s:\n",driver.info_3->driverpath));
+ DEBUG(3,("Data File: %s:\n",driver.info_3->datafile));
+ DEBUG(3,("Language Monitor: %s:\n",driver.info_3->monitorname));
+ DEBUG(3,("Driver Location: %s:\n",location));
+ DEBUG(3,("Data Type: %s:\n",driver.info_3->defaultdatatype));
+ DEBUG(3,("Help File: %s:\n",driver.info_3->helpfile));
+ PACKI(desc,"N",count); /* number of files to copy */
+
+ for ( i=0; i<count && driver.info_3->dependentfiles && *driver.info_3->dependentfiles[i]; i++)
+ {
+ trim_string(driver.info_3->dependentfiles[i], "\\print$\\WIN40\\0\\", 0);
+ PACKS(desc,"z",driver.info_3->dependentfiles[i]); /* driver files to copy */
+ DEBUG(3,("Dependent File: %s:\n",driver.info_3->dependentfiles[i]));
+ }
+
+ /* sanity check */
+ if ( i != count )
+ DEBUG(3,("fill_printq_info_52: file count specified by client [%d] != number of dependent files [%i]\n",
+ count, i));
+
+ DEBUG(3,("fill_printq_info on <%s> gave %d entries\n", SERVICE(snum),i));
+
+ desc->errcode=NERR_Success;
+ goto done;
+
+err:
+ DEBUG(3,("fill_printq_info: Can't supply driver files\n"));
+ desc->errcode=NERR_notsupported;
+
+done:
+ if ( printer )
+ free_a_printer( &printer, 2 );
+
+ if ( driver.info_3 )
+ free_a_printer_driver( driver, 3 );
+}
+
+
+static void fill_printq_info(connection_struct *conn, int snum, int uLevel,
+ struct pack_desc* desc,
+ int count, print_queue_struct* queue,
+ print_status_struct* status)
+{
+ switch (uLevel) {
+ case 1:
+ case 2:
+ PACKS(desc,"B13",SERVICE(snum));
+ break;
+ case 3:
+ case 4:
+ case 5:
+ PACKS(desc,"z",Expand(conn,snum,SERVICE(snum)));
+ break;
+ case 51:
+ PACKI(desc,"K",printq_status(status->status));
+ break;
+ }
+
+ if (uLevel == 1 || uLevel == 2) {
+ PACKS(desc,"B",""); /* alignment */
+ PACKI(desc,"W",5); /* priority */
+ PACKI(desc,"W",0); /* start time */
+ PACKI(desc,"W",0); /* until time */
+ PACKS(desc,"z",""); /* pSepFile */
+ PACKS(desc,"z","lpd"); /* pPrProc */
+ PACKS(desc,"z",SERVICE(snum)); /* pDestinations */
+ PACKS(desc,"z",""); /* pParms */
+ if (snum < 0) {
+ PACKS(desc,"z","UNKNOWN PRINTER");
+ PACKI(desc,"W",LPSTAT_ERROR);
+ }
+ else if (!status || !status->message[0]) {
+ PACKS(desc,"z",Expand(conn,snum,lp_comment(snum)));
+ PACKI(desc,"W",LPSTAT_OK); /* status */
+ } else {
+ PACKS(desc,"z",status->message);
+ PACKI(desc,"W",printq_status(status->status)); /* status */
+ }
+ PACKI(desc,(uLevel == 1 ? "W" : "N"),count);
+ }
+
+ if (uLevel == 3 || uLevel == 4) {
+ pstring drivername;
+
+ PACKI(desc,"W",5); /* uPriority */
+ PACKI(desc,"W",0); /* uStarttime */
+ PACKI(desc,"W",0); /* uUntiltime */
+ PACKI(desc,"W",5); /* pad1 */
+ PACKS(desc,"z",""); /* pszSepFile */
+ PACKS(desc,"z","WinPrint"); /* pszPrProc */
+ PACKS(desc,"z",NULL); /* pszParms */
+ PACKS(desc,"z",NULL); /* pszComment - don't ask.... JRA */
+ /* "don't ask" that it's done this way to fix corrupted
+ Win9X/ME printer comments. */
+ if (!status) {
+ PACKI(desc,"W",LPSTAT_OK); /* fsStatus */
+ } else {
+ PACKI(desc,"W",printq_status(status->status)); /* fsStatus */
+ }
+ PACKI(desc,(uLevel == 3 ? "W" : "N"),count); /* cJobs */
+ PACKS(desc,"z",SERVICE(snum)); /* pszPrinters */
+ get_driver_name(snum,drivername);
+ PACKS(desc,"z",drivername); /* pszDriverName */
+ PackDriverData(desc); /* pDriverData */
+ }
+
+ if (uLevel == 2 || uLevel == 4) {
+ int i;
+ for (i=0;i<count;i++)
+ fill_printjob_info(conn,snum,uLevel == 2 ? 1 : 2,desc,&queue[i],i);
+ }
+
+ if (uLevel==52)
+ fill_printq_info_52( conn, snum, desc, count );
+}
+
+/* This function returns the number of files for a given driver */
+static int get_printerdrivernumber(int snum)
+{
+ int result = 0;
+ NT_PRINTER_DRIVER_INFO_LEVEL driver;
+ NT_PRINTER_INFO_LEVEL *printer = NULL;
+
+ ZERO_STRUCT(driver);
+
+ if ( !W_ERROR_IS_OK(get_a_printer( NULL, &printer, 2, lp_servicename(snum))) ) {
+ DEBUG(3,("get_printerdrivernumber: Failed to lookup printer [%s]\n",
+ lp_servicename(snum)));
+ goto done;
+ }
+
+ if ( !W_ERROR_IS_OK(get_a_printer_driver(&driver, 3, printer->info_2->drivername,
+ "Windows 4.0", 0)) )
+ {
+ DEBUG(3,("get_printerdrivernumber: Failed to lookup driver [%s]\n",
+ printer->info_2->drivername));
+ goto done;
+ }
+
+ /* count the number of files */
+ while ( driver.info_3->dependentfiles && *driver.info_3->dependentfiles[result] )
+ result++;
+ \
+ done:
+ if ( printer )
+ free_a_printer( &printer, 2 );
+
+ if ( driver.info_3 )
+ free_a_printer_driver( driver, 3 );
+
+ return result;
+}
+
+static BOOL api_DosPrintQGetInfo(connection_struct *conn,
+ uint16 vuid, char *param,char *data,
+ int mdrcnt,int mprcnt,
+ char **rdata,char **rparam,
+ int *rdata_len,int *rparam_len)
+{
+ char *str1 = param+2;
+ char *str2 = skip_string(str1,1);
+ char *p = skip_string(str2,1);
+ char *QueueName = p;
+ int uLevel;
+ int count=0;
+ int snum;
+ char* str3;
+ struct pack_desc desc;
+ print_queue_struct *queue=NULL;
+ print_status_struct status;
+ char* tmpdata=NULL;
+
+ memset((char *)&status,'\0',sizeof(status));
+ memset((char *)&desc,'\0',sizeof(desc));
+
+ p = skip_string(p,1);
+ uLevel = SVAL(p,0);
+ str3 = p + 4;
+
+ /* remove any trailing username */
+ if ((p = strchr_m(QueueName,'%')))
+ *p = 0;
+
+ DEBUG(3,("api_DosPrintQGetInfo uLevel=%d name=%s\n",uLevel,QueueName));
+
+ /* check it's a supported varient */
+ if (!prefix_ok(str1,"zWrLh"))
+ return False;
+ if (!check_printq_info(&desc,uLevel,str2,str3)) {
+ /*
+ * Patch from Scott Moomaw <scott@bridgewater.edu>
+ * to return the 'invalid info level' error if an
+ * unknown level was requested.
+ */
+ *rdata_len = 0;
+ *rparam_len = 6;
+ *rparam = REALLOC(*rparam,*rparam_len);
+ SSVALS(*rparam,0,ERRunknownlevel);
+ SSVAL(*rparam,2,0);
+ SSVAL(*rparam,4,0);
+ return(True);
+ }
+
+ snum = lp_servicenumber(QueueName);
+ if (snum < 0 && pcap_printername_ok(QueueName,NULL)) {
+ int pnum = lp_servicenumber(PRINTERS_NAME);
+ if (pnum >= 0) {
+ lp_add_printer(QueueName,pnum);
+ snum = lp_servicenumber(QueueName);
+ }
+ }
+
+ if (snum < 0 || !VALID_SNUM(snum))
+ return(False);
+
+ if (uLevel==52) {
+ count = get_printerdrivernumber(snum);
+ DEBUG(3,("api_DosPrintQGetInfo: Driver files count: %d\n",count));
+ } else {
+ count = print_queue_status(snum, &queue,&status);
+ }
+
+ if (mdrcnt > 0) {
+ *rdata = REALLOC(*rdata,mdrcnt);
+ desc.base = *rdata;
+ desc.buflen = mdrcnt;
+ } else {
+ /*
+ * Don't return data but need to get correct length
+ * init_package will return wrong size if buflen=0
+ */
+ desc.buflen = getlen(desc.format);
+ desc.base = tmpdata = (char *) malloc (desc.buflen);
+ }
+
+ if (init_package(&desc,1,count)) {
+ desc.subcount = count;
+ fill_printq_info(conn,snum,uLevel,&desc,count,queue,&status);
+ }
+
+ *rdata_len = desc.usedlen;
+
+ /*
+ * We must set the return code to ERRbuftoosmall
+ * in order to support lanman style printing with Win NT/2k
+ * clients --jerry
+ */
+ if (!mdrcnt && lp_disable_spoolss())
+ desc.errcode = ERRbuftoosmall;
+
+ *rdata_len = desc.usedlen;
+ *rparam_len = 6;
+ *rparam = REALLOC(*rparam,*rparam_len);
+ SSVALS(*rparam,0,desc.errcode);
+ SSVAL(*rparam,2,0);
+ SSVAL(*rparam,4,desc.neededlen);
+
+ DEBUG(4,("printqgetinfo: errorcode %d\n",desc.errcode));
+
+ SAFE_FREE(queue);
+ SAFE_FREE(tmpdata);
+
+ return(True);
+}
+
+/****************************************************************************
+ View list of all print jobs on all queues.
+****************************************************************************/
+
+static BOOL api_DosPrintQEnum(connection_struct *conn, uint16 vuid, char* param, char* data,
+ int mdrcnt, int mprcnt,
+ char **rdata, char** rparam,
+ int *rdata_len, int *rparam_len)
+{
+ char *param_format = param+2;
+ char *output_format1 = skip_string(param_format,1);
+ char *p = skip_string(output_format1,1);
+ int uLevel = SVAL(p,0);
+ char *output_format2 = p + 4;
+ int services = lp_numservices();
+ int i, n;
+ struct pack_desc desc;
+ print_queue_struct **queue = NULL;
+ print_status_struct *status = NULL;
+ int* subcntarr = NULL;
+ int queuecnt, subcnt=0, succnt=0;
+
+ memset((char *)&desc,'\0',sizeof(desc));
+
+ DEBUG(3,("DosPrintQEnum uLevel=%d\n",uLevel));
+
+ if (!prefix_ok(param_format,"WrLeh")) return False;
+ if (!check_printq_info(&desc,uLevel,output_format1,output_format2)) {
+ /*
+ * Patch from Scott Moomaw <scott@bridgewater.edu>
+ * to return the 'invalid info level' error if an
+ * unknown level was requested.
+ */
+ *rdata_len = 0;
+ *rparam_len = 6;
+ *rparam = REALLOC(*rparam,*rparam_len);
+ SSVALS(*rparam,0,ERRunknownlevel);
+ SSVAL(*rparam,2,0);
+ SSVAL(*rparam,4,0);
+ return(True);
+ }
+
+ queuecnt = 0;
+ for (i = 0; i < services; i++)
+ if (lp_snum_ok(i) && lp_print_ok(i) && lp_browseable(i))
+ queuecnt++;
+ if (uLevel > 0) {
+ if((queue = (print_queue_struct**)malloc(queuecnt*sizeof(print_queue_struct*))) == NULL) {
+ DEBUG(0,("api_DosPrintQEnum: malloc fail !\n"));
+ return False;
+ }
+ memset(queue,0,queuecnt*sizeof(print_queue_struct*));
+ if((status = (print_status_struct*)malloc(queuecnt*sizeof(print_status_struct))) == NULL) {
+ DEBUG(0,("api_DosPrintQEnum: malloc fail !\n"));
+ return False;
+ }
+ memset(status,0,queuecnt*sizeof(print_status_struct));
+ if((subcntarr = (int*)malloc(queuecnt*sizeof(int))) == NULL) {
+ DEBUG(0,("api_DosPrintQEnum: malloc fail !\n"));
+ return False;
+ }
+ subcnt = 0;
+ n = 0;
+ for (i = 0; i < services; i++)
+ if (lp_snum_ok(i) && lp_print_ok(i) && lp_browseable(i)) {
+ subcntarr[n] = print_queue_status(i, &queue[n],&status[n]);
+ subcnt += subcntarr[n];
+ n++;
+ }
+ }
+ if (mdrcnt > 0) *rdata = REALLOC(*rdata,mdrcnt);
+ desc.base = *rdata;
+ desc.buflen = mdrcnt;
+
+ if (init_package(&desc,queuecnt,subcnt)) {
+ n = 0;
+ succnt = 0;
+ for (i = 0; i < services; i++)
+ if (lp_snum_ok(i) && lp_print_ok(i) && lp_browseable(i)) {
+ fill_printq_info(conn,i,uLevel,&desc,subcntarr[n],queue[n],&status[n]);
+ n++;
+ if (desc.errcode == NERR_Success) succnt = n;
+ }
+ }
+
+ SAFE_FREE(subcntarr);
+
+ *rdata_len = desc.usedlen;
+ *rparam_len = 8;
+ *rparam = REALLOC(*rparam,*rparam_len);
+ SSVALS(*rparam,0,desc.errcode);
+ SSVAL(*rparam,2,0);
+ SSVAL(*rparam,4,succnt);
+ SSVAL(*rparam,6,queuecnt);
+
+ for (i = 0; i < queuecnt; i++) {
+ if (queue) SAFE_FREE(queue[i]);
+ }
+
+ SAFE_FREE(queue);
+ SAFE_FREE(status);
+
+ return True;
+}
+
+/****************************************************************************
+ get info level for a server list query
+ ****************************************************************************/
+static BOOL check_server_info(int uLevel, char* id)
+{
+ switch( uLevel ) {
+ case 0:
+ if (strcmp(id,"B16") != 0) return False;
+ break;
+ case 1:
+ if (strcmp(id,"B16BBDz") != 0) return False;
+ break;
+ default:
+ return False;
+ }
+ return True;
+}
+
+struct srv_info_struct
+{
+ fstring name;
+ uint32 type;
+ fstring comment;
+ fstring domain;
+ BOOL server_added;
+};
+
+
+/*******************************************************************
+ get server info lists from the files saved by nmbd. Return the
+ number of entries
+ ******************************************************************/
+static int get_server_info(uint32 servertype,
+ struct srv_info_struct **servers,
+ const char *domain)
+{
+ int count=0;
+ int alloced=0;
+ char **lines;
+ BOOL local_list_only;
+ int i;
+
+ lines = file_lines_load(lock_path(SERVER_LIST), NULL);
+ if (!lines) {
+ DEBUG(4,("Can't open %s - %s\n",lock_path(SERVER_LIST),strerror(errno)));
+ return(0);
+ }
+
+ /* request for everything is code for request all servers */
+ if (servertype == SV_TYPE_ALL)
+ servertype &= ~(SV_TYPE_DOMAIN_ENUM|SV_TYPE_LOCAL_LIST_ONLY);
+
+ local_list_only = (servertype & SV_TYPE_LOCAL_LIST_ONLY);
+
+ DEBUG(4,("Servertype search: %8x\n",servertype));
+
+ for (i=0;lines[i];i++) {
+ fstring stype;
+ struct srv_info_struct *s;
+ const char *ptr = lines[i];
+ BOOL ok = True;
+
+ if (!*ptr) continue;
+
+ if (count == alloced) {
+ struct srv_info_struct *ts;
+
+ alloced += 10;
+ ts = (struct srv_info_struct *)
+ Realloc(*servers,sizeof(**servers)*alloced);
+ if (!ts) {
+ DEBUG(0,("get_server_info: failed to enlarge servers info struct!\n"));
+ return(0);
+ }
+ else *servers = ts;
+ memset((char *)((*servers)+count),'\0',sizeof(**servers)*(alloced-count));
+ }
+ s = &(*servers)[count];
+
+ if (!next_token(&ptr,s->name , NULL, sizeof(s->name))) continue;
+ if (!next_token(&ptr,stype , NULL, sizeof(stype))) continue;
+ if (!next_token(&ptr,s->comment, NULL, sizeof(s->comment))) continue;
+ if (!next_token(&ptr,s->domain , NULL, sizeof(s->domain))) {
+ /* this allows us to cope with an old nmbd */
+ fstrcpy(s->domain,lp_workgroup());
+ }
+
+ if (sscanf(stype,"%X",&s->type) != 1) {
+ DEBUG(4,("r:host file "));
+ ok = False;
+ }
+
+ /* Filter the servers/domains we return based on what was asked for. */
+
+ /* Check to see if we are being asked for a local list only. */
+ if(local_list_only && ((s->type & SV_TYPE_LOCAL_LIST_ONLY) == 0)) {
+ DEBUG(4,("r: local list only"));
+ ok = False;
+ }
+
+ /* doesn't match up: don't want it */
+ if (!(servertype & s->type)) {
+ DEBUG(4,("r:serv type "));
+ ok = False;
+ }
+
+ if ((servertype & SV_TYPE_DOMAIN_ENUM) !=
+ (s->type & SV_TYPE_DOMAIN_ENUM))
+ {
+ DEBUG(4,("s: dom mismatch "));
+ ok = False;
+ }
+
+ if (!strequal(domain, s->domain) && !(servertype & SV_TYPE_DOMAIN_ENUM))
+ {
+ ok = False;
+ }
+
+ /* We should never return a server type with a SV_TYPE_LOCAL_LIST_ONLY set. */
+ s->type &= ~SV_TYPE_LOCAL_LIST_ONLY;
+
+ if (ok)
+ {
+ DEBUG(4,("**SV** %20s %8x %25s %15s\n",
+ s->name, s->type, s->comment, s->domain));
+
+ s->server_added = True;
+ count++;
+ }
+ else
+ {
+ DEBUG(4,("%20s %8x %25s %15s\n",
+ s->name, s->type, s->comment, s->domain));
+ }
+ }
+
+ file_lines_free(lines);
+ return(count);
+}
+
+
+/*******************************************************************
+ fill in a server info structure
+ ******************************************************************/
+static int fill_srv_info(struct srv_info_struct *service,
+ int uLevel, char **buf, int *buflen,
+ char **stringbuf, int *stringspace, char *baseaddr)
+{
+ int struct_len;
+ char* p;
+ char* p2;
+ int l2;
+ int len;
+
+ switch (uLevel) {
+ case 0: struct_len = 16; break;
+ case 1: struct_len = 26; break;
+ default: return -1;
+ }
+
+ if (!buf)
+ {
+ len = 0;
+ switch (uLevel)
+ {
+ case 1:
+ len = strlen(service->comment)+1;
+ break;
+ }
+
+ if (buflen) *buflen = struct_len;
+ if (stringspace) *stringspace = len;
+ return struct_len + len;
+ }
+
+ len = struct_len;
+ p = *buf;
+ if (*buflen < struct_len) return -1;
+ if (stringbuf)
+ {
+ p2 = *stringbuf;
+ l2 = *stringspace;
+ }
+ else
+ {
+ p2 = p + struct_len;
+ l2 = *buflen - struct_len;
+ }
+ if (!baseaddr) baseaddr = p;
+
+ switch (uLevel)
+ {
+ case 0:
+ push_ascii(p,service->name, 15, STR_TERMINATE);
+ break;
+
+ case 1:
+ push_ascii(p,service->name,15, STR_TERMINATE);
+ SIVAL(p,18,service->type);
+ SIVAL(p,22,PTR_DIFF(p2,baseaddr));
+ len += CopyAndAdvance(&p2,service->comment,&l2);
+ break;
+ }
+
+ if (stringbuf)
+ {
+ *buf = p + struct_len;
+ *buflen -= struct_len;
+ *stringbuf = p2;
+ *stringspace = l2;
+ }
+ else
+ {
+ *buf = p2;
+ *buflen -= len;
+ }
+ return len;
+}
+
+
+static BOOL srv_comp(struct srv_info_struct *s1,struct srv_info_struct *s2)
+{
+ return(strcmp(s1->name,s2->name));
+}
+
+/****************************************************************************
+ view list of servers available (or possibly domains). The info is
+ extracted from lists saved by nmbd on the local host
+ ****************************************************************************/
+static BOOL api_RNetServerEnum(connection_struct *conn, uint16 vuid, char *param, char *data,
+ int mdrcnt, int mprcnt, char **rdata,
+ char **rparam, int *rdata_len, int *rparam_len)
+{
+ char *str1 = param+2;
+ char *str2 = skip_string(str1,1);
+ char *p = skip_string(str2,1);
+ int uLevel = SVAL(p,0);
+ int buf_len = SVAL(p,2);
+ uint32 servertype = IVAL(p,4);
+ char *p2;
+ int data_len, fixed_len, string_len;
+ int f_len = 0, s_len = 0;
+ struct srv_info_struct *servers=NULL;
+ int counted=0,total=0;
+ int i,missed;
+ fstring domain;
+ BOOL domain_request;
+ BOOL local_request;
+
+ /* If someone sets all the bits they don't really mean to set
+ DOMAIN_ENUM and LOCAL_LIST_ONLY, they just want all the
+ known servers. */
+
+ if (servertype == SV_TYPE_ALL)
+ servertype &= ~(SV_TYPE_DOMAIN_ENUM|SV_TYPE_LOCAL_LIST_ONLY);
+
+ /* If someone sets SV_TYPE_LOCAL_LIST_ONLY but hasn't set
+ any other bit (they may just set this bit on it's own) they
+ want all the locally seen servers. However this bit can be
+ set on its own so set the requested servers to be
+ ALL - DOMAIN_ENUM. */
+
+ if ((servertype & SV_TYPE_LOCAL_LIST_ONLY) && !(servertype & SV_TYPE_DOMAIN_ENUM))
+ servertype = SV_TYPE_ALL & ~(SV_TYPE_DOMAIN_ENUM);
+
+ domain_request = ((servertype & SV_TYPE_DOMAIN_ENUM) != 0);
+ local_request = ((servertype & SV_TYPE_LOCAL_LIST_ONLY) != 0);
+
+ p += 8;
+
+ if (!prefix_ok(str1,"WrLehD")) return False;
+ if (!check_server_info(uLevel,str2)) return False;
+
+ DEBUG(4, ("server request level: %s %8x ", str2, servertype));
+ DEBUG(4, ("domains_req:%s ", BOOLSTR(domain_request)));
+ DEBUG(4, ("local_only:%s\n", BOOLSTR(local_request)));
+
+ if (strcmp(str1, "WrLehDz") == 0) {
+ pull_ascii_fstring(domain, p);
+ } else {
+ fstrcpy(domain, lp_workgroup());
+ }
+
+ if (lp_browse_list())
+ total = get_server_info(servertype,&servers,domain);
+
+ data_len = fixed_len = string_len = 0;
+ missed = 0;
+
+ if (total > 0)
+ qsort(servers,total,sizeof(servers[0]),QSORT_CAST srv_comp);
+
+ {
+ char *lastname=NULL;
+
+ for (i=0;i<total;i++)
+ {
+ struct srv_info_struct *s = &servers[i];
+ if (lastname && strequal(lastname,s->name)) continue;
+ lastname = s->name;
+ data_len += fill_srv_info(s,uLevel,0,&f_len,0,&s_len,0);
+ DEBUG(4,("fill_srv_info %20s %8x %25s %15s\n",
+ s->name, s->type, s->comment, s->domain));
+
+ if (data_len <= buf_len) {
+ counted++;
+ fixed_len += f_len;
+ string_len += s_len;
+ } else {
+ missed++;
+ }
+ }
+ }
+
+ *rdata_len = fixed_len + string_len;
+ *rdata = REALLOC(*rdata,*rdata_len);
+ memset(*rdata,'\0',*rdata_len);
+
+ p2 = (*rdata) + fixed_len; /* auxilliary data (strings) will go here */
+ p = *rdata;
+ f_len = fixed_len;
+ s_len = string_len;
+
+ {
+ char *lastname=NULL;
+ int count2 = counted;
+ for (i = 0; i < total && count2;i++)
+ {
+ struct srv_info_struct *s = &servers[i];
+ if (lastname && strequal(lastname,s->name)) continue;
+ lastname = s->name;
+ fill_srv_info(s,uLevel,&p,&f_len,&p2,&s_len,*rdata);
+ DEBUG(4,("fill_srv_info %20s %8x %25s %15s\n",
+ s->name, s->type, s->comment, s->domain));
+ count2--;
+ }
+ }
+
+ *rparam_len = 8;
+ *rparam = REALLOC(*rparam,*rparam_len);
+ SSVAL(*rparam,0,(missed == 0 ? NERR_Success : ERRmoredata));
+ SSVAL(*rparam,2,0);
+ SSVAL(*rparam,4,counted);
+ SSVAL(*rparam,6,counted+missed);
+
+ SAFE_FREE(servers);
+
+ DEBUG(3,("NetServerEnum domain = %s uLevel=%d counted=%d total=%d\n",
+ domain,uLevel,counted,counted+missed));
+
+ return(True);
+}
+
+/****************************************************************************
+ command 0x34 - suspected of being a "Lookup Names" stub api
+ ****************************************************************************/
+static BOOL api_RNetGroupGetUsers(connection_struct *conn, uint16 vuid, char *param, char *data,
+ int mdrcnt, int mprcnt, char **rdata,
+ char **rparam, int *rdata_len, int *rparam_len)
+{
+ char *str1 = param+2;
+ char *str2 = skip_string(str1,1);
+ char *p = skip_string(str2,1);
+ int uLevel = SVAL(p,0);
+ int buf_len = SVAL(p,2);
+ int counted=0;
+ int missed=0;
+
+ DEBUG(5,("RNetGroupGetUsers: %s %s %s %d %d\n",
+ str1, str2, p, uLevel, buf_len));
+
+ if (!prefix_ok(str1,"zWrLeh")) return False;
+
+ *rdata_len = 0;
+
+ *rparam_len = 8;
+ *rparam = REALLOC(*rparam,*rparam_len);
+
+ SSVAL(*rparam,0,0x08AC); /* informational warning message */
+ SSVAL(*rparam,2,0);
+ SSVAL(*rparam,4,counted);
+ SSVAL(*rparam,6,counted+missed);
+
+ return(True);
+}
+
+/****************************************************************************
+ get info about a share
+ ****************************************************************************/
+static BOOL check_share_info(int uLevel, char* id)
+{
+ switch( uLevel ) {
+ case 0:
+ if (strcmp(id,"B13") != 0) return False;
+ break;
+ case 1:
+ if (strcmp(id,"B13BWz") != 0) return False;
+ break;
+ case 2:
+ if (strcmp(id,"B13BWzWWWzB9B") != 0) return False;
+ break;
+ case 91:
+ if (strcmp(id,"B13BWzWWWzB9BB9BWzWWzWW") != 0) return False;
+ break;
+ default: return False;
+ }
+ return True;
+}
+
+static int fill_share_info(connection_struct *conn, int snum, int uLevel,
+ char** buf, int* buflen,
+ char** stringbuf, int* stringspace, char* baseaddr)
+{
+ int struct_len;
+ char* p;
+ char* p2;
+ int l2;
+ int len;
+
+ switch( uLevel ) {
+ case 0: struct_len = 13; break;
+ case 1: struct_len = 20; break;
+ case 2: struct_len = 40; break;
+ case 91: struct_len = 68; break;
+ default: return -1;
+ }
+
+
+ if (!buf)
+ {
+ len = 0;
+ if (uLevel > 0) len += StrlenExpanded(conn,snum,lp_comment(snum));
+ if (uLevel > 1) len += strlen(lp_pathname(snum)) + 1;
+ if (buflen) *buflen = struct_len;
+ if (stringspace) *stringspace = len;
+ return struct_len + len;
+ }
+
+ len = struct_len;
+ p = *buf;
+ if ((*buflen) < struct_len) return -1;
+ if (stringbuf)
+ {
+ p2 = *stringbuf;
+ l2 = *stringspace;
+ }
+ else
+ {
+ p2 = p + struct_len;
+ l2 = (*buflen) - struct_len;
+ }
+ if (!baseaddr) baseaddr = p;
+
+ push_ascii(p,lp_servicename(snum),13, STR_TERMINATE);
+
+ if (uLevel > 0)
+ {
+ int type;
+ SCVAL(p,13,0);
+ type = STYPE_DISKTREE;
+ if (lp_print_ok(snum)) type = STYPE_PRINTQ;
+ if (strequal("IPC",lp_fstype(snum))) type = STYPE_IPC;
+ SSVAL(p,14,type); /* device type */
+ SIVAL(p,16,PTR_DIFF(p2,baseaddr));
+ len += CopyExpanded(conn,snum,&p2,lp_comment(snum),&l2);
+ }
+
+ if (uLevel > 1)
+ {
+ SSVAL(p,20,ACCESS_READ|ACCESS_WRITE|ACCESS_CREATE); /* permissions */
+ SSVALS(p,22,-1); /* max uses */
+ SSVAL(p,24,1); /* current uses */
+ SIVAL(p,26,PTR_DIFF(p2,baseaddr)); /* local pathname */
+ len += CopyAndAdvance(&p2,lp_pathname(snum),&l2);
+ memset(p+30,0,SHPWLEN+2); /* passwd (reserved), pad field */
+ }
+
+ if (uLevel > 2)
+ {
+ memset(p+40,0,SHPWLEN+2);
+ SSVAL(p,50,0);
+ SIVAL(p,52,0);
+ SSVAL(p,56,0);
+ SSVAL(p,58,0);
+ SIVAL(p,60,0);
+ SSVAL(p,64,0);
+ SSVAL(p,66,0);
+ }
+
+ if (stringbuf)
+ {
+ (*buf) = p + struct_len;
+ (*buflen) -= struct_len;
+ (*stringbuf) = p2;
+ (*stringspace) = l2;
+ }
+ else
+ {
+ (*buf) = p2;
+ (*buflen) -= len;
+ }
+ return len;
+}
+
+static BOOL api_RNetShareGetInfo(connection_struct *conn,uint16 vuid, char *param,char *data,
+ int mdrcnt,int mprcnt,
+ char **rdata,char **rparam,
+ int *rdata_len,int *rparam_len)
+{
+ char *str1 = param+2;
+ char *str2 = skip_string(str1,1);
+ char *netname = skip_string(str2,1);
+ char *p = skip_string(netname,1);
+ int uLevel = SVAL(p,0);
+ int snum = find_service(netname);
+
+ if (snum < 0) return False;
+
+ /* check it's a supported varient */
+ if (!prefix_ok(str1,"zWrLh")) return False;
+ if (!check_share_info(uLevel,str2)) return False;
+
+ *rdata = REALLOC(*rdata,mdrcnt);
+ p = *rdata;
+ *rdata_len = fill_share_info(conn,snum,uLevel,&p,&mdrcnt,0,0,0);
+ if (*rdata_len < 0) return False;
+
+ *rparam_len = 6;
+ *rparam = REALLOC(*rparam,*rparam_len);
+ SSVAL(*rparam,0,NERR_Success);
+ SSVAL(*rparam,2,0); /* converter word */
+ SSVAL(*rparam,4,*rdata_len);
+
+ return(True);
+}
+
+/****************************************************************************
+ View the list of available shares.
+
+ This function is the server side of the NetShareEnum() RAP call.
+ It fills the return buffer with share names and share comments.
+ Note that the return buffer normally (in all known cases) allows only
+ twelve byte strings for share names (plus one for a nul terminator).
+ Share names longer than 12 bytes must be skipped.
+ ****************************************************************************/
+static BOOL api_RNetShareEnum( connection_struct *conn,
+ uint16 vuid,
+ char *param,
+ char *data,
+ int mdrcnt,
+ int mprcnt,
+ char **rdata,
+ char **rparam,
+ int *rdata_len,
+ int *rparam_len )
+{
+ char *str1 = param+2;
+ char *str2 = skip_string(str1,1);
+ char *p = skip_string(str2,1);
+ int uLevel = SVAL(p,0);
+ int buf_len = SVAL(p,2);
+ char *p2;
+ int count=lp_numservices();
+ int total=0,counted=0;
+ BOOL missed = False;
+ int i;
+ int data_len, fixed_len, string_len;
+ int f_len = 0, s_len = 0;
+
+ if (!prefix_ok(str1,"WrLeh")) return False;
+ if (!check_share_info(uLevel,str2)) return False;
+
+ data_len = fixed_len = string_len = 0;
+ for (i=0;i<count;i++)
+ if( lp_browseable( i )
+ && lp_snum_ok( i )
+ && (strlen( lp_servicename( i ) ) < 13) ) /* Maximum name length. */
+ {
+ total++;
+ data_len += fill_share_info(conn,i,uLevel,0,&f_len,0,&s_len,0);
+ if (data_len <= buf_len)
+ {
+ counted++;
+ fixed_len += f_len;
+ string_len += s_len;
+ }
+ else
+ missed = True;
+ }
+ *rdata_len = fixed_len + string_len;
+ *rdata = REALLOC(*rdata,*rdata_len);
+ memset(*rdata,0,*rdata_len);
+
+ p2 = (*rdata) + fixed_len; /* auxiliary data (strings) will go here */
+ p = *rdata;
+ f_len = fixed_len;
+ s_len = string_len;
+ for( i = 0; i < count; i++ )
+ {
+ if( lp_browseable( i )
+ && lp_snum_ok( i )
+ && (strlen( lp_servicename( i ) ) < 13) )
+ {
+ if( fill_share_info( conn,i,uLevel,&p,&f_len,&p2,&s_len,*rdata ) < 0 )
+ break;
+ }
+ }
+
+ *rparam_len = 8;
+ *rparam = REALLOC(*rparam,*rparam_len);
+ SSVAL(*rparam,0,missed ? ERRmoredata : NERR_Success);
+ SSVAL(*rparam,2,0);
+ SSVAL(*rparam,4,counted);
+ SSVAL(*rparam,6,total);
+
+ DEBUG(3,("RNetShareEnum gave %d entries of %d (%d %d %d %d)\n",
+ counted,total,uLevel,
+ buf_len,*rdata_len,mdrcnt));
+ return(True);
+} /* api_RNetShareEnum */
+
+/****************************************************************************
+ Add a share
+ ****************************************************************************/
+static BOOL api_RNetShareAdd(connection_struct *conn,uint16 vuid, char *param,char *data,
+ int mdrcnt,int mprcnt,
+ char **rdata,char **rparam,
+ int *rdata_len,int *rparam_len)
+{
+ char *str1 = param+2;
+ char *str2 = skip_string(str1,1);
+ char *p = skip_string(str2,1);
+ int uLevel = SVAL(p,0);
+ fstring sharename;
+ fstring comment;
+ pstring pathname;
+ char *command, *cmdname;
+ unsigned int offset;
+ int snum;
+ int res = ERRunsup;
+
+ /* check it's a supported varient */
+ if (!prefix_ok(str1, RAP_WShareAdd_REQ)) return False;
+ if (!check_share_info(uLevel, str2)) return False;
+ if (uLevel != 2) return False;
+
+ pull_ascii_fstring(sharename, data);
+ snum = find_service(sharename);
+ if (snum >= 0) { /* already exists */
+ res = ERRfilexists;
+ goto error_exit;
+ }
+
+ /* only support disk share adds */
+ if (SVAL(data,14) != STYPE_DISKTREE) return False;
+
+ offset = IVAL(data, 16);
+ if (offset >= mdrcnt) {
+ res = ERRinvalidparam;
+ goto error_exit;
+ }
+ pull_ascii_fstring(comment, offset? (data+offset) : "");
+
+ offset = IVAL(data, 26);
+ if (offset >= mdrcnt) {
+ res = ERRinvalidparam;
+ goto error_exit;
+ }
+ pull_ascii_pstring(pathname, offset? (data+offset) : "");
+
+ string_replace(sharename, '"', ' ');
+ string_replace(pathname, '"', ' ');
+ string_replace(comment, '"', ' ');
+
+ cmdname = lp_add_share_cmd();
+
+ if (!cmdname || *cmdname == '\0') return False;
+
+ asprintf(&command, "%s \"%s\" \"%s\" \"%s\" \"%s\"",
+ lp_add_share_cmd(), dyn_CONFIGFILE, sharename, pathname, comment);
+
+ if (command) {
+ DEBUG(10,("api_RNetShareAdd: Running [%s]\n", command ));
+ if ((res = smbrun(command, NULL)) != 0) {
+ DEBUG(1,("api_RNetShareAdd: Running [%s] returned (%d)\n", command, res ));
+ SAFE_FREE(command);
+ res = ERRnoaccess;
+ goto error_exit;
+ } else {
+ SAFE_FREE(command);
+ message_send_all(conn_tdb_ctx(), MSG_SMB_CONF_UPDATED, NULL, 0, False, NULL);
+ }
+ } else return False;
+
+ *rparam_len = 6;
+ *rparam = REALLOC(*rparam, *rparam_len);
+ SSVAL(*rparam, 0, NERR_Success);
+ SSVAL(*rparam, 2, 0); /* converter word */
+ SSVAL(*rparam, 4, *rdata_len);
+ *rdata_len = 0;
+
+ return True;
+
+error_exit:
+ *rparam_len = 4;
+ *rparam = REALLOC(*rparam, *rparam_len);
+ *rdata_len = 0;
+ SSVAL(*rparam, 0, res);
+ SSVAL(*rparam, 2, 0);
+ return True;
+
+}
+
+/****************************************************************************
+ view list of groups available
+ ****************************************************************************/
+static BOOL api_RNetGroupEnum(connection_struct *conn,uint16 vuid, char *param,char *data,
+ int mdrcnt,int mprcnt,
+ char **rdata,char **rparam,
+ int *rdata_len,int *rparam_len)
+{
+ int i;
+ int errflags=0;
+ int resume_context, cli_buf_size;
+ char *str1 = param+2;
+ char *str2 = skip_string(str1,1);
+ char *p = skip_string(str2,1);
+ BOOL ret;
+
+ GROUP_MAP *group_list;
+ int num_entries;
+
+ if (strcmp(str1,"WrLeh") != 0)
+ return False;
+
+ /* parameters
+ * W-> resume context (number of users to skip)
+ * r -> return parameter pointer to receive buffer
+ * L -> length of receive buffer
+ * e -> return parameter number of entries
+ * h -> return parameter total number of users
+ */
+ if (strcmp("B21",str2) != 0)
+ return False;
+
+ /* get list of domain groups SID_DOMAIN_GRP=2 */
+ become_root();
+ ret = pdb_enum_group_mapping(SID_NAME_DOM_GRP , &group_list, &num_entries, False);
+ unbecome_root();
+
+ if( !ret ) {
+ DEBUG(3,("api_RNetGroupEnum:failed to get group list"));
+ return False;
+ }
+
+ resume_context = SVAL(p,0);
+ cli_buf_size=SVAL(p+2,0);
+ DEBUG(10,("api_RNetGroupEnum:resume context: %d, client buffer size: %d\n", resume_context, cli_buf_size));
+
+ *rdata_len = cli_buf_size;
+ *rdata = REALLOC(*rdata,*rdata_len);
+
+ p = *rdata;
+
+ for(i=resume_context; i<num_entries; i++) {
+ char* name=group_list[i].nt_name;
+ if( ((PTR_DIFF(p,*rdata)+21) <= *rdata_len) ) {
+ /* truncate the name at 21 chars. */
+ memcpy(p, name, 21);
+ DEBUG(10,("adding entry %d group %s\n", i, p));
+ p += 21;
+ } else {
+ /* set overflow error */
+ DEBUG(3,("overflow on entry %d group %s\n", i, name));
+ errflags=234;
+ break;
+ }
+ }
+
+ *rdata_len = PTR_DIFF(p,*rdata);
+
+ *rparam_len = 8;
+ *rparam = REALLOC(*rparam,*rparam_len);
+
+ SSVAL(*rparam, 0, errflags);
+ SSVAL(*rparam, 2, 0); /* converter word */
+ SSVAL(*rparam, 4, i-resume_context); /* is this right?? */
+ SSVAL(*rparam, 6, num_entries); /* is this right?? */
+
+ return(True);
+}
+
+/*******************************************************************
+ get groups that a user is a member of
+ ******************************************************************/
+static BOOL api_NetUserGetGroups(connection_struct *conn,uint16 vuid, char *param,char *data,
+ int mdrcnt,int mprcnt,
+ char **rdata,char **rparam,
+ int *rdata_len,int *rparam_len)
+{
+ char *str1 = param+2;
+ char *str2 = skip_string(str1,1);
+ char *UserName = skip_string(str2,1);
+ char *p = skip_string(UserName,1);
+ int uLevel = SVAL(p,0);
+ const char *level_string;
+ int count=0;
+ SAM_ACCOUNT *sampw = NULL;
+ BOOL ret = False;
+ DOM_GID *gids = NULL;
+ int num_groups = 0;
+ int i;
+ fstring grp_domain;
+ fstring grp_name;
+ enum SID_NAME_USE grp_type;
+ DOM_SID sid, dom_sid;
+
+ *rparam_len = 8;
+ *rparam = REALLOC(*rparam,*rparam_len);
+
+ /* check it's a supported varient */
+
+ if ( strcmp(str1,"zWrLeh") != 0 )
+ return False;
+
+ switch( uLevel ) {
+ case 0:
+ level_string = "B21";
+ break;
+ default:
+ return False;
+ }
+
+ if (strcmp(level_string,str2) != 0)
+ return False;
+
+ *rdata_len = mdrcnt + 1024;
+ *rdata = REALLOC(*rdata,*rdata_len);
+
+ SSVAL(*rparam,0,NERR_Success);
+ SSVAL(*rparam,2,0); /* converter word */
+
+ p = *rdata;
+
+ /* Lookup the user information; This should only be one of
+ our accounts (not remote domains) */
+
+ pdb_init_sam( &sampw );
+
+ become_root(); /* ROOT BLOCK */
+
+ if ( !pdb_getsampwnam(sampw, UserName) )
+ goto out;
+
+ /* this next set of code is horribly inefficient, but since
+ it is rarely called, I'm going to leave it like this since
+ it easier to follow --jerry */
+
+ /* get the list of group SIDs */
+
+ if ( !get_domain_user_groups(conn->mem_ctx, &num_groups, &gids, sampw) ) {
+ DEBUG(1,("api_NetUserGetGroups: get_domain_user_groups() failed!\n"));
+ goto out;
+ }
+
+ /* convert to names (we don't support universal groups so the domain
+ can only be ours) */
+
+ sid_copy( &dom_sid, get_global_sam_sid() );
+ for (i=0; i<num_groups; i++) {
+
+ /* make the DOM_GID into a DOM_SID and then lookup
+ the name */
+
+ sid_copy( &sid, &dom_sid );
+ sid_append_rid( &sid, gids[i].g_rid );
+
+ if ( lookup_sid(&sid, grp_domain, grp_name, &grp_type) ) {
+ pstrcpy(p, grp_name);
+ p += 21;
+ count++;
+ }
+ }
+
+ *rdata_len = PTR_DIFF(p,*rdata);
+
+ SSVAL(*rparam,4,count); /* is this right?? */
+ SSVAL(*rparam,6,count); /* is this right?? */
+
+ ret = True;
+
+out:
+ unbecome_root(); /* END ROOT BLOCK */
+
+ pdb_free_sam( &sampw );
+
+ return ret;
+}
+
+/*******************************************************************
+ get all users
+ ******************************************************************/
+static BOOL api_RNetUserEnum(connection_struct *conn,uint16 vuid, char *param,char *data,
+ int mdrcnt,int mprcnt,
+ char **rdata,char **rparam,
+ int *rdata_len,int *rparam_len)
+{
+ SAM_ACCOUNT *pwd=NULL;
+ int count_sent=0;
+ int count_total=0;
+ int errflags=0;
+ int resume_context, cli_buf_size;
+
+ char *str1 = param+2;
+ char *str2 = skip_string(str1,1);
+ char *p = skip_string(str2,1);
+
+ if (strcmp(str1,"WrLeh") != 0)
+ return False;
+ /* parameters
+ * W-> resume context (number of users to skip)
+ * r -> return parameter pointer to receive buffer
+ * L -> length of receive buffer
+ * e -> return parameter number of entries
+ * h -> return parameter total number of users
+ */
+
+ resume_context = SVAL(p,0);
+ cli_buf_size=SVAL(p+2,0);
+ DEBUG(10,("api_RNetUserEnum:resume context: %d, client buffer size: %d\n", resume_context, cli_buf_size));
+
+ *rparam_len = 8;
+ *rparam = REALLOC(*rparam,*rparam_len);
+
+ /* check it's a supported varient */
+ if (strcmp("B21",str2) != 0)
+ return False;
+
+ *rdata_len = cli_buf_size;
+ *rdata = REALLOC(*rdata,*rdata_len);
+
+ p = *rdata;
+
+ /* to get user list enumerations for NetUserEnum in B21 format */
+ pdb_init_sam(&pwd);
+
+ /* Open the passgrp file - not for update. */
+ become_root();
+ if(!pdb_setsampwent(False)) {
+ DEBUG(0, ("api_RNetUserEnum:unable to open sam database.\n"));
+ unbecome_root();
+ return False;
+ }
+ errflags=NERR_Success;
+
+ while ( pdb_getsampwent(pwd) ) {
+ const char *name=pdb_get_username(pwd);
+ if ((name) && (*(name+strlen(name)-1)!='$')) {
+ count_total++;
+ if(count_total>=resume_context) {
+ if( ((PTR_DIFF(p,*rdata)+21)<=*rdata_len)&&(strlen(name)<=21) ) {
+ pstrcpy(p,name);
+ DEBUG(10,("api_RNetUserEnum:adding entry %d username %s\n",count_sent,p));
+ p += 21;
+ count_sent++;
+ } else {
+ /* set overflow error */
+ DEBUG(10,("api_RNetUserEnum:overflow on entry %d username %s\n",count_sent,name));
+ errflags=234;
+ break;
+ }
+ }
+ }
+ } ;
+
+ pdb_endsampwent();
+ unbecome_root();
+
+ pdb_free_sam(&pwd);
+
+ *rdata_len = PTR_DIFF(p,*rdata);
+
+ SSVAL(*rparam,0,errflags);
+ SSVAL(*rparam,2,0); /* converter word */
+ SSVAL(*rparam,4,count_sent); /* is this right?? */
+ SSVAL(*rparam,6,count_total); /* is this right?? */
+
+ return True;
+}
+
+
+
+/****************************************************************************
+ get the time of day info
+ ****************************************************************************/
+static BOOL api_NetRemoteTOD(connection_struct *conn,uint16 vuid, char *param,char *data,
+ int mdrcnt,int mprcnt,
+ char **rdata,char **rparam,
+ int *rdata_len,int *rparam_len)
+{
+ char *p;
+ *rparam_len = 4;
+ *rparam = REALLOC(*rparam,*rparam_len);
+
+ *rdata_len = 21;
+ *rdata = REALLOC(*rdata,*rdata_len);
+
+ SSVAL(*rparam,0,NERR_Success);
+ SSVAL(*rparam,2,0); /* converter word */
+
+ p = *rdata;
+
+ {
+ struct tm *t;
+ time_t unixdate = time(NULL);
+
+ put_dos_date3(p,0,unixdate); /* this is the time that is looked at
+ by NT in a "net time" operation,
+ it seems to ignore the one below */
+
+ /* the client expects to get localtime, not GMT, in this bit
+ (I think, this needs testing) */
+ t = LocalTime(&unixdate);
+
+ SIVAL(p,4,0); /* msecs ? */
+ SCVAL(p,8,t->tm_hour);
+ SCVAL(p,9,t->tm_min);
+ SCVAL(p,10,t->tm_sec);
+ SCVAL(p,11,0); /* hundredths of seconds */
+ SSVALS(p,12,TimeDiff(unixdate)/60); /* timezone in minutes from GMT */
+ SSVAL(p,14,10000); /* timer interval in 0.0001 of sec */
+ SCVAL(p,16,t->tm_mday);
+ SCVAL(p,17,t->tm_mon + 1);
+ SSVAL(p,18,1900+t->tm_year);
+ SCVAL(p,20,t->tm_wday);
+ }
+
+
+ return(True);
+}
+
+/****************************************************************************
+ Set the user password.
+*****************************************************************************/
+
+static BOOL api_SetUserPassword(connection_struct *conn,uint16 vuid, char *param,char *data,
+ int mdrcnt,int mprcnt,
+ char **rdata,char **rparam,
+ int *rdata_len,int *rparam_len)
+{
+ char *p = skip_string(param+2,2);
+ fstring user;
+ fstring pass1,pass2;
+
+ pull_ascii_fstring(user,p);
+
+ p = skip_string(p,1);
+
+ memset(pass1,'\0',sizeof(pass1));
+ memset(pass2,'\0',sizeof(pass2));
+ memcpy(pass1,p,16);
+ memcpy(pass2,p+16,16);
+
+ *rparam_len = 4;
+ *rparam = REALLOC(*rparam,*rparam_len);
+
+ *rdata_len = 0;
+
+ SSVAL(*rparam,0,NERR_badpass);
+ SSVAL(*rparam,2,0); /* converter word */
+
+ DEBUG(3,("Set password for <%s>\n",user));
+
+ /*
+ * Attempt to verify the old password against smbpasswd entries
+ * Win98 clients send old and new password in plaintext for this call.
+ */
+
+ {
+ auth_serversupplied_info *server_info = NULL;
+ DATA_BLOB password = data_blob(pass1, strlen(pass1)+1);
+
+ if (NT_STATUS_IS_OK(check_plaintext_password(user,password,&server_info))) {
+
+ become_root();
+ if (NT_STATUS_IS_OK(change_oem_password(server_info->sam_account, pass1, pass2, False))) {
+ SSVAL(*rparam,0,NERR_Success);
+ }
+ unbecome_root();
+
+ free_server_info(&server_info);
+ }
+ data_blob_clear_free(&password);
+ }
+
+ /*
+ * If the plaintext change failed, attempt
+ * the old encrypted method. NT will generate this
+ * after trying the samr method. Note that this
+ * method is done as a last resort as this
+ * password change method loses the NT password hash
+ * and cannot change the UNIX password as no plaintext
+ * is received.
+ */
+
+ if(SVAL(*rparam,0) != NERR_Success) {
+ SAM_ACCOUNT *hnd = NULL;
+
+ if (check_lanman_password(user,(unsigned char *)pass1,(unsigned char *)pass2, &hnd)) {
+ become_root();
+ if (change_lanman_password(hnd,(uchar *)pass2)) {
+ SSVAL(*rparam,0,NERR_Success);
+ }
+ unbecome_root();
+ pdb_free_sam(&hnd);
+ }
+ }
+
+ memset((char *)pass1,'\0',sizeof(fstring));
+ memset((char *)pass2,'\0',sizeof(fstring));
+
+ return(True);
+}
+
+/****************************************************************************
+ Set the user password (SamOEM version - gets plaintext).
+****************************************************************************/
+
+static BOOL api_SamOEMChangePassword(connection_struct *conn,uint16 vuid, char *param,char *data,
+ int mdrcnt,int mprcnt,
+ char **rdata,char **rparam,
+ int *rdata_len,int *rparam_len)
+{
+ fstring user;
+ char *p = param + 2;
+ *rparam_len = 2;
+ *rparam = REALLOC(*rparam,*rparam_len);
+
+ *rdata_len = 0;
+
+ SSVAL(*rparam,0,NERR_badpass);
+
+ /*
+ * Check the parameter definition is correct.
+ */
+
+ if(!strequal(param + 2, "zsT")) {
+ DEBUG(0,("api_SamOEMChangePassword: Invalid parameter string %s\n", param + 2));
+ return False;
+ }
+ p = skip_string(p, 1);
+
+ if(!strequal(p, "B516B16")) {
+ DEBUG(0,("api_SamOEMChangePassword: Invalid data parameter string %s\n", p));
+ return False;
+ }
+ p = skip_string(p,1);
+ p += pull_ascii_fstring(user,p);
+
+ DEBUG(3,("api_SamOEMChangePassword: Change password for <%s>\n",user));
+
+ /*
+ * Pass the user through the NT -> unix user mapping
+ * function.
+ */
+
+ (void)map_username(user);
+
+ if (NT_STATUS_IS_OK(pass_oem_change(user, (uchar*) data, (uchar *)&data[516], NULL, NULL))) {
+ SSVAL(*rparam,0,NERR_Success);
+ }
+
+ return(True);
+}
+
+/****************************************************************************
+ delete a print job
+ Form: <W> <>
+ ****************************************************************************/
+static BOOL api_RDosPrintJobDel(connection_struct *conn,uint16 vuid, char *param,char *data,
+ int mdrcnt,int mprcnt,
+ char **rdata,char **rparam,
+ int *rdata_len,int *rparam_len)
+{
+ int function = SVAL(param,0);
+ char *str1 = param+2;
+ char *str2 = skip_string(str1,1);
+ char *p = skip_string(str2,1);
+ uint32 jobid;
+ int snum;
+ int errcode;
+ extern struct current_user current_user;
+ WERROR werr = WERR_OK;
+
+ if(!rap_to_pjobid(SVAL(p,0),&snum,&jobid))
+ return False;
+
+ /* check it's a supported varient */
+ if (!(strcsequal(str1,"W") && strcsequal(str2,"")))
+ return(False);
+
+ *rparam_len = 4;
+ *rparam = REALLOC(*rparam,*rparam_len);
+ *rdata_len = 0;
+
+ if (!print_job_exists(snum, jobid)) {
+ errcode = NERR_JobNotFound;
+ goto out;
+ }
+
+ errcode = NERR_notsupported;
+
+ switch (function) {
+ case 81: /* delete */
+ if (print_job_delete(&current_user, snum, jobid, &werr))
+ errcode = NERR_Success;
+ break;
+ case 82: /* pause */
+ if (print_job_pause(&current_user, snum, jobid, &werr))
+ errcode = NERR_Success;
+ break;
+ case 83: /* resume */
+ if (print_job_resume(&current_user, snum, jobid, &werr))
+ errcode = NERR_Success;
+ break;
+ }
+
+ if (!W_ERROR_IS_OK(werr))
+ errcode = W_ERROR_V(werr);
+
+ out:
+ SSVAL(*rparam,0,errcode);
+ SSVAL(*rparam,2,0); /* converter word */
+
+ return(True);
+}
+
+/****************************************************************************
+ Purge a print queue - or pause or resume it.
+ ****************************************************************************/
+static BOOL api_WPrintQueueCtrl(connection_struct *conn,uint16 vuid, char *param,char *data,
+ int mdrcnt,int mprcnt,
+ char **rdata,char **rparam,
+ int *rdata_len,int *rparam_len)
+{
+ int function = SVAL(param,0);
+ char *str1 = param+2;
+ char *str2 = skip_string(str1,1);
+ char *QueueName = skip_string(str2,1);
+ int errcode = NERR_notsupported;
+ int snum;
+ WERROR werr = WERR_OK;
+ extern struct current_user current_user;
+
+ /* check it's a supported varient */
+ if (!(strcsequal(str1,"z") && strcsequal(str2,"")))
+ return(False);
+
+ *rparam_len = 4;
+ *rparam = REALLOC(*rparam,*rparam_len);
+ *rdata_len = 0;
+
+ snum = print_queue_snum(QueueName);
+
+ if (snum == -1) {
+ errcode = NERR_JobNotFound;
+ goto out;
+ }
+
+ switch (function) {
+ case 74: /* Pause queue */
+ if (print_queue_pause(&current_user, snum, &werr)) errcode = NERR_Success;
+ break;
+ case 75: /* Resume queue */
+ if (print_queue_resume(&current_user, snum, &werr)) errcode = NERR_Success;
+ break;
+ case 103: /* Purge */
+ if (print_queue_purge(&current_user, snum, &werr)) errcode = NERR_Success;
+ break;
+ }
+
+ if (!W_ERROR_IS_OK(werr)) errcode = W_ERROR_V(werr);
+
+ out:
+ SSVAL(*rparam,0,errcode);
+ SSVAL(*rparam,2,0); /* converter word */
+
+ return(True);
+}
+
+
+/****************************************************************************
+ set the property of a print job (undocumented?)
+ ? function = 0xb -> set name of print job
+ ? function = 0x6 -> move print job up/down
+ Form: <WWsTP> <WWzWWDDzzzzzzzzzzlz>
+ or <WWsTP> <WB21BB16B10zWWzDDz>
+****************************************************************************/
+static int check_printjob_info(struct pack_desc* desc,
+ int uLevel, char* id)
+{
+ desc->subformat = NULL;
+ switch( uLevel ) {
+ case 0: desc->format = "W"; break;
+ case 1: desc->format = "WB21BB16B10zWWzDDz"; break;
+ case 2: desc->format = "WWzWWDDzz"; break;
+ case 3: desc->format = "WWzWWDDzzzzzzzzzzlz"; break;
+ case 4: desc->format = "WWzWWDDzzzzzDDDDDDD"; break;
+ default: return False;
+ }
+ if (strcmp(desc->format,id) != 0) return False;
+ return True;
+}
+
+static BOOL api_PrintJobInfo(connection_struct *conn,uint16 vuid,char *param,char *data,
+ int mdrcnt,int mprcnt,
+ char **rdata,char **rparam,
+ int *rdata_len,int *rparam_len)
+{
+ struct pack_desc desc;
+ char *str1 = param+2;
+ char *str2 = skip_string(str1,1);
+ char *p = skip_string(str2,1);
+ uint32 jobid;
+ int snum;
+ int uLevel = SVAL(p,2);
+ int function = SVAL(p,4);
+ int place, errcode;
+
+ if(!rap_to_pjobid(SVAL(p,0),&snum,&jobid))
+ return False;
+ *rparam_len = 4;
+ *rparam = REALLOC(*rparam,*rparam_len);
+
+ *rdata_len = 0;
+
+ /* check it's a supported varient */
+ if ((strcmp(str1,"WWsTP")) ||
+ (!check_printjob_info(&desc,uLevel,str2)))
+ return(False);
+
+ if (!print_job_exists(snum, jobid)) {
+ errcode=NERR_JobNotFound;
+ goto out;
+ }
+
+ errcode = NERR_notsupported;
+
+ switch (function) {
+ case 0x6:
+ /* change job place in the queue,
+ data gives the new place */
+ place = SVAL(data,0);
+ if (print_job_set_place(snum, jobid, place)) {
+ errcode=NERR_Success;
+ }
+ break;
+
+ case 0xb:
+ /* change print job name, data gives the name */
+ if (print_job_set_name(snum, jobid, data)) {
+ errcode=NERR_Success;
+ }
+ break;
+
+ default:
+ return False;
+ }
+
+ out:
+ SSVALS(*rparam,0,errcode);
+ SSVAL(*rparam,2,0); /* converter word */
+
+ return(True);
+}
+
+
+/****************************************************************************
+ get info about the server
+ ****************************************************************************/
+static BOOL api_RNetServerGetInfo(connection_struct *conn,uint16 vuid, char *param,char *data,
+ int mdrcnt,int mprcnt,
+ char **rdata,char **rparam,
+ int *rdata_len,int *rparam_len)
+{
+ char *str1 = param+2;
+ char *str2 = skip_string(str1,1);
+ char *p = skip_string(str2,1);
+ int uLevel = SVAL(p,0);
+ char *p2;
+ int struct_len;
+
+ DEBUG(4,("NetServerGetInfo level %d\n",uLevel));
+
+ /* check it's a supported varient */
+ if (!prefix_ok(str1,"WrLh")) return False;
+ switch( uLevel ) {
+ case 0:
+ if (strcmp(str2,"B16") != 0) return False;
+ struct_len = 16;
+ break;
+ case 1:
+ if (strcmp(str2,"B16BBDz") != 0) return False;
+ struct_len = 26;
+ break;
+ case 2:
+ if (strcmp(str2,"B16BBDzDDDWWzWWWWWWWBB21zWWWWWWWWWWWWWWWWWWWWWWz")
+ != 0) return False;
+ struct_len = 134;
+ break;
+ case 3:
+ if (strcmp(str2,"B16BBDzDDDWWzWWWWWWWBB21zWWWWWWWWWWWWWWWWWWWWWWzDWz")
+ != 0) return False;
+ struct_len = 144;
+ break;
+ case 20:
+ if (strcmp(str2,"DN") != 0) return False;
+ struct_len = 6;
+ break;
+ case 50:
+ if (strcmp(str2,"B16BBDzWWzzz") != 0) return False;
+ struct_len = 42;
+ break;
+ default: return False;
+ }
+
+ *rdata_len = mdrcnt;
+ *rdata = REALLOC(*rdata,*rdata_len);
+
+ p = *rdata;
+ p2 = p + struct_len;
+ if (uLevel != 20) {
+ srvstr_push(NULL, p,local_machine,16,
+ STR_ASCII|STR_UPPER|STR_TERMINATE);
+ }
+ p += 16;
+ if (uLevel > 0)
+ {
+ struct srv_info_struct *servers=NULL;
+ int i,count;
+ pstring comment;
+ uint32 servertype= lp_default_server_announce();
+
+ push_ascii(comment,lp_serverstring(), MAX_SERVER_STRING_LENGTH,STR_TERMINATE);
+
+ if ((count=get_server_info(SV_TYPE_ALL,&servers,lp_workgroup()))>0) {
+ for (i=0;i<count;i++) {
+ if (strequal(servers[i].name,local_machine)) {
+ servertype = servers[i].type;
+ push_ascii(comment,servers[i].comment,sizeof(pstring),STR_TERMINATE);
+ }
+ }
+ }
+ SAFE_FREE(servers);
+
+ SCVAL(p,0,lp_major_announce_version());
+ SCVAL(p,1,lp_minor_announce_version());
+ SIVAL(p,2,servertype);
+
+ if (mdrcnt == struct_len) {
+ SIVAL(p,6,0);
+ } else {
+ SIVAL(p,6,PTR_DIFF(p2,*rdata));
+ standard_sub_conn(conn,comment,sizeof(comment));
+ StrnCpy(p2,comment,MAX(mdrcnt - struct_len,0));
+ p2 = skip_string(p2,1);
+ }
+ }
+ if (uLevel > 1)
+ {
+ return False; /* not yet implemented */
+ }
+
+ *rdata_len = PTR_DIFF(p2,*rdata);
+
+ *rparam_len = 6;
+ *rparam = REALLOC(*rparam,*rparam_len);
+ SSVAL(*rparam,0,NERR_Success);
+ SSVAL(*rparam,2,0); /* converter word */
+ SSVAL(*rparam,4,*rdata_len);
+
+ return(True);
+}
+
+
+/****************************************************************************
+ get info about the server
+ ****************************************************************************/
+static BOOL api_NetWkstaGetInfo(connection_struct *conn,uint16 vuid, char *param,char *data,
+ int mdrcnt,int mprcnt,
+ char **rdata,char **rparam,
+ int *rdata_len,int *rparam_len)
+{
+ char *str1 = param+2;
+ char *str2 = skip_string(str1,1);
+ char *p = skip_string(str2,1);
+ char *p2;
+ extern userdom_struct current_user_info;
+ int level = SVAL(p,0);
+
+ DEBUG(4,("NetWkstaGetInfo level %d\n",level));
+
+ *rparam_len = 6;
+ *rparam = REALLOC(*rparam,*rparam_len);
+
+ /* check it's a supported varient */
+ if (!(level==10 && strcsequal(str1,"WrLh") && strcsequal(str2,"zzzBBzz")))
+ return(False);
+
+ *rdata_len = mdrcnt + 1024;
+ *rdata = REALLOC(*rdata,*rdata_len);
+
+ SSVAL(*rparam,0,NERR_Success);
+ SSVAL(*rparam,2,0); /* converter word */
+
+ p = *rdata;
+ p2 = p + 22;
+
+
+ SIVAL(p,0,PTR_DIFF(p2,*rdata)); /* host name */
+ pstrcpy(p2,local_machine);
+ strupper_m(p2);
+ p2 = skip_string(p2,1);
+ p += 4;
+
+ SIVAL(p,0,PTR_DIFF(p2,*rdata));
+ pstrcpy(p2,current_user_info.smb_name);
+ p2 = skip_string(p2,1);
+ p += 4;
+
+ SIVAL(p,0,PTR_DIFF(p2,*rdata)); /* login domain */
+ pstrcpy(p2,lp_workgroup());
+ strupper_m(p2);
+ p2 = skip_string(p2,1);
+ p += 4;
+
+ SCVAL(p,0,lp_major_announce_version()); /* system version - e.g 4 in 4.1 */
+ SCVAL(p,1,lp_minor_announce_version()); /* system version - e.g .1 in 4.1 */
+ p += 2;
+
+ SIVAL(p,0,PTR_DIFF(p2,*rdata));
+ pstrcpy(p2,lp_workgroup()); /* don't know. login domain?? */
+ p2 = skip_string(p2,1);
+ p += 4;
+
+ SIVAL(p,0,PTR_DIFF(p2,*rdata)); /* don't know */
+ pstrcpy(p2,"");
+ p2 = skip_string(p2,1);
+ p += 4;
+
+ *rdata_len = PTR_DIFF(p2,*rdata);
+
+ SSVAL(*rparam,4,*rdata_len);
+
+ return(True);
+}
+
+/****************************************************************************
+ get info about a user
+
+ struct user_info_11 {
+ char usri11_name[21]; 0-20
+ char usri11_pad; 21
+ char *usri11_comment; 22-25
+ char *usri11_usr_comment; 26-29
+ unsigned short usri11_priv; 30-31
+ unsigned long usri11_auth_flags; 32-35
+ long usri11_password_age; 36-39
+ char *usri11_homedir; 40-43
+ char *usri11_parms; 44-47
+ long usri11_last_logon; 48-51
+ long usri11_last_logoff; 52-55
+ unsigned short usri11_bad_pw_count; 56-57
+ unsigned short usri11_num_logons; 58-59
+ char *usri11_logon_server; 60-63
+ unsigned short usri11_country_code; 64-65
+ char *usri11_workstations; 66-69
+ unsigned long usri11_max_storage; 70-73
+ unsigned short usri11_units_per_week; 74-75
+ unsigned char *usri11_logon_hours; 76-79
+ unsigned short usri11_code_page; 80-81
+ };
+
+where:
+
+ usri11_name specifies the user name for which information is retireved
+
+ usri11_pad aligns the next data structure element to a word boundary
+
+ usri11_comment is a null terminated ASCII comment
+
+ usri11_user_comment is a null terminated ASCII comment about the user
+
+ usri11_priv specifies the level of the privilege assigned to the user.
+ The possible values are:
+
+Name Value Description
+USER_PRIV_GUEST 0 Guest privilege
+USER_PRIV_USER 1 User privilege
+USER_PRV_ADMIN 2 Administrator privilege
+
+ usri11_auth_flags specifies the account operator privileges. The
+ possible values are:
+
+Name Value Description
+AF_OP_PRINT 0 Print operator
+
+
+Leach, Naik [Page 28]
+
+
+
+INTERNET-DRAFT CIFS Remote Admin Protocol January 10, 1997
+
+
+AF_OP_COMM 1 Communications operator
+AF_OP_SERVER 2 Server operator
+AF_OP_ACCOUNTS 3 Accounts operator
+
+
+ usri11_password_age specifies how many seconds have elapsed since the
+ password was last changed.
+
+ usri11_home_dir points to a null terminated ASCII string that contains
+ the path name of the user's home directory.
+
+ usri11_parms points to a null terminated ASCII string that is set
+ aside for use by applications.
+
+ usri11_last_logon specifies the time when the user last logged on.
+ This value is stored as the number of seconds elapsed since
+ 00:00:00, January 1, 1970.
+
+ usri11_last_logoff specifies the time when the user last logged off.
+ This value is stored as the number of seconds elapsed since
+ 00:00:00, January 1, 1970. A value of 0 means the last logoff
+ time is unknown.
+
+ usri11_bad_pw_count specifies the number of incorrect passwords
+ entered since the last successful logon.
+
+ usri11_log1_num_logons specifies the number of times this user has
+ logged on. A value of -1 means the number of logons is unknown.
+
+ usri11_logon_server points to a null terminated ASCII string that
+ contains the name of the server to which logon requests are sent.
+ A null string indicates logon requests should be sent to the
+ domain controller.
+
+ usri11_country_code specifies the country code for the user's language
+ of choice.
+
+ usri11_workstations points to a null terminated ASCII string that
+ contains the names of workstations the user may log on from.
+ There may be up to 8 workstations, with the names separated by
+ commas. A null strings indicates there are no restrictions.
+
+ usri11_max_storage specifies the maximum amount of disk space the user
+ can occupy. A value of 0xffffffff indicates there are no
+ restrictions.
+
+ usri11_units_per_week specifies the equal number of time units into
+ which a week is divided. This value must be equal to 168.
+
+ usri11_logon_hours points to a 21 byte (168 bits) string that
+ specifies the time during which the user can log on. Each bit
+ represents one unique hour in a week. The first bit (bit 0, word
+ 0) is Sunday, 0:00 to 0:59, the second bit (bit 1, word 0) is
+
+
+
+Leach, Naik [Page 29]
+
+
+
+INTERNET-DRAFT CIFS Remote Admin Protocol January 10, 1997
+
+
+ Sunday, 1:00 to 1:59 and so on. A null pointer indicates there
+ are no restrictions.
+
+ usri11_code_page specifies the code page for the user's language of
+ choice
+
+All of the pointers in this data structure need to be treated
+specially. The pointer is a 32 bit pointer. The higher 16 bits need
+to be ignored. The converter word returned in the parameters section
+needs to be subtracted from the lower 16 bits to calculate an offset
+into the return buffer where this ASCII string resides.
+
+There is no auxiliary data in the response.
+
+ ****************************************************************************/
+
+#define usri11_name 0
+#define usri11_pad 21
+#define usri11_comment 22
+#define usri11_usr_comment 26
+#define usri11_full_name 30
+#define usri11_priv 34
+#define usri11_auth_flags 36
+#define usri11_password_age 40
+#define usri11_homedir 44
+#define usri11_parms 48
+#define usri11_last_logon 52
+#define usri11_last_logoff 56
+#define usri11_bad_pw_count 60
+#define usri11_num_logons 62
+#define usri11_logon_server 64
+#define usri11_country_code 68
+#define usri11_workstations 70
+#define usri11_max_storage 74
+#define usri11_units_per_week 78
+#define usri11_logon_hours 80
+#define usri11_code_page 84
+#define usri11_end 86
+
+#define USER_PRIV_GUEST 0
+#define USER_PRIV_USER 1
+#define USER_PRIV_ADMIN 2
+
+#define AF_OP_PRINT 0
+#define AF_OP_COMM 1
+#define AF_OP_SERVER 2
+#define AF_OP_ACCOUNTS 3
+
+
+static BOOL api_RNetUserGetInfo(connection_struct *conn,uint16 vuid, char *param,char *data,
+ int mdrcnt,int mprcnt,
+ char **rdata,char **rparam,
+ int *rdata_len,int *rparam_len)
+{
+ char *str1 = param+2;
+ char *str2 = skip_string(str1,1);
+ char *UserName = skip_string(str2,1);
+ char *p = skip_string(UserName,1);
+ int uLevel = SVAL(p,0);
+ char *p2;
+ const char *level_string;
+
+ /* get NIS home of a previously validated user - simeon */
+ /* With share level security vuid will always be zero.
+ Don't depend on vuser being non-null !!. JRA */
+ user_struct *vuser = get_valid_user_struct(vuid);
+ if(vuser != NULL)
+ DEBUG(3,(" Username of UID %d is %s\n", (int)vuser->uid,
+ vuser->user.unix_name));
+
+ *rparam_len = 6;
+ *rparam = REALLOC(*rparam,*rparam_len);
+
+ DEBUG(4,("RNetUserGetInfo level=%d\n", uLevel));
+
+ /* check it's a supported variant */
+ if (strcmp(str1,"zWrLh") != 0) return False;
+ switch( uLevel )
+ {
+ case 0: level_string = "B21"; break;
+ case 1: level_string = "B21BB16DWzzWz"; break;
+ case 2: level_string = "B21BB16DWzzWzDzzzzDDDDWb21WWzWW"; break;
+ case 10: level_string = "B21Bzzz"; break;
+ case 11: level_string = "B21BzzzWDDzzDDWWzWzDWb21W"; break;
+ default: return False;
+ }
+
+ if (strcmp(level_string,str2) != 0) return False;
+
+ *rdata_len = mdrcnt + 1024;
+ *rdata = REALLOC(*rdata,*rdata_len);
+
+ SSVAL(*rparam,0,NERR_Success);
+ SSVAL(*rparam,2,0); /* converter word */
+
+ p = *rdata;
+ p2 = p + usri11_end;
+
+ memset(p,0,21);
+ fstrcpy(p+usri11_name,UserName); /* 21 bytes - user name */
+
+ if (uLevel > 0)
+ {
+ SCVAL(p,usri11_pad,0); /* padding - 1 byte */
+ *p2 = 0;
+ }
+ if (uLevel >= 10)
+ {
+ SIVAL(p,usri11_comment,PTR_DIFF(p2,p)); /* comment */
+ pstrcpy(p2,"Comment");
+ p2 = skip_string(p2,1);
+
+ SIVAL(p,usri11_usr_comment,PTR_DIFF(p2,p)); /* user_comment */
+ pstrcpy(p2,"UserComment");
+ p2 = skip_string(p2,1);
+
+ /* EEK! the cifsrap.txt doesn't have this in!!!! */
+ SIVAL(p,usri11_full_name,PTR_DIFF(p2,p)); /* full name */
+ pstrcpy(p2,((vuser != NULL) ? vuser->user.full_name : UserName));
+ p2 = skip_string(p2,1);
+ }
+
+ if (uLevel == 11) /* modelled after NTAS 3.51 reply */
+ {
+ SSVAL(p,usri11_priv,conn->admin_user?USER_PRIV_ADMIN:USER_PRIV_USER);
+ SIVAL(p,usri11_auth_flags,AF_OP_PRINT); /* auth flags */
+ SIVALS(p,usri11_password_age,-1); /* password age */
+ SIVAL(p,usri11_homedir,PTR_DIFF(p2,p)); /* home dir */
+ pstrcpy(p2, vuser && vuser->homedir ? vuser->homedir : "");
+ p2 = skip_string(p2,1);
+ SIVAL(p,usri11_parms,PTR_DIFF(p2,p)); /* parms */
+ pstrcpy(p2,"");
+ p2 = skip_string(p2,1);
+ SIVAL(p,usri11_last_logon,0); /* last logon */
+ SIVAL(p,usri11_last_logoff,0); /* last logoff */
+ SSVALS(p,usri11_bad_pw_count,-1); /* bad pw counts */
+ SSVALS(p,usri11_num_logons,-1); /* num logons */
+ SIVAL(p,usri11_logon_server,PTR_DIFF(p2,p)); /* logon server */
+ pstrcpy(p2,"\\\\*");
+ p2 = skip_string(p2,1);
+ SSVAL(p,usri11_country_code,0); /* country code */
+
+ SIVAL(p,usri11_workstations,PTR_DIFF(p2,p)); /* workstations */
+ pstrcpy(p2,"");
+ p2 = skip_string(p2,1);
+
+ SIVALS(p,usri11_max_storage,-1); /* max storage */
+ SSVAL(p,usri11_units_per_week,168); /* units per week */
+ SIVAL(p,usri11_logon_hours,PTR_DIFF(p2,p)); /* logon hours */
+
+ /* a simple way to get logon hours at all times. */
+ memset(p2,0xff,21);
+ SCVAL(p2,21,0); /* fix zero termination */
+ p2 = skip_string(p2,1);
+
+ SSVAL(p,usri11_code_page,0); /* code page */
+ }
+ if (uLevel == 1 || uLevel == 2)
+ {
+ memset(p+22,' ',16); /* password */
+ SIVALS(p,38,-1); /* password age */
+ SSVAL(p,42,
+ conn->admin_user?USER_PRIV_ADMIN:USER_PRIV_USER);
+ SIVAL(p,44,PTR_DIFF(p2,*rdata)); /* home dir */
+ pstrcpy(p2, vuser && vuser->homedir ? vuser->homedir : "");
+ p2 = skip_string(p2,1);
+ SIVAL(p,48,PTR_DIFF(p2,*rdata)); /* comment */
+ *p2++ = 0;
+ SSVAL(p,52,0); /* flags */
+ SIVAL(p,54,PTR_DIFF(p2,*rdata)); /* script_path */
+ pstrcpy(p2,vuser && vuser->logon_script ? vuser->logon_script : "");
+ p2 = skip_string(p2,1);
+ if (uLevel == 2)
+ {
+ SIVAL(p,60,0); /* auth_flags */
+ SIVAL(p,64,PTR_DIFF(p2,*rdata)); /* full_name */
+ pstrcpy(p2,((vuser != NULL) ? vuser->user.full_name : UserName));
+ p2 = skip_string(p2,1);
+ SIVAL(p,68,0); /* urs_comment */
+ SIVAL(p,72,PTR_DIFF(p2,*rdata)); /* parms */
+ pstrcpy(p2,"");
+ p2 = skip_string(p2,1);
+ SIVAL(p,76,0); /* workstations */
+ SIVAL(p,80,0); /* last_logon */
+ SIVAL(p,84,0); /* last_logoff */
+ SIVALS(p,88,-1); /* acct_expires */
+ SIVALS(p,92,-1); /* max_storage */
+ SSVAL(p,96,168); /* units_per_week */
+ SIVAL(p,98,PTR_DIFF(p2,*rdata)); /* logon_hours */
+ memset(p2,-1,21);
+ p2 += 21;
+ SSVALS(p,102,-1); /* bad_pw_count */
+ SSVALS(p,104,-1); /* num_logons */
+ SIVAL(p,106,PTR_DIFF(p2,*rdata)); /* logon_server */
+ pstrcpy(p2,"\\\\%L");
+ standard_sub_conn(conn, p2,0);
+ p2 = skip_string(p2,1);
+ SSVAL(p,110,49); /* country_code */
+ SSVAL(p,112,860); /* code page */
+ }
+ }
+
+ *rdata_len = PTR_DIFF(p2,*rdata);
+
+ SSVAL(*rparam,4,*rdata_len); /* is this right?? */
+
+ return(True);
+}
+
+static BOOL api_WWkstaUserLogon(connection_struct *conn,uint16 vuid, char *param,char *data,
+ int mdrcnt,int mprcnt,
+ char **rdata,char **rparam,
+ int *rdata_len,int *rparam_len)
+{
+ char *str1 = param+2;
+ char *str2 = skip_string(str1,1);
+ char *p = skip_string(str2,1);
+ int uLevel;
+ struct pack_desc desc;
+ char* name;
+ /* With share level security vuid will always be zero.
+ Don't depend on vuser being non-null !!. JRA */
+ user_struct *vuser = get_valid_user_struct(vuid);
+ if(vuser != NULL)
+ DEBUG(3,(" Username of UID %d is %s\n", (int)vuser->uid,
+ vuser->user.unix_name));
+
+ uLevel = SVAL(p,0);
+ name = p + 2;
+
+ memset((char *)&desc,'\0',sizeof(desc));
+
+ DEBUG(3,("WWkstaUserLogon uLevel=%d name=%s\n",uLevel,name));
+
+ /* check it's a supported varient */
+ if (strcmp(str1,"OOWb54WrLh") != 0) return False;
+ if (uLevel != 1 || strcmp(str2,"WB21BWDWWDDDDDDDzzzD") != 0) return False;
+ if (mdrcnt > 0) *rdata = REALLOC(*rdata,mdrcnt);
+ desc.base = *rdata;
+ desc.buflen = mdrcnt;
+ desc.subformat = NULL;
+ desc.format = str2;
+
+ if (init_package(&desc,1,0))
+ {
+ PACKI(&desc,"W",0); /* code */
+ PACKS(&desc,"B21",name); /* eff. name */
+ PACKS(&desc,"B",""); /* pad */
+ PACKI(&desc,"W",
+ conn->admin_user?USER_PRIV_ADMIN:USER_PRIV_USER);
+ PACKI(&desc,"D",0); /* auth flags XXX */
+ PACKI(&desc,"W",0); /* num logons */
+ PACKI(&desc,"W",0); /* bad pw count */
+ PACKI(&desc,"D",0); /* last logon */
+ PACKI(&desc,"D",-1); /* last logoff */
+ PACKI(&desc,"D",-1); /* logoff time */
+ PACKI(&desc,"D",-1); /* kickoff time */
+ PACKI(&desc,"D",0); /* password age */
+ PACKI(&desc,"D",0); /* password can change */
+ PACKI(&desc,"D",-1); /* password must change */
+ {
+ fstring mypath;
+ fstrcpy(mypath,"\\\\");
+ fstrcat(mypath,local_machine);
+ strupper_m(mypath);
+ PACKS(&desc,"z",mypath); /* computer */
+ }
+ PACKS(&desc,"z",lp_workgroup());/* domain */
+
+ PACKS(&desc,"z", vuser && vuser->logon_script ? vuser->logon_script :""); /* script path */
+
+ PACKI(&desc,"D",0x00000000); /* reserved */
+ }
+
+ *rdata_len = desc.usedlen;
+ *rparam_len = 6;
+ *rparam = REALLOC(*rparam,*rparam_len);
+ SSVALS(*rparam,0,desc.errcode);
+ SSVAL(*rparam,2,0);
+ SSVAL(*rparam,4,desc.neededlen);
+
+ DEBUG(4,("WWkstaUserLogon: errorcode %d\n",desc.errcode));
+ return(True);
+}
+
+
+/****************************************************************************
+ api_WAccessGetUserPerms
+ ****************************************************************************/
+static BOOL api_WAccessGetUserPerms(connection_struct *conn,uint16 vuid, char *param,char *data,
+ int mdrcnt,int mprcnt,
+ char **rdata,char **rparam,
+ int *rdata_len,int *rparam_len)
+{
+ char *str1 = param+2;
+ char *str2 = skip_string(str1,1);
+ char *user = skip_string(str2,1);
+ char *resource = skip_string(user,1);
+
+ DEBUG(3,("WAccessGetUserPerms user=%s resource=%s\n",user,resource));
+
+ /* check it's a supported varient */
+ if (strcmp(str1,"zzh") != 0) return False;
+ if (strcmp(str2,"") != 0) return False;
+
+ *rparam_len = 6;
+ *rparam = REALLOC(*rparam,*rparam_len);
+ SSVALS(*rparam,0,0); /* errorcode */
+ SSVAL(*rparam,2,0); /* converter word */
+ SSVAL(*rparam,4,0x7f); /* permission flags */
+
+ return(True);
+}
+
+/****************************************************************************
+ api_WPrintJobEnumerate
+ ****************************************************************************/
+static BOOL api_WPrintJobGetInfo(connection_struct *conn,uint16 vuid, char *param,char *data,
+ int mdrcnt,int mprcnt,
+ char **rdata,char **rparam,
+ int *rdata_len,int *rparam_len)
+{
+ char *str1 = param+2;
+ char *str2 = skip_string(str1,1);
+ char *p = skip_string(str2,1);
+ int uLevel;
+ int count;
+ int i;
+ int snum;
+ uint32 jobid;
+ struct pack_desc desc;
+ print_queue_struct *queue=NULL;
+ print_status_struct status;
+ char *tmpdata=NULL;
+
+ uLevel = SVAL(p,2);
+
+ memset((char *)&desc,'\0',sizeof(desc));
+ memset((char *)&status,'\0',sizeof(status));
+
+ DEBUG(3,("WPrintJobGetInfo uLevel=%d uJobId=0x%X\n",uLevel,SVAL(p,0)));
+
+ /* check it's a supported varient */
+ if (strcmp(str1,"WWrLh") != 0) return False;
+ if (!check_printjob_info(&desc,uLevel,str2)) return False;
+
+ if(!rap_to_pjobid(SVAL(p,0),&snum,&jobid))
+ return False;
+
+ if (snum < 0 || !VALID_SNUM(snum)) return(False);
+
+ count = print_queue_status(snum,&queue,&status);
+ for (i = 0; i < count; i++) {
+ if (queue[i].job == jobid) break;
+ }
+
+ if (mdrcnt > 0) {
+ *rdata = REALLOC(*rdata,mdrcnt);
+ desc.base = *rdata;
+ desc.buflen = mdrcnt;
+ } else {
+ /*
+ * Don't return data but need to get correct length
+ * init_package will return wrong size if buflen=0
+ */
+ desc.buflen = getlen(desc.format);
+ desc.base = tmpdata = (char *)malloc ( desc.buflen );
+ }
+
+ if (init_package(&desc,1,0)) {
+ if (i < count) {
+ fill_printjob_info(conn,snum,uLevel,&desc,&queue[i],i);
+ *rdata_len = desc.usedlen;
+ }
+ else {
+ desc.errcode = NERR_JobNotFound;
+ *rdata_len = 0;
+ }
+ }
+
+ *rparam_len = 6;
+ *rparam = REALLOC(*rparam,*rparam_len);
+ SSVALS(*rparam,0,desc.errcode);
+ SSVAL(*rparam,2,0);
+ SSVAL(*rparam,4,desc.neededlen);
+
+ SAFE_FREE(queue);
+ SAFE_FREE(tmpdata);
+
+ DEBUG(4,("WPrintJobGetInfo: errorcode %d\n",desc.errcode));
+ return(True);
+}
+
+static BOOL api_WPrintJobEnumerate(connection_struct *conn,uint16 vuid, char *param,char *data,
+ int mdrcnt,int mprcnt,
+ char **rdata,char **rparam,
+ int *rdata_len,int *rparam_len)
+{
+ char *str1 = param+2;
+ char *str2 = skip_string(str1,1);
+ char *p = skip_string(str2,1);
+ char* name = p;
+ int uLevel;
+ int count;
+ int i, succnt=0;
+ int snum;
+ struct pack_desc desc;
+ print_queue_struct *queue=NULL;
+ print_status_struct status;
+
+ memset((char *)&desc,'\0',sizeof(desc));
+ memset((char *)&status,'\0',sizeof(status));
+
+ p = skip_string(p,1);
+ uLevel = SVAL(p,0);
+
+ DEBUG(3,("WPrintJobEnumerate uLevel=%d name=%s\n",uLevel,name));
+
+ /* check it's a supported variant */
+ if (strcmp(str1,"zWrLeh") != 0) return False;
+ if (uLevel > 2) return False; /* defined only for uLevel 0,1,2 */
+ if (!check_printjob_info(&desc,uLevel,str2)) return False;
+
+ snum = lp_servicenumber(name);
+ if (snum < 0 && pcap_printername_ok(name,NULL)) {
+ int pnum = lp_servicenumber(PRINTERS_NAME);
+ if (pnum >= 0) {
+ lp_add_printer(name,pnum);
+ snum = lp_servicenumber(name);
+ }
+ }
+
+ if (snum < 0 || !VALID_SNUM(snum)) return(False);
+
+ count = print_queue_status(snum,&queue,&status);
+ if (mdrcnt > 0) *rdata = REALLOC(*rdata,mdrcnt);
+ desc.base = *rdata;
+ desc.buflen = mdrcnt;
+
+ if (init_package(&desc,count,0)) {
+ succnt = 0;
+ for (i = 0; i < count; i++) {
+ fill_printjob_info(conn,snum,uLevel,&desc,&queue[i],i);
+ if (desc.errcode == NERR_Success) succnt = i+1;
+ }
+ }
+
+ *rdata_len = desc.usedlen;
+
+ *rparam_len = 8;
+ *rparam = REALLOC(*rparam,*rparam_len);
+ SSVALS(*rparam,0,desc.errcode);
+ SSVAL(*rparam,2,0);
+ SSVAL(*rparam,4,succnt);
+ SSVAL(*rparam,6,count);
+
+ SAFE_FREE(queue);
+
+ DEBUG(4,("WPrintJobEnumerate: errorcode %d\n",desc.errcode));
+ return(True);
+}
+
+static int check_printdest_info(struct pack_desc* desc,
+ int uLevel, char* id)
+{
+ desc->subformat = NULL;
+ switch( uLevel ) {
+ case 0: desc->format = "B9"; break;
+ case 1: desc->format = "B9B21WWzW"; break;
+ case 2: desc->format = "z"; break;
+ case 3: desc->format = "zzzWWzzzWW"; break;
+ default: return False;
+ }
+ if (strcmp(desc->format,id) != 0) return False;
+ return True;
+}
+
+static void fill_printdest_info(connection_struct *conn, int snum, int uLevel,
+ struct pack_desc* desc)
+{
+ char buf[100];
+ strncpy(buf,SERVICE(snum),sizeof(buf)-1);
+ buf[sizeof(buf)-1] = 0;
+ strupper_m(buf);
+ if (uLevel <= 1) {
+ PACKS(desc,"B9",buf); /* szName */
+ if (uLevel == 1) {
+ PACKS(desc,"B21",""); /* szUserName */
+ PACKI(desc,"W",0); /* uJobId */
+ PACKI(desc,"W",0); /* fsStatus */
+ PACKS(desc,"z",""); /* pszStatus */
+ PACKI(desc,"W",0); /* time */
+ }
+ }
+ if (uLevel == 2 || uLevel == 3) {
+ PACKS(desc,"z",buf); /* pszPrinterName */
+ if (uLevel == 3) {
+ PACKS(desc,"z",""); /* pszUserName */
+ PACKS(desc,"z",""); /* pszLogAddr */
+ PACKI(desc,"W",0); /* uJobId */
+ PACKI(desc,"W",0); /* fsStatus */
+ PACKS(desc,"z",""); /* pszStatus */
+ PACKS(desc,"z",""); /* pszComment */
+ PACKS(desc,"z","NULL"); /* pszDrivers */
+ PACKI(desc,"W",0); /* time */
+ PACKI(desc,"W",0); /* pad1 */
+ }
+ }
+}
+
+static BOOL api_WPrintDestGetInfo(connection_struct *conn,uint16 vuid, char *param,char *data,
+ int mdrcnt,int mprcnt,
+ char **rdata,char **rparam,
+ int *rdata_len,int *rparam_len)
+{
+ char *str1 = param+2;
+ char *str2 = skip_string(str1,1);
+ char *p = skip_string(str2,1);
+ char* PrinterName = p;
+ int uLevel;
+ struct pack_desc desc;
+ int snum;
+ char *tmpdata=NULL;
+
+ memset((char *)&desc,'\0',sizeof(desc));
+
+ p = skip_string(p,1);
+ uLevel = SVAL(p,0);
+
+ DEBUG(3,("WPrintDestGetInfo uLevel=%d PrinterName=%s\n",uLevel,PrinterName));
+
+ /* check it's a supported varient */
+ if (strcmp(str1,"zWrLh") != 0) return False;
+ if (!check_printdest_info(&desc,uLevel,str2)) return False;
+
+ snum = lp_servicenumber(PrinterName);
+ if (snum < 0 && pcap_printername_ok(PrinterName,NULL)) {
+ int pnum = lp_servicenumber(PRINTERS_NAME);
+ if (pnum >= 0) {
+ lp_add_printer(PrinterName,pnum);
+ snum = lp_servicenumber(PrinterName);
+ }
+ }
+
+ if (snum < 0) {
+ *rdata_len = 0;
+ desc.errcode = NERR_DestNotFound;
+ desc.neededlen = 0;
+ }
+ else {
+ if (mdrcnt > 0) {
+ *rdata = REALLOC(*rdata,mdrcnt);
+ desc.base = *rdata;
+ desc.buflen = mdrcnt;
+ } else {
+ /*
+ * Don't return data but need to get correct length
+ * init_package will return wrong size if buflen=0
+ */
+ desc.buflen = getlen(desc.format);
+ desc.base = tmpdata = (char *)malloc ( desc.buflen );
+ }
+ if (init_package(&desc,1,0)) {
+ fill_printdest_info(conn,snum,uLevel,&desc);
+ }
+ *rdata_len = desc.usedlen;
+ }
+
+ *rparam_len = 6;
+ *rparam = REALLOC(*rparam,*rparam_len);
+ SSVALS(*rparam,0,desc.errcode);
+ SSVAL(*rparam,2,0);
+ SSVAL(*rparam,4,desc.neededlen);
+
+ DEBUG(4,("WPrintDestGetInfo: errorcode %d\n",desc.errcode));
+ SAFE_FREE(tmpdata);
+ return(True);
+}
+
+static BOOL api_WPrintDestEnum(connection_struct *conn,uint16 vuid, char *param,char *data,
+ int mdrcnt,int mprcnt,
+ char **rdata,char **rparam,
+ int *rdata_len,int *rparam_len)
+{
+ char *str1 = param+2;
+ char *str2 = skip_string(str1,1);
+ char *p = skip_string(str2,1);
+ int uLevel;
+ int queuecnt;
+ int i, n, succnt=0;
+ struct pack_desc desc;
+ int services = lp_numservices();
+
+ memset((char *)&desc,'\0',sizeof(desc));
+
+ uLevel = SVAL(p,0);
+
+ DEBUG(3,("WPrintDestEnum uLevel=%d\n",uLevel));
+
+ /* check it's a supported varient */
+ if (strcmp(str1,"WrLeh") != 0) return False;
+ if (!check_printdest_info(&desc,uLevel,str2)) return False;
+
+ queuecnt = 0;
+ for (i = 0; i < services; i++)
+ if (lp_snum_ok(i) && lp_print_ok(i) && lp_browseable(i))
+ queuecnt++;
+
+ if (mdrcnt > 0) *rdata = REALLOC(*rdata,mdrcnt);
+ desc.base = *rdata;
+ desc.buflen = mdrcnt;
+ if (init_package(&desc,queuecnt,0)) {
+ succnt = 0;
+ n = 0;
+ for (i = 0; i < services; i++) {
+ if (lp_snum_ok(i) && lp_print_ok(i) && lp_browseable(i)) {
+ fill_printdest_info(conn,i,uLevel,&desc);
+ n++;
+ if (desc.errcode == NERR_Success) succnt = n;
+ }
+ }
+ }
+
+ *rdata_len = desc.usedlen;
+
+ *rparam_len = 8;
+ *rparam = REALLOC(*rparam,*rparam_len);
+ SSVALS(*rparam,0,desc.errcode);
+ SSVAL(*rparam,2,0);
+ SSVAL(*rparam,4,succnt);
+ SSVAL(*rparam,6,queuecnt);
+
+ DEBUG(4,("WPrintDestEnumerate: errorcode %d\n",desc.errcode));
+ return(True);
+}
+
+static BOOL api_WPrintDriverEnum(connection_struct *conn,uint16 vuid, char *param,char *data,
+ int mdrcnt,int mprcnt,
+ char **rdata,char **rparam,
+ int *rdata_len,int *rparam_len)
+{
+ char *str1 = param+2;
+ char *str2 = skip_string(str1,1);
+ char *p = skip_string(str2,1);
+ int uLevel;
+ int succnt;
+ struct pack_desc desc;
+
+ memset((char *)&desc,'\0',sizeof(desc));
+
+ uLevel = SVAL(p,0);
+
+ DEBUG(3,("WPrintDriverEnum uLevel=%d\n",uLevel));
+
+ /* check it's a supported varient */
+ if (strcmp(str1,"WrLeh") != 0) return False;
+ if (uLevel != 0 || strcmp(str2,"B41") != 0) return False;
+
+ if (mdrcnt > 0) *rdata = REALLOC(*rdata,mdrcnt);
+ desc.base = *rdata;
+ desc.buflen = mdrcnt;
+ if (init_package(&desc,1,0)) {
+ PACKS(&desc,"B41","NULL");
+ }
+
+ succnt = (desc.errcode == NERR_Success ? 1 : 0);
+
+ *rdata_len = desc.usedlen;
+
+ *rparam_len = 8;
+ *rparam = REALLOC(*rparam,*rparam_len);
+ SSVALS(*rparam,0,desc.errcode);
+ SSVAL(*rparam,2,0);
+ SSVAL(*rparam,4,succnt);
+ SSVAL(*rparam,6,1);
+
+ DEBUG(4,("WPrintDriverEnum: errorcode %d\n",desc.errcode));
+ return(True);
+}
+
+static BOOL api_WPrintQProcEnum(connection_struct *conn,uint16 vuid, char *param,char *data,
+ int mdrcnt,int mprcnt,
+ char **rdata,char **rparam,
+ int *rdata_len,int *rparam_len)
+{
+ char *str1 = param+2;
+ char *str2 = skip_string(str1,1);
+ char *p = skip_string(str2,1);
+ int uLevel;
+ int succnt;
+ struct pack_desc desc;
+
+ memset((char *)&desc,'\0',sizeof(desc));
+
+ uLevel = SVAL(p,0);
+
+ DEBUG(3,("WPrintQProcEnum uLevel=%d\n",uLevel));
+
+ /* check it's a supported varient */
+ if (strcmp(str1,"WrLeh") != 0) return False;
+ if (uLevel != 0 || strcmp(str2,"B13") != 0) return False;
+
+ if (mdrcnt > 0) *rdata = REALLOC(*rdata,mdrcnt);
+ desc.base = *rdata;
+ desc.buflen = mdrcnt;
+ desc.format = str2;
+ if (init_package(&desc,1,0)) {
+ PACKS(&desc,"B13","lpd");
+ }
+
+ succnt = (desc.errcode == NERR_Success ? 1 : 0);
+
+ *rdata_len = desc.usedlen;
+
+ *rparam_len = 8;
+ *rparam = REALLOC(*rparam,*rparam_len);
+ SSVALS(*rparam,0,desc.errcode);
+ SSVAL(*rparam,2,0);
+ SSVAL(*rparam,4,succnt);
+ SSVAL(*rparam,6,1);
+
+ DEBUG(4,("WPrintQProcEnum: errorcode %d\n",desc.errcode));
+ return(True);
+}
+
+static BOOL api_WPrintPortEnum(connection_struct *conn,uint16 vuid, char *param,char *data,
+ int mdrcnt,int mprcnt,
+ char **rdata,char **rparam,
+ int *rdata_len,int *rparam_len)
+{
+ char *str1 = param+2;
+ char *str2 = skip_string(str1,1);
+ char *p = skip_string(str2,1);
+ int uLevel;
+ int succnt;
+ struct pack_desc desc;
+
+ memset((char *)&desc,'\0',sizeof(desc));
+
+ uLevel = SVAL(p,0);
+
+ DEBUG(3,("WPrintPortEnum uLevel=%d\n",uLevel));
+
+ /* check it's a supported varient */
+ if (strcmp(str1,"WrLeh") != 0) return False;
+ if (uLevel != 0 || strcmp(str2,"B9") != 0) return False;
+
+ if (mdrcnt > 0) *rdata = REALLOC(*rdata,mdrcnt);
+ memset((char *)&desc,'\0',sizeof(desc));
+ desc.base = *rdata;
+ desc.buflen = mdrcnt;
+ desc.format = str2;
+ if (init_package(&desc,1,0)) {
+ PACKS(&desc,"B13","lp0");
+ }
+
+ succnt = (desc.errcode == NERR_Success ? 1 : 0);
+
+ *rdata_len = desc.usedlen;
+
+ *rparam_len = 8;
+ *rparam = REALLOC(*rparam,*rparam_len);
+ SSVALS(*rparam,0,desc.errcode);
+ SSVAL(*rparam,2,0);
+ SSVAL(*rparam,4,succnt);
+ SSVAL(*rparam,6,1);
+
+ DEBUG(4,("WPrintPortEnum: errorcode %d\n",desc.errcode));
+ return(True);
+}
+
+
+/****************************************************************************
+ List open sessions
+ ****************************************************************************/
+static BOOL api_RNetSessionEnum(connection_struct *conn,uint16 vuid, char *param, char *data,
+ int mdrcnt,int mprcnt,
+ char **rdata,char **rparam,
+ int *rdata_len,int *rparam_len)
+
+{
+ char *str1 = param+2;
+ char *str2 = skip_string(str1,1);
+ char *p = skip_string(str2,1);
+ int uLevel;
+ struct pack_desc desc;
+ struct sessionid *session_list;
+ int i, num_sessions;
+
+ memset((char *)&desc,'\0',sizeof(desc));
+
+ uLevel = SVAL(p,0);
+
+ DEBUG(3,("RNetSessionEnum uLevel=%d\n",uLevel));
+ DEBUG(7,("RNetSessionEnum req string=%s\n",str1));
+ DEBUG(7,("RNetSessionEnum ret string=%s\n",str2));
+
+ /* check it's a supported varient */
+ if (strcmp(str1,RAP_NetSessionEnum_REQ) != 0) return False;
+ if (uLevel != 2 || strcmp(str2,RAP_SESSION_INFO_L2) != 0) return False;
+
+ num_sessions = list_sessions(&session_list);
+
+ if (mdrcnt > 0) *rdata = REALLOC(*rdata,mdrcnt);
+ memset((char *)&desc,'\0',sizeof(desc));
+ desc.base = *rdata;
+ desc.buflen = mdrcnt;
+ desc.format = str2;
+ if (!init_package(&desc,num_sessions,0)) {
+ return False;
+ }
+
+ for(i=0; i<num_sessions; i++) {
+ PACKS(&desc, "z", session_list[i].remote_machine);
+ PACKS(&desc, "z", session_list[i].username);
+ PACKI(&desc, "W", 1); /* num conns */
+ PACKI(&desc, "W", 0); /* num opens */
+ PACKI(&desc, "W", 1); /* num users */
+ PACKI(&desc, "D", 0); /* session time */
+ PACKI(&desc, "D", 0); /* idle time */
+ PACKI(&desc, "D", 0); /* flags */
+ PACKS(&desc, "z", "Unknown Client"); /* client type string */
+ }
+
+ *rdata_len = desc.usedlen;
+
+ *rparam_len = 8;
+ *rparam = REALLOC(*rparam,*rparam_len);
+ SSVALS(*rparam,0,desc.errcode);
+ SSVAL(*rparam,2,0); /* converter */
+ SSVAL(*rparam,4,num_sessions); /* count */
+
+ DEBUG(4,("RNetSessionEnum: errorcode %d\n",desc.errcode));
+ return True;
+}
+
+
+/****************************************************************************
+ The buffer was too small
+ ****************************************************************************/
+
+static BOOL api_TooSmall(connection_struct *conn,uint16 vuid, char *param,char *data,
+ int mdrcnt,int mprcnt,
+ char **rdata,char **rparam,
+ int *rdata_len,int *rparam_len)
+{
+ *rparam_len = MIN(*rparam_len,mprcnt);
+ *rparam = REALLOC(*rparam,*rparam_len);
+
+ *rdata_len = 0;
+
+ SSVAL(*rparam,0,NERR_BufTooSmall);
+
+ DEBUG(3,("Supplied buffer too small in API command\n"));
+
+ return(True);
+}
+
+
+/****************************************************************************
+ The request is not supported
+ ****************************************************************************/
+
+static BOOL api_Unsupported(connection_struct *conn,uint16 vuid, char *param,char *data,
+ int mdrcnt,int mprcnt,
+ char **rdata,char **rparam,
+ int *rdata_len,int *rparam_len)
+{
+ *rparam_len = 4;
+ *rparam = REALLOC(*rparam,*rparam_len);
+
+ *rdata_len = 0;
+
+ SSVAL(*rparam,0,NERR_notsupported);
+ SSVAL(*rparam,2,0); /* converter word */
+
+ DEBUG(3,("Unsupported API command\n"));
+
+ return(True);
+}
+
+
+
+
+static const struct
+{
+ const char *name;
+ int id;
+ BOOL (*fn)(connection_struct *,uint16,char *,char *,
+ int,int,char **,char **,int *,int *);
+ BOOL auth_user; /* Deny anonymous access? */
+} api_commands[] = {
+ {"RNetShareEnum", RAP_WshareEnum, api_RNetShareEnum, True},
+ {"RNetShareGetInfo", RAP_WshareGetInfo, api_RNetShareGetInfo},
+ {"RNetShareAdd", RAP_WshareAdd, api_RNetShareAdd},
+ {"RNetSessionEnum", RAP_WsessionEnum, api_RNetSessionEnum, True},
+ {"RNetServerGetInfo", RAP_WserverGetInfo, api_RNetServerGetInfo},
+ {"RNetGroupEnum", RAP_WGroupEnum, api_RNetGroupEnum, True},
+ {"RNetGroupGetUsers", RAP_WGroupGetUsers, api_RNetGroupGetUsers, True},
+ {"RNetUserEnum", RAP_WUserEnum, api_RNetUserEnum, True},
+ {"RNetUserGetInfo", RAP_WUserGetInfo, api_RNetUserGetInfo},
+ {"NetUserGetGroups", RAP_WUserGetGroups, api_NetUserGetGroups},
+ {"NetWkstaGetInfo", RAP_WWkstaGetInfo, api_NetWkstaGetInfo},
+ {"DosPrintQEnum", RAP_WPrintQEnum, api_DosPrintQEnum, True},
+ {"DosPrintQGetInfo", RAP_WPrintQGetInfo, api_DosPrintQGetInfo},
+ {"WPrintQueuePause", RAP_WPrintQPause, api_WPrintQueueCtrl},
+ {"WPrintQueueResume", RAP_WPrintQContinue, api_WPrintQueueCtrl},
+ {"WPrintJobEnumerate",RAP_WPrintJobEnum, api_WPrintJobEnumerate},
+ {"WPrintJobGetInfo", RAP_WPrintJobGetInfo, api_WPrintJobGetInfo},
+ {"RDosPrintJobDel", RAP_WPrintJobDel, api_RDosPrintJobDel},
+ {"RDosPrintJobPause", RAP_WPrintJobPause, api_RDosPrintJobDel},
+ {"RDosPrintJobResume",RAP_WPrintJobContinue, api_RDosPrintJobDel},
+ {"WPrintDestEnum", RAP_WPrintDestEnum, api_WPrintDestEnum},
+ {"WPrintDestGetInfo", RAP_WPrintDestGetInfo, api_WPrintDestGetInfo},
+ {"NetRemoteTOD", RAP_NetRemoteTOD, api_NetRemoteTOD},
+ {"WPrintQueuePurge", RAP_WPrintQPurge, api_WPrintQueueCtrl},
+ {"NetServerEnum", RAP_NetServerEnum2, api_RNetServerEnum}, /* anon OK */
+ {"WAccessGetUserPerms",RAP_WAccessGetUserPerms,api_WAccessGetUserPerms},
+ {"SetUserPassword", RAP_WUserPasswordSet2, api_SetUserPassword},
+ {"WWkstaUserLogon", RAP_WWkstaUserLogon, api_WWkstaUserLogon},
+ {"PrintJobInfo", RAP_WPrintJobSetInfo, api_PrintJobInfo},
+ {"WPrintDriverEnum", RAP_WPrintDriverEnum, api_WPrintDriverEnum},
+ {"WPrintQProcEnum", RAP_WPrintQProcessorEnum,api_WPrintQProcEnum},
+ {"WPrintPortEnum", RAP_WPrintPortEnum, api_WPrintPortEnum},
+ {"SamOEMChangePassword",RAP_SamOEMChgPasswordUser2_P,api_SamOEMChangePassword}, /* anon OK */
+ {NULL, -1, api_Unsupported}};
+
+/* The following RAP calls are not implemented by Samba:
+
+ RAP_WFileEnum2 - anon not OK
+*/
+
+/****************************************************************************
+ Handle remote api calls
+ ****************************************************************************/
+
+int api_reply(connection_struct *conn,uint16 vuid,char *outbuf,char *data,char *params,
+ int tdscnt,int tpscnt,int mdrcnt,int mprcnt)
+{
+ int api_command;
+ char *rdata = NULL;
+ char *rparam = NULL;
+ int rdata_len = 0;
+ int rparam_len = 0;
+ BOOL reply=False;
+ int i;
+
+ if (!params) {
+ DEBUG(0,("ERROR: NULL params in api_reply()\n"));
+ return 0;
+ }
+
+ api_command = SVAL(params,0);
+
+ DEBUG(3,("Got API command %d of form <%s> <%s> (tdscnt=%d,tpscnt=%d,mdrcnt=%d,mprcnt=%d)\n",
+ api_command,
+ params+2,
+ skip_string(params+2,1),
+ tdscnt,tpscnt,mdrcnt,mprcnt));
+
+ for (i=0;api_commands[i].name;i++) {
+ if (api_commands[i].id == api_command && api_commands[i].fn) {
+ DEBUG(3,("Doing %s\n",api_commands[i].name));
+ break;
+ }
+ }
+
+ /* Check whether this api call can be done anonymously */
+
+ if (api_commands[i].auth_user && lp_restrict_anonymous()) {
+ user_struct *user = get_valid_user_struct(vuid);
+
+ if (!user || user->guest)
+ return ERROR_NT(NT_STATUS_ACCESS_DENIED);
+ }
+
+ rdata = (char *)malloc(1024);
+ if (rdata)
+ memset(rdata,'\0',1024);
+
+ rparam = (char *)malloc(1024);
+ if (rparam)
+ memset(rparam,'\0',1024);
+
+ if(!rdata || !rparam) {
+ DEBUG(0,("api_reply: malloc fail !\n"));
+ return -1;
+ }
+
+ reply = api_commands[i].fn(conn,vuid,params,data,mdrcnt,mprcnt,
+ &rdata,&rparam,&rdata_len,&rparam_len);
+
+
+ if (rdata_len > mdrcnt ||
+ rparam_len > mprcnt) {
+ reply = api_TooSmall(conn,vuid,params,data,mdrcnt,mprcnt,
+ &rdata,&rparam,&rdata_len,&rparam_len);
+ }
+
+ /* if we get False back then it's actually unsupported */
+ if (!reply)
+ api_Unsupported(conn,vuid,params,data,mdrcnt,mprcnt,
+ &rdata,&rparam,&rdata_len,&rparam_len);
+
+ send_trans_reply(outbuf, rparam, rparam_len, rdata, rdata_len, False);
+
+ SAFE_FREE(rdata);
+ SAFE_FREE(rparam);
+
+ return -1;
+}
diff --git a/source/smbd/mangle.c b/source/smbd/mangle.c
new file mode 100644
index 00000000000..c5d7582c033
--- /dev/null
+++ b/source/smbd/mangle.c
@@ -0,0 +1,124 @@
+/*
+ Unix SMB/CIFS implementation.
+ Name mangling interface
+ 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 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"
+
+static struct mangle_fns *mangle_fns;
+
+/* this allows us to add more mangling backends */
+static const struct {
+ const char *name;
+ struct mangle_fns *(*init_fn)(void);
+} mangle_backends[] = {
+ { "hash", mangle_hash_init },
+ { "hash2", mangle_hash2_init },
+ /*{ "tdb", mangle_tdb_init }, */
+ { NULL, NULL }
+};
+
+/*
+ initialise the mangling subsystem
+*/
+static void mangle_init(void)
+{
+ int i;
+ char *method;
+
+ if (mangle_fns)
+ return;
+
+ method = lp_mangling_method();
+
+ /* find the first mangling method that manages to initialise and
+ matches the "mangling method" parameter */
+ for (i=0; mangle_backends[i].name && !mangle_fns; i++) {
+ if (!method || !*method || strcmp(method, mangle_backends[i].name) == 0) {
+ mangle_fns = mangle_backends[i].init_fn();
+ }
+ }
+
+ if (!mangle_fns) {
+ DEBUG(0,("Failed to initialise mangling system '%s'\n", method));
+ exit_server("mangling init failed");
+ }
+}
+
+
+/*
+ reset the cache. This is called when smb.conf has been reloaded
+*/
+void mangle_reset_cache(void)
+{
+ mangle_init();
+
+ mangle_fns->reset();
+}
+
+/*
+ see if a filename has come out of our mangling code
+*/
+BOOL mangle_is_mangled(const char *s)
+{
+ return mangle_fns->is_mangled(s);
+}
+
+/*
+ see if a filename matches the rules of a 8.3 filename
+*/
+BOOL mangle_is_8_3(const char *fname, BOOL check_case)
+{
+ return mangle_fns->is_8_3(fname, check_case, False);
+}
+
+BOOL mangle_is_8_3_wildcards(const char *fname, BOOL check_case)
+{
+ return mangle_fns->is_8_3(fname, check_case, True);
+}
+
+/*
+ try to reverse map a 8.3 name to the original filename. This doesn't have to
+ always succeed, as the directory handling code in smbd will scan the directory
+ looking for a matching name if it doesn't. It should succeed most of the time
+ or there will be a huge performance penalty
+*/
+BOOL mangle_check_cache(char *s)
+{
+ return mangle_fns->check_cache(s);
+}
+
+/*
+ map a long filename to a 8.3 name.
+ */
+
+void mangle_map(pstring OutName, BOOL need83, BOOL cache83, int snum)
+{
+ /* name mangling can be disabled for speed, in which case
+ we just truncate the string */
+ if (!lp_manglednames(snum)) {
+ if (need83) {
+ string_truncate(OutName, 12);
+ }
+ return;
+ }
+
+ /* invoke the inane "mangled map" code */
+ mangle_map_filename(OutName, snum);
+ mangle_fns->name_map(OutName, need83, cache83);
+}
diff --git a/source/smbd/mangle_hash.c b/source/smbd/mangle_hash.c
new file mode 100644
index 00000000000..16722ae6e9d
--- /dev/null
+++ b/source/smbd/mangle_hash.c
@@ -0,0 +1,783 @@
+/*
+ Unix SMB/CIFS implementation.
+ Name mangling
+ Copyright (C) Andrew Tridgell 1992-2002
+ Copyright (C) Simo Sorce 2001
+ 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 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.
+*/
+
+
+/* -------------------------------------------------------------------------- **
+ * Notable problems...
+ *
+ * March/April 1998 CRH
+ * - Many of the functions in this module overwrite string buffers passed to
+ * them. This causes a variety of problems and is, generally speaking,
+ * dangerous and scarry. See the kludge notes in name_map()
+ * below.
+ * - It seems that something is calling name_map() twice. The
+ * first call is probably some sort of test. Names which contain
+ * illegal characters are being doubly mangled. I'm not sure, but
+ * I'm guessing the problem is in server.c.
+ *
+ * -------------------------------------------------------------------------- **
+ */
+
+/* -------------------------------------------------------------------------- **
+ * History...
+ *
+ * March/April 1998 CRH
+ * Updated a bit. Rewrote is_mangled() to be a bit more selective.
+ * Rewrote the mangled name cache. Added comments here and there.
+ * &c.
+ * -------------------------------------------------------------------------- **
+ */
+
+#include "includes.h"
+
+
+/* -------------------------------------------------------------------------- **
+ * External Variables...
+ */
+
+extern int case_default; /* Are conforming 8.3 names all upper or lower? */
+extern BOOL case_mangle; /* If true, all chars in 8.3 should be same case. */
+
+/* -------------------------------------------------------------------------- **
+ * Other stuff...
+ *
+ * magic_char - This is the magic char used for mangling. It's
+ * global. There is a call to lp_magicchar() in server.c
+ * that is used to override the initial value.
+ *
+ * MANGLE_BASE - This is the number of characters we use for name mangling.
+ *
+ * basechars - The set characters used for name mangling. This
+ * is static (scope is this file only).
+ *
+ * mangle() - Macro used to select a character from basechars (i.e.,
+ * mangle(n) will return the nth digit, modulo MANGLE_BASE).
+ *
+ * chartest - array 0..255. The index range is the set of all possible
+ * values of a byte. For each byte value, the content is a
+ * two nibble pair. See BASECHAR_MASK and ILLEGAL_MASK,
+ * below.
+ *
+ * ct_initialized - False until the chartest array has been initialized via
+ * a call to init_chartest().
+ *
+ * BASECHAR_MASK - Masks the upper nibble of a one-byte value.
+ *
+ * ILLEGAL_MASK - Masks the lower nibble of a one-byte value.
+ *
+ * isbasecahr() - Given a character, check the chartest array to see
+ * if that character is in the basechars set. This is
+ * faster than using strchr_m().
+ *
+ * isillegal() - Given a character, check the chartest array to see
+ * if that character is in the illegal characters set.
+ * This is faster than using strchr_m().
+ *
+ * mangled_cache - Cache header used for storing mangled -> original
+ * reverse maps.
+ *
+ * mc_initialized - False until the mangled_cache structure has been
+ * initialized via a call to reset_mangled_cache().
+ *
+ * MANGLED_CACHE_MAX_ENTRIES - Default maximum number of entries for the
+ * cache. A value of 0 indicates "infinite".
+ *
+ * MANGLED_CACHE_MAX_MEMORY - Default maximum amount of memory for the
+ * cache. When the cache was kept as an array of 256
+ * byte strings, the default cache size was 50 entries.
+ * This required a fixed 12.5Kbytes of memory. The
+ * mangled stack parameter is no longer used (though
+ * this might change). We're now using a fixed 16Kbyte
+ * maximum cache size. This will probably be much more
+ * than 50 entries.
+ */
+
+char magic_char = '~';
+
+static char basechars[] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ_-!@#$%";
+#define MANGLE_BASE (sizeof(basechars)/sizeof(char)-1)
+
+static unsigned char chartest[256] = { 0 };
+static BOOL ct_initialized = False;
+
+#define mangle(V) ((char)(basechars[(V) % MANGLE_BASE]))
+#define BASECHAR_MASK 0xf0
+#define ILLEGAL_MASK 0x0f
+#define isbasechar(C) ( (chartest[ ((C) & 0xff) ]) & BASECHAR_MASK )
+#define isillegal(C) ( (chartest[ ((C) & 0xff) ]) & ILLEGAL_MASK )
+
+static ubi_cacheRoot mangled_cache[1] = { { { 0, 0, 0, 0 }, 0, 0, 0, 0, 0, 0 } };
+static BOOL mc_initialized = False;
+#define MANGLED_CACHE_MAX_ENTRIES 1024
+#define MANGLED_CACHE_MAX_MEMORY 0
+
+/* -------------------------------------------------------------------------- **
+ * External Variables...
+ */
+
+extern int case_default; /* Are conforming 8.3 names all upper or lower? */
+extern BOOL case_mangle; /* If true, all chars in 8.3 should be same case. */
+
+/* -------------------------------------------------------------------- */
+
+static NTSTATUS has_valid_83_chars(const smb_ucs2_t *s, BOOL allow_wildcards)
+{
+ if (!s || !*s)
+ return NT_STATUS_INVALID_PARAMETER;
+
+ /* CHECK: this should not be necessary if the ms wild chars
+ are not valid in valid.dat --- simo */
+ if (!allow_wildcards && ms_has_wild_w(s))
+ return NT_STATUS_UNSUCCESSFUL;
+
+ while (*s) {
+ if(!isvalid83_w(*s))
+ return NT_STATUS_UNSUCCESSFUL;
+ s++;
+ }
+
+ return NT_STATUS_OK;
+}
+
+/* return False if something fail and
+ * return 2 alloced unicode strings that contain prefix and extension
+ */
+
+static NTSTATUS mangle_get_prefix(const smb_ucs2_t *ucs2_string, smb_ucs2_t **prefix,
+ smb_ucs2_t **extension, BOOL allow_wildcards)
+{
+ size_t ext_len;
+ smb_ucs2_t *p;
+
+ *extension = 0;
+ *prefix = strdup_w(ucs2_string);
+ if (!*prefix) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ if ((p = strrchr_w(*prefix, UCS2_CHAR('.')))) {
+ ext_len = strlen_w(p+1);
+ if ((ext_len > 0) && (ext_len < 4) && (p != *prefix) &&
+ (NT_STATUS_IS_OK(has_valid_83_chars(p+1,allow_wildcards)))) /* check extension */ {
+ *p = 0;
+ *extension = strdup_w(p+1);
+ if (!*extension) {
+ SAFE_FREE(*prefix);
+ return NT_STATUS_NO_MEMORY;
+ }
+ }
+ }
+ return NT_STATUS_OK;
+}
+
+/* ************************************************************************** **
+ * Return NT_STATUS_UNSUCCESSFUL if a name is a special msdos reserved name.
+ *
+ * Input: fname - String containing the name to be tested.
+ *
+ * Output: NT_STATUS_UNSUCCESSFUL, if the name matches one of the list of reserved names.
+ *
+ * Notes: This is a static function called by is_8_3(), below.
+ *
+ * ************************************************************************** **
+ */
+
+static NTSTATUS is_valid_name(const smb_ucs2_t *fname, BOOL allow_wildcards, BOOL only_8_3)
+{
+ smb_ucs2_t *str, *p;
+ NTSTATUS ret = NT_STATUS_OK;
+
+ if (!fname || !*fname)
+ return NT_STATUS_INVALID_PARAMETER;
+
+ /* . and .. are valid names. */
+ if (strcmp_wa(fname, ".")==0 || strcmp_wa(fname, "..")==0)
+ return NT_STATUS_OK;
+
+ /* Name cannot start with '.' */
+ if (*fname == UCS2_CHAR('.'))
+ return NT_STATUS_UNSUCCESSFUL;
+
+ if (only_8_3) {
+ ret = has_valid_83_chars(fname, allow_wildcards);
+ if (!NT_STATUS_IS_OK(ret))
+ return ret;
+ }
+
+ str = strdup_w(fname);
+ p = strchr_w(str, UCS2_CHAR('.'));
+ if (p && p[1] == UCS2_CHAR(0)) {
+ /* Name cannot end in '.' */
+ SAFE_FREE(str);
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+ if (p)
+ *p = 0;
+ strupper_w(str);
+ p = &(str[1]);
+
+ switch(str[0])
+ {
+ case UCS2_CHAR('A'):
+ if(strcmp_wa(p, "UX") == 0)
+ ret = NT_STATUS_UNSUCCESSFUL;
+ break;
+ case UCS2_CHAR('C'):
+ if((strcmp_wa(p, "LOCK$") == 0)
+ || (strcmp_wa(p, "ON") == 0)
+ || (strcmp_wa(p, "OM1") == 0)
+ || (strcmp_wa(p, "OM2") == 0)
+ || (strcmp_wa(p, "OM3") == 0)
+ || (strcmp_wa(p, "OM4") == 0)
+ )
+ ret = NT_STATUS_UNSUCCESSFUL;
+ break;
+ case UCS2_CHAR('L'):
+ if((strcmp_wa(p, "PT1") == 0)
+ || (strcmp_wa(p, "PT2") == 0)
+ || (strcmp_wa(p, "PT3") == 0)
+ )
+ ret = NT_STATUS_UNSUCCESSFUL;
+ break;
+ case UCS2_CHAR('N'):
+ if(strcmp_wa(p, "UL") == 0)
+ ret = NT_STATUS_UNSUCCESSFUL;
+ break;
+ case UCS2_CHAR('P'):
+ if(strcmp_wa(p, "RN") == 0)
+ ret = NT_STATUS_UNSUCCESSFUL;
+ break;
+ default:
+ break;
+ }
+
+ SAFE_FREE(str);
+ return ret;
+}
+
+static NTSTATUS is_8_3_w(const smb_ucs2_t *fname, BOOL allow_wildcards)
+{
+ smb_ucs2_t *pref = 0, *ext = 0;
+ size_t plen;
+ NTSTATUS ret = NT_STATUS_UNSUCCESSFUL;
+
+ if (!fname || !*fname)
+ return NT_STATUS_INVALID_PARAMETER;
+
+ if (strlen_w(fname) > 12)
+ return NT_STATUS_UNSUCCESSFUL;
+
+ if (strcmp_wa(fname, ".") == 0 || strcmp_wa(fname, "..") == 0)
+ return NT_STATUS_OK;
+
+ if (!NT_STATUS_IS_OK(is_valid_name(fname, allow_wildcards, True)))
+ goto done;
+
+ if (!NT_STATUS_IS_OK(mangle_get_prefix(fname, &pref, &ext, allow_wildcards)))
+ goto done;
+ plen = strlen_w(pref);
+
+ if (strchr_wa(pref, '.'))
+ goto done;
+ if (plen < 1 || plen > 8)
+ goto done;
+ if (ext && (strlen_w(ext) > 3))
+ goto done;
+
+ ret = NT_STATUS_OK;
+
+done:
+ SAFE_FREE(pref);
+ SAFE_FREE(ext);
+ return ret;
+}
+
+static BOOL is_8_3(const char *fname, BOOL check_case, BOOL allow_wildcards)
+{
+ const char *f;
+ smb_ucs2_t *ucs2name;
+ NTSTATUS ret = NT_STATUS_UNSUCCESSFUL;
+ size_t size;
+
+ if (!fname || !*fname)
+ return False;
+ if ((f = strrchr(fname, '/')) == NULL)
+ f = fname;
+ else
+ f++;
+
+ if (strlen(f) > 12)
+ return False;
+
+ size = push_ucs2_allocate(&ucs2name, f);
+ if (size == (size_t)-1) {
+ DEBUG(0,("is_8_3: internal error push_ucs2_allocate() failed!\n"));
+ goto done;
+ }
+
+ ret = is_8_3_w(ucs2name, allow_wildcards);
+
+done:
+ SAFE_FREE(ucs2name);
+
+ if (!NT_STATUS_IS_OK(ret)) {
+ return False;
+ }
+
+ return True;
+}
+
+
+
+/* -------------------------------------------------------------------------- **
+ * Functions...
+ */
+
+/* ************************************************************************** **
+ * Initialize the static character test array.
+ *
+ * Input: none
+ *
+ * Output: none
+ *
+ * Notes: This function changes (loads) the contents of the <chartest>
+ * array. The scope of <chartest> is this file.
+ *
+ * ************************************************************************** **
+ */
+static void init_chartest( void )
+{
+ const char *illegalchars = "*\\/?<>|\":";
+ const unsigned char *s;
+
+ memset( (char *)chartest, '\0', 256 );
+
+ for( s = (const unsigned char *)illegalchars; *s; s++ )
+ chartest[*s] = ILLEGAL_MASK;
+
+ for( s = (const unsigned char *)basechars; *s; s++ )
+ chartest[*s] |= BASECHAR_MASK;
+
+ ct_initialized = True;
+}
+
+/* ************************************************************************** **
+ * Return True if the name *could be* a mangled name.
+ *
+ * Input: s - A path name - in UNIX pathname format.
+ *
+ * Output: True if the name matches the pattern described below in the
+ * notes, else False.
+ *
+ * Notes: The input name is *not* tested for 8.3 compliance. This must be
+ * done separately. This function returns true if the name contains
+ * a magic character followed by excactly two characters from the
+ * basechars list (above), which in turn are followed either by the
+ * nul (end of string) byte or a dot (extension) or by a '/' (end of
+ * a directory name).
+ *
+ * ************************************************************************** **
+ */
+static BOOL is_mangled(const char *s)
+{
+ char *magic;
+
+ if( !ct_initialized )
+ init_chartest();
+
+ magic = strchr_m( s, magic_char );
+ while( magic && magic[1] && magic[2] ) { /* 3 chars, 1st is magic. */
+ if( ('.' == magic[3] || '/' == magic[3] || !(magic[3])) /* Ends with '.' or nul or '/' ? */
+ && isbasechar( toupper(magic[1]) ) /* is 2nd char basechar? */
+ && isbasechar( toupper(magic[2]) ) ) /* is 3rd char basechar? */
+ return( True ); /* If all above, then true, */
+ magic = strchr_m( magic+1, magic_char ); /* else seek next magic. */
+ }
+ return( False );
+}
+
+/* ************************************************************************** **
+ * Compare two cache keys and return a value indicating their ordinal
+ * relationship.
+ *
+ * Input: ItemPtr - Pointer to a comparison key. In this case, this will
+ * be a mangled name string.
+ * NodePtr - Pointer to a node in the cache. The node structure
+ * will be followed in memory by a mangled name string.
+ *
+ * Output: A signed integer, as follows:
+ * (x < 0) <==> Key1 less than Key2
+ * (x == 0) <==> Key1 equals Key2
+ * (x > 0) <==> Key1 greater than Key2
+ *
+ * Notes: This is a ubiqx-style comparison routine. See ubi_BinTree for
+ * more info.
+ *
+ * ************************************************************************** **
+ */
+static signed int cache_compare( ubi_btItemPtr ItemPtr, ubi_btNodePtr NodePtr )
+{
+ char *Key1 = (char *)ItemPtr;
+ char *Key2 = (char *)(((ubi_cacheEntryPtr)NodePtr) + 1);
+
+ return( StrCaseCmp( Key1, Key2 ) );
+}
+
+/* ************************************************************************** **
+ * Free a cache entry.
+ *
+ * Input: WarrenZevon - Pointer to the entry that is to be returned to
+ * Nirvana.
+ * Output: none.
+ *
+ * Notes: This function gets around the possibility that the standard
+ * free() function may be implemented as a macro, or other evil
+ * subversions (oh, so much fun).
+ *
+ * ************************************************************************** **
+ */
+static void cache_free_entry( ubi_trNodePtr WarrenZevon )
+{
+ ZERO_STRUCTP(WarrenZevon);
+ SAFE_FREE( WarrenZevon );
+}
+
+/* ************************************************************************** **
+ * Initializes or clears the mangled cache.
+ *
+ * Input: none.
+ * Output: none.
+ *
+ * Notes: There is a section below that is commented out. It shows how
+ * one might use lp_ calls to set the maximum memory and entry size
+ * of the cache. You might also want to remove the constants used
+ * in ubi_cacheInit() and replace them with lp_ calls. If so, then
+ * the calls to ubi_cacheSetMax*() would be moved into the else
+ * clause. Another option would be to pass in the max_entries and
+ * max_memory values as parameters. crh 09-Apr-1998.
+ *
+ * ************************************************************************** **
+ */
+
+static void mangle_reset( void )
+{
+ if( !mc_initialized ) {
+ (void)ubi_cacheInit( mangled_cache,
+ cache_compare,
+ cache_free_entry,
+ MANGLED_CACHE_MAX_ENTRIES,
+ MANGLED_CACHE_MAX_MEMORY );
+ mc_initialized = True;
+ } else {
+ (void)ubi_cacheClear( mangled_cache );
+ }
+
+ /*
+ (void)ubi_cacheSetMaxEntries( mangled_cache, lp_mangled_cache_entries() );
+ (void)ubi_cacheSetMaxMemory( mangled_cache, lp_mangled_cache_memory() );
+ */
+}
+
+/* ************************************************************************** **
+ * Add a mangled name into the cache.
+ *
+ * Notes: If the mangled cache has not been initialized, then the
+ * function will simply fail. It could initialize the cache,
+ * but that's not the way it was done before I changed the
+ * cache mechanism, so I'm sticking with the old method.
+ *
+ * If the extension of the raw name maps directly to the
+ * extension of the mangled name, then we'll store both names
+ * *without* extensions. That way, we can provide consistent
+ * reverse mangling for all names that match. The test here is
+ * a bit more careful than the one done in earlier versions of
+ * mangle.c:
+ *
+ * - the extension must exist on the raw name,
+ * - it must be all lower case
+ * - it must match the mangled extension (to prove that no
+ * mangling occurred).
+ *
+ * crh 07-Apr-1998
+ *
+ * ************************************************************************** **
+ */
+static void cache_mangled_name( char *mangled_name, char *raw_name )
+{
+ ubi_cacheEntryPtr new_entry;
+ char *s1;
+ char *s2;
+ size_t mangled_len;
+ size_t raw_len;
+ size_t i;
+
+ /* If the cache isn't initialized, give up. */
+ if( !mc_initialized )
+ return;
+
+ /* Init the string lengths. */
+ mangled_len = strlen( mangled_name );
+ raw_len = strlen( raw_name );
+
+ /* See if the extensions are unmangled. If so, store the entry
+ * without the extension, thus creating a "group" reverse map.
+ */
+ s1 = strrchr( mangled_name, '.' );
+ if( s1 && (s2 = strrchr( raw_name, '.' )) ) {
+ i = 1;
+ while( s1[i] && (tolower( s1[i] ) == s2[i]) )
+ i++;
+ if( !s1[i] && !s2[i] ) {
+ mangled_len -= i;
+ raw_len -= i;
+ }
+ }
+
+ /* Allocate a new cache entry. If the allocation fails, just return. */
+ i = sizeof( ubi_cacheEntry ) + mangled_len + raw_len + 2;
+ new_entry = malloc( i );
+ if( !new_entry )
+ return;
+
+ /* Fill the new cache entry, and add it to the cache. */
+ s1 = (char *)(new_entry + 1);
+ s2 = (char *)&(s1[mangled_len + 1]);
+ safe_strcpy( s1, mangled_name, mangled_len );
+ safe_strcpy( s2, raw_name, raw_len );
+ ubi_cachePut( mangled_cache, i, new_entry, s1 );
+}
+
+/* ************************************************************************** **
+ * Check for a name on the mangled name stack
+ *
+ * Input: s - Input *and* output string buffer.
+ *
+ * Output: True if the name was found in the cache, else False.
+ *
+ * Notes: If a reverse map is found, the function will overwrite the string
+ * space indicated by the input pointer <s>. This is frightening.
+ * It should be rewritten to return NULL if the long name was not
+ * found, and a pointer to the long name if it was found.
+ *
+ * ************************************************************************** **
+ */
+
+static BOOL check_cache( char *s )
+{
+ ubi_cacheEntryPtr FoundPtr;
+ char *ext_start = NULL;
+ char *found_name;
+ char *saved_ext = NULL;
+
+ /* If the cache isn't initialized, give up. */
+ if( !mc_initialized )
+ return( False );
+
+ FoundPtr = ubi_cacheGet( mangled_cache, (ubi_trItemPtr)s );
+
+ /* If we didn't find the name *with* the extension, try without. */
+ if( !FoundPtr ) {
+ ext_start = strrchr( s, '.' );
+ if( ext_start ) {
+ if((saved_ext = strdup(ext_start)) == NULL)
+ return False;
+
+ *ext_start = '\0';
+ FoundPtr = ubi_cacheGet( mangled_cache, (ubi_trItemPtr)s );
+ /*
+ * At this point s is the name without the
+ * extension. We re-add the extension if saved_ext
+ * is not null, before freeing saved_ext.
+ */
+ }
+ }
+
+ /* Okay, if we haven't found it we're done. */
+ if( !FoundPtr ) {
+ if(saved_ext) {
+ /* Replace the saved_ext as it was truncated. */
+ (void)pstrcat( s, saved_ext );
+ SAFE_FREE(saved_ext);
+ }
+ return( False );
+ }
+
+ /* If we *did* find it, we need to copy it into the string buffer. */
+ found_name = (char *)(FoundPtr + 1);
+ found_name += (strlen( found_name ) + 1);
+
+ (void)pstrcpy( s, found_name );
+ if( saved_ext ) {
+ /* Replace the saved_ext as it was truncated. */
+ (void)pstrcat( s, saved_ext );
+ SAFE_FREE(saved_ext);
+ }
+
+ return( True );
+}
+
+/*****************************************************************************
+ * do the actual mangling to 8.3 format
+ * the buffer must be able to hold 13 characters (including the null)
+ *****************************************************************************
+ */
+static void to_8_3(char *s)
+{
+ int csum;
+ char *p;
+ char extension[4];
+ char base[9];
+ int baselen = 0;
+ int extlen = 0;
+
+ extension[0] = 0;
+ base[0] = 0;
+
+ p = strrchr(s,'.');
+ if( p && (strlen(p+1) < (size_t)4) ) {
+ BOOL all_normal = ( strisnormal(p+1) ); /* XXXXXXXXX */
+
+ if( all_normal && p[1] != 0 ) {
+ *p = 0;
+ csum = str_checksum( s );
+ *p = '.';
+ } else
+ csum = str_checksum(s);
+ } else
+ csum = str_checksum(s);
+
+ strupper_m( s );
+
+ if( p ) {
+ if( p == s )
+ safe_strcpy( extension, "___", 3 );
+ else {
+ *p++ = 0;
+ while( *p && extlen < 3 ) {
+ if ( *p != '.') {
+ extension[extlen++] = p[0];
+ }
+ p++;
+ }
+ extension[extlen] = 0;
+ }
+ }
+
+ p = s;
+
+ while( *p && baselen < 5 ) {
+ if (*p != '.') {
+ base[baselen++] = p[0];
+ }
+ p++;
+ }
+ base[baselen] = 0;
+
+ csum = csum % (MANGLE_BASE*MANGLE_BASE);
+
+ (void)slprintf(s, 12, "%s%c%c%c",
+ base, magic_char, mangle( csum/MANGLE_BASE ), mangle( csum ) );
+
+ if( *extension ) {
+ (void)pstrcat( s, "." );
+ (void)pstrcat( s, extension );
+ }
+}
+
+/*****************************************************************************
+ * Convert a filename to DOS format. Return True if successful.
+ *
+ * Input: OutName - Source *and* destination buffer.
+ *
+ * NOTE that OutName must point to a memory space that
+ * is at least 13 bytes in size!
+ *
+ * need83 - If False, name mangling will be skipped unless the
+ * name contains illegal characters. Mapping will still
+ * be done, if appropriate. This is probably used to
+ * signal that a client does not require name mangling,
+ * thus skipping the name mangling even on shares which
+ * have name-mangling turned on.
+ * cache83 - If False, the mangled name cache will not be updated.
+ * This is usually used to prevent that we overwrite
+ * a conflicting cache entry prematurely, i.e. before
+ * we know whether the client is really interested in the
+ * current name. (See PR#13758). UKD.
+ *
+ * Output: Returns False only if the name wanted mangling but the share does
+ * not have name mangling turned on.
+ *
+ * ****************************************************************************
+ */
+
+static void name_map(char *OutName, BOOL need83, BOOL cache83)
+{
+ smb_ucs2_t *OutName_ucs2;
+ DEBUG(5,("name_map( %s, need83 = %s, cache83 = %s)\n", OutName,
+ need83 ? "True" : "False", cache83 ? "True" : "False"));
+
+ if (push_ucs2_allocate(&OutName_ucs2, OutName) == (size_t)-1) {
+ DEBUG(0, ("push_ucs2_allocate failed!\n"));
+ return;
+ }
+
+ if( !need83 && !NT_STATUS_IS_OK(is_valid_name(OutName_ucs2, False, False)))
+ need83 = True;
+
+ /* check if it's already in 8.3 format */
+ if (need83 && !NT_STATUS_IS_OK(is_8_3_w(OutName_ucs2, False))) {
+ char *tmp = NULL;
+
+ /* mangle it into 8.3 */
+ if (cache83)
+ tmp = strdup(OutName);
+
+ to_8_3(OutName);
+
+ if(tmp != NULL) {
+ cache_mangled_name(OutName, tmp);
+ SAFE_FREE(tmp);
+ }
+ }
+
+ DEBUG(5,("name_map() ==> [%s]\n", OutName));
+ SAFE_FREE(OutName_ucs2);
+}
+
+/*
+ the following provides the abstraction layer to make it easier
+ to drop in an alternative mangling implementation
+*/
+static struct mangle_fns mangle_fns = {
+ is_mangled,
+ is_8_3,
+ mangle_reset,
+ check_cache,
+ name_map
+};
+
+/* return the methods for this mangling implementation */
+struct mangle_fns *mangle_hash_init(void)
+{
+ mangle_reset();
+
+ return &mangle_fns;
+}
diff --git a/source/smbd/mangle_hash2.c b/source/smbd/mangle_hash2.c
new file mode 100644
index 00000000000..62087e7e593
--- /dev/null
+++ b/source/smbd/mangle_hash2.c
@@ -0,0 +1,709 @@
+/*
+ Unix SMB/CIFS implementation.
+ new hash based name mangling implementation
+ Copyright (C) Andrew Tridgell 2002
+ Copyright (C) Simo Sorce 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 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.
+*/
+
+/*
+ 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
+ */
+
+ /* 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
+ */
+
+/*
+ ===============================================================================
+ 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!!
+ ===============================================================================
+*/
+
+
+#include "includes.h"
+
+#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
+
+/* by default have a max of 4096 entries in the cache. */
+#ifndef MANGLE_CACHE_SIZE
+#define MANGLE_CACHE_SIZE 4096
+#endif
+
+#define FNV1_PRIME 0x01000193
+/*the following number is a fnv1 of the string: idra@samba.org 2002 */
+#define FNV1_INIT 0xa6b93095
+
+/* these tables are used to provide fast tests for characters */
+static unsigned char char_flags[256];
+
+#define FLAG_CHECK(c, flag) (char_flags[(unsigned char)(c)] & (flag))
+
+/*
+ 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.
+*/
+static unsigned mangle_prefix;
+
+/* 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
+*/
+static char **prefix_cache;
+static u32 *prefix_cache_hashes;
+
+/* these are the characters we use in the 8.3 hash. Must be 36 chars long */
+static const char *basechars = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
+static unsigned char base_reverse[256];
+#define base_forward(v) basechars[v]
+
+/* the list of reserved dos names - all of these are illegal */
+static const char *reserved_names[] =
+{ "AUX", "LOCK$", "CON", "COM1", "COM2", "COM3", "COM4",
+ "LPT1", "LPT2", "LPT3", "NUL", "PRN", NULL };
+
+/*
+ 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 u32 mangle_hash(const char *key, unsigned length)
+{
+ u32 value;
+ u32 i;
+ fstring str;
+
+ /* we have to uppercase here to ensure that the mangled name
+ doesn't depend on the case of the long name. Note that this
+ is the only place where we need to use a multi-byte string
+ function */
+ strncpy(str, key, length);
+ str[length] = 0;
+ strupper_m(str);
+
+ /* the length of a multi-byte string can change after a strupper_m */
+ length = strlen(str);
+
+ /* Set the initial value from the key size. */
+ for (value = FNV1_INIT, i=0; i < length; i++) {
+ value *= (u32)FNV1_PRIME;
+ value ^= (u32)(str[i]);
+ }
+
+ /* note that we force it to a 31 bit hash, to keep within the limits
+ of the 36^6 mangle space */
+ return value & ~0x80000000;
+}
+
+/*
+ initialise (ie. allocate) the prefix cache
+ */
+static BOOL cache_init(void)
+{
+ if (prefix_cache) return True;
+
+ prefix_cache = calloc(MANGLE_CACHE_SIZE, sizeof(char *));
+ if (!prefix_cache) return False;
+
+ prefix_cache_hashes = calloc(MANGLE_CACHE_SIZE, sizeof(u32));
+ if (!prefix_cache_hashes) return False;
+
+ return True;
+}
+
+/*
+ insert an entry into the prefix cache. The string might not be null
+ terminated */
+static void cache_insert(const char *prefix, int length, u32 hash)
+{
+ int i = hash % MANGLE_CACHE_SIZE;
+
+ if (prefix_cache[i]) {
+ free(prefix_cache[i]);
+ }
+
+ prefix_cache[i] = strndup(prefix, length);
+ prefix_cache_hashes[i] = hash;
+}
+
+/*
+ lookup an entry in the prefix cache. Return NULL if not found.
+*/
+static const char *cache_lookup(u32 hash)
+{
+ int i = hash % MANGLE_CACHE_SIZE;
+
+ if (!prefix_cache[i] || hash != prefix_cache_hashes[i]) {
+ return NULL;
+ }
+
+ /* yep, it matched */
+ return 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(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<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=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(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(s, PTR_DIFF(p, s))) {
+ return True;
+ }
+ }
+
+ /* and the last part ... */
+ return is_mangled_component(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(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;
+}
+
+
+/*
+ reset the mangling cache on a smb.conf reload. This only really makes sense for
+ mangling backends that have parameters in smb.conf, and as this backend doesn't
+ this is a NULL operation
+*/
+static void mangle_reset(void)
+{
+ /* noop */
+}
+
+
+/*
+ try to find a 8.3 name in the cache, and if found then
+ replace the string with the original long name.
+
+ The filename must be able to hold at least sizeof(fstring)
+*/
+static BOOL check_cache(char *name)
+{
+ u32 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(name)) {
+ M_DEBUG(10,("check_cache: %s -> not mangled\n", name));
+ return False;
+ }
+
+ /* we need to extract the hash from the 8.3 name */
+ hash = base_reverse[(unsigned char)name[7]];
+ for (multiplier=36, i=5;i>=mangle_prefix;i--) {
+ u32 v = base_reverse[(unsigned char)name[i]];
+ hash += multiplier * v;
+ multiplier *= 36;
+ }
+
+ /* now look in the prefix cache for that hash */
+ prefix = cache_lookup(hash);
+ if (!prefix) {
+ M_DEBUG(10,("check_cache: %s -> %08X -> not found\n", name, hash));
+ return False;
+ }
+
+ /* 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]) {
+ M_DEBUG(10,("check_cache: %s -> %s.%s\n", name, prefix, extension));
+ slprintf(name, sizeof(fstring), "%s.%s", prefix, extension);
+ } else {
+ M_DEBUG(10,("check_cache: %s -> %s\n", name, prefix));
+ fstrcpy(name, prefix);
+ }
+
+ return True;
+}
+
+
+/*
+ look for a DOS reserved name
+*/
+static BOOL is_reserved_name(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++) {
+ int len = strlen(reserved_names[i]);
+ /* note that we match on COM1 as well as COM1.foo */
+ if (strnequal(name, reserved_names[i], len) &&
+ (name[len] == '.' || name[len] == 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(const char *name)
+{
+ const char *dot_pos = NULL;
+ BOOL alldots = True;
+ size_t numdots = 0;
+
+ while (*name) {
+ if (((unsigned int)name[0]) > 128 && (name[1] != 0)) {
+ /* Possible start of mb character. */
+ char mbc[2];
+ /*
+ * Note that if CH_UNIX is utf8 a string may be 3
+ * bytes, but this is ok as mb utf8 characters don't
+ * contain embedded ascii bytes. We are really checking
+ * for mb UNIX asian characters like Japanese (SJIS) here.
+ * JRA.
+ */
+ if (convert_string(CH_UNIX, CH_UCS2, name, 2, mbc, 2, False) == 2) {
+ /* Was a good mb string. */
+ name += 2;
+ continue;
+ }
+ }
+
+ if (FLAG_CHECK(name[0], FLAG_ILLEGAL)) {
+ return False;
+ }
+ if (name[0] == '.') {
+ dot_pos = name;
+ numdots++;
+ } else {
+ alldots = False;
+ }
+ name++;
+ }
+
+ if (dot_pos) {
+ if (alldots && (numdots == 1 || numdots == 2))
+ return True; /* . or .. is a valid name */
+
+ /* A valid long name cannot end in '.' */
+ if (dot_pos[1] == '\0')
+ return False;
+ }
+
+ 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
+
+ the name parameter must be able to hold 13 bytes
+*/
+static void name_map(fstring name, BOOL need83, BOOL cache83)
+{
+ char *dot_p;
+ char lead_chars[7];
+ char extension[4];
+ unsigned int extension_length, i;
+ unsigned int prefix_len;
+ u32 hash, v;
+ char new_name[13];
+
+ /* reserved names are handled specially */
+ if (!is_reserved_name(name)) {
+ /* if the name is already a valid 8.3 name then we don't need to
+ do anything */
+ if (is_8_3(name, False, False)) {
+ return;
+ }
+
+ /* if the caller doesn't strictly need 8.3 then just check for illegal
+ filenames */
+ if (!need83 && is_legal_name(name)) {
+ return;
+ }
+ }
+
+ /* 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<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(lead_chars[i]);
+ }
+ for (;i<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++) {
+ 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(name, prefix_len);
+
+ /* now form the mangled name. */
+ for (i=0;i<mangle_prefix;i++) {
+ new_name[i] = lead_chars[i];
+ }
+ new_name[7] = base_forward(v % 36);
+ new_name[6] = '~';
+ for (i=5; i>=mangle_prefix; i--) {
+ v = v / 36;
+ new_name[i] = base_forward(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(name, prefix_len, hash);
+ }
+
+ M_DEBUG(10,("name_map: %s -> %08X -> %s (cache=%d)\n",
+ name, hash, new_name, cache83));
+
+ /* and overwrite the old name */
+ fstrcpy(name, new_name);
+
+ /* all done, we've managed to mangle it */
+}
+
+
+/* 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(void)
+{
+ int i;
+
+ memset(char_flags, 0, sizeof(char_flags));
+
+ for (i=1;i<128;i++) {
+ if ((i >= '0' && i <= '9') ||
+ (i >= 'a' && i <= 'z') ||
+ (i >= 'A' && i <= 'Z')) {
+ char_flags[i] |= (FLAG_ASCII | FLAG_BASECHAR);
+ }
+ if (strchr("_-$~", i)) {
+ char_flags[i] |= FLAG_ASCII;
+ }
+
+ if (strchr("*\\/?<>|\":", i)) {
+ char_flags[i] |= FLAG_ILLEGAL;
+ }
+
+ if (strchr("*?\"<>", i)) {
+ char_flags[i] |= FLAG_WILDCARD;
+ }
+ }
+
+ memset(base_reverse, 0, sizeof(base_reverse));
+ for (i=0;i<36;i++) {
+ base_reverse[(unsigned char)base_forward(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];
+
+ char_flags[c1] |= FLAG_POSSIBLE1;
+ char_flags[c2] |= FLAG_POSSIBLE2;
+ char_flags[c3] |= FLAG_POSSIBLE3;
+ char_flags[c4] |= FLAG_POSSIBLE4;
+ char_flags[tolower(c1)] |= FLAG_POSSIBLE1;
+ char_flags[tolower(c2)] |= FLAG_POSSIBLE2;
+ char_flags[tolower(c3)] |= FLAG_POSSIBLE3;
+ char_flags[tolower(c4)] |= FLAG_POSSIBLE4;
+
+ char_flags[(unsigned char)'.'] |= FLAG_POSSIBLE4;
+ }
+}
+
+
+/*
+ the following provides the abstraction layer to make it easier
+ to drop in an alternative mangling implementation */
+static struct mangle_fns mangle_fns = {
+ is_mangled,
+ is_8_3,
+ mangle_reset,
+ check_cache,
+ name_map
+};
+
+/* return the methods for this mangling implementation */
+struct mangle_fns *mangle_hash2_init(void)
+{
+ /* the mangle prefix can only be in the mange 1 to 6 */
+ mangle_prefix = lp_mangle_prefix();
+ if (mangle_prefix > 6) {
+ mangle_prefix = 6;
+ }
+ if (mangle_prefix < 1) {
+ mangle_prefix = 1;
+ }
+
+ init_tables();
+ mangle_reset();
+
+ if (!cache_init()) {
+ return NULL;
+ }
+
+ return &mangle_fns;
+}
diff --git a/source/smbd/mangle_map.c b/source/smbd/mangle_map.c
new file mode 100644
index 00000000000..9e798fd41b4
--- /dev/null
+++ b/source/smbd/mangle_map.c
@@ -0,0 +1,212 @@
+/*
+ Unix SMB/CIFS implementation.
+ Name mapping code
+ Copyright (C) Jeremy Allison 1998
+ 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 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"
+
+
+/* ************************************************************************** **
+ * Used only in do_fwd_mangled_map(), below.
+ * ************************************************************************** **
+ */
+static char *map_filename( char *s, /* This is null terminated */
+ const char *pattern, /* This isn't. */
+ int len ) /* This is the length of pattern. */
+ {
+ static pstring matching_bit; /* The bit of the string which matches */
+ /* a * in pattern if indeed there is a * */
+ char *sp; /* Pointer into s. */
+ char *pp; /* Pointer into p. */
+ char *match_start; /* Where the matching bit starts. */
+ pstring pat;
+
+ StrnCpy( pat, pattern, len ); /* Get pattern into a proper string! */
+ pstrcpy( matching_bit, "" ); /* Match but no star gets this. */
+ pp = pat; /* Initialize the pointers. */
+ sp = s;
+
+ if( strequal(s, ".") || strequal(s, ".."))
+ {
+ return NULL; /* Do not map '.' and '..' */
+ }
+
+ if( (len == 1) && (*pattern == '*') )
+ {
+ return NULL; /* Impossible, too ambiguous for */
+ } /* words! */
+
+ while( (*sp) /* Not the end of the string. */
+ && (*pp) /* Not the end of the pattern. */
+ && (*sp == *pp) /* The two match. */
+ && (*pp != '*') ) /* No wildcard. */
+ {
+ sp++; /* Keep looking. */
+ pp++;
+ }
+
+ if( !*sp && !*pp ) /* End of pattern. */
+ return( matching_bit ); /* Simple match. Return empty string. */
+
+ if( *pp == '*' )
+ {
+ pp++; /* Always interrested in the chacter */
+ /* after the '*' */
+ if( !*pp ) /* It is at the end of the pattern. */
+ {
+ StrnCpy( matching_bit, s, sp-s );
+ return( matching_bit );
+ }
+ else
+ {
+ /* The next character in pattern must match a character further */
+ /* along s than sp so look for that character. */
+ match_start = sp;
+ while( (*sp) /* Not the end of s. */
+ && (*sp != *pp) ) /* Not the same */
+ sp++; /* Keep looking. */
+ if( !*sp ) /* Got to the end without a match. */
+ {
+ return( NULL );
+ } /* Still hope for a match. */
+ else
+ {
+ /* Now sp should point to a matching character. */
+ StrnCpy(matching_bit, match_start, sp-match_start);
+ /* Back to needing a stright match again. */
+ while( (*sp) /* Not the end of the string. */
+ && (*pp) /* Not the end of the pattern. */
+ && (*sp == *pp) ) /* The two match. */
+ {
+ sp++; /* Keep looking. */
+ pp++;
+ }
+ if( !*sp && !*pp ) /* Both at end so it matched */
+ return( matching_bit );
+ else
+ return( NULL );
+ }
+ }
+ }
+ return( NULL ); /* No match. */
+ } /* map_filename */
+
+
+/* ************************************************************************** **
+ * MangledMap is a series of name pairs in () separated by spaces.
+ * If s matches the first of the pair then the name given is the
+ * second of the pair. A * means any number of any character and if
+ * present in the second of the pair as well as the first the
+ * matching part of the first string takes the place of the * in the
+ * second.
+ *
+ * I wanted this so that we could have RCS files which can be used
+ * by UNIX and DOS programs. My mapping string is (RCS rcs) which
+ * converts the UNIX RCS file subdirectory to lowercase thus
+ * preventing mangling.
+ *
+ * See 'mangled map' in smb.conf(5).
+ *
+ * ************************************************************************** **
+ */
+static void mangled_map(char *s, const char *MangledMap)
+{
+ const char *start=MangledMap; /* Use this to search for mappings. */
+ const char *end; /* Used to find the end of strings. */
+ char *match_string;
+ pstring new_string; /* Make up the result here. */
+ char *np; /* Points into new_string. */
+
+ DEBUG( 5, ("Mangled Mapping '%s' map '%s'\n", s, MangledMap) );
+ while( *start ) {
+ while( (*start) && (*start != '(') )
+ start++;
+ if( !*start )
+ continue; /* Always check for the end. */
+ start++; /* Skip the ( */
+ end = start; /* Search for the ' ' or a ')' */
+ DEBUG( 5, ("Start of first in pair '%s'\n", start) );
+ while( (*end) && !((*end == ' ') || (*end == ')')) )
+ end++;
+ if( !*end ) {
+ start = end;
+ continue; /* Always check for the end. */
+ }
+ DEBUG( 5, ("End of first in pair '%s'\n", end) );
+ if( (match_string = map_filename( s, start, end-start )) ) {
+ int size_left = sizeof(new_string) - 1;
+ DEBUG( 5, ("Found a match\n") );
+ /* Found a match. */
+ start = end + 1; /* Point to start of what it is to become. */
+ DEBUG( 5, ("Start of second in pair '%s'\n", start) );
+ end = start;
+ np = new_string;
+ while( (*end && size_left > 0) /* Not the end of string. */
+ && (*end != ')') /* Not the end of the pattern. */
+ && (*end != '*') ) { /* Not a wildcard. */
+ *np++ = *end++;
+ size_left--;
+ }
+
+ if( !*end ) {
+ start = end;
+ continue; /* Always check for the end. */
+ }
+ if( *end == '*' ) {
+ if (size_left > 0 )
+ safe_strcpy( np, match_string, size_left );
+ np += strlen( match_string );
+ size_left -= strlen( match_string );
+ end++; /* Skip the '*' */
+ while ((*end && size_left > 0) /* Not the end of string. */
+ && (*end != ')') /* Not the end of the pattern. */
+ && (*end != '*')) { /* Not a wildcard. */
+ *np++ = *end++;
+ size_left--;
+ }
+ }
+ if (!*end) {
+ start = end;
+ continue; /* Always check for the end. */
+ }
+ if (size_left > 0)
+ *np++ = '\0'; /* NULL terminate it. */
+ DEBUG(5,("End of second in pair '%s'\n", end));
+ new_string[sizeof(new_string)-1] = '\0';
+ pstrcpy( s, new_string ); /* Substitute with the new name. */
+ DEBUG( 5, ("s is now '%s'\n", s) );
+ }
+ start = end; /* Skip a bit which cannot be wanted anymore. */
+ start++;
+ }
+}
+
+/*
+ front end routine to the mangled map code
+ personally I think that the whole idea of "mangled map" is completely bogus
+*/
+void mangle_map_filename(fstring fname, int snum)
+{
+ char *map;
+
+ map = lp_mangled_map(snum);
+ if (!map || !*map) return;
+
+ mangled_map(fname, map);
+}
diff --git a/source/smbd/message.c b/source/smbd/message.c
new file mode 100644
index 00000000000..f853a914753
--- /dev/null
+++ b/source/smbd/message.c
@@ -0,0 +1,237 @@
+/*
+ Unix SMB/CIFS implementation.
+ SMB messaging
+ 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 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.
+*/
+/*
+ This file handles the messaging system calls for winpopup style
+ messages
+*/
+
+
+#include "includes.h"
+
+extern userdom_struct current_user_info;
+
+/* look in server.c for some explanation of these variables */
+static char msgbuf[1600];
+static int msgpos;
+static fstring msgfrom;
+static fstring msgto;
+
+/****************************************************************************
+deliver the message
+****************************************************************************/
+static void msg_deliver(void)
+{
+ pstring name;
+ int i;
+ int fd;
+ char *msg;
+ int len;
+
+ if (! (*lp_msg_command()))
+ {
+ DEBUG(1,("no messaging command specified\n"));
+ msgpos = 0;
+ return;
+ }
+
+ /* put it in a temporary file */
+ slprintf(name,sizeof(name)-1, "%s/msg.XXXXXX",tmpdir());
+ fd = smb_mkstemp(name);
+
+ if (fd == -1) {
+ DEBUG(1,("can't open message file %s\n",name));
+ return;
+ }
+
+ /*
+ * Incoming message is in DOS codepage format. Convert to UNIX.
+ */
+
+ if ((len = (int)convert_string_allocate(NULL,CH_DOS, CH_UNIX, msgbuf, msgpos, (void **) &msg, True)) < 0 || !msg) {
+ DEBUG(3,("Conversion failed, delivering message in DOS codepage format\n"));
+ for (i = 0; i < msgpos;) {
+ if (msgbuf[i] == '\r' && i < (msgpos-1) && msgbuf[i+1] == '\n') {
+ i++; continue;
+ }
+ write(fd, &msgbuf[i++], 1);
+ }
+ } else {
+ for (i = 0; i < len;) {
+ if (msg[i] == '\r' && i < (len-1) && msg[i+1] == '\n') {
+ i++; continue;
+ }
+ write(fd, &msg[i++],1);
+ }
+ SAFE_FREE(msg);
+ }
+ close(fd);
+
+
+ /* run the command */
+ if (*lp_msg_command())
+ {
+ fstring alpha_msgfrom;
+ fstring alpha_msgto;
+ pstring s;
+
+ pstrcpy(s,lp_msg_command());
+ pstring_sub(s,"%f",alpha_strcpy(alpha_msgfrom,msgfrom,NULL,sizeof(alpha_msgfrom)));
+ pstring_sub(s,"%t",alpha_strcpy(alpha_msgto,msgto,NULL,sizeof(alpha_msgto)));
+ standard_sub_basic(current_user_info.smb_name, s, sizeof(s));
+ pstring_sub(s,"%s",name);
+ smbrun(s,NULL);
+ }
+
+ msgpos = 0;
+}
+
+
+
+/****************************************************************************
+ reply to a sends
+****************************************************************************/
+int reply_sends(connection_struct *conn,
+ char *inbuf,char *outbuf, int dum_size, int dum_buffsize)
+{
+ int len;
+ char *msg;
+ int outsize = 0;
+ char *p;
+
+ START_PROFILE(SMBsends);
+
+ msgpos = 0;
+
+ if (! (*lp_msg_command())) {
+ END_PROFILE(SMBsends);
+ return(ERROR_DOS(ERRSRV,ERRmsgoff));
+ }
+
+ outsize = set_message(outbuf,0,0,True);
+
+ p = smb_buf(inbuf)+1;
+ p += srvstr_pull_buf(inbuf, msgfrom, p, sizeof(msgfrom), STR_TERMINATE) + 1;
+ p += srvstr_pull_buf(inbuf, msgto, p, sizeof(msgto), STR_TERMINATE) + 1;
+
+ msg = p;
+
+ len = SVAL(msg,0);
+ len = MIN(len,sizeof(msgbuf)-msgpos);
+
+ memset(msgbuf,'\0',sizeof(msgbuf));
+
+ memcpy(&msgbuf[msgpos],msg+2,len);
+ msgpos += len;
+
+ msg_deliver();
+
+ END_PROFILE(SMBsends);
+ return(outsize);
+}
+
+
+/****************************************************************************
+ reply to a sendstrt
+****************************************************************************/
+int reply_sendstrt(connection_struct *conn,
+ char *inbuf,char *outbuf, int dum_size, int dum_buffsize)
+{
+ int outsize = 0;
+ char *p;
+
+ START_PROFILE(SMBsendstrt);
+
+ if (! (*lp_msg_command())) {
+ END_PROFILE(SMBsendstrt);
+ return(ERROR_DOS(ERRSRV,ERRmsgoff));
+ }
+
+ outsize = set_message(outbuf,1,0,True);
+
+ memset(msgbuf,'\0',sizeof(msgbuf));
+ msgpos = 0;
+
+ p = smb_buf(inbuf)+1;
+ p += srvstr_pull_buf(inbuf, msgfrom, p, sizeof(msgfrom), STR_TERMINATE) + 1;
+ p += srvstr_pull_buf(inbuf, msgto, p, sizeof(msgto), STR_TERMINATE) + 1;
+
+ DEBUG( 3, ( "SMBsendstrt (from %s to %s)\n", msgfrom, msgto ) );
+
+ END_PROFILE(SMBsendstrt);
+ return(outsize);
+}
+
+
+/****************************************************************************
+ reply to a sendtxt
+****************************************************************************/
+int reply_sendtxt(connection_struct *conn,
+ char *inbuf,char *outbuf, int dum_size, int dum_buffsize)
+{
+ int len;
+ int outsize = 0;
+ char *msg;
+ START_PROFILE(SMBsendtxt);
+
+ if (! (*lp_msg_command())) {
+ END_PROFILE(SMBsendtxt);
+ return(ERROR_DOS(ERRSRV,ERRmsgoff));
+ }
+
+ outsize = set_message(outbuf,0,0,True);
+
+ msg = smb_buf(inbuf) + 1;
+
+ len = SVAL(msg,0);
+ len = MIN(len,sizeof(msgbuf)-msgpos);
+
+ memcpy(&msgbuf[msgpos],msg+2,len);
+ msgpos += len;
+
+ DEBUG( 3, ( "SMBsendtxt\n" ) );
+
+ END_PROFILE(SMBsendtxt);
+ return(outsize);
+}
+
+
+/****************************************************************************
+ reply to a sendend
+****************************************************************************/
+int reply_sendend(connection_struct *conn,
+ char *inbuf,char *outbuf, int dum_size, int dum_buffsize)
+{
+ int outsize = 0;
+ START_PROFILE(SMBsendend);
+
+ if (! (*lp_msg_command())) {
+ END_PROFILE(SMBsendend);
+ return(ERROR_DOS(ERRSRV,ERRmsgoff));
+ }
+
+ outsize = set_message(outbuf,0,0,True);
+
+ DEBUG(3,("SMBsendend\n"));
+
+ msg_deliver();
+
+ END_PROFILE(SMBsendend);
+ return(outsize);
+}
diff --git a/source/smbd/negprot.c b/source/smbd/negprot.c
new file mode 100644
index 00000000000..96961368fb1
--- /dev/null
+++ b/source/smbd/negprot.c
@@ -0,0 +1,550 @@
+/*
+ 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 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"
+
+extern int Protocol;
+extern int max_recv;
+BOOL global_encrypted_passwords_negotiated = False;
+BOOL global_spnego_negotiated = False;
+struct auth_context *negprot_global_auth_context = NULL;
+
+static void get_challenge(char buff[8])
+{
+ NTSTATUS nt_status;
+ const uint8 *cryptkey;
+
+ /* We might be called more than once, muliple negprots are premitted */
+ if (negprot_global_auth_context) {
+ DEBUG(3, ("get challenge: is this a secondary negprot? negprot_global_auth_context is non-NULL!\n"));
+ (negprot_global_auth_context->free)(&negprot_global_auth_context);
+ }
+
+ DEBUG(10, ("get challenge: creating negprot_global_auth_context\n"));
+ if (!NT_STATUS_IS_OK(nt_status = make_auth_context_subsystem(&negprot_global_auth_context))) {
+ DEBUG(0, ("make_auth_context_subsystem returned %s", nt_errstr(nt_status)));
+ smb_panic("cannot make_negprot_global_auth_context!\n");
+ }
+ DEBUG(10, ("get challenge: getting challenge\n"));
+ cryptkey = negprot_global_auth_context->get_ntlm_challenge(negprot_global_auth_context);
+ memcpy(buff, cryptkey, 8);
+}
+
+/****************************************************************************
+ Reply for the core protocol.
+****************************************************************************/
+
+static int reply_corep(char *inbuf, char *outbuf)
+{
+ int outsize = set_message(outbuf,1,0,True);
+
+ Protocol = PROTOCOL_CORE;
+
+ return outsize;
+}
+
+/****************************************************************************
+ Reply for the coreplus protocol.
+****************************************************************************/
+
+static int reply_coreplus(char *inbuf, char *outbuf)
+{
+ int raw = (lp_readraw()?1:0) | (lp_writeraw()?2:0);
+ int outsize = set_message(outbuf,13,0,True);
+ SSVAL(outbuf,smb_vwv5,raw); /* tell redirector we support
+ readbraw and writebraw (possibly) */
+ /* Reply, SMBlockread, SMBwritelock supported. */
+ SCVAL(outbuf,smb_flg,FLAG_REPLY|FLAG_SUPPORT_LOCKREAD);
+ SSVAL(outbuf,smb_vwv1,0x1); /* user level security, don't encrypt */
+
+ Protocol = PROTOCOL_COREPLUS;
+
+ return outsize;
+}
+
+/****************************************************************************
+ Reply for the lanman 1.0 protocol.
+****************************************************************************/
+
+static int reply_lanman1(char *inbuf, char *outbuf)
+{
+ int raw = (lp_readraw()?1:0) | (lp_writeraw()?2:0);
+ int secword=0;
+ time_t t = time(NULL);
+
+ global_encrypted_passwords_negotiated = lp_encrypted_passwords();
+
+ if (lp_security()>=SEC_USER)
+ secword |= NEGOTIATE_SECURITY_USER_LEVEL;
+ if (global_encrypted_passwords_negotiated)
+ secword |= NEGOTIATE_SECURITY_CHALLENGE_RESPONSE;
+
+ set_message(outbuf,13,global_encrypted_passwords_negotiated?8:0,True);
+ SSVAL(outbuf,smb_vwv1,secword);
+ /* Create a token value and add it to the outgoing packet. */
+ if (global_encrypted_passwords_negotiated) {
+ get_challenge(smb_buf(outbuf));
+ SSVAL(outbuf,smb_vwv11, 8);
+ }
+
+ Protocol = PROTOCOL_LANMAN1;
+
+ /* Reply, SMBlockread, SMBwritelock supported. */
+ SCVAL(outbuf,smb_flg,FLAG_REPLY|FLAG_SUPPORT_LOCKREAD);
+ SSVAL(outbuf,smb_vwv2,max_recv);
+ SSVAL(outbuf,smb_vwv3,lp_maxmux()); /* maxmux */
+ SSVAL(outbuf,smb_vwv4,1);
+ SSVAL(outbuf,smb_vwv5,raw); /* tell redirector we support
+ readbraw writebraw (possibly) */
+ SIVAL(outbuf,smb_vwv6,sys_getpid());
+ SSVAL(outbuf,smb_vwv10, TimeDiff(t)/60);
+
+ put_dos_date(outbuf,smb_vwv8,t);
+
+ return (smb_len(outbuf)+4);
+}
+
+/****************************************************************************
+ Reply for the lanman 2.0 protocol.
+****************************************************************************/
+
+static int reply_lanman2(char *inbuf, char *outbuf)
+{
+ int raw = (lp_readraw()?1:0) | (lp_writeraw()?2:0);
+ int secword=0;
+ time_t t = time(NULL);
+
+ global_encrypted_passwords_negotiated = lp_encrypted_passwords();
+
+ if (lp_security()>=SEC_USER)
+ secword |= NEGOTIATE_SECURITY_USER_LEVEL;
+ if (global_encrypted_passwords_negotiated)
+ secword |= NEGOTIATE_SECURITY_CHALLENGE_RESPONSE;
+
+ set_message(outbuf,13,global_encrypted_passwords_negotiated?8:0,True);
+ SSVAL(outbuf,smb_vwv1,secword);
+ SIVAL(outbuf,smb_vwv6,sys_getpid());
+
+ /* Create a token value and add it to the outgoing packet. */
+ if (global_encrypted_passwords_negotiated) {
+ get_challenge(smb_buf(outbuf));
+ SSVAL(outbuf,smb_vwv11, 8);
+ }
+
+ Protocol = PROTOCOL_LANMAN2;
+
+ /* Reply, SMBlockread, SMBwritelock supported. */
+ SCVAL(outbuf,smb_flg,FLAG_REPLY|FLAG_SUPPORT_LOCKREAD);
+ SSVAL(outbuf,smb_vwv2,max_recv);
+ SSVAL(outbuf,smb_vwv3,lp_maxmux());
+ SSVAL(outbuf,smb_vwv4,1);
+ SSVAL(outbuf,smb_vwv5,raw); /* readbraw and/or writebraw */
+ SSVAL(outbuf,smb_vwv10, TimeDiff(t)/60);
+ put_dos_date(outbuf,smb_vwv8,t);
+
+ return (smb_len(outbuf)+4);
+}
+
+/****************************************************************************
+ Generate the spnego negprot reply blob. Return the number of bytes used.
+****************************************************************************/
+
+static int negprot_spnego(char *p)
+{
+ DATA_BLOB blob;
+ nstring dos_name;
+ fstring unix_name;
+ uint8 guid[17];
+ const char *OIDs_krb5[] = {OID_KERBEROS5,
+ OID_KERBEROS5_OLD,
+ OID_NTLMSSP,
+ NULL};
+ const char *OIDs_plain[] = {OID_NTLMSSP, NULL};
+ char *principal;
+ int len;
+
+ global_spnego_negotiated = True;
+
+ ZERO_STRUCT(guid);
+
+ safe_strcpy(unix_name, global_myname(), sizeof(unix_name)-1);
+ strlower_m(unix_name);
+ push_ascii_nstring(dos_name, unix_name);
+ safe_strcpy((char *)guid, dos_name, sizeof(guid)-1);
+
+#ifdef DEVELOPER
+ /* valgrind fixer... */
+ {
+ size_t sl = strlen(guid);
+ if (sizeof(guid)-sl)
+ memset(&guid[sl], '\0', sizeof(guid)-sl);
+ }
+#endif
+
+#if 0
+ /* strangely enough, NT does not sent the single OID NTLMSSP when
+ not a ADS member, it sends no OIDs at all
+
+ we can't do this until we teach our sesssion setup parser to know
+ about raw NTLMSSP (clients send no ASN.1 wrapping if we do this)
+ */
+ if (lp_security() != SEC_ADS) {
+ memcpy(p, guid, 16);
+ return 16;
+ }
+#endif
+ if (lp_security() != SEC_ADS) {
+ blob = spnego_gen_negTokenInit(guid, OIDs_plain, "NONE");
+ } else {
+ asprintf(&principal, "%s$@%s", guid, lp_realm());
+ blob = spnego_gen_negTokenInit(guid, OIDs_krb5, principal);
+ free(principal);
+ }
+ memcpy(p, blob.data, blob.length);
+ len = blob.length;
+ data_blob_free(&blob);
+ return len;
+}
+
+/****************************************************************************
+ Reply for the nt protocol.
+****************************************************************************/
+
+static int reply_nt1(char *inbuf, char *outbuf)
+{
+ /* dual names + lock_and_read + nt SMBs + remote API calls */
+ int capabilities = CAP_NT_FIND|CAP_LOCK_AND_READ|
+ CAP_LEVEL_II_OPLOCKS;
+
+ int secword=0;
+ time_t t = time(NULL);
+ char *p, *q;
+ BOOL negotiate_spnego = False;
+
+ global_encrypted_passwords_negotiated = lp_encrypted_passwords();
+
+ /* do spnego in user level security if the client
+ supports it and we can do encrypted passwords */
+
+ if (global_encrypted_passwords_negotiated &&
+ (lp_security() != SEC_SHARE) &&
+ lp_use_spnego() &&
+ (SVAL(inbuf, smb_flg2) & FLAGS2_EXTENDED_SECURITY)) {
+ negotiate_spnego = True;
+ capabilities |= CAP_EXTENDED_SECURITY;
+ }
+
+ capabilities |= CAP_NT_SMBS|CAP_RPC_REMOTE_APIS;
+
+ if (lp_unix_extensions()) {
+ capabilities |= CAP_UNIX;
+ }
+
+ if (lp_large_readwrite() && (SMB_OFF_T_BITS == 64))
+ capabilities |= CAP_LARGE_READX|CAP_LARGE_WRITEX|CAP_W2K_SMBS;
+
+ if (SMB_OFF_T_BITS == 64)
+ capabilities |= CAP_LARGE_FILES;
+
+ if (lp_readraw() && lp_writeraw())
+ capabilities |= CAP_RAW_MODE;
+
+ /* allow for disabling unicode */
+ if (lp_unicode())
+ capabilities |= CAP_UNICODE;
+
+ if (lp_nt_status_support())
+ capabilities |= CAP_STATUS32;
+
+ if (lp_host_msdfs())
+ capabilities |= CAP_DFS;
+
+ if (lp_security() >= SEC_USER)
+ secword |= NEGOTIATE_SECURITY_USER_LEVEL;
+ if (global_encrypted_passwords_negotiated)
+ secword |= NEGOTIATE_SECURITY_CHALLENGE_RESPONSE;
+
+ if (lp_server_signing()) {
+ if (lp_security() >= SEC_USER) {
+ secword |= NEGOTIATE_SECURITY_SIGNATURES_ENABLED;
+ /* No raw mode with smb signing. */
+ capabilities &= ~CAP_RAW_MODE;
+ if (lp_server_signing() == Required)
+ secword |=NEGOTIATE_SECURITY_SIGNATURES_REQUIRED;
+ srv_set_signing_negotiated();
+ } else {
+ DEBUG(0,("reply_nt1: smb signing is incompatible with share level security !\n"));
+ if (lp_server_signing() == Required) {
+ exit_server("reply_nt1: smb signing required and share level security selected.");
+ }
+ }
+ }
+
+ set_message(outbuf,17,0,True);
+
+ SCVAL(outbuf,smb_vwv1,secword);
+
+ Protocol = PROTOCOL_NT1;
+
+ SSVAL(outbuf,smb_vwv1+1,lp_maxmux()); /* maxmpx */
+ SSVAL(outbuf,smb_vwv2+1,1); /* num vcs */
+ SIVAL(outbuf,smb_vwv3+1,max_recv); /* max buffer. LOTS! */
+ SIVAL(outbuf,smb_vwv5+1,0x10000); /* raw size. full 64k */
+ SIVAL(outbuf,smb_vwv7+1,sys_getpid()); /* session key */
+ SIVAL(outbuf,smb_vwv9+1,capabilities); /* capabilities */
+ put_long_date(outbuf+smb_vwv11+1,t);
+ SSVALS(outbuf,smb_vwv15+1,TimeDiff(t)/60);
+
+ p = q = smb_buf(outbuf);
+ if (!negotiate_spnego) {
+ /* Create a token value and add it to the outgoing packet. */
+ if (global_encrypted_passwords_negotiated) {
+ /* note that we do not send a challenge at all if
+ we are using plaintext */
+ get_challenge(p);
+ SSVALS(outbuf,smb_vwv16+1,8);
+ p += 8;
+ }
+ p += srvstr_push(outbuf, p, lp_workgroup(), -1,
+ STR_UNICODE|STR_TERMINATE|STR_NOALIGN);
+ DEBUG(3,("not using SPNEGO\n"));
+ } else {
+ int len = negprot_spnego(p);
+
+ SSVALS(outbuf,smb_vwv16+1,len);
+ p += len;
+ DEBUG(3,("using SPNEGO\n"));
+ }
+
+ SSVAL(outbuf,smb_vwv17, p - q); /* length of challenge+domain strings */
+ set_message_end(outbuf, p);
+
+ return (smb_len(outbuf)+4);
+}
+
+/* these are the protocol lists used for auto architecture detection:
+
+WinNT 3.51:
+protocol [PC NETWORK PROGRAM 1.0]
+protocol [XENIX CORE]
+protocol [MICROSOFT NETWORKS 1.03]
+protocol [LANMAN1.0]
+protocol [Windows for Workgroups 3.1a]
+protocol [LM1.2X002]
+protocol [LANMAN2.1]
+protocol [NT LM 0.12]
+
+Win95:
+protocol [PC NETWORK PROGRAM 1.0]
+protocol [XENIX CORE]
+protocol [MICROSOFT NETWORKS 1.03]
+protocol [LANMAN1.0]
+protocol [Windows for Workgroups 3.1a]
+protocol [LM1.2X002]
+protocol [LANMAN2.1]
+protocol [NT LM 0.12]
+
+Win2K:
+protocol [PC NETWORK PROGRAM 1.0]
+protocol [LANMAN1.0]
+protocol [Windows for Workgroups 3.1a]
+protocol [LM1.2X002]
+protocol [LANMAN2.1]
+protocol [NT LM 0.12]
+
+OS/2:
+protocol [PC NETWORK PROGRAM 1.0]
+protocol [XENIX CORE]
+protocol [LANMAN1.0]
+protocol [LM1.2X002]
+protocol [LANMAN2.1]
+*/
+
+/*
+ * Modified to recognize the architecture of the remote machine better.
+ *
+ * This appears to be the matrix of which protocol is used by which
+ * MS product.
+ Protocol WfWg Win95 WinNT Win2K OS/2
+ PC NETWORK PROGRAM 1.0 1 1 1 1 1
+ XENIX CORE 2 2
+ MICROSOFT NETWORKS 3.0 2 2
+ DOS LM1.2X002 3 3
+ MICROSOFT NETWORKS 1.03 3
+ DOS LANMAN2.1 4 4
+ LANMAN1.0 4 2 3
+ Windows for Workgroups 3.1a 5 5 5 3
+ LM1.2X002 6 4 4
+ LANMAN2.1 7 5 5
+ NT LM 0.12 6 8 6
+ *
+ * tim@fsg.com 09/29/95
+ * Win2K added by matty 17/7/99
+ */
+
+#define ARCH_WFWG 0x3 /* This is a fudge because WfWg is like Win95 */
+#define ARCH_WIN95 0x2
+#define ARCH_WINNT 0x4
+#define ARCH_WIN2K 0xC /* Win2K is like NT */
+#define ARCH_OS2 0x14 /* Again OS/2 is like NT */
+#define ARCH_SAMBA 0x20
+
+#define ARCH_ALL 0x3F
+
+/* List of supported protocols, most desired first */
+static const struct {
+ const char *proto_name;
+ const char *short_name;
+ int (*proto_reply_fn)(char *, char *);
+ int protocol_level;
+} supported_protocols[] = {
+ {"NT LANMAN 1.0", "NT1", reply_nt1, PROTOCOL_NT1},
+ {"NT LM 0.12", "NT1", reply_nt1, PROTOCOL_NT1},
+ {"LM1.2X002", "LANMAN2", reply_lanman2, PROTOCOL_LANMAN2},
+ {"Samba", "LANMAN2", reply_lanman2, PROTOCOL_LANMAN2},
+ {"DOS LM1.2X002", "LANMAN2", reply_lanman2, PROTOCOL_LANMAN2},
+ {"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.
+****************************************************************************/
+
+int reply_negprot(connection_struct *conn,
+ char *inbuf,char *outbuf, int dum_size,
+ int dum_buffsize)
+{
+ int outsize = set_message(outbuf,1,0,True);
+ int Index=0;
+ int choice= -1;
+ int protocol;
+ char *p;
+ int bcc = SVAL(smb_buf(inbuf),-2);
+ int arch = ARCH_ALL;
+
+ static BOOL done_negprot = False;
+
+ START_PROFILE(SMBnegprot);
+
+ if (done_negprot) {
+ END_PROFILE(SMBnegprot);
+ exit_server("multiple negprot's are not permitted");
+ }
+ done_negprot = True;
+
+ p = smb_buf(inbuf)+1;
+ while (p < (smb_buf(inbuf) + bcc)) {
+ Index++;
+ DEBUG(3,("Requested protocol [%s]\n",p));
+ if (strcsequal(p,"Windows for Workgroups 3.1a"))
+ arch &= ( ARCH_WFWG | ARCH_WIN95 | ARCH_WINNT | ARCH_WIN2K );
+ else if (strcsequal(p,"DOS LM1.2X002"))
+ arch &= ( ARCH_WFWG | ARCH_WIN95 );
+ else if (strcsequal(p,"DOS LANMAN2.1"))
+ arch &= ( ARCH_WFWG | ARCH_WIN95 );
+ else if (strcsequal(p,"NT LM 0.12"))
+ arch &= ( ARCH_WIN95 | ARCH_WINNT | ARCH_WIN2K );
+ else if (strcsequal(p,"LANMAN2.1"))
+ arch &= ( ARCH_WINNT | ARCH_WIN2K | ARCH_OS2 );
+ else if (strcsequal(p,"LM1.2X002"))
+ arch &= ( ARCH_WINNT | ARCH_WIN2K | ARCH_OS2 );
+ else if (strcsequal(p,"MICROSOFT NETWORKS 1.03"))
+ arch &= ARCH_WINNT;
+ else if (strcsequal(p,"XENIX CORE"))
+ arch &= ( ARCH_WINNT | ARCH_OS2 );
+ else if (strcsequal(p,"Samba")) {
+ arch = ARCH_SAMBA;
+ break;
+ }
+
+ p += strlen(p) + 2;
+ }
+
+ switch ( arch ) {
+ case ARCH_SAMBA:
+ set_remote_arch(RA_SAMBA);
+ break;
+ case ARCH_WFWG:
+ set_remote_arch(RA_WFWG);
+ break;
+ case ARCH_WIN95:
+ set_remote_arch(RA_WIN95);
+ break;
+ case ARCH_WINNT:
+ if(SVAL(inbuf,smb_flg2)==FLAGS2_WIN2K_SIGNATURE)
+ set_remote_arch(RA_WIN2K);
+ else
+ set_remote_arch(RA_WINNT);
+ break;
+ case ARCH_WIN2K:
+ set_remote_arch(RA_WIN2K);
+ break;
+ case ARCH_OS2:
+ set_remote_arch(RA_OS2);
+ break;
+ default:
+ set_remote_arch(RA_UNKNOWN);
+ break;
+ }
+
+ /* possibly reload - change of architecture */
+ reload_services(True);
+
+ /* Check for protocols, most desirable first */
+ for (protocol = 0; supported_protocols[protocol].proto_name; protocol++) {
+ p = smb_buf(inbuf)+1;
+ Index = 0;
+ if ((supported_protocols[protocol].protocol_level <= lp_maxprotocol()) &&
+ (supported_protocols[protocol].protocol_level >= lp_minprotocol()))
+ while (p < (smb_buf(inbuf) + bcc)) {
+ if (strequal(p,supported_protocols[protocol].proto_name))
+ choice = Index;
+ Index++;
+ p += strlen(p) + 2;
+ }
+ if(choice != -1)
+ break;
+ }
+
+ SSVAL(outbuf,smb_vwv0,choice);
+ if(choice != -1) {
+ extern fstring remote_proto;
+ fstrcpy(remote_proto,supported_protocols[protocol].short_name);
+ reload_services(True);
+ outsize = supported_protocols[protocol].proto_reply_fn(inbuf, outbuf);
+ DEBUG(3,("Selected protocol %s\n",supported_protocols[protocol].proto_name));
+ } else {
+ DEBUG(0,("No protocol supported !\n"));
+ }
+ SSVAL(outbuf,smb_vwv0,choice);
+
+ DEBUG( 5, ( "negprot index=%d\n", choice ) );
+
+ if ((lp_server_signing() == Required) && (Protocol < PROTOCOL_NT1)) {
+ exit_server("SMB signing is required and client negotiated a downlevel protocol");
+ }
+
+ END_PROFILE(SMBnegprot);
+ return(outsize);
+}
diff --git a/source/smbd/noquotas.c b/source/smbd/noquotas.c
new file mode 100644
index 00000000000..85caef57e1a
--- /dev/null
+++ b/source/smbd/noquotas.c
@@ -0,0 +1,38 @@
+/*
+ Unix SMB/CIFS implementation.
+ No support for quotas :-).
+ 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 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"
+
+/*
+ * Needed for auto generation of proto.h.
+ */
+
+BOOL disk_quotas(const char *path,SMB_BIG_UINT *bsize,SMB_BIG_UINT *dfree,SMB_BIG_UINT *dsize)
+{
+ (*bsize) = 512; /* This value should be ignored */
+
+ /* And just to be sure we set some values that hopefully */
+ /* will be larger that any possible real-world value */
+ (*dfree) = (SMB_BIG_UINT)-1;
+ (*dsize) = (SMB_BIG_UINT)-1;
+
+ /* As we have select not to use quotas, allways fail */
+ return False;
+}
diff --git a/source/smbd/notify.c b/source/smbd/notify.c
new file mode 100644
index 00000000000..9adf827c794
--- /dev/null
+++ b/source/smbd/notify.c
@@ -0,0 +1,225 @@
+/*
+ Unix SMB/CIFS implementation.
+ change notify handling
+ Copyright (C) Andrew Tridgell 2000
+ Copyright (C) Jeremy Allison 1994-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 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"
+
+static struct cnotify_fns *cnotify;
+
+/****************************************************************************
+ This is the structure to queue to implement NT change
+ notify. It consists of smb_size bytes stored from the
+ transact command (to keep the mid, tid etc around).
+ Plus the fid to examine and notify private data.
+*****************************************************************************/
+
+struct change_notify {
+ struct change_notify *next, *prev;
+ files_struct *fsp;
+ connection_struct *conn;
+ uint32 flags;
+ char request_buf[smb_size];
+ void *change_data;
+};
+
+static struct change_notify *change_notify_list;
+
+/****************************************************************************
+ Setup the common parts of the return packet and send it.
+*****************************************************************************/
+
+static void change_notify_reply_packet(char *inbuf, NTSTATUS error_code)
+{
+ char outbuf[smb_size+38];
+
+ memset(outbuf, '\0', sizeof(outbuf));
+ construct_reply_common(inbuf, outbuf);
+
+ ERROR_NT(error_code);
+
+ /*
+ * Seems NT needs a transact command with an error code
+ * in it. This is a longer packet than a simple error.
+ */
+ set_message(outbuf,18,0,False);
+
+ if (!send_smb(smbd_server_fd(),outbuf))
+ exit_server("change_notify_reply_packet: send_smb failed.");
+}
+
+/****************************************************************************
+ Remove an entry from the list and free it, also closing any
+ directory handle if necessary.
+*****************************************************************************/
+
+static void change_notify_remove(struct change_notify *cnbp)
+{
+ cnotify->remove_notify(cnbp->change_data);
+ DLIST_REMOVE(change_notify_list, cnbp);
+ ZERO_STRUCTP(cnbp);
+ SAFE_FREE(cnbp);
+}
+
+/****************************************************************************
+ Delete entries by fnum from the change notify pending queue.
+*****************************************************************************/
+
+void remove_pending_change_notify_requests_by_fid(files_struct *fsp)
+{
+ struct change_notify *cnbp, *next;
+
+ for (cnbp=change_notify_list; cnbp; cnbp=next) {
+ next=cnbp->next;
+ if (cnbp->fsp->fnum == fsp->fnum) {
+ change_notify_remove(cnbp);
+ }
+ }
+}
+
+/****************************************************************************
+ Delete entries by mid from the change notify pending queue. Always send reply.
+*****************************************************************************/
+
+void remove_pending_change_notify_requests_by_mid(int mid)
+{
+ struct change_notify *cnbp, *next;
+
+ for (cnbp=change_notify_list; cnbp; cnbp=next) {
+ next=cnbp->next;
+ if(SVAL(cnbp->request_buf,smb_mid) == mid) {
+ change_notify_reply_packet(cnbp->request_buf,NT_STATUS_CANCELLED);
+ change_notify_remove(cnbp);
+ }
+ }
+}
+
+/****************************************************************************
+ Delete entries by filename and cnum from the change notify pending queue.
+ Always send reply.
+*****************************************************************************/
+
+void remove_pending_change_notify_requests_by_filename(files_struct *fsp)
+{
+ struct change_notify *cnbp, *next;
+
+ for (cnbp=change_notify_list; cnbp; cnbp=next) {
+ next=cnbp->next;
+ /*
+ * We know it refers to the same directory if the connection number and
+ * the filename are identical.
+ */
+ if((cnbp->fsp->conn == fsp->conn) && strequal(cnbp->fsp->fsp_name,fsp->fsp_name)) {
+ change_notify_reply_packet(cnbp->request_buf,NT_STATUS_CANCELLED);
+ change_notify_remove(cnbp);
+ }
+ }
+}
+
+/****************************************************************************
+ Return true if there are pending change notifies.
+****************************************************************************/
+
+int change_notify_timeout(void)
+{
+ return cnotify->select_time;
+}
+
+/****************************************************************************
+ Process the change notify queue. Note that this is only called as root.
+ Returns True if there are still outstanding change notify requests on the
+ queue.
+*****************************************************************************/
+
+BOOL process_pending_change_notify_queue(time_t t)
+{
+ struct change_notify *cnbp, *next;
+ uint16 vuid;
+
+ for (cnbp=change_notify_list; cnbp; cnbp=next) {
+ next=cnbp->next;
+
+ vuid = (lp_security() == SEC_SHARE) ? UID_FIELD_INVALID : SVAL(cnbp->request_buf,smb_uid);
+
+ if (cnotify->check_notify(cnbp->conn, vuid, cnbp->fsp->fsp_name, cnbp->flags, cnbp->change_data, t)) {
+ DEBUG(10,("process_pending_change_notify_queue: dir %s changed !\n", cnbp->fsp->fsp_name ));
+ change_notify_reply_packet(cnbp->request_buf,STATUS_NOTIFY_ENUM_DIR);
+ change_notify_remove(cnbp);
+ }
+ }
+
+ return (change_notify_list != NULL);
+}
+
+/****************************************************************************
+ Now queue an entry on the notify change list.
+ We only need to save smb_size bytes from this incoming packet
+ as we will always by returning a 'read the directory yourself'
+ error.
+****************************************************************************/
+
+BOOL change_notify_set(char *inbuf, files_struct *fsp, connection_struct *conn, uint32 flags)
+{
+ struct change_notify *cnbp;
+
+ if((cnbp = (struct change_notify *)malloc(sizeof(*cnbp))) == NULL) {
+ DEBUG(0,("change_notify_set: malloc fail !\n" ));
+ return -1;
+ }
+
+ ZERO_STRUCTP(cnbp);
+
+ memcpy(cnbp->request_buf, inbuf, smb_size);
+ cnbp->fsp = fsp;
+ cnbp->conn = conn;
+ cnbp->flags = flags;
+ cnbp->change_data = cnotify->register_notify(conn, fsp->fsp_name, flags);
+
+ if (!cnbp->change_data) {
+ SAFE_FREE(cnbp);
+ return False;
+ }
+
+ DLIST_ADD(change_notify_list, cnbp);
+
+ /* Push the MID of this packet on the signing queue. */
+ srv_defer_sign_response(SVAL(inbuf,smb_mid));
+
+ return True;
+}
+
+/****************************************************************************
+ Initialise the change notify subsystem.
+****************************************************************************/
+
+BOOL init_change_notify(void)
+{
+#if HAVE_KERNEL_CHANGE_NOTIFY
+ if (lp_kernel_change_notify())
+ cnotify = kernel_notify_init();
+#endif
+ if (!cnotify) cnotify = hash_notify_init();
+
+ if (!cnotify) {
+ DEBUG(0,("Failed to init change notify system\n"));
+ return False;
+ }
+
+ return True;
+}
diff --git a/source/smbd/notify_hash.c b/source/smbd/notify_hash.c
new file mode 100644
index 00000000000..ec414454f9e
--- /dev/null
+++ b/source/smbd/notify_hash.c
@@ -0,0 +1,225 @@
+/*
+ Unix SMB/CIFS implementation.
+ change notify handling - hash based implementation
+ Copyright (C) Jeremy Allison 1994-1998
+ 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 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"
+
+struct change_data {
+ time_t last_check_time; /* time we last checked this entry */
+ time_t modify_time; /* Info from the directory we're monitoring. */
+ time_t status_time; /* Info from the directory we're monitoring. */
+ time_t total_time; /* Total time of all directory entries - don't care if it wraps. */
+ unsigned int num_entries; /* Zero or the number of files in the directory. */
+ unsigned int mode_sum;
+ unsigned char name_hash[16];
+};
+
+/****************************************************************************
+ Create the hash we will use to determine if the contents changed.
+*****************************************************************************/
+
+static BOOL notify_hash(connection_struct *conn, char *path, uint32 flags,
+ struct change_data *data, struct change_data *old_data)
+{
+ SMB_STRUCT_STAT st;
+ pstring full_name;
+ char *p;
+ const char *fname;
+ size_t remaining_len;
+ size_t fullname_len;
+ void *dp;
+
+ ZERO_STRUCTP(data);
+
+ if(SMB_VFS_STAT(conn,path, &st) == -1)
+ return False;
+
+ data->modify_time = st.st_mtime;
+ data->status_time = st.st_ctime;
+
+ if (old_data) {
+ /*
+ * Shortcut to avoid directory scan if the time
+ * has changed - we always must return true then.
+ */
+ if (old_data->modify_time != data->modify_time ||
+ old_data->status_time != data->status_time ) {
+ return True;
+ }
+ }
+
+ /*
+ * If we are to watch for changes that are only stored
+ * in inodes of files, not in the directory inode, we must
+ * scan the directory and produce a unique identifier with
+ * which we can determine if anything changed. We use the
+ * modify and change times from all the files in the
+ * directory, added together (ignoring wrapping if it's
+ * larger than the max time_t value).
+ */
+
+ dp = OpenDir(conn, path, True);
+ if (dp == NULL)
+ return False;
+
+ data->num_entries = 0;
+
+ pstrcpy(full_name, path);
+ pstrcat(full_name, "/");
+
+ fullname_len = strlen(full_name);
+ remaining_len = sizeof(full_name) - fullname_len - 1;
+ p = &full_name[fullname_len];
+
+ while ((fname = ReadDirName(dp))) {
+ if(strequal(fname, ".") || strequal(fname, ".."))
+ continue;
+
+ data->num_entries++;
+ safe_strcpy(p, fname, remaining_len);
+
+ ZERO_STRUCT(st);
+
+ /*
+ * Do the stat - but ignore errors.
+ */
+ SMB_VFS_STAT(conn,full_name, &st);
+
+ /*
+ * Always sum the times.
+ */
+
+ data->total_time += (st.st_mtime + st.st_ctime);
+
+ /*
+ * If requested hash the names.
+ */
+
+ if (flags & (FILE_NOTIFY_CHANGE_DIR_NAME|FILE_NOTIFY_CHANGE_FILE_NAME|FILE_NOTIFY_CHANGE_FILE)) {
+ int i;
+ unsigned char tmp_hash[16];
+ mdfour(tmp_hash, (const unsigned char *)fname, strlen(fname));
+ for (i=0;i<16;i++)
+ data->name_hash[i] ^= tmp_hash[i];
+ }
+
+ /*
+ * If requested sum the mode_t's.
+ */
+
+ if (flags & (FILE_NOTIFY_CHANGE_ATTRIBUTES|FILE_NOTIFY_CHANGE_SECURITY))
+ data->mode_sum += st.st_mode;
+ }
+
+ CloseDir(dp);
+
+ return True;
+}
+
+/****************************************************************************
+ Register a change notify request.
+*****************************************************************************/
+
+static void *hash_register_notify(connection_struct *conn, char *path, uint32 flags)
+{
+ struct change_data data;
+
+ if (!notify_hash(conn, path, flags, &data, NULL))
+ return NULL;
+
+ data.last_check_time = time(NULL);
+
+ return (void *)memdup(&data, sizeof(data));
+}
+
+/****************************************************************************
+ Check if a change notify should be issued.
+ A time of zero means instantaneous check - don't modify the last check time.
+*****************************************************************************/
+
+static BOOL hash_check_notify(connection_struct *conn, uint16 vuid, char *path, uint32 flags, void *datap, time_t t)
+{
+ struct change_data *data = (struct change_data *)datap;
+ struct change_data data2;
+
+ if (t && t < data->last_check_time + lp_change_notify_timeout())
+ return False;
+
+ if (!change_to_user(conn,vuid))
+ return True;
+ if (!set_current_service(conn,True)) {
+ change_to_root_user();
+ return True;
+ }
+
+ if (!notify_hash(conn, path, flags, &data2, data) ||
+ data2.modify_time != data->modify_time ||
+ data2.status_time != data->status_time ||
+ data2.total_time != data->total_time ||
+ data2.num_entries != data->num_entries ||
+ data2.mode_sum != data->mode_sum ||
+ memcmp(data2.name_hash, data->name_hash, sizeof(data2.name_hash))) {
+ change_to_root_user();
+ return True;
+ }
+
+ if (t)
+ data->last_check_time = t;
+
+ change_to_root_user();
+
+ return False;
+}
+
+/****************************************************************************
+ Remove a change notify data structure.
+*****************************************************************************/
+
+static void hash_remove_notify(void *datap)
+{
+ free(datap);
+}
+
+/****************************************************************************
+ Setup hash based change notify.
+****************************************************************************/
+
+struct cnotify_fns *hash_notify_init(void)
+{
+ static struct cnotify_fns cnotify;
+
+ cnotify.register_notify = hash_register_notify;
+ cnotify.check_notify = hash_check_notify;
+ cnotify.remove_notify = hash_remove_notify;
+ cnotify.select_time = lp_change_notify_timeout();
+
+ return &cnotify;
+}
+
+/*
+ change_notify_reply_packet(cnbp->request_buf,ERRSRV,ERRaccess);
+ change_notify_reply_packet(cnbp->request_buf,0,NT_STATUS_NOTIFY_ENUM_DIR);
+
+ chain_size = 0;
+ file_chain_reset();
+
+ uint16 vuid = (lp_security() == SEC_SHARE) ? UID_FIELD_INVALID :
+ SVAL(cnbp->request_buf,smb_uid);
+*/
diff --git a/source/smbd/notify_kernel.c b/source/smbd/notify_kernel.c
new file mode 100644
index 00000000000..8fcc18a09f9
--- /dev/null
+++ b/source/smbd/notify_kernel.c
@@ -0,0 +1,245 @@
+/*
+ Unix SMB/Netbios implementation.
+ Version 3.0
+ change notify handling - linux kernel based implementation
+ 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 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"
+
+#if HAVE_KERNEL_CHANGE_NOTIFY
+
+#define FD_PENDING_SIZE 20
+static SIG_ATOMIC_T fd_pending_array[FD_PENDING_SIZE];
+static SIG_ATOMIC_T signals_received;
+
+#ifndef DN_ACCESS
+#define DN_ACCESS 0x00000001 /* File accessed in directory */
+#define DN_MODIFY 0x00000002 /* File modified in directory */
+#define DN_CREATE 0x00000004 /* File created in directory */
+#define DN_DELETE 0x00000008 /* File removed from directory */
+#define DN_RENAME 0x00000010 /* File renamed in directory */
+#define DN_ATTRIB 0x00000020 /* File changed attribute */
+#define DN_MULTISHOT 0x80000000 /* Don't remove notifier */
+#endif
+
+
+#ifndef RT_SIGNAL_NOTIFY
+#define RT_SIGNAL_NOTIFY (SIGRTMIN+2)
+#endif
+
+#ifndef F_SETSIG
+#define F_SETSIG 10
+#endif
+
+#ifndef F_NOTIFY
+#define F_NOTIFY 1026
+#endif
+
+/****************************************************************************
+ This is the structure to keep the information needed to
+ determine if a directory has changed.
+*****************************************************************************/
+
+struct change_data {
+ int directory_handle;
+};
+
+/****************************************************************************
+ The signal handler for change notify.
+ The Linux kernel has a bug in that we should be able to block any
+ further delivery of RT signals until the kernel_check_notify() function
+ unblocks them, but it seems that any signal mask we're setting here is
+ being overwritten on exit from this handler. I should create a standalone
+ test case for the kernel hackers. JRA.
+*****************************************************************************/
+
+static void signal_handler(int sig, siginfo_t *info, void *unused)
+{
+ if (signals_received < FD_PENDING_SIZE - 1) {
+ fd_pending_array[signals_received] = (SIG_ATOMIC_T)info->si_fd;
+ signals_received++;
+ } /* Else signal is lost. */
+ sys_select_signal();
+}
+
+/****************************************************************************
+ Check if a change notify should be issued.
+ time non-zero means timeout check (used for hash). Ignore this (async method
+ where time is zero will be used instead).
+*****************************************************************************/
+
+static BOOL kernel_check_notify(connection_struct *conn, uint16 vuid, char *path, uint32 flags, void *datap, time_t t)
+{
+ struct change_data *data = (struct change_data *)datap;
+ int i;
+ BOOL ret = False;
+
+ if (t)
+ return False;
+
+ BlockSignals(True, RT_SIGNAL_NOTIFY);
+ for (i = 0; i < signals_received; i++) {
+ if (data->directory_handle == (int)fd_pending_array[i]) {
+ DEBUG(3,("kernel_check_notify: kernel change notify on %s fd[%d]=%d (signals_received=%d)\n",
+ path, i, (int)fd_pending_array[i], (int)signals_received ));
+
+ close((int)fd_pending_array[i]);
+ fd_pending_array[i] = (SIG_ATOMIC_T)-1;
+ if (signals_received - i - 1) {
+ memmove((void *)&fd_pending_array[i], (void *)&fd_pending_array[i+1],
+ sizeof(SIG_ATOMIC_T)*(signals_received-i-1));
+ }
+ data->directory_handle = -1;
+ signals_received--;
+ ret = True;
+ break;
+ }
+ }
+ BlockSignals(False, RT_SIGNAL_NOTIFY);
+ return ret;
+}
+
+/****************************************************************************
+ Remove a change notify data structure.
+*****************************************************************************/
+
+static void kernel_remove_notify(void *datap)
+{
+ struct change_data *data = (struct change_data *)datap;
+ int fd = data->directory_handle;
+ if (fd != -1) {
+ int i;
+ BlockSignals(True, RT_SIGNAL_NOTIFY);
+ for (i = 0; i < signals_received; i++) {
+ if (fd == (int)fd_pending_array[i]) {
+ fd_pending_array[i] = (SIG_ATOMIC_T)-1;
+ if (signals_received - i - 1) {
+ memmove((void *)&fd_pending_array[i], (void *)&fd_pending_array[i+1],
+ sizeof(SIG_ATOMIC_T)*(signals_received-i-1));
+ }
+ data->directory_handle = -1;
+ signals_received--;
+ break;
+ }
+ }
+ close(fd);
+ BlockSignals(False, RT_SIGNAL_NOTIFY);
+ }
+ SAFE_FREE(data);
+ DEBUG(3,("kernel_remove_notify: fd=%d\n", fd));
+}
+
+/****************************************************************************
+ Register a change notify request.
+*****************************************************************************/
+
+static void *kernel_register_notify(connection_struct *conn, char *path, uint32 flags)
+{
+ struct change_data data;
+ int fd;
+ unsigned long kernel_flags;
+
+ fd = sys_open(path,O_RDONLY, 0);
+
+ if (fd == -1) {
+ DEBUG(3,("Failed to open directory %s for change notify\n", path));
+ return NULL;
+ }
+
+ if (sys_fcntl_long(fd, F_SETSIG, RT_SIGNAL_NOTIFY) == -1) {
+ DEBUG(3,("Failed to set signal handler for change notify\n"));
+ return NULL;
+ }
+
+ kernel_flags = DN_CREATE|DN_DELETE|DN_RENAME; /* creation/deletion changes everything! */
+ if (flags & FILE_NOTIFY_CHANGE_FILE) kernel_flags |= DN_MODIFY;
+ if (flags & FILE_NOTIFY_CHANGE_DIR_NAME) kernel_flags |= DN_RENAME|DN_DELETE;
+ if (flags & FILE_NOTIFY_CHANGE_ATTRIBUTES) kernel_flags |= DN_ATTRIB;
+ if (flags & FILE_NOTIFY_CHANGE_SIZE) kernel_flags |= DN_MODIFY;
+ if (flags & FILE_NOTIFY_CHANGE_LAST_WRITE) kernel_flags |= DN_MODIFY;
+ if (flags & FILE_NOTIFY_CHANGE_LAST_ACCESS) kernel_flags |= DN_ACCESS;
+ if (flags & FILE_NOTIFY_CHANGE_CREATION) kernel_flags |= DN_CREATE;
+ if (flags & FILE_NOTIFY_CHANGE_SECURITY) kernel_flags |= DN_ATTRIB;
+ if (flags & FILE_NOTIFY_CHANGE_EA) kernel_flags |= DN_ATTRIB;
+ if (flags & FILE_NOTIFY_CHANGE_FILE_NAME) kernel_flags |= DN_RENAME|DN_DELETE;
+
+ if (sys_fcntl_long(fd, F_NOTIFY, kernel_flags) == -1) {
+ DEBUG(3,("Failed to set async flag for change notify\n"));
+ return NULL;
+ }
+
+ data.directory_handle = fd;
+
+ DEBUG(3,("kernel change notify on %s (ntflags=0x%x flags=0x%x) fd=%d\n",
+ path, (int)flags, (int)kernel_flags, fd));
+
+ return (void *)memdup(&data, sizeof(data));
+}
+
+/****************************************************************************
+ See if the kernel supports change notify.
+****************************************************************************/
+
+static BOOL kernel_notify_available(void)
+{
+ int fd, ret;
+ fd = open("/tmp", O_RDONLY);
+ if (fd == -1)
+ return False; /* uggh! */
+ ret = sys_fcntl_long(fd, F_NOTIFY, 0);
+ close(fd);
+ return ret == 0;
+}
+
+/****************************************************************************
+ Setup kernel based change notify.
+****************************************************************************/
+
+struct cnotify_fns *kernel_notify_init(void)
+{
+ static struct cnotify_fns cnotify;
+ struct sigaction act;
+
+ ZERO_STRUCT(act);
+
+ act.sa_handler = NULL;
+ act.sa_sigaction = signal_handler;
+ act.sa_flags = SA_SIGINFO;
+ sigemptyset( &act.sa_mask );
+ if (sigaction(RT_SIGNAL_NOTIFY, &act, NULL) != 0) {
+ DEBUG(0,("Failed to setup RT_SIGNAL_NOTIFY handler\n"));
+ return NULL;
+ }
+
+ if (!kernel_notify_available())
+ return NULL;
+
+ cnotify.register_notify = kernel_register_notify;
+ cnotify.check_notify = kernel_check_notify;
+ cnotify.remove_notify = kernel_remove_notify;
+ cnotify.select_time = -1;
+
+ /* the signal can start off blocked due to a bug in bash */
+ BlockSignals(False, RT_SIGNAL_NOTIFY);
+
+ return &cnotify;
+}
+
+#else
+ void notify_kernel_dummy(void) {}
+#endif /* HAVE_KERNEL_CHANGE_NOTIFY */
diff --git a/source/smbd/ntquotas.c b/source/smbd/ntquotas.c
new file mode 100644
index 00000000000..555f32d773f
--- /dev/null
+++ b/source/smbd/ntquotas.c
@@ -0,0 +1,262 @@
+/*
+ Unix SMB/CIFS implementation.
+ NT QUOTA suppport
+ 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 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"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_QUOTA
+
+static SMB_BIG_UINT limit_nt2unix(SMB_BIG_UINT in, SMB_BIG_UINT bsize)
+{
+ SMB_BIG_UINT ret = (SMB_BIG_UINT)0;
+
+ ret = (SMB_BIG_UINT)(in/bsize);
+ if (in>0 && ret==0) {
+ /* we have to make sure that a overflow didn't set NO_LIMIT */
+ ret = (SMB_BIG_UINT)1;
+ }
+
+ if (in == SMB_NTQUOTAS_NO_LIMIT)
+ ret = SMB_QUOTAS_NO_LIMIT;
+ else if (in == SMB_NTQUOTAS_NO_SPACE)
+ ret = SMB_QUOTAS_NO_SPACE;
+ else if (in == SMB_NTQUOTAS_NO_ENTRY)
+ ret = SMB_QUOTAS_NO_LIMIT;
+
+ return ret;
+}
+
+static SMB_BIG_UINT limit_unix2nt(SMB_BIG_UINT in, SMB_BIG_UINT bsize)
+{
+ SMB_BIG_UINT ret = (SMB_BIG_UINT)0;
+
+ ret = (SMB_BIG_UINT)(in*bsize);
+
+ if (ret < in) {
+ /* we overflow */
+ ret = SMB_NTQUOTAS_NO_LIMIT;
+ }
+
+ if (in == SMB_QUOTAS_NO_LIMIT)
+ ret = SMB_NTQUOTAS_NO_LIMIT;
+
+ return ret;
+}
+
+static SMB_BIG_UINT limit_blk2inodes(SMB_BIG_UINT in)
+{
+ SMB_BIG_UINT ret = (SMB_BIG_UINT)0;
+
+ ret = (SMB_BIG_UINT)(in/2);
+
+ if (ret == 0 && in != 0)
+ ret = (SMB_BIG_UINT)1;
+
+ return ret;
+}
+
+int vfs_get_ntquota(files_struct *fsp, enum SMB_QUOTA_TYPE qtype, DOM_SID *psid, SMB_NTQUOTA_STRUCT *qt)
+{
+ int ret;
+ SMB_DISK_QUOTA D;
+ unid_t id;
+
+ ZERO_STRUCT(D);
+
+ if (!fsp||!fsp->conn||!qt)
+ return (-1);
+
+ ZERO_STRUCT(*qt);
+
+ id.uid = -1;
+
+ if (psid && !NT_STATUS_IS_OK(sid_to_uid(psid, &id.uid))) {
+ DEBUG(0,("sid_to_uid: failed, SID[%s]\n",
+ sid_string_static(psid)));
+ }
+
+ ret = SMB_VFS_GET_QUOTA(fsp->conn, qtype, id, &D);
+
+ if (psid)
+ qt->sid = *psid;
+
+ if (ret!=0) {
+ return ret;
+ }
+
+ qt->usedspace = (SMB_BIG_UINT)D.curblocks*D.bsize;
+ qt->softlim = limit_unix2nt(D.softlimit, D.bsize);
+ qt->hardlim = limit_unix2nt(D.hardlimit, D.bsize);
+ qt->qflags = D.qflags;
+
+
+ return 0;
+}
+
+int vfs_set_ntquota(files_struct *fsp, enum SMB_QUOTA_TYPE qtype, DOM_SID *psid, SMB_NTQUOTA_STRUCT *qt)
+{
+ int ret;
+ SMB_DISK_QUOTA D;
+ unid_t id;
+ ZERO_STRUCT(D);
+
+ if (!fsp||!fsp->conn||!qt)
+ return (-1);
+
+ id.uid = -1;
+
+ D.bsize = (SMB_BIG_UINT)QUOTABLOCK_SIZE;
+
+ D.softlimit = limit_nt2unix(qt->softlim,D.bsize);
+ D.hardlimit = limit_nt2unix(qt->hardlim,D.bsize);
+ D.qflags = qt->qflags;
+
+ D.isoftlimit = limit_blk2inodes(D.softlimit);
+ D.ihardlimit = limit_blk2inodes(D.hardlimit);
+
+ if (psid && !NT_STATUS_IS_OK(sid_to_uid(psid, &id.uid))) {
+ DEBUG(0,("sid_to_uid: failed, SID[%s]\n",
+ sid_string_static(psid)));
+ }
+
+ ret = SMB_VFS_SET_QUOTA(fsp->conn, qtype, id, &D);
+
+ return ret;
+}
+
+static BOOL allready_in_quota_list(SMB_NTQUOTA_LIST *qt_list, uid_t uid)
+{
+ SMB_NTQUOTA_LIST *tmp_list = NULL;
+
+ if (!qt_list)
+ return False;
+
+ for (tmp_list=qt_list;tmp_list!=NULL;tmp_list=tmp_list->next) {
+ if (tmp_list->uid == uid) {
+ return True;
+ }
+ }
+
+ return False;
+}
+
+int vfs_get_user_ntquota_list(files_struct *fsp, SMB_NTQUOTA_LIST **qt_list)
+{
+ struct passwd *usr;
+ TALLOC_CTX *mem_ctx = NULL;
+
+ if (!fsp||!fsp->conn||!qt_list)
+ return (-1);
+
+ *qt_list = NULL;
+
+ if ((mem_ctx=talloc_init("SMB_USER_QUOTA_LIST"))==NULL) {
+ DEBUG(0,("talloc_init() failed\n"));
+ return (-1);
+ }
+
+ sys_setpwent();
+ while ((usr = sys_getpwent()) != NULL) {
+ SMB_NTQUOTA_STRUCT tmp_qt;
+ SMB_NTQUOTA_LIST *tmp_list_ent;
+ DOM_SID sid;
+
+ ZERO_STRUCT(tmp_qt);
+
+ if (allready_in_quota_list((*qt_list),usr->pw_uid)) {
+ DEBUG(5,("record for uid[%ld] allready in the list\n",(long)usr->pw_uid));
+ continue;
+ }
+
+ if (!NT_STATUS_IS_OK(uid_to_sid(&sid, usr->pw_uid))) {
+ DEBUG(0,("uid_to_sid failed for %ld\n",(long)usr->pw_uid));
+ continue;
+ }
+
+ if (vfs_get_ntquota(fsp, SMB_USER_QUOTA_TYPE, &sid, &tmp_qt)!=0) {
+ DEBUG(5,("no quota entry for sid[%s] path[%s]\n",
+ sid_string_static(&sid),fsp->conn->connectpath));
+ continue;
+ }
+
+ DEBUG(15,("quota entry for id[%s] path[%s]\n",
+ sid_string_static(&sid),fsp->conn->connectpath));
+
+ if ((tmp_list_ent=(SMB_NTQUOTA_LIST *)talloc_zero(mem_ctx,sizeof(SMB_NTQUOTA_LIST)))==NULL) {
+ DEBUG(0,("talloc_zero() failed\n"));
+ *qt_list = NULL;
+ talloc_destroy(mem_ctx);
+ return (-1);
+ }
+
+ if ((tmp_list_ent->quotas=(SMB_NTQUOTA_STRUCT *)talloc_zero(mem_ctx,sizeof(SMB_NTQUOTA_STRUCT)))==NULL) {
+ DEBUG(0,("talloc_zero() failed\n"));
+ *qt_list = NULL;
+ talloc_destroy(mem_ctx);
+ return (-1);
+ }
+
+ tmp_list_ent->uid = usr->pw_uid;
+ memcpy(tmp_list_ent->quotas,&tmp_qt,sizeof(tmp_qt));
+ tmp_list_ent->mem_ctx = mem_ctx;
+
+ DLIST_ADD((*qt_list),tmp_list_ent);
+
+ }
+ sys_endpwent();
+
+ return 0;
+}
+
+void *init_quota_handle(TALLOC_CTX *mem_ctx)
+{
+ SMB_NTQUOTA_HANDLE *qt_handle;
+
+ if (!mem_ctx)
+ return False;
+
+ qt_handle = (SMB_NTQUOTA_HANDLE *)talloc_zero(mem_ctx,sizeof(SMB_NTQUOTA_HANDLE));
+ if (qt_handle==NULL) {
+ DEBUG(0,("talloc_zero() failed\n"));
+ return NULL;
+ }
+
+ return (void *)qt_handle;
+}
+
+void destroy_quota_handle(void **pqt_handle)
+{
+ SMB_NTQUOTA_HANDLE *qt_handle = NULL;
+ if (!pqt_handle||!(*pqt_handle))
+ return;
+
+ qt_handle = (*pqt_handle);
+
+
+ if (qt_handle->quota_list)
+ free_ntquota_list(&qt_handle->quota_list);
+
+ qt_handle->quota_list = NULL;
+ qt_handle->tmp_list = NULL;
+ qt_handle = NULL;
+
+ return;
+}
+
diff --git a/source/smbd/nttrans.c b/source/smbd/nttrans.c
new file mode 100644
index 00000000000..018f6bbbece
--- /dev/null
+++ b/source/smbd/nttrans.c
@@ -0,0 +1,2780 @@
+/*
+ Unix SMB/CIFS implementation.
+ SMB NT transaction handling
+ Copyright (C) Jeremy Allison 1994-1998
+ 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 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"
+
+extern int Protocol;
+extern int smb_read_error;
+extern int global_oplock_break;
+extern BOOL case_sensitive;
+extern BOOL case_preserve;
+extern BOOL short_case_preserve;
+extern struct current_user current_user;
+
+static const char *known_nt_pipes[] = {
+ "\\LANMAN",
+ "\\srvsvc",
+ "\\samr",
+ "\\wkssvc",
+ "\\NETLOGON",
+ "\\ntlsa",
+ "\\ntsvcs",
+ "\\lsass",
+ "\\lsarpc",
+ "\\winreg",
+ "\\spoolss",
+ "\\netdfs",
+ "\\rpcecho",
+ "\\epmapper",
+ NULL
+};
+
+/* Map generic permissions to file object specific permissions */
+
+struct generic_mapping file_generic_mapping = {
+ FILE_GENERIC_READ,
+ FILE_GENERIC_WRITE,
+ FILE_GENERIC_EXECUTE,
+ FILE_GENERIC_ALL
+};
+
+static char *nttrans_realloc(char **ptr, size_t size)
+{
+ char *tptr = NULL;
+ if (ptr==NULL)
+ smb_panic("nttrans_realloc() called with NULL ptr\n");
+
+ tptr = Realloc_zero(*ptr, size);
+ if(tptr == NULL) {
+ *ptr = NULL;
+ return NULL;
+ }
+
+ *ptr = tptr;
+
+ return tptr;
+}
+
+
+/****************************************************************************
+ Send the required number of replies back.
+ We assume all fields other than the data fields are
+ set correctly for the type of call.
+ HACK ! Always assumes smb_setup field is zero.
+****************************************************************************/
+
+static int send_nt_replies(char *inbuf, char *outbuf, int bufsize, NTSTATUS nt_error, char *params,
+ int paramsize, char *pdata, int datasize)
+{
+ extern int max_send;
+ int data_to_send = datasize;
+ int params_to_send = paramsize;
+ int useable_space;
+ char *pp = params;
+ char *pd = pdata;
+ int params_sent_thistime, data_sent_thistime, total_sent_thistime;
+ int alignment_offset = 3;
+ int data_alignment_offset = 0;
+
+ /*
+ * Initially set the wcnt area to be 18 - this is true for all
+ * transNT replies.
+ */
+
+ set_message(outbuf,18,0,True);
+
+ if (NT_STATUS_V(nt_error))
+ ERROR_NT(nt_error);
+
+ /*
+ * If there genuinely are no parameters or data to send just send
+ * the empty packet.
+ */
+
+ if(params_to_send == 0 && data_to_send == 0) {
+ if (!send_smb(smbd_server_fd(),outbuf))
+ exit_server("send_nt_replies: send_smb failed.");
+ return 0;
+ }
+
+ /*
+ * When sending params and data ensure that both are nicely aligned.
+ * Only do this alignment when there is also data to send - else
+ * can cause NT redirector problems.
+ */
+
+ if (((params_to_send % 4) != 0) && (data_to_send != 0))
+ data_alignment_offset = 4 - (params_to_send % 4);
+
+ /*
+ * Space is bufsize minus Netbios over TCP header minus SMB header.
+ * The alignment_offset is to align the param bytes on a four byte
+ * boundary (2 bytes for data len, one byte pad).
+ * NT needs this to work correctly.
+ */
+
+ useable_space = bufsize - ((smb_buf(outbuf)+
+ alignment_offset+data_alignment_offset) -
+ outbuf);
+
+ /*
+ * useable_space can never be more than max_send minus the
+ * alignment offset.
+ */
+
+ useable_space = MIN(useable_space,
+ max_send - (alignment_offset+data_alignment_offset));
+
+
+ while (params_to_send || data_to_send) {
+
+ /*
+ * Calculate whether we will totally or partially fill this packet.
+ */
+
+ total_sent_thistime = params_to_send + data_to_send +
+ alignment_offset + data_alignment_offset;
+
+ /*
+ * We can never send more than useable_space.
+ */
+
+ total_sent_thistime = MIN(total_sent_thistime, useable_space);
+
+ set_message(outbuf, 18, total_sent_thistime, True);
+
+ /*
+ * Set total params and data to be sent.
+ */
+
+ SIVAL(outbuf,smb_ntr_TotalParameterCount,paramsize);
+ SIVAL(outbuf,smb_ntr_TotalDataCount,datasize);
+
+ /*
+ * Calculate how many parameters and data we can fit into
+ * this packet. Parameters get precedence.
+ */
+
+ params_sent_thistime = MIN(params_to_send,useable_space);
+ data_sent_thistime = useable_space - params_sent_thistime;
+ data_sent_thistime = MIN(data_sent_thistime,data_to_send);
+
+ SIVAL(outbuf,smb_ntr_ParameterCount,params_sent_thistime);
+
+ if(params_sent_thistime == 0) {
+ SIVAL(outbuf,smb_ntr_ParameterOffset,0);
+ SIVAL(outbuf,smb_ntr_ParameterDisplacement,0);
+ } else {
+ /*
+ * smb_ntr_ParameterOffset is the offset from the start of the SMB header to the
+ * parameter bytes, however the first 4 bytes of outbuf are
+ * the Netbios over TCP header. Thus use smb_base() to subtract
+ * them from the calculation.
+ */
+
+ SIVAL(outbuf,smb_ntr_ParameterOffset,
+ ((smb_buf(outbuf)+alignment_offset) - smb_base(outbuf)));
+ /*
+ * Absolute displacement of param bytes sent in this packet.
+ */
+
+ SIVAL(outbuf,smb_ntr_ParameterDisplacement,pp - params);
+ }
+
+ /*
+ * Deal with the data portion.
+ */
+
+ SIVAL(outbuf,smb_ntr_DataCount, data_sent_thistime);
+
+ if(data_sent_thistime == 0) {
+ SIVAL(outbuf,smb_ntr_DataOffset,0);
+ SIVAL(outbuf,smb_ntr_DataDisplacement, 0);
+ } else {
+ /*
+ * The offset of the data bytes is the offset of the
+ * parameter bytes plus the number of parameters being sent this time.
+ */
+
+ SIVAL(outbuf,smb_ntr_DataOffset,((smb_buf(outbuf)+alignment_offset) -
+ smb_base(outbuf)) + params_sent_thistime + data_alignment_offset);
+ SIVAL(outbuf,smb_ntr_DataDisplacement, pd - pdata);
+ }
+
+ /*
+ * Copy the param bytes into the packet.
+ */
+
+ if(params_sent_thistime)
+ memcpy((smb_buf(outbuf)+alignment_offset),pp,params_sent_thistime);
+
+ /*
+ * Copy in the data bytes
+ */
+
+ if(data_sent_thistime)
+ memcpy(smb_buf(outbuf)+alignment_offset+params_sent_thistime+
+ data_alignment_offset,pd,data_sent_thistime);
+
+ DEBUG(9,("nt_rep: params_sent_thistime = %d, data_sent_thistime = %d, useable_space = %d\n",
+ params_sent_thistime, data_sent_thistime, useable_space));
+ DEBUG(9,("nt_rep: params_to_send = %d, data_to_send = %d, paramsize = %d, datasize = %d\n",
+ params_to_send, data_to_send, paramsize, datasize));
+
+ /* Send the packet */
+ if (!send_smb(smbd_server_fd(),outbuf))
+ exit_server("send_nt_replies: send_smb failed.");
+
+ pp += params_sent_thistime;
+ pd += data_sent_thistime;
+
+ params_to_send -= params_sent_thistime;
+ data_to_send -= data_sent_thistime;
+
+ /*
+ * Sanity check
+ */
+
+ if(params_to_send < 0 || data_to_send < 0) {
+ DEBUG(0,("send_nt_replies failed sanity check pts = %d, dts = %d\n!!!",
+ params_to_send, data_to_send));
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+/****************************************************************************
+ Save case statics.
+****************************************************************************/
+
+static BOOL saved_case_sensitive;
+static BOOL saved_case_preserve;
+static BOOL saved_short_case_preserve;
+
+/****************************************************************************
+ Save case semantics.
+****************************************************************************/
+
+static void set_posix_case_semantics(uint32 file_attributes)
+{
+ if(!(file_attributes & FILE_FLAG_POSIX_SEMANTICS))
+ return;
+
+ saved_case_sensitive = case_sensitive;
+ saved_case_preserve = case_preserve;
+ saved_short_case_preserve = short_case_preserve;
+
+ /* Set to POSIX. */
+ case_sensitive = True;
+ case_preserve = True;
+ short_case_preserve = True;
+}
+
+/****************************************************************************
+ Restore case semantics.
+****************************************************************************/
+
+static void restore_case_semantics(uint32 file_attributes)
+{
+ if(!(file_attributes & FILE_FLAG_POSIX_SEMANTICS))
+ return;
+
+ case_sensitive = saved_case_sensitive;
+ case_preserve = saved_case_preserve;
+ short_case_preserve = saved_short_case_preserve;
+}
+
+/****************************************************************************
+ Utility function to map create disposition.
+****************************************************************************/
+
+static int map_create_disposition( uint32 create_disposition)
+{
+ int ret;
+
+ switch( create_disposition ) {
+ case FILE_CREATE:
+ /* create if not exist, fail if exist */
+ ret = (FILE_CREATE_IF_NOT_EXIST|FILE_EXISTS_FAIL);
+ break;
+ case FILE_SUPERSEDE:
+ case FILE_OVERWRITE_IF:
+ /* create if not exist, trunc if exist */
+ ret = (FILE_CREATE_IF_NOT_EXIST|FILE_EXISTS_TRUNCATE);
+ break;
+ case FILE_OPEN:
+ /* fail if not exist, open if exists */
+ ret = (FILE_FAIL_IF_NOT_EXIST|FILE_EXISTS_OPEN);
+ break;
+ case FILE_OPEN_IF:
+ /* create if not exist, open if exists */
+ ret = (FILE_CREATE_IF_NOT_EXIST|FILE_EXISTS_OPEN);
+ break;
+ case FILE_OVERWRITE:
+ /* fail if not exist, truncate if exists */
+ ret = (FILE_FAIL_IF_NOT_EXIST|FILE_EXISTS_TRUNCATE);
+ break;
+ default:
+ DEBUG(0,("map_create_disposition: Incorrect value for create_disposition = %d\n",
+ create_disposition ));
+ return -1;
+ }
+
+ DEBUG(10,("map_create_disposition: Mapped create_disposition 0x%lx to 0x%x\n",
+ (unsigned long)create_disposition, ret ));
+
+ return ret;
+}
+
+/****************************************************************************
+ Utility function to map share modes.
+****************************************************************************/
+
+static int map_share_mode( char *fname, uint32 create_options,
+ uint32 *desired_access, uint32 share_access, uint32 file_attributes)
+{
+ int smb_open_mode = -1;
+ uint32 original_desired_access = *desired_access;
+
+ /*
+ * Convert GENERIC bits to specific bits.
+ */
+
+ se_map_generic(desired_access, &file_generic_mapping);
+
+ switch( *desired_access & (FILE_READ_DATA|FILE_WRITE_DATA|FILE_APPEND_DATA) ) {
+ case FILE_READ_DATA:
+ smb_open_mode = DOS_OPEN_RDONLY;
+ break;
+ case FILE_WRITE_DATA:
+ case FILE_APPEND_DATA:
+ case FILE_WRITE_DATA|FILE_APPEND_DATA:
+ smb_open_mode = DOS_OPEN_WRONLY;
+ break;
+ case FILE_READ_DATA|FILE_WRITE_DATA:
+ case FILE_READ_DATA|FILE_WRITE_DATA|FILE_APPEND_DATA:
+ case FILE_READ_DATA|FILE_APPEND_DATA:
+ smb_open_mode = DOS_OPEN_RDWR;
+ break;
+ }
+
+ /*
+ * NB. For DELETE_ACCESS we should really check the
+ * directory permissions, as that is what controls
+ * delete, and for WRITE_DAC_ACCESS we should really
+ * check the ownership, as that is what controls the
+ * chmod. Note that this is *NOT* a security hole (this
+ * note is for you, Andrew) as we are not *allowing*
+ * the access at this point, the actual unlink or
+ * chown or chmod call would do this. We are just helping
+ * clients out by telling them if they have a hope
+ * of any of this succeeding. POSIX acls may still
+ * deny the real call. JRA.
+ */
+
+ if (smb_open_mode == -1) {
+
+ if(*desired_access & (DELETE_ACCESS|WRITE_DAC_ACCESS|WRITE_OWNER_ACCESS|SYNCHRONIZE_ACCESS|
+ FILE_EXECUTE|FILE_READ_ATTRIBUTES|
+ FILE_READ_EA|FILE_WRITE_EA|SYSTEM_SECURITY_ACCESS|
+ FILE_WRITE_ATTRIBUTES|READ_CONTROL_ACCESS)) {
+ smb_open_mode = DOS_OPEN_RDONLY;
+ } else if(*desired_access == 0) {
+
+ /*
+ * JRA - NT seems to sometimes send desired_access as zero. play it safe
+ * and map to a stat open.
+ */
+
+ smb_open_mode = DOS_OPEN_RDONLY;
+
+ } else {
+ DEBUG(0,("map_share_mode: Incorrect value 0x%lx for desired_access to file %s\n",
+ (unsigned long)*desired_access, fname));
+ return -1;
+ }
+ }
+
+ /*
+ * Set the special bit that means allow share delete.
+ * This is held outside the normal share mode bits at 1<<15.
+ * JRA.
+ */
+
+ if(share_access & FILE_SHARE_DELETE) {
+ smb_open_mode |= ALLOW_SHARE_DELETE;
+ DEBUG(10,("map_share_mode: FILE_SHARE_DELETE requested. open_mode = 0x%x\n", smb_open_mode));
+ }
+
+ if(*desired_access & DELETE_ACCESS) {
+ DEBUG(10,("map_share_mode: DELETE_ACCESS requested. open_mode = 0x%x\n", smb_open_mode));
+ }
+
+ /*
+ * We need to store the intent to open for Delete. This
+ * is what determines if a delete on close flag can be set.
+ * This is the wrong way (and place) to store this, but for 2.2 this
+ * is the only practical way. JRA.
+ */
+
+ if (create_options & FILE_DELETE_ON_CLOSE) {
+ /*
+ * W2K3 bug compatibility mode... To set delete on close
+ * the redirector must have *specifically* set DELETE_ACCESS
+ * in the desired_access field. Just asking for GENERIC_ALL won't do. JRA.
+ */
+
+ if (!(original_desired_access & DELETE_ACCESS)) {
+ DEBUG(5,("map_share_mode: FILE_DELETE_ON_CLOSE requested without \
+DELETE_ACCESS for file %s. (desired_access = 0x%lx)\n",
+ fname, (unsigned long)*desired_access));
+ return -1;
+ }
+ /* Implicit delete access is *NOT* requested... */
+ smb_open_mode |= DELETE_ON_CLOSE_FLAG;
+ DEBUG(10,("map_share_mode: FILE_DELETE_ON_CLOSE requested. open_mode = 0x%x\n", smb_open_mode));
+ }
+
+ /* Add in the requested share mode. */
+ switch( share_access & (FILE_SHARE_READ|FILE_SHARE_WRITE)) {
+ case FILE_SHARE_READ:
+ smb_open_mode |= SET_DENY_MODE(DENY_WRITE);
+ break;
+ case FILE_SHARE_WRITE:
+ smb_open_mode |= SET_DENY_MODE(DENY_READ);
+ break;
+ case (FILE_SHARE_READ|FILE_SHARE_WRITE):
+ smb_open_mode |= SET_DENY_MODE(DENY_NONE);
+ break;
+ case FILE_SHARE_NONE:
+ smb_open_mode |= SET_DENY_MODE(DENY_ALL);
+ break;
+ }
+
+ /*
+ * Handle an O_SYNC request.
+ */
+
+ if(file_attributes & FILE_FLAG_WRITE_THROUGH)
+ smb_open_mode |= FILE_SYNC_OPENMODE;
+
+ DEBUG(10,("map_share_mode: Mapped desired access 0x%lx, share access 0x%lx, file attributes 0x%lx \
+to open_mode 0x%x\n", (unsigned long)*desired_access, (unsigned long)share_access,
+ (unsigned long)file_attributes, smb_open_mode ));
+
+ return smb_open_mode;
+}
+
+/****************************************************************************
+ Reply to an NT create and X call on a pipe.
+****************************************************************************/
+
+static int nt_open_pipe(char *fname, connection_struct *conn,
+ char *inbuf, char *outbuf, int *ppnum)
+{
+ smb_np_struct *p = NULL;
+
+ uint16 vuid = SVAL(inbuf, smb_uid);
+ int i;
+
+ DEBUG(4,("nt_open_pipe: Opening pipe %s.\n", fname));
+
+ /* See if it is one we want to handle. */
+
+ if (lp_disable_spoolss() && strequal(fname, "\\spoolss"))
+ return(ERROR_BOTH(NT_STATUS_OBJECT_NAME_NOT_FOUND,ERRDOS,ERRbadpipe));
+
+ for( i = 0; known_nt_pipes[i]; i++ )
+ if( strequal(fname,known_nt_pipes[i]))
+ break;
+
+ if ( known_nt_pipes[i] == NULL )
+ return(ERROR_BOTH(NT_STATUS_OBJECT_NAME_NOT_FOUND,ERRDOS,ERRbadpipe));
+
+ /* Strip \\ off the name. */
+ fname++;
+
+ DEBUG(3,("nt_open_pipe: Known pipe %s opening.\n", fname));
+
+ p = open_rpc_pipe_p(fname, conn, vuid);
+ if (!p)
+ return(ERROR_DOS(ERRSRV,ERRnofids));
+
+ *ppnum = p->pnum;
+
+ return 0;
+}
+
+/****************************************************************************
+ Reply to an NT create and X call for pipes.
+****************************************************************************/
+
+static int do_ntcreate_pipe_open(connection_struct *conn,
+ char *inbuf,char *outbuf,int length,int bufsize)
+{
+ pstring fname;
+ int ret;
+ int pnum = -1;
+ char *p = NULL;
+
+ srvstr_pull_buf(inbuf, fname, smb_buf(inbuf), sizeof(fname), STR_TERMINATE);
+
+ if ((ret = nt_open_pipe(fname, conn, inbuf, outbuf, &pnum)) != 0)
+ return ret;
+
+ /*
+ * Deal with pipe return.
+ */
+
+ set_message(outbuf,34,0,True);
+
+ p = outbuf + smb_vwv2;
+ p++;
+ SSVAL(p,0,pnum);
+ p += 2;
+ SIVAL(p,0,FILE_WAS_OPENED);
+ p += 4;
+ p += 32;
+ SIVAL(p,0,FILE_ATTRIBUTE_NORMAL); /* File Attributes. */
+ p += 20;
+ /* File type. */
+ SSVAL(p,0,FILE_TYPE_MESSAGE_MODE_PIPE);
+ /* Device state. */
+ SSVAL(p,2, 0x5FF); /* ? */
+
+ DEBUG(5,("do_ntcreate_pipe_open: open pipe = %s\n", fname));
+
+ return chain_reply(inbuf,outbuf,length,bufsize);
+}
+
+/****************************************************************************
+ Reply to an NT create and X call.
+****************************************************************************/
+
+int reply_ntcreate_and_X(connection_struct *conn,
+ char *inbuf,char *outbuf,int length,int bufsize)
+{
+ int result;
+ pstring fname;
+ enum FAKE_FILE_TYPE fake_file_type = FAKE_FILE_TYPE_NONE;
+ uint32 flags = IVAL(inbuf,smb_ntcreate_Flags);
+ uint32 desired_access = IVAL(inbuf,smb_ntcreate_DesiredAccess);
+ uint32 file_attributes = IVAL(inbuf,smb_ntcreate_FileAttributes);
+ uint32 share_access = IVAL(inbuf,smb_ntcreate_ShareAccess);
+ uint32 create_disposition = IVAL(inbuf,smb_ntcreate_CreateDisposition);
+ uint32 create_options = IVAL(inbuf,smb_ntcreate_CreateOptions);
+ uint16 root_dir_fid = (uint16)IVAL(inbuf,smb_ntcreate_RootDirectoryFid);
+ SMB_BIG_UINT allocation_size = 0;
+ int smb_ofun;
+ int smb_open_mode;
+ /* Breakout the oplock request bits so we can set the
+ reply bits separately. */
+ int oplock_request = 0;
+ int fmode=0,rmode=0;
+ SMB_OFF_T file_len = 0;
+ SMB_STRUCT_STAT sbuf;
+ int smb_action = 0;
+ BOOL bad_path = False;
+ files_struct *fsp=NULL;
+ char *p = NULL;
+ time_t c_time;
+ BOOL extended_oplock_granted = False;
+ NTSTATUS status;
+
+ START_PROFILE(SMBntcreateX);
+
+ DEBUG(10,("reply_ntcreateX: flags = 0x%x, desired_access = 0x%x \
+file_attributes = 0x%x, share_access = 0x%x, create_disposition = 0x%x \
+create_options = 0x%x root_dir_fid = 0x%x\n", flags, desired_access, file_attributes,
+ share_access, create_disposition,
+ create_options, root_dir_fid ));
+
+ /* If it's an IPC, use the pipe handler. */
+
+ if (IS_IPC(conn)) {
+ if (lp_nt_pipe_support()) {
+ END_PROFILE(SMBntcreateX);
+ return do_ntcreate_pipe_open(conn,inbuf,outbuf,length,bufsize);
+ } else {
+ END_PROFILE(SMBntcreateX);
+ return(ERROR_DOS(ERRDOS,ERRnoaccess));
+ }
+ }
+
+ if (create_options & FILE_OPEN_BY_FILE_ID) {
+ END_PROFILE(SMBntcreateX);
+ return ERROR_NT(NT_STATUS_NOT_SUPPORTED);
+ }
+
+ /*
+ * We need to construct the open_and_X ofun value from the
+ * NT values, as that's what our code is structured to accept.
+ */
+
+ if((smb_ofun = map_create_disposition( create_disposition )) == -1) {
+ END_PROFILE(SMBntcreateX);
+ return(ERROR_DOS(ERRDOS,ERRnoaccess));
+ }
+
+ /*
+ * Get the file name.
+ */
+
+ if(root_dir_fid != 0) {
+ /*
+ * This filename is relative to a directory fid.
+ */
+ files_struct *dir_fsp = file_fsp(inbuf,smb_ntcreate_RootDirectoryFid);
+ size_t dir_name_len;
+
+ if(!dir_fsp) {
+ END_PROFILE(SMBntcreateX);
+ return(ERROR_DOS(ERRDOS,ERRbadfid));
+ }
+
+ if(!dir_fsp->is_directory) {
+
+ srvstr_get_path(inbuf, fname, smb_buf(inbuf), sizeof(fname), 0, STR_TERMINATE, &status);
+ if (!NT_STATUS_IS_OK(status)) {
+ END_PROFILE(SMBntcreateX);
+ return ERROR_NT(status);
+ }
+
+ /*
+ * Check to see if this is a mac fork of some kind.
+ */
+
+ if( strchr_m(fname, ':')) {
+ END_PROFILE(SMBntcreateX);
+ return ERROR_NT(NT_STATUS_OBJECT_PATH_NOT_FOUND);
+ }
+
+ /*
+ we need to handle the case when we get a
+ relative open relative to a file and the
+ pathname is blank - this is a reopen!
+ (hint from demyn plantenberg)
+ */
+
+ END_PROFILE(SMBntcreateX);
+ return(ERROR_DOS(ERRDOS,ERRbadfid));
+ }
+
+ /*
+ * Copy in the base directory name.
+ */
+
+ pstrcpy( fname, dir_fsp->fsp_name );
+ dir_name_len = strlen(fname);
+
+ /*
+ * Ensure it ends in a '\'.
+ */
+
+ if(fname[dir_name_len-1] != '\\' && fname[dir_name_len-1] != '/') {
+ pstrcat(fname, "\\");
+ dir_name_len++;
+ }
+
+ srvstr_get_path(inbuf, &fname[dir_name_len], smb_buf(inbuf), sizeof(fname)-dir_name_len, 0, STR_TERMINATE, &status);
+ if (!NT_STATUS_IS_OK(status)) {
+ END_PROFILE(SMBntcreateX);
+ return ERROR_NT(status);
+ }
+ } else {
+ srvstr_get_path(inbuf, fname, smb_buf(inbuf), sizeof(fname), 0, STR_TERMINATE, &status);
+ if (!NT_STATUS_IS_OK(status)) {
+ END_PROFILE(SMBntcreateX);
+ return ERROR_NT(status);
+ }
+
+ /*
+ * Check to see if this is a mac fork of some kind.
+ */
+
+ if( strchr_m(fname, ':')) {
+
+#ifdef HAVE_SYS_QUOTAS
+ if ((fake_file_type=is_fake_file(fname))!=FAKE_FILE_TYPE_NONE) {
+ /*
+ * here we go! support for changing the disk quotas --metze
+ *
+ * we need to fake up to open this MAGIC QUOTA file
+ * and return a valid FID
+ *
+ * w2k close this file directly after openening
+ * xp also tries a QUERY_FILE_INFO on the file and then close it
+ */
+ } else {
+#endif
+ END_PROFILE(SMBntcreateX);
+ return ERROR_NT(NT_STATUS_OBJECT_PATH_NOT_FOUND);
+#ifdef HAVE_SYS_QUOTAS
+ }
+#endif
+ }
+ }
+
+ /*
+ * Now contruct the smb_open_mode value from the filename,
+ * desired access and the share access.
+ */
+ RESOLVE_DFSPATH(fname, conn, inbuf, outbuf);
+
+ if((smb_open_mode = map_share_mode(fname, create_options, &desired_access,
+ share_access,
+ file_attributes)) == -1) {
+ END_PROFILE(SMBntcreateX);
+ return ERROR_NT(NT_STATUS_INVALID_PARAMETER);
+ }
+
+ oplock_request = (flags & REQUEST_OPLOCK) ? EXCLUSIVE_OPLOCK : 0;
+ if (oplock_request) {
+ oplock_request |= (flags & REQUEST_BATCH_OPLOCK) ? BATCH_OPLOCK : 0;
+ }
+
+ /*
+ * Ordinary file or directory.
+ */
+
+ /*
+ * Check if POSIX semantics are wanted.
+ */
+
+ set_posix_case_semantics(file_attributes);
+
+ unix_convert(fname,conn,0,&bad_path,&sbuf);
+
+ /*
+ * If it's a request for a directory open, deal with it separately.
+ */
+
+ if(create_options & FILE_DIRECTORY_FILE) {
+ oplock_request = 0;
+
+ /* Can't open a temp directory. IFS kit test. */
+ if (file_attributes & FILE_ATTRIBUTE_TEMPORARY) {
+ END_PROFILE(SMBntcreateX);
+ return ERROR_NT(NT_STATUS_INVALID_PARAMETER);
+ }
+
+ fsp = open_directory(conn, fname, &sbuf, desired_access, smb_open_mode, smb_ofun, &smb_action);
+
+ restore_case_semantics(file_attributes);
+
+ if(!fsp) {
+ END_PROFILE(SMBntcreateX);
+ return set_bad_path_error(errno, bad_path, outbuf, ERRDOS,ERRnoaccess);
+ }
+ } else {
+ /*
+ * Ordinary file case.
+ */
+
+ /* NB. We have a potential bug here. If we
+ * cause an oplock break to ourselves, then we
+ * could end up processing filename related
+ * SMB requests whilst we await the oplock
+ * break response. As we may have changed the
+ * filename case semantics to be POSIX-like,
+ * this could mean a filename request could
+ * fail when it should succeed. This is a rare
+ * condition, but eventually we must arrange
+ * to restore the correct case semantics
+ * before issuing an oplock break request to
+ * our client. JRA. */
+
+ if (fake_file_type==FAKE_FILE_TYPE_NONE) {
+ fsp = open_file_shared1(conn,fname,&sbuf,
+ desired_access,
+ smb_open_mode,
+ smb_ofun,file_attributes,oplock_request,
+ &rmode,&smb_action);
+ } else {
+ /* to open a fake_file --metze */
+ fsp = open_fake_file_shared1(fake_file_type,conn,fname,&sbuf,
+ desired_access,
+ smb_open_mode,
+ smb_ofun,file_attributes, oplock_request,
+ &rmode,&smb_action);
+ }
+
+ if (!fsp) {
+ /* We cheat here. There are two cases we
+ * care about. One is a directory rename,
+ * where the NT client will attempt to
+ * open the source directory for
+ * DELETE access. Note that when the
+ * NT client does this it does *not*
+ * set the directory bit in the
+ * request packet. This is translated
+ * into a read/write open
+ * request. POSIX states that any open
+ * for write request on a directory
+ * will generate an EISDIR error, so
+ * we can catch this here and open a
+ * pseudo handle that is flagged as a
+ * directory. The second is an open
+ * for a permissions read only, which
+ * we handle in the open_file_stat case. JRA.
+ */
+
+ if(errno == EISDIR) {
+
+ /*
+ * Fail the open if it was explicitly a non-directory file.
+ */
+
+ if (create_options & FILE_NON_DIRECTORY_FILE) {
+ restore_case_semantics(file_attributes);
+ SSVAL(outbuf, smb_flg2,
+ SVAL(outbuf,smb_flg2) | FLAGS2_32_BIT_ERROR_CODES);
+ END_PROFILE(SMBntcreateX);
+ return ERROR_NT(NT_STATUS_FILE_IS_A_DIRECTORY);
+ }
+
+ oplock_request = 0;
+ fsp = open_directory(conn, fname, &sbuf, desired_access, smb_open_mode, smb_ofun, &smb_action);
+
+ if(!fsp) {
+ restore_case_semantics(file_attributes);
+ END_PROFILE(SMBntcreateX);
+ return set_bad_path_error(errno, bad_path, outbuf, ERRDOS,ERRnoaccess);
+ }
+ } else {
+
+ restore_case_semantics(file_attributes);
+ END_PROFILE(SMBntcreateX);
+ return set_bad_path_error(errno, bad_path, outbuf, ERRDOS,ERRnoaccess);
+ }
+ }
+ }
+
+ restore_case_semantics(file_attributes);
+
+ file_len = sbuf.st_size;
+ fmode = dos_mode(conn,fname,&sbuf);
+ if(fmode == 0)
+ fmode = FILE_ATTRIBUTE_NORMAL;
+ if (!fsp->is_directory && (fmode & aDIR)) {
+ close_file(fsp,False);
+ END_PROFILE(SMBntcreateX);
+ return ERROR_DOS(ERRDOS,ERRnoaccess);
+ }
+
+ /* Save the requested allocation size. */
+ allocation_size = (SMB_BIG_UINT)IVAL(inbuf,smb_ntcreate_AllocationSize);
+#ifdef LARGE_SMB_OFF_T
+ allocation_size |= (((SMB_BIG_UINT)IVAL(inbuf,smb_ntcreate_AllocationSize + 4)) << 32);
+#endif
+ if (allocation_size && (allocation_size > (SMB_BIG_UINT)file_len)) {
+ fsp->initial_allocation_size = SMB_ROUNDUP(allocation_size,SMB_ROUNDUP_ALLOCATION_SIZE);
+ if (fsp->is_directory) {
+ close_file(fsp,False);
+ END_PROFILE(SMBntcreateX);
+ /* Can't set allocation size on a directory. */
+ return ERROR_NT(NT_STATUS_ACCESS_DENIED);
+ }
+ if (vfs_allocate_file_space(fsp, fsp->initial_allocation_size) == -1) {
+ close_file(fsp,False);
+ END_PROFILE(SMBntcreateX);
+ return ERROR_NT(NT_STATUS_DISK_FULL);
+ }
+ } else {
+ fsp->initial_allocation_size = SMB_ROUNDUP(((SMB_BIG_UINT)file_len),SMB_ROUNDUP_ALLOCATION_SIZE);
+ }
+
+ /*
+ * If the caller set the extended oplock request bit
+ * and we granted one (by whatever means) - set the
+ * correct bit for extended oplock reply.
+ */
+
+ if (oplock_request && lp_fake_oplocks(SNUM(conn)))
+ extended_oplock_granted = True;
+
+ if(oplock_request && EXCLUSIVE_OPLOCK_TYPE(fsp->oplock_type))
+ extended_oplock_granted = True;
+
+#if 0
+ /* W2K sends back 42 words here ! If we do the same it breaks offline sync. Go figure... ? JRA. */
+ set_message(outbuf,42,0,True);
+#else
+ set_message(outbuf,34,0,True);
+#endif
+
+ p = outbuf + smb_vwv2;
+
+ /*
+ * Currently as we don't support level II oplocks we just report
+ * exclusive & batch here.
+ */
+
+ if (extended_oplock_granted) {
+ if (flags & REQUEST_BATCH_OPLOCK) {
+ SCVAL(p,0, BATCH_OPLOCK_RETURN);
+ } else {
+ SCVAL(p,0, EXCLUSIVE_OPLOCK_RETURN);
+ }
+ } else if (LEVEL_II_OPLOCK_TYPE(fsp->oplock_type)) {
+ SCVAL(p,0, LEVEL_II_OPLOCK_RETURN);
+ } else {
+ SCVAL(p,0,NO_OPLOCK_RETURN);
+ }
+
+ p++;
+ SSVAL(p,0,fsp->fnum);
+ p += 2;
+ if ((create_disposition == FILE_SUPERSEDE) && (smb_action == FILE_WAS_OVERWRITTEN))
+ SIVAL(p,0,FILE_WAS_SUPERSEDED);
+ else
+ SIVAL(p,0,smb_action);
+ p += 4;
+
+ /* Create time. */
+ c_time = get_create_time(&sbuf,lp_fake_dir_create_times(SNUM(conn)));
+
+ if (lp_dos_filetime_resolution(SNUM(conn))) {
+ c_time &= ~1;
+ sbuf.st_atime &= ~1;
+ sbuf.st_mtime &= ~1;
+ sbuf.st_mtime &= ~1;
+ }
+
+ put_long_date(p,c_time);
+ p += 8;
+ put_long_date(p,sbuf.st_atime); /* access time */
+ p += 8;
+ put_long_date(p,sbuf.st_mtime); /* write time */
+ p += 8;
+ put_long_date(p,sbuf.st_mtime); /* change time */
+ p += 8;
+ SIVAL(p,0,fmode); /* File Attributes. */
+ p += 4;
+ SOFF_T(p, 0, get_allocation_size(fsp,&sbuf));
+ p += 8;
+ SOFF_T(p,0,file_len);
+ p += 8;
+ if (flags & EXTENDED_RESPONSE_REQUIRED)
+ SSVAL(p,2,0x7);
+ p += 4;
+ SCVAL(p,0,fsp->is_directory ? 1 : 0);
+
+ DEBUG(5,("reply_ntcreate_and_X: fnum = %d, open name = %s\n", fsp->fnum, fsp->fsp_name));
+
+ result = chain_reply(inbuf,outbuf,length,bufsize);
+ END_PROFILE(SMBntcreateX);
+ return result;
+}
+
+/****************************************************************************
+ Reply to a NT_TRANSACT_CREATE call to open a pipe.
+****************************************************************************/
+
+static int do_nt_transact_create_pipe( connection_struct *conn, char *inbuf, char *outbuf, int length, int bufsize,
+ char **ppsetup, uint32 setup_count,
+ char **ppparams, uint32 parameter_count,
+ char **ppdata, uint32 data_count)
+{
+ pstring fname;
+ char *params = *ppparams;
+ int ret;
+ int pnum = -1;
+ char *p = NULL;
+ NTSTATUS status;
+
+ /*
+ * Ensure minimum number of parameters sent.
+ */
+
+ if(parameter_count < 54) {
+ DEBUG(0,("do_nt_transact_create_pipe - insufficient parameters (%u)\n", (unsigned int)parameter_count));
+ return ERROR_DOS(ERRDOS,ERRnoaccess);
+ }
+
+ srvstr_get_path(inbuf, fname, params+53, sizeof(fname), parameter_count-53, STR_TERMINATE, &status);
+ if (!NT_STATUS_IS_OK(status)) {
+ return ERROR_NT(status);
+ }
+
+ if ((ret = nt_open_pipe(fname, conn, inbuf, outbuf, &pnum)) != 0)
+ return ret;
+
+ /* Realloc the size of parameters and data we will return */
+ params = nttrans_realloc(ppparams, 69);
+ if(params == NULL)
+ return ERROR_DOS(ERRDOS,ERRnomem);
+
+ p = params;
+ SCVAL(p,0,NO_OPLOCK_RETURN);
+
+ p += 2;
+ SSVAL(p,0,pnum);
+ p += 2;
+ SIVAL(p,0,FILE_WAS_OPENED);
+ p += 8;
+
+ p += 32;
+ SIVAL(p,0,FILE_ATTRIBUTE_NORMAL); /* File Attributes. */
+ p += 20;
+ /* File type. */
+ SSVAL(p,0,FILE_TYPE_MESSAGE_MODE_PIPE);
+ /* Device state. */
+ SSVAL(p,2, 0x5FF); /* ? */
+
+ DEBUG(5,("do_nt_transact_create_pipe: open name = %s\n", fname));
+
+ /* Send the required number of replies */
+ send_nt_replies(inbuf, outbuf, bufsize, NT_STATUS_OK, params, 69, *ppdata, 0);
+
+ return -1;
+}
+
+/****************************************************************************
+ Internal fn to set security descriptors.
+****************************************************************************/
+
+static NTSTATUS set_sd(files_struct *fsp, char *data, uint32 sd_len, uint32 security_info_sent)
+{
+ prs_struct pd;
+ SEC_DESC *psd = NULL;
+ TALLOC_CTX *mem_ctx;
+ BOOL ret;
+
+ if (sd_len == 0) {
+ return NT_STATUS_OK;
+ }
+
+ /*
+ * Init the parse struct we will unmarshall from.
+ */
+
+ if ((mem_ctx = talloc_init("set_sd")) == NULL) {
+ DEBUG(0,("set_sd: talloc_init failed.\n"));
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ prs_init(&pd, 0, mem_ctx, UNMARSHALL);
+
+ /*
+ * Setup the prs_struct to point at the memory we just
+ * allocated.
+ */
+
+ prs_give_memory( &pd, data, sd_len, False);
+
+ /*
+ * Finally, unmarshall from the data buffer.
+ */
+
+ if(!sec_io_desc( "sd data", &psd, &pd, 1)) {
+ DEBUG(0,("set_sd: Error in unmarshalling security descriptor.\n"));
+ /*
+ * Return access denied for want of a better error message..
+ */
+ talloc_destroy(mem_ctx);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ if (psd->off_owner_sid==0)
+ security_info_sent &= ~OWNER_SECURITY_INFORMATION;
+ if (psd->off_grp_sid==0)
+ security_info_sent &= ~GROUP_SECURITY_INFORMATION;
+ if (psd->off_sacl==0)
+ security_info_sent &= ~SACL_SECURITY_INFORMATION;
+ if (psd->off_dacl==0)
+ security_info_sent &= ~DACL_SECURITY_INFORMATION;
+
+ ret = SMB_VFS_FSET_NT_ACL( fsp, fsp->fd, security_info_sent, psd);
+
+ if (!ret) {
+ talloc_destroy(mem_ctx);
+ return NT_STATUS_ACCESS_DENIED;
+ }
+
+ talloc_destroy(mem_ctx);
+
+ return NT_STATUS_OK;
+}
+
+/****************************************************************************
+ Reply to a NT_TRANSACT_CREATE call (needs to process SD's).
+****************************************************************************/
+
+static int call_nt_transact_create(connection_struct *conn, char *inbuf, char *outbuf, int length, int bufsize,
+ char **ppsetup, uint32 setup_count,
+ char **ppparams, uint32 parameter_count,
+ char **ppdata, uint32 data_count)
+{
+ pstring fname;
+ char *params = *ppparams;
+ char *data = *ppdata;
+ /* Breakout the oplock request bits so we can set the reply bits separately. */
+ int oplock_request = 0;
+ int fmode=0,rmode=0;
+ SMB_OFF_T file_len = 0;
+ SMB_STRUCT_STAT sbuf;
+ int smb_action = 0;
+ BOOL bad_path = False;
+ files_struct *fsp = NULL;
+ char *p = NULL;
+ BOOL extended_oplock_granted = False;
+ uint32 flags;
+ uint32 desired_access;
+ uint32 file_attributes;
+ uint32 share_access;
+ uint32 create_disposition;
+ uint32 create_options;
+ uint32 sd_len;
+ uint16 root_dir_fid;
+ SMB_BIG_UINT allocation_size = 0;
+ int smb_ofun;
+ int smb_open_mode;
+ time_t c_time;
+ NTSTATUS status;
+
+ DEBUG(5,("call_nt_transact_create\n"));
+
+ /*
+ * If it's an IPC, use the pipe handler.
+ */
+
+ if (IS_IPC(conn)) {
+ if (lp_nt_pipe_support())
+ return do_nt_transact_create_pipe(conn, inbuf, outbuf, length,
+ bufsize,
+ ppsetup, setup_count,
+ ppparams, parameter_count,
+ ppdata, data_count);
+ else
+ return ERROR_DOS(ERRDOS,ERRnoaccess);
+ }
+
+ /*
+ * Ensure minimum number of parameters sent.
+ */
+
+ if(parameter_count < 54) {
+ DEBUG(0,("call_nt_transact_create - insufficient parameters (%u)\n", (unsigned int)parameter_count));
+ return ERROR_DOS(ERRDOS,ERRnoaccess);
+ }
+
+ flags = IVAL(params,0);
+ desired_access = IVAL(params,8);
+ file_attributes = IVAL(params,20);
+ share_access = IVAL(params,24);
+ create_disposition = IVAL(params,28);
+ create_options = IVAL(params,32);
+ sd_len = IVAL(params,36);
+ root_dir_fid = (uint16)IVAL(params,4);
+
+ if (create_options & FILE_OPEN_BY_FILE_ID) {
+ return ERROR_NT(NT_STATUS_NOT_SUPPORTED);
+ }
+
+ /*
+ * We need to construct the open_and_X ofun value from the
+ * NT values, as that's what our code is structured to accept.
+ */
+
+ if((smb_ofun = map_create_disposition( create_disposition )) == -1)
+ return ERROR_DOS(ERRDOS,ERRbadmem);
+
+ /*
+ * Get the file name.
+ */
+
+ if(root_dir_fid != 0) {
+ /*
+ * This filename is relative to a directory fid.
+ */
+
+ files_struct *dir_fsp = file_fsp(params,4);
+ size_t dir_name_len;
+
+ if(!dir_fsp)
+ return ERROR_DOS(ERRDOS,ERRbadfid);
+
+ if(!dir_fsp->is_directory) {
+ srvstr_get_path(inbuf, fname, params+53, sizeof(fname), parameter_count-53, STR_TERMINATE, &status);
+ if (!NT_STATUS_IS_OK(status)) {
+ return ERROR_NT(status);
+ }
+
+ /*
+ * Check to see if this is a mac fork of some kind.
+ */
+
+ if( strchr_m(fname, ':'))
+ return ERROR_NT(NT_STATUS_OBJECT_PATH_NOT_FOUND);
+
+ return ERROR_DOS(ERRDOS,ERRbadfid);
+ }
+
+ /*
+ * Copy in the base directory name.
+ */
+
+ pstrcpy( fname, dir_fsp->fsp_name );
+ dir_name_len = strlen(fname);
+
+ /*
+ * Ensure it ends in a '\'.
+ */
+
+ if((fname[dir_name_len-1] != '\\') && (fname[dir_name_len-1] != '/')) {
+ pstrcat(fname, "\\");
+ dir_name_len++;
+ }
+
+ {
+ pstring tmpname;
+ srvstr_get_path(inbuf, tmpname, params+53, sizeof(tmpname), parameter_count-53, STR_TERMINATE, &status);
+ if (!NT_STATUS_IS_OK(status)) {
+ return ERROR_NT(status);
+ }
+ pstrcat(fname, tmpname);
+ }
+ } else {
+ srvstr_get_path(inbuf, fname, params+53, sizeof(fname), parameter_count-53, STR_TERMINATE, &status);
+ if (!NT_STATUS_IS_OK(status)) {
+ return ERROR_NT(status);
+ }
+
+ /*
+ * Check to see if this is a mac fork of some kind.
+ */
+
+ if( strchr_m(fname, ':'))
+ return ERROR_NT(NT_STATUS_OBJECT_PATH_NOT_FOUND);
+ }
+
+ /*
+ * Now contruct the smb_open_mode value from the desired access
+ * and the share access.
+ */
+
+ if((smb_open_mode = map_share_mode( fname, create_options, &desired_access,
+ share_access, file_attributes)) == -1)
+ return ERROR_NT(NT_STATUS_INVALID_PARAMETER);
+
+ oplock_request = (flags & REQUEST_OPLOCK) ? EXCLUSIVE_OPLOCK : 0;
+ oplock_request |= (flags & REQUEST_BATCH_OPLOCK) ? BATCH_OPLOCK : 0;
+
+ /*
+ * Check if POSIX semantics are wanted.
+ */
+
+ set_posix_case_semantics(file_attributes);
+
+ RESOLVE_DFSPATH(fname, conn, inbuf, outbuf);
+
+ unix_convert(fname,conn,0,&bad_path,&sbuf);
+
+ /*
+ * If it's a request for a directory open, deal with it separately.
+ */
+
+ if(create_options & FILE_DIRECTORY_FILE) {
+
+ /* Can't open a temp directory. IFS kit test. */
+ if (file_attributes & FILE_ATTRIBUTE_TEMPORARY) {
+ return ERROR_NT(NT_STATUS_INVALID_PARAMETER);
+ }
+
+ oplock_request = 0;
+
+ /*
+ * We will get a create directory here if the Win32
+ * app specified a security descriptor in the
+ * CreateDirectory() call.
+ */
+
+ fsp = open_directory(conn, fname, &sbuf, desired_access, smb_open_mode, smb_ofun, &smb_action);
+
+ if(!fsp) {
+ restore_case_semantics(file_attributes);
+ return set_bad_path_error(errno, bad_path, outbuf, ERRDOS,ERRnoaccess);
+ }
+
+ } else {
+
+ /*
+ * Ordinary file case.
+ */
+
+ fsp = open_file_shared1(conn,fname,&sbuf,desired_access,
+ smb_open_mode,smb_ofun,file_attributes,
+ oplock_request,&rmode,&smb_action);
+
+ if (!fsp) {
+
+ if(errno == EISDIR) {
+
+ /*
+ * Fail the open if it was explicitly a non-directory file.
+ */
+
+ if (create_options & FILE_NON_DIRECTORY_FILE) {
+ restore_case_semantics(file_attributes);
+ SSVAL(outbuf, smb_flg2, SVAL(outbuf,smb_flg2) | FLAGS2_32_BIT_ERROR_CODES);
+ return ERROR_NT(NT_STATUS_FILE_IS_A_DIRECTORY);
+ }
+
+ oplock_request = 0;
+ fsp = open_directory(conn, fname, &sbuf, desired_access, smb_open_mode, smb_ofun, &smb_action);
+
+ if(!fsp) {
+ restore_case_semantics(file_attributes);
+ return set_bad_path_error(errno, bad_path, outbuf, ERRDOS,ERRnoaccess);
+ }
+ } else {
+ restore_case_semantics(file_attributes);
+ return set_bad_path_error(errno, bad_path, outbuf, ERRDOS,ERRnoaccess);
+ }
+ }
+
+ file_len = sbuf.st_size;
+ fmode = dos_mode(conn,fname,&sbuf);
+ if(fmode == 0)
+ fmode = FILE_ATTRIBUTE_NORMAL;
+
+ if (fmode & aDIR) {
+ close_file(fsp,False);
+ restore_case_semantics(file_attributes);
+ return ERROR_DOS(ERRDOS,ERRnoaccess);
+ }
+
+ /*
+ * If the caller set the extended oplock request bit
+ * and we granted one (by whatever means) - set the
+ * correct bit for extended oplock reply.
+ */
+
+ if (oplock_request && lp_fake_oplocks(SNUM(conn)))
+ extended_oplock_granted = True;
+
+ if(oplock_request && EXCLUSIVE_OPLOCK_TYPE(fsp->oplock_type))
+ extended_oplock_granted = True;
+ }
+
+ /*
+ * Now try and apply the desired SD.
+ */
+
+ if (sd_len && !NT_STATUS_IS_OK(status = set_sd( fsp, data, sd_len, ALL_SECURITY_INFORMATION))) {
+ close_file(fsp,False);
+ restore_case_semantics(file_attributes);
+ return ERROR_NT(status);
+ }
+
+ restore_case_semantics(file_attributes);
+
+ /* Save the requested allocation size. */
+ allocation_size = (SMB_BIG_UINT)IVAL(params,12);
+#ifdef LARGE_SMB_OFF_T
+ allocation_size |= (((SMB_BIG_UINT)IVAL(params,16)) << 32);
+#endif
+ if (allocation_size && (allocation_size > file_len)) {
+ fsp->initial_allocation_size = SMB_ROUNDUP(allocation_size,SMB_ROUNDUP_ALLOCATION_SIZE);
+ if (fsp->is_directory) {
+ close_file(fsp,False);
+ END_PROFILE(SMBntcreateX);
+ /* Can't set allocation size on a directory. */
+ return ERROR_NT(NT_STATUS_ACCESS_DENIED);
+ }
+ if (vfs_allocate_file_space(fsp, fsp->initial_allocation_size) == -1) {
+ close_file(fsp,False);
+ return ERROR_NT(NT_STATUS_DISK_FULL);
+ }
+ } else {
+ fsp->initial_allocation_size = SMB_ROUNDUP(((SMB_BIG_UINT)file_len),SMB_ROUNDUP_ALLOCATION_SIZE);
+ }
+
+ /* Realloc the size of parameters and data we will return */
+ params = nttrans_realloc(ppparams, 69);
+ if(params == NULL)
+ return ERROR_DOS(ERRDOS,ERRnomem);
+
+ p = params;
+ if (extended_oplock_granted)
+ SCVAL(p,0, BATCH_OPLOCK_RETURN);
+ else if (LEVEL_II_OPLOCK_TYPE(fsp->oplock_type))
+ SCVAL(p,0, LEVEL_II_OPLOCK_RETURN);
+ else
+ SCVAL(p,0,NO_OPLOCK_RETURN);
+
+ p += 2;
+ SSVAL(p,0,fsp->fnum);
+ p += 2;
+ if ((create_disposition == FILE_SUPERSEDE) && (smb_action == FILE_WAS_OVERWRITTEN))
+ SIVAL(p,0,FILE_WAS_SUPERSEDED);
+ else
+ SIVAL(p,0,smb_action);
+ p += 8;
+
+ /* Create time. */
+ c_time = get_create_time(&sbuf,lp_fake_dir_create_times(SNUM(conn)));
+
+ if (lp_dos_filetime_resolution(SNUM(conn))) {
+ c_time &= ~1;
+ sbuf.st_atime &= ~1;
+ sbuf.st_mtime &= ~1;
+ sbuf.st_mtime &= ~1;
+ }
+
+ put_long_date(p,c_time);
+ p += 8;
+ put_long_date(p,sbuf.st_atime); /* access time */
+ p += 8;
+ put_long_date(p,sbuf.st_mtime); /* write time */
+ p += 8;
+ put_long_date(p,sbuf.st_mtime); /* change time */
+ p += 8;
+ SIVAL(p,0,fmode); /* File Attributes. */
+ p += 4;
+ SOFF_T(p, 0, get_allocation_size(fsp,&sbuf));
+ p += 8;
+ SOFF_T(p,0,file_len);
+ p += 8;
+ if (flags & EXTENDED_RESPONSE_REQUIRED)
+ SSVAL(p,2,0x7);
+ p += 4;
+ SCVAL(p,0,fsp->is_directory ? 1 : 0);
+
+ DEBUG(5,("call_nt_transact_create: open name = %s\n", fname));
+
+ /* Send the required number of replies */
+ send_nt_replies(inbuf, outbuf, bufsize, NT_STATUS_OK, params, 69, *ppdata, 0);
+
+ return -1;
+}
+
+/****************************************************************************
+ Reply to a NT CANCEL request.
+****************************************************************************/
+
+int reply_ntcancel(connection_struct *conn,
+ char *inbuf,char *outbuf,int length,int bufsize)
+{
+ /*
+ * Go through and cancel any pending change notifies.
+ */
+
+ int mid = SVAL(inbuf,smb_mid);
+ START_PROFILE(SMBntcancel);
+ remove_pending_change_notify_requests_by_mid(mid);
+ remove_pending_lock_requests_by_mid(mid);
+ srv_cancel_sign_response(mid);
+
+ DEBUG(3,("reply_ntcancel: cancel called on mid = %d.\n", mid));
+
+ END_PROFILE(SMBntcancel);
+ return(-1);
+}
+
+/****************************************************************************
+ Reply to a NT rename request.
+****************************************************************************/
+
+int reply_ntrename(connection_struct *conn,
+ char *inbuf,char *outbuf,int length,int bufsize)
+{
+ int outsize = 0;
+ pstring name;
+ pstring newname;
+ char *p;
+ NTSTATUS status;
+ uint16 attrs = SVAL(inbuf,smb_vwv0);
+ uint16 rename_type = SVAL(inbuf,smb_vwv1);
+
+ START_PROFILE(SMBntrename);
+
+ if ((rename_type != RENAME_FLAG_RENAME) && (rename_type != RENAME_FLAG_HARD_LINK)) {
+ END_PROFILE(SMBntrename);
+ return ERROR_NT(NT_STATUS_ACCESS_DENIED);
+ }
+
+ p = smb_buf(inbuf) + 1;
+ p += srvstr_get_path(inbuf, name, p, sizeof(name), 0, STR_TERMINATE, &status);
+ if (!NT_STATUS_IS_OK(status)) {
+ END_PROFILE(SMBntrename);
+ return ERROR_NT(status);
+ }
+
+ if( strchr_m(name, ':')) {
+ /* Can't rename a stream. */
+ END_PROFILE(SMBntrename);
+ return ERROR_NT(NT_STATUS_ACCESS_DENIED);
+ }
+
+ p++;
+ p += srvstr_get_path(inbuf, newname, p, sizeof(newname), 0, STR_TERMINATE, &status);
+ if (!NT_STATUS_IS_OK(status)) {
+ END_PROFILE(SMBntrename);
+ return ERROR_NT(status);
+ }
+
+ RESOLVE_DFSPATH(name, conn, inbuf, outbuf);
+ RESOLVE_DFSPATH(newname, conn, inbuf, outbuf);
+
+ DEBUG(3,("reply_ntrename : %s -> %s\n",name,newname));
+
+ if (rename_type == RENAME_FLAG_RENAME) {
+ status = rename_internals(conn, name, newname, attrs, False);
+ } else {
+ status = hardlink_internals(conn, name, newname);
+ }
+
+ if (!NT_STATUS_IS_OK(status)) {
+ END_PROFILE(SMBntrename);
+ return ERROR_NT(status);
+ }
+
+ /*
+ * Win2k needs a changenotify request response before it will
+ * update after a rename..
+ */
+ process_pending_change_notify_queue((time_t)0);
+ outsize = set_message(outbuf,0,0,True);
+
+ END_PROFILE(SMBntrename);
+ return(outsize);
+}
+
+/****************************************************************************
+ Reply to an unsolicited SMBNTtranss - just ignore it!
+****************************************************************************/
+
+int reply_nttranss(connection_struct *conn,
+ char *inbuf,char *outbuf,int length,int bufsize)
+{
+ START_PROFILE(SMBnttranss);
+ DEBUG(4,("Ignoring nttranss of length %d\n",length));
+ END_PROFILE(SMBnttranss);
+ return(-1);
+}
+
+/****************************************************************************
+ Reply to a notify change - queue the request and
+ don't allow a directory to be opened.
+****************************************************************************/
+
+static int call_nt_transact_notify_change(connection_struct *conn, char *inbuf, char *outbuf, int length, int bufsize,
+ char **ppsetup, uint32 setup_count,
+ char **ppparams, uint32 parameter_count,
+ char **ppdata, uint32 data_count)
+{
+ char *setup = *ppsetup;
+ files_struct *fsp;
+ uint32 flags;
+
+ if(setup_count < 6)
+ return ERROR_DOS(ERRDOS,ERRbadfunc);
+
+ fsp = file_fsp(setup,4);
+ flags = IVAL(setup, 0);
+
+ DEBUG(3,("call_nt_transact_notify_change\n"));
+
+ if(!fsp)
+ return ERROR_DOS(ERRDOS,ERRbadfid);
+
+ if((!fsp->is_directory) || (conn != fsp->conn))
+ return ERROR_DOS(ERRDOS,ERRbadfid);
+
+ if (!change_notify_set(inbuf, fsp, conn, flags))
+ return(UNIXERROR(ERRDOS,ERRbadfid));
+
+ DEBUG(3,("call_nt_transact_notify_change: notify change called on directory \
+name = %s\n", fsp->fsp_name ));
+
+ return -1;
+}
+
+/****************************************************************************
+ Reply to an NT transact rename command.
+****************************************************************************/
+
+static int call_nt_transact_rename(connection_struct *conn, char *inbuf, char *outbuf, int length, int bufsize,
+ char **ppsetup, uint32 setup_count,
+ char **ppparams, uint32 parameter_count,
+ char **ppdata, uint32 data_count)
+{
+ char *params = *ppparams;
+ pstring new_name;
+ files_struct *fsp = NULL;
+ BOOL replace_if_exists = False;
+ NTSTATUS status;
+
+ if(parameter_count < 4)
+ return ERROR_DOS(ERRDOS,ERRbadfunc);
+
+ fsp = file_fsp(params, 0);
+ replace_if_exists = (SVAL(params,2) & RENAME_REPLACE_IF_EXISTS) ? True : False;
+ CHECK_FSP(fsp, conn);
+ srvstr_get_path(inbuf, new_name, params+4, sizeof(new_name), -1, STR_TERMINATE, &status);
+ if (!NT_STATUS_IS_OK(status)) {
+ return ERROR_NT(status);
+ }
+
+ status = rename_internals(conn, fsp->fsp_name,
+ new_name, 0, replace_if_exists);
+ if (!NT_STATUS_IS_OK(status))
+ return ERROR_NT(status);
+
+ /*
+ * Rename was successful.
+ */
+ send_nt_replies(inbuf, outbuf, bufsize, NT_STATUS_OK, NULL, 0, NULL, 0);
+
+ DEBUG(3,("nt transact rename from = %s, to = %s succeeded.\n",
+ fsp->fsp_name, new_name));
+
+ /*
+ * Win2k needs a changenotify request response before it will
+ * update after a rename..
+ */
+
+ process_pending_change_notify_queue((time_t)0);
+
+ return -1;
+}
+
+/******************************************************************************
+ Fake up a completely empty SD.
+*******************************************************************************/
+
+static size_t get_null_nt_acl(TALLOC_CTX *mem_ctx, SEC_DESC **ppsd)
+{
+ extern DOM_SID global_sid_World;
+ size_t sd_size;
+
+ *ppsd = make_standard_sec_desc( mem_ctx, &global_sid_World, &global_sid_World, NULL, &sd_size);
+ if(!*ppsd) {
+ DEBUG(0,("get_null_nt_acl: Unable to malloc space for security descriptor.\n"));
+ sd_size = 0;
+ }
+
+ return sd_size;
+}
+
+/****************************************************************************
+ Reply to query a security descriptor.
+****************************************************************************/
+
+static int call_nt_transact_query_security_desc(connection_struct *conn, char *inbuf, char *outbuf, int length, int bufsize,
+ char **ppsetup, uint32 setup_count,
+ char **ppparams, uint32 parameter_count,
+ char **ppdata, uint32 data_count)
+{
+ uint32 max_data_count = IVAL(inbuf,smb_nt_MaxDataCount);
+ char *params = *ppparams;
+ char *data = *ppdata;
+ prs_struct pd;
+ SEC_DESC *psd = NULL;
+ size_t sd_size;
+ uint32 security_info_wanted;
+ TALLOC_CTX *mem_ctx;
+ files_struct *fsp = NULL;
+
+ if(parameter_count < 8)
+ return ERROR_DOS(ERRDOS,ERRbadfunc);
+
+ fsp = file_fsp(params,0);
+ if(!fsp)
+ return ERROR_DOS(ERRDOS,ERRbadfid);
+
+ security_info_wanted = IVAL(params,4);
+
+ DEBUG(3,("call_nt_transact_query_security_desc: file = %s\n", fsp->fsp_name ));
+
+ params = nttrans_realloc(ppparams, 4);
+ if(params == NULL)
+ return ERROR_DOS(ERRDOS,ERRnomem);
+
+ if ((mem_ctx = talloc_init("call_nt_transact_query_security_desc")) == NULL) {
+ DEBUG(0,("call_nt_transact_query_security_desc: talloc_init failed.\n"));
+ return ERROR_DOS(ERRDOS,ERRnomem);
+ }
+
+ /*
+ * Get the permissions to return.
+ */
+
+ if (!lp_nt_acl_support(SNUM(conn)))
+ sd_size = get_null_nt_acl(mem_ctx, &psd);
+ else
+ sd_size = SMB_VFS_FGET_NT_ACL(fsp, fsp->fd, security_info_wanted, &psd);
+
+ if (sd_size == 0) {
+ talloc_destroy(mem_ctx);
+ return(UNIXERROR(ERRDOS,ERRnoaccess));
+ }
+
+ DEBUG(3,("call_nt_transact_query_security_desc: sd_size = %d.\n",(int)sd_size));
+
+ SIVAL(params,0,(uint32)sd_size);
+
+ if(max_data_count < sd_size) {
+
+ send_nt_replies(inbuf, outbuf, bufsize, NT_STATUS_BUFFER_TOO_SMALL,
+ params, 4, *ppdata, 0);
+ talloc_destroy(mem_ctx);
+ return -1;
+ }
+
+ /*
+ * Allocate the data we will point this at.
+ */
+
+ data = nttrans_realloc(ppdata, sd_size);
+ if(data == NULL) {
+ talloc_destroy(mem_ctx);
+ return ERROR_DOS(ERRDOS,ERRnomem);
+ }
+
+ /*
+ * Init the parse struct we will marshall into.
+ */
+
+ prs_init(&pd, 0, mem_ctx, MARSHALL);
+
+ /*
+ * Setup the prs_struct to point at the memory we just
+ * allocated.
+ */
+
+ prs_give_memory( &pd, data, (uint32)sd_size, False);
+
+ /*
+ * Finally, linearize into the outgoing buffer.
+ */
+
+ if(!sec_io_desc( "sd data", &psd, &pd, 1)) {
+ DEBUG(0,("call_nt_transact_query_security_desc: Error in marshalling \
+security descriptor.\n"));
+ /*
+ * Return access denied for want of a better error message..
+ */
+ talloc_destroy(mem_ctx);
+ return(UNIXERROR(ERRDOS,ERRnoaccess));
+ }
+
+ /*
+ * Now we can delete the security descriptor.
+ */
+
+ talloc_destroy(mem_ctx);
+
+ send_nt_replies(inbuf, outbuf, bufsize, NT_STATUS_OK, params, 4, data, (int)sd_size);
+ return -1;
+}
+
+/****************************************************************************
+ Reply to set a security descriptor. Map to UNIX perms or POSIX ACLs.
+****************************************************************************/
+
+static int call_nt_transact_set_security_desc(connection_struct *conn, char *inbuf, char *outbuf, int length, int bufsize,
+ char **ppsetup, uint32 setup_count,
+ char **ppparams, uint32 parameter_count,
+ char **ppdata, uint32 data_count)
+{
+ char *params= *ppparams;
+ char *data = *ppdata;
+ files_struct *fsp = NULL;
+ uint32 security_info_sent = 0;
+ NTSTATUS nt_status;
+
+ if(parameter_count < 8)
+ return ERROR_DOS(ERRDOS,ERRbadfunc);
+
+ if((fsp = file_fsp(params,0)) == NULL)
+ return ERROR_DOS(ERRDOS,ERRbadfid);
+
+ if(!lp_nt_acl_support(SNUM(conn)))
+ goto done;
+
+ security_info_sent = IVAL(params,4);
+
+ DEBUG(3,("call_nt_transact_set_security_desc: file = %s, sent 0x%x\n", fsp->fsp_name,
+ (unsigned int)security_info_sent ));
+
+ if (data_count == 0)
+ return ERROR_DOS(ERRDOS, ERRnoaccess);
+
+ if (!NT_STATUS_IS_OK(nt_status = set_sd( fsp, data, data_count, security_info_sent)))
+ return ERROR_NT(nt_status);
+
+ done:
+
+ send_nt_replies(inbuf, outbuf, bufsize, NT_STATUS_OK, NULL, 0, NULL, 0);
+ return -1;
+}
+
+/****************************************************************************
+ Reply to NT IOCTL
+****************************************************************************/
+
+static int call_nt_transact_ioctl(connection_struct *conn, char *inbuf, char *outbuf, int length, int bufsize,
+ char **ppsetup, uint32 setup_count,
+ char **ppparams, uint32 parameter_count,
+ char **ppdata, uint32 data_count)
+{
+ uint32 function;
+ uint16 fidnum;
+ files_struct *fsp;
+ uint8 isFSctl;
+ uint8 compfilter;
+ static BOOL logged_message;
+ char *pdata = *ppdata;
+
+ if (setup_count != 8) {
+ DEBUG(3,("call_nt_transact_ioctl: invalid setup count %d\n", setup_count));
+ return ERROR_NT(NT_STATUS_NOT_SUPPORTED);
+ }
+
+ function = IVAL(*ppsetup, 0);
+ fidnum = SVAL(*ppsetup, 4);
+ isFSctl = CVAL(*ppsetup, 6);
+ compfilter = CVAL(*ppsetup, 7);
+
+ DEBUG(10,("call_nt_transact_ioctl: function[0x%08X] FID[0x%04X] isFSctl[0x%02X] compfilter[0x%02X]\n",
+ function, fidnum, isFSctl, compfilter));
+
+ fsp=file_fsp(*ppsetup, 4);
+ /* this check is done in each implemented function case for now
+ because I don't want to break anything... --metze
+ FSP_BELONGS_CONN(fsp,conn);*/
+
+ switch (function) {
+ case FSCTL_SET_SPARSE:
+ /* pretend this succeeded - tho strictly we should
+ mark the file sparse (if the local fs supports it)
+ so we can know if we need to pre-allocate or not */
+
+ DEBUG(10,("FSCTL_SET_SPARSE: called on FID[0x%04X](but not implemented)\n", fidnum));
+ send_nt_replies(inbuf, outbuf, bufsize, NT_STATUS_OK, NULL, 0, NULL, 0);
+ return -1;
+
+ case FSCTL_0x000900C0:
+ /* pretend this succeeded - don't know what this really is
+ but works ok like this --metze
+ */
+
+ DEBUG(10,("FSCTL_0x000900C0: called on FID[0x%04X](but not implemented)\n",fidnum));
+ send_nt_replies(inbuf, outbuf, bufsize, NT_STATUS_OK, NULL, 0, NULL, 0);
+ return -1;
+
+ case FSCTL_GET_REPARSE_POINT:
+ /* pretend this fail - my winXP does it like this
+ * --metze
+ */
+
+ DEBUG(10,("FSCTL_GET_REPARSE_POINT: called on FID[0x%04X](but not implemented)\n",fidnum));
+ send_nt_replies(inbuf, outbuf, bufsize, NT_STATUS_NOT_A_REPARSE_POINT, NULL, 0, NULL, 0);
+ return -1;
+
+ case FSCTL_SET_REPARSE_POINT:
+ /* pretend this fail - I'm assuming this because of the FSCTL_GET_REPARSE_POINT case.
+ * --metze
+ */
+
+ DEBUG(10,("FSCTL_SET_REPARSE_POINT: called on FID[0x%04X](but not implemented)\n",fidnum));
+ send_nt_replies(inbuf, outbuf, bufsize, NT_STATUS_NOT_A_REPARSE_POINT, NULL, 0, NULL, 0);
+ return -1;
+
+ case FSCTL_GET_SHADOW_COPY_DATA: /* don't know if this name is right...*/
+ {
+ /*
+ * This is called to retrieve the number of Shadow Copies (a.k.a. snapshots)
+ * and return their volume names. If max_data_count is 16, then it is just
+ * asking for the number of volumes and length of the combined names.
+ *
+ * pdata is the data allocated by our caller, but that uses
+ * total_data_count (which is 0 in our case) rather than max_data_count.
+ * Allocate the correct amount and return the pointer to let
+ * it be deallocated when we return.
+ */
+ uint32 max_data_count = IVAL(inbuf,smb_nt_MaxDataCount);
+ SHADOW_COPY_DATA *shadow_data = NULL;
+ TALLOC_CTX *shadow_mem_ctx = NULL;
+ BOOL labels = False;
+ uint32 labels_data_count = 0;
+ uint32 i;
+ char *cur_pdata;
+
+ FSP_BELONGS_CONN(fsp,conn);
+
+ if (max_data_count < 16) {
+ DEBUG(0,("FSCTL_GET_SHADOW_COPY_DATA: max_data_count(%u) < 16 is invalid!\n",
+ max_data_count));
+ return ERROR_NT(NT_STATUS_INVALID_PARAMETER);
+ }
+
+ if (max_data_count > 16) {
+ labels = True;
+ }
+
+ shadow_mem_ctx = talloc_init("SHADOW_COPY_DATA");
+ if (shadow_mem_ctx == NULL) {
+ DEBUG(0,("talloc_init(SHADOW_COPY_DATA) failed!\n"));
+ return ERROR_NT(NT_STATUS_NO_MEMORY);
+ }
+
+ shadow_data = (SHADOW_COPY_DATA *)talloc_zero(shadow_mem_ctx,sizeof(SHADOW_COPY_DATA));
+ if (shadow_data == NULL) {
+ DEBUG(0,("talloc_zero() failed!\n"));
+ return ERROR_NT(NT_STATUS_NO_MEMORY);
+ }
+
+ shadow_data->mem_ctx = shadow_mem_ctx;
+
+ /*
+ * Call the VFS routine to actually do the work.
+ */
+ if (SMB_VFS_GET_SHADOW_COPY_DATA(fsp, shadow_data, labels)!=0) {
+ talloc_destroy(shadow_data->mem_ctx);
+ if (errno == ENOSYS) {
+ DEBUG(5,("FSCTL_GET_SHADOW_COPY_DATA: connectpath %s, not supported.\n",
+ conn->connectpath));
+ return ERROR_NT(NT_STATUS_NOT_SUPPORTED);
+ } else {
+ DEBUG(0,("FSCTL_GET_SHADOW_COPY_DATA: connectpath %s, failed.\n",
+ conn->connectpath));
+ return ERROR_NT(NT_STATUS_UNSUCCESSFUL);
+ }
+ }
+
+ labels_data_count = (shadow_data->num_volumes*2*sizeof(SHADOW_COPY_LABEL))+2;
+
+ if (!labels) {
+ data_count = 16;
+ } else {
+ data_count = 12+labels_data_count+4;
+ }
+
+ if (max_data_count<data_count) {
+ DEBUG(0,("FSCTL_GET_SHADOW_COPY_DATA: max_data_count(%u) too small (%u) bytes needed!\n",
+ max_data_count,data_count));
+ talloc_destroy(shadow_data->mem_ctx);
+ return ERROR_NT(NT_STATUS_BUFFER_TOO_SMALL);
+ }
+
+ pdata = nttrans_realloc(ppdata, data_count);
+ if (pdata == NULL) {
+ talloc_destroy(shadow_data->mem_ctx);
+ return ERROR_NT(NT_STATUS_NO_MEMORY);
+ }
+
+ cur_pdata = pdata;
+
+ /* num_volumes 4 bytes */
+ SIVAL(pdata,0,shadow_data->num_volumes);
+
+ if (labels) {
+ /* num_labels 4 bytes */
+ SIVAL(pdata,4,shadow_data->num_volumes);
+ }
+
+ /* needed_data_count 4 bytes */
+ SIVAL(pdata,8,labels_data_count);
+
+ cur_pdata+=12;
+
+ DEBUG(10,("FSCTL_GET_SHADOW_COPY_DATA: %u volumes for path[%s].\n",
+ shadow_data->num_volumes,fsp->fsp_name));
+ if (labels && shadow_data->labels) {
+ for (i=0;i<shadow_data->num_volumes;i++) {
+ srvstr_push(outbuf, cur_pdata, shadow_data->labels[i], 2*sizeof(SHADOW_COPY_LABEL), STR_UNICODE|STR_TERMINATE);
+ cur_pdata+=2*sizeof(SHADOW_COPY_LABEL);
+ DEBUGADD(10,("Label[%u]: '%s'\n",i,shadow_data->labels[i]));
+ }
+ }
+
+ talloc_destroy(shadow_data->mem_ctx);
+
+ send_nt_replies(inbuf, outbuf, bufsize, NT_STATUS_OK, NULL, 0, pdata, data_count);
+
+ return -1;
+ }
+
+ case FSCTL_FIND_FILES_BY_SID: /* I hope this name is right */
+ {
+ /* pretend this succeeded -
+ *
+ * we have to send back a list with all files owned by this SID
+ *
+ * but I have to check that --metze
+ */
+ DOM_SID sid;
+ uid_t uid;
+ size_t sid_len = MIN(data_count-4,SID_MAX_SIZE);
+
+ DEBUG(10,("FSCTL_FIND_FILES_BY_SID: called on FID[0x%04X]\n",fidnum));
+
+ FSP_BELONGS_CONN(fsp,conn);
+
+ /* unknown 4 bytes: this is not the length of the sid :-( */
+ /*unknown = IVAL(pdata,0);*/
+
+ sid_parse(pdata+4,sid_len,&sid);
+ DEBUGADD(10,("for SID: %s\n",sid_string_static(&sid)));
+
+ if (!NT_STATUS_IS_OK(sid_to_uid(&sid, &uid))) {
+ DEBUG(0,("sid_to_uid: failed, sid[%s] sid_len[%lu]\n",
+ sid_string_static(&sid),(unsigned long)sid_len));
+ uid = (-1);
+ }
+
+ /* we can take a look at the find source :-)
+ *
+ * find ./ -uid $uid -name '*' is what we need here
+ *
+ *
+ * and send 4bytes len and then NULL terminated unicode strings
+ * for each file
+ *
+ * but I don't know how to deal with the paged results
+ * (maybe we can hang the result anywhere in the fsp struct)
+ *
+ * we don't send all files at once
+ * and at the next we should *not* start from the beginning,
+ * so we have to cache the result
+ *
+ * --metze
+ */
+
+ /* this works for now... */
+ send_nt_replies(inbuf, outbuf, bufsize, NT_STATUS_OK, NULL, 0, NULL, 0);
+ return -1;
+ }
+ default:
+ if (!logged_message) {
+ logged_message = True; /* Only print this once... */
+ DEBUG(0,("call_nt_transact_ioctl(0x%x): Currently not implemented.\n",
+ function));
+ }
+ }
+
+ return ERROR_NT(NT_STATUS_NOT_SUPPORTED);
+}
+
+
+#ifdef HAVE_SYS_QUOTAS
+/****************************************************************************
+ Reply to get user quota
+****************************************************************************/
+
+static int call_nt_transact_get_user_quota(connection_struct *conn, char *inbuf, char *outbuf, int length, int bufsize,
+ char **ppsetup, uint32 setup_count,
+ char **ppparams, uint32 parameter_count,
+ char **ppdata, uint32 data_count)
+{
+ NTSTATUS nt_status = NT_STATUS_OK;
+ uint32 max_data_count = IVAL(inbuf,smb_nt_MaxDataCount);
+ char *params = *ppparams;
+ char *pdata = *ppdata;
+ char *entry;
+ int data_len=0,param_len=0;
+ int qt_len=0;
+ int entry_len = 0;
+ files_struct *fsp = NULL;
+ uint16 level = 0;
+ size_t sid_len;
+ DOM_SID sid;
+ BOOL start_enum = True;
+ SMB_NTQUOTA_STRUCT qt;
+ SMB_NTQUOTA_LIST *tmp_list;
+ SMB_NTQUOTA_HANDLE *qt_handle = NULL;
+ extern struct current_user current_user;
+
+ ZERO_STRUCT(qt);
+
+ /* access check */
+ if (current_user.uid != 0) {
+ DEBUG(1,("set_user_quota: access_denied service [%s] user [%s]\n",
+ lp_servicename(SNUM(conn)),conn->user));
+ return ERROR_DOS(ERRDOS,ERRnoaccess);
+ }
+
+ /*
+ * Ensure minimum number of parameters sent.
+ */
+
+ if (parameter_count < 4) {
+ DEBUG(0,("TRANSACT_GET_USER_QUOTA: requires %d >= 4 bytes parameters\n",parameter_count));
+ return ERROR_DOS(ERRDOS,ERRinvalidparam);
+ }
+
+ /* maybe we can check the quota_fnum */
+ fsp = file_fsp(params,0);
+ if (!CHECK_NTQUOTA_HANDLE_OK(fsp,conn)) {
+ DEBUG(3,("TRANSACT_GET_USER_QUOTA: no valid QUOTA HANDLE\n"));
+ return ERROR_NT(NT_STATUS_INVALID_HANDLE);
+ }
+
+ /* the NULL pointer cheking for fsp->fake_file_handle->pd
+ * is done by CHECK_NTQUOTA_HANDLE_OK()
+ */
+ qt_handle = (SMB_NTQUOTA_HANDLE *)fsp->fake_file_handle->pd;
+
+ level = SVAL(params,2);
+
+ /* unknown 12 bytes leading in params */
+
+ switch (level) {
+ case TRANSACT_GET_USER_QUOTA_LIST_CONTINUE:
+ /* seems that we should continue with the enum here --metze */
+
+ if (qt_handle->quota_list!=NULL &&
+ qt_handle->tmp_list==NULL) {
+
+ /* free the list */
+ free_ntquota_list(&(qt_handle->quota_list));
+
+ /* Realloc the size of parameters and data we will return */
+ param_len = 4;
+ params = nttrans_realloc(ppparams, param_len);
+ if(params == NULL)
+ return ERROR_DOS(ERRDOS,ERRnomem);
+
+ data_len = 0;
+ SIVAL(params,0,data_len);
+
+ break;
+ }
+
+ start_enum = False;
+
+ case TRANSACT_GET_USER_QUOTA_LIST_START:
+
+ if (qt_handle->quota_list==NULL &&
+ qt_handle->tmp_list==NULL) {
+ start_enum = True;
+ }
+
+ if (start_enum && vfs_get_user_ntquota_list(fsp,&(qt_handle->quota_list))!=0)
+ return ERROR_DOS(ERRSRV,ERRerror);
+
+ /* Realloc the size of parameters and data we will return */
+ param_len = 4;
+ params = nttrans_realloc(ppparams, param_len);
+ if(params == NULL)
+ return ERROR_DOS(ERRDOS,ERRnomem);
+
+ /* we should not trust the value in max_data_count*/
+ max_data_count = MIN(max_data_count,2048);
+
+ pdata = nttrans_realloc(ppdata, max_data_count);/* should be max data count from client*/
+ if(pdata == NULL)
+ return ERROR_DOS(ERRDOS,ERRnomem);
+
+ entry = pdata;
+
+
+ /* set params Size of returned Quota Data 4 bytes*/
+ /* but set it later when we know it */
+
+ /* for each entry push the data */
+
+ if (start_enum) {
+ qt_handle->tmp_list = qt_handle->quota_list;
+ }
+
+ tmp_list = qt_handle->tmp_list;
+
+ for (;((tmp_list!=NULL)&&((qt_len +40+SID_MAX_SIZE)<max_data_count));
+ tmp_list=tmp_list->next,entry+=entry_len,qt_len+=entry_len) {
+
+ sid_len = sid_size(&tmp_list->quotas->sid);
+ entry_len = 40 + sid_len;
+
+ /* nextoffset entry 4 bytes */
+ SIVAL(entry,0,entry_len);
+
+ /* then the len of the SID 4 bytes */
+ SIVAL(entry,4,sid_len);
+
+ /* unknown data 8 bytes SMB_BIG_UINT */
+ SBIG_UINT(entry,8,(SMB_BIG_UINT)0); /* this is not 0 in windows...-metze*/
+
+ /* the used disk space 8 bytes SMB_BIG_UINT */
+ SBIG_UINT(entry,16,tmp_list->quotas->usedspace);
+
+ /* the soft quotas 8 bytes SMB_BIG_UINT */
+ SBIG_UINT(entry,24,tmp_list->quotas->softlim);
+
+ /* the hard quotas 8 bytes SMB_BIG_UINT */
+ SBIG_UINT(entry,32,tmp_list->quotas->hardlim);
+
+ /* and now the SID */
+ sid_linearize(entry+40, sid_len, &tmp_list->quotas->sid);
+ }
+
+ qt_handle->tmp_list = tmp_list;
+
+ /* overwrite the offset of the last entry */
+ SIVAL(entry-entry_len,0,0);
+
+ data_len = 4+qt_len;
+ /* overwrite the params quota_data_len */
+ SIVAL(params,0,data_len);
+
+ break;
+
+ case TRANSACT_GET_USER_QUOTA_FOR_SID:
+
+ /* unknown 4 bytes IVAL(pdata,0) */
+
+ if (data_count < 8) {
+ DEBUG(0,("TRANSACT_GET_USER_QUOTA_FOR_SID: requires %d >= %d bytes data\n",data_count,8));
+ return ERROR_DOS(ERRDOS,ERRunknownlevel);
+ }
+
+ sid_len = IVAL(pdata,4);
+
+ if (data_count < 8+sid_len) {
+ DEBUG(0,("TRANSACT_GET_USER_QUOTA_FOR_SID: requires %d >= %lu bytes data\n",data_count,(unsigned long)(8+sid_len)));
+ return ERROR_DOS(ERRDOS,ERRunknownlevel);
+ }
+
+ data_len = 4+40+sid_len;
+
+ if (max_data_count < data_len) {
+ DEBUG(0,("TRANSACT_GET_USER_QUOTA_FOR_SID: max_data_count(%d) < data_len(%d)\n",
+ max_data_count, data_len));
+ param_len = 4;
+ SIVAL(params,0,data_len);
+ data_len = 0;
+ nt_status = NT_STATUS_BUFFER_TOO_SMALL;
+ break;
+ }
+
+ sid_parse(pdata+8,sid_len,&sid);
+
+
+ if (vfs_get_ntquota(fsp, SMB_USER_QUOTA_TYPE, &sid, &qt)!=0) {
+ ZERO_STRUCT(qt);
+ /*
+ * we have to return zero's in all fields
+ * instead of returning an error here
+ * --metze
+ */
+ }
+
+ /* Realloc the size of parameters and data we will return */
+ param_len = 4;
+ params = nttrans_realloc(ppparams, param_len);
+ if(params == NULL)
+ return ERROR_DOS(ERRDOS,ERRnomem);
+
+ pdata = nttrans_realloc(ppdata, data_len);
+ if(pdata == NULL)
+ return ERROR_DOS(ERRDOS,ERRnomem);
+
+ entry = pdata;
+
+ /* set params Size of returned Quota Data 4 bytes*/
+ SIVAL(params,0,data_len);
+
+ /* nextoffset entry 4 bytes */
+ SIVAL(entry,0,0);
+
+ /* then the len of the SID 4 bytes */
+ SIVAL(entry,4,sid_len);
+
+ /* unknown data 8 bytes SMB_BIG_UINT */
+ SBIG_UINT(entry,8,(SMB_BIG_UINT)0); /* this is not 0 in windows...-mezte*/
+
+ /* the used disk space 8 bytes SMB_BIG_UINT */
+ SBIG_UINT(entry,16,qt.usedspace);
+
+ /* the soft quotas 8 bytes SMB_BIG_UINT */
+ SBIG_UINT(entry,24,qt.softlim);
+
+ /* the hard quotas 8 bytes SMB_BIG_UINT */
+ SBIG_UINT(entry,32,qt.hardlim);
+
+ /* and now the SID */
+ sid_linearize(entry+40, sid_len, &sid);
+
+ break;
+
+ default:
+ DEBUG(0,("do_nt_transact_get_user_quota: fnum %d unknown level 0x%04hX\n",fsp->fnum,level));
+ return ERROR_DOS(ERRSRV,ERRerror);
+ break;
+ }
+
+ send_nt_replies(inbuf, outbuf, bufsize, nt_status, params, param_len, pdata, data_len);
+
+ return -1;
+}
+
+/****************************************************************************
+ Reply to set user quota
+****************************************************************************/
+
+static int call_nt_transact_set_user_quota(connection_struct *conn, char *inbuf, char *outbuf, int length, int bufsize,
+ char **ppsetup, uint32 setup_count,
+ char **ppparams, uint32 parameter_count,
+ char **ppdata, uint32 data_count)
+{
+ char *params = *ppparams;
+ char *pdata = *ppdata;
+ int data_len=0,param_len=0;
+ SMB_NTQUOTA_STRUCT qt;
+ size_t sid_len;
+ DOM_SID sid;
+ files_struct *fsp = NULL;
+
+ ZERO_STRUCT(qt);
+
+ /* access check */
+ if (conn->admin_user != True) {
+ DEBUG(1,("set_user_quota: access_denied service [%s] user [%s]\n",
+ lp_servicename(SNUM(conn)),conn->user));
+ return ERROR_DOS(ERRDOS,ERRnoaccess);
+ }
+
+ /*
+ * Ensure minimum number of parameters sent.
+ */
+
+ if (parameter_count < 2) {
+ DEBUG(0,("TRANSACT_SET_USER_QUOTA: requires %d >= 2 bytes parameters\n",parameter_count));
+ return ERROR_DOS(ERRDOS,ERRinvalidparam);
+ }
+
+ /* maybe we can check the quota_fnum */
+ fsp = file_fsp(params,0);
+ if (!CHECK_NTQUOTA_HANDLE_OK(fsp,conn)) {
+ DEBUG(3,("TRANSACT_GET_USER_QUOTA: no valid QUOTA HANDLE\n"));
+ return ERROR_NT(NT_STATUS_INVALID_HANDLE);
+ }
+
+ if (data_count < 40) {
+ DEBUG(0,("TRANSACT_SET_USER_QUOTA: requires %d >= %d bytes data\n",data_count,40));
+ return ERROR_DOS(ERRDOS,ERRunknownlevel);
+ }
+
+ /* offset to next quota record.
+ * 4 bytes IVAL(pdata,0)
+ * unused here...
+ */
+
+ /* sid len */
+ sid_len = IVAL(pdata,4);
+
+ if (data_count < 40+sid_len) {
+ DEBUG(0,("TRANSACT_SET_USER_QUOTA: requires %d >= %lu bytes data\n",data_count,(unsigned long)40+sid_len));
+ return ERROR_DOS(ERRDOS,ERRunknownlevel);
+ }
+
+ /* unknown 8 bytes in pdata
+ * maybe its the change time in NTTIME
+ */
+
+ /* the used space 8 bytes (SMB_BIG_UINT)*/
+ qt.usedspace = (SMB_BIG_UINT)IVAL(pdata,16);
+#ifdef LARGE_SMB_OFF_T
+ qt.usedspace |= (((SMB_BIG_UINT)IVAL(pdata,20)) << 32);
+#else /* LARGE_SMB_OFF_T */
+ if ((IVAL(pdata,20) != 0)&&
+ ((qt.usedspace != 0xFFFFFFFF)||
+ (IVAL(pdata,20)!=0xFFFFFFFF))) {
+ /* more than 32 bits? */
+ return ERROR_DOS(ERRDOS,ERRunknownlevel);
+ }
+#endif /* LARGE_SMB_OFF_T */
+
+ /* the soft quotas 8 bytes (SMB_BIG_UINT)*/
+ qt.softlim = (SMB_BIG_UINT)IVAL(pdata,24);
+#ifdef LARGE_SMB_OFF_T
+ qt.softlim |= (((SMB_BIG_UINT)IVAL(pdata,28)) << 32);
+#else /* LARGE_SMB_OFF_T */
+ if ((IVAL(pdata,28) != 0)&&
+ ((qt.softlim != 0xFFFFFFFF)||
+ (IVAL(pdata,28)!=0xFFFFFFFF))) {
+ /* more than 32 bits? */
+ return ERROR_DOS(ERRDOS,ERRunknownlevel);
+ }
+#endif /* LARGE_SMB_OFF_T */
+
+ /* the hard quotas 8 bytes (SMB_BIG_UINT)*/
+ qt.hardlim = (SMB_BIG_UINT)IVAL(pdata,32);
+#ifdef LARGE_SMB_OFF_T
+ qt.hardlim |= (((SMB_BIG_UINT)IVAL(pdata,36)) << 32);
+#else /* LARGE_SMB_OFF_T */
+ if ((IVAL(pdata,36) != 0)&&
+ ((qt.hardlim != 0xFFFFFFFF)||
+ (IVAL(pdata,36)!=0xFFFFFFFF))) {
+ /* more than 32 bits? */
+ return ERROR_DOS(ERRDOS,ERRunknownlevel);
+ }
+#endif /* LARGE_SMB_OFF_T */
+
+ sid_parse(pdata+40,sid_len,&sid);
+ DEBUGADD(8,("SID: %s\n",sid_string_static(&sid)));
+
+ /* 44 unknown bytes left... */
+
+ if (vfs_set_ntquota(fsp, SMB_USER_QUOTA_TYPE, &sid, &qt)!=0) {
+ return ERROR_DOS(ERRSRV,ERRerror);
+ }
+
+ send_nt_replies(inbuf, outbuf, bufsize, NT_STATUS_OK, params, param_len, pdata, data_len);
+
+ return -1;
+}
+#endif /* HAVE_SYS_QUOTAS */
+
+/****************************************************************************
+ Reply to a SMBNTtrans.
+****************************************************************************/
+
+int reply_nttrans(connection_struct *conn,
+ char *inbuf,char *outbuf,int length,int bufsize)
+{
+ int outsize = 0;
+#if 0 /* Not used. */
+ uint16 max_setup_count = CVAL(inbuf, smb_nt_MaxSetupCount);
+ uint32 max_parameter_count = IVAL(inbuf, smb_nt_MaxParameterCount);
+ uint32 max_data_count = IVAL(inbuf,smb_nt_MaxDataCount);
+#endif /* Not used. */
+ uint32 total_parameter_count = IVAL(inbuf, smb_nt_TotalParameterCount);
+ uint32 total_data_count = IVAL(inbuf, smb_nt_TotalDataCount);
+ uint32 parameter_count = IVAL(inbuf,smb_nt_ParameterCount);
+ uint32 parameter_offset = IVAL(inbuf,smb_nt_ParameterOffset);
+ uint32 data_count = IVAL(inbuf,smb_nt_DataCount);
+ uint32 data_offset = IVAL(inbuf,smb_nt_DataOffset);
+ uint16 setup_count = 2*CVAL(inbuf,smb_nt_SetupCount); /* setup count is in *words* */
+ uint16 function_code = SVAL( inbuf, smb_nt_Function);
+ char *params = NULL, *data = NULL, *setup = NULL;
+ uint32 num_params_sofar, num_data_sofar;
+ START_PROFILE(SMBnttrans);
+
+ if(global_oplock_break &&
+ ((function_code == NT_TRANSACT_CREATE) ||
+ (function_code == NT_TRANSACT_RENAME))) {
+ /*
+ * Queue this open message as we are the process of an oplock break.
+ */
+
+ DEBUG(2,("reply_nttrans: queueing message code 0x%x \
+due to being in oplock break state.\n", (unsigned int)function_code ));
+
+ push_oplock_pending_smb_message( inbuf, length);
+ END_PROFILE(SMBnttrans);
+ return -1;
+ }
+
+ if (IS_IPC(conn) && (function_code != NT_TRANSACT_CREATE)) {
+ END_PROFILE(SMBnttrans);
+ return ERROR_DOS(ERRSRV,ERRaccess);
+ }
+
+ outsize = set_message(outbuf,0,0,True);
+
+ /*
+ * All nttrans messages we handle have smb_wct == 19 + setup_count.
+ * Ensure this is so as a sanity check.
+ */
+
+ if(CVAL(inbuf, smb_wct) != 19 + (setup_count/2)) {
+ DEBUG(2,("Invalid smb_wct %d in nttrans call (should be %d)\n",
+ CVAL(inbuf, smb_wct), 19 + (setup_count/2)));
+ goto bad_param;
+ }
+
+ /* Allocate the space for the setup, the maximum needed parameters and data */
+
+ if(setup_count > 0)
+ setup = (char *)malloc(setup_count);
+ if (total_parameter_count > 0)
+ params = (char *)malloc(total_parameter_count);
+ if (total_data_count > 0)
+ data = (char *)malloc(total_data_count);
+
+ if ((total_parameter_count && !params) || (total_data_count && !data) ||
+ (setup_count && !setup)) {
+ SAFE_FREE(setup);
+ SAFE_FREE(params);
+ SAFE_FREE(data);
+ DEBUG(0,("reply_nttrans : Out of memory\n"));
+ END_PROFILE(SMBnttrans);
+ return ERROR_DOS(ERRDOS,ERRnomem);
+ }
+
+ /* Copy the param and data bytes sent with this request into the params buffer */
+ num_params_sofar = parameter_count;
+ num_data_sofar = data_count;
+
+ if (parameter_count > total_parameter_count || data_count > total_data_count)
+ goto bad_param;
+
+ if(setup) {
+ DEBUG(10,("reply_nttrans: setup_count = %d\n", setup_count));
+ if ((smb_nt_SetupStart + setup_count < smb_nt_SetupStart) ||
+ (smb_nt_SetupStart + setup_count < setup_count))
+ goto bad_param;
+ if (smb_nt_SetupStart + setup_count > length)
+ goto bad_param;
+
+ memcpy( setup, &inbuf[smb_nt_SetupStart], setup_count);
+ dump_data(10, setup, setup_count);
+ }
+ if(params) {
+ DEBUG(10,("reply_nttrans: parameter_count = %d\n", parameter_count));
+ if ((parameter_offset + parameter_count < parameter_offset) ||
+ (parameter_offset + parameter_count < parameter_count))
+ goto bad_param;
+ if ((smb_base(inbuf) + parameter_offset + parameter_count > inbuf + length)||
+ (smb_base(inbuf) + parameter_offset + parameter_count < smb_base(inbuf)))
+ goto bad_param;
+
+ memcpy( params, smb_base(inbuf) + parameter_offset, parameter_count);
+ dump_data(10, params, parameter_count);
+ }
+ if(data) {
+ DEBUG(10,("reply_nttrans: data_count = %d\n",data_count));
+ if ((data_offset + data_count < data_offset) || (data_offset + data_count < data_count))
+ goto bad_param;
+ if ((smb_base(inbuf) + data_offset + data_count > inbuf + length) ||
+ (smb_base(inbuf) + data_offset + data_count < smb_base(inbuf)))
+ goto bad_param;
+
+ memcpy( data, smb_base(inbuf) + data_offset, data_count);
+ dump_data(10, data, data_count);
+ }
+
+ srv_signing_trans_start(SVAL(inbuf,smb_mid));
+
+ if(num_data_sofar < total_data_count || num_params_sofar < total_parameter_count) {
+ /* We need to send an interim response then receive the rest
+ of the parameter/data bytes */
+ outsize = set_message(outbuf,0,0,True);
+ srv_signing_trans_stop();
+ if (!send_smb(smbd_server_fd(),outbuf))
+ exit_server("reply_nttrans: send_smb failed.");
+
+ while( num_data_sofar < total_data_count || num_params_sofar < total_parameter_count) {
+ BOOL ret;
+ uint32 parameter_displacement;
+ uint32 data_displacement;
+
+ ret = receive_next_smb(inbuf,bufsize,SMB_SECONDARY_WAIT);
+
+ /*
+ * The sequence number for the trans reply is always
+ * based on the last secondary received.
+ */
+
+ srv_signing_trans_start(SVAL(inbuf,smb_mid));
+
+ if((ret && (CVAL(inbuf, smb_com) != SMBnttranss)) || !ret) {
+ outsize = set_message(outbuf,0,0,True);
+ if(ret) {
+ DEBUG(0,("reply_nttrans: Invalid secondary nttrans packet\n"));
+ } else {
+ DEBUG(0,("reply_nttrans: %s in getting secondary nttrans response.\n",
+ (smb_read_error == READ_ERROR) ? "error" : "timeout" ));
+ }
+ goto bad_param;
+ }
+
+ /* Revise total_params and total_data in case they have changed downwards */
+ if (IVAL(inbuf, smb_nts_TotalParameterCount) < total_parameter_count)
+ total_parameter_count = IVAL(inbuf, smb_nts_TotalParameterCount);
+ if (IVAL(inbuf, smb_nts_TotalDataCount) < total_data_count)
+ total_data_count = IVAL(inbuf, smb_nts_TotalDataCount);
+
+ parameter_count = IVAL(inbuf,smb_nts_ParameterCount);
+ parameter_offset = IVAL(inbuf, smb_nts_ParameterOffset);
+ parameter_displacement = IVAL(inbuf, smb_nts_ParameterDisplacement);
+ num_params_sofar += parameter_count;
+
+ data_count = IVAL(inbuf, smb_nts_DataCount);
+ data_displacement = IVAL(inbuf, smb_nts_DataDisplacement);
+ data_offset = IVAL(inbuf, smb_nts_DataOffset);
+ num_data_sofar += data_count;
+
+ if (num_params_sofar > total_parameter_count || num_data_sofar > total_data_count) {
+ DEBUG(0,("reply_nttrans2: data overflow in secondary nttrans packet"));
+ goto bad_param;
+ }
+
+ if (parameter_count) {
+ if (parameter_displacement + parameter_count >= total_parameter_count)
+ goto bad_param;
+ if ((parameter_displacement + parameter_count < parameter_displacement) ||
+ (parameter_displacement + parameter_count < parameter_count))
+ goto bad_param;
+ if (parameter_displacement > total_parameter_count)
+ goto bad_param;
+ if ((smb_base(inbuf) + parameter_offset + parameter_count >= inbuf + bufsize) ||
+ (smb_base(inbuf) + parameter_offset + parameter_count < smb_base(inbuf)))
+ goto bad_param;
+ if (parameter_displacement + params < params)
+ goto bad_param;
+
+ memcpy( &params[parameter_displacement], smb_base(inbuf) + parameter_offset, parameter_count);
+ }
+
+ if (data_count) {
+ if (data_displacement + data_count >= total_data_count)
+ goto bad_param;
+ if ((data_displacement + data_count < data_displacement) ||
+ (data_displacement + data_count < data_count))
+ goto bad_param;
+ if (data_displacement > total_data_count)
+ goto bad_param;
+ if ((smb_base(inbuf) + data_offset + data_count >= inbuf + bufsize) ||
+ (smb_base(inbuf) + data_offset + data_count < smb_base(inbuf)))
+ goto bad_param;
+ if (data_displacement + data < data)
+ goto bad_param;
+
+ memcpy( &data[data_displacement], smb_base(inbuf)+ data_offset, data_count);
+ }
+ }
+ }
+
+ if (Protocol >= PROTOCOL_NT1)
+ SSVAL(outbuf,smb_flg2,SVAL(outbuf,smb_flg2) | FLAGS2_IS_LONG_NAME);
+
+ /* Now we must call the relevant NT_TRANS function */
+ switch(function_code) {
+ case NT_TRANSACT_CREATE:
+ START_PROFILE_NESTED(NT_transact_create);
+ outsize = call_nt_transact_create(conn, inbuf, outbuf,
+ length, bufsize,
+ &setup, setup_count,
+ &params, total_parameter_count,
+ &data, total_data_count);
+ END_PROFILE_NESTED(NT_transact_create);
+ break;
+ case NT_TRANSACT_IOCTL:
+ START_PROFILE_NESTED(NT_transact_ioctl);
+ outsize = call_nt_transact_ioctl(conn, inbuf, outbuf,
+ length, bufsize,
+ &setup, setup_count,
+ &params, total_parameter_count,
+ &data, total_data_count);
+ END_PROFILE_NESTED(NT_transact_ioctl);
+ break;
+ case NT_TRANSACT_SET_SECURITY_DESC:
+ START_PROFILE_NESTED(NT_transact_set_security_desc);
+ outsize = call_nt_transact_set_security_desc(conn, inbuf, outbuf,
+ length, bufsize,
+ &setup, setup_count,
+ &params, total_parameter_count,
+ &data, total_data_count);
+ END_PROFILE_NESTED(NT_transact_set_security_desc);
+ break;
+ case NT_TRANSACT_NOTIFY_CHANGE:
+ START_PROFILE_NESTED(NT_transact_notify_change);
+ outsize = call_nt_transact_notify_change(conn, inbuf, outbuf,
+ length, bufsize,
+ &setup, setup_count,
+ &params, total_parameter_count,
+ &data, total_data_count);
+ END_PROFILE_NESTED(NT_transact_notify_change);
+ break;
+ case NT_TRANSACT_RENAME:
+ START_PROFILE_NESTED(NT_transact_rename);
+ outsize = call_nt_transact_rename(conn, inbuf, outbuf,
+ length, bufsize,
+ &setup, setup_count,
+ &params, total_parameter_count,
+ &data, total_data_count);
+ END_PROFILE_NESTED(NT_transact_rename);
+ break;
+
+ case NT_TRANSACT_QUERY_SECURITY_DESC:
+ START_PROFILE_NESTED(NT_transact_query_security_desc);
+ outsize = call_nt_transact_query_security_desc(conn, inbuf, outbuf,
+ length, bufsize,
+ &setup, setup_count,
+ &params, total_parameter_count,
+ &data, total_data_count);
+ END_PROFILE_NESTED(NT_transact_query_security_desc);
+ break;
+#ifdef HAVE_SYS_QUOTAS
+ case NT_TRANSACT_GET_USER_QUOTA:
+ START_PROFILE_NESTED(NT_transact_get_user_quota);
+ outsize = call_nt_transact_get_user_quota(conn, inbuf, outbuf,
+ length, bufsize,
+ &setup, setup_count,
+ &params, total_parameter_count,
+ &data, total_data_count);
+ END_PROFILE_NESTED(NT_transact_get_user_quota);
+ break;
+ case NT_TRANSACT_SET_USER_QUOTA:
+ START_PROFILE_NESTED(NT_transact_set_user_quota);
+ outsize = call_nt_transact_set_user_quota(conn, inbuf, outbuf,
+ length, bufsize,
+ &setup, setup_count,
+ &params, total_parameter_count,
+ &data, total_data_count);
+ END_PROFILE_NESTED(NT_transact_set_user_quota);
+ break;
+#endif /* HAVE_SYS_QUOTAS */
+ default:
+ /* Error in request */
+ DEBUG(0,("reply_nttrans: Unknown request %d in nttrans call\n", function_code));
+ SAFE_FREE(setup);
+ SAFE_FREE(params);
+ SAFE_FREE(data);
+ END_PROFILE(SMBnttrans);
+ srv_signing_trans_stop();
+ return ERROR_DOS(ERRSRV,ERRerror);
+ }
+
+ /* As we do not know how many data packets will need to be
+ returned here the various call_nt_transact_xxxx calls
+ must send their own. Thus a call_nt_transact_xxxx routine only
+ returns a value other than -1 when it wants to send
+ an error packet.
+ */
+
+ srv_signing_trans_stop();
+
+ SAFE_FREE(setup);
+ SAFE_FREE(params);
+ SAFE_FREE(data);
+ END_PROFILE(SMBnttrans);
+ return outsize; /* If a correct response was needed the call_nt_transact_xxxx
+ calls have already sent it. If outsize != -1 then it is
+ returning an error packet. */
+
+ bad_param:
+
+ srv_signing_trans_stop();
+ SAFE_FREE(params);
+ SAFE_FREE(data);
+ SAFE_FREE(setup);
+ END_PROFILE(SMBnttrans);
+ return ERROR_NT(NT_STATUS_INVALID_PARAMETER);
+}
diff --git a/source/smbd/open.c b/source/smbd/open.c
new file mode 100644
index 00000000000..8ab5dab6ac9
--- /dev/null
+++ b/source/smbd/open.c
@@ -0,0 +1,1462 @@
+/*
+ Unix SMB/CIFS implementation.
+ file opening and share modes
+ Copyright (C) Andrew Tridgell 1992-1998
+ Copyright (C) Jeremy Allison 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 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"
+
+extern userdom_struct current_user_info;
+extern uint16 global_oplock_port;
+extern uint16 global_smbpid;
+extern BOOL global_client_failed_oplock_break;
+
+/****************************************************************************
+ fd support routines - attempt to do a dos_open.
+****************************************************************************/
+
+static int fd_open(struct connection_struct *conn, const char *fname,
+ int flags, mode_t mode)
+{
+ int fd;
+#ifdef O_NOFOLLOW
+ if (!lp_symlinks(SNUM(conn)))
+ flags |= O_NOFOLLOW;
+#endif
+
+ fd = SMB_VFS_OPEN(conn,fname,flags,mode);
+
+ DEBUG(10,("fd_open: name %s, flags = 0%o mode = 0%o, fd = %d. %s\n", fname,
+ flags, (int)mode, fd, (fd == -1) ? strerror(errno) : "" ));
+
+ return fd;
+}
+
+/****************************************************************************
+ Close the file associated with a fsp.
+****************************************************************************/
+
+int fd_close(struct connection_struct *conn, files_struct *fsp)
+{
+ if (fsp->fd == -1)
+ return 0; /* what we used to call a stat open. */
+ return fd_close_posix(conn, fsp);
+}
+
+
+/****************************************************************************
+ Check a filename for the pipe string.
+****************************************************************************/
+
+static void check_for_pipe(const char *fname)
+{
+ /* special case of pipe opens */
+ char s[10];
+ StrnCpy(s,fname,sizeof(s)-1);
+ strlower_m(s);
+ if (strstr(s,"pipe/")) {
+ DEBUG(3,("Rejecting named pipe open for %s\n",fname));
+ unix_ERR_class = ERRSRV;
+ unix_ERR_code = ERRaccess;
+ unix_ERR_ntstatus = NT_STATUS_ACCESS_DENIED;
+ }
+}
+
+/****************************************************************************
+ Open a file.
+****************************************************************************/
+
+static BOOL open_file(files_struct *fsp,connection_struct *conn,
+ const char *fname,SMB_STRUCT_STAT *psbuf,int flags,mode_t mode, uint32 desired_access)
+{
+ extern struct current_user current_user;
+ int accmode = (flags & O_ACCMODE);
+ int local_flags = flags;
+
+ fsp->fd = -1;
+ fsp->oplock_type = NO_OPLOCK;
+ errno = EPERM;
+
+ /* Check permissions */
+
+ /*
+ * This code was changed after seeing a client open request
+ * containing the open mode of (DENY_WRITE/read-only) with
+ * the 'create if not exist' bit set. The previous code
+ * would fail to open the file read only on a read-only share
+ * as it was checking the flags parameter directly against O_RDONLY,
+ * this was failing as the flags parameter was set to O_RDONLY|O_CREAT.
+ * JRA.
+ */
+
+ if (!CAN_WRITE(conn)) {
+ /* It's a read-only share - fail if we wanted to write. */
+ if(accmode != O_RDONLY) {
+ DEBUG(3,("Permission denied opening %s\n",fname));
+ check_for_pipe(fname);
+ return False;
+ } else if(flags & O_CREAT) {
+ /* We don't want to write - but we must make sure that O_CREAT
+ doesn't create the file if we have write access into the
+ directory.
+ */
+ flags &= ~O_CREAT;
+ local_flags &= ~O_CREAT;
+ }
+ }
+
+ /*
+ * This little piece of insanity is inspired by the
+ * fact that an NT client can open a file for O_RDONLY,
+ * but set the create disposition to FILE_EXISTS_TRUNCATE.
+ * If the client *can* write to the file, then it expects to
+ * truncate the file, even though it is opening for readonly.
+ * Quicken uses this stupid trick in backup file creation...
+ * Thanks *greatly* to "David W. Chapman Jr." <dwcjr@inethouston.net>
+ * for helping track this one down. It didn't bite us in 2.0.x
+ * as we always opened files read-write in that release. JRA.
+ */
+
+ if ((accmode == O_RDONLY) && ((flags & O_TRUNC) == O_TRUNC)) {
+ DEBUG(10,("open_file: truncate requested on read-only open for file %s\n",fname ));
+ local_flags = (flags & ~O_ACCMODE)|O_RDWR;
+ }
+
+ if ((desired_access & (FILE_READ_DATA|FILE_WRITE_DATA|FILE_APPEND_DATA|FILE_EXECUTE)) ||
+ (local_flags & O_CREAT) || ((local_flags & O_TRUNC) == O_TRUNC) ) {
+
+ /*
+ * We can't actually truncate here as the file may be locked.
+ * open_file_shared will take care of the truncate later. JRA.
+ */
+
+ local_flags &= ~O_TRUNC;
+
+#if defined(O_NONBLOCK) && defined(S_ISFIFO)
+ /*
+ * We would block on opening a FIFO with no one else on the
+ * other end. Do what we used to do and add O_NONBLOCK to the
+ * open flags. JRA.
+ */
+
+ if (VALID_STAT(*psbuf) && S_ISFIFO(psbuf->st_mode))
+ local_flags |= O_NONBLOCK;
+#endif
+
+ /* Don't create files with Microsoft wildcard characters. */
+ if ((local_flags & O_CREAT) && !VALID_STAT(*psbuf) && ms_has_wild(fname)) {
+ unix_ERR_class = ERRDOS;
+ unix_ERR_code = ERRinvalidname;
+ unix_ERR_ntstatus = NT_STATUS_OBJECT_NAME_INVALID;
+ return False;
+ }
+
+ /* Actually do the open */
+ fsp->fd = fd_open(conn, fname, local_flags, mode);
+ if (fsp->fd == -1) {
+ DEBUG(3,("Error opening file %s (%s) (local_flags=%d) (flags=%d)\n",
+ fname,strerror(errno),local_flags,flags));
+ check_for_pipe(fname);
+ return False;
+ }
+
+ /* Inherit the ACL if the file was created. */
+ if ((local_flags & O_CREAT) && !VALID_STAT(*psbuf))
+ inherit_access_acl(conn, fname, mode);
+
+ } else
+ fsp->fd = -1; /* What we used to call a stat open. */
+
+ if (!VALID_STAT(*psbuf)) {
+ int ret;
+
+ if (fsp->fd == -1)
+ ret = SMB_VFS_STAT(conn, fname, psbuf);
+ else {
+ ret = SMB_VFS_FSTAT(fsp,fsp->fd,psbuf);
+ /* If we have an fd, this stat should succeed. */
+ if (ret == -1)
+ DEBUG(0,("Error doing fstat on open file %s (%s)\n", fname,strerror(errno) ));
+ }
+
+ /* For a non-io open, this stat failing means file not found. JRA */
+ if (ret == -1) {
+ fd_close(conn, fsp);
+ return False;
+ }
+ }
+
+ /*
+ * POSIX allows read-only opens of directories. We don't
+ * want to do this (we use a different code path for this)
+ * so catch a directory open and return an EISDIR. JRA.
+ */
+
+ if(S_ISDIR(psbuf->st_mode)) {
+ fd_close(conn, fsp);
+ errno = EISDIR;
+ return False;
+ }
+
+ fsp->mode = psbuf->st_mode;
+ fsp->inode = psbuf->st_ino;
+ fsp->dev = psbuf->st_dev;
+ fsp->vuid = current_user.vuid;
+ fsp->file_pid = global_smbpid;
+ fsp->size = psbuf->st_size;
+ fsp->can_lock = True;
+ fsp->can_read = ((flags & O_WRONLY)==0);
+ fsp->can_write = ((flags & (O_WRONLY|O_RDWR))!=0);
+ fsp->share_mode = 0;
+ fsp->desired_access = desired_access;
+ fsp->print_file = False;
+ fsp->modified = False;
+ fsp->oplock_type = NO_OPLOCK;
+ fsp->sent_oplock_break = NO_BREAK_SENT;
+ fsp->is_directory = False;
+ fsp->is_stat = False;
+ fsp->directory_delete_on_close = False;
+ string_set(&fsp->fsp_name,fname);
+ fsp->wcp = NULL; /* Write cache pointer. */
+
+ DEBUG(2,("%s opened file %s read=%s write=%s (numopen=%d)\n",
+ *current_user_info.smb_name ? current_user_info.smb_name : conn->user,fsp->fsp_name,
+ BOOLSTR(fsp->can_read), BOOLSTR(fsp->can_write),
+ conn->num_files_open + 1));
+
+ return True;
+}
+
+/****************************************************************************
+ C. Hoch 11/22/95
+ Helper for open_file_shared.
+ Truncate a file after checking locking; close file if locked.
+ **************************************************************************/
+
+static int truncate_unless_locked(struct connection_struct *conn, files_struct *fsp)
+{
+ SMB_BIG_UINT mask = (SMB_BIG_UINT)-1;
+
+ if (is_locked(fsp,fsp->conn,mask,0,WRITE_LOCK,True)){
+ errno = EACCES;
+ unix_ERR_class = ERRDOS;
+ unix_ERR_code = ERRlock;
+ unix_ERR_ntstatus = dos_to_ntstatus(ERRDOS, ERRlock);
+ return -1;
+ } else {
+ return SMB_VFS_FTRUNCATE(fsp,fsp->fd,0);
+ }
+}
+
+/*******************************************************************
+return True if the filename is one of the special executable types
+********************************************************************/
+static BOOL is_executable(const char *fname)
+{
+ if ((fname = strrchr_m(fname,'.'))) {
+ if (strequal(fname,".com") ||
+ strequal(fname,".dll") ||
+ strequal(fname,".exe") ||
+ strequal(fname,".sym")) {
+ return True;
+ }
+ }
+ return False;
+}
+
+enum {AFAIL,AREAD,AWRITE,AALL};
+
+/*******************************************************************
+reproduce the share mode access table
+this is horrendoously complex, and really can't be justified on any
+rational grounds except that this is _exactly_ what NT does. See
+the DENY1 and DENY2 tests in smbtorture for a comprehensive set of
+test routines.
+********************************************************************/
+static int access_table(int new_deny,int old_deny,int old_mode,
+ BOOL same_pid, BOOL isexe)
+{
+ if (new_deny == DENY_ALL || old_deny == DENY_ALL) return(AFAIL);
+
+ if (same_pid) {
+ if (isexe && old_mode == DOS_OPEN_RDONLY &&
+ old_deny == DENY_DOS && new_deny == DENY_READ) {
+ return AFAIL;
+ }
+ if (!isexe && old_mode == DOS_OPEN_RDONLY &&
+ old_deny == DENY_DOS && new_deny == DENY_DOS) {
+ return AREAD;
+ }
+ if (new_deny == DENY_FCB && old_deny == DENY_DOS) {
+ if (isexe) return AFAIL;
+ if (old_mode == DOS_OPEN_RDONLY) return AFAIL;
+ return AALL;
+ }
+ if (old_mode == DOS_OPEN_RDONLY && old_deny == DENY_DOS) {
+ if (new_deny == DENY_FCB || new_deny == DENY_READ) {
+ if (isexe) return AREAD;
+ return AFAIL;
+ }
+ }
+ if (old_deny == DENY_FCB) {
+ if (new_deny == DENY_DOS || new_deny == DENY_FCB) return AALL;
+ return AFAIL;
+ }
+ }
+
+ if (old_deny == DENY_DOS || new_deny == DENY_DOS ||
+ old_deny == DENY_FCB || new_deny == DENY_FCB) {
+ if (isexe) {
+ if (old_deny == DENY_FCB || new_deny == DENY_FCB) {
+ return AFAIL;
+ }
+ if (old_deny == DENY_DOS) {
+ if (new_deny == DENY_READ &&
+ (old_mode == DOS_OPEN_RDONLY ||
+ old_mode == DOS_OPEN_RDWR)) {
+ return AFAIL;
+ }
+ if (new_deny == DENY_WRITE &&
+ (old_mode == DOS_OPEN_WRONLY ||
+ old_mode == DOS_OPEN_RDWR)) {
+ return AFAIL;
+ }
+ return AALL;
+ }
+ if (old_deny == DENY_NONE) return AALL;
+ if (old_deny == DENY_READ) return AWRITE;
+ if (old_deny == DENY_WRITE) return AREAD;
+ }
+ /* it isn't a exe, dll, sym or com file */
+ if (old_deny == new_deny && same_pid)
+ return(AALL);
+
+ if (old_deny == DENY_READ || new_deny == DENY_READ) return AFAIL;
+ if (old_mode == DOS_OPEN_RDONLY) return(AREAD);
+
+ return(AFAIL);
+ }
+
+ switch (new_deny)
+ {
+ case DENY_WRITE:
+ if (old_deny==DENY_WRITE && old_mode==DOS_OPEN_RDONLY) return(AREAD);
+ if (old_deny==DENY_READ && old_mode==DOS_OPEN_RDONLY) return(AWRITE);
+ if (old_deny==DENY_NONE && old_mode==DOS_OPEN_RDONLY) return(AALL);
+ return(AFAIL);
+ case DENY_READ:
+ if (old_deny==DENY_WRITE && old_mode==DOS_OPEN_WRONLY) return(AREAD);
+ if (old_deny==DENY_READ && old_mode==DOS_OPEN_WRONLY) return(AWRITE);
+ if (old_deny==DENY_NONE && old_mode==DOS_OPEN_WRONLY) return(AALL);
+ return(AFAIL);
+ case DENY_NONE:
+ if (old_deny==DENY_WRITE) return(AREAD);
+ if (old_deny==DENY_READ) return(AWRITE);
+ if (old_deny==DENY_NONE) return(AALL);
+ return(AFAIL);
+ }
+ return(AFAIL);
+}
+
+
+/****************************************************************************
+check if we can open a file with a share mode
+****************************************************************************/
+
+static BOOL check_share_mode(connection_struct *conn, share_mode_entry *share, int share_mode, uint32 desired_access,
+ const char *fname, BOOL fcbopen, int *flags)
+{
+ int deny_mode = GET_DENY_MODE(share_mode);
+ int old_open_mode = GET_OPEN_MODE(share->share_mode);
+ int old_deny_mode = GET_DENY_MODE(share->share_mode);
+
+ /*
+ * share modes = false means don't bother to check for
+ * DENY mode conflict. This is a *really* bad idea :-). JRA.
+ */
+
+ if(!lp_share_modes(SNUM(conn)))
+ return True;
+
+ /*
+ * Don't allow any opens once the delete on close flag has been
+ * set.
+ */
+
+ if (GET_DELETE_ON_CLOSE_FLAG(share->share_mode)) {
+ DEBUG(5,("check_share_mode: Failing open on file %s as delete on close flag is set.\n",
+ fname ));
+ /* Use errno to map to correct error. */
+ unix_ERR_class = SMB_SUCCESS;
+ unix_ERR_code = 0;
+ unix_ERR_ntstatus = NT_STATUS_OK;
+ return False;
+ }
+
+ /* this is a nasty hack, but necessary until we rewrite our open
+ handling to use a NTCreateX call as the basic call.
+ NT may open a file with neither read nor write access, and in
+ this case it expects the open not to conflict with any
+ existing deny modes. This happens (for example) during a
+ "xcopy /o" where the second file descriptor is used for
+ ACL sets
+ (tridge)
+ */
+
+ /*
+ * This is a bit wierd - the test for desired access not having the
+ * critical bits seems seems odd. Firstly, if both opens have no
+ * critical bits then always ignore. Then check the "allow delete"
+ * then check for either. This probably isn't quite right yet but
+ * gets us much closer. JRA.
+ */
+
+ /*
+ * If desired_access doesn't contain READ_DATA,WRITE_DATA,APPEND_DATA or EXECUTE
+ * and the existing desired_acces then share modes don't conflict.
+ */
+
+ if ( !(desired_access & (FILE_READ_DATA|FILE_WRITE_DATA|FILE_APPEND_DATA|FILE_EXECUTE)) &&
+ !(share->desired_access & (FILE_READ_DATA|FILE_WRITE_DATA|FILE_APPEND_DATA|FILE_EXECUTE)) ) {
+
+ /*
+ * Wrinkle discovered by smbtorture....
+ * If both are non-io open and requester is asking for delete and current open has delete access
+ * but neither open has allowed file share delete then deny.... this is very strange and
+ * seems to be the only case in which non-io opens conflict. JRA.
+ */
+
+ if ((desired_access & DELETE_ACCESS) && (share->desired_access & DELETE_ACCESS) &&
+ (!GET_ALLOW_SHARE_DELETE(share->share_mode) || !GET_ALLOW_SHARE_DELETE(share_mode))) {
+ DEBUG(5,("check_share_mode: Failing open on file %s as delete access requests conflict.\n",
+ fname ));
+ unix_ERR_class = ERRDOS;
+ unix_ERR_code = ERRbadshare;
+ unix_ERR_ntstatus = NT_STATUS_SHARING_VIOLATION;
+
+ return False;
+ }
+
+ DEBUG(5,("check_share_mode: Allowing open on file %s as both desired access (0x%x) \
+and existing desired access (0x%x) are non-data opens\n",
+ fname, (unsigned int)desired_access, (unsigned int)share->desired_access ));
+ return True;
+ }
+
+ /*
+ * If delete access was requested and the existing share mode doesn't have
+ * ALLOW_SHARE_DELETE then deny.
+ */
+
+ if ((desired_access & DELETE_ACCESS) && !GET_ALLOW_SHARE_DELETE(share->share_mode)) {
+ DEBUG(5,("check_share_mode: Failing open on file %s as delete access requested and allow share delete not set.\n",
+ fname ));
+ unix_ERR_class = ERRDOS;
+ unix_ERR_code = ERRbadshare;
+ unix_ERR_ntstatus = NT_STATUS_SHARING_VIOLATION;
+
+ return False;
+ }
+
+ /*
+ * The inverse of the above.
+ * If delete access was granted and the new share mode doesn't have
+ * ALLOW_SHARE_DELETE then deny.
+ */
+
+ if ((share->desired_access & DELETE_ACCESS) && !GET_ALLOW_SHARE_DELETE(share_mode)) {
+ DEBUG(5,("check_share_mode: Failing open on file %s as delete access granted and allow share delete not requested.\n",
+ fname ));
+ unix_ERR_class = ERRDOS;
+ unix_ERR_code = ERRbadshare;
+ unix_ERR_ntstatus = NT_STATUS_SHARING_VIOLATION;
+
+ return False;
+ }
+
+ /*
+ * If desired_access doesn't contain READ_DATA,WRITE_DATA,APPEND_DATA or EXECUTE
+ * then share modes don't conflict. Likewise with existing desired access.
+ */
+
+ if ( !(desired_access & (FILE_READ_DATA|FILE_WRITE_DATA|FILE_APPEND_DATA|FILE_EXECUTE)) ||
+ !(share->desired_access & (FILE_READ_DATA|FILE_WRITE_DATA|FILE_APPEND_DATA|FILE_EXECUTE)) ) {
+ DEBUG(5,("check_share_mode: Allowing open on file %s as desired access (0x%x) doesn't conflict with\
+existing desired access (0x%x).\n", fname, (unsigned int)desired_access, (unsigned int)share->desired_access ));
+ return True;
+ }
+
+ {
+ int access_allowed = access_table(deny_mode,old_deny_mode,old_open_mode,
+ (share->pid == sys_getpid()),is_executable(fname));
+
+ if ((access_allowed == AFAIL) ||
+ (!fcbopen && (access_allowed == AREAD && *flags == O_RDWR)) ||
+ (access_allowed == AREAD && *flags != O_RDONLY) ||
+ (access_allowed == AWRITE && *flags != O_WRONLY)) {
+
+ DEBUG(2,("Share violation on file (%d,%d,%d,%d,%s,fcbopen = %d, flags = %d) = %d\n",
+ deny_mode,old_deny_mode,old_open_mode,
+ (int)share->pid,fname, fcbopen, *flags, access_allowed));
+
+ unix_ERR_class = ERRDOS;
+ unix_ERR_code = ERRbadshare;
+ unix_ERR_ntstatus = NT_STATUS_SHARING_VIOLATION;
+
+ return False;
+ }
+
+ if (access_allowed == AREAD)
+ *flags = O_RDONLY;
+
+ if (access_allowed == AWRITE)
+ *flags = O_WRONLY;
+
+ }
+
+ return True;
+}
+
+
+#if defined(DEVELOPER)
+static void validate_my_share_entries(int num, share_mode_entry *share_entry)
+{
+ files_struct *fsp;
+
+ if (share_entry->pid != sys_getpid())
+ return;
+
+ fsp = file_find_dif(share_entry->dev, share_entry->inode, share_entry->share_file_id);
+ if (!fsp) {
+ DEBUG(0,("validate_my_share_entries: PANIC : %s\n", share_mode_str(num, share_entry) ));
+ smb_panic("validate_my_share_entries: Cannot match a share entry with an open file\n");
+ }
+
+ if (((uint16)fsp->oplock_type) != share_entry->op_type) {
+ pstring str;
+ DEBUG(0,("validate_my_share_entries: PANIC : %s\n", share_mode_str(num, share_entry) ));
+ slprintf(str, sizeof(str)-1, "validate_my_share_entries: file %s, oplock_type = 0x%x, op_type = 0x%x\n",
+ fsp->fsp_name, (unsigned int)fsp->oplock_type, (unsigned int)share_entry->op_type );
+ smb_panic(str);
+ }
+}
+#endif
+
+/****************************************************************************
+ Deal with open deny mode and oplock break processing.
+ Invarient: Share mode must be locked on entry and exit.
+ Returns -1 on error, or number of share modes on success (may be zero).
+****************************************************************************/
+
+static int open_mode_check(connection_struct *conn, const char *fname, SMB_DEV_T dev,
+ SMB_INO_T inode,
+ uint32 desired_access,
+ int share_mode, int *p_flags, int *p_oplock_request,
+ BOOL *p_all_current_opens_are_level_II)
+{
+ int i;
+ int num_share_modes;
+ int oplock_contention_count = 0;
+ share_mode_entry *old_shares = 0;
+ BOOL fcbopen = False;
+ BOOL broke_oplock;
+
+ if(GET_OPEN_MODE(share_mode) == DOS_OPEN_FCB)
+ fcbopen = True;
+
+ num_share_modes = get_share_modes(conn, dev, inode, &old_shares);
+
+ if(num_share_modes == 0)
+ return 0;
+
+ /*
+ * Check if the share modes will give us access.
+ */
+
+ do {
+ share_mode_entry broken_entry;
+
+ broke_oplock = False;
+ *p_all_current_opens_are_level_II = True;
+
+ for(i = 0; i < num_share_modes; i++) {
+ share_mode_entry *share_entry = &old_shares[i];
+
+#if defined(DEVELOPER)
+ validate_my_share_entries(i, share_entry);
+#endif
+
+ /*
+ * By observation of NetBench, oplocks are broken *before* share
+ * modes are checked. This allows a file to be closed by the client
+ * if the share mode would deny access and the client has an oplock.
+ * Check if someone has an oplock on this file. If so we must break
+ * it before continuing.
+ */
+
+ if((*p_oplock_request && EXCLUSIVE_OPLOCK_TYPE(share_entry->op_type)) ||
+ (!*p_oplock_request && (share_entry->op_type != NO_OPLOCK))) {
+
+ BOOL opb_ret;
+
+ DEBUG(5,("open_mode_check: oplock_request = %d, breaking oplock (%x) on file %s, \
+dev = %x, inode = %.0f\n", *p_oplock_request, share_entry->op_type, fname, (unsigned int)dev, (double)inode));
+
+ /* Ensure the reply for the open uses the correct sequence number. */
+ /* This isn't a real deferred packet as it's response will also increment
+ * the sequence.
+ */
+ srv_defer_sign_response(get_current_mid());
+
+ /* Oplock break - unlock to request it. */
+ unlock_share_entry(conn, dev, inode);
+
+ opb_ret = request_oplock_break(share_entry, False);
+
+ /* Now relock. */
+ lock_share_entry(conn, dev, inode);
+
+ if(opb_ret == False) {
+ DEBUG(0,("open_mode_check: FAILED when breaking oplock (%x) on file %s, \
+dev = %x, inode = %.0f\n", old_shares[i].op_type, fname, (unsigned int)dev, (double)inode));
+ SAFE_FREE(old_shares);
+ errno = EACCES;
+ unix_ERR_class = ERRDOS;
+ unix_ERR_code = ERRbadshare;
+ unix_ERR_ntstatus = NT_STATUS_SHARING_VIOLATION;
+ return -1;
+ }
+
+ broke_oplock = True;
+ broken_entry = *share_entry;
+ break;
+
+ } else if (!LEVEL_II_OPLOCK_TYPE(share_entry->op_type)) {
+ *p_all_current_opens_are_level_II = False;
+ }
+
+ /* someone else has a share lock on it, check to see if we can too */
+ if (!check_share_mode(conn, share_entry, share_mode, desired_access,
+ fname, fcbopen, p_flags)) {
+ SAFE_FREE(old_shares);
+ errno = EACCES;
+ return -1;
+ }
+
+ } /* end for */
+
+ if(broke_oplock) {
+ SAFE_FREE(old_shares);
+ num_share_modes = get_share_modes(conn, dev, inode, &old_shares);
+ oplock_contention_count++;
+
+ /* Paranoia check that this is no longer an exlusive entry. */
+ for(i = 0; i < num_share_modes; i++) {
+ share_mode_entry *share_entry = &old_shares[i];
+
+ if (share_modes_identical(&broken_entry, share_entry) &&
+ EXCLUSIVE_OPLOCK_TYPE(share_entry->op_type) ) {
+
+ /*
+ * This should not happen. The target left this oplock
+ * as exlusive.... The process *must* be dead....
+ */
+
+ DEBUG(0,("open_mode_check: exlusive oplock left by process %d after break ! For file %s, \
+dev = %x, inode = %.0f. Deleting it to continue...\n", (int)broken_entry.pid, fname, (unsigned int)dev, (double)inode));
+
+ if (process_exists(broken_entry.pid)) {
+ DEBUG(0,("open_mode_check: Existent process %lu left active oplock.\n",
+ (unsigned long)broken_entry.pid ));
+ }
+
+ if (del_share_entry(dev, inode, &broken_entry, NULL) == -1) {
+ errno = EACCES;
+ unix_ERR_class = ERRDOS;
+ unix_ERR_code = ERRbadshare;
+ unix_ERR_ntstatus = NT_STATUS_SHARING_VIOLATION;
+ return -1;
+ }
+
+ /*
+ * We must reload the share modes after deleting the
+ * other process's entry.
+ */
+
+ SAFE_FREE(old_shares);
+ num_share_modes = get_share_modes(conn, dev, inode, &old_shares);
+ break;
+ }
+ } /* end for paranoia... */
+ } /* end if broke_oplock */
+
+ } while(broke_oplock);
+
+ if(old_shares != 0)
+ SAFE_FREE(old_shares);
+
+ /*
+ * Refuse to grant an oplock in case the contention limit is
+ * reached when going through the lock list multiple times.
+ */
+
+ if(oplock_contention_count >= lp_oplock_contention_limit(SNUM(conn))) {
+ *p_oplock_request = 0;
+ DEBUG(4,("open_mode_check: oplock contention = %d. Not granting oplock.\n",
+ oplock_contention_count ));
+ }
+
+ return num_share_modes;
+}
+
+/****************************************************************************
+set a kernel flock on a file for NFS interoperability
+this requires a patch to Linux
+****************************************************************************/
+static void kernel_flock(files_struct *fsp, int deny_mode)
+{
+#if HAVE_KERNEL_SHARE_MODES
+ int kernel_mode = 0;
+ if (deny_mode == DENY_READ) kernel_mode = LOCK_MAND|LOCK_WRITE;
+ else if (deny_mode == DENY_WRITE) kernel_mode = LOCK_MAND|LOCK_READ;
+ else if (deny_mode == DENY_ALL) kernel_mode = LOCK_MAND;
+ if (kernel_mode) flock(fsp->fd, kernel_mode);
+#endif
+ ;;
+}
+
+
+static BOOL open_match_attributes(connection_struct *conn, const char *path, uint32 old_dos_mode, uint32 new_dos_mode,
+ mode_t existing_mode, mode_t new_mode, mode_t *returned_mode)
+{
+ uint32 noarch_old_dos_mode, noarch_new_dos_mode;
+
+ noarch_old_dos_mode = (old_dos_mode & ~FILE_ATTRIBUTE_ARCHIVE);
+ noarch_new_dos_mode = (new_dos_mode & ~FILE_ATTRIBUTE_ARCHIVE);
+
+ if((noarch_old_dos_mode == 0 && noarch_new_dos_mode != 0) ||
+ (noarch_old_dos_mode != 0 && ((noarch_old_dos_mode & noarch_new_dos_mode) == noarch_old_dos_mode)))
+ *returned_mode = new_mode;
+ else
+ *returned_mode = (mode_t)0;
+
+ DEBUG(10,("open_match_attributes: file %s old_dos_mode = 0x%x, existing_mode = 0%o, new_dos_mode = 0x%x returned_mode = 0%o\n",
+ path,
+ old_dos_mode, (unsigned int)existing_mode, new_dos_mode, (unsigned int)*returned_mode ));
+
+ /* If we're mapping SYSTEM and HIDDEN ensure they match. */
+ if (lp_map_system(SNUM(conn)) || lp_store_dos_attributes(SNUM(conn))) {
+ if ((old_dos_mode & FILE_ATTRIBUTE_SYSTEM) && !(new_dos_mode & FILE_ATTRIBUTE_SYSTEM))
+ return False;
+ }
+ if (lp_map_hidden(SNUM(conn)) || lp_store_dos_attributes(SNUM(conn))) {
+ if ((old_dos_mode & FILE_ATTRIBUTE_HIDDEN) && !(new_dos_mode & FILE_ATTRIBUTE_HIDDEN))
+ return False;
+ }
+ return True;
+}
+
+/****************************************************************************
+ Open a file with a share mode.
+****************************************************************************/
+
+files_struct *open_file_shared(connection_struct *conn,char *fname, SMB_STRUCT_STAT *psbuf,
+ int share_mode,int ofun, uint32 new_dos_mode, int oplock_request,
+ int *Access,int *action)
+{
+ return open_file_shared1(conn, fname, psbuf, 0, share_mode, ofun, new_dos_mode,
+ oplock_request, Access, action);
+}
+
+/****************************************************************************
+ Open a file with a share mode.
+****************************************************************************/
+
+files_struct *open_file_shared1(connection_struct *conn,char *fname, SMB_STRUCT_STAT *psbuf,
+ uint32 desired_access,
+ int share_mode,int ofun, uint32 new_dos_mode,
+ int oplock_request,
+ int *Access,int *paction)
+{
+ int flags=0;
+ int flags2=0;
+ int deny_mode = GET_DENY_MODE(share_mode);
+ BOOL allow_share_delete = GET_ALLOW_SHARE_DELETE(share_mode);
+ BOOL delete_on_close = GET_DELETE_ON_CLOSE_FLAG(share_mode);
+ BOOL file_existed = VALID_STAT(*psbuf);
+ BOOL fcbopen = False;
+ BOOL def_acl = False;
+ SMB_DEV_T dev = 0;
+ SMB_INO_T inode = 0;
+ int num_share_modes = 0;
+ BOOL all_current_opens_are_level_II = False;
+ BOOL fsp_open = False;
+ files_struct *fsp = NULL;
+ int open_mode=0;
+ uint16 port = 0;
+ mode_t new_mode = (mode_t)0;
+ int action;
+ uint32 existing_dos_mode = 0;
+ /* We add aARCH to this as this mode is only used if the file is created new. */
+ mode_t mode = unix_mode(conn,new_dos_mode | aARCH,fname);
+
+ if (conn->printer) {
+ /* printers are handled completely differently. Most of the passed parameters are
+ ignored */
+ if (Access)
+ *Access = DOS_OPEN_WRONLY;
+ if (action)
+ *paction = FILE_WAS_CREATED;
+ return print_fsp_open(conn, fname);
+ }
+
+ fsp = file_new(conn);
+ if(!fsp)
+ return NULL;
+
+ DEBUG(10,("open_file_shared: fname = %s, dos_attrs = %x, share_mode = %x, ofun = %x, mode = %o, oplock request = %d\n",
+ fname, new_dos_mode, share_mode, ofun, (int)mode, oplock_request ));
+
+ if (!check_name(fname,conn)) {
+ file_free(fsp);
+ return NULL;
+ }
+
+ new_dos_mode &= SAMBA_ATTRIBUTES_MASK;
+ if (file_existed) {
+ existing_dos_mode = dos_mode(conn, fname, psbuf);
+ }
+
+ /* ignore any oplock requests if oplocks are disabled */
+ if (!lp_oplocks(SNUM(conn)) || global_client_failed_oplock_break) {
+ oplock_request = 0;
+ }
+
+ /* this is for OS/2 EAs - try and say we don't support them */
+ if (strstr(fname,".+,;=[].")) {
+ unix_ERR_class = ERRDOS;
+ /* OS/2 Workplace shell fix may be main code stream in a later release. */
+#if 1 /* OS2_WPS_FIX - Recent versions of OS/2 need this. */
+ unix_ERR_code = ERRcannotopen;
+#else /* OS2_WPS_FIX */
+ unix_ERR_code = ERROR_EAS_NOT_SUPPORTED;
+#endif /* OS2_WPS_FIX */
+
+ DEBUG(5,("open_file_shared: OS/2 EA's are not supported.\n"));
+ file_free(fsp);
+ return NULL;
+ }
+
+ if ((GET_FILE_OPEN_DISPOSITION(ofun) == FILE_EXISTS_FAIL) && file_existed) {
+ DEBUG(5,("open_file_shared: create new requested for file %s and file already exists.\n",
+ fname ));
+ file_free(fsp);
+ if (S_ISDIR(psbuf->st_mode)) {
+ errno = EISDIR;
+ } else {
+ errno = EEXIST;
+ }
+ return NULL;
+ }
+
+ if (CAN_WRITE(conn) && (GET_FILE_CREATE_DISPOSITION(ofun) == FILE_CREATE_IF_NOT_EXIST))
+ flags2 |= O_CREAT;
+
+ if (CAN_WRITE(conn) && (GET_FILE_OPEN_DISPOSITION(ofun) == FILE_EXISTS_TRUNCATE))
+ flags2 |= O_TRUNC;
+
+ /* We only care about matching attributes on file exists and truncate. */
+ if (file_existed && (GET_FILE_OPEN_DISPOSITION(ofun) == FILE_EXISTS_TRUNCATE)) {
+ if (!open_match_attributes(conn, fname, existing_dos_mode, new_dos_mode,
+ psbuf->st_mode, mode, &new_mode)) {
+ DEBUG(5,("open_file_shared: attributes missmatch for file %s (%x %x) (0%o, 0%o)\n",
+ fname, existing_dos_mode, new_dos_mode,
+ (int)psbuf->st_mode, (int)mode ));
+ file_free(fsp);
+ errno = EACCES;
+ return NULL;
+ }
+ }
+
+ if (GET_FILE_OPEN_DISPOSITION(ofun) == FILE_EXISTS_FAIL)
+ flags2 |= O_EXCL;
+
+ /* note that we ignore the append flag as
+ append does not mean the same thing under dos and unix */
+
+ switch (GET_OPEN_MODE(share_mode)) {
+ case DOS_OPEN_WRONLY:
+ flags = O_WRONLY;
+ if (desired_access == 0)
+ desired_access = FILE_WRITE_DATA;
+ break;
+ case DOS_OPEN_FCB:
+ fcbopen = True;
+ flags = O_RDWR;
+ if (desired_access == 0)
+ desired_access = FILE_READ_DATA|FILE_WRITE_DATA;
+ break;
+ case DOS_OPEN_RDWR:
+ flags = O_RDWR;
+ if (desired_access == 0)
+ desired_access = FILE_READ_DATA|FILE_WRITE_DATA;
+ break;
+ default:
+ flags = O_RDONLY;
+ if (desired_access == 0)
+ desired_access = FILE_READ_DATA;
+ break;
+ }
+
+#if defined(O_SYNC)
+ if (GET_FILE_SYNC_OPENMODE(share_mode)) {
+ flags2 |= O_SYNC;
+ }
+#endif /* O_SYNC */
+
+ if (flags != O_RDONLY && file_existed &&
+ (!CAN_WRITE(conn) || IS_DOS_READONLY(existing_dos_mode))) {
+ if (!fcbopen) {
+ DEBUG(5,("open_file_shared: read/write access requested for file %s on read only %s\n",
+ fname, !CAN_WRITE(conn) ? "share" : "file" ));
+ file_free(fsp);
+ errno = EACCES;
+ return NULL;
+ }
+ flags = O_RDONLY;
+ }
+
+ if (deny_mode > DENY_NONE && deny_mode!=DENY_FCB) {
+ DEBUG(2,("Invalid deny mode %d on file %s\n",deny_mode,fname));
+ file_free(fsp);
+ errno = EINVAL;
+ return NULL;
+ }
+
+ if (file_existed) {
+
+ dev = psbuf->st_dev;
+ inode = psbuf->st_ino;
+
+ lock_share_entry(conn, dev, inode);
+
+ num_share_modes = open_mode_check(conn, fname, dev, inode,
+ desired_access,
+ share_mode,
+ &flags, &oplock_request, &all_current_opens_are_level_II);
+ if(num_share_modes == -1) {
+
+ /*
+ * This next line is a subtlety we need for MS-Access. If a file open will
+ * fail due to share permissions and also for security (access)
+ * reasons, we need to return the access failed error, not the
+ * share error. This means we must attempt to open the file anyway
+ * in order to get the UNIX access error - even if we're going to
+ * fail the open for share reasons. This is bad, as we're burning
+ * another fd if there are existing locks but there's nothing else
+ * we can do. We also ensure we're not going to create or tuncate
+ * the file as we only want an access decision at this stage. JRA.
+ */
+ errno = 0;
+ fsp_open = open_file(fsp,conn,fname,psbuf,
+ flags|(flags2&~(O_TRUNC|O_CREAT)),mode,desired_access);
+
+ DEBUG(4,("open_file_shared : share_mode deny - calling open_file with \
+flags=0x%X flags2=0x%X mode=0%o returned %d\n",
+ flags,(flags2&~(O_TRUNC|O_CREAT)),(int)mode,(int)fsp_open ));
+
+ if (!fsp_open && errno) {
+ unix_ERR_class = ERRDOS;
+ unix_ERR_code = ERRnoaccess;
+ unix_ERR_ntstatus = NT_STATUS_ACCESS_DENIED;
+ }
+
+ unlock_share_entry(conn, dev, inode);
+ if (fsp_open)
+ fd_close(conn, fsp);
+ file_free(fsp);
+ return NULL;
+ }
+
+ /*
+ * We exit this block with the share entry *locked*.....
+ */
+ }
+
+ /*
+ * Ensure we pay attention to default ACLs on directories if required.
+ */
+
+ if ((flags2 & O_CREAT) && lp_inherit_acls(SNUM(conn)) &&
+ (def_acl = directory_has_default_acl(conn, parent_dirname(fname))))
+ mode = 0777;
+
+ DEBUG(4,("calling open_file with flags=0x%X flags2=0x%X mode=0%o\n",
+ flags,flags2,(int)mode));
+
+ /*
+ * open_file strips any O_TRUNC flags itself.
+ */
+
+ fsp_open = open_file(fsp,conn,fname,psbuf,flags|flags2,mode,desired_access);
+
+ if (!fsp_open && (flags == O_RDWR) && (errno != ENOENT) && fcbopen) {
+ if((fsp_open = open_file(fsp,conn,fname,psbuf,O_RDONLY,mode,desired_access)) == True)
+ flags = O_RDONLY;
+ }
+
+ if (!fsp_open) {
+ if(file_existed)
+ unlock_share_entry(conn, dev, inode);
+ file_free(fsp);
+ return NULL;
+ }
+
+ /*
+ * Deal with the race condition where two smbd's detect the file doesn't
+ * exist and do the create at the same time. One of them will win and
+ * set a share mode, the other (ie. this one) should check if the
+ * requested share mode for this create is allowed.
+ */
+
+ if (!file_existed) {
+
+ /*
+ * Now the file exists and fsp is successfully opened,
+ * fsp->dev and fsp->inode are valid and should replace the
+ * dev=0,inode=0 from a non existent file. Spotted by
+ * Nadav Danieli <nadavd@exanet.com>. JRA.
+ */
+
+ dev = fsp->dev;
+ inode = fsp->inode;
+
+ lock_share_entry_fsp(fsp);
+
+ num_share_modes = open_mode_check(conn, fname, dev, inode,
+ desired_access,
+ share_mode,
+ &flags, &oplock_request, &all_current_opens_are_level_II);
+
+ if(num_share_modes == -1) {
+ unlock_share_entry_fsp(fsp);
+ fd_close(conn,fsp);
+ file_free(fsp);
+ return NULL;
+ }
+
+ /*
+ * If there are any share modes set then the file *did*
+ * exist. Ensure we return the correct value for action.
+ */
+
+ if (num_share_modes > 0)
+ file_existed = True;
+
+ /*
+ * We exit this block with the share entry *locked*.....
+ */
+ }
+
+ /* note that we ignore failure for the following. It is
+ basically a hack for NFS, and NFS will never set one of
+ these only read them. Nobody but Samba can ever set a deny
+ mode and we have already checked our more authoritative
+ locking database for permission to set this deny mode. If
+ the kernel refuses the operations then the kernel is wrong */
+ kernel_flock(fsp, deny_mode);
+
+ /*
+ * At this point onwards, we can guarentee that the share entry
+ * is locked, whether we created the file or not, and that the
+ * deny mode is compatible with all current opens.
+ */
+
+ /*
+ * If requested, truncate the file.
+ */
+
+ if (flags2&O_TRUNC) {
+ /*
+ * We are modifing the file after open - update the stat struct..
+ */
+ if ((truncate_unless_locked(conn,fsp) == -1) || (SMB_VFS_FSTAT(fsp,fsp->fd,psbuf)==-1)) {
+ unlock_share_entry_fsp(fsp);
+ fd_close(conn,fsp);
+ file_free(fsp);
+ return NULL;
+ }
+ }
+
+ switch (flags) {
+ case O_RDONLY:
+ open_mode = DOS_OPEN_RDONLY;
+ break;
+ case O_RDWR:
+ open_mode = DOS_OPEN_RDWR;
+ break;
+ case O_WRONLY:
+ open_mode = DOS_OPEN_WRONLY;
+ break;
+ }
+
+ fsp->share_mode = SET_DENY_MODE(deny_mode) |
+ SET_OPEN_MODE(open_mode) |
+ SET_ALLOW_SHARE_DELETE(allow_share_delete);
+
+ DEBUG(10,("open_file_shared : share_mode = %x\n", fsp->share_mode ));
+
+ if (Access) {
+ (*Access) = open_mode;
+ }
+
+ if (file_existed && !(flags2 & O_TRUNC))
+ action = FILE_WAS_OPENED;
+ if (file_existed && (flags2 & O_TRUNC))
+ action = FILE_WAS_OVERWRITTEN;
+ if (!file_existed)
+ action = FILE_WAS_CREATED;
+
+ if (paction) {
+ *paction = action;
+ }
+
+ /*
+ * Setup the oplock info in both the shared memory and
+ * file structs.
+ */
+
+ if(oplock_request && (num_share_modes == 0) &&
+ !IS_VETO_OPLOCK_PATH(conn,fname) && set_file_oplock(fsp, oplock_request) ) {
+ port = global_oplock_port;
+ } else if (oplock_request && all_current_opens_are_level_II) {
+ port = global_oplock_port;
+ oplock_request = LEVEL_II_OPLOCK;
+ set_file_oplock(fsp, oplock_request);
+ } else {
+ port = 0;
+ oplock_request = 0;
+ }
+
+ set_share_mode(fsp, port, oplock_request);
+
+ if (delete_on_close) {
+ NTSTATUS result = set_delete_on_close_internal(fsp, delete_on_close);
+
+ if (NT_STATUS_V(result) != NT_STATUS_V(NT_STATUS_OK)) {
+ /* Remember to delete the mode we just added. */
+ del_share_mode(fsp, NULL);
+ unlock_share_entry_fsp(fsp);
+ fd_close(conn,fsp);
+ file_free(fsp);
+ return NULL;
+ }
+ }
+
+ if (action == FILE_WAS_OVERWRITTEN || action == FILE_WAS_CREATED) {
+ /* Files should be initially set as archive */
+ if (lp_map_archive(SNUM(conn)) || lp_store_dos_attributes(SNUM(conn))) {
+ file_set_dosmode(conn, fname, new_dos_mode | aARCH, NULL);
+ }
+ }
+
+ /*
+ * Take care of inherited ACLs on created files - if default ACL not
+ * selected.
+ */
+
+ if (!file_existed && !def_acl) {
+
+ int saved_errno = errno; /* We might get ENOSYS in the next call.. */
+
+ if (SMB_VFS_FCHMOD_ACL(fsp, fsp->fd, mode) == -1 && errno == ENOSYS)
+ errno = saved_errno; /* Ignore ENOSYS */
+
+ } else if (new_mode) {
+
+ int ret = -1;
+
+ /* Attributes need changing. File already existed. */
+
+ {
+ int saved_errno = errno; /* We might get ENOSYS in the next call.. */
+ ret = SMB_VFS_FCHMOD_ACL(fsp, fsp->fd, new_mode);
+
+ if (ret == -1 && errno == ENOSYS) {
+ errno = saved_errno; /* Ignore ENOSYS */
+ } else {
+ DEBUG(5, ("open_file_shared: failed to reset attributes of file %s to 0%o\n",
+ fname, (int)new_mode));
+ ret = 0; /* Don't do the fchmod below. */
+ }
+ }
+
+ if ((ret == -1) && (SMB_VFS_FCHMOD(fsp, fsp->fd, new_mode) == -1))
+ DEBUG(5, ("open_file_shared: failed to reset attributes of file %s to 0%o\n",
+ fname, (int)new_mode));
+ }
+
+ unlock_share_entry_fsp(fsp);
+
+ conn->num_files_open++;
+
+ return fsp;
+}
+
+/****************************************************************************
+ Open a file for for write to ensure that we can fchmod it.
+****************************************************************************/
+
+files_struct *open_file_fchmod(connection_struct *conn, const char *fname, SMB_STRUCT_STAT *psbuf)
+{
+ files_struct *fsp = NULL;
+ BOOL fsp_open;
+
+ if (!VALID_STAT(*psbuf))
+ return NULL;
+
+ fsp = file_new(conn);
+ if(!fsp)
+ return NULL;
+
+ /* note! we must use a non-zero desired access or we don't get
+ a real file descriptor. Oh what a twisted web we weave. */
+ fsp_open = open_file(fsp,conn,fname,psbuf,O_WRONLY,0,FILE_WRITE_DATA);
+
+ /*
+ * This is not a user visible file open.
+ * Don't set a share mode and don't increment
+ * the conn->num_files_open.
+ */
+
+ if (!fsp_open) {
+ file_free(fsp);
+ return NULL;
+ }
+
+ return fsp;
+}
+
+/****************************************************************************
+ Close the fchmod file fd - ensure no locks are lost.
+****************************************************************************/
+
+int close_file_fchmod(files_struct *fsp)
+{
+ int ret = fd_close(fsp->conn, fsp);
+ file_free(fsp);
+ return ret;
+}
+
+/****************************************************************************
+ Open a directory from an NT SMB call.
+****************************************************************************/
+
+files_struct *open_directory(connection_struct *conn, char *fname, SMB_STRUCT_STAT *psbuf,
+ uint32 desired_access, int share_mode, int smb_ofun, int *action)
+{
+ extern struct current_user current_user;
+ BOOL got_stat = False;
+ files_struct *fsp = file_new(conn);
+ BOOL delete_on_close = GET_DELETE_ON_CLOSE_FLAG(share_mode);
+
+ if(!fsp)
+ return NULL;
+
+ if (VALID_STAT(*psbuf))
+ got_stat = True;
+
+ if (got_stat && (GET_FILE_OPEN_DISPOSITION(smb_ofun) == FILE_EXISTS_FAIL)) {
+ file_free(fsp);
+ errno = EEXIST; /* Setup so correct error is returned to client. */
+ return NULL;
+ }
+
+ if (GET_FILE_CREATE_DISPOSITION(smb_ofun) == FILE_CREATE_IF_NOT_EXIST) {
+
+ if (got_stat) {
+
+ if(!S_ISDIR(psbuf->st_mode)) {
+ DEBUG(0,("open_directory: %s is not a directory !\n", fname ));
+ file_free(fsp);
+ errno = EACCES;
+ return NULL;
+ }
+ *action = FILE_WAS_OPENED;
+
+ } else {
+
+ /*
+ * Try and create the directory.
+ */
+
+ if(!CAN_WRITE(conn)) {
+ DEBUG(2,("open_directory: failing create on read-only share\n"));
+ file_free(fsp);
+ errno = EACCES;
+ return NULL;
+ }
+
+ if (ms_has_wild(fname)) {
+ file_free(fsp);
+ DEBUG(5,("open_directory: failing create on filename %s with wildcards\n", fname));
+ unix_ERR_class = ERRDOS;
+ unix_ERR_code = ERRinvalidname;
+ unix_ERR_ntstatus = NT_STATUS_OBJECT_NAME_INVALID;
+ return NULL;
+ }
+
+ if( strchr_m(fname, ':')) {
+ file_free(fsp);
+ DEBUG(5,("open_directory: failing create on filename %s with colon in name\n", fname));
+ unix_ERR_class = ERRDOS;
+ unix_ERR_code = ERRinvalidname;
+ unix_ERR_ntstatus = NT_STATUS_NOT_A_DIRECTORY;
+ return NULL;
+ }
+
+ if(vfs_MkDir(conn,fname, unix_mode(conn,aDIR, fname)) < 0) {
+ DEBUG(2,("open_directory: unable to create %s. Error was %s\n",
+ fname, strerror(errno) ));
+ file_free(fsp);
+ return NULL;
+ }
+
+ if(SMB_VFS_STAT(conn,fname, psbuf) != 0) {
+ file_free(fsp);
+ return NULL;
+ }
+
+ *action = FILE_WAS_CREATED;
+
+ }
+ } else {
+
+ /*
+ * Don't create - just check that it *was* a directory.
+ */
+
+ if(!got_stat) {
+ DEBUG(3,("open_directory: unable to stat name = %s. Error was %s\n",
+ fname, strerror(errno) ));
+ file_free(fsp);
+ return NULL;
+ }
+
+ if(!S_ISDIR(psbuf->st_mode)) {
+ DEBUG(0,("open_directory: %s is not a directory !\n", fname ));
+ file_free(fsp);
+ return NULL;
+ }
+
+ *action = FILE_WAS_OPENED;
+ }
+
+ DEBUG(5,("open_directory: opening directory %s\n", fname));
+
+ /*
+ * Setup the files_struct for it.
+ */
+
+ fsp->mode = psbuf->st_mode;
+ fsp->inode = psbuf->st_ino;
+ fsp->dev = psbuf->st_dev;
+ fsp->size = psbuf->st_size;
+ fsp->vuid = current_user.vuid;
+ fsp->file_pid = global_smbpid;
+ fsp->can_lock = True;
+ fsp->can_read = False;
+ fsp->can_write = False;
+ fsp->share_mode = share_mode;
+ fsp->desired_access = desired_access;
+ fsp->print_file = False;
+ fsp->modified = False;
+ fsp->oplock_type = NO_OPLOCK;
+ fsp->sent_oplock_break = NO_BREAK_SENT;
+ fsp->is_directory = True;
+ fsp->is_stat = False;
+ fsp->directory_delete_on_close = False;
+ string_set(&fsp->fsp_name,fname);
+
+ if (delete_on_close) {
+ NTSTATUS result = set_delete_on_close_internal(fsp, delete_on_close);
+
+ if (NT_STATUS_V(result) != NT_STATUS_V(NT_STATUS_OK)) {
+ file_free(fsp);
+ return NULL;
+ }
+ }
+ conn->num_files_open++;
+
+ return fsp;
+}
+
+/****************************************************************************
+ Open a pseudo-file (no locking checks - a 'stat' open).
+****************************************************************************/
+
+files_struct *open_file_stat(connection_struct *conn, char *fname, SMB_STRUCT_STAT *psbuf)
+{
+ extern struct current_user current_user;
+ files_struct *fsp = NULL;
+
+ if (!VALID_STAT(*psbuf))
+ return NULL;
+
+ /* Can't 'stat' open directories. */
+ if(S_ISDIR(psbuf->st_mode))
+ return NULL;
+
+ fsp = file_new(conn);
+ if(!fsp)
+ return NULL;
+
+ DEBUG(5,("open_file_stat: 'opening' file %s\n", fname));
+
+ /*
+ * Setup the files_struct for it.
+ */
+
+ fsp->mode = psbuf->st_mode;
+ /*
+ * Don't store dev or inode, we don't want any iterator
+ * to see this.
+ */
+ fsp->inode = (SMB_INO_T)0;
+ fsp->dev = (SMB_DEV_T)0;
+ fsp->size = psbuf->st_size;
+ fsp->vuid = current_user.vuid;
+ fsp->file_pid = global_smbpid;
+ fsp->can_lock = False;
+ fsp->can_read = False;
+ fsp->can_write = False;
+ fsp->share_mode = 0;
+ fsp->desired_access = 0;
+ fsp->print_file = False;
+ fsp->modified = False;
+ fsp->oplock_type = NO_OPLOCK;
+ fsp->sent_oplock_break = NO_BREAK_SENT;
+ fsp->is_directory = False;
+ fsp->is_stat = True;
+ fsp->directory_delete_on_close = False;
+ string_set(&fsp->fsp_name,fname);
+
+ conn->num_files_open++;
+
+ return fsp;
+}
diff --git a/source/smbd/oplock.c b/source/smbd/oplock.c
new file mode 100644
index 00000000000..19e6956d9ef
--- /dev/null
+++ b/source/smbd/oplock.c
@@ -0,0 +1,1260 @@
+/*
+ Unix SMB/CIFS implementation.
+ oplock processing
+ Copyright (C) Andrew Tridgell 1992-1998
+ Copyright (C) Jeremy Allison 1998 - 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 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"
+
+/* Oplock ipc UDP socket. */
+static int oplock_sock = -1;
+uint16 global_oplock_port = 0;
+
+/* Current number of oplocks we have outstanding. */
+static int32 exclusive_oplocks_open = 0;
+static int32 level_II_oplocks_open = 0;
+BOOL global_client_failed_oplock_break = False;
+BOOL global_oplock_break = False;
+
+extern int smb_read_error;
+
+static struct kernel_oplocks *koplocks;
+
+static BOOL oplock_break(SMB_DEV_T dev, SMB_INO_T inode, unsigned long file_id, BOOL local);
+
+/****************************************************************************
+ Get the number of current exclusive oplocks.
+****************************************************************************/
+
+int32 get_number_of_exclusive_open_oplocks(void)
+{
+ return exclusive_oplocks_open;
+}
+
+/****************************************************************************
+ Return True if an oplock message is pending.
+****************************************************************************/
+
+BOOL oplock_message_waiting(fd_set *fds)
+{
+ if (koplocks && koplocks->msg_waiting(fds))
+ return True;
+
+ if (FD_ISSET(oplock_sock, fds))
+ return True;
+
+ return False;
+}
+
+/****************************************************************************
+ Read an oplock break message from either the oplock UDP fd or the
+ kernel (if kernel oplocks are supported).
+
+ If timeout is zero then *fds contains the file descriptors that
+ are ready to be read and acted upon. If timeout is non-zero then
+ *fds contains the file descriptors to be selected on for read.
+ The timeout is in milliseconds
+
+****************************************************************************/
+
+BOOL receive_local_message( char *buffer, int buffer_len, int timeout)
+{
+ struct sockaddr_in from;
+ socklen_t fromlen = sizeof(from);
+ int32 msg_len = 0;
+ fd_set fds;
+ int selrtn = -1;
+
+ FD_ZERO(&fds);
+ smb_read_error = 0;
+
+ /*
+ * We need to check for kernel oplocks before going into the select
+ * here, as the EINTR generated by the linux kernel oplock may have
+ * already been eaten. JRA.
+ */
+
+ if (koplocks && koplocks->msg_waiting(&fds)) {
+ return koplocks->receive_message(&fds, buffer, buffer_len);
+ }
+
+ while (timeout > 0 && selrtn == -1) {
+ struct timeval to;
+ int maxfd = oplock_sock;
+ time_t starttime = time(NULL);
+
+ FD_ZERO(&fds);
+ maxfd = setup_oplock_select_set(&fds);
+
+ to.tv_sec = timeout / 1000;
+ to.tv_usec = (timeout % 1000) * 1000;
+
+ DEBUG(5,("receive_local_message: doing select with timeout of %d ms\n", timeout));
+
+ selrtn = sys_select(maxfd+1,&fds,NULL,NULL,&to);
+
+ if (selrtn == -1 && errno == EINTR) {
+
+ /* could be a kernel oplock interrupt */
+ if (koplocks && koplocks->msg_waiting(&fds)) {
+ return koplocks->receive_message(&fds, buffer, buffer_len);
+ }
+
+ /*
+ * Linux 2.0.x seems to have a bug in that
+ * it can return -1, EINTR with a timeout of zero.
+ * Make sure we bail out here with a read timeout
+ * if we got EINTR on a timeout of 1 or less.
+ */
+
+ if (timeout <= 1) {
+ smb_read_error = READ_TIMEOUT;
+ return False;
+ }
+
+ /* Not a kernel interrupt - could be a SIGUSR1 message. We must restart. */
+ /* We need to decrement the timeout here. */
+ timeout -= ((time(NULL) - starttime)*1000);
+ if (timeout < 0)
+ timeout = 1;
+
+ DEBUG(5,("receive_local_message: EINTR : new timeout %d ms\n", timeout));
+ continue;
+ }
+
+ /* Check if error */
+ if(selrtn == -1) {
+ /* something is wrong. Maybe the socket is dead? */
+ smb_read_error = READ_ERROR;
+ return False;
+ }
+
+ /* Did we timeout ? */
+ if (selrtn == 0) {
+ smb_read_error = READ_TIMEOUT;
+ return False;
+ }
+ }
+
+ if (koplocks && koplocks->msg_waiting(&fds)) {
+ return koplocks->receive_message(&fds, buffer, buffer_len);
+ }
+
+ if (!FD_ISSET(oplock_sock, &fds))
+ return False;
+
+ /*
+ * From here down we deal with the smbd <--> smbd
+ * oplock break protocol only.
+ */
+
+ /*
+ * Read a loopback udp message.
+ */
+ msg_len = sys_recvfrom(oplock_sock, &buffer[OPBRK_CMD_HEADER_LEN],
+ buffer_len - OPBRK_CMD_HEADER_LEN, 0, (struct sockaddr *)&from, &fromlen);
+
+ if(msg_len < 0) {
+ DEBUG(0,("receive_local_message. Error in recvfrom. (%s).\n",strerror(errno)));
+ return False;
+ }
+
+ /* Validate message length. */
+ if(msg_len > (buffer_len - OPBRK_CMD_HEADER_LEN)) {
+ DEBUG(0,("receive_local_message: invalid msg_len (%d) max can be %d\n", msg_len,
+ buffer_len - OPBRK_CMD_HEADER_LEN));
+ return False;
+ }
+
+ /* Validate message from address (must be localhost). */
+ if(from.sin_addr.s_addr != htonl(INADDR_LOOPBACK)) {
+ DEBUG(0,("receive_local_message: invalid 'from' address \
+(was %lx should be 127.0.0.1)\n", (long)from.sin_addr.s_addr));
+ return False;
+ }
+
+ /* Setup the message header */
+ SIVAL(buffer,OPBRK_CMD_LEN_OFFSET,msg_len);
+ SSVAL(buffer,OPBRK_CMD_PORT_OFFSET,ntohs(from.sin_port));
+
+ return True;
+}
+
+/****************************************************************************
+ Attempt to set an oplock on a file. Always succeeds if kernel oplocks are
+ disabled (just sets flags). Returns True if oplock set.
+****************************************************************************/
+
+BOOL set_file_oplock(files_struct *fsp, int oplock_type)
+{
+ if (koplocks && !koplocks->set_oplock(fsp, oplock_type))
+ return False;
+
+ fsp->oplock_type = oplock_type;
+ fsp->sent_oplock_break = NO_BREAK_SENT;
+ if (oplock_type == LEVEL_II_OPLOCK)
+ level_II_oplocks_open++;
+ else
+ exclusive_oplocks_open++;
+
+ DEBUG(5,("set_file_oplock: granted oplock on file %s, dev = %x, inode = %.0f, file_id = %lu, \
+tv_sec = %x, tv_usec = %x\n",
+ fsp->fsp_name, (unsigned int)fsp->dev, (double)fsp->inode, fsp->file_id,
+ (int)fsp->open_time.tv_sec, (int)fsp->open_time.tv_usec ));
+
+ return True;
+}
+
+/****************************************************************************
+ Attempt to release an oplock on a file. Decrements oplock count.
+****************************************************************************/
+
+void release_file_oplock(files_struct *fsp)
+{
+ if ((fsp->oplock_type != NO_OPLOCK) && koplocks)
+ koplocks->release_oplock(fsp);
+
+ if (fsp->oplock_type == LEVEL_II_OPLOCK)
+ level_II_oplocks_open--;
+ else if (fsp->oplock_type)
+ exclusive_oplocks_open--;
+
+ fsp->oplock_type = NO_OPLOCK;
+ fsp->sent_oplock_break = NO_BREAK_SENT;
+
+ flush_write_cache(fsp, OPLOCK_RELEASE_FLUSH);
+}
+
+/****************************************************************************
+ Attempt to downgrade an oplock on a file. Doesn't decrement oplock count.
+****************************************************************************/
+
+static void downgrade_file_oplock(files_struct *fsp)
+{
+ if (koplocks)
+ koplocks->release_oplock(fsp);
+ fsp->oplock_type = LEVEL_II_OPLOCK;
+ exclusive_oplocks_open--;
+ level_II_oplocks_open++;
+ fsp->sent_oplock_break = NO_BREAK_SENT;
+}
+
+/****************************************************************************
+ Remove a file oplock. Copes with level II and exclusive.
+ Locks then unlocks the share mode lock. Client can decide to go directly
+ to none even if a "break-to-level II" was sent.
+****************************************************************************/
+
+BOOL remove_oplock(files_struct *fsp, BOOL break_to_none)
+{
+ SMB_DEV_T dev = fsp->dev;
+ SMB_INO_T inode = fsp->inode;
+ BOOL ret = True;
+
+ /* Remove the oplock flag from the sharemode. */
+ if (lock_share_entry_fsp(fsp) == False) {
+ DEBUG(0,("remove_oplock: failed to lock share entry for file %s\n",
+ fsp->fsp_name ));
+ return False;
+ }
+
+ if (fsp->sent_oplock_break == EXCLUSIVE_BREAK_SENT || break_to_none) {
+ /*
+ * Deal with a reply when a break-to-none was sent.
+ */
+
+ if(remove_share_oplock(fsp)==False) {
+ DEBUG(0,("remove_oplock: failed to remove share oplock for file %s fnum %d, \
+dev = %x, inode = %.0f\n", fsp->fsp_name, fsp->fnum, (unsigned int)dev, (double)inode));
+ ret = False;
+ }
+
+ release_file_oplock(fsp);
+ } else {
+ /*
+ * Deal with a reply when a break-to-level II was sent.
+ */
+ if(downgrade_share_oplock(fsp)==False) {
+ DEBUG(0,("remove_oplock: failed to downgrade share oplock for file %s fnum %d, \
+dev = %x, inode = %.0f\n", fsp->fsp_name, fsp->fnum, (unsigned int)dev, (double)inode));
+ ret = False;
+ }
+
+ downgrade_file_oplock(fsp);
+ }
+
+ unlock_share_entry_fsp(fsp);
+ return ret;
+}
+
+/****************************************************************************
+ Setup the listening set of file descriptors for an oplock break
+ message either from the UDP socket or from the kernel. Returns the maximum
+ fd used.
+****************************************************************************/
+
+int setup_oplock_select_set( fd_set *fds)
+{
+ int maxfd = oplock_sock;
+
+ if(oplock_sock == -1)
+ return 0;
+
+ FD_SET(oplock_sock,fds);
+
+ if (koplocks && koplocks->notification_fd != -1) {
+ FD_SET(koplocks->notification_fd, fds);
+ maxfd = MAX(maxfd, koplocks->notification_fd);
+ }
+
+ return maxfd;
+}
+
+/****************************************************************************
+ Process an oplock break message - whether it came from the UDP socket
+ or from the kernel.
+****************************************************************************/
+
+BOOL process_local_message(char *buffer, int buf_size)
+{
+ int32 msg_len;
+ uint16 from_port;
+ char *msg_start;
+ pid_t remotepid;
+ SMB_DEV_T dev;
+ SMB_INO_T inode;
+ unsigned long file_id;
+ uint16 break_cmd_type;
+
+ msg_len = IVAL(buffer,OPBRK_CMD_LEN_OFFSET);
+ from_port = SVAL(buffer,OPBRK_CMD_PORT_OFFSET);
+
+ msg_start = &buffer[OPBRK_CMD_HEADER_LEN];
+
+ DEBUG(5,("process_local_message: Got a message of length %d from port (%d)\n",
+ msg_len, from_port));
+
+ /*
+ * Pull the info out of the requesting packet.
+ */
+
+ break_cmd_type = SVAL(msg_start,OPBRK_MESSAGE_CMD_OFFSET);
+
+ switch(break_cmd_type) {
+ case KERNEL_OPLOCK_BREAK_CMD:
+ if (!koplocks) {
+ DEBUG(0,("unexpected kernel oplock break!\n"));
+ break;
+ }
+ if (!koplocks->parse_message(msg_start, msg_len, &inode, &dev, &file_id)) {
+ DEBUG(0,("kernel oplock break parse failure!\n"));
+ }
+ break;
+
+ case OPLOCK_BREAK_CMD:
+ case LEVEL_II_OPLOCK_BREAK_CMD:
+ case ASYNC_LEVEL_II_OPLOCK_BREAK_CMD:
+
+ /* Ensure that the msg length is correct. */
+ if(msg_len != OPLOCK_BREAK_MSG_LEN) {
+ DEBUG(0,("process_local_message: incorrect length for OPLOCK_BREAK_CMD (was %d, should be %d).\n",
+ (int)msg_len, (int)OPLOCK_BREAK_MSG_LEN));
+ return False;
+ }
+
+ memcpy((char *)&remotepid, msg_start+OPLOCK_BREAK_PID_OFFSET,sizeof(remotepid));
+ memcpy((char *)&inode, msg_start+OPLOCK_BREAK_INODE_OFFSET,sizeof(inode));
+ memcpy((char *)&dev, msg_start+OPLOCK_BREAK_DEV_OFFSET,sizeof(dev));
+ memcpy((char *)&file_id, msg_start+OPLOCK_BREAK_FILEID_OFFSET,sizeof(file_id));
+
+ DEBUG(5,("process_local_message: (%s) oplock break request from \
+pid %d, port %d, dev = %x, inode = %.0f, file_id = %lu\n",
+ (break_cmd_type == OPLOCK_BREAK_CMD) ? "exclusive" : "level II",
+ (int)remotepid, from_port, (unsigned int)dev, (double)inode, file_id));
+ break;
+
+ /*
+ * Keep this as a debug case - eventually we can remove it.
+ */
+ case 0x8001:
+ DEBUG(0,("process_local_message: Received unsolicited break \
+reply - dumping info.\n"));
+
+ if(msg_len != OPLOCK_BREAK_MSG_LEN) {
+ DEBUG(0,("process_local_message: ubr: incorrect length for reply \
+(was %d, should be %d).\n", (int)msg_len, (int)OPLOCK_BREAK_MSG_LEN));
+ return False;
+ }
+
+ memcpy((char *)&inode, msg_start+OPLOCK_BREAK_INODE_OFFSET,sizeof(inode));
+ memcpy((char *)&remotepid, msg_start+OPLOCK_BREAK_PID_OFFSET,sizeof(remotepid));
+ memcpy((char *)&dev, msg_start+OPLOCK_BREAK_DEV_OFFSET,sizeof(dev));
+ memcpy((char *)&file_id, msg_start+OPLOCK_BREAK_FILEID_OFFSET,sizeof(file_id));
+
+ DEBUG(0,("process_local_message: unsolicited oplock break reply from \
+pid %d, port %d, dev = %x, inode = %.0f, file_id = %lu\n",
+ (int)remotepid, from_port, (unsigned int)dev, (double)inode, file_id));
+
+ return False;
+
+ default:
+ DEBUG(0,("process_local_message: unknown UDP message command code (%x) - ignoring.\n",
+ (unsigned int)SVAL(msg_start,0)));
+ return False;
+ }
+
+ /*
+ * Now actually process the break request.
+ */
+
+ if((exclusive_oplocks_open + level_II_oplocks_open) != 0) {
+ if (oplock_break(dev, inode, file_id, False) == False) {
+ DEBUG(0,("process_local_message: oplock break failed.\n"));
+ return False;
+ }
+ } else {
+ /*
+ * If we have no record of any currently open oplocks,
+ * it's not an error, as a close command may have
+ * just been issued on the file that was oplocked.
+ * Just log a message and return success in this case.
+ */
+ DEBUG(3,("process_local_message: oplock break requested with no outstanding \
+oplocks. Returning success.\n"));
+ }
+
+ /*
+ * Do the appropriate reply - none in the kernel or async level II case.
+ */
+
+ if(break_cmd_type == OPLOCK_BREAK_CMD || break_cmd_type == LEVEL_II_OPLOCK_BREAK_CMD) {
+ struct sockaddr_in toaddr;
+
+ /* Send the message back after OR'ing in the 'REPLY' bit. */
+ SSVAL(msg_start,OPBRK_MESSAGE_CMD_OFFSET,break_cmd_type | CMD_REPLY);
+
+ memset((char *)&toaddr,'\0',sizeof(toaddr));
+ toaddr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
+ toaddr.sin_port = htons(from_port);
+ toaddr.sin_family = AF_INET;
+
+ if(sys_sendto( oplock_sock, msg_start, OPLOCK_BREAK_MSG_LEN, 0,
+ (struct sockaddr *)&toaddr, sizeof(toaddr)) < 0) {
+ DEBUG(0,("process_local_message: sendto process %d failed. Errno was %s\n",
+ (int)remotepid, strerror(errno)));
+ return False;
+ }
+
+ DEBUG(5,("process_local_message: oplock break reply sent to \
+pid %d, port %d, for file dev = %x, inode = %.0f, file_id = %lu\n",
+ (int)remotepid, from_port, (unsigned int)dev, (double)inode, file_id));
+ }
+
+ return True;
+}
+
+/****************************************************************************
+ Set up an oplock break message.
+****************************************************************************/
+
+static void prepare_break_message(char *outbuf, files_struct *fsp, BOOL level2)
+{
+ memset(outbuf,'\0',smb_size);
+ set_message(outbuf,8,0,True);
+
+ SCVAL(outbuf,smb_com,SMBlockingX);
+ SSVAL(outbuf,smb_tid,fsp->conn->cnum);
+ SSVAL(outbuf,smb_pid,0xFFFF);
+ SSVAL(outbuf,smb_uid,0);
+ SSVAL(outbuf,smb_mid,0xFFFF);
+ SCVAL(outbuf,smb_vwv0,0xFF);
+ SSVAL(outbuf,smb_vwv2,fsp->fnum);
+ SCVAL(outbuf,smb_vwv3,LOCKING_ANDX_OPLOCK_RELEASE);
+ SCVAL(outbuf,smb_vwv3+1,level2 ? OPLOCKLEVEL_II : OPLOCKLEVEL_NONE);
+}
+
+/****************************************************************************
+ Function to do the waiting before sending a local break.
+****************************************************************************/
+
+static void wait_before_sending_break(BOOL local_request)
+{
+ extern struct timeval smb_last_time;
+
+ if(local_request) {
+ struct timeval cur_tv;
+ long wait_left = (long)lp_oplock_break_wait_time();
+
+ if (wait_left == 0)
+ return;
+
+ GetTimeOfDay(&cur_tv);
+
+ wait_left -= ((cur_tv.tv_sec - smb_last_time.tv_sec)*1000) +
+ ((cur_tv.tv_usec - smb_last_time.tv_usec)/1000);
+
+ if(wait_left > 0) {
+ wait_left = MIN(wait_left, 1000);
+ sys_usleep(wait_left * 1000);
+ }
+ }
+}
+
+/****************************************************************************
+ Ensure that we have a valid oplock.
+****************************************************************************/
+
+static files_struct *initial_break_processing(SMB_DEV_T dev, SMB_INO_T inode, unsigned long file_id)
+{
+ files_struct *fsp = NULL;
+
+ if( DEBUGLVL( 3 ) ) {
+ dbgtext( "initial_break_processing: called for dev = %x, inode = %.0f file_id = %lu\n",
+ (unsigned int)dev, (double)inode, file_id);
+ dbgtext( "Current oplocks_open (exclusive = %d, levelII = %d)\n",
+ exclusive_oplocks_open, level_II_oplocks_open );
+ }
+
+ /*
+ * We need to search the file open table for the
+ * entry containing this dev and inode, and ensure
+ * we have an oplock on it.
+ */
+
+ fsp = file_find_dif(dev, inode, file_id);
+
+ if(fsp == NULL) {
+ /* The file could have been closed in the meantime - return success. */
+ if( DEBUGLVL( 3 ) ) {
+ dbgtext( "initial_break_processing: cannot find open file with " );
+ dbgtext( "dev = %x, inode = %.0f file_id = %lu", (unsigned int)dev,
+ (double)inode, file_id);
+ dbgtext( "allowing break to succeed.\n" );
+ }
+ return NULL;
+ }
+
+ /* Ensure we have an oplock on the file */
+
+ /*
+ * There is a potential race condition in that an oplock could
+ * have been broken due to another udp request, and yet there are
+ * still oplock break messages being sent in the udp message
+ * queue for this file. So return true if we don't have an oplock,
+ * as we may have just freed it.
+ */
+
+ if(fsp->oplock_type == NO_OPLOCK) {
+ if( DEBUGLVL( 3 ) ) {
+ dbgtext( "initial_break_processing: file %s ", fsp->fsp_name );
+ dbgtext( "(dev = %x, inode = %.0f, file_id = %lu) has no oplock.\n",
+ (unsigned int)dev, (double)inode, fsp->file_id );
+ dbgtext( "Allowing break to succeed regardless.\n" );
+ }
+ return NULL;
+ }
+
+ return fsp;
+}
+
+/****************************************************************************
+ Process a level II oplock break directly.
+****************************************************************************/
+
+BOOL oplock_break_level2(files_struct *fsp, BOOL local_request, int token)
+{
+ extern uint32 global_client_caps;
+ char outbuf[128];
+ BOOL got_lock = False;
+ SMB_DEV_T dev = fsp->dev;
+ SMB_INO_T inode = fsp->inode;
+
+ /*
+ * We can have a level II oplock even if the client is not
+ * level II oplock aware. In this case just remove the
+ * flags and don't send the break-to-none message to
+ * the client.
+ */
+
+ if (global_client_caps & CAP_LEVEL_II_OPLOCKS) {
+ /*
+ * If we are sending an oplock break due to an SMB sent
+ * by our own client we ensure that we wait at leat
+ * lp_oplock_break_wait_time() milliseconds before sending
+ * the packet. Sending the packet sooner can break Win9x
+ * and has reported to cause problems on NT. JRA.
+ */
+
+ wait_before_sending_break(local_request);
+
+ /* Prepare the SMBlockingX message. */
+
+ prepare_break_message( outbuf, fsp, False);
+ if (!send_smb(smbd_server_fd(), outbuf))
+ exit_server("oplock_break_level2: send_smb failed.");
+ }
+
+ /*
+ * Now we must update the shared memory structure to tell
+ * everyone else we no longer have a level II oplock on
+ * this open file. If local_request is true then token is
+ * the existing lock on the shared memory area.
+ */
+
+ if(!local_request && lock_share_entry_fsp(fsp) == False) {
+ DEBUG(0,("oplock_break_level2: unable to lock share entry for file %s\n", fsp->fsp_name ));
+ } else {
+ got_lock = True;
+ }
+
+ if(remove_share_oplock(fsp)==False) {
+ DEBUG(0,("oplock_break_level2: unable to remove level II oplock for file %s\n", fsp->fsp_name ));
+ }
+
+ release_file_oplock(fsp);
+
+ if (!local_request && got_lock)
+ unlock_share_entry_fsp(fsp);
+
+ if(level_II_oplocks_open < 0) {
+ DEBUG(0,("oplock_break_level2: level_II_oplocks_open < 0 (%d). PANIC ERROR\n",
+ level_II_oplocks_open));
+ abort();
+ }
+
+ if( DEBUGLVL( 3 ) ) {
+ dbgtext( "oplock_break_level2: returning success for " );
+ dbgtext( "dev = %x, inode = %.0f, file_id = %lu\n", (unsigned int)dev, (double)inode, fsp->file_id );
+ dbgtext( "Current level II oplocks_open = %d\n", level_II_oplocks_open );
+ }
+
+ return True;
+}
+
+/****************************************************************************
+ Process an oplock break directly.
+****************************************************************************/
+
+static BOOL oplock_break(SMB_DEV_T dev, SMB_INO_T inode, unsigned long file_id, BOOL local_request)
+{
+ extern uint32 global_client_caps;
+ extern struct current_user current_user;
+ char *inbuf = NULL;
+ char *outbuf = NULL;
+ files_struct *fsp = NULL;
+ time_t start_time;
+ BOOL shutdown_server = False;
+ BOOL oplock_timeout = False;
+ BOOL sign_state;
+ connection_struct *saved_user_conn;
+ connection_struct *saved_fsp_conn;
+ int saved_vuid;
+ pstring saved_dir;
+ int timeout = (OPLOCK_BREAK_TIMEOUT * 1000);
+ pstring file_name;
+ BOOL using_levelII;
+
+ if((fsp = initial_break_processing(dev, inode, file_id)) == NULL)
+ return True;
+
+ /*
+ * Deal with a level II oplock going break to none separately.
+ */
+
+ if (LEVEL_II_OPLOCK_TYPE(fsp->oplock_type))
+ return oplock_break_level2(fsp, local_request, -1);
+
+ /* Mark the oplock break as sent - we don't want to send twice! */
+ if (fsp->sent_oplock_break) {
+ if( DEBUGLVL( 0 ) ) {
+ dbgtext( "oplock_break: ERROR: oplock_break already sent for " );
+ dbgtext( "file %s ", fsp->fsp_name);
+ dbgtext( "(dev = %x, inode = %.0f, file_id = %lu)\n", (unsigned int)dev, (double)inode, fsp->file_id );
+ }
+
+ /*
+ * We have to fail the open here as we cannot send another oplock break on
+ * this file whilst we are awaiting a response from the client - neither
+ * can we allow another open to succeed while we are waiting for the client.
+ */
+ return False;
+ }
+
+ if(global_oplock_break) {
+ DEBUG(0,("ABORT : ABORT : recursion in oplock_break !!!!!\n"));
+ abort();
+ }
+
+ /*
+ * Now comes the horrid part. We must send an oplock break to the client,
+ * and then process incoming messages until we get a close or oplock release.
+ * At this point we know we need a new inbuf/outbuf buffer pair.
+ * We cannot use these staticaly as we may recurse into here due to
+ * messages crossing on the wire.
+ */
+
+ if((inbuf = (char *)malloc(BUFFER_SIZE + LARGE_WRITEX_HDR_SIZE + SAFETY_MARGIN))==NULL) {
+ DEBUG(0,("oplock_break: malloc fail for input buffer.\n"));
+ return False;
+ }
+
+ if((outbuf = (char *)malloc(BUFFER_SIZE + LARGE_WRITEX_HDR_SIZE + SAFETY_MARGIN))==NULL) {
+ DEBUG(0,("oplock_break: malloc fail for output buffer.\n"));
+ SAFE_FREE(inbuf);
+ return False;
+ }
+
+ /*
+ * If we are sending an oplock break due to an SMB sent
+ * by our own client we ensure that we wait at leat
+ * lp_oplock_break_wait_time() milliseconds before sending
+ * the packet. Sending the packet sooner can break Win9x
+ * and has reported to cause problems on NT. JRA.
+ */
+
+ wait_before_sending_break(local_request);
+
+ /* Prepare the SMBlockingX message. */
+
+ if ((global_client_caps & CAP_LEVEL_II_OPLOCKS) &&
+ !koplocks && /* NOTE: we force levelII off for kernel oplocks - this will change when it is supported */
+ lp_level2_oplocks(SNUM(fsp->conn))) {
+ using_levelII = True;
+ } else {
+ using_levelII = False;
+ }
+
+ prepare_break_message( outbuf, fsp, using_levelII);
+ /* Remember if we just sent a break to level II on this file. */
+ fsp->sent_oplock_break = using_levelII? LEVEL_II_BREAK_SENT:EXCLUSIVE_BREAK_SENT;
+
+ /* Save the server smb signing state. */
+ sign_state = srv_oplock_set_signing(False);
+
+ if (!send_smb(smbd_server_fd(), outbuf)) {
+ srv_oplock_set_signing(sign_state);
+ exit_server("oplock_break: send_smb failed.");
+ }
+
+ /* Restore the sign state to what it was. */
+ srv_oplock_set_signing(sign_state);
+
+ /* We need this in case a readraw crosses on the wire. */
+ global_oplock_break = True;
+
+ /* Process incoming messages. */
+
+ /*
+ * JRA - If we don't get a break from the client in OPLOCK_BREAK_TIMEOUT
+ * seconds we should just die....
+ */
+
+ start_time = time(NULL);
+
+ /*
+ * Save the information we need to re-become the
+ * user, then unbecome the user whilst we're doing this.
+ */
+ saved_user_conn = current_user.conn;
+ saved_vuid = current_user.vuid;
+ saved_fsp_conn = fsp->conn;
+ change_to_root_user();
+ vfs_GetWd(saved_fsp_conn,saved_dir);
+ /* Save the chain fnum. */
+ file_chain_save();
+
+ /*
+ * From Charles Hoch <hoch@exemplary.com>. If the break processing
+ * code closes the file (as it often does), then the fsp pointer here
+ * points to free()'d memory. We *must* revalidate fsp each time
+ * around the loop.
+ */
+
+ pstrcpy(file_name, fsp->fsp_name);
+
+ while((fsp = initial_break_processing(dev, inode, file_id)) &&
+ OPEN_FSP(fsp) && EXCLUSIVE_OPLOCK_TYPE(fsp->oplock_type)) {
+ if(receive_smb(smbd_server_fd(),inbuf, timeout) == False) {
+ /*
+ * Die if we got an error.
+ */
+
+ if (smb_read_error == READ_EOF) {
+ DEBUG( 0, ( "oplock_break: end of file from client\n" ) );
+ shutdown_server = True;
+ } else if (smb_read_error == READ_ERROR) {
+ DEBUG( 0, ("oplock_break: receive_smb error (%s)\n", strerror(errno)) );
+ shutdown_server = True;
+ } else if (smb_read_error == READ_BAD_SIG) {
+ DEBUG( 0, ("oplock_break: bad signature from client\n" ));
+ shutdown_server = True;
+ } else if (smb_read_error == READ_TIMEOUT) {
+ DEBUG( 0, ( "oplock_break: receive_smb timed out after %d seconds.\n", OPLOCK_BREAK_TIMEOUT ) );
+ oplock_timeout = True;
+ }
+
+ DEBUGADD( 0, ( "oplock_break failed for file %s ", file_name ) );
+ DEBUGADD( 0, ( "(dev = %x, inode = %.0f, file_id = %lu).\n",
+ (unsigned int)dev, (double)inode, file_id));
+
+ break;
+ }
+
+ /*
+ * There are certain SMB requests that we shouldn't allow
+ * to recurse. opens, renames and deletes are the obvious
+ * ones. This is handled in the switch_message() function.
+ * If global_oplock_break is set they will push the packet onto
+ * the pending smb queue and return -1 (no reply).
+ * JRA.
+ */
+
+ process_smb(inbuf, outbuf);
+
+ /*
+ * Die if we go over the time limit.
+ */
+
+ if((time(NULL) - start_time) > OPLOCK_BREAK_TIMEOUT) {
+ if( DEBUGLVL( 0 ) ) {
+ dbgtext( "oplock_break: no break received from client " );
+ dbgtext( "within %d seconds.\n", OPLOCK_BREAK_TIMEOUT );
+ dbgtext( "oplock_break failed for file %s ", fsp->fsp_name );
+ dbgtext( "(dev = %x, inode = %.0f, file_id = %lu).\n",
+ (unsigned int)dev, (double)inode, file_id );
+ }
+ oplock_timeout = True;
+ break;
+ }
+ }
+
+ /*
+ * Go back to being the user who requested the oplock
+ * break.
+ */
+ if((saved_user_conn != NULL) && (saved_vuid != UID_FIELD_INVALID) && !change_to_user(saved_user_conn, saved_vuid)) {
+ DEBUG( 0, ( "oplock_break: unable to re-become user!" ) );
+ DEBUGADD( 0, ( "Shutting down server\n" ) );
+ close(oplock_sock);
+ exit_server("unable to re-become user");
+ }
+
+ /* Including the directory. */
+ vfs_ChDir(saved_fsp_conn,saved_dir);
+
+ /* Restore the chain fnum. */
+ file_chain_restore();
+
+ /* Free the buffers we've been using to recurse. */
+ SAFE_FREE(inbuf);
+ SAFE_FREE(outbuf);
+
+ /* We need this in case a readraw crossed on the wire. */
+ if(global_oplock_break)
+ global_oplock_break = False;
+
+ /*
+ * If the client timed out then clear the oplock (or go to level II)
+ * and continue. This seems to be what NT does and is better than dropping
+ * the connection.
+ */
+
+ if(oplock_timeout && (fsp = initial_break_processing(dev, inode, file_id)) &&
+ OPEN_FSP(fsp) && EXCLUSIVE_OPLOCK_TYPE(fsp->oplock_type)) {
+ DEBUG(0,("oplock_break: client failure in oplock break in file %s\n", fsp->fsp_name));
+ remove_oplock(fsp,True);
+#if FASCIST_OPLOCK_BACKOFF
+ global_client_failed_oplock_break = True; /* Never grant this client an oplock again. */
+#endif
+ }
+
+ /*
+ * If the client had an error we must die.
+ */
+
+ if(shutdown_server) {
+ DEBUG( 0, ( "oplock_break: client failure in break - " ) );
+ DEBUGADD( 0, ( "shutting down this smbd.\n" ) );
+ close(oplock_sock);
+ exit_server("oplock break failure");
+ }
+
+ /* Santity check - remove this later. JRA */
+ if(exclusive_oplocks_open < 0) {
+ DEBUG(0,("oplock_break: exclusive_oplocks_open < 0 (%d). PANIC ERROR\n", exclusive_oplocks_open));
+ abort();
+ }
+
+ if( DEBUGLVL( 3 ) ) {
+ dbgtext( "oplock_break: returning success for " );
+ dbgtext( "dev = %x, inode = %.0f, file_id = %lu\n", (unsigned int)dev, (double)inode, file_id );
+ dbgtext( "Current exclusive_oplocks_open = %d\n", exclusive_oplocks_open );
+ }
+
+ return True;
+}
+
+/****************************************************************************
+Send an oplock break message to another smbd process. If the oplock is held
+by the local smbd then call the oplock break function directly.
+****************************************************************************/
+
+BOOL request_oplock_break(share_mode_entry *share_entry, BOOL async)
+{
+ char op_break_msg[OPLOCK_BREAK_MSG_LEN];
+ struct sockaddr_in addr_out;
+ pid_t pid = sys_getpid();
+ time_t start_time;
+ int time_left;
+ SMB_DEV_T dev = share_entry->dev;
+ SMB_INO_T inode = share_entry->inode;
+ unsigned long file_id = share_entry->share_file_id;
+ uint16 break_cmd_type;
+
+ if(pid == share_entry->pid) {
+ /* We are breaking our own oplock, make sure it's us. */
+ if(share_entry->op_port != global_oplock_port) {
+ DEBUG(0,("request_oplock_break: corrupt share mode entry - pid = %d, port = %d \
+should be %d\n", (int)pid, share_entry->op_port, global_oplock_port));
+ return False;
+ }
+
+ DEBUG(5,("request_oplock_break: breaking our own oplock\n"));
+
+#if 1 /* JRA PARANOIA TEST.... */
+ {
+ files_struct *fsp = file_find_dif(dev, inode, file_id);
+ if (!fsp) {
+ DEBUG(0,("request_oplock_break: PANIC : breaking our own oplock requested for \
+dev = %x, inode = %.0f, file_id = %lu and no fsp found !\n",
+ (unsigned int)dev, (double)inode, file_id ));
+ smb_panic("request_oplock_break: no fsp found for our own oplock\n");
+ }
+ }
+#endif /* END JRA PARANOIA TEST... */
+
+ /* Call oplock break direct. */
+ return oplock_break(dev, inode, file_id, True);
+ }
+
+ /* We need to send a OPLOCK_BREAK_CMD message to the port in the share mode entry. */
+
+ if (LEVEL_II_OPLOCK_TYPE(share_entry->op_type)) {
+ break_cmd_type = async ? ASYNC_LEVEL_II_OPLOCK_BREAK_CMD : LEVEL_II_OPLOCK_BREAK_CMD;
+ } else {
+ break_cmd_type = OPLOCK_BREAK_CMD;
+ }
+
+ SSVAL(op_break_msg,OPBRK_MESSAGE_CMD_OFFSET,break_cmd_type);
+ memcpy(op_break_msg+OPLOCK_BREAK_PID_OFFSET,(char *)&pid,sizeof(pid));
+ memcpy(op_break_msg+OPLOCK_BREAK_DEV_OFFSET,(char *)&dev,sizeof(dev));
+ memcpy(op_break_msg+OPLOCK_BREAK_INODE_OFFSET,(char *)&inode,sizeof(inode));
+ memcpy(op_break_msg+OPLOCK_BREAK_FILEID_OFFSET,(char *)&file_id,sizeof(file_id));
+
+ /* Set the address and port. */
+ memset((char *)&addr_out,'\0',sizeof(addr_out));
+ addr_out.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
+ addr_out.sin_port = htons( share_entry->op_port );
+ addr_out.sin_family = AF_INET;
+
+ if( DEBUGLVL( 3 ) ) {
+ dbgtext( "request_oplock_break: sending a %s oplock break message to ", async ? "asynchronous" : "synchronous" );
+ dbgtext( "pid %d on port %d ", (int)share_entry->pid, share_entry->op_port );
+ dbgtext( "for dev = %x, inode = %.0f, file_id = %lu\n",
+ (unsigned int)dev, (double)inode, file_id );
+ }
+
+ if(sys_sendto(oplock_sock,op_break_msg,OPLOCK_BREAK_MSG_LEN,0,
+ (struct sockaddr *)&addr_out,sizeof(addr_out)) < 0) {
+ if( DEBUGLVL( 0 ) ) {
+ dbgtext( "request_oplock_break: failed when sending a oplock " );
+ dbgtext( "break message to pid %d ", (int)share_entry->pid );
+ dbgtext( "on port %d ", share_entry->op_port );
+ dbgtext( "for dev = %x, inode = %.0f, file_id = %lu\n",
+ (unsigned int)dev, (double)inode, file_id );
+ dbgtext( "Error was %s\n", strerror(errno) );
+ }
+ return False;
+ }
+
+ /*
+ * If we just sent a message to a level II oplock share entry in async mode then
+ * we are done and may return.
+ */
+
+ if (LEVEL_II_OPLOCK_TYPE(share_entry->op_type) && async) {
+ DEBUG(3,("request_oplock_break: sent async break message to level II entry.\n"));
+ return True;
+ }
+
+ /*
+ * Now we must await the oplock broken message coming back
+ * from the target smbd process. Timeout if it fails to
+ * return in (OPLOCK_BREAK_TIMEOUT + OPLOCK_BREAK_TIMEOUT_FUDGEFACTOR) seconds.
+ * While we get messages that aren't ours, loop.
+ */
+
+ start_time = time(NULL);
+ time_left = OPLOCK_BREAK_TIMEOUT+OPLOCK_BREAK_TIMEOUT_FUDGEFACTOR;
+
+ while(time_left >= 0) {
+ char op_break_reply[OPBRK_CMD_HEADER_LEN+OPLOCK_BREAK_MSG_LEN];
+ uint16 reply_from_port;
+ char *reply_msg_start;
+
+ if(receive_local_message(op_break_reply, sizeof(op_break_reply),
+ time_left ? time_left * 1000 : 1) == False) {
+ if(smb_read_error == READ_TIMEOUT) {
+ if( DEBUGLVL( 0 ) ) {
+ dbgtext( "request_oplock_break: no response received to oplock " );
+ dbgtext( "break request to pid %d ", (int)share_entry->pid );
+ dbgtext( "on port %d ", share_entry->op_port );
+ dbgtext( "for dev = %x, inode = %.0f, file_id = %lu\n",
+ (unsigned int)dev, (double)inode, file_id );
+ }
+
+ /*
+ * This is a hack to make handling of failing clients more robust.
+ * If a oplock break response message is not received in the timeout
+ * period we may assume that the smbd servicing that client holding
+ * the oplock has died and the client changes were lost anyway, so
+ * we should continue to try and open the file.
+ */
+ break;
+ } else {
+ if( DEBUGLVL( 0 ) ) {
+ dbgtext( "request_oplock_break: error in response received " );
+ dbgtext( "to oplock break request to pid %d ", (int)share_entry->pid );
+ dbgtext( "on port %d ", share_entry->op_port );
+ dbgtext( "for dev = %x, inode = %.0f, file_id = %lu\n",
+ (unsigned int)dev, (double)inode, file_id );
+ dbgtext( "Error was (%s).\n", strerror(errno) );
+ }
+ }
+ return False;
+ }
+
+ reply_from_port = SVAL(op_break_reply,OPBRK_CMD_PORT_OFFSET);
+ reply_msg_start = &op_break_reply[OPBRK_CMD_HEADER_LEN];
+
+ /*
+ * Test to see if this is the reply we are awaiting (ie. the one we sent with the CMD_REPLY flag OR'ed in).
+ */
+ if((SVAL(reply_msg_start,OPBRK_MESSAGE_CMD_OFFSET) & CMD_REPLY) &&
+ ((SVAL(reply_msg_start,OPBRK_MESSAGE_CMD_OFFSET) & ~CMD_REPLY) == break_cmd_type) &&
+ (reply_from_port == share_entry->op_port) &&
+ (memcmp(&reply_msg_start[OPLOCK_BREAK_PID_OFFSET], &op_break_msg[OPLOCK_BREAK_PID_OFFSET],
+ OPLOCK_BREAK_MSG_LEN - OPLOCK_BREAK_PID_OFFSET) == 0)) {
+
+ /*
+ * This is the reply we've been waiting for.
+ */
+ break;
+ } else {
+ /*
+ * This is another message - a break request.
+ * Note that both kernel oplock break requests
+ * and UDP inter-smbd oplock break requests will
+ * be processed here.
+ *
+ * Process it to prevent potential deadlock.
+ * Note that the code in switch_message() prevents
+ * us from recursing into here as any SMB requests
+ * we might process that would cause another oplock
+ * break request to be made will be queued.
+ * JRA.
+ */
+
+ process_local_message(op_break_reply, sizeof(op_break_reply));
+ }
+
+ time_left -= (time(NULL) - start_time);
+ }
+
+ DEBUG(3,("request_oplock_break: broke oplock.\n"));
+
+ return True;
+}
+
+/****************************************************************************
+ Attempt to break an oplock on a file (if oplocked).
+ Returns True if the file was closed as a result of
+ the oplock break, False otherwise.
+ Used as a last ditch attempt to free a space in the
+ file table when we have run out.
+****************************************************************************/
+
+BOOL attempt_close_oplocked_file(files_struct *fsp)
+{
+ DEBUG(5,("attempt_close_oplocked_file: checking file %s.\n", fsp->fsp_name));
+
+ if (EXCLUSIVE_OPLOCK_TYPE(fsp->oplock_type) && !fsp->sent_oplock_break && (fsp->fd != -1)) {
+ /* Try and break the oplock. */
+ if (oplock_break(fsp->dev, fsp->inode, fsp->file_id, True)) {
+ if(file_find_fsp(fsp) == NULL) /* Did the oplock break close the file ? */
+ return True;
+ }
+ }
+
+ return False;
+}
+
+/****************************************************************************
+ This function is called on any file modification or lock request. If a file
+ is level 2 oplocked then it must tell all other level 2 holders to break to none.
+****************************************************************************/
+
+void release_level_2_oplocks_on_change(files_struct *fsp)
+{
+ share_mode_entry *share_list = NULL;
+ pid_t pid = sys_getpid();
+ int token = -1;
+ int num_share_modes = 0;
+ int i;
+
+ /*
+ * If this file is level II oplocked then we need
+ * to grab the shared memory lock and inform all
+ * other files with a level II lock that they need
+ * to flush their read caches. We keep the lock over
+ * the shared memory area whilst doing this.
+ */
+
+ if (!LEVEL_II_OPLOCK_TYPE(fsp->oplock_type))
+ return;
+
+ if (lock_share_entry_fsp(fsp) == False) {
+ DEBUG(0,("release_level_2_oplocks_on_change: failed to lock share mode entry for file %s.\n", fsp->fsp_name ));
+ }
+
+ num_share_modes = get_share_modes(fsp->conn, fsp->dev, fsp->inode, &share_list);
+
+ DEBUG(10,("release_level_2_oplocks_on_change: num_share_modes = %d\n",
+ num_share_modes ));
+
+ for(i = 0; i < num_share_modes; i++) {
+ share_mode_entry *share_entry = &share_list[i];
+
+ /*
+ * As there could have been multiple writes waiting at the lock_share_entry
+ * gate we may not be the first to enter. Hence the state of the op_types
+ * in the share mode entries may be partly NO_OPLOCK and partly LEVEL_II
+ * oplock. It will do no harm to re-send break messages to those smbd's
+ * that are still waiting their turn to remove their LEVEL_II state, and
+ * also no harm to ignore existing NO_OPLOCK states. JRA.
+ */
+
+ DEBUG(10,("release_level_2_oplocks_on_change: share_entry[%i]->op_type == %d\n",
+ i, share_entry->op_type ));
+
+ if (share_entry->op_type == NO_OPLOCK)
+ continue;
+
+ /* Paranoia .... */
+ if (EXCLUSIVE_OPLOCK_TYPE(share_entry->op_type)) {
+ DEBUG(0,("release_level_2_oplocks_on_change: PANIC. share mode entry %d is an exlusive oplock !\n", i ));
+ unlock_share_entry(fsp->conn, fsp->dev, fsp->inode);
+ abort();
+ }
+
+ /*
+ * Check if this is a file we have open (including the
+ * file we've been called to do write_file on. If so
+ * then break it directly without releasing the lock.
+ */
+
+ if (pid == share_entry->pid) {
+ files_struct *new_fsp = file_find_dif(share_entry->dev, share_entry->inode, share_entry->share_file_id);
+
+ /* Paranoia check... */
+ if(new_fsp == NULL) {
+ DEBUG(0,("release_level_2_oplocks_on_change: PANIC. share mode entry %d is not a local file !\n", i ));
+ unlock_share_entry(fsp->conn, fsp->dev, fsp->inode);
+ abort();
+ }
+
+ DEBUG(10,("release_level_2_oplocks_on_change: breaking our own oplock.\n"));
+
+ oplock_break_level2(new_fsp, True, token);
+
+ } else {
+
+ /*
+ * This is a remote file and so we send an asynchronous
+ * message.
+ */
+
+ DEBUG(10,("release_level_2_oplocks_on_change: breaking remote oplock (async).\n"));
+ request_oplock_break(share_entry, True);
+ }
+ }
+
+ SAFE_FREE(share_list);
+ unlock_share_entry_fsp(fsp);
+
+ /* Paranoia check... */
+ if (LEVEL_II_OPLOCK_TYPE(fsp->oplock_type)) {
+ DEBUG(0,("release_level_2_oplocks_on_change: PANIC. File %s still has a level II oplock.\n", fsp->fsp_name));
+ smb_panic("release_level_2_oplocks_on_change");
+ }
+}
+
+/****************************************************************************
+setup oplocks for this process
+****************************************************************************/
+
+BOOL init_oplocks(void)
+{
+ struct sockaddr_in sock_name;
+ socklen_t len = sizeof(sock_name);
+
+ DEBUG(3,("open_oplock_ipc: opening loopback UDP socket.\n"));
+
+ /* Open a lookback UDP socket on a random port. */
+ oplock_sock = open_socket_in(SOCK_DGRAM, 0, 0, htonl(INADDR_LOOPBACK),False);
+ if (oplock_sock == -1) {
+ DEBUG(0,("open_oplock_ipc: Failed to get local UDP socket for \
+address %lx. Error was %s\n", (long)htonl(INADDR_LOOPBACK), strerror(errno)));
+ global_oplock_port = 0;
+ return(False);
+ }
+
+ /* Find out the transient UDP port we have been allocated. */
+ if(getsockname(oplock_sock, (struct sockaddr *)&sock_name, &len)<0) {
+ DEBUG(0,("open_oplock_ipc: Failed to get local UDP port. Error was %s\n",
+ strerror(errno)));
+ close(oplock_sock);
+ oplock_sock = -1;
+ global_oplock_port = 0;
+ return False;
+ }
+ global_oplock_port = ntohs(sock_name.sin_port);
+
+ if (lp_kernel_oplocks()) {
+#if HAVE_KERNEL_OPLOCKS_IRIX
+ koplocks = irix_init_kernel_oplocks();
+#elif HAVE_KERNEL_OPLOCKS_LINUX
+ koplocks = linux_init_kernel_oplocks();
+#endif
+ }
+
+ DEBUG(3,("open_oplock ipc: pid = %d, global_oplock_port = %u\n",
+ (int)sys_getpid(), global_oplock_port));
+
+ return True;
+}
diff --git a/source/smbd/oplock_irix.c b/source/smbd/oplock_irix.c
new file mode 100644
index 00000000000..ffcf3d0af4d
--- /dev/null
+++ b/source/smbd/oplock_irix.c
@@ -0,0 +1,285 @@
+/*
+ Unix SMB/CIFS implementation.
+ IRIX kernel oplock processing
+ 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 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"
+
+#if HAVE_KERNEL_OPLOCKS_IRIX
+
+static int oplock_pipe_write = -1;
+static int oplock_pipe_read = -1;
+
+/****************************************************************************
+ Test to see if IRIX kernel oplocks work.
+****************************************************************************/
+
+static BOOL irix_oplocks_available(void)
+{
+ int fd;
+ int pfd[2];
+ pstring tmpname;
+
+ oplock_set_capability(True, False);
+
+ slprintf(tmpname,sizeof(tmpname)-1, "%s/koplock.%d", lp_lockdir(), (int)sys_getpid());
+
+ if(pipe(pfd) != 0) {
+ DEBUG(0,("check_kernel_oplocks: Unable to create pipe. Error was %s\n",
+ strerror(errno) ));
+ return False;
+ }
+
+ if((fd = sys_open(tmpname, O_RDWR|O_CREAT|O_EXCL|O_TRUNC, 0600)) < 0) {
+ DEBUG(0,("check_kernel_oplocks: Unable to open temp test file %s. Error was %s\n",
+ tmpname, strerror(errno) ));
+ unlink( tmpname );
+ close(pfd[0]);
+ close(pfd[1]);
+ return False;
+ }
+
+ unlink(tmpname);
+
+ if(sys_fcntl_long(fd, F_OPLKREG, pfd[1]) == -1) {
+ DEBUG(0,("check_kernel_oplocks: Kernel oplocks are not available on this machine. \
+Disabling kernel oplock support.\n" ));
+ close(pfd[0]);
+ close(pfd[1]);
+ close(fd);
+ return False;
+ }
+
+ if(sys_fcntl_long(fd, F_OPLKACK, OP_REVOKE) < 0 ) {
+ DEBUG(0,("check_kernel_oplocks: Error when removing kernel oplock. Error was %s. \
+Disabling kernel oplock support.\n", strerror(errno) ));
+ close(pfd[0]);
+ close(pfd[1]);
+ close(fd);
+ return False;
+ }
+
+ close(pfd[0]);
+ close(pfd[1]);
+ close(fd);
+
+ return True;
+}
+
+/****************************************************************************
+ * Deal with the IRIX kernel <--> smbd
+ * oplock break protocol.
+****************************************************************************/
+
+static BOOL irix_oplock_receive_message(fd_set *fds, char *buffer, int buffer_len)
+{
+ extern int smb_read_error;
+ oplock_stat_t os;
+ char dummy;
+ files_struct *fsp;
+
+ /*
+ * Read one byte of zero to clear the
+ * kernel break notify message.
+ */
+
+ if(read(oplock_pipe_read, &dummy, 1) != 1) {
+ DEBUG(0,("irix_oplock_receive_message: read of kernel notification failed. \
+Error was %s.\n", strerror(errno) ));
+ smb_read_error = READ_ERROR;
+ return False;
+ }
+
+ /*
+ * Do a query to get the
+ * device and inode of the file that has the break
+ * request outstanding.
+ */
+
+ if(sys_fcntl_ptr(oplock_pipe_read, F_OPLKSTAT, &os) < 0) {
+ DEBUG(0,("irix_oplock_receive_message: fcntl of kernel notification failed. \
+Error was %s.\n", strerror(errno) ));
+ if(errno == EAGAIN) {
+ /*
+ * Duplicate kernel break message - ignore.
+ */
+ memset(buffer, '\0', KERNEL_OPLOCK_BREAK_MSG_LEN);
+ return True;
+ }
+ smb_read_error = READ_ERROR;
+ return False;
+ }
+
+ /*
+ * We only have device and inode info here - we have to guess that this
+ * is the first fsp open with this dev,ino pair.
+ */
+
+ if ((fsp = file_find_di_first((SMB_DEV_T)os.os_dev, (SMB_INO_T)os.os_ino)) == NULL) {
+ DEBUG(0,("irix_oplock_receive_message: unable to find open file with dev = %x, inode = %.0f\n",
+ (unsigned int)os.os_dev, (double)os.os_ino ));
+ return False;
+ }
+
+ DEBUG(5,("irix_oplock_receive_message: kernel oplock break request received for \
+dev = %x, inode = %.0f\n, file_id = %ul", (unsigned int)fsp->dev, (double)fsp->inode, fsp->file_id ));
+
+ /*
+ * Create a kernel oplock break message.
+ */
+
+ /* Setup the message header */
+ SIVAL(buffer,OPBRK_CMD_LEN_OFFSET,KERNEL_OPLOCK_BREAK_MSG_LEN);
+ SSVAL(buffer,OPBRK_CMD_PORT_OFFSET,0);
+
+ buffer += OPBRK_CMD_HEADER_LEN;
+
+ SSVAL(buffer,OPBRK_MESSAGE_CMD_OFFSET,KERNEL_OPLOCK_BREAK_CMD);
+
+ memcpy(buffer + KERNEL_OPLOCK_BREAK_DEV_OFFSET, (char *)&fsp->dev, sizeof(fsp->dev));
+ memcpy(buffer + KERNEL_OPLOCK_BREAK_INODE_OFFSET, (char *)&fsp->inode, sizeof(fsp->inode));
+ memcpy(buffer + KERNEL_OPLOCK_BREAK_FILEID_OFFSET, (char *)&fsp->file_id, sizeof(fsp->file_id));
+
+ return True;
+}
+
+/****************************************************************************
+ Attempt to set an kernel oplock on a file.
+****************************************************************************/
+
+static BOOL irix_set_kernel_oplock(files_struct *fsp, int oplock_type)
+{
+ if (sys_fcntl_long(fsp->fd, F_OPLKREG, oplock_pipe_write) == -1) {
+ if(errno != EAGAIN) {
+ DEBUG(0,("irix_set_kernel_oplock: Unable to get kernel oplock on file %s, dev = %x, \
+inode = %.0f, file_id = %ul. Error was %s\n",
+ fsp->fsp_name, (unsigned int)fsp->dev, (double)fsp->inode, fsp->file_id,
+ strerror(errno) ));
+ } else {
+ DEBUG(5,("irix_set_kernel_oplock: Refused oplock on file %s, fd = %d, dev = %x, \
+inode = %.0f, file_id = %ul. Another process had the file open.\n",
+ fsp->fsp_name, fsp->fd, (unsigned int)fsp->dev, (double)fsp->inode, fsp->file_id ));
+ }
+ return False;
+ }
+
+ DEBUG(10,("irix_set_kernel_oplock: got kernel oplock on file %s, dev = %x, inode = %.0f, file_id = %ul\n",
+ fsp->fsp_name, (unsigned int)fsp->dev, (double)fsp->inode, fsp->file_id));
+
+ return True;
+}
+
+/****************************************************************************
+ Release a kernel oplock on a file.
+****************************************************************************/
+
+static void irix_release_kernel_oplock(files_struct *fsp)
+{
+ if (DEBUGLVL(10)) {
+ /*
+ * Check and print out the current kernel
+ * oplock state of this file.
+ */
+ int state = sys_fcntl_long(fsp->fd, F_OPLKACK, -1);
+ dbgtext("irix_release_kernel_oplock: file %s, dev = %x, inode = %.0f file_id = %ul, has kernel \
+oplock state of %x.\n", fsp->fsp_name, (unsigned int)fsp->dev,
+ (double)fsp->inode, fsp->file_id, state );
+ }
+
+ /*
+ * Remove the kernel oplock on this file.
+ */
+ if(sys_fcntl_long(fsp->fd, F_OPLKACK, OP_REVOKE) < 0) {
+ if( DEBUGLVL( 0 )) {
+ dbgtext("irix_release_kernel_oplock: Error when removing kernel oplock on file " );
+ dbgtext("%s, dev = %x, inode = %.0f, file_id = %ul. Error was %s\n",
+ fsp->fsp_name, (unsigned int)fsp->dev,
+ (double)fsp->inode, fsp->file_id, strerror(errno) );
+ }
+ }
+}
+
+/****************************************************************************
+ Parse a kernel oplock message.
+****************************************************************************/
+
+static BOOL irix_kernel_oplock_parse(char *msg_start, int msg_len,
+ SMB_INO_T *inode, SMB_DEV_T *dev, unsigned long *file_id)
+{
+ /* Ensure that the msg length is correct. */
+ if(msg_len != KERNEL_OPLOCK_BREAK_MSG_LEN) {
+ DEBUG(0,("incorrect length for KERNEL_OPLOCK_BREAK_CMD (was %d, should be %d).\n",
+ msg_len, KERNEL_OPLOCK_BREAK_MSG_LEN));
+ return False;
+ }
+
+ memcpy((char *)inode, msg_start+KERNEL_OPLOCK_BREAK_INODE_OFFSET, sizeof(*inode));
+ memcpy((char *)dev, msg_start+KERNEL_OPLOCK_BREAK_DEV_OFFSET, sizeof(*dev));
+ memcpy((char *)file_id, msg_start+KERNEL_OPLOCK_BREAK_FILEID_OFFSET, sizeof(*file_id));
+
+ DEBUG(5,("kernel oplock break request for file dev = %x, inode = %.0f, file_id = %ul\n",
+ (unsigned int)*dev, (double)*inode, *file_id));
+
+ return True;
+}
+
+/****************************************************************************
+ Set *maxfd to include oplock read pipe.
+****************************************************************************/
+
+static BOOL irix_oplock_msg_waiting(fd_set *fds)
+{
+ if (oplock_pipe_read == -1)
+ return False;
+
+ return FD_ISSET(oplock_pipe_read,fds);
+}
+
+/****************************************************************************
+ Setup kernel oplocks.
+****************************************************************************/
+
+struct kernel_oplocks *irix_init_kernel_oplocks(void)
+{
+ int pfd[2];
+ static struct kernel_oplocks koplocks;
+
+ if (!irix_oplocks_available())
+ return NULL;
+
+ if(pipe(pfd) != 0) {
+ DEBUG(0,("setup_kernel_oplock_pipe: Unable to create pipe. Error was %s\n",
+ strerror(errno) ));
+ return False;
+ }
+
+ oplock_pipe_read = pfd[0];
+ oplock_pipe_write = pfd[1];
+
+ koplocks.receive_message = irix_oplock_receive_message;
+ koplocks.set_oplock = irix_set_kernel_oplock;
+ koplocks.release_oplock = irix_release_kernel_oplock;
+ koplocks.parse_message = irix_kernel_oplock_parse;
+ koplocks.msg_waiting = irix_oplock_msg_waiting;
+ koplocks.notification_fd = oplock_pipe_read;
+
+ return &koplocks;
+}
+#else
+ void oplock_irix_dummy(void) {}
+#endif /* HAVE_KERNEL_OPLOCKS_IRIX */
diff --git a/source/smbd/oplock_linux.c b/source/smbd/oplock_linux.c
new file mode 100644
index 00000000000..5de9dd56e68
--- /dev/null
+++ b/source/smbd/oplock_linux.c
@@ -0,0 +1,309 @@
+/*
+ Unix SMB/CIFS implementation.
+ kernel oplock processing for Linux
+ 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 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"
+
+#if HAVE_KERNEL_OPLOCKS_LINUX
+
+static SIG_ATOMIC_T signals_received;
+#define FD_PENDING_SIZE 100
+static SIG_ATOMIC_T fd_pending_array[FD_PENDING_SIZE];
+
+#ifndef F_SETLEASE
+#define F_SETLEASE 1024
+#endif
+
+#ifndef F_GETLEASE
+#define F_GETLEASE 1025
+#endif
+
+#ifndef CAP_LEASE
+#define CAP_LEASE 28
+#endif
+
+#ifndef RT_SIGNAL_LEASE
+#define RT_SIGNAL_LEASE (SIGRTMIN+1)
+#endif
+
+#ifndef F_SETSIG
+#define F_SETSIG 10
+#endif
+
+/****************************************************************************
+ Handle a LEASE signal, incrementing the signals_received and blocking the signal.
+****************************************************************************/
+
+static void signal_handler(int sig, siginfo_t *info, void *unused)
+{
+ if (signals_received < FD_PENDING_SIZE - 1) {
+ fd_pending_array[signals_received] = (SIG_ATOMIC_T)info->si_fd;
+ signals_received++;
+ } /* Else signal is lost. */
+ sys_select_signal();
+}
+
+/****************************************************************************
+ Try to gain a linux capability.
+****************************************************************************/
+
+static void set_capability(unsigned capability)
+{
+#ifndef _LINUX_CAPABILITY_VERSION
+#define _LINUX_CAPABILITY_VERSION 0x19980330
+#endif
+ /* these can be removed when they are in glibc headers */
+ struct {
+ uint32 version;
+ int pid;
+ } header;
+ struct {
+ uint32 effective;
+ uint32 permitted;
+ uint32 inheritable;
+ } data;
+
+ header.version = _LINUX_CAPABILITY_VERSION;
+ header.pid = 0;
+
+ if (capget(&header, &data) == -1) {
+ DEBUG(3,("Unable to get kernel capabilities (%s)\n", strerror(errno)));
+ return;
+ }
+
+ data.effective |= (1<<capability);
+
+ if (capset(&header, &data) == -1) {
+ DEBUG(3,("Unable to set %d capability (%s)\n",
+ capability, strerror(errno)));
+ }
+}
+
+/****************************************************************************
+ Call SETLEASE. If we get EACCES then we try setting up the right capability and
+ try again
+****************************************************************************/
+
+static int linux_setlease(int fd, int leasetype)
+{
+ int ret;
+
+ if (fcntl(fd, F_SETSIG, RT_SIGNAL_LEASE) == -1) {
+ DEBUG(3,("Failed to set signal handler for kernel lease\n"));
+ return -1;
+ }
+
+ ret = fcntl(fd, F_SETLEASE, leasetype);
+ if (ret == -1 && errno == EACCES) {
+ set_capability(CAP_LEASE);
+ ret = fcntl(fd, F_SETLEASE, leasetype);
+ }
+
+ return ret;
+}
+
+/****************************************************************************
+ * Deal with the Linux kernel <--> smbd
+ * oplock break protocol.
+****************************************************************************/
+
+static BOOL linux_oplock_receive_message(fd_set *fds, char *buffer, int buffer_len)
+{
+ int fd;
+ struct files_struct *fsp;
+
+ BlockSignals(True, RT_SIGNAL_LEASE);
+ fd = fd_pending_array[0];
+ fsp = file_find_fd(fd);
+ fd_pending_array[0] = (SIG_ATOMIC_T)-1;
+ if (signals_received > 1)
+ memmove((void *)&fd_pending_array[0], (void *)&fd_pending_array[1],
+ sizeof(SIG_ATOMIC_T)*(signals_received-1));
+ signals_received--;
+ /* now we can receive more signals */
+ BlockSignals(False, RT_SIGNAL_LEASE);
+
+ if (fsp == NULL) {
+ DEBUG(0,("Invalid file descriptor %d in kernel oplock break!\n", (int)fd));
+ return False;
+ }
+
+ DEBUG(3,("linux_oplock_receive_message: kernel oplock break request received for \
+dev = %x, inode = %.0f fd = %d, fileid = %lu \n", (unsigned int)fsp->dev, (double)fsp->inode,
+ fd, fsp->file_id));
+
+ /*
+ * Create a kernel oplock break message.
+ */
+
+ /* Setup the message header */
+ SIVAL(buffer,OPBRK_CMD_LEN_OFFSET,KERNEL_OPLOCK_BREAK_MSG_LEN);
+ SSVAL(buffer,OPBRK_CMD_PORT_OFFSET,0);
+
+ buffer += OPBRK_CMD_HEADER_LEN;
+
+ SSVAL(buffer,OPBRK_MESSAGE_CMD_OFFSET,KERNEL_OPLOCK_BREAK_CMD);
+
+ memcpy(buffer + KERNEL_OPLOCK_BREAK_DEV_OFFSET, (char *)&fsp->dev, sizeof(fsp->dev));
+ memcpy(buffer + KERNEL_OPLOCK_BREAK_INODE_OFFSET, (char *)&fsp->inode, sizeof(fsp->inode));
+ memcpy(buffer + KERNEL_OPLOCK_BREAK_FILEID_OFFSET, (char *)&fsp->file_id, sizeof(fsp->file_id));
+
+ return True;
+}
+
+/****************************************************************************
+ Attempt to set an kernel oplock on a file.
+****************************************************************************/
+
+static BOOL linux_set_kernel_oplock(files_struct *fsp, int oplock_type)
+{
+ if (linux_setlease(fsp->fd, F_WRLCK) == -1) {
+ DEBUG(3,("linux_set_kernel_oplock: Refused oplock on file %s, fd = %d, dev = %x, \
+inode = %.0f. (%s)\n",
+ fsp->fsp_name, fsp->fd,
+ (unsigned int)fsp->dev, (double)fsp->inode, strerror(errno)));
+ return False;
+ }
+
+ DEBUG(3,("linux_set_kernel_oplock: got kernel oplock on file %s, dev = %x, inode = %.0f, file_id = %lu\n",
+ fsp->fsp_name, (unsigned int)fsp->dev, (double)fsp->inode, fsp->file_id));
+
+ return True;
+}
+
+/****************************************************************************
+ Release a kernel oplock on a file.
+****************************************************************************/
+
+static void linux_release_kernel_oplock(files_struct *fsp)
+{
+ if (DEBUGLVL(10)) {
+ /*
+ * Check and print out the current kernel
+ * oplock state of this file.
+ */
+ int state = fcntl(fsp->fd, F_GETLEASE, 0);
+ dbgtext("linux_release_kernel_oplock: file %s, dev = %x, inode = %.0f file_id = %lu has kernel \
+oplock state of %x.\n", fsp->fsp_name, (unsigned int)fsp->dev,
+ (double)fsp->inode, fsp->file_id, state );
+ }
+
+ /*
+ * Remove the kernel oplock on this file.
+ */
+ if (linux_setlease(fsp->fd, F_UNLCK) == -1) {
+ if (DEBUGLVL(0)) {
+ dbgtext("linux_release_kernel_oplock: Error when removing kernel oplock on file " );
+ dbgtext("%s, dev = %x, inode = %.0f, file_id = %lu. Error was %s\n",
+ fsp->fsp_name, (unsigned int)fsp->dev,
+ (double)fsp->inode, fsp->file_id, strerror(errno) );
+ }
+ }
+}
+
+/****************************************************************************
+ Parse a kernel oplock message.
+****************************************************************************/
+
+static BOOL linux_kernel_oplock_parse(char *msg_start, int msg_len, SMB_INO_T *inode,
+ SMB_DEV_T *dev, unsigned long *file_id)
+{
+ /* Ensure that the msg length is correct. */
+ if (msg_len != KERNEL_OPLOCK_BREAK_MSG_LEN) {
+ DEBUG(0,("incorrect length for KERNEL_OPLOCK_BREAK_CMD (was %d, should be %lu).\n",
+ msg_len, (unsigned long)KERNEL_OPLOCK_BREAK_MSG_LEN));
+ return False;
+ }
+
+ memcpy((char *)inode, msg_start+KERNEL_OPLOCK_BREAK_INODE_OFFSET, sizeof(*inode));
+ memcpy((char *)dev, msg_start+KERNEL_OPLOCK_BREAK_DEV_OFFSET, sizeof(*dev));
+ memcpy((char *)file_id, msg_start+KERNEL_OPLOCK_BREAK_FILEID_OFFSET, sizeof(*file_id));
+
+ DEBUG(3,("kernel oplock break request for file dev = %x, inode = %.0f, file_id = %lu\n",
+ (unsigned int)*dev, (double)*inode, *file_id));
+
+ return True;
+}
+
+/****************************************************************************
+ See if a oplock message is waiting.
+****************************************************************************/
+
+static BOOL linux_oplock_msg_waiting(fd_set *fds)
+{
+ return signals_received != 0;
+}
+
+/****************************************************************************
+ See if the kernel supports oplocks.
+****************************************************************************/
+
+static BOOL linux_oplocks_available(void)
+{
+ int fd, ret;
+ fd = open("/dev/null", O_RDONLY);
+ if (fd == -1)
+ return False; /* uggh! */
+ ret = fcntl(fd, F_GETLEASE, 0);
+ close(fd);
+ return ret == F_UNLCK;
+}
+
+/****************************************************************************
+ Setup kernel oplocks.
+****************************************************************************/
+
+struct kernel_oplocks *linux_init_kernel_oplocks(void)
+{
+ static struct kernel_oplocks koplocks;
+ struct sigaction act;
+
+ if (!linux_oplocks_available()) {
+ DEBUG(3,("Linux kernel oplocks not available\n"));
+ return NULL;
+ }
+
+ ZERO_STRUCT(act);
+
+ act.sa_handler = NULL;
+ act.sa_sigaction = signal_handler;
+ act.sa_flags = SA_SIGINFO;
+ sigemptyset( &act.sa_mask );
+ if (sigaction(RT_SIGNAL_LEASE, &act, NULL) != 0) {
+ DEBUG(0,("Failed to setup RT_SIGNAL_LEASE handler\n"));
+ return NULL;
+ }
+
+ koplocks.receive_message = linux_oplock_receive_message;
+ koplocks.set_oplock = linux_set_kernel_oplock;
+ koplocks.release_oplock = linux_release_kernel_oplock;
+ koplocks.parse_message = linux_kernel_oplock_parse;
+ koplocks.msg_waiting = linux_oplock_msg_waiting;
+ koplocks.notification_fd = -1;
+
+ /* the signal can start off blocked due to a bug in bash */
+ BlockSignals(False, RT_SIGNAL_LEASE);
+
+ DEBUG(3,("Linux kernel oplocks enabled\n"));
+
+ return &koplocks;
+}
+#else
+ void oplock_linux_dummy(void) {}
+#endif /* HAVE_KERNEL_OPLOCKS_LINUX */
diff --git a/source/smbd/password.c b/source/smbd/password.c
new file mode 100644
index 00000000000..9f6dad423ad
--- /dev/null
+++ b/source/smbd/password.c
@@ -0,0 +1,551 @@
+/*
+ Unix SMB/CIFS implementation.
+ Password and authentication handling
+ 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 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"
+
+/* users from session setup */
+static pstring session_users="";
+
+/* this holds info on user ids that are already validated for this VC */
+static user_struct *validated_users;
+static int next_vuid = VUID_OFFSET;
+static int num_validated_vuids;
+
+extern userdom_struct current_user_info;
+
+
+/****************************************************************************
+ Check if a uid has been validated, and return an pointer to the user_struct
+ if it has. NULL if not. vuid is biased by an offset. This allows us to
+ tell random client vuid's (normally zero) from valid vuids.
+****************************************************************************/
+
+user_struct *get_valid_user_struct(uint16 vuid)
+{
+ user_struct *usp;
+ int count=0;
+
+ if (vuid == UID_FIELD_INVALID)
+ return NULL;
+
+ for (usp=validated_users;usp;usp=usp->next,count++) {
+ if (vuid == usp->vuid) {
+ if (count > 10) {
+ DLIST_PROMOTE(validated_users, usp);
+ }
+ return usp;
+ }
+ }
+
+ return NULL;
+}
+
+/****************************************************************************
+ Invalidate a uid.
+****************************************************************************/
+
+void invalidate_vuid(uint16 vuid)
+{
+ user_struct *vuser = get_valid_user_struct(vuid);
+
+ if (vuser == NULL)
+ return;
+
+ SAFE_FREE(vuser->homedir);
+ SAFE_FREE(vuser->unix_homedir);
+ SAFE_FREE(vuser->logon_script);
+
+ session_yield(vuser);
+ SAFE_FREE(vuser->session_keystr);
+
+ free_server_info(&vuser->server_info);
+
+ data_blob_free(&vuser->session_key);
+
+ DLIST_REMOVE(validated_users, vuser);
+
+ /* clear the vuid from the 'cache' on each connection, and
+ from the vuid 'owner' of connections */
+ conn_clear_vuid_cache(vuid);
+
+ SAFE_FREE(vuser->groups);
+ delete_nt_token(&vuser->nt_user_token);
+ destroy_privilege(&vuser->privs);
+ SAFE_FREE(vuser);
+ num_validated_vuids--;
+}
+
+/****************************************************************************
+ Invalidate all vuid entries for this process.
+****************************************************************************/
+
+void invalidate_all_vuids(void)
+{
+ user_struct *usp, *next=NULL;
+
+ for (usp=validated_users;usp;usp=next) {
+ next = usp->next;
+
+ invalidate_vuid(usp->vuid);
+ }
+}
+
+/**
+ * register that a valid login has been performed, establish 'session'.
+ * @param server_info The token returned from the authentication process.
+ * (now 'owned' by register_vuid)
+ *
+ * @param session_key The User session key for the login session (now also 'owned' by register_vuid)
+ *
+ * @param respose_blob The NT challenge-response, if available. (May be freed after this call)
+ *
+ * @param smb_name The untranslated name of the user
+ *
+ * @return Newly allocated vuid, biased by an offset. (This allows us to
+ * tell random client vuid's (normally zero) from valid vuids.)
+ *
+ */
+
+int register_vuid(auth_serversupplied_info *server_info, DATA_BLOB session_key, DATA_BLOB response_blob, const char *smb_name)
+{
+ user_struct *vuser = NULL;
+
+ /* Ensure no vuid gets registered in share level security. */
+ if(lp_security() == SEC_SHARE) {
+ data_blob_free(&session_key);
+ return UID_FIELD_INVALID;
+ }
+
+ /* Limit allowed vuids to 16bits - VUID_OFFSET. */
+ if (num_validated_vuids >= 0xFFFF-VUID_OFFSET) {
+ data_blob_free(&session_key);
+ return UID_FIELD_INVALID;
+ }
+
+ if((vuser = (user_struct *)malloc( sizeof(user_struct) )) == NULL) {
+ DEBUG(0,("Failed to malloc users struct!\n"));
+ data_blob_free(&session_key);
+ return UID_FIELD_INVALID;
+ }
+
+ ZERO_STRUCTP(vuser);
+
+ /* Allocate a free vuid. Yes this is a linear search... :-) */
+ while( get_valid_user_struct(next_vuid) != NULL ) {
+ next_vuid++;
+ /* Check for vuid wrap. */
+ if (next_vuid == UID_FIELD_INVALID)
+ next_vuid = VUID_OFFSET;
+ }
+
+ DEBUG(10,("register_vuid: allocated vuid = %u\n", (unsigned int)next_vuid ));
+
+ vuser->vuid = next_vuid;
+
+ /* the next functions should be done by a SID mapping system (SMS) as
+ * the new real sam db won't have reference to unix uids or gids
+ */
+
+ vuser->uid = server_info->uid;
+ vuser->gid = server_info->gid;
+
+ vuser->n_groups = server_info->n_groups;
+ if (vuser->n_groups) {
+ if (!(vuser->groups = memdup(server_info->groups, sizeof(gid_t) * vuser->n_groups))) {
+ DEBUG(0,("register_vuid: failed to memdup vuser->groups\n"));
+ data_blob_free(&session_key);
+ free(vuser);
+ free_server_info(&server_info);
+ return UID_FIELD_INVALID;
+ }
+ }
+
+ vuser->guest = server_info->guest;
+ fstrcpy(vuser->user.unix_name, server_info->unix_name);
+
+ /* This is a potentially untrusted username */
+ alpha_strcpy(vuser->user.smb_name, smb_name, ". _-$", sizeof(vuser->user.smb_name));
+
+ fstrcpy(vuser->user.domain, pdb_get_domain(server_info->sam_account));
+ fstrcpy(vuser->user.full_name, pdb_get_fullname(server_info->sam_account));
+
+ {
+ /* Keep the homedir handy */
+ const char *homedir = pdb_get_homedir(server_info->sam_account);
+ const char *logon_script = pdb_get_logon_script(server_info->sam_account);
+
+ if (!IS_SAM_DEFAULT(server_info->sam_account, PDB_UNIXHOMEDIR)) {
+ const char *unix_homedir = pdb_get_unix_homedir(server_info->sam_account);
+ if (unix_homedir) {
+ vuser->unix_homedir = smb_xstrdup(unix_homedir);
+ }
+ } else {
+ struct passwd *passwd = getpwnam_alloc(vuser->user.unix_name);
+ if (passwd) {
+ vuser->unix_homedir = smb_xstrdup(passwd->pw_dir);
+ passwd_free(&passwd);
+ }
+ }
+
+ if (homedir) {
+ vuser->homedir = smb_xstrdup(homedir);
+ }
+ if (logon_script) {
+ vuser->logon_script = smb_xstrdup(logon_script);
+ }
+ }
+
+ vuser->session_key = session_key;
+
+ DEBUG(10,("register_vuid: (%u,%u) %s %s %s guest=%d\n",
+ (unsigned int)vuser->uid,
+ (unsigned int)vuser->gid,
+ vuser->user.unix_name, vuser->user.smb_name, vuser->user.domain, vuser->guest ));
+
+ DEBUG(3, ("User name: %s\tReal name: %s\n",vuser->user.unix_name,vuser->user.full_name));
+
+ if (server_info->ptok) {
+ vuser->nt_user_token = dup_nt_token(server_info->ptok);
+ } else {
+ DEBUG(1, ("server_info does not contain a user_token - cannot continue\n"));
+ free_server_info(&server_info);
+ data_blob_free(&session_key);
+ SAFE_FREE(vuser->homedir);
+ SAFE_FREE(vuser->unix_homedir);
+ SAFE_FREE(vuser->logon_script);
+
+ SAFE_FREE(vuser);
+ return UID_FIELD_INVALID;
+ }
+
+ if (server_info->privs) {
+ init_privilege(&(vuser->privs));
+ dup_priv_set(vuser->privs, server_info->privs);
+ }
+
+ /* use this to keep tabs on all our info from the authentication */
+ vuser->server_info = server_info;
+
+ DEBUG(3,("UNIX uid %d is UNIX user %s, and will be vuid %u\n",(int)vuser->uid,vuser->user.unix_name, vuser->vuid));
+
+ next_vuid++;
+ num_validated_vuids++;
+
+ DLIST_ADD(validated_users, vuser);
+
+ if (!session_claim(vuser)) {
+ DEBUG(1,("Failed to claim session for vuid=%d\n", vuser->vuid));
+ invalidate_vuid(vuser->vuid);
+ return -1;
+ }
+
+ /* Register a home dir service for this user iff
+ (a) This is not a guest connection,
+ (b) we have a home directory defined, and
+ (c) there s not an existing static share by that name */
+
+ if ( (!vuser->guest)
+ && vuser->unix_homedir
+ && *(vuser->unix_homedir)
+ && (lp_servicenumber(vuser->user.unix_name) == -1) )
+ {
+ DEBUG(3, ("Adding/updating homes service for user '%s' using home directory: '%s'\n",
+ vuser->user.unix_name, vuser->unix_homedir));
+
+ vuser->homes_snum = add_home_service(vuser->user.unix_name,
+ vuser->user.unix_name, vuser->unix_homedir);
+ } else {
+ vuser->homes_snum = -1;
+ }
+
+ if (srv_is_signing_negotiated() && !vuser->guest && !srv_signing_started()) {
+ /* Try and turn on server signing on the first non-guest sessionsetup. */
+ srv_set_signing(vuser->session_key, response_blob);
+ }
+
+ /* fill in the current_user_info struct */
+ set_current_user_info( &vuser->user );
+
+
+ return vuser->vuid;
+}
+
+/****************************************************************************
+ Add a name to the session users list.
+****************************************************************************/
+
+void add_session_user(const char *user)
+{
+ fstring suser;
+ struct passwd *passwd;
+
+ if (!(passwd = Get_Pwnam(user)))
+ return;
+
+ fstrcpy(suser,passwd->pw_name);
+
+ if (suser && *suser && !in_list(suser,session_users,False)) {
+ if (strlen(suser) + strlen(session_users) + 2 >= sizeof(pstring)) {
+ DEBUG(1,("Too many session users??\n"));
+ } else {
+ pstrcat(session_users," ");
+ pstrcat(session_users,suser);
+ }
+ }
+}
+
+/****************************************************************************
+ Check if a username is valid.
+****************************************************************************/
+
+BOOL user_ok(const char *user,int snum, gid_t *groups, size_t n_groups)
+{
+ char **valid, **invalid;
+ BOOL ret;
+
+ valid = invalid = NULL;
+ ret = True;
+
+ if (lp_invalid_users(snum)) {
+ str_list_copy(&invalid, lp_invalid_users(snum));
+ if (invalid && str_list_substitute(invalid, "%S", lp_servicename(snum))) {
+ if ( invalid && str_list_sub_basic(invalid, current_user_info.smb_name) ) {
+ ret = !user_in_list(user, (const char **)invalid, groups, n_groups);
+ }
+ }
+ }
+ if (invalid)
+ str_list_free (&invalid);
+
+ if (ret && lp_valid_users(snum)) {
+ str_list_copy(&valid, lp_valid_users(snum));
+ if ( valid && str_list_substitute(valid, "%S", lp_servicename(snum)) ) {
+ if ( valid && str_list_sub_basic(valid, current_user_info.smb_name) ) {
+ ret = user_in_list(user, (const char **)valid, groups, n_groups);
+ }
+ }
+ }
+ if (valid)
+ str_list_free (&valid);
+
+ if (ret && lp_onlyuser(snum)) {
+ char **user_list = str_list_make (lp_username(snum), NULL);
+ if (user_list && str_list_substitute(user_list, "%S", lp_servicename(snum))) {
+ ret = user_in_list(user, (const char **)user_list, groups, n_groups);
+ }
+ if (user_list) str_list_free (&user_list);
+ }
+
+ return(ret);
+}
+
+/****************************************************************************
+ Validate a group username entry. Return the username or NULL.
+****************************************************************************/
+
+static char *validate_group(char *group, DATA_BLOB password,int snum)
+{
+#ifdef HAVE_NETGROUP
+ {
+ char *host, *user, *domain;
+ setnetgrent(group);
+ while (getnetgrent(&host, &user, &domain)) {
+ if (user) {
+ if (user_ok(user, snum, NULL, 0) &&
+ password_ok(user,password)) {
+ endnetgrent();
+ return(user);
+ }
+ }
+ }
+ endnetgrent();
+ }
+#endif
+
+#ifdef HAVE_GETGRENT
+ {
+ struct group *gptr;
+ setgrent();
+ while ((gptr = (struct group *)getgrent())) {
+ if (strequal(gptr->gr_name,group))
+ break;
+ }
+
+ /*
+ * As user_ok can recurse doing a getgrent(), we must
+ * copy the member list into a pstring on the stack before
+ * use. Bug pointed out by leon@eatworms.swmed.edu.
+ */
+
+ if (gptr) {
+ pstring member_list;
+ char *member;
+ size_t copied_len = 0;
+ int i;
+
+ *member_list = '\0';
+ member = member_list;
+
+ for(i = 0; gptr->gr_mem && gptr->gr_mem[i]; i++) {
+ size_t member_len = strlen(gptr->gr_mem[i]) + 1;
+ if( copied_len + member_len < sizeof(pstring)) {
+
+ DEBUG(10,("validate_group: = gr_mem = %s\n", gptr->gr_mem[i]));
+
+ safe_strcpy(member, gptr->gr_mem[i], sizeof(pstring) - copied_len - 1);
+ copied_len += member_len;
+ member += copied_len;
+ } else {
+ *member = '\0';
+ }
+ }
+
+ endgrent();
+
+ member = member_list;
+ while (*member) {
+ static fstring name;
+ fstrcpy(name,member);
+ if (user_ok(name,snum, NULL, 0) &&
+ password_ok(name,password)) {
+ endgrent();
+ return(&name[0]);
+ }
+
+ DEBUG(10,("validate_group = member = %s\n", member));
+
+ member += strlen(member) + 1;
+ }
+ } else {
+ endgrent();
+ return NULL;
+ }
+ }
+#endif
+ return(NULL);
+}
+
+/****************************************************************************
+ Check for authority to login to a service with a given username/password.
+ Note this is *NOT* used when logging on using sessionsetup_and_X.
+****************************************************************************/
+
+BOOL authorise_login(int snum, fstring user, DATA_BLOB password,
+ BOOL *guest)
+{
+ BOOL ok = False;
+
+#if DEBUG_PASSWORD
+ DEBUG(100,("authorise_login: checking authorisation on user=%s pass=%s\n",
+ user,password.data));
+#endif
+
+ *guest = False;
+
+ /* there are several possibilities:
+ 1) login as the given user with given password
+ 2) login as a previously registered username with the given password
+ 3) login as a session list username with the given password
+ 4) login as a previously validated user/password pair
+ 5) login as the "user =" user with given password
+ 6) login as the "user =" user with no password (guest connection)
+ 7) login as guest user with no password
+
+ if the service is guest_only then steps 1 to 5 are skipped
+ */
+
+ /* now check the list of session users */
+ if (!ok) {
+ char *auser;
+ char *user_list = strdup(session_users);
+ if (!user_list)
+ return(False);
+
+ for (auser=strtok(user_list,LIST_SEP); !ok && auser;
+ auser = strtok(NULL,LIST_SEP)) {
+ fstring user2;
+ fstrcpy(user2,auser);
+ if (!user_ok(user2,snum, NULL, 0))
+ continue;
+
+ if (password_ok(user2,password)) {
+ ok = True;
+ fstrcpy(user,user2);
+ DEBUG(3,("authorise_login: ACCEPTED: session list username (%s) \
+and given password ok\n", user));
+ }
+ }
+
+ SAFE_FREE(user_list);
+ }
+
+ /* check the user= fields and the given password */
+ if (!ok && lp_username(snum)) {
+ char *auser;
+ pstring user_list;
+ pstrcpy(user_list,lp_username(snum));
+
+ pstring_sub(user_list,"%S",lp_servicename(snum));
+
+ for (auser=strtok(user_list,LIST_SEP); auser && !ok;
+ auser = strtok(NULL,LIST_SEP)) {
+ if (*auser == '@') {
+ auser = validate_group(auser+1,password,snum);
+ if (auser) {
+ ok = True;
+ fstrcpy(user,auser);
+ DEBUG(3,("authorise_login: ACCEPTED: group username \
+and given password ok (%s)\n", user));
+ }
+ } else {
+ fstring user2;
+ fstrcpy(user2,auser);
+ if (user_ok(user2,snum, NULL, 0) && password_ok(user2,password)) {
+ ok = True;
+ fstrcpy(user,user2);
+ DEBUG(3,("authorise_login: ACCEPTED: user list username \
+and given password ok (%s)\n", user));
+ }
+ }
+ }
+ }
+
+ /* check for a normal guest connection */
+ if (!ok && GUEST_OK(snum)) {
+ fstring guestname;
+ fstrcpy(guestname,lp_guestaccount());
+ if (Get_Pwnam(guestname)) {
+ fstrcpy(user,guestname);
+ ok = True;
+ DEBUG(3,("authorise_login: ACCEPTED: guest account and guest ok (%s)\n",
+ user));
+ } else {
+ DEBUG(0,("authorise_login: Invalid guest account %s??\n",guestname));
+ }
+ *guest = True;
+ }
+
+ if (ok && !user_ok(user, snum, NULL, 0)) {
+ DEBUG(0,("authorise_login: rejected invalid user %s\n",user));
+ ok = False;
+ }
+
+ return(ok);
+}
diff --git a/source/smbd/pipes.c b/source/smbd/pipes.c
new file mode 100644
index 00000000000..f7e9c595c13
--- /dev/null
+++ b/source/smbd/pipes.c
@@ -0,0 +1,264 @@
+/*
+ Unix SMB/CIFS implementation.
+ Pipe SMB reply routines
+ Copyright (C) Andrew Tridgell 1992-1998
+ Copyright (C) Luke Kenneth Casson Leighton 1996-1998
+ Copyright (C) Paul Ashton 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 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.
+*/
+/*
+ This file handles reply_ calls on named pipes that the server
+ makes to handle specific protocols
+*/
+
+
+#include "includes.h"
+
+#define PIPE "\\PIPE\\"
+#define PIPELEN strlen(PIPE)
+
+extern struct pipe_id_info pipe_names[];
+
+/****************************************************************************
+ reply to an open and X on a named pipe
+
+ This code is basically stolen from reply_open_and_X with some
+ wrinkles to handle pipes.
+****************************************************************************/
+int reply_open_pipe_and_X(connection_struct *conn,
+ char *inbuf,char *outbuf,int length,int bufsize)
+{
+ pstring fname;
+ pstring pipe_name;
+ uint16 vuid = SVAL(inbuf, smb_uid);
+ smb_np_struct *p;
+ int smb_ofun = SVAL(inbuf,smb_vwv8);
+ int size=0,fmode=0,mtime=0,rmode=0;
+ int i;
+
+ /* XXXX we need to handle passed times, sattr and flags */
+ srvstr_pull_buf(inbuf, pipe_name, smb_buf(inbuf), sizeof(pipe_name), STR_TERMINATE);
+
+ /* If the name doesn't start \PIPE\ then this is directed */
+ /* at a mailslot or something we really, really don't understand, */
+ /* not just something we really don't understand. */
+ if ( strncmp(pipe_name,PIPE,PIPELEN) != 0 )
+ return(ERROR_DOS(ERRSRV,ERRaccess));
+
+ DEBUG(4,("Opening pipe %s.\n", pipe_name));
+
+ /* See if it is one we want to handle. */
+ for( i = 0; pipe_names[i].client_pipe ; i++ )
+ if( strequal(pipe_name,pipe_names[i].client_pipe) )
+ break;
+
+ if (pipe_names[i].client_pipe == NULL)
+ return(ERROR_BOTH(NT_STATUS_OBJECT_NAME_NOT_FOUND,ERRDOS,ERRbadpipe));
+
+ /* Strip \PIPE\ off the name. */
+ pstrcpy(fname, pipe_name + PIPELEN);
+
+
+#if 0
+ /*
+ * Hack for NT printers... JRA.
+ */
+ if(should_fail_next_srvsvc_open(fname))
+ return(ERROR(ERRSRV,ERRaccess));
+#endif
+
+ /* Known pipes arrive with DIR attribs. Remove it so a regular file */
+ /* can be opened and add it in after the open. */
+ DEBUG(3,("Known pipe %s opening.\n",fname));
+ smb_ofun |= FILE_CREATE_IF_NOT_EXIST;
+
+ p = open_rpc_pipe_p(fname, conn, vuid);
+ if (!p) return(ERROR_DOS(ERRSRV,ERRnofids));
+
+ /* Prepare the reply */
+ set_message(outbuf,15,0,True);
+
+ /* Mark the opened file as an existing named pipe in message mode. */
+ SSVAL(outbuf,smb_vwv9,2);
+ SSVAL(outbuf,smb_vwv10,0xc700);
+
+ if (rmode == 2) {
+ DEBUG(4,("Resetting open result to open from create.\n"));
+ rmode = 1;
+ }
+
+ SSVAL(outbuf,smb_vwv2, p->pnum);
+ SSVAL(outbuf,smb_vwv3,fmode);
+ put_dos_date3(outbuf,smb_vwv4,mtime);
+ SIVAL(outbuf,smb_vwv6,size);
+ SSVAL(outbuf,smb_vwv8,rmode);
+ SSVAL(outbuf,smb_vwv11,0x0001);
+
+ return chain_reply(inbuf,outbuf,length,bufsize);
+}
+
+/****************************************************************************
+ reply to a write on a pipe
+****************************************************************************/
+int reply_pipe_write(char *inbuf,char *outbuf,int length,int dum_bufsize)
+{
+ smb_np_struct *p = get_rpc_pipe_p(inbuf,smb_vwv0);
+ size_t numtowrite = SVAL(inbuf,smb_vwv1);
+ int nwritten;
+ int outsize;
+ char *data;
+
+ if (!p)
+ return(ERROR_DOS(ERRDOS,ERRbadfid));
+
+ data = smb_buf(inbuf) + 3;
+
+ if (numtowrite == 0)
+ nwritten = 0;
+ else
+ nwritten = write_to_pipe(p, data, numtowrite);
+
+ if ((nwritten == 0 && numtowrite != 0) || (nwritten < 0))
+ return (UNIXERROR(ERRDOS,ERRnoaccess));
+
+ outsize = set_message(outbuf,1,0,True);
+
+ SSVAL(outbuf,smb_vwv0,nwritten);
+
+ DEBUG(3,("write-IPC pnum=%04x nwritten=%d\n",
+ p->pnum, nwritten));
+
+ return(outsize);
+}
+
+/****************************************************************************
+ Reply to a write and X.
+
+ This code is basically stolen from reply_write_and_X with some
+ wrinkles to handle pipes.
+****************************************************************************/
+
+int reply_pipe_write_and_X(char *inbuf,char *outbuf,int length,int bufsize)
+{
+ smb_np_struct *p = get_rpc_pipe_p(inbuf,smb_vwv2);
+ size_t numtowrite = SVAL(inbuf,smb_vwv10);
+ int nwritten = -1;
+ int smb_doff = SVAL(inbuf, smb_vwv11);
+ BOOL pipe_start_message_raw = ((SVAL(inbuf, smb_vwv7) & (PIPE_START_MESSAGE|PIPE_RAW_MODE)) ==
+ (PIPE_START_MESSAGE|PIPE_RAW_MODE));
+ char *data;
+
+ if (!p)
+ return(ERROR_DOS(ERRDOS,ERRbadfid));
+
+ data = smb_base(inbuf) + smb_doff;
+
+ if (numtowrite == 0)
+ nwritten = 0;
+ else {
+ if(pipe_start_message_raw) {
+ /*
+ * For the start of a message in named pipe byte mode,
+ * the first two bytes are a length-of-pdu field. Ignore
+ * them (we don't trust the client. JRA.
+ */
+ if(numtowrite < 2) {
+ DEBUG(0,("reply_pipe_write_and_X: start of message set and not enough data sent.(%u)\n",
+ (unsigned int)numtowrite ));
+ return (UNIXERROR(ERRDOS,ERRnoaccess));
+ }
+
+ data += 2;
+ numtowrite -= 2;
+ }
+ nwritten = write_to_pipe(p, data, numtowrite);
+ }
+
+ if ((nwritten == 0 && numtowrite != 0) || (nwritten < 0))
+ return (UNIXERROR(ERRDOS,ERRnoaccess));
+
+ set_message(outbuf,6,0,True);
+
+ nwritten = (pipe_start_message_raw ? nwritten + 2 : nwritten);
+ SSVAL(outbuf,smb_vwv2,nwritten);
+
+ DEBUG(3,("writeX-IPC pnum=%04x nwritten=%d\n",
+ p->pnum, nwritten));
+
+ return chain_reply(inbuf,outbuf,length,bufsize);
+}
+
+/****************************************************************************
+ reply to a read and X
+
+ This code is basically stolen from reply_read_and_X with some
+ wrinkles to handle pipes.
+****************************************************************************/
+int reply_pipe_read_and_X(char *inbuf,char *outbuf,int length,int bufsize)
+{
+ smb_np_struct *p = get_rpc_pipe_p(inbuf,smb_vwv2);
+ int smb_maxcnt = SVAL(inbuf,smb_vwv5);
+ int smb_mincnt = SVAL(inbuf,smb_vwv6);
+ int nread = -1;
+ char *data;
+ BOOL unused;
+
+ /* we don't use the offset given to use for pipe reads. This
+ is deliberate, instead we always return the next lump of
+ data on the pipe */
+#if 0
+ uint32 smb_offs = IVAL(inbuf,smb_vwv3);
+#endif
+
+ if (!p)
+ return(ERROR_DOS(ERRDOS,ERRbadfid));
+
+ set_message(outbuf,12,0,True);
+ data = smb_buf(outbuf);
+
+ nread = read_from_pipe(p, data, smb_maxcnt, &unused);
+
+ if (nread < 0)
+ return(UNIXERROR(ERRDOS,ERRnoaccess));
+
+ SSVAL(outbuf,smb_vwv5,nread);
+ SSVAL(outbuf,smb_vwv6,smb_offset(data,outbuf));
+ SSVAL(smb_buf(outbuf),-2,nread);
+
+ DEBUG(3,("readX-IPC pnum=%04x min=%d max=%d nread=%d\n",
+ p->pnum, smb_mincnt, smb_maxcnt, nread));
+
+ return chain_reply(inbuf,outbuf,length,bufsize);
+}
+
+/****************************************************************************
+ reply to a close
+****************************************************************************/
+int reply_pipe_close(connection_struct *conn, char *inbuf,char *outbuf)
+{
+ smb_np_struct *p = get_rpc_pipe_p(inbuf,smb_vwv0);
+ int outsize = set_message(outbuf,0,0,True);
+
+ if (!p)
+ return(ERROR_DOS(ERRDOS,ERRbadfid));
+
+ DEBUG(5,("reply_pipe_close: pnum:%x\n", p->pnum));
+
+ if (!close_rpc_pipe_hnd(p))
+ return ERROR_DOS(ERRDOS,ERRbadfid);
+
+ return(outsize);
+}
diff --git a/source/smbd/posix_acls.c b/source/smbd/posix_acls.c
new file mode 100644
index 00000000000..620e123e14d
--- /dev/null
+++ b/source/smbd/posix_acls.c
@@ -0,0 +1,3382 @@
+/*
+ Unix SMB/CIFS implementation.
+ SMB NT Security Descriptor / Unix permission conversion.
+ Copyright (C) Jeremy Allison 1994-2000.
+ Copyright (C) Andreas Gruenbacher 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 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"
+
+/****************************************************************************
+ Data structures representing the internal ACE format.
+****************************************************************************/
+
+enum ace_owner {UID_ACE, GID_ACE, WORLD_ACE};
+enum ace_attribute {ALLOW_ACE, DENY_ACE}; /* Used for incoming NT ACLS. */
+
+typedef union posix_id {
+ uid_t uid;
+ gid_t gid;
+ int world;
+} posix_id;
+
+typedef struct canon_ace {
+ struct canon_ace *next, *prev;
+ SMB_ACL_TAG_T type;
+ mode_t perms; /* Only use S_I(R|W|X)USR mode bits here. */
+ DOM_SID trustee;
+ enum ace_owner owner_type;
+ enum ace_attribute attr;
+ posix_id unix_ug;
+ BOOL inherited;
+} canon_ace;
+
+#define ALL_ACE_PERMS (S_IRUSR|S_IWUSR|S_IXUSR)
+
+/*
+ * EA format of user.SAMBA_PAI (Samba_Posix_Acl_Interitance)
+ * attribute on disk.
+ *
+ * | 1 | 1 | 2 | 2 | ....
+ * +------+------+-------------+---------------------+-------------+--------------------+
+ * | vers | flag | num_entries | num_default_entries | ..entries.. | default_entries... |
+ * +------+------+-------------+---------------------+-------------+--------------------+
+ */
+
+#define PAI_VERSION_OFFSET 0
+#define PAI_FLAG_OFFSET 1
+#define PAI_NUM_ENTRIES_OFFSET 2
+#define PAI_NUM_DEFAULT_ENTRIES_OFFSET 4
+#define PAI_ENTRIES_BASE 6
+
+#define PAI_VERSION 1
+#define PAI_ACL_FLAG_PROTECTED 0x1
+#define PAI_ENTRY_LENGTH 5
+
+/*
+ * In memory format of user.SAMBA_PAI attribute.
+ */
+
+struct pai_entry {
+ struct pai_entry *next, *prev;
+ enum ace_owner owner_type;
+ posix_id unix_ug;
+};
+
+struct pai_val {
+ BOOL protected;
+ unsigned int num_entries;
+ struct pai_entry *entry_list;
+ unsigned int num_def_entries;
+ struct pai_entry *def_entry_list;
+};
+
+/************************************************************************
+ Return a uint32 of the pai_entry principal.
+************************************************************************/
+
+static uint32 get_pai_entry_val(struct pai_entry *paie)
+{
+ switch (paie->owner_type) {
+ case UID_ACE:
+ DEBUG(10,("get_pai_entry_val: uid = %u\n", (unsigned int)paie->unix_ug.uid ));
+ return (uint32)paie->unix_ug.uid;
+ case GID_ACE:
+ DEBUG(10,("get_pai_entry_val: gid = %u\n", (unsigned int)paie->unix_ug.gid ));
+ return (uint32)paie->unix_ug.gid;
+ case WORLD_ACE:
+ default:
+ DEBUG(10,("get_pai_entry_val: world ace\n"));
+ return (uint32)-1;
+ }
+}
+
+/************************************************************************
+ Return a uint32 of the entry principal.
+************************************************************************/
+
+static uint32 get_entry_val(canon_ace *ace_entry)
+{
+ switch (ace_entry->owner_type) {
+ case UID_ACE:
+ DEBUG(10,("get_entry_val: uid = %u\n", (unsigned int)ace_entry->unix_ug.uid ));
+ return (uint32)ace_entry->unix_ug.uid;
+ case GID_ACE:
+ DEBUG(10,("get_entry_val: gid = %u\n", (unsigned int)ace_entry->unix_ug.gid ));
+ return (uint32)ace_entry->unix_ug.gid;
+ case WORLD_ACE:
+ default:
+ DEBUG(10,("get_entry_val: world ace\n"));
+ return (uint32)-1;
+ }
+}
+
+/************************************************************************
+ Count the inherited entries.
+************************************************************************/
+
+static unsigned int num_inherited_entries(canon_ace *ace_list)
+{
+ unsigned int num_entries = 0;
+
+ for (; ace_list; ace_list = ace_list->next)
+ if (ace_list->inherited)
+ num_entries++;
+ return num_entries;
+}
+
+/************************************************************************
+ Create the on-disk format. Caller must free.
+************************************************************************/
+
+static char *create_pai_buf(canon_ace *file_ace_list, canon_ace *dir_ace_list, BOOL protected, size_t *store_size)
+{
+ char *pai_buf = NULL;
+ canon_ace *ace_list = NULL;
+ char *entry_offset = NULL;
+ unsigned int num_entries = 0;
+ unsigned int num_def_entries = 0;
+
+ for (ace_list = file_ace_list; ace_list; ace_list = ace_list->next)
+ if (ace_list->inherited)
+ num_entries++;
+
+ for (ace_list = dir_ace_list; ace_list; ace_list = ace_list->next)
+ if (ace_list->inherited)
+ num_def_entries++;
+
+ DEBUG(10,("create_pai_buf: num_entries = %u, num_def_entries = %u\n", num_entries, num_def_entries ));
+
+ *store_size = PAI_ENTRIES_BASE + ((num_entries + num_def_entries)*PAI_ENTRY_LENGTH);
+
+ pai_buf = malloc(*store_size);
+ if (!pai_buf) {
+ return NULL;
+ }
+
+ /* Set up the header. */
+ memset(pai_buf, '\0', PAI_ENTRIES_BASE);
+ SCVAL(pai_buf,PAI_VERSION_OFFSET,PAI_VERSION);
+ SCVAL(pai_buf,PAI_FLAG_OFFSET,(protected ? PAI_ACL_FLAG_PROTECTED : 0));
+ SSVAL(pai_buf,PAI_NUM_ENTRIES_OFFSET,num_entries);
+ SSVAL(pai_buf,PAI_NUM_DEFAULT_ENTRIES_OFFSET,num_def_entries);
+
+ entry_offset = pai_buf + PAI_ENTRIES_BASE;
+
+ for (ace_list = dir_ace_list; ace_list; ace_list = ace_list->next) {
+ if (ace_list->inherited) {
+ uint8 type_val = (unsigned char)ace_list->owner_type;
+ uint32 entry_val = get_entry_val(ace_list);
+
+ SCVAL(entry_offset,0,type_val);
+ SIVAL(entry_offset,1,entry_val);
+ entry_offset += PAI_ENTRY_LENGTH;
+ }
+ }
+
+ for (ace_list = file_ace_list; ace_list; ace_list = ace_list->next) {
+ if (ace_list->inherited) {
+ uint8 type_val = (unsigned char)ace_list->owner_type;
+ uint32 entry_val = get_entry_val(ace_list);
+
+ SCVAL(entry_offset,0,type_val);
+ SIVAL(entry_offset,1,entry_val);
+ entry_offset += PAI_ENTRY_LENGTH;
+ }
+ }
+
+ return pai_buf;
+}
+
+/************************************************************************
+ Store the user.SAMBA_PAI attribute on disk.
+************************************************************************/
+
+static void store_inheritance_attributes(files_struct *fsp, canon_ace *file_ace_list,
+ canon_ace *dir_ace_list, BOOL protected)
+{
+ int ret;
+ size_t store_size;
+ char *pai_buf;
+
+ if (!lp_map_acl_inherit(SNUM(fsp->conn)))
+ return;
+
+ /*
+ * Don't store if this ACL isn't protected and
+ * none of the entries in it are marked as inherited.
+ */
+
+ if (!protected && num_inherited_entries(file_ace_list) == 0 && num_inherited_entries(dir_ace_list) == 0) {
+ /* Instead just remove the attribute if it exists. */
+ if (fsp->fd != -1)
+ SMB_VFS_FREMOVEXATTR(fsp, fsp->fd, SAMBA_POSIX_INHERITANCE_EA_NAME);
+ else
+ SMB_VFS_REMOVEXATTR(fsp->conn, fsp->fsp_name, SAMBA_POSIX_INHERITANCE_EA_NAME);
+ return;
+ }
+
+ pai_buf = create_pai_buf(file_ace_list, dir_ace_list, protected, &store_size);
+
+ if (fsp->fd != -1)
+ ret = SMB_VFS_FSETXATTR(fsp, fsp->fd, SAMBA_POSIX_INHERITANCE_EA_NAME,
+ pai_buf, store_size, 0);
+ else
+ ret = SMB_VFS_SETXATTR(fsp->conn,fsp->fsp_name, SAMBA_POSIX_INHERITANCE_EA_NAME,
+ pai_buf, store_size, 0);
+
+ SAFE_FREE(pai_buf);
+
+ DEBUG(10,("store_inheritance_attribute:%s for file %s\n", protected ? " (protected)" : "", fsp->fsp_name));
+ if (ret == -1 && errno != ENOSYS)
+ DEBUG(1,("store_inheritance_attribute: Error %s\n", strerror(errno) ));
+}
+
+/************************************************************************
+ Delete the in memory inheritance info.
+************************************************************************/
+
+static void free_inherited_info(struct pai_val *pal)
+{
+ if (pal) {
+ struct pai_entry *paie, *paie_next;
+ for (paie = pal->entry_list; paie; paie = paie_next) {
+ paie_next = paie->next;
+ SAFE_FREE(paie);
+ }
+ for (paie = pal->def_entry_list; paie; paie = paie_next) {
+ paie_next = paie->next;
+ SAFE_FREE(paie);
+ }
+ SAFE_FREE(pal);
+ }
+}
+
+/************************************************************************
+ Was this ACL protected ?
+************************************************************************/
+
+static BOOL get_protected_flag(struct pai_val *pal)
+{
+ if (!pal)
+ return False;
+ return pal->protected;
+}
+
+/************************************************************************
+ Was this ACE inherited ?
+************************************************************************/
+
+static BOOL get_inherited_flag(struct pai_val *pal, canon_ace *ace_entry, BOOL default_ace)
+{
+ struct pai_entry *paie;
+
+ if (!pal)
+ return False;
+
+ /* If the entry exists it is inherited. */
+ for (paie = (default_ace ? pal->def_entry_list : pal->entry_list); paie; paie = paie->next) {
+ if (ace_entry->owner_type == paie->owner_type &&
+ get_entry_val(ace_entry) == get_pai_entry_val(paie))
+ return True;
+ }
+ return False;
+}
+
+/************************************************************************
+ Ensure an attribute just read is valid.
+************************************************************************/
+
+static BOOL check_pai_ok(char *pai_buf, size_t pai_buf_data_size)
+{
+ uint16 num_entries;
+ uint16 num_def_entries;
+
+ if (pai_buf_data_size < PAI_ENTRIES_BASE) {
+ /* Corrupted - too small. */
+ return False;
+ }
+
+ if (CVAL(pai_buf,PAI_VERSION_OFFSET) != PAI_VERSION)
+ return False;
+
+ num_entries = SVAL(pai_buf,PAI_NUM_ENTRIES_OFFSET);
+ num_def_entries = SVAL(pai_buf,PAI_NUM_DEFAULT_ENTRIES_OFFSET);
+
+ /* Check the entry lists match. */
+ /* Each entry is 5 bytes (type plus 4 bytes of uid or gid). */
+
+ if (((num_entries + num_def_entries)*PAI_ENTRY_LENGTH) + PAI_ENTRIES_BASE != pai_buf_data_size)
+ return False;
+
+ return True;
+}
+
+
+/************************************************************************
+ Convert to in-memory format.
+************************************************************************/
+
+static struct pai_val *create_pai_val(char *buf, size_t size)
+{
+ char *entry_offset;
+ struct pai_val *paiv = NULL;
+ int i;
+
+ if (!check_pai_ok(buf, size))
+ return NULL;
+
+ paiv = malloc(sizeof(struct pai_val));
+ if (!paiv)
+ return NULL;
+
+ memset(paiv, '\0', sizeof(struct pai_val));
+
+ paiv->protected = (CVAL(buf,PAI_FLAG_OFFSET) == PAI_ACL_FLAG_PROTECTED);
+
+ paiv->num_entries = SVAL(buf,PAI_NUM_ENTRIES_OFFSET);
+ paiv->num_def_entries = SVAL(buf,PAI_NUM_DEFAULT_ENTRIES_OFFSET);
+
+ entry_offset = buf + PAI_ENTRIES_BASE;
+
+ DEBUG(10,("create_pai_val:%s num_entries = %u, num_def_entries = %u\n",
+ paiv->protected ? " (protected)" : "", paiv->num_entries, paiv->num_def_entries ));
+
+ for (i = 0; i < paiv->num_entries; i++) {
+ struct pai_entry *paie;
+
+ paie = malloc(sizeof(struct pai_entry));
+ if (!paie) {
+ free_inherited_info(paiv);
+ return NULL;
+ }
+
+ paie->owner_type = (enum ace_owner)CVAL(entry_offset,0);
+ switch( paie->owner_type) {
+ case UID_ACE:
+ paie->unix_ug.uid = (uid_t)IVAL(entry_offset,1);
+ DEBUG(10,("create_pai_val: uid = %u\n", (unsigned int)paie->unix_ug.uid ));
+ break;
+ case GID_ACE:
+ paie->unix_ug.gid = (gid_t)IVAL(entry_offset,1);
+ DEBUG(10,("create_pai_val: gid = %u\n", (unsigned int)paie->unix_ug.gid ));
+ break;
+ case WORLD_ACE:
+ paie->unix_ug.world = -1;
+ DEBUG(10,("create_pai_val: world ace\n"));
+ break;
+ default:
+ free_inherited_info(paiv);
+ return NULL;
+ }
+ entry_offset += PAI_ENTRY_LENGTH;
+ DLIST_ADD(paiv->entry_list, paie);
+ }
+
+ for (i = 0; i < paiv->num_def_entries; i++) {
+ struct pai_entry *paie;
+
+ paie = malloc(sizeof(struct pai_entry));
+ if (!paie) {
+ free_inherited_info(paiv);
+ return NULL;
+ }
+
+ paie->owner_type = (enum ace_owner)CVAL(entry_offset,0);
+ switch( paie->owner_type) {
+ case UID_ACE:
+ paie->unix_ug.uid = (uid_t)IVAL(entry_offset,1);
+ DEBUG(10,("create_pai_val: (def) uid = %u\n", (unsigned int)paie->unix_ug.uid ));
+ break;
+ case GID_ACE:
+ paie->unix_ug.gid = (gid_t)IVAL(entry_offset,1);
+ DEBUG(10,("create_pai_val: (def) gid = %u\n", (unsigned int)paie->unix_ug.gid ));
+ break;
+ case WORLD_ACE:
+ paie->unix_ug.world = -1;
+ DEBUG(10,("create_pai_val: (def) world ace\n"));
+ break;
+ default:
+ free_inherited_info(paiv);
+ return NULL;
+ }
+ entry_offset += PAI_ENTRY_LENGTH;
+ DLIST_ADD(paiv->def_entry_list, paie);
+ }
+
+ return paiv;
+}
+
+/************************************************************************
+ Load the user.SAMBA_PAI attribute.
+************************************************************************/
+
+static struct pai_val *load_inherited_info(files_struct *fsp)
+{
+ char *pai_buf;
+ size_t pai_buf_size = 1024;
+ struct pai_val *paiv = NULL;
+ ssize_t ret;
+
+ if (!lp_map_acl_inherit(SNUM(fsp->conn)))
+ return NULL;
+
+ if ((pai_buf = malloc(pai_buf_size)) == NULL)
+ return NULL;
+
+ do {
+ if (fsp->fd != -1)
+ ret = SMB_VFS_FGETXATTR(fsp, fsp->fd, SAMBA_POSIX_INHERITANCE_EA_NAME,
+ pai_buf, pai_buf_size);
+ else
+ ret = SMB_VFS_GETXATTR(fsp->conn,fsp->fsp_name,SAMBA_POSIX_INHERITANCE_EA_NAME,
+ pai_buf, pai_buf_size);
+
+ if (ret == -1) {
+ if (errno != ERANGE) {
+ break;
+ }
+ /* Buffer too small - enlarge it. */
+ pai_buf_size *= 2;
+ SAFE_FREE(pai_buf);
+ if ((pai_buf = malloc(pai_buf_size)) == NULL)
+ return NULL;
+ }
+ } while (ret == -1);
+
+ DEBUG(10,("load_inherited_info: ret = %lu for file %s\n", (unsigned long)ret, fsp->fsp_name));
+
+ if (ret == -1) {
+ /* No attribute or not supported. */
+#if defined(ENOATTR)
+ if (errno != ENOATTR)
+ DEBUG(10,("load_inherited_info: Error %s\n", strerror(errno) ));
+#else
+ if (errno != ENOSYS)
+ DEBUG(10,("load_inherited_info: Error %s\n", strerror(errno) ));
+#endif
+ SAFE_FREE(pai_buf);
+ return NULL;
+ }
+
+ paiv = create_pai_val(pai_buf, ret);
+
+ if (paiv && paiv->protected)
+ DEBUG(10,("load_inherited_info: ACL is protected for file %s\n", fsp->fsp_name));
+
+ SAFE_FREE(pai_buf);
+ return paiv;
+}
+
+/****************************************************************************
+ Functions to manipulate the internal ACE format.
+****************************************************************************/
+
+/****************************************************************************
+ Count a linked list of canonical ACE entries.
+****************************************************************************/
+
+static size_t count_canon_ace_list( canon_ace *list_head )
+{
+ size_t count = 0;
+ canon_ace *ace;
+
+ for (ace = list_head; ace; ace = ace->next)
+ count++;
+
+ return count;
+}
+
+/****************************************************************************
+ Free a linked list of canonical ACE entries.
+****************************************************************************/
+
+static void free_canon_ace_list( canon_ace *list_head )
+{
+ while (list_head) {
+ canon_ace *old_head = list_head;
+ DLIST_REMOVE(list_head, list_head);
+ SAFE_FREE(old_head);
+ }
+}
+
+/****************************************************************************
+ Function to duplicate a canon_ace entry.
+****************************************************************************/
+
+static canon_ace *dup_canon_ace( canon_ace *src_ace)
+{
+ canon_ace *dst_ace = (canon_ace *)malloc(sizeof(canon_ace));
+
+ if (dst_ace == NULL)
+ return NULL;
+
+ *dst_ace = *src_ace;
+ dst_ace->prev = dst_ace->next = NULL;
+ return dst_ace;
+}
+
+/****************************************************************************
+ Print out a canon ace.
+****************************************************************************/
+
+static void print_canon_ace(canon_ace *pace, int num)
+{
+ fstring str;
+
+ dbgtext( "canon_ace index %d. Type = %s ", num, pace->attr == ALLOW_ACE ? "allow" : "deny" );
+ dbgtext( "SID = %s ", sid_to_string( str, &pace->trustee));
+ if (pace->owner_type == UID_ACE) {
+ const char *u_name = uidtoname(pace->unix_ug.uid);
+ dbgtext( "uid %u (%s) ", (unsigned int)pace->unix_ug.uid, u_name );
+ } else if (pace->owner_type == GID_ACE) {
+ char *g_name = gidtoname(pace->unix_ug.gid);
+ dbgtext( "gid %u (%s) ", (unsigned int)pace->unix_ug.gid, g_name );
+ } else
+ dbgtext( "other ");
+ switch (pace->type) {
+ case SMB_ACL_USER:
+ dbgtext( "SMB_ACL_USER ");
+ break;
+ case SMB_ACL_USER_OBJ:
+ dbgtext( "SMB_ACL_USER_OBJ ");
+ break;
+ case SMB_ACL_GROUP:
+ dbgtext( "SMB_ACL_GROUP ");
+ break;
+ case SMB_ACL_GROUP_OBJ:
+ dbgtext( "SMB_ACL_GROUP_OBJ ");
+ break;
+ case SMB_ACL_OTHER:
+ dbgtext( "SMB_ACL_OTHER ");
+ break;
+ }
+ if (pace->inherited)
+ dbgtext( "(inherited) ");
+ dbgtext( "perms ");
+ dbgtext( "%c", pace->perms & S_IRUSR ? 'r' : '-');
+ dbgtext( "%c", pace->perms & S_IWUSR ? 'w' : '-');
+ dbgtext( "%c\n", pace->perms & S_IXUSR ? 'x' : '-');
+}
+
+/****************************************************************************
+ Print out a canon ace list.
+****************************************************************************/
+
+static void print_canon_ace_list(const char *name, canon_ace *ace_list)
+{
+ int count = 0;
+
+ if( DEBUGLVL( 10 )) {
+ dbgtext( "print_canon_ace_list: %s\n", name );
+ for (;ace_list; ace_list = ace_list->next, count++)
+ print_canon_ace(ace_list, count );
+ }
+}
+
+/****************************************************************************
+ Map POSIX ACL perms to canon_ace permissions (a mode_t containing only S_(R|W|X)USR bits).
+****************************************************************************/
+
+static mode_t convert_permset_to_mode_t(connection_struct *conn, SMB_ACL_PERMSET_T permset)
+{
+ mode_t ret = 0;
+
+ ret |= (SMB_VFS_SYS_ACL_GET_PERM(conn, permset, SMB_ACL_READ) ? S_IRUSR : 0);
+ ret |= (SMB_VFS_SYS_ACL_GET_PERM(conn, permset, SMB_ACL_WRITE) ? S_IWUSR : 0);
+ ret |= (SMB_VFS_SYS_ACL_GET_PERM(conn, permset, SMB_ACL_EXECUTE) ? S_IXUSR : 0);
+
+ return ret;
+}
+
+/****************************************************************************
+ Map generic UNIX permissions to canon_ace permissions (a mode_t containing only S_(R|W|X)USR bits).
+****************************************************************************/
+
+static mode_t unix_perms_to_acl_perms(mode_t mode, int r_mask, int w_mask, int x_mask)
+{
+ mode_t ret = 0;
+
+ if (mode & r_mask)
+ ret |= S_IRUSR;
+ if (mode & w_mask)
+ ret |= S_IWUSR;
+ if (mode & x_mask)
+ ret |= S_IXUSR;
+
+ return ret;
+}
+
+/****************************************************************************
+ Map canon_ace permissions (a mode_t containing only S_(R|W|X)USR bits) to
+ an SMB_ACL_PERMSET_T.
+****************************************************************************/
+
+static int map_acl_perms_to_permset(connection_struct *conn, mode_t mode, SMB_ACL_PERMSET_T *p_permset)
+{
+ if (SMB_VFS_SYS_ACL_CLEAR_PERMS(conn, *p_permset) == -1)
+ return -1;
+ if (mode & S_IRUSR) {
+ if (SMB_VFS_SYS_ACL_ADD_PERM(conn, *p_permset, SMB_ACL_READ) == -1)
+ return -1;
+ }
+ if (mode & S_IWUSR) {
+ if (SMB_VFS_SYS_ACL_ADD_PERM(conn, *p_permset, SMB_ACL_WRITE) == -1)
+ return -1;
+ }
+ if (mode & S_IXUSR) {
+ if (SMB_VFS_SYS_ACL_ADD_PERM(conn, *p_permset, SMB_ACL_EXECUTE) == -1)
+ return -1;
+ }
+ return 0;
+}
+/****************************************************************************
+ Function to create owner and group SIDs from a SMB_STRUCT_STAT.
+****************************************************************************/
+
+static void create_file_sids(SMB_STRUCT_STAT *psbuf, DOM_SID *powner_sid, DOM_SID *pgroup_sid)
+{
+ uid_to_sid( powner_sid, psbuf->st_uid );
+ gid_to_sid( pgroup_sid, psbuf->st_gid );
+}
+
+/****************************************************************************
+ Merge aces with a common sid - if both are allow or deny, OR the permissions together and
+ delete the second one. If the first is deny, mask the permissions off and delete the allow
+ if the permissions become zero, delete the deny if the permissions are non zero.
+****************************************************************************/
+
+static void merge_aces( canon_ace **pp_list_head )
+{
+ canon_ace *list_head = *pp_list_head;
+ canon_ace *curr_ace_outer;
+ canon_ace *curr_ace_outer_next;
+
+ /*
+ * First, merge allow entries with identical SIDs, and deny entries
+ * with identical SIDs.
+ */
+
+ for (curr_ace_outer = list_head; curr_ace_outer; curr_ace_outer = curr_ace_outer_next) {
+ canon_ace *curr_ace;
+ canon_ace *curr_ace_next;
+
+ curr_ace_outer_next = curr_ace_outer->next; /* Save the link in case we delete. */
+
+ for (curr_ace = curr_ace_outer->next; curr_ace; curr_ace = curr_ace_next) {
+
+ curr_ace_next = curr_ace->next; /* Save the link in case of delete. */
+
+ if (sid_equal(&curr_ace->trustee, &curr_ace_outer->trustee) &&
+ (curr_ace->attr == curr_ace_outer->attr)) {
+
+ if( DEBUGLVL( 10 )) {
+ dbgtext("merge_aces: Merging ACE's\n");
+ print_canon_ace( curr_ace_outer, 0);
+ print_canon_ace( curr_ace, 0);
+ }
+
+ /* Merge two allow or two deny ACE's. */
+
+ curr_ace_outer->perms |= curr_ace->perms;
+ DLIST_REMOVE(list_head, curr_ace);
+ SAFE_FREE(curr_ace);
+ curr_ace_outer_next = curr_ace_outer->next; /* We may have deleted the link. */
+ }
+ }
+ }
+
+ /*
+ * Now go through and mask off allow permissions with deny permissions.
+ * We can delete either the allow or deny here as we know that each SID
+ * appears only once in the list.
+ */
+
+ for (curr_ace_outer = list_head; curr_ace_outer; curr_ace_outer = curr_ace_outer_next) {
+ canon_ace *curr_ace;
+ canon_ace *curr_ace_next;
+
+ curr_ace_outer_next = curr_ace_outer->next; /* Save the link in case we delete. */
+
+ for (curr_ace = curr_ace_outer->next; curr_ace; curr_ace = curr_ace_next) {
+
+ curr_ace_next = curr_ace->next; /* Save the link in case of delete. */
+
+ /*
+ * Subtract ACE's with different entries. Due to the ordering constraints
+ * we've put on the ACL, we know the deny must be the first one.
+ */
+
+ if (sid_equal(&curr_ace->trustee, &curr_ace_outer->trustee) &&
+ (curr_ace_outer->attr == DENY_ACE) && (curr_ace->attr == ALLOW_ACE)) {
+
+ if( DEBUGLVL( 10 )) {
+ dbgtext("merge_aces: Masking ACE's\n");
+ print_canon_ace( curr_ace_outer, 0);
+ print_canon_ace( curr_ace, 0);
+ }
+
+ curr_ace->perms &= ~curr_ace_outer->perms;
+
+ if (curr_ace->perms == 0) {
+
+ /*
+ * The deny overrides the allow. Remove the allow.
+ */
+
+ DLIST_REMOVE(list_head, curr_ace);
+ SAFE_FREE(curr_ace);
+ curr_ace_outer_next = curr_ace_outer->next; /* We may have deleted the link. */
+
+ } else {
+
+ /*
+ * Even after removing permissions, there
+ * are still allow permissions - delete the deny.
+ * It is safe to delete the deny here,
+ * as we are guarenteed by the deny first
+ * ordering that all the deny entries for
+ * this SID have already been merged into one
+ * before we can get to an allow ace.
+ */
+
+ DLIST_REMOVE(list_head, curr_ace_outer);
+ SAFE_FREE(curr_ace_outer);
+ break;
+ }
+ }
+
+ } /* end for curr_ace */
+ } /* end for curr_ace_outer */
+
+ /* We may have modified the list. */
+
+ *pp_list_head = list_head;
+}
+
+/****************************************************************************
+ Check if we need to return NT4.x compatible ACL entries.
+****************************************************************************/
+
+static BOOL nt4_compatible_acls(void)
+{
+ const char *compat = lp_acl_compatibility();
+
+ if (*compat == '\0') {
+ enum remote_arch_types ra_type = get_remote_arch();
+
+ /* Automatically adapt to client */
+ return (ra_type <= RA_WINNT);
+ } else
+ return (strequal(compat, "winnt"));
+}
+
+
+/****************************************************************************
+ Map canon_ace perms to permission bits NT.
+ The attr element is not used here - we only process deny entries on set,
+ not get. Deny entries are implicit on get with ace->perms = 0.
+****************************************************************************/
+
+static SEC_ACCESS map_canon_ace_perms(int *pacl_type, DOM_SID *powner_sid, canon_ace *ace)
+{
+ SEC_ACCESS sa;
+ uint32 nt_mask = 0;
+
+ *pacl_type = SEC_ACE_TYPE_ACCESS_ALLOWED;
+
+ if ((ace->perms & ALL_ACE_PERMS) == ALL_ACE_PERMS) {
+ nt_mask = UNIX_ACCESS_RWX;
+ } else if ((ace->perms & ALL_ACE_PERMS) == (mode_t)0) {
+ /*
+ * Windows NT refuses to display ACEs with no permissions in them (but
+ * they are perfectly legal with Windows 2000). If the ACE has empty
+ * permissions we cannot use 0, so we use the otherwise unused
+ * WRITE_OWNER permission, which we ignore when we set an ACL.
+ * We abstract this into a #define of UNIX_ACCESS_NONE to allow this
+ * to be changed in the future.
+ */
+
+ if (nt4_compatible_acls())
+ nt_mask = UNIX_ACCESS_NONE;
+ else
+ nt_mask = 0;
+ } else {
+ nt_mask |= ((ace->perms & S_IRUSR) ? UNIX_ACCESS_R : 0 );
+ nt_mask |= ((ace->perms & S_IWUSR) ? UNIX_ACCESS_W : 0 );
+ nt_mask |= ((ace->perms & S_IXUSR) ? UNIX_ACCESS_X : 0 );
+ }
+
+ DEBUG(10,("map_canon_ace_perms: Mapped (UNIX) %x to (NT) %x\n",
+ (unsigned int)ace->perms, (unsigned int)nt_mask ));
+
+ init_sec_access(&sa,nt_mask);
+ return sa;
+}
+
+/****************************************************************************
+ Map NT perms to a UNIX mode_t.
+****************************************************************************/
+
+#define FILE_SPECIFIC_READ_BITS (FILE_READ_DATA|FILE_READ_EA|FILE_READ_ATTRIBUTES)
+#define FILE_SPECIFIC_WRITE_BITS (FILE_WRITE_DATA|FILE_APPEND_DATA|FILE_WRITE_EA|FILE_WRITE_ATTRIBUTES)
+#define FILE_SPECIFIC_EXECUTE_BITS (FILE_EXECUTE)
+
+static mode_t map_nt_perms( SEC_ACCESS sec_access, int type)
+{
+ mode_t mode = 0;
+
+ switch(type) {
+ case S_IRUSR:
+ if(sec_access.mask & GENERIC_ALL_ACCESS)
+ mode = S_IRUSR|S_IWUSR|S_IXUSR;
+ else {
+ mode |= (sec_access.mask & (GENERIC_READ_ACCESS|FILE_SPECIFIC_READ_BITS)) ? S_IRUSR : 0;
+ mode |= (sec_access.mask & (GENERIC_WRITE_ACCESS|FILE_SPECIFIC_WRITE_BITS)) ? S_IWUSR : 0;
+ mode |= (sec_access.mask & (GENERIC_EXECUTE_ACCESS|FILE_SPECIFIC_EXECUTE_BITS)) ? S_IXUSR : 0;
+ }
+ break;
+ case S_IRGRP:
+ if(sec_access.mask & GENERIC_ALL_ACCESS)
+ mode = S_IRGRP|S_IWGRP|S_IXGRP;
+ else {
+ mode |= (sec_access.mask & (GENERIC_READ_ACCESS|FILE_SPECIFIC_READ_BITS)) ? S_IRGRP : 0;
+ mode |= (sec_access.mask & (GENERIC_WRITE_ACCESS|FILE_SPECIFIC_WRITE_BITS)) ? S_IWGRP : 0;
+ mode |= (sec_access.mask & (GENERIC_EXECUTE_ACCESS|FILE_SPECIFIC_EXECUTE_BITS)) ? S_IXGRP : 0;
+ }
+ break;
+ case S_IROTH:
+ if(sec_access.mask & GENERIC_ALL_ACCESS)
+ mode = S_IROTH|S_IWOTH|S_IXOTH;
+ else {
+ mode |= (sec_access.mask & (GENERIC_READ_ACCESS|FILE_SPECIFIC_READ_BITS)) ? S_IROTH : 0;
+ mode |= (sec_access.mask & (GENERIC_WRITE_ACCESS|FILE_SPECIFIC_WRITE_BITS)) ? S_IWOTH : 0;
+ mode |= (sec_access.mask & (GENERIC_EXECUTE_ACCESS|FILE_SPECIFIC_EXECUTE_BITS)) ? S_IXOTH : 0;
+ }
+ break;
+ }
+
+ return mode;
+}
+
+/****************************************************************************
+ Unpack a SEC_DESC into a UNIX owner and group.
+****************************************************************************/
+
+static BOOL unpack_nt_owners(SMB_STRUCT_STAT *psbuf, uid_t *puser, gid_t *pgrp, uint32 security_info_sent, SEC_DESC *psd)
+{
+ DOM_SID owner_sid;
+ DOM_SID grp_sid;
+
+ *puser = (uid_t)-1;
+ *pgrp = (gid_t)-1;
+
+ if(security_info_sent == 0) {
+ DEBUG(0,("unpack_nt_owners: no security info sent !\n"));
+ return True;
+ }
+
+ /*
+ * Validate the owner and group SID's.
+ */
+
+ memset(&owner_sid, '\0', sizeof(owner_sid));
+ memset(&grp_sid, '\0', sizeof(grp_sid));
+
+ DEBUG(5,("unpack_nt_owners: validating owner_sids.\n"));
+
+ /*
+ * Don't immediately fail if the owner sid cannot be validated.
+ * This may be a group chown only set.
+ */
+
+ if (security_info_sent & OWNER_SECURITY_INFORMATION) {
+ sid_copy(&owner_sid, psd->owner_sid);
+ if (!NT_STATUS_IS_OK(sid_to_uid(&owner_sid, puser))) {
+#if ACL_FORCE_UNMAPPABLE
+ /* this allows take ownership to work reasonably */
+ extern struct current_user current_user;
+ *puser = current_user.uid;
+#else
+ DEBUG(3,("unpack_nt_owners: unable to validate owner sid for %s\n",
+ sid_string_static(&owner_sid)));
+ return False;
+#endif
+ }
+ }
+
+ /*
+ * Don't immediately fail if the group sid cannot be validated.
+ * This may be an owner chown only set.
+ */
+
+ if (security_info_sent & GROUP_SECURITY_INFORMATION) {
+ sid_copy(&grp_sid, psd->grp_sid);
+ if (!NT_STATUS_IS_OK(sid_to_gid( &grp_sid, pgrp))) {
+#if ACL_FORCE_UNMAPPABLE
+ /* this allows take group ownership to work reasonably */
+ extern struct current_user current_user;
+ *pgrp = current_user.gid;
+#else
+ DEBUG(3,("unpack_nt_owners: unable to validate group sid.\n"));
+ return False;
+#endif
+ }
+ }
+
+ DEBUG(5,("unpack_nt_owners: owner_sids validated.\n"));
+
+ return True;
+}
+
+/****************************************************************************
+ Ensure the enforced permissions for this share apply.
+****************************************************************************/
+
+static void apply_default_perms(files_struct *fsp, canon_ace *pace, mode_t type)
+{
+ int snum = SNUM(fsp->conn);
+ mode_t and_bits = (mode_t)0;
+ mode_t or_bits = (mode_t)0;
+
+ /* Get the initial bits to apply. */
+
+ if (fsp->is_directory) {
+ and_bits = lp_dir_security_mask(snum);
+ or_bits = lp_force_dir_security_mode(snum);
+ } else {
+ and_bits = lp_security_mask(snum);
+ or_bits = lp_force_security_mode(snum);
+ }
+
+ /* Now bounce them into the S_USR space. */
+ switch(type) {
+ case S_IRUSR:
+ /* Ensure owner has read access. */
+ pace->perms |= S_IRUSR;
+ if (fsp->is_directory)
+ pace->perms |= (S_IWUSR|S_IXUSR);
+ and_bits = unix_perms_to_acl_perms(and_bits, S_IRUSR, S_IWUSR, S_IXUSR);
+ or_bits = unix_perms_to_acl_perms(or_bits, S_IRUSR, S_IWUSR, S_IXUSR);
+ break;
+ case S_IRGRP:
+ and_bits = unix_perms_to_acl_perms(and_bits, S_IRGRP, S_IWGRP, S_IXGRP);
+ or_bits = unix_perms_to_acl_perms(or_bits, S_IRGRP, S_IWGRP, S_IXGRP);
+ break;
+ case S_IROTH:
+ and_bits = unix_perms_to_acl_perms(and_bits, S_IROTH, S_IWOTH, S_IXOTH);
+ or_bits = unix_perms_to_acl_perms(or_bits, S_IROTH, S_IWOTH, S_IXOTH);
+ break;
+ }
+
+ pace->perms = ((pace->perms & and_bits)|or_bits);
+}
+
+/****************************************************************************
+ Check if a given uid/SID is in a group gid/SID. This is probably very
+ expensive and will need optimisation. A *lot* of optimisation :-). JRA.
+****************************************************************************/
+
+static BOOL uid_entry_in_group( canon_ace *uid_ace, canon_ace *group_ace )
+{
+ extern DOM_SID global_sid_World;
+ fstring u_name;
+ fstring g_name;
+ extern struct current_user current_user;
+
+ /* "Everyone" always matches every uid. */
+
+ if (sid_equal(&group_ace->trustee, &global_sid_World))
+ return True;
+
+ /* Assume that the current user is in the current group (force group) */
+
+ if (uid_ace->unix_ug.uid == current_user.uid && group_ace->unix_ug.gid == current_user.gid)
+ return True;
+
+ fstrcpy(u_name, uidtoname(uid_ace->unix_ug.uid));
+ fstrcpy(g_name, gidtoname(group_ace->unix_ug.gid));
+
+ /*
+ * Due to the winbind interfaces we need to do this via names,
+ * not uids/gids.
+ */
+
+ return user_in_group_list(u_name, g_name, NULL, 0);
+}
+
+/****************************************************************************
+ A well formed POSIX file or default ACL has at least 3 entries, a
+ SMB_ACL_USER_OBJ, SMB_ACL_GROUP_OBJ, SMB_ACL_OTHER_OBJ.
+ In addition, the owner must always have at least read access.
+ When using this call on get_acl, the pst struct is valid and contains
+ the mode of the file. When using this call on set_acl, the pst struct has
+ been modified to have a mode containing the default for this file or directory
+ type.
+****************************************************************************/
+
+static BOOL ensure_canon_entry_valid(canon_ace **pp_ace,
+ files_struct *fsp,
+ DOM_SID *pfile_owner_sid,
+ DOM_SID *pfile_grp_sid,
+ SMB_STRUCT_STAT *pst,
+ BOOL setting_acl)
+{
+ extern DOM_SID global_sid_World;
+ canon_ace *pace;
+ BOOL got_user = False;
+ BOOL got_grp = False;
+ BOOL got_other = False;
+ canon_ace *pace_other = NULL;
+ canon_ace *pace_group = NULL;
+
+ for (pace = *pp_ace; pace; pace = pace->next) {
+ if (pace->type == SMB_ACL_USER_OBJ) {
+
+ if (setting_acl)
+ apply_default_perms(fsp, pace, S_IRUSR);
+ got_user = True;
+
+ } else if (pace->type == SMB_ACL_GROUP_OBJ) {
+
+ /*
+ * Ensure create mask/force create mode is respected on set.
+ */
+
+ if (setting_acl)
+ apply_default_perms(fsp, pace, S_IRGRP);
+ got_grp = True;
+ pace_group = pace;
+
+ } else if (pace->type == SMB_ACL_OTHER) {
+
+ /*
+ * Ensure create mask/force create mode is respected on set.
+ */
+
+ if (setting_acl)
+ apply_default_perms(fsp, pace, S_IROTH);
+ got_other = True;
+ pace_other = pace;
+ }
+ }
+
+ if (!got_user) {
+ if ((pace = (canon_ace *)malloc(sizeof(canon_ace))) == NULL) {
+ DEBUG(0,("ensure_canon_entry_valid: malloc fail.\n"));
+ return False;
+ }
+
+ ZERO_STRUCTP(pace);
+ pace->type = SMB_ACL_USER_OBJ;
+ pace->owner_type = UID_ACE;
+ pace->unix_ug.uid = pst->st_uid;
+ pace->trustee = *pfile_owner_sid;
+ pace->attr = ALLOW_ACE;
+
+ if (setting_acl) {
+ /* If we only got an "everyone" perm, just use that. */
+ if (!got_grp && got_other)
+ pace->perms = pace_other->perms;
+ else if (got_grp && uid_entry_in_group(pace, pace_group))
+ pace->perms = pace_group->perms;
+ else
+ pace->perms = 0;
+
+ apply_default_perms(fsp, pace, S_IRUSR);
+ } else {
+ pace->perms = unix_perms_to_acl_perms(pst->st_mode, S_IRUSR, S_IWUSR, S_IXUSR);
+ }
+
+ DLIST_ADD(*pp_ace, pace);
+ }
+
+ if (!got_grp) {
+ if ((pace = (canon_ace *)malloc(sizeof(canon_ace))) == NULL) {
+ DEBUG(0,("ensure_canon_entry_valid: malloc fail.\n"));
+ return False;
+ }
+
+ ZERO_STRUCTP(pace);
+ pace->type = SMB_ACL_GROUP_OBJ;
+ pace->owner_type = GID_ACE;
+ pace->unix_ug.uid = pst->st_gid;
+ pace->trustee = *pfile_grp_sid;
+ pace->attr = ALLOW_ACE;
+ if (setting_acl) {
+ /* If we only got an "everyone" perm, just use that. */
+ if (got_other)
+ pace->perms = pace_other->perms;
+ else
+ pace->perms = 0;
+ apply_default_perms(fsp, pace, S_IRGRP);
+ } else {
+ pace->perms = unix_perms_to_acl_perms(pst->st_mode, S_IRGRP, S_IWGRP, S_IXGRP);
+ }
+
+ DLIST_ADD(*pp_ace, pace);
+ }
+
+ if (!got_other) {
+ if ((pace = (canon_ace *)malloc(sizeof(canon_ace))) == NULL) {
+ DEBUG(0,("ensure_canon_entry_valid: malloc fail.\n"));
+ return False;
+ }
+
+ ZERO_STRUCTP(pace);
+ pace->type = SMB_ACL_OTHER;
+ pace->owner_type = WORLD_ACE;
+ pace->unix_ug.world = -1;
+ pace->trustee = global_sid_World;
+ pace->attr = ALLOW_ACE;
+ if (setting_acl) {
+ pace->perms = 0;
+ apply_default_perms(fsp, pace, S_IROTH);
+ } else
+ pace->perms = unix_perms_to_acl_perms(pst->st_mode, S_IROTH, S_IWOTH, S_IXOTH);
+
+ DLIST_ADD(*pp_ace, pace);
+ }
+
+ return True;
+}
+
+/****************************************************************************
+ Check if a POSIX ACL has the required SMB_ACL_USER_OBJ and SMB_ACL_GROUP_OBJ entries.
+ If it does not have them, check if there are any entries where the trustee is the
+ file owner or the owning group, and map these to SMB_ACL_USER_OBJ and SMB_ACL_GROUP_OBJ.
+****************************************************************************/
+
+static void check_owning_objs(canon_ace *ace, DOM_SID *pfile_owner_sid, DOM_SID *pfile_grp_sid)
+{
+ BOOL got_user_obj, got_group_obj;
+ canon_ace *current_ace;
+ int i, entries;
+
+ entries = count_canon_ace_list(ace);
+ got_user_obj = False;
+ got_group_obj = False;
+
+ for (i=0, current_ace = ace; i < entries; i++, current_ace = current_ace->next) {
+ if (current_ace->type == SMB_ACL_USER_OBJ)
+ got_user_obj = True;
+ else if (current_ace->type == SMB_ACL_GROUP_OBJ)
+ got_group_obj = True;
+ }
+ if (got_user_obj && got_group_obj) {
+ DEBUG(10,("check_owning_objs: ACL had owning user/group entries.\n"));
+ return;
+ }
+
+ for (i=0, current_ace = ace; i < entries; i++, current_ace = current_ace->next) {
+ if (!got_user_obj && current_ace->owner_type == UID_ACE &&
+ sid_equal(&current_ace->trustee, pfile_owner_sid)) {
+ current_ace->type = SMB_ACL_USER_OBJ;
+ got_user_obj = True;
+ }
+ if (!got_group_obj && current_ace->owner_type == GID_ACE &&
+ sid_equal(&current_ace->trustee, pfile_grp_sid)) {
+ current_ace->type = SMB_ACL_GROUP_OBJ;
+ got_group_obj = True;
+ }
+ }
+ if (!got_user_obj)
+ DEBUG(10,("check_owning_objs: ACL is missing an owner entry.\n"));
+ if (!got_group_obj)
+ DEBUG(10,("check_owning_objs: ACL is missing an owning group entry.\n"));
+}
+
+/****************************************************************************
+ Unpack a SEC_DESC into two canonical ace lists.
+****************************************************************************/
+
+static BOOL create_canon_ace_lists(files_struct *fsp, SMB_STRUCT_STAT *pst,
+ DOM_SID *pfile_owner_sid,
+ DOM_SID *pfile_grp_sid,
+ canon_ace **ppfile_ace, canon_ace **ppdir_ace,
+ SEC_ACL *dacl)
+{
+ extern DOM_SID global_sid_Creator_Owner;
+ extern DOM_SID global_sid_Creator_Group;
+ extern DOM_SID global_sid_World;
+ extern struct generic_mapping file_generic_mapping;
+ BOOL all_aces_are_inherit_only = (fsp->is_directory ? True : False);
+ canon_ace *file_ace = NULL;
+ canon_ace *dir_ace = NULL;
+ canon_ace *tmp_ace = NULL;
+ canon_ace *current_ace = NULL;
+ BOOL got_dir_allow = False;
+ BOOL got_file_allow = False;
+ int i, j;
+
+ *ppfile_ace = NULL;
+ *ppdir_ace = NULL;
+
+ /*
+ * Convert the incoming ACL into a more regular form.
+ */
+
+ for(i = 0; i < dacl->num_aces; i++) {
+ SEC_ACE *psa = &dacl->ace[i];
+
+ if((psa->type != SEC_ACE_TYPE_ACCESS_ALLOWED) && (psa->type != SEC_ACE_TYPE_ACCESS_DENIED)) {
+ DEBUG(3,("create_canon_ace_lists: unable to set anything but an ALLOW or DENY ACE.\n"));
+ return False;
+ }
+
+ if (nt4_compatible_acls()) {
+ /*
+ * The security mask may be UNIX_ACCESS_NONE which should map into
+ * no permissions (we overload the WRITE_OWNER bit for this) or it
+ * should be one of the ALL/EXECUTE/READ/WRITE bits. Arrange for this
+ * to be so. Any other bits override the UNIX_ACCESS_NONE bit.
+ */
+
+ /*
+ * Convert GENERIC bits to specific bits.
+ */
+
+ se_map_generic(&psa->info.mask, &file_generic_mapping);
+
+ psa->info.mask &= (UNIX_ACCESS_NONE|FILE_ALL_ACCESS);
+
+ if(psa->info.mask != UNIX_ACCESS_NONE)
+ psa->info.mask &= ~UNIX_ACCESS_NONE;
+ }
+ }
+
+ /*
+ * Deal with the fact that NT 4.x re-writes the canonical format
+ * that we return for default ACLs. If a directory ACE is identical
+ * to a inherited directory ACE then NT changes the bits so that the
+ * first ACE is set to OI|IO and the second ACE for this SID is set
+ * to CI. We need to repair this. JRA.
+ */
+
+ for(i = 0; i < dacl->num_aces; i++) {
+ SEC_ACE *psa1 = &dacl->ace[i];
+
+ for (j = i + 1; j < dacl->num_aces; j++) {
+ SEC_ACE *psa2 = &dacl->ace[j];
+
+ if (psa1->info.mask != psa2->info.mask)
+ continue;
+
+ if (!sid_equal(&psa1->trustee, &psa2->trustee))
+ continue;
+
+ /*
+ * Ok - permission bits and SIDs are equal.
+ * Check if flags were re-written.
+ */
+
+ if (psa1->flags & SEC_ACE_FLAG_INHERIT_ONLY) {
+
+ psa1->flags |= (psa2->flags & (SEC_ACE_FLAG_CONTAINER_INHERIT|SEC_ACE_FLAG_OBJECT_INHERIT));
+ psa2->flags &= ~(SEC_ACE_FLAG_CONTAINER_INHERIT|SEC_ACE_FLAG_OBJECT_INHERIT);
+
+ } else if (psa2->flags & SEC_ACE_FLAG_INHERIT_ONLY) {
+
+ psa2->flags |= (psa1->flags & (SEC_ACE_FLAG_CONTAINER_INHERIT|SEC_ACE_FLAG_OBJECT_INHERIT));
+ psa1->flags &= ~(SEC_ACE_FLAG_CONTAINER_INHERIT|SEC_ACE_FLAG_OBJECT_INHERIT);
+
+ }
+ }
+ }
+
+ for(i = 0; i < dacl->num_aces; i++) {
+ SEC_ACE *psa = &dacl->ace[i];
+
+ /*
+ * Ignore non-mappable SIDs (NT Authority, BUILTIN etc).
+ */
+
+ if (non_mappable_sid(&psa->trustee)) {
+ fstring str;
+ DEBUG(10,("create_canon_ace_lists: ignoring non-mappable SID %s\n",
+ sid_to_string(str, &psa->trustee) ));
+ continue;
+ }
+
+ /*
+ * Create a cannon_ace entry representing this NT DACL ACE.
+ */
+
+ if ((current_ace = (canon_ace *)malloc(sizeof(canon_ace))) == NULL) {
+ free_canon_ace_list(file_ace);
+ free_canon_ace_list(dir_ace);
+ DEBUG(0,("create_canon_ace_lists: malloc fail.\n"));
+ return False;
+ }
+
+ ZERO_STRUCTP(current_ace);
+
+ sid_copy(&current_ace->trustee, &psa->trustee);
+
+ /*
+ * Try and work out if the SID is a user or group
+ * as we need to flag these differently for POSIX.
+ * Note what kind of a POSIX ACL this should map to.
+ */
+
+ if( sid_equal(&current_ace->trustee, &global_sid_World)) {
+ current_ace->owner_type = WORLD_ACE;
+ current_ace->unix_ug.world = -1;
+ current_ace->type = SMB_ACL_OTHER;
+ } else if (sid_equal(&current_ace->trustee, &global_sid_Creator_Owner)) {
+ current_ace->owner_type = UID_ACE;
+ current_ace->unix_ug.uid = pst->st_uid;
+ current_ace->type = SMB_ACL_USER_OBJ;
+
+ /*
+ * The Creator Owner entry only specifies inheritable permissions,
+ * never access permissions. WinNT doesn't always set the ACE to
+ *INHERIT_ONLY, though.
+ */
+
+ if (nt4_compatible_acls())
+ psa->flags |= SEC_ACE_FLAG_INHERIT_ONLY;
+ } else if (sid_equal(&current_ace->trustee, &global_sid_Creator_Group)) {
+ current_ace->owner_type = GID_ACE;
+ current_ace->unix_ug.gid = pst->st_gid;
+ current_ace->type = SMB_ACL_GROUP_OBJ;
+
+ /*
+ * The Creator Group entry only specifies inheritable permissions,
+ * never access permissions. WinNT doesn't always set the ACE to
+ *INHERIT_ONLY, though.
+ */
+ if (nt4_compatible_acls())
+ psa->flags |= SEC_ACE_FLAG_INHERIT_ONLY;
+
+ } else if (NT_STATUS_IS_OK(sid_to_gid( &current_ace->trustee, &current_ace->unix_ug.gid))) {
+ current_ace->owner_type = GID_ACE;
+ current_ace->type = SMB_ACL_GROUP;
+ } else if (NT_STATUS_IS_OK(sid_to_uid( &current_ace->trustee, &current_ace->unix_ug.uid))) {
+ current_ace->owner_type = UID_ACE;
+ current_ace->type = SMB_ACL_USER;
+ } else {
+ fstring str;
+
+ free_canon_ace_list(file_ace);
+ free_canon_ace_list(dir_ace);
+ DEBUG(0,("create_canon_ace_lists: unable to map SID %s to uid or gid.\n",
+ sid_to_string(str, &current_ace->trustee) ));
+ SAFE_FREE(current_ace);
+ return False;
+ }
+
+ /*
+ * Map the given NT permissions into a UNIX mode_t containing only
+ * S_I(R|W|X)USR bits.
+ */
+
+ current_ace->perms |= map_nt_perms( psa->info, S_IRUSR);
+ current_ace->attr = (psa->type == SEC_ACE_TYPE_ACCESS_ALLOWED) ? ALLOW_ACE : DENY_ACE;
+ current_ace->inherited = ((psa->flags & SEC_ACE_FLAG_INHERITED_ACE) ? True : False);
+
+ /*
+ * Now add the created ace to either the file list, the directory
+ * list, or both. We *MUST* preserve the order here (hence we use
+ * DLIST_ADD_END) as NT ACLs are order dependent.
+ */
+
+ if (fsp->is_directory) {
+
+ /*
+ * We can only add to the default POSIX ACE list if the ACE is
+ * designed to be inherited by both files and directories.
+ */
+
+ if ((psa->flags & (SEC_ACE_FLAG_OBJECT_INHERIT|SEC_ACE_FLAG_CONTAINER_INHERIT)) ==
+ (SEC_ACE_FLAG_OBJECT_INHERIT|SEC_ACE_FLAG_CONTAINER_INHERIT)) {
+
+ DLIST_ADD_END(dir_ace, current_ace, tmp_ace);
+
+ /*
+ * Note if this was an allow ace. We can't process
+ * any further deny ace's after this.
+ */
+
+ if (current_ace->attr == ALLOW_ACE)
+ got_dir_allow = True;
+
+ if ((current_ace->attr == DENY_ACE) && got_dir_allow) {
+ DEBUG(0,("create_canon_ace_lists: malformed ACL in inheritable ACL ! \
+Deny entry after Allow entry. Failing to set on file %s.\n", fsp->fsp_name ));
+ free_canon_ace_list(file_ace);
+ free_canon_ace_list(dir_ace);
+ SAFE_FREE(current_ace);
+ return False;
+ }
+
+ if( DEBUGLVL( 10 )) {
+ dbgtext("create_canon_ace_lists: adding dir ACL:\n");
+ print_canon_ace( current_ace, 0);
+ }
+
+ /*
+ * If this is not an inherit only ACE we need to add a duplicate
+ * to the file acl.
+ */
+
+ if (!(psa->flags & SEC_ACE_FLAG_INHERIT_ONLY)) {
+ canon_ace *dup_ace = dup_canon_ace(current_ace);
+
+ if (!dup_ace) {
+ DEBUG(0,("create_canon_ace_lists: malloc fail !\n"));
+ free_canon_ace_list(file_ace);
+ free_canon_ace_list(dir_ace);
+ return False;
+ }
+
+ /*
+ * We must not free current_ace here as its
+ * pointer is now owned by the dir_ace list.
+ */
+ current_ace = dup_ace;
+ } else {
+ /*
+ * We must not free current_ace here as its
+ * pointer is now owned by the dir_ace list.
+ */
+ current_ace = NULL;
+ }
+ }
+ }
+
+ /*
+ * Only add to the file ACL if not inherit only.
+ */
+
+ if (!(psa->flags & SEC_ACE_FLAG_INHERIT_ONLY)) {
+ DLIST_ADD_END(file_ace, current_ace, tmp_ace);
+
+ /*
+ * Note if this was an allow ace. We can't process
+ * any further deny ace's after this.
+ */
+
+ if (current_ace->attr == ALLOW_ACE)
+ got_file_allow = True;
+
+ if ((current_ace->attr == DENY_ACE) && got_file_allow) {
+ DEBUG(0,("create_canon_ace_lists: malformed ACL in file ACL ! \
+Deny entry after Allow entry. Failing to set on file %s.\n", fsp->fsp_name ));
+ free_canon_ace_list(file_ace);
+ free_canon_ace_list(dir_ace);
+ SAFE_FREE(current_ace);
+ return False;
+ }
+
+ if( DEBUGLVL( 10 )) {
+ dbgtext("create_canon_ace_lists: adding file ACL:\n");
+ print_canon_ace( current_ace, 0);
+ }
+ all_aces_are_inherit_only = False;
+ /*
+ * We must not free current_ace here as its
+ * pointer is now owned by the file_ace list.
+ */
+ current_ace = NULL;
+ }
+
+ /*
+ * Free if ACE was not added.
+ */
+
+ SAFE_FREE(current_ace);
+ }
+
+ if (fsp->is_directory && all_aces_are_inherit_only) {
+ /*
+ * Windows 2000 is doing one of these weird 'inherit acl'
+ * traverses to conserve NTFS ACL resources. Just pretend
+ * there was no DACL sent. JRA.
+ */
+
+ DEBUG(10,("create_canon_ace_lists: Win2k inherit acl traverse. Ignoring DACL.\n"));
+ free_canon_ace_list(file_ace);
+ free_canon_ace_list(dir_ace);
+ file_ace = NULL;
+ dir_ace = NULL;
+ } else {
+ /*
+ * Check if we have SMB_ACL_USER_OBJ and SMB_ACL_GROUP_OBJ entries in each
+ * ACL. If we don't have them, check if any SMB_ACL_USER/SMB_ACL_GROUP
+ * entries can be converted to *_OBJ. Usually we will already have these
+ * entries in the Default ACL, and the Access ACL will not have them.
+ */
+ check_owning_objs(file_ace, pfile_owner_sid, pfile_grp_sid);
+ check_owning_objs(dir_ace, pfile_owner_sid, pfile_grp_sid);
+ }
+
+ *ppfile_ace = file_ace;
+ *ppdir_ace = dir_ace;
+
+ return True;
+}
+
+/****************************************************************************
+ ASCII art time again... JRA :-).
+
+ We have 4 cases to process when moving from an NT ACL to a POSIX ACL. Firstly,
+ we insist the ACL is in canonical form (ie. all DENY entries preceede ALLOW
+ entries). Secondly, the merge code has ensured that all duplicate SID entries for
+ allow or deny have been merged, so the same SID can only appear once in the deny
+ list or once in the allow list.
+
+ We then process as follows :
+
+ ---------------------------------------------------------------------------
+ First pass - look for a Everyone DENY entry.
+
+ If it is deny all (rwx) trunate the list at this point.
+ Else, walk the list from this point and use the deny permissions of this
+ entry as a mask on all following allow entries. Finally, delete
+ the Everyone DENY entry (we have applied it to everything possible).
+
+ In addition, in this pass we remove any DENY entries that have
+ no permissions (ie. they are a DENY nothing).
+ ---------------------------------------------------------------------------
+ Second pass - only deal with deny user entries.
+
+ DENY user1 (perms XXX)
+
+ new_perms = 0
+ for all following allow group entries where user1 is in group
+ new_perms |= group_perms;
+
+ user1 entry perms = new_perms & ~ XXX;
+
+ Convert the deny entry to an allow entry with the new perms and
+ push to the end of the list. Note if the user was in no groups
+ this maps to a specific allow nothing entry for this user.
+
+ The common case from the NT ACL choser (userX deny all) is
+ optimised so we don't do the group lookup - we just map to
+ an allow nothing entry.
+
+ What we're doing here is inferring the allow permissions the
+ person setting the ACE on user1 wanted by looking at the allow
+ permissions on the groups the user is currently in. This will
+ be a snapshot, depending on group membership but is the best
+ we can do and has the advantage of failing closed rather than
+ open.
+ ---------------------------------------------------------------------------
+ Third pass - only deal with deny group entries.
+
+ DENY group1 (perms XXX)
+
+ for all following allow user entries where user is in group1
+ user entry perms = user entry perms & ~ XXX;
+
+ If there is a group Everyone allow entry with permissions YYY,
+ convert the group1 entry to an allow entry and modify its
+ permissions to be :
+
+ new_perms = YYY & ~ XXX
+
+ and push to the end of the list.
+
+ If there is no group Everyone allow entry then convert the
+ group1 entry to a allow nothing entry and push to the end of the list.
+
+ Note that the common case from the NT ACL choser (groupX deny all)
+ cannot be optimised here as we need to modify user entries who are
+ in the group to change them to a deny all also.
+
+ What we're doing here is modifying the allow permissions of
+ user entries (which are more specific in POSIX ACLs) to mask
+ out the explicit deny set on the group they are in. This will
+ be a snapshot depending on current group membership but is the
+ best we can do and has the advantage of failing closed rather
+ than open.
+ ---------------------------------------------------------------------------
+ Fourth pass - cope with cumulative permissions.
+
+ for all allow user entries, if there exists an allow group entry with
+ more permissive permissions, and the user is in that group, rewrite the
+ allow user permissions to contain both sets of permissions.
+
+ Currently the code for this is #ifdef'ed out as these semantics make
+ no sense to me. JRA.
+ ---------------------------------------------------------------------------
+
+ Note we *MUST* do the deny user pass first as this will convert deny user
+ entries into allow user entries which can then be processed by the deny
+ group pass.
+
+ The above algorithm took a *lot* of thinking about - hence this
+ explaination :-). JRA.
+****************************************************************************/
+
+/****************************************************************************
+ Process a canon_ace list entries. This is very complex code. We need
+ to go through and remove the "deny" permissions from any allow entry that matches
+ the id of this entry. We have already refused any NT ACL that wasn't in correct
+ order (DENY followed by ALLOW). If any allow entry ends up with zero permissions,
+ we just remove it (to fail safe). We have already removed any duplicate ace
+ entries. Treat an "Everyone" DENY_ACE as a special case - use it to mask all
+ allow entries.
+****************************************************************************/
+
+static void process_deny_list( canon_ace **pp_ace_list )
+{
+ extern DOM_SID global_sid_World;
+ canon_ace *ace_list = *pp_ace_list;
+ canon_ace *curr_ace = NULL;
+ canon_ace *curr_ace_next = NULL;
+
+ /* Pass 1 above - look for an Everyone, deny entry. */
+
+ for (curr_ace = ace_list; curr_ace; curr_ace = curr_ace_next) {
+ canon_ace *allow_ace_p;
+
+ curr_ace_next = curr_ace->next; /* So we can't lose the link. */
+
+ if (curr_ace->attr != DENY_ACE)
+ continue;
+
+ if (curr_ace->perms == (mode_t)0) {
+
+ /* Deny nothing entry - delete. */
+
+ DLIST_REMOVE(ace_list, curr_ace);
+ continue;
+ }
+
+ if (!sid_equal(&curr_ace->trustee, &global_sid_World))
+ continue;
+
+ /* JRATEST - assert. */
+ SMB_ASSERT(curr_ace->owner_type == WORLD_ACE);
+
+ if (curr_ace->perms == ALL_ACE_PERMS) {
+
+ /*
+ * Optimisation. This is a DENY_ALL to Everyone. Truncate the
+ * list at this point including this entry.
+ */
+
+ canon_ace *prev_entry = curr_ace->prev;
+
+ free_canon_ace_list( curr_ace );
+ if (prev_entry)
+ prev_entry->next = NULL;
+ else {
+ /* We deleted the entire list. */
+ ace_list = NULL;
+ }
+ break;
+ }
+
+ for (allow_ace_p = curr_ace->next; allow_ace_p; allow_ace_p = allow_ace_p->next) {
+
+ /*
+ * Only mask off allow entries.
+ */
+
+ if (allow_ace_p->attr != ALLOW_ACE)
+ continue;
+
+ allow_ace_p->perms &= ~curr_ace->perms;
+ }
+
+ /*
+ * Now it's been applied, remove it.
+ */
+
+ DLIST_REMOVE(ace_list, curr_ace);
+ }
+
+ /* Pass 2 above - deal with deny user entries. */
+
+ for (curr_ace = ace_list; curr_ace; curr_ace = curr_ace_next) {
+ mode_t new_perms = (mode_t)0;
+ canon_ace *allow_ace_p;
+ canon_ace *tmp_ace;
+
+ curr_ace_next = curr_ace->next; /* So we can't lose the link. */
+
+ if (curr_ace->attr != DENY_ACE)
+ continue;
+
+ if (curr_ace->owner_type != UID_ACE)
+ continue;
+
+ if (curr_ace->perms == ALL_ACE_PERMS) {
+
+ /*
+ * Optimisation - this is a deny everything to this user.
+ * Convert to an allow nothing and push to the end of the list.
+ */
+
+ curr_ace->attr = ALLOW_ACE;
+ curr_ace->perms = (mode_t)0;
+ DLIST_DEMOTE(ace_list, curr_ace, tmp_ace);
+ continue;
+ }
+
+ for (allow_ace_p = curr_ace->next; allow_ace_p; allow_ace_p = allow_ace_p->next) {
+
+ if (allow_ace_p->attr != ALLOW_ACE)
+ continue;
+
+ /* We process GID_ACE and WORLD_ACE entries only. */
+
+ if (allow_ace_p->owner_type == UID_ACE)
+ continue;
+
+ if (uid_entry_in_group( curr_ace, allow_ace_p))
+ new_perms |= allow_ace_p->perms;
+ }
+
+ /*
+ * Convert to a allow entry, modify the perms and push to the end
+ * of the list.
+ */
+
+ curr_ace->attr = ALLOW_ACE;
+ curr_ace->perms = (new_perms & ~curr_ace->perms);
+ DLIST_DEMOTE(ace_list, curr_ace, tmp_ace);
+ }
+
+ /* Pass 3 above - deal with deny group entries. */
+
+ for (curr_ace = ace_list; curr_ace; curr_ace = curr_ace_next) {
+ canon_ace *tmp_ace;
+ canon_ace *allow_ace_p;
+ canon_ace *allow_everyone_p = NULL;
+
+ curr_ace_next = curr_ace->next; /* So we can't lose the link. */
+
+ if (curr_ace->attr != DENY_ACE)
+ continue;
+
+ if (curr_ace->owner_type != GID_ACE)
+ continue;
+
+ for (allow_ace_p = curr_ace->next; allow_ace_p; allow_ace_p = allow_ace_p->next) {
+
+ if (allow_ace_p->attr != ALLOW_ACE)
+ continue;
+
+ /* Store a pointer to the Everyone allow, if it exists. */
+ if (allow_ace_p->owner_type == WORLD_ACE)
+ allow_everyone_p = allow_ace_p;
+
+ /* We process UID_ACE entries only. */
+
+ if (allow_ace_p->owner_type != UID_ACE)
+ continue;
+
+ /* Mask off the deny group perms. */
+
+ if (uid_entry_in_group( allow_ace_p, curr_ace))
+ allow_ace_p->perms &= ~curr_ace->perms;
+ }
+
+ /*
+ * Convert the deny to an allow with the correct perms and
+ * push to the end of the list.
+ */
+
+ curr_ace->attr = ALLOW_ACE;
+ if (allow_everyone_p)
+ curr_ace->perms = allow_everyone_p->perms & ~curr_ace->perms;
+ else
+ curr_ace->perms = (mode_t)0;
+ DLIST_DEMOTE(ace_list, curr_ace, tmp_ace);
+
+ }
+
+ /* Doing this fourth pass allows Windows semantics to be layered
+ * on top of POSIX semantics. I'm not sure if this is desirable.
+ * For example, in W2K ACLs there is no way to say, "Group X no
+ * access, user Y full access" if user Y is a member of group X.
+ * This seems completely broken semantics to me.... JRA.
+ */
+
+#if 0
+ /* Pass 4 above - deal with allow entries. */
+
+ for (curr_ace = ace_list; curr_ace; curr_ace = curr_ace_next) {
+ canon_ace *allow_ace_p;
+
+ curr_ace_next = curr_ace->next; /* So we can't lose the link. */
+
+ if (curr_ace->attr != ALLOW_ACE)
+ continue;
+
+ if (curr_ace->owner_type != UID_ACE)
+ continue;
+
+ for (allow_ace_p = ace_list; allow_ace_p; allow_ace_p = allow_ace_p->next) {
+
+ if (allow_ace_p->attr != ALLOW_ACE)
+ continue;
+
+ /* We process GID_ACE entries only. */
+
+ if (allow_ace_p->owner_type != GID_ACE)
+ continue;
+
+ /* OR in the group perms. */
+
+ if (uid_entry_in_group( curr_ace, allow_ace_p))
+ curr_ace->perms |= allow_ace_p->perms;
+ }
+ }
+#endif
+
+ *pp_ace_list = ace_list;
+}
+
+/****************************************************************************
+ Create a default mode that will be used if a security descriptor entry has
+ no user/group/world entries.
+****************************************************************************/
+
+static mode_t create_default_mode(files_struct *fsp, BOOL interitable_mode)
+{
+ int snum = SNUM(fsp->conn);
+ mode_t and_bits = (mode_t)0;
+ mode_t or_bits = (mode_t)0;
+ mode_t mode = interitable_mode ? unix_mode( fsp->conn, FILE_ATTRIBUTE_ARCHIVE, fsp->fsp_name) : S_IRUSR;
+
+ if (fsp->is_directory)
+ mode |= (S_IWUSR|S_IXUSR);
+
+ /*
+ * Now AND with the create mode/directory mode bits then OR with the
+ * force create mode/force directory mode bits.
+ */
+
+ if (fsp->is_directory) {
+ and_bits = lp_dir_security_mask(snum);
+ or_bits = lp_force_dir_security_mode(snum);
+ } else {
+ and_bits = lp_security_mask(snum);
+ or_bits = lp_force_security_mode(snum);
+ }
+
+ return ((mode & and_bits)|or_bits);
+}
+
+/****************************************************************************
+ Unpack a SEC_DESC into two canonical ace lists. We don't depend on this
+ succeeding.
+****************************************************************************/
+
+static BOOL unpack_canon_ace(files_struct *fsp,
+ SMB_STRUCT_STAT *pst,
+ DOM_SID *pfile_owner_sid,
+ DOM_SID *pfile_grp_sid,
+ canon_ace **ppfile_ace, canon_ace **ppdir_ace,
+ uint32 security_info_sent, SEC_DESC *psd)
+{
+ canon_ace *file_ace = NULL;
+ canon_ace *dir_ace = NULL;
+
+ *ppfile_ace = NULL;
+ *ppdir_ace = NULL;
+
+ if(security_info_sent == 0) {
+ DEBUG(0,("unpack_canon_ace: no security info sent !\n"));
+ return False;
+ }
+
+ /*
+ * If no DACL then this is a chown only security descriptor.
+ */
+
+ if(!(security_info_sent & DACL_SECURITY_INFORMATION) || !psd->dacl)
+ return True;
+
+ /*
+ * Now go through the DACL and create the canon_ace lists.
+ */
+
+ if (!create_canon_ace_lists( fsp, pst, pfile_owner_sid, pfile_grp_sid,
+ &file_ace, &dir_ace, psd->dacl))
+ return False;
+
+ if ((file_ace == NULL) && (dir_ace == NULL)) {
+ /* W2K traverse DACL set - ignore. */
+ return True;
+ }
+
+ /*
+ * Go through the canon_ace list and merge entries
+ * belonging to identical users of identical allow or deny type.
+ * We can do this as all deny entries come first, followed by
+ * all allow entries (we have mandated this before accepting this acl).
+ */
+
+ print_canon_ace_list( "file ace - before merge", file_ace);
+ merge_aces( &file_ace );
+
+ print_canon_ace_list( "dir ace - before merge", dir_ace);
+ merge_aces( &dir_ace );
+
+ /*
+ * NT ACLs are order dependent. Go through the acl lists and
+ * process DENY entries by masking the allow entries.
+ */
+
+ print_canon_ace_list( "file ace - before deny", file_ace);
+ process_deny_list( &file_ace);
+
+ print_canon_ace_list( "dir ace - before deny", dir_ace);
+ process_deny_list( &dir_ace);
+
+ /*
+ * A well formed POSIX file or default ACL has at least 3 entries, a
+ * SMB_ACL_USER_OBJ, SMB_ACL_GROUP_OBJ, SMB_ACL_OTHER_OBJ
+ * and optionally a mask entry. Ensure this is the case.
+ */
+
+ print_canon_ace_list( "file ace - before valid", file_ace);
+
+ /*
+ * A default 3 element mode entry for a file should be r-- --- ---.
+ * A default 3 element mode entry for a directory should be rwx --- ---.
+ */
+
+ pst->st_mode = create_default_mode(fsp, False);
+
+ if (!ensure_canon_entry_valid(&file_ace, fsp, pfile_owner_sid, pfile_grp_sid, pst, True)) {
+ free_canon_ace_list(file_ace);
+ free_canon_ace_list(dir_ace);
+ return False;
+ }
+
+ print_canon_ace_list( "dir ace - before valid", dir_ace);
+
+ /*
+ * A default inheritable 3 element mode entry for a directory should be the
+ * mode Samba will use to create a file within. Ensure user rwx bits are set if
+ * it's a directory.
+ */
+
+ pst->st_mode = create_default_mode(fsp, True);
+
+ if (dir_ace && !ensure_canon_entry_valid(&dir_ace, fsp, pfile_owner_sid, pfile_grp_sid, pst, True)) {
+ free_canon_ace_list(file_ace);
+ free_canon_ace_list(dir_ace);
+ return False;
+ }
+
+ print_canon_ace_list( "file ace - return", file_ace);
+ print_canon_ace_list( "dir ace - return", dir_ace);
+
+ *ppfile_ace = file_ace;
+ *ppdir_ace = dir_ace;
+ return True;
+
+}
+
+/******************************************************************************
+ When returning permissions, try and fit NT display
+ semantics if possible. Note the the canon_entries here must have been malloced.
+ The list format should be - first entry = owner, followed by group and other user
+ entries, last entry = other.
+
+ Note that this doesn't exactly match the NT semantics for an ACL. As POSIX entries
+ are not ordered, and match on the most specific entry rather than walking a list,
+ then a simple POSIX permission of rw-r--r-- should really map to 5 entries,
+
+ Entry 0: owner : deny all except read and write.
+ Entry 1: group : deny all except read.
+ Entry 2: owner : allow read and write.
+ Entry 3: group : allow read.
+ Entry 4: Everyone : allow read.
+
+ But NT cannot display this in their ACL editor !
+********************************************************************************/
+
+static void arrange_posix_perms( char *filename, canon_ace **pp_list_head)
+{
+ canon_ace *list_head = *pp_list_head;
+ canon_ace *owner_ace = NULL;
+ canon_ace *other_ace = NULL;
+ canon_ace *ace = NULL;
+
+ for (ace = list_head; ace; ace = ace->next) {
+ if (ace->type == SMB_ACL_USER_OBJ)
+ owner_ace = ace;
+ else if (ace->type == SMB_ACL_OTHER) {
+ /* Last ace - this is "other" */
+ other_ace = ace;
+ }
+ }
+
+ if (!owner_ace || !other_ace) {
+ DEBUG(0,("arrange_posix_perms: Invalid POSIX permissions for file %s, missing owner or other.\n",
+ filename ));
+ return;
+ }
+
+ /*
+ * The POSIX algorithm applies to owner first, and other last,
+ * so ensure they are arranged in this order.
+ */
+
+ if (owner_ace) {
+ DLIST_PROMOTE(list_head, owner_ace);
+ }
+
+ if (other_ace) {
+ DLIST_DEMOTE(list_head, other_ace, ace);
+ }
+
+ /* We have probably changed the head of the list. */
+
+ *pp_list_head = list_head;
+}
+
+/****************************************************************************
+ Create a linked list of canonical ACE entries.
+****************************************************************************/
+
+static canon_ace *canonicalise_acl( files_struct *fsp, SMB_ACL_T posix_acl, SMB_STRUCT_STAT *psbuf,
+ DOM_SID *powner, DOM_SID *pgroup, struct pai_val *pal, SMB_ACL_TYPE_T the_acl_type)
+{
+ extern DOM_SID global_sid_World;
+ connection_struct *conn = fsp->conn;
+ mode_t acl_mask = (S_IRUSR|S_IWUSR|S_IXUSR);
+ canon_ace *list_head = NULL;
+ canon_ace *ace = NULL;
+ canon_ace *next_ace = NULL;
+ int entry_id = SMB_ACL_FIRST_ENTRY;
+ SMB_ACL_ENTRY_T entry;
+ size_t ace_count;
+
+ while ( posix_acl && (SMB_VFS_SYS_ACL_GET_ENTRY(conn, posix_acl, entry_id, &entry) == 1)) {
+ SMB_ACL_TAG_T tagtype;
+ SMB_ACL_PERMSET_T permset;
+ DOM_SID sid;
+ posix_id unix_ug;
+ enum ace_owner owner_type;
+
+ /* get_next... */
+ if (entry_id == SMB_ACL_FIRST_ENTRY)
+ entry_id = SMB_ACL_NEXT_ENTRY;
+
+ /* Is this a MASK entry ? */
+ if (SMB_VFS_SYS_ACL_GET_TAG_TYPE(conn, entry, &tagtype) == -1)
+ continue;
+
+ if (SMB_VFS_SYS_ACL_GET_PERMSET(conn, entry, &permset) == -1)
+ continue;
+
+ /* Decide which SID to use based on the ACL type. */
+ switch(tagtype) {
+ case SMB_ACL_USER_OBJ:
+ /* Get the SID from the owner. */
+ sid_copy(&sid, powner);
+ unix_ug.uid = psbuf->st_uid;
+ owner_type = UID_ACE;
+ break;
+ case SMB_ACL_USER:
+ {
+ uid_t *puid = (uid_t *)SMB_VFS_SYS_ACL_GET_QUALIFIER(conn, entry);
+ if (puid == NULL) {
+ DEBUG(0,("canonicalise_acl: Failed to get uid.\n"));
+ continue;
+ }
+ /*
+ * A SMB_ACL_USER entry for the owner is shadowed by the
+ * SMB_ACL_USER_OBJ entry and Windows also cannot represent
+ * that entry, so we ignore it. We also don't create such
+ * entries out of the blue when setting ACLs, so a get/set
+ * cycle will drop them.
+ */
+ if (the_acl_type == SMB_ACL_TYPE_ACCESS && *puid == psbuf->st_uid)
+ continue;
+ uid_to_sid( &sid, *puid);
+ unix_ug.uid = *puid;
+ owner_type = UID_ACE;
+ SMB_VFS_SYS_ACL_FREE_QUALIFIER(conn, (void *)puid,tagtype);
+ break;
+ }
+ case SMB_ACL_GROUP_OBJ:
+ /* Get the SID from the owning group. */
+ sid_copy(&sid, pgroup);
+ unix_ug.gid = psbuf->st_gid;
+ owner_type = GID_ACE;
+ break;
+ case SMB_ACL_GROUP:
+ {
+ gid_t *pgid = (gid_t *)SMB_VFS_SYS_ACL_GET_QUALIFIER(conn, entry);
+ if (pgid == NULL) {
+ DEBUG(0,("canonicalise_acl: Failed to get gid.\n"));
+ continue;
+ }
+ gid_to_sid( &sid, *pgid);
+ unix_ug.gid = *pgid;
+ owner_type = GID_ACE;
+ SMB_VFS_SYS_ACL_FREE_QUALIFIER(conn, (void *)pgid,tagtype);
+ break;
+ }
+ case SMB_ACL_MASK:
+ acl_mask = convert_permset_to_mode_t(conn, permset);
+ continue; /* Don't count the mask as an entry. */
+ case SMB_ACL_OTHER:
+ /* Use the Everyone SID */
+ sid = global_sid_World;
+ unix_ug.world = -1;
+ owner_type = WORLD_ACE;
+ break;
+ default:
+ DEBUG(0,("canonicalise_acl: Unknown tagtype %u\n", (unsigned int)tagtype));
+ continue;
+ }
+
+ /*
+ * Add this entry to the list.
+ */
+
+ if ((ace = (canon_ace *)malloc(sizeof(canon_ace))) == NULL)
+ goto fail;
+
+ ZERO_STRUCTP(ace);
+ ace->type = tagtype;
+ ace->perms = convert_permset_to_mode_t(conn, permset);
+ ace->attr = ALLOW_ACE;
+ ace->trustee = sid;
+ ace->unix_ug = unix_ug;
+ ace->owner_type = owner_type;
+ ace->inherited = get_inherited_flag(pal, ace, (the_acl_type == SMB_ACL_TYPE_DEFAULT));
+
+ DLIST_ADD(list_head, ace);
+ }
+
+ /*
+ * This next call will ensure we have at least a user/group/world set.
+ */
+
+ if (!ensure_canon_entry_valid(&list_head, fsp, powner, pgroup, psbuf, False))
+ goto fail;
+
+ /*
+ * Now go through the list, masking the permissions with the
+ * acl_mask. Ensure all DENY Entries are at the start of the list.
+ */
+
+ DEBUG(10,("canonicalise_acl: %s ace entries before arrange :\n", the_acl_type == SMB_ACL_TYPE_ACCESS ? "Access" : "Default" ));
+
+ for ( ace_count = 0, ace = list_head; ace; ace = next_ace, ace_count++) {
+ next_ace = ace->next;
+
+ /* Masks are only applied to entries other than USER_OBJ and OTHER. */
+ if (ace->type != SMB_ACL_OTHER && ace->type != SMB_ACL_USER_OBJ)
+ ace->perms &= acl_mask;
+
+ if (ace->perms == 0) {
+ DLIST_PROMOTE(list_head, ace);
+ }
+
+ if( DEBUGLVL( 10 ) ) {
+ print_canon_ace(ace, ace_count);
+ }
+ }
+
+ arrange_posix_perms(fsp->fsp_name,&list_head );
+
+ print_canon_ace_list( "canonicalise_acl: ace entries after arrange", list_head );
+
+ return list_head;
+
+ fail:
+
+ free_canon_ace_list(list_head);
+ return NULL;
+}
+
+/****************************************************************************
+ Attempt to apply an ACL to a file or directory.
+****************************************************************************/
+
+static BOOL set_canon_ace_list(files_struct *fsp, canon_ace *the_ace, BOOL default_ace, BOOL *pacl_set_support)
+{
+ connection_struct *conn = fsp->conn;
+ BOOL ret = False;
+ SMB_ACL_T the_acl = SMB_VFS_SYS_ACL_INIT(conn, (int)count_canon_ace_list(the_ace) + 1);
+ canon_ace *p_ace;
+ int i;
+ SMB_ACL_ENTRY_T mask_entry;
+ BOOL got_mask_entry = False;
+ SMB_ACL_PERMSET_T mask_permset;
+ SMB_ACL_TYPE_T the_acl_type = (default_ace ? SMB_ACL_TYPE_DEFAULT : SMB_ACL_TYPE_ACCESS);
+ BOOL needs_mask = False;
+ mode_t mask_perms = 0;
+
+#if defined(POSIX_ACL_NEEDS_MASK)
+ /* HP-UX always wants to have a mask (called "class" there). */
+ needs_mask = True;
+#endif
+
+ if (the_acl == NULL) {
+
+ if (errno != ENOSYS) {
+ /*
+ * Only print this error message if we have some kind of ACL
+ * support that's not working. Otherwise we would always get this.
+ */
+ DEBUG(0,("set_canon_ace_list: Unable to init %s ACL. (%s)\n",
+ default_ace ? "default" : "file", strerror(errno) ));
+ }
+ *pacl_set_support = False;
+ return False;
+ }
+
+ if( DEBUGLVL( 10 )) {
+ dbgtext("set_canon_ace_list: setting ACL:\n");
+ for (i = 0, p_ace = the_ace; p_ace; p_ace = p_ace->next, i++ ) {
+ print_canon_ace( p_ace, i);
+ }
+ }
+
+ for (i = 0, p_ace = the_ace; p_ace; p_ace = p_ace->next, i++ ) {
+ SMB_ACL_ENTRY_T the_entry;
+ SMB_ACL_PERMSET_T the_permset;
+
+ /*
+ * ACLs only "need" an ACL_MASK entry if there are any named user or
+ * named group entries. But if there is an ACL_MASK entry, it applies
+ * to ACL_USER, ACL_GROUP, and ACL_GROUP_OBJ entries. Set the mask
+ * so that it doesn't deny (i.e., mask off) any permissions.
+ */
+
+ if (p_ace->type == SMB_ACL_USER || p_ace->type == SMB_ACL_GROUP) {
+ needs_mask = True;
+ mask_perms |= p_ace->perms;
+ } else if (p_ace->type == SMB_ACL_GROUP_OBJ) {
+ mask_perms |= p_ace->perms;
+ }
+
+ /*
+ * Get the entry for this ACE.
+ */
+
+ if (SMB_VFS_SYS_ACL_CREATE_ENTRY(conn, &the_acl, &the_entry) == -1) {
+ DEBUG(0,("set_canon_ace_list: Failed to create entry %d. (%s)\n",
+ i, strerror(errno) ));
+ goto done;
+ }
+
+ if (p_ace->type == SMB_ACL_MASK) {
+ mask_entry = the_entry;
+ got_mask_entry = True;
+ }
+
+ /*
+ * Ok - we now know the ACL calls should be working, don't
+ * allow fallback to chmod.
+ */
+
+ *pacl_set_support = True;
+
+ /*
+ * Initialise the entry from the canon_ace.
+ */
+
+ /*
+ * First tell the entry what type of ACE this is.
+ */
+
+ if (SMB_VFS_SYS_ACL_SET_TAG_TYPE(conn, the_entry, p_ace->type) == -1) {
+ DEBUG(0,("set_canon_ace_list: Failed to set tag type on entry %d. (%s)\n",
+ i, strerror(errno) ));
+ goto done;
+ }
+
+ /*
+ * Only set the qualifier (user or group id) if the entry is a user
+ * or group id ACE.
+ */
+
+ if ((p_ace->type == SMB_ACL_USER) || (p_ace->type == SMB_ACL_GROUP)) {
+ if (SMB_VFS_SYS_ACL_SET_QUALIFIER(conn, the_entry,(void *)&p_ace->unix_ug.uid) == -1) {
+ DEBUG(0,("set_canon_ace_list: Failed to set qualifier on entry %d. (%s)\n",
+ i, strerror(errno) ));
+ goto done;
+ }
+ }
+
+ /*
+ * Convert the mode_t perms in the canon_ace to a POSIX permset.
+ */
+
+ if (SMB_VFS_SYS_ACL_GET_PERMSET(conn, the_entry, &the_permset) == -1) {
+ DEBUG(0,("set_canon_ace_list: Failed to get permset on entry %d. (%s)\n",
+ i, strerror(errno) ));
+ goto done;
+ }
+
+ if (map_acl_perms_to_permset(conn, p_ace->perms, &the_permset) == -1) {
+ DEBUG(0,("set_canon_ace_list: Failed to create permset for mode (%u) on entry %d. (%s)\n",
+ (unsigned int)p_ace->perms, i, strerror(errno) ));
+ goto done;
+ }
+
+ /*
+ * ..and apply them to the entry.
+ */
+
+ if (SMB_VFS_SYS_ACL_SET_PERMSET(conn, the_entry, the_permset) == -1) {
+ DEBUG(0,("set_canon_ace_list: Failed to add permset on entry %d. (%s)\n",
+ i, strerror(errno) ));
+ goto done;
+ }
+
+ if( DEBUGLVL( 10 ))
+ print_canon_ace( p_ace, i);
+
+ }
+
+ if (needs_mask && !got_mask_entry) {
+ if (SMB_VFS_SYS_ACL_CREATE_ENTRY(conn, &the_acl, &mask_entry) == -1) {
+ DEBUG(0,("set_canon_ace_list: Failed to create mask entry. (%s)\n", strerror(errno) ));
+ goto done;
+ }
+
+ if (SMB_VFS_SYS_ACL_SET_TAG_TYPE(conn, mask_entry, SMB_ACL_MASK) == -1) {
+ DEBUG(0,("set_canon_ace_list: Failed to set tag type on mask entry. (%s)\n",strerror(errno) ));
+ goto done;
+ }
+
+ if (SMB_VFS_SYS_ACL_GET_PERMSET(conn, mask_entry, &mask_permset) == -1) {
+ DEBUG(0,("set_canon_ace_list: Failed to get mask permset. (%s)\n", strerror(errno) ));
+ goto done;
+ }
+
+ if (map_acl_perms_to_permset(conn, S_IRUSR|S_IWUSR|S_IXUSR, &mask_permset) == -1) {
+ DEBUG(0,("set_canon_ace_list: Failed to create mask permset. (%s)\n", strerror(errno) ));
+ goto done;
+ }
+
+ if (SMB_VFS_SYS_ACL_SET_PERMSET(conn, mask_entry, mask_permset) == -1) {
+ DEBUG(0,("set_canon_ace_list: Failed to add mask permset. (%s)\n", strerror(errno) ));
+ goto done;
+ }
+ }
+
+ /*
+ * Check if the ACL is valid.
+ */
+
+ if (SMB_VFS_SYS_ACL_VALID(conn, the_acl) == -1) {
+ DEBUG(0,("set_canon_ace_list: ACL type (%s) is invalid for set (%s).\n",
+ the_acl_type == SMB_ACL_TYPE_DEFAULT ? "directory default" : "file",
+ strerror(errno) ));
+ goto done;
+ }
+
+ /*
+ * Finally apply it to the file or directory.
+ */
+
+ if(default_ace || fsp->is_directory || fsp->fd == -1) {
+ if (SMB_VFS_SYS_ACL_SET_FILE(conn, fsp->fsp_name, the_acl_type, the_acl) == -1) {
+ /*
+ * Some systems allow all the above calls and only fail with no ACL support
+ * when attempting to apply the acl. HPUX with HFS is an example of this. JRA.
+ */
+ if (errno == ENOSYS)
+ *pacl_set_support = False;
+
+#ifdef ENOTSUP
+ if (errno == ENOTSUP)
+ *pacl_set_support = False;
+#endif
+
+ DEBUG(2,("set_canon_ace_list: sys_acl_set_file type %s failed for file %s (%s).\n",
+ the_acl_type == SMB_ACL_TYPE_DEFAULT ? "directory default" : "file",
+ fsp->fsp_name, strerror(errno) ));
+ goto done;
+ }
+ } else {
+ if (SMB_VFS_SYS_ACL_SET_FD(fsp, fsp->fd, the_acl) == -1) {
+ /*
+ * Some systems allow all the above calls and only fail with no ACL support
+ * when attempting to apply the acl. HPUX with HFS is an example of this. JRA.
+ */
+ if (errno == ENOSYS)
+ *pacl_set_support = False;
+
+#ifdef ENOTSUP
+ if (errno == ENOTSUP)
+ *pacl_set_support = False;
+#endif
+
+ DEBUG(2,("set_canon_ace_list: sys_acl_set_file failed for file %s (%s).\n",
+ fsp->fsp_name, strerror(errno) ));
+ goto done;
+ }
+ }
+
+ ret = True;
+
+ done:
+
+ if (the_acl != NULL)
+ SMB_VFS_SYS_ACL_FREE_ACL(conn, the_acl);
+
+ return ret;
+}
+
+/****************************************************************************
+ Find a particular canon_ace entry.
+****************************************************************************/
+
+static struct canon_ace *canon_ace_entry_for(struct canon_ace *list, SMB_ACL_TAG_T type, posix_id *id)
+{
+ while (list) {
+ if (list->type == type && ((type != SMB_ACL_USER && type != SMB_ACL_GROUP) ||
+ (type == SMB_ACL_USER && id && id->uid == list->unix_ug.uid) ||
+ (type == SMB_ACL_GROUP && id && id->gid == list->unix_ug.gid)))
+ break;
+ list = list->next;
+ }
+ return list;
+}
+
+/****************************************************************************
+
+****************************************************************************/
+
+SMB_ACL_T free_empty_sys_acl(connection_struct *conn, SMB_ACL_T the_acl)
+{
+ SMB_ACL_ENTRY_T entry;
+
+ if (!the_acl)
+ return NULL;
+ if (SMB_VFS_SYS_ACL_GET_ENTRY(conn, the_acl, SMB_ACL_FIRST_ENTRY, &entry) != 1) {
+ SMB_VFS_SYS_ACL_FREE_ACL(conn, the_acl);
+ return NULL;
+ }
+ return the_acl;
+}
+
+/****************************************************************************
+ Convert a canon_ace to a generic 3 element permission - if possible.
+****************************************************************************/
+
+#define MAP_PERM(p,mask,result) (((p) & (mask)) ? (result) : 0 )
+
+static BOOL convert_canon_ace_to_posix_perms( files_struct *fsp, canon_ace *file_ace_list, mode_t *posix_perms)
+{
+ int snum = SNUM(fsp->conn);
+ size_t ace_count = count_canon_ace_list(file_ace_list);
+ canon_ace *ace_p;
+ canon_ace *owner_ace = NULL;
+ canon_ace *group_ace = NULL;
+ canon_ace *other_ace = NULL;
+ mode_t and_bits;
+ mode_t or_bits;
+
+ if (ace_count != 3) {
+ DEBUG(3,("convert_canon_ace_to_posix_perms: Too many ACE entries for file %s to convert to \
+posix perms.\n", fsp->fsp_name ));
+ return False;
+ }
+
+ for (ace_p = file_ace_list; ace_p; ace_p = ace_p->next) {
+ if (ace_p->owner_type == UID_ACE)
+ owner_ace = ace_p;
+ else if (ace_p->owner_type == GID_ACE)
+ group_ace = ace_p;
+ else if (ace_p->owner_type == WORLD_ACE)
+ other_ace = ace_p;
+ }
+
+ if (!owner_ace || !group_ace || !other_ace) {
+ DEBUG(3,("convert_canon_ace_to_posix_perms: Can't get standard entries for file %s.\n",
+ fsp->fsp_name ));
+ return False;
+ }
+
+ *posix_perms = (mode_t)0;
+
+ *posix_perms |= owner_ace->perms;
+ *posix_perms |= MAP_PERM(group_ace->perms, S_IRUSR, S_IRGRP);
+ *posix_perms |= MAP_PERM(group_ace->perms, S_IWUSR, S_IWGRP);
+ *posix_perms |= MAP_PERM(group_ace->perms, S_IXUSR, S_IXGRP);
+ *posix_perms |= MAP_PERM(other_ace->perms, S_IRUSR, S_IROTH);
+ *posix_perms |= MAP_PERM(other_ace->perms, S_IWUSR, S_IWOTH);
+ *posix_perms |= MAP_PERM(other_ace->perms, S_IXUSR, S_IXOTH);
+
+ /* The owner must have at least read access. */
+
+ *posix_perms |= S_IRUSR;
+ if (fsp->is_directory)
+ *posix_perms |= (S_IWUSR|S_IXUSR);
+
+ /* If requested apply the masks. */
+
+ /* Get the initial bits to apply. */
+
+ if (fsp->is_directory) {
+ and_bits = lp_dir_security_mask(snum);
+ or_bits = lp_force_dir_security_mode(snum);
+ } else {
+ and_bits = lp_security_mask(snum);
+ or_bits = lp_force_security_mode(snum);
+ }
+
+ *posix_perms = (((*posix_perms) & and_bits)|or_bits);
+
+ DEBUG(10,("convert_canon_ace_to_posix_perms: converted u=%o,g=%o,w=%o to perm=0%o for file %s.\n",
+ (int)owner_ace->perms, (int)group_ace->perms, (int)other_ace->perms, (int)*posix_perms,
+ fsp->fsp_name ));
+
+ return True;
+}
+
+/****************************************************************************
+ Incoming NT ACLs on a directory can be split into a default POSIX acl (CI|OI|IO) and
+ a normal POSIX acl. Win2k needs these split acls re-merging into one ACL
+ with CI|OI set so it is inherited and also applies to the directory.
+ Based on code from "Jim McDonough" <jmcd@us.ibm.com>.
+****************************************************************************/
+
+static size_t merge_default_aces( SEC_ACE *nt_ace_list, size_t num_aces)
+{
+ size_t i, j;
+
+ for (i = 0; i < num_aces; i++) {
+ for (j = i+1; j < num_aces; j++) {
+ uint32 i_flags_ni = (nt_ace_list[i].flags & ~SEC_ACE_FLAG_INHERITED_ACE);
+ uint32 j_flags_ni = (nt_ace_list[j].flags & ~SEC_ACE_FLAG_INHERITED_ACE);
+ BOOL i_inh = (nt_ace_list[i].flags & SEC_ACE_FLAG_INHERITED_ACE) ? True : False;
+ BOOL j_inh = (nt_ace_list[j].flags & SEC_ACE_FLAG_INHERITED_ACE) ? True : False;
+
+ /* We know the lower number ACE's are file entries. */
+ if ((nt_ace_list[i].type == nt_ace_list[j].type) &&
+ (nt_ace_list[i].size == nt_ace_list[j].size) &&
+ (nt_ace_list[i].info.mask == nt_ace_list[j].info.mask) &&
+ sid_equal(&nt_ace_list[i].trustee, &nt_ace_list[j].trustee) &&
+ (i_inh == j_inh) &&
+ (i_flags_ni == 0) &&
+ (j_flags_ni == (SEC_ACE_FLAG_OBJECT_INHERIT|
+ SEC_ACE_FLAG_CONTAINER_INHERIT|
+ SEC_ACE_FLAG_INHERIT_ONLY))) {
+ /*
+ * W2K wants to have access allowed zero access ACE's
+ * at the end of the list. If the mask is zero, merge
+ * the non-inherited ACE onto the inherited ACE.
+ */
+
+ if (nt_ace_list[i].info.mask == 0) {
+ nt_ace_list[j].flags = SEC_ACE_FLAG_OBJECT_INHERIT|SEC_ACE_FLAG_CONTAINER_INHERIT|
+ (i_inh ? SEC_ACE_FLAG_INHERITED_ACE : 0);
+ if (num_aces - i - 1 > 0)
+ memmove(&nt_ace_list[i], &nt_ace_list[i+1], (num_aces-i-1) *
+ sizeof(SEC_ACE));
+
+ DEBUG(10,("merge_default_aces: Merging zero access ACE %u onto ACE %u.\n",
+ (unsigned int)i, (unsigned int)j ));
+ } else {
+ /*
+ * These are identical except for the flags.
+ * Merge the inherited ACE onto the non-inherited ACE.
+ */
+
+ nt_ace_list[i].flags = SEC_ACE_FLAG_OBJECT_INHERIT|SEC_ACE_FLAG_CONTAINER_INHERIT|
+ (i_inh ? SEC_ACE_FLAG_INHERITED_ACE : 0);
+ if (num_aces - j - 1 > 0)
+ memmove(&nt_ace_list[j], &nt_ace_list[j+1], (num_aces-j-1) *
+ sizeof(SEC_ACE));
+
+ DEBUG(10,("merge_default_aces: Merging ACE %u onto ACE %u.\n",
+ (unsigned int)j, (unsigned int)i ));
+ }
+ num_aces--;
+ break;
+ }
+ }
+ }
+
+ return num_aces;
+}
+/****************************************************************************
+ Reply to query a security descriptor from an fsp. If it succeeds it allocates
+ the space for the return elements and returns the size needed to return the
+ security descriptor. This should be the only external function needed for
+ the UNIX style get ACL.
+****************************************************************************/
+
+size_t get_nt_acl(files_struct *fsp, uint32 security_info, SEC_DESC **ppdesc)
+{
+ extern DOM_SID global_sid_Builtin_Administrators;
+ extern DOM_SID global_sid_Builtin_Users;
+ extern DOM_SID global_sid_Creator_Owner;
+ extern DOM_SID global_sid_Creator_Group;
+ connection_struct *conn = fsp->conn;
+ SMB_STRUCT_STAT sbuf;
+ SEC_ACE *nt_ace_list = NULL;
+ DOM_SID owner_sid;
+ DOM_SID group_sid;
+ size_t sd_size = 0;
+ SEC_ACL *psa = NULL;
+ size_t num_acls = 0;
+ size_t num_dir_acls = 0;
+ size_t num_aces = 0;
+ SMB_ACL_T posix_acl = NULL;
+ SMB_ACL_T dir_acl = NULL;
+ canon_ace *file_ace = NULL;
+ canon_ace *dir_ace = NULL;
+ size_t num_profile_acls = 0;
+ struct pai_val *pal = NULL;
+ SEC_DESC *psd = NULL;
+
+ *ppdesc = NULL;
+
+ DEBUG(10,("get_nt_acl: called for file %s\n", fsp->fsp_name ));
+
+ if(fsp->is_directory || fsp->fd == -1) {
+
+ /* Get the stat struct for the owner info. */
+ if(SMB_VFS_STAT(fsp->conn,fsp->fsp_name, &sbuf) != 0) {
+ return 0;
+ }
+ /*
+ * Get the ACL from the path.
+ */
+
+ posix_acl = SMB_VFS_SYS_ACL_GET_FILE(conn, fsp->fsp_name, SMB_ACL_TYPE_ACCESS);
+
+ /*
+ * If it's a directory get the default POSIX ACL.
+ */
+
+ if(fsp->is_directory) {
+ dir_acl = SMB_VFS_SYS_ACL_GET_FILE(conn, fsp->fsp_name, SMB_ACL_TYPE_DEFAULT);
+ dir_acl = free_empty_sys_acl(conn, dir_acl);
+ }
+
+ } else {
+
+ /* Get the stat struct for the owner info. */
+ if(SMB_VFS_FSTAT(fsp,fsp->fd,&sbuf) != 0) {
+ return 0;
+ }
+ /*
+ * Get the ACL from the fd.
+ */
+ posix_acl = SMB_VFS_SYS_ACL_GET_FD(fsp, fsp->fd);
+ }
+
+ DEBUG(5,("get_nt_acl : file ACL %s, directory ACL %s\n",
+ posix_acl ? "present" : "absent",
+ dir_acl ? "present" : "absent" ));
+
+ pal = load_inherited_info(fsp);
+
+ /*
+ * Get the owner, group and world SIDs.
+ */
+
+ if (lp_profile_acls(SNUM(fsp->conn))) {
+ /* For WXP SP1 the owner must be administrators. */
+ sid_copy(&owner_sid, &global_sid_Builtin_Administrators);
+ sid_copy(&group_sid, &global_sid_Builtin_Users);
+ num_profile_acls = 2;
+ } else {
+ create_file_sids(&sbuf, &owner_sid, &group_sid);
+ }
+
+ if ((security_info & DACL_SECURITY_INFORMATION) && !(security_info & PROTECTED_DACL_SECURITY_INFORMATION)) {
+
+ /*
+ * In the optimum case Creator Owner and Creator Group would be used for
+ * the ACL_USER_OBJ and ACL_GROUP_OBJ entries, respectively, but this
+ * would lead to usability problems under Windows: The Creator entries
+ * are only available in browse lists of directories and not for files;
+ * additionally the identity of the owning group couldn't be determined.
+ * We therefore use those identities only for Default ACLs.
+ */
+
+ /* Create the canon_ace lists. */
+ file_ace = canonicalise_acl( fsp, posix_acl, &sbuf, &owner_sid, &group_sid, pal, SMB_ACL_TYPE_ACCESS );
+
+ /* We must have *some* ACLS. */
+
+ if (count_canon_ace_list(file_ace) == 0) {
+ DEBUG(0,("get_nt_acl : No ACLs on file (%s) !\n", fsp->fsp_name ));
+ return 0;
+ }
+
+ if (fsp->is_directory && dir_acl) {
+ dir_ace = canonicalise_acl(fsp, dir_acl, &sbuf,
+ &global_sid_Creator_Owner,
+ &global_sid_Creator_Group, pal, SMB_ACL_TYPE_DEFAULT );
+ }
+
+ /*
+ * Create the NT ACE list from the canonical ace lists.
+ */
+
+ {
+ canon_ace *ace;
+ int nt_acl_type;
+ int i;
+
+ if (nt4_compatible_acls() && dir_ace) {
+ /*
+ * NT 4 chokes if an ACL contains an INHERIT_ONLY entry
+ * but no non-INHERIT_ONLY entry for one SID. So we only
+ * remove entries from the Access ACL if the
+ * corresponding Default ACL entries have also been
+ * removed. ACEs for CREATOR-OWNER and CREATOR-GROUP
+ * are exceptions. We can do nothing
+ * intelligent if the Default ACL contains entries that
+ * are not also contained in the Access ACL, so this
+ * case will still fail under NT 4.
+ */
+
+ ace = canon_ace_entry_for(dir_ace, SMB_ACL_OTHER, NULL);
+ if (ace && !ace->perms) {
+ DLIST_REMOVE(dir_ace, ace);
+ SAFE_FREE(ace);
+
+ ace = canon_ace_entry_for(file_ace, SMB_ACL_OTHER, NULL);
+ if (ace && !ace->perms) {
+ DLIST_REMOVE(file_ace, ace);
+ SAFE_FREE(ace);
+ }
+ }
+
+ /*
+ * WinNT doesn't usually have Creator Group
+ * in browse lists, so we send this entry to
+ * WinNT even if it contains no relevant
+ * permissions. Once we can add
+ * Creator Group to browse lists we can
+ * re-enable this.
+ */
+
+#if 0
+ ace = canon_ace_entry_for(dir_ace, SMB_ACL_GROUP_OBJ, NULL);
+ if (ace && !ace->perms) {
+ DLIST_REMOVE(dir_ace, ace);
+ SAFE_FREE(ace);
+ }
+#endif
+
+ ace = canon_ace_entry_for(file_ace, SMB_ACL_GROUP_OBJ, NULL);
+ if (ace && !ace->perms) {
+ DLIST_REMOVE(file_ace, ace);
+ SAFE_FREE(ace);
+ }
+ }
+
+ num_acls = count_canon_ace_list(file_ace);
+ num_dir_acls = count_canon_ace_list(dir_ace);
+
+ /* Allocate the ace list. */
+ if ((nt_ace_list = (SEC_ACE *)malloc((num_acls + num_profile_acls + num_dir_acls)* sizeof(SEC_ACE))) == NULL) {
+ DEBUG(0,("get_nt_acl: Unable to malloc space for nt_ace_list.\n"));
+ goto done;
+ }
+
+ memset(nt_ace_list, '\0', (num_acls + num_dir_acls) * sizeof(SEC_ACE) );
+
+ /*
+ * Create the NT ACE list from the canonical ace lists.
+ */
+
+ ace = file_ace;
+
+ for (i = 0; i < num_acls; i++, ace = ace->next) {
+ SEC_ACCESS acc;
+
+ acc = map_canon_ace_perms(&nt_acl_type, &owner_sid, ace );
+ init_sec_ace(&nt_ace_list[num_aces++], &ace->trustee, nt_acl_type, acc, ace->inherited ? SEC_ACE_FLAG_INHERITED_ACE : 0);
+ }
+
+ /* The User must have access to a profile share - even if we can't map the SID. */
+ if (lp_profile_acls(SNUM(fsp->conn))) {
+ SEC_ACCESS acc;
+
+ init_sec_access(&acc,FILE_GENERIC_ALL);
+ init_sec_ace(&nt_ace_list[num_aces++], &global_sid_Builtin_Users, SEC_ACE_TYPE_ACCESS_ALLOWED,
+ acc, 0);
+ }
+
+ ace = dir_ace;
+
+ for (i = 0; i < num_dir_acls; i++, ace = ace->next) {
+ SEC_ACCESS acc;
+
+ acc = map_canon_ace_perms(&nt_acl_type, &owner_sid, ace );
+ init_sec_ace(&nt_ace_list[num_aces++], &ace->trustee, nt_acl_type, acc,
+ SEC_ACE_FLAG_OBJECT_INHERIT|SEC_ACE_FLAG_CONTAINER_INHERIT|
+ SEC_ACE_FLAG_INHERIT_ONLY|
+ (ace->inherited ? SEC_ACE_FLAG_INHERITED_ACE : 0));
+ }
+
+ /* The User must have access to a profile share - even if we can't map the SID. */
+ if (lp_profile_acls(SNUM(fsp->conn))) {
+ SEC_ACCESS acc;
+
+ init_sec_access(&acc,FILE_GENERIC_ALL);
+ init_sec_ace(&nt_ace_list[num_aces++], &global_sid_Builtin_Users, SEC_ACE_TYPE_ACCESS_ALLOWED, acc,
+ SEC_ACE_FLAG_OBJECT_INHERIT|SEC_ACE_FLAG_CONTAINER_INHERIT|
+ SEC_ACE_FLAG_INHERIT_ONLY|0);
+ }
+
+ /*
+ * Merge POSIX default ACLs and normal ACLs into one NT ACE.
+ * Win2K needs this to get the inheritance correct when replacing ACLs
+ * on a directory tree. Based on work by Jim @ IBM.
+ */
+
+ num_aces = merge_default_aces(nt_ace_list, num_aces);
+
+ }
+
+ if (num_aces) {
+ if((psa = make_sec_acl( main_loop_talloc_get(), ACL_REVISION, num_aces, nt_ace_list)) == NULL) {
+ DEBUG(0,("get_nt_acl: Unable to malloc space for acl.\n"));
+ goto done;
+ }
+ }
+ } /* security_info & DACL_SECURITY_INFORMATION */
+
+ psd = make_standard_sec_desc( main_loop_talloc_get(),
+ (security_info & OWNER_SECURITY_INFORMATION) ? &owner_sid : NULL,
+ (security_info & GROUP_SECURITY_INFORMATION) ? &group_sid : NULL,
+ psa,
+ &sd_size);
+
+ if(!psd) {
+ DEBUG(0,("get_nt_acl: Unable to malloc space for security descriptor.\n"));
+ sd_size = 0;
+ } else {
+ /*
+ * Windows 2000: The DACL_PROTECTED flag in the security
+ * descriptor marks the ACL as non-inheriting, i.e., no
+ * ACEs from higher level directories propagate to this
+ * ACL. In the POSIX ACL model permissions are only
+ * inherited at file create time, so ACLs never contain
+ * any ACEs that are inherited dynamically. The DACL_PROTECTED
+ * flag doesn't seem to bother Windows NT.
+ */
+ if (get_protected_flag(pal))
+ psd->type |= SE_DESC_DACL_PROTECTED;
+ }
+
+ if (psd->dacl)
+ dacl_sort_into_canonical_order(psd->dacl->ace, (unsigned int)psd->dacl->num_aces);
+
+ *ppdesc = psd;
+
+ done:
+
+ if (posix_acl)
+ SMB_VFS_SYS_ACL_FREE_ACL(conn, posix_acl);
+ if (dir_acl)
+ SMB_VFS_SYS_ACL_FREE_ACL(conn, dir_acl);
+ free_canon_ace_list(file_ace);
+ free_canon_ace_list(dir_ace);
+ free_inherited_info(pal);
+ SAFE_FREE(nt_ace_list);
+
+ return sd_size;
+}
+
+/****************************************************************************
+ Try to chown a file. We will be able to chown it under the following conditions.
+
+ 1) If we have root privileges, then it will just work.
+ 2) If we have write permission to the file and dos_filemodes is set
+ then allow chown to the currently authenticated user.
+****************************************************************************/
+
+static int try_chown(connection_struct *conn, const char *fname, uid_t uid, gid_t gid)
+{
+ int ret;
+ extern struct current_user current_user;
+ files_struct *fsp;
+ SMB_STRUCT_STAT st;
+
+ /* try the direct way first */
+ ret = SMB_VFS_CHOWN(conn, fname, uid, gid);
+ if (ret == 0)
+ return 0;
+
+ if(!CAN_WRITE(conn) || !lp_dos_filemode(SNUM(conn)))
+ return -1;
+
+ if (SMB_VFS_STAT(conn,fname,&st))
+ return -1;
+
+ fsp = open_file_fchmod(conn,fname,&st);
+ if (!fsp)
+ return -1;
+
+ /* only allow chown to the current user. This is more secure,
+ and also copes with the case where the SID in a take ownership ACL is
+ a local SID on the users workstation
+ */
+ uid = current_user.uid;
+
+ become_root();
+ /* Keep the current file gid the same. */
+ ret = SMB_VFS_FCHOWN(fsp, fsp->fd, uid, (gid_t)-1);
+ unbecome_root();
+
+ close_file_fchmod(fsp);
+
+ return ret;
+}
+
+/****************************************************************************
+ Reply to set a security descriptor on an fsp. security_info_sent is the
+ description of the following NT ACL.
+ This should be the only external function needed for the UNIX style set ACL.
+****************************************************************************/
+
+BOOL set_nt_acl(files_struct *fsp, uint32 security_info_sent, SEC_DESC *psd)
+{
+ connection_struct *conn = fsp->conn;
+ uid_t user = (uid_t)-1;
+ gid_t grp = (gid_t)-1;
+ SMB_STRUCT_STAT sbuf;
+ DOM_SID file_owner_sid;
+ DOM_SID file_grp_sid;
+ canon_ace *file_ace_list = NULL;
+ canon_ace *dir_ace_list = NULL;
+ BOOL acl_perms = False;
+ mode_t orig_mode = (mode_t)0;
+ uid_t orig_uid;
+ gid_t orig_gid;
+ BOOL need_chown = False;
+ extern struct current_user current_user;
+
+ DEBUG(10,("set_nt_acl: called for file %s\n", fsp->fsp_name ));
+
+ if (!CAN_WRITE(conn)) {
+ DEBUG(10,("set acl rejected on read-only share\n"));
+ return False;
+ }
+
+ /*
+ * Get the current state of the file.
+ */
+
+ if(fsp->is_directory || fsp->fd == -1) {
+ if(SMB_VFS_STAT(fsp->conn,fsp->fsp_name, &sbuf) != 0)
+ return False;
+ } else {
+ if(SMB_VFS_FSTAT(fsp,fsp->fd,&sbuf) != 0)
+ return False;
+ }
+
+ /* Save the original elements we check against. */
+ orig_mode = sbuf.st_mode;
+ orig_uid = sbuf.st_uid;
+ orig_gid = sbuf.st_gid;
+
+ /*
+ * Unpack the user/group/world id's.
+ */
+
+ if (!unpack_nt_owners( &sbuf, &user, &grp, security_info_sent, psd))
+ return False;
+
+ /*
+ * Do we need to chown ?
+ */
+
+ if (((user != (uid_t)-1) && (orig_uid != user)) || (( grp != (gid_t)-1) && (orig_gid != grp)))
+ need_chown = True;
+
+ /*
+ * Chown before setting ACL only if we don't change the user, or
+ * if we change to the current user, but not if we want to give away
+ * the file.
+ */
+
+ if (need_chown && (user == (uid_t)-1 || user == current_user.uid)) {
+
+ DEBUG(3,("set_nt_acl: chown %s. uid = %u, gid = %u.\n",
+ fsp->fsp_name, (unsigned int)user, (unsigned int)grp ));
+
+ if(try_chown( fsp->conn, fsp->fsp_name, user, grp) == -1) {
+ DEBUG(3,("set_nt_acl: chown %s, %u, %u failed. Error = %s.\n",
+ fsp->fsp_name, (unsigned int)user, (unsigned int)grp, strerror(errno) ));
+ return False;
+ }
+
+ /*
+ * Recheck the current state of the file, which may have changed.
+ * (suid/sgid bits, for instance)
+ */
+
+ if(fsp->is_directory) {
+ if(SMB_VFS_STAT(fsp->conn, fsp->fsp_name, &sbuf) != 0) {
+ return False;
+ }
+ } else {
+
+ int ret;
+
+ if(fsp->fd == -1)
+ ret = SMB_VFS_STAT(fsp->conn, fsp->fsp_name, &sbuf);
+ else
+ ret = SMB_VFS_FSTAT(fsp,fsp->fd,&sbuf);
+
+ if(ret != 0)
+ return False;
+ }
+
+ /* Save the original elements we check against. */
+ orig_mode = sbuf.st_mode;
+ orig_uid = sbuf.st_uid;
+ orig_gid = sbuf.st_gid;
+
+ /* We did it, don't try again */
+ need_chown = False;
+ }
+
+ create_file_sids(&sbuf, &file_owner_sid, &file_grp_sid);
+
+ acl_perms = unpack_canon_ace( fsp, &sbuf, &file_owner_sid, &file_grp_sid,
+ &file_ace_list, &dir_ace_list, security_info_sent, psd);
+
+ /* Ignore W2K traverse DACL set. */
+ if (file_ace_list || dir_ace_list) {
+
+ if (!acl_perms) {
+ DEBUG(3,("set_nt_acl: cannot set permissions\n"));
+ free_canon_ace_list(file_ace_list);
+ free_canon_ace_list(dir_ace_list);
+ return False;
+ }
+
+ /*
+ * Only change security if we got a DACL.
+ */
+
+ if((security_info_sent & DACL_SECURITY_INFORMATION) && (psd->dacl != NULL)) {
+
+ BOOL acl_set_support = False;
+ BOOL ret = False;
+
+ /*
+ * Try using the POSIX ACL set first. Fall back to chmod if
+ * we have no ACL support on this filesystem.
+ */
+
+ if (acl_perms && file_ace_list) {
+ ret = set_canon_ace_list(fsp, file_ace_list, False, &acl_set_support);
+ if (acl_set_support && ret == False) {
+ DEBUG(3,("set_nt_acl: failed to set file acl on file %s (%s).\n", fsp->fsp_name, strerror(errno) ));
+ free_canon_ace_list(file_ace_list);
+ free_canon_ace_list(dir_ace_list);
+ return False;
+ }
+ }
+
+ if (acl_perms && acl_set_support && fsp->is_directory) {
+ if (dir_ace_list) {
+ if (!set_canon_ace_list(fsp, dir_ace_list, True, &acl_set_support)) {
+ DEBUG(3,("set_nt_acl: failed to set default acl on directory %s (%s).\n", fsp->fsp_name, strerror(errno) ));
+ free_canon_ace_list(file_ace_list);
+ free_canon_ace_list(dir_ace_list);
+ return False;
+ }
+ } else {
+
+ /*
+ * No default ACL - delete one if it exists.
+ */
+
+ if (SMB_VFS_SYS_ACL_DELETE_DEF_FILE(conn, fsp->fsp_name) == -1) {
+ DEBUG(3,("set_nt_acl: sys_acl_delete_def_file failed (%s)\n", strerror(errno)));
+ free_canon_ace_list(file_ace_list);
+ free_canon_ace_list(dir_ace_list);
+ return False;
+ }
+ }
+ }
+
+ if (acl_set_support)
+ store_inheritance_attributes(fsp, file_ace_list, dir_ace_list,
+ (psd->type & SE_DESC_DACL_PROTECTED) ? True : False);
+
+ /*
+ * If we cannot set using POSIX ACLs we fall back to checking if we need to chmod.
+ */
+
+ if(!acl_set_support && acl_perms) {
+ mode_t posix_perms;
+
+ if (!convert_canon_ace_to_posix_perms( fsp, file_ace_list, &posix_perms)) {
+ free_canon_ace_list(file_ace_list);
+ free_canon_ace_list(dir_ace_list);
+ DEBUG(3,("set_nt_acl: failed to convert file acl to posix permissions for file %s.\n",
+ fsp->fsp_name ));
+ return False;
+ }
+
+ if (orig_mode != posix_perms) {
+
+ DEBUG(3,("set_nt_acl: chmod %s. perms = 0%o.\n",
+ fsp->fsp_name, (unsigned int)posix_perms ));
+
+ if(SMB_VFS_CHMOD(conn,fsp->fsp_name, posix_perms) == -1) {
+ DEBUG(3,("set_nt_acl: chmod %s, 0%o failed. Error = %s.\n",
+ fsp->fsp_name, (unsigned int)posix_perms, strerror(errno) ));
+ free_canon_ace_list(file_ace_list);
+ free_canon_ace_list(dir_ace_list);
+ return False;
+ }
+ }
+ }
+ }
+
+ free_canon_ace_list(file_ace_list);
+ free_canon_ace_list(dir_ace_list);
+ }
+
+ /* Any chown pending? */
+ if (need_chown) {
+
+ DEBUG(3,("set_nt_acl: chown %s. uid = %u, gid = %u.\n",
+ fsp->fsp_name, (unsigned int)user, (unsigned int)grp ));
+
+ if(try_chown( fsp->conn, fsp->fsp_name, user, grp) == -1) {
+ DEBUG(3,("set_nt_acl: chown %s, %u, %u failed. Error = %s.\n",
+ fsp->fsp_name, (unsigned int)user, (unsigned int)grp, strerror(errno) ));
+ return False;
+ }
+ }
+
+ return True;
+}
+
+/****************************************************************************
+ Get the actual group bits stored on a file with an ACL. Has no effect if
+ the file has no ACL. Needed in dosmode code where the stat() will return
+ the mask bits, not the real group bits, for a file with an ACL.
+****************************************************************************/
+
+int get_acl_group_bits( connection_struct *conn, const char *fname, mode_t *mode )
+{
+ int entry_id = SMB_ACL_FIRST_ENTRY;
+ SMB_ACL_ENTRY_T entry;
+ SMB_ACL_T posix_acl;
+
+ posix_acl = SMB_VFS_SYS_ACL_GET_FILE(conn, fname, SMB_ACL_TYPE_ACCESS);
+ if (posix_acl == (SMB_ACL_T)NULL)
+ return -1;
+
+ while (SMB_VFS_SYS_ACL_GET_ENTRY(conn, posix_acl, entry_id, &entry) == 1) {
+ SMB_ACL_TAG_T tagtype;
+ SMB_ACL_PERMSET_T permset;
+
+ /* get_next... */
+ if (entry_id == SMB_ACL_FIRST_ENTRY)
+ entry_id = SMB_ACL_NEXT_ENTRY;
+
+ if (SMB_VFS_SYS_ACL_GET_TAG_TYPE(conn, entry, &tagtype) ==-1)
+ return -1;
+
+ if (tagtype == SMB_ACL_GROUP_OBJ) {
+ if (SMB_VFS_SYS_ACL_GET_PERMSET(conn, entry, &permset) == -1) {
+ return -1;
+ } else {
+ *mode &= ~(S_IRGRP|S_IWGRP|S_IXGRP);
+ *mode |= (SMB_VFS_SYS_ACL_GET_PERM(conn, permset, SMB_ACL_READ) ? S_IRGRP : 0);
+ *mode |= (SMB_VFS_SYS_ACL_GET_PERM(conn, permset, SMB_ACL_WRITE) ? S_IWGRP : 0);
+ *mode |= (SMB_VFS_SYS_ACL_GET_PERM(conn, permset, SMB_ACL_EXECUTE) ? S_IXGRP : 0);
+ return 0;;
+ }
+ }
+ }
+ return -1;
+}
+
+/****************************************************************************
+ Do a chmod by setting the ACL USER_OBJ, GROUP_OBJ and OTHER bits in an ACL
+ and set the mask to rwx. Needed to preserve complex ACLs set by NT.
+****************************************************************************/
+
+static int chmod_acl_internals( connection_struct *conn, SMB_ACL_T posix_acl, mode_t mode)
+{
+ int entry_id = SMB_ACL_FIRST_ENTRY;
+ SMB_ACL_ENTRY_T entry;
+ int num_entries = 0;
+
+ while ( SMB_VFS_SYS_ACL_GET_ENTRY(conn, posix_acl, entry_id, &entry) == 1) {
+ SMB_ACL_TAG_T tagtype;
+ SMB_ACL_PERMSET_T permset;
+ mode_t perms;
+
+ /* get_next... */
+ if (entry_id == SMB_ACL_FIRST_ENTRY)
+ entry_id = SMB_ACL_NEXT_ENTRY;
+
+ if (SMB_VFS_SYS_ACL_GET_TAG_TYPE(conn, entry, &tagtype) == -1)
+ return -1;
+
+ if (SMB_VFS_SYS_ACL_GET_PERMSET(conn, entry, &permset) == -1)
+ return -1;
+
+ num_entries++;
+
+ switch(tagtype) {
+ case SMB_ACL_USER_OBJ:
+ perms = unix_perms_to_acl_perms(mode, S_IRUSR, S_IWUSR, S_IXUSR);
+ break;
+ case SMB_ACL_GROUP_OBJ:
+ perms = unix_perms_to_acl_perms(mode, S_IRGRP, S_IWGRP, S_IXGRP);
+ break;
+ case SMB_ACL_MASK:
+ /*
+ * FIXME: The ACL_MASK entry permissions should really be set to
+ * the union of the permissions of all ACL_USER,
+ * ACL_GROUP_OBJ, and ACL_GROUP entries. That's what
+ * acl_calc_mask() does, but Samba ACLs doesn't provide it.
+ */
+ perms = S_IRUSR|S_IWUSR|S_IXUSR;
+ break;
+ case SMB_ACL_OTHER:
+ perms = unix_perms_to_acl_perms(mode, S_IROTH, S_IWOTH, S_IXOTH);
+ break;
+ default:
+ continue;
+ }
+
+ if (map_acl_perms_to_permset(conn, perms, &permset) == -1)
+ return -1;
+
+ if (SMB_VFS_SYS_ACL_SET_PERMSET(conn, entry, permset) == -1)
+ return -1;
+ }
+
+ /*
+ * If this is a simple 3 element ACL or no elements then it's a standard
+ * UNIX permission set. Just use chmod...
+ */
+
+ if ((num_entries == 3) || (num_entries == 0))
+ return -1;
+
+ return 0;
+}
+
+/****************************************************************************
+ Get the access ACL of FROM, do a chmod by setting the ACL USER_OBJ,
+ GROUP_OBJ and OTHER bits in an ACL and set the mask to rwx. Set the
+ resulting ACL on TO. Note that name is in UNIX character set.
+****************************************************************************/
+
+static int copy_access_acl(connection_struct *conn, const char *from, const char *to, mode_t mode)
+{
+ SMB_ACL_T posix_acl = NULL;
+ int ret = -1;
+
+ if ((posix_acl = SMB_VFS_SYS_ACL_GET_FILE(conn, from, SMB_ACL_TYPE_ACCESS)) == NULL)
+ return -1;
+
+ if ((ret = chmod_acl_internals(conn, posix_acl, mode)) == -1)
+ goto done;
+
+ ret = SMB_VFS_SYS_ACL_SET_FILE(conn, to, SMB_ACL_TYPE_ACCESS, posix_acl);
+
+ done:
+
+ SMB_VFS_SYS_ACL_FREE_ACL(conn, posix_acl);
+ return ret;
+}
+
+/****************************************************************************
+ Do a chmod by setting the ACL USER_OBJ, GROUP_OBJ and OTHER bits in an ACL
+ and set the mask to rwx. Needed to preserve complex ACLs set by NT.
+ Note that name is in UNIX character set.
+****************************************************************************/
+
+int chmod_acl(connection_struct *conn, const char *name, mode_t mode)
+{
+ return copy_access_acl(conn, name, name, mode);
+}
+
+/****************************************************************************
+ If "inherit permissions" is set and the parent directory has no default
+ ACL but it does have an Access ACL, inherit this Access ACL to file name.
+****************************************************************************/
+
+int inherit_access_acl(connection_struct *conn, const char *name, mode_t mode)
+{
+ pstring dirname;
+ pstrcpy(dirname, parent_dirname(name));
+
+ if (!lp_inherit_perms(SNUM(conn)) || directory_has_default_acl(conn, dirname))
+ return 0;
+
+ return copy_access_acl(conn, dirname, name, mode);
+}
+
+/****************************************************************************
+ Do an fchmod by setting the ACL USER_OBJ, GROUP_OBJ and OTHER bits in an ACL
+ and set the mask to rwx. Needed to preserve complex ACLs set by NT.
+****************************************************************************/
+
+int fchmod_acl(files_struct *fsp, int fd, mode_t mode)
+{
+ connection_struct *conn = fsp->conn;
+ SMB_ACL_T posix_acl = NULL;
+ int ret = -1;
+
+ if ((posix_acl = SMB_VFS_SYS_ACL_GET_FD(fsp, fd)) == NULL)
+ return -1;
+
+ if ((ret = chmod_acl_internals(conn, posix_acl, mode)) == -1)
+ goto done;
+
+ ret = SMB_VFS_SYS_ACL_SET_FD(fsp, fd, posix_acl);
+
+ done:
+
+ SMB_VFS_SYS_ACL_FREE_ACL(conn, posix_acl);
+ return ret;
+}
+
+/****************************************************************************
+ Check for an existing default POSIX ACL on a directory.
+****************************************************************************/
+
+BOOL directory_has_default_acl(connection_struct *conn, const char *fname)
+{
+ SMB_ACL_T dir_acl = SMB_VFS_SYS_ACL_GET_FILE( conn, fname, SMB_ACL_TYPE_DEFAULT);
+ BOOL has_acl = False;
+ SMB_ACL_ENTRY_T entry;
+
+ if (dir_acl != NULL && (SMB_VFS_SYS_ACL_GET_ENTRY(conn, dir_acl, SMB_ACL_FIRST_ENTRY, &entry) == 1))
+ has_acl = True;
+
+ if (dir_acl)
+ SMB_VFS_SYS_ACL_FREE_ACL(conn, dir_acl);
+ return has_acl;
+}
diff --git a/source/smbd/process.c b/source/smbd/process.c
new file mode 100644
index 00000000000..718d1bb67b2
--- /dev/null
+++ b/source/smbd/process.c
@@ -0,0 +1,1367 @@
+/*
+ Unix SMB/CIFS implementation.
+ process incoming packets - main loop
+ 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 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"
+
+struct timeval smb_last_time;
+
+static char *InBuffer = NULL;
+char *OutBuffer = NULL;
+char *last_inbuf = NULL;
+
+/*
+ * Size of data we can send to client. Set
+ * by the client for all protocols above CORE.
+ * Set by us for CORE protocol.
+ */
+int max_send = BUFFER_SIZE;
+/*
+ * Size of the data we can receive. Set by us.
+ * Can be modified by the max xmit parameter.
+ */
+int max_recv = BUFFER_SIZE;
+
+extern int last_message;
+extern int global_oplock_break;
+extern userdom_struct current_user_info;
+extern int smb_read_error;
+SIG_ATOMIC_T reload_after_sighup = 0;
+SIG_ATOMIC_T got_sig_term = 0;
+BOOL global_machine_password_needs_changing = False;
+extern int max_send;
+
+/****************************************************************************
+ Function to return the current request mid from Inbuffer.
+****************************************************************************/
+
+uint16 get_current_mid(void)
+{
+ return SVAL(InBuffer,smb_mid);
+}
+
+/****************************************************************************
+ structure to hold a linked list of queued messages.
+ for processing.
+****************************************************************************/
+
+typedef struct {
+ ubi_slNode msg_next;
+ char *msg_buf;
+ int msg_len;
+} pending_message_list;
+
+static ubi_slList smb_oplock_queue = { NULL, (ubi_slNodePtr)&smb_oplock_queue, 0};
+
+/****************************************************************************
+ Function to push a message onto the tail of a linked list of smb messages ready
+ for processing.
+****************************************************************************/
+
+static BOOL push_message(ubi_slList *list_head, char *buf, int msg_len)
+{
+ pending_message_list *msg = (pending_message_list *)
+ malloc(sizeof(pending_message_list));
+
+ if(msg == NULL) {
+ DEBUG(0,("push_message: malloc fail (1)\n"));
+ return False;
+ }
+
+ msg->msg_buf = (char *)malloc(msg_len);
+ if(msg->msg_buf == NULL) {
+ DEBUG(0,("push_message: malloc fail (2)\n"));
+ SAFE_FREE(msg);
+ return False;
+ }
+
+ memcpy(msg->msg_buf, buf, msg_len);
+ msg->msg_len = msg_len;
+
+ ubi_slAddTail( list_head, msg);
+
+ /* Push the MID of this packet on the signing queue. */
+ srv_defer_sign_response(SVAL(buf,smb_mid));
+
+ return True;
+}
+
+/****************************************************************************
+ Function to push a smb message onto a linked list of local smb messages ready
+ for processing.
+****************************************************************************/
+
+BOOL push_oplock_pending_smb_message(char *buf, int msg_len)
+{
+ return push_message(&smb_oplock_queue, buf, msg_len);
+}
+
+/****************************************************************************
+ Do all async processing in here. This includes UDB oplock messages, kernel
+ oplock messages, change notify events etc.
+****************************************************************************/
+
+static void async_processing(char *buffer, int buffer_len)
+{
+ DEBUG(10,("async_processing: Doing async processing.\n"));
+
+ /* check for oplock messages (both UDP and kernel) */
+ if (receive_local_message(buffer, buffer_len, 1)) {
+ process_local_message(buffer, buffer_len);
+ }
+
+ if (got_sig_term) {
+ exit_server("Caught TERM signal");
+ }
+
+ /* check for async change notify events */
+ process_pending_change_notify_queue(0);
+
+ /* check for sighup processing */
+ if (reload_after_sighup) {
+ change_to_root_user();
+ DEBUG(1,("Reloading services after SIGHUP\n"));
+ reload_services(False);
+ reload_after_sighup = 0;
+ }
+}
+
+/****************************************************************************
+ Do a select on an two fd's - with timeout.
+
+ If a local udp message has been pushed onto the
+ queue (this can only happen during oplock break
+ processing) call async_processing()
+
+ If a pending smb message has been pushed onto the
+ queue (this can only happen during oplock break
+ processing) return this next.
+
+ If the first smbfd is ready then read an smb from it.
+ if the second (loopback UDP) fd is ready then read a message
+ from it and setup the buffer header to identify the length
+ and from address.
+ Returns False on timeout or error.
+ Else returns True.
+
+The timeout is in milliseconds
+****************************************************************************/
+
+static BOOL receive_message_or_smb(char *buffer, int buffer_len, int timeout)
+{
+ fd_set fds;
+ int selrtn;
+ struct timeval to;
+ int maxfd;
+
+ smb_read_error = 0;
+
+ again:
+
+ /*
+ * Note that this call must be before processing any SMB
+ * messages as we need to synchronously process any messages
+ * we may have sent to ourselves from the previous SMB.
+ */
+ message_dispatch();
+
+ /*
+ * Check to see if we already have a message on the smb queue.
+ * If so - copy and return it.
+ */
+ if(ubi_slCount(&smb_oplock_queue) != 0) {
+ pending_message_list *msg = (pending_message_list *)ubi_slRemHead(&smb_oplock_queue);
+ memcpy(buffer, msg->msg_buf, MIN(buffer_len, msg->msg_len));
+
+ /* Free the message we just copied. */
+ SAFE_FREE(msg->msg_buf);
+ SAFE_FREE(msg);
+
+ DEBUG(5,("receive_message_or_smb: returning queued smb message.\n"));
+ return True;
+ }
+
+
+ /*
+ * Setup the select read fd set.
+ */
+
+ FD_ZERO(&fds);
+
+ /*
+ * Ensure we process oplock break messages by preference.
+ * We have to do this before the select, after the select
+ * and if the select returns EINTR. This is due to the fact
+ * that the selects called from async_processing can eat an EINTR
+ * caused by a signal (we can't take the break message there).
+ * This is hideously complex - *MUST* be simplified for 3.0 ! JRA.
+ */
+
+ if (oplock_message_waiting(&fds)) {
+ DEBUG(10,("receive_message_or_smb: oplock_message is waiting.\n"));
+ async_processing(buffer, buffer_len);
+ /*
+ * After async processing we must go and do the select again, as
+ * the state of the flag in fds for the server file descriptor is
+ * indeterminate - we may have done I/O on it in the oplock processing. JRA.
+ */
+ goto again;
+ }
+
+ FD_SET(smbd_server_fd(),&fds);
+ maxfd = setup_oplock_select_set(&fds);
+
+ to.tv_sec = timeout / 1000;
+ to.tv_usec = (timeout % 1000) * 1000;
+
+ selrtn = sys_select(MAX(maxfd,smbd_server_fd())+1,&fds,NULL,NULL,timeout>0?&to:NULL);
+
+ /* if we get EINTR then maybe we have received an oplock
+ signal - treat this as select returning 1. This is ugly, but
+ is the best we can do until the oplock code knows more about
+ signals */
+ if (selrtn == -1 && errno == EINTR) {
+ async_processing(buffer, buffer_len);
+ /*
+ * After async processing we must go and do the select again, as
+ * the state of the flag in fds for the server file descriptor is
+ * indeterminate - we may have done I/O on it in the oplock processing. JRA.
+ */
+ goto again;
+ }
+
+ /* Check if error */
+ if (selrtn == -1) {
+ /* something is wrong. Maybe the socket is dead? */
+ smb_read_error = READ_ERROR;
+ return False;
+ }
+
+ /* Did we timeout ? */
+ if (selrtn == 0) {
+ smb_read_error = READ_TIMEOUT;
+ return False;
+ }
+
+ /*
+ * Ensure we process oplock break messages by preference.
+ * This is IMPORTANT ! Otherwise we can starve other processes
+ * sending us an oplock break message. JRA.
+ */
+
+ if (oplock_message_waiting(&fds)) {
+ async_processing(buffer, buffer_len);
+ /*
+ * After async processing we must go and do the select again, as
+ * the state of the flag in fds for the server file descriptor is
+ * indeterminate - we may have done I/O on it in the oplock processing. JRA.
+ */
+ goto again;
+ }
+
+ return receive_smb(smbd_server_fd(), buffer, 0);
+}
+
+/****************************************************************************
+Get the next SMB packet, doing the local message processing automatically.
+****************************************************************************/
+
+BOOL receive_next_smb(char *inbuf, int bufsize, int timeout)
+{
+ BOOL got_keepalive;
+ BOOL ret;
+
+ do {
+ ret = receive_message_or_smb(inbuf,bufsize,timeout);
+
+ got_keepalive = (ret && (CVAL(inbuf,0) == SMBkeepalive));
+ } while (ret && got_keepalive);
+
+ return ret;
+}
+
+/****************************************************************************
+ We're terminating and have closed all our files/connections etc.
+ If there are any pending local messages we need to respond to them
+ before termination so that other smbds don't think we just died whilst
+ holding oplocks.
+****************************************************************************/
+
+void respond_to_all_remaining_local_messages(void)
+{
+ char buffer[1024];
+
+ /*
+ * Assert we have no exclusive open oplocks.
+ */
+
+ if(get_number_of_exclusive_open_oplocks()) {
+ DEBUG(0,("respond_to_all_remaining_local_messages: PANIC : we have %d exclusive oplocks.\n",
+ get_number_of_exclusive_open_oplocks() ));
+ return;
+ }
+
+ /*
+ * Keep doing receive_local_message with a 1 ms timeout until
+ * we have no more messages.
+ */
+
+ while(receive_local_message(buffer, sizeof(buffer), 1)) {
+ /* Deal with oplock break requests from other smbd's. */
+ process_local_message(buffer, sizeof(buffer));
+ }
+
+ return;
+}
+
+
+/*
+These flags determine some of the permissions required to do an operation
+
+Note that I don't set NEED_WRITE on some write operations because they
+are used by some brain-dead clients when printing, and I don't want to
+force write permissions on print services.
+*/
+#define AS_USER (1<<0)
+#define NEED_WRITE (1<<1)
+#define TIME_INIT (1<<2)
+#define CAN_IPC (1<<3)
+#define AS_GUEST (1<<5)
+#define QUEUE_IN_OPLOCK (1<<6)
+
+/*
+ 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;
+ int (*fn)(connection_struct *conn, char *, char *, int, int);
+ int flags;
+} smb_messages[256] = {
+
+/* 0x00 */ { "SMBmkdir",reply_mkdir,AS_USER | NEED_WRITE},
+/* 0x01 */ { "SMBrmdir",reply_rmdir,AS_USER | NEED_WRITE},
+/* 0x02 */ { "SMBopen",reply_open,AS_USER | QUEUE_IN_OPLOCK },
+/* 0x03 */ { "SMBcreate",reply_mknew,AS_USER},
+/* 0x04 */ { "SMBclose",reply_close,AS_USER | CAN_IPC },
+/* 0x05 */ { "SMBflush",reply_flush,AS_USER},
+/* 0x06 */ { "SMBunlink",reply_unlink,AS_USER | NEED_WRITE | QUEUE_IN_OPLOCK},
+/* 0x07 */ { "SMBmv",reply_mv,AS_USER | NEED_WRITE | QUEUE_IN_OPLOCK},
+/* 0x08 */ { "SMBgetatr",reply_getatr,AS_USER},
+/* 0x09 */ { "SMBsetatr",reply_setatr,AS_USER | NEED_WRITE},
+/* 0x0a */ { "SMBread",reply_read,AS_USER},
+/* 0x0b */ { "SMBwrite",reply_write,AS_USER | CAN_IPC },
+/* 0x0c */ { "SMBlock",reply_lock,AS_USER},
+/* 0x0d */ { "SMBunlock",reply_unlock,AS_USER},
+/* 0x0e */ { "SMBctemp",reply_ctemp,AS_USER | QUEUE_IN_OPLOCK },
+/* 0x0f */ { "SMBmknew",reply_mknew,AS_USER},
+/* 0x10 */ { "SMBchkpth",reply_chkpth,AS_USER},
+/* 0x11 */ { "SMBexit",reply_exit,0},
+/* 0x12 */ { "SMBlseek",reply_lseek,AS_USER},
+/* 0x13 */ { "SMBlockread",reply_lockread,AS_USER},
+/* 0x14 */ { "SMBwriteunlock",reply_writeunlock,AS_USER},
+/* 0x15 */ { NULL, NULL, 0 },
+/* 0x16 */ { NULL, NULL, 0 },
+/* 0x17 */ { NULL, NULL, 0 },
+/* 0x18 */ { NULL, NULL, 0 },
+/* 0x19 */ { NULL, NULL, 0 },
+/* 0x1a */ { "SMBreadbraw",reply_readbraw,AS_USER},
+/* 0x1b */ { "SMBreadBmpx",reply_readbmpx,AS_USER},
+/* 0x1c */ { "SMBreadBs",NULL,0 },
+/* 0x1d */ { "SMBwritebraw",reply_writebraw,AS_USER},
+/* 0x1e */ { "SMBwriteBmpx",reply_writebmpx,AS_USER},
+/* 0x1f */ { "SMBwriteBs",reply_writebs,AS_USER},
+/* 0x20 */ { "SMBwritec",NULL,0},
+/* 0x21 */ { NULL, NULL, 0 },
+/* 0x22 */ { "SMBsetattrE",reply_setattrE,AS_USER | NEED_WRITE },
+/* 0x23 */ { "SMBgetattrE",reply_getattrE,AS_USER },
+/* 0x24 */ { "SMBlockingX",reply_lockingX,AS_USER },
+/* 0x25 */ { "SMBtrans",reply_trans,AS_USER | CAN_IPC },
+/* 0x26 */ { "SMBtranss",NULL,AS_USER | CAN_IPC},
+/* 0x27 */ { "SMBioctl",reply_ioctl,0},
+/* 0x28 */ { "SMBioctls",NULL,AS_USER},
+/* 0x29 */ { "SMBcopy",reply_copy,AS_USER | NEED_WRITE | QUEUE_IN_OPLOCK },
+/* 0x2a */ { "SMBmove",NULL,AS_USER | NEED_WRITE | QUEUE_IN_OPLOCK },
+/* 0x2b */ { "SMBecho",reply_echo,0},
+/* 0x2c */ { "SMBwriteclose",reply_writeclose,AS_USER},
+/* 0x2d */ { "SMBopenX",reply_open_and_X,AS_USER | CAN_IPC | QUEUE_IN_OPLOCK },
+/* 0x2e */ { "SMBreadX",reply_read_and_X,AS_USER | CAN_IPC },
+/* 0x2f */ { "SMBwriteX",reply_write_and_X,AS_USER | CAN_IPC },
+/* 0x30 */ { NULL, NULL, 0 },
+/* 0x31 */ { NULL, NULL, 0 },
+/* 0x32 */ { "SMBtrans2", reply_trans2, AS_USER | CAN_IPC },
+/* 0x33 */ { "SMBtranss2", reply_transs2, AS_USER},
+/* 0x34 */ { "SMBfindclose", reply_findclose,AS_USER},
+/* 0x35 */ { "SMBfindnclose", reply_findnclose, AS_USER},
+/* 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",reply_tcon,0},
+/* 0x71 */ { "SMBtdis",reply_tdis,0},
+/* 0x72 */ { "SMBnegprot",reply_negprot,0},
+/* 0x73 */ { "SMBsesssetupX",reply_sesssetup_and_X,0},
+/* 0x74 */ { "SMBulogoffX", reply_ulogoffX, 0}, /* ulogoff doesn't give a valid TID */
+/* 0x75 */ { "SMBtconX",reply_tcon_and_X,0},
+/* 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",reply_dskattr,AS_USER},
+/* 0x81 */ { "SMBsearch",reply_search,AS_USER},
+/* 0x82 */ { "SMBffirst",reply_search,AS_USER},
+/* 0x83 */ { "SMBfunique",reply_search,AS_USER},
+/* 0x84 */ { "SMBfclose",reply_fclose,AS_USER},
+/* 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", reply_nttrans, AS_USER | CAN_IPC | QUEUE_IN_OPLOCK},
+/* 0xa1 */ { "SMBnttranss", reply_nttranss, AS_USER | CAN_IPC },
+/* 0xa2 */ { "SMBntcreateX", reply_ntcreate_and_X, AS_USER | CAN_IPC | QUEUE_IN_OPLOCK },
+/* 0xa3 */ { NULL, NULL, 0 },
+/* 0xa4 */ { "SMBntcancel", reply_ntcancel, 0 },
+/* 0xa5 */ { "SMBntrename", reply_ntrename, AS_USER | NEED_WRITE | QUEUE_IN_OPLOCK },
+/* 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",reply_printopen,AS_USER | QUEUE_IN_OPLOCK },
+/* 0xc1 */ { "SMBsplwr",reply_printwrite,AS_USER},
+/* 0xc2 */ { "SMBsplclose",reply_printclose,AS_USER},
+/* 0xc3 */ { "SMBsplretq",reply_printqueue,AS_USER},
+/* 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",reply_sends,AS_GUEST},
+/* 0xd1 */ { "SMBsendb",NULL,AS_GUEST},
+/* 0xd2 */ { "SMBfwdname",NULL,AS_GUEST},
+/* 0xd3 */ { "SMBcancelf",NULL,AS_GUEST},
+/* 0xd4 */ { "SMBgetmac",NULL,AS_GUEST},
+/* 0xd5 */ { "SMBsendstrt",reply_sendstrt,AS_GUEST},
+/* 0xd6 */ { "SMBsendend",reply_sendend,AS_GUEST},
+/* 0xd7 */ { "SMBsendtxt",reply_sendtxt,AS_GUEST},
+/* 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 }
+
+};
+
+/*******************************************************************
+ Dump a packet to a file.
+********************************************************************/
+
+static void smb_dump(const char *name, int type, char *data, ssize_t len)
+{
+ int fd, i;
+ pstring fname;
+ if (DEBUGLEVEL < 50) return;
+
+ if (len < 4) len = smb_len(data)+4;
+ for (i=1;i<100;i++) {
+ slprintf(fname,sizeof(fname)-1, "/tmp/%s.%d.%s", name, i,
+ type ? "req" : "resp");
+ fd = open(fname, O_WRONLY|O_CREAT|O_EXCL, 0644);
+ if (fd != -1 || errno != EEXIST) break;
+ }
+ if (fd != -1) {
+ ssize_t ret = write(fd, data, len);
+ if (ret != len)
+ DEBUG(0,("smb_dump: problem: write returned %d\n", (int)ret ));
+ close(fd);
+ DEBUG(0,("created %s len %lu\n", fname, (unsigned long)len));
+ }
+}
+
+
+/****************************************************************************
+ Do a switch on the message type, and return the response size
+****************************************************************************/
+
+static int switch_message(int type,char *inbuf,char *outbuf,int size,int bufsize)
+{
+ static pid_t pid= (pid_t)-1;
+ int outsize = 0;
+ extern uint16 global_smbpid;
+
+ type &= 0xff;
+
+ if (pid == (pid_t)-1)
+ pid = sys_getpid();
+
+ errno = 0;
+ last_message = type;
+
+ /* Make sure this is an SMB packet. smb_size contains NetBIOS header so subtract 4 from it. */
+ if ((strncmp(smb_base(inbuf),"\377SMB",4) != 0) || (size < (smb_size - 4))) {
+ DEBUG(2,("Non-SMB packet of length %d. Terminating server\n",smb_len(inbuf)));
+ exit_server("Non-SMB packet");
+ return(-1);
+ }
+
+ /* yuck! this is an interim measure before we get rid of our
+ current inbuf/outbuf system */
+ global_smbpid = SVAL(inbuf,smb_pid);
+
+ if (smb_messages[type].fn == NULL) {
+ DEBUG(0,("Unknown message type %d!\n",type));
+ smb_dump("Unknown", 1, inbuf, size);
+ outsize = reply_unknown(inbuf,outbuf);
+ } else {
+ int flags = smb_messages[type].flags;
+ static uint16 last_session_tag = UID_FIELD_INVALID;
+ /* In share mode security we must ignore the vuid. */
+ uint16 session_tag = (lp_security() == SEC_SHARE) ? UID_FIELD_INVALID : SVAL(inbuf,smb_uid);
+ connection_struct *conn = conn_find(SVAL(inbuf,smb_tid));
+
+ DEBUG(3,("switch message %s (pid %d)\n",smb_fn_name(type),(int)pid));
+
+ smb_dump(smb_fn_name(type), 1, inbuf, size);
+ if(global_oplock_break) {
+ if(flags & QUEUE_IN_OPLOCK) {
+ /*
+ * Queue this message as we are the process of an oplock break.
+ */
+
+ DEBUG( 2, ( "switch_message: queueing message due to being in " ) );
+ DEBUGADD( 2, ( "oplock break state.\n" ) );
+
+ push_oplock_pending_smb_message( inbuf, size );
+ return -1;
+ }
+ }
+
+ /* Ensure this value is replaced in the incoming packet. */
+ SSVAL(inbuf,smb_uid,session_tag);
+
+ /*
+ * Ensure the correct username is in current_user_info.
+ * This is a really ugly bugfix for problems with
+ * multiple session_setup_and_X's being done and
+ * allowing %U and %G substitutions to work correctly.
+ * There is a reason this code is done here, don't
+ * move it unless you know what you're doing... :-).
+ * JRA.
+ */
+
+ if (session_tag != last_session_tag) {
+ user_struct *vuser = NULL;
+
+ last_session_tag = session_tag;
+ if(session_tag != UID_FIELD_INVALID)
+ vuser = get_valid_user_struct(session_tag);
+ if(vuser != NULL)
+ set_current_user_info(&vuser->user);
+ }
+
+ /* does this protocol need to be run as root? */
+ if (!(flags & AS_USER))
+ change_to_root_user();
+
+ /* does this protocol need a valid tree connection? */
+ if ((flags & AS_USER) && !conn)
+ return ERROR_DOS(ERRSRV, ERRinvnid);
+
+
+ /* does this protocol need to be run as the connected user? */
+ if ((flags & AS_USER) && !change_to_user(conn,session_tag)) {
+ if (flags & AS_GUEST)
+ flags &= ~AS_USER;
+ else
+ return(ERROR_DOS(ERRSRV,ERRaccess));
+ }
+
+ /* this code is to work around a bug is MS client 3 without
+ introducing a security hole - it needs to be able to do
+ print queue checks as guest if it isn't logged in properly */
+ if (flags & AS_USER)
+ flags &= ~AS_GUEST;
+
+ /* does it need write permission? */
+ if ((flags & NEED_WRITE) && !CAN_WRITE(conn))
+ return(ERROR_DOS(ERRSRV,ERRaccess));
+
+ /* ipc services are limited */
+ if (IS_IPC(conn) && (flags & AS_USER) && !(flags & CAN_IPC))
+ return(ERROR_DOS(ERRSRV,ERRaccess));
+
+ /* load service specific parameters */
+ if (conn && !set_current_service(conn,(flags & AS_USER)?True:False))
+ return(ERROR_DOS(ERRSRV,ERRaccess));
+
+ /* does this protocol need to be run as guest? */
+ if ((flags & AS_GUEST) && (!change_to_guest() ||
+ !check_access(smbd_server_fd(), lp_hostsallow(-1), lp_hostsdeny(-1))))
+ return(ERROR_DOS(ERRSRV,ERRaccess));
+
+ last_inbuf = inbuf;
+
+ outsize = smb_messages[type].fn(conn, inbuf,outbuf,size,bufsize);
+ }
+
+ smb_dump(smb_fn_name(type), 0, outbuf, outsize);
+
+ return(outsize);
+}
+
+
+/****************************************************************************
+ Construct a reply to the incoming packet.
+****************************************************************************/
+
+static int construct_reply(char *inbuf,char *outbuf,int size,int bufsize)
+{
+ int type = CVAL(inbuf,smb_com);
+ int outsize = 0;
+ int msg_type = CVAL(inbuf,0);
+
+ GetTimeOfDay(&smb_last_time);
+
+ chain_size = 0;
+ file_chain_reset();
+ reset_chain_p();
+
+ if (msg_type != 0)
+ return(reply_special(inbuf,outbuf));
+
+ construct_reply_common(inbuf, outbuf);
+
+ outsize = switch_message(type,inbuf,outbuf,size,bufsize);
+
+ outsize += chain_size;
+
+ if(outsize > 4)
+ smb_setlen(outbuf,outsize - 4);
+ return(outsize);
+}
+
+/****************************************************************************
+ Keep track of the number of running smbd's. This functionality is used to
+ 'hard' limit Samba overhead on resource constrained systems.
+****************************************************************************/
+
+static BOOL process_count_update_successful = False;
+
+static int32 increment_smbd_process_count(void)
+{
+ int32 total_smbds;
+
+ if (lp_max_smbd_processes()) {
+ total_smbds = 0;
+ if (tdb_change_int32_atomic(conn_tdb_ctx(), "INFO/total_smbds", &total_smbds, 1) == -1)
+ return 1;
+ process_count_update_successful = True;
+ return total_smbds + 1;
+ }
+ return 1;
+}
+
+void decrement_smbd_process_count(void)
+{
+ int32 total_smbds;
+
+ if (lp_max_smbd_processes() && process_count_update_successful) {
+ total_smbds = 1;
+ tdb_change_int32_atomic(conn_tdb_ctx(), "INFO/total_smbds", &total_smbds, -1);
+ }
+}
+
+static BOOL smbd_process_limit(void)
+{
+ int32 total_smbds;
+
+ if (lp_max_smbd_processes()) {
+
+ /* Always add one to the smbd process count, as exit_server() always
+ * subtracts one.
+ */
+
+ if (!conn_tdb_ctx()) {
+ DEBUG(0,("smbd_process_limit: max smbd processes parameter set with status parameter not \
+set. Ignoring max smbd restriction.\n"));
+ return False;
+ }
+
+ total_smbds = increment_smbd_process_count();
+ return total_smbds > lp_max_smbd_processes();
+ }
+ else
+ return False;
+}
+
+/****************************************************************************
+ Process an smb from the client - split out from the smbd_process() code so
+ it can be used by the oplock break code.
+****************************************************************************/
+
+void process_smb(char *inbuf, char *outbuf)
+{
+ static int trans_num;
+ int msg_type = CVAL(inbuf,0);
+ int32 len = smb_len(inbuf);
+ int nread = len + 4;
+
+ DO_PROFILE_INC(smb_count);
+
+ if (trans_num == 0) {
+ /* on the first packet, check the global hosts allow/ hosts
+ deny parameters before doing any parsing of the packet
+ passed to us by the client. This prevents attacks on our
+ parsing code from hosts not in the hosts allow list */
+ if (smbd_process_limit() ||
+ !check_access(smbd_server_fd(), lp_hostsallow(-1), lp_hostsdeny(-1))) {
+ /* send a negative session response "not listening on calling name" */
+ static unsigned char buf[5] = {0x83, 0, 0, 1, 0x81};
+ DEBUG( 1, ( "Connection denied from %s\n", client_addr() ) );
+ (void)send_smb(smbd_server_fd(),(char *)buf);
+ exit_server("connection denied");
+ }
+ }
+
+ DEBUG( 6, ( "got message type 0x%x of len 0x%x\n", msg_type, len ) );
+ DEBUG( 3, ( "Transaction %d of length %d\n", trans_num, nread ) );
+
+ if (msg_type == 0)
+ show_msg(inbuf);
+ else if(msg_type == SMBkeepalive)
+ return; /* Keepalive packet. */
+
+ nread = construct_reply(inbuf,outbuf,nread,max_send);
+
+ if(nread > 0) {
+ if (CVAL(outbuf,0) == 0)
+ show_msg(outbuf);
+
+ if (nread != smb_len(outbuf) + 4) {
+ DEBUG(0,("ERROR: Invalid message response size! %d %d\n",
+ nread, smb_len(outbuf)));
+ } else if (!send_smb(smbd_server_fd(),outbuf)) {
+ exit_server("process_smb: send_smb failed.");
+ }
+ }
+ trans_num++;
+}
+
+/****************************************************************************
+ Return a string containing the function name of a SMB command.
+****************************************************************************/
+
+const char *smb_fn_name(int type)
+{
+ const char *unknown_name = "SMBunknown";
+
+ if (smb_messages[type].name == NULL)
+ return(unknown_name);
+
+ return(smb_messages[type].name);
+}
+
+/****************************************************************************
+ Helper functions for contruct_reply.
+****************************************************************************/
+
+static uint32 common_flags2 = FLAGS2_LONG_PATH_COMPONENTS|FLAGS2_EXTENDED_SECURITY|FLAGS2_32_BIT_ERROR_CODES;
+
+void remove_from_common_flags2(uint32 v)
+{
+ common_flags2 &= ~v;
+}
+
+void construct_reply_common(char *inbuf,char *outbuf)
+{
+ memset(outbuf,'\0',smb_size);
+
+ set_message(outbuf,0,0,True);
+ SCVAL(outbuf,smb_com,CVAL(inbuf,smb_com));
+
+ memcpy(outbuf+4,inbuf+4,4);
+ SCVAL(outbuf,smb_rcls,SMB_SUCCESS);
+ SCVAL(outbuf,smb_reh,0);
+ SCVAL(outbuf,smb_flg, FLAG_REPLY | (CVAL(inbuf,smb_flg) & FLAG_CASELESS_PATHNAMES));
+ SSVAL(outbuf,smb_flg2,
+ (SVAL(inbuf,smb_flg2) & FLAGS2_UNICODE_STRINGS) |
+ common_flags2);
+
+ SSVAL(outbuf,smb_err,SMB_SUCCESS);
+ SSVAL(outbuf,smb_tid,SVAL(inbuf,smb_tid));
+ SSVAL(outbuf,smb_pid,SVAL(inbuf,smb_pid));
+ SSVAL(outbuf,smb_uid,SVAL(inbuf,smb_uid));
+ SSVAL(outbuf,smb_mid,SVAL(inbuf,smb_mid));
+}
+
+/****************************************************************************
+ Construct a chained reply and add it to the already made reply
+****************************************************************************/
+
+int chain_reply(char *inbuf,char *outbuf,int size,int bufsize)
+{
+ static char *orig_inbuf;
+ static char *orig_outbuf;
+ int smb_com1, smb_com2 = CVAL(inbuf,smb_vwv0);
+ unsigned smb_off2 = SVAL(inbuf,smb_vwv1);
+ char *inbuf2, *outbuf2;
+ int outsize2;
+ char inbuf_saved[smb_wct];
+ char outbuf_saved[smb_wct];
+ int wct = CVAL(outbuf,smb_wct);
+ int outsize = smb_size + 2*wct + SVAL(outbuf,smb_vwv0+2*wct);
+
+ /* maybe its not chained */
+ if (smb_com2 == 0xFF) {
+ SCVAL(outbuf,smb_vwv0,0xFF);
+ return outsize;
+ }
+
+ if (chain_size == 0) {
+ /* this is the first part of the chain */
+ orig_inbuf = inbuf;
+ orig_outbuf = outbuf;
+ }
+
+ /*
+ * The original Win95 redirector dies on a reply to
+ * a lockingX and read chain unless the chain reply is
+ * 4 byte aligned. JRA.
+ */
+
+ outsize = (outsize + 3) & ~3;
+
+ /* we need to tell the client where the next part of the reply will be */
+ SSVAL(outbuf,smb_vwv1,smb_offset(outbuf+outsize,outbuf));
+ SCVAL(outbuf,smb_vwv0,smb_com2);
+
+ /* remember how much the caller added to the chain, only counting stuff
+ after the parameter words */
+ chain_size += outsize - smb_wct;
+
+ /* work out pointers into the original packets. The
+ headers on these need to be filled in */
+ inbuf2 = orig_inbuf + smb_off2 + 4 - smb_wct;
+ outbuf2 = orig_outbuf + SVAL(outbuf,smb_vwv1) + 4 - smb_wct;
+
+ /* remember the original command type */
+ smb_com1 = CVAL(orig_inbuf,smb_com);
+
+ /* save the data which will be overwritten by the new headers */
+ memcpy(inbuf_saved,inbuf2,smb_wct);
+ memcpy(outbuf_saved,outbuf2,smb_wct);
+
+ /* give the new packet the same header as the last part of the SMB */
+ memmove(inbuf2,inbuf,smb_wct);
+
+ /* create the in buffer */
+ SCVAL(inbuf2,smb_com,smb_com2);
+
+ /* create the out buffer */
+ construct_reply_common(inbuf2, outbuf2);
+
+ DEBUG(3,("Chained message\n"));
+ show_msg(inbuf2);
+
+ /* process the request */
+ outsize2 = switch_message(smb_com2,inbuf2,outbuf2,size-chain_size,
+ bufsize-chain_size);
+
+ /* copy the new reply and request headers over the old ones, but
+ preserve the smb_com field */
+ memmove(orig_outbuf,outbuf2,smb_wct);
+ SCVAL(orig_outbuf,smb_com,smb_com1);
+
+ /* restore the saved data, being careful not to overwrite any
+ data from the reply header */
+ memcpy(inbuf2,inbuf_saved,smb_wct);
+
+ {
+ int ofs = smb_wct - PTR_DIFF(outbuf2,orig_outbuf);
+ if (ofs < 0) ofs = 0;
+ memmove(outbuf2+ofs,outbuf_saved+ofs,smb_wct-ofs);
+ }
+
+ return outsize2;
+}
+
+/****************************************************************************
+ Setup the needed select timeout.
+****************************************************************************/
+
+static int setup_select_timeout(void)
+{
+ int select_timeout;
+ int t;
+
+ select_timeout = blocking_locks_timeout(SMBD_SELECT_TIMEOUT);
+ select_timeout *= 1000;
+
+ t = change_notify_timeout();
+ if (t != -1)
+ select_timeout = MIN(select_timeout, t*1000);
+
+ if (print_notify_messages_pending())
+ select_timeout = MIN(select_timeout, 1000);
+
+ return select_timeout;
+}
+
+/****************************************************************************
+ Check if services need reloading.
+****************************************************************************/
+
+void check_reload(int t)
+{
+ static time_t last_smb_conf_reload_time = 0;
+
+ if(last_smb_conf_reload_time == 0)
+ last_smb_conf_reload_time = t;
+
+ if (reload_after_sighup || (t >= last_smb_conf_reload_time+SMBD_RELOAD_CHECK)) {
+ reload_services(True);
+ reload_after_sighup = False;
+ last_smb_conf_reload_time = t;
+ }
+}
+
+/****************************************************************************
+ Process any timeout housekeeping. Return False if the caller should exit.
+****************************************************************************/
+
+static BOOL timeout_processing(int deadtime, int *select_timeout, time_t *last_timeout_processing_time)
+{
+ static time_t last_keepalive_sent_time = 0;
+ static time_t last_idle_closed_check = 0;
+ time_t t;
+ BOOL allidle = True;
+ extern int keepalive;
+
+ if (smb_read_error == READ_EOF) {
+ DEBUG(3,("timeout_processing: End of file from client (client has disconnected).\n"));
+ return False;
+ }
+
+ if (smb_read_error == READ_ERROR) {
+ DEBUG(3,("timeout_processing: receive_smb error (%s) Exiting\n",
+ strerror(errno)));
+ return False;
+ }
+
+ if (smb_read_error == READ_BAD_SIG) {
+ DEBUG(3,("timeout_processing: receive_smb error bad smb signature. Exiting\n"));
+ return False;
+ }
+
+ *last_timeout_processing_time = t = time(NULL);
+
+ if(last_keepalive_sent_time == 0)
+ last_keepalive_sent_time = t;
+
+ if(last_idle_closed_check == 0)
+ last_idle_closed_check = t;
+
+ /* become root again if waiting */
+ change_to_root_user();
+
+ /* run all registered idle events */
+ smb_run_idle_events(t);
+
+ /* check if we need to reload services */
+ check_reload(t);
+
+ /* automatic timeout if all connections are closed */
+ if (conn_num_open()==0 && (t - last_idle_closed_check) >= IDLE_CLOSED_TIMEOUT) {
+ DEBUG( 2, ( "Closing idle connection\n" ) );
+ return False;
+ } else {
+ last_idle_closed_check = t;
+ }
+
+ if (keepalive && (t - last_keepalive_sent_time)>keepalive) {
+ extern struct auth_context *negprot_global_auth_context;
+ if (!send_keepalive(smbd_server_fd())) {
+ DEBUG( 2, ( "Keepalive failed - exiting.\n" ) );
+ return False;
+ }
+
+ /* send a keepalive for a password server or the like.
+ This is attached to the auth_info created in the
+ negprot */
+ if (negprot_global_auth_context && negprot_global_auth_context->challenge_set_method
+ && negprot_global_auth_context->challenge_set_method->send_keepalive) {
+
+ negprot_global_auth_context->challenge_set_method->send_keepalive
+ (&negprot_global_auth_context->challenge_set_method->private_data);
+ }
+
+ last_keepalive_sent_time = t;
+ }
+
+ /* check for connection timeouts */
+ allidle = conn_idle_all(t, deadtime);
+
+ if (allidle && conn_num_open()>0) {
+ DEBUG(2,("Closing idle connection 2.\n"));
+ return False;
+ }
+
+ if(global_machine_password_needs_changing &&
+ /* for ADS we need to do a regular ADS password change, not a domain
+ password change */
+ lp_security() == SEC_DOMAIN) {
+
+ unsigned char trust_passwd_hash[16];
+ time_t lct;
+
+ /*
+ * We're in domain level security, and the code that
+ * read the machine password flagged that the machine
+ * password needs changing.
+ */
+
+ /*
+ * First, open the machine password file with an exclusive lock.
+ */
+
+ if (secrets_lock_trust_account_password(lp_workgroup(), True) == False) {
+ DEBUG(0,("process: unable to lock the machine account password for \
+machine %s in domain %s.\n", global_myname(), lp_workgroup() ));
+ return True;
+ }
+
+ if(!secrets_fetch_trust_account_password(lp_workgroup(), trust_passwd_hash, &lct, NULL)) {
+ DEBUG(0,("process: unable to read the machine account password for \
+machine %s in domain %s.\n", global_myname(), lp_workgroup()));
+ secrets_lock_trust_account_password(lp_workgroup(), False);
+ return True;
+ }
+
+ /*
+ * Make sure someone else hasn't already done this.
+ */
+
+ if(t < lct + lp_machine_password_timeout()) {
+ global_machine_password_needs_changing = False;
+ secrets_lock_trust_account_password(lp_workgroup(), False);
+ return True;
+ }
+
+ /* always just contact the PDC here */
+
+ change_trust_account_password( lp_workgroup(), NULL);
+ global_machine_password_needs_changing = False;
+ secrets_lock_trust_account_password(lp_workgroup(), False);
+ }
+
+ /*
+ * Check to see if we have any blocking locks
+ * outstanding on the queue.
+ */
+ process_blocking_lock_queue(t);
+
+ /* update printer queue caches if necessary */
+
+ update_monitored_printq_cache();
+
+ /*
+ * Check to see if we have any change notifies
+ * outstanding on the queue.
+ */
+ process_pending_change_notify_queue(t);
+
+ /*
+ * Now we are root, check if the log files need pruning.
+ * Force a log file check.
+ */
+ force_check_log_size();
+ check_log_size();
+
+ /* Send any queued printer notify message to interested smbd's. */
+
+ print_notify_send_messages(0);
+
+ /*
+ * Modify the select timeout depending upon
+ * what we have remaining in our queues.
+ */
+
+ *select_timeout = setup_select_timeout();
+
+ return True;
+}
+
+/****************************************************************************
+ process commands from the client
+****************************************************************************/
+
+void smbd_process(void)
+{
+ extern int smb_echo_count;
+ time_t last_timeout_processing_time = time(NULL);
+ unsigned int num_smbs = 0;
+ const size_t total_buffer_size = BUFFER_SIZE + LARGE_WRITEX_HDR_SIZE + SAFETY_MARGIN;
+
+ InBuffer = (char *)malloc(total_buffer_size);
+ OutBuffer = (char *)malloc(total_buffer_size);
+ if ((InBuffer == NULL) || (OutBuffer == NULL))
+ return;
+
+#if defined(DEVELOPER)
+ clobber_region(SAFE_STRING_FUNCTION_NAME, SAFE_STRING_LINE, InBuffer, total_buffer_size);
+ clobber_region(SAFE_STRING_FUNCTION_NAME, SAFE_STRING_LINE, OutBuffer, total_buffer_size);
+#endif
+
+ max_recv = MIN(lp_maxxmit(),BUFFER_SIZE);
+
+ while (True) {
+ int deadtime = lp_deadtime()*60;
+ int select_timeout = setup_select_timeout();
+ int num_echos;
+
+ if (deadtime <= 0)
+ deadtime = DEFAULT_SMBD_TIMEOUT;
+
+ errno = 0;
+
+ /* free up temporary memory */
+ lp_talloc_free();
+ main_loop_talloc_free();
+
+ /* run all registered idle events */
+ smb_run_idle_events(time(NULL));
+
+
+ /* Did someone ask for immediate checks on things like blocking locks ? */
+ if (select_timeout == 0) {
+ if(!timeout_processing( deadtime, &select_timeout, &last_timeout_processing_time))
+ return;
+ num_smbs = 0; /* Reset smb counter. */
+ }
+
+#if defined(DEVELOPER)
+ clobber_region(SAFE_STRING_FUNCTION_NAME, SAFE_STRING_LINE, InBuffer, total_buffer_size);
+#endif
+
+ while (!receive_message_or_smb(InBuffer,BUFFER_SIZE+LARGE_WRITEX_HDR_SIZE,select_timeout)) {
+ if(!timeout_processing( deadtime, &select_timeout, &last_timeout_processing_time))
+ return;
+ num_smbs = 0; /* Reset smb counter. */
+ }
+
+ /*
+ * Ensure we do timeout processing if the SMB we just got was
+ * only an echo request. This allows us to set the select
+ * timeout in 'receive_message_or_smb()' to any value we like
+ * without worrying that the client will send echo requests
+ * faster than the select timeout, thus starving out the
+ * essential processing (change notify, blocking locks) that
+ * the timeout code does. JRA.
+ */
+ num_echos = smb_echo_count;
+
+ clobber_region(SAFE_STRING_FUNCTION_NAME, SAFE_STRING_LINE, OutBuffer, total_buffer_size);
+
+ process_smb(InBuffer, OutBuffer);
+
+ if (smb_echo_count != num_echos) {
+ if(!timeout_processing( deadtime, &select_timeout, &last_timeout_processing_time))
+ return;
+ num_smbs = 0; /* Reset smb counter. */
+ }
+
+ num_smbs++;
+
+ /*
+ * If we are getting smb requests in a constant stream
+ * with no echos, make sure we attempt timeout processing
+ * every select_timeout milliseconds - but only check for this
+ * every 200 smb requests.
+ */
+
+ if ((num_smbs % 200) == 0) {
+ time_t new_check_time = time(NULL);
+ if(new_check_time - last_timeout_processing_time >= (select_timeout/1000)) {
+ if(!timeout_processing( deadtime, &select_timeout, &last_timeout_processing_time))
+ return;
+ num_smbs = 0; /* Reset smb counter. */
+ last_timeout_processing_time = new_check_time; /* Reset time. */
+ }
+ }
+
+ /* The timeout_processing function isn't run nearly
+ often enough to implement 'max log size' without
+ overrunning the size of the file by many megabytes.
+ This is especially true if we are running at debug
+ level 10. Checking every 50 SMBs is a nice
+ tradeoff of performance vs log file size overrun. */
+
+ if ((num_smbs % 50) == 0 && need_to_check_log_size()) {
+ change_to_root_user();
+ check_log_size();
+ }
+ }
+}
diff --git a/source/smbd/quotas.c b/source/smbd/quotas.c
new file mode 100644
index 00000000000..e439c1e571a
--- /dev/null
+++ b/source/smbd/quotas.c
@@ -0,0 +1,1279 @@
+/*
+ Unix SMB/CIFS implementation.
+ support for quotas
+ 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 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.
+*/
+
+
+/*
+ * This is one of the most system dependent parts of Samba, and its
+ * done a litle differently. Each system has its own way of doing
+ * things :-(
+ */
+
+#include "includes.h"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_QUOTA
+
+#ifndef HAVE_SYS_QUOTAS
+
+/* just a quick hack because sysquotas.h is included before linux/quota.h */
+#ifdef QUOTABLOCK_SIZE
+#undef QUOTABLOCK_SIZE
+#endif
+
+#ifdef WITH_QUOTAS
+
+#if defined(VXFS_QUOTA)
+
+/*
+ * In addition to their native filesystems, some systems have Veritas VxFS.
+ * Declare here, define at end: reduces likely "include" interaction problems.
+ * David Lee <T.D.Lee@durham.ac.uk>
+ */
+BOOL disk_quotas_vxfs(const pstring name, char *path, SMB_BIG_UINT *bsize, SMB_BIG_UINT *dfree, SMB_BIG_UINT *dsize);
+
+#endif /* VXFS_QUOTA */
+
+#ifdef LINUX
+
+#include <sys/types.h>
+#include <mntent.h>
+
+/*
+ * This shouldn't be neccessary - it should be /usr/include/sys/quota.h
+ * So we include all the files has *should* be in the system into a large,
+ * grungy samba_linux_quoatas.h Sometimes I *hate* Linux :-). JRA.
+ */
+
+#include "samba_linux_quota.h"
+#include "samba_xfs_quota.h"
+
+typedef struct _LINUX_SMB_DISK_QUOTA {
+ SMB_BIG_UINT bsize;
+ SMB_BIG_UINT hardlimit; /* In bsize units. */
+ SMB_BIG_UINT softlimit; /* In bsize units. */
+ SMB_BIG_UINT curblocks; /* In bsize units. */
+ SMB_BIG_UINT ihardlimit; /* inode hard limit. */
+ SMB_BIG_UINT isoftlimit; /* inode soft limit. */
+ SMB_BIG_UINT curinodes; /* Current used inodes. */
+} LINUX_SMB_DISK_QUOTA;
+
+/****************************************************************************
+ Abstract out the XFS Quota Manager quota get call.
+****************************************************************************/
+
+static int get_smb_linux_xfs_quota(char *path, uid_t euser_id, gid_t egrp_id, LINUX_SMB_DISK_QUOTA *dp)
+{
+ struct fs_disk_quota D;
+ int ret;
+
+ ZERO_STRUCT(D);
+
+ ret = quotactl(QCMD(Q_XGETQUOTA,USRQUOTA), path, euser_id, (caddr_t)&D);
+
+ if (ret)
+ ret = quotactl(QCMD(Q_XGETQUOTA,GRPQUOTA), path, egrp_id, (caddr_t)&D);
+
+ if (ret)
+ return ret;
+
+ dp->bsize = (SMB_BIG_UINT)512;
+ dp->softlimit = (SMB_BIG_UINT)D.d_blk_softlimit;
+ dp->hardlimit = (SMB_BIG_UINT)D.d_blk_hardlimit;
+ dp->ihardlimit = (SMB_BIG_UINT)D.d_ino_hardlimit;
+ dp->isoftlimit = (SMB_BIG_UINT)D.d_ino_softlimit;
+ dp->curinodes = (SMB_BIG_UINT)D.d_icount;
+ dp->curblocks = (SMB_BIG_UINT)D.d_bcount;
+
+ return ret;
+}
+
+/****************************************************************************
+ Abstract out the old and new Linux quota get calls.
+****************************************************************************/
+
+static int get_smb_linux_v1_quota(char *path, uid_t euser_id, gid_t egrp_id, LINUX_SMB_DISK_QUOTA *dp)
+{
+ struct v1_kern_dqblk D;
+ int ret;
+
+ ZERO_STRUCT(D);
+
+ ret = quotactl(QCMD(Q_V1_GETQUOTA,USRQUOTA), path, euser_id, (caddr_t)&D);
+
+ if (ret && errno != EDQUOT)
+ ret = quotactl(QCMD(Q_V1_GETQUOTA,GRPQUOTA), path, egrp_id, (caddr_t)&D);
+
+ if (ret && errno != EDQUOT)
+ return ret;
+
+ dp->bsize = (SMB_BIG_UINT)QUOTABLOCK_SIZE;
+ dp->softlimit = (SMB_BIG_UINT)D.dqb_bsoftlimit;
+ dp->hardlimit = (SMB_BIG_UINT)D.dqb_bhardlimit;
+ dp->ihardlimit = (SMB_BIG_UINT)D.dqb_ihardlimit;
+ dp->isoftlimit = (SMB_BIG_UINT)D.dqb_isoftlimit;
+ dp->curinodes = (SMB_BIG_UINT)D.dqb_curinodes;
+ dp->curblocks = (SMB_BIG_UINT)D.dqb_curblocks;
+
+ return ret;
+}
+
+static int get_smb_linux_v2_quota(char *path, uid_t euser_id, gid_t egrp_id, LINUX_SMB_DISK_QUOTA *dp)
+{
+ struct v2_kern_dqblk D;
+ int ret;
+
+ ZERO_STRUCT(D);
+
+ ret = quotactl(QCMD(Q_V2_GETQUOTA,USRQUOTA), path, euser_id, (caddr_t)&D);
+
+ if (ret && errno != EDQUOT)
+ ret = quotactl(QCMD(Q_V2_GETQUOTA,GRPQUOTA), path, egrp_id, (caddr_t)&D);
+
+ if (ret && errno != EDQUOT)
+ return ret;
+
+ dp->bsize = (SMB_BIG_UINT)QUOTABLOCK_SIZE;
+ dp->softlimit = (SMB_BIG_UINT)D.dqb_bsoftlimit;
+ dp->hardlimit = (SMB_BIG_UINT)D.dqb_bhardlimit;
+ dp->ihardlimit = (SMB_BIG_UINT)D.dqb_ihardlimit;
+ dp->isoftlimit = (SMB_BIG_UINT)D.dqb_isoftlimit;
+ dp->curinodes = (SMB_BIG_UINT)D.dqb_curinodes;
+ dp->curblocks = ((SMB_BIG_UINT)D.dqb_curspace) / dp->bsize;
+
+ return ret;
+}
+
+/****************************************************************************
+ Brand-new generic quota interface.
+****************************************************************************/
+
+static int get_smb_linux_gen_quota(char *path, uid_t euser_id, gid_t egrp_id, LINUX_SMB_DISK_QUOTA *dp)
+{
+ struct if_dqblk D;
+ int ret;
+
+ ZERO_STRUCT(D);
+
+ ret = quotactl(QCMD(Q_GETQUOTA,USRQUOTA), path, euser_id, (caddr_t)&D);
+
+ if (ret && errno != EDQUOT)
+ ret = quotactl(QCMD(Q_GETQUOTA,GRPQUOTA), path, egrp_id, (caddr_t)&D);
+
+ if (ret && errno != EDQUOT)
+ return ret;
+
+ dp->bsize = (SMB_BIG_UINT)QUOTABLOCK_SIZE;
+ dp->softlimit = (SMB_BIG_UINT)D.dqb_bsoftlimit;
+ dp->hardlimit = (SMB_BIG_UINT)D.dqb_bhardlimit;
+ dp->ihardlimit = (SMB_BIG_UINT)D.dqb_ihardlimit;
+ dp->isoftlimit = (SMB_BIG_UINT)D.dqb_isoftlimit;
+ dp->curinodes = (SMB_BIG_UINT)D.dqb_curinodes;
+ dp->curblocks = ((SMB_BIG_UINT)D.dqb_curspace) / dp->bsize;
+
+ return ret;
+}
+
+/****************************************************************************
+ Try to get the disk space from disk quotas (LINUX version).
+****************************************************************************/
+
+BOOL disk_quotas(const char *path, SMB_BIG_UINT *bsize, SMB_BIG_UINT *dfree, SMB_BIG_UINT *dsize)
+{
+ int r;
+ SMB_STRUCT_STAT S;
+ FILE *fp;
+ LINUX_SMB_DISK_QUOTA D;
+ struct mntent *mnt;
+ SMB_DEV_T devno;
+ int found;
+ uid_t euser_id;
+ gid_t egrp_id;
+
+ euser_id = geteuid();
+ egrp_id = getegid();
+
+ /* find the block device file */
+
+ if ( sys_stat(path, &S) == -1 )
+ return(False) ;
+
+ devno = S.st_dev ;
+
+ fp = setmntent(MOUNTED,"r");
+ found = False ;
+
+ while ((mnt = getmntent(fp))) {
+ if ( sys_stat(mnt->mnt_dir,&S) == -1 )
+ continue ;
+
+ if (S.st_dev == devno) {
+ found = True ;
+ break;
+ }
+ }
+
+ endmntent(fp) ;
+
+ if (!found)
+ return(False);
+
+ save_re_uid();
+ set_effective_uid(0);
+
+ if (strcmp(mnt->mnt_type, "xfs")==0) {
+ r=get_smb_linux_xfs_quota(mnt->mnt_fsname, euser_id, egrp_id, &D);
+ } else {
+ r=get_smb_linux_gen_quota(mnt->mnt_fsname, euser_id, egrp_id, &D);
+ if (r == -1 && errno != EDQUOT) {
+ r=get_smb_linux_v2_quota(mnt->mnt_fsname, euser_id, egrp_id, &D);
+ if (r == -1 && errno != EDQUOT)
+ r=get_smb_linux_v1_quota(mnt->mnt_fsname, euser_id, egrp_id, &D);
+ }
+ }
+
+ restore_re_uid();
+
+ /* Use softlimit to determine disk space, except when it has been exceeded */
+ *bsize = D.bsize;
+ if (r == -1) {
+ if (errno == EDQUOT) {
+ *dfree =0;
+ *dsize =D.curblocks;
+ return (True);
+ } else {
+ return(False);
+ }
+ }
+
+ /* Use softlimit to determine disk space, except when it has been exceeded */
+ if (
+ (D.softlimit && D.curblocks >= D.softlimit) ||
+ (D.hardlimit && D.curblocks >= D.hardlimit) ||
+ (D.isoftlimit && D.curinodes >= D.isoftlimit) ||
+ (D.ihardlimit && D.curinodes>=D.ihardlimit)
+ ) {
+ *dfree = 0;
+ *dsize = D.curblocks;
+ } else if (D.softlimit==0 && D.hardlimit==0) {
+ return(False);
+ } else {
+ if (D.softlimit == 0)
+ D.softlimit = D.hardlimit;
+ *dfree = D.softlimit - D.curblocks;
+ *dsize = D.softlimit;
+ }
+
+ return (True);
+}
+
+#elif defined(CRAY)
+
+#include <sys/quota.h>
+#include <mntent.h>
+
+/****************************************************************************
+try to get the disk space from disk quotas (CRAY VERSION)
+****************************************************************************/
+
+BOOL disk_quotas(const char *path, SMB_BIG_UINT *bsize, SMB_BIG_UINT *dfree, SMB_BIG_UINT *dsize)
+{
+ struct mntent *mnt;
+ FILE *fd;
+ SMB_STRUCT_STAT sbuf;
+ SMB_DEV_T devno ;
+ static SMB_DEV_T devno_cached = 0 ;
+ static pstring name;
+ struct q_request request ;
+ struct qf_header header ;
+ static int quota_default = 0 ;
+ int found ;
+
+ if ( sys_stat(path,&sbuf) == -1 )
+ return(False) ;
+
+ devno = sbuf.st_dev ;
+
+ if ( devno != devno_cached ) {
+
+ devno_cached = devno ;
+
+ if ((fd = setmntent(KMTAB)) == NULL)
+ return(False) ;
+
+ found = False ;
+
+ while ((mnt = getmntent(fd)) != NULL) {
+
+ if ( sys_stat(mnt->mnt_dir,&sbuf) == -1 )
+ continue ;
+
+ if (sbuf.st_dev == devno) {
+
+ found = True ;
+ break ;
+
+ }
+
+ }
+
+ pstrcpy(name,mnt->mnt_dir) ;
+ endmntent(fd) ;
+
+ if ( ! found )
+ return(False) ;
+ }
+
+ request.qf_magic = QF_MAGIC ;
+ request.qf_entry.id = geteuid() ;
+
+ if (quotactl(name, Q_GETQUOTA, &request) == -1)
+ return(False) ;
+
+ if ( ! request.user )
+ return(False) ;
+
+ if ( request.qf_entry.user_q.f_quota == QFV_DEFAULT ) {
+
+ if ( ! quota_default ) {
+
+ if ( quotactl(name, Q_GETHEADER, &header) == -1 )
+ return(False) ;
+ else
+ quota_default = header.user_h.def_fq ;
+ }
+
+ *dfree = quota_default ;
+
+ }else if ( request.qf_entry.user_q.f_quota == QFV_PREVENT ) {
+
+ *dfree = 0 ;
+
+ }else{
+
+ *dfree = request.qf_entry.user_q.f_quota ;
+
+ }
+
+ *dsize = request.qf_entry.user_q.f_use ;
+
+ if ( *dfree < *dsize )
+ *dfree = 0 ;
+ else
+ *dfree -= *dsize ;
+
+ *bsize = 4096 ; /* Cray blocksize */
+
+ return(True) ;
+
+}
+
+
+#elif defined(SUNOS5) || defined(SUNOS4)
+
+#include <fcntl.h>
+#include <sys/param.h>
+#if defined(SUNOS5)
+#include <sys/fs/ufs_quota.h>
+#include <sys/mnttab.h>
+#include <sys/mntent.h>
+#else /* defined(SUNOS4) */
+#include <ufs/quota.h>
+#include <mntent.h>
+#endif
+
+#if defined(SUNOS5)
+
+/****************************************************************************
+ Allows querying of remote hosts for quotas on NFS mounted shares.
+ Supports normal NFS and AMD mounts.
+ Alan Romeril <a.romeril@ic.ac.uk> July 2K.
+****************************************************************************/
+
+#include <rpc/rpc.h>
+#include <rpc/types.h>
+#include <rpcsvc/rquota.h>
+#include <rpc/nettype.h>
+#include <rpc/xdr.h>
+
+static int quotastat;
+
+static int xdr_getquota_args(XDR *xdrsp, struct getquota_args *args)
+{
+ if (!xdr_string(xdrsp, &args->gqa_pathp, RQ_PATHLEN ))
+ return(0);
+ if (!xdr_int(xdrsp, &args->gqa_uid))
+ return(0);
+ return (1);
+}
+
+static int xdr_getquota_rslt(XDR *xdrsp, struct getquota_rslt *gqr)
+{
+ if (!xdr_int(xdrsp, &quotastat)) {
+ DEBUG(6,("nfs_quotas: Status bad or zero\n"));
+ return 0;
+ }
+ if (!xdr_int(xdrsp, &gqr->getquota_rslt_u.gqr_rquota.rq_bsize)) {
+ DEBUG(6,("nfs_quotas: Block size bad or zero\n"));
+ return 0;
+ }
+ if (!xdr_bool(xdrsp, &gqr->getquota_rslt_u.gqr_rquota.rq_active)) {
+ DEBUG(6,("nfs_quotas: Active bad or zero\n"));
+ return 0;
+ }
+ if (!xdr_int(xdrsp, &gqr->getquota_rslt_u.gqr_rquota.rq_bhardlimit)) {
+ DEBUG(6,("nfs_quotas: Hardlimit bad or zero\n"));
+ return 0;
+ }
+ if (!xdr_int(xdrsp, &gqr->getquota_rslt_u.gqr_rquota.rq_bsoftlimit)) {
+ DEBUG(6,("nfs_quotas: Softlimit bad or zero\n"));
+ return 0;
+ }
+ if (!xdr_int(xdrsp, &gqr->getquota_rslt_u.gqr_rquota.rq_curblocks)) {
+ DEBUG(6,("nfs_quotas: Currentblocks bad or zero\n"));
+ return 0;
+ }
+ return (1);
+}
+
+/* Restricted to SUNOS5 for the moment, I haven`t access to others to test. */
+static BOOL nfs_quotas(char *nfspath, uid_t euser_id, SMB_BIG_UINT *bsize, SMB_BIG_UINT *dfree, SMB_BIG_UINT *dsize)
+{
+ uid_t uid = euser_id;
+ struct dqblk D;
+ char *mnttype = nfspath;
+ CLIENT *clnt;
+ struct getquota_rslt gqr;
+ struct getquota_args args;
+ char *cutstr, *pathname, *host, *testpath;
+ int len;
+ static struct timeval timeout = {2,0};
+ enum clnt_stat clnt_stat;
+ BOOL ret = True;
+
+ *bsize = *dfree = *dsize = (SMB_BIG_UINT)0;
+
+ len=strcspn(mnttype, ":");
+ pathname=strstr(mnttype, ":");
+ cutstr = (char *) malloc(len+1);
+ if (!cutstr)
+ return False;
+
+ memset(cutstr, '\0', len+1);
+ host = strncat(cutstr,mnttype, sizeof(char) * len );
+ DEBUG(5,("nfs_quotas: looking for mount on \"%s\"\n", cutstr));
+ DEBUG(5,("nfs_quotas: of path \"%s\"\n", mnttype));
+ testpath=strchr_m(mnttype, ':');
+ args.gqa_pathp = testpath+1;
+ args.gqa_uid = uid;
+
+ DEBUG(5,("nfs_quotas: Asking for host \"%s\" rpcprog \"%i\" rpcvers \"%i\" network \"%s\"\n", host, RQUOTAPROG, RQUOTAVERS, "udp"));
+
+ if ((clnt = clnt_create(host, RQUOTAPROG, RQUOTAVERS, "udp")) == NULL) {
+ ret = False;
+ goto out;
+ }
+
+ clnt->cl_auth = authunix_create_default();
+ DEBUG(9,("nfs_quotas: auth_success\n"));
+
+ clnt_stat=clnt_call(clnt, RQUOTAPROC_GETQUOTA, xdr_getquota_args, (caddr_t)&args, xdr_getquota_rslt, (caddr_t)&gqr, timeout);
+
+ if (clnt_stat != RPC_SUCCESS) {
+ DEBUG(9,("nfs_quotas: clnt_call fail\n"));
+ ret = False;
+ goto out;
+ }
+
+ /*
+ * quotastat returns 0 if the rpc call fails, 1 if quotas exist, 2 if there is
+ * no quota set, and 3 if no permission to get the quota. If 0 or 3 return
+ * something sensible.
+ */
+
+ switch ( quotastat ) {
+ case 0:
+ DEBUG(9,("nfs_quotas: Remote Quotas Failed! Error \"%i\" \n", quotastat ));
+ ret = False;
+ goto out;
+
+ case 1:
+ DEBUG(9,("nfs_quotas: Good quota data\n"));
+ D.dqb_bsoftlimit = gqr.getquota_rslt_u.gqr_rquota.rq_bsoftlimit;
+ D.dqb_bhardlimit = gqr.getquota_rslt_u.gqr_rquota.rq_bhardlimit;
+ D.dqb_curblocks = gqr.getquota_rslt_u.gqr_rquota.rq_curblocks;
+ break;
+
+ case 2:
+ case 3:
+ D.dqb_bsoftlimit = 1;
+ D.dqb_curblocks = 1;
+ DEBUG(9,("nfs_quotas: Remote Quotas returned \"%i\" \n", quotastat ));
+ break;
+
+ default:
+ DEBUG(9,("nfs_quotas: Remote Quotas Questionable! Error \"%i\" \n", quotastat ));
+ break;
+ }
+
+ DEBUG(10,("nfs_quotas: Let`s look at D a bit closer... status \"%i\" bsize \"%i\" active? \"%i\" bhard \"%i\" bsoft \"%i\" curb \"%i\" \n",
+ quotastat,
+ gqr.getquota_rslt_u.gqr_rquota.rq_bsize,
+ gqr.getquota_rslt_u.gqr_rquota.rq_active,
+ gqr.getquota_rslt_u.gqr_rquota.rq_bhardlimit,
+ gqr.getquota_rslt_u.gqr_rquota.rq_bsoftlimit,
+ gqr.getquota_rslt_u.gqr_rquota.rq_curblocks));
+
+ *bsize = gqr.getquota_rslt_u.gqr_rquota.rq_bsize;
+ *dsize = D.dqb_bsoftlimit;
+
+ if (D.dqb_curblocks == D.dqb_curblocks == 1)
+ *bsize = 512;
+
+ if (D.dqb_curblocks > D.dqb_bsoftlimit) {
+ *dfree = 0;
+ *dsize = D.dqb_curblocks;
+ } else
+ *dfree = D.dqb_bsoftlimit - D.dqb_curblocks;
+
+ out:
+
+ if (clnt) {
+ if (clnt->cl_auth)
+ auth_destroy(clnt->cl_auth);
+ clnt_destroy(clnt);
+ }
+
+ DEBUG(5,("nfs_quotas: For path \"%s\" returning bsize %.0f, dfree %.0f, dsize %.0f\n",args.gqa_pathp,(double)*bsize,(double)*dfree,(double)*dsize));
+
+ SAFE_FREE(cutstr);
+ DEBUG(10,("nfs_quotas: End of nfs_quotas\n" ));
+ return ret;
+}
+#endif
+
+/****************************************************************************
+try to get the disk space from disk quotas (SunOS & Solaris2 version)
+Quota code by Peter Urbanec (amiga@cse.unsw.edu.au).
+****************************************************************************/
+
+BOOL disk_quotas(const char *path, SMB_BIG_UINT *bsize, SMB_BIG_UINT *dfree, SMB_BIG_UINT *dsize)
+{
+ uid_t euser_id;
+ int ret;
+ struct dqblk D;
+#if defined(SUNOS5)
+ struct quotctl command;
+ int file;
+ static struct mnttab mnt;
+ static pstring name;
+ pstring devopt;
+#else /* SunOS4 */
+ struct mntent *mnt;
+ static pstring name;
+#endif
+ FILE *fd;
+ SMB_STRUCT_STAT sbuf;
+ SMB_DEV_T devno ;
+ static SMB_DEV_T devno_cached = 0 ;
+ static int found ;
+
+ euser_id = geteuid();
+
+ if ( sys_stat(path,&sbuf) == -1 )
+ return(False) ;
+
+ devno = sbuf.st_dev ;
+ DEBUG(5,("disk_quotas: looking for path \"%s\" devno=%x\n", path,(unsigned int)devno));
+ if ( devno != devno_cached ) {
+ devno_cached = devno ;
+#if defined(SUNOS5)
+ if ((fd = sys_fopen(MNTTAB, "r")) == NULL)
+ return(False) ;
+
+ found = False ;
+ slprintf(devopt, sizeof(devopt) - 1, "dev=%x", (unsigned int)devno);
+ while (getmntent(fd, &mnt) == 0) {
+ if( !hasmntopt(&mnt, devopt) )
+ continue;
+
+ DEBUG(5,("disk_quotas: testing \"%s\" %s\n", mnt.mnt_mountp,devopt));
+
+ /* quotas are only on vxfs, UFS or NFS */
+ if ( strcmp( mnt.mnt_fstype, MNTTYPE_UFS ) == 0 ||
+ strcmp( mnt.mnt_fstype, "nfs" ) == 0 ||
+ strcmp( mnt.mnt_fstype, "vxfs" ) == 0 ) {
+ found = True ;
+ break;
+ }
+ }
+
+ pstrcpy(name,mnt.mnt_mountp) ;
+ pstrcat(name,"/quotas") ;
+ fclose(fd) ;
+#else /* SunOS4 */
+ if ((fd = setmntent(MOUNTED, "r")) == NULL)
+ return(False) ;
+
+ found = False ;
+ while ((mnt = getmntent(fd)) != NULL) {
+ if ( sys_stat(mnt->mnt_dir,&sbuf) == -1 )
+ continue ;
+ DEBUG(5,("disk_quotas: testing \"%s\" devno=%x\n", mnt->mnt_dir,(unsigned int)sbuf.st_dev));
+ if (sbuf.st_dev == devno) {
+ found = True ;
+ break;
+ }
+ }
+
+ pstrcpy(name,mnt->mnt_fsname) ;
+ endmntent(fd) ;
+#endif
+ }
+
+ if ( ! found )
+ return(False) ;
+
+ save_re_uid();
+ set_effective_uid(0);
+
+#if defined(SUNOS5)
+ if ( strcmp( mnt.mnt_fstype, "nfs" ) == 0) {
+ BOOL retval;
+ DEBUG(5,("disk_quotas: looking for mountpath (NFS) \"%s\"\n", mnt.mnt_special));
+ retval = nfs_quotas(mnt.mnt_special, euser_id, bsize, dfree, dsize);
+ restore_re_uid();
+ return retval;
+ }
+
+ DEBUG(5,("disk_quotas: looking for quotas file \"%s\"\n", name));
+ if((file=sys_open(name, O_RDONLY,0))<0) {
+ restore_re_uid();
+ return(False);
+ }
+ command.op = Q_GETQUOTA;
+ command.uid = euser_id;
+ command.addr = (caddr_t) &D;
+ ret = ioctl(file, Q_QUOTACTL, &command);
+ close(file);
+#else
+ DEBUG(5,("disk_quotas: trying quotactl on device \"%s\"\n", name));
+ ret = quotactl(Q_GETQUOTA, name, euser_id, &D);
+#endif
+
+ restore_re_uid();
+
+ if (ret < 0) {
+ DEBUG(5,("disk_quotas ioctl (Solaris) failed. Error = %s\n", strerror(errno) ));
+
+#if defined(SUNOS5) && defined(VXFS_QUOTA)
+ /* If normal quotactl() fails, try vxfs private calls */
+ set_effective_uid(euser_id);
+ DEBUG(5,("disk_quotas: mount type \"%s\"\n", mnt.mnt_fstype));
+ if ( 0 == strcmp ( mnt.mnt_fstype, "vxfs" )) {
+ BOOL retval;
+ retval = disk_quotas_vxfs(name, path, bsize, dfree, dsize);
+ return(retval);
+ }
+#else
+ return(False);
+#endif
+ }
+
+ /* If softlimit is zero, set it equal to hardlimit.
+ */
+
+ if (D.dqb_bsoftlimit==0)
+ D.dqb_bsoftlimit = D.dqb_bhardlimit;
+
+ /* Use softlimit to determine disk space. A user exceeding the quota is told
+ * that there's no space left. Writes might actually work for a bit if the
+ * hardlimit is set higher than softlimit. Effectively the disk becomes
+ * made of rubber latex and begins to expand to accommodate the user :-)
+ */
+
+ if (D.dqb_bsoftlimit==0)
+ return(False);
+ *bsize = DEV_BSIZE;
+ *dsize = D.dqb_bsoftlimit;
+
+ if (D.dqb_curblocks > D.dqb_bsoftlimit) {
+ *dfree = 0;
+ *dsize = D.dqb_curblocks;
+ } else
+ *dfree = D.dqb_bsoftlimit - D.dqb_curblocks;
+
+ DEBUG(5,("disk_quotas for path \"%s\" returning bsize %.0f, dfree %.0f, dsize %.0f\n",
+ path,(double)*bsize,(double)*dfree,(double)*dsize));
+
+ return(True);
+}
+
+
+#elif defined(OSF1)
+#include <ufs/quota.h>
+
+/****************************************************************************
+try to get the disk space from disk quotas - OSF1 version
+****************************************************************************/
+
+BOOL disk_quotas(const char *path, SMB_BIG_UINT *bsize, SMB_BIG_UINT *dfree, SMB_BIG_UINT *dsize)
+{
+ int r, save_errno;
+ struct dqblk D;
+ SMB_STRUCT_STAT S;
+ uid_t euser_id;
+
+ /*
+ * This code presumes that OSF1 will only
+ * give out quota info when the real uid
+ * matches the effective uid. JRA.
+ */
+ euser_id = geteuid();
+ save_re_uid();
+ if (set_re_uid() != 0) return False;
+
+ r= quotactl(path,QCMD(Q_GETQUOTA, USRQUOTA),euser_id,(char *) &D);
+ if (r) {
+ save_errno = errno;
+ }
+
+ restore_re_uid();
+
+ *bsize = DEV_BSIZE;
+
+ if (r)
+ {
+ if (save_errno == EDQUOT) /* disk quota exceeded */
+ {
+ *dfree = 0;
+ *dsize = D.dqb_curblocks;
+ return (True);
+ }
+ else
+ return (False);
+ }
+
+ /* If softlimit is zero, set it equal to hardlimit.
+ */
+
+ if (D.dqb_bsoftlimit==0)
+ D.dqb_bsoftlimit = D.dqb_bhardlimit;
+
+ /* Use softlimit to determine disk space, except when it has been exceeded */
+
+ if (D.dqb_bsoftlimit==0)
+ return(False);
+
+ if ((D.dqb_curblocks>D.dqb_bsoftlimit)) {
+ *dfree = 0;
+ *dsize = D.dqb_curblocks;
+ } else {
+ *dfree = D.dqb_bsoftlimit - D.dqb_curblocks;
+ *dsize = D.dqb_bsoftlimit;
+ }
+ return (True);
+}
+
+#elif defined (IRIX6)
+/****************************************************************************
+try to get the disk space from disk quotas (IRIX 6.2 version)
+****************************************************************************/
+
+#include <sys/quota.h>
+#include <mntent.h>
+
+BOOL disk_quotas(const char *path, SMB_BIG_UINT *bsize, SMB_BIG_UINT *dfree, SMB_BIG_UINT *dsize)
+{
+ uid_t euser_id;
+ int r;
+ struct dqblk D;
+ struct fs_disk_quota F;
+ SMB_STRUCT_STAT S;
+ FILE *fp;
+ struct mntent *mnt;
+ SMB_DEV_T devno;
+ int found;
+
+ /* find the block device file */
+
+ if ( sys_stat(path, &S) == -1 ) {
+ return(False) ;
+ }
+
+ devno = S.st_dev ;
+
+ fp = setmntent(MOUNTED,"r");
+ found = False ;
+
+ while ((mnt = getmntent(fp))) {
+ if ( sys_stat(mnt->mnt_dir,&S) == -1 )
+ continue ;
+ if (S.st_dev == devno) {
+ found = True ;
+ break ;
+ }
+ }
+ endmntent(fp) ;
+
+ if (!found) {
+ return(False);
+ }
+
+ euser_id=geteuid();
+ save_re_uid();
+ set_effective_uid(0);
+
+ /* Use softlimit to determine disk space, except when it has been exceeded */
+
+ *bsize = 512;
+
+ if ( 0 == strcmp ( mnt->mnt_type, "efs" ))
+ {
+ r=quotactl (Q_GETQUOTA, mnt->mnt_fsname, euser_id, (caddr_t) &D);
+
+ restore_re_uid();
+
+ if (r==-1)
+ return(False);
+
+ /* Use softlimit to determine disk space, except when it has been exceeded */
+ if (
+ (D.dqb_bsoftlimit && D.dqb_curblocks>=D.dqb_bsoftlimit) ||
+ (D.dqb_bhardlimit && D.dqb_curblocks>=D.dqb_bhardlimit) ||
+ (D.dqb_fsoftlimit && D.dqb_curfiles>=D.dqb_fsoftlimit) ||
+ (D.dqb_fhardlimit && D.dqb_curfiles>=D.dqb_fhardlimit)
+ )
+ {
+ *dfree = 0;
+ *dsize = D.dqb_curblocks;
+ }
+ else if (D.dqb_bsoftlimit==0 && D.dqb_bhardlimit==0)
+ {
+ return(False);
+ }
+ else
+ {
+ *dfree = D.dqb_bsoftlimit - D.dqb_curblocks;
+ *dsize = D.dqb_bsoftlimit;
+ }
+
+ }
+ else if ( 0 == strcmp ( mnt->mnt_type, "xfs" ))
+ {
+ r=quotactl (Q_XGETQUOTA, mnt->mnt_fsname, euser_id, (caddr_t) &F);
+
+ restore_re_uid();
+
+ if (r==-1)
+ return(False);
+
+ /* Use softlimit to determine disk space, except when it has been exceeded */
+ if (
+ (F.d_blk_softlimit && F.d_bcount>=F.d_blk_softlimit) ||
+ (F.d_blk_hardlimit && F.d_bcount>=F.d_blk_hardlimit) ||
+ (F.d_ino_softlimit && F.d_icount>=F.d_ino_softlimit) ||
+ (F.d_ino_hardlimit && F.d_icount>=F.d_ino_hardlimit)
+ )
+ {
+ *dfree = 0;
+ *dsize = F.d_bcount;
+ }
+ else if (F.d_blk_softlimit==0 && F.d_blk_hardlimit==0)
+ {
+ return(False);
+ }
+ else
+ {
+ *dfree = (F.d_blk_softlimit - F.d_bcount);
+ *dsize = F.d_blk_softlimit;
+ }
+
+ }
+ else
+ {
+ restore_re_uid();
+ return(False);
+ }
+
+ return (True);
+
+}
+
+#else
+
+#if defined(__FreeBSD__) || defined(__OpenBSD__)
+#include <ufs/ufs/quota.h>
+#include <machine/param.h>
+#elif AIX
+/* AIX quota patch from Ole Holm Nielsen <ohnielse@fysik.dtu.dk> */
+#include <jfs/quota.h>
+/* AIX 4.X: Rename members of the dqblk structure (ohnielse@fysik.dtu.dk) */
+#define dqb_curfiles dqb_curinodes
+#define dqb_fhardlimit dqb_ihardlimit
+#define dqb_fsoftlimit dqb_isoftlimit
+#else /* !__FreeBSD__ && !AIX && !__OpenBSD__ */
+#include <sys/quota.h>
+#include <devnm.h>
+#endif
+
+/****************************************************************************
+try to get the disk space from disk quotas - default version
+****************************************************************************/
+
+BOOL disk_quotas(const char *path, SMB_BIG_UINT *bsize, SMB_BIG_UINT *dfree, SMB_BIG_UINT *dsize)
+{
+ int r;
+ struct dqblk D;
+ uid_t euser_id;
+#if !defined(__FreeBSD__) && !defined(AIX) && !defined(__OpenBSD__)
+ char dev_disk[256];
+ SMB_STRUCT_STAT S;
+
+ /* find the block device file */
+
+#ifdef HPUX
+ /* Need to set the cache flag to 1 for HPUX. Seems
+ * to have a significant performance boost when
+ * lstat calls on /dev access this function.
+ */
+ if ((sys_stat(path, &S)<0) || (devnm(S_IFBLK, S.st_dev, dev_disk, 256, 1)<0))
+#else
+ if ((sys_stat(path, &S)<0) || (devnm(S_IFBLK, S.st_dev, dev_disk, 256, 0)<0))
+ return (False);
+#endif /* ifdef HPUX */
+
+#endif /* !defined(__FreeBSD__) && !defined(AIX) && !defined(__OpenBSD__) */
+
+ euser_id = geteuid();
+
+#ifdef HPUX
+ /* for HPUX, real uid must be same as euid to execute quotactl for euid */
+ save_re_uid();
+ if (set_re_uid() != 0) return False;
+
+ r=quotactl(Q_GETQUOTA, dev_disk, euser_id, &D);
+
+ restore_re_uid();
+#else
+#if defined(__FreeBSD__) || defined(__OpenBSD__)
+ {
+ /* FreeBSD patches from Marty Moll <martym@arbor.edu> */
+ gid_t egrp_id;
+
+ save_re_uid();
+ set_effective_uid(0);
+
+ egrp_id = getegid();
+ r= quotactl(path,QCMD(Q_GETQUOTA,USRQUOTA),euser_id,(char *) &D);
+
+ /* As FreeBSD has group quotas, if getting the user
+ quota fails, try getting the group instead. */
+ if (r) {
+ r= quotactl(path,QCMD(Q_GETQUOTA,GRPQUOTA),egrp_id,(char *) &D);
+ }
+
+ restore_re_uid();
+ }
+#elif defined(AIX)
+ /* AIX has both USER and GROUP quotas:
+ Get the USER quota (ohnielse@fysik.dtu.dk) */
+ save_re_uid();
+ if (set_re_uid() != 0)
+ return False;
+ r= quotactl(path,QCMD(Q_GETQUOTA,USRQUOTA),euser_id,(char *) &D);
+ restore_re_uid();
+#else /* !__FreeBSD__ && !AIX && !__OpenBSD__ */
+ r=quotactl(Q_GETQUOTA, dev_disk, euser_id, &D);
+#endif /* !__FreeBSD__ && !AIX && !__OpenBSD__ */
+#endif /* HPUX */
+
+ /* Use softlimit to determine disk space, except when it has been exceeded */
+#if defined(__FreeBSD__) || defined(__OpenBSD__)
+ *bsize = DEV_BSIZE;
+#else /* !__FreeBSD__ && !__OpenBSD__ */
+ *bsize = 1024;
+#endif /*!__FreeBSD__ && !__OpenBSD__ */
+
+ if (r)
+ {
+ if (errno == EDQUOT)
+ {
+ *dfree =0;
+ *dsize =D.dqb_curblocks;
+ return (True);
+ }
+ else return(False);
+ }
+
+ /* If softlimit is zero, set it equal to hardlimit.
+ */
+
+ if (D.dqb_bsoftlimit==0)
+ D.dqb_bsoftlimit = D.dqb_bhardlimit;
+
+ if (D.dqb_bsoftlimit==0)
+ return(False);
+ /* Use softlimit to determine disk space, except when it has been exceeded */
+ if ((D.dqb_curblocks>D.dqb_bsoftlimit)
+#if !defined(__FreeBSD__) && !defined(__OpenBSD__)
+||((D.dqb_curfiles>D.dqb_fsoftlimit) && (D.dqb_fsoftlimit != 0))
+#endif
+ ) {
+ *dfree = 0;
+ *dsize = D.dqb_curblocks;
+ }
+ else {
+ *dfree = D.dqb_bsoftlimit - D.dqb_curblocks;
+ *dsize = D.dqb_bsoftlimit;
+ }
+ return (True);
+}
+
+#endif
+
+#if defined(VXFS_QUOTA)
+
+/****************************************************************************
+Try to get the disk space from Veritas disk quotas.
+ David Lee <T.D.Lee@durham.ac.uk> August 1999.
+
+Background assumptions:
+ Potentially under many Operating Systems. Initially Solaris 2.
+
+ My guess is that Veritas is largely, though not entirely,
+ independent of OS. So I have separated it out.
+
+ There may be some details. For example, OS-specific "include" files.
+
+ It is understood that HPUX 10 somehow gets Veritas quotas without
+ any special effort; if so, this routine need not be compiled in.
+ Dirk De Wachter <Dirk.DeWachter@rug.ac.be>
+
+Warning:
+ It is understood that Veritas do not publicly support this ioctl interface.
+ Rather their preference would be for the user (us) to call the native
+ OS and then for the OS itself to call through to the VxFS filesystem.
+ Presumably HPUX 10, see above, does this.
+
+Hints for porting:
+ Add your OS to "IFLIST" below.
+ Get it to compile successfully:
+ Almost certainly "include"s require attention: see SUNOS5.
+ In the main code above, arrange for it to be called: see SUNOS5.
+ Test!
+
+****************************************************************************/
+
+/* "IFLIST"
+ * This "if" is a list of ports:
+ * if defined(OS1) || defined(OS2) || ...
+ */
+#if defined(SUNOS5)
+
+#if defined(SUNOS5)
+#include <sys/fs/vx_solaris.h>
+#endif
+#include <sys/fs/vx_machdep.h>
+#include <sys/fs/vx_layout.h>
+#include <sys/fs/vx_quota.h>
+#include <sys/fs/vx_aioctl.h>
+#include <sys/fs/vx_ioctl.h>
+
+BOOL disk_quotas_vxfs(const pstring name, char *path, SMB_BIG_UINT *bsize, SMB_BIG_UINT *dfree, SMB_BIG_UINT *dsize)
+{
+ uid_t user_id, euser_id;
+ int ret;
+ struct vx_dqblk D;
+ struct vx_quotctl quotabuf;
+ struct vx_genioctl genbuf;
+ pstring qfname;
+ int file;
+
+ /*
+ * "name" may or may not include a trailing "/quotas".
+ * Arranging consistency of calling here in "quotas.c" may not be easy and
+ * it might be easier to examine and adjust it here.
+ * Fortunately, VxFS seems not to mind at present.
+ */
+ pstrcpy(qfname, name) ;
+ /* pstrcat(qfname, "/quotas") ; */ /* possibly examine and adjust "name" */
+
+ euser_id = geteuid();
+ set_effective_uid(0);
+
+ DEBUG(5,("disk_quotas: looking for VxFS quotas file \"%s\"\n", qfname));
+ if((file=sys_open(qfname, O_RDONLY,0))<0) {
+ set_effective_uid(euser_id);
+ return(False);
+ }
+ genbuf.ioc_cmd = VX_QUOTACTL;
+ genbuf.ioc_up = (void *) &quotabuf;
+
+ quotabuf.cmd = VX_GETQUOTA;
+ quotabuf.uid = euser_id;
+ quotabuf.addr = (caddr_t) &D;
+ ret = ioctl(file, VX_ADMIN_IOCTL, &genbuf);
+ close(file);
+
+ set_effective_uid(euser_id);
+
+ if (ret < 0) {
+ DEBUG(5,("disk_quotas ioctl (VxFS) failed. Error = %s\n", strerror(errno) ));
+ return(False);
+ }
+
+ /* If softlimit is zero, set it equal to hardlimit.
+ */
+
+ if (D.dqb_bsoftlimit==0)
+ D.dqb_bsoftlimit = D.dqb_bhardlimit;
+
+ /* Use softlimit to determine disk space. A user exceeding the quota is told
+ * that there's no space left. Writes might actually work for a bit if the
+ * hardlimit is set higher than softlimit. Effectively the disk becomes
+ * made of rubber latex and begins to expand to accommodate the user :-)
+ */
+ DEBUG(5,("disk_quotas for path \"%s\" block c/s/h %ld/%ld/%ld; file c/s/h %ld/%ld/%ld\n",
+ path, D.dqb_curblocks, D.dqb_bsoftlimit, D.dqb_bhardlimit,
+ D.dqb_curfiles, D.dqb_fsoftlimit, D.dqb_fhardlimit));
+
+ if (D.dqb_bsoftlimit==0)
+ return(False);
+ *bsize = DEV_BSIZE;
+ *dsize = D.dqb_bsoftlimit;
+
+ if (D.dqb_curblocks > D.dqb_bsoftlimit) {
+ *dfree = 0;
+ *dsize = D.dqb_curblocks;
+ } else
+ *dfree = D.dqb_bsoftlimit - D.dqb_curblocks;
+
+ DEBUG(5,("disk_quotas for path \"%s\" returning bsize %.0f, dfree %.0f, dsize %.0f\n",
+ path,(double)*bsize,(double)*dfree,(double)*dsize));
+
+ return(True);
+}
+
+#endif /* SUNOS5 || ... */
+
+#endif /* VXFS_QUOTA */
+
+#else /* WITH_QUOTAS */
+
+BOOL disk_quotas(const char *path,SMB_BIG_UINT *bsize,SMB_BIG_UINT *dfree,SMB_BIG_UINT *dsize)
+{
+ (*bsize) = 512; /* This value should be ignored */
+
+ /* And just to be sure we set some values that hopefully */
+ /* will be larger that any possible real-world value */
+ (*dfree) = (SMB_BIG_UINT)-1;
+ (*dsize) = (SMB_BIG_UINT)-1;
+
+ /* As we have select not to use quotas, allways fail */
+ return False;
+}
+#endif /* WITH_QUOTAS */
+
+#else /* HAVE_SYS_QUOTAS */
+/* wrapper to the new sys_quota interface
+ this file should be removed later
+ */
+BOOL disk_quotas(const char *path,SMB_BIG_UINT *bsize,SMB_BIG_UINT *dfree,SMB_BIG_UINT *dsize)
+{
+ int r;
+ SMB_DISK_QUOTA D;
+ unid_t id;
+
+ id.uid = geteuid();
+
+ ZERO_STRUCT(D);
+ r=sys_get_quota(path, SMB_USER_QUOTA_TYPE, id, &D);
+
+ /* Use softlimit to determine disk space, except when it has been exceeded */
+ *bsize = D.bsize;
+ if (r == -1) {
+ if (errno == EDQUOT) {
+ *dfree =0;
+ *dsize =D.curblocks;
+ return (True);
+ } else {
+ goto try_group_quota;
+ }
+ }
+
+ /* Use softlimit to determine disk space, except when it has been exceeded */
+ if (
+ (D.softlimit && D.curblocks >= D.softlimit) ||
+ (D.hardlimit && D.curblocks >= D.hardlimit) ||
+ (D.isoftlimit && D.curinodes >= D.isoftlimit) ||
+ (D.ihardlimit && D.curinodes>=D.ihardlimit)
+ ) {
+ *dfree = 0;
+ *dsize = D.curblocks;
+ } else if (D.softlimit==0 && D.hardlimit==0) {
+ goto try_group_quota;
+ } else {
+ if (D.softlimit == 0)
+ D.softlimit = D.hardlimit;
+ *dfree = D.softlimit - D.curblocks;
+ *dsize = D.softlimit;
+ }
+
+ return True;
+
+try_group_quota:
+ id.gid = getegid();
+
+ ZERO_STRUCT(D);
+ r=sys_get_quota(path, SMB_GROUP_QUOTA_TYPE, id, &D);
+
+ /* Use softlimit to determine disk space, except when it has been exceeded */
+ *bsize = D.bsize;
+ if (r == -1) {
+ if (errno == EDQUOT) {
+ *dfree =0;
+ *dsize =D.curblocks;
+ return (True);
+ } else {
+ return False;
+ }
+ }
+
+ /* Use softlimit to determine disk space, except when it has been exceeded */
+ if (
+ (D.softlimit && D.curblocks >= D.softlimit) ||
+ (D.hardlimit && D.curblocks >= D.hardlimit) ||
+ (D.isoftlimit && D.curinodes >= D.isoftlimit) ||
+ (D.ihardlimit && D.curinodes>=D.ihardlimit)
+ ) {
+ *dfree = 0;
+ *dsize = D.curblocks;
+ } else if (D.softlimit==0 && D.hardlimit==0) {
+ return False;
+ } else {
+ if (D.softlimit == 0)
+ D.softlimit = D.hardlimit;
+ *dfree = D.softlimit - D.curblocks;
+ *dsize = D.softlimit;
+ }
+
+ return (True);
+}
+#endif /* HAVE_SYS_QUOTAS */
diff --git a/source/smbd/reply.c b/source/smbd/reply.c
new file mode 100644
index 00000000000..ac239c7e042
--- /dev/null
+++ b/source/smbd/reply.c
@@ -0,0 +1,4974 @@
+/*
+ Unix SMB/CIFS implementation.
+ Main SMB reply routines
+ Copyright (C) Andrew Tridgell 1992-1998
+ Copyright (C) Andrew Bartlett 2001
+ Copyright (C) Jeremy Allison 1992-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 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.
+*/
+/*
+ This file handles most of the reply_ calls that the server
+ makes to handle specific protocols
+*/
+
+#include "includes.h"
+
+/* look in server.c for some explanation of these variables */
+extern int Protocol;
+extern int max_send;
+extern int max_recv;
+extern char magic_char;
+extern BOOL case_sensitive;
+extern BOOL case_preserve;
+extern BOOL short_case_preserve;
+extern int global_oplock_break;
+unsigned int smb_echo_count = 0;
+
+extern BOOL global_encrypted_passwords_negotiated;
+
+/****************************************************************************
+ Ensure we check the path in *exactly* the same way as W2K.
+ We're assuming here that '/' is not the second byte in any multibyte char
+ set (a safe assumption). '\\' *may* be the second byte in a multibyte char
+ set.
+****************************************************************************/
+
+NTSTATUS check_path_syntax(pstring destname, const pstring srcname)
+{
+ char *d = destname;
+ const char *s = srcname;
+ NTSTATUS ret = NT_STATUS_OK;
+
+ while (*s) {
+ if (IS_DIRECTORY_SEP(*s)) {
+ /*
+ * Safe to assume is not the second part of a mb char as this is handled below.
+ */
+ /* Eat multiple '/' or '\\' */
+ while (IS_DIRECTORY_SEP(*s)) {
+ s++;
+ }
+ if ((s[0] == '.') && (s[1] == '\0')) {
+ ret = NT_STATUS_OBJECT_NAME_INVALID;
+ break;
+ }
+ if ((d != destname) && (*s != '\0')) {
+ /* We only care about non-leading or trailing '/' or '\\' */
+ *d++ = '/';
+ }
+ } else if ((s[0] == '.') && (s[1] == '.') && (IS_DIRECTORY_SEP(s[2]) || s[2] == '\0')) {
+ /* Uh oh - "../" or "..\\" or "..\0" ! */
+
+ /*
+ * No mb char starts with '.' so we're safe checking the directory separator here.
+ */
+
+ /* If we just added a '/', delete it. */
+
+ if ((d > destname) && (*(d-1) == '/')) {
+ *(d-1) = '\0';
+ if (d == (destname + 1)) {
+ d--;
+ } else {
+ d -= 2;
+ }
+ }
+ /* Are we at the start ? Can't go back further if so. */
+ if (d == destname) {
+ ret = NT_STATUS_OBJECT_PATH_SYNTAX_BAD;
+ break;
+ }
+ /* Go back one level... */
+ /* We know this is safe as '/' cannot be part of a mb sequence. */
+ /* NOTE - if this assumption is invalid we are not in good shape... */
+ while (d > destname) {
+ if (*d == '/')
+ break;
+ d--;
+ }
+ s += 3;
+ } else if ((s[0] == '.') && (IS_DIRECTORY_SEP(s[1]) || (s[1] == '\0'))) {
+
+ /*
+ * No mb char starts with '.' so we're safe checking the directory separator here.
+ */
+
+ /* "./" or ".\\" fails with a different error depending on where it is... */
+
+ if (s == srcname) {
+ ret = NT_STATUS_OBJECT_NAME_INVALID;
+ break;
+ } else {
+ if (s[1] != '\0' && s[2] == '\0') {
+ ret = NT_STATUS_INVALID_PARAMETER;
+ break;
+ }
+ ret = NT_STATUS_OBJECT_PATH_NOT_FOUND;
+ break;
+ }
+ s++;
+ } else {
+ if (!(*s & 0x80)) {
+ *d++ = *s++;
+ } else {
+ switch(next_mb_char_size(s)) {
+ case 4:
+ *d++ = *s++;
+ case 3:
+ *d++ = *s++;
+ case 2:
+ *d++ = *s++;
+ case 1:
+ *d++ = *s++;
+ break;
+ default:
+ DEBUG(0,("check_path_syntax: character length assumptions invalid !\n"));
+ *d = '\0';
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+ }
+ }
+ }
+ *d = '\0';
+ return ret;
+}
+
+/****************************************************************************
+ Pull a string and check the path - provide for error return.
+****************************************************************************/
+
+size_t srvstr_get_path(char *inbuf, char *dest, const char *src, size_t dest_len, size_t src_len, int flags, NTSTATUS *err)
+{
+ pstring tmppath;
+ char *tmppath_ptr = tmppath;
+ size_t ret;
+#ifdef DEVELOPER
+ SMB_ASSERT(dest_len == sizeof(pstring));
+#endif
+
+ if (src_len == 0) {
+ ret = srvstr_pull_buf( inbuf, tmppath_ptr, src, dest_len, flags);
+ } else {
+ ret = srvstr_pull( inbuf, tmppath_ptr, src, dest_len, src_len, flags);
+ }
+ *err = check_path_syntax(dest, tmppath);
+ return ret;
+}
+
+/****************************************************************************
+ Reply to a special message.
+****************************************************************************/
+
+int reply_special(char *inbuf,char *outbuf)
+{
+ int outsize = 4;
+ int msg_type = CVAL(inbuf,0);
+ int msg_flags = CVAL(inbuf,1);
+ fstring name1,name2;
+ char name_type = 0;
+
+ static BOOL already_got_session = False;
+
+ *name1 = *name2 = 0;
+
+ memset(outbuf,'\0',smb_size);
+
+ smb_setlen(outbuf,0);
+
+ switch (msg_type) {
+ case 0x81: /* session request */
+
+ if (already_got_session) {
+ exit_server("multiple session request not permitted");
+ }
+
+ SCVAL(outbuf,0,0x82);
+ SCVAL(outbuf,3,0);
+ if (name_len(inbuf+4) > 50 ||
+ name_len(inbuf+4 + name_len(inbuf + 4)) > 50) {
+ DEBUG(0,("Invalid name length in session request\n"));
+ return(0);
+ }
+ name_extract(inbuf,4,name1);
+ name_type = name_extract(inbuf,4 + name_len(inbuf + 4),name2);
+ DEBUG(2,("netbios connect: name1=%s name2=%s\n",
+ name1,name2));
+
+ set_local_machine_name(name1, True);
+ set_remote_machine_name(name2, True);
+
+ DEBUG(2,("netbios connect: local=%s remote=%s, name type = %x\n",
+ get_local_machine_name(), get_remote_machine_name(),
+ name_type));
+
+ if (name_type == 'R') {
+ /* We are being asked for a pathworks session ---
+ no thanks! */
+ SCVAL(outbuf, 0,0x83);
+ break;
+ }
+
+ /* only add the client's machine name to the list
+ of possibly valid usernames if we are operating
+ in share mode security */
+ if (lp_security() == SEC_SHARE) {
+ add_session_user(get_remote_machine_name());
+ }
+
+ reload_services(True);
+ reopen_logs();
+
+ claim_connection(NULL,"",0,True,FLAG_MSG_GENERAL|FLAG_MSG_SMBD);
+
+ already_got_session = True;
+ break;
+
+ case 0x89: /* session keepalive request
+ (some old clients produce this?) */
+ SCVAL(outbuf,0,SMBkeepalive);
+ SCVAL(outbuf,3,0);
+ break;
+
+ case 0x82: /* positive session response */
+ case 0x83: /* negative session response */
+ case 0x84: /* retarget session response */
+ DEBUG(0,("Unexpected session response\n"));
+ break;
+
+ case SMBkeepalive: /* session keepalive */
+ default:
+ return(0);
+ }
+
+ DEBUG(5,("init msg_type=0x%x msg_flags=0x%x\n",
+ msg_type, msg_flags));
+
+ return(outsize);
+}
+
+/****************************************************************************
+ Reply to a tcon.
+****************************************************************************/
+
+int reply_tcon(connection_struct *conn,
+ char *inbuf,char *outbuf, int dum_size, int dum_buffsize)
+{
+ const char *service;
+ pstring service_buf;
+ pstring password;
+ pstring dev;
+ int outsize = 0;
+ uint16 vuid = SVAL(inbuf,smb_uid);
+ int pwlen=0;
+ NTSTATUS nt_status;
+ char *p;
+ DATA_BLOB password_blob;
+
+ START_PROFILE(SMBtcon);
+
+ *service_buf = *password = *dev = 0;
+
+ p = smb_buf(inbuf)+1;
+ p += srvstr_pull_buf(inbuf, service_buf, p, sizeof(service_buf), STR_TERMINATE) + 1;
+ pwlen = srvstr_pull_buf(inbuf, password, p, sizeof(password), STR_TERMINATE) + 1;
+ p += pwlen;
+ p += srvstr_pull_buf(inbuf, dev, p, sizeof(dev), STR_TERMINATE) + 1;
+
+ p = strrchr_m(service_buf,'\\');
+ if (p) {
+ service = p+1;
+ } else {
+ service = service_buf;
+ }
+
+ password_blob = data_blob(password, pwlen+1);
+
+ conn = make_connection(service,password_blob,dev,vuid,&nt_status);
+
+ data_blob_clear_free(&password_blob);
+
+ if (!conn) {
+ END_PROFILE(SMBtcon);
+ return ERROR_NT(nt_status);
+ }
+
+ outsize = set_message(outbuf,2,0,True);
+ SSVAL(outbuf,smb_vwv0,max_recv);
+ SSVAL(outbuf,smb_vwv1,conn->cnum);
+ SSVAL(outbuf,smb_tid,conn->cnum);
+
+ DEBUG(3,("tcon service=%s cnum=%d\n",
+ service, conn->cnum));
+
+ END_PROFILE(SMBtcon);
+ return(outsize);
+}
+
+/****************************************************************************
+ Reply to a tcon and X.
+****************************************************************************/
+
+int reply_tcon_and_X(connection_struct *conn, char *inbuf,char *outbuf,int length,int bufsize)
+{
+ fstring service;
+ DATA_BLOB password;
+
+ /* what the cleint thinks the device is */
+ fstring client_devicetype;
+ /* what the server tells the client the share represents */
+ const char *server_devicetype;
+ NTSTATUS nt_status;
+ uint16 vuid = SVAL(inbuf,smb_uid);
+ int passlen = SVAL(inbuf,smb_vwv3);
+ pstring path;
+ char *p, *q;
+ extern BOOL global_encrypted_passwords_negotiated;
+
+ START_PROFILE(SMBtconX);
+
+ *service = *client_devicetype = 0;
+
+ /* we might have to close an old one */
+ if ((SVAL(inbuf,smb_vwv2) & 0x1) && conn) {
+ close_cnum(conn,vuid);
+ }
+
+ if (passlen > MAX_PASS_LEN) {
+ return ERROR_DOS(ERRDOS,ERRbuftoosmall);
+ }
+
+ if (global_encrypted_passwords_negotiated) {
+ password = data_blob(smb_buf(inbuf),passlen);
+ } else {
+ password = data_blob(smb_buf(inbuf),passlen+1);
+ /* Ensure correct termination */
+ password.data[passlen]=0;
+ }
+
+ p = smb_buf(inbuf) + passlen;
+ p += srvstr_pull_buf(inbuf, path, p, sizeof(path), STR_TERMINATE);
+
+ /*
+ * the service name can be either: \\server\share
+ * or share directly like on the DELL PowerVault 705
+ */
+ if (*path=='\\') {
+ q = strchr_m(path+2,'\\');
+ if (!q) {
+ END_PROFILE(SMBtconX);
+ return(ERROR_DOS(ERRDOS,ERRnosuchshare));
+ }
+ fstrcpy(service,q+1);
+ }
+ else
+ fstrcpy(service,path);
+
+ p += srvstr_pull(inbuf, client_devicetype, p, sizeof(client_devicetype), 6, STR_ASCII);
+
+ DEBUG(4,("Client requested device type [%s] for share [%s]\n", client_devicetype, service));
+
+ conn = make_connection(service,password,client_devicetype,vuid,&nt_status);
+
+ data_blob_clear_free(&password);
+
+ if (!conn) {
+ END_PROFILE(SMBtconX);
+ return ERROR_NT(nt_status);
+ }
+
+ if ( IS_IPC(conn) )
+ server_devicetype = "IPC";
+ else if ( IS_PRINT(conn) )
+ server_devicetype = "LPT1:";
+ else
+ server_devicetype = "A:";
+
+ if (Protocol < PROTOCOL_NT1) {
+ set_message(outbuf,2,0,True);
+ p = smb_buf(outbuf);
+ p += srvstr_push(outbuf, p, server_devicetype, -1,
+ STR_TERMINATE|STR_ASCII);
+ set_message_end(outbuf,p);
+ } else {
+ /* NT sets the fstype of IPC$ to the null string */
+ const char *fstype = IS_IPC(conn) ? "" : lp_fstype(SNUM(conn));
+
+ set_message(outbuf,3,0,True);
+
+ p = smb_buf(outbuf);
+ p += srvstr_push(outbuf, p, server_devicetype, -1,
+ STR_TERMINATE|STR_ASCII);
+ p += srvstr_push(outbuf, p, fstype, -1,
+ STR_TERMINATE);
+
+ set_message_end(outbuf,p);
+
+ /* what does setting this bit do? It is set by NT4 and
+ may affect the ability to autorun mounted cdroms */
+ SSVAL(outbuf, smb_vwv2, SMB_SUPPORT_SEARCH_BITS|
+ (lp_csc_policy(SNUM(conn)) << 2));
+
+ init_dfsroot(conn, inbuf, outbuf);
+ }
+
+
+ DEBUG(3,("tconX service=%s \n",
+ service));
+
+ /* set the incoming and outgoing tid to the just created one */
+ SSVAL(inbuf,smb_tid,conn->cnum);
+ SSVAL(outbuf,smb_tid,conn->cnum);
+
+ END_PROFILE(SMBtconX);
+ return chain_reply(inbuf,outbuf,length,bufsize);
+}
+
+/****************************************************************************
+ Reply to an unknown type.
+****************************************************************************/
+
+int reply_unknown(char *inbuf,char *outbuf)
+{
+ int type;
+ type = CVAL(inbuf,smb_com);
+
+ DEBUG(0,("unknown command type (%s): type=%d (0x%X)\n",
+ smb_fn_name(type), type, type));
+
+ return(ERROR_DOS(ERRSRV,ERRunknownsmb));
+}
+
+/****************************************************************************
+ Reply to an ioctl.
+****************************************************************************/
+
+int reply_ioctl(connection_struct *conn,
+ char *inbuf,char *outbuf, int dum_size, int dum_buffsize)
+{
+ uint16 device = SVAL(inbuf,smb_vwv1);
+ uint16 function = SVAL(inbuf,smb_vwv2);
+ uint32 ioctl_code = (device << 16) + function;
+ int replysize, outsize;
+ char *p;
+ START_PROFILE(SMBioctl);
+
+ DEBUG(4, ("Received IOCTL (code 0x%x)\n", ioctl_code));
+
+ switch (ioctl_code) {
+ case IOCTL_QUERY_JOB_INFO:
+ replysize = 32;
+ break;
+ default:
+ END_PROFILE(SMBioctl);
+ return(ERROR_DOS(ERRSRV,ERRnosupport));
+ }
+
+ outsize = set_message(outbuf,8,replysize+1,True);
+ SSVAL(outbuf,smb_vwv1,replysize); /* Total data bytes returned */
+ SSVAL(outbuf,smb_vwv5,replysize); /* Data bytes this buffer */
+ SSVAL(outbuf,smb_vwv6,52); /* Offset to data */
+ p = smb_buf(outbuf) + 1; /* Allow for alignment */
+
+ switch (ioctl_code) {
+ case IOCTL_QUERY_JOB_INFO:
+ {
+ files_struct *fsp = file_fsp(inbuf,smb_vwv0);
+ if (!fsp) {
+ END_PROFILE(SMBioctl);
+ return(UNIXERROR(ERRDOS,ERRbadfid));
+ }
+ SSVAL(p,0,fsp->rap_print_jobid); /* Job number */
+ srvstr_push(outbuf, p+2, global_myname(), 15, STR_TERMINATE|STR_ASCII);
+ srvstr_push(outbuf, p+18, lp_servicename(SNUM(conn)), 13, STR_TERMINATE|STR_ASCII);
+ break;
+ }
+ }
+
+ END_PROFILE(SMBioctl);
+ return outsize;
+}
+
+/****************************************************************************
+ Reply to a chkpth.
+****************************************************************************/
+
+int reply_chkpth(connection_struct *conn, char *inbuf,char *outbuf, int dum_size, int dum_buffsize)
+{
+ int outsize = 0;
+ int mode;
+ pstring name;
+ BOOL ok = False;
+ BOOL bad_path = False;
+ SMB_STRUCT_STAT sbuf;
+ NTSTATUS status;
+
+ START_PROFILE(SMBchkpth);
+
+ srvstr_get_path(inbuf, name, smb_buf(inbuf) + 1, sizeof(name), 0, STR_TERMINATE, &status);
+ if (!NT_STATUS_IS_OK(status)) {
+ END_PROFILE(SMBchkpth);
+ return ERROR_NT(status);
+ }
+
+ RESOLVE_DFSPATH(name, conn, inbuf, outbuf);
+
+ unix_convert(name,conn,0,&bad_path,&sbuf);
+
+ mode = SVAL(inbuf,smb_vwv0);
+
+ if (check_name(name,conn)) {
+ if (VALID_STAT(sbuf) || SMB_VFS_STAT(conn,name,&sbuf) == 0)
+ if (!(ok = S_ISDIR(sbuf.st_mode))) {
+ END_PROFILE(SMBchkpth);
+ return ERROR_BOTH(NT_STATUS_NOT_A_DIRECTORY,ERRDOS,ERRbadpath);
+ }
+ }
+
+ if (!ok) {
+ /* We special case this - as when a Windows machine
+ is parsing a path is steps through the components
+ one at a time - if a component fails it expects
+ ERRbadpath, not ERRbadfile.
+ */
+ if(errno == ENOENT) {
+ /*
+ * Windows returns different error codes if
+ * the parent directory is valid but not the
+ * last component - it returns NT_STATUS_OBJECT_NAME_NOT_FOUND
+ * for that case and NT_STATUS_OBJECT_PATH_NOT_FOUND
+ * if the path is invalid.
+ */
+ if (bad_path) {
+ END_PROFILE(SMBchkpth);
+ return ERROR_NT(NT_STATUS_OBJECT_PATH_NOT_FOUND);
+ } else {
+ END_PROFILE(SMBchkpth);
+ return ERROR_NT(NT_STATUS_OBJECT_NAME_NOT_FOUND);
+ }
+ } else if (errno == ENOTDIR) {
+ END_PROFILE(SMBchkpth);
+ return ERROR_NT(NT_STATUS_NOT_A_DIRECTORY);
+ }
+
+ END_PROFILE(SMBchkpth);
+ return(UNIXERROR(ERRDOS,ERRbadpath));
+ }
+
+ outsize = set_message(outbuf,0,0,True);
+
+ DEBUG(3,("chkpth %s mode=%d\n", name, mode));
+
+ END_PROFILE(SMBchkpth);
+ return(outsize);
+}
+
+/****************************************************************************
+ Reply to a getatr.
+****************************************************************************/
+
+int reply_getatr(connection_struct *conn, char *inbuf,char *outbuf, int dum_size, int dum_buffsize)
+{
+ pstring fname;
+ int outsize = 0;
+ SMB_STRUCT_STAT sbuf;
+ BOOL ok = False;
+ int mode=0;
+ SMB_OFF_T size=0;
+ time_t mtime=0;
+ BOOL bad_path = False;
+ char *p;
+ NTSTATUS status;
+
+ START_PROFILE(SMBgetatr);
+
+ p = smb_buf(inbuf) + 1;
+ p += srvstr_get_path(inbuf, fname, p, sizeof(fname), 0, STR_TERMINATE, &status);
+ if (!NT_STATUS_IS_OK(status)) {
+ END_PROFILE(SMBgetatr);
+ return ERROR_NT(status);
+ }
+
+ RESOLVE_DFSPATH(fname, conn, inbuf, outbuf);
+
+ /* dos smetimes asks for a stat of "" - it returns a "hidden directory"
+ under WfWg - weird! */
+ if (! (*fname)) {
+ mode = aHIDDEN | aDIR;
+ if (!CAN_WRITE(conn))
+ mode |= aRONLY;
+ size = 0;
+ mtime = 0;
+ ok = True;
+ } else {
+ unix_convert(fname,conn,0,&bad_path,&sbuf);
+ if (check_name(fname,conn)) {
+ if (VALID_STAT(sbuf) || SMB_VFS_STAT(conn,fname,&sbuf) == 0) {
+ mode = dos_mode(conn,fname,&sbuf);
+ size = sbuf.st_size;
+ mtime = sbuf.st_mtime;
+ if (mode & aDIR)
+ size = 0;
+ ok = True;
+ } else {
+ DEBUG(3,("stat of %s failed (%s)\n",fname,strerror(errno)));
+ }
+ }
+ }
+
+ if (!ok) {
+ END_PROFILE(SMBgetatr);
+ return set_bad_path_error(errno, bad_path, outbuf, ERRDOS,ERRbadfile);
+ }
+
+ outsize = set_message(outbuf,10,0,True);
+
+ SSVAL(outbuf,smb_vwv0,mode);
+ if(lp_dos_filetime_resolution(SNUM(conn)) )
+ put_dos_date3(outbuf,smb_vwv1,mtime & ~1);
+ else
+ put_dos_date3(outbuf,smb_vwv1,mtime);
+ SIVAL(outbuf,smb_vwv3,(uint32)size);
+
+ if (Protocol >= PROTOCOL_NT1)
+ SSVAL(outbuf,smb_flg2,SVAL(outbuf, smb_flg2) | FLAGS2_IS_LONG_NAME);
+
+ DEBUG( 3, ( "getatr name=%s mode=%d size=%d\n", fname, mode, (uint32)size ) );
+
+ END_PROFILE(SMBgetatr);
+ return(outsize);
+}
+
+/****************************************************************************
+ Reply to a setatr.
+****************************************************************************/
+
+int reply_setatr(connection_struct *conn, char *inbuf,char *outbuf, int dum_size, int dum_buffsize)
+{
+ pstring fname;
+ int outsize = 0;
+ BOOL ok=False;
+ int mode;
+ time_t mtime;
+ SMB_STRUCT_STAT sbuf;
+ BOOL bad_path = False;
+ char *p;
+ NTSTATUS status;
+
+ START_PROFILE(SMBsetatr);
+
+ p = smb_buf(inbuf) + 1;
+ p += srvstr_get_path(inbuf, fname, p, sizeof(fname), 0, STR_TERMINATE, &status);
+ if (!NT_STATUS_IS_OK(status)) {
+ END_PROFILE(SMBsetatr);
+ return ERROR_NT(status);
+ }
+
+ unix_convert(fname,conn,0,&bad_path,&sbuf);
+
+ mode = SVAL(inbuf,smb_vwv0);
+ mtime = make_unix_date3(inbuf+smb_vwv1);
+
+ if (mode != FILE_ATTRIBUTE_NORMAL) {
+ if (VALID_STAT_OF_DIR(sbuf))
+ mode |= aDIR;
+ else
+ mode &= ~aDIR;
+
+ if (check_name(fname,conn)) {
+ ok = (file_set_dosmode(conn,fname,mode,NULL) == 0);
+ }
+ } else {
+ ok = True;
+ }
+
+ if (ok)
+ ok = set_filetime(conn,fname,mtime);
+
+ if (!ok) {
+ END_PROFILE(SMBsetatr);
+ return set_bad_path_error(errno, bad_path, outbuf, ERRDOS, ERRnoaccess);
+ }
+
+ outsize = set_message(outbuf,0,0,True);
+
+ DEBUG( 3, ( "setatr name=%s mode=%d\n", fname, mode ) );
+
+ END_PROFILE(SMBsetatr);
+ return(outsize);
+}
+
+/****************************************************************************
+ Reply to a dskattr.
+****************************************************************************/
+
+int reply_dskattr(connection_struct *conn, char *inbuf,char *outbuf, int dum_size, int dum_buffsize)
+{
+ int outsize = 0;
+ SMB_BIG_UINT dfree,dsize,bsize;
+ START_PROFILE(SMBdskattr);
+
+ SMB_VFS_DISK_FREE(conn,".",True,&bsize,&dfree,&dsize);
+
+ outsize = set_message(outbuf,5,0,True);
+
+ if (Protocol <= PROTOCOL_LANMAN2) {
+ double total_space, free_space;
+ /* we need to scale this to a number that DOS6 can handle. We
+ use floating point so we can handle large drives on systems
+ that don't have 64 bit integers
+
+ we end up displaying a maximum of 2G to DOS systems
+ */
+ total_space = dsize * (double)bsize;
+ free_space = dfree * (double)bsize;
+
+ dsize = (total_space+63*512) / (64*512);
+ dfree = (free_space+63*512) / (64*512);
+
+ if (dsize > 0xFFFF) dsize = 0xFFFF;
+ if (dfree > 0xFFFF) dfree = 0xFFFF;
+
+ SSVAL(outbuf,smb_vwv0,dsize);
+ SSVAL(outbuf,smb_vwv1,64); /* this must be 64 for dos systems */
+ SSVAL(outbuf,smb_vwv2,512); /* and this must be 512 */
+ SSVAL(outbuf,smb_vwv3,dfree);
+ } else {
+ SSVAL(outbuf,smb_vwv0,dsize);
+ SSVAL(outbuf,smb_vwv1,bsize/512);
+ SSVAL(outbuf,smb_vwv2,512);
+ SSVAL(outbuf,smb_vwv3,dfree);
+ }
+
+ DEBUG(3,("dskattr dfree=%d\n", (unsigned int)dfree));
+
+ END_PROFILE(SMBdskattr);
+ return(outsize);
+}
+
+/****************************************************************************
+ Reply to a search.
+ Can be called from SMBsearch, SMBffirst or SMBfunique.
+****************************************************************************/
+
+int reply_search(connection_struct *conn, char *inbuf,char *outbuf, int dum_size, int dum_buffsize)
+{
+ pstring mask;
+ pstring directory;
+ pstring fname;
+ SMB_OFF_T size;
+ int mode;
+ time_t date;
+ int dirtype;
+ int outsize = 0;
+ unsigned int numentries = 0;
+ unsigned int maxentries = 0;
+ BOOL finished = False;
+ char *p;
+ BOOL ok = False;
+ int status_len;
+ pstring path;
+ char status[21];
+ int dptr_num= -1;
+ BOOL check_descend = False;
+ BOOL expect_close = False;
+ BOOL can_open = True;
+ BOOL bad_path = False;
+ NTSTATUS nt_status;
+ START_PROFILE(SMBsearch);
+
+ *mask = *directory = *fname = 0;
+
+ /* If we were called as SMBffirst then we must expect close. */
+ if(CVAL(inbuf,smb_com) == SMBffirst)
+ expect_close = True;
+
+ outsize = set_message(outbuf,1,3,True);
+ maxentries = SVAL(inbuf,smb_vwv0);
+ dirtype = SVAL(inbuf,smb_vwv1);
+ p = smb_buf(inbuf) + 1;
+ p += srvstr_get_path(inbuf, path, p, sizeof(path), 0, STR_TERMINATE, &nt_status);
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ END_PROFILE(SMBsearch);
+ return ERROR_NT(nt_status);
+ }
+ p++;
+ status_len = SVAL(p, 0);
+ p += 2;
+
+ /* dirtype &= ~aDIR; */
+
+ if (status_len == 0) {
+ SMB_STRUCT_STAT sbuf;
+ pstring dir2;
+
+ pstrcpy(directory,path);
+ pstrcpy(dir2,path);
+ unix_convert(directory,conn,0,&bad_path,&sbuf);
+ unix_format(dir2);
+
+ if (!check_name(directory,conn))
+ can_open = False;
+
+ p = strrchr_m(dir2,'/');
+ if (p == NULL) {
+ pstrcpy(mask,dir2);
+ *dir2 = 0;
+ } else {
+ *p = 0;
+ pstrcpy(mask,p+1);
+ }
+
+ p = strrchr_m(directory,'/');
+ if (!p)
+ *directory = 0;
+ else
+ *p = 0;
+
+ if (strlen(directory) == 0)
+ pstrcpy(directory,".");
+ memset((char *)status,'\0',21);
+ SCVAL(status,0,(dirtype & 0x1F));
+ } else {
+ int status_dirtype;
+
+ memcpy(status,p,21);
+ status_dirtype = CVAL(status,0) & 0x1F;
+ if (status_dirtype != (dirtype & 0x1F))
+ dirtype = status_dirtype;
+
+ conn->dirptr = dptr_fetch(status+12,&dptr_num);
+ if (!conn->dirptr)
+ goto SearchEmpty;
+ string_set(&conn->dirpath,dptr_path(dptr_num));
+ pstrcpy(mask, dptr_wcard(dptr_num));
+ }
+
+ if (can_open) {
+ p = smb_buf(outbuf) + 3;
+ ok = True;
+
+ if (status_len == 0) {
+ dptr_num = dptr_create(conn,directory,True,expect_close,SVAL(inbuf,smb_pid));
+ if (dptr_num < 0) {
+ if(dptr_num == -2) {
+ END_PROFILE(SMBsearch);
+ return set_bad_path_error(errno, bad_path, outbuf, ERRDOS, ERRnofids);
+ }
+ END_PROFILE(SMBsearch);
+ return ERROR_DOS(ERRDOS,ERRnofids);
+ }
+ dptr_set_wcard(dptr_num, strdup(mask));
+ dptr_set_attr(dptr_num, dirtype);
+ } else {
+ dirtype = dptr_attr(dptr_num);
+ }
+
+ DEBUG(4,("dptr_num is %d\n",dptr_num));
+
+ if (ok) {
+ if ((dirtype&0x1F) == aVOLID) {
+ memcpy(p,status,21);
+ make_dir_struct(p,"???????????",volume_label(SNUM(conn)),0,aVOLID,0);
+ dptr_fill(p+12,dptr_num);
+ if (dptr_zero(p+12) && (status_len==0))
+ numentries = 1;
+ else
+ numentries = 0;
+ p += DIR_STRUCT_SIZE;
+ } else {
+ unsigned int i;
+ maxentries = MIN(maxentries, ((BUFFER_SIZE - (p - outbuf))/DIR_STRUCT_SIZE));
+
+ DEBUG(8,("dirpath=<%s> dontdescend=<%s>\n",
+ conn->dirpath,lp_dontdescend(SNUM(conn))));
+ if (in_list(conn->dirpath, lp_dontdescend(SNUM(conn)),True))
+ check_descend = True;
+
+ for (i=numentries;(i<maxentries) && !finished;i++) {
+ finished = !get_dir_entry(conn,mask,dirtype,fname,&size,&mode,&date,check_descend);
+ if (!finished) {
+ memcpy(p,status,21);
+ make_dir_struct(p,mask,fname,size,mode,date);
+ dptr_fill(p+12,dptr_num);
+ numentries++;
+ }
+ p += DIR_STRUCT_SIZE;
+ }
+ }
+ } /* if (ok ) */
+ }
+
+
+ SearchEmpty:
+
+ /* If we were called as SMBffirst with smb_search_id == NULL
+ and no entries were found then return error and close dirptr
+ (X/Open spec) */
+
+ if(ok && expect_close && numentries == 0 && status_len == 0) {
+ if (Protocol < PROTOCOL_NT1) {
+ SCVAL(outbuf,smb_rcls,ERRDOS);
+ SSVAL(outbuf,smb_err,ERRnofiles);
+ }
+ /* Also close the dptr - we know it's gone */
+ dptr_close(&dptr_num);
+ } else if (numentries == 0 || !ok) {
+ if (Protocol < PROTOCOL_NT1) {
+ SCVAL(outbuf,smb_rcls,ERRDOS);
+ SSVAL(outbuf,smb_err,ERRnofiles);
+ }
+ dptr_close(&dptr_num);
+ }
+
+ /* If we were called as SMBfunique, then we can close the dirptr now ! */
+ if(dptr_num >= 0 && CVAL(inbuf,smb_com) == SMBfunique)
+ dptr_close(&dptr_num);
+
+ SSVAL(outbuf,smb_vwv0,numentries);
+ SSVAL(outbuf,smb_vwv1,3 + numentries * DIR_STRUCT_SIZE);
+ SCVAL(smb_buf(outbuf),0,5);
+ SSVAL(smb_buf(outbuf),1,numentries*DIR_STRUCT_SIZE);
+
+ if (Protocol >= PROTOCOL_NT1)
+ SSVAL(outbuf,smb_flg2,SVAL(outbuf, smb_flg2) | FLAGS2_IS_LONG_NAME);
+
+ outsize += DIR_STRUCT_SIZE*numentries;
+ smb_setlen(outbuf,outsize - 4);
+
+ if ((! *directory) && dptr_path(dptr_num))
+ slprintf(directory, sizeof(directory)-1, "(%s)",dptr_path(dptr_num));
+
+ DEBUG( 4, ( "%s mask=%s path=%s dtype=%d nument=%u of %u\n",
+ smb_fn_name(CVAL(inbuf,smb_com)),
+ mask, directory, dirtype, numentries, maxentries ) );
+
+ END_PROFILE(SMBsearch);
+ return(outsize);
+}
+
+/****************************************************************************
+ Reply to a fclose (stop directory search).
+****************************************************************************/
+
+int reply_fclose(connection_struct *conn, char *inbuf,char *outbuf, int dum_size, int dum_buffsize)
+{
+ int outsize = 0;
+ int status_len;
+ pstring path;
+ char status[21];
+ int dptr_num= -2;
+ char *p;
+ NTSTATUS err;
+
+ START_PROFILE(SMBfclose);
+
+ outsize = set_message(outbuf,1,0,True);
+ p = smb_buf(inbuf) + 1;
+ p += srvstr_get_path(inbuf, path, p, sizeof(path), 0, STR_TERMINATE, &err);
+ if (!NT_STATUS_IS_OK(err)) {
+ END_PROFILE(SMBfclose);
+ return ERROR_NT(err);
+ }
+ p++;
+ status_len = SVAL(p,0);
+ p += 2;
+
+ if (status_len == 0) {
+ END_PROFILE(SMBfclose);
+ return ERROR_DOS(ERRSRV,ERRsrverror);
+ }
+
+ memcpy(status,p,21);
+
+ if(dptr_fetch(status+12,&dptr_num)) {
+ /* Close the dptr - we know it's gone */
+ dptr_close(&dptr_num);
+ }
+
+ SSVAL(outbuf,smb_vwv0,0);
+
+ DEBUG(3,("search close\n"));
+
+ END_PROFILE(SMBfclose);
+ return(outsize);
+}
+
+/****************************************************************************
+ Reply to an open.
+****************************************************************************/
+
+int reply_open(connection_struct *conn, char *inbuf,char *outbuf, int dum_size, int dum_buffsize)
+{
+ pstring fname;
+ int outsize = 0;
+ int fmode=0;
+ int share_mode;
+ SMB_OFF_T size = 0;
+ time_t mtime=0;
+ int rmode=0;
+ SMB_STRUCT_STAT sbuf;
+ BOOL bad_path = False;
+ files_struct *fsp;
+ int oplock_request = CORE_OPLOCK_REQUEST(inbuf);
+ uint16 dos_attr = SVAL(inbuf,smb_vwv1);
+ NTSTATUS status;
+ START_PROFILE(SMBopen);
+
+ share_mode = SVAL(inbuf,smb_vwv0);
+
+ srvstr_get_path(inbuf, fname, smb_buf(inbuf)+1, sizeof(fname), 0, STR_TERMINATE, &status);
+ if (!NT_STATUS_IS_OK(status)) {
+ END_PROFILE(SMBopen);
+ return ERROR_NT(status);
+ }
+
+ RESOLVE_DFSPATH(fname, conn, inbuf, outbuf);
+
+ unix_convert(fname,conn,0,&bad_path,&sbuf);
+
+ fsp = open_file_shared(conn,fname,&sbuf,share_mode,(FILE_FAIL_IF_NOT_EXIST|FILE_EXISTS_OPEN),
+ (uint32)dos_attr, oplock_request,&rmode,NULL);
+
+ if (!fsp) {
+ END_PROFILE(SMBopen);
+ return set_bad_path_error(errno, bad_path, outbuf, ERRDOS, ERRnoaccess);
+ }
+
+ size = sbuf.st_size;
+ fmode = dos_mode(conn,fname,&sbuf);
+ mtime = sbuf.st_mtime;
+
+ if (fmode & aDIR) {
+ DEBUG(3,("attempt to open a directory %s\n",fname));
+ close_file(fsp,False);
+ END_PROFILE(SMBopen);
+ return ERROR_DOS(ERRDOS,ERRnoaccess);
+ }
+
+ outsize = set_message(outbuf,7,0,True);
+ SSVAL(outbuf,smb_vwv0,fsp->fnum);
+ SSVAL(outbuf,smb_vwv1,fmode);
+ if(lp_dos_filetime_resolution(SNUM(conn)) )
+ put_dos_date3(outbuf,smb_vwv2,mtime & ~1);
+ else
+ put_dos_date3(outbuf,smb_vwv2,mtime);
+ SIVAL(outbuf,smb_vwv4,(uint32)size);
+ SSVAL(outbuf,smb_vwv6,rmode);
+
+ if (oplock_request && lp_fake_oplocks(SNUM(conn)))
+ SCVAL(outbuf,smb_flg,CVAL(outbuf,smb_flg)|CORE_OPLOCK_GRANTED);
+
+ if(EXCLUSIVE_OPLOCK_TYPE(fsp->oplock_type))
+ SCVAL(outbuf,smb_flg,CVAL(outbuf,smb_flg)|CORE_OPLOCK_GRANTED);
+ END_PROFILE(SMBopen);
+ return(outsize);
+}
+
+/****************************************************************************
+ Reply to an open and X.
+****************************************************************************/
+
+int reply_open_and_X(connection_struct *conn, char *inbuf,char *outbuf,int length,int bufsize)
+{
+ pstring fname;
+ int smb_mode = SVAL(inbuf,smb_vwv3);
+ int smb_attr = SVAL(inbuf,smb_vwv5);
+ /* Breakout the oplock request bits so we can set the
+ reply bits separately. */
+ BOOL ex_oplock_request = EXTENDED_OPLOCK_REQUEST(inbuf);
+ BOOL core_oplock_request = CORE_OPLOCK_REQUEST(inbuf);
+ BOOL oplock_request = ex_oplock_request | core_oplock_request;
+#if 0
+ int open_flags = SVAL(inbuf,smb_vwv2);
+ int smb_sattr = SVAL(inbuf,smb_vwv4);
+ uint32 smb_time = make_unix_date3(inbuf+smb_vwv6);
+#endif
+ int smb_ofun = SVAL(inbuf,smb_vwv8);
+ SMB_OFF_T size=0;
+ int fmode=0,mtime=0,rmode=0;
+ SMB_STRUCT_STAT sbuf;
+ int smb_action = 0;
+ BOOL bad_path = False;
+ files_struct *fsp;
+ NTSTATUS status;
+ START_PROFILE(SMBopenX);
+
+ /* If it's an IPC, pass off the pipe handler. */
+ if (IS_IPC(conn)) {
+ if (lp_nt_pipe_support()) {
+ END_PROFILE(SMBopenX);
+ return reply_open_pipe_and_X(conn, inbuf,outbuf,length,bufsize);
+ } else {
+ END_PROFILE(SMBopenX);
+ return ERROR_DOS(ERRSRV,ERRaccess);
+ }
+ }
+
+ /* XXXX we need to handle passed times, sattr and flags */
+ srvstr_get_path(inbuf, fname, smb_buf(inbuf), sizeof(fname), 0, STR_TERMINATE, &status);
+ if (!NT_STATUS_IS_OK(status)) {
+ END_PROFILE(SMBopenX);
+ return ERROR_NT(status);
+ }
+
+ RESOLVE_DFSPATH(fname, conn, inbuf, outbuf);
+
+ unix_convert(fname,conn,0,&bad_path,&sbuf);
+
+ fsp = open_file_shared(conn,fname,&sbuf,smb_mode,smb_ofun,(uint32)smb_attr,
+ oplock_request, &rmode,&smb_action);
+
+ if (!fsp) {
+ END_PROFILE(SMBopenX);
+ return set_bad_path_error(errno, bad_path, outbuf, ERRDOS, ERRnoaccess);
+ }
+
+ size = sbuf.st_size;
+ fmode = dos_mode(conn,fname,&sbuf);
+ mtime = sbuf.st_mtime;
+ if (fmode & aDIR) {
+ close_file(fsp,False);
+ END_PROFILE(SMBopenX);
+ return ERROR_DOS(ERRDOS,ERRnoaccess);
+ }
+
+ /* If the caller set the extended oplock request bit
+ and we granted one (by whatever means) - set the
+ correct bit for extended oplock reply.
+ */
+
+ if (ex_oplock_request && lp_fake_oplocks(SNUM(conn)))
+ smb_action |= EXTENDED_OPLOCK_GRANTED;
+
+ if(ex_oplock_request && EXCLUSIVE_OPLOCK_TYPE(fsp->oplock_type))
+ smb_action |= EXTENDED_OPLOCK_GRANTED;
+
+ /* If the caller set the core oplock request bit
+ and we granted one (by whatever means) - set the
+ correct bit for core oplock reply.
+ */
+
+ if (core_oplock_request && lp_fake_oplocks(SNUM(conn)))
+ SCVAL(outbuf,smb_flg,CVAL(outbuf,smb_flg)|CORE_OPLOCK_GRANTED);
+
+ if(core_oplock_request && EXCLUSIVE_OPLOCK_TYPE(fsp->oplock_type))
+ SCVAL(outbuf,smb_flg,CVAL(outbuf,smb_flg)|CORE_OPLOCK_GRANTED);
+
+ set_message(outbuf,15,0,True);
+ SSVAL(outbuf,smb_vwv2,fsp->fnum);
+ SSVAL(outbuf,smb_vwv3,fmode);
+ if(lp_dos_filetime_resolution(SNUM(conn)) )
+ put_dos_date3(outbuf,smb_vwv4,mtime & ~1);
+ else
+ put_dos_date3(outbuf,smb_vwv4,mtime);
+ SIVAL(outbuf,smb_vwv6,(uint32)size);
+ SSVAL(outbuf,smb_vwv8,rmode);
+ SSVAL(outbuf,smb_vwv11,smb_action);
+
+ END_PROFILE(SMBopenX);
+ return chain_reply(inbuf,outbuf,length,bufsize);
+}
+
+/****************************************************************************
+ Reply to a SMBulogoffX.
+****************************************************************************/
+
+int reply_ulogoffX(connection_struct *conn, char *inbuf,char *outbuf,int length,int bufsize)
+{
+ uint16 vuid = SVAL(inbuf,smb_uid);
+ user_struct *vuser = get_valid_user_struct(vuid);
+ START_PROFILE(SMBulogoffX);
+
+ if(vuser == 0)
+ DEBUG(3,("ulogoff, vuser id %d does not map to user.\n", vuid));
+
+ /* in user level security we are supposed to close any files
+ open by this user */
+ if ((vuser != 0) && (lp_security() != SEC_SHARE))
+ file_close_user(vuid);
+
+ invalidate_vuid(vuid);
+
+ set_message(outbuf,2,0,True);
+
+ DEBUG( 3, ( "ulogoffX vuid=%d\n", vuid ) );
+
+ END_PROFILE(SMBulogoffX);
+ return chain_reply(inbuf,outbuf,length,bufsize);
+}
+
+/****************************************************************************
+ Reply to a mknew or a create.
+****************************************************************************/
+
+int reply_mknew(connection_struct *conn, char *inbuf,char *outbuf, int dum_size, int dum_buffsize)
+{
+ pstring fname;
+ int com;
+ int outsize = 0;
+ int createmode;
+ int ofun = 0;
+ BOOL bad_path = False;
+ files_struct *fsp;
+ int oplock_request = CORE_OPLOCK_REQUEST(inbuf);
+ SMB_STRUCT_STAT sbuf;
+ NTSTATUS status;
+ START_PROFILE(SMBcreate);
+
+ com = SVAL(inbuf,smb_com);
+
+ createmode = SVAL(inbuf,smb_vwv0);
+ srvstr_get_path(inbuf, fname, smb_buf(inbuf) + 1, sizeof(fname), 0, STR_TERMINATE, &status);
+ if (!NT_STATUS_IS_OK(status)) {
+ END_PROFILE(SMBcreate);
+ return ERROR_NT(status);
+ }
+
+ RESOLVE_DFSPATH(fname, conn, inbuf, outbuf);
+
+ unix_convert(fname,conn,0,&bad_path,&sbuf);
+
+ if (createmode & aVOLID)
+ DEBUG(0,("Attempt to create file (%s) with volid set - please report this\n",fname));
+
+ if(com == SMBmknew) {
+ /* We should fail if file exists. */
+ ofun = FILE_CREATE_IF_NOT_EXIST;
+ } else {
+ /* SMBcreate - Create if file doesn't exist, truncate if it does. */
+ ofun = FILE_CREATE_IF_NOT_EXIST|FILE_EXISTS_TRUNCATE;
+ }
+
+ /* Open file in dos compatibility share mode. */
+ fsp = open_file_shared(conn,fname,&sbuf,SET_DENY_MODE(DENY_FCB)|SET_OPEN_MODE(DOS_OPEN_FCB),
+ ofun, (uint32)createmode, oplock_request, NULL, NULL);
+
+ if (!fsp) {
+ END_PROFILE(SMBcreate);
+ return set_bad_path_error(errno, bad_path, outbuf, ERRDOS, ERRnoaccess);
+ }
+
+ outsize = set_message(outbuf,1,0,True);
+ SSVAL(outbuf,smb_vwv0,fsp->fnum);
+
+ if (oplock_request && lp_fake_oplocks(SNUM(conn)))
+ SCVAL(outbuf,smb_flg,CVAL(outbuf,smb_flg)|CORE_OPLOCK_GRANTED);
+
+ if(EXCLUSIVE_OPLOCK_TYPE(fsp->oplock_type))
+ SCVAL(outbuf,smb_flg,CVAL(outbuf,smb_flg)|CORE_OPLOCK_GRANTED);
+
+ DEBUG( 2, ( "new file %s\n", fname ) );
+ DEBUG( 3, ( "mknew %s fd=%d dmode=%d\n", fname, fsp->fd, createmode ) );
+
+ END_PROFILE(SMBcreate);
+ return(outsize);
+}
+
+/****************************************************************************
+ Reply to a create temporary file.
+****************************************************************************/
+
+int reply_ctemp(connection_struct *conn, char *inbuf,char *outbuf, int dum_size, int dum_buffsize)
+{
+ pstring fname;
+ int outsize = 0;
+ int createattr;
+ BOOL bad_path = False;
+ files_struct *fsp;
+ int oplock_request = CORE_OPLOCK_REQUEST(inbuf);
+ int tmpfd;
+ SMB_STRUCT_STAT sbuf;
+ char *p, *s;
+ NTSTATUS status;
+ unsigned int namelen;
+
+ START_PROFILE(SMBctemp);
+
+ createattr = SVAL(inbuf,smb_vwv0);
+ srvstr_get_path(inbuf, fname, smb_buf(inbuf)+1, sizeof(fname), 0, STR_TERMINATE, &status);
+ if (!NT_STATUS_IS_OK(status)) {
+ END_PROFILE(SMBctemp);
+ return ERROR_NT(status);
+ }
+ if (*fname) {
+ pstrcat(fname,"/TMXXXXXX");
+ } else {
+ pstrcat(fname,"TMXXXXXX");
+ }
+
+ RESOLVE_DFSPATH(fname, conn, inbuf, outbuf);
+
+ unix_convert(fname,conn,0,&bad_path,&sbuf);
+
+ tmpfd = smb_mkstemp(fname);
+ if (tmpfd == -1) {
+ END_PROFILE(SMBctemp);
+ return(UNIXERROR(ERRDOS,ERRnoaccess));
+ }
+
+ SMB_VFS_STAT(conn,fname,&sbuf);
+
+ /* Open file in dos compatibility share mode. */
+ /* We should fail if file does not exist. */
+ fsp = open_file_shared(conn,fname,&sbuf,
+ SET_DENY_MODE(DENY_FCB)|SET_OPEN_MODE(DOS_OPEN_FCB),
+ FILE_EXISTS_OPEN|FILE_FAIL_IF_NOT_EXIST,
+ (uint32)createattr, oplock_request, NULL, NULL);
+
+ /* close fd from smb_mkstemp() */
+ close(tmpfd);
+
+ if (!fsp) {
+ END_PROFILE(SMBctemp);
+ return set_bad_path_error(errno, bad_path, outbuf, ERRDOS, ERRnoaccess);
+ }
+
+ outsize = set_message(outbuf,1,0,True);
+ SSVAL(outbuf,smb_vwv0,fsp->fnum);
+
+ /* the returned filename is relative to the directory */
+ s = strrchr_m(fname, '/');
+ if (!s)
+ s = fname;
+ else
+ s++;
+
+ p = smb_buf(outbuf);
+#if 0
+ /* Tested vs W2K3 - this doesn't seem to be here - null terminated filename is the only
+ thing in the byte section. JRA */
+ SSVALS(p, 0, -1); /* what is this? not in spec */
+#endif
+ namelen = srvstr_push(outbuf, p, s, -1, STR_ASCII|STR_TERMINATE);
+ p += namelen;
+ outsize = set_message_end(outbuf, p);
+
+ if (oplock_request && lp_fake_oplocks(SNUM(conn)))
+ SCVAL(outbuf,smb_flg,CVAL(outbuf,smb_flg)|CORE_OPLOCK_GRANTED);
+
+ if (EXCLUSIVE_OPLOCK_TYPE(fsp->oplock_type))
+ SCVAL(outbuf,smb_flg,CVAL(outbuf,smb_flg)|CORE_OPLOCK_GRANTED);
+
+ DEBUG( 2, ( "created temp file %s\n", fname ) );
+ DEBUG( 3, ( "ctemp %s fd=%d umode=%o\n",
+ fname, fsp->fd, sbuf.st_mode ) );
+
+ END_PROFILE(SMBctemp);
+ return(outsize);
+}
+
+/*******************************************************************
+ Check if a user is allowed to rename a file.
+********************************************************************/
+
+static NTSTATUS can_rename(char *fname,connection_struct *conn, SMB_STRUCT_STAT *pst)
+{
+ int smb_action;
+ int access_mode;
+ files_struct *fsp;
+
+ if (!CAN_WRITE(conn))
+ return NT_STATUS_MEDIA_WRITE_PROTECTED;
+
+ if (S_ISDIR(pst->st_mode))
+ return NT_STATUS_OK;
+
+ /* We need a better way to return NT status codes from open... */
+ unix_ERR_class = 0;
+ unix_ERR_code = 0;
+
+ fsp = open_file_shared1(conn, fname, pst, DELETE_ACCESS, SET_DENY_MODE(DENY_ALL),
+ (FILE_FAIL_IF_NOT_EXIST|FILE_EXISTS_OPEN), FILE_ATTRIBUTE_NORMAL, 0, &access_mode, &smb_action);
+
+ if (!fsp) {
+ NTSTATUS ret = NT_STATUS_ACCESS_DENIED;
+ if (unix_ERR_class == ERRDOS && unix_ERR_code == ERRbadshare)
+ ret = NT_STATUS_SHARING_VIOLATION;
+ unix_ERR_class = 0;
+ unix_ERR_code = 0;
+ unix_ERR_ntstatus = NT_STATUS_OK;
+ return ret;
+ }
+ close_file(fsp,False);
+ return NT_STATUS_OK;
+}
+
+/*******************************************************************
+ Check if a user is allowed to delete a file.
+********************************************************************/
+
+static NTSTATUS can_delete(char *fname,connection_struct *conn, int dirtype, BOOL bad_path)
+{
+ SMB_STRUCT_STAT sbuf;
+ int fmode;
+ int smb_action;
+ int access_mode;
+ files_struct *fsp;
+
+ DEBUG(10,("can_delete: %s, dirtype = %d\n",
+ fname, dirtype ));
+
+ if (!CAN_WRITE(conn))
+ return NT_STATUS_MEDIA_WRITE_PROTECTED;
+
+ if (SMB_VFS_LSTAT(conn,fname,&sbuf) != 0) {
+ if(errno == ENOENT) {
+ if (bad_path)
+ return NT_STATUS_OBJECT_PATH_NOT_FOUND;
+ else
+ return NT_STATUS_OBJECT_NAME_NOT_FOUND;
+ }
+ return map_nt_error_from_unix(errno);
+ }
+
+ fmode = dos_mode(conn,fname,&sbuf);
+
+ /* Can't delete a directory. */
+ if (fmode & aDIR)
+ return NT_STATUS_FILE_IS_A_DIRECTORY;
+#if 0 /* JRATEST */
+ else if (dirtype & aDIR) /* Asked for a directory and it isn't. */
+ return NT_STATUS_OBJECT_NAME_INVALID;
+#endif /* JRATEST */
+
+ if (!lp_delete_readonly(SNUM(conn))) {
+ if (fmode & aRONLY)
+ return NT_STATUS_CANNOT_DELETE;
+ }
+ if ((fmode & ~dirtype) & (aHIDDEN | aSYSTEM))
+ return NT_STATUS_NO_SUCH_FILE;
+
+ /* We need a better way to return NT status codes from open... */
+ unix_ERR_class = 0;
+ unix_ERR_code = 0;
+
+ fsp = open_file_shared1(conn, fname, &sbuf, DELETE_ACCESS, SET_DENY_MODE(DENY_ALL),
+ (FILE_FAIL_IF_NOT_EXIST|FILE_EXISTS_OPEN), FILE_ATTRIBUTE_NORMAL, 0, &access_mode, &smb_action);
+
+ if (!fsp) {
+ NTSTATUS ret = NT_STATUS_ACCESS_DENIED;
+ if (!NT_STATUS_IS_OK(unix_ERR_ntstatus))
+ ret = unix_ERR_ntstatus;
+ else if (unix_ERR_class == ERRDOS && unix_ERR_code == ERRbadshare)
+ ret = NT_STATUS_SHARING_VIOLATION;
+ unix_ERR_class = 0;
+ unix_ERR_code = 0;
+ unix_ERR_ntstatus = NT_STATUS_OK;
+ return ret;
+ }
+ close_file(fsp,False);
+ return NT_STATUS_OK;
+}
+
+/****************************************************************************
+ The guts of the unlink command, split out so it may be called by the NT SMB
+ code.
+****************************************************************************/
+
+NTSTATUS unlink_internals(connection_struct *conn, int dirtype, char *name)
+{
+ pstring directory;
+ pstring mask;
+ char *p;
+ int count=0;
+ NTSTATUS error = NT_STATUS_OK;
+ BOOL has_wild;
+ BOOL bad_path = False;
+ BOOL rc = True;
+ SMB_STRUCT_STAT sbuf;
+
+ *directory = *mask = 0;
+
+ /* We must check for wildcards in the name given
+ * directly by the client - before any unmangling.
+ * This prevents an unmangling of a UNIX name containing
+ * a DOS wildcard like '*' or '?' from unmangling into
+ * a wildcard delete which was not intended.
+ * FIX for #226. JRA.
+ */
+
+ has_wild = ms_has_wild(name);
+
+ rc = unix_convert(name,conn,0,&bad_path,&sbuf);
+
+ p = strrchr_m(name,'/');
+ if (!p) {
+ pstrcpy(directory,".");
+ pstrcpy(mask,name);
+ } else {
+ *p = 0;
+ pstrcpy(directory,name);
+ pstrcpy(mask,p+1);
+ }
+
+ /*
+ * We should only check the mangled cache
+ * here if unix_convert failed. This means
+ * that the path in 'mask' doesn't exist
+ * on the file system and so we need to look
+ * for a possible mangle. This patch from
+ * Tine Smukavec <valentin.smukavec@hermes.si>.
+ */
+
+ if (!rc && mangle_is_mangled(mask))
+ mangle_check_cache( mask );
+
+ if (!has_wild) {
+ pstrcat(directory,"/");
+ pstrcat(directory,mask);
+ error = can_delete(directory,conn,dirtype,bad_path);
+ if (!NT_STATUS_IS_OK(error))
+ return error;
+
+ if (SMB_VFS_UNLINK(conn,directory) == 0) {
+ count++;
+ }
+ } else {
+ void *dirptr = NULL;
+ const char *dname;
+
+ if (check_name(directory,conn))
+ dirptr = OpenDir(conn, directory, True);
+
+ /* XXXX the CIFS spec says that if bit0 of the flags2 field is set then
+ the pattern matches against the long name, otherwise the short name
+ We don't implement this yet XXXX
+ */
+
+ if (dirptr) {
+ error = NT_STATUS_NO_SUCH_FILE;
+
+ if (strequal(mask,"????????.???"))
+ pstrcpy(mask,"*");
+
+ while ((dname = ReadDirName(dirptr))) {
+ pstring fname;
+ BOOL sys_direntry = False;
+ pstrcpy(fname,dname);
+
+ /* Quick check for "." and ".." */
+ if (fname[0] == '.') {
+ if (!fname[1] || (fname[1] == '.' && !fname[2])) {
+ if ((dirtype & aDIR)) {
+ sys_direntry = True;
+ } else {
+ continue;
+ }
+ }
+ }
+
+ if(!mask_match(fname, mask, case_sensitive))
+ continue;
+
+ if (sys_direntry) {
+ error = NT_STATUS_OBJECT_NAME_INVALID;
+ break;
+ }
+
+ slprintf(fname,sizeof(fname)-1, "%s/%s",directory,dname);
+ error = can_delete(fname,conn,dirtype,bad_path);
+ if (!NT_STATUS_IS_OK(error)) {
+ continue;
+ }
+ if (SMB_VFS_UNLINK(conn,fname) == 0)
+ count++;
+ DEBUG(3,("unlink_internals: succesful unlink [%s]\n",fname));
+ }
+ CloseDir(dirptr);
+ }
+ }
+
+ if (count == 0 && NT_STATUS_IS_OK(error)) {
+ error = map_nt_error_from_unix(errno);
+ }
+
+ return error;
+}
+
+/****************************************************************************
+ Reply to a unlink
+****************************************************************************/
+
+int reply_unlink(connection_struct *conn, char *inbuf,char *outbuf, int dum_size,
+ int dum_buffsize)
+{
+ int outsize = 0;
+ pstring name;
+ int dirtype;
+ NTSTATUS status;
+ START_PROFILE(SMBunlink);
+
+ dirtype = SVAL(inbuf,smb_vwv0);
+
+ srvstr_get_path(inbuf, name, smb_buf(inbuf) + 1, sizeof(name), 0, STR_TERMINATE, &status);
+ if (!NT_STATUS_IS_OK(status)) {
+ END_PROFILE(SMBunlink);
+ return ERROR_NT(status);
+ }
+
+ RESOLVE_DFSPATH(name, conn, inbuf, outbuf);
+
+ DEBUG(3,("reply_unlink : %s\n",name));
+
+ status = unlink_internals(conn, dirtype, name);
+ if (!NT_STATUS_IS_OK(status))
+ return ERROR_NT(status);
+
+ /*
+ * Win2k needs a changenotify request response before it will
+ * update after a rename..
+ */
+ process_pending_change_notify_queue((time_t)0);
+
+ outsize = set_message(outbuf,0,0,True);
+
+ END_PROFILE(SMBunlink);
+ return outsize;
+}
+
+/****************************************************************************
+ Fail for readbraw.
+****************************************************************************/
+
+void fail_readraw(void)
+{
+ pstring errstr;
+ slprintf(errstr, sizeof(errstr)-1, "FAIL ! reply_readbraw: socket write fail (%s)",
+ strerror(errno) );
+ exit_server(errstr);
+}
+
+/****************************************************************************
+ Use sendfile in readbraw.
+****************************************************************************/
+
+void send_file_readbraw(connection_struct *conn, files_struct *fsp, SMB_OFF_T startpos, size_t nread,
+ ssize_t mincount, char *outbuf)
+{
+ ssize_t ret=0;
+
+#if defined(WITH_SENDFILE)
+ /*
+ * We can only use sendfile on a non-chained packet and on a file
+ * that is exclusively oplocked. reply_readbraw has already checked the length.
+ */
+
+ if ((nread > 0) && (lp_write_cache_size(SNUM(conn)) == 0) &&
+ EXCLUSIVE_OPLOCK_TYPE(fsp->oplock_type) && lp_use_sendfile(SNUM(conn)) ) {
+ DATA_BLOB header;
+
+ _smb_setlen(outbuf,nread);
+ header.data = outbuf;
+ header.length = 4;
+ header.free = NULL;
+
+ if ( SMB_VFS_SENDFILE( smbd_server_fd(), fsp, fsp->fd, &header, startpos, nread) == -1) {
+ /*
+ * Special hack for broken Linux with no 64 bit clean sendfile. If we
+ * return ENOSYS then pretend we just got a normal read.
+ */
+ if (errno == ENOSYS)
+ goto normal_read;
+
+ DEBUG(0,("send_file_readbraw: sendfile failed for file %s (%s). Terminating\n",
+ fsp->fsp_name, strerror(errno) ));
+ exit_server("send_file_readbraw sendfile failed");
+ }
+
+ }
+
+ normal_read:
+#endif
+
+ if (nread > 0) {
+ ret = read_file(fsp,outbuf+4,startpos,nread);
+#if 0 /* mincount appears to be ignored in a W2K server. JRA. */
+ if (ret < mincount)
+ ret = 0;
+#else
+ if (ret < nread)
+ ret = 0;
+#endif
+ }
+
+ _smb_setlen(outbuf,ret);
+ if (write_data(smbd_server_fd(),outbuf,4+ret) != 4+ret)
+ fail_readraw();
+}
+
+/****************************************************************************
+ Reply to a readbraw (core+ protocol).
+****************************************************************************/
+
+int reply_readbraw(connection_struct *conn, char *inbuf, char *outbuf, int dum_size, int dum_buffsize)
+{
+ extern struct current_user current_user;
+ ssize_t maxcount,mincount;
+ size_t nread = 0;
+ SMB_OFF_T startpos;
+ char *header = outbuf;
+ files_struct *fsp;
+ START_PROFILE(SMBreadbraw);
+
+ if (srv_is_signing_active()) {
+ exit_server("reply_readbraw: SMB signing is active - raw reads/writes are disallowed.");
+ }
+
+ /*
+ * Special check if an oplock break has been issued
+ * and the readraw request croses on the wire, we must
+ * return a zero length response here.
+ */
+
+ if(global_oplock_break) {
+ _smb_setlen(header,0);
+ if (write_data(smbd_server_fd(),header,4) != 4)
+ fail_readraw();
+ DEBUG(5,("readbraw - oplock break finished\n"));
+ END_PROFILE(SMBreadbraw);
+ return -1;
+ }
+
+ fsp = file_fsp(inbuf,smb_vwv0);
+
+ if (!FNUM_OK(fsp,conn) || !fsp->can_read) {
+ /*
+ * fsp could be NULL here so use the value from the packet. JRA.
+ */
+ DEBUG(3,("fnum %d not open in readbraw - cache prime?\n",(int)SVAL(inbuf,smb_vwv0)));
+ _smb_setlen(header,0);
+ if (write_data(smbd_server_fd(),header,4) != 4)
+ fail_readraw();
+ END_PROFILE(SMBreadbraw);
+ return(-1);
+ }
+
+ CHECK_FSP(fsp,conn);
+
+ flush_write_cache(fsp, READRAW_FLUSH);
+
+ startpos = IVAL_TO_SMB_OFF_T(inbuf,smb_vwv1);
+ if(CVAL(inbuf,smb_wct) == 10) {
+ /*
+ * This is a large offset (64 bit) read.
+ */
+#ifdef LARGE_SMB_OFF_T
+
+ startpos |= (((SMB_OFF_T)IVAL(inbuf,smb_vwv8)) << 32);
+
+#else /* !LARGE_SMB_OFF_T */
+
+ /*
+ * Ensure we haven't been sent a >32 bit offset.
+ */
+
+ if(IVAL(inbuf,smb_vwv8) != 0) {
+ DEBUG(0,("readbraw - large offset (%x << 32) used and we don't support \
+64 bit offsets.\n", (unsigned int)IVAL(inbuf,smb_vwv8) ));
+ _smb_setlen(header,0);
+ if (write_data(smbd_server_fd(),header,4) != 4)
+ fail_readraw();
+ END_PROFILE(SMBreadbraw);
+ return(-1);
+ }
+
+#endif /* LARGE_SMB_OFF_T */
+
+ if(startpos < 0) {
+ DEBUG(0,("readbraw - negative 64 bit readraw offset (%.0f) !\n", (double)startpos ));
+ _smb_setlen(header,0);
+ if (write_data(smbd_server_fd(),header,4) != 4)
+ fail_readraw();
+ END_PROFILE(SMBreadbraw);
+ return(-1);
+ }
+ }
+ maxcount = (SVAL(inbuf,smb_vwv3) & 0xFFFF);
+ mincount = (SVAL(inbuf,smb_vwv4) & 0xFFFF);
+
+ /* ensure we don't overrun the packet size */
+ maxcount = MIN(65535,maxcount);
+
+ if (!is_locked(fsp,conn,(SMB_BIG_UINT)maxcount,(SMB_BIG_UINT)startpos, READ_LOCK,False)) {
+ SMB_OFF_T size = fsp->size;
+ SMB_OFF_T sizeneeded = startpos + maxcount;
+
+ if (size < sizeneeded) {
+ SMB_STRUCT_STAT st;
+ if (SMB_VFS_FSTAT(fsp,fsp->fd,&st) == 0)
+ size = st.st_size;
+ if (!fsp->can_write)
+ fsp->size = size;
+ }
+
+ if (startpos >= size)
+ nread = 0;
+ else
+ nread = MIN(maxcount,(size - startpos));
+ }
+
+#if 0 /* mincount appears to be ignored in a W2K server. JRA. */
+ if (nread < mincount)
+ nread = 0;
+#endif
+
+ DEBUG( 3, ( "readbraw fnum=%d start=%.0f max=%d min=%d nread=%d\n", fsp->fnum, (double)startpos,
+ (int)maxcount, (int)mincount, (int)nread ) );
+
+ send_file_readbraw(conn, fsp, startpos, nread, mincount, outbuf);
+
+ DEBUG(5,("readbraw finished\n"));
+ END_PROFILE(SMBreadbraw);
+ return -1;
+}
+
+/****************************************************************************
+ Reply to a lockread (core+ protocol).
+****************************************************************************/
+
+int reply_lockread(connection_struct *conn, char *inbuf,char *outbuf, int length, int dum_buffsiz)
+{
+ ssize_t nread = -1;
+ char *data;
+ int outsize = 0;
+ SMB_OFF_T startpos;
+ size_t numtoread;
+ NTSTATUS status;
+ files_struct *fsp = file_fsp(inbuf,smb_vwv0);
+ BOOL my_lock_ctx = False;
+ START_PROFILE(SMBlockread);
+
+ CHECK_FSP(fsp,conn);
+ CHECK_READ(fsp);
+
+ release_level_2_oplocks_on_change(fsp);
+
+ numtoread = SVAL(inbuf,smb_vwv1);
+ startpos = IVAL_TO_SMB_OFF_T(inbuf,smb_vwv2);
+
+ outsize = set_message(outbuf,5,3,True);
+ numtoread = MIN(BUFFER_SIZE-outsize,numtoread);
+ data = smb_buf(outbuf) + 3;
+
+ /*
+ * NB. Discovered by Menny Hamburger at Mainsoft. This is a core+
+ * protocol request that predates the read/write lock concept.
+ * Thus instead of asking for a read lock here we need to ask
+ * for a write lock. JRA.
+ * Note that the requested lock size is unaffected by max_recv.
+ */
+
+ status = do_lock_spin(fsp, conn, SVAL(inbuf,smb_pid),
+ (SMB_BIG_UINT)numtoread, (SMB_BIG_UINT)startpos, WRITE_LOCK, &my_lock_ctx);
+
+ if (NT_STATUS_V(status)) {
+#if 0
+ /*
+ * We used to make lockread a blocking lock. It turns out
+ * that this isn't on W2k. Found by the Samba 4 RAW-READ torture
+ * tester. JRA.
+ */
+
+ if (lp_blocking_locks(SNUM(conn)) && !my_lock_ctx && ERROR_WAS_LOCK_DENIED(status)) {
+ /*
+ * A blocking lock was requested. Package up
+ * this smb into a queued request and push it
+ * onto the blocking lock queue.
+ */
+ if(push_blocking_lock_request(inbuf, length, -1, 0, SVAL(inbuf,smb_pid), (SMB_BIG_UINT)startpos,
+ (SMB_BIG_UINT)numtoread)) {
+ END_PROFILE(SMBlockread);
+ return -1;
+ }
+ }
+#endif
+ END_PROFILE(SMBlockread);
+ return ERROR_NT(status);
+ }
+
+ /*
+ * However the requested READ size IS affected by max_recv. Insanity.... JRA.
+ */
+
+ if (numtoread > max_recv) {
+ DEBUG(0,("reply_lockread: requested read size (%u) is greater than maximum allowed (%u). \
+Returning short read of maximum allowed for compatibility with Windows 2000.\n",
+ (unsigned int)numtoread, (unsigned int)max_recv ));
+ numtoread = MIN(numtoread,max_recv);
+ }
+ nread = read_file(fsp,data,startpos,numtoread);
+
+ if (nread < 0) {
+ END_PROFILE(SMBlockread);
+ return(UNIXERROR(ERRDOS,ERRnoaccess));
+ }
+
+ outsize += nread;
+ SSVAL(outbuf,smb_vwv0,nread);
+ SSVAL(outbuf,smb_vwv5,nread+3);
+ SSVAL(smb_buf(outbuf),1,nread);
+
+ DEBUG(3,("lockread fnum=%d num=%d nread=%d\n",
+ fsp->fnum, (int)numtoread, (int)nread));
+
+ END_PROFILE(SMBlockread);
+ return(outsize);
+}
+
+/****************************************************************************
+ Reply to a read.
+****************************************************************************/
+
+int reply_read(connection_struct *conn, char *inbuf,char *outbuf, int size, int dum_buffsize)
+{
+ size_t numtoread;
+ ssize_t nread = 0;
+ char *data;
+ SMB_OFF_T startpos;
+ int outsize = 0;
+ files_struct *fsp = file_fsp(inbuf,smb_vwv0);
+ START_PROFILE(SMBread);
+
+ CHECK_FSP(fsp,conn);
+ CHECK_READ(fsp);
+
+ numtoread = SVAL(inbuf,smb_vwv1);
+ startpos = IVAL_TO_SMB_OFF_T(inbuf,smb_vwv2);
+
+ outsize = set_message(outbuf,5,3,True);
+ numtoread = MIN(BUFFER_SIZE-outsize,numtoread);
+ /*
+ * The requested read size cannot be greater than max_recv. JRA.
+ */
+ if (numtoread > max_recv) {
+ DEBUG(0,("reply_read: requested read size (%u) is greater than maximum allowed (%u). \
+Returning short read of maximum allowed for compatibility with Windows 2000.\n",
+ (unsigned int)numtoread, (unsigned int)max_recv ));
+ numtoread = MIN(numtoread,max_recv);
+ }
+
+ data = smb_buf(outbuf) + 3;
+
+ if (is_locked(fsp,conn,(SMB_BIG_UINT)numtoread,(SMB_BIG_UINT)startpos, READ_LOCK,False)) {
+ END_PROFILE(SMBread);
+ return ERROR_DOS(ERRDOS,ERRlock);
+ }
+
+ if (numtoread > 0)
+ nread = read_file(fsp,data,startpos,numtoread);
+
+ if (nread < 0) {
+ END_PROFILE(SMBread);
+ return(UNIXERROR(ERRDOS,ERRnoaccess));
+ }
+
+ outsize += nread;
+ SSVAL(outbuf,smb_vwv0,nread);
+ SSVAL(outbuf,smb_vwv5,nread+3);
+ SCVAL(smb_buf(outbuf),0,1);
+ SSVAL(smb_buf(outbuf),1,nread);
+
+ DEBUG( 3, ( "read fnum=%d num=%d nread=%d\n",
+ fsp->fnum, (int)numtoread, (int)nread ) );
+
+ END_PROFILE(SMBread);
+ return(outsize);
+}
+
+/****************************************************************************
+ Reply to a read and X - possibly using sendfile.
+****************************************************************************/
+
+int send_file_readX(connection_struct *conn, char *inbuf,char *outbuf,int length,
+ files_struct *fsp, SMB_OFF_T startpos, size_t smb_maxcnt)
+{
+ ssize_t nread = -1;
+ char *data = smb_buf(outbuf);
+
+#if defined(WITH_SENDFILE)
+ /*
+ * We can only use sendfile on a non-chained packet and on a file
+ * that is exclusively oplocked.
+ */
+
+ if ((CVAL(inbuf,smb_vwv0) == 0xFF) && EXCLUSIVE_OPLOCK_TYPE(fsp->oplock_type) &&
+ lp_use_sendfile(SNUM(conn)) && (lp_write_cache_size(SNUM(conn)) == 0) ) {
+ SMB_STRUCT_STAT sbuf;
+ DATA_BLOB header;
+
+ if(SMB_VFS_FSTAT(fsp,fsp->fd, &sbuf) == -1)
+ return(UNIXERROR(ERRDOS,ERRnoaccess));
+
+ if (startpos > sbuf.st_size)
+ goto normal_read;
+
+ if (smb_maxcnt > (sbuf.st_size - startpos))
+ smb_maxcnt = (sbuf.st_size - startpos);
+
+ if (smb_maxcnt == 0)
+ goto normal_read;
+
+ /*
+ * Set up the packet header before send. We
+ * assume here the sendfile will work (get the
+ * correct amount of data).
+ */
+
+ SSVAL(outbuf,smb_vwv2,0xFFFF); /* Remaining - must be -1. */
+ SSVAL(outbuf,smb_vwv5,smb_maxcnt);
+ SSVAL(outbuf,smb_vwv6,smb_offset(data,outbuf));
+ SSVAL(smb_buf(outbuf),-2,smb_maxcnt);
+ SCVAL(outbuf,smb_vwv0,0xFF);
+ set_message(outbuf,12,smb_maxcnt,False);
+ header.data = outbuf;
+ header.length = data - outbuf;
+ header.free = NULL;
+
+ if ( SMB_VFS_SENDFILE( smbd_server_fd(), fsp, fsp->fd, &header, startpos, smb_maxcnt) == -1) {
+ /*
+ * Special hack for broken Linux with no 64 bit clean sendfile. If we
+ * return ENOSYS then pretend we just got a normal read.
+ */
+ if (errno == ENOSYS)
+ goto normal_read;
+
+ DEBUG(0,("send_file_readX: sendfile failed for file %s (%s). Terminating\n",
+ fsp->fsp_name, strerror(errno) ));
+ exit_server("send_file_readX sendfile failed");
+ }
+
+ DEBUG( 3, ( "send_file_readX: sendfile fnum=%d max=%d nread=%d\n",
+ fsp->fnum, (int)smb_maxcnt, (int)nread ) );
+ return -1;
+ }
+
+ normal_read:
+
+#endif
+
+ nread = read_file(fsp,data,startpos,smb_maxcnt);
+
+ if (nread < 0) {
+ END_PROFILE(SMBreadX);
+ return(UNIXERROR(ERRDOS,ERRnoaccess));
+ }
+
+ SSVAL(outbuf,smb_vwv2,0xFFFF); /* Remaining - must be -1. */
+ SSVAL(outbuf,smb_vwv5,nread);
+ SSVAL(outbuf,smb_vwv6,smb_offset(data,outbuf));
+ SSVAL(smb_buf(outbuf),-2,nread);
+
+ DEBUG( 3, ( "send_file_readX fnum=%d max=%d nread=%d\n",
+ fsp->fnum, (int)smb_maxcnt, (int)nread ) );
+
+ return nread;
+}
+
+/****************************************************************************
+ Reply to a read and X.
+****************************************************************************/
+
+int reply_read_and_X(connection_struct *conn, char *inbuf,char *outbuf,int length,int bufsize)
+{
+ files_struct *fsp = file_fsp(inbuf,smb_vwv2);
+ SMB_OFF_T startpos = IVAL_TO_SMB_OFF_T(inbuf,smb_vwv3);
+ ssize_t nread = -1;
+ size_t smb_maxcnt = SVAL(inbuf,smb_vwv5);
+#if 0
+ size_t smb_mincnt = SVAL(inbuf,smb_vwv6);
+#endif
+
+ START_PROFILE(SMBreadX);
+
+ /* If it's an IPC, pass off the pipe handler. */
+ if (IS_IPC(conn)) {
+ END_PROFILE(SMBreadX);
+ return reply_pipe_read_and_X(inbuf,outbuf,length,bufsize);
+ }
+
+ CHECK_FSP(fsp,conn);
+ CHECK_READ(fsp);
+
+ set_message(outbuf,12,0,True);
+
+ if(CVAL(inbuf,smb_wct) == 12) {
+#ifdef LARGE_SMB_OFF_T
+ /*
+ * This is a large offset (64 bit) read.
+ */
+ startpos |= (((SMB_OFF_T)IVAL(inbuf,smb_vwv10)) << 32);
+
+#else /* !LARGE_SMB_OFF_T */
+
+ /*
+ * Ensure we haven't been sent a >32 bit offset.
+ */
+
+ if(IVAL(inbuf,smb_vwv10) != 0) {
+ DEBUG(0,("reply_read_and_X - large offset (%x << 32) used and we don't support \
+64 bit offsets.\n", (unsigned int)IVAL(inbuf,smb_vwv10) ));
+ END_PROFILE(SMBreadX);
+ return ERROR_DOS(ERRDOS,ERRbadaccess);
+ }
+
+#endif /* LARGE_SMB_OFF_T */
+
+ }
+
+ if (is_locked(fsp,conn,(SMB_BIG_UINT)smb_maxcnt,(SMB_BIG_UINT)startpos, READ_LOCK,False)) {
+ END_PROFILE(SMBreadX);
+ return ERROR_DOS(ERRDOS,ERRlock);
+ }
+
+ nread = send_file_readX(conn, inbuf, outbuf, length, fsp, startpos, smb_maxcnt);
+ if (nread != -1)
+ nread = chain_reply(inbuf,outbuf,length,bufsize);
+
+ END_PROFILE(SMBreadX);
+ return nread;
+}
+
+/****************************************************************************
+ Reply to a writebraw (core+ or LANMAN1.0 protocol).
+****************************************************************************/
+
+int reply_writebraw(connection_struct *conn, char *inbuf,char *outbuf, int size, int dum_buffsize)
+{
+ ssize_t nwritten=0;
+ ssize_t total_written=0;
+ size_t numtowrite=0;
+ size_t tcount;
+ SMB_OFF_T startpos;
+ char *data=NULL;
+ BOOL write_through;
+ files_struct *fsp = file_fsp(inbuf,smb_vwv0);
+ int outsize = 0;
+ START_PROFILE(SMBwritebraw);
+
+ if (srv_is_signing_active()) {
+ exit_server("reply_writebraw: SMB signing is active - raw reads/writes are disallowed.");
+ }
+
+ CHECK_FSP(fsp,conn);
+ CHECK_WRITE(fsp);
+
+ tcount = IVAL(inbuf,smb_vwv1);
+ startpos = IVAL_TO_SMB_OFF_T(inbuf,smb_vwv3);
+ write_through = BITSETW(inbuf+smb_vwv7,0);
+
+ /* We have to deal with slightly different formats depending
+ on whether we are using the core+ or lanman1.0 protocol */
+
+ if(Protocol <= PROTOCOL_COREPLUS) {
+ numtowrite = SVAL(smb_buf(inbuf),-2);
+ data = smb_buf(inbuf);
+ } else {
+ numtowrite = SVAL(inbuf,smb_vwv10);
+ data = smb_base(inbuf) + SVAL(inbuf, smb_vwv11);
+ }
+
+ /* force the error type */
+ SCVAL(inbuf,smb_com,SMBwritec);
+ SCVAL(outbuf,smb_com,SMBwritec);
+
+ if (is_locked(fsp,conn,(SMB_BIG_UINT)tcount,(SMB_BIG_UINT)startpos, WRITE_LOCK,False)) {
+ END_PROFILE(SMBwritebraw);
+ return(ERROR_DOS(ERRDOS,ERRlock));
+ }
+
+ if (numtowrite>0)
+ nwritten = write_file(fsp,data,startpos,numtowrite);
+
+ DEBUG(3,("writebraw1 fnum=%d start=%.0f num=%d wrote=%d sync=%d\n",
+ fsp->fnum, (double)startpos, (int)numtowrite, (int)nwritten, (int)write_through));
+
+ if (nwritten < (ssize_t)numtowrite) {
+ END_PROFILE(SMBwritebraw);
+ return(UNIXERROR(ERRHRD,ERRdiskfull));
+ }
+
+ total_written = nwritten;
+
+ /* Return a message to the redirector to tell it to send more bytes */
+ SCVAL(outbuf,smb_com,SMBwritebraw);
+ SSVALS(outbuf,smb_vwv0,-1);
+ outsize = set_message(outbuf,Protocol>PROTOCOL_COREPLUS?1:0,0,True);
+ if (!send_smb(smbd_server_fd(),outbuf))
+ exit_server("reply_writebraw: send_smb failed.");
+
+ /* Now read the raw data into the buffer and write it */
+ if (read_smb_length(smbd_server_fd(),inbuf,SMB_SECONDARY_WAIT) == -1) {
+ exit_server("secondary writebraw failed");
+ }
+
+ /* Even though this is not an smb message, smb_len returns the generic length of an smb message */
+ numtowrite = smb_len(inbuf);
+
+ /* Set up outbuf to return the correct return */
+ outsize = set_message(outbuf,1,0,True);
+ SCVAL(outbuf,smb_com,SMBwritec);
+ SSVAL(outbuf,smb_vwv0,total_written);
+
+ if (numtowrite != 0) {
+
+ if (numtowrite > BUFFER_SIZE) {
+ DEBUG(0,("reply_writebraw: Oversize secondary write raw requested (%u). Terminating\n",
+ (unsigned int)numtowrite ));
+ exit_server("secondary writebraw failed");
+ }
+
+ if (tcount > nwritten+numtowrite) {
+ DEBUG(3,("Client overestimated the write %d %d %d\n",
+ (int)tcount,(int)nwritten,(int)numtowrite));
+ }
+
+ if (read_data( smbd_server_fd(), inbuf+4, numtowrite) != numtowrite ) {
+ DEBUG(0,("reply_writebraw: Oversize secondary write raw read failed (%s). Terminating\n",
+ strerror(errno) ));
+ exit_server("secondary writebraw failed");
+ }
+
+ nwritten = write_file(fsp,inbuf+4,startpos+nwritten,numtowrite);
+
+ if (nwritten < (ssize_t)numtowrite) {
+ SCVAL(outbuf,smb_rcls,ERRHRD);
+ SSVAL(outbuf,smb_err,ERRdiskfull);
+ }
+
+ if (nwritten > 0)
+ total_written += nwritten;
+ }
+
+ if ((lp_syncalways(SNUM(conn)) || write_through) && lp_strict_sync(SNUM(conn)))
+ sync_file(conn,fsp);
+
+ DEBUG(3,("writebraw2 fnum=%d start=%.0f num=%d wrote=%d\n",
+ fsp->fnum, (double)startpos, (int)numtowrite,(int)total_written));
+
+ /* we won't return a status if write through is not selected - this follows what WfWg does */
+ END_PROFILE(SMBwritebraw);
+ if (!write_through && total_written==tcount) {
+
+#if RABBIT_PELLET_FIX
+ /*
+ * Fix for "rabbit pellet" mode, trigger an early TCP ack by
+ * sending a SMBkeepalive. Thanks to DaveCB at Sun for this. JRA.
+ */
+ if (!send_keepalive(smbd_server_fd()))
+ exit_server("reply_writebraw: send of keepalive failed");
+#endif
+ return(-1);
+ }
+
+ return(outsize);
+}
+
+/****************************************************************************
+ Reply to a writeunlock (core+).
+****************************************************************************/
+
+int reply_writeunlock(connection_struct *conn, char *inbuf,char *outbuf,
+ int size, int dum_buffsize)
+{
+ ssize_t nwritten = -1;
+ size_t numtowrite;
+ SMB_OFF_T startpos;
+ char *data;
+ NTSTATUS status = NT_STATUS_OK;
+ files_struct *fsp = file_fsp(inbuf,smb_vwv0);
+ int outsize = 0;
+ START_PROFILE(SMBwriteunlock);
+
+ CHECK_FSP(fsp,conn);
+ CHECK_WRITE(fsp);
+
+ numtowrite = SVAL(inbuf,smb_vwv1);
+ startpos = IVAL_TO_SMB_OFF_T(inbuf,smb_vwv2);
+ data = smb_buf(inbuf) + 3;
+
+ if (numtowrite && is_locked(fsp,conn,(SMB_BIG_UINT)numtowrite,(SMB_BIG_UINT)startpos,
+ WRITE_LOCK,False)) {
+ END_PROFILE(SMBwriteunlock);
+ return ERROR_DOS(ERRDOS,ERRlock);
+ }
+
+ /* The special X/Open SMB protocol handling of
+ zero length writes is *NOT* done for
+ this call */
+ if(numtowrite == 0)
+ nwritten = 0;
+ else
+ nwritten = write_file(fsp,data,startpos,numtowrite);
+
+ if (lp_syncalways(SNUM(conn)))
+ sync_file(conn,fsp);
+
+ if(((nwritten == 0) && (numtowrite != 0))||(nwritten < 0)) {
+ END_PROFILE(SMBwriteunlock);
+ return(UNIXERROR(ERRHRD,ERRdiskfull));
+ }
+
+ if (numtowrite) {
+ status = do_unlock(fsp, conn, SVAL(inbuf,smb_pid), (SMB_BIG_UINT)numtowrite,
+ (SMB_BIG_UINT)startpos);
+ if (NT_STATUS_V(status)) {
+ END_PROFILE(SMBwriteunlock);
+ return ERROR_NT(status);
+ }
+ }
+
+ outsize = set_message(outbuf,1,0,True);
+
+ SSVAL(outbuf,smb_vwv0,nwritten);
+
+ DEBUG(3,("writeunlock fnum=%d num=%d wrote=%d\n",
+ fsp->fnum, (int)numtowrite, (int)nwritten));
+
+ END_PROFILE(SMBwriteunlock);
+ return outsize;
+}
+
+/****************************************************************************
+ Reply to a write.
+****************************************************************************/
+
+int reply_write(connection_struct *conn, char *inbuf,char *outbuf,int size,int dum_buffsize)
+{
+ size_t numtowrite;
+ ssize_t nwritten = -1;
+ SMB_OFF_T startpos;
+ char *data;
+ files_struct *fsp = file_fsp(inbuf,smb_vwv0);
+ int outsize = 0;
+ START_PROFILE(SMBwrite);
+
+ /* If it's an IPC, pass off the pipe handler. */
+ if (IS_IPC(conn)) {
+ END_PROFILE(SMBwrite);
+ return reply_pipe_write(inbuf,outbuf,size,dum_buffsize);
+ }
+
+ CHECK_FSP(fsp,conn);
+ CHECK_WRITE(fsp);
+
+ numtowrite = SVAL(inbuf,smb_vwv1);
+ startpos = IVAL_TO_SMB_OFF_T(inbuf,smb_vwv2);
+ data = smb_buf(inbuf) + 3;
+
+ if (is_locked(fsp,conn,(SMB_BIG_UINT)numtowrite,(SMB_BIG_UINT)startpos, WRITE_LOCK,False)) {
+ END_PROFILE(SMBwrite);
+ return ERROR_DOS(ERRDOS,ERRlock);
+ }
+
+ /*
+ * X/Open SMB protocol says that if smb_vwv1 is
+ * zero then the file size should be extended or
+ * truncated to the size given in smb_vwv[2-3].
+ */
+
+ if(numtowrite == 0) {
+ /*
+ * This is actually an allocate call, and set EOF. JRA.
+ */
+ nwritten = vfs_allocate_file_space(fsp, (SMB_OFF_T)startpos);
+ if (nwritten < 0) {
+ END_PROFILE(SMBwrite);
+ return ERROR_NT(NT_STATUS_DISK_FULL);
+ }
+ nwritten = vfs_set_filelen(fsp, (SMB_OFF_T)startpos);
+ if (nwritten < 0) {
+ END_PROFILE(SMBwrite);
+ return ERROR_NT(NT_STATUS_DISK_FULL);
+ }
+ } else
+ nwritten = write_file(fsp,data,startpos,numtowrite);
+
+ if (lp_syncalways(SNUM(conn)))
+ sync_file(conn,fsp);
+
+ if(((nwritten == 0) && (numtowrite != 0))||(nwritten < 0)) {
+ END_PROFILE(SMBwrite);
+ return(UNIXERROR(ERRHRD,ERRdiskfull));
+ }
+
+ outsize = set_message(outbuf,1,0,True);
+
+ SSVAL(outbuf,smb_vwv0,nwritten);
+
+ if (nwritten < (ssize_t)numtowrite) {
+ SCVAL(outbuf,smb_rcls,ERRHRD);
+ SSVAL(outbuf,smb_err,ERRdiskfull);
+ }
+
+ DEBUG(3,("write fnum=%d num=%d wrote=%d\n", fsp->fnum, (int)numtowrite, (int)nwritten));
+
+ END_PROFILE(SMBwrite);
+ return(outsize);
+}
+
+/****************************************************************************
+ Reply to a write and X.
+****************************************************************************/
+
+int reply_write_and_X(connection_struct *conn, char *inbuf,char *outbuf,int length,int bufsize)
+{
+ files_struct *fsp = file_fsp(inbuf,smb_vwv2);
+ SMB_OFF_T startpos = IVAL_TO_SMB_OFF_T(inbuf,smb_vwv3);
+ size_t numtowrite = SVAL(inbuf,smb_vwv10);
+ BOOL write_through = BITSETW(inbuf+smb_vwv7,0);
+ ssize_t nwritten = -1;
+ unsigned int smb_doff = SVAL(inbuf,smb_vwv11);
+ unsigned int smblen = smb_len(inbuf);
+ char *data;
+ BOOL large_writeX = ((CVAL(inbuf,smb_wct) == 14) && (smblen > 0xFFFF));
+ START_PROFILE(SMBwriteX);
+
+ /* If it's an IPC, pass off the pipe handler. */
+ if (IS_IPC(conn)) {
+ END_PROFILE(SMBwriteX);
+ return reply_pipe_write_and_X(inbuf,outbuf,length,bufsize);
+ }
+
+ CHECK_FSP(fsp,conn);
+ CHECK_WRITE(fsp);
+
+ /* Deal with possible LARGE_WRITEX */
+ if (large_writeX)
+ numtowrite |= ((((size_t)SVAL(inbuf,smb_vwv9)) & 1 )<<16);
+
+ if(smb_doff > smblen || (smb_doff + numtowrite > smblen)) {
+ END_PROFILE(SMBwriteX);
+ return ERROR_DOS(ERRDOS,ERRbadmem);
+ }
+
+ data = smb_base(inbuf) + smb_doff;
+
+ if(CVAL(inbuf,smb_wct) == 14) {
+#ifdef LARGE_SMB_OFF_T
+ /*
+ * This is a large offset (64 bit) write.
+ */
+ startpos |= (((SMB_OFF_T)IVAL(inbuf,smb_vwv12)) << 32);
+
+#else /* !LARGE_SMB_OFF_T */
+
+ /*
+ * Ensure we haven't been sent a >32 bit offset.
+ */
+
+ if(IVAL(inbuf,smb_vwv12) != 0) {
+ DEBUG(0,("reply_write_and_X - large offset (%x << 32) used and we don't support \
+64 bit offsets.\n", (unsigned int)IVAL(inbuf,smb_vwv12) ));
+ END_PROFILE(SMBwriteX);
+ return ERROR_DOS(ERRDOS,ERRbadaccess);
+ }
+
+#endif /* LARGE_SMB_OFF_T */
+ }
+
+ if (is_locked(fsp,conn,(SMB_BIG_UINT)numtowrite,(SMB_BIG_UINT)startpos, WRITE_LOCK,False)) {
+ END_PROFILE(SMBwriteX);
+ return ERROR_DOS(ERRDOS,ERRlock);
+ }
+
+ /* X/Open SMB protocol says that, unlike SMBwrite
+ if the length is zero then NO truncation is
+ done, just a write of zero. To truncate a file,
+ use SMBwrite. */
+
+ if(numtowrite == 0)
+ nwritten = 0;
+ else
+ nwritten = write_file(fsp,data,startpos,numtowrite);
+
+ if(((nwritten == 0) && (numtowrite != 0))||(nwritten < 0)) {
+ END_PROFILE(SMBwriteX);
+ return(UNIXERROR(ERRHRD,ERRdiskfull));
+ }
+
+ set_message(outbuf,6,0,True);
+
+ SSVAL(outbuf,smb_vwv2,nwritten);
+ if (large_writeX)
+ SSVAL(outbuf,smb_vwv4,(nwritten>>16)&1);
+
+ if (nwritten < (ssize_t)numtowrite) {
+ SCVAL(outbuf,smb_rcls,ERRHRD);
+ SSVAL(outbuf,smb_err,ERRdiskfull);
+ }
+
+ DEBUG(3,("writeX fnum=%d num=%d wrote=%d\n",
+ fsp->fnum, (int)numtowrite, (int)nwritten));
+
+ if (lp_syncalways(SNUM(conn)) || write_through)
+ sync_file(conn,fsp);
+
+ END_PROFILE(SMBwriteX);
+ return chain_reply(inbuf,outbuf,length,bufsize);
+}
+
+/****************************************************************************
+ Reply to a lseek.
+****************************************************************************/
+
+int reply_lseek(connection_struct *conn, char *inbuf,char *outbuf, int size, int dum_buffsize)
+{
+ SMB_OFF_T startpos;
+ SMB_OFF_T res= -1;
+ int mode,umode;
+ int outsize = 0;
+ files_struct *fsp = file_fsp(inbuf,smb_vwv0);
+ START_PROFILE(SMBlseek);
+
+ CHECK_FSP(fsp,conn);
+
+ flush_write_cache(fsp, SEEK_FLUSH);
+
+ mode = SVAL(inbuf,smb_vwv1) & 3;
+ /* NB. This doesn't use IVAL_TO_SMB_OFF_T as startpos can be signed in this case. */
+ startpos = (SMB_OFF_T)IVALS(inbuf,smb_vwv2);
+
+ switch (mode) {
+ case 0:
+ umode = SEEK_SET;
+ res = startpos;
+ break;
+ case 1:
+ umode = SEEK_CUR;
+ res = fsp->pos + startpos;
+ break;
+ case 2:
+ umode = SEEK_END;
+ break;
+ default:
+ umode = SEEK_SET;
+ res = startpos;
+ break;
+ }
+
+ if (umode == SEEK_END) {
+ if((res = SMB_VFS_LSEEK(fsp,fsp->fd,startpos,umode)) == -1) {
+ if(errno == EINVAL) {
+ SMB_OFF_T current_pos = startpos;
+ SMB_STRUCT_STAT sbuf;
+
+ if(SMB_VFS_FSTAT(fsp,fsp->fd, &sbuf) == -1) {
+ END_PROFILE(SMBlseek);
+ return(UNIXERROR(ERRDOS,ERRnoaccess));
+ }
+
+ current_pos += sbuf.st_size;
+ if(current_pos < 0)
+ res = SMB_VFS_LSEEK(fsp,fsp->fd,0,SEEK_SET);
+ }
+ }
+
+ if(res == -1) {
+ END_PROFILE(SMBlseek);
+ return(UNIXERROR(ERRDOS,ERRnoaccess));
+ }
+ }
+
+ fsp->pos = res;
+
+ outsize = set_message(outbuf,2,0,True);
+ SIVAL(outbuf,smb_vwv0,res);
+
+ DEBUG(3,("lseek fnum=%d ofs=%.0f newpos = %.0f mode=%d\n",
+ fsp->fnum, (double)startpos, (double)res, mode));
+
+ END_PROFILE(SMBlseek);
+ return(outsize);
+}
+
+/****************************************************************************
+ Reply to a flush.
+****************************************************************************/
+
+int reply_flush(connection_struct *conn, char *inbuf,char *outbuf, int size, int dum_buffsize)
+{
+ int outsize = set_message(outbuf,0,0,True);
+ uint16 fnum = SVAL(inbuf,smb_vwv0);
+ files_struct *fsp = file_fsp(inbuf,smb_vwv0);
+ START_PROFILE(SMBflush);
+
+ if (fnum != 0xFFFF)
+ CHECK_FSP(fsp,conn);
+
+ if (!fsp) {
+ file_sync_all(conn);
+ } else {
+ sync_file(conn,fsp);
+ }
+
+ DEBUG(3,("flush\n"));
+ END_PROFILE(SMBflush);
+ return(outsize);
+}
+
+/****************************************************************************
+ Reply to a exit.
+****************************************************************************/
+
+int reply_exit(connection_struct *conn,
+ char *inbuf,char *outbuf, int dum_size, int dum_buffsize)
+{
+ int outsize;
+ START_PROFILE(SMBexit);
+
+ file_close_pid(SVAL(inbuf,smb_pid));
+
+ outsize = set_message(outbuf,0,0,True);
+
+ DEBUG(3,("exit\n"));
+
+ END_PROFILE(SMBexit);
+ return(outsize);
+}
+
+/****************************************************************************
+ Reply to a close - has to deal with closing a directory opened by NT SMB's.
+****************************************************************************/
+
+int reply_close(connection_struct *conn, char *inbuf,char *outbuf, int size,
+ int dum_buffsize)
+{
+ extern struct current_user current_user;
+ int outsize = 0;
+ time_t mtime;
+ int32 eclass = 0, err = 0;
+ files_struct *fsp = NULL;
+ START_PROFILE(SMBclose);
+
+ outsize = set_message(outbuf,0,0,True);
+
+ /* If it's an IPC, pass off to the pipe handler. */
+ if (IS_IPC(conn)) {
+ END_PROFILE(SMBclose);
+ return reply_pipe_close(conn, inbuf,outbuf);
+ }
+
+ fsp = file_fsp(inbuf,smb_vwv0);
+
+ /*
+ * We can only use CHECK_FSP if we know it's not a directory.
+ */
+
+ if(!fsp || (fsp->conn != conn) || (fsp->vuid != current_user.vuid)) {
+ END_PROFILE(SMBclose);
+ return ERROR_DOS(ERRDOS,ERRbadfid);
+ }
+
+ if(fsp->is_directory) {
+ /*
+ * Special case - close NT SMB directory handle.
+ */
+ DEBUG(3,("close %s fnum=%d\n", fsp->is_directory ? "directory" : "stat file open", fsp->fnum));
+ close_file(fsp,True);
+ } else {
+ /*
+ * Close ordinary file.
+ */
+ int close_err;
+ pstring file_name;
+
+ /* Save the name for time set in close. */
+ pstrcpy( file_name, fsp->fsp_name);
+
+ DEBUG(3,("close fd=%d fnum=%d (numopen=%d)\n",
+ fsp->fd, fsp->fnum,
+ conn->num_files_open));
+
+ /*
+ * close_file() returns the unix errno if an error
+ * was detected on close - normally this is due to
+ * a disk full error. If not then it was probably an I/O error.
+ */
+
+ if((close_err = close_file(fsp,True)) != 0) {
+ errno = close_err;
+ END_PROFILE(SMBclose);
+ return (UNIXERROR(ERRHRD,ERRgeneral));
+ }
+
+ /*
+ * Now take care of any time sent in the close.
+ */
+
+ mtime = make_unix_date3(inbuf+smb_vwv1);
+
+ /* try and set the date */
+ set_filetime(conn, file_name, mtime);
+
+ }
+
+ /* We have a cached error */
+ if(eclass || err) {
+ END_PROFILE(SMBclose);
+ return ERROR_DOS(eclass,err);
+ }
+
+ END_PROFILE(SMBclose);
+ return(outsize);
+}
+
+/****************************************************************************
+ Reply to a writeclose (Core+ protocol).
+****************************************************************************/
+
+int reply_writeclose(connection_struct *conn,
+ char *inbuf,char *outbuf, int size, int dum_buffsize)
+{
+ size_t numtowrite;
+ ssize_t nwritten = -1;
+ int outsize = 0;
+ int close_err = 0;
+ SMB_OFF_T startpos;
+ char *data;
+ time_t mtime;
+ files_struct *fsp = file_fsp(inbuf,smb_vwv0);
+ START_PROFILE(SMBwriteclose);
+
+ CHECK_FSP(fsp,conn);
+ CHECK_WRITE(fsp);
+
+ numtowrite = SVAL(inbuf,smb_vwv1);
+ startpos = IVAL_TO_SMB_OFF_T(inbuf,smb_vwv2);
+ mtime = make_unix_date3(inbuf+smb_vwv4);
+ data = smb_buf(inbuf) + 1;
+
+ if (numtowrite && is_locked(fsp,conn,(SMB_BIG_UINT)numtowrite,(SMB_BIG_UINT)startpos, WRITE_LOCK,False)) {
+ END_PROFILE(SMBwriteclose);
+ return ERROR_DOS(ERRDOS,ERRlock);
+ }
+
+ nwritten = write_file(fsp,data,startpos,numtowrite);
+
+ set_filetime(conn, fsp->fsp_name,mtime);
+
+ /*
+ * More insanity. W2K only closes the file if writelen > 0.
+ * JRA.
+ */
+
+ if (numtowrite) {
+ DEBUG(3,("reply_writeclose: zero length write doesn't close file %s\n",
+ fsp->fsp_name ));
+ close_err = close_file(fsp,True);
+ }
+
+ DEBUG(3,("writeclose fnum=%d num=%d wrote=%d (numopen=%d)\n",
+ fsp->fnum, (int)numtowrite, (int)nwritten,
+ conn->num_files_open));
+
+ if(((nwritten == 0) && (numtowrite != 0))||(nwritten < 0)) {
+ END_PROFILE(SMBwriteclose);
+ return(UNIXERROR(ERRHRD,ERRdiskfull));
+ }
+
+ if(close_err != 0) {
+ errno = close_err;
+ END_PROFILE(SMBwriteclose);
+ return(UNIXERROR(ERRHRD,ERRgeneral));
+ }
+
+ outsize = set_message(outbuf,1,0,True);
+
+ SSVAL(outbuf,smb_vwv0,nwritten);
+ END_PROFILE(SMBwriteclose);
+ return(outsize);
+}
+
+/****************************************************************************
+ Reply to a lock.
+****************************************************************************/
+
+int reply_lock(connection_struct *conn,
+ char *inbuf,char *outbuf, int length, int dum_buffsize)
+{
+ int outsize = set_message(outbuf,0,0,True);
+ SMB_BIG_UINT count,offset;
+ NTSTATUS status;
+ files_struct *fsp = file_fsp(inbuf,smb_vwv0);
+ BOOL my_lock_ctx = False;
+
+ START_PROFILE(SMBlock);
+
+ CHECK_FSP(fsp,conn);
+
+ release_level_2_oplocks_on_change(fsp);
+
+ count = (SMB_BIG_UINT)IVAL(inbuf,smb_vwv1);
+ offset = (SMB_BIG_UINT)IVAL(inbuf,smb_vwv3);
+
+ DEBUG(3,("lock fd=%d fnum=%d offset=%.0f count=%.0f\n",
+ fsp->fd, fsp->fnum, (double)offset, (double)count));
+
+ status = do_lock_spin(fsp, conn, SVAL(inbuf,smb_pid), count, offset, WRITE_LOCK, &my_lock_ctx);
+ if (NT_STATUS_V(status)) {
+#if 0
+ /* Tests using Samba4 against W2K show this call never creates a blocking lock. */
+ if (lp_blocking_locks(SNUM(conn)) && !my_lock_ctx && ERROR_WAS_LOCK_DENIED(status)) {
+ /*
+ * A blocking lock was requested. Package up
+ * this smb into a queued request and push it
+ * onto the blocking lock queue.
+ */
+ if(push_blocking_lock_request(inbuf, length, -1, 0, SVAL(inbuf,smb_pid), offset, count)) {
+ END_PROFILE(SMBlock);
+ return -1;
+ }
+ }
+#endif
+ END_PROFILE(SMBlock);
+ return ERROR_NT(status);
+ }
+
+ END_PROFILE(SMBlock);
+ return(outsize);
+}
+
+/****************************************************************************
+ Reply to a unlock.
+****************************************************************************/
+
+int reply_unlock(connection_struct *conn, char *inbuf,char *outbuf, int size,
+ int dum_buffsize)
+{
+ int outsize = set_message(outbuf,0,0,True);
+ SMB_BIG_UINT count,offset;
+ NTSTATUS status;
+ files_struct *fsp = file_fsp(inbuf,smb_vwv0);
+ START_PROFILE(SMBunlock);
+
+ CHECK_FSP(fsp,conn);
+
+ count = (SMB_BIG_UINT)IVAL(inbuf,smb_vwv1);
+ offset = (SMB_BIG_UINT)IVAL(inbuf,smb_vwv3);
+
+ status = do_unlock(fsp, conn, SVAL(inbuf,smb_pid), count, offset);
+ if (NT_STATUS_V(status)) {
+ END_PROFILE(SMBunlock);
+ return ERROR_NT(status);
+ }
+
+ DEBUG( 3, ( "unlock fd=%d fnum=%d offset=%.0f count=%.0f\n",
+ fsp->fd, fsp->fnum, (double)offset, (double)count ) );
+
+ END_PROFILE(SMBunlock);
+ return(outsize);
+}
+
+/****************************************************************************
+ Reply to a tdis.
+****************************************************************************/
+
+int reply_tdis(connection_struct *conn,
+ char *inbuf,char *outbuf, int dum_size, int dum_buffsize)
+{
+ int outsize = set_message(outbuf,0,0,True);
+ uint16 vuid;
+ START_PROFILE(SMBtdis);
+
+ vuid = SVAL(inbuf,smb_uid);
+
+ if (!conn) {
+ DEBUG(4,("Invalid connection in tdis\n"));
+ END_PROFILE(SMBtdis);
+ return ERROR_DOS(ERRSRV,ERRinvnid);
+ }
+
+ conn->used = False;
+
+ close_cnum(conn,vuid);
+
+ END_PROFILE(SMBtdis);
+ return outsize;
+}
+
+/****************************************************************************
+ Reply to a echo.
+****************************************************************************/
+
+int reply_echo(connection_struct *conn,
+ char *inbuf,char *outbuf, int dum_size, int dum_buffsize)
+{
+ int smb_reverb = SVAL(inbuf,smb_vwv0);
+ int seq_num;
+ unsigned int data_len = smb_buflen(inbuf);
+ int outsize = set_message(outbuf,1,data_len,True);
+ START_PROFILE(SMBecho);
+
+ if (data_len > BUFFER_SIZE) {
+ DEBUG(0,("reply_echo: data_len too large.\n"));
+ END_PROFILE(SMBecho);
+ return -1;
+ }
+
+ /* copy any incoming data back out */
+ if (data_len > 0)
+ memcpy(smb_buf(outbuf),smb_buf(inbuf),data_len);
+
+ if (smb_reverb > 100) {
+ DEBUG(0,("large reverb (%d)?? Setting to 100\n",smb_reverb));
+ smb_reverb = 100;
+ }
+
+ for (seq_num =1 ; seq_num <= smb_reverb ; seq_num++) {
+ SSVAL(outbuf,smb_vwv0,seq_num);
+
+ smb_setlen(outbuf,outsize - 4);
+
+ if (!send_smb(smbd_server_fd(),outbuf))
+ exit_server("reply_echo: send_smb failed.");
+ }
+
+ DEBUG(3,("echo %d times\n", smb_reverb));
+
+ smb_echo_count++;
+
+ END_PROFILE(SMBecho);
+ return -1;
+}
+
+/****************************************************************************
+ Reply to a printopen.
+****************************************************************************/
+
+int reply_printopen(connection_struct *conn,
+ char *inbuf,char *outbuf, int dum_size, int dum_buffsize)
+{
+ int outsize = 0;
+ files_struct *fsp;
+ START_PROFILE(SMBsplopen);
+
+ if (!CAN_PRINT(conn)) {
+ END_PROFILE(SMBsplopen);
+ return ERROR_DOS(ERRDOS,ERRnoaccess);
+ }
+
+ /* Open for exclusive use, write only. */
+ fsp = print_fsp_open(conn, NULL);
+
+ if (!fsp) {
+ END_PROFILE(SMBsplopen);
+ return(UNIXERROR(ERRDOS,ERRnoaccess));
+ }
+
+ outsize = set_message(outbuf,1,0,True);
+ SSVAL(outbuf,smb_vwv0,fsp->fnum);
+
+ DEBUG(3,("openprint fd=%d fnum=%d\n",
+ fsp->fd, fsp->fnum));
+
+ END_PROFILE(SMBsplopen);
+ return(outsize);
+}
+
+/****************************************************************************
+ Reply to a printclose.
+****************************************************************************/
+
+int reply_printclose(connection_struct *conn,
+ char *inbuf,char *outbuf, int dum_size, int dum_buffsize)
+{
+ int outsize = set_message(outbuf,0,0,True);
+ files_struct *fsp = file_fsp(inbuf,smb_vwv0);
+ int close_err = 0;
+ START_PROFILE(SMBsplclose);
+
+ CHECK_FSP(fsp,conn);
+
+ if (!CAN_PRINT(conn)) {
+ END_PROFILE(SMBsplclose);
+ return ERROR_NT(NT_STATUS_UNSUCCESSFUL);
+ }
+
+ DEBUG(3,("printclose fd=%d fnum=%d\n",
+ fsp->fd,fsp->fnum));
+
+ close_err = close_file(fsp,True);
+
+ if(close_err != 0) {
+ errno = close_err;
+ END_PROFILE(SMBsplclose);
+ return(UNIXERROR(ERRHRD,ERRgeneral));
+ }
+
+ END_PROFILE(SMBsplclose);
+ return(outsize);
+}
+
+/****************************************************************************
+ Reply to a printqueue.
+****************************************************************************/
+
+int reply_printqueue(connection_struct *conn,
+ char *inbuf,char *outbuf, int dum_size, int dum_buffsize)
+{
+ int outsize = set_message(outbuf,2,3,True);
+ int max_count = SVAL(inbuf,smb_vwv0);
+ int start_index = SVAL(inbuf,smb_vwv1);
+ START_PROFILE(SMBsplretq);
+
+ /* we used to allow the client to get the cnum wrong, but that
+ is really quite gross and only worked when there was only
+ one printer - I think we should now only accept it if they
+ get it right (tridge) */
+ if (!CAN_PRINT(conn)) {
+ END_PROFILE(SMBsplretq);
+ return ERROR_DOS(ERRDOS,ERRnoaccess);
+ }
+
+ SSVAL(outbuf,smb_vwv0,0);
+ SSVAL(outbuf,smb_vwv1,0);
+ SCVAL(smb_buf(outbuf),0,1);
+ SSVAL(smb_buf(outbuf),1,0);
+
+ DEBUG(3,("printqueue start_index=%d max_count=%d\n",
+ start_index, max_count));
+
+ {
+ print_queue_struct *queue = NULL;
+ print_status_struct status;
+ char *p = smb_buf(outbuf) + 3;
+ int count = print_queue_status(SNUM(conn), &queue, &status);
+ int num_to_get = ABS(max_count);
+ int first = (max_count>0?start_index:start_index+max_count+1);
+ int i;
+
+ if (first >= count)
+ num_to_get = 0;
+ else
+ num_to_get = MIN(num_to_get,count-first);
+
+
+ for (i=first;i<first+num_to_get;i++) {
+ put_dos_date2(p,0,queue[i].time);
+ SCVAL(p,4,(queue[i].status==LPQ_PRINTING?2:3));
+ SSVAL(p,5, queue[i].job);
+ SIVAL(p,7,queue[i].size);
+ SCVAL(p,11,0);
+ srvstr_push(outbuf, p+12, queue[i].fs_user, 16, STR_ASCII);
+ p += 28;
+ }
+
+ if (count > 0) {
+ outsize = set_message(outbuf,2,28*count+3,False);
+ SSVAL(outbuf,smb_vwv0,count);
+ SSVAL(outbuf,smb_vwv1,(max_count>0?first+count:first-1));
+ SCVAL(smb_buf(outbuf),0,1);
+ SSVAL(smb_buf(outbuf),1,28*count);
+ }
+
+ SAFE_FREE(queue);
+
+ DEBUG(3,("%d entries returned in queue\n",count));
+ }
+
+ END_PROFILE(SMBsplretq);
+ return(outsize);
+}
+
+/****************************************************************************
+ Reply to a printwrite.
+****************************************************************************/
+
+int reply_printwrite(connection_struct *conn, char *inbuf,char *outbuf, int dum_size, int dum_buffsize)
+{
+ int numtowrite;
+ int outsize = set_message(outbuf,0,0,True);
+ char *data;
+ files_struct *fsp = file_fsp(inbuf,smb_vwv0);
+
+ START_PROFILE(SMBsplwr);
+
+ if (!CAN_PRINT(conn)) {
+ END_PROFILE(SMBsplwr);
+ return ERROR_DOS(ERRDOS,ERRnoaccess);
+ }
+
+ CHECK_FSP(fsp,conn);
+ CHECK_WRITE(fsp);
+
+ numtowrite = SVAL(smb_buf(inbuf),1);
+ data = smb_buf(inbuf) + 3;
+
+ if (write_file(fsp,data,-1,numtowrite) != numtowrite) {
+ END_PROFILE(SMBsplwr);
+ return(UNIXERROR(ERRHRD,ERRdiskfull));
+ }
+
+ DEBUG( 3, ( "printwrite fnum=%d num=%d\n", fsp->fnum, numtowrite ) );
+
+ END_PROFILE(SMBsplwr);
+ return(outsize);
+}
+
+/****************************************************************************
+ The guts of the mkdir command, split out so it may be called by the NT SMB
+ code.
+****************************************************************************/
+
+NTSTATUS mkdir_internal(connection_struct *conn, pstring directory)
+{
+ BOOL bad_path = False;
+ SMB_STRUCT_STAT sbuf;
+ int ret= -1;
+
+ unix_convert(directory,conn,0,&bad_path,&sbuf);
+
+ if( strchr_m(directory, ':')) {
+ return NT_STATUS_NOT_A_DIRECTORY;
+ }
+
+ if (ms_has_wild(directory)) {
+ return NT_STATUS_OBJECT_NAME_INVALID;
+ }
+
+ if (check_name(directory, conn))
+ ret = vfs_MkDir(conn,directory,unix_mode(conn,aDIR,directory));
+
+ if (ret == -1) {
+ if(errno == ENOENT) {
+ if (bad_path)
+ return NT_STATUS_OBJECT_PATH_NOT_FOUND;
+ else
+ return NT_STATUS_OBJECT_NAME_NOT_FOUND;
+ }
+ return map_nt_error_from_unix(errno);
+ }
+
+ return NT_STATUS_OK;
+}
+
+/****************************************************************************
+ Reply to a mkdir.
+****************************************************************************/
+
+int reply_mkdir(connection_struct *conn, char *inbuf,char *outbuf, int dum_size, int dum_buffsize)
+{
+ pstring directory;
+ int outsize;
+ NTSTATUS status;
+ START_PROFILE(SMBmkdir);
+
+ srvstr_get_path(inbuf, directory, smb_buf(inbuf) + 1, sizeof(directory), 0, STR_TERMINATE, &status);
+ if (!NT_STATUS_IS_OK(status)) {
+ END_PROFILE(SMBmkdir);
+ return ERROR_NT(status);
+ }
+
+ RESOLVE_DFSPATH(directory, conn, inbuf, outbuf);
+
+ status = mkdir_internal(conn, directory);
+ if (!NT_STATUS_IS_OK(status)) {
+ END_PROFILE(SMBmkdir);
+ return ERROR_NT(status);
+ }
+
+ outsize = set_message(outbuf,0,0,True);
+
+ DEBUG( 3, ( "mkdir %s ret=%d\n", directory, outsize ) );
+
+ END_PROFILE(SMBmkdir);
+ return(outsize);
+}
+
+/****************************************************************************
+ Static function used by reply_rmdir to delete an entire directory
+ tree recursively. Return False on ok, True on fail.
+****************************************************************************/
+
+static BOOL recursive_rmdir(connection_struct *conn, char *directory)
+{
+ const char *dname = NULL;
+ BOOL ret = False;
+ void *dirptr = OpenDir(conn, directory, False);
+
+ if(dirptr == NULL)
+ return True;
+
+ while((dname = ReadDirName(dirptr))) {
+ pstring fullname;
+ SMB_STRUCT_STAT st;
+
+ if((strcmp(dname, ".") == 0) || (strcmp(dname, "..")==0))
+ continue;
+
+ /* Construct the full name. */
+ if(strlen(directory) + strlen(dname) + 1 >= sizeof(fullname)) {
+ errno = ENOMEM;
+ ret = True;
+ break;
+ }
+
+ pstrcpy(fullname, directory);
+ pstrcat(fullname, "/");
+ pstrcat(fullname, dname);
+
+ if(SMB_VFS_LSTAT(conn,fullname, &st) != 0) {
+ ret = True;
+ break;
+ }
+
+ if(st.st_mode & S_IFDIR) {
+ if(recursive_rmdir(conn, fullname)!=0) {
+ ret = True;
+ break;
+ }
+ if(SMB_VFS_RMDIR(conn,fullname) != 0) {
+ ret = True;
+ break;
+ }
+ } else if(SMB_VFS_UNLINK(conn,fullname) != 0) {
+ ret = True;
+ break;
+ }
+ }
+ CloseDir(dirptr);
+ return ret;
+}
+
+/****************************************************************************
+ The internals of the rmdir code - called elsewhere.
+****************************************************************************/
+
+BOOL rmdir_internals(connection_struct *conn, char *directory)
+{
+ BOOL ok;
+
+ ok = (SMB_VFS_RMDIR(conn,directory) == 0);
+ if(!ok && ((errno == ENOTEMPTY)||(errno == EEXIST)) && lp_veto_files(SNUM(conn))) {
+ /*
+ * Check to see if the only thing in this directory are
+ * vetoed files/directories. If so then delete them and
+ * retry. If we fail to delete any of them (and we *don't*
+ * do a recursive delete) then fail the rmdir.
+ */
+ BOOL all_veto_files = True;
+ const char *dname;
+ void *dirptr = OpenDir(conn, directory, False);
+
+ if(dirptr != NULL) {
+ int dirpos = TellDir(dirptr);
+ while ((dname = ReadDirName(dirptr))) {
+ if((strcmp(dname, ".") == 0) || (strcmp(dname, "..")==0))
+ continue;
+ if(!IS_VETO_PATH(conn, dname)) {
+ all_veto_files = False;
+ break;
+ }
+ }
+
+ if(all_veto_files) {
+ SeekDir(dirptr,dirpos);
+ while ((dname = ReadDirName(dirptr))) {
+ pstring fullname;
+ SMB_STRUCT_STAT st;
+
+ if((strcmp(dname, ".") == 0) || (strcmp(dname, "..")==0))
+ continue;
+
+ /* Construct the full name. */
+ if(strlen(directory) + strlen(dname) + 1 >= sizeof(fullname)) {
+ errno = ENOMEM;
+ break;
+ }
+
+ pstrcpy(fullname, directory);
+ pstrcat(fullname, "/");
+ pstrcat(fullname, dname);
+
+ if(SMB_VFS_LSTAT(conn,fullname, &st) != 0)
+ break;
+ if(st.st_mode & S_IFDIR) {
+ if(lp_recursive_veto_delete(SNUM(conn))) {
+ if(recursive_rmdir(conn, fullname) != 0)
+ break;
+ }
+ if(SMB_VFS_RMDIR(conn,fullname) != 0)
+ break;
+ } else if(SMB_VFS_UNLINK(conn,fullname) != 0)
+ break;
+ }
+ CloseDir(dirptr);
+ /* Retry the rmdir */
+ ok = (SMB_VFS_RMDIR(conn,directory) == 0);
+ } else {
+ CloseDir(dirptr);
+ }
+ } else {
+ errno = ENOTEMPTY;
+ }
+ }
+
+ if (!ok)
+ DEBUG(3,("rmdir_internals: couldn't remove directory %s : %s\n", directory,strerror(errno)));
+
+ return ok;
+}
+
+/****************************************************************************
+ Reply to a rmdir.
+****************************************************************************/
+
+int reply_rmdir(connection_struct *conn, char *inbuf,char *outbuf, int dum_size, int dum_buffsize)
+{
+ pstring directory;
+ int outsize = 0;
+ BOOL ok = False;
+ BOOL bad_path = False;
+ SMB_STRUCT_STAT sbuf;
+ NTSTATUS status;
+ START_PROFILE(SMBrmdir);
+
+ srvstr_get_path(inbuf, directory, smb_buf(inbuf) + 1, sizeof(directory), 0, STR_TERMINATE, &status);
+ if (!NT_STATUS_IS_OK(status)) {
+ END_PROFILE(SMBrmdir);
+ return ERROR_NT(status);
+ }
+
+ RESOLVE_DFSPATH(directory, conn, inbuf, outbuf)
+
+ unix_convert(directory,conn, NULL,&bad_path,&sbuf);
+
+ if (check_name(directory,conn)) {
+ dptr_closepath(directory,SVAL(inbuf,smb_pid));
+ ok = rmdir_internals(conn, directory);
+ }
+
+ if (!ok) {
+ END_PROFILE(SMBrmdir);
+ return set_bad_path_error(errno, bad_path, outbuf, ERRDOS, ERRbadpath);
+ }
+
+ outsize = set_message(outbuf,0,0,True);
+
+ DEBUG( 3, ( "rmdir %s\n", directory ) );
+
+ END_PROFILE(SMBrmdir);
+ return(outsize);
+}
+
+/*******************************************************************
+ Resolve wildcards in a filename rename.
+ Note that name is in UNIX charset and thus potentially can be more
+ than fstring buffer (255 bytes) especially in default UTF-8 case.
+ Therefore, we use pstring inside and all calls should ensure that
+ name2 is at least pstring-long (they do already)
+********************************************************************/
+
+static BOOL resolve_wildcards(const char *name1, char *name2)
+{
+ pstring root1,root2;
+ pstring ext1,ext2;
+ char *p,*p2, *pname1, *pname2;
+ int available_space, actual_space;
+
+
+ pname1 = strrchr_m(name1,'/');
+ pname2 = strrchr_m(name2,'/');
+
+ if (!pname1 || !pname2)
+ return(False);
+
+ pstrcpy(root1,pname1);
+ pstrcpy(root2,pname2);
+ p = strrchr_m(root1,'.');
+ if (p) {
+ *p = 0;
+ pstrcpy(ext1,p+1);
+ } else {
+ pstrcpy(ext1,"");
+ }
+ p = strrchr_m(root2,'.');
+ if (p) {
+ *p = 0;
+ pstrcpy(ext2,p+1);
+ } else {
+ pstrcpy(ext2,"");
+ }
+
+ p = root1;
+ p2 = root2;
+ while (*p2) {
+ if (*p2 == '?') {
+ *p2 = *p;
+ p2++;
+ } else if (*p2 == '*') {
+ pstrcpy(p2, p);
+ break;
+ } else {
+ p2++;
+ }
+ if (*p)
+ p++;
+ }
+
+ p = ext1;
+ p2 = ext2;
+ while (*p2) {
+ if (*p2 == '?') {
+ *p2 = *p;
+ p2++;
+ } else if (*p2 == '*') {
+ pstrcpy(p2, p);
+ break;
+ } else {
+ p2++;
+ }
+ if (*p)
+ p++;
+ }
+
+ available_space = sizeof(pstring) - PTR_DIFF(pname2, name2);
+
+ if (ext2[0]) {
+ actual_space = snprintf(pname2, available_space - 1, "%s.%s", root2, ext2);
+ if (actual_space >= available_space - 1) {
+ DEBUG(1,("resolve_wildcards: can't fit resolved name into specified buffer (overrun by %d bytes)\n",
+ actual_space - available_space));
+ }
+ } else {
+ pstrcpy_base(pname2, root2, name2);
+ }
+
+ return(True);
+}
+
+/****************************************************************************
+ Ensure open files have their names updates.
+****************************************************************************/
+
+static void rename_open_files(connection_struct *conn, SMB_DEV_T dev, SMB_INO_T inode, char *newname)
+{
+ files_struct *fsp;
+ BOOL did_rename = False;
+
+ for(fsp = file_find_di_first(dev, inode); fsp; fsp = file_find_di_next(fsp)) {
+ DEBUG(10,("rename_open_files: renaming file fnum %d (dev = %x, inode = %.0f) from %s -> %s\n",
+ fsp->fnum, (unsigned int)fsp->dev, (double)fsp->inode,
+ fsp->fsp_name, newname ));
+ string_set(&fsp->fsp_name, newname);
+ did_rename = True;
+ }
+
+ if (!did_rename)
+ DEBUG(10,("rename_open_files: no open files on dev %x, inode %.0f for %s\n",
+ (unsigned int)dev, (double)inode, newname ));
+}
+
+/****************************************************************************
+ Rename an open file - given an fsp.
+****************************************************************************/
+
+NTSTATUS rename_internals_fsp(connection_struct *conn, files_struct *fsp, char *newname, BOOL replace_if_exists)
+{
+ SMB_STRUCT_STAT sbuf;
+ BOOL bad_path = False;
+ pstring newname_last_component;
+ NTSTATUS error = NT_STATUS_OK;
+ BOOL dest_exists;
+ BOOL rcdest = True;
+
+ ZERO_STRUCT(sbuf);
+ rcdest = unix_convert(newname,conn,newname_last_component,&bad_path,&sbuf);
+
+ /* Quick check for "." and ".." */
+ if (!bad_path && newname_last_component[0] == '.') {
+ if (!newname_last_component[1] || (newname_last_component[1] == '.' && !newname_last_component[2])) {
+ return NT_STATUS_ACCESS_DENIED;
+ }
+ }
+ if (!rcdest && bad_path) {
+ return NT_STATUS_OBJECT_PATH_NOT_FOUND;
+ }
+
+ /* Ensure newname contains a '/' */
+ if(strrchr_m(newname,'/') == 0) {
+ pstring tmpstr;
+
+ pstrcpy(tmpstr, "./");
+ pstrcat(tmpstr, newname);
+ pstrcpy(newname, tmpstr);
+ }
+
+ /*
+ * Check for special case with case preserving and not
+ * case sensitive. If the old last component differs from the original
+ * last component only by case, then we should allow
+ * the rename (user is trying to change the case of the
+ * filename).
+ */
+
+ if((case_sensitive == False) && (case_preserve == True) &&
+ strequal(newname, fsp->fsp_name)) {
+ char *p;
+ pstring newname_modified_last_component;
+
+ /*
+ * Get the last component of the modified name.
+ * Note that we guarantee that newname contains a '/'
+ * character above.
+ */
+ p = strrchr_m(newname,'/');
+ pstrcpy(newname_modified_last_component,p+1);
+
+ if(strcsequal(newname_modified_last_component,
+ newname_last_component) == False) {
+ /*
+ * Replace the modified last component with
+ * the original.
+ */
+ pstrcpy(p+1, newname_last_component);
+ }
+ }
+
+ /*
+ * If the src and dest names are identical - including case,
+ * don't do the rename, just return success.
+ */
+
+ if (strcsequal(fsp->fsp_name, newname)) {
+ DEBUG(3,("rename_internals_fsp: identical names in rename %s - returning success\n",
+ newname));
+ return NT_STATUS_OK;
+ }
+
+ dest_exists = vfs_object_exist(conn,newname,NULL);
+
+ if(!replace_if_exists && dest_exists) {
+ DEBUG(3,("rename_internals_fsp: dest exists doing rename %s -> %s\n",
+ fsp->fsp_name,newname));
+ return NT_STATUS_OBJECT_NAME_COLLISION;
+ }
+
+ error = can_rename(newname,conn,&sbuf);
+
+ if (dest_exists && !NT_STATUS_IS_OK(error)) {
+ DEBUG(3,("rename_internals: Error %s rename %s -> %s\n",
+ nt_errstr(error), fsp->fsp_name,newname));
+ if (NT_STATUS_EQUAL(error,NT_STATUS_SHARING_VIOLATION))
+ error = NT_STATUS_ACCESS_DENIED;
+ return error;
+ }
+
+ if(SMB_VFS_RENAME(conn,fsp->fsp_name, newname) == 0) {
+ DEBUG(3,("rename_internals_fsp: succeeded doing rename on %s -> %s\n",
+ fsp->fsp_name,newname));
+ rename_open_files(conn, fsp->dev, fsp->inode, newname);
+ return NT_STATUS_OK;
+ }
+
+ if (errno == ENOTDIR || errno == EISDIR)
+ error = NT_STATUS_OBJECT_NAME_COLLISION;
+ else
+ error = map_nt_error_from_unix(errno);
+
+ DEBUG(3,("rename_internals_fsp: Error %s rename %s -> %s\n",
+ nt_errstr(error), fsp->fsp_name,newname));
+
+ return error;
+}
+
+/****************************************************************************
+ The guts of the rename command, split out so it may be called by the NT SMB
+ code.
+****************************************************************************/
+
+NTSTATUS rename_internals(connection_struct *conn, char *name, char *newname, uint16 attrs, BOOL replace_if_exists)
+{
+ pstring directory;
+ pstring mask;
+ pstring last_component_src;
+ pstring last_component_dest;
+ char *p;
+ BOOL has_wild;
+ BOOL bad_path_src = False;
+ BOOL bad_path_dest = False;
+ int count=0;
+ NTSTATUS error = NT_STATUS_OK;
+ BOOL rc = True;
+ BOOL rcdest = True;
+ SMB_STRUCT_STAT sbuf1, sbuf2;
+
+ *directory = *mask = 0;
+
+ ZERO_STRUCT(sbuf1);
+ ZERO_STRUCT(sbuf2);
+
+ rc = unix_convert(name,conn,last_component_src,&bad_path_src,&sbuf1);
+ if (!rc && bad_path_src) {
+ if (ms_has_wild(last_component_src))
+ return NT_STATUS_OBJECT_NAME_NOT_FOUND;
+ return NT_STATUS_OBJECT_PATH_NOT_FOUND;
+ }
+
+ /* Quick check for "." and ".." */
+ if (last_component_src[0] == '.') {
+ if (!last_component_src[1] || (last_component_src[1] == '.' && !last_component_src[2])) {
+ return NT_STATUS_OBJECT_NAME_INVALID;
+ }
+ }
+
+ rcdest = unix_convert(newname,conn,last_component_dest,&bad_path_dest,&sbuf2);
+
+ /* Quick check for "." and ".." */
+ if (last_component_dest[0] == '.') {
+ if (!last_component_dest[1] || (last_component_dest[1] == '.' && !last_component_dest[2])) {
+ return NT_STATUS_OBJECT_NAME_INVALID;
+ }
+ }
+
+ /*
+ * Split the old name into directory and last component
+ * strings. Note that unix_convert may have stripped off a
+ * leading ./ from both name and newname if the rename is
+ * at the root of the share. We need to make sure either both
+ * name and newname contain a / character or neither of them do
+ * as this is checked in resolve_wildcards().
+ */
+
+ p = strrchr_m(name,'/');
+ if (!p) {
+ pstrcpy(directory,".");
+ pstrcpy(mask,name);
+ } else {
+ *p = 0;
+ pstrcpy(directory,name);
+ pstrcpy(mask,p+1);
+ *p = '/'; /* Replace needed for exceptional test below. */
+ }
+
+ /*
+ * We should only check the mangled cache
+ * here if unix_convert failed. This means
+ * that the path in 'mask' doesn't exist
+ * on the file system and so we need to look
+ * for a possible mangle. This patch from
+ * Tine Smukavec <valentin.smukavec@hermes.si>.
+ */
+
+ if (!rc && mangle_is_mangled(mask))
+ mangle_check_cache( mask );
+
+ has_wild = ms_has_wild(mask);
+
+ if (!has_wild) {
+ /*
+ * No wildcards - just process the one file.
+ */
+ BOOL is_short_name = mangle_is_8_3(name, True);
+
+ /* Add a terminating '/' to the directory name. */
+ pstrcat(directory,"/");
+ pstrcat(directory,mask);
+
+ /* Ensure newname contains a '/' also */
+ if(strrchr_m(newname,'/') == 0) {
+ pstring tmpstr;
+
+ pstrcpy(tmpstr, "./");
+ pstrcat(tmpstr, newname);
+ pstrcpy(newname, tmpstr);
+ }
+
+ DEBUG(3,("rename_internals: case_sensitive = %d, case_preserve = %d, short case preserve = %d, \
+directory = %s, newname = %s, last_component_dest = %s, is_8_3 = %d\n",
+ case_sensitive, case_preserve, short_case_preserve, directory,
+ newname, last_component_dest, is_short_name));
+
+ /*
+ * Check for special case with case preserving and not
+ * case sensitive, if directory and newname are identical,
+ * and the old last component differs from the original
+ * last component only by case, then we should allow
+ * the rename (user is trying to change the case of the
+ * filename).
+ */
+ if((case_sensitive == False) &&
+ (((case_preserve == True) &&
+ (is_short_name == False)) ||
+ ((short_case_preserve == True) &&
+ (is_short_name == True))) &&
+ strcsequal(directory, newname)) {
+ pstring modified_last_component;
+
+ /*
+ * Get the last component of the modified name.
+ * Note that we guarantee that newname contains a '/'
+ * character above.
+ */
+ p = strrchr_m(newname,'/');
+ pstrcpy(modified_last_component,p+1);
+
+ if(strcsequal(modified_last_component,
+ last_component_dest) == False) {
+ /*
+ * Replace the modified last component with
+ * the original.
+ */
+ pstrcpy(p+1, last_component_dest);
+ }
+ }
+
+ resolve_wildcards(directory,newname);
+
+ /*
+ * The source object must exist.
+ */
+
+ if (!vfs_object_exist(conn, directory, &sbuf1)) {
+ DEBUG(3,("rename_internals: source doesn't exist doing rename %s -> %s\n",
+ directory,newname));
+
+ if (errno == ENOTDIR || errno == EISDIR || errno == ENOENT) {
+ /*
+ * Must return different errors depending on whether the parent
+ * directory existed or not.
+ */
+
+ p = strrchr_m(directory, '/');
+ if (!p)
+ return NT_STATUS_OBJECT_NAME_NOT_FOUND;
+ *p = '\0';
+ if (vfs_object_exist(conn, directory, NULL))
+ return NT_STATUS_OBJECT_NAME_NOT_FOUND;
+ return NT_STATUS_OBJECT_PATH_NOT_FOUND;
+ }
+ error = map_nt_error_from_unix(errno);
+ DEBUG(3,("rename_internals: Error %s rename %s -> %s\n",
+ nt_errstr(error), directory,newname));
+
+ return error;
+ }
+
+ if (!rcdest && bad_path_dest) {
+ if (ms_has_wild(last_component_dest))
+ return NT_STATUS_OBJECT_NAME_INVALID;
+ return NT_STATUS_OBJECT_PATH_NOT_FOUND;
+ }
+
+ error = can_rename(directory,conn,&sbuf1);
+
+ if (!NT_STATUS_IS_OK(error)) {
+ DEBUG(3,("rename_internals: Error %s rename %s -> %s\n",
+ nt_errstr(error), directory,newname));
+ return error;
+ }
+
+ /*
+ * If the src and dest names are identical - including case,
+ * don't do the rename, just return success.
+ */
+
+ if (strcsequal(directory, newname)) {
+ rename_open_files(conn, sbuf1.st_dev, sbuf1.st_ino, newname);
+ DEBUG(3,("rename_internals: identical names in rename %s - returning success\n", directory));
+ return NT_STATUS_OK;
+ }
+
+ if(!replace_if_exists && vfs_object_exist(conn,newname,NULL)) {
+ DEBUG(3,("rename_internals: dest exists doing rename %s -> %s\n",
+ directory,newname));
+ return NT_STATUS_OBJECT_NAME_COLLISION;
+ }
+
+ if(SMB_VFS_RENAME(conn,directory, newname) == 0) {
+ DEBUG(3,("rename_internals: succeeded doing rename on %s -> %s\n",
+ directory,newname));
+ rename_open_files(conn, sbuf1.st_dev, sbuf1.st_ino, newname);
+ return NT_STATUS_OK;
+ }
+
+ if (errno == ENOTDIR || errno == EISDIR)
+ error = NT_STATUS_OBJECT_NAME_COLLISION;
+ else
+ error = map_nt_error_from_unix(errno);
+
+ DEBUG(3,("rename_internals: Error %s rename %s -> %s\n",
+ nt_errstr(error), directory,newname));
+
+ return error;
+ } else {
+ /*
+ * Wildcards - process each file that matches.
+ */
+ void *dirptr = NULL;
+ const char *dname;
+ pstring destname;
+
+ if (check_name(directory,conn))
+ dirptr = OpenDir(conn, directory, True);
+
+ if (dirptr) {
+ error = NT_STATUS_NO_SUCH_FILE;
+/* Was error = NT_STATUS_OBJECT_NAME_NOT_FOUND; - gentest fix. JRA */
+
+ if (strequal(mask,"????????.???"))
+ pstrcpy(mask,"*");
+
+ while ((dname = ReadDirName(dirptr))) {
+ pstring fname;
+ BOOL sysdir_entry = False;
+
+ pstrcpy(fname,dname);
+
+ /* Quick check for "." and ".." */
+ if (fname[0] == '.') {
+ if (!fname[1] || (fname[1] == '.' && !fname[2])) {
+ if (attrs & aDIR) {
+ sysdir_entry = True;
+ } else {
+ continue;
+ }
+ }
+ }
+
+ if(!mask_match(fname, mask, case_sensitive))
+ continue;
+
+ if (sysdir_entry) {
+ error = NT_STATUS_OBJECT_NAME_INVALID;
+ break;
+ }
+
+ error = NT_STATUS_ACCESS_DENIED;
+ slprintf(fname,sizeof(fname)-1,"%s/%s",directory,dname);
+ if (!vfs_object_exist(conn, fname, &sbuf1)) {
+ error = NT_STATUS_OBJECT_NAME_NOT_FOUND;
+ DEBUG(6,("rename %s failed. Error %s\n", fname, nt_errstr(error)));
+ continue;
+ }
+ error = can_rename(fname,conn,&sbuf1);
+ if (!NT_STATUS_IS_OK(error)) {
+ DEBUG(6,("rename %s refused\n", fname));
+ continue;
+ }
+ pstrcpy(destname,newname);
+
+ if (!resolve_wildcards(fname,destname)) {
+ DEBUG(6,("resolve_wildcards %s %s failed\n",
+ fname, destname));
+ continue;
+ }
+
+ if (strcsequal(fname,destname)) {
+ rename_open_files(conn, sbuf1.st_dev, sbuf1.st_ino, newname);
+ DEBUG(3,("rename_internals: identical names in wildcard rename %s - success\n", fname));
+ count++;
+ error = NT_STATUS_OK;
+ continue;
+ }
+
+ if (!replace_if_exists &&
+ vfs_file_exist(conn,destname, NULL)) {
+ DEBUG(6,("file_exist %s\n", destname));
+ error = NT_STATUS_OBJECT_NAME_COLLISION;
+ continue;
+ }
+
+ if (!SMB_VFS_RENAME(conn,fname,destname)) {
+ rename_open_files(conn, sbuf1.st_dev, sbuf1.st_ino, newname);
+ count++;
+ error = NT_STATUS_OK;
+ }
+ DEBUG(3,("rename_internals: doing rename on %s -> %s\n",fname,destname));
+ }
+ CloseDir(dirptr);
+ }
+
+ if (!NT_STATUS_EQUAL(error,NT_STATUS_NO_SUCH_FILE)) {
+ if (!rcdest && bad_path_dest) {
+ if (ms_has_wild(last_component_dest))
+ return NT_STATUS_OBJECT_NAME_INVALID;
+ return NT_STATUS_OBJECT_PATH_NOT_FOUND;
+ }
+ }
+ }
+
+ if (count == 0 && NT_STATUS_IS_OK(error)) {
+ error = map_nt_error_from_unix(errno);
+ }
+
+ return error;
+}
+
+/****************************************************************************
+ Reply to a mv.
+****************************************************************************/
+
+int reply_mv(connection_struct *conn, char *inbuf,char *outbuf, int dum_size,
+ int dum_buffsize)
+{
+ int outsize = 0;
+ pstring name;
+ pstring newname;
+ char *p;
+ uint16 attrs = SVAL(inbuf,smb_vwv0);
+ NTSTATUS status;
+
+ START_PROFILE(SMBmv);
+
+ p = smb_buf(inbuf) + 1;
+ p += srvstr_get_path(inbuf, name, p, sizeof(name), 0, STR_TERMINATE, &status);
+ if (!NT_STATUS_IS_OK(status)) {
+ END_PROFILE(SMBmv);
+ return ERROR_NT(status);
+ }
+ p++;
+ p += srvstr_get_path(inbuf, newname, p, sizeof(newname), 0, STR_TERMINATE, &status);
+ if (!NT_STATUS_IS_OK(status)) {
+ END_PROFILE(SMBmv);
+ return ERROR_NT(status);
+ }
+
+ RESOLVE_DFSPATH(name, conn, inbuf, outbuf);
+ RESOLVE_DFSPATH(newname, conn, inbuf, outbuf);
+
+ DEBUG(3,("reply_mv : %s -> %s\n",name,newname));
+
+ status = rename_internals(conn, name, newname, attrs, False);
+ if (!NT_STATUS_IS_OK(status)) {
+ END_PROFILE(SMBmv);
+ return ERROR_NT(status);
+ }
+
+ /*
+ * Win2k needs a changenotify request response before it will
+ * update after a rename..
+ */
+ process_pending_change_notify_queue((time_t)0);
+ outsize = set_message(outbuf,0,0,True);
+
+ END_PROFILE(SMBmv);
+ return(outsize);
+}
+
+/*******************************************************************
+ Copy a file as part of a reply_copy.
+******************************************************************/
+
+static BOOL copy_file(char *src,char *dest1,connection_struct *conn, int ofun,
+ int count,BOOL target_is_directory, int *err_ret)
+{
+ int Access,action;
+ SMB_STRUCT_STAT src_sbuf, sbuf2;
+ SMB_OFF_T ret=-1;
+ files_struct *fsp1,*fsp2;
+ pstring dest;
+ uint32 dosattrs;
+
+ *err_ret = 0;
+
+ pstrcpy(dest,dest1);
+ if (target_is_directory) {
+ char *p = strrchr_m(src,'/');
+ if (p)
+ p++;
+ else
+ p = src;
+ pstrcat(dest,"/");
+ pstrcat(dest,p);
+ }
+
+ if (!vfs_file_exist(conn,src,&src_sbuf))
+ return(False);
+
+ fsp1 = open_file_shared(conn,src,&src_sbuf,SET_DENY_MODE(DENY_NONE)|SET_OPEN_MODE(DOS_OPEN_RDONLY),
+ (FILE_FAIL_IF_NOT_EXIST|FILE_EXISTS_OPEN),FILE_ATTRIBUTE_NORMAL,0,&Access,&action);
+
+ if (!fsp1)
+ return(False);
+
+ if (!target_is_directory && count)
+ ofun = FILE_EXISTS_OPEN;
+
+ dosattrs = dos_mode(conn, src, &src_sbuf);
+ if (SMB_VFS_STAT(conn,dest,&sbuf2) == -1)
+ ZERO_STRUCTP(&sbuf2);
+
+ fsp2 = open_file_shared(conn,dest,&sbuf2,SET_DENY_MODE(DENY_NONE)|SET_OPEN_MODE(DOS_OPEN_WRONLY),
+ ofun,dosattrs,0,&Access,&action);
+
+ if (!fsp2) {
+ close_file(fsp1,False);
+ return(False);
+ }
+
+ if ((ofun&3) == 1) {
+ if(SMB_VFS_LSEEK(fsp2,fsp2->fd,0,SEEK_END) == -1) {
+ DEBUG(0,("copy_file: error - vfs lseek returned error %s\n", strerror(errno) ));
+ /*
+ * Stop the copy from occurring.
+ */
+ ret = -1;
+ src_sbuf.st_size = 0;
+ }
+ }
+
+ if (src_sbuf.st_size)
+ ret = vfs_transfer_file(fsp1, fsp2, src_sbuf.st_size);
+
+ close_file(fsp1,False);
+
+ /* Ensure the modtime is set correctly on the destination file. */
+ fsp2->pending_modtime = src_sbuf.st_mtime;
+
+ /*
+ * As we are opening fsp1 read-only we only expect
+ * an error on close on fsp2 if we are out of space.
+ * Thus we don't look at the error return from the
+ * close of fsp1.
+ */
+ *err_ret = close_file(fsp2,False);
+
+ return(ret == (SMB_OFF_T)src_sbuf.st_size);
+}
+
+/****************************************************************************
+ Reply to a file copy.
+****************************************************************************/
+
+int reply_copy(connection_struct *conn, char *inbuf,char *outbuf, int dum_size, int dum_buffsize)
+{
+ int outsize = 0;
+ pstring name;
+ pstring directory;
+ pstring mask,newname;
+ char *p;
+ int count=0;
+ int error = ERRnoaccess;
+ int err = 0;
+ BOOL has_wild;
+ BOOL exists=False;
+ int tid2 = SVAL(inbuf,smb_vwv0);
+ int ofun = SVAL(inbuf,smb_vwv1);
+ int flags = SVAL(inbuf,smb_vwv2);
+ BOOL target_is_directory=False;
+ BOOL bad_path1 = False;
+ BOOL bad_path2 = False;
+ BOOL rc = True;
+ SMB_STRUCT_STAT sbuf1, sbuf2;
+ NTSTATUS status;
+
+ START_PROFILE(SMBcopy);
+
+ *directory = *mask = 0;
+
+ p = smb_buf(inbuf);
+ p += srvstr_get_path(inbuf, name, p, sizeof(name), 0, STR_TERMINATE, &status);
+ if (!NT_STATUS_IS_OK(status)) {
+ END_PROFILE(SMBcopy);
+ return ERROR_NT(status);
+ }
+ p += srvstr_get_path(inbuf, newname, p, sizeof(newname), 0, STR_TERMINATE, &status);
+ if (!NT_STATUS_IS_OK(status)) {
+ END_PROFILE(SMBcopy);
+ return ERROR_NT(status);
+ }
+
+ DEBUG(3,("reply_copy : %s -> %s\n",name,newname));
+
+ if (tid2 != conn->cnum) {
+ /* can't currently handle inter share copies XXXX */
+ DEBUG(3,("Rejecting inter-share copy\n"));
+ END_PROFILE(SMBcopy);
+ return ERROR_DOS(ERRSRV,ERRinvdevice);
+ }
+
+ RESOLVE_DFSPATH(name, conn, inbuf, outbuf);
+ RESOLVE_DFSPATH(newname, conn, inbuf, outbuf);
+
+ rc = unix_convert(name,conn,0,&bad_path1,&sbuf1);
+ unix_convert(newname,conn,0,&bad_path2,&sbuf2);
+
+ target_is_directory = VALID_STAT_OF_DIR(sbuf2);
+
+ if ((flags&1) && target_is_directory) {
+ END_PROFILE(SMBcopy);
+ return ERROR_DOS(ERRDOS,ERRbadfile);
+ }
+
+ if ((flags&2) && !target_is_directory) {
+ END_PROFILE(SMBcopy);
+ return ERROR_DOS(ERRDOS,ERRbadpath);
+ }
+
+ if ((flags&(1<<5)) && VALID_STAT_OF_DIR(sbuf1)) {
+ /* wants a tree copy! XXXX */
+ DEBUG(3,("Rejecting tree copy\n"));
+ END_PROFILE(SMBcopy);
+ return ERROR_DOS(ERRSRV,ERRerror);
+ }
+
+ p = strrchr_m(name,'/');
+ if (!p) {
+ pstrcpy(directory,"./");
+ pstrcpy(mask,name);
+ } else {
+ *p = 0;
+ pstrcpy(directory,name);
+ pstrcpy(mask,p+1);
+ }
+
+ /*
+ * We should only check the mangled cache
+ * here if unix_convert failed. This means
+ * that the path in 'mask' doesn't exist
+ * on the file system and so we need to look
+ * for a possible mangle. This patch from
+ * Tine Smukavec <valentin.smukavec@hermes.si>.
+ */
+
+ if (!rc && mangle_is_mangled(mask))
+ mangle_check_cache( mask );
+
+ has_wild = ms_has_wild(mask);
+
+ if (!has_wild) {
+ pstrcat(directory,"/");
+ pstrcat(directory,mask);
+ if (resolve_wildcards(directory,newname) &&
+ copy_file(directory,newname,conn,ofun, count,target_is_directory,&err))
+ count++;
+ if(!count && err) {
+ errno = err;
+ END_PROFILE(SMBcopy);
+ return(UNIXERROR(ERRHRD,ERRgeneral));
+ }
+ if (!count) {
+ exists = vfs_file_exist(conn,directory,NULL);
+ }
+ } else {
+ void *dirptr = NULL;
+ const char *dname;
+ pstring destname;
+
+ if (check_name(directory,conn))
+ dirptr = OpenDir(conn, directory, True);
+
+ if (dirptr) {
+ error = ERRbadfile;
+
+ if (strequal(mask,"????????.???"))
+ pstrcpy(mask,"*");
+
+ while ((dname = ReadDirName(dirptr))) {
+ pstring fname;
+ pstrcpy(fname,dname);
+
+ if(!mask_match(fname, mask, case_sensitive))
+ continue;
+
+ error = ERRnoaccess;
+ slprintf(fname,sizeof(fname)-1, "%s/%s",directory,dname);
+ pstrcpy(destname,newname);
+ if (resolve_wildcards(fname,destname) &&
+ copy_file(fname,destname,conn,ofun,
+ count,target_is_directory,&err))
+ count++;
+ DEBUG(3,("reply_copy : doing copy on %s -> %s\n",fname,destname));
+ }
+ CloseDir(dirptr);
+ }
+ }
+
+ if (count == 0) {
+ if(err) {
+ /* Error on close... */
+ errno = err;
+ END_PROFILE(SMBcopy);
+ return(UNIXERROR(ERRHRD,ERRgeneral));
+ }
+
+ if (exists) {
+ END_PROFILE(SMBcopy);
+ return ERROR_DOS(ERRDOS,error);
+ } else {
+ if((errno == ENOENT) && (bad_path1 || bad_path2)) {
+ unix_ERR_class = ERRDOS;
+ unix_ERR_code = ERRbadpath;
+ }
+ END_PROFILE(SMBcopy);
+ return(UNIXERROR(ERRDOS,error));
+ }
+ }
+
+ outsize = set_message(outbuf,1,0,True);
+ SSVAL(outbuf,smb_vwv0,count);
+
+ END_PROFILE(SMBcopy);
+ return(outsize);
+}
+
+/****************************************************************************
+ Reply to a setdir.
+****************************************************************************/
+
+int reply_setdir(connection_struct *conn, char *inbuf,char *outbuf, int dum_size, int dum_buffsize)
+{
+ int snum;
+ int outsize = 0;
+ BOOL ok = False;
+ pstring newdir;
+ NTSTATUS status;
+
+ START_PROFILE(pathworks_setdir);
+
+ snum = SNUM(conn);
+ if (!CAN_SETDIR(snum)) {
+ END_PROFILE(pathworks_setdir);
+ return ERROR_DOS(ERRDOS,ERRnoaccess);
+ }
+
+ srvstr_get_path(inbuf, newdir, smb_buf(inbuf) + 1, sizeof(newdir), 0, STR_TERMINATE, &status);
+ if (!NT_STATUS_IS_OK(status)) {
+ END_PROFILE(pathworks_setdir);
+ return ERROR_NT(status);
+ }
+
+ if (strlen(newdir) == 0) {
+ ok = True;
+ } else {
+ ok = vfs_directory_exist(conn,newdir,NULL);
+ if (ok)
+ string_set(&conn->connectpath,newdir);
+ }
+
+ if (!ok) {
+ END_PROFILE(pathworks_setdir);
+ return ERROR_DOS(ERRDOS,ERRbadpath);
+ }
+
+ outsize = set_message(outbuf,0,0,True);
+ SCVAL(outbuf,smb_reh,CVAL(inbuf,smb_reh));
+
+ DEBUG(3,("setdir %s\n", newdir));
+
+ END_PROFILE(pathworks_setdir);
+ return(outsize);
+}
+
+/****************************************************************************
+ Get a lock pid, dealing with large count requests.
+****************************************************************************/
+
+uint16 get_lock_pid( char *data, int data_offset, BOOL large_file_format)
+{
+ if(!large_file_format)
+ return SVAL(data,SMB_LPID_OFFSET(data_offset));
+ else
+ return SVAL(data,SMB_LARGE_LPID_OFFSET(data_offset));
+}
+
+/****************************************************************************
+ Get a lock count, dealing with large count requests.
+****************************************************************************/
+
+SMB_BIG_UINT get_lock_count( char *data, int data_offset, BOOL large_file_format)
+{
+ SMB_BIG_UINT count = 0;
+
+ if(!large_file_format) {
+ count = (SMB_BIG_UINT)IVAL(data,SMB_LKLEN_OFFSET(data_offset));
+ } else {
+
+#if defined(HAVE_LONGLONG)
+ count = (((SMB_BIG_UINT) IVAL(data,SMB_LARGE_LKLEN_OFFSET_HIGH(data_offset))) << 32) |
+ ((SMB_BIG_UINT) IVAL(data,SMB_LARGE_LKLEN_OFFSET_LOW(data_offset)));
+#else /* HAVE_LONGLONG */
+
+ /*
+ * NT4.x seems to be broken in that it sends large file (64 bit)
+ * lockingX calls even if the CAP_LARGE_FILES was *not*
+ * negotiated. For boxes without large unsigned ints truncate the
+ * lock count by dropping the top 32 bits.
+ */
+
+ if(IVAL(data,SMB_LARGE_LKLEN_OFFSET_HIGH(data_offset)) != 0) {
+ DEBUG(3,("get_lock_count: truncating lock count (high)0x%x (low)0x%x to just low count.\n",
+ (unsigned int)IVAL(data,SMB_LARGE_LKLEN_OFFSET_HIGH(data_offset)),
+ (unsigned int)IVAL(data,SMB_LARGE_LKLEN_OFFSET_LOW(data_offset)) ));
+ SIVAL(data,SMB_LARGE_LKLEN_OFFSET_HIGH(data_offset),0);
+ }
+
+ count = (SMB_BIG_UINT)IVAL(data,SMB_LARGE_LKLEN_OFFSET_LOW(data_offset));
+#endif /* HAVE_LONGLONG */
+ }
+
+ return count;
+}
+
+#if !defined(HAVE_LONGLONG)
+/****************************************************************************
+ Pathetically try and map a 64 bit lock offset into 31 bits. I hate Windows :-).
+****************************************************************************/
+
+static uint32 map_lock_offset(uint32 high, uint32 low)
+{
+ unsigned int i;
+ uint32 mask = 0;
+ uint32 highcopy = high;
+
+ /*
+ * Try and find out how many significant bits there are in high.
+ */
+
+ for(i = 0; highcopy; i++)
+ highcopy >>= 1;
+
+ /*
+ * We use 31 bits not 32 here as POSIX
+ * lock offsets may not be negative.
+ */
+
+ mask = (~0) << (31 - i);
+
+ if(low & mask)
+ return 0; /* Fail. */
+
+ high <<= (31 - i);
+
+ return (high|low);
+}
+#endif /* !defined(HAVE_LONGLONG) */
+
+/****************************************************************************
+ Get a lock offset, dealing with large offset requests.
+****************************************************************************/
+
+SMB_BIG_UINT get_lock_offset( char *data, int data_offset, BOOL large_file_format, BOOL *err)
+{
+ SMB_BIG_UINT offset = 0;
+
+ *err = False;
+
+ if(!large_file_format) {
+ offset = (SMB_BIG_UINT)IVAL(data,SMB_LKOFF_OFFSET(data_offset));
+ } else {
+
+#if defined(HAVE_LONGLONG)
+ offset = (((SMB_BIG_UINT) IVAL(data,SMB_LARGE_LKOFF_OFFSET_HIGH(data_offset))) << 32) |
+ ((SMB_BIG_UINT) IVAL(data,SMB_LARGE_LKOFF_OFFSET_LOW(data_offset)));
+#else /* HAVE_LONGLONG */
+
+ /*
+ * NT4.x seems to be broken in that it sends large file (64 bit)
+ * lockingX calls even if the CAP_LARGE_FILES was *not*
+ * negotiated. For boxes without large unsigned ints mangle the
+ * lock offset by mapping the top 32 bits onto the lower 32.
+ */
+
+ if(IVAL(data,SMB_LARGE_LKOFF_OFFSET_HIGH(data_offset)) != 0) {
+ uint32 low = IVAL(data,SMB_LARGE_LKOFF_OFFSET_LOW(data_offset));
+ uint32 high = IVAL(data,SMB_LARGE_LKOFF_OFFSET_HIGH(data_offset));
+ uint32 new_low = 0;
+
+ if((new_low = map_lock_offset(high, low)) == 0) {
+ *err = True;
+ return (SMB_BIG_UINT)-1;
+ }
+
+ DEBUG(3,("get_lock_offset: truncating lock offset (high)0x%x (low)0x%x to offset 0x%x.\n",
+ (unsigned int)high, (unsigned int)low, (unsigned int)new_low ));
+ SIVAL(data,SMB_LARGE_LKOFF_OFFSET_HIGH(data_offset),0);
+ SIVAL(data,SMB_LARGE_LKOFF_OFFSET_LOW(data_offset),new_low);
+ }
+
+ offset = (SMB_BIG_UINT)IVAL(data,SMB_LARGE_LKOFF_OFFSET_LOW(data_offset));
+#endif /* HAVE_LONGLONG */
+ }
+
+ return offset;
+}
+
+/****************************************************************************
+ Reply to a lockingX request.
+****************************************************************************/
+
+int reply_lockingX(connection_struct *conn, char *inbuf,char *outbuf,int length,int bufsize)
+{
+ files_struct *fsp = file_fsp(inbuf,smb_vwv2);
+ unsigned char locktype = CVAL(inbuf,smb_vwv3);
+ unsigned char oplocklevel = CVAL(inbuf,smb_vwv3+1);
+ uint16 num_ulocks = SVAL(inbuf,smb_vwv6);
+ uint16 num_locks = SVAL(inbuf,smb_vwv7);
+ SMB_BIG_UINT count = 0, offset = 0;
+ uint16 lock_pid;
+ int32 lock_timeout = IVAL(inbuf,smb_vwv4);
+ int i;
+ char *data;
+ BOOL large_file_format = (locktype & LOCKING_ANDX_LARGE_FILES)?True:False;
+ BOOL err;
+ BOOL my_lock_ctx = False;
+ NTSTATUS status;
+
+ START_PROFILE(SMBlockingX);
+
+ CHECK_FSP(fsp,conn);
+
+ data = smb_buf(inbuf);
+
+ if (locktype & (LOCKING_ANDX_CANCEL_LOCK | LOCKING_ANDX_CHANGE_LOCKTYPE)) {
+ /* we don't support these - and CANCEL_LOCK makes w2k
+ and XP reboot so I don't really want to be
+ compatible! (tridge) */
+ return ERROR_NT(NT_STATUS_NOT_SUPPORTED);
+ }
+
+ /* Check if this is an oplock break on a file
+ we have granted an oplock on.
+ */
+ if ((locktype & LOCKING_ANDX_OPLOCK_RELEASE)) {
+ /* Client can insist on breaking to none. */
+ BOOL break_to_none = (oplocklevel == 0);
+
+ DEBUG(5,("reply_lockingX: oplock break reply (%u) from client for fnum = %d\n",
+ (unsigned int)oplocklevel, fsp->fnum ));
+
+ /*
+ * Make sure we have granted an exclusive or batch oplock on this file.
+ */
+
+ if(!EXCLUSIVE_OPLOCK_TYPE(fsp->oplock_type)) {
+ DEBUG(0,("reply_lockingX: Error : oplock break from client for fnum = %d and \
+no oplock granted on this file (%s).\n", fsp->fnum, fsp->fsp_name));
+
+ /* if this is a pure oplock break request then don't send a reply */
+ if (num_locks == 0 && num_ulocks == 0) {
+ END_PROFILE(SMBlockingX);
+ return -1;
+ } else {
+ END_PROFILE(SMBlockingX);
+ return ERROR_DOS(ERRDOS,ERRlock);
+ }
+ }
+
+ if (remove_oplock(fsp, break_to_none) == False) {
+ DEBUG(0,("reply_lockingX: error in removing oplock on file %s\n",
+ fsp->fsp_name ));
+ }
+
+ /* if this is a pure oplock break request then don't send a reply */
+ if (num_locks == 0 && num_ulocks == 0) {
+ /* Sanity check - ensure a pure oplock break is not a
+ chained request. */
+ if(CVAL(inbuf,smb_vwv0) != 0xff)
+ DEBUG(0,("reply_lockingX: Error : pure oplock break is a chained %d request !\n",
+ (unsigned int)CVAL(inbuf,smb_vwv0) ));
+ END_PROFILE(SMBlockingX);
+ return -1;
+ }
+ }
+
+ /*
+ * We do this check *after* we have checked this is not a oplock break
+ * response message. JRA.
+ */
+
+ release_level_2_oplocks_on_change(fsp);
+
+ /* Data now points at the beginning of the list
+ of smb_unlkrng structs */
+ for(i = 0; i < (int)num_ulocks; i++) {
+ lock_pid = get_lock_pid( data, i, large_file_format);
+ count = get_lock_count( data, i, large_file_format);
+ offset = get_lock_offset( data, i, large_file_format, &err);
+
+ /*
+ * There is no error code marked "stupid client bug".... :-).
+ */
+ if(err) {
+ END_PROFILE(SMBlockingX);
+ return ERROR_DOS(ERRDOS,ERRnoaccess);
+ }
+
+ DEBUG(10,("reply_lockingX: unlock start=%.0f, len=%.0f for pid %u, file %s\n",
+ (double)offset, (double)count, (unsigned int)lock_pid, fsp->fsp_name ));
+
+ status = do_unlock(fsp,conn,lock_pid,count,offset);
+ if (NT_STATUS_V(status)) {
+ END_PROFILE(SMBlockingX);
+ return ERROR_NT(status);
+ }
+ }
+
+ /* Setup the timeout in seconds. */
+
+ lock_timeout = ((lock_timeout == -1) ? -1 : (lock_timeout+999)/1000);
+
+ /* Now do any requested locks */
+ data += ((large_file_format ? 20 : 10)*num_ulocks);
+
+ /* Data now points at the beginning of the list
+ of smb_lkrng structs */
+
+ for(i = 0; i < (int)num_locks; i++) {
+ lock_pid = get_lock_pid( data, i, large_file_format);
+ count = get_lock_count( data, i, large_file_format);
+ offset = get_lock_offset( data, i, large_file_format, &err);
+
+ /*
+ * There is no error code marked "stupid client bug".... :-).
+ */
+ if(err) {
+ END_PROFILE(SMBlockingX);
+ return ERROR_DOS(ERRDOS,ERRnoaccess);
+ }
+
+ DEBUG(10,("reply_lockingX: lock start=%.0f, len=%.0f for pid %u, file %s timeout = %d\n",
+ (double)offset, (double)count, (unsigned int)lock_pid,
+ fsp->fsp_name, (int)lock_timeout ));
+
+ status = do_lock_spin(fsp,conn,lock_pid, count,offset,
+ ((locktype & 1) ? READ_LOCK : WRITE_LOCK), &my_lock_ctx);
+ if (NT_STATUS_V(status)) {
+ /*
+ * Interesting fact found by IFSTEST /t LockOverlappedTest...
+ * Even if it's our own lock context, we need to wait here as
+ * there may be an unlock on the way.
+ * So I removed a "&& !my_lock_ctx" from the following
+ * if statement. JRA.
+ */
+ if ((lock_timeout != 0) && lp_blocking_locks(SNUM(conn)) && ERROR_WAS_LOCK_DENIED(status)) {
+ /*
+ * A blocking lock was requested. Package up
+ * this smb into a queued request and push it
+ * onto the blocking lock queue.
+ */
+ if(push_blocking_lock_request(inbuf, length, lock_timeout, i, lock_pid, offset, count)) {
+ END_PROFILE(SMBlockingX);
+ return -1;
+ }
+ }
+ break;
+ }
+ }
+
+ /* If any of the above locks failed, then we must unlock
+ all of the previous locks (X/Open spec). */
+ if (i != num_locks && num_locks != 0) {
+ /*
+ * Ensure we don't do a remove on the lock that just failed,
+ * as under POSIX rules, if we have a lock already there, we
+ * will delete it (and we shouldn't) .....
+ */
+ for(i--; i >= 0; i--) {
+ lock_pid = get_lock_pid( data, i, large_file_format);
+ count = get_lock_count( data, i, large_file_format);
+ offset = get_lock_offset( data, i, large_file_format, &err);
+
+ /*
+ * There is no error code marked "stupid client bug".... :-).
+ */
+ if(err) {
+ END_PROFILE(SMBlockingX);
+ return ERROR_DOS(ERRDOS,ERRnoaccess);
+ }
+
+ do_unlock(fsp,conn,lock_pid,count,offset);
+ }
+ END_PROFILE(SMBlockingX);
+ return ERROR_NT(status);
+ }
+
+ set_message(outbuf,2,0,True);
+
+ DEBUG( 3, ( "lockingX fnum=%d type=%d num_locks=%d num_ulocks=%d\n",
+ fsp->fnum, (unsigned int)locktype, num_locks, num_ulocks ) );
+
+ END_PROFILE(SMBlockingX);
+ return chain_reply(inbuf,outbuf,length,bufsize);
+}
+
+/****************************************************************************
+ Reply to a SMBreadbmpx (read block multiplex) request.
+****************************************************************************/
+
+int reply_readbmpx(connection_struct *conn, char *inbuf,char *outbuf,int length,int bufsize)
+{
+ ssize_t nread = -1;
+ ssize_t total_read;
+ char *data;
+ SMB_OFF_T startpos;
+ int outsize;
+ size_t maxcount;
+ int max_per_packet;
+ size_t tcount;
+ int pad;
+ files_struct *fsp = file_fsp(inbuf,smb_vwv0);
+ START_PROFILE(SMBreadBmpx);
+
+ /* this function doesn't seem to work - disable by default */
+ if (!lp_readbmpx()) {
+ END_PROFILE(SMBreadBmpx);
+ return ERROR_DOS(ERRSRV,ERRuseSTD);
+ }
+
+ outsize = set_message(outbuf,8,0,True);
+
+ CHECK_FSP(fsp,conn);
+ CHECK_READ(fsp);
+
+ startpos = IVAL_TO_SMB_OFF_T(inbuf,smb_vwv1);
+ maxcount = SVAL(inbuf,smb_vwv3);
+
+ data = smb_buf(outbuf);
+ pad = ((long)data)%4;
+ if (pad)
+ pad = 4 - pad;
+ data += pad;
+
+ max_per_packet = bufsize-(outsize+pad);
+ tcount = maxcount;
+ total_read = 0;
+
+ if (is_locked(fsp,conn,(SMB_BIG_UINT)maxcount,(SMB_BIG_UINT)startpos, READ_LOCK,False)) {
+ END_PROFILE(SMBreadBmpx);
+ return ERROR_DOS(ERRDOS,ERRlock);
+ }
+
+ do {
+ size_t N = MIN(max_per_packet,tcount-total_read);
+
+ nread = read_file(fsp,data,startpos,N);
+
+ if (nread <= 0)
+ nread = 0;
+
+ if (nread < (ssize_t)N)
+ tcount = total_read + nread;
+
+ set_message(outbuf,8,nread,False);
+ SIVAL(outbuf,smb_vwv0,startpos);
+ SSVAL(outbuf,smb_vwv2,tcount);
+ SSVAL(outbuf,smb_vwv6,nread);
+ SSVAL(outbuf,smb_vwv7,smb_offset(data,outbuf));
+
+ if (!send_smb(smbd_server_fd(),outbuf))
+ exit_server("reply_readbmpx: send_smb failed.");
+
+ total_read += nread;
+ startpos += nread;
+ } while (total_read < (ssize_t)tcount);
+
+ END_PROFILE(SMBreadBmpx);
+ return(-1);
+}
+
+/****************************************************************************
+ Reply to a SMBsetattrE.
+****************************************************************************/
+
+int reply_setattrE(connection_struct *conn, char *inbuf,char *outbuf, int size, int dum_buffsize)
+{
+ struct utimbuf unix_times;
+ int outsize = 0;
+ files_struct *fsp = file_fsp(inbuf,smb_vwv0);
+ START_PROFILE(SMBsetattrE);
+
+ outsize = set_message(outbuf,0,0,True);
+
+ if(!fsp || (fsp->conn != conn)) {
+ END_PROFILE(SMBgetattrE);
+ return ERROR_DOS(ERRDOS,ERRbadfid);
+ }
+
+ /*
+ * Convert the DOS times into unix times. Ignore create
+ * time as UNIX can't set this.
+ */
+
+ unix_times.actime = make_unix_date2(inbuf+smb_vwv3);
+ unix_times.modtime = make_unix_date2(inbuf+smb_vwv5);
+
+ /*
+ * Patch from Ray Frush <frush@engr.colostate.edu>
+ * Sometimes times are sent as zero - ignore them.
+ */
+
+ if ((unix_times.actime == 0) && (unix_times.modtime == 0)) {
+ /* Ignore request */
+ if( DEBUGLVL( 3 ) ) {
+ dbgtext( "reply_setattrE fnum=%d ", fsp->fnum);
+ dbgtext( "ignoring zero request - not setting timestamps of 0\n" );
+ }
+ END_PROFILE(SMBsetattrE);
+ return(outsize);
+ } else if ((unix_times.actime != 0) && (unix_times.modtime == 0)) {
+ /* set modify time = to access time if modify time was 0 */
+ unix_times.modtime = unix_times.actime;
+ }
+
+ /* Set the date on this file */
+ if(file_utime(conn, fsp->fsp_name, &unix_times)) {
+ END_PROFILE(SMBsetattrE);
+ return ERROR_DOS(ERRDOS,ERRnoaccess);
+ }
+
+ DEBUG( 3, ( "reply_setattrE fnum=%d actime=%d modtime=%d\n",
+ fsp->fnum, (int)unix_times.actime, (int)unix_times.modtime ) );
+
+ END_PROFILE(SMBsetattrE);
+ return(outsize);
+}
+
+
+/* Back from the dead for OS/2..... JRA. */
+
+/****************************************************************************
+ Reply to a SMBwritebmpx (write block multiplex primary) request.
+****************************************************************************/
+
+int reply_writebmpx(connection_struct *conn, char *inbuf,char *outbuf, int size, int dum_buffsize)
+{
+ size_t numtowrite;
+ ssize_t nwritten = -1;
+ int outsize = 0;
+ SMB_OFF_T startpos;
+ size_t tcount;
+ BOOL write_through;
+ int smb_doff;
+ char *data;
+ files_struct *fsp = file_fsp(inbuf,smb_vwv0);
+ START_PROFILE(SMBwriteBmpx);
+
+ CHECK_FSP(fsp,conn);
+ CHECK_WRITE(fsp);
+ CHECK_ERROR(fsp);
+
+ tcount = SVAL(inbuf,smb_vwv1);
+ startpos = IVAL_TO_SMB_OFF_T(inbuf,smb_vwv3);
+ write_through = BITSETW(inbuf+smb_vwv7,0);
+ numtowrite = SVAL(inbuf,smb_vwv10);
+ smb_doff = SVAL(inbuf,smb_vwv11);
+
+ data = smb_base(inbuf) + smb_doff;
+
+ /* If this fails we need to send an SMBwriteC response,
+ not an SMBwritebmpx - set this up now so we don't forget */
+ SCVAL(outbuf,smb_com,SMBwritec);
+
+ if (is_locked(fsp,conn,(SMB_BIG_UINT)tcount,(SMB_BIG_UINT)startpos,WRITE_LOCK,False)) {
+ END_PROFILE(SMBwriteBmpx);
+ return(ERROR_DOS(ERRDOS,ERRlock));
+ }
+
+ nwritten = write_file(fsp,data,startpos,numtowrite);
+
+ if(lp_syncalways(SNUM(conn)) || write_through)
+ sync_file(conn,fsp);
+
+ if(nwritten < (ssize_t)numtowrite) {
+ END_PROFILE(SMBwriteBmpx);
+ return(UNIXERROR(ERRHRD,ERRdiskfull));
+ }
+
+ /* If the maximum to be written to this file
+ is greater than what we just wrote then set
+ up a secondary struct to be attached to this
+ fd, we will use this to cache error messages etc. */
+
+ if((ssize_t)tcount > nwritten) {
+ write_bmpx_struct *wbms;
+ if(fsp->wbmpx_ptr != NULL)
+ wbms = fsp->wbmpx_ptr; /* Use an existing struct */
+ else
+ wbms = (write_bmpx_struct *)malloc(sizeof(write_bmpx_struct));
+ if(!wbms) {
+ DEBUG(0,("Out of memory in reply_readmpx\n"));
+ END_PROFILE(SMBwriteBmpx);
+ return(ERROR_DOS(ERRSRV,ERRnoresource));
+ }
+ wbms->wr_mode = write_through;
+ wbms->wr_discard = False; /* No errors yet */
+ wbms->wr_total_written = nwritten;
+ wbms->wr_errclass = 0;
+ wbms->wr_error = 0;
+ fsp->wbmpx_ptr = wbms;
+ }
+
+ /* We are returning successfully, set the message type back to
+ SMBwritebmpx */
+ SCVAL(outbuf,smb_com,SMBwriteBmpx);
+
+ outsize = set_message(outbuf,1,0,True);
+
+ SSVALS(outbuf,smb_vwv0,-1); /* We don't support smb_remaining */
+
+ DEBUG( 3, ( "writebmpx fnum=%d num=%d wrote=%d\n",
+ fsp->fnum, (int)numtowrite, (int)nwritten ) );
+
+ if (write_through && tcount==nwritten) {
+ /* We need to send both a primary and a secondary response */
+ smb_setlen(outbuf,outsize - 4);
+ if (!send_smb(smbd_server_fd(),outbuf))
+ exit_server("reply_writebmpx: send_smb failed.");
+
+ /* Now the secondary */
+ outsize = set_message(outbuf,1,0,True);
+ SCVAL(outbuf,smb_com,SMBwritec);
+ SSVAL(outbuf,smb_vwv0,nwritten);
+ }
+
+ END_PROFILE(SMBwriteBmpx);
+ return(outsize);
+}
+
+/****************************************************************************
+ Reply to a SMBwritebs (write block multiplex secondary) request.
+****************************************************************************/
+
+int reply_writebs(connection_struct *conn, char *inbuf,char *outbuf, int dum_size, int dum_buffsize)
+{
+ size_t numtowrite;
+ ssize_t nwritten = -1;
+ int outsize = 0;
+ SMB_OFF_T startpos;
+ size_t tcount;
+ BOOL write_through;
+ int smb_doff;
+ char *data;
+ write_bmpx_struct *wbms;
+ BOOL send_response = False;
+ files_struct *fsp = file_fsp(inbuf,smb_vwv0);
+ START_PROFILE(SMBwriteBs);
+
+ CHECK_FSP(fsp,conn);
+ CHECK_WRITE(fsp);
+
+ tcount = SVAL(inbuf,smb_vwv1);
+ startpos = IVAL_TO_SMB_OFF_T(inbuf,smb_vwv2);
+ numtowrite = SVAL(inbuf,smb_vwv6);
+ smb_doff = SVAL(inbuf,smb_vwv7);
+
+ data = smb_base(inbuf) + smb_doff;
+
+ /* We need to send an SMBwriteC response, not an SMBwritebs */
+ SCVAL(outbuf,smb_com,SMBwritec);
+
+ /* This fd should have an auxiliary struct attached,
+ check that it does */
+ wbms = fsp->wbmpx_ptr;
+ if(!wbms) {
+ END_PROFILE(SMBwriteBs);
+ return(-1);
+ }
+
+ /* If write through is set we can return errors, else we must cache them */
+ write_through = wbms->wr_mode;
+
+ /* Check for an earlier error */
+ if(wbms->wr_discard) {
+ END_PROFILE(SMBwriteBs);
+ return -1; /* Just discard the packet */
+ }
+
+ nwritten = write_file(fsp,data,startpos,numtowrite);
+
+ if(lp_syncalways(SNUM(conn)) || write_through)
+ sync_file(conn,fsp);
+
+ if (nwritten < (ssize_t)numtowrite) {
+ if(write_through) {
+ /* We are returning an error - we can delete the aux struct */
+ if (wbms)
+ free((char *)wbms);
+ fsp->wbmpx_ptr = NULL;
+ END_PROFILE(SMBwriteBs);
+ return(ERROR_DOS(ERRHRD,ERRdiskfull));
+ }
+ END_PROFILE(SMBwriteBs);
+ return(CACHE_ERROR(wbms,ERRHRD,ERRdiskfull));
+ }
+
+ /* Increment the total written, if this matches tcount
+ we can discard the auxiliary struct (hurrah !) and return a writeC */
+ wbms->wr_total_written += nwritten;
+ if(wbms->wr_total_written >= tcount) {
+ if (write_through) {
+ outsize = set_message(outbuf,1,0,True);
+ SSVAL(outbuf,smb_vwv0,wbms->wr_total_written);
+ send_response = True;
+ }
+
+ free((char *)wbms);
+ fsp->wbmpx_ptr = NULL;
+ }
+
+ if(send_response) {
+ END_PROFILE(SMBwriteBs);
+ return(outsize);
+ }
+
+ END_PROFILE(SMBwriteBs);
+ return(-1);
+}
+
+/****************************************************************************
+ Reply to a SMBgetattrE.
+****************************************************************************/
+
+int reply_getattrE(connection_struct *conn, char *inbuf,char *outbuf, int size, int dum_buffsize)
+{
+ SMB_STRUCT_STAT sbuf;
+ int outsize = 0;
+ int mode;
+ files_struct *fsp = file_fsp(inbuf,smb_vwv0);
+ START_PROFILE(SMBgetattrE);
+
+ outsize = set_message(outbuf,11,0,True);
+
+ if(!fsp || (fsp->conn != conn)) {
+ END_PROFILE(SMBgetattrE);
+ return ERROR_DOS(ERRDOS,ERRbadfid);
+ }
+
+ /* Do an fstat on this file */
+ if(fsp_stat(fsp, &sbuf)) {
+ END_PROFILE(SMBgetattrE);
+ return(UNIXERROR(ERRDOS,ERRnoaccess));
+ }
+
+ mode = dos_mode(conn,fsp->fsp_name,&sbuf);
+
+ /*
+ * Convert the times into dos times. Set create
+ * date to be last modify date as UNIX doesn't save
+ * this.
+ */
+
+ put_dos_date2(outbuf,smb_vwv0,get_create_time(&sbuf,lp_fake_dir_create_times(SNUM(conn))));
+ put_dos_date2(outbuf,smb_vwv2,sbuf.st_atime);
+ put_dos_date2(outbuf,smb_vwv4,sbuf.st_mtime);
+
+ if (mode & aDIR) {
+ SIVAL(outbuf,smb_vwv6,0);
+ SIVAL(outbuf,smb_vwv8,0);
+ } else {
+ uint32 allocation_size = get_allocation_size(fsp, &sbuf);
+ SIVAL(outbuf,smb_vwv6,(uint32)sbuf.st_size);
+ SIVAL(outbuf,smb_vwv8,allocation_size);
+ }
+ SSVAL(outbuf,smb_vwv10, mode);
+
+ DEBUG( 3, ( "reply_getattrE fnum=%d\n", fsp->fnum));
+
+ END_PROFILE(SMBgetattrE);
+ return(outsize);
+}
diff --git a/source/smbd/sec_ctx.c b/source/smbd/sec_ctx.c
new file mode 100644
index 00000000000..fee71b5ec96
--- /dev/null
+++ b/source/smbd/sec_ctx.c
@@ -0,0 +1,449 @@
+/*
+ Unix SMB/CIFS implementation.
+ uid/user handling
+ 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 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"
+
+extern struct current_user current_user;
+
+struct sec_ctx {
+ uid_t uid;
+ uid_t gid;
+ int ngroups;
+ gid_t *groups;
+ NT_USER_TOKEN *token;
+ PRIVILEGE_SET *privs;
+};
+
+/* A stack of security contexts. We include the current context as being
+ the first one, so there is room for another MAX_SEC_CTX_DEPTH more. */
+
+static struct sec_ctx sec_ctx_stack[MAX_SEC_CTX_DEPTH + 1];
+static int sec_ctx_stack_ndx;
+
+/****************************************************************************
+ Become the specified uid.
+****************************************************************************/
+
+static BOOL become_uid(uid_t uid)
+{
+ /* Check for dodgy uid values */
+
+ if (uid == (uid_t)-1 ||
+ ((sizeof(uid_t) == 2) && (uid == (uid_t)65535))) {
+ static int done;
+
+ if (!done) {
+ DEBUG(1,("WARNING: using uid %d is a security risk\n",
+ (int)uid));
+ done = 1;
+ }
+ }
+
+ /* Set effective user id */
+
+ set_effective_uid(uid);
+
+ DO_PROFILE_INC(uid_changes);
+ return True;
+}
+
+/****************************************************************************
+ Become the specified gid.
+****************************************************************************/
+
+static BOOL become_gid(gid_t gid)
+{
+ /* Check for dodgy gid values */
+
+ if (gid == (gid_t)-1 || ((sizeof(gid_t) == 2) &&
+ (gid == (gid_t)65535))) {
+ static int done;
+
+ if (!done) {
+ DEBUG(1,("WARNING: using gid %d is a security risk\n",
+ (int)gid));
+ done = 1;
+ }
+ }
+
+ /* Set effective group id */
+
+ set_effective_gid(gid);
+ return True;
+}
+
+/****************************************************************************
+ Become the specified uid and gid.
+****************************************************************************/
+
+static BOOL become_id(uid_t uid, gid_t gid)
+{
+ return become_gid(gid) && become_uid(uid);
+}
+
+/****************************************************************************
+ Drop back to root privileges in order to change to another user.
+****************************************************************************/
+
+static void gain_root(void)
+{
+ if (non_root_mode()) {
+ return;
+ }
+
+ if (geteuid() != 0) {
+ set_effective_uid(0);
+
+ if (geteuid() != 0) {
+ DEBUG(0,
+ ("Warning: You appear to have a trapdoor "
+ "uid system\n"));
+ }
+ }
+
+ if (getegid() != 0) {
+ set_effective_gid(0);
+
+ if (getegid() != 0) {
+ DEBUG(0,
+ ("Warning: You appear to have a trapdoor "
+ "gid system\n"));
+ }
+ }
+}
+
+/****************************************************************************
+ Get the list of current groups.
+****************************************************************************/
+
+int get_current_groups(gid_t gid, int *p_ngroups, gid_t **p_groups)
+{
+ int i;
+ gid_t grp;
+ int ngroups;
+ gid_t *groups = NULL;
+
+ (*p_ngroups) = 0;
+ (*p_groups) = NULL;
+
+ /* this looks a little strange, but is needed to cope with
+ systems that put the current egid in the group list
+ returned from getgroups() (tridge) */
+ save_re_gid();
+ set_effective_gid(gid);
+ setgid(gid);
+
+ ngroups = sys_getgroups(0,&grp);
+ if (ngroups <= 0) {
+ goto fail;
+ }
+
+ if((groups = (gid_t *)malloc(sizeof(gid_t)*(ngroups+1))) == NULL) {
+ DEBUG(0,("setup_groups malloc fail !\n"));
+ goto fail;
+ }
+
+ if ((ngroups = sys_getgroups(ngroups,groups)) == -1) {
+ goto fail;
+ }
+
+ restore_re_gid();
+
+ (*p_ngroups) = ngroups;
+ (*p_groups) = groups;
+
+ DEBUG( 3, ( "get_current_groups: user is in %u groups: ", ngroups));
+ for (i = 0; i < ngroups; i++ ) {
+ DEBUG( 3, ( "%s%d", (i ? ", " : ""), (int)groups[i] ) );
+ }
+ DEBUG( 3, ( "\n" ) );
+
+ return ngroups;
+
+fail:
+ SAFE_FREE(groups);
+ restore_re_gid();
+ return -1;
+}
+
+/****************************************************************************
+ Initialize the groups a user belongs to.
+****************************************************************************/
+
+BOOL initialise_groups(char *user, uid_t uid, gid_t gid)
+{
+ struct sec_ctx *prev_ctx_p;
+ BOOL result = True;
+
+ if (non_root_mode()) {
+ return True;
+ }
+
+ become_root();
+
+ /* Call initgroups() to get user groups */
+
+ if (winbind_initgroups(user,gid) == -1) {
+ DEBUG(0,("Unable to initgroups. Error was %s\n", strerror(errno) ));
+ if (getuid() == 0) {
+ if (gid < 0 || gid > 32767 || uid < 0 || uid > 32767) {
+ DEBUG(0,("This is probably a problem with the account %s\n", user));
+ }
+ }
+ result = False;
+ goto done;
+ }
+
+ /* Store groups in previous user's security context. This will
+ always work as the become_root() call increments the stack
+ pointer. */
+
+ prev_ctx_p = &sec_ctx_stack[sec_ctx_stack_ndx - 1];
+
+ SAFE_FREE(prev_ctx_p->groups);
+ prev_ctx_p->ngroups = 0;
+
+ get_current_groups(gid, &prev_ctx_p->ngroups, &prev_ctx_p->groups);
+
+ done:
+ unbecome_root();
+
+ return result;
+}
+
+/****************************************************************************
+ Create a new security context on the stack. It is the same as the old
+ one. User changes are done using the set_sec_ctx() function.
+****************************************************************************/
+
+BOOL push_sec_ctx(void)
+{
+ struct sec_ctx *ctx_p;
+
+ /* Check we don't overflow our stack */
+
+ if (sec_ctx_stack_ndx == MAX_SEC_CTX_DEPTH) {
+ DEBUG(0, ("Security context stack overflow!\n"));
+ smb_panic("Security context stack overflow!\n");
+ }
+
+ /* Store previous user context */
+
+ sec_ctx_stack_ndx++;
+
+ ctx_p = &sec_ctx_stack[sec_ctx_stack_ndx];
+
+ ctx_p->uid = geteuid();
+ ctx_p->gid = getegid();
+
+ DEBUG(3, ("push_sec_ctx(%u, %u) : sec_ctx_stack_ndx = %d\n",
+ (unsigned int)ctx_p->uid, (unsigned int)ctx_p->gid, sec_ctx_stack_ndx ));
+
+ ctx_p->token = dup_nt_token(sec_ctx_stack[sec_ctx_stack_ndx-1].token);
+
+ ctx_p->ngroups = sys_getgroups(0, NULL);
+
+ if (ctx_p->ngroups != 0) {
+ if (!(ctx_p->groups = malloc(ctx_p->ngroups * sizeof(gid_t)))) {
+ DEBUG(0, ("Out of memory in push_sec_ctx()\n"));
+ delete_nt_token(&ctx_p->token);
+ return False;
+ }
+
+ sys_getgroups(ctx_p->ngroups, ctx_p->groups);
+ } else {
+ ctx_p->groups = NULL;
+ }
+
+ init_privilege(&ctx_p->privs);
+ if (! NT_STATUS_IS_OK(dup_priv_set(ctx_p->privs, sec_ctx_stack[sec_ctx_stack_ndx-1].privs))) {
+ DEBUG(0, ("Out of memory on dup_priv_set() in push_sec_ctx()\n"));
+ delete_nt_token(&ctx_p->token);
+ destroy_privilege(&ctx_p->privs);
+ return False;
+ }
+
+ return True;
+}
+
+/****************************************************************************
+ Set the current security context to a given user.
+****************************************************************************/
+
+void set_sec_ctx(uid_t uid, gid_t gid, int ngroups, gid_t *groups, NT_USER_TOKEN *token, PRIVILEGE_SET *privs)
+{
+ struct sec_ctx *ctx_p = &sec_ctx_stack[sec_ctx_stack_ndx];
+
+ /* Set the security context */
+
+ DEBUG(3, ("setting sec ctx (%u, %u) - sec_ctx_stack_ndx = %d\n",
+ (unsigned int)uid, (unsigned int)gid, sec_ctx_stack_ndx));
+
+ debug_nt_user_token(DBGC_CLASS, 5, token);
+ debug_unix_user_token(DBGC_CLASS, 5, uid, gid, ngroups, groups);
+
+ gain_root();
+
+#ifdef HAVE_SETGROUPS
+ sys_setgroups(ngroups, groups);
+#endif
+
+ ctx_p->ngroups = ngroups;
+
+ SAFE_FREE(ctx_p->groups);
+ if (token && (token == ctx_p->token))
+ smb_panic("DUPLICATE_TOKEN");
+
+ delete_nt_token(&ctx_p->token);
+ if (ctx_p->privs)
+ reset_privilege(ctx_p->privs);
+ else
+ init_privilege(&ctx_p->privs);
+
+ ctx_p->groups = memdup(groups, sizeof(gid_t) * ngroups);
+ ctx_p->token = dup_nt_token(token);
+ dup_priv_set(ctx_p->privs, privs);
+
+ become_id(uid, gid);
+
+ ctx_p->uid = uid;
+ ctx_p->gid = gid;
+
+ /* Update current_user stuff */
+
+ current_user.uid = uid;
+ current_user.gid = gid;
+ current_user.ngroups = ngroups;
+ current_user.groups = groups;
+ current_user.nt_user_token = ctx_p->token;
+ current_user.privs = ctx_p->privs;
+}
+
+/****************************************************************************
+ Become root context.
+****************************************************************************/
+
+void set_root_sec_ctx(void)
+{
+ /* May need to worry about supplementary groups at some stage */
+
+ set_sec_ctx(0, 0, 0, NULL, NULL, NULL);
+}
+
+/****************************************************************************
+ Pop a security context from the stack.
+****************************************************************************/
+
+BOOL pop_sec_ctx(void)
+{
+ struct sec_ctx *ctx_p;
+ struct sec_ctx *prev_ctx_p;
+
+ /* Check for stack underflow */
+
+ if (sec_ctx_stack_ndx == 0) {
+ DEBUG(0, ("Security context stack underflow!\n"));
+ smb_panic("Security context stack underflow!\n");
+ }
+
+ ctx_p = &sec_ctx_stack[sec_ctx_stack_ndx];
+
+ /* Clear previous user info */
+
+ ctx_p->uid = (uid_t)-1;
+ ctx_p->gid = (gid_t)-1;
+
+ SAFE_FREE(ctx_p->groups);
+ ctx_p->ngroups = 0;
+
+ delete_nt_token(&ctx_p->token);
+ destroy_privilege(&ctx_p->privs);
+
+ /* Pop back previous user */
+
+ sec_ctx_stack_ndx--;
+
+ gain_root();
+
+ prev_ctx_p = &sec_ctx_stack[sec_ctx_stack_ndx];
+
+#ifdef HAVE_SETGROUPS
+ sys_setgroups(prev_ctx_p->ngroups, prev_ctx_p->groups);
+#endif
+
+ become_id(prev_ctx_p->uid, prev_ctx_p->gid);
+
+ /* Update current_user stuff */
+
+ current_user.uid = prev_ctx_p->uid;
+ current_user.gid = prev_ctx_p->gid;
+ current_user.ngroups = prev_ctx_p->ngroups;
+ current_user.groups = prev_ctx_p->groups;
+ current_user.nt_user_token = prev_ctx_p->token;
+ current_user.privs = prev_ctx_p->privs;
+
+ DEBUG(3, ("pop_sec_ctx (%u, %u) - sec_ctx_stack_ndx = %d\n",
+ (unsigned int)geteuid(), (unsigned int)getegid(), sec_ctx_stack_ndx));
+
+ return True;
+}
+
+/* Initialise the security context system */
+
+void init_sec_ctx(void)
+{
+ int i;
+ struct sec_ctx *ctx_p;
+
+ /* Initialise security context stack */
+
+ memset(sec_ctx_stack, 0, sizeof(struct sec_ctx) * MAX_SEC_CTX_DEPTH);
+
+ for (i = 0; i < MAX_SEC_CTX_DEPTH; i++) {
+ sec_ctx_stack[i].uid = (uid_t)-1;
+ sec_ctx_stack[i].gid = (gid_t)-1;
+ }
+
+ /* Initialise first level of stack. It is the current context */
+ ctx_p = &sec_ctx_stack[0];
+
+ ctx_p->uid = geteuid();
+ ctx_p->gid = getegid();
+
+ get_current_groups(ctx_p->gid, &ctx_p->ngroups, &ctx_p->groups);
+
+ ctx_p->token = NULL; /* Maps to guest user. */
+ ctx_p->privs = NULL;
+
+ /* Initialise current_user global */
+
+ current_user.uid = ctx_p->uid;
+ current_user.gid = ctx_p->gid;
+ current_user.ngroups = ctx_p->ngroups;
+ current_user.groups = ctx_p->groups;
+
+ /* The conn and vuid are usually taken care of by other modules.
+ We initialise them here. */
+
+ current_user.conn = NULL;
+ current_user.vuid = UID_FIELD_INVALID;
+ current_user.nt_user_token = NULL;
+ current_user.privs = NULL;
+}
diff --git a/source/smbd/server.c b/source/smbd/server.c
new file mode 100644
index 00000000000..53d07fd905c
--- /dev/null
+++ b/source/smbd/server.c
@@ -0,0 +1,909 @@
+/*
+ Unix SMB/CIFS implementation.
+ Main SMB server routines
+ Copyright (C) Andrew Tridgell 1992-1998
+ Copyright (C) Martin Pool 2002
+ Copyright (C) Jelmer Vernooij 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 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"
+
+static int am_parent = 1;
+
+/* the last message the was processed */
+int last_message = -1;
+
+/* a useful macro to debug the last message processed */
+#define LAST_MESSAGE() smb_fn_name(last_message)
+
+extern pstring user_socket_options;
+extern SIG_ATOMIC_T got_sig_term;
+extern SIG_ATOMIC_T reload_after_sighup;
+
+#ifdef WITH_DFS
+extern int dcelogin_atmost_once;
+#endif /* WITH_DFS */
+
+/* really we should have a top level context structure that has the
+ client file descriptor as an element. That would require a major rewrite :(
+
+ the following 2 functions are an alternative - they make the file
+ descriptor private to smbd
+ */
+static int server_fd = -1;
+
+int smbd_server_fd(void)
+{
+ return server_fd;
+}
+
+static void smbd_set_server_fd(int fd)
+{
+ server_fd = fd;
+ client_setfd(fd);
+}
+
+/****************************************************************************
+ Terminate signal.
+****************************************************************************/
+
+static void sig_term(void)
+{
+ got_sig_term = 1;
+ sys_select_signal();
+}
+
+/****************************************************************************
+ Catch a sighup.
+****************************************************************************/
+
+static void sig_hup(int sig)
+{
+ reload_after_sighup = 1;
+ sys_select_signal();
+}
+
+/****************************************************************************
+ Send a SIGTERM to our process group.
+*****************************************************************************/
+
+static void killkids(void)
+{
+ if(am_parent) kill(0,SIGTERM);
+}
+
+/****************************************************************************
+ Process a sam sync message - not sure whether to do this here or
+ somewhere else.
+****************************************************************************/
+
+static void msg_sam_sync(int UNUSED(msg_type), pid_t UNUSED(pid),
+ void *UNUSED(buf), size_t UNUSED(len))
+{
+ DEBUG(10, ("** sam sync message received, ignoring\n"));
+}
+
+/****************************************************************************
+ Process a sam sync replicate message - not sure whether to do this here or
+ somewhere else.
+****************************************************************************/
+
+static void msg_sam_repl(int msg_type, pid_t pid, void *buf, size_t len)
+{
+ uint32 low_serial;
+
+ if (len != sizeof(uint32))
+ return;
+
+ low_serial = *((uint32 *)buf);
+
+ DEBUG(3, ("received sam replication message, serial = 0x%04x\n",
+ low_serial));
+}
+
+/****************************************************************************
+ Open the socket communication - inetd.
+****************************************************************************/
+
+static BOOL open_sockets_inetd(void)
+{
+ /* Started from inetd. fd 0 is the socket. */
+ /* We will abort gracefully when the client or remote system
+ goes away */
+ smbd_set_server_fd(dup(0));
+
+ /* close our standard file descriptors */
+ close_low_fds(False); /* Don't close stderr */
+
+ set_socket_options(smbd_server_fd(),"SO_KEEPALIVE");
+ set_socket_options(smbd_server_fd(), user_socket_options);
+
+ return True;
+}
+
+static void msg_exit_server(int msg_type, pid_t src, void *buf, size_t len)
+{
+ exit_server("Got a SHUTDOWN message");
+}
+
+
+/****************************************************************************
+ Have we reached the process limit ?
+****************************************************************************/
+
+static BOOL allowable_number_of_smbd_processes(void)
+{
+ int max_processes = lp_max_smbd_processes();
+
+ if (!max_processes)
+ return True;
+
+ {
+ TDB_CONTEXT *tdb = conn_tdb_ctx();
+ int32 val;
+ if (!tdb) {
+ DEBUG(0,("allowable_number_of_smbd_processes: can't open connection tdb.\n" ));
+ return False;
+ }
+
+ val = tdb_fetch_int32(tdb, "INFO/total_smbds");
+ if (val == -1 && (tdb_error(tdb) != TDB_ERR_NOEXIST)) {
+ DEBUG(0,("allowable_number_of_smbd_processes: can't fetch INFO/total_smbds. Error %s\n",
+ tdb_errorstr(tdb) ));
+ return False;
+ }
+ if (val > max_processes) {
+ DEBUG(0,("allowable_number_of_smbd_processes: number of processes (%d) is over allowed limit (%d)\n",
+ val, max_processes ));
+ return False;
+ }
+ }
+ return True;
+}
+
+/****************************************************************************
+ Open the socket communication.
+****************************************************************************/
+
+static BOOL open_sockets_smbd(BOOL is_daemon, BOOL interactive, const char *smb_ports)
+{
+ int num_interfaces = iface_count();
+ int num_sockets = 0;
+ int fd_listenset[FD_SETSIZE];
+ fd_set listen_set;
+ int s;
+ int i;
+ char *ports;
+
+ if (!is_daemon) {
+ return open_sockets_inetd();
+ }
+
+
+#ifdef HAVE_ATEXIT
+ {
+ static int atexit_set;
+ if(atexit_set == 0) {
+ atexit_set=1;
+ atexit(killkids);
+ }
+ }
+#endif
+
+ /* Stop zombies */
+ CatchChild();
+
+ FD_ZERO(&listen_set);
+
+ /* use a reasonable default set of ports - listing on 445 and 139 */
+ if (!smb_ports) {
+ ports = lp_smb_ports();
+ if (!ports || !*ports) {
+ ports = smb_xstrdup(SMB_PORTS);
+ } else {
+ ports = smb_xstrdup(ports);
+ }
+ } else {
+ ports = smb_xstrdup(smb_ports);
+ }
+
+ if (lp_interfaces() && lp_bind_interfaces_only()) {
+ /* 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.
+ */
+
+ /* Now open a listen socket for each of the
+ interfaces. */
+ for(i = 0; i < num_interfaces; i++) {
+ struct in_addr *ifip = iface_n_ip(i);
+ fstring tok;
+ const char *ptr;
+
+ if(ifip == NULL) {
+ DEBUG(0,("open_sockets_smbd: interface %d has NULL IP address !\n", i));
+ continue;
+ }
+
+ for (ptr=ports; next_token(&ptr, tok, NULL, sizeof(tok)); ) {
+ unsigned port = atoi(tok);
+ if (port == 0) continue;
+ s = fd_listenset[num_sockets] = open_socket_in(SOCK_STREAM, port, 0, ifip->s_addr, True);
+ if(s == -1)
+ return False;
+
+ /* ready to listen */
+ set_socket_options(s,"SO_KEEPALIVE");
+ set_socket_options(s,user_socket_options);
+
+ /* Set server socket to non-blocking for the accept. */
+ set_blocking(s,False);
+
+ if (listen(s, SMBD_LISTEN_BACKLOG) == -1) {
+ DEBUG(0,("listen: %s\n",strerror(errno)));
+ close(s);
+ return False;
+ }
+ FD_SET(s,&listen_set);
+
+ num_sockets++;
+ if (num_sockets >= FD_SETSIZE) {
+ DEBUG(0,("open_sockets_smbd: Too many sockets to bind to\n"));
+ return False;
+ }
+ }
+ }
+ } else {
+ /* Just bind to 0.0.0.0 - accept connections
+ from anywhere. */
+
+ fstring tok;
+ const char *ptr;
+
+ num_interfaces = 1;
+
+ for (ptr=ports; next_token(&ptr, tok, NULL, sizeof(tok)); ) {
+ unsigned port = atoi(tok);
+ if (port == 0) continue;
+ /* open an incoming socket */
+ s = open_socket_in(SOCK_STREAM, port, 0,
+ interpret_addr(lp_socket_address()),True);
+ if (s == -1)
+ return(False);
+
+ /* ready to listen */
+ set_socket_options(s,"SO_KEEPALIVE");
+ set_socket_options(s,user_socket_options);
+
+ /* Set server socket to non-blocking for the accept. */
+ set_blocking(s,False);
+
+ if (listen(s, SMBD_LISTEN_BACKLOG) == -1) {
+ DEBUG(0,("open_sockets_smbd: listen: %s\n",
+ strerror(errno)));
+ close(s);
+ return False;
+ }
+
+ fd_listenset[num_sockets] = s;
+ FD_SET(s,&listen_set);
+
+ num_sockets++;
+
+ if (num_sockets >= FD_SETSIZE) {
+ DEBUG(0,("open_sockets_smbd: Too many sockets to bind to\n"));
+ return False;
+ }
+ }
+ }
+
+ SAFE_FREE(ports);
+
+ /* Listen to messages */
+
+ message_register(MSG_SMB_SAM_SYNC, msg_sam_sync);
+ message_register(MSG_SMB_SAM_REPL, msg_sam_repl);
+ message_register(MSG_SHUTDOWN, msg_exit_server);
+
+ /* now accept incoming connections - forking a new process
+ for each incoming connection */
+ DEBUG(2,("waiting for a connection\n"));
+ while (1) {
+ fd_set lfds;
+ int num;
+
+ /* Free up temporary memory from the main smbd. */
+ lp_talloc_free();
+
+ /* Ensure we respond to PING and DEBUG messages from the main smbd. */
+ message_dispatch();
+
+ memcpy((char *)&lfds, (char *)&listen_set,
+ sizeof(listen_set));
+
+ num = sys_select(FD_SETSIZE,&lfds,NULL,NULL,NULL);
+
+ if (num == -1 && errno == EINTR) {
+ if (got_sig_term) {
+ exit_server("Caught TERM signal");
+ }
+
+ /* check for sighup processing */
+ if (reload_after_sighup) {
+ change_to_root_user();
+ DEBUG(1,("Reloading services after SIGHUP\n"));
+ reload_services(False);
+ reload_after_sighup = 0;
+ }
+
+ continue;
+ }
+
+ /* check if we need to reload services */
+ check_reload(time(NULL));
+
+ /* Find the sockets that are read-ready -
+ accept on these. */
+ for( ; num > 0; num--) {
+ struct sockaddr addr;
+ socklen_t in_addrlen = sizeof(addr);
+
+ s = -1;
+ for(i = 0; i < num_sockets; i++) {
+ if(FD_ISSET(fd_listenset[i],&lfds)) {
+ s = fd_listenset[i];
+ /* Clear this so we don't look
+ at it again. */
+ FD_CLR(fd_listenset[i],&lfds);
+ break;
+ }
+ }
+
+ smbd_set_server_fd(accept(s,&addr,&in_addrlen));
+
+ if (smbd_server_fd() == -1 && errno == EINTR)
+ continue;
+
+ if (smbd_server_fd() == -1) {
+ DEBUG(0,("open_sockets_smbd: accept: %s\n",
+ strerror(errno)));
+ continue;
+ }
+
+ /* Ensure child is set to blocking mode */
+ set_blocking(smbd_server_fd(),True);
+
+ if (smbd_server_fd() != -1 && interactive)
+ return True;
+
+ if (allowable_number_of_smbd_processes() && smbd_server_fd() != -1 && sys_fork()==0) {
+ /* Child code ... */
+
+ /* close the listening socket(s) */
+ for(i = 0; i < num_sockets; i++)
+ close(fd_listenset[i]);
+
+ /* close our standard file
+ descriptors */
+ close_low_fds(False);
+ am_parent = 0;
+
+ set_socket_options(smbd_server_fd(),"SO_KEEPALIVE");
+ set_socket_options(smbd_server_fd(),user_socket_options);
+
+ /* this is needed so that we get decent entries
+ in smbstatus for port 445 connects */
+ set_remote_machine_name(get_peer_addr(smbd_server_fd()), False);
+
+ /* Reset global variables in util.c so
+ that client substitutions will be
+ done correctly in the process. */
+ reset_globals_after_fork();
+
+ /* tdb needs special fork handling - remove CLEAR_IF_FIRST flags */
+ if (tdb_reopen_all() == -1) {
+ DEBUG(0,("tdb_reopen_all failed.\n"));
+ smb_panic("tdb_reopen_all failed.");
+ }
+
+ return True;
+ }
+ /* The parent doesn't need this socket */
+ close(smbd_server_fd());
+
+ /* Sun May 6 18:56:14 2001 ackley@cs.unm.edu:
+ Clear the closed fd info out of server_fd --
+ and more importantly, out of client_fd in
+ util_sock.c, to avoid a possible
+ getpeername failure if we reopen the logs
+ and use %I in the filename.
+ */
+
+ smbd_set_server_fd(-1);
+
+ /* Force parent to check log size after
+ * spawning child. Fix from
+ * klausr@ITAP.Physik.Uni-Stuttgart.De. The
+ * parent smbd will log to logserver.smb. It
+ * writes only two messages for each child
+ * started/finished. But each child writes,
+ * say, 50 messages also in logserver.smb,
+ * begining with the debug_count of the
+ * parent, before the child opens its own log
+ * file logserver.client. In a worst case
+ * scenario the size of logserver.smb would be
+ * checked after about 50*50=2500 messages
+ * (ca. 100kb).
+ * */
+ force_check_log_size();
+
+ } /* end for num */
+ } /* end while 1 */
+
+/* NOTREACHED return True; */
+}
+
+/****************************************************************************
+ Reload the services file.
+**************************************************************************/
+
+BOOL reload_services(BOOL test)
+{
+ BOOL ret;
+
+ if (lp_loaded()) {
+ pstring fname;
+ pstrcpy(fname,lp_configfile());
+ if (file_exist(fname, NULL) &&
+ !strcsequal(fname, dyn_CONFIGFILE)) {
+ pstrcpy(dyn_CONFIGFILE, fname);
+ test = False;
+ }
+ }
+
+ reopen_logs();
+
+ if (test && !lp_file_list_changed())
+ return(True);
+
+ lp_killunused(conn_snum_used);
+
+ ret = lp_load(dyn_CONFIGFILE, False, False, True);
+
+ load_printers();
+
+ /* perhaps the config filename is now set */
+ if (!test)
+ reload_services(True);
+
+ reopen_logs();
+
+ load_interfaces();
+
+ {
+ if (smbd_server_fd() != -1) {
+ set_socket_options(smbd_server_fd(),"SO_KEEPALIVE");
+ set_socket_options(smbd_server_fd(), user_socket_options);
+ }
+ }
+
+ mangle_reset_cache();
+ reset_stat_cache();
+
+ /* this forces service parameters to be flushed */
+ set_current_service(NULL,True);
+
+ return(ret);
+}
+
+
+#if DUMP_CORE
+/*******************************************************************
+prepare to dump a core file - carefully!
+********************************************************************/
+static BOOL dump_core(void)
+{
+ char *p;
+ pstring dname;
+
+ pstrcpy(dname,lp_logfile());
+ if ((p=strrchr_m(dname,'/'))) *p=0;
+ pstrcat(dname,"/corefiles");
+ mkdir(dname,0700);
+ sys_chown(dname,getuid(),getgid());
+ chmod(dname,0700);
+ if (chdir(dname)) return(False);
+ umask(~(0700));
+
+#ifdef HAVE_GETRLIMIT
+#ifdef RLIMIT_CORE
+ {
+ struct rlimit rlp;
+ getrlimit(RLIMIT_CORE, &rlp);
+ rlp.rlim_cur = MAX(4*1024*1024,rlp.rlim_cur);
+ setrlimit(RLIMIT_CORE, &rlp);
+ getrlimit(RLIMIT_CORE, &rlp);
+ DEBUG(3,("Core limits now %d %d\n",
+ (int)rlp.rlim_cur,(int)rlp.rlim_max));
+ }
+#endif
+#endif
+
+
+ DEBUG(0,("Dumping core in %s\n", dname));
+ abort();
+ return(True);
+}
+#endif
+
+/****************************************************************************
+ Exit the server.
+****************************************************************************/
+
+void exit_server(const char *reason)
+{
+ static int firsttime=1;
+ extern char *last_inbuf;
+ extern struct auth_context *negprot_global_auth_context;
+
+ if (!firsttime)
+ exit(0);
+ firsttime = 0;
+
+ change_to_root_user();
+ DEBUG(2,("Closing connections\n"));
+
+ if (negprot_global_auth_context) {
+ (negprot_global_auth_context->free)(&negprot_global_auth_context);
+ }
+
+ conn_close_all();
+
+ invalidate_all_vuids();
+
+ print_notify_send_messages(3); /* 3 second timeout. */
+
+ /* run all registered exit events */
+ smb_run_exit_events();
+
+ /* delete our entry in the connections database. */
+ yield_connection(NULL,"");
+
+ respond_to_all_remaining_local_messages();
+ decrement_smbd_process_count();
+
+#ifdef WITH_DFS
+ if (dcelogin_atmost_once) {
+ dfs_unlogin();
+ }
+#endif
+
+ if (!reason) {
+ int oldlevel = DEBUGLEVEL;
+ DEBUGLEVEL = 10;
+ DEBUG(0,("Last message was %s\n",smb_fn_name(last_message)));
+ if (last_inbuf)
+ show_msg(last_inbuf);
+ DEBUGLEVEL = oldlevel;
+ DEBUG(0,("===============================================================\n"));
+#if DUMP_CORE
+ if (dump_core()) return;
+#endif
+ }
+
+ locking_end();
+ printing_end();
+
+ DEBUG(3,("Server exit (%s)\n", (reason ? reason : "")));
+ exit(0);
+}
+
+/****************************************************************************
+ Initialise connect, service and file structs.
+****************************************************************************/
+
+static BOOL init_structs(void )
+{
+ /*
+ * Set the machine NETBIOS name if not already
+ * set from the config file.
+ */
+
+ if (!init_names())
+ return False;
+
+ conn_init();
+
+ file_init();
+
+ /* for RPC pipes */
+ init_rpc_pipe_hnd();
+
+ init_dptrs();
+
+ secrets_init();
+
+ return True;
+}
+
+/****************************************************************************
+ main program.
+****************************************************************************/
+
+/* Declare prototype for build_options() to avoid having to run it through
+ mkproto.h. Mixing $(builddir) and $(srcdir) source files in the current
+ prototype generation system is too complicated. */
+
+void build_options(BOOL screen);
+
+ int main(int argc,const char *argv[])
+{
+ /* shall I run as a daemon */
+ static BOOL is_daemon = False;
+ static BOOL interactive = False;
+ static BOOL Fork = True;
+ static BOOL log_stdout = False;
+ static char *ports = NULL;
+ int opt;
+ poptContext pc;
+
+ struct poptOption long_options[] = {
+ POPT_AUTOHELP
+ {"daemon", 'D', POPT_ARG_VAL, &is_daemon, True, "Become a daemon (default)" },
+ {"interactive", 'i', POPT_ARG_VAL, &interactive, True, "Run interactive (not a daemon)"},
+ {"foreground", 'F', POPT_ARG_VAL, &Fork, False, "Run daemon in foreground (for daemontools & etc)" },
+ {"log-stdout", 'S', POPT_ARG_VAL, &log_stdout, True, "Log to stdout" },
+ {"build-options", 'b', POPT_ARG_NONE, NULL, 'b', "Print build options" },
+ {"port", 'p', POPT_ARG_STRING, &ports, 0, "Listen on the specified ports"},
+ POPT_COMMON_SAMBA
+ { NULL }
+ };
+
+#ifdef HAVE_SET_AUTH_PARAMETERS
+ set_auth_parameters(argc,argv);
+#endif
+
+ pc = poptGetContext("smbd", argc, argv, long_options, 0);
+
+ while((opt = poptGetNextOpt(pc)) != -1) {
+ switch (opt) {
+ case 'b':
+ build_options(True); /* Display output to screen as well as debug */
+ exit(0);
+ break;
+ }
+ }
+
+ poptFreeContext(pc);
+
+#ifdef HAVE_SETLUID
+ /* needed for SecureWare on SCO */
+ setluid(0);
+#endif
+
+ sec_init();
+
+ load_case_tables();
+
+ set_remote_machine_name("smbd", False);
+
+ if (interactive) {
+ Fork = False;
+ log_stdout = True;
+ }
+
+ if (log_stdout && Fork) {
+ DEBUG(0,("ERROR: Can't log to stdout (-S) unless daemon is in foreground (-F) or interactive (-i)\n"));
+ exit(1);
+ }
+
+ setup_logging(argv[0],log_stdout);
+
+ /* we want to re-seed early to prevent time delays causing
+ client problems at a later date. (tridge) */
+ generate_random_buffer(NULL, 0, False);
+
+ /* make absolutely sure we run as root - to handle cases where people
+ are crazy enough to have it setuid */
+
+ gain_root_privilege();
+ gain_root_group_privilege();
+
+ fault_setup((void (*)(void *))exit_server);
+ CatchSignal(SIGTERM , SIGNAL_CAST sig_term);
+ CatchSignal(SIGHUP,SIGNAL_CAST sig_hup);
+
+ /* we are never interested in SIGPIPE */
+ BlockSignals(True,SIGPIPE);
+
+#if defined(SIGFPE)
+ /* we are never interested in SIGFPE */
+ BlockSignals(True,SIGFPE);
+#endif
+
+#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, SIGUSR1);
+ BlockSignals(False, SIGTERM);
+
+ /* we want total control over the permissions on created files,
+ so set our umask to 0 */
+ umask(0);
+
+ init_sec_ctx();
+
+ reopen_logs();
+
+ DEBUG(0,( "smbd version %s started.\n", SAMBA_VERSION_STRING));
+ DEBUGADD(0,( "Copyright Andrew Tridgell and the Samba Team 1992-2004\n"));
+
+ DEBUG(2,("uid=%d gid=%d euid=%d egid=%d\n",
+ (int)getuid(),(int)getgid(),(int)geteuid(),(int)getegid()));
+
+ /* Output the build options to the debug log */
+ build_options(False);
+
+ if (sizeof(uint16) < 2 || sizeof(uint32) < 4) {
+ DEBUG(0,("ERROR: Samba is not configured correctly for the word size on your machine\n"));
+ exit(1);
+ }
+
+ /*
+ * Do this before reload_services.
+ */
+
+ if (!reload_services(False))
+ return(-1);
+
+ init_structs();
+
+#ifdef WITH_PROFILE
+ if (!profile_setup(False)) {
+ DEBUG(0,("ERROR: failed to setup profiling\n"));
+ return -1;
+ }
+#endif
+
+ DEBUG(3,( "loaded services\n"));
+
+ if (!is_daemon && !is_a_socket(0)) {
+ if (!interactive)
+ DEBUG(0,("standard input is not a socket, assuming -D option\n"));
+
+ /*
+ * Setting is_daemon here prevents us from eventually calling
+ * the open_sockets_inetd()
+ */
+
+ is_daemon = True;
+ }
+
+ if (is_daemon && !interactive) {
+ DEBUG( 3, ( "Becoming a daemon.\n" ) );
+ become_daemon(Fork);
+ }
+
+#if HAVE_SETPGID
+ /*
+ * If we're interactive we want to set our own process group for
+ * signal management.
+ */
+ if (interactive)
+ setpgid( (pid_t)0, (pid_t)0);
+#endif
+
+ if (!directory_exist(lp_lockdir(), NULL))
+ mkdir(lp_lockdir(), 0755);
+
+ if (is_daemon)
+ pidfile_create("smbd");
+
+ /* Setup all the TDB's - including CLEAR_IF_FIRST tdb's. */
+ if (!message_init())
+ exit(1);
+
+ if (!session_init())
+ exit(1);
+
+ if (conn_tdb_ctx() == NULL)
+ exit(1);
+
+ if (!locking_init(0))
+ exit(1);
+
+ if (!share_info_db_init())
+ exit(1);
+
+ namecache_enable();
+
+ if (!init_registry())
+ exit(1);
+
+ if (!print_backend_init())
+ exit(1);
+
+ /* Setup the main smbd so that we can get messages. */
+ claim_connection(NULL,"",0,True,FLAG_MSG_GENERAL|FLAG_MSG_SMBD);
+
+ /*
+ DO NOT ENABLE THIS TILL YOU COPE WITH KILLING THESE TASKS AND INETD
+ THIS *killed* LOTS OF BUILD FARM MACHINES. IT CREATED HUNDREDS OF
+ smbd PROCESSES THAT NEVER DIE
+ start_background_queue();
+ */
+
+ if (!open_sockets_smbd(is_daemon, interactive, ports))
+ exit(1);
+
+ /*
+ * everything after this point is run after the fork()
+ */
+
+ /* Initialise the password backed before the global_sam_sid
+ to ensure that we fetch from ldap before we make a domain sid up */
+
+ if(!initialize_password_db(False))
+ exit(1);
+
+ if(!get_global_sam_sid()) {
+ DEBUG(0,("ERROR: Samba cannot create a SAM SID.\n"));
+ exit(1);
+ }
+
+ static_init_rpc;
+
+ init_modules();
+
+ /* possibly reload the services file. */
+ reload_services(True);
+
+ if (!init_account_policy()) {
+ DEBUG(0,("Could not open account policy tdb.\n"));
+ exit(1);
+ }
+
+ if (*lp_rootdir()) {
+ if (sys_chroot(lp_rootdir()) == 0)
+ DEBUG(2,("Changed root to %s\n", lp_rootdir()));
+ }
+
+ /* Setup oplocks */
+ if (!init_oplocks())
+ exit(1);
+
+ /* Setup change notify */
+ if (!init_change_notify())
+ exit(1);
+
+ /* re-initialise the timezone */
+ TimeInit();
+
+ /* register our message handlers */
+ message_register(MSG_SMB_FORCE_TDIS, msg_force_tdis);
+
+ smbd_process();
+
+ namecache_shutdown();
+ exit_server("normal exit");
+ return(0);
+}
diff --git a/source/smbd/service.c b/source/smbd/service.c
new file mode 100644
index 00000000000..1910ef9b72b
--- /dev/null
+++ b/source/smbd/service.c
@@ -0,0 +1,838 @@
+/*
+ Unix SMB/CIFS implementation.
+ service (connection) opening and closing
+ 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 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"
+
+extern struct timeval smb_last_time;
+extern int case_default;
+extern BOOL case_preserve;
+extern BOOL short_case_preserve;
+extern BOOL case_mangle;
+extern BOOL case_sensitive;
+extern BOOL use_mangled_map;
+extern userdom_struct current_user_info;
+
+
+/****************************************************************************
+ Load parameters specific to a connection/service.
+****************************************************************************/
+
+BOOL set_current_service(connection_struct *conn,BOOL do_chdir)
+{
+ extern char magic_char;
+ static connection_struct *last_conn;
+ int snum;
+
+ if (!conn) {
+ last_conn = NULL;
+ return(False);
+ }
+
+ conn->lastused = smb_last_time.tv_sec;
+
+ snum = SNUM(conn);
+
+ if (do_chdir &&
+ vfs_ChDir(conn,conn->connectpath) != 0 &&
+ vfs_ChDir(conn,conn->origpath) != 0) {
+ DEBUG(0,("chdir (%s) failed\n",
+ conn->connectpath));
+ return(False);
+ }
+
+ if (conn == last_conn)
+ return(True);
+
+ last_conn = conn;
+
+ case_default = lp_defaultcase(snum);
+ case_preserve = lp_preservecase(snum);
+ short_case_preserve = lp_shortpreservecase(snum);
+ case_mangle = lp_casemangle(snum);
+ case_sensitive = lp_casesensitive(snum);
+ magic_char = lp_magicchar(snum);
+ use_mangled_map = (*lp_mangled_map(snum) ? True:False);
+ return(True);
+}
+
+/****************************************************************************
+ Add a home service. Returns the new service number or -1 if fail.
+****************************************************************************/
+
+int add_home_service(const char *service, const char *username, const char *homedir)
+{
+ int iHomeService;
+
+ if (!service || !homedir)
+ return -1;
+
+ if ((iHomeService = lp_servicenumber(HOMES_NAME)) < 0)
+ return -1;
+
+ /*
+ * If this is a winbindd provided username, remove
+ * the domain component before adding the service.
+ * Log a warning if the "path=" parameter does not
+ * include any macros.
+ */
+
+ {
+ const char *p = strchr(service,*lp_winbind_separator());
+
+ /* We only want the 'user' part of the string */
+ if (p) {
+ service = p + 1;
+ }
+ }
+
+ if (!lp_add_home(service, iHomeService, username, homedir)) {
+ return -1;
+ }
+
+ return lp_servicenumber(service);
+
+}
+
+
+/**
+ * Find a service entry.
+ *
+ * @param service is modified (to canonical form??)
+ **/
+
+int find_service(fstring service)
+{
+ int iService;
+
+ all_string_sub(service,"\\","/",0);
+
+ iService = lp_servicenumber(service);
+
+ /* now handle the special case of a home directory */
+ if (iService < 0) {
+ char *phome_dir = get_user_home_dir(service);
+
+ if(!phome_dir) {
+ /*
+ * Try mapping the servicename, it may
+ * be a Windows to unix mapped user name.
+ */
+ if(map_username(service))
+ phome_dir = get_user_home_dir(service);
+ }
+
+ DEBUG(3,("checking for home directory %s gave %s\n",service,
+ phome_dir?phome_dir:"(NULL)"));
+
+ iService = add_home_service(service,service /* 'username' */, phome_dir);
+ }
+
+ /* If we still don't have a service, attempt to add it as a printer. */
+ if (iService < 0) {
+ int iPrinterService;
+
+ if ((iPrinterService = lp_servicenumber(PRINTERS_NAME)) >= 0) {
+ char *pszTemp;
+
+ DEBUG(3,("checking whether %s is a valid printer name...\n", service));
+ pszTemp = lp_printcapname();
+ if ((pszTemp != NULL) && pcap_printername_ok(service, pszTemp)) {
+ DEBUG(3,("%s is a valid printer name\n", service));
+ DEBUG(3,("adding %s as a printer service\n", service));
+ lp_add_printer(service, iPrinterService);
+ iService = lp_servicenumber(service);
+ if (iService < 0) {
+ DEBUG(0,("failed to add %s as a printer service!\n", service));
+ }
+ } else {
+ DEBUG(3,("%s is not a valid printer name\n", service));
+ }
+ }
+ }
+
+ /* Check for default vfs service? Unsure whether to implement this */
+ if (iService < 0) {
+ }
+
+ /* just possibly it's a default service? */
+ if (iService < 0) {
+ char *pdefservice = lp_defaultservice();
+ if (pdefservice && *pdefservice && !strequal(pdefservice,service) && !strstr_m(service,"..")) {
+ /*
+ * We need to do a local copy here as lp_defaultservice()
+ * returns one of the rotating lp_string buffers that
+ * could get overwritten by the recursive find_service() call
+ * below. Fix from Josef Hinteregger <joehtg@joehtg.co.at>.
+ */
+ pstring defservice;
+ pstrcpy(defservice, pdefservice);
+ iService = find_service(defservice);
+ if (iService >= 0) {
+ all_string_sub(service, "_","/",0);
+ iService = lp_add_service(service, iService);
+ }
+ }
+ }
+
+ if (iService >= 0) {
+ if (!VALID_SNUM(iService)) {
+ DEBUG(0,("Invalid snum %d for %s\n",iService, service));
+ iService = -1;
+ }
+ }
+
+ if (iService < 0)
+ DEBUG(3,("find_service() failed to find service %s\n", service));
+
+ return (iService);
+}
+
+
+/****************************************************************************
+ do some basic sainity checks on the share.
+ This function modifies dev, ecode.
+****************************************************************************/
+
+static NTSTATUS share_sanity_checks(int snum, fstring dev)
+{
+
+ if (!lp_snum_ok(snum) ||
+ !check_access(smbd_server_fd(),
+ lp_hostsallow(snum), lp_hostsdeny(snum))) {
+ return NT_STATUS_ACCESS_DENIED;
+ }
+
+ if (dev[0] == '?' || !dev[0]) {
+ if (lp_print_ok(snum)) {
+ fstrcpy(dev,"LPT1:");
+ } else if (strequal(lp_fstype(snum), "IPC")) {
+ fstrcpy(dev, "IPC");
+ } else {
+ fstrcpy(dev,"A:");
+ }
+ }
+
+ strupper_m(dev);
+
+ if (lp_print_ok(snum)) {
+ if (!strequal(dev, "LPT1:")) {
+ return NT_STATUS_BAD_DEVICE_TYPE;
+ }
+ } else if (strequal(lp_fstype(snum), "IPC")) {
+ if (!strequal(dev, "IPC")) {
+ return NT_STATUS_BAD_DEVICE_TYPE;
+ }
+ } else if (!strequal(dev, "A:")) {
+ return NT_STATUS_BAD_DEVICE_TYPE;
+ }
+
+ /* Behave as a printer if we are supposed to */
+ if (lp_print_ok(snum) && (strcmp(dev, "A:") == 0)) {
+ fstrcpy(dev, "LPT1:");
+ }
+
+ return NT_STATUS_OK;
+}
+
+/****************************************************************************
+ Make a connection, given the snum to connect to, and the vuser of the
+ connecting user if appropriate.
+****************************************************************************/
+
+static connection_struct *make_connection_snum(int snum, user_struct *vuser,
+ DATA_BLOB password,
+ const char *pdev, NTSTATUS *status)
+{
+ struct passwd *pass = NULL;
+ BOOL guest = False;
+ connection_struct *conn;
+ struct stat st;
+ fstring user;
+ fstring dev;
+
+ *user = 0;
+ fstrcpy(dev, pdev);
+
+ if (NT_STATUS_IS_ERR(*status = share_sanity_checks(snum, dev))) {
+ return NULL;
+ }
+
+ conn = conn_new();
+ if (!conn) {
+ DEBUG(0,("Couldn't find free connection.\n"));
+ *status = NT_STATUS_INSUFFICIENT_RESOURCES;
+ return NULL;
+ }
+
+ if (lp_guest_only(snum)) {
+ const char *guestname = lp_guestaccount();
+ guest = True;
+ pass = getpwnam_alloc(guestname);
+ if (!pass) {
+ DEBUG(0,("make_connection_snum: Invalid guest account %s??\n",guestname));
+ conn_free(conn);
+ *status = NT_STATUS_NO_SUCH_USER;
+ return NULL;
+ }
+ fstrcpy(user,pass->pw_name);
+ conn->force_user = True;
+ conn->uid = pass->pw_uid;
+ conn->gid = pass->pw_gid;
+ string_set(&conn->user,pass->pw_name);
+ passwd_free(&pass);
+ DEBUG(3,("Guest only user %s\n",user));
+ } else if (vuser) {
+ if (vuser->guest) {
+ if (!lp_guest_ok(snum)) {
+ DEBUG(2, ("guest user (from session setup) not permitted to access this share (%s)\n", lp_servicename(snum)));
+ conn_free(conn);
+ *status = NT_STATUS_ACCESS_DENIED;
+ return NULL;
+ }
+ } else {
+ if (!user_ok(vuser->user.unix_name, snum, vuser->groups, vuser->n_groups)) {
+ DEBUG(2, ("user '%s' (from session setup) not permitted to access this share (%s)\n", vuser->user.unix_name, lp_servicename(snum)));
+ conn_free(conn);
+ *status = NT_STATUS_ACCESS_DENIED;
+ return NULL;
+ }
+ }
+ conn->vuid = vuser->vuid;
+ conn->uid = vuser->uid;
+ conn->gid = vuser->gid;
+ string_set(&conn->user,vuser->user.unix_name);
+ fstrcpy(user,vuser->user.unix_name);
+ guest = vuser->guest;
+ } else if (lp_security() == SEC_SHARE) {
+ /* add it as a possible user name if we
+ are in share mode security */
+ add_session_user(lp_servicename(snum));
+ /* shall we let them in? */
+ if (!authorise_login(snum,user,password,&guest)) {
+ DEBUG( 2, ( "Invalid username/password for [%s]\n",
+ lp_servicename(snum)) );
+ conn_free(conn);
+ *status = NT_STATUS_WRONG_PASSWORD;
+ return NULL;
+ }
+ pass = Get_Pwnam(user);
+ conn->force_user = True;
+ conn->uid = pass->pw_uid;
+ conn->gid = pass->pw_gid;
+ string_set(&conn->user, pass->pw_name);
+ fstrcpy(user, pass->pw_name);
+
+ } else {
+ DEBUG(0, ("invalid VUID (vuser) but not in security=share\n"));
+ conn_free(conn);
+ *status = NT_STATUS_ACCESS_DENIED;
+ return NULL;
+ }
+
+ add_session_user(user);
+
+ safe_strcpy(conn->client_address, client_addr(),
+ sizeof(conn->client_address)-1);
+ conn->num_files_open = 0;
+ conn->lastused = time(NULL);
+ conn->service = snum;
+ conn->used = True;
+ conn->printer = (strncmp(dev,"LPT",3) == 0);
+ conn->ipc = ((strncmp(dev,"IPC",3) == 0) || strequal(dev,"ADMIN$"));
+ conn->dirptr = NULL;
+ conn->veto_list = NULL;
+ conn->hide_list = NULL;
+ conn->veto_oplock_list = NULL;
+ string_set(&conn->dirpath,"");
+ string_set(&conn->user,user);
+ conn->nt_user_token = NULL;
+ conn->privs = NULL;
+
+ conn->read_only = lp_readonly(conn->service);
+ conn->admin_user = False;
+
+ /*
+ * If force user is true, then store the
+ * given userid and also the groups
+ * of the user we're forcing.
+ */
+
+ if (*lp_force_user(snum)) {
+ struct passwd *pass2;
+ pstring fuser;
+ pstrcpy(fuser,lp_force_user(snum));
+
+ /* Allow %S to be used by force user. */
+ pstring_sub(fuser,"%S",lp_servicename(snum));
+
+ pass2 = (struct passwd *)Get_Pwnam(fuser);
+ if (pass2) {
+ conn->uid = pass2->pw_uid;
+ conn->gid = pass2->pw_gid;
+ string_set(&conn->user,pass2->pw_name);
+ fstrcpy(user,pass2->pw_name);
+ conn->force_user = True;
+ DEBUG(3,("Forced user %s\n",user));
+ } else {
+ DEBUG(1,("Couldn't find user %s\n",fuser));
+ conn_free(conn);
+ *status = NT_STATUS_NO_SUCH_USER;
+ return NULL;
+ }
+ }
+
+#ifdef HAVE_GETGRNAM
+ /*
+ * If force group is true, then override
+ * any groupid stored for the connecting user.
+ */
+
+ if (*lp_force_group(snum)) {
+ gid_t gid;
+ pstring gname;
+ pstring tmp_gname;
+ BOOL user_must_be_member = False;
+
+ pstrcpy(tmp_gname,lp_force_group(snum));
+
+ if (tmp_gname[0] == '+') {
+ user_must_be_member = True;
+ /* even now, tmp_gname is null terminated */
+ pstrcpy(gname,&tmp_gname[1]);
+ } else {
+ pstrcpy(gname,tmp_gname);
+ }
+ /* default service may be a group name */
+ pstring_sub(gname,"%S",lp_servicename(snum));
+ gid = nametogid(gname);
+
+ if (gid != (gid_t)-1) {
+
+ /*
+ * If the user has been forced and the forced group starts
+ * with a '+', then we only set the group to be the forced
+ * group if the forced user is a member of that group.
+ * Otherwise, the meaning of the '+' would be ignored.
+ */
+ if (conn->force_user && user_must_be_member) {
+ if (user_in_group_list( user, gname, NULL, 0)) {
+ conn->gid = gid;
+ DEBUG(3,("Forced group %s for member %s\n",gname,user));
+ }
+ } else {
+ conn->gid = gid;
+ DEBUG(3,("Forced group %s\n",gname));
+ }
+ conn->force_group = True;
+ } else {
+ DEBUG(1,("Couldn't find group %s\n",gname));
+ conn_free(conn);
+ *status = NT_STATUS_NO_SUCH_GROUP;
+ return NULL;
+ }
+ }
+#endif /* HAVE_GETGRNAM */
+
+ {
+ pstring s;
+ pstrcpy(s,lp_pathname(snum));
+ standard_sub_conn(conn,s,sizeof(s));
+ string_set(&conn->connectpath,s);
+ DEBUG(3,("Connect path is '%s' for service [%s]\n",s, lp_servicename(snum)));
+ }
+
+ if (conn->force_user || conn->force_group) {
+
+ /* groups stuff added by ih */
+ conn->ngroups = 0;
+ conn->groups = NULL;
+
+ /* Find all the groups this uid is in and
+ store them. Used by change_to_user() */
+ initialise_groups(conn->user, conn->uid, conn->gid);
+ get_current_groups(conn->gid, &conn->ngroups,&conn->groups);
+
+ conn->nt_user_token = create_nt_token(conn->uid, conn->gid,
+ conn->ngroups, conn->groups,
+ guest);
+
+ init_privilege(&(conn->privs));
+ pdb_get_privilege_set(conn->nt_user_token->user_sids, conn->nt_user_token->num_sids, conn->privs);
+ }
+
+ /*
+ * New code to check if there's a share security descripter
+ * added from NT server manager. This is done after the
+ * smb.conf checks are done as we need a uid and token. JRA.
+ *
+ */
+
+ {
+ BOOL can_write = share_access_check(conn, snum, vuser, FILE_WRITE_DATA);
+
+ if (!can_write) {
+ if (!share_access_check(conn, snum, vuser, FILE_READ_DATA)) {
+ /* No access, read or write. */
+ DEBUG(0,( "make_connection: connection to %s denied due to security descriptor.\n",
+ lp_servicename(snum)));
+ conn_free(conn);
+ *status = NT_STATUS_ACCESS_DENIED;
+ return NULL;
+ } else {
+ conn->read_only = True;
+ }
+ }
+ }
+ /* Initialise VFS function pointers */
+
+ if (!smbd_vfs_init(conn)) {
+ DEBUG(0, ("vfs_init failed for service %s\n", lp_servicename(SNUM(conn))));
+ conn_free(conn);
+ *status = NT_STATUS_BAD_NETWORK_NAME;
+ return NULL;
+ }
+
+/* ROOT Activities: */
+ /* check number of connections */
+ if (!claim_connection(conn,
+ lp_servicename(SNUM(conn)),
+ lp_max_connections(SNUM(conn)),
+ False,0)) {
+ DEBUG(1,("too many connections - rejected\n"));
+ conn_free(conn);
+ *status = NT_STATUS_INSUFFICIENT_RESOURCES;
+ return NULL;
+ }
+
+ /* Preexecs are done here as they might make the dir we are to ChDir to below */
+ /* execute any "root preexec = " line */
+ if (*lp_rootpreexec(SNUM(conn))) {
+ int ret;
+ pstring cmd;
+ pstrcpy(cmd,lp_rootpreexec(SNUM(conn)));
+ standard_sub_conn(conn,cmd,sizeof(cmd));
+ DEBUG(5,("cmd=%s\n",cmd));
+ ret = smbrun(cmd,NULL);
+ if (ret != 0 && lp_rootpreexec_close(SNUM(conn))) {
+ DEBUG(1,("root preexec gave %d - failing connection\n", ret));
+ yield_connection(conn, lp_servicename(SNUM(conn)));
+ conn_free(conn);
+ *status = NT_STATUS_ACCESS_DENIED;
+ return NULL;
+ }
+ }
+
+/* USER Activites: */
+ if (!change_to_user(conn, conn->vuid)) {
+ /* No point continuing if they fail the basic checks */
+ DEBUG(0,("Can't become connected user!\n"));
+ conn_free(conn);
+ *status = NT_STATUS_LOGON_FAILURE;
+ return NULL;
+ }
+
+ /* Remember that a different vuid can connect later without these checks... */
+
+ /* Preexecs are done here as they might make the dir we are to ChDir to below */
+ /* execute any "preexec = " line */
+ if (*lp_preexec(SNUM(conn))) {
+ int ret;
+ pstring cmd;
+ pstrcpy(cmd,lp_preexec(SNUM(conn)));
+ standard_sub_conn(conn,cmd,sizeof(cmd));
+ ret = smbrun(cmd,NULL);
+ if (ret != 0 && lp_preexec_close(SNUM(conn))) {
+ DEBUG(1,("preexec gave %d - failing connection\n", ret));
+ change_to_root_user();
+ yield_connection(conn, lp_servicename(SNUM(conn)));
+ conn_free(conn);
+ *status = NT_STATUS_ACCESS_DENIED;
+ return NULL;
+ }
+ }
+
+#ifdef WITH_FAKE_KASERVER
+ if (lp_afs_share(SNUM(conn))) {
+ afs_login(conn);
+ }
+#endif
+
+#if CHECK_PATH_ON_TCONX
+ /* win2000 does not check the permissions on the directory
+ during the tree connect, instead relying on permission
+ check during individual operations. To match this behaviour
+ I have disabled this chdir check (tridge) */
+ if (vfs_ChDir(conn,conn->connectpath) != 0) {
+ DEBUG(0,("%s (%s) Can't change directory to %s (%s)\n",
+ get_remote_machine_name(), conn->client_address,
+ conn->connectpath,strerror(errno)));
+ change_to_root_user();
+ yield_connection(conn, lp_servicename(SNUM(conn)));
+ conn_free(conn);
+ *status = NT_STATUS_BAD_NETWORK_NAME;
+ return NULL;
+ }
+#else
+ /* the alternative is just to check the directory exists */
+ if (stat(conn->connectpath, &st) != 0 || !S_ISDIR(st.st_mode)) {
+ DEBUG(0,("'%s' does not exist or is not a directory, when connecting to [%s]\n", conn->connectpath, lp_servicename(SNUM(conn))));
+ change_to_root_user();
+ yield_connection(conn, lp_servicename(SNUM(conn)));
+ conn_free(conn);
+ *status = NT_STATUS_BAD_NETWORK_NAME;
+ return NULL;
+ }
+#endif
+
+ string_set(&conn->origpath,conn->connectpath);
+
+#if SOFTLINK_OPTIMISATION
+ /* resolve any soft links early if possible */
+ if (vfs_ChDir(conn,conn->connectpath) == 0) {
+ pstring s;
+ pstrcpy(s,conn->connectpath);
+ vfs_GetWd(conn,s);
+ string_set(&conn->connectpath,s);
+ vfs_ChDir(conn,conn->connectpath);
+ }
+#endif
+
+ /*
+ * Print out the 'connected as' stuff here as we need
+ * to know the effective uid and gid we will be using
+ * (at least initially).
+ */
+
+ if( DEBUGLVL( IS_IPC(conn) ? 3 : 1 ) ) {
+ dbgtext( "%s (%s) ", get_remote_machine_name(), conn->client_address );
+ dbgtext( "%s", srv_is_signing_active() ? "signed " : "");
+ dbgtext( "connect to service %s ", lp_servicename(SNUM(conn)) );
+ dbgtext( "initially as user %s ", user );
+ dbgtext( "(uid=%d, gid=%d) ", (int)geteuid(), (int)getegid() );
+ dbgtext( "(pid %d)\n", (int)sys_getpid() );
+ }
+
+ /* Add veto/hide lists */
+ if (!IS_IPC(conn) && !IS_PRINT(conn)) {
+ set_namearray( &conn->veto_list, lp_veto_files(SNUM(conn)));
+ set_namearray( &conn->hide_list, lp_hide_files(SNUM(conn)));
+ set_namearray( &conn->veto_oplock_list, lp_veto_oplocks(SNUM(conn)));
+ }
+
+ /* Invoke VFS make connection hook */
+
+ if (SMB_VFS_CONNECT(conn, lp_servicename(snum), user) < 0) {
+ DEBUG(0,("make_connection: VFS make connection failed!\n"));
+ change_to_root_user();
+ conn_free(conn);
+ *status = NT_STATUS_UNSUCCESSFUL;
+ return NULL;
+ }
+
+ /* we've finished with the user stuff - go back to root */
+ change_to_root_user();
+
+ return(conn);
+}
+
+/***************************************************************************************
+ Simple wrapper function for make_connection() to include a call to
+ vfs_chdir()
+ **************************************************************************************/
+
+connection_struct *make_connection_with_chdir(const char *service_in, DATA_BLOB password,
+ const char *dev, uint16 vuid, NTSTATUS *status)
+{
+ connection_struct *conn = NULL;
+
+ conn = make_connection(service_in, password, dev, vuid, status);
+
+ /*
+ * make_connection() does not change the directory for us any more
+ * so we have to do it as a separate step --jerry
+ */
+
+ if ( conn && vfs_ChDir(conn,conn->connectpath) != 0 ) {
+ DEBUG(0,("move_driver_to_download_area: Can't change directory to %s for [print$] (%s)\n",
+ conn->connectpath,strerror(errno)));
+ yield_connection(conn, lp_servicename(SNUM(conn)));
+ conn_free(conn);
+ *status = NT_STATUS_UNSUCCESSFUL;
+ return NULL;
+ }
+
+ return conn;
+}
+
+/****************************************************************************
+ Make a connection to a service.
+ *
+ * @param service
+****************************************************************************/
+
+connection_struct *make_connection(const char *service_in, DATA_BLOB password,
+ const char *pdev, uint16 vuid, NTSTATUS *status)
+{
+ uid_t euid;
+ user_struct *vuser = NULL;
+ fstring service;
+ fstring dev;
+ int snum = -1;
+
+ fstrcpy(dev, pdev);
+
+ /* This must ONLY BE CALLED AS ROOT. As it exits this function as root. */
+ if (!non_root_mode() && (euid = geteuid()) != 0) {
+ DEBUG(0,("make_connection: PANIC ERROR. Called as nonroot (%u)\n", (unsigned int)euid ));
+ smb_panic("make_connection: PANIC ERROR. Called as nonroot\n");
+ }
+
+ if(lp_security() != SEC_SHARE) {
+ vuser = get_valid_user_struct(vuid);
+ if (!vuser) {
+ DEBUG(1,("make_connection: refusing to connect with no session setup\n"));
+ *status = NT_STATUS_ACCESS_DENIED;
+ return NULL;
+ }
+ }
+
+ /* Logic to try and connect to the correct [homes] share, preferably without too many
+ getpwnam() lookups. This is particulary nasty for winbind usernames, where the
+ share name isn't the same as unix username.
+
+ The snum of the homes share is stored on the vuser at session setup time.
+ */
+
+ if (strequal(service_in,HOMES_NAME)) {
+ if(lp_security() != SEC_SHARE) {
+ DATA_BLOB no_pw = data_blob(NULL, 0);
+ if (vuser->homes_snum == -1) {
+ DEBUG(2, ("[homes] share not available for this user because it was not found or created at session setup time\n"));
+ *status = NT_STATUS_BAD_NETWORK_NAME;
+ return NULL;
+ }
+ DEBUG(5, ("making a connection to [homes] service created at session setup time\n"));
+ return make_connection_snum(vuser->homes_snum,
+ vuser, no_pw,
+ dev, status);
+ } else {
+ /* Security = share. Try with current_user_info.smb_name
+ * as the username. */
+ if (*current_user_info.smb_name) {
+ fstring unix_username;
+ fstrcpy(unix_username,
+ current_user_info.smb_name);
+ map_username(unix_username);
+ snum = find_service(unix_username);
+ }
+ if (snum != -1) {
+ DEBUG(5, ("making a connection to 'homes' service %s based on security=share\n", service_in));
+ return make_connection_snum(snum, NULL,
+ password,
+ dev, status);
+ }
+ }
+ } else if ((lp_security() != SEC_SHARE) && (vuser->homes_snum != -1)
+ && strequal(service_in, lp_servicename(vuser->homes_snum))) {
+ DATA_BLOB no_pw = data_blob(NULL, 0);
+ DEBUG(5, ("making a connection to 'homes' service [%s] created at session setup time\n", service_in));
+ return make_connection_snum(vuser->homes_snum,
+ vuser, no_pw,
+ dev, status);
+ }
+
+ fstrcpy(service, service_in);
+
+ strlower_m(service);
+
+ snum = find_service(service);
+
+ if (snum < 0) {
+ if (strequal(service,"IPC$") || strequal(service,"ADMIN$")) {
+ DEBUG(3,("refusing IPC connection to %s\n", service));
+ *status = NT_STATUS_ACCESS_DENIED;
+ return NULL;
+ }
+
+ DEBUG(0,("%s (%s) couldn't find service %s\n",
+ get_remote_machine_name(), client_addr(), service));
+ *status = NT_STATUS_BAD_NETWORK_NAME;
+ return NULL;
+ }
+
+ /* Handle non-Dfs clients attempting connections to msdfs proxy */
+ if (lp_host_msdfs() && (*lp_msdfs_proxy(snum) != '\0')) {
+ DEBUG(3, ("refusing connection to dfs proxy '%s'\n", service));
+ *status = NT_STATUS_BAD_NETWORK_NAME;
+ return NULL;
+ }
+
+ DEBUG(5, ("making a connection to 'normal' service %s\n", service));
+
+ return make_connection_snum(snum, vuser,
+ password,
+ dev, status);
+}
+
+/****************************************************************************
+close a cnum
+****************************************************************************/
+void close_cnum(connection_struct *conn, uint16 vuid)
+{
+ DirCacheFlush(SNUM(conn));
+
+ change_to_root_user();
+
+ DEBUG(IS_IPC(conn)?3:1, ("%s (%s) closed connection to service %s\n",
+ get_remote_machine_name(),conn->client_address,
+ lp_servicename(SNUM(conn))));
+
+ /* Call VFS disconnect hook */
+ SMB_VFS_DISCONNECT(conn);
+
+ yield_connection(conn, lp_servicename(SNUM(conn)));
+
+ file_close_conn(conn);
+ dptr_closecnum(conn);
+
+ /* make sure we leave the directory available for unmount */
+ vfs_ChDir(conn, "/");
+
+ /* execute any "postexec = " line */
+ if (*lp_postexec(SNUM(conn)) &&
+ change_to_user(conn, vuid)) {
+ pstring cmd;
+ pstrcpy(cmd,lp_postexec(SNUM(conn)));
+ standard_sub_conn(conn,cmd,sizeof(cmd));
+ smbrun(cmd,NULL);
+ change_to_root_user();
+ }
+
+ change_to_root_user();
+ /* execute any "root postexec = " line */
+ if (*lp_rootpostexec(SNUM(conn))) {
+ pstring cmd;
+ pstrcpy(cmd,lp_rootpostexec(SNUM(conn)));
+ standard_sub_conn(conn,cmd,sizeof(cmd));
+ smbrun(cmd,NULL);
+ }
+
+ conn_free(conn);
+}
diff --git a/source/smbd/session.c b/source/smbd/session.c
new file mode 100644
index 00000000000..61118f13dd9
--- /dev/null
+++ b/source/smbd/session.c
@@ -0,0 +1,250 @@
+/*
+ Unix SMB/CIFS implementation.
+ session handling for utmp and PAM
+ Copyright (C) tridge@samba.org 2001
+ Copyright (C) abartlet@pcug.org.au 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 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.
+*/
+
+/* a "session" is claimed when we do a SessionSetupX operation
+ and is yielded when the corresponding vuid is destroyed.
+
+ sessions are used to populate utmp and PAM session structures
+*/
+
+#include "includes.h"
+
+static TDB_CONTEXT *tdb;
+
+BOOL session_init(void)
+{
+ if (tdb)
+ return True;
+
+ tdb = tdb_open_ex(lock_path("sessionid.tdb"), 0, TDB_CLEAR_IF_FIRST|TDB_DEFAULT,
+ O_RDWR | O_CREAT, 0644, smbd_tdb_log);
+ if (!tdb) {
+ DEBUG(1,("session_init: failed to open sessionid tdb\n"));
+ return False;
+ }
+
+ return True;
+}
+
+/* called when a session is created */
+BOOL session_claim(user_struct *vuser)
+{
+ int i = 0;
+ TDB_DATA data;
+ struct sockaddr sa;
+ struct in_addr *client_ip;
+ struct sessionid sessionid;
+ uint32 pid = (uint32)sys_getpid();
+ TDB_DATA key;
+ fstring keystr;
+ char * hostname;
+ int tdb_store_flag; /* If using utmp, we do an inital 'lock hold' store,
+ but we don't need this if we are just using the
+ (unique) pid/vuid combination */
+
+ vuser->session_keystr = NULL;
+
+ /* don't register sessions for the guest user - its just too
+ expensive to go through pam session code for browsing etc */
+ if (vuser->guest) {
+ return True;
+ }
+
+ if (!session_init())
+ return False;
+
+ ZERO_STRUCT(sessionid);
+
+ data.dptr = NULL;
+ data.dsize = 0;
+
+ if (lp_utmp()) {
+ for (i=1;i<MAX_SESSION_ID;i++) {
+ slprintf(keystr, sizeof(keystr)-1, "ID/%d", i);
+ key.dptr = keystr;
+ key.dsize = strlen(keystr)+1;
+
+ if (tdb_store(tdb, key, data, TDB_INSERT) == 0) break;
+ }
+
+ if (i == MAX_SESSION_ID) {
+ DEBUG(1,("session_claim: out of session IDs (max is %d)\n",
+ MAX_SESSION_ID));
+ return False;
+ }
+ slprintf(sessionid.id_str, sizeof(sessionid.id_str)-1, SESSION_UTMP_TEMPLATE, i);
+ tdb_store_flag = TDB_MODIFY;
+ } else
+ {
+ slprintf(keystr, sizeof(keystr)-1, "ID/%lu/%u",
+ (long unsigned int)sys_getpid(),
+ vuser->vuid);
+ slprintf(sessionid.id_str, sizeof(sessionid.id_str)-1,
+ SESSION_TEMPLATE, (long unsigned int)sys_getpid(),
+ vuser->vuid);
+
+ key.dptr = keystr;
+ key.dsize = strlen(keystr)+1;
+
+ tdb_store_flag = TDB_REPLACE;
+ }
+
+ /* If 'hostname lookup' == yes, then do the DNS lookup. This is
+ needed because utmp and PAM both expect DNS names
+
+ client_name() handles this case internally.
+ */
+
+ hostname = client_name();
+ if (strcmp(hostname, "UNKNOWN") == 0) {
+ hostname = client_addr();
+ }
+
+ fstrcpy(sessionid.username, vuser->user.unix_name);
+ fstrcpy(sessionid.hostname, hostname);
+ sessionid.id_num = i; /* Only valid for utmp sessions */
+ sessionid.pid = pid;
+ sessionid.uid = vuser->uid;
+ sessionid.gid = vuser->gid;
+ fstrcpy(sessionid.remote_machine, get_remote_machine_name());
+ fstrcpy(sessionid.ip_addr, client_addr());
+
+ client_ip = client_inaddr(&sa);
+
+ if (!smb_pam_claim_session(sessionid.username, sessionid.id_str, sessionid.hostname)) {
+ DEBUG(1,("pam_session rejected the session for %s [%s]\n",
+ sessionid.username, sessionid.id_str));
+ if (tdb_store_flag == TDB_MODIFY) {
+ tdb_delete(tdb, key);
+ }
+ return False;
+ }
+
+ data.dptr = (char *)&sessionid;
+ data.dsize = sizeof(sessionid);
+ if (tdb_store(tdb, key, data, tdb_store_flag) != 0) {
+ DEBUG(1,("session_claim: unable to create session id record\n"));
+ return False;
+ }
+
+ if (lp_utmp()) {
+ sys_utmp_claim(sessionid.username, sessionid.hostname,
+ client_ip,
+ sessionid.id_str, sessionid.id_num);
+ }
+
+ vuser->session_keystr = strdup(keystr);
+ if (!vuser->session_keystr) {
+ DEBUG(0, ("session_claim: strdup() failed for session_keystr\n"));
+ return False;
+ }
+ return True;
+}
+
+/* called when a session is destroyed */
+void session_yield(user_struct *vuser)
+{
+ TDB_DATA dbuf;
+ struct sessionid sessionid;
+ struct in_addr *client_ip;
+ TDB_DATA key;
+
+ if (!tdb) return;
+
+ if (!vuser->session_keystr) {
+ return;
+ }
+
+ key.dptr = vuser->session_keystr;
+ key.dsize = strlen(vuser->session_keystr)+1;
+
+ dbuf = tdb_fetch(tdb, key);
+
+ if (dbuf.dsize != sizeof(sessionid))
+ return;
+
+ memcpy(&sessionid, dbuf.dptr, sizeof(sessionid));
+
+ client_ip = interpret_addr2(sessionid.ip_addr);
+
+ SAFE_FREE(dbuf.dptr);
+
+ if (lp_utmp()) {
+ sys_utmp_yield(sessionid.username, sessionid.hostname,
+ client_ip,
+ sessionid.id_str, sessionid.id_num);
+ }
+
+ smb_pam_close_session(sessionid.username, sessionid.id_str, sessionid.hostname);
+
+ tdb_delete(tdb, key);
+}
+
+static BOOL session_traverse(int (*fn)(TDB_CONTEXT *, TDB_DATA, TDB_DATA, void *), void *state)
+{
+ if (!session_init()) {
+ DEBUG(3, ("No tdb opened\n"));
+ return False;
+ }
+
+ tdb_traverse(tdb, fn, state);
+ return True;
+}
+
+struct session_list {
+ int count;
+ struct sessionid *sessions;
+};
+
+static int gather_sessioninfo(TDB_CONTEXT *stdb, TDB_DATA kbuf, TDB_DATA dbuf,
+ void *state)
+{
+ struct session_list *sesslist = (struct session_list *) state;
+ const struct sessionid *current = (const struct sessionid *) dbuf.dptr;
+
+ sesslist->count += 1;
+ sesslist->sessions = REALLOC(sesslist->sessions, sesslist->count *
+ sizeof(struct sessionid));
+
+ memcpy(&sesslist->sessions[sesslist->count - 1], current,
+ sizeof(struct sessionid));
+ DEBUG(7,("gather_sessioninfo session from %s@%s\n",
+ current->username, current->remote_machine));
+ return 0;
+}
+
+int list_sessions(struct sessionid **session_list)
+{
+ struct session_list sesslist;
+
+ sesslist.count = 0;
+ sesslist.sessions = NULL;
+
+ if (!session_traverse(gather_sessioninfo, (void *) &sesslist)) {
+ DEBUG(3, ("Session traverse failed\n"));
+ SAFE_FREE(sesslist.sessions);
+ *session_list = NULL;
+ return 0;
+ }
+
+ *session_list = sesslist.sessions;
+ return sesslist.count;
+}
diff --git a/source/smbd/sesssetup.c b/source/smbd/sesssetup.c
new file mode 100644
index 00000000000..b8777be6971
--- /dev/null
+++ b/source/smbd/sesssetup.c
@@ -0,0 +1,938 @@
+/*
+ Unix SMB/CIFS implementation.
+ handle SMBsessionsetup
+ Copyright (C) Andrew Tridgell 1998-2001
+ Copyright (C) Andrew Bartlett 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 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"
+
+uint32 global_client_caps = 0;
+
+static struct auth_ntlmssp_state *global_ntlmssp_state;
+
+/*
+ on a logon error possibly map the error to success if "map to guest"
+ is set approriately
+*/
+static NTSTATUS do_map_to_guest(NTSTATUS status, auth_serversupplied_info **server_info,
+ const char *user, const char *domain)
+{
+ if (NT_STATUS_EQUAL(status, NT_STATUS_NO_SUCH_USER)) {
+ if ((lp_map_to_guest() == MAP_TO_GUEST_ON_BAD_USER) ||
+ (lp_map_to_guest() == MAP_TO_GUEST_ON_BAD_PASSWORD)) {
+ DEBUG(3,("No such user %s [%s] - using guest account\n",
+ user, domain));
+ status = make_server_info_guest(server_info);
+ }
+ }
+
+ if (NT_STATUS_EQUAL(status, NT_STATUS_WRONG_PASSWORD)) {
+ if (lp_map_to_guest() == MAP_TO_GUEST_ON_BAD_PASSWORD) {
+ DEBUG(3,("Registered username %s for guest access\n",user));
+ status = make_server_info_guest(server_info);
+ }
+ }
+
+ return status;
+}
+
+/****************************************************************************
+ Add the standard 'Samba' signature to the end of the session setup.
+****************************************************************************/
+
+static int add_signature(char *outbuf, char *p)
+{
+ char *start = p;
+ fstring lanman;
+
+ fstr_sprintf( lanman, "Samba %s", SAMBA_VERSION_STRING);
+
+ p += srvstr_push(outbuf, p, "Unix", -1, STR_TERMINATE);
+ p += srvstr_push(outbuf, p, lanman, -1, STR_TERMINATE);
+ p += srvstr_push(outbuf, p, lp_workgroup(), -1, STR_TERMINATE);
+
+ return PTR_DIFF(p, start);
+}
+
+/****************************************************************************
+ Send a security blob via a session setup reply.
+****************************************************************************/
+
+static BOOL reply_sesssetup_blob(connection_struct *conn, char *outbuf,
+ DATA_BLOB blob, NTSTATUS nt_status)
+{
+ char *p;
+
+ set_message(outbuf,4,0,True);
+
+ nt_status = nt_status_squash(nt_status);
+ SIVAL(outbuf, smb_rcls, NT_STATUS_V(nt_status));
+ SSVAL(outbuf, smb_vwv0, 0xFF); /* no chaining possible */
+ SSVAL(outbuf, smb_vwv3, blob.length);
+ p = smb_buf(outbuf);
+
+ /* should we cap this? */
+ memcpy(p, blob.data, blob.length);
+ p += blob.length;
+
+ p += add_signature( outbuf, p );
+
+ set_message_end(outbuf,p);
+
+ return send_smb(smbd_server_fd(),outbuf);
+}
+
+/****************************************************************************
+ Do a 'guest' logon, getting back the
+****************************************************************************/
+
+static NTSTATUS check_guest_password(auth_serversupplied_info **server_info)
+{
+ struct auth_context *auth_context;
+ auth_usersupplied_info *user_info = NULL;
+
+ NTSTATUS nt_status;
+ unsigned char chal[8];
+
+ ZERO_STRUCT(chal);
+
+ DEBUG(3,("Got anonymous request\n"));
+
+ if (!NT_STATUS_IS_OK(nt_status = make_auth_context_fixed(&auth_context, chal))) {
+ return nt_status;
+ }
+
+ if (!make_user_info_guest(&user_info)) {
+ (auth_context->free)(&auth_context);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ nt_status = auth_context->check_ntlm_password(auth_context, user_info, server_info);
+ (auth_context->free)(&auth_context);
+ free_user_info(&user_info);
+ return nt_status;
+}
+
+
+#ifdef HAVE_KRB5
+/****************************************************************************
+reply to a session setup spnego negotiate packet for kerberos
+****************************************************************************/
+static int reply_spnego_kerberos(connection_struct *conn,
+ char *inbuf, char *outbuf,
+ int length, int bufsize,
+ DATA_BLOB *secblob)
+{
+ DATA_BLOB ticket;
+ char *client, *p, *domain;
+ fstring netbios_domain_name;
+ struct passwd *pw;
+ char *user;
+ int sess_vuid;
+ NTSTATUS ret;
+ DATA_BLOB auth_data;
+ DATA_BLOB ap_rep, ap_rep_wrapped, response;
+ auth_serversupplied_info *server_info = NULL;
+ DATA_BLOB session_key;
+ uint8 tok_id[2];
+ BOOL foreign = False;
+ DATA_BLOB nullblob = data_blob(NULL, 0);
+ fstring real_username;
+
+ ZERO_STRUCT(ticket);
+ ZERO_STRUCT(auth_data);
+ ZERO_STRUCT(ap_rep);
+ ZERO_STRUCT(ap_rep_wrapped);
+ ZERO_STRUCT(response);
+
+ if (!spnego_parse_krb5_wrap(*secblob, &ticket, tok_id)) {
+ return ERROR_NT(NT_STATUS_LOGON_FAILURE);
+ }
+
+ ret = ads_verify_ticket(lp_realm(), &ticket, &client, &auth_data, &ap_rep, &session_key);
+
+ data_blob_free(&ticket);
+
+ if (!NT_STATUS_IS_OK(ret)) {
+ DEBUG(1,("Failed to verify incoming ticket!\n"));
+ return ERROR_NT(NT_STATUS_LOGON_FAILURE);
+ }
+
+ data_blob_free(&auth_data);
+
+ DEBUG(3,("Ticket name is [%s]\n", client));
+
+ p = strchr_m(client, '@');
+ if (!p) {
+ DEBUG(3,("Doesn't look like a valid principal\n"));
+ data_blob_free(&ap_rep);
+ SAFE_FREE(client);
+ return ERROR_NT(NT_STATUS_LOGON_FAILURE);
+ }
+
+ *p = 0;
+ if (!strequal(p+1, lp_realm())) {
+ DEBUG(3,("Ticket for foreign realm %s@%s\n", client, p+1));
+ if (!lp_allow_trusted_domains()) {
+ data_blob_free(&ap_rep);
+ SAFE_FREE(client);
+ return ERROR_NT(NT_STATUS_LOGON_FAILURE);
+ }
+ foreign = True;
+ }
+
+ /* this gives a fully qualified user name (ie. with full realm).
+ that leads to very long usernames, but what else can we do? */
+
+ domain = p+1;
+
+ {
+ /* If we have winbind running, we can (and must) shorten the
+ username by using the short netbios name. Otherwise we will
+ have inconsistent user names. With Kerberos, we get the
+ fully qualified realm, with ntlmssp we get the short
+ name. And even w2k3 does use ntlmssp if you for example
+ connect to an ip address. */
+
+ struct winbindd_request wb_request;
+ struct winbindd_response wb_response;
+ NSS_STATUS wb_result;
+
+ ZERO_STRUCT(wb_request);
+ ZERO_STRUCT(wb_response);
+
+ DEBUG(10, ("Mapping [%s] to short name\n", domain));
+
+ fstrcpy(wb_request.domain_name, domain);
+
+ wb_result = winbindd_request(WINBINDD_DOMAIN_INFO,
+ &wb_request, &wb_response);
+
+ if (wb_result == NSS_STATUS_SUCCESS) {
+
+ fstrcpy(netbios_domain_name,
+ wb_response.data.domain_info.name);
+ domain = netbios_domain_name;
+
+ DEBUG(10, ("Mapped to [%s]\n", domain));
+ } else {
+ DEBUG(3, ("Could not find short name -- winbind "
+ "not running?\n"));
+ }
+ }
+
+ asprintf(&user, "%s%c%s", domain, *lp_winbind_separator(), client);
+
+ /* lookup the passwd struct, create a new user if necessary */
+
+ pw = smb_getpwnam( user, real_username, True );
+
+ if (!pw) {
+ DEBUG(1,("Username %s is invalid on this system\n",user));
+ SAFE_FREE(user);
+ SAFE_FREE(client);
+ data_blob_free(&ap_rep);
+ return ERROR_NT(NT_STATUS_LOGON_FAILURE);
+ }
+
+ /* setup the string used by %U */
+
+ sub_set_smb_name( real_username );
+ reload_services(True);
+
+ if (!NT_STATUS_IS_OK(ret = make_server_info_pw(&server_info, real_username, pw)))
+ {
+ DEBUG(1,("make_server_info_from_pw failed!\n"));
+ SAFE_FREE(user);
+ SAFE_FREE(client);
+ data_blob_free(&ap_rep);
+ return ERROR_NT(ret);
+ }
+
+ /* make_server_info_pw does not set the domain. Without this we end up
+ * with the local netbios name in substitutions for %D. */
+
+ if (server_info->sam_account != NULL) {
+ pdb_set_domain(server_info->sam_account, domain, PDB_SET);
+ }
+
+ /* register_vuid keeps the server info */
+ sess_vuid = register_vuid(server_info, session_key, nullblob, client);
+
+ SAFE_FREE(user);
+ SAFE_FREE(client);
+
+ if (sess_vuid == -1) {
+ ret = NT_STATUS_LOGON_FAILURE;
+ } else {
+ /* current_user_info is changed on new vuid */
+ reload_services( True );
+
+ set_message(outbuf,4,0,True);
+ SSVAL(outbuf, smb_vwv3, 0);
+
+ if (server_info->guest) {
+ SSVAL(outbuf,smb_vwv2,1);
+ }
+
+ SSVAL(outbuf, smb_uid, sess_vuid);
+
+ if (!server_info->guest && !srv_signing_started()) {
+ /* We need to start the signing engine
+ * here but a W2K client sends the old
+ * "BSRSPYL " signature instead of the
+ * correct one. Subsequent packets will
+ * be correct.
+ */
+ srv_check_sign_mac(inbuf, False);
+ }
+ }
+
+ /* wrap that up in a nice GSS-API wrapping */
+ if (NT_STATUS_IS_OK(ret)) {
+ ap_rep_wrapped = spnego_gen_krb5_wrap(ap_rep, TOK_ID_KRB_AP_REP);
+ } else {
+ ap_rep_wrapped = data_blob(NULL, 0);
+ }
+ response = spnego_gen_auth_response(&ap_rep_wrapped, ret, OID_KERBEROS5_OLD);
+ reply_sesssetup_blob(conn, outbuf, response, ret);
+
+ data_blob_free(&ap_rep);
+ data_blob_free(&ap_rep_wrapped);
+ data_blob_free(&response);
+
+ return -1; /* already replied */
+}
+#endif
+
+/****************************************************************************
+ Send a session setup reply, wrapped in SPNEGO.
+ Get vuid and check first.
+ End the NTLMSSP exchange context if we are OK/complete fail
+***************************************************************************/
+
+static BOOL reply_spnego_ntlmssp(connection_struct *conn, char *inbuf, char *outbuf,
+ AUTH_NTLMSSP_STATE **auth_ntlmssp_state,
+ DATA_BLOB *ntlmssp_blob, NTSTATUS nt_status)
+{
+ BOOL ret;
+ DATA_BLOB response;
+ struct auth_serversupplied_info *server_info = NULL;
+
+ if (NT_STATUS_IS_OK(nt_status)) {
+ server_info = (*auth_ntlmssp_state)->server_info;
+ } else {
+ nt_status = do_map_to_guest(nt_status,
+ &server_info,
+ (*auth_ntlmssp_state)->ntlmssp_state->user,
+ (*auth_ntlmssp_state)->ntlmssp_state->domain);
+ }
+
+ if (NT_STATUS_IS_OK(nt_status)) {
+ int sess_vuid;
+ DATA_BLOB nullblob = data_blob(NULL, 0);
+ DATA_BLOB session_key = data_blob((*auth_ntlmssp_state)->ntlmssp_state->session_key.data, (*auth_ntlmssp_state)->ntlmssp_state->session_key.length);
+
+ /* register_vuid keeps the server info */
+ sess_vuid = register_vuid(server_info, session_key, nullblob, (*auth_ntlmssp_state)->ntlmssp_state->user);
+ (*auth_ntlmssp_state)->server_info = NULL;
+
+ if (sess_vuid == -1) {
+ nt_status = NT_STATUS_LOGON_FAILURE;
+ } else {
+
+ /* current_user_info is changed on new vuid */
+ reload_services( True );
+
+ set_message(outbuf,4,0,True);
+ SSVAL(outbuf, smb_vwv3, 0);
+
+ if (server_info->guest) {
+ SSVAL(outbuf,smb_vwv2,1);
+ }
+
+ SSVAL(outbuf,smb_uid,sess_vuid);
+
+ if (!server_info->guest && !srv_signing_started()) {
+ /* We need to start the signing engine
+ * here but a W2K client sends the old
+ * "BSRSPYL " signature instead of the
+ * correct one. Subsequent packets will
+ * be correct.
+ */
+
+ srv_check_sign_mac(inbuf, False);
+ }
+ }
+ }
+
+ response = spnego_gen_auth_response(ntlmssp_blob, nt_status, OID_NTLMSSP);
+ ret = reply_sesssetup_blob(conn, outbuf, response, nt_status);
+ data_blob_free(&response);
+
+ /* NT_STATUS_MORE_PROCESSING_REQUIRED from our NTLMSSP code tells us,
+ and the other end, that we are not finished yet. */
+
+ if (!ret || !NT_STATUS_EQUAL(nt_status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
+ auth_ntlmssp_end(auth_ntlmssp_state);
+ }
+
+ return ret;
+}
+
+/****************************************************************************
+ Reply to a session setup spnego negotiate packet.
+****************************************************************************/
+
+static int reply_spnego_negotiate(connection_struct *conn,
+ char *inbuf,
+ char *outbuf,
+ int length, int bufsize,
+ DATA_BLOB blob1)
+{
+ char *OIDs[ASN1_MAX_OIDS];
+ DATA_BLOB secblob;
+ int i;
+ DATA_BLOB chal;
+ BOOL got_kerberos = False;
+ NTSTATUS nt_status;
+
+ /* parse out the OIDs and the first sec blob */
+ if (!parse_negTokenTarg(blob1, OIDs, &secblob)) {
+ return ERROR_NT(NT_STATUS_LOGON_FAILURE);
+ }
+
+ /* only look at the first OID for determining the mechToken --
+ accoirding to RFC2478, we should choose the one we want
+ and renegotiate, but i smell a client bug here..
+
+ Problem observed when connecting to a member (samba box)
+ of an AD domain as a user in a Samba domain. Samba member
+ server sent back krb5/mskrb5/ntlmssp as mechtypes, but the
+ client (2ksp3) replied with ntlmssp/mskrb5/krb5 and an
+ NTLMSSP mechtoken. --jerry */
+
+ if (strcmp(OID_KERBEROS5, OIDs[0]) == 0 ||
+ strcmp(OID_KERBEROS5_OLD, OIDs[0]) == 0) {
+ got_kerberos = True;
+ }
+
+ for (i=0;OIDs[i];i++) {
+ DEBUG(3,("Got OID %s\n", OIDs[i]));
+ free(OIDs[i]);
+ }
+ DEBUG(3,("Got secblob of size %lu\n", (unsigned long)secblob.length));
+
+#ifdef HAVE_KRB5
+ if (got_kerberos && (SEC_ADS == lp_security())) {
+ int ret = reply_spnego_kerberos(conn, inbuf, outbuf,
+ length, bufsize, &secblob);
+ data_blob_free(&secblob);
+ return ret;
+ }
+#endif
+
+ if (global_ntlmssp_state) {
+ auth_ntlmssp_end(&global_ntlmssp_state);
+ }
+
+ nt_status = auth_ntlmssp_start(&global_ntlmssp_state);
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ return ERROR_NT(nt_status);
+ }
+
+ nt_status = auth_ntlmssp_update(global_ntlmssp_state,
+ secblob, &chal);
+
+ data_blob_free(&secblob);
+
+ reply_spnego_ntlmssp(conn, inbuf, outbuf, &global_ntlmssp_state,
+ &chal, nt_status);
+
+ data_blob_free(&chal);
+
+ /* already replied */
+ return -1;
+}
+
+/****************************************************************************
+ Reply to a session setup spnego auth packet.
+****************************************************************************/
+
+static int reply_spnego_auth(connection_struct *conn, char *inbuf, char *outbuf,
+ int length, int bufsize,
+ DATA_BLOB blob1)
+{
+ DATA_BLOB auth, auth_reply;
+ NTSTATUS nt_status = NT_STATUS_INVALID_PARAMETER;
+
+ if (!spnego_parse_auth(blob1, &auth)) {
+#if 0
+ file_save("auth.dat", blob1.data, blob1.length);
+#endif
+ return ERROR_NT(NT_STATUS_INVALID_PARAMETER);
+ }
+
+ if (!global_ntlmssp_state) {
+ /* auth before negotiatiate? */
+ return ERROR_NT(NT_STATUS_INVALID_PARAMETER);
+ }
+
+ nt_status = auth_ntlmssp_update(global_ntlmssp_state,
+ auth, &auth_reply);
+
+ data_blob_free(&auth);
+
+ reply_spnego_ntlmssp(conn, inbuf, outbuf, &global_ntlmssp_state,
+ &auth_reply, nt_status);
+
+ data_blob_free(&auth_reply);
+
+ /* and tell smbd that we have already replied to this packet */
+ return -1;
+}
+
+/****************************************************************************
+ Reply to a session setup command.
+****************************************************************************/
+
+static int reply_sesssetup_and_X_spnego(connection_struct *conn, char *inbuf,
+ char *outbuf,
+ int length,int bufsize)
+{
+ uint8 *p;
+ DATA_BLOB blob1;
+ int ret;
+ size_t bufrem;
+ fstring native_os, native_lanman, primary_domain;
+ char *p2;
+ uint16 data_blob_len = SVAL(inbuf, smb_vwv7);
+ enum remote_arch_types ra_type = get_remote_arch();
+
+ DEBUG(3,("Doing spnego session setup\n"));
+
+ if (global_client_caps == 0) {
+ global_client_caps = IVAL(inbuf,smb_vwv10);
+
+ if (!(global_client_caps & CAP_STATUS32)) {
+ remove_from_common_flags2(FLAGS2_32_BIT_ERROR_CODES);
+ }
+
+ }
+
+ p = (uint8 *)smb_buf(inbuf);
+
+ if (data_blob_len == 0) {
+ /* an invalid request */
+ return ERROR_NT(NT_STATUS_LOGON_FAILURE);
+ }
+
+ bufrem = smb_bufrem(inbuf, p);
+ /* pull the spnego blob */
+ blob1 = data_blob(p, MIN(bufrem, data_blob_len));
+
+#if 0
+ file_save("negotiate.dat", blob1.data, blob1.length);
+#endif
+
+ p2 = inbuf + smb_vwv13 + data_blob_len;
+ p2 += srvstr_pull_buf(inbuf, native_os, p2, sizeof(native_os), STR_TERMINATE);
+ p2 += srvstr_pull_buf(inbuf, native_lanman, p2, sizeof(native_lanman), STR_TERMINATE);
+ p2 += srvstr_pull_buf(inbuf, primary_domain, p2, sizeof(primary_domain), STR_TERMINATE);
+ DEBUG(3,("NativeOS=[%s] NativeLanMan=[%s] PrimaryDomain=[%s]\n",
+ native_os, native_lanman, primary_domain));
+
+ if ( ra_type == RA_WIN2K ) {
+ /* Windows 2003 doesn't set the native lanman string,
+ but does set primary domain which is a bug I think */
+
+ if ( !strlen(native_lanman) )
+ ra_lanman_string( primary_domain );
+ else
+ ra_lanman_string( native_lanman );
+ }
+
+ if (blob1.data[0] == ASN1_APPLICATION(0)) {
+ /* its a negTokenTarg packet */
+ ret = reply_spnego_negotiate(conn, inbuf, outbuf, length, bufsize, blob1);
+ data_blob_free(&blob1);
+ return ret;
+ }
+
+ if (blob1.data[0] == ASN1_CONTEXT(1)) {
+ /* its a auth packet */
+ ret = reply_spnego_auth(conn, inbuf, outbuf, length, bufsize, blob1);
+ data_blob_free(&blob1);
+ return ret;
+ }
+
+ /* what sort of packet is this? */
+ DEBUG(1,("Unknown packet in reply_sesssetup_and_X_spnego\n"));
+
+ data_blob_free(&blob1);
+
+ return ERROR_NT(NT_STATUS_LOGON_FAILURE);
+}
+
+/****************************************************************************
+ On new VC == 0, shutdown *all* old connections and users.
+ It seems that only NT4.x does this. At W2K and above (XP etc.).
+ a new session setup with VC==0 is ignored.
+****************************************************************************/
+
+static void setup_new_vc_session(void)
+{
+ DEBUG(2,("setup_new_vc_session: New VC == 0, if NT4.x compatible we would close all old resources.\n"));
+#if 0
+ conn_close_all();
+ invalidate_all_vuids();
+#endif
+}
+
+/****************************************************************************
+ Reply to a session setup command.
+****************************************************************************/
+
+int reply_sesssetup_and_X(connection_struct *conn, char *inbuf,char *outbuf,
+ int length,int bufsize)
+{
+ int sess_vuid;
+ int smb_bufsize;
+ DATA_BLOB lm_resp;
+ DATA_BLOB nt_resp;
+ DATA_BLOB plaintext_password;
+ fstring user;
+ fstring sub_user; /* Sainitised username for substituion */
+ fstring domain;
+ fstring native_os;
+ fstring native_lanman;
+ fstring primary_domain;
+ static BOOL done_sesssetup = False;
+ extern BOOL global_encrypted_passwords_negotiated;
+ extern BOOL global_spnego_negotiated;
+ extern int Protocol;
+ extern int max_send;
+
+ auth_usersupplied_info *user_info = NULL;
+ extern struct auth_context *negprot_global_auth_context;
+ auth_serversupplied_info *server_info = NULL;
+
+ NTSTATUS nt_status;
+
+ BOOL doencrypt = global_encrypted_passwords_negotiated;
+
+ DATA_BLOB session_key;
+
+ START_PROFILE(SMBsesssetupX);
+
+ ZERO_STRUCT(lm_resp);
+ ZERO_STRUCT(nt_resp);
+ ZERO_STRUCT(plaintext_password);
+
+ DEBUG(3,("wct=%d flg2=0x%x\n", CVAL(inbuf, smb_wct), SVAL(inbuf, smb_flg2)));
+
+ /* a SPNEGO session setup has 12 command words, whereas a normal
+ NT1 session setup has 13. See the cifs spec. */
+ if (CVAL(inbuf, smb_wct) == 12 &&
+ (SVAL(inbuf, smb_flg2) & FLAGS2_EXTENDED_SECURITY)) {
+ if (!global_spnego_negotiated) {
+ DEBUG(0,("reply_sesssetup_and_X: Rejecting attempt at SPNEGO session setup when it was not negoitiated.\n"));
+ return ERROR_NT(NT_STATUS_UNSUCCESSFUL);
+ }
+
+ if (SVAL(inbuf,smb_vwv4) == 0) {
+ setup_new_vc_session();
+ }
+ return reply_sesssetup_and_X_spnego(conn, inbuf, outbuf, length, bufsize);
+ }
+
+ smb_bufsize = SVAL(inbuf,smb_vwv2);
+
+ if (Protocol < PROTOCOL_NT1) {
+ uint16 passlen1 = SVAL(inbuf,smb_vwv7);
+ if ((passlen1 > MAX_PASS_LEN) || (passlen1 > smb_bufrem(inbuf, smb_buf(inbuf)))) {
+ return ERROR_NT(NT_STATUS_INVALID_PARAMETER);
+ }
+
+ if (doencrypt) {
+ lm_resp = data_blob(smb_buf(inbuf), passlen1);
+ } else {
+ plaintext_password = data_blob(smb_buf(inbuf), passlen1+1);
+ /* Ensure null termination */
+ plaintext_password.data[passlen1] = 0;
+ }
+
+ srvstr_pull_buf(inbuf, user, smb_buf(inbuf)+passlen1, sizeof(user), STR_TERMINATE);
+ *domain = 0;
+
+ } else {
+ uint16 passlen1 = SVAL(inbuf,smb_vwv7);
+ uint16 passlen2 = SVAL(inbuf,smb_vwv8);
+ enum remote_arch_types ra_type = get_remote_arch();
+ char *p = smb_buf(inbuf);
+ char *save_p = smb_buf(inbuf);
+ uint16 byte_count;
+
+
+ if(global_client_caps == 0) {
+ global_client_caps = IVAL(inbuf,smb_vwv11);
+
+ if (!(global_client_caps & CAP_STATUS32)) {
+ remove_from_common_flags2(FLAGS2_32_BIT_ERROR_CODES);
+ }
+
+ /* client_caps is used as final determination if client is NT or Win95.
+ This is needed to return the correct error codes in some
+ circumstances.
+ */
+
+ if(ra_type == RA_WINNT || ra_type == RA_WIN2K || ra_type == RA_WIN95) {
+ if(!(global_client_caps & (CAP_NT_SMBS | CAP_STATUS32))) {
+ set_remote_arch( RA_WIN95);
+ }
+ }
+ }
+
+ if (!doencrypt) {
+ /* both Win95 and WinNT stuff up the password lengths for
+ non-encrypting systems. Uggh.
+
+ if passlen1==24 its a win95 system, and its setting the
+ password length incorrectly. Luckily it still works with the
+ default code because Win95 will null terminate the password
+ anyway
+
+ if passlen1>0 and passlen2>0 then maybe its a NT box and its
+ setting passlen2 to some random value which really stuffs
+ things up. we need to fix that one. */
+
+ if (passlen1 > 0 && passlen2 > 0 && passlen2 != 24 && passlen2 != 1)
+ passlen2 = 0;
+ }
+
+ /* check for nasty tricks */
+ if (passlen1 > MAX_PASS_LEN || passlen1 > smb_bufrem(inbuf, p)) {
+ return ERROR_NT(NT_STATUS_INVALID_PARAMETER);
+ }
+
+ if (passlen2 > MAX_PASS_LEN || passlen2 > smb_bufrem(inbuf, p+passlen1)) {
+ return ERROR_NT(NT_STATUS_INVALID_PARAMETER);
+ }
+
+ /* Save the lanman2 password and the NT md4 password. */
+
+ if ((doencrypt) && (passlen1 != 0) && (passlen1 != 24)) {
+ doencrypt = False;
+ }
+
+ if (doencrypt) {
+ lm_resp = data_blob(p, passlen1);
+ nt_resp = data_blob(p+passlen1, passlen2);
+ } else {
+ pstring pass;
+ BOOL unic=SVAL(inbuf, smb_flg2) & FLAGS2_UNICODE_STRINGS;
+
+ if ((ra_type == RA_WINNT) && (passlen2 == 0) && unic && passlen1) {
+ /* NT4.0 stuffs up plaintext unicode password lengths... */
+ srvstr_pull(inbuf, pass, smb_buf(inbuf) + 1,
+ sizeof(pass), passlen1, STR_TERMINATE);
+ } else {
+ srvstr_pull(inbuf, pass, smb_buf(inbuf),
+ sizeof(pass), unic ? passlen2 : passlen1,
+ STR_TERMINATE);
+ }
+ plaintext_password = data_blob(pass, strlen(pass)+1);
+ }
+
+ p += passlen1 + passlen2;
+ p += srvstr_pull_buf(inbuf, user, p, sizeof(user), STR_TERMINATE);
+ p += srvstr_pull_buf(inbuf, domain, p, sizeof(domain), STR_TERMINATE);
+ p += srvstr_pull_buf(inbuf, native_os, p, sizeof(native_os), STR_TERMINATE);
+ p += srvstr_pull_buf(inbuf, native_lanman, p, sizeof(native_lanman), STR_TERMINATE);
+
+ /* not documented or decoded by Ethereal but there is one more string
+ in the extra bytes which is the same as the PrimaryDomain when using
+ extended security. Windows NT 4 and 2003 use this string to store
+ the native lanman string. Windows 9x does not include a string here
+ at all so we have to check if we have any extra bytes left */
+
+ byte_count = SVAL(inbuf, smb_vwv13);
+ if ( PTR_DIFF(p, save_p) < byte_count)
+ p += srvstr_pull_buf(inbuf, primary_domain, p, sizeof(primary_domain), STR_TERMINATE);
+ else
+ fstrcpy( primary_domain, "null" );
+
+ DEBUG(3,("Domain=[%s] NativeOS=[%s] NativeLanMan=[%s] PrimaryDomain=[%s]\n",
+ domain, native_os, native_lanman, primary_domain));
+
+ if ( ra_type == RA_WIN2K ) {
+ if ( strlen(native_lanman) == 0 )
+ ra_lanman_string( primary_domain );
+ else
+ ra_lanman_string( native_lanman );
+ }
+
+ }
+
+ if (SVAL(inbuf,smb_vwv4) == 0) {
+ setup_new_vc_session();
+ }
+
+ DEBUG(3,("sesssetupX:name=[%s]\\[%s]@[%s]\n", domain, user, get_remote_machine_name()));
+
+ if (*user) {
+ if (global_spnego_negotiated) {
+
+ /* This has to be here, because this is a perfectly valid behaviour for guest logons :-( */
+
+ DEBUG(0,("reply_sesssetup_and_X: Rejecting attempt at 'normal' session setup after negotiating spnego.\n"));
+ return ERROR_NT(NT_STATUS_UNSUCCESSFUL);
+ }
+ fstrcpy(sub_user, user);
+
+ /* setup the string used by %U */
+ sub_set_smb_name(user);
+ } else {
+ fstrcpy(sub_user, lp_guestaccount());
+ }
+
+ sub_set_smb_name(sub_user);
+
+ reload_services(True);
+
+ if (lp_security() == SEC_SHARE) {
+ /* in share level we should ignore any passwords */
+
+ data_blob_free(&lm_resp);
+ data_blob_free(&nt_resp);
+ data_blob_clear_free(&plaintext_password);
+
+ map_username(sub_user);
+ add_session_user(sub_user);
+ /* Then force it to null for the benfit of the code below */
+ *user = 0;
+ }
+
+ if (!*user) {
+
+ nt_status = check_guest_password(&server_info);
+
+ } else if (doencrypt) {
+ if (!negprot_global_auth_context) {
+ DEBUG(0, ("reply_sesssetup_and_X: Attempted encrypted session setup without negprot denied!\n"));
+ return ERROR_NT(NT_STATUS_LOGON_FAILURE);
+ }
+ nt_status = make_user_info_for_reply_enc(&user_info, user, domain,
+ lm_resp, nt_resp);
+ if (NT_STATUS_IS_OK(nt_status)) {
+ nt_status = negprot_global_auth_context->check_ntlm_password(negprot_global_auth_context,
+ user_info,
+ &server_info);
+ }
+ } else {
+ struct auth_context *plaintext_auth_context = NULL;
+ const uint8 *chal;
+ if (NT_STATUS_IS_OK(nt_status = make_auth_context_subsystem(&plaintext_auth_context))) {
+ chal = plaintext_auth_context->get_ntlm_challenge(plaintext_auth_context);
+
+ if (!make_user_info_for_reply(&user_info,
+ user, domain, chal,
+ plaintext_password)) {
+ nt_status = NT_STATUS_NO_MEMORY;
+ }
+
+ if (NT_STATUS_IS_OK(nt_status)) {
+ nt_status = plaintext_auth_context->check_ntlm_password(plaintext_auth_context,
+ user_info,
+ &server_info);
+
+ (plaintext_auth_context->free)(&plaintext_auth_context);
+ }
+ }
+ }
+
+ free_user_info(&user_info);
+
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ nt_status = do_map_to_guest(nt_status, &server_info, user, domain);
+ }
+
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ data_blob_free(&nt_resp);
+ data_blob_free(&lm_resp);
+ data_blob_clear_free(&plaintext_password);
+ return ERROR_NT(nt_status_squash(nt_status));
+ }
+
+ if (server_info->nt_session_key.data) {
+ session_key = data_blob(server_info->nt_session_key.data, server_info->nt_session_key.length);
+ } else if (server_info->lm_session_key.length >= 8 && lm_resp.length == 24) {
+ session_key = data_blob(NULL, 16);
+ SMBsesskeygen_lmv1(server_info->lm_session_key.data, lm_resp.data,
+ session_key.data);
+ } else {
+ session_key = data_blob(NULL, 0);
+ }
+
+ data_blob_free(&lm_resp);
+ data_blob_clear_free(&plaintext_password);
+
+ /* it's ok - setup a reply */
+ set_message(outbuf,3,0,True);
+ if (Protocol >= PROTOCOL_NT1) {
+ char *p = smb_buf( outbuf );
+ p += add_signature( outbuf, p );
+ set_message_end( outbuf, p );
+ /* perhaps grab OS version here?? */
+ }
+
+ if (server_info->guest) {
+ SSVAL(outbuf,smb_vwv2,1);
+ }
+
+ /* register the name and uid as being validated, so further connections
+ to a uid can get through without a password, on the same VC */
+
+ /* register_vuid keeps the server info */
+ sess_vuid = register_vuid(server_info, session_key, nt_resp, sub_user);
+ data_blob_free(&nt_resp);
+
+ if (sess_vuid == -1) {
+ return ERROR_NT(NT_STATUS_LOGON_FAILURE);
+ }
+
+ /* current_user_info is changed on new vuid */
+ reload_services( True );
+
+ if (!server_info->guest && !srv_signing_started() && !srv_check_sign_mac(inbuf, True)) {
+ exit_server("reply_sesssetup_and_X: bad smb signature");
+ }
+
+ SSVAL(outbuf,smb_uid,sess_vuid);
+ SSVAL(inbuf,smb_uid,sess_vuid);
+
+ if (!done_sesssetup)
+ max_send = MIN(max_send,smb_bufsize);
+
+ done_sesssetup = True;
+
+ END_PROFILE(SMBsesssetupX);
+ return chain_reply(inbuf,outbuf,length,bufsize);
+}
diff --git a/source/smbd/srvstr.c b/source/smbd/srvstr.c
new file mode 100644
index 00000000000..409fd30a679
--- /dev/null
+++ b/source/smbd/srvstr.c
@@ -0,0 +1,44 @@
+/*
+ Unix SMB/CIFS implementation.
+ server specific string routines
+ Copyright (C) Andrew Tridgell 2001
+ Copyright (C) Andrew Bartlett <abartlet@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 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"
+extern int max_send;
+
+/* Make sure we can't write a string past the end of the buffer */
+
+size_t srvstr_push_fn(const char *function, unsigned int line,
+ const char *base_ptr, void *dest,
+ const char *src, int dest_len, int flags)
+{
+ size_t buf_used = PTR_DIFF(dest, base_ptr);
+ if (dest_len == -1) {
+ if (((ptrdiff_t)dest < (ptrdiff_t)base_ptr) || (buf_used > (size_t)max_send)) {
+#if 0
+ DEBUG(0, ("Pushing string of 'unlimited' length into non-SMB buffer!\n"));
+#endif
+ return push_string_fn(function, line, base_ptr, dest, src, -1, flags);
+ }
+ return push_string_fn(function, line, base_ptr, dest, src, max_send - buf_used, flags);
+ }
+
+ /* 'normal' push into size-specified buffer */
+ return push_string_fn(function, line, base_ptr, dest, src, dest_len, flags);
+}
diff --git a/source/smbd/statcache.c b/source/smbd/statcache.c
new file mode 100644
index 00000000000..d996f5e4938
--- /dev/null
+++ b/source/smbd/statcache.c
@@ -0,0 +1,339 @@
+/*
+ Unix SMB/CIFS implementation.
+ stat cache code
+ Copyright (C) Andrew Tridgell 1992-2000
+ Copyright (C) Jeremy Allison 1999-2000
+ Copyright (C) Andrew Bartlett <abartlet@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 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"
+
+extern BOOL case_sensitive;
+
+/****************************************************************************
+ Stat cache code used in unix_convert.
+*****************************************************************************/
+
+typedef struct {
+ char *original_path;
+ char *translated_path;
+ size_t translated_path_length;
+ char names[2]; /* This is extended via malloc... */
+} stat_cache_entry;
+
+#define INIT_STAT_CACHE_SIZE 512
+static hash_table stat_cache;
+
+/**
+ * Add an entry into the stat cache.
+ *
+ * @param full_orig_name The original name as specified by the client
+ * @param orig_translated_path The name on our filesystem.
+ *
+ * @note Only the first strlen(orig_translated_path) characters are stored
+ * into the cache. This means that full_orig_name will be internally
+ * truncated.
+ *
+ */
+
+void stat_cache_add( const char *full_orig_name, const char *orig_translated_path)
+{
+ stat_cache_entry *scp;
+ stat_cache_entry *found_scp;
+ char *translated_path;
+ size_t translated_path_length;
+
+ char *original_path;
+ size_t original_path_length;
+
+ hash_element *hash_elem;
+
+ if (!lp_stat_cache())
+ return;
+
+ /*
+ * Don't cache trivial valid directory entries such as . and ..
+ */
+
+ if((*full_orig_name == '\0') || (full_orig_name[0] == '.' &&
+ ((full_orig_name[1] == '\0') ||
+ (full_orig_name[1] == '.' && full_orig_name[1] == '\0'))))
+ return;
+
+ /*
+ * If we are in case insentive mode, we don't need to
+ * store names that need no translation - else, it
+ * would be a waste.
+ */
+
+ if(case_sensitive && (strcmp(full_orig_name, orig_translated_path) == 0))
+ return;
+
+ /*
+ * Remove any trailing '/' characters from the
+ * translated path.
+ */
+
+ translated_path = strdup(orig_translated_path);
+ if (!translated_path)
+ return;
+
+ translated_path_length = strlen(translated_path);
+
+ if(translated_path[translated_path_length-1] == '/') {
+ translated_path[translated_path_length-1] = '\0';
+ translated_path_length--;
+ }
+
+ if(case_sensitive) {
+ original_path = strdup(full_orig_name);
+ } else {
+ original_path = strdup_upper(full_orig_name);
+ }
+
+ if (!original_path) {
+ SAFE_FREE(translated_path);
+ return;
+ }
+
+ original_path_length = strlen(original_path);
+
+ if(original_path[original_path_length-1] == '/') {
+ original_path[original_path_length-1] = '\0';
+ original_path_length--;
+ }
+
+ if (original_path_length != translated_path_length) {
+ if (original_path_length < translated_path_length) {
+ DEBUG(0, ("OOPS - tried to store stat cache entry for weird length paths [%s] %lu and [%s] %lu)!\n",
+ original_path, (unsigned long)original_path_length, translated_path, (unsigned long)translated_path_length));
+ SAFE_FREE(original_path);
+ SAFE_FREE(translated_path);
+ return;
+ }
+
+ /* we only want to store the first part of original_path,
+ up to the length of translated_path */
+
+ original_path[translated_path_length] = '\0';
+ original_path_length = translated_path_length;
+ }
+
+ /*
+ * Check this name doesn't exist in the cache before we
+ * add it.
+ */
+
+ if ((hash_elem = hash_lookup(&stat_cache, original_path))) {
+ found_scp = (stat_cache_entry *)(hash_elem->value);
+ if (strcmp((found_scp->translated_path), orig_translated_path) == 0) {
+ /* already in hash table */
+ SAFE_FREE(original_path);
+ SAFE_FREE(translated_path);
+ return;
+ }
+ /* hash collision - remove before we re-add */
+ hash_remove(&stat_cache, hash_elem);
+ }
+
+ /*
+ * New entry.
+ */
+
+ if((scp = (stat_cache_entry *)malloc(sizeof(stat_cache_entry)
+ +original_path_length
+ +translated_path_length)) == NULL) {
+ DEBUG(0,("stat_cache_add: Out of memory !\n"));
+ SAFE_FREE(original_path);
+ SAFE_FREE(translated_path);
+ return;
+ }
+
+ scp->original_path = scp->names;
+ /* pointer into the structure... */
+ scp->translated_path = scp->names + original_path_length + 1;
+ safe_strcpy(scp->original_path, original_path, original_path_length);
+ safe_strcpy(scp->translated_path, translated_path, translated_path_length);
+ scp->translated_path_length = translated_path_length;
+
+ hash_insert(&stat_cache, (char *)scp, original_path);
+
+ SAFE_FREE(original_path);
+ SAFE_FREE(translated_path);
+
+ DEBUG(5,("stat_cache_add: Added entry %s -> %s\n", scp->original_path, scp->translated_path));
+}
+
+/**
+ * Look through the stat cache for an entry
+ *
+ * The hash-table's internals will promote it to the top if found.
+ *
+ * @param conn A connection struct to do the stat() with.
+ * @param name The path we are attempting to cache, modified by this routine
+ * to be correct as far as the cache can tell us
+ * @param dirpath The path as far as the stat cache told us.
+ * @param start A pointer into name, for where to 'start' in fixing the rest of the name up.
+ * @param psd A stat buffer, NOT from the cache, but just a side-effect.
+ *
+ * @return True if we translated (and did a scuccessful stat on) the entire name.
+ *
+ */
+
+BOOL stat_cache_lookup(connection_struct *conn, pstring name, pstring dirpath,
+ char **start, SMB_STRUCT_STAT *pst)
+{
+ stat_cache_entry *scp;
+ char *chk_name;
+ size_t namelen;
+ hash_element *hash_elem;
+ char *sp;
+ BOOL sizechanged = False;
+ unsigned int num_components = 0;
+
+ if (!lp_stat_cache())
+ return False;
+
+ namelen = strlen(name);
+
+ *start = name;
+
+ DO_PROFILE_INC(statcache_lookups);
+
+ /*
+ * Don't lookup trivial valid directory entries.
+ */
+ if((*name == '\0') || (name[0] == '.' &&
+ ((name[1] == '\0') ||
+ (name[1] == '.' && name[1] == '\0'))))
+ return False;
+
+ if (case_sensitive) {
+ chk_name = strdup(name);
+ if (!chk_name) {
+ DEBUG(0, ("stat_cache_lookup: strdup failed!\n"));
+ return False;
+ }
+
+ } else {
+ chk_name = strdup_upper(name);
+ if (!chk_name) {
+ DEBUG(0, ("stat_cache_lookup: strdup_upper failed!\n"));
+ return False;
+ }
+
+ /*
+ * In some language encodings the length changes
+ * if we uppercase. We need to treat this differently
+ * below.
+ */
+ if (strlen(chk_name) != namelen)
+ sizechanged = True;
+ }
+
+ while (1) {
+ hash_elem = hash_lookup(&stat_cache, chk_name);
+ if(hash_elem == NULL) {
+ DEBUG(10,("stat_cache_lookup: lookup failed for name [%s]\n", chk_name ));
+ /*
+ * Didn't find it - remove last component for next try.
+ */
+ sp = strrchr_m(chk_name, '/');
+ if (sp) {
+ *sp = '\0';
+ /*
+ * Count the number of times we have done this,
+ * we'll need it when reconstructing the string.
+ */
+ if (sizechanged)
+ num_components++;
+
+ } else {
+ /*
+ * We reached the end of the name - no match.
+ */
+ DO_PROFILE_INC(statcache_misses);
+ SAFE_FREE(chk_name);
+ return False;
+ }
+ if((*chk_name == '\0') || (strcmp(chk_name, ".") == 0)
+ || (strcmp(chk_name, "..") == 0)) {
+ DO_PROFILE_INC(statcache_misses);
+ SAFE_FREE(chk_name);
+ return False;
+ }
+ } else {
+ scp = (stat_cache_entry *)(hash_elem->value);
+ DEBUG(10,("stat_cache_lookup: lookup succeeded for name [%s] -> [%s]\n", chk_name, scp->translated_path ));
+ DO_PROFILE_INC(statcache_hits);
+ if(SMB_VFS_STAT(conn,scp->translated_path, pst) != 0) {
+ /* Discard this entry - it doesn't exist in the filesystem. */
+ hash_remove(&stat_cache, hash_elem);
+ SAFE_FREE(chk_name);
+ return False;
+ }
+
+ if (!sizechanged) {
+ memcpy(name, scp->translated_path, MIN(sizeof(pstring)-1, scp->translated_path_length));
+ } else if (num_components == 0) {
+ pstrcpy(name, scp->translated_path);
+ } else {
+ sp = strnrchr_m(name, '/', num_components);
+ if (sp) {
+ pstring last_component;
+ pstrcpy(last_component, sp);
+ pstrcpy(name, scp->translated_path);
+ pstrcat(name, last_component);
+ } else {
+ pstrcpy(name, scp->translated_path);
+ }
+ }
+
+ /* set pointer for 'where to start' on fixing the rest of the name */
+ *start = &name[scp->translated_path_length];
+ if(**start == '/')
+ ++*start;
+
+ pstrcpy(dirpath, scp->translated_path);
+ SAFE_FREE(chk_name);
+ return (namelen == scp->translated_path_length);
+ }
+ }
+}
+
+/*************************************************************************** **
+ * Initializes or clears the stat cache.
+ *
+ * Input: none.
+ * Output: none.
+ *
+ * ************************************************************************** **
+ */
+BOOL reset_stat_cache( void )
+{
+ static BOOL initialised;
+ if (!lp_stat_cache())
+ return True;
+
+ if (initialised) {
+ hash_clear(&stat_cache);
+ }
+
+ initialised = hash_table_init( &stat_cache, INIT_STAT_CACHE_SIZE,
+ (compare_function)(strcmp));
+ return initialised;
+}
diff --git a/source/smbd/tdbutil.c b/source/smbd/tdbutil.c
new file mode 100644
index 00000000000..cafcde20374
--- /dev/null
+++ b/source/smbd/tdbutil.c
@@ -0,0 +1,85 @@
+/*
+ Unix SMB/CIFS implementation.
+ Main SMB server routines
+ Copyright (C) Jeremy Allison 2003
+ Copyright (C) Gerald (Jerry) Carter 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 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"
+
+
+/**********************************************************************
+ logging function used by smbd to detect and remove corrupted tdb's
+**********************************************************************/
+
+void smbd_tdb_log(TDB_CONTEXT *tdb, int level, const char *format, ...)
+{
+ va_list ap;
+ char *ptr = NULL;
+ BOOL decrement_smbd_count;
+
+ va_start(ap, format);
+ vasprintf(&ptr, format, ap);
+ va_end(ap);
+
+ if (!ptr || !*ptr)
+ return;
+
+ DEBUG(level, ("tdb(%s): %s", tdb->name ? tdb->name : "unnamed", ptr));
+
+ if (tdb->ecode == TDB_ERR_CORRUPT) {
+ int ret;
+
+ DEBUG(0,("tdb_log: TDB %s is corrupt. Removing file and stopping this process.\n",
+ tdb->name ));
+
+ become_root();
+ ret = unlink(tdb->name);
+ if ( ret ) {
+ DEBUG(0,("ERROR: %s\n", strerror(errno)));
+ }
+ unbecome_root();
+
+
+ /* if its not connections.tdb, then make sure we decrement the
+ smbd count. If connections.tdb is bad, there's nothing we
+ can do and everything will eventually shut down or clean
+ up anyways */
+
+ if ( strcmp(tdb->name, lock_path("connections.tdb")) == 0 )
+ decrement_smbd_count = False;
+ else
+ decrement_smbd_count = True;
+
+ /* now die */
+
+ smb_panic2("corrupt tdb\n", decrement_smbd_count );
+ }
+
+ if (tdb->ecode == TDB_ERR_IO)
+ {
+ if ( strcmp(tdb->name, lock_path("connections.tdb")) == 0 )
+ decrement_smbd_count = False;
+ else
+ decrement_smbd_count = True;
+
+ smb_panic2( "i/o error on tdb.\n", decrement_smbd_count );
+ }
+
+ SAFE_FREE(ptr);
+}
+
diff --git a/source/smbd/trans2.c b/source/smbd/trans2.c
new file mode 100644
index 00000000000..a88722edde5
--- /dev/null
+++ b/source/smbd/trans2.c
@@ -0,0 +1,4165 @@
+/*
+ Unix SMB/CIFS implementation.
+ SMB transaction2 handling
+ Copyright (C) Jeremy Allison 1994-2003
+ Copyright (C) Stefan (metze) Metzmacher 2003
+
+ Extensively modified by Andrew Tridgell, 1995
+
+ This program is free software; you can 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"
+
+extern int Protocol;
+extern BOOL case_sensitive;
+extern int smb_read_error;
+extern fstring local_machine;
+extern int global_oplock_break;
+extern uint32 global_client_caps;
+extern struct current_user current_user;
+
+#define get_file_size(sbuf) ((sbuf).st_size)
+
+/* given a stat buffer return the allocated size on disk, taking into
+ account sparse files */
+SMB_BIG_UINT get_allocation_size(files_struct *fsp, SMB_STRUCT_STAT *sbuf)
+{
+ SMB_BIG_UINT ret;
+#if defined(HAVE_STAT_ST_BLOCKS) && defined(STAT_ST_BLOCKSIZE)
+ ret = (SMB_BIG_UINT)STAT_ST_BLOCKSIZE * (SMB_BIG_UINT)sbuf->st_blocks;
+#else
+ ret = (SMB_BIG_UINT)get_file_size(*sbuf);
+#endif
+ if (!ret && fsp && fsp->initial_allocation_size)
+ ret = fsp->initial_allocation_size;
+ ret = SMB_ROUNDUP(ret,SMB_ROUNDUP_ALLOCATION_SIZE);
+ return ret;
+}
+
+/****************************************************************************
+ Utility functions for dealing with extended attributes.
+****************************************************************************/
+
+static const char *prohibited_ea_names[] = {
+ SAMBA_POSIX_INHERITANCE_EA_NAME,
+ SAMBA_XATTR_DOS_ATTRIB,
+ NULL
+};
+
+/****************************************************************************
+ Refuse to allow clients to overwrite our private xattrs.
+****************************************************************************/
+
+static BOOL samba_private_attr_name(const char *unix_ea_name)
+{
+ int i;
+
+ for (i = 0; prohibited_ea_names[i]; i++) {
+ if (strequal( prohibited_ea_names[i], unix_ea_name))
+ return True;
+ }
+ return False;
+}
+
+struct ea_list {
+ struct ea_list *next, *prev;
+ struct ea_struct ea;
+};
+
+/****************************************************************************
+ Get one EA value. Fill in a struct ea_struct.
+****************************************************************************/
+
+static BOOL get_ea_value(TALLOC_CTX *mem_ctx, connection_struct *conn, files_struct *fsp,
+ const char *fname, char *ea_name, struct ea_struct *pea)
+{
+ /* Get the value of this xattr. Max size is 64k. */
+ size_t attr_size = 256;
+ char *val = NULL;
+ ssize_t sizeret;
+
+ again:
+
+ val = talloc_realloc(mem_ctx, val, attr_size);
+ if (!val) {
+ return False;
+ }
+
+ if (fsp && fsp->fd != -1) {
+ sizeret = SMB_VFS_FGETXATTR(fsp, fsp->fd, ea_name, val, attr_size);
+ } else {
+ sizeret = SMB_VFS_GETXATTR(conn, fname, ea_name, val, attr_size);
+ }
+
+ if (sizeret == -1 && errno == ERANGE && attr_size != 65536) {
+ attr_size = 65536;
+ goto again;
+ }
+
+ if (sizeret == -1) {
+ return False;
+ }
+
+ DEBUG(10,("get_ea_value: EA %s is of length %d: ", ea_name, sizeret));
+ dump_data(10, val, sizeret);
+
+ pea->flags = 0;
+ if (strnequal(ea_name, "user.", 5)) {
+ pea->name = &ea_name[5];
+ } else {
+ pea->name = ea_name;
+ }
+ pea->value.data = val;
+ pea->value.length = (size_t)sizeret;
+ return True;
+}
+
+/****************************************************************************
+ Return a linked list of the total EA's. Plus a guess as to the total size
+ (NB. The is not the total size on the wire - we need to convert to DOS
+ codepage for that).
+****************************************************************************/
+
+static struct ea_list *get_ea_list(TALLOC_CTX *mem_ctx, connection_struct *conn, files_struct *fsp, const char *fname, size_t *pea_total_len)
+{
+ /* Get a list of all xattrs. Max namesize is 64k. */
+ size_t ea_namelist_size = 1024;
+ char *ea_namelist;
+ char *p;
+ ssize_t sizeret;
+ int i;
+ struct ea_list *ea_list_head = NULL;
+
+ if (pea_total_len) {
+ *pea_total_len = 0;
+ }
+
+ if (!lp_ea_support(SNUM(conn))) {
+ return NULL;
+ }
+
+ for (i = 0, ea_namelist = talloc(mem_ctx, ea_namelist_size); i < 6;
+ ea_namelist = talloc_realloc(mem_ctx, ea_namelist, ea_namelist_size), i++) {
+ if (fsp && fsp->fd != -1) {
+ sizeret = SMB_VFS_FLISTXATTR(fsp, fsp->fd, ea_namelist, ea_namelist_size);
+ } else {
+ sizeret = SMB_VFS_LISTXATTR(conn, fname, ea_namelist, ea_namelist_size);
+ }
+
+ if (sizeret == -1 && errno == ERANGE) {
+ ea_namelist_size *= 2;
+ } else {
+ break;
+ }
+ }
+
+ if (sizeret == -1)
+ return NULL;
+
+ DEBUG(10,("get_ea_list: ea_namelist size = %d\n", sizeret ));
+
+ if (sizeret) {
+ for (p = ea_namelist; p - ea_namelist < sizeret; p += strlen(p) + 1) {
+ struct ea_list *listp, *tmp;
+
+ if (strnequal(p, "system.", 7) || samba_private_attr_name(p))
+ continue;
+
+ listp = talloc(mem_ctx, sizeof(struct ea_list));
+ if (!listp)
+ return NULL;
+
+ if (!get_ea_value(mem_ctx, conn, fsp, fname, p, &listp->ea)) {
+ return NULL;
+ }
+
+ if (pea_total_len) {
+ *pea_total_len += 4 + strlen(p) + 1 + listp->ea.value.length;
+ }
+ DLIST_ADD_END(ea_list_head, listp, tmp);
+ }
+ }
+
+ /* Add on 4 for total length. */
+ if (pea_total_len) {
+ *pea_total_len += 4;
+ }
+ return ea_list_head;
+}
+
+/****************************************************************************
+ Fill a qfilepathinfo buffer with EA's.
+****************************************************************************/
+
+static unsigned int fill_ea_buffer(char *pdata, unsigned int total_data_size,
+ connection_struct *conn, files_struct *fsp, const char *fname)
+{
+ unsigned int ret_data_size = 4;
+ char *p = pdata;
+ size_t total_ea_len;
+ TALLOC_CTX *mem_ctx = talloc_init("fill_ea_buffer");
+ struct ea_list *ea_list = get_ea_list(mem_ctx, conn, fsp, fname, &total_ea_len);
+
+ SMB_ASSERT(total_data_size >= 4);
+
+ SIVAL(pdata,0,0);
+ if (!mem_ctx) {
+ return 4;
+ }
+
+ if (!ea_list) {
+ talloc_destroy(mem_ctx);
+ return 4;
+ }
+
+ if (total_ea_len > total_data_size) {
+ talloc_destroy(mem_ctx);
+ return 4;
+ }
+
+ total_data_size -= 4;
+ for (p = pdata + 4; ea_list; ea_list = ea_list->next) {
+ size_t dos_namelen;
+ fstring dos_ea_name;
+ push_ascii_fstring(dos_ea_name, ea_list->ea.name);
+ dos_namelen = strlen(dos_ea_name);
+ if (dos_namelen > 255 || dos_namelen == 0) {
+ break;
+ }
+ if (ea_list->ea.value.length > 65535) {
+ break;
+ }
+ if (4 + dos_namelen + 1 + ea_list->ea.value.length > total_data_size) {
+ break;
+ }
+
+ /* We know we have room. */
+ SCVAL(p,0,ea_list->ea.flags);
+ SCVAL(p,1,dos_namelen);
+ SSVAL(p,2,ea_list->ea.value.length);
+ fstrcpy(p+4, dos_ea_name);
+ memcpy( p + 4 + dos_namelen + 1, ea_list->ea.value.data, ea_list->ea.value.length);
+
+ total_data_size -= 4 + dos_namelen + 1 + ea_list->ea.value.length;
+ p += 4 + dos_namelen + 1 + ea_list->ea.value.length;
+ }
+
+ ret_data_size = PTR_DIFF(p, pdata);
+ talloc_destroy(mem_ctx);
+ SIVAL(pdata,0,ret_data_size);
+ return ret_data_size;
+}
+
+static unsigned int estimate_ea_size(connection_struct *conn, files_struct *fsp, const char *fname)
+{
+ size_t total_ea_len = 0;
+ TALLOC_CTX *mem_ctx = talloc_init("estimate_ea_size");
+
+ (void)get_ea_list(mem_ctx, conn, fsp, fname, &total_ea_len);
+ talloc_destroy(mem_ctx);
+ return total_ea_len;
+}
+
+/****************************************************************************
+ Set or delete an extended attribute.
+****************************************************************************/
+
+static NTSTATUS set_ea(connection_struct *conn, files_struct *fsp, const char *fname,
+ char *pdata, int total_data)
+{
+ unsigned int namelen;
+ unsigned int ealen;
+ int ret;
+ fstring unix_ea_name;
+
+ if (!lp_ea_support(SNUM(conn))) {
+ return NT_STATUS_EAS_NOT_SUPPORTED;
+ }
+
+ if (total_data < 8) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ if (IVAL(pdata,0) > total_data) {
+ DEBUG(10,("set_ea: bad total data size (%u) > %u\n", IVAL(pdata,0), (unsigned int)total_data));
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ pdata += 4;
+ namelen = CVAL(pdata,1);
+ ealen = SVAL(pdata,2);
+ pdata += 4;
+ if (total_data < 8 + namelen + 1 + ealen) {
+ DEBUG(10,("set_ea: bad total data size (%u) < 8 + namelen (%u) + 1 + ealen (%u)\n",
+ (unsigned int)total_data, namelen, ealen));
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ if (pdata[namelen] != '\0') {
+ DEBUG(10,("set_ea: ea name not null terminated\n"));
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ fstrcpy(unix_ea_name, "user."); /* All EA's must start with user. */
+ pull_ascii(&unix_ea_name[5], pdata, sizeof(fstring) - 5, -1, STR_TERMINATE);
+ pdata += (namelen + 1);
+
+ DEBUG(10,("set_ea: ea_name %s ealen = %u\n", unix_ea_name, ealen));
+ if (ealen) {
+ DEBUG(10,("set_ea: data :\n"));
+ dump_data(10, pdata, ealen);
+ }
+
+ if (samba_private_attr_name(unix_ea_name)) {
+ DEBUG(10,("set_ea: ea name %s is a private Samba name.\n", unix_ea_name));
+ return NT_STATUS_ACCESS_DENIED;
+ }
+
+ if (ealen == 0) {
+ /* Remove the attribute. */
+ if (fsp && (fsp->fd != -1)) {
+ DEBUG(10,("set_ea: deleting ea name %s on file %s by file descriptor.\n",
+ unix_ea_name, fsp->fsp_name));
+ ret = SMB_VFS_FREMOVEXATTR(fsp, fsp->fd, unix_ea_name);
+ } else {
+ DEBUG(10,("set_ea: deleting ea name %s on file %s.\n",
+ unix_ea_name, fname));
+ ret = SMB_VFS_REMOVEXATTR(conn, fname, unix_ea_name);
+ }
+#ifdef ENOATTR
+ /* Removing a non existent attribute always succeeds. */
+ DEBUG(10,("set_ea: deleting ea name %s didn't exist - succeeding by default.\n", unix_ea_name));
+ if (ret == -1 && errno == ENOATTR) {
+ ret = 0;
+ }
+#endif
+ } else {
+ if (fsp && (fsp->fd != -1)) {
+ DEBUG(10,("set_ea: setting ea name %s on file %s by file descriptor.\n",
+ unix_ea_name, fsp->fsp_name));
+ ret = SMB_VFS_FSETXATTR(fsp, fsp->fd, unix_ea_name, pdata, ealen, 0);
+ } else {
+ DEBUG(10,("set_ea: setting ea name %s on file %s.\n",
+ unix_ea_name, fname));
+ ret = SMB_VFS_SETXATTR(conn, fname, unix_ea_name, pdata, ealen, 0);
+ }
+ }
+
+ if (ret == -1) {
+ if (errno == ENOTSUP) {
+ return NT_STATUS_EAS_NOT_SUPPORTED;
+ }
+ return map_nt_error_from_unix(errno);
+ }
+
+ return NT_STATUS_OK;
+}
+
+/****************************************************************************
+ Send the required number of replies back.
+ We assume all fields other than the data fields are
+ set correctly for the type of call.
+ HACK ! Always assumes smb_setup field is zero.
+****************************************************************************/
+
+static int send_trans2_replies(char *outbuf,
+ int bufsize,
+ char *params,
+ int paramsize,
+ char *pdata,
+ int datasize)
+{
+ /* As we are using a protocol > LANMAN1 then the max_send
+ variable must have been set in the sessetupX call.
+ This takes precedence over the max_xmit field in the
+ global struct. These different max_xmit variables should
+ be merged as this is now too confusing */
+
+ extern int max_send;
+ int data_to_send = datasize;
+ int params_to_send = paramsize;
+ int useable_space;
+ char *pp = params;
+ char *pd = pdata;
+ int params_sent_thistime, data_sent_thistime, total_sent_thistime;
+ int alignment_offset = 1; /* JRA. This used to be 3. Set to 1 to make netmon parse ok. */
+ int data_alignment_offset = 0;
+
+ /* Initially set the wcnt area to be 10 - this is true for all trans2 replies */
+
+ set_message(outbuf,10,0,True);
+
+ /* If there genuinely are no parameters or data to send just send the empty packet */
+
+ if(params_to_send == 0 && data_to_send == 0) {
+ if (!send_smb(smbd_server_fd(),outbuf))
+ exit_server("send_trans2_replies: send_smb failed.");
+ return 0;
+ }
+
+ /* When sending params and data ensure that both are nicely aligned */
+ /* Only do this alignment when there is also data to send - else
+ can cause NT redirector problems. */
+
+ if (((params_to_send % 4) != 0) && (data_to_send != 0))
+ data_alignment_offset = 4 - (params_to_send % 4);
+
+ /* Space is bufsize minus Netbios over TCP header minus SMB header */
+ /* The alignment_offset is to align the param bytes on an even byte
+ boundary. NT 4.0 Beta needs this to work correctly. */
+
+ useable_space = bufsize - ((smb_buf(outbuf)+ alignment_offset+data_alignment_offset) - outbuf);
+
+ /* useable_space can never be more than max_send minus the alignment offset. */
+
+ useable_space = MIN(useable_space, max_send - (alignment_offset+data_alignment_offset));
+
+ while (params_to_send || data_to_send) {
+ /* Calculate whether we will totally or partially fill this packet */
+
+ total_sent_thistime = params_to_send + data_to_send + alignment_offset + data_alignment_offset;
+
+ /* We can never send more than useable_space */
+ /*
+ * Note that 'useable_space' does not include the alignment offsets,
+ * but we must include the alignment offsets in the calculation of
+ * the length of the data we send over the wire, as the alignment offsets
+ * are sent here. Fix from Marc_Jacobsen@hp.com.
+ */
+
+ total_sent_thistime = MIN(total_sent_thistime, useable_space+ alignment_offset + data_alignment_offset);
+
+ set_message(outbuf, 10, total_sent_thistime, True);
+
+ /* Set total params and data to be sent */
+ SSVAL(outbuf,smb_tprcnt,paramsize);
+ SSVAL(outbuf,smb_tdrcnt,datasize);
+
+ /* Calculate how many parameters and data we can fit into
+ * this packet. Parameters get precedence
+ */
+
+ params_sent_thistime = MIN(params_to_send,useable_space);
+ data_sent_thistime = useable_space - params_sent_thistime;
+ data_sent_thistime = MIN(data_sent_thistime,data_to_send);
+
+ SSVAL(outbuf,smb_prcnt, params_sent_thistime);
+
+ /* smb_proff is the offset from the start of the SMB header to the
+ parameter bytes, however the first 4 bytes of outbuf are
+ the Netbios over TCP header. Thus use smb_base() to subtract
+ them from the calculation */
+
+ SSVAL(outbuf,smb_proff,((smb_buf(outbuf)+alignment_offset) - smb_base(outbuf)));
+
+ if(params_sent_thistime == 0)
+ SSVAL(outbuf,smb_prdisp,0);
+ else
+ /* Absolute displacement of param bytes sent in this packet */
+ SSVAL(outbuf,smb_prdisp,pp - params);
+
+ SSVAL(outbuf,smb_drcnt, data_sent_thistime);
+ if(data_sent_thistime == 0) {
+ SSVAL(outbuf,smb_droff,0);
+ SSVAL(outbuf,smb_drdisp, 0);
+ } else {
+ /* The offset of the data bytes is the offset of the
+ parameter bytes plus the number of parameters being sent this time */
+ SSVAL(outbuf,smb_droff,((smb_buf(outbuf)+alignment_offset) -
+ smb_base(outbuf)) + params_sent_thistime + data_alignment_offset);
+ SSVAL(outbuf,smb_drdisp, pd - pdata);
+ }
+
+ /* Copy the param bytes into the packet */
+
+ if(params_sent_thistime)
+ memcpy((smb_buf(outbuf)+alignment_offset),pp,params_sent_thistime);
+
+ /* Copy in the data bytes */
+ if(data_sent_thistime)
+ memcpy(smb_buf(outbuf)+alignment_offset+params_sent_thistime+
+ data_alignment_offset,pd,data_sent_thistime);
+
+ DEBUG(9,("t2_rep: params_sent_thistime = %d, data_sent_thistime = %d, useable_space = %d\n",
+ params_sent_thistime, data_sent_thistime, useable_space));
+ DEBUG(9,("t2_rep: params_to_send = %d, data_to_send = %d, paramsize = %d, datasize = %d\n",
+ params_to_send, data_to_send, paramsize, datasize));
+
+ /* Send the packet */
+ if (!send_smb(smbd_server_fd(),outbuf))
+ exit_server("send_trans2_replies: send_smb failed.");
+
+ pp += params_sent_thistime;
+ pd += data_sent_thistime;
+
+ params_to_send -= params_sent_thistime;
+ data_to_send -= data_sent_thistime;
+
+ /* Sanity check */
+ if(params_to_send < 0 || data_to_send < 0) {
+ DEBUG(0,("send_trans2_replies failed sanity check pts = %d, dts = %d\n!!!",
+ params_to_send, data_to_send));
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+/****************************************************************************
+ Reply to a TRANSACT2_OPEN.
+****************************************************************************/
+
+static int call_trans2open(connection_struct *conn, char *inbuf, char *outbuf, int bufsize,
+ char **pparams, int total_params, char **ppdata, int total_data)
+{
+ char *params = *pparams;
+ int16 open_mode;
+ int16 open_attr;
+ BOOL oplock_request;
+#if 0
+ BOOL return_additional_info;
+ int16 open_sattr;
+ time_t open_time;
+#endif
+ int16 open_ofun;
+ int32 open_size;
+ char *pname;
+ pstring fname;
+ SMB_OFF_T size=0;
+ int fmode=0,mtime=0,rmode;
+ SMB_INO_T inode = 0;
+ SMB_STRUCT_STAT sbuf;
+ int smb_action = 0;
+ BOOL bad_path = False;
+ files_struct *fsp;
+ NTSTATUS status;
+
+ /*
+ * Ensure we have enough parameters to perform the operation.
+ */
+
+ if (total_params < 29)
+ return(ERROR_DOS(ERRDOS,ERRinvalidparam));
+
+ open_mode = SVAL(params, 2);
+ open_attr = SVAL(params,6);
+ oplock_request = (((SVAL(params,0)|(1<<1))>>1) | ((SVAL(params,0)|(1<<2))>>1));
+#if 0
+ return_additional_info = BITSETW(params,0);
+ open_sattr = SVAL(params, 4);
+ open_time = make_unix_date3(params+8);
+#endif
+ open_ofun = SVAL(params,12);
+ open_size = IVAL(params,14);
+ pname = &params[28];
+
+ if (IS_IPC(conn))
+ return(ERROR_DOS(ERRSRV,ERRaccess));
+
+ srvstr_get_path(inbuf, fname, pname, sizeof(fname), -1, STR_TERMINATE, &status);
+ if (!NT_STATUS_IS_OK(status)) {
+ return ERROR_NT(status);
+ }
+
+ DEBUG(3,("trans2open %s mode=%d attr=%d ofun=%d size=%d\n",
+ fname,open_mode, open_attr, open_ofun, open_size));
+
+ /* XXXX we need to handle passed times, sattr and flags */
+
+ unix_convert(fname,conn,0,&bad_path,&sbuf);
+
+ if (!check_name(fname,conn)) {
+ return set_bad_path_error(errno, bad_path, outbuf, ERRDOS,ERRnoaccess);
+ }
+
+ fsp = open_file_shared(conn,fname,&sbuf,open_mode,open_ofun,(uint32)open_attr,
+ oplock_request, &rmode,&smb_action);
+
+ if (!fsp) {
+ return set_bad_path_error(errno, bad_path, outbuf, ERRDOS,ERRnoaccess);
+ }
+
+ size = get_file_size(sbuf);
+ fmode = dos_mode(conn,fname,&sbuf);
+ mtime = sbuf.st_mtime;
+ inode = sbuf.st_ino;
+ if (fmode & aDIR) {
+ close_file(fsp,False);
+ return(ERROR_DOS(ERRDOS,ERRnoaccess));
+ }
+
+ /* Realloc the size of parameters and data we will return */
+ params = Realloc(*pparams, 28);
+ if( params == NULL )
+ return(ERROR_DOS(ERRDOS,ERRnomem));
+ *pparams = params;
+
+ memset((char *)params,'\0',28);
+ SSVAL(params,0,fsp->fnum);
+ SSVAL(params,2,fmode);
+ put_dos_date2(params,4, mtime);
+ SIVAL(params,8, (uint32)size);
+ SSVAL(params,12,rmode);
+
+ if (oplock_request && lp_fake_oplocks(SNUM(conn)))
+ smb_action |= EXTENDED_OPLOCK_GRANTED;
+
+ SSVAL(params,18,smb_action);
+
+ /*
+ * WARNING - this may need to be changed if SMB_INO_T <> 4 bytes.
+ */
+ SIVAL(params,20,inode);
+
+ /* Send the required number of replies */
+ send_trans2_replies(outbuf, bufsize, params, 28, *ppdata, 0);
+
+ return -1;
+}
+
+/*********************************************************
+ Routine to check if a given string matches exactly.
+ as a special case a mask of "." does NOT match. That
+ is required for correct wildcard semantics
+ Case can be significant or not.
+**********************************************************/
+
+static BOOL exact_match(char *str,char *mask, BOOL case_sig)
+{
+ if (mask[0] == '.' && mask[1] == 0)
+ return False;
+ if (case_sig)
+ return strcmp(str,mask)==0;
+ if (StrCaseCmp(str,mask) != 0) {
+ return False;
+ }
+ if (ms_has_wild(str)) {
+ return False;
+ }
+ return True;
+}
+
+/****************************************************************************
+ Return the filetype for UNIX extensions.
+****************************************************************************/
+
+static uint32 unix_filetype(mode_t mode)
+{
+ if(S_ISREG(mode))
+ return UNIX_TYPE_FILE;
+ else if(S_ISDIR(mode))
+ return UNIX_TYPE_DIR;
+#ifdef S_ISLNK
+ else if(S_ISLNK(mode))
+ return UNIX_TYPE_SYMLINK;
+#endif
+#ifdef S_ISCHR
+ else if(S_ISCHR(mode))
+ return UNIX_TYPE_CHARDEV;
+#endif
+#ifdef S_ISBLK
+ else if(S_ISBLK(mode))
+ return UNIX_TYPE_BLKDEV;
+#endif
+#ifdef S_ISFIFO
+ else if(S_ISFIFO(mode))
+ return UNIX_TYPE_FIFO;
+#endif
+#ifdef S_ISSOCK
+ else if(S_ISSOCK(mode))
+ return UNIX_TYPE_SOCKET;
+#endif
+
+ DEBUG(0,("unix_filetype: unknown filetype %u", (unsigned)mode));
+ return UNIX_TYPE_UNKNOWN;
+}
+
+/****************************************************************************
+ Return the major devicenumber for UNIX extensions.
+****************************************************************************/
+
+static uint32 unix_dev_major(SMB_DEV_T dev)
+{
+#if defined(HAVE_DEVICE_MAJOR_FN)
+ return (uint32)major(dev);
+#else
+ return (uint32)(dev >> 8);
+#endif
+}
+
+/****************************************************************************
+ Return the minor devicenumber for UNIX extensions.
+****************************************************************************/
+
+static uint32 unix_dev_minor(SMB_DEV_T dev)
+{
+#if defined(HAVE_DEVICE_MINOR_FN)
+ return (uint32)minor(dev);
+#else
+ return (uint32)(dev & 0xff);
+#endif
+}
+
+/****************************************************************************
+ Map wire perms onto standard UNIX permissions. Obey share restrictions.
+****************************************************************************/
+
+static mode_t unix_perms_from_wire( connection_struct *conn, SMB_STRUCT_STAT *pst, uint32 perms)
+{
+ mode_t ret = 0;
+
+ if (perms == SMB_MODE_NO_CHANGE)
+ return pst->st_mode;
+
+ ret |= ((perms & UNIX_X_OTH ) ? S_IXOTH : 0);
+ ret |= ((perms & UNIX_W_OTH ) ? S_IWOTH : 0);
+ ret |= ((perms & UNIX_R_OTH ) ? S_IROTH : 0);
+ ret |= ((perms & UNIX_X_GRP ) ? S_IXGRP : 0);
+ ret |= ((perms & UNIX_W_GRP ) ? S_IWGRP : 0);
+ ret |= ((perms & UNIX_R_GRP ) ? S_IRGRP : 0);
+ ret |= ((perms & UNIX_X_USR ) ? S_IXUSR : 0);
+ ret |= ((perms & UNIX_W_USR ) ? S_IWUSR : 0);
+ ret |= ((perms & UNIX_R_USR ) ? S_IRUSR : 0);
+#ifdef S_ISVTX
+ ret |= ((perms & UNIX_STICKY ) ? S_ISVTX : 0);
+#endif
+#ifdef S_ISGID
+ ret |= ((perms & UNIX_SET_GID ) ? S_ISGID : 0);
+#endif
+#ifdef S_ISUID
+ ret |= ((perms & UNIX_SET_UID ) ? S_ISUID : 0);
+#endif
+
+ if (VALID_STAT(*pst) && S_ISDIR(pst->st_mode)) {
+ ret &= lp_dir_mask(SNUM(conn));
+ /* Add in force bits */
+ ret |= lp_force_dir_mode(SNUM(conn));
+ } else {
+ /* Apply mode mask */
+ ret &= lp_create_mask(SNUM(conn));
+ /* Add in force bits */
+ ret |= lp_force_create_mode(SNUM(conn));
+ }
+
+ return ret;
+}
+
+/****************************************************************************
+ Checks for SMB_TIME_NO_CHANGE and if not found calls interpret_long_date.
+****************************************************************************/
+
+time_t interpret_long_unix_date(char *p)
+{
+ DEBUG(1,("interpret_long_unix_date\n"));
+ if(IVAL(p,0) == SMB_TIME_NO_CHANGE_LO &&
+ IVAL(p,4) == SMB_TIME_NO_CHANGE_HI) {
+ return -1;
+ } else {
+ return interpret_long_date(p);
+ }
+}
+
+/****************************************************************************
+ Get a level dependent lanman2 dir entry.
+****************************************************************************/
+
+static BOOL get_lanman2_dir_entry(connection_struct *conn,
+ void *inbuf, void *outbuf,
+ char *path_mask,int dirtype,int info_level,
+ int requires_resume_key,
+ BOOL dont_descend,char **ppdata,
+ char *base_data, int space_remaining,
+ BOOL *out_of_space, BOOL *got_exact_match,
+ int *last_name_off)
+{
+ const char *dname;
+ BOOL found = False;
+ SMB_STRUCT_STAT sbuf;
+ pstring mask;
+ pstring pathreal;
+ pstring fname;
+ char *p, *q, *pdata = *ppdata;
+ uint32 reskey=0;
+ int prev_dirpos=0;
+ int mode=0;
+ SMB_OFF_T file_size = 0;
+ SMB_BIG_UINT allocation_size = 0;
+ uint32 len;
+ time_t mdate=0, adate=0, cdate=0;
+ char *nameptr;
+ BOOL was_8_3;
+ int nt_extmode; /* Used for NT connections instead of mode */
+ BOOL needslash = ( conn->dirpath[strlen(conn->dirpath) -1] != '/');
+
+ *fname = 0;
+ *out_of_space = False;
+ *got_exact_match = False;
+
+ if (!conn->dirptr)
+ return(False);
+
+ p = strrchr_m(path_mask,'/');
+ if(p != NULL) {
+ if(p[1] == '\0')
+ pstrcpy(mask,"*.*");
+ else
+ pstrcpy(mask, p+1);
+ } else
+ pstrcpy(mask, path_mask);
+
+ while (!found) {
+ BOOL got_match;
+
+ /* Needed if we run out of space */
+ prev_dirpos = TellDir(conn->dirptr);
+ dname = ReadDirName(conn->dirptr);
+
+ /*
+ * Due to bugs in NT client redirectors we are not using
+ * resume keys any more - set them to zero.
+ * Check out the related comments in findfirst/findnext.
+ * JRA.
+ */
+
+ reskey = 0;
+
+ DEBUG(8,("get_lanman2_dir_entry:readdir on dirptr 0x%lx now at offset %d\n",
+ (long)conn->dirptr,TellDir(conn->dirptr)));
+
+ if (!dname)
+ return(False);
+
+ pstrcpy(fname,dname);
+
+ if(!(got_match = *got_exact_match = exact_match(fname, mask, case_sensitive)))
+ got_match = mask_match(fname, mask, case_sensitive);
+
+ if(!got_match && !mangle_is_8_3(fname, False)) {
+
+ /*
+ * It turns out that NT matches wildcards against
+ * both long *and* short names. This may explain some
+ * of the wildcard wierdness from old DOS clients
+ * that some people have been seeing.... JRA.
+ */
+
+ pstring newname;
+ pstrcpy( newname, fname);
+ mangle_map( newname, True, False, SNUM(conn));
+ if(!(got_match = *got_exact_match = exact_match(newname, mask, case_sensitive)))
+ got_match = mask_match(newname, mask, case_sensitive);
+ }
+
+ if(got_match) {
+ BOOL isdots = (strequal(fname,"..") || strequal(fname,"."));
+ if (dont_descend && !isdots)
+ continue;
+
+ pstrcpy(pathreal,conn->dirpath);
+ if(needslash)
+ pstrcat(pathreal,"/");
+ pstrcat(pathreal,dname);
+
+ if (INFO_LEVEL_IS_UNIX(info_level)) {
+ if (SMB_VFS_LSTAT(conn,pathreal,&sbuf) != 0) {
+ DEBUG(5,("get_lanman2_dir_entry:Couldn't lstat [%s] (%s)\n",
+ pathreal,strerror(errno)));
+ continue;
+ }
+ } else if (SMB_VFS_STAT(conn,pathreal,&sbuf) != 0) {
+
+ /* Needed to show the msdfs symlinks as
+ * directories */
+
+ if(lp_host_msdfs() &&
+ lp_msdfs_root(SNUM(conn)) &&
+ is_msdfs_link(conn, pathreal, NULL, NULL,
+ &sbuf)) {
+
+ DEBUG(5,("get_lanman2_dir_entry: Masquerading msdfs link %s as a directory\n", pathreal));
+ sbuf.st_mode = (sbuf.st_mode & 0xFFF) | S_IFDIR;
+
+ } else {
+
+ DEBUG(5,("get_lanman2_dir_entry:Couldn't stat [%s] (%s)\n",
+ pathreal,strerror(errno)));
+ continue;
+ }
+ }
+
+ mode = dos_mode(conn,pathreal,&sbuf);
+
+ if (!dir_check_ftype(conn,mode,&sbuf,dirtype)) {
+ DEBUG(5,("[%s] attribs didn't match %x\n",fname,dirtype));
+ continue;
+ }
+
+ file_size = get_file_size(sbuf);
+ allocation_size = get_allocation_size(NULL,&sbuf);
+ mdate = sbuf.st_mtime;
+ adate = sbuf.st_atime;
+ cdate = get_create_time(&sbuf,lp_fake_dir_create_times(SNUM(conn)));
+
+ if (lp_dos_filetime_resolution(SNUM(conn))) {
+ cdate &= ~1;
+ mdate &= ~1;
+ adate &= ~1;
+ }
+
+ if(mode & aDIR)
+ file_size = 0;
+
+ DEBUG(5,("get_lanman2_dir_entry found %s fname=%s\n",pathreal,fname));
+
+ found = True;
+ }
+ }
+
+ mangle_map(fname,False,True,SNUM(conn));
+
+ p = pdata;
+ nameptr = p;
+
+ nt_extmode = mode ? mode : FILE_ATTRIBUTE_NORMAL;
+
+ switch (info_level) {
+ case SMB_INFO_STANDARD:
+ if(requires_resume_key) {
+ SIVAL(p,0,reskey);
+ p += 4;
+ }
+ put_dos_date2(p,l1_fdateCreation,cdate);
+ put_dos_date2(p,l1_fdateLastAccess,adate);
+ put_dos_date2(p,l1_fdateLastWrite,mdate);
+ SIVAL(p,l1_cbFile,(uint32)file_size);
+ SIVAL(p,l1_cbFileAlloc,(uint32)allocation_size);
+ SSVAL(p,l1_attrFile,mode);
+ p += l1_achName;
+ nameptr = p;
+ p += align_string(outbuf, p, 0);
+ len = srvstr_push(outbuf, p, fname, -1, STR_TERMINATE);
+ if (SVAL(outbuf, smb_flg2) & FLAGS2_UNICODE_STRINGS)
+ SCVAL(nameptr, -1, len-2);
+ else
+ SCVAL(nameptr, -1, len-1);
+ p += len;
+ break;
+
+ case SMB_INFO_QUERY_EA_SIZE:
+ if(requires_resume_key) {
+ SIVAL(p,0,reskey);
+ p += 4;
+ }
+ put_dos_date2(p,l2_fdateCreation,cdate);
+ put_dos_date2(p,l2_fdateLastAccess,adate);
+ put_dos_date2(p,l2_fdateLastWrite,mdate);
+ SIVAL(p,l2_cbFile,(uint32)file_size);
+ SIVAL(p,l2_cbFileAlloc,(uint32)allocation_size);
+ SSVAL(p,l2_attrFile,mode);
+ SIVAL(p,l2_cbList,0); /* No extended attributes */
+ p += l2_achName;
+ nameptr = p;
+ len = srvstr_push(outbuf, p, fname, -1, STR_TERMINATE | STR_NOALIGN);
+ if (SVAL(outbuf, smb_flg2) & FLAGS2_UNICODE_STRINGS)
+ SCVAL(nameptr, -1, len-2);
+ else
+ SCVAL(nameptr, -1, len-1);
+ p += len;
+ break;
+
+ case SMB_FIND_FILE_BOTH_DIRECTORY_INFO:
+ was_8_3 = mangle_is_8_3(fname, True);
+ p += 4;
+ SIVAL(p,0,reskey); p += 4;
+ put_long_date(p,cdate); p += 8;
+ put_long_date(p,adate); p += 8;
+ put_long_date(p,mdate); p += 8;
+ put_long_date(p,mdate); p += 8;
+ SOFF_T(p,0,file_size);
+ SOFF_T(p,8,allocation_size);
+ p += 16;
+ SIVAL(p,0,nt_extmode); p += 4;
+ q = p; p += 4;
+ SIVAL(p,0,0); p += 4;
+ /* Clear the short name buffer. This is
+ * IMPORTANT as not doing so will trigger
+ * a Win2k client bug. JRA.
+ */
+ memset(p,'\0',26);
+ if (!was_8_3 && lp_manglednames(SNUM(conn))) {
+ pstring mangled_name;
+ pstrcpy(mangled_name, fname);
+ mangle_map(mangled_name,True,True,SNUM(conn));
+ mangled_name[12] = 0;
+ len = srvstr_push(outbuf, p+2, mangled_name, 24, STR_UPPER|STR_UNICODE);
+ SSVAL(p, 0, len);
+ } else {
+ SSVAL(p,0,0);
+ *(p+2) = 0;
+ }
+ p += 2 + 24;
+ len = srvstr_push(outbuf, p, fname, -1, STR_TERMINATE_ASCII);
+ SIVAL(q,0,len);
+ p += len;
+ len = PTR_DIFF(p, pdata);
+ len = (len + 3) & ~3;
+ SIVAL(pdata,0,len);
+ p = pdata + len;
+ break;
+
+ case SMB_FIND_FILE_DIRECTORY_INFO:
+ p += 4;
+ SIVAL(p,0,reskey); p += 4;
+ put_long_date(p,cdate); p += 8;
+ put_long_date(p,adate); p += 8;
+ put_long_date(p,mdate); p += 8;
+ put_long_date(p,mdate); p += 8;
+ SOFF_T(p,0,file_size);
+ SOFF_T(p,8,allocation_size);
+ p += 16;
+ SIVAL(p,0,nt_extmode); p += 4;
+ p += 4;
+ len = srvstr_push(outbuf, p, fname, -1, STR_TERMINATE_ASCII);
+ SIVAL(p, -4, len);
+ p += len;
+ len = PTR_DIFF(p, pdata);
+ len = (len + 3) & ~3;
+ SIVAL(pdata,0,len);
+ p = pdata + len;
+ break;
+
+ case SMB_FIND_FILE_FULL_DIRECTORY_INFO:
+ p += 4;
+ SIVAL(p,0,reskey); p += 4;
+ put_long_date(p,cdate); p += 8;
+ put_long_date(p,adate); p += 8;
+ put_long_date(p,mdate); p += 8;
+ put_long_date(p,mdate); p += 8;
+ SOFF_T(p,0,file_size);
+ SOFF_T(p,8,allocation_size);
+ p += 16;
+ SIVAL(p,0,nt_extmode);
+ p += 4;
+
+ SIVAL(p,4,0); /* ea size */
+ len = srvstr_push(outbuf, p+8, fname, -1, STR_TERMINATE_ASCII);
+ SIVAL(p, 0, len);
+ p += 8 + len;
+
+ len = PTR_DIFF(p, pdata);
+ len = (len + 3) & ~3;
+ SIVAL(pdata,0,len);
+ p = pdata + len;
+ break;
+
+ case SMB_FIND_FILE_NAMES_INFO:
+ p += 4;
+ SIVAL(p,0,reskey); p += 4;
+ p += 4;
+ /* this must *not* be null terminated or w2k gets in a loop trying to set an
+ acl on a dir (tridge) */
+ len = srvstr_push(outbuf, p, fname, -1, STR_TERMINATE_ASCII);
+ SIVAL(p, -4, len);
+ p += len;
+ len = PTR_DIFF(p, pdata);
+ len = (len + 3) & ~3;
+ SIVAL(pdata,0,len);
+ p = pdata + len;
+ break;
+
+ case SMB_FIND_FILE_LEVEL_261:
+ p += 4;
+ SIVAL(p,0,reskey); p += 4;
+ put_long_date(p,cdate); p += 8;
+ put_long_date(p,adate); p += 8;
+ put_long_date(p,mdate); p += 8;
+ put_long_date(p,mdate); p += 8;
+ SOFF_T(p,0,file_size);
+ SOFF_T(p,8,allocation_size);
+ p += 16;
+ SIVAL(p,0,nt_extmode);
+ p += 4;
+ len = srvstr_push(outbuf, p + 20, fname, -1, STR_TERMINATE_ASCII);
+ SIVAL(p, 0, len);
+ memset(p+4,'\0',16); /* EA size. Unknown 0 1 2 */
+ p += 20 + len; /* Strlen, EA size. Unknown 0 1 2, string itself */
+ len = PTR_DIFF(p, pdata);
+ len = (len + 3) & ~3;
+ SIVAL(pdata,0,len);
+ p = pdata + len;
+ break;
+
+ case SMB_FIND_FILE_LEVEL_262:
+ was_8_3 = mangle_is_8_3(fname, True);
+ p += 4;
+ SIVAL(p,0,reskey); p += 4;
+ put_long_date(p,cdate); p += 8;
+ put_long_date(p,adate); p += 8;
+ put_long_date(p,mdate); p += 8;
+ put_long_date(p,mdate); p += 8;
+ SOFF_T(p,0,file_size);
+ SOFF_T(p,8,allocation_size);
+ p += 16;
+ SIVAL(p,0,nt_extmode); p += 4;
+ q = p; p += 4;
+ SIVAL(p,0,0); p += 4;
+ /* Clear the short name buffer. This is
+ * IMPORTANT as not doing so will trigger
+ * a Win2k client bug. JRA.
+ */
+ memset(p,'\0',26);
+ if (!was_8_3 && lp_manglednames(SNUM(conn))) {
+ pstring mangled_name;
+ pstrcpy(mangled_name, fname);
+ mangle_map(mangled_name,True,True,SNUM(conn));
+ mangled_name[12] = 0;
+ len = srvstr_push(outbuf, p+2, mangled_name, 24, STR_UPPER|STR_UNICODE);
+ SSVAL(p, 0, len);
+ } else {
+ SSVAL(p,0,0);
+ *(p+2) = 0;
+ }
+ p += 2 + 24;
+ memset(p, '\0', 10); /* 2 4 byte unknowns plus a zero reserved. */
+ p += 10;
+ len = srvstr_push(outbuf, p, fname, -1, STR_TERMINATE_ASCII);
+ SIVAL(q,0,len);
+ p += len;
+ len = PTR_DIFF(p, pdata);
+ len = (len + 3) & ~3;
+ SIVAL(pdata,0,len);
+ p = pdata + len;
+ break;
+
+ /* CIFS UNIX Extension. */
+
+ case SMB_FIND_FILE_UNIX:
+ p+= 4;
+ SIVAL(p,0,reskey); p+= 4; /* Used for continuing search. */
+
+ /* Begin of SMB_QUERY_FILE_UNIX_BASIC */
+ SOFF_T(p,0,get_file_size(sbuf)); /* File size 64 Bit */
+ p+= 8;
+
+ SOFF_T(p,0,get_allocation_size(NULL,&sbuf)); /* Number of bytes used on disk - 64 Bit */
+ p+= 8;
+
+ put_long_date(p,sbuf.st_ctime); /* Creation Time 64 Bit */
+ put_long_date(p+8,sbuf.st_atime); /* Last access time 64 Bit */
+ put_long_date(p+16,sbuf.st_mtime); /* Last modification time 64 Bit */
+ p+= 24;
+
+ SIVAL(p,0,sbuf.st_uid); /* user id for the owner */
+ SIVAL(p,4,0);
+ p+= 8;
+
+ SIVAL(p,0,sbuf.st_gid); /* group id of owner */
+ SIVAL(p,4,0);
+ p+= 8;
+
+ SIVAL(p,0,unix_filetype(sbuf.st_mode));
+ p+= 4;
+
+ SIVAL(p,0,unix_dev_major(sbuf.st_rdev)); /* Major device number if type is device */
+ SIVAL(p,4,0);
+ p+= 8;
+
+ SIVAL(p,0,unix_dev_minor(sbuf.st_rdev)); /* Minor device number if type is device */
+ SIVAL(p,4,0);
+ p+= 8;
+
+ SINO_T(p,0,(SMB_INO_T)sbuf.st_ino); /* inode number */
+ p+= 8;
+
+ SIVAL(p,0, unix_perms_to_wire(sbuf.st_mode)); /* Standard UNIX file permissions */
+ SIVAL(p,4,0);
+ p+= 8;
+
+ SIVAL(p,0,sbuf.st_nlink); /* number of hard links */
+ SIVAL(p,4,0);
+ p+= 8;
+
+ len = srvstr_push(outbuf, p, fname, -1, STR_TERMINATE);
+ p += len;
+
+ len = PTR_DIFF(p, pdata);
+ len = (len + 3) & ~3;
+ SIVAL(pdata,0,len); /* Offset from this structure to the beginning of the next one */
+ p = pdata + len;
+ /* End of SMB_QUERY_FILE_UNIX_BASIC */
+
+ break;
+
+ default:
+ return(False);
+ }
+
+
+ if (PTR_DIFF(p,pdata) > space_remaining) {
+ /* Move the dirptr back to prev_dirpos */
+ SeekDir(conn->dirptr, prev_dirpos);
+ *out_of_space = True;
+ DEBUG(9,("get_lanman2_dir_entry: out of space\n"));
+ return False; /* Not finished - just out of space */
+ }
+
+ /* Setup the last_filename pointer, as an offset from base_data */
+ *last_name_off = PTR_DIFF(nameptr,base_data);
+ /* Advance the data pointer to the next slot */
+ *ppdata = p;
+
+ return(found);
+}
+
+/****************************************************************************
+ Reply to a TRANS2_FINDFIRST.
+****************************************************************************/
+
+static int call_trans2findfirst(connection_struct *conn, char *inbuf, char *outbuf, int bufsize,
+ char **pparams, int total_params, char **ppdata, int total_data)
+{
+ /* We must be careful here that we don't return more than the
+ allowed number of data bytes. If this means returning fewer than
+ maxentries then so be it. We assume that the redirector has
+ enough room for the fixed number of parameter bytes it has
+ requested. */
+ uint32 max_data_bytes = SVAL(inbuf, smb_mdrcnt);
+ char *params = *pparams;
+ char *pdata = *ppdata;
+ int dirtype = SVAL(params,0);
+ int maxentries = SVAL(params,2);
+ BOOL close_after_first = BITSETW(params+4,0);
+ BOOL close_if_end = BITSETW(params+4,1);
+ BOOL requires_resume_key = BITSETW(params+4,2);
+ int info_level = SVAL(params,6);
+ pstring directory;
+ pstring mask;
+ char *p, *wcard;
+ int last_name_off=0;
+ int dptr_num = -1;
+ int numentries = 0;
+ int i;
+ BOOL finished = False;
+ BOOL dont_descend = False;
+ BOOL out_of_space = False;
+ int space_remaining;
+ BOOL bad_path = False;
+ SMB_STRUCT_STAT sbuf;
+ NTSTATUS ntstatus = NT_STATUS_OK;
+
+ if (total_params < 12)
+ return(ERROR_DOS(ERRDOS,ERRinvalidparam));
+
+ *directory = *mask = 0;
+
+ DEBUG(3,("call_trans2findfirst: dirtype = %d, maxentries = %d, close_after_first=%d, \
+close_if_end = %d requires_resume_key = %d level = %d, max_data_bytes = %d\n",
+ dirtype, maxentries, close_after_first, close_if_end, requires_resume_key,
+ info_level, max_data_bytes));
+
+ switch (info_level) {
+ case SMB_INFO_STANDARD:
+ case SMB_INFO_QUERY_EA_SIZE:
+ case SMB_FIND_FILE_DIRECTORY_INFO:
+ case SMB_FIND_FILE_FULL_DIRECTORY_INFO:
+ case SMB_FIND_FILE_NAMES_INFO:
+ case SMB_FIND_FILE_BOTH_DIRECTORY_INFO:
+ case SMB_FIND_FILE_LEVEL_261:
+ case SMB_FIND_FILE_LEVEL_262:
+ break;
+ case SMB_FIND_FILE_UNIX:
+ if (!lp_unix_extensions())
+ return(ERROR_DOS(ERRDOS,ERRunknownlevel));
+ break;
+ default:
+ return(ERROR_DOS(ERRDOS,ERRunknownlevel));
+ }
+
+ srvstr_get_path(inbuf, directory, params+12, sizeof(directory), -1, STR_TERMINATE, &ntstatus);
+ if (!NT_STATUS_IS_OK(ntstatus)) {
+ return ERROR_NT(ntstatus);
+ }
+
+ RESOLVE_FINDFIRST_DFSPATH(directory, conn, inbuf, outbuf);
+
+ unix_convert(directory,conn,0,&bad_path,&sbuf);
+ if(!check_name(directory,conn)) {
+ return set_bad_path_error(errno, bad_path, outbuf, ERRDOS,ERRbadpath);
+ }
+
+ p = strrchr_m(directory,'/');
+ if(p == NULL) {
+ /* Windows and OS/2 systems treat search on the root '\' as if it were '\*' */
+ if((directory[0] == '.') && (directory[1] == '\0'))
+ pstrcpy(mask,"*");
+ else
+ pstrcpy(mask,directory);
+ pstrcpy(directory,"./");
+ } else {
+ pstrcpy(mask,p+1);
+ *p = 0;
+ }
+
+ DEBUG(5,("dir=%s, mask = %s\n",directory, mask));
+
+ pdata = Realloc(*ppdata, max_data_bytes + 1024);
+ if( pdata == NULL )
+ return(ERROR_DOS(ERRDOS,ERRnomem));
+
+ *ppdata = pdata;
+ memset((char *)pdata,'\0',max_data_bytes + 1024);
+
+ /* Realloc the params space */
+ params = Realloc(*pparams, 10);
+ if (params == NULL)
+ return ERROR_DOS(ERRDOS,ERRnomem);
+ *pparams = params;
+
+ dptr_num = dptr_create(conn,directory, False, True ,SVAL(inbuf,smb_pid));
+ if (dptr_num < 0)
+ return(UNIXERROR(ERRDOS,ERRbadfile));
+
+ /* Save the wildcard match and attribs we are using on this directory -
+ needed as lanman2 assumes these are being saved between calls */
+
+ if(!(wcard = strdup(mask))) {
+ dptr_close(&dptr_num);
+ return ERROR_DOS(ERRDOS,ERRnomem);
+ }
+
+ dptr_set_wcard(dptr_num, wcard);
+ dptr_set_attr(dptr_num, dirtype);
+
+ DEBUG(4,("dptr_num is %d, wcard = %s, attr = %d\n",dptr_num, wcard, dirtype));
+
+ /* We don't need to check for VOL here as this is returned by
+ a different TRANS2 call. */
+
+ DEBUG(8,("dirpath=<%s> dontdescend=<%s>\n", conn->dirpath,lp_dontdescend(SNUM(conn))));
+ if (in_list(conn->dirpath,lp_dontdescend(SNUM(conn)),case_sensitive))
+ dont_descend = True;
+
+ p = pdata;
+ space_remaining = max_data_bytes;
+ out_of_space = False;
+
+ for (i=0;(i<maxentries) && !finished && !out_of_space;i++) {
+ BOOL got_exact_match = False;
+
+ /* this is a heuristic to avoid seeking the dirptr except when
+ absolutely necessary. It allows for a filename of about 40 chars */
+ if (space_remaining < DIRLEN_GUESS && numentries > 0) {
+ out_of_space = True;
+ finished = False;
+ } else {
+ finished = !get_lanman2_dir_entry(conn,
+ inbuf, outbuf,
+ mask,dirtype,info_level,
+ requires_resume_key,dont_descend,
+ &p,pdata,space_remaining, &out_of_space, &got_exact_match,
+ &last_name_off);
+ }
+
+ if (finished && out_of_space)
+ finished = False;
+
+ if (!finished && !out_of_space)
+ numentries++;
+
+ /*
+ * As an optimisation if we know we aren't looking
+ * for a wildcard name (ie. the name matches the wildcard exactly)
+ * then we can finish on any (first) match.
+ * This speeds up large directory searches. JRA.
+ */
+
+ if(got_exact_match)
+ finished = True;
+
+ space_remaining = max_data_bytes - PTR_DIFF(p,pdata);
+ }
+
+ /* Check if we can close the dirptr */
+ if(close_after_first || (finished && close_if_end)) {
+ DEBUG(5,("call_trans2findfirst - (2) closing dptr_num %d\n", dptr_num));
+ dptr_close(&dptr_num);
+ }
+
+ /*
+ * If there are no matching entries we must return ERRDOS/ERRbadfile -
+ * from observation of NT.
+ */
+
+ if(numentries == 0) {
+ dptr_close(&dptr_num);
+ return ERROR_DOS(ERRDOS,ERRbadfile);
+ }
+
+ /* At this point pdata points to numentries directory entries. */
+
+ /* Set up the return parameter block */
+ SSVAL(params,0,dptr_num);
+ SSVAL(params,2,numentries);
+ SSVAL(params,4,finished);
+ SSVAL(params,6,0); /* Never an EA error */
+ SSVAL(params,8,last_name_off);
+
+ send_trans2_replies( outbuf, bufsize, params, 10, pdata, PTR_DIFF(p,pdata));
+
+ if ((! *directory) && dptr_path(dptr_num))
+ slprintf(directory,sizeof(directory)-1, "(%s)",dptr_path(dptr_num));
+
+ DEBUG( 4, ( "%s mask=%s directory=%s dirtype=%d numentries=%d\n",
+ smb_fn_name(CVAL(inbuf,smb_com)),
+ mask, directory, dirtype, numentries ) );
+
+ /*
+ * Force a name mangle here to ensure that the
+ * mask as an 8.3 name is top of the mangled cache.
+ * The reasons for this are subtle. Don't remove
+ * this code unless you know what you are doing
+ * (see PR#13758). JRA.
+ */
+
+ if(!mangle_is_8_3_wildcards( mask, False))
+ mangle_map(mask, True, True, SNUM(conn));
+
+ return(-1);
+}
+
+/****************************************************************************
+ Reply to a TRANS2_FINDNEXT.
+****************************************************************************/
+
+static int call_trans2findnext(connection_struct *conn, char *inbuf, char *outbuf, int length, int bufsize,
+ char **pparams, int total_params, char **ppdata, int total_data)
+{
+ /* We must be careful here that we don't return more than the
+ allowed number of data bytes. If this means returning fewer than
+ maxentries then so be it. We assume that the redirector has
+ enough room for the fixed number of parameter bytes it has
+ requested. */
+ int max_data_bytes = SVAL(inbuf, smb_mdrcnt);
+ char *params = *pparams;
+ char *pdata = *ppdata;
+ int dptr_num = SVAL(params,0);
+ int maxentries = SVAL(params,2);
+ uint16 info_level = SVAL(params,4);
+ uint32 resume_key = IVAL(params,6);
+ BOOL close_after_request = BITSETW(params+10,0);
+ BOOL close_if_end = BITSETW(params+10,1);
+ BOOL requires_resume_key = BITSETW(params+10,2);
+ BOOL continue_bit = BITSETW(params+10,3);
+ pstring resume_name;
+ pstring mask;
+ pstring directory;
+ char *p;
+ uint16 dirtype;
+ int numentries = 0;
+ int i, last_name_off=0;
+ BOOL finished = False;
+ BOOL dont_descend = False;
+ BOOL out_of_space = False;
+ int space_remaining;
+ NTSTATUS ntstatus = NT_STATUS_OK;
+
+ if (total_params < 12)
+ return(ERROR_DOS(ERRDOS,ERRinvalidparam));
+
+ *mask = *directory = *resume_name = 0;
+
+ srvstr_get_path(inbuf, resume_name, params+12, sizeof(resume_name), -1, STR_TERMINATE, &ntstatus);
+ if (!NT_STATUS_IS_OK(ntstatus)) {
+ return ERROR_NT(ntstatus);
+ }
+
+ DEBUG(3,("call_trans2findnext: dirhandle = %d, max_data_bytes = %d, maxentries = %d, \
+close_after_request=%d, close_if_end = %d requires_resume_key = %d \
+resume_key = %d resume name = %s continue=%d level = %d\n",
+ dptr_num, max_data_bytes, maxentries, close_after_request, close_if_end,
+ requires_resume_key, resume_key, resume_name, continue_bit, info_level));
+
+ switch (info_level) {
+ case SMB_INFO_STANDARD:
+ case SMB_INFO_QUERY_EA_SIZE:
+ case SMB_FIND_FILE_DIRECTORY_INFO:
+ case SMB_FIND_FILE_FULL_DIRECTORY_INFO:
+ case SMB_FIND_FILE_NAMES_INFO:
+ case SMB_FIND_FILE_BOTH_DIRECTORY_INFO:
+ break;
+ case SMB_FIND_FILE_UNIX:
+ if (!lp_unix_extensions())
+ return(ERROR_DOS(ERRDOS,ERRunknownlevel));
+ break;
+ default:
+ return ERROR_DOS(ERRDOS,ERRunknownlevel);
+ }
+
+ pdata = Realloc( *ppdata, max_data_bytes + 1024);
+ if(pdata == NULL)
+ return ERROR_DOS(ERRDOS,ERRnomem);
+
+ *ppdata = pdata;
+ memset((char *)pdata,'\0',max_data_bytes + 1024);
+
+ /* Realloc the params space */
+ params = Realloc(*pparams, 6*SIZEOFWORD);
+ if( params == NULL )
+ return ERROR_DOS(ERRDOS,ERRnomem);
+
+ *pparams = params;
+
+ /* Check that the dptr is valid */
+ if(!(conn->dirptr = dptr_fetch_lanman2(dptr_num)))
+ return ERROR_DOS(ERRDOS,ERRnofiles);
+
+ string_set(&conn->dirpath,dptr_path(dptr_num));
+
+ /* Get the wildcard mask from the dptr */
+ if((p = dptr_wcard(dptr_num))== NULL) {
+ DEBUG(2,("dptr_num %d has no wildcard\n", dptr_num));
+ return ERROR_DOS(ERRDOS,ERRnofiles);
+ }
+
+ pstrcpy(mask, p);
+ pstrcpy(directory,conn->dirpath);
+
+ /* Get the attr mask from the dptr */
+ dirtype = dptr_attr(dptr_num);
+
+ DEBUG(3,("dptr_num is %d, mask = %s, attr = %x, dirptr=(0x%lX,%d)\n",
+ dptr_num, mask, dirtype,
+ (long)conn->dirptr,
+ TellDir(conn->dirptr)));
+
+ /* We don't need to check for VOL here as this is returned by
+ a different TRANS2 call. */
+
+ DEBUG(8,("dirpath=<%s> dontdescend=<%s>\n",conn->dirpath,lp_dontdescend(SNUM(conn))));
+ if (in_list(conn->dirpath,lp_dontdescend(SNUM(conn)),case_sensitive))
+ dont_descend = True;
+
+ p = pdata;
+ space_remaining = max_data_bytes;
+ out_of_space = False;
+
+ /*
+ * Seek to the correct position. We no longer use the resume key but
+ * depend on the last file name instead.
+ */
+
+ if(requires_resume_key && *resume_name && !continue_bit) {
+
+ /*
+ * Fix for NT redirector problem triggered by resume key indexes
+ * changing between directory scans. We now return a resume key of 0
+ * and instead look for the filename to continue from (also given
+ * to us by NT/95/smbfs/smbclient). If no other scans have been done between the
+ * findfirst/findnext (as is usual) then the directory pointer
+ * should already be at the correct place. Check this by scanning
+ * backwards looking for an exact (ie. case sensitive) filename match.
+ * If we get to the beginning of the directory and haven't found it then scan
+ * forwards again looking for a match. JRA.
+ */
+
+ int current_pos, start_pos;
+ const char *dname = NULL;
+ pstring dname_pstring;
+ void *dirptr = conn->dirptr;
+ start_pos = TellDir(dirptr);
+ for(current_pos = start_pos; current_pos >= 0; current_pos--) {
+ DEBUG(7,("call_trans2findnext: seeking to pos %d\n", current_pos));
+
+ SeekDir(dirptr, current_pos);
+ dname = ReadDirName(dirptr);
+ if (dname) {
+ /*
+ * Remember, mangle_map is called by
+ * get_lanman2_dir_entry(), so the resume name
+ * could be mangled. Ensure we do the same
+ * here.
+ */
+
+ /* make sure we get a copy that mangle_map can modify */
+
+ pstrcpy(dname_pstring, dname);
+ mangle_map( dname_pstring, False, True, SNUM(conn));
+
+ if(strcsequal( resume_name, dname_pstring)) {
+ SeekDir(dirptr, current_pos+1);
+ DEBUG(7,("call_trans2findnext: got match at pos %d\n", current_pos+1 ));
+ break;
+ }
+ }
+ }
+
+ /*
+ * Scan forward from start if not found going backwards.
+ */
+
+ if(current_pos < 0) {
+ DEBUG(7,("call_trans2findnext: notfound: seeking to pos %d\n", start_pos));
+ SeekDir(dirptr, start_pos);
+ for(current_pos = start_pos; (dname = ReadDirName(dirptr)) != NULL; SeekDir(dirptr,++current_pos)) {
+
+ /*
+ * Remember, mangle_map is called by
+ * get_lanman2_dir_entry(), so the resume name
+ * could be mangled. Ensure we do the same
+ * here.
+ */
+
+ if(dname) {
+ /* make sure we get a copy that mangle_map can modify */
+
+ pstrcpy(dname_pstring, dname);
+ mangle_map(dname_pstring, False, True, SNUM(conn));
+
+ if(strcsequal( resume_name, dname_pstring)) {
+ SeekDir(dirptr, current_pos+1);
+ DEBUG(7,("call_trans2findnext: got match at pos %d\n", current_pos+1 ));
+ break;
+ }
+ }
+ } /* end for */
+ } /* end if current_pos */
+ } /* end if requires_resume_key && !continue_bit */
+
+ for (i=0;(i<(int)maxentries) && !finished && !out_of_space ;i++) {
+ BOOL got_exact_match = False;
+
+ /* this is a heuristic to avoid seeking the dirptr except when
+ absolutely necessary. It allows for a filename of about 40 chars */
+ if (space_remaining < DIRLEN_GUESS && numentries > 0) {
+ out_of_space = True;
+ finished = False;
+ } else {
+ finished = !get_lanman2_dir_entry(conn,
+ inbuf, outbuf,
+ mask,dirtype,info_level,
+ requires_resume_key,dont_descend,
+ &p,pdata,space_remaining, &out_of_space, &got_exact_match,
+ &last_name_off);
+ }
+
+ if (finished && out_of_space)
+ finished = False;
+
+ if (!finished && !out_of_space)
+ numentries++;
+
+ /*
+ * As an optimisation if we know we aren't looking
+ * for a wildcard name (ie. the name matches the wildcard exactly)
+ * then we can finish on any (first) match.
+ * This speeds up large directory searches. JRA.
+ */
+
+ if(got_exact_match)
+ finished = True;
+
+ space_remaining = max_data_bytes - PTR_DIFF(p,pdata);
+ }
+
+ /* Check if we can close the dirptr */
+ if(close_after_request || (finished && close_if_end)) {
+ DEBUG(5,("call_trans2findnext: closing dptr_num = %d\n", dptr_num));
+ dptr_close(&dptr_num); /* This frees up the saved mask */
+ }
+
+ /* Set up the return parameter block */
+ SSVAL(params,0,numentries);
+ SSVAL(params,2,finished);
+ SSVAL(params,4,0); /* Never an EA error */
+ SSVAL(params,6,last_name_off);
+
+ send_trans2_replies( outbuf, bufsize, params, 8, pdata, PTR_DIFF(p,pdata));
+
+ if ((! *directory) && dptr_path(dptr_num))
+ slprintf(directory,sizeof(directory)-1, "(%s)",dptr_path(dptr_num));
+
+ DEBUG( 3, ( "%s mask=%s directory=%s dirtype=%d numentries=%d\n",
+ smb_fn_name(CVAL(inbuf,smb_com)),
+ mask, directory, dirtype, numentries ) );
+
+ return(-1);
+}
+
+/****************************************************************************
+ Reply to a TRANS2_QFSINFO (query filesystem info).
+****************************************************************************/
+
+static int call_trans2qfsinfo(connection_struct *conn, char *inbuf, char *outbuf,
+ int length, int bufsize,
+ char **pparams, int total_params, char **ppdata, int total_data)
+{
+ int max_data_bytes = SVAL(inbuf, smb_mdrcnt);
+ char *pdata = *ppdata;
+ char *params = *pparams;
+ uint16 info_level = SVAL(params,0);
+ int data_len, len;
+ SMB_STRUCT_STAT st;
+ char *vname = volume_label(SNUM(conn));
+ int snum = SNUM(conn);
+ char *fstype = lp_fstype(SNUM(conn));
+ int quota_flag = 0;
+
+ DEBUG(3,("call_trans2qfsinfo: level = %d\n", info_level));
+
+ if(SMB_VFS_STAT(conn,".",&st)!=0) {
+ DEBUG(2,("call_trans2qfsinfo: stat of . failed (%s)\n", strerror(errno)));
+ return ERROR_DOS(ERRSRV,ERRinvdevice);
+ }
+
+ pdata = Realloc(*ppdata, max_data_bytes + 1024);
+ if ( pdata == NULL )
+ return ERROR_DOS(ERRDOS,ERRnomem);
+
+ *ppdata = pdata;
+ memset((char *)pdata,'\0',max_data_bytes + 1024);
+
+ switch (info_level) {
+ case SMB_INFO_ALLOCATION:
+ {
+ SMB_BIG_UINT dfree,dsize,bsize,block_size,sectors_per_unit,bytes_per_sector;
+ data_len = 18;
+ SMB_VFS_DISK_FREE(conn,".",False,&bsize,&dfree,&dsize);
+ block_size = lp_block_size(snum);
+ if (bsize < block_size) {
+ SMB_BIG_UINT factor = block_size/bsize;
+ bsize = block_size;
+ dsize /= factor;
+ dfree /= factor;
+ }
+ if (bsize > block_size) {
+ SMB_BIG_UINT factor = bsize/block_size;
+ bsize = block_size;
+ dsize *= factor;
+ dfree *= factor;
+ }
+ bytes_per_sector = 512;
+ sectors_per_unit = bsize/bytes_per_sector;
+
+ DEBUG(5,("call_trans2qfsinfo : SMB_INFO_ALLOCATION id=%x, bsize=%u, cSectorUnit=%u, \
+cBytesSector=%u, cUnitTotal=%u, cUnitAvail=%d\n", (unsigned int)st.st_dev, (unsigned int)bsize, (unsigned int)sectors_per_unit,
+ (unsigned int)bytes_per_sector, (unsigned int)dsize, (unsigned int)dfree));
+
+ SIVAL(pdata,l1_idFileSystem,st.st_dev);
+ SIVAL(pdata,l1_cSectorUnit,sectors_per_unit);
+ SIVAL(pdata,l1_cUnit,dsize);
+ SIVAL(pdata,l1_cUnitAvail,dfree);
+ SSVAL(pdata,l1_cbSector,bytes_per_sector);
+ break;
+ }
+
+ case SMB_INFO_VOLUME:
+ /* Return volume name */
+ /*
+ * Add volume serial number - hash of a combination of
+ * the called hostname and the service name.
+ */
+ SIVAL(pdata,0,str_checksum(lp_servicename(snum)) ^ (str_checksum(local_machine)<<16) );
+ len = srvstr_push(outbuf, pdata+l2_vol_szVolLabel, vname, -1, STR_NOALIGN);
+ SCVAL(pdata,l2_vol_cch,len);
+ data_len = l2_vol_szVolLabel + len;
+ DEBUG(5,("call_trans2qfsinfo : time = %x, namelen = %d, name = %s\n",
+ (unsigned)st.st_ctime, len, vname));
+ break;
+
+ case SMB_QUERY_FS_ATTRIBUTE_INFO:
+ case SMB_FS_ATTRIBUTE_INFORMATION:
+
+
+#if defined(HAVE_SYS_QUOTAS)
+ quota_flag = FILE_VOLUME_QUOTAS;
+#endif
+
+ SIVAL(pdata,0,FILE_CASE_PRESERVED_NAMES|FILE_CASE_SENSITIVE_SEARCH|
+ (lp_nt_acl_support(SNUM(conn)) ? FILE_PERSISTENT_ACLS : 0)|
+ quota_flag); /* FS ATTRIBUTES */
+
+ SIVAL(pdata,4,255); /* Max filename component length */
+ /* NOTE! the fstype must *not* be null terminated or win98 won't recognise it
+ and will think we can't do long filenames */
+ len = srvstr_push(outbuf, pdata+12, fstype, -1, STR_UNICODE);
+ SIVAL(pdata,8,len);
+ data_len = 12 + len;
+ break;
+
+ case SMB_QUERY_FS_LABEL_INFO:
+ case SMB_FS_LABEL_INFORMATION:
+ len = srvstr_push(outbuf, pdata+4, vname, -1, 0);
+ data_len = 4 + len;
+ SIVAL(pdata,0,len);
+ break;
+
+ case SMB_QUERY_FS_VOLUME_INFO:
+ case SMB_FS_VOLUME_INFORMATION:
+
+ /*
+ * Add volume serial number - hash of a combination of
+ * the called hostname and the service name.
+ */
+ SIVAL(pdata,8,str_checksum(lp_servicename(snum)) ^
+ (str_checksum(local_machine)<<16));
+
+ len = srvstr_push(outbuf, pdata+18, vname, -1, STR_UNICODE);
+ SIVAL(pdata,12,len);
+ data_len = 18+len;
+ DEBUG(5,("call_trans2qfsinfo : SMB_QUERY_FS_VOLUME_INFO namelen = %d, vol=%s serv=%s\n",
+ (int)strlen(vname),vname, lp_servicename(snum)));
+ break;
+
+ case SMB_QUERY_FS_SIZE_INFO:
+ case SMB_FS_SIZE_INFORMATION:
+ {
+ SMB_BIG_UINT dfree,dsize,bsize,block_size,sectors_per_unit,bytes_per_sector;
+ data_len = 24;
+ SMB_VFS_DISK_FREE(conn,".",False,&bsize,&dfree,&dsize);
+ block_size = lp_block_size(snum);
+ if (bsize < block_size) {
+ SMB_BIG_UINT factor = block_size/bsize;
+ bsize = block_size;
+ dsize /= factor;
+ dfree /= factor;
+ }
+ if (bsize > block_size) {
+ SMB_BIG_UINT factor = bsize/block_size;
+ bsize = block_size;
+ dsize *= factor;
+ dfree *= factor;
+ }
+ bytes_per_sector = 512;
+ sectors_per_unit = bsize/bytes_per_sector;
+ DEBUG(5,("call_trans2qfsinfo : SMB_QUERY_FS_SIZE_INFO bsize=%u, cSectorUnit=%u, \
+cBytesSector=%u, cUnitTotal=%u, cUnitAvail=%d\n", (unsigned int)bsize, (unsigned int)sectors_per_unit,
+ (unsigned int)bytes_per_sector, (unsigned int)dsize, (unsigned int)dfree));
+ SBIG_UINT(pdata,0,dsize);
+ SBIG_UINT(pdata,8,dfree);
+ SIVAL(pdata,16,sectors_per_unit);
+ SIVAL(pdata,20,bytes_per_sector);
+ break;
+ }
+
+ case SMB_FS_FULL_SIZE_INFORMATION:
+ {
+ SMB_BIG_UINT dfree,dsize,bsize,block_size,sectors_per_unit,bytes_per_sector;
+ data_len = 32;
+ SMB_VFS_DISK_FREE(conn,".",False,&bsize,&dfree,&dsize);
+ block_size = lp_block_size(snum);
+ if (bsize < block_size) {
+ SMB_BIG_UINT factor = block_size/bsize;
+ bsize = block_size;
+ dsize /= factor;
+ dfree /= factor;
+ }
+ if (bsize > block_size) {
+ SMB_BIG_UINT factor = bsize/block_size;
+ bsize = block_size;
+ dsize *= factor;
+ dfree *= factor;
+ }
+ bytes_per_sector = 512;
+ sectors_per_unit = bsize/bytes_per_sector;
+ DEBUG(5,("call_trans2qfsinfo : SMB_QUERY_FS_FULL_SIZE_INFO bsize=%u, cSectorUnit=%u, \
+cBytesSector=%u, cUnitTotal=%u, cUnitAvail=%d\n", (unsigned int)bsize, (unsigned int)sectors_per_unit,
+ (unsigned int)bytes_per_sector, (unsigned int)dsize, (unsigned int)dfree));
+ SBIG_UINT(pdata,0,dsize); /* Total Allocation units. */
+ SBIG_UINT(pdata,8,dfree); /* Caller available allocation units. */
+ SBIG_UINT(pdata,16,dfree); /* Actual available allocation units. */
+ SIVAL(pdata,24,sectors_per_unit); /* Sectors per allocation unit. */
+ SIVAL(pdata,28,bytes_per_sector); /* Bytes per sector. */
+ break;
+ }
+
+ case SMB_QUERY_FS_DEVICE_INFO:
+ case SMB_FS_DEVICE_INFORMATION:
+ data_len = 8;
+ SIVAL(pdata,0,0); /* dev type */
+ SIVAL(pdata,4,0); /* characteristics */
+ break;
+
+#ifdef HAVE_SYS_QUOTAS
+ case SMB_FS_QUOTA_INFORMATION:
+ /*
+ * what we have to send --metze:
+ *
+ * Unknown1: 24 NULL bytes
+ * Soft Quota Treshold: 8 bytes seems like SMB_BIG_UINT or so
+ * Hard Quota Limit: 8 bytes seems like SMB_BIG_UINT or so
+ * Quota Flags: 2 byte :
+ * Unknown3: 6 NULL bytes
+ *
+ * 48 bytes total
+ *
+ * details for Quota Flags:
+ *
+ * 0x0020 Log Limit: log if the user exceeds his Hard Quota
+ * 0x0010 Log Warn: log if the user exceeds his Soft Quota
+ * 0x0002 Deny Disk: deny disk access when the user exceeds his Hard Quota
+ * 0x0001 Enable Quotas: enable quota for this fs
+ *
+ */
+ {
+ /* we need to fake up a fsp here,
+ * because its not send in this call
+ */
+ files_struct fsp;
+ SMB_NTQUOTA_STRUCT quotas;
+
+ ZERO_STRUCT(fsp);
+ ZERO_STRUCT(quotas);
+
+ fsp.conn = conn;
+ fsp.fnum = -1;
+ fsp.fd = -1;
+
+ /* access check */
+ if (conn->admin_user != True) {
+ DEBUG(0,("set_user_quota: access_denied service [%s] user [%s]\n",
+ lp_servicename(SNUM(conn)),conn->user));
+ return ERROR_DOS(ERRDOS,ERRnoaccess);
+ }
+
+ if (vfs_get_ntquota(&fsp, SMB_USER_FS_QUOTA_TYPE, NULL, &quotas)!=0) {
+ DEBUG(0,("vfs_get_ntquota() failed for service [%s]\n",lp_servicename(SNUM(conn))));
+ return ERROR_DOS(ERRSRV,ERRerror);
+ }
+
+ data_len = 48;
+
+ DEBUG(10,("SMB_FS_QUOTA_INFORMATION: for service [%s]\n",lp_servicename(SNUM(conn))));
+
+ /* Unknown1 24 NULL bytes*/
+ SBIG_UINT(pdata,0,(SMB_BIG_UINT)0);
+ SBIG_UINT(pdata,8,(SMB_BIG_UINT)0);
+ SBIG_UINT(pdata,16,(SMB_BIG_UINT)0);
+
+ /* Default Soft Quota 8 bytes */
+ SBIG_UINT(pdata,24,quotas.softlim);
+
+ /* Default Hard Quota 8 bytes */
+ SBIG_UINT(pdata,32,quotas.hardlim);
+
+ /* Quota flag 2 bytes */
+ SSVAL(pdata,40,quotas.qflags);
+
+ /* Unknown3 6 NULL bytes */
+ SSVAL(pdata,42,0);
+ SIVAL(pdata,44,0);
+
+ break;
+ }
+#endif /* HAVE_SYS_QUOTAS */
+ case SMB_FS_OBJECTID_INFORMATION:
+ data_len = 64;
+ break;
+
+ /*
+ * Query the version and capabilities of the CIFS UNIX extensions
+ * in use.
+ */
+
+ case SMB_QUERY_CIFS_UNIX_INFO:
+ if (!lp_unix_extensions())
+ return ERROR_DOS(ERRDOS,ERRunknownlevel);
+ data_len = 12;
+ SSVAL(pdata,0,CIFS_UNIX_MAJOR_VERSION);
+ SSVAL(pdata,2,CIFS_UNIX_MINOR_VERSION);
+ SBIG_UINT(pdata,4,((SMB_BIG_UINT)0)); /* No capabilities for now... */
+ break;
+
+ case SMB_MAC_QUERY_FS_INFO:
+ /*
+ * Thursby MAC extension... ONLY on NTFS filesystems
+ * once we do streams then we don't need this
+ */
+ if (strequal(lp_fstype(SNUM(conn)),"NTFS")) {
+ data_len = 88;
+ SIVAL(pdata,84,0x100); /* Don't support mac... */
+ break;
+ }
+ /* drop through */
+ default:
+ return ERROR_DOS(ERRDOS,ERRunknownlevel);
+ }
+
+
+ send_trans2_replies( outbuf, bufsize, params, 0, pdata, data_len);
+
+ DEBUG( 4, ( "%s info_level = %d\n", smb_fn_name(CVAL(inbuf,smb_com)), info_level) );
+
+ return -1;
+}
+
+#ifdef HAVE_SYS_QUOTAS
+/****************************************************************************
+ Reply to a TRANS2_SETFSINFO (set filesystem info).
+****************************************************************************/
+
+static int call_trans2setfsinfo(connection_struct *conn,
+ char *inbuf, char *outbuf, int length, int bufsize,
+ char **pparams, int total_params, char **ppdata, int total_data)
+{
+ char *pdata = *ppdata;
+ char *params = *pparams;
+ files_struct *fsp = NULL;
+ uint16 info_level;
+ int outsize;
+ SMB_NTQUOTA_STRUCT quotas;
+
+ ZERO_STRUCT(quotas);
+
+ DEBUG(10,("call_trans2setfsinfo: SET_FS_QUOTA: for service [%s]\n",lp_servicename(SNUM(conn))));
+
+ /* access check */
+ if ((conn->admin_user != True)||!CAN_WRITE(conn)) {
+ DEBUG(0,("set_user_quota: access_denied service [%s] user [%s]\n",
+ lp_servicename(SNUM(conn)),conn->user));
+ return ERROR_DOS(ERRSRV,ERRaccess);
+ }
+
+ /* */
+ if (total_params < 4) {
+ DEBUG(0,("call_trans2setfsinfo: requires total_params(%d) >= 4 bytes!\n",
+ total_params));
+ return ERROR_DOS(ERRDOS,ERRinvalidparam);
+ }
+
+ fsp = file_fsp(params,0);
+
+ if (!CHECK_NTQUOTA_HANDLE_OK(fsp,conn)) {
+ DEBUG(3,("TRANSACT_GET_USER_QUOTA: no valid QUOTA HANDLE\n"));
+ return ERROR_NT(NT_STATUS_INVALID_HANDLE);
+ }
+
+ info_level = SVAL(params,2);
+
+ switch(info_level) {
+ case SMB_FS_QUOTA_INFORMATION:
+ /* note: normaly there're 48 bytes,
+ * but we didn't use the last 6 bytes for now
+ * --metze
+ */
+ if (total_data < 42) {
+ DEBUG(0,("call_trans2setfsinfo: SET_FS_QUOTA: requires total_data(%d) >= 42 bytes!\n",
+ total_data));
+ return ERROR_DOS(ERRDOS,ERRunknownlevel);
+ }
+
+ /* unknown_1 24 NULL bytes in pdata*/
+
+ /* the soft quotas 8 bytes (SMB_BIG_UINT)*/
+ quotas.softlim = (SMB_BIG_UINT)IVAL(pdata,24);
+#ifdef LARGE_SMB_OFF_T
+ quotas.softlim |= (((SMB_BIG_UINT)IVAL(pdata,28)) << 32);
+#else /* LARGE_SMB_OFF_T */
+ if ((IVAL(pdata,28) != 0)&&
+ ((quotas.softlim != 0xFFFFFFFF)||
+ (IVAL(pdata,28)!=0xFFFFFFFF))) {
+ /* more than 32 bits? */
+ return ERROR_DOS(ERRDOS,ERRunknownlevel);
+ }
+#endif /* LARGE_SMB_OFF_T */
+
+ /* the hard quotas 8 bytes (SMB_BIG_UINT)*/
+ quotas.hardlim = (SMB_BIG_UINT)IVAL(pdata,32);
+#ifdef LARGE_SMB_OFF_T
+ quotas.hardlim |= (((SMB_BIG_UINT)IVAL(pdata,36)) << 32);
+#else /* LARGE_SMB_OFF_T */
+ if ((IVAL(pdata,36) != 0)&&
+ ((quotas.hardlim != 0xFFFFFFFF)||
+ (IVAL(pdata,36)!=0xFFFFFFFF))) {
+ /* more than 32 bits? */
+ return ERROR_DOS(ERRDOS,ERRunknownlevel);
+ }
+#endif /* LARGE_SMB_OFF_T */
+
+ /* quota_flags 2 bytes **/
+ quotas.qflags = SVAL(pdata,40);
+
+ /* unknown_2 6 NULL bytes follow*/
+
+ /* now set the quotas */
+ if (vfs_set_ntquota(fsp, SMB_USER_FS_QUOTA_TYPE, NULL, &quotas)!=0) {
+ DEBUG(0,("vfs_set_ntquota() failed for service [%s]\n",lp_servicename(SNUM(conn))));
+ return ERROR_DOS(ERRSRV,ERRerror);
+ }
+
+ break;
+ default:
+ DEBUG(3,("call_trans2setfsinfo: unknown level (0x%X) not implemented yet.\n",
+ info_level));
+ return ERROR_DOS(ERRDOS,ERRunknownlevel);
+ break;
+ }
+
+ /*
+ * sending this reply works fine,
+ * but I'm not sure it's the same
+ * like windows do...
+ * --metze
+ */
+ outsize = set_message(outbuf,10,0,True);
+
+ return outsize;
+}
+#endif /* HAVE_SYS_QUOTAS */
+
+/****************************************************************************
+ * Utility function to set bad path error.
+ ****************************************************************************/
+
+int set_bad_path_error(int err, BOOL bad_path, char *outbuf, int def_class, uint32 def_code)
+{
+ DEBUG(10,("set_bad_path_error: err = %d bad_path = %d\n",
+ err, (int)bad_path ));
+
+ if(err == ENOENT) {
+ if (bad_path) {
+ return ERROR_NT(NT_STATUS_OBJECT_PATH_NOT_FOUND);
+ } else {
+ return ERROR_NT(NT_STATUS_OBJECT_NAME_NOT_FOUND);
+ }
+ }
+ return UNIXERROR(def_class,def_code);
+}
+
+/****************************************************************************
+ Reply to a TRANS2_QFILEPATHINFO or TRANSACT2_QFILEINFO (query file info by
+ file name or file id).
+****************************************************************************/
+
+static int call_trans2qfilepathinfo(connection_struct *conn,
+ char *inbuf, char *outbuf, int length,
+ int bufsize,
+ char **pparams, int total_params, char **ppdata, int total_data)
+{
+ int max_data_bytes = SVAL(inbuf, smb_mdrcnt);
+ char *params = *pparams;
+ char *pdata = *ppdata;
+ uint16 tran_call = SVAL(inbuf, smb_setup0);
+ uint16 info_level;
+ int mode=0;
+ SMB_OFF_T file_size=0;
+ SMB_BIG_UINT allocation_size=0;
+ unsigned int data_size;
+ unsigned int param_size = 2;
+ SMB_STRUCT_STAT sbuf;
+ pstring fname, dos_fname;
+ char *fullpathname;
+ char *base_name;
+ char *p;
+ SMB_OFF_T pos = 0;
+ BOOL bad_path = False;
+ BOOL delete_pending = False;
+ int len;
+ time_t c_time;
+ files_struct *fsp = NULL;
+ uint32 desired_access = 0x12019F; /* Default - GENERIC_EXECUTE mapping from Windows */
+
+ if (!params)
+ return ERROR_NT(NT_STATUS_INVALID_PARAMETER);
+
+ if (tran_call == TRANSACT2_QFILEINFO) {
+ if (total_params < 4)
+ return(ERROR_DOS(ERRDOS,ERRinvalidparam));
+
+ fsp = file_fsp(params,0);
+ info_level = SVAL(params,2);
+
+ DEBUG(3,("call_trans2qfilepathinfo: TRANSACT2_QFILEINFO: level = %d\n", info_level));
+
+ if(fsp && (fsp->fake_file_handle)) {
+ /*
+ * This is actually for the QUOTA_FAKE_FILE --metze
+ */
+
+ pstrcpy(fname, fsp->fsp_name);
+ unix_convert(fname,conn,0,&bad_path,&sbuf);
+ if (!check_name(fname,conn)) {
+ DEBUG(3,("call_trans2qfilepathinfo: fileinfo of %s failed for fake_file(%s)\n",fname,strerror(errno)));
+ return set_bad_path_error(errno, bad_path, outbuf, ERRDOS,ERRbadpath);
+ }
+
+ } else if(fsp && (fsp->is_directory || fsp->fd == -1)) {
+ /*
+ * This is actually a QFILEINFO on a directory
+ * handle (returned from an NT SMB). NT5.0 seems
+ * to do this call. JRA.
+ */
+ pstrcpy(fname, fsp->fsp_name);
+ unix_convert(fname,conn,0,&bad_path,&sbuf);
+ if (!check_name(fname,conn)) {
+ DEBUG(3,("call_trans2qfilepathinfo: fileinfo of %s failed (%s)\n",fname,strerror(errno)));
+ return set_bad_path_error(errno, bad_path, outbuf, ERRDOS,ERRbadpath);
+ }
+
+ if (INFO_LEVEL_IS_UNIX(info_level)) {
+ /* Always do lstat for UNIX calls. */
+ if (SMB_VFS_LSTAT(conn,fname,&sbuf)) {
+ DEBUG(3,("call_trans2qfilepathinfo: SMB_VFS_LSTAT of %s failed (%s)\n",fname,strerror(errno)));
+ return set_bad_path_error(errno, bad_path, outbuf, ERRDOS,ERRbadpath);
+ }
+ } else if (!VALID_STAT(sbuf) && SMB_VFS_STAT(conn,fname,&sbuf)) {
+ DEBUG(3,("call_trans2qfilepathinfo: SMB_VFS_STAT of %s failed (%s)\n",fname,strerror(errno)));
+ return set_bad_path_error(errno, bad_path, outbuf, ERRDOS,ERRbadpath);
+ }
+
+ delete_pending = fsp->directory_delete_on_close;
+ } else {
+ /*
+ * Original code - this is an open file.
+ */
+ CHECK_FSP(fsp,conn);
+
+ pstrcpy(fname, fsp->fsp_name);
+ if (SMB_VFS_FSTAT(fsp,fsp->fd,&sbuf) != 0) {
+ DEBUG(3,("fstat of fnum %d failed (%s)\n", fsp->fnum, strerror(errno)));
+ return(UNIXERROR(ERRDOS,ERRbadfid));
+ }
+ pos = fsp->position_information;
+ delete_pending = fsp->delete_on_close;
+ desired_access = fsp->desired_access;
+ }
+ } else {
+ NTSTATUS status = NT_STATUS_OK;
+
+ /* qpathinfo */
+ if (total_params < 6)
+ return(ERROR_DOS(ERRDOS,ERRinvalidparam));
+
+ info_level = SVAL(params,0);
+
+ DEBUG(3,("call_trans2qfilepathinfo: TRANSACT2_QPATHINFO: level = %d\n", info_level));
+
+ srvstr_get_path(inbuf, fname, &params[6], sizeof(fname), -1, STR_TERMINATE, &status);
+ if (!NT_STATUS_IS_OK(status)) {
+ return ERROR_NT(status);
+ }
+
+ RESOLVE_DFSPATH(fname, conn, inbuf, outbuf);
+
+ unix_convert(fname,conn,0,&bad_path,&sbuf);
+ if (!check_name(fname,conn)) {
+ DEBUG(3,("call_trans2qfilepathinfo: fileinfo of %s failed (%s)\n",fname,strerror(errno)));
+ return set_bad_path_error(errno, bad_path, outbuf, ERRDOS,ERRbadpath);
+ }
+
+ if (INFO_LEVEL_IS_UNIX(info_level)) {
+ /* Always do lstat for UNIX calls. */
+ if (SMB_VFS_LSTAT(conn,fname,&sbuf)) {
+ DEBUG(3,("call_trans2qfilepathinfo: SMB_VFS_LSTAT of %s failed (%s)\n",fname,strerror(errno)));
+ return set_bad_path_error(errno, bad_path, outbuf, ERRDOS,ERRbadpath);
+ }
+ } else if (!VALID_STAT(sbuf) && SMB_VFS_STAT(conn,fname,&sbuf) && (info_level != SMB_INFO_IS_NAME_VALID)) {
+ DEBUG(3,("call_trans2qfilepathinfo: SMB_VFS_STAT of %s failed (%s)\n",fname,strerror(errno)));
+ return set_bad_path_error(errno, bad_path, outbuf, ERRDOS,ERRbadpath);
+ }
+ }
+
+ if (INFO_LEVEL_IS_UNIX(info_level) && !lp_unix_extensions())
+ return ERROR_DOS(ERRDOS,ERRunknownlevel);
+
+ DEBUG(3,("call_trans2qfilepathinfo %s (fnum = %d) level=%d call=%d total_data=%d\n",
+ fname,fsp ? fsp->fnum : -1, info_level,tran_call,total_data));
+
+ p = strrchr_m(fname,'/');
+ if (!p)
+ base_name = fname;
+ else
+ base_name = p+1;
+
+ mode = dos_mode(conn,fname,&sbuf);
+ if (!mode)
+ mode = FILE_ATTRIBUTE_NORMAL;
+
+ fullpathname = fname;
+ file_size = get_file_size(sbuf);
+ allocation_size = get_allocation_size(fsp,&sbuf);
+ if (mode & aDIR)
+ file_size = 0;
+
+ params = Realloc(*pparams,2);
+ if (params == NULL)
+ return ERROR_DOS(ERRDOS,ERRnomem);
+ *pparams = params;
+ memset((char *)params,'\0',2);
+ data_size = max_data_bytes + 1024;
+ pdata = Realloc(*ppdata, data_size);
+ if ( pdata == NULL )
+ return ERROR_DOS(ERRDOS,ERRnomem);
+ *ppdata = pdata;
+
+ if (total_data > 0 && IVAL(pdata,0) == total_data) {
+ /* uggh, EAs for OS2 */
+ DEBUG(4,("Rejecting EA request with total_data=%d\n",total_data));
+ return ERROR_DOS(ERRDOS,ERReasnotsupported);
+ }
+
+ memset((char *)pdata,'\0',data_size);
+
+ c_time = get_create_time(&sbuf,lp_fake_dir_create_times(SNUM(conn)));
+
+ if (lp_dos_filetime_resolution(SNUM(conn))) {
+ c_time &= ~1;
+ sbuf.st_atime &= ~1;
+ sbuf.st_mtime &= ~1;
+ sbuf.st_mtime &= ~1;
+ }
+
+ /* NT expects the name to be in an exact form of the *full*
+ filename. See the trans2 torture test */
+ if (strequal(base_name,".")) {
+ pstrcpy(dos_fname, "\\");
+ } else {
+ pstr_sprintf(dos_fname, "\\%s", fname);
+ string_replace(dos_fname, '/', '\\');
+ }
+
+ switch (info_level) {
+ case SMB_INFO_STANDARD:
+ case SMB_INFO_QUERY_EA_SIZE:
+ data_size = (info_level==1?22:26);
+ put_dos_date2(pdata,l1_fdateCreation,c_time);
+ put_dos_date2(pdata,l1_fdateLastAccess,sbuf.st_atime);
+ put_dos_date2(pdata,l1_fdateLastWrite,sbuf.st_mtime); /* write time */
+ SIVAL(pdata,l1_cbFile,(uint32)file_size);
+ SIVAL(pdata,l1_cbFileAlloc,(uint32)allocation_size);
+ SSVAL(pdata,l1_attrFile,mode);
+ SIVAL(pdata,l1_attrFile+2,0); /* this is what win2003 does */
+ break;
+
+ case SMB_INFO_IS_NAME_VALID:
+ if (tran_call == TRANSACT2_QFILEINFO) {
+ /* os/2 needs this ? really ?*/
+ return ERROR_DOS(ERRDOS,ERRbadfunc);
+ }
+ data_size = 0;
+ param_size = 0;
+ break;
+
+ case SMB_INFO_QUERY_EAS_FROM_LIST:
+ data_size = 24;
+ put_dos_date2(pdata,0,c_time);
+ put_dos_date2(pdata,4,sbuf.st_atime);
+ put_dos_date2(pdata,8,sbuf.st_mtime);
+ SIVAL(pdata,12,(uint32)file_size);
+ SIVAL(pdata,16,(uint32)allocation_size);
+ SIVAL(pdata,20,mode);
+ break;
+
+ case SMB_INFO_QUERY_ALL_EAS:
+ /* We have data_size bytes to put EA's into. */
+ data_size = fill_ea_buffer(pdata, data_size, conn, fsp, fname);
+ break;
+
+ case SMB_FILE_BASIC_INFORMATION:
+ case SMB_QUERY_FILE_BASIC_INFO:
+
+ if (info_level == SMB_QUERY_FILE_BASIC_INFO)
+ data_size = 36; /* w95 returns 40 bytes not 36 - why ?. */
+ else {
+ data_size = 40;
+ SIVAL(pdata,36,0);
+ }
+ put_long_date(pdata,c_time);
+ put_long_date(pdata+8,sbuf.st_atime);
+ put_long_date(pdata+16,sbuf.st_mtime); /* write time */
+ put_long_date(pdata+24,sbuf.st_mtime); /* change time */
+ SIVAL(pdata,32,mode);
+
+ DEBUG(5,("SMB_QFBI - "));
+ {
+ time_t create_time = c_time;
+ DEBUG(5,("create: %s ", ctime(&create_time)));
+ }
+ DEBUG(5,("access: %s ", ctime(&sbuf.st_atime)));
+ DEBUG(5,("write: %s ", ctime(&sbuf.st_mtime)));
+ DEBUG(5,("change: %s ", ctime(&sbuf.st_mtime)));
+ DEBUG(5,("mode: %x\n", mode));
+
+ break;
+
+ case SMB_FILE_STANDARD_INFORMATION:
+ case SMB_QUERY_FILE_STANDARD_INFO:
+
+ data_size = 24;
+ SOFF_T(pdata,0,allocation_size);
+ SOFF_T(pdata,8,file_size);
+ if (delete_pending & sbuf.st_nlink)
+ SIVAL(pdata,16,sbuf.st_nlink - 1);
+ else
+ SIVAL(pdata,16,sbuf.st_nlink);
+ SCVAL(pdata,20,0);
+ SCVAL(pdata,21,(mode&aDIR)?1:0);
+ break;
+
+ case SMB_FILE_EA_INFORMATION:
+ case SMB_QUERY_FILE_EA_INFO:
+ {
+ unsigned int ea_size = estimate_ea_size(conn, fsp, fname);
+ data_size = 4;
+ SIVAL(pdata,0,ea_size);
+ break;
+ }
+
+ /* Get the 8.3 name - used if NT SMB was negotiated. */
+ case SMB_QUERY_FILE_ALT_NAME_INFO:
+ case SMB_FILE_ALTERNATE_NAME_INFORMATION:
+ {
+ pstring short_name;
+
+ pstrcpy(short_name,base_name);
+ /* Mangle if not already 8.3 */
+ if(!mangle_is_8_3(short_name, True)) {
+ mangle_map(short_name,True,True,SNUM(conn));
+ }
+ len = srvstr_push(outbuf, pdata+4, short_name, -1, STR_UNICODE);
+ data_size = 4 + len;
+ SIVAL(pdata,0,len);
+ break;
+ }
+
+ case SMB_QUERY_FILE_NAME_INFO:
+ /*
+ this must be *exactly* right for ACLs on mapped drives to work
+ */
+ len = srvstr_push(outbuf, pdata+4, dos_fname, -1, STR_UNICODE);
+ data_size = 4 + len;
+ SIVAL(pdata,0,len);
+ break;
+
+ case SMB_FILE_ALLOCATION_INFORMATION:
+ case SMB_QUERY_FILE_ALLOCATION_INFO:
+ data_size = 8;
+ SOFF_T(pdata,0,allocation_size);
+ break;
+
+ case SMB_FILE_END_OF_FILE_INFORMATION:
+ case SMB_QUERY_FILE_END_OF_FILEINFO:
+ data_size = 8;
+ SOFF_T(pdata,0,file_size);
+ break;
+
+ case SMB_QUERY_FILE_ALL_INFO:
+ case SMB_FILE_ALL_INFORMATION:
+ put_long_date(pdata,c_time);
+ put_long_date(pdata+8,sbuf.st_atime);
+ put_long_date(pdata+16,sbuf.st_mtime); /* write time */
+ put_long_date(pdata+24,sbuf.st_mtime); /* change time */
+ SIVAL(pdata,32,mode);
+ pdata += 40;
+ SOFF_T(pdata,0,allocation_size);
+ SOFF_T(pdata,8,file_size);
+ if (delete_pending && sbuf.st_nlink)
+ SIVAL(pdata,16,sbuf.st_nlink - 1);
+ else
+ SIVAL(pdata,16,sbuf.st_nlink);
+ SCVAL(pdata,20,delete_pending);
+ SCVAL(pdata,21,(mode&aDIR)?1:0);
+ pdata += 24;
+ pdata += 4; /* EA info */
+ len = srvstr_push(outbuf, pdata+4, dos_fname, -1, STR_UNICODE);
+ SIVAL(pdata,0,len);
+ pdata += 4 + len;
+ data_size = PTR_DIFF(pdata,(*ppdata));
+ break;
+
+ case SMB_FILE_INTERNAL_INFORMATION:
+ /* This should be an index number - looks like
+ dev/ino to me :-)
+
+ I think this causes us to fail the IFSKIT
+ BasicFileInformationTest. -tpot */
+
+ SIVAL(pdata,0,sbuf.st_dev);
+ SIVAL(pdata,4,sbuf.st_ino);
+ data_size = 8;
+ break;
+
+ case SMB_FILE_ACCESS_INFORMATION:
+ SIVAL(pdata,0,desired_access);
+ data_size = 4;
+ break;
+
+ case SMB_FILE_NAME_INFORMATION:
+ /* Pathname with leading '\'. */
+ {
+ size_t byte_len;
+ byte_len = dos_PutUniCode(pdata+4,dos_fname,max_data_bytes,False);
+ SIVAL(pdata,0,byte_len);
+ data_size = 4 + byte_len;
+ break;
+ }
+
+ case SMB_FILE_DISPOSITION_INFORMATION:
+ data_size = 1;
+ SCVAL(pdata,0,delete_pending);
+ break;
+
+ case SMB_FILE_POSITION_INFORMATION:
+ data_size = 8;
+ SOFF_T(pdata,0,pos);
+ break;
+
+ case SMB_FILE_MODE_INFORMATION:
+ SIVAL(pdata,0,mode);
+ data_size = 4;
+ break;
+
+ case SMB_FILE_ALIGNMENT_INFORMATION:
+ SIVAL(pdata,0,0); /* No alignment needed. */
+ data_size = 4;
+ break;
+
+#if 0
+ /*
+ * NT4 server just returns "invalid query" to this - if we try to answer
+ * it then NTws gets a BSOD! (tridge).
+ * W2K seems to want this. JRA.
+ */
+ case SMB_QUERY_FILE_STREAM_INFO:
+#endif
+ case SMB_FILE_STREAM_INFORMATION:
+ if (mode & aDIR) {
+ data_size = 0;
+ } else {
+ size_t byte_len = dos_PutUniCode(pdata+24,"::$DATA", 0xE, False);
+ SIVAL(pdata,0,0); /* ??? */
+ SIVAL(pdata,4,byte_len); /* Byte length of unicode string ::$DATA */
+ SOFF_T(pdata,8,file_size);
+ SIVAL(pdata,16,allocation_size);
+ SIVAL(pdata,20,0); /* ??? */
+ data_size = 24 + byte_len;
+ }
+ break;
+
+ case SMB_QUERY_COMPRESSION_INFO:
+ case SMB_FILE_COMPRESSION_INFORMATION:
+ SOFF_T(pdata,0,file_size);
+ SIVAL(pdata,8,0); /* ??? */
+ SIVAL(pdata,12,0); /* ??? */
+ data_size = 16;
+ break;
+
+ case SMB_FILE_NETWORK_OPEN_INFORMATION:
+ put_long_date(pdata,c_time);
+ put_long_date(pdata+8,sbuf.st_atime);
+ put_long_date(pdata+16,sbuf.st_mtime); /* write time */
+ put_long_date(pdata+24,sbuf.st_mtime); /* change time */
+ SIVAL(pdata,32,allocation_size);
+ SOFF_T(pdata,40,file_size);
+ SIVAL(pdata,48,mode);
+ SIVAL(pdata,52,0); /* ??? */
+ data_size = 56;
+ break;
+
+ case SMB_FILE_ATTRIBUTE_TAG_INFORMATION:
+ SIVAL(pdata,0,mode);
+ SIVAL(pdata,4,0);
+ data_size = 8;
+ break;
+
+ /*
+ * CIFS UNIX Extensions.
+ */
+
+ case SMB_QUERY_FILE_UNIX_BASIC:
+
+ DEBUG(4,("call_trans2qfilepathinfo: st_mode=%o\n",(int)sbuf.st_mode));
+
+ SOFF_T(pdata,0,get_file_size(sbuf)); /* File size 64 Bit */
+ pdata += 8;
+
+ SOFF_T(pdata,0,get_allocation_size(fsp,&sbuf)); /* Number of bytes used on disk - 64 Bit */
+ pdata += 8;
+
+ put_long_date(pdata,sbuf.st_ctime); /* Creation Time 64 Bit */
+ put_long_date(pdata+8,sbuf.st_atime); /* Last access time 64 Bit */
+ put_long_date(pdata+16,sbuf.st_mtime); /* Last modification time 64 Bit */
+ pdata += 24;
+
+ SIVAL(pdata,0,sbuf.st_uid); /* user id for the owner */
+ SIVAL(pdata,4,0);
+ pdata += 8;
+
+ SIVAL(pdata,0,sbuf.st_gid); /* group id of owner */
+ SIVAL(pdata,4,0);
+ pdata += 8;
+
+ SIVAL(pdata,0,unix_filetype(sbuf.st_mode));
+ pdata += 4;
+
+ SIVAL(pdata,0,unix_dev_major(sbuf.st_rdev)); /* Major device number if type is device */
+ SIVAL(pdata,4,0);
+ pdata += 8;
+
+ SIVAL(pdata,0,unix_dev_minor(sbuf.st_rdev)); /* Minor device number if type is device */
+ SIVAL(pdata,4,0);
+ pdata += 8;
+
+ SINO_T(pdata,0,(SMB_INO_T)sbuf.st_ino); /* inode number */
+ pdata += 8;
+
+ SIVAL(pdata,0, unix_perms_to_wire(sbuf.st_mode)); /* Standard UNIX file permissions */
+ SIVAL(pdata,4,0);
+ pdata += 8;
+
+ SIVAL(pdata,0,sbuf.st_nlink); /* number of hard links */
+ SIVAL(pdata,4,0);
+ pdata += 8+1;
+ data_size = PTR_DIFF(pdata,(*ppdata));
+
+ {
+ int i;
+ DEBUG(4,("call_trans2qfilepathinfo: SMB_QUERY_FILE_UNIX_BASIC"));
+
+ for (i=0; i<100; i++)
+ DEBUG(4,("%d=%x, ",i, (*ppdata)[i]));
+ DEBUG(4,("\n"));
+ }
+
+ break;
+
+ case SMB_QUERY_FILE_UNIX_LINK:
+ {
+ pstring buffer;
+
+#ifdef S_ISLNK
+ if(!S_ISLNK(sbuf.st_mode))
+ return(UNIXERROR(ERRSRV,ERRbadlink));
+#else
+ return(UNIXERROR(ERRDOS,ERRbadlink));
+#endif
+ len = SMB_VFS_READLINK(conn,fullpathname, buffer, sizeof(pstring)-1); /* read link */
+ if (len == -1)
+ return(UNIXERROR(ERRDOS,ERRnoaccess));
+ buffer[len] = 0;
+ len = srvstr_push(outbuf, pdata, buffer, -1, STR_TERMINATE);
+ pdata += len;
+ data_size = PTR_DIFF(pdata,(*ppdata));
+
+ break;
+ }
+
+ default:
+ return ERROR_DOS(ERRDOS,ERRunknownlevel);
+ }
+
+ send_trans2_replies(outbuf, bufsize, params, param_size, *ppdata, data_size);
+
+ return(-1);
+}
+
+/****************************************************************************
+ Deal with the internal needs of setting the delete on close flag. Note that
+ as the tdb locking is recursive, it is safe to call this from within
+ open_file_shared. JRA.
+****************************************************************************/
+
+NTSTATUS set_delete_on_close_internal(files_struct *fsp, BOOL delete_on_close)
+{
+ /*
+ * Only allow delete on close for writable shares.
+ */
+
+ if (delete_on_close && !CAN_WRITE(fsp->conn)) {
+ DEBUG(10,("set_delete_on_close_internal: file %s delete on close flag set but write access denied on share.\n",
+ fsp->fsp_name ));
+ return NT_STATUS_ACCESS_DENIED;
+ }
+ /*
+ * Only allow delete on close for files/directories opened with delete intent.
+ */
+
+ if (delete_on_close && !(fsp->desired_access & DELETE_ACCESS)) {
+ DEBUG(10,("set_delete_on_close_internal: file %s delete on close flag set but delete access denied.\n",
+ fsp->fsp_name ));
+ return NT_STATUS_ACCESS_DENIED;
+ }
+
+ if(fsp->is_directory) {
+ fsp->directory_delete_on_close = delete_on_close;
+ DEBUG(10, ("set_delete_on_close_internal: %s delete on close flag for fnum = %d, directory %s\n",
+ delete_on_close ? "Added" : "Removed", fsp->fnum, fsp->fsp_name ));
+ } else {
+ fsp->delete_on_close = delete_on_close;
+ DEBUG(10, ("set_delete_on_close_internal: %s delete on close flag for fnum = %d, file %s\n",
+ delete_on_close ? "Added" : "Removed", fsp->fnum, fsp->fsp_name ));
+ }
+
+ return NT_STATUS_OK;
+}
+
+/****************************************************************************
+ Sets the delete on close flag over all share modes on this file.
+ Modify the share mode entry for all files open
+ on this device and inode to tell other smbds we have
+ changed the delete on close flag. This will be noticed
+ in the close code, the last closer will delete the file
+ if flag is set.
+****************************************************************************/
+
+NTSTATUS set_delete_on_close_over_all(files_struct *fsp, BOOL delete_on_close)
+{
+ DEBUG(10,("set_delete_on_close_over_all: %s delete on close flag for fnum = %d, file %s\n",
+ delete_on_close ? "Adding" : "Removing", fsp->fnum, fsp->fsp_name ));
+
+ if (fsp->is_directory || fsp->is_stat)
+ return NT_STATUS_OK;
+
+ if (lock_share_entry_fsp(fsp) == False)
+ return NT_STATUS_ACCESS_DENIED;
+
+ if (!modify_delete_flag(fsp->dev, fsp->inode, delete_on_close)) {
+ DEBUG(0,("set_delete_on_close_internal: failed to change delete on close flag for file %s\n",
+ fsp->fsp_name ));
+ unlock_share_entry_fsp(fsp);
+ return NT_STATUS_ACCESS_DENIED;
+ }
+
+ unlock_share_entry_fsp(fsp);
+ return NT_STATUS_OK;
+}
+
+/****************************************************************************
+ Returns true if this pathname is within the share, and thus safe.
+****************************************************************************/
+
+static int ensure_link_is_safe(connection_struct *conn, const char *link_dest_in, char *link_dest_out)
+{
+#ifdef PATH_MAX
+ char resolved_name[PATH_MAX+1];
+#else
+ pstring resolved_name;
+#endif
+ fstring last_component;
+ pstring link_dest;
+ pstring link_test;
+ char *p;
+ BOOL bad_path = False;
+ SMB_STRUCT_STAT sbuf;
+
+ pstrcpy(link_dest, link_dest_in);
+ unix_convert(link_dest,conn,0,&bad_path,&sbuf);
+
+ /* Store the UNIX converted path. */
+ pstrcpy(link_dest_out, link_dest);
+
+ p = strrchr(link_dest, '/');
+ if (p) {
+ fstrcpy(last_component, p+1);
+ *p = '\0';
+ } else {
+ fstrcpy(last_component, link_dest);
+ pstrcpy(link_dest, "./");
+ }
+
+ if (SMB_VFS_REALPATH(conn,link_dest,resolved_name) == NULL)
+ return -1;
+
+ pstrcpy(link_dest, resolved_name);
+ pstrcat(link_dest, "/");
+ pstrcat(link_dest, last_component);
+
+ if (*link_dest != '/') {
+ /* Relative path. */
+ pstrcpy(link_test, conn->connectpath);
+ pstrcat(link_test, "/");
+ pstrcat(link_test, link_dest);
+ } else {
+ pstrcpy(link_test, link_dest);
+ }
+
+ /*
+ * Check if the link is within the share.
+ */
+
+ if (strncmp(conn->connectpath, link_test, strlen(conn->connectpath))) {
+ errno = EACCES;
+ return -1;
+ }
+ return 0;
+}
+
+/****************************************************************************
+ Set a hard link (called by UNIX extensions and by NT rename with HARD link
+ code.
+****************************************************************************/
+
+NTSTATUS hardlink_internals(connection_struct *conn, char *name, char *newname)
+{
+ BOOL bad_path_src = False;
+ BOOL bad_path_dest = False;
+ SMB_STRUCT_STAT sbuf1, sbuf2;
+ BOOL rc, rcdest;
+ pstring last_component_src;
+ pstring last_component_dest;
+ NTSTATUS status = NT_STATUS_OK;
+
+ ZERO_STRUCT(sbuf1);
+ ZERO_STRUCT(sbuf2);
+
+ /* No wildcards. */
+ if (ms_has_wild(name) || ms_has_wild(newname)) {
+ return NT_STATUS_OBJECT_PATH_SYNTAX_BAD;
+ }
+
+ rc = unix_convert(name,conn,last_component_src,&bad_path_src,&sbuf1);
+ if (!rc && bad_path_src) {
+ return NT_STATUS_OBJECT_PATH_NOT_FOUND;
+ }
+
+ /* Quick check for "." and ".." */
+ if (last_component_src[0] == '.') {
+ if (!last_component_src[1] || (last_component_src[1] == '.' && !last_component_src[2])) {
+ return NT_STATUS_OBJECT_NAME_INVALID;
+ }
+ }
+
+ /* source must already exist. */
+ if (!VALID_STAT(sbuf1)) {
+ return NT_STATUS_OBJECT_NAME_NOT_FOUND;
+ }
+
+ rcdest = unix_convert(newname,conn,last_component_dest,&bad_path_dest,&sbuf2);
+ if (!rcdest && bad_path_dest) {
+ return NT_STATUS_OBJECT_PATH_NOT_FOUND;
+ }
+
+ /* Quick check for "." and ".." */
+ if (last_component_dest[0] == '.') {
+ if (!last_component_dest[1] || (last_component_dest[1] == '.' && !last_component_dest[2])) {
+ return NT_STATUS_OBJECT_NAME_INVALID;
+ }
+ }
+
+ /* Disallow if already exists. */
+ if (VALID_STAT(sbuf2)) {
+ return NT_STATUS_OBJECT_NAME_COLLISION;
+ }
+
+ /* No links from a directory. */
+ if (S_ISDIR(sbuf1.st_mode)) {
+ return NT_STATUS_FILE_IS_A_DIRECTORY;
+ }
+
+ if (ensure_link_is_safe(conn, newname, newname) != 0)
+ return NT_STATUS_ACCESS_DENIED;
+
+ DEBUG(10,("hardlink_internals: doing hard link %s -> %s\n", name, newname ));
+
+ if (SMB_VFS_LINK(conn,name,newname) != 0) {
+ status = map_nt_error_from_unix(errno);
+ DEBUG(3,("hardlink_internals: Error %s link %s -> %s\n",
+ nt_errstr(status), name,newname));
+ }
+
+ return status;
+}
+
+/****************************************************************************
+ Reply to a TRANS2_SETFILEINFO (set file info by fileid).
+****************************************************************************/
+
+static int call_trans2setfilepathinfo(connection_struct *conn,
+ char *inbuf, char *outbuf, int length, int bufsize,
+ char **pparams, int total_params, char **ppdata, int total_data)
+{
+ char *params = *pparams;
+ char *pdata = *ppdata;
+ uint16 tran_call = SVAL(inbuf, smb_setup0);
+ uint16 info_level;
+ int dosmode=0;
+ SMB_OFF_T size=0;
+ struct utimbuf tvs;
+ SMB_STRUCT_STAT sbuf;
+ pstring fname;
+ int fd = -1;
+ BOOL bad_path = False;
+ files_struct *fsp = NULL;
+ uid_t set_owner = (uid_t)SMB_UID_NO_CHANGE;
+ gid_t set_grp = (uid_t)SMB_GID_NO_CHANGE;
+ mode_t unixmode = 0;
+ NTSTATUS status = NT_STATUS_OK;
+
+ if (!params)
+ return ERROR_NT(NT_STATUS_INVALID_PARAMETER);
+
+ if (tran_call == TRANSACT2_SETFILEINFO) {
+ if (total_params < 4)
+ return(ERROR_DOS(ERRDOS,ERRinvalidparam));
+
+ fsp = file_fsp(params,0);
+ info_level = SVAL(params,2);
+
+ if(fsp && (fsp->is_directory || fsp->fd == -1)) {
+ /*
+ * This is actually a SETFILEINFO on a directory
+ * handle (returned from an NT SMB). NT5.0 seems
+ * to do this call. JRA.
+ */
+ pstrcpy(fname, fsp->fsp_name);
+ unix_convert(fname,conn,0,&bad_path,&sbuf);
+ if (!check_name(fname,conn) || (!VALID_STAT(sbuf))) {
+ DEBUG(3,("call_trans2setfilepathinfo: fileinfo of %s failed (%s)\n",fname,strerror(errno)));
+ return set_bad_path_error(errno, bad_path, outbuf, ERRDOS,ERRbadpath);
+ }
+ } else if (fsp && fsp->print_file) {
+ /*
+ * Doing a DELETE_ON_CLOSE should cancel a print job.
+ */
+ if ((info_level == SMB_SET_FILE_DISPOSITION_INFO) && CVAL(pdata,0)) {
+ fsp->share_mode = FILE_DELETE_ON_CLOSE;
+
+ DEBUG(3,("call_trans2setfilepathinfo: Cancelling print job (%s)\n", fsp->fsp_name ));
+
+ SSVAL(params,0,0);
+ send_trans2_replies(outbuf, bufsize, params, 2, *ppdata, 0);
+ return(-1);
+ } else
+ return (UNIXERROR(ERRDOS,ERRbadpath));
+ } else {
+ /*
+ * Original code - this is an open file.
+ */
+ CHECK_FSP(fsp,conn);
+
+ pstrcpy(fname, fsp->fsp_name);
+ fd = fsp->fd;
+
+ if (SMB_VFS_FSTAT(fsp,fd,&sbuf) != 0) {
+ DEBUG(3,("call_trans2setfilepathinfo: fstat of fnum %d failed (%s)\n",fsp->fnum, strerror(errno)));
+ return(UNIXERROR(ERRDOS,ERRbadfid));
+ }
+ }
+ } else {
+ /* set path info */
+ if (total_params < 6)
+ return(ERROR_DOS(ERRDOS,ERRinvalidparam));
+
+ info_level = SVAL(params,0);
+ srvstr_get_path(inbuf, fname, &params[6], sizeof(fname), -1, STR_TERMINATE, &status);
+ if (!NT_STATUS_IS_OK(status)) {
+ return ERROR_NT(status);
+ }
+ unix_convert(fname,conn,0,&bad_path,&sbuf);
+
+ /*
+ * For CIFS UNIX extensions the target name may not exist.
+ */
+
+ if(!VALID_STAT(sbuf) && !INFO_LEVEL_IS_UNIX(info_level)) {
+ DEBUG(3,("call_trans2setfilepathinfo: stat of %s failed (%s)\n", fname, strerror(errno)));
+ return set_bad_path_error(errno, bad_path, outbuf, ERRDOS,ERRbadpath);
+ }
+
+ if(!check_name(fname, conn)) {
+ return set_bad_path_error(errno, bad_path, outbuf, ERRDOS,ERRbadpath);
+ }
+
+ }
+
+ if (!CAN_WRITE(conn))
+ return ERROR_DOS(ERRSRV,ERRaccess);
+
+ if (INFO_LEVEL_IS_UNIX(info_level) && !lp_unix_extensions())
+ return ERROR_DOS(ERRDOS,ERRunknownlevel);
+
+ if (VALID_STAT(sbuf))
+ unixmode = sbuf.st_mode;
+
+ DEBUG(3,("call_trans2setfilepathinfo(%d) %s (fnum %d) info_level=%d totdata=%d\n",
+ tran_call,fname, fsp ? fsp->fnum : -1, info_level,total_data));
+
+ /* Realloc the parameter and data sizes */
+ params = Realloc(*pparams,2);
+ if(params == NULL)
+ return ERROR_DOS(ERRDOS,ERRnomem);
+ *pparams = params;
+
+ SSVAL(params,0,0);
+
+ if (fsp) {
+ /* the pending modtime overrides the current modtime */
+ sbuf.st_mtime = fsp->pending_modtime;
+ }
+
+ size = get_file_size(sbuf);
+ tvs.modtime = sbuf.st_mtime;
+ tvs.actime = sbuf.st_atime;
+ dosmode = dos_mode(conn,fname,&sbuf);
+ unixmode = sbuf.st_mode;
+
+ set_owner = VALID_STAT(sbuf) ? sbuf.st_uid : (uid_t)SMB_UID_NO_CHANGE;
+ set_grp = VALID_STAT(sbuf) ? sbuf.st_gid : (gid_t)SMB_GID_NO_CHANGE;
+
+ switch (info_level) {
+ case SMB_INFO_STANDARD:
+ {
+ if (total_data < 12)
+ return(ERROR_DOS(ERRDOS,ERRinvalidparam));
+
+ /* access time */
+ tvs.actime = make_unix_date2(pdata+l1_fdateLastAccess);
+ /* write time */
+ tvs.modtime = make_unix_date2(pdata+l1_fdateLastWrite);
+ break;
+ }
+
+ case SMB_INFO_SET_EA:
+ status = set_ea(conn, fsp, fname, pdata, total_data);
+ if (NT_STATUS_V(status) != NT_STATUS_V(NT_STATUS_OK))
+ return ERROR_NT(status);
+ break;
+
+ /* XXXX um, i don't think this is right.
+ it's also not in the cifs6.txt spec.
+ */
+ case SMB_INFO_QUERY_EAS_FROM_LIST:
+ if (total_data < 28)
+ return(ERROR_DOS(ERRDOS,ERRinvalidparam));
+
+ tvs.actime = make_unix_date2(pdata+8);
+ tvs.modtime = make_unix_date2(pdata+12);
+ size = IVAL(pdata,16);
+ dosmode = IVAL(pdata,24);
+ break;
+
+ /* XXXX nor this. not in cifs6.txt, either. */
+ case SMB_INFO_QUERY_ALL_EAS:
+ if (total_data < 28)
+ return(ERROR_DOS(ERRDOS,ERRinvalidparam));
+
+ tvs.actime = make_unix_date2(pdata+8);
+ tvs.modtime = make_unix_date2(pdata+12);
+ size = IVAL(pdata,16);
+ dosmode = IVAL(pdata,24);
+ break;
+
+ case SMB_SET_FILE_BASIC_INFO:
+ case SMB_FILE_BASIC_INFORMATION:
+ {
+ /* Patch to do this correctly from Paul Eggert <eggert@twinsun.com>. */
+ time_t write_time;
+ time_t changed_time;
+
+ if (total_data < 36)
+ return(ERROR_DOS(ERRDOS,ERRinvalidparam));
+
+ /* Ignore create time at offset pdata. */
+
+ /* access time */
+ tvs.actime = interpret_long_date(pdata+8);
+
+ write_time = interpret_long_date(pdata+16);
+ changed_time = interpret_long_date(pdata+24);
+
+ tvs.modtime = MIN(write_time, changed_time);
+
+ if (write_time > tvs.modtime && write_time != 0xffffffff) {
+ tvs.modtime = write_time;
+ }
+ /* Prefer a defined time to an undefined one. */
+ if (tvs.modtime == (time_t)0 || tvs.modtime == (time_t)-1)
+ tvs.modtime = (write_time == (time_t)0 || write_time == (time_t)-1
+ ? changed_time : write_time);
+
+ /* attributes */
+ dosmode = IVAL(pdata,32);
+ break;
+ }
+
+ case SMB_FILE_ALLOCATION_INFORMATION:
+ case SMB_SET_FILE_ALLOCATION_INFO:
+ {
+ int ret = -1;
+ SMB_BIG_UINT allocation_size;
+
+ if (total_data < 8)
+ return(ERROR_DOS(ERRDOS,ERRinvalidparam));
+
+ allocation_size = (SMB_BIG_UINT)IVAL(pdata,0);
+#ifdef LARGE_SMB_OFF_T
+ allocation_size |= (((SMB_BIG_UINT)IVAL(pdata,4)) << 32);
+#else /* LARGE_SMB_OFF_T */
+ if (IVAL(pdata,4) != 0) /* more than 32 bits? */
+ return ERROR_DOS(ERRDOS,ERRunknownlevel);
+#endif /* LARGE_SMB_OFF_T */
+ DEBUG(10,("call_trans2setfilepathinfo: Set file allocation info for file %s to %.0f\n",
+ fname, (double)allocation_size ));
+
+ if (allocation_size)
+ allocation_size = SMB_ROUNDUP(allocation_size,SMB_ROUNDUP_ALLOCATION_SIZE);
+
+ if(allocation_size != get_file_size(sbuf)) {
+ SMB_STRUCT_STAT new_sbuf;
+
+ DEBUG(10,("call_trans2setfilepathinfo: file %s : setting new allocation size to %.0f\n",
+ fname, (double)allocation_size ));
+
+ if (fd == -1) {
+ files_struct *new_fsp = NULL;
+ int access_mode = 0;
+ int action = 0;
+
+ if(global_oplock_break) {
+ /* Queue this file modify as we are the process of an oplock break. */
+
+ DEBUG(2,("call_trans2setfilepathinfo: queueing message due to being "));
+ DEBUGADD(2,( "in oplock break state.\n"));
+
+ push_oplock_pending_smb_message(inbuf, length);
+ return -1;
+ }
+
+ new_fsp = open_file_shared1(conn, fname, &sbuf,FILE_WRITE_DATA,
+ SET_OPEN_MODE(DOS_OPEN_RDWR),
+ (FILE_FAIL_IF_NOT_EXIST|FILE_EXISTS_OPEN),
+ FILE_ATTRIBUTE_NORMAL,
+ 0, &access_mode, &action);
+
+ if (new_fsp == NULL)
+ return(UNIXERROR(ERRDOS,ERRbadpath));
+ ret = vfs_allocate_file_space(new_fsp, allocation_size);
+ if (SMB_VFS_FSTAT(new_fsp,new_fsp->fd,&new_sbuf) != 0) {
+ DEBUG(3,("call_trans2setfilepathinfo: fstat of fnum %d failed (%s)\n",
+ new_fsp->fnum, strerror(errno)));
+ ret = -1;
+ }
+ close_file(new_fsp,True);
+ } else {
+ ret = vfs_allocate_file_space(fsp, allocation_size);
+ if (SMB_VFS_FSTAT(fsp,fd,&new_sbuf) != 0) {
+ DEBUG(3,("call_trans2setfilepathinfo: fstat of fnum %d failed (%s)\n",
+ fsp->fnum, strerror(errno)));
+ ret = -1;
+ }
+ }
+ if (ret == -1)
+ return ERROR_NT(NT_STATUS_DISK_FULL);
+
+ /* Allocate can truncate size... */
+ size = get_file_size(new_sbuf);
+ }
+
+ break;
+ }
+
+ case SMB_FILE_END_OF_FILE_INFORMATION:
+ case SMB_SET_FILE_END_OF_FILE_INFO:
+ {
+ if (total_data < 8)
+ return(ERROR_DOS(ERRDOS,ERRinvalidparam));
+
+ size = IVAL(pdata,0);
+#ifdef LARGE_SMB_OFF_T
+ size |= (((SMB_OFF_T)IVAL(pdata,4)) << 32);
+#else /* LARGE_SMB_OFF_T */
+ if (IVAL(pdata,4) != 0) /* more than 32 bits? */
+ return ERROR_DOS(ERRDOS,ERRunknownlevel);
+#endif /* LARGE_SMB_OFF_T */
+ DEBUG(10,("call_trans2setfilepathinfo: Set end of file info for file %s to %.0f\n", fname, (double)size ));
+ break;
+ }
+
+ case SMB_FILE_DISPOSITION_INFORMATION:
+ case SMB_SET_FILE_DISPOSITION_INFO: /* Set delete on close for open file. */
+ {
+ BOOL delete_on_close;
+
+ if (total_data < 1)
+ return(ERROR_DOS(ERRDOS,ERRinvalidparam));
+
+ delete_on_close = (CVAL(pdata,0) ? True : False);
+
+ /* Just ignore this set on a path. */
+ if (tran_call != TRANSACT2_SETFILEINFO)
+ break;
+
+ if (fsp == NULL)
+ return(UNIXERROR(ERRDOS,ERRbadfid));
+
+ status = set_delete_on_close_internal(fsp, delete_on_close);
+
+ if (NT_STATUS_V(status) != NT_STATUS_V(NT_STATUS_OK))
+ return ERROR_NT(status);
+
+ /* The set is across all open files on this dev/inode pair. */
+ status =set_delete_on_close_over_all(fsp, delete_on_close);
+ if (NT_STATUS_V(status) != NT_STATUS_V(NT_STATUS_OK))
+ return ERROR_NT(status);
+
+ break;
+ }
+
+ case SMB_FILE_POSITION_INFORMATION:
+ {
+ SMB_BIG_UINT position_information;
+
+ if (total_data < 8)
+ return(ERROR_DOS(ERRDOS,ERRinvalidparam));
+
+ position_information = (SMB_BIG_UINT)IVAL(pdata,0);
+#ifdef LARGE_SMB_OFF_T
+ position_information |= (((SMB_BIG_UINT)IVAL(pdata,4)) << 32);
+#else /* LARGE_SMB_OFF_T */
+ if (IVAL(pdata,4) != 0) /* more than 32 bits? */
+ return ERROR_DOS(ERRDOS,ERRunknownlevel);
+#endif /* LARGE_SMB_OFF_T */
+ DEBUG(10,("call_trans2setfilepathinfo: Set file position information for file %s to %.0f\n",
+ fname, (double)position_information ));
+ if (fsp)
+ fsp->position_information = position_information;
+ break;
+ }
+
+ /*
+ * CIFS UNIX extensions.
+ */
+
+ case SMB_SET_FILE_UNIX_BASIC:
+ {
+ uint32 raw_unixmode;
+
+ if (total_data < 100)
+ return(ERROR_DOS(ERRDOS,ERRinvalidparam));
+
+ if(IVAL(pdata, 0) != SMB_SIZE_NO_CHANGE_LO &&
+ IVAL(pdata, 4) != SMB_SIZE_NO_CHANGE_HI) {
+ size=IVAL(pdata,0); /* first 8 Bytes are size */
+#ifdef LARGE_SMB_OFF_T
+ size |= (((SMB_OFF_T)IVAL(pdata,4)) << 32);
+#else /* LARGE_SMB_OFF_T */
+ if (IVAL(pdata,4) != 0) /* more than 32 bits? */
+ return ERROR_DOS(ERRDOS,ERRunknownlevel);
+#endif /* LARGE_SMB_OFF_T */
+ }
+ pdata+=24; /* ctime & st_blocks are not changed */
+ tvs.actime = interpret_long_unix_date(pdata); /* access_time */
+ tvs.modtime = interpret_long_unix_date(pdata+8); /* modification_time */
+ pdata+=16;
+ set_owner = (uid_t)IVAL(pdata,0);
+ pdata += 8;
+ set_grp = (gid_t)IVAL(pdata,0);
+ pdata += 8;
+ raw_unixmode = IVAL(pdata,28);
+ unixmode = unix_perms_from_wire(conn, &sbuf, raw_unixmode);
+ dosmode = 0; /* Ensure dos mode change doesn't override this. */
+
+ DEBUG(10,("call_trans2setfilepathinfo: SMB_SET_FILE_UNIX_BASIC: name = %s \
+size = %.0f, uid = %u, gid = %u, raw perms = 0%o\n",
+ fname, (double)size, (unsigned int)set_owner, (unsigned int)set_grp, (int)raw_unixmode));
+
+ if (!VALID_STAT(sbuf)) {
+
+ /*
+ * The only valid use of this is to create character and block
+ * devices, and named pipes. This is deprecated (IMHO) and
+ * a new info level should be used for mknod. JRA.
+ */
+
+#if !defined(HAVE_MAKEDEV_FN)
+ return(ERROR_DOS(ERRDOS,ERRnoaccess));
+#else /* HAVE_MAKEDEV_FN */
+ uint32 file_type = IVAL(pdata,0);
+ uint32 dev_major = IVAL(pdata,4);
+ uint32 dev_minor = IVAL(pdata,12);
+
+ uid_t myuid = geteuid();
+ gid_t mygid = getegid();
+ SMB_DEV_T dev;
+
+ if (tran_call == TRANSACT2_SETFILEINFO)
+ return(ERROR_DOS(ERRDOS,ERRnoaccess));
+
+ if (raw_unixmode == SMB_MODE_NO_CHANGE)
+ return(ERROR_DOS(ERRDOS,ERRinvalidparam));
+
+ dev = makedev(dev_major, dev_minor);
+
+ /* We can only create as the owner/group we are. */
+
+ if ((set_owner != myuid) && (set_owner != (uid_t)SMB_UID_NO_CHANGE))
+ return(ERROR_DOS(ERRDOS,ERRnoaccess));
+ if ((set_grp != mygid) && (set_grp != (gid_t)SMB_GID_NO_CHANGE))
+ return(ERROR_DOS(ERRDOS,ERRnoaccess));
+
+ if (file_type != UNIX_TYPE_CHARDEV && file_type != UNIX_TYPE_BLKDEV &&
+ file_type != UNIX_TYPE_FIFO)
+ return(ERROR_DOS(ERRDOS,ERRnoaccess));
+
+ DEBUG(10,("call_trans2setfilepathinfo: SMB_SET_FILE_UNIX_BASIC doing mknod dev %.0f mode \
+0%o for file %s\n", (double)dev, unixmode, fname ));
+
+ /* Ok - do the mknod. */
+ if (SMB_VFS_MKNOD(conn,dos_to_unix_static(fname), unixmode, dev) != 0)
+ return(UNIXERROR(ERRDOS,ERRnoaccess));
+
+ inherit_access_acl(conn, fname, unixmode);
+
+ SSVAL(params,0,0);
+ send_trans2_replies(outbuf, bufsize, params, 2, *ppdata, 0);
+ return(-1);
+#endif /* HAVE_MAKEDEV_FN */
+
+ }
+
+ /*
+ * Deal with the UNIX specific mode set.
+ */
+
+ if (raw_unixmode != SMB_MODE_NO_CHANGE) {
+ DEBUG(10,("call_trans2setfilepathinfo: SMB_SET_FILE_UNIX_BASIC setting mode 0%o for file %s\n",
+ (unsigned int)unixmode, fname ));
+ if (SMB_VFS_CHMOD(conn,fname,unixmode) != 0)
+ return(UNIXERROR(ERRDOS,ERRnoaccess));
+ }
+
+ /*
+ * Deal with the UNIX specific uid set.
+ */
+
+ if ((set_owner != (uid_t)SMB_UID_NO_CHANGE) && (sbuf.st_uid != set_owner)) {
+ DEBUG(10,("call_trans2setfilepathinfo: SMB_SET_FILE_UNIX_BASIC changing owner %u for file %s\n",
+ (unsigned int)set_owner, fname ));
+ if (SMB_VFS_CHOWN(conn,fname,set_owner, (gid_t)-1) != 0)
+ return(UNIXERROR(ERRDOS,ERRnoaccess));
+ }
+
+ /*
+ * Deal with the UNIX specific gid set.
+ */
+
+ if ((set_grp != (uid_t)SMB_GID_NO_CHANGE) && (sbuf.st_gid != set_grp)) {
+ DEBUG(10,("call_trans2setfilepathinfo: SMB_SET_FILE_UNIX_BASIC changing group %u for file %s\n",
+ (unsigned int)set_owner, fname ));
+ if (SMB_VFS_CHOWN(conn,fname,(uid_t)-1, set_grp) != 0)
+ return(UNIXERROR(ERRDOS,ERRnoaccess));
+ }
+ break;
+ }
+
+ case SMB_SET_FILE_UNIX_LINK:
+ {
+ pstring link_dest;
+ /* Set a symbolic link. */
+ /* Don't allow this if follow links is false. */
+
+ if (!lp_symlinks(SNUM(conn)))
+ return(ERROR_DOS(ERRDOS,ERRnoaccess));
+
+ srvstr_get_path(inbuf, link_dest, pdata, sizeof(link_dest), -1, STR_TERMINATE, &status);
+ if (!NT_STATUS_IS_OK(status)) {
+ return ERROR_NT(status);
+ }
+
+ if (ensure_link_is_safe(conn, link_dest, link_dest) != 0)
+ return(UNIXERROR(ERRDOS,ERRnoaccess));
+
+ DEBUG(10,("call_trans2setfilepathinfo: SMB_SET_FILE_UNIX_LINK doing symlink %s -> %s\n",
+ fname, link_dest ));
+
+ if (SMB_VFS_SYMLINK(conn,link_dest,fname) != 0)
+ return(UNIXERROR(ERRDOS,ERRnoaccess));
+ SSVAL(params,0,0);
+ send_trans2_replies(outbuf, bufsize, params, 2, *ppdata, 0);
+ return(-1);
+ }
+
+ case SMB_SET_FILE_UNIX_HLINK:
+ {
+ pstring link_dest;
+
+ /* Set a hard link. */
+ srvstr_get_path(inbuf, link_dest, pdata, sizeof(link_dest), -1, STR_TERMINATE, &status);
+ if (!NT_STATUS_IS_OK(status)) {
+ return ERROR_NT(status);
+ }
+
+ DEBUG(10,("call_trans2setfilepathinfo: SMB_SET_FILE_UNIX_LINK doing hard link %s -> %s\n",
+ fname, link_dest ));
+
+ status = hardlink_internals(conn, fname, link_dest);
+ if (!NT_STATUS_IS_OK(status)) {
+ return ERROR_NT(status);
+ }
+
+ SSVAL(params,0,0);
+ send_trans2_replies(outbuf, bufsize, params, 2, *ppdata, 0);
+ return(-1);
+ }
+
+ case SMB_FILE_RENAME_INFORMATION:
+ {
+ BOOL overwrite;
+ uint32 root_fid;
+ uint32 len;
+ pstring newname;
+ pstring base_name;
+ char *p;
+
+ if (total_data < 12)
+ return(ERROR_DOS(ERRDOS,ERRinvalidparam));
+
+ overwrite = (CVAL(pdata,0) ? True : False);
+ root_fid = IVAL(pdata,4);
+ len = IVAL(pdata,8);
+ srvstr_get_path(inbuf, newname, &pdata[12], sizeof(newname), len, 0, &status);
+ if (!NT_STATUS_IS_OK(status)) {
+ return ERROR_NT(status);
+ }
+
+ /* Check the new name has no '/' characters. */
+ if (strchr_m(newname, '/'))
+ return ERROR_NT(NT_STATUS_NOT_SUPPORTED);
+
+ RESOLVE_DFSPATH(newname, conn, inbuf, outbuf);
+
+ /* Create the base directory. */
+ pstrcpy(base_name, fname);
+ p = strrchr_m(base_name, '/');
+ if (p)
+ *p = '\0';
+ /* Append the new name. */
+ pstrcat(base_name, "/");
+ pstrcat(base_name, newname);
+
+ if (fsp) {
+ DEBUG(10,("call_trans2setfilepathinfo: SMB_FILE_RENAME_INFORMATION (fnum %d) %s -> %s\n",
+ fsp->fnum, fsp->fsp_name, base_name ));
+ status = rename_internals_fsp(conn, fsp, base_name, overwrite);
+ } else {
+ DEBUG(10,("call_trans2setfilepathinfo: SMB_FILE_RENAME_INFORMATION %s -> %s\n",
+ fname, newname ));
+ status = rename_internals(conn, fname, base_name, 0, overwrite);
+ }
+ if (!NT_STATUS_IS_OK(status)) {
+ return ERROR_NT(status);
+ }
+ process_pending_change_notify_queue((time_t)0);
+ SSVAL(params,0,0);
+ send_trans2_replies(outbuf, bufsize, params, 2, *ppdata, 0);
+ return(-1);
+ }
+ default:
+ return ERROR_DOS(ERRDOS,ERRunknownlevel);
+ }
+
+ /* get some defaults (no modifications) if any info is zero or -1. */
+ if (tvs.actime == (time_t)0 || tvs.actime == (time_t)-1)
+ tvs.actime = sbuf.st_atime;
+
+ if (tvs.modtime == (time_t)0 || tvs.modtime == (time_t)-1)
+ tvs.modtime = sbuf.st_mtime;
+
+ DEBUG(6,("actime: %s " , ctime(&tvs.actime)));
+ DEBUG(6,("modtime: %s ", ctime(&tvs.modtime)));
+ DEBUG(6,("size: %.0f ", (double)size));
+
+ if (dosmode) {
+ if (S_ISDIR(sbuf.st_mode))
+ dosmode |= aDIR;
+ else
+ dosmode &= ~aDIR;
+ }
+
+ DEBUG(6,("dosmode: %x\n" , dosmode));
+
+ if(!((info_level == SMB_SET_FILE_END_OF_FILE_INFO) ||
+ (info_level == SMB_SET_FILE_ALLOCATION_INFO) ||
+ (info_level == SMB_FILE_ALLOCATION_INFORMATION) ||
+ (info_level == SMB_FILE_END_OF_FILE_INFORMATION))) {
+
+ /*
+ * Only do this test if we are not explicitly
+ * changing the size of a file.
+ */
+ if (!size)
+ size = get_file_size(sbuf);
+ }
+
+ /*
+ * Try and set the times, size and mode of this file -
+ * if they are different from the current values
+ */
+ if (sbuf.st_mtime != tvs.modtime || sbuf.st_atime != tvs.actime) {
+ if(fsp != NULL) {
+ /*
+ * This was a setfileinfo on an open file.
+ * NT does this a lot. It's actually pointless
+ * setting the time here, as it will be overwritten
+ * on the next write, so we save the request
+ * away and will set it on file close. JRA.
+ */
+
+ if (tvs.modtime != (time_t)0 && tvs.modtime != (time_t)-1) {
+ DEBUG(10,("call_trans2setfilepathinfo: setting pending modtime to %s\n", ctime(&tvs.modtime) ));
+ fsp->pending_modtime = tvs.modtime;
+ }
+
+ } else {
+
+ DEBUG(10,("call_trans2setfilepathinfo: setting utimes to modified values.\n"));
+
+ if(file_utime(conn, fname, &tvs)!=0)
+ return(UNIXERROR(ERRDOS,ERRnoaccess));
+ }
+ }
+
+ /* check the mode isn't different, before changing it */
+ if ((dosmode != 0) && (dosmode != dos_mode(conn, fname, &sbuf))) {
+
+ DEBUG(10,("call_trans2setfilepathinfo: file %s : setting dos mode %x\n", fname, dosmode ));
+
+ if(file_set_dosmode(conn, fname, dosmode, NULL)) {
+ DEBUG(2,("file_set_dosmode of %s failed (%s)\n", fname, strerror(errno)));
+ return(UNIXERROR(ERRDOS,ERRnoaccess));
+ }
+ }
+
+ if (size != get_file_size(sbuf)) {
+
+ int ret;
+
+ DEBUG(10,("call_trans2setfilepathinfo: file %s : setting new size to %.0f\n",
+ fname, (double)size ));
+
+ if (fd == -1) {
+ files_struct *new_fsp = NULL;
+ int access_mode = 0;
+ int action = 0;
+
+ if(global_oplock_break) {
+ /* Queue this file modify as we are the process of an oplock break. */
+
+ DEBUG(2,("call_trans2setfilepathinfo: queueing message due to being "));
+ DEBUGADD(2,( "in oplock break state.\n"));
+
+ push_oplock_pending_smb_message(inbuf, length);
+ return -1;
+ }
+
+ new_fsp = open_file_shared(conn, fname, &sbuf,
+ SET_OPEN_MODE(DOS_OPEN_RDWR),
+ (FILE_FAIL_IF_NOT_EXIST|FILE_EXISTS_OPEN),
+ FILE_ATTRIBUTE_NORMAL,
+ 0, &access_mode, &action);
+
+ if (new_fsp == NULL)
+ return(UNIXERROR(ERRDOS,ERRbadpath));
+ ret = vfs_set_filelen(new_fsp, size);
+ close_file(new_fsp,True);
+ } else {
+ ret = vfs_set_filelen(fsp, size);
+ }
+
+ if (ret == -1)
+ return (UNIXERROR(ERRHRD,ERRdiskfull));
+ }
+
+ SSVAL(params,0,0);
+ send_trans2_replies(outbuf, bufsize, params, 2, *ppdata, 0);
+
+ return(-1);
+}
+
+/****************************************************************************
+ Reply to a TRANS2_MKDIR (make directory with extended attributes).
+****************************************************************************/
+
+static int call_trans2mkdir(connection_struct *conn,
+ char *inbuf, char *outbuf, int length, int bufsize,
+ char **pparams, int total_params, char **ppdata, int total_data)
+{
+ char *params = *pparams;
+ pstring directory;
+ int ret = -1;
+ SMB_STRUCT_STAT sbuf;
+ BOOL bad_path = False;
+ NTSTATUS status = NT_STATUS_OK;
+
+ if (!CAN_WRITE(conn))
+ return ERROR_DOS(ERRSRV,ERRaccess);
+
+ if (total_params < 4)
+ return(ERROR_DOS(ERRDOS,ERRinvalidparam));
+
+ srvstr_get_path(inbuf, directory, &params[4], sizeof(directory), -1, STR_TERMINATE, &status);
+ if (!NT_STATUS_IS_OK(status)) {
+ return ERROR_NT(status);
+ }
+
+ DEBUG(3,("call_trans2mkdir : name = %s\n", directory));
+
+ unix_convert(directory,conn,0,&bad_path,&sbuf);
+ if (check_name(directory,conn))
+ ret = vfs_MkDir(conn,directory,unix_mode(conn,aDIR,directory));
+
+ if(ret < 0) {
+ DEBUG(5,("call_trans2mkdir error (%s)\n", strerror(errno)));
+ return set_bad_path_error(errno, bad_path, outbuf, ERRDOS,ERRnoaccess);
+ }
+
+ /* Realloc the parameter and data sizes */
+ params = Realloc(*pparams,2);
+ if(params == NULL)
+ return ERROR_DOS(ERRDOS,ERRnomem);
+ *pparams = params;
+
+ SSVAL(params,0,0);
+
+ send_trans2_replies(outbuf, bufsize, params, 2, *ppdata, 0);
+
+ return(-1);
+}
+
+/****************************************************************************
+ Reply to a TRANS2_FINDNOTIFYFIRST (start monitoring a directory for changes).
+ We don't actually do this - we just send a null response.
+****************************************************************************/
+
+static int call_trans2findnotifyfirst(connection_struct *conn,
+ char *inbuf, char *outbuf, int length, int bufsize,
+ char **pparams, int total_params, char **ppdata, int total_data)
+{
+ static uint16 fnf_handle = 257;
+ char *params = *pparams;
+ uint16 info_level;
+
+ if (total_params < 6)
+ return(ERROR_DOS(ERRDOS,ERRinvalidparam));
+
+ info_level = SVAL(params,4);
+ DEBUG(3,("call_trans2findnotifyfirst - info_level %d\n", info_level));
+
+ switch (info_level) {
+ case 1:
+ case 2:
+ break;
+ default:
+ return ERROR_DOS(ERRDOS,ERRunknownlevel);
+ }
+
+ /* Realloc the parameter and data sizes */
+ params = Realloc(*pparams,6);
+ if(params == NULL)
+ return ERROR_DOS(ERRDOS,ERRnomem);
+ *pparams = params;
+
+ SSVAL(params,0,fnf_handle);
+ SSVAL(params,2,0); /* No changes */
+ SSVAL(params,4,0); /* No EA errors */
+
+ fnf_handle++;
+
+ if(fnf_handle == 0)
+ fnf_handle = 257;
+
+ send_trans2_replies(outbuf, bufsize, params, 6, *ppdata, 0);
+
+ return(-1);
+}
+
+/****************************************************************************
+ Reply to a TRANS2_FINDNOTIFYNEXT (continue monitoring a directory for
+ changes). Currently this does nothing.
+****************************************************************************/
+
+static int call_trans2findnotifynext(connection_struct *conn,
+ char *inbuf, char *outbuf, int length, int bufsize,
+ char **pparams, int total_params, char **ppdata, int total_data)
+{
+ char *params = *pparams;
+
+ DEBUG(3,("call_trans2findnotifynext\n"));
+
+ /* Realloc the parameter and data sizes */
+ params = Realloc(*pparams,4);
+ if(params == NULL)
+ return ERROR_DOS(ERRDOS,ERRnomem);
+ *pparams = params;
+
+ SSVAL(params,0,0); /* No changes */
+ SSVAL(params,2,0); /* No EA errors */
+
+ send_trans2_replies(outbuf, bufsize, params, 4, *ppdata, 0);
+
+ return(-1);
+}
+
+/****************************************************************************
+ Reply to a TRANS2_GET_DFS_REFERRAL - Shirish Kalele <kalele@veritas.com>.
+****************************************************************************/
+
+static int call_trans2getdfsreferral(connection_struct *conn, char* inbuf,
+ char* outbuf, int length, int bufsize,
+ char **pparams, int total_params, char **ppdata, int total_data)
+{
+ char *params = *pparams;
+ pstring pathname;
+ int reply_size = 0;
+ int max_referral_level;
+
+ DEBUG(10,("call_trans2getdfsreferral\n"));
+
+ if (total_params < 2)
+ return(ERROR_DOS(ERRDOS,ERRinvalidparam));
+
+ max_referral_level = SVAL(params,0);
+
+ if(!lp_host_msdfs())
+ return ERROR_DOS(ERRDOS,ERRbadfunc);
+
+ srvstr_pull(inbuf, pathname, &params[2], sizeof(pathname), -1, STR_TERMINATE);
+ if((reply_size = setup_dfs_referral(conn, pathname,max_referral_level,ppdata)) < 0)
+ return UNIXERROR(ERRDOS,ERRbadfile);
+
+ SSVAL(outbuf,smb_flg2,SVAL(outbuf,smb_flg2) | FLAGS2_DFS_PATHNAMES);
+ send_trans2_replies(outbuf,bufsize,0,0,*ppdata,reply_size);
+
+ return(-1);
+}
+
+#define LMCAT_SPL 0x53
+#define LMFUNC_GETJOBID 0x60
+
+/****************************************************************************
+ Reply to a TRANS2_IOCTL - used for OS/2 printing.
+****************************************************************************/
+
+static int call_trans2ioctl(connection_struct *conn, char* inbuf,
+ char* outbuf, int length, int bufsize,
+ char **pparams, int total_params, char **ppdata, int total_data)
+{
+ char *pdata = *ppdata;
+ files_struct *fsp = file_fsp(inbuf,smb_vwv15);
+
+ /* check for an invalid fid before proceeding */
+
+ if (!fsp)
+ return(ERROR_DOS(ERRDOS,ERRbadfid));
+
+ if ((SVAL(inbuf,(smb_setup+4)) == LMCAT_SPL) &&
+ (SVAL(inbuf,(smb_setup+6)) == LMFUNC_GETJOBID)) {
+ pdata = Realloc(*ppdata, 32);
+ if(pdata == NULL)
+ return ERROR_DOS(ERRDOS,ERRnomem);
+ *ppdata = pdata;
+
+ /* NOTE - THIS IS ASCII ONLY AT THE MOMENT - NOT SURE IF OS/2
+ CAN ACCEPT THIS IN UNICODE. JRA. */
+
+ SSVAL(pdata,0,fsp->rap_print_jobid); /* Job number */
+ srvstr_push( outbuf, pdata + 2, global_myname(), 15, STR_ASCII|STR_TERMINATE); /* Our NetBIOS name */
+ srvstr_push( outbuf, pdata+18, lp_servicename(SNUM(conn)), 13, STR_ASCII|STR_TERMINATE); /* Service name */
+ send_trans2_replies(outbuf,bufsize,*pparams,0,*ppdata,32);
+ return(-1);
+ } else {
+ DEBUG(2,("Unknown TRANS2_IOCTL\n"));
+ return ERROR_DOS(ERRSRV,ERRerror);
+ }
+}
+
+/****************************************************************************
+ Reply to a SMBfindclose (stop trans2 directory search).
+****************************************************************************/
+
+int reply_findclose(connection_struct *conn,
+ char *inbuf,char *outbuf,int length,int bufsize)
+{
+ int outsize = 0;
+ int dptr_num=SVALS(inbuf,smb_vwv0);
+ START_PROFILE(SMBfindclose);
+
+ DEBUG(3,("reply_findclose, dptr_num = %d\n", dptr_num));
+
+ dptr_close(&dptr_num);
+
+ outsize = set_message(outbuf,0,0,True);
+
+ DEBUG(3,("SMBfindclose dptr_num = %d\n", dptr_num));
+
+ END_PROFILE(SMBfindclose);
+ return(outsize);
+}
+
+/****************************************************************************
+ Reply to a SMBfindnclose (stop FINDNOTIFYFIRST directory search).
+****************************************************************************/
+
+int reply_findnclose(connection_struct *conn,
+ char *inbuf,char *outbuf,int length,int bufsize)
+{
+ int outsize = 0;
+ int dptr_num= -1;
+ START_PROFILE(SMBfindnclose);
+
+ dptr_num = SVAL(inbuf,smb_vwv0);
+
+ DEBUG(3,("reply_findnclose, dptr_num = %d\n", dptr_num));
+
+ /* We never give out valid handles for a
+ findnotifyfirst - so any dptr_num is ok here.
+ Just ignore it. */
+
+ outsize = set_message(outbuf,0,0,True);
+
+ DEBUG(3,("SMB_findnclose dptr_num = %d\n", dptr_num));
+
+ END_PROFILE(SMBfindnclose);
+ return(outsize);
+}
+
+/****************************************************************************
+ Reply to a SMBtranss2 - just ignore it!
+****************************************************************************/
+
+int reply_transs2(connection_struct *conn,
+ char *inbuf,char *outbuf,int length,int bufsize)
+{
+ START_PROFILE(SMBtranss2);
+ DEBUG(4,("Ignoring transs2 of length %d\n",length));
+ END_PROFILE(SMBtranss2);
+ return(-1);
+}
+
+/****************************************************************************
+ Reply to a SMBtrans2.
+****************************************************************************/
+
+int reply_trans2(connection_struct *conn,
+ char *inbuf,char *outbuf,int length,int bufsize)
+{
+ int outsize = 0;
+ unsigned int total_params = SVAL(inbuf, smb_tpscnt);
+ unsigned int total_data =SVAL(inbuf, smb_tdscnt);
+#if 0
+ unsigned int max_param_reply = SVAL(inbuf, smb_mprcnt);
+ unsigned int max_data_reply = SVAL(inbuf, smb_mdrcnt);
+ unsigned int max_setup_fields = SVAL(inbuf, smb_msrcnt);
+ BOOL close_tid = BITSETW(inbuf+smb_flags,0);
+ BOOL no_final_response = BITSETW(inbuf+smb_flags,1);
+ int32 timeout = IVALS(inbuf,smb_timeout);
+#endif
+ unsigned int suwcnt = SVAL(inbuf, smb_suwcnt);
+ unsigned int tran_call = SVAL(inbuf, smb_setup0);
+ char *params = NULL, *data = NULL;
+ unsigned int num_params, num_params_sofar, num_data, num_data_sofar;
+ START_PROFILE(SMBtrans2);
+
+ if(global_oplock_break && (tran_call == TRANSACT2_OPEN)) {
+ /* Queue this open message as we are the process of an
+ * oplock break. */
+
+ DEBUG(2,("reply_trans2: queueing message trans2open due to being "));
+ DEBUGADD(2,( "in oplock break state.\n"));
+
+ push_oplock_pending_smb_message(inbuf, length);
+ END_PROFILE(SMBtrans2);
+ return -1;
+ }
+
+ if (IS_IPC(conn) && (tran_call != TRANSACT2_OPEN)
+ && (tran_call != TRANSACT2_GET_DFS_REFERRAL)) {
+ END_PROFILE(SMBtrans2);
+ return ERROR_DOS(ERRSRV,ERRaccess);
+ }
+
+ outsize = set_message(outbuf,0,0,True);
+
+ /* All trans2 messages we handle have smb_sucnt == 1 - ensure this
+ is so as a sanity check */
+ if (suwcnt != 1) {
+ /*
+ * Need to have rc=0 for ioctl to get job id for OS/2.
+ * Network printing will fail if function is not successful.
+ * Similar function in reply.c will be used if protocol
+ * is LANMAN1.0 instead of LM1.2X002.
+ * Until DosPrintSetJobInfo with PRJINFO3 is supported,
+ * outbuf doesn't have to be set(only job id is used).
+ */
+ if ( (suwcnt == 4) && (tran_call == TRANSACT2_IOCTL) &&
+ (SVAL(inbuf,(smb_setup+4)) == LMCAT_SPL) &&
+ (SVAL(inbuf,(smb_setup+6)) == LMFUNC_GETJOBID)) {
+ DEBUG(2,("Got Trans2 DevIOctl jobid\n"));
+ } else {
+ DEBUG(2,("Invalid smb_sucnt in trans2 call(%u)\n",suwcnt));
+ DEBUG(2,("Transaction is %d\n",tran_call));
+ END_PROFILE(SMBtrans2);
+ ERROR_DOS(ERRDOS,ERRinvalidparam);
+ }
+ }
+
+ /* Allocate the space for the maximum needed parameters and data */
+ if (total_params > 0)
+ params = (char *)malloc(total_params);
+ if (total_data > 0)
+ data = (char *)malloc(total_data);
+
+ if ((total_params && !params) || (total_data && !data)) {
+ DEBUG(2,("Out of memory in reply_trans2\n"));
+ SAFE_FREE(params);
+ SAFE_FREE(data);
+ END_PROFILE(SMBtrans2);
+ return ERROR_DOS(ERRDOS,ERRnomem);
+ }
+
+ /* Copy the param and data bytes sent with this request into
+ the params buffer */
+ num_params = num_params_sofar = SVAL(inbuf,smb_pscnt);
+ num_data = num_data_sofar = SVAL(inbuf, smb_dscnt);
+
+ if (num_params > total_params || num_data > total_data)
+ exit_server("invalid params in reply_trans2");
+
+ if(params) {
+ unsigned int psoff = SVAL(inbuf, smb_psoff);
+ if ((psoff + num_params < psoff) || (psoff + num_params < num_params))
+ goto bad_param;
+ if ((smb_base(inbuf) + psoff + num_params > inbuf + length) ||
+ (smb_base(inbuf) + psoff + num_params < smb_base(inbuf)))
+ goto bad_param;
+ memcpy( params, smb_base(inbuf) + psoff, num_params);
+ }
+ if(data) {
+ unsigned int dsoff = SVAL(inbuf, smb_dsoff);
+ if ((dsoff + num_data < dsoff) || (dsoff + num_data < num_data))
+ goto bad_param;
+ if ((smb_base(inbuf) + dsoff + num_data > inbuf + length) ||
+ (smb_base(inbuf) + dsoff + num_data < smb_base(inbuf)))
+ goto bad_param;
+ memcpy( data, smb_base(inbuf) + dsoff, num_data);
+ }
+
+ srv_signing_trans_start(SVAL(inbuf,smb_mid));
+
+ if(num_data_sofar < total_data || num_params_sofar < total_params) {
+ /* We need to send an interim response then receive the rest
+ of the parameter/data bytes */
+ outsize = set_message(outbuf,0,0,True);
+ srv_signing_trans_stop();
+ if (!send_smb(smbd_server_fd(),outbuf))
+ exit_server("reply_trans2: send_smb failed.");
+
+ while (num_data_sofar < total_data ||
+ num_params_sofar < total_params) {
+ BOOL ret;
+ unsigned int param_disp;
+ unsigned int param_off;
+ unsigned int data_disp;
+ unsigned int data_off;
+
+ ret = receive_next_smb(inbuf,bufsize,SMB_SECONDARY_WAIT);
+
+ /*
+ * The sequence number for the trans reply is always
+ * based on the last secondary received.
+ */
+
+ srv_signing_trans_start(SVAL(inbuf,smb_mid));
+
+ if ((ret &&
+ (CVAL(inbuf, smb_com) != SMBtranss2)) || !ret) {
+ outsize = set_message(outbuf,0,0,True);
+ if(ret)
+ DEBUG(0,("reply_trans2: Invalid secondary trans2 packet\n"));
+ else
+ DEBUG(0,("reply_trans2: %s in getting secondary trans2 response.\n",
+ (smb_read_error == READ_ERROR) ? "error" : "timeout" ));
+ goto bad_param;
+ }
+
+ /* Revise total_params and total_data in case
+ they have changed downwards */
+ if (SVAL(inbuf, smb_tpscnt) < total_params)
+ total_params = SVAL(inbuf, smb_tpscnt);
+ if (SVAL(inbuf, smb_tdscnt) < total_data)
+ total_data = SVAL(inbuf, smb_tdscnt);
+
+ num_params = SVAL(inbuf,smb_spscnt);
+ param_off = SVAL(inbuf, smb_spsoff);
+ param_disp = SVAL(inbuf, smb_spsdisp);
+ num_params_sofar += num_params;
+
+ num_data = SVAL(inbuf, smb_sdscnt);
+ data_off = SVAL(inbuf, smb_sdsoff);
+ data_disp = SVAL(inbuf, smb_sdsdisp);
+ num_data_sofar += num_data;
+
+ if (num_params_sofar > total_params || num_data_sofar > total_data)
+ goto bad_param;
+
+ if (num_params) {
+ if (param_disp + num_params >= total_params)
+ goto bad_param;
+ if ((param_disp + num_params < param_disp) ||
+ (param_disp + num_params < num_params))
+ goto bad_param;
+ if (param_disp > total_params)
+ goto bad_param;
+ if ((smb_base(inbuf) + param_off + num_params >= inbuf + bufsize) ||
+ (smb_base(inbuf) + param_off + num_params < smb_base(inbuf)))
+ goto bad_param;
+ if (params + param_disp < params)
+ goto bad_param;
+
+ memcpy( &params[param_disp], smb_base(inbuf) + param_off, num_params);
+ }
+ if (num_data) {
+ if (data_disp + num_data >= total_data)
+ goto bad_param;
+ if ((data_disp + num_data < data_disp) ||
+ (data_disp + num_data < num_data))
+ goto bad_param;
+ if (data_disp > total_data)
+ goto bad_param;
+ if ((smb_base(inbuf) + data_off + num_data >= inbuf + bufsize) ||
+ (smb_base(inbuf) + data_off + num_data < smb_base(inbuf)))
+ goto bad_param;
+ if (data + data_disp < data)
+ goto bad_param;
+
+ memcpy( &data[data_disp], smb_base(inbuf) + data_off, num_data);
+ }
+ }
+ }
+
+ if (Protocol >= PROTOCOL_NT1) {
+ SSVAL(outbuf,smb_flg2,SVAL(outbuf,smb_flg2) | 0x40); /* IS_LONG_NAME */
+ }
+
+ /* Now we must call the relevant TRANS2 function */
+ switch(tran_call) {
+ case TRANSACT2_OPEN:
+ START_PROFILE_NESTED(Trans2_open);
+ outsize = call_trans2open(conn, inbuf, outbuf, bufsize,
+ &params, total_params, &data, total_data);
+ END_PROFILE_NESTED(Trans2_open);
+ break;
+
+ case TRANSACT2_FINDFIRST:
+ START_PROFILE_NESTED(Trans2_findfirst);
+ outsize = call_trans2findfirst(conn, inbuf, outbuf, bufsize,
+ &params, total_params, &data, total_data);
+ END_PROFILE_NESTED(Trans2_findfirst);
+ break;
+
+ case TRANSACT2_FINDNEXT:
+ START_PROFILE_NESTED(Trans2_findnext);
+ outsize = call_trans2findnext(conn, inbuf, outbuf, length, bufsize,
+ &params, total_params, &data, total_data);
+ END_PROFILE_NESTED(Trans2_findnext);
+ break;
+
+ case TRANSACT2_QFSINFO:
+ START_PROFILE_NESTED(Trans2_qfsinfo);
+ outsize = call_trans2qfsinfo(conn, inbuf, outbuf, length, bufsize,
+ &params, total_params, &data, total_data);
+ END_PROFILE_NESTED(Trans2_qfsinfo);
+ break;
+
+#ifdef HAVE_SYS_QUOTAS
+ case TRANSACT2_SETFSINFO:
+ START_PROFILE_NESTED(Trans2_setfsinfo);
+ outsize = call_trans2setfsinfo(conn, inbuf, outbuf, length, bufsize,
+ &params, total_params, &data, total_data);
+ END_PROFILE_NESTED(Trans2_setfsinfo);
+ break;
+#endif
+ case TRANSACT2_QPATHINFO:
+ case TRANSACT2_QFILEINFO:
+ START_PROFILE_NESTED(Trans2_qpathinfo);
+ outsize = call_trans2qfilepathinfo(conn, inbuf, outbuf, length, bufsize,
+ &params, total_params, &data, total_data);
+ END_PROFILE_NESTED(Trans2_qpathinfo);
+ break;
+ case TRANSACT2_SETPATHINFO:
+ case TRANSACT2_SETFILEINFO:
+ START_PROFILE_NESTED(Trans2_setpathinfo);
+ outsize = call_trans2setfilepathinfo(conn, inbuf, outbuf, length, bufsize,
+ &params, total_params, &data, total_data);
+ END_PROFILE_NESTED(Trans2_setpathinfo);
+ break;
+
+ case TRANSACT2_FINDNOTIFYFIRST:
+ START_PROFILE_NESTED(Trans2_findnotifyfirst);
+ outsize = call_trans2findnotifyfirst(conn, inbuf, outbuf, length, bufsize,
+ &params, total_params, &data, total_data);
+ END_PROFILE_NESTED(Trans2_findnotifyfirst);
+ break;
+
+ case TRANSACT2_FINDNOTIFYNEXT:
+ START_PROFILE_NESTED(Trans2_findnotifynext);
+ outsize = call_trans2findnotifynext(conn, inbuf, outbuf, length, bufsize,
+ &params, total_params, &data, total_data);
+ END_PROFILE_NESTED(Trans2_findnotifynext);
+ break;
+ case TRANSACT2_MKDIR:
+ START_PROFILE_NESTED(Trans2_mkdir);
+ outsize = call_trans2mkdir(conn, inbuf, outbuf, length, bufsize,
+ &params, total_params, &data, total_data);
+ END_PROFILE_NESTED(Trans2_mkdir);
+ break;
+
+ case TRANSACT2_GET_DFS_REFERRAL:
+ START_PROFILE_NESTED(Trans2_get_dfs_referral);
+ outsize = call_trans2getdfsreferral(conn,inbuf,outbuf,length, bufsize,
+ &params, total_params, &data, total_data);
+ END_PROFILE_NESTED(Trans2_get_dfs_referral);
+ break;
+ case TRANSACT2_IOCTL:
+ START_PROFILE_NESTED(Trans2_ioctl);
+ outsize = call_trans2ioctl(conn,inbuf,outbuf,length, bufsize,
+ &params, total_params, &data, total_data);
+ END_PROFILE_NESTED(Trans2_ioctl);
+ break;
+ default:
+ /* Error in request */
+ DEBUG(2,("Unknown request %d in trans2 call\n", tran_call));
+ SAFE_FREE(params);
+ SAFE_FREE(data);
+ END_PROFILE(SMBtrans2);
+ srv_signing_trans_stop();
+ return ERROR_DOS(ERRSRV,ERRerror);
+ }
+
+ /* As we do not know how many data packets will need to be
+ returned here the various call_trans2xxxx calls
+ must send their own. Thus a call_trans2xxx routine only
+ returns a value other than -1 when it wants to send
+ an error packet.
+ */
+
+ srv_signing_trans_stop();
+
+ SAFE_FREE(params);
+ SAFE_FREE(data);
+ END_PROFILE(SMBtrans2);
+ return outsize; /* If a correct response was needed the
+ call_trans2xxx calls have already sent
+ it. If outsize != -1 then it is returning */
+
+ bad_param:
+
+ srv_signing_trans_stop();
+ SAFE_FREE(params);
+ SAFE_FREE(data);
+ END_PROFILE(SMBtrans2);
+ return ERROR_NT(NT_STATUS_INVALID_PARAMETER);
+}
diff --git a/source/smbd/uid.c b/source/smbd/uid.c
new file mode 100644
index 00000000000..ff3dd1a56ef
--- /dev/null
+++ b/source/smbd/uid.c
@@ -0,0 +1,430 @@
+/*
+ Unix SMB/CIFS implementation.
+ uid/user handling
+ 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 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"
+
+/* what user is current? */
+extern struct current_user current_user;
+
+/****************************************************************************
+ Become the guest user without changing the security context stack.
+****************************************************************************/
+
+BOOL change_to_guest(void)
+{
+ static struct passwd *pass=NULL;
+
+ if (!pass) {
+ /* Don't need to free() this as its stored in a static */
+ pass = getpwnam_alloc(lp_guestaccount());
+ if (!pass)
+ return(False);
+ }
+
+#ifdef AIX
+ /* MWW: From AIX FAQ patch to WU-ftpd: call initgroups before
+ setting IDs */
+ initgroups(pass->pw_name, pass->pw_gid);
+#endif
+
+ set_sec_ctx(pass->pw_uid, pass->pw_gid, 0, NULL, NULL, NULL);
+
+ current_user.conn = NULL;
+ current_user.vuid = UID_FIELD_INVALID;
+
+ passwd_free(&pass);
+
+ return True;
+}
+
+/****************************************************************************
+ Readonly share for this user ?
+****************************************************************************/
+
+static BOOL is_share_read_only_for_user(connection_struct *conn, user_struct *vuser)
+{
+ char **list;
+ const char *service = lp_servicename(conn->service);
+ BOOL read_only_ret = lp_readonly(conn->service);
+
+ if (!service)
+ return read_only_ret;
+
+ str_list_copy(&list, lp_readlist(conn->service));
+ if (list) {
+ if (!str_list_sub_basic(list, vuser->user.smb_name) ) {
+ DEBUG(0, ("is_share_read_only_for_user: ERROR: read list substitution failed\n"));
+ }
+ if (!str_list_substitute(list, "%S", service)) {
+ DEBUG(0, ("is_share_read_only_for_user: ERROR: read list service substitution failed\n"));
+ }
+ if (user_in_list(vuser->user.unix_name, (const char **)list, vuser->groups, vuser->n_groups)) {
+ read_only_ret = True;
+ }
+ str_list_free(&list);
+ }
+
+ str_list_copy(&list, lp_writelist(conn->service));
+ if (list) {
+ if (!str_list_sub_basic(list, vuser->user.smb_name) ) {
+ DEBUG(0, ("is_share_read_only_for_user: ERROR: write list substitution failed\n"));
+ }
+ if (!str_list_substitute(list, "%S", service)) {
+ DEBUG(0, ("is_share_read_only_for_user: ERROR: write list service substitution failed\n"));
+ }
+ if (user_in_list(vuser->user.unix_name, (const char **)list, vuser->groups, vuser->n_groups)) {
+ read_only_ret = False;
+ }
+ str_list_free(&list);
+ }
+
+ DEBUG(10,("is_share_read_only_for_user: share %s is %s for unix user %s\n",
+ service, read_only_ret ? "read-only" : "read-write", vuser->user.unix_name ));
+
+ return read_only_ret;
+}
+
+/*******************************************************************
+ Check if a username is OK.
+********************************************************************/
+
+static BOOL check_user_ok(connection_struct *conn, user_struct *vuser,int snum)
+{
+ unsigned int i;
+ struct vuid_cache_entry *ent = NULL;
+ BOOL readonly_share;
+
+ for (i=0;i<conn->vuid_cache.entries && i< VUID_CACHE_SIZE;i++) {
+ if (conn->vuid_cache.array[i].vuid == vuser->vuid) {
+ ent = &conn->vuid_cache.array[i];
+ conn->read_only = ent->read_only;
+ conn->admin_user = ent->admin_user;
+ return(True);
+ }
+ }
+
+ if (!user_ok(vuser->user.unix_name,snum, vuser->groups, vuser->n_groups))
+ return(False);
+
+ readonly_share = is_share_read_only_for_user(conn, vuser);
+
+ if (!share_access_check(conn, snum, vuser, readonly_share ? FILE_READ_DATA : FILE_WRITE_DATA)) {
+ return False;
+ }
+
+ i = conn->vuid_cache.entries % VUID_CACHE_SIZE;
+ if (conn->vuid_cache.entries < VUID_CACHE_SIZE)
+ conn->vuid_cache.entries++;
+
+ ent = &conn->vuid_cache.array[i];
+ ent->vuid = vuser->vuid;
+ ent->read_only = readonly_share;
+
+ if (user_in_list(vuser->user.unix_name ,lp_admin_users(conn->service), vuser->groups, vuser->n_groups)) {
+ ent->admin_user = True;
+ } else {
+ ent->admin_user = False;
+ }
+
+ conn->read_only = ent->read_only;
+ conn->admin_user = ent->admin_user;
+
+ return(True);
+}
+
+/****************************************************************************
+ Become the user of a connection number without changing the security context
+ stack, but modify the currnet_user entries.
+****************************************************************************/
+
+BOOL change_to_user(connection_struct *conn, uint16 vuid)
+{
+ user_struct *vuser = get_valid_user_struct(vuid);
+ int snum;
+ gid_t gid;
+ uid_t uid;
+ char group_c;
+ BOOL must_free_token_priv = False;
+ NT_USER_TOKEN *token = NULL;
+ PRIVILEGE_SET *privs = NULL;
+
+ if (!conn) {
+ DEBUG(2,("change_to_user: Connection not open\n"));
+ return(False);
+ }
+
+ /*
+ * We need a separate check in security=share mode due to vuid
+ * always being UID_FIELD_INVALID. If we don't do this then
+ * in share mode security we are *always* changing uid's between
+ * SMB's - this hurts performance - Badly.
+ */
+
+ if((lp_security() == SEC_SHARE) && (current_user.conn == conn) &&
+ (current_user.uid == conn->uid)) {
+ DEBUG(4,("change_to_user: Skipping user change - already user\n"));
+ return(True);
+ } else if ((current_user.conn == conn) &&
+ (vuser != 0) && (current_user.vuid == vuid) &&
+ (current_user.uid == vuser->uid)) {
+ DEBUG(4,("change_to_user: Skipping user change - already user\n"));
+ return(True);
+ }
+
+ snum = SNUM(conn);
+
+ if (conn->force_user) /* security = share sets this too */ {
+ uid = conn->uid;
+ gid = conn->gid;
+ current_user.groups = conn->groups;
+ current_user.ngroups = conn->ngroups;
+ token = conn->nt_user_token;
+ privs = conn->privs;
+ } else if ((vuser) && check_user_ok(conn, vuser, snum)) {
+ uid = conn->admin_user ? 0 : vuser->uid;
+ gid = vuser->gid;
+ current_user.ngroups = vuser->n_groups;
+ current_user.groups = vuser->groups;
+ token = vuser->nt_user_token;
+ privs = vuser->privs;
+ } else {
+ DEBUG(2,("change_to_user: Invalid vuid used %d or vuid not permitted access to share.\n",vuid));
+ return False;
+ }
+
+ /*
+ * See if we should force group for this service.
+ * If so this overrides any group set in the force
+ * user code.
+ */
+
+ if((group_c = *lp_force_group(snum))) {
+ BOOL is_guest = False;
+
+ if(group_c == '+') {
+
+ /*
+ * Only force group if the user is a member of
+ * the service group. Check the group memberships for
+ * this user (we already have this) to
+ * see if we should force the group.
+ */
+
+ int i;
+ for (i = 0; i < current_user.ngroups; i++) {
+ if (current_user.groups[i] == conn->gid) {
+ gid = conn->gid;
+ break;
+ }
+ }
+ } else {
+ gid = conn->gid;
+ }
+
+ /*
+ * We've changed the group list in the token - we must
+ * re-create it.
+ */
+
+ if (vuser && vuser->guest)
+ is_guest = True;
+
+ token = create_nt_token(uid, gid, current_user.ngroups, current_user.groups, is_guest);
+ if (!token) {
+ DEBUG(1, ("change_to_user: create_nt_token failed!\n"));
+ return False;
+ }
+ pdb_get_privilege_set(token->user_sids, token->num_sids, privs);
+ must_free_token_priv = True;
+ }
+
+ set_sec_ctx(uid, gid, current_user.ngroups, current_user.groups, token, privs);
+
+ /*
+ * Free the new token (as set_sec_ctx copies it).
+ */
+
+ if (must_free_token_priv) {
+ delete_nt_token(&token);
+ destroy_privilege(&privs);
+ }
+
+ current_user.conn = conn;
+ current_user.vuid = vuid;
+
+ DEBUG(5,("change_to_user uid=(%d,%d) gid=(%d,%d)\n",
+ (int)getuid(),(int)geteuid(),(int)getgid(),(int)getegid()));
+
+ return(True);
+}
+
+/****************************************************************************
+ Go back to being root without changing the security context stack,
+ but modify the current_user entries.
+****************************************************************************/
+
+BOOL change_to_root_user(void)
+{
+ set_root_sec_ctx();
+
+ DEBUG(5,("change_to_root_user: now uid=(%d,%d) gid=(%d,%d)\n",
+ (int)getuid(),(int)geteuid(),(int)getgid(),(int)getegid()));
+
+ current_user.conn = NULL;
+ current_user.vuid = UID_FIELD_INVALID;
+
+ return(True);
+}
+
+/****************************************************************************
+ Become the user of an authenticated connected named pipe.
+ When this is called we are currently running as the connection
+ user. Doesn't modify current_user.
+****************************************************************************/
+
+BOOL become_authenticated_pipe_user(pipes_struct *p)
+{
+ if (!push_sec_ctx())
+ return False;
+
+ set_sec_ctx(p->pipe_user.uid, p->pipe_user.gid,
+ p->pipe_user.ngroups, p->pipe_user.groups, p->pipe_user.nt_user_token, p->pipe_user.privs);
+
+ return True;
+}
+
+/****************************************************************************
+ Unbecome the user of an authenticated connected named pipe.
+ When this is called we are running as the authenticated pipe
+ user and need to go back to being the connection user. Doesn't modify
+ current_user.
+****************************************************************************/
+
+BOOL unbecome_authenticated_pipe_user(void)
+{
+ return pop_sec_ctx();
+}
+
+/****************************************************************************
+ Utility functions used by become_xxx/unbecome_xxx.
+****************************************************************************/
+
+struct conn_ctx {
+ connection_struct *conn;
+ uint16 vuid;
+};
+
+/* A stack of current_user connection contexts. */
+
+static struct conn_ctx conn_ctx_stack[MAX_SEC_CTX_DEPTH];
+static int conn_ctx_stack_ndx;
+
+static void push_conn_ctx(void)
+{
+ struct conn_ctx *ctx_p;
+
+ /* Check we don't overflow our stack */
+
+ if (conn_ctx_stack_ndx == MAX_SEC_CTX_DEPTH) {
+ DEBUG(0, ("Connection context stack overflow!\n"));
+ smb_panic("Connection context stack overflow!\n");
+ }
+
+ /* Store previous user context */
+ ctx_p = &conn_ctx_stack[conn_ctx_stack_ndx];
+
+ ctx_p->conn = current_user.conn;
+ ctx_p->vuid = current_user.vuid;
+
+ DEBUG(3, ("push_conn_ctx(%u) : conn_ctx_stack_ndx = %d\n",
+ (unsigned int)ctx_p->vuid, conn_ctx_stack_ndx ));
+
+ conn_ctx_stack_ndx++;
+}
+
+static void pop_conn_ctx(void)
+{
+ struct conn_ctx *ctx_p;
+
+ /* Check for stack underflow. */
+
+ if (conn_ctx_stack_ndx == 0) {
+ DEBUG(0, ("Connection context stack underflow!\n"));
+ smb_panic("Connection context stack underflow!\n");
+ }
+
+ conn_ctx_stack_ndx--;
+ ctx_p = &conn_ctx_stack[conn_ctx_stack_ndx];
+
+ current_user.conn = ctx_p->conn;
+ current_user.vuid = ctx_p->vuid;
+
+ ctx_p->conn = NULL;
+ ctx_p->vuid = UID_FIELD_INVALID;
+}
+
+/****************************************************************************
+ Temporarily become a root user. Must match with unbecome_root(). Saves and
+ restores the connection context.
+****************************************************************************/
+
+void become_root(void)
+{
+ push_sec_ctx();
+ push_conn_ctx();
+ set_root_sec_ctx();
+}
+
+/* Unbecome the root user */
+
+void unbecome_root(void)
+{
+ pop_sec_ctx();
+ pop_conn_ctx();
+}
+
+/****************************************************************************
+ Push the current security context then force a change via change_to_user().
+ Saves and restores the connection context.
+****************************************************************************/
+
+BOOL become_user(connection_struct *conn, uint16 vuid)
+{
+ if (!push_sec_ctx())
+ return False;
+
+ push_conn_ctx();
+
+ if (!change_to_user(conn, vuid)) {
+ pop_sec_ctx();
+ pop_conn_ctx();
+ return False;
+ }
+
+ return True;
+}
+
+BOOL unbecome_user(void)
+{
+ pop_sec_ctx();
+ pop_conn_ctx();
+ return True;
+}
+
diff --git a/source/smbd/utmp.c b/source/smbd/utmp.c
new file mode 100644
index 00000000000..a521d0113d4
--- /dev/null
+++ b/source/smbd/utmp.c
@@ -0,0 +1,595 @@
+/*
+ Unix SMB/CIFS implementation.
+ utmp routines
+ Copyright (C) T.D.Lee@durham.ac.uk 1999
+ Heavily modified by Andrew Bartlett and Tridge, April 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 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"
+
+/****************************************************************************
+Reflect connection status in utmp/wtmp files.
+ T.D.Lee@durham.ac.uk September 1999
+
+ With grateful thanks since then to many who have helped port it to
+ different operating systems. The variety of OS quirks thereby
+ uncovered is amazing...
+
+Hints for porting:
+ o Always attempt to use programmatic interface (pututline() etc.)
+ Indeed, at present only programmatic use is supported.
+ o The only currently supported programmatic interface to "wtmp{,x}"
+ is through "updwtmp*()" routines.
+ o The "x" (utmpx/wtmpx; HAVE_UTMPX_H) seems preferable.
+ o The HAVE_* items should identify supported features.
+ o If at all possible, avoid "if defined(MY-OS)" constructions.
+
+OS observations and status:
+ Almost every OS seems to have its own quirks.
+
+ Solaris 2.x:
+ Tested on 2.6 and 2.7; should be OK on other flavours.
+ AIX:
+ Apparently has utmpx.h but doesn't implement.
+ OSF:
+ Has utmpx.h, but (e.g.) no "getutmpx()". (Is this like AIX ?)
+ Redhat 6:
+ utmpx.h seems not to set default filenames. non-x better.
+ IRIX 6.5:
+ Not tested. Appears to have "x".
+ HP-UX 9.x:
+ Not tested. Appears to lack "x".
+ HP-UX 10.x:
+ Not tested.
+ "updwtmp*()" routines seem absent, so no current wtmp* support.
+ Has "ut_addr": probably trivial to implement (although remember
+ that IPv6 is coming...).
+
+ FreeBSD:
+ No "putut*()" type of interface.
+ No "ut_type" and associated defines.
+ Write files directly. Alternatively use its login(3)/logout(3).
+ SunOS 4:
+ Not tested. Resembles FreeBSD, but no login()/logout().
+
+lastlog:
+ Should "lastlog" files, if any, be updated?
+ BSD systems (SunOS 4, FreeBSD):
+ o Prominent mention on man pages.
+ System-V (e.g. Solaris 2):
+ o No mention on man pages, even under "man -k".
+ o Has a "/var/adm/lastlog" file, but pututxline() etc. seem
+ not to touch it.
+ o Despite downplaying (above), nevertheless has <lastlog.h>.
+ So perhaps UN*X "lastlog" facility is intended for tty/terminal only?
+
+Notes:
+ Each connection requires a small number (starting at 0, working up)
+ to represent the line. This must be unique within and across all
+ smbd processes. It is the 'id_num' from Samba's session.c code.
+
+ The 4 byte 'ut_id' component is vital to distinguish connections,
+ of which there could be several hundred or even thousand.
+ Entries seem to be printable characters, with optional NULL pads.
+
+ We need to be distinct from other entries in utmp/wtmp.
+
+ Observed things: therefore avoid them. Add to this list please.
+ From Solaris 2.x (because that's what I have):
+ 'sN' : run-levels; N: [0-9]
+ 'co' : console
+ 'CC' : arbitrary things; C: [a-z]
+ 'rXNN' : rlogin; N: [0-9]; X: [0-9a-z]
+ 'tXNN' : rlogin; N: [0-9]; X: [0-9a-z]
+ '/NNN' : Solaris CDE
+ 'ftpZ' : ftp (Z is the number 255, aka 0377, aka 0xff)
+ Mostly a record uses the same 'ut_id' in both "utmp" and "wtmp",
+ but differences have been seen.
+
+ Arbitrarily I have chosen to use a distinctive 'SM' for the
+ first two bytes.
+
+ The remaining two bytes encode the session 'id_num' (see above).
+ Our caller (session.c) should note our 16-bit limitation.
+
+****************************************************************************/
+
+#ifndef WITH_UTMP
+/*
+ * Not WITH_UTMP? Simply supply dummy routines.
+ */
+
+void sys_utmp_claim(const char *username, const char *hostname,
+ struct in_addr *ipaddr,
+ const char *id_str, int id_num)
+{}
+
+void sys_utmp_yield(const char *username, const char *hostname,
+ struct in_addr *ipaddr,
+ const char *id_str, int id_num)
+{}
+
+#else /* WITH_UTMP */
+
+#include <utmp.h>
+
+#ifdef HAVE_UTMPX_H
+#include <utmpx.h>
+#endif
+
+/* BSD systems: some may need lastlog.h (SunOS 4), some may not (FreeBSD) */
+/* Some System-V systems (e.g. Solaris 2) declare this too. */
+#ifdef HAVE_LASTLOG_H
+#include <lastlog.h>
+#endif
+
+/****************************************************************************
+ Default paths to various {u,w}tmp{,x} files.
+****************************************************************************/
+
+#ifdef HAVE_UTMPX_H
+
+static const char *ux_pathname =
+# if defined (UTMPX_FILE)
+ UTMPX_FILE ;
+# elif defined (_UTMPX_FILE)
+ _UTMPX_FILE ;
+# elif defined (_PATH_UTMPX)
+ _PATH_UTMPX ;
+# else
+ "" ;
+# endif
+
+static const char *wx_pathname =
+# if defined (WTMPX_FILE)
+ WTMPX_FILE ;
+# elif defined (_WTMPX_FILE)
+ _WTMPX_FILE ;
+# elif defined (_PATH_WTMPX)
+ _PATH_WTMPX ;
+# else
+ "" ;
+# endif
+
+#endif /* HAVE_UTMPX_H */
+
+static const char *ut_pathname =
+# if defined (UTMP_FILE)
+ UTMP_FILE ;
+# elif defined (_UTMP_FILE)
+ _UTMP_FILE ;
+# elif defined (_PATH_UTMP)
+ _PATH_UTMP ;
+# else
+ "" ;
+# endif
+
+static const char *wt_pathname =
+# if defined (WTMP_FILE)
+ WTMP_FILE ;
+# elif defined (_WTMP_FILE)
+ _WTMP_FILE ;
+# elif defined (_PATH_WTMP)
+ _PATH_WTMP ;
+# else
+ "" ;
+# endif
+
+/* BSD-like systems might want "lastlog" support. */
+/* *** Not yet implemented */
+#ifndef HAVE_PUTUTLINE /* see "pututline_my()" */
+static const char *ll_pathname =
+# if defined (_PATH_LASTLOG) /* what other names (if any?) */
+ _PATH_LASTLOG ;
+# else
+ "" ;
+# endif /* _PATH_LASTLOG */
+#endif /* HAVE_PUTUTLINE */
+
+/*
+ * Get name of {u,w}tmp{,x} file.
+ * return: fname contains filename
+ * Possibly empty if this code not yet ported to this system.
+ *
+ * utmp{,x}: try "utmp dir", then default (a define)
+ * wtmp{,x}: try "wtmp dir", then "utmp dir", then default (a define)
+ */
+static void uw_pathname(pstring fname, const char *uw_name, const char *uw_default)
+{
+ pstring dirname;
+
+ pstrcpy(dirname, "");
+
+ /* For w-files, first look for explicit "wtmp dir" */
+ if (uw_name[0] == 'w') {
+ pstrcpy(dirname,lp_wtmpdir());
+ trim_char(dirname,'\0','/');
+ }
+
+ /* For u-files and non-explicit w-dir, look for "utmp dir" */
+ if (dirname == 0 || strlen(dirname) == 0) {
+ pstrcpy(dirname,lp_utmpdir());
+ trim_char(dirname,'\0','/');
+ }
+
+ /* If explicit directory above, use it */
+ if (dirname != 0 && strlen(dirname) != 0) {
+ pstrcpy(fname, dirname);
+ pstrcat(fname, "/");
+ pstrcat(fname, uw_name);
+ return;
+ }
+
+ /* No explicit directory: attempt to use default paths */
+ if (strlen(uw_default) == 0) {
+ /* No explicit setting, no known default.
+ * Has it yet been ported to this OS?
+ */
+ DEBUG(2,("uw_pathname: unable to determine pathname\n"));
+ }
+ pstrcpy(fname, uw_default);
+}
+
+#ifndef HAVE_PUTUTLINE
+
+/****************************************************************************
+ Update utmp file directly. No subroutine interface: probably a BSD system.
+****************************************************************************/
+
+static void pututline_my(pstring uname, struct utmp *u, BOOL claim)
+{
+ DEBUG(1,("pututline_my: not yet implemented\n"));
+ /* BSD implementor: may want to consider (or not) adjusting "lastlog" */
+}
+#endif /* HAVE_PUTUTLINE */
+
+#ifndef HAVE_UPDWTMP
+
+/****************************************************************************
+ Update wtmp file directly. No subroutine interface: probably a BSD system.
+ Credit: Michail Vidiassov <master@iaas.msu.ru>
+****************************************************************************/
+
+static void updwtmp_my(pstring wname, struct utmp *u, BOOL claim)
+{
+ int fd;
+ struct stat buf;
+
+ if (! claim) {
+ /*
+ * BSD-like systems:
+ * may use empty ut_name to distinguish a logout record.
+ *
+ * May need "if defined(SUNOS4)" etc. around some of these,
+ * but try to avoid if possible.
+ *
+ * SunOS 4:
+ * man page indicates ut_name and ut_host both NULL
+ * FreeBSD 4.0:
+ * man page appears not to specify (hints non-NULL)
+ * A correspondent suggest at least ut_name should be NULL
+ */
+#if defined(HAVE_UT_UT_NAME)
+ memset((char *)&u->ut_name, '\0', sizeof(u->ut_name));
+#endif
+#if defined(HAVE_UT_UT_HOST)
+ memset((char *)&u->ut_host, '\0', sizeof(u->ut_host));
+#endif
+ }
+ /* Stolen from logwtmp function in libutil.
+ * May be more locking/blocking is needed?
+ */
+ if ((fd = open(wname, O_WRONLY|O_APPEND, 0)) < 0)
+ return;
+ if (fstat(fd, &buf) == 0) {
+ if (write(fd, (char *)u, sizeof(struct utmp)) != sizeof(struct utmp))
+ (void) ftruncate(fd, buf.st_size);
+ }
+ (void) close(fd);
+}
+#endif /* HAVE_UPDWTMP */
+
+/****************************************************************************
+ Update via utmp/wtmp (not utmpx/wtmpx).
+****************************************************************************/
+
+static void utmp_nox_update(struct utmp *u, BOOL claim)
+{
+ pstring uname, wname;
+#if defined(PUTUTLINE_RETURNS_UTMP)
+ struct utmp *urc;
+#endif /* PUTUTLINE_RETURNS_UTMP */
+
+ uw_pathname(uname, "utmp", ut_pathname);
+ DEBUG(2,("utmp_nox_update: uname:%s\n", uname));
+
+#ifdef HAVE_PUTUTLINE
+ if (strlen(uname) != 0) {
+ utmpname(uname);
+ }
+
+# if defined(PUTUTLINE_RETURNS_UTMP)
+ setutent();
+ urc = pututline(u);
+ endutent();
+ if (urc == NULL) {
+ DEBUG(2,("utmp_nox_update: pututline() failed\n"));
+ return;
+ }
+# else /* PUTUTLINE_RETURNS_UTMP */
+ setutent();
+ pututline(u);
+ endutent();
+# endif /* PUTUTLINE_RETURNS_UTMP */
+
+#else /* HAVE_PUTUTLINE */
+ if (strlen(uname) != 0) {
+ pututline_my(uname, u, claim);
+ }
+#endif /* HAVE_PUTUTLINE */
+
+ uw_pathname(wname, "wtmp", wt_pathname);
+ DEBUG(2,("utmp_nox_update: wname:%s\n", wname));
+ if (strlen(wname) != 0) {
+#ifdef HAVE_UPDWTMP
+ updwtmp(wname, u);
+ /*
+ * updwtmp() and the newer updwtmpx() may be unsymmetrical.
+ * At least one OS, Solaris 2.x declares the former in the
+ * "utmpx" (latter) file and context.
+ * In the Solaris case this is irrelevant: it has both and
+ * we always prefer the "x" case, so doesn't come here.
+ * But are there other systems, with no "x", which lack
+ * updwtmp() perhaps?
+ */
+#else
+ updwtmp_my(wname, u, claim);
+#endif /* HAVE_UPDWTMP */
+ }
+}
+
+/****************************************************************************
+ Copy a string in the utmp structure.
+****************************************************************************/
+
+static void utmp_strcpy(char *dest, const char *src, size_t n)
+{
+ size_t len = 0;
+
+ memset(dest, '\0', n);
+ if (src)
+ len = strlen(src);
+ if (len >= n) {
+ memcpy(dest, src, n);
+ } else {
+ if (len)
+ memcpy(dest, src, len);
+ }
+}
+
+/****************************************************************************
+ Update via utmpx/wtmpx (preferred) or via utmp/wtmp.
+****************************************************************************/
+
+static void sys_utmp_update(struct utmp *u, const char *hostname, BOOL claim)
+{
+#if !defined(HAVE_UTMPX_H)
+ /* No utmpx stuff. Drop to non-x stuff */
+ utmp_nox_update(u, claim);
+#elif !defined(HAVE_PUTUTXLINE)
+ /* Odd. Have utmpx.h but no "pututxline()". Drop to non-x stuff */
+ DEBUG(1,("utmp_update: have utmpx.h but no pututxline() function\n"));
+ utmp_nox_update(u, claim);
+#elif !defined(HAVE_GETUTMPX)
+ /* Odd. Have utmpx.h but no "getutmpx()". Drop to non-x stuff */
+ DEBUG(1,("utmp_update: have utmpx.h but no getutmpx() function\n"));
+ utmp_nox_update(u, claim);
+#else
+ pstring uname, wname;
+ struct utmpx ux, *uxrc;
+
+ getutmpx(u, &ux);
+
+#if defined(HAVE_UX_UT_SYSLEN)
+ if (hostname)
+ ux.ut_syslen = strlen(hostname) + 1; /* include end NULL */
+ else
+ ux.ut_syslen = 0;
+#endif
+#if defined(HAVE_UT_UT_HOST)
+ utmp_strcpy(ux.ut_host, hostname, sizeof(ux.ut_host));
+#endif
+
+ uw_pathname(uname, "utmpx", ux_pathname);
+ uw_pathname(wname, "wtmpx", wx_pathname);
+ DEBUG(2,("utmp_update: uname:%s wname:%s\n", uname, wname));
+ /*
+ * Check for either uname or wname being empty.
+ * Some systems, such as Redhat 6, have a "utmpx.h" which doesn't
+ * define default filenames.
+ * Also, our local installation has not provided an override.
+ * Drop to non-x method. (E.g. RH6 has good defaults in "utmp.h".)
+ */
+ if ((strlen(uname) == 0) || (strlen(wname) == 0)) {
+ utmp_nox_update(u, claim);
+ } else {
+ utmpxname(uname);
+ setutxent();
+ uxrc = pututxline(&ux);
+ endutxent();
+ if (uxrc == NULL) {
+ DEBUG(2,("utmp_update: pututxline() failed\n"));
+ return;
+ }
+ updwtmpx(wname, &ux);
+ }
+#endif /* HAVE_UTMPX_H */
+}
+
+#if defined(HAVE_UT_UT_ID)
+/****************************************************************************
+ Encode the unique connection number into "ut_id".
+****************************************************************************/
+
+static int ut_id_encode(int i, char *fourbyte)
+{
+ int nbase;
+ const char *ut_id_encstr = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
+
+ fourbyte[0] = 'S';
+ fourbyte[1] = 'M';
+
+/*
+ * Encode remaining 2 bytes from 'i'.
+ * 'ut_id_encstr' is the character set on which modulo arithmetic is done.
+ * Example: digits would produce the base-10 numbers from '001'.
+ */
+ nbase = strlen(ut_id_encstr);
+
+ fourbyte[3] = ut_id_encstr[i % nbase];
+ i /= nbase;
+ fourbyte[2] = ut_id_encstr[i % nbase];
+ i /= nbase;
+
+ return(i); /* 0: good; else overflow */
+}
+#endif /* defined(HAVE_UT_UT_ID) */
+
+
+/*
+ fill a system utmp structure given all the info we can gather
+*/
+static BOOL sys_utmp_fill(struct utmp *u,
+ const char *username, const char *hostname,
+ struct in_addr *ipaddr,
+ const char *id_str, int id_num)
+{
+ struct timeval timeval;
+
+ /*
+ * ut_name, ut_user:
+ * Several (all?) systems seems to define one as the other.
+ * It is easier and clearer simply to let the following take its course,
+ * rather than to try to detect and optimise.
+ */
+#if defined(HAVE_UT_UT_USER)
+ utmp_strcpy(u->ut_user, username, sizeof(u->ut_user));
+#elif defined(HAVE_UT_UT_NAME)
+ utmp_strcpy(u->ut_name, username, sizeof(u->ut_name));
+#endif
+
+ /*
+ * ut_line:
+ * If size limit proves troublesome, then perhaps use "ut_id_encode()".
+ */
+ if (strlen(id_str) > sizeof(u->ut_line)) {
+ DEBUG(1,("id_str [%s] is too long for %lu char utmp field\n",
+ id_str, (unsigned long)sizeof(u->ut_line)));
+ return False;
+ }
+ utmp_strcpy(u->ut_line, id_str, sizeof(u->ut_line));
+
+#if defined(HAVE_UT_UT_PID)
+ u->ut_pid = sys_getpid();
+#endif
+
+/*
+ * ut_time, ut_tv:
+ * Some have one, some the other. Many have both, but defined (aliased).
+ * It is easier and clearer simply to let the following take its course.
+ * But note that we do the more precise ut_tv as the final assignment.
+ */
+#if defined(HAVE_UT_UT_TIME)
+ gettimeofday(&timeval, NULL);
+ u->ut_time = timeval.tv_sec;
+#elif defined(HAVE_UT_UT_TV)
+ gettimeofday(&timeval, NULL);
+ u->ut_tv = timeval;
+#else
+#error "with-utmp must have UT_TIME or UT_TV"
+#endif
+
+#if defined(HAVE_UT_UT_HOST)
+ utmp_strcpy(u->ut_host, hostname, sizeof(u->ut_host));
+#endif
+#if defined(HAVE_UT_UT_ADDR)
+ if (ipaddr)
+ u->ut_addr = ipaddr->s_addr;
+ /*
+ * "(unsigned long) ut_addr" apparently exists on at least HP-UX 10.20.
+ * Volunteer to implement, please ...
+ */
+#endif
+
+#if defined(HAVE_UT_UT_ID)
+ if (ut_id_encode(id_num, u->ut_id) != 0) {
+ DEBUG(1,("utmp_fill: cannot encode id %d\n", id_num));
+ return False;
+ }
+#endif
+
+ return True;
+}
+
+/****************************************************************************
+ Close a connection.
+****************************************************************************/
+
+void sys_utmp_yield(const char *username, const char *hostname,
+ struct in_addr *ipaddr,
+ const char *id_str, int id_num)
+{
+ struct utmp u;
+
+ ZERO_STRUCT(u);
+
+#if defined(HAVE_UT_UT_EXIT)
+ u.ut_exit.e_termination = 0;
+ u.ut_exit.e_exit = 0;
+#endif
+
+#if defined(HAVE_UT_UT_TYPE)
+ u.ut_type = DEAD_PROCESS;
+#endif
+
+ if (!sys_utmp_fill(&u, username, hostname, ipaddr, id_str, id_num)) return;
+
+ sys_utmp_update(&u, NULL, False);
+}
+
+/****************************************************************************
+ Claim a entry in whatever utmp system the OS uses.
+****************************************************************************/
+
+void sys_utmp_claim(const char *username, const char *hostname,
+ struct in_addr *ipaddr,
+ const char *id_str, int id_num)
+{
+ struct utmp u;
+
+ ZERO_STRUCT(u);
+
+#if defined(HAVE_UT_UT_TYPE)
+ u.ut_type = USER_PROCESS;
+#endif
+
+ if (!sys_utmp_fill(&u, username, hostname, ipaddr, id_str, id_num)) return;
+
+ sys_utmp_update(&u, hostname, True);
+}
+
+#endif /* WITH_UTMP */
diff --git a/source/smbd/vfs-wrap.c b/source/smbd/vfs-wrap.c
new file mode 100644
index 00000000000..5393dfc7556
--- /dev/null
+++ b/source/smbd/vfs-wrap.c
@@ -0,0 +1,1031 @@
+/*
+ Unix SMB/CIFS implementation.
+ Wrap disk only vfs functions to sidestep dodgy compilers.
+ Copyright (C) Tim Potter 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 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"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_VFS
+
+
+/* Check for NULL pointer parameters in vfswrap_* functions */
+
+/* We don't want to have NULL function pointers lying around. Someone
+ is sure to try and execute them. These stubs are used to prevent
+ this possibility. */
+
+int vfswrap_dummy_connect(vfs_handle_struct *handle, connection_struct *conn, const char *service, const char *user)
+{
+ return 0; /* Return >= 0 for success */
+}
+
+void vfswrap_dummy_disconnect(vfs_handle_struct *handle, connection_struct *conn)
+{
+}
+
+/* Disk operations */
+
+SMB_BIG_UINT vfswrap_disk_free(vfs_handle_struct *handle, connection_struct *conn, const char *path, BOOL small_query, SMB_BIG_UINT *bsize,
+ SMB_BIG_UINT *dfree, SMB_BIG_UINT *dsize)
+{
+ SMB_BIG_UINT result;
+
+ result = sys_disk_free(path, small_query, bsize, dfree, dsize);
+ return result;
+}
+
+int vfswrap_get_quota(struct vfs_handle_struct *handle, struct connection_struct *conn, enum SMB_QUOTA_TYPE qtype, unid_t id, SMB_DISK_QUOTA *qt)
+{
+#ifdef HAVE_SYS_QUOTAS
+ int result;
+
+ START_PROFILE(syscall_get_quota);
+ result = sys_get_quota(conn->connectpath, qtype, id, qt);
+ END_PROFILE(syscall_get_quota);
+ return result;
+#else
+ errno = ENOSYS;
+ return -1;
+#endif
+}
+
+int vfswrap_set_quota(struct vfs_handle_struct *handle, struct connection_struct *conn, enum SMB_QUOTA_TYPE qtype, unid_t id, SMB_DISK_QUOTA *qt)
+{
+#ifdef HAVE_SYS_QUOTAS
+ int result;
+
+ START_PROFILE(syscall_set_quota);
+ result = sys_set_quota(conn->connectpath, qtype, id, qt);
+ END_PROFILE(syscall_set_quota);
+ return result;
+#else
+ errno = ENOSYS;
+ return -1;
+#endif
+}
+
+int vfswrap_get_shadow_copy_data(struct vfs_handle_struct *handle, struct files_struct *fsp, SHADOW_COPY_DATA *shadow_copy_data, BOOL labels)
+{
+ errno = ENOSYS;
+ return -1; /* Not implemented. */
+}
+
+/* Directory operations */
+
+DIR *vfswrap_opendir(vfs_handle_struct *handle, connection_struct *conn, const char *fname)
+{
+ DIR *result;
+
+ START_PROFILE(syscall_opendir);
+ result = opendir(fname);
+ END_PROFILE(syscall_opendir);
+ return result;
+}
+
+struct dirent *vfswrap_readdir(vfs_handle_struct *handle, connection_struct *conn, DIR *dirp)
+{
+ struct dirent *result;
+
+ START_PROFILE(syscall_readdir);
+ result = readdir(dirp);
+ END_PROFILE(syscall_readdir);
+ return result;
+}
+
+int vfswrap_mkdir(vfs_handle_struct *handle, connection_struct *conn, const char *path, mode_t mode)
+{
+ int result;
+ BOOL has_dacl = False;
+
+ START_PROFILE(syscall_mkdir);
+
+ if (lp_inherit_acls(SNUM(conn)) && (has_dacl = directory_has_default_acl(conn, parent_dirname(path))))
+ mode = 0777;
+
+ result = mkdir(path, mode);
+
+ if (result == 0 && !has_dacl) {
+ /*
+ * We need to do this as the default behavior of POSIX ACLs
+ * is to set the mask to be the requested group permission
+ * bits, not the group permission bits to be the requested
+ * group permission bits. This is not what we want, as it will
+ * mess up any inherited ACL bits that were set. JRA.
+ */
+ int saved_errno = errno; /* We may get ENOSYS */
+ if ((SMB_VFS_CHMOD_ACL(conn, path, mode) == -1) && (errno == ENOSYS))
+ errno = saved_errno;
+ }
+
+ END_PROFILE(syscall_mkdir);
+ return result;
+}
+
+int vfswrap_rmdir(vfs_handle_struct *handle, connection_struct *conn, const char *path)
+{
+ int result;
+
+ START_PROFILE(syscall_rmdir);
+ result = rmdir(path);
+ END_PROFILE(syscall_rmdir);
+ return result;
+}
+
+int vfswrap_closedir(vfs_handle_struct *handle, connection_struct *conn, DIR *dirp)
+{
+ int result;
+
+ START_PROFILE(syscall_closedir);
+ result = closedir(dirp);
+ END_PROFILE(syscall_closedir);
+ return result;
+}
+
+/* File operations */
+
+int vfswrap_open(vfs_handle_struct *handle, connection_struct *conn, const char *fname, int flags, mode_t mode)
+{
+ int result;
+
+ START_PROFILE(syscall_open);
+ result = sys_open(fname, flags, mode);
+ END_PROFILE(syscall_open);
+ return result;
+}
+
+int vfswrap_close(vfs_handle_struct *handle, files_struct *fsp, int fd)
+{
+ int result;
+
+ START_PROFILE(syscall_close);
+
+ result = close(fd);
+ END_PROFILE(syscall_close);
+ return result;
+}
+
+ssize_t vfswrap_read(vfs_handle_struct *handle, files_struct *fsp, int fd, void *data, size_t n)
+{
+ ssize_t result;
+
+ START_PROFILE_BYTES(syscall_read, n);
+ result = sys_read(fd, data, n);
+ END_PROFILE(syscall_read);
+ return result;
+}
+
+ssize_t vfswrap_pread(vfs_handle_struct *handle, files_struct *fsp, int fd, void *data,
+ size_t n, SMB_OFF_T offset)
+{
+ ssize_t result;
+
+#if defined(HAVE_PREAD) || defined(HAVE_PREAD64)
+ START_PROFILE_BYTES(syscall_pread, n);
+ result = sys_pread(fd, data, n, offset);
+ END_PROFILE(syscall_pread);
+
+ if (result == -1 && errno == ESPIPE) {
+ /* Maintain the fiction that pipes can be seeked (sought?) on. */
+ result = SMB_VFS_READ(fsp, fd, data, n);
+ fsp->pos = 0;
+ }
+
+#else /* HAVE_PREAD */
+ SMB_OFF_T curr;
+ int lerrno;
+
+ curr = SMB_VFS_LSEEK(fsp, fd, 0, SEEK_CUR);
+ if (curr == -1 && errno == ESPIPE) {
+ /* Maintain the fiction that pipes can be seeked (sought?) on. */
+ result = SMB_VFS_READ(fsp, fd, data, n);
+ fsp->pos = 0;
+ return result;
+ }
+
+ if (SMB_VFS_LSEEK(fsp, fd, offset, SEEK_SET) == -1) {
+ return -1;
+ }
+
+ errno = 0;
+ result = SMB_VFS_READ(fsp, fd, data, n);
+ lerrno = errno;
+
+ SMB_VFS_LSEEK(fsp, fd, curr, SEEK_SET);
+ errno = lerrno;
+
+#endif /* HAVE_PREAD */
+
+ return result;
+}
+
+ssize_t vfswrap_write(vfs_handle_struct *handle, files_struct *fsp, int fd, const void *data, size_t n)
+{
+ ssize_t result;
+
+ START_PROFILE_BYTES(syscall_write, n);
+ result = sys_write(fd, data, n);
+ END_PROFILE(syscall_write);
+ return result;
+}
+
+ssize_t vfswrap_pwrite(vfs_handle_struct *handle, files_struct *fsp, int fd, const void *data,
+ size_t n, SMB_OFF_T offset)
+{
+ ssize_t result;
+
+#if defined(HAVE_PWRITE) || defined(HAVE_PRWITE64)
+ START_PROFILE_BYTES(syscall_pwrite, n);
+ result = sys_pwrite(fd, data, n, offset);
+ END_PROFILE(syscall_pwrite);
+
+ if (result == -1 && errno == ESPIPE) {
+ /* Maintain the fiction that pipes can be sought on. */
+ result = SMB_VFS_WRITE(fsp, fd, data, n);
+ }
+
+#else /* HAVE_PWRITE */
+ SMB_OFF_T curr;
+ int lerrno;
+
+ curr = SMB_VFS_LSEEK(fsp, fd, 0, SEEK_CUR);
+ if (curr == -1) {
+ return -1;
+ }
+
+ if (SMB_VFS_LSEEK(fsp, fd, offset, SEEK_SET) == -1) {
+ return -1;
+ }
+
+ result = SMB_VFS_WRITE(fsp, fd, data, n);
+ lerrno = errno;
+
+ SMB_VFS_LSEEK(fsp, fd, curr, SEEK_SET);
+ errno = lerrno;
+
+#endif /* HAVE_PWRITE */
+
+ return result;
+}
+
+SMB_OFF_T vfswrap_lseek(vfs_handle_struct *handle, files_struct *fsp, int filedes, SMB_OFF_T offset, int whence)
+{
+ SMB_OFF_T result = 0;
+
+ START_PROFILE(syscall_lseek);
+
+ /* Cope with 'stat' file opens. */
+ if (filedes != -1)
+ result = sys_lseek(filedes, offset, whence);
+
+ /*
+ * We want to maintain the fiction that we can seek
+ * on a fifo for file system purposes. This allows
+ * people to set up UNIX fifo's that feed data to Windows
+ * applications. JRA.
+ */
+
+ if((result == -1) && (errno == ESPIPE)) {
+ result = 0;
+ errno = 0;
+ }
+
+ END_PROFILE(syscall_lseek);
+ return result;
+}
+
+ssize_t vfswrap_sendfile(vfs_handle_struct *handle, int tofd, files_struct *fsp, int fromfd, const DATA_BLOB *hdr,
+ SMB_OFF_T offset, size_t n)
+{
+ ssize_t result;
+
+ START_PROFILE_BYTES(syscall_sendfile, n);
+ result = sys_sendfile(tofd, fromfd, hdr, offset, n);
+ END_PROFILE(syscall_sendfile);
+ return result;
+}
+
+/*********************************************************
+ For rename across filesystems Patch from Warren Birnbaum
+ <warrenb@hpcvscdp.cv.hp.com>
+**********************************************************/
+
+static int copy_reg(const char *source, const char *dest)
+{
+ SMB_STRUCT_STAT source_stats;
+ int saved_errno;
+ int ifd = -1;
+ int ofd = -1;
+
+ if (sys_lstat (source, &source_stats) == -1)
+ return -1;
+
+ if (!S_ISREG (source_stats.st_mode))
+ return -1;
+
+ if((ifd = sys_open (source, O_RDONLY, 0)) < 0)
+ return -1;
+
+ if (unlink (dest) && errno != ENOENT)
+ return -1;
+
+#ifdef O_NOFOLLOW
+ if((ofd = sys_open (dest, O_WRONLY | O_CREAT | O_TRUNC | O_NOFOLLOW, 0600)) < 0 )
+#else
+ if((ofd = sys_open (dest, O_WRONLY | O_CREAT | O_TRUNC , 0600)) < 0 )
+#endif
+ goto err;
+
+ if (transfer_file(ifd, ofd, (size_t)-1) == -1)
+ goto err;
+
+ /*
+ * Try to preserve ownership. For non-root it might fail, but that's ok.
+ * But root probably wants to know, e.g. if NFS disallows it.
+ */
+
+#ifdef HAVE_FCHOWN
+ if ((fchown(ofd, source_stats.st_uid, source_stats.st_gid) == -1) && (errno != EPERM))
+#else
+ if ((chown(dest, source_stats.st_uid, source_stats.st_gid) == -1) && (errno != EPERM))
+#endif
+ goto err;
+
+ /*
+ * fchown turns off set[ug]id bits for non-root,
+ * so do the chmod last.
+ */
+
+#if defined(HAVE_FCHMOD)
+ if (fchmod (ofd, source_stats.st_mode & 07777))
+#else
+ if (chmod (dest, source_stats.st_mode & 07777))
+#endif
+ goto err;
+
+ if (close (ifd) == -1)
+ goto err;
+
+ if (close (ofd) == -1)
+ return -1;
+
+ /* Try to copy the old file's modtime and access time. */
+ {
+ struct utimbuf tv;
+
+ tv.actime = source_stats.st_atime;
+ tv.modtime = source_stats.st_mtime;
+ utime(dest, &tv);
+ }
+
+ if (unlink (source) == -1)
+ return -1;
+
+ return 0;
+
+ err:
+
+ saved_errno = errno;
+ if (ifd != -1)
+ close(ifd);
+ if (ofd != -1)
+ close(ofd);
+ errno = saved_errno;
+ return -1;
+}
+
+int vfswrap_rename(vfs_handle_struct *handle, connection_struct *conn, const char *old, const char *new)
+{
+ int result;
+
+ START_PROFILE(syscall_rename);
+ result = rename(old, new);
+ if (errno == EXDEV) {
+ /* Rename across filesystems needed. */
+ result = copy_reg(old, new);
+ }
+
+ END_PROFILE(syscall_rename);
+ return result;
+}
+
+int vfswrap_fsync(vfs_handle_struct *handle, files_struct *fsp, int fd)
+{
+#ifdef HAVE_FSYNC
+ int result;
+
+ START_PROFILE(syscall_fsync);
+ result = fsync(fd);
+ END_PROFILE(syscall_fsync);
+ return result;
+#else
+ return 0;
+#endif
+}
+
+int vfswrap_stat(vfs_handle_struct *handle, connection_struct *conn, const char *fname, SMB_STRUCT_STAT *sbuf)
+{
+ int result;
+
+ START_PROFILE(syscall_stat);
+ result = sys_stat(fname, sbuf);
+ END_PROFILE(syscall_stat);
+ return result;
+}
+
+int vfswrap_fstat(vfs_handle_struct *handle, files_struct *fsp, int fd, SMB_STRUCT_STAT *sbuf)
+{
+ int result;
+
+ START_PROFILE(syscall_fstat);
+ result = sys_fstat(fd, sbuf);
+ END_PROFILE(syscall_fstat);
+ return result;
+}
+
+int vfswrap_lstat(vfs_handle_struct *handle, connection_struct *conn, const char *path, SMB_STRUCT_STAT *sbuf)
+{
+ int result;
+
+ START_PROFILE(syscall_lstat);
+ result = sys_lstat(path, sbuf);
+ END_PROFILE(syscall_lstat);
+ return result;
+}
+
+int vfswrap_unlink(vfs_handle_struct *handle, connection_struct *conn, const char *path)
+{
+ int result;
+
+ START_PROFILE(syscall_unlink);
+ result = unlink(path);
+ END_PROFILE(syscall_unlink);
+ return result;
+}
+
+int vfswrap_chmod(vfs_handle_struct *handle, connection_struct *conn, const char *path, mode_t mode)
+{
+ int result;
+
+ START_PROFILE(syscall_chmod);
+
+ /*
+ * We need to do this due to the fact that the default POSIX ACL
+ * chmod modifies the ACL *mask* for the group owner, not the
+ * group owner bits directly. JRA.
+ */
+
+
+ {
+ int saved_errno = errno; /* We might get ENOSYS */
+ if ((result = SMB_VFS_CHMOD_ACL(conn, path, mode)) == 0) {
+ END_PROFILE(syscall_chmod);
+ return result;
+ }
+ /* Error - return the old errno. */
+ errno = saved_errno;
+ }
+
+ result = chmod(path, mode);
+ END_PROFILE(syscall_chmod);
+ return result;
+}
+
+int vfswrap_fchmod(vfs_handle_struct *handle, files_struct *fsp, int fd, mode_t mode)
+{
+ int result;
+
+ START_PROFILE(syscall_fchmod);
+
+ /*
+ * We need to do this due to the fact that the default POSIX ACL
+ * chmod modifies the ACL *mask* for the group owner, not the
+ * group owner bits directly. JRA.
+ */
+
+ {
+ int saved_errno = errno; /* We might get ENOSYS */
+ if ((result = SMB_VFS_FCHMOD_ACL(fsp, fd, mode)) == 0) {
+ END_PROFILE(syscall_chmod);
+ return result;
+ }
+ /* Error - return the old errno. */
+ errno = saved_errno;
+ }
+
+#if defined(HAVE_FCHMOD)
+ result = fchmod(fd, mode);
+#else
+ result = -1;
+ errno = ENOSYS;
+#endif
+
+ END_PROFILE(syscall_fchmod);
+ return result;
+}
+
+int vfswrap_chown(vfs_handle_struct *handle, connection_struct *conn, const char *path, uid_t uid, gid_t gid)
+{
+ int result;
+
+ START_PROFILE(syscall_chown);
+ result = sys_chown(path, uid, gid);
+ END_PROFILE(syscall_chown);
+ return result;
+}
+
+int vfswrap_fchown(vfs_handle_struct *handle, files_struct *fsp, int fd, uid_t uid, gid_t gid)
+{
+#ifdef HAVE_FCHOWN
+ int result;
+
+ START_PROFILE(syscall_fchown);
+ result = fchown(fd, uid, gid);
+ END_PROFILE(syscall_fchown);
+ return result;
+#else
+ errno = ENOSYS;
+ return -1;
+#endif
+}
+
+int vfswrap_chdir(vfs_handle_struct *handle, connection_struct *conn, const char *path)
+{
+ int result;
+
+ START_PROFILE(syscall_chdir);
+ result = chdir(path);
+ END_PROFILE(syscall_chdir);
+ return result;
+}
+
+char *vfswrap_getwd(vfs_handle_struct *handle, connection_struct *conn, char *path)
+{
+ char *result;
+
+ START_PROFILE(syscall_getwd);
+ result = sys_getwd(path);
+ END_PROFILE(syscall_getwd);
+ return result;
+}
+
+int vfswrap_utime(vfs_handle_struct *handle, connection_struct *conn, const char *path, struct utimbuf *times)
+{
+ int result;
+
+ START_PROFILE(syscall_utime);
+ result = utime(path, times);
+ END_PROFILE(syscall_utime);
+ return result;
+}
+
+/*********************************************************************
+ A version of ftruncate that will write the space on disk if strict
+ allocate is set.
+**********************************************************************/
+
+static int strict_allocate_ftruncate(vfs_handle_struct *handle, files_struct *fsp, int fd, SMB_OFF_T len)
+{
+ SMB_STRUCT_STAT st;
+ SMB_OFF_T currpos = SMB_VFS_LSEEK(fsp, fd, 0, SEEK_CUR);
+ unsigned char zero_space[4096];
+ SMB_OFF_T space_to_write;
+
+ if (currpos == -1)
+ return -1;
+
+ if (SMB_VFS_FSTAT(fsp, fd, &st) == -1)
+ return -1;
+
+ space_to_write = len - st.st_size;
+
+#ifdef S_ISFIFO
+ if (S_ISFIFO(st.st_mode))
+ return 0;
+#endif
+
+ if (st.st_size == len)
+ return 0;
+
+ /* Shrink - just ftruncate. */
+ if (st.st_size > len)
+ return sys_ftruncate(fd, len);
+
+ /* Write out the real space on disk. */
+ if (SMB_VFS_LSEEK(fsp, fd, st.st_size, SEEK_SET) != st.st_size)
+ return -1;
+
+ space_to_write = len - st.st_size;
+
+ memset(zero_space, '\0', sizeof(zero_space));
+ while ( space_to_write > 0) {
+ SMB_OFF_T retlen;
+ SMB_OFF_T current_len_to_write = MIN(sizeof(zero_space),space_to_write);
+
+ retlen = SMB_VFS_WRITE(fsp,fsp->fd,(char *)zero_space,current_len_to_write);
+ if (retlen <= 0)
+ return -1;
+
+ space_to_write -= retlen;
+ }
+
+ /* Seek to where we were */
+ if (SMB_VFS_LSEEK(fsp, fd, currpos, SEEK_SET) != currpos)
+ return -1;
+
+ return 0;
+}
+
+int vfswrap_ftruncate(vfs_handle_struct *handle, files_struct *fsp, int fd, SMB_OFF_T len)
+{
+ int result = -1;
+ SMB_STRUCT_STAT st;
+ char c = 0;
+ SMB_OFF_T currpos;
+
+ START_PROFILE(syscall_ftruncate);
+
+ if (lp_strict_allocate(SNUM(fsp->conn))) {
+ result = strict_allocate_ftruncate(handle, fsp, fd, len);
+ END_PROFILE(syscall_ftruncate);
+ return result;
+ }
+
+ /* we used to just check HAVE_FTRUNCATE_EXTEND and only use
+ sys_ftruncate if the system supports it. Then I discovered that
+ you can have some filesystems that support ftruncate
+ expansion and some that don't! On Linux fat can't do
+ ftruncate extend but ext2 can. */
+
+ result = sys_ftruncate(fd, len);
+ if (result == 0)
+ goto done;
+
+ /* According to W. R. Stevens advanced UNIX prog. Pure 4.3 BSD cannot
+ extend a file with ftruncate. Provide alternate implementation
+ for this */
+ currpos = SMB_VFS_LSEEK(fsp, fd, 0, SEEK_CUR);
+ if (currpos == -1) {
+ goto done;
+ }
+
+ /* Do an fstat to see if the file is longer than the requested
+ size in which case the ftruncate above should have
+ succeeded or shorter, in which case seek to len - 1 and
+ write 1 byte of zero */
+ if (SMB_VFS_FSTAT(fsp, fd, &st) == -1) {
+ goto done;
+ }
+
+#ifdef S_ISFIFO
+ if (S_ISFIFO(st.st_mode)) {
+ result = 0;
+ goto done;
+ }
+#endif
+
+ if (st.st_size == len) {
+ result = 0;
+ goto done;
+ }
+
+ if (st.st_size > len) {
+ /* the sys_ftruncate should have worked */
+ goto done;
+ }
+
+ if (SMB_VFS_LSEEK(fsp, fd, len-1, SEEK_SET) != len -1)
+ goto done;
+
+ if (SMB_VFS_WRITE(fsp, fd, &c, 1)!=1)
+ goto done;
+
+ /* Seek to where we were */
+ if (SMB_VFS_LSEEK(fsp, fd, currpos, SEEK_SET) != currpos)
+ goto done;
+ result = 0;
+
+ done:
+
+ END_PROFILE(syscall_ftruncate);
+ return result;
+}
+
+BOOL vfswrap_lock(vfs_handle_struct *handle, files_struct *fsp, int fd, int op, SMB_OFF_T offset, SMB_OFF_T count, int type)
+{
+ BOOL result;
+
+ START_PROFILE(syscall_fcntl_lock);
+ result = fcntl_lock(fd, op, offset, count,type);
+ END_PROFILE(syscall_fcntl_lock);
+ return result;
+}
+
+int vfswrap_symlink(vfs_handle_struct *handle, connection_struct *conn, const char *oldpath, const char *newpath)
+{
+ int result;
+
+ START_PROFILE(syscall_symlink);
+ result = sys_symlink(oldpath, newpath);
+ END_PROFILE(syscall_symlink);
+ return result;
+}
+
+int vfswrap_readlink(vfs_handle_struct *handle, connection_struct *conn, const char *path, char *buf, size_t bufsiz)
+{
+ int result;
+
+ START_PROFILE(syscall_readlink);
+ result = sys_readlink(path, buf, bufsiz);
+ END_PROFILE(syscall_readlink);
+ return result;
+}
+
+int vfswrap_link(vfs_handle_struct *handle, connection_struct *conn, const char *oldpath, const char *newpath)
+{
+ int result;
+
+ START_PROFILE(syscall_link);
+ result = sys_link(oldpath, newpath);
+ END_PROFILE(syscall_link);
+ return result;
+}
+
+int vfswrap_mknod(vfs_handle_struct *handle, connection_struct *conn, const char *pathname, mode_t mode, SMB_DEV_T dev)
+{
+ int result;
+
+ START_PROFILE(syscall_mknod);
+ result = sys_mknod(pathname, mode, dev);
+ END_PROFILE(syscall_mknod);
+ return result;
+}
+
+char *vfswrap_realpath(vfs_handle_struct *handle, connection_struct *conn, const char *path, char *resolved_path)
+{
+ char *result;
+
+ START_PROFILE(syscall_realpath);
+ result = sys_realpath(path, resolved_path);
+ END_PROFILE(syscall_realpath);
+ return result;
+}
+
+size_t vfswrap_fget_nt_acl(vfs_handle_struct *handle, files_struct *fsp, int fd, uint32 security_info, SEC_DESC **ppdesc)
+{
+ size_t result;
+
+ START_PROFILE(fget_nt_acl);
+ result = get_nt_acl(fsp, security_info, ppdesc);
+ END_PROFILE(fget_nt_acl);
+ return result;
+}
+
+size_t vfswrap_get_nt_acl(vfs_handle_struct *handle, files_struct *fsp, const char *name, uint32 security_info, SEC_DESC **ppdesc)
+{
+ size_t result;
+
+ START_PROFILE(get_nt_acl);
+ result = get_nt_acl(fsp, security_info, ppdesc);
+ END_PROFILE(get_nt_acl);
+ return result;
+}
+
+BOOL vfswrap_fset_nt_acl(vfs_handle_struct *handle, files_struct *fsp, int fd, uint32 security_info_sent, SEC_DESC *psd)
+{
+ BOOL result;
+
+ START_PROFILE(fset_nt_acl);
+ result = set_nt_acl(fsp, security_info_sent, psd);
+ END_PROFILE(fset_nt_acl);
+ return result;
+}
+
+BOOL vfswrap_set_nt_acl(vfs_handle_struct *handle, files_struct *fsp, const char *name, uint32 security_info_sent, SEC_DESC *psd)
+{
+ BOOL result;
+
+ START_PROFILE(set_nt_acl);
+ result = set_nt_acl(fsp, security_info_sent, psd);
+ END_PROFILE(set_nt_acl);
+ return result;
+}
+
+int vfswrap_chmod_acl(vfs_handle_struct *handle, connection_struct *conn, const char *name, mode_t mode)
+{
+#ifdef HAVE_NO_ACL
+ errno = ENOSYS;
+ return -1;
+#else
+ int result;
+
+ START_PROFILE(chmod_acl);
+ result = chmod_acl(conn, name, mode);
+ END_PROFILE(chmod_acl);
+ return result;
+#endif
+}
+
+int vfswrap_fchmod_acl(vfs_handle_struct *handle, files_struct *fsp, int fd, mode_t mode)
+{
+#ifdef HAVE_NO_ACL
+ errno = ENOSYS;
+ return -1;
+#else
+ int result;
+
+ START_PROFILE(fchmod_acl);
+ result = fchmod_acl(fsp, fd, mode);
+ END_PROFILE(fchmod_acl);
+ return result;
+#endif
+}
+
+int vfswrap_sys_acl_get_entry(vfs_handle_struct *handle, connection_struct *conn, SMB_ACL_T theacl, int entry_id, SMB_ACL_ENTRY_T *entry_p)
+{
+ return sys_acl_get_entry(theacl, entry_id, entry_p);
+}
+
+int vfswrap_sys_acl_get_tag_type(vfs_handle_struct *handle, connection_struct *conn, SMB_ACL_ENTRY_T entry_d, SMB_ACL_TAG_T *tag_type_p)
+{
+ return sys_acl_get_tag_type(entry_d, tag_type_p);
+}
+
+int vfswrap_sys_acl_get_permset(vfs_handle_struct *handle, connection_struct *conn, SMB_ACL_ENTRY_T entry_d, SMB_ACL_PERMSET_T *permset_p)
+{
+ return sys_acl_get_permset(entry_d, permset_p);
+}
+
+void * vfswrap_sys_acl_get_qualifier(vfs_handle_struct *handle, connection_struct *conn, SMB_ACL_ENTRY_T entry_d)
+{
+ return sys_acl_get_qualifier(entry_d);
+}
+
+SMB_ACL_T vfswrap_sys_acl_get_file(vfs_handle_struct *handle, connection_struct *conn, const char *path_p, SMB_ACL_TYPE_T type)
+{
+ return sys_acl_get_file(path_p, type);
+}
+
+SMB_ACL_T vfswrap_sys_acl_get_fd(vfs_handle_struct *handle, files_struct *fsp, int fd)
+{
+ return sys_acl_get_fd(fd);
+}
+
+int vfswrap_sys_acl_clear_perms(vfs_handle_struct *handle, connection_struct *conn, SMB_ACL_PERMSET_T permset)
+{
+ return sys_acl_clear_perms(permset);
+}
+
+int vfswrap_sys_acl_add_perm(vfs_handle_struct *handle, connection_struct *conn, SMB_ACL_PERMSET_T permset, SMB_ACL_PERM_T perm)
+{
+ return sys_acl_add_perm(permset, perm);
+}
+
+char * vfswrap_sys_acl_to_text(vfs_handle_struct *handle, connection_struct *conn, SMB_ACL_T theacl, ssize_t *plen)
+{
+ return sys_acl_to_text(theacl, plen);
+}
+
+SMB_ACL_T vfswrap_sys_acl_init(vfs_handle_struct *handle, connection_struct *conn, int count)
+{
+ return sys_acl_init(count);
+}
+
+int vfswrap_sys_acl_create_entry(vfs_handle_struct *handle, connection_struct *conn, SMB_ACL_T *pacl, SMB_ACL_ENTRY_T *pentry)
+{
+ return sys_acl_create_entry(pacl, pentry);
+}
+
+int vfswrap_sys_acl_set_tag_type(vfs_handle_struct *handle, connection_struct *conn, SMB_ACL_ENTRY_T entry, SMB_ACL_TAG_T tagtype)
+{
+ return sys_acl_set_tag_type(entry, tagtype);
+}
+
+int vfswrap_sys_acl_set_qualifier(vfs_handle_struct *handle, connection_struct *conn, SMB_ACL_ENTRY_T entry, void *qual)
+{
+ return sys_acl_set_qualifier(entry, qual);
+}
+
+int vfswrap_sys_acl_set_permset(vfs_handle_struct *handle, connection_struct *conn, SMB_ACL_ENTRY_T entry, SMB_ACL_PERMSET_T permset)
+{
+ return sys_acl_set_permset(entry, permset);
+}
+
+int vfswrap_sys_acl_valid(vfs_handle_struct *handle, connection_struct *conn, SMB_ACL_T theacl )
+{
+ return sys_acl_valid(theacl );
+}
+
+int vfswrap_sys_acl_set_file(vfs_handle_struct *handle, connection_struct *conn, const char *name, SMB_ACL_TYPE_T acltype, SMB_ACL_T theacl)
+{
+ return sys_acl_set_file(name, acltype, theacl);
+}
+
+int vfswrap_sys_acl_set_fd(vfs_handle_struct *handle, files_struct *fsp, int fd, SMB_ACL_T theacl)
+{
+ return sys_acl_set_fd(fd, theacl);
+}
+
+int vfswrap_sys_acl_delete_def_file(vfs_handle_struct *handle, connection_struct *conn, const char *path)
+{
+ return sys_acl_delete_def_file(path);
+}
+
+int vfswrap_sys_acl_get_perm(vfs_handle_struct *handle, connection_struct *conn, SMB_ACL_PERMSET_T permset, SMB_ACL_PERM_T perm)
+{
+ return sys_acl_get_perm(permset, perm);
+}
+
+int vfswrap_sys_acl_free_text(vfs_handle_struct *handle, connection_struct *conn, char *text)
+{
+ return sys_acl_free_text(text);
+}
+
+int vfswrap_sys_acl_free_acl(vfs_handle_struct *handle, connection_struct *conn, SMB_ACL_T posix_acl)
+{
+ return sys_acl_free_acl(posix_acl);
+}
+
+int vfswrap_sys_acl_free_qualifier(vfs_handle_struct *handle, connection_struct *conn, void *qualifier, SMB_ACL_TAG_T tagtype)
+{
+ return sys_acl_free_qualifier(qualifier, tagtype);
+}
+
+/****************************************************************
+ Extended attribute operations.
+*****************************************************************/
+
+ssize_t vfswrap_getxattr(struct vfs_handle_struct *handle,struct connection_struct *conn,const char *path, const char *name, void *value, size_t size)
+{
+ return sys_getxattr(path, name, value, size);
+}
+
+ssize_t vfswrap_lgetxattr(struct vfs_handle_struct *handle,struct connection_struct *conn,const char *path, const char *name, void *value, size_t size)
+{
+ return sys_lgetxattr(path, name, value, size);
+}
+
+ssize_t vfswrap_fgetxattr(struct vfs_handle_struct *handle, struct files_struct *fsp,int fd, const char *name, void *value, size_t size)
+{
+ return sys_fgetxattr(fd, name, value, size);
+}
+
+ssize_t vfswrap_listxattr(struct vfs_handle_struct *handle, struct connection_struct *conn,const char *path, char *list, size_t size)
+{
+ return sys_listxattr(path, list, size);
+}
+
+ssize_t vfswrap_llistxattr(struct vfs_handle_struct *handle, struct connection_struct *conn,const char *path, char *list, size_t size)
+{
+ return sys_llistxattr(path, list, size);
+}
+
+ssize_t vfswrap_flistxattr(struct vfs_handle_struct *handle, struct files_struct *fsp,int fd, char *list, size_t size)
+{
+ return sys_flistxattr(fd, list, size);
+}
+
+int vfswrap_removexattr(struct vfs_handle_struct *handle, struct connection_struct *conn,const char *path, const char *name)
+{
+ return sys_removexattr(path, name);
+}
+
+int vfswrap_lremovexattr(struct vfs_handle_struct *handle, struct connection_struct *conn,const char *path, const char *name)
+{
+ return sys_lremovexattr(path, name);
+}
+
+int vfswrap_fremovexattr(struct vfs_handle_struct *handle, struct files_struct *fsp,int fd, const char *name)
+{
+ return sys_fremovexattr(fd, name);
+}
+
+int vfswrap_setxattr(struct vfs_handle_struct *handle, struct connection_struct *conn,const char *path, const char *name, const void *value, size_t size, int flags)
+{
+ return sys_setxattr(path, name, value, size, flags);
+}
+
+int vfswrap_lsetxattr(struct vfs_handle_struct *handle, struct connection_struct *conn,const char *path, const char *name, const void *value, size_t size, int flags)
+{
+ return sys_lsetxattr(path, name, value, size, flags);
+}
+
+int vfswrap_fsetxattr(struct vfs_handle_struct *handle, struct files_struct *fsp,int fd, const char *name, const void *value, size_t size, int flags)
+{
+ return sys_fsetxattr(fd, name, value, size, flags);
+}
diff --git a/source/smbd/vfs.c b/source/smbd/vfs.c
new file mode 100644
index 00000000000..4f3234775a2
--- /dev/null
+++ b/source/smbd/vfs.c
@@ -0,0 +1,951 @@
+/*
+ Unix SMB/Netbios implementation.
+ Version 1.9.
+ VFS initialisation and support functions
+ Copyright (C) Tim Potter 1999
+ Copyright (C) Alexander Bokovoy 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 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.
+
+ This work was sponsored by Optifacio Software Services, Inc.
+*/
+
+#include "includes.h"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_VFS
+
+struct vfs_init_function_entry {
+ char *name;
+ vfs_op_tuple *vfs_op_tuples;
+ struct vfs_init_function_entry *prev, *next;
+};
+
+static struct vfs_init_function_entry *backends = NULL;
+
+/* Some structures to help us initialise the vfs operations table */
+
+struct vfs_syminfo {
+ char *name;
+ void *fptr;
+};
+
+/* Default vfs hooks. WARNING: The order of these initialisers is
+ very important. They must be in the same order as defined in
+ vfs.h. Change at your own peril. */
+
+static struct vfs_ops default_vfs = {
+
+ {
+ /* Disk operations */
+
+ vfswrap_dummy_connect,
+ vfswrap_dummy_disconnect,
+ vfswrap_disk_free,
+ vfswrap_get_quota,
+ vfswrap_set_quota,
+ vfswrap_get_shadow_copy_data,
+
+ /* Directory operations */
+
+ vfswrap_opendir,
+ vfswrap_readdir,
+ vfswrap_mkdir,
+ vfswrap_rmdir,
+ vfswrap_closedir,
+
+ /* File operations */
+
+ vfswrap_open,
+ vfswrap_close,
+ vfswrap_read,
+ vfswrap_pread,
+ vfswrap_write,
+ vfswrap_pwrite,
+ vfswrap_lseek,
+ vfswrap_sendfile,
+ vfswrap_rename,
+ vfswrap_fsync,
+ vfswrap_stat,
+ vfswrap_fstat,
+ vfswrap_lstat,
+ vfswrap_unlink,
+ vfswrap_chmod,
+ vfswrap_fchmod,
+ vfswrap_chown,
+ vfswrap_fchown,
+ vfswrap_chdir,
+ vfswrap_getwd,
+ vfswrap_utime,
+ vfswrap_ftruncate,
+ vfswrap_lock,
+ vfswrap_symlink,
+ vfswrap_readlink,
+ vfswrap_link,
+ vfswrap_mknod,
+ vfswrap_realpath,
+
+ /* Windows ACL operations. */
+ vfswrap_fget_nt_acl,
+ vfswrap_get_nt_acl,
+ vfswrap_fset_nt_acl,
+ vfswrap_set_nt_acl,
+
+ /* POSIX ACL operations. */
+ vfswrap_chmod_acl,
+ vfswrap_fchmod_acl,
+
+ vfswrap_sys_acl_get_entry,
+ vfswrap_sys_acl_get_tag_type,
+ vfswrap_sys_acl_get_permset,
+ vfswrap_sys_acl_get_qualifier,
+ vfswrap_sys_acl_get_file,
+ vfswrap_sys_acl_get_fd,
+ vfswrap_sys_acl_clear_perms,
+ vfswrap_sys_acl_add_perm,
+ vfswrap_sys_acl_to_text,
+ vfswrap_sys_acl_init,
+ vfswrap_sys_acl_create_entry,
+ vfswrap_sys_acl_set_tag_type,
+ vfswrap_sys_acl_set_qualifier,
+ vfswrap_sys_acl_set_permset,
+ vfswrap_sys_acl_valid,
+ vfswrap_sys_acl_set_file,
+ vfswrap_sys_acl_set_fd,
+ vfswrap_sys_acl_delete_def_file,
+ vfswrap_sys_acl_get_perm,
+ vfswrap_sys_acl_free_text,
+ vfswrap_sys_acl_free_acl,
+ vfswrap_sys_acl_free_qualifier,
+
+ /* EA operations. */
+ vfswrap_getxattr,
+ vfswrap_lgetxattr,
+ vfswrap_fgetxattr,
+ vfswrap_listxattr,
+ vfswrap_llistxattr,
+ vfswrap_flistxattr,
+ vfswrap_removexattr,
+ vfswrap_lremovexattr,
+ vfswrap_fremovexattr,
+ vfswrap_setxattr,
+ vfswrap_lsetxattr,
+ vfswrap_fsetxattr
+ }
+};
+
+/****************************************************************************
+ maintain the list of available backends
+****************************************************************************/
+
+static struct vfs_init_function_entry *vfs_find_backend_entry(const char *name)
+{
+ struct vfs_init_function_entry *entry = backends;
+
+ while(entry) {
+ if (strcmp(entry->name, name)==0) return entry;
+ entry = entry->next;
+ }
+
+ return NULL;
+}
+
+NTSTATUS smb_register_vfs(int version, const char *name, vfs_op_tuple *vfs_op_tuples)
+{
+ struct vfs_init_function_entry *entry = backends;
+
+ if ((version != SMB_VFS_INTERFACE_VERSION)) {
+ DEBUG(0, ("Failed to register vfs module.\n"
+ "The module was compiled against SMB_VFS_INTERFACE_VERSION %d,\n"
+ "current SMB_VFS_INTERFACE_VERSION is %d.\n"
+ "Please recompile against the current Samba Version!\n",
+ version, SMB_VFS_INTERFACE_VERSION));
+ return NT_STATUS_OBJECT_TYPE_MISMATCH;
+ }
+
+ if (!name || !name[0] || !vfs_op_tuples) {
+ DEBUG(0,("smb_register_vfs() called with NULL pointer or empty name!\n"));
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ if (vfs_find_backend_entry(name)) {
+ DEBUG(0,("VFS module %s already loaded!\n", name));
+ return NT_STATUS_OBJECT_NAME_COLLISION;
+ }
+
+ entry = smb_xmalloc(sizeof(struct vfs_init_function_entry));
+ entry->name = smb_xstrdup(name);
+ entry->vfs_op_tuples = vfs_op_tuples;
+
+ DLIST_ADD(backends, entry);
+ DEBUG(5, ("Successfully added vfs backend '%s'\n", name));
+ return NT_STATUS_OK;
+}
+
+/****************************************************************************
+ initialise default vfs hooks
+****************************************************************************/
+
+static void vfs_init_default(connection_struct *conn)
+{
+ DEBUG(3, ("Initialising default vfs hooks\n"));
+
+ memcpy(&conn->vfs.ops, &default_vfs.ops, sizeof(default_vfs.ops));
+ memcpy(&conn->vfs_opaque.ops, &default_vfs.ops, sizeof(default_vfs.ops));
+}
+
+/****************************************************************************
+ initialise custom vfs hooks
+ ****************************************************************************/
+
+BOOL vfs_init_custom(connection_struct *conn, const char *vfs_object)
+{
+ vfs_op_tuple *ops;
+ char *module_name = NULL;
+ char *module_param = NULL, *p;
+ int i;
+ vfs_handle_struct *handle;
+ struct vfs_init_function_entry *entry;
+
+ if (!conn||!vfs_object||!vfs_object[0]) {
+ DEBUG(0,("vfs_init_custon() called with NULL pointer or emtpy vfs_object!\n"));
+ return False;
+ }
+
+ if(!backends) static_init_vfs;
+
+ DEBUG(3, ("Initialising custom vfs hooks from [%s]\n", vfs_object));
+
+ module_name = smb_xstrdup(vfs_object);
+
+ p = strchr(module_name, ':');
+
+ if (p) {
+ *p = 0;
+ module_param = p+1;
+ trim_char(module_param, ' ', ' ');
+ }
+
+ trim_char(module_name, ' ', ' ');
+
+ /* First, try to load the module with the new module system */
+ if((entry = vfs_find_backend_entry(module_name)) ||
+ (NT_STATUS_IS_OK(smb_probe_module("vfs", module_name)) &&
+ (entry = vfs_find_backend_entry(module_name)))) {
+
+ DEBUGADD(5,("Successfully loaded vfs module [%s] with the new modules system\n", vfs_object));
+
+ if ((ops = entry->vfs_op_tuples) == NULL) {
+ DEBUG(0, ("entry->vfs_op_tuples==NULL for [%s] failed\n", vfs_object));
+ SAFE_FREE(module_name);
+ return False;
+ }
+ } else {
+ DEBUG(0,("Can't find a vfs module [%s]\n",vfs_object));
+ SAFE_FREE(module_name);
+ return False;
+ }
+
+ handle = (vfs_handle_struct *)talloc_zero(conn->mem_ctx,sizeof(vfs_handle_struct));
+ if (!handle) {
+ DEBUG(0,("talloc_zero() failed!\n"));
+ SAFE_FREE(module_name);
+ return False;
+ }
+ memcpy(&handle->vfs_next, &conn->vfs, sizeof(struct vfs_ops));
+ handle->conn = conn;
+ if (module_param) {
+ handle->param = talloc_strdup(conn->mem_ctx, module_param);
+ }
+ DLIST_ADD(conn->vfs_handles, handle);
+
+ for(i=0; ops[i].op != NULL; i++) {
+ DEBUG(5, ("Checking operation #%d (type %d, layer %d)\n", i, ops[i].type, ops[i].layer));
+ if(ops[i].layer == SMB_VFS_LAYER_OPAQUE) {
+ /* Check whether this operation was already made opaque by different module */
+ if(((void**)&conn->vfs_opaque.ops)[ops[i].type] == ((void**)&default_vfs.ops)[ops[i].type]) {
+ /* No, it isn't overloaded yet. Overload. */
+ DEBUGADD(5, ("Making operation type %d opaque [module %s]\n", ops[i].type, vfs_object));
+ ((void**)&conn->vfs_opaque.ops)[ops[i].type] = ops[i].op;
+ ((vfs_handle_struct **)&conn->vfs_opaque.handles)[ops[i].type] = handle;
+ }
+ }
+ /* Change current VFS disposition*/
+ DEBUGADD(5, ("Accepting operation type %d from module %s\n", ops[i].type, vfs_object));
+ ((void**)&conn->vfs.ops)[ops[i].type] = ops[i].op;
+ ((vfs_handle_struct **)&conn->vfs.handles)[ops[i].type] = handle;
+ }
+
+ SAFE_FREE(module_name);
+ return True;
+}
+
+/*****************************************************************
+ Generic VFS init.
+******************************************************************/
+
+BOOL smbd_vfs_init(connection_struct *conn)
+{
+ const char **vfs_objects;
+ unsigned int i = 0;
+ int j = 0;
+
+ /* Normal share - initialise with disk access functions */
+ vfs_init_default(conn);
+ vfs_objects = lp_vfs_objects(SNUM(conn));
+
+ /* Override VFS functions if 'vfs object' was not specified*/
+ if (!vfs_objects || !vfs_objects[0])
+ return True;
+
+ for (i=0; vfs_objects[i] ;) {
+ i++;
+ }
+
+ for (j=i-1; j >= 0; j--) {
+ if (!vfs_init_custom(conn, vfs_objects[j])) {
+ DEBUG(0, ("smbd_vfs_init: vfs_init_custom failed for %s\n", vfs_objects[j]));
+ return False;
+ }
+ }
+ return True;
+}
+
+/*******************************************************************
+ Check if directory exists.
+********************************************************************/
+
+BOOL vfs_directory_exist(connection_struct *conn, const char *dname, SMB_STRUCT_STAT *st)
+{
+ SMB_STRUCT_STAT st2;
+ BOOL ret;
+
+ if (!st)
+ st = &st2;
+
+ if (SMB_VFS_STAT(conn,dname,st) != 0)
+ return(False);
+
+ ret = S_ISDIR(st->st_mode);
+ if(!ret)
+ errno = ENOTDIR;
+
+ return ret;
+}
+
+/*******************************************************************
+ vfs mkdir wrapper
+********************************************************************/
+
+int vfs_MkDir(connection_struct *conn, const char *name, mode_t mode)
+{
+ int ret;
+ SMB_STRUCT_STAT sbuf;
+
+ if(!(ret=SMB_VFS_MKDIR(conn, name, mode))) {
+
+ inherit_access_acl(conn, name, mode);
+
+ /*
+ * Check if high bits should have been set,
+ * then (if bits are missing): add them.
+ * Consider bits automagically set by UNIX, i.e. SGID bit from parent dir.
+ */
+ if(mode & ~(S_IRWXU|S_IRWXG|S_IRWXO) &&
+ !SMB_VFS_STAT(conn,name,&sbuf) && (mode & ~sbuf.st_mode))
+ SMB_VFS_CHMOD(conn,name,sbuf.st_mode | (mode & ~sbuf.st_mode));
+ }
+ return ret;
+}
+
+/*******************************************************************
+ Check if an object exists in the vfs.
+********************************************************************/
+
+BOOL vfs_object_exist(connection_struct *conn,const char *fname,SMB_STRUCT_STAT *sbuf)
+{
+ SMB_STRUCT_STAT st;
+
+ if (!sbuf)
+ sbuf = &st;
+
+ ZERO_STRUCTP(sbuf);
+
+ if (SMB_VFS_STAT(conn,fname,sbuf) == -1)
+ return(False);
+ return True;
+}
+
+/*******************************************************************
+ Check if a file exists in the vfs.
+********************************************************************/
+
+BOOL vfs_file_exist(connection_struct *conn, const char *fname,SMB_STRUCT_STAT *sbuf)
+{
+ SMB_STRUCT_STAT st;
+
+ if (!sbuf)
+ sbuf = &st;
+
+ ZERO_STRUCTP(sbuf);
+
+ if (SMB_VFS_STAT(conn,fname,sbuf) == -1)
+ return False;
+ return(S_ISREG(sbuf->st_mode));
+}
+
+/****************************************************************************
+ Read data from fsp on the vfs. (note: EINTR re-read differs from vfs_write_data)
+****************************************************************************/
+
+ssize_t vfs_read_data(files_struct *fsp, char *buf, size_t byte_count)
+{
+ size_t total=0;
+
+ while (total < byte_count)
+ {
+ ssize_t ret = SMB_VFS_READ(fsp, fsp->fd, buf + total,
+ byte_count - total);
+
+ if (ret == 0) return total;
+ if (ret == -1) {
+ if (errno == EINTR)
+ continue;
+ else
+ return -1;
+ }
+ total += ret;
+ }
+ return (ssize_t)total;
+}
+
+ssize_t vfs_pread_data(files_struct *fsp, char *buf,
+ size_t byte_count, SMB_OFF_T offset)
+{
+ size_t total=0;
+
+ while (total < byte_count)
+ {
+ ssize_t ret = SMB_VFS_PREAD(fsp, fsp->fd, buf + total,
+ byte_count - total, offset + total);
+
+ if (ret == 0) return total;
+ if (ret == -1) {
+ if (errno == EINTR)
+ continue;
+ else
+ return -1;
+ }
+ total += ret;
+ }
+ return (ssize_t)total;
+}
+
+/****************************************************************************
+ Write data to a fd on the vfs.
+****************************************************************************/
+
+ssize_t vfs_write_data(files_struct *fsp,const char *buffer,size_t N)
+{
+ size_t total=0;
+ ssize_t ret;
+
+ while (total < N) {
+ ret = SMB_VFS_WRITE(fsp,fsp->fd,buffer + total,N - total);
+
+ if (ret == -1)
+ return -1;
+ if (ret == 0)
+ return total;
+
+ total += ret;
+ }
+ return (ssize_t)total;
+}
+
+ssize_t vfs_pwrite_data(files_struct *fsp,const char *buffer,
+ size_t N, SMB_OFF_T offset)
+{
+ size_t total=0;
+ ssize_t ret;
+
+ while (total < N) {
+ ret = SMB_VFS_PWRITE(fsp, fsp->fd, buffer + total,
+ N - total, offset + total);
+
+ if (ret == -1)
+ return -1;
+ if (ret == 0)
+ return total;
+
+ total += ret;
+ }
+ return (ssize_t)total;
+}
+/****************************************************************************
+ An allocate file space call using the vfs interface.
+ Allocates space for a file from a filedescriptor.
+ Returns 0 on success, -1 on failure.
+****************************************************************************/
+
+int vfs_allocate_file_space(files_struct *fsp, SMB_BIG_UINT len)
+{
+ int ret;
+ SMB_STRUCT_STAT st;
+ connection_struct *conn = fsp->conn;
+ SMB_BIG_UINT space_avail;
+ SMB_BIG_UINT bsize,dfree,dsize;
+
+ release_level_2_oplocks_on_change(fsp);
+
+ /*
+ * Actually try and commit the space on disk....
+ */
+
+ DEBUG(10,("vfs_allocate_file_space: file %s, len %.0f\n", fsp->fsp_name, (double)len ));
+
+ if (((SMB_OFF_T)len) < 0) {
+ DEBUG(0,("vfs_allocate_file_space: %s negative len requested.\n", fsp->fsp_name ));
+ return -1;
+ }
+
+ ret = SMB_VFS_FSTAT(fsp,fsp->fd,&st);
+ if (ret == -1)
+ return ret;
+
+ if (len == (SMB_BIG_UINT)st.st_size)
+ return 0;
+
+ if (len < (SMB_BIG_UINT)st.st_size) {
+ /* Shrink - use ftruncate. */
+
+ DEBUG(10,("vfs_allocate_file_space: file %s, shrink. Current size %.0f\n",
+ fsp->fsp_name, (double)st.st_size ));
+
+ flush_write_cache(fsp, SIZECHANGE_FLUSH);
+ if ((ret = SMB_VFS_FTRUNCATE(fsp, fsp->fd, (SMB_OFF_T)len)) != -1) {
+ set_filelen_write_cache(fsp, len);
+ }
+ return ret;
+ }
+
+ /* Grow - we need to test if we have enough space. */
+
+ if (!lp_strict_allocate(SNUM(fsp->conn)))
+ return 0;
+
+ len -= st.st_size;
+ len /= 1024; /* Len is now number of 1k blocks needed. */
+ space_avail = SMB_VFS_DISK_FREE(conn,fsp->fsp_name,False,&bsize,&dfree,&dsize);
+
+ DEBUG(10,("vfs_allocate_file_space: file %s, grow. Current size %.0f, needed blocks = %.0f, space avail = %.0f\n",
+ fsp->fsp_name, (double)st.st_size, (double)len, (double)space_avail ));
+
+ if (len > space_avail) {
+ errno = ENOSPC;
+ return -1;
+ }
+
+ return 0;
+}
+
+/****************************************************************************
+ A vfs set_filelen call.
+ set the length of a file from a filedescriptor.
+ Returns 0 on success, -1 on failure.
+****************************************************************************/
+
+int vfs_set_filelen(files_struct *fsp, SMB_OFF_T len)
+{
+ int ret;
+
+ release_level_2_oplocks_on_change(fsp);
+ DEBUG(10,("vfs_set_filelen: ftruncate %s to len %.0f\n", fsp->fsp_name, (double)len));
+ flush_write_cache(fsp, SIZECHANGE_FLUSH);
+ if ((ret = SMB_VFS_FTRUNCATE(fsp, fsp->fd, len)) != -1)
+ set_filelen_write_cache(fsp, len);
+
+ return ret;
+}
+
+/****************************************************************************
+ Transfer some data (n bytes) between two file_struct's.
+****************************************************************************/
+
+static files_struct *in_fsp;
+static files_struct *out_fsp;
+
+static ssize_t read_fn(int fd, void *buf, size_t len)
+{
+ return SMB_VFS_READ(in_fsp, fd, buf, len);
+}
+
+static ssize_t write_fn(int fd, const void *buf, size_t len)
+{
+ return SMB_VFS_WRITE(out_fsp, fd, buf, len);
+}
+
+SMB_OFF_T vfs_transfer_file(files_struct *in, files_struct *out, SMB_OFF_T n)
+{
+ in_fsp = in;
+ out_fsp = out;
+
+ return transfer_file_internal(in_fsp->fd, out_fsp->fd, n, read_fn, write_fn);
+}
+
+/*******************************************************************
+ A vfs_readdir wrapper which just returns the file name.
+********************************************************************/
+
+char *vfs_readdirname(connection_struct *conn, void *p)
+{
+ struct dirent *ptr= NULL;
+ char *dname;
+
+ if (!p)
+ return(NULL);
+
+ ptr = (struct dirent *)SMB_VFS_READDIR(conn,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
+
+ return(dname);
+}
+
+/*******************************************************************
+ A wrapper for vfs_chdir().
+********************************************************************/
+
+int vfs_ChDir(connection_struct *conn, const char *path)
+{
+ int res;
+ static pstring LastDir="";
+
+ if (strcsequal(path,"."))
+ return(0);
+
+ if (*path == '/' && strcsequal(LastDir,path))
+ return(0);
+
+ DEBUG(4,("vfs_ChDir to %s\n",path));
+
+ res = SMB_VFS_CHDIR(conn,path);
+ if (!res)
+ pstrcpy(LastDir,path);
+ return(res);
+}
+
+/* number of list structures for a caching GetWd function. */
+#define MAX_GETWDCACHE (50)
+
+static struct {
+ SMB_DEV_T dev; /* These *must* be compatible with the types returned in a stat() call. */
+ SMB_INO_T inode; /* These *must* be compatible with the types returned in a stat() call. */
+ char *dos_path; /* The pathname in DOS format. */
+ BOOL valid;
+} ino_list[MAX_GETWDCACHE];
+
+extern BOOL use_getwd_cache;
+
+/****************************************************************************
+ Prompte a ptr (to make it recently used)
+****************************************************************************/
+
+static void array_promote(char *array,int elsize,int element)
+{
+ char *p;
+ if (element == 0)
+ return;
+
+ p = (char *)malloc(elsize);
+
+ if (!p) {
+ DEBUG(5,("array_promote: malloc fail\n"));
+ return;
+ }
+
+ memcpy(p,array + element * elsize, elsize);
+ memmove(array + elsize,array,elsize*element);
+ memcpy(array,p,elsize);
+ SAFE_FREE(p);
+}
+
+/*******************************************************************
+ Return the absolute current directory path - given a UNIX pathname.
+ Note that this path is returned in DOS format, not UNIX
+ format. Note this can be called with conn == NULL.
+********************************************************************/
+
+char *vfs_GetWd(connection_struct *conn, char *path)
+{
+ pstring s;
+ static BOOL getwd_cache_init = False;
+ SMB_STRUCT_STAT st, st2;
+ int i;
+
+ *s = 0;
+
+ if (!use_getwd_cache)
+ return(SMB_VFS_GETWD(conn,path));
+
+ /* init the cache */
+ if (!getwd_cache_init) {
+ getwd_cache_init = True;
+ for (i=0;i<MAX_GETWDCACHE;i++) {
+ string_set(&ino_list[i].dos_path,"");
+ ino_list[i].valid = False;
+ }
+ }
+
+ /* Get the inode of the current directory, if this doesn't work we're
+ in trouble :-) */
+
+ if (SMB_VFS_STAT(conn, ".",&st) == -1) {
+ DEBUG(0,("Very strange, couldn't stat \".\" path=%s\n", path));
+ return(SMB_VFS_GETWD(conn,path));
+ }
+
+
+ for (i=0; i<MAX_GETWDCACHE; i++) {
+ if (ino_list[i].valid) {
+
+ /* If we have found an entry with a matching inode and dev number
+ then find the inode number for the directory in the cached string.
+ If this agrees with that returned by the stat for the current
+ directory then all is o.k. (but make sure it is a directory all
+ the same...) */
+
+ if (st.st_ino == ino_list[i].inode && st.st_dev == ino_list[i].dev) {
+ if (SMB_VFS_STAT(conn,ino_list[i].dos_path,&st2) == 0) {
+ if (st.st_ino == st2.st_ino && st.st_dev == st2.st_dev &&
+ (st2.st_mode & S_IFMT) == S_IFDIR) {
+ pstrcpy (path, ino_list[i].dos_path);
+
+ /* promote it for future use */
+ array_promote((char *)&ino_list[0],sizeof(ino_list[0]),i);
+ return (path);
+ } else {
+ /* If the inode is different then something's changed,
+ scrub the entry and start from scratch. */
+ ino_list[i].valid = False;
+ }
+ }
+ }
+ }
+ }
+
+ /* We don't have the information to hand so rely on traditional methods.
+ The very slow getcwd, which spawns a process on some systems, or the
+ not quite so bad getwd. */
+
+ if (!SMB_VFS_GETWD(conn,s)) {
+ DEBUG(0,("vfs_GetWd: SMB_VFS_GETWD call failed, errno %s\n",strerror(errno)));
+ return (NULL);
+ }
+
+ pstrcpy(path,s);
+
+ DEBUG(5,("vfs_GetWd %s, inode %.0f, dev %.0f\n",s,(double)st.st_ino,(double)st.st_dev));
+
+ /* add it to the cache */
+ i = MAX_GETWDCACHE - 1;
+ string_set(&ino_list[i].dos_path,s);
+ ino_list[i].dev = st.st_dev;
+ ino_list[i].inode = st.st_ino;
+ ino_list[i].valid = True;
+
+ /* put it at the top of the list */
+ array_promote((char *)&ino_list[0],sizeof(ino_list[0]),i);
+
+ return (path);
+}
+
+
+/* check if the file 'nmae' is a symlink, in that case check that it point to
+ a file that reside under the 'dir' tree */
+
+static BOOL readlink_check(connection_struct *conn, const char *dir, char *name)
+{
+ BOOL ret = True;
+ pstring flink;
+ pstring cleanlink;
+ pstring savedir;
+ pstring realdir;
+ size_t reallen;
+
+ if (!vfs_GetWd(conn, savedir)) {
+ DEBUG(0,("couldn't vfs_GetWd for %s %s\n", name, dir));
+ return False;
+ }
+
+ if (vfs_ChDir(conn, dir) != 0) {
+ DEBUG(0,("couldn't vfs_ChDir to %s\n", dir));
+ return False;
+ }
+
+ if (!vfs_GetWd(conn, realdir)) {
+ DEBUG(0,("couldn't vfs_GetWd for %s\n", dir));
+ vfs_ChDir(conn, savedir);
+ return(False);
+ }
+
+ reallen = strlen(realdir);
+ if (realdir[reallen -1] == '/') {
+ reallen--;
+ realdir[reallen] = 0;
+ }
+
+ if (SMB_VFS_READLINK(conn, name, flink, sizeof(pstring) -1) != -1) {
+ DEBUG(3,("reduce_name: file path name %s is a symlink\nChecking it's path\n", name));
+ if (*flink == '/') {
+ pstrcpy(cleanlink, flink);
+ } else {
+ pstrcpy(cleanlink, realdir);
+ pstrcat(cleanlink, "/");
+ pstrcat(cleanlink, flink);
+ }
+ unix_clean_name(cleanlink);
+
+ if (strncmp(cleanlink, realdir, reallen) != 0) {
+ DEBUG(2,("Bad access attempt? s=%s dir=%s newname=%s l=%d\n", name, realdir, cleanlink, (int)reallen));
+ ret = False;
+ }
+ }
+
+ vfs_ChDir(conn, savedir);
+
+ return ret;
+}
+
+/*******************************************************************
+ Reduce a file name, removing .. elements and checking that
+ it is below dir in the heirachy. This uses vfs_GetWd() and so must be run
+ on the system that has the referenced file system.
+********************************************************************/
+
+BOOL reduce_name(connection_struct *conn, pstring s, const char *dir)
+{
+#ifndef REDUCE_PATHS
+ return True;
+#else
+ pstring dir2;
+ pstring wd;
+ pstring base_name;
+ pstring newname;
+ char *p=NULL;
+ BOOL relative = (*s != '/');
+
+ *dir2 = *wd = *base_name = *newname = 0;
+
+ DEBUG(3,("reduce_name [%s] [%s]\n",s,dir));
+
+ /* We know there are no double slashes as this comes from srvstr_get_path().
+ and has gone through check_path_syntax(). JRA */
+
+ pstrcpy(base_name,s);
+ p = strrchr_m(base_name,'/');
+
+ if (!p)
+ return readlink_check(conn, dir, s);
+
+ if (!vfs_GetWd(conn,wd)) {
+ DEBUG(0,("couldn't vfs_GetWd for %s %s\n",s,dir));
+ return(False);
+ }
+
+ if (vfs_ChDir(conn,dir) != 0) {
+ DEBUG(0,("couldn't vfs_ChDir to %s\n",dir));
+ return(False);
+ }
+
+ if (!vfs_GetWd(conn,dir2)) {
+ DEBUG(0,("couldn't vfs_GetWd for %s\n",dir));
+ vfs_ChDir(conn,wd);
+ return(False);
+ }
+
+ if (p && (p != base_name)) {
+ *p = 0;
+ if (strcmp(p+1,".")==0)
+ p[1]=0;
+ if (strcmp(p+1,"..")==0)
+ *p = '/';
+ }
+
+ if (vfs_ChDir(conn,base_name) != 0) {
+ vfs_ChDir(conn,wd);
+ DEBUG(3,("couldn't vfs_ChDir for %s %s basename=%s\n",s,dir,base_name));
+ return(False);
+ }
+
+ if (!vfs_GetWd(conn,newname)) {
+ vfs_ChDir(conn,wd);
+ DEBUG(2,("couldn't get vfs_GetWd for %s %s\n",s,base_name));
+ return(False);
+ }
+
+ if (p && (p != base_name)) {
+ pstrcat(newname,"/");
+ pstrcat(newname,p+1);
+ }
+
+ {
+ size_t l = strlen(dir2);
+ char *last_slash = strrchr_m(dir2, '/');
+
+ if (last_slash && (last_slash[1] == '\0'))
+ l--;
+
+ if (strncmp(newname,dir2,l) != 0) {
+ vfs_ChDir(conn,wd);
+ DEBUG(2,("Bad access attempt: s=%s dir=%s newname=%s l=%d\n",s,dir2,newname,(int)l));
+ return(False);
+ }
+
+ if (!readlink_check(conn, dir, newname)) {
+ DEBUG(2, ("Bad access attemt: %s is a symlink outside the share path", s));
+ return(False);
+ }
+
+ if (relative) {
+ if (newname[l] == '/')
+ pstrcpy(s,newname + l + 1);
+ else
+ pstrcpy(s,newname+l);
+ } else
+ pstrcpy(s,newname);
+ }
+
+ vfs_ChDir(conn,wd);
+
+ if (strlen(s) == 0)
+ pstrcpy(s,"./");
+
+ DEBUG(3,("reduced to %s\n",s));
+ return(True);
+#endif
+}
diff --git a/source/smbwrapper/.cvsignore b/source/smbwrapper/.cvsignore
new file mode 100644
index 00000000000..7835612d320
--- /dev/null
+++ b/source/smbwrapper/.cvsignore
@@ -0,0 +1,8 @@
+*.po
+*.po32
+kernel_stat.h
+smbsh
+tst
+tst.c
+wrapper.h
+xstat.c
diff --git a/source/smbwrapper/PORTING b/source/smbwrapper/PORTING
new file mode 100644
index 00000000000..884246d078a
--- /dev/null
+++ b/source/smbwrapper/PORTING
@@ -0,0 +1,77 @@
+This describes how to port the smbwrapper portion of Samba to a new
+unix-like platform. Note that porting smbwrapper is considerably
+harder than porting Samba, for Samba you generally just need to run
+configure and recompile whereas for smbwrapper some extra effort is
+generally required.
+
+
+STEP 1
+------
+
+The first step is to work out how to create a shared library on your
+OS and how to compile C code to produce position independent object
+files (PIC files). You shoud be able to find this information in the
+man pages for your compiler and loader (ld). Then modify configure.in
+to give that information to Samba.
+
+
+STEP 2
+------
+
+The next step is to work out how to preload shared objects. On many
+systems this is done using a LD_PRELOAD environment variable. On
+others (shc as IRIX) it may use a _RTL_LIST variable.
+
+To make sure it works I suggest you create two C files like this:
+
+/* first C file */
+main()
+{
+ unlink("foo.txt");
+}
+
+/* second C file */
+#include <stdio.h>
+
+int unlink(char *fname)
+{
+ fprintf(stderr,"unlink(%s) called\n",fname);
+}
+
+
+then compile the first as an ordinary C program and the second as a
+shared library. Then use LD_PRELOAD to preload the resulting shared
+library. Then run the first program. It should print "unlink(foo.txt)
+called". If it doesn't then consult your man pages till you get it
+right.
+
+Once you work this out then edit smbwrapper/smbsh.in and add a section
+if necessary to correctly set the necessary preload options for your
+OS.
+
+
+STEP 3
+------
+
+The next step is to work out how to make direct system calls. On most
+machines this will work without any source code changes to
+smbwrapper. To test that it does work create the following C program:
+
+#include <sys/syscall.h>
+main()
+{
+ syscall(SYS_write, 1, "hello world\n", 12);
+}
+
+and try to compile/run it. If it produces "hello world" then syscall()
+works as expected. If not then work out what needs to be changed and
+then make that change in realcalls.h. For example, on IRIX 6.4 the
+system call numbers are wrong and need to be fixed up by getting an
+offset right.
+
+
+STEP 4
+------
+
+Try compiling smbwrapper! Then test it. Then debug it. Simple really :)
+
diff --git a/source/smbwrapper/README b/source/smbwrapper/README
new file mode 100644
index 00000000000..8d5c376f825
--- /dev/null
+++ b/source/smbwrapper/README
@@ -0,0 +1,94 @@
+This is a prelodable shared library that provides SMB client services
+for existing executables. Using this you can simulate a smb
+filesystem.
+
+*** This is code under development. Some things don't work yet ***
+
+Currently this code has been tested on:
+
+- Linux 2.0 with glibc2 (RH5.1)
+- Linux 2.1 with glibc2
+- Solaris 2.5.1 with gcc
+- Solaris 2.6 with gcc
+- SunOS 4.1.3 with gcc
+- IRIX 6.4 with cc
+- OSF1 with gcc
+
+
+It probably won't run on other systems without some porting. If you
+have a different system then see the file PORTING.
+
+To use it you need to do this:
+
+1) build smbwrapper.so using the command "make smbwrapper"
+3) run smbsh
+
+You will be asked for a username and password. After that you will be
+returned to a shell prompt. It is actually a subshell running with
+smbwrapper enabled.
+
+Now try to access /smb/SERVER for some SMB server name and see what
+happens. If you use the -W option to set your workgroup or have
+workgroup set in your smb.conf then listing /smb/ should list all SMB
+servers in your workgroup.
+
+
+OPTIONS
+-------
+
+-U username
+ specify the username and optional password (as user%password)
+
+-d debug level
+ This is an integer that controls the internal debug level of smbw. It
+ defaults to 0, which means no debug info.
+
+-l logfile
+ The place where smbw debug logs are put. If this is not set then
+ stderr is used.
+
+-P prefix
+ The root of the SMB filesystem. This defaults to /smb/ but you can
+ set it to any name you like.
+
+-W workgroup
+ This is the workgroup used for browsing (ie. listing machines in the
+ /smb/ directory). It defaults to the one set in smb.conf.
+
+-R resolve order
+ This allows you to override the setting of the name resolve order
+ from smb.conf
+
+
+ATTRIBUTE MAPPING
+-----------------
+
+smbwrapper does an inverse attribute maping to what Samba does. This
+means that the archive bit appears as the user execute bit, the system
+bit appears as the group execute bit and the hidden bit appears as the
+other execute bit. You can control these with chmod. The mapping can
+be enabled an disabled using the normal smb.conf controls (ie. "map
+archive", "map system" and "map hidden").
+
+Read-only files appear as non-writeable by everyone. Writeable files
+appear as writeable by the current user.
+
+
+WHAT WORKS
+----------
+
+Things that I have tried and do seem to work include:
+
+ emacs, tar, ls, cmp, cp, rsync, du, cat, rm, mv, less, more, wc, head,
+ tail, bash, tcsh, mkdir, rmdir, vim, xedit, diff
+
+things that I know don't work:
+
+ anything executing from the share
+ anything that uses mmap
+ redirection within shells to smbsh files
+
+If you want to help with the development of this code then join the
+samba-technical mailing list.
+
+
diff --git a/source/smbwrapper/realcalls.c b/source/smbwrapper/realcalls.c
new file mode 100644
index 00000000000..b64f4a43dbf
--- /dev/null
+++ b/source/smbwrapper/realcalls.c
@@ -0,0 +1,48 @@
+/*
+ Unix SMB/CIFS implementation.
+ SMB wrapper functions for calls that syscall() can't do
+ 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 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 "realcalls.h"
+
+#ifdef REPLACE_UTIME
+int real_utime(const char *name, struct utimbuf *buf)
+{
+ struct timeval tv[2];
+
+ tv[0].tv_sec = buf->actime;
+ tv[0].tv_usec = 0;
+ tv[1].tv_sec = buf->modtime;
+ tv[1].tv_usec = 0;
+
+ return real_utimes(name, &tv[0]);
+}
+#endif
+
+#ifdef REPLACE_UTIMES
+int real_utimes(const char *name, struct timeval tv[2])
+{
+ struct utimbuf buf;
+
+ buf.actime = tv[0].tv_sec;
+ buf.modtime = tv[1].tv_sec;
+
+ return real_utime(name, &buf);
+}
+#endif
diff --git a/source/smbwrapper/realcalls.h b/source/smbwrapper/realcalls.h
new file mode 100644
index 00000000000..6c230dba056
--- /dev/null
+++ b/source/smbwrapper/realcalls.h
@@ -0,0 +1,263 @@
+/*
+ Unix SMB/CIFS implementation.
+ defintions of syscall entries
+ 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 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.
+*/
+
+#if HAVE_SYS_SYSCALL_H
+#include <sys/syscall.h>
+#elif HAVE_SYSCALL_H
+#include <syscall.h>
+#endif
+
+#ifdef IRIX
+/* amazingly, IRIX gets its own syscall numbers wrong! */
+#ifdef SYSVoffset
+#if (SYSVoffset == 1)
+#undef SYSVoffset
+#define SYSVoffset 1000
+#endif
+#endif
+#endif
+
+/* this file is partly derived from zlibc by Alain Knaff */
+
+#define real_access(fn, mode) (syscall(SYS_access, (fn), (mode)))
+#define real_chdir(fn) (syscall(SYS_chdir, (fn)))
+#define real_chmod(fn, mode) (syscall(SYS_chmod,(fn), (mode)))
+#define real_chown(fn, owner, group) (syscall(SYS_chown,(fn),(owner),(group)))
+
+#ifdef SYS_getdents
+#define real_getdents(fd, dirp, count) (syscall(SYS_getdents, (fd), (dirp), (count)))
+#endif
+
+#define real_link(fn1, fn2) (syscall(SYS_link, (fn1), (fn2)))
+
+#define real_open(fn,flags,mode) (syscall(SYS_open, (fn), (flags), (mode)))
+
+#ifdef SYS_open64
+#define real_open64(fn,flags,mode) (syscall(SYS_open64, (fn), (flags), (mode)))
+#elif HAVE__OPEN64
+#define real_open64(fn,flags,mode) (_open64(fn,flags,mode))
+#define NO_OPEN64_ALIAS
+#elif HAVE___OPEN64
+#define real_open64(fn,flags,mode) (__open64(fn,flags,mode))
+#define NO_OPEN64_ALIAS
+#endif
+
+#ifdef HAVE__FORK
+#define real_fork() (_fork())
+#elif HAVE___FORK
+#define real_fork() (__fork())
+#elif SYS_fork
+#define real_fork() (syscall(SYS_fork))
+#endif
+
+#ifdef HAVE__OPENDIR
+#define real_opendir(fn) (_opendir(fn))
+#elif SYS_opendir
+#define real_opendir(fn) (syscall(SYS_opendir,(fn)))
+#elif HAVE___OPENDIR
+#define real_opendir(fn) (__opendir(fn))
+#endif
+
+#ifdef HAVE__READDIR
+#define real_readdir(d) (_readdir(d))
+#elif HAVE___READDIR
+#define real_readdir(d) (__readdir(d))
+#elif SYS_readdir
+#define real_readdir(d) (syscall(SYS_readdir,(d)))
+#endif
+
+#ifdef HAVE__CLOSEDIR
+#define real_closedir(d) (_closedir(d))
+#elif SYS_closedir
+#define real_closedir(d) (syscall(SYS_closedir,(d)))
+#elif HAVE___CLOSEDIR
+#define real_closedir(d) (__closedir(d))
+#endif
+
+#ifdef HAVE__SEEKDIR
+#define real_seekdir(d,l) (_seekdir(d,l))
+#elif SYS_seekdir
+#define real_seekdir(d,l) (syscall(SYS_seekdir,(d),(l)))
+#elif HAVE___SEEKDIR
+#define real_seekdir(d,l) (__seekdir(d,l))
+#else
+#define NO_SEEKDIR_WRAPPER
+#endif
+
+#ifdef HAVE__TELLDIR
+#define real_telldir(d) (_telldir(d))
+#elif SYS_telldir
+#define real_telldir(d) (syscall(SYS_telldir,(d)))
+#elif HAVE___TELLDIR
+#define real_telldir(d) (__telldir(d))
+#endif
+
+#ifdef HAVE__DUP
+#define real_dup(d) (_dup(d))
+#elif SYS_dup
+#define real_dup(d) (syscall(SYS_dup,(d)))
+#elif HAVE___DUP
+#define real_dup(d) (__dup(d))
+#endif
+
+#ifdef HAVE__DUP2
+#define real_dup2(d1,d2) (_dup2(d1,d2))
+#elif SYS_dup2
+#define real_dup2(d1,d2) (syscall(SYS_dup2,(d1),(d2)))
+#elif HAVE___DUP2
+#define real_dup2(d1,d2) (__dup2(d1,d2))
+#endif
+
+#ifdef HAVE__GETCWD
+#define real_getcwd(b,s) ((char *)_getcwd(b,s))
+#elif SYS_getcwd
+#define real_getcwd(b,s) ((char *)syscall(SYS_getcwd,(b),(s)))
+#elif HAVE___GETCWD
+#define real_getcwd(b,s) ((char *)__getcwd(b,s))
+#endif
+
+#ifdef HAVE__STAT
+#define real_stat(fn,st) (_stat(fn,st))
+#elif SYS_stat
+#define real_stat(fn,st) (syscall(SYS_stat,(fn),(st)))
+#elif HAVE___STAT
+#define real_stat(fn,st) (__stat(fn,st))
+#endif
+
+#ifdef HAVE__LSTAT
+#define real_lstat(fn,st) (_lstat(fn,st))
+#elif SYS_lstat
+#define real_lstat(fn,st) (syscall(SYS_lstat,(fn),(st)))
+#elif HAVE___LSTAT
+#define real_lstat(fn,st) (__lstat(fn,st))
+#endif
+
+#ifdef HAVE__FSTAT
+#define real_fstat(fd,st) (_fstat(fd,st))
+#elif SYS_fstat
+#define real_fstat(fd,st) (syscall(SYS_fstat,(fd),(st)))
+#elif HAVE___FSTAT
+#define real_fstat(fd,st) (__fstat(fd,st))
+#endif
+
+#if defined(HAVE_SYS_ACL_H) && defined(HAVE__ACL)
+#define real_acl(fn,cmd,n,buf) (_acl(fn,cmd,n,buf))
+#elif SYS_acl
+#define real_acl(fn,cmd,n,buf) (syscall(SYS_acl,(fn),(cmd),(n),(buf)))
+#elif HAVE___ACL
+#define real_acl(fn,cmd,n,buf) (__acl(fn,cmd,n,buf))
+#else
+#define NO_ACL_WRAPPER
+#endif
+
+#ifdef HAVE__FACL
+#define real_facl(fd,cmd,n,buf) (_facl(fd,cmd,n,buf))
+#elif SYS_facl
+#define real_facl(fd,cmd,n,buf) (syscall(SYS_facl,(fd),(cmd),(n),(buf)))
+#elif HAVE___FACL
+#define real_facl(fd,cmd,n,buf) (__facl(fd,cmd,n,buf))
+#else
+#define NO_FACL_WRAPPER
+#endif
+
+
+#ifdef HAVE__STAT64
+#define real_stat64(fn,st) (_stat64(fn,st))
+#elif HAVE___STAT64
+#define real_stat64(fn,st) (__stat64(fn,st))
+#endif
+
+#ifdef HAVE__LSTAT64
+#define real_lstat64(fn,st) (_lstat64(fn,st))
+#elif HAVE___LSTAT64
+#define real_lstat64(fn,st) (__lstat64(fn,st))
+#endif
+
+#ifdef HAVE__FSTAT64
+#define real_fstat64(fd,st) (_fstat64(fd,st))
+#elif HAVE___FSTAT64
+#define real_fstat64(fd,st) (__fstat64(fd,st))
+#endif
+
+#ifdef HAVE__READDIR64
+#define real_readdir64(d) (_readdir64(d))
+#elif HAVE___READDIR64
+#define real_readdir64(d) (__readdir64(d))
+#endif
+
+#ifdef HAVE__LLSEEK
+#define real_llseek(fd,ofs,whence) (_llseek(fd,ofs,whence))
+#elif HAVE___LLSEEK
+#define real_llseek(fd,ofs,whence) (__llseek(fd,ofs,whence))
+#elif HAVE___SYS_LLSEEK
+#define real_llseek(fd,ofs,whence) (__sys_llseek(fd,ofs,whence))
+#endif
+
+
+#ifdef HAVE__PREAD
+#define real_pread(fd,buf,size,ofs) (_pread(fd,buf,size,ofs))
+#elif HAVE___PREAD
+#define real_pread(fd,buf,size,ofs) (__pread(fd,buf,size,ofs))
+#endif
+
+#ifdef HAVE__PREAD64
+#define real_pread64(fd,buf,size,ofs) (_pread64(fd,buf,size,ofs))
+#elif HAVE___PREAD64
+#define real_pread64(fd,buf,size,ofs) (__pread64(fd,buf,size,ofs))
+#endif
+
+#ifdef HAVE__PWRITE
+#define real_pwrite(fd,buf,size,ofs) (_pwrite(fd,buf,size,ofs))
+#elif HAVE___PWRITE
+#define real_pwrite(fd,buf,size,ofs) (__pwrite(fd,buf,size,ofs))
+#endif
+
+#ifdef HAVE__PWRITE64
+#define real_pwrite64(fd,buf,size,ofs) (_pwrite64(fd,buf,size,ofs))
+#elif HAVE___PWRITE64
+#define real_pwrite64(fd,buf,size,ofs) (__pwrite64(fd,buf,size,ofs))
+#endif
+
+
+#define real_readlink(fn,buf,len) (syscall(SYS_readlink, (fn), (buf), (len)))
+#define real_rename(fn1, fn2) (syscall(SYS_rename, (fn1), (fn2)))
+#define real_symlink(fn1, fn2) (syscall(SYS_symlink, (fn1), (fn2)))
+#define real_read(fd, buf, count ) (syscall(SYS_read, (fd), (buf), (count)))
+#define real_lseek(fd, offset, whence) (syscall(SYS_lseek, (fd), (offset), (whence)))
+#define real_write(fd, buf, count ) (syscall(SYS_write, (fd), (buf), (count)))
+#define real_close(fd) (syscall(SYS_close, (fd)))
+#define real_fchdir(fd) (syscall(SYS_fchdir, (fd)))
+#define real_fcntl(fd,cmd,arg) (syscall(SYS_fcntl, (fd), (cmd), (arg)))
+#define real_symlink(fn1, fn2) (syscall(SYS_symlink, (fn1), (fn2)))
+#define real_unlink(fn) (syscall(SYS_unlink, (fn)))
+#define real_rmdir(fn) (syscall(SYS_rmdir, (fn)))
+#define real_mkdir(fn, mode) (syscall(SYS_mkdir, (fn), (mode)))
+
+#ifdef SYS_utime
+#define real_utime(fn, buf) (syscall(SYS_utime, (fn), (buf)))
+#else
+#define REPLACE_UTIME 1
+#endif
+
+#ifdef SYS_utimes
+#define real_utimes(fn, buf) (syscall(SYS_utimes, (fn), (buf)))
+#else
+#define REPLACE_UTIMES 1
+#endif
diff --git a/source/smbwrapper/shared.c b/source/smbwrapper/shared.c
new file mode 100644
index 00000000000..ca8df5841d1
--- /dev/null
+++ b/source/smbwrapper/shared.c
@@ -0,0 +1,203 @@
+/*
+ Unix SMB/CIFS implementation.
+ SMB wrapper functions - shared variables
+ 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 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"
+
+static int shared_fd;
+static char *variables;
+static int shared_size;
+
+/*****************************************************
+setup the shared area
+*******************************************************/
+void smbw_setup_shared(void)
+{
+ int fd;
+ pstring name, s;
+
+ slprintf(name,sizeof(name)-1, "%s/smbw.XXXXXX",tmpdir());
+
+ fd = smb_mkstemp(name);
+
+ if (fd == -1) goto failed;
+
+ unlink(name);
+
+ shared_fd = set_maxfiles(SMBW_MAX_OPEN);
+
+ while (shared_fd && dup2(fd, shared_fd) != shared_fd) shared_fd--;
+
+ if (shared_fd == 0) goto failed;
+
+ close(fd);
+
+ DEBUG(4,("created shared_fd=%d\n", shared_fd));
+
+ slprintf(s,sizeof(s)-1,"%d", shared_fd);
+
+ setenv("SMBW_HANDLE", s, 1);
+
+ return;
+
+ failed:
+ perror("Failed to setup shared variable area ");
+ exit(1);
+}
+
+static int locked;
+
+/*****************************************************
+lock the shared variable area
+*******************************************************/
+static void lockit(void)
+{
+ if (shared_fd == 0) {
+ char *p = getenv("SMBW_HANDLE");
+ if (!p) {
+ DEBUG(0,("ERROR: can't get smbw shared handle\n"));
+ exit(1);
+ }
+ shared_fd = atoi(p);
+ }
+ if (locked==0 &&
+ fcntl_lock(shared_fd,SMB_F_SETLKW,0,1,F_WRLCK)==False) {
+ DEBUG(0,("ERROR: can't get smbw shared lock (%s)\n", strerror(errno)));
+ exit(1);
+ }
+ locked++;
+}
+
+/*****************************************************
+unlock the shared variable area
+*******************************************************/
+static void unlockit(void)
+{
+ locked--;
+ if (locked == 0) {
+ fcntl_lock(shared_fd,SMB_F_SETLK,0,1,F_UNLCK);
+ }
+}
+
+
+/*****************************************************
+get a variable from the shared area
+*******************************************************/
+char *smbw_getshared(const char *name)
+{
+ int i;
+ struct stat st;
+ char *var;
+
+ lockit();
+
+ /* maybe the area has changed */
+ if (fstat(shared_fd, &st)) goto failed;
+
+ if (st.st_size != shared_size) {
+ var = (char *)Realloc(variables, st.st_size);
+ if (!var) goto failed;
+ else variables = var;
+ shared_size = st.st_size;
+ lseek(shared_fd, 0, SEEK_SET);
+ if (read(shared_fd, variables, shared_size) != shared_size) {
+ goto failed;
+ }
+ }
+
+ unlockit();
+
+ i=0;
+ while (i < shared_size) {
+ char *n, *v;
+ int l1, l2;
+
+ l1 = SVAL(&variables[i], 0);
+ l2 = SVAL(&variables[i], 2);
+
+ n = &variables[i+4];
+ v = &variables[i+4+l1];
+ i += 4+l1+l2;
+
+ if (strcmp(name,n)) {
+ continue;
+ }
+ return v;
+ }
+
+ return NULL;
+
+ failed:
+ DEBUG(0,("smbw: shared variables corrupt (%s)\n", strerror(errno)));
+ exit(1);
+ return NULL;
+}
+
+
+
+/*****************************************************
+set a variable in the shared area
+*******************************************************/
+void smbw_setshared(const char *name, const char *val)
+{
+ int l1, l2;
+ char *var;
+
+ /* we don't allow variable overwrite */
+ if (smbw_getshared(name)) return;
+
+ lockit();
+
+ l1 = strlen(name)+1;
+ l2 = strlen(val)+1;
+
+ var = (char *)Realloc(variables, shared_size + l1+l2+4);
+
+ if (!var) {
+ DEBUG(0,("out of memory in smbw_setshared\n"));
+ exit(1);
+ }
+
+ variables = var;
+
+ SSVAL(&variables[shared_size], 0, l1);
+ SSVAL(&variables[shared_size], 2, l2);
+
+ safe_strcpy(&variables[shared_size] + 4, name, l1-1);
+ safe_strcpy(&variables[shared_size] + 4 + l1, val, l2-1);
+
+ shared_size += l1+l2+4;
+
+ lseek(shared_fd, 0, SEEK_SET);
+ if (write(shared_fd, variables, shared_size) != shared_size) {
+ DEBUG(0,("smbw_setshared failed (%s)\n", strerror(errno)));
+ exit(1);
+ }
+
+ unlockit();
+}
+
+
+/*****************************************************************
+return true if the passed fd is the SMBW_HANDLE
+*****************************************************************/
+int smbw_shared_fd(int fd)
+{
+ return (shared_fd && shared_fd == fd);
+}
diff --git a/source/smbwrapper/smbsh.c b/source/smbwrapper/smbsh.c
new file mode 100644
index 00000000000..7a2b8e09d7a
--- /dev/null
+++ b/source/smbwrapper/smbsh.c
@@ -0,0 +1,127 @@
+/*
+ Unix SMB/CIFS implementation.
+ SMB wrapper functions - frontend
+ 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 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"
+
+static void smbsh_usage(void)
+{
+ printf("smbsh [options]\n\n");
+ printf(" -W workgroup\n");
+ printf(" -U username\n");
+ printf(" -P prefix\n");
+ printf(" -R resolve order\n");
+ printf(" -d debug level\n");
+ printf(" -l logfile\n");
+ printf(" -L libdir\n");
+ exit(0);
+}
+
+int main(int argc, char *argv[])
+{
+ char *p, *u;
+ const char *libd = dyn_LIBDIR;
+ pstring line, wd;
+ int opt;
+ extern char *optarg;
+ extern int optind;
+
+ dbf = x_stdout;
+ smbw_setup_shared();
+
+ while ((opt = getopt(argc, argv, "W:U:R:d:P:l:hL:")) != EOF) {
+ switch (opt) {
+ case 'L':
+ libd = optarg;
+ break;
+ case 'W':
+ smbw_setshared("WORKGROUP", optarg);
+ break;
+ case 'l':
+ smbw_setshared("LOGFILE", optarg);
+ break;
+ case 'P':
+ smbw_setshared("PREFIX", optarg);
+ break;
+ case 'd':
+ smbw_setshared("DEBUG", optarg);
+ break;
+ case 'U':
+ p = strchr_m(optarg,'%');
+ if (p) {
+ *p=0;
+ smbw_setshared("PASSWORD",p+1);
+ }
+ smbw_setshared("USER", optarg);
+ break;
+ case 'R':
+ smbw_setshared("RESOLVE_ORDER",optarg);
+ break;
+
+ case 'h':
+ default:
+ smbsh_usage();
+ }
+ }
+
+
+ if (!smbw_getshared("USER")) {
+ printf("Username: ");
+ u = fgets_slash(line, sizeof(line)-1, x_stdin);
+ smbw_setshared("USER", u);
+ }
+
+ if (!smbw_getshared("PASSWORD")) {
+ p = getpass("Password: ");
+ smbw_setshared("PASSWORD", p);
+ }
+
+ setenv("PS1", "smbsh$ ", 1);
+
+ sys_getwd(wd);
+
+ slprintf(line,sizeof(line)-1,"PWD_%d", (int)getpid());
+
+ smbw_setshared(line, wd);
+
+ slprintf(line,sizeof(line)-1,"%s/smbwrapper.so", libd);
+ setenv("LD_PRELOAD", line, 1);
+
+ slprintf(line,sizeof(line)-1,"%s/smbwrapper.32.so", libd);
+
+ if (file_exist(line, NULL)) {
+ slprintf(line,sizeof(line)-1,"%s/smbwrapper.32.so:DEFAULT", libd);
+ setenv("_RLD_LIST", line, 1);
+ slprintf(line,sizeof(line)-1,"%s/smbwrapper.so:DEFAULT", libd);
+ setenv("_RLDN32_LIST", line, 1);
+ } else {
+ slprintf(line,sizeof(line)-1,"%s/smbwrapper.so:DEFAULT", libd);
+ setenv("_RLD_LIST", line, 1);
+ }
+
+ {
+ char *shellpath = getenv("SHELL");
+ if(shellpath)
+ execl(shellpath,"smbsh",NULL);
+ else
+ execl("/bin/sh","smbsh",NULL);
+ }
+ printf("launch failed!\n");
+ return 1;
+}
diff --git a/source/smbwrapper/smbsh.in b/source/smbwrapper/smbsh.in
new file mode 100644
index 00000000000..323f0916993
--- /dev/null
+++ b/source/smbwrapper/smbsh.in
@@ -0,0 +1,54 @@
+#! /bin/sh
+
+SMBW_LIBDIR=${SMBW_LIBDIR-@builddir@/smbwrapper}
+
+if [ ! -f ${SMBW_LIBDIR}/smbwrapper.so ]; then
+ echo You need to set LIBDIR in smbsh
+ exit
+fi
+
+# a simple launcher for the smbwrapper.so preloadde library
+
+if [ x"${SMBW_USER+set}" != x"set" ]; then
+ echo username?
+ read user
+ SMBW_USER=$user
+ export SMBW_USER
+fi
+
+# this doesn't hide the password - we need a proper launch app for that
+if [ x"${SMBW_PASSWORD+set}" != x"set" ]; then
+ echo password?
+ read pass
+ SMBW_PASSWORD=$pass
+ export SMBW_PASSWORD
+fi
+
+PWD=`pwd`
+export PWD
+PS1='smbsh$ '
+export PS1
+
+
+host_os=@HOST_OS@
+
+case "$host_os" in
+ *irix*)
+ _RLDN32_LIST=$SMBW_LIBDIR/smbwrapper.so:DEFAULT
+ _RLD_LIST=$SMBW_LIBDIR/smbwrapper.32.so:DEFAULT
+ export _RLDN32_LIST
+ export _RLD_LIST
+ ;;
+ *osf*)
+ _RLD_LIST=$SMBW_LIBDIR/smbwrapper.so:DEFAULT
+ export _RLD_LIST
+ ;;
+ *)
+ LD_PRELOAD=$SMBW_LIBDIR/smbwrapper.so
+ export LD_PRELOAD
+ ;;
+esac
+
+echo starting smbwrapper on $host_os
+
+exec ${SMBW_SHELL-${SHELL-/bin/sh}} ${1+"$@"}
diff --git a/source/smbwrapper/smbw.c b/source/smbwrapper/smbw.c
new file mode 100644
index 00000000000..0ddacdf8ba5
--- /dev/null
+++ b/source/smbwrapper/smbw.c
@@ -0,0 +1,1562 @@
+/*
+ Unix SMB/CIFS implementation.
+ SMB wrapper functions
+ 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 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 "realcalls.h"
+
+pstring smbw_cwd;
+
+static struct smbw_file *smbw_files;
+static struct smbw_server *smbw_srvs;
+
+struct bitmap *smbw_file_bmap;
+
+fstring smbw_prefix = SMBW_PREFIX;
+
+int smbw_busy=0;
+
+/* needs to be here because of dumb include files on some systems */
+int creat_bits = O_WRONLY|O_CREAT|O_TRUNC;
+
+
+/*****************************************************
+initialise structures
+*******************************************************/
+void smbw_init(void)
+{
+ extern BOOL in_client;
+ static int initialised;
+ char *p;
+ int eno;
+ pstring line;
+
+ if (initialised) return;
+ initialised = 1;
+
+ eno = errno;
+
+ smbw_busy++;
+
+ DEBUGLEVEL = 0;
+ setup_logging("smbsh",True);
+
+ dbf = x_stderr;
+
+ if ((p=smbw_getshared("LOGFILE"))) {
+ dbf = sys_fopen(p, "a");
+ }
+
+ smbw_file_bmap = bitmap_allocate(SMBW_MAX_OPEN);
+ if (!smbw_file_bmap) {
+ exit(1);
+ }
+
+ in_client = True;
+
+ load_interfaces();
+
+ if ((p=smbw_getshared("SERVICESF"))) {
+ pstrcpy(dyn_CONFIGFILE, p);
+ }
+
+ lp_load(dyn_CONFIGFILE,True,False,False);
+
+ if (!init_names())
+ exit(1);
+
+ if ((p=smbw_getshared("DEBUG"))) {
+ DEBUGLEVEL = atoi(p);
+ }
+
+ if ((p=smbw_getshared("RESOLVE_ORDER"))) {
+ lp_set_name_resolve_order(p);
+ }
+
+ if ((p=smbw_getshared("PREFIX"))) {
+ slprintf(smbw_prefix,sizeof(fstring)-1, "/%s/", p);
+ all_string_sub(smbw_prefix,"//", "/", 0);
+ DEBUG(2,("SMBW_PREFIX is %s\n", smbw_prefix));
+ }
+
+ slprintf(line,sizeof(line)-1,"PWD_%d", (int)getpid());
+
+ p = smbw_getshared(line);
+ if (!p) {
+ sys_getwd(smbw_cwd);
+ }
+ pstrcpy(smbw_cwd, p);
+ DEBUG(4,("Initial cwd is %s\n", smbw_cwd));
+
+ smbw_busy--;
+
+ set_maxfiles(SMBW_MAX_OPEN);
+
+ BlockSignals(True,SIGPIPE);
+
+ errno = eno;
+}
+
+/*****************************************************
+determine if a file descriptor is a smb one
+*******************************************************/
+int smbw_fd(int fd)
+{
+ if (smbw_busy) return 0;
+ return smbw_file_bmap && bitmap_query(smbw_file_bmap, fd);
+}
+
+/*****************************************************
+determine if a file descriptor is an internal smbw fd
+*******************************************************/
+int smbw_local_fd(int fd)
+{
+ struct smbw_server *srv;
+
+ smbw_init();
+
+ if (smbw_busy) return 0;
+ if (smbw_shared_fd(fd)) return 1;
+
+ for (srv=smbw_srvs;srv;srv=srv->next) {
+ if (srv->cli.fd == fd) return 1;
+ }
+
+ return 0;
+}
+
+/*****************************************************
+a crude inode number generator
+*******************************************************/
+ino_t smbw_inode(const char *name)
+{
+ if (!*name) return 2;
+ return (ino_t)str_checksum(name);
+}
+
+/*****************************************************
+remove redundent stuff from a filename
+*******************************************************/
+void clean_fname(char *name)
+{
+ char *p, *p2;
+ int l;
+ int modified = 1;
+
+ if (!name) return;
+
+ while (modified) {
+ modified = 0;
+
+ DEBUG(5,("cleaning %s\n", name));
+
+ if ((p=strstr(name,"/./"))) {
+ modified = 1;
+ while (*p) {
+ p[0] = p[2];
+ p++;
+ }
+ }
+
+ if ((p=strstr(name,"//"))) {
+ modified = 1;
+ while (*p) {
+ p[0] = p[1];
+ p++;
+ }
+ }
+
+ if (strcmp(name,"/../")==0) {
+ modified = 1;
+ name[1] = 0;
+ }
+
+ if ((p=strstr(name,"/../"))) {
+ modified = 1;
+ for (p2=(p>name?p-1:p);p2>name;p2--) {
+ if (p2[0] == '/') break;
+ }
+ while (*p2) {
+ p2[0] = p2[3];
+ p2++;
+ }
+ }
+
+ if (strcmp(name,"/..")==0) {
+ modified = 1;
+ name[1] = 0;
+ }
+
+ l = strlen(name);
+ p = l>=3?(name+l-3):name;
+ if (strcmp(p,"/..")==0) {
+ modified = 1;
+ for (p2=p-1;p2>name;p2--) {
+ if (p2[0] == '/') break;
+ }
+ if (p2==name) {
+ p[0] = '/';
+ p[1] = 0;
+ } else {
+ p2[0] = 0;
+ }
+ }
+
+ l = strlen(name);
+ p = l>=2?(name+l-2):name;
+ if (strcmp(p,"/.")==0) {
+ if (p == name) {
+ p[1] = 0;
+ } else {
+ p[0] = 0;
+ }
+ }
+
+ if (strncmp(p=name,"./",2) == 0) {
+ modified = 1;
+ do {
+ p[0] = p[2];
+ } while (*p++);
+ }
+
+ l = strlen(p=name);
+ if (l > 1 && p[l-1] == '/') {
+ modified = 1;
+ p[l-1] = 0;
+ }
+ }
+}
+
+
+
+/*****************************************************
+find a workgroup (any workgroup!) that has a master
+browser on the local network
+*******************************************************/
+static char *smbw_find_workgroup(void)
+{
+ fstring server;
+ char *p;
+ struct in_addr *ip_list = NULL;
+ int count = 0;
+ int i;
+
+ /* first off see if an existing workgroup name exists */
+ p = smbw_getshared("WORKGROUP");
+ if (!p) p = lp_workgroup();
+
+ slprintf(server, sizeof(server), "%s#1D", p);
+ if (smbw_server(server, "IPC$")) return p;
+
+ /* go looking for workgroups */
+ if (!name_resolve_bcast(MSBROWSE, 1, &ip_list, &count)) {
+ DEBUG(1,("No workgroups found!"));
+ return p;
+ }
+
+ for (i=0;i<count;i++) {
+ static fstring name;
+ if (name_status_find("*", 0, 0x1d, ip_list[i], name)) {
+ slprintf(server, sizeof(server), "%s#1D", name);
+ if (smbw_server(server, "IPC$")) {
+ smbw_setshared("WORKGROUP", name);
+ SAFE_FREE(ip_list);
+ return name;
+ }
+ }
+ }
+
+ SAFE_FREE(ip_list);
+
+ return p;
+}
+
+/*****************************************************
+parse a smb path into its components.
+server is one of
+ 1) the name of the SMB server
+ 2) WORKGROUP#1D for share listing
+ 3) WORKGROUP#__ for workgroup listing
+share is the share on the server to query
+path is the SMB path on the server
+return the full path (ie. add cwd if needed)
+*******************************************************/
+char *smbw_parse_path(const char *fname, char *server, char *share, char *path)
+{
+ static pstring s;
+ char *p;
+ int len;
+ fstring workgroup;
+
+ /* add cwd if necessary */
+ if (fname[0] != '/') {
+ slprintf(s, sizeof(s), "%s/%s", smbw_cwd, fname);
+ } else {
+ pstrcpy(s, fname);
+ }
+ clean_fname(s);
+
+ /* see if it has the right prefix */
+ len = strlen(smbw_prefix)-1;
+ if (strncmp(s,smbw_prefix,len) ||
+ (s[len] != '/' && s[len] != 0)) return s;
+
+ /* ok, its for us. Now parse out the workgroup, share etc. */
+ p = s+len;
+ if (*p == '/') p++;
+ if (!next_token(&p, workgroup, "/", sizeof(fstring))) {
+ /* we're in /smb - give a list of workgroups */
+ slprintf(server,sizeof(fstring), "%s#01", smbw_find_workgroup());
+ fstrcpy(share,"IPC$");
+ pstrcpy(path,"");
+ return s;
+ }
+
+ if (!next_token(&p, server, "/", sizeof(fstring))) {
+ /* we are in /smb/WORKGROUP */
+ slprintf(server,sizeof(fstring), "%s#1D", workgroup);
+ fstrcpy(share,"IPC$");
+ pstrcpy(path,"");
+ }
+
+ if (!next_token(&p, share, "/", sizeof(fstring))) {
+ /* we are in /smb/WORKGROUP/SERVER */
+ fstrcpy(share,"IPC$");
+ pstrcpy(path,"");
+ }
+
+ pstrcpy(path, p);
+
+ all_string_sub(path, "/", "\\", 0);
+
+ return s;
+}
+
+/*****************************************************
+determine if a path name (possibly relative) is in the
+smb name space
+*******************************************************/
+int smbw_path(const char *path)
+{
+ fstring server, share;
+ pstring s;
+ char *cwd;
+ int len;
+
+ if(!path)
+ return 0;
+
+ /* this is needed to prevent recursion with the BSD malloc which
+ opens /etc/malloc.conf on the first call */
+ if (strncmp(path,"/etc/", 5) == 0) {
+ return 0;
+ }
+
+ smbw_init();
+
+ len = strlen(smbw_prefix)-1;
+
+ if (path[0] == '/' && strncmp(path,smbw_prefix,len)) {
+ return 0;
+ }
+
+ if (smbw_busy) return 0;
+
+ DEBUG(3,("smbw_path(%s)\n", path));
+
+ cwd = smbw_parse_path(path, server, share, s);
+
+ if (strncmp(cwd,smbw_prefix,len) == 0 &&
+ (cwd[len] == '/' || cwd[len] == 0)) {
+ return 1;
+ }
+
+ return 0;
+}
+
+/*****************************************************
+return a unix errno from a SMB error pair
+*******************************************************/
+int smbw_errno(struct cli_state *c)
+{
+ return cli_errno(c);
+}
+
+/* Return a username and password given a server and share name */
+
+void get_envvar_auth_data(char *server, char *share, char **workgroup,
+ char **username, char **password)
+{
+ /* Fall back to shared memory/environment variables */
+
+ *username = smbw_getshared("USER");
+ if (!*username) *username = getenv("USER");
+ if (!*username) *username = "guest";
+
+ *workgroup = smbw_getshared("WORKGROUP");
+ if (!*workgroup) *workgroup = lp_workgroup();
+
+ *password = smbw_getshared("PASSWORD");
+ if (!*password) *password = "";
+}
+
+static smbw_get_auth_data_fn get_auth_data_fn = get_envvar_auth_data;
+
+/*****************************************************
+set the get auth data function
+******************************************************/
+void smbw_set_auth_data_fn(smbw_get_auth_data_fn fn)
+{
+ get_auth_data_fn = fn;
+}
+
+/*****************************************************
+return a connection to a server (existing or new)
+*******************************************************/
+struct smbw_server *smbw_server(char *server, char *share)
+{
+ struct smbw_server *srv=NULL;
+ struct cli_state c;
+ char *username;
+ char *password;
+ char *workgroup;
+ struct nmb_name called, calling;
+ char *p, *server_n = server;
+ fstring group;
+ pstring ipenv;
+ struct in_addr ip;
+
+ zero_ip(&ip);
+ ZERO_STRUCT(c);
+
+ get_auth_data_fn(server, share, &workgroup, &username, &password);
+
+ /* try to use an existing connection */
+ for (srv=smbw_srvs;srv;srv=srv->next) {
+ if (strcmp(server,srv->server_name)==0 &&
+ strcmp(share,srv->share_name)==0 &&
+ strcmp(workgroup,srv->workgroup)==0 &&
+ strcmp(username, srv->username) == 0)
+ return srv;
+ }
+
+ if (server[0] == 0) {
+ errno = EPERM;
+ return NULL;
+ }
+
+ make_nmb_name(&calling, global_myname(), 0x0);
+ make_nmb_name(&called , server, 0x20);
+
+ DEBUG(4,("server_n=[%s] server=[%s]\n", server_n, server));
+
+ if ((p=strchr_m(server_n,'#')) &&
+ (strcmp(p+1,"1D")==0 || strcmp(p+1,"01")==0)) {
+ struct in_addr sip;
+ pstring s;
+
+ fstrcpy(group, server_n);
+ p = strchr_m(group,'#');
+ *p = 0;
+
+ /* cache the workgroup master lookup */
+ slprintf(s,sizeof(s)-1,"MASTER_%s", group);
+ if (!(server_n = smbw_getshared(s))) {
+ if (!find_master_ip(group, &sip)) {
+ errno = ENOENT;
+ return NULL;
+ }
+ fstrcpy(group, inet_ntoa(sip));
+ server_n = group;
+ smbw_setshared(s,server_n);
+ }
+ }
+
+ DEBUG(4,(" -> server_n=[%s] server=[%s]\n", server_n, server));
+
+ again:
+ slprintf(ipenv,sizeof(ipenv)-1,"HOST_%s", server_n);
+
+ zero_ip(&ip);
+ if ((p=smbw_getshared(ipenv))) {
+ ip = *(interpret_addr2(p));
+ }
+
+ /* have to open a new connection */
+ if (!cli_initialise(&c) || !cli_connect(&c, server_n, &ip)) {
+ errno = ENOENT;
+ return NULL;
+ }
+
+ if (!cli_session_request(&c, &calling, &called)) {
+ cli_shutdown(&c);
+ if (strcmp(called.name, "*SMBSERVER")) {
+ make_nmb_name(&called , "*SMBSERVER", 0x20);
+ goto again;
+ }
+ errno = ENOENT;
+ return NULL;
+ }
+
+ DEBUG(4,(" session request ok\n"));
+
+ if (!cli_negprot(&c)) {
+ cli_shutdown(&c);
+ errno = ENOENT;
+ return NULL;
+ }
+
+ if (!cli_session_setup(&c, username,
+ password, strlen(password),
+ password, strlen(password),
+ workgroup) &&
+ /* try an anonymous login if it failed */
+ !cli_session_setup(&c, "", "", 1,"", 0, workgroup)) {
+ cli_shutdown(&c);
+ errno = EPERM;
+ return NULL;
+ }
+
+ DEBUG(4,(" session setup ok\n"));
+
+ if (!cli_send_tconX(&c, share, "?????",
+ password, strlen(password)+1)) {
+ errno = smbw_errno(&c);
+ cli_shutdown(&c);
+ return NULL;
+ }
+
+ smbw_setshared(ipenv,inet_ntoa(ip));
+
+ DEBUG(4,(" tconx ok\n"));
+
+ srv = (struct smbw_server *)malloc(sizeof(*srv));
+ if (!srv) {
+ errno = ENOMEM;
+ goto failed;
+ }
+
+ ZERO_STRUCTP(srv);
+
+ srv->cli = c;
+
+ srv->dev = (dev_t)(str_checksum(server) ^ str_checksum(share));
+
+ srv->server_name = strdup(server);
+ if (!srv->server_name) {
+ errno = ENOMEM;
+ goto failed;
+ }
+
+ srv->share_name = strdup(share);
+ if (!srv->share_name) {
+ errno = ENOMEM;
+ goto failed;
+ }
+
+ srv->workgroup = strdup(workgroup);
+ if (!srv->workgroup) {
+ errno = ENOMEM;
+ goto failed;
+ }
+
+ srv->username = strdup(username);
+ if (!srv->username) {
+ errno = ENOMEM;
+ goto failed;
+ }
+
+ /* some programs play with file descriptors fairly intimately. We
+ try to get out of the way by duping to a high fd number */
+ if (fcntl(SMBW_CLI_FD + srv->cli.fd, F_GETFD) && errno == EBADF) {
+ if (dup2(srv->cli.fd,SMBW_CLI_FD+srv->cli.fd) ==
+ srv->cli.fd+SMBW_CLI_FD) {
+ close(srv->cli.fd);
+ srv->cli.fd += SMBW_CLI_FD;
+ }
+ }
+
+ DLIST_ADD(smbw_srvs, srv);
+
+ return srv;
+
+ failed:
+ cli_shutdown(&c);
+ if (!srv) return NULL;
+
+ SAFE_FREE(srv->server_name);
+ SAFE_FREE(srv->share_name);
+ SAFE_FREE(srv);
+ return NULL;
+}
+
+
+/*****************************************************
+map a fd to a smbw_file structure
+*******************************************************/
+struct smbw_file *smbw_file(int fd)
+{
+ struct smbw_file *file;
+
+ for (file=smbw_files;file;file=file->next) {
+ if (file->fd == fd) return file;
+ }
+ return NULL;
+}
+
+/*****************************************************
+a wrapper for open()
+*******************************************************/
+int smbw_open(const char *fname, int flags, mode_t mode)
+{
+ fstring server, share;
+ pstring path;
+ struct smbw_server *srv=NULL;
+ int eno=0, fd = -1;
+ struct smbw_file *file=NULL;
+
+ smbw_init();
+
+ if (!fname) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ smbw_busy++;
+
+ /* work out what server they are after */
+ smbw_parse_path(fname, server, share, path);
+
+ /* get a connection to the server */
+ srv = smbw_server(server, share);
+ if (!srv) {
+ /* smbw_server sets errno */
+ goto failed;
+ }
+
+ if (path[strlen(path)-1] == '\\') {
+ fd = -1;
+ } else {
+ fd = cli_open(&srv->cli, path, flags, DENY_NONE);
+ }
+ if (fd == -1) {
+ /* it might be a directory. Maybe we should use chkpath? */
+ eno = smbw_errno(&srv->cli);
+ fd = smbw_dir_open(fname);
+ if (fd == -1) errno = eno;
+ smbw_busy--;
+ return fd;
+ }
+
+ file = (struct smbw_file *)malloc(sizeof(*file));
+ if (!file) {
+ errno = ENOMEM;
+ goto failed;
+ }
+
+ ZERO_STRUCTP(file);
+
+ file->f = (struct smbw_filedes *)malloc(sizeof(*(file->f)));
+ if (!file->f) {
+ errno = ENOMEM;
+ goto failed;
+ }
+
+ ZERO_STRUCTP(file->f);
+
+ file->f->cli_fd = fd;
+ file->f->fname = strdup(path);
+ if (!file->f->fname) {
+ errno = ENOMEM;
+ goto failed;
+ }
+ file->srv = srv;
+ file->fd = open(SMBW_DUMMY, O_WRONLY);
+ if (file->fd == -1) {
+ errno = EMFILE;
+ goto failed;
+ }
+
+ if (bitmap_query(smbw_file_bmap, file->fd)) {
+ DEBUG(0,("ERROR: fd used in smbw_open\n"));
+ errno = EIO;
+ goto failed;
+ }
+
+ file->f->ref_count=1;
+
+ bitmap_set(smbw_file_bmap, file->fd);
+
+ DLIST_ADD(smbw_files, file);
+
+ DEBUG(4,("opened %s\n", fname));
+
+ smbw_busy--;
+ return file->fd;
+
+ failed:
+ if (fd != -1) {
+ cli_close(&srv->cli, fd);
+ }
+ if (file) {
+ if (file->f) {
+ SAFE_FREE(file->f->fname);
+ SAFE_FREE(file->f);
+ }
+ SAFE_FREE(file);
+ }
+ smbw_busy--;
+ return -1;
+}
+
+
+/*****************************************************
+a wrapper for pread()
+*******************************************************/
+ssize_t smbw_pread(int fd, void *buf, size_t count, off_t ofs)
+{
+ struct smbw_file *file;
+ int ret;
+
+ smbw_busy++;
+
+ file = smbw_file(fd);
+ if (!file) {
+ errno = EBADF;
+ smbw_busy--;
+ return -1;
+ }
+
+ ret = cli_read(&file->srv->cli, file->f->cli_fd, buf, ofs, count);
+
+ if (ret == -1) {
+ errno = smbw_errno(&file->srv->cli);
+ smbw_busy--;
+ return -1;
+ }
+
+ smbw_busy--;
+ return ret;
+}
+
+/*****************************************************
+a wrapper for read()
+*******************************************************/
+ssize_t smbw_read(int fd, void *buf, size_t count)
+{
+ struct smbw_file *file;
+ int ret;
+
+ DEBUG(4,("smbw_read(%d, %d)\n", fd, (int)count));
+
+ smbw_busy++;
+
+ file = smbw_file(fd);
+ if (!file) {
+ errno = EBADF;
+ smbw_busy--;
+ return -1;
+ }
+
+ ret = cli_read(&file->srv->cli, file->f->cli_fd, buf,
+ file->f->offset, count);
+
+ if (ret == -1) {
+ errno = smbw_errno(&file->srv->cli);
+ smbw_busy--;
+ return -1;
+ }
+
+ file->f->offset += ret;
+
+ DEBUG(4,(" -> %d\n", ret));
+
+ smbw_busy--;
+ return ret;
+}
+
+
+
+/*****************************************************
+a wrapper for write()
+*******************************************************/
+ssize_t smbw_write(int fd, void *buf, size_t count)
+{
+ struct smbw_file *file;
+ int ret;
+
+ smbw_busy++;
+
+ file = smbw_file(fd);
+ if (!file) {
+ errno = EBADF;
+ smbw_busy--;
+ return -1;
+ }
+
+ ret = cli_write(&file->srv->cli, file->f->cli_fd, 0, buf, file->f->offset, count);
+
+ if (ret == -1) {
+ errno = smbw_errno(&file->srv->cli);
+ smbw_busy--;
+ return -1;
+ }
+
+ file->f->offset += ret;
+
+ smbw_busy--;
+ return ret;
+}
+
+/*****************************************************
+a wrapper for pwrite()
+*******************************************************/
+ssize_t smbw_pwrite(int fd, void *buf, size_t count, off_t ofs)
+{
+ struct smbw_file *file;
+ int ret;
+
+ smbw_busy++;
+
+ file = smbw_file(fd);
+ if (!file) {
+ errno = EBADF;
+ smbw_busy--;
+ return -1;
+ }
+
+ ret = cli_write(&file->srv->cli, file->f->cli_fd, 0, buf, ofs, count);
+
+ if (ret == -1) {
+ errno = smbw_errno(&file->srv->cli);
+ smbw_busy--;
+ return -1;
+ }
+
+ smbw_busy--;
+ return ret;
+}
+
+/*****************************************************
+a wrapper for close()
+*******************************************************/
+int smbw_close(int fd)
+{
+ struct smbw_file *file;
+
+ smbw_busy++;
+
+ file = smbw_file(fd);
+ if (!file) {
+ int ret = smbw_dir_close(fd);
+ smbw_busy--;
+ return ret;
+ }
+
+ if (file->f->ref_count == 1 &&
+ !cli_close(&file->srv->cli, file->f->cli_fd)) {
+ errno = smbw_errno(&file->srv->cli);
+ smbw_busy--;
+ return -1;
+ }
+
+
+ bitmap_clear(smbw_file_bmap, file->fd);
+ close(file->fd);
+
+ DLIST_REMOVE(smbw_files, file);
+
+ file->f->ref_count--;
+ if (file->f->ref_count == 0) {
+ SAFE_FREE(file->f->fname);
+ SAFE_FREE(file->f);
+ }
+ ZERO_STRUCTP(file);
+ SAFE_FREE(file);
+
+ smbw_busy--;
+
+ return 0;
+}
+
+
+/*****************************************************
+a wrapper for fcntl()
+*******************************************************/
+int smbw_fcntl(int fd, int cmd, long arg)
+{
+ return 0;
+}
+
+
+/*****************************************************
+a wrapper for access()
+*******************************************************/
+int smbw_access(const char *name, int mode)
+{
+ struct stat st;
+
+ DEBUG(4,("smbw_access(%s, 0x%x)\n", name, mode));
+
+ if (smbw_stat(name, &st)) return -1;
+
+ if (((mode & R_OK) && !(st.st_mode & S_IRUSR)) ||
+ ((mode & W_OK) && !(st.st_mode & S_IWUSR)) ||
+ ((mode & X_OK) && !(st.st_mode & S_IXUSR))) {
+ errno = EACCES;
+ return -1;
+ }
+
+ return 0;
+}
+
+/*****************************************************
+a wrapper for realink() - needed for correct errno setting
+*******************************************************/
+int smbw_readlink(const char *path, char *buf, size_t bufsize)
+{
+ struct stat st;
+ int ret;
+
+ ret = smbw_stat(path, &st);
+ if (ret != 0) {
+ DEBUG(4,("readlink(%s) failed\n", path));
+ return -1;
+ }
+
+ /* it exists - say it isn't a link */
+ DEBUG(4,("readlink(%s) not a link\n", path));
+
+ errno = EINVAL;
+ return -1;
+}
+
+
+/*****************************************************
+a wrapper for unlink()
+*******************************************************/
+int smbw_unlink(const char *fname)
+{
+ struct smbw_server *srv;
+ fstring server, share;
+ pstring path;
+
+ if (!fname) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ smbw_init();
+
+ smbw_busy++;
+
+ /* work out what server they are after */
+ smbw_parse_path(fname, server, share, path);
+
+ /* get a connection to the server */
+ srv = smbw_server(server, share);
+ if (!srv) {
+ /* smbw_server sets errno */
+ goto failed;
+ }
+
+ if (strncmp(srv->cli.dev, "LPT", 3) == 0) {
+ int job = smbw_stat_printjob(srv, path, NULL, NULL);
+ if (job == -1) {
+ goto failed;
+ }
+ if (cli_printjob_del(&srv->cli, job) != 0) {
+ goto failed;
+ }
+ } else if (!cli_unlink(&srv->cli, path)) {
+ errno = smbw_errno(&srv->cli);
+ goto failed;
+ }
+
+ smbw_busy--;
+ return 0;
+
+ failed:
+ smbw_busy--;
+ return -1;
+}
+
+
+/*****************************************************
+a wrapper for rename()
+*******************************************************/
+int smbw_rename(const char *oldname, const char *newname)
+{
+ struct smbw_server *srv;
+ fstring server1, share1;
+ pstring path1;
+ fstring server2, share2;
+ pstring path2;
+
+ if (!oldname || !newname) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ smbw_init();
+
+ DEBUG(4,("smbw_rename(%s,%s)\n", oldname, newname));
+
+ smbw_busy++;
+
+ /* work out what server they are after */
+ smbw_parse_path(oldname, server1, share1, path1);
+ smbw_parse_path(newname, server2, share2, path2);
+
+ if (strcmp(server1, server2) || strcmp(share1, share2)) {
+ /* can't cross filesystems */
+ errno = EXDEV;
+ return -1;
+ }
+
+ /* get a connection to the server */
+ srv = smbw_server(server1, share1);
+ if (!srv) {
+ /* smbw_server sets errno */
+ goto failed;
+ }
+
+ if (!cli_rename(&srv->cli, path1, path2)) {
+ int eno = smbw_errno(&srv->cli);
+ if (eno != EEXIST ||
+ !cli_unlink(&srv->cli, path2) ||
+ !cli_rename(&srv->cli, path1, path2)) {
+ errno = eno;
+ goto failed;
+ }
+ }
+
+ smbw_busy--;
+ return 0;
+
+ failed:
+ smbw_busy--;
+ return -1;
+}
+
+
+/*****************************************************
+a wrapper for utime and utimes
+*******************************************************/
+static int smbw_settime(const char *fname, time_t t)
+{
+ struct smbw_server *srv;
+ fstring server, share;
+ pstring path;
+ uint16 mode;
+
+ if (!fname) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ smbw_init();
+
+ smbw_busy++;
+
+ /* work out what server they are after */
+ smbw_parse_path(fname, server, share, path);
+
+ /* get a connection to the server */
+ srv = smbw_server(server, share);
+ if (!srv) {
+ /* smbw_server sets errno */
+ goto failed;
+ }
+
+ if (!cli_getatr(&srv->cli, path, &mode, NULL, NULL)) {
+ errno = smbw_errno(&srv->cli);
+ goto failed;
+ }
+
+ if (!cli_setatr(&srv->cli, path, mode, t)) {
+ /* some servers always refuse directory changes */
+ if (!(mode & aDIR)) {
+ errno = smbw_errno(&srv->cli);
+ goto failed;
+ }
+ }
+
+ smbw_busy--;
+ return 0;
+
+ failed:
+ smbw_busy--;
+ return -1;
+}
+
+/*****************************************************
+a wrapper for utime
+*******************************************************/
+int smbw_utime(const char *fname, void *buf)
+{
+ struct utimbuf *tbuf = (struct utimbuf *)buf;
+ return smbw_settime(fname, tbuf?tbuf->modtime:time(NULL));
+}
+
+/*****************************************************
+a wrapper for utime
+*******************************************************/
+int smbw_utimes(const char *fname, void *buf)
+{
+ struct timeval *tbuf = (struct timeval *)buf;
+ return smbw_settime(fname, tbuf?tbuf->tv_sec:time(NULL));
+}
+
+
+/*****************************************************
+a wrapper for chown()
+*******************************************************/
+int smbw_chown(const char *fname, uid_t owner, gid_t group)
+{
+ struct smbw_server *srv;
+ fstring server, share;
+ pstring path;
+ uint16 mode;
+
+ if (!fname) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ smbw_init();
+
+ smbw_busy++;
+
+ /* work out what server they are after */
+ smbw_parse_path(fname, server, share, path);
+
+ /* get a connection to the server */
+ srv = smbw_server(server, share);
+ if (!srv) {
+ /* smbw_server sets errno */
+ goto failed;
+ }
+
+ if (!cli_getatr(&srv->cli, path, &mode, NULL, NULL)) {
+ errno = smbw_errno(&srv->cli);
+ goto failed;
+ }
+
+ /* assume success */
+
+ smbw_busy--;
+ return 0;
+
+ failed:
+ smbw_busy--;
+ return -1;
+}
+
+/*****************************************************
+a wrapper for chmod()
+*******************************************************/
+int smbw_chmod(const char *fname, mode_t newmode)
+{
+ struct smbw_server *srv;
+ fstring server, share;
+ pstring path;
+ uint32 mode;
+
+ if (!fname) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ smbw_init();
+
+ smbw_busy++;
+
+ /* work out what server they are after */
+ smbw_parse_path(fname, server, share, path);
+
+ /* get a connection to the server */
+ srv = smbw_server(server, share);
+ if (!srv) {
+ /* smbw_server sets errno */
+ goto failed;
+ }
+
+ mode = 0;
+
+ if (!(newmode & (S_IWUSR | S_IWGRP | S_IWOTH))) mode |= aRONLY;
+ if ((newmode & S_IXUSR) && lp_map_archive(-1)) mode |= aARCH;
+ if ((newmode & S_IXGRP) && lp_map_system(-1)) mode |= aSYSTEM;
+ if ((newmode & S_IXOTH) && lp_map_hidden(-1)) mode |= aHIDDEN;
+
+ if (!cli_setatr(&srv->cli, path, mode, 0)) {
+ errno = smbw_errno(&srv->cli);
+ goto failed;
+ }
+
+ smbw_busy--;
+ return 0;
+
+ failed:
+ smbw_busy--;
+ return -1;
+}
+
+/*****************************************************
+a wrapper for lseek()
+*******************************************************/
+off_t smbw_lseek(int fd, off_t offset, int whence)
+{
+ struct smbw_file *file;
+ size_t size;
+
+ smbw_busy++;
+
+ file = smbw_file(fd);
+ if (!file) {
+ off_t ret = smbw_dir_lseek(fd, offset, whence);
+ smbw_busy--;
+ return ret;
+ }
+
+ switch (whence) {
+ case SEEK_SET:
+ file->f->offset = offset;
+ break;
+ case SEEK_CUR:
+ file->f->offset += offset;
+ break;
+ case SEEK_END:
+ if (!cli_qfileinfo(&file->srv->cli, file->f->cli_fd,
+ NULL, &size, NULL, NULL, NULL,
+ NULL, NULL) &&
+ !cli_getattrE(&file->srv->cli, file->f->cli_fd,
+ NULL, &size, NULL, NULL, NULL)) {
+ errno = EINVAL;
+ smbw_busy--;
+ return -1;
+ }
+ file->f->offset = size + offset;
+ break;
+ }
+
+ smbw_busy--;
+ return file->f->offset;
+}
+
+
+/*****************************************************
+a wrapper for dup()
+*******************************************************/
+int smbw_dup(int fd)
+{
+ int fd2;
+ struct smbw_file *file, *file2;
+
+ smbw_busy++;
+
+ file = smbw_file(fd);
+ if (!file) {
+ errno = EBADF;
+ goto failed;
+ }
+
+ fd2 = dup(file->fd);
+ if (fd2 == -1) {
+ goto failed;
+ }
+
+ if (bitmap_query(smbw_file_bmap, fd2)) {
+ DEBUG(0,("ERROR: fd already open in dup!\n"));
+ errno = EIO;
+ goto failed;
+ }
+
+ file2 = (struct smbw_file *)malloc(sizeof(*file2));
+ if (!file2) {
+ close(fd2);
+ errno = ENOMEM;
+ goto failed;
+ }
+
+ ZERO_STRUCTP(file2);
+
+ *file2 = *file;
+ file2->fd = fd2;
+
+ file->f->ref_count++;
+
+ bitmap_set(smbw_file_bmap, fd2);
+
+ DLIST_ADD(smbw_files, file2);
+
+ smbw_busy--;
+ return fd2;
+
+ failed:
+ smbw_busy--;
+ return -1;
+}
+
+
+/*****************************************************
+a wrapper for dup2()
+*******************************************************/
+int smbw_dup2(int fd, int fd2)
+{
+ struct smbw_file *file, *file2;
+
+ smbw_busy++;
+
+ file = smbw_file(fd);
+ if (!file) {
+ errno = EBADF;
+ goto failed;
+ }
+
+ if (bitmap_query(smbw_file_bmap, fd2)) {
+ DEBUG(0,("ERROR: fd already open in dup2!\n"));
+ errno = EIO;
+ goto failed;
+ }
+
+ if (dup2(file->fd, fd2) != fd2) {
+ goto failed;
+ }
+
+ file2 = (struct smbw_file *)malloc(sizeof(*file2));
+ if (!file2) {
+ close(fd2);
+ errno = ENOMEM;
+ goto failed;
+ }
+
+ ZERO_STRUCTP(file2);
+
+ *file2 = *file;
+ file2->fd = fd2;
+
+ file->f->ref_count++;
+
+ bitmap_set(smbw_file_bmap, fd2);
+
+ DLIST_ADD(smbw_files, file2);
+
+ smbw_busy--;
+ return fd2;
+
+ failed:
+ smbw_busy--;
+ return -1;
+}
+
+
+/*****************************************************
+close a connection to a server
+*******************************************************/
+static void smbw_srv_close(struct smbw_server *srv)
+{
+ smbw_busy++;
+
+ cli_shutdown(&srv->cli);
+
+ SAFE_FREE(srv->server_name);
+ SAFE_FREE(srv->share_name);
+
+ DLIST_REMOVE(smbw_srvs, srv);
+
+ ZERO_STRUCTP(srv);
+
+ SAFE_FREE(srv);
+
+ smbw_busy--;
+}
+
+/*****************************************************
+when we fork we have to close all connections and files
+in the child
+*******************************************************/
+int smbw_fork(void)
+{
+ pid_t child;
+ int p[2];
+ char c=0;
+ pstring line;
+
+ struct smbw_file *file, *next_file;
+ struct smbw_server *srv, *next_srv;
+
+ if (pipe(p)) return real_fork();
+
+ child = real_fork();
+
+ if (child) {
+ /* block the parent for a moment until the sockets are
+ closed */
+ close(p[1]);
+ read(p[0], &c, 1);
+ close(p[0]);
+ return child;
+ }
+
+ close(p[0]);
+
+ /* close all files */
+ for (file=smbw_files;file;file=next_file) {
+ next_file = file->next;
+ close(file->fd);
+ }
+
+ /* close all server connections */
+ for (srv=smbw_srvs;srv;srv=next_srv) {
+ next_srv = srv->next;
+ smbw_srv_close(srv);
+ }
+
+ slprintf(line,sizeof(line)-1,"PWD_%d", (int)getpid());
+ smbw_setshared(line,smbw_cwd);
+
+ /* unblock the parent */
+ write(p[1], &c, 1);
+ close(p[1]);
+
+ /* and continue in the child */
+ return 0;
+}
+
+#ifndef NO_ACL_WRAPPER
+/*****************************************************
+say no to acls
+*******************************************************/
+ int smbw_acl(const char *pathp, int cmd, int nentries, aclent_t *aclbufp)
+{
+ if (cmd == GETACL || cmd == GETACLCNT) return 0;
+ errno = ENOSYS;
+ return -1;
+}
+#endif
+
+#ifndef NO_FACL_WRAPPER
+/*****************************************************
+say no to acls
+*******************************************************/
+ int smbw_facl(int fd, int cmd, int nentries, aclent_t *aclbufp)
+{
+ if (cmd == GETACL || cmd == GETACLCNT) return 0;
+ errno = ENOSYS;
+ return -1;
+}
+#endif
+
+#ifdef HAVE_EXPLICIT_LARGEFILE_SUPPORT
+#ifdef HAVE_STAT64
+/* this can't be in wrapped.c because of include conflicts */
+ void stat64_convert(struct stat *st, struct stat64 *st64)
+{
+ st64->st_size = st->st_size;
+ st64->st_mode = st->st_mode;
+ st64->st_ino = st->st_ino;
+ st64->st_dev = st->st_dev;
+ st64->st_rdev = st->st_rdev;
+ st64->st_nlink = st->st_nlink;
+ st64->st_uid = st->st_uid;
+ st64->st_gid = st->st_gid;
+ st64->st_atime = st->st_atime;
+ st64->st_mtime = st->st_mtime;
+ st64->st_ctime = st->st_ctime;
+#ifdef HAVE_STAT_ST_BLKSIZE
+ st64->st_blksize = st->st_blksize;
+#endif
+#ifdef HAVE_STAT_ST_BLOCKS
+ st64->st_blocks = st->st_blocks;
+#endif
+}
+#endif
+
+#ifdef HAVE_READDIR64
+ void dirent64_convert(struct dirent *d, struct dirent64 *d64)
+{
+ d64->d_ino = d->d_ino;
+ d64->d_off = d->d_off;
+ d64->d_reclen = d->d_reclen;
+ pstrcpy(d64->d_name, d->d_name);
+}
+#endif
+#endif
+
+
+#ifdef HAVE___XSTAT
+/* Definition of `struct stat' used in the linux kernel.. */
+struct kernel_stat {
+ unsigned short int st_dev;
+ unsigned short int __pad1;
+ unsigned long int st_ino;
+ unsigned short int st_mode;
+ unsigned short int st_nlink;
+ unsigned short int st_uid;
+ unsigned short int st_gid;
+ unsigned short int st_rdev;
+ unsigned short int __pad2;
+ unsigned long int st_size;
+ unsigned long int st_blksize;
+ unsigned long int st_blocks;
+ unsigned long int st_atime_;
+ unsigned long int __unused1;
+ unsigned long int st_mtime_;
+ unsigned long int __unused2;
+ unsigned long int st_ctime_;
+ unsigned long int __unused3;
+ unsigned long int __unused4;
+ unsigned long int __unused5;
+};
+
+/*
+ * Prototype for gcc in 'fussy' mode.
+ */
+ void xstat_convert(int vers, struct stat *st, struct kernel_stat *kbuf);
+ void xstat_convert(int vers, struct stat *st, struct kernel_stat *kbuf)
+{
+#ifdef _STAT_VER_LINUX_OLD
+ if (vers == _STAT_VER_LINUX_OLD) {
+ memcpy(st, kbuf, sizeof(*st));
+ return;
+ }
+#endif
+
+ ZERO_STRUCTP(st);
+
+ st->st_dev = kbuf->st_dev;
+ st->st_ino = kbuf->st_ino;
+ st->st_mode = kbuf->st_mode;
+ st->st_nlink = kbuf->st_nlink;
+ st->st_uid = kbuf->st_uid;
+ st->st_gid = kbuf->st_gid;
+ st->st_rdev = kbuf->st_rdev;
+ st->st_size = kbuf->st_size;
+#ifdef HAVE_STAT_ST_BLKSIZE
+ st->st_blksize = kbuf->st_blksize;
+#endif
+#ifdef HAVE_STAT_ST_BLOCKS
+ st->st_blocks = kbuf->st_blocks;
+#endif
+ st->st_atime = kbuf->st_atime_;
+ st->st_mtime = kbuf->st_mtime_;
+ st->st_ctime = kbuf->st_ctime_;
+}
+#endif
diff --git a/source/smbwrapper/smbw.h b/source/smbwrapper/smbw.h
new file mode 100644
index 00000000000..3f0b1cbb443
--- /dev/null
+++ b/source/smbwrapper/smbw.h
@@ -0,0 +1,71 @@
+/*
+ Unix SMB/CIFS implementation.
+ SMB wrapper functions - definitions
+ 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 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 _SMBW_H
+#define _SMBW_H
+
+#define SMBW_PREFIX "/smb/"
+#define SMBW_DUMMY "/dev/null"
+
+#define SMBW_CLI_FD 512
+#define SMBW_MAX_OPEN 8192
+
+#define SMBW_FILE_MODE (S_IFREG | 0444)
+#define SMBW_DIR_MODE (S_IFDIR | 0555)
+
+struct smbw_server {
+ struct smbw_server *next, *prev;
+ struct cli_state cli;
+ char *server_name;
+ char *share_name;
+ char *workgroup;
+ char *username;
+ dev_t dev;
+ BOOL no_pathinfo2;
+};
+
+struct smbw_filedes {
+ int cli_fd;
+ int ref_count;
+ char *fname;
+ off_t offset;
+};
+
+struct smbw_file {
+ struct smbw_file *next, *prev;
+ struct smbw_filedes *f;
+ int fd;
+ struct smbw_server *srv;
+};
+
+struct smbw_dir {
+ struct smbw_dir *next, *prev;
+ int fd;
+ int offset, count, malloced;
+ struct smbw_server *srv;
+ struct file_info *list;
+ char *path;
+};
+
+typedef void (*smbw_get_auth_data_fn)(char *server, char *share,
+ char **workgroup, char **username,
+ char **password);
+
+#endif /* _SMBW_H */
diff --git a/source/smbwrapper/smbw_cache.c b/source/smbwrapper/smbw_cache.c
new file mode 100644
index 00000000000..fcb0eda8050
--- /dev/null
+++ b/source/smbwrapper/smbw_cache.c
@@ -0,0 +1,207 @@
+/*
+ Unix SMB/CIFS implementation.
+ SMB wrapper directory functions
+ 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 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"
+
+/* We cache lists of workgroups, lists of servers in workgroups, and lists
+ of shares exported by servers. */
+
+#define CACHE_TIMEOUT 30
+
+struct name_list {
+ struct name_list *prev, *next;
+ char *name;
+ uint32 stype;
+ char *comment;
+};
+
+struct cached_names {
+ struct cached_names *prev, *next;
+ char *key;
+ struct name_list *name_list;
+ time_t cache_timeout;
+ int result;
+};
+
+static struct cached_names *cached_names = NULL;
+
+/* Find a list of cached name for a workgroup, server or share list */
+
+static struct cached_names *find_cached_names(char *key)
+{
+ struct cached_names *tmp;
+
+ for (tmp = cached_names; tmp; tmp = tmp->next) {
+ if (strequal(tmp->key, key)) {
+ return tmp;
+ }
+ }
+
+ return NULL;
+}
+
+/* Add a name to a list stored in the state variable */
+
+static void add_cached_names(const char *name, uint32 stype,
+ const char *comment, void *state)
+{
+ struct name_list **name_list = (struct name_list **)state;
+ struct name_list *new_name;
+
+ new_name = (struct name_list *)malloc(sizeof(struct name_list));
+ if (!new_name) return;
+
+ ZERO_STRUCTP(new_name);
+
+ new_name->name = strdup(name);
+ new_name->stype = stype;
+ new_name->comment = strdup(comment);
+
+ DLIST_ADD(*name_list, new_name);
+}
+
+static void free_name_list(struct name_list *name_list)
+{
+ struct name_list *tmp = name_list;
+
+ while(tmp) {
+ struct name_list *next;
+
+ next = tmp->next;
+
+ SAFE_FREE(tmp->name);
+ SAFE_FREE(tmp->comment);
+ SAFE_FREE(tmp);
+
+ tmp = next;
+ }
+}
+
+/* Wrapper for NetServerEnum function */
+
+BOOL smbw_NetServerEnum(struct cli_state *cli, char *workgroup, uint32 stype,
+ void (*fn)(const char *, uint32, const char *, void *),
+ void *state)
+{
+ struct cached_names *names;
+ struct name_list *tmp;
+ time_t now = time(NULL);
+ char key[PATH_MAX];
+ BOOL result = True;
+
+ slprintf(key, PATH_MAX - 1, "%s/%s#%s", cli->desthost,
+ workgroup, (stype == SV_TYPE_DOMAIN_ENUM ? "DOM" : "SRV"));
+
+ names = find_cached_names(key);
+
+ if (names == NULL || (now - names->cache_timeout) > CACHE_TIMEOUT) {
+ struct cached_names *new_names = NULL;
+
+ /* No names cached for this workgroup */
+
+ if (names == NULL) {
+ new_names = (struct cached_names *)
+ malloc(sizeof(struct cached_names));
+
+ ZERO_STRUCTP(new_names);
+ DLIST_ADD(cached_names, new_names);
+
+ } else {
+
+ /* Dispose of out of date name list */
+
+ free_name_list(names->name_list);
+ names->name_list = NULL;
+
+ new_names = names;
+ }
+
+ result = cli_NetServerEnum(cli, workgroup, stype,
+ add_cached_names,
+ &new_names->name_list);
+
+ new_names->cache_timeout = now;
+ new_names->result = result;
+ new_names->key = strdup(key);
+
+ names = new_names;
+ }
+
+ /* Return names by running callback function. */
+
+ for (tmp = names->name_list; tmp; tmp = tmp->next)
+ fn(tmp->name, stype, tmp->comment, state);
+
+ return names->result;
+}
+
+/* Wrapper for RNetShareEnum function */
+
+int smbw_RNetShareEnum(struct cli_state *cli,
+ void (*fn)(const char *, uint32, const char *, void *),
+ void *state)
+{
+ struct cached_names *names;
+ struct name_list *tmp;
+ time_t now = time(NULL);
+ char key[PATH_MAX];
+
+ slprintf(key, PATH_MAX - 1, "SHARE/%s", cli->desthost);
+
+ names = find_cached_names(key);
+
+ if (names == NULL || (now - names->cache_timeout) > CACHE_TIMEOUT) {
+ struct cached_names *new_names = NULL;
+
+ /* No names cached for this server */
+
+ if (names == NULL) {
+ new_names = (struct cached_names *)
+ malloc(sizeof(struct cached_names));
+
+ ZERO_STRUCTP(new_names);
+ DLIST_ADD(cached_names, new_names);
+
+ } else {
+
+ /* Dispose of out of date name list */
+
+ free_name_list(names->name_list);
+ names->name_list = NULL;
+
+ new_names = names;
+ }
+
+ new_names->result = cli_RNetShareEnum(cli, add_cached_names,
+ &new_names->name_list);
+
+ new_names->cache_timeout = now;
+ new_names->key = strdup(key);
+
+ names = new_names;
+ }
+
+ /* Return names by running callback function. */
+
+ for (tmp = names->name_list; tmp; tmp = tmp->next)
+ fn(tmp->name, tmp->stype, tmp->comment, state);
+
+ return names->result;
+}
diff --git a/source/smbwrapper/smbw_dir.c b/source/smbwrapper/smbw_dir.c
new file mode 100644
index 00000000000..0a6deede41f
--- /dev/null
+++ b/source/smbwrapper/smbw_dir.c
@@ -0,0 +1,689 @@
+/*
+ Unix SMB/CIFS implementation.
+ SMB wrapper directory functions
+ 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 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 "realcalls.h"
+
+extern pstring smbw_cwd;
+extern fstring smbw_prefix;
+
+static struct smbw_dir *smbw_dirs;
+
+extern struct bitmap *smbw_file_bmap;
+
+extern int smbw_busy;
+
+/*****************************************************
+map a fd to a smbw_dir structure
+*******************************************************/
+struct smbw_dir *smbw_dir(int fd)
+{
+ struct smbw_dir *dir;
+
+ for (dir=smbw_dirs;dir;dir=dir->next) {
+ if (dir->fd == fd) return dir;
+ }
+ return NULL;
+}
+
+/*****************************************************
+check if a DIR* is one of ours
+*******************************************************/
+int smbw_dirp(DIR *dirp)
+{
+ struct smbw_dir *d = (struct smbw_dir *)dirp;
+ struct smbw_dir *dir;
+
+ for (dir=smbw_dirs;dir;dir=dir->next) {
+ if (dir == d) return 1;
+ }
+ return 0;
+}
+
+/*****************************************************
+free a smbw_dir structure and all entries
+*******************************************************/
+static void free_dir(struct smbw_dir *dir)
+{
+ if(!dir) return;
+
+ SAFE_FREE(dir->list);
+ SAFE_FREE(dir->path);
+ ZERO_STRUCTP(dir);
+ SAFE_FREE(dir);
+}
+
+static struct smbw_dir *cur_dir;
+
+/*****************************************************
+add a entry to a directory listing
+*******************************************************/
+static void smbw_dir_add(struct file_info *finfo, const char *mask,
+ void *state)
+{
+ struct file_info *cdl;
+
+ DEBUG(5,("%s\n", finfo->name));
+
+ if (cur_dir->malloced == cur_dir->count) {
+ cdl = (struct file_info *)Realloc(cur_dir->list,
+ sizeof(cur_dir->list[0])*
+ (cur_dir->count+100));
+ if (!cdl) {
+ /* oops */
+ return;
+ }
+ cur_dir->list = cdl;
+ cur_dir->malloced += 100;
+ }
+
+ cur_dir->list[cur_dir->count] = *finfo;
+ cur_dir->count++;
+}
+
+/*****************************************************
+add a entry to a directory listing
+*******************************************************/
+static void smbw_share_add(const char *share, uint32 type,
+ const char *comment, void *state)
+{
+ struct file_info finfo;
+
+ if (strcmp(share,"IPC$") == 0) return;
+
+ ZERO_STRUCT(finfo);
+
+ pstrcpy(finfo.name, share);
+ finfo.mode = aRONLY | aDIR;
+
+ smbw_dir_add(&finfo, NULL, NULL);
+}
+
+
+/*****************************************************
+add a server to a directory listing
+*******************************************************/
+static void smbw_server_add(const char *name, uint32 type,
+ const char *comment, void *state)
+{
+ struct file_info finfo;
+
+ ZERO_STRUCT(finfo);
+
+ pstrcpy(finfo.name, name);
+ finfo.mode = aRONLY | aDIR;
+
+ smbw_dir_add(&finfo, NULL, NULL);
+}
+
+
+/*****************************************************
+add a entry to a directory listing
+*******************************************************/
+static void smbw_printjob_add(struct print_job_info *job)
+{
+ struct file_info finfo;
+
+ ZERO_STRUCT(finfo);
+
+ pstrcpy(finfo.name, job->name);
+ finfo.mode = aRONLY | aDIR;
+ finfo.mtime = job->t;
+ finfo.atime = job->t;
+ finfo.ctime = job->t;
+ finfo.uid = nametouid(job->user);
+ finfo.mode = aRONLY;
+ finfo.size = job->size;
+
+ smbw_dir_add(&finfo, NULL, NULL);
+}
+
+
+/*****************************************************
+open a directory on the server
+*******************************************************/
+int smbw_dir_open(const char *fname)
+{
+ fstring server, share;
+ pstring path;
+ struct smbw_server *srv=NULL;
+ struct smbw_dir *dir=NULL;
+ pstring mask;
+ int fd;
+ char *s, *p;
+
+ if (!fname) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ smbw_init();
+
+ /* work out what server they are after */
+ s = smbw_parse_path(fname, server, share, path);
+
+ DEBUG(4,("dir_open share=%s\n", share));
+
+ /* get a connection to the server */
+ srv = smbw_server(server, share);
+ if (!srv) {
+ /* smbw_server sets errno */
+ goto failed;
+ }
+
+ dir = (struct smbw_dir *)malloc(sizeof(*dir));
+ if (!dir) {
+ errno = ENOMEM;
+ goto failed;
+ }
+
+ ZERO_STRUCTP(dir);
+
+ cur_dir = dir;
+
+ slprintf(mask, sizeof(mask)-1, "%s\\*", path);
+ all_string_sub(mask,"\\\\","\\",0);
+
+ if ((p=strstr(srv->server_name,"#01"))) {
+ *p = 0;
+ smbw_server_add(".",0,"", NULL);
+ smbw_server_add("..",0,"", NULL);
+ smbw_NetServerEnum(&srv->cli, srv->server_name,
+ SV_TYPE_DOMAIN_ENUM, smbw_server_add, NULL);
+ *p = '#';
+ } else if ((p=strstr(srv->server_name,"#1D"))) {
+ DEBUG(4,("doing NetServerEnum\n"));
+ *p = 0;
+ smbw_server_add(".",0,"", NULL);
+ smbw_server_add("..",0,"", NULL);
+ smbw_NetServerEnum(&srv->cli, srv->server_name, SV_TYPE_ALL,
+ smbw_server_add, NULL);
+ *p = '#';
+ } else if ((strcmp(srv->cli.dev,"IPC") == 0) || (strequal(share,"IPC$"))) {
+ DEBUG(4,("doing NetShareEnum\n"));
+ smbw_share_add(".",0,"", NULL);
+ smbw_share_add("..",0,"", NULL);
+ if (smbw_RNetShareEnum(&srv->cli, smbw_share_add, NULL) < 0) {
+ errno = smbw_errno(&srv->cli);
+ goto failed;
+ }
+ } else if (strncmp(srv->cli.dev,"LPT",3) == 0) {
+ smbw_share_add(".",0,"", NULL);
+ smbw_share_add("..",0,"", NULL);
+ if (cli_print_queue(&srv->cli, smbw_printjob_add) < 0) {
+ errno = smbw_errno(&srv->cli);
+ goto failed;
+ }
+ } else {
+#if 0
+ if (strcmp(path,"\\") == 0) {
+ smbw_share_add(".",0,"");
+ smbw_share_add("..",0,"");
+ }
+#endif
+ if (cli_list(&srv->cli, mask, aHIDDEN|aSYSTEM|aDIR,
+ smbw_dir_add, NULL) < 0) {
+ errno = smbw_errno(&srv->cli);
+ goto failed;
+ }
+ }
+
+ cur_dir = NULL;
+
+ fd = open(SMBW_DUMMY, O_WRONLY);
+ if (fd == -1) {
+ errno = EMFILE;
+ goto failed;
+ }
+
+ if (bitmap_query(smbw_file_bmap, fd)) {
+ DEBUG(0,("ERROR: fd used in smbw_dir_open\n"));
+ errno = EIO;
+ goto failed;
+ }
+
+ DLIST_ADD(smbw_dirs, dir);
+
+ bitmap_set(smbw_file_bmap, fd);
+
+ dir->fd = fd;
+ dir->srv = srv;
+ dir->path = strdup(s);
+
+ DEBUG(4,(" -> %d\n", dir->count));
+
+ return dir->fd;
+
+ failed:
+ free_dir(dir);
+
+ return -1;
+}
+
+/*****************************************************
+a wrapper for fstat() on a directory
+*******************************************************/
+int smbw_dir_fstat(int fd, struct stat *st)
+{
+ struct smbw_dir *dir;
+
+ dir = smbw_dir(fd);
+ if (!dir) {
+ errno = EBADF;
+ return -1;
+ }
+
+ ZERO_STRUCTP(st);
+
+ smbw_setup_stat(st, "", dir->count*DIRP_SIZE, aDIR);
+
+ st->st_dev = dir->srv->dev;
+
+ return 0;
+}
+
+/*****************************************************
+close a directory handle
+*******************************************************/
+int smbw_dir_close(int fd)
+{
+ struct smbw_dir *dir;
+
+ dir = smbw_dir(fd);
+ if (!dir) {
+ errno = EBADF;
+ return -1;
+ }
+
+ bitmap_clear(smbw_file_bmap, dir->fd);
+ close(dir->fd);
+
+ DLIST_REMOVE(smbw_dirs, dir);
+
+ free_dir(dir);
+
+ return 0;
+}
+
+/*****************************************************
+a wrapper for getdents()
+*******************************************************/
+int smbw_getdents(unsigned int fd, struct dirent *dirp, int count)
+{
+ struct smbw_dir *dir;
+ int n=0;
+
+ smbw_busy++;
+
+ dir = smbw_dir(fd);
+ if (!dir) {
+ errno = EBADF;
+ smbw_busy--;
+ return -1;
+ }
+
+ while (count>=DIRP_SIZE && (dir->offset < dir->count)) {
+#if HAVE_DIRENT_D_OFF
+ dirp->d_off = (dir->offset+1)*DIRP_SIZE;
+#endif
+ dirp->d_reclen = DIRP_SIZE;
+ fstrcpy(&dirp->d_name[0], dir->list[dir->offset].name);
+ dirp->d_ino = smbw_inode(dir->list[dir->offset].name);
+ dir->offset++;
+ count -= dirp->d_reclen;
+#if HAVE_DIRENT_D_OFF
+ if (dir->offset == dir->count) {
+ dirp->d_off = -1;
+ }
+#endif
+ dirp = (struct dirent *)(((char *)dirp) + DIRP_SIZE);
+ n++;
+ }
+
+ smbw_busy--;
+ return n*DIRP_SIZE;
+}
+
+
+/*****************************************************
+a wrapper for chdir()
+*******************************************************/
+int smbw_chdir(const char *name)
+{
+ struct smbw_server *srv;
+ fstring server, share;
+ pstring path;
+ uint16 mode = aDIR;
+ char *cwd;
+ int len;
+
+ smbw_init();
+
+ len = strlen(smbw_prefix);
+
+ if (smbw_busy) return real_chdir(name);
+
+ smbw_busy++;
+
+ if (!name) {
+ errno = EINVAL;
+ goto failed;
+ }
+
+ DEBUG(4,("smbw_chdir(%s)\n", name));
+
+ /* work out what server they are after */
+ cwd = smbw_parse_path(name, server, share, path);
+
+ /* a special case - accept cd to /smb */
+ if (strncmp(cwd, smbw_prefix, len-1) == 0 &&
+ cwd[len-1] == 0) {
+ goto success1;
+ }
+
+ if (strncmp(cwd,smbw_prefix,strlen(smbw_prefix))) {
+ if (real_chdir(cwd) == 0) {
+ goto success2;
+ }
+ goto failed;
+ }
+
+ /* get a connection to the server */
+ srv = smbw_server(server, share);
+ if (!srv) {
+ /* smbw_server sets errno */
+ goto failed;
+ }
+
+ if (strncmp(srv->cli.dev,"IPC",3) &&
+ !strequal(share, "IPC$") &&
+ strncmp(srv->cli.dev,"LPT",3) &&
+ !smbw_getatr(srv, path,
+ &mode, NULL, NULL, NULL, NULL, NULL)) {
+ errno = smbw_errno(&srv->cli);
+ goto failed;
+ }
+
+ if (!(mode & aDIR)) {
+ errno = ENOTDIR;
+ goto failed;
+ }
+
+ success1:
+ /* we don't want the old directory to be busy */
+ real_chdir("/");
+
+ success2:
+
+ DEBUG(4,("set SMBW_CWD to %s\n", cwd));
+
+ pstrcpy(smbw_cwd, cwd);
+
+ smbw_busy--;
+ return 0;
+
+ failed:
+ smbw_busy--;
+ return -1;
+}
+
+
+/*****************************************************
+a wrapper for lseek() on directories
+*******************************************************/
+off_t smbw_dir_lseek(int fd, off_t offset, int whence)
+{
+ struct smbw_dir *dir;
+ off_t ret;
+
+ dir = smbw_dir(fd);
+ if (!dir) {
+ errno = EBADF;
+ return -1;
+ }
+
+ switch (whence) {
+ case SEEK_SET:
+ dir->offset = offset/DIRP_SIZE;
+ break;
+ case SEEK_CUR:
+ dir->offset += offset/DIRP_SIZE;
+ break;
+ case SEEK_END:
+ dir->offset = (dir->count * DIRP_SIZE) + offset;
+ dir->offset /= DIRP_SIZE;
+ break;
+ }
+
+ ret = dir->offset * DIRP_SIZE;
+
+ DEBUG(4,(" -> %d\n", (int)ret));
+
+ return ret;
+}
+
+
+/*****************************************************
+a wrapper for mkdir()
+*******************************************************/
+int smbw_mkdir(const char *fname, mode_t mode)
+{
+ struct smbw_server *srv;
+ fstring server, share;
+ pstring path;
+
+ if (!fname) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ smbw_init();
+
+ smbw_busy++;
+
+ /* work out what server they are after */
+ smbw_parse_path(fname, server, share, path);
+
+ /* get a connection to the server */
+ srv = smbw_server(server, share);
+ if (!srv) {
+ /* smbw_server sets errno */
+ goto failed;
+ }
+
+ if (!cli_mkdir(&srv->cli, path)) {
+ errno = smbw_errno(&srv->cli);
+ goto failed;
+ }
+
+ smbw_busy--;
+ return 0;
+
+ failed:
+ smbw_busy--;
+ return -1;
+}
+
+/*****************************************************
+a wrapper for rmdir()
+*******************************************************/
+int smbw_rmdir(const char *fname)
+{
+ struct smbw_server *srv;
+ fstring server, share;
+ pstring path;
+
+ if (!fname) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ smbw_init();
+
+ smbw_busy++;
+
+ /* work out what server they are after */
+ smbw_parse_path(fname, server, share, path);
+
+ /* get a connection to the server */
+ srv = smbw_server(server, share);
+ if (!srv) {
+ /* smbw_server sets errno */
+ goto failed;
+ }
+
+ if (!cli_rmdir(&srv->cli, path)) {
+ errno = smbw_errno(&srv->cli);
+ goto failed;
+ }
+
+ smbw_busy--;
+ return 0;
+
+ failed:
+ smbw_busy--;
+ return -1;
+}
+
+
+/*****************************************************
+a wrapper for getcwd()
+*******************************************************/
+char *smbw_getcwd(char *buf, size_t size)
+{
+ smbw_init();
+
+ if (smbw_busy) {
+ return (char *)real_getcwd(buf, size);
+ }
+
+ smbw_busy++;
+
+ if (!buf) {
+ if (size <= 0) size = strlen(smbw_cwd)+1;
+ buf = (char *)malloc(size);
+ if (!buf) {
+ errno = ENOMEM;
+ smbw_busy--;
+ return NULL;
+ }
+ }
+
+ if (strlen(smbw_cwd) > size-1) {
+ errno = ERANGE;
+ smbw_busy--;
+ return NULL;
+ }
+
+ safe_strcpy(buf, smbw_cwd, size);
+
+ smbw_busy--;
+ return buf;
+}
+
+/*****************************************************
+a wrapper for fchdir()
+*******************************************************/
+int smbw_fchdir(unsigned int fd)
+{
+ struct smbw_dir *dir;
+ int ret;
+
+ smbw_busy++;
+
+ dir = smbw_dir(fd);
+ if (dir) {
+ smbw_busy--;
+ return chdir(dir->path);
+ }
+
+ ret = real_fchdir(fd);
+ if (ret == 0) {
+ sys_getwd(smbw_cwd);
+ }
+
+ smbw_busy--;
+ return ret;
+}
+
+/*****************************************************
+open a directory on the server
+*******************************************************/
+DIR *smbw_opendir(const char *fname)
+{
+ int fd;
+
+ smbw_busy++;
+
+ fd = smbw_dir_open(fname);
+
+ if (fd == -1) {
+ smbw_busy--;
+ return NULL;
+ }
+
+ smbw_busy--;
+
+ return (DIR *)smbw_dir(fd);
+}
+
+/*****************************************************
+read one entry from a directory
+*******************************************************/
+struct dirent *smbw_readdir(DIR *dirp)
+{
+ struct smbw_dir *d = (struct smbw_dir *)dirp;
+ static union {
+ char buf[DIRP_SIZE];
+ struct dirent de;
+ } dbuf;
+
+ if (smbw_getdents(d->fd, &dbuf.de, DIRP_SIZE) > 0)
+ return &dbuf.de;
+
+ return NULL;
+}
+
+/*****************************************************
+close a DIR*
+*******************************************************/
+int smbw_closedir(DIR *dirp)
+{
+ struct smbw_dir *d = (struct smbw_dir *)dirp;
+ return smbw_close(d->fd);
+}
+
+/*****************************************************
+seek in a directory
+*******************************************************/
+void smbw_seekdir(DIR *dirp, off_t offset)
+{
+ struct smbw_dir *d = (struct smbw_dir *)dirp;
+ smbw_dir_lseek(d->fd,offset, SEEK_SET);
+}
+
+/*****************************************************
+current loc in a directory
+*******************************************************/
+off_t smbw_telldir(DIR *dirp)
+{
+ struct smbw_dir *d = (struct smbw_dir *)dirp;
+ return smbw_dir_lseek(d->fd,0,SEEK_CUR);
+}
diff --git a/source/smbwrapper/smbw_stat.c b/source/smbwrapper/smbw_stat.c
new file mode 100644
index 00000000000..bb76ef006a4
--- /dev/null
+++ b/source/smbwrapper/smbw_stat.c
@@ -0,0 +1,254 @@
+/*
+ Unix SMB/CIFS implementation.
+ SMB wrapper stat functions
+ 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 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"
+
+extern int smbw_busy;
+
+/*****************************************************
+setup basic info in a stat structure
+*******************************************************/
+void smbw_setup_stat(struct stat *st, char *fname, size_t size, int mode)
+{
+ st->st_mode = 0;
+
+ if (IS_DOS_DIR(mode)) {
+ st->st_mode = SMBW_DIR_MODE;
+ } else {
+ st->st_mode = SMBW_FILE_MODE;
+ }
+
+ if (IS_DOS_ARCHIVE(mode)) st->st_mode |= S_IXUSR;
+ if (IS_DOS_SYSTEM(mode)) st->st_mode |= S_IXGRP;
+ if (IS_DOS_HIDDEN(mode)) st->st_mode |= S_IXOTH;
+ if (!IS_DOS_READONLY(mode)) st->st_mode |= S_IWUSR;
+
+ st->st_size = size;
+#ifdef HAVE_STAT_ST_BLKSIZE
+ st->st_blksize = 512;
+#endif
+#ifdef HAVE_STAT_ST_BLOCKS
+ st->st_blocks = (size+511)/512;
+#endif
+ st->st_uid = getuid();
+ st->st_gid = getgid();
+ if (IS_DOS_DIR(mode)) {
+ st->st_nlink = 2;
+ } else {
+ st->st_nlink = 1;
+ }
+ if (st->st_ino == 0) {
+ st->st_ino = smbw_inode(fname);
+ }
+}
+
+
+/*****************************************************
+try to do a QPATHINFO and if that fails then do a getatr
+this is needed because win95 sometimes refuses the qpathinfo
+*******************************************************/
+BOOL smbw_getatr(struct smbw_server *srv, char *path,
+ uint16 *mode, size_t *size,
+ time_t *c_time, time_t *a_time, time_t *m_time,
+ SMB_INO_T *ino)
+{
+ DEBUG(4,("sending qpathinfo\n"));
+
+ if (!srv->no_pathinfo2 &&
+ cli_qpathinfo2(&srv->cli, path, c_time, a_time, m_time, NULL,
+ size, mode, ino)) return True;
+
+ /* if this is NT then don't bother with the getatr */
+ if (srv->cli.capabilities & CAP_NT_SMBS) return False;
+
+ if (cli_getatr(&srv->cli, path, mode, size, m_time)) {
+ a_time = c_time = m_time;
+ srv->no_pathinfo2 = True;
+ return True;
+ }
+ return False;
+}
+
+
+static struct print_job_info printjob;
+
+/*****************************************************
+gather info from a printjob listing
+*******************************************************/
+static void smbw_printjob_stat(struct print_job_info *job)
+{
+ if (strcmp(job->name, printjob.name) == 0) {
+ printjob = *job;
+ }
+}
+
+/*****************************************************
+stat a printjob
+*******************************************************/
+int smbw_stat_printjob(struct smbw_server *srv,char *path,
+ size_t *size, time_t *m_time)
+{
+ if (path[0] == '\\') path++;
+
+ ZERO_STRUCT(printjob);
+
+ fstrcpy(printjob.name, path);
+ cli_print_queue(&srv->cli, smbw_printjob_stat);
+
+ if (size) {
+ *size = printjob.size;
+ }
+ if (m_time) {
+ *m_time = printjob.t;
+ }
+ return printjob.id;
+}
+
+
+/*****************************************************
+a wrapper for fstat()
+*******************************************************/
+int smbw_fstat(int fd, struct stat *st)
+{
+ struct smbw_file *file;
+ time_t c_time, a_time, m_time;
+ size_t size;
+ uint16 mode;
+ SMB_INO_T ino = 0;
+
+ smbw_busy++;
+
+ ZERO_STRUCTP(st);
+
+ file = smbw_file(fd);
+ if (!file) {
+ int ret = smbw_dir_fstat(fd, st);
+ smbw_busy--;
+ return ret;
+ }
+
+ if (!cli_qfileinfo(&file->srv->cli, file->f->cli_fd,
+ &mode, &size, &c_time, &a_time, &m_time, NULL,
+ &ino) &&
+ !cli_getattrE(&file->srv->cli, file->f->cli_fd,
+ &mode, &size, &c_time, &a_time, &m_time)) {
+ errno = EINVAL;
+ smbw_busy--;
+ return -1;
+ }
+
+ st->st_ino = ino;
+
+ smbw_setup_stat(st, file->f->fname, size, mode);
+
+ st->st_atime = a_time;
+ st->st_ctime = c_time;
+ st->st_mtime = m_time;
+ st->st_dev = file->srv->dev;
+
+ smbw_busy--;
+ return 0;
+}
+
+
+/*****************************************************
+a wrapper for stat()
+*******************************************************/
+int smbw_stat(const char *fname, struct stat *st)
+{
+ struct smbw_server *srv;
+ fstring server, share;
+ pstring path;
+ time_t m_time=0, a_time=0, c_time=0;
+ size_t size=0;
+ uint16 mode=0;
+ SMB_INO_T ino = 0;
+ int result = 0;
+
+ ZERO_STRUCTP(st);
+
+ if (!fname) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ DEBUG(4,("stat(%s)\n", fname));
+
+ smbw_init();
+
+ smbw_busy++;
+
+ /* work out what server they are after */
+ smbw_parse_path(fname, server, share, path);
+
+ /* get a connection to the server */
+ srv = smbw_server(server, share);
+ if (!srv) {
+
+ /* For shares we aren't allowed to connect to, or no master
+ browser found, return an empty directory */
+
+ if ((server[0] && share[0] && !path[0] && errno == EACCES) ||
+ (!path[0] && errno == ENOENT)) {
+ mode = aDIR | aRONLY;
+ smbw_setup_stat(st, path, size, mode);
+ goto done;
+ }
+
+ /* smbw_server sets errno */
+ result = -1;
+ goto done;
+ }
+
+ DEBUG(4,("smbw_stat\n"));
+
+ if (strncmp(srv->cli.dev,"IPC",3) == 0) {
+ mode = aDIR | aRONLY;
+ } else if (strncmp(srv->cli.dev,"LPT",3) == 0) {
+ if (strcmp(path,"\\") == 0) {
+ mode = aDIR | aRONLY;
+ } else {
+ mode = aRONLY;
+ smbw_stat_printjob(srv, path, &size, &m_time);
+ c_time = a_time = m_time;
+ }
+ } else {
+ if (!smbw_getatr(srv, path,
+ &mode, &size, &c_time, &a_time, &m_time,
+ &ino)) {
+ errno = smbw_errno(&srv->cli);
+ result = -1;
+ goto done;
+ }
+ }
+
+ st->st_ino = ino;
+
+ smbw_setup_stat(st, path, size, mode);
+
+ st->st_atime = a_time;
+ st->st_ctime = c_time;
+ st->st_mtime = m_time;
+ st->st_dev = srv->dev;
+
+ done:
+ smbw_busy--;
+ return result;
+}
diff --git a/source/smbwrapper/wrapped.c b/source/smbwrapper/wrapped.c
new file mode 100644
index 00000000000..338ee0d5b1b
--- /dev/null
+++ b/source/smbwrapper/wrapped.c
@@ -0,0 +1,705 @@
+/*
+ Unix SMB/CIFS implementation.
+ SMB wrapper functions
+ 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 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.
+*/
+
+/* NOTE: This file WILL produce compiler warnings. They are unavoidable
+
+ Do not try and get rid of them by including other include files or
+ by including includes.h or proto.h or you will break portability.
+ */
+
+#include "config.h"
+#include <sys/types.h>
+#include <errno.h>
+#include "realcalls.h"
+
+#ifndef NULL
+# define NULL ((void *)0)
+#endif
+
+ int open(char *name, int flags, mode_t mode)
+{
+ if (smbw_path(name)) {
+ return smbw_open(name, flags, mode);
+ }
+
+ return real_open(name, flags, mode);
+}
+
+#ifdef HAVE__OPEN
+ int _open(char *name, int flags, mode_t mode)
+{
+ return open(name, flags, mode);
+}
+#elif HAVE___OPEN
+ int __open(char *name, int flags, mode_t mode)
+{
+ return open(name, flags, mode);
+}
+#endif
+
+
+#ifdef HAVE_OPEN64
+ int open64(char *name, int flags, mode_t mode)
+{
+ if (smbw_path(name)) {
+ return smbw_open(name, flags, mode);
+ }
+
+ return real_open64(name, flags, mode);
+}
+#endif
+
+#ifndef NO_OPEN64_ALIAS
+#ifdef HAVE__OPEN64
+ int _open64(char *name, int flags, mode_t mode)
+{
+ return open64(name, flags, mode);
+}
+#elif HAVE___OPEN64
+ int __open64(char *name, int flags, mode_t mode)
+{
+ return open64(name, flags, mode);
+}
+#endif
+#endif
+
+#ifdef HAVE_PREAD
+ ssize_t pread(int fd, void *buf, size_t size, off_t ofs)
+{
+ if (smbw_fd(fd)) {
+ return smbw_pread(fd, buf, size, ofs);
+ }
+
+ return real_pread(fd, buf, size, ofs);
+}
+#endif
+
+#if defined(HAVE_PREAD64) && defined(HAVE_EXPLICIT_LARGEFILE_SUPPORT)
+ ssize_t pread64(int fd, void *buf, size_t size, off64_t ofs)
+{
+ if (smbw_fd(fd)) {
+ return smbw_pread(fd, buf, size, ofs);
+ }
+
+ return real_pread64(fd, buf, size, ofs);
+}
+#endif
+
+#ifdef HAVE_PWRITE
+ ssize_t pwrite(int fd, void *buf, size_t size, off_t ofs)
+{
+ if (smbw_fd(fd)) {
+ return smbw_pwrite(fd, buf, size, ofs);
+ }
+
+ return real_pwrite(fd, buf, size, ofs);
+}
+#endif
+
+#if defined(HAVE_PWRITE64) && defined(HAVE_EXPLICIT_LARGEFILE_SUPPORT)
+ ssize_t pwrite64(int fd, void *buf, size_t size, off64_t ofs)
+{
+ if (smbw_fd(fd)) {
+ return smbw_pwrite(fd, buf, size, ofs);
+ }
+
+ return real_pwrite64(fd, buf, size, ofs);
+}
+#endif
+
+
+ int chdir(char *name)
+{
+ return smbw_chdir(name);
+}
+
+#ifdef HAVE___CHDIR
+ int __chdir(char *name)
+{
+ return chdir(name);
+}
+#elif HAVE__CHDIR
+ int _chdir(char *name)
+{
+ return chdir(name);
+}
+#endif
+
+
+ int close(int fd)
+{
+ if (smbw_fd(fd)) {
+ return smbw_close(fd);
+ }
+ if (smbw_local_fd(fd)) {
+ errno = EBADF;
+ return -1;
+ }
+
+ return real_close(fd);
+}
+
+#ifdef HAVE___CLOSE
+ int __close(int fd)
+{
+ return close(fd);
+}
+#elif HAVE__CLOSE
+ int _close(int fd)
+{
+ return close(fd);
+}
+#endif
+
+
+ int fchdir(int fd)
+{
+ return smbw_fchdir(fd);
+}
+
+#ifdef HAVE___FCHDIR
+ int __fchdir(int fd)
+{
+ return fchdir(fd);
+}
+#elif HAVE__FCHDIR
+ int _fchdir(int fd)
+{
+ return fchdir(fd);
+}
+#endif
+
+
+ int fcntl(int fd, int cmd, long arg)
+{
+ if (smbw_fd(fd)) {
+ return smbw_fcntl(fd, cmd, arg);
+ }
+
+ return real_fcntl(fd, cmd, arg);
+}
+
+
+#ifdef HAVE___FCNTL
+ int __fcntl(int fd, int cmd, long arg)
+{
+ return fcntl(fd, cmd, arg);
+}
+#elif HAVE__FCNTL
+ int _fcntl(int fd, int cmd, long arg)
+{
+ return fcntl(fd, cmd, arg);
+}
+#endif
+
+
+
+#ifdef real_getdents
+ int getdents(int fd, void *dirp, unsigned int count)
+{
+ if (smbw_fd(fd)) {
+ return smbw_getdents(fd, dirp, count);
+ }
+
+ return real_getdents(fd, dirp, count);
+}
+#endif
+
+#ifdef HAVE___GETDENTS
+ int __getdents(int fd, void *dirp, unsigned int count)
+{
+ return getdents(fd, dirp, count);
+}
+#elif HAVE__GETDENTS
+ int _getdents(int fd, void *dirp, unsigned int count)
+{
+ return getdents(fd, dirp, count);
+}
+#endif
+
+
+ off_t lseek(int fd, off_t offset, int whence)
+{
+ if (smbw_fd(fd)) {
+ return smbw_lseek(fd, offset, whence);
+ }
+
+ return real_lseek(fd, offset, whence);
+}
+
+#ifdef HAVE___LSEEK
+ off_t __lseek(int fd, off_t offset, int whence)
+{
+ return lseek(fd, offset, whence);
+}
+#elif HAVE__LSEEK
+ off_t _lseek(int fd, off_t offset, int whence)
+{
+ return lseek(fd, offset, whence);
+}
+#endif
+
+
+ ssize_t read(int fd, void *buf, size_t count)
+{
+ if (smbw_fd(fd)) {
+ return smbw_read(fd, buf, count);
+ }
+
+ return real_read(fd, buf, count);
+}
+
+#ifdef HAVE___READ
+ ssize_t __read(int fd, void *buf, size_t count)
+{
+ return read(fd, buf, count);
+}
+#elif HAVE__READ
+ ssize_t _read(int fd, void *buf, size_t count)
+{
+ return read(fd, buf, count);
+}
+#endif
+
+
+ ssize_t write(int fd, void *buf, size_t count)
+{
+ if (smbw_fd(fd)) {
+ return smbw_write(fd, buf, count);
+ }
+
+ return real_write(fd, buf, count);
+}
+
+#ifdef HAVE___WRITE
+ ssize_t __write(int fd, void *buf, size_t count)
+{
+ return write(fd, buf, count);
+}
+#elif HAVE__WRITE
+ ssize_t _write(int fd, void *buf, size_t count)
+{
+ return write(fd, buf, count);
+}
+#endif
+
+
+
+ int access(char *name, int mode)
+{
+ if (smbw_path(name)) {
+ return smbw_access(name, mode);
+ }
+
+ return real_access(name, mode);
+}
+
+
+
+ int chmod(char *name,mode_t mode)
+{
+ if (smbw_path(name)) {
+ return smbw_chmod(name, mode);
+ }
+
+ return real_chmod(name, mode);
+}
+
+
+
+ int chown(char *name,uid_t owner, gid_t group)
+{
+ if (smbw_path(name)) {
+ return smbw_chown(name, owner, group);
+ }
+
+ return real_chown(name, owner, group);
+}
+
+
+ char *getcwd(char *buf, size_t size)
+{
+ return (char *)smbw_getcwd(buf, size);
+}
+
+
+
+
+ int mkdir(char *name, mode_t mode)
+{
+ if (smbw_path(name)) {
+ return smbw_mkdir(name, mode);
+ }
+
+ return real_mkdir(name, mode);
+}
+
+
+#if HAVE___FXSTAT
+ int __fxstat(int vers, int fd, void *st)
+{
+ double xx[32];
+ int ret;
+
+ if (smbw_fd(fd)) {
+ return smbw_fstat(fd, st);
+ }
+
+ ret = real_fstat(fd, xx);
+ xstat_convert(vers, st, xx);
+ return ret;
+}
+#endif
+
+#if HAVE___XSTAT
+ int __xstat(int vers, char *name, void *st)
+{
+ double xx[32];
+ int ret;
+
+ if (smbw_path(name)) {
+ return smbw_stat(name, st);
+ }
+
+ ret = real_stat(name, xx);
+ xstat_convert(vers, st, xx);
+ return ret;
+}
+#endif
+
+
+#if HAVE___LXSTAT
+ int __lxstat(int vers, char *name, void *st)
+{
+ double xx[32];
+ int ret;
+
+ if (smbw_path(name)) {
+ return smbw_stat(name, st);
+ }
+
+ ret = real_lstat(name, xx);
+ xstat_convert(vers, st, xx);
+ return ret;
+}
+#endif
+
+
+ int stat(char *name, void *st)
+{
+#if HAVE___XSTAT
+ return __xstat(0, name, st);
+#else
+ if (smbw_path(name)) {
+ return smbw_stat(name, st);
+ }
+ return real_stat(name, st);
+#endif
+}
+
+ int lstat(char *name, void *st)
+{
+#if HAVE___LXSTAT
+ return __lxstat(0, name, st);
+#else
+ if (smbw_path(name)) {
+ return smbw_stat(name, st);
+ }
+ return real_lstat(name, st);
+#endif
+}
+
+ int fstat(int fd, void *st)
+{
+#if HAVE___LXSTAT
+ return __fxstat(0, fd, st);
+#else
+ if (smbw_fd(fd)) {
+ return smbw_fstat(fd, st);
+ }
+ return real_fstat(fd, st);
+#endif
+}
+
+
+ int unlink(char *name)
+{
+ if (smbw_path(name)) {
+ return smbw_unlink(name);
+ }
+
+ return real_unlink(name);
+}
+
+
+#ifdef HAVE_UTIME
+ int utime(char *name,void *tvp)
+{
+ if (smbw_path(name)) {
+ return smbw_utime(name, tvp);
+ }
+
+ return real_utime(name, tvp);
+}
+#endif
+
+#ifdef HAVE_UTIMES
+ int utimes(const char *name, const struct timeval *tvp)
+{
+ if (smbw_path(name)) {
+ return smbw_utimes(name, tvp);
+ }
+
+ return real_utimes(name, tvp);
+}
+#endif
+
+ int readlink(char *path, char *buf, size_t bufsize)
+{
+ if (smbw_path(path)) {
+ return smbw_readlink(path, buf, bufsize);
+ }
+
+ return real_readlink(path, buf, bufsize);
+}
+
+
+ int rename(char *oldname,char *newname)
+{
+ int p1, p2;
+ p1 = smbw_path(oldname);
+ p2 = smbw_path(newname);
+ if (p1 ^ p2) {
+ /* can't cross filesystem boundaries */
+ errno = EXDEV;
+ return -1;
+ }
+ if (p1 && p2) {
+ return smbw_rename(oldname, newname);
+ }
+
+ return real_rename(oldname, newname);
+}
+
+ int rmdir(char *name)
+{
+ if (smbw_path(name)) {
+ return smbw_rmdir(name);
+ }
+
+ return real_rmdir(name);
+}
+
+
+ int symlink(char *topath,char *frompath)
+{
+ int p1, p2;
+ p1 = smbw_path(topath);
+ p2 = smbw_path(frompath);
+ if (p1 || p2) {
+ /* can't handle symlinks */
+ errno = EPERM;
+ return -1;
+ }
+
+ return real_symlink(topath, frompath);
+}
+
+ int dup(int fd)
+{
+ if (smbw_fd(fd)) {
+ return smbw_dup(fd);
+ }
+
+ return real_dup(fd);
+}
+
+ int dup2(int oldfd, int newfd)
+{
+ if (smbw_fd(newfd)) {
+ close(newfd);
+ }
+
+ if (smbw_fd(oldfd)) {
+ return smbw_dup2(oldfd, newfd);
+ }
+
+ return real_dup2(oldfd, newfd);
+}
+
+#ifdef real_opendir
+ void *opendir(char *name)
+{
+ if (smbw_path(name)) {
+ return (void *)smbw_opendir(name);
+ }
+
+ return (void *)real_opendir(name);
+}
+#endif
+
+#ifdef real_readdir
+ void *readdir(void *dir)
+{
+ if (smbw_dirp(dir)) {
+ return (void *)smbw_readdir(dir);
+ }
+
+ return (void *)real_readdir(dir);
+}
+#endif
+
+#ifdef real_closedir
+ int closedir(void *dir)
+{
+ if (smbw_dirp(dir)) {
+ return smbw_closedir(dir);
+ }
+
+ return real_closedir(dir);
+}
+#endif
+
+#ifdef real_telldir
+ off_t telldir(void *dir)
+{
+ if (smbw_dirp(dir)) {
+ return smbw_telldir(dir);
+ }
+
+ return real_telldir(dir);
+}
+#endif
+
+#ifdef real_seekdir
+ int seekdir(void *dir, off_t offset)
+{
+ if (smbw_dirp(dir)) {
+ smbw_seekdir(dir, offset);
+ return 0;
+ }
+
+ real_seekdir(dir, offset);
+ return 0;
+}
+#endif
+
+
+#ifndef NO_ACL_WRAPPER
+ int acl(char *pathp, int cmd, int nentries, void *aclbufp)
+{
+ if (smbw_path(pathp)) {
+ return smbw_acl(pathp, cmd, nentries, aclbufp);
+ }
+
+ return real_acl(pathp, cmd, nentries, aclbufp);
+}
+#endif
+
+#ifndef NO_FACL_WRAPPER
+ int facl(int fd, int cmd, int nentries, void *aclbufp)
+{
+ if (smbw_fd(fd)) {
+ return smbw_facl(fd, cmd, nentries, aclbufp);
+ }
+
+ return real_facl(fd, cmd, nentries, aclbufp);
+}
+#endif
+
+ int creat(char *path, mode_t mode)
+{
+ extern int creat_bits;
+ return open(path, creat_bits, mode);
+}
+
+#ifdef HAVE_CREAT64
+ int creat64(char *path, mode_t mode)
+{
+ extern int creat_bits;
+ return open64(path, creat_bits, mode);
+}
+#endif
+
+#ifdef HAVE_STAT64
+ int stat64(char *name, void *st64)
+{
+ if (smbw_path(name)) {
+ double xx[32];
+ int ret = stat(name, xx);
+ stat64_convert(xx, st64);
+ return ret;
+ }
+ return real_stat64(name, st64);
+}
+
+ int fstat64(int fd, void *st64)
+{
+ if (smbw_fd(fd)) {
+ double xx[32];
+ int ret = fstat(fd, xx);
+ stat64_convert(xx, st64);
+ return ret;
+ }
+ return real_fstat64(fd, st64);
+}
+
+ int lstat64(char *name, void *st64)
+{
+ if (smbw_path(name)) {
+ double xx[32];
+ int ret = lstat(name, xx);
+ stat64_convert(xx, st64);
+ return ret;
+ }
+ return real_lstat64(name, st64);
+}
+#endif
+
+#ifdef HAVE_LLSEEK
+ offset_t llseek(int fd, offset_t ofs, int whence)
+{
+ if (smbw_fd(fd)) {
+ return lseek(fd, ofs, whence);
+ }
+ return real_llseek(fd, ofs, whence);
+}
+#endif
+
+#ifdef HAVE_READDIR64
+ void *readdir64(void *dir)
+{
+ if (smbw_dirp(dir)) {
+ static double xx[70];
+ void *d;
+ d = (void *)readdir(dir);
+ if (!d) return NULL;
+ dirent64_convert(d, xx);
+ return xx;
+ }
+ return (void *)real_readdir64(dir);
+}
+#endif
+
+ int fork(void)
+{
+ return smbw_fork();
+}
+
diff --git a/source/stf/.cvsignore b/source/stf/.cvsignore
new file mode 100644
index 00000000000..bcf41506063
--- /dev/null
+++ b/source/stf/.cvsignore
@@ -0,0 +1,2 @@
+*.pyc
+testtmp
diff --git a/source/stf/README.stf b/source/stf/README.stf
new file mode 100644
index 00000000000..3fbd33cb6cc
--- /dev/null
+++ b/source/stf/README.stf
@@ -0,0 +1,3 @@
+This directory contains the Samba Testing Framework, a Python-based
+system for exercising Samba in various ways. It is quite small at the
+moment.
diff --git a/source/stf/comfychair.py b/source/stf/comfychair.py
new file mode 100644
index 00000000000..522f9bedeba
--- /dev/null
+++ b/source/stf/comfychair.py
@@ -0,0 +1,445 @@
+#! /usr/bin/env python
+
+# Copyright (C) 2002, 2003 by Martin Pool <mbp@samba.org>
+# Copyright (C) 2003 by Tim Potter <tpot@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., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+# USA
+
+"""comfychair: a Python-based instrument of software torture.
+
+Copyright (C) 2002, 2003 by Martin Pool <mbp@samba.org>
+Copyright (C) 2003 by Tim Potter <tpot@samba.org>
+
+This is a test framework designed for testing programs written in
+Python, or (through a fork/exec interface) any other language.
+
+For more information, see the file README.comfychair.
+
+To run a test suite based on ComfyChair, just run it as a program.
+"""
+
+import sys, re
+
+
+class TestCase:
+ """A base class for tests. This class defines required functions which
+ can optionally be overridden by subclasses. It also provides some
+ utility functions for"""
+
+ def __init__(self):
+ self.test_log = ""
+ self.background_pids = []
+ self._cleanups = []
+ self._enter_rundir()
+ self._save_environment()
+ self.add_cleanup(self.teardown)
+
+
+ # --------------------------------------------------
+ # Save and restore directory
+ def _enter_rundir(self):
+ import os
+ self.basedir = os.getcwd()
+ self.add_cleanup(self._restore_directory)
+ self.rundir = os.path.join(self.basedir,
+ 'testtmp',
+ self.__class__.__name__)
+ self.tmpdir = os.path.join(self.rundir, 'tmp')
+ os.system("rm -fr %s" % self.rundir)
+ os.makedirs(self.tmpdir)
+ os.system("mkdir -p %s" % self.rundir)
+ os.chdir(self.rundir)
+
+ def _restore_directory(self):
+ import os
+ os.chdir(self.basedir)
+
+ # --------------------------------------------------
+ # Save and restore environment
+ def _save_environment(self):
+ import os
+ self._saved_environ = os.environ.copy()
+ self.add_cleanup(self._restore_environment)
+
+ def _restore_environment(self):
+ import os
+ os.environ.clear()
+ os.environ.update(self._saved_environ)
+
+
+ def setup(self):
+ """Set up test fixture."""
+ pass
+
+ def teardown(self):
+ """Tear down test fixture."""
+ pass
+
+ def runtest(self):
+ """Run the test."""
+ pass
+
+
+ def add_cleanup(self, c):
+ """Queue a cleanup to be run when the test is complete."""
+ self._cleanups.append(c)
+
+
+ def fail(self, reason = ""):
+ """Say the test failed."""
+ raise AssertionError(reason)
+
+
+ #############################################################
+ # Requisition methods
+
+ def require(self, predicate, message):
+ """Check a predicate for running this test.
+
+If the predicate value is not true, the test is skipped with a message explaining
+why."""
+ if not predicate:
+ raise NotRunError, message
+
+ def require_root(self):
+ """Skip this test unless run by root."""
+ import os
+ self.require(os.getuid() == 0,
+ "must be root to run this test")
+
+ #############################################################
+ # Assertion methods
+
+ def assert_(self, expr, reason = ""):
+ if not expr:
+ raise AssertionError(reason)
+
+ def assert_equal(self, a, b):
+ if not a == b:
+ raise AssertionError("assertEquals failed: %s" % `(a, b)`)
+
+ def assert_notequal(self, a, b):
+ if a == b:
+ raise AssertionError("assertNotEqual failed: %s" % `(a, b)`)
+
+ def assert_re_match(self, pattern, s):
+ """Assert that a string matches a particular pattern
+
+ Inputs:
+ pattern string: regular expression
+ s string: to be matched
+
+ Raises:
+ AssertionError if not matched
+ """
+ if not re.match(pattern, s):
+ raise AssertionError("string does not match regexp\n"
+ " string: %s\n"
+ " re: %s" % (`s`, `pattern`))
+
+ def assert_re_search(self, pattern, s):
+ """Assert that a string *contains* a particular pattern
+
+ Inputs:
+ pattern string: regular expression
+ s string: to be searched
+
+ Raises:
+ AssertionError if not matched
+ """
+ if not re.search(pattern, s):
+ raise AssertionError("string does not contain regexp\n"
+ " string: %s\n"
+ " re: %s" % (`s`, `pattern`))
+
+
+ def assert_no_file(self, filename):
+ import os.path
+ assert not os.path.exists(filename), ("file exists but should not: %s" % filename)
+
+
+ #############################################################
+ # Methods for running programs
+
+ def runcmd_background(self, cmd):
+ import os
+ self.test_log = self.test_log + "Run in background:\n" + `cmd` + "\n"
+ pid = os.fork()
+ if pid == 0:
+ # child
+ try:
+ os.execvp("/bin/sh", ["/bin/sh", "-c", cmd])
+ finally:
+ os._exit(127)
+ self.test_log = self.test_log + "pid: %d\n" % pid
+ return pid
+
+
+ def runcmd(self, cmd, expectedResult = 0):
+ """Run a command, fail if the command returns an unexpected exit
+ code. Return the output produced."""
+ rc, output, stderr = self.runcmd_unchecked(cmd)
+ if rc != expectedResult:
+ raise AssertionError("""command returned %d; expected %s: \"%s\"
+stdout:
+%s
+stderr:
+%s""" % (rc, expectedResult, cmd, output, stderr))
+
+ return output, stderr
+
+
+ def run_captured(self, cmd):
+ """Run a command, capturing stdout and stderr.
+
+ Based in part on popen2.py
+
+ Returns (waitstatus, stdout, stderr)."""
+ import os, types
+ pid = os.fork()
+ if pid == 0:
+ # child
+ try:
+ pid = os.getpid()
+ openmode = os.O_WRONLY|os.O_CREAT|os.O_TRUNC
+
+ outfd = os.open('%d.out' % pid, openmode, 0666)
+ os.dup2(outfd, 1)
+ os.close(outfd)
+
+ errfd = os.open('%d.err' % pid, openmode, 0666)
+ os.dup2(errfd, 2)
+ os.close(errfd)
+
+ if isinstance(cmd, types.StringType):
+ cmd = ['/bin/sh', '-c', cmd]
+
+ os.execvp(cmd[0], cmd)
+ finally:
+ os._exit(127)
+ else:
+ # parent
+ exited_pid, waitstatus = os.waitpid(pid, 0)
+ stdout = open('%d.out' % pid).read()
+ stderr = open('%d.err' % pid).read()
+ return waitstatus, stdout, stderr
+
+
+ def runcmd_unchecked(self, cmd, skip_on_noexec = 0):
+ """Invoke a command; return (exitcode, stdout, stderr)"""
+ import os
+ waitstatus, stdout, stderr = self.run_captured(cmd)
+ assert not os.WIFSIGNALED(waitstatus), \
+ ("%s terminated with signal %d" % (`cmd`, os.WTERMSIG(waitstatus)))
+ rc = os.WEXITSTATUS(waitstatus)
+ self.test_log = self.test_log + ("""Run command: %s
+Wait status: %#x (exit code %d, signal %d)
+stdout:
+%s
+stderr:
+%s""" % (cmd, waitstatus, os.WEXITSTATUS(waitstatus), os.WTERMSIG(waitstatus),
+ stdout, stderr))
+ if skip_on_noexec and rc == 127:
+ # Either we could not execute the command or the command
+ # returned exit code 127. According to system(3) we can't
+ # tell the difference.
+ raise NotRunError, "could not execute %s" % `cmd`
+ return rc, stdout, stderr
+
+
+ def explain_failure(self, exc_info = None):
+ print "test_log:"
+ print self.test_log
+
+
+ def log(self, msg):
+ """Log a message to the test log. This message is displayed if
+ the test fails, or when the runtests function is invoked with
+ the verbose option."""
+ self.test_log = self.test_log + msg + "\n"
+
+
+class NotRunError(Exception):
+ """Raised if a test must be skipped because of missing resources"""
+ def __init__(self, value = None):
+ self.value = value
+
+
+def _report_error(case, debugger):
+ """Ask the test case to explain failure, and optionally run a debugger
+
+ Input:
+ case TestCase instance
+ debugger if true, a debugger function to be applied to the traceback
+"""
+ import sys
+ ex = sys.exc_info()
+ print "-----------------------------------------------------------------"
+ if ex:
+ import traceback
+ traceback.print_exc(file=sys.stdout)
+ case.explain_failure()
+ print "-----------------------------------------------------------------"
+
+ if debugger:
+ tb = ex[2]
+ debugger(tb)
+
+
+def runtests(test_list, verbose = 0, debugger = None):
+ """Run a series of tests.
+
+ Inputs:
+ test_list sequence of TestCase classes
+ verbose print more information as testing proceeds
+ debugger debugger object to be applied to errors
+
+ Returns:
+ unix return code: 0 for success, 1 for failures, 2 for test failure
+ """
+ import traceback
+ ret = 0
+ for test_class in test_list:
+ print "%-30s" % _test_name(test_class),
+ # flush now so that long running tests are easier to follow
+ sys.stdout.flush()
+
+ obj = None
+ try:
+ try: # run test and show result
+ obj = test_class()
+ obj.setup()
+ obj.runtest()
+ print "OK"
+ except KeyboardInterrupt:
+ print "INTERRUPT"
+ _report_error(obj, debugger)
+ ret = 2
+ break
+ except NotRunError, msg:
+ print "NOTRUN, %s" % msg.value
+ except:
+ print "FAIL"
+ _report_error(obj, debugger)
+ ret = 1
+ finally:
+ while obj and obj._cleanups:
+ try:
+ apply(obj._cleanups.pop())
+ except KeyboardInterrupt:
+ print "interrupted during teardown"
+ _report_error(obj, debugger)
+ ret = 2
+ break
+ except:
+ print "error during teardown"
+ _report_error(obj, debugger)
+ ret = 1
+ # Display log file if we're verbose
+ if ret == 0 and verbose:
+ obj.explain_failure()
+
+ return ret
+
+
+def _test_name(test_class):
+ """Return a human-readable name for a test class.
+ """
+ try:
+ return test_class.__name__
+ except:
+ return `test_class`
+
+
+def print_help():
+ """Help for people running tests"""
+ import sys
+ print """%s: software test suite based on ComfyChair
+
+usage:
+ To run all tests, just run this program. To run particular tests,
+ list them on the command line.
+
+options:
+ --help show usage message
+ --list list available tests
+ --verbose, -v show more information while running tests
+ --post-mortem, -p enter Python debugger on error
+""" % sys.argv[0]
+
+
+def print_list(test_list):
+ """Show list of available tests"""
+ for test_class in test_list:
+ print " %s" % _test_name(test_class)
+
+
+def main(tests, extra_tests=[]):
+ """Main entry point for test suites based on ComfyChair.
+
+ inputs:
+ tests Sequence of TestCase subclasses to be run by default.
+ extra_tests Sequence of TestCase subclasses that are available but
+ not run by default.
+
+Test suites should contain this boilerplate:
+
+ if __name__ == '__main__':
+ comfychair.main(tests)
+
+This function handles standard options such as --help and --list, and
+by default runs all tests in the suggested order.
+
+Calls sys.exit() on completion.
+"""
+ from sys import argv
+ import getopt, sys
+
+ opt_verbose = 0
+ debugger = None
+
+ opts, args = getopt.getopt(argv[1:], 'pv',
+ ['help', 'list', 'verbose', 'post-mortem'])
+ for opt, opt_arg in opts:
+ if opt == '--help':
+ print_help()
+ return
+ elif opt == '--list':
+ print_list(tests + extra_tests)
+ return
+ elif opt == '--verbose' or opt == '-v':
+ opt_verbose = 1
+ elif opt == '--post-mortem' or opt == '-p':
+ import pdb
+ debugger = pdb.post_mortem
+
+ if args:
+ all_tests = tests + extra_tests
+ by_name = {}
+ for t in all_tests:
+ by_name[_test_name(t)] = t
+ which_tests = []
+ for name in args:
+ which_tests.append(by_name[name])
+ else:
+ which_tests = tests
+
+ sys.exit(runtests(which_tests, verbose=opt_verbose,
+ debugger=debugger))
+
+
+if __name__ == '__main__':
+ print __doc__
diff --git a/source/stf/example.py b/source/stf/example.py
new file mode 100755
index 00000000000..96e34bf3d3b
--- /dev/null
+++ b/source/stf/example.py
@@ -0,0 +1,38 @@
+#! /usr/bin/env python
+
+# Copyright (C) 2003 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 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
+
+
+"""example of using ComfyChair"""
+
+import comfychair
+
+class OnePlusOne(comfychair.TestCase):
+ def runtest(self):
+ self.assert_(1 + 1 == 2)
+
+class FailTest(comfychair.TestCase):
+ def runtest(self):
+ self.assert_(1 + 1 == 3)
+
+tests = [OnePlusOne]
+extra_tests = [FailTest]
+
+if __name__ == '__main__':
+ comfychair.main(tests, extra_tests=extra_tests)
+
diff --git a/source/stf/info3cache.py b/source/stf/info3cache.py
new file mode 100755
index 00000000000..96d5a1d4596
--- /dev/null
+++ b/source/stf/info3cache.py
@@ -0,0 +1,54 @@
+#!/usr/bin/python
+#
+# Upon a winbindd authentication, test that an info3 record is cached in
+# netsamlogon_cache.tdb and cache records are removed from winbindd_cache.tdb
+#
+
+import comfychair, stf
+from samba import tdb, winbind
+
+#
+# We want to implement the following test on a win2k native mode domain.
+#
+# 1. trash netsamlogon_cache.tdb
+# 2. wbinfo -r DOMAIN\Administrator [FAIL]
+# 3. wbinfo --auth-crap DOMAIN\Administrator%password [PASS]
+# 4. wbinfo -r DOMAIN\Administrator [PASS]
+#
+# Also for step 3 we want to try 'wbinfo --auth-smbd' and
+# 'wbinfo --auth-plaintext'
+#
+
+#
+# TODO: To implement this test we need to be able to
+#
+# - pass username%password combination for an invidivual winbindd request
+# (so we can get the administrator SID so we can clear the info3 cache)
+#
+# - start/restart winbindd (to trash the winbind cache)
+#
+# - from samba import dynconfig (to find location of info3 cache)
+#
+# - be able to modify the winbindd cache (to set/reset individual winbind
+# cache entries)
+#
+# - have --auth-crap present in HEAD
+#
+
+class WinbindAuthCrap(comfychair.TestCase):
+ def runtest(self):
+ raise comfychair.NotRunError, "not implemented"
+
+class WinbindAuthSmbd(comfychair.TestCase):
+ def runtest(self):
+ # Grr - winbindd in HEAD doesn't contain the auth_smbd function
+ raise comfychair.NotRunError, "no auth_smbd in HEAD"
+
+class WinbindAuthPlaintext(comfychair.TestCase):
+ def runtest(self):
+ raise comfychair.NotRunError, "not implemented"
+
+tests = [WinbindAuthCrap, WinbindAuthSmbd, WinbindAuthPlaintext]
+
+if __name__ == "__main__":
+ comfychair.main(tests)
diff --git a/source/stf/notes.txt b/source/stf/notes.txt
new file mode 100644
index 00000000000..68aca63c237
--- /dev/null
+++ b/source/stf/notes.txt
@@ -0,0 +1,175 @@
+ -*- indented-text -*-
+
+(set lotus no)
+
+
+
+Notes on using comfychair with Samba (samba testing framework units):
+
+The tests need to rely on some external resources, such as
+
+If suitable resources are not available, need to skip particular
+tests. Must include a message indicating what resources would be
+needed to run that test. (e.g. must be root.)
+
+We want to be able to select and run particular subsets of tests, such
+as "all winbind tests".
+
+We want to keep the number of configurable parameters down as much as
+possible, to make it easy on people running the tests.
+
+Wherever possible, the tests should set up their preconditions, but a
+few basic resources need to be provided by the people running the
+tests. So for example, rather than asking the user for the name of a
+non-root user, we should give the tests the administrator name and
+password, and it can create a new user to use.
+
+This makes it simpler to get the tests running, and possible also
+makes them more reproducible.
+
+In the future, rather than using NT machines provided by the test
+person, we might have a way to drive VMWare non-persistent sessions,
+to make tests even more tightly controlled.
+
+
+Another design question is how to communicate this information to the
+tests. If there's a lot of settings, then it might need to be stored
+in a configuration file.
+
+However, if we succeed in cutting down the number of parameters, then
+it might be straightforward to pass the information on the command
+line or in an environment variable.
+
+Environment variables are probably better because they can't be seen
+by other users, and they are more easily passed down through an
+invocation of "make check".
+
+
+
+Notes on Samba Testing Framework for Unittests
+----------------------------------------------
+
+This is to be read after reading the notes.txt from comfychair. I'm
+proposing a slightly more concrete description of what's described
+there.
+
+The model of having tests require named resources looks useful for
+incorporation into a framework that can be run by many people in
+widely different environments.
+
+Some possible environments for running the test framework in are:
+
+ - Casual downloader of Samba compiling from source and just wants
+ to run 'make check'. May only have one Unix machine and a
+ handful of clients.
+
+ - Samba team member with access to a small number of other
+ machines or VMware sessions.
+
+ - PSA developer who may not have intimate knowledge of Samba
+ internals and is only interested in testing against the PSA.
+
+ - Non-team hacker wanting to run test suite after making small
+ hacks.
+
+ - Build farm environment (loaner machine with no physical access
+ or root privilege).
+
+ - HP BAT.
+
+Developers in most of these environments are also potential test case
+authors. It should be easy for people unfamiliar with the framework
+to write new tests and have them work. We should provide examples and
+the existing tests should well written and understandable.
+
+Different types of tests:
+
+ - Tests that check Samba internals and link against
+ libbigballofmud.so. For example:
+
+ - Upper/lowercase string functions
+ - user_in_list() for large lists
+
+ - Tests that use the Samba Python extensions.
+
+ - Tests that execute Samba command line programs, for example
+ smbpasswd.
+
+ - Tests that require other resources on the network such as domain
+ controllers or PSAs.
+
+ - Tests that are performed on the documentation or the source code
+ such as:
+
+ - grep for common spelling mistakes made by abartlet (-:
+ - grep for company copyright (IBM, HP)
+
+ - Link to other existing testing frameworks (smbtorture,
+ abartlet's bash based build farm tests)
+
+I propose a TestResourceManager which would be instantiated by a test
+case. The test case would require("resourcename") as part of its
+constructor and raise a comfychair.NotRun exception if the resource
+was not present. A TestResource class could be defined which could
+read a configuration file or examine a environment variable and
+register a resource only if some condition was satisfied.
+
+It would be nice to be able to completely separate the PSA testing
+from the test framework. This would entail being able to define test
+resources dynamically, possibly with a plugin type system.
+
+class TestResourceManager:
+ def __init__(self, name):
+ self.resources = {}
+
+ def register(self, resource):
+ name = resource.name()
+ if self.resources.has_key(name):
+ raise "Test manager already has resource %s" % name
+ self.resources[name] = resource
+
+ def require(self, resource_name):
+ if not self.resources.has_key(resource_name):
+ raise "Test manager does not have resources %s" % resource_name
+
+class TestResource:
+ def __init__(self, name):
+ self.name = name
+
+ def name(self):
+ return self.name
+
+import os
+
+trm = TestResourceManager()
+
+if os.getuid() == 0:
+ trm.register(TestResource("root"))
+
+A config-o-matic Python module can take a list of machines and
+administrator%password entries and classify them by operating system
+version and service pack. These resources would be registered with
+the TestResourceManager.
+
+Some random thoughts about named resources for network servers:
+
+require("nt4.sp3")
+require("nt4.domaincontroller")
+require("psa")
+
+Some kind of format for location of passwords, libraries:
+
+require("exec(smbpasswd)")
+require("lib(bigballofmud)")
+
+maybe require("exec.smbpasswd") looks nicer...
+
+The require() function could return a dictionary of configuration
+information or some handle to fetch dynamic information on. We may
+need to create and destroy extra users or print queues. How to manage
+cleanup of dynamic resources?
+
+Requirements for running stf:
+
+ - Python, obviously
+ - Samba python extensions
diff --git a/source/stf/osver.py b/source/stf/osver.py
new file mode 100755
index 00000000000..68601fa7bb4
--- /dev/null
+++ b/source/stf/osver.py
@@ -0,0 +1,55 @@
+#!/usr/bin/python
+#
+# Utilities for determining the Windows operating system version remotely.
+#
+
+from samba import srvsvc
+
+# Constants
+
+PLATFORM_UNKNOWN = 0
+PLATFORM_WIN9X = 1
+PLATFORM_NT4 = 2
+PLATFORM_NT5 = 3 # Windows 2000
+
+def platform_name(platform_type):
+
+ platform_names = { PLATFORM_UNKNOWN: "Unknown",
+ PLATFORM_WIN9X: "Windows 9x",
+ PLATFORM_NT4: "Windows NT",
+ PLATFORM_NT5: "Windows 2000" }
+
+ if platform_names.has_key(platform_type):
+ return platform_names[platform_type]
+
+ return "Unknown"
+
+def platform_type(info101):
+ """Determine the operating system type from a SRV_INFO_101."""
+
+ if info101['major_version'] == 4 and info101['minor_version'] == 0:
+ return PLATFORM_NT4
+
+ if info101['major_version'] == 5 and info101['minor_version'] == 0:
+ return PLATFORM_NT5
+
+ return PLATFORM_UNKNOWN
+
+def is_domain_controller(info101):
+ """Return true if the server_type field from a SRV_INFO_101
+ indicates a domain controller."""
+ return info101['server_type'] & srvsvc.SV_TYPE_DOMAIN_CTRL
+
+def os_version(name):
+ info = srvsvc.netservergetinfo("\\\\%s" % name, 101)
+ return platform_type(info)
+
+if __name__ == "__main__":
+ import sys
+ if len(sys.argv) != 2:
+ print "Usage: osver.py server"
+ sys.exit(0)
+ info = srvsvc.netservergetinfo("\\\\%s" % sys.argv[1], 101)
+ print "platform type = %d" % platform_type(info)
+ if is_domain_controller(info):
+ print "%s is a domain controller" % sys.argv[1]
diff --git a/source/stf/pythoncheck.py b/source/stf/pythoncheck.py
new file mode 100755
index 00000000000..398bb2c3d69
--- /dev/null
+++ b/source/stf/pythoncheck.py
@@ -0,0 +1,48 @@
+#! /usr/bin/python
+
+# Comfychair test cases for Samba python extensions
+
+# Copyright (C) 2003 by Tim Potter <tpot@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., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+# USA
+
+"""These tests are run by Samba's "make check"."""
+
+import sys, comfychair
+
+class ImportTest(comfychair.TestCase):
+ """Check that all modules can be imported without error."""
+ def runtest(self):
+ python_modules = ['spoolss', 'lsa', 'samr', 'winbind', 'winreg',
+ 'srvsvc', 'tdb', 'smb', 'tdbpack']
+ for m in python_modules:
+ try:
+ __import__('samba.%s' % m)
+ except ImportError, msg:
+ self.log(str(msg))
+ self.fail('error importing %s module' % m)
+
+tests = [ImportTest]
+
+if __name__ == '__main__':
+ # Some magic to repend build directory to python path so we see the
+ # objects we have built and not previously installed stuff.
+ from distutils.util import get_platform
+ from os import getcwd
+ sys.path.insert(0, '%s/build/lib.%s-%s' %
+ (getcwd(), get_platform(), sys.version[0:3]))
+
+ comfychair.main(tests)
diff --git a/source/stf/sambalib.py b/source/stf/sambalib.py
new file mode 100644
index 00000000000..13d38e2a281
--- /dev/null
+++ b/source/stf/sambalib.py
@@ -0,0 +1,41 @@
+#! /usr/bin/python
+
+# Comfychair test cases for Samba string functions.
+
+# Copyright (C) 2003 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 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
+
+"""Tests for Samba library functions."""
+
+import sys, re, comfychair
+from unicodenames import *
+
+class snprintf_Test(comfychair.TestCase):
+ def runtest(self):
+ # Everything is built in to the test
+ out, err = self.runcmd('t_snprintf')
+
+# Define the tests exported by this module
+tests = [snprintf_Test]
+
+# Handle execution of this file as a main program
+if __name__ == '__main__':
+ comfychair.main(tests)
+
+# Local variables:
+# coding: utf-8
+# End:
diff --git a/source/stf/smbcontrol.py b/source/stf/smbcontrol.py
new file mode 100755
index 00000000000..30c331819c7
--- /dev/null
+++ b/source/stf/smbcontrol.py
@@ -0,0 +1,238 @@
+#!/usr/bin/python
+#
+# Test for smbcontrol command line argument handling.
+#
+
+import comfychair
+
+class NoArgs(comfychair.TestCase):
+ """Test no arguments produces usage message."""
+ def runtest(self):
+ out = self.runcmd("smbcontrol", expectedResult = 1)
+ self.assert_re_match("Usage: smbcontrol", out[1])
+
+class OneArg(comfychair.TestCase):
+ """Test single argument produces usage message."""
+ def runtest(self):
+ out = self.runcmd("smbcontrol foo", expectedResult = 1)
+ self.assert_re_match("Usage: smbcontrol", out[1])
+
+class SmbdDest(comfychair.TestCase):
+ """Test the broadcast destination 'smbd'."""
+ def runtest(self):
+ out = self.runcmd("smbcontrol smbd noop")
+
+class NmbdDest(comfychair.TestCase):
+ """Test the destination 'nmbd'."""
+ def runtest(self):
+ # We need a way to start/stop/whatever nmbd
+ raise comfychair.NotRunError, "not implemented"
+
+class PidDest(comfychair.TestCase):
+ """Test a pid number destination'."""
+ def runtest(self):
+ out = self.runcmd("smbcontrol 1234 noop")
+
+class SelfDest(comfychair.TestCase):
+ """Test the destination 'self'."""
+ def runtest(self):
+ out = self.runcmd("smbcontrol self noop")
+
+class WinbinddDest(comfychair.TestCase):
+ """Test the destination 'winbindd'."""
+ def runtest(self):
+ # We need a way to start/stop/whatever winbindd
+ raise comfychair.NotRunError, "not implemented"
+
+class BadDest(comfychair.TestCase):
+ """Test a bad destination."""
+ def runtest(self):
+ out = self.runcmd("smbcontrol foo noop", expectedResult = 1)
+
+class BadCmd(comfychair.TestCase):
+ """Test a bad command."""
+ def runtest(self):
+ out = self.runcmd("smbcontrol self spottyfoot", expectedResult = 1)
+ self.assert_re_match("smbcontrol: unknown command", out[1]);
+
+class NoArgCmdTest(comfychair.TestCase):
+ """A test class that tests a command with no argument."""
+ def runtest(self):
+ self.require_root()
+ out = self.runcmd("smbcontrol self %s" % self.cmd)
+ out = self.runcmd("smbcontrol self %s spottyfoot" % self.cmd,
+ expectedResult = 1)
+
+class ForceElection(NoArgCmdTest):
+ """Test a force-election message."""
+ def setup(self):
+ self.cmd = "force-election"
+
+class SamSync(NoArgCmdTest):
+ """Test a samsync message."""
+ def setup(self):
+ self.cmd = "samsync"
+
+class SamRepl(NoArgCmdTest):
+ """Test a samrepl message."""
+ def setup(self):
+ self.cmd = "samrepl"
+
+class DmallocChanged(NoArgCmdTest):
+ """Test a dmalloc-changed message."""
+ def setup(self):
+ self.cmd = "dmalloc-log-changed"
+
+class DmallocMark(NoArgCmdTest):
+ """Test a dmalloc-mark message."""
+ def setup(self):
+ self.cmd = "dmalloc-mark"
+
+class Shutdown(NoArgCmdTest):
+ """Test a shutdown message."""
+ def setup(self):
+ self.cmd = "shutdown"
+
+class Ping(NoArgCmdTest):
+ """Test a ping message."""
+ def setup(self):
+ self.cmd = "ping"
+
+class Debuglevel(NoArgCmdTest):
+ """Test a debuglevel message."""
+ def setup(self):
+ self.cmd = "debuglevel"
+
+class OneArgCmdTest(comfychair.TestCase):
+ """A test class that tests a command with one argument."""
+ def runtest(self):
+ self.require_root()
+ out = self.runcmd("smbcontrol self %s spottyfoot" % self.cmd)
+ out = self.runcmd("smbcontrol self %s" % self.cmd, expectedResult = 1)
+
+class DrvUpgrade(OneArgCmdTest):
+ """Test driver upgrade message."""
+ def setup(self):
+ self.cmd = "drvupgrade"
+
+class CloseShare(OneArgCmdTest):
+ """Test close share message."""
+ def setup(self):
+ self.cmd = "close-share"
+
+class Debug(OneArgCmdTest):
+ """Test a debug message."""
+ def setup(self):
+ self.cmd = "debug"
+
+class PrintNotify(comfychair.TestCase):
+ """Test print notification commands."""
+ def runtest(self):
+
+ # No subcommand
+
+ out = self.runcmd("smbcontrol self printnotify", expectedResult = 1)
+ self.assert_re_match("Must specify subcommand", out[1]);
+
+ # Invalid subcommand name
+
+ out = self.runcmd("smbcontrol self printnotify spottyfoot",
+ expectedResult = 1)
+ self.assert_re_match("Invalid subcommand", out[1]);
+
+ # Queue commands
+
+ for cmd in ["queuepause", "queueresume"]:
+
+ out = self.runcmd("smbcontrol self printnotify %s" % cmd,
+ expectedResult = 1)
+ self.assert_re_match("Usage:", out[1])
+
+ out = self.runcmd("smbcontrol self printnotify %s spottyfoot"
+ % cmd)
+
+ # Job commands
+
+ for cmd in ["jobpause", "jobresume", "jobdelete"]:
+
+ out = self.runcmd("smbcontrol self printnotify %s" % cmd,
+ expectedResult = 1)
+ self.assert_re_match("Usage:", out[1])
+
+ out = self.runcmd("smbcontrol self printnotify %s spottyfoot"
+ % cmd, expectedResult = 1)
+ self.assert_re_match("Usage:", out[1])
+
+ out = self.runcmd("smbcontrol self printnotify %s spottyfoot 123"
+ % cmd)
+
+ # Printer properties
+
+ out = self.runcmd("smbcontrol self printnotify printer",
+ expectedResult = 1)
+ self.assert_re_match("Usage", out[1])
+
+ out = self.runcmd("smbcontrol self printnotify printer spottyfoot",
+ expectedResult = 1)
+ self.assert_re_match("Usage", out[1])
+
+ for cmd in ["comment", "port", "driver"]:
+
+ out = self.runcmd("smbcontrol self printnotify printer spottyfoot "
+ "%s" % cmd, expectedResult = 1)
+ self.assert_re_match("Usage", out[1])
+
+ out = self.runcmd("smbcontrol self printnotify printer spottyfoot "
+ "%s value" % cmd)
+
+class Profile(comfychair.TestCase):
+ """Test setting the profiling level."""
+ def runtest(self):
+ self.require_root()
+ out = self.runcmd("smbcontrol self profile", expectedResult = 1)
+ self.assert_re_match("Usage", out[1])
+
+ out = self.runcmd("smbcontrol self profile spottyfoot",
+ expectedResult = 1)
+ self.assert_re_match("Unknown", out[1])
+
+ for cmd in ["off", "count", "on", "flush"]:
+ out = self.runcmd("smbcontrol self profile %s" % cmd)
+
+class ProfileLevel(comfychair.TestCase):
+ """Test requesting the current profiling level."""
+ def runtest(self):
+ self.require_root()
+ out = self.runcmd("smbcontrol self profilelevel spottyfoot",
+ expectedResult = 1)
+ self.assert_re_match("Usage", out[1])
+
+ out = self.runcmd("smbcontrol self profilelevel")
+
+class TimeoutArg(comfychair.TestCase):
+ """Test the --timeout argument."""
+ def runtest(self):
+ out = self.runcmd("smbcontrol --timeout 5 self noop")
+ out = self.runcmd("smbcontrol --timeout spottyfoot self noop",
+ expectedResult = 1)
+
+class ConfigFileArg(comfychair.TestCase):
+ """Test the --configfile argument."""
+ def runtest(self):
+ out = self.runcmd("smbcontrol --configfile /dev/null self noop")
+
+class BogusArg(comfychair.TestCase):
+ """Test a bogus command line argument."""
+ def runtest(self):
+ out = self.runcmd("smbcontrol --bogus self noop", expectedResult = 1)
+
+tests = [NoArgs, OneArg, SmbdDest, NmbdDest, WinbinddDest, PidDest,
+ SelfDest, BadDest, BadCmd, Debug, ForceElection, SamSync,
+ SamRepl, DmallocMark, DmallocChanged, Shutdown, DrvUpgrade,
+ CloseShare, Ping, Debuglevel, PrintNotify, Profile, ProfileLevel,
+ TimeoutArg, ConfigFileArg, BogusArg]
+
+# Handle execution of this file as a main program
+
+if __name__ == '__main__':
+ comfychair.main(tests)
diff --git a/source/stf/spoolss.py b/source/stf/spoolss.py
new file mode 100755
index 00000000000..735291508bc
--- /dev/null
+++ b/source/stf/spoolss.py
@@ -0,0 +1,288 @@
+#!/usr/bin/python
+
+import re
+import comfychair, stf
+from samba import spoolss
+
+class PrintServerTest(comfychair.TestCase):
+ """An abstract class requiring a print server."""
+ def setUp(self):
+ # TODO: create a test printer
+ self.server = stf.get_server(platform = "nt")
+ self.require(self.server != None, "print server required")
+ # TODO: remove hardcoded printer name
+ self.printername = "p"
+ self.uncname = "\\\\%s\\%s" % \
+ (self.server["hostname"], self.printername)
+
+class W2kPrintServerTest(comfychair.TestCase):
+ """An abstract class requiring a print server."""
+ def setUp(self):
+ # TODO: create a test printer
+ self.server = stf.get_server(platform = "nt5")
+ self.require(self.server != None, "print server required")
+ # TODO: remove hardcoded printer name
+ self.printername = "p"
+ self.uncname = "\\\\%s\\%s" % \
+ (self.server["hostname"], self.printername)
+
+class CredentialTest(PrintServerTest):
+ """An class that calls a function with various sets of credentials."""
+ def runTest(self):
+
+ bad_user_creds = {"username": "spotty",
+ "domain": "dog",
+ "password": "bone"}
+
+ cases = ((self.server["administrator"], "Admin credentials", 1),
+ (bad_user_creds, "Bad credentials", 0))
+
+ # TODO: add unpriv user case
+
+ for creds, testname, result in cases:
+ try:
+ self.runTestArg(creds)
+ except:
+ if result:
+ import traceback
+ traceback.print_exc()
+ self.fail("rpc with creds %s failed when it "
+ "should have suceeded" % creds)
+ return
+
+ if not result:
+ self.fail("rpc with creds %s suceeded when it should "
+ "have failed" % creds)
+
+class ArgTestServer(PrintServerTest):
+ """Test a RPC that takes a UNC print server name."""
+ def runTest(self):
+
+ # List of test cases, %s substituted for server name
+
+ cases = (("", "No server name", 0),
+ ("\\\\%s", "Valid server name", 1),
+ ("\\%s", "Invalid unc server name", 0),
+ ("\\\\%s__", "Invalid unc server name", 0))
+
+ for unc, testname, result in cases:
+ unc = re.sub("%s", self.server["hostname"], unc)
+ try:
+ self.runTestArg(unc)
+ except:
+ if result:
+ self.fail("rpc(\"%s\") failed when it should have "
+ "suceeded" % unc)
+ return
+
+ if not result:
+ # Suceeded when we should have failed
+ self.fail("rpc(\"%s\") suceeded when it should have "
+ "failed" % unc)
+
+class ArgTestServerAndPrinter(ArgTestServer):
+ """Test a RPC that takes a UNC print server or UNC printer name."""
+ def runTest(self):
+
+ ArgTestServer.runTest(self)
+
+ # List of test cases, %s substituted for server name, %p substituted
+ # for printer name.
+
+ cases = (("\\\\%s\\%p", "Valid server and printer name", 1),
+ ("\\\\%s\\%p__", "Valid server, invalid printer name", 0),
+ ("\\\\%s__\\%p", "Invalid server, valid printer name", 0))
+
+ for unc, testname, result in cases:
+ unc = re.sub("%s", self.server["hostname"], unc)
+ unc = re.sub("%p", self.printername, unc)
+ try:
+ self.runTestArg(unc)
+ except:
+ if result:
+ self.fail("openprinter(\"%s\") failed when it should have "
+ "suceeded" % unc)
+ return
+
+ if not result:
+ # Suceeded when we should have failed
+ self.fail("openprinter(\"%s\") suceeded when it should have "
+ "failed" % unc)
+
+class OpenPrinterArg(ArgTestServerAndPrinter):
+ """Test the OpenPrinter RPC with combinations of valid and invalid
+ server and printer names."""
+ def runTestArg(self, unc):
+ spoolss.openprinter(unc)
+
+class OpenPrinterCred(CredentialTest):
+ """Test opening printer with good and bad credentials."""
+ def runTestArg(self, creds):
+ spoolss.openprinter(self.uncname, creds = creds)
+
+class ClosePrinter(PrintServerTest):
+ """Test the ClosePrinter RPC on a printer handle."""
+ def runTest(self):
+ hnd = spoolss.openprinter(self.uncname)
+ spoolss.closeprinter(hnd)
+
+class ClosePrinterServer(PrintServerTest):
+ """Test the ClosePrinter RPC on a print server handle."""
+ def runTest(self):
+ hnd = spoolss.openprinter("\\\\%s" % self.server["hostname"])
+ spoolss.closeprinter(hnd)
+
+class GetPrinterInfo(PrintServerTest):
+ """Retrieve printer info at various levels."""
+
+ # Sample printer data
+
+ sample_info = {
+ 0: {'printer_errors': 0, 'unknown18': 0, 'unknown13': 0, 'unknown26': 0, 'cjobs': 0, 'unknown11': 0, 'server_name': '\\\\win2kdc1', 'total_pages': 0, 'unknown15': 586, 'unknown16': 0, 'month': 2, 'unknown20': 0, 'second': 23, 'unknown22': 983040, 'unknown25': 0, 'total_bytes': 0, 'unknown27': 0, 'year': 2003, 'build_version': 2195, 'unknown28': 0, 'global_counter': 4, 'day': 13, 'minute': 53, 'total_jobs': 0, 'unknown29': 1114112, 'name': '\\\\win2kdc1\\p', 'hour': 2, 'level': 0, 'c_setprinter': 0, 'change_id': 522454169, 'major_version': 5, 'unknown23': 15, 'day_of_week': 4, 'unknown14': 1, 'session_counter': 2, 'status': 1, 'unknown7': 1, 'unknown8': 0, 'unknown9': 0, 'milliseconds': 421, 'unknown24': 0},
+ 1: {'comment': "I'm a teapot!", 'level': 1, 'flags': 8388608, 'name': '\\\\win2kdc1\\p', 'description': '\\\\win2kdc1\\p,HP LaserJet 4,Canberra office'},
+ 2: {'comment': "I'm a teapot!", 'status': 1, 'print_processor': 'WinPrint', 'until_time': 0, 'share_name': 'p', 'start_time': 0, 'device_mode': {'icm_method': 1, 'bits_per_pel': 0, 'log_pixels': 0, 'orientation': 1, 'panning_width': 0, 'color': 2, 'pels_width': 0, 'print_quality': 600, 'driver_version': 24, 'display_flags': 0, 'y_resolution': 600, 'media_type': 0, 'display_frequency': 0, 'icm_intent': 0, 'pels_height': 0, 'reserved1': 0, 'size': 220, 'scale': 100, 'dither_type': 0, 'panning_height': 0, 'default_source': 7, 'duplex': 1, 'fields': 16131, 'spec_version': 1025, 'copies': 1, 'device_name': '\\\\win2kdc1\\p', 'paper_size': 1, 'paper_length': 0, 'private': 'private', 'collate': 0, 'paper_width': 0, 'form_name': 'Letter', 'reserved2': 0, 'tt_option': 0}, 'port_name': 'LPT1:', 'sepfile': '', 'parameters': '', 'security_descriptor': {'group_sid': 'S-1-5-21-1606980848-1677128483-854245398-513', 'sacl': None, 'dacl': {'ace_list': [{'flags': 0, 'type': 0, 'mask': 983052, 'trustee': 'S-1-5-32-544'}, {'flags': 9, 'type': 0, 'mask': 983056, 'trustee': 'S-1-5-32-544'}, {'flags': 0, 'type': 0, 'mask': 131080, 'trustee': 'S-1-5-21-1606980848-1677128483-854245398-1121'}, {'flags': 10, 'type': 0, 'mask': 131072, 'trustee': 'S-1-3-0'}, {'flags': 9, 'type': 0, 'mask': 983056, 'trustee': 'S-1-3-0'}, {'flags': 0, 'type': 0, 'mask': 131080, 'trustee': 'S-1-5-21-1606980848-1677128483-854245398-1124'}, {'flags': 0, 'type': 0, 'mask': 131080, 'trustee': 'S-1-1-0'}, {'flags': 0, 'type': 0, 'mask': 983052, 'trustee': 'S-1-5-32-550'}, {'flags': 9, 'type': 0, 'mask': 983056, 'trustee': 'S-1-5-32-550'}, {'flags': 0, 'type': 0, 'mask': 983052, 'trustee': 'S-1-5-32-549'}, {'flags': 9, 'type': 0, 'mask': 983056, 'trustee': 'S-1-5-32-549'}, {'flags': 0, 'type': 0, 'mask': 983052, 'trustee': 'S-1-5-21-1606980848-1677128483-854245398-1106'}], 'revision': 2}, 'owner_sid': 'S-1-5-32-544', 'revision': 1}, 'name': '\\\\win2kdc1\\p', 'server_name': '\\\\win2kdc1', 'level': 2, 'datatype': 'RAW', 'cjobs': 0, 'average_ppm': 0, 'priority': 1, 'driver_name': 'HP LaserJet 4', 'location': 'Canberra office', 'attributes': 8776, 'default_priority': 0},
+ 3: {'flags': 4, 'security_descriptor': {'group_sid': 'S-1-5-21-1606980848-1677128483-854245398-513', 'sacl': None, 'dacl': {'ace_list': [{'flags': 0, 'type': 0, 'mask': 983052, 'trustee': 'S-1-5-32-544'}, {'flags': 9, 'type': 0, 'mask': 983056, 'trustee': 'S-1-5-32-544'}, {'flags': 0, 'type': 0, 'mask': 131080, 'trustee': 'S-1-5-21-1606980848-1677128483-854245398-1121'}, {'flags': 10, 'type': 0, 'mask': 131072, 'trustee': 'S-1-3-0'}, {'flags': 9, 'type': 0, 'mask': 983056, 'trustee': 'S-1-3-0'}, {'flags': 0, 'type': 0, 'mask': 131080, 'trustee': 'S-1-5-21-1606980848-1677128483-854245398-1124'}, {'flags': 0, 'type': 0, 'mask': 131080, 'trustee': 'S-1-1-0'}, {'flags': 0, 'type': 0, 'mask': 983052, 'trustee': 'S-1-5-32-550'}, {'flags': 9, 'type': 0, 'mask': 983056, 'trustee': 'S-1-5-32-550'}, {'flags': 0, 'type': 0, 'mask': 983052, 'trustee': 'S-1-5-32-549'}, {'flags': 9, 'type': 0, 'mask': 983056, 'trustee': 'S-1-5-32-549'}, {'flags': 0, 'type': 0, 'mask': 983052, 'trustee': 'S-1-5-21-1606980848-1677128483-854245398-1106'}], 'revision': 2}, 'owner_sid': 'S-1-5-32-544', 'revision': 1}, 'level': 3}
+ }
+
+ def runTest(self):
+ self.hnd = spoolss.openprinter(self.uncname)
+
+ # Everyone should have getprinter levels 0-3
+
+ for i in (0, 1, 2, 3):
+ info = self.hnd.getprinter(level = i)
+ try:
+ stf.dict_check(self.sample_info[i], info)
+ except ValueError, msg:
+ raise "info%d: %s" % (i, msg)
+
+class EnumPrinters(PrintServerTest):
+ """Enumerate print info at various levels."""
+
+ sample_info = {
+
+ 0: {'q': {'printer_errors': 0, 'unknown18': 0, 'unknown13': 0, 'unknown26': 0, 'cjobs': 0, 'unknown11': 0, 'server_name': '', 'total_pages': 0, 'unknown15': 586, 'unknown16': 0, 'month': 2, 'unknown20': 0, 'second': 23, 'unknown22': 983040, 'unknown25': 0, 'total_bytes': 0, 'unknown27': 0, 'year': 2003, 'build_version': 2195, 'unknown28': 0, 'global_counter': 4, 'day': 13, 'minute': 53, 'total_jobs': 0, 'unknown29': -1833435136, 'name': 'q', 'hour': 2, 'level': 0, 'c_setprinter': 0, 'change_id': 522454169, 'major_version': 5, 'unknown23': 15, 'day_of_week': 4, 'unknown14': 1, 'session_counter': 1, 'status': 0, 'unknown7': 1, 'unknown8': 0, 'unknown9': 0, 'milliseconds': 421, 'unknown24': 0}, 'p': {'printer_errors': 0, 'unknown18': 0, 'unknown13': 0, 'unknown26': 0, 'cjobs': 0, 'unknown11': 0, 'server_name': '', 'total_pages': 0, 'unknown15': 586, 'unknown16': 0, 'month': 2, 'unknown20': 0, 'second': 23, 'unknown22': 983040, 'unknown25': 0, 'total_bytes': 0, 'unknown27': 0, 'year': 2003, 'build_version': 2195, 'unknown28': 0, 'global_counter': 4, 'day': 13, 'minute': 53, 'total_jobs': 0, 'unknown29': -1831337984, 'name': 'p', 'hour': 2, 'level': 0, 'c_setprinter': 0, 'change_id': 522454169, 'major_version': 5, 'unknown23': 15, 'day_of_week': 4, 'unknown14': 1, 'session_counter': 1, 'status': 1, 'unknown7': 1, 'unknown8': 0, 'unknown9': 0, 'milliseconds': 421, 'unknown24': 0}, 'magpie': {'printer_errors': 0, 'unknown18': 0, 'unknown13': 0, 'unknown26': 0, 'cjobs': 0, 'unknown11': 0, 'server_name': '', 'total_pages': 0, 'unknown15': 586, 'unknown16': 0, 'month': 2, 'unknown20': 0, 'second': 23, 'unknown22': 983040, 'unknown25': 0, 'total_bytes': 0, 'unknown27': 0, 'year': 2003, 'build_version': 2195, 'unknown28': 0, 'global_counter': 4, 'day': 13, 'minute': 53, 'total_jobs': 0, 'unknown29': 1114112, 'name': 'magpie', 'hour': 2, 'level': 0, 'c_setprinter': 0, 'change_id': 522454169, 'major_version': 5, 'unknown23': 15, 'day_of_week': 4, 'unknown14': 1, 'session_counter': 1, 'status': 0, 'unknown7': 1, 'unknown8': 0, 'unknown9': 0, 'milliseconds': 421, 'unknown24': 0}},
+
+ 1: {'q': {'comment': 'cheepy birds', 'level': 1, 'flags': 8388608, 'name': 'q', 'description': 'q,HP LaserJet 4,'}, 'p': {'comment': "I'm a teapot!", 'level': 1, 'flags': 8388608, 'name': 'p', 'description': 'p,HP LaserJet 4,Canberra office'}, 'magpie': {'comment': '', 'level': 1, 'flags': 8388608, 'name': 'magpie', 'description': 'magpie,Generic / Text Only,'}}
+ }
+
+ def runTest(self):
+ for i in (0, 1):
+ info = spoolss.enumprinters(
+ "\\\\%s" % self.server["hostname"], level = i)
+ try:
+ stf.dict_check(self.sample_info[i], info)
+ except ValueError, msg:
+ raise "info%d: %s" % (i, msg)
+
+class EnumPrintersArg(ArgTestServer):
+ def runTestArg(self, unc):
+ spoolss.enumprinters(unc)
+
+class EnumPrintersCred(CredentialTest):
+ """Test opening printer with good and bad credentials."""
+ def runTestArg(self, creds):
+ spoolss.enumprinters(
+ "\\\\%s" % self.server["hostname"], creds = creds)
+
+class EnumPrinterdrivers(PrintServerTest):
+
+ sample_info = {
+ 1: {'Okipage 10ex (PCL5E) : STANDARD': {'name': 'Okipage 10ex (PCL5E) : STANDARD', 'level': 1}, 'Generic / Text Only': {'name': 'Generic / Text Only', 'level': 1}, 'Brother HL-1030 series': {'name': 'Brother HL-1030 series', 'level': 1}, 'Brother HL-1240 series': {'name': 'Brother HL-1240 series', 'level': 1}, 'HP DeskJet 1220C Printer': {'name': 'HP DeskJet 1220C Printer', 'level': 1}, 'HP LaserJet 4100 PCL 6': {'name': 'HP LaserJet 4100 PCL 6', 'level': 1}, 'HP LaserJet 4': {'name': 'HP LaserJet 4', 'level': 1}},
+ 2: {'Okipage 10ex (PCL5E) : STANDARD': {'version': 2, 'config_file': '\\\\WIN2KDC1\\print$\\W32X86\\2\\RASDDUI.DLL', 'name': 'Okipage 10ex (PCL5E) : STANDARD', 'driver_path': '\\\\WIN2KDC1\\print$\\W32X86\\2\\RASDD.DLL', 'data_file': '\\\\WIN2KDC1\\print$\\W32X86\\2\\OKIPAGE.DLL', 'level': 2, 'architecture': 'Windows NT x86'}, 'Generic / Text Only': {'version': 3, 'config_file': '\\\\WIN2KDC1\\print$\\W32X86\\3\\UNIDRVUI.DLL', 'name': 'Generic / Text Only', 'driver_path': '\\\\WIN2KDC1\\print$\\W32X86\\3\\UNIDRV.DLL', 'data_file': '\\\\WIN2KDC1\\print$\\W32X86\\3\\TTY.GPD', 'level': 2, 'architecture': 'Windows NT x86'}, 'Brother HL-1030 series': {'version': 3, 'config_file': '\\\\WIN2KDC1\\print$\\W32X86\\3\\BRUHL99A.DLL', 'name': 'Brother HL-1030 series', 'driver_path': '\\\\WIN2KDC1\\print$\\W32X86\\3\\BROHL99A.DLL', 'data_file': '\\\\WIN2KDC1\\print$\\W32X86\\3\\BROHL103.PPD', 'level': 2, 'architecture': 'Windows NT x86'}, 'Brother HL-1240 series': {'version': 3, 'config_file': '\\\\WIN2KDC1\\print$\\W32X86\\3\\BRUHL99A.DLL', 'name': 'Brother HL-1240 series', 'driver_path': '\\\\WIN2KDC1\\print$\\W32X86\\3\\BROHL99A.DLL', 'data_file': '\\\\WIN2KDC1\\print$\\W32X86\\3\\BROHL124.PPD', 'level': 2, 'architecture': 'Windows NT x86'}, 'HP DeskJet 1220C Printer': {'version': 3, 'config_file': '\\\\WIN2KDC1\\print$\\W32X86\\3\\HPW8KMD.DLL', 'name': 'HP DeskJet 1220C Printer', 'driver_path': '\\\\WIN2KDC1\\print$\\W32X86\\3\\HPW8KMD.DLL', 'data_file': '\\\\WIN2KDC1\\print$\\W32X86\\3\\HPW8KMD.DLL', 'level': 2, 'architecture': 'Windows NT x86'}, 'HP LaserJet 4100 PCL 6': {'version': 3, 'config_file': '\\\\WIN2KDC1\\print$\\W32X86\\3\\HPBF042E.DLL', 'name': 'HP LaserJet 4100 PCL 6', 'driver_path': '\\\\WIN2KDC1\\print$\\W32X86\\3\\HPBF042G.DLL', 'data_file': '\\\\WIN2KDC1\\print$\\W32X86\\3\\HPBF042I.PMD', 'level': 2, 'architecture': 'Windows NT x86'}, 'HP LaserJet 4': {'version': 2, 'config_file': '\\\\WIN2KDC1\\print$\\W32X86\\2\\hpblff0.dll', 'name': 'HP LaserJet 4', 'driver_path': '\\\\WIN2KDC1\\print$\\W32X86\\2\\hpblff2.dll', 'data_file': '\\\\WIN2KDC1\\print$\\W32X86\\2\\hpblff39.pmd', 'level': 2, 'architecture': 'Windows NT x86'}}
+ }
+
+ def runTest(self):
+ for i in (1, 2):
+ info = spoolss.enumprinterdrivers(
+ "\\\\%s" % self.server["hostname"], level = i)
+ try:
+ if not self.sample_info.has_key(i):
+ self.log("%s" % info)
+ self.fail()
+ stf.dict_check(self.sample_info[i], info)
+ except ValueError, msg:
+ raise "info%d: %s" % (i, msg)
+
+class EnumPrinterdriversArg(ArgTestServer):
+ def runTestArg(self, unc):
+ spoolss.enumprinterdrivers(unc)
+
+class EnumPrinterdriversCred(CredentialTest):
+ """Test opening printer with good and bad credentials."""
+ def runTestArg(self, creds):
+ spoolss.enumprinterdrivers(
+ "\\\\%s" % self.server["hostname"], creds = creds)
+
+def usage():
+ print "Usage: spoolss.py [options] [test1[,test2...]]"
+ print "\t -v/--verbose Display debugging information"
+ print "\t -l/--list-tests List available tests"
+ print
+ print "A list of comma separated test names or regular expressions"
+ print "can be used to filter the tests performed."
+
+def test_match(subtest_list, test_name):
+ """Return true if a test matches a comma separated list of regular
+ expression of test names."""
+ # re.match does an implicit ^ at the start of the pattern.
+ # Explicitly anchor to end to avoid matching substrings.
+ for s in string.split(subtest_list, ","):
+ if re.match(s + "$", test_name):
+ return 1
+ return 0
+
+if __name__ == "__main__":
+ import os, sys, string
+ import getopt
+
+ try:
+ opts, args = getopt.getopt(sys.argv[1:], "vl", \
+ ["verbose", "list-tests"])
+ except getopt.GetoptError:
+ usage()
+ sys.exit(0)
+
+ verbose = 0
+ list_tests = 0
+
+ for opt, arg in opts:
+ if opt in ("-v", "--verbose"):
+ verbose = 1
+ if opt in ("-l", "--list-tests"):
+ list_tests = 1
+
+ if len(args) > 1:
+ usage()
+ sys.exit(0)
+
+ test_list = [
+ OpenPrinterArg,
+ OpenPrinterCred,
+ ClosePrinter,
+ ClosePrinterServer,
+ GetPrinterInfo,
+ EnumPrinters,
+ EnumPrintersCred,
+ EnumPrintersArg,
+ EnumPrinterdrivers,
+ EnumPrinterdriversCred,
+ EnumPrinterdriversArg,
+ ]
+
+ if len(args):
+ t = []
+ for test in test_list:
+ if test_match(args[0], test.__name__):
+ t.append(test)
+ test_list = t
+
+ if os.environ.has_key("SAMBA_DEBUG"):
+ spoolss.setup_logging(interactive = 1)
+ spoolss.set_debuglevel(10)
+
+ if list_tests:
+ for test in test_list:
+ print test.__name__
+ else:
+ comfychair.runtests(test_list, verbose = verbose)
diff --git a/source/stf/standardcheck.py b/source/stf/standardcheck.py
new file mode 100644
index 00000000000..e3292353e8b
--- /dev/null
+++ b/source/stf/standardcheck.py
@@ -0,0 +1,34 @@
+#! /usr/bin/python
+
+# Comfychair test cases for Samba
+
+# Copyright (C) 2003 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 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
+
+"""These tests are run by Samba's "make check"."""
+
+import strings, comfychair
+import smbcontrol, sambalib
+
+# There should not be any actual tests in here: this file just serves
+# to define the ones run by default. They're imported from other
+# modules.
+
+tests = strings.tests + smbcontrol.tests + sambalib.tests
+
+if __name__ == '__main__':
+ comfychair.main(tests)
diff --git a/source/stf/stf.py b/source/stf/stf.py
new file mode 100755
index 00000000000..ee0ff735612
--- /dev/null
+++ b/source/stf/stf.py
@@ -0,0 +1,101 @@
+#!/usr/bin/python
+#
+# Samba Testing Framework for Unit-testing
+#
+
+import os, string, re
+import osver
+
+def get_server_list_from_string(s):
+
+ server_list = []
+
+ # Format is a list of server:domain\username%password separated
+ # by commas.
+
+ for entry in string.split(s, ","):
+
+ # Parse entry
+
+ m = re.match("(.*):(.*)(\\\\|/)(.*)%(.*)", entry)
+ if not m:
+ raise "badly formed server list entry '%s'" % entry
+
+ server = m.group(1)
+ domain = m.group(2)
+ username = m.group(4)
+ password = m.group(5)
+
+ # Categorise servers
+
+ server_list.append({"platform": osver.os_version(server),
+ "hostname": server,
+ "administrator": {"username": username,
+ "domain": domain,
+ "password" : password}})
+
+ return server_list
+
+def get_server_list():
+ """Iterate through all sources of server info and append them all
+ in one big list."""
+
+ server_list = []
+
+ # The $STF_SERVERS environment variable
+
+ if os.environ.has_key("STF_SERVERS"):
+ server_list = server_list + \
+ get_server_list_from_string(os.environ["STF_SERVERS"])
+
+ return server_list
+
+def get_server(platform = None):
+ """Return configuration information for a server. The platform
+ argument can be a string either 'nt4' or 'nt5' for Windows NT or
+ Windows 2000 servers, or just 'nt' for Windows NT and higher."""
+
+ server_list = get_server_list()
+
+ for server in server_list:
+ if platform:
+ p = server["platform"]
+ if platform == "nt":
+ if (p == osver.PLATFORM_NT4 or p == osver.PLATFORM_NT5):
+ return server
+ if platform == "nt4" and p == osver.PLATFORM_NT4:
+ return server
+ if platform == "nt5" and p == osver.PLATFORM_NT5:
+ return server
+ else:
+ # No filter defined, return first in list
+ return server
+
+ return None
+
+def dict_check(sample_dict, real_dict):
+ """Check that real_dict contains all the keys present in sample_dict
+ and no extras. Also check that common keys are of them same type."""
+ tmp = real_dict.copy()
+ for key in sample_dict.keys():
+ # Check existing key and type
+ if not real_dict.has_key(key):
+ raise ValueError, "dict does not contain key '%s'" % key
+ if type(sample_dict[key]) != type(real_dict[key]):
+ raise ValueError, "dict has differing types (%s vs %s) for key " \
+ "'%s'" % (type(sample_dict[key]), type(real_dict[key]), key)
+ # Check dictionaries recursively
+ if type(sample_dict[key]) == dict:
+ dict_check(sample_dict[key], real_dict[key])
+ # Delete visited keys from copy
+ del(tmp[key])
+ # Any keys leftover are present in the real dict but not the sample
+ if len(tmp) == 0:
+ return
+ result = "dict has extra keys: "
+ for key in tmp.keys():
+ result = result + key + " "
+ raise ValueError, result
+
+if __name__ == "__main__":
+ print get_server(platform = "nt")
diff --git a/source/stf/strings.py b/source/stf/strings.py
new file mode 100755
index 00000000000..86f7acdeb47
--- /dev/null
+++ b/source/stf/strings.py
@@ -0,0 +1,151 @@
+#! /usr/bin/python
+
+# Comfychair test cases for Samba string functions.
+
+# Copyright (C) 2003 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 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
+
+# XXX: All this code assumes that the Unix character set is UTF-8,
+# which is the most common setting. I guess it would be better to
+# force it to that value while running the tests. I'm not sure of the
+# best way to do that yet.
+#
+# Note that this is NOT the case in C code until the loadparm table is
+# intialized -- the default seems to be ASCII, which rather lets Samba
+# off the hook. :-) The best way seems to be to put this in the test
+# harnesses:
+#
+# lp_load("/dev/null", True, False, False);
+#
+# -- mbp
+
+import sys, re, comfychair
+from unicodenames import *
+
+def signum(a):
+ if a < 0:
+ return -1
+ elif a > 0:
+ return +1
+ else:
+ return 0
+
+
+class PushUCS2_Tests(comfychair.TestCase):
+ """Conversion to/from UCS2"""
+ def runtest(self):
+ OE = LATIN_CAPITAL_LETTER_O_WITH_DIARESIS
+ oe = LATIN_CAPITAL_LETTER_O_WITH_DIARESIS
+ cases = ['hello',
+ 'hello world',
+ 'g' + OE + OE + 'gomobile',
+ 'g' + OE + oe + 'gomobile',
+ u'foo\u0100',
+ KATAKANA_LETTER_A * 20,
+ ]
+ for u8str in cases:
+ out, err = self.runcmd("t_push_ucs2 \"%s\"" % u8str.encode('utf-8'))
+ self.assert_equal(out, "0\n")
+
+
+class StrCaseCmp(comfychair.TestCase):
+ """String comparisons in simple ASCII"""
+ def run_strcmp(self, a, b, expect):
+ out, err = self.runcmd('t_strcmp \"%s\" \"%s\"' % (a.encode('utf-8'), b.encode('utf-8')))
+ if signum(int(out)) != expect:
+ self.fail("comparison failed:\n"
+ " a=%s\n"
+ " b=%s\n"
+ " expected=%s\n"
+ " result=%s\n" % (`a`, `b`, `expect`, `out`))
+
+ def runtest(self):
+ # A, B, strcasecmp(A, B)
+ cases = [('hello', 'hello', 0),
+ ('hello', 'goodbye', +1),
+ ('goodbye', 'hello', -1),
+ ('hell', 'hello', -1),
+ ('', '', 0),
+ ('a', '', +1),
+ ('', 'a', -1),
+ ('a', 'A', 0),
+ ('aa', 'aA', 0),
+ ('Aa', 'aa', 0),
+ ('longstring ' * 100, 'longstring ' * 100, 0),
+ ('longstring ' * 100, 'longstring ' * 100 + 'a', -1),
+ ('longstring ' * 100 + 'a', 'longstring ' * 100, +1),
+ (KATAKANA_LETTER_A, KATAKANA_LETTER_A, 0),
+ (KATAKANA_LETTER_A, 'a', 1),
+ ]
+ for a, b, expect in cases:
+ self.run_strcmp(a, b, expect)
+
+class strstr_m(comfychair.TestCase):
+ """String comparisons in simple ASCII"""
+ def run_strstr(self, a, b, expect):
+ out, err = self.runcmd('t_strstr \"%s\" \"%s\"' % (a.encode('utf-8'), b.encode('utf-8')))
+ if (out != (expect + '\n').encode('utf-8')):
+ self.fail("comparison failed:\n"
+ " a=%s\n"
+ " b=%s\n"
+ " expected=%s\n"
+ " result=%s\n" % (`a`, `b`, `expect+'\n'`, `out`))
+
+ def runtest(self):
+ # A, B, strstr_m(A, B)
+ cases = [('hello', 'hello', 'hello'),
+ ('hello', 'goodbye', '(null)'),
+ ('goodbye', 'hello', '(null)'),
+ ('hell', 'hello', '(null)'),
+ ('hello', 'hell', 'hello'),
+ ('', '', ''),
+ ('a', '', 'a'),
+ ('', 'a', '(null)'),
+ ('a', 'A', '(null)'),
+ ('aa', 'aA', '(null)'),
+ ('Aa', 'aa', '(null)'),
+ ('%v foo', '%v', '%v foo'),
+ ('foo %v foo', '%v', '%v foo'),
+ ('foo %v', '%v', '%v'),
+ ('longstring ' * 100, 'longstring ' * 99, 'longstring ' * 100),
+ ('longstring ' * 99, 'longstring ' * 100, '(null)'),
+ ('longstring a' * 99, 'longstring ' * 100 + 'a', '(null)'),
+ ('longstring ' * 100 + 'a', 'longstring ' * 100, 'longstring ' * 100 + 'a'),
+ (KATAKANA_LETTER_A, KATAKANA_LETTER_A + 'bcd', '(null)'),
+ (KATAKANA_LETTER_A + 'bcde', KATAKANA_LETTER_A + 'bcd', KATAKANA_LETTER_A + 'bcde'),
+ ('d'+KATAKANA_LETTER_A + 'bcd', KATAKANA_LETTER_A + 'bcd', KATAKANA_LETTER_A + 'bcd'),
+ ('d'+KATAKANA_LETTER_A + 'bd', KATAKANA_LETTER_A + 'bcd', '(null)'),
+
+ ('e'+KATAKANA_LETTER_A + 'bcdf', KATAKANA_LETTER_A + 'bcd', KATAKANA_LETTER_A + 'bcdf'),
+ (KATAKANA_LETTER_A, KATAKANA_LETTER_A + 'bcd', '(null)'),
+ (KATAKANA_LETTER_A*3, 'a', '(null)'),
+ ]
+ for a, b, expect in cases:
+ self.run_strstr(a, b, expect)
+
+# Define the tests exported by this module
+tests = [StrCaseCmp,
+ strstr_m,
+ PushUCS2_Tests]
+
+# Handle execution of this file as a main program
+if __name__ == '__main__':
+ comfychair.main(tests)
+
+# Local variables:
+# coding: utf-8
+# End:
diff --git a/source/stf/test.py b/source/stf/test.py
new file mode 100755
index 00000000000..fb57926cc3a
--- /dev/null
+++ b/source/stf/test.py
@@ -0,0 +1,33 @@
+#!/usr/bin/python
+
+# meta-test-case / example for comfychair. Should demonstrate
+# different kinds of failure.
+
+import comfychair
+
+class NormalTest(comfychair.TestCase):
+ def runtest(self):
+ pass
+
+class RootTest(comfychair.TestCase):
+ def setup(self):
+ self.require_root()
+
+ def runTest(self):
+ pass
+
+class GoodExecTest(comfychair.TestCase):
+ def runtest(self):
+ stdout = self.runcmd("ls -l")
+
+class BadExecTest(comfychair.TestCase):
+ def setup(self):
+ exit, stdout = self.runcmd_unchecked("spottyfoot --slobber",
+ skip_on_noexec = 1)
+
+
+tests = [NormalTest, RootTest, GoodExecTest, BadExecTest]
+
+if __name__ == '__main__':
+ comfychair.main(tests)
+
diff --git a/source/stf/unicodenames.py b/source/stf/unicodenames.py
new file mode 100644
index 00000000000..d4100cb7f90
--- /dev/null
+++ b/source/stf/unicodenames.py
@@ -0,0 +1,33 @@
+#! /usr/bin/python
+
+# Copyright (C) 2003 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 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
+
+
+"""
+Defines symbolic names for a few UNICODE characters, to make test
+source code more readable on machines that don't have all the
+necessary fonts.
+
+You can do "import *" on this file safely.
+"""
+
+LATIN_CAPITAL_LETTER_N_WITH_TILDE = u'\u004e'
+LATIN_CAPITAL_LETTER_O_WITH_DIARESIS = u'\u00d6'
+LATIN_SMALL_LETTER_O_WITH_DIARESIS = u'\u00f6'
+
+KATAKANA_LETTER_A = u'\u30a2'
diff --git a/source/tdb/.cvsignore b/source/tdb/.cvsignore
new file mode 100644
index 00000000000..66445fe2690
--- /dev/null
+++ b/source/tdb/.cvsignore
@@ -0,0 +1,11 @@
+*.po
+*.po32
+tdbbackup
+tdbdump
+tdbtest
+tdbtool
+tdbtorture
+test.db
+test.gdbm
+test.tdb
+torture.tdb
diff --git a/source/tdb/Makefile b/source/tdb/Makefile
new file mode 100644
index 00000000000..59fbb079bd0
--- /dev/null
+++ b/source/tdb/Makefile
@@ -0,0 +1,29 @@
+#
+# Makefile for tdb directory
+#
+
+CFLAGS = -DSTANDALONE -DTDB_DEBUG -g -DHAVE_MMAP=1
+CC = gcc
+
+PROGS = tdbtest tdbtool tdbtorture
+TDB_OBJ = tdb.o spinlock.o
+
+default: $(PROGS)
+
+tdbtest: tdbtest.o $(TDB_OBJ)
+ $(CC) $(CFLAGS) -o tdbtest tdbtest.o $(TDB_OBJ) -lgdbm
+
+tdbtool: tdbtool.o $(TDB_OBJ)
+ $(CC) $(CFLAGS) -o tdbtool tdbtool.o $(TDB_OBJ)
+
+tdbtorture: tdbtorture.o $(TDB_OBJ)
+ $(CC) $(CFLAGS) -o tdbtorture tdbtorture.o $(TDB_OBJ)
+
+tdbdump: tdbdump.o $(TDB_OBJ)
+ $(CC) $(CFLAGS) -o tdbdump tdbdump.o $(TDB_OBJ)
+
+tdbbackup: tdbbackup.o $(TDB_OBJ)
+ $(CC) $(CFLAGS) -o tdbbackup tdbbackup.o $(TDB_OBJ)
+
+clean:
+ rm -f $(PROGS) *.o *~ *% core test.db test.tdb test.gdbm
diff --git a/source/tdb/README b/source/tdb/README
new file mode 100644
index 00000000000..fac3eacb4db
--- /dev/null
+++ b/source/tdb/README
@@ -0,0 +1,167 @@
+tdb - a trivial database system
+tridge@linuxcare.com December 1999
+==================================
+
+This is a simple database API. It was inspired by the realisation that
+in Samba we have several ad-hoc bits of code that essentially
+implement small databases for sharing structures between parts of
+Samba. As I was about to add another I realised that a generic
+database module was called for to replace all the ad-hoc bits.
+
+I based the interface on gdbm. I couldn't use gdbm as we need to be
+able to have multiple writers to the databases at one time.
+
+Compilation
+-----------
+
+add HAVE_MMAP=1 to use mmap instead of read/write
+add TDB_DEBUG=1 for verbose debug info
+add NOLOCK=1 to disable locking code
+
+Testing
+-------
+
+Compile tdbtest.c and link with gdbm for testing. tdbtest will perform
+identical operations via tdb and gdbm then make sure the result is the
+same
+
+Also included is tdbtool, which allows simple database manipulation
+on the commandline.
+
+tdbtest and tdbtool are not built as part of Samba, but are included
+for completeness.
+
+Interface
+---------
+
+The interface is very similar to gdbm except for the following:
+
+- different open interface. The tdb_open call is more similar to a
+ traditional open()
+- no tdbm_reorganise() function
+- no tdbm_sync() function. No operations are cached in the library anyway
+- added a tdb_traverse() function for traversing the whole database
+
+A general rule for using tdb is that the caller frees any returned
+TDB_DATA structures. Just call free(p.dptr) to free a TDB_DATA
+return value called p. This is the same as gdbm.
+
+here is a full list of tdb functions with brief descriptions.
+
+
+----------------------------------------------------------------------
+TDB_CONTEXT *tdb_open(char *name, int hash_size, int tdb_flags,
+ int open_flags, mode_t mode)
+
+ open the database, creating it if necessary
+
+ The open_flags and mode are passed straight to the open call on the database
+ file. A flags value of O_WRONLY is invalid
+
+ The hash size is advisory, use zero for a default value.
+
+ return is NULL on error
+
+ possible tdb_flags are:
+ TDB_CLEAR_IF_FIRST - clear database if we are the only one with it open
+ TDB_INTERNAL - don't use a file, instaed store the data in
+ memory. The filename is ignored in this case.
+ TDB_NOLOCK - don't do any locking
+ TDB_NOMMAP - don't use mmap
+
+----------------------------------------------------------------------
+char *tdb_error(TDB_CONTEXT *tdb);
+
+ return a error string for the last tdb error
+
+----------------------------------------------------------------------
+int tdb_close(TDB_CONTEXT *tdb);
+
+ close a database
+
+----------------------------------------------------------------------
+int tdb_update(TDB_CONTEXT *tdb, TDB_DATA key, TDB_DATA dbuf);
+
+ update an entry in place - this only works if the new data size
+ is <= the old data size and the key exists.
+ on failure return -1
+
+----------------------------------------------------------------------
+TDB_DATA tdb_fetch(TDB_CONTEXT *tdb, TDB_DATA key);
+
+ fetch an entry in the database given a key
+ if the return value has a null dptr then a error occurred
+
+ caller must free the resulting data
+
+----------------------------------------------------------------------
+int tdb_exists(TDB_CONTEXT *tdb, TDB_DATA key);
+
+ check if an entry in the database exists
+
+ note that 1 is returned if the key is found and 0 is returned if not found
+ this doesn't match the conventions in the rest of this module, but is
+ compatible with gdbm
+
+----------------------------------------------------------------------
+int tdb_traverse(TDB_CONTEXT *tdb, int (*fn)(TDB_CONTEXT *tdb,
+ TDB_DATA key, TDB_DATA dbuf, void *state), void *state);
+
+ traverse the entire database - calling fn(tdb, key, data, state) on each
+ element.
+
+ return -1 on error or the record count traversed
+
+ if fn is NULL then it is not called
+
+ a non-zero return value from fn() indicates that the traversal should stop
+
+----------------------------------------------------------------------
+TDB_DATA tdb_firstkey(TDB_CONTEXT *tdb);
+
+ find the first entry in the database and return its key
+
+ the caller must free the returned data
+
+----------------------------------------------------------------------
+TDB_DATA tdb_nextkey(TDB_CONTEXT *tdb, TDB_DATA key);
+
+ find the next entry in the database, returning its key
+
+ the caller must free the returned data
+
+----------------------------------------------------------------------
+int tdb_delete(TDB_CONTEXT *tdb, TDB_DATA key);
+
+ delete an entry in the database given a key
+
+----------------------------------------------------------------------
+int tdb_store(TDB_CONTEXT *tdb, TDB_DATA key, TDB_DATA dbuf, int flag);
+
+ store an element in the database, replacing any existing element
+ with the same key
+
+ If flag==TDB_INSERT then don't overwrite an existing entry
+ If flag==TDB_MODIFY then don't create a new entry
+
+ return 0 on success, -1 on failure
+
+----------------------------------------------------------------------
+int tdb_writelock(TDB_CONTEXT *tdb);
+
+ lock the database. If we already have it locked then don't do anything
+
+----------------------------------------------------------------------
+int tdb_writeunlock(TDB_CONTEXT *tdb);
+ unlock the database
+
+----------------------------------------------------------------------
+int tdb_lockchain(TDB_CONTEXT *tdb, TDB_DATA key);
+
+ lock one hash chain. This is meant to be used to reduce locking
+ contention - it cannot guarantee how many records will be locked
+
+----------------------------------------------------------------------
+int tdb_unlockchain(TDB_CONTEXT *tdb, TDB_DATA key);
+
+ unlock one hash chain
diff --git a/source/tdb/spinlock.c b/source/tdb/spinlock.c
new file mode 100644
index 00000000000..24c4371decc
--- /dev/null
+++ b/source/tdb/spinlock.c
@@ -0,0 +1,481 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ trivial database library
+
+ Copyright (C) Anton Blanchard 2001
+
+ ** NOTE! The following LGPL license applies to the tdb
+ ** 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 2 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, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+*/
+#if HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#if STANDALONE
+#include <stdlib.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <string.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <sys/stat.h>
+#include <time.h>
+#include <signal.h>
+#include "tdb.h"
+#include "spinlock.h"
+
+#define DEBUG
+#else
+#include "includes.h"
+#endif
+
+#ifdef USE_SPINLOCKS
+
+/*
+ * ARCH SPECIFIC
+ */
+
+#if defined(SPARC_SPINLOCKS)
+
+static inline int __spin_trylock(spinlock_t *lock)
+{
+ unsigned int result;
+
+ asm volatile("ldstub [%1], %0"
+ : "=r" (result)
+ : "r" (lock)
+ : "memory");
+
+ return (result == 0) ? 0 : EBUSY;
+}
+
+static inline void __spin_unlock(spinlock_t *lock)
+{
+ asm volatile("":::"memory");
+ *lock = 0;
+}
+
+static inline void __spin_lock_init(spinlock_t *lock)
+{
+ *lock = 0;
+}
+
+static inline int __spin_is_locked(spinlock_t *lock)
+{
+ return (*lock != 0);
+}
+
+#elif defined(POWERPC_SPINLOCKS)
+
+static inline int __spin_trylock(spinlock_t *lock)
+{
+ unsigned int result;
+
+ __asm__ __volatile__(
+"1: lwarx %0,0,%1\n\
+ cmpwi 0,%0,0\n\
+ li %0,0\n\
+ bne- 2f\n\
+ li %0,1\n\
+ stwcx. %0,0,%1\n\
+ bne- 1b\n\
+ isync\n\
+2:" : "=&r"(result)
+ : "r"(lock)
+ : "cr0", "memory");
+
+ return (result == 1) ? 0 : EBUSY;
+}
+
+static inline void __spin_unlock(spinlock_t *lock)
+{
+ asm volatile("eieio":::"memory");
+ *lock = 0;
+}
+
+static inline void __spin_lock_init(spinlock_t *lock)
+{
+ *lock = 0;
+}
+
+static inline int __spin_is_locked(spinlock_t *lock)
+{
+ return (*lock != 0);
+}
+
+#elif defined(INTEL_SPINLOCKS)
+
+static inline int __spin_trylock(spinlock_t *lock)
+{
+ int oldval;
+
+ asm volatile("xchgl %0,%1"
+ : "=r" (oldval), "=m" (*lock)
+ : "0" (0)
+ : "memory");
+
+ return oldval > 0 ? 0 : EBUSY;
+}
+
+static inline void __spin_unlock(spinlock_t *lock)
+{
+ asm volatile("":::"memory");
+ *lock = 1;
+}
+
+static inline void __spin_lock_init(spinlock_t *lock)
+{
+ *lock = 1;
+}
+
+static inline int __spin_is_locked(spinlock_t *lock)
+{
+ return (*lock != 1);
+}
+
+#elif defined(MIPS_SPINLOCKS) && defined(sgi) && (_COMPILER_VERSION >= 730)
+
+/* Implement spinlocks on IRIX using the MIPSPro atomic fetch operations. See
+ * sync(3) for the details of the intrinsic operations.
+ *
+ * "sgi" and "_COMPILER_VERSION" are always defined by MIPSPro.
+ */
+
+#if defined(STANDALONE)
+
+/* MIPSPro 7.3 has "__inline" as an extension, but not "inline. */
+#define inline __inline
+
+#endif /* STANDALONE */
+
+/* Returns 0 if the lock is acquired, EBUSY otherwise. */
+static inline int __spin_trylock(spinlock_t *lock)
+{
+ unsigned int val;
+ val = __lock_test_and_set(lock, 1);
+ return val == 0 ? 0 : EBUSY;
+}
+
+static inline void __spin_unlock(spinlock_t *lock)
+{
+ __lock_release(lock);
+}
+
+static inline void __spin_lock_init(spinlock_t *lock)
+{
+ __lock_release(lock);
+}
+
+/* Returns 1 if the lock is held, 0 otherwise. */
+static inline int __spin_is_locked(spinlock_t *lock)
+{
+ unsigned int val;
+ val = __add_and_fetch(lock, 0);
+ return val;
+}
+
+#elif defined(MIPS_SPINLOCKS)
+
+static inline unsigned int load_linked(unsigned long addr)
+{
+ unsigned int res;
+
+ __asm__ __volatile__("ll\t%0,(%1)"
+ : "=r" (res)
+ : "r" (addr));
+
+ return res;
+}
+
+static inline unsigned int store_conditional(unsigned long addr, unsigned int value)
+{
+ unsigned int res;
+
+ __asm__ __volatile__("sc\t%0,(%2)"
+ : "=r" (res)
+ : "0" (value), "r" (addr));
+ return res;
+}
+
+static inline int __spin_trylock(spinlock_t *lock)
+{
+ unsigned int mw;
+
+ do {
+ mw = load_linked(lock);
+ if (mw)
+ return EBUSY;
+ } while (!store_conditional(lock, 1));
+
+ asm volatile("":::"memory");
+
+ return 0;
+}
+
+static inline void __spin_unlock(spinlock_t *lock)
+{
+ asm volatile("":::"memory");
+ *lock = 0;
+}
+
+static inline void __spin_lock_init(spinlock_t *lock)
+{
+ *lock = 0;
+}
+
+static inline int __spin_is_locked(spinlock_t *lock)
+{
+ return (*lock != 0);
+}
+
+#else
+#error Need to implement spinlock code in spinlock.c
+#endif
+
+/*
+ * OS SPECIFIC
+ */
+
+static void yield_cpu(void)
+{
+ struct timespec tm;
+
+#ifdef USE_SCHED_YIELD
+ sched_yield();
+#else
+ /* Linux will busy loop for delays < 2ms on real time tasks */
+ tm.tv_sec = 0;
+ tm.tv_nsec = 2000000L + 1;
+ nanosleep(&tm, NULL);
+#endif
+}
+
+static int this_is_smp(void)
+{
+#if defined(HAVE_SYSCONF) && defined(SYSCONF_SC_NPROC_ONLN)
+ return (sysconf(_SC_NPROC_ONLN) > 1) ? 1 : 0;
+#else
+ return 0;
+#endif
+}
+
+/*
+ * GENERIC
+ */
+
+static int smp_machine = 0;
+
+static inline void __spin_lock(spinlock_t *lock)
+{
+ int ntries = 0;
+
+ while(__spin_trylock(lock)) {
+ while(__spin_is_locked(lock)) {
+ if (smp_machine && ntries++ < MAX_BUSY_LOOPS)
+ continue;
+ yield_cpu();
+ }
+ }
+}
+
+static void __read_lock(tdb_rwlock_t *rwlock)
+{
+ int ntries = 0;
+
+ while(1) {
+ __spin_lock(&rwlock->lock);
+
+ if (!(rwlock->count & RWLOCK_BIAS)) {
+ rwlock->count++;
+ __spin_unlock(&rwlock->lock);
+ return;
+ }
+
+ __spin_unlock(&rwlock->lock);
+
+ while(rwlock->count & RWLOCK_BIAS) {
+ if (smp_machine && ntries++ < MAX_BUSY_LOOPS)
+ continue;
+ yield_cpu();
+ }
+ }
+}
+
+static void __write_lock(tdb_rwlock_t *rwlock)
+{
+ int ntries = 0;
+
+ while(1) {
+ __spin_lock(&rwlock->lock);
+
+ if (rwlock->count == 0) {
+ rwlock->count |= RWLOCK_BIAS;
+ __spin_unlock(&rwlock->lock);
+ return;
+ }
+
+ __spin_unlock(&rwlock->lock);
+
+ while(rwlock->count != 0) {
+ if (smp_machine && ntries++ < MAX_BUSY_LOOPS)
+ continue;
+ yield_cpu();
+ }
+ }
+}
+
+static void __write_unlock(tdb_rwlock_t *rwlock)
+{
+ __spin_lock(&rwlock->lock);
+
+#ifdef DEBUG
+ if (!(rwlock->count & RWLOCK_BIAS))
+ fprintf(stderr, "bug: write_unlock\n");
+#endif
+
+ rwlock->count &= ~RWLOCK_BIAS;
+ __spin_unlock(&rwlock->lock);
+}
+
+static void __read_unlock(tdb_rwlock_t *rwlock)
+{
+ __spin_lock(&rwlock->lock);
+
+#ifdef DEBUG
+ if (!rwlock->count)
+ fprintf(stderr, "bug: read_unlock\n");
+
+ if (rwlock->count & RWLOCK_BIAS)
+ fprintf(stderr, "bug: read_unlock\n");
+#endif
+
+ rwlock->count--;
+ __spin_unlock(&rwlock->lock);
+}
+
+/* TDB SPECIFIC */
+
+/* lock a list in the database. list -1 is the alloc list */
+int tdb_spinlock(TDB_CONTEXT *tdb, int list, int rw_type)
+{
+ tdb_rwlock_t *rwlocks;
+
+ if (!tdb->map_ptr) return -1;
+ rwlocks = (tdb_rwlock_t *)((char *)tdb->map_ptr + tdb->header.rwlocks);
+
+ switch(rw_type) {
+ case F_RDLCK:
+ __read_lock(&rwlocks[list+1]);
+ break;
+
+ case F_WRLCK:
+ __write_lock(&rwlocks[list+1]);
+ break;
+
+ default:
+ return TDB_ERRCODE(TDB_ERR_LOCK, -1);
+ }
+ return 0;
+}
+
+/* unlock the database. */
+int tdb_spinunlock(TDB_CONTEXT *tdb, int list, int rw_type)
+{
+ tdb_rwlock_t *rwlocks;
+
+ if (!tdb->map_ptr) return -1;
+ rwlocks = (tdb_rwlock_t *)((char *)tdb->map_ptr + tdb->header.rwlocks);
+
+ switch(rw_type) {
+ case F_RDLCK:
+ __read_unlock(&rwlocks[list+1]);
+ break;
+
+ case F_WRLCK:
+ __write_unlock(&rwlocks[list+1]);
+ break;
+
+ default:
+ return TDB_ERRCODE(TDB_ERR_LOCK, -1);
+ }
+
+ return 0;
+}
+
+int tdb_create_rwlocks(int fd, unsigned int hash_size)
+{
+ unsigned size, i;
+ tdb_rwlock_t *rwlocks;
+
+ size = TDB_SPINLOCK_SIZE(hash_size);
+ rwlocks = malloc(size);
+ if (!rwlocks)
+ return -1;
+
+ for(i = 0; i < hash_size+1; i++) {
+ __spin_lock_init(&rwlocks[i].lock);
+ rwlocks[i].count = 0;
+ }
+
+ /* Write it out (appending to end) */
+ if (write(fd, rwlocks, size) != size) {
+ free(rwlocks);
+ return -1;
+ }
+ smp_machine = this_is_smp();
+ free(rwlocks);
+ return 0;
+}
+
+int tdb_clear_spinlocks(TDB_CONTEXT *tdb)
+{
+ tdb_rwlock_t *rwlocks;
+ unsigned i;
+
+ if (tdb->header.rwlocks == 0) return 0;
+ if (!tdb->map_ptr) return -1;
+
+ /* We're mmapped here */
+ rwlocks = (tdb_rwlock_t *)((char *)tdb->map_ptr + tdb->header.rwlocks);
+ for(i = 0; i < tdb->header.hash_size+1; i++) {
+ __spin_lock_init(&rwlocks[i].lock);
+ rwlocks[i].count = 0;
+ }
+ return 0;
+}
+#else
+int tdb_create_rwlocks(int fd, unsigned int hash_size) { return 0; }
+int tdb_spinlock(TDB_CONTEXT *tdb, int list, int rw_type) { return -1; }
+int tdb_spinunlock(TDB_CONTEXT *tdb, int list, int rw_type) { return -1; }
+
+/* Non-spinlock version: remove spinlock pointer */
+int tdb_clear_spinlocks(TDB_CONTEXT *tdb)
+{
+ tdb_off off = (tdb_off)((char *)&tdb->header.rwlocks
+ - (char *)&tdb->header);
+
+ tdb->header.rwlocks = 0;
+ if (lseek(tdb->fd, off, SEEK_SET) != off
+ || write(tdb->fd, (void *)&tdb->header.rwlocks,
+ sizeof(tdb->header.rwlocks))
+ != sizeof(tdb->header.rwlocks))
+ return -1;
+ return 0;
+}
+#endif
diff --git a/source/tdb/spinlock.h b/source/tdb/spinlock.h
new file mode 100644
index 00000000000..967fe37457f
--- /dev/null
+++ b/source/tdb/spinlock.h
@@ -0,0 +1,59 @@
+#ifndef __SPINLOCK_H__
+#define __SPINLOCK_H__
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "tdb.h"
+
+#ifdef USE_SPINLOCKS
+
+#define RWLOCK_BIAS 0x1000UL
+
+/* OS SPECIFIC */
+#define MAX_BUSY_LOOPS 1000
+#undef USE_SCHED_YIELD
+
+/* ARCH SPECIFIC */
+/* We should make sure these are padded to a cache line */
+#if defined(SPARC_SPINLOCKS)
+typedef volatile char spinlock_t;
+#elif defined(POWERPC_SPINLOCKS)
+typedef volatile unsigned long spinlock_t;
+#elif defined(INTEL_SPINLOCKS)
+typedef volatile int spinlock_t;
+#elif defined(MIPS_SPINLOCKS)
+typedef volatile unsigned long spinlock_t;
+#else
+#error Need to implement spinlock code in spinlock.h
+#endif
+
+typedef struct {
+ spinlock_t lock;
+ volatile int count;
+} tdb_rwlock_t;
+
+int tdb_spinlock(TDB_CONTEXT *tdb, int list, int rw_type);
+int tdb_spinunlock(TDB_CONTEXT *tdb, int list, int rw_type);
+int tdb_create_rwlocks(int fd, unsigned int hash_size);
+int tdb_clear_spinlocks(TDB_CONTEXT *tdb);
+
+#define TDB_SPINLOCK_SIZE(hash_size) (((hash_size) + 1) * sizeof(tdb_rwlock_t))
+
+#else /* !USE_SPINLOCKS */
+#if 0
+#define tdb_create_rwlocks(fd, hash_size) 0
+#define tdb_spinlock(tdb, list, rw_type) (-1)
+#define tdb_spinunlock(tdb, list, rw_type) (-1)
+#else
+int tdb_spinlock(TDB_CONTEXT *tdb, int list, int rw_type);
+int tdb_spinunlock(TDB_CONTEXT *tdb, int list, int rw_type);
+int tdb_create_rwlocks(int fd, unsigned int hash_size);
+#endif
+int tdb_clear_spinlocks(TDB_CONTEXT *tdb);
+#define TDB_SPINLOCK_SIZE(hash_size) 0
+
+#endif
+
+#endif
diff --git a/source/tdb/tdb.c b/source/tdb/tdb.c
new file mode 100644
index 00000000000..cda9fc24750
--- /dev/null
+++ b/source/tdb/tdb.c
@@ -0,0 +1,2090 @@
+ /*
+ Unix SMB/CIFS implementation.
+
+ trivial database library
+
+ Copyright (C) Andrew Tridgell 1999-2004
+ Copyright (C) Paul `Rusty' Russell 2000
+ Copyright (C) Jeremy Allison 2000-2003
+
+ ** NOTE! The following LGPL license applies to the tdb
+ ** 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 2 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, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+*/
+
+
+/* NOTE: If you use tdbs under valgrind, and in particular if you run
+ * tdbtorture, you may get spurious "uninitialized value" warnings. I
+ * think this is because valgrind doesn't understand that the mmap'd
+ * area may be written to by other processes. Memory can, from the
+ * point of view of the grinded process, spontaneously become
+ * initialized.
+ *
+ * I can think of a few solutions. [mbp 20030311]
+ *
+ * 1 - Write suppressions for Valgrind so that it doesn't complain
+ * about this. Probably the most reasonable but people need to
+ * remember to use them.
+ *
+ * 2 - Use IO not mmap when running under valgrind. Not so nice.
+ *
+ * 3 - Use the special valgrind macros to mark memory as valid at the
+ * right time. Probably too hard -- the process just doesn't know.
+ */
+
+#ifdef STANDALONE
+#if HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <string.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <signal.h>
+#include "tdb.h"
+#include "spinlock.h"
+#else
+#include "includes.h"
+#endif
+
+#define TDB_MAGIC_FOOD "TDB file\n"
+#define TDB_VERSION (0x26011967 + 6)
+#define TDB_MAGIC (0x26011999U)
+#define TDB_FREE_MAGIC (~TDB_MAGIC)
+#define TDB_DEAD_MAGIC (0xFEE1DEAD)
+#define TDB_ALIGNMENT 4
+#define MIN_REC_SIZE (2*sizeof(struct list_struct) + TDB_ALIGNMENT)
+#define DEFAULT_HASH_SIZE 131
+#define TDB_PAGE_SIZE 0x2000
+#define FREELIST_TOP (sizeof(struct tdb_header))
+#define TDB_ALIGN(x,a) (((x) + (a)-1) & ~((a)-1))
+#define TDB_BYTEREV(x) (((((x)&0xff)<<24)|((x)&0xFF00)<<8)|(((x)>>8)&0xFF00)|((x)>>24))
+#define TDB_DEAD(r) ((r)->magic == TDB_DEAD_MAGIC)
+#define TDB_BAD_MAGIC(r) ((r)->magic != TDB_MAGIC && !TDB_DEAD(r))
+#define TDB_HASH_TOP(hash) (FREELIST_TOP + (BUCKET(hash)+1)*sizeof(tdb_off))
+#define TDB_DATA_START(hash_size) (TDB_HASH_TOP(hash_size-1) + TDB_SPINLOCK_SIZE(hash_size))
+
+
+/* NB assumes there is a local variable called "tdb" that is the
+ * current context, also takes doubly-parenthesized print-style
+ * argument. */
+#define TDB_LOG(x) (tdb->log_fn?((tdb->log_fn x),0) : 0)
+
+/* lock offsets */
+#define GLOBAL_LOCK 0
+#define ACTIVE_LOCK 4
+
+#ifndef MAP_FILE
+#define MAP_FILE 0
+#endif
+
+#ifndef MAP_FAILED
+#define MAP_FAILED ((void *)-1)
+#endif
+
+/* free memory if the pointer is valid and zero the pointer */
+#ifndef SAFE_FREE
+#define SAFE_FREE(x) do { if ((x) != NULL) {free((x)); (x)=NULL;} } while(0)
+#endif
+
+#define BUCKET(hash) ((hash) % tdb->header.hash_size)
+TDB_DATA tdb_null;
+
+/* all contexts, to ensure no double-opens (fcntl locks don't nest!) */
+static TDB_CONTEXT *tdbs = NULL;
+
+static int tdb_munmap(TDB_CONTEXT *tdb)
+{
+ if (tdb->flags & TDB_INTERNAL)
+ return 0;
+
+#ifdef HAVE_MMAP
+ if (tdb->map_ptr) {
+ int ret = munmap(tdb->map_ptr, tdb->map_size);
+ if (ret != 0)
+ return ret;
+ }
+#endif
+ tdb->map_ptr = NULL;
+ return 0;
+}
+
+static void tdb_mmap(TDB_CONTEXT *tdb)
+{
+ if (tdb->flags & TDB_INTERNAL)
+ return;
+
+#ifdef HAVE_MMAP
+ if (!(tdb->flags & TDB_NOMMAP)) {
+ tdb->map_ptr = mmap(NULL, tdb->map_size,
+ PROT_READ|(tdb->read_only? 0:PROT_WRITE),
+ MAP_SHARED|MAP_FILE, tdb->fd, 0);
+
+ /*
+ * NB. When mmap fails it returns MAP_FAILED *NOT* NULL !!!!
+ */
+
+ if (tdb->map_ptr == MAP_FAILED) {
+ tdb->map_ptr = NULL;
+ TDB_LOG((tdb, 2, "tdb_mmap failed for size %d (%s)\n",
+ tdb->map_size, strerror(errno)));
+ }
+ } else {
+ tdb->map_ptr = NULL;
+ }
+#else
+ tdb->map_ptr = NULL;
+#endif
+}
+
+/* Endian conversion: we only ever deal with 4 byte quantities */
+static void *convert(void *buf, u32 size)
+{
+ u32 i, *p = buf;
+ for (i = 0; i < size / 4; i++)
+ p[i] = TDB_BYTEREV(p[i]);
+ return buf;
+}
+#define DOCONV() (tdb->flags & TDB_CONVERT)
+#define CONVERT(x) (DOCONV() ? convert(&x, sizeof(x)) : &x)
+
+/* the body of the database is made of one list_struct for the free space
+ plus a separate data list for each hash value */
+struct list_struct {
+ tdb_off next; /* offset of the next record in the list */
+ tdb_len rec_len; /* total byte length of record */
+ tdb_len key_len; /* byte length of key */
+ tdb_len data_len; /* byte length of data */
+ u32 full_hash; /* the full 32 bit hash of the key */
+ u32 magic; /* try to catch errors */
+ /* the following union is implied:
+ union {
+ char record[rec_len];
+ struct {
+ char key[key_len];
+ char data[data_len];
+ }
+ u32 totalsize; (tailer)
+ }
+ */
+};
+
+/***************************************************************
+ Allow a caller to set a "alarm" flag that tdb can check to abort
+ a blocking lock on SIGALRM.
+***************************************************************/
+
+static sig_atomic_t *palarm_fired;
+
+void tdb_set_lock_alarm(sig_atomic_t *palarm)
+{
+ palarm_fired = palarm;
+}
+
+/* a byte range locking function - return 0 on success
+ this functions locks/unlocks 1 byte at the specified offset.
+
+ On error, errno is also set so that errors are passed back properly
+ through tdb_open(). */
+static int tdb_brlock(TDB_CONTEXT *tdb, tdb_off offset,
+ int rw_type, int lck_type, int probe)
+{
+ struct flock fl;
+ int ret;
+
+ if (tdb->flags & TDB_NOLOCK)
+ return 0;
+ if ((rw_type == F_WRLCK) && (tdb->read_only)) {
+ errno = EACCES;
+ return -1;
+ }
+
+ fl.l_type = rw_type;
+ fl.l_whence = SEEK_SET;
+ fl.l_start = offset;
+ fl.l_len = 1;
+ fl.l_pid = 0;
+
+ do {
+ ret = fcntl(tdb->fd,lck_type,&fl);
+ if (ret == -1 && errno == EINTR && palarm_fired && *palarm_fired)
+ break;
+ } while (ret == -1 && errno == EINTR);
+
+ if (ret == -1) {
+ if (!probe && lck_type != F_SETLK) {
+ /* Ensure error code is set for log fun to examine. */
+ if (errno == EINTR && palarm_fired && *palarm_fired)
+ tdb->ecode = TDB_ERR_LOCK_TIMEOUT;
+ else
+ tdb->ecode = TDB_ERR_LOCK;
+ TDB_LOG((tdb, 5,"tdb_brlock failed (fd=%d) at offset %d rw_type=%d lck_type=%d\n",
+ tdb->fd, offset, rw_type, lck_type));
+ }
+ /* Was it an alarm timeout ? */
+ if (errno == EINTR && palarm_fired && *palarm_fired) {
+ TDB_LOG((tdb, 5, "tdb_brlock timed out (fd=%d) at offset %d rw_type=%d lck_type=%d\n",
+ tdb->fd, offset, rw_type, lck_type));
+ return TDB_ERRCODE(TDB_ERR_LOCK_TIMEOUT, -1);
+ }
+ /* Otherwise - generic lock error. errno set by fcntl.
+ * EAGAIN is an expected return from non-blocking
+ * locks. */
+ if (errno != EAGAIN) {
+ TDB_LOG((tdb, 5, "tdb_brlock failed (fd=%d) at offset %d rw_type=%d lck_type=%d: %s\n",
+ tdb->fd, offset, rw_type, lck_type,
+ strerror(errno)));
+ }
+ return TDB_ERRCODE(TDB_ERR_LOCK, -1);
+ }
+ return 0;
+}
+
+/* lock a list in the database. list -1 is the alloc list */
+static int tdb_lock(TDB_CONTEXT *tdb, int list, int ltype)
+{
+ if (list < -1 || list >= (int)tdb->header.hash_size) {
+ TDB_LOG((tdb, 0,"tdb_lock: invalid list %d for ltype=%d\n",
+ list, ltype));
+ return -1;
+ }
+ if (tdb->flags & TDB_NOLOCK)
+ return 0;
+
+ /* Since fcntl locks don't nest, we do a lock for the first one,
+ and simply bump the count for future ones */
+ if (tdb->locked[list+1].count == 0) {
+ if (!tdb->read_only && tdb->header.rwlocks) {
+ if (tdb_spinlock(tdb, list, ltype)) {
+ TDB_LOG((tdb, 0, "tdb_lock spinlock failed on list ltype=%d\n",
+ list, ltype));
+ return -1;
+ }
+ } else if (tdb_brlock(tdb,FREELIST_TOP+4*list,ltype,F_SETLKW, 0)) {
+ TDB_LOG((tdb, 0,"tdb_lock failed on list %d ltype=%d (%s)\n",
+ list, ltype, strerror(errno)));
+ return -1;
+ }
+ tdb->locked[list+1].ltype = ltype;
+ }
+ tdb->locked[list+1].count++;
+ return 0;
+}
+
+/* unlock the database: returns void because it's too late for errors. */
+ /* changed to return int it may be interesting to know there
+ has been an error --simo */
+static int tdb_unlock(TDB_CONTEXT *tdb, int list, int ltype)
+{
+ int ret = -1;
+
+ if (tdb->flags & TDB_NOLOCK)
+ return 0;
+
+ /* Sanity checks */
+ if (list < -1 || list >= (int)tdb->header.hash_size) {
+ TDB_LOG((tdb, 0, "tdb_unlock: list %d invalid (%d)\n", list, tdb->header.hash_size));
+ return ret;
+ }
+
+ if (tdb->locked[list+1].count==0) {
+ TDB_LOG((tdb, 0, "tdb_unlock: count is 0\n"));
+ return ret;
+ }
+
+ if (tdb->locked[list+1].count == 1) {
+ /* Down to last nested lock: unlock underneath */
+ if (!tdb->read_only && tdb->header.rwlocks) {
+ ret = tdb_spinunlock(tdb, list, ltype);
+ } else {
+ ret = tdb_brlock(tdb, FREELIST_TOP+4*list, F_UNLCK, F_SETLKW, 0);
+ }
+ } else {
+ ret = 0;
+ }
+ tdb->locked[list+1].count--;
+
+ if (ret)
+ TDB_LOG((tdb, 0,"tdb_unlock: An error occurred unlocking!\n"));
+ return ret;
+}
+
+/* This is based on the hash algorithm from gdbm */
+static u32 tdb_hash(TDB_DATA *key)
+{
+ u32 value; /* Used to compute the hash value. */
+ u32 i; /* Used to cycle through random values. */
+
+ /* Set the initial value from the key size. */
+ for (value = 0x238F13AF * key->dsize, i=0; i < key->dsize; i++)
+ value = (value + (key->dptr[i] << (i*5 % 24)));
+
+ return (1103515243 * value + 12345);
+}
+
+/* check for an out of bounds access - if it is out of bounds then
+ see if the database has been expanded by someone else and expand
+ if necessary
+ note that "len" is the minimum length needed for the db
+*/
+static int tdb_oob(TDB_CONTEXT *tdb, tdb_off len, int probe)
+{
+ struct stat st;
+ if (len <= tdb->map_size)
+ return 0;
+ if (tdb->flags & TDB_INTERNAL) {
+ if (!probe) {
+ /* Ensure ecode is set for log fn. */
+ tdb->ecode = TDB_ERR_IO;
+ TDB_LOG((tdb, 0,"tdb_oob len %d beyond internal malloc size %d\n",
+ (int)len, (int)tdb->map_size));
+ }
+ return TDB_ERRCODE(TDB_ERR_IO, -1);
+ }
+
+ if (fstat(tdb->fd, &st) == -1)
+ return TDB_ERRCODE(TDB_ERR_IO, -1);
+
+ if (st.st_size < (size_t)len) {
+ if (!probe) {
+ /* Ensure ecode is set for log fn. */
+ tdb->ecode = TDB_ERR_IO;
+ TDB_LOG((tdb, 0,"tdb_oob len %d beyond eof at %d\n",
+ (int)len, (int)st.st_size));
+ }
+ return TDB_ERRCODE(TDB_ERR_IO, -1);
+ }
+
+ /* Unmap, update size, remap */
+ if (tdb_munmap(tdb) == -1)
+ return TDB_ERRCODE(TDB_ERR_IO, -1);
+ tdb->map_size = st.st_size;
+ tdb_mmap(tdb);
+ return 0;
+}
+
+/* write a lump of data at a specified offset */
+static int tdb_write(TDB_CONTEXT *tdb, tdb_off off, void *buf, tdb_len len)
+{
+ if (tdb_oob(tdb, off + len, 0) != 0)
+ return -1;
+
+ if (tdb->map_ptr)
+ memcpy(off + (char *)tdb->map_ptr, buf, len);
+#ifdef HAVE_PWRITE
+ else if (pwrite(tdb->fd, buf, len, off) != (ssize_t)len) {
+#else
+ else if (lseek(tdb->fd, off, SEEK_SET) != off
+ || write(tdb->fd, buf, len) != (ssize_t)len) {
+#endif
+ /* Ensure ecode is set for log fn. */
+ tdb->ecode = TDB_ERR_IO;
+ TDB_LOG((tdb, 0,"tdb_write failed at %d len=%d (%s)\n",
+ off, len, strerror(errno)));
+ return TDB_ERRCODE(TDB_ERR_IO, -1);
+ }
+ return 0;
+}
+
+/* read a lump of data at a specified offset, maybe convert */
+static int tdb_read(TDB_CONTEXT *tdb,tdb_off off,void *buf,tdb_len len,int cv)
+{
+ if (tdb_oob(tdb, off + len, 0) != 0)
+ return -1;
+
+ if (tdb->map_ptr)
+ memcpy(buf, off + (char *)tdb->map_ptr, len);
+#ifdef HAVE_PREAD
+ else if (pread(tdb->fd, buf, len, off) != (ssize_t)len) {
+#else
+ else if (lseek(tdb->fd, off, SEEK_SET) != off
+ || read(tdb->fd, buf, len) != (ssize_t)len) {
+#endif
+ /* Ensure ecode is set for log fn. */
+ tdb->ecode = TDB_ERR_IO;
+ TDB_LOG((tdb, 0,"tdb_read failed at %d len=%d (%s)\n",
+ off, len, strerror(errno)));
+ return TDB_ERRCODE(TDB_ERR_IO, -1);
+ }
+ if (cv)
+ convert(buf, len);
+ return 0;
+}
+
+/* read a lump of data, allocating the space for it */
+static char *tdb_alloc_read(TDB_CONTEXT *tdb, tdb_off offset, tdb_len len)
+{
+ char *buf;
+
+ if (!(buf = malloc(len))) {
+ /* Ensure ecode is set for log fn. */
+ tdb->ecode = TDB_ERR_OOM;
+ TDB_LOG((tdb, 0,"tdb_alloc_read malloc failed len=%d (%s)\n",
+ len, strerror(errno)));
+ return TDB_ERRCODE(TDB_ERR_OOM, buf);
+ }
+ if (tdb_read(tdb, offset, buf, len, 0) == -1) {
+ SAFE_FREE(buf);
+ return NULL;
+ }
+ return buf;
+}
+
+/* read/write a tdb_off */
+static int ofs_read(TDB_CONTEXT *tdb, tdb_off offset, tdb_off *d)
+{
+ return tdb_read(tdb, offset, (char*)d, sizeof(*d), DOCONV());
+}
+static int ofs_write(TDB_CONTEXT *tdb, tdb_off offset, tdb_off *d)
+{
+ tdb_off off = *d;
+ return tdb_write(tdb, offset, CONVERT(off), sizeof(*d));
+}
+
+/* read/write a record */
+static int rec_read(TDB_CONTEXT *tdb, tdb_off offset, struct list_struct *rec)
+{
+ if (tdb_read(tdb, offset, rec, sizeof(*rec),DOCONV()) == -1)
+ return -1;
+ if (TDB_BAD_MAGIC(rec)) {
+ /* Ensure ecode is set for log fn. */
+ tdb->ecode = TDB_ERR_CORRUPT;
+ TDB_LOG((tdb, 0,"rec_read bad magic 0x%x at offset=%d\n", rec->magic, offset));
+ return TDB_ERRCODE(TDB_ERR_CORRUPT, -1);
+ }
+ return tdb_oob(tdb, rec->next+sizeof(*rec), 0);
+}
+static int rec_write(TDB_CONTEXT *tdb, tdb_off offset, struct list_struct *rec)
+{
+ struct list_struct r = *rec;
+ return tdb_write(tdb, offset, CONVERT(r), sizeof(r));
+}
+
+/* read a freelist record and check for simple errors */
+static int rec_free_read(TDB_CONTEXT *tdb, tdb_off off, struct list_struct *rec)
+{
+ if (tdb_read(tdb, off, rec, sizeof(*rec),DOCONV()) == -1)
+ return -1;
+
+ if (rec->magic == TDB_MAGIC) {
+ /* this happens when a app is showdown while deleting a record - we should
+ not completely fail when this happens */
+ TDB_LOG((tdb, 0,"rec_free_read non-free magic at offset=%d - fixing\n",
+ rec->magic, off));
+ rec->magic = TDB_FREE_MAGIC;
+ if (tdb_write(tdb, off, rec, sizeof(*rec)) == -1)
+ return -1;
+ }
+
+ if (rec->magic != TDB_FREE_MAGIC) {
+ /* Ensure ecode is set for log fn. */
+ tdb->ecode = TDB_ERR_CORRUPT;
+ TDB_LOG((tdb, 0,"rec_free_read bad magic 0x%x at offset=%d\n",
+ rec->magic, off));
+ return TDB_ERRCODE(TDB_ERR_CORRUPT, -1);
+ }
+ if (tdb_oob(tdb, rec->next+sizeof(*rec), 0) != 0)
+ return -1;
+ return 0;
+}
+
+/* update a record tailer (must hold allocation lock) */
+static int update_tailer(TDB_CONTEXT *tdb, tdb_off offset,
+ const struct list_struct *rec)
+{
+ tdb_off totalsize;
+
+ /* Offset of tailer from record header */
+ totalsize = sizeof(*rec) + rec->rec_len;
+ return ofs_write(tdb, offset + totalsize - sizeof(tdb_off),
+ &totalsize);
+}
+
+static tdb_off tdb_dump_record(TDB_CONTEXT *tdb, tdb_off offset)
+{
+ struct list_struct rec;
+ tdb_off tailer_ofs, tailer;
+
+ if (tdb_read(tdb, offset, (char *)&rec, sizeof(rec), DOCONV()) == -1) {
+ printf("ERROR: failed to read record at %u\n", offset);
+ return 0;
+ }
+
+ printf(" rec: offset=%u next=%d rec_len=%d key_len=%d data_len=%d full_hash=0x%x magic=0x%x\n",
+ offset, rec.next, rec.rec_len, rec.key_len, rec.data_len, rec.full_hash, rec.magic);
+
+ tailer_ofs = offset + sizeof(rec) + rec.rec_len - sizeof(tdb_off);
+ if (ofs_read(tdb, tailer_ofs, &tailer) == -1) {
+ printf("ERROR: failed to read tailer at %u\n", tailer_ofs);
+ return rec.next;
+ }
+
+ if (tailer != rec.rec_len + sizeof(rec)) {
+ printf("ERROR: tailer does not match record! tailer=%u totalsize=%u\n",
+ (unsigned)tailer, (unsigned)(rec.rec_len + sizeof(rec)));
+ }
+ return rec.next;
+}
+
+static int tdb_dump_chain(TDB_CONTEXT *tdb, int i)
+{
+ tdb_off rec_ptr, top;
+
+ top = TDB_HASH_TOP(i);
+
+ if (tdb_lock(tdb, i, F_WRLCK) != 0)
+ return -1;
+
+ if (ofs_read(tdb, top, &rec_ptr) == -1)
+ return tdb_unlock(tdb, i, F_WRLCK);
+
+ if (rec_ptr)
+ printf("hash=%d\n", i);
+
+ while (rec_ptr) {
+ rec_ptr = tdb_dump_record(tdb, rec_ptr);
+ }
+
+ return tdb_unlock(tdb, i, F_WRLCK);
+}
+
+void tdb_dump_all(TDB_CONTEXT *tdb)
+{
+ int i;
+ for (i=0;i<tdb->header.hash_size;i++) {
+ tdb_dump_chain(tdb, i);
+ }
+ printf("freelist:\n");
+ tdb_dump_chain(tdb, -1);
+}
+
+int tdb_printfreelist(TDB_CONTEXT *tdb)
+{
+ int ret;
+ long total_free = 0;
+ tdb_off offset, rec_ptr;
+ struct list_struct rec;
+
+ if ((ret = tdb_lock(tdb, -1, F_WRLCK)) != 0)
+ return ret;
+
+ offset = FREELIST_TOP;
+
+ /* read in the freelist top */
+ if (ofs_read(tdb, offset, &rec_ptr) == -1) {
+ tdb_unlock(tdb, -1, F_WRLCK);
+ return 0;
+ }
+
+ printf("freelist top=[0x%08x]\n", rec_ptr );
+ while (rec_ptr) {
+ if (tdb_read(tdb, rec_ptr, (char *)&rec, sizeof(rec), DOCONV()) == -1) {
+ tdb_unlock(tdb, -1, F_WRLCK);
+ return -1;
+ }
+
+ if (rec.magic != TDB_FREE_MAGIC) {
+ printf("bad magic 0x%08x in free list\n", rec.magic);
+ tdb_unlock(tdb, -1, F_WRLCK);
+ return -1;
+ }
+
+ printf("entry offset=[0x%08x], rec.rec_len = [0x%08x (%d)]\n", rec.next, rec.rec_len, rec.rec_len );
+ total_free += rec.rec_len;
+
+ /* move to the next record */
+ rec_ptr = rec.next;
+ }
+ printf("total rec_len = [0x%08x (%d)]\n", (int)total_free,
+ (int)total_free);
+
+ return tdb_unlock(tdb, -1, F_WRLCK);
+}
+
+/* Remove an element from the freelist. Must have alloc lock. */
+static int remove_from_freelist(TDB_CONTEXT *tdb, tdb_off off, tdb_off next)
+{
+ tdb_off last_ptr, i;
+
+ /* read in the freelist top */
+ last_ptr = FREELIST_TOP;
+ while (ofs_read(tdb, last_ptr, &i) != -1 && i != 0) {
+ if (i == off) {
+ /* We've found it! */
+ return ofs_write(tdb, last_ptr, &next);
+ }
+ /* Follow chain (next offset is at start of record) */
+ last_ptr = i;
+ }
+ TDB_LOG((tdb, 0,"remove_from_freelist: not on list at off=%d\n", off));
+ return TDB_ERRCODE(TDB_ERR_CORRUPT, -1);
+}
+
+/* Add an element into the freelist. Merge adjacent records if
+ neccessary. */
+static int tdb_free(TDB_CONTEXT *tdb, tdb_off offset, struct list_struct *rec)
+{
+ tdb_off right, left;
+
+ /* Allocation and tailer lock */
+ if (tdb_lock(tdb, -1, F_WRLCK) != 0)
+ return -1;
+
+ /* set an initial tailer, so if we fail we don't leave a bogus record */
+ if (update_tailer(tdb, offset, rec) != 0) {
+ TDB_LOG((tdb, 0, "tdb_free: upfate_tailer failed!\n"));
+ goto fail;
+ }
+
+ /* Look right first (I'm an Australian, dammit) */
+ right = offset + sizeof(*rec) + rec->rec_len;
+ if (right + sizeof(*rec) <= tdb->map_size) {
+ struct list_struct r;
+
+ if (tdb_read(tdb, right, &r, sizeof(r), DOCONV()) == -1) {
+ TDB_LOG((tdb, 0, "tdb_free: right read failed at %u\n", right));
+ goto left;
+ }
+
+ /* If it's free, expand to include it. */
+ if (r.magic == TDB_FREE_MAGIC) {
+ if (remove_from_freelist(tdb, right, r.next) == -1) {
+ TDB_LOG((tdb, 0, "tdb_free: right free failed at %u\n", right));
+ goto left;
+ }
+ rec->rec_len += sizeof(r) + r.rec_len;
+ }
+ }
+
+left:
+ /* Look left */
+ left = offset - sizeof(tdb_off);
+ if (left > TDB_DATA_START(tdb->header.hash_size)) {
+ struct list_struct l;
+ tdb_off leftsize;
+
+ /* Read in tailer and jump back to header */
+ if (ofs_read(tdb, left, &leftsize) == -1) {
+ TDB_LOG((tdb, 0, "tdb_free: left offset read failed at %u\n", left));
+ goto update;
+ }
+ left = offset - leftsize;
+
+ /* Now read in record */
+ if (tdb_read(tdb, left, &l, sizeof(l), DOCONV()) == -1) {
+ TDB_LOG((tdb, 0, "tdb_free: left read failed at %u (%u)\n", left, leftsize));
+ goto update;
+ }
+
+ /* If it's free, expand to include it. */
+ if (l.magic == TDB_FREE_MAGIC) {
+ if (remove_from_freelist(tdb, left, l.next) == -1) {
+ TDB_LOG((tdb, 0, "tdb_free: left free failed at %u\n", left));
+ goto update;
+ } else {
+ offset = left;
+ rec->rec_len += leftsize;
+ }
+ }
+ }
+
+update:
+ if (update_tailer(tdb, offset, rec) == -1) {
+ TDB_LOG((tdb, 0, "tdb_free: update_tailer failed at %u\n", offset));
+ goto fail;
+ }
+
+ /* Now, prepend to free list */
+ rec->magic = TDB_FREE_MAGIC;
+
+ if (ofs_read(tdb, FREELIST_TOP, &rec->next) == -1 ||
+ rec_write(tdb, offset, rec) == -1 ||
+ ofs_write(tdb, FREELIST_TOP, &offset) == -1) {
+ TDB_LOG((tdb, 0, "tdb_free record write failed at offset=%d\n", offset));
+ goto fail;
+ }
+
+ /* And we're done. */
+ tdb_unlock(tdb, -1, F_WRLCK);
+ return 0;
+
+ fail:
+ tdb_unlock(tdb, -1, F_WRLCK);
+ return -1;
+}
+
+
+/* expand a file. we prefer to use ftruncate, as that is what posix
+ says to use for mmap expansion */
+static int expand_file(TDB_CONTEXT *tdb, tdb_off size, tdb_off addition)
+{
+ char buf[1024];
+#if HAVE_FTRUNCATE_EXTEND
+ if (ftruncate(tdb->fd, size+addition) != 0) {
+ TDB_LOG((tdb, 0, "expand_file ftruncate to %d failed (%s)\n",
+ size+addition, strerror(errno)));
+ return -1;
+ }
+#else
+ char b = 0;
+
+#ifdef HAVE_PWRITE
+ if (pwrite(tdb->fd, &b, 1, (size+addition) - 1) != 1) {
+#else
+ if (lseek(tdb->fd, (size+addition) - 1, SEEK_SET) != (size+addition) - 1 ||
+ write(tdb->fd, &b, 1) != 1) {
+#endif
+ TDB_LOG((tdb, 0, "expand_file to %d failed (%s)\n",
+ size+addition, strerror(errno)));
+ return -1;
+ }
+#endif
+
+ /* now fill the file with something. This ensures that the file isn't sparse, which would be
+ very bad if we ran out of disk. This must be done with write, not via mmap */
+ memset(buf, 0x42, sizeof(buf));
+ while (addition) {
+ int n = addition>sizeof(buf)?sizeof(buf):addition;
+#ifdef HAVE_PWRITE
+ int ret = pwrite(tdb->fd, buf, n, size);
+#else
+ int ret;
+ if (lseek(tdb->fd, size, SEEK_SET) != size)
+ return -1;
+ ret = write(tdb->fd, buf, n);
+#endif
+ if (ret != n) {
+ TDB_LOG((tdb, 0, "expand_file write of %d failed (%s)\n",
+ n, strerror(errno)));
+ return -1;
+ }
+ addition -= n;
+ size += n;
+ }
+ return 0;
+}
+
+
+/* expand the database at least size bytes by expanding the underlying
+ file and doing the mmap again if necessary */
+static int tdb_expand(TDB_CONTEXT *tdb, tdb_off size)
+{
+ struct list_struct rec;
+ tdb_off offset;
+
+ if (tdb_lock(tdb, -1, F_WRLCK) == -1) {
+ TDB_LOG((tdb, 0, "lock failed in tdb_expand\n"));
+ return -1;
+ }
+
+ /* must know about any previous expansions by another process */
+ tdb_oob(tdb, tdb->map_size + 1, 1);
+
+ /* always make room for at least 10 more records, and round
+ the database up to a multiple of TDB_PAGE_SIZE */
+ size = TDB_ALIGN(tdb->map_size + size*10, TDB_PAGE_SIZE) - tdb->map_size;
+
+ if (!(tdb->flags & TDB_INTERNAL))
+ tdb_munmap(tdb);
+
+ /*
+ * We must ensure the file is unmapped before doing this
+ * to ensure consistency with systems like OpenBSD where
+ * writes and mmaps are not consistent.
+ */
+
+ /* expand the file itself */
+ if (!(tdb->flags & TDB_INTERNAL)) {
+ if (expand_file(tdb, tdb->map_size, size) != 0)
+ goto fail;
+ }
+
+ tdb->map_size += size;
+
+ if (tdb->flags & TDB_INTERNAL)
+ tdb->map_ptr = realloc(tdb->map_ptr, tdb->map_size);
+ else {
+ /*
+ * We must ensure the file is remapped before adding the space
+ * to ensure consistency with systems like OpenBSD where
+ * writes and mmaps are not consistent.
+ */
+
+ /* We're ok if the mmap fails as we'll fallback to read/write */
+ tdb_mmap(tdb);
+ }
+
+ /* form a new freelist record */
+ memset(&rec,'\0',sizeof(rec));
+ rec.rec_len = size - sizeof(rec);
+
+ /* link it into the free list */
+ offset = tdb->map_size - size;
+ if (tdb_free(tdb, offset, &rec) == -1)
+ goto fail;
+
+ tdb_unlock(tdb, -1, F_WRLCK);
+ return 0;
+ fail:
+ tdb_unlock(tdb, -1, F_WRLCK);
+ return -1;
+}
+
+/* allocate some space from the free list. The offset returned points
+ to a unconnected list_struct within the database with room for at
+ least length bytes of total data
+
+ 0 is returned if the space could not be allocated
+ */
+static tdb_off tdb_allocate(TDB_CONTEXT *tdb, tdb_len length,
+ struct list_struct *rec)
+{
+ tdb_off rec_ptr, last_ptr, newrec_ptr;
+ struct list_struct newrec;
+
+ memset(&newrec, '\0', sizeof(newrec));
+
+ if (tdb_lock(tdb, -1, F_WRLCK) == -1)
+ return 0;
+
+ /* Extra bytes required for tailer */
+ length += sizeof(tdb_off);
+
+ again:
+ last_ptr = FREELIST_TOP;
+
+ /* read in the freelist top */
+ if (ofs_read(tdb, FREELIST_TOP, &rec_ptr) == -1)
+ goto fail;
+
+ /* keep looking until we find a freelist record big enough */
+ while (rec_ptr) {
+ if (rec_free_read(tdb, rec_ptr, rec) == -1)
+ goto fail;
+
+ if (rec->rec_len >= length) {
+ /* found it - now possibly split it up */
+ if (rec->rec_len > length + MIN_REC_SIZE) {
+ /* Length of left piece */
+ length = TDB_ALIGN(length, TDB_ALIGNMENT);
+
+ /* Right piece to go on free list */
+ newrec.rec_len = rec->rec_len
+ - (sizeof(*rec) + length);
+ newrec_ptr = rec_ptr + sizeof(*rec) + length;
+
+ /* And left record is shortened */
+ rec->rec_len = length;
+ } else
+ newrec_ptr = 0;
+
+ /* Remove allocated record from the free list */
+ if (ofs_write(tdb, last_ptr, &rec->next) == -1)
+ goto fail;
+
+ /* Update header: do this before we drop alloc
+ lock, otherwise tdb_free() might try to
+ merge with us, thinking we're free.
+ (Thanks Jeremy Allison). */
+ rec->magic = TDB_MAGIC;
+ if (rec_write(tdb, rec_ptr, rec) == -1)
+ goto fail;
+
+ /* Did we create new block? */
+ if (newrec_ptr) {
+ /* Update allocated record tailer (we
+ shortened it). */
+ if (update_tailer(tdb, rec_ptr, rec) == -1)
+ goto fail;
+
+ /* Free new record */
+ if (tdb_free(tdb, newrec_ptr, &newrec) == -1)
+ goto fail;
+ }
+
+ /* all done - return the new record offset */
+ tdb_unlock(tdb, -1, F_WRLCK);
+ return rec_ptr;
+ }
+ /* move to the next record */
+ last_ptr = rec_ptr;
+ rec_ptr = rec->next;
+ }
+ /* we didn't find enough space. See if we can expand the
+ database and if we can then try again */
+ if (tdb_expand(tdb, length + sizeof(*rec)) == 0)
+ goto again;
+ fail:
+ tdb_unlock(tdb, -1, F_WRLCK);
+ return 0;
+}
+
+/* initialise a new database with a specified hash size */
+static int tdb_new_database(TDB_CONTEXT *tdb, int hash_size)
+{
+ struct tdb_header *newdb;
+ int size, ret = -1;
+
+ /* We make it up in memory, then write it out if not internal */
+ size = sizeof(struct tdb_header) + (hash_size+1)*sizeof(tdb_off);
+ if (!(newdb = calloc(size, 1)))
+ return TDB_ERRCODE(TDB_ERR_OOM, -1);
+
+ /* Fill in the header */
+ newdb->version = TDB_VERSION;
+ newdb->hash_size = hash_size;
+#ifdef USE_SPINLOCKS
+ newdb->rwlocks = size;
+#endif
+ if (tdb->flags & TDB_INTERNAL) {
+ tdb->map_size = size;
+ tdb->map_ptr = (char *)newdb;
+ memcpy(&tdb->header, newdb, sizeof(tdb->header));
+ /* Convert the `ondisk' version if asked. */
+ CONVERT(*newdb);
+ return 0;
+ }
+ if (lseek(tdb->fd, 0, SEEK_SET) == -1)
+ goto fail;
+
+ if (ftruncate(tdb->fd, 0) == -1)
+ goto fail;
+
+ /* This creates an endian-converted header, as if read from disk */
+ CONVERT(*newdb);
+ memcpy(&tdb->header, newdb, sizeof(tdb->header));
+ /* Don't endian-convert the magic food! */
+ memcpy(newdb->magic_food, TDB_MAGIC_FOOD, strlen(TDB_MAGIC_FOOD)+1);
+ if (write(tdb->fd, newdb, size) != size)
+ ret = -1;
+ else
+ ret = tdb_create_rwlocks(tdb->fd, hash_size);
+
+ fail:
+ SAFE_FREE(newdb);
+ return ret;
+}
+
+/* Returns 0 on fail. On success, return offset of record, and fills
+ in rec */
+static tdb_off tdb_find(TDB_CONTEXT *tdb, TDB_DATA key, u32 hash,
+ struct list_struct *r)
+{
+ tdb_off rec_ptr;
+
+ /* read in the hash top */
+ if (ofs_read(tdb, TDB_HASH_TOP(hash), &rec_ptr) == -1)
+ return 0;
+
+ /* keep looking until we find the right record */
+ while (rec_ptr) {
+ if (rec_read(tdb, rec_ptr, r) == -1)
+ return 0;
+
+ if (!TDB_DEAD(r) && hash==r->full_hash && key.dsize==r->key_len) {
+ char *k;
+ /* a very likely hit - read the key */
+ k = tdb_alloc_read(tdb, rec_ptr + sizeof(*r),
+ r->key_len);
+ if (!k)
+ return 0;
+
+ if (memcmp(key.dptr, k, key.dsize) == 0) {
+ SAFE_FREE(k);
+ return rec_ptr;
+ }
+ SAFE_FREE(k);
+ }
+ rec_ptr = r->next;
+ }
+ return TDB_ERRCODE(TDB_ERR_NOEXIST, 0);
+}
+
+/* If they do lockkeys, check that this hash is one they locked */
+static int tdb_keylocked(TDB_CONTEXT *tdb, u32 hash)
+{
+ u32 i;
+ if (!tdb->lockedkeys)
+ return 1;
+ for (i = 0; i < tdb->lockedkeys[0]; i++)
+ if (tdb->lockedkeys[i+1] == hash)
+ return 1;
+ return TDB_ERRCODE(TDB_ERR_NOLOCK, 0);
+}
+
+/* As tdb_find, but if you succeed, keep the lock */
+static tdb_off tdb_find_lock_hash(TDB_CONTEXT *tdb, TDB_DATA key, u32 hash, int locktype,
+ struct list_struct *rec)
+{
+ u32 rec_ptr;
+
+ if (!tdb_keylocked(tdb, hash))
+ return 0;
+ if (tdb_lock(tdb, BUCKET(hash), locktype) == -1)
+ return 0;
+ if (!(rec_ptr = tdb_find(tdb, key, hash, rec)))
+ tdb_unlock(tdb, BUCKET(hash), locktype);
+ return rec_ptr;
+}
+
+enum TDB_ERROR tdb_error(TDB_CONTEXT *tdb)
+{
+ return tdb->ecode;
+}
+
+static struct tdb_errname {
+ enum TDB_ERROR ecode; const char *estring;
+} emap[] = { {TDB_SUCCESS, "Success"},
+ {TDB_ERR_CORRUPT, "Corrupt database"},
+ {TDB_ERR_IO, "IO Error"},
+ {TDB_ERR_LOCK, "Locking error"},
+ {TDB_ERR_OOM, "Out of memory"},
+ {TDB_ERR_EXISTS, "Record exists"},
+ {TDB_ERR_NOLOCK, "Lock exists on other keys"},
+ {TDB_ERR_NOEXIST, "Record does not exist"} };
+
+/* Error string for the last tdb error */
+const char *tdb_errorstr(TDB_CONTEXT *tdb)
+{
+ u32 i;
+ for (i = 0; i < sizeof(emap) / sizeof(struct tdb_errname); i++)
+ if (tdb->ecode == emap[i].ecode)
+ return emap[i].estring;
+ return "Invalid error code";
+}
+
+/* update an entry in place - this only works if the new data size
+ is <= the old data size and the key exists.
+ on failure return -1.
+*/
+
+static int tdb_update_hash(TDB_CONTEXT *tdb, TDB_DATA key, u32 hash, TDB_DATA dbuf)
+{
+ struct list_struct rec;
+ tdb_off rec_ptr;
+
+ /* find entry */
+ if (!(rec_ptr = tdb_find(tdb, key, hash, &rec)))
+ return -1;
+
+ /* must be long enough key, data and tailer */
+ if (rec.rec_len < key.dsize + dbuf.dsize + sizeof(tdb_off)) {
+ tdb->ecode = TDB_SUCCESS; /* Not really an error */
+ return -1;
+ }
+
+ if (tdb_write(tdb, rec_ptr + sizeof(rec) + rec.key_len,
+ dbuf.dptr, dbuf.dsize) == -1)
+ return -1;
+
+ if (dbuf.dsize != rec.data_len) {
+ /* update size */
+ rec.data_len = dbuf.dsize;
+ return rec_write(tdb, rec_ptr, &rec);
+ }
+
+ return 0;
+}
+
+/* find an entry in the database given a key */
+/* If an entry doesn't exist tdb_err will be set to
+ * TDB_ERR_NOEXIST. If a key has no data attached
+ * tdb_err will not be set. Both will return a
+ * zero pptr and zero dsize.
+ */
+
+TDB_DATA tdb_fetch(TDB_CONTEXT *tdb, TDB_DATA key)
+{
+ tdb_off rec_ptr;
+ struct list_struct rec;
+ TDB_DATA ret;
+ u32 hash;
+
+ /* find which hash bucket it is in */
+ hash = tdb_hash(&key);
+ if (!(rec_ptr = tdb_find_lock_hash(tdb,key,hash,F_RDLCK,&rec)))
+ return tdb_null;
+
+ if (rec.data_len)
+ ret.dptr = tdb_alloc_read(tdb, rec_ptr + sizeof(rec) + rec.key_len,
+ rec.data_len);
+ else
+ ret.dptr = NULL;
+ ret.dsize = rec.data_len;
+ tdb_unlock(tdb, BUCKET(rec.full_hash), F_RDLCK);
+ return ret;
+}
+
+/* check if an entry in the database exists
+
+ note that 1 is returned if the key is found and 0 is returned if not found
+ this doesn't match the conventions in the rest of this module, but is
+ compatible with gdbm
+*/
+static int tdb_exists_hash(TDB_CONTEXT *tdb, TDB_DATA key, u32 hash)
+{
+ struct list_struct rec;
+
+ if (tdb_find_lock_hash(tdb, key, hash, F_RDLCK, &rec) == 0)
+ return 0;
+ tdb_unlock(tdb, BUCKET(rec.full_hash), F_RDLCK);
+ return 1;
+}
+
+int tdb_exists(TDB_CONTEXT *tdb, TDB_DATA key)
+{
+ u32 hash = tdb_hash(&key);
+ return tdb_exists_hash(tdb, key, hash);
+}
+
+/* record lock stops delete underneath */
+static int lock_record(TDB_CONTEXT *tdb, tdb_off off)
+{
+ return off ? tdb_brlock(tdb, off, F_RDLCK, F_SETLKW, 0) : 0;
+}
+/*
+ Write locks override our own fcntl readlocks, so check it here.
+ Note this is meant to be F_SETLK, *not* F_SETLKW, as it's not
+ an error to fail to get the lock here.
+*/
+
+static int write_lock_record(TDB_CONTEXT *tdb, tdb_off off)
+{
+ struct tdb_traverse_lock *i;
+ for (i = &tdb->travlocks; i; i = i->next)
+ if (i->off == off)
+ return -1;
+ return tdb_brlock(tdb, off, F_WRLCK, F_SETLK, 1);
+}
+
+/*
+ Note this is meant to be F_SETLK, *not* F_SETLKW, as it's not
+ an error to fail to get the lock here.
+*/
+
+static int write_unlock_record(TDB_CONTEXT *tdb, tdb_off off)
+{
+ return tdb_brlock(tdb, off, F_UNLCK, F_SETLK, 0);
+}
+/* fcntl locks don't stack: avoid unlocking someone else's */
+static int unlock_record(TDB_CONTEXT *tdb, tdb_off off)
+{
+ struct tdb_traverse_lock *i;
+ u32 count = 0;
+
+ if (off == 0)
+ return 0;
+ for (i = &tdb->travlocks; i; i = i->next)
+ if (i->off == off)
+ count++;
+ return (count == 1 ? tdb_brlock(tdb, off, F_UNLCK, F_SETLKW, 0) : 0);
+}
+
+/* actually delete an entry in the database given the offset */
+static int do_delete(TDB_CONTEXT *tdb, tdb_off rec_ptr, struct list_struct*rec)
+{
+ tdb_off last_ptr, i;
+ struct list_struct lastrec;
+
+ if (tdb->read_only) return -1;
+
+ if (write_lock_record(tdb, rec_ptr) == -1) {
+ /* Someone traversing here: mark it as dead */
+ rec->magic = TDB_DEAD_MAGIC;
+ return rec_write(tdb, rec_ptr, rec);
+ }
+ if (write_unlock_record(tdb, rec_ptr) != 0)
+ return -1;
+
+ /* find previous record in hash chain */
+ if (ofs_read(tdb, TDB_HASH_TOP(rec->full_hash), &i) == -1)
+ return -1;
+ for (last_ptr = 0; i != rec_ptr; last_ptr = i, i = lastrec.next)
+ if (rec_read(tdb, i, &lastrec) == -1)
+ return -1;
+
+ /* unlink it: next ptr is at start of record. */
+ if (last_ptr == 0)
+ last_ptr = TDB_HASH_TOP(rec->full_hash);
+ if (ofs_write(tdb, last_ptr, &rec->next) == -1)
+ return -1;
+
+ /* recover the space */
+ if (tdb_free(tdb, rec_ptr, rec) == -1)
+ return -1;
+ return 0;
+}
+
+/* Uses traverse lock: 0 = finish, -1 = error, other = record offset */
+static int tdb_next_lock(TDB_CONTEXT *tdb, struct tdb_traverse_lock *tlock,
+ struct list_struct *rec)
+{
+ int want_next = (tlock->off != 0);
+
+ /* No traversal allows if you've called tdb_lockkeys() */
+ if (tdb->lockedkeys)
+ return TDB_ERRCODE(TDB_ERR_NOLOCK, -1);
+
+ /* Lock each chain from the start one. */
+ for (; tlock->hash < tdb->header.hash_size; tlock->hash++) {
+ if (tdb_lock(tdb, tlock->hash, F_WRLCK) == -1)
+ return -1;
+
+ /* No previous record? Start at top of chain. */
+ if (!tlock->off) {
+ if (ofs_read(tdb, TDB_HASH_TOP(tlock->hash),
+ &tlock->off) == -1)
+ goto fail;
+ } else {
+ /* Otherwise unlock the previous record. */
+ if (unlock_record(tdb, tlock->off) != 0)
+ goto fail;
+ }
+
+ if (want_next) {
+ /* We have offset of old record: grab next */
+ if (rec_read(tdb, tlock->off, rec) == -1)
+ goto fail;
+ tlock->off = rec->next;
+ }
+
+ /* Iterate through chain */
+ while( tlock->off) {
+ tdb_off current;
+ if (rec_read(tdb, tlock->off, rec) == -1)
+ goto fail;
+ if (!TDB_DEAD(rec)) {
+ /* Woohoo: we found one! */
+ if (lock_record(tdb, tlock->off) != 0)
+ goto fail;
+ return tlock->off;
+ }
+ /* Try to clean dead ones from old traverses */
+ current = tlock->off;
+ tlock->off = rec->next;
+ if (!tdb->read_only &&
+ do_delete(tdb, current, rec) != 0)
+ goto fail;
+ }
+ tdb_unlock(tdb, tlock->hash, F_WRLCK);
+ want_next = 0;
+ }
+ /* We finished iteration without finding anything */
+ return TDB_ERRCODE(TDB_SUCCESS, 0);
+
+ fail:
+ tlock->off = 0;
+ if (tdb_unlock(tdb, tlock->hash, F_WRLCK) != 0)
+ TDB_LOG((tdb, 0, "tdb_next_lock: On error unlock failed!\n"));
+ return -1;
+}
+
+/* traverse the entire database - calling fn(tdb, key, data) on each element.
+ return -1 on error or the record count traversed
+ if fn is NULL then it is not called
+ a non-zero return value from fn() indicates that the traversal should stop
+ */
+int tdb_traverse(TDB_CONTEXT *tdb, tdb_traverse_func fn, void *private)
+{
+ TDB_DATA key, dbuf;
+ struct list_struct rec;
+ struct tdb_traverse_lock tl = { NULL, 0, 0 };
+ int ret, count = 0;
+
+ /* This was in the initializaton, above, but the IRIX compiler
+ * did not like it. crh
+ */
+ tl.next = tdb->travlocks.next;
+
+ /* fcntl locks don't stack: beware traverse inside traverse */
+ tdb->travlocks.next = &tl;
+
+ /* tdb_next_lock places locks on the record returned, and its chain */
+ while ((ret = tdb_next_lock(tdb, &tl, &rec)) > 0) {
+ count++;
+ /* now read the full record */
+ key.dptr = tdb_alloc_read(tdb, tl.off + sizeof(rec),
+ rec.key_len + rec.data_len);
+ if (!key.dptr) {
+ ret = -1;
+ if (tdb_unlock(tdb, tl.hash, F_WRLCK) != 0)
+ goto out;
+ if (unlock_record(tdb, tl.off) != 0)
+ TDB_LOG((tdb, 0, "tdb_traverse: key.dptr == NULL and unlock_record failed!\n"));
+ goto out;
+ }
+ key.dsize = rec.key_len;
+ dbuf.dptr = key.dptr + rec.key_len;
+ dbuf.dsize = rec.data_len;
+
+ /* Drop chain lock, call out */
+ if (tdb_unlock(tdb, tl.hash, F_WRLCK) != 0) {
+ ret = -1;
+ goto out;
+ }
+ if (fn && fn(tdb, key, dbuf, private)) {
+ /* They want us to terminate traversal */
+ ret = count;
+ if (unlock_record(tdb, tl.off) != 0) {
+ TDB_LOG((tdb, 0, "tdb_traverse: unlock_record failed!\n"));;
+ ret = -1;
+ }
+ tdb->travlocks.next = tl.next;
+ SAFE_FREE(key.dptr);
+ return count;
+ }
+ SAFE_FREE(key.dptr);
+ }
+out:
+ tdb->travlocks.next = tl.next;
+ if (ret < 0)
+ return -1;
+ else
+ return count;
+}
+
+/* find the first entry in the database and return its key */
+TDB_DATA tdb_firstkey(TDB_CONTEXT *tdb)
+{
+ TDB_DATA key;
+ struct list_struct rec;
+
+ /* release any old lock */
+ if (unlock_record(tdb, tdb->travlocks.off) != 0)
+ return tdb_null;
+ tdb->travlocks.off = tdb->travlocks.hash = 0;
+
+ if (tdb_next_lock(tdb, &tdb->travlocks, &rec) <= 0)
+ return tdb_null;
+ /* now read the key */
+ key.dsize = rec.key_len;
+ key.dptr =tdb_alloc_read(tdb,tdb->travlocks.off+sizeof(rec),key.dsize);
+ if (tdb_unlock(tdb, BUCKET(tdb->travlocks.hash), F_WRLCK) != 0)
+ TDB_LOG((tdb, 0, "tdb_firstkey: error occurred while tdb_unlocking!\n"));
+ return key;
+}
+
+/* find the next entry in the database, returning its key */
+TDB_DATA tdb_nextkey(TDB_CONTEXT *tdb, TDB_DATA oldkey)
+{
+ u32 oldhash;
+ TDB_DATA key = tdb_null;
+ struct list_struct rec;
+ char *k = NULL;
+
+ /* Is locked key the old key? If so, traverse will be reliable. */
+ if (tdb->travlocks.off) {
+ if (tdb_lock(tdb,tdb->travlocks.hash,F_WRLCK))
+ return tdb_null;
+ if (rec_read(tdb, tdb->travlocks.off, &rec) == -1
+ || !(k = tdb_alloc_read(tdb,tdb->travlocks.off+sizeof(rec),
+ rec.key_len))
+ || memcmp(k, oldkey.dptr, oldkey.dsize) != 0) {
+ /* No, it wasn't: unlock it and start from scratch */
+ if (unlock_record(tdb, tdb->travlocks.off) != 0)
+ return tdb_null;
+ if (tdb_unlock(tdb, tdb->travlocks.hash, F_WRLCK) != 0)
+ return tdb_null;
+ tdb->travlocks.off = 0;
+ }
+
+ SAFE_FREE(k);
+ }
+
+ if (!tdb->travlocks.off) {
+ /* No previous element: do normal find, and lock record */
+ tdb->travlocks.off = tdb_find_lock_hash(tdb, oldkey, tdb_hash(&oldkey), F_WRLCK, &rec);
+ if (!tdb->travlocks.off)
+ return tdb_null;
+ tdb->travlocks.hash = BUCKET(rec.full_hash);
+ if (lock_record(tdb, tdb->travlocks.off) != 0) {
+ TDB_LOG((tdb, 0, "tdb_nextkey: lock_record failed (%s)!\n", strerror(errno)));
+ return tdb_null;
+ }
+ }
+ oldhash = tdb->travlocks.hash;
+
+ /* Grab next record: locks chain and returned record,
+ unlocks old record */
+ if (tdb_next_lock(tdb, &tdb->travlocks, &rec) > 0) {
+ key.dsize = rec.key_len;
+ key.dptr = tdb_alloc_read(tdb, tdb->travlocks.off+sizeof(rec),
+ key.dsize);
+ /* Unlock the chain of this new record */
+ if (tdb_unlock(tdb, tdb->travlocks.hash, F_WRLCK) != 0)
+ TDB_LOG((tdb, 0, "tdb_nextkey: WARNING tdb_unlock failed!\n"));
+ }
+ /* Unlock the chain of old record */
+ if (tdb_unlock(tdb, BUCKET(oldhash), F_WRLCK) != 0)
+ TDB_LOG((tdb, 0, "tdb_nextkey: WARNING tdb_unlock failed!\n"));
+ return key;
+}
+
+/* delete an entry in the database given a key */
+static int tdb_delete_hash(TDB_CONTEXT *tdb, TDB_DATA key, u32 hash)
+{
+ tdb_off rec_ptr;
+ struct list_struct rec;
+ int ret;
+
+ if (!(rec_ptr = tdb_find_lock_hash(tdb, key, hash, F_WRLCK, &rec)))
+ return -1;
+ ret = do_delete(tdb, rec_ptr, &rec);
+ if (tdb_unlock(tdb, BUCKET(rec.full_hash), F_WRLCK) != 0)
+ TDB_LOG((tdb, 0, "tdb_delete: WARNING tdb_unlock failed!\n"));
+ return ret;
+}
+
+int tdb_delete(TDB_CONTEXT *tdb, TDB_DATA key)
+{
+ u32 hash = tdb_hash(&key);
+ return tdb_delete_hash(tdb, key, hash);
+}
+
+/* store an element in the database, replacing any existing element
+ with the same key
+
+ return 0 on success, -1 on failure
+*/
+int tdb_store(TDB_CONTEXT *tdb, TDB_DATA key, TDB_DATA dbuf, int flag)
+{
+ struct list_struct rec;
+ u32 hash;
+ tdb_off rec_ptr;
+ char *p = NULL;
+ int ret = 0;
+
+ /* find which hash bucket it is in */
+ hash = tdb_hash(&key);
+ if (!tdb_keylocked(tdb, hash))
+ return -1;
+ if (tdb_lock(tdb, BUCKET(hash), F_WRLCK) == -1)
+ return -1;
+
+ /* check for it existing, on insert. */
+ if (flag == TDB_INSERT) {
+ if (tdb_exists_hash(tdb, key, hash)) {
+ tdb->ecode = TDB_ERR_EXISTS;
+ goto fail;
+ }
+ } else {
+ /* first try in-place update, on modify or replace. */
+ if (tdb_update_hash(tdb, key, hash, dbuf) == 0)
+ goto out;
+ if (tdb->ecode == TDB_ERR_NOEXIST &&
+ flag == TDB_MODIFY) {
+ /* if the record doesn't exist and we are in TDB_MODIFY mode then
+ we should fail the store */
+ goto fail;
+ }
+ }
+ /* reset the error code potentially set by the tdb_update() */
+ tdb->ecode = TDB_SUCCESS;
+
+ /* delete any existing record - if it doesn't exist we don't
+ care. Doing this first reduces fragmentation, and avoids
+ coalescing with `allocated' block before it's updated. */
+ if (flag != TDB_INSERT)
+ tdb_delete_hash(tdb, key, hash);
+
+ /* Copy key+value *before* allocating free space in case malloc
+ fails and we are left with a dead spot in the tdb. */
+
+ if (!(p = (char *)malloc(key.dsize + dbuf.dsize))) {
+ tdb->ecode = TDB_ERR_OOM;
+ goto fail;
+ }
+
+ memcpy(p, key.dptr, key.dsize);
+ if (dbuf.dsize)
+ memcpy(p+key.dsize, dbuf.dptr, dbuf.dsize);
+
+ /* we have to allocate some space */
+ if (!(rec_ptr = tdb_allocate(tdb, key.dsize + dbuf.dsize, &rec)))
+ goto fail;
+
+ /* Read hash top into next ptr */
+ if (ofs_read(tdb, TDB_HASH_TOP(hash), &rec.next) == -1)
+ goto fail;
+
+ rec.key_len = key.dsize;
+ rec.data_len = dbuf.dsize;
+ rec.full_hash = hash;
+ rec.magic = TDB_MAGIC;
+
+ /* write out and point the top of the hash chain at it */
+ if (rec_write(tdb, rec_ptr, &rec) == -1
+ || tdb_write(tdb, rec_ptr+sizeof(rec), p, key.dsize+dbuf.dsize)==-1
+ || ofs_write(tdb, TDB_HASH_TOP(hash), &rec_ptr) == -1) {
+ /* Need to tdb_unallocate() here */
+ goto fail;
+ }
+ out:
+ SAFE_FREE(p);
+ tdb_unlock(tdb, BUCKET(hash), F_WRLCK);
+ return ret;
+fail:
+ ret = -1;
+ goto out;
+}
+
+/* Attempt to append data to an entry in place - this only works if the new data size
+ is <= the old data size and the key exists.
+ on failure return -1. Record must be locked before calling.
+*/
+static int tdb_append_inplace(TDB_CONTEXT *tdb, TDB_DATA key, u32 hash, TDB_DATA new_dbuf)
+{
+ struct list_struct rec;
+ tdb_off rec_ptr;
+
+ /* find entry */
+ if (!(rec_ptr = tdb_find(tdb, key, hash, &rec)))
+ return -1;
+
+ /* Append of 0 is always ok. */
+ if (new_dbuf.dsize == 0)
+ return 0;
+
+ /* must be long enough for key, old data + new data and tailer */
+ if (rec.rec_len < key.dsize + rec.data_len + new_dbuf.dsize + sizeof(tdb_off)) {
+ /* No room. */
+ tdb->ecode = TDB_SUCCESS; /* Not really an error */
+ return -1;
+ }
+
+ if (tdb_write(tdb, rec_ptr + sizeof(rec) + rec.key_len + rec.data_len,
+ new_dbuf.dptr, new_dbuf.dsize) == -1)
+ return -1;
+
+ /* update size */
+ rec.data_len += new_dbuf.dsize;
+ return rec_write(tdb, rec_ptr, &rec);
+}
+
+/* Append to an entry. Create if not exist. */
+
+int tdb_append(TDB_CONTEXT *tdb, TDB_DATA key, TDB_DATA new_dbuf)
+{
+ struct list_struct rec;
+ u32 hash;
+ tdb_off rec_ptr;
+ char *p = NULL;
+ int ret = 0;
+ size_t new_data_size = 0;
+
+ /* find which hash bucket it is in */
+ hash = tdb_hash(&key);
+ if (!tdb_keylocked(tdb, hash))
+ return -1;
+ if (tdb_lock(tdb, BUCKET(hash), F_WRLCK) == -1)
+ return -1;
+
+ /* first try in-place. */
+ if (tdb_append_inplace(tdb, key, hash, new_dbuf) == 0)
+ goto out;
+
+ /* reset the error code potentially set by the tdb_append_inplace() */
+ tdb->ecode = TDB_SUCCESS;
+
+ /* find entry */
+ if (!(rec_ptr = tdb_find(tdb, key, hash, &rec))) {
+ if (tdb->ecode != TDB_ERR_NOEXIST)
+ goto fail;
+
+ /* Not found - create. */
+
+ ret = tdb_store(tdb, key, new_dbuf, TDB_INSERT);
+ goto out;
+ }
+
+ new_data_size = rec.data_len + new_dbuf.dsize;
+
+ /* Copy key+old_value+value *before* allocating free space in case malloc
+ fails and we are left with a dead spot in the tdb. */
+
+ if (!(p = (char *)malloc(key.dsize + new_data_size))) {
+ tdb->ecode = TDB_ERR_OOM;
+ goto fail;
+ }
+
+ /* Copy the key in place. */
+ memcpy(p, key.dptr, key.dsize);
+
+ /* Now read the old data into place. */
+ if (rec.data_len &&
+ tdb_read(tdb, rec_ptr + sizeof(rec) + rec.key_len, p + key.dsize, rec.data_len, 0) == -1)
+ goto fail;
+
+ /* Finally append the new data. */
+ if (new_dbuf.dsize)
+ memcpy(p+key.dsize+rec.data_len, new_dbuf.dptr, new_dbuf.dsize);
+
+ /* delete any existing record - if it doesn't exist we don't
+ care. Doing this first reduces fragmentation, and avoids
+ coalescing with `allocated' block before it's updated. */
+
+ tdb_delete_hash(tdb, key, hash);
+
+ if (!(rec_ptr = tdb_allocate(tdb, key.dsize + new_data_size, &rec)))
+ goto fail;
+
+ /* Read hash top into next ptr */
+ if (ofs_read(tdb, TDB_HASH_TOP(hash), &rec.next) == -1)
+ goto fail;
+
+ rec.key_len = key.dsize;
+ rec.data_len = new_data_size;
+ rec.full_hash = hash;
+ rec.magic = TDB_MAGIC;
+
+ /* write out and point the top of the hash chain at it */
+ if (rec_write(tdb, rec_ptr, &rec) == -1
+ || tdb_write(tdb, rec_ptr+sizeof(rec), p, key.dsize+new_data_size)==-1
+ || ofs_write(tdb, TDB_HASH_TOP(hash), &rec_ptr) == -1) {
+ /* Need to tdb_unallocate() here */
+ goto fail;
+ }
+
+ out:
+ SAFE_FREE(p);
+ tdb_unlock(tdb, BUCKET(hash), F_WRLCK);
+ return ret;
+
+fail:
+ ret = -1;
+ goto out;
+}
+
+static int tdb_already_open(dev_t device,
+ ino_t ino)
+{
+ TDB_CONTEXT *i;
+
+ for (i = tdbs; i; i = i->next) {
+ if (i->device == device && i->inode == ino) {
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+/* open the database, creating it if necessary
+
+ The open_flags and mode are passed straight to the open call on the
+ database file. A flags value of O_WRONLY is invalid. The hash size
+ is advisory, use zero for a default value.
+
+ Return is NULL on error, in which case errno is also set. Don't
+ try to call tdb_error or tdb_errname, just do strerror(errno).
+
+ @param name may be NULL for internal databases. */
+TDB_CONTEXT *tdb_open(const char *name, int hash_size, int tdb_flags,
+ int open_flags, mode_t mode)
+{
+ return tdb_open_ex(name, hash_size, tdb_flags, open_flags, mode, NULL);
+}
+
+
+TDB_CONTEXT *tdb_open_ex(const char *name, int hash_size, int tdb_flags,
+ int open_flags, mode_t mode,
+ tdb_log_func log_fn)
+{
+ TDB_CONTEXT *tdb;
+ struct stat st;
+ int rev = 0, locked = 0;
+ unsigned char *vp;
+ u32 vertest;
+
+ if (!(tdb = calloc(1, sizeof *tdb))) {
+ /* Can't log this */
+ errno = ENOMEM;
+ goto fail;
+ }
+ tdb->fd = -1;
+ tdb->name = NULL;
+ tdb->map_ptr = NULL;
+ tdb->lockedkeys = NULL;
+ tdb->flags = tdb_flags;
+ tdb->open_flags = open_flags;
+ tdb->log_fn = log_fn;
+
+ if ((open_flags & O_ACCMODE) == O_WRONLY) {
+ TDB_LOG((tdb, 0, "tdb_open_ex: can't open tdb %s write-only\n",
+ name));
+ errno = EINVAL;
+ goto fail;
+ }
+
+ if (hash_size == 0)
+ hash_size = DEFAULT_HASH_SIZE;
+ if ((open_flags & O_ACCMODE) == O_RDONLY) {
+ tdb->read_only = 1;
+ /* read only databases don't do locking or clear if first */
+ tdb->flags |= TDB_NOLOCK;
+ tdb->flags &= ~TDB_CLEAR_IF_FIRST;
+ }
+
+ /* internal databases don't mmap or lock, and start off cleared */
+ if (tdb->flags & TDB_INTERNAL) {
+ tdb->flags |= (TDB_NOLOCK | TDB_NOMMAP);
+ tdb->flags &= ~TDB_CLEAR_IF_FIRST;
+ if (tdb_new_database(tdb, hash_size) != 0) {
+ TDB_LOG((tdb, 0, "tdb_open_ex: tdb_new_database failed!"));
+ goto fail;
+ }
+ goto internal;
+ }
+
+ if ((tdb->fd = open(name, open_flags, mode)) == -1) {
+ TDB_LOG((tdb, 5, "tdb_open_ex: could not open file %s: %s\n",
+ name, strerror(errno)));
+ goto fail; /* errno set by open(2) */
+ }
+
+ /* ensure there is only one process initialising at once */
+ if (tdb_brlock(tdb, GLOBAL_LOCK, F_WRLCK, F_SETLKW, 0) == -1) {
+ TDB_LOG((tdb, 0, "tdb_open_ex: failed to get global lock on %s: %s\n",
+ name, strerror(errno)));
+ goto fail; /* errno set by tdb_brlock */
+ }
+
+ /* we need to zero database if we are the only one with it open */
+ if ((tdb_flags & TDB_CLEAR_IF_FIRST) &&
+ (locked = (tdb_brlock(tdb, ACTIVE_LOCK, F_WRLCK, F_SETLK, 0) == 0))) {
+ open_flags |= O_CREAT;
+ if (ftruncate(tdb->fd, 0) == -1) {
+ TDB_LOG((tdb, 0, "tdb_open_ex: "
+ "failed to truncate %s: %s\n",
+ name, strerror(errno)));
+ goto fail; /* errno set by ftruncate */
+ }
+ }
+
+ if (read(tdb->fd, &tdb->header, sizeof(tdb->header)) != sizeof(tdb->header)
+ || strcmp(tdb->header.magic_food, TDB_MAGIC_FOOD) != 0
+ || (tdb->header.version != TDB_VERSION
+ && !(rev = (tdb->header.version==TDB_BYTEREV(TDB_VERSION))))) {
+ /* its not a valid database - possibly initialise it */
+ if (!(open_flags & O_CREAT) || tdb_new_database(tdb, hash_size) == -1) {
+ errno = EIO; /* ie bad format or something */
+ goto fail;
+ }
+ rev = (tdb->flags & TDB_CONVERT);
+ }
+ vp = (unsigned char *)&tdb->header.version;
+ vertest = (((u32)vp[0]) << 24) | (((u32)vp[1]) << 16) |
+ (((u32)vp[2]) << 8) | (u32)vp[3];
+ tdb->flags |= (vertest==TDB_VERSION) ? TDB_BIGENDIAN : 0;
+ if (!rev)
+ tdb->flags &= ~TDB_CONVERT;
+ else {
+ tdb->flags |= TDB_CONVERT;
+ convert(&tdb->header, sizeof(tdb->header));
+ }
+ if (fstat(tdb->fd, &st) == -1)
+ goto fail;
+
+ /* Is it already in the open list? If so, fail. */
+ if (tdb_already_open(st.st_dev, st.st_ino)) {
+ TDB_LOG((tdb, 2, "tdb_open_ex: "
+ "%s (%d,%d) is already open in this process\n",
+ name, st.st_dev, st.st_ino));
+ errno = EBUSY;
+ goto fail;
+ }
+
+ if (!(tdb->name = (char *)strdup(name))) {
+ errno = ENOMEM;
+ goto fail;
+ }
+
+ tdb->map_size = st.st_size;
+ tdb->device = st.st_dev;
+ tdb->inode = st.st_ino;
+ tdb->locked = calloc(tdb->header.hash_size+1, sizeof(tdb->locked[0]));
+ if (!tdb->locked) {
+ TDB_LOG((tdb, 2, "tdb_open_ex: "
+ "failed to allocate lock structure for %s\n",
+ name));
+ errno = ENOMEM;
+ goto fail;
+ }
+ tdb_mmap(tdb);
+ if (locked) {
+ if (!tdb->read_only)
+ if (tdb_clear_spinlocks(tdb) != 0) {
+ TDB_LOG((tdb, 0, "tdb_open_ex: "
+ "failed to clear spinlock\n"));
+ goto fail;
+ }
+ if (tdb_brlock(tdb, ACTIVE_LOCK, F_UNLCK, F_SETLK, 0) == -1) {
+ TDB_LOG((tdb, 0, "tdb_open_ex: "
+ "failed to take ACTIVE_LOCK on %s: %s\n",
+ name, strerror(errno)));
+ goto fail;
+ }
+
+ }
+
+ /* We always need to do this if the CLEAR_IF_FIRST flag is set, even if
+ we didn't get the initial exclusive lock as we need to let all other
+ users know we're using it. */
+
+ if (tdb_flags & TDB_CLEAR_IF_FIRST) {
+ /* leave this lock in place to indicate it's in use */
+ if (tdb_brlock(tdb, ACTIVE_LOCK, F_RDLCK, F_SETLKW, 0) == -1)
+ goto fail;
+ }
+
+
+ internal:
+ /* Internal (memory-only) databases skip all the code above to
+ * do with disk files, and resume here by releasing their
+ * global lock and hooking into the active list. */
+ if (tdb_brlock(tdb, GLOBAL_LOCK, F_UNLCK, F_SETLKW, 0) == -1)
+ goto fail;
+ tdb->next = tdbs;
+ tdbs = tdb;
+ return tdb;
+
+ fail:
+ { int save_errno = errno;
+
+ if (!tdb)
+ return NULL;
+
+ if (tdb->map_ptr) {
+ if (tdb->flags & TDB_INTERNAL)
+ SAFE_FREE(tdb->map_ptr);
+ else
+ tdb_munmap(tdb);
+ }
+ SAFE_FREE(tdb->name);
+ if (tdb->fd != -1)
+ if (close(tdb->fd) != 0)
+ TDB_LOG((tdb, 5, "tdb_open_ex: failed to close tdb->fd on error!\n"));
+ SAFE_FREE(tdb->locked);
+ SAFE_FREE(tdb);
+ errno = save_errno;
+ return NULL;
+ }
+}
+
+/**
+ * Close a database.
+ *
+ * @returns -1 for error; 0 for success.
+ **/
+int tdb_close(TDB_CONTEXT *tdb)
+{
+ TDB_CONTEXT **i;
+ int ret = 0;
+
+ if (tdb->map_ptr) {
+ if (tdb->flags & TDB_INTERNAL)
+ SAFE_FREE(tdb->map_ptr);
+ else
+ tdb_munmap(tdb);
+ }
+ SAFE_FREE(tdb->name);
+ if (tdb->fd != -1)
+ ret = close(tdb->fd);
+ SAFE_FREE(tdb->locked);
+ SAFE_FREE(tdb->lockedkeys);
+
+ /* Remove from contexts list */
+ for (i = &tdbs; *i; i = &(*i)->next) {
+ if (*i == tdb) {
+ *i = tdb->next;
+ break;
+ }
+ }
+
+ memset(tdb, 0, sizeof(*tdb));
+ SAFE_FREE(tdb);
+
+ return ret;
+}
+
+/* lock/unlock entire database */
+int tdb_lockall(TDB_CONTEXT *tdb)
+{
+ u32 i;
+
+ /* There are no locks on read-only dbs */
+ if (tdb->read_only)
+ return TDB_ERRCODE(TDB_ERR_LOCK, -1);
+ if (tdb->lockedkeys)
+ return TDB_ERRCODE(TDB_ERR_NOLOCK, -1);
+ for (i = 0; i < tdb->header.hash_size; i++)
+ if (tdb_lock(tdb, i, F_WRLCK))
+ break;
+
+ /* If error, release locks we have... */
+ if (i < tdb->header.hash_size) {
+ u32 j;
+
+ for ( j = 0; j < i; j++)
+ tdb_unlock(tdb, j, F_WRLCK);
+ return TDB_ERRCODE(TDB_ERR_NOLOCK, -1);
+ }
+
+ return 0;
+}
+void tdb_unlockall(TDB_CONTEXT *tdb)
+{
+ u32 i;
+ for (i=0; i < tdb->header.hash_size; i++)
+ tdb_unlock(tdb, i, F_WRLCK);
+}
+
+int tdb_lockkeys(TDB_CONTEXT *tdb, u32 number, TDB_DATA keys[])
+{
+ u32 i, j, hash;
+
+ /* Can't lock more keys if already locked */
+ if (tdb->lockedkeys)
+ return TDB_ERRCODE(TDB_ERR_NOLOCK, -1);
+ if (!(tdb->lockedkeys = malloc(sizeof(u32) * (number+1))))
+ return TDB_ERRCODE(TDB_ERR_OOM, -1);
+ /* First number in array is # keys */
+ tdb->lockedkeys[0] = number;
+
+ /* Insertion sort by bucket */
+ for (i = 0; i < number; i++) {
+ hash = tdb_hash(&keys[i]);
+ for (j = 0; j < i && BUCKET(tdb->lockedkeys[j+1]) < BUCKET(hash); j++);
+ memmove(&tdb->lockedkeys[j+2], &tdb->lockedkeys[j+1], sizeof(u32) * (i-j));
+ tdb->lockedkeys[j+1] = hash;
+ }
+ /* Finally, lock in order */
+ for (i = 0; i < number; i++)
+ if (tdb_lock(tdb, i, F_WRLCK))
+ break;
+
+ /* If error, release locks we have... */
+ if (i < number) {
+ for ( j = 0; j < i; j++)
+ tdb_unlock(tdb, j, F_WRLCK);
+ SAFE_FREE(tdb->lockedkeys);
+ return TDB_ERRCODE(TDB_ERR_NOLOCK, -1);
+ }
+ return 0;
+}
+
+/* Unlock the keys previously locked by tdb_lockkeys() */
+void tdb_unlockkeys(TDB_CONTEXT *tdb)
+{
+ u32 i;
+ if (!tdb->lockedkeys)
+ return;
+ for (i = 0; i < tdb->lockedkeys[0]; i++)
+ tdb_unlock(tdb, tdb->lockedkeys[i+1], F_WRLCK);
+ SAFE_FREE(tdb->lockedkeys);
+}
+
+/* lock/unlock one hash chain. This is meant to be used to reduce
+ contention - it cannot guarantee how many records will be locked */
+int tdb_chainlock(TDB_CONTEXT *tdb, TDB_DATA key)
+{
+ return tdb_lock(tdb, BUCKET(tdb_hash(&key)), F_WRLCK);
+}
+
+int tdb_chainunlock(TDB_CONTEXT *tdb, TDB_DATA key)
+{
+ return tdb_unlock(tdb, BUCKET(tdb_hash(&key)), F_WRLCK);
+}
+
+int tdb_chainlock_read(TDB_CONTEXT *tdb, TDB_DATA key)
+{
+ return tdb_lock(tdb, BUCKET(tdb_hash(&key)), F_RDLCK);
+}
+
+int tdb_chainunlock_read(TDB_CONTEXT *tdb, TDB_DATA key)
+{
+ return tdb_unlock(tdb, BUCKET(tdb_hash(&key)), F_RDLCK);
+}
+
+
+/* register a loging function */
+void tdb_logging_function(TDB_CONTEXT *tdb, void (*fn)(TDB_CONTEXT *, int , const char *, ...))
+{
+ tdb->log_fn = fn;
+}
+
+
+/* reopen a tdb - this can be used after a fork to ensure that we have an independent
+ seek pointer from our parent and to re-establish locks */
+int tdb_reopen(TDB_CONTEXT *tdb)
+{
+ struct stat st;
+
+ if (tdb->flags & TDB_INTERNAL)
+ return 0; /* Nothing to do. */
+ if (tdb_munmap(tdb) != 0) {
+ TDB_LOG((tdb, 0, "tdb_reopen: munmap failed (%s)\n", strerror(errno)));
+ goto fail;
+ }
+ if (close(tdb->fd) != 0)
+ TDB_LOG((tdb, 0, "tdb_reopen: WARNING closing tdb->fd failed!\n"));
+ tdb->fd = open(tdb->name, tdb->open_flags & ~(O_CREAT|O_TRUNC), 0);
+ if (tdb->fd == -1) {
+ TDB_LOG((tdb, 0, "tdb_reopen: open failed (%s)\n", strerror(errno)));
+ goto fail;
+ }
+ if (fstat(tdb->fd, &st) != 0) {
+ TDB_LOG((tdb, 0, "tdb_reopen: fstat failed (%s)\n", strerror(errno)));
+ goto fail;
+ }
+ if (st.st_ino != tdb->inode || st.st_dev != tdb->device) {
+ TDB_LOG((tdb, 0, "tdb_reopen: file dev/inode has changed!\n"));
+ goto fail;
+ }
+ tdb_mmap(tdb);
+ if ((tdb->flags & TDB_CLEAR_IF_FIRST) && (tdb_brlock(tdb, ACTIVE_LOCK, F_RDLCK, F_SETLKW, 0) == -1)) {
+ TDB_LOG((tdb, 0, "tdb_reopen: failed to obtain active lock\n"));
+ goto fail;
+ }
+
+ return 0;
+
+fail:
+ tdb_close(tdb);
+ return -1;
+}
+
+/* reopen all tdb's */
+int tdb_reopen_all(void)
+{
+ TDB_CONTEXT *tdb;
+
+ for (tdb=tdbs; tdb; tdb = tdb->next) {
+ /* Ensure no clear-if-first. */
+ tdb->flags &= ~TDB_CLEAR_IF_FIRST;
+ if (tdb_reopen(tdb) != 0)
+ return -1;
+ }
+
+ return 0;
+}
diff --git a/source/tdb/tdb.h b/source/tdb/tdb.h
new file mode 100644
index 00000000000..eb120a8cecd
--- /dev/null
+++ b/source/tdb/tdb.h
@@ -0,0 +1,151 @@
+#ifndef __TDB_H__
+#define __TDB_H__
+
+/*
+ Unix SMB/CIFS implementation.
+
+ trivial database library
+
+ Copyright (C) Andrew Tridgell 1999-2004
+
+ ** NOTE! The following LGPL license applies to the tdb
+ ** 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 2 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, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+*/
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+/* flags to tdb_store() */
+#define TDB_REPLACE 1
+#define TDB_INSERT 2
+#define TDB_MODIFY 3
+
+/* flags for tdb_open() */
+#define TDB_DEFAULT 0 /* just a readability place holder */
+#define TDB_CLEAR_IF_FIRST 1
+#define TDB_INTERNAL 2 /* don't store on disk */
+#define TDB_NOLOCK 4 /* don't do any locking */
+#define TDB_NOMMAP 8 /* don't use mmap */
+#define TDB_CONVERT 16 /* convert endian (internal use) */
+#define TDB_BIGENDIAN 32 /* header is big-endian (internal use) */
+
+#define TDB_ERRCODE(code, ret) ((tdb->ecode = (code)), ret)
+
+/* error codes */
+enum TDB_ERROR {TDB_SUCCESS=0, TDB_ERR_CORRUPT, TDB_ERR_IO, TDB_ERR_LOCK,
+ TDB_ERR_OOM, TDB_ERR_EXISTS, TDB_ERR_NOLOCK, TDB_ERR_LOCK_TIMEOUT,
+ TDB_ERR_NOEXIST};
+
+#ifndef u32
+#define u32 unsigned
+#endif
+
+typedef struct {
+ char *dptr;
+ size_t dsize;
+} TDB_DATA;
+
+typedef u32 tdb_len;
+typedef u32 tdb_off;
+
+/* this is stored at the front of every database */
+struct tdb_header {
+ char magic_food[32]; /* for /etc/magic */
+ u32 version; /* version of the code */
+ u32 hash_size; /* number of hash entries */
+ tdb_off rwlocks;
+ tdb_off reserved[31];
+};
+
+struct tdb_lock_type {
+ u32 count;
+ u32 ltype;
+};
+
+struct tdb_traverse_lock {
+ struct tdb_traverse_lock *next;
+ u32 off;
+ u32 hash;
+};
+
+/* this is the context structure that is returned from a db open */
+typedef struct tdb_context {
+ char *name; /* the name of the database */
+ void *map_ptr; /* where it is currently mapped */
+ int fd; /* open file descriptor for the database */
+ tdb_len map_size; /* how much space has been mapped */
+ int read_only; /* opened read-only */
+ struct tdb_lock_type *locked; /* array of chain locks */
+ enum TDB_ERROR ecode; /* error code for last tdb error */
+ struct tdb_header header; /* a cached copy of the header */
+ u32 flags; /* the flags passed to tdb_open */
+ u32 *lockedkeys; /* array of locked keys: first is #keys */
+ struct tdb_traverse_lock travlocks; /* current traversal locks */
+ struct tdb_context *next; /* all tdbs to avoid multiple opens */
+ dev_t device; /* uniquely identifies this tdb */
+ ino_t inode; /* uniquely identifies this tdb */
+ void (*log_fn)(struct tdb_context *tdb, int level, const char *, ...); /* logging function */
+ int open_flags; /* flags used in the open - needed by reopen */
+} TDB_CONTEXT;
+
+typedef int (*tdb_traverse_func)(TDB_CONTEXT *, TDB_DATA, TDB_DATA, void *);
+typedef void (*tdb_log_func)(TDB_CONTEXT *, int , const char *, ...);
+
+TDB_CONTEXT *tdb_open(const char *name, int hash_size, int tdb_flags,
+ int open_flags, mode_t mode);
+TDB_CONTEXT *tdb_open_ex(const char *name, int hash_size, int tdb_flags,
+ int open_flags, mode_t mode,
+ tdb_log_func log_fn);
+
+int tdb_reopen(TDB_CONTEXT *tdb);
+int tdb_reopen_all(void);
+void tdb_logging_function(TDB_CONTEXT *tdb, tdb_log_func);
+enum TDB_ERROR tdb_error(TDB_CONTEXT *tdb);
+const char *tdb_errorstr(TDB_CONTEXT *tdb);
+TDB_DATA tdb_fetch(TDB_CONTEXT *tdb, TDB_DATA key);
+int tdb_delete(TDB_CONTEXT *tdb, TDB_DATA key);
+int tdb_store(TDB_CONTEXT *tdb, TDB_DATA key, TDB_DATA dbuf, int flag);
+int tdb_append(TDB_CONTEXT *tdb, TDB_DATA key, TDB_DATA new_dbuf);
+int tdb_close(TDB_CONTEXT *tdb);
+TDB_DATA tdb_firstkey(TDB_CONTEXT *tdb);
+TDB_DATA tdb_nextkey(TDB_CONTEXT *tdb, TDB_DATA key);
+int tdb_traverse(TDB_CONTEXT *tdb, tdb_traverse_func fn, void *);
+int tdb_exists(TDB_CONTEXT *tdb, TDB_DATA key);
+int tdb_lockkeys(TDB_CONTEXT *tdb, u32 number, TDB_DATA keys[]);
+void tdb_unlockkeys(TDB_CONTEXT *tdb);
+int tdb_lockall(TDB_CONTEXT *tdb);
+void tdb_unlockall(TDB_CONTEXT *tdb);
+
+/* Low level locking functions: use with care */
+void tdb_set_lock_alarm(sig_atomic_t *palarm);
+int tdb_chainlock(TDB_CONTEXT *tdb, TDB_DATA key);
+int tdb_chainunlock(TDB_CONTEXT *tdb, TDB_DATA key);
+
+/* Debug functions. Not used in production. */
+void tdb_dump_all(TDB_CONTEXT *tdb);
+int tdb_printfreelist(TDB_CONTEXT *tdb);
+
+extern TDB_DATA tdb_null;
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* tdb.h */
diff --git a/source/tdb/tdb.magic b/source/tdb/tdb.magic
new file mode 100644
index 00000000000..f5619e7327e
--- /dev/null
+++ b/source/tdb/tdb.magic
@@ -0,0 +1,10 @@
+# Magic file(1) information about tdb files.
+#
+# Install this into /etc/magic or the corresponding location for your
+# system, or pass as a -m argument to file(1).
+
+# You may use and redistribute this file without restriction.
+
+0 string TDB\ file TDB database
+>32 lelong =0x2601196D version 6, little-endian
+>>36 lelong x hash size %d bytes
diff --git a/source/tdb/tdbback.c b/source/tdb/tdbback.c
new file mode 100644
index 00000000000..68b6fadc882
--- /dev/null
+++ b/source/tdb/tdbback.c
@@ -0,0 +1,211 @@
+/*
+ Unix SMB/CIFS implementation.
+ low level tdb backup and restore utility
+ 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 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.
+*/
+
+#ifdef STANDALONE
+#if HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <errno.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <string.h>
+#include <fcntl.h>
+#include <time.h>
+#include <sys/mman.h>
+
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <ctype.h>
+#include <signal.h>
+
+#else
+#include "includes.h"
+#endif
+
+#include "tdb.h"
+
+static int failed;
+
+char *add_suffix(const char *name, const char *suffix)
+{
+ char *ret;
+ int len = strlen(name) + strlen(suffix) + 1;
+ ret = malloc(len);
+ if (!ret) {
+ fprintf(stderr,"Out of memory!\n");
+ exit(1);
+ }
+ snprintf(ret, len, "%s%s", name, suffix);
+ return ret;
+}
+
+static int copy_fn(TDB_CONTEXT *tdb, TDB_DATA key, TDB_DATA dbuf, void *state)
+{
+ TDB_CONTEXT *tdb_new = (TDB_CONTEXT *)state;
+
+ if (tdb_store(tdb_new, key, dbuf, TDB_INSERT) != 0) {
+ fprintf(stderr,"Failed to insert into %s\n", tdb_new->name);
+ failed = 1;
+ return 1;
+ }
+ return 0;
+}
+
+
+static int test_fn(TDB_CONTEXT *tdb, TDB_DATA key, TDB_DATA dbuf, void *state)
+{
+ return 0;
+}
+
+/*
+ carefully backup a tdb, validating the contents and
+ only doing the backup if its OK
+ this function is also used for restore
+*/
+int backup_tdb(const char *old_name, const char *new_name)
+{
+ TDB_CONTEXT *tdb;
+ TDB_CONTEXT *tdb_new;
+ char *tmp_name;
+ struct stat st;
+ int count1, count2;
+
+ tmp_name = add_suffix(new_name, ".tmp");
+
+ /* stat the old tdb to find its permissions */
+ if (stat(old_name, &st) != 0) {
+ perror(old_name);
+ return 1;
+ }
+
+ /* open the old tdb */
+ tdb = tdb_open(old_name, 0, 0, O_RDWR, 0);
+ if (!tdb) {
+ printf("Failed to open %s\n", old_name);
+ return 1;
+ }
+
+ /* create the new tdb */
+ unlink(tmp_name);
+ tdb_new = tdb_open(tmp_name, tdb->header.hash_size,
+ TDB_DEFAULT, O_RDWR|O_CREAT|O_EXCL,
+ st.st_mode & 0777);
+ if (!tdb_new) {
+ perror(tmp_name);
+ free(tmp_name);
+ return 1;
+ }
+
+ /* lock the old tdb */
+ if (tdb_lockall(tdb) != 0) {
+ fprintf(stderr,"Failed to lock %s\n", old_name);
+ tdb_close(tdb);
+ tdb_close(tdb_new);
+ unlink(tmp_name);
+ free(tmp_name);
+ return 1;
+ }
+
+ failed = 0;
+
+ /* traverse and copy */
+ count1 = tdb_traverse(tdb, copy_fn, (void *)tdb_new);
+ if (count1 < 0 || failed) {
+ fprintf(stderr,"failed to copy %s\n", old_name);
+ tdb_close(tdb);
+ tdb_close(tdb_new);
+ unlink(tmp_name);
+ free(tmp_name);
+ return 1;
+ }
+
+ /* close the old tdb */
+ tdb_close(tdb);
+
+ /* close the new tdb and re-open read-only */
+ tdb_close(tdb_new);
+ tdb_new = tdb_open(tmp_name, 0, TDB_DEFAULT, O_RDONLY, 0);
+ if (!tdb_new) {
+ fprintf(stderr,"failed to reopen %s\n", tmp_name);
+ unlink(tmp_name);
+ perror(tmp_name);
+ free(tmp_name);
+ return 1;
+ }
+
+ /* traverse the new tdb to confirm */
+ count2 = tdb_traverse(tdb_new, test_fn, 0);
+ if (count2 != count1) {
+ fprintf(stderr,"failed to copy %s\n", old_name);
+ tdb_close(tdb_new);
+ unlink(tmp_name);
+ free(tmp_name);
+ return 1;
+ }
+
+ /* make sure the new tdb has reached stable storage */
+ fsync(tdb_new->fd);
+
+ /* close the new tdb and rename it to .bak */
+ tdb_close(tdb_new);
+ unlink(new_name);
+ if (rename(tmp_name, new_name) != 0) {
+ perror(new_name);
+ free(tmp_name);
+ return 1;
+ }
+
+ free(tmp_name);
+
+ return 0;
+}
+
+
+
+/*
+ verify a tdb and if it is corrupt then restore from *.bak
+*/
+int verify_tdb(const char *fname, const char *bak_name)
+{
+ TDB_CONTEXT *tdb;
+ int count = -1;
+
+ /* open the tdb */
+ tdb = tdb_open(fname, 0, 0, O_RDONLY, 0);
+
+ /* traverse the tdb, then close it */
+ if (tdb) {
+ count = tdb_traverse(tdb, test_fn, NULL);
+ tdb_close(tdb);
+ }
+
+ /* count is < 0 means an error */
+ if (count < 0) {
+ printf("restoring %s\n", fname);
+ return backup_tdb(bak_name, fname);
+ }
+
+ printf("%s : %d records\n", fname, count);
+
+ return 0;
+}
diff --git a/source/tdb/tdbback.h b/source/tdb/tdbback.h
new file mode 100644
index 00000000000..7ebeaa494d6
--- /dev/null
+++ b/source/tdb/tdbback.h
@@ -0,0 +1,23 @@
+/*
+ Unix SMB/CIFS implementation.
+ low level tdb backup and restore utility
+ 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 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.
+*/
+
+char *add_suffix(const char *name, const char *suffix);
+int backup_tdb(const char *old_name, const char *new_name);
+int verify_tdb(const char *fname, const char *bak_name);
diff --git a/source/tdb/tdbbackup.c b/source/tdb/tdbbackup.c
new file mode 100644
index 00000000000..1a0e1c1588f
--- /dev/null
+++ b/source/tdb/tdbbackup.c
@@ -0,0 +1,149 @@
+/*
+ Unix SMB/CIFS implementation.
+ low level tdb backup and restore utility
+ 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 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.
+*/
+
+/*
+
+ This program is meant for backup/restore of tdb databases. Typical usage would be:
+ tdbbackup *.tdb
+ when Samba shuts down cleanly, which will make a backup of all the local databases
+ to *.bak files. Then on Samba startup you would use:
+ tdbbackup -v *.tdb
+ and this will check the databases for corruption and if corruption is detected then
+ the backup will be restored.
+
+ You may also like to do a backup on a regular basis while Samba is
+ running, perhaps using cron.
+
+ The reason this program is needed is to cope with power failures
+ while Samba is running. A power failure could lead to database
+ corruption and Samba will then not start correctly.
+
+ Note that many of the databases in Samba are transient and thus
+ don't need to be backed up, so you can optimise the above a little
+ by only running the backup on the critical databases.
+
+ */
+
+#ifdef STANDALONE
+#if HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <errno.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <string.h>
+#include <fcntl.h>
+#include <time.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <ctype.h>
+#include <signal.h>
+
+#else
+
+#include "includes.h"
+
+#endif
+
+#include "tdb.h"
+#include "tdbback.h"
+
+/*
+ see if one file is newer than another
+*/
+static int file_newer(const char *fname1, const char *fname2)
+{
+ struct stat st1, st2;
+ if (stat(fname1, &st1) != 0) {
+ return 0;
+ }
+ if (stat(fname2, &st2) != 0) {
+ return 1;
+ }
+ return (st1.st_mtime > st2.st_mtime);
+}
+
+static void usage(void)
+{
+ printf("Usage: tdbbackup [options] <fname...>\n\n");
+ printf(" -h this help message\n");
+ printf(" -s suffix set the backup suffix\n");
+ printf(" -v verify mode (restore if corrupt)\n");
+}
+
+
+ int main(int argc, char *argv[])
+{
+ int i;
+ int ret = 0;
+ int c;
+ int verify = 0;
+ const char *suffix = ".bak";
+ extern int optind;
+ extern char *optarg;
+
+ while ((c = getopt(argc, argv, "vhs:")) != -1) {
+ switch (c) {
+ case 'h':
+ usage();
+ exit(0);
+ case 'v':
+ verify = 1;
+ break;
+ case 's':
+ suffix = optarg;
+ break;
+ }
+ }
+
+ argc -= optind;
+ argv += optind;
+
+ if (argc < 1) {
+ usage();
+ exit(1);
+ }
+
+ for (i=0; i<argc; i++) {
+ const char *fname = argv[i];
+ char *bak_name;
+
+ bak_name = add_suffix(fname, suffix);
+
+ if (verify) {
+ if (verify_tdb(fname, bak_name) != 0) {
+ ret = 1;
+ }
+ } else {
+ if (file_newer(fname, bak_name) &&
+ backup_tdb(fname, bak_name) != 0) {
+ ret = 1;
+ }
+ }
+
+ free(bak_name);
+ }
+
+ return ret;
+}
diff --git a/source/tdb/tdbdump.c b/source/tdb/tdbdump.c
new file mode 100644
index 00000000000..d2530f9f54c
--- /dev/null
+++ b/source/tdb/tdbdump.c
@@ -0,0 +1,88 @@
+/*
+ Unix SMB/CIFS implementation.
+ simple tdb dump util
+ 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 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 <errno.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <string.h>
+#include <fcntl.h>
+#include <time.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <ctype.h>
+#include <signal.h>
+#include "tdb.h"
+
+static void print_data(TDB_DATA d)
+{
+ unsigned char *p = d.dptr;
+ int len = d.dsize;
+ while (len--) {
+ if (isprint(*p) && !strchr("\"\\", *p)) {
+ fputc(*p, stdout);
+ } else {
+ printf("\\%02X", *p);
+ }
+ p++;
+ }
+}
+
+static int traverse_fn(TDB_CONTEXT *tdb, TDB_DATA key, TDB_DATA dbuf, void *state)
+{
+ printf("{\n");
+ printf("key = \"");
+ print_data(key);
+ printf("\"\n");
+ printf("data = \"");
+ print_data(dbuf);
+ printf("\"\n");
+ printf("}\n");
+ return 0;
+}
+
+static int dump_tdb(const char *fname)
+{
+ TDB_CONTEXT *tdb;
+
+ tdb = tdb_open(fname, 0, 0, O_RDONLY, 0);
+ if (!tdb) {
+ printf("Failed to open %s\n", fname);
+ return 1;
+ }
+
+ tdb_traverse(tdb, traverse_fn, NULL);
+ return 0;
+}
+
+ int main(int argc, char *argv[])
+{
+ char *fname;
+
+ if (argc < 2) {
+ printf("Usage: tdbdump <fname>\n");
+ exit(1);
+ }
+
+ fname = argv[1];
+
+ return dump_tdb(fname);
+}
diff --git a/source/tdb/tdbtest.c b/source/tdb/tdbtest.c
new file mode 100644
index 00000000000..89295a3291f
--- /dev/null
+++ b/source/tdb/tdbtest.c
@@ -0,0 +1,263 @@
+#include <stdlib.h>
+#include <stdio.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <string.h>
+#include <fcntl.h>
+#include <stdarg.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <signal.h>
+#include "tdb.h"
+#include <gdbm.h>
+
+/* a test program for tdb - the trivial database */
+
+
+
+#define DELETE_PROB 7
+#define STORE_PROB 5
+
+static TDB_CONTEXT *db;
+static GDBM_FILE gdbm;
+
+struct timeval tp1,tp2;
+
+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 fatal(char *why)
+{
+ perror(why);
+ exit(1);
+}
+
+static void tdb_log(TDB_CONTEXT *tdb, int level, const char *format, ...)
+{
+ va_list ap;
+
+ va_start(ap, format);
+ vfprintf(stdout, format, ap);
+ va_end(ap);
+ fflush(stdout);
+}
+
+static void compare_db(void)
+{
+ TDB_DATA d, key, nextkey;
+ datum gd, gkey, gnextkey;
+
+ key = tdb_firstkey(db);
+ while (key.dptr) {
+ d = tdb_fetch(db, key);
+ gkey.dptr = key.dptr;
+ gkey.dsize = key.dsize;
+
+ gd = gdbm_fetch(gdbm, gkey);
+
+ if (!gd.dptr) fatal("key not in gdbm");
+ if (gd.dsize != d.dsize) fatal("data sizes differ");
+ if (memcmp(gd.dptr, d.dptr, d.dsize)) {
+ fatal("data differs");
+ }
+
+ nextkey = tdb_nextkey(db, key);
+ free(key.dptr);
+ free(d.dptr);
+ free(gd.dptr);
+ key = nextkey;
+ }
+
+ gkey = gdbm_firstkey(gdbm);
+ while (gkey.dptr) {
+ gd = gdbm_fetch(gdbm, gkey);
+ key.dptr = gkey.dptr;
+ key.dsize = gkey.dsize;
+
+ d = tdb_fetch(db, key);
+
+ if (!d.dptr) fatal("key not in db");
+ if (d.dsize != gd.dsize) fatal("data sizes differ");
+ if (memcmp(d.dptr, gd.dptr, gd.dsize)) {
+ fatal("data differs");
+ }
+
+ gnextkey = gdbm_nextkey(gdbm, gkey);
+ free(gkey.dptr);
+ free(gd.dptr);
+ free(d.dptr);
+ gkey = gnextkey;
+ }
+}
+
+static char *randbuf(int len)
+{
+ char *buf;
+ int i;
+ buf = (char *)malloc(len+1);
+
+ for (i=0;i<len;i++) {
+ buf[i] = 'a' + (rand() % 26);
+ }
+ buf[i] = 0;
+ return buf;
+}
+
+static void addrec_db(void)
+{
+ int klen, dlen;
+ char *k, *d;
+ TDB_DATA key, data;
+
+ klen = 1 + (rand() % 4);
+ dlen = 1 + (rand() % 100);
+
+ k = randbuf(klen);
+ d = randbuf(dlen);
+
+ key.dptr = k;
+ key.dsize = klen+1;
+
+ data.dptr = d;
+ data.dsize = dlen+1;
+
+ if (rand() % DELETE_PROB == 0) {
+ tdb_delete(db, key);
+ } else if (rand() % STORE_PROB == 0) {
+ if (tdb_store(db, key, data, TDB_REPLACE) != 0) {
+ fatal("tdb_store failed");
+ }
+ } else {
+ data = tdb_fetch(db, key);
+ if (data.dptr) free(data.dptr);
+ }
+
+ free(k);
+ free(d);
+}
+
+static void addrec_gdbm(void)
+{
+ int klen, dlen;
+ char *k, *d;
+ datum key, data;
+
+ klen = 1 + (rand() % 4);
+ dlen = 1 + (rand() % 100);
+
+ k = randbuf(klen);
+ d = randbuf(dlen);
+
+ key.dptr = k;
+ key.dsize = klen+1;
+
+ data.dptr = d;
+ data.dsize = dlen+1;
+
+ if (rand() % DELETE_PROB == 0) {
+ gdbm_delete(gdbm, key);
+ } else if (rand() % STORE_PROB == 0) {
+ if (gdbm_store(gdbm, key, data, GDBM_REPLACE) != 0) {
+ fatal("gdbm_store failed");
+ }
+ } else {
+ data = gdbm_fetch(gdbm, key);
+ if (data.dptr) free(data.dptr);
+ }
+
+ free(k);
+ free(d);
+}
+
+static int traverse_fn(TDB_CONTEXT *tdb, TDB_DATA key, TDB_DATA dbuf, void *state)
+{
+#if 0
+ printf("[%s] [%s]\n", key.dptr, dbuf.dptr);
+#endif
+ tdb_delete(tdb, key);
+ return 0;
+}
+
+static void merge_test(void)
+{
+ int i;
+ char keys[5][2];
+ TDB_DATA key, data;
+
+ for (i = 0; i < 5; i++) {
+ sprintf(keys[i], "%d", i);
+ key.dptr = keys[i];
+ key.dsize = 2;
+
+ data.dptr = "test";
+ data.dsize = 4;
+
+ if (tdb_store(db, key, data, TDB_REPLACE) != 0) {
+ fatal("tdb_store failed");
+ }
+ }
+
+ key.dptr = keys[0];
+ tdb_delete(db, key);
+ key.dptr = keys[4];
+ tdb_delete(db, key);
+ key.dptr = keys[2];
+ tdb_delete(db, key);
+ key.dptr = keys[1];
+ tdb_delete(db, key);
+ key.dptr = keys[3];
+ tdb_delete(db, key);
+}
+
+int main(int argc, char *argv[])
+{
+ int i, seed=0;
+ int loops = 10000;
+
+ unlink("test.gdbm");
+
+ db = tdb_open("test.tdb", 0, TDB_CLEAR_IF_FIRST,
+ O_RDWR | O_CREAT | O_TRUNC, 0600);
+ gdbm = gdbm_open("test.gdbm", 512, GDBM_WRITER|GDBM_NEWDB|GDBM_FAST,
+ 0600, NULL);
+
+ if (!db || !gdbm) {
+ fatal("db open failed");
+ }
+
+ tdb_logging_function(db, tdb_log);
+
+#if 1
+ srand(seed);
+ start_timer();
+ for (i=0;i<loops;i++) addrec_gdbm();
+ printf("gdbm got %.2f ops/sec\n", i/end_timer());
+#endif
+
+ merge_test();
+
+ srand(seed);
+ start_timer();
+ for (i=0;i<loops;i++) addrec_db();
+ printf("tdb got %.2f ops/sec\n", i/end_timer());
+
+ compare_db();
+
+ printf("traversed %d records\n", tdb_traverse(db, traverse_fn, NULL));
+ printf("traversed %d records\n", tdb_traverse(db, traverse_fn, NULL));
+
+ tdb_close(db);
+ gdbm_close(gdbm);
+
+ return 0;
+}
diff --git a/source/tdb/tdbtool.c b/source/tdb/tdbtool.c
new file mode 100644
index 00000000000..92009dcef48
--- /dev/null
+++ b/source/tdb/tdbtool.c
@@ -0,0 +1,547 @@
+/*
+ Unix SMB/CIFS implementation.
+ Samba database functions
+ Copyright (C) Andrew Tridgell 1999-2000
+ Copyright (C) Paul `Rusty' Russell 2000
+ Copyright (C) Jeremy Allison 2000
+ Copyright (C) Andrew Esh 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 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 <errno.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <string.h>
+#include <fcntl.h>
+#include <time.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <ctype.h>
+#include <signal.h>
+#include "tdb.h"
+
+/* a tdb tool for manipulating a tdb database */
+
+#define FSTRING_LEN 256
+typedef char fstring[FSTRING_LEN];
+
+typedef struct connections_key {
+ pid_t pid;
+ int cnum;
+ fstring name;
+} connections_key;
+
+typedef struct connections_data {
+ int magic;
+ pid_t pid;
+ int cnum;
+ uid_t uid;
+ gid_t gid;
+ char name[24];
+ char addr[24];
+ char machine[128];
+ time_t start;
+} connections_data;
+
+static TDB_CONTEXT *tdb;
+
+static int print_rec(TDB_CONTEXT *the_tdb, TDB_DATA key, TDB_DATA dbuf, void *state);
+
+static void print_asc(unsigned char *buf,int len)
+{
+ int i;
+
+ /* We're probably printing ASCII strings so don't try to display
+ the trailing NULL character. */
+
+ if (buf[len - 1] == 0)
+ len--;
+
+ for (i=0;i<len;i++)
+ printf("%c",isprint(buf[i])?buf[i]:'.');
+}
+
+static void print_data(unsigned char *buf,int len)
+{
+ int i=0;
+ if (len<=0) return;
+ printf("[%03X] ",i);
+ for (i=0;i<len;) {
+ printf("%02X ",(int)buf[i]);
+ i++;
+ if (i%8 == 0) printf(" ");
+ if (i%16 == 0) {
+ print_asc(&buf[i-16],8); printf(" ");
+ print_asc(&buf[i-8],8); printf("\n");
+ if (i<len) printf("[%03X] ",i);
+ }
+ }
+ if (i%16) {
+ int n;
+
+ n = 16 - (i%16);
+ printf(" ");
+ if (n>8) printf(" ");
+ while (n--) printf(" ");
+
+ n = i%16;
+ if (n > 8) n = 8;
+ print_asc(&buf[i-(i%16)],n); printf(" ");
+ n = (i%16) - n;
+ if (n>0) print_asc(&buf[i-n],n);
+ printf("\n");
+ }
+}
+
+static void help(void)
+{
+ printf("\n"
+"tdbtool: \n"
+" create dbname : create a database\n"
+" open dbname : open an existing database\n"
+" erase : erase the database\n"
+" dump : dump the database as strings\n"
+" insert key data : insert a record\n"
+" move key file : move a record to a destination tdb\n"
+" store key data : store a record (replace)\n"
+" show key : show a record by key\n"
+" delete key : delete a record by key\n"
+" list : print the database hash table and freelist\n"
+" free : print the database freelist\n"
+" 1 | first : print the first record\n"
+" n | next : print the next record\n"
+" q | quit : terminate\n"
+" \\n : repeat 'next' command\n"
+"\n");
+}
+
+static void terror(char *why)
+{
+ printf("%s\n", why);
+}
+
+static char *get_token(int startover)
+{
+ static char tmp[1024];
+ static char *cont = NULL;
+ char *insert, *start;
+ char *k = strtok(NULL, " ");
+
+ if (!k)
+ return NULL;
+
+ if (startover)
+ start = tmp;
+ else
+ start = cont;
+
+ strcpy(start, k);
+ insert = start + strlen(start) - 1;
+ while (*insert == '\\') {
+ *insert++ = ' ';
+ k = strtok(NULL, " ");
+ if (!k)
+ break;
+ strcpy(insert, k);
+ insert = start + strlen(start) - 1;
+ }
+
+ /* Get ready for next call */
+ cont = start + strlen(start) + 1;
+ return start;
+}
+
+static void create_tdb(void)
+{
+ char *tok = get_token(1);
+ if (!tok) {
+ help();
+ return;
+ }
+ if (tdb) tdb_close(tdb);
+ tdb = tdb_open(tok, 0, TDB_CLEAR_IF_FIRST,
+ O_RDWR | O_CREAT | O_TRUNC, 0600);
+ if (!tdb) {
+ printf("Could not create %s: %s\n", tok, strerror(errno));
+ }
+}
+
+static void open_tdb(void)
+{
+ char *tok = get_token(1);
+ if (!tok) {
+ help();
+ return;
+ }
+ if (tdb) tdb_close(tdb);
+ tdb = tdb_open(tok, 0, 0, O_RDWR, 0600);
+ if (!tdb) {
+ printf("Could not open %s: %s\n", tok, strerror(errno));
+ }
+}
+
+static void insert_tdb(void)
+{
+ char *k = get_token(1);
+ char *d = get_token(0);
+ TDB_DATA key, dbuf;
+
+ if (!k || !d) {
+ help();
+ return;
+ }
+
+ key.dptr = k;
+ key.dsize = strlen(k)+1;
+ dbuf.dptr = d;
+ dbuf.dsize = strlen(d)+1;
+
+ if (tdb_store(tdb, key, dbuf, TDB_INSERT) == -1) {
+ terror("insert failed");
+ }
+}
+
+static void store_tdb(void)
+{
+ char *k = get_token(1);
+ char *d = get_token(0);
+ TDB_DATA key, dbuf;
+
+ if (!k || !d) {
+ help();
+ return;
+ }
+
+ key.dptr = k;
+ key.dsize = strlen(k)+1;
+ dbuf.dptr = d;
+ dbuf.dsize = strlen(d)+1;
+
+ printf("Storing key:\n");
+ print_rec(tdb, key, dbuf, NULL);
+
+ if (tdb_store(tdb, key, dbuf, TDB_REPLACE) == -1) {
+ terror("store failed");
+ }
+}
+
+static void show_tdb(void)
+{
+ char *k = get_token(1);
+ TDB_DATA key, dbuf;
+
+ if (!k) {
+ help();
+ return;
+ }
+
+ key.dptr = k;
+ key.dsize = strlen(k)+1;
+
+ dbuf = tdb_fetch(tdb, key);
+ if (!dbuf.dptr) {
+ /* maybe it is non-NULL terminated key? */
+ key.dsize = strlen(k);
+ dbuf = tdb_fetch(tdb, key);
+
+ if ( !dbuf.dptr ) {
+ terror("fetch failed");
+ return;
+ }
+ }
+
+ /* printf("%s : %*.*s\n", k, (int)dbuf.dsize, (int)dbuf.dsize, dbuf.dptr); */
+ print_rec(tdb, key, dbuf, NULL);
+
+ free( dbuf.dptr );
+
+ return;
+}
+
+static void delete_tdb(void)
+{
+ char *k = get_token(1);
+ TDB_DATA key;
+
+ if (!k) {
+ help();
+ return;
+ }
+
+ key.dptr = k;
+ key.dsize = strlen(k)+1;
+
+ if (tdb_delete(tdb, key) != 0) {
+ terror("delete failed");
+ }
+}
+
+static void move_rec(void)
+{
+ char *k = get_token(1);
+ char *file = get_token(0);
+ TDB_DATA key, dbuf;
+ TDB_CONTEXT *dst_tdb;
+
+ if (!k) {
+ help();
+ return;
+ }
+
+ if ( !file ) {
+ terror("need destination tdb name");
+ return;
+ }
+
+ key.dptr = k;
+ key.dsize = strlen(k)+1;
+
+ dbuf = tdb_fetch(tdb, key);
+ if (!dbuf.dptr) {
+ /* maybe it is non-NULL terminated key? */
+ key.dsize = strlen(k);
+ dbuf = tdb_fetch(tdb, key);
+
+ if ( !dbuf.dptr ) {
+ terror("fetch failed");
+ return;
+ }
+ }
+
+ print_rec(tdb, key, dbuf, NULL);
+
+ dst_tdb = tdb_open(file, 0, 0, O_RDWR, 0600);
+ if ( !dst_tdb ) {
+ terror("unable to open destination tdb");
+ return;
+ }
+
+ if ( tdb_store( dst_tdb, key, dbuf, TDB_REPLACE ) == -1 ) {
+ terror("failed to move record");
+ }
+ else
+ printf("record moved\n");
+
+ tdb_close( dst_tdb );
+
+ return;
+}
+
+#if 0
+static int print_conn_key(TDB_DATA key)
+{
+ printf( "pid =%5d ", ((connections_key*)key.dptr)->pid);
+ printf( "cnum =%10d ", ((connections_key*)key.dptr)->cnum);
+ printf( "name =[%s]\n", ((connections_key*)key.dptr)->name);
+ return 0;
+}
+
+static int print_conn_data(TDB_DATA dbuf)
+{
+ printf( "pid =%5d ", ((connections_data*)dbuf.dptr)->pid);
+ printf( "cnum =%10d ", ((connections_data*)dbuf.dptr)->cnum);
+ printf( "name =[%s]\n", ((connections_data*)dbuf.dptr)->name);
+
+ printf( "uid =%5d ", ((connections_data*)dbuf.dptr)->uid);
+ printf( "addr =[%s]\n", ((connections_data*)dbuf.dptr)->addr);
+ printf( "gid =%5d ", ((connections_data*)dbuf.dptr)->gid);
+ printf( "machine=[%s]\n", ((connections_data*)dbuf.dptr)->machine);
+ printf( "start = %s\n", ctime(&((connections_data*)dbuf.dptr)->start));
+ return 0;
+}
+#endif
+
+static int print_rec(TDB_CONTEXT *the_tdb, TDB_DATA key, TDB_DATA dbuf, void *state)
+{
+#if 0
+ print_conn_key(key);
+ print_conn_data(dbuf);
+ return 0;
+#else
+ printf("\nkey %d bytes\n", key.dsize);
+ print_asc(key.dptr, key.dsize);
+ printf("\ndata %d bytes\n", dbuf.dsize);
+ print_data(dbuf.dptr, dbuf.dsize);
+ return 0;
+#endif
+}
+
+static int print_key(TDB_CONTEXT *the_tdb, TDB_DATA key, TDB_DATA dbuf, void *state)
+{
+ print_asc(key.dptr, key.dsize);
+ printf("\n");
+ return 0;
+}
+
+static int total_bytes;
+
+static int traverse_fn(TDB_CONTEXT *the_tdb, TDB_DATA key, TDB_DATA dbuf, void *state)
+{
+ total_bytes += dbuf.dsize;
+ return 0;
+}
+
+static void info_tdb(void)
+{
+ int count;
+ total_bytes = 0;
+ if ((count = tdb_traverse(tdb, traverse_fn, NULL) == -1))
+ printf("Error = %s\n", tdb_errorstr(tdb));
+ else
+ printf("%d records totalling %d bytes\n", count, total_bytes);
+}
+
+static char *tdb_getline(char *prompt)
+{
+ static char line[1024];
+ char *p;
+ fputs(prompt, stdout);
+ line[0] = 0;
+ p = fgets(line, sizeof(line)-1, stdin);
+ if (p) p = strchr(p, '\n');
+ if (p) *p = 0;
+ return p?line:NULL;
+}
+
+static int do_delete_fn(TDB_CONTEXT *the_tdb, TDB_DATA key, TDB_DATA dbuf,
+ void *state)
+{
+ return tdb_delete(the_tdb, key);
+}
+
+static void first_record(TDB_CONTEXT *the_tdb, TDB_DATA *pkey)
+{
+ TDB_DATA dbuf;
+ *pkey = tdb_firstkey(the_tdb);
+
+ dbuf = tdb_fetch(the_tdb, *pkey);
+ if (!dbuf.dptr) terror("fetch failed");
+ else {
+ /* printf("%s : %*.*s\n", k, (int)dbuf.dsize, (int)dbuf.dsize, dbuf.dptr); */
+ print_rec(the_tdb, *pkey, dbuf, NULL);
+ }
+}
+
+static void next_record(TDB_CONTEXT *the_tdb, TDB_DATA *pkey)
+{
+ TDB_DATA dbuf;
+ *pkey = tdb_nextkey(the_tdb, *pkey);
+
+ dbuf = tdb_fetch(the_tdb, *pkey);
+ if (!dbuf.dptr)
+ terror("fetch failed");
+ else
+ /* printf("%s : %*.*s\n", k, (int)dbuf.dsize, (int)dbuf.dsize, dbuf.dptr); */
+ print_rec(the_tdb, *pkey, dbuf, NULL);
+}
+
+int main(int argc, char *argv[])
+{
+ int bIterate = 0;
+ char *line;
+ char *tok;
+ TDB_DATA iterate_kbuf;
+
+ if (argv[1]) {
+ static char tmp[1024];
+ sprintf(tmp, "open %s", argv[1]);
+ tok=strtok(tmp," ");
+ open_tdb();
+ }
+
+ while ((line = tdb_getline("tdb> "))) {
+
+ /* Shell command */
+
+ if (line[0] == '!') {
+ system(line + 1);
+ continue;
+ }
+
+ if ((tok = strtok(line," "))==NULL) {
+ if (bIterate)
+ next_record(tdb, &iterate_kbuf);
+ continue;
+ }
+ if (strcmp(tok,"create") == 0) {
+ bIterate = 0;
+ create_tdb();
+ continue;
+ } else if (strcmp(tok,"open") == 0) {
+ open_tdb();
+ continue;
+ } else if ((strcmp(tok, "q") == 0) ||
+ (strcmp(tok, "quit") == 0)) {
+ break;
+ }
+
+ /* all the rest require a open database */
+ if (!tdb) {
+ bIterate = 0;
+ terror("database not open");
+ help();
+ continue;
+ }
+
+ if (strcmp(tok,"insert") == 0) {
+ bIterate = 0;
+ insert_tdb();
+ } else if (strcmp(tok,"store") == 0) {
+ bIterate = 0;
+ store_tdb();
+ } else if (strcmp(tok,"show") == 0) {
+ bIterate = 0;
+ show_tdb();
+ } else if (strcmp(tok,"erase") == 0) {
+ bIterate = 0;
+ tdb_traverse(tdb, do_delete_fn, NULL);
+ } else if (strcmp(tok,"delete") == 0) {
+ bIterate = 0;
+ delete_tdb();
+ } else if (strcmp(tok,"dump") == 0) {
+ bIterate = 0;
+ tdb_traverse(tdb, print_rec, NULL);
+ } else if (strcmp(tok,"move") == 0) {
+ bIterate = 0;
+ move_rec();
+ } else if (strcmp(tok,"list") == 0) {
+ tdb_dump_all(tdb);
+ } else if (strcmp(tok, "free") == 0) {
+ tdb_printfreelist(tdb);
+ } else if (strcmp(tok,"info") == 0) {
+ info_tdb();
+ } else if ( (strcmp(tok, "1") == 0) ||
+ (strcmp(tok, "first") == 0)) {
+ bIterate = 1;
+ first_record(tdb, &iterate_kbuf);
+ } else if ((strcmp(tok, "n") == 0) ||
+ (strcmp(tok, "next") == 0)) {
+ next_record(tdb, &iterate_kbuf);
+ } else if ((strcmp(tok, "keys") == 0)) {
+ bIterate = 0;
+ tdb_traverse(tdb, print_key, NULL);
+ } else {
+ help();
+ }
+ }
+
+ if (tdb) tdb_close(tdb);
+
+ return 0;
+}
diff --git a/source/tdb/tdbtorture.c b/source/tdb/tdbtorture.c
new file mode 100644
index 00000000000..3f704e537ea
--- /dev/null
+++ b/source/tdb/tdbtorture.c
@@ -0,0 +1,227 @@
+#include <stdlib.h>
+#include <time.h>
+#include <stdio.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <string.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <stdarg.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <sys/wait.h>
+#include "tdb.h"
+
+/* this tests tdb by doing lots of ops from several simultaneous
+ writers - that stresses the locking code. Build with TDB_DEBUG=1
+ for best effect */
+
+
+
+#define REOPEN_PROB 30
+#define DELETE_PROB 8
+#define STORE_PROB 4
+#define APPEND_PROB 6
+#define LOCKSTORE_PROB 0
+#define TRAVERSE_PROB 20
+#define CULL_PROB 100
+#define KEYLEN 3
+#define DATALEN 100
+#define LOCKLEN 20
+
+static TDB_CONTEXT *db;
+
+static void tdb_log(TDB_CONTEXT *tdb, int level, const char *format, ...)
+{
+ va_list ap;
+
+ va_start(ap, format);
+ vfprintf(stdout, format, ap);
+ va_end(ap);
+ fflush(stdout);
+#if 0
+ {
+ char *ptr;
+ asprintf(&ptr,"xterm -e gdb /proc/%d/exe %d", getpid(), getpid());
+ system(ptr);
+ free(ptr);
+ }
+#endif
+}
+
+static void fatal(char *why)
+{
+ perror(why);
+ exit(1);
+}
+
+static char *randbuf(int len)
+{
+ char *buf;
+ int i;
+ buf = (char *)malloc(len+1);
+
+ for (i=0;i<len;i++) {
+ buf[i] = 'a' + (rand() % 26);
+ }
+ buf[i] = 0;
+ return buf;
+}
+
+static int cull_traverse(TDB_CONTEXT *tdb, TDB_DATA key, TDB_DATA dbuf,
+ void *state)
+{
+ if (random() % CULL_PROB == 0) {
+ tdb_delete(tdb, key);
+ }
+ return 0;
+}
+
+static void addrec_db(void)
+{
+ int klen, dlen, slen;
+ char *k, *d, *s;
+ TDB_DATA key, data, lockkey;
+
+ klen = 1 + (rand() % KEYLEN);
+ dlen = 1 + (rand() % DATALEN);
+ slen = 1 + (rand() % LOCKLEN);
+
+ k = randbuf(klen);
+ d = randbuf(dlen);
+ s = randbuf(slen);
+
+ key.dptr = k;
+ key.dsize = klen+1;
+
+ data.dptr = d;
+ data.dsize = dlen+1;
+
+ lockkey.dptr = s;
+ lockkey.dsize = slen+1;
+
+#if REOPEN_PROB
+ if (random() % REOPEN_PROB == 0) {
+ tdb_reopen_all();
+ goto next;
+ }
+#endif
+
+#if DELETE_PROB
+ if (random() % DELETE_PROB == 0) {
+ tdb_delete(db, key);
+ goto next;
+ }
+#endif
+
+#if STORE_PROB
+ if (random() % STORE_PROB == 0) {
+ if (tdb_store(db, key, data, TDB_REPLACE) != 0) {
+ fatal("tdb_store failed");
+ }
+ goto next;
+ }
+#endif
+
+#if APPEND_PROB
+ if (random() % APPEND_PROB == 0) {
+ if (tdb_append(db, key, data) != 0) {
+ fatal("tdb_append failed");
+ }
+ goto next;
+ }
+#endif
+
+#if LOCKSTORE_PROB
+ if (random() % LOCKSTORE_PROB == 0) {
+ tdb_chainlock(db, lockkey);
+ data = tdb_fetch(db, key);
+ if (tdb_store(db, key, data, TDB_REPLACE) != 0) {
+ fatal("tdb_store failed");
+ }
+ if (data.dptr) free(data.dptr);
+ tdb_chainunlock(db, lockkey);
+ goto next;
+ }
+#endif
+
+#if TRAVERSE_PROB
+ if (random() % TRAVERSE_PROB == 0) {
+ tdb_traverse(db, cull_traverse, NULL);
+ goto next;
+ }
+#endif
+
+ data = tdb_fetch(db, key);
+ if (data.dptr) free(data.dptr);
+
+next:
+ free(k);
+ free(d);
+ free(s);
+}
+
+static int traverse_fn(TDB_CONTEXT *tdb, TDB_DATA key, TDB_DATA dbuf,
+ void *state)
+{
+ tdb_delete(tdb, key);
+ return 0;
+}
+
+#ifndef NPROC
+#define NPROC 6
+#endif
+
+#ifndef NLOOPS
+#define NLOOPS 200000
+#endif
+
+int main(int argc, char *argv[])
+{
+ int i, seed=0;
+ int loops = NLOOPS;
+ pid_t pids[NPROC];
+
+ pids[0] = getpid();
+
+ for (i=0;i<NPROC-1;i++) {
+ if ((pids[i+1]=fork()) == 0) break;
+ }
+
+ db = tdb_open("torture.tdb", 2, TDB_CLEAR_IF_FIRST,
+ O_RDWR | O_CREAT, 0600);
+ if (!db) {
+ fatal("db open failed");
+ }
+ tdb_logging_function(db, tdb_log);
+
+ srand(seed + getpid());
+ srandom(seed + getpid() + time(NULL));
+ for (i=0;i<loops;i++) addrec_db();
+
+ tdb_traverse(db, NULL, NULL);
+ tdb_traverse(db, traverse_fn, NULL);
+ tdb_traverse(db, traverse_fn, NULL);
+
+ tdb_close(db);
+
+ if (getpid() == pids[0]) {
+ for (i=0;i<NPROC-1;i++) {
+ int status;
+ if (waitpid(pids[i+1], &status, 0) != pids[i+1]) {
+ printf("failed to wait for %d\n",
+ (int)pids[i+1]);
+ exit(1);
+ }
+ if (WEXITSTATUS(status) != 0) {
+ printf("child %d exited with status %d\n",
+ (int)pids[i+1], WEXITSTATUS(status));
+ exit(1);
+ }
+ }
+ printf("OK\n");
+ }
+
+ return 0;
+}
diff --git a/source/tdb/tdbutil.c b/source/tdb/tdbutil.c
new file mode 100644
index 00000000000..09e55e2e702
--- /dev/null
+++ b/source/tdb/tdbutil.c
@@ -0,0 +1,829 @@
+/*
+ Unix SMB/CIFS implementation.
+ tdb utility functions
+ Copyright (C) Andrew Tridgell 1992-1998
+ 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 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 <fnmatch.h>
+
+/* these are little tdb utility functions that are meant to make
+ dealing with a tdb database a little less cumbersome in Samba */
+
+static SIG_ATOMIC_T gotalarm;
+
+/***************************************************************
+ Signal function to tell us we timed out.
+****************************************************************/
+
+static void gotalarm_sig(void)
+{
+ gotalarm = 1;
+}
+
+/***************************************************************
+ Make a TDB_DATA and keep the const warning in one place
+****************************************************************/
+
+static TDB_DATA make_tdb_data(const char *dptr, size_t dsize)
+{
+ TDB_DATA ret;
+ ret.dptr = dptr;
+ ret.dsize = dsize;
+ return ret;
+}
+
+/****************************************************************************
+ Lock a chain with timeout (in seconds).
+****************************************************************************/
+
+static int tdb_chainlock_with_timeout_internal( TDB_CONTEXT *tdb, TDB_DATA key, unsigned int timeout, int rw_type)
+{
+ /* Allow tdb_chainlock to be interrupted by an alarm. */
+ int ret;
+ gotalarm = 0;
+ tdb_set_lock_alarm(&gotalarm);
+
+ if (timeout) {
+ CatchSignal(SIGALRM, SIGNAL_CAST gotalarm_sig);
+ alarm(timeout);
+ }
+
+ if (rw_type == F_RDLCK)
+ ret = tdb_chainlock_read(tdb, key);
+ else
+ ret = tdb_chainlock(tdb, key);
+
+ if (timeout) {
+ alarm(0);
+ CatchSignal(SIGALRM, SIGNAL_CAST SIG_IGN);
+ if (gotalarm) {
+ DEBUG(0,("tdb_chainlock_with_timeout_internal: alarm (%u) timed out for key %s in tdb %s\n",
+ timeout, key.dptr, tdb->name ));
+ /* TODO: If we time out waiting for a lock, it might
+ * be nice to use F_GETLK to get the pid of the
+ * process currently holding the lock and print that
+ * as part of the debugging message. -- mbp */
+ return -1;
+ }
+ }
+
+ return ret;
+}
+
+/****************************************************************************
+ Write lock a chain. Return -1 if timeout or lock failed.
+****************************************************************************/
+
+int tdb_chainlock_with_timeout( TDB_CONTEXT *tdb, TDB_DATA key, unsigned int timeout)
+{
+ return tdb_chainlock_with_timeout_internal(tdb, key, timeout, F_WRLCK);
+}
+
+/****************************************************************************
+ Lock a chain by string. Return -1 if timeout or lock failed.
+****************************************************************************/
+
+int tdb_lock_bystring(TDB_CONTEXT *tdb, const char *keyval, unsigned int timeout)
+{
+ TDB_DATA key = make_tdb_data(keyval, strlen(keyval)+1);
+
+ return tdb_chainlock_with_timeout_internal(tdb, key, timeout, F_WRLCK);
+}
+
+/****************************************************************************
+ Unlock a chain by string.
+****************************************************************************/
+
+void tdb_unlock_bystring(TDB_CONTEXT *tdb, const char *keyval)
+{
+ TDB_DATA key = make_tdb_data(keyval, strlen(keyval)+1);
+
+ tdb_chainunlock(tdb, key);
+}
+
+/****************************************************************************
+ Read lock a chain by string. Return -1 if timeout or lock failed.
+****************************************************************************/
+
+int tdb_read_lock_bystring(TDB_CONTEXT *tdb, const char *keyval, unsigned int timeout)
+{
+ TDB_DATA key = make_tdb_data(keyval, strlen(keyval)+1);
+
+ return tdb_chainlock_with_timeout_internal(tdb, key, timeout, F_RDLCK);
+}
+
+/****************************************************************************
+ Read unlock a chain by string.
+****************************************************************************/
+
+void tdb_read_unlock_bystring(TDB_CONTEXT *tdb, const char *keyval)
+{
+ TDB_DATA key = make_tdb_data(keyval, strlen(keyval)+1);
+
+ tdb_chainunlock_read(tdb, key);
+}
+
+
+/****************************************************************************
+ Fetch a int32 value by a arbitrary blob key, return -1 if not found.
+ Output is int32 in native byte order.
+****************************************************************************/
+
+int32 tdb_fetch_int32_byblob(TDB_CONTEXT *tdb, const char *keyval, size_t len)
+{
+ TDB_DATA key = make_tdb_data(keyval, len);
+ TDB_DATA data;
+ int32 ret;
+
+ data = tdb_fetch(tdb, key);
+ if (!data.dptr || data.dsize != sizeof(int32)) {
+ SAFE_FREE(data.dptr);
+ return -1;
+ }
+
+ ret = IVAL(data.dptr,0);
+ SAFE_FREE(data.dptr);
+ return ret;
+}
+
+/****************************************************************************
+ Fetch a int32 value by string key, return -1 if not found.
+ Output is int32 in native byte order.
+****************************************************************************/
+
+int32 tdb_fetch_int32(TDB_CONTEXT *tdb, const char *keystr)
+{
+ return tdb_fetch_int32_byblob(tdb, keystr, strlen(keystr) + 1);
+}
+
+/****************************************************************************
+ Store a int32 value by an arbitary blob key, return 0 on success, -1 on failure.
+ Input is int32 in native byte order. Output in tdb is in little-endian.
+****************************************************************************/
+
+int tdb_store_int32_byblob(TDB_CONTEXT *tdb, const char *keystr, size_t len, int32 v)
+{
+ TDB_DATA key = make_tdb_data(keystr, len);
+ TDB_DATA data;
+ int32 v_store;
+
+ SIVAL(&v_store,0,v);
+ data.dptr = (void *)&v_store;
+ data.dsize = sizeof(int32);
+
+ return tdb_store(tdb, key, data, TDB_REPLACE);
+}
+
+/****************************************************************************
+ Store a int32 value by string key, return 0 on success, -1 on failure.
+ Input is int32 in native byte order. Output in tdb is in little-endian.
+****************************************************************************/
+
+int tdb_store_int32(TDB_CONTEXT *tdb, const char *keystr, int32 v)
+{
+ return tdb_store_int32_byblob(tdb, keystr, strlen(keystr) + 1, v);
+}
+
+/****************************************************************************
+ Fetch a uint32 value by a arbitrary blob key, return -1 if not found.
+ Output is uint32 in native byte order.
+****************************************************************************/
+
+BOOL tdb_fetch_uint32_byblob(TDB_CONTEXT *tdb, const char *keyval, size_t len, uint32 *value)
+{
+ TDB_DATA key = make_tdb_data(keyval, len);
+ TDB_DATA data;
+
+ data = tdb_fetch(tdb, key);
+ if (!data.dptr || data.dsize != sizeof(uint32)) {
+ SAFE_FREE(data.dptr);
+ return False;
+ }
+
+ *value = IVAL(data.dptr,0);
+ SAFE_FREE(data.dptr);
+ return True;
+}
+
+/****************************************************************************
+ Fetch a uint32 value by string key, return -1 if not found.
+ Output is uint32 in native byte order.
+****************************************************************************/
+
+BOOL tdb_fetch_uint32(TDB_CONTEXT *tdb, const char *keystr, uint32 *value)
+{
+ return tdb_fetch_uint32_byblob(tdb, keystr, strlen(keystr) + 1, value);
+}
+
+/****************************************************************************
+ Store a uint32 value by an arbitary blob key, return 0 on success, -1 on failure.
+ Input is uint32 in native byte order. Output in tdb is in little-endian.
+****************************************************************************/
+
+BOOL tdb_store_uint32_byblob(TDB_CONTEXT *tdb, const char *keystr, size_t len, uint32 value)
+{
+ TDB_DATA key = make_tdb_data(keystr, len);
+ TDB_DATA data;
+ uint32 v_store;
+ BOOL ret = True;
+
+ SIVAL(&v_store, 0, value);
+ data.dptr = (void *)&v_store;
+ data.dsize = sizeof(uint32);
+
+ if (tdb_store(tdb, key, data, TDB_REPLACE) == -1)
+ ret = False;
+
+ return ret;
+}
+
+/****************************************************************************
+ Store a uint32 value by string key, return 0 on success, -1 on failure.
+ Input is uint32 in native byte order. Output in tdb is in little-endian.
+****************************************************************************/
+
+BOOL tdb_store_uint32(TDB_CONTEXT *tdb, const char *keystr, uint32 value)
+{
+ return tdb_store_uint32_byblob(tdb, keystr, strlen(keystr) + 1, value);
+}
+/****************************************************************************
+ Store a buffer by a null terminated string key. Return 0 on success, -1
+ on failure.
+****************************************************************************/
+
+int tdb_store_bystring(TDB_CONTEXT *tdb, const char *keystr, TDB_DATA data, int flags)
+{
+ TDB_DATA key = make_tdb_data(keystr, strlen(keystr)+1);
+
+ return tdb_store(tdb, key, data, flags);
+}
+
+/****************************************************************************
+ Fetch a buffer using a null terminated string key. Don't forget to call
+ free() on the result dptr.
+****************************************************************************/
+
+TDB_DATA tdb_fetch_bystring(TDB_CONTEXT *tdb, const char *keystr)
+{
+ TDB_DATA key = make_tdb_data(keystr, strlen(keystr)+1);
+
+ return tdb_fetch(tdb, key);
+}
+
+/****************************************************************************
+ Delete an entry using a null terminated string key.
+****************************************************************************/
+
+int tdb_delete_bystring(TDB_CONTEXT *tdb, const char *keystr)
+{
+ TDB_DATA key = make_tdb_data(keystr, strlen(keystr)+1);
+
+ return tdb_delete(tdb, key);
+}
+
+/****************************************************************************
+ Atomic integer change. Returns old value. To create, set initial value in *oldval.
+****************************************************************************/
+
+int32 tdb_change_int32_atomic(TDB_CONTEXT *tdb, const char *keystr, int32 *oldval, int32 change_val)
+{
+ int32 val;
+ int32 ret = -1;
+
+ if (tdb_lock_bystring(tdb, keystr,0) == -1)
+ return -1;
+
+ if ((val = tdb_fetch_int32(tdb, keystr)) == -1) {
+ /* The lookup failed */
+ if (tdb_error(tdb) != TDB_ERR_NOEXIST) {
+ /* but not because it didn't exist */
+ goto err_out;
+ }
+
+ /* Start with 'old' value */
+ val = *oldval;
+
+ } else {
+ /* It worked, set return value (oldval) to tdb data */
+ *oldval = val;
+ }
+
+ /* Increment value for storage and return next time */
+ val += change_val;
+
+ if (tdb_store_int32(tdb, keystr, val) == -1)
+ goto err_out;
+
+ ret = 0;
+
+ err_out:
+
+ tdb_unlock_bystring(tdb, keystr);
+ return ret;
+}
+
+/****************************************************************************
+ Atomic unsigned integer change. Returns old value. To create, set initial value in *oldval.
+****************************************************************************/
+
+BOOL tdb_change_uint32_atomic(TDB_CONTEXT *tdb, const char *keystr, uint32 *oldval, uint32 change_val)
+{
+ uint32 val;
+ BOOL ret = False;
+
+ if (tdb_lock_bystring(tdb, keystr,0) == -1)
+ return False;
+
+ if (!tdb_fetch_uint32(tdb, keystr, &val)) {
+ /* It failed */
+ if (tdb_error(tdb) != TDB_ERR_NOEXIST) {
+ /* and not because it didn't exist */
+ goto err_out;
+ }
+
+ /* Start with 'old' value */
+ val = *oldval;
+
+ } else {
+ /* it worked, set return value (oldval) to tdb data */
+ *oldval = val;
+
+ }
+
+ /* get a new value to store */
+ val += change_val;
+
+ if (!tdb_store_uint32(tdb, keystr, val))
+ goto err_out;
+
+ ret = True;
+
+ err_out:
+
+ tdb_unlock_bystring(tdb, keystr);
+ return ret;
+}
+
+/****************************************************************************
+ Useful pair of routines for packing/unpacking data consisting of
+ integers and strings.
+****************************************************************************/
+
+size_t tdb_pack(char *buf, int bufsize, const char *fmt, ...)
+{
+ va_list ap;
+ uint8 bt;
+ uint16 w;
+ uint32 d;
+ int i;
+ void *p;
+ int len;
+ char *s;
+ char c;
+ char *buf0 = buf;
+ const char *fmt0 = fmt;
+ int bufsize0 = bufsize;
+
+ va_start(ap, fmt);
+
+ while (*fmt) {
+ switch ((c = *fmt++)) {
+ case 'b': /* unsigned 8-bit integer */
+ len = 1;
+ bt = (uint8)va_arg(ap, int);
+ if (bufsize && bufsize >= len)
+ SSVAL(buf, 0, bt);
+ break;
+ case 'w': /* unsigned 16-bit integer */
+ len = 2;
+ w = (uint16)va_arg(ap, int);
+ if (bufsize && bufsize >= len)
+ SSVAL(buf, 0, w);
+ break;
+ case 'd': /* signed 32-bit integer (standard int in most systems) */
+ len = 4;
+ d = va_arg(ap, uint32);
+ if (bufsize && bufsize >= len)
+ SIVAL(buf, 0, d);
+ break;
+ case 'p': /* pointer */
+ len = 4;
+ p = va_arg(ap, void *);
+ d = p?1:0;
+ if (bufsize && bufsize >= len)
+ SIVAL(buf, 0, d);
+ break;
+ case 'P': /* null-terminated string */
+ s = va_arg(ap,char *);
+ w = strlen(s);
+ len = w + 1;
+ if (bufsize && bufsize >= len)
+ memcpy(buf, s, len);
+ break;
+ case 'f': /* null-terminated string */
+ s = va_arg(ap,char *);
+ w = strlen(s);
+ len = w + 1;
+ if (bufsize && bufsize >= len)
+ memcpy(buf, s, len);
+ break;
+ case 'B': /* fixed-length string */
+ i = va_arg(ap, int);
+ s = va_arg(ap, char *);
+ len = 4+i;
+ if (bufsize && bufsize >= len) {
+ SIVAL(buf, 0, i);
+ memcpy(buf+4, s, i);
+ }
+ break;
+ default:
+ DEBUG(0,("Unknown tdb_pack format %c in %s\n",
+ c, fmt));
+ len = 0;
+ break;
+ }
+
+ buf += len;
+ if (bufsize)
+ bufsize -= len;
+ if (bufsize < 0)
+ bufsize = 0;
+ }
+
+ va_end(ap);
+
+ DEBUG(18,("tdb_pack(%s, %d) -> %d\n",
+ fmt0, bufsize0, (int)PTR_DIFF(buf, buf0)));
+
+ return PTR_DIFF(buf, buf0);
+}
+
+/****************************************************************************
+ Useful pair of routines for packing/unpacking data consisting of
+ integers and strings.
+****************************************************************************/
+
+int tdb_unpack(char *buf, int bufsize, const char *fmt, ...)
+{
+ va_list ap;
+ uint8 *bt;
+ uint16 *w;
+ uint32 *d;
+ int len;
+ int *i;
+ void **p;
+ char *s, **b;
+ char c;
+ char *buf0 = buf;
+ const char *fmt0 = fmt;
+ int bufsize0 = bufsize;
+
+ va_start(ap, fmt);
+
+ while (*fmt) {
+ switch ((c=*fmt++)) {
+ case 'b':
+ len = 1;
+ bt = va_arg(ap, uint8 *);
+ if (bufsize < len)
+ goto no_space;
+ *bt = SVAL(buf, 0);
+ break;
+ case 'w':
+ len = 2;
+ w = va_arg(ap, uint16 *);
+ if (bufsize < len)
+ goto no_space;
+ *w = SVAL(buf, 0);
+ break;
+ case 'd':
+ len = 4;
+ d = va_arg(ap, uint32 *);
+ if (bufsize < len)
+ goto no_space;
+ *d = IVAL(buf, 0);
+ break;
+ case 'p':
+ len = 4;
+ p = va_arg(ap, void **);
+ if (bufsize < len)
+ goto no_space;
+ *p = (void *)IVAL(buf, 0);
+ break;
+ case 'P':
+ s = va_arg(ap,char *);
+ len = strlen(buf) + 1;
+ if (bufsize < len || len > sizeof(pstring))
+ goto no_space;
+ memcpy(s, buf, len);
+ break;
+ case 'f':
+ s = va_arg(ap,char *);
+ len = strlen(buf) + 1;
+ if (bufsize < len || len > sizeof(fstring))
+ goto no_space;
+ memcpy(s, buf, len);
+ break;
+ case 'B':
+ i = va_arg(ap, int *);
+ b = va_arg(ap, char **);
+ len = 4;
+ if (bufsize < len)
+ goto no_space;
+ *i = IVAL(buf, 0);
+ if (! *i) {
+ *b = NULL;
+ break;
+ }
+ len += *i;
+ if (bufsize < len)
+ goto no_space;
+ *b = (char *)malloc(*i);
+ if (! *b)
+ goto no_space;
+ memcpy(*b, buf+4, *i);
+ break;
+ default:
+ DEBUG(0,("Unknown tdb_unpack format %c in %s\n",
+ c, fmt));
+
+ len = 0;
+ break;
+ }
+
+ buf += len;
+ bufsize -= len;
+ }
+
+ va_end(ap);
+
+ DEBUG(18,("tdb_unpack(%s, %d) -> %d\n",
+ fmt0, bufsize0, (int)PTR_DIFF(buf, buf0)));
+
+ return PTR_DIFF(buf, buf0);
+
+ no_space:
+ return -1;
+}
+
+
+/**
+ * Pack SID passed by pointer
+ *
+ * @param pack_buf pointer to buffer which is to be filled with packed data
+ * @param bufsize size of packing buffer
+ * @param sid pointer to sid to be packed
+ *
+ * @return length of the packed representation of the whole structure
+ **/
+size_t tdb_sid_pack(char* pack_buf, int bufsize, DOM_SID* sid)
+{
+ int idx;
+ size_t len = 0;
+
+ if (!sid || !pack_buf) return -1;
+
+ len += tdb_pack(pack_buf + len, bufsize - len, "bb", sid->sid_rev_num,
+ sid->num_auths);
+
+ for (idx = 0; idx < 6; idx++) {
+ len += tdb_pack(pack_buf + len, bufsize - len, "b", sid->id_auth[idx]);
+ }
+
+ for (idx = 0; idx < MAXSUBAUTHS; idx++) {
+ len += tdb_pack(pack_buf + len, bufsize - len, "d", sid->sub_auths[idx]);
+ }
+
+ return len;
+}
+
+
+/**
+ * Unpack SID into a pointer
+ *
+ * @param pack_buf pointer to buffer with packed representation
+ * @param bufsize size of the buffer
+ * @param sid pointer to sid structure to be filled with unpacked data
+ *
+ * @return size of structure unpacked from buffer
+ **/
+size_t tdb_sid_unpack(char* pack_buf, int bufsize, DOM_SID* sid)
+{
+ int idx, len = 0;
+
+ if (!sid || !pack_buf) return -1;
+
+ len += tdb_unpack(pack_buf + len, bufsize - len, "bb",
+ &sid->sid_rev_num, &sid->num_auths);
+
+ for (idx = 0; idx < 6; idx++) {
+ len += tdb_unpack(pack_buf + len, bufsize - len, "b", &sid->id_auth[idx]);
+ }
+
+ for (idx = 0; idx < MAXSUBAUTHS; idx++) {
+ len += tdb_unpack(pack_buf + len, bufsize - len, "d", &sid->sub_auths[idx]);
+ }
+
+ return len;
+}
+
+
+/**
+ * Pack TRUSTED_DOM_PASS passed by pointer
+ *
+ * @param pack_buf pointer to buffer which is to be filled with packed data
+ * @param bufsize size of the buffer
+ * @param pass pointer to trusted domain password to be packed
+ *
+ * @return length of the packed representation of the whole structure
+ **/
+size_t tdb_trusted_dom_pass_pack(char* pack_buf, int bufsize, TRUSTED_DOM_PASS* pass)
+{
+ int idx, len = 0;
+
+ if (!pack_buf || !pass) return -1;
+
+ /* packing unicode domain name and password */
+ len += tdb_pack(pack_buf + len, bufsize - len, "d", pass->uni_name_len);
+
+ for (idx = 0; idx < 32; idx++)
+ len += tdb_pack(pack_buf + len, bufsize - len, "w", pass->uni_name[idx]);
+
+ len += tdb_pack(pack_buf + len, bufsize - len, "dPd", pass->pass_len,
+ pass->pass, pass->mod_time);
+
+ /* packing SID structure */
+ len += tdb_sid_pack(pack_buf + len, bufsize - len, &pass->domain_sid);
+
+ return len;
+}
+
+
+/**
+ * Unpack TRUSTED_DOM_PASS passed by pointer
+ *
+ * @param pack_buf pointer to buffer with packed representation
+ * @param bufsize size of the buffer
+ * @param pass pointer to trusted domain password to be filled with unpacked data
+ *
+ * @return size of structure unpacked from buffer
+ **/
+size_t tdb_trusted_dom_pass_unpack(char* pack_buf, int bufsize, TRUSTED_DOM_PASS* pass)
+{
+ int idx, len = 0;
+
+ if (!pack_buf || !pass) return -1;
+
+ /* unpack unicode domain name and plaintext password */
+ len += tdb_unpack(pack_buf, bufsize - len, "d", &pass->uni_name_len);
+
+ for (idx = 0; idx < 32; idx++)
+ len += tdb_unpack(pack_buf + len, bufsize - len, "w", &pass->uni_name[idx]);
+
+ len += tdb_unpack(pack_buf + len, bufsize - len, "dPd", &pass->pass_len, &pass->pass,
+ &pass->mod_time);
+
+ /* unpack domain sid */
+ len += tdb_sid_unpack(pack_buf + len, bufsize - len, &pass->domain_sid);
+
+ return len;
+}
+
+
+/****************************************************************************
+ Log tdb messages via DEBUG().
+****************************************************************************/
+
+static void tdb_log(TDB_CONTEXT *tdb, int level, const char *format, ...)
+{
+ va_list ap;
+ char *ptr = NULL;
+
+ va_start(ap, format);
+ vasprintf(&ptr, format, ap);
+ va_end(ap);
+
+ if (!ptr || !*ptr)
+ return;
+
+ DEBUG(level, ("tdb(%s): %s", tdb->name ? tdb->name : "unnamed", ptr));
+ SAFE_FREE(ptr);
+}
+
+/****************************************************************************
+ Like tdb_open() but also setup a logging function that redirects to
+ the samba DEBUG() system.
+****************************************************************************/
+
+TDB_CONTEXT *tdb_open_log(const char *name, int hash_size, int tdb_flags,
+ int open_flags, mode_t mode)
+{
+ TDB_CONTEXT *tdb;
+
+ if (!lp_use_mmap())
+ tdb_flags |= TDB_NOMMAP;
+
+ tdb = tdb_open_ex(name, hash_size, tdb_flags,
+ open_flags, mode, tdb_log);
+ if (!tdb)
+ return NULL;
+
+ return tdb;
+}
+
+
+/****************************************************************************
+ Allow tdb_delete to be used as a tdb_traversal_fn.
+****************************************************************************/
+
+int tdb_traverse_delete_fn(TDB_CONTEXT *the_tdb, TDB_DATA key, TDB_DATA dbuf,
+ void *state)
+{
+ return tdb_delete(the_tdb, key);
+}
+
+
+
+/**
+ * Search across the whole tdb for keys that match the given pattern
+ * return the result as a list of keys
+ *
+ * @param tdb pointer to opened tdb file context
+ * @param pattern searching pattern used by fnmatch(3) functions
+ *
+ * @return list of keys found by looking up with given pattern
+ **/
+TDB_LIST_NODE *tdb_search_keys(TDB_CONTEXT *tdb, const char* pattern)
+{
+ TDB_DATA key, next;
+ TDB_LIST_NODE *list = NULL;
+ TDB_LIST_NODE *rec = NULL;
+ TDB_LIST_NODE *tmp = NULL;
+
+ for (key = tdb_firstkey(tdb); key.dptr; key = next) {
+ /* duplicate key string to ensure null-termination */
+ char *key_str = (char*) strndup(key.dptr, key.dsize);
+ if (!key_str) {
+ DEBUG(0, ("tdb_search_keys: strndup() failed!\n"));
+ smb_panic("strndup failed!\n");
+ }
+
+ DEBUG(18, ("checking %s for match to pattern %s\n", key_str, pattern));
+
+ next = tdb_nextkey(tdb, key);
+
+ /* do the pattern checking */
+ if (fnmatch(pattern, key_str, 0) == 0) {
+ rec = (TDB_LIST_NODE*) malloc(sizeof(*rec));
+ ZERO_STRUCTP(rec);
+
+ rec->node_key = key;
+
+ DLIST_ADD_END(list, rec, tmp);
+
+ DEBUG(18, ("checking %s matched pattern %s\n", key_str, pattern));
+ } else {
+ free(key.dptr);
+ }
+
+ /* free duplicated key string */
+ free(key_str);
+ }
+
+ return list;
+
+}
+
+
+/**
+ * Free the list returned by tdb_search_keys
+ *
+ * @param node list of results found by tdb_search_keys
+ **/
+void tdb_search_list_free(TDB_LIST_NODE* node)
+{
+ TDB_LIST_NODE *next_node;
+
+ while (node) {
+ next_node = node->next;
+ SAFE_FREE(node->node_key.dptr);
+ SAFE_FREE(node);
+ node = next_node;
+ };
+}
diff --git a/source/tdb/tdbutil.h b/source/tdb/tdbutil.h
new file mode 100644
index 00000000000..1a181a962f2
--- /dev/null
+++ b/source/tdb/tdbutil.h
@@ -0,0 +1,38 @@
+/*
+ Unix SMB/CIFS implementation.
+ tdb utility functions
+ Copyright (C) Andrew Tridgell 1999
+ 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 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 __TDBUTIL_H__
+#define __TDBUTIL_H__
+
+
+/* single node of a list returned by tdb_search_keys */
+typedef struct keys_node
+{
+ struct keys_node *prev, *next;
+ TDB_DATA node_key;
+} TDB_LIST_NODE;
+
+
+TDB_LIST_NODE *tdb_search_keys(TDB_CONTEXT*, const char*);
+void tdb_search_list_free(TDB_LIST_NODE*);
+
+
+#endif /* __TDBUTIL_H__ */
diff --git a/source/tests/.cvsignore b/source/tests/.cvsignore
new file mode 100644
index 00000000000..b6c1f01120e
--- /dev/null
+++ b/source/tests/.cvsignore
@@ -0,0 +1 @@
+unixsock
diff --git a/source/tests/README b/source/tests/README
new file mode 100644
index 00000000000..cf1be8b00a7
--- /dev/null
+++ b/source/tests/README
@@ -0,0 +1,10 @@
+This directory contains autoconf test programs that are too large to
+comfortably fit in configure.in.
+
+These programs should test one feature of the OS and exit(0) if it
+works or exit(1) if it doesn't work (do _not_ use return)
+
+The programs should be kept simple and to the point. Beautiful/fast
+code is not necessary
+
+
diff --git a/source/tests/crack.c b/source/tests/crack.c
new file mode 100644
index 00000000000..36119b3bbc6
--- /dev/null
+++ b/source/tests/crack.c
@@ -0,0 +1,12 @@
+#include <crack.h>
+
+#ifndef HAVE_CRACKLIB_DICTPATH
+#ifndef CRACKLIB_DICTPATH
+#define CRACKLIB_DICTPATH SAMBA_CRACKLIB_DICTPATH
+#endif
+#endif
+
+int main(int argc, char **argv) {
+ FascistCheck("Foo", CRACKLIB_DICTPATH);
+ return 0;
+}
diff --git a/source/tests/crypttest.c b/source/tests/crypttest.c
new file mode 100644
index 00000000000..efee2e593d4
--- /dev/null
+++ b/source/tests/crypttest.c
@@ -0,0 +1,852 @@
+#if defined(HAVE_UNISTD_H)
+#include <unistd.h>
+#endif
+
+#include <sys/types.h>
+
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+
+#ifdef HAVE_STRINGS_H
+#include <strings.h>
+#endif
+
+#if !defined(HAVE_CRYPT)
+
+/*
+ This bit of code was derived from the UFC-crypt package which
+ carries the following copyright
+
+ Modified for use by Samba by Andrew Tridgell, October 1994
+
+ Note that this routine is only faster on some machines. Under Linux 1.1.51
+ libc 4.5.26 I actually found this routine to be slightly slower.
+
+ Under SunOS I found a huge speedup by using these routines
+ (a factor of 20 or so)
+
+ Warning: I've had a report from Steve Kennedy <steve@gbnet.org>
+ that this crypt routine may sometimes get the wrong answer. Only
+ use UFC_CRYT if you really need it.
+
+*/
+
+/*
+ * UFC-crypt: ultra fast crypt(3) implementation
+ *
+ * Copyright (C) 1991-1998, Free Software Foundation, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 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 Library General Public
+ * License along with this library; if not, write to the Free
+ * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * @(#)crypt_util.c 2.31 02/08/92
+ *
+ * Support routines
+ *
+ */
+
+
+#ifndef long32
+#if (SIZEOF_INT == 4)
+#define long32 int
+#elif (SIZEOF_LONG == 4)
+#define long32 long
+#elif (SIZEOF_SHORT == 4)
+#define long32 short
+#else
+/* uggh - no 32 bit type?? probably a CRAY. just hope this works ... */
+#define long32 int
+#endif
+#endif
+
+#ifndef long64
+#ifdef HAVE_LONGLONG
+#define long64 long long long
+#endif
+#endif
+
+#ifndef ufc_long
+#define ufc_long unsigned
+#endif
+
+#ifndef _UFC_64_
+#define _UFC_32_
+#endif
+
+/*
+ * Permutation done once on the 56 bit
+ * key derived from the original 8 byte ASCII key.
+ */
+static int pc1[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
+};
+
+/*
+ * How much to rotate each 28 bit half of the pc1 permutated
+ * 56 bit key before using pc2 to give the i' key
+ */
+static int rots[16] = {
+ 1, 1, 2, 2, 2, 2, 2, 2, 1, 2, 2, 2, 2, 2, 2, 1
+};
+
+/*
+ * Permutation giving the key
+ * of the i' DES round
+ */
+static int pc2[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
+};
+
+/*
+ * The E expansion table which selects
+ * bits from the 32 bit intermediate result.
+ */
+static int esel[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 int e_inverse[64];
+
+/*
+ * Permutation done on the
+ * result of sbox lookups
+ */
+static int perm32[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
+};
+
+/*
+ * The sboxes
+ */
+static int 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 }
+ }
+};
+
+/*
+ * This is the final
+ * permutation matrix
+ */
+static int final_perm[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
+};
+
+/*
+ * The 16 DES keys in BITMASK format
+ */
+#ifdef _UFC_32_
+long32 _ufc_keytab[16][2];
+#endif
+
+#ifdef _UFC_64_
+long64 _ufc_keytab[16];
+#endif
+
+
+#define ascii_to_bin(c) ((c)>='a'?(c-59):(c)>='A'?((c)-53):(c)-'.')
+#define bin_to_ascii(c) ((c)>=38?((c)-38+'a'):(c)>=12?((c)-12+'A'):(c)+'.')
+
+/* Macro to set a bit (0..23) */
+#define BITMASK(i) ( (1<<(11-(i)%12+3)) << ((i)<12?16:0) )
+
+/*
+ * sb arrays:
+ *
+ * Workhorses of the inner loop of the DES implementation.
+ * They do sbox lookup, shifting of this value, 32 bit
+ * permutation and E permutation for the next round.
+ *
+ * Kept in 'BITMASK' format.
+ */
+
+#ifdef _UFC_32_
+long32 _ufc_sb0[8192], _ufc_sb1[8192], _ufc_sb2[8192], _ufc_sb3[8192];
+static long32 *sb[4] = {_ufc_sb0, _ufc_sb1, _ufc_sb2, _ufc_sb3};
+#endif
+
+#ifdef _UFC_64_
+long64 _ufc_sb0[4096], _ufc_sb1[4096], _ufc_sb2[4096], _ufc_sb3[4096];
+static long64 *sb[4] = {_ufc_sb0, _ufc_sb1, _ufc_sb2, _ufc_sb3};
+#endif
+
+/*
+ * eperm32tab: do 32 bit permutation and E selection
+ *
+ * The first index is the byte number in the 32 bit value to be permuted
+ * - second - is the value of this byte
+ * - third - selects the two 32 bit values
+ *
+ * The table is used and generated internally in init_des to speed it up
+ */
+static ufc_long eperm32tab[4][256][2];
+
+/*
+ * do_pc1: permform pc1 permutation in the key schedule generation.
+ *
+ * The first index is the byte number in the 8 byte ASCII key
+ * - second - - the two 28 bits halfs of the result
+ * - third - selects the 7 bits actually used of each byte
+ *
+ * The result is kept with 28 bit per 32 bit with the 4 most significant
+ * bits zero.
+ */
+static ufc_long do_pc1[8][2][128];
+
+/*
+ * do_pc2: permform pc2 permutation in the key schedule generation.
+ *
+ * The first index is the septet number in the two 28 bit intermediate values
+ * - second - - - septet values
+ *
+ * Knowledge of the structure of the pc2 permutation is used.
+ *
+ * The result is kept with 28 bit per 32 bit with the 4 most significant
+ * bits zero.
+ */
+static ufc_long do_pc2[8][128];
+
+/*
+ * efp: undo an extra e selection and do final
+ * permutation giving the DES result.
+ *
+ * Invoked 6 bit a time on two 48 bit values
+ * giving two 32 bit longs.
+ */
+static ufc_long efp[16][64][2];
+
+static unsigned char bytemask[8] = {
+ 0x80, 0x40, 0x20, 0x10, 0x08, 0x04, 0x02, 0x01
+};
+
+static ufc_long longmask[32] = {
+ 0x80000000, 0x40000000, 0x20000000, 0x10000000,
+ 0x08000000, 0x04000000, 0x02000000, 0x01000000,
+ 0x00800000, 0x00400000, 0x00200000, 0x00100000,
+ 0x00080000, 0x00040000, 0x00020000, 0x00010000,
+ 0x00008000, 0x00004000, 0x00002000, 0x00001000,
+ 0x00000800, 0x00000400, 0x00000200, 0x00000100,
+ 0x00000080, 0x00000040, 0x00000020, 0x00000010,
+ 0x00000008, 0x00000004, 0x00000002, 0x00000001
+};
+
+
+/*
+ * Silly rewrite of 'bzero'. I do so
+ * because some machines don't have
+ * bzero and some don't have memset.
+ */
+
+static void clearmem(char *start, int cnt)
+ { while(cnt--)
+ *start++ = '\0';
+ }
+
+static int initialized = 0;
+
+/* lookup a 6 bit value in sbox */
+
+#define s_lookup(i,s) sbox[(i)][(((s)>>4) & 0x2)|((s) & 0x1)][((s)>>1) & 0xf];
+
+/*
+ * Initialize unit - may be invoked directly
+ * by fcrypt users.
+ */
+
+static void ufc_init_des(void)
+ { int comes_from_bit;
+ int bit, sg;
+ ufc_long j;
+ ufc_long mask1, mask2;
+
+ /*
+ * Create the do_pc1 table used
+ * to affect pc1 permutation
+ * when generating keys
+ */
+ for(bit = 0; bit < 56; bit++) {
+ comes_from_bit = pc1[bit] - 1;
+ mask1 = bytemask[comes_from_bit % 8 + 1];
+ mask2 = longmask[bit % 28 + 4];
+ for(j = 0; j < 128; j++) {
+ if(j & mask1)
+ do_pc1[comes_from_bit / 8][bit / 28][j] |= mask2;
+ }
+ }
+
+ /*
+ * Create the do_pc2 table used
+ * to affect pc2 permutation when
+ * generating keys
+ */
+ for(bit = 0; bit < 48; bit++) {
+ comes_from_bit = pc2[bit] - 1;
+ mask1 = bytemask[comes_from_bit % 7 + 1];
+ mask2 = BITMASK(bit % 24);
+ for(j = 0; j < 128; j++) {
+ if(j & mask1)
+ do_pc2[comes_from_bit / 7][j] |= mask2;
+ }
+ }
+
+ /*
+ * Now generate the table used to do combined
+ * 32 bit permutation and e expansion
+ *
+ * We use it because we have to permute 16384 32 bit
+ * longs into 48 bit in order to initialize sb.
+ *
+ * Looping 48 rounds per permutation becomes
+ * just too slow...
+ *
+ */
+
+ clearmem((char*)eperm32tab, sizeof(eperm32tab));
+
+ for(bit = 0; bit < 48; bit++) {
+ ufc_long inner_mask1,comes_from;
+
+ comes_from = perm32[esel[bit]-1]-1;
+ inner_mask1 = bytemask[comes_from % 8];
+
+ for(j = 256; j--;) {
+ if(j & inner_mask1)
+ eperm32tab[comes_from / 8][j][bit / 24] |= BITMASK(bit % 24);
+ }
+ }
+
+ /*
+ * Create the sb tables:
+ *
+ * For each 12 bit segment of an 48 bit intermediate
+ * result, the sb table precomputes the two 4 bit
+ * values of the sbox lookups done with the two 6
+ * bit halves, shifts them to their proper place,
+ * sends them through perm32 and finally E expands
+ * them so that they are ready for the next
+ * DES round.
+ *
+ */
+ for(sg = 0; sg < 4; sg++) {
+ int j1, j2;
+ int s1, s2;
+
+ for(j1 = 0; j1 < 64; j1++) {
+ s1 = s_lookup(2 * sg, j1);
+ for(j2 = 0; j2 < 64; j2++) {
+ ufc_long to_permute, inx;
+
+ s2 = s_lookup(2 * sg + 1, j2);
+ to_permute = ((s1 << 4) | s2) << (24 - 8 * sg);
+
+#ifdef _UFC_32_
+ inx = ((j1 << 6) | j2) << 1;
+ sb[sg][inx ] = eperm32tab[0][(to_permute >> 24) & 0xff][0];
+ sb[sg][inx+1] = eperm32tab[0][(to_permute >> 24) & 0xff][1];
+ sb[sg][inx ] |= eperm32tab[1][(to_permute >> 16) & 0xff][0];
+ sb[sg][inx+1] |= eperm32tab[1][(to_permute >> 16) & 0xff][1];
+ sb[sg][inx ] |= eperm32tab[2][(to_permute >> 8) & 0xff][0];
+ sb[sg][inx+1] |= eperm32tab[2][(to_permute >> 8) & 0xff][1];
+ sb[sg][inx ] |= eperm32tab[3][(to_permute) & 0xff][0];
+ sb[sg][inx+1] |= eperm32tab[3][(to_permute) & 0xff][1];
+#endif
+#ifdef _UFC_64_
+ inx = ((j1 << 6) | j2);
+ sb[sg][inx] =
+ ((long64)eperm32tab[0][(to_permute >> 24) & 0xff][0] << 32) |
+ (long64)eperm32tab[0][(to_permute >> 24) & 0xff][1];
+ sb[sg][inx] |=
+ ((long64)eperm32tab[1][(to_permute >> 16) & 0xff][0] << 32) |
+ (long64)eperm32tab[1][(to_permute >> 16) & 0xff][1];
+ sb[sg][inx] |=
+ ((long64)eperm32tab[2][(to_permute >> 8) & 0xff][0] << 32) |
+ (long64)eperm32tab[2][(to_permute >> 8) & 0xff][1];
+ sb[sg][inx] |=
+ ((long64)eperm32tab[3][(to_permute) & 0xff][0] << 32) |
+ (long64)eperm32tab[3][(to_permute) & 0xff][1];
+#endif
+ }
+ }
+ }
+
+ /*
+ * Create an inverse matrix for esel telling
+ * where to plug out bits if undoing it
+ */
+ for(bit=48; bit--;) {
+ e_inverse[esel[bit] - 1 ] = bit;
+ e_inverse[esel[bit] - 1 + 32] = bit + 48;
+ }
+
+ /*
+ * create efp: the matrix used to
+ * undo the E expansion and effect final permutation
+ */
+ clearmem((char*)efp, sizeof efp);
+ for(bit = 0; bit < 64; bit++) {
+ int o_bit, o_long;
+ ufc_long word_value, inner_mask1, inner_mask2;
+ int comes_from_f_bit, comes_from_e_bit;
+ int comes_from_word, bit_within_word;
+
+ /* See where bit i belongs in the two 32 bit long's */
+ o_long = bit / 32; /* 0..1 */
+ o_bit = bit % 32; /* 0..31 */
+
+ /*
+ * And find a bit in the e permutated value setting this bit.
+ *
+ * Note: the e selection may have selected the same bit several
+ * times. By the initialization of e_inverse, we only look
+ * for one specific instance.
+ */
+ comes_from_f_bit = final_perm[bit] - 1; /* 0..63 */
+ comes_from_e_bit = e_inverse[comes_from_f_bit]; /* 0..95 */
+ comes_from_word = comes_from_e_bit / 6; /* 0..15 */
+ bit_within_word = comes_from_e_bit % 6; /* 0..5 */
+
+ inner_mask1 = longmask[bit_within_word + 26];
+ inner_mask2 = longmask[o_bit];
+
+ for(word_value = 64; word_value--;) {
+ if(word_value & inner_mask1)
+ efp[comes_from_word][word_value][o_long] |= inner_mask2;
+ }
+ }
+ initialized++;
+ }
+
+/*
+ * Process the elements of the sb table permuting the
+ * bits swapped in the expansion by the current salt.
+ */
+
+#ifdef _UFC_32_
+static void shuffle_sb(long32 *k, ufc_long saltbits)
+ { ufc_long j;
+ long32 x;
+ for(j=4096; j--;) {
+ x = (k[0] ^ k[1]) & (long32)saltbits;
+ *k++ ^= x;
+ *k++ ^= x;
+ }
+ }
+#endif
+
+#ifdef _UFC_64_
+static void shuffle_sb(long64 *k, ufc_long saltbits)
+ { ufc_long j;
+ long64 x;
+ for(j=4096; j--;) {
+ x = ((*k >> 32) ^ *k) & (long64)saltbits;
+ *k++ ^= (x << 32) | x;
+ }
+ }
+#endif
+
+/*
+ * Setup the unit for a new salt
+ * Hopefully we'll not see a new salt in each crypt call.
+ */
+
+static unsigned char current_salt[3] = "&&"; /* invalid value */
+static ufc_long current_saltbits = 0;
+static int direction = 0;
+
+static void setup_salt(const char *s1)
+ { ufc_long i, j, saltbits;
+ const unsigned char *s2 = (const unsigned char *)s1;
+
+ if(!initialized)
+ ufc_init_des();
+
+ if(s2[0] == current_salt[0] && s2[1] == current_salt[1])
+ return;
+ current_salt[0] = s2[0]; current_salt[1] = s2[1];
+
+ /*
+ * This is the only crypt change to DES:
+ * entries are swapped in the expansion table
+ * according to the bits set in the salt.
+ */
+ saltbits = 0;
+ for(i = 0; i < 2; i++) {
+ long c=ascii_to_bin(s2[i]);
+ if(c < 0 || c > 63)
+ c = 0;
+ for(j = 0; j < 6; j++) {
+ if((c >> j) & 0x1)
+ saltbits |= BITMASK(6 * i + j);
+ }
+ }
+
+ /*
+ * Permute the sb table values
+ * to reflect the changed e
+ * selection table
+ */
+ shuffle_sb(_ufc_sb0, current_saltbits ^ saltbits);
+ shuffle_sb(_ufc_sb1, current_saltbits ^ saltbits);
+ shuffle_sb(_ufc_sb2, current_saltbits ^ saltbits);
+ shuffle_sb(_ufc_sb3, current_saltbits ^ saltbits);
+
+ current_saltbits = saltbits;
+ }
+
+static void ufc_mk_keytab(char *key)
+ { ufc_long v1, v2, *k1;
+ int i;
+#ifdef _UFC_32_
+ long32 v, *k2 = &_ufc_keytab[0][0];
+#endif
+#ifdef _UFC_64_
+ long64 v, *k2 = &_ufc_keytab[0];
+#endif
+
+ v1 = v2 = 0; k1 = &do_pc1[0][0][0];
+ for(i = 8; i--;) {
+ v1 |= k1[*key & 0x7f]; k1 += 128;
+ v2 |= k1[*key++ & 0x7f]; k1 += 128;
+ }
+
+ for(i = 0; i < 16; i++) {
+ k1 = &do_pc2[0][0];
+
+ v1 = (v1 << rots[i]) | (v1 >> (28 - rots[i]));
+ v = k1[(v1 >> 21) & 0x7f]; k1 += 128;
+ v |= k1[(v1 >> 14) & 0x7f]; k1 += 128;
+ v |= k1[(v1 >> 7) & 0x7f]; k1 += 128;
+ v |= k1[(v1 ) & 0x7f]; k1 += 128;
+
+#ifdef _UFC_32_
+ *k2++ = v;
+ v = 0;
+#endif
+#ifdef _UFC_64_
+ v <<= 32;
+#endif
+
+ v2 = (v2 << rots[i]) | (v2 >> (28 - rots[i]));
+ v |= k1[(v2 >> 21) & 0x7f]; k1 += 128;
+ v |= k1[(v2 >> 14) & 0x7f]; k1 += 128;
+ v |= k1[(v2 >> 7) & 0x7f]; k1 += 128;
+ v |= k1[(v2 ) & 0x7f];
+
+ *k2++ = v;
+ }
+
+ direction = 0;
+ }
+
+/*
+ * Undo an extra E selection and do final permutations
+ */
+
+ufc_long *_ufc_dofinalperm(ufc_long l1, ufc_long l2, ufc_long r1, ufc_long r2)
+ { ufc_long v1, v2, x;
+ static ufc_long ary[2];
+
+ x = (l1 ^ l2) & current_saltbits; l1 ^= x; l2 ^= x;
+ x = (r1 ^ r2) & current_saltbits; r1 ^= x; r2 ^= x;
+
+ v1=v2=0; l1 >>= 3; l2 >>= 3; r1 >>= 3; r2 >>= 3;
+
+ v1 |= efp[15][ r2 & 0x3f][0]; v2 |= efp[15][ r2 & 0x3f][1];
+ v1 |= efp[14][(r2 >>= 6) & 0x3f][0]; v2 |= efp[14][ r2 & 0x3f][1];
+ v1 |= efp[13][(r2 >>= 10) & 0x3f][0]; v2 |= efp[13][ r2 & 0x3f][1];
+ v1 |= efp[12][(r2 >>= 6) & 0x3f][0]; v2 |= efp[12][ r2 & 0x3f][1];
+
+ v1 |= efp[11][ r1 & 0x3f][0]; v2 |= efp[11][ r1 & 0x3f][1];
+ v1 |= efp[10][(r1 >>= 6) & 0x3f][0]; v2 |= efp[10][ r1 & 0x3f][1];
+ v1 |= efp[ 9][(r1 >>= 10) & 0x3f][0]; v2 |= efp[ 9][ r1 & 0x3f][1];
+ v1 |= efp[ 8][(r1 >>= 6) & 0x3f][0]; v2 |= efp[ 8][ r1 & 0x3f][1];
+
+ v1 |= efp[ 7][ l2 & 0x3f][0]; v2 |= efp[ 7][ l2 & 0x3f][1];
+ v1 |= efp[ 6][(l2 >>= 6) & 0x3f][0]; v2 |= efp[ 6][ l2 & 0x3f][1];
+ v1 |= efp[ 5][(l2 >>= 10) & 0x3f][0]; v2 |= efp[ 5][ l2 & 0x3f][1];
+ v1 |= efp[ 4][(l2 >>= 6) & 0x3f][0]; v2 |= efp[ 4][ l2 & 0x3f][1];
+
+ v1 |= efp[ 3][ l1 & 0x3f][0]; v2 |= efp[ 3][ l1 & 0x3f][1];
+ v1 |= efp[ 2][(l1 >>= 6) & 0x3f][0]; v2 |= efp[ 2][ l1 & 0x3f][1];
+ v1 |= efp[ 1][(l1 >>= 10) & 0x3f][0]; v2 |= efp[ 1][ l1 & 0x3f][1];
+ v1 |= efp[ 0][(l1 >>= 6) & 0x3f][0]; v2 |= efp[ 0][ l1 & 0x3f][1];
+
+ ary[0] = v1; ary[1] = v2;
+ return ary;
+ }
+
+/*
+ * crypt only: convert from 64 bit to 11 bit ASCII
+ * prefixing with the salt
+ */
+
+static char *output_conversion(ufc_long v1, ufc_long v2, const char *salt)
+ { static char outbuf[14];
+ int i, s;
+
+ outbuf[0] = salt[0];
+ outbuf[1] = salt[1] ? salt[1] : salt[0];
+
+ for(i = 0; i < 5; i++)
+ outbuf[i + 2] = bin_to_ascii((v1 >> (26 - 6 * i)) & 0x3f);
+
+ s = (v2 & 0xf) << 2;
+ v2 = (v2 >> 2) | ((v1 & 0x3) << 30);
+
+ for(i = 5; i < 10; i++)
+ outbuf[i + 2] = bin_to_ascii((v2 >> (56 - 6 * i)) & 0x3f);
+
+ outbuf[12] = bin_to_ascii(s);
+ outbuf[13] = 0;
+
+ return outbuf;
+ }
+
+/*
+ * UNIX crypt function
+ */
+
+static ufc_long *_ufc_doit(ufc_long , ufc_long, ufc_long, ufc_long, ufc_long);
+
+char *ufc_crypt(const char *key,const char *salt)
+ { ufc_long *s;
+ char ktab[9];
+
+ /*
+ * Hack DES tables according to salt
+ */
+ setup_salt(salt);
+
+ /*
+ * Setup key schedule
+ */
+ clearmem(ktab, sizeof ktab);
+ strncpy(ktab, key, 8);
+ ufc_mk_keytab(ktab);
+
+ /*
+ * Go for the 25 DES encryptions
+ */
+ s = _ufc_doit((ufc_long)0, (ufc_long)0,
+ (ufc_long)0, (ufc_long)0, (ufc_long)25);
+
+ /*
+ * And convert back to 6 bit ASCII
+ */
+ return output_conversion(s[0], s[1], salt);
+ }
+
+
+#ifdef _UFC_32_
+
+/*
+ * 32 bit version
+ */
+
+extern long32 _ufc_keytab[16][2];
+extern long32 _ufc_sb0[], _ufc_sb1[], _ufc_sb2[], _ufc_sb3[];
+
+#define SBA(sb, v) (*(long32*)((char*)(sb)+(v)))
+
+static ufc_long *_ufc_doit(ufc_long l1, ufc_long l2, ufc_long r1, ufc_long r2, ufc_long itr)
+ { int i;
+ long32 s, *k;
+
+ while(itr--) {
+ k = &_ufc_keytab[0][0];
+ for(i=8; i--; ) {
+ s = *k++ ^ r1;
+ l1 ^= SBA(_ufc_sb1, s & 0xffff); l2 ^= SBA(_ufc_sb1, (s & 0xffff)+4);
+ l1 ^= SBA(_ufc_sb0, s >>= 16); l2 ^= SBA(_ufc_sb0, (s) +4);
+ s = *k++ ^ r2;
+ l1 ^= SBA(_ufc_sb3, s & 0xffff); l2 ^= SBA(_ufc_sb3, (s & 0xffff)+4);
+ l1 ^= SBA(_ufc_sb2, s >>= 16); l2 ^= SBA(_ufc_sb2, (s) +4);
+
+ s = *k++ ^ l1;
+ r1 ^= SBA(_ufc_sb1, s & 0xffff); r2 ^= SBA(_ufc_sb1, (s & 0xffff)+4);
+ r1 ^= SBA(_ufc_sb0, s >>= 16); r2 ^= SBA(_ufc_sb0, (s) +4);
+ s = *k++ ^ l2;
+ r1 ^= SBA(_ufc_sb3, s & 0xffff); r2 ^= SBA(_ufc_sb3, (s & 0xffff)+4);
+ r1 ^= SBA(_ufc_sb2, s >>= 16); r2 ^= SBA(_ufc_sb2, (s) +4);
+ }
+ s=l1; l1=r1; r1=s; s=l2; l2=r2; r2=s;
+ }
+ return _ufc_dofinalperm(l1, l2, r1, r2);
+ }
+
+#endif
+
+#ifdef _UFC_64_
+
+/*
+ * 64 bit version
+ */
+
+extern long64 _ufc_keytab[16];
+extern long64 _ufc_sb0[], _ufc_sb1[], _ufc_sb2[], _ufc_sb3[];
+
+#define SBA(sb, v) (*(long64*)((char*)(sb)+(v)))
+
+static ufc_long *_ufc_doit(ufc_long l1, ufc_long l2, ufc_long r1, ufc_long r2, ufc_long itr)
+ { int i;
+ long64 l, r, s, *k;
+
+ l = (((long64)l1) << 32) | ((long64)l2);
+ r = (((long64)r1) << 32) | ((long64)r2);
+
+ while(itr--) {
+ k = &_ufc_keytab[0];
+ for(i=8; i--; ) {
+ s = *k++ ^ r;
+ l ^= SBA(_ufc_sb3, (s >> 0) & 0xffff);
+ l ^= SBA(_ufc_sb2, (s >> 16) & 0xffff);
+ l ^= SBA(_ufc_sb1, (s >> 32) & 0xffff);
+ l ^= SBA(_ufc_sb0, (s >> 48) & 0xffff);
+
+ s = *k++ ^ l;
+ r ^= SBA(_ufc_sb3, (s >> 0) & 0xffff);
+ r ^= SBA(_ufc_sb2, (s >> 16) & 0xffff);
+ r ^= SBA(_ufc_sb1, (s >> 32) & 0xffff);
+ r ^= SBA(_ufc_sb0, (s >> 48) & 0xffff);
+ }
+ s=l; l=r; r=s;
+ }
+
+ l1 = l >> 32; l2 = l & 0xffffffff;
+ r1 = r >> 32; r2 = r & 0xffffffff;
+ return _ufc_dofinalperm(l1, l2, r1, r2);
+ }
+
+#endif
+
+#define crypt ufc_crypt
+#endif
+
+main()
+{
+ char passwd[9];
+ char salt[9];
+ char c_out1[256];
+ char c_out2[256];
+
+ char expected_out[14];
+
+ strcpy(expected_out, "12yJ.Of/NQ.Pk");
+ strcpy(passwd, "12345678");
+ strcpy(salt, "12345678");
+
+ strcpy(c_out1, crypt(passwd, salt));
+ salt[2] = '\0';
+ strcpy(c_out2, crypt(passwd, salt));
+
+ /*
+ * If the non-trucated salt fails but the
+ * truncated salt succeeds then exit 1.
+ */
+
+ if((strcmp(c_out1, expected_out) != 0) &&
+ (strcmp(c_out2, expected_out) == 0))
+ exit(1);
+
+#ifdef HAVE_BIGCRYPT
+ /*
+ * Try the same with bigcrypt...
+ */
+
+ {
+ char big_passwd[17];
+ char big_salt[17];
+ char big_c_out1[256];
+ char big_c_out2[256];
+ char big_expected_out[27];
+
+ strcpy(big_passwd, "1234567812345678");
+ strcpy(big_salt, "1234567812345678");
+ strcpy(big_expected_out, "12yJ.Of/NQ.PklfyCuHi/rwM");
+
+ strcpy(big_c_out1, bigcrypt(big_passwd, big_salt));
+ big_salt[2] = '\0';
+ strcpy(big_c_out2, bigcrypt(big_passwd, big_salt));
+
+ /*
+ * If the non-trucated salt fails but the
+ * truncated salt succeeds then exit 1.
+ */
+
+ if((strcmp(big_c_out1, big_expected_out) != 0) &&
+ (strcmp(big_c_out2, big_expected_out) == 0))
+ exit(1);
+
+ }
+#endif
+
+ exit(0);
+}
diff --git a/source/tests/fcntl_lock.c b/source/tests/fcntl_lock.c
new file mode 100644
index 00000000000..3dc12a38973
--- /dev/null
+++ b/source/tests/fcntl_lock.c
@@ -0,0 +1,121 @@
+/* test whether fcntl locking works on this system */
+
+#if defined(HAVE_UNISTD_H)
+#include <unistd.h>
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/types.h>
+
+#ifdef HAVE_FCNTL_H
+#include <fcntl.h>
+#endif
+
+#ifdef HAVE_SYS_FCNTL_H
+#include <sys/fcntl.h>
+#endif
+
+#ifdef HAVE_SYS_WAIT_H
+#include <sys/wait.h>
+#endif
+
+#include <errno.h>
+
+static int sys_waitpid(pid_t pid,int *status,int options)
+{
+#ifdef HAVE_WAITPID
+ return waitpid(pid,status,options);
+#else /* USE_WAITPID */
+ return wait4(pid, status, options, NULL);
+#endif /* USE_WAITPID */
+}
+
+#define DATA "conftest.fcntl"
+
+#ifndef SEEK_SET
+#define SEEK_SET 0
+#endif
+
+/* lock a byte range in a open file */
+int main(int argc, char *argv[])
+{
+ struct flock lock;
+ int fd, ret, status=1;
+ pid_t pid;
+ char *testdir = NULL;
+
+ testdir = getenv("TESTDIR");
+ if (testdir) chdir(testdir);
+
+ alarm(10);
+
+ if (!(pid=fork())) {
+ sleep(2);
+ fd = open(DATA, O_RDONLY);
+
+ if (fd == -1) {
+ fprintf(stderr,"ERROR: failed to open %s (errno=%d)\n",
+ DATA, (int)errno);
+ exit(1);
+ }
+
+ lock.l_type = F_WRLCK;
+ lock.l_whence = SEEK_SET;
+ lock.l_start = 0;
+ lock.l_len = 4;
+ lock.l_pid = getpid();
+
+ lock.l_type = F_WRLCK;
+
+ /* check if a lock applies */
+ ret = fcntl(fd,F_GETLK,&lock);
+
+ if ((ret == -1) ||
+ (lock.l_type == F_UNLCK)) {
+ fprintf(stderr,"ERROR: lock test failed (ret=%d errno=%d)\n", ret, (int)errno);
+ exit(1);
+ } else {
+ exit(0);
+ }
+ }
+
+ unlink(DATA);
+ fd = open(DATA, O_RDWR|O_CREAT|O_EXCL, 0600);
+
+ if (fd == -1) {
+ fprintf(stderr,"ERROR: failed to open %s (errno=%d)\n",
+ DATA, (int)errno);
+ exit(1);
+ }
+
+ lock.l_type = F_WRLCK;
+ lock.l_whence = SEEK_SET;
+ lock.l_start = 0;
+ lock.l_len = 4;
+ lock.l_pid = getpid();
+
+ /* set a 4 byte write lock */
+ fcntl(fd,F_SETLK,&lock);
+
+ sys_waitpid(pid, &status, 0);
+
+ unlink(DATA);
+
+#if defined(WIFEXITED) && defined(WEXITSTATUS)
+ if(WIFEXITED(status)) {
+ status = WEXITSTATUS(status);
+ } else {
+ status = 1;
+ }
+#else /* defined(WIFEXITED) && defined(WEXITSTATUS) */
+ status = (status == 0) ? 0 : 1;
+#endif /* defined(WIFEXITED) && defined(WEXITSTATUS) */
+
+ if (status) {
+ fprintf(stderr,"ERROR: lock test failed with status=%d\n",
+ status);
+ }
+
+ exit(status);
+}
diff --git a/source/tests/fcntl_lock64.c b/source/tests/fcntl_lock64.c
new file mode 100644
index 00000000000..e5ecd88fd04
--- /dev/null
+++ b/source/tests/fcntl_lock64.c
@@ -0,0 +1,96 @@
+/* test whether 64 bit fcntl locking really works on this system */
+
+#if defined(HAVE_UNISTD_H)
+#include <unistd.h>
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/types.h>
+
+#ifdef HAVE_FCNTL_H
+#include <fcntl.h>
+#endif
+
+#ifdef HAVE_SYS_FCNTL_H
+#include <sys/fcntl.h>
+#endif
+
+#include <errno.h>
+
+static int sys_waitpid(pid_t pid,int *status,int options)
+{
+#ifdef HAVE_WAITPID
+ return waitpid(pid,status,options);
+#else /* USE_WAITPID */
+ return wait4(pid, status, options, NULL);
+#endif /* USE_WAITPID */
+}
+
+#define DATA "conftest.fcntl64"
+
+/* lock a byte range in a open file */
+int main(int argc, char *argv[])
+{
+ struct flock64 lock;
+ int fd, ret, status=1;
+ pid_t pid;
+
+ if (!(pid=fork())) {
+ sleep(2);
+ fd = open64(DATA, O_RDONLY);
+
+ if (fd == -1) exit(1);
+
+ lock.l_type = F_WRLCK;
+ lock.l_whence = SEEK_SET;
+ lock.l_start = 0;
+ lock.l_len = 4;
+ lock.l_pid = getpid();
+
+ lock.l_type = F_WRLCK;
+
+ /* check if a lock applies */
+ ret = fcntl(fd,F_GETLK64,&lock);
+
+ if ((ret == -1) ||
+ (lock.l_type == F_UNLCK)) {
+/* printf("No lock conflict\n"); */
+ exit(1);
+ } else {
+/* printf("lock conflict\n"); */
+ exit(0);
+ }
+ }
+
+ fd = open64(DATA, O_RDWR|O_CREAT|O_TRUNC, 0600);
+
+ lock.l_type = F_WRLCK;
+ lock.l_whence = SEEK_SET;
+#if defined(COMPILER_SUPPORTS_LL)
+ lock.l_start = 0x100000000LL;
+#else
+ lock.l_start = 0x100000000;
+#endif
+ lock.l_len = 4;
+ lock.l_pid = getpid();
+
+ /* set a 4 byte write lock */
+ fcntl(fd,F_SETLK64,&lock);
+
+ sys_waitpid(pid, &status, 0);
+
+#if defined(WIFEXITED) && defined(WEXITSTATUS)
+ if(WIFEXITED(status)) {
+ status = WEXITSTATUS(status);
+ } else {
+ status = 1;
+ }
+#else /* defined(WIFEXITED) && defined(WEXITSTATUS) */
+ status = (status == 0) ? 0 : 1;
+#endif /* defined(WIFEXITED) && defined(WEXITSTATUS) */
+
+ unlink(DATA);
+
+ exit(status);
+}
diff --git a/source/tests/ftruncate.c b/source/tests/ftruncate.c
new file mode 100644
index 00000000000..93282782eed
--- /dev/null
+++ b/source/tests/ftruncate.c
@@ -0,0 +1,27 @@
+/* test whether ftruncte() can extend a file */
+
+#if defined(HAVE_UNISTD_H)
+#include <unistd.h>
+#endif
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
+#define DATA "conftest.trunc"
+#define LEN 7663
+
+main()
+{
+ int *buf;
+ int fd = open(DATA,O_RDWR|O_CREAT|O_TRUNC,0666);
+
+ ftruncate(fd, LEN);
+
+ unlink(DATA);
+
+ if (lseek(fd, 0, SEEK_END) == LEN) {
+ exit(0);
+ }
+ exit(1);
+}
diff --git a/source/tests/getgroups.c b/source/tests/getgroups.c
new file mode 100644
index 00000000000..343fd5a184f
--- /dev/null
+++ b/source/tests/getgroups.c
@@ -0,0 +1,66 @@
+/* this tests whether getgroups actually returns lists of integers
+ rather than gid_t. The test only works if the user running
+ the test is in at least 1 group
+
+ The test is designed to check for those broken OSes that define
+ getgroups() as returning an array of gid_t but actually return a
+ array of ints! Ultrix is one culprit
+ */
+
+#if defined(HAVE_UNISTD_H)
+#include <unistd.h>
+#endif
+
+#include <sys/types.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <grp.h>
+
+main()
+{
+ int i;
+ int *igroups;
+ char *cgroups;
+ int grp = 0;
+ int ngroups = getgroups(0,&grp);
+
+ if (sizeof(gid_t) == sizeof(int)) {
+ fprintf(stderr,"gid_t and int are the same size\n");
+ exit(1);
+ }
+
+ if (ngroups <= 0)
+ ngroups = 32;
+
+ igroups = (int *)malloc(sizeof(int)*ngroups);
+
+ for (i=0;i<ngroups;i++)
+ igroups[i] = 0x42424242;
+
+ ngroups = getgroups(ngroups,(gid_t *)igroups);
+
+ if (igroups[0] == 0x42424242)
+ ngroups = 0;
+
+ if (ngroups == 0) {
+ printf("WARNING: can't determine getgroups return type\n");
+ exit(1);
+ }
+
+ cgroups = (char *)igroups;
+
+ if (ngroups == 1 &&
+ cgroups[2] == 0x42 && cgroups[3] == 0x42) {
+ fprintf(stderr,"getgroups returns gid_t\n");
+ exit(1);
+ }
+
+ for (i=0;i<ngroups;i++) {
+ if (igroups[i] == 0x42424242) {
+ fprintf(stderr,"getgroups returns gid_t\n");
+ exit(1);
+ }
+ }
+
+ exit(0);
+}
diff --git a/source/tests/shared_mmap.c b/source/tests/shared_mmap.c
new file mode 100644
index 00000000000..fcef75d0d61
--- /dev/null
+++ b/source/tests/shared_mmap.c
@@ -0,0 +1,68 @@
+/* this tests whether we can use a shared writeable mmap on a file -
+ as needed for the mmap varient of FAST_SHARE_MODES */
+
+#if defined(HAVE_UNISTD_H)
+#include <unistd.h>
+#endif
+#include <sys/mman.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
+#define DATA "conftest.mmap"
+
+#ifndef MAP_FILE
+#define MAP_FILE 0
+#endif
+
+main()
+{
+ int *buf;
+ int i;
+ int fd = open(DATA,O_RDWR|O_CREAT|O_TRUNC,0666);
+ int count=7;
+
+ if (fd == -1) exit(1);
+
+ for (i=0;i<10000;i++) {
+ write(fd,&i,sizeof(i));
+ }
+
+ close(fd);
+
+ if (fork() == 0) {
+ fd = open(DATA,O_RDWR);
+ if (fd == -1) exit(1);
+
+ buf = (int *)mmap(NULL, 10000*sizeof(int),
+ (PROT_READ | PROT_WRITE),
+ MAP_FILE | MAP_SHARED,
+ fd, 0);
+
+ while (count-- && buf[9124] != 55732) sleep(1);
+
+ if (count <= 0) exit(1);
+
+ buf[1763] = 7268;
+ exit(0);
+ }
+
+ fd = open(DATA,O_RDWR);
+ if (fd == -1) exit(1);
+
+ buf = (int *)mmap(NULL, 10000*sizeof(int),
+ (PROT_READ | PROT_WRITE),
+ MAP_FILE | MAP_SHARED,
+ fd, 0);
+
+ if (buf == (int *)-1) exit(1);
+
+ buf[9124] = 55732;
+
+ while (count-- && buf[1763] != 7268) sleep(1);
+
+ unlink(DATA);
+
+ if (count > 0) exit(0);
+ exit(1);
+}
diff --git a/source/tests/shlib.c b/source/tests/shlib.c
new file mode 100644
index 00000000000..761d9fd5c57
--- /dev/null
+++ b/source/tests/shlib.c
@@ -0,0 +1,6 @@
+/* a trivial function used to test building shared libraries */
+
+int foo(void)
+{
+ return 1;
+}
diff --git a/source/tests/summary.c b/source/tests/summary.c
new file mode 100644
index 00000000000..79a530b0136
--- /dev/null
+++ b/source/tests/summary.c
@@ -0,0 +1,30 @@
+#include <stdio.h>
+
+main()
+{
+#if !(defined(HAVE_FCNTL_LOCK) || defined(HAVE_STRUCT_FLOCK64))
+ printf("ERROR: No locking available. Running Samba would be unsafe\n");
+ exit(1);
+#endif
+
+#if !(defined(HAVE_IFACE_IFCONF) || defined(HAVE_IFACE_IFREQ) || defined(HAVE_IFACE_AIX))
+ printf("WARNING: No automated network interface determination\n");
+#endif
+
+#if !(defined(USE_SETEUID) || defined(USE_SETREUID) || defined(USE_SETRESUID) || defined(USE_SETUIDX))
+ printf("ERROR: no seteuid method available\n");
+ exit(1);
+#endif
+
+#if !(defined(STAT_STATVFS) || defined(STAT_STATVFS64) || defined(STAT_STATFS3_OSF1) || defined(STAT_STATFS2_BSIZE) || defined(STAT_STATFS4) || defined(STAT_STATFS2_FSIZE) || defined(STAT_STATFS2_FS_DATA))
+ printf("ERROR: No disk free routine!\n");
+ exit(1);
+#endif
+
+#if !((defined(HAVE_RANDOM) || defined(HAVE_RAND)) && (defined(HAVE_SRANDOM) || defined(HAVE_SRAND)))
+ printf("ERROR: No random or srandom routine!\n");
+ exit(1);
+#endif
+
+ exit(0);
+}
diff --git a/source/tests/sysquotas.c b/source/tests/sysquotas.c
new file mode 100644
index 00000000000..2aa643326c0
--- /dev/null
+++ b/source/tests/sysquotas.c
@@ -0,0 +1,94 @@
+/* this test should find out what quota api is available on the os */
+
+#if defined(HAVE_QUOTACTL_4A)
+/* long quotactl(int cmd, char *special, qid_t id, caddr_t addr) */
+
+#ifdef HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+
+#ifdef HAVE_ASM_TYPES_H
+#include <asm/types.h>
+#endif
+
+#if defined(HAVE_LINUX_QUOTA_H)
+# include <linux/quota.h>
+# if defined(HAVE_STRUCT_IF_DQBLK)
+# define SYS_DQBLK if_dqblk
+# elif defined(HAVE_STRUCT_MEM_DQBLK)
+# define SYS_DQBLK mem_dqblk
+# endif
+#elif defined(HAVE_SYS_QUOTA_H)
+# include <sys/quota.h>
+#endif
+
+#ifndef SYS_DQBLK
+#define SYS_DQBLK dqblk
+#endif
+
+ int autoconf_quota(void)
+{
+ int ret = -1;
+ struct SYS_DQBLK D;
+
+ ret = quotactl(Q_GETQUOTA,"/dev/hda1",0,(void *)&D);
+
+ return ret;
+}
+
+#elif defined(HAVE_QUOTACTL_4B)
+/* int quotactl(const char *path, int cmd, int id, char *addr); */
+
+#ifdef HAVE_SYS_QUOTA_H
+#include <sys/quota.h>
+#else /* *BSD */
+#include <sys/types.h>
+#include <ufs/ufs/quota.h>
+#include <machine/param.h>
+#endif
+
+ int autoconf_quota(void)
+{
+ int ret = -1;
+ struct dqblk D;
+
+ ret = quotactl("/",Q_GETQUOTA,0,(char *) &D);
+
+ return ret;
+}
+
+#elif defined(HAVE_QUOTACTL_3)
+/* int quotactl (char *spec, int request, char *arg); */
+
+#ifdef HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+#ifdef HAVE_SYS_QUOTA_H
+#include <sys/quota.h>
+#endif
+
+ int autoconf_quota(void)
+{
+ int ret = -1;
+ struct q_request request;
+
+ ret = quotactl("/", Q_GETQUOTA, &request);
+
+ return ret;
+}
+
+#elif defined(HAVE_QUOTACTL_2)
+
+#error HAVE_QUOTACTL_2 not implemented
+
+#else
+
+#error Unknow QUOTACTL prototype
+
+#endif
+
+ int main(void)
+{
+ autoconf_quota();
+ return 0;
+}
diff --git a/source/tests/trivial.c b/source/tests/trivial.c
new file mode 100644
index 00000000000..2723637a0ff
--- /dev/null
+++ b/source/tests/trivial.c
@@ -0,0 +1,4 @@
+main()
+{
+ exit(0);
+}
diff --git a/source/tests/unixsock.c b/source/tests/unixsock.c
new file mode 100644
index 00000000000..f2765d68f67
--- /dev/null
+++ b/source/tests/unixsock.c
@@ -0,0 +1,93 @@
+/* -*- c-file-style: "linux" -*-
+ *
+ * Try creating a Unix-domain socket, opening it, and reading from it.
+ * The POSIX name for these is AF_LOCAL/PF_LOCAL.
+ *
+ * This is used by the Samba autoconf scripts to detect systems which
+ * don't have Unix-domain sockets, such as (probably) VMS, or systems
+ * on which they are broken under some conditions, such as RedHat 7.0
+ * (unpatched). We can't build WinBind there at the moment.
+ *
+ * Coding standard says to always use exit() for this, not return, so
+ * we do.
+ *
+ * Martin Pool <mbp@samba.org>, June 2000. */
+
+/* TODO: Look for AF_LOCAL (most standard), AF_UNIX, and AF_FILE. */
+
+#include <stdio.h>
+
+#ifdef HAVE_SYS_SOCKET_H
+# include <sys/socket.h>
+#endif
+
+#ifdef HAVE_SYS_UN_H
+# include <sys/un.h>
+#endif
+
+#ifdef HAVE_SYS_TYPES_H
+# include <sys/types.h>
+#endif
+
+#if HAVE_SYS_WAIT_H
+# include <sys/wait.h>
+#endif
+
+#if HAVE_ERRNO_DECL
+# include <errno.h>
+#else
+extern int errno;
+#endif
+
+static int bind_socket(char const *filename)
+{
+ int sock_fd;
+ struct sockaddr_un name;
+ size_t size;
+
+ /* Create the socket. */
+ if ((sock_fd = socket(PF_LOCAL, SOCK_STREAM, 0)) < 0) {
+ perror ("socket(PF_LOCAL, SOCK_STREAM)");
+ exit(1);
+ }
+
+ /* Bind a name to the socket. */
+ name.sun_family = AF_LOCAL;
+ strncpy(name.sun_path, filename, sizeof (name.sun_path));
+
+ /* The size of the address is
+ the offset of the start of the filename,
+ plus its length,
+ plus one for the terminating null byte.
+ Alternatively you can just do:
+ size = SUN_LEN (&name);
+ */
+ size = SUN_LEN(&name);
+ /* XXX: This probably won't work on unfriendly libcs */
+
+ if (bind(sock_fd, (struct sockaddr *) &name, size) < 0) {
+ perror ("bind");
+ exit(1);
+ }
+
+ return sock_fd;
+}
+
+
+int main(void)
+{
+ int sock_fd;
+ int kid;
+ char const *filename = "conftest.unixsock.sock";
+
+ /* abolish hanging */
+ alarm(15); /* secs */
+
+ if ((sock_fd = bind_socket(filename)) < 0)
+ exit(1);
+
+ /* the socket will be deleted when autoconf cleans up these
+ files. */
+
+ exit(0);
+}
diff --git a/source/torture/cmd_vfs.c b/source/torture/cmd_vfs.c
new file mode 100644
index 00000000000..bfce4b88b41
--- /dev/null
+++ b/source/torture/cmd_vfs.c
@@ -0,0 +1,1060 @@
+/*
+ Unix SMB/CIFS implementation.
+ VFS module functions
+
+ Copyright (C) Simo Sorce 2002
+ Copyright (C) Eric Lorimer 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 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 "vfstest.h"
+
+static const char *null_string = "";
+
+static NTSTATUS cmd_load_module(struct vfs_state *vfs, TALLOC_CTX *mem_ctx, int argc, const char **argv)
+{
+ int i;
+
+ if (argc < 2) {
+ printf("Usage: load <modules>\n");
+ return NT_STATUS_OK;
+ }
+
+ for (i=argc-1;i>0;i--) {
+ if (!vfs_init_custom(vfs->conn, argv[i])) {
+ DEBUG(0, ("load: (vfs_init_custom failed for %s)\n", argv[i]));
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+ }
+ printf("load: ok\n");
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS cmd_populate(struct vfs_state *vfs, TALLOC_CTX *mem_ctx, int argc, const char **argv)
+{
+ char c;
+ size_t size;
+ if (argc != 3) {
+ printf("Usage: populate <char> <size>\n");
+ return NT_STATUS_OK;
+ }
+ c = argv[1][0];
+ size = atoi(argv[2]);
+ vfs->data = (char *)talloc(mem_ctx, size);
+ if (vfs->data == NULL) {
+ printf("populate: error=-1 (not enough memory)");
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+ memset(vfs->data, c, size);
+ vfs->data_size = size;
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS cmd_show_data(struct vfs_state *vfs, TALLOC_CTX *mem_ctx, int argc, const char **argv)
+{
+ size_t offset;
+ size_t len;
+ if (argc != 1 && argc != 3) {
+ printf("Usage: showdata [<offset> <len>]\n");
+ return NT_STATUS_OK;
+ }
+ if (vfs->data == NULL || vfs->data_size == 0) {
+ printf("show_data: error=-1 (buffer empty)\n");
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ if (argc == 3) {
+ offset = atoi(argv[1]);
+ len = atoi(argv[2]);
+ } else {
+ offset = 0;
+ len = vfs->data_size;
+ }
+ if ((offset + len) > vfs->data_size) {
+ printf("show_data: error=-1 (not enough data in buffer)\n");
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+ dump_data(0, (char *)(vfs->data) + offset, len);
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS cmd_connect(struct vfs_state *vfs, TALLOC_CTX *mem_ctx, int argc, const char **argv)
+{
+ SMB_VFS_CONNECT(vfs->conn, lp_servicename(vfs->conn->service), "vfstest");
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS cmd_disconnect(struct vfs_state *vfs, TALLOC_CTX *mem_ctx, int argc, const char **argv)
+{
+ SMB_VFS_DISCONNECT(vfs->conn);
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS cmd_disk_free(struct vfs_state *vfs, TALLOC_CTX *mem_ctx, int argc, const char **argv)
+{
+ SMB_BIG_UINT diskfree, bsize, dfree, dsize;
+ if (argc != 2) {
+ printf("Usage: disk_free <path>\n");
+ return NT_STATUS_OK;
+ }
+
+ diskfree = SMB_VFS_DISK_FREE(vfs->conn, argv[1], False, &bsize, &dfree, &dsize);
+ printf("disk_free: %lu, bsize = %lu, dfree = %lu, dsize = %lu\n",
+ (unsigned long)diskfree,
+ (unsigned long)bsize,
+ (unsigned long)dfree,
+ (unsigned long)dsize);
+ return NT_STATUS_OK;
+}
+
+
+static NTSTATUS cmd_opendir(struct vfs_state *vfs, TALLOC_CTX *mem_ctx, int argc, const char **argv)
+{
+ if (argc != 2) {
+ printf("Usage: opendir <fname>\n");
+ return NT_STATUS_OK;
+ }
+
+ vfs->currentdir = SMB_VFS_OPENDIR(vfs->conn, argv[1]);
+ if (vfs->currentdir == NULL) {
+ printf("opendir error=%d (%s)\n", errno, strerror(errno));
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ printf("opendir: ok\n");
+ return NT_STATUS_OK;
+}
+
+
+static NTSTATUS cmd_readdir(struct vfs_state *vfs, TALLOC_CTX *mem_ctx, int argc, const char **argv)
+{
+ struct dirent *dent;
+
+ if (vfs->currentdir == NULL) {
+ printf("readdir: error=-1 (no open directory)\n");
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ dent = SMB_VFS_READDIR(vfs->conn, vfs->currentdir);
+ if (dent == NULL) {
+ printf("readdir: NULL\n");
+ return NT_STATUS_OK;
+ }
+
+ printf("readdir: %s\n", dent->d_name);
+ return NT_STATUS_OK;
+}
+
+
+static NTSTATUS cmd_mkdir(struct vfs_state *vfs, TALLOC_CTX *mem_ctx, int argc, const char **argv)
+{
+ if (argc != 2) {
+ printf("Usage: mkdir <path>\n");
+ return NT_STATUS_OK;
+ }
+
+ if (SMB_VFS_MKDIR(vfs->conn, argv[1], 00755) == -1) {
+ printf("mkdir error=%d (%s)\n", errno, strerror(errno));
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ printf("mkdir: ok\n");
+ return NT_STATUS_OK;
+}
+
+
+static NTSTATUS cmd_closedir(struct vfs_state *vfs, TALLOC_CTX *mem_ctx, int argc, const char **argv)
+{
+ int ret;
+
+ if (vfs->currentdir == NULL) {
+ printf("closedir: failure (no directory open)\n");
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ ret = SMB_VFS_CLOSEDIR(vfs->conn, vfs->currentdir);
+ if (ret == -1) {
+ printf("closedir failure: %s\n", strerror(errno));
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ printf("closedir: ok\n");
+ vfs->currentdir = NULL;
+ return NT_STATUS_OK;
+}
+
+
+static NTSTATUS cmd_open(struct vfs_state *vfs, TALLOC_CTX *mem_ctx, int argc, const char **argv)
+{
+ int flags, fd;
+ mode_t mode;
+ const char *flagstr;
+
+ mode = 00400;
+
+ if (argc < 3 || argc > 5) {
+ printf("Usage: open <filename> <flags> <mode>\n");
+ printf(" flags: O = O_RDONLY\n");
+ printf(" R = O_RDWR\n");
+ printf(" W = O_WRONLY\n");
+ printf(" C = O_CREAT\n");
+ printf(" E = O_EXCL\n");
+ printf(" T = O_TRUNC\n");
+ printf(" A = O_APPEND\n");
+ printf(" N = O_NONBLOCK/O_NDELAY\n");
+#ifdef O_SYNC
+ printf(" S = O_SYNC\n");
+#endif
+#ifdef O_NOFOLLOW
+ printf(" F = O_NOFOLLOW\n");
+#endif
+ printf(" mode: see open.2\n");
+ printf(" mode is ignored if C flag not present\n");
+ printf(" mode defaults to 00400\n");
+ return NT_STATUS_OK;
+ }
+ flags = 0;
+ flagstr = argv[2];
+ while (*flagstr) {
+ switch (*flagstr) {
+ case 'O':
+ flags |= O_RDONLY;
+ break;
+ case 'R':
+ flags |= O_RDWR;
+ break;
+ case 'W':
+ flags |= O_WRONLY;
+ break;
+ case 'C':
+ flags |= O_CREAT;
+ break;
+ case 'E':
+ flags |= O_EXCL;
+ break;
+ case 'T':
+ flags |= O_TRUNC;
+ break;
+ case 'A':
+ flags |= O_APPEND;
+ break;
+ case 'N':
+ flags |= O_NONBLOCK;
+ break;
+#ifdef O_SYNC
+ case 'S':
+ flags |= O_SYNC;
+ break;
+#endif
+#ifdef O_NOFOLLOW
+ case 'F':
+ flags |= O_NOFOLLOW;
+ break;
+#endif
+ default:
+ printf("open: error=-1 (invalid flag!)\n");
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+ flagstr++;
+ }
+ if ((flags & O_CREAT) && argc == 4) {
+ if (sscanf(argv[3], "%o", &mode) == 0) {
+ printf("open: error=-1 (invalid mode!)\n");
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+ }
+
+ fd = SMB_VFS_OPEN(vfs->conn, argv[1], flags, mode);
+ if (fd == -1) {
+ printf("open: error=%d (%s)\n", errno, strerror(errno));
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ vfs->files[fd] = (struct files_struct *)malloc(sizeof(struct files_struct));
+ vfs->files[fd]->fsp_name = strdup(argv[1]);
+ vfs->files[fd]->fd = fd;
+ vfs->files[fd]->conn = vfs->conn;
+ printf("open: fd=%d\n", fd);
+ return NT_STATUS_OK;
+}
+
+
+static NTSTATUS cmd_pathfunc(struct vfs_state *vfs, TALLOC_CTX *mem_ctx, int argc, const char **argv)
+{
+ int ret = -1;
+
+ if (argc != 2) {
+ printf("Usage: %s <path>\n", argv[0]);
+ return NT_STATUS_OK;
+ }
+
+ if (strcmp("rmdir", argv[0]) == 0 ) {
+ ret = SMB_VFS_RMDIR(vfs->conn, argv[1]);
+ } else if (strcmp("unlink", argv[0]) == 0 ) {
+ ret = SMB_VFS_UNLINK(vfs->conn, argv[1]);
+ } else if (strcmp("chdir", argv[0]) == 0 ) {
+ ret = SMB_VFS_CHDIR(vfs->conn, argv[1]);
+ } else {
+ printf("%s: error=%d (invalid function name!)\n", argv[0], errno);
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ if (ret == -1) {
+ printf("%s: error=%d (%s)\n", argv[0], errno, strerror(errno));
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ printf("%s: ok\n", argv[0]);
+ return NT_STATUS_OK;
+}
+
+
+static NTSTATUS cmd_close(struct vfs_state *vfs, TALLOC_CTX *mem_ctx, int argc, const char **argv)
+{
+ int fd, ret;
+
+ if (argc != 2) {
+ printf("Usage: close <fd>\n");
+ return NT_STATUS_OK;
+ }
+
+ fd = atoi(argv[1]);
+ if (vfs->files[fd] == NULL) {
+ printf("close: error=-1 (invalid file descriptor)\n");
+ return NT_STATUS_OK;
+ }
+
+ ret = SMB_VFS_CLOSE(vfs->files[fd], fd);
+ if (ret == -1 )
+ printf("close: error=%d (%s)\n", errno, strerror(errno));
+ else
+ printf("close: ok\n");
+
+ SAFE_FREE(vfs->files[fd]->fsp_name);
+ SAFE_FREE(vfs->files[fd]);
+ vfs->files[fd] = NULL;
+ return NT_STATUS_OK;
+}
+
+
+static NTSTATUS cmd_read(struct vfs_state *vfs, TALLOC_CTX *mem_ctx, int argc, const char **argv)
+{
+ int fd;
+ size_t size, rsize;
+
+ if (argc != 3) {
+ printf("Usage: read <fd> <size>\n");
+ return NT_STATUS_OK;
+ }
+
+ /* do some error checking on these */
+ fd = atoi(argv[1]);
+ size = atoi(argv[2]);
+ vfs->data = (char *)talloc(mem_ctx, size);
+ if (vfs->data == NULL) {
+ printf("read: error=-1 (not enough memory)");
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+ vfs->data_size = size;
+
+ rsize = SMB_VFS_READ(vfs->files[fd], fd, vfs->data, size);
+ if (rsize == -1) {
+ printf("read: error=%d (%s)\n", errno, strerror(errno));
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ printf("read: ok\n");
+ return NT_STATUS_OK;
+}
+
+
+static NTSTATUS cmd_write(struct vfs_state *vfs, TALLOC_CTX *mem_ctx, int argc, const char **argv)
+{
+ int fd, size, wsize;
+
+ if (argc != 3) {
+ printf("Usage: write <fd> <size>\n");
+ return NT_STATUS_OK;
+ }
+
+ /* some error checking should go here */
+ fd = atoi(argv[1]);
+ size = atoi(argv[2]);
+ if (vfs->data == NULL) {
+ printf("write: error=-1 (buffer empty, please populate it before writing)");
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ if (vfs->data_size < size) {
+ printf("write: error=-1 (buffer too small, please put some more data in)");
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ wsize = SMB_VFS_WRITE(vfs->files[fd], fd, vfs->data, size);
+
+ if (wsize == -1) {
+ printf("write: error=%d (%s)\n", errno, strerror(errno));
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ printf("write: ok\n");
+ return NT_STATUS_OK;
+}
+
+
+static NTSTATUS cmd_lseek(struct vfs_state *vfs, TALLOC_CTX *mem_ctx, int argc, const char **argv)
+{
+ int fd, offset, whence;
+ SMB_OFF_T pos;
+
+ if (argc != 4) {
+ printf("Usage: lseek <fd> <offset> <whence>\n...where whence is 1 => SEEK_SET, 2 => SEEK_CUR, 3 => SEEK_END\n");
+ return NT_STATUS_OK;
+ }
+
+ fd = atoi(argv[1]);
+ offset = atoi(argv[2]);
+ whence = atoi(argv[3]);
+ switch (whence) {
+ case 1: whence = SEEK_SET; break;
+ case 2: whence = SEEK_CUR; break;
+ default: whence = SEEK_END;
+ }
+
+ pos = SMB_VFS_LSEEK(vfs->files[fd], fd, offset, whence);
+ if (pos == (SMB_OFF_T)-1) {
+ printf("lseek: error=%d (%s)\n", errno, strerror(errno));
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ printf("lseek: ok\n");
+ return NT_STATUS_OK;
+}
+
+
+static NTSTATUS cmd_rename(struct vfs_state *vfs, TALLOC_CTX *mem_ctx, int argc, const char **argv)
+{
+ int ret;
+ if (argc != 3) {
+ printf("Usage: rename <old> <new>\n");
+ return NT_STATUS_OK;
+ }
+
+ ret = SMB_VFS_RENAME(vfs->conn, argv[1], argv[2]);
+ if (ret == -1) {
+ printf("rename: error=%d (%s)\n", errno, strerror(errno));
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ printf("rename: ok\n");
+ return NT_STATUS_OK;
+}
+
+
+static NTSTATUS cmd_fsync(struct vfs_state *vfs, TALLOC_CTX *mem_ctx, int argc, const char **argv)
+{
+ int ret, fd;
+ if (argc != 2) {
+ printf("Usage: fsync <fd>\n");
+ return NT_STATUS_OK;
+ }
+
+ fd = atoi(argv[1]);
+ ret = SMB_VFS_FSYNC(vfs->files[fd], fd);
+ if (ret == -1) {
+ printf("fsync: error=%d (%s)\n", errno, strerror(errno));
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ printf("fsync: ok\n");
+ return NT_STATUS_OK;
+}
+
+
+static NTSTATUS cmd_stat(struct vfs_state *vfs, TALLOC_CTX *mem_ctx, int argc, const char **argv)
+{
+ int ret;
+ const char *user;
+ const char *group;
+ struct passwd *pwd = NULL;
+ struct group *grp = NULL;
+ SMB_STRUCT_STAT st;
+
+ if (argc != 2) {
+ printf("Usage: stat <fname>\n");
+ return NT_STATUS_OK;
+ }
+
+ ret = SMB_VFS_STAT(vfs->conn, argv[1], &st);
+ if (ret == -1) {
+ printf("stat: error=%d (%s)\n", errno, strerror(errno));
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ pwd = sys_getpwuid(st.st_uid);
+ if (pwd != NULL) user = pwd->pw_name;
+ else user = null_string;
+ grp = sys_getgrgid(st.st_gid);
+ if (grp != NULL) group = grp->gr_name;
+ else group = null_string;
+
+ printf("stat: ok\n");
+ printf(" File: %s", argv[1]);
+ if (S_ISREG(st.st_mode)) printf(" Regular File\n");
+ else if (S_ISDIR(st.st_mode)) printf(" Directory\n");
+ else if (S_ISCHR(st.st_mode)) printf(" Character Device\n");
+ else if (S_ISBLK(st.st_mode)) printf(" Block Device\n");
+ else if (S_ISFIFO(st.st_mode)) printf(" Fifo\n");
+ else if (S_ISLNK(st.st_mode)) printf(" Symbolic Link\n");
+ else if (S_ISSOCK(st.st_mode)) printf(" Socket\n");
+ printf(" Size: %10u", (unsigned int)st.st_size);
+#ifdef HAVE_STAT_ST_BLOCKS
+ printf(" Blocks: %9u", (unsigned int)st.st_blocks);
+#endif
+#ifdef HAVE_STAT_ST_BLKSIZE
+ printf(" IO Block: %u\n", (unsigned int)st.st_blksize);
+#endif
+ printf(" Device: 0x%10x", (unsigned int)st.st_dev);
+ printf(" Inode: %10u", (unsigned int)st.st_ino);
+ printf(" Links: %10u\n", (unsigned int)st.st_nlink);
+ printf(" Access: %05o", (st.st_mode) & 007777);
+ printf(" Uid: %5lu/%.16s Gid: %5lu/%.16s\n", (unsigned long)st.st_uid, user,
+ (unsigned long)st.st_gid, group);
+ printf(" Access: %s", ctime(&(st.st_atime)));
+ printf(" Modify: %s", ctime(&(st.st_mtime)));
+ printf(" Change: %s", ctime(&(st.st_ctime)));
+
+ SAFE_FREE(pwd);
+ SAFE_FREE(grp);
+ return NT_STATUS_OK;
+}
+
+
+static NTSTATUS cmd_fstat(struct vfs_state *vfs, TALLOC_CTX *mem_ctx, int argc, const char **argv)
+{
+ int fd;
+ const char *user;
+ const char *group;
+ struct passwd *pwd = NULL;
+ struct group *grp = NULL;
+ SMB_STRUCT_STAT st;
+
+ if (argc != 2) {
+ printf("Usage: fstat <fd>\n");
+ return NT_STATUS_OK;
+ }
+
+ fd = atoi(argv[1]);
+ if (fd < 0 || fd > 1024) {
+ printf("fstat: error=%d (file descriptor out of range)\n", EBADF);
+ return NT_STATUS_OK;
+ }
+
+ if (vfs->files[fd] == NULL) {
+ printf("fstat: error=%d (invalid file descriptor)\n", EBADF);
+ return NT_STATUS_OK;
+ }
+
+ if (SMB_VFS_FSTAT(vfs->files[fd], fd, &st) == -1) {
+ printf("fstat: error=%d (%s)\n", errno, strerror(errno));
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ pwd = sys_getpwuid(st.st_uid);
+ if (pwd != NULL) user = pwd->pw_name;
+ else user = null_string;
+ grp = sys_getgrgid(st.st_gid);
+ if (grp != NULL) group = grp->gr_name;
+ else group = null_string;
+
+ printf("fstat: ok\n");
+ if (S_ISREG(st.st_mode)) printf(" Regular File\n");
+ else if (S_ISDIR(st.st_mode)) printf(" Directory\n");
+ else if (S_ISCHR(st.st_mode)) printf(" Character Device\n");
+ else if (S_ISBLK(st.st_mode)) printf(" Block Device\n");
+ else if (S_ISFIFO(st.st_mode)) printf(" Fifo\n");
+ else if (S_ISLNK(st.st_mode)) printf(" Symbolic Link\n");
+ else if (S_ISSOCK(st.st_mode)) printf(" Socket\n");
+ printf(" Size: %10u", (unsigned int)st.st_size);
+#ifdef HAVE_STAT_ST_BLOCKS
+ printf(" Blocks: %9u", (unsigned int)st.st_blocks);
+#endif
+#ifdef HAVE_STAT_ST_BLKSIZE
+ printf(" IO Block: %u\n", (unsigned int)st.st_blksize);
+#endif
+ printf(" Device: 0x%10x", (unsigned int)st.st_dev);
+ printf(" Inode: %10u", (unsigned int)st.st_ino);
+ printf(" Links: %10u\n", (unsigned int)st.st_nlink);
+ printf(" Access: %05o", (st.st_mode) & 007777);
+ printf(" Uid: %5lu/%.16s Gid: %5lu/%.16s\n", (unsigned long)st.st_uid, user,
+ (unsigned long)st.st_gid, group);
+ printf(" Access: %s", ctime(&(st.st_atime)));
+ printf(" Modify: %s", ctime(&(st.st_mtime)));
+ printf(" Change: %s", ctime(&(st.st_ctime)));
+
+ SAFE_FREE(pwd);
+ SAFE_FREE(grp);
+ return NT_STATUS_OK;
+}
+
+
+static NTSTATUS cmd_lstat(struct vfs_state *vfs, TALLOC_CTX *mem_ctx, int argc, const char **argv)
+{
+ const char *user;
+ const char *group;
+ struct passwd *pwd = NULL;
+ struct group *grp = NULL;
+ SMB_STRUCT_STAT st;
+
+ if (argc != 2) {
+ printf("Usage: lstat <path>\n");
+ return NT_STATUS_OK;
+ }
+
+ if (SMB_VFS_LSTAT(vfs->conn, argv[1], &st) == -1) {
+ printf("lstat: error=%d (%s)\n", errno, strerror(errno));
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ pwd = sys_getpwuid(st.st_uid);
+ if (pwd != NULL) user = pwd->pw_name;
+ else user = null_string;
+ grp = sys_getgrgid(st.st_gid);
+ if (grp != NULL) group = grp->gr_name;
+ else group = null_string;
+
+ printf("lstat: ok\n");
+ if (S_ISREG(st.st_mode)) printf(" Regular File\n");
+ else if (S_ISDIR(st.st_mode)) printf(" Directory\n");
+ else if (S_ISCHR(st.st_mode)) printf(" Character Device\n");
+ else if (S_ISBLK(st.st_mode)) printf(" Block Device\n");
+ else if (S_ISFIFO(st.st_mode)) printf(" Fifo\n");
+ else if (S_ISLNK(st.st_mode)) printf(" Symbolic Link\n");
+ else if (S_ISSOCK(st.st_mode)) printf(" Socket\n");
+ printf(" Size: %10u", (unsigned int)st.st_size);
+#ifdef HAVE_STAT_ST_BLOCKS
+ printf(" Blocks: %9u", (unsigned int)st.st_blocks);
+#endif
+#ifdef HAVE_STAT_ST_BLKSIZE
+ printf(" IO Block: %u\n", (unsigned int)st.st_blksize);
+#endif
+ printf(" Device: 0x%10x", (unsigned int)st.st_dev);
+ printf(" Inode: %10u", (unsigned int)st.st_ino);
+ printf(" Links: %10u\n", (unsigned int)st.st_nlink);
+ printf(" Access: %05o", (st.st_mode) & 007777);
+ printf(" Uid: %5lu/%.16s Gid: %5lu/%.16s\n", (unsigned long)st.st_uid, user,
+ (unsigned long)st.st_gid, group);
+ printf(" Access: %s", ctime(&(st.st_atime)));
+ printf(" Modify: %s", ctime(&(st.st_mtime)));
+ printf(" Change: %s", ctime(&(st.st_ctime)));
+
+ SAFE_FREE(pwd);
+ SAFE_FREE(grp);
+ return NT_STATUS_OK;
+}
+
+
+static NTSTATUS cmd_chmod(struct vfs_state *vfs, TALLOC_CTX *mem_ctx, int argc, const char **argv)
+{
+ mode_t mode;
+ if (argc != 3) {
+ printf("Usage: chmod <path> <mode>\n");
+ return NT_STATUS_OK;
+ }
+
+ mode = atoi(argv[2]);
+ if (SMB_VFS_CHMOD(vfs->conn, argv[1], mode) == -1) {
+ printf("chmod: error=%d (%s)\n", errno, strerror(errno));
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ printf("chmod: ok\n");
+ return NT_STATUS_OK;
+}
+
+
+static NTSTATUS cmd_fchmod(struct vfs_state *vfs, TALLOC_CTX *mem_ctx, int argc, const char **argv)
+{
+ int fd;
+ mode_t mode;
+ if (argc != 3) {
+ printf("Usage: fchmod <fd> <mode>\n");
+ return NT_STATUS_OK;
+ }
+
+ fd = atoi(argv[1]);
+ mode = atoi(argv[2]);
+ if (fd < 0 || fd > 1024) {
+ printf("fchmod: error=%d (file descriptor out of range)\n", EBADF);
+ return NT_STATUS_OK;
+ }
+ if (vfs->files[fd] == NULL) {
+ printf("fchmod: error=%d (invalid file descriptor)\n", EBADF);
+ return NT_STATUS_OK;
+ }
+
+ if (SMB_VFS_FCHMOD(vfs->files[fd], fd, mode) == -1) {
+ printf("fchmod: error=%d (%s)\n", errno, strerror(errno));
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ printf("fchmod: ok\n");
+ return NT_STATUS_OK;
+}
+
+
+static NTSTATUS cmd_chown(struct vfs_state *vfs, TALLOC_CTX *mem_ctx, int argc, const char **argv)
+{
+ uid_t uid;
+ gid_t gid;
+ if (argc != 4) {
+ printf("Usage: chown <path> <uid> <gid>\n");
+ return NT_STATUS_OK;
+ }
+
+ uid = atoi(argv[2]);
+ gid = atoi(argv[3]);
+ if (SMB_VFS_CHOWN(vfs->conn, argv[1], uid, gid) == -1) {
+ printf("chown: error=%d (%s)\n", errno, strerror(errno));
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ printf("chown: ok\n");
+ return NT_STATUS_OK;
+}
+
+
+static NTSTATUS cmd_fchown(struct vfs_state *vfs, TALLOC_CTX *mem_ctx, int argc, const char **argv)
+{
+ uid_t uid;
+ gid_t gid;
+ int fd;
+ if (argc != 4) {
+ printf("Usage: fchown <fd> <uid> <gid>\n");
+ return NT_STATUS_OK;
+ }
+
+ uid = atoi(argv[2]);
+ gid = atoi(argv[3]);
+ fd = atoi(argv[1]);
+ if (fd < 0 || fd > 1024) {
+ printf("fchown: faliure=%d (file descriptor out of range)\n", EBADF);
+ return NT_STATUS_OK;
+ }
+ if (vfs->files[fd] == NULL) {
+ printf("fchown: error=%d (invalid file descriptor)\n", EBADF);
+ return NT_STATUS_OK;
+ }
+ if (SMB_VFS_FCHOWN(vfs->files[fd], fd, uid, gid) == -1) {
+ printf("fchown error=%d (%s)\n", errno, strerror(errno));
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ printf("fchown: ok\n");
+ return NT_STATUS_OK;
+}
+
+
+static NTSTATUS cmd_getwd(struct vfs_state *vfs, TALLOC_CTX *mem_ctx, int argc, const char **argv)
+{
+ char buf[PATH_MAX];
+ if (SMB_VFS_GETWD(vfs->conn, buf) == NULL) {
+ printf("getwd: error=%d (%s)\n", errno, strerror(errno));
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ printf("getwd: %s\n", buf);
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS cmd_utime(struct vfs_state *vfs, TALLOC_CTX *mem_ctx, int argc, const char **argv)
+{
+ struct utimbuf times;
+ if (argc != 4) {
+ printf("Usage: utime <path> <access> <modify>\n");
+ return NT_STATUS_OK;
+ }
+ times.actime = atoi(argv[2]);
+ times.modtime = atoi(argv[3]);
+ if (SMB_VFS_UTIME(vfs->conn, argv[1], &times) != 0) {
+ printf("utime: error=%d (%s)\n", errno, strerror(errno));
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ printf("utime: ok\n");
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS cmd_ftruncate(struct vfs_state *vfs, TALLOC_CTX *mem_ctx, int argc, const char **argv)
+{
+ int fd;
+ SMB_OFF_T off;
+ if (argc != 3) {
+ printf("Usage: ftruncate <fd> <length>\n");
+ return NT_STATUS_OK;
+ }
+
+ fd = atoi(argv[1]);
+ off = atoi(argv[2]);
+ if (fd < 0 || fd > 1024) {
+ printf("ftruncate: error=%d (file descriptor out of range)\n", EBADF);
+ return NT_STATUS_OK;
+ }
+ if (vfs->files[fd] == NULL) {
+ printf("ftruncate: error=%d (invalid file descriptor)\n", EBADF);
+ return NT_STATUS_OK;
+ }
+
+ if (SMB_VFS_FTRUNCATE(vfs->files[fd], fd, off) == -1) {
+ printf("ftruncate: error=%d (%s)\n", errno, strerror(errno));
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ printf("ftruncate: ok\n");
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS cmd_lock(struct vfs_state *vfs, TALLOC_CTX *mem_ctx, int argc, const char **argv)
+{
+ BOOL ret;
+ int fd;
+ int op;
+ long offset;
+ long count;
+ int type;
+ const char *typestr;
+
+ if (argc != 6) {
+ printf("Usage: lock <fd> <op> <offset> <count> <type>\n");
+ printf(" ops: G = F_GETLK\n");
+ printf(" S = F_SETLK\n");
+ printf(" W = F_SETLKW\n");
+ printf(" type: R = F_RDLCK\n");
+ printf(" W = F_WRLCK\n");
+ printf(" U = F_UNLCK\n");
+ return NT_STATUS_OK;
+ }
+
+ if (sscanf(argv[1], "%d", &fd) == 0) {
+ printf("lock: error=-1 (error parsing fd)\n");
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ op = 0;
+ switch (*argv[2]) {
+ case 'G':
+ op = F_GETLK;
+ break;
+ case 'S':
+ op = F_SETLK;
+ break;
+ case 'W':
+ op = F_SETLKW;
+ break;
+ default:
+ printf("lock: error=-1 (invalid op flag!)\n");
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ if (sscanf(argv[3], "%ld", &offset) == 0) {
+ printf("lock: error=-1 (error parsing fd)\n");
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ if (sscanf(argv[4], "%ld", &count) == 0) {
+ printf("lock: error=-1 (error parsing fd)\n");
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ type = 0;
+ typestr = argv[5];
+ while(*typestr) {
+ switch (*typestr) {
+ case 'R':
+ type |= F_RDLCK;
+ break;
+ case 'W':
+ type |= F_WRLCK;
+ break;
+ case 'U':
+ type |= F_UNLCK;
+ break;
+ default:
+ printf("lock: error=-1 (invalid type flag!)\n");
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+ typestr++;
+ }
+
+ printf("lock: debug lock(fd=%d, op=%d, offset=%ld, count=%ld, type=%d))\n", fd, op, offset, count, type);
+
+ if ((ret = SMB_VFS_LOCK(vfs->files[fd], fd, op, offset, count, type)) == False) {
+ printf("lock: error=%d (%s)\n", errno, strerror(errno));
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ printf("lock: ok\n");
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS cmd_symlink(struct vfs_state *vfs, TALLOC_CTX *mem_ctx, int argc, const char **argv)
+{
+ if (argc != 3) {
+ printf("Usage: symlink <path> <link>\n");
+ return NT_STATUS_OK;
+ }
+
+ if (SMB_VFS_SYMLINK(vfs->conn, argv[1], argv[2]) == -1) {
+ printf("symlink: error=%d (%s)\n", errno, strerror(errno));
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ printf("symlink: ok\n");
+ return NT_STATUS_OK;
+}
+
+
+static NTSTATUS cmd_readlink(struct vfs_state *vfs, TALLOC_CTX *mem_ctx, int argc, const char **argv)
+{
+ char buffer[PATH_MAX];
+ int size;
+
+ if (argc != 2) {
+ printf("Usage: readlink <path>\n");
+ return NT_STATUS_OK;
+ }
+
+ if ((size = SMB_VFS_READLINK(vfs->conn, argv[1], buffer, PATH_MAX)) == -1) {
+ printf("readlink: error=%d (%s)\n", errno, strerror(errno));
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ buffer[size] = '\0';
+ printf("readlink: %s\n", buffer);
+ return NT_STATUS_OK;
+}
+
+
+static NTSTATUS cmd_link(struct vfs_state *vfs, TALLOC_CTX *mem_ctx, int argc, const char **argv)
+{
+ if (argc != 3) {
+ printf("Usage: link <path> <link>\n");
+ return NT_STATUS_OK;
+ }
+
+ if (SMB_VFS_LINK(vfs->conn, argv[1], argv[2]) == -1) {
+ printf("link: error=%d (%s)\n", errno, strerror(errno));
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ printf("link: ok\n");
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS cmd_mknod(struct vfs_state *vfs, TALLOC_CTX *mem_ctx, int argc, const char **argv)
+{
+ mode_t mode;
+ unsigned int dev_val;
+ SMB_DEV_T dev;
+
+ if (argc != 4) {
+ printf("Usage: mknod <path> <mode> <dev>\n");
+ printf(" mode is octal\n");
+ printf(" dev is hex\n");
+ return NT_STATUS_OK;
+ }
+
+ if (sscanf(argv[2], "%o", &mode) == 0) {
+ printf("open: error=-1 (invalid mode!)\n");
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ if (sscanf(argv[3], "%x", &dev_val) == 0) {
+ printf("open: error=-1 (invalid dev!)\n");
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+ dev = (SMB_DEV_T)dev_val;
+
+ if (SMB_VFS_MKNOD(vfs->conn, argv[1], mode, dev) == -1) {
+ printf("mknod: error=%d (%s)\n", errno, strerror(errno));
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ printf("mknod: ok\n");
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS cmd_realpath(struct vfs_state *vfs, TALLOC_CTX *mem_ctx, int argc, const char **argv)
+{
+ char respath[PATH_MAX];
+
+ if (argc != 2) {
+ printf("Usage: realpath <path>\n");
+ return NT_STATUS_OK;
+ }
+
+ if (SMB_VFS_REALPATH(vfs->conn, argv[1], respath) == NULL) {
+ printf("realpath: error=%d (%s)\n", errno, strerror(errno));
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ printf("realpath: ok\n");
+ return NT_STATUS_OK;
+}
+
+struct cmd_set vfs_commands[] = {
+
+ { "VFS Commands" },
+
+ { "load", cmd_load_module, "Load a module", "load <module.so>" },
+ { "populate", cmd_populate, "Populate a data buffer", "populate <char> <size>" },
+ { "showdata", cmd_show_data, "Show data currently in data buffer", "show_data [<offset> <len>]"},
+ { "connect", cmd_connect, "VFS connect()", "connect" },
+ { "disconnect", cmd_disconnect, "VFS disconnect()", "disconnect" },
+ { "disk_free", cmd_disk_free, "VFS disk_free()", "disk_free <path>" },
+ { "opendir", cmd_opendir, "VFS opendir()", "opendir <fname>" },
+ { "readdir", cmd_readdir, "VFS readdir()", "readdir" },
+ { "mkdir", cmd_mkdir, "VFS mkdir()", "mkdir <path>" },
+ { "rmdir", cmd_pathfunc, "VFS rmdir()", "rmdir <path>" },
+ { "closedir", cmd_closedir, "VFS closedir()", "closedir" },
+ { "open", cmd_open, "VFS open()", "open <fname>" },
+ { "close", cmd_close, "VFS close()", "close <fd>" },
+ { "read", cmd_read, "VFS read()", "read <fd> <size>" },
+ { "write", cmd_write, "VFS write()", "write <fd> <size>" },
+ { "lseek", cmd_lseek, "VFS lseek()", "lseek <fd> <offset> <whence>" },
+ { "rename", cmd_rename, "VFS rename()", "rename <old> <new>" },
+ { "fsync", cmd_fsync, "VFS fsync()", "fsync <fd>" },
+ { "stat", cmd_stat, "VFS stat()", "stat <fname>" },
+ { "fstat", cmd_fstat, "VFS fstat()", "fstat <fd>" },
+ { "lstat", cmd_lstat, "VFS lstat()", "lstat <fname>" },
+ { "unlink", cmd_pathfunc, "VFS unlink()", "unlink <fname>" },
+ { "chmod", cmd_chmod, "VFS chmod()", "chmod <path> <mode>" },
+ { "fchmod", cmd_fchmod, "VFS fchmod()", "fchmod <fd> <mode>" },
+ { "chown", cmd_chown, "VFS chown()", "chown <path> <uid> <gid>" },
+ { "fchown", cmd_fchown, "VFS fchown()", "fchown <fd> <uid> <gid>" },
+ { "chdir", cmd_pathfunc, "VFS chdir()", "chdir <path>" },
+ { "getwd", cmd_getwd, "VFS getwd()", "getwd" },
+ { "utime", cmd_utime, "VFS utime()", "utime <path> <access> <modify>" },
+ { "ftruncate", cmd_ftruncate, "VFS ftruncate()", "ftruncate <fd> <length>" },
+ { "lock", cmd_lock, "VFS lock()", "lock <f> <op> <offset> <count> <type>" },
+ { "symlink", cmd_symlink, "VFS symlink()", "symlink <old> <new>" },
+ { "readlink", cmd_readlink, "VFS readlink()", "readlink <path>" },
+ { "link", cmd_link, "VFS link()", "link <oldpath> <newpath>" },
+ { "mknod", cmd_mknod, "VFS mknod()", "mknod <path> <mode> <dev>" },
+ { "realpath", cmd_realpath, "VFS realpath()", "realpath <path>" },
+ { NULL }
+};
diff --git a/source/torture/denytest.c b/source/torture/denytest.c
new file mode 100644
index 00000000000..89b0fdf93f6
--- /dev/null
+++ b/source/torture/denytest.c
@@ -0,0 +1,1577 @@
+/*
+ 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 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.
+*/
+
+#define NO_SYSLOG
+
+#include "includes.h"
+
+extern BOOL torture_showall;
+
+enum deny_result {A_0=0, A_X=1, A_R=2, A_W=3, A_RW=5};
+
+
+static const char *denystr(int denymode)
+{
+ 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)
+{
+ 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)
+{
+ 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 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 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(unsigned i, unsigned total)
+{
+ if (i % 10 != 0) return;
+ printf("%5d/%5d\r", i, total);
+ fflush(stdout);
+}
+
+/*
+ this produces a matrix of deny mode behaviour for 1 connection
+ */
+BOOL torture_denytest1(int dummy)
+{
+ struct cli_state *cli1;
+ int fnum1, fnum2;
+ int i;
+ BOOL correct = True;
+ const char *fnames[2] = {"\\denytest1.dat", "\\denytest1.exe"};
+
+ if (!torture_open_connection(&cli1)) {
+ return False;
+ }
+
+ printf("starting denytest1\n");
+
+ for (i=0;i<2;i++) {
+ cli_unlink(cli1, fnames[i]);
+ fnum1 = cli_open(cli1, fnames[i], O_RDWR|O_CREAT, DENY_NONE);
+ cli_write(cli1, fnum1, 0, fnames[i], 0, strlen(fnames[i]));
+ cli_close(cli1, fnum1);
+ }
+
+ printf("testing %ld entries\n", (unsigned long)ARRAY_SIZE(denytable1));
+
+ for (i=0; i<ARRAY_SIZE(denytable1); i++) {
+ enum deny_result res;
+ const char *fname = fnames[denytable1[i].isexe];
+
+ progress_bar(i, ARRAY_SIZE(denytable1));
+
+ fnum1 = cli_open(cli1, fname,
+ denytable1[i].mode1,
+ denytable1[i].deny1);
+ fnum2 = cli_open(cli1, fname,
+ denytable1[i].mode2,
+ denytable1[i].deny2);
+
+ if (fnum1 == -1) {
+ res = A_X;
+ } else if (fnum2 == -1) {
+ res = A_0;
+ } else {
+ char x = 1;
+ res = A_0;
+ if (cli_read(cli1, fnum2, (void *)&x, 0, 1) == 1) {
+ res += A_R;
+ }
+ if (cli_write(cli1, fnum2, 0, (void *)&x, 0, 1) == 1) {
+ res += A_W;
+ }
+ }
+
+ if (res != denytable1[i].result) {
+ correct = False;
+ }
+
+ if (torture_showall || res != denytable1[i].result) {
+ printf("%s %8s %10s %8s %10s %s (correct=%s)\n",
+ fname,
+ denystr(denytable1[i].deny1),
+ openstr(denytable1[i].mode1),
+ denystr(denytable1[i].deny2),
+ openstr(denytable1[i].mode2),
+ resultstr(res),
+ resultstr(denytable1[i].result));
+ }
+
+ cli_close(cli1, fnum1);
+ cli_close(cli1, fnum2);
+ }
+
+ for (i=0;i<2;i++) {
+ cli_unlink(cli1, fnames[i]);
+ }
+
+ if (!torture_close_connection(cli1)) {
+ correct = False;
+ }
+
+ printf("finshed denytest1\n");
+ return correct;
+}
+
+
+/*
+ this produces a matrix of deny mode behaviour with 2 connections
+ */
+BOOL torture_denytest2(int dummy)
+{
+ static struct cli_state *cli1, *cli2;
+ int fnum1, fnum2;
+ int i;
+ BOOL correct = True;
+ const char *fnames[2] = {"\\denytest2.dat", "\\denytest2.exe"};
+
+ if (!torture_open_connection(&cli1) || !torture_open_connection(&cli2)) {
+ return False;
+ }
+
+ printf("starting denytest2\n");
+
+ for (i=0;i<2;i++) {
+ cli_unlink(cli1, fnames[i]);
+ fnum1 = cli_open(cli1, fnames[i], O_RDWR|O_CREAT, DENY_NONE);
+ cli_write(cli1, fnum1, 0, fnames[i], 0, strlen(fnames[i]));
+ cli_close(cli1, fnum1);
+ }
+
+ for (i=0; i<ARRAY_SIZE(denytable2); i++) {
+ enum deny_result res;
+ const char *fname = fnames[denytable2[i].isexe];
+
+ progress_bar(i, ARRAY_SIZE(denytable1));
+
+ fnum1 = cli_open(cli1, fname,
+ denytable2[i].mode1,
+ denytable2[i].deny1);
+ fnum2 = cli_open(cli2, fname,
+ denytable2[i].mode2,
+ denytable2[i].deny2);
+
+ if (fnum1 == -1) {
+ res = A_X;
+ } else if (fnum2 == -1) {
+ res = A_0;
+ } else {
+ char x = 1;
+ res = A_0;
+ if (cli_read(cli2, fnum2, (void *)&x, 0, 1) == 1) {
+ res += A_R;
+ }
+ if (cli_write(cli2, fnum2, 0, (void *)&x, 0, 1) == 1) {
+ res += A_W;
+ }
+ }
+
+ if (res != denytable2[i].result) {
+ correct = False;
+ }
+
+ if (torture_showall || res != denytable2[i].result) {
+ printf("%s %8s %10s %8s %10s %s (correct=%s)\n",
+ fname,
+ denystr(denytable2[i].deny1),
+ openstr(denytable2[i].mode1),
+ denystr(denytable2[i].deny2),
+ openstr(denytable2[i].mode2),
+ resultstr(res),
+ resultstr(denytable2[i].result));
+ }
+
+ cli_close(cli1, fnum1);
+ cli_close(cli2, fnum2);
+ }
+
+ for (i=0;i<2;i++) {
+ cli_unlink(cli1, fnames[i]);
+ }
+
+ if (!torture_close_connection(cli1)) {
+ correct = False;
+ }
+ if (!torture_close_connection(cli2)) {
+ correct = False;
+ }
+
+ printf("finshed denytest2\n");
+ return correct;
+}
+
diff --git a/source/torture/locktest.c b/source/torture/locktest.c
new file mode 100644
index 00000000000..86379bf3b6d
--- /dev/null
+++ b/source/torture/locktest.c
@@ -0,0 +1,678 @@
+/*
+ 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 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.
+*/
+
+#define NO_SYSLOG
+
+#include "includes.h"
+
+static fstring password[2];
+static fstring username[2];
+static int got_user;
+static int got_pass;
+static BOOL use_kerberos;
+static int numops = 1000;
+static BOOL showall;
+static BOOL analyze;
+static BOOL hide_unlock_fails;
+static BOOL use_oplocks;
+static unsigned lock_range = 100;
+static unsigned lock_base = 0;
+static unsigned min_length = 0;
+static BOOL exact_error_codes;
+static BOOL 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
+
+#define NASTY_POSIX_LOCK_HACK 0
+
+enum lock_op {OP_LOCK, OP_UNLOCK, OP_REOPEN};
+
+struct record {
+ enum lock_op lock_op;
+ enum brl_type lock_type;
+ char conn, f;
+ SMB_BIG_UINT start, len;
+ char needed;
+};
+
+#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;
+
+static void print_brl(SMB_DEV_T dev, SMB_INO_T ino, int pid,
+ enum brl_type lock_type,
+ br_off start, br_off size)
+{
+#if NASTY_POSIX_LOCK_HACK
+ {
+ pstring cmd;
+ static SMB_INO_T lastino;
+
+ if (lastino != ino) {
+ slprintf(cmd, sizeof(cmd),
+ "egrep POSIX.*%u /proc/locks", (int)ino);
+ system(cmd);
+ }
+ lastino = ino;
+ }
+#endif
+
+ printf("%6d %05x:%05x %s %.0f:%.0f(%.0f)\n",
+ (int)pid, (int)dev, (int)ino,
+ lock_type==READ_LOCK?"R":"W",
+ (double)start, (double)start+size-1,(double)size);
+
+}
+
+
+static void show_locks(void)
+{
+ brl_forall(print_brl);
+ /* system("cat /proc/locks"); */
+}
+
+
+/*****************************************************
+return a connection to a server
+*******************************************************/
+static struct cli_state *connect_one(char *share, int snum)
+{
+ struct cli_state *c;
+ struct nmb_name called, calling;
+ char *server_n;
+ fstring server;
+ struct in_addr ip;
+ fstring myname;
+ static int count;
+
+ fstrcpy(server,share+2);
+ share = strchr_m(server,'\\');
+ if (!share) return NULL;
+ *share = 0;
+ share++;
+
+ server_n = server;
+
+ zero_ip(&ip);
+
+ slprintf(myname,sizeof(myname), "lock-%lu-%u", (unsigned long)getpid(), count++);
+
+ make_nmb_name(&calling, myname, 0x0);
+ make_nmb_name(&called , server, 0x20);
+
+ again:
+ zero_ip(&ip);
+
+ /* have to open a new connection */
+ if (!(c=cli_initialise(NULL)) || !cli_connect(c, server_n, &ip)) {
+ DEBUG(0,("Connection to %s failed\n", server_n));
+ return NULL;
+ }
+
+ c->use_kerberos = use_kerberos;
+
+ if (!cli_session_request(c, &calling, &called)) {
+ DEBUG(0,("session request to %s failed\n", called.name));
+ cli_shutdown(c);
+ if (strcmp(called.name, "*SMBSERVER")) {
+ make_nmb_name(&called , "*SMBSERVER", 0x20);
+ goto again;
+ }
+ return NULL;
+ }
+
+ DEBUG(4,(" session request ok\n"));
+
+ if (!cli_negprot(c)) {
+ DEBUG(0,("protocol negotiation failed\n"));
+ cli_shutdown(c);
+ return NULL;
+ }
+
+ if (!got_pass) {
+ char *pass = getpass("Password: ");
+ if (pass) {
+ fstrcpy(password[0], pass);
+ fstrcpy(password[1], pass);
+ }
+ }
+
+ if (got_pass == 1) {
+ fstrcpy(password[1], password[0]);
+ fstrcpy(username[1], username[0]);
+ }
+
+ if (!cli_session_setup(c, username[snum],
+ password[snum], strlen(password[snum]),
+ password[snum], strlen(password[snum]),
+ lp_workgroup())) {
+ DEBUG(0,("session setup failed: %s\n", cli_errstr(c)));
+ return NULL;
+ }
+
+ /*
+ * These next two lines are needed to emulate
+ * old client behaviour for people who have
+ * scripts based on client output.
+ * QUESTION ? Do we want to have a 'client compatibility
+ * mode to turn these on/off ? JRA.
+ */
+
+ if (*c->server_domain || *c->server_os || *c->server_type)
+ DEBUG(1,("Domain=[%s] OS=[%s] Server=[%s]\n",
+ c->server_domain,c->server_os,c->server_type));
+
+ DEBUG(4,(" session setup ok\n"));
+
+ if (!cli_send_tconX(c, share, "?????",
+ password[snum], strlen(password[snum])+1)) {
+ DEBUG(0,("tree connect failed: %s\n", cli_errstr(c)));
+ cli_shutdown(c);
+ return NULL;
+ }
+
+ DEBUG(4,(" tconx ok\n"));
+
+ c->use_oplocks = use_oplocks;
+
+ return c;
+}
+
+
+static void reconnect(struct cli_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) {
+ cli_close(cli[server][conn], fnum[server][conn][f]);
+ fnum[server][conn][f] = -1;
+ }
+ }
+ cli_ulogoff(cli[server][conn]);
+ cli_shutdown(cli[server][conn]);
+ }
+ cli[server][conn] = connect_one(share[server], server);
+ if (!cli[server][conn]) {
+ DEBUG(0,("Failed to connect to %s\n", share[server]));
+ exit(1);
+ }
+ }
+}
+
+
+
+static BOOL test_one(struct cli_state *cli[NSERVERS][NCONNECTIONS],
+ int fnum[NSERVERS][NCONNECTIONS][NFILES],
+ struct record *rec)
+{
+ unsigned conn = rec->conn;
+ unsigned f = rec->f;
+ SMB_BIG_UINT start = rec->start;
+ SMB_BIG_UINT 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++) {
+ ret[server] = cli_lock64(cli[server][conn],
+ fnum[server][conn][f],
+ start, len, LOCK_TIMEOUT, op);
+ status[server] = cli_nt_error(cli[server][conn]);
+ 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 (showall || !NT_STATUS_EQUAL(status[0],status[1])) show_locks();
+ if (!NT_STATUS_EQUAL(status[0],status[1])) return False;
+ break;
+
+ case OP_UNLOCK:
+ /* unset a lock */
+ for (server=0;server<NSERVERS;server++) {
+ ret[server] = cli_unlock64(cli[server][conn],
+ fnum[server][conn][f],
+ start, len);
+ status[server] = cli_nt_error(cli[server][conn]);
+ }
+ 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 (showall || !NT_STATUS_EQUAL(status[0],status[1])) show_locks();
+ 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++) {
+ cli_close(cli[server][conn], fnum[server][conn][f]);
+ fnum[server][conn][f] = -1;
+ }
+ for (server=0;server<NSERVERS;server++) {
+ fnum[server][conn][f] = cli_open(cli[server][conn], 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);
+ show_locks();
+ }
+ break;
+ }
+
+ return True;
+}
+
+static void close_files(struct cli_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) {
+ cli_close(cli[server][conn], fnum[server][conn][f]);
+ fnum[server][conn][f] = -1;
+ }
+ }
+ for (server=0;server<NSERVERS;server++) {
+ cli_unlink(cli[server][0], FILENAME);
+ }
+}
+
+static void open_files(struct cli_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] = cli_open(cli[server][conn], 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 cli_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 void test_locks(char *share[NSERVERS])
+{
+ struct cli_state *cli[NSERVERS][NCONNECTIONS];
+ int fnum[NSERVERS][NCONNECTIONS][NFILES];
+ int n, i, n1, skip, r1, r2;
+
+ ZERO_STRUCT(fnum);
+ ZERO_STRUCT(cli);
+
+ recorded = (struct record *)malloc(sizeof(*recorded) * 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 + ((unsigned)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;
+ 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(cli, fnum, share);
+ open_files(cli, fnum);
+ n = retest(cli, fnum, numops);
+
+ if (n == numops || !analyze) return;
+ n++;
+
+ skip = n/2;
+
+ while (1) {
+ n1 = n;
+
+ close_files(cli, fnum);
+ reconnect(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(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);
+ }
+}
+
+
+
+static void usage(void)
+{
+ printf(
+"Usage:\n\
+ locktest //server1/share1 //server2/share2 [options..]\n\
+ options:\n\
+ -U user%%pass (may be specified twice)\n\
+ -k use kerberos\n\
+ -s seed\n\
+ -o numops\n\
+ -u hide unlock fails\n\
+ -a (show all ops)\n\
+ -A analyse for minimal ops\n\
+ -O use oplocks\n\
+ -E enable exact error code checking\n\
+ -Z enable the zero/zero lock\n\
+ -R range set lock range\n\
+ -B base set lock base\n\
+ -M min set min lock length\n\
+");
+}
+
+/****************************************************************************
+ main program
+****************************************************************************/
+ int main(int argc,char *argv[])
+{
+ char *share[NSERVERS];
+ extern char *optarg;
+ extern int optind;
+ int opt;
+ char *p;
+ int seed, server;
+
+ setlinebuf(stdout);
+
+ dbf = x_stderr;
+
+ if (argc < 3 || argv[1][0] == '-') {
+ usage();
+ exit(1);
+ }
+
+ setup_logging(argv[0],True);
+
+ for (server=0;server<NSERVERS;server++) {
+ share[server] = argv[1+server];
+ all_string_sub(share[server],"/","\\",0);
+ }
+
+ argc -= NSERVERS;
+ argv += NSERVERS;
+
+ lp_load(dyn_CONFIGFILE,True,False,False);
+ load_interfaces();
+
+ if (getenv("USER")) {
+ fstrcpy(username[0],getenv("USER"));
+ fstrcpy(username[1],getenv("USER"));
+ }
+
+ seed = time(NULL);
+
+ while ((opt = getopt(argc, argv, "U:s:ho:aAW:OkR:B:M:EZ")) != EOF) {
+ switch (opt) {
+ case 'k':
+#ifdef HAVE_KRB5
+ use_kerberos = True;
+#else
+ d_printf("No kerberos support compiled in\n");
+ exit(1);
+#endif
+ break;
+ case 'U':
+ got_user = 1;
+ if (got_pass == 2) {
+ d_printf("Max of 2 usernames\n");
+ exit(1);
+ }
+ fstrcpy(username[got_pass],optarg);
+ p = strchr_m(username[got_pass],'%');
+ if (p) {
+ *p = 0;
+ fstrcpy(password[got_pass], p+1);
+ got_pass++;
+ }
+ break;
+ case 'R':
+ lock_range = strtol(optarg, NULL, 0);
+ break;
+ case 'B':
+ lock_base = strtol(optarg, NULL, 0);
+ break;
+ case 'M':
+ min_length = strtol(optarg, NULL, 0);
+ 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 'Z':
+ zero_zero = True;
+ break;
+ case 'E':
+ exact_error_codes = True;
+ break;
+ case 'h':
+ usage();
+ exit(1);
+ default:
+ printf("Unknown option %c (%d)\n", (char)opt, opt);
+ exit(1);
+ }
+ }
+
+ if(use_kerberos && !got_user) got_pass = True;
+
+ argc -= optind;
+ argv += optind;
+
+ DEBUG(0,("seed=%u\n", seed));
+ srandom(seed);
+
+ test_locks(share);
+
+ return(0);
+}
diff --git a/source/torture/locktest2.c b/source/torture/locktest2.c
new file mode 100644
index 00000000000..5fbaf9ec584
--- /dev/null
+++ b/source/torture/locktest2.c
@@ -0,0 +1,560 @@
+/*
+ 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 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.
+*/
+
+#define NO_SYSLOG
+
+#include "includes.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;
+ unsigned start, len;
+ char needed;
+};
+
+static struct record *recorded;
+
+static int try_open(struct cli_state *c, char *nfs, int fstype, const char *fname, int flags)
+{
+ pstring path;
+
+ switch (fstype) {
+ case FSTYPE_SMB:
+ return cli_open(c, fname, flags, DENY_NONE);
+
+ case FSTYPE_NFS:
+ slprintf(path, sizeof(path), "%s%s", nfs, fname);
+ pstring_sub(path,"\\", "/");
+ return open(path, flags, 0666);
+ }
+
+ return -1;
+}
+
+static BOOL try_close(struct cli_state *c, int fstype, int fd)
+{
+ switch (fstype) {
+ case FSTYPE_SMB:
+ return cli_close(c, fd);
+
+ case FSTYPE_NFS:
+ return close(fd) == 0;
+ }
+
+ return False;
+}
+
+static BOOL try_lock(struct cli_state *c, int fstype,
+ int fd, unsigned start, unsigned len,
+ enum brl_type op)
+{
+ struct flock lock;
+
+ switch (fstype) {
+ case FSTYPE_SMB:
+ return cli_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 cli_state *c, int fstype,
+ int fd, unsigned start, unsigned len)
+{
+ struct flock lock;
+
+ switch (fstype) {
+ case FSTYPE_SMB:
+ return cli_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;
+}
+
+static void print_brl(SMB_DEV_T dev, SMB_INO_T ino, int pid,
+ enum brl_type lock_type,
+ br_off start, br_off size)
+{
+ printf("%6d %05x:%05x %s %.0f:%.0f(%.0f)\n",
+ (int)pid, (int)dev, (int)ino,
+ lock_type==READ_LOCK?"R":"W",
+ (double)start, (double)start+size-1,(double)size);
+
+}
+
+/*****************************************************
+return a connection to a server
+*******************************************************/
+static struct cli_state *connect_one(char *share)
+{
+ struct cli_state *c;
+ char *server_n;
+ fstring server;
+ fstring myname;
+ static int count;
+ NTSTATUS nt_status;
+
+ fstrcpy(server,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) {
+ fstrcpy(password, pass);
+ }
+ }
+
+ slprintf(myname,sizeof(myname), "lock-%lu-%u", (unsigned long)getpid(), count++);
+
+ nt_status = cli_full_connection(&c, myname, server_n, NULL, 0, share, "?????",
+ username, lp_workgroup(), password, 0,
+ Undefined, NULL);
+
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ DEBUG(0, ("cli_full_connection failed with error %s\n", nt_errstr(nt_status)));
+ return NULL;
+ }
+
+ c->use_oplocks = use_oplocks;
+
+ return c;
+}
+
+
+static void reconnect(struct cli_state *cli[NSERVERS][NCONNECTIONS],
+ char *nfs[NSERVERS],
+ int fnum[NSERVERS][NUMFSTYPES][NCONNECTIONS][NFILES],
+ 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++) {
+ cli_close(cli[server][conn], fnum[server][fstype][conn][f]);
+ }
+ cli_ulogoff(cli[server][conn]);
+ cli_shutdown(cli[server][conn]);
+ }
+ cli[server][conn] = connect_one(share[server]);
+ if (!cli[server][conn]) {
+ DEBUG(0,("Failed to connect to %s\n", share[server]));
+ exit(1);
+ }
+ }
+}
+
+
+
+static BOOL test_one(struct cli_state *cli[NSERVERS][NCONNECTIONS],
+ char *nfs[NSERVERS],
+ int fnum[NSERVERS][NUMFSTYPES][NCONNECTIONS][NFILES],
+ struct record *rec)
+{
+ unsigned conn = rec->conn;
+ unsigned f = rec->f;
+ unsigned fstype = rec->fstype;
+ unsigned start = rec->start;
+ unsigned len = rec->len;
+ unsigned r1 = rec->r1;
+ unsigned 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 (showall) brl_forall(print_brl);
+ 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 (showall) brl_forall(print_brl);
+ 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);
+ brl_forall(print_brl);
+ }
+ }
+ return True;
+}
+
+static void close_files(struct cli_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++) {
+ cli_unlink(cli[server][0], FILENAME);
+ }
+}
+
+static void open_files(struct cli_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 cli_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(char *share1, char *share2, char *nfspath1, char *nfspath2)
+{
+ struct cli_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 = (struct record *)malloc(sizeof(*recorded) * 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 + ((unsigned)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(cli, nfs, fnum, 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(cli, nfs, fnum, 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(cli, nfs, fnum, 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;
+ int opt;
+ char *p;
+ int seed;
+
+ 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],True);
+
+ argc -= 4;
+ argv += 4;
+
+ lp_load(dyn_CONFIGFILE,True,False,False);
+ load_interfaces();
+
+ if (getenv("USER")) {
+ fstrcpy(username,getenv("USER"));
+ }
+
+ seed = time(NULL);
+
+ while ((opt = getopt(argc, argv, "U:s:ho:aAW:O")) != EOF) {
+ switch (opt) {
+ case 'U':
+ fstrcpy(username,optarg);
+ p = strchr_m(username,'%');
+ if (p) {
+ *p = 0;
+ fstrcpy(password, 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);
+
+ locking_init(1);
+ test_locks(share1, share2, nfspath1, nfspath2);
+
+ return(0);
+}
diff --git a/source/torture/mangle_test.c b/source/torture/mangle_test.c
new file mode 100644
index 00000000000..f31621b23b7
--- /dev/null
+++ b/source/torture/mangle_test.c
@@ -0,0 +1,213 @@
+/*
+ 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 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"
+
+static TDB_CONTEXT *tdb;
+
+#define NAME_LENGTH 20
+
+static unsigned total, collisions, failures;
+
+static BOOL test_one(struct cli_state *cli, const char *name)
+{
+ int fnum;
+ fstring shortname;
+ fstring name2;
+ NTSTATUS status;
+ TDB_DATA data;
+
+ total++;
+
+ fnum = cli_open(cli, name, O_RDWR|O_CREAT|O_EXCL, DENY_NONE);
+ if (fnum == -1) {
+ printf("open of %s failed (%s)\n", name, cli_errstr(cli));
+ return False;
+ }
+
+ if (!cli_close(cli, fnum)) {
+ printf("close of %s failed (%s)\n", name, cli_errstr(cli));
+ return False;
+ }
+
+ /* get the short name */
+ status = cli_qpathinfo_alt_name(cli, name, shortname);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("query altname of %s failed (%s)\n", name, cli_errstr(cli));
+ return False;
+ }
+
+ fstr_sprintf(name2, "\\mangle_test\\%s", shortname);
+ if (!cli_unlink(cli, name2)) {
+ printf("unlink of %s (%s) failed (%s)\n",
+ name2, name, cli_errstr(cli));
+ return False;
+ }
+
+ /* recreate by short name */
+ fnum = cli_open(cli, name2, O_RDWR|O_CREAT|O_EXCL, DENY_NONE);
+ if (fnum == -1) {
+ printf("open2 of %s failed (%s)\n", name2, cli_errstr(cli));
+ return False;
+ }
+ if (!cli_close(cli, fnum)) {
+ printf("close of %s failed (%s)\n", name, cli_errstr(cli));
+ return False;
+ }
+
+ /* and unlink by long name */
+ if (!cli_unlink(cli, name)) {
+ printf("unlink2 of %s (%s) failed (%s)\n",
+ name, name2, cli_errstr(cli));
+ failures++;
+ cli_unlink(cli, 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 (!strequal(name, data.dptr)) {
+ /* 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 = name;
+ namedata.dsize = strlen(name)+1;
+ tdb_store_bystring(tdb, shortname, namedata, TDB_REPLACE);
+ }
+
+ return True;
+}
+
+
+static void gen_name(char *name)
+{
+ const char *chars = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz._-$~... ";
+ unsigned max_idx = strlen(chars);
+ unsigned len;
+ int i;
+ char *p;
+
+ fstrcpy(name, "\\mangle_test\\");
+ p = name + strlen(name);
+
+ len = 1 + random() % NAME_LENGTH;
+
+ for (i=0;i<len;i++) {
+ p[i] = chars[random() % max_idx];
+ }
+
+ p[i] = 0;
+
+ if (strcmp(p, ".") == 0 || strcmp(p, "..") == 0) {
+ 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 (random() % 10 == 0) {
+ if (strlen(p) <= 5) {
+ fstrcpy(p, "ABCDE");
+ } else {
+ /* try not to kill off the null termination */
+ memcpy(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;
+ }
+ }
+}
+
+
+BOOL torture_mangle(int dummy)
+{
+ extern int torture_numops;
+ static struct cli_state *cli;
+ int i;
+ BOOL ret = True;
+
+ printf("starting mangle test\n");
+
+ if (!torture_open_connection(&cli)) {
+ return False;
+ }
+
+ /* 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;
+ }
+
+ cli_unlink(cli, "\\mangle_test\\*");
+ cli_rmdir(cli, "\\mangle_test");
+
+ if (!cli_mkdir(cli, "\\mangle_test")) {
+ printf("ERROR: Failed to make directory\n");
+ return False;
+ }
+
+ for (i=0;i<torture_numops;i++) {
+ fstring name;
+ ZERO_STRUCT(name);
+
+ gen_name(name);
+
+ if (!test_one(cli, name)) {
+ ret = False;
+ break;
+ }
+ if (total && total % 100 == 0) {
+ printf("collisions %u/%u - %.2f%% (%u failures)\r",
+ collisions, total, (100.0*collisions) / total, failures);
+ }
+ }
+
+ cli_unlink(cli, "\\mangle_test\\*");
+ if (!cli_rmdir(cli, "\\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);
+
+ torture_close_connection(cli);
+
+ printf("mangle test finished\n");
+ return (ret && (failures == 0));
+}
diff --git a/source/torture/masktest.c b/source/torture/masktest.c
new file mode 100644
index 00000000000..8c44f35f958
--- /dev/null
+++ b/source/torture/masktest.c
@@ -0,0 +1,538 @@
+/*
+ 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 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.
+*/
+
+#define NO_SYSLOG
+
+#include "includes.h"
+
+static fstring password;
+static fstring username;
+static int got_pass;
+static int max_protocol = PROTOCOL_NT1;
+static BOOL showall = False;
+static BOOL old_list = False;
+static const char *maskchars = "<>\"?*abc.";
+static const char *filechars = "abcdefghijklm.";
+static int verbose;
+static int die_on_error;
+static int NumLoops = 0;
+static int ignore_dot_errors = 0;
+
+/* a test fn for LANMAN mask support */
+int ms_fnmatch_lanman_core(const char *pattern, const char *string)
+{
+ const char *p = pattern, *n = string;
+ char c;
+
+ if (strcmp(p,"?")==0 && strcmp(n,".")==0) goto match;
+
+ while ((c = *p++)) {
+ switch (c) {
+ case '.':
+ /* if (! *n && ! *p) goto match; */
+ if (*n != '.') goto nomatch;
+ n++;
+ break;
+
+ case '?':
+ if ((*n == '.' && n[1] != '.') || ! *n) goto next;
+ n++;
+ break;
+
+ case '>':
+ if (n[0] == '.') {
+ if (! n[1] && ms_fnmatch_lanman_core(p, n+1) == 0) goto match;
+ if (ms_fnmatch_lanman_core(p, n) == 0) goto match;
+ goto nomatch;
+ }
+ if (! *n) goto next;
+ n++;
+ break;
+
+ case '*':
+ if (! *p) goto match;
+ for (; *n; n++) {
+ if (ms_fnmatch_lanman_core(p, n) == 0) goto match;
+ }
+ break;
+
+ case '<':
+ for (; *n; n++) {
+ if (ms_fnmatch_lanman_core(p, n) == 0) goto match;
+ if (*n == '.' && !strchr_m(n+1,'.')) {
+ n++;
+ break;
+ }
+ }
+ break;
+
+ case '"':
+ if (*n == 0 && ms_fnmatch_lanman_core(p, n) == 0) goto match;
+ if (*n != '.') goto nomatch;
+ n++;
+ break;
+
+ default:
+ if (c != *n) goto nomatch;
+ n++;
+ }
+ }
+
+ if (! *n) goto match;
+
+ nomatch:
+ if (verbose) printf("NOMATCH pattern=[%s] string=[%s]\n", pattern, string);
+ return -1;
+
+next:
+ if (ms_fnmatch_lanman_core(p, n) == 0) goto match;
+ goto nomatch;
+
+ match:
+ if (verbose) printf("MATCH pattern=[%s] string=[%s]\n", pattern, string);
+ return 0;
+}
+
+int ms_fnmatch_lanman(const char *pattern, const char *string)
+{
+ if (!strpbrk(pattern, "?*<>\"")) {
+ if (strcmp(string,"..") == 0)
+ string = ".";
+
+ return strcmp(pattern, string);
+ }
+
+ if (strcmp(string,"..") == 0 || strcmp(string,".") == 0) {
+ return ms_fnmatch_lanman_core(pattern, "..") &&
+ ms_fnmatch_lanman_core(pattern, ".");
+ }
+
+ return ms_fnmatch_lanman_core(pattern, string);
+}
+
+static BOOL reg_match_one(struct cli_state *cli, const char *pattern, const char *file)
+{
+ /* oh what a weird world this is */
+ if (old_list && strcmp(pattern, "*.*") == 0) return True;
+
+ if (strcmp(pattern,".") == 0) return False;
+
+ if (max_protocol <= PROTOCOL_LANMAN2) {
+ return ms_fnmatch_lanman(pattern, file)==0;
+ }
+
+ if (strcmp(file,"..") == 0) file = ".";
+
+ return ms_fnmatch(pattern, file, cli->protocol, False /* not case sensitive */)==0;
+}
+
+static char *reg_test(struct cli_state *cli, char *pattern, char *long_name, char *short_name)
+{
+ static fstring ret;
+ fstrcpy(ret, "---");
+
+ 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
+*******************************************************/
+struct cli_state *connect_one(char *share)
+{
+ struct cli_state *c;
+ struct nmb_name called, calling;
+ char *server_n;
+ char *server;
+ struct in_addr ip;
+
+ server = share+2;
+ share = strchr_m(server,'\\');
+ if (!share) return NULL;
+ *share = 0;
+ share++;
+
+ server_n = server;
+
+ zero_ip(&ip);
+
+ make_nmb_name(&calling, "masktest", 0x0);
+ make_nmb_name(&called , server, 0x20);
+
+ again:
+ zero_ip(&ip);
+
+ /* have to open a new connection */
+ if (!(c=cli_initialise(NULL)) || !cli_connect(c, server_n, &ip)) {
+ DEBUG(0,("Connection to %s failed\n", server_n));
+ return NULL;
+ }
+
+ c->protocol = max_protocol;
+
+ if (!cli_session_request(c, &calling, &called)) {
+ DEBUG(0,("session request to %s failed\n", called.name));
+ cli_shutdown(c);
+ if (strcmp(called.name, "*SMBSERVER")) {
+ make_nmb_name(&called , "*SMBSERVER", 0x20);
+ goto again;
+ }
+ return NULL;
+ }
+
+ DEBUG(4,(" session request ok\n"));
+
+ if (!cli_negprot(c)) {
+ DEBUG(0,("protocol negotiation failed\n"));
+ cli_shutdown(c);
+ return NULL;
+ }
+
+ if (!got_pass) {
+ char *pass = getpass("Password: ");
+ if (pass) {
+ fstrcpy(password, pass);
+ }
+ }
+
+ if (!cli_session_setup(c, username,
+ password, strlen(password),
+ password, strlen(password),
+ lp_workgroup())) {
+ DEBUG(0,("session setup failed: %s\n", cli_errstr(c)));
+ return NULL;
+ }
+
+ /*
+ * These next two lines are needed to emulate
+ * old client behaviour for people who have
+ * scripts based on client output.
+ * QUESTION ? Do we want to have a 'client compatibility
+ * mode to turn these on/off ? JRA.
+ */
+
+ if (*c->server_domain || *c->server_os || *c->server_type)
+ DEBUG(1,("Domain=[%s] OS=[%s] Server=[%s]\n",
+ c->server_domain,c->server_os,c->server_type));
+
+ DEBUG(4,(" session setup ok\n"));
+
+ if (!cli_send_tconX(c, share, "?????",
+ password, strlen(password)+1)) {
+ DEBUG(0,("tree connect failed: %s\n", cli_errstr(c)));
+ cli_shutdown(c);
+ return NULL;
+ }
+
+ DEBUG(4,(" tconx ok\n"));
+
+ return c;
+}
+
+static char *resultp;
+static file_info *f_info;
+
+static void listfn(file_info *f, const char *s, void *state)
+{
+ if (strcmp(f->name,".") == 0) {
+ resultp[0] = '+';
+ } else if (strcmp(f->name,"..") == 0) {
+ resultp[1] = '+';
+ } else {
+ resultp[2] = '+';
+ }
+ f_info = f;
+}
+
+static void get_real_name(struct cli_state *cli,
+ pstring long_name, fstring short_name)
+{
+ /* nasty hack to force level 260 listings - tridge */
+ cli->capabilities |= CAP_NT_SMBS;
+ if (max_protocol <= PROTOCOL_LANMAN1) {
+ cli_list_new(cli, "\\masktest\\*.*", aHIDDEN | aDIR, listfn, NULL);
+ } else {
+ cli_list_new(cli, "\\masktest\\*", aHIDDEN | aDIR, listfn, NULL);
+ }
+ if (f_info) {
+ fstrcpy(short_name, f_info->short_name);
+ strlower_m(short_name);
+ pstrcpy(long_name, f_info->name);
+ strlower_m(long_name);
+ }
+
+ if (*short_name == 0) {
+ fstrcpy(short_name, long_name);
+ }
+
+#if 0
+ if (!strchr_m(short_name,'.')) {
+ fstrcat(short_name,".");
+ }
+#endif
+}
+
+static void testpair(struct cli_state *cli, char *mask, char *file)
+{
+ int fnum;
+ fstring res1;
+ char *res2;
+ static int count;
+ fstring short_name;
+ pstring long_name;
+
+ count++;
+
+ fstrcpy(res1, "---");
+
+ fnum = cli_open(cli, file, O_CREAT|O_TRUNC|O_RDWR, 0);
+ if (fnum == -1) {
+ DEBUG(0,("Can't create %s\n", file));
+ return;
+ }
+ cli_close(cli, fnum);
+
+ resultp = res1;
+ fstrcpy(short_name, "");
+ f_info = NULL;
+ get_real_name(cli, long_name, short_name);
+ f_info = NULL;
+ fstrcpy(res1, "---");
+ cli_list(cli, mask, aHIDDEN | aDIR, listfn, NULL);
+
+ res2 = reg_test(cli, mask, long_name, short_name);
+
+ if (showall ||
+ ((strcmp(res1, res2) && !ignore_dot_errors) ||
+ (strcmp(res1+2, res2+2) && ignore_dot_errors))) {
+ DEBUG(0,("%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);
+ }
+
+ cli_unlink(cli, file);
+
+ if (count % 100 == 0) DEBUG(0,("%d\n", count));
+}
+
+static void test_mask(int argc, char *argv[],
+ struct cli_state *cli)
+{
+ pstring mask, file;
+ int l1, l2, i, l;
+ int mc_len = strlen(maskchars);
+ int fc_len = strlen(filechars);
+
+ cli_mkdir(cli, "\\masktest");
+
+ cli_unlink(cli, "\\masktest\\*");
+
+ if (argc >= 2) {
+ while (argc >= 2) {
+ pstrcpy(mask,"\\masktest\\");
+ pstrcpy(file,"\\masktest\\");
+ pstrcat(mask, argv[0]);
+ pstrcat(file, argv[1]);
+ testpair(cli, mask, file);
+ argv += 2;
+ argc -= 2;
+ }
+ goto finished;
+ }
+
+ while (1) {
+ l1 = 1 + random() % 20;
+ l2 = 1 + random() % 20;
+ pstrcpy(mask,"\\masktest\\");
+ pstrcpy(file,"\\masktest\\");
+ 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 (strcmp(file+l,".") == 0 ||
+ strcmp(file+l,"..") == 0 ||
+ strcmp(mask+l,"..") == 0) continue;
+
+ if (strspn(file+l, ".") == strlen(file+l)) continue;
+
+ testpair(cli, mask, file);
+ if (NumLoops && (--NumLoops == 0))
+ break;
+ }
+
+ finished:
+ cli_rmdir(cli, "\\masktest");
+}
+
+
+static void usage(void)
+{
+ printf(
+"Usage:\n\
+ masktest //server/share [options..]\n\
+ options:\n\
+ -d debuglevel\n\
+ -n numloops\n\
+ -W workgroup\n\
+ -U user%%pass\n\
+ -s seed\n\
+ -M max protocol\n\
+ -f filechars (default %s)\n\
+ -m maskchars (default %s)\n\
+ -v verbose mode\n\
+ -E die on error\n\
+ -a show all tests\n\
+ -i ignore . and .. errors\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\
+",
+ filechars, maskchars);
+}
+
+/****************************************************************************
+ main program
+****************************************************************************/
+ int main(int argc,char *argv[])
+{
+ char *share;
+ struct cli_state *cli;
+ extern char *optarg;
+ extern int optind;
+ extern BOOL AllowDebugChange;
+ int opt;
+ char *p;
+ int seed;
+
+ setlinebuf(stdout);
+
+ dbf = x_stderr;
+
+ DEBUGLEVEL = 0;
+ AllowDebugChange = False;
+
+ if (argc < 2 || argv[1][0] == '-') {
+ usage();
+ exit(1);
+ }
+
+ share = argv[1];
+
+ all_string_sub(share,"/","\\",0);
+
+ setup_logging(argv[0],True);
+
+ argc -= 1;
+ argv += 1;
+
+ lp_load(dyn_CONFIGFILE,True,False,False);
+ load_interfaces();
+
+ if (getenv("USER")) {
+ fstrcpy(username,getenv("USER"));
+ }
+
+ seed = time(NULL);
+
+ while ((opt = getopt(argc, argv, "n:d:U:s:hm:f:aoW:M:vEi")) != EOF) {
+ switch (opt) {
+ case 'n':
+ NumLoops = atoi(optarg);
+ break;
+ case 'd':
+ DEBUGLEVEL = atoi(optarg);
+ break;
+ case 'E':
+ die_on_error = 1;
+ break;
+ case 'i':
+ ignore_dot_errors = 1;
+ break;
+ case 'v':
+ verbose++;
+ break;
+ case 'M':
+ max_protocol = interpret_protocol(optarg, max_protocol);
+ break;
+ case 'U':
+ fstrcpy(username,optarg);
+ p = strchr_m(username,'%');
+ if (p) {
+ *p = 0;
+ fstrcpy(password, p+1);
+ got_pass = 1;
+ }
+ break;
+ case 's':
+ seed = atoi(optarg);
+ break;
+ case 'h':
+ usage();
+ exit(1);
+ case 'm':
+ maskchars = optarg;
+ break;
+ case 'f':
+ filechars = optarg;
+ break;
+ case 'a':
+ showall = 1;
+ break;
+ case 'o':
+ old_list = True;
+ break;
+ default:
+ printf("Unknown option %c (%d)\n", (char)opt, opt);
+ exit(1);
+ }
+ }
+
+ argc -= optind;
+ argv += optind;
+
+
+ cli = connect_one(share);
+ 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\n", seed));
+ srandom(seed);
+
+ test_mask(argc, argv, cli);
+
+ return(0);
+}
diff --git a/source/torture/msgtest.c b/source/torture/msgtest.c
new file mode 100644
index 00000000000..c40973a75c8
--- /dev/null
+++ b/source/torture/msgtest.c
@@ -0,0 +1,91 @@
+/*
+ Unix SMB/CIFS implementation.
+ 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 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.
+*/
+
+/*
+ test code for internal messaging
+ */
+
+#define NO_SYSLOG
+
+#include "includes.h"
+
+static int pong_count;
+
+/****************************************************************************
+a useful function for testing the message system
+****************************************************************************/
+void pong_message(int msg_type, pid_t src, void *buf, size_t len)
+{
+ pong_count++;
+}
+
+ int main(int argc, char *argv[])
+{
+ pid_t pid;
+ int i, n;
+ char buf[12];
+
+ setup_logging(argv[0],True);
+
+ lp_load(dyn_CONFIGFILE,False,False,False);
+
+ message_init();
+
+ if (argc != 3) {
+ fprintf(stderr, "%s: Usage - %s pid count\n", argv[0], argv[0]);
+ exit(1);
+ }
+
+ pid = atoi(argv[1]);
+ n = atoi(argv[2]);
+
+ message_register(MSG_PONG, pong_message);
+
+ for (i=0;i<n;i++) {
+ message_send_pid(pid, MSG_PING, NULL, 0, True);
+ }
+
+ while (pong_count < i) {
+ message_dispatch();
+ smb_msleep(1);
+ }
+
+ /* Now test that the duplicate filtering code works. */
+ pong_count = 0;
+
+ safe_strcpy(buf, "1234567890", sizeof(buf)-1);
+
+ for (i=0;i<n;i++) {
+ message_send_pid(getpid(), MSG_PING, NULL, 0, False);
+ message_send_pid(getpid(), MSG_PING, buf, 11, False);
+ }
+
+ for (i=0;i<n;i++) {
+ message_dispatch();
+ smb_msleep(1);
+ }
+
+ if (pong_count != 2) {
+ fprintf(stderr, "Duplicate filter failed (%d).\n", pong_count);
+ exit(1);
+ }
+
+ return (0);
+}
+
diff --git a/source/torture/nbio.c b/source/torture/nbio.c
new file mode 100644
index 00000000000..2e79584d23f
--- /dev/null
+++ b/source/torture/nbio.c
@@ -0,0 +1,318 @@
+#define NBDEBUG 0
+
+/*
+ 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 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.
+*/
+
+#define NO_SYSLOG
+
+#include "includes.h"
+
+#define MAX_FILES 1000
+
+static char buf[70000];
+extern int line_count;
+extern int nbio_id;
+static int nprocs;
+
+static struct {
+ int fd;
+ int handle;
+} ftable[MAX_FILES];
+
+static struct {
+ double bytes_in, bytes_out;
+ int line;
+ int done;
+} *children;
+
+double nbio_total(void)
+{
+ int i;
+ double total = 0;
+ for (i=0;i<nprocs;i++) {
+ total += children[i].bytes_out + children[i].bytes_in;
+ }
+ return total;
+}
+
+void nb_alarm(int ignore)
+{
+ int i;
+ int lines=0, num_clients=0;
+ if (nbio_id != -1) return;
+
+ for (i=0;i<nprocs;i++) {
+ lines += children[i].line;
+ if (!children[i].done) num_clients++;
+ }
+
+ printf("%4d %8d %.2f MB/sec\r", num_clients, lines/nprocs, 1.0e-6 * nbio_total() / end_timer());
+
+ signal(SIGALRM, nb_alarm);
+ alarm(1);
+}
+
+void nbio_shmem(int n)
+{
+ nprocs = n;
+ children = shm_setup(sizeof(*children) * nprocs);
+ if (!children) {
+ printf("Failed to setup shared memory!\n");
+ exit(1);
+ }
+}
+
+#if 0
+static int ne_find_handle(int handle)
+{
+ int i;
+ children[nbio_id].line = line_count;
+ for (i=0;i<MAX_FILES;i++) {
+ if (ftable[i].handle == handle) return i;
+ }
+ return -1;
+}
+#endif
+
+static int find_handle(int handle)
+{
+ int i;
+ children[nbio_id].line = line_count;
+ for (i=0;i<MAX_FILES;i++) {
+ if (ftable[i].handle == handle) return i;
+ }
+ printf("(%d) ERROR: handle %d was not found\n",
+ line_count, handle);
+ exit(1);
+
+ return -1; /* Not reached */
+}
+
+
+static struct cli_state *c;
+
+static void sigsegv(int sig)
+{
+ char line[200];
+ printf("segv at line %d\n", line_count);
+ slprintf(line, sizeof(line), "/usr/X11R6/bin/xterm -e gdb /proc/%d/exe %d",
+ (int)getpid(), (int)getpid());
+ system(line);
+ exit(1);
+}
+
+void nb_setup(struct cli_state *cli)
+{
+ signal(SIGSEGV, sigsegv);
+ c = cli;
+ start_timer();
+ children[nbio_id].done = 0;
+}
+
+
+void nb_unlink(const char *fname)
+{
+ if (!cli_unlink(c, fname)) {
+#if NBDEBUG
+ printf("(%d) unlink %s failed (%s)\n",
+ line_count, fname, cli_errstr(c));
+#endif
+ }
+}
+
+
+void nb_createx(const char *fname,
+ unsigned create_options, unsigned create_disposition, int handle)
+{
+ int fd, i;
+ uint32 desired_access;
+
+ if (create_options & FILE_DIRECTORY_FILE) {
+ desired_access = FILE_READ_DATA;
+ } else {
+ desired_access = FILE_READ_DATA | FILE_WRITE_DATA;
+ }
+
+ fd = cli_nt_create_full(c, fname, 0,
+ desired_access,
+ 0x0,
+ FILE_SHARE_READ|FILE_SHARE_WRITE,
+ create_disposition,
+ create_options, 0);
+ if (fd == -1 && handle != -1) {
+ printf("ERROR: cli_nt_create_full failed for %s - %s\n",
+ fname, cli_errstr(c));
+ exit(1);
+ }
+ if (fd != -1 && handle == -1) {
+ printf("ERROR: cli_nt_create_full succeeded for %s\n", fname);
+ exit(1);
+ }
+ if (fd == -1) return;
+
+ for (i=0;i<MAX_FILES;i++) {
+ if (ftable[i].handle == 0) break;
+ }
+ if (i == MAX_FILES) {
+ printf("(%d) file table full for %s\n", line_count,
+ fname);
+ exit(1);
+ }
+ ftable[i].handle = handle;
+ ftable[i].fd = fd;
+}
+
+void nb_writex(int handle, int offset, int size, int ret_size)
+{
+ int i;
+
+ if (buf[0] == 0) memset(buf, 1, sizeof(buf));
+
+ i = find_handle(handle);
+ if (cli_write(c, ftable[i].fd, 0, buf, offset, size) != ret_size) {
+ printf("(%d) ERROR: write failed on handle %d, fd %d \
+errno %d (%s)\n", line_count, handle, ftable[i].fd, errno, strerror(errno));
+ exit(1);
+ }
+
+ children[nbio_id].bytes_out += ret_size;
+}
+
+void nb_readx(int handle, int offset, int size, int ret_size)
+{
+ int i, ret;
+
+ i = find_handle(handle);
+ if ((ret=cli_read(c, ftable[i].fd, buf, offset, size)) != ret_size) {
+ printf("(%d) ERROR: read failed on handle %d ofs=%d size=%d res=%d fd %d errno %d (%s)\n",
+ line_count, handle, offset, size, ret, ftable[i].fd, errno, strerror(errno));
+ exit(1);
+ }
+ children[nbio_id].bytes_in += ret_size;
+}
+
+void nb_close(int handle)
+{
+ int i;
+ i = find_handle(handle);
+ if (!cli_close(c, ftable[i].fd)) {
+ printf("(%d) close failed on handle %d\n", line_count, handle);
+ exit(1);
+ }
+ ftable[i].handle = 0;
+}
+
+void nb_rmdir(const char *fname)
+{
+ if (!cli_rmdir(c, fname)) {
+ printf("ERROR: rmdir %s failed (%s)\n",
+ fname, cli_errstr(c));
+ exit(1);
+ }
+}
+
+void nb_rename(const char *old, const char *new)
+{
+ if (!cli_rename(c, old, new)) {
+ printf("ERROR: rename %s %s failed (%s)\n",
+ old, new, cli_errstr(c));
+ exit(1);
+ }
+}
+
+
+void nb_qpathinfo(const char *fname)
+{
+ cli_qpathinfo(c, fname, NULL, NULL, NULL, NULL, NULL);
+}
+
+void nb_qfileinfo(int fnum)
+{
+ int i;
+ i = find_handle(fnum);
+ cli_qfileinfo(c, ftable[i].fd, NULL, NULL, NULL, NULL, NULL, NULL, NULL);
+}
+
+void nb_qfsinfo(int level)
+{
+ int bsize, total, avail;
+ /* this is not the right call - we need cli_qfsinfo() */
+ cli_dskattr(c, &bsize, &total, &avail);
+}
+
+static void find_fn(file_info *finfo, const char *name, void *state)
+{
+ /* noop */
+}
+
+void nb_findfirst(const char *mask)
+{
+ cli_list(c, mask, 0, find_fn, NULL);
+}
+
+void nb_flush(int fnum)
+{
+ int i;
+ i = find_handle(fnum);
+ /* hmmm, we don't have cli_flush() yet */
+}
+
+static int total_deleted;
+
+static void delete_fn(file_info *finfo, const char *name, void *state)
+{
+ char *s, *n;
+ if (finfo->name[0] == '.') return;
+
+ n = strdup(name);
+ n[strlen(n)-1] = 0;
+ asprintf(&s, "%s%s", n, finfo->name);
+ if (finfo->mode & aDIR) {
+ char *s2;
+ asprintf(&s2, "%s\\*", s);
+ cli_list(c, s2, aDIR, delete_fn, NULL);
+ nb_rmdir(s);
+ } else {
+ total_deleted++;
+ nb_unlink(s);
+ }
+ free(s);
+ free(n);
+}
+
+void nb_deltree(const char *dname)
+{
+ char *mask;
+ asprintf(&mask, "%s\\*", dname);
+
+ total_deleted = 0;
+ cli_list(c, mask, aDIR, delete_fn, NULL);
+ free(mask);
+ cli_rmdir(c, dname);
+
+ if (total_deleted) printf("WARNING: Cleaned up %d files\n", total_deleted);
+}
+
+
+void nb_cleanup(void)
+{
+ cli_rmdir(c, "clients");
+ children[nbio_id].done = 1;
+}
diff --git a/source/torture/nsstest.c b/source/torture/nsstest.c
new file mode 100644
index 00000000000..a803cd7e719
--- /dev/null
+++ b/source/torture/nsstest.c
@@ -0,0 +1,451 @@
+/*
+ Unix SMB/CIFS implementation.
+ nss tester for winbindd
+ Copyright (C) Andrew Tridgell 2001
+ 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 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"
+
+static const char *so_path = "/lib/libnss_winbind.so";
+static const char *nss_name = "winbind";
+static int nss_errno;
+static NSS_STATUS last_error;
+static int total_errors;
+
+static void *find_fn(const char *name)
+{
+ pstring s;
+ static void *h;
+ void *res;
+
+ pstr_sprintf(s, "_nss_%s_%s", nss_name, name);
+
+ if (!h) {
+ h = sys_dlopen(so_path, RTLD_LAZY);
+ }
+ if (!h) {
+ printf("Can't open shared library %s\n", so_path);
+ exit(1);
+ }
+ res = sys_dlsym(h, s);
+ if (!res) {
+ printf("Can't find function %s\n", s);
+ total_errors++;
+ return NULL;
+ }
+ return res;
+}
+
+static void report_nss_error(const char *who, NSS_STATUS status)
+{
+ last_error = status;
+ total_errors++;
+ printf("ERROR %s: NSS_STATUS=%d %d (nss_errno=%d)\n",
+ who, status, NSS_STATUS_SUCCESS, nss_errno);
+}
+
+static struct passwd *nss_getpwent(void)
+{
+ NSS_STATUS (*_nss_getpwent_r)(struct passwd *, char *,
+ size_t , int *) = find_fn("getpwent_r");
+ static struct passwd pwd;
+ static char buf[1000];
+ NSS_STATUS status;
+
+ if (!_nss_getpwent_r)
+ return NULL;
+
+ status = _nss_getpwent_r(&pwd, buf, sizeof(buf), &nss_errno);
+ if (status == NSS_STATUS_NOTFOUND) {
+ return NULL;
+ }
+ if (status != NSS_STATUS_SUCCESS) {
+ report_nss_error("getpwent", status);
+ return NULL;
+ }
+ return &pwd;
+}
+
+static struct passwd *nss_getpwnam(const char *name)
+{
+ NSS_STATUS (*_nss_getpwnam_r)(const char *, struct passwd *, char *,
+ size_t , int *) = find_fn("getpwnam_r");
+ static struct passwd pwd;
+ static char buf[1000];
+ NSS_STATUS status;
+
+ if (!_nss_getpwnam_r)
+ return NULL;
+
+ status = _nss_getpwnam_r(name, &pwd, buf, sizeof(buf), &nss_errno);
+ if (status == NSS_STATUS_NOTFOUND) {
+ return NULL;
+ }
+ if (status != NSS_STATUS_SUCCESS) {
+ report_nss_error("getpwnam", status);
+ return NULL;
+ }
+ return &pwd;
+}
+
+static struct passwd *nss_getpwuid(uid_t uid)
+{
+ NSS_STATUS (*_nss_getpwuid_r)(uid_t , struct passwd *, char *,
+ size_t , int *) = find_fn("getpwuid_r");
+ static struct passwd pwd;
+ static char buf[1000];
+ NSS_STATUS status;
+
+ if (!_nss_getpwuid_r)
+ return NULL;
+
+ status = _nss_getpwuid_r(uid, &pwd, buf, sizeof(buf), &nss_errno);
+ if (status == NSS_STATUS_NOTFOUND) {
+ return NULL;
+ }
+ if (status != NSS_STATUS_SUCCESS) {
+ report_nss_error("getpwuid", status);
+ return NULL;
+ }
+ return &pwd;
+}
+
+static void nss_setpwent(void)
+{
+ NSS_STATUS (*_nss_setpwent)(void) = find_fn("setpwent");
+ NSS_STATUS status;
+
+ if (!_nss_setpwent)
+ return;
+
+ status = _nss_setpwent();
+ if (status != NSS_STATUS_SUCCESS) {
+ report_nss_error("setpwent", status);
+ }
+}
+
+static void nss_endpwent(void)
+{
+ NSS_STATUS (*_nss_endpwent)(void) = find_fn("endpwent");
+ NSS_STATUS status;
+
+ if (!_nss_endpwent)
+ return;
+
+ status = _nss_endpwent();
+ if (status != NSS_STATUS_SUCCESS) {
+ report_nss_error("endpwent", status);
+ }
+}
+
+
+static struct group *nss_getgrent(void)
+{
+ NSS_STATUS (*_nss_getgrent_r)(struct group *, char *,
+ size_t , int *) = find_fn("getgrent_r");
+ static struct group grp;
+ static char *buf;
+ static int buflen = 1024;
+ NSS_STATUS status;
+
+ if (!_nss_getgrent_r)
+ return NULL;
+
+ if (!buf)
+ buf = malloc(buflen);
+
+again:
+ status = _nss_getgrent_r(&grp, buf, buflen, &nss_errno);
+ if (status == NSS_STATUS_TRYAGAIN) {
+ buflen *= 2;
+ buf = realloc(buf, buflen);
+ goto again;
+ }
+ if (status == NSS_STATUS_NOTFOUND) {
+ return NULL;
+ }
+ if (status != NSS_STATUS_SUCCESS) {
+ report_nss_error("getgrent", status);
+ return NULL;
+ }
+ return &grp;
+}
+
+static struct group *nss_getgrnam(const char *name)
+{
+ NSS_STATUS (*_nss_getgrnam_r)(const char *, struct group *, char *,
+ size_t , int *) = find_fn("getgrnam_r");
+ static struct group grp;
+ static char *buf;
+ static int buflen = 1000;
+ NSS_STATUS status;
+
+ if (!_nss_getgrnam_r)
+ return NULL;
+
+ if (!buf)
+ buf = malloc(buflen);
+again:
+ status = _nss_getgrnam_r(name, &grp, buf, buflen, &nss_errno);
+ if (status == NSS_STATUS_TRYAGAIN) {
+ buflen *= 2;
+ buf = realloc(buf, buflen);
+ goto again;
+ }
+ if (status == NSS_STATUS_NOTFOUND) {
+ return NULL;
+ }
+ if (status != NSS_STATUS_SUCCESS) {
+ report_nss_error("getgrnam", status);
+ return NULL;
+ }
+ return &grp;
+}
+
+static struct group *nss_getgrgid(gid_t gid)
+{
+ NSS_STATUS (*_nss_getgrgid_r)(gid_t , struct group *, char *,
+ size_t , int *) = find_fn("getgrgid_r");
+ static struct group grp;
+ static char *buf;
+ static int buflen = 1000;
+ NSS_STATUS status;
+
+ if (!_nss_getgrgid_r)
+ return NULL;
+
+ if (!buf)
+ buf = malloc(buflen);
+
+again:
+ status = _nss_getgrgid_r(gid, &grp, buf, buflen, &nss_errno);
+ if (status == NSS_STATUS_TRYAGAIN) {
+ buflen *= 2;
+ buf = realloc(buf, buflen);
+ goto again;
+ }
+ if (status == NSS_STATUS_NOTFOUND) {
+ return NULL;
+ }
+ if (status != NSS_STATUS_SUCCESS) {
+ report_nss_error("getgrgid", status);
+ return NULL;
+ }
+ return &grp;
+}
+
+static void nss_setgrent(void)
+{
+ NSS_STATUS (*_nss_setgrent)(void) = find_fn("setgrent");
+ NSS_STATUS status;
+
+ if (!_nss_setgrent)
+ return;
+
+ status = _nss_setgrent();
+ if (status != NSS_STATUS_SUCCESS) {
+ report_nss_error("setgrent", status);
+ }
+}
+
+static void nss_endgrent(void)
+{
+ NSS_STATUS (*_nss_endgrent)(void) = find_fn("endgrent");
+ NSS_STATUS status;
+
+ if (!_nss_endgrent)
+ return;
+
+ status = _nss_endgrent();
+ if (status != NSS_STATUS_SUCCESS) {
+ report_nss_error("endgrent", status);
+ }
+}
+
+static int nss_initgroups(char *user, gid_t group, gid_t **groups, long int *start, long int *size)
+{
+ NSS_STATUS (*_nss_initgroups)(char *, gid_t , long int *,
+ long int *, gid_t **, long int , int *) =
+ find_fn("initgroups_dyn");
+ NSS_STATUS status;
+
+ if (!_nss_initgroups)
+ return NSS_STATUS_UNAVAIL;
+
+ status = _nss_initgroups(user, group, start, size, groups, 0, &nss_errno);
+ if (status != NSS_STATUS_SUCCESS) {
+ report_nss_error("initgroups", status);
+ }
+ return status;
+}
+
+static void print_passwd(struct passwd *pwd)
+{
+ printf("%s:%s:%lu:%lu:%s:%s:%s\n",
+ pwd->pw_name,
+ pwd->pw_passwd,
+ (unsigned long)pwd->pw_uid,
+ (unsigned long)pwd->pw_gid,
+ pwd->pw_gecos,
+ pwd->pw_dir,
+ pwd->pw_shell);
+}
+
+static void print_group(struct group *grp)
+{
+ int i;
+ printf("%s:%s:%lu: ",
+ grp->gr_name,
+ grp->gr_passwd,
+ (unsigned long)grp->gr_gid);
+
+ if (!grp->gr_mem[0]) {
+ printf("\n");
+ return;
+ }
+
+ for (i=0; grp->gr_mem[i+1]; i++) {
+ printf("%s, ", grp->gr_mem[i]);
+ }
+ printf("%s\n", grp->gr_mem[i]);
+}
+
+static void nss_test_initgroups(char *name, gid_t gid)
+{
+ long int size = 16;
+ long int start = 1;
+ gid_t *groups = NULL;
+ int i;
+ NSS_STATUS status;
+
+ groups = (gid_t *)malloc(size * sizeof(gid_t));
+ groups[0] = gid;
+
+ status = nss_initgroups(name, gid, &groups, &start, &size);
+ if (status == NSS_STATUS_UNAVAIL) {
+ printf("No initgroups fn\n");
+ return;
+ }
+
+ for (i=0; i<start-1; i++) {
+ printf("%lu, ", (unsigned long)groups[i]);
+ }
+ printf("%lu\n", (unsigned long)groups[i]);
+}
+
+
+static void nss_test_users(void)
+{
+ struct passwd *pwd;
+
+ nss_setpwent();
+ /* loop over all users */
+ while ((pwd = nss_getpwent())) {
+ printf("Testing user %s\n", pwd->pw_name);
+ printf("getpwent: "); print_passwd(pwd);
+ pwd = nss_getpwuid(pwd->pw_uid);
+ if (!pwd) {
+ total_errors++;
+ printf("ERROR: can't getpwuid\n");
+ continue;
+ }
+ printf("getpwuid: "); print_passwd(pwd);
+ pwd = nss_getpwnam(pwd->pw_name);
+ if (!pwd) {
+ total_errors++;
+ printf("ERROR: can't getpwnam\n");
+ continue;
+ }
+ printf("getpwnam: "); print_passwd(pwd);
+ printf("initgroups: "); nss_test_initgroups(pwd->pw_name, pwd->pw_gid);
+ printf("\n");
+ }
+ nss_endpwent();
+}
+
+static void nss_test_groups(void)
+{
+ struct group *grp;
+
+ nss_setgrent();
+ /* loop over all groups */
+ while ((grp = nss_getgrent())) {
+ printf("Testing group %s\n", grp->gr_name);
+ printf("getgrent: "); print_group(grp);
+ grp = nss_getgrnam(grp->gr_name);
+ if (!grp) {
+ total_errors++;
+ printf("ERROR: can't getgrnam\n");
+ continue;
+ }
+ printf("getgrnam: "); print_group(grp);
+ grp = nss_getgrgid(grp->gr_gid);
+ if (!grp) {
+ total_errors++;
+ printf("ERROR: can't getgrgid\n");
+ continue;
+ }
+ printf("getgrgid: "); print_group(grp);
+ printf("\n");
+ }
+ nss_endgrent();
+}
+
+static void nss_test_errors(void)
+{
+ struct passwd *pwd;
+ struct group *grp;
+
+ pwd = getpwnam("nosuchname");
+ if (pwd || last_error != NSS_STATUS_NOTFOUND) {
+ total_errors++;
+ printf("ERROR Non existant user gave error %d\n", last_error);
+ }
+
+ pwd = getpwuid(0xFFF0);
+ if (pwd || last_error != NSS_STATUS_NOTFOUND) {
+ total_errors++;
+ printf("ERROR Non existant uid gave error %d\n", last_error);
+ }
+
+ grp = getgrnam("nosuchgroup");
+ if (grp || last_error != NSS_STATUS_NOTFOUND) {
+ total_errors++;
+ printf("ERROR Non existant group gave error %d\n", last_error);
+ }
+
+ grp = getgrgid(0xFFF0);
+ if (grp || last_error != NSS_STATUS_NOTFOUND) {
+ total_errors++;
+ printf("ERROR Non existant gid gave error %d\n", last_error);
+ }
+}
+
+ int main(int argc, char *argv[])
+{
+ if (argc > 1) so_path = argv[1];
+ if (argc > 2) nss_name = argv[2];
+
+ nss_test_users();
+ nss_test_groups();
+ nss_test_errors();
+
+ printf("total_errors=%d\n", total_errors);
+
+ return total_errors;
+}
diff --git a/source/torture/rpctorture.c b/source/torture/rpctorture.c
new file mode 100644
index 00000000000..d95c0cee0fe
--- /dev/null
+++ b/source/torture/rpctorture.c
@@ -0,0 +1,541 @@
+/*
+ Unix SMB/CIFS implementation.
+ SMB client
+ Copyright (C) Andrew Tridgell 1994-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 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"
+
+#ifndef REGISTER
+#define REGISTER 0
+#endif
+
+extern pstring global_myname;
+
+extern pstring user_socket_options;
+
+
+extern file_info def_finfo;
+
+#define CNV_LANG(s) dos2unix_format(s,False)
+#define CNV_INPUT(s) unix2dos_format(s,True)
+
+static struct cli_state smbcli;
+struct cli_state *smb_cli = &smbcli;
+
+FILE *out_hnd;
+
+static pstring password; /* local copy only, if one is entered */
+
+/****************************************************************************
+initialise smb client structure
+****************************************************************************/
+void rpcclient_init(void)
+{
+ memset((char *)smb_cli, '\0', sizeof(smb_cli));
+ cli_initialise(smb_cli);
+ smb_cli->capabilities |= CAP_NT_SMBS;
+}
+
+/****************************************************************************
+make smb client connection
+****************************************************************************/
+static BOOL rpcclient_connect(struct client_info *info)
+{
+ struct nmb_name calling;
+ struct nmb_name called;
+
+ make_nmb_name(&called , dns_to_netbios_name(info->dest_host ), info->name_type);
+ make_nmb_name(&calling, dns_to_netbios_name(info->myhostname), 0x0);
+
+ if (!cli_establish_connection(smb_cli,
+ info->dest_host, &info->dest_ip,
+ &calling, &called,
+ info->share, info->svc_type,
+ False, True))
+ {
+ DEBUG(0,("rpcclient_connect: connection failed\n"));
+ cli_shutdown(smb_cli);
+ return False;
+ }
+
+ return True;
+}
+
+/****************************************************************************
+stop the smb connection(s?)
+****************************************************************************/
+static void rpcclient_stop(void)
+{
+ cli_shutdown(smb_cli);
+}
+
+/****************************************************************************
+ log in as an nt user, log out again.
+****************************************************************************/
+void run_enums_test(int num_ops, struct client_info *cli_info, struct cli_state *cli)
+{
+ pstring cmd;
+ int i;
+
+ /* establish connections. nothing to stop these being re-established. */
+ rpcclient_connect(cli_info);
+
+ DEBUG(5,("rpcclient_connect: cli->fd:%d\n", cli->fd));
+ if (cli->fd <= 0)
+ {
+ fprintf(out_hnd, "warning: connection could not be established to %s<%02x>\n",
+ cli_info->dest_host, cli_info->name_type);
+ return;
+ }
+
+ for (i = 0; i < num_ops; i++)
+ {
+ set_first_token("");
+ cmd_srv_enum_sess(cli_info);
+ set_first_token("");
+ cmd_srv_enum_shares(cli_info);
+ set_first_token("");
+ cmd_srv_enum_files(cli_info);
+
+ if (password[0] != 0)
+ {
+ slprintf(cmd, sizeof(cmd)-1, "1");
+ set_first_token(cmd);
+ }
+ else
+ {
+ set_first_token("");
+ }
+ cmd_srv_enum_conn(cli_info);
+ }
+
+ rpcclient_stop();
+
+}
+
+/****************************************************************************
+ log in as an nt user, log out again.
+****************************************************************************/
+void run_ntlogin_test(int num_ops, struct client_info *cli_info, struct cli_state *cli)
+{
+ pstring cmd;
+ int i;
+
+ /* establish connections. nothing to stop these being re-established. */
+ rpcclient_connect(cli_info);
+
+ DEBUG(5,("rpcclient_connect: cli->fd:%d\n", cli->fd));
+ if (cli->fd <= 0)
+ {
+ fprintf(out_hnd, "warning: connection could not be established to %s<%02x>\n",
+ cli_info->dest_host, cli_info->name_type);
+ return;
+ }
+
+ for (i = 0; i < num_ops; i++)
+ {
+ slprintf(cmd, sizeof(cmd)-1, "%s %s", cli->user_name, password);
+ set_first_token(cmd);
+
+ cmd_netlogon_login_test(cli_info);
+ }
+
+ rpcclient_stop();
+
+}
+
+/****************************************************************************
+ runs n simultaneous functions.
+****************************************************************************/
+static void create_procs(int nprocs, int numops,
+ struct client_info *cli_info, struct cli_state *cli,
+ void (*fn)(int, struct client_info *, struct cli_state *))
+{
+ int i, status;
+
+ for (i=0;i<nprocs;i++)
+ {
+ if (fork() == 0)
+ {
+ pid_t mypid = getpid();
+ sys_srandom(mypid ^ time(NULL));
+ fn(numops, cli_info, cli);
+ fflush(out_hnd);
+ _exit(0);
+ }
+ }
+
+ for (i=0;i<nprocs;i++)
+ {
+ waitpid(0, &status, 0);
+ }
+}
+/****************************************************************************
+usage on the program - OUT OF DATE!
+****************************************************************************/
+static void usage(char *pname)
+{
+ fprintf(out_hnd, "Usage: %s service <password> [-d debuglevel] [-l log] ",
+ pname);
+
+ fprintf(out_hnd, "\nVersion %s\n",SAMBA_VERSION_STRING);
+ fprintf(out_hnd, "\t-d debuglevel set the debuglevel\n");
+ fprintf(out_hnd, "\t-l log basename. Basename for log/debug files\n");
+ fprintf(out_hnd, "\t-n netbios name. Use this name as my netbios name\n");
+ fprintf(out_hnd, "\t-m max protocol set the max protocol level\n");
+ fprintf(out_hnd, "\t-I dest IP use this IP to connect to\n");
+ fprintf(out_hnd, "\t-E write messages to stderr instead of stdout\n");
+ fprintf(out_hnd, "\t-U username set the network username\n");
+ fprintf(out_hnd, "\t-W workgroup set the workgroup name\n");
+ fprintf(out_hnd, "\t-t terminal code terminal i/o code {sjis|euc|jis7|jis8|junet|hex}\n");
+ fprintf(out_hnd, "\n");
+}
+
+enum client_action
+{
+ CLIENT_NONE,
+ CLIENT_IPC,
+ CLIENT_SVC
+};
+
+/****************************************************************************
+ main program
+****************************************************************************/
+ int main(int argc,char *argv[])
+{
+ char *pname = argv[0];
+ int opt;
+ extern char *optarg;
+ extern int optind;
+ pstring term_code;
+ BOOL got_pass = False;
+ char *cmd_str="";
+ enum client_action cli_action = CLIENT_NONE;
+ int nprocs = 1;
+ int numops = 100;
+ pstring logfile;
+
+ struct client_info cli_info;
+
+ out_hnd = stdout;
+
+ rpcclient_init();
+
+#ifdef KANJI
+ pstrcpy(term_code, KANJI);
+#else /* KANJI */
+ *term_code = 0;
+#endif /* KANJI */
+
+ if (!lp_load(dyn_CONFIGFILE,True, False, False))
+ {
+ fprintf(stderr, "Can't load %s - run testparm to debug it\n", dyn_CONFIGFILE);
+ }
+
+ DEBUGLEVEL = 0;
+
+ cli_info.put_total_size = 0;
+ cli_info.put_total_time_ms = 0;
+ cli_info.get_total_size = 0;
+ cli_info.get_total_time_ms = 0;
+
+ cli_info.dir_total = 0;
+ cli_info.newer_than = 0;
+ cli_info.archive_level = 0;
+ cli_info.print_mode = 1;
+
+ cli_info.translation = False;
+ cli_info.recurse_dir = False;
+ cli_info.lowercase = False;
+ cli_info.prompt = True;
+ cli_info.abort_mget = True;
+
+ cli_info.dest_ip.s_addr = 0;
+ cli_info.name_type = 0x20;
+
+ pstrcpy(cli_info.cur_dir , "\\");
+ pstrcpy(cli_info.file_sel, "");
+ pstrcpy(cli_info.base_dir, "");
+ pstrcpy(smb_cli->domain, "");
+ pstrcpy(smb_cli->user_name, "");
+ pstrcpy(cli_info.myhostname, "");
+ pstrcpy(cli_info.dest_host, "");
+
+ pstrcpy(cli_info.svc_type, "A:");
+ pstrcpy(cli_info.share, "");
+ pstrcpy(cli_info.service, "");
+
+ ZERO_STRUCT(cli_info.dom.level3_sid);
+ pstrcpy(cli_info.dom.level3_dom, "");
+ ZERO_STRUCT(cli_info.dom.level5_sid);
+ pstrcpy(cli_info.dom.level5_dom, "");
+
+ smb_cli->nt_pipe_fnum = 0xffff;
+
+ setup_logging(pname, True);
+
+ if (!get_myname(global_myname))
+ {
+ fprintf(stderr, "Failed to get my hostname.\n");
+ }
+
+ password[0] = 0;
+
+ if (argc < 2)
+ {
+ usage(pname);
+ exit(1);
+ }
+
+ if (*argv[1] != '-')
+ {
+ pstrcpy(cli_info.service, argv[1]);
+ /* Convert any '/' characters in the service name to '\' characters */
+ string_replace( cli_info.service, '/','\\');
+ argc--;
+ argv++;
+
+ DEBUG(1,("service: %s\n", cli_info.service));
+
+ if (count_chars(cli_info.service,'\\') < 3)
+ {
+ usage(pname);
+ printf("\n%s: Not enough '\\' characters in service\n", cli_info.service);
+ exit(1);
+ }
+
+ /*
+ if (count_chars(cli_info.service,'\\') > 3)
+ {
+ usage(pname);
+ printf("\n%s: Too many '\\' characters in service\n", cli_info.service);
+ exit(1);
+ }
+ */
+
+ if (argc > 1 && (*argv[1] != '-'))
+ {
+ got_pass = True;
+ pstrcpy(password,argv[1]);
+ memset(argv[1],'X',strlen(argv[1]));
+ argc--;
+ argv++;
+ }
+
+ cli_action = CLIENT_SVC;
+ }
+
+ while ((opt = getopt(argc, argv,"s:O:M:S:i:N:o:n:d:l:hI:EB:U:L:t:m:W:T:D:c:")) != EOF)
+ {
+ switch (opt)
+ {
+ case 'm':
+ {
+ /* FIXME ... max_protocol seems to be funny here */
+
+ int max_protocol = 0;
+ max_protocol = interpret_protocol(optarg,max_protocol);
+ fprintf(stderr, "max protocol not currently supported\n");
+ break;
+ }
+
+ case 'O':
+ {
+ pstrcpy(user_socket_options,optarg);
+ break;
+ }
+
+ case 'S':
+ {
+ pstrcpy(cli_info.dest_host,optarg);
+ strupper_m(cli_info.dest_host);
+ cli_action = CLIENT_IPC;
+ break;
+ }
+
+ case 'i':
+ {
+ pstrcpy(scope, optarg);
+ break;
+ }
+
+ case 'U':
+ {
+ char *lp;
+ pstrcpy(smb_cli->user_name,optarg);
+ if ((lp=strchr_m(smb_cli->user_name,'%')))
+ {
+ *lp = 0;
+ pstrcpy(password,lp+1);
+ got_pass = True;
+ memset(strchr_m(optarg,'%')+1,'X',strlen(password));
+ }
+ break;
+ }
+
+ case 'W':
+ {
+ pstrcpy(smb_cli->domain,optarg);
+ break;
+ }
+
+ case 'E':
+ {
+ dbf = x_stderr;
+ break;
+ }
+
+ case 'I':
+ {
+ cli_info.dest_ip = *interpret_addr2(optarg);
+ if (is_zero_ip(cli_info.dest_ip))
+ {
+ exit(1);
+ }
+ break;
+ }
+
+ case 'N':
+ {
+ nprocs = atoi(optarg);
+ break;
+ }
+
+ case 'o':
+ {
+ numops = atoi(optarg);
+ break;
+ }
+
+ case 'n':
+ {
+ fstrcpy(global_myname, optarg);
+ break;
+ }
+
+ case 'd':
+ {
+ if (*optarg == 'A')
+ DEBUGLEVEL = 10000;
+ else
+ DEBUGLEVEL = atoi(optarg);
+ break;
+ }
+
+ case 'l':
+ {
+ slprintf(logfile, sizeof(logfile)-1,
+ "%s.client",optarg);
+ lp_set_logfile(logfile);
+ break;
+ }
+
+ case 'c':
+ {
+ cmd_str = optarg;
+ got_pass = True;
+ break;
+ }
+
+ case 'h':
+ {
+ usage(pname);
+ exit(0);
+ break;
+ }
+
+ case 's':
+ {
+ pstrcpy(dyn_CONFIGFILE, optarg);
+ break;
+ }
+
+ case 't':
+ {
+ pstrcpy(term_code, optarg);
+ break;
+ }
+
+ default:
+ {
+ usage(pname);
+ exit(1);
+ break;
+ }
+ }
+ }
+
+ if (cli_action == CLIENT_NONE)
+ {
+ usage(pname);
+ exit(1);
+ }
+
+ strupper_m(global_myname);
+ fstrcpy(cli_info.myhostname, global_myname);
+
+ DEBUG(3,("%s client started (version %s)\n",timestring(False),SAMBA_VERSION_STRING));
+
+ if (*smb_cli->domain == 0)
+ {
+ pstrcpy(smb_cli->domain,lp_workgroup());
+ }
+ strupper_m(smb_cli->domain);
+
+ load_interfaces();
+
+ if (cli_action == CLIENT_IPC)
+ {
+ pstrcpy(cli_info.share, "IPC$");
+ pstrcpy(cli_info.svc_type, "IPC");
+ }
+
+ fstrcpy(cli_info.mach_acct, cli_info.myhostname);
+ strupper_m(cli_info.mach_acct);
+ fstrcat(cli_info.mach_acct, "$");
+
+ /* set the password cache info */
+ if (got_pass)
+ {
+ if (password[0] == 0)
+ {
+ pwd_set_nullpwd(&(smb_cli->pwd));
+ }
+ else
+ {
+ pwd_make_lm_nt_16(&(smb_cli->pwd), password); /* generate 16 byte hashes */
+ }
+ }
+ else
+ {
+ char *pwd = getpass("Enter Password:");
+ safe_strcpy(password, pwd, sizeof(password));
+ pwd_make_lm_nt_16(&(smb_cli->pwd), password); /* generate 16 byte hashes */
+ }
+
+ create_procs(nprocs, numops, &cli_info, smb_cli, run_enums_test);
+
+ if (password[0] != 0)
+ {
+ create_procs(nprocs, numops, &cli_info, smb_cli, run_ntlogin_test);
+ }
+
+ fflush(out_hnd);
+
+ return(0);
+}
diff --git a/source/torture/samtest.h b/source/torture/samtest.h
new file mode 100644
index 00000000000..a136ab191e4
--- /dev/null
+++ b/source/torture/samtest.h
@@ -0,0 +1,38 @@
+/*
+ Unix SMB/CIFS implementation.
+ SAM module tester
+
+ Copyright (C) Jelmer Vernooij 2002
+
+ Most of this code was ripped off of rpcclient.
+ Copyright (C) Tim Potter 2000-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 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.
+*/
+
+struct samtest_state {
+ SAM_CONTEXT *context;
+ NT_USER_TOKEN *token;
+};
+
+struct cmd_set {
+ char *name;
+ NTSTATUS (*fn)(struct samtest_state *sam, TALLOC_CTX *mem_ctx, int argc,
+ char **argv);
+ char *description;
+ char *usage;
+};
+
+
diff --git a/source/torture/scanner.c b/source/torture/scanner.c
new file mode 100644
index 00000000000..93f89c105cf
--- /dev/null
+++ b/source/torture/scanner.c
@@ -0,0 +1,430 @@
+/*
+ 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 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.
+*/
+
+#define NO_SYSLOG
+
+#include "includes.h"
+
+#define VERBOSE 0
+#define OP_MIN 0
+#define OP_MAX 20
+
+/****************************************************************************
+look for a partial hit
+****************************************************************************/
+static void trans2_check_hit(const char *format, int op, int level, NTSTATUS status)
+{
+ if (NT_STATUS_V(status) == NT_STATUS_V(NT_STATUS_INVALID_LEVEL) ||
+ NT_STATUS_V(status) == NT_STATUS_V(NT_STATUS_NOT_IMPLEMENTED) ||
+ NT_STATUS_V(status) == NT_STATUS_V(NT_STATUS_NOT_SUPPORTED) ||
+ NT_STATUS_V(status) == NT_STATUS_V(NT_STATUS_UNSUCCESSFUL) ||
+ NT_STATUS_V(status) == NT_STATUS_V(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 cli_state *cli,
+ int op,
+ char *param, char *data,
+ int param_len, int data_len,
+ int *rparam_len, int *rdata_len)
+{
+ uint16 setup = op;
+ char *rparam=NULL, *rdata=NULL;
+
+ if (!cli_send_trans(cli, SMBtrans2,
+ NULL, /* name */
+ -1, 0, /* fid, flags */
+ &setup, 1, 0, /* setup, length, max */
+ param, param_len, 2, /* param, length, max */
+ data, data_len, cli->max_xmit /* data, length, max */
+ )) {
+ return cli_nt_error(cli);
+ }
+
+ cli_receive_trans(cli, SMBtrans2,
+ &rparam, rparam_len,
+ &rdata, rdata_len);
+
+ SAFE_FREE(rdata);
+ SAFE_FREE(rparam);
+
+ return cli_nt_error(cli);
+}
+
+
+static NTSTATUS try_trans2_len(struct cli_state *cli,
+ const char *format,
+ int op, int level,
+ char *param, char *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,
+ sizeof(pstring), 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 < sizeof(pstring)) {
+ 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 for existance of a trans2 call
+****************************************************************************/
+static BOOL scan_trans2(struct cli_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;
+ pstring param, data;
+ NTSTATUS status;
+
+ memset(data, 0, sizeof(data));
+ 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)) 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)) 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)) return True;
+
+ /* try with a file name */
+ param_len = 6;
+ SSVAL(param, 0, level);
+ SSVAL(param, 2, 0);
+ SSVAL(param, 4, 0);
+ param_len += clistr_push(cli, &param[6], fname, -1, STR_TERMINATE);
+
+ status = try_trans2_len(cli, "fname", op, level, param, data, param_len, &data_len,
+ &rparam_len, &rdata_len);
+ if (NT_STATUS_IS_OK(status)) 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 += clistr_push(cli, &param[6], "\\newfile.dat", -1, STR_TERMINATE);
+
+ status = try_trans2_len(cli, "newfile", op, level, param, data, param_len, &data_len,
+ &rparam_len, &rdata_len);
+ cli_unlink(cli, "\\newfile.dat");
+ cli_rmdir(cli, "\\newfile.dat");
+ if (NT_STATUS_IS_OK(status)) return True;
+
+ /* try dfs style */
+ cli_mkdir(cli, "\\testdir");
+ param_len = 2;
+ SSVAL(param, 0, level);
+ param_len += clistr_push(cli, &param[2], "\\testdir", -1, STR_TERMINATE);
+
+ status = try_trans2_len(cli, "dfs", op, level, param, data, param_len, &data_len,
+ &rparam_len, &rdata_len);
+ cli_rmdir(cli, "\\testdir");
+ if (NT_STATUS_IS_OK(status)) return True;
+
+ return False;
+}
+
+
+BOOL torture_trans2_scan(int dummy)
+{
+ static struct cli_state *cli;
+ int op, level;
+ const char *fname = "\\scanner.dat";
+ int fnum, dnum;
+
+ printf("starting trans2 scan test\n");
+
+ if (!torture_open_connection(&cli)) {
+ return False;
+ }
+
+ fnum = cli_open(cli, fname, O_RDWR | O_CREAT | O_TRUNC,
+ DENY_NONE);
+ dnum = cli_open(cli, "\\", 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_trans2(cli, op, level, fnum, dnum, fname);
+ }
+
+ for (level = 0x100; level <= 0x130; level++) {
+ scan_trans2(cli, op, level, fnum, dnum, fname);
+ }
+
+ for (level = 1000; level < 1050; level++) {
+ scan_trans2(cli, op, level, fnum, dnum, fname);
+ }
+ }
+
+ torture_close_connection(cli);
+
+ printf("trans2 scan finished\n");
+ return True;
+}
+
+
+
+
+/****************************************************************************
+look for a partial hit
+****************************************************************************/
+static void nttrans_check_hit(const char *format, int op, int level, NTSTATUS status)
+{
+ if (NT_STATUS_V(status) == NT_STATUS_V(NT_STATUS_INVALID_LEVEL) ||
+ NT_STATUS_V(status) == NT_STATUS_V(NT_STATUS_NOT_IMPLEMENTED) ||
+ NT_STATUS_V(status) == NT_STATUS_V(NT_STATUS_NOT_SUPPORTED) ||
+ NT_STATUS_V(status) == NT_STATUS_V(NT_STATUS_UNSUCCESSFUL) ||
+ NT_STATUS_V(status) == NT_STATUS_V(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 nttrans call
+****************************************************************************/
+static NTSTATUS try_nttrans(struct cli_state *cli,
+ int op,
+ char *param, char *data,
+ int param_len, int data_len,
+ int *rparam_len, int *rdata_len)
+{
+ char *rparam=NULL, *rdata=NULL;
+
+ if (!cli_send_nt_trans(cli, op,
+ 0,
+ NULL, 0, 0,
+ param, param_len, 2, /* param, length, max */
+ data, data_len, cli->max_xmit /* data, length, max */
+ )) {
+ return cli_nt_error(cli);
+ }
+
+ cli_receive_nt_trans(cli,
+ &rparam, rparam_len,
+ &rdata, rdata_len);
+
+ SAFE_FREE(rdata);
+ SAFE_FREE(rparam);
+
+ return cli_nt_error(cli);
+}
+
+
+static NTSTATUS try_nttrans_len(struct cli_state *cli,
+ const char *format,
+ int op, int level,
+ char *param, char *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,
+ sizeof(pstring), 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 < sizeof(pstring)) {
+ 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 cli_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;
+ pstring param, data;
+ NTSTATUS status;
+
+ memset(data, 0, sizeof(data));
+ 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)) 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)) 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)) return True;
+
+ /* try with a file name */
+ param_len = 6;
+ SSVAL(param, 0, level);
+ SSVAL(param, 2, 0);
+ SSVAL(param, 4, 0);
+ param_len += clistr_push(cli, &param[6], fname, -1, STR_TERMINATE);
+
+ status = try_nttrans_len(cli, "fname", op, level, param, data, param_len, &data_len,
+ &rparam_len, &rdata_len);
+ if (NT_STATUS_IS_OK(status)) 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 += clistr_push(cli, &param[6], "\\newfile.dat", -1, STR_TERMINATE);
+
+ status = try_nttrans_len(cli, "newfile", op, level, param, data, param_len, &data_len,
+ &rparam_len, &rdata_len);
+ cli_unlink(cli, "\\newfile.dat");
+ cli_rmdir(cli, "\\newfile.dat");
+ if (NT_STATUS_IS_OK(status)) return True;
+
+ /* try dfs style */
+ cli_mkdir(cli, "\\testdir");
+ param_len = 2;
+ SSVAL(param, 0, level);
+ param_len += clistr_push(cli, &param[2], "\\testdir", -1, STR_TERMINATE);
+
+ status = try_nttrans_len(cli, "dfs", op, level, param, data, param_len, &data_len,
+ &rparam_len, &rdata_len);
+ cli_rmdir(cli, "\\testdir");
+ if (NT_STATUS_IS_OK(status)) return True;
+
+ return False;
+}
+
+
+BOOL torture_nttrans_scan(int dummy)
+{
+ static struct cli_state *cli;
+ int op, level;
+ const char *fname = "\\scanner.dat";
+ int fnum, dnum;
+
+ printf("starting nttrans scan test\n");
+
+ if (!torture_open_connection(&cli)) {
+ return False;
+ }
+
+ fnum = cli_open(cli, fname, O_RDWR | O_CREAT | O_TRUNC,
+ DENY_NONE);
+ dnum = cli_open(cli, "\\", O_RDONLY, DENY_NONE);
+
+ for (op=OP_MIN; op<=OP_MAX; op++) {
+ printf("Scanning op=%d\n", op);
+ for (level = 0; level <= 50; level++) {
+ scan_nttrans(cli, op, level, fnum, dnum, fname);
+ }
+
+ for (level = 0x100; level <= 0x130; level++) {
+ scan_nttrans(cli, op, level, fnum, dnum, fname);
+ }
+
+ for (level = 1000; level < 1050; level++) {
+ scan_nttrans(cli, op, level, fnum, dnum, fname);
+ }
+ }
+
+ torture_close_connection(cli);
+
+ printf("nttrans scan finished\n");
+ return True;
+}
diff --git a/source/torture/smbiconv.c b/source/torture/smbiconv.c
new file mode 100644
index 00000000000..1dd168b0bb0
--- /dev/null
+++ b/source/torture/smbiconv.c
@@ -0,0 +1,243 @@
+/*
+ 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 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"
+
+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" },
+ POPT_COMMON_SAMBA
+ POPT_TABLEEND
+ };
+
+ setlinebuf(stdout);
+
+ pc = poptGetContext("smbiconv", argc, (const char **) argv,
+ long_options, 0);
+
+ poptSetOtherOptionHelp(pc, "[FILE] ...");
+
+ while(poptGetNextOpt(pc) != -1);
+
+ /* the following functions are part of the Samba debugging
+ facilities. See lib/debug.c */
+ setup_logging("smbiconv", True);
+
+ 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(to, from);
+ if((int)cd == -1) {
+ DEBUG(0,("unable to find from or to encoding, exiting...\n"));
+ 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/source/torture/t_doschar.c b/source/torture/t_doschar.c
new file mode 100644
index 00000000000..41698350d6e
--- /dev/null
+++ b/source/torture/t_doschar.c
@@ -0,0 +1,42 @@
+/*
+ Samba - Unix SMB/CIFS implementation
+ Test harness for check_dos_char
+ Copyright (C) Martin Pool 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 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"
+
+
+/*
+ * Just print out DOS validity or not for every character.
+ *
+ * DOS validity for a Unicode character set means that it can be
+ * represented in DOS codepage, and that the DOS character maps back
+ * to the same Unicode character.
+ *
+ * This depends on which DOS codepage is configured.
+ */
+ int main(void)
+{
+ smb_ucs2_t i;
+
+ for (i = 0; i < 0xffff; i++) {
+ printf("%d %d\n", (int) i, (int) check_dos_char(i));
+ }
+
+ return 0;
+}
diff --git a/source/torture/t_push_ucs2.c b/source/torture/t_push_ucs2.c
new file mode 100644
index 00000000000..8bfc6f7ad9e
--- /dev/null
+++ b/source/torture/t_push_ucs2.c
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2003 by Martin Pool
+ * Copyright (C) 2003 by Andrew Bartlett
+ *
+ * Test harness for push_ucs2
+ */
+
+#include "includes.h"
+
+static int check_push_ucs2(const char *orig)
+{
+ smb_ucs2_t *dest = NULL;
+ char *orig2 = NULL;
+ int ret;
+
+ push_ucs2_allocate(&dest, orig);
+ pull_ucs2_allocate(&orig2, dest);
+ ret = strcmp(orig, orig2);
+ if (ret) {
+ fprintf(stderr, "orig: %s\n", orig);
+ fprintf(stderr, "orig (UNIX -> UCS2 -> UNIX): %s\n", orig2);
+ }
+
+ SAFE_FREE(dest);
+ SAFE_FREE(orig2);
+
+ return ret;
+}
+
+int main(int argc, char *argv[])
+{
+ int i, ret = 0;
+ int count = 1;
+
+ /* Needed to initialize character set */
+ lp_load("/dev/null", True, False, False);
+
+ if (argc < 2) {
+ fprintf(stderr, "usage: %s STRING1 [COUNT]\n"
+ "Checks that a string translated UNIX->UCS2->UNIX is unchanged\n"
+ "Should be always 0\n",
+ argv[0]);
+ return 2;
+ }
+ if (argc >= 3)
+ count = atoi(argv[2]);
+
+ for (i = 0; ((i < count) && (!ret)); i++)
+ ret = check_push_ucs2(argv[1]);
+
+ printf("%d\n", ret);
+
+ return 0;
+}
diff --git a/source/torture/t_strcmp.c b/source/torture/t_strcmp.c
new file mode 100644
index 00000000000..bc8640ee550
--- /dev/null
+++ b/source/torture/t_strcmp.c
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2003 by Martin Pool
+ *
+ * Test harness for StrCaseCmp
+ */
+
+#include "includes.h"
+
+int main(int argc, char *argv[])
+{
+ int i, ret;
+ int iters = 1;
+
+ /* Needed to initialize character set */
+ lp_load("/dev/null", True, False, False);
+
+ if (argc < 3) {
+ fprintf(stderr, "usage: %s STRING1 STRING2 [ITERS]\n"
+ "Compares two strings, prints the results of StrCaseCmp\n",
+ argv[0]);
+ return 2;
+ }
+ if (argc >= 4)
+ iters = atoi(argv[3]);
+
+ for (i = 0; i < iters; i++)
+ ret = StrCaseCmp(argv[1], argv[2]);
+
+ printf("%d\n", ret);
+
+ return 0;
+}
diff --git a/source/torture/t_stringoverflow.c b/source/torture/t_stringoverflow.c
new file mode 100644
index 00000000000..ec14d81189e
--- /dev/null
+++ b/source/torture/t_stringoverflow.c
@@ -0,0 +1,23 @@
+#include "includes.h"
+
+ int main(void)
+{
+ fstring dest;
+ char *ptr = dest;
+
+ printf("running on valgrind? %d\n", RUNNING_ON_VALGRIND);
+
+ /* Try copying a string into an fstring buffer. The string
+ * will actually fit, but this is still wrong because you
+ * can't pstrcpy into an fstring. This should trap in a
+ * developer build. */
+
+#if 0
+ /* As of CVS 20030318, this will be trapped at compile time! */
+ pstrcpy(dest, "hello");
+#endif /* 0 */
+
+ pstrcpy(ptr, "hello!");
+
+ return 0;
+}
diff --git a/source/torture/t_strstr.c b/source/torture/t_strstr.c
new file mode 100644
index 00000000000..25709526fe8
--- /dev/null
+++ b/source/torture/t_strstr.c
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2003 by Martin Pool
+ *
+ * Test harness for strstr_m
+ */
+
+#include "includes.h"
+
+int main(int argc, char *argv[])
+{
+ int i;
+ int iters = 1;
+
+ char *ret;
+
+ /* Needed to initialize character set */
+ lp_load("/dev/null", True, False, False);
+
+ if (argc < 3) {
+ fprintf(stderr, "usage: %s STRING1 STRING2 [ITERS]\n"
+ "Compares two strings, prints the results of strstr_m\n",
+ argv[0]);
+ return 2;
+ }
+ if (argc >= 4)
+ iters = atoi(argv[3]);
+
+ for (i = 0; i < iters; i++) {
+ ret = strstr_m(argv[1], argv[2]);
+ }
+
+ printf("%s\n", ret);
+
+ return 0;
+}
diff --git a/source/torture/torture.c b/source/torture/torture.c
new file mode 100644
index 00000000000..86bdca62a86
--- /dev/null
+++ b/source/torture/torture.c
@@ -0,0 +1,4994 @@
+/*
+ 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 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.
+*/
+
+#define NO_SYSLOG
+
+#include "includes.h"
+
+static fstring host, workgroup, share, password, username, myname;
+static int max_protocol = PROTOCOL_NT1;
+static const char *sockops="TCP_NODELAY";
+static int nprocs=1;
+static int port_to_use=0;
+int torture_numops=100;
+static int procnum; /* records process count number when forking */
+static struct cli_state *current_cli;
+static fstring randomfname;
+static BOOL use_oplocks;
+static BOOL use_level_II_oplocks;
+static const char *client_txt = "client_oplocks.txt";
+static BOOL use_kerberos;
+
+BOOL torture_showall = False;
+
+static double create_procs(BOOL (*fn)(int), BOOL *result);
+
+
+static struct timeval tp1,tp2;
+
+void start_timer(void)
+{
+ gettimeofday(&tp1,NULL);
+}
+
+double end_timer(void)
+{
+ gettimeofday(&tp2,NULL);
+ return((tp2.tv_sec - tp1.tv_sec) +
+ (tp2.tv_usec - tp1.tv_usec)*1.0e-6);
+}
+
+
+/* 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;
+}
+
+
+static BOOL open_nbt_connection(struct cli_state *c)
+{
+ struct nmb_name called, calling;
+ struct in_addr ip;
+
+ ZERO_STRUCTP(c);
+
+ make_nmb_name(&calling, myname, 0x0);
+ make_nmb_name(&called , host, 0x20);
+
+ zero_ip(&ip);
+
+ if (!cli_initialise(c)) {
+ printf("Failed initialize cli_struct to connect with %s\n", host);
+ return False;
+ }
+
+ c->port = port_to_use;
+
+ if (!cli_connect(c, host, &ip)) {
+ printf("Failed to connect with %s\n", host);
+ return False;
+ }
+
+ c->use_kerberos = use_kerberos;
+
+ c->timeout = 120000; /* set a really long timeout (2 minutes) */
+ if (use_oplocks) c->use_oplocks = True;
+ if (use_level_II_oplocks) c->use_level_II_oplocks = True;
+
+ if (!cli_session_request(c, &calling, &called)) {
+ /*
+ * Well, that failed, try *SMBSERVER ...
+ * However, we must reconnect as well ...
+ */
+ if (!cli_connect(c, host, &ip)) {
+ printf("Failed to connect with %s\n", host);
+ return False;
+ }
+
+ make_nmb_name(&called, "*SMBSERVER", 0x20);
+ if (!cli_session_request(c, &calling, &called)) {
+ printf("%s rejected the session\n",host);
+ printf("We tried with a called name of %s & %s\n",
+ host, "*SMBSERVER");
+ cli_shutdown(c);
+ return False;
+ }
+ }
+
+ return True;
+}
+
+BOOL torture_open_connection(struct cli_state **c)
+{
+ BOOL retry;
+ int flags = 0;
+ NTSTATUS status;
+
+ if (use_kerberos)
+ flags |= CLI_FULL_CONNECTION_USE_KERBEROS;
+
+ status = cli_full_connection(c, myname,
+ host, NULL, port_to_use,
+ share, "?????",
+ username, workgroup,
+ password, flags, Undefined, &retry);
+ if (!NT_STATUS_IS_OK(status)) {
+ return False;
+ }
+
+ if (use_oplocks) (*c)->use_oplocks = True;
+ if (use_level_II_oplocks) (*c)->use_level_II_oplocks = True;
+ (*c)->timeout = 120000; /* set a really long timeout (2 minutes) */
+
+ return True;
+}
+
+BOOL torture_cli_session_setup2(struct cli_state *cli, uint16 *new_vuid)
+{
+ uint16 old_vuid = cli->vuid;
+ fstring old_user_name;
+ size_t passlen = strlen(password);
+ BOOL ret;
+
+ fstrcpy(old_user_name, cli->user_name);
+ cli->vuid = 0;
+ ret = cli_session_setup(cli, username, password, passlen, password, passlen, workgroup);
+ *new_vuid = cli->vuid;
+ cli->vuid = old_vuid;
+ fstrcpy(cli->user_name, old_user_name);
+ return ret;
+}
+
+
+BOOL torture_close_connection(struct cli_state *c)
+{
+ BOOL ret = True;
+ if (!cli_tdis(c)) {
+ printf("tdis failed (%s)\n", cli_errstr(c));
+ ret = False;
+ }
+
+ cli_shutdown(c);
+
+ return ret;
+}
+
+
+/* check if the server produced the expected error code */
+static BOOL check_error(int line, struct cli_state *c,
+ uint8 eclass, uint32 ecode, NTSTATUS nterr)
+{
+ if (cli_is_dos_error(c)) {
+ uint8 class;
+ uint32 num;
+
+ /* Check DOS error */
+
+ cli_dos_error(c, &class, &num);
+
+ if (eclass != class || ecode != num) {
+ printf("unexpected error code class=%d code=%d\n",
+ (int)class, (int)num);
+ printf(" expected %d/%d %s (line=%d)\n",
+ (int)eclass, (int)ecode, nt_errstr(nterr), line);
+ return False;
+ }
+
+ } else {
+ NTSTATUS status;
+
+ /* Check NT error */
+
+ status = cli_nt_error(c);
+
+ if (NT_STATUS_V(nterr) != NT_STATUS_V(status)) {
+ printf("unexpected error code %s\n", nt_errstr(status));
+ printf(" expected %s (line=%d)\n", nt_errstr(nterr), line);
+ return False;
+ }
+ }
+
+ return True;
+}
+
+
+static BOOL wait_lock(struct cli_state *c, int fnum, uint32 offset, uint32 len)
+{
+ while (!cli_lock(c, fnum, offset, len, -1, WRITE_LOCK)) {
+ if (!check_error(__LINE__, c, ERRDOS, ERRlock, NT_STATUS_LOCK_NOT_GRANTED)) return False;
+ }
+ return True;
+}
+
+
+static BOOL rw_torture(struct cli_state *c)
+{
+ const char *lockfname = "\\torture.lck";
+ fstring fname;
+ int fnum;
+ int fnum2;
+ pid_t pid2, pid = getpid();
+ int i, j;
+ char buf[1024];
+ BOOL correct = True;
+
+ fnum2 = cli_open(c, lockfname, O_RDWR | O_CREAT | O_EXCL,
+ DENY_NONE);
+ if (fnum2 == -1)
+ fnum2 = cli_open(c, lockfname, O_RDWR, DENY_NONE);
+ if (fnum2 == -1) {
+ printf("open of %s failed (%s)\n", lockfname, cli_errstr(c));
+ return False;
+ }
+
+
+ for (i=0;i<torture_numops;i++) {
+ unsigned n = (unsigned)sys_random()%10;
+ if (i % 10 == 0) {
+ printf("%d\r", i); fflush(stdout);
+ }
+ slprintf(fname, sizeof(fstring) - 1, "\\torture.%u", n);
+
+ if (!wait_lock(c, fnum2, n*sizeof(int), sizeof(int))) {
+ return False;
+ }
+
+ fnum = cli_open(c, fname, O_RDWR | O_CREAT | O_TRUNC, DENY_ALL);
+ if (fnum == -1) {
+ printf("open failed (%s)\n", cli_errstr(c));
+ correct = False;
+ break;
+ }
+
+ if (cli_write(c, fnum, 0, (char *)&pid, 0, sizeof(pid)) != sizeof(pid)) {
+ printf("write failed (%s)\n", cli_errstr(c));
+ correct = False;
+ }
+
+ for (j=0;j<50;j++) {
+ if (cli_write(c, fnum, 0, (char *)buf,
+ sizeof(pid)+(j*sizeof(buf)),
+ sizeof(buf)) != sizeof(buf)) {
+ printf("write failed (%s)\n", cli_errstr(c));
+ correct = False;
+ }
+ }
+
+ pid2 = 0;
+
+ if (cli_read(c, fnum, (char *)&pid2, 0, sizeof(pid)) != sizeof(pid)) {
+ printf("read failed (%s)\n", cli_errstr(c));
+ correct = False;
+ }
+
+ if (pid2 != pid) {
+ printf("data corruption!\n");
+ correct = False;
+ }
+
+ if (!cli_close(c, fnum)) {
+ printf("close failed (%s)\n", cli_errstr(c));
+ correct = False;
+ }
+
+ if (!cli_unlink(c, fname)) {
+ printf("unlink failed (%s)\n", cli_errstr(c));
+ correct = False;
+ }
+
+ if (!cli_unlock(c, fnum2, n*sizeof(int), sizeof(int))) {
+ printf("unlock failed (%s)\n", cli_errstr(c));
+ correct = False;
+ }
+ }
+
+ cli_close(c, fnum2);
+ cli_unlink(c, lockfname);
+
+ printf("%d\n", i);
+
+ return correct;
+}
+
+static BOOL run_torture(int dummy)
+{
+ struct cli_state *cli;
+ BOOL ret;
+
+ cli = current_cli;
+
+ cli_sockopt(cli, sockops);
+
+ ret = rw_torture(cli);
+
+ if (!torture_close_connection(cli)) {
+ ret = False;
+ }
+
+ return ret;
+}
+
+static BOOL rw_torture3(struct cli_state *c, char *lockfname)
+{
+ int fnum = -1;
+ unsigned int i = 0;
+ char buf[131072];
+ char buf_rd[131072];
+ unsigned count;
+ unsigned countprev = 0;
+ ssize_t sent = 0;
+ BOOL correct = True;
+
+ srandom(1);
+ for (i = 0; i < sizeof(buf); i += sizeof(uint32))
+ {
+ SIVAL(buf, i, sys_random());
+ }
+
+ if (procnum == 0)
+ {
+ fnum = cli_open(c, lockfname, O_RDWR | O_CREAT | O_EXCL,
+ DENY_NONE);
+ if (fnum == -1) {
+ printf("first open read/write of %s failed (%s)\n",
+ lockfname, cli_errstr(c));
+ return False;
+ }
+ }
+ else
+ {
+ for (i = 0; i < 500 && fnum == -1; i++)
+ {
+ fnum = cli_open(c, lockfname, O_RDONLY,
+ DENY_NONE);
+ smb_msleep(10);
+ }
+ if (fnum == -1) {
+ printf("second open read-only of %s failed (%s)\n",
+ lockfname, cli_errstr(c));
+ return False;
+ }
+ }
+
+ i = 0;
+ for (count = 0; count < sizeof(buf); count += sent)
+ {
+ if (count >= countprev) {
+ printf("%d %8d\r", i, count);
+ fflush(stdout);
+ i++;
+ countprev += (sizeof(buf) / 20);
+ }
+
+ if (procnum == 0)
+ {
+ sent = ((unsigned)sys_random()%(20))+ 1;
+ if (sent > sizeof(buf) - count)
+ {
+ sent = sizeof(buf) - count;
+ }
+
+ if (cli_write(c, fnum, 0, buf+count, count, (size_t)sent) != sent) {
+ printf("write failed (%s)\n", cli_errstr(c));
+ correct = False;
+ }
+ }
+ else
+ {
+ sent = cli_read(c, fnum, buf_rd+count, count,
+ sizeof(buf)-count);
+ if (sent < 0)
+ {
+ printf("read failed offset:%d size:%ld (%s)\n",
+ count, (unsigned long)sizeof(buf)-count,
+ cli_errstr(c));
+ correct = False;
+ sent = 0;
+ }
+ if (sent > 0)
+ {
+ if (memcmp(buf_rd+count, buf+count, sent) != 0)
+ {
+ printf("read/write compare failed\n");
+ printf("offset: %d req %ld recvd %ld\n", count, (unsigned long)sizeof(buf)-count, (unsigned long)sent);
+ correct = False;
+ break;
+ }
+ }
+ }
+
+ }
+
+ if (!cli_close(c, fnum)) {
+ printf("close failed (%s)\n", cli_errstr(c));
+ correct = False;
+ }
+
+ return correct;
+}
+
+static BOOL rw_torture2(struct cli_state *c1, struct cli_state *c2)
+{
+ const char *lockfname = "\\torture2.lck";
+ int fnum1;
+ int fnum2;
+ int i;
+ uchar buf[131072];
+ uchar buf_rd[131072];
+ BOOL correct = True;
+ ssize_t bytes_read;
+
+ if (!cli_unlink(c1, lockfname)) {
+ printf("unlink failed (%s) (normal, this file should not exist)\n", cli_errstr(c1));
+ }
+
+ fnum1 = cli_open(c1, lockfname, O_RDWR | O_CREAT | O_EXCL,
+ DENY_NONE);
+ if (fnum1 == -1) {
+ printf("first open read/write of %s failed (%s)\n",
+ lockfname, cli_errstr(c1));
+ return False;
+ }
+ fnum2 = cli_open(c2, lockfname, O_RDONLY,
+ DENY_NONE);
+ if (fnum2 == -1) {
+ printf("second open read-only of %s failed (%s)\n",
+ lockfname, cli_errstr(c2));
+ cli_close(c1, fnum1);
+ return False;
+ }
+
+ for (i=0;i<torture_numops;i++)
+ {
+ size_t buf_size = ((unsigned)sys_random()%(sizeof(buf)-1))+ 1;
+ if (i % 10 == 0) {
+ printf("%d\r", i); fflush(stdout);
+ }
+
+ generate_random_buffer(buf, buf_size, False);
+
+ if (cli_write(c1, fnum1, 0, buf, 0, buf_size) != buf_size) {
+ printf("write failed (%s)\n", cli_errstr(c1));
+ correct = False;
+ break;
+ }
+
+ if ((bytes_read = cli_read(c2, fnum2, buf_rd, 0, buf_size)) != buf_size) {
+ printf("read failed (%s)\n", cli_errstr(c2));
+ printf("read %d, expected %ld\n", bytes_read,
+ (unsigned long)buf_size);
+ correct = False;
+ break;
+ }
+
+ if (memcmp(buf_rd, buf, buf_size) != 0)
+ {
+ printf("read/write compare failed\n");
+ correct = False;
+ break;
+ }
+ }
+
+ if (!cli_close(c2, fnum2)) {
+ printf("close failed (%s)\n", cli_errstr(c2));
+ correct = False;
+ }
+ if (!cli_close(c1, fnum1)) {
+ printf("close failed (%s)\n", cli_errstr(c1));
+ correct = False;
+ }
+
+ if (!cli_unlink(c1, lockfname)) {
+ printf("unlink failed (%s)\n", cli_errstr(c1));
+ correct = False;
+ }
+
+ return correct;
+}
+
+static BOOL run_readwritetest(int dummy)
+{
+ static struct cli_state *cli1, *cli2;
+ BOOL test1, test2;
+
+ if (!torture_open_connection(&cli1) || !torture_open_connection(&cli2)) {
+ return False;
+ }
+ cli_sockopt(cli1, sockops);
+ cli_sockopt(cli2, sockops);
+
+ printf("starting readwritetest\n");
+
+ test1 = rw_torture2(cli1, cli2);
+ printf("Passed readwritetest v1: %s\n", BOOLSTR(test1));
+
+ if (test1) {
+ test2 = rw_torture2(cli1, cli1);
+ printf("Passed readwritetest v2: %s\n", BOOLSTR(test2));
+ }
+
+ if (!torture_close_connection(cli1)) {
+ test1 = False;
+ }
+
+ if (!torture_close_connection(cli2)) {
+ test2 = False;
+ }
+
+ return (test1 && test2);
+}
+
+static BOOL run_readwritemulti(int dummy)
+{
+ struct cli_state *cli;
+ BOOL test;
+
+ cli = current_cli;
+
+ cli_sockopt(cli, sockops);
+
+ printf("run_readwritemulti: fname %s\n", randomfname);
+ test = rw_torture3(cli, randomfname);
+
+ if (!torture_close_connection(cli)) {
+ test = False;
+ }
+
+ return test;
+}
+
+static BOOL run_readwritelarge(int dummy)
+{
+ static struct cli_state *cli1;
+ int fnum1;
+ const char *lockfname = "\\large.dat";
+ size_t fsize;
+ char buf[126*1024];
+ BOOL correct = True;
+
+ if (!torture_open_connection(&cli1)) {
+ return False;
+ }
+ cli_sockopt(cli1, sockops);
+ memset(buf,'\0',sizeof(buf));
+
+ cli1->max_xmit = 128*1024;
+
+ printf("starting readwritelarge\n");
+
+ cli_unlink(cli1, lockfname);
+
+ fnum1 = cli_open(cli1, lockfname, O_RDWR | O_CREAT | O_EXCL, DENY_NONE);
+ if (fnum1 == -1) {
+ printf("open read/write of %s failed (%s)\n", lockfname, cli_errstr(cli1));
+ return False;
+ }
+
+ cli_write(cli1, fnum1, 0, buf, 0, sizeof(buf));
+
+ if (!cli_qfileinfo(cli1, fnum1, NULL, &fsize, NULL, NULL, NULL, NULL, NULL)) {
+ printf("qfileinfo failed (%s)\n", cli_errstr(cli1));
+ correct = False;
+ }
+
+ if (fsize == sizeof(buf))
+ printf("readwritelarge test 1 succeeded (size = %lx)\n",
+ (unsigned long)fsize);
+ else {
+ printf("readwritelarge test 1 failed (size = %lx)\n",
+ (unsigned long)fsize);
+ correct = False;
+ }
+
+ if (!cli_close(cli1, fnum1)) {
+ printf("close failed (%s)\n", cli_errstr(cli1));
+ correct = False;
+ }
+
+ if (!cli_unlink(cli1, lockfname)) {
+ printf("unlink failed (%s)\n", cli_errstr(cli1));
+ correct = False;
+ }
+
+ fnum1 = cli_open(cli1, lockfname, O_RDWR | O_CREAT | O_EXCL, DENY_NONE);
+ if (fnum1 == -1) {
+ printf("open read/write of %s failed (%s)\n", lockfname, cli_errstr(cli1));
+ return False;
+ }
+
+ cli1->max_xmit = 4*1024;
+
+ cli_smbwrite(cli1, fnum1, buf, 0, sizeof(buf));
+
+ if (!cli_qfileinfo(cli1, fnum1, NULL, &fsize, NULL, NULL, NULL, NULL, NULL)) {
+ printf("qfileinfo failed (%s)\n", cli_errstr(cli1));
+ correct = False;
+ }
+
+ if (fsize == sizeof(buf))
+ printf("readwritelarge test 2 succeeded (size = %lx)\n",
+ (unsigned long)fsize);
+ else {
+ printf("readwritelarge test 2 failed (size = %lx)\n",
+ (unsigned long)fsize);
+ correct = False;
+ }
+
+#if 0
+ /* ToDo - set allocation. JRA */
+ if(!cli_set_allocation_size(cli1, fnum1, 0)) {
+ printf("set allocation size to zero failed (%s)\n", cli_errstr(&cli1));
+ return False;
+ }
+ if (!cli_qfileinfo(cli1, fnum1, NULL, &fsize, NULL, NULL, NULL, NULL, NULL)) {
+ printf("qfileinfo failed (%s)\n", cli_errstr(cli1));
+ correct = False;
+ }
+ if (fsize != 0)
+ printf("readwritelarge test 3 (truncate test) succeeded (size = %x)\n", fsize);
+#endif
+
+ if (!cli_close(cli1, fnum1)) {
+ printf("close failed (%s)\n", cli_errstr(cli1));
+ correct = False;
+ }
+
+ if (!torture_close_connection(cli1)) {
+ correct = False;
+ }
+ return correct;
+}
+
+int line_count = 0;
+int nbio_id;
+
+#define ival(s) strtol(s, NULL, 0)
+
+/* run a test that simulates an approximate netbench client load */
+static BOOL run_netbench(int client)
+{
+ struct cli_state *cli;
+ int i;
+ fstring fname;
+ pstring line;
+ char cname[20];
+ FILE *f;
+ const char *params[20];
+ BOOL correct = True;
+
+ cli = current_cli;
+
+ nbio_id = client;
+
+ cli_sockopt(cli, sockops);
+
+ nb_setup(cli);
+
+ slprintf(cname,sizeof(fname), "client%d", client);
+
+ f = fopen(client_txt, "r");
+
+ if (!f) {
+ perror(client_txt);
+ return False;
+ }
+
+ while (fgets(line, sizeof(line)-1, f)) {
+ line_count++;
+
+ line[strlen(line)-1] = 0;
+
+ /* printf("[%d] %s\n", line_count, line); */
+
+ all_string_sub(line,"client1", cname, sizeof(line));
+
+ /* parse the command parameters */
+ params[0] = strtok(line," ");
+ i = 0;
+ while (params[i]) params[++i] = strtok(NULL," ");
+
+ params[i] = "";
+
+ if (i < 2) continue;
+
+ if (!strncmp(params[0],"SMB", 3)) {
+ printf("ERROR: You are using a dbench 1 load file\n");
+ exit(1);
+ }
+
+ if (!strcmp(params[0],"NTCreateX")) {
+ nb_createx(params[1], ival(params[2]), ival(params[3]),
+ ival(params[4]));
+ } else if (!strcmp(params[0],"Close")) {
+ nb_close(ival(params[1]));
+ } else if (!strcmp(params[0],"Rename")) {
+ nb_rename(params[1], params[2]);
+ } else if (!strcmp(params[0],"Unlink")) {
+ nb_unlink(params[1]);
+ } else if (!strcmp(params[0],"Deltree")) {
+ nb_deltree(params[1]);
+ } else if (!strcmp(params[0],"Rmdir")) {
+ nb_rmdir(params[1]);
+ } else if (!strcmp(params[0],"QUERY_PATH_INFORMATION")) {
+ nb_qpathinfo(params[1]);
+ } else if (!strcmp(params[0],"QUERY_FILE_INFORMATION")) {
+ nb_qfileinfo(ival(params[1]));
+ } else if (!strcmp(params[0],"QUERY_FS_INFORMATION")) {
+ nb_qfsinfo(ival(params[1]));
+ } else if (!strcmp(params[0],"FIND_FIRST")) {
+ nb_findfirst(params[1]);
+ } else if (!strcmp(params[0],"WriteX")) {
+ nb_writex(ival(params[1]),
+ ival(params[2]), ival(params[3]), ival(params[4]));
+ } else if (!strcmp(params[0],"ReadX")) {
+ nb_readx(ival(params[1]),
+ ival(params[2]), ival(params[3]), ival(params[4]));
+ } else if (!strcmp(params[0],"Flush")) {
+ nb_flush(ival(params[1]));
+ } else {
+ printf("Unknown operation %s\n", params[0]);
+ exit(1);
+ }
+ }
+ fclose(f);
+
+ nb_cleanup();
+
+ if (!torture_close_connection(cli)) {
+ correct = False;
+ }
+
+ return correct;
+}
+
+
+/* run a test that simulates an approximate netbench client load */
+static BOOL run_nbench(int dummy)
+{
+ double t;
+ BOOL correct = True;
+
+ nbio_shmem(nprocs);
+
+ nbio_id = -1;
+
+ signal(SIGALRM, nb_alarm);
+ alarm(1);
+ t = create_procs(run_netbench, &correct);
+ alarm(0);
+
+ printf("\nThroughput %g MB/sec\n",
+ 1.0e-6 * nbio_total() / t);
+ return correct;
+}
+
+
+/*
+ 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 run_locktest1(int dummy)
+{
+ struct cli_state *cli1, *cli2;
+ const char *fname = "\\lockt1.lck";
+ int fnum1, fnum2, fnum3;
+ time_t t1, t2;
+ unsigned lock_timeout;
+
+ if (!torture_open_connection(&cli1) || !torture_open_connection(&cli2)) {
+ return False;
+ }
+ cli_sockopt(cli1, sockops);
+ cli_sockopt(cli2, sockops);
+
+ printf("starting locktest1\n");
+
+ cli_unlink(cli1, fname);
+
+ fnum1 = cli_open(cli1, fname, O_RDWR|O_CREAT|O_EXCL, DENY_NONE);
+ if (fnum1 == -1) {
+ printf("open of %s failed (%s)\n", fname, cli_errstr(cli1));
+ return False;
+ }
+ fnum2 = cli_open(cli1, fname, O_RDWR, DENY_NONE);
+ if (fnum2 == -1) {
+ printf("open2 of %s failed (%s)\n", fname, cli_errstr(cli1));
+ return False;
+ }
+ fnum3 = cli_open(cli2, fname, O_RDWR, DENY_NONE);
+ if (fnum3 == -1) {
+ printf("open3 of %s failed (%s)\n", fname, cli_errstr(cli2));
+ return False;
+ }
+
+ if (!cli_lock(cli1, fnum1, 0, 4, 0, WRITE_LOCK)) {
+ printf("lock1 failed (%s)\n", cli_errstr(cli1));
+ return False;
+ }
+
+
+ if (cli_lock(cli2, fnum3, 0, 4, 0, WRITE_LOCK)) {
+ printf("lock2 succeeded! This is a locking bug\n");
+ return False;
+ } else {
+ if (!check_error(__LINE__, cli2, ERRDOS, ERRlock,
+ NT_STATUS_LOCK_NOT_GRANTED)) return False;
+ }
+
+
+ lock_timeout = (1 + (random() % 20));
+ printf("Testing lock timeout with timeout=%u\n", lock_timeout);
+ t1 = time(NULL);
+ if (cli_lock(cli2, fnum3, 0, 4, lock_timeout * 500, WRITE_LOCK)) {
+ printf("lock3 succeeded! This is a locking bug\n");
+ return False;
+ } else {
+ if (!check_error(__LINE__, cli2, ERRDOS, ERRlock,
+ NT_STATUS_FILE_LOCK_CONFLICT)) return False;
+ }
+ t2 = time(NULL);
+
+ if (ABS(t2 - t1) < lock_timeout-1) {
+ printf("error: This server appears not to support timed lock requests\n");
+ }
+
+ printf("server slept for %u seconds for a %u second timeout\n",
+ (unsigned int)(t2-t1), lock_timeout);
+
+ if (!cli_close(cli1, fnum2)) {
+ printf("close1 failed (%s)\n", cli_errstr(cli1));
+ return False;
+ }
+
+ if (cli_lock(cli2, fnum3, 0, 4, 0, WRITE_LOCK)) {
+ printf("lock4 succeeded! This is a locking bug\n");
+ return False;
+ } else {
+ if (!check_error(__LINE__, cli2, ERRDOS, ERRlock,
+ NT_STATUS_FILE_LOCK_CONFLICT)) return False;
+ }
+
+ if (!cli_close(cli1, fnum1)) {
+ printf("close2 failed (%s)\n", cli_errstr(cli1));
+ return False;
+ }
+
+ if (!cli_close(cli2, fnum3)) {
+ printf("close3 failed (%s)\n", cli_errstr(cli2));
+ return False;
+ }
+
+ if (!cli_unlink(cli1, fname)) {
+ printf("unlink failed (%s)\n", cli_errstr(cli1));
+ return False;
+ }
+
+
+ if (!torture_close_connection(cli1)) {
+ return False;
+ }
+
+ if (!torture_close_connection(cli2)) {
+ return False;
+ }
+
+ printf("Passed locktest1\n");
+ return True;
+}
+
+/*
+ this checks to see if a secondary tconx can use open files from an
+ earlier tconx
+ */
+static BOOL run_tcon_test(int dummy)
+{
+ static struct cli_state *cli;
+ const char *fname = "\\tcontest.tmp";
+ int fnum1;
+ uint16 cnum1, cnum2, cnum3;
+ uint16 vuid1, vuid2;
+ char buf[4];
+ BOOL ret = True;
+
+ if (!torture_open_connection(&cli)) {
+ return False;
+ }
+ cli_sockopt(cli, sockops);
+
+ printf("starting tcontest\n");
+
+ cli_unlink(cli, fname);
+
+ fnum1 = cli_open(cli, fname, O_RDWR|O_CREAT|O_EXCL, DENY_NONE);
+ if (fnum1 == -1) {
+ printf("open of %s failed (%s)\n", fname, cli_errstr(cli));
+ return False;
+ }
+
+ cnum1 = cli->cnum;
+ vuid1 = cli->vuid;
+
+ if (cli_write(cli, fnum1, 0, buf, 130, 4) != 4) {
+ printf("initial write failed (%s)", cli_errstr(cli));
+ return False;
+ }
+
+ if (!cli_send_tconX(cli, share, "?????",
+ password, strlen(password)+1)) {
+ printf("%s refused 2nd tree connect (%s)\n", host,
+ cli_errstr(cli));
+ cli_shutdown(cli);
+ return False;
+ }
+
+ cnum2 = cli->cnum;
+ cnum3 = MAX(cnum1, cnum2) + 1; /* any invalid number */
+ vuid2 = cli->vuid + 1;
+
+ /* try a write with the wrong tid */
+ cli->cnum = cnum2;
+
+ if (cli_write(cli, fnum1, 0, buf, 130, 4) == 4) {
+ printf("* server allows write with wrong TID\n");
+ ret = False;
+ } else {
+ printf("server fails write with wrong TID : %s\n", cli_errstr(cli));
+ }
+
+
+ /* try a write with an invalid tid */
+ cli->cnum = cnum3;
+
+ if (cli_write(cli, fnum1, 0, buf, 130, 4) == 4) {
+ printf("* server allows write with invalid TID\n");
+ ret = False;
+ } else {
+ printf("server fails write with invalid TID : %s\n", cli_errstr(cli));
+ }
+
+ /* try a write with an invalid vuid */
+ cli->vuid = vuid2;
+ cli->cnum = cnum1;
+
+ if (cli_write(cli, fnum1, 0, buf, 130, 4) == 4) {
+ printf("* server allows write with invalid VUID\n");
+ ret = False;
+ } else {
+ printf("server fails write with invalid VUID : %s\n", cli_errstr(cli));
+ }
+
+ cli->cnum = cnum1;
+ cli->vuid = vuid1;
+
+ if (!cli_close(cli, fnum1)) {
+ printf("close failed (%s)\n", cli_errstr(cli));
+ return False;
+ }
+
+ cli->cnum = cnum2;
+
+ if (!cli_tdis(cli)) {
+ printf("secondary tdis failed (%s)\n", cli_errstr(cli));
+ return False;
+ }
+
+ cli->cnum = cnum1;
+
+ if (!torture_close_connection(cli)) {
+ return False;
+ }
+
+ return ret;
+}
+
+
+/*
+ checks for old style tcon support
+ */
+static BOOL run_tcon2_test(int dummy)
+{
+ static struct cli_state *cli;
+ uint16 cnum, max_xmit;
+ char *service;
+ NTSTATUS status;
+
+ if (!torture_open_connection(&cli)) {
+ return False;
+ }
+ cli_sockopt(cli, sockops);
+
+ printf("starting tcon2 test\n");
+
+ asprintf(&service, "\\\\%s\\%s", host, share);
+
+ status = cli_raw_tcon(cli, service, password, "?????", &max_xmit, &cnum);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("tcon2 failed : %s\n", cli_errstr(cli));
+ } else {
+ printf("tcon OK : max_xmit=%d cnum=%d tid=%d\n",
+ (int)max_xmit, (int)cnum, SVAL(cli->inbuf, smb_tid));
+ }
+
+ if (!torture_close_connection(cli)) {
+ return False;
+ }
+
+ printf("Passed tcon2 test\n");
+ return True;
+}
+
+static BOOL tcon_devtest(struct cli_state *cli,
+ const char *myshare, const char *devtype,
+ const char *return_devtype,
+ NTSTATUS expected_error)
+{
+ BOOL status;
+ BOOL ret;
+
+ status = cli_send_tconX(cli, myshare, devtype,
+ password, strlen(password)+1);
+
+ if (NT_STATUS_IS_OK(expected_error)) {
+ if (status) {
+ if (strcmp(cli->dev, return_devtype) == 0) {
+ ret = True;
+ } else {
+ printf("tconX to share %s with type %s "
+ "succeeded but returned the wrong "
+ "device type (got [%s] but should have got [%s])\n",
+ myshare, devtype, cli->dev, return_devtype);
+ ret = False;
+ }
+ } else {
+ printf("tconX to share %s with type %s "
+ "should have succeeded but failed\n",
+ myshare, devtype);
+ ret = False;
+ }
+ cli_tdis(cli);
+ } else {
+ if (status) {
+ printf("tconx to share %s with type %s "
+ "should have failed but succeeded\n",
+ myshare, devtype);
+ ret = False;
+ } else {
+ if (NT_STATUS_EQUAL(cli_nt_error(cli),
+ expected_error)) {
+ ret = True;
+ } else {
+ printf("Returned unexpected error\n");
+ ret = False;
+ }
+ }
+ }
+ return ret;
+}
+
+/*
+ checks for correct tconX support
+ */
+static BOOL run_tcon_devtype_test(int dummy)
+{
+ static struct cli_state *cli1 = NULL;
+ BOOL retry;
+ int flags = 0;
+ NTSTATUS status;
+ BOOL ret = True;
+
+ status = cli_full_connection(&cli1, myname,
+ host, NULL, port_to_use,
+ NULL, NULL,
+ username, workgroup,
+ password, flags, Undefined, &retry);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("could not open connection\n");
+ return False;
+ }
+
+ if (!tcon_devtest(cli1, "IPC$", "A:", NULL, NT_STATUS_BAD_DEVICE_TYPE))
+ ret = False;
+
+ if (!tcon_devtest(cli1, "IPC$", "?????", "IPC", NT_STATUS_OK))
+ ret = False;
+
+ if (!tcon_devtest(cli1, "IPC$", "LPT:", NULL, NT_STATUS_BAD_DEVICE_TYPE))
+ ret = False;
+
+ if (!tcon_devtest(cli1, "IPC$", "IPC", "IPC", NT_STATUS_OK))
+ ret = False;
+
+ if (!tcon_devtest(cli1, "IPC$", "FOOBA", NULL, NT_STATUS_BAD_DEVICE_TYPE))
+ ret = False;
+
+ if (!tcon_devtest(cli1, share, "A:", "A:", NT_STATUS_OK))
+ ret = False;
+
+ if (!tcon_devtest(cli1, share, "?????", "A:", NT_STATUS_OK))
+ ret = False;
+
+ if (!tcon_devtest(cli1, share, "LPT:", NULL, NT_STATUS_BAD_DEVICE_TYPE))
+ ret = False;
+
+ if (!tcon_devtest(cli1, share, "IPC", NULL, NT_STATUS_BAD_DEVICE_TYPE))
+ ret = False;
+
+ if (!tcon_devtest(cli1, share, "FOOBA", NULL, NT_STATUS_BAD_DEVICE_TYPE))
+ ret = False;
+
+ cli_shutdown(cli1);
+
+ if (ret)
+ printf("Passed tcondevtest\n");
+
+ return ret;
+}
+
+
+/*
+ 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 run_locktest2(int dummy)
+{
+ static struct cli_state *cli;
+ const char *fname = "\\lockt2.lck";
+ int fnum1, fnum2, fnum3;
+ BOOL correct = True;
+
+ if (!torture_open_connection(&cli)) {
+ return False;
+ }
+
+ cli_sockopt(cli, sockops);
+
+ printf("starting locktest2\n");
+
+ cli_unlink(cli, fname);
+
+ cli_setpid(cli, 1);
+
+ fnum1 = cli_open(cli, fname, O_RDWR|O_CREAT|O_EXCL, DENY_NONE);
+ if (fnum1 == -1) {
+ printf("open of %s failed (%s)\n", fname, cli_errstr(cli));
+ return False;
+ }
+
+ fnum2 = cli_open(cli, fname, O_RDWR, DENY_NONE);
+ if (fnum2 == -1) {
+ printf("open2 of %s failed (%s)\n", fname, cli_errstr(cli));
+ return False;
+ }
+
+ cli_setpid(cli, 2);
+
+ fnum3 = cli_open(cli, fname, O_RDWR, DENY_NONE);
+ if (fnum3 == -1) {
+ printf("open3 of %s failed (%s)\n", fname, cli_errstr(cli));
+ return False;
+ }
+
+ cli_setpid(cli, 1);
+
+ if (!cli_lock(cli, fnum1, 0, 4, 0, WRITE_LOCK)) {
+ printf("lock1 failed (%s)\n", cli_errstr(cli));
+ return False;
+ }
+
+ if (cli_lock(cli, fnum1, 0, 4, 0, WRITE_LOCK)) {
+ printf("WRITE lock1 succeeded! This is a locking bug\n");
+ correct = False;
+ } else {
+ if (!check_error(__LINE__, cli, ERRDOS, ERRlock,
+ NT_STATUS_LOCK_NOT_GRANTED)) return False;
+ }
+
+ if (cli_lock(cli, fnum2, 0, 4, 0, WRITE_LOCK)) {
+ printf("WRITE lock2 succeeded! This is a locking bug\n");
+ correct = False;
+ } else {
+ if (!check_error(__LINE__, cli, ERRDOS, ERRlock,
+ NT_STATUS_LOCK_NOT_GRANTED)) return False;
+ }
+
+ if (cli_lock(cli, fnum2, 0, 4, 0, READ_LOCK)) {
+ printf("READ lock2 succeeded! This is a locking bug\n");
+ correct = False;
+ } else {
+ if (!check_error(__LINE__, cli, ERRDOS, ERRlock,
+ NT_STATUS_FILE_LOCK_CONFLICT)) return False;
+ }
+
+ if (!cli_lock(cli, fnum1, 100, 4, 0, WRITE_LOCK)) {
+ printf("lock at 100 failed (%s)\n", cli_errstr(cli));
+ }
+ cli_setpid(cli, 2);
+ if (cli_unlock(cli, fnum1, 100, 4)) {
+ printf("unlock at 100 succeeded! This is a locking bug\n");
+ correct = False;
+ }
+
+ if (cli_unlock(cli, fnum1, 0, 4)) {
+ printf("unlock1 succeeded! This is a locking bug\n");
+ correct = False;
+ } else {
+ if (!check_error(__LINE__, cli,
+ ERRDOS, ERRlock,
+ NT_STATUS_RANGE_NOT_LOCKED)) return False;
+ }
+
+ if (cli_unlock(cli, fnum1, 0, 8)) {
+ printf("unlock2 succeeded! This is a locking bug\n");
+ correct = False;
+ } else {
+ if (!check_error(__LINE__, cli,
+ ERRDOS, ERRlock,
+ NT_STATUS_RANGE_NOT_LOCKED)) return False;
+ }
+
+ if (cli_lock(cli, fnum3, 0, 4, 0, WRITE_LOCK)) {
+ printf("lock3 succeeded! This is a locking bug\n");
+ correct = False;
+ } else {
+ if (!check_error(__LINE__, cli, ERRDOS, ERRlock, NT_STATUS_LOCK_NOT_GRANTED)) return False;
+ }
+
+ cli_setpid(cli, 1);
+
+ if (!cli_close(cli, fnum1)) {
+ printf("close1 failed (%s)\n", cli_errstr(cli));
+ return False;
+ }
+
+ if (!cli_close(cli, fnum2)) {
+ printf("close2 failed (%s)\n", cli_errstr(cli));
+ return False;
+ }
+
+ if (!cli_close(cli, fnum3)) {
+ printf("close3 failed (%s)\n", cli_errstr(cli));
+ return False;
+ }
+
+ if (!torture_close_connection(cli)) {
+ correct = False;
+ }
+
+ printf("locktest2 finished\n");
+
+ return correct;
+}
+
+
+/*
+ This test checks that
+
+ 1) the server supports the full offset range in lock requests
+*/
+static BOOL run_locktest3(int dummy)
+{
+ static struct cli_state *cli1, *cli2;
+ const char *fname = "\\lockt3.lck";
+ int fnum1, fnum2, i;
+ uint32 offset;
+ BOOL correct = True;
+
+#define NEXT_OFFSET offset += (~(uint32)0) / torture_numops
+
+ if (!torture_open_connection(&cli1) || !torture_open_connection(&cli2)) {
+ return False;
+ }
+ cli_sockopt(cli1, sockops);
+ cli_sockopt(cli2, sockops);
+
+ printf("starting locktest3\n");
+
+ cli_unlink(cli1, fname);
+
+ fnum1 = cli_open(cli1, fname, O_RDWR|O_CREAT|O_EXCL, DENY_NONE);
+ if (fnum1 == -1) {
+ printf("open of %s failed (%s)\n", fname, cli_errstr(cli1));
+ return False;
+ }
+ fnum2 = cli_open(cli2, fname, O_RDWR, DENY_NONE);
+ if (fnum2 == -1) {
+ printf("open2 of %s failed (%s)\n", fname, cli_errstr(cli2));
+ return False;
+ }
+
+ for (offset=i=0;i<torture_numops;i++) {
+ NEXT_OFFSET;
+ if (!cli_lock(cli1, fnum1, offset-1, 1, 0, WRITE_LOCK)) {
+ printf("lock1 %d failed (%s)\n",
+ i,
+ cli_errstr(cli1));
+ return False;
+ }
+
+ if (!cli_lock(cli2, fnum2, offset-2, 1, 0, WRITE_LOCK)) {
+ printf("lock2 %d failed (%s)\n",
+ i,
+ cli_errstr(cli1));
+ return False;
+ }
+ }
+
+ for (offset=i=0;i<torture_numops;i++) {
+ NEXT_OFFSET;
+
+ if (cli_lock(cli1, fnum1, offset-2, 1, 0, WRITE_LOCK)) {
+ printf("error: lock1 %d succeeded!\n", i);
+ return False;
+ }
+
+ if (cli_lock(cli2, fnum2, offset-1, 1, 0, WRITE_LOCK)) {
+ printf("error: lock2 %d succeeded!\n", i);
+ return False;
+ }
+
+ if (cli_lock(cli1, fnum1, offset-1, 1, 0, WRITE_LOCK)) {
+ printf("error: lock3 %d succeeded!\n", i);
+ return False;
+ }
+
+ if (cli_lock(cli2, fnum2, offset-2, 1, 0, WRITE_LOCK)) {
+ printf("error: lock4 %d succeeded!\n", i);
+ return False;
+ }
+ }
+
+ for (offset=i=0;i<torture_numops;i++) {
+ NEXT_OFFSET;
+
+ if (!cli_unlock(cli1, fnum1, offset-1, 1)) {
+ printf("unlock1 %d failed (%s)\n",
+ i,
+ cli_errstr(cli1));
+ return False;
+ }
+
+ if (!cli_unlock(cli2, fnum2, offset-2, 1)) {
+ printf("unlock2 %d failed (%s)\n",
+ i,
+ cli_errstr(cli1));
+ return False;
+ }
+ }
+
+ if (!cli_close(cli1, fnum1)) {
+ printf("close1 failed (%s)\n", cli_errstr(cli1));
+ return False;
+ }
+
+ if (!cli_close(cli2, fnum2)) {
+ printf("close2 failed (%s)\n", cli_errstr(cli2));
+ return False;
+ }
+
+ if (!cli_unlink(cli1, fname)) {
+ printf("unlink failed (%s)\n", cli_errstr(cli1));
+ return False;
+ }
+
+ if (!torture_close_connection(cli1)) {
+ correct = False;
+ }
+
+ if (!torture_close_connection(cli2)) {
+ correct = False;
+ }
+
+ printf("finished locktest3\n");
+
+ return correct;
+}
+
+#define EXPECTED(ret, v) if ((ret) != (v)) { \
+ printf("** "); correct = False; \
+ }
+
+/*
+ looks at overlapping locks
+*/
+static BOOL run_locktest4(int dummy)
+{
+ static struct cli_state *cli1, *cli2;
+ const char *fname = "\\lockt4.lck";
+ int fnum1, fnum2, f;
+ BOOL ret;
+ char buf[1000];
+ BOOL correct = True;
+
+ if (!torture_open_connection(&cli1) || !torture_open_connection(&cli2)) {
+ return False;
+ }
+
+ cli_sockopt(cli1, sockops);
+ cli_sockopt(cli2, sockops);
+
+ printf("starting locktest4\n");
+
+ cli_unlink(cli1, fname);
+
+ fnum1 = cli_open(cli1, fname, O_RDWR|O_CREAT|O_EXCL, DENY_NONE);
+ fnum2 = cli_open(cli2, fname, O_RDWR, DENY_NONE);
+
+ memset(buf, 0, sizeof(buf));
+
+ if (cli_write(cli1, fnum1, 0, buf, 0, sizeof(buf)) != sizeof(buf)) {
+ printf("Failed to create file\n");
+ correct = False;
+ goto fail;
+ }
+
+ ret = cli_lock(cli1, fnum1, 0, 4, 0, WRITE_LOCK) &&
+ cli_lock(cli1, fnum1, 2, 4, 0, WRITE_LOCK);
+ EXPECTED(ret, False);
+ printf("the same process %s set overlapping write locks\n", ret?"can":"cannot");
+
+ ret = cli_lock(cli1, fnum1, 10, 4, 0, READ_LOCK) &&
+ cli_lock(cli1, fnum1, 12, 4, 0, READ_LOCK);
+ EXPECTED(ret, True);
+ printf("the same process %s set overlapping read locks\n", ret?"can":"cannot");
+
+ ret = cli_lock(cli1, fnum1, 20, 4, 0, WRITE_LOCK) &&
+ cli_lock(cli2, fnum2, 22, 4, 0, WRITE_LOCK);
+ EXPECTED(ret, False);
+ printf("a different connection %s set overlapping write locks\n", ret?"can":"cannot");
+
+ ret = cli_lock(cli1, fnum1, 30, 4, 0, READ_LOCK) &&
+ cli_lock(cli2, fnum2, 32, 4, 0, READ_LOCK);
+ EXPECTED(ret, True);
+ printf("a different connection %s set overlapping read locks\n", ret?"can":"cannot");
+
+ ret = (cli_setpid(cli1, 1), cli_lock(cli1, fnum1, 40, 4, 0, WRITE_LOCK)) &&
+ (cli_setpid(cli1, 2), cli_lock(cli1, fnum1, 42, 4, 0, WRITE_LOCK));
+ EXPECTED(ret, False);
+ printf("a different pid %s set overlapping write locks\n", ret?"can":"cannot");
+
+ ret = (cli_setpid(cli1, 1), cli_lock(cli1, fnum1, 50, 4, 0, READ_LOCK)) &&
+ (cli_setpid(cli1, 2), cli_lock(cli1, fnum1, 52, 4, 0, READ_LOCK));
+ EXPECTED(ret, True);
+ printf("a different pid %s set overlapping read locks\n", ret?"can":"cannot");
+
+ ret = cli_lock(cli1, fnum1, 60, 4, 0, READ_LOCK) &&
+ cli_lock(cli1, fnum1, 60, 4, 0, READ_LOCK);
+ EXPECTED(ret, True);
+ printf("the same process %s set the same read lock twice\n", ret?"can":"cannot");
+
+ ret = cli_lock(cli1, fnum1, 70, 4, 0, WRITE_LOCK) &&
+ cli_lock(cli1, fnum1, 70, 4, 0, WRITE_LOCK);
+ EXPECTED(ret, False);
+ printf("the same process %s set the same write lock twice\n", ret?"can":"cannot");
+
+ ret = cli_lock(cli1, fnum1, 80, 4, 0, READ_LOCK) &&
+ cli_lock(cli1, fnum1, 80, 4, 0, WRITE_LOCK);
+ EXPECTED(ret, False);
+ printf("the same process %s overlay a read lock with a write lock\n", ret?"can":"cannot");
+
+ ret = cli_lock(cli1, fnum1, 90, 4, 0, WRITE_LOCK) &&
+ cli_lock(cli1, fnum1, 90, 4, 0, READ_LOCK);
+ EXPECTED(ret, True);
+ printf("the same process %s overlay a write lock with a read lock\n", ret?"can":"cannot");
+
+ ret = (cli_setpid(cli1, 1), cli_lock(cli1, fnum1, 100, 4, 0, WRITE_LOCK)) &&
+ (cli_setpid(cli1, 2), cli_lock(cli1, fnum1, 100, 4, 0, READ_LOCK));
+ EXPECTED(ret, False);
+ printf("a different pid %s overlay a write lock with a read lock\n", ret?"can":"cannot");
+
+ ret = cli_lock(cli1, fnum1, 110, 4, 0, READ_LOCK) &&
+ cli_lock(cli1, fnum1, 112, 4, 0, READ_LOCK) &&
+ cli_unlock(cli1, fnum1, 110, 6);
+ EXPECTED(ret, False);
+ printf("the same process %s coalesce read locks\n", ret?"can":"cannot");
+
+
+ ret = cli_lock(cli1, fnum1, 120, 4, 0, WRITE_LOCK) &&
+ (cli_read(cli2, fnum2, buf, 120, 4) == 4);
+ EXPECTED(ret, False);
+ printf("this server %s strict write locking\n", ret?"doesn't do":"does");
+
+ ret = cli_lock(cli1, fnum1, 130, 4, 0, READ_LOCK) &&
+ (cli_write(cli2, fnum2, 0, buf, 130, 4) == 4);
+ EXPECTED(ret, False);
+ printf("this server %s strict read locking\n", ret?"doesn't do":"does");
+
+
+ ret = cli_lock(cli1, fnum1, 140, 4, 0, READ_LOCK) &&
+ cli_lock(cli1, fnum1, 140, 4, 0, READ_LOCK) &&
+ cli_unlock(cli1, fnum1, 140, 4) &&
+ cli_unlock(cli1, fnum1, 140, 4);
+ EXPECTED(ret, True);
+ printf("this server %s do recursive read locking\n", ret?"does":"doesn't");
+
+
+ ret = cli_lock(cli1, fnum1, 150, 4, 0, WRITE_LOCK) &&
+ cli_lock(cli1, fnum1, 150, 4, 0, READ_LOCK) &&
+ cli_unlock(cli1, fnum1, 150, 4) &&
+ (cli_read(cli2, fnum2, buf, 150, 4) == 4) &&
+ !(cli_write(cli2, fnum2, 0, buf, 150, 4) == 4) &&
+ cli_unlock(cli1, fnum1, 150, 4);
+ EXPECTED(ret, True);
+ printf("this server %s do recursive lock overlays\n", ret?"does":"doesn't");
+
+ ret = cli_lock(cli1, fnum1, 160, 4, 0, READ_LOCK) &&
+ cli_unlock(cli1, fnum1, 160, 4) &&
+ (cli_write(cli2, fnum2, 0, buf, 160, 4) == 4) &&
+ (cli_read(cli2, fnum2, buf, 160, 4) == 4);
+ EXPECTED(ret, True);
+ printf("the same process %s remove a read lock using write locking\n", ret?"can":"cannot");
+
+ ret = cli_lock(cli1, fnum1, 170, 4, 0, WRITE_LOCK) &&
+ cli_unlock(cli1, fnum1, 170, 4) &&
+ (cli_write(cli2, fnum2, 0, buf, 170, 4) == 4) &&
+ (cli_read(cli2, fnum2, buf, 170, 4) == 4);
+ EXPECTED(ret, True);
+ printf("the same process %s remove a write lock using read locking\n", ret?"can":"cannot");
+
+ ret = cli_lock(cli1, fnum1, 190, 4, 0, WRITE_LOCK) &&
+ cli_lock(cli1, fnum1, 190, 4, 0, READ_LOCK) &&
+ cli_unlock(cli1, fnum1, 190, 4) &&
+ !(cli_write(cli2, fnum2, 0, buf, 190, 4) == 4) &&
+ (cli_read(cli2, fnum2, buf, 190, 4) == 4);
+ EXPECTED(ret, True);
+ printf("the same process %s remove the first lock first\n", ret?"does":"doesn't");
+
+ cli_close(cli1, fnum1);
+ cli_close(cli2, fnum2);
+ fnum1 = cli_open(cli1, fname, O_RDWR, DENY_NONE);
+ f = cli_open(cli1, fname, O_RDWR, DENY_NONE);
+ ret = cli_lock(cli1, fnum1, 0, 8, 0, READ_LOCK) &&
+ cli_lock(cli1, f, 0, 1, 0, READ_LOCK) &&
+ cli_close(cli1, fnum1) &&
+ ((fnum1 = cli_open(cli1, fname, O_RDWR, DENY_NONE)) != -1) &&
+ cli_lock(cli1, fnum1, 7, 1, 0, WRITE_LOCK);
+ cli_close(cli1, f);
+ cli_close(cli1, fnum1);
+ EXPECTED(ret, True);
+ printf("the server %s have the NT byte range lock bug\n", !ret?"does":"doesn't");
+
+ fail:
+ cli_close(cli1, fnum1);
+ cli_close(cli2, fnum2);
+ cli_unlink(cli1, fname);
+ torture_close_connection(cli1);
+ torture_close_connection(cli2);
+
+ printf("finished locktest4\n");
+ return correct;
+}
+
+/*
+ looks at lock upgrade/downgrade.
+*/
+static BOOL run_locktest5(int dummy)
+{
+ static struct cli_state *cli1, *cli2;
+ const char *fname = "\\lockt5.lck";
+ int fnum1, fnum2, fnum3;
+ BOOL ret;
+ char buf[1000];
+ BOOL correct = True;
+
+ if (!torture_open_connection(&cli1) || !torture_open_connection(&cli2)) {
+ return False;
+ }
+
+ cli_sockopt(cli1, sockops);
+ cli_sockopt(cli2, sockops);
+
+ printf("starting locktest5\n");
+
+ cli_unlink(cli1, fname);
+
+ fnum1 = cli_open(cli1, fname, O_RDWR|O_CREAT|O_EXCL, DENY_NONE);
+ fnum2 = cli_open(cli2, fname, O_RDWR, DENY_NONE);
+ fnum3 = cli_open(cli1, fname, O_RDWR, DENY_NONE);
+
+ memset(buf, 0, sizeof(buf));
+
+ if (cli_write(cli1, fnum1, 0, buf, 0, sizeof(buf)) != sizeof(buf)) {
+ printf("Failed to create file\n");
+ correct = False;
+ goto fail;
+ }
+
+ /* Check for NT bug... */
+ ret = cli_lock(cli1, fnum1, 0, 8, 0, READ_LOCK) &&
+ cli_lock(cli1, fnum3, 0, 1, 0, READ_LOCK);
+ cli_close(cli1, fnum1);
+ fnum1 = cli_open(cli1, fname, O_RDWR, DENY_NONE);
+ ret = cli_lock(cli1, fnum1, 7, 1, 0, WRITE_LOCK);
+ EXPECTED(ret, True);
+ printf("this server %s the NT locking bug\n", ret ? "doesn't have" : "has");
+ cli_close(cli1, fnum1);
+ fnum1 = cli_open(cli1, fname, O_RDWR, DENY_NONE);
+ cli_unlock(cli1, fnum3, 0, 1);
+
+ ret = cli_lock(cli1, fnum1, 0, 4, 0, WRITE_LOCK) &&
+ cli_lock(cli1, fnum1, 1, 1, 0, READ_LOCK);
+ EXPECTED(ret, True);
+ printf("the same process %s overlay a write with a read lock\n", ret?"can":"cannot");
+
+ ret = cli_lock(cli2, fnum2, 0, 4, 0, READ_LOCK);
+ EXPECTED(ret, False);
+
+ printf("a different processs %s get a read lock on the first process lock stack\n", ret?"can":"cannot");
+
+ /* Unlock the process 2 lock. */
+ cli_unlock(cli2, fnum2, 0, 4);
+
+ ret = cli_lock(cli1, fnum3, 0, 4, 0, READ_LOCK);
+ EXPECTED(ret, False);
+
+ printf("the same processs on a different fnum %s get a read lock\n", ret?"can":"cannot");
+
+ /* Unlock the process 1 fnum3 lock. */
+ cli_unlock(cli1, fnum3, 0, 4);
+
+ /* Stack 2 more locks here. */
+ ret = cli_lock(cli1, fnum1, 0, 4, 0, READ_LOCK) &&
+ cli_lock(cli1, fnum1, 0, 4, 0, READ_LOCK);
+
+ EXPECTED(ret, True);
+ printf("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 = cli_unlock(cli1, fnum1, 0, 4) &&
+ cli_lock(cli2, fnum2, 0, 4, 0, READ_LOCK);
+
+ EXPECTED(ret, True);
+ printf("the first unlock removes the %s lock\n", ret?"WRITE":"READ");
+
+ /* Unlock the process 2 lock. */
+ cli_unlock(cli2, fnum2, 0, 4);
+
+ /* We should have 3 stacked locks here. Ensure we need to do 3 unlocks. */
+
+ ret = cli_unlock(cli1, fnum1, 1, 1) &&
+ cli_unlock(cli1, fnum1, 0, 4) &&
+ cli_unlock(cli1, fnum1, 0, 4);
+
+ EXPECTED(ret, True);
+ printf("the same process %s unlock the stack of 4 locks\n", ret?"can":"cannot");
+
+ /* Ensure the next unlock fails. */
+ ret = cli_unlock(cli1, fnum1, 0, 4);
+ EXPECTED(ret, False);
+ printf("the same process %s count the lock stack\n", !ret?"can":"cannot");
+
+ /* Ensure connection 2 can get a write lock. */
+ ret = cli_lock(cli2, fnum2, 0, 4, 0, WRITE_LOCK);
+ EXPECTED(ret, True);
+
+ printf("a different processs %s get a write lock on the unlocked stack\n", ret?"can":"cannot");
+
+
+ fail:
+ cli_close(cli1, fnum1);
+ cli_close(cli2, fnum2);
+ cli_unlink(cli1, fname);
+ if (!torture_close_connection(cli1)) {
+ correct = False;
+ }
+ if (!torture_close_connection(cli2)) {
+ correct = False;
+ }
+
+ printf("finished locktest5\n");
+
+ return correct;
+}
+
+/*
+ tries the unusual lockingX locktype bits
+*/
+static BOOL run_locktest6(int dummy)
+{
+ static struct cli_state *cli;
+ const char *fname[1] = { "\\lock6.txt" };
+ int i;
+ int fnum;
+ NTSTATUS status;
+
+ if (!torture_open_connection(&cli)) {
+ return False;
+ }
+
+ cli_sockopt(cli, sockops);
+
+ printf("starting locktest6\n");
+
+ for (i=0;i<1;i++) {
+ printf("Testing %s\n", fname[i]);
+
+ cli_unlink(cli, fname[i]);
+
+ fnum = cli_open(cli, fname[i], O_RDWR|O_CREAT|O_EXCL, DENY_NONE);
+ status = cli_locktype(cli, fnum, 0, 8, 0, LOCKING_ANDX_CHANGE_LOCKTYPE);
+ cli_close(cli, fnum);
+ printf("CHANGE_LOCKTYPE gave %s\n", nt_errstr(status));
+
+ fnum = cli_open(cli, fname[i], O_RDWR, DENY_NONE);
+ status = cli_locktype(cli, fnum, 0, 8, 0, LOCKING_ANDX_CANCEL_LOCK);
+ cli_close(cli, fnum);
+ printf("CANCEL_LOCK gave %s\n", nt_errstr(status));
+
+ cli_unlink(cli, fname[i]);
+ }
+
+ torture_close_connection(cli);
+
+ printf("finished locktest6\n");
+ return True;
+}
+
+static BOOL run_locktest7(int dummy)
+{
+ struct cli_state *cli1;
+ const char *fname = "\\lockt7.lck";
+ int fnum1;
+ char buf[200];
+ BOOL correct = False;
+
+ if (!torture_open_connection(&cli1)) {
+ return False;
+ }
+
+ cli_sockopt(cli1, sockops);
+
+ printf("starting locktest7\n");
+
+ cli_unlink(cli1, fname);
+
+ fnum1 = cli_open(cli1, fname, O_RDWR|O_CREAT|O_EXCL, DENY_NONE);
+
+ memset(buf, 0, sizeof(buf));
+
+ if (cli_write(cli1, fnum1, 0, buf, 0, sizeof(buf)) != sizeof(buf)) {
+ printf("Failed to create file\n");
+ goto fail;
+ }
+
+ cli_setpid(cli1, 1);
+
+ if (!cli_lock(cli1, fnum1, 130, 4, 0, READ_LOCK)) {
+ printf("Unable to apply read lock on range 130:4, error was %s\n", cli_errstr(cli1));
+ goto fail;
+ } else {
+ printf("pid1 successfully locked range 130:4 for READ\n");
+ }
+
+ if (cli_read(cli1, fnum1, buf, 130, 4) != 4) {
+ printf("pid1 unable to read the range 130:4, error was %s\n", cli_errstr(cli1));
+ goto fail;
+ } else {
+ printf("pid1 successfully read the range 130:4\n");
+ }
+
+ if (cli_write(cli1, fnum1, 0, buf, 130, 4) != 4) {
+ printf("pid1 unable to write to the range 130:4, error was %s\n", cli_errstr(cli1));
+ if (NT_STATUS_V(cli_nt_error(cli1)) != NT_STATUS_V(NT_STATUS_FILE_LOCK_CONFLICT)) {
+ printf("Incorrect error (should be NT_STATUS_FILE_LOCK_CONFLICT)\n");
+ goto fail;
+ }
+ } else {
+ printf("pid1 successfully wrote to the range 130:4 (should be denied)\n");
+ goto fail;
+ }
+
+ cli_setpid(cli1, 2);
+
+ if (cli_read(cli1, fnum1, buf, 130, 4) != 4) {
+ printf("pid2 unable to read the range 130:4, error was %s\n", cli_errstr(cli1));
+ } else {
+ printf("pid2 successfully read the range 130:4\n");
+ }
+
+ if (cli_write(cli1, fnum1, 0, buf, 130, 4) != 4) {
+ printf("pid2 unable to write to the range 130:4, error was %s\n", cli_errstr(cli1));
+ if (NT_STATUS_V(cli_nt_error(cli1)) != NT_STATUS_V(NT_STATUS_FILE_LOCK_CONFLICT)) {
+ printf("Incorrect error (should be NT_STATUS_FILE_LOCK_CONFLICT)\n");
+ goto fail;
+ }
+ } else {
+ printf("pid2 successfully wrote to the range 130:4 (should be denied)\n");
+ goto fail;
+ }
+
+ cli_setpid(cli1, 1);
+ cli_unlock(cli1, fnum1, 130, 4);
+
+ if (!cli_lock(cli1, fnum1, 130, 4, 0, WRITE_LOCK)) {
+ printf("Unable to apply write lock on range 130:4, error was %s\n", cli_errstr(cli1));
+ goto fail;
+ } else {
+ printf("pid1 successfully locked range 130:4 for WRITE\n");
+ }
+
+ if (cli_read(cli1, fnum1, buf, 130, 4) != 4) {
+ printf("pid1 unable to read the range 130:4, error was %s\n", cli_errstr(cli1));
+ goto fail;
+ } else {
+ printf("pid1 successfully read the range 130:4\n");
+ }
+
+ if (cli_write(cli1, fnum1, 0, buf, 130, 4) != 4) {
+ printf("pid1 unable to write to the range 130:4, error was %s\n", cli_errstr(cli1));
+ goto fail;
+ } else {
+ printf("pid1 successfully wrote to the range 130:4\n");
+ }
+
+ cli_setpid(cli1, 2);
+
+ if (cli_read(cli1, fnum1, buf, 130, 4) != 4) {
+ printf("pid2 unable to read the range 130:4, error was %s\n", cli_errstr(cli1));
+ if (NT_STATUS_V(cli_nt_error(cli1)) != NT_STATUS_V(NT_STATUS_FILE_LOCK_CONFLICT)) {
+ printf("Incorrect error (should be NT_STATUS_FILE_LOCK_CONFLICT)\n");
+ goto fail;
+ }
+ } else {
+ printf("pid2 successfully read the range 130:4 (should be denied)\n");
+ goto fail;
+ }
+
+ if (cli_write(cli1, fnum1, 0, buf, 130, 4) != 4) {
+ printf("pid2 unable to write to the range 130:4, error was %s\n", cli_errstr(cli1));
+ if (NT_STATUS_V(cli_nt_error(cli1)) != NT_STATUS_V(NT_STATUS_FILE_LOCK_CONFLICT)) {
+ printf("Incorrect error (should be NT_STATUS_FILE_LOCK_CONFLICT)\n");
+ goto fail;
+ }
+ } else {
+ printf("pid2 successfully wrote to the range 130:4 (should be denied)\n");
+ goto fail;
+ }
+
+ cli_unlock(cli1, fnum1, 130, 0);
+ correct = True;
+
+fail:
+ cli_close(cli1, fnum1);
+ cli_unlink(cli1, fname);
+ torture_close_connection(cli1);
+
+ printf("finished locktest7\n");
+ return correct;
+}
+
+/*
+test whether fnums and tids open on one VC are available on another (a major
+security hole)
+*/
+static BOOL run_fdpasstest(int dummy)
+{
+ struct cli_state *cli1, *cli2;
+ const char *fname = "\\fdpass.tst";
+ int fnum1;
+ pstring buf;
+
+ if (!torture_open_connection(&cli1) || !torture_open_connection(&cli2)) {
+ return False;
+ }
+ cli_sockopt(cli1, sockops);
+ cli_sockopt(cli2, sockops);
+
+ printf("starting fdpasstest\n");
+
+ cli_unlink(cli1, fname);
+
+ fnum1 = cli_open(cli1, fname, O_RDWR|O_CREAT|O_EXCL, DENY_NONE);
+ if (fnum1 == -1) {
+ printf("open of %s failed (%s)\n", fname, cli_errstr(cli1));
+ return False;
+ }
+
+ if (cli_write(cli1, fnum1, 0, "hello world\n", 0, 13) != 13) {
+ printf("write failed (%s)\n", cli_errstr(cli1));
+ return False;
+ }
+
+ cli2->vuid = cli1->vuid;
+ cli2->cnum = cli1->cnum;
+ cli2->pid = cli1->pid;
+
+ if (cli_read(cli2, fnum1, buf, 0, 13) == 13) {
+ printf("read succeeded! nasty security hole [%s]\n",
+ buf);
+ return False;
+ }
+
+ cli_close(cli1, fnum1);
+ cli_unlink(cli1, fname);
+
+ torture_close_connection(cli1);
+ torture_close_connection(cli2);
+
+ printf("finished fdpasstest\n");
+ return True;
+}
+
+static BOOL run_fdsesstest(int dummy)
+{
+ struct cli_state *cli;
+ uint16 new_vuid;
+ uint16 saved_vuid;
+ uint16 new_cnum;
+ uint16 saved_cnum;
+ const char *fname = "\\fdsess.tst";
+ const char *fname1 = "\\fdsess1.tst";
+ int fnum1;
+ int fnum2;
+ pstring buf;
+ BOOL ret = True;
+
+ if (!torture_open_connection(&cli))
+ return False;
+ cli_sockopt(cli, sockops);
+
+ if (!torture_cli_session_setup2(cli, &new_vuid))
+ return False;
+
+ saved_cnum = cli->cnum;
+ if (!cli_send_tconX(cli, share, "?????", "", 1))
+ return False;
+ new_cnum = cli->cnum;
+ cli->cnum = saved_cnum;
+
+ printf("starting fdsesstest\n");
+
+ cli_unlink(cli, fname);
+ cli_unlink(cli, fname1);
+
+ fnum1 = cli_open(cli, fname, O_RDWR|O_CREAT|O_EXCL, DENY_NONE);
+ if (fnum1 == -1) {
+ printf("open of %s failed (%s)\n", fname, cli_errstr(cli));
+ return False;
+ }
+
+ if (cli_write(cli, fnum1, 0, "hello world\n", 0, 13) != 13) {
+ printf("write failed (%s)\n", cli_errstr(cli));
+ return False;
+ }
+
+ saved_vuid = cli->vuid;
+ cli->vuid = new_vuid;
+
+ if (cli_read(cli, fnum1, buf, 0, 13) == 13) {
+ printf("read succeeded with different vuid! nasty security hole [%s]\n",
+ buf);
+ ret = False;
+ }
+ /* Try to open a file with different vuid, samba cnum. */
+ fnum2 = cli_open(cli, fname1, O_RDWR|O_CREAT|O_EXCL, DENY_NONE);
+ if (fnum2 != -1) {
+ printf("create with different vuid, same cnum succeeded.\n");
+ cli_close(cli, fnum2);
+ cli_unlink(cli, fname1);
+ } else {
+ printf("create with different vuid, same cnum failed.\n");
+ printf("This will cause problems with service clients.\n");
+ ret = False;
+ }
+
+ cli->vuid = saved_vuid;
+
+ /* Try with same vuid, different cnum. */
+ cli->cnum = new_cnum;
+
+ if (cli_read(cli, fnum1, buf, 0, 13) == 13) {
+ printf("read succeeded with different cnum![%s]\n",
+ buf);
+ ret = False;
+ }
+
+ cli->cnum = saved_cnum;
+ cli_close(cli, fnum1);
+ cli_unlink(cli, fname);
+
+ torture_close_connection(cli);
+
+ printf("finished fdsesstest\n");
+ return ret;
+}
+
+/*
+ This test checks that
+
+ 1) the server does not allow an unlink on a file that is open
+*/
+static BOOL run_unlinktest(int dummy)
+{
+ struct cli_state *cli;
+ const char *fname = "\\unlink.tst";
+ int fnum;
+ BOOL correct = True;
+
+ if (!torture_open_connection(&cli)) {
+ return False;
+ }
+
+ cli_sockopt(cli, sockops);
+
+ printf("starting unlink test\n");
+
+ cli_unlink(cli, fname);
+
+ cli_setpid(cli, 1);
+
+ fnum = cli_open(cli, fname, O_RDWR|O_CREAT|O_EXCL, DENY_NONE);
+ if (fnum == -1) {
+ printf("open of %s failed (%s)\n", fname, cli_errstr(cli));
+ return False;
+ }
+
+ if (cli_unlink(cli, fname)) {
+ printf("error: server allowed unlink on an open file\n");
+ correct = False;
+ } else {
+ correct = check_error(__LINE__, cli, ERRDOS, ERRbadshare,
+ NT_STATUS_SHARING_VIOLATION);
+ }
+
+ cli_close(cli, fnum);
+ cli_unlink(cli, fname);
+
+ if (!torture_close_connection(cli)) {
+ correct = False;
+ }
+
+ printf("unlink test finished\n");
+
+ return correct;
+}
+
+
+/*
+test how many open files this server supports on the one socket
+*/
+static BOOL run_maxfidtest(int dummy)
+{
+ struct cli_state *cli;
+ const char *template = "\\maxfid.%d.%d";
+ fstring fname;
+ int fnums[0x11000], i;
+ int retries=4;
+ BOOL correct = True;
+
+ cli = current_cli;
+
+ if (retries <= 0) {
+ printf("failed to connect\n");
+ return False;
+ }
+
+ cli_sockopt(cli, sockops);
+
+ for (i=0; i<0x11000; i++) {
+ slprintf(fname,sizeof(fname)-1,template, i,(int)getpid());
+ if ((fnums[i] = cli_open(cli, fname,
+ O_RDWR|O_CREAT|O_TRUNC, DENY_NONE)) ==
+ -1) {
+ printf("open of %s failed (%s)\n",
+ fname, cli_errstr(cli));
+ printf("maximum fnum is %d\n", i);
+ break;
+ }
+ printf("%6d\r", i);
+ }
+ printf("%6d\n", i);
+ i--;
+
+ printf("cleaning up\n");
+ for (;i>=0;i--) {
+ slprintf(fname,sizeof(fname)-1,template, i,(int)getpid());
+ cli_close(cli, fnums[i]);
+ if (!cli_unlink(cli, fname)) {
+ printf("unlink of %s failed (%s)\n",
+ fname, cli_errstr(cli));
+ correct = False;
+ }
+ printf("%6d\r", i);
+ }
+ printf("%6d\n", 0);
+
+ printf("maxfid test finished\n");
+ if (!torture_close_connection(cli)) {
+ correct = False;
+ }
+ return correct;
+}
+
+/* generate a random buffer */
+static void rand_buf(char *buf, int len)
+{
+ while (len--) {
+ *buf = (char)sys_random();
+ buf++;
+ }
+}
+
+/* send smb negprot commands, not reading the response */
+static BOOL run_negprot_nowait(int dummy)
+{
+ int i;
+ static struct cli_state cli;
+ BOOL correct = True;
+
+ printf("starting negprot nowait test\n");
+
+ if (!open_nbt_connection(&cli)) {
+ return False;
+ }
+
+ for (i=0;i<50000;i++) {
+ cli_negprot_send(&cli);
+ }
+
+ if (!torture_close_connection(&cli)) {
+ correct = False;
+ }
+
+ printf("finished negprot nowait test\n");
+
+ return correct;
+}
+
+
+/* send random IPC commands */
+static BOOL run_randomipc(int dummy)
+{
+ char *rparam = NULL;
+ char *rdata = NULL;
+ int rdrcnt,rprcnt;
+ pstring param;
+ int api, param_len, i;
+ struct cli_state *cli;
+ BOOL correct = True;
+ int count = 50000;
+
+ printf("starting random ipc test\n");
+
+ if (!torture_open_connection(&cli)) {
+ return False;
+ }
+
+ for (i=0;i<count;i++) {
+ api = sys_random() % 500;
+ param_len = (sys_random() % 64);
+
+ rand_buf(param, param_len);
+
+ SSVAL(param,0,api);
+
+ cli_api(cli,
+ param, param_len, 8,
+ NULL, 0, BUFFER_SIZE,
+ &rparam, &rprcnt,
+ &rdata, &rdrcnt);
+ if (i % 100 == 0) {
+ printf("%d/%d\r", i,count);
+ }
+ }
+ printf("%d/%d\n", i, count);
+
+ if (!torture_close_connection(cli)) {
+ correct = False;
+ }
+
+ printf("finished random ipc test\n");
+
+ return correct;
+}
+
+
+
+static void browse_callback(const char *sname, uint32 stype,
+ const char *comment, void *state)
+{
+ printf("\t%20.20s %08x %s\n", sname, stype, comment);
+}
+
+
+
+/*
+ This test checks the browse list code
+
+*/
+static BOOL run_browsetest(int dummy)
+{
+ static struct cli_state *cli;
+ BOOL correct = True;
+
+ printf("starting browse test\n");
+
+ if (!torture_open_connection(&cli)) {
+ return False;
+ }
+
+ printf("domain list:\n");
+ cli_NetServerEnum(cli, cli->server_domain,
+ SV_TYPE_DOMAIN_ENUM,
+ browse_callback, NULL);
+
+ printf("machine list:\n");
+ cli_NetServerEnum(cli, cli->server_domain,
+ SV_TYPE_ALL,
+ browse_callback, NULL);
+
+ if (!torture_close_connection(cli)) {
+ correct = False;
+ }
+
+ printf("browse test finished\n");
+
+ return correct;
+
+}
+
+
+/*
+ This checks how the getatr calls works
+*/
+static BOOL run_attrtest(int dummy)
+{
+ struct cli_state *cli;
+ int fnum;
+ time_t t, t2;
+ const char *fname = "\\attrib123456789.tst";
+ BOOL correct = True;
+
+ printf("starting attrib test\n");
+
+ if (!torture_open_connection(&cli)) {
+ return False;
+ }
+
+ cli_unlink(cli, fname);
+ fnum = cli_open(cli, fname,
+ O_RDWR | O_CREAT | O_TRUNC, DENY_NONE);
+ cli_close(cli, fnum);
+ if (!cli_getatr(cli, fname, NULL, NULL, &t)) {
+ printf("getatr failed (%s)\n", cli_errstr(cli));
+ correct = False;
+ }
+
+ if (abs(t - time(NULL)) > 60*60*24*10) {
+ printf("ERROR: SMBgetatr bug. time is %s",
+ ctime(&t));
+ t = time(NULL);
+ correct = True;
+ }
+
+ t2 = t-60*60*24; /* 1 day ago */
+
+ if (!cli_setatr(cli, fname, 0, t2)) {
+ printf("setatr failed (%s)\n", cli_errstr(cli));
+ correct = True;
+ }
+
+ if (!cli_getatr(cli, fname, NULL, NULL, &t)) {
+ printf("getatr failed (%s)\n", cli_errstr(cli));
+ correct = True;
+ }
+
+ if (t != t2) {
+ printf("ERROR: getatr/setatr bug. times are\n%s",
+ ctime(&t));
+ printf("%s", ctime(&t2));
+ correct = True;
+ }
+
+ cli_unlink(cli, fname);
+
+ if (!torture_close_connection(cli)) {
+ correct = False;
+ }
+
+ printf("attrib test finished\n");
+
+ return correct;
+}
+
+
+/*
+ This checks a couple of trans2 calls
+*/
+static BOOL run_trans2test(int dummy)
+{
+ struct cli_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";
+ pstring pname;
+ BOOL correct = True;
+
+ printf("starting trans2 test\n");
+
+ if (!torture_open_connection(&cli)) {
+ return False;
+ }
+
+ cli_unlink(cli, fname);
+ fnum = cli_open(cli, fname,
+ O_RDWR | O_CREAT | O_TRUNC, DENY_NONE);
+ if (!cli_qfileinfo(cli, fnum, NULL, &size, &c_time, &a_time, &m_time,
+ NULL, NULL)) {
+ printf("ERROR: qfileinfo failed (%s)\n", cli_errstr(cli));
+ correct = False;
+ }
+
+ if (!cli_qfilename(cli, fnum, pname)) {
+ printf("ERROR: qfilename failed (%s)\n", cli_errstr(cli));
+ correct = False;
+ }
+
+ if (strcmp(pname, fname)) {
+ printf("qfilename gave different name? [%s] [%s]\n",
+ fname, pname);
+ correct = False;
+ }
+
+ cli_close(cli, fnum);
+
+ sleep(2);
+
+ cli_unlink(cli, fname);
+ fnum = cli_open(cli, fname,
+ O_RDWR | O_CREAT | O_TRUNC, DENY_NONE);
+ if (fnum == -1) {
+ printf("open of %s failed (%s)\n", fname, cli_errstr(cli));
+ return False;
+ }
+ cli_close(cli, fnum);
+
+ if (!cli_qpathinfo(cli, fname, &c_time, &a_time, &m_time, &size, NULL)) {
+ printf("ERROR: qpathinfo failed (%s)\n", cli_errstr(cli));
+ correct = False;
+ } else {
+ if (c_time != m_time) {
+ printf("create time=%s", ctime(&c_time));
+ printf("modify time=%s", ctime(&m_time));
+ printf("This system appears to have sticky create times\n");
+ }
+ if (a_time % (60*60) == 0) {
+ printf("access time=%s", ctime(&a_time));
+ printf("This system appears to set a midnight access time\n");
+ correct = False;
+ }
+
+ if (abs(m_time - time(NULL)) > 60*60*24*7) {
+ printf("ERROR: totally incorrect times - maybe word reversed? mtime=%s", ctime(&m_time));
+ correct = False;
+ }
+ }
+
+
+ cli_unlink(cli, fname);
+ fnum = cli_open(cli, fname,
+ O_RDWR | O_CREAT | O_TRUNC, DENY_NONE);
+ cli_close(cli, fnum);
+ if (!cli_qpathinfo2(cli, fname, &c_time, &a_time, &m_time,
+ &w_time, &size, NULL, NULL)) {
+ printf("ERROR: qpathinfo2 failed (%s)\n", cli_errstr(cli));
+ correct = False;
+ } else {
+ if (w_time < 60*60*24*2) {
+ printf("write time=%s", ctime(&w_time));
+ printf("This system appears to set a initial 0 write time\n");
+ correct = False;
+ }
+ }
+
+ cli_unlink(cli, fname);
+
+
+ /* check if the server updates the directory modification time
+ when creating a new file */
+ if (!cli_mkdir(cli, dname)) {
+ printf("ERROR: mkdir failed (%s)\n", cli_errstr(cli));
+ correct = False;
+ }
+ sleep(3);
+ if (!cli_qpathinfo2(cli, "\\trans2\\", &c_time, &a_time, &m_time,
+ &w_time, &size, NULL, NULL)) {
+ printf("ERROR: qpathinfo2 failed (%s)\n", cli_errstr(cli));
+ correct = False;
+ }
+
+ fnum = cli_open(cli, fname2,
+ O_RDWR | O_CREAT | O_TRUNC, DENY_NONE);
+ cli_write(cli, fnum, 0, (char *)&fnum, 0, sizeof(fnum));
+ cli_close(cli, fnum);
+ if (!cli_qpathinfo2(cli, "\\trans2\\", &c_time, &a_time, &m_time2,
+ &w_time, &size, NULL, NULL)) {
+ printf("ERROR: qpathinfo2 failed (%s)\n", cli_errstr(cli));
+ correct = False;
+ } else {
+ if (m_time2 == m_time) {
+ printf("This system does not update directory modification times\n");
+ correct = False;
+ }
+ }
+ cli_unlink(cli, fname2);
+ cli_rmdir(cli, dname);
+
+ if (!torture_close_connection(cli)) {
+ correct = False;
+ }
+
+ printf("trans2 test finished\n");
+
+ return correct;
+}
+
+/*
+ This checks new W2K calls.
+*/
+
+static BOOL new_trans(struct cli_state *pcli, int fnum, int level)
+{
+ char *buf = NULL;
+ uint32 len;
+ BOOL correct = True;
+
+ if (!cli_qfileinfo_test(pcli, fnum, level, &buf, &len)) {
+ printf("ERROR: qfileinfo (%d) failed (%s)\n", level, cli_errstr(pcli));
+ correct = False;
+ } else {
+ printf("qfileinfo: level %d, len = %u\n", level, len);
+ dump_data(0, buf, len);
+ printf("\n");
+ }
+ SAFE_FREE(buf);
+ return correct;
+}
+
+static BOOL run_w2ktest(int dummy)
+{
+ struct cli_state *cli;
+ int fnum;
+ const char *fname = "\\w2ktest\\w2k.tst";
+ int level;
+ BOOL correct = True;
+
+ printf("starting w2k test\n");
+
+ if (!torture_open_connection(&cli)) {
+ return False;
+ }
+
+ fnum = cli_open(cli, fname,
+ O_RDWR | O_CREAT , DENY_NONE);
+
+ for (level = 1004; level < 1040; level++) {
+ new_trans(cli, fnum, level);
+ }
+
+ cli_close(cli, fnum);
+
+ if (!torture_close_connection(cli)) {
+ correct = False;
+ }
+
+ printf("w2k test finished\n");
+
+ return correct;
+}
+
+
+/*
+ this is a harness for some oplock tests
+ */
+static BOOL run_oplock1(int dummy)
+{
+ struct cli_state *cli1;
+ const char *fname = "\\lockt1.lck";
+ int fnum1;
+ BOOL correct = True;
+
+ printf("starting oplock test 1\n");
+
+ if (!torture_open_connection(&cli1)) {
+ return False;
+ }
+
+ cli_unlink(cli1, fname);
+
+ cli_sockopt(cli1, sockops);
+
+ cli1->use_oplocks = True;
+
+ fnum1 = cli_open(cli1, fname, O_RDWR|O_CREAT|O_EXCL, DENY_NONE);
+ if (fnum1 == -1) {
+ printf("open of %s failed (%s)\n", fname, cli_errstr(cli1));
+ return False;
+ }
+
+ cli1->use_oplocks = False;
+
+ cli_unlink(cli1, fname);
+ cli_unlink(cli1, fname);
+
+ if (!cli_close(cli1, fnum1)) {
+ printf("close2 failed (%s)\n", cli_errstr(cli1));
+ return False;
+ }
+
+ if (!cli_unlink(cli1, fname)) {
+ printf("unlink failed (%s)\n", cli_errstr(cli1));
+ return False;
+ }
+
+ if (!torture_close_connection(cli1)) {
+ correct = False;
+ }
+
+ printf("finished oplock test 1\n");
+
+ return correct;
+}
+
+static BOOL run_oplock2(int dummy)
+{
+ struct cli_state *cli1, *cli2;
+ const char *fname = "\\lockt2.lck";
+ int fnum1, fnum2;
+ int saved_use_oplocks = use_oplocks;
+ char buf[4];
+ BOOL correct = True;
+ volatile BOOL *shared_correct;
+
+ shared_correct = (volatile BOOL *)shm_setup(sizeof(BOOL));
+ *shared_correct = True;
+
+ use_level_II_oplocks = True;
+ use_oplocks = True;
+
+ printf("starting oplock test 2\n");
+
+ if (!torture_open_connection(&cli1)) {
+ use_level_II_oplocks = False;
+ use_oplocks = saved_use_oplocks;
+ return False;
+ }
+
+ cli1->use_oplocks = True;
+ cli1->use_level_II_oplocks = True;
+
+ if (!torture_open_connection(&cli2)) {
+ use_level_II_oplocks = False;
+ use_oplocks = saved_use_oplocks;
+ return False;
+ }
+
+ cli2->use_oplocks = True;
+ cli2->use_level_II_oplocks = True;
+
+ cli_unlink(cli1, fname);
+
+ cli_sockopt(cli1, sockops);
+ cli_sockopt(cli2, sockops);
+
+ fnum1 = cli_open(cli1, fname, O_RDWR|O_CREAT|O_EXCL, DENY_NONE);
+ if (fnum1 == -1) {
+ printf("open of %s failed (%s)\n", fname, cli_errstr(cli1));
+ return False;
+ }
+
+ /* Don't need the globals any more. */
+ use_level_II_oplocks = False;
+ use_oplocks = saved_use_oplocks;
+
+ if (fork() == 0) {
+ /* Child code */
+ fnum2 = cli_open(cli2, fname, O_RDWR, DENY_NONE);
+ if (fnum2 == -1) {
+ printf("second open of %s failed (%s)\n", fname, cli_errstr(cli1));
+ *shared_correct = False;
+ exit(0);
+ }
+
+ sleep(2);
+
+ if (!cli_close(cli2, fnum2)) {
+ printf("close2 failed (%s)\n", cli_errstr(cli1));
+ *shared_correct = False;
+ }
+
+ exit(0);
+ }
+
+ sleep(2);
+
+ /* Ensure cli1 processes the break. */
+
+ if (cli_read(cli1, fnum1, buf, 0, 4) != 4) {
+ printf("read on fnum1 failed (%s)\n", cli_errstr(cli1));
+ correct = False;
+ }
+
+ /* Should now be at level II. */
+ /* Test if sending a write locks causes a break to none. */
+
+ if (!cli_lock(cli1, fnum1, 0, 4, 0, READ_LOCK)) {
+ printf("lock failed (%s)\n", cli_errstr(cli1));
+ correct = False;
+ }
+
+ cli_unlock(cli1, fnum1, 0, 4);
+
+ sleep(2);
+
+ if (!cli_lock(cli1, fnum1, 0, 4, 0, WRITE_LOCK)) {
+ printf("lock failed (%s)\n", cli_errstr(cli1));
+ correct = False;
+ }
+
+ cli_unlock(cli1, fnum1, 0, 4);
+
+ sleep(2);
+
+ cli_read(cli1, fnum1, buf, 0, 4);
+
+#if 0
+ if (cli_write(cli1, fnum1, 0, buf, 0, 4) != 4) {
+ printf("write on fnum1 failed (%s)\n", cli_errstr(cli1));
+ correct = False;
+ }
+#endif
+
+ if (!cli_close(cli1, fnum1)) {
+ printf("close1 failed (%s)\n", cli_errstr(cli1));
+ correct = False;
+ }
+
+ sleep(4);
+
+ if (!cli_unlink(cli1, fname)) {
+ printf("unlink failed (%s)\n", cli_errstr(cli1));
+ correct = False;
+ }
+
+ if (!torture_close_connection(cli1)) {
+ correct = False;
+ }
+
+ if (!*shared_correct) {
+ correct = False;
+ }
+
+ printf("finished oplock test 2\n");
+
+ return correct;
+}
+
+/* handler for oplock 3 tests */
+static BOOL oplock3_handler(struct cli_state *cli, int fnum, unsigned char level)
+{
+ printf("got oplock break fnum=%d level=%d\n",
+ fnum, level);
+ return cli_oplock_ack(cli, fnum, level);
+}
+
+static BOOL run_oplock3(int dummy)
+{
+ struct cli_state *cli;
+ const char *fname = "\\oplockt3.dat";
+ int fnum;
+ char buf[4] = "abcd";
+ BOOL correct = True;
+ volatile BOOL *shared_correct;
+
+ shared_correct = (volatile BOOL *)shm_setup(sizeof(BOOL));
+ *shared_correct = True;
+
+ printf("starting oplock test 3\n");
+
+ if (fork() == 0) {
+ /* Child code */
+ use_oplocks = True;
+ use_level_II_oplocks = True;
+ if (!torture_open_connection(&cli)) {
+ *shared_correct = False;
+ exit(0);
+ }
+ sleep(2);
+ /* try to trigger a oplock break in parent */
+ fnum = cli_open(cli, fname, O_RDWR, DENY_NONE);
+ cli_write(cli, fnum, 0, buf, 0, 4);
+ exit(0);
+ }
+
+ /* parent code */
+ use_oplocks = True;
+ use_level_II_oplocks = True;
+ if (!torture_open_connection(&cli)) {
+ return False;
+ }
+ cli_oplock_handler(cli, oplock3_handler);
+ fnum = cli_open(cli, fname, O_RDWR|O_CREAT, DENY_NONE);
+ cli_write(cli, fnum, 0, buf, 0, 4);
+ cli_close(cli, fnum);
+ fnum = cli_open(cli, fname, O_RDWR, DENY_NONE);
+ cli->timeout = 20000;
+ cli_receive_smb(cli);
+ printf("finished oplock test 3\n");
+
+ return (correct && *shared_correct);
+
+/* What are we looking for here? What's sucess and what's FAILURE? */
+}
+
+
+
+/*
+ Test delete on close semantics.
+ */
+static BOOL run_deletetest(int dummy)
+{
+ struct cli_state *cli1 = NULL;
+ struct cli_state *cli2 = NULL;
+ const char *fname = "\\delete.file";
+ int fnum1 = -1;
+ int fnum2 = -1;
+ BOOL correct = True;
+
+ printf("starting delete test\n");
+
+ if (!torture_open_connection(&cli1)) {
+ return False;
+ }
+
+ cli_sockopt(cli1, sockops);
+
+ /* Test 1 - this should delete the file on close. */
+
+ cli_setatr(cli1, fname, 0, 0);
+ cli_unlink(cli1, fname);
+
+ fnum1 = cli_nt_create_full(cli1, fname, 0, GENERIC_ALL_ACCESS|DELETE_ACCESS, FILE_ATTRIBUTE_NORMAL,
+ 0, FILE_OVERWRITE_IF,
+ FILE_DELETE_ON_CLOSE, 0);
+
+ if (fnum1 == -1) {
+ printf("[1] open of %s failed (%s)\n", fname, cli_errstr(cli1));
+ correct = False;
+ goto fail;
+ }
+
+#if 0 /* JRATEST */
+ {
+ uint32 *accinfo = NULL;
+ uint32 len;
+ cli_qfileinfo_test(cli1, fnum1, SMB_FILE_ACCESS_INFORMATION, (char **)&accinfo, &len);
+ if (accinfo)
+ printf("access mode = 0x%lx\n", *accinfo);
+ SAFE_FREE(accinfo);
+ }
+#endif
+
+ if (!cli_close(cli1, fnum1)) {
+ printf("[1] close failed (%s)\n", cli_errstr(cli1));
+ correct = False;
+ goto fail;
+ }
+
+ fnum1 = cli_open(cli1, fname, O_RDWR, DENY_NONE);
+ if (fnum1 != -1) {
+ printf("[1] open of %s succeeded (should fail)\n", fname);
+ correct = False;
+ goto fail;
+ }
+
+ printf("first delete on close test succeeded.\n");
+
+ /* Test 2 - this should delete the file on close. */
+
+ cli_setatr(cli1, fname, 0, 0);
+ cli_unlink(cli1, fname);
+
+ fnum1 = cli_nt_create_full(cli1, fname, 0, GENERIC_ALL_ACCESS,
+ FILE_ATTRIBUTE_NORMAL, FILE_SHARE_NONE,
+ FILE_OVERWRITE_IF, 0, 0);
+
+ if (fnum1 == -1) {
+ printf("[2] open of %s failed (%s)\n", fname, cli_errstr(cli1));
+ correct = False;
+ goto fail;
+ }
+
+ if (!cli_nt_delete_on_close(cli1, fnum1, True)) {
+ printf("[2] setting delete_on_close failed (%s)\n", cli_errstr(cli1));
+ correct = False;
+ goto fail;
+ }
+
+ if (!cli_close(cli1, fnum1)) {
+ printf("[2] close failed (%s)\n", cli_errstr(cli1));
+ correct = False;
+ goto fail;
+ }
+
+ fnum1 = cli_open(cli1, fname, O_RDONLY, DENY_NONE);
+ if (fnum1 != -1) {
+ printf("[2] open of %s succeeded should have been deleted on close !\n", fname);
+ if (!cli_close(cli1, fnum1)) {
+ printf("[2] close failed (%s)\n", cli_errstr(cli1));
+ correct = False;
+ goto fail;
+ }
+ cli_unlink(cli1, fname);
+ } else
+ printf("second delete on close test succeeded.\n");
+
+ /* Test 3 - ... */
+ cli_setatr(cli1, fname, 0, 0);
+ cli_unlink(cli1, fname);
+
+ fnum1 = cli_nt_create_full(cli1, fname, 0, GENERIC_ALL_ACCESS, FILE_ATTRIBUTE_NORMAL,
+ FILE_SHARE_READ|FILE_SHARE_WRITE, FILE_OVERWRITE_IF, 0, 0);
+
+ if (fnum1 == -1) {
+ printf("[3] open - 1 of %s failed (%s)\n", fname, cli_errstr(cli1));
+ correct = False;
+ goto fail;
+ }
+
+ /* This should fail with a sharing violation - open for delete is only compatible
+ with SHARE_DELETE. */
+
+ fnum2 = cli_nt_create_full(cli1, fname, 0, GENERIC_READ_ACCESS, FILE_ATTRIBUTE_NORMAL,
+ FILE_SHARE_READ|FILE_SHARE_WRITE, FILE_OPEN, 0, 0);
+
+ if (fnum2 != -1) {
+ printf("[3] open - 2 of %s succeeded - should have failed.\n", fname);
+ correct = False;
+ goto fail;
+ }
+
+ /* This should succeed. */
+
+ fnum2 = cli_nt_create_full(cli1, fname, 0, GENERIC_READ_ACCESS, FILE_ATTRIBUTE_NORMAL,
+ FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE, FILE_OPEN, 0, 0);
+
+ if (fnum2 == -1) {
+ printf("[3] open - 2 of %s failed (%s)\n", fname, cli_errstr(cli1));
+ correct = False;
+ goto fail;
+ }
+
+ if (!cli_nt_delete_on_close(cli1, fnum1, True)) {
+ printf("[3] setting delete_on_close failed (%s)\n", cli_errstr(cli1));
+ correct = False;
+ goto fail;
+ }
+
+ if (!cli_close(cli1, fnum1)) {
+ printf("[3] close 1 failed (%s)\n", cli_errstr(cli1));
+ correct = False;
+ goto fail;
+ }
+
+ if (!cli_close(cli1, fnum2)) {
+ printf("[3] close 2 failed (%s)\n", cli_errstr(cli1));
+ correct = False;
+ goto fail;
+ }
+
+ /* This should fail - file should no longer be there. */
+
+ fnum1 = cli_open(cli1, fname, O_RDONLY, DENY_NONE);
+ if (fnum1 != -1) {
+ printf("[3] open of %s succeeded should have been deleted on close !\n", fname);
+ if (!cli_close(cli1, fnum1)) {
+ printf("[3] close failed (%s)\n", cli_errstr(cli1));
+ }
+ cli_unlink(cli1, fname);
+ correct = False;
+ goto fail;
+ } else
+ printf("third delete on close test succeeded.\n");
+
+ /* Test 4 ... */
+ cli_setatr(cli1, fname, 0, 0);
+ cli_unlink(cli1, fname);
+
+ fnum1 = cli_nt_create_full(cli1, fname, 0, FILE_READ_DATA|FILE_WRITE_DATA|DELETE_ACCESS,
+ FILE_ATTRIBUTE_NORMAL, FILE_SHARE_READ|FILE_SHARE_WRITE, FILE_OVERWRITE_IF, 0, 0);
+
+ if (fnum1 == -1) {
+ printf("[4] open of %s failed (%s)\n", fname, cli_errstr(cli1));
+ correct = False;
+ goto fail;
+ }
+
+ /* This should succeed. */
+ fnum2 = cli_nt_create_full(cli1, fname, 0, GENERIC_READ_ACCESS,
+ FILE_ATTRIBUTE_NORMAL, FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE, FILE_OPEN, 0, 0);
+ if (fnum2 == -1) {
+ printf("[4] open - 2 of %s failed (%s)\n", fname, cli_errstr(cli1));
+ correct = False;
+ goto fail;
+ }
+
+ if (!cli_close(cli1, fnum2)) {
+ printf("[4] close - 1 failed (%s)\n", cli_errstr(cli1));
+ correct = False;
+ goto fail;
+ }
+
+ if (!cli_nt_delete_on_close(cli1, fnum1, True)) {
+ printf("[4] setting delete_on_close failed (%s)\n", cli_errstr(cli1));
+ correct = False;
+ goto fail;
+ }
+
+ /* This should fail - no more opens once delete on close set. */
+ fnum2 = cli_nt_create_full(cli1, fname, 0, GENERIC_READ_ACCESS,
+ FILE_ATTRIBUTE_NORMAL, FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE,
+ FILE_OPEN, 0, 0);
+ if (fnum2 != -1) {
+ printf("[4] open - 3 of %s succeeded ! Should have failed.\n", fname );
+ correct = False;
+ goto fail;
+ } else
+ printf("fourth delete on close test succeeded.\n");
+
+ if (!cli_close(cli1, fnum1)) {
+ printf("[4] close - 2 failed (%s)\n", cli_errstr(cli1));
+ correct = False;
+ goto fail;
+ }
+
+ /* Test 5 ... */
+ cli_setatr(cli1, fname, 0, 0);
+ cli_unlink(cli1, fname);
+
+ fnum1 = cli_open(cli1, fname, O_RDWR|O_CREAT, DENY_NONE);
+ if (fnum1 == -1) {
+ printf("[5] open of %s failed (%s)\n", fname, cli_errstr(cli1));
+ correct = False;
+ goto fail;
+ }
+
+ /* This should fail - only allowed on NT opens with DELETE access. */
+
+ if (cli_nt_delete_on_close(cli1, fnum1, True)) {
+ printf("[5] setting delete_on_close on OpenX file succeeded - should fail !\n");
+ correct = False;
+ goto fail;
+ }
+
+ if (!cli_close(cli1, fnum1)) {
+ printf("[5] close - 2 failed (%s)\n", cli_errstr(cli1));
+ correct = False;
+ goto fail;
+ }
+
+ printf("fifth delete on close test succeeded.\n");
+
+ /* Test 6 ... */
+ cli_setatr(cli1, fname, 0, 0);
+ cli_unlink(cli1, fname);
+
+ fnum1 = cli_nt_create_full(cli1, fname, 0, FILE_READ_DATA|FILE_WRITE_DATA,
+ FILE_ATTRIBUTE_NORMAL, FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE,
+ FILE_OVERWRITE_IF, 0, 0);
+
+ if (fnum1 == -1) {
+ printf("[6] open of %s failed (%s)\n", fname, cli_errstr(cli1));
+ correct = False;
+ goto fail;
+ }
+
+ /* This should fail - only allowed on NT opens with DELETE access. */
+
+ if (cli_nt_delete_on_close(cli1, fnum1, True)) {
+ printf("[6] setting delete_on_close on file with no delete access succeeded - should fail !\n");
+ correct = False;
+ goto fail;
+ }
+
+ if (!cli_close(cli1, fnum1)) {
+ printf("[6] close - 2 failed (%s)\n", cli_errstr(cli1));
+ correct = False;
+ goto fail;
+ }
+
+ printf("sixth delete on close test succeeded.\n");
+
+ /* Test 7 ... */
+ cli_setatr(cli1, fname, 0, 0);
+ cli_unlink(cli1, fname);
+
+ fnum1 = cli_nt_create_full(cli1, fname, 0, FILE_READ_DATA|FILE_WRITE_DATA|DELETE_ACCESS,
+ FILE_ATTRIBUTE_NORMAL, 0, FILE_OVERWRITE_IF, 0, 0);
+
+ if (fnum1 == -1) {
+ printf("[7] open of %s failed (%s)\n", fname, cli_errstr(cli1));
+ correct = False;
+ goto fail;
+ }
+
+ if (!cli_nt_delete_on_close(cli1, fnum1, True)) {
+ printf("[7] setting delete_on_close on file failed !\n");
+ correct = False;
+ goto fail;
+ }
+
+ if (!cli_nt_delete_on_close(cli1, fnum1, False)) {
+ printf("[7] unsetting delete_on_close on file failed !\n");
+ correct = False;
+ goto fail;
+ }
+
+ if (!cli_close(cli1, fnum1)) {
+ printf("[7] close - 2 failed (%s)\n", cli_errstr(cli1));
+ correct = False;
+ goto fail;
+ }
+
+ /* This next open should succeed - we reset the flag. */
+
+ fnum1 = cli_open(cli1, fname, O_RDONLY, DENY_NONE);
+ if (fnum1 == -1) {
+ printf("[5] open of %s failed (%s)\n", fname, cli_errstr(cli1));
+ correct = False;
+ goto fail;
+ }
+
+ if (!cli_close(cli1, fnum1)) {
+ printf("[7] close - 2 failed (%s)\n", cli_errstr(cli1));
+ correct = False;
+ goto fail;
+ }
+
+ printf("seventh delete on close test succeeded.\n");
+
+ /* Test 7 ... */
+ cli_setatr(cli1, fname, 0, 0);
+ cli_unlink(cli1, fname);
+
+ if (!torture_open_connection(&cli2)) {
+ printf("[8] failed to open second connection.\n");
+ correct = False;
+ goto fail;
+ }
+
+ cli_sockopt(cli1, sockops);
+
+ fnum1 = cli_nt_create_full(cli1, fname, 0, FILE_READ_DATA|FILE_WRITE_DATA|DELETE_ACCESS,
+ FILE_ATTRIBUTE_NORMAL, FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE,
+ FILE_OVERWRITE_IF, 0, 0);
+
+ if (fnum1 == -1) {
+ printf("[8] open of %s failed (%s)\n", fname, cli_errstr(cli1));
+ correct = False;
+ goto fail;
+ }
+
+ fnum2 = cli_nt_create_full(cli2, fname, 0, FILE_READ_DATA|FILE_WRITE_DATA|DELETE_ACCESS,
+ FILE_ATTRIBUTE_NORMAL, FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE,
+ FILE_OPEN, 0, 0);
+
+ if (fnum2 == -1) {
+ printf("[8] open of %s failed (%s)\n", fname, cli_errstr(cli1));
+ correct = False;
+ goto fail;
+ }
+
+ if (!cli_nt_delete_on_close(cli1, fnum1, True)) {
+ printf("[8] setting delete_on_close on file failed !\n");
+ correct = False;
+ goto fail;
+ }
+
+ if (!cli_close(cli1, fnum1)) {
+ printf("[8] close - 1 failed (%s)\n", cli_errstr(cli1));
+ correct = False;
+ goto fail;
+ }
+
+ if (!cli_close(cli2, fnum2)) {
+ printf("[8] close - 2 failed (%s)\n", cli_errstr(cli2));
+ correct = False;
+ goto fail;
+ }
+
+ /* This should fail.. */
+ fnum1 = cli_open(cli1, fname, O_RDONLY, DENY_NONE);
+ if (fnum1 != -1) {
+ printf("[8] open of %s succeeded should have been deleted on close !\n", fname);
+ goto fail;
+ correct = False;
+ } else
+ printf("eighth delete on close test succeeded.\n");
+
+ /* This should fail - we need to set DELETE_ACCESS. */
+ fnum1 = cli_nt_create_full(cli1, fname, 0,FILE_READ_DATA|FILE_WRITE_DATA,
+ FILE_ATTRIBUTE_NORMAL, FILE_SHARE_NONE, FILE_OVERWRITE_IF, FILE_DELETE_ON_CLOSE, 0);
+
+ if (fnum1 != -1) {
+ printf("[9] open of %s succeeded should have failed!\n", fname);
+ correct = False;
+ goto fail;
+ }
+
+ printf("ninth delete on close test succeeded.\n");
+
+ fnum1 = cli_nt_create_full(cli1, fname, 0, FILE_READ_DATA|FILE_WRITE_DATA|DELETE_ACCESS,
+ FILE_ATTRIBUTE_NORMAL, FILE_SHARE_NONE, FILE_OVERWRITE_IF, FILE_DELETE_ON_CLOSE, 0);
+ if (fnum1 == -1) {
+ printf("[10] open of %s failed (%s)\n", fname, cli_errstr(cli1));
+ correct = False;
+ goto fail;
+ }
+
+ /* This should delete the file. */
+ if (!cli_close(cli1, fnum1)) {
+ printf("[10] close failed (%s)\n", cli_errstr(cli1));
+ correct = False;
+ goto fail;
+ }
+
+ /* This should fail.. */
+ fnum1 = cli_open(cli1, fname, O_RDONLY, DENY_NONE);
+ if (fnum1 != -1) {
+ printf("[10] open of %s succeeded should have been deleted on close !\n", fname);
+ goto fail;
+ correct = False;
+ } else
+ printf("tenth delete on close test succeeded.\n");
+ printf("finished delete test\n");
+
+ fail:
+ /* FIXME: This will crash if we aborted before cli2 got
+ * intialized, because these functions don't handle
+ * uninitialized connections. */
+
+ if (fnum1 != -1) cli_close(cli1, fnum1);
+ if (fnum2 != -1) cli_close(cli1, fnum2);
+ cli_setatr(cli1, fname, 0, 0);
+ cli_unlink(cli1, fname);
+
+ if (cli1 && !torture_close_connection(cli1)) {
+ correct = False;
+ }
+ if (cli2 && !torture_close_connection(cli2)) {
+ correct = False;
+ }
+ return correct;
+}
+
+
+/*
+ print out server properties
+ */
+static BOOL run_properties(int dummy)
+{
+ static struct cli_state *cli;
+ BOOL correct = True;
+
+ printf("starting properties test\n");
+
+ ZERO_STRUCT(cli);
+
+ if (!torture_open_connection(&cli)) {
+ return False;
+ }
+
+ cli_sockopt(cli, sockops);
+
+ d_printf("Capabilities 0x%08x\n", cli->capabilities);
+
+ if (!torture_close_connection(cli)) {
+ correct = False;
+ }
+
+ return correct;
+}
+
+
+
+/* FIRST_DESIRED_ACCESS 0xf019f */
+#define FIRST_DESIRED_ACCESS FILE_READ_DATA|FILE_WRITE_DATA|FILE_APPEND_DATA|\
+ FILE_READ_EA| /* 0xf */ \
+ FILE_WRITE_EA|FILE_READ_ATTRIBUTES| /* 0x90 */ \
+ FILE_WRITE_ATTRIBUTES| /* 0x100 */ \
+ DELETE_ACCESS|READ_CONTROL_ACCESS|\
+ WRITE_DAC_ACCESS|WRITE_OWNER_ACCESS /* 0xf0000 */
+/* SECOND_DESIRED_ACCESS 0xe0080 */
+#define SECOND_DESIRED_ACCESS FILE_READ_ATTRIBUTES| /* 0x80 */ \
+ READ_CONTROL_ACCESS|WRITE_DAC_ACCESS|\
+ WRITE_OWNER_ACCESS /* 0xe0000 */
+
+#if 0
+#define THIRD_DESIRED_ACCESS FILE_READ_ATTRIBUTES| /* 0x80 */ \
+ READ_CONTROL_ACCESS|WRITE_DAC_ACCESS|\
+ FILE_READ_DATA|\
+ WRITE_OWNER_ACCESS /* */
+#endif
+
+/*
+ Test ntcreate calls made by xcopy
+ */
+static BOOL run_xcopy(int dummy)
+{
+ static struct cli_state *cli1;
+ const char *fname = "\\test.txt";
+ BOOL correct = True;
+ int fnum1, fnum2;
+
+ printf("starting xcopy test\n");
+
+ if (!torture_open_connection(&cli1)) {
+ return False;
+ }
+
+ fnum1 = cli_nt_create_full(cli1, fname, 0,
+ FIRST_DESIRED_ACCESS, FILE_ATTRIBUTE_ARCHIVE,
+ FILE_SHARE_NONE, FILE_OVERWRITE_IF,
+ 0x4044, 0);
+
+ if (fnum1 == -1) {
+ printf("First open failed - %s\n", cli_errstr(cli1));
+ return False;
+ }
+
+ fnum2 = cli_nt_create_full(cli1, fname, 0,
+ SECOND_DESIRED_ACCESS, 0,
+ FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE, FILE_OPEN,
+ 0x200000, 0);
+ if (fnum2 == -1) {
+ printf("second open failed - %s\n", cli_errstr(cli1));
+ return False;
+ }
+
+ if (!torture_close_connection(cli1)) {
+ correct = False;
+ }
+
+ return correct;
+}
+
+/*
+ Test rename on files open with share delete and no share delete.
+ */
+static BOOL run_rename(int dummy)
+{
+ static struct cli_state *cli1;
+ const char *fname = "\\test.txt";
+ const char *fname1 = "\\test1.txt";
+ BOOL correct = True;
+ int fnum1, fnum2;
+
+ printf("starting rename test\n");
+
+ if (!torture_open_connection(&cli1)) {
+ return False;
+ }
+
+ cli_unlink(cli1, fname);
+ cli_unlink(cli1, fname1);
+ fnum1 = cli_nt_create_full(cli1, fname, 0, GENERIC_READ_ACCESS, FILE_ATTRIBUTE_NORMAL,
+ FILE_SHARE_READ, FILE_OVERWRITE_IF, 0, 0);
+
+ if (fnum1 == -1) {
+ printf("First open failed - %s\n", cli_errstr(cli1));
+ return False;
+ }
+
+ if (!cli_rename(cli1, fname, fname1)) {
+ printf("First rename failed (SHARE_READ) (this is correct) - %s\n", cli_errstr(cli1));
+ } else {
+ printf("First rename succeeded (SHARE_READ) - this should have failed !\n");
+ correct = False;
+ }
+
+ if (!cli_close(cli1, fnum1)) {
+ printf("close - 1 failed (%s)\n", cli_errstr(cli1));
+ return False;
+ }
+
+ cli_unlink(cli1, fname);
+ cli_unlink(cli1, fname1);
+ fnum1 = cli_nt_create_full(cli1, fname, 0, GENERIC_READ_ACCESS, FILE_ATTRIBUTE_NORMAL,
+#if 0
+ FILE_SHARE_DELETE|FILE_SHARE_NONE, FILE_OVERWRITE_IF, 0, 0);
+#else
+ FILE_SHARE_DELETE|FILE_SHARE_READ, FILE_OVERWRITE_IF, 0, 0);
+#endif
+
+ if (fnum1 == -1) {
+ printf("Second open failed - %s\n", cli_errstr(cli1));
+ return False;
+ }
+
+ if (!cli_rename(cli1, fname, fname1)) {
+ printf("Second rename failed (SHARE_DELETE | SHARE_READ) - this should have succeeded - %s\n", cli_errstr(cli1));
+ correct = False;
+ } else {
+ printf("Second rename succeeded (SHARE_DELETE | SHARE_READ)\n");
+ }
+
+ if (!cli_close(cli1, fnum1)) {
+ printf("close - 2 failed (%s)\n", cli_errstr(cli1));
+ return False;
+ }
+
+ cli_unlink(cli1, fname);
+ cli_unlink(cli1, fname1);
+
+ fnum1 = cli_nt_create_full(cli1, fname, 0, READ_CONTROL_ACCESS, FILE_ATTRIBUTE_NORMAL,
+ FILE_SHARE_NONE, FILE_OVERWRITE_IF, 0, 0);
+
+ if (fnum1 == -1) {
+ printf("Third open failed - %s\n", cli_errstr(cli1));
+ return False;
+ }
+
+
+#if 0
+ {
+ int fnum2;
+
+ fnum2 = cli_nt_create_full(cli1, fname, 0, DELETE_ACCESS, FILE_ATTRIBUTE_NORMAL,
+ FILE_SHARE_NONE, FILE_OVERWRITE_IF, 0, 0);
+
+ if (fnum2 == -1) {
+ printf("Fourth open failed - %s\n", cli_errstr(cli1));
+ return False;
+ }
+ if (!cli_nt_delete_on_close(cli1, fnum2, True)) {
+ printf("[8] setting delete_on_close on file failed !\n");
+ return False;
+ }
+
+ if (!cli_close(cli1, fnum2)) {
+ printf("close - 4 failed (%s)\n", cli_errstr(cli1));
+ return False;
+ }
+ }
+#endif
+
+ if (!cli_rename(cli1, fname, fname1)) {
+ printf("Third rename failed (SHARE_NONE) - this should have succeeded - %s\n", cli_errstr(cli1));
+ correct = False;
+ } else {
+ printf("Third rename succeeded (SHARE_NONE)\n");
+ }
+
+ if (!cli_close(cli1, fnum1)) {
+ printf("close - 3 failed (%s)\n", cli_errstr(cli1));
+ return False;
+ }
+
+ cli_unlink(cli1, fname);
+ cli_unlink(cli1, fname1);
+
+ /*----*/
+
+ fnum1 = cli_nt_create_full(cli1, fname, 0, GENERIC_READ_ACCESS, FILE_ATTRIBUTE_NORMAL,
+ FILE_SHARE_READ | FILE_SHARE_WRITE, FILE_OVERWRITE_IF, 0, 0);
+
+ if (fnum1 == -1) {
+ printf("Fourth open failed - %s\n", cli_errstr(cli1));
+ return False;
+ }
+
+ if (!cli_rename(cli1, fname, fname1)) {
+ printf("Fourth rename failed (SHARE_READ | SHARE_WRITE) (this is correct) - %s\n", cli_errstr(cli1));
+ } else {
+ printf("Fourth rename succeeded (SHARE_READ | SHARE_WRITE) - this should have failed !\n");
+ correct = False;
+ }
+
+ if (!cli_close(cli1, fnum1)) {
+ printf("close - 4 failed (%s)\n", cli_errstr(cli1));
+ return False;
+ }
+
+ cli_unlink(cli1, fname);
+ cli_unlink(cli1, fname1);
+
+ /*--*/
+
+ fnum1 = cli_nt_create_full(cli1, fname, 0, GENERIC_READ_ACCESS, FILE_ATTRIBUTE_NORMAL,
+ FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, FILE_OVERWRITE_IF, 0, 0);
+
+ if (fnum1 == -1) {
+ printf("Fifth open failed - %s\n", cli_errstr(cli1));
+ return False;
+ }
+
+ if (!cli_rename(cli1, fname, fname1)) {
+ printf("Fifth rename failed (SHARE_READ | SHARE_WRITE | SHARE_DELETE) - this should have failed ! \n");
+ correct = False;
+ } else {
+ printf("Fifth rename succeeded (SHARE_READ | SHARE_WRITE | SHARE_DELETE) (this is correct) - %s\n", cli_errstr(cli1));
+ }
+
+ /*
+ * Now check if the first name still exists ...
+ */
+
+ /*fnum2 = cli_nt_create_full(cli1, fname, 0, GENERIC_READ_ACCESS, FILE_ATTRIBUTE_NORMAL,
+ FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, FILE_OVERWRITE_IF, 0, 0);
+
+ if (fnum2 == -1) {
+ printf("Opening original file after rename of open file fails: %s\n",
+ cli_errstr(cli1));
+ }
+ else {
+ printf("Opening original file after rename of open file works ...\n");
+ (void)cli_close(cli1, fnum2);
+ } */
+
+ /*--*/
+
+
+ if (!cli_close(cli1, fnum1)) {
+ printf("close - 5 failed (%s)\n", cli_errstr(cli1));
+ return False;
+ }
+
+ cli_unlink(cli1, fname);
+ cli_unlink(cli1, fname1);
+
+ if (!torture_close_connection(cli1)) {
+ correct = False;
+ }
+
+ return correct;
+}
+
+static BOOL run_pipe_number(int dummy)
+{
+ struct cli_state *cli1;
+ const char *pipe_name = "\\SPOOLSS";
+ int fnum;
+ int num_pipes = 0;
+
+ printf("starting pipenumber test\n");
+ if (!torture_open_connection(&cli1)) {
+ return False;
+ }
+
+ cli_sockopt(cli1, sockops);
+ while(1) {
+ fnum = cli_nt_create_full(cli1, pipe_name, 0, FILE_READ_DATA, FILE_ATTRIBUTE_NORMAL,
+ FILE_SHARE_READ|FILE_SHARE_WRITE, FILE_OPEN_IF, 0, 0);
+
+ if (fnum == -1) {
+ printf("Open of pipe %s failed with error (%s)\n", pipe_name, cli_errstr(cli1));
+ break;
+ }
+ num_pipes++;
+ }
+
+ printf("pipe_number test - we can open %d %s pipes.\n", num_pipes, pipe_name );
+ torture_close_connection(cli1);
+ return True;
+}
+
+/*
+ Test open mode returns on read-only files.
+ */
+static BOOL run_opentest(int dummy)
+{
+ static struct cli_state *cli1;
+ static struct cli_state *cli2;
+ const char *fname = "\\readonly.file";
+ int fnum1, fnum2;
+ char buf[20];
+ size_t fsize;
+ BOOL correct = True;
+ char *tmp_path;
+
+ printf("starting open test\n");
+
+ if (!torture_open_connection(&cli1)) {
+ return False;
+ }
+
+ cli_setatr(cli1, fname, 0, 0);
+ cli_unlink(cli1, fname);
+
+ cli_sockopt(cli1, sockops);
+
+ fnum1 = cli_open(cli1, fname, O_RDWR|O_CREAT|O_EXCL, DENY_NONE);
+ if (fnum1 == -1) {
+ printf("open of %s failed (%s)\n", fname, cli_errstr(cli1));
+ return False;
+ }
+
+ if (!cli_close(cli1, fnum1)) {
+ printf("close2 failed (%s)\n", cli_errstr(cli1));
+ return False;
+ }
+
+ if (!cli_setatr(cli1, fname, aRONLY, 0)) {
+ printf("cli_setatr failed (%s)\n", cli_errstr(cli1));
+ return False;
+ }
+
+ fnum1 = cli_open(cli1, fname, O_RDONLY, DENY_WRITE);
+ if (fnum1 == -1) {
+ printf("open of %s failed (%s)\n", fname, cli_errstr(cli1));
+ return False;
+ }
+
+ /* This will fail - but the error should be ERRnoaccess, not ERRbadshare. */
+ fnum2 = cli_open(cli1, fname, O_RDWR, DENY_ALL);
+
+ if (check_error(__LINE__, cli1, ERRDOS, ERRnoaccess,
+ NT_STATUS_ACCESS_DENIED)) {
+ printf("correct error code ERRDOS/ERRnoaccess returned\n");
+ }
+
+ printf("finished open test 1\n");
+
+ cli_close(cli1, fnum1);
+
+ /* Now try not readonly and ensure ERRbadshare is returned. */
+
+ cli_setatr(cli1, fname, 0, 0);
+
+ fnum1 = cli_open(cli1, fname, O_RDONLY, DENY_WRITE);
+ if (fnum1 == -1) {
+ printf("open of %s failed (%s)\n", fname, cli_errstr(cli1));
+ return False;
+ }
+
+ /* This will fail - but the error should be ERRshare. */
+ fnum2 = cli_open(cli1, fname, O_RDWR, DENY_ALL);
+
+ if (check_error(__LINE__, cli1, ERRDOS, ERRbadshare,
+ NT_STATUS_SHARING_VIOLATION)) {
+ printf("correct error code ERRDOS/ERRbadshare returned\n");
+ }
+
+ if (!cli_close(cli1, fnum1)) {
+ printf("close2 failed (%s)\n", cli_errstr(cli1));
+ return False;
+ }
+
+ cli_unlink(cli1, fname);
+
+ printf("finished open test 2\n");
+
+ /* Test truncate open disposition on file opened for read. */
+
+ fnum1 = cli_open(cli1, fname, O_RDWR|O_CREAT|O_EXCL, DENY_NONE);
+ if (fnum1 == -1) {
+ printf("(3) open (1) of %s failed (%s)\n", fname, cli_errstr(cli1));
+ return False;
+ }
+
+ /* write 20 bytes. */
+
+ memset(buf, '\0', 20);
+
+ if (cli_write(cli1, fnum1, 0, buf, 0, 20) != 20) {
+ printf("write failed (%s)\n", cli_errstr(cli1));
+ correct = False;
+ }
+
+ if (!cli_close(cli1, fnum1)) {
+ printf("(3) close1 failed (%s)\n", cli_errstr(cli1));
+ return False;
+ }
+
+ /* Ensure size == 20. */
+ if (!cli_getatr(cli1, fname, NULL, &fsize, NULL)) {
+ printf("(3) getatr failed (%s)\n", cli_errstr(cli1));
+ return False;
+ }
+
+ if (fsize != 20) {
+ printf("(3) file size != 20\n");
+ return False;
+ }
+
+ /* Now test if we can truncate a file opened for readonly. */
+
+ fnum1 = cli_open(cli1, fname, O_RDONLY|O_TRUNC, DENY_NONE);
+ if (fnum1 == -1) {
+ printf("(3) open (2) of %s failed (%s)\n", fname, cli_errstr(cli1));
+ return False;
+ }
+
+ if (!cli_close(cli1, fnum1)) {
+ printf("close2 failed (%s)\n", cli_errstr(cli1));
+ return False;
+ }
+
+ /* Ensure size == 0. */
+ if (!cli_getatr(cli1, fname, NULL, &fsize, NULL)) {
+ printf("(3) getatr failed (%s)\n", cli_errstr(cli1));
+ return False;
+ }
+
+ if (fsize != 0) {
+ printf("(3) file size != 0\n");
+ return False;
+ }
+ printf("finished open test 3\n");
+
+ cli_unlink(cli1, fname);
+
+
+ printf("testing ctemp\n");
+ fnum1 = cli_ctemp(cli1, "\\", &tmp_path);
+ if (fnum1 == -1) {
+ printf("ctemp failed (%s)\n", cli_errstr(cli1));
+ return False;
+ }
+ printf("ctemp gave path %s\n", tmp_path);
+ if (!cli_close(cli1, fnum1)) {
+ printf("close of temp failed (%s)\n", cli_errstr(cli1));
+ }
+ if (!cli_unlink(cli1, tmp_path)) {
+ printf("unlink of temp failed (%s)\n", cli_errstr(cli1));
+ }
+
+ /* Test the non-io opens... */
+
+ if (!torture_open_connection(&cli2)) {
+ return False;
+ }
+
+ cli_setatr(cli2, fname, 0, 0);
+ cli_unlink(cli2, fname);
+
+ cli_sockopt(cli2, sockops);
+
+ printf("TEST #1 testing 2 non-io opens (no delete)\n");
+
+ fnum1 = cli_nt_create_full(cli1, fname, 0, FILE_READ_ATTRIBUTES, FILE_ATTRIBUTE_NORMAL,
+ FILE_SHARE_NONE, FILE_OVERWRITE_IF, 0, 0);
+
+ if (fnum1 == -1) {
+ printf("test 1 open 1 of %s failed (%s)\n", fname, cli_errstr(cli1));
+ return False;
+ }
+
+ fnum2 = cli_nt_create_full(cli2, fname, 0, FILE_READ_ATTRIBUTES, FILE_ATTRIBUTE_NORMAL,
+ FILE_SHARE_NONE, FILE_OPEN_IF, 0, 0);
+
+ if (fnum2 == -1) {
+ printf("test 1 open 2 of %s failed (%s)\n", fname, cli_errstr(cli2));
+ return False;
+ }
+
+ if (!cli_close(cli1, fnum1)) {
+ printf("test 1 close 1 of %s failed (%s)\n", fname, cli_errstr(cli1));
+ return False;
+ }
+ if (!cli_close(cli2, fnum2)) {
+ printf("test 1 close 2 of %s failed (%s)\n", fname, cli_errstr(cli2));
+ return False;
+ }
+
+ printf("non-io open test #1 passed.\n");
+
+ cli_unlink(cli1, fname);
+
+ printf("TEST #2 testing 2 non-io opens (first with delete)\n");
+
+ fnum1 = cli_nt_create_full(cli1, fname, 0, DELETE_ACCESS|FILE_READ_ATTRIBUTES, FILE_ATTRIBUTE_NORMAL,
+ FILE_SHARE_NONE, FILE_OVERWRITE_IF, 0, 0);
+
+ if (fnum1 == -1) {
+ printf("test 2 open 1 of %s failed (%s)\n", fname, cli_errstr(cli1));
+ return False;
+ }
+
+ fnum2 = cli_nt_create_full(cli2, fname, 0, FILE_READ_ATTRIBUTES, FILE_ATTRIBUTE_NORMAL,
+ FILE_SHARE_NONE, FILE_OPEN_IF, 0, 0);
+
+ if (fnum2 == -1) {
+ printf("test 2 open 2 of %s failed (%s)\n", fname, cli_errstr(cli2));
+ return False;
+ }
+
+ if (!cli_close(cli1, fnum1)) {
+ printf("test 1 close 1 of %s failed (%s)\n", fname, cli_errstr(cli1));
+ return False;
+ }
+ if (!cli_close(cli2, fnum2)) {
+ printf("test 1 close 2 of %s failed (%s)\n", fname, cli_errstr(cli1));
+ return False;
+ }
+
+ printf("non-io open test #2 passed.\n");
+
+ cli_unlink(cli1, fname);
+
+ printf("TEST #3 testing 2 non-io opens (second with delete)\n");
+
+ fnum1 = cli_nt_create_full(cli1, fname, 0, FILE_READ_ATTRIBUTES, FILE_ATTRIBUTE_NORMAL,
+ FILE_SHARE_NONE, FILE_OVERWRITE_IF, 0, 0);
+
+ if (fnum1 == -1) {
+ printf("test 3 open 1 of %s failed (%s)\n", fname, cli_errstr(cli1));
+ return False;
+ }
+
+ fnum2 = cli_nt_create_full(cli2, fname, 0, DELETE_ACCESS|FILE_READ_ATTRIBUTES, FILE_ATTRIBUTE_NORMAL,
+ FILE_SHARE_NONE, FILE_OPEN_IF, 0, 0);
+
+ if (fnum2 == -1) {
+ printf("test 3 open 2 of %s failed (%s)\n", fname, cli_errstr(cli2));
+ return False;
+ }
+
+ if (!cli_close(cli1, fnum1)) {
+ printf("test 3 close 1 of %s failed (%s)\n", fname, cli_errstr(cli1));
+ return False;
+ }
+ if (!cli_close(cli2, fnum2)) {
+ printf("test 3 close 2 of %s failed (%s)\n", fname, cli_errstr(cli2));
+ return False;
+ }
+
+ printf("non-io open test #3 passed.\n");
+
+ cli_unlink(cli1, fname);
+
+ printf("TEST #4 testing 2 non-io opens (both with delete)\n");
+
+ fnum1 = cli_nt_create_full(cli1, fname, 0, DELETE_ACCESS|FILE_READ_ATTRIBUTES, FILE_ATTRIBUTE_NORMAL,
+ FILE_SHARE_NONE, FILE_OVERWRITE_IF, 0, 0);
+
+ if (fnum1 == -1) {
+ printf("test 4 open 1 of %s failed (%s)\n", fname, cli_errstr(cli1));
+ return False;
+ }
+
+ fnum2 = cli_nt_create_full(cli2, fname, 0, DELETE_ACCESS|FILE_READ_ATTRIBUTES, FILE_ATTRIBUTE_NORMAL,
+ FILE_SHARE_NONE, FILE_OPEN_IF, 0, 0);
+
+ if (fnum2 != -1) {
+ printf("test 4 open 2 of %s SUCCEEDED - should have failed (%s)\n", fname, cli_errstr(cli2));
+ return False;
+ }
+
+ printf("test 3 open 2 of %s gave %s (correct error should be %s)\n", fname, cli_errstr(cli2), "sharing violation");
+
+ if (!cli_close(cli1, fnum1)) {
+ printf("test 4 close 1 of %s failed (%s)\n", fname, cli_errstr(cli1));
+ return False;
+ }
+
+ printf("non-io open test #4 passed.\n");
+
+ cli_unlink(cli1, fname);
+
+ printf("TEST #5 testing 2 non-io opens (both with delete - both with file share delete)\n");
+
+ fnum1 = cli_nt_create_full(cli1, fname, 0, DELETE_ACCESS|FILE_READ_ATTRIBUTES, FILE_ATTRIBUTE_NORMAL,
+ FILE_SHARE_DELETE, FILE_OVERWRITE_IF, 0, 0);
+
+ if (fnum1 == -1) {
+ printf("test 5 open 1 of %s failed (%s)\n", fname, cli_errstr(cli1));
+ return False;
+ }
+
+ fnum2 = cli_nt_create_full(cli2, fname, 0, DELETE_ACCESS|FILE_READ_ATTRIBUTES, FILE_ATTRIBUTE_NORMAL,
+ FILE_SHARE_DELETE, FILE_OPEN_IF, 0, 0);
+
+ if (fnum2 == -1) {
+ printf("test 5 open 2 of %s failed (%s)\n", fname, cli_errstr(cli2));
+ return False;
+ }
+
+ if (!cli_close(cli1, fnum1)) {
+ printf("test 5 close 1 of %s failed (%s)\n", fname, cli_errstr(cli1));
+ return False;
+ }
+
+ if (!cli_close(cli2, fnum2)) {
+ printf("test 5 close 2 of %s failed (%s)\n", fname, cli_errstr(cli2));
+ return False;
+ }
+
+ printf("non-io open test #5 passed.\n");
+
+ printf("TEST #6 testing 1 non-io open, one io open\n");
+
+ cli_unlink(cli1, fname);
+
+ fnum1 = cli_nt_create_full(cli1, fname, 0, FILE_READ_DATA, FILE_ATTRIBUTE_NORMAL,
+ FILE_SHARE_NONE, FILE_OVERWRITE_IF, 0, 0);
+
+ if (fnum1 == -1) {
+ printf("test 6 open 1 of %s failed (%s)\n", fname, cli_errstr(cli1));
+ return False;
+ }
+
+ fnum2 = cli_nt_create_full(cli2, fname, 0, FILE_READ_ATTRIBUTES, FILE_ATTRIBUTE_NORMAL,
+ FILE_SHARE_READ, FILE_OPEN_IF, 0, 0);
+
+ if (fnum2 == -1) {
+ printf("test 6 open 2 of %s failed (%s)\n", fname, cli_errstr(cli2));
+ return False;
+ }
+
+ if (!cli_close(cli1, fnum1)) {
+ printf("test 6 close 1 of %s failed (%s)\n", fname, cli_errstr(cli1));
+ return False;
+ }
+
+ if (!cli_close(cli2, fnum2)) {
+ printf("test 6 close 2 of %s failed (%s)\n", fname, cli_errstr(cli2));
+ return False;
+ }
+
+ printf("non-io open test #6 passed.\n");
+
+ printf("TEST #7 testing 1 non-io open, one io open with delete\n");
+
+ cli_unlink(cli1, fname);
+
+ fnum1 = cli_nt_create_full(cli1, fname, 0, FILE_READ_DATA, FILE_ATTRIBUTE_NORMAL,
+ FILE_SHARE_NONE, FILE_OVERWRITE_IF, 0, 0);
+
+ if (fnum1 == -1) {
+ printf("test 7 open 1 of %s failed (%s)\n", fname, cli_errstr(cli1));
+ return False;
+ }
+
+ fnum2 = cli_nt_create_full(cli2, fname, 0, DELETE_ACCESS|FILE_READ_ATTRIBUTES, FILE_ATTRIBUTE_NORMAL,
+ FILE_SHARE_READ|FILE_SHARE_DELETE, FILE_OPEN_IF, 0, 0);
+
+ if (fnum2 != -1) {
+ printf("test 7 open 2 of %s SUCCEEDED - should have failed (%s)\n", fname, cli_errstr(cli2));
+ return False;
+ }
+
+ printf("test 7 open 2 of %s gave %s (correct error should be %s)\n", fname, cli_errstr(cli2), "sharing violation");
+
+ if (!cli_close(cli1, fnum1)) {
+ printf("test 7 close 1 of %s failed (%s)\n", fname, cli_errstr(cli1));
+ return False;
+ }
+
+ printf("non-io open test #7 passed.\n");
+
+ cli_unlink(cli1, fname);
+
+ if (!torture_close_connection(cli1)) {
+ correct = False;
+ }
+ if (!torture_close_connection(cli2)) {
+ correct = False;
+ }
+
+ return correct;
+}
+
+static uint32 open_attrs_table[] = {
+ FILE_ATTRIBUTE_NORMAL,
+ FILE_ATTRIBUTE_ARCHIVE,
+ FILE_ATTRIBUTE_READONLY,
+ FILE_ATTRIBUTE_HIDDEN,
+ FILE_ATTRIBUTE_SYSTEM,
+
+ FILE_ATTRIBUTE_ARCHIVE|FILE_ATTRIBUTE_READONLY,
+ FILE_ATTRIBUTE_ARCHIVE|FILE_ATTRIBUTE_HIDDEN,
+ FILE_ATTRIBUTE_ARCHIVE|FILE_ATTRIBUTE_SYSTEM,
+ FILE_ATTRIBUTE_ARCHIVE|FILE_ATTRIBUTE_READONLY|FILE_ATTRIBUTE_HIDDEN,
+ FILE_ATTRIBUTE_ARCHIVE|FILE_ATTRIBUTE_READONLY|FILE_ATTRIBUTE_SYSTEM,
+ FILE_ATTRIBUTE_ARCHIVE|FILE_ATTRIBUTE_HIDDEN|FILE_ATTRIBUTE_SYSTEM,
+
+ FILE_ATTRIBUTE_READONLY|FILE_ATTRIBUTE_HIDDEN,
+ FILE_ATTRIBUTE_READONLY|FILE_ATTRIBUTE_SYSTEM,
+ FILE_ATTRIBUTE_READONLY|FILE_ATTRIBUTE_HIDDEN|FILE_ATTRIBUTE_SYSTEM,
+ FILE_ATTRIBUTE_HIDDEN,FILE_ATTRIBUTE_SYSTEM,
+};
+
+struct trunc_open_results {
+ unsigned int num;
+ uint32 init_attr;
+ uint32 trunc_attr;
+ uint32 result_attr;
+};
+
+static struct trunc_open_results attr_results[] = {
+ { 0, FILE_ATTRIBUTE_NORMAL, FILE_ATTRIBUTE_NORMAL, FILE_ATTRIBUTE_ARCHIVE },
+ { 1, FILE_ATTRIBUTE_NORMAL, FILE_ATTRIBUTE_ARCHIVE, FILE_ATTRIBUTE_ARCHIVE },
+ { 2, FILE_ATTRIBUTE_NORMAL, FILE_ATTRIBUTE_READONLY, FILE_ATTRIBUTE_ARCHIVE|FILE_ATTRIBUTE_READONLY },
+ { 16, FILE_ATTRIBUTE_ARCHIVE, FILE_ATTRIBUTE_NORMAL, FILE_ATTRIBUTE_ARCHIVE },
+ { 17, FILE_ATTRIBUTE_ARCHIVE, FILE_ATTRIBUTE_ARCHIVE, FILE_ATTRIBUTE_ARCHIVE },
+ { 18, FILE_ATTRIBUTE_ARCHIVE, FILE_ATTRIBUTE_READONLY, FILE_ATTRIBUTE_ARCHIVE|FILE_ATTRIBUTE_READONLY },
+ { 51, FILE_ATTRIBUTE_HIDDEN, FILE_ATTRIBUTE_HIDDEN, FILE_ATTRIBUTE_ARCHIVE|FILE_ATTRIBUTE_HIDDEN },
+ { 54, FILE_ATTRIBUTE_HIDDEN, FILE_ATTRIBUTE_ARCHIVE|FILE_ATTRIBUTE_HIDDEN, FILE_ATTRIBUTE_ARCHIVE|FILE_ATTRIBUTE_HIDDEN },
+ { 56, FILE_ATTRIBUTE_HIDDEN, FILE_ATTRIBUTE_ARCHIVE|FILE_ATTRIBUTE_READONLY|FILE_ATTRIBUTE_HIDDEN, FILE_ATTRIBUTE_ARCHIVE|FILE_ATTRIBUTE_READONLY|FILE_ATTRIBUTE_HIDDEN },
+ { 68, FILE_ATTRIBUTE_SYSTEM, FILE_ATTRIBUTE_SYSTEM, FILE_ATTRIBUTE_ARCHIVE|FILE_ATTRIBUTE_SYSTEM },
+ { 71, FILE_ATTRIBUTE_SYSTEM, FILE_ATTRIBUTE_ARCHIVE|FILE_ATTRIBUTE_SYSTEM, FILE_ATTRIBUTE_ARCHIVE|FILE_ATTRIBUTE_SYSTEM },
+ { 73, FILE_ATTRIBUTE_SYSTEM, FILE_ATTRIBUTE_ARCHIVE|FILE_ATTRIBUTE_READONLY|FILE_ATTRIBUTE_SYSTEM, FILE_ATTRIBUTE_ARCHIVE|FILE_ATTRIBUTE_READONLY|FILE_ATTRIBUTE_SYSTEM },
+ { 99, FILE_ATTRIBUTE_ARCHIVE|FILE_ATTRIBUTE_HIDDEN, FILE_ATTRIBUTE_HIDDEN,FILE_ATTRIBUTE_ARCHIVE|FILE_ATTRIBUTE_HIDDEN },
+ { 102, FILE_ATTRIBUTE_ARCHIVE|FILE_ATTRIBUTE_HIDDEN, FILE_ATTRIBUTE_ARCHIVE|FILE_ATTRIBUTE_HIDDEN, FILE_ATTRIBUTE_ARCHIVE|FILE_ATTRIBUTE_HIDDEN },
+ { 104, FILE_ATTRIBUTE_ARCHIVE|FILE_ATTRIBUTE_HIDDEN, FILE_ATTRIBUTE_ARCHIVE|FILE_ATTRIBUTE_READONLY|FILE_ATTRIBUTE_HIDDEN, FILE_ATTRIBUTE_ARCHIVE|FILE_ATTRIBUTE_READONLY|FILE_ATTRIBUTE_HIDDEN },
+ { 116, FILE_ATTRIBUTE_ARCHIVE|FILE_ATTRIBUTE_SYSTEM, FILE_ATTRIBUTE_SYSTEM, FILE_ATTRIBUTE_ARCHIVE|FILE_ATTRIBUTE_SYSTEM },
+ { 119, FILE_ATTRIBUTE_ARCHIVE|FILE_ATTRIBUTE_SYSTEM, FILE_ATTRIBUTE_ARCHIVE|FILE_ATTRIBUTE_SYSTEM, FILE_ATTRIBUTE_ARCHIVE|FILE_ATTRIBUTE_SYSTEM },
+ { 121, FILE_ATTRIBUTE_ARCHIVE|FILE_ATTRIBUTE_SYSTEM, FILE_ATTRIBUTE_ARCHIVE|FILE_ATTRIBUTE_READONLY|FILE_ATTRIBUTE_SYSTEM, FILE_ATTRIBUTE_ARCHIVE|FILE_ATTRIBUTE_READONLY|FILE_ATTRIBUTE_SYSTEM },
+ { 170, FILE_ATTRIBUTE_ARCHIVE|FILE_ATTRIBUTE_SYSTEM|FILE_ATTRIBUTE_HIDDEN, FILE_ATTRIBUTE_ARCHIVE|FILE_ATTRIBUTE_SYSTEM|FILE_ATTRIBUTE_HIDDEN, FILE_ATTRIBUTE_ARCHIVE|FILE_ATTRIBUTE_SYSTEM|FILE_ATTRIBUTE_HIDDEN },
+ { 173, FILE_ATTRIBUTE_ARCHIVE|FILE_ATTRIBUTE_SYSTEM|FILE_ATTRIBUTE_HIDDEN, FILE_ATTRIBUTE_READONLY|FILE_ATTRIBUTE_HIDDEN|FILE_ATTRIBUTE_SYSTEM, FILE_ATTRIBUTE_ARCHIVE|FILE_ATTRIBUTE_READONLY|FILE_ATTRIBUTE_HIDDEN|FILE_ATTRIBUTE_SYSTEM },
+ { 227, FILE_ATTRIBUTE_HIDDEN, FILE_ATTRIBUTE_HIDDEN, FILE_ATTRIBUTE_ARCHIVE|FILE_ATTRIBUTE_HIDDEN },
+ { 230, FILE_ATTRIBUTE_HIDDEN, FILE_ATTRIBUTE_ARCHIVE|FILE_ATTRIBUTE_HIDDEN, FILE_ATTRIBUTE_ARCHIVE|FILE_ATTRIBUTE_HIDDEN },
+ { 232, FILE_ATTRIBUTE_HIDDEN, FILE_ATTRIBUTE_ARCHIVE|FILE_ATTRIBUTE_READONLY|FILE_ATTRIBUTE_HIDDEN, FILE_ATTRIBUTE_ARCHIVE|FILE_ATTRIBUTE_READONLY|FILE_ATTRIBUTE_HIDDEN },
+ { 244, FILE_ATTRIBUTE_SYSTEM, FILE_ATTRIBUTE_SYSTEM, FILE_ATTRIBUTE_ARCHIVE|FILE_ATTRIBUTE_SYSTEM },
+ { 247, FILE_ATTRIBUTE_SYSTEM, FILE_ATTRIBUTE_ARCHIVE|FILE_ATTRIBUTE_SYSTEM, FILE_ATTRIBUTE_ARCHIVE|FILE_ATTRIBUTE_SYSTEM },
+ { 249, FILE_ATTRIBUTE_SYSTEM, FILE_ATTRIBUTE_ARCHIVE|FILE_ATTRIBUTE_READONLY|FILE_ATTRIBUTE_SYSTEM, FILE_ATTRIBUTE_ARCHIVE|FILE_ATTRIBUTE_READONLY|FILE_ATTRIBUTE_SYSTEM }
+};
+
+static BOOL run_openattrtest(int dummy)
+{
+ static struct cli_state *cli1;
+ const char *fname = "\\openattr.file";
+ int fnum1;
+ BOOL correct = True;
+ uint16 attr;
+ unsigned int i, j, k, l;
+
+ printf("starting open attr test\n");
+
+ if (!torture_open_connection(&cli1)) {
+ return False;
+ }
+
+ cli_sockopt(cli1, sockops);
+
+ for (k = 0, i = 0; i < sizeof(open_attrs_table)/sizeof(uint32); i++) {
+ cli_setatr(cli1, fname, 0, 0);
+ cli_unlink(cli1, fname);
+ fnum1 = cli_nt_create_full(cli1, fname, 0, FILE_WRITE_DATA, open_attrs_table[i],
+ FILE_SHARE_NONE, FILE_OVERWRITE_IF, 0, 0);
+
+ if (fnum1 == -1) {
+ printf("open %d (1) of %s failed (%s)\n", i, fname, cli_errstr(cli1));
+ return False;
+ }
+
+ if (!cli_close(cli1, fnum1)) {
+ printf("close %d (1) of %s failed (%s)\n", i, fname, cli_errstr(cli1));
+ return False;
+ }
+
+ for (j = 0; j < sizeof(open_attrs_table)/sizeof(uint32); j++) {
+ fnum1 = cli_nt_create_full(cli1, fname, 0, FILE_READ_DATA|FILE_WRITE_DATA, open_attrs_table[j],
+ FILE_SHARE_NONE, FILE_OVERWRITE, 0, 0);
+
+ if (fnum1 == -1) {
+ for (l = 0; l < sizeof(attr_results)/sizeof(struct trunc_open_results); l++) {
+ if (attr_results[l].num == k) {
+ printf("[%d] trunc open 0x%x -> 0x%x of %s failed - should have succeeded !(0x%x:%s)\n",
+ k, open_attrs_table[i],
+ open_attrs_table[j],
+ fname, NT_STATUS_V(cli_nt_error(cli1)), cli_errstr(cli1));
+ correct = False;
+ }
+ }
+ if (NT_STATUS_V(cli_nt_error(cli1)) != NT_STATUS_V(NT_STATUS_ACCESS_DENIED)) {
+ printf("[%d] trunc open 0x%x -> 0x%x failed with wrong error code %s\n",
+ k, open_attrs_table[i], open_attrs_table[j],
+ cli_errstr(cli1));
+ correct = False;
+ }
+#if 0
+ printf("[%d] trunc open 0x%x -> 0x%x failed\n", k, open_attrs_table[i], open_attrs_table[j]);
+#endif
+ k++;
+ continue;
+ }
+
+ if (!cli_close(cli1, fnum1)) {
+ printf("close %d (2) of %s failed (%s)\n", j, fname, cli_errstr(cli1));
+ return False;
+ }
+
+ if (!cli_getatr(cli1, fname, &attr, NULL, NULL)) {
+ printf("getatr(2) failed (%s)\n", cli_errstr(cli1));
+ return False;
+ }
+
+#if 0
+ printf("[%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 < sizeof(attr_results)/sizeof(struct trunc_open_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) {
+ printf("getatr check failed. [0x%x] trunc [0x%x] got attr 0x%x, should be 0x%x\n",
+ open_attrs_table[i],
+ open_attrs_table[j],
+ (unsigned int)attr,
+ attr_results[l].result_attr);
+ correct = False;
+ }
+ break;
+ }
+ }
+ k++;
+ }
+ }
+
+ cli_setatr(cli1, fname, 0, 0);
+ cli_unlink(cli1, fname);
+
+ printf("open attr test %s.\n", correct ? "passed" : "failed");
+
+ if (!torture_close_connection(cli1)) {
+ correct = False;
+ }
+ return correct;
+}
+
+static void list_fn(file_info *finfo, const char *name, void *state)
+{
+
+}
+
+/*
+ test directory listing speed
+ */
+static BOOL run_dirtest(int dummy)
+{
+ int i;
+ static struct cli_state *cli;
+ int fnum;
+ double t1;
+ BOOL correct = True;
+
+ printf("starting directory test\n");
+
+ if (!torture_open_connection(&cli)) {
+ return False;
+ }
+
+ cli_sockopt(cli, sockops);
+
+ srandom(0);
+ for (i=0;i<torture_numops;i++) {
+ fstring fname;
+ slprintf(fname, sizeof(fname), "\\%x", (int)random());
+ fnum = cli_open(cli, fname, O_RDWR|O_CREAT, DENY_NONE);
+ if (fnum == -1) {
+ fprintf(stderr,"Failed to open %s\n", fname);
+ return False;
+ }
+ cli_close(cli, fnum);
+ }
+
+ t1 = end_timer();
+
+ printf("Matched %d\n", cli_list(cli, "a*.*", 0, list_fn, NULL));
+ printf("Matched %d\n", cli_list(cli, "b*.*", 0, list_fn, NULL));
+ printf("Matched %d\n", cli_list(cli, "xyzabc", 0, list_fn, NULL));
+
+ printf("dirtest core %g seconds\n", end_timer() - t1);
+
+ srandom(0);
+ for (i=0;i<torture_numops;i++) {
+ fstring fname;
+ slprintf(fname, sizeof(fname), "\\%x", (int)random());
+ cli_unlink(cli, fname);
+ }
+
+ if (!torture_close_connection(cli)) {
+ correct = False;
+ }
+
+ printf("finished dirtest\n");
+
+ return correct;
+}
+
+static void del_fn(file_info *finfo, const char *mask, void *state)
+{
+ struct cli_state *pcli = (struct cli_state *)state;
+ fstring fname;
+ slprintf(fname, sizeof(fname), "\\LISTDIR\\%s", finfo->name);
+
+ if (strcmp(finfo->name, ".") == 0 || strcmp(finfo->name, "..") == 0)
+ return;
+
+ if (finfo->mode & aDIR) {
+ if (!cli_rmdir(pcli, fname))
+ printf("del_fn: failed to rmdir %s\n,", fname );
+ } else {
+ if (!cli_unlink(pcli, fname))
+ printf("del_fn: failed to unlink %s\n,", fname );
+ }
+}
+
+
+/*
+ sees what IOCTLs are supported
+ */
+BOOL torture_ioctl_test(int dummy)
+{
+ static struct cli_state *cli;
+ uint16 device, function;
+ int fnum;
+ const char *fname = "\\ioctl.dat";
+ DATA_BLOB blob;
+ NTSTATUS status;
+
+ if (!torture_open_connection(&cli)) {
+ return False;
+ }
+
+ printf("starting ioctl test\n");
+
+ cli_unlink(cli, fname);
+
+ fnum = cli_open(cli, fname, O_RDWR|O_CREAT|O_EXCL, DENY_NONE);
+ if (fnum == -1) {
+ printf("open of %s failed (%s)\n", fname, cli_errstr(cli));
+ return False;
+ }
+
+ status = cli_raw_ioctl(cli, fnum, 0x2d0000 | (0x0420<<2), &blob);
+ printf("ioctl device info: %s\n", cli_errstr(cli));
+
+ status = cli_raw_ioctl(cli, fnum, IOCTL_QUERY_JOB_INFO, &blob);
+ printf("ioctl job info: %s\n", cli_errstr(cli));
+
+ for (device=0;device<0x100;device++) {
+ printf("testing device=0x%x\n", device);
+ for (function=0;function<0x100;function++) {
+ uint32 code = (device<<16) | function;
+
+ status = cli_raw_ioctl(cli, fnum, code, &blob);
+
+ if (NT_STATUS_IS_OK(status)) {
+ printf("ioctl 0x%x OK : %d bytes\n", code, blob.length);
+ data_blob_free(&blob);
+ }
+ }
+ }
+
+ if (!torture_close_connection(cli)) {
+ return False;
+ }
+
+ return True;
+}
+
+
+/*
+ tries varients of chkpath
+ */
+BOOL torture_chkpath_test(int dummy)
+{
+ static struct cli_state *cli;
+ int fnum;
+ BOOL ret;
+
+ if (!torture_open_connection(&cli)) {
+ return False;
+ }
+
+ printf("starting chkpath test\n");
+
+ /* cleanup from an old run */
+ cli_rmdir(cli, "\\chkpath.dir\\dir2");
+ cli_unlink(cli, "\\chkpath.dir\\*");
+ cli_rmdir(cli, "\\chkpath.dir");
+
+ if (!cli_mkdir(cli, "\\chkpath.dir")) {
+ printf("mkdir1 failed : %s\n", cli_errstr(cli));
+ return False;
+ }
+
+ if (!cli_mkdir(cli, "\\chkpath.dir\\dir2")) {
+ printf("mkdir2 failed : %s\n", cli_errstr(cli));
+ return False;
+ }
+
+ fnum = cli_open(cli, "\\chkpath.dir\\foo.txt", O_RDWR|O_CREAT|O_EXCL, DENY_NONE);
+ if (fnum == -1) {
+ printf("open1 failed (%s)\n", cli_errstr(cli));
+ return False;
+ }
+ cli_close(cli, fnum);
+
+ if (!cli_chkpath(cli, "\\chkpath.dir")) {
+ printf("chkpath1 failed: %s\n", cli_errstr(cli));
+ ret = False;
+ }
+
+ if (!cli_chkpath(cli, "\\chkpath.dir\\dir2")) {
+ printf("chkpath2 failed: %s\n", cli_errstr(cli));
+ ret = False;
+ }
+
+ if (!cli_chkpath(cli, "\\chkpath.dir\\foo.txt")) {
+ ret = check_error(__LINE__, cli, ERRDOS, ERRbadpath,
+ NT_STATUS_NOT_A_DIRECTORY);
+ } else {
+ printf("* chkpath on a file should fail\n");
+ ret = False;
+ }
+
+ if (!cli_chkpath(cli, "\\chkpath.dir\\bar.txt")) {
+ ret = check_error(__LINE__, cli, ERRDOS, ERRbadfile,
+ NT_STATUS_OBJECT_NAME_NOT_FOUND);
+ } else {
+ printf("* chkpath on a non existant file should fail\n");
+ ret = False;
+ }
+
+ if (!cli_chkpath(cli, "\\chkpath.dir\\dirxx\\bar.txt")) {
+ ret = check_error(__LINE__, cli, ERRDOS, ERRbadpath,
+ NT_STATUS_OBJECT_PATH_NOT_FOUND);
+ } else {
+ printf("* chkpath on a non existent component should fail\n");
+ ret = False;
+ }
+
+ cli_rmdir(cli, "\\chkpath.dir\\dir2");
+ cli_unlink(cli, "\\chkpath.dir\\*");
+ cli_rmdir(cli, "\\chkpath.dir");
+
+ if (!torture_close_connection(cli)) {
+ return False;
+ }
+
+ return ret;
+}
+
+static BOOL run_eatest(int dummy)
+{
+ static struct cli_state *cli;
+ const char *fname = "\\eatest.txt";
+ BOOL correct = True;
+ int fnum, i;
+ size_t num_eas;
+ struct ea_struct *ea_list = NULL;
+ TALLOC_CTX *mem_ctx = talloc_init("eatest");
+
+ printf("starting eatest\n");
+
+ if (!torture_open_connection(&cli)) {
+ return False;
+ }
+
+ cli_unlink(cli, fname);
+ fnum = cli_nt_create_full(cli, fname, 0,
+ FIRST_DESIRED_ACCESS, FILE_ATTRIBUTE_ARCHIVE,
+ FILE_SHARE_NONE, FILE_OVERWRITE_IF,
+ 0x4044, 0);
+
+ if (fnum == -1) {
+ printf("open failed - %s\n", cli_errstr(cli));
+ return False;
+ }
+
+ for (i = 0; i < 10; i++) {
+ fstring ea_name, ea_val;
+
+ slprintf(ea_name, sizeof(ea_name), "EA_%d", i);
+ memset(ea_val, (char)i+1, i+1);
+ if (!cli_set_ea_fnum(cli, fnum, ea_name, ea_val, i+1)) {
+ printf("ea_set of name %s failed - %s\n", ea_name, cli_errstr(cli));
+ return False;
+ }
+ }
+
+ cli_close(cli, fnum);
+ for (i = 0; i < 10; i++) {
+ fstring ea_name, ea_val;
+
+ slprintf(ea_name, sizeof(ea_name), "EA_%d", i+10);
+ memset(ea_val, (char)i+1, i+1);
+ if (!cli_set_ea_path(cli, fname, ea_name, ea_val, i+1)) {
+ printf("ea_set of name %s failed - %s\n", ea_name, cli_errstr(cli));
+ return False;
+ }
+ }
+
+ if (!cli_get_ea_list_path(cli, fname, mem_ctx, &num_eas, &ea_list)) {
+ printf("ea_get list failed - %s\n", cli_errstr(cli));
+ correct = False;
+ }
+
+ printf("num_eas = %d\n", num_eas);
+
+ if (num_eas != 20) {
+ printf("Should be 20 EA's stored... failing.\n");
+ correct = False;
+ }
+
+ for (i = 0; i < num_eas; i++) {
+ printf("%d: ea_name = %s. Val = ", i, ea_list[i].name);
+ dump_data(0, ea_list[i].value.data, ea_list[i].value.length);
+ }
+
+ /* Setting EA's to zero length deletes them. Test this */
+ printf("Now deleting all EA's....\n");
+
+ for (i = 0; i < 20; i++) {
+ fstring ea_name;
+ slprintf(ea_name, sizeof(ea_name), "EA_%d", i);
+ if (!cli_set_ea_path(cli, fname, ea_name, "", 0)) {
+ printf("ea_set of name %s failed - %s\n", ea_name, cli_errstr(cli));
+ return False;
+ }
+ }
+
+ if (!cli_get_ea_list_path(cli, fname, mem_ctx, &num_eas, &ea_list)) {
+ printf("ea_get list failed - %s\n", cli_errstr(cli));
+ correct = False;
+ }
+
+ printf("num_eas = %d\n", num_eas);
+ for (i = 0; i < num_eas; i++) {
+ printf("%d: ea_name = %s. Val = ", i, ea_list[i].name);
+ dump_data(0, ea_list[i].value.data, ea_list[i].value.length);
+ }
+
+ if (num_eas != 0) {
+ printf("deleting EA's failed.\n");
+ correct = False;
+ }
+
+ /* Try and delete a non existant EA. */
+ if (!cli_set_ea_path(cli, fname, "foo", "", 0)) {
+ printf("deleting non-existant EA 'foo' should succeed. %s\n", cli_errstr(cli));
+ correct = False;
+ }
+
+ talloc_destroy(mem_ctx);
+ if (!torture_close_connection(cli)) {
+ correct = False;
+ }
+
+ return correct;
+}
+
+static BOOL run_dirtest1(int dummy)
+{
+ int i;
+ static struct cli_state *cli;
+ int fnum, num_seen;
+ BOOL correct = True;
+
+ printf("starting directory test\n");
+
+ if (!torture_open_connection(&cli)) {
+ return False;
+ }
+
+ cli_sockopt(cli, sockops);
+
+ cli_list(cli, "\\LISTDIR\\*", 0, del_fn, cli);
+ cli_list(cli, "\\LISTDIR\\*", aDIR, del_fn, cli);
+ cli_rmdir(cli, "\\LISTDIR");
+ cli_mkdir(cli, "\\LISTDIR");
+
+ /* Create 1000 files and 1000 directories. */
+ for (i=0;i<1000;i++) {
+ fstring fname;
+ slprintf(fname, sizeof(fname), "\\LISTDIR\\f%d", i);
+ fnum = cli_nt_create_full(cli, fname, 0, GENERIC_ALL_ACCESS, FILE_ATTRIBUTE_ARCHIVE,
+ FILE_SHARE_READ|FILE_SHARE_WRITE, FILE_OVERWRITE_IF, 0, 0);
+ if (fnum == -1) {
+ fprintf(stderr,"Failed to open %s\n", fname);
+ return False;
+ }
+ cli_close(cli, fnum);
+ }
+ for (i=0;i<1000;i++) {
+ fstring fname;
+ slprintf(fname, sizeof(fname), "\\LISTDIR\\d%d", i);
+ if (!cli_mkdir(cli, fname)) {
+ fprintf(stderr,"Failed to open %s\n", fname);
+ return False;
+ }
+ }
+
+ /* Now ensure that doing an old list sees both files and directories. */
+ num_seen = cli_list_old(cli, "\\LISTDIR\\*", aDIR, list_fn, NULL);
+ printf("num_seen = %d\n", num_seen );
+ /* We should see 100 files + 1000 directories + . and .. */
+ if (num_seen != 2002)
+ correct = False;
+
+ /* Ensure if we have the "must have" bits we only see the
+ * relevent entries.
+ */
+ num_seen = cli_list_old(cli, "\\LISTDIR\\*", (aDIR<<8)|aDIR, list_fn, NULL);
+ printf("num_seen = %d\n", num_seen );
+ if (num_seen != 1002)
+ correct = False;
+
+ num_seen = cli_list_old(cli, "\\LISTDIR\\*", (aARCH<<8)|aDIR, list_fn, NULL);
+ printf("num_seen = %d\n", num_seen );
+ if (num_seen != 1000)
+ correct = False;
+
+ /* Delete everything. */
+ cli_list(cli, "\\LISTDIR\\*", 0, del_fn, cli);
+ cli_list(cli, "\\LISTDIR\\*", aDIR, del_fn, cli);
+ cli_rmdir(cli, "\\LISTDIR");
+
+#if 0
+ printf("Matched %d\n", cli_list(cli, "a*.*", 0, list_fn, NULL));
+ printf("Matched %d\n", cli_list(cli, "b*.*", 0, list_fn, NULL));
+ printf("Matched %d\n", cli_list(cli, "xyzabc", 0, list_fn, NULL));
+#endif
+
+ if (!torture_close_connection(cli)) {
+ correct = False;
+ }
+
+ printf("finished dirtest1\n");
+
+ return correct;
+}
+
+static BOOL run_error_map_extract(int dummy) {
+
+ static struct cli_state c_dos;
+ static struct cli_state c_nt;
+
+ uint32 error;
+
+ uint32 flgs2, errnum;
+ uint8 errclass;
+
+ NTSTATUS nt_status;
+
+ fstring user;
+
+ /* NT-Error connection */
+
+ if (!open_nbt_connection(&c_nt)) {
+ return False;
+ }
+
+ c_nt.use_spnego = False;
+
+ if (!cli_negprot(&c_nt)) {
+ printf("%s rejected the NT-error negprot (%s)\n",host, cli_errstr(&c_nt));
+ cli_shutdown(&c_nt);
+ return False;
+ }
+
+ if (!cli_session_setup(&c_nt, "", "", 0, "", 0,
+ workgroup)) {
+ printf("%s rejected the NT-error initial session setup (%s)\n",host, cli_errstr(&c_nt));
+ return False;
+ }
+
+ /* DOS-Error connection */
+
+ if (!open_nbt_connection(&c_dos)) {
+ return False;
+ }
+
+ c_dos.use_spnego = False;
+ c_dos.force_dos_errors = True;
+
+ if (!cli_negprot(&c_dos)) {
+ printf("%s rejected the DOS-error negprot (%s)\n",host, cli_errstr(&c_dos));
+ cli_shutdown(&c_dos);
+ return False;
+ }
+
+ if (!cli_session_setup(&c_dos, "", "", 0, "", 0,
+ workgroup)) {
+ printf("%s rejected the DOS-error initial session setup (%s)\n",host, cli_errstr(&c_dos));
+ return False;
+ }
+
+ for (error=(0xc0000000 | 0x1); error < (0xc0000000| 0xFFF); error++) {
+ fstr_sprintf(user, "%X", error);
+
+ if (cli_session_setup(&c_nt, user,
+ password, strlen(password),
+ password, strlen(password),
+ workgroup)) {
+ printf("/** Session setup succeeded. This shouldn't happen...*/\n");
+ }
+
+ flgs2 = SVAL(c_nt.inbuf,smb_flg2);
+
+ /* Case #1: 32-bit NT errors */
+ if (flgs2 & FLAGS2_32_BIT_ERROR_CODES) {
+ nt_status = NT_STATUS(IVAL(c_nt.inbuf,smb_rcls));
+ } else {
+ printf("/** Dos error on NT connection! (%s) */\n",
+ cli_errstr(&c_nt));
+ nt_status = NT_STATUS(0xc0000000);
+ }
+
+ if (cli_session_setup(&c_dos, user,
+ password, strlen(password),
+ password, strlen(password),
+ workgroup)) {
+ printf("/** Session setup succeeded. This shouldn't happen...*/\n");
+ }
+ flgs2 = SVAL(c_dos.inbuf,smb_flg2), errnum;
+
+ /* Case #1: 32-bit NT errors */
+ if (flgs2 & FLAGS2_32_BIT_ERROR_CODES) {
+ printf("/** NT error on DOS connection! (%s) */\n",
+ cli_errstr(&c_nt));
+ errnum = errclass = 0;
+ } else {
+ cli_dos_error(&c_dos, &errclass, &errnum);
+ }
+
+ if (NT_STATUS_V(nt_status) != error) {
+ printf("/*\t{ This NT error code was 'sqashed'\n\t from %s to %s \n\t during the session setup }\n*/\n",
+ get_nt_error_c_code(NT_STATUS(error)),
+ get_nt_error_c_code(nt_status));
+ }
+
+ printf("\t{%s,\t%s,\t%s},\n",
+ smb_dos_err_class(errclass),
+ smb_dos_err_name(errclass, errnum),
+ get_nt_error_c_code(NT_STATUS(error)));
+ }
+ return True;
+}
+
+static double create_procs(BOOL (*fn)(int), BOOL *result)
+{
+ int i, status;
+ volatile pid_t *child_status;
+ volatile BOOL *child_status_out;
+ int synccount;
+ int tries = 8;
+
+ synccount = 0;
+
+ child_status = (volatile pid_t *)shm_setup(sizeof(pid_t)*nprocs);
+ if (!child_status) {
+ printf("Failed to setup shared memory\n");
+ return -1;
+ }
+
+ child_status_out = (volatile BOOL *)shm_setup(sizeof(BOOL)*nprocs);
+ if (!child_status_out) {
+ printf("Failed to setup result status shared memory\n");
+ return -1;
+ }
+
+ for (i = 0; i < nprocs; i++) {
+ child_status[i] = 0;
+ child_status_out[i] = True;
+ }
+
+ start_timer();
+
+ for (i=0;i<nprocs;i++) {
+ procnum = i;
+ if (fork() == 0) {
+ pid_t mypid = getpid();
+ sys_srandom(((int)mypid) ^ ((int)time(NULL)));
+
+ slprintf(myname,sizeof(myname),"CLIENT%d", i);
+
+ while (1) {
+ if (torture_open_connection(&current_cli)) break;
+ if (tries-- == 0) {
+ printf("pid %d failed to start\n", (int)getpid());
+ _exit(1);
+ }
+ smb_msleep(10);
+ }
+
+ child_status[i] = getpid();
+
+ while (child_status[i] && end_timer() < 5) smb_msleep(2);
+
+ child_status_out[i] = fn(i);
+ _exit(0);
+ }
+ }
+
+ do {
+ synccount = 0;
+ for (i=0;i<nprocs;i++) {
+ if (child_status[i]) synccount++;
+ }
+ if (synccount == nprocs) break;
+ smb_msleep(10);
+ } while (end_timer() < 30);
+
+ if (synccount != nprocs) {
+ printf("FAILED TO START %d CLIENTS (started %d)\n", nprocs, synccount);
+ *result = False;
+ return end_timer();
+ }
+
+ /* start the client load */
+ start_timer();
+
+ for (i=0;i<nprocs;i++) {
+ child_status[i] = 0;
+ }
+
+ printf("%d clients started\n", nprocs);
+
+ for (i=0;i<nprocs;i++) {
+ while (waitpid(0, &status, 0) == -1 && errno == EINTR) /* noop */ ;
+ }
+
+ printf("\n");
+
+ for (i=0;i<nprocs;i++) {
+ if (!child_status_out[i]) {
+ *result = False;
+ }
+ }
+ return end_timer();
+}
+
+#define FLAG_MULTIPROC 1
+
+static struct {
+ const char *name;
+ BOOL (*fn)(int);
+ unsigned flags;
+} torture_ops[] = {
+ {"FDPASS", run_fdpasstest, 0},
+ {"LOCK1", run_locktest1, 0},
+ {"LOCK2", run_locktest2, 0},
+ {"LOCK3", run_locktest3, 0},
+ {"LOCK4", run_locktest4, 0},
+ {"LOCK5", run_locktest5, 0},
+ {"LOCK6", run_locktest6, 0},
+ {"LOCK7", run_locktest7, 0},
+ {"UNLINK", run_unlinktest, 0},
+ {"BROWSE", run_browsetest, 0},
+ {"ATTR", run_attrtest, 0},
+ {"TRANS2", run_trans2test, 0},
+ {"MAXFID", run_maxfidtest, FLAG_MULTIPROC},
+ {"TORTURE",run_torture, FLAG_MULTIPROC},
+ {"RANDOMIPC", run_randomipc, 0},
+ {"NEGNOWAIT", run_negprot_nowait, 0},
+ {"NBENCH", run_nbench, 0},
+ {"OPLOCK1", run_oplock1, 0},
+ {"OPLOCK2", run_oplock2, 0},
+ {"OPLOCK3", run_oplock3, 0},
+ {"DIR", run_dirtest, 0},
+ {"DIR1", run_dirtest1, 0},
+ {"DENY1", torture_denytest1, 0},
+ {"DENY2", torture_denytest2, 0},
+ {"TCON", run_tcon_test, 0},
+ {"TCONDEV", run_tcon_devtype_test, 0},
+ {"RW1", run_readwritetest, 0},
+ {"RW2", run_readwritemulti, FLAG_MULTIPROC},
+ {"RW3", run_readwritelarge, 0},
+ {"OPEN", run_opentest, 0},
+#if 1
+ {"OPENATTR", run_openattrtest, 0},
+#endif
+ {"XCOPY", run_xcopy, 0},
+ {"RENAME", run_rename, 0},
+ {"DELETE", run_deletetest, 0},
+ {"PROPERTIES", run_properties, 0},
+ {"MANGLE", torture_mangle, 0},
+ {"W2K", run_w2ktest, 0},
+ {"TRANS2SCAN", torture_trans2_scan, 0},
+ {"NTTRANSSCAN", torture_nttrans_scan, 0},
+ {"UTABLE", torture_utable, 0},
+ {"CASETABLE", torture_casetable, 0},
+ {"ERRMAPEXTRACT", run_error_map_extract, 0},
+ {"PIPE_NUMBER", run_pipe_number, 0},
+ {"TCON2", run_tcon2_test, 0},
+ {"IOCTL", torture_ioctl_test, 0},
+ {"CHKPATH", torture_chkpath_test, 0},
+ {"FDSESS", run_fdsesstest, 0},
+ { "EATEST", run_eatest, 0},
+ {NULL, NULL, 0}};
+
+
+
+/****************************************************************************
+run a specified test or "ALL"
+****************************************************************************/
+static BOOL run_test(const char *name)
+{
+ BOOL ret = True;
+ BOOL result = True;
+ BOOL found = False;
+ int i;
+ double t;
+ if (strequal(name,"ALL")) {
+ for (i=0;torture_ops[i].name;i++) {
+ run_test(torture_ops[i].name);
+ }
+ found = True;
+ }
+
+ for (i=0;torture_ops[i].name;i++) {
+ fstr_sprintf(randomfname, "\\XX%x",
+ (unsigned)random());
+
+ if (strequal(name, torture_ops[i].name)) {
+ found = True;
+ printf("Running %s\n", name);
+ if (torture_ops[i].flags & FLAG_MULTIPROC) {
+ t = create_procs(torture_ops[i].fn, &result);
+ if (!result) {
+ ret = False;
+ printf("TEST %s FAILED!\n", name);
+ }
+
+ } else {
+ start_timer();
+ if (!torture_ops[i].fn(0)) {
+ ret = False;
+ printf("TEST %s FAILED!\n", name);
+ }
+ t = end_timer();
+ }
+ printf("%s took %g secs\n\n", name, t);
+ }
+ }
+
+ if (!found) {
+ printf("Did not find a test named %s\n", name);
+ ret = False;
+ }
+
+ return ret;
+}
+
+
+static void usage(void)
+{
+ int i;
+
+ printf("Usage: smbtorture //server/share <options> TEST1 TEST2 ...\n");
+
+ printf("\t-d debuglevel\n");
+ printf("\t-U user%%pass\n");
+ printf("\t-k use kerberos\n");
+ printf("\t-N numprocs\n");
+ printf("\t-n my_netbios_name\n");
+ printf("\t-W workgroup\n");
+ printf("\t-o num_operations\n");
+ printf("\t-O socket_options\n");
+ printf("\t-m maximum protocol\n");
+ printf("\t-L use oplocks\n");
+ printf("\t-c CLIENT.TXT specify client load file for NBENCH\n");
+ printf("\t-A showall\n");
+ printf("\t-p port\n");
+ printf("\t-s seed\n");
+ printf("\n\n");
+
+ printf("tests are:");
+ for (i=0;torture_ops[i].name;i++) {
+ printf(" %s", torture_ops[i].name);
+ }
+ printf("\n");
+
+ printf("default test is ALL\n");
+
+ exit(1);
+}
+
+/****************************************************************************
+ main program
+****************************************************************************/
+ int main(int argc,char *argv[])
+{
+ int opt, i;
+ char *p;
+ int gotuser = 0;
+ int gotpass = 0;
+ extern char *optarg;
+ extern int optind;
+ BOOL correct = True;
+
+ dbf = x_stdout;
+
+#ifdef HAVE_SETBUFFER
+ setbuffer(stdout, NULL, 0);
+#endif
+
+ lp_load(dyn_CONFIGFILE,True,False,False);
+ load_interfaces();
+
+ if (argc < 2) {
+ usage();
+ }
+
+ for(p = argv[1]; *p; p++)
+ if(*p == '\\')
+ *p = '/';
+
+ if (strncmp(argv[1], "//", 2)) {
+ usage();
+ }
+
+ fstrcpy(host, &argv[1][2]);
+ p = strchr_m(&host[2],'/');
+ if (!p) {
+ usage();
+ }
+ *p = 0;
+ fstrcpy(share, p+1);
+
+ get_myname(myname);
+
+ if (*username == 0 && getenv("LOGNAME")) {
+ fstrcpy(username,getenv("LOGNAME"));
+ }
+
+ argc--;
+ argv++;
+
+ srandom(time(NULL));
+
+ fstrcpy(workgroup, lp_workgroup());
+
+ while ((opt = getopt(argc, argv, "p:hW:U:n:N:O:o:m:Ld:Ac:ks:")) != EOF) {
+ switch (opt) {
+ case 'p':
+ port_to_use = atoi(optarg);
+ break;
+ case 's':
+ srandom(atoi(optarg));
+ break;
+ case 'W':
+ fstrcpy(workgroup,optarg);
+ break;
+ case 'm':
+ max_protocol = interpret_protocol(optarg, max_protocol);
+ break;
+ case 'N':
+ nprocs = atoi(optarg);
+ break;
+ case 'o':
+ torture_numops = atoi(optarg);
+ break;
+ case 'd':
+ DEBUGLEVEL = atoi(optarg);
+ break;
+ case 'O':
+ sockops = optarg;
+ break;
+ case 'L':
+ use_oplocks = True;
+ break;
+ case 'A':
+ torture_showall = True;
+ break;
+ case 'n':
+ fstrcpy(myname, optarg);
+ break;
+ case 'c':
+ client_txt = optarg;
+ break;
+ case 'k':
+#ifdef HAVE_KRB5
+ use_kerberos = True;
+#else
+ d_printf("No kerberos support compiled in\n");
+ exit(1);
+#endif
+ break;
+ case 'U':
+ gotuser = 1;
+ fstrcpy(username,optarg);
+ p = strchr_m(username,'%');
+ if (p) {
+ *p = 0;
+ fstrcpy(password, p+1);
+ gotpass = 1;
+ }
+ break;
+ default:
+ printf("Unknown option %c (%d)\n", (char)opt, opt);
+ usage();
+ }
+ }
+
+ if(use_kerberos && !gotuser) gotpass = True;
+
+ while (!gotpass) {
+ p = getpass("Password:");
+ if (p) {
+ fstrcpy(password, p);
+ gotpass = 1;
+ }
+ }
+
+ printf("host=%s share=%s user=%s myname=%s\n",
+ host, share, username, myname);
+
+ if (argc == optind) {
+ correct = run_test("ALL");
+ } else {
+ for (i=optind;i<argc;i++) {
+ if (!run_test(argv[i])) {
+ correct = False;
+ }
+ }
+ }
+
+ if (correct) {
+ return(0);
+ } else {
+ return(1);
+ }
+}
diff --git a/source/torture/utable.c b/source/torture/utable.c
new file mode 100644
index 00000000000..ba803a0da4f
--- /dev/null
+++ b/source/torture/utable.c
@@ -0,0 +1,193 @@
+/*
+ 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 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.
+*/
+
+#define NO_SYSLOG
+
+#include "includes.h"
+
+BOOL torture_utable(int dummy)
+{
+ struct cli_state *cli;
+ fstring fname, alt_name;
+ int fnum;
+ smb_ucs2_t c2;
+ int c, len, fd;
+ int chars_allowed=0, alt_allowed=0;
+ uint8 valid[0x10000];
+
+ printf("starting utable\n");
+
+ if (!torture_open_connection(&cli)) {
+ return False;
+ }
+
+ memset(valid, 0, sizeof(valid));
+
+ cli_mkdir(cli, "\\utable");
+ cli_unlink(cli, "\\utable\\*");
+
+ for (c=1; c < 0x10000; c++) {
+ char *p;
+
+ SSVAL(&c2, 0, c);
+ fstrcpy(fname, "\\utable\\x");
+ p = fname+strlen(fname);
+ len = convert_string(CH_UCS2, CH_UNIX,
+ &c2, 2,
+ p, sizeof(fname)-strlen(fname), True);
+ p[len] = 0;
+ fstrcat(fname,"_a_long_extension");
+
+ fnum = cli_open(cli, fname, O_RDWR | O_CREAT | O_TRUNC,
+ DENY_NONE);
+ if (fnum == -1) continue;
+
+ chars_allowed++;
+
+ cli_qpathinfo_alt_name(cli, fname, alt_name);
+
+ if (strncmp(alt_name, "X_A_L", 5) != 0) {
+ alt_allowed++;
+ valid[c] = 1;
+ d_printf("fname=[%s] alt_name=[%s]\n", fname, alt_name);
+ }
+
+ cli_close(cli, fnum);
+ cli_unlink(cli, fname);
+
+ if (c % 100 == 0) {
+ printf("%d (%d/%d)\r", c, chars_allowed, alt_allowed);
+ }
+ }
+ printf("%d (%d/%d)\n", c, chars_allowed, alt_allowed);
+
+ cli_rmdir(cli, "\\utable");
+
+ d_printf("%d chars allowed %d alt chars allowed\n", chars_allowed, alt_allowed);
+
+ fd = open("valid.dat", O_WRONLY|O_CREAT|O_TRUNC, 0644);
+ if (fd == -1) {
+ d_printf("Failed to create valid.dat - %s", strerror(errno));
+ return False;
+ }
+ write(fd, valid, 0x10000);
+ close(fd);
+ d_printf("wrote valid.dat\n");
+
+ return True;
+}
+
+
+static char *form_name(int c)
+{
+ static fstring fname;
+ smb_ucs2_t c2;
+ char *p;
+ int len;
+
+ fstrcpy(fname, "\\utable\\");
+ p = fname+strlen(fname);
+ SSVAL(&c2, 0, c);
+
+ len = convert_string(CH_UCS2, CH_UNIX,
+ &c2, 2,
+ p, sizeof(fname)-strlen(fname), True);
+ p[len] = 0;
+ return fname;
+}
+
+BOOL torture_casetable(int dummy)
+{
+ static struct cli_state *cli;
+ char *fname;
+ int fnum;
+ int c, i;
+#define MAX_EQUIVALENCE 8
+ smb_ucs2_t equiv[0x10000][MAX_EQUIVALENCE];
+ printf("starting casetable\n");
+
+ if (!torture_open_connection(&cli)) {
+ return False;
+ }
+
+ memset(equiv, 0, sizeof(equiv));
+
+ cli_unlink(cli, "\\utable\\*");
+ cli_rmdir(cli, "\\utable");
+ if (!cli_mkdir(cli, "\\utable")) {
+ printf("Failed to create utable directory!\n");
+ return False;
+ }
+
+ for (c=1; c < 0x10000; c++) {
+ size_t size;
+
+ if (c == '.' || c == '\\') continue;
+
+ printf("%04x (%c)\n", c, isprint(c)?c:'.');
+
+ fname = form_name(c);
+ fnum = cli_nt_create_full(cli, fname, 0,
+ GENERIC_ALL_ACCESS,
+ FILE_ATTRIBUTE_NORMAL,
+ FILE_SHARE_NONE,
+ FILE_OPEN_IF, 0, 0);
+
+ if (fnum == -1) {
+ printf("Failed to create file with char %04x\n", c);
+ continue;
+ }
+
+ size = 0;
+
+ if (!cli_qfileinfo(cli, 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) {
+ printf("too many chars match?? size=%ld c=0x%04x\n",
+ (unsigned long)size, c);
+ cli_close(cli, fnum);
+ return False;
+ }
+
+ cli_read(cli, fnum, (char *)c2, 0, size);
+ printf("%04x: ", c);
+ equiv[c][0] = c;
+ for (i=0; i<size/sizeof(int); i++) {
+ printf("%04x ", c2[i]);
+ equiv[c][i+1] = c2[i];
+ }
+ printf("\n");
+ fflush(stdout);
+ }
+
+ cli_write(cli, fnum, 0, (char *)&c, size, sizeof(c));
+ cli_close(cli, fnum);
+ }
+
+ cli_unlink(cli, "\\utable\\*");
+ cli_rmdir(cli, "\\utable");
+
+ return True;
+}
diff --git a/source/torture/vfstest.c b/source/torture/vfstest.c
new file mode 100644
index 00000000000..88fe3486493
--- /dev/null
+++ b/source/torture/vfstest.c
@@ -0,0 +1,571 @@
+/*
+ Unix SMB/CIFS implementation.
+ VFS module tester
+
+ Copyright (C) Simo Sorce 2002
+ Copyright (C) Eric Lorimer 2002
+ Copyright (C) Jelmer Vernooij 2002,2003
+
+ Most of this code was ripped off of rpcclient.
+ Copyright (C) Tim Potter 2000-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 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 "vfstest.h"
+
+/* List to hold groups of commands */
+static struct cmd_list {
+ struct cmd_list *prev, *next;
+ struct cmd_set *cmd_set;
+} *cmd_list;
+
+extern pstring user_socket_options;
+
+/****************************************************************************
+handle completion of commands for readline
+****************************************************************************/
+static char **completion_fn(char *text, int start, int end)
+{
+#define MAX_COMPLETIONS 100
+ char **matches;
+ int i, count=0;
+ struct cmd_list *commands = cmd_list;
+
+ if (start)
+ return NULL;
+
+ /* make sure we have a list of valid commands */
+ if (!commands)
+ return NULL;
+
+ matches = (char **)malloc(sizeof(matches[0])*MAX_COMPLETIONS);
+ if (!matches) return NULL;
+
+ matches[count++] = strdup(text);
+ if (!matches[0]) return NULL;
+
+ while (commands && count < MAX_COMPLETIONS-1)
+ {
+ if (!commands->cmd_set)
+ break;
+
+ for (i=0; commands->cmd_set[i].name; i++)
+ {
+ if ((strncmp(text, commands->cmd_set[i].name, strlen(text)) == 0) &&
+ commands->cmd_set[i].fn)
+ {
+ matches[count] = strdup(commands->cmd_set[i].name);
+ if (!matches[count])
+ return NULL;
+ count++;
+ }
+ }
+
+ commands = commands->next;
+
+ }
+
+ if (count == 2) {
+ SAFE_FREE(matches[0]);
+ matches[0] = strdup(matches[1]);
+ }
+ matches[count] = NULL;
+ return matches;
+}
+
+static char* next_command(char** cmdstr)
+{
+ static pstring command;
+ char *p;
+
+ if (!cmdstr || !(*cmdstr))
+ return NULL;
+
+ p = strchr_m(*cmdstr, ';');
+ if (p)
+ *p = '\0';
+ pstrcpy(command, *cmdstr);
+ *cmdstr = p;
+
+ return command;
+}
+
+/* Load specified configuration file */
+static NTSTATUS cmd_conf(struct vfs_state *vfs, TALLOC_CTX *mem_ctx,
+ int argc, const char **argv)
+{
+ if (argc != 2) {
+ printf("Usage: %s <smb.conf>\n", argv[0]);
+ return NT_STATUS_OK;
+ }
+
+ if (!lp_load(argv[1], False, True, False)) {
+ printf("Error loading \"%s\"\n", argv[1]);
+ return NT_STATUS_OK;
+ }
+
+ printf("\"%s\" successfully loaded\n", argv[1]);
+ return NT_STATUS_OK;
+}
+
+/* Display help on commands */
+static NTSTATUS cmd_help(struct vfs_state *vfs, TALLOC_CTX *mem_ctx,
+ int argc, const char **argv)
+{
+ struct cmd_list *tmp;
+ struct cmd_set *tmp_set;
+
+ /* Usage */
+ if (argc > 2) {
+ printf("Usage: %s [command]\n", argv[0]);
+ return NT_STATUS_OK;
+ }
+
+ /* Help on one command */
+
+ if (argc == 2) {
+ for (tmp = cmd_list; tmp; tmp = tmp->next) {
+
+ tmp_set = tmp->cmd_set;
+
+ while(tmp_set->name) {
+ if (strequal(argv[1], tmp_set->name)) {
+ if (tmp_set->usage &&
+ tmp_set->usage[0])
+ printf("%s\n", tmp_set->usage);
+ else
+ printf("No help for %s\n", tmp_set->name);
+
+ return NT_STATUS_OK;
+ }
+
+ tmp_set++;
+ }
+ }
+
+ printf("No such command: %s\n", argv[1]);
+ return NT_STATUS_OK;
+ }
+
+ /* List all commands */
+
+ for (tmp = cmd_list; tmp; tmp = tmp->next) {
+
+ tmp_set = tmp->cmd_set;
+
+ while(tmp_set->name) {
+
+ printf("%15s\t\t%s\n", tmp_set->name,
+ tmp_set->description ? tmp_set->description:
+ "");
+
+ tmp_set++;
+ }
+ }
+
+ return NT_STATUS_OK;
+}
+
+/* Change the debug level */
+static NTSTATUS cmd_debuglevel(struct vfs_state *vfs, TALLOC_CTX *mem_ctx, int argc, const char **argv)
+{
+ if (argc > 2) {
+ printf("Usage: %s [debuglevel]\n", argv[0]);
+ return NT_STATUS_OK;
+ }
+
+ if (argc == 2) {
+ DEBUGLEVEL = atoi(argv[1]);
+ }
+
+ printf("debuglevel is %d\n", DEBUGLEVEL);
+
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS cmd_freemem(struct vfs_state *vfs, TALLOC_CTX *mem_ctx, int argc, const char **argv)
+{
+ /* Cleanup */
+ talloc_destroy(mem_ctx);
+ mem_ctx = NULL;
+ vfs->data = NULL;
+ vfs->data_size = 0;
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS cmd_quit(struct vfs_state *vfs, TALLOC_CTX *mem_ctx, int argc, const char **argv)
+{
+ /* Cleanup */
+ talloc_destroy(mem_ctx);
+
+ exit(0);
+ return NT_STATUS_OK; /* NOTREACHED */
+}
+
+static struct cmd_set vfstest_commands[] = {
+
+ { "GENERAL OPTIONS" },
+
+ { "conf", cmd_conf, "Load smb configuration file", "conf <smb.conf>" },
+ { "help", cmd_help, "Get help on commands", "" },
+ { "?", cmd_help, "Get help on commands", "" },
+ { "debuglevel", cmd_debuglevel, "Set debug level", "" },
+ { "freemem", cmd_freemem, "Free currently allocated buffers", "" },
+ { "exit", cmd_quit, "Exit program", "" },
+ { "quit", cmd_quit, "Exit program", "" },
+
+ { NULL }
+};
+
+static struct cmd_set separator_command[] = {
+ { "---------------", NULL, "----------------------" },
+ { NULL }
+};
+
+
+extern struct cmd_set vfs_commands[];
+static struct cmd_set *vfstest_command_list[] = {
+ vfstest_commands,
+ vfs_commands,
+ NULL
+};
+
+static void add_command_set(struct cmd_set *cmd_set)
+{
+ struct cmd_list *entry;
+
+ if (!(entry = (struct cmd_list *)malloc(sizeof(struct cmd_list)))) {
+ DEBUG(0, ("out of memory\n"));
+ return;
+ }
+
+ ZERO_STRUCTP(entry);
+
+ entry->cmd_set = cmd_set;
+ DLIST_ADD(cmd_list, entry);
+}
+
+static NTSTATUS do_cmd(struct vfs_state *vfs, struct cmd_set *cmd_entry, char *cmd)
+{
+ const char *p = cmd;
+ char **argv = NULL;
+ NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+ pstring buf;
+ TALLOC_CTX *mem_ctx = NULL;
+ int argc = 0, i;
+
+ /* Count number of arguments first time through the loop then
+ allocate memory and strdup them. */
+
+ again:
+ while(next_token(&p, buf, " ", sizeof(buf))) {
+ if (argv) {
+ argv[argc] = strdup(buf);
+ }
+
+ argc++;
+ }
+
+ if (!argv) {
+
+ /* Create argument list */
+
+ argv = (char **)malloc(sizeof(char *) * argc);
+ memset(argv, 0, sizeof(char *) * argc);
+
+ if (!argv) {
+ fprintf(stderr, "out of memory\n");
+ result = NT_STATUS_NO_MEMORY;
+ goto done;
+ }
+
+ p = cmd;
+ argc = 0;
+
+ goto again;
+ }
+
+ /* Call the function */
+
+ if (cmd_entry->fn) {
+
+ if (mem_ctx == NULL) {
+ /* Create mem_ctx */
+ if (!(mem_ctx = talloc_init("do_cmd"))) {
+ DEBUG(0, ("talloc_init() failed\n"));
+ goto done;
+ }
+ }
+
+ /* Run command */
+ result = cmd_entry->fn(vfs, mem_ctx, argc, (const char **)argv);
+
+ } else {
+ fprintf (stderr, "Invalid command\n");
+ goto done;
+ }
+
+ done:
+
+ /* Cleanup */
+
+ if (argv) {
+ for (i = 0; i < argc; i++)
+ SAFE_FREE(argv[i]);
+
+ SAFE_FREE(argv);
+ }
+
+ return result;
+}
+
+/* Process a command entered at the prompt or as part of -c */
+static NTSTATUS process_cmd(struct vfs_state *vfs, char *cmd)
+{
+ struct cmd_list *temp_list;
+ BOOL found = False;
+ pstring buf;
+ const char *p = cmd;
+ NTSTATUS result = NT_STATUS_OK;
+ int len = 0;
+
+ if (cmd[strlen(cmd) - 1] == '\n')
+ cmd[strlen(cmd) - 1] = '\0';
+
+ if (!next_token(&p, buf, " ", sizeof(buf))) {
+ return NT_STATUS_OK;
+ }
+
+ /* strip the trainly \n if it exsists */
+ len = strlen(buf);
+ if (buf[len-1] == '\n')
+ buf[len-1] = '\0';
+
+ /* Search for matching commands */
+
+ for (temp_list = cmd_list; temp_list; temp_list = temp_list->next) {
+ struct cmd_set *temp_set = temp_list->cmd_set;
+
+ while(temp_set->name) {
+ if (strequal(buf, temp_set->name)) {
+ found = True;
+ result = do_cmd(vfs, temp_set, cmd);
+
+ goto done;
+ }
+ temp_set++;
+ }
+ }
+
+ done:
+ if (!found && buf[0]) {
+ printf("command not found: %s\n", buf);
+ return NT_STATUS_OK;
+ }
+
+ if (!NT_STATUS_IS_OK(result)) {
+ printf("result was %s\n", nt_errstr(result));
+ }
+
+ return result;
+}
+
+static void process_file(struct vfs_state *pvfs, char *filename) {
+ FILE *file;
+ char command[3 * PATH_MAX];
+
+ if (*filename == '-') {
+ file = stdin;
+ } else {
+ file = fopen(filename, "r");
+ if (file == NULL) {
+ printf("vfstest: error reading file (%s)!", filename);
+ printf("errno n.%d: %s", errno, strerror(errno));
+ exit(-1);
+ }
+ }
+
+ while (fgets(command, 3 * PATH_MAX, file) != NULL) {
+ process_cmd(pvfs, command);
+ }
+}
+
+void exit_server(const char *reason)
+{
+ DEBUG(3,("Server exit (%s)\n", (reason ? reason : "")));
+ exit(0);
+}
+
+static int server_fd = -1;
+int last_message = -1;
+
+int smbd_server_fd(void)
+{
+ return server_fd;
+}
+
+/****************************************************************************
+ Reload the services file.
+**************************************************************************/
+
+BOOL reload_services(BOOL test)
+{
+ BOOL ret;
+
+ if (lp_loaded()) {
+ pstring fname;
+ pstrcpy(fname,lp_configfile());
+ if (file_exist(fname, NULL) &&
+ !strcsequal(fname, dyn_CONFIGFILE)) {
+ pstrcpy(dyn_CONFIGFILE, fname);
+ test = False;
+ }
+ }
+
+ reopen_logs();
+
+ if (test && !lp_file_list_changed())
+ return(True);
+
+ lp_killunused(conn_snum_used);
+
+ ret = lp_load(dyn_CONFIGFILE, False, False, True);
+
+ load_printers();
+
+ /* perhaps the config filename is now set */
+ if (!test)
+ reload_services(True);
+
+ reopen_logs();
+
+ load_interfaces();
+
+ {
+ if (smbd_server_fd() != -1) {
+ set_socket_options(smbd_server_fd(),"SO_KEEPALIVE");
+ set_socket_options(smbd_server_fd(), user_socket_options);
+ }
+ }
+
+ mangle_reset_cache();
+ reset_stat_cache();
+
+ /* this forces service parameters to be flushed */
+ set_current_service(NULL,True);
+
+ return (ret);
+}
+
+/* Main function */
+
+int main(int argc, char *argv[])
+{
+ static char *cmdstr = NULL;
+ struct cmd_set **cmd_set;
+ static struct vfs_state vfs;
+ int i;
+ static char *filename = NULL;
+
+ /* 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
+ {"file", 'f', POPT_ARG_STRING, &filename, 0, },
+ {"command", 'c', POPT_ARG_STRING, &cmdstr, 0, "Execute specified list of commands" },
+ POPT_COMMON_SAMBA
+ POPT_TABLEEND
+ };
+
+
+ setlinebuf(stdout);
+
+ pc = poptGetContext("vfstest", argc, (const char **) argv,
+ long_options, 0);
+
+ while(poptGetNextOpt(pc) != -1);
+
+
+ poptFreeContext(pc);
+
+ /* TODO: check output */
+ reload_services(False);
+
+ /* the following functions are part of the Samba debugging
+ facilities. See lib/debug.c */
+ setup_logging("vfstest", True);
+
+ /* Load command lists */
+
+ cmd_set = vfstest_command_list;
+
+ while(*cmd_set) {
+ add_command_set(*cmd_set);
+ add_command_set(separator_command);
+ cmd_set++;
+ }
+
+ /* some basic initialization stuff */
+ sec_init();
+ conn_init();
+ vfs.conn = conn_new();
+ string_set(&vfs.conn->user,"vfstest");
+ for (i=0; i < 1024; i++)
+ vfs.files[i] = NULL;
+
+ /* some advanced initiliazation stuff */
+ smbd_vfs_init(vfs.conn);
+
+ /* Do we have a file input? */
+ if (filename && filename[0]) {
+ process_file(&vfs, filename);
+ return 0;
+ }
+
+ /* Do anything specified with -c */
+ if (cmdstr && cmdstr[0]) {
+ char *cmd;
+ char *p = cmdstr;
+
+ while((cmd=next_command(&p)) != NULL) {
+ process_cmd(&vfs, cmd);
+ }
+
+ return 0;
+ }
+
+ /* Loop around accepting commands */
+
+ while(1) {
+ pstring prompt;
+ char *line;
+
+ slprintf(prompt, sizeof(prompt) - 1, "vfstest $> ");
+
+ line = smb_readline(prompt, NULL, completion_fn);
+
+ if (line == NULL)
+ break;
+
+ if (line[0] != '\n')
+ process_cmd(&vfs, line);
+ }
+
+ free(vfs.conn);
+ return 0;
+}
diff --git a/source/torture/vfstest.h b/source/torture/vfstest.h
new file mode 100644
index 00000000000..1e030fad047
--- /dev/null
+++ b/source/torture/vfstest.h
@@ -0,0 +1,45 @@
+/*
+ Unix SMB/CIFS implementation.
+ VFS module tester
+
+ Copyright (C) Simo Sorce 2002
+ Copyright (C) Eric Lorimer 2002
+
+ Most of this code was ripped off of rpcclient.
+ Copyright (C) Tim Potter 2000-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 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.
+*/
+
+struct func_entry {
+ char *name;
+ int (*fn)(struct connection_struct *conn, const char *path);
+};
+
+struct vfs_state {
+ struct connection_struct *conn;
+ struct files_struct *files[1024];
+ DIR *currentdir;
+ void *data;
+ size_t data_size;
+};
+
+struct cmd_set {
+ const char *name;
+ NTSTATUS (*fn)(struct vfs_state *vfs, TALLOC_CTX *mem_ctx, int argc,
+ const char **argv);
+ const char *description;
+ const char *usage;
+};
diff --git a/source/ubiqx/.cvsignore b/source/ubiqx/.cvsignore
new file mode 100644
index 00000000000..07da2225c72
--- /dev/null
+++ b/source/ubiqx/.cvsignore
@@ -0,0 +1,3 @@
+*.po
+*.po32
+
diff --git a/source/ubiqx/COPYING.LIB b/source/ubiqx/COPYING.LIB
new file mode 100644
index 00000000000..8c8377da464
--- /dev/null
+++ b/source/ubiqx/COPYING.LIB
@@ -0,0 +1,481 @@
+ GNU LIBRARY GENERAL PUBLIC LICENSE
+ Version 2, June 1991
+
+ Copyright (C) 1991 Free Software Foundation, Inc.
+ 675 Mass Ave, Cambridge, MA 02139, USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+[This is the first released version of the library GPL. It is
+ numbered 2 because it goes with version 2 of the ordinary GPL.]
+
+ Preamble
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+Licenses are intended to guarantee your freedom to share and change
+free software--to make sure the software is free for all its users.
+
+ This license, the Library General Public License, applies to some
+specially designated Free Software Foundation software, and to any
+other libraries whose authors decide to use it. You can use it for
+your libraries, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+ To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if
+you distribute copies of the library, or if you modify it.
+
+ For example, if you distribute copies of the library, whether gratis
+or for a fee, you must give the recipients all the rights that we gave
+you. You must make sure that they, too, receive or can get the source
+code. If you link a program with the library, you must provide
+complete object files to the recipients so that they can relink them
+with the library, after making changes to the library and recompiling
+it. And you must show them these terms so they know their rights.
+
+ Our method of protecting your rights has two steps: (1) copyright
+the library, and (2) offer you this license which gives you legal
+permission to copy, distribute and/or modify the library.
+
+ Also, for each distributor's protection, we want to make certain
+that everyone understands that there is no warranty for this free
+library. If the library is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original
+version, so that any problems introduced by others will not reflect on
+the original authors' reputations.
+
+ Finally, any free program is threatened constantly by software
+patents. We wish to avoid the danger that companies distributing free
+software will individually obtain patent licenses, thus in effect
+transforming the program into proprietary software. To prevent this,
+we have made it clear that any patent must be licensed for everyone's
+free use or not licensed at all.
+
+ Most GNU software, including some libraries, is covered by the ordinary
+GNU General Public License, which was designed for utility programs. This
+license, the GNU Library General Public License, applies to certain
+designated libraries. This license is quite different from the ordinary
+one; be sure to read it in full, and don't assume that anything in it is
+the same as in the ordinary license.
+
+ The reason we have a separate public license for some libraries is that
+they blur the distinction we usually make between modifying or adding to a
+program and simply using it. Linking a program with a library, without
+changing the library, is in some sense simply using the library, and is
+analogous to running a utility program or application program. However, in
+a textual and legal sense, the linked executable is a combined work, a
+derivative of the original library, and the ordinary General Public License
+treats it as such.
+
+ Because of this blurred distinction, using the ordinary General
+Public License for libraries did not effectively promote software
+sharing, because most developers did not use the libraries. We
+concluded that weaker conditions might promote sharing better.
+
+ However, unrestricted linking of non-free programs would deprive the
+users of those programs of all benefit from the free status of the
+libraries themselves. This Library General Public License is intended to
+permit developers of non-free programs to use free libraries, while
+preserving your freedom as a user of such programs to change the free
+libraries that are incorporated in them. (We have not seen how to achieve
+this as regards changes in header files, but we have achieved it as regards
+changes in the actual functions of the Library.) The hope is that this
+will lead to faster development of free libraries.
+
+ The precise terms and conditions for copying, distribution and
+modification follow. Pay close attention to the difference between a
+"work based on the library" and a "work that uses the library". The
+former contains code derived from the library, while the latter only
+works together with the library.
+
+ Note that it is possible for a library to be covered by the ordinary
+General Public License rather than by this special one.
+
+ GNU LIBRARY GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License Agreement applies to any software library which
+contains a notice placed by the copyright holder or other authorized
+party saying it may be distributed under the terms of this Library
+General Public License (also called "this License"). Each licensee is
+addressed as "you".
+
+ A "library" means a collection of software functions and/or data
+prepared so as to be conveniently linked with application programs
+(which use some of those functions and data) to form executables.
+
+ The "Library", below, refers to any such software library or work
+which has been distributed under these terms. A "work based on the
+Library" means either the Library or any derivative work under
+copyright law: that is to say, a work containing the Library or a
+portion of it, either verbatim or with modifications and/or translated
+straightforwardly into another language. (Hereinafter, translation is
+included without limitation in the term "modification".)
+
+ "Source code" for a work means the preferred form of the work for
+making modifications to it. For a library, complete source code means
+all the source code for all modules it contains, plus any associated
+interface definition files, plus the scripts used to control compilation
+and installation of the library.
+
+ Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running a program using the Library is not restricted, and output from
+such a program is covered only if its contents constitute a work based
+on the Library (independent of the use of the Library in a tool for
+writing it). Whether that is true depends on what the Library does
+and what the program that uses the Library does.
+
+ 1. You may copy and distribute verbatim copies of the Library's
+complete source code as you receive it, in any medium, provided that
+you conspicuously and appropriately publish on each copy an
+appropriate copyright notice and disclaimer of warranty; keep intact
+all the notices that refer to this License and to the absence of any
+warranty; and distribute a copy of this License along with the
+Library.
+
+ You may charge a fee for the physical act of transferring a copy,
+and you may at your option offer warranty protection in exchange for a
+fee.
+
+ 2. You may modify your copy or copies of the Library or any portion
+of it, thus forming a work based on the Library, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+ a) The modified work must itself be a software library.
+
+ b) You must cause the files modified to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+ c) You must cause the whole of the work to be licensed at no
+ charge to all third parties under the terms of this License.
+
+ d) If a facility in the modified Library refers to a function or a
+ table of data to be supplied by an application program that uses
+ the facility, other than as an argument passed when the facility
+ is invoked, then you must make a good faith effort to ensure that,
+ in the event an application does not supply such function or
+ table, the facility still operates, and performs whatever part of
+ its purpose remains meaningful.
+
+ (For example, a function in a library to compute square roots has
+ a purpose that is entirely well-defined independent of the
+ application. Therefore, Subsection 2d requires that any
+ application-supplied function or table used by this function must
+ be optional: if the application does not supply it, the square
+ root function must still compute square roots.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Library,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Library, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote
+it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Library.
+
+In addition, mere aggregation of another work not based on the Library
+with the Library (or with a work based on the Library) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+ 3. You may opt to apply the terms of the ordinary GNU General Public
+License instead of this License to a given copy of the Library. To do
+this, you must alter all the notices that refer to this License, so
+that they refer to the ordinary GNU General Public License, version 2,
+instead of to this License. (If a newer version than version 2 of the
+ordinary GNU General Public License has appeared, then you can specify
+that version instead if you wish.) Do not make any other change in
+these notices.
+
+ Once this change is made in a given copy, it is irreversible for
+that copy, so the ordinary GNU General Public License applies to all
+subsequent copies and derivative works made from that copy.
+
+ This option is useful when you wish to copy part of the code of
+the Library into a program that is not a library.
+
+ 4. You may copy and distribute the Library (or a portion or
+derivative of it, under Section 2) in object code or executable form
+under the terms of Sections 1 and 2 above provided that you accompany
+it with the complete corresponding machine-readable source code, which
+must be distributed under the terms of Sections 1 and 2 above on a
+medium customarily used for software interchange.
+
+ If distribution of object code is made by offering access to copy
+from a designated place, then offering equivalent access to copy the
+source code from the same place satisfies the requirement to
+distribute the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+ 5. A program that contains no derivative of any portion of the
+Library, but is designed to work with the Library by being compiled or
+linked with it, is called a "work that uses the Library". Such a
+work, in isolation, is not a derivative work of the Library, and
+therefore falls outside the scope of this License.
+
+ However, linking a "work that uses the Library" with the Library
+creates an executable that is a derivative of the Library (because it
+contains portions of the Library), rather than a "work that uses the
+library". The executable is therefore covered by this License.
+Section 6 states terms for distribution of such executables.
+
+ When a "work that uses the Library" uses material from a header file
+that is part of the Library, the object code for the work may be a
+derivative work of the Library even though the source code is not.
+Whether this is true is especially significant if the work can be
+linked without the Library, or if the work is itself a library. The
+threshold for this to be true is not precisely defined by law.
+
+ If such an object file uses only numerical parameters, data
+structure layouts and accessors, and small macros and small inline
+functions (ten lines or less in length), then the use of the object
+file is unrestricted, regardless of whether it is legally a derivative
+work. (Executables containing this object code plus portions of the
+Library will still fall under Section 6.)
+
+ Otherwise, if the work is a derivative of the Library, you may
+distribute the object code for the work under the terms of Section 6.
+Any executables containing that work also fall under Section 6,
+whether or not they are linked directly with the Library itself.
+
+ 6. As an exception to the Sections above, you may also compile or
+link a "work that uses the Library" with the Library to produce a
+work containing portions of the Library, and distribute that work
+under terms of your choice, provided that the terms permit
+modification of the work for the customer's own use and reverse
+engineering for debugging such modifications.
+
+ You must give prominent notice with each copy of the work that the
+Library is used in it and that the Library and its use are covered by
+this License. You must supply a copy of this License. If the work
+during execution displays copyright notices, you must include the
+copyright notice for the Library among them, as well as a reference
+directing the user to the copy of this License. Also, you must do one
+of these things:
+
+ a) Accompany the work with the complete corresponding
+ machine-readable source code for the Library including whatever
+ changes were used in the work (which must be distributed under
+ Sections 1 and 2 above); and, if the work is an executable linked
+ with the Library, with the complete machine-readable "work that
+ uses the Library", as object code and/or source code, so that the
+ user can modify the Library and then relink to produce a modified
+ executable containing the modified Library. (It is understood
+ that the user who changes the contents of definitions files in the
+ Library will not necessarily be able to recompile the application
+ to use the modified definitions.)
+
+ b) Accompany the work with a written offer, valid for at
+ least three years, to give the same user the materials
+ specified in Subsection 6a, above, for a charge no more
+ than the cost of performing this distribution.
+
+ c) If distribution of the work is made by offering access to copy
+ from a designated place, offer equivalent access to copy the above
+ specified materials from the same place.
+
+ d) Verify that the user has already received a copy of these
+ materials or that you have already sent this user a copy.
+
+ For an executable, the required form of the "work that uses the
+Library" must include any data and utility programs needed for
+reproducing the executable from it. However, as a special exception,
+the source code distributed need not include anything that is normally
+distributed (in either source or binary form) with the major
+components (compiler, kernel, and so on) of the operating system on
+which the executable runs, unless that component itself accompanies
+the executable.
+
+ It may happen that this requirement contradicts the license
+restrictions of other proprietary libraries that do not normally
+accompany the operating system. Such a contradiction means you cannot
+use both them and the Library together in an executable that you
+distribute.
+
+ 7. You may place library facilities that are a work based on the
+Library side-by-side in a single library together with other library
+facilities not covered by this License, and distribute such a combined
+library, provided that the separate distribution of the work based on
+the Library and of the other library facilities is otherwise
+permitted, and provided that you do these two things:
+
+ a) Accompany the combined library with a copy of the same work
+ based on the Library, uncombined with any other library
+ facilities. This must be distributed under the terms of the
+ Sections above.
+
+ b) Give prominent notice with the combined library of the fact
+ that part of it is a work based on the Library, and explaining
+ where to find the accompanying uncombined form of the same work.
+
+ 8. You may not copy, modify, sublicense, link with, or distribute
+the Library except as expressly provided under this License. Any
+attempt otherwise to copy, modify, sublicense, link with, or
+distribute the Library is void, and will automatically terminate your
+rights under this License. However, parties who have received copies,
+or rights, from you under this License will not have their licenses
+terminated so long as such parties remain in full compliance.
+
+ 9. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Library or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Library (or any work based on the
+Library), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Library or works based on it.
+
+ 10. Each time you redistribute the Library (or any work based on the
+Library), the recipient automatically receives a license from the
+original licensor to copy, distribute, link with or modify the Library
+subject to these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+ 11. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Library at all. For example, if a patent
+license would not permit royalty-free redistribution of the Library by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Library.
+
+If any portion of this section is held invalid or unenforceable under any
+particular circumstance, the balance of the section is intended to apply,
+and the section as a whole is intended to apply in other circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+ 12. If the distribution and/or use of the Library is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Library under this License may add
+an explicit geographical distribution limitation excluding those countries,
+so that distribution is permitted only in or among countries not thus
+excluded. In such case, this License incorporates the limitation as if
+written in the body of this License.
+
+ 13. The Free Software Foundation may publish revised and/or new
+versions of the Library General Public License from time to time.
+Such new versions will be similar in spirit to the present version,
+but may differ in detail to address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Library
+specifies a version number of this License which applies to it and
+"any later version", you have the option of following the terms and
+conditions either of that version or of any later version published by
+the Free Software Foundation. If the Library does not specify a
+license version number, you may choose any version ever published by
+the Free Software Foundation.
+
+ 14. If you wish to incorporate parts of the Library into other free
+programs whose distribution conditions are incompatible with these,
+write to the author to ask for permission. For software which is
+copyrighted by the Free Software Foundation, write to the Free
+Software Foundation; we sometimes make exceptions for this. Our
+decision will be guided by the two goals of preserving the free status
+of all derivatives of our free software and of promoting the sharing
+and reuse of software generally.
+
+ NO WARRANTY
+
+ 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
+WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
+EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
+OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY
+KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
+LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
+THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+ 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
+WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
+AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU
+FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR
+CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE
+LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
+RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
+FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
+SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+DAMAGES.
+
+ END OF TERMS AND CONDITIONS
+
+ Appendix: How to Apply These Terms to Your New Libraries
+
+ If you develop a new library, and you want it to be of the greatest
+possible use to the public, we recommend making it free software that
+everyone can redistribute and change. You can do so by permitting
+redistribution under these terms (or, alternatively, under the terms of the
+ordinary General Public License).
+
+ To apply these terms, attach the following notices to the library. It is
+safest to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least the
+"copyright" line and a pointer to where the full notice is found.
+
+ <one line to give the library's name and a brief idea of what it does.>
+ Copyright (C) <year> <name of author>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 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 Library General Public
+ License along with this library; if not, write to the Free
+ Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+Also add information on how to contact you by electronic and paper mail.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the library, if
+necessary. Here is a sample; alter the names:
+
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the
+ library `Frob' (a library for tweaking knobs) written by James Random Hacker.
+
+ <signature of Ty Coon>, 1 April 1990
+ Ty Coon, President of Vice
+
+That's all there is to it!
diff --git a/source/ubiqx/README.UBI b/source/ubiqx/README.UBI
new file mode 100644
index 00000000000..a2c14ca62c9
--- /dev/null
+++ b/source/ubiqx/README.UBI
@@ -0,0 +1,18 @@
+Fri Apr 17 10:21:56 CDT 1998
+
+The C code files in the samba/source/ubiqx directory are licensed under
+the terms of the GNU LIBRARY GENERAL PUBLIC LICENSE (LGPL). A copy of the
+LGPL should also be included in this directory under the name COPYING.LIB.
+If this file is not present, you can obtain a copy of the LGPL by writing
+to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139,
+USA.
+
+The versions of the ubiqx modules distributed with Samba may have been
+modified for inclusion with Samba. The main distribution, which contains
+additional available modules, can be found at:
+
+ http://www.interads.co.uk/~crh/ubiqx/
+
+Chris Hertel
+Samba Team
+ubiqx@ubiqx.mn.org
diff --git a/source/ubiqx/debugparse.c b/source/ubiqx/debugparse.c
new file mode 100644
index 00000000000..195fc245bc0
--- /dev/null
+++ b/source/ubiqx/debugparse.c
@@ -0,0 +1,308 @@
+/* ========================================================================== **
+ * debugparse.c
+ *
+ * Copyright (C) 1998 by Christopher R. Hertel
+ *
+ * Email: crh@ubiqx.mn.org
+ *
+ * -------------------------------------------------------------------------- **
+ * This module is a very simple parser for Samba debug log files.
+ * -------------------------------------------------------------------------- **
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 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 Library General Public
+ * License along with this library; if not, write to the Free
+ * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * -------------------------------------------------------------------------- **
+ * The important function in this module is dbg_char2token(). The rest is
+ * basically fluff. (Potentially useful fluff, but still fluff.)
+ * ========================================================================== **
+ */
+
+#include "debugparse.h"
+
+/* -------------------------------------------------------------------------- **
+ * Constants...
+ *
+ * DBG_BSIZE - This internal constant is used only by dbg_test(). It is the
+ * size of the read buffer. I've tested the function using a
+ * DBG_BSIZE value of 2.
+ */
+
+#define DBG_BSIZE 128
+
+/* -------------------------------------------------------------------------- **
+ * Functions...
+ */
+
+const char *dbg_token2string( dbg_Token tok )
+ /* ------------------------------------------------------------------------ **
+ * Given a token, return a string describing the token.
+ *
+ * Input: tok - One of the set of dbg_Tokens defined in debugparse.h.
+ *
+ * Output: A string identifying the token. This is useful for debugging,
+ * etc.
+ *
+ * Note: If the token is not known, this function will return the
+ * string "<unknown>".
+ *
+ * ------------------------------------------------------------------------ **
+ */
+ {
+ switch( tok )
+ {
+ case dbg_null:
+ return( "null" );
+ case dbg_ignore:
+ return( "ignore" );
+ case dbg_header:
+ return( "header" );
+ case dbg_timestamp:
+ return( "time stamp" );
+ case dbg_level:
+ return( "level" );
+ case dbg_sourcefile:
+ return( "source file" );
+ case dbg_function:
+ return( "function" );
+ case dbg_lineno:
+ return( "line number" );
+ case dbg_message:
+ return( "message" );
+ case dbg_eof:
+ return( "[EOF]" );
+ }
+ return( "<unknown>" );
+ } /* dbg_token2string */
+
+dbg_Token dbg_char2token( dbg_Token *state, int c )
+ /* ------------------------------------------------------------------------ **
+ * Parse input one character at a time.
+ *
+ * Input: state - A pointer to a token variable. This is used to
+ * maintain the parser state between calls. For
+ * each input stream, you should set up a separate
+ * state variable and initialize it to dbg_null.
+ * Pass a pointer to it into this function with each
+ * character in the input stream. See dbg_test()
+ * for an example.
+ * c - The "current" character in the input stream.
+ *
+ * Output: A token.
+ * The token value will change when delimiters are found,
+ * which indicate a transition between syntactical objects.
+ * Possible return values are:
+ *
+ * dbg_null - The input character was an end-of-line.
+ * This resets the parser to its initial state
+ * in preparation for parsing the next line.
+ * dbg_eof - Same as dbg_null, except that the character
+ * was an end-of-file.
+ * dbg_ignore - Returned for whitespace and delimiters.
+ * These lexical tokens are only of interest
+ * to the parser.
+ * dbg_header - Indicates the start of a header line. The
+ * input character was '[' and was the first on
+ * the line.
+ * dbg_timestamp - Indicates that the input character was part
+ * of a header timestamp.
+ * dbg_level - Indicates that the input character was part
+ * of the debug-level value in the header.
+ * dbg_sourcefile - Indicates that the input character was part
+ * of the sourcefile name in the header.
+ * dbg_function - Indicates that the input character was part
+ * of the function name in the header.
+ * dbg_lineno - Indicates that the input character was part
+ * of the DEBUG call line number in the header.
+ * dbg_message - Indicates that the input character was part
+ * of the DEBUG message text.
+ *
+ * ------------------------------------------------------------------------ **
+ */
+ {
+ /* The terminating characters that we see will greatly depend upon
+ * how they are read. For example, if gets() is used instead of
+ * fgets(), then we will not see newline characters. A lot also
+ * depends on the calling function, which may handle terminators
+ * itself.
+ *
+ * '\n', '\0', and EOF are all considered line terminators. The
+ * dbg_eof token is sent back if an EOF is encountered.
+ *
+ * Warning: only allow the '\0' character to be sent if you are
+ * using gets() to read whole lines (thus replacing '\n'
+ * with '\0'). Sending '\0' at the wrong time will mess
+ * up the parsing.
+ */
+ switch( c )
+ {
+ case EOF:
+ *state = dbg_null; /* Set state to null (initial state) so */
+ return( dbg_eof ); /* that we can restart with new input. */
+ case '\n':
+ case '\0':
+ *state = dbg_null; /* A newline or eoln resets to the null state. */
+ return( dbg_null );
+ }
+
+ /* When within the body of the message, only a line terminator
+ * can cause a change of state. We've already checked for line
+ * terminators, so if the current state is dbg_msgtxt, simply
+ * return that as our current token.
+ */
+ if( dbg_message == *state )
+ return( dbg_message );
+
+ /* If we are at the start of a new line, and the input character
+ * is an opening bracket, then the line is a header line, otherwise
+ * it's a message body line.
+ */
+ if( dbg_null == *state )
+ {
+ if( '[' == c )
+ {
+ *state = dbg_timestamp;
+ return( dbg_header );
+ }
+ *state = dbg_message;
+ return( dbg_message );
+ }
+
+ /* We've taken care of terminators, text blocks and new lines.
+ * The remaining possibilities are all within the header line
+ * itself.
+ */
+
+ /* Within the header line, whitespace can be ignored *except*
+ * within the timestamp.
+ */
+ if( isspace( c ) )
+ {
+ /* Fudge. The timestamp may contain space characters. */
+ if( (' ' == c) && (dbg_timestamp == *state) )
+ return( dbg_timestamp );
+ /* Otherwise, ignore whitespace. */
+ return( dbg_ignore );
+ }
+
+ /* Okay, at this point we know we're somewhere in the header.
+ * Valid header *states* are: dbg_timestamp, dbg_level,
+ * dbg_sourcefile, dbg_function, and dbg_lineno.
+ */
+ switch( c )
+ {
+ case ',':
+ if( dbg_timestamp == *state )
+ {
+ *state = dbg_level;
+ return( dbg_ignore );
+ }
+ break;
+ case ']':
+ if( dbg_level == *state )
+ {
+ *state = dbg_sourcefile;
+ return( dbg_ignore );
+ }
+ break;
+ case ':':
+ if( dbg_sourcefile == *state )
+ {
+ *state = dbg_function;
+ return( dbg_ignore );
+ }
+ break;
+ case '(':
+ if( dbg_function == *state )
+ {
+ *state = dbg_lineno;
+ return( dbg_ignore );
+ }
+ break;
+ case ')':
+ if( dbg_lineno == *state )
+ {
+ *state = dbg_null;
+ return( dbg_ignore );
+ }
+ break;
+ }
+
+ /* If the previous block did not result in a state change, then
+ * return the current state as the current token.
+ */
+ return( *state );
+ } /* dbg_char2token */
+
+void dbg_test( void )
+ /* ------------------------------------------------------------------------ **
+ * Simple test function.
+ *
+ * Input: none.
+ * Output: none.
+ * Notes: This function was used to test dbg_char2token(). It reads a
+ * Samba log file from stdin and prints parsing info to stdout.
+ * It also serves as a simple example.
+ *
+ * ------------------------------------------------------------------------ **
+ */
+ {
+ char bufr[DBG_BSIZE];
+ int i;
+ int linecount = 1;
+ dbg_Token old = dbg_null,
+ new = dbg_null,
+ state = dbg_null;
+
+ while( fgets( bufr, DBG_BSIZE, stdin ) )
+ {
+ for( i = 0; bufr[i]; i++ )
+ {
+ old = new;
+ new = dbg_char2token( &state, bufr[i] );
+ switch( new )
+ {
+ case dbg_header:
+ if( linecount > 1 )
+ (void)putchar( '\n' );
+ break;
+ case dbg_null:
+ linecount++;
+ break;
+ case dbg_ignore:
+ break;
+ default:
+ if( old != new )
+ (void)printf( "\n[%05d]%12s: ", linecount, dbg_token2string(new) );
+ (void)putchar( bufr[i] );
+ }
+ }
+ }
+ (void)putchar( '\n' );
+ } /* dbg_test */
+
+
+/* -------------------------------------------------------------------------- **
+ * This simple main line can be uncommented and used to test the parser.
+ */
+
+/*
+ * int main( void )
+ * {
+ * dbg_test();
+ * return( 0 );
+ * }
+ */
+
+/* ========================================================================== */
diff --git a/source/ubiqx/debugparse.h b/source/ubiqx/debugparse.h
new file mode 100644
index 00000000000..458eee74558
--- /dev/null
+++ b/source/ubiqx/debugparse.h
@@ -0,0 +1,127 @@
+#ifndef DEBUGPARSE_H
+#define DEBUGPARSE_H
+/* ========================================================================== **
+ * debugparse.c
+ *
+ * Copyright (C) 1998 by Christopher R. Hertel
+ *
+ * Email: crh@ubiqx.mn.org
+ *
+ * -------------------------------------------------------------------------- **
+ * This module is a very simple parser for Samba debug log files.
+ * -------------------------------------------------------------------------- **
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 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 Library General Public
+ * License along with this library; if not, write to the Free
+ * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * -------------------------------------------------------------------------- **
+ * The important function in this module is dbg_char2token(). The rest is
+ * basically fluff. (Potentially useful fluff, but still fluff.)
+ * ========================================================================== **
+ */
+
+#include "sys_include.h"
+
+/* This module compiles quite nicely outside of the Samba environment.
+ * You'll need the following headers:
+#include <ctype.h>
+#include <stdio.h>
+#include <string.h>
+ */
+
+/* -------------------------------------------------------------------------- **
+ * These are the tokens returned by dbg_char2token().
+ */
+
+typedef enum
+ {
+ dbg_null = 0,
+ dbg_ignore,
+ dbg_header,
+ dbg_timestamp,
+ dbg_level,
+ dbg_sourcefile,
+ dbg_function,
+ dbg_lineno,
+ dbg_message,
+ dbg_eof
+ } dbg_Token;
+
+/* -------------------------------------------------------------------------- **
+ * Function prototypes...
+ */
+
+ const char *dbg_token2string( dbg_Token tok );
+ /* ------------------------------------------------------------------------ **
+ * Given a token, return a string describing the token.
+ *
+ * Input: tok - One of the set of dbg_Tokens defined in debugparse.h.
+ *
+ * Output: A string identifying the token. This is useful for debugging,
+ * etc.
+ *
+ * Note: If the token is not known, this function will return the
+ * string "<unknown>".
+ *
+ * ------------------------------------------------------------------------ **
+ */
+
+ dbg_Token dbg_char2token( dbg_Token *state, int c );
+ /* ------------------------------------------------------------------------ **
+ * Parse input one character at a time.
+ *
+ * Input: state - A pointer to a token variable. This is used to
+ * maintain the parser state between calls. For
+ * each input stream, you should set up a separate
+ * state variable and initialize it to dbg_null.
+ * Pass a pointer to it into this function with each
+ * character in the input stream. See dbg_test()
+ * for an example.
+ * c - The "current" character in the input stream.
+ *
+ * Output: A token.
+ * The token value will change when delimiters are found,
+ * which indicate a transition between syntactical objects.
+ * Possible return values are:
+ *
+ * dbg_null - The input character was an end-of-line.
+ * This resets the parser to its initial state
+ * in preparation for parsing the next line.
+ * dbg_eof - Same as dbg_null, except that the character
+ * was an end-of-file.
+ * dbg_ignore - Returned for whitespace and delimiters.
+ * These lexical tokens are only of interest
+ * to the parser.
+ * dbg_header - Indicates the start of a header line. The
+ * input character was '[' and was the first on
+ * the line.
+ * dbg_timestamp - Indicates that the input character was part
+ * of a header timestamp.
+ * dbg_level - Indicates that the input character was part
+ * of the debug-level value in the header.
+ * dbg_sourcefile - Indicates that the input character was part
+ * of the sourcefile name in the header.
+ * dbg_function - Indicates that the input character was part
+ * of the function name in the header.
+ * dbg_lineno - Indicates that the input character was part
+ * of the DEBUG call line number in the header.
+ * dbg_message - Indicates that the input character was part
+ * of the DEBUG message text.
+ *
+ * ------------------------------------------------------------------------ **
+ */
+
+
+/* -------------------------------------------------------------------------- */
+#endif /* DEBUGPARSE_H */
diff --git a/source/ubiqx/sys_include.h b/source/ubiqx/sys_include.h
new file mode 100644
index 00000000000..8ff270afe85
--- /dev/null
+++ b/source/ubiqx/sys_include.h
@@ -0,0 +1,52 @@
+#ifndef SYS_INCLUDE_H
+#define SYS_INCLUDE_H
+/* ========================================================================== **
+ * sys_include.h
+ *
+ * Copyright (C) 1998 by Christopher R. Hertel
+ *
+ * Email: crh@ubiqx.mn.org
+ * -------------------------------------------------------------------------- **
+ * This header provides system declarations and data types used internally
+ * by the ubiqx modules.
+ * -------------------------------------------------------------------------- **
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 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 Library General Public
+ * License along with this library; if not, write to the Free
+ * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * -------------------------------------------------------------------------- **
+ *
+ * Samba version of sys_include.h
+ *
+ * ========================================================================== **
+ */
+
+#ifndef _INCLUDES_H
+
+/* Block the inclusion of some Samba headers so that ubiqx types won't be
+ * used before the headers that define them. These headers are not needed
+ * in the ubiqx modules anyway.
+ */
+#define _PROTO_H_
+#define _NAMESERV_H_
+#define _HASH_H_
+
+/* The main Samba system-adaptive header file.
+ */
+#include "includes.h"
+
+#endif /* _INCLUDES_H */
+
+/* ================================ The End ================================= */
+#endif /* SYS_INCLUDE_H */
diff --git a/source/ubiqx/ubi_BinTree.c b/source/ubiqx/ubi_BinTree.c
new file mode 100644
index 00000000000..8a4d4612800
--- /dev/null
+++ b/source/ubiqx/ubi_BinTree.c
@@ -0,0 +1,1132 @@
+/* ========================================================================== **
+ * ubi_BinTree.c
+ *
+ * Copyright (C) 1991-1998 by Christopher R. Hertel
+ *
+ * Email: crh@ubiqx.mn.org
+ * -------------------------------------------------------------------------- **
+ *
+ * This module implements a simple binary tree.
+ *
+ * -------------------------------------------------------------------------- **
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 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 Library General Public
+ * License along with this library; if not, write to the Free
+ * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * -------------------------------------------------------------------------- **
+ *
+ * Log: ubi_BinTree.c,v
+ * Revision 4.10 2000/06/06 20:38:40 crh
+ * In the ReplaceNode() function, the old node header was being copied
+ * to the new node header using a byte-by-byte copy. This was causing
+ * the 'insure' software testing program to report a memory leak. The
+ * fix was to do a simple assignement: *newnode = *oldnode;
+ * This quieted the (errant) memory leak reports and is probably a bit
+ * faster than the bytewise copy.
+ *
+ * Revision 4.9 2000/01/08 23:24:30 crh
+ * Clarified a variety of if( pointer ) lines, replacing them with
+ * if( NULL != pointer ). This is more correct, and I have heard
+ * of at least one (obscure?) system out there that uses a non-zero
+ * value for NULL.
+ * Also, speed improvement in Neighbor(). It was comparing pointers
+ * when it could have compared two gender values. The pointer
+ * comparison was somewhat indirect (does pointer equal the pointer
+ * of the parent of the node pointed to by pointer). Urq.
+ *
+ * Revision 4.8 1999/09/22 03:40:30 crh
+ * Modified ubi_btTraverse() and ubi_btKillTree(). They now return an
+ * unsigned long indicating the number of nodes processed. The change
+ * is subtle. An empty tree formerly returned False, and now returns
+ * zero.
+ *
+ * Revision 4.7 1998/10/21 06:14:42 crh
+ * Fixed bugs in FirstOf() and LastOf() reported by Massimo Campostrini.
+ * See function comments.
+ *
+ * Revision 4.6 1998/07/25 17:02:10 crh
+ * Added the ubi_trNewTree() macro.
+ *
+ * Revision 4.5 1998/06/04 21:29:27 crh
+ * Upper-cased defined constants (eg UBI_BINTREE_H) in some header files.
+ * This is more "standard", and is what people expect. Weird, eh?
+ *
+ * Revision 4.4 1998/06/03 17:42:46 crh
+ * Further fiddling with sys_include.h. It's now in ubi_BinTree.h which is
+ * included by all of the binary tree files.
+ *
+ * Reminder: Some of the ubi_tr* macros in ubi_BinTree.h are redefined in
+ * ubi_AVLtree.h and ubi_SplayTree.h. This allows easy swapping
+ * of tree types by simply changing a header. Unfortunately, the
+ * macro redefinitions in ubi_AVLtree.h and ubi_SplayTree.h will
+ * conflict if used together. You must either choose a single tree
+ * type, or use the underlying function calls directly. Compare
+ * the two header files for more information.
+ *
+ * Revision 4.3 1998/06/02 01:28:43 crh
+ * Changed ubi_null.h to sys_include.h to make it more generic.
+ *
+ * Revision 4.2 1998/05/20 04:32:36 crh
+ * The C file now includes ubi_null.h. See ubi_null.h for more info.
+ * Also, the balance and gender fields of the node were declared as
+ * signed char. As I understand it, at least one SunOS or Solaris
+ * compiler doesn't like "signed char". The declarations were
+ * wrong anyway, so I changed them to simple "char".
+ *
+ * Revision 4.1 1998/03/31 06:11:57 crh
+ * Thomas Aglassinger sent E'mail pointing out errors in the
+ * dereferencing of function pointers, and a missing typecast.
+ * Thanks, Thomas!
+ *
+ * Revision 4.0 1998/03/10 03:19:22 crh
+ * Added the AVL field 'balance' to the ubi_btNode structure. This means
+ * that all BinTree modules now use the same basic node structure, which
+ * greatly simplifies the AVL module.
+ * Decided that this was a big enough change to justify a new major revision
+ * number. 3.0 was an error, so we're at 4.0.
+ *
+ * Revision 2.6 1998/01/24 06:27:46 crh
+ * Added ubi_trCount() macro.
+ *
+ * Revision 2.5 1997/12/23 03:56:29 crh
+ * In this version, all constants & macros defined in the header file have
+ * the ubi_tr prefix. Also cleaned up anything that gcc complained about
+ * when run with '-pedantic -fsyntax-only -Wall'.
+ *
+ * Revision 2.4 1997/07/26 04:11:10 crh
+ * + Just to be annoying I changed ubi_TRUE and ubi_FALSE to ubi_trTRUE
+ * and ubi_trFALSE.
+ * + There is now a type ubi_trBool to go with ubi_trTRUE and ubi_trFALSE.
+ * + There used to be something called "ubi_TypeDefs.h". I got rid of it.
+ * + Added function ubi_btLeafNode().
+ *
+ * Revision 2.3 1997/06/03 05:16:17 crh
+ * Changed TRUE and FALSE to ubi_TRUE and ubi_FALSE to avoid conflicts.
+ * Also changed the interface to function InitTree(). See the comments
+ * for this function for more information.
+ *
+ * Revision 2.2 1995/10/03 22:00:07 CRH
+ * Ubisized!
+ *
+ * Revision 2.1 95/03/09 23:37:10 CRH
+ * Added the ModuleID static string and function. These modules are now
+ * self-identifying.
+ *
+ * Revision 2.0 95/02/27 22:00:17 CRH
+ * Revision 2.0 of this program includes the following changes:
+ *
+ * 1) A fix to a major typo in the RepaceNode() function.
+ * 2) The addition of the static function Border().
+ * 3) The addition of the public functions FirstOf() and LastOf(), which
+ * use Border(). These functions are used with trees that allow
+ * duplicate keys.
+ * 4) A complete rewrite of the Locate() function. Locate() now accepts
+ * a "comparison" operator.
+ * 5) Overall enhancements to both code and comments.
+ *
+ * I decided to give this a new major rev number because the interface has
+ * changed. In particular, there are two new functions, and changes to the
+ * Locate() function.
+ *
+ * Revision 1.0 93/10/15 22:44:59 CRH
+ * With this revision, I have added a set of #define's that provide a single,
+ * standard API to all existing tree modules. Until now, each of the three
+ * existing modules had a different function and typedef prefix, as follows:
+ *
+ * Module Prefix
+ * ubi_BinTree ubi_bt
+ * ubi_AVLtree ubi_avl
+ * ubi_SplayTree ubi_spt
+ *
+ * To further complicate matters, only those portions of the base module
+ * (ubi_BinTree) that were superceeded in the new module had the new names.
+ * For example, if you were using ubi_SplayTree, the locate function was
+ * called "ubi_sptLocate", but the next and previous functions remained
+ * "ubi_btNext" and "ubi_btPrev".
+ *
+ * This was not too terrible if you were familiar with the modules and knew
+ * exactly which tree model you wanted to use. If you wanted to be able to
+ * change modules (for speed comparisons, etc), things could get messy very
+ * quickly.
+ *
+ * So, I have added a set of defined names that get redefined in any of the
+ * descendant modules. To use this standardized interface in your code,
+ * simply replace all occurances of "ubi_bt", "ubi_avl", and "ubi_spt" with
+ * "ubi_tr". The "ubi_tr" names will resolve to the correct function or
+ * datatype names for the module that you are using. Just remember to
+ * include the header for that module in your program file. Because these
+ * names are handled by the preprocessor, there is no added run-time
+ * overhead.
+ *
+ * Note that the original names do still exist, and can be used if you wish
+ * to write code directly to a specific module. This should probably only be
+ * done if you are planning to implement a new descendant type, such as
+ * red/black trees. CRH
+ *
+ * V0.0 - June, 1991 - Written by Christopher R. Hertel (CRH).
+ *
+ * ========================================================================== **
+ */
+
+#include "ubi_BinTree.h" /* Header for this module. */
+
+/* ========================================================================== **
+ * Static data.
+ */
+
+static char ModuleID[] = "ubi_BinTree\n\
+\tRevision: 4.10 \n\
+\tDate: 2000/06/06 20:38:40 \n\
+\tAuthor: crh \n";
+
+/* ========================================================================== **
+ * Internal (private) functions.
+ */
+
+static ubi_btNodePtr qFind( ubi_btCompFunc cmp,
+ ubi_btItemPtr FindMe,
+ register ubi_btNodePtr p )
+ /* ------------------------------------------------------------------------ **
+ * This function performs a non-recursive search of a tree for a node
+ * matching a specific key. It is called "qFind()" because it is
+ * faster that TreeFind (below).
+ *
+ * Input:
+ * cmp - a pointer to the tree's comparison function.
+ * FindMe - a pointer to the key value for which to search.
+ * p - a pointer to the starting point of the search. <p>
+ * is considered to be the root of a subtree, and only
+ * the subtree will be searched.
+ *
+ * Output:
+ * A pointer to a node with a key that matches the key indicated by
+ * FindMe, or NULL if no such node was found.
+ *
+ * Note: In a tree that allows duplicates, the pointer returned *might
+ * not* point to the (sequentially) first occurance of the
+ * desired key.
+ * ------------------------------------------------------------------------ **
+ */
+ {
+ int tmp;
+
+ while( (NULL != p)
+ && ((tmp = ubi_trAbNormal( (*cmp)(FindMe, p) )) != ubi_trEQUAL) )
+ p = p->Link[tmp];
+
+ return( p );
+ } /* qFind */
+
+static ubi_btNodePtr TreeFind( ubi_btItemPtr findme,
+ ubi_btNodePtr p,
+ ubi_btNodePtr *parentp,
+ char *gender,
+ ubi_btCompFunc CmpFunc )
+ /* ------------------------------------------------------------------------ **
+ * TreeFind() searches a tree for a given value (findme). It will return a
+ * pointer to the target node, if found, or NULL if the target node was not
+ * found.
+ *
+ * TreeFind() also returns, via parameters, a pointer to the parent of the
+ * target node, and a LEFT or RIGHT value indicating which child of the
+ * parent is the target node. *If the target is not found*, then these
+ * values indicate the place at which the target *should be found*. This
+ * is useful when inserting a new node into a tree or searching for nodes
+ * "near" the target node.
+ *
+ * The parameters are:
+ *
+ * findme - is a pointer to the key information to be searched for.
+ * p - points to the root of the tree to be searched.
+ * parentp - will return a pointer to a pointer to the !parent! of the
+ * target node, which can be especially usefull if the target
+ * was not found.
+ * gender - returns LEFT or RIGHT to indicate which child of *parentp
+ * was last searched.
+ * CmpFunc - points to the comparison function.
+ *
+ * This function is called by ubi_btLocate() and ubi_btInsert().
+ * ------------------------------------------------------------------------ **
+ */
+ {
+ register ubi_btNodePtr tmp_p = p;
+ ubi_btNodePtr tmp_pp = NULL;
+ char tmp_gender = ubi_trEQUAL;
+ int tmp_cmp;
+
+ while( (NULL != tmp_p)
+ && (ubi_trEQUAL != (tmp_cmp = ubi_trAbNormal((*CmpFunc)(findme, tmp_p)))) )
+ {
+ tmp_pp = tmp_p; /* Keep track of previous node. */
+ tmp_gender = (char)tmp_cmp; /* Keep track of sex of child. */
+ tmp_p = tmp_p->Link[tmp_cmp]; /* Go to child. */
+ }
+ *parentp = tmp_pp; /* Return results. */
+ *gender = tmp_gender;
+ return( tmp_p );
+ } /* TreeFind */
+
+static void ReplaceNode( ubi_btNodePtr *parent,
+ ubi_btNodePtr oldnode,
+ ubi_btNodePtr newnode )
+ /* ------------------------------------------------------------------------ **
+ * Remove node oldnode from the tree, replacing it with node newnode.
+ *
+ * Input:
+ * parent - A pointer to he parent pointer of the node to be
+ * replaced. <parent> may point to the Link[] field of
+ * a parent node, or it may indicate the root pointer at
+ * the top of the tree.
+ * oldnode - A pointer to the node that is to be replaced.
+ * newnode - A pointer to the node that is to be installed in the
+ * place of <*oldnode>.
+ *
+ * Notes: Don't forget to free oldnode.
+ * Also, this function used to have a really nasty typo
+ * bug. "oldnode" and "newnode" were swapped in the line
+ * that now reads:
+ * ((unsigned char *)newnode)[i] = ((unsigned char *)oldnode)[i];
+ * Bleah!
+ * ------------------------------------------------------------------------ **
+ */
+ {
+ *newnode = *oldnode; /* Copy node internals to new node. */
+
+ (*parent) = newnode; /* Old node's parent points to new child. */
+ /* Now tell the children about their new step-parent. */
+ if( oldnode->Link[ubi_trLEFT] )
+ (oldnode->Link[ubi_trLEFT])->Link[ubi_trPARENT] = newnode;
+ if( oldnode->Link[ubi_trRIGHT] )
+ (oldnode->Link[ubi_trRIGHT])->Link[ubi_trPARENT] = newnode;
+ } /* ReplaceNode */
+
+static void SwapNodes( ubi_btRootPtr RootPtr,
+ ubi_btNodePtr Node1,
+ ubi_btNodePtr Node2 )
+ /* ------------------------------------------------------------------------ **
+ * This function swaps two nodes in the tree. Node1 will take the place of
+ * Node2, and Node2 will fill in the space left vacant by Node 1.
+ *
+ * Input:
+ * RootPtr - pointer to the tree header structure for this tree.
+ * Node1 - \
+ * > These are the two nodes which are to be swapped.
+ * Node2 - /
+ *
+ * Notes:
+ * This function does a three step swap, using a dummy node as a place
+ * holder. This function is used by ubi_btRemove().
+ * ------------------------------------------------------------------------ **
+ */
+ {
+ ubi_btNodePtr *Parent;
+ ubi_btNode dummy;
+ ubi_btNodePtr dummy_p = &dummy;
+
+ /* Replace Node 1 with the dummy, thus removing Node1 from the tree. */
+ if( NULL != Node1->Link[ubi_trPARENT] )
+ Parent = &((Node1->Link[ubi_trPARENT])->Link[(int)(Node1->gender)]);
+ else
+ Parent = &(RootPtr->root);
+ ReplaceNode( Parent, Node1, dummy_p );
+
+ /* Swap Node 1 with Node 2, placing Node 1 back into the tree. */
+ if( NULL != Node2->Link[ubi_trPARENT] )
+ Parent = &((Node2->Link[ubi_trPARENT])->Link[(int)(Node2->gender)]);
+ else
+ Parent = &(RootPtr->root);
+ ReplaceNode( Parent, Node2, Node1 );
+
+ /* Swap Node 2 and the dummy, thus placing Node 2 back into the tree. */
+ if( NULL != dummy_p->Link[ubi_trPARENT] )
+ Parent = &((dummy_p->Link[ubi_trPARENT])->Link[(int)(dummy_p->gender)]);
+ else
+ Parent = &(RootPtr->root);
+ ReplaceNode( Parent, dummy_p, Node2 );
+ } /* SwapNodes */
+
+/* -------------------------------------------------------------------------- **
+ * These routines allow you to walk through the tree, forwards or backwards.
+ */
+
+static ubi_btNodePtr SubSlide( register ubi_btNodePtr P,
+ register int whichway )
+ /* ------------------------------------------------------------------------ **
+ * Slide down the side of a subtree.
+ *
+ * Given a starting node, this function returns a pointer to the LEFT-, or
+ * RIGHT-most descendent, *or* (if whichway is PARENT) to the tree root.
+ *
+ * Input: P - a pointer to a starting place.
+ * whichway - the direction (LEFT, RIGHT, or PARENT) in which to
+ * travel.
+ * Output: A pointer to a node that is either the root, or has no
+ * whichway-th child but is within the subtree of P. Note that
+ * the return value may be the same as P. The return value *will
+ * be* NULL if P is NULL.
+ * ------------------------------------------------------------------------ **
+ */
+ {
+
+ if( NULL != P )
+ while( NULL != P->Link[ whichway ] )
+ P = P->Link[ whichway ];
+ return( P );
+ } /* SubSlide */
+
+static ubi_btNodePtr Neighbor( register ubi_btNodePtr P,
+ register int whichway )
+ /* ------------------------------------------------------------------------ **
+ * Given starting point p, return the (key order) next or preceeding node
+ * in the tree.
+ *
+ * Input: P - Pointer to our starting place node.
+ * whichway - the direction in which to travel to find the
+ * neighbor, i.e., the RIGHT neighbor or the LEFT
+ * neighbor.
+ *
+ * Output: A pointer to the neighboring node, or NULL if P was NULL.
+ *
+ * Notes: If whichway is PARENT, the results are unpredictable.
+ * ------------------------------------------------------------------------ **
+ */
+ {
+ if( P )
+ {
+ if( NULL != P->Link[ whichway ] )
+ return( SubSlide( P->Link[ whichway ], (char)ubi_trRevWay(whichway) ) );
+ else
+ while( NULL != P->Link[ ubi_trPARENT ] )
+ {
+ if( whichway == P->gender )
+ P = P->Link[ ubi_trPARENT ];
+ else
+ return( P->Link[ ubi_trPARENT ] );
+ }
+ }
+ return( NULL );
+ } /* Neighbor */
+
+static ubi_btNodePtr Border( ubi_btRootPtr RootPtr,
+ ubi_btItemPtr FindMe,
+ ubi_btNodePtr p,
+ int whichway )
+ /* ------------------------------------------------------------------------ **
+ * Given starting point p, which has a key value equal to *FindMe, locate
+ * the first (index order) node with the same key value.
+ *
+ * This function is useful in trees that have can have duplicate keys.
+ * For example, consider the following tree:
+ * Tree Traversal
+ * 2 If <p> points to the root and <whichway> is RIGHT, 3
+ * / \ then the return value will be a pointer to the / \
+ * 2 2 RIGHT child of the root node. The tree on 2 5
+ * / / \ the right shows the order of traversal. / / \
+ * 1 2 3 1 4 6
+ *
+ * Input: RootPtr - Pointer to the tree root structure.
+ * FindMe - Key value for comparisons.
+ * p - Pointer to the starting-point node.
+ * whichway - the direction in which to travel to find the
+ * neighbor, i.e., the RIGHT neighbor or the LEFT
+ * neighbor.
+ *
+ * Output: A pointer to the first (index, or "traversal", order) node with
+ * a Key value that matches *FindMe.
+ *
+ * Notes: If whichway is PARENT, or if the tree does not allow duplicate
+ * keys, this function will return <p>.
+ * ------------------------------------------------------------------------ **
+ */
+ {
+ register ubi_btNodePtr q;
+
+ /* Exit if there's nothing that can be done. */
+ if( !ubi_trDups_OK( RootPtr ) || (ubi_trPARENT == whichway) )
+ return( p );
+
+ /* First, if needed, move up the tree. We need to get to the root of the
+ * subtree that contains all of the matching nodes.
+ */
+ q = p->Link[ubi_trPARENT];
+ while( (NULL != q)
+ && (ubi_trEQUAL == ubi_trAbNormal( (*(RootPtr->cmp))(FindMe, q) )) )
+ {
+ p = q;
+ q = p->Link[ubi_trPARENT];
+ }
+
+ /* Next, move back down in the "whichway" direction. */
+ q = p->Link[whichway];
+ while( NULL != q )
+ {
+ q = qFind( RootPtr->cmp, FindMe, q );
+ if( q )
+ {
+ p = q;
+ q = p->Link[whichway];
+ }
+ }
+ return( p );
+ } /* Border */
+
+
+/* ========================================================================== **
+ * Exported utilities.
+ */
+
+long ubi_btSgn( register long x )
+ /* ------------------------------------------------------------------------ **
+ * Return the sign of x; {negative,zero,positive} ==> {-1, 0, 1}.
+ *
+ * Input: x - a signed long integer value.
+ *
+ * Output: the "sign" of x, represented as follows:
+ * -1 == negative
+ * 0 == zero (no sign)
+ * 1 == positive
+ *
+ * Note: This utility is provided in order to facilitate the conversion
+ * of C comparison function return values into BinTree direction
+ * values: {LEFT, PARENT, EQUAL}. It is INCORPORATED into the
+ * ubi_trAbNormal() conversion macro!
+ *
+ * ------------------------------------------------------------------------ **
+ */
+ {
+ return( (x)?((x>0)?(1):(-1)):(0) );
+ } /* ubi_btSgn */
+
+ubi_btNodePtr ubi_btInitNode( ubi_btNodePtr NodePtr )
+ /* ------------------------------------------------------------------------ **
+ * Initialize a tree node.
+ *
+ * Input: a pointer to a ubi_btNode structure to be initialized.
+ * Output: a pointer to the initialized ubi_btNode structure (ie. the
+ * same as the input pointer).
+ * ------------------------------------------------------------------------ **
+ */
+ {
+ NodePtr->Link[ ubi_trLEFT ] = NULL;
+ NodePtr->Link[ ubi_trPARENT ] = NULL;
+ NodePtr->Link[ ubi_trRIGHT ] = NULL;
+ NodePtr->gender = ubi_trEQUAL;
+ NodePtr->balance = ubi_trEQUAL;
+ return( NodePtr );
+ } /* ubi_btInitNode */
+
+ubi_btRootPtr ubi_btInitTree( ubi_btRootPtr RootPtr,
+ ubi_btCompFunc CompFunc,
+ char Flags )
+ /* ------------------------------------------------------------------------ **
+ * Initialize the fields of a Tree Root header structure.
+ *
+ * Input: RootPtr - a pointer to an ubi_btRoot structure to be
+ * initialized.
+ * CompFunc - a pointer to a comparison function that will be used
+ * whenever nodes in the tree must be compared against
+ * outside values.
+ * Flags - One bytes worth of flags. Flags include
+ * ubi_trOVERWRITE and ubi_trDUPKEY. See the header
+ * file for more info.
+ *
+ * Output: a pointer to the initialized ubi_btRoot structure (ie. the
+ * same value as RootPtr).
+ *
+ * Note: The interface to this function has changed from that of
+ * previous versions. The <Flags> parameter replaces two
+ * boolean parameters that had the same basic effect.
+ *
+ * ------------------------------------------------------------------------ **
+ */
+ {
+ if( RootPtr )
+ {
+ RootPtr->root = NULL;
+ RootPtr->count = 0L;
+ RootPtr->cmp = CompFunc;
+ RootPtr->flags = (Flags & ubi_trDUPKEY) ? ubi_trDUPKEY : Flags;
+ } /* There are only two supported flags, and they are
+ * mutually exclusive. ubi_trDUPKEY takes precedence
+ * over ubi_trOVERWRITE.
+ */
+ return( RootPtr );
+ } /* ubi_btInitTree */
+
+ubi_trBool ubi_btInsert( ubi_btRootPtr RootPtr,
+ ubi_btNodePtr NewNode,
+ ubi_btItemPtr ItemPtr,
+ ubi_btNodePtr *OldNode )
+ /* ------------------------------------------------------------------------ **
+ * This function uses a non-recursive algorithm to add a new element to the
+ * tree.
+ *
+ * Input: RootPtr - a pointer to the ubi_btRoot structure that indicates
+ * the root of the tree to which NewNode is to be added.
+ * NewNode - a pointer to an ubi_btNode structure that is NOT
+ * part of any tree.
+ * ItemPtr - A pointer to the sort key that is stored within
+ * *NewNode. ItemPtr MUST point to information stored
+ * in *NewNode or an EXACT DUPLICATE. The key data
+ * indicated by ItemPtr is used to place the new node
+ * into the tree.
+ * OldNode - a pointer to an ubi_btNodePtr. When searching
+ * the tree, a duplicate node may be found. If
+ * duplicates are allowed, then the new node will
+ * be simply placed into the tree. If duplicates
+ * are not allowed, however, then one of two things
+ * may happen.
+ * 1) if overwritting *is not* allowed, this
+ * function will return FALSE (indicating that
+ * the new node could not be inserted), and
+ * *OldNode will point to the duplicate that is
+ * still in the tree.
+ * 2) if overwritting *is* allowed, then this
+ * function will swap **OldNode for *NewNode.
+ * In this case, *OldNode will point to the node
+ * that was removed (thus allowing you to free
+ * the node).
+ * ** If you are using overwrite mode, ALWAYS **
+ * ** check the return value of this parameter! **
+ * Note: You may pass NULL in this parameter, the
+ * function knows how to cope. If you do this,
+ * however, there will be no way to return a
+ * pointer to an old (ie. replaced) node (which is
+ * a problem if you are using overwrite mode).
+ *
+ * Output: a boolean value indicating success or failure. The function
+ * will return FALSE if the node could not be added to the tree.
+ * Such failure will only occur if duplicates are not allowed,
+ * nodes cannot be overwritten, AND a duplicate key was found
+ * within the tree.
+ * ------------------------------------------------------------------------ **
+ */
+ {
+ ubi_btNodePtr OtherP,
+ parent = NULL;
+ char tmp;
+
+ if( NULL == OldNode ) /* If they didn't give us a pointer, supply our own. */
+ OldNode = &OtherP;
+
+ (void)ubi_btInitNode( NewNode ); /* Init the new node's BinTree fields. */
+
+ /* Find a place for the new node. */
+ *OldNode = TreeFind(ItemPtr, (RootPtr->root), &parent, &tmp, (RootPtr->cmp));
+
+ /* Now add the node to the tree... */
+ if( NULL == (*OldNode) ) /* The easy one: we have a space for a new node! */
+ {
+ if( NULL == parent )
+ RootPtr->root = NewNode;
+ else
+ {
+ parent->Link[(int)tmp] = NewNode;
+ NewNode->Link[ubi_trPARENT] = parent;
+ NewNode->gender = tmp;
+ }
+ (RootPtr->count)++;
+ return( ubi_trTRUE );
+ }
+
+ /* If we reach this point, we know that a duplicate node exists. This
+ * section adds the node to the tree if duplicate keys are allowed.
+ */
+ if( ubi_trDups_OK(RootPtr) ) /* Key exists, add duplicate */
+ {
+ ubi_btNodePtr q;
+
+ tmp = ubi_trRIGHT;
+ q = (*OldNode);
+ *OldNode = NULL;
+ while( NULL != q )
+ {
+ parent = q;
+ if( tmp == ubi_trEQUAL )
+ tmp = ubi_trRIGHT;
+ q = q->Link[(int)tmp];
+ if ( q )
+ tmp = ubi_trAbNormal( (*(RootPtr->cmp))(ItemPtr, q) );
+ }
+ parent->Link[(int)tmp] = NewNode;
+ NewNode->Link[ubi_trPARENT] = parent;
+ NewNode->gender = tmp;
+ (RootPtr->count)++;
+ return( ubi_trTRUE );
+ }
+
+ /* If we get to *this* point, we know that we are not allowed to have
+ * duplicate nodes, but our node keys match, so... may we replace the
+ * old one?
+ */
+ if( ubi_trOvwt_OK(RootPtr) ) /* Key exists, we replace */
+ {
+ if( NULL == parent )
+ ReplaceNode( &(RootPtr->root), *OldNode, NewNode );
+ else
+ ReplaceNode( &(parent->Link[(int)((*OldNode)->gender)]),
+ *OldNode, NewNode );
+ return( ubi_trTRUE );
+ }
+
+ return( ubi_trFALSE ); /* Failure: could not replace an existing node. */
+ } /* ubi_btInsert */
+
+ubi_btNodePtr ubi_btRemove( ubi_btRootPtr RootPtr,
+ ubi_btNodePtr DeadNode )
+ /* ------------------------------------------------------------------------ **
+ * This function removes the indicated node from the tree.
+ *
+ * Input: RootPtr - A pointer to the header of the tree that contains
+ * the node to be removed.
+ * DeadNode - A pointer to the node that will be removed.
+ *
+ * Output: This function returns a pointer to the node that was removed
+ * from the tree (ie. the same as DeadNode).
+ *
+ * Note: The node MUST be in the tree indicated by RootPtr. If not,
+ * strange and evil things will happen to your trees.
+ * ------------------------------------------------------------------------ **
+ */
+ {
+ ubi_btNodePtr p,
+ *parentp;
+ int tmp;
+
+ /* if the node has both left and right subtrees, then we have to swap
+ * it with another node. The other node we choose will be the Prev()ious
+ * node, which is garunteed to have no RIGHT child.
+ */
+ if( (NULL != DeadNode->Link[ubi_trLEFT])
+ && (NULL != DeadNode->Link[ubi_trRIGHT]) )
+ SwapNodes( RootPtr, DeadNode, ubi_btPrev( DeadNode ) );
+
+ /* The parent of the node to be deleted may be another node, or it may be
+ * the root of the tree. Since we're not sure, it's best just to have
+ * a pointer to the parent pointer, whatever it is.
+ */
+ if( NULL == DeadNode->Link[ubi_trPARENT] )
+ parentp = &( RootPtr->root );
+ else
+ parentp = &((DeadNode->Link[ubi_trPARENT])->Link[(int)(DeadNode->gender)]);
+
+ /* Now link the parent to the only grand-child and patch up the gender. */
+ tmp = ((DeadNode->Link[ubi_trLEFT])?ubi_trLEFT:ubi_trRIGHT);
+
+ p = (DeadNode->Link[tmp]);
+ if( NULL != p )
+ {
+ p->Link[ubi_trPARENT] = DeadNode->Link[ubi_trPARENT];
+ p->gender = DeadNode->gender;
+ }
+ (*parentp) = p;
+
+ /* Finished, reduce the node count and return. */
+ (RootPtr->count)--;
+ return( DeadNode );
+ } /* ubi_btRemove */
+
+ubi_btNodePtr ubi_btLocate( ubi_btRootPtr RootPtr,
+ ubi_btItemPtr FindMe,
+ ubi_trCompOps CompOp )
+ /* ------------------------------------------------------------------------ **
+ * The purpose of ubi_btLocate() is to find a node or set of nodes given
+ * a target value and a "comparison operator". The Locate() function is
+ * more flexible and (in the case of trees that may contain dupicate keys)
+ * more precise than the ubi_btFind() function. The latter is faster,
+ * but it only searches for exact matches and, if the tree contains
+ * duplicates, Find() may return a pointer to any one of the duplicate-
+ * keyed records.
+ *
+ * Input:
+ * RootPtr - A pointer to the header of the tree to be searched.
+ * FindMe - An ubi_btItemPtr that indicates the key for which to
+ * search.
+ * CompOp - One of the following:
+ * CompOp Return a pointer to the node with
+ * ------ ---------------------------------
+ * ubi_trLT - the last key value that is less
+ * than FindMe.
+ * ubi_trLE - the first key matching FindMe, or
+ * the last key that is less than
+ * FindMe.
+ * ubi_trEQ - the first key matching FindMe.
+ * ubi_trGE - the first key matching FindMe, or the
+ * first key greater than FindMe.
+ * ubi_trGT - the first key greater than FindMe.
+ * Output:
+ * A pointer to the node matching the criteria listed above under
+ * CompOp, or NULL if no node matched the criteria.
+ *
+ * Notes:
+ * In the case of trees with duplicate keys, Locate() will behave as
+ * follows:
+ *
+ * Find: 3 Find: 3
+ * Keys: 1 2 2 2 3 3 3 3 3 4 4 Keys: 1 1 2 2 2 4 4 5 5 5 6
+ * ^ ^ ^ ^ ^
+ * LT EQ GT LE GE
+ *
+ * That is, when returning a pointer to a node with a key that is LESS
+ * THAN the target key (FindMe), Locate() will return a pointer to the
+ * LAST matching node.
+ * When returning a pointer to a node with a key that is GREATER
+ * THAN the target key (FindMe), Locate() will return a pointer to the
+ * FIRST matching node.
+ *
+ * See Also: ubi_btFind(), ubi_btFirstOf(), ubi_btLastOf().
+ * ------------------------------------------------------------------------ **
+ */
+ {
+ register ubi_btNodePtr p;
+ ubi_btNodePtr parent;
+ char whichkid;
+
+ /* Start by searching for a matching node. */
+ p = TreeFind( FindMe,
+ RootPtr->root,
+ &parent,
+ &whichkid,
+ RootPtr->cmp );
+
+ if( NULL != p ) /* If we have found a match, we can resolve as follows: */
+ {
+ switch( CompOp )
+ {
+ case ubi_trLT: /* It's just a jump to the left... */
+ p = Border( RootPtr, FindMe, p, ubi_trLEFT );
+ return( Neighbor( p, ubi_trLEFT ) );
+ case ubi_trGT: /* ...and then a jump to the right. */
+ p = Border( RootPtr, FindMe, p, ubi_trRIGHT );
+ return( Neighbor( p, ubi_trRIGHT ) );
+ default:
+ p = Border( RootPtr, FindMe, p, ubi_trLEFT );
+ return( p );
+ }
+ }
+
+ /* Else, no match. */
+ if( ubi_trEQ == CompOp ) /* If we were looking for an exact match... */
+ return( NULL ); /* ...forget it. */
+
+ /* We can still return a valid result for GT, GE, LE, and LT.
+ * <parent> points to a node with a value that is either just before or
+ * just after the target value.
+ * Remaining possibilities are LT and GT (including LE & GE).
+ */
+ if( (ubi_trLT == CompOp) || (ubi_trLE == CompOp) )
+ return( (ubi_trLEFT == whichkid) ? Neighbor( parent, whichkid ) : parent );
+ else
+ return( (ubi_trRIGHT == whichkid) ? Neighbor( parent, whichkid ) : parent );
+ } /* ubi_btLocate */
+
+ubi_btNodePtr ubi_btFind( ubi_btRootPtr RootPtr,
+ ubi_btItemPtr FindMe )
+ /* ------------------------------------------------------------------------ **
+ * This function performs a non-recursive search of a tree for any node
+ * matching a specific key.
+ *
+ * Input:
+ * RootPtr - a pointer to the header of the tree to be searched.
+ * FindMe - a pointer to the key value for which to search.
+ *
+ * Output:
+ * A pointer to a node with a key that matches the key indicated by
+ * FindMe, or NULL if no such node was found.
+ *
+ * Note: In a tree that allows duplicates, the pointer returned *might
+ * not* point to the (sequentially) first occurance of the
+ * desired key. In such a tree, it may be more useful to use
+ * ubi_btLocate().
+ * ------------------------------------------------------------------------ **
+ */
+ {
+ return( qFind( RootPtr->cmp, FindMe, RootPtr->root ) );
+ } /* ubi_btFind */
+
+ubi_btNodePtr ubi_btNext( ubi_btNodePtr P )
+ /* ------------------------------------------------------------------------ **
+ * Given the node indicated by P, find the (sorted order) Next node in the
+ * tree.
+ * Input: P - a pointer to a node that exists in a binary tree.
+ * Output: A pointer to the "next" node in the tree, or NULL if P pointed
+ * to the "last" node in the tree or was NULL.
+ * ------------------------------------------------------------------------ **
+ */
+ {
+ return( Neighbor( P, ubi_trRIGHT ) );
+ } /* ubi_btNext */
+
+ubi_btNodePtr ubi_btPrev( ubi_btNodePtr P )
+ /* ------------------------------------------------------------------------ **
+ * Given the node indicated by P, find the (sorted order) Previous node in
+ * the tree.
+ * Input: P - a pointer to a node that exists in a binary tree.
+ * Output: A pointer to the "previous" node in the tree, or NULL if P
+ * pointed to the "first" node in the tree or was NULL.
+ * ------------------------------------------------------------------------ **
+ */
+ {
+ return( Neighbor( P, ubi_trLEFT ) );
+ } /* ubi_btPrev */
+
+ubi_btNodePtr ubi_btFirst( ubi_btNodePtr P )
+ /* ------------------------------------------------------------------------ **
+ * Given the node indicated by P, find the (sorted order) First node in the
+ * subtree of which *P is the root.
+ * Input: P - a pointer to a node that exists in a binary tree.
+ * Output: A pointer to the "first" node in a subtree that has *P as its
+ * root. This function will return NULL only if P is NULL.
+ * Note: In general, you will be passing in the value of the root field
+ * of an ubi_btRoot structure.
+ * ------------------------------------------------------------------------ **
+ */
+ {
+ return( SubSlide( P, ubi_trLEFT ) );
+ } /* ubi_btFirst */
+
+ubi_btNodePtr ubi_btLast( ubi_btNodePtr P )
+ /* ------------------------------------------------------------------------ **
+ * Given the node indicated by P, find the (sorted order) Last node in the
+ * subtree of which *P is the root.
+ * Input: P - a pointer to a node that exists in a binary tree.
+ * Output: A pointer to the "last" node in a subtree that has *P as its
+ * root. This function will return NULL only if P is NULL.
+ * Note: In general, you will be passing in the value of the root field
+ * of an ubi_btRoot structure.
+ * ------------------------------------------------------------------------ **
+ */
+ {
+ return( SubSlide( P, ubi_trRIGHT ) );
+ } /* ubi_btLast */
+
+ubi_btNodePtr ubi_btFirstOf( ubi_btRootPtr RootPtr,
+ ubi_btItemPtr MatchMe,
+ ubi_btNodePtr p )
+ /* ------------------------------------------------------------------------ **
+ * Given a tree that a allows duplicate keys, and a pointer to a node in
+ * the tree, this function will return a pointer to the first (traversal
+ * order) node with the same key value.
+ *
+ * Input: RootPtr - A pointer to the root of the tree.
+ * MatchMe - A pointer to the key value. This should probably
+ * point to the key within node *p.
+ * p - A pointer to a node in the tree.
+ * Output: A pointer to the first node in the set of nodes with keys
+ * matching <FindMe>.
+ * Notes: Node *p MUST be in the set of nodes with keys matching
+ * <FindMe>. If not, this function will return NULL.
+ *
+ * 4.7: Bug found & fixed by Massimo Campostrini,
+ * Istituto Nazionale di Fisica Nucleare, Sezione di Pisa.
+ *
+ * ------------------------------------------------------------------------ **
+ */
+ {
+ /* If our starting point is invalid, return NULL. */
+ if( (NULL == p)
+ || (ubi_trEQUAL != ubi_trAbNormal( (*(RootPtr->cmp))( MatchMe, p ) )) )
+ return( NULL );
+ return( Border( RootPtr, MatchMe, p, ubi_trLEFT ) );
+ } /* ubi_btFirstOf */
+
+ubi_btNodePtr ubi_btLastOf( ubi_btRootPtr RootPtr,
+ ubi_btItemPtr MatchMe,
+ ubi_btNodePtr p )
+ /* ------------------------------------------------------------------------ **
+ * Given a tree that a allows duplicate keys, and a pointer to a node in
+ * the tree, this function will return a pointer to the last (traversal
+ * order) node with the same key value.
+ *
+ * Input: RootPtr - A pointer to the root of the tree.
+ * MatchMe - A pointer to the key value. This should probably
+ * point to the key within node *p.
+ * p - A pointer to a node in the tree.
+ * Output: A pointer to the last node in the set of nodes with keys
+ * matching <FindMe>.
+ * Notes: Node *p MUST be in the set of nodes with keys matching
+ * <FindMe>. If not, this function will return NULL.
+ *
+ * 4.7: Bug found & fixed by Massimo Campostrini,
+ * Istituto Nazionale di Fisica Nucleare, Sezione di Pisa.
+ *
+ * ------------------------------------------------------------------------ **
+ */
+ {
+ /* If our starting point is invalid, return NULL. */
+ if( (NULL != p)
+ || (ubi_trEQUAL != ubi_trAbNormal( (*(RootPtr->cmp))( MatchMe, p ) )) )
+ return( NULL );
+ return( Border( RootPtr, MatchMe, p, ubi_trRIGHT ) );
+ } /* ubi_btLastOf */
+
+unsigned long ubi_btTraverse( ubi_btRootPtr RootPtr,
+ ubi_btActionRtn EachNode,
+ void *UserData )
+ /* ------------------------------------------------------------------------ **
+ * Traverse a tree in sorted order (non-recursively). At each node, call
+ * (*EachNode)(), passing a pointer to the current node, and UserData as the
+ * second parameter.
+ *
+ * Input: RootPtr - a pointer to an ubi_btRoot structure that indicates
+ * the tree to be traversed.
+ * EachNode - a pointer to a function to be called at each node
+ * as the node is visited.
+ * UserData - a generic pointer that may point to anything that
+ * you choose.
+ *
+ * Output: A count of the number of nodes visited. This will be zero
+ * if the tree is empty.
+ *
+ * ------------------------------------------------------------------------ **
+ */
+ {
+ ubi_btNodePtr p = ubi_btFirst( RootPtr->root );
+ unsigned long count = 0;
+
+ while( NULL != p )
+ {
+ (*EachNode)( p, UserData );
+ count++;
+ p = ubi_btNext( p );
+ }
+ return( count );
+ } /* ubi_btTraverse */
+
+unsigned long ubi_btKillTree( ubi_btRootPtr RootPtr,
+ ubi_btKillNodeRtn FreeNode )
+ /* ------------------------------------------------------------------------ **
+ * Delete an entire tree (non-recursively) and reinitialize the ubi_btRoot
+ * structure. Return a count of the number of nodes deleted.
+ *
+ * Input: RootPtr - a pointer to an ubi_btRoot structure that indicates
+ * the root of the tree to delete.
+ * FreeNode - a function that will be called for each node in the
+ * tree to deallocate the memory used by the node.
+ *
+ * Output: The number of nodes removed from the tree.
+ * A value of 0 will be returned if:
+ * - The tree actually contains 0 entries.
+ * - the value of <RootPtr> is NULL, in which case the tree is
+ * assumed to be empty
+ * - the value of <FreeNode> is NULL, in which case entries
+ * cannot be removed, so 0 is returned. *Make sure that you
+ * provide a valid value for <FreeNode>*.
+ * In all other cases, you should get a positive value equal to
+ * the value of RootPtr->count upon entry.
+ *
+ * ------------------------------------------------------------------------ **
+ */
+ {
+ ubi_btNodePtr p, q;
+ unsigned long count = 0;
+
+ if( (NULL == RootPtr) || (NULL == FreeNode) )
+ return( 0 );
+
+ p = ubi_btFirst( RootPtr->root );
+ while( NULL != p )
+ {
+ q = p;
+ while( q->Link[ubi_trRIGHT] )
+ q = SubSlide( q->Link[ubi_trRIGHT], ubi_trLEFT );
+ p = q->Link[ubi_trPARENT];
+ if( NULL != p )
+ p->Link[ ((p->Link[ubi_trLEFT] == q)?ubi_trLEFT:ubi_trRIGHT) ] = NULL;
+ (*FreeNode)((void *)q);
+ count++;
+ }
+
+ /* overkill... */
+ (void)ubi_btInitTree( RootPtr,
+ RootPtr->cmp,
+ RootPtr->flags );
+ return( count );
+ } /* ubi_btKillTree */
+
+ubi_btNodePtr ubi_btLeafNode( ubi_btNodePtr leader )
+ /* ------------------------------------------------------------------------ **
+ * Returns a pointer to a leaf node.
+ *
+ * Input: leader - Pointer to a node at which to start the descent.
+ *
+ * Output: A pointer to a leaf node selected in a somewhat arbitrary
+ * manner.
+ *
+ * Notes: I wrote this function because I was using splay trees as a
+ * database cache. The cache had a maximum size on it, and I
+ * needed a way of choosing a node to sacrifice if the cache
+ * became full. In a splay tree, less recently accessed nodes
+ * tend toward the bottom of the tree, meaning that leaf nodes
+ * are good candidates for removal. (I really can't think of
+ * any other reason to use this function.)
+ * + In a simple binary tree or an AVL tree, the most recently
+ * added nodes tend to be nearer the bottom, making this a *bad*
+ * way to choose which node to remove from the cache.
+ * + Randomizing the traversal order is probably a good idea. You
+ * can improve the randomization of leaf node selection by passing
+ * in pointers to nodes other than the root node each time. A
+ * pointer to any node in the tree will do. Of course, if you
+ * pass a pointer to a leaf node you'll get the same thing back.
+ *
+ * ------------------------------------------------------------------------ **
+ */
+ {
+ ubi_btNodePtr follower = NULL;
+ int whichway = ubi_trLEFT;
+
+ while( NULL != leader )
+ {
+ follower = leader;
+ leader = follower->Link[ whichway ];
+ if( NULL == leader )
+ {
+ whichway = ubi_trRevWay( whichway );
+ leader = follower->Link[ whichway ];
+ }
+ }
+
+ return( follower );
+ } /* ubi_btLeafNode */
+
+int ubi_btModuleID( int size, char *list[] )
+ /* ------------------------------------------------------------------------ **
+ * Returns a set of strings that identify the module.
+ *
+ * Input: size - The number of elements in the array <list>.
+ * list - An array of pointers of type (char *). This array
+ * should, initially, be empty. This function will fill
+ * in the array with pointers to strings.
+ * Output: The number of elements of <list> that were used. If this value
+ * is less than <size>, the values of the remaining elements are
+ * not guaranteed.
+ *
+ * Notes: Please keep in mind that the pointers returned indicate strings
+ * stored in static memory. Don't free() them, don't write over
+ * them, etc. Just read them.
+ * ------------------------------------------------------------------------ **
+ */
+ {
+ if( size > 0 )
+ {
+ list[0] = ModuleID;
+ if( size > 1 )
+ list[1] = NULL;
+ return( 1 );
+ }
+ return( 0 );
+ } /* ubi_btModuleID */
+
+
+/* ========================================================================== */
diff --git a/source/ubiqx/ubi_BinTree.h b/source/ubiqx/ubi_BinTree.h
new file mode 100644
index 00000000000..c0c6d593094
--- /dev/null
+++ b/source/ubiqx/ubi_BinTree.h
@@ -0,0 +1,864 @@
+#ifndef UBI_BINTREE_H
+#define UBI_BINTREE_H
+/* ========================================================================== **
+ * ubi_BinTree.h
+ *
+ * Copyright (C) 1991-1998 by Christopher R. Hertel
+ *
+ * Email: crh@ubiqx.mn.org
+ * -------------------------------------------------------------------------- **
+ *
+ * This module implements a simple binary tree.
+ *
+ * -------------------------------------------------------------------------- **
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 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 Library General Public
+ * License along with this library; if not, write to the Free
+ * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * -------------------------------------------------------------------------- **
+ *
+ * Log: ubi_BinTree.h,v
+ * Revision 4.10 2000/06/06 20:38:40 crh
+ * In the ReplaceNode() function, the old node header was being copied
+ * to the new node header using a byte-by-byte copy. This was causing
+ * the 'insure' software testing program to report a memory leak. The
+ * fix was to do a simple assignement: *newnode = *oldnode;
+ * This quieted the (errant) memory leak reports and is probably a bit
+ * faster than the bytewise copy.
+ *
+ * Revision 4.9 2000/01/08 23:24:30 crh
+ * Clarified a variety of if( pointer ) lines, replacing them with
+ * if( NULL != pointer ). This is more correct, and I have heard
+ * of at least one (obscure?) system out there that uses a non-zero
+ * value for NULL.
+ * Also, speed improvement in Neighbor(). It was comparing pointers
+ * when it could have compared two gender values. The pointer
+ * comparison was somewhat indirect (does pointer equal the pointer
+ * of the parent of the node pointed to by pointer). Urq.
+ *
+ * Revision 4.8 1999/09/22 03:40:30 crh
+ * Modified ubi_btTraverse() and ubi_btKillTree(). They now return an
+ * unsigned long indicating the number of nodes processed. The change
+ * is subtle. An empty tree formerly returned False, and now returns
+ * zero.
+ *
+ * Revision 4.7 1998/10/21 06:15:07 crh
+ * Fixed bugs in FirstOf() and LastOf() reported by Massimo Campostrini.
+ * See function comments.
+ *
+ * Revision 4.6 1998/07/25 17:02:10 crh
+ * Added the ubi_trNewTree() macro.
+ *
+ * Revision 4.5 1998/06/04 21:29:27 crh
+ * Upper-cased defined constants (eg UBI_BINTREE_H) in some header files.
+ * This is more "standard", and is what people expect. Weird, eh?
+ *
+ * Revision 4.4 1998/06/03 17:42:46 crh
+ * Further fiddling with sys_include.h. It's now in ubi_BinTree.h which is
+ * included by all of the binary tree files.
+ *
+ * Reminder: Some of the ubi_tr* macros in ubi_BinTree.h are redefined in
+ * ubi_AVLtree.h and ubi_SplayTree.h. This allows easy swapping
+ * of tree types by simply changing a header. Unfortunately, the
+ * macro redefinitions in ubi_AVLtree.h and ubi_SplayTree.h will
+ * conflict if used together. You must either choose a single tree
+ * type, or use the underlying function calls directly. Compare
+ * the two header files for more information.
+ *
+ * Revision 4.3 1998/06/02 01:28:43 crh
+ * Changed ubi_null.h to sys_include.h to make it more generic.
+ *
+ * Revision 4.2 1998/05/20 04:32:36 crh
+ * The C file now includes ubi_null.h. See ubi_null.h for more info.
+ * Also, the balance and gender fields of the node were declared as
+ * signed char. As I understand it, at least one SunOS or Solaris
+ * compiler doesn't like "signed char". The declarations were
+ * wrong anyway, so I changed them to simple "char".
+ *
+ * Revision 4.1 1998/03/31 06:13:47 crh
+ * Thomas Aglassinger sent E'mail pointing out errors in the
+ * dereferencing of function pointers, and a missing typecast.
+ * Thanks, Thomas!
+ *
+ * Revision 4.0 1998/03/10 03:16:04 crh
+ * Added the AVL field 'balance' to the ubi_btNode structure. This means
+ * that all BinTree modules now use the same basic node structure, which
+ * greatly simplifies the AVL module.
+ * Decided that this was a big enough change to justify a new major revision
+ * number. 3.0 was an error, so we're at 4.0.
+ *
+ * Revision 2.6 1998/01/24 06:27:30 crh
+ * Added ubi_trCount() macro.
+ *
+ * Revision 2.5 1997/12/23 03:59:21 crh
+ * In this version, all constants & macros defined in the header file have
+ * the ubi_tr prefix. Also cleaned up anything that gcc complained about
+ * when run with '-pedantic -fsyntax-only -Wall'.
+ *
+ * Revision 2.4 1997/07/26 04:11:14 crh
+ * + Just to be annoying I changed ubi_TRUE and ubi_FALSE to ubi_trTRUE
+ * and ubi_trFALSE.
+ * + There is now a type ubi_trBool to go with ubi_trTRUE and ubi_trFALSE.
+ * + There used to be something called "ubi_TypeDefs.h". I got rid of it.
+ * + Added function ubi_btLeafNode().
+ *
+ * Revision 2.3 1997/06/03 05:15:27 crh
+ * Changed TRUE and FALSE to ubi_TRUE and ubi_FALSE to avoid conflicts.
+ * Also changed the interface to function InitTree(). See the comments
+ * for this function for more information.
+ *
+ * Revision 2.2 1995/10/03 22:00:40 CRH
+ * Ubisized!
+ *
+ * Revision 2.1 95/03/09 23:43:46 CRH
+ * Added the ModuleID static string and function. These modules are now
+ * self-identifying.
+ *
+ * Revision 2.0 95/02/27 22:00:33 CRH
+ * Revision 2.0 of this program includes the following changes:
+ *
+ * 1) A fix to a major typo in the RepaceNode() function.
+ * 2) The addition of the static function Border().
+ * 3) The addition of the public functions FirstOf() and LastOf(), which
+ * use Border(). These functions are used with trees that allow
+ * duplicate keys.
+ * 4) A complete rewrite of the Locate() function. Locate() now accepts
+ * a "comparison" operator.
+ * 5) Overall enhancements to both code and comments.
+ *
+ * I decided to give this a new major rev number because the interface has
+ * changed. In particular, there are two new functions, and changes to the
+ * Locate() function.
+ *
+ * Revision 1.0 93/10/15 22:55:04 CRH
+ * With this revision, I have added a set of #define's that provide a single,
+ * standard API to all existing tree modules. Until now, each of the three
+ * existing modules had a different function and typedef prefix, as follows:
+ *
+ * Module Prefix
+ * ubi_BinTree ubi_bt
+ * ubi_AVLtree ubi_avl
+ * ubi_SplayTree ubi_spt
+ *
+ * To further complicate matters, only those portions of the base module
+ * (ubi_BinTree) that were superceeded in the new module had the new names.
+ * For example, if you were using ubi_SplayTree, the locate function was
+ * called "ubi_sptLocate", but the next and previous functions remained
+ * "ubi_btNext" and "ubi_btPrev".
+ *
+ * This was not too terrible if you were familiar with the modules and knew
+ * exactly which tree model you wanted to use. If you wanted to be able to
+ * change modules (for speed comparisons, etc), things could get messy very
+ * quickly.
+ *
+ * So, I have added a set of defined names that get redefined in any of the
+ * descendant modules. To use this standardized interface in your code,
+ * simply replace all occurances of "ubi_bt", "ubi_avl", and "ubi_spt" with
+ * "ubi_tr". The "ubi_tr" names will resolve to the correct function or
+ * datatype names for the module that you are using. Just remember to
+ * include the header for that module in your program file. Because these
+ * names are handled by the preprocessor, there is no added run-time
+ * overhead.
+ *
+ * Note that the original names do still exist, and can be used if you wish
+ * to write code directly to a specific module. This should probably only be
+ * done if you are planning to implement a new descendant type, such as
+ * red/black trees. CRH
+ *
+ * V0.0 - June, 1991 - Written by Christopher R. Hertel (CRH).
+ *
+ * ========================================================================== **
+ */
+
+#include "sys_include.h" /* Global include file, used to adapt the ubiqx
+ * modules to the host environment and the project
+ * with which the modules will be used. See
+ * sys_include.h for more info.
+ */
+
+/* -------------------------------------------------------------------------- **
+ * Macros and constants.
+ *
+ * General purpose:
+ * ubi_trTRUE - Boolean TRUE.
+ * ubi_trFALSE - Boolean FALSE.
+ *
+ * Flags used in the tree header:
+ * ubi_trOVERWRITE - This flag indicates that an existing node may be
+ * overwritten by a new node with a matching key.
+ * ubi_trDUPKEY - This flag indicates that the tree allows duplicate
+ * keys. If the tree does allow duplicates, the
+ * overwrite flag is ignored.
+ *
+ * Node link array index constants: (Each node has an array of three
+ * pointers. One to the left, one to the right, and one back to the
+ * parent.)
+ * ubi_trLEFT - Left child pointer.
+ * ubi_trPARENT - Parent pointer.
+ * ubi_trRIGHT - Right child pointer.
+ * ubi_trEQUAL - Synonym for PARENT.
+ *
+ * ubi_trCompOps: These values are used in the ubi_trLocate() function.
+ * ubi_trLT - request the first instance of the greatest key less than
+ * the search key.
+ * ubi_trLE - request the first instance of the greatest key that is less
+ * than or equal to the search key.
+ * ubi_trEQ - request the first instance of key that is equal to the
+ * search key.
+ * ubi_trGE - request the first instance of a key that is greater than
+ * or equal to the search key.
+ * ubi_trGT - request the first instance of the first key that is greater
+ * than the search key.
+ * -------------------------------------------------------------------------- **
+ */
+
+#define ubi_trTRUE 0xFF
+#define ubi_trFALSE 0x00
+
+#define ubi_trOVERWRITE 0x01 /* Turn on allow overwrite */
+#define ubi_trDUPKEY 0x02 /* Turn on allow duplicate keys */
+
+/* Pointer array index constants... */
+#define ubi_trLEFT 0x00
+#define ubi_trPARENT 0x01
+#define ubi_trRIGHT 0x02
+#define ubi_trEQUAL ubi_trPARENT
+
+typedef enum {
+ ubi_trLT = 1,
+ ubi_trLE,
+ ubi_trEQ,
+ ubi_trGE,
+ ubi_trGT
+ } ubi_trCompOps;
+
+/* -------------------------------------------------------------------------- **
+ * These three macros allow simple manipulation of pointer index values (LEFT,
+ * RIGHT, and PARENT).
+ *
+ * Normalize() - converts {LEFT, PARENT, RIGHT} into {-1, 0 ,1}. C
+ * uses {negative, zero, positive} values to indicate
+ * {less than, equal to, greater than}.
+ * AbNormal() - converts {negative, zero, positive} to {LEFT, PARENT,
+ * RIGHT} (opposite of Normalize()). Note: C comparison
+ * functions, such as strcmp(), return {negative, zero,
+ * positive} values, which are not necessarily {-1, 0,
+ * 1}. This macro uses the the ubi_btSgn() function to
+ * compensate.
+ * RevWay() - converts LEFT to RIGHT and RIGHT to LEFT. PARENT (EQUAL)
+ * is left as is.
+ * -------------------------------------------------------------------------- **
+ */
+#define ubi_trNormalize(W) ((char)( (W) - ubi_trEQUAL ))
+#define ubi_trAbNormal(W) ((char)( ((char)ubi_btSgn( (long)(W) )) \
+ + ubi_trEQUAL ))
+#define ubi_trRevWay(W) ((char)( ubi_trEQUAL - ((W) - ubi_trEQUAL) ))
+
+/* -------------------------------------------------------------------------- **
+ * These macros allow us to quickly read the values of the OVERWRITE and
+ * DUPlicate KEY bits of the tree root flags field.
+ * -------------------------------------------------------------------------- **
+ */
+#define ubi_trDups_OK(A) \
+ ((ubi_trDUPKEY & ((A)->flags))?(ubi_trTRUE):(ubi_trFALSE))
+#define ubi_trOvwt_OK(A) \
+ ((ubi_trOVERWRITE & ((A)->flags))?(ubi_trTRUE):(ubi_trFALSE))
+
+/* -------------------------------------------------------------------------- **
+ * Additional Macros...
+ *
+ * ubi_trCount() - Given a pointer to a tree root, this macro returns the
+ * number of nodes currently in the tree.
+ *
+ * ubi_trNewTree() - This macro makes it easy to declare and initialize a
+ * tree header in one step. The line
+ *
+ * static ubi_trNewTree( MyTree, cmpfn, ubi_trDUPKEY );
+ *
+ * is equivalent to
+ *
+ * static ubi_trRoot MyTree[1]
+ * = {{ NULL, cmpfn, 0, ubi_trDUPKEY }};
+ *
+ * -------------------------------------------------------------------------- **
+ */
+
+#define ubi_trCount( R ) (((ubi_trRootPtr)(R))->count)
+
+#define ubi_trNewTree( N, C, F ) ubi_trRoot (N)[1] = {{ NULL, (C), 0, (F) }}
+
+/* -------------------------------------------------------------------------- **
+ * Typedefs...
+ *
+ * ubi_trBool - Your typcial true or false...
+ *
+ * Item Pointer: The ubi_btItemPtr is a generic pointer. It is used to
+ * indicate a key that is being searched for within the tree.
+ * Searching occurs whenever the ubi_trFind(), ubi_trLocate(),
+ * or ubi_trInsert() functions are called.
+ * -------------------------------------------------------------------------- **
+ */
+
+typedef unsigned char ubi_trBool;
+
+typedef void *ubi_btItemPtr; /* A pointer to key data within a node. */
+
+/* ------------------------------------------------------------------------- **
+ * Binary Tree Node Structure: This structure defines the basic elements of
+ * the tree nodes. In general you *SHOULD NOT PLAY WITH THESE FIELDS*!
+ * But, of course, I have to put the structure into this header so that
+ * you can use it as a building block.
+ *
+ * The fields are as follows:
+ * Link - an array of pointers. These pointers are manipulated by
+ * the BT routines. The pointers indicate the left and right
+ * child nodes and the parent node. By keeping track of the
+ * parent pointer, we avoid the need for recursive routines or
+ * hand-tooled stacks to keep track of our path back to the
+ * root. The use of these pointers is subject to change without
+ * notice.
+ * gender - a one-byte field indicating whether the node is the RIGHT or
+ * LEFT child of its parent. If the node is the root of the
+ * tree, gender will be PARENT.
+ * balance - only used by the AVL tree module. This field indicates
+ * the height balance at a given node. See ubi_AVLtree for
+ * details.
+ *
+ * ------------------------------------------------------------------------- **
+ */
+typedef struct ubi_btNodeStruct {
+ struct ubi_btNodeStruct *Link[ 3 ];
+ char gender;
+ char balance;
+ } ubi_btNode;
+
+typedef ubi_btNode *ubi_btNodePtr; /* Pointer to an ubi_btNode structure. */
+
+/* ------------------------------------------------------------------------- **
+ * The next three typedefs define standard function types used by the binary
+ * tree management routines. In particular:
+ *
+ * ubi_btCompFunc is a pointer to a comparison function. Comparison
+ * functions are passed an ubi_btItemPtr and an
+ * ubi_btNodePtr. They return a value that is (<0), 0,
+ * or (>0) to indicate that the Item is (respectively)
+ * "less than", "equal to", or "greater than" the Item
+ * contained within the node. (See ubi_btInitTree()).
+ * ubi_btActionRtn is a pointer to a function that may be called for each
+ * node visited when performing a tree traversal (see
+ * ubi_btTraverse()). The function will be passed two
+ * parameters: the first is a pointer to a node in the
+ * tree, the second is a generic pointer that may point to
+ * anything that you like.
+ * ubi_btKillNodeRtn is a pointer to a function that will deallocate the
+ * memory used by a node (see ubi_btKillTree()). Since
+ * memory management is left up to you, deallocation may
+ * mean anything that you want it to mean. Just remember
+ * that the tree *will* be destroyed and that none of the
+ * node pointers will be valid any more.
+ * ------------------------------------------------------------------------- **
+ */
+
+typedef int (*ubi_btCompFunc)( ubi_btItemPtr, ubi_btNodePtr );
+
+typedef void (*ubi_btActionRtn)( ubi_btNodePtr, void * );
+
+typedef void (*ubi_btKillNodeRtn)( ubi_btNodePtr );
+
+/* -------------------------------------------------------------------------- **
+ * Tree Root Structure: This structure gives us a convenient handle for
+ * accessing whole binary trees. The fields are:
+ * root - A pointer to the root node of the tree.
+ * count - A count of the number of nodes stored in the tree.
+ * cmp - A pointer to the comparison routine to be used when building or
+ * searching the tree.
+ * flags - A set of bit flags. Two flags are currently defined:
+ *
+ * ubi_trOVERWRITE - If set, this flag indicates that a new node should
+ * (bit 0x01) overwrite an old node if the two have identical
+ * keys (ie., the keys are equal).
+ * ubi_trDUPKEY - If set, this flag indicates that the tree is
+ * (bit 0x02) allowed to contain nodes with duplicate keys.
+ *
+ * NOTE: ubi_trInsert() tests ubi_trDUPKEY before ubi_trOVERWRITE.
+ *
+ * All of these values are set when you initialize the root structure by
+ * calling ubi_trInitTree().
+ * -------------------------------------------------------------------------- **
+ */
+
+typedef struct {
+ ubi_btNodePtr root; /* A pointer to the root node of the tree */
+ ubi_btCompFunc cmp; /* A pointer to the tree's comparison function */
+ unsigned long count; /* A count of the number of nodes in the tree */
+ char flags; /* Overwrite Y|N, Duplicate keys Y|N... */
+ } ubi_btRoot;
+
+typedef ubi_btRoot *ubi_btRootPtr; /* Pointer to an ubi_btRoot structure. */
+
+
+/* -------------------------------------------------------------------------- **
+ * Function Prototypes.
+ */
+
+long ubi_btSgn( long x );
+ /* ------------------------------------------------------------------------ **
+ * Return the sign of x; {negative,zero,positive} ==> {-1, 0, 1}.
+ *
+ * Input: x - a signed long integer value.
+ *
+ * Output: the "sign" of x, represented as follows:
+ * -1 == negative
+ * 0 == zero (no sign)
+ * 1 == positive
+ *
+ * Note: This utility is provided in order to facilitate the conversion
+ * of C comparison function return values into BinTree direction
+ * values: {LEFT, PARENT, EQUAL}. It is INCORPORATED into the
+ * AbNormal() conversion macro!
+ *
+ * ------------------------------------------------------------------------ **
+ */
+
+ubi_btNodePtr ubi_btInitNode( ubi_btNodePtr NodePtr );
+ /* ------------------------------------------------------------------------ **
+ * Initialize a tree node.
+ *
+ * Input: a pointer to a ubi_btNode structure to be initialized.
+ * Output: a pointer to the initialized ubi_btNode structure (ie. the
+ * same as the input pointer).
+ * ------------------------------------------------------------------------ **
+ */
+
+ubi_btRootPtr ubi_btInitTree( ubi_btRootPtr RootPtr,
+ ubi_btCompFunc CompFunc,
+ char Flags );
+ /* ------------------------------------------------------------------------ **
+ * Initialize the fields of a Tree Root header structure.
+ *
+ * Input: RootPtr - a pointer to an ubi_btRoot structure to be
+ * initialized.
+ * CompFunc - a pointer to a comparison function that will be used
+ * whenever nodes in the tree must be compared against
+ * outside values.
+ * Flags - One bytes worth of flags. Flags include
+ * ubi_trOVERWRITE and ubi_trDUPKEY. See the header
+ * file for more info.
+ *
+ * Output: a pointer to the initialized ubi_btRoot structure (ie. the
+ * same value as RootPtr).
+ *
+ * Note: The interface to this function has changed from that of
+ * previous versions. The <Flags> parameter replaces two
+ * boolean parameters that had the same basic effect.
+ * ------------------------------------------------------------------------ **
+ */
+
+ubi_trBool ubi_btInsert( ubi_btRootPtr RootPtr,
+ ubi_btNodePtr NewNode,
+ ubi_btItemPtr ItemPtr,
+ ubi_btNodePtr *OldNode );
+ /* ------------------------------------------------------------------------ **
+ * This function uses a non-recursive algorithm to add a new element to the
+ * tree.
+ *
+ * Input: RootPtr - a pointer to the ubi_btRoot structure that indicates
+ * the root of the tree to which NewNode is to be added.
+ * NewNode - a pointer to an ubi_btNode structure that is NOT
+ * part of any tree.
+ * ItemPtr - A pointer to the sort key that is stored within
+ * *NewNode. ItemPtr MUST point to information stored
+ * in *NewNode or an EXACT DUPLICATE. The key data
+ * indicated by ItemPtr is used to place the new node
+ * into the tree.
+ * OldNode - a pointer to an ubi_btNodePtr. When searching
+ * the tree, a duplicate node may be found. If
+ * duplicates are allowed, then the new node will
+ * be simply placed into the tree. If duplicates
+ * are not allowed, however, then one of two things
+ * may happen.
+ * 1) if overwritting *is not* allowed, this
+ * function will return FALSE (indicating that
+ * the new node could not be inserted), and
+ * *OldNode will point to the duplicate that is
+ * still in the tree.
+ * 2) if overwritting *is* allowed, then this
+ * function will swap **OldNode for *NewNode.
+ * In this case, *OldNode will point to the node
+ * that was removed (thus allowing you to free
+ * the node).
+ * ** If you are using overwrite mode, ALWAYS **
+ * ** check the return value of this parameter! **
+ * Note: You may pass NULL in this parameter, the
+ * function knows how to cope. If you do this,
+ * however, there will be no way to return a
+ * pointer to an old (ie. replaced) node (which is
+ * a problem if you are using overwrite mode).
+ *
+ * Output: a boolean value indicating success or failure. The function
+ * will return FALSE if the node could not be added to the tree.
+ * Such failure will only occur if duplicates are not allowed,
+ * nodes cannot be overwritten, AND a duplicate key was found
+ * within the tree.
+ * ------------------------------------------------------------------------ **
+ */
+
+ubi_btNodePtr ubi_btRemove( ubi_btRootPtr RootPtr,
+ ubi_btNodePtr DeadNode );
+ /* ------------------------------------------------------------------------ **
+ * This function removes the indicated node from the tree.
+ *
+ * Input: RootPtr - A pointer to the header of the tree that contains
+ * the node to be removed.
+ * DeadNode - A pointer to the node that will be removed.
+ *
+ * Output: This function returns a pointer to the node that was removed
+ * from the tree (ie. the same as DeadNode).
+ *
+ * Note: The node MUST be in the tree indicated by RootPtr. If not,
+ * strange and evil things will happen to your trees.
+ * ------------------------------------------------------------------------ **
+ */
+
+ubi_btNodePtr ubi_btLocate( ubi_btRootPtr RootPtr,
+ ubi_btItemPtr FindMe,
+ ubi_trCompOps CompOp );
+ /* ------------------------------------------------------------------------ **
+ * The purpose of ubi_btLocate() is to find a node or set of nodes given
+ * a target value and a "comparison operator". The Locate() function is
+ * more flexible and (in the case of trees that may contain dupicate keys)
+ * more precise than the ubi_btFind() function. The latter is faster,
+ * but it only searches for exact matches and, if the tree contains
+ * duplicates, Find() may return a pointer to any one of the duplicate-
+ * keyed records.
+ *
+ * Input:
+ * RootPtr - A pointer to the header of the tree to be searched.
+ * FindMe - An ubi_btItemPtr that indicates the key for which to
+ * search.
+ * CompOp - One of the following:
+ * CompOp Return a pointer to the node with
+ * ------ ---------------------------------
+ * ubi_trLT - the last key value that is less
+ * than FindMe.
+ * ubi_trLE - the first key matching FindMe, or
+ * the last key that is less than
+ * FindMe.
+ * ubi_trEQ - the first key matching FindMe.
+ * ubi_trGE - the first key matching FindMe, or the
+ * first key greater than FindMe.
+ * ubi_trGT - the first key greater than FindMe.
+ * Output:
+ * A pointer to the node matching the criteria listed above under
+ * CompOp, or NULL if no node matched the criteria.
+ *
+ * Notes:
+ * In the case of trees with duplicate keys, Locate() will behave as
+ * follows:
+ *
+ * Find: 3 Find: 3
+ * Keys: 1 2 2 2 3 3 3 3 3 4 4 Keys: 1 1 2 2 2 4 4 5 5 5 6
+ * ^ ^ ^ ^ ^
+ * LT EQ GT LE GE
+ *
+ * That is, when returning a pointer to a node with a key that is LESS
+ * THAN the target key (FindMe), Locate() will return a pointer to the
+ * LAST matching node.
+ * When returning a pointer to a node with a key that is GREATER
+ * THAN the target key (FindMe), Locate() will return a pointer to the
+ * FIRST matching node.
+ *
+ * See Also: ubi_btFind(), ubi_btFirstOf(), ubi_btLastOf().
+ * ------------------------------------------------------------------------ **
+ */
+
+ubi_btNodePtr ubi_btFind( ubi_btRootPtr RootPtr,
+ ubi_btItemPtr FindMe );
+ /* ------------------------------------------------------------------------ **
+ * This function performs a non-recursive search of a tree for any node
+ * matching a specific key.
+ *
+ * Input:
+ * RootPtr - a pointer to the header of the tree to be searched.
+ * FindMe - a pointer to the key value for which to search.
+ *
+ * Output:
+ * A pointer to a node with a key that matches the key indicated by
+ * FindMe, or NULL if no such node was found.
+ *
+ * Note: In a tree that allows duplicates, the pointer returned *might
+ * not* point to the (sequentially) first occurance of the
+ * desired key. In such a tree, it may be more useful to use
+ * ubi_btLocate().
+ * ------------------------------------------------------------------------ **
+ */
+
+ubi_btNodePtr ubi_btNext( ubi_btNodePtr P );
+ /* ------------------------------------------------------------------------ **
+ * Given the node indicated by P, find the (sorted order) Next node in the
+ * tree.
+ * Input: P - a pointer to a node that exists in a binary tree.
+ * Output: A pointer to the "next" node in the tree, or NULL if P pointed
+ * to the "last" node in the tree or was NULL.
+ * ------------------------------------------------------------------------ **
+ */
+
+ubi_btNodePtr ubi_btPrev( ubi_btNodePtr P );
+ /* ------------------------------------------------------------------------ **
+ * Given the node indicated by P, find the (sorted order) Previous node in
+ * the tree.
+ * Input: P - a pointer to a node that exists in a binary tree.
+ * Output: A pointer to the "previous" node in the tree, or NULL if P
+ * pointed to the "first" node in the tree or was NULL.
+ * ------------------------------------------------------------------------ **
+ */
+
+ubi_btNodePtr ubi_btFirst( ubi_btNodePtr P );
+ /* ------------------------------------------------------------------------ **
+ * Given the node indicated by P, find the (sorted order) First node in the
+ * subtree of which *P is the root.
+ * Input: P - a pointer to a node that exists in a binary tree.
+ * Output: A pointer to the "first" node in a subtree that has *P as its
+ * root. This function will return NULL only if P is NULL.
+ * Note: In general, you will be passing in the value of the root field
+ * of an ubi_btRoot structure.
+ * ------------------------------------------------------------------------ **
+ */
+
+ubi_btNodePtr ubi_btLast( ubi_btNodePtr P );
+ /* ------------------------------------------------------------------------ **
+ * Given the node indicated by P, find the (sorted order) Last node in the
+ * subtree of which *P is the root.
+ * Input: P - a pointer to a node that exists in a binary tree.
+ * Output: A pointer to the "last" node in a subtree that has *P as its
+ * root. This function will return NULL only if P is NULL.
+ * Note: In general, you will be passing in the value of the root field
+ * of an ubi_btRoot structure.
+ * ------------------------------------------------------------------------ **
+ */
+
+ubi_btNodePtr ubi_btFirstOf( ubi_btRootPtr RootPtr,
+ ubi_btItemPtr MatchMe,
+ ubi_btNodePtr p );
+ /* ------------------------------------------------------------------------ **
+ * Given a tree that a allows duplicate keys, and a pointer to a node in
+ * the tree, this function will return a pointer to the first (traversal
+ * order) node with the same key value.
+ *
+ * Input: RootPtr - A pointer to the root of the tree.
+ * MatchMe - A pointer to the key value. This should probably
+ * point to the key within node *p.
+ * p - A pointer to a node in the tree.
+ * Output: A pointer to the first node in the set of nodes with keys
+ * matching <FindMe>.
+ * Notes: Node *p MUST be in the set of nodes with keys matching
+ * <FindMe>. If not, this function will return NULL.
+ *
+ * 4.7: Bug found & fixed by Massimo Campostrini,
+ * Istituto Nazionale di Fisica Nucleare, Sezione di Pisa.
+ *
+ * ------------------------------------------------------------------------ **
+ */
+
+ubi_btNodePtr ubi_btLastOf( ubi_btRootPtr RootPtr,
+ ubi_btItemPtr MatchMe,
+ ubi_btNodePtr p );
+ /* ------------------------------------------------------------------------ **
+ * Given a tree that a allows duplicate keys, and a pointer to a node in
+ * the tree, this function will return a pointer to the last (traversal
+ * order) node with the same key value.
+ *
+ * Input: RootPtr - A pointer to the root of the tree.
+ * MatchMe - A pointer to the key value. This should probably
+ * point to the key within node *p.
+ * p - A pointer to a node in the tree.
+ * Output: A pointer to the last node in the set of nodes with keys
+ * matching <FindMe>.
+ * Notes: Node *p MUST be in the set of nodes with keys matching
+ * <FindMe>. If not, this function will return NULL.
+ *
+ * 4.7: Bug found & fixed by Massimo Campostrini,
+ * Istituto Nazionale di Fisica Nucleare, Sezione di Pisa.
+ *
+ * ------------------------------------------------------------------------ **
+ */
+
+unsigned long ubi_btTraverse( ubi_btRootPtr RootPtr,
+ ubi_btActionRtn EachNode,
+ void *UserData );
+ /* ------------------------------------------------------------------------ **
+ * Traverse a tree in sorted order (non-recursively). At each node, call
+ * (*EachNode)(), passing a pointer to the current node, and UserData as the
+ * second parameter.
+ *
+ * Input: RootPtr - a pointer to an ubi_btRoot structure that indicates
+ * the tree to be traversed.
+ * EachNode - a pointer to a function to be called at each node
+ * as the node is visited.
+ * UserData - a generic pointer that may point to anything that
+ * you choose.
+ *
+ * Output: A count of the number of nodes visited. This will be zero
+ * if the tree is empty.
+ *
+ * ------------------------------------------------------------------------ **
+ */
+
+
+unsigned long ubi_btKillTree( ubi_btRootPtr RootPtr,
+ ubi_btKillNodeRtn FreeNode );
+ /* ------------------------------------------------------------------------ **
+ * Delete an entire tree (non-recursively) and reinitialize the ubi_btRoot
+ * structure. Return a count of the number of nodes deleted.
+ *
+ * Input: RootPtr - a pointer to an ubi_btRoot structure that indicates
+ * the root of the tree to delete.
+ * FreeNode - a function that will be called for each node in the
+ * tree to deallocate the memory used by the node.
+ *
+ * Output: The number of nodes removed from the tree.
+ * A value of 0 will be returned if:
+ * - The tree actually contains 0 entries.
+ * - the value of <RootPtr> is NULL, in which case the tree is
+ * assumed to be empty
+ * - the value of <FreeNode> is NULL, in which case entries
+ * cannot be removed, so 0 is returned. *Make sure that you
+ * provide a valid value for <FreeNode>*.
+ * In all other cases, you should get a positive value equal to
+ * the value of RootPtr->count upon entry.
+ *
+ * ------------------------------------------------------------------------ **
+ */
+
+ubi_btNodePtr ubi_btLeafNode( ubi_btNodePtr leader );
+ /* ------------------------------------------------------------------------ **
+ * Returns a pointer to a leaf node.
+ *
+ * Input: leader - Pointer to a node at which to start the descent.
+ *
+ * Output: A pointer to a leaf node selected in a somewhat arbitrary
+ * manner.
+ *
+ * Notes: I wrote this function because I was using splay trees as a
+ * database cache. The cache had a maximum size on it, and I
+ * needed a way of choosing a node to sacrifice if the cache
+ * became full. In a splay tree, less recently accessed nodes
+ * tend toward the bottom of the tree, meaning that leaf nodes
+ * are good candidates for removal. (I really can't think of
+ * any other reason to use this function.)
+ * + In a simple binary tree or an AVL tree, the most recently
+ * added nodes tend to be nearer the bottom, making this a *bad*
+ * way to choose which node to remove from the cache.
+ * + Randomizing the traversal order is probably a good idea. You
+ * can improve the randomization of leaf node selection by passing
+ * in pointers to nodes other than the root node each time. A
+ * pointer to any node in the tree will do. Of course, if you
+ * pass a pointer to a leaf node you'll get the same thing back.
+ *
+ * ------------------------------------------------------------------------ **
+ */
+
+
+int ubi_btModuleID( int size, char *list[] );
+ /* ------------------------------------------------------------------------ **
+ * Returns a set of strings that identify the module.
+ *
+ * Input: size - The number of elements in the array <list>.
+ * list - An array of pointers of type (char *). This array
+ * should, initially, be empty. This function will fill
+ * in the array with pointers to strings.
+ * Output: The number of elements of <list> that were used. If this value
+ * is less than <size>, the values of the remaining elements are
+ * not guaranteed.
+ *
+ * Notes: Please keep in mind that the pointers returned indicate strings
+ * stored in static memory. Don't free() them, don't write over
+ * them, etc. Just read them.
+ * ------------------------------------------------------------------------ **
+ */
+
+/* -------------------------------------------------------------------------- **
+ * Masquarade...
+ *
+ * This set of defines allows you to write programs that will use any of the
+ * implemented binary tree modules (currently BinTree, AVLtree, and SplayTree).
+ * Instead of using ubi_bt..., use ubi_tr..., and select the tree type by
+ * including the appropriate module header.
+ */
+
+#define ubi_trItemPtr ubi_btItemPtr
+
+#define ubi_trNode ubi_btNode
+#define ubi_trNodePtr ubi_btNodePtr
+
+#define ubi_trRoot ubi_btRoot
+#define ubi_trRootPtr ubi_btRootPtr
+
+#define ubi_trCompFunc ubi_btCompFunc
+#define ubi_trActionRtn ubi_btActionRtn
+#define ubi_trKillNodeRtn ubi_btKillNodeRtn
+
+#define ubi_trSgn( x ) ubi_btSgn( x )
+
+#define ubi_trInitNode( Np ) ubi_btInitNode( (ubi_btNodePtr)(Np) )
+
+#define ubi_trInitTree( Rp, Cf, Fl ) \
+ ubi_btInitTree( (ubi_btRootPtr)(Rp), (ubi_btCompFunc)(Cf), (Fl) )
+
+#define ubi_trInsert( Rp, Nn, Ip, On ) \
+ ubi_btInsert( (ubi_btRootPtr)(Rp), (ubi_btNodePtr)(Nn), \
+ (ubi_btItemPtr)(Ip), (ubi_btNodePtr *)(On) )
+
+#define ubi_trRemove( Rp, Dn ) \
+ ubi_btRemove( (ubi_btRootPtr)(Rp), (ubi_btNodePtr)(Dn) )
+
+#define ubi_trLocate( Rp, Ip, Op ) \
+ ubi_btLocate( (ubi_btRootPtr)(Rp), \
+ (ubi_btItemPtr)(Ip), \
+ (ubi_trCompOps)(Op) )
+
+#define ubi_trFind( Rp, Ip ) \
+ ubi_btFind( (ubi_btRootPtr)(Rp), (ubi_btItemPtr)(Ip) )
+
+#define ubi_trNext( P ) ubi_btNext( (ubi_btNodePtr)(P) )
+
+#define ubi_trPrev( P ) ubi_btPrev( (ubi_btNodePtr)(P) )
+
+#define ubi_trFirst( P ) ubi_btFirst( (ubi_btNodePtr)(P) )
+
+#define ubi_trLast( P ) ubi_btLast( (ubi_btNodePtr)(P) )
+
+#define ubi_trFirstOf( Rp, Ip, P ) \
+ ubi_btFirstOf( (ubi_btRootPtr)(Rp), \
+ (ubi_btItemPtr)(Ip), \
+ (ubi_btNodePtr)(P) )
+
+#define ubi_trLastOf( Rp, Ip, P ) \
+ ubi_btLastOf( (ubi_btRootPtr)(Rp), \
+ (ubi_btItemPtr)(Ip), \
+ (ubi_btNodePtr)(P) )
+
+#define ubi_trTraverse( Rp, En, Ud ) \
+ ubi_btTraverse((ubi_btRootPtr)(Rp), (ubi_btActionRtn)(En), (void *)(Ud))
+
+#define ubi_trKillTree( Rp, Fn ) \
+ ubi_btKillTree( (ubi_btRootPtr)(Rp), (ubi_btKillNodeRtn)(Fn) )
+
+#define ubi_trLeafNode( Nd ) \
+ ubi_btLeafNode( (ubi_btNodePtr)(Nd) )
+
+#define ubi_trModuleID( s, l ) ubi_btModuleID( s, l )
+
+/* ========================================================================== */
+#endif /* UBI_BINTREE_H */
diff --git a/source/ubiqx/ubi_Cache.c b/source/ubiqx/ubi_Cache.c
new file mode 100644
index 00000000000..f428dcefe97
--- /dev/null
+++ b/source/ubiqx/ubi_Cache.c
@@ -0,0 +1,505 @@
+/* ========================================================================== **
+ * ubi_Cache.c
+ *
+ * Copyright (C) 1997 by Christopher R. Hertel
+ *
+ * Email: crh@ubiqx.mn.org
+ * -------------------------------------------------------------------------- **
+ *
+ * This module implements a generic cache.
+ *
+ * -------------------------------------------------------------------------- **
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 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 Library General Public
+ * License along with this library; if not, write to the Free
+ * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * -------------------------------------------------------------------------- **
+ *
+ * This module uses a splay tree to implement a simple cache. The cache
+ * module adds a thin layer of functionality to the splay tree. In
+ * particular:
+ *
+ * - The tree (cache) may be limited in size by the number of
+ * entries permitted or the amount of memory used. When either
+ * limit is exceeded cache entries are removed until the cache
+ * conforms.
+ * - Some statistical information is kept so that an approximate
+ * "hit ratio" can be calculated.
+ * - There are several functions available that provide access to
+ * and management of cache size limits, hit ratio, and tree
+ * trimming.
+ *
+ * The splay tree is used because recently accessed items tend toward the
+ * top of the tree and less recently accessed items tend toward the bottom.
+ * This makes it easy to purge less recently used items should the cache
+ * exceed its limits.
+ *
+ * To use this module, you will need to supply a comparison function of
+ * type ubi_trCompFunc and a node-freeing function of type
+ * ubi_trKillNodeRtn. See ubi_BinTree.h for more information on
+ * these. (This is all basic ubiqx tree management stuff.)
+ *
+ * Notes:
+ *
+ * - Cache performance will start to suffer dramatically if the
+ * cache becomes large enough to force the OS to start swapping
+ * memory to disk. This is because the nodes of the underlying tree
+ * will be scattered across memory in an order that is completely
+ * unrelated to their traversal order. As more and more of the
+ * cache is placed into swap space, more and more swaps will be
+ * required for a simple traversal (...and then there's the splay
+ * operation).
+ *
+ * In one simple test under Linux, the load and dump of a cache of
+ * 400,000 entries took only 1min, 40sec of real time. The same
+ * test with 450,000 records took 2 *hours* and eight minutes.
+ *
+ * - In an effort to save memory, I considered using an unsigned
+ * short to save the per-entry entry size. I would have tucked this
+ * value into some unused space in the tree node structure. On
+ * 32-bit word aligned systems this would have saved an additional
+ * four bytes per entry. I may revisit this issue, but for now I've
+ * decided against it.
+ *
+ * Using an unsigned short would limit the size of an entry to 64K
+ * bytes. That's probably more than enough for most applications.
+ * The key word in that last sentence, however, is "probably". I
+ * really dislike imposing such limits on things.
+ *
+ * - Each entry keeps track of the amount of memory it used and the
+ * cache header keeps the total. This information is provided via
+ * the EntrySize parameter in ubi_cachePut(), so it is up to you to
+ * make sure that the numbers are accurate. (The numbers don't even
+ * have to represent bytes used.)
+ *
+ * As you consider this, note that the strdup() function--as an
+ * example--will call malloc(). The latter generally allocates a
+ * multiple of the system word size, which may be more than the
+ * number of bytes needed to store the string.
+ *
+ * -------------------------------------------------------------------------- **
+ *
+ * Log: ubi_Cache.c,v
+ * Revision 0.4 1999/09/22 03:42:24 crh
+ * Fixed a minor typo.
+ *
+ * Revision 0.3 1998/06/03 18:00:15 crh
+ * Further fiddling with sys_include.h, which is no longer explicitly
+ * included by this module since it is inherited from ubi_BinTree.h.
+ *
+ * Revision 0.2 1998/06/02 01:36:18 crh
+ * Changed include name from ubi_null.h to sys_include.h to make it
+ * more generic.
+ *
+ * Revision 0.1 1998/05/20 04:36:02 crh
+ * The C file now includes ubi_null.h. See ubi_null.h for more info.
+ *
+ * Revision 0.0 1997/12/18 06:24:33 crh
+ * Initial Revision.
+ *
+ * ========================================================================== **
+ */
+
+#include "ubi_Cache.h" /* Header for *this* module. */
+
+/* -------------------------------------------------------------------------- **
+ * Static data...
+ */
+
+/* commented out until I make use of it...
+static char ModuleID[] =
+"ubi_Cache\n\
+\tRevision: 0.4 \n\
+\tDate: 1999/09/22 03:42:24 \n\
+\tAuthor: crh \n";
+*/
+
+/* -------------------------------------------------------------------------- **
+ * Internal functions...
+ */
+
+static void free_entry( ubi_cacheRootPtr CachePtr, ubi_cacheEntryPtr EntryPtr )
+ /* ------------------------------------------------------------------------ **
+ * Free a ubi_cacheEntry, and adjust the mem_used counter accordingly.
+ *
+ * Input: CachePtr - A pointer to the cache from which the entry has
+ * been removed.
+ * EntryPtr - A pointer to the already removed entry.
+ *
+ * Output: none.
+ *
+ * Notes: The entry must be removed from the cache *before* this function
+ * is called!!!!
+ * ------------------------------------------------------------------------ **
+ */
+ {
+ CachePtr->mem_used -= EntryPtr->entry_size;
+ (*CachePtr->free_func)( (void *)EntryPtr );
+ } /* free_entry */
+
+static void cachetrim( ubi_cacheRootPtr crptr )
+ /* ------------------------------------------------------------------------ **
+ * Remove entries from the cache until the number of entries and the amount
+ * of memory used are *both* below or at the maximum.
+ *
+ * Input: crptr - pointer to the cache to be trimmed.
+ *
+ * Output: None.
+ *
+ * ------------------------------------------------------------------------ **
+ */
+ {
+ while( ( crptr->max_entries && (crptr->max_entries < crptr->root.count) )
+ || ( crptr->max_memory && (crptr->max_memory < crptr->mem_used) ) )
+ {
+ if( !ubi_cacheReduce( crptr, 1 ) )
+ return;
+ }
+ } /* cachetrim */
+
+
+/* -------------------------------------------------------------------------- **
+ * Exported functions...
+ */
+
+ubi_cacheRootPtr ubi_cacheInit( ubi_cacheRootPtr CachePtr,
+ ubi_trCompFunc CompFunc,
+ ubi_trKillNodeRtn FreeFunc,
+ unsigned long MaxEntries,
+ unsigned long MaxMemory )
+ /* ------------------------------------------------------------------------ **
+ * Initialize a cache header structure.
+ *
+ * Input: CachePtr - A pointer to a ubi_cacheRoot structure that is
+ * to be initialized.
+ * CompFunc - A pointer to the function that will be called
+ * to compare two cache values. See the module
+ * comments, above, for more information.
+ * FreeFunc - A pointer to a function that will be called
+ * to free a cache entry. If you allocated
+ * the cache entry using malloc(), then this
+ * will likely be free(). If you are allocating
+ * cache entries from a free list, then this will
+ * likely be a function that returns memory to the
+ * free list, etc.
+ * MaxEntries - The maximum number of entries that will be
+ * allowed to exist in the cache. If this limit
+ * is exceeded, then existing entries will be
+ * removed from the cache. A value of zero
+ * indicates that there is no limit on the number
+ * of cache entries. See ubi_cachePut().
+ * MaxMemory - The maximum amount of memory, in bytes, to be
+ * allocated to the cache (excluding the cache
+ * header). If this is exceeded, existing entries
+ * in the cache will be removed until enough memory
+ * has been freed to meet the condition. See
+ * ubi_cachePut().
+ *
+ * Output: A pointer to the initialized cache (i.e., the same as CachePtr).
+ *
+ * Notes: Both MaxEntries and MaxMemory may be changed after the cache
+ * has been created. See
+ * ubi_cacheSetMaxEntries()
+ * ubi_cacheSetMaxMemory()
+ * ubi_cacheGetMaxEntries()
+ * ubi_cacheGetMaxMemory() (the latter two are macros).
+ *
+ * - Memory is allocated in multiples of the word size. The
+ * return value of the strlen() function does not reflect
+ * this; it will allways be less than or equal to the amount
+ * of memory actually allocated. Keep this in mind when
+ * choosing a value for MaxMemory.
+ *
+ * ------------------------------------------------------------------------ **
+ */
+ {
+ if( CachePtr )
+ {
+ (void)ubi_trInitTree( CachePtr, CompFunc, ubi_trOVERWRITE );
+ CachePtr->free_func = FreeFunc;
+ CachePtr->max_entries = MaxEntries;
+ CachePtr->max_memory = MaxMemory;
+ CachePtr->mem_used = 0;
+ CachePtr->cache_hits = 0;
+ CachePtr->cache_trys = 0;
+ }
+ return( CachePtr );
+ } /* ubi_cacheInit */
+
+ubi_cacheRootPtr ubi_cacheClear( ubi_cacheRootPtr CachePtr )
+ /* ------------------------------------------------------------------------ **
+ * Remove and free all entries in an existing cache.
+ *
+ * Input: CachePtr - A pointer to the cache that is to be cleared.
+ *
+ * Output: A pointer to the cache header (i.e., the same as CachePtr).
+ * This function re-initializes the cache header.
+ *
+ * ------------------------------------------------------------------------ **
+ */
+ {
+ if( CachePtr )
+ {
+ (void)ubi_trKillTree( CachePtr, CachePtr->free_func );
+ CachePtr->mem_used = 0;
+ CachePtr->cache_hits = 0;
+ CachePtr->cache_trys = 0;
+ }
+ return( CachePtr );
+ } /* ubi_cacheClear */
+
+void ubi_cachePut( ubi_cacheRootPtr CachePtr,
+ unsigned long EntrySize,
+ ubi_cacheEntryPtr EntryPtr,
+ ubi_trItemPtr Key )
+ /* ------------------------------------------------------------------------ **
+ * Add an entry to the cache.
+ *
+ * Input: CachePtr - A pointer to the cache into which the entry
+ * will be added.
+ * EntrySize - The size, in bytes, of the memory block indicated
+ * by EntryPtr. This will be copied into the
+ * EntryPtr->entry_size field.
+ * EntryPtr - A pointer to a memory block that begins with a
+ * ubi_cacheEntry structure. The entry structure
+ * should be followed immediately by the data to be
+ * cached (even if that is a pointer to yet more data).
+ * Key - Pointer used to identify the lookup key within the
+ * Entry.
+ *
+ * Output: None.
+ *
+ * Notes: After adding the new node, the cache is "trimmed". This
+ * removes extra nodes if the tree has exceeded it's memory or
+ * entry count limits. It is unlikely that the newly added node
+ * will be purged from the cache (assuming a reasonably large
+ * cache), since new nodes in a splay tree (which is what this
+ * module was designed to use) are moved to the top of the tree
+ * and the cache purge process removes nodes from the bottom of
+ * the tree.
+ * - The underlying splay tree is opened in OVERWRITE mode. If
+ * the input key matches an existing key, the existing entry will
+ * be politely removed from the tree and freed.
+ * - Memory is allocated in multiples of the word size. The
+ * return value of the strlen() function does not reflect
+ * this; it will allways be less than or equal to the amount
+ * of memory actually allocated.
+ *
+ * ------------------------------------------------------------------------ **
+ */
+ {
+ ubi_trNodePtr OldNode;
+
+ EntryPtr->entry_size = EntrySize;
+ CachePtr->mem_used += EntrySize;
+ (void)ubi_trInsert( CachePtr, EntryPtr, Key, &OldNode );
+ if( OldNode )
+ free_entry( CachePtr, (ubi_cacheEntryPtr)OldNode );
+
+ cachetrim( CachePtr );
+ } /* ubi_cachePut */
+
+ubi_cacheEntryPtr ubi_cacheGet( ubi_cacheRootPtr CachePtr,
+ ubi_trItemPtr FindMe )
+ /* ------------------------------------------------------------------------ **
+ * Attempt to retrieve an entry from the cache.
+ *
+ * Input: CachePtr - A ponter to the cache that is to be searched.
+ * FindMe - A ubi_trItemPtr that indicates the key for which
+ * to search.
+ *
+ * Output: A pointer to the cache entry that was found, or NULL if no
+ * matching entry was found.
+ *
+ * Notes: This function also updates the hit ratio counters.
+ * The counters are unsigned short. If the number of cache tries
+ * reaches 32768, then both the number of tries and the number of
+ * hits are divided by two. This prevents the counters from
+ * overflowing. See the comments in ubi_cacheHitRatio() for
+ * additional notes.
+ *
+ * ------------------------------------------------------------------------ **
+ */
+ {
+ ubi_trNodePtr FoundPtr;
+
+ FoundPtr = ubi_trFind( CachePtr, FindMe );
+
+ if( FoundPtr )
+ CachePtr->cache_hits++;
+ CachePtr->cache_trys++;
+
+ if( CachePtr->cache_trys & 0x8000 )
+ {
+ CachePtr->cache_hits = CachePtr->cache_hits / 2;
+ CachePtr->cache_trys = CachePtr->cache_trys / 2;
+ }
+
+ return( (ubi_cacheEntryPtr)FoundPtr );
+ } /* ubi_cacheGet */
+
+ubi_trBool ubi_cacheDelete( ubi_cacheRootPtr CachePtr, ubi_trItemPtr DeleteMe )
+ /* ------------------------------------------------------------------------ **
+ * Find and delete the specified cache entry.
+ *
+ * Input: CachePtr - A pointer to the cache.
+ * DeleteMe - The key of the entry to be deleted.
+ *
+ * Output: TRUE if the entry was found & freed, else FALSE.
+ *
+ * ------------------------------------------------------------------------ **
+ */
+ {
+ ubi_trNodePtr FoundPtr;
+
+ FoundPtr = ubi_trFind( CachePtr, DeleteMe );
+ if( FoundPtr )
+ {
+ (void)ubi_trRemove( CachePtr, FoundPtr );
+ free_entry( CachePtr, (ubi_cacheEntryPtr)FoundPtr );
+ return( ubi_trTRUE );
+ }
+ return( ubi_trFALSE );
+ } /* ubi_cacheDelete */
+
+ubi_trBool ubi_cacheReduce( ubi_cacheRootPtr CachePtr, unsigned long count )
+ /* ------------------------------------------------------------------------ **
+ * Remove <count> entries from the bottom of the cache.
+ *
+ * Input: CachePtr - A pointer to the cache which is to be reduced in
+ * size.
+ * count - The number of entries to remove.
+ *
+ * Output: The function will return TRUE if <count> entries were removed,
+ * else FALSE. A return value of FALSE should indicate that
+ * there were less than <count> entries in the cache, and that the
+ * cache is now empty.
+ *
+ * Notes: This function forces a reduction in the number of cache entries
+ * without requiring that the MaxMemory or MaxEntries values be
+ * changed.
+ *
+ * ------------------------------------------------------------------------ **
+ */
+ {
+ ubi_trNodePtr NodePtr;
+
+ while( count )
+ {
+ NodePtr = ubi_trLeafNode( CachePtr->root.root );
+ if( NULL == NodePtr )
+ return( ubi_trFALSE );
+ else
+ {
+ (void)ubi_trRemove( CachePtr, NodePtr );
+ free_entry( CachePtr, (ubi_cacheEntryPtr)NodePtr );
+ }
+ count--;
+ }
+ return( ubi_trTRUE );
+ } /* ubi_cacheReduce */
+
+unsigned long ubi_cacheSetMaxEntries( ubi_cacheRootPtr CachePtr,
+ unsigned long NewSize )
+ /* ------------------------------------------------------------------------ **
+ * Change the maximum number of entries allowed to exist in the cache.
+ *
+ * Input: CachePtr - A pointer to the cache to be modified.
+ * NewSize - The new maximum number of cache entries.
+ *
+ * Output: The maximum number of entries previously allowed to exist in
+ * the cache.
+ *
+ * Notes: If the new size is less than the old size, this function will
+ * trim the cache (remove excess entries).
+ * - A value of zero indicates an unlimited number of entries.
+ *
+ * ------------------------------------------------------------------------ **
+ */
+ {
+ unsigned long oldsize = CachePtr->max_entries; /* Save the old value. */
+
+ CachePtr->max_entries = NewSize; /* Apply the new value. */
+ if( (NewSize < oldsize) || (NewSize && !oldsize) ) /* If size is smaller, */
+ cachetrim( CachePtr ); /* remove excess. */
+ return( oldsize );
+ } /* ubi_cacheSetMaxEntries */
+
+unsigned long ubi_cacheSetMaxMemory( ubi_cacheRootPtr CachePtr,
+ unsigned long NewSize )
+ /* ------------------------------------------------------------------------ **
+ * Change the maximum amount of memory to be used for storing cache
+ * entries.
+ *
+ * Input: CachePtr - A pointer to the cache to be modified.
+ * NewSize - The new cache memory size.
+ *
+ * Output: The previous maximum memory size.
+ *
+ * Notes: If the new size is less than the old size, this function will
+ * trim the cache (remove excess entries).
+ * - A value of zero indicates that the cache has no memory limit.
+ *
+ * ------------------------------------------------------------------------ **
+ */
+ {
+ unsigned long oldsize = CachePtr->max_memory; /* Save the old value. */
+
+ CachePtr->max_memory = NewSize; /* Apply the new value. */
+ if( (NewSize < oldsize) || (NewSize && !oldsize) ) /* If size is smaller, */
+ cachetrim( CachePtr ); /* remove excess. */
+ return( oldsize );
+ } /* ubi_cacheSetMaxMemory */
+
+int ubi_cacheHitRatio( ubi_cacheRootPtr CachePtr )
+ /* ------------------------------------------------------------------------ **
+ * Returns a value that is 10,000 times the slightly weighted average hit
+ * ratio for the cache.
+ *
+ * Input: CachePtr - Pointer to the cache to be queried.
+ *
+ * Output: An integer that is 10,000 times the number of successful
+ * cache hits divided by the number of cache lookups, or:
+ * (10000 * hits) / trys
+ * You can easily convert this to a float, or do something
+ * like this (where i is the return value of this function):
+ *
+ * printf( "Hit rate : %d.%02d%%\n", (i/100), (i%100) );
+ *
+ * Notes: I say "slightly-weighted", because the numerator and
+ * denominator are both accumulated in locations of type
+ * 'unsigned short'. If the number of cache trys becomes
+ * large enough, both are divided by two. (See function
+ * ubi_cacheGet().)
+ * Dividing both numerator and denominator by two does not
+ * change the ratio (much...it is an integer divide), but it
+ * does mean that subsequent increments to either counter will
+ * have twice as much significance as previous ones.
+ *
+ * - The value returned by this function will be in the range
+ * [0..10000] because ( 0 <= cache_hits <= cache_trys ) will
+ * always be true.
+ *
+ * ------------------------------------------------------------------------ **
+ */
+ {
+ int tmp = 0;
+
+ if( CachePtr->cache_trys )
+ tmp = (int)( (10000 * (long)(CachePtr->cache_hits) )
+ / (long)(CachePtr->cache_trys) );
+ return( tmp );
+ } /* ubi_cacheHitRatio */
+
+/* -------------------------------------------------------------------------- */
diff --git a/source/ubiqx/ubi_Cache.h b/source/ubiqx/ubi_Cache.h
new file mode 100644
index 00000000000..0fc3a074f72
--- /dev/null
+++ b/source/ubiqx/ubi_Cache.h
@@ -0,0 +1,412 @@
+#ifndef UBI_CACHE_H
+#define UBI_CACHE_H
+/* ========================================================================== **
+ * ubi_Cache.h
+ *
+ * Copyright (C) 1997 by Christopher R. Hertel
+ *
+ * Email: crh@ubiqx.mn.org
+ * -------------------------------------------------------------------------- **
+ *
+ * This module implements a generic cache.
+ *
+ * -------------------------------------------------------------------------- **
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 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 Library General Public
+ * License along with this library; if not, write to the Free
+ * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * -------------------------------------------------------------------------- **
+ *
+ * This module uses a splay tree to implement a simple cache. The cache
+ * module adds a thin layer of functionality to the splay tree. In
+ * particular:
+ *
+ * - The tree (cache) may be limited in size by the number of
+ * entries permitted or the amount of memory used. When either
+ * limit is exceeded cache entries are removed until the cache
+ * conforms.
+ * - Some statistical information is kept so that an approximate
+ * "hit ratio" can be calculated.
+ * - There are several functions available that provide access to
+ * and management of cache size limits, hit ratio, and tree
+ * trimming.
+ *
+ * The splay tree is used because recently accessed items tend toward the
+ * top of the tree and less recently accessed items tend toward the bottom.
+ * This makes it easy to purge less recently used items should the cache
+ * exceed its limits.
+ *
+ * To use this module, you will need to supply a comparison function of
+ * type ubi_trCompFunc and a node-freeing function of type
+ * ubi_trKillNodeRtn. See ubi_BinTree.h for more information on
+ * these. (This is all basic ubiqx tree management stuff.)
+ *
+ * Notes:
+ *
+ * - Cache performance will start to suffer dramatically if the
+ * cache becomes large enough to force the OS to start swapping
+ * memory to disk. This is because the nodes of the underlying tree
+ * will be scattered across memory in an order that is completely
+ * unrelated to their traversal order. As more and more of the
+ * cache is placed into swap space, more and more swaps will be
+ * required for a simple traversal (...and then there's the splay
+ * operation).
+ *
+ * In one simple test under Linux, the load and dump of a cache of
+ * 400,000 entries took only 1min, 40sec of real time. The same
+ * test with 450,000 records took 2 *hours* and eight minutes.
+ *
+ * - In an effort to save memory, I considered using an unsigned
+ * short to save the per-entry entry size. I would have tucked this
+ * value into some unused space in the tree node structure. On
+ * 32-bit word aligned systems this would have saved an additional
+ * four bytes per entry. I may revisit this issue, but for now I've
+ * decided against it.
+ *
+ * Using an unsigned short would limit the size of an entry to 64K
+ * bytes. That's probably more than enough for most applications.
+ * The key word in that last sentence, however, is "probably". I
+ * really dislike imposing such limits on things.
+ *
+ * - Each entry keeps track of the amount of memory it used and the
+ * cache header keeps the total. This information is provided via
+ * the EntrySize parameter in ubi_cachePut(), so it is up to you to
+ * make sure that the numbers are accurate. (The numbers don't even
+ * have to represent bytes used.)
+ *
+ * As you consider this, note that the strdup() function--as an
+ * example--will call malloc(). The latter generally allocates a
+ * multiple of the system word size, which may be more than the
+ * number of bytes needed to store the string.
+ *
+ * -------------------------------------------------------------------------- **
+ *
+ * Log: ubi_Cache.h,v
+ * Revision 0.4 1999/09/22 03:42:24 crh
+ * Fixed a minor typo.
+ *
+ * Revision 0.3 1998/06/03 18:00:15 crh
+ * Further fiddling with sys_include.h, which is no longer explicitly
+ * included by this module since it is inherited from ubi_BinTree.h.
+ *
+ * Revision 0.2 1998/06/02 01:36:18 crh
+ * Changed include name from ubi_null.h to sys_include.h to make it
+ * more generic.
+ *
+ * Revision 0.1 1998/05/20 04:36:02 crh
+ * The C file now includes ubi_null.h. See ubi_null.h for more info.
+ *
+ * Revision 0.0 1997/12/18 06:25:23 crh
+ * Initial Revision.
+ *
+ * ========================================================================== **
+ */
+
+#include "ubi_SplayTree.h"
+
+/* -------------------------------------------------------------------------- **
+ * Typedefs...
+ *
+ * ubi_cacheRoot - Cache header structure, which consists of a binary
+ * tree root and other required housekeeping fields, as
+ * listed below.
+ * ubi_cacheRootPtr - Pointer to a Cache.
+ *
+ * ubi_cacheEntry - A cache Entry, which consists of a tree node
+ * structure and the size (in bytes) of the entry
+ * data. The entry size should be supplied via
+ * the EntrySize parameter of the ubi_cachePut()
+ * function.
+ *
+ * ubi_cacheEntryPtr - Pointer to a ubi_cacheEntry.
+ *
+ */
+
+typedef struct
+ {
+ ubi_trRoot root; /* Splay tree control structure. */
+ ubi_trKillNodeRtn free_func; /* Function used to free entries. */
+ unsigned long max_entries; /* Max cache entries. 0 == unlimited */
+ unsigned long max_memory; /* Max memory to use. 0 == unlimited */
+ unsigned long mem_used; /* Memory currently in use (bytes). */
+ unsigned short cache_hits; /* Incremented on succesful find. */
+ unsigned short cache_trys; /* Incremented on cache lookup. */
+ } ubi_cacheRoot;
+
+typedef ubi_cacheRoot *ubi_cacheRootPtr;
+
+
+typedef struct
+ {
+ ubi_trNode node; /* Tree node structure. */
+ unsigned long entry_size; /* Entry size. Used when managing
+ * caches with maximum memory limits.
+ */
+ } ubi_cacheEntry;
+
+typedef ubi_cacheEntry *ubi_cacheEntryPtr;
+
+
+/* -------------------------------------------------------------------------- **
+ * Macros...
+ *
+ * ubi_cacheGetMaxEntries() - Report the current maximum number of entries
+ * allowed in the cache. Zero indicates no
+ * maximum.
+ * ubi_cacheGetMaxMemory() - Report the current maximum amount of memory
+ * that may be used in the cache. Zero
+ * indicates no maximum.
+ * ubi_cacheGetEntryCount() - Report the current number of entries in the
+ * cache.
+ * ubi_cacheGetMemUsed() - Report the amount of memory currently in use
+ * by the cache.
+ */
+
+#define ubi_cacheGetMaxEntries( Cptr ) (((ubi_cacheRootPtr)(Cptr))->max_entries)
+#define ubi_cacheGetMaxMemory( Cptr ) (((ubi_cacheRootPtr)(Cptr))->max_memory)
+
+#define ubi_cacheGetEntryCount( Cptr ) (((ubi_cacheRootPtr)(Cptr))->root.count)
+#define ubi_cacheGetMemUsed( Cptr ) (((ubi_cacheRootPtr)(Cptr))->mem_used)
+
+/* -------------------------------------------------------------------------- **
+ * Prototypes...
+ */
+
+ubi_cacheRootPtr ubi_cacheInit( ubi_cacheRootPtr CachePtr,
+ ubi_trCompFunc CompFunc,
+ ubi_trKillNodeRtn FreeFunc,
+ unsigned long MaxEntries,
+ unsigned long MaxMemory );
+ /* ------------------------------------------------------------------------ **
+ * Initialize a cache header structure.
+ *
+ * Input: CachePtr - A pointer to a ubi_cacheRoot structure that is
+ * to be initialized.
+ * CompFunc - A pointer to the function that will be called
+ * to compare two cache values. See the module
+ * comments, above, for more information.
+ * FreeFunc - A pointer to a function that will be called
+ * to free a cache entry. If you allocated
+ * the cache entry using malloc(), then this
+ * will likely be free(). If you are allocating
+ * cache entries from a free list, then this will
+ * likely be a function that returns memory to the
+ * free list, etc.
+ * MaxEntries - The maximum number of entries that will be
+ * allowed to exist in the cache. If this limit
+ * is exceeded, then existing entries will be
+ * removed from the cache. A value of zero
+ * indicates that there is no limit on the number
+ * of cache entries. See ubi_cachePut().
+ * MaxMemory - The maximum amount of memory, in bytes, to be
+ * allocated to the cache (excluding the cache
+ * header). If this is exceeded, existing entries
+ * in the cache will be removed until enough memory
+ * has been freed to meet the condition. See
+ * ubi_cachePut().
+ *
+ * Output: A pointer to the initialized cache (i.e., the same as CachePtr).
+ *
+ * Notes: Both MaxEntries and MaxMemory may be changed after the cache
+ * has been created. See
+ * ubi_cacheSetMaxEntries()
+ * ubi_cacheSetMaxMemory()
+ * ubi_cacheGetMaxEntries()
+ * ubi_cacheGetMaxMemory() (the latter two are macros).
+ *
+ * - Memory is allocated in multiples of the word size. The
+ * return value of the strlen() function does not reflect
+ * this; it will allways be less than or equal to the amount
+ * of memory actually allocated. Keep this in mind when
+ * choosing a value for MaxMemory.
+ *
+ * ------------------------------------------------------------------------ **
+ */
+
+ubi_cacheRootPtr ubi_cacheClear( ubi_cacheRootPtr CachePtr );
+ /* ------------------------------------------------------------------------ **
+ * Remove and free all entries in an existing cache.
+ *
+ * Input: CachePtr - A pointer to the cache that is to be cleared.
+ *
+ * Output: A pointer to the cache header (i.e., the same as CachePtr).
+ * This function re-initializes the cache header.
+ *
+ * ------------------------------------------------------------------------ **
+ */
+
+void ubi_cachePut( ubi_cacheRootPtr CachePtr,
+ unsigned long EntrySize,
+ ubi_cacheEntryPtr EntryPtr,
+ ubi_trItemPtr Key );
+ /* ------------------------------------------------------------------------ **
+ * Add an entry to the cache.
+ *
+ * Input: CachePtr - A pointer to the cache into which the entry
+ * will be added.
+ * EntrySize - The size, in bytes, of the memory block indicated
+ * by EntryPtr. This will be copied into the
+ * EntryPtr->entry_size field.
+ * EntryPtr - A pointer to a memory block that begins with a
+ * ubi_cacheEntry structure. The entry structure
+ * should be followed immediately by the data to be
+ * cached (even if that is a pointer to yet more data).
+ * Key - Pointer used to identify the lookup key within the
+ * Entry.
+ *
+ * Output: None.
+ *
+ * Notes: After adding the new node, the cache is "trimmed". This
+ * removes extra nodes if the tree has exceeded it's memory or
+ * entry count limits. It is unlikely that the newly added node
+ * will be purged from the cache (assuming a reasonably large
+ * cache), since new nodes in a splay tree (which is what this
+ * module was designed to use) are moved to the top of the tree
+ * and the cache purge process removes nodes from the bottom of
+ * the tree.
+ * - The underlying splay tree is opened in OVERWRITE mode. If
+ * the input key matches an existing key, the existing entry will
+ * be politely removed from the tree and freed.
+ * - Memory is allocated in multiples of the word size. The
+ * return value of the strlen() function does not reflect
+ * this; it will allways be less than or equal to the amount
+ * of memory actually allocated.
+ *
+ * ------------------------------------------------------------------------ **
+ */
+
+ubi_cacheEntryPtr ubi_cacheGet( ubi_cacheRootPtr CachePtr,
+ ubi_trItemPtr FindMe );
+ /* ------------------------------------------------------------------------ **
+ * Attempt to retrieve an entry from the cache.
+ *
+ * Input: CachePtr - A ponter to the cache that is to be searched.
+ * FindMe - A ubi_trItemPtr that indicates the key for which
+ * to search.
+ *
+ * Output: A pointer to the cache entry that was found, or NULL if no
+ * matching entry was found.
+ *
+ * Notes: This function also updates the hit ratio counters.
+ * The counters are unsigned short. If the number of cache tries
+ * reaches 32768, then both the number of tries and the number of
+ * hits are divided by two. This prevents the counters from
+ * overflowing. See the comments in ubi_cacheHitRatio() for
+ * additional notes.
+ *
+ * ------------------------------------------------------------------------ **
+ */
+
+ubi_trBool ubi_cacheDelete( ubi_cacheRootPtr CachePtr, ubi_trItemPtr DeleteMe );
+ /* ------------------------------------------------------------------------ **
+ * Find and delete the specified cache entry.
+ *
+ * Input: CachePtr - A pointer to the cache.
+ * DeleteMe - The key of the entry to be deleted.
+ *
+ * Output: TRUE if the entry was found & freed, else FALSE.
+ *
+ * ------------------------------------------------------------------------ **
+ */
+
+ubi_trBool ubi_cacheReduce( ubi_cacheRootPtr CachePtr, unsigned long count );
+ /* ------------------------------------------------------------------------ **
+ * Remove <count> entries from the bottom of the cache.
+ *
+ * Input: CachePtr - A pointer to the cache which is to be reduced in
+ * size.
+ * count - The number of entries to remove.
+ *
+ * Output: The function will return TRUE if <count> entries were removed,
+ * else FALSE. A return value of FALSE should indicate that
+ * there were less than <count> entries in the cache, and that the
+ * cache is now empty.
+ *
+ * Notes: This function forces a reduction in the number of cache entries
+ * without requiring that the MaxMemory or MaxEntries values be
+ * changed.
+ *
+ * ------------------------------------------------------------------------ **
+ */
+
+unsigned long ubi_cacheSetMaxEntries( ubi_cacheRootPtr CachePtr,
+ unsigned long NewSize );
+ /* ------------------------------------------------------------------------ **
+ * Change the maximum number of entries allowed to exist in the cache.
+ *
+ * Input: CachePtr - A pointer to the cache to be modified.
+ * NewSize - The new maximum number of cache entries.
+ *
+ * Output: The maximum number of entries previously allowed to exist in
+ * the cache.
+ *
+ * Notes: If the new size is less than the old size, this function will
+ * trim the cache (remove excess entries).
+ * - A value of zero indicates an unlimited number of entries.
+ *
+ * ------------------------------------------------------------------------ **
+ */
+
+unsigned long ubi_cacheSetMaxMemory( ubi_cacheRootPtr CachePtr,
+ unsigned long NewSize );
+ /* ------------------------------------------------------------------------ **
+ * Change the maximum amount of memory to be used for storing cache
+ * entries.
+ *
+ * Input: CachePtr - A pointer to the cache to be modified.
+ * NewSize - The new cache memory size.
+ *
+ * Output: The previous maximum memory size.
+ *
+ * Notes: If the new size is less than the old size, this function will
+ * trim the cache (remove excess entries).
+ * - A value of zero indicates that the cache has no memory limit.
+ *
+ * ------------------------------------------------------------------------ **
+ */
+
+int ubi_cacheHitRatio( ubi_cacheRootPtr CachePtr );
+ /* ------------------------------------------------------------------------ **
+ * Returns a value that is 10,000 times the slightly weighted average hit
+ * ratio for the cache.
+ *
+ * Input: CachePtr - Pointer to the cache to be queried.
+ *
+ * Output: An integer that is 10,000 times the number of successful
+ * cache hits divided by the number of cache lookups, or:
+ * (10000 * hits) / trys
+ * You can easily convert this to a float, or do something
+ * like this (where i is the return value of this function):
+ *
+ * printf( "Hit rate : %d.%02d%%\n", (i/100), (i%100) );
+ *
+ * Notes: I say "slightly-weighted", because the numerator and
+ * denominator are both accumulated in locations of type
+ * 'unsigned short'. If the number of cache trys becomes
+ * large enough, both are divided by two. (See function
+ * ubi_cacheGet().)
+ * Dividing both numerator and denominator by two does not
+ * change the ratio (much...it is an integer divide), but it
+ * does mean that subsequent increments to either counter will
+ * have twice as much significance as previous ones.
+ *
+ * - The value returned by this function will be in the range
+ * [0..10000] because ( 0 <= cache_hits <= cache_trys ) will
+ * always be true.
+ *
+ * ------------------------------------------------------------------------ **
+ */
+
+/* -------------------------------------------------------------------------- */
+#endif /* ubi_CACHE_H */
diff --git a/source/ubiqx/ubi_SplayTree.c b/source/ubiqx/ubi_SplayTree.c
new file mode 100644
index 00000000000..222506bd06b
--- /dev/null
+++ b/source/ubiqx/ubi_SplayTree.c
@@ -0,0 +1,512 @@
+/* ========================================================================== **
+ * ubi_SplayTree.c
+ *
+ * Copyright (C) 1993-1998 by Christopher R. Hertel
+ *
+ * Email: crh@ubiqx.mn.org
+ * -------------------------------------------------------------------------- **
+ *
+ * This module implements "splay" trees. Splay trees are binary trees
+ * that are rearranged (splayed) whenever a node is accessed. The
+ * splaying process *tends* to make the tree bushier (improves balance),
+ * and the nodes that are accessed most frequently *tend* to be closer to
+ * the top.
+ *
+ * References: "Self-Adjusting Binary Search Trees", by Daniel Sleator and
+ * Robert Tarjan. Journal of the Association for Computing
+ * Machinery Vol 32, No. 3, July 1985 pp. 652-686
+ *
+ * See also: http://www.cs.cmu.edu/~sleator/
+ *
+ * -------------------------------------------------------------------------- **
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 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 Library General Public
+ * License along with this library; if not, write to the Free
+ * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * -------------------------------------------------------------------------- **
+ *
+ * Log: ubi_SplayTree.c,v
+ * Revision 4.5 2000/01/08 23:26:49 crh
+ * Added ubi_trSplay() macro, which does a type cast for us.
+ *
+ * Revision 4.4 1998/06/04 21:29:27 crh
+ * Upper-cased defined constants (eg UBI_BINTREE_H) in some header files.
+ * This is more "standard", and is what people expect. Weird, eh?
+ *
+ * Revision 4.3 1998/06/03 17:45:05 crh
+ * Further fiddling with sys_include.h. It's now in ubi_BinTree.h which is
+ * included by all of the binary tree files.
+ *
+ * Also fixed some warnings produced by lint on Irix 6.2, which doesn't seem
+ * to like syntax like this:
+ *
+ * if( (a = b) )
+ *
+ * The fix was to change lines like the above to:
+ *
+ * if( 0 != (a=b) )
+ *
+ * Which means the same thing.
+ *
+ * Reminder: Some of the ubi_tr* macros in ubi_BinTree.h are redefined in
+ * ubi_AVLtree.h and ubi_SplayTree.h. This allows easy swapping
+ * of tree types by simply changing a header. Unfortunately, the
+ * macro redefinitions in ubi_AVLtree.h and ubi_SplayTree.h will
+ * conflict if used together. You must either choose a single tree
+ * type, or use the underlying function calls directly. Compare
+ * the two header files for more information.
+ *
+ * Revision 4.2 1998/06/02 01:29:14 crh
+ * Changed ubi_null.h to sys_include.h to make it more generic.
+ *
+ * Revision 4.1 1998/05/20 04:37:54 crh
+ * The C file now includes ubi_null.h. See ubi_null.h for more info.
+ *
+ * Revision 4.0 1998/03/10 03:41:33 crh
+ * Minor comment changes. The revision number is now 4.0 to match the
+ * BinTree and AVLtree modules.
+ *
+ * Revision 2.7 1998/01/24 06:37:08 crh
+ * Added a URL for more information.
+ *
+ * Revision 2.6 1997/12/23 04:01:12 crh
+ * In this version, all constants & macros defined in the header file have
+ * the ubi_tr prefix. Also cleaned up anything that gcc complained about
+ * when run with '-pedantic -fsyntax-only -Wall'.
+ *
+ * Revision 2.5 1997/07/26 04:15:42 crh
+ * + Cleaned up a few minor syntax annoyances that gcc discovered for me.
+ * + Changed ubi_TRUE and ubi_FALSE to ubi_trTRUE and ubi_trFALSE.
+ *
+ * Revision 2.4 1997/06/03 04:42:21 crh
+ * Changed TRUE and FALSE to ubi_TRUE and ubi_FALSE to avoid causing
+ * problems.
+ *
+ * Revision 2.3 1995/10/03 22:19:07 CRH
+ * Ubisized!
+ * Also, added the function ubi_sptSplay().
+ *
+ * Revision 2.1 95/03/09 23:54:42 CRH
+ * Added the ModuleID static string and function. These modules are now
+ * self-identifying.
+ *
+ * Revision 2.0 95/02/27 22:34:46 CRH
+ * This module was updated to match the interface changes made to the
+ * ubi_BinTree module. In particular, the interface to the Locate() function
+ * has changed. See ubi_BinTree for more information on changes and new
+ * functions.
+ *
+ * The revision number was also upped to match ubi_BinTree.
+ *
+ * Revision 1.1 93/10/18 20:35:16 CRH
+ * I removed the hard-coded logical device names from the include file
+ * specifications. CRH
+ *
+ * Revision 1.0 93/10/15 23:00:15 CRH
+ * With this revision, I have added a set of #define's that provide a single,
+ * standard API to all existing tree modules. Until now, each of the three
+ * existing modules had a different function and typedef prefix, as follows:
+ *
+ * Module Prefix
+ * ubi_BinTree ubi_bt
+ * ubi_AVLtree ubi_avl
+ * ubi_SplayTree ubi_spt
+ *
+ * To further complicate matters, only those portions of the base module
+ * (ubi_BinTree) that were superceeded in the new module had the new names.
+ * For example, if you were using ubi_SplayTree, the locate function was
+ * called "ubi_sptLocate", but the next and previous functions remained
+ * "ubi_btNext" and "ubi_btPrev".
+ *
+ * This was not too terrible if you were familiar with the modules and knew
+ * exactly which tree model you wanted to use. If you wanted to be able to
+ * change modules (for speed comparisons, etc), things could get messy very
+ * quickly.
+ *
+ * So, I have added a set of defined names that get redefined in any of the
+ * descendant modules. To use this standardized interface in your code,
+ * simply replace all occurances of "ubi_bt", "ubi_avl", and "ubi_spt" with
+ * "ubi_tr". The "ubi_tr" names will resolve to the correct function or
+ * datatype names for the module that you are using. Just remember to
+ * include the header for that module in your program file. Because these
+ * names are handled by the preprocessor, there is no added run-time
+ * overhead.
+ *
+ * Note that the original names do still exist, and can be used if you wish
+ * to write code directly to a specific module. This should probably only be
+ * done if you are planning to implement a new descendant type, such as
+ * red/black trees. CRH
+ *
+ * Revision 0.1 93/04/25 22:03:32 CRH
+ * Simply changed the <exec/types.h> #include reference the .c file to
+ * use <stdlib.h> instead. The latter is portable, the former is not.
+ *
+ * Revision 0.0 93/04/21 23:05:52 CRH
+ * Initial version, written by Christopher R. Hertel.
+ * This module implements Splay Trees using the ubi_BinTree module as a basis.
+ *
+ * ========================================================================== **
+ */
+
+#include "ubi_SplayTree.h" /* Header for THIS module. */
+
+/* ========================================================================== **
+ * Static data.
+ */
+
+static char ModuleID[] = "ubi_SplayTree\n\
+\tRevision: 4.5 \n\
+\tDate: 2000/01/08 23:26:49 \n\
+\tAuthor: crh \n";
+
+
+/* ========================================================================== **
+ * Private functions...
+ */
+
+static void Rotate( ubi_btNodePtr p )
+ /* ------------------------------------------------------------------------ **
+ * This function performs a single rotation, moving node *p up one level
+ * in the tree.
+ *
+ * Input: p - a pointer to an ubi_btNode in a tree.
+ *
+ * Output: None.
+ *
+ * Notes: This implements a single rotation in either direction (left
+ * or right). This is the basic building block of all splay
+ * tree rotations.
+ * ------------------------------------------------------------------------ **
+ */
+ {
+ ubi_btNodePtr parentp;
+ ubi_btNodePtr tmp;
+ char way;
+ char revway;
+
+ parentp = p->Link[ubi_trPARENT]; /* Find parent. */
+
+ if( parentp ) /* If no parent, then we're already the root. */
+ {
+ way = p->gender;
+ revway = ubi_trRevWay(way);
+ tmp = p->Link[(int)revway];
+
+ parentp->Link[(int)way] = tmp;
+ if( tmp )
+ {
+ tmp->Link[ubi_trPARENT] = parentp;
+ tmp->gender = way;
+ }
+
+ tmp = parentp->Link[ubi_trPARENT];
+ p->Link[ubi_trPARENT] = tmp;
+ p->gender = parentp->gender;
+ if( tmp )
+ tmp->Link[(int)(p->gender)] = p;
+
+ parentp->Link[ubi_trPARENT] = p;
+ parentp->gender = revway;
+ p->Link[(int)revway] = parentp;
+ }
+ } /* Rotate */
+
+static ubi_btNodePtr Splay( ubi_btNodePtr SplayWithMe )
+ /* ------------------------------------------------------------------------ **
+ * Move the node indicated by SplayWithMe to the root of the tree by
+ * splaying the tree.
+ *
+ * Input: SplayWithMe - A pointer to an ubi_btNode within a tree.
+ *
+ * Output: A pointer to the root of the splay tree (i.e., the same as
+ * SplayWithMe).
+ * ------------------------------------------------------------------------ **
+ */
+ {
+ ubi_btNodePtr parent;
+
+ while( NULL != (parent = SplayWithMe->Link[ubi_trPARENT]) )
+ {
+ if( parent->gender == SplayWithMe->gender ) /* Zig-Zig */
+ Rotate( parent );
+ else
+ {
+ if( ubi_trEQUAL != parent->gender ) /* Zig-Zag */
+ Rotate( SplayWithMe );
+ }
+ Rotate( SplayWithMe ); /* Zig */
+ } /* while */
+ return( SplayWithMe );
+ } /* Splay */
+
+/* ========================================================================== **
+ * Exported utilities.
+ */
+
+ubi_trBool ubi_sptInsert( ubi_btRootPtr RootPtr,
+ ubi_btNodePtr NewNode,
+ ubi_btItemPtr ItemPtr,
+ ubi_btNodePtr *OldNode )
+ /* ------------------------------------------------------------------------ **
+ * This function uses a non-recursive algorithm to add a new element to the
+ * splay tree.
+ *
+ * Input: RootPtr - a pointer to the ubi_btRoot structure that indicates
+ * the root of the tree to which NewNode is to be added.
+ * NewNode - a pointer to an ubi_btNode structure that is NOT
+ * part of any tree.
+ * ItemPtr - A pointer to the sort key that is stored within
+ * *NewNode. ItemPtr MUST point to information stored
+ * in *NewNode or an EXACT DUPLICATE. The key data
+ * indicated by ItemPtr is used to place the new node
+ * into the tree.
+ * OldNode - a pointer to an ubi_btNodePtr. When searching
+ * the tree, a duplicate node may be found. If
+ * duplicates are allowed, then the new node will
+ * be simply placed into the tree. If duplicates
+ * are not allowed, however, then one of two things
+ * may happen.
+ * 1) if overwritting *is not* allowed, this
+ * function will return FALSE (indicating that
+ * the new node could not be inserted), and
+ * *OldNode will point to the duplicate that is
+ * still in the tree.
+ * 2) if overwritting *is* allowed, then this
+ * function will swap **OldNode for *NewNode.
+ * In this case, *OldNode will point to the node
+ * that was removed (thus allowing you to free
+ * the node).
+ * ** If you are using overwrite mode, ALWAYS **
+ * ** check the return value of this parameter! **
+ * Note: You may pass NULL in this parameter, the
+ * function knows how to cope. If you do this,
+ * however, there will be no way to return a
+ * pointer to an old (ie. replaced) node (which is
+ * a problem if you are using overwrite mode).
+ *
+ * Output: a boolean value indicating success or failure. The function
+ * will return FALSE if the node could not be added to the tree.
+ * Such failure will only occur if duplicates are not allowed,
+ * nodes cannot be overwritten, AND a duplicate key was found
+ * within the tree.
+ * ------------------------------------------------------------------------ **
+ */
+ {
+ ubi_btNodePtr OtherP;
+
+ if( !(OldNode) )
+ OldNode = &OtherP;
+
+ if( ubi_btInsert( RootPtr, NewNode, ItemPtr, OldNode ) )
+ {
+ RootPtr->root = Splay( NewNode );
+ return( ubi_trTRUE );
+ }
+
+ /* Splay the unreplacable, duplicate keyed, unique, old node. */
+ RootPtr->root = Splay( (*OldNode) );
+ return( ubi_trFALSE );
+ } /* ubi_sptInsert */
+
+ubi_btNodePtr ubi_sptRemove( ubi_btRootPtr RootPtr, ubi_btNodePtr DeadNode )
+ /* ------------------------------------------------------------------------ **
+ * This function removes the indicated node from the tree.
+ *
+ * Input: RootPtr - A pointer to the header of the tree that contains
+ * the node to be removed.
+ * DeadNode - A pointer to the node that will be removed.
+ *
+ * Output: This function returns a pointer to the node that was removed
+ * from the tree (ie. the same as DeadNode).
+ *
+ * Note: The node MUST be in the tree indicated by RootPtr. If not,
+ * strange and evil things will happen to your trees.
+ * ------------------------------------------------------------------------ **
+ */
+ {
+ ubi_btNodePtr p;
+
+ (void)Splay( DeadNode ); /* Move dead node to root. */
+ if( NULL != (p = DeadNode->Link[ubi_trLEFT]) )
+ { /* If left subtree exists... */
+ ubi_btNodePtr q = DeadNode->Link[ubi_trRIGHT];
+
+ p->Link[ubi_trPARENT] = NULL; /* Left subtree node becomes root.*/
+ p->gender = ubi_trPARENT;
+ p = ubi_btLast( p ); /* Find rightmost left node... */
+ p->Link[ubi_trRIGHT] = q; /* ...attach right tree. */
+ if( q )
+ q->Link[ubi_trPARENT] = p;
+ RootPtr->root = Splay( p ); /* Resplay at p. */
+ }
+ else
+ {
+ if( NULL != (p = DeadNode->Link[ubi_trRIGHT]) )
+ { /* No left, but right subtree exists... */
+ p->Link[ubi_trPARENT] = NULL; /* Right subtree root becomes... */
+ p->gender = ubi_trPARENT; /* ...overall tree root. */
+ RootPtr->root = p;
+ }
+ else
+ RootPtr->root = NULL; /* No subtrees => empty tree. */
+ }
+
+ (RootPtr->count)--; /* Decrement node count. */
+ return( DeadNode ); /* Return pointer to pruned node. */
+ } /* ubi_sptRemove */
+
+ubi_btNodePtr ubi_sptLocate( ubi_btRootPtr RootPtr,
+ ubi_btItemPtr FindMe,
+ ubi_trCompOps CompOp )
+ /* ------------------------------------------------------------------------ **
+ * The purpose of ubi_btLocate() is to find a node or set of nodes given
+ * a target value and a "comparison operator". The Locate() function is
+ * more flexible and (in the case of trees that may contain dupicate keys)
+ * more precise than the ubi_btFind() function. The latter is faster,
+ * but it only searches for exact matches and, if the tree contains
+ * duplicates, Find() may return a pointer to any one of the duplicate-
+ * keyed records.
+ *
+ * Input:
+ * RootPtr - A pointer to the header of the tree to be searched.
+ * FindMe - An ubi_btItemPtr that indicates the key for which to
+ * search.
+ * CompOp - One of the following:
+ * CompOp Return a pointer to the node with
+ * ------ ---------------------------------
+ * ubi_trLT - the last key value that is less
+ * than FindMe.
+ * ubi_trLE - the first key matching FindMe, or
+ * the last key that is less than
+ * FindMe.
+ * ubi_trEQ - the first key matching FindMe.
+ * ubi_trGE - the first key matching FindMe, or the
+ * first key greater than FindMe.
+ * ubi_trGT - the first key greater than FindMe.
+ * Output:
+ * A pointer to the node matching the criteria listed above under
+ * CompOp, or NULL if no node matched the criteria.
+ *
+ * Notes:
+ * In the case of trees with duplicate keys, Locate() will behave as
+ * follows:
+ *
+ * Find: 3 Find: 3
+ * Keys: 1 2 2 2 3 3 3 3 3 4 4 Keys: 1 1 2 2 2 4 4 5 5 5 6
+ * ^ ^ ^ ^ ^
+ * LT EQ GT LE GE
+ *
+ * That is, when returning a pointer to a node with a key that is LESS
+ * THAN the target key (FindMe), Locate() will return a pointer to the
+ * LAST matching node.
+ * When returning a pointer to a node with a key that is GREATER
+ * THAN the target key (FindMe), Locate() will return a pointer to the
+ * FIRST matching node.
+ *
+ * See Also: ubi_btFind(), ubi_btFirstOf(), ubi_btLastOf().
+ * ------------------------------------------------------------------------ **
+ */
+ {
+ ubi_btNodePtr p;
+
+ p = ubi_btLocate( RootPtr, FindMe, CompOp );
+ if( p )
+ RootPtr->root = Splay( p );
+ return( p );
+ } /* ubi_sptLocate */
+
+ubi_btNodePtr ubi_sptFind( ubi_btRootPtr RootPtr,
+ ubi_btItemPtr FindMe )
+ /* ------------------------------------------------------------------------ **
+ * This function performs a non-recursive search of a tree for any node
+ * matching a specific key.
+ *
+ * Input:
+ * RootPtr - a pointer to the header of the tree to be searched.
+ * FindMe - a pointer to the key value for which to search.
+ *
+ * Output:
+ * A pointer to a node with a key that matches the key indicated by
+ * FindMe, or NULL if no such node was found.
+ *
+ * Note: In a tree that allows duplicates, the pointer returned *might
+ * not* point to the (sequentially) first occurance of the
+ * desired key. In such a tree, it may be more useful to use
+ * ubi_sptLocate().
+ * ------------------------------------------------------------------------ **
+ */
+ {
+ ubi_btNodePtr p;
+
+ p = ubi_btFind( RootPtr, FindMe );
+ if( p )
+ RootPtr->root = Splay( p );
+ return( p );
+ } /* ubi_sptFind */
+
+void ubi_sptSplay( ubi_btRootPtr RootPtr,
+ ubi_btNodePtr SplayMe )
+ /* ------------------------------------------------------------------------ **
+ * This function allows you to splay the tree at a given node, thus moving
+ * the node to the top of the tree.
+ *
+ * Input:
+ * RootPtr - a pointer to the header of the tree to be splayed.
+ * SplayMe - a pointer to a node within the tree. This will become
+ * the new root node.
+ * Output: None.
+ *
+ * Notes: This is an uncharacteristic function for this group of modules
+ * in that it provides access to the internal balancing routines,
+ * which would normally be hidden.
+ * Splaying the tree will not damage it (assuming that I've done
+ * *my* job), but there is overhead involved. I don't recommend
+ * that you use this function unless you understand the underlying
+ * Splay Tree principles involved.
+ * ------------------------------------------------------------------------ **
+ */
+ {
+ RootPtr->root = Splay( SplayMe );
+ } /* ubi_sptSplay */
+
+int ubi_sptModuleID( int size, char *list[] )
+ /* ------------------------------------------------------------------------ **
+ * Returns a set of strings that identify the module.
+ *
+ * Input: size - The number of elements in the array <list>.
+ * list - An array of pointers of type (char *). This array
+ * should, initially, be empty. This function will fill
+ * in the array with pointers to strings.
+ * Output: The number of elements of <list> that were used. If this value
+ * is less than <size>, the values of the remaining elements are
+ * not guaranteed.
+ *
+ * Notes: Please keep in mind that the pointers returned indicate strings
+ * stored in static memory. Don't free() them, don't write over
+ * them, etc. Just read them.
+ * ------------------------------------------------------------------------ **
+ */
+ {
+ if( size > 0 )
+ {
+ list[0] = ModuleID;
+ if( size > 1 )
+ return( 1 + ubi_btModuleID( --size, &(list[1]) ) );
+ return( 1 );
+ }
+ return( 0 );
+ } /* ubi_sptModuleID */
+
+/* ================================ The End ================================= */
+
diff --git a/source/ubiqx/ubi_SplayTree.h b/source/ubiqx/ubi_SplayTree.h
new file mode 100644
index 00000000000..e4fac796a91
--- /dev/null
+++ b/source/ubiqx/ubi_SplayTree.h
@@ -0,0 +1,377 @@
+#ifndef UBI_SPLAYTREE_H
+#define UBI_SPLAYTREE_H
+/* ========================================================================== **
+ * ubi_SplayTree.h
+ *
+ * Copyright (C) 1993-1998 by Christopher R. Hertel
+ *
+ * Email: crh@ubiqx.mn.org
+ * -------------------------------------------------------------------------- **
+ *
+ * This module implements "splay" trees. Splay trees are binary trees
+ * that are rearranged (splayed) whenever a node is accessed. The
+ * splaying process *tends* to make the tree bushier (improves balance),
+ * and the nodes that are accessed most frequently *tend* to be closer to
+ * the top.
+ *
+ * References: "Self-Adjusting Binary Search Trees", by Daniel Sleator and
+ * Robert Tarjan. Journal of the Association for Computing
+ * Machinery Vol 32, No. 3, July 1985 pp. 652-686
+ *
+ * See also: http://www.cs.cmu.edu/~sleator/
+ *
+ * -------------------------------------------------------------------------- **
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 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 Library General Public
+ * License along with this library; if not, write to the Free
+ * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * -------------------------------------------------------------------------- **
+ *
+ * Log: ubi_SplayTree.h,v
+ * Revision 4.5 2000/01/08 23:26:49 crh
+ * Added ubi_trSplay() macro, which does a type cast for us.
+ *
+ * Revision 4.4 1998/06/04 21:29:27 crh
+ * Upper-cased defined constants (eg UBI_BINTREE_H) in some header files.
+ * This is more "standard", and is what people expect. Weird, eh?
+ *
+ * Revision 4.3 1998/06/03 17:45:05 crh
+ * Further fiddling with sys_include.h. It's now in ubi_BinTree.h which is
+ * included by all of the binary tree files.
+ *
+ * Also fixed some warnings produced by lint on Irix 6.2, which doesn't seem
+ * to like syntax like this:
+ *
+ * if( (a = b) )
+ *
+ * The fix was to change lines like the above to:
+ *
+ * if( 0 != (a=b) )
+ *
+ * Which means the same thing.
+ *
+ * Reminder: Some of the ubi_tr* macros in ubi_BinTree.h are redefined in
+ * ubi_AVLtree.h and ubi_SplayTree.h. This allows easy swapping
+ * of tree types by simply changing a header. Unfortunately, the
+ * macro redefinitions in ubi_AVLtree.h and ubi_SplayTree.h will
+ * conflict if used together. You must either choose a single tree
+ * type, or use the underlying function calls directly. Compare
+ * the two header files for more information.
+ *
+ * Revision 4.2 1998/06/02 01:29:14 crh
+ * Changed ubi_null.h to sys_include.h to make it more generic.
+ *
+ * Revision 4.1 1998/05/20 04:37:54 crh
+ * The C file now includes ubi_null.h. See ubi_null.h for more info.
+ *
+ * Revision 4.0 1998/03/10 03:40:57 crh
+ * Minor comment changes. The revision number is now 4.0 to match the
+ * BinTree and AVLtree modules.
+ *
+ * Revision 2.7 1998/01/24 06:37:57 crh
+ * Added a URL for more information.
+ *
+ * Revision 2.6 1997/12/23 04:02:20 crh
+ * In this version, all constants & macros defined in the header file have
+ * the ubi_tr prefix. Also cleaned up anything that gcc complained about
+ * when run with '-pedantic -fsyntax-only -Wall'.
+ *
+ * Revision 2.5 1997/07/26 04:15:46 crh
+ * + Cleaned up a few minor syntax annoyances that gcc discovered for me.
+ * + Changed ubi_TRUE and ubi_FALSE to ubi_trTRUE and ubi_trFALSE.
+ *
+ * Revision 2.4 1997/06/03 05:22:56 crh
+ * Changed TRUE and FALSE to ubi_TRUE and ubi_FALSE to avoid causing
+ * problems.
+ *
+ * Revision 2.3 1995/10/03 22:19:37 CRH
+ * Ubisized!
+ * Also, added the function ubi_sptSplay().
+ *
+ * Revision 2.1 95/03/09 23:55:04 CRH
+ * Added the ModuleID static string and function. These modules are now
+ * self-identifying.
+ *
+ * Revision 2.0 95/02/27 22:34:55 CRH
+ * This module was updated to match the interface changes made to the
+ * ubi_BinTree module. In particular, the interface to the Locate() function
+ * has changed. See ubi_BinTree for more information on changes and new
+ * functions.
+ *
+ * The revision number was also upped to match ubi_BinTree.
+ *
+ *
+ * Revision 1.0 93/10/15 22:59:36 CRH
+ * With this revision, I have added a set of #define's that provide a single,
+ * standard API to all existing tree modules. Until now, each of the three
+ * existing modules had a different function and typedef prefix, as follows:
+ *
+ * Module Prefix
+ * ubi_BinTree ubi_bt
+ * ubi_AVLtree ubi_avl
+ * ubi_SplayTree ubi_spt
+ *
+ * To further complicate matters, only those portions of the base module
+ * (ubi_BinTree) that were superceeded in the new module had the new names.
+ * For example, if you were using ubi_SplayTree, the locate function was
+ * called "ubi_sptLocate", but the next and previous functions remained
+ * "ubi_btNext" and "ubi_btPrev".
+ *
+ * This was not too terrible if you were familiar with the modules and knew
+ * exactly which tree model you wanted to use. If you wanted to be able to
+ * change modules (for speed comparisons, etc), things could get messy very
+ * quickly.
+ *
+ * So, I have added a set of defined names that get redefined in any of the
+ * descendant modules. To use this standardized interface in your code,
+ * simply replace all occurances of "ubi_bt", "ubi_avl", and "ubi_spt" with
+ * "ubi_tr". The "ubi_tr" names will resolve to the correct function or
+ * datatype names for the module that you are using. Just remember to
+ * include the header for that module in your program file. Because these
+ * names are handled by the preprocessor, there is no added run-time
+ * overhead.
+ *
+ * Note that the original names do still exist, and can be used if you wish
+ * to write code directly to a specific module. This should probably only be
+ * done if you are planning to implement a new descendant type, such as
+ * red/black trees. CRH
+ *
+ * Revision 0.0 93/04/21 23:07:13 CRH
+ * Initial version, written by Christopher R. Hertel.
+ * This module implements Splay Trees using the ubi_BinTree module as a basis.
+ *
+ * ========================================================================== **
+ */
+
+#include "ubi_BinTree.h" /* Base binary tree functions, types, etc. */
+
+/* ========================================================================== **
+ * Function prototypes...
+ */
+
+ubi_trBool ubi_sptInsert( ubi_btRootPtr RootPtr,
+ ubi_btNodePtr NewNode,
+ ubi_btItemPtr ItemPtr,
+ ubi_btNodePtr *OldNode );
+ /* ------------------------------------------------------------------------ **
+ * This function uses a non-recursive algorithm to add a new element to the
+ * splay tree.
+ *
+ * Input: RootPtr - a pointer to the ubi_btRoot structure that indicates
+ * the root of the tree to which NewNode is to be added.
+ * NewNode - a pointer to an ubi_btNode structure that is NOT
+ * part of any tree.
+ * ItemPtr - A pointer to the sort key that is stored within
+ * *NewNode. ItemPtr MUST point to information stored
+ * in *NewNode or an EXACT DUPLICATE. The key data
+ * indicated by ItemPtr is used to place the new node
+ * into the tree.
+ * OldNode - a pointer to an ubi_btNodePtr. When searching
+ * the tree, a duplicate node may be found. If
+ * duplicates are allowed, then the new node will
+ * be simply placed into the tree. If duplicates
+ * are not allowed, however, then one of two things
+ * may happen.
+ * 1) if overwritting *is not* allowed, this
+ * function will return FALSE (indicating that
+ * the new node could not be inserted), and
+ * *OldNode will point to the duplicate that is
+ * still in the tree.
+ * 2) if overwritting *is* allowed, then this
+ * function will swap **OldNode for *NewNode.
+ * In this case, *OldNode will point to the node
+ * that was removed (thus allowing you to free
+ * the node).
+ * ** If you are using overwrite mode, ALWAYS **
+ * ** check the return value of this parameter! **
+ * Note: You may pass NULL in this parameter, the
+ * function knows how to cope. If you do this,
+ * however, there will be no way to return a
+ * pointer to an old (ie. replaced) node (which is
+ * a problem if you are using overwrite mode).
+ *
+ * Output: a boolean value indicating success or failure. The function
+ * will return FALSE if the node could not be added to the tree.
+ * Such failure will only occur if duplicates are not allowed,
+ * nodes cannot be overwritten, AND a duplicate key was found
+ * within the tree.
+ * ------------------------------------------------------------------------ **
+ */
+
+ubi_btNodePtr ubi_sptRemove( ubi_btRootPtr RootPtr, ubi_btNodePtr DeadNode );
+ /* ------------------------------------------------------------------------ **
+ * This function removes the indicated node from the tree.
+ *
+ * Input: RootPtr - A pointer to the header of the tree that contains
+ * the node to be removed.
+ * DeadNode - A pointer to the node that will be removed.
+ *
+ * Output: This function returns a pointer to the node that was removed
+ * from the tree (ie. the same as DeadNode).
+ *
+ * Note: The node MUST be in the tree indicated by RootPtr. If not,
+ * strange and evil things will happen to your trees.
+ * ------------------------------------------------------------------------ **
+ */
+
+ubi_btNodePtr ubi_sptLocate( ubi_btRootPtr RootPtr,
+ ubi_btItemPtr FindMe,
+ ubi_trCompOps CompOp );
+ /* ------------------------------------------------------------------------ **
+ * The purpose of ubi_btLocate() is to find a node or set of nodes given
+ * a target value and a "comparison operator". The Locate() function is
+ * more flexible and (in the case of trees that may contain dupicate keys)
+ * more precise than the ubi_btFind() function. The latter is faster,
+ * but it only searches for exact matches and, if the tree contains
+ * duplicates, Find() may return a pointer to any one of the duplicate-
+ * keyed records.
+ *
+ * Input:
+ * RootPtr - A pointer to the header of the tree to be searched.
+ * FindMe - An ubi_btItemPtr that indicates the key for which to
+ * search.
+ * CompOp - One of the following:
+ * CompOp Return a pointer to the node with
+ * ------ ---------------------------------
+ * ubi_trLT - the last key value that is less
+ * than FindMe.
+ * ubi_trLE - the first key matching FindMe, or
+ * the last key that is less than
+ * FindMe.
+ * ubi_trEQ - the first key matching FindMe.
+ * ubi_trGE - the first key matching FindMe, or the
+ * first key greater than FindMe.
+ * ubi_trGT - the first key greater than FindMe.
+ * Output:
+ * A pointer to the node matching the criteria listed above under
+ * CompOp, or NULL if no node matched the criteria.
+ *
+ * Notes:
+ * In the case of trees with duplicate keys, Locate() will behave as
+ * follows:
+ *
+ * Find: 3 Find: 3
+ * Keys: 1 2 2 2 3 3 3 3 3 4 4 Keys: 1 1 2 2 2 4 4 5 5 5 6
+ * ^ ^ ^ ^ ^
+ * LT EQ GT LE GE
+ *
+ * That is, when returning a pointer to a node with a key that is LESS
+ * THAN the target key (FindMe), Locate() will return a pointer to the
+ * LAST matching node.
+ * When returning a pointer to a node with a key that is GREATER
+ * THAN the target key (FindMe), Locate() will return a pointer to the
+ * FIRST matching node.
+ *
+ * See Also: ubi_btFind(), ubi_btFirstOf(), ubi_btLastOf().
+ * ------------------------------------------------------------------------ **
+ */
+
+ubi_btNodePtr ubi_sptFind( ubi_btRootPtr RootPtr,
+ ubi_btItemPtr FindMe );
+ /* ------------------------------------------------------------------------ **
+ * This function performs a non-recursive search of a tree for any node
+ * matching a specific key.
+ *
+ * Input:
+ * RootPtr - a pointer to the header of the tree to be searched.
+ * FindMe - a pointer to the key value for which to search.
+ *
+ * Output:
+ * A pointer to a node with a key that matches the key indicated by
+ * FindMe, or NULL if no such node was found.
+ *
+ * Note: In a tree that allows duplicates, the pointer returned *might
+ * not* point to the (sequentially) first occurance of the
+ * desired key. In such a tree, it may be more useful to use
+ * ubi_sptLocate().
+ * ------------------------------------------------------------------------ **
+ */
+
+void ubi_sptSplay( ubi_btRootPtr RootPtr,
+ ubi_btNodePtr SplayMe );
+ /* ------------------------------------------------------------------------ **
+ * This function allows you to splay the tree at a given node, thus moving
+ * the node to the top of the tree.
+ *
+ * Input:
+ * RootPtr - a pointer to the header of the tree to be splayed.
+ * SplayMe - a pointer to a node within the tree. This will become
+ * the new root node.
+ * Output: None.
+ *
+ * Notes: This is an uncharacteristic function for this group of modules
+ * in that it provides access to the internal balancing routines,
+ * which would normally be hidden.
+ * Splaying the tree will not damage it (assuming that I've done
+ * *my* job), but there is overhead involved. I don't recommend
+ * that you use this function unless you understand the underlying
+ * Splay Tree principles involved.
+ * ------------------------------------------------------------------------ **
+ */
+
+int ubi_sptModuleID( int size, char *list[] );
+ /* ------------------------------------------------------------------------ **
+ * Returns a set of strings that identify the module.
+ *
+ * Input: size - The number of elements in the array <list>.
+ * list - An array of pointers of type (char *). This array
+ * should, initially, be empty. This function will fill
+ * in the array with pointers to strings.
+ * Output: The number of elements of <list> that were used. If this value
+ * is less than <size>, the values of the remaining elements are
+ * not guaranteed.
+ *
+ * Notes: Please keep in mind that the pointers returned indicate strings
+ * stored in static memory. Don't free() them, don't write over
+ * them, etc. Just read them.
+ * ------------------------------------------------------------------------ **
+ */
+
+/* -------------------------------------------------------------------------- **
+ * Masquarade...
+ *
+ * This set of defines allows you to write programs that will use any of the
+ * implemented binary tree modules (currently BinTree, AVLtree, and SplayTree).
+ * Instead of using ubi_bt..., use ubi_tr..., and select the tree type by
+ * including the appropriate module header.
+ */
+
+#undef ubi_trInsert
+#undef ubi_trRemove
+#undef ubi_trLocate
+#undef ubi_trFind
+#undef ubi_trModuleID
+
+#define ubi_trInsert( Rp, Nn, Ip, On ) \
+ ubi_sptInsert( (ubi_btRootPtr)(Rp), (ubi_btNodePtr)(Nn), \
+ (ubi_btItemPtr)(Ip), (ubi_btNodePtr *)(On) )
+
+#define ubi_trRemove( Rp, Dn ) \
+ ubi_sptRemove( (ubi_btRootPtr)(Rp), (ubi_btNodePtr)(Dn) )
+
+#define ubi_trLocate( Rp, Ip, Op ) \
+ ubi_sptLocate( (ubi_btRootPtr)(Rp), \
+ (ubi_btItemPtr)(Ip), \
+ (ubi_trCompOps)(Op) )
+
+#define ubi_trFind( Rp, Ip ) \
+ ubi_sptFind( (ubi_btRootPtr)(Rp), (ubi_btItemPtr)(Ip) )
+
+#define ubi_trSplay( Rp, Sm ) \
+ ubi_sptSplay( (ubi_btRootPtr)(Rp), (ubi_btNodePtr)(Sm) )
+
+#define ubi_trModuleID( s, l ) ubi_sptModuleID( s, l )
+
+/* ================================ The End ================================= */
+#endif /* UBI_SPLAYTREE_H */
diff --git a/source/ubiqx/ubi_dLinkList.c b/source/ubiqx/ubi_dLinkList.c
new file mode 100644
index 00000000000..eb95033c695
--- /dev/null
+++ b/source/ubiqx/ubi_dLinkList.c
@@ -0,0 +1,171 @@
+/* ========================================================================== **
+ * ubi_dLinkList.c
+ *
+ * Copyright (C) 1997, 1998 by Christopher R. Hertel
+ *
+ * Email: crh@ubiqx.mn.org
+ * -------------------------------------------------------------------------- **
+ * This module implements simple doubly-linked lists.
+ * -------------------------------------------------------------------------- **
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 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 Library General Public
+ * License along with this library; if not, write to the Free
+ * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * -------------------------------------------------------------------------- **
+ *
+ * Log: ubi_dLinkList.c,v
+ * Revision 0.11 1999/06/19 16:58:06 crh
+ * Renamed the ubi_slRemove() function in ubi_sLinkList to
+ * ubi_slRemoveNext(). I was bothered by the fact that it didn't
+ * match the functionality of the ubi_dlRemove() function in
+ * ubi_dLinkList. The new name is more 'correct'.
+ *
+ * Revision 0.10 1998/07/24 07:30:20 crh
+ * Added the ubi_dlNewList() macro.
+ *
+ * Revision 0.9 1998/06/04 21:29:27 crh
+ * Upper-cased defined constants (eg UBI_BINTREE_H) in some header files.
+ * This is more "standard", and is what people expect. Weird, eh?
+ *
+ * Revision 0.8 1998/06/03 18:06:03 crh
+ * Further fiddling with sys_include.h, which has been moved from the .c file
+ * to the .h file.
+ *
+ * Revision 0.7 1998/06/02 01:38:47 crh
+ * Changed include file name from ubi_null.h to sys_include.h to make it
+ * more generic.
+ *
+ * Revision 0.6 1998/05/20 04:38:05 crh
+ * The C file now includes ubi_null.h. See ubi_null.h for more info.
+ *
+ * Revision 0.5 1998/03/10 02:55:00 crh
+ * Simplified the code and added macros for stack & queue manipulations.
+ *
+ * Revision 0.4 1998/01/03 01:53:56 crh
+ * Added ubi_dlCount() macro.
+ *
+ * Revision 0.3 1997/10/15 03:05:39 crh
+ * Added some handy type casting to the macros. Added AddHere and RemThis
+ * macros.
+ *
+ * Revision 0.2 1997/10/08 03:07:21 crh
+ * Fixed a few forgotten link-ups in Insert(), and fixed the AddHead()
+ * macro, which was passing the wrong value for <After> to Insert().
+ *
+ * Revision 0.1 1997/10/07 04:34:07 crh
+ * Initial Revision.
+ *
+ * -------------------------------------------------------------------------- **
+ * This module is similar to the ubi_sLinkList module, but it is neither a
+ * descendant type nor an easy drop-in replacement for the latter. One key
+ * difference is that the ubi_dlRemove() function removes the indicated node,
+ * while the ubi_slRemoveNext() function (in ubi_sLinkList) removes the node
+ * *following* the indicated node.
+ *
+ * ========================================================================== **
+ */
+
+#include "ubi_dLinkList.h" /* Header for *this* module. */
+
+/* ========================================================================== **
+ * Functions...
+ */
+
+ubi_dlListPtr ubi_dlInitList( ubi_dlListPtr ListPtr )
+ /* ------------------------------------------------------------------------ **
+ * Initialize a doubly-linked list header.
+ *
+ * Input: ListPtr - A pointer to the list structure that is to be
+ * initialized for use.
+ *
+ * Output: A pointer to the initialized list header (i.e., same as
+ * <ListPtr>).
+ *
+ * ------------------------------------------------------------------------ **
+ */
+ {
+ ListPtr->Head = NULL;
+ ListPtr->Tail = NULL;
+ ListPtr->count = 0;
+ return( ListPtr );
+ } /* ubi_dlInitList */
+
+ubi_dlNodePtr ubi_dlInsert( ubi_dlListPtr ListPtr,
+ ubi_dlNodePtr New,
+ ubi_dlNodePtr After )
+ /* ------------------------------------------------------------------------ **
+ * Insert a new node into the list.
+ *
+ * Input: ListPtr - A pointer to the list into which the node is to
+ * be inserted.
+ * New - Pointer to the new node.
+ * After - NULL, or a pointer to a node that is already in the
+ * list.
+ * If NULL, then <New> will be added at the head of the
+ * list, else it will be added following <After>.
+ *
+ * Output: A pointer to the node that was inserted into the list (i.e.,
+ * the same as <New>).
+ *
+ * ------------------------------------------------------------------------ **
+ */
+ {
+ ubi_dlNodePtr PredNode = After ? After : (ubi_dlNodePtr)ListPtr;
+
+ New->Next = PredNode->Next;
+ New->Prev = After;
+ PredNode->Next = New;
+ if( New->Next )
+ New->Next->Prev = New;
+ else
+ ListPtr->Tail = New;
+
+ (ListPtr->count)++;
+
+ return( New );
+ } /* ubi_dlInsert */
+
+ubi_dlNodePtr ubi_dlRemove( ubi_dlListPtr ListPtr, ubi_dlNodePtr Old )
+ /* ------------------------------------------------------------------------ **
+ * Remove a node from the list.
+ *
+ * Input: ListPtr - A pointer to the list from which <Old> is to be
+ * removed.
+ * Old - A pointer to the node that is to be removed from the
+ * list.
+ *
+ * Output: A pointer to the node that was removed (i.e., <Old>).
+ *
+ * ------------------------------------------------------------------------ **
+ */
+ {
+ if( Old )
+ {
+ if( Old->Next )
+ Old->Next->Prev = Old->Prev;
+ else
+ ListPtr->Tail = Old->Prev;
+
+ if( Old->Prev )
+ Old->Prev->Next = Old->Next;
+ else
+ ListPtr->Head = Old->Next;
+
+ (ListPtr->count)--;
+ }
+
+ return( Old );
+ } /* ubi_dlRemove */
+
+/* ================================ The End ================================= */
diff --git a/source/ubiqx/ubi_dLinkList.h b/source/ubiqx/ubi_dLinkList.h
new file mode 100644
index 00000000000..682e566ee67
--- /dev/null
+++ b/source/ubiqx/ubi_dLinkList.h
@@ -0,0 +1,242 @@
+#ifndef UBI_DLINKLIST_H
+#define UBI_DLINKLIST_H
+/* ========================================================================== **
+ * ubi_dLinkList.h
+ *
+ * Copyright (C) 1997, 1998 by Christopher R. Hertel
+ *
+ * Email: crh@ubiqx.mn.org
+ * -------------------------------------------------------------------------- **
+ * This module implements simple doubly-linked lists.
+ * -------------------------------------------------------------------------- **
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 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 Library General Public
+ * License along with this library; if not, write to the Free
+ * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * -------------------------------------------------------------------------- **
+ *
+ * Log: ubi_dLinkList.h,v
+ * Revision 0.11 1999/06/19 16:58:06 crh
+ * Renamed the ubi_slRemove() function in ubi_sLinkList to
+ * ubi_slRemoveNext(). I was bothered by the fact that it didn't
+ * match the functionality of the ubi_dlRemove() function in
+ * ubi_dLinkList. The new name is more 'correct'.
+ *
+ * Revision 0.10 1998/07/24 07:30:20 crh
+ * Added the ubi_dlNewList() macro.
+ *
+ * Revision 0.9 1998/06/04 21:29:27 crh
+ * Upper-cased defined constants (eg UBI_BINTREE_H) in some header files.
+ * This is more "standard", and is what people expect. Weird, eh?
+ *
+ * Revision 0.8 1998/06/03 18:06:03 crh
+ * Further fiddling with sys_include.h, which has been moved from the .c file
+ * to the .h file.
+ *
+ * Revision 0.7 1998/06/02 01:38:47 crh
+ * Changed include file name from ubi_null.h to sys_include.h to make it
+ * more generic.
+ *
+ * Revision 0.6 1998/05/20 04:38:05 crh
+ * The C file now includes ubi_null.h. See ubi_null.h for more info.
+ *
+ * Revision 0.5 1998/03/10 02:54:04 crh
+ * Simplified the code and added macros for stack & queue manipulations.
+ *
+ * Revision 0.4 1998/01/03 01:53:44 crh
+ * Added ubi_dlCount() macro.
+ *
+ * Revision 0.3 1997/10/15 03:04:31 crh
+ * Added some handy type casting to the macros. Added AddHere and RemThis
+ * macros.
+ *
+ * Revision 0.2 1997/10/08 03:08:16 crh
+ * Fixed a few forgotten link-ups in Insert(), and fixed the AddHead()
+ * macro, which was passing the wrong value for <After> to Insert().
+ *
+ * Revision 0.1 1997/10/07 04:34:38 crh
+ * Initial Revision.
+ *
+ * -------------------------------------------------------------------------- **
+ * This module is similar to the ubi_sLinkList module, but it is neither a
+ * descendant type nor an easy drop-in replacement for the latter. One key
+ * difference is that the ubi_dlRemove() function removes the indicated node,
+ * while the ubi_slRemoveNext() function (in ubi_sLinkList) removes the node
+ * *following* the indicated node.
+ *
+ * ========================================================================== **
+ */
+
+#include "sys_include.h" /* System-specific includes. */
+
+/* ========================================================================== **
+ * Typedefs...
+ *
+ * ubi_dlNode - This is the basic node structure.
+ * ubi_dlNodePtr - Pointer to a node.
+ * ubi_dlList - This is the list header structure.
+ * ubi_dlListPtr - Pointer to a List (i.e., a list header structure).
+ *
+ */
+
+typedef struct ubi_dlListNode
+ {
+ struct ubi_dlListNode *Next;
+ struct ubi_dlListNode *Prev;
+ } ubi_dlNode;
+
+typedef ubi_dlNode *ubi_dlNodePtr;
+
+typedef struct
+ {
+ ubi_dlNodePtr Head;
+ ubi_dlNodePtr Tail;
+ unsigned long count;
+ } ubi_dlList;
+
+typedef ubi_dlList *ubi_dlListPtr;
+
+/* ========================================================================== **
+ * Macros...
+ *
+ * ubi_dlNewList - Macro used to declare and initialize a new list in one
+ * swell foop. It is used when defining a variable of
+ * type ubi_dlList. The definition
+ * static ubi_dlNewList( gerbil );
+ * is translated to
+ * static ubi_dlList gerbil[1] = {{ NULL, NULL, 0 }};
+ *
+ * ubi_dlCount - Return the number of entries currently in the list.
+ *
+ * ubi_dlAddHead - Add a new node at the head of the list.
+ * ubi_dlAddNext - Add a node following the given node.
+ * ubi_dlAddTail - Add a new node at the tail of the list.
+ * Note: AddTail evaluates the L parameter twice.
+ *
+ * ubi_dlRemHead - Remove the node at the head of the list, if any.
+ * Note: RemHead evaluates the L parameter twice.
+ * ubi_dlRemThis - Remove the indicated node.
+ * ubi_dlRemTail - Remove the node at the tail of the list, if any.
+ * Note: RemTail evaluates the L parameter twice.
+ *
+ * ubi_dlFirst - Return a pointer to the first node in the list, if any.
+ * ubi_dlLast - Return a pointer to the last node in the list, if any.
+ * ubi_dlNext - Given a node, return a pointer to the next node.
+ * ubi_dlPrev - Given a node, return a pointer to the previous node.
+ *
+ * ubi_dlPush - Add a node at the head of the list (synonym of AddHead).
+ * ubi_dlPop - Remove a node at the head of the list (synonym of RemHead).
+ * ubi_dlEnqueue - Add a node at the tail of the list (sysnonym of AddTail).
+ * ubi_dlDequeue - Remove a node at the head of the list (synonym of RemHead).
+ *
+ * Note that all of these provide type casting of the parameters. The
+ * Add and Rem macros are nothing more than nice front-ends to the
+ * Insert and Remove operations.
+ *
+ * Also note that the First, Next and Last macros do no parameter checking!
+ *
+ */
+
+#define ubi_dlNewList( L ) ubi_dlList (L)[1] = {{ NULL, NULL, 0 }}
+
+#define ubi_dlCount( L ) (((ubi_dlListPtr)(L))->count)
+
+#define ubi_dlAddHead( L, N ) \
+ ubi_dlInsert( (ubi_dlListPtr)(L), (ubi_dlNodePtr)(N), NULL )
+
+#define ubi_dlAddNext( L, N, A ) \
+ ubi_dlInsert( (ubi_dlListPtr)(L), \
+ (ubi_dlNodePtr)(N), \
+ (ubi_dlNodePtr)(A) )
+
+#define ubi_dlAddTail( L, N ) \
+ ubi_dlInsert( (ubi_dlListPtr)(L), \
+ (ubi_dlNodePtr)(N), \
+ (((ubi_dlListPtr)(L))->Tail) )
+
+#define ubi_dlRemHead( L ) ubi_dlRemove( (ubi_dlListPtr)(L), \
+ (((ubi_dlListPtr)(L))->Head) )
+
+#define ubi_dlRemThis( L, N ) ubi_dlRemove( (ubi_dlListPtr)(L), \
+ (ubi_dlNodePtr)(N) )
+
+#define ubi_dlRemTail( L ) ubi_dlRemove( (ubi_dlListPtr)(L), \
+ (((ubi_dlListPtr)(L))->Tail) )
+
+#define ubi_dlFirst( L ) (((ubi_dlListPtr)(L))->Head)
+
+#define ubi_dlLast( L ) (((ubi_dlListPtr)(L))->Tail)
+
+#define ubi_dlNext( N ) (((ubi_dlNodePtr)(N))->Next)
+
+#define ubi_dlPrev( N ) (((ubi_dlNodePtr)(N))->Prev)
+
+#define ubi_dlPush ubi_dlAddHead
+#define ubi_dlPop ubi_dlRemHead
+#define ubi_dlEnqueue ubi_dlAddTail
+#define ubi_dlDequeue ubi_dlRemHead
+
+/* ========================================================================== **
+ * Function prototypes...
+ */
+
+ubi_dlListPtr ubi_dlInitList( ubi_dlListPtr ListPtr );
+ /* ------------------------------------------------------------------------ **
+ * Initialize a doubly-linked list header.
+ *
+ * Input: ListPtr - A pointer to the list structure that is to be
+ * initialized for use.
+ *
+ * Output: A pointer to the initialized list header (i.e., same as
+ * <ListPtr>).
+ *
+ * ------------------------------------------------------------------------ **
+ */
+
+ubi_dlNodePtr ubi_dlInsert( ubi_dlListPtr ListPtr,
+ ubi_dlNodePtr New,
+ ubi_dlNodePtr After );
+ /* ------------------------------------------------------------------------ **
+ * Insert a new node into the list.
+ *
+ * Input: ListPtr - A pointer to the list into which the node is to
+ * be inserted.
+ * New - Pointer to the new node.
+ * After - NULL, or a pointer to a node that is already in the
+ * list.
+ * If NULL, then <New> will be added at the head of the
+ * list, else it will be added following <After>.
+ *
+ * Output: A pointer to the node that was inserted into the list (i.e.,
+ * the same as <New>).
+ *
+ * ------------------------------------------------------------------------ **
+ */
+
+ubi_dlNodePtr ubi_dlRemove( ubi_dlListPtr ListPtr, ubi_dlNodePtr Old );
+ /* ------------------------------------------------------------------------ **
+ * Remove a node from the list.
+ *
+ * Input: ListPtr - A pointer to the list from which <Old> is to be
+ * removed.
+ * Old - A pointer to the node that is to be removed from the
+ * list.
+ *
+ * Output: A pointer to the node that was removed (i.e., <Old>).
+ *
+ * ------------------------------------------------------------------------ **
+ */
+
+/* ================================ The End ================================= */
+#endif /* UBI_DLINKLIST_H */
diff --git a/source/ubiqx/ubi_sLinkList.c b/source/ubiqx/ubi_sLinkList.c
new file mode 100644
index 00000000000..ff75931b470
--- /dev/null
+++ b/source/ubiqx/ubi_sLinkList.c
@@ -0,0 +1,187 @@
+/* ========================================================================== **
+ * ubi_sLinkList.c
+ *
+ * Copyright (C) 1997, 1998 by Christopher R. Hertel
+ *
+ * Email: crh@ubiqx.mn.org
+ * -------------------------------------------------------------------------- **
+ * This module implements a simple singly-linked list.
+ * -------------------------------------------------------------------------- **
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 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 Library General Public
+ * License along with this library; if not, write to the Free
+ * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * -------------------------------------------------------------------------- **
+ *
+ * Log: ubi_sLinkList.c,v
+ * Revision 0.10 1999/06/19 16:58:06 crh
+ * Renamed the ubi_slRemove() function in ubi_sLinkList to
+ * ubi_slRemoveNext(). I was bothered by the fact that it didn't
+ * match the functionality of the ubi_dlRemove() function in
+ * ubi_dLinkList. The new name is more 'correct'.
+ *
+ * Revision 0.9 1998/07/24 07:30:20 crh
+ * Added the ubi_slNewList() macro.
+ *
+ * Revision 0.8 1998/06/04 21:29:27 crh
+ * Upper-cased defined constants (eg UBI_BINTREE_H) in some header files.
+ * This is more "standard", and is what people expect. Weird, eh?
+ *
+ * Revision 0.7 1998/06/03 18:06:03 crh
+ * Further fiddling with sys_include.h, which has been moved from the .c file
+ * to the .h file.
+ *
+ * Revision 0.6 1998/06/02 01:38:47 crh
+ * Changed include file name from ubi_null.h to sys_include.h to make it
+ * more generic.
+ *
+ * Revision 0.5 1998/05/20 04:38:05 crh
+ * The C file now includes ubi_null.h. See ubi_null.h for more info.
+ *
+ * Revision 0.4 1998/03/10 02:23:20 crh
+ * Combined ubi_StackQueue and ubi_sLinkList into one module. Redesigned
+ * the functions and macros. Not a complete rewrite but close to it.
+ *
+ * Revision 0.3 1998/01/03 01:59:52 crh
+ * Added ubi_slCount() macro.
+ *
+ * Revision 0.2 1997/10/21 03:35:18 crh
+ * Added parameter <After> in function Insert(). Made necessary changes
+ * to macro AddHead() and added macro AddHere().
+ *
+ * Revision 0.1 1997/10/16 02:53:45 crh
+ * Initial Revision.
+ *
+ * -------------------------------------------------------------------------- **
+ * This module implements a singly-linked list which may also be used as a
+ * queue or a stack. For a queue, entries are added at the tail and removed
+ * from the head of the list. For a stack, the entries are entered and
+ * removed from the head of the list. A traversal of the list will always
+ * start at the head of the list and proceed toward the tail. This is all
+ * mind-numbingly simple, but I'm surprised by the number of programs out
+ * there which re-implement this a dozen or so times.
+ *
+ * Note: When the list header is initialized, the Tail pointer is set to
+ * point to the Head pointer. This simplifies things a great deal,
+ * except that you can't initialize a stack or queue by simply
+ * zeroing it out. One sure way to initialize the header is to call
+ * ubi_slInit(). Another option would be something like this:
+ *
+ * ubi_slNewList( MyList );
+ *
+ * Which translates to:
+ *
+ * ubi_slList MyList[1] = { NULL, (ubi_slNodePtr)MyList, 0 };
+ *
+ * See ubi_slInit(), ubi_slNewList(), and the ubi_slList structure
+ * for more info.
+ *
+ * + Also, note that this module is similar to the ubi_dLinkList
+ * module. There are three key differences:
+ * - This is a singly-linked list, the other is a doubly-linked
+ * list.
+ * - In this module, if the list is empty, the tail pointer will
+ * point back to the head of the list as described above. This
+ * is not done in ubi_dLinkList.
+ * - The ubi_slRemoveNext() function, by necessity, removes the
+ * 'next' node. In ubi_dLinkList, the ubi_dlRemove() function
+ * removes the 'current' node.
+ *
+ * ========================================================================== **
+ */
+
+#include "ubi_sLinkList.h" /* Header for *this* module. */
+
+/* ========================================================================== **
+ * Functions...
+ */
+
+ubi_slListPtr ubi_slInitList( ubi_slListPtr ListPtr )
+ /* ------------------------------------------------------------------------ **
+ * Initialize a singly-linked list header.
+ *
+ * Input: ListPtr - A pointer to the list structure that is to be
+ * initialized for use.
+ *
+ * Output: A pointer to the initialized list header (i.e., same as
+ * <ListPtr>).
+ *
+ * ------------------------------------------------------------------------ **
+ */
+ {
+ ListPtr->Head = NULL;
+ ListPtr->Tail = (ubi_slNodePtr)ListPtr;
+ ListPtr->count = 0;
+ return( ListPtr );
+ } /* ubi_slInitList */
+
+ubi_slNodePtr ubi_slInsert( ubi_slListPtr ListPtr,
+ ubi_slNodePtr New,
+ ubi_slNodePtr After )
+ /* ------------------------------------------------------------------------ **
+ * Add a node to the list.
+ *
+ * Input: ListPtr - A pointer to the list into which the node is to
+ * be inserted.
+ * New - Pointer to the node that is to be added to the list.
+ * After - Pointer to a list in a node after which the new node
+ * will be inserted. If NULL, then the new node will
+ * be added at the head of the list.
+ *
+ * Output: A pointer to the node that was inserted into the list (i.e.,
+ * the same as <New>).
+ *
+ * ------------------------------------------------------------------------ **
+ */
+ {
+ After = After ? After : (ubi_slNodePtr)ListPtr;
+ New->Next = After->Next;
+ After->Next = New;
+ if( !(New->Next) )
+ ListPtr->Tail = New;
+ (ListPtr->count)++;
+ return( New );
+ } /* ubi_slInsert */
+
+ubi_slNodePtr ubi_slRemoveNext( ubi_slListPtr ListPtr, ubi_slNodePtr AfterMe )
+ /* ------------------------------------------------------------------------ **
+ * Remove the node followng <AfterMe>. If <AfterMe> is NULL, remove from
+ * the head of the list.
+ *
+ * Input: ListPtr - A pointer to the list from which the node is to be
+ * removed.
+ * AfterMe - Pointer to the node preceeding the node to be
+ * removed.
+ *
+ * Output: A pointer to the node that was removed, or NULL if the list is
+ * empty.
+ *
+ * ------------------------------------------------------------------------ **
+ */
+ {
+ ubi_slNodePtr DelNode;
+
+ AfterMe = AfterMe ? AfterMe : (ubi_slNodePtr)ListPtr;
+ DelNode = AfterMe->Next;
+ if( DelNode )
+ {
+ if( !(DelNode->Next) )
+ ListPtr->Tail = AfterMe;
+ AfterMe->Next = DelNode->Next;
+ (ListPtr->count)--;
+ }
+ return( DelNode );
+ } /* ubi_slRemoveNext */
+
+/* ================================ The End ================================= */
diff --git a/source/ubiqx/ubi_sLinkList.h b/source/ubiqx/ubi_sLinkList.h
new file mode 100644
index 00000000000..53bfa400671
--- /dev/null
+++ b/source/ubiqx/ubi_sLinkList.h
@@ -0,0 +1,254 @@
+#ifndef UBI_SLINKLIST_H
+#define UBI_SLINKLIST_H
+/* ========================================================================== **
+ * ubi_sLinkList.h
+ *
+ * Copyright (C) 1997, 1998 by Christopher R. Hertel
+ *
+ * Email: crh@ubiqx.mn.org
+ * -------------------------------------------------------------------------- **
+ * This module implements a simple singly-linked list.
+ * -------------------------------------------------------------------------- **
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 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 Library General Public
+ * License along with this library; if not, write to the Free
+ * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * -------------------------------------------------------------------------- **
+ *
+ * Log: ubi_sLinkList.h,v
+ * Revision 0.10 1999/06/19 16:58:06 crh
+ * Renamed the ubi_slRemove() function in ubi_sLinkList to
+ * ubi_slRemoveNext(). I was bothered by the fact that it didn't
+ * match the functionality of the ubi_dlRemove() function in
+ * ubi_dLinkList. The new name is more 'correct'.
+ *
+ * Revision 0.9 1998/07/24 07:30:20 crh
+ * Added the ubi_slNewList() macro.
+ *
+ * Revision 0.8 1998/06/04 21:29:27 crh
+ * Upper-cased defined constants (eg UBI_BINTREE_H) in some header files.
+ * This is more "standard", and is what people expect. Weird, eh?
+ *
+ * Revision 0.7 1998/06/03 18:06:03 crh
+ * Further fiddling with sys_include.h, which has been moved from the .c file
+ * to the .h file.
+ *
+ * Revision 0.6 1998/06/02 01:38:47 crh
+ * Changed include file name from ubi_null.h to sys_include.h to make it
+ * more generic.
+ *
+ * Revision 0.5 1998/05/20 04:38:05 crh
+ * The C file now includes ubi_null.h. See ubi_null.h for more info.
+ *
+ * Revision 0.4 1998/03/10 02:22:39 crh
+ * Combined ubi_StackQueue and ubi_sLinkList into one module. Redesigned
+ * the functions and macros. Not a complete rewrite but close to it.
+ *
+ * Revision 0.3 1998/01/03 02:00:02 crh
+ * Added ubi_slCount() macro.
+ *
+ * Revision 0.2 1997/10/21 03:36:14 crh
+ * Added parameter <After> in function Insert(). Made necessary changes
+ * to macro AddHead() and added macro AddHere().
+ *
+ * Revision 0.1 1997/10/16 02:54:08 crh
+ * Initial Revision.
+ *
+ * -------------------------------------------------------------------------- **
+ * This module implements a singly-linked list which may also be used as a
+ * queue or a stack. For a queue, entries are added at the tail and removed
+ * from the head of the list. For a stack, the entries are entered and
+ * removed from the head of the list. A traversal of the list will always
+ * start at the head of the list and proceed toward the tail. This is all
+ * mind-numbingly simple, but I'm surprised by the number of programs out
+ * there which re-implement this a dozen or so times.
+ *
+ * Note: When the list header is initialized, the Tail pointer is set to
+ * point to the Head pointer. This simplifies things a great deal,
+ * except that you can't initialize a stack or queue by simply
+ * zeroing it out. One sure way to initialize the header is to call
+ * ubi_slInit(). Another option would be something like this:
+ *
+ * ubi_slNewList( MyList );
+ *
+ * Which translates to:
+ *
+ * ubi_slList MyList[1] = { NULL, (ubi_slNodePtr)MyList, 0 };
+ *
+ * See ubi_slInit(), ubi_slNewList(), and the ubi_slList structure
+ * for more info.
+ *
+ * + Also, note that this module is similar to the ubi_dLinkList
+ * module. There are three key differences:
+ * - This is a singly-linked list, the other is a doubly-linked
+ * list.
+ * - In this module, if the list is empty, the tail pointer will
+ * point back to the head of the list as described above. This
+ * is not done in ubi_dLinkList.
+ * - The ubi_slRemoveNext() function, by necessity, removes the
+ * 'next' node. In ubi_dLinkList, the ubi_dlRemove() function
+ * removes the 'current' node.
+ *
+ * ========================================================================== **
+ */
+
+#include "sys_include.h" /* System-specific includes. */
+
+/* ========================================================================== **
+ * Typedefs...
+ *
+ * ubi_slNode - This is the basic node structure.
+ * ubi_slNodePtr - Pointer to a node.
+ * ubi_slList - This is the list header structure.
+ * ubi_slListPtr - Pointer to a List (i.e., a list header structure).
+ *
+ */
+
+typedef struct ubi_slListNode
+ {
+ struct ubi_slListNode *Next;
+ } ubi_slNode;
+
+typedef ubi_slNode *ubi_slNodePtr;
+
+typedef struct
+ {
+ ubi_slNodePtr Head;
+ ubi_slNodePtr Tail;
+ unsigned long count;
+ } ubi_slList;
+
+typedef ubi_slList *ubi_slListPtr;
+
+
+/* ========================================================================== **
+ * Macros...
+ *
+ * ubi_slNewList - Macro used to declare and initialize a list header in
+ * one step.
+ *
+ * ubi_slCount - Returns the current number of entries in the list.
+ *
+ * ubi_slAddHead - Add a new node at the head of the list.
+ * ubi_slAddNext - Add a new node following the indicated node.
+ * ubi_slAddTail - Add a new node to the tail of the list.
+ * Note: AddTail evaluates the L parameter twice.
+ *
+ * ubi_slRemHead - Remove the node at the head of the list, if any.
+ * ubi_slRemNext - Remove the node following the given node.
+ *
+ * ubi_slFirst - Return a pointer to the first node in the list, if any.
+ * ubi_slNext - Given a node, return a pointer to the next node.
+ * ubi_slLast - Return a pointer to the last node in the list, if any.
+ *
+ * ubi_slPush - Add a node at the head of the list (synonym of AddHead).
+ * ubi_slPop - Remove a node at the head of the list (synonym of RemHead).
+ * ubi_slEnqueue - Add a node at the tail of the list (sysnonym of AddTail).
+ * ubi_slDequeue - Remove a node at the head of the list (synonym of RemHead).
+ *
+ * Note that all of these provide type casting of the parameters. The
+ * Add and Rem macros are nothing more than nice front-ends to the
+ * Insert and Remove functions.
+ *
+ * Also note that the First, Next and Last macros do no parameter checking!
+ *
+ */
+
+#define ubi_slNewList( L ) ubi_slList (L)[1] = {{ NULL, (ubi_slNodePtr)(L), 0 }}
+
+#define ubi_slCount( L ) (((ubi_slListPtr)(L))->count)
+
+#define ubi_slAddHead( L, N ) \
+ ubi_slInsert( (ubi_slListPtr)(L), (ubi_slNodePtr)(N), NULL )
+
+#define ubi_slAddNext( L, N, A ) \
+ ubi_slInsert( (ubi_slListPtr)(L), \
+ (ubi_slNodePtr)(N), \
+ (ubi_slNodePtr)(A) )
+
+#define ubi_slAddTail( L, N ) \
+ ubi_slInsert( (ubi_slListPtr)(L), \
+ (ubi_slNodePtr)(N), \
+ ((ubi_slListPtr)(L))->Tail )
+
+#define ubi_slRemHead( L ) ubi_slRemoveNext( (ubi_slListPtr)(L), NULL )
+
+#define ubi_slRemNext( L, N ) \
+ ubi_slRemoveNext( (ubi_slListPtr)(L), (ubi_slNodePtr)(N) )
+
+#define ubi_slFirst( L ) (((ubi_slListPtr)(L))->Head)
+
+#define ubi_slNext( N ) (((ubi_slNodePtr)(N))->Next)
+
+#define ubi_slLast( L ) (((ubi_slListPtr)(L))->Tail)
+
+#define ubi_slPush ubi_slAddHead
+#define ubi_slPop ubi_slRemHead
+#define ubi_slEnqueue ubi_slAddTail
+#define ubi_slDequeue ubi_slRemHead
+
+/* ========================================================================== **
+ * Function prototypes...
+ */
+
+ubi_slListPtr ubi_slInitList( ubi_slListPtr ListPtr );
+ /* ------------------------------------------------------------------------ **
+ * Initialize a singly-linked list header.
+ *
+ * Input: ListPtr - A pointer to the list structure that is to be
+ * initialized for use.
+ *
+ * Output: A pointer to the initialized list header (i.e., same as
+ * <ListPtr>).
+ *
+ * ------------------------------------------------------------------------ **
+ */
+
+ubi_slNodePtr ubi_slInsert( ubi_slListPtr ListPtr,
+ ubi_slNodePtr New,
+ ubi_slNodePtr After );
+ /* ------------------------------------------------------------------------ **
+ * Add a node to the list.
+ *
+ * Input: ListPtr - A pointer to the list into which the node is to
+ * be inserted.
+ * New - Pointer to the node that is to be added to the list.
+ * After - Pointer to a list in a node after which the new node
+ * will be inserted. If NULL, then the new node will
+ * be added at the head of the list.
+ *
+ * Output: A pointer to the node that was inserted into the list (i.e.,
+ * the same as <New>).
+ *
+ * ------------------------------------------------------------------------ **
+ */
+
+ubi_slNodePtr ubi_slRemoveNext( ubi_slListPtr ListPtr, ubi_slNodePtr AfterMe );
+ /* ------------------------------------------------------------------------ **
+ * Remove the node followng <AfterMe>. If <AfterMe> is NULL, remove from
+ * the head of the list.
+ *
+ * Input: ListPtr - A pointer to the list from which the node is to be
+ * removed.
+ * AfterMe - Pointer to the node preceeding the node to be
+ * removed.
+ *
+ * Output: A pointer to the node that was removed, or NULL if the list is
+ * empty.
+ *
+ * ------------------------------------------------------------------------ **
+ */
+
+/* ================================ The End ================================= */
+#endif /* UBI_SLINKLIST_H */
diff --git a/source/utils/.cvsignore b/source/utils/.cvsignore
new file mode 100644
index 00000000000..6b8749f64eb
--- /dev/null
+++ b/source/utils/.cvsignore
@@ -0,0 +1 @@
+net_proto.h \ No newline at end of file
diff --git a/source/utils/debug2html.c b/source/utils/debug2html.c
new file mode 100644
index 00000000000..f9a1f43f461
--- /dev/null
+++ b/source/utils/debug2html.c
@@ -0,0 +1,253 @@
+/* ========================================================================== **
+ * debug2html.c
+ *
+ * Copyright (C) 1998 by Christopher R. Hertel
+ *
+ * Email: crh@ubiqx.mn.org
+ *
+ * -------------------------------------------------------------------------- **
+ * Parse Samba debug logs (2.0 & greater) and output the results as HTML.
+ * -------------------------------------------------------------------------- **
+ *
+ * This program is free software; you can 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.
+ *
+ * -------------------------------------------------------------------------- **
+ * This program provides an example of the use of debugparse.c, and also
+ * does a decent job of converting Samba logs into HTML.
+ * -------------------------------------------------------------------------- **
+ *
+ * Revision 1.4 1998/11/13 03:37:01 tridge
+ * fixes for OSF1 compilation
+ *
+ * Revision 1.3 1998/10/28 20:33:35 crh
+ * I've moved the debugparse module files into the ubiqx directory because I
+ * know that 'make proto' will ignore them there. The debugparse.h header
+ * file is included in includes.h, and includes.h is included in debugparse.c,
+ * so all of the pieces "see" each other. I've compiled and tested this,
+ * and it does seem to work. It's the same compromise model I used when
+ * adding the ubiqx modules into the system, which is why I put it all into
+ * the same directory.
+ *
+ * Chris -)-----
+ *
+ * Revision 1.1 1998/10/26 23:21:37 crh
+ * Here is the simple debug parser and the debug2html converter. Still to do:
+ *
+ * * Debug message filtering.
+ * * I need to add all this to Makefile.in
+ * (If it looks at all strange I'll ask for help.)
+ *
+ * If you want to compile debug2html, you'll need to do it by hand until I
+ * make the changes to Makefile.in. Sorry.
+ *
+ * Chris -)-----
+ *
+ * ========================================================================== **
+ */
+
+#include "debugparse.h"
+
+/* -------------------------------------------------------------------------- **
+ * The size of the read buffer.
+ */
+
+#define DBG_BSIZE 1024
+
+/* -------------------------------------------------------------------------- **
+ * Functions...
+ */
+
+static dbg_Token modechange( dbg_Token new, dbg_Token mode )
+ /* ------------------------------------------------------------------------ **
+ * Handle a switch between header and message printing.
+ *
+ * Input: new - The token value of the current token. This indicates
+ * the lexical item currently being recognized.
+ * mode - The current mode. This is either dbg_null or
+ * dbg_message. It could really be any toggle
+ * (true/false, etc.)
+ *
+ * Output: The new mode. This will be the same as the input mode unless
+ * there was a transition in or out of message processing.
+ *
+ * Notes: The purpose of the mode value is to mark the beginning and end
+ * of the message text block. In order to show the text in its
+ * correct format, it must be included within a <PRE></PRE> block.
+ *
+ * ------------------------------------------------------------------------ **
+ */
+ {
+ switch( new )
+ {
+ case dbg_null:
+ case dbg_ignore:
+ return( mode );
+ case dbg_message:
+ if( dbg_message != mode )
+ {
+ /* Switching to message mode. */
+ (void)printf( "<PRE>\n" );
+ return( dbg_message );
+ }
+ break;
+ default:
+ if( dbg_message == mode )
+ {
+ /* Switching out of message mode. */
+ (void)printf( "</PRE>\n\n" );
+ return( dbg_null );
+ }
+ }
+
+ return( mode );
+ } /* modechange */
+
+static void newblock( dbg_Token old, dbg_Token new )
+ /* ------------------------------------------------------------------------ **
+ * Handle the transition between tokens.
+ *
+ * Input: old - The previous token.
+ * new - The current token.
+ *
+ * Output: none.
+ *
+ * Notes: This is called whenever there is a transition from one token
+ * type to another. It first prints the markup tags that close
+ * the previous token, and then the markup tags for the new
+ * token.
+ *
+ * ------------------------------------------------------------------------ **
+ */
+ {
+ switch( old )
+ {
+ case dbg_timestamp:
+ (void)printf( ",</B>" );
+ break;
+ case dbg_level:
+ (void)printf( "</FONT>]</B>\n " );
+ break;
+ case dbg_sourcefile:
+ (void)printf( ":" );
+ break;
+ case dbg_lineno:
+ (void)printf( ")" );
+ break;
+ }
+
+ switch( new )
+ {
+ case dbg_timestamp:
+ (void)printf( "<B>[" );
+ break;
+ case dbg_level:
+ (void)printf( " <B><FONT COLOR=MAROON>" );
+ break;
+ case dbg_lineno:
+ (void)printf( "(" );
+ break;
+ }
+ } /* newblock */
+
+static void charprint( dbg_Token tok, int c )
+ /* ------------------------------------------------------------------------ **
+ * Filter the input characters to determine what goes to output.
+ *
+ * Input: tok - The token value of the current character.
+ * c - The current character.
+ *
+ * Output: none.
+ *
+ * ------------------------------------------------------------------------ **
+ */
+ {
+ switch( tok )
+ {
+ case dbg_ignore:
+ case dbg_header:
+ break;
+ case dbg_null:
+ case dbg_eof:
+ (void)putchar( '\n' );
+ break;
+ default:
+ switch( c )
+ {
+ case '<':
+ (void)printf( "&lt;" );
+ break;
+ case '>':
+ (void)printf( "&gt;" );
+ break;
+ case '&':
+ (void)printf( "&amp;" );
+ break;
+ case '\"':
+ (void)printf( "&#34;" );
+ break;
+ default:
+ (void)putchar( c );
+ break;
+ }
+ }
+ } /* charprint */
+
+int main( int argc, char *argv[] )
+ /* ------------------------------------------------------------------------ **
+ * This simple program scans and parses Samba debug logs, and produces HTML
+ * output.
+ *
+ * Input: argc - Currently ignored.
+ * argv - Currently ignored.
+ *
+ * Output: Always zero.
+ *
+ * Notes: The HTML output is sent to stdout.
+ *
+ * ------------------------------------------------------------------------ **
+ */
+ {
+ int i;
+ int len;
+ char bufr[DBG_BSIZE];
+ dbg_Token old = dbg_null,
+ new = dbg_null,
+ state = dbg_null,
+ mode = dbg_null;
+
+ (void)printf( "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 3.2//EN\">\n" );
+ (void)printf( "<HTML>\n<HEAD>\n" );
+ (void)printf( " <TITLE>Samba Debug Output</TITLE>\n</HEAD>\n\n<BODY>\n" );
+
+ while( (!feof( stdin ))
+ && ((len = fread( bufr, 1, DBG_BSIZE, stdin )) > 0) )
+ {
+ for( i = 0; i < len; i++ )
+ {
+ old = new;
+ new = dbg_char2token( &state, bufr[i] );
+ if( new != old )
+ {
+ mode = modechange( new, mode );
+ newblock( old, new );
+ }
+ charprint( new, bufr[i] );
+ }
+ }
+ (void)modechange( dbg_eof, mode );
+
+ (void)printf( "</BODY>\n</HTML>\n" );
+ return( 0 );
+ } /* main */
diff --git a/source/utils/editreg.c b/source/utils/editreg.c
new file mode 100644
index 00000000000..a0cfa2bb07d
--- /dev/null
+++ b/source/utils/editreg.c
@@ -0,0 +1,4145 @@
+/*
+ Samba Unix/Linux SMB client utility editreg.c
+ Copyright (C) 2002 Richard Sharpe, rsharpe@richardsharpe.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 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. */
+
+/*************************************************************************
+
+ A utility to edit a Windows NT/2K etc registry file.
+
+ Many of the ideas in here come from other people and software.
+ I first looked in Wine in misc/registry.c and was also influenced by
+ http://www.wednesday.demon.co.uk/dosreg.html
+
+ Which seems to contain comments from someone else. I reproduce them here
+ incase the site above disappears. It actually comes from
+ http://home.eunet.no/~pnordahl/ntpasswd/WinReg.txt.
+
+ The goal here is to read the registry into memory, manipulate it, and then
+ write it out if it was changed by any actions of the user.
+
+The windows NT registry has 2 different blocks, where one can occur many
+times...
+
+the "regf"-Block
+================
+
+"regf" is obviosly 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!
+
+Offset Size Contents
+0x00000000 D-Word ID: ASCII-"regf" = 0x66676572
+0x00000004 D-Word ???? //see struct REGF
+0x00000008 D-Word ???? Always the same value as at 0x00000004
+0x0000000C Q-Word last modify date in WinNT date-format
+0x00000014 D-Word 1
+0x00000018 D-Word 3
+0x0000001C D-Word 0
+0x00000020 D-Word 1
+0x00000024 D-Word Offset of 1st key record
+0x00000028 D-Word Size of the data-blocks (Filesize-4kb)
+0x0000002C D-Word 1
+0x000001FC D-Word Sum of all D-Words from 0x00000000 to
+0x000001FB //XOR of all words. Nigel
+
+I have analyzed more registry files (from multiple machines running
+NT 4.0 german version) and could not find an explanation for the values
+marked with ???? the rest of the first 4kb page is not important...
+
+the "hbin"-Block
+================
+I don't know what "hbin" stands for, but this block is always a multiple
+of 4kb in size.
+
+Inside these hbin-blocks the different records are placed. The memory-
+management looks like a C-compiler heap management to me...
+
+hbin-Header
+===========
+Offset Size Contents
+0x0000 D-Word ID: ASCII-"hbin" = 0x6E696268
+0x0004 D-Word Offset from the 1st hbin-Block
+0x0008 D-Word Offset to the next hbin-Block
+0x001C D-Word Block-size
+
+The values in 0x0008 and 0x001C should be the same, so I don't know
+if they are correct or swapped...
+
+From offset 0x0020 inside a hbin-block data is stored with the following
+format:
+
+Offset Size Contents
+0x0000 D-Word Data-block size //this size must be a
+multiple of 8. Nigel
+0x0004 ???? Data
+
+If the size field is negative (bit 31 set), the corresponding block
+is free and has a size of -blocksize!
+
+That does not seem to be true. All block lengths seem to be negative!
+(Richard Sharpe)
+
+The data is stored as one record per block. Block size is a multiple
+of 4 and the last block reaches the next hbin-block, leaving no room.
+
+(That also seems incorrect, in that the block size if a multiple of 8.
+That is, the block, including the 4 byte header, is always a multiple of
+8 bytes. Richard Sharpe.)
+
+Records in the hbin-blocks
+==========================
+
+nk-Record
+
+ The nk-record can be treated as a kombination of tree-record and
+ key-record of the win 95 registry.
+
+lf-Record
+
+ The lf-record is the counterpart to the RGKN-record (the
+ hash-function)
+
+vk-Record
+
+ The vk-record consists information to a single value.
+
+sk-Record
+
+ sk (? Security Key ?) is the ACL of the registry.
+
+Value-Lists
+
+ The value-lists contain information about which values are inside a
+ sub-key and don't have a header.
+
+Datas
+
+ The datas of the registry are (like the value-list) stored without a
+ header.
+
+All offset-values are relative to the first hbin-block and point to the
+block-size field of the record-entry. to get the file offset, you have to add
+the header size (4kb) and the size field (4 bytes)...
+
+the nk-Record
+=============
+Offset Size Contents
+0x0000 Word ID: ASCII-"nk" = 0x6B6E
+0x0002 Word for the root-key: 0x2C, otherwise 0x20 //key symbolic links 0x10. Nigel
+0x0004 Q-Word write-date/time in windows nt notation
+0x0010 D-Word Offset of Owner/Parent key
+0x0014 D-Word number of sub-Keys
+0x001C D-Word Offset of the sub-key lf-Records
+0x0024 D-Word number of values
+0x0028 D-Word Offset of the Value-List
+0x002C D-Word Offset of the sk-Record
+
+0x0030 D-Word Offset of the Class-Name //see NK structure for the use of these fields. Nigel
+0x0044 D-Word Unused (data-trash) //some kind of run time index. Does not appear to be important. Nigel
+0x0048 Word name-length
+0x004A Word class-name length
+0x004C ???? key-name
+
+the Value-List
+==============
+Offset Size Contents
+0x0000 D-Word Offset 1st Value
+0x0004 D-Word Offset 2nd Value
+0x???? D-Word Offset nth Value
+
+To determine the number of values, you have to look at the owner-nk-record!
+
+Der vk-Record
+=============
+Offset Size Contents
+0x0000 Word ID: ASCII-"vk" = 0x6B76
+0x0002 Word name length
+0x0004 D-Word length of the data //if top bit is set when offset contains data. Nigel
+0x0008 D-Word Offset of Data
+0x000C D-Word Type of value
+0x0010 Word Flag
+0x0012 Word Unused (data-trash)
+0x0014 ???? Name
+
+If bit 0 of the flag-word is set, a name is present, otherwise the value has no name (=default)
+
+If the data-size is lower 5, the data-offset value is used to store the data itself!
+
+The data-types
+==============
+Wert Beteutung
+0x0001 RegSZ: character string (in UNICODE!)
+0x0002 ExpandSZ: string with "%var%" expanding (UNICODE!)
+0x0003 RegBin: raw-binary value
+0x0004 RegDWord: Dword
+0x0007 RegMultiSZ: multiple strings, seperated with 0
+ (UNICODE!)
+
+The "lf"-record
+===============
+Offset Size Contents
+0x0000 Word ID: ASCII-"lf" = 0x666C
+0x0002 Word number of keys
+0x0004 ???? Hash-Records
+
+Hash-Record
+===========
+Offset Size Contents
+0x0000 D-Word Offset of corresponding "nk"-Record
+0x0004 D-Word ASCII: the first 4 characters of the key-name, padded with 0's. Case sensitiv!
+
+Keep in mind, that the value at 0x0004 is used for checking the data-consistency! If you change the
+key-name you have to change the hash-value too!
+
+//These hashrecords must be sorted low to high within the lf record. Nigel.
+
+The "sk"-block
+==============
+(due to the complexity of the SAM-info, not clear jet)
+(This is just a self-relative security descriptor in the data. R Sharpe.)
+
+
+Offset Size Contents
+0x0000 Word ID: ASCII-"sk" = 0x6B73
+0x0002 Word Unused
+0x0004 D-Word Offset of previous "sk"-Record
+0x0008 D-Word Offset of next "sk"-Record
+0x000C D-Word usage-counter
+0x0010 D-Word Size of "sk"-record in bytes
+???? //standard self
+relative security desciptor. Nigel
+???? ???? Security and auditing settings...
+????
+
+The usage counter counts the number of references to this
+"sk"-record. You can use one "sk"-record for the entire registry!
+
+Windows nt date/time format
+===========================
+The time-format is a 64-bit integer which is incremented every
+0,0000001 seconds by 1 (I don't know how accurate it realy is!)
+It starts with 0 at the 1st of january 1601 0:00! All values are
+stored in GMT time! The time-zone is important to get the real
+time!
+
+Common values for win95 and win-nt
+==================================
+Offset values marking an "end of list", are either 0 or -1 (0xFFFFFFFF).
+If a value has no name (length=0, flag(bit 0)=0), it is treated as the
+"Default" entry...
+If a value has no data (length=0), it is displayed as empty.
+
+simplyfied win-3.?? registry:
+=============================
+
++-----------+
+| next rec. |---+ +----->+------------+
+| first sub | | | | Usage cnt. |
+| name | | +-->+------------+ | | length |
+| value | | | | next rec. | | | text |------->+-------+
++-----------+ | | | name rec. |--+ +------------+ | xxxxx |
+ +------------+ | | value rec. |-------->+------------+ +-------+
+ v | +------------+ | Usage cnt. |
++-----------+ | | length |
+| next rec. | | | text |------->+-------+
+| first sub |------+ +------------+ | xxxxx |
+| name | +-------+
+| value |
++-----------+
+
+Greatly simplyfied structure of the nt-registry:
+================================================
+
++---------------------------------------------------------------+
+| |
+v |
++---------+ +---------->+-----------+ +----->+---------+ |
+| "nk" | | | lf-rec. | | | nk-rec. | |
+| ID | | | # of keys | | | parent |---+
+| Date | | | 1st key |--+ | .... |
+| parent | | +-----------+ +---------+
+| suk-keys|-----+
+| values |--------------------->+----------+
+| SK-rec. |---------------+ | 1. value |--> +----------+
+| class |--+ | +----------+ | vk-rec. |
++---------+ | | | .... |
+ v | | data |--> +-------+
+ +------------+ | +----------+ | xxxxx |
+ | Class name | | +-------+
+ +------------+ |
+ v
+ +---------+ +---------+
+ +----->| next sk |--->| Next sk |--+
+ | +---| prev sk |<---| prev sk | |
+ | | | .... | | ... | |
+ | | +---------+ +---------+ |
+ | | ^ |
+ | | | |
+ | +--------------------+ |
+ +----------------------------------+
+
+---------------------------------------------------------------------------
+
+Hope this helps.... (Although it was "fun" for me to uncover this things,
+ it took me several sleepless nights ;)
+
+ B.D.
+
+*************************************************************************/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <assert.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <sys/mman.h>
+#include <string.h>
+#include <fcntl.h>
+
+#define False 0
+#define True 1
+#define REG_KEY_LIST_SIZE 10
+
+/*
+ * Structures for dealing with the on-disk format of the registry
+ */
+
+#define IVAL(buf) ((unsigned int) \
+ (unsigned int)*((unsigned char *)(buf)+3)<<24| \
+ (unsigned int)*((unsigned char *)(buf)+2)<<16| \
+ (unsigned int)*((unsigned char *)(buf)+1)<<8| \
+ (unsigned int)*((unsigned char *)(buf)+0))
+
+#define SVAL(buf) ((unsigned short) \
+ (unsigned short)*((unsigned char *)(buf)+1)<<8| \
+ (unsigned short)*((unsigned char *)(buf)+0))
+
+#define CVAL(buf) ((unsigned char)*((unsigned char *)(buf)))
+
+#define SIVAL(buf, val) \
+ ((((unsigned char *)(buf))[0])=(unsigned char)((val)&0xFF),\
+ (((unsigned char *)(buf))[1])=(unsigned char)(((val)>>8)&0xFF),\
+ (((unsigned char *)(buf))[2])=(unsigned char)(((val)>>16)&0xFF),\
+ (((unsigned char *)(buf))[3])=(unsigned char)((val)>>24))
+
+#define SSVAL(buf, val) \
+ ((((unsigned char *)(buf))[0])=(unsigned char)((val)&0xFF),\
+ (((unsigned char *)(buf))[1])=(unsigned char)((val)>>8))
+
+static int verbose = 0;
+static int print_security = 0;
+static int full_print = 0;
+static const char *def_owner_sid_str = NULL;
+
+/*
+ * These definitions are for the in-memory registry structure.
+ * It is a tree structure that mimics what you see with tools like regedit
+ */
+
+/*
+ * DateTime struct for Windows
+ */
+
+typedef struct date_time_s {
+ unsigned int low, high;
+} NTTIME;
+
+/*
+ * Definition of a Key. It has a name, classname, date/time last modified,
+ * sub-keys, values, and a security descriptor
+ */
+
+#define REG_ROOT_KEY 1
+#define REG_SUB_KEY 2
+#define REG_SYM_LINK 3
+
+typedef struct key_sec_desc_s KEY_SEC_DESC;
+
+typedef struct reg_key_s {
+ char *name; /* Name of the key */
+ char *class_name;
+ int type; /* One of REG_ROOT_KEY or REG_SUB_KEY */
+ NTTIME last_mod; /* Time last modified */
+ struct reg_key_s *owner;
+ struct key_list_s *sub_keys;
+ struct val_list_s *values;
+ KEY_SEC_DESC *security;
+ unsigned int offset; /* Offset of the record in the file */
+} REG_KEY;
+
+/*
+ * The KEY_LIST struct lists sub-keys.
+ */
+
+typedef struct key_list_s {
+ int key_count;
+ int max_keys;
+ REG_KEY *keys[1];
+} KEY_LIST;
+
+typedef struct val_key_s {
+ char *name;
+ int has_name;
+ int data_type;
+ int data_len;
+ void *data_blk; /* Might want a separate block */
+} VAL_KEY;
+
+typedef struct val_list_s {
+ int val_count;
+ int max_vals;
+ VAL_KEY *vals[1];
+} VAL_LIST;
+
+#ifndef MAXSUBAUTHS
+#define MAXSUBAUTHS 15
+#endif
+
+typedef struct sid_s {
+ unsigned char ver, auths;
+ unsigned char auth[6];
+ unsigned int sub_auths[MAXSUBAUTHS];
+} sid_t;
+
+typedef struct ace_struct_s {
+ unsigned char type, flags;
+ unsigned int perms; /* Perhaps a better def is in order */
+ sid_t *trustee;
+} ACE;
+
+typedef struct acl_struct_s {
+ unsigned short rev, refcnt;
+ unsigned short num_aces;
+ ACE *aces[1];
+} ACL;
+
+typedef struct sec_desc_s {
+ unsigned int rev, type;
+ sid_t *owner, *group;
+ ACL *sacl, *dacl;
+} SEC_DESC;
+
+#define SEC_DESC_NON 0
+#define SEC_DESC_RES 1
+#define SEC_DESC_OCU 2
+#define SEC_DESC_NBK 3
+typedef struct sk_struct SK_HDR;
+struct key_sec_desc_s {
+ struct key_sec_desc_s *prev, *next;
+ int ref_cnt;
+ int state;
+ int offset;
+ SK_HDR *sk_hdr; /* This means we must keep the registry in memory */
+ SEC_DESC *sec_desc;
+};
+
+/*
+ * All of the structures below actually have a four-byte length before them
+ * which always seems to be negative. The following macro retrieves that
+ * size as an integer
+ */
+
+#define BLK_SIZE(b) ((int)*(int *)(((int *)b)-1))
+
+typedef unsigned int DWORD;
+typedef unsigned short WORD;
+
+#define REG_REGF_ID 0x66676572
+
+typedef struct regf_block {
+ DWORD REGF_ID; /* regf */
+ DWORD uk1;
+ DWORD uk2;
+ DWORD tim1, tim2;
+ DWORD uk3; /* 1 */
+ DWORD uk4; /* 3 */
+ DWORD uk5; /* 0 */
+ DWORD uk6; /* 1 */
+ DWORD first_key; /* offset */
+ unsigned int dblk_size;
+ DWORD uk7[116]; /* 1 */
+ DWORD chksum;
+} REGF_HDR;
+
+typedef struct hbin_sub_struct {
+ DWORD dblocksize;
+ char data[1];
+} HBIN_SUB_HDR;
+
+#define REG_HBIN_ID 0x6E696268
+
+typedef struct hbin_struct {
+ DWORD HBIN_ID; /* hbin */
+ DWORD off_from_first;
+ DWORD off_to_next;
+ DWORD uk1;
+ DWORD uk2;
+ DWORD uk3;
+ DWORD uk4;
+ DWORD blk_size;
+ HBIN_SUB_HDR hbin_sub_hdr;
+} HBIN_HDR;
+
+#define REG_NK_ID 0x6B6E
+
+typedef struct nk_struct {
+ WORD NK_ID;
+ WORD type;
+ DWORD t1, t2;
+ DWORD uk1;
+ DWORD own_off;
+ DWORD subk_num;
+ DWORD uk2;
+ DWORD lf_off;
+ DWORD uk3;
+ DWORD val_cnt;
+ DWORD val_off;
+ DWORD sk_off;
+ DWORD clsnam_off;
+ DWORD unk4[4];
+ DWORD unk5;
+ WORD nam_len;
+ WORD clsnam_len;
+ char key_nam[1]; /* Actual length determined by nam_len */
+} NK_HDR;
+
+#define REG_SK_ID 0x6B73
+
+struct sk_struct {
+ WORD SK_ID;
+ WORD uk1;
+ DWORD prev_off;
+ DWORD next_off;
+ DWORD ref_cnt;
+ DWORD rec_size;
+ char sec_desc[1];
+};
+
+typedef struct ace_struct {
+ unsigned char type;
+ unsigned char flags;
+ unsigned short length;
+ unsigned int perms;
+ sid_t trustee;
+} REG_ACE;
+
+typedef struct acl_struct {
+ WORD rev;
+ WORD size;
+ DWORD num_aces;
+ REG_ACE *aces; /* One or more ACEs */
+} REG_ACL;
+
+typedef struct sec_desc_rec {
+ WORD rev;
+ WORD type;
+ DWORD owner_off;
+ DWORD group_off;
+ DWORD sacl_off;
+ DWORD dacl_off;
+} REG_SEC_DESC;
+
+typedef struct hash_struct {
+ DWORD nk_off;
+ char hash[4];
+} HASH_REC;
+
+#define REG_LF_ID 0x666C
+
+typedef struct lf_struct {
+ WORD LF_ID;
+ WORD key_count;
+ struct hash_struct hr[1]; /* Array of hash records, depending on key_count */
+} LF_HDR;
+
+typedef DWORD VL_TYPE[1]; /* Value list is an array of vk rec offsets */
+
+#define REG_VK_ID 0x6B76
+
+typedef struct vk_struct {
+ WORD VK_ID;
+ WORD nam_len;
+ DWORD dat_len; /* If top-bit set, offset contains the data */
+ DWORD dat_off;
+ DWORD dat_type;
+ WORD flag; /* =1, has name, else no name (=Default). */
+ WORD unk1;
+ char dat_name[1]; /* Name starts here ... */
+} VK_HDR;
+
+#define REG_TYPE_DELETE -1
+#define REG_TYPE_NONE 0
+#define REG_TYPE_REGSZ 1
+#define REG_TYPE_EXPANDSZ 2
+#define REG_TYPE_BIN 3
+#define REG_TYPE_DWORD 4
+#define REG_TYPE_MULTISZ 7
+
+typedef struct _val_str {
+ unsigned int val;
+ const char * str;
+} VAL_STR;
+
+/* A map of sk offsets in the regf to KEY_SEC_DESCs for quick lookup etc */
+typedef struct sk_map_s {
+ int sk_off;
+ KEY_SEC_DESC *key_sec_desc;
+} SK_MAP;
+
+/*
+ * This structure keeps track of the output format of the registry
+ */
+#define REG_OUTBLK_HDR 1
+#define REG_OUTBLK_HBIN 2
+
+typedef struct hbin_blk_s {
+ int type, size;
+ struct hbin_blk_s *next;
+ char *data; /* The data block */
+ unsigned int file_offset; /* Offset in file */
+ unsigned int free_space; /* Amount of free space in block */
+ unsigned int fsp_off; /* Start of free space in block */
+ int complete, stored;
+} HBIN_BLK;
+
+/*
+ * This structure keeps all the registry stuff in one place
+ */
+typedef struct regf_struct_s {
+ int reg_type;
+ char *regfile_name, *outfile_name;
+ int fd;
+ struct stat sbuf;
+ char *base;
+ int modified;
+ NTTIME last_mod_time;
+ REG_KEY *root; /* Root of the tree for this file */
+ int sk_count, sk_map_size;
+ SK_MAP *sk_map;
+ const char *owner_sid_str;
+ SEC_DESC *def_sec_desc;
+ /*
+ * These next pointers point to the blocks used to contain the
+ * keys when we are preparing to write them to a file
+ */
+ HBIN_BLK *blk_head, *blk_tail, *free_space;
+} REGF;
+
+/*
+ * An API for accessing/creating/destroying items above
+ */
+
+/*
+ * Iterate over the keys, depth first, calling a function for each key
+ * and indicating if it is terminal or non-terminal and if it has values.
+ *
+ * In addition, for each value in the list, call a value list function
+ */
+
+typedef int (*key_print_f)(const char *path, char *key_name, char *class_name,
+ int root, int terminal, int values);
+
+typedef int (*val_print_f)(const char *path, char *val_name, int val_type,
+ int data_len, void *data_blk, int terminal,
+ int first, int last);
+
+typedef int (*sec_print_f)(SEC_DESC *sec_desc);
+
+static
+int nt_key_iterator(REGF *regf, REG_KEY *key_tree, int bf, const char *path,
+ key_print_f key_print, sec_print_f sec_print,
+ val_print_f val_print);
+
+static
+int nt_val_list_iterator(REGF *regf, VAL_LIST *val_list, int bf, char *path,
+ int terminal, val_print_f val_print)
+{
+ int i;
+
+ if (!val_list) return 1;
+
+ if (!val_print) return 1;
+
+ for (i=0; i<val_list->val_count; i++) {
+ if (!val_print(path, val_list->vals[i]->name, val_list->vals[i]->data_type,
+ val_list->vals[i]->data_len, val_list->vals[i]->data_blk,
+ terminal,
+ (i == 0),
+ (i == val_list->val_count))) {
+
+ return 0;
+
+ }
+ }
+
+ return 1;
+}
+
+static
+int nt_key_list_iterator(REGF *regf, KEY_LIST *key_list, int bf,
+ const char *path,
+ key_print_f key_print, sec_print_f sec_print,
+ val_print_f val_print)
+{
+ int i;
+
+ if (!key_list) return 1;
+
+ for (i=0; i< key_list->key_count; i++) {
+ if (!nt_key_iterator(regf, key_list->keys[i], bf, path, key_print,
+ sec_print, val_print)) {
+ return 0;
+ }
+ }
+ return 1;
+}
+
+static
+int nt_key_iterator(REGF *regf, REG_KEY *key_tree, int bf, const char *path,
+ key_print_f key_print, sec_print_f sec_print,
+ val_print_f val_print)
+{
+ int path_len = strlen(path);
+ char *new_path;
+
+ if (!regf || !key_tree)
+ return -1;
+
+ /* List the key first, then the values, then the sub-keys */
+
+ if (key_print) {
+
+ if (!(*key_print)(path, key_tree->name,
+ key_tree->class_name,
+ (key_tree->type == REG_ROOT_KEY),
+ (key_tree->sub_keys == NULL),
+ (key_tree->values?(key_tree->values->val_count):0)))
+ return 0;
+ }
+
+ /*
+ * If we have a security print routine, call it
+ * If the security print routine returns false, stop.
+ */
+ if (sec_print) {
+ if (key_tree->security && !(*sec_print)(key_tree->security->sec_desc))
+ return 0;
+ }
+
+ new_path = (char *)malloc(path_len + 1 + strlen(key_tree->name) + 1);
+ if (!new_path) return 0; /* Errors? */
+ new_path[0] = '\0';
+ strcat(new_path, path);
+ strcat(new_path, key_tree->name);
+ strcat(new_path, "\\");
+
+ /*
+ * Now, iterate through the values in the val_list
+ */
+
+ if (key_tree->values &&
+ !nt_val_list_iterator(regf, key_tree->values, bf, new_path,
+ (key_tree->values!=NULL),
+ val_print)) {
+
+ free(new_path);
+ return 0;
+ }
+
+ /*
+ * Now, iterate through the keys in the key list
+ */
+
+ if (key_tree->sub_keys &&
+ !nt_key_list_iterator(regf, key_tree->sub_keys, bf, new_path, key_print,
+ sec_print, val_print)) {
+ free(new_path);
+ return 0;
+ }
+
+ free(new_path);
+ return 1;
+}
+
+static
+REG_KEY *nt_find_key_by_name(REG_KEY *tree, char *key);
+
+/*
+ * Find key by name in a list ...
+ * Take the first component and search for that in the list
+ */
+static
+REG_KEY *nt_find_key_in_list_by_name(KEY_LIST *list, char *key)
+{
+ int i;
+ REG_KEY *res = NULL;
+
+ if (!list || !key || !*key) return NULL;
+
+ for (i = 0; i < list->key_count; i++)
+ if ((res = nt_find_key_by_name(list->keys[i], key)))
+ return res;
+
+ return NULL;
+}
+
+/*
+ * Find key by name in a tree ... We will assume absolute names here, but we
+ * need the root of the tree ...
+ */
+static
+REG_KEY *nt_find_key_by_name(REG_KEY *tree, char *key)
+{
+ char *lname = NULL, *c1, *c2;
+ REG_KEY *tmp;
+
+ if (!tree || !key || !*key) return NULL;
+
+ lname = strdup(key);
+ if (!lname) return NULL;
+
+ /*
+ * Make sure that the first component is correct ...
+ */
+ c1 = lname;
+ c2 = strchr(c1, '\\');
+ if (c2) { /* Split here ... */
+ *c2 = 0;
+ c2++;
+ }
+ if (strcmp(c1, tree->name) != 0) goto error;
+
+ if (c2) {
+ tmp = nt_find_key_in_list_by_name(tree->sub_keys, c2);
+ free(lname);
+ return tmp;
+ }
+ else {
+ if (lname) free(lname);
+ return tree;
+ }
+ error:
+ if (lname) free(lname);
+ return NULL;
+}
+
+/* Make, delete keys */
+static
+int nt_delete_val_key(VAL_KEY *val_key)
+{
+
+ if (val_key) {
+ if (val_key->name) free(val_key->name);
+ if (val_key->data_blk) free(val_key->data_blk);
+ free(val_key);
+ };
+ return 1;
+}
+
+static
+int nt_delete_val_list(VAL_LIST *vl)
+{
+ int i;
+
+ if (vl) {
+ for (i=0; i<vl->val_count; i++)
+ nt_delete_val_key(vl->vals[i]);
+ free(vl);
+ }
+ return 1;
+}
+
+static
+int nt_delete_reg_key(REG_KEY *key, int delete_name);
+
+static
+int nt_delete_key_list(KEY_LIST *key_list, int delete_name)
+{
+ int i;
+
+ if (key_list) {
+ for (i=0; i<key_list->key_count; i++)
+ nt_delete_reg_key(key_list->keys[i], False);
+ free(key_list);
+ }
+ return 1;
+}
+
+/*
+ * Find the key, and if it exists, delete it ...
+ */
+static
+int nt_delete_key_by_name(REGF *regf, char *name)
+{
+ REG_KEY *key;
+
+ if (!name || !*name) return 0;
+
+ key = nt_find_key_by_name(regf->root, name);
+
+ if (key) {
+ if (key == regf->root) regf->root = NULL;
+ return nt_delete_reg_key(key, True);
+ }
+
+ return 0;
+
+}
+
+static
+int nt_delete_sid(sid_t *sid)
+{
+
+ if (sid) free(sid);
+ return 1;
+
+}
+
+static
+int nt_delete_ace(ACE *ace)
+{
+
+ if (ace) {
+ nt_delete_sid(ace->trustee);
+ free(ace);
+ }
+ return 1;
+
+}
+
+static
+int nt_delete_acl(ACL *acl)
+{
+
+ if (acl) {
+ int i;
+
+ for (i=0; i<acl->num_aces; i++)
+ nt_delete_ace(acl->aces[i]);
+
+ free(acl);
+ }
+ return 1;
+}
+
+static
+int nt_delete_sec_desc(SEC_DESC *sec_desc)
+{
+
+ if (sec_desc) {
+
+ nt_delete_sid(sec_desc->owner);
+ nt_delete_sid(sec_desc->group);
+ nt_delete_acl(sec_desc->sacl);
+ nt_delete_acl(sec_desc->dacl);
+ free(sec_desc);
+
+ }
+ return 1;
+}
+
+static
+int nt_delete_key_sec_desc(KEY_SEC_DESC *key_sec_desc)
+{
+
+ if (key_sec_desc) {
+ key_sec_desc->ref_cnt--;
+ if (key_sec_desc->ref_cnt<=0) {
+ /*
+ * There should always be a next and prev, even if they point to us
+ */
+ key_sec_desc->next->prev = key_sec_desc->prev;
+ key_sec_desc->prev->next = key_sec_desc->next;
+ nt_delete_sec_desc(key_sec_desc->sec_desc);
+ }
+ }
+ return 1;
+}
+
+static
+int nt_delete_reg_key(REG_KEY *key, int delete_name)
+{
+
+ if (key) {
+ if (key->name) free(key->name);
+ if (key->class_name) free(key->class_name);
+
+ /*
+ * We will delete the owner if we are not the root and told to ...
+ */
+
+ if (key->owner && key->owner->sub_keys && delete_name) {
+ REG_KEY *own;
+ KEY_LIST *kl;
+ int i;
+ /* Find our owner, look in keylist for us and shuffle up */
+ /* Perhaps should be a function */
+
+ own = key->owner;
+ kl = own->sub_keys;
+
+ for (i=0; i < kl->key_count && kl->keys[i] != key ; i++) {
+ /* Just find the entry ... */
+ }
+
+ if (i == kl->key_count) {
+ fprintf(stderr, "Bad data structure. Key not found in key list of owner\n");
+ }
+ else {
+ int j;
+
+ /*
+ * Shuffle up. Works for the last one also
+ */
+ for (j = i + 1; j < kl->key_count; j++) {
+ kl->keys[j - 1] = kl->keys[j];
+ }
+
+ kl->key_count--;
+ }
+ }
+
+ if (key->sub_keys) nt_delete_key_list(key->sub_keys, False);
+ if (key->values) nt_delete_val_list(key->values);
+ if (key->security) nt_delete_key_sec_desc(key->security);
+ free(key);
+ }
+ return 1;
+}
+
+/*
+ * Convert a string to a value ...
+ * FIXME: Error handling and convert this at command parse time ...
+ */
+static
+void *str_to_val(int type, char *val, int *len)
+{
+ unsigned int *dwordp = NULL;
+
+ if (!len || !val) return NULL;
+
+ switch (type) {
+ case REG_TYPE_REGSZ:
+ *len = strlen(val);
+ return (void *)val;
+
+ case REG_TYPE_DWORD:
+ dwordp = (unsigned int *)malloc(sizeof(unsigned int));
+ if (!dwordp) return NULL;
+ /* Allow for ddddd and 0xhhhhh and 0ooooo */
+ if (strncmp(val, "0x", 2) == 0 || strncmp(val, "0X", 2) == 0) {
+ sscanf(&val[2], "%X", dwordp);
+ }
+ else if (*val == '0') {
+ sscanf(&val[1], "%o", dwordp);
+ }
+ else {
+ sscanf(val, "%d", dwordp);
+ }
+ *len = sizeof(unsigned int);
+ return (void *)dwordp;
+
+ /* FIXME: Implement more of these */
+
+ default:
+ return NULL;
+ }
+
+ return NULL;
+}
+
+/*
+ * Add a value to the key specified ... We have to parse the value some more
+ * based on the type to get it in the correct internal form
+ * An empty name will be converted to "<No Name>" before here
+ * Hmmm, maybe not. has_name is for that
+ */
+static
+VAL_KEY *nt_add_reg_value(REG_KEY *key, char *name, int type, char *value)
+{
+ int i;
+ VAL_KEY *tmp = NULL;
+
+ if (!key || !key->values || !name || !*name) return NULL;
+
+ assert(type != REG_TYPE_DELETE); /* We never process deletes here */
+
+ for (i = 0; i < key->values->val_count; i++) {
+ if ((!key->values->vals[i]->has_name && !*name) ||
+ (key->values->vals[i]->has_name &&
+ strcmp(name, key->values->vals[i]->name) == 0)){ /* Change the value */
+ free(key->values->vals[i]->data_blk);
+ key->values->vals[i]->data_blk = str_to_val(type, value, &
+ key->values->vals[i]->data_len);
+ return key->values->vals[i];
+ }
+ }
+
+ /*
+ * If we get here, the name was not found, so insert it
+ */
+
+ tmp = (VAL_KEY *)malloc(sizeof(VAL_KEY));
+ if (!tmp) goto error;
+
+ memset(tmp, 0, sizeof(VAL_KEY));
+ tmp->name = strdup(name);
+ tmp->has_name = True;
+ if (!tmp->name) goto error;
+ tmp->data_type = type;
+ tmp->data_blk = str_to_val(type, value, &tmp->data_len);
+
+ /* Now, add to val list */
+
+ if (key->values->val_count >= key->values->max_vals) {
+ /*
+ * Allocate some more space
+ */
+
+ if ((key->values = (VAL_LIST *)realloc(key->values, sizeof(VAL_LIST) +
+ key->values->val_count - 1 +
+ REG_KEY_LIST_SIZE))) {
+ key->values->max_vals += REG_KEY_LIST_SIZE;
+ }
+ else goto error;
+ }
+
+ i = key->values->val_count;
+ key->values->val_count++;
+ key->values->vals[i] = tmp;
+ return tmp;
+
+ error:
+ if (tmp) nt_delete_val_key(tmp);
+ return NULL;
+}
+
+/*
+ * Delete a value. We return the value and let the caller deal with it.
+ */
+static
+VAL_KEY *nt_delete_reg_value(REG_KEY *key, char *name)
+{
+ int i, j;
+
+ if (!key || !key->values || !name || !*name) return NULL;
+
+ /* FIXME: Allow empty value name */
+ for (i = 0; i< key->values->val_count; i++) {
+ if ((!key->values->vals[i]->has_name && !*name) ||
+ (key->values->vals[i]->has_name &&
+ strcmp(name, key->values->vals[i]->name) == 0)) {
+ VAL_KEY *val;
+
+ val = key->values->vals[i];
+
+ /* Shuffle down */
+ for (j = i + 1; j < key->values->val_count; j++)
+ key->values->vals[j - 1] = key->values->vals[j];
+
+ key->values->val_count--;
+
+ return val;
+ }
+ }
+ return NULL;
+}
+
+/*
+ * Add a key to the tree ... We walk down the components matching until
+ * we don't find any. There must be a match on the first component ...
+ * We return the key structure for the final component as that is
+ * often where we want to add values ...
+ */
+
+/*
+ * Convert a string of the form S-1-5-x[-y-z-r] to a SID
+ */
+static
+int sid_string_to_sid(sid_t **sid, const char *sid_str)
+{
+ int i = 0, auth;
+ const char *lstr;
+
+ *sid = (sid_t *)malloc(sizeof(sid_t));
+ if (!*sid) return 0;
+
+ memset(*sid, 0, sizeof(sid_t));
+
+ if (strncmp(sid_str, "S-1-5", 5)) {
+ fprintf(stderr, "Does not conform to S-1-5...: %s\n", sid_str);
+ return 0;
+ }
+
+ /* We only allow strings of form S-1-5... */
+
+ (*sid)->ver = 1;
+ (*sid)->auth[5] = 5;
+
+ lstr = sid_str + 5;
+
+ while (1) {
+ if (!lstr || !lstr[0] || sscanf(lstr, "-%u", &auth) == 0) {
+ if (i < 1) {
+ fprintf(stderr, "Not of form -d-d...: %s, %u\n", lstr, i);
+ return 0;
+ }
+ (*sid)->auths=i;
+ return 1;
+ }
+
+ (*sid)->sub_auths[i] = auth;
+ i++;
+ lstr = strchr(lstr + 1, '-');
+ }
+
+ /*return 1; */ /* Not Reached ... */
+}
+
+/*
+ * Create an ACE
+ */
+static
+ACE *nt_create_ace(int type, int flags, unsigned int perms, const char *sid)
+{
+ ACE *ace;
+
+ ace = (ACE *)malloc(sizeof(ACE));
+ if (!ace) goto error;
+ ace->type = type;
+ ace->flags = flags;
+ ace->perms = perms;
+ if (!sid_string_to_sid(&ace->trustee, sid))
+ goto error;
+ return ace;
+
+ error:
+ if (ace) nt_delete_ace(ace);
+ return NULL;
+}
+
+/*
+ * Create a default ACL
+ */
+static
+ACL *nt_create_default_acl(REGF *regf)
+{
+ ACL *acl;
+
+ acl = (ACL *)malloc(sizeof(ACL) + 7*sizeof(ACE *));
+ if (!acl) goto error;
+
+ acl->rev = 2;
+ acl->refcnt = 1;
+ acl->num_aces = 8;
+
+ acl->aces[0] = nt_create_ace(0x00, 0x0, 0xF003F, regf->owner_sid_str);
+ if (!acl->aces[0]) goto error;
+ acl->aces[1] = nt_create_ace(0x00, 0x0, 0xF003F, "S-1-5-18");
+ if (!acl->aces[1]) goto error;
+ acl->aces[2] = nt_create_ace(0x00, 0x0, 0xF003F, "S-1-5-32-544");
+ if (!acl->aces[2]) goto error;
+ acl->aces[3] = nt_create_ace(0x00, 0x0, 0x20019, "S-1-5-12");
+ if (!acl->aces[3]) goto error;
+ acl->aces[4] = nt_create_ace(0x00, 0x0B, 0x10000000, regf->owner_sid_str);
+ if (!acl->aces[4]) goto error;
+ acl->aces[5] = nt_create_ace(0x00, 0x0B, 0x10000000, "S-1-5-18");
+ if (!acl->aces[5]) goto error;
+ acl->aces[6] = nt_create_ace(0x00, 0x0B, 0x10000000, "S-1-5-32-544");
+ if (!acl->aces[6]) goto error;
+ acl->aces[7] = nt_create_ace(0x00, 0x0B, 0x80000000, "S-1-5-12");
+ if (!acl->aces[7]) goto error;
+ return acl;
+
+ error:
+ if (acl) nt_delete_acl(acl);
+ return NULL;
+}
+
+/*
+ * Create a default security descriptor. We pull in things from env
+ * if need be
+ */
+static
+SEC_DESC *nt_create_def_sec_desc(REGF *regf)
+{
+ SEC_DESC *tmp;
+
+ tmp = (SEC_DESC *)malloc(sizeof(SEC_DESC));
+ if (!tmp) return NULL;
+
+ tmp->rev = 1;
+ tmp->type = 0x8004;
+ if (!sid_string_to_sid(&tmp->owner, "S-1-5-32-544")) goto error;
+ if (!sid_string_to_sid(&tmp->group, "S-1-5-18")) goto error;
+ tmp->sacl = NULL;
+ tmp->dacl = nt_create_default_acl(regf);
+
+ return tmp;
+
+ error:
+ if (tmp) nt_delete_sec_desc(tmp);
+ return NULL;
+}
+
+/*
+ * We will implement inheritence that is based on what the parent's SEC_DESC
+ * says, but the Owner and Group SIDs can be overwridden from the command line
+ * and additional ACEs can be applied from the command line etc.
+ */
+static
+KEY_SEC_DESC *nt_inherit_security(REG_KEY *key)
+{
+
+ if (!key) return NULL;
+ return key->security;
+}
+
+/*
+ * Create an initial security descriptor and init other structures, if needed
+ * We assume that the initial security stuff is empty ...
+ */
+static
+KEY_SEC_DESC *nt_create_init_sec(REGF *regf)
+{
+ KEY_SEC_DESC *tsec = NULL;
+
+ tsec = (KEY_SEC_DESC *)malloc(sizeof(KEY_SEC_DESC));
+ if (!tsec) return NULL;
+
+ tsec->ref_cnt = 1;
+ tsec->state = SEC_DESC_NBK;
+ tsec->offset = 0;
+
+ tsec->sec_desc = regf->def_sec_desc;
+
+ return tsec;
+}
+
+/*
+ * Add a sub-key
+ */
+static
+REG_KEY *nt_add_reg_key_list(REGF *regf, REG_KEY *key, char * name, int create)
+{
+ int i;
+ REG_KEY *ret = NULL, *tmp = NULL;
+ KEY_LIST *list;
+ char *lname, *c1, *c2;
+
+ if (!key || !name || !*name) return NULL;
+
+ list = key->sub_keys;
+ if (!list) { /* Create an empty list */
+
+ list = (KEY_LIST *)malloc(sizeof(KEY_LIST) + (REG_KEY_LIST_SIZE - 1) * sizeof(REG_KEY *));
+ list->key_count = 0;
+ list->max_keys = REG_KEY_LIST_SIZE;
+
+ }
+
+ lname = strdup(name);
+ if (!lname) return NULL;
+
+ c1 = lname;
+ c2 = strchr(c1, '\\');
+ if (c2) { /* Split here ... */
+ *c2 = 0;
+ c2++;
+ }
+
+ for (i = 0; i < list->key_count; i++) {
+ if (strcmp(list->keys[i]->name, c1) == 0) {
+ ret = nt_add_reg_key_list(regf, list->keys[i], c2, create);
+ free(lname);
+ return ret;
+ }
+ }
+
+ /*
+ * If we reach here we could not find the the first component
+ * so create it ...
+ */
+
+ if (list->key_count < list->max_keys){
+ list->key_count++;
+ }
+ else { /* Create more space in the list ... */
+ if (!(list = (KEY_LIST *)realloc(list, sizeof(KEY_LIST) +
+ (list->max_keys + REG_KEY_LIST_SIZE - 1)
+ * sizeof(REG_KEY *))))
+ goto error;
+
+ list->max_keys += REG_KEY_LIST_SIZE;
+ list->key_count++;
+ }
+
+ /*
+ * add the new key at the new slot
+ * FIXME: Sort the list someday
+ */
+
+ /*
+ * We want to create the key, and then do the rest
+ */
+
+ tmp = (REG_KEY *)malloc(sizeof(REG_KEY));
+
+ memset(tmp, 0, sizeof(REG_KEY));
+
+ tmp->name = strdup(c1);
+ if (!tmp->name) goto error;
+ tmp->owner = key;
+ tmp->type = REG_SUB_KEY;
+ /*
+ * Next, pull security from the parent, but override with
+ * anything passed in on the command line
+ */
+ tmp->security = nt_inherit_security(key);
+
+ list->keys[list->key_count - 1] = tmp;
+
+ if (c2) {
+ ret = nt_add_reg_key_list(regf, key, c2, True);
+ }
+
+ if (lname) free(lname);
+
+ return ret;
+
+ error:
+ if (tmp) free(tmp);
+ if (lname) free(lname);
+ return NULL;
+}
+
+/*
+ * This routine only adds a key from the root down.
+ * It calls helper functions to handle sub-key lists and sub-keys
+ */
+static
+REG_KEY *nt_add_reg_key(REGF *regf, char *name, int create)
+{
+ char *lname = NULL, *c1, *c2;
+ REG_KEY * tmp = NULL;
+
+ /*
+ * Look until we hit the first component that does not exist, and
+ * then add from there. However, if the first component does not
+ * match and the path we are given is the root, then it must match
+ */
+ if (!regf || !name || !*name) return NULL;
+
+ lname = strdup(name);
+ if (!lname) return NULL;
+
+ c1 = lname;
+ c2 = strchr(c1, '\\');
+ if (c2) { /* Split here ... */
+ *c2 = 0;
+ c2++;
+ }
+
+ /*
+ * If the root does not exist, create it and make it equal to the
+ * first component ...
+ */
+
+ if (!regf->root) {
+
+ tmp = (REG_KEY *)malloc(sizeof(REG_KEY));
+ if (!tmp) goto error;
+ memset(tmp, 0, sizeof(REG_KEY));
+ tmp->name = strdup(c1);
+ if (!tmp->name) goto error;
+ tmp->security = nt_create_init_sec(regf);
+ if (!tmp->security) goto error;
+ regf->root = tmp;
+
+ }
+ else {
+ /*
+ * If we don't match, then we have to return error ...
+ * If we do match on this component, check the next one in the
+ * list, and if not found, add it ... short circuit, add all the
+ * way down
+ */
+
+ if (strcmp(c1, regf->root->name) != 0)
+ goto error;
+ }
+
+ tmp = nt_add_reg_key_list(regf, regf->root, c2, True);
+ free(lname);
+ return tmp;
+
+ error:
+ if (tmp) free(tmp);
+ if (lname) free(lname);
+ return NULL;
+}
+
+/*
+ * Load and unload a registry file.
+ *
+ * Load, loads it into memory as a tree, while unload sealizes/flattens it
+ */
+
+/*
+ * Get the starting record for NT Registry file
+ */
+
+/*
+ * Where we keep all the regf stuff for one registry.
+ * This is the structure that we use to tie the in memory tree etc
+ * together. By keeping separate structs, we can operate on different
+ * registries at the same time.
+ * Currently, the SK_MAP is an array of mapping structure.
+ * Since we only need this on input and output, we fill in the structure
+ * as we go on input. On output, we know how many SK items we have, so
+ * we can allocate the structure as we need to.
+ * If you add stuff here that is dynamically allocated, add the
+ * appropriate free statements below.
+ */
+
+#define REGF_REGTYPE_NONE 0
+#define REGF_REGTYPE_NT 1
+#define REGF_REGTYPE_W9X 2
+
+#define TTTONTTIME(r, t1, t2) (r)->last_mod_time.low = (t1); \
+ (r)->last_mod_time.high = (t2);
+
+#define REGF_HDR_BLKSIZ 0x1000
+
+#define OFF(f) ((f) + REGF_HDR_BLKSIZ + 4)
+#define LOCN(base, f) ((base) + OFF(f))
+
+const VAL_STR reg_type_names[] = {
+ { REG_TYPE_REGSZ, "REG_SZ" },
+ { REG_TYPE_EXPANDSZ, "REG_EXPAND_SZ" },
+ { REG_TYPE_BIN, "REG_BIN" },
+ { REG_TYPE_DWORD, "REG_DWORD" },
+ { REG_TYPE_MULTISZ, "REG_MULTI_SZ" },
+ { 0, NULL },
+};
+
+static
+const char *val_to_str(unsigned int val, const VAL_STR *val_array)
+{
+ int i = 0;
+
+ if (!val_array) return NULL;
+
+ while (val_array[i].val && val_array[i].str) {
+
+ if (val_array[i].val == val) return val_array[i].str;
+ i++;
+
+ }
+
+ return NULL;
+
+}
+
+/*
+ * Convert from UniCode to Ascii ... Does not take into account other lang
+ * Restrict by ascii_max if > 0
+ */
+static
+int uni_to_ascii(unsigned char *uni, unsigned char *ascii, int ascii_max,
+ int uni_max)
+{
+ int i = 0;
+
+ while (i < ascii_max && !(!uni[i*2] && !uni[i*2+1])) {
+ if (uni_max > 0 && (i*2) >= uni_max) break;
+ ascii[i] = uni[i*2];
+ i++;
+
+ }
+
+ ascii[i] = '\0';
+
+ return i;
+}
+
+/*
+ * Convert a data value to a string for display
+ */
+static
+int data_to_ascii(unsigned char *datap, int len, int type, char *ascii, int ascii_max)
+{
+ unsigned char *asciip;
+ int i;
+
+ switch (type) {
+ case REG_TYPE_REGSZ:
+ if (verbose) fprintf(stderr, "Len: %d\n", len);
+ /* FIXME. This has to be fixed. It has to be UNICODE */
+ return uni_to_ascii(datap, ascii, len, ascii_max);
+ break; /*NOTREACHED*/
+
+ case REG_TYPE_EXPANDSZ:
+ return uni_to_ascii(datap, ascii, len, ascii_max);
+ break;
+
+ case REG_TYPE_BIN:
+ asciip = ascii;
+ for (i=0; (i<len)&&(i+1)*3<ascii_max; i++) {
+ int str_rem = ascii_max - ((int)asciip - (int)ascii);
+ asciip += snprintf(asciip, str_rem, "%02x", *(unsigned char *)(datap+i));
+ if (i < len && str_rem > 0)
+ *asciip = ' '; asciip++;
+ }
+ *asciip = '\0';
+ return ((int)asciip - (int)ascii);
+ break;
+
+ case REG_TYPE_DWORD:
+ if (*(int *)datap == 0)
+ return snprintf(ascii, ascii_max, "0");
+ else
+ return snprintf(ascii, ascii_max, "0x%x", *(int *)datap);
+ break;
+
+ case REG_TYPE_MULTISZ:
+
+ break;
+
+ default:
+ return 0;
+ break;
+ }
+
+ return len;
+
+}
+
+static
+REG_KEY *nt_get_key_tree(REGF *regf, NK_HDR *nk_hdr, int size, REG_KEY *parent);
+
+static
+int nt_set_regf_input_file(REGF *regf, char *filename)
+{
+ return ((regf->regfile_name = strdup(filename)) != NULL);
+}
+
+static
+int nt_set_regf_output_file(REGF *regf, char *filename)
+{
+ return ((regf->outfile_name = strdup(filename)) != NULL);
+}
+
+/* Create a regf structure and init it */
+
+static
+REGF *nt_create_regf(void)
+{
+ REGF *tmp = (REGF *)malloc(sizeof(REGF));
+ if (!tmp) return tmp;
+ memset(tmp, 0, sizeof(REGF));
+ tmp->owner_sid_str = def_owner_sid_str;
+ return tmp;
+}
+
+/* Free all the bits and pieces ... Assumes regf was malloc'd */
+/* If you add stuff to REGF, add the relevant free bits here */
+static
+int nt_free_regf(REGF *regf)
+{
+ if (!regf) return 0;
+
+ if (regf->regfile_name) free(regf->regfile_name);
+ if (regf->outfile_name) free(regf->outfile_name);
+
+ nt_delete_reg_key(regf->root, False); /* Free the tree */
+ free(regf->sk_map);
+ regf->sk_count = regf->sk_map_size = 0;
+
+ free(regf);
+
+ return 1;
+}
+
+/* Get the header of the registry. Return a pointer to the structure
+ * If the mmap'd area has not been allocated, then mmap the input file
+ */
+static
+REGF_HDR *nt_get_regf_hdr(REGF *regf)
+{
+ if (!regf)
+ return NULL; /* What about errors */
+
+ if (!regf->regfile_name)
+ return NULL; /* What about errors */
+
+ if (!regf->base) { /* Try to mmap etc the file */
+
+ if ((regf->fd = open(regf->regfile_name, O_RDONLY, 0000)) <0) {
+ return NULL; /* What about errors? */
+ }
+
+ if (fstat(regf->fd, &regf->sbuf) < 0) {
+ return NULL;
+ }
+
+ regf->base = mmap(0, regf->sbuf.st_size, PROT_READ, MAP_SHARED, regf->fd, 0);
+
+ if ((int)regf->base == 1) {
+ fprintf(stderr, "Could not mmap file: %s, %s\n", regf->regfile_name,
+ strerror(errno));
+ return NULL;
+ }
+ }
+
+ /*
+ * At this point, regf->base != NULL, and we should be able to read the
+ * header
+ */
+
+ assert(regf->base != NULL);
+
+ return (REGF_HDR *)regf->base;
+}
+
+/*
+ * Validate a regf header
+ * For now, do nothing, but we should check the checksum
+ */
+static
+int valid_regf_hdr(REGF_HDR *regf_hdr)
+{
+ if (!regf_hdr) return 0;
+
+ return 1;
+}
+
+/*
+ * Process an SK header ...
+ * Every time we see a new one, add it to the map. Otherwise, just look it up.
+ * We will do a simple linear search for the moment, since many KEYs have the
+ * same security descriptor.
+ * We allocate the map in increments of 10 entries.
+ */
+
+/*
+ * Create a new entry in the map, and increase the size of the map if needed
+ */
+static
+SK_MAP *alloc_sk_map_entry(REGF *regf, KEY_SEC_DESC *tmp, int sk_off)
+{
+ if (!regf->sk_map) { /* Allocate a block of 10 */
+ regf->sk_map = (SK_MAP *)malloc(sizeof(SK_MAP) * 10);
+ if (!regf->sk_map) {
+ free(tmp);
+ return NULL;
+ }
+ regf->sk_map_size = 10;
+ regf->sk_count = 1;
+ (regf->sk_map)[0].sk_off = sk_off;
+ (regf->sk_map)[0].key_sec_desc = tmp;
+ }
+ else { /* Simply allocate a new slot, unless we have to expand the list */
+ int ndx = regf->sk_count;
+ if (regf->sk_count >= regf->sk_map_size) {
+ regf->sk_map = (SK_MAP *)realloc(regf->sk_map,
+ (regf->sk_map_size + 10)*sizeof(SK_MAP));
+ if (!regf->sk_map) {
+ free(tmp);
+ return NULL;
+ }
+ /*
+ * ndx already points at the first entry of the new block
+ */
+ regf->sk_map_size += 10;
+ }
+ (regf->sk_map)[ndx].sk_off = sk_off;
+ (regf->sk_map)[ndx].key_sec_desc = tmp;
+ regf->sk_count++;
+ }
+ return regf->sk_map;
+}
+
+/*
+ * Search for a KEY_SEC_DESC in the sk_map, but don't create one if not
+ * found
+ */
+static
+KEY_SEC_DESC *lookup_sec_key(SK_MAP *sk_map, int count, int sk_off)
+{
+ int i;
+
+ if (!sk_map) return NULL;
+
+ for (i = 0; i < count; i++) {
+
+ if (sk_map[i].sk_off == sk_off)
+ return sk_map[i].key_sec_desc;
+
+ }
+
+ return NULL;
+
+}
+
+/*
+ * Allocate a KEY_SEC_DESC if we can't find one in the map
+ */
+static
+KEY_SEC_DESC *lookup_create_sec_key(REGF *regf, SK_MAP *sk_map, int sk_off)
+{
+ KEY_SEC_DESC *tmp = lookup_sec_key(regf->sk_map, regf->sk_count, sk_off);
+
+ if (tmp) {
+ return tmp;
+ }
+ else { /* Allocate a new one */
+ tmp = (KEY_SEC_DESC *)malloc(sizeof(KEY_SEC_DESC));
+ if (!tmp) {
+ return NULL;
+ }
+ memset(tmp, 0, sizeof(KEY_SEC_DESC)); /* Neatly sets offset to 0 */
+ tmp->state = SEC_DESC_RES;
+ if (!alloc_sk_map_entry(regf, tmp, sk_off)) {
+ return NULL;
+ }
+ return tmp;
+ }
+}
+
+/*
+ * Allocate storage and duplicate a SID
+ * We could allocate the SID to be only the size needed, but I am too lazy.
+ */
+static
+sid_t *dup_sid(sid_t *sid)
+{
+ sid_t *tmp = (sid_t *)malloc(sizeof(sid_t));
+ int i;
+
+ if (!tmp) return NULL;
+ tmp->ver = sid->ver;
+ tmp->auths = sid->auths;
+ for (i=0; i<6; i++) {
+ tmp->auth[i] = sid->auth[i];
+ }
+ for (i=0; i<tmp->auths&&i<MAXSUBAUTHS; i++) {
+ tmp->sub_auths[i] = sid->sub_auths[i];
+ }
+ return tmp;
+}
+
+/*
+ * Allocate space for an ACE and duplicate the registry encoded one passed in
+ */
+static
+ACE *dup_ace(REG_ACE *ace)
+{
+ ACE *tmp = NULL;
+
+ tmp = (ACE *)malloc(sizeof(ACE));
+
+ if (!tmp) return NULL;
+
+ tmp->type = CVAL(&ace->type);
+ tmp->flags = CVAL(&ace->flags);
+ tmp->perms = IVAL(&ace->perms);
+ tmp->trustee = dup_sid(&ace->trustee);
+ return tmp;
+}
+
+/*
+ * Allocate space for an ACL and duplicate the registry encoded one passed in
+ */
+static
+ACL *dup_acl(REG_ACL *acl)
+{
+ ACL *tmp = NULL;
+ REG_ACE* ace;
+ int i, num_aces;
+
+ num_aces = IVAL(&acl->num_aces);
+
+ tmp = (ACL *)malloc(sizeof(ACL) + (num_aces - 1)*sizeof(ACE *));
+ if (!tmp) return NULL;
+
+ tmp->num_aces = num_aces;
+ tmp->refcnt = 1;
+ tmp->rev = SVAL(&acl->rev);
+ if (verbose) fprintf(stdout, "ACL: refcnt: %u, rev: %u\n", tmp->refcnt,
+ tmp->rev);
+ ace = (REG_ACE *)&acl->aces;
+ for (i=0; i<num_aces; i++) {
+ tmp->aces[i] = dup_ace(ace);
+ ace = (REG_ACE *)((char *)ace + SVAL(&ace->length));
+ /* XXX: FIXME, should handle malloc errors */
+ }
+
+ return tmp;
+}
+
+static
+SEC_DESC *process_sec_desc(REGF *regf, REG_SEC_DESC *sec_desc)
+{
+ SEC_DESC *tmp = NULL;
+
+ tmp = (SEC_DESC *)malloc(sizeof(SEC_DESC));
+
+ if (!tmp) {
+ return NULL;
+ }
+
+ tmp->rev = SVAL(&sec_desc->rev);
+ tmp->type = SVAL(&sec_desc->type);
+ if (verbose) fprintf(stdout, "SEC_DESC Rev: %0X, Type: %0X\n",
+ tmp->rev, tmp->type);
+ if (verbose) fprintf(stdout, "SEC_DESC Owner Off: %0X\n",
+ IVAL(&sec_desc->owner_off));
+ if (verbose) fprintf(stdout, "SEC_DESC Group Off: %0X\n",
+ IVAL(&sec_desc->group_off));
+ if (verbose) fprintf(stdout, "SEC_DESC DACL Off: %0X\n",
+ IVAL(&sec_desc->dacl_off));
+ tmp->owner = dup_sid((sid_t *)((char *)sec_desc + IVAL(&sec_desc->owner_off)));
+ if (!tmp->owner) {
+ free(tmp);
+ return NULL;
+ }
+ tmp->group = dup_sid((sid_t *)((char *)sec_desc + IVAL(&sec_desc->group_off)));
+ if (!tmp->group) {
+ free(tmp);
+ return NULL;
+ }
+
+ /* Now pick up the SACL and DACL */
+
+ if (sec_desc->sacl_off)
+ tmp->sacl = dup_acl((REG_ACL *)((char *)sec_desc + IVAL(&sec_desc->sacl_off)));
+ else
+ tmp->sacl = NULL;
+
+ if (sec_desc->dacl_off)
+ tmp->dacl = dup_acl((REG_ACL *)((char *)sec_desc + IVAL(&sec_desc->dacl_off)));
+ else
+ tmp->dacl = NULL;
+
+ return tmp;
+}
+
+static
+KEY_SEC_DESC *process_sk(REGF *regf, SK_HDR *sk_hdr, int sk_off, int size)
+{
+ KEY_SEC_DESC *tmp = NULL;
+ int sk_next_off, sk_prev_off, sk_size;
+ REG_SEC_DESC *sec_desc;
+
+ if (!sk_hdr) return NULL;
+
+ if (SVAL(&sk_hdr->SK_ID) != REG_SK_ID) {
+ fprintf(stderr, "Unrecognized SK Header ID: %08X, %s\n", (int)sk_hdr,
+ regf->regfile_name);
+ return NULL;
+ }
+
+ if (-size < (sk_size = IVAL(&sk_hdr->rec_size))) {
+ fprintf(stderr, "Incorrect SK record size: %d vs %d. %s\n",
+ -size, sk_size, regf->regfile_name);
+ return NULL;
+ }
+
+ /*
+ * Now, we need to look up the SK Record in the map, and return it
+ * Since the map contains the SK_OFF mapped to KEY_SEC_DESC, we can
+ * use that
+ */
+
+ if (regf->sk_map &&
+ ((tmp = lookup_sec_key(regf->sk_map, regf->sk_count, sk_off)) != NULL)
+ && (tmp->state == SEC_DESC_OCU)) {
+ tmp->ref_cnt++;
+ return tmp;
+ }
+
+ /* Here, we have an item in the map that has been reserved, or tmp==NULL. */
+
+ assert(tmp == NULL || (tmp && tmp->state != SEC_DESC_NON));
+
+ /*
+ * Now, allocate a KEY_SEC_DESC, and parse the structure here, and add the
+ * new KEY_SEC_DESC to the mapping structure, since the offset supplied is
+ * the actual offset of structure. The same offset will be used by
+ * all future references to this structure
+ * We could put all this unpleasantness in a function.
+ */
+
+ if (!tmp) {
+ tmp = (KEY_SEC_DESC *)malloc(sizeof(KEY_SEC_DESC));
+ if (!tmp) return NULL;
+ memset(tmp, 0, sizeof(KEY_SEC_DESC));
+
+ /*
+ * Allocate an entry in the SK_MAP ...
+ * We don't need to free tmp, because that is done for us if the
+ * sm_map entry can't be expanded when we need more space in the map.
+ */
+
+ if (!alloc_sk_map_entry(regf, tmp, sk_off)) {
+ return NULL;
+ }
+ }
+
+ tmp->ref_cnt++;
+ tmp->state = SEC_DESC_OCU;
+
+ /*
+ * Now, process the actual sec desc and plug the values in
+ */
+
+ sec_desc = (REG_SEC_DESC *)&sk_hdr->sec_desc[0];
+ tmp->sec_desc = process_sec_desc(regf, sec_desc);
+
+ /*
+ * Now forward and back links. Here we allocate an entry in the sk_map
+ * if it does not exist, and mark it reserved
+ */
+
+ sk_prev_off = IVAL(&sk_hdr->prev_off);
+ tmp->prev = lookup_create_sec_key(regf, regf->sk_map, sk_prev_off);
+ assert(tmp->prev != NULL);
+ sk_next_off = IVAL(&sk_hdr->next_off);
+ tmp->next = lookup_create_sec_key(regf, regf->sk_map, sk_next_off);
+ assert(tmp->next != NULL);
+
+ return tmp;
+}
+
+/*
+ * Process a VK header and return a value
+ */
+static
+VAL_KEY *process_vk(REGF *regf, VK_HDR *vk_hdr, int size)
+{
+ char val_name[1024];
+ int nam_len, dat_len, flag, dat_type, dat_off, vk_id;
+ const char *val_type;
+ VAL_KEY *tmp = NULL;
+
+ if (!vk_hdr) return NULL;
+
+ if ((vk_id = SVAL(&vk_hdr->VK_ID)) != REG_VK_ID) {
+ fprintf(stderr, "Unrecognized VK header ID: %0X, block: %0X, %s\n",
+ vk_id, (int)vk_hdr, regf->regfile_name);
+ return NULL;
+ }
+
+ nam_len = SVAL(&vk_hdr->nam_len);
+ val_name[nam_len] = '\0';
+ flag = SVAL(&vk_hdr->flag);
+ dat_type = IVAL(&vk_hdr->dat_type);
+ dat_len = IVAL(&vk_hdr->dat_len); /* If top bit, offset contains data */
+ dat_off = IVAL(&vk_hdr->dat_off);
+
+ tmp = (VAL_KEY *)malloc(sizeof(VAL_KEY));
+ if (!tmp) {
+ goto error;
+ }
+ memset(tmp, 0, sizeof(VAL_KEY));
+ tmp->has_name = flag;
+ tmp->data_type = dat_type;
+
+ if (flag & 0x01) {
+ strncpy(val_name, vk_hdr->dat_name, nam_len);
+ tmp->name = strdup(val_name);
+ if (!tmp->name) {
+ goto error;
+ }
+ }
+ else
+ strncpy(val_name, "<No Name>", 10);
+
+ /*
+ * Allocate space and copy the data as a BLOB
+ */
+
+ if (dat_len) {
+
+ char *dtmp = (char *)malloc(dat_len&0x7FFFFFFF);
+
+ if (!dtmp) {
+ goto error;
+ }
+
+ tmp->data_blk = dtmp;
+
+ if ((dat_len&0x80000000) == 0) { /* The data is pointed to by the offset */
+ char *dat_ptr = LOCN(regf->base, dat_off);
+ bcopy(dat_ptr, dtmp, dat_len);
+ }
+ else { /* The data is in the offset or type */
+ /*
+ * FIXME.
+ * Some registry files seem to have wierd fields. If top bit is set,
+ * but len is 0, the type seems to be the value ...
+ * Not sure how to handle this last type for the moment ...
+ */
+ dat_len = dat_len & 0x7FFFFFFF;
+ bcopy(&dat_off, dtmp, dat_len);
+ }
+
+ tmp->data_len = dat_len;
+ }
+
+ val_type = val_to_str(dat_type, reg_type_names);
+
+ /*
+ * We need to save the data area as well
+ */
+
+ if (verbose) fprintf(stdout, " %s : %s : \n", val_name, val_type);
+
+ return tmp;
+
+ error:
+ if (tmp) nt_delete_val_key(tmp);
+ return NULL;
+
+}
+
+/*
+ * Process a VL Header and return a list of values
+ */
+static
+VAL_LIST *process_vl(REGF *regf, VL_TYPE vl, int count, int size)
+{
+ int i, vk_off;
+ VK_HDR *vk_hdr;
+ VAL_LIST *tmp = NULL;
+
+ if (!vl) return NULL;
+
+ if (-size < (count+1)*sizeof(int)){
+ fprintf(stderr, "Error in VL header format. Size less than space required. %d\n", -size);
+ return NULL;
+ }
+
+ tmp = (VAL_LIST *)malloc(sizeof(VAL_LIST) + (count - 1) * sizeof(VAL_KEY *));
+ if (!tmp) {
+ goto error;
+ }
+
+ for (i=0; i<count; i++) {
+ vk_off = IVAL(&vl[i]);
+ vk_hdr = (VK_HDR *)LOCN(regf->base, vk_off);
+ tmp->vals[i] = process_vk(regf, vk_hdr, BLK_SIZE(vk_hdr));
+ if (!tmp->vals[i]){
+ goto error;
+ }
+ }
+
+ tmp->val_count = count;
+ tmp->max_vals = count;
+
+ return tmp;
+
+ error:
+ /* XXX: FIXME, free the partially allocated structure */
+ return NULL;
+}
+
+/*
+ * Process an LF Header and return a list of sub-keys
+ */
+static
+KEY_LIST *process_lf(REGF *regf, LF_HDR *lf_hdr, int size, REG_KEY *parent)
+{
+ int count, i, nk_off;
+ unsigned int lf_id;
+ KEY_LIST *tmp;
+
+ if (!lf_hdr) return NULL;
+
+ if ((lf_id = SVAL(&lf_hdr->LF_ID)) != REG_LF_ID) {
+ fprintf(stderr, "Unrecognized LF Header format: %0X, Block: %0X, %s.\n",
+ lf_id, (int)lf_hdr, regf->regfile_name);
+ return NULL;
+ }
+
+ assert(size < 0);
+
+ count = SVAL(&lf_hdr->key_count);
+ if (verbose) fprintf(stdout, "Key Count: %u\n", count);
+ if (count <= 0) return NULL;
+
+ /* Now, we should allocate a KEY_LIST struct and fill it in ... */
+
+ tmp = (KEY_LIST *)malloc(sizeof(KEY_LIST) + (count - 1) * sizeof(REG_KEY *));
+ if (!tmp) {
+ goto error;
+ }
+
+ tmp->key_count = count;
+ tmp->max_keys = count;
+
+ for (i=0; i<count; i++) {
+ NK_HDR *nk_hdr;
+
+ nk_off = IVAL(&lf_hdr->hr[i].nk_off);
+ if (verbose) fprintf(stdout, "NK Offset: %0X\n", nk_off);
+ nk_hdr = (NK_HDR *)LOCN(regf->base, nk_off);
+ tmp->keys[i] = nt_get_key_tree(regf, nk_hdr, BLK_SIZE(nk_hdr), parent);
+ if (!tmp->keys[i]) {
+ goto error;
+ }
+ }
+
+ return tmp;
+
+ error:
+ if (tmp) nt_delete_key_list(tmp, False);
+ return NULL;
+}
+
+/*
+ * This routine is passed an NK_HDR pointer and retrieves the entire tree
+ * from there down. It returns a REG_KEY *.
+ */
+static
+REG_KEY *nt_get_key_tree(REGF *regf, NK_HDR *nk_hdr, int size, REG_KEY *parent)
+{
+ REG_KEY *tmp = NULL, *own;
+ int name_len, clsname_len, lf_off, val_off, val_count, sk_off, own_off;
+ unsigned int nk_id;
+ LF_HDR *lf_hdr;
+ VL_TYPE *vl;
+ SK_HDR *sk_hdr;
+ char key_name[1024], cls_name[1024];
+
+ if (!nk_hdr) return NULL;
+
+ if ((nk_id = SVAL(&nk_hdr->NK_ID)) != REG_NK_ID) {
+ fprintf(stderr, "Unrecognized NK Header format: %08X, Block: %0X. %s\n",
+ nk_id, (int)nk_hdr, regf->regfile_name);
+ return NULL;
+ }
+
+ assert(size < 0);
+
+ name_len = SVAL(&nk_hdr->nam_len);
+ clsname_len = SVAL(&nk_hdr->clsnam_len);
+
+ /*
+ * The value of -size should be ge
+ * (sizeof(NK_HDR) - 1 + name_len)
+ * The -1 accounts for the fact that we included the first byte of
+ * the name in the structure. clsname_len is the length of the thing
+ * pointed to by clsnam_off
+ */
+
+ if (-size < (sizeof(NK_HDR) - 1 + name_len)) {
+ fprintf(stderr, "Incorrect NK_HDR size: %d, %0X\n", -size, (int)nk_hdr);
+ fprintf(stderr, "Sizeof NK_HDR: %d, name_len %d, clsname_len %d\n",
+ sizeof(NK_HDR), name_len, clsname_len);
+ /*return NULL;*/
+ }
+
+ if (verbose) fprintf(stdout, "NK HDR: Name len: %d, class name len: %d\n",
+ name_len, clsname_len);
+
+ /* Fish out the key name and process the LF list */
+
+ assert(name_len < sizeof(key_name));
+
+ /* Allocate the key struct now */
+ tmp = (REG_KEY *)malloc(sizeof(REG_KEY));
+ if (!tmp) return tmp;
+ memset(tmp, 0, sizeof(REG_KEY));
+
+ tmp->type = (SVAL(&nk_hdr->type)==0x2C?REG_ROOT_KEY:REG_SUB_KEY);
+
+ strncpy(key_name, nk_hdr->key_nam, name_len);
+ key_name[name_len] = '\0';
+
+ if (verbose) fprintf(stdout, "Key name: %s\n", key_name);
+
+ tmp->name = strdup(key_name);
+ if (!tmp->name) {
+ goto error;
+ }
+
+ /*
+ * Fish out the class name, it is in UNICODE, while the key name is
+ * ASCII :-)
+ */
+
+ if (clsname_len) { /* Just print in Ascii for now */
+ char *clsnamep;
+ int clsnam_off;
+
+ clsnam_off = IVAL(&nk_hdr->clsnam_off);
+ clsnamep = LOCN(regf->base, clsnam_off);
+ if (verbose) fprintf(stdout, "Class Name Offset: %0X\n", clsnam_off);
+
+ memset(cls_name, 0, clsname_len);
+ uni_to_ascii(clsnamep, cls_name, sizeof(cls_name), clsname_len);
+
+ /*
+ * I am keeping class name as an ascii string for the moment.
+ * That means it needs to be converted on output.
+ * It will also piss off people who need Unicode/UTF-8 strings. Sorry.
+ * XXX: FIXME
+ */
+
+ tmp->class_name = strdup(cls_name);
+ if (!tmp->class_name) {
+ goto error;
+ }
+
+ if (verbose) fprintf(stdout, " Class Name: %s\n", cls_name);
+
+ }
+
+ /*
+ * Process the owner offset ...
+ */
+
+ own_off = IVAL(&nk_hdr->own_off);
+ own = (REG_KEY *)LOCN(regf->base, own_off);
+ if (verbose) fprintf(stdout, "Owner Offset: %0X\n", own_off);
+
+ if (verbose) fprintf(stdout, " Owner locn: %0X, Our locn: %0X\n",
+ (unsigned int)own, (unsigned int)nk_hdr);
+
+ /*
+ * We should verify that the owner field is correct ...
+ * for now, we don't worry ...
+ */
+
+ tmp->owner = parent;
+
+ /*
+ * If there are any values, process them here
+ */
+
+ val_count = IVAL(&nk_hdr->val_cnt);
+ if (verbose) fprintf(stdout, "Val Count: %d\n", val_count);
+ if (val_count) {
+
+ val_off = IVAL(&nk_hdr->val_off);
+ vl = (VL_TYPE *)LOCN(regf->base, val_off);
+ if (verbose) fprintf(stdout, "Val List Offset: %0X\n", val_off);
+
+ tmp->values = process_vl(regf, *vl, val_count, BLK_SIZE(vl));
+ if (!tmp->values) {
+ goto error;
+ }
+
+ }
+
+ /*
+ * Also handle the SK header ...
+ */
+
+ sk_off = IVAL(&nk_hdr->sk_off);
+ sk_hdr = (SK_HDR *)LOCN(regf->base, sk_off);
+ if (verbose) fprintf(stdout, "SK Offset: %0X\n", sk_off);
+
+ if (sk_off != -1) {
+
+ tmp->security = process_sk(regf, sk_hdr, sk_off, BLK_SIZE(sk_hdr));
+
+ }
+
+ lf_off = IVAL(&nk_hdr->lf_off);
+ if (verbose) fprintf(stdout, "SubKey list offset: %0X\n", lf_off);
+
+ /*
+ * No more subkeys if lf_off == -1
+ */
+
+ if (lf_off != -1) {
+
+ lf_hdr = (LF_HDR *)LOCN(regf->base, lf_off);
+
+ tmp->sub_keys = process_lf(regf, lf_hdr, BLK_SIZE(lf_hdr), tmp);
+ if (!tmp->sub_keys){
+ goto error;
+ }
+
+ }
+
+ return tmp;
+
+ error:
+ if (tmp) nt_delete_reg_key(tmp, False);
+ return NULL;
+}
+
+static
+int nt_load_registry(REGF *regf)
+{
+ REGF_HDR *regf_hdr;
+ unsigned int regf_id, hbin_id;
+ HBIN_HDR *hbin_hdr;
+ NK_HDR *first_key;
+
+ /* Get the header */
+
+ if ((regf_hdr = nt_get_regf_hdr(regf)) == NULL) {
+ return -1;
+ }
+
+ /* Now process that header and start to read the rest in */
+
+ if ((regf_id = IVAL(&regf_hdr->REGF_ID)) != REG_REGF_ID) {
+ fprintf(stderr, "Unrecognized NT registry header id: %0X, %s\n",
+ regf_id, regf->regfile_name);
+ return -1;
+ }
+
+ /*
+ * Validate the header ...
+ */
+ if (!valid_regf_hdr(regf_hdr)) {
+ fprintf(stderr, "Registry file header does not validate: %s\n",
+ regf->regfile_name);
+ return -1;
+ }
+
+ /* Update the last mod date, and then go get the first NK record and on */
+
+ TTTONTTIME(regf, IVAL(&regf_hdr->tim1), IVAL(&regf_hdr->tim2));
+
+ /*
+ * The hbin hdr seems to be just uninteresting garbage. Check that
+ * it is there, but that is all.
+ */
+
+ hbin_hdr = (HBIN_HDR *)(regf->base + REGF_HDR_BLKSIZ);
+
+ if ((hbin_id = IVAL(&hbin_hdr->HBIN_ID)) != REG_HBIN_ID) {
+ fprintf(stderr, "Unrecognized registry hbin hdr ID: %0X, %s\n",
+ hbin_id, regf->regfile_name);
+ return -1;
+ }
+
+ /*
+ * Get a pointer to the first key from the hreg_hdr
+ */
+
+ if (verbose) fprintf(stdout, "First Key: %0X\n",
+ IVAL(&regf_hdr->first_key));
+
+ first_key = (NK_HDR *)LOCN(regf->base, IVAL(&regf_hdr->first_key));
+ if (verbose) fprintf(stdout, "First Key Offset: %0X\n",
+ IVAL(&regf_hdr->first_key));
+
+ if (verbose) fprintf(stdout, "Data Block Size: %d\n",
+ IVAL(&regf_hdr->dblk_size));
+
+ if (verbose) fprintf(stdout, "Offset to next hbin block: %0X\n",
+ IVAL(&hbin_hdr->off_to_next));
+
+ if (verbose) fprintf(stdout, "HBIN block size: %0X\n",
+ IVAL(&hbin_hdr->blk_size));
+
+ /*
+ * Now, get the registry tree by processing that NK recursively
+ */
+
+ regf->root = nt_get_key_tree(regf, first_key, BLK_SIZE(first_key), NULL);
+
+ assert(regf->root != NULL);
+
+ /*
+ * Unmap the registry file, as we might want to read in another
+ * tree etc.
+ */
+
+ if (regf->base) munmap(regf->base, regf->sbuf.st_size);
+ regf->base = NULL;
+ close(regf->fd); /* Ignore the error :-) */
+
+ return 1;
+}
+
+/*
+ * Allocate a new hbin block, set up the header for the block etc
+ */
+static
+HBIN_BLK *nt_create_hbin_blk(REGF *regf, int size)
+{
+ HBIN_BLK *tmp;
+ HBIN_HDR *hdr;
+
+ if (!regf || !size) return NULL;
+
+ /* Round size up to multiple of REGF_HDR_BLKSIZ */
+
+ size = (size + (REGF_HDR_BLKSIZ - 1)) & ~(REGF_HDR_BLKSIZ - 1);
+
+ tmp = (HBIN_BLK *)malloc(sizeof(HBIN_BLK));
+ memset(tmp, 0, sizeof(HBIN_BLK));
+
+ tmp->data = malloc(size);
+ if (!tmp->data) goto error;
+
+ memset(tmp->data, 0, size); /* Make it pristine */
+
+ tmp->size = size;
+ tmp->file_offset = regf->blk_tail->file_offset + regf->blk_tail->size;
+
+ tmp->free_space = size - (sizeof(HBIN_HDR) - sizeof(HBIN_SUB_HDR));
+ tmp->fsp_off = size - tmp->free_space;
+
+ /*
+ * Now, build the header in the data block
+ */
+ hdr = (HBIN_HDR *)tmp->data;
+ hdr->HBIN_ID = REG_HBIN_ID;
+ hdr->off_from_first = tmp->file_offset - REGF_HDR_BLKSIZ;
+ hdr->off_to_next = tmp->size;
+ hdr->blk_size = tmp->size;
+
+ /*
+ * Now link it in
+ */
+
+ regf->blk_tail->next = tmp;
+ regf->blk_tail = tmp;
+ if (!regf->free_space) regf->free_space = tmp;
+
+ return tmp;
+ error:
+ if (tmp) free(tmp);
+ return NULL;
+}
+
+/*
+ * Allocate a unit of space ... and return a pointer as function param
+ * and the block's offset as a side effect
+ */
+static
+void *nt_alloc_regf_space(REGF *regf, int size, unsigned int *off)
+{
+ int tmp = 0;
+ void *ret = NULL;
+ HBIN_BLK *blk;
+
+ if (!regf || !size || !off) return NULL;
+
+ assert(regf->blk_head != NULL);
+
+ /*
+ * round up size to include header and then to 8-byte boundary
+ */
+ size = (size + 4 + 7) & ~7;
+
+ /*
+ * Check if there is space, if none, grab a block
+ */
+ if (!regf->free_space) {
+ if (!nt_create_hbin_blk(regf, REGF_HDR_BLKSIZ))
+ return NULL;
+ }
+
+ /*
+ * Now, chain down the list of blocks looking for free space
+ */
+
+ for (blk = regf->free_space; blk != NULL; blk = blk->next) {
+ if (blk->free_space <= size) {
+ tmp = blk->file_offset + blk->fsp_off - REGF_HDR_BLKSIZ;
+ ret = blk->data + blk->fsp_off;
+ blk->free_space -= size;
+ blk->fsp_off += size;
+
+ /* Insert the header */
+ ((HBIN_SUB_HDR *)ret)->dblocksize = -size;
+
+ /*
+ * Fix up the free space ptr
+ * If it is NULL, we fix it up next time
+ */
+
+ if (!blk->free_space)
+ regf->free_space = blk->next;
+
+ *off = tmp;
+ return (((char *)ret)+4);/* The pointer needs to be to the data struct */
+ }
+ }
+
+ /*
+ * If we got here, we need to add another block, which might be
+ * larger than one block -- deal with that later
+ */
+ if (nt_create_hbin_blk(regf, REGF_HDR_BLKSIZ)) {
+ blk = regf->free_space;
+ tmp = blk->file_offset + blk->fsp_off - REGF_HDR_BLKSIZ;
+ ret = blk->data + blk->fsp_off;
+ blk->free_space -= size;
+ blk->fsp_off += size;
+
+ /* Insert the header */
+ ((HBIN_SUB_HDR *)ret)->dblocksize = -size;
+
+ /*
+ * Fix up the free space ptr
+ * If it is NULL, we fix it up next time
+ */
+
+ if (!blk->free_space)
+ regf->free_space = blk->next;
+
+ *off = tmp;
+ return (((char *)ret) + 4);/* The pointer needs to be to the data struct */
+ }
+
+ return NULL;
+}
+
+/*
+ * Compute the size of a SID stored ...
+ */
+static
+unsigned int sid_size(sid_t *sid)
+{
+ unsigned int size;
+
+ if (!sid) return 0;
+
+ size = 8 + (sid->auths * sizeof(unsigned int));
+
+ return size;
+}
+
+/*
+ * Compute the size of an ACE on disk from its components
+ */
+static
+unsigned int ace_size(ACE *ace)
+{
+ unsigned int size;
+
+ if (!ace) return 0;
+
+ size = 8 + sid_size(ace->trustee);
+
+ return size;
+}
+
+/*
+ * Compute the size of an ACL from its components ...
+ */
+static
+unsigned int acl_size(ACL *acl)
+{
+ unsigned int size;
+ int i;
+
+ if (!acl) return 0;
+
+ size = 8;
+ for (i = 0; i < acl->num_aces; i++)
+ size += ace_size(acl->aces[i]);
+
+ return size;
+}
+
+/*
+ * Compute the size of the sec desc as a self-relative SD
+ */
+static
+unsigned int sec_desc_size(SEC_DESC *sd)
+{
+ unsigned int size;
+
+ if (!sd) return 0;
+
+ size = 20;
+
+ if (sd->owner) size += sid_size(sd->owner);
+ if (sd->group) size += sid_size(sd->group);
+ if (sd->sacl) size += acl_size(sd->sacl);
+ if (sd->dacl) size += acl_size(sd->dacl);
+
+ return size;
+}
+
+/*
+ * Store a SID at the location provided
+ */
+static
+int nt_store_SID(REGF *regf, sid_t *sid, unsigned char *locn)
+{
+ int i;
+ unsigned char *p = locn;
+
+ if (!regf || !sid || !locn) return 0;
+
+ *p = sid->ver; p++;
+ *p = sid->auths; p++;
+
+ for (i=0; i < 6; i++) {
+ *p = sid->auth[i]; p++;
+ }
+
+ for (i=0; i < sid->auths; i++) {
+ SIVAL(p, sid->sub_auths[i]); p+=4;
+ }
+
+ return p - locn;
+
+}
+
+static
+int nt_store_ace(REGF *regf, ACE *ace, unsigned char *locn)
+{
+ int size = 0;
+ REG_ACE *reg_ace = (REG_ACE *)locn;
+ unsigned char *p;
+
+ if (!regf || !ace || !locn) return 0;
+
+ reg_ace->type = ace->type;
+ reg_ace->flags = ace->flags;
+
+ /* Deal with the length when we have stored the SID */
+
+ p = (unsigned char *)&reg_ace->perms;
+
+ SIVAL(p, ace->perms); p += 4;
+
+ size = nt_store_SID(regf, ace->trustee, p);
+
+ size += 8; /* Size of the fixed header */
+
+ p = (unsigned char *)&reg_ace->length;
+
+ SSVAL(p, size);
+
+ return size;
+}
+
+/*
+ * Store an ACL at the location provided
+ */
+static
+int nt_store_acl(REGF *regf, ACL *acl, unsigned char *locn)
+{
+ int size = 0, i;
+ unsigned char *p = locn, *s;
+
+ if (!regf || !acl || !locn) return 0;
+
+ /*
+ * Now store the header and then the ACEs ...
+ */
+
+ SSVAL(p, acl->rev);
+
+ p += 2; s = p; /* Save this for the size field */
+
+ p += 2;
+
+ SIVAL(p, acl->num_aces);
+
+ p += 4;
+
+ for (i = 0; i < acl->num_aces; i++) {
+ size = nt_store_ace(regf, acl->aces[i], p);
+ p += size;
+ }
+
+ size = s - locn;
+ SSVAL(s, size);
+ return size;
+}
+
+/*
+ * Flatten and store the Sec Desc
+ * Windows lays out the DACL first, but since there is no SACL, it might be
+ * that first, then the owner, then the group SID. So, we do it that way
+ * too.
+ */
+static
+unsigned int nt_store_sec_desc(REGF *regf, SEC_DESC *sd, char *locn)
+{
+ REG_SEC_DESC *rsd = (REG_SEC_DESC *)locn;
+ unsigned int size = 0, off = 0;
+
+ if (!regf || !sd || !locn) return 0;
+
+ /*
+ * Now, fill in the first two fields, then lay out the various fields
+ * as needed
+ */
+
+ rsd->rev = 0x01;
+ /* Self relative, DACL pres, owner and group not defaulted */
+ rsd->type = 0x8004;
+
+ off = 4 * sizeof(DWORD) + 4;
+
+ if (sd->sacl){
+ size = nt_store_acl(regf, sd->sacl, (char *)(locn + off));
+ rsd->sacl_off = off;
+ }
+ else
+ rsd->sacl_off = 0;
+
+ off += size;
+
+ if (sd->dacl) {
+ rsd->dacl_off = off;
+ size = nt_store_acl(regf, sd->dacl, (char *)(locn + off));
+ }
+ else {
+ rsd->dacl_off = 0;
+ }
+
+ off += size;
+
+ /* Now the owner and group SIDs */
+
+ if (sd->owner) {
+ rsd->owner_off = off;
+ size = nt_store_SID(regf, sd->owner, (char *)(locn + off));
+ }
+ else {
+ rsd->owner_off = 0;
+ }
+
+ off += size;
+
+ if (sd->group) {
+ rsd->group_off = off;
+ size = nt_store_SID(regf, sd->group, (char *)(locn + off));
+ }
+ else {
+ rsd->group_off = 0;
+ }
+
+ off += size;
+
+ return size;
+}
+
+/*
+ * Store the security information
+ *
+ * If it has already been stored, just get its offset from record
+ * otherwise, store it and record its offset
+ */
+static
+unsigned int nt_store_security(REGF *regf, KEY_SEC_DESC *sec)
+{
+ int size = 0;
+ unsigned int sk_off;
+ SK_HDR *sk_hdr;
+
+ if (sec->offset) return sec->offset;
+
+ /*
+ * OK, we don't have this one in the file yet. We must compute the
+ * size taken by the security descriptor as a self-relative SD, which
+ * means making one pass over each structure and figuring it out
+ */
+
+ size = sec_desc_size(sec->sec_desc);
+
+ /* Allocate that much space */
+
+ sk_hdr = nt_alloc_regf_space(regf, size, &sk_off);
+ sec->sk_hdr = sk_hdr;
+
+ if (!sk_hdr) return 0;
+
+ /* Now, lay out the sec_desc in the space provided */
+
+ sk_hdr->SK_ID = REG_SK_ID;
+
+ /*
+ * We can't deal with the next and prev offset in the SK_HDRs until the
+ * whole tree has been stored, then we can go and deal with them
+ */
+
+ sk_hdr->ref_cnt = sec->ref_cnt;
+ sk_hdr->rec_size = size; /* Is this correct */
+
+ /* Now, lay out the sec_desc */
+
+ if (!nt_store_sec_desc(regf, sec->sec_desc, (char *)&sk_hdr->sec_desc))
+ return 0;
+
+ return sk_off;
+
+}
+
+/*
+ * Store a VAL LIST
+ */
+static
+int nt_store_val_list(REGF *regf, VAL_LIST * values)
+{
+
+ return 0;
+}
+
+/*
+ * Store a KEY in the file ...
+ *
+ * We store this depth first, and defer storing the lf struct until
+ * all the sub-keys have been stored.
+ *
+ * We store the NK hdr, any SK header, class name, and VK structure, then
+ * recurse down the LF structures ...
+ *
+ * We return the offset of the NK struct
+ * FIXME, FIXME, FIXME: Convert to using SIVAL and SSVAL ...
+ */
+static
+int nt_store_reg_key(REGF *regf, REG_KEY *key)
+{
+ NK_HDR *nk_hdr;
+ unsigned int nk_off, sk_off, size;
+
+ if (!regf || !key) return 0;
+
+ size = sizeof(NK_HDR) + strlen(key->name) - 1;
+ nk_hdr = nt_alloc_regf_space(regf, size, &nk_off);
+ if (!nk_hdr) goto error;
+
+ key->offset = nk_off; /* We will need this later */
+
+ /*
+ * Now fill in each field etc ...
+ */
+
+ nk_hdr->NK_ID = REG_NK_ID;
+ if (key->type == REG_ROOT_KEY)
+ nk_hdr->type = 0x2C;
+ else
+ nk_hdr->type = 0x20;
+
+ /* FIXME: Fill in the time of last update */
+
+ if (key->type != REG_ROOT_KEY)
+ nk_hdr->own_off = key->owner->offset;
+
+ if (key->sub_keys)
+ nk_hdr->subk_num = key->sub_keys->key_count;
+
+ /*
+ * Now, process the Sec Desc and then store its offset
+ */
+
+ sk_off = nt_store_security(regf, key->security);
+ nk_hdr->sk_off = sk_off;
+
+ /*
+ * Then, store the val list and store its offset
+ */
+ if (key->values) {
+ nk_hdr->val_cnt = key->values->val_count;
+ nk_hdr->val_off = nt_store_val_list(regf, key->values);
+ }
+ else {
+ nk_hdr->val_off = -1;
+ nk_hdr->val_cnt = 0;
+ }
+
+ /*
+ * Finally, store the subkeys, and their offsets
+ */
+
+ error:
+ return 0;
+}
+
+/*
+ * Store the registry header ...
+ * We actually create the registry header block and link it to the chain
+ * of output blocks.
+ */
+static
+REGF_HDR *nt_get_reg_header(REGF *regf)
+{
+ HBIN_BLK *tmp = NULL;
+
+ tmp = (HBIN_BLK *)malloc(sizeof(HBIN_BLK));
+ if (!tmp) return 0;
+
+ memset(tmp, 0, sizeof(HBIN_BLK));
+ tmp->type = REG_OUTBLK_HDR;
+ tmp->size = REGF_HDR_BLKSIZ;
+ tmp->data = malloc(REGF_HDR_BLKSIZ);
+ if (!tmp->data) goto error;
+
+ memset(tmp->data, 0, REGF_HDR_BLKSIZ); /* Make it pristine, unlike Windows */
+ regf->blk_head = regf->blk_tail = tmp;
+
+ return (REGF_HDR *)tmp->data;
+
+ error:
+ if (tmp) free(tmp);
+ return NULL;
+}
+
+/*
+ * Store the registry in the output file
+ * We write out the header and then each of the keys etc into the file
+ * We have to flatten the data structure ...
+ *
+ * The structures are stored in a depth-first fashion, with all records
+ * aligned on 8-byte boundaries, with sub-keys and values layed down before
+ * the lists that contain them. SK records are layed down first, however.
+ * The lf fields are layed down after all sub-keys have been layed down, it
+ * seems, including the whole tree associated with each sub-key.
+ */
+static
+int nt_store_registry(REGF *regf)
+{
+ REGF_HDR *reg;
+ int fkey, fd;
+
+ /*
+ * Get a header ... and partially fill it in ...
+ */
+ reg = nt_get_reg_header(regf);
+
+ /*
+ * Store the first key, which will store the whole thing
+ */
+ fkey = nt_store_reg_key(regf, regf->root);
+
+ /*
+ * At this point we have the registry as a series of blocks, so
+ * run down that series of blocks and save them ...
+ */
+
+ if (!regf->outfile_name) {
+ fprintf(stderr, "Cannot write file without a name!\n");
+ return 0;
+ }
+
+ if ((fd = open(regf->outfile_name, O_WRONLY, 0666)) < 0) {
+ fprintf(stderr, "Unable to create file %s: %s\n", regf->outfile_name,
+ strerror(errno));
+ return 0;
+ }
+
+ return 1;
+}
+
+/*
+ * Routines to parse a REGEDIT4 file
+ *
+ * The file consists of:
+ *
+ * REGEDIT4
+ * \[[-]key-path\]\n
+ * <value-spec>*
+ *
+ * Format:
+ * [cmd:]name=type:value
+ *
+ * cmd = a|d|c|add|delete|change|as|ds|cs
+ *
+ * There can be more than one key-path and value-spec.
+ *
+ * Since we want to support more than one type of file format, we
+ * construct a command-file structure that keeps info about the command file
+ */
+
+#define FMT_UNREC -1
+#define FMT_REGEDIT4 0
+#define FMT_EDITREG1_1 1
+
+#define FMT_STRING_REGEDIT4 "REGEDIT4"
+#define FMT_STRING_EDITREG1_0 "EDITREG1.0"
+
+#define CMD_NONE 0
+#define CMD_ADD_KEY 1
+#define CMD_DEL_KEY 2
+
+#define CMD_KEY 1
+#define CMD_VAL 2
+
+typedef struct val_spec_list {
+ struct val_spec_list *next;
+ char *name;
+ int type;
+ char *val; /* Kept as a char string, really? */
+} VAL_SPEC_LIST;
+
+typedef struct command_s {
+ int cmd;
+ char *key;
+ int val_count;
+ VAL_SPEC_LIST *val_spec_list, *val_spec_last;
+} CMD;
+
+typedef struct cmd_line {
+ int len, line_len;
+ char *line;
+} CMD_LINE;
+
+static
+void free_val_spec_list(VAL_SPEC_LIST *vl)
+{
+ if (!vl) return;
+ if (vl->name) free(vl->name);
+ if (vl->val) free(vl->val);
+ free(vl);
+
+}
+
+/*
+ * Some routines to handle lines of info in the command files
+ */
+static
+void skip_to_eol(int fd)
+{
+ int rc;
+ char ch = 0;
+
+ while ((rc = read(fd, &ch, 1)) == 1) {
+ if (ch == 0x0A) return;
+ }
+ if (rc < 0) {
+ fprintf(stderr, "Could not read file descriptor: %d, %s\n",
+ fd, strerror(errno));
+ exit(1);
+ }
+}
+
+static
+void free_cmd(CMD *cmd)
+{
+ if (!cmd) return;
+
+ while (cmd->val_spec_list) {
+ VAL_SPEC_LIST *tmp;
+
+ tmp = cmd->val_spec_list;
+ cmd->val_spec_list = tmp->next;
+ free(tmp);
+ }
+
+ free(cmd);
+
+}
+
+static
+void free_cmd_line(CMD_LINE *cmd_line)
+{
+ if (cmd_line) {
+ if (cmd_line->line) free(cmd_line->line);
+ free(cmd_line);
+ }
+}
+
+static
+void print_line(struct cmd_line *cl)
+{
+ char *pl;
+
+ if (!cl) return;
+
+ if ((pl = malloc(cl->line_len + 1)) == NULL) {
+ fprintf(stderr, "Unable to allocate space to print line: %s\n",
+ strerror(errno));
+ exit(1);
+ }
+
+ strncpy(pl, cl->line, cl->line_len);
+ pl[cl->line_len] = 0;
+
+ fprintf(stdout, "%s\n", pl);
+ free(pl);
+}
+
+#define INIT_ALLOC 10
+
+/*
+ * Read a line from the input file.
+ * NULL returned when EOF and no chars read
+ * Otherwise we return a cmd_line *
+ * Exit if other errors
+ */
+static
+struct cmd_line *get_cmd_line(int fd)
+{
+ struct cmd_line *cl = (CMD_LINE *)malloc(sizeof(CMD_LINE));
+ int i = 0, rc;
+ unsigned char ch;
+
+ if (!cl) {
+ fprintf(stderr, "Unable to allocate structure for command line: %s\n",
+ strerror(errno));
+ exit(1);
+ }
+
+ cl->len = INIT_ALLOC;
+
+ /*
+ * Allocate some space for the line. We extend later if needed.
+ */
+
+ if ((cl->line = (char *)malloc(INIT_ALLOC)) == NULL) {
+ fprintf(stderr, "Unable to allocate initial space for line: %s\n",
+ strerror(errno));
+ exit(1);
+ }
+
+ /*
+ * Now read in the chars to EOL. Don't store the EOL in the
+ * line. What about CR?
+ */
+
+ while ((rc = read(fd, &ch, 1)) == 1 && ch != '\n') {
+ if (ch == '\r') continue; /* skip CR */
+ if (i == cl->len) {
+ /*
+ * Allocate some more memory
+ */
+ if ((cl->line = realloc(cl->line, cl->len + INIT_ALLOC)) == NULL) {
+ fprintf(stderr, "Unable to realloc space for line: %s\n",
+ strerror(errno));
+ exit(1);
+ }
+ cl->len += INIT_ALLOC;
+ }
+ cl->line[i] = ch;
+ i++;
+ }
+
+ /* read 0 and we were at loc'n 0, return NULL */
+ if (rc == 0 && i == 0) {
+ free_cmd_line(cl);
+ return NULL;
+ }
+
+ cl->line_len = i;
+
+ return cl;
+
+}
+
+/*
+ * parse_value: parse out a value. We pull it apart as:
+ *
+ * <value> ::= <value-name>=<type>:<value-string>
+ *
+ * <value-name> ::= char-string-without-spaces | '"' char-string '"'
+ *
+ * If it parsed OK, return the <value-name> as a string, and the
+ * value type and value-string in parameters.
+ *
+ * The value name can be empty. There can only be one empty name in
+ * a list of values. A value of - removes the value entirely.
+ */
+static
+char *dup_str(char *s, int len)
+{
+ char *nstr;
+ nstr = (char *)malloc(len + 1);
+ if (nstr) {
+ memcpy(nstr, s, len);
+ nstr[len] = 0;
+ }
+ return nstr;
+}
+
+static
+char *parse_name(char *nstr)
+{
+ int len = 0, start = 0;
+ if (!nstr) return NULL;
+
+ len = strlen(nstr);
+
+ while (len && nstr[len - 1] == ' ') len--;
+
+ nstr[len] = 0; /* Trim any spaces ... if there were none, doesn't matter */
+
+ /*
+ * Beginning and end should be '"' or neither should be so
+ */
+ if ((nstr[0] == '"' && nstr[len - 1] != '"') ||
+ (nstr[0] != '"' && nstr[len - 1] == '"'))
+ return NULL;
+
+ if (nstr[0] == '"') {
+ start = 1;
+ len -= 2;
+ }
+
+ return dup_str(&nstr[start], len);
+}
+
+static
+int parse_value_type(char *tstr)
+{
+ int len = strlen(tstr);
+
+ while (len && tstr[len - 1] == ' ') len--;
+ tstr[len] = 0;
+
+ if (strcmp(tstr, "REG_DWORD") == 0)
+ return REG_TYPE_DWORD;
+ else if (strcmp(tstr, "dword") == 0)
+ return REG_TYPE_DWORD;
+ else if (strcmp(tstr, "REG_EXPAND_SZ") == 0)
+ return REG_TYPE_EXPANDSZ;
+ else if (strcmp(tstr, "REG_BIN") == 0)
+ return REG_TYPE_BIN;
+ else if (strcmp(tstr, "REG_SZ") == 0)
+ return REG_TYPE_REGSZ;
+ else if (strcmp(tstr, "REG_MULTI_SZ") == 0)
+ return REG_TYPE_MULTISZ;
+ else if (strcmp(tstr, "-") == 0)
+ return REG_TYPE_DELETE;
+
+ return 0;
+}
+
+static
+char *parse_val_str(char *vstr)
+{
+
+ return dup_str(vstr, strlen(vstr));
+
+}
+
+static
+char *parse_value(struct cmd_line *cl, int *vtype, char **val)
+{
+ char *p1 = NULL, *p2 = NULL, *nstr = NULL, *tstr = NULL, *vstr = NULL;
+
+ if (!cl || !vtype || !val) return NULL;
+ if (!cl->line_len) return NULL;
+
+ p1 = dup_str(cl->line, cl->line_len);
+ /* FIXME: Better return codes etc ... */
+ if (!p1) return NULL;
+ p2 = strchr(p1, '=');
+ if (!p2) return NULL;
+
+ *p2 = 0; p2++; /* Split into two strings at p2 */
+
+ /* Now, parse the name ... */
+
+ nstr = parse_name(p1);
+ if (!nstr) goto error;
+
+ /* Now, split the remainder and parse on type and val ... */
+
+ tstr = p2;
+ while (*tstr == ' ') tstr++; /* Skip leading white space */
+ p2 = strchr(p2, ':');
+
+ if (p2) {
+ *p2 = 0; p2++; /* split on the : */
+ }
+
+ *vtype = parse_value_type(tstr);
+
+ if (!vtype) goto error;
+
+ if (!p2 || !*p2) return nstr;
+
+ /* Now, parse the value string. It should return a newly malloc'd string */
+
+ while (*p2 == ' ') p2++; /* Skip leading space */
+ vstr = parse_val_str(p2);
+
+ if (!vstr) goto error;
+
+ *val = vstr;
+
+ return nstr;
+
+ error:
+ if (p1) free(p1);
+ if (nstr) free(nstr);
+ if (vstr) free(vstr);
+ return NULL;
+}
+
+/*
+ * Parse out a key. Look for a correctly formatted key [...]
+ * and whether it is a delete or add? A delete is signalled
+ * by a - in front of the key.
+ * Assumes that there are no leading and trailing spaces
+ */
+
+static
+char *parse_key(struct cmd_line *cl, int *cmd)
+{
+ int start = 1;
+ char *tmp;
+
+ if (cl->line[0] != '[' ||
+ cl->line[cl->line_len - 1] != ']') return NULL;
+ if (cl->line_len == 2) return NULL;
+ *cmd = CMD_ADD_KEY;
+ if (cl->line[1] == '-') {
+ if (cl->line_len == 3) return NULL;
+ start = 2;
+ *cmd = CMD_DEL_KEY;
+ }
+ tmp = malloc(cl->line_len - 1 - start + 1);
+ if (!tmp) return tmp; /* Bail out on no mem ... FIXME */
+ strncpy(tmp, &cl->line[start], cl->line_len - 1 - start);
+ tmp[cl->line_len - 1 - start] = 0;
+ return tmp;
+}
+
+/*
+ * Parse a line to determine if we have a key or a value
+ * We only check for key or val ...
+ */
+
+static
+int parse_line(struct cmd_line *cl)
+{
+
+ if (!cl || cl->len == 0) return 0;
+
+ if (cl->line[0] == '[') /* No further checking for now */
+ return CMD_KEY;
+ else
+ return CMD_VAL;
+}
+
+/*
+ * We seek to offset 0, read in the required number of bytes,
+ * and compare to the correct value.
+ * We then seek back to the original location
+ */
+static
+int regedit4_file_type(int fd)
+{
+ int cur_ofs = 0;
+ char desc[9];
+
+ cur_ofs = lseek(fd, 0, SEEK_CUR); /* Get current offset */
+ if (cur_ofs < 0) {
+ fprintf(stderr, "Unable to get current offset: %s\n", strerror(errno));
+ exit(1); /* FIXME */
+ }
+
+ if (cur_ofs) {
+ lseek(fd, 0, SEEK_SET);
+ }
+
+ if (read(fd, desc, 8) < 8) {
+ fprintf(stderr, "Unable to read command file format\n");
+ exit(2); /* FIXME */
+ }
+
+ desc[8] = 0;
+
+ if (strcmp(desc, FMT_STRING_REGEDIT4) == 0) {
+ if (cur_ofs) {
+ lseek(fd, cur_ofs, SEEK_SET);
+ }
+ else {
+ skip_to_eol(fd);
+ }
+ return FMT_REGEDIT4;
+ }
+
+ return FMT_UNREC;
+}
+
+/*
+ * Run though the data in the line and strip anything after a comment
+ * char.
+ */
+static
+void strip_comment(struct cmd_line *cl)
+{
+ int i;
+
+ if (!cl) return;
+
+ for (i = 0; i < cl->line_len; i++) {
+ if (cl->line[i] == ';') {
+ cl->line_len = i;
+ return;
+ }
+ }
+}
+
+/*
+ * trim leading space
+ */
+
+static
+void trim_leading_spaces(struct cmd_line *cl)
+{
+ int i;
+
+ if (!cl) return;
+
+ for (i = 0; i < cl->line_len; i++) {
+ if (cl->line[i] != ' '){
+ if (i) memcpy(cl->line, &cl->line[i], cl->line_len - i);
+ return;
+ }
+ }
+}
+
+/*
+ * trim trailing spaces
+ */
+static
+void trim_trailing_spaces(struct cmd_line *cl)
+{
+ int i;
+
+ if (!cl) return;
+
+ for (i = cl->line_len; i == 0; i--) {
+ if (cl->line[i-1] != ' ' &&
+ cl->line[i-1] != '\t') {
+ cl->line_len = i;
+ }
+ }
+}
+
+/*
+ * Get a command ... This consists of possibly multiple lines:
+ * [key]
+ * values*
+ * possibly Empty line
+ *
+ * value ::= <value-name>=<value-type>':'<value-string>
+ * <value-name> is some path, possibly enclosed in quotes ...
+ * We alctually look for the next key to terminate a previous key
+ * if <value-type> == '-', then it is a delete type.
+ */
+static
+CMD *regedit4_get_cmd(int fd)
+{
+ struct command_s *cmd = NULL;
+ struct cmd_line *cl = NULL;
+ struct val_spec_list *vl = NULL;
+
+ if ((cmd = (struct command_s *)malloc(sizeof(struct command_s))) == NULL) {
+ fprintf(stderr, "Unable to malloc space for command: %s\n",
+ strerror(errno));
+ exit(1);
+ }
+
+ cmd->cmd = CMD_NONE;
+ cmd->key = NULL;
+ cmd->val_count = 0;
+ cmd->val_spec_list = cmd->val_spec_last = NULL;
+ while ((cl = get_cmd_line(fd))) {
+
+ /*
+ * If it is an empty command line, and we already have a key
+ * then exit from here ... FIXME: Clean up the parser
+ */
+
+ if (cl->line_len == 0 && cmd->key) {
+ free_cmd_line(cl);
+ break;
+ }
+
+ strip_comment(cl); /* remove anything beyond a comment char */
+ trim_trailing_spaces(cl);
+ trim_leading_spaces(cl);
+
+ if (cl->line_len == 0) { /* An empty line */
+ free_cmd_line(cl);
+ }
+ else { /* Else, non-empty ... */
+ /*
+ * Parse out the bits ...
+ */
+ switch (parse_line(cl)) {
+ case CMD_KEY:
+ if ((cmd->key = parse_key(cl, &cmd->cmd)) == NULL) {
+ fprintf(stderr, "Error parsing key from line: ");
+ print_line(cl);
+ fprintf(stderr, "\n");
+ }
+ break;
+
+ case CMD_VAL:
+ /*
+ * We need to add the value stuff to the list
+ * There could be a \ on the end which we need to
+ * handle at some time
+ */
+ vl = (struct val_spec_list *)malloc(sizeof(struct val_spec_list));
+ if (!vl) goto error;
+ vl->next = NULL;
+ vl->val = NULL;
+ vl->name = parse_value(cl, &vl->type, &vl->val);
+ if (!vl->name) goto error;
+ if (cmd->val_spec_list == NULL) {
+ cmd->val_spec_list = cmd->val_spec_last = vl;
+ }
+ else {
+ cmd->val_spec_last->next = vl;
+ cmd->val_spec_last = vl;
+ }
+ cmd->val_count++;
+ break;
+
+ default:
+ fprintf(stderr, "Unrecognized line in command file: \n");
+ print_line(cl);
+ break;
+ }
+ }
+
+ }
+ if (!cmd->cmd) goto error; /* End of file ... */
+
+ return cmd;
+
+ error:
+ if (vl) free(vl);
+ if (cmd) free_cmd(cmd);
+ return NULL;
+}
+
+static
+int regedit4_exec_cmd(CMD *cmd)
+{
+
+ return 0;
+}
+
+static
+int editreg_1_0_file_type(int fd)
+{
+ int cur_ofs = 0;
+ char desc[11];
+
+ cur_ofs = lseek(fd, 0, SEEK_CUR); /* Get current offset */
+ if (cur_ofs < 0) {
+ fprintf(stderr, "Unable to get current offset: %s\n", strerror(errno));
+ exit(1); /* FIXME */
+ }
+
+ if (cur_ofs) {
+ lseek(fd, 0, SEEK_SET);
+ }
+
+ if (read(fd, desc, 10) < 10) {
+ fprintf(stderr, "Unable to read command file format\n");
+ exit(2); /* FIXME */
+ }
+
+ desc[10] = 0;
+
+ if (strcmp(desc, FMT_STRING_EDITREG1_0) == 0) {
+ lseek(fd, cur_ofs, SEEK_SET);
+ return FMT_REGEDIT4;
+ }
+
+ return FMT_UNREC;
+}
+
+static
+CMD *editreg_1_0_get_cmd(int fd)
+{
+ return NULL;
+}
+
+static
+int editreg_1_0_exec_cmd(CMD *cmd)
+{
+
+ return -1;
+}
+
+typedef struct command_ops_s {
+ int type;
+ int (*file_type)(int fd);
+ CMD *(*get_cmd)(int fd);
+ int (*exec_cmd)(CMD *cmd);
+} CMD_OPS;
+
+CMD_OPS default_cmd_ops[] = {
+ {0, regedit4_file_type, regedit4_get_cmd, regedit4_exec_cmd},
+ {1, editreg_1_0_file_type, editreg_1_0_get_cmd, editreg_1_0_exec_cmd},
+ {-1, NULL, NULL, NULL}
+};
+
+typedef struct command_file_s {
+ char *name;
+ int type, fd;
+ CMD_OPS cmd_ops;
+} CMD_FILE;
+
+/*
+ * Create a new command file structure
+ */
+
+static
+CMD_FILE *cmd_file_create(char *file)
+{
+ CMD_FILE *tmp;
+ struct stat sbuf;
+ int i = 0;
+
+ /*
+ * Let's check if the file exists ...
+ * No use creating the cmd_file structure if the file does not exist
+ */
+
+ if (stat(file, &sbuf) < 0) { /* Not able to access file */
+
+ return NULL;
+ }
+
+ tmp = (CMD_FILE *)malloc(sizeof(CMD_FILE));
+ if (!tmp) {
+ return NULL;
+ }
+
+ /*
+ * Let's fill in some of the fields;
+ */
+
+ tmp->name = strdup(file);
+
+ if ((tmp->fd = open(file, O_RDONLY, 666)) < 0) {
+ free(tmp);
+ return NULL;
+ }
+
+ /*
+ * Now, try to find the format by indexing through the table
+ */
+ while (default_cmd_ops[i].type != -1) {
+ if ((tmp->type = default_cmd_ops[i].file_type(tmp->fd)) >= 0) {
+ tmp->cmd_ops = default_cmd_ops[i];
+ return tmp;
+ }
+ i++;
+ }
+
+ /*
+ * If we got here, return NULL, as we could not figure out the type
+ * of command file.
+ *
+ * What about errors?
+ */
+
+ free(tmp);
+ return NULL;
+}
+
+/*
+ * Extract commands from the command file, and execute them.
+ * We pass a table of command callbacks for that
+ */
+
+/*
+ * Main code from here on ...
+ */
+
+/*
+ * key print function here ...
+ */
+
+static
+int print_key(const char *path, char *name, char *class_name, int root,
+ int terminal, int vals)
+{
+
+ if (full_print || terminal) fprintf(stdout, "[%s%s]\n", path, name);
+
+ return 1;
+}
+
+/*
+ * Sec Desc print functions
+ */
+
+static
+void print_type(unsigned char type)
+{
+ switch (type) {
+ case 0x00:
+ fprintf(stdout, " ALLOW");
+ break;
+ case 0x01:
+ fprintf(stdout, " DENY");
+ break;
+ case 0x02:
+ fprintf(stdout, " AUDIT");
+ break;
+ case 0x03:
+ fprintf(stdout, " ALARM");
+ break;
+ case 0x04:
+ fprintf(stdout, "ALLOW CPD");
+ break;
+ case 0x05:
+ fprintf(stdout, "OBJ ALLOW");
+ break;
+ case 0x06:
+ fprintf(stdout, " OBJ DENY");
+ default:
+ fprintf(stdout, " UNKNOWN");
+ break;
+ }
+}
+
+static
+void print_flags(unsigned char flags)
+{
+ char flg_output[21];
+ int some = 0;
+
+ flg_output[0] = 0;
+ if (!flags) {
+ fprintf(stdout, " ");
+ return;
+ }
+ if (flags & 0x01) {
+ if (some) strcat(flg_output, ",");
+ some = 1;
+ strcat(flg_output, "OI");
+ }
+ if (flags & 0x02) {
+ if (some) strcat(flg_output, ",");
+ some = 1;
+ strcat(flg_output, "CI");
+ }
+ if (flags & 0x04) {
+ if (some) strcat(flg_output, ",");
+ some = 1;
+ strcat(flg_output, "NP");
+ }
+ if (flags & 0x08) {
+ if (some) strcat(flg_output, ",");
+ some = 1;
+ strcat(flg_output, "IO");
+ }
+ if (flags & 0x10) {
+ if (some) strcat(flg_output, ",");
+ some = 1;
+ strcat(flg_output, "IA");
+ }
+ if (flags == 0xF) {
+ if (some) strcat(flg_output, ",");
+ some = 1;
+ strcat(flg_output, "VI");
+ }
+ fprintf(stdout, " %s", flg_output);
+}
+
+static
+void print_perms(int perms)
+{
+ fprintf(stdout, " %8X", perms);
+}
+
+static
+void print_sid(sid_t *sid)
+{
+ int i, comps = sid->auths;
+ fprintf(stdout, "S-%u-%u", sid->ver, sid->auth[5]);
+
+ for (i = 0; i < comps; i++) {
+
+ fprintf(stdout, "-%u", sid->sub_auths[i]);
+
+ }
+ fprintf(stdout, "\n");
+}
+
+static
+void print_acl(ACL *acl, const char *prefix)
+{
+ int i;
+
+ for (i = 0; i < acl->num_aces; i++) {
+ fprintf(stdout, ";;%s", prefix);
+ print_type(acl->aces[i]->type);
+ print_flags(acl->aces[i]->flags);
+ print_perms(acl->aces[i]->perms);
+ fprintf(stdout, " ");
+ print_sid(acl->aces[i]->trustee);
+ }
+}
+
+static
+int print_sec(SEC_DESC *sec_desc)
+{
+ if (!print_security) return 1;
+ fprintf(stdout, ";; SECURITY\n");
+ fprintf(stdout, ";; Owner: ");
+ print_sid(sec_desc->owner);
+ fprintf(stdout, ";; Group: ");
+ print_sid(sec_desc->group);
+ if (sec_desc->sacl) {
+ fprintf(stdout, ";; SACL:\n");
+ print_acl(sec_desc->sacl, " ");
+ }
+ if (sec_desc->dacl) {
+ fprintf(stdout, ";; DACL:\n");
+ print_acl(sec_desc->dacl, " ");
+ }
+ return 1;
+}
+
+/*
+ * Value print function here ...
+ */
+static
+int print_val(const char *path, char *val_name, int val_type, int data_len,
+ void *data_blk, int terminal, int first, int last)
+{
+ char data_asc[1024];
+
+ memset(data_asc, 0, sizeof(data_asc));
+ if (!terminal && first)
+ fprintf(stdout, "%s\n", path);
+ data_to_ascii((unsigned char *)data_blk, data_len, val_type, data_asc,
+ sizeof(data_asc) - 1);
+ fprintf(stdout, " %s = %s : %s\n", (val_name?val_name:"<No Name>"),
+ val_to_str(val_type, reg_type_names), data_asc);
+ return 1;
+}
+
+static
+void usage(void)
+{
+ fprintf(stderr, "Usage: editreg [-f] [-v] [-p] [-k] [-s] [-c <command-file>] <registryfile>\n");
+ fprintf(stderr, "Version: 0.1\n\n");
+ fprintf(stderr, "\n\t-v\t sets verbose mode");
+ fprintf(stderr, "\n\t-f\t sets full print mode where non-terminals are printed");
+ fprintf(stderr, "\n\t-p\t prints the registry");
+ fprintf(stderr, "\n\t-s\t prints security descriptors");
+ fprintf(stderr, "\n\t-c <command-file>\t specifies a command file");
+ fprintf(stderr, "\n");
+}
+
+int main(int argc, char *argv[])
+{
+ REGF *regf;
+ extern char *optarg;
+ extern int optind;
+ int opt, print_keys = 0;
+ int regf_opt = 1; /* Command name */
+ int commands = 0, modified = 0;
+ char *cmd_file_name = NULL;
+ char *out_file_name = NULL;
+ CMD_FILE *cmd_file = NULL;
+ sid_t *lsid;
+
+ if (argc < 2) {
+ usage();
+ exit(1);
+ }
+
+ /*
+ * Now, process the arguments
+ */
+
+ while ((opt = getopt(argc, argv, "fspvko:O:c:")) != EOF) {
+ switch (opt) {
+ case 'c':
+ commands = 1;
+ cmd_file_name = optarg;
+ regf_opt += 2;
+ break;
+
+ case 'f':
+ full_print = 1;
+ regf_opt++;
+ break;
+
+ case 'o':
+ out_file_name = optarg;
+ regf_opt += 2;
+ break;
+
+ case 'O':
+ def_owner_sid_str = strdup(optarg);
+ regf_opt += 2;
+ if (!sid_string_to_sid(&lsid, def_owner_sid_str)) {
+ fprintf(stderr, "Default Owner SID: %s is incorrectly formatted\n",
+ def_owner_sid_str);
+ free(&def_owner_sid_str[0]);
+ def_owner_sid_str = NULL;
+ }
+ else
+ nt_delete_sid(lsid);
+ break;
+
+ case 'p':
+ print_keys++;
+ regf_opt++;
+ break;
+
+ case 's':
+ print_security++;
+ full_print++;
+ regf_opt++;
+ break;
+
+ case 'v':
+ verbose++;
+ regf_opt++;
+ break;
+
+ case 'k':
+ regf_opt++;
+ break;
+
+ default:
+ usage();
+ exit(1);
+ break;
+ }
+ }
+
+ /*
+ * We only want to complain about the lack of a default owner SID if
+ * we need one. This approximates that need
+ */
+ if (!def_owner_sid_str) {
+ def_owner_sid_str = "S-1-5-21-1-2-3-4";
+ if (out_file_name || verbose)
+ fprintf(stderr, "Warning, default owner SID not set. Setting to %s\n",
+ def_owner_sid_str);
+ }
+
+ if ((regf = nt_create_regf()) == NULL) {
+ fprintf(stderr, "Could not create registry object: %s\n", strerror(errno));
+ exit(2);
+ }
+
+ if (regf_opt < argc) { /* We have a registry file */
+ if (!nt_set_regf_input_file(regf, argv[regf_opt])) {
+ fprintf(stderr, "Could not set name of registry file: %s, %s\n",
+ argv[regf_opt], strerror(errno));
+ exit(3);
+ }
+
+ /* Now, open it, and bring it into memory :-) */
+
+ if (nt_load_registry(regf) < 0) {
+ fprintf(stderr, "Could not load registry: %s\n", argv[1]);
+ exit(4);
+ }
+ }
+
+ if (out_file_name) {
+ if (!nt_set_regf_output_file(regf, out_file_name)) {
+ fprintf(stderr, "Could not set name of output registry file: %s, %s\n",
+ out_file_name, strerror(errno));
+ exit(3);
+ }
+
+ }
+
+ if (commands) {
+ CMD *cmd;
+
+ cmd_file = cmd_file_create(cmd_file_name);
+
+ while ((cmd = cmd_file->cmd_ops.get_cmd(cmd_file->fd)) != NULL) {
+
+ /*
+ * Now, apply the requests to the tree ...
+ */
+ switch (cmd->cmd) {
+ case CMD_ADD_KEY: {
+ REG_KEY *tmp = NULL;
+
+ tmp = nt_find_key_by_name(regf->root, cmd->key);
+
+ /* If we found it, apply the other bits, else create such a key */
+
+ if (!tmp) {
+ tmp = nt_add_reg_key(regf, cmd->key, True);
+ modified = 1;
+ }
+
+ while (cmd->val_count) {
+ VAL_SPEC_LIST *val = cmd->val_spec_list;
+ VAL_KEY *reg_val = NULL;
+
+ if (val->type == REG_TYPE_DELETE) {
+ reg_val = nt_delete_reg_value(tmp, val -> name);
+ if (reg_val) nt_delete_val_key(reg_val);
+ modified = 1;
+ }
+ else {
+ reg_val = nt_add_reg_value(tmp, val->name, val->type,
+ val->val);
+ modified = 1;
+ }
+
+ cmd->val_spec_list = val->next;
+ free_val_spec_list(val);
+ cmd->val_count--;
+ }
+
+ break;
+ }
+
+ case CMD_DEL_KEY:
+ /*
+ * Any value does not matter ...
+ * Find the key if it exists, and delete it ...
+ */
+
+ nt_delete_key_by_name(regf, cmd->key);
+ modified = 1;
+ break;
+ }
+ }
+ free_cmd(cmd);
+ }
+
+ /*
+ * At this point, we should have a registry in memory and should be able
+ * to iterate over it.
+ */
+
+ if (print_keys) {
+ nt_key_iterator(regf, regf->root, 0, "", print_key, print_sec, print_val);
+ }
+
+ /*
+ * If there was an out_file_name and the tree was modified, print it
+ */
+ if (modified && out_file_name)
+ if (!nt_store_registry(regf)) {
+ fprintf(stdout, "Error storing registry\n");
+ }
+
+ return 0;
+}
diff --git a/source/utils/log2pcaphex.c b/source/utils/log2pcaphex.c
new file mode 100644
index 00000000000..4804b993382
--- /dev/null
+++ b/source/utils/log2pcaphex.c
@@ -0,0 +1,294 @@
+/*
+ Unix SMB/CIFS implementation.
+ Utility to extract pcap files from samba (log level 10) log files
+
+ Copyright (C) Jelmer Vernooij 2003
+ Thanks to Tim Potter for the genial idea
+
+ Portions (from capconvert.c) (C) Andrew Tridgell 1997
+ Portions (from text2pcap.c) (C) Ashok Narayanan 2001
+
+ Example use with -h parameter:
+ log2pcaphex < samba-log-file | text2pcap -T 139,139 - foo.pcap
+
+ TODO: Have correct IP and TCP checksums.
+
+ This program is free software; you can 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 <assert.h>
+
+int quiet = 0;
+int hexformat = 0;
+
+#define itoa(a) ((a) < 0xa?'0'+(a):'A' + (a-0xa))
+
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/time.h>
+#include <stdio.h>
+#include <fcntl.h>
+
+#define TCPDUMP_MAGIC 0xa1b2c3d4
+
+/* tcpdump file format */
+struct tcpdump_file_header {
+ uint32 magic;
+ uint16 major;
+ uint16 minor;
+ int32 zone;
+ uint32 sigfigs;
+ uint32 snaplen;
+ uint32 linktype;
+};
+
+struct tcpdump_packet {
+ struct timeval ts;
+ uint32 caplen;
+ uint32 len;
+};
+
+typedef struct {
+ uint8 ver_hdrlen;
+ uint8 dscp;
+ uint16 packet_length;
+ uint16 identification;
+ uint8 flags;
+ uint8 fragment;
+ uint8 ttl;
+ uint8 protocol;
+ uint16 hdr_checksum;
+ uint32 src_addr;
+ uint32 dest_addr;
+} hdr_ip_t;
+
+static hdr_ip_t HDR_IP = {0x45, 0, 0, 0x3412, 0, 0, 0xff, 6, 0, 0x01010101, 0x02020202};
+
+typedef struct {
+ uint16 source_port;
+ uint16 dest_port;
+ uint32 seq_num;
+ uint32 ack_num;
+ uint8 hdr_length;
+ uint8 flags;
+ uint16 window;
+ uint16 checksum;
+ uint16 urg;
+} hdr_tcp_t;
+
+static hdr_tcp_t HDR_TCP = {139, 139, 0, 0, 0x50, 0, 0, 0, 0};
+
+void print_pcap_header(FILE *out)
+{
+ struct tcpdump_file_header h;
+ h.magic = TCPDUMP_MAGIC;
+ h.major = 2;
+ h.minor = 4;
+ h.zone = 0;
+ h.sigfigs = 0;
+ h.snaplen = 102400; /* As long packets as possible */
+ h.linktype = 101; /* Raw IP */
+ fwrite(&h, sizeof(struct tcpdump_file_header), 1, out);
+}
+
+void print_pcap_packet(FILE *out, unsigned char *data, long length, long caplen)
+{
+ static int i = 0;
+ struct tcpdump_packet p;
+ i++;
+ p.ts.tv_usec = 0;
+ p.ts.tv_sec = 0;
+ p.caplen = caplen;
+ p.len = length;
+ fwrite(&p, sizeof(struct tcpdump_packet), 1, out);
+ fwrite(data, sizeof(unsigned char), caplen, out);
+}
+
+void print_hex_packet(FILE *out, unsigned char *data, long length)
+{
+ long i,cur = 0;
+ while(cur < length) {
+ fprintf(out, "%06lX ", cur);
+ for(i = cur; i < length && i < cur + 16; i++) {
+ fprintf(out, "%02x ", data[i]);
+ }
+
+ cur = i;
+ fprintf(out, "\n");
+ }
+}
+
+void print_netbios_packet(FILE *out, unsigned char *data, long length, long actual_length)
+{
+ unsigned char *newdata; long offset = 0;
+ long newlen;
+
+ newlen = length+sizeof(HDR_IP)+sizeof(HDR_TCP);
+ newdata = malloc(newlen);
+
+ HDR_IP.packet_length = htons(newlen);
+ HDR_TCP.window = htons(0x2000);
+ HDR_TCP.source_port = HDR_TCP.dest_port = htons(139);
+
+ memcpy(newdata+offset, &HDR_IP, sizeof(HDR_IP));offset+=sizeof(HDR_IP);
+ memcpy(newdata+offset, &HDR_TCP, sizeof(HDR_TCP));offset+=sizeof(HDR_TCP);
+ memcpy(newdata+offset,data,length);
+
+ print_pcap_packet(out, newdata, newlen, actual_length+offset);
+ free(newdata);
+}
+
+unsigned char *curpacket = NULL;
+long curpacket_len = 0;
+
+void read_log_msg(FILE *in, unsigned char **_buffer, long *buffersize, long *data_offset, long *data_length)
+{
+ unsigned char *buffer;
+ int tmp; long i;
+ assert(fscanf(in, " size=%ld\n", buffersize));
+ *buffersize+=4; /* for netbios */
+ buffer = malloc(*buffersize);
+ memset(buffer, 0, *buffersize);
+ /* NetBIOS */
+ buffer[0] = 0x00;
+ buffer[1] = 0x00;
+ memcpy(buffer+2, &buffersize, 2);
+ buffer[4] = 0xFF;
+ buffer[5] = 'S';
+ buffer[6] = 'M';
+ buffer[7] = 'B';
+ assert(fscanf(in, " smb_com=0x%x\n", &tmp)); buffer[smb_com] = tmp;
+ assert(fscanf(in, " smb_rcls=%d\n", &tmp)); buffer[smb_rcls] = tmp;
+ assert(fscanf(in, " smb_reh=%d\n", &tmp)); buffer[smb_reh] = tmp;
+ assert(fscanf(in, " smb_err=%d\n", &tmp)); memcpy(buffer+smb_err, &tmp, 2);
+ assert(fscanf(in, " smb_flg=%d\n", &tmp)); buffer[smb_flg] = tmp;
+ assert(fscanf(in, " smb_flg2=%d\n", &tmp)); memcpy(buffer+smb_flg2, &tmp, 2);
+ assert(fscanf(in, " smb_tid=%d\n", &tmp)); memcpy(buffer+smb_tid, &tmp, 2);
+ assert(fscanf(in, " smb_pid=%d\n", &tmp)); memcpy(buffer+smb_pid, &tmp, 2);
+ assert(fscanf(in, " smb_uid=%d\n", &tmp)); memcpy(buffer+smb_uid, &tmp, 2);
+ assert(fscanf(in, " smb_mid=%d\n", &tmp)); memcpy(buffer+smb_mid, &tmp, 2);
+ assert(fscanf(in, " smt_wct=%d\n", &tmp)); buffer[smb_wct] = tmp;
+ for(i = 0; i < buffer[smb_wct]; i++) {
+ assert(fscanf(in, " smb_vwv[%*2d]=%*5d (0x%X)\n", &tmp));
+ memcpy(buffer+smb_vwv+i*2, &tmp, 2);
+ }
+
+ *data_offset = smb_vwv+buffer[smb_wct]*2;
+ assert(fscanf(in, " smb_bcc=%ld\n", data_length)); buffer[(*data_offset)] = *data_length;
+ (*data_offset)+=2;
+ *_buffer = buffer;
+}
+
+long read_log_data(FILE *in, unsigned char *buffer, long data_length)
+{
+ long i, addr; char real[2][16]; int ret;
+ unsigned char tmp;
+ for(i = 0; i < data_length; i++) {
+ if(i % 16 == 0){
+ if(i != 0) { /* Read data after each line */
+ assert(fscanf(in, "%8s %8s", real[0], real[1]) == 2);
+ }
+ ret = fscanf(in, " [%03lX]", &addr);
+ if(!ret) {
+ if(!quiet)fprintf(stderr, "Only first %ld bytes are logged, packet trace will be incomplete\nTry a higher log level\n", i);
+ return i-1;
+ }
+ assert(addr == i);
+ }
+ if(!fscanf(in, "%02lX", &tmp)) {
+ if(!quiet)fprintf(stderr, "Only first %ld bytes are logged, packet trace will be incomplete\nTry a higher log level\n", i-1);
+ return i-1;
+ }
+ buffer[i] = tmp;
+ }
+ return data_length;
+}
+
+int main (int argc, char **argv)
+{
+ const char *infile, *outfile;
+ FILE *out, *in;
+ int opt;
+ poptContext pc;
+ char buffer[4096];
+ long data_offset, data_length;
+ long data_bytes_read;
+ int in_packet = 0;
+ struct poptOption long_options[] = {
+ POPT_AUTOHELP
+ { "quiet", 'q', POPT_ARG_NONE, &quiet, 0, "Be quiet, don't output warnings" },
+ { "hex", 'h', POPT_ARG_NONE, &hexformat, 0, "Output format readable by text2pcap" },
+ POPT_TABLEEND
+ };
+
+ pc = poptGetContext(NULL, argc, (const char **) argv, long_options,
+ POPT_CONTEXT_KEEP_FIRST);
+ poptSetOtherOptionHelp(pc, "[<infile> [<outfile>]]");
+
+
+ while((opt = poptGetNextOpt(pc)) != -1) {
+ switch (opt) {
+ }
+ }
+
+ poptGetArg(pc); /* Drop argv[0], the program name */
+
+ infile = poptGetArg(pc);
+
+ if(infile) {
+ in = fopen(infile, "r");
+ if(!in) {
+ perror("fopen");
+ return 1;
+ }
+ } else in = stdin;
+
+ outfile = poptGetArg(pc);
+
+ if(outfile) {
+ out = fopen(outfile, "w+");
+ if(!out) {
+ perror("fopen");
+ fprintf(stderr, "Can't find %s, using stdout...\n", outfile);
+ }
+ }
+
+ if(!outfile) out = stdout;
+
+ if(!hexformat)print_pcap_header(out);
+
+ while(!feof(in)) {
+ fgets(buffer, sizeof(buffer), in);
+ if(buffer[0] == '[') { /* Header */
+ if(strstr(buffer, "show_msg")) {
+ in_packet++;
+ if(in_packet == 1)continue;
+ read_log_msg(in, &curpacket, &curpacket_len, &data_offset, &data_length);
+ } else if(in_packet && strstr(buffer, "dump_data")) {
+ data_bytes_read = read_log_data(in, curpacket+data_offset, data_length);
+ } else {
+ if(in_packet){
+ if(hexformat) print_hex_packet(out, curpacket, curpacket_len);
+ else print_netbios_packet(out, curpacket, curpacket_len, data_bytes_read+data_offset);
+ free(curpacket);
+ }
+ in_packet = 0;
+ }
+ }
+ }
+
+ return 0;
+}
diff --git a/source/utils/net.c b/source/utils/net.c
new file mode 100644
index 00000000000..e4484488b61
--- /dev/null
+++ b/source/utils/net.c
@@ -0,0 +1,796 @@
+/*
+ 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)
+
+ 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 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. */
+
+/*****************************************************/
+/* */
+/* 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.h"
+
+/***********************************************************************/
+/* Beginning of internationalization section. Translatable constants */
+/* should be kept in this area and referenced in the rest of the code. */
+/* */
+/* No functions, outside of Samba or LSB (Linux Standards Base) should */
+/* be used (if possible). */
+/***********************************************************************/
+
+#define YES_STRING "Yes"
+#define NO_STRING "No"
+
+/************************************************************************************/
+/* end of internationalization section */
+/************************************************************************************/
+
+/* Yes, these buggers are globals.... */
+const char *opt_requester_name = NULL;
+const char *opt_host = NULL;
+const char *opt_password = NULL;
+const char *opt_user_name = NULL;
+BOOL opt_user_specified = False;
+const char *opt_workgroup = NULL;
+int opt_long_list_entries = 0;
+int opt_reboot = 0;
+int opt_force = 0;
+int opt_port = 0;
+int opt_maxusers = -1;
+const char *opt_comment = "";
+const char *opt_container = "cn=Users";
+int opt_flags = -1;
+int opt_timeout = 0;
+const char *opt_target_workgroup = NULL;
+int opt_machine_pass = 0;
+BOOL opt_localgroup = False;
+BOOL opt_domaingroup = False;
+const char *opt_newntname = "";
+int opt_rid = 0;
+
+BOOL opt_have_ip = False;
+struct in_addr opt_dest_ip;
+
+extern BOOL AllowDebugChange;
+
+uint32 get_sec_channel_type(const char *param)
+{
+ if (!(param && *param)) {
+ return get_default_sec_channel();
+ } else {
+ if (strequal(param, "PDC")) {
+ return SEC_CHAN_BDC;
+ } else if (strequal(param, "BDC")) {
+ return SEC_CHAN_BDC;
+ } else if (strequal(param, "MEMBER")) {
+ return SEC_CHAN_WKSTA;
+#if 0
+ } else if (strequal(param, "DOMAIN")) {
+ return SEC_CHAN_DOMAIN;
+#endif
+ } else {
+ return get_default_sec_channel();
+ }
+ }
+}
+
+/*
+ run a function from a function table. If not found then
+ call the specified usage function
+*/
+int net_run_function(int argc, const char **argv, struct functable *table,
+ int (*usage_fn)(int argc, const char **argv))
+{
+ int i;
+
+ if (argc < 1) {
+ d_printf("\nUsage: \n");
+ return usage_fn(argc, argv);
+ }
+ for (i=0; table[i].funcname; i++) {
+ if (StrCaseCmp(argv[0], table[i].funcname) == 0)
+ return table[i].fn(argc-1, argv+1);
+ }
+ d_printf("No command: %s\n", argv[0]);
+ return usage_fn(argc, argv);
+}
+
+
+/****************************************************************************
+connect to \\server\ipc$
+****************************************************************************/
+NTSTATUS connect_to_ipc(struct cli_state **c, struct in_addr *server_ip,
+ const char *server_name)
+{
+ NTSTATUS nt_status;
+
+ if (!opt_password && !opt_machine_pass) {
+ char *pass = getpass("Password:");
+ if (pass) {
+ opt_password = strdup(pass);
+ }
+ }
+
+ nt_status = cli_full_connection(c, opt_requester_name, server_name,
+ server_ip, opt_port,
+ "IPC$", "IPC",
+ opt_user_name, opt_workgroup,
+ opt_password, 0, Undefined, NULL);
+
+ if (NT_STATUS_IS_OK(nt_status)) {
+ return nt_status;
+ } else {
+ d_printf("Could not connect to server %s\n", server_name);
+
+ /* Display a nicer message depending on the result */
+
+ if (NT_STATUS_V(nt_status) ==
+ NT_STATUS_V(NT_STATUS_LOGON_FAILURE))
+ d_printf("The username or password was not correct.\n");
+
+ if (NT_STATUS_V(nt_status) ==
+ NT_STATUS_V(NT_STATUS_ACCOUNT_LOCKED_OUT))
+ d_printf("The account was locked out.\n");
+
+ if (NT_STATUS_V(nt_status) ==
+ NT_STATUS_V(NT_STATUS_ACCOUNT_DISABLED))
+ d_printf("The account was disabled.\n");
+
+ return nt_status;
+ }
+}
+
+/****************************************************************************
+connect to \\server\ipc$ anonymously
+****************************************************************************/
+NTSTATUS connect_to_ipc_anonymous(struct cli_state **c,
+ struct in_addr *server_ip, const char *server_name)
+{
+ NTSTATUS nt_status;
+
+ nt_status = cli_full_connection(c, opt_requester_name, server_name,
+ server_ip, opt_port,
+ "IPC$", "IPC",
+ "", "",
+ "", 0, Undefined, NULL);
+
+ if (NT_STATUS_IS_OK(nt_status)) {
+ return nt_status;
+ } else {
+ DEBUG(1,("Cannot connect to server (anonymously). Error was %s\n", nt_errstr(nt_status)));
+ return nt_status;
+ }
+}
+
+/****************************************************************************
+ Use the local machine's password for this session
+****************************************************************************/
+int net_use_machine_password(void)
+{
+ char *user_name = NULL;
+
+ if (!secrets_init()) {
+ d_printf("ERROR: Unable to open secrets database\n");
+ exit(1);
+ }
+
+ user_name = NULL;
+ opt_password = secrets_fetch_machine_password(opt_target_workgroup, NULL, NULL);
+ if (asprintf(&user_name, "%s$@%s", global_myname(), lp_realm()) == -1) {
+ return -1;
+ }
+ opt_user_name = user_name;
+ return 0;
+}
+
+BOOL net_find_server(unsigned flags, struct in_addr *server_ip, char **server_name)
+{
+
+ if (opt_host) {
+ *server_name = strdup(opt_host);
+ }
+
+ if (opt_have_ip) {
+ *server_ip = opt_dest_ip;
+ if (!*server_name) {
+ *server_name = strdup(inet_ntoa(opt_dest_ip));
+ }
+ } else if (*server_name) {
+ /* resolve the IP address */
+ if (!resolve_name(*server_name, server_ip, 0x20)) {
+ DEBUG(1,("Unable to resolve server name\n"));
+ return False;
+ }
+ } else if (flags & NET_FLAGS_PDC) {
+ struct in_addr pdc_ip;
+
+ if (get_pdc_ip(opt_target_workgroup, &pdc_ip)) {
+ fstring dc_name;
+
+ if (is_zero_ip(pdc_ip))
+ return False;
+
+ if ( !name_status_find(opt_target_workgroup, 0x1b, 0x20, pdc_ip, dc_name) )
+ return False;
+
+ *server_name = strdup(dc_name);
+ *server_ip = pdc_ip;
+ }
+
+ } else if (flags & NET_FLAGS_DMB) {
+ struct in_addr msbrow_ip;
+ /* if (!resolve_name(MSBROWSE, &msbrow_ip, 1)) */
+ if (!resolve_name(opt_target_workgroup, &msbrow_ip, 0x1B)) {
+ DEBUG(1,("Unable to resolve domain browser via name lookup\n"));
+ return False;
+ } else {
+ *server_ip = msbrow_ip;
+ }
+ *server_name = strdup(inet_ntoa(opt_dest_ip));
+ } else if (flags & NET_FLAGS_MASTER) {
+ struct in_addr brow_ips;
+ if (!resolve_name(opt_target_workgroup, &brow_ips, 0x1D)) {
+ /* go looking for workgroups */
+ DEBUG(1,("Unable to resolve master browser via name lookup\n"));
+ return False;
+ } else {
+ *server_ip = brow_ips;
+ }
+ *server_name = strdup(inet_ntoa(opt_dest_ip));
+ } else if (!(flags & NET_FLAGS_LOCALHOST_DEFAULT_INSANE)) {
+ extern struct in_addr loopback_ip;
+ *server_ip = loopback_ip;
+ *server_name = strdup("127.0.0.1");
+ }
+
+ if (!server_name || !*server_name) {
+ DEBUG(1,("no server to connect to\n"));
+ return False;
+ }
+
+ return True;
+}
+
+
+BOOL net_find_pdc(struct in_addr *server_ip, fstring server_name, const char *domain_name)
+{
+ if (get_pdc_ip(domain_name, server_ip)) {
+ if (is_zero_ip(*server_ip))
+ return False;
+
+ if (!name_status_find(domain_name, 0x1b, 0x20, *server_ip, server_name))
+ return False;
+
+ return True;
+ }
+ else
+ return False;
+}
+
+
+struct cli_state *net_make_ipc_connection(unsigned flags)
+{
+ char *server_name = NULL;
+ struct in_addr server_ip;
+ struct cli_state *cli = NULL;
+ NTSTATUS nt_status;
+
+ if (!net_find_server(flags, &server_ip, &server_name)) {
+ d_printf("\nUnable to find a suitable server\n");
+ return NULL;
+ }
+
+ if (flags & NET_FLAGS_ANONYMOUS) {
+ nt_status = connect_to_ipc_anonymous(&cli, &server_ip, server_name);
+ } else {
+ nt_status = connect_to_ipc(&cli, &server_ip, server_name);
+ }
+
+ SAFE_FREE(server_name);
+ if (NT_STATUS_IS_OK(nt_status)) {
+ return cli;
+ } else {
+ return NULL;
+ }
+}
+
+static int net_user(int argc, const char **argv)
+{
+ if (net_ads_check() == 0)
+ return net_ads_user(argc, argv);
+
+ /* if server is not specified, default to PDC? */
+ if (net_rpc_check(NET_FLAGS_PDC))
+ return net_rpc_user(argc, argv);
+
+ return net_rap_user(argc, argv);
+}
+
+static int net_group(int argc, const char **argv)
+{
+ if (net_ads_check() == 0)
+ return net_ads_group(argc, argv);
+
+ if (argc == 0 && net_rpc_check(NET_FLAGS_PDC))
+ return net_rpc_group(argc, argv);
+
+ return net_rap_group(argc, argv);
+}
+
+static int net_join(int argc, const char **argv)
+{
+ if (net_ads_check() == 0) {
+ if (net_ads_join(argc, argv) == 0)
+ return 0;
+ else
+ d_printf("ADS join did not work, falling back to RPC...\n");
+ }
+ return net_rpc_join(argc, argv);
+}
+
+static int net_changetrustpw(int argc, const char **argv)
+{
+ if (net_ads_check() == 0)
+ return net_ads_changetrustpw(argc, argv);
+
+ return net_rpc_changetrustpw(argc, argv);
+}
+
+static int net_changesecretpw(int argc, const char **argv)
+{
+ char *trust_pw;
+ uint32 sec_channel_type = SEC_CHAN_WKSTA;
+
+ if(opt_force) {
+ trust_pw = getpass("Enter machine password: ");
+
+ if (!secrets_store_machine_password(trust_pw, lp_workgroup(), sec_channel_type)) {
+ d_printf("Unable to write the machine account password in the secrets database");
+ return 1;
+ }
+ else {
+ d_printf("Modified trust account password in secrets database\n");
+ }
+ }
+ else {
+ d_printf("Machine account password change requires the -f flag.\n");
+ d_printf("Do NOT use this function unless you know what it does!\n");
+ d_printf("This function will change the ADS Domain member machine account password in the secrets.tdb file!\n");
+ }
+
+ return 0;
+}
+
+static int net_share(int argc, const char **argv)
+{
+ if (net_rpc_check(0))
+ return net_rpc_share(argc, argv);
+ return net_rap_share(argc, argv);
+}
+
+static int net_file(int argc, const char **argv)
+{
+ if (net_rpc_check(0))
+ return net_rpc_file(argc, argv);
+ return net_rap_file(argc, argv);
+}
+
+/*
+ Retrieve our local SID or the SID for the specified name
+ */
+static int net_getlocalsid(int argc, const char **argv)
+{
+ DOM_SID sid;
+ const char *name;
+ fstring sid_str;
+
+ if (argc >= 1) {
+ name = argv[0];
+ }
+ else {
+ name = global_myname();
+ }
+
+ if(!initialize_password_db(False)) {
+ DEBUG(0, ("WARNING: Could not open passdb - local sid may not reflect passdb\n"
+ "backend knowlege (such as the sid stored in LDAP)\n"));
+ }
+
+ /* Generate one, if it doesn't exist */
+ get_global_sam_sid();
+
+ if (!secrets_fetch_domain_sid(name, &sid)) {
+ DEBUG(0, ("Can't fetch domain SID for name: %s\n", name));
+ return 1;
+ }
+ sid_to_string(sid_str, &sid);
+ d_printf("SID for domain %s is: %s\n", name, sid_str);
+ return 0;
+}
+
+static int net_setlocalsid(int argc, const char **argv)
+{
+ DOM_SID sid;
+
+ if ( (argc != 1)
+ || (strncmp(argv[0], "S-1-5-21-", strlen("S-1-5-21-")) != 0)
+ || (!string_to_sid(&sid, argv[0]))
+ || (sid.num_auths != 4)) {
+ d_printf("usage: net setlocalsid S-1-5-21-x-y-z\n");
+ return 1;
+ }
+
+ if (!secrets_store_domain_sid(global_myname(), &sid)) {
+ DEBUG(0,("Can't store domain SID as a pdc/bdc.\n"));
+ return 1;
+ }
+
+ return 0;
+}
+
+static int net_getdomainsid(int argc, const char **argv)
+{
+ DOM_SID domain_sid;
+ fstring sid_str;
+
+ if(!initialize_password_db(False)) {
+ DEBUG(0, ("WARNING: Could not open passdb - domain sid may not reflect passdb\n"
+ "backend knowlege (such as the sid stored in LDAP)\n"));
+ }
+
+ /* Generate one, if it doesn't exist */
+ get_global_sam_sid();
+
+ if (!secrets_fetch_domain_sid(global_myname(), &domain_sid)) {
+ d_printf("Could not fetch local SID\n");
+ return 1;
+ }
+ sid_to_string(sid_str, &domain_sid);
+ d_printf("SID for domain %s is: %s\n", global_myname(), sid_str);
+
+ if (!secrets_fetch_domain_sid(opt_workgroup, &domain_sid)) {
+ d_printf("Could not fetch domain SID\n");
+ return 1;
+ }
+
+ sid_to_string(sid_str, &domain_sid);
+ d_printf("SID for domain %s is: %s\n", opt_workgroup, sid_str);
+
+ return 0;
+}
+
+#ifdef WITH_FAKE_KASERVER
+
+int net_afskey_usage(int argc, const char **argv)
+{
+ d_printf(" net afskey filename\n"
+ "\tImports a OpenAFS KeyFile into our secrets.tdb\n\n");
+ return -1;
+}
+
+static int net_afskey(int argc, const char **argv)
+{
+ int fd;
+ struct afs_keyfile keyfile;
+
+ if (argc != 2) {
+ d_printf("usage: 'net afskey <keyfile> cell'\n");
+ return -1;
+ }
+
+ if (!secrets_init()) {
+ d_printf("Could not open secrets.tdb\n");
+ return -1;
+ }
+
+ if ((fd = open(argv[0], O_RDONLY, 0)) < 0) {
+ d_printf("Could not open %s\n", argv[0]);
+ return -1;
+ }
+
+ if (read(fd, &keyfile, sizeof(keyfile)) != sizeof(keyfile)) {
+ d_printf("Could not read keyfile\n");
+ return -1;
+ }
+
+ if (!secrets_store_afs_keyfile(argv[1], &keyfile)) {
+ d_printf("Could not write keyfile to secrets.tdb\n");
+ return -1;
+ }
+
+ return 0;
+}
+
+#endif /* WITH_FAKE_KASERVER */
+
+static uint32 get_maxrid(void)
+{
+ SAM_ACCOUNT *pwd = NULL;
+ uint32 max_rid = 0;
+ GROUP_MAP *map = NULL;
+ int num_entries = 0;
+ int i;
+
+ if (!pdb_setsampwent(False)) {
+ DEBUG(0, ("load_sampwd_entries: Unable to open passdb.\n"));
+ return 0;
+ }
+
+ for (; (NT_STATUS_IS_OK(pdb_init_sam(&pwd)))
+ && pdb_getsampwent(pwd) == True; pwd=NULL) {
+ uint32 rid;
+
+ if (!sid_peek_rid(pdb_get_user_sid(pwd), &rid)) {
+ DEBUG(0, ("can't get RID for user '%s'\n",
+ pdb_get_username(pwd)));
+ pdb_free_sam(&pwd);
+ continue;
+ }
+
+ if (rid > max_rid)
+ max_rid = rid;
+
+ DEBUG(1,("%d is user '%s'\n", rid, pdb_get_username(pwd)));
+ pdb_free_sam(&pwd);
+ }
+
+ pdb_endsampwent();
+ pdb_free_sam(&pwd);
+
+ if (!pdb_enum_group_mapping(SID_NAME_UNKNOWN, &map, &num_entries,
+ ENUM_ONLY_MAPPED))
+ return max_rid;
+
+ for (i = 0; i < num_entries; i++) {
+ uint32 rid;
+
+ if (!sid_peek_check_rid(get_global_sam_sid(), &map[i].sid,
+ &rid)) {
+ DEBUG(3, ("skipping map for group '%s', SID %s\n",
+ map[i].nt_name,
+ sid_string_static(&map[i].sid)));
+ continue;
+ }
+ DEBUG(1,("%d is group '%s'\n", rid, map[i].nt_name));
+
+ if (rid > max_rid)
+ max_rid = rid;
+ }
+
+ SAFE_FREE(map);
+
+ return max_rid;
+}
+
+static int net_maxrid(int argc, const char **argv)
+{
+ uint32 rid;
+
+ if (argc != 0) {
+ DEBUG(0, ("usage: net maxrid\n"));
+ return 1;
+ }
+
+ if ((rid = get_maxrid()) == 0) {
+ DEBUG(0, ("can't get current maximum rid\n"));
+ return 1;
+ }
+
+ d_printf("Currently used maximum rid: %d\n", rid);
+
+ return 0;
+}
+
+/* main function table */
+static struct functable net_func[] = {
+ {"RPC", net_rpc},
+ {"RAP", net_rap},
+ {"ADS", net_ads},
+
+ /* eventually these should auto-choose the transport ... */
+ {"FILE", net_file},
+ {"SHARE", net_share},
+ {"SESSION", net_rap_session},
+ {"SERVER", net_rap_server},
+ {"DOMAIN", net_rap_domain},
+ {"PRINTQ", net_rap_printq},
+ {"USER", net_user},
+ {"GROUP", net_group},
+ {"GROUPMAP", net_groupmap},
+ {"VALIDATE", net_rap_validate},
+ {"GROUPMEMBER", net_rap_groupmember},
+ {"ADMIN", net_rap_admin},
+ {"SERVICE", net_rap_service},
+ {"PASSWORD", net_rap_password},
+ {"CHANGETRUSTPW", net_changetrustpw},
+ {"CHANGESECRETPW", net_changesecretpw},
+ {"TIME", net_time},
+ {"LOOKUP", net_lookup},
+ {"JOIN", net_join},
+ {"CACHE", net_cache},
+ {"GETLOCALSID", net_getlocalsid},
+ {"SETLOCALSID", net_setlocalsid},
+ {"GETDOMAINSID", net_getdomainsid},
+ {"MAXRID", net_maxrid},
+ {"IDMAP", net_idmap},
+ {"STATUS", net_status},
+#ifdef WITH_FAKE_KASERVER
+ {"AFSKEY", net_afskey},
+#endif
+ {"PRIV", net_priv},
+
+ {"HELP", net_help},
+ {NULL, NULL}
+};
+
+
+/****************************************************************************
+ main program
+****************************************************************************/
+ int main(int argc, const char **argv)
+{
+ int opt,i;
+ char *p;
+ int rc = 0;
+ int argc_new = 0;
+ const char ** argv_new;
+ poptContext pc;
+
+ struct poptOption long_options[] = {
+ {"help", 'h', POPT_ARG_NONE, 0, 'h'},
+ {"workgroup", 'w', POPT_ARG_STRING, &opt_target_workgroup},
+ {"user", 'U', POPT_ARG_STRING, &opt_user_name, 'U'},
+ {"ipaddress", 'I', POPT_ARG_STRING, 0,'I'},
+ {"port", 'p', POPT_ARG_INT, &opt_port},
+ {"myname", 'n', POPT_ARG_STRING, &opt_requester_name},
+ {"server", 'S', POPT_ARG_STRING, &opt_host},
+ {"container", 'c', POPT_ARG_STRING, &opt_container},
+ {"comment", 'C', POPT_ARG_STRING, &opt_comment},
+ {"maxusers", 'M', POPT_ARG_INT, &opt_maxusers},
+ {"flags", 'F', POPT_ARG_INT, &opt_flags},
+ {"long", 'l', POPT_ARG_NONE, &opt_long_list_entries},
+ {"reboot", 'r', POPT_ARG_NONE, &opt_reboot},
+ {"force", 'f', POPT_ARG_NONE, &opt_force},
+ {"timeout", 't', POPT_ARG_INT, &opt_timeout},
+ {"machine-pass",'P', POPT_ARG_NONE, &opt_machine_pass},
+ {"myworkgroup", 'W', POPT_ARG_STRING, &opt_workgroup},
+
+ /* Options for 'net groupmap set' */
+ {"local", 'L', POPT_ARG_NONE, &opt_localgroup},
+ {"domain", 'D', POPT_ARG_NONE, &opt_domaingroup},
+ {"ntname", 'N', POPT_ARG_STRING, &opt_newntname},
+ {"rid", 'R', POPT_ARG_INT, &opt_rid},
+
+ POPT_COMMON_SAMBA
+ { 0, 0, 0, 0}
+ };
+
+ zero_ip(&opt_dest_ip);
+
+ /* set default debug level to 0 regardless of what smb.conf sets */
+ DEBUGLEVEL_CLASS[DBGC_ALL] = 0;
+ dbf = x_stderr;
+
+ pc = poptGetContext(NULL, argc, (const char **) argv, long_options,
+ POPT_CONTEXT_KEEP_FIRST);
+
+ while((opt = poptGetNextOpt(pc)) != -1) {
+ switch (opt) {
+ case 'h':
+ net_help(argc, argv);
+ exit(0);
+ break;
+ case 'I':
+ opt_dest_ip = *interpret_addr2(poptGetOptArg(pc));
+ if (is_zero_ip(opt_dest_ip))
+ d_printf("\nInvalid ip address specified\n");
+ else
+ opt_have_ip = True;
+ break;
+ case 'U':
+ opt_user_specified = True;
+ opt_user_name = strdup(opt_user_name);
+ p = strchr(opt_user_name,'%');
+ if (p) {
+ *p = 0;
+ opt_password = p+1;
+ }
+ break;
+ default:
+ d_printf("\nInvalid option %s: %s\n",
+ poptBadOption(pc, 0), poptStrerror(opt));
+ net_help(argc, argv);
+ exit(1);
+ }
+ }
+
+ /*
+ * Don't load debug level from smb.conf. It should be
+ * set by cmdline arg or remain default (0)
+ */
+ AllowDebugChange = False;
+ lp_load(dyn_CONFIGFILE,True,False,False);
+
+ 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 (!opt_requester_name) {
+ static fstring myname;
+ get_myname(myname);
+ opt_requester_name = myname;
+ }
+
+ if (!opt_user_name && getenv("LOGNAME")) {
+ opt_user_name = getenv("LOGNAME");
+ }
+
+ if (!opt_workgroup) {
+ opt_workgroup = smb_xstrdup(lp_workgroup());
+ }
+
+ if (!opt_target_workgroup) {
+ opt_target_workgroup = smb_xstrdup(lp_workgroup());
+ }
+
+ if (!init_names())
+ exit(1);
+
+ load_interfaces();
+
+ /* this makes sure that when we do things like call scripts,
+ that it won't assert becouse we are not root */
+ sec_init();
+
+ if (opt_machine_pass) {
+ /* it is very useful to be able to make ads queries as the
+ machine account for testing purposes and for domain leave */
+
+ net_use_machine_password();
+ }
+
+ if (!opt_password) {
+ opt_password = getenv("PASSWD");
+ }
+
+ rc = net_run_function(argc_new-1, argv_new+1, net_func, net_help);
+
+ DEBUG(2,("return code = %d\n", rc));
+ return rc;
+}
diff --git a/source/utils/net.h b/source/utils/net.h
new file mode 100644
index 00000000000..62d5a742375
--- /dev/null
+++ b/source/utils/net.h
@@ -0,0 +1,69 @@
+/*
+ Samba Unix/Linux SMB client library
+ Distributed SMB/CIFS Server Management Utility
+ Copyright (C) 2001 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 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 "../utils/net_proto.h"
+
+#define NET_FLAGS_MASTER 1
+#define NET_FLAGS_DMB 2
+
+/* Would it be insane to set 'localhost' as the default
+ remote host for this operation?
+
+ For example, localhost is insane for a 'join' operation.
+*/
+#define NET_FLAGS_LOCALHOST_DEFAULT_INSANE 4
+
+/* We want to find the PDC only */
+#define NET_FLAGS_PDC 8
+
+/* We want an anonymous connection */
+#define NET_FLAGS_ANONYMOUS 16
+
+/* don't open an RPC pipe */
+#define NET_FLAGS_NO_PIPE 32
+
+extern int opt_maxusers;
+extern const char *opt_comment;
+extern const char *opt_container;
+extern int opt_flags;
+
+extern const char *opt_comment;
+
+extern const char *opt_target_workgroup;
+extern const char *opt_workgroup;
+extern int opt_long_list_entries;
+extern int opt_reboot;
+extern int opt_force;
+extern int opt_machine_pass;
+extern int opt_timeout;
+extern const char *opt_host;
+extern const char *opt_user_name;
+extern const char *opt_password;
+extern BOOL opt_user_specified;
+
+extern BOOL opt_localgroup;
+extern BOOL opt_domaingroup;
+extern const char *opt_newntname;
+extern int opt_rid;
+
+extern BOOL opt_have_ip;
+extern struct in_addr opt_dest_ip;
+
+extern const char *share_type[];
+
diff --git a/source/utils/net_ads.c b/source/utils/net_ads.c
new file mode 100644
index 00000000000..6eec71aedf3
--- /dev/null
+++ b/source/utils/net_ads.c
@@ -0,0 +1,1328 @@
+/*
+ Samba Unix/Linux SMB client library
+ net ads commands
+ Copyright (C) 2001 Andrew Tridgell (tridge@samba.org)
+ Copyright (C) 2001 Remus Koos (remuskoos@yahoo.com)
+ Copyright (C) 2002 Jim McDonough (jmcd@us.ibm.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 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 "../utils/net.h"
+
+#ifdef HAVE_ADS
+
+int net_ads_usage(int argc, const char **argv)
+{
+ d_printf(
+"\nnet ads join <org_unit>"\
+"\n\tjoins the local machine to a ADS realm\n"\
+"\nnet ads leave"\
+"\n\tremoves the local machine from a ADS realm\n"\
+"\nnet ads testjoin"\
+"\n\ttests that an exiting join is OK\n"\
+"\nnet ads user"\
+"\n\tlist, add, or delete users in the realm\n"\
+"\nnet ads group"\
+"\n\tlist, add, or delete groups in the realm\n"\
+"\nnet ads info"\
+"\n\tshows some info on the server\n"\
+"\nnet ads status"\
+"\n\tdump the machine account details to stdout\n"
+"\nnet ads lookup"\
+"\n\tperform a CLDAP search on the server\n"
+"\nnet ads password <username@realm> <password> -Uadmin_username@realm%%admin_pass"\
+"\n\tchange a user's password using an admin account"\
+"\n\t(note: use realm in UPPERCASE, prompts if password is obmitted)\n"\
+"\nnet ads changetrustpw"\
+"\n\tchange the trust account password of this machine in the AD tree\n"\
+"\nnet ads printer [info | publish | remove] <printername> <servername>"\
+"\n\t lookup, add, or remove directory entry for a printer\n"\
+"\nnet ads search"\
+"\n\tperform a raw LDAP search and dump the results\n"
+"\nnet ads dn"\
+"\n\tperform a raw LDAP search and dump attributes of a particular DN\n"
+ );
+ return -1;
+}
+
+
+/*
+ this implements the CLDAP based netlogon lookup requests
+ for finding the domain controller of a ADS domain
+*/
+static int net_ads_lookup(int argc, const char **argv)
+{
+ ADS_STRUCT *ads;
+
+ ads = ads_init(NULL, opt_target_workgroup, opt_host);
+ if (ads) {
+ ads->auth.flags |= ADS_AUTH_NO_BIND;
+ }
+
+ ads_connect(ads);
+
+ if (!ads || !ads->config.realm) {
+ d_printf("Didn't find the cldap server!\n");
+ return -1;
+ }
+
+ return ads_cldap_netlogon(ads);
+}
+
+
+
+static int net_ads_info(int argc, const char **argv)
+{
+ ADS_STRUCT *ads;
+
+ ads = ads_init(NULL, opt_target_workgroup, opt_host);
+
+ if (ads) {
+ ads->auth.flags |= ADS_AUTH_NO_BIND;
+ }
+
+ ads_connect(ads);
+
+ if (!ads || !ads->config.realm) {
+ d_printf("Didn't find the ldap server!\n");
+ return -1;
+ }
+
+ d_printf("LDAP server: %s\n", inet_ntoa(ads->ldap_ip));
+ d_printf("LDAP server name: %s\n", ads->config.ldap_server_name);
+ d_printf("Realm: %s\n", ads->config.realm);
+ d_printf("Bind Path: %s\n", ads->config.bind_path);
+ d_printf("LDAP port: %d\n", ads->ldap_port);
+ d_printf("Server time: %s\n", http_timestring(ads->config.current_time));
+
+ d_printf("KDC server: %s\n", ads->auth.kdc_server );
+ d_printf("Server time offset: %d\n", ads->auth.time_offset );
+
+ return 0;
+}
+
+static void use_in_memory_ccache(void) {
+ /* Use in-memory credentials cache so we do not interfere with
+ * existing credentials */
+ setenv(KRB5_ENV_CCNAME, "MEMORY:net_ads", 1);
+}
+
+static ADS_STRUCT *ads_startup(void)
+{
+ ADS_STRUCT *ads;
+ ADS_STATUS status;
+ BOOL need_password = False;
+ BOOL second_time = False;
+ char *cp;
+
+ /* lp_realm() should be handled by a command line param,
+ However, the join requires that realm be set in smb.conf
+ and compares our realm with the remote server's so this is
+ ok until someone needs more flexibility */
+
+ ads = ads_init(lp_realm(), opt_target_workgroup, opt_host);
+
+ if (!opt_user_name) {
+ opt_user_name = "administrator";
+ }
+
+ if (opt_user_specified) {
+ need_password = True;
+ }
+
+retry:
+ if (!opt_password && need_password && !opt_machine_pass) {
+ char *prompt;
+ asprintf(&prompt,"%s's password: ", opt_user_name);
+ opt_password = getpass(prompt);
+ free(prompt);
+ }
+
+ if (opt_password) {
+ use_in_memory_ccache();
+ ads->auth.password = smb_xstrdup(opt_password);
+ }
+
+ ads->auth.user_name = smb_xstrdup(opt_user_name);
+
+ /*
+ * If the username is of the form "name@realm",
+ * extract the realm and convert to upper case.
+ * This is only used to establish the connection.
+ */
+ if ((cp = strchr(ads->auth.user_name, '@'))!=0) {
+ *cp++ = '\0';
+ ads->auth.realm = smb_xstrdup(cp);
+ strupper_m(ads->auth.realm);
+ }
+
+ status = ads_connect(ads);
+
+ if (!ADS_ERR_OK(status)) {
+ if (!need_password && !second_time) {
+ need_password = True;
+ second_time = True;
+ goto retry;
+ } else {
+ DEBUG(1,("ads_connect: %s\n", ads_errstr(status)));
+ return NULL;
+ }
+ }
+ return ads;
+}
+
+
+/*
+ Check to see if connection can be made via ads.
+ ads_startup() stores the password in opt_password if it needs to so
+ that rpc or rap can use it without re-prompting.
+*/
+int net_ads_check(void)
+{
+ ADS_STRUCT *ads;
+
+ ads = ads_startup();
+ if (!ads)
+ return -1;
+ ads_destroy(&ads);
+ return 0;
+}
+
+/*
+ determine the netbios workgroup name for a domain
+ */
+static int net_ads_workgroup(int argc, const char **argv)
+{
+ ADS_STRUCT *ads;
+ TALLOC_CTX *ctx;
+ const char *workgroup;
+
+ if (!(ads = ads_startup())) return -1;
+
+ if (!(ctx = talloc_init("net_ads_workgroup"))) {
+ return -1;
+ }
+
+ if (!ADS_ERR_OK(ads_workgroup_name(ads, ctx, &workgroup))) {
+ d_printf("Failed to find workgroup for realm '%s'\n",
+ ads->config.realm);
+ talloc_destroy(ctx);
+ return -1;
+ }
+
+ d_printf("Workgroup: %s\n", workgroup);
+
+ talloc_destroy(ctx);
+
+ return 0;
+}
+
+
+
+static BOOL usergrp_display(char *field, void **values, void *data_area)
+{
+ char **disp_fields = (char **) data_area;
+
+ if (!field) { /* must be end of record */
+ if (!strchr_m(disp_fields[0], '$')) {
+ if (disp_fields[1])
+ d_printf("%-21.21s %s\n",
+ disp_fields[0], disp_fields[1]);
+ else
+ d_printf("%s\n", disp_fields[0]);
+ }
+ SAFE_FREE(disp_fields[0]);
+ SAFE_FREE(disp_fields[1]);
+ return True;
+ }
+ if (!values) /* must be new field, indicate string field */
+ return True;
+ if (StrCaseCmp(field, "sAMAccountName") == 0) {
+ disp_fields[0] = strdup((char *) values[0]);
+ }
+ if (StrCaseCmp(field, "description") == 0)
+ disp_fields[1] = strdup((char *) values[0]);
+ return True;
+}
+
+static int net_ads_user_usage(int argc, const char **argv)
+{
+ return net_help_user(argc, argv);
+}
+
+static int ads_user_add(int argc, const char **argv)
+{
+ ADS_STRUCT *ads;
+ ADS_STATUS status;
+ char *upn, *userdn;
+ void *res=NULL;
+ int rc = -1;
+
+ if (argc < 1) return net_ads_user_usage(argc, argv);
+
+ if (!(ads = ads_startup())) return -1;
+
+ status = ads_find_user_acct(ads, &res, argv[0]);
+
+ if (!ADS_ERR_OK(status)) {
+ d_printf("ads_user_add: %s\n", ads_errstr(status));
+ goto done;
+ }
+
+ if (ads_count_replies(ads, res)) {
+ d_printf("ads_user_add: User %s already exists\n", argv[0]);
+ goto done;
+ }
+
+ status = ads_add_user_acct(ads, argv[0], opt_container, opt_comment);
+
+ if (!ADS_ERR_OK(status)) {
+ d_printf("Could not add user %s: %s\n", argv[0],
+ ads_errstr(status));
+ goto done;
+ }
+
+ /* if no password is to be set, we're done */
+ if (argc == 1) {
+ d_printf("User %s added\n", argv[0]);
+ rc = 0;
+ goto done;
+ }
+
+ /* try setting the password */
+ asprintf(&upn, "%s@%s", argv[0], ads->config.realm);
+ status = ads_krb5_set_password(ads->auth.kdc_server, upn, argv[1],
+ ads->auth.time_offset);
+ safe_free(upn);
+ if (ADS_ERR_OK(status)) {
+ d_printf("User %s added\n", argv[0]);
+ rc = 0;
+ goto done;
+ }
+
+ /* password didn't set, delete account */
+ d_printf("Could not add user %s. Error setting password %s\n",
+ argv[0], ads_errstr(status));
+ ads_msgfree(ads, res);
+ status=ads_find_user_acct(ads, &res, argv[0]);
+ if (ADS_ERR_OK(status)) {
+ userdn = ads_get_dn(ads, res);
+ ads_del_dn(ads, userdn);
+ ads_memfree(ads, userdn);
+ }
+
+ done:
+ if (res)
+ ads_msgfree(ads, res);
+ ads_destroy(&ads);
+ return rc;
+}
+
+static int ads_user_info(int argc, const char **argv)
+{
+ ADS_STRUCT *ads;
+ ADS_STATUS rc;
+ void *res;
+ const char *attrs[] = {"memberOf", NULL};
+ char *searchstring=NULL;
+ char **grouplist;
+ char *escaped_user = escape_ldap_string_alloc(argv[0]);
+
+ if (argc < 1) return net_ads_user_usage(argc, argv);
+
+ if (!(ads = ads_startup())) return -1;
+
+ if (!escaped_user) {
+ d_printf("ads_user_info: failed to escape user %s\n", argv[0]);
+ return -1;
+ }
+
+ asprintf(&searchstring, "(sAMAccountName=%s)", escaped_user);
+ rc = ads_search(ads, &res, searchstring, attrs);
+ safe_free(searchstring);
+
+ if (!ADS_ERR_OK(rc)) {
+ d_printf("ads_search: %s\n", ads_errstr(rc));
+ return -1;
+ }
+
+ grouplist = ldap_get_values(ads->ld, res, "memberOf");
+
+ if (grouplist) {
+ int i;
+ char **groupname;
+ for (i=0;grouplist[i];i++) {
+ groupname = ldap_explode_dn(grouplist[i], 1);
+ d_printf("%s\n", groupname[0]);
+ ldap_value_free(groupname);
+ }
+ ldap_value_free(grouplist);
+ }
+
+ ads_msgfree(ads, res);
+
+ ads_destroy(&ads);
+ return 0;
+}
+
+static int ads_user_delete(int argc, const char **argv)
+{
+ ADS_STRUCT *ads;
+ ADS_STATUS rc;
+ void *res;
+ char *userdn;
+
+ if (argc < 1) return net_ads_user_usage(argc, argv);
+
+ if (!(ads = ads_startup())) return -1;
+
+ rc = ads_find_user_acct(ads, &res, argv[0]);
+ if (!ADS_ERR_OK(rc)) {
+ DEBUG(0, ("User %s does not exist\n", argv[0]));
+ return -1;
+ }
+ userdn = ads_get_dn(ads, res);
+ ads_msgfree(ads, res);
+ rc = ads_del_dn(ads, userdn);
+ ads_memfree(ads, userdn);
+ if (!ADS_ERR_OK(rc)) {
+ d_printf("User %s deleted\n", argv[0]);
+ return 0;
+ }
+ d_printf("Error deleting user %s: %s\n", argv[0],
+ ads_errstr(rc));
+ return -1;
+}
+
+int net_ads_user(int argc, const char **argv)
+{
+ struct functable func[] = {
+ {"ADD", ads_user_add},
+ {"INFO", ads_user_info},
+ {"DELETE", ads_user_delete},
+ {NULL, NULL}
+ };
+ ADS_STRUCT *ads;
+ ADS_STATUS rc;
+ const char *shortattrs[] = {"sAMAccountName", NULL};
+ const char *longattrs[] = {"sAMAccountName", "description", NULL};
+ char *disp_fields[2] = {NULL, NULL};
+
+ if (argc == 0) {
+ if (!(ads = ads_startup())) return -1;
+
+ if (opt_long_list_entries)
+ d_printf("\nUser name Comment"\
+ "\n-----------------------------\n");
+
+ rc = ads_do_search_all_fn(ads, ads->config.bind_path,
+ LDAP_SCOPE_SUBTREE,
+ "(objectclass=user)",
+ opt_long_list_entries ? longattrs :
+ shortattrs, usergrp_display,
+ disp_fields);
+ ads_destroy(&ads);
+ return 0;
+ }
+
+ return net_run_function(argc, argv, func, net_ads_user_usage);
+}
+
+static int net_ads_group_usage(int argc, const char **argv)
+{
+ return net_help_group(argc, argv);
+}
+
+static int ads_group_add(int argc, const char **argv)
+{
+ ADS_STRUCT *ads;
+ ADS_STATUS status;
+ void *res=NULL;
+ int rc = -1;
+
+ if (argc < 1) return net_ads_group_usage(argc, argv);
+
+ if (!(ads = ads_startup())) return -1;
+
+ status = ads_find_user_acct(ads, &res, argv[0]);
+
+ if (!ADS_ERR_OK(status)) {
+ d_printf("ads_group_add: %s\n", ads_errstr(status));
+ goto done;
+ }
+
+ if (ads_count_replies(ads, res)) {
+ d_printf("ads_group_add: Group %s already exists\n", argv[0]);
+ ads_msgfree(ads, res);
+ goto done;
+ }
+
+ status = ads_add_group_acct(ads, argv[0], opt_container, opt_comment);
+
+ if (ADS_ERR_OK(status)) {
+ d_printf("Group %s added\n", argv[0]);
+ rc = 0;
+ } else {
+ d_printf("Could not add group %s: %s\n", argv[0],
+ ads_errstr(status));
+ }
+
+ done:
+ if (res)
+ ads_msgfree(ads, res);
+ ads_destroy(&ads);
+ return rc;
+}
+
+static int ads_group_delete(int argc, const char **argv)
+{
+ ADS_STRUCT *ads;
+ ADS_STATUS rc;
+ void *res;
+ char *groupdn;
+
+ if (argc < 1) return net_ads_group_usage(argc, argv);
+
+ if (!(ads = ads_startup())) return -1;
+
+ rc = ads_find_user_acct(ads, &res, argv[0]);
+ if (!ADS_ERR_OK(rc)) {
+ DEBUG(0, ("Group %s does not exist\n", argv[0]));
+ return -1;
+ }
+ groupdn = ads_get_dn(ads, res);
+ ads_msgfree(ads, res);
+ rc = ads_del_dn(ads, groupdn);
+ ads_memfree(ads, groupdn);
+ if (!ADS_ERR_OK(rc)) {
+ d_printf("Group %s deleted\n", argv[0]);
+ return 0;
+ }
+ d_printf("Error deleting group %s: %s\n", argv[0],
+ ads_errstr(rc));
+ return -1;
+}
+
+int net_ads_group(int argc, const char **argv)
+{
+ struct functable func[] = {
+ {"ADD", ads_group_add},
+ {"DELETE", ads_group_delete},
+ {NULL, NULL}
+ };
+ ADS_STRUCT *ads;
+ ADS_STATUS rc;
+ const char *shortattrs[] = {"sAMAccountName", NULL};
+ const char *longattrs[] = {"sAMAccountName", "description", NULL};
+ char *disp_fields[2] = {NULL, NULL};
+
+ if (argc == 0) {
+ if (!(ads = ads_startup())) return -1;
+
+ if (opt_long_list_entries)
+ d_printf("\nGroup name Comment"\
+ "\n-----------------------------\n");
+ rc = ads_do_search_all_fn(ads, ads->config.bind_path,
+ LDAP_SCOPE_SUBTREE,
+ "(objectclass=group)",
+ opt_long_list_entries ? longattrs :
+ shortattrs, usergrp_display,
+ disp_fields);
+
+ ads_destroy(&ads);
+ return 0;
+ }
+ return net_run_function(argc, argv, func, net_ads_group_usage);
+}
+
+static int net_ads_status(int argc, const char **argv)
+{
+ ADS_STRUCT *ads;
+ ADS_STATUS rc;
+ void *res;
+
+ if (!(ads = ads_startup())) return -1;
+
+ rc = ads_find_machine_acct(ads, &res, global_myname());
+ if (!ADS_ERR_OK(rc)) {
+ d_printf("ads_find_machine_acct: %s\n", ads_errstr(rc));
+ return -1;
+ }
+
+ if (ads_count_replies(ads, res) == 0) {
+ d_printf("No machine account for '%s' found\n", global_myname());
+ return -1;
+ }
+
+ ads_dump(ads, res);
+
+ return 0;
+}
+
+static int net_ads_leave(int argc, const char **argv)
+{
+ ADS_STRUCT *ads = NULL;
+ ADS_STATUS rc;
+
+ if (!secrets_init()) {
+ DEBUG(1,("Failed to initialise secrets database\n"));
+ return -1;
+ }
+
+ if (!opt_password) {
+ net_use_machine_password();
+ }
+
+ if (!(ads = ads_startup())) {
+ return -1;
+ }
+
+ rc = ads_leave_realm(ads, global_myname());
+ if (!ADS_ERR_OK(rc)) {
+ d_printf("Failed to delete host '%s' from the '%s' realm.\n",
+ global_myname(), ads->config.realm);
+ return -1;
+ }
+
+ d_printf("Removed '%s' from realm '%s'\n", global_myname(), ads->config.realm);
+
+ return 0;
+}
+
+static int net_ads_join_ok(void)
+{
+ ADS_STRUCT *ads = NULL;
+
+ if (!secrets_init()) {
+ DEBUG(1,("Failed to initialise secrets database\n"));
+ return -1;
+ }
+
+ net_use_machine_password();
+
+ if (!(ads = ads_startup())) {
+ return -1;
+ }
+
+ ads_destroy(&ads);
+ return 0;
+}
+
+/*
+ check that an existing join is OK
+ */
+int net_ads_testjoin(int argc, const char **argv)
+{
+ use_in_memory_ccache();
+
+ /* Display success or failure */
+ if (net_ads_join_ok() != 0) {
+ fprintf(stderr,"Join to domain is not valid\n");
+ return -1;
+ }
+
+ printf("Join is OK\n");
+ return 0;
+}
+
+/*
+ join a domain using ADS
+ */
+int net_ads_join(int argc, const char **argv)
+{
+ ADS_STRUCT *ads;
+ ADS_STATUS rc;
+ char *password;
+ char *machine_account = NULL;
+ char *tmp_password;
+ const char *org_unit = "Computers";
+ char *dn;
+ void *res;
+ DOM_SID dom_sid;
+ char *ou_str;
+ uint32 sec_channel_type = SEC_CHAN_WKSTA;
+ uint32 account_type = UF_WORKSTATION_TRUST_ACCOUNT;
+ const char *short_domain_name = NULL;
+ TALLOC_CTX *ctx = NULL;
+
+ if (argc > 0) org_unit = argv[0];
+
+ if (!secrets_init()) {
+ DEBUG(1,("Failed to initialise secrets database\n"));
+ return -1;
+ }
+
+ tmp_password = generate_random_str(DEFAULT_TRUST_ACCOUNT_PASSWORD_LENGTH);
+ password = strdup(tmp_password);
+
+ if (!(ads = ads_startup())) return -1;
+
+ if (!*lp_realm()) {
+ d_printf("realm must be set in in smb.conf for ADS join to succeed.\n");
+ return -1;
+ }
+
+ if (strcmp(ads->config.realm, lp_realm()) != 0) {
+ d_printf("realm of remote server (%s) and realm in smb.conf (%s) DO NOT match. Aborting join\n", ads->config.realm, lp_realm());
+ return -1;
+ }
+
+ ou_str = ads_ou_string(org_unit);
+ asprintf(&dn, "%s,%s", ou_str, ads->config.bind_path);
+ free(ou_str);
+
+ rc = ads_search_dn(ads, &res, dn, NULL);
+ ads_msgfree(ads, res);
+
+ if (rc.error_type == ENUM_ADS_ERROR_LDAP && rc.err.rc == LDAP_NO_SUCH_OBJECT) {
+ d_printf("ads_join_realm: organizational unit %s does not exist (dn:%s)\n",
+ org_unit, dn);
+ return -1;
+ }
+ free(dn);
+
+ if (!ADS_ERR_OK(rc)) {
+ d_printf("ads_join_realm: %s\n", ads_errstr(rc));
+ return -1;
+ }
+
+ rc = ads_join_realm(ads, global_myname(), account_type, org_unit);
+ if (!ADS_ERR_OK(rc)) {
+ d_printf("ads_join_realm: %s\n", ads_errstr(rc));
+ return -1;
+ }
+
+ rc = ads_domain_sid(ads, &dom_sid);
+ if (!ADS_ERR_OK(rc)) {
+ d_printf("ads_domain_sid: %s\n", ads_errstr(rc));
+ return -1;
+ }
+
+ if (asprintf(&machine_account, "%s$", global_myname()) == -1) {
+ d_printf("asprintf failed\n");
+ return -1;
+ }
+
+ rc = ads_set_machine_password(ads, machine_account, password);
+ if (!ADS_ERR_OK(rc)) {
+ d_printf("ads_set_machine_password: %s\n", ads_errstr(rc));
+ return -1;
+ }
+
+ /* make sure we get the right workgroup */
+
+ if ( !(ctx = talloc_init("net ads join")) ) {
+ d_printf("talloc_init() failed!\n");
+ return -1;
+ }
+
+ rc = ads_workgroup_name(ads, ctx, &short_domain_name);
+ if ( ADS_ERR_OK(rc) ) {
+ if ( !strequal(lp_workgroup(), short_domain_name) ) {
+ d_printf("The workgroup in smb.conf does not match the short\n");
+ d_printf("domain name obtained from the server.\n");
+ d_printf("Using the name [%s] from the server.\n", short_domain_name);
+ d_printf("You should set \"workgroup = %s\" in smb.conf.\n", short_domain_name);
+ }
+ }
+ else
+ short_domain_name = lp_workgroup();
+
+ d_printf("Using short domain name -- %s\n", short_domain_name);
+
+ /* HACK ALRET! Store the sid and password under bother the lp_workgroup()
+ value from smb.conf and the string returned from the server. The former is
+ neede to bootstrap winbindd's first connection to the DC to get the real
+ short domain name --jerry */
+
+ if (!secrets_store_domain_sid(lp_workgroup(), &dom_sid)) {
+ DEBUG(1,("Failed to save domain sid\n"));
+ return -1;
+ }
+
+ if (!secrets_store_machine_password(password, lp_workgroup(), sec_channel_type)) {
+ DEBUG(1,("Failed to save machine password\n"));
+ return -1;
+ }
+
+ if (!secrets_store_domain_sid(short_domain_name, &dom_sid)) {
+ DEBUG(1,("Failed to save domain sid\n"));
+ return -1;
+ }
+
+ if (!secrets_store_machine_password(password, short_domain_name, sec_channel_type)) {
+ DEBUG(1,("Failed to save machine password\n"));
+ return -1;
+ }
+
+ d_printf("Joined '%s' to realm '%s'\n", global_myname(), ads->config.realm);
+
+ SAFE_FREE(password);
+ SAFE_FREE(machine_account);
+ if ( ctx )
+ talloc_destroy(ctx);
+ return 0;
+}
+
+int net_ads_printer_usage(int argc, const char **argv)
+{
+ d_printf(
+"\nnet ads printer search <printer>"
+"\n\tsearch for a printer in the directory"
+"\nnet ads printer info <printer> <server>"
+"\n\tlookup info in directory for printer on server"
+"\n\t(note: printer defaults to \"*\", server defaults to local)\n"
+"\nnet ads printer publish <printername>"
+"\n\tpublish printer in directory"
+"\n\t(note: printer name is required)\n"
+"\nnet ads printer remove <printername>"
+"\n\tremove printer from directory"
+"\n\t(note: printer name is required)\n");
+ return -1;
+}
+
+static int net_ads_printer_search(int argc, const char **argv)
+{
+ ADS_STRUCT *ads;
+ ADS_STATUS rc;
+ void *res = NULL;
+
+ if (!(ads = ads_startup()))
+ return -1;
+
+ rc = ads_find_printers(ads, &res);
+
+ if (!ADS_ERR_OK(rc)) {
+ d_printf("ads_find_printer: %s\n", ads_errstr(rc));
+ ads_msgfree(ads, res);
+ return -1;
+ }
+
+ if (ads_count_replies(ads, res) == 0) {
+ d_printf("No results found\n");
+ ads_msgfree(ads, res);
+ return -1;
+ }
+
+ ads_dump(ads, res);
+ ads_msgfree(ads, res);
+
+ return 0;
+}
+
+static int net_ads_printer_info(int argc, const char **argv)
+{
+ ADS_STRUCT *ads;
+ ADS_STATUS rc;
+ const char *servername, *printername;
+ void *res = NULL;
+
+ if (!(ads = ads_startup())) return -1;
+
+ if (argc > 0)
+ printername = argv[0];
+ else
+ printername = "*";
+
+ if (argc > 1)
+ servername = argv[1];
+ else
+ servername = global_myname();
+
+ rc = ads_find_printer_on_server(ads, &res, printername, servername);
+
+ if (!ADS_ERR_OK(rc)) {
+ d_printf("ads_find_printer_on_server: %s\n", ads_errstr(rc));
+ ads_msgfree(ads, res);
+ return -1;
+ }
+
+ if (ads_count_replies(ads, res) == 0) {
+ d_printf("Printer '%s' not found\n", printername);
+ ads_msgfree(ads, res);
+ return -1;
+ }
+
+ ads_dump(ads, res);
+ ads_msgfree(ads, res);
+
+ return 0;
+}
+
+void do_drv_upgrade_printer(int msg_type, pid_t src, void *buf, size_t len)
+{
+ return;
+}
+
+static int net_ads_printer_publish(int argc, const char **argv)
+{
+ ADS_STRUCT *ads;
+ ADS_STATUS rc;
+ const char *servername, *printername;
+ struct cli_state *cli;
+ struct in_addr server_ip;
+ NTSTATUS nt_status;
+ TALLOC_CTX *mem_ctx = talloc_init("net_ads_printer_publish");
+ ADS_MODLIST mods = ads_init_mods(mem_ctx);
+ char *prt_dn, *srv_dn, **srv_cn;
+ void *res = NULL;
+
+ if (!(ads = ads_startup())) return -1;
+
+ if (argc < 1)
+ return net_ads_printer_usage(argc, argv);
+
+ printername = argv[0];
+
+ if (argc == 2)
+ servername = argv[1];
+ else
+ servername = global_myname();
+
+ /* Get printer data from SPOOLSS */
+
+ resolve_name(servername, &server_ip, 0x20);
+
+ nt_status = cli_full_connection(&cli, global_myname(), servername,
+ &server_ip, 0,
+ "IPC$", "IPC",
+ opt_user_name, opt_workgroup,
+ opt_password ? opt_password : "",
+ CLI_FULL_CONNECTION_USE_KERBEROS,
+ Undefined, NULL);
+
+ if (NT_STATUS_IS_ERR(nt_status)) {
+ d_printf("Unable to open a connnection to %s to obtain data "
+ "for %s\n", servername, printername);
+ return -1;
+ }
+
+ /* Publish on AD server */
+
+ ads_find_machine_acct(ads, &res, servername);
+
+ if (ads_count_replies(ads, res) == 0) {
+ d_printf("Could not find machine account for server %s\n",
+ servername);
+ return -1;
+ }
+
+ srv_dn = ldap_get_dn(ads->ld, res);
+ srv_cn = ldap_explode_dn(srv_dn, 1);
+
+ asprintf(&prt_dn, "cn=%s-%s,%s", srv_cn[0], printername, srv_dn);
+
+ cli_nt_session_open(cli, PI_SPOOLSS);
+ get_remote_printer_publishing_data(cli, mem_ctx, &mods, printername);
+
+ rc = ads_add_printer_entry(ads, prt_dn, mem_ctx, &mods);
+ if (!ADS_ERR_OK(rc)) {
+ d_printf("ads_publish_printer: %s\n", ads_errstr(rc));
+ return -1;
+ }
+
+ d_printf("published printer\n");
+
+ return 0;
+}
+
+static int net_ads_printer_remove(int argc, const char **argv)
+{
+ ADS_STRUCT *ads;
+ ADS_STATUS rc;
+ const char *servername;
+ char *prt_dn;
+ void *res = NULL;
+
+ if (!(ads = ads_startup())) return -1;
+
+ if (argc < 1)
+ return net_ads_printer_usage(argc, argv);
+
+ if (argc > 1)
+ servername = argv[1];
+ else
+ servername = global_myname();
+
+ rc = ads_find_printer_on_server(ads, &res, argv[0], servername);
+
+ if (!ADS_ERR_OK(rc)) {
+ d_printf("ads_find_printer_on_server: %s\n", ads_errstr(rc));
+ ads_msgfree(ads, res);
+ return -1;
+ }
+
+ if (ads_count_replies(ads, res) == 0) {
+ d_printf("Printer '%s' not found\n", argv[1]);
+ ads_msgfree(ads, res);
+ return -1;
+ }
+
+ prt_dn = ads_get_dn(ads, res);
+ ads_msgfree(ads, res);
+ rc = ads_del_dn(ads, prt_dn);
+ ads_memfree(ads, prt_dn);
+
+ if (!ADS_ERR_OK(rc)) {
+ d_printf("ads_del_dn: %s\n", ads_errstr(rc));
+ return -1;
+ }
+
+ return 0;
+}
+
+static int net_ads_printer(int argc, const char **argv)
+{
+ struct functable func[] = {
+ {"SEARCH", net_ads_printer_search},
+ {"INFO", net_ads_printer_info},
+ {"PUBLISH", net_ads_printer_publish},
+ {"REMOVE", net_ads_printer_remove},
+ {NULL, NULL}
+ };
+
+ return net_run_function(argc, argv, func, net_ads_printer_usage);
+}
+
+
+static int net_ads_password(int argc, const char **argv)
+{
+ ADS_STRUCT *ads;
+ const char *auth_principal = opt_user_name;
+ const char *auth_password = opt_password;
+ char *realm = NULL;
+ char *new_password = NULL;
+ char *c, *prompt;
+ const char *user;
+ ADS_STATUS ret;
+
+ if (opt_user_name == NULL || opt_password == NULL) {
+ d_printf("You must supply an administrator username/password\n");
+ return -1;
+ }
+
+
+ if (argc < 1) {
+ d_printf("ERROR: You must say which username to change password for\n");
+ return -1;
+ }
+
+ user = argv[0];
+ if (!strchr(user, '@')) {
+ asprintf(&c, "%s@%s", argv[0], lp_realm());
+ user = c;
+ }
+
+ use_in_memory_ccache();
+ c = strchr(auth_principal, '@');
+ if (c) {
+ realm = ++c;
+ } else {
+ realm = lp_realm();
+ }
+
+ /* use the realm so we can eventually change passwords for users
+ in realms other than default */
+ if (!(ads = ads_init(realm, NULL, NULL))) return -1;
+
+ /* we don't actually need a full connect, but it's the easy way to
+ fill in the KDC's addresss */
+ ads_connect(ads);
+
+ if (!ads || !ads->config.realm) {
+ d_printf("Didn't find the kerberos server!\n");
+ return -1;
+ }
+
+ if (argv[1]) {
+ new_password = (char *)argv[1];
+ } else {
+ asprintf(&prompt, "Enter new password for %s:", user);
+ new_password = getpass(prompt);
+ free(prompt);
+ }
+
+ ret = kerberos_set_password(ads->auth.kdc_server, auth_principal,
+ auth_password, user, new_password, ads->auth.time_offset);
+ if (!ADS_ERR_OK(ret)) {
+ d_printf("Password change failed :-( ...\n");
+ ads_destroy(&ads);
+ return -1;
+ }
+
+ d_printf("Password change for %s completed.\n", user);
+ ads_destroy(&ads);
+
+ return 0;
+}
+
+
+int net_ads_changetrustpw(int argc, const char **argv)
+{
+ ADS_STRUCT *ads;
+ char *host_principal;
+ char *hostname;
+ ADS_STATUS ret;
+
+ if (!secrets_init()) {
+ DEBUG(1,("Failed to initialise secrets database\n"));
+ return -1;
+ }
+
+ net_use_machine_password();
+
+ use_in_memory_ccache();
+
+ if (!(ads = ads_startup())) {
+ return -1;
+ }
+
+ hostname = strdup(global_myname());
+ strlower_m(hostname);
+ asprintf(&host_principal, "%s@%s", hostname, ads->config.realm);
+ SAFE_FREE(hostname);
+ d_printf("Changing password for principal: HOST/%s\n", host_principal);
+
+ ret = ads_change_trust_account_password(ads, host_principal);
+
+ if (!ADS_ERR_OK(ret)) {
+ d_printf("Password change failed :-( ...\n");
+ ads_destroy(&ads);
+ SAFE_FREE(host_principal);
+ return -1;
+ }
+
+ d_printf("Password change for principal HOST/%s succeeded.\n", host_principal);
+ ads_destroy(&ads);
+ SAFE_FREE(host_principal);
+
+ return 0;
+}
+
+/*
+ help for net ads search
+*/
+static int net_ads_search_usage(int argc, const char **argv)
+{
+ d_printf(
+ "\nnet ads search <expression> <attributes...>\n"\
+ "\nperform a raw LDAP search on a ADS server and dump the results\n"\
+ "The expression is a standard LDAP search expression, and the\n"\
+ "attributes are a list of LDAP fields to show in the results\n\n"\
+ "Example: net ads search '(objectCategory=group)' sAMAccountName\n\n"
+ );
+ net_common_flags_usage(argc, argv);
+ return -1;
+}
+
+
+/*
+ general ADS search function. Useful in diagnosing problems in ADS
+*/
+static int net_ads_search(int argc, const char **argv)
+{
+ ADS_STRUCT *ads;
+ ADS_STATUS rc;
+ const char *ldap_exp;
+ const char **attrs;
+ void *res = NULL;
+
+ if (argc < 1) {
+ return net_ads_search_usage(argc, argv);
+ }
+
+ if (!(ads = ads_startup())) {
+ return -1;
+ }
+
+ ldap_exp = argv[0];
+ attrs = (argv + 1);
+
+ rc = ads_do_search_all(ads, ads->config.bind_path,
+ LDAP_SCOPE_SUBTREE,
+ ldap_exp, attrs, &res);
+ if (!ADS_ERR_OK(rc)) {
+ d_printf("search failed: %s\n", ads_errstr(rc));
+ return -1;
+ }
+
+ d_printf("Got %d replies\n\n", ads_count_replies(ads, res));
+
+ /* dump the results */
+ ads_dump(ads, res);
+
+ ads_msgfree(ads, res);
+ ads_destroy(&ads);
+
+ return 0;
+}
+
+
+/*
+ help for net ads search
+*/
+static int net_ads_dn_usage(int argc, const char **argv)
+{
+ d_printf(
+ "\nnet ads dn <dn> <attributes...>\n"\
+ "\nperform a raw LDAP search on a ADS server and dump the results\n"\
+ "The DN standard LDAP DN, and the attributes are a list of LDAP fields \n"\
+ "to show in the results\n\n"\
+ "Example: net ads dn 'CN=administrator,CN=Users,DC=my,DC=domain' sAMAccountName\n\n"
+ );
+ net_common_flags_usage(argc, argv);
+ return -1;
+}
+
+
+/*
+ general ADS search function. Useful in diagnosing problems in ADS
+*/
+static int net_ads_dn(int argc, const char **argv)
+{
+ ADS_STRUCT *ads;
+ ADS_STATUS rc;
+ const char *dn;
+ const char **attrs;
+ void *res = NULL;
+
+ if (argc < 1) {
+ return net_ads_dn_usage(argc, argv);
+ }
+
+ if (!(ads = ads_startup())) {
+ return -1;
+ }
+
+ dn = argv[0];
+ attrs = (argv + 1);
+
+ rc = ads_do_search_all(ads, dn,
+ LDAP_SCOPE_BASE,
+ "(objectclass=*)", attrs, &res);
+ if (!ADS_ERR_OK(rc)) {
+ d_printf("search failed: %s\n", ads_errstr(rc));
+ return -1;
+ }
+
+ d_printf("Got %d replies\n\n", ads_count_replies(ads, res));
+
+ /* dump the results */
+ ads_dump(ads, res);
+
+ ads_msgfree(ads, res);
+ ads_destroy(&ads);
+
+ return 0;
+}
+
+
+int net_ads_help(int argc, const char **argv)
+{
+ struct functable func[] = {
+ {"USER", net_ads_user_usage},
+ {"GROUP", net_ads_group_usage},
+ {"PRINTER", net_ads_printer_usage},
+ {"SEARCH", net_ads_search_usage},
+#if 0
+ {"INFO", net_ads_info},
+ {"JOIN", net_ads_join},
+ {"LEAVE", net_ads_leave},
+ {"STATUS", net_ads_status},
+ {"PASSWORD", net_ads_password},
+ {"CHANGETRUSTPW", net_ads_changetrustpw},
+#endif
+ {NULL, NULL}
+ };
+
+ return net_run_function(argc, argv, func, net_ads_usage);
+}
+
+int net_ads(int argc, const char **argv)
+{
+ struct functable func[] = {
+ {"INFO", net_ads_info},
+ {"JOIN", net_ads_join},
+ {"TESTJOIN", net_ads_testjoin},
+ {"LEAVE", net_ads_leave},
+ {"STATUS", net_ads_status},
+ {"USER", net_ads_user},
+ {"GROUP", net_ads_group},
+ {"PASSWORD", net_ads_password},
+ {"CHANGETRUSTPW", net_ads_changetrustpw},
+ {"PRINTER", net_ads_printer},
+ {"SEARCH", net_ads_search},
+ {"DN", net_ads_dn},
+ {"WORKGROUP", net_ads_workgroup},
+ {"LOOKUP", net_ads_lookup},
+ {"HELP", net_ads_help},
+ {NULL, NULL}
+ };
+
+ return net_run_function(argc, argv, func, net_ads_usage);
+}
+
+#else
+
+static int net_ads_noads(void)
+{
+ d_printf("ADS support not compiled in\n");
+ return -1;
+}
+
+int net_ads_usage(int argc, const char **argv)
+{
+ return net_ads_noads();
+}
+
+int net_ads_help(int argc, const char **argv)
+{
+ return net_ads_noads();
+}
+
+int net_ads_changetrustpw(int argc, const char **argv)
+{
+ return net_ads_noads();
+}
+
+int net_ads_join(int argc, const char **argv)
+{
+ return net_ads_noads();
+}
+
+int net_ads_user(int argc, const char **argv)
+{
+ return net_ads_noads();
+}
+
+int net_ads_group(int argc, const char **argv)
+{
+ return net_ads_noads();
+}
+
+/* this one shouldn't display a message */
+int net_ads_check(void)
+{
+ return -1;
+}
+
+int net_ads(int argc, const char **argv)
+{
+ return net_ads_usage(argc, argv);
+}
+
+#endif
diff --git a/source/utils/net_ads_cldap.c b/source/utils/net_ads_cldap.c
new file mode 100644
index 00000000000..1903172cf75
--- /dev/null
+++ b/source/utils/net_ads_cldap.c
@@ -0,0 +1,364 @@
+/*
+ Samba Unix/Linux SMB client library
+ net ads cldap functions
+ Copyright (C) 2001 Andrew Tridgell (tridge@samba.org)
+ Copyright (C) 2003 Jim McDonough (jmcd@us.ibm.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 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 "../utils/net.h"
+
+#ifdef HAVE_ADS
+
+#define MAX_DNS_LABEL 255 + 1
+
+struct cldap_netlogon_reply {
+ uint32 type;
+ uint32 flags;
+ UUID_FLAT guid;
+
+ char forest[MAX_DNS_LABEL];
+ char domain[MAX_DNS_LABEL];
+ char hostname[MAX_DNS_LABEL];
+
+ char netbios_domain[MAX_DNS_LABEL];
+ char netbios_hostname[MAX_DNS_LABEL];
+
+ char unk[MAX_DNS_LABEL];
+ char user_name[MAX_DNS_LABEL];
+ char site_name[MAX_DNS_LABEL];
+ char site_name_2[MAX_DNS_LABEL];
+
+ uint32 version;
+ uint16 lmnt_token;
+ uint16 lm20_token;
+};
+
+/*
+ These seem to be strings as described in RFC1035 4.1.4 and can be:
+
+ - a sequence of labels ending in a zero octet
+ - a pointer
+ - a sequence of labels ending with a pointer
+
+ A label is a byte where the first two bits must be zero and the remaining
+ bits represent the length of the label followed by the label itself.
+ Therefore, the length of a label is at max 64 bytes. Under RFC1035, a
+ sequence of labels cannot exceed 255 bytes.
+
+ A pointer consists of a 14 bit offset from the beginning of the data.
+
+ struct ptr {
+ unsigned ident:2; // must be 11
+ unsigned offset:14; // from the beginning of data
+ };
+
+ This is used as a method to compress the packet by eliminated duplicate
+ domain components. Since a UDP packet should probably be < 512 bytes and a
+ DNS name can be up to 255 bytes, this actually makes a lot of sense.
+*/
+static unsigned pull_netlogon_string(char *ret, const char *ptr,
+ const char *data)
+{
+ char *pret = ret;
+ int followed_ptr = 0;
+ unsigned ret_len = 0;
+
+ memset(pret, 0, MAX_DNS_LABEL);
+ do {
+ if ((*ptr & 0xc0) == 0xc0) {
+ uint16 len;
+
+ if (!followed_ptr) {
+ ret_len += 2;
+ followed_ptr = 1;
+ }
+ len = ((ptr[0] & 0x3f) << 8) | ptr[1];
+ ptr = data + len;
+ } else if (*ptr) {
+ uint8 len = (uint8)*(ptr++);
+
+ if ((pret - ret + len + 1) >= MAX_DNS_LABEL) {
+ d_printf("DC returning too long DNS name\n");
+ return 0;
+ }
+
+ if (pret != ret) {
+ *pret = '.';
+ pret++;
+ }
+ memcpy(pret, ptr, len);
+ pret += len;
+ ptr += len;
+
+ if (!followed_ptr) {
+ ret_len += (len + 1);
+ }
+ }
+ } while (*ptr);
+
+ return followed_ptr ? ret_len : ret_len + 1;
+}
+
+/*
+ do a cldap netlogon query
+*/
+static int send_cldap_netlogon(int sock, const char *domain,
+ const char *hostname, unsigned ntversion)
+{
+ ASN1_DATA data;
+ char ntver[4];
+#ifdef CLDAP_USER_QUERY
+ char aac[4];
+
+ SIVAL(aac, 0, 0x00000180);
+#endif
+ SIVAL(ntver, 0, ntversion);
+
+ memset(&data, 0, sizeof(data));
+
+ asn1_push_tag(&data,ASN1_SEQUENCE(0));
+ asn1_write_Integer(&data, 4);
+ asn1_push_tag(&data, ASN1_APPLICATION(3));
+ asn1_write_OctetString(&data, NULL, 0);
+ asn1_write_enumerated(&data, 0);
+ asn1_write_enumerated(&data, 0);
+ asn1_write_Integer(&data, 0);
+ asn1_write_Integer(&data, 0);
+ asn1_write_BOOLEAN2(&data, False);
+ asn1_push_tag(&data, ASN1_CONTEXT(0));
+
+ asn1_push_tag(&data, ASN1_CONTEXT(3));
+ asn1_write_OctetString(&data, "DnsDomain", 9);
+ asn1_write_OctetString(&data, domain, strlen(domain));
+ asn1_pop_tag(&data);
+
+ asn1_push_tag(&data, ASN1_CONTEXT(3));
+ asn1_write_OctetString(&data, "Host", 4);
+ asn1_write_OctetString(&data, hostname, strlen(hostname));
+ asn1_pop_tag(&data);
+
+#ifdef CLDAP_USER_QUERY
+ asn1_push_tag(&data, ASN1_CONTEXT(3));
+ asn1_write_OctetString(&data, "User", 4);
+ asn1_write_OctetString(&data, "SAMBA$", 6);
+ asn1_pop_tag(&data);
+
+ asn1_push_tag(&data, ASN1_CONTEXT(3));
+ asn1_write_OctetString(&data, "AAC", 4);
+ asn1_write_OctetString(&data, aac, 4);
+ asn1_pop_tag(&data);
+#endif
+
+ asn1_push_tag(&data, ASN1_CONTEXT(3));
+ asn1_write_OctetString(&data, "NtVer", 5);
+ asn1_write_OctetString(&data, ntver, 4);
+ asn1_pop_tag(&data);
+
+ asn1_pop_tag(&data);
+
+ asn1_push_tag(&data,ASN1_SEQUENCE(0));
+ asn1_write_OctetString(&data, "NetLogon", 8);
+ asn1_pop_tag(&data);
+ asn1_pop_tag(&data);
+ asn1_pop_tag(&data);
+
+ if (data.has_error) {
+ d_printf("Failed to build cldap netlogon at offset %d\n", (int)data.ofs);
+ asn1_free(&data);
+ return -1;
+ }
+
+ if (write(sock, data.data, data.length) != (ssize_t)data.length) {
+ d_printf("failed to send cldap query (%s)\n", strerror(errno));
+ }
+
+ asn1_free(&data);
+
+ return 0;
+}
+
+
+/*
+ receive a cldap netlogon reply
+*/
+static int recv_cldap_netlogon(int sock, struct cldap_netlogon_reply *reply)
+{
+ int ret;
+ ASN1_DATA data;
+ DATA_BLOB blob;
+ DATA_BLOB os1, os2, os3;
+ uint32 i1;
+ char *p;
+
+ blob = data_blob(NULL, 8192);
+
+ ret = read(sock, blob.data, blob.length);
+
+ if (ret <= 0) {
+ d_printf("no reply received to cldap netlogon\n");
+ return -1;
+ }
+ blob.length = ret;
+
+ asn1_load(&data, blob);
+ asn1_start_tag(&data, ASN1_SEQUENCE(0));
+ asn1_read_Integer(&data, &i1);
+ asn1_start_tag(&data, ASN1_APPLICATION(4));
+ asn1_read_OctetString(&data, &os1);
+ asn1_start_tag(&data, ASN1_SEQUENCE(0));
+ asn1_start_tag(&data, ASN1_SEQUENCE(0));
+ asn1_read_OctetString(&data, &os2);
+ asn1_start_tag(&data, ASN1_SET);
+ asn1_read_OctetString(&data, &os3);
+ asn1_end_tag(&data);
+ asn1_end_tag(&data);
+ asn1_end_tag(&data);
+ asn1_end_tag(&data);
+ asn1_end_tag(&data);
+
+ if (data.has_error) {
+ d_printf("Failed to parse cldap reply\n");
+ return -1;
+ }
+
+ p = (char *)os3.data;
+
+ reply->type = IVAL(p, 0); p += 4;
+ reply->flags = IVAL(p, 0); p += 4;
+
+ memcpy(&reply->guid.info, p, UUID_FLAT_SIZE);
+ p += UUID_FLAT_SIZE;
+
+ p += pull_netlogon_string(reply->forest, p, (const char *)os3.data);
+ p += pull_netlogon_string(reply->domain, p, (const char *)os3.data);
+ p += pull_netlogon_string(reply->hostname, p, (const char *)os3.data);
+ p += pull_netlogon_string(reply->netbios_domain, p, (const char *)os3.data);
+ p += pull_netlogon_string(reply->netbios_hostname, p, (const char *)os3.data);
+ p += pull_netlogon_string(reply->unk, p, (const char *)os3.data);
+
+ if (reply->type == SAMLOGON_AD_R) {
+ p += pull_netlogon_string(reply->user_name, p, (const char *)os3.data);
+ } else {
+ *reply->user_name = 0;
+ }
+
+ p += pull_netlogon_string(reply->site_name, p, (const char *)os3.data);
+ p += pull_netlogon_string(reply->site_name_2, p, (const char *)os3.data);
+
+ reply->version = IVAL(p, 0);
+ reply->lmnt_token = SVAL(p, 4);
+ reply->lm20_token = SVAL(p, 6);
+
+ data_blob_free(&os1);
+ data_blob_free(&os2);
+ data_blob_free(&os3);
+ data_blob_free(&blob);
+
+ return 0;
+}
+
+/*
+ do a cldap netlogon query
+*/
+int ads_cldap_netlogon(ADS_STRUCT *ads)
+{
+ int sock;
+ int ret;
+ struct cldap_netlogon_reply reply;
+
+ sock = open_udp_socket(inet_ntoa(ads->ldap_ip), ads->ldap_port);
+ if (sock == -1) {
+ d_printf("Failed to open udp socket to %s:%u\n",
+ inet_ntoa(ads->ldap_ip),
+ ads->ldap_port);
+ return -1;
+
+ }
+
+ ret = send_cldap_netlogon(sock, ads->config.realm, global_myname(), 6);
+ if (ret != 0) {
+ return ret;
+ }
+ ret = recv_cldap_netlogon(sock, &reply);
+ close(sock);
+
+ if (ret == -1) {
+ return -1;
+ }
+
+ d_printf("Information for Domain Controller: %s\n\n",
+ ads->config.ldap_server_name);
+
+ d_printf("Response Type: ");
+ switch (reply.type) {
+ case SAMLOGON_AD_UNK_R:
+ d_printf("SAMLOGON\n");
+ break;
+ case SAMLOGON_AD_R:
+ d_printf("SAMLOGON_USER\n");
+ break;
+ default:
+ d_printf("0x%x\n", reply.type);
+ break;
+ }
+ d_printf("GUID: %s\n",
+ smb_uuid_string_static(smb_uuid_unpack_static(reply.guid)));
+ d_printf("Flags:\n"
+ "\tIs a PDC: %s\n"
+ "\tIs a GC of the forest: %s\n"
+ "\tIs an LDAP server: %s\n"
+ "\tSupports DS: %s\n"
+ "\tIs running a KDC: %s\n"
+ "\tIs running time services: %s\n"
+ "\tIs the closest DC: %s\n"
+ "\tIs writable: %s\n"
+ "\tHas a hardware clock: %s\n"
+ "\tIs a non-domain NC serviced by LDAP server: %s\n",
+ (reply.flags & ADS_PDC) ? "yes" : "no",
+ (reply.flags & ADS_GC) ? "yes" : "no",
+ (reply.flags & ADS_LDAP) ? "yes" : "no",
+ (reply.flags & ADS_DS) ? "yes" : "no",
+ (reply.flags & ADS_KDC) ? "yes" : "no",
+ (reply.flags & ADS_TIMESERV) ? "yes" : "no",
+ (reply.flags & ADS_CLOSEST) ? "yes" : "no",
+ (reply.flags & ADS_WRITABLE) ? "yes" : "no",
+ (reply.flags & ADS_GOOD_TIMESERV) ? "yes" : "no",
+ (reply.flags & ADS_NDNC) ? "yes" : "no");
+
+ printf("Forest:\t\t\t%s\n", reply.forest);
+ printf("Domain:\t\t\t%s\n", reply.domain);
+ printf("Domain Controller:\t%s\n", reply.hostname);
+
+ printf("Pre-Win2k Domain:\t%s\n", reply.netbios_domain);
+ printf("Pre-Win2k Hostname:\t%s\n", reply.netbios_hostname);
+
+ if (*reply.unk) printf("Unk:\t\t\t%s\n", reply.unk);
+ if (*reply.user_name) printf("User name:\t%s\n", reply.user_name);
+
+ printf("Site Name:\t\t%s\n", reply.site_name);
+ printf("Site Name (2):\t\t%s\n", reply.site_name_2);
+
+ d_printf("NT Version: %d\n", reply.version);
+ d_printf("LMNT Token: %.2x\n", reply.lmnt_token);
+ d_printf("LM20 Token: %.2x\n", reply.lm20_token);
+
+ return ret;
+}
+
+
+#endif
diff --git a/source/utils/net_cache.c b/source/utils/net_cache.c
new file mode 100644
index 00000000000..a9559164587
--- /dev/null
+++ b/source/utils/net_cache.c
@@ -0,0 +1,348 @@
+/*
+ Samba Unix/Linux SMB client library
+ Distributed SMB/CIFS Server Management Utility
+ 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 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 "net.h"
+
+/**
+ * @file net_cache.c
+ * @brief This is part of the net tool which is basically command
+ * line wrapper for gencache.c functions (mainly for testing)
+ *
+ **/
+
+
+/*
+ * These routines are used via gencache_iterate() to display the cache's contents
+ * (print_cache_entry) and to flush it (delete_cache_entry).
+ * Both of them are defined by first arg of gencache_iterate() routine.
+ */
+static void print_cache_entry(const char* keystr, const char* datastr,
+ const time_t timeout, void* dptr)
+{
+ char* timeout_str;
+ time_t now_t = time(NULL);
+ struct tm timeout_tm, *now_tm;
+ /* localtime returns statically allocated pointer, so timeout_tm
+ has to be copied somewhere else */
+ memcpy(&timeout_tm, localtime(&timeout), sizeof(struct tm));
+ now_tm = localtime(&now_t);
+
+ /* form up timeout string depending whether it's today's date or not */
+ if (timeout_tm.tm_year != now_tm->tm_year ||
+ timeout_tm.tm_mon != now_tm->tm_mon ||
+ timeout_tm.tm_mday != now_tm->tm_mday) {
+
+ timeout_str = asctime(&timeout_tm);
+ timeout_str[strlen(timeout_str) - 1] = '\0'; /* remove tailing CR */
+ } else
+ asprintf(&timeout_str, "%.2d:%.2d:%.2d", timeout_tm.tm_hour,
+ timeout_tm.tm_min, timeout_tm.tm_sec);
+
+ d_printf("Key: %s\t Timeout: %s\t Value: %s %s\n", keystr,
+ timeout_str, datastr, timeout > now_t ? "": "(expired)");
+}
+
+static void delete_cache_entry(const char* keystr, const char* datastr,
+ const time_t timeout, void* dptr)
+{
+ if (!gencache_del(keystr))
+ d_printf("Couldn't delete entry! key = %s\n", keystr);
+}
+
+
+/**
+ * Parse text representation of timeout value
+ *
+ * @param timeout_str string containing text representation of the timeout
+ * @return numeric timeout of time_t type
+ **/
+static time_t parse_timeout(const char* timeout_str)
+{
+ char sign = '\0', *number = NULL, unit = '\0';
+ int len, number_begin, number_end;
+ time_t timeout;
+
+ /* sign detection */
+ if (timeout_str[0] == '!' || timeout_str[0] == '+') {
+ sign = timeout_str[0];
+ number_begin = 1;
+ } else {
+ number_begin = 0;
+ }
+
+ /* unit detection */
+ len = strlen(timeout_str);
+ switch (timeout_str[len - 1]) {
+ case 's':
+ case 'm':
+ case 'h':
+ case 'd':
+ case 'w': unit = timeout_str[len - 1];
+ }
+
+ /* number detection */
+ len = (sign) ? strlen(&timeout_str[number_begin]) : len;
+ number_end = (unit) ? len - 1 : len;
+ number = strndup(&timeout_str[number_begin], number_end);
+
+ /* calculate actual timeout value */
+ timeout = (time_t)atoi(number);
+
+ switch (unit) {
+ case 'm': timeout *= 60; break;
+ case 'h': timeout *= 60*60; break;
+ case 'd': timeout *= 60*60*24; break;
+ case 'w': timeout *= 60*60*24*7; break; /* that's fair enough, I think :) */
+ }
+
+ switch (sign) {
+ case '!': timeout = time(NULL) - timeout; break;
+ case '+':
+ default: timeout += time(NULL); break;
+ }
+
+ if (number) SAFE_FREE(number);
+ return timeout;
+}
+
+
+/**
+ * Add an entry to the cache. If it does exist, then set it.
+ *
+ * @param argv key, value and timeout are passed in command line
+ * @return 0 on success, otherwise failure
+ **/
+static int net_cache_add(int argc, const char **argv)
+{
+ const char *keystr, *datastr, *timeout_str;
+ time_t timeout;
+
+ if (argc < 3) {
+ d_printf("\nUsage: net cache add <key string> <data string> <timeout>\n");
+ return -1;
+ }
+
+ keystr = argv[0];
+ datastr = argv[1];
+ timeout_str = argv[2];
+
+ /* parse timeout given in command line */
+ timeout = parse_timeout(timeout_str);
+ if (!timeout) {
+ d_printf("Invalid timeout argument.\n");
+ return -1;
+ }
+
+ if (gencache_set(keystr, datastr, timeout)) {
+ d_printf("New cache entry stored successfully.\n");
+ gencache_shutdown();
+ return 0;
+ }
+
+ d_printf("Entry couldn't be added. Perhaps there's already such a key.\n");
+ gencache_shutdown();
+ return -1;
+}
+
+
+/**
+ * Set new value of an existing entry in the cache. Fail If the entry doesn't
+ * exist.
+ *
+ * @param argv key being searched and new value and timeout to set in the entry
+ * @return 0 on success, otherwise failure
+ **/
+static int net_cache_set(int argc, const char **argv)
+{
+ const char *keystr, *datastr, *timeout_str;
+ time_t timeout;
+
+ if (argc < 3) {
+ d_printf("\nUsage: net cache set <key string> <data string> <timeout>\n");
+ return -1;
+ }
+
+ keystr = argv[0];
+ datastr = argv[1];
+ timeout_str = argv[2];
+
+ /* parse timeout given in command line */
+ timeout = parse_timeout(timeout_str);
+ if (!timeout) {
+ d_printf("Invalid timeout argument.\n");
+ return -1;
+ }
+
+ if (gencache_set_only(keystr, datastr, timeout)) {
+ d_printf("Cache entry set successfully.\n");
+ gencache_shutdown();
+ return 0;
+ }
+
+ d_printf("Entry couldn't be set. Perhaps there's no such a key.\n");
+ gencache_shutdown();
+ return -1;
+}
+
+
+/**
+ * Delete an entry in the cache
+ *
+ * @param argv key to delete an entry of
+ * @return 0 on success, otherwise failure
+ **/
+static int net_cache_del(int argc, const char **argv)
+{
+ const char *keystr = argv[0];
+
+ if (argc < 1) {
+ d_printf("\nUsage: net cache del <key string>\n");
+ return -1;
+ }
+
+ if(gencache_del(keystr)) {
+ d_printf("Entry deleted.\n");
+ return 0;
+ }
+
+ d_printf("Couldn't delete specified entry\n");
+ return -1;
+}
+
+
+/**
+ * Get and display an entry from the cache
+ *
+ * @param argv key to search an entry of
+ * @return 0 on success, otherwise failure
+ **/
+static int net_cache_get(int argc, const char **argv)
+{
+ const char* keystr = argv[0];
+ char* valuestr;
+ time_t timeout;
+
+ if (argc < 1) {
+ d_printf("\nUsage: net cache get <key>\n");
+ return -1;
+ }
+
+ if (gencache_get(keystr, &valuestr, &timeout)) {
+ print_cache_entry(keystr, valuestr, timeout, NULL);
+ return 0;
+ }
+
+ d_printf("Failed to find entry\n");
+ return -1;
+}
+
+
+/**
+ * Search an entry/entries in the cache
+ *
+ * @param argv key pattern to match the entries to
+ * @return 0 on success, otherwise failure
+ **/
+static int net_cache_search(int argc, const char **argv)
+{
+ const char* pattern;
+
+ if (argc < 1) {
+ d_printf("Usage: net cache search <pattern>\n");
+ return -1;
+ }
+
+ pattern = argv[0];
+ gencache_iterate(print_cache_entry, NULL, pattern);
+ return 0;
+}
+
+
+/**
+ * List the contents of the cache
+ *
+ * @param argv ignored in this functionailty
+ * @return always returns 0
+ **/
+static int net_cache_list(int argc, const char **argv)
+{
+ const char* pattern = "*";
+ gencache_iterate(print_cache_entry, NULL, pattern);
+ gencache_shutdown();
+ return 0;
+}
+
+
+/**
+ * Flush the whole cache
+ *
+ * @param argv ignored in this functionality
+ * @return always returns 0
+ **/
+static int net_cache_flush(int argc, const char **argv)
+{
+ const char* pattern = "*";
+ gencache_iterate(delete_cache_entry, NULL, pattern);
+ gencache_shutdown();
+ return 0;
+}
+
+
+/**
+ * Short help
+ *
+ * @param argv ignored in this functionality
+ * @return always returns -1
+ **/
+static int net_cache_usage(int argc, const char **argv)
+{
+ d_printf(" net cache add \t add add new cache entry\n");
+ d_printf(" net cache set \t set new value for existing cache entry\n");
+ d_printf(" net cache del \t delete existing cache entry by key\n");
+ d_printf(" net cache flush \t delete all entries existing in the cache\n");
+ d_printf(" net cache get \t get cache entry by key\n");
+ d_printf(" net cache search \t search for entries in the cache, by given pattern\n");
+ d_printf(" net cache list \t list all cache entries (just like search for \"*\")\n");
+ return -1;
+}
+
+
+/**
+ * Entry point to 'net cache' subfunctionality
+ *
+ * @param argv arguments passed to further called functions
+ * @return whatever further functions return
+ **/
+int net_cache(int argc, const char **argv)
+{
+ struct functable func[] = {
+ {"add", net_cache_add},
+ {"set", net_cache_set},
+ {"del", net_cache_del},
+ {"get", net_cache_get},
+ {"search", net_cache_search},
+ {"list", net_cache_list},
+ {"flush", net_cache_flush},
+ {NULL, NULL}
+ };
+
+ return net_run_function(argc, argv, func, net_cache_usage);
+}
diff --git a/source/utils/net_groupmap.c b/source/utils/net_groupmap.c
new file mode 100644
index 00000000000..78e763e1818
--- /dev/null
+++ b/source/utils/net_groupmap.c
@@ -0,0 +1,767 @@
+/*
+ * Unix SMB/CIFS implementation.
+ * RPC Pipe client / server routines
+ * Copyright (C) Andrew Tridgell 1992-2000,
+ * Copyright (C) Jean François Micouleau 1998-2001.
+ * Copyright (C) Gerald Carter 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 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 "../utils/net.h"
+
+
+/*********************************************************
+ utility function to parse an integer parameter from
+ "parameter = value"
+**********************************************************/
+static uint32 get_int_param( const char* param )
+{
+ char *p;
+
+ p = strchr( param, '=' );
+ if ( !p )
+ return 0;
+
+ return atoi(p+1);
+}
+
+/*********************************************************
+ utility function to parse an integer parameter from
+ "parameter = value"
+**********************************************************/
+static char* get_string_param( const char* param )
+{
+ char *p;
+
+ p = strchr( param, '=' );
+ if ( !p )
+ return NULL;
+
+ return (p+1);
+}
+
+/*********************************************************
+ Figure out if the input was an NT group or a SID string.
+ Return the SID.
+**********************************************************/
+static BOOL get_sid_from_input(DOM_SID *sid, char *input)
+{
+ GROUP_MAP map;
+
+ if (StrnCaseCmp( input, "S-", 2)) {
+ /* Perhaps its the NT group name? */
+ if (!pdb_getgrnam(&map, input)) {
+ printf("NT Group %s doesn't exist in mapping DB\n", input);
+ return False;
+ } else {
+ *sid = map.sid;
+ }
+ } else {
+ if (!string_to_sid(sid, input)) {
+ printf("converting sid %s from a string failed!\n", input);
+ return False;
+ }
+ }
+ return True;
+}
+
+/*********************************************************
+ Dump a GROUP_MAP entry to stdout (long or short listing)
+**********************************************************/
+
+static void print_map_entry ( GROUP_MAP map, BOOL long_list )
+{
+ fstring string_sid;
+ fstring group_type;
+
+ decode_sid_name_use(group_type, map.sid_name_use);
+ sid_to_string(string_sid, &map.sid);
+
+ if (!long_list)
+ d_printf("%s (%s) -> %s\n", map.nt_name, string_sid, gidtoname(map.gid));
+ else {
+ d_printf("%s\n", map.nt_name);
+ d_printf("\tSID : %s\n", string_sid);
+ d_printf("\tUnix group: %s\n", gidtoname(map.gid));
+ d_printf("\tGroup type: %s\n", group_type);
+ d_printf("\tComment : %s\n", map.comment);
+ }
+
+}
+/*********************************************************
+ List the groups.
+**********************************************************/
+static int net_groupmap_list(int argc, const char **argv)
+{
+ int entries;
+ BOOL long_list = False;
+ int i;
+ fstring ntgroup = "";
+ fstring sid_string = "";
+
+ /* get the options */
+ for ( i=0; i<argc; i++ ) {
+ if ( !StrCaseCmp(argv[i], "verbose")) {
+ long_list = True;
+ }
+ else if ( !StrnCaseCmp(argv[i], "ntgroup", strlen("ntgroup")) ) {
+ fstrcpy( ntgroup, get_string_param( argv[i] ) );
+ if ( !ntgroup[0] ) {
+ d_printf("must supply a name\n");
+ return -1;
+ }
+ }
+ else if ( !StrnCaseCmp(argv[i], "sid", strlen("sid")) ) {
+ fstrcpy( sid_string, get_string_param( argv[i] ) );
+ if ( !sid_string[0] ) {
+ d_printf("must supply a SID\n");
+ return -1;
+ }
+ }
+ else {
+ d_printf("Bad option: %s\n", argv[i]);
+ return -1;
+ }
+ }
+
+ /* list a single group is given a name */
+ if ( ntgroup[0] || sid_string[0] ) {
+ DOM_SID sid;
+ GROUP_MAP map;
+
+ if ( sid_string[0] )
+ fstrcpy( ntgroup, sid_string);
+
+ if (!get_sid_from_input(&sid, ntgroup)) {
+ return -1;
+ }
+
+ /* Get the current mapping from the database */
+ if(!pdb_getgrsid(&map, sid)) {
+ d_printf("Failure to local group SID in the database\n");
+ return -1;
+ }
+
+ print_map_entry( map, long_list );
+ }
+ else {
+ GROUP_MAP *map=NULL;
+ /* enumerate all group mappings */
+ if (!pdb_enum_group_mapping(SID_NAME_UNKNOWN, &map, &entries, ENUM_ALL_MAPPED))
+ return -1;
+
+ for (i=0; i<entries; i++) {
+ print_map_entry( map[i], long_list );
+ }
+
+ SAFE_FREE(map);
+ }
+
+ return 0;
+}
+
+/*********************************************************
+ Add a new group mapping entry
+**********************************************************/
+
+static int net_groupmap_add(int argc, const char **argv)
+{
+ DOM_SID sid;
+ fstring ntgroup = "";
+ fstring unixgrp = "";
+ fstring string_sid = "";
+ fstring type = "";
+ fstring ntcomment = "";
+ enum SID_NAME_USE sid_type = SID_NAME_DOM_GRP;
+ uint32 rid = 0;
+ gid_t gid;
+ int i;
+
+ /* get the options */
+ for ( i=0; i<argc; i++ ) {
+ if ( !StrnCaseCmp(argv[i], "rid", strlen("rid")) ) {
+ rid = get_int_param(argv[i]);
+ if ( rid < DOMAIN_GROUP_RID_ADMINS ) {
+ d_printf("RID must be greater than %d\n", (uint32)DOMAIN_GROUP_RID_ADMINS-1);
+ return -1;
+ }
+ }
+ else if ( !StrnCaseCmp(argv[i], "unixgroup", strlen("unixgroup")) ) {
+ fstrcpy( unixgrp, get_string_param( argv[i] ) );
+ if ( !unixgrp[0] ) {
+ d_printf("must supply a name\n");
+ return -1;
+ }
+ }
+ else if ( !StrnCaseCmp(argv[i], "ntgroup", strlen("ntgroup")) ) {
+ fstrcpy( ntgroup, get_string_param( argv[i] ) );
+ if ( !ntgroup[0] ) {
+ d_printf("must supply a name\n");
+ return -1;
+ }
+ }
+ else if ( !StrnCaseCmp(argv[i], "sid", strlen("sid")) ) {
+ fstrcpy( string_sid, get_string_param( argv[i] ) );
+ if ( !string_sid[0] ) {
+ d_printf("must supply a SID\n");
+ return -1;
+ }
+ }
+ else if ( !StrnCaseCmp(argv[i], "comment", strlen("comment")) ) {
+ fstrcpy( ntcomment, get_string_param( argv[i] ) );
+ if ( !ntcomment[0] ) {
+ d_printf("must supply a comment string\n");
+ return -1;
+ }
+ }
+ else if ( !StrnCaseCmp(argv[i], "type", strlen("type")) ) {
+ fstrcpy( type, get_string_param( argv[i] ) );
+ switch ( type[0] ) {
+ case 'b':
+ case 'B':
+ sid_type = SID_NAME_WKN_GRP;
+ break;
+ case 'd':
+ case 'D':
+ sid_type = SID_NAME_DOM_GRP;
+ break;
+ case 'l':
+ case 'L':
+ sid_type = SID_NAME_ALIAS;
+ break;
+ }
+ }
+ else {
+ d_printf("Bad option: %s\n", argv[i]);
+ return -1;
+ }
+ }
+
+ if ( !unixgrp[0] ) {
+ d_printf("Usage: net groupmap add {rid=<int>|sid=<string>} unixgroup=<string> [type=<domain|local|builtin>] [ntgroup=<string>] [comment=<string>]\n");
+ return -1;
+ }
+
+ if ( (gid = nametogid(unixgrp)) == (gid_t)-1 ) {
+ d_printf("Can't lookup UNIX group %s\n", unixgrp);
+ return -1;
+ }
+
+ if ( (rid == 0) && (string_sid[0] == '\0') ) {
+ d_printf("No rid or sid specified, choosing algorithmic mapping\n");
+ rid = pdb_gid_to_group_rid(gid);
+ }
+
+ /* append the rid to our own domain/machine SID if we don't have a full SID */
+ if ( !string_sid[0] ) {
+ sid_copy(&sid, get_global_sam_sid());
+ sid_append_rid(&sid, rid);
+ sid_to_string(string_sid, &sid);
+ }
+
+ if (!ntcomment[0])
+ fstrcpy(ntcomment, "Local Unix group");
+
+ if (!ntgroup[0] )
+ fstrcpy( ntgroup, unixgrp );
+
+
+ if (!add_initial_entry(gid, string_sid, sid_type, ntgroup, ntcomment)) {
+ d_printf("adding entry for group %s failed!\n", ntgroup);
+ return -1;
+ }
+
+ d_printf("Successully added group %s to the mapping db\n", ntgroup);
+ return 0;
+}
+
+static int net_groupmap_modify(int argc, const char **argv)
+{
+ DOM_SID sid;
+ GROUP_MAP map;
+ fstring ntcomment = "";
+ fstring type = "";
+ fstring ntgroup = "";
+ fstring unixgrp = "";
+ fstring sid_string = "";
+ enum SID_NAME_USE sid_type = SID_NAME_UNKNOWN;
+ int i;
+ gid_t gid;
+
+ /* get the options */
+ for ( i=0; i<argc; i++ ) {
+ if ( !StrnCaseCmp(argv[i], "ntgroup", strlen("ntgroup")) ) {
+ fstrcpy( ntgroup, get_string_param( argv[i] ) );
+ if ( !ntgroup[0] ) {
+ d_printf("must supply a name\n");
+ return -1;
+ }
+ }
+ else if ( !StrnCaseCmp(argv[i], "sid", strlen("sid")) ) {
+ fstrcpy( sid_string, get_string_param( argv[i] ) );
+ if ( !sid_string[0] ) {
+ d_printf("must supply a name\n");
+ return -1;
+ }
+ }
+ else if ( !StrnCaseCmp(argv[i], "comment", strlen("comment")) ) {
+ fstrcpy( ntcomment, get_string_param( argv[i] ) );
+ if ( !ntcomment[0] ) {
+ d_printf("must supply a comment string\n");
+ return -1;
+ }
+ }
+ else if ( !StrnCaseCmp(argv[i], "unixgroup", strlen("unixgroup")) ) {
+ fstrcpy( unixgrp, get_string_param( argv[i] ) );
+ if ( !unixgrp[0] ) {
+ d_printf("must supply a group name\n");
+ return -1;
+ }
+ }
+ else if ( !StrnCaseCmp(argv[i], "type", strlen("type")) ) {
+ fstrcpy( type, get_string_param( argv[i] ) );
+ switch ( type[0] ) {
+ case 'd':
+ case 'D':
+ sid_type = SID_NAME_DOM_GRP;
+ break;
+ case 'l':
+ case 'L':
+ sid_type = SID_NAME_ALIAS;
+ break;
+ }
+ }
+ else {
+ d_printf("Bad option: %s\n", argv[i]);
+ return -1;
+ }
+ }
+
+ if ( !ntgroup[0] && !sid_string[0] ) {
+ d_printf("Usage: net groupmap modify {ntgroup=<string>|sid=<SID>} [comment=<string>] [unixgroup=<string>] [type=<domain|local>]\n");
+ return -1;
+ }
+
+ /* give preference to the SID; if both the ntgroup name and SID
+ are defined, use the SID and assume that the group name could be a
+ new name */
+
+ if ( sid_string[0] ) {
+ if (!get_sid_from_input(&sid, sid_string)) {
+ return -1;
+ }
+ }
+ else {
+ if (!get_sid_from_input(&sid, ntgroup)) {
+ return -1;
+ }
+ }
+
+ /* Get the current mapping from the database */
+ if(!pdb_getgrsid(&map, sid)) {
+ d_printf("Failure to local group SID in the database\n");
+ return -1;
+ }
+
+ /*
+ * Allow changing of group type only between domain and local
+ * We disallow changing Builtin groups !!! (SID problem)
+ */
+ if (sid_type != SID_NAME_UNKNOWN) {
+ if (map.sid_name_use == SID_NAME_WKN_GRP) {
+ d_printf("You can only change between domain and local groups.\n");
+ return -1;
+ }
+
+ map.sid_name_use=sid_type;
+ }
+
+ /* Change comment if new one */
+ if ( ntcomment[0] )
+ fstrcpy( map.comment, ntcomment );
+
+ if ( ntgroup[0] )
+ fstrcpy( map.nt_name, ntgroup );
+
+ if ( unixgrp[0] ) {
+ gid = nametogid( unixgrp );
+ if ( gid == -1 ) {
+ d_printf("Unable to lookup UNIX group %s. Make sure the group exists.\n",
+ unixgrp);
+ return -1;
+ }
+
+ map.gid = gid;
+ }
+
+ if ( !pdb_update_group_mapping_entry(&map) ) {
+ d_printf("Could not update group database\n");
+ return -1;
+ }
+
+ d_printf("Updated mapping entry for %s\n", map.nt_name);
+
+ return 0;
+}
+
+static int net_groupmap_delete(int argc, const char **argv)
+{
+ DOM_SID sid;
+ fstring ntgroup = "";
+ fstring sid_string = "";
+ int i;
+
+ /* get the options */
+ for ( i=0; i<argc; i++ ) {
+ if ( !StrnCaseCmp(argv[i], "ntgroup", strlen("ntgroup")) ) {
+ fstrcpy( ntgroup, get_string_param( argv[i] ) );
+ if ( !ntgroup[0] ) {
+ d_printf("must supply a name\n");
+ return -1;
+ }
+ }
+ else if ( !StrnCaseCmp(argv[i], "sid", strlen("sid")) ) {
+ fstrcpy( sid_string, get_string_param( argv[i] ) );
+ if ( !sid_string[0] ) {
+ d_printf("must supply a SID\n");
+ return -1;
+ }
+ }
+ else {
+ d_printf("Bad option: %s\n", argv[i]);
+ return -1;
+ }
+ }
+
+ if ( !ntgroup[0] && !sid_string[0]) {
+ d_printf("Usage: net groupmap delete {ntgroup=<string>|sid=<SID>}\n");
+ return -1;
+ }
+
+ /* give preference to the SID if we have that */
+
+ if ( sid_string[0] )
+ fstrcpy( ntgroup, sid_string );
+
+ if ( !get_sid_from_input(&sid, ntgroup) ) {
+ d_printf("Unable to resolve group %s to a SID\n", ntgroup);
+ return -1;
+ }
+
+ if ( !pdb_delete_group_mapping_entry(sid) ) {
+ printf("Failed to removing group %s from the mapping db!\n", ntgroup);
+ return -1;
+ }
+
+ d_printf("Sucessfully removed %s from the mapping db\n", ntgroup);
+
+ return 0;
+}
+
+static int net_groupmap_set(int argc, const char **argv)
+{
+ const char *ntgroup = NULL;
+ struct group *grp = NULL;
+ GROUP_MAP map;
+ BOOL have_map = False;
+
+ if ((argc < 1) || (argc > 2)) {
+ d_printf("Usage: net groupmap set \"NT Group\" "
+ "[\"unix group\"] [-C \"comment\"] [-L] [-D]\n");
+ return -1;
+ }
+
+ if ( opt_localgroup && opt_domaingroup ) {
+ d_printf("Can only specify -L or -D, not both\n");
+ return -1;
+ }
+
+ ntgroup = argv[0];
+
+ if (argc == 2) {
+ grp = getgrnam(argv[1]);
+
+ if (grp == NULL) {
+ d_printf("Could not find unix group %s\n", argv[1]);
+ return -1;
+ }
+ }
+
+ have_map = pdb_getgrnam(&map, ntgroup);
+
+ if (!have_map) {
+ DOM_SID sid;
+ have_map = ( (strncmp(ntgroup, "S-", 2) == 0) &&
+ string_to_sid(&sid, ntgroup) &&
+ pdb_getgrsid(&map, sid) );
+ }
+
+ if (!have_map) {
+
+ /* Ok, add it */
+
+ if (grp == NULL) {
+ d_printf("Could not find group mapping for %s\n",
+ ntgroup);
+ return -1;
+ }
+
+ map.gid = grp->gr_gid;
+
+ if (opt_rid == 0) {
+ opt_rid = pdb_gid_to_group_rid(map.gid);
+ }
+
+ sid_copy(&map.sid, get_global_sam_sid());
+ sid_append_rid(&map.sid, opt_rid);
+
+ map.sid_name_use = SID_NAME_DOM_GRP;
+ fstrcpy(map.nt_name, ntgroup);
+ fstrcpy(map.comment, "");
+
+ if (!pdb_add_group_mapping_entry(&map)) {
+ d_printf("Could not add mapping entry for %s\n",
+ ntgroup);
+ return -1;
+ }
+ }
+
+ /* Now we have a mapping entry, update that stuff */
+
+ if ( opt_localgroup || opt_domaingroup ) {
+ if (map.sid_name_use == SID_NAME_WKN_GRP) {
+ d_printf("Can't change type of the BUILTIN group %s\n",
+ map.nt_name);
+ return -1;
+ }
+ }
+
+ if (opt_localgroup)
+ map.sid_name_use = SID_NAME_ALIAS;
+
+ if (opt_domaingroup)
+ map.sid_name_use = SID_NAME_DOM_GRP;
+
+ /* The case (opt_domaingroup && opt_localgroup) was tested for above */
+
+ if (strlen(opt_comment) > 0)
+ fstrcpy(map.comment, opt_comment);
+
+ if (strlen(opt_newntname) > 0)
+ fstrcpy(map.nt_name, opt_newntname);
+
+ if (grp != NULL)
+ map.gid = grp->gr_gid;
+
+ if (!pdb_update_group_mapping_entry(&map)) {
+ d_printf("Could not update group mapping for %s\n", ntgroup);
+ return -1;
+ }
+
+ return 0;
+}
+
+static int net_groupmap_cleanup(int argc, const char **argv)
+{
+ GROUP_MAP *map = NULL;
+ int i, entries;
+
+ if (!pdb_enum_group_mapping(SID_NAME_UNKNOWN, &map, &entries,
+ ENUM_ALL_MAPPED)) {
+ d_printf("Could not list group mappings\n");
+ return -1;
+ }
+
+ for (i=0; i<entries; i++) {
+
+ if (map[i].sid_name_use == SID_NAME_WKN_GRP)
+ continue;
+
+ if (map[i].gid == -1)
+ printf("Group %s is not mapped\n", map[i].nt_name);
+
+ if (!sid_check_is_in_our_domain(&map[i].sid)) {
+ printf("Deleting mapping for NT Group %s, sid %s\n",
+ map[i].nt_name,
+ sid_string_static(&map[i].sid));
+ pdb_delete_group_mapping_entry(map[i].sid);
+ }
+ }
+
+ SAFE_FREE(map);
+
+ return 0;
+}
+
+static int net_groupmap_addmem(int argc, const char **argv)
+{
+ DOM_SID alias, member;
+
+ if ( (argc != 2) ||
+ !string_to_sid(&alias, argv[0]) ||
+ !string_to_sid(&member, argv[1]) ) {
+ d_printf("Usage: net groupmap addmem alias-sid member-sid\n");
+ return -1;
+ }
+
+ if (!pdb_add_aliasmem(&alias, &member)) {
+ d_printf("Could not add sid %s to alias %s\n",
+ argv[1], argv[0]);
+ return -1;
+ }
+
+ return 0;
+}
+
+static int net_groupmap_delmem(int argc, const char **argv)
+{
+ DOM_SID alias, member;
+
+ if ( (argc != 2) ||
+ !string_to_sid(&alias, argv[0]) ||
+ !string_to_sid(&member, argv[1]) ) {
+ d_printf("Usage: net groupmap delmem alias-sid member-sid\n");
+ return -1;
+ }
+
+ if (!pdb_del_aliasmem(&alias, &member)) {
+ d_printf("Could not delete sid %s from alias %s\n",
+ argv[1], argv[0]);
+ return -1;
+ }
+
+ return 0;
+}
+
+static int net_groupmap_listmem(int argc, const char **argv)
+{
+ DOM_SID alias;
+ DOM_SID *members;
+ int i, num;
+ NTSTATUS result;
+
+ if ( (argc != 1) ||
+ !string_to_sid(&alias, argv[0]) ) {
+ d_printf("Usage: net groupmap listmem alias-sid\n");
+ return -1;
+ }
+
+ if (!pdb_enum_aliasmem(&alias, &members, &num)) {
+ d_printf("Could not list members for sid %s: %s\n",
+ argv[0], nt_errstr(result));
+ return -1;
+ }
+
+ for (i = 0; i < num; i++) {
+ printf("%s\n", sid_string_static(&(members[i])));
+ }
+
+ SAFE_FREE(members);
+
+ return 0;
+}
+
+static int net_groupmap_memberships(int argc, const char **argv)
+{
+ DOM_SID member;
+ DOM_SID *aliases;
+ int i, num;
+ NTSTATUS result;
+
+ if ( (argc != 1) ||
+ !string_to_sid(&member, argv[0]) ) {
+ d_printf("Usage: net groupmap memberof sid\n");
+ return -1;
+ }
+
+ if (!pdb_enum_alias_memberships(&member, &aliases, &num)) {
+ d_printf("Could not list memberships for sid %s: %s\n",
+ argv[0], nt_errstr(result));
+ return -1;
+ }
+
+ for (i = 0; i < num; i++) {
+ printf("%s\n", sid_string_static(&(aliases[i])));
+ }
+
+ SAFE_FREE(aliases);
+
+ return 0;
+}
+
+int net_help_groupmap(int argc, const char **argv)
+{
+ d_printf("net groupmap add"\
+ "\n Create a new group mapping\n");
+ d_printf("net groupmap modify"\
+ "\n Update a group mapping\n");
+ d_printf("net groupmap delete"\
+ "\n Remove a group mapping\n");
+ d_printf("net groupmap addmember"\
+ "\n Add a foreign alias member\n");
+ d_printf("net groupmap delmember"\
+ "\n Delete a foreign alias member\n");
+ d_printf("net groupmap listmembers"\
+ "\n List foreign group members\n");
+ d_printf("net groupmap memberships"\
+ "\n List foreign group memberships\n");
+ d_printf("net groupmap list"\
+ "\n List current group map\n");
+ d_printf("net groupmap set"\
+ "\n Set group mapping\n");
+ d_printf("net groupmap cleanup"\
+ "\n Remove foreign group mapping entries\n");
+
+ return -1;
+}
+
+
+/***********************************************************
+ migrated functionality from smbgroupedit
+ **********************************************************/
+int net_groupmap(int argc, const char **argv)
+{
+ struct functable func[] = {
+ {"add", net_groupmap_add},
+ {"modify", net_groupmap_modify},
+ {"delete", net_groupmap_delete},
+ {"set", net_groupmap_set},
+ {"cleanup", net_groupmap_cleanup},
+ {"addmem", net_groupmap_addmem},
+ {"delmem", net_groupmap_delmem},
+ {"listmem", net_groupmap_listmem},
+ {"memberships", net_groupmap_memberships},
+ {"list", net_groupmap_list},
+ {"help", net_help_groupmap},
+ {NULL, NULL}
+ };
+
+ /* we shouldn't have silly checks like this */
+#if 0
+ if (getuid() != 0) {
+ d_printf("You must be root to edit group mappings.\nExiting...\n");
+ return -1;
+ }
+#endif
+
+ if ( argc )
+ return net_run_function(argc, argv, func, net_help_groupmap);
+
+ return net_help_groupmap( argc, argv );
+}
+
diff --git a/source/utils/net_help.c b/source/utils/net_help.c
new file mode 100644
index 00000000000..38261be90a7
--- /dev/null
+++ b/source/utils/net_help.c
@@ -0,0 +1,220 @@
+/*
+ Samba Unix/Linux SMB client library
+ net help commands
+ Copyright (C) 2002 Jim McDonough (jmcd@us.ibm.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 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 "../utils/net.h"
+
+int net_common_methods_usage(int argc, const char**argv)
+{
+ d_printf("Valid methods: (auto-detected if not specified)\n");
+ d_printf("\tads\t\t\t\tActive Directory (LDAP/Kerberos)\n");
+ d_printf("\trpc\t\t\t\tDCE-RPC\n");
+ d_printf("\trap\t\t\t\tRAP (older systems)\n");
+ d_printf("\n");
+ return 0;
+}
+
+int net_common_flags_usage(int argc, const char **argv)
+{
+ d_printf("Valid targets: choose one (none defaults to localhost)\n");
+ d_printf("\t-S or --server=<server>\t\tserver name\n");
+ d_printf("\t-I or --ipaddress=<ipaddr>\taddress of target server\n");
+ d_printf("\t-w or --workgroup=<wg>\t\ttarget workgroup or domain\n");
+
+ d_printf("\n");
+ d_printf("Valid miscellaneous options are:\n"); /* misc options */
+ d_printf("\t-p or --port=<port>\t\tconnection port on target\n");
+ d_printf("\t-W or --myworkgroup=<wg>\tclient workgroup\n");
+ d_printf("\t-d or --debuglevel=<level>\tdebug level (0-10)\n");
+ d_printf("\t-n or --myname=<name>\t\tclient name\n");
+ d_printf("\t-U or --user=<name>\t\tuser name\n");
+ d_printf("\t-s or --configfile=<path>\tpathname of smb.conf file\n");
+ d_printf("\t-l or --long\t\t\tDisplay full information\n");
+ d_printf("\t-V or --version\t\t\tPrint samba version information\n");
+ d_printf("\t-P or --machine-pass\t\tAuthenticate as machine account\n");
+ return -1;
+}
+
+static int help_usage(int argc, const char **argv)
+{
+ d_printf(
+"\n"\
+"Usage: net help <function>\n"\
+"\n"\
+"Valid functions are:\n"\
+" RPC RAP ADS FILE SHARE SESSION SERVER DOMAIN PRINTQ USER GROUP VALIDATE\n"\
+" GROUPMEMBER ADMIN SERVICE PASSWORD TIME LOOKUP GETLOCALSID SETLOCALSID\n"\
+" CHANGESCRETPW\n");
+ return -1;
+}
+
+int net_help_user(int argc, const char **argv)
+{
+ d_printf("\nnet [<method>] user [misc. options] [targets]"\
+ "\n\tList users\n\n");
+ d_printf("net [<method>] user DELETE <name> [misc. options] [targets]"\
+ "\n\tDelete specified user\n");
+ d_printf("\nnet [<method>] user INFO <name> [misc. options] [targets]"\
+ "\n\tList the domain groups of the specified user\n");
+ d_printf("\nnet [<method>] user ADD <name> [password] [-c container] "\
+ "[-F user flags] [misc. options]"\
+ " [targets]\n\tAdd specified user\n");
+
+ net_common_methods_usage(argc, argv);
+ net_common_flags_usage(argc, argv);
+ d_printf("\t-C or --comment=<comment>\tdescriptive comment (for add only)\n");
+ d_printf("\t-c or --container=<container>\tLDAP container, defaults to cn=Users (for add in ADS only)\n");
+ return -1;
+}
+
+int net_help_group(int argc, const char **argv)
+{
+ d_printf("net [<method>] group [misc. options] [targets]"\
+ "\n\tList user groups\n\n");
+ d_printf("net rpc group LIST [global|local|builtin]* [misc. options]"\
+ "\n\tList specific user groups\n\n");
+ d_printf("net [<method>] group DELETE <name> "\
+ "[misc. options] [targets]"\
+ "\n\tDelete specified group\n");
+ d_printf("\nnet [<method>] group ADD <name> [-C comment] [-c container]"\
+ " [misc. options] [targets]\n\tCreate specified group\n");
+ d_printf("\nnet rpc group MEMBERS <name>\n\tList Group Members\n\n");
+ net_common_methods_usage(argc, argv);
+ net_common_flags_usage(argc, argv);
+ d_printf("\t-C or --comment=<comment>\tdescriptive comment (for add only)\n");
+ d_printf("\t-c or --container=<container>\tLDAP container, defaults to cn=Users (for add in ADS only)\n");
+ return -1;
+}
+
+int net_help_join(int argc, const char **argv)
+{
+ d_printf("\nnet [<method>] join [misc. options]\n"
+ "\tjoins this server to a domain\n");
+ d_printf("Valid methods: (auto-detected if not specified)\n");
+ d_printf("\tads\t\t\t\tActive Directory (LDAP/Kerberos)\n");
+ d_printf("\trpc\t\t\t\tDCE-RPC\n");
+ net_common_flags_usage(argc, argv);
+ return -1;
+}
+
+int net_help_share(int argc, const char **argv)
+{
+ d_printf(
+ "\nnet [<method>] share [misc. options] [targets] \n"
+ "\tenumerates all exported resources (network shares) "
+ "on target server\n\n"
+ "net [<method>] share ADD <name=serverpath> [misc. options] [targets]"
+ "\n\tAdds a share from a server (makes the export active)\n\n"
+ "net [<method>] share DELETE <sharename> [misc. options] [targets]\n"
+ "\n\tDeletes a share from a server (makes the export inactive)\n");
+ net_common_methods_usage(argc, argv);
+ net_common_flags_usage(argc, argv);
+ d_printf(
+ "\t-C or --comment=<comment>\tdescriptive comment (for add only)\n"
+ "\t-M or --maxusers=<num>\t\tmax users allowed for share\n");
+ return -1;
+}
+
+int net_help_file(int argc, const char **argv)
+{
+ d_printf("net [<method>] file [misc. options] [targets]\n"\
+ "\tlists all open files on file server\n\n");
+ d_printf("net [<method>] file USER <username> "\
+ "[misc. options] [targets]"\
+ "\n\tlists all files opened by username on file server\n\n");
+ d_printf("net [<method>] file CLOSE <id> [misc. options] [targets]\n"\
+ "\tcloses specified file on target server\n\n");
+ d_printf("net [rap] file INFO <id> [misc. options] [targets]\n"\
+ "\tdisplays information about the specified open file\n");
+
+ net_common_methods_usage(argc, argv);
+ net_common_flags_usage(argc, argv);
+ return -1;
+}
+
+int net_help_status(int argc, const char **argv)
+{
+ d_printf(" net status sessions [parseable] "
+ "Show list of open sessions\n");
+ d_printf(" net status shares [parseable] "
+ "Show list of open shares\n");
+ return -1;
+}
+
+static int net_usage(int argc, const char **argv)
+{
+ d_printf(" net time\t\tto view or set time information\n"\
+ " net lookup\t\tto lookup host name or ip address\n"\
+ " net user\t\tto manage users\n"\
+ " net group\t\tto manage groups\n"\
+ " net groupmap\t\tto manage group mappings\n"\
+ " net join\t\tto join a domain\n"\
+ " net cache\t\tto operate on cache tdb file\n"\
+ " net getlocalsid [NAME]\tto get the SID for local name\n"\
+ " net setlocalsid SID\tto set the local domain SID\n"\
+ " net changesecretpw\tto change the machine password in the local secrets database only\n"\
+ " \tthis requires the -f flag as a safety barrier\n"\
+ " net status\t\tShow server status\n"\
+ "\n"\
+ " net ads <command>\tto run ADS commands\n"\
+ " net rap <command>\tto run RAP (pre-RPC) commands\n"\
+ " net rpc <command>\tto run RPC commands\n"\
+ "\n"\
+ "Type \"net help <option>\" to get more information on that option\n");
+ net_common_flags_usage(argc, argv);
+ return -1;
+}
+
+/*
+ handle "net help *" subcommands
+*/
+int net_help(int argc, const char **argv)
+{
+ struct functable func[] = {
+ {"ADS", net_ads_help},
+ {"RAP", net_rap_help},
+ {"RPC", net_rpc_help},
+
+ {"FILE", net_help_file},
+ {"SHARE", net_help_share},
+ {"SESSION", net_rap_session_usage},
+ {"SERVER", net_rap_server_usage},
+ {"DOMAIN", net_rap_domain_usage},
+ {"PRINTQ", net_rap_printq_usage},
+ {"USER", net_help_user},
+ {"GROUP", net_help_group},
+ {"GROUPMAP", net_help_groupmap},
+ {"JOIN", net_help_join},
+ {"VALIDATE", net_rap_validate_usage},
+ {"GROUPMEMBER", net_rap_groupmember_usage},
+ {"ADMIN", net_rap_admin_usage},
+ {"SERVICE", net_rap_service_usage},
+ {"PASSWORD", net_rap_password_usage},
+ {"TIME", net_time_usage},
+ {"LOOKUP", net_lookup_usage},
+#ifdef WITH_FAKE_KASERVER
+ {"AFSKEY", net_afskey_usage},
+#endif
+
+ {"HELP", help_usage},
+ {NULL, NULL}};
+
+ return net_run_function(argc, argv, func, net_usage);
+}
diff --git a/source/utils/net_idmap.c b/source/utils/net_idmap.c
new file mode 100644
index 00000000000..f5b4bf1b4a7
--- /dev/null
+++ b/source/utils/net_idmap.c
@@ -0,0 +1,264 @@
+/*
+ Samba Unix/Linux SMB client library
+ Distributed SMB/CIFS Server Management Utility
+ Copyright (C) 2003 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 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 "../utils/net.h"
+
+
+/***********************************************************
+ Helper function for net_idmap_dump. Dump one entry.
+ **********************************************************/
+static int net_idmap_dump_one_entry(TDB_CONTEXT *tdb,
+ TDB_DATA key,
+ TDB_DATA data,
+ void *unused)
+{
+ if (strcmp(key.dptr, "USER HWM") == 0) {
+ printf("USER HWM %d\n", IVAL(data.dptr,0));
+ return 0;
+ }
+
+ if (strcmp(key.dptr, "GROUP HWM") == 0) {
+ printf("GROUP HWM %d\n", IVAL(data.dptr,0));
+ return 0;
+ }
+
+ if (strncmp(key.dptr, "S-", 2) != 0)
+ return 0;
+
+ printf("%s %s\n", data.dptr, key.dptr);
+ return 0;
+}
+
+/***********************************************************
+ Dump the current idmap
+ **********************************************************/
+static int net_idmap_dump(int argc, const char **argv)
+{
+ TDB_CONTEXT *idmap_tdb;
+
+ if ( argc != 1 )
+ return net_help_idmap( argc, argv );
+
+ idmap_tdb = tdb_open_log(argv[0], 0, TDB_DEFAULT, O_RDONLY, 0);
+
+ if (idmap_tdb == NULL) {
+ d_printf("Could not open idmap: %s\n", argv[0]);
+ return -1;
+ }
+
+ tdb_traverse(idmap_tdb, net_idmap_dump_one_entry, NULL);
+
+ tdb_close(idmap_tdb);
+
+ return 0;
+}
+
+/***********************************************************
+ Fix up the HWMs after a idmap restore.
+ **********************************************************/
+
+struct hwms {
+ BOOL ok;
+ int user_hwm;
+ int group_hwm;
+};
+
+static int net_idmap_find_max_id(TDB_CONTEXT *tdb, TDB_DATA key, TDB_DATA data,
+ void *handle)
+{
+ struct hwms *hwms = (struct hwms *)handle;
+ int *idptr = NULL;
+ int id;
+
+ if (strncmp(key.dptr, "S-", 2) != 0)
+ return 0;
+
+ if (sscanf(data.dptr, "GID %d", &id) == 1) {
+ idptr = &hwms->group_hwm;
+ }
+
+ if (sscanf(data.dptr, "UID %d", &id) == 1) {
+ idptr = &hwms->user_hwm;
+ }
+
+ if (idptr == NULL) {
+ d_printf("Illegal idmap entry: [%s]->[%s]\n",
+ key.dptr, data.dptr);
+ hwms->ok = False;
+ return -1;
+ }
+
+ if (*idptr <= id)
+ *idptr = id+1;
+
+ return 0;
+}
+
+static NTSTATUS net_idmap_fixup_hwm(void)
+{
+ NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+ TDB_CONTEXT *idmap_tdb;
+ char *tdbfile = NULL;
+
+ struct hwms hwms;
+ struct hwms highest;
+
+ if (!lp_idmap_uid(&hwms.user_hwm, &highest.user_hwm) ||
+ !lp_idmap_gid(&hwms.group_hwm, &highest.group_hwm)) {
+ d_printf("idmap range missing\n");
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ tdbfile = strdup(lock_path("winbindd_idmap.tdb"));
+ if (!tdbfile) {
+ DEBUG(0, ("idmap_init: out of memory!\n"));
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ idmap_tdb = tdb_open_log(tdbfile, 0, TDB_DEFAULT, O_RDWR, 0);
+
+ if (idmap_tdb == NULL) {
+ d_printf("Could not open idmap: %s\n", tdbfile);
+ return NT_STATUS_NO_SUCH_FILE;
+ }
+
+ hwms.ok = True;
+
+ tdb_traverse(idmap_tdb, net_idmap_find_max_id, &hwms);
+
+ if (!hwms.ok) {
+ goto done;
+ }
+
+ d_printf("USER HWM: %d GROUP HWM: %d\n",
+ hwms.user_hwm, hwms.group_hwm);
+
+ if (hwms.user_hwm >= highest.user_hwm) {
+ d_printf("Highest UID out of uid range\n");
+ goto done;
+ }
+
+ if (hwms.group_hwm >= highest.group_hwm) {
+ d_printf("Highest GID out of gid range\n");
+ goto done;
+ }
+
+ if ((tdb_store_int32(idmap_tdb, "USER HWM", hwms.user_hwm) != 0) ||
+ (tdb_store_int32(idmap_tdb, "GROUP HWM", hwms.group_hwm) != 0)) {
+ d_printf("Could not store HWMs\n");
+ goto done;
+ }
+
+ result = NT_STATUS_OK;
+ done:
+ tdb_close(idmap_tdb);
+ return result;
+}
+
+/***********************************************************
+ Write entries from stdin to current local idmap
+ **********************************************************/
+static int net_idmap_restore(int argc, const char **argv)
+{
+ if (!idmap_init(lp_idmap_backend())) {
+ d_printf("Could not init idmap\n");
+ return -1;
+ }
+
+ while (!feof(stdin)) {
+ fstring line, sid_string;
+ int len;
+ unid_t id;
+ int type = ID_EMPTY;
+ DOM_SID sid;
+
+ if (fgets(line, sizeof(line)-1, stdin) == NULL)
+ break;
+
+ len = strlen(line);
+
+ if ( (len > 0) && (line[len-1] == '\n') )
+ line[len-1] = '\0';
+
+ /* Yuck - this is broken for sizeof(gid_t) != sizeof(int) */
+
+ if (sscanf(line, "GID %d %s", &id.gid, sid_string) == 2) {
+ type = ID_GROUPID;
+ }
+
+ /* Yuck - this is broken for sizeof(uid_t) != sizeof(int) */
+
+ if (sscanf(line, "UID %d %s", &id.uid, sid_string) == 2) {
+ type = ID_USERID;
+ }
+
+ if (type == ID_EMPTY) {
+ d_printf("ignoring invalid line [%s]\n", line);
+ continue;
+ }
+
+ if (!string_to_sid(&sid, sid_string)) {
+ d_printf("ignoring invalid sid [%s]\n", sid_string);
+ continue;
+ }
+
+ if (!NT_STATUS_IS_OK(idmap_set_mapping(&sid, id, type))) {
+ d_printf("Could not set mapping of %s %lu to sid %s\n",
+ (type == ID_GROUPID) ? "GID" : "UID",
+ (type == ID_GROUPID) ? (unsigned long)id.gid:
+ (unsigned long)id.uid,
+ sid_string_static(&sid));
+ continue;
+ }
+
+ }
+
+ idmap_close();
+
+ return NT_STATUS_IS_OK(net_idmap_fixup_hwm()) ? 0 : -1;
+}
+
+int net_help_idmap(int argc, const char **argv)
+{
+ d_printf("net idmap dump filename"\
+ "\n Dump current id mapping\n");
+
+ d_printf("net idmap restore"\
+ "\n Restore entries from stdin to current local idmap\n");
+
+ return -1;
+}
+
+/***********************************************************
+ Look at the current idmap
+ **********************************************************/
+int net_idmap(int argc, const char **argv)
+{
+ struct functable func[] = {
+ {"dump", net_idmap_dump},
+ {"restore", net_idmap_restore},
+ {"help", net_help_idmap},
+ {NULL, NULL}
+ };
+
+ return net_run_function(argc, argv, func, net_help_idmap);
+}
+
+
diff --git a/source/utils/net_lookup.c b/source/utils/net_lookup.c
new file mode 100644
index 00000000000..cef0ea5fbed
--- /dev/null
+++ b/source/utils/net_lookup.c
@@ -0,0 +1,258 @@
+/*
+ Samba Unix/Linux SMB client library
+ net lookup command
+ Copyright (C) 2001 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 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 "../utils/net.h"
+
+int net_lookup_usage(int argc, const char **argv)
+{
+ d_printf(
+" net lookup [host] HOSTNAME[#<type>]\n\tgives IP for a hostname\n\n"
+" net lookup ldap [domain]\n\tgives IP of domain's ldap server\n\n"
+" net lookup kdc [realm]\n\tgives IP of realm's kerberos KDC\n\n"
+" net lookup dc [domain]\n\tgives IP of domains Domain Controllers\n\n"
+" net lookup master [domain|wg]\n\tgive IP of master browser\n\n"
+);
+ return -1;
+}
+
+/* lookup a hostname giving an IP */
+static int net_lookup_host(int argc, const char **argv)
+{
+ struct in_addr ip;
+ int name_type = 0x20;
+ const char *name = argv[0];
+ char *p;
+
+ if (argc == 0)
+ return net_lookup_usage(argc, argv);
+
+ p = strchr_m(name,'#');
+ if (p) {
+ *p = '\0';
+ sscanf(++p,"%x",&name_type);
+ }
+
+ if (!resolve_name(name, &ip, name_type)) {
+ /* we deliberately use DEBUG() here to send it to stderr
+ so scripts aren't mucked up */
+ DEBUG(0,("Didn't find %s#%02x\n", name, name_type));
+ return -1;
+ }
+
+ d_printf("%s\n", inet_ntoa(ip));
+ return 0;
+}
+
+static void print_ldap_srvlist(char *srvlist)
+{
+ char *cur, *next;
+ struct in_addr ip;
+ BOOL printit;
+
+ cur = srvlist;
+ do {
+ next = strchr(cur,':');
+ if (next) *next++='\0';
+ printit = resolve_name(cur, &ip, 0x20);
+ cur=next;
+ next=cur ? strchr(cur,' ') :NULL;
+ if (next)
+ *next++='\0';
+ if (printit)
+ d_printf("%s:%s\n", inet_ntoa(ip), cur?cur:"");
+ cur = next;
+ } while (next);
+}
+
+
+static int net_lookup_ldap(int argc, const char **argv)
+{
+#ifdef HAVE_LDAP
+ char *srvlist;
+ const char *domain;
+ int rc;
+ struct in_addr addr;
+ struct hostent *hostent;
+
+ if (argc > 0)
+ domain = argv[0];
+ else
+ domain = opt_target_workgroup;
+
+ DEBUG(9, ("Lookup up ldap for domain %s\n", domain));
+ rc = ldap_domain2hostlist(domain, &srvlist);
+ if ((rc == LDAP_SUCCESS) && srvlist) {
+ print_ldap_srvlist(srvlist);
+ return 0;
+ }
+
+ DEBUG(9, ("Looking up DC for domain %s\n", domain));
+ if (!get_pdc_ip(domain, &addr))
+ return -1;
+
+ hostent = gethostbyaddr((char *) &addr.s_addr, sizeof(addr.s_addr),
+ AF_INET);
+ if (!hostent)
+ return -1;
+
+ DEBUG(9, ("Found DC with DNS name %s\n", hostent->h_name));
+ domain = strchr(hostent->h_name, '.');
+ if (!domain)
+ return -1;
+ domain++;
+
+ DEBUG(9, ("Looking up ldap for domain %s\n", domain));
+ rc = ldap_domain2hostlist(domain, &srvlist);
+ if ((rc == LDAP_SUCCESS) && srvlist) {
+ print_ldap_srvlist(srvlist);
+ return 0;
+ }
+ return -1;
+#endif
+ DEBUG(1,("No LDAP support\n"));
+ return -1;
+}
+
+static int net_lookup_dc(int argc, const char **argv)
+{
+ struct ip_service *ip_list;
+ struct in_addr addr;
+ char *pdc_str = NULL;
+ const char *domain=opt_target_workgroup;
+ int count, i;
+
+ if (argc > 0)
+ domain=argv[0];
+
+ /* first get PDC */
+ if (!get_pdc_ip(domain, &addr))
+ return -1;
+
+ asprintf(&pdc_str, "%s", inet_ntoa(addr));
+ d_printf("%s\n", pdc_str);
+
+ if (!get_sorted_dc_list(domain, &ip_list, &count, False)) {
+ SAFE_FREE(pdc_str);
+ return 0;
+ }
+ for (i=0;i<count;i++) {
+ char *dc_str = inet_ntoa(ip_list[i].ip);
+ if (!strequal(pdc_str, dc_str))
+ d_printf("%s\n", dc_str);
+ }
+ SAFE_FREE(pdc_str);
+ return 0;
+}
+
+static int net_lookup_master(int argc, const char **argv)
+{
+ struct in_addr master_ip;
+ const char *domain=opt_target_workgroup;
+
+ if (argc > 0)
+ domain=argv[0];
+
+ if (!find_master_ip(domain, &master_ip))
+ return -1;
+ d_printf("%s\n", inet_ntoa(master_ip));
+ return 0;
+}
+
+static int net_lookup_kdc(int argc, const char **argv)
+{
+#ifdef HAVE_KRB5
+ krb5_error_code rc;
+ krb5_context ctx;
+ struct sockaddr_in *addrs;
+ int num_kdcs,i;
+ krb5_data realm;
+ char **realms;
+
+ rc = krb5_init_context(&ctx);
+ if (rc) {
+ DEBUG(1,("krb5_init_context failed (%s)\n",
+ error_message(rc)));
+ return -1;
+ }
+
+ if (argc>0) {
+ realm.data = (krb5_pointer) argv[0];
+ realm.length = strlen(argv[0]);
+ } else if (lp_realm() && *lp_realm()) {
+ realm.data = (krb5_pointer) lp_realm();
+ realm.length = strlen(realm.data);
+ } else {
+ rc = krb5_get_host_realm(ctx, NULL, &realms);
+ if (rc) {
+ DEBUG(1,("krb5_gethost_realm failed (%s)\n",
+ error_message(rc)));
+ return -1;
+ }
+ realm.data = (krb5_pointer) *realms;
+ realm.length = strlen(realm.data);
+ }
+
+ rc = krb5_locate_kdc(ctx, &realm, &addrs, &num_kdcs, 0);
+ if (rc) {
+ DEBUG(1, ("krb5_locate_kdc failed (%s)\n", error_message(rc)));
+ return -1;
+ }
+ for (i=0;i<num_kdcs;i++)
+ if (addrs[i].sin_family == AF_INET)
+ d_printf("%s:%hd\n", inet_ntoa(addrs[i].sin_addr),
+ ntohs(addrs[i].sin_port));
+ return 0;
+
+#endif
+ DEBUG(1, ("No kerberos support\n"));
+ return -1;
+}
+
+
+/* lookup hosts or IP addresses using internal samba lookup fns */
+int net_lookup(int argc, const char **argv)
+{
+ int i;
+
+ struct functable table[] = {
+ {"HOST", net_lookup_host},
+ {"LDAP", net_lookup_ldap},
+ {"DC", net_lookup_dc},
+ {"MASTER", net_lookup_master},
+ {"KDC", net_lookup_kdc},
+ {NULL, NULL}
+ };
+
+ if (argc < 1) {
+ d_printf("\nUsage: \n");
+ return net_lookup_usage(argc, argv);
+ }
+ for (i=0; table[i].funcname; i++) {
+ if (StrCaseCmp(argv[0], table[i].funcname) == 0)
+ return table[i].fn(argc-1, argv+1);
+ }
+
+ /* Default to lookup a hostname so 'net lookup foo#1b' can be
+ used instead of 'net lookup host foo#1b'. The host syntax
+ is a bit confusing as non #00 names can't really be
+ considered hosts as such. */
+
+ return net_lookup_host(argc, argv);
+}
diff --git a/source/utils/net_privileges.c b/source/utils/net_privileges.c
new file mode 100644
index 00000000000..95a3326ce3e
--- /dev/null
+++ b/source/utils/net_privileges.c
@@ -0,0 +1,362 @@
+/*
+ * Unix SMB/CIFS implementation.
+ * RPC Pipe client / server routines
+ * Copyright (C) Andrew Tridgell 1992-2000,
+ * Copyright (C) Jean François Micouleau 1998-2001.
+ * Copyright (C) Gerald Carter 2003.
+ * Copyright (C) Simo Sorce 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 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 "../utils/net.h"
+
+extern PRIVS privs[];
+
+/*********************************************************
+ utility function to parse an integer parameter from
+ "parameter = value"
+**********************************************************/
+static uint32 get_int_param( const char* param )
+{
+ char *p;
+
+ p = strchr( param, '=' );
+ if ( !p )
+ return 0;
+
+ return atoi(p+1);
+}
+
+/*********************************************************
+ utility function to parse an integer parameter from
+ "parameter = value"
+**********************************************************/
+static char* get_string_param( const char* param )
+{
+ char *p;
+
+ p = strchr( param, '=' );
+ if ( !p )
+ return NULL;
+
+ return (p+1);
+}
+
+/*********************************************************
+ Dump a GROUP_MAP entry to stdout (long or short listing)
+**********************************************************/
+
+static void print_priv_entry(const char *privname, const char *description, const char *sid_list)
+{
+ d_printf("%s\n", privname);
+
+ if (description) {
+ d_printf("\tdescription: %s\n", description);
+ }
+
+ if (sid_list) {
+ d_printf("\tSIDs: %s\n", sid_list);
+ } else {
+ d_printf("\tNo SIDs in this privilege\n");
+ }
+}
+
+/*********************************************************
+ List the groups.
+**********************************************************/
+static int net_priv_list(int argc, const char **argv)
+{
+ fstring privname = "";
+ fstring sid_string = "";
+ int i;
+ BOOL verbose = False;
+
+ /* get the options */
+ for ( i=0; i<argc; i++ ) {
+ if (StrnCaseCmp(argv[i], "privname", strlen("privname")) == 0) {
+ fstrcpy(privname, get_string_param(argv[i]));
+ if (!privname[0]) {
+ d_printf("must supply a name\n");
+ return -1;
+ }
+ }
+ else if (StrnCaseCmp(argv[i], "sid", strlen("sid")) == 0) {
+ fstrcpy(sid_string, get_string_param(argv[i]));
+ if (!sid_string[0]) {
+ d_printf("must supply a SID\n");
+ return -1;
+ }
+ }
+ else if (StrnCaseCmp(argv[i], "verbose", strlen("verbose")) == 0) {
+ verbose = True;
+ }
+ else {
+ d_printf("Bad option: %s\n", argv[i]);
+ return -1;
+ }
+ }
+
+ if (sid_string[0] != '\0') {
+ /* list all privileges of a single sid */
+
+ } else {
+ char *sid_list = NULL;
+
+ if (privname[0] != '\0') {
+ const char *description = NULL;
+
+ BOOL found = False;
+
+ for (i=0; privs[i].se_priv != SE_ALL_PRIVS; i++) {
+ if (StrCaseCmp(privs[i].priv, privname) == 0) {
+ description = privs[i].description;
+ found = True;
+ break;
+ }
+ }
+ if (!found) {
+ d_printf("No such privilege!\n");
+ return -1;
+ }
+
+ /* Get the current privilege from the database */
+ pdb_get_privilege_entry(privname, &sid_list);
+ print_priv_entry(privname, description, sid_list);
+
+ SAFE_FREE(sid_list);
+
+ } else for (i=0; privs[i].se_priv != SE_ALL_PRIVS; i++) {
+
+ if (!pdb_get_privilege_entry(privs[i].priv, &sid_list)) {
+ if (!verbose)
+ continue;
+
+ sid_list = NULL;
+ }
+
+ print_priv_entry(privs[i].priv, privs[i].description, sid_list);
+
+ SAFE_FREE(sid_list);
+ }
+ }
+
+ return 0;
+}
+
+/*********************************************************
+ Add a sid to a privilege entry
+**********************************************************/
+
+static int net_priv_add(int argc, const char **argv)
+{
+ DOM_SID sid;
+ fstring privname = "";
+ fstring sid_string = "";
+ uint32 rid = 0;
+ int i;
+
+ /* get the options */
+ for ( i=0; i<argc; i++ ) {
+ if (StrnCaseCmp(argv[i], "rid", strlen("rid")) == 0) {
+ rid = get_int_param(argv[i]);
+ if (rid < DOMAIN_GROUP_RID_ADMINS) {
+ d_printf("RID must be greater than %d\n", (uint32)DOMAIN_GROUP_RID_ADMINS-1);
+ return -1;
+ }
+ }
+ else if (StrnCaseCmp(argv[i], "privilege", strlen("privilege")) == 0) {
+ BOOL found = False;
+ int j;
+
+ fstrcpy(privname, get_string_param(argv[i]));
+ if (!privname[0]) {
+ d_printf("must supply a name\n");
+ return -1;
+ }
+ for (j=0; privs[j].se_priv != SE_ALL_PRIVS; j++) {
+ if (StrCaseCmp(privs[j].priv, privname) == 0) {
+ found = True;
+ }
+ }
+ if (!found) {
+ d_printf("unknown privilege name");
+ return -1;
+ }
+ }
+ else if (StrnCaseCmp(argv[i], "sid", strlen("sid")) == 0) {
+ fstrcpy(sid_string, get_string_param(argv[i]));
+ if (!sid_string[0]) {
+ d_printf("must supply a SID\n");
+ return -1;
+ }
+ }
+ else {
+ d_printf("Bad option: %s\n", argv[i]);
+ return -1;
+ }
+ }
+
+ if (privname[0] == '\0') {
+ d_printf("Usage: net priv add {rid=<int>|sid=<string>} privilege=<string>\n");
+ return -1;
+ }
+
+ if ((rid == 0) && (sid_string[0] == '\0')) {
+ d_printf("No rid or sid specified\n");
+ d_printf("Usage: net priv add {rid=<int>|sid=<string>} privilege=<string>\n");
+ return -1;
+ }
+
+ /* append the rid to our own domain/machine SID if we don't have a full SID */
+ if (sid_string[0] == '\0') {
+ sid_copy(&sid, get_global_sam_sid());
+ sid_append_rid(&sid, rid);
+ sid_to_string(sid_string, &sid);
+ } else {
+ string_to_sid(&sid, sid_string);
+ }
+
+ if (!pdb_add_sid_to_privilege(privname, &sid)) {
+ d_printf("adding sid %s to privilege %s failed!\n", sid_string, privname);
+ return -1;
+ }
+
+ d_printf("Successully added SID %s to privilege %s\n", sid_string, privname);
+ return 0;
+}
+
+/*********************************************************
+ Remove a SID froma privilege entry
+**********************************************************/
+
+static int net_priv_remove(int argc, const char **argv)
+{
+ DOM_SID sid;
+ fstring privname = "";
+ fstring sid_string = "";
+ uint32 rid = 0;
+ int i;
+
+ /* get the options */
+ for ( i=0; i<argc; i++ ) {
+ if (StrnCaseCmp(argv[i], "rid", strlen("rid")) == 0) {
+ rid = get_int_param(argv[i]);
+ if (rid < DOMAIN_GROUP_RID_ADMINS) {
+ d_printf("RID must be greater than %d\n", (uint32)DOMAIN_GROUP_RID_ADMINS-1);
+ return -1;
+ }
+ }
+ else if (StrnCaseCmp(argv[i], "privilege", strlen("privilege")) == 0) {
+ BOOL found = False;
+ int j;
+
+ fstrcpy(privname, get_string_param(argv[i]));
+ if (!privname[0]) {
+ d_printf("must supply a name\n");
+ return -1;
+ }
+ for (j=0; privs[j].se_priv != SE_ALL_PRIVS; j++) {
+ if (StrCaseCmp(privs[j].priv, privname) == 0) {
+ found = True;
+ }
+ }
+ if (!found) {
+ d_printf("unknown privilege name");
+ return -1;
+ }
+ }
+ else if (StrnCaseCmp(argv[i], "sid", strlen("sid")) == 0) {
+ fstrcpy(sid_string, get_string_param(argv[i]));
+ if (!sid_string[0]) {
+ d_printf("must supply a SID\n");
+ return -1;
+ }
+ }
+ else {
+ d_printf("Bad option: %s\n", argv[i]);
+ return -1;
+ }
+ }
+
+ if (privname[0] == '\0') {
+ d_printf("Usage: net priv remove {rid=<int>|sid=<string>} privilege=<string>\n");
+ return -1;
+ }
+
+ if ((rid == 0) && (sid_string[0] == '\0')) {
+ d_printf("No rid or sid specified\n");
+ d_printf("Usage: net priv remove {rid=<int>|sid=<string>} privilege=<string>\n");
+ return -1;
+ }
+
+ /* append the rid to our own domain/machine SID if we don't have a full SID */
+ if (sid_string[0] == '\0') {
+ sid_copy(&sid, get_global_sam_sid());
+ sid_append_rid(&sid, rid);
+ sid_to_string(sid_string, &sid);
+ } else {
+ string_to_sid(&sid, sid_string);
+ }
+
+ if (!pdb_remove_sid_from_privilege(privname, &sid)) {
+ d_printf("removing sid %s from privilege %s failed!\n", sid_string, privname);
+ return -1;
+ }
+
+ d_printf("Successully removed SID %s from privilege %s\n", sid_string, privname);
+ return 0;
+}
+
+int net_help_priv(int argc, const char **argv)
+{
+ d_printf("net priv add sid\n" \
+ " Add sid to privilege\n");
+ d_printf("net priv remove sid\n"\
+ " Remove sid from privilege\n");
+ d_printf("net priv list\n"\
+ " List sids per privilege\n");
+
+ return -1;
+}
+
+
+/***********************************************************
+ migrated functionality from smbgroupedit
+ **********************************************************/
+int net_priv(int argc, const char **argv)
+{
+ struct functable func[] = {
+ {"add", net_priv_add},
+ {"remove", net_priv_remove},
+ {"list", net_priv_list},
+ {"help", net_help_priv},
+ {NULL, NULL}
+ };
+
+ /* we shouldn't have silly checks like this */
+ if (getuid() != 0) {
+ d_printf("You must be root to edit privilege mappings.\nExiting...\n");
+ return -1;
+ }
+
+ if ( argc )
+ return net_run_function(argc, argv, func, net_help_priv);
+
+ return net_help_priv(argc, argv);
+}
+
diff --git a/source/utils/net_rap.c b/source/utils/net_rap.c
new file mode 100644
index 00000000000..39254641abf
--- /dev/null
+++ b/source/utils/net_rap.c
@@ -0,0 +1,1054 @@
+/*
+ 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)
+
+ Originally written by Steve and Jim. Largely rewritten by tridge in
+ November 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 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 "../utils/net.h"
+
+/* The following messages were for error checking that is not properly
+ reported at the moment. Which should be reinstated? */
+#define ERRMSG_TARGET_WG_NOT_VALID "\nTarget workgroup option not valid "\
+ "except on net rap server command, ignored"
+#define ERRMSG_INVALID_HELP_OPTION "\nInvalid help option\n"
+
+#define ERRMSG_BOTH_SERVER_IPADDRESS "\nTarget server and IP address both "\
+ "specified. Do not set both at the same time. The target IP address was used\n"
+
+const char *share_type[] = {
+ "Disk",
+ "Print",
+ "Dev",
+ "IPC"
+};
+
+static int errmsg_not_implemented(void)
+{
+ d_printf("\nNot implemented\n");
+ return 0;
+}
+
+int net_rap_file_usage(int argc, const char **argv)
+{
+ return net_help_file(argc, argv);
+}
+
+/***************************************************************************
+ list info on an open file
+***************************************************************************/
+static void file_fn(const char * pPath, const char * pUser, uint16 perms,
+ uint16 locks, uint32 id)
+{
+ d_printf("%-7.1d %-20.20s 0x%-4.2x %-6.1d %s\n",
+ id, pUser, perms, locks, pPath);
+}
+
+static void one_file_fn(const char *pPath, const char *pUser, uint16 perms,
+ uint16 locks, uint32 id)
+{
+ d_printf("File ID %d\n"\
+ "User name %s\n"\
+ "Locks 0x%-4.2x\n"\
+ "Path %s\n"\
+ "Permissions 0x%x\n",
+ id, pUser, locks, pPath, perms);
+}
+
+
+static int rap_file_close(int argc, const char **argv)
+{
+ struct cli_state *cli;
+ int ret;
+ if (argc == 0) {
+ d_printf("\nMissing fileid of file to close\n\n");
+ return net_rap_file_usage(argc, argv);
+ }
+
+ if (!(cli = net_make_ipc_connection(0)))
+ return -1;
+
+ ret = cli_NetFileClose(cli, atoi(argv[0]));
+ cli_shutdown(cli);
+ return ret;
+}
+
+static int rap_file_info(int argc, const char **argv)
+{
+ struct cli_state *cli;
+ int ret;
+ if (argc == 0)
+ return net_rap_file_usage(argc, argv);
+
+ if (!(cli = net_make_ipc_connection(0)))
+ return -1;
+
+ ret = cli_NetFileGetInfo(cli, atoi(argv[0]), one_file_fn);
+ cli_shutdown(cli);
+ return ret;
+}
+
+static int rap_file_user(int argc, const char **argv)
+{
+ if (argc == 0)
+ return net_rap_file_usage(argc, argv);
+
+ d_printf("net rap file user not implemented yet\n");
+ return -1;
+}
+
+int net_rap_file(int argc, const char **argv)
+{
+ struct functable func[] = {
+ {"CLOSE", rap_file_close},
+ {"USER", rap_file_user},
+ {"INFO", rap_file_info},
+ {NULL, NULL}
+ };
+
+ if (argc == 0) {
+ struct cli_state *cli;
+ int ret;
+
+ if (!(cli = net_make_ipc_connection(0)))
+ return -1;
+
+ /* list open files */
+ d_printf(
+ "\nEnumerating open files on remote server:\n\n"\
+ "\nFileId Opened by Perms Locks Path \n"\
+ "------ --------- ----- ----- ---- \n");
+ ret = cli_NetFileEnum(cli, NULL, NULL, file_fn);
+ cli_shutdown(cli);
+ return ret;
+ }
+
+ return net_run_function(argc, argv, func, net_rap_file_usage);
+}
+
+int net_rap_share_usage(int argc, const char **argv)
+{
+ return net_help_share(argc, argv);
+}
+
+static void long_share_fn(const char *share_name, uint32 type,
+ const char *comment, void *state)
+{
+ d_printf("%-12s %-8.8s %-50s\n",
+ share_name, share_type[type], comment);
+}
+
+static void share_fn(const char *share_name, uint32 type,
+ const char *comment, void *state)
+{
+ d_printf("%s\n", share_name);
+}
+
+static int rap_share_delete(int argc, const char **argv)
+{
+ struct cli_state *cli;
+ int ret;
+
+ if (argc == 0) {
+ d_printf("\n\nShare name not specified\n");
+ return net_rap_share_usage(argc, argv);
+ }
+
+ if (!(cli = net_make_ipc_connection(0)))
+ return -1;
+
+ ret = cli_NetShareDelete(cli, argv[0]);
+ cli_shutdown(cli);
+ return ret;
+}
+
+static int rap_share_add(int argc, const char **argv)
+{
+ struct cli_state *cli;
+ int ret;
+
+ RAP_SHARE_INFO_2 sinfo;
+ char *p;
+ char *sharename;
+
+ if (argc == 0) {
+ d_printf("\n\nShare name not specified\n");
+ return net_rap_share_usage(argc, argv);
+ }
+
+ if (!(cli = net_make_ipc_connection(0)))
+ return -1;
+
+ sharename = strdup(argv[0]);
+ p = strchr(sharename, '=');
+ *p = 0;
+ strlcpy(sinfo.share_name, sharename, sizeof(sinfo.share_name));
+ sinfo.reserved1 = '\0';
+ sinfo.share_type = 0;
+ sinfo.comment = smb_xstrdup(opt_comment);
+ sinfo.perms = 0;
+ sinfo.maximum_users = opt_maxusers;
+ sinfo.active_users = 0;
+ sinfo.path = p+1;
+ memset(sinfo.password, '\0', sizeof(sinfo.password));
+ sinfo.reserved2 = '\0';
+
+ ret = cli_NetShareAdd(cli, &sinfo);
+ cli_shutdown(cli);
+ return ret;
+}
+
+
+int net_rap_share(int argc, const char **argv)
+{
+ struct functable func[] = {
+ {"DELETE", rap_share_delete},
+ {"CLOSE", rap_share_delete},
+ {"ADD", rap_share_add},
+ {NULL, NULL}
+ };
+
+ if (argc == 0) {
+ struct cli_state *cli;
+ int ret;
+
+ if (!(cli = net_make_ipc_connection(0)))
+ return -1;
+
+ if (opt_long_list_entries) {
+ d_printf(
+ "\nEnumerating shared resources (exports) on remote server:\n\n"\
+ "\nShare name Type Description\n"\
+ "---------- ---- -----------\n");
+ ret = cli_RNetShareEnum(cli, long_share_fn, NULL);
+ } else {
+ ret = cli_RNetShareEnum(cli, share_fn, NULL);
+ }
+ cli_shutdown(cli);
+ return ret;
+ }
+
+ return net_run_function(argc, argv, func, net_rap_share_usage);
+}
+
+
+int net_rap_session_usage(int argc, const char **argv)
+{
+ d_printf(
+ "\nnet rap session [misc. options] [targets]"\
+ "\n\tenumerates all active SMB/CIFS sessions on target server\n");
+ d_printf(
+ "\nnet rap session DELETE <client_name> [misc. options] [targets] \n"\
+ "\tor"\
+ "\nnet rap session CLOSE <client_name> [misc. options] [targets]"\
+ "\n\tDeletes (closes) a session from specified client to server\n");
+ d_printf(
+ "\nnet rap session INFO <client_name>"\
+ "\n\tEnumerates all open files in specified session\n");
+
+ net_common_flags_usage(argc, argv);
+ return -1;
+}
+
+static void list_sessions_func(char *wsname, char *username, uint16 conns,
+ uint16 opens, uint16 users, uint32 sess_time,
+ uint32 idle_time, uint32 user_flags, char *clitype)
+{
+ int hrs = idle_time / 3600;
+ int min = (idle_time / 60) % 60;
+ int sec = idle_time % 60;
+
+ d_printf("\\\\%-18.18s %-20.20s %-18.18s %5d %2.2d:%2.2d:%2.2d\n",
+ wsname, username, clitype, opens, hrs, min, sec);
+}
+
+static void display_session_func(const char *wsname, const char *username,
+ uint16 conns, uint16 opens, uint16 users,
+ uint32 sess_time, uint32 idle_time,
+ uint32 user_flags, const char *clitype)
+{
+ int ihrs = idle_time / 3600;
+ int imin = (idle_time / 60) % 60;
+ int isec = idle_time % 60;
+ int shrs = sess_time / 3600;
+ int smin = (sess_time / 60) % 60;
+ int ssec = sess_time % 60;
+ d_printf("User name %-20.20s\n"\
+ "Computer %-20.20s\n"\
+ "Guest logon %-20.20s\n"\
+ "Client Type %-40.40s\n"\
+ "Sess time %2.2d:%2.2d:%2.2d\n"\
+ "Idle time %2.2d:%2.2d:%2.2d\n",
+ username, wsname,
+ (user_flags&0x0)?"yes":"no", clitype,
+ shrs, smin, ssec, ihrs, imin, isec);
+}
+
+static void display_conns_func(uint16 conn_id, uint16 conn_type, uint16 opens,
+ uint16 users, uint32 conn_time,
+ const char *username, const char *netname)
+{
+ d_printf("%-14.14s %-8.8s %5d\n",
+ netname, share_type[conn_type], opens);
+}
+
+static int rap_session_info(int argc, const char **argv)
+{
+ const char *sessname;
+ struct cli_state *cli;
+ int ret;
+
+ if (!(cli = net_make_ipc_connection(0)))
+ return -1;
+
+ if (argc == 0)
+ return net_rap_session_usage(argc, argv);
+
+ sessname = argv[0];
+
+ ret = cli_NetSessionGetInfo(cli, sessname, display_session_func);
+ if (ret < 0) {
+ cli_shutdown(cli);
+ return ret;
+ }
+
+ d_printf("Share name Type # Opens\n-------------------------"\
+ "-----------------------------------------------------\n");
+ ret = cli_NetConnectionEnum(cli, sessname, display_conns_func);
+ cli_shutdown(cli);
+ return ret;
+}
+
+static int rap_session_delete(int argc, const char **argv)
+{
+ struct cli_state *cli;
+ int ret;
+
+ if (!(cli = net_make_ipc_connection(0)))
+ return -1;
+
+ if (argc == 0)
+ return net_rap_session_usage(argc, argv);
+
+ ret = cli_NetSessionDel(cli, argv[0]);
+ cli_shutdown(cli);
+ return ret;
+}
+
+int net_rap_session(int argc, const char **argv)
+{
+ struct functable func[] = {
+ {"INFO", rap_session_info},
+ {"DELETE", rap_session_delete},
+ {"CLOSE", rap_session_delete},
+ {NULL, NULL}
+ };
+
+ if (argc == 0) {
+ struct cli_state *cli;
+ int ret;
+
+ if (!(cli = net_make_ipc_connection(0)))
+ return -1;
+
+ d_printf("Computer User name "\
+ "Client Type Opens Idle time\n"\
+ "------------------------------------------"\
+ "------------------------------------\n");
+ ret = cli_NetSessionEnum(cli, list_sessions_func);
+
+ cli_shutdown(cli);
+ return ret;
+ }
+
+ return net_run_function(argc, argv, func, net_rap_session_usage);
+}
+
+/****************************************************************************
+list a server name
+****************************************************************************/
+static void display_server_func(const char *name, uint32 m,
+ const char *comment, void * reserved)
+{
+ d_printf("\t%-16.16s %s\n", name, comment);
+}
+
+
+int net_rap_server_usage(int argc, const char **argv)
+{
+ d_printf("net rap server [misc. options] [target]\n\t"\
+ "lists the servers in the specified domain or workgroup.\n");
+ d_printf("\n\tIf domain is not specified, it uses the current"\
+ " domain or workgroup as\n\tthe default.\n");
+
+ net_common_flags_usage(argc, argv);
+ return -1;
+}
+
+int net_rap_server(int argc, const char **argv)
+{
+ struct cli_state *cli;
+ int ret;
+
+ if (!(cli = net_make_ipc_connection(0)))
+ return -1;
+
+ d_printf("\nEnumerating servers in this domain or workgroup: \n\n"\
+ "\tServer name Server description\n"\
+ "\t------------- ----------------------------\n");
+
+ ret = cli_NetServerEnum(cli, cli->server_domain, SV_TYPE_ALL,
+ display_server_func,NULL);
+ cli_shutdown(cli);
+ return ret;
+}
+
+int net_rap_domain_usage(int argc, const char **argv)
+{
+ d_printf("net rap domain [misc. options] [target]\n\tlists the"\
+ " domains or workgroups visible on the current network\n");
+
+ net_common_flags_usage(argc, argv);
+ return -1;
+}
+
+
+int net_rap_domain(int argc, const char **argv)
+{
+ struct cli_state *cli;
+ int ret;
+
+ if (!(cli = net_make_ipc_connection(0)))
+ return -1;
+
+ d_printf("\nEnumerating domains:\n\n"\
+ "\tDomain name Server name of Browse Master\n"\
+ "\t------------- ----------------------------\n");
+
+ ret = cli_NetServerEnum(cli, cli->server_domain, SV_TYPE_DOMAIN_ENUM,
+ display_server_func,NULL);
+ cli_shutdown(cli);
+ return ret;
+}
+
+int net_rap_printq_usage(int argc, const char **argv)
+{
+ d_printf(
+ "net rap printq [misc. options] [targets]\n"\
+ "\tor\n"\
+ "net rap printq list [<queue_name>] [misc. options] [targets]\n"\
+ "\tlists the specified queue and jobs on the target server.\n"\
+ "\tIf the queue name is not specified, all queues are listed.\n\n");
+ d_printf(
+ "net rap printq delete [<queue name>] [misc. options] [targets]\n"\
+ "\tdeletes the specified job number on the target server, or the\n"\
+ "\tprinter queue if no job number is specified\n");
+
+ net_common_flags_usage(argc, argv);
+
+ return -1;
+}
+
+static void enum_queue(const char *queuename, uint16 pri, uint16 start,
+ uint16 until, const char *sep, const char *pproc,
+ const char *dest, const char *qparms,
+ const char *qcomment, uint16 status, uint16 jobcount)
+{
+ d_printf("%-17.17s Queue %5d jobs ",
+ queuename, jobcount);
+
+ switch (status) {
+ case 0:
+ d_printf("*Printer Active*\n");
+ break;
+ case 1:
+ d_printf("*Printer Paused*\n");
+ break;
+ case 2:
+ d_printf("*Printer error*\n");
+ break;
+ case 3:
+ d_printf("*Delete Pending*\n");
+ break;
+ default:
+ d_printf("**UNKNOWN STATUS**\n");
+ }
+}
+
+static void enum_jobs(uint16 jobid, const char *ownername,
+ const char *notifyname, const char *datatype,
+ const char *jparms, uint16 pos, uint16 status,
+ const char *jstatus, unsigned int submitted, unsigned int jobsize,
+ const char *comment)
+{
+ d_printf(" %-23.23s %5d %9d ",
+ ownername, jobid, jobsize);
+ switch (status) {
+ case 0:
+ d_printf("Waiting\n");
+ break;
+ case 1:
+ d_printf("Held in queue\n");
+ break;
+ case 2:
+ d_printf("Spooling\n");
+ break;
+ case 3:
+ d_printf("Printing\n");
+ break;
+ default:
+ d_printf("**UNKNOWN STATUS**\n");
+ }
+}
+
+#define PRINTQ_ENUM_DISPLAY \
+ "Print queues at \\\\%s\n\n"\
+ "Name Job # Size Status\n\n"\
+ "------------------------------------------------------------------"\
+ "-------------\n"
+
+static int rap_printq_info(int argc, const char **argv)
+{
+ struct cli_state *cli;
+ int ret;
+
+ if (argc == 0)
+ return net_rap_printq_usage(argc, argv);
+
+ if (!(cli = net_make_ipc_connection(0)))
+ return -1;
+
+ d_printf(PRINTQ_ENUM_DISPLAY, cli->desthost); /* list header */
+ ret = cli_NetPrintQGetInfo(cli, argv[0], enum_queue, enum_jobs);
+ cli_shutdown(cli);
+ return ret;
+}
+
+static int rap_printq_delete(int argc, const char **argv)
+{
+ struct cli_state *cli;
+ int ret;
+
+ if (argc == 0)
+ return net_rap_printq_usage(argc, argv);
+
+ if (!(cli = net_make_ipc_connection(0)))
+ return -1;
+
+ ret = cli_printjob_del(cli, atoi(argv[0]));
+ cli_shutdown(cli);
+ return ret;
+}
+
+int net_rap_printq(int argc, const char **argv)
+{
+ struct cli_state *cli;
+ int ret;
+
+ struct functable func[] = {
+ {"INFO", rap_printq_info},
+ {"DELETE", rap_printq_delete},
+ {NULL, NULL}
+ };
+
+ if (argc == 0) {
+ if (!(cli = net_make_ipc_connection(0)))
+ return -1;
+
+ d_printf(PRINTQ_ENUM_DISPLAY, cli->desthost); /* list header */
+ ret = cli_NetPrintQEnum(cli, enum_queue, enum_jobs);
+ cli_shutdown(cli);
+ return ret;
+ }
+
+ return net_run_function(argc, argv, func, net_rap_printq_usage);
+}
+
+
+static int net_rap_user_usage(int argc, const char **argv)
+{
+ return net_help_user(argc, argv);
+}
+
+static void user_fn(const char *user_name, const char *comment,
+ const char * home_dir, const char * logon_script,
+ void *state)
+{
+ d_printf("%-21.21s\n", user_name);
+}
+
+static void long_user_fn(const char *user_name, const char *comment,
+ const char * home_dir, const char * logon_script,
+ void *state)
+{
+ d_printf("%-21.21s %s\n",
+ user_name, comment);
+}
+
+static void group_member_fn(const char *user_name, void *state)
+{
+ d_printf("%-21.21s\n", user_name);
+}
+
+static int rap_user_delete(int argc, const char **argv)
+{
+ struct cli_state *cli;
+ int ret;
+
+ if (argc == 0) {
+ d_printf("\n\nUser name not specified\n");
+ return net_rap_user_usage(argc, argv);
+ }
+
+ if (!(cli = net_make_ipc_connection(0)))
+ return -1;
+
+ ret = cli_NetUserDelete(cli, argv[0]);
+ cli_shutdown(cli);
+ return ret;
+}
+
+static int rap_user_add(int argc, const char **argv)
+{
+ struct cli_state *cli;
+ int ret;
+ RAP_USER_INFO_1 userinfo;
+
+ if (argc == 0) {
+ d_printf("\n\nUser name not specified\n");
+ return net_rap_user_usage(argc, argv);
+ }
+
+ if (!(cli = net_make_ipc_connection(0)))
+ return -1;
+
+ safe_strcpy(userinfo.user_name, argv[0], sizeof(userinfo.user_name)-1);
+ if (opt_flags == -1)
+ opt_flags = 0x21;
+
+ userinfo.userflags = opt_flags;
+ userinfo.reserved1 = '\0';
+ userinfo.comment = smb_xstrdup(opt_comment);
+ userinfo.priv = 1;
+ userinfo.home_dir = NULL;
+ userinfo.logon_script = NULL;
+
+ ret = cli_NetUserAdd(cli, &userinfo);
+
+ cli_shutdown(cli);
+ return ret;
+}
+
+static int rap_user_info(int argc, const char **argv)
+{
+ struct cli_state *cli;
+ int ret;
+ if (argc == 0) {
+ d_printf("\n\nUser name not specified\n");
+ return net_rap_user_usage(argc, argv);
+ }
+
+ if (!(cli = net_make_ipc_connection(0)))
+ return -1;
+
+ ret = cli_NetUserGetGroups(cli, argv[0], group_member_fn, NULL);
+ cli_shutdown(cli);
+ return ret;
+}
+
+int net_rap_user(int argc, const char **argv)
+{
+ int ret = -1;
+ struct functable func[] = {
+ {"ADD", rap_user_add},
+ {"INFO", rap_user_info},
+ {"DELETE", rap_user_delete},
+ {NULL, NULL}
+ };
+
+ if (argc == 0) {
+ struct cli_state *cli;
+ if (!(cli = net_make_ipc_connection(0)))
+ goto done;
+ if (opt_long_list_entries) {
+ d_printf("\nUser name Comment"\
+ "\n-----------------------------\n");
+ ret = cli_RNetUserEnum(cli, long_user_fn, NULL);
+ cli_shutdown(cli);
+ goto done;
+ }
+ ret = cli_RNetUserEnum(cli, user_fn, NULL);
+ cli_shutdown(cli);
+ goto done;
+ }
+
+ ret = net_run_function(argc, argv, func, net_rap_user_usage);
+ done:
+ if (ret != 0) {
+ DEBUG(1, ("Net user returned: %d\n", ret));
+ }
+ return ret;
+}
+
+
+int net_rap_group_usage(int argc, const char **argv)
+{
+ return net_help_group(argc, argv);
+}
+
+static void long_group_fn(const char *group_name, const char *comment,
+ void *state)
+{
+ d_printf("%-21.21s %s\n", group_name, comment);
+}
+
+static void group_fn(const char *group_name, const char *comment, void *state)
+{
+ d_printf("%-21.21s\n", group_name);
+}
+
+static int rap_group_delete(int argc, const char **argv)
+{
+ struct cli_state *cli;
+ int ret;
+ if (argc == 0) {
+ d_printf("\n\nGroup name not specified\n");
+ return net_rap_group_usage(argc, argv);
+ }
+
+ if (!(cli = net_make_ipc_connection(0)))
+ return -1;
+
+ ret = cli_NetGroupDelete(cli, argv[0]);
+ cli_shutdown(cli);
+ return ret;
+}
+
+static int rap_group_add(int argc, const char **argv)
+{
+ struct cli_state *cli;
+ int ret;
+ RAP_GROUP_INFO_1 grinfo;
+
+ if (argc == 0) {
+ d_printf("\n\nGroup name not specified\n");
+ return net_rap_group_usage(argc, argv);
+ }
+
+ if (!(cli = net_make_ipc_connection(0)))
+ return -1;
+
+ /* BB check for length 21 or smaller explicitly ? BB */
+ safe_strcpy(grinfo.group_name, argv[0], sizeof(grinfo.group_name)-1);
+ grinfo.reserved1 = '\0';
+ grinfo.comment = smb_xstrdup(opt_comment);
+
+ ret = cli_NetGroupAdd(cli, &grinfo);
+ cli_shutdown(cli);
+ return ret;
+}
+
+int net_rap_group(int argc, const char **argv)
+{
+ struct functable func[] = {
+ {"ADD", rap_group_add},
+ {"DELETE", rap_group_delete},
+ {NULL, NULL}
+ };
+
+ if (argc == 0) {
+ struct cli_state *cli;
+ int ret;
+ if (!(cli = net_make_ipc_connection(0)))
+ return -1;
+ if (opt_long_list_entries) {
+ d_printf("Group name Comment\n");
+ d_printf("-----------------------------\n");
+ ret = cli_RNetGroupEnum(cli, long_group_fn, NULL);
+ cli_shutdown(cli);
+ return ret;
+ }
+ ret = cli_RNetGroupEnum(cli, group_fn, NULL);
+ cli_shutdown(cli);
+ return ret;
+ }
+
+ return net_run_function(argc, argv, func, net_rap_group_usage);
+}
+
+int net_rap_groupmember_usage(int argc, const char **argv)
+{
+ d_printf(
+ "net rap groupmember LIST <group> [misc. options] [targets]"\
+ "\n\t Enumerate users in a group\n"\
+ "\nnet rap groupmember DELETE <group> <user> [misc. options] "\
+ "[targets]\n\t Delete sepcified user from specified group\n"\
+ "\nnet rap groupmember ADD <group> <user> [misc. options] [targets]"\
+ "\n\t Add specified user to specified group\n");
+
+ net_common_flags_usage(argc, argv);
+ return -1;
+}
+
+
+static int rap_groupmember_add(int argc, const char **argv)
+{
+ struct cli_state *cli;
+ int ret;
+ if (argc != 2) {
+ d_printf("\n\nGroup or user name not specified\n");
+ return net_rap_groupmember_usage(argc, argv);
+ }
+
+ if (!(cli = net_make_ipc_connection(0)))
+ return -1;
+
+ ret = cli_NetGroupAddUser(cli, argv[0], argv[1]);
+ cli_shutdown(cli);
+ return ret;
+}
+
+static int rap_groupmember_delete(int argc, const char **argv)
+{
+ struct cli_state *cli;
+ int ret;
+ if (argc != 2) {
+ d_printf("\n\nGroup or user name not specified\n");
+ return net_rap_groupmember_usage(argc, argv);
+ }
+
+ if (!(cli = net_make_ipc_connection(0)))
+ return -1;
+
+ ret = cli_NetGroupDelUser(cli, argv[0], argv[1]);
+ cli_shutdown(cli);
+ return ret;
+}
+
+static int rap_groupmember_list(int argc, const char **argv)
+{
+ struct cli_state *cli;
+ int ret;
+ if (argc == 0) {
+ d_printf("\n\nGroup name not specified\n");
+ return net_rap_groupmember_usage(argc, argv);
+ }
+
+ if (!(cli = net_make_ipc_connection(0)))
+ return -1;
+
+ ret = cli_NetGroupGetUsers(cli, argv[0], group_member_fn, NULL );
+ cli_shutdown(cli);
+ return ret;
+}
+
+int net_rap_groupmember(int argc, const char **argv)
+{
+ struct functable func[] = {
+ {"ADD", rap_groupmember_add},
+ {"LIST", rap_groupmember_list},
+ {"DELETE", rap_groupmember_delete},
+ {NULL, NULL}
+ };
+
+ return net_run_function(argc, argv, func, net_rap_groupmember_usage);
+}
+
+int net_rap_validate_usage(int argc, const char **argv)
+{
+ d_printf("net rap validate <username> [password]\n"\
+ "\tValidate user and password to check whether they"\
+ " can access target server or domain\n");
+
+ net_common_flags_usage(argc, argv);
+ return -1;
+}
+
+int net_rap_validate(int argc, const char **argv)
+{
+ return errmsg_not_implemented();
+}
+
+int net_rap_service_usage(int argc, const char **argv)
+{
+ d_printf("net rap service [misc. options] [targets] \n"\
+ "\tlists all running service daemons on target server\n");
+ d_printf("\nnet rap service START <name> [service startup arguments]"\
+ " [misc. options] [targets]"\
+ "\n\tStart named service on remote server\n");
+ d_printf("\nnet rap service STOP <name> [misc. options] [targets]\n"\
+ "\n\tStop named service on remote server\n");
+
+ net_common_flags_usage(argc, argv);
+ return -1;
+}
+
+static int rap_service_start(int argc, const char **argv)
+{
+ return errmsg_not_implemented();
+}
+
+static int rap_service_stop(int argc, const char **argv)
+{
+ return errmsg_not_implemented();
+}
+
+int net_rap_service(int argc, const char **argv)
+{
+ struct functable func[] = {
+ {"START", rap_service_start},
+ {"STOP", rap_service_stop},
+ {NULL, NULL}
+ };
+
+ if (argc == 0) {
+ struct cli_state *cli;
+ int ret;
+ if (!(cli = net_make_ipc_connection(0)))
+ return -1;
+
+ if (opt_long_list_entries) {
+ d_printf("Service name Comment\n");
+ d_printf("-----------------------------\n");
+ ret = cli_RNetServiceEnum(cli, long_group_fn, NULL);
+ }
+ ret = cli_RNetServiceEnum(cli, group_fn, NULL);
+ cli_shutdown(cli);
+ return ret;
+ }
+
+ return net_run_function(argc, argv, func, net_rap_service_usage);
+}
+
+int net_rap_password_usage(int argc, const char **argv)
+{
+ d_printf(
+ "net rap password <user> <oldpwo> <newpw> [misc. options] [target]\n"\
+ "\tchanges the password for the specified user at target\n");
+
+ return -1;
+}
+
+
+int net_rap_password(int argc, const char **argv)
+{
+ struct cli_state *cli;
+ int ret;
+
+ if (argc < 3)
+ return net_rap_password_usage(argc, argv);
+
+ if (!(cli = net_make_ipc_connection(0)))
+ return -1;
+
+ /* BB Add check for password lengths? */
+ ret = cli_oem_change_password(cli, argv[0], argv[2], argv[1]);
+ cli_shutdown(cli);
+ return ret;
+}
+
+int net_rap_admin_usage(int argc, const char **argv)
+{
+ d_printf(
+ "net rap admin <remote command> [cmd args [env]] [misc. options] [targets]"\
+ "\n\texecutes a remote command on an os/2 target server\n");
+
+ return -1;
+}
+
+
+int net_rap_admin(int argc, const char **argv)
+{
+ return errmsg_not_implemented();
+}
+
+/* The help subsystem for the RAP subcommand */
+
+int net_rap_usage(int argc, const char **argv)
+{
+ d_printf(" net rap domain \tto list domains \n"\
+ " net rap file \t\tto list open files on a server \n"\
+ " net rap group \tto list user groups \n"\
+ " net rap groupmember \tto list users in a group \n"\
+ " net rap password \tto change the password of a user\n"\
+ " net rap printq \tto list the print queues on a server\n"\
+ " net rap server \tto list servers in a domain\n"\
+ " net rap session \tto list clients with open sessions to a server\n"\
+ " net rap share \tto list shares exported by a server\n"\
+ " net rap user \t\tto list users\n"\
+ " net rap validate \tto check whether a user and the corresponding password are valid\n"\
+ " net rap help\n"\
+ "\nType \"net help <option>\" to get more information on that option\n\n");
+
+ net_common_flags_usage(argc, argv);
+ return -1;
+}
+
+/*
+ handle "net rap help *" subcommands
+*/
+int net_rap_help(int argc, const char **argv)
+{
+ struct functable func[] = {
+ {"FILE", net_rap_file_usage},
+ {"SHARE", net_rap_share_usage},
+ {"SESSION", net_rap_session_usage},
+ {"SERVER", net_rap_server_usage},
+ {"DOMAIN", net_rap_domain_usage},
+ {"PRINTQ", net_rap_printq_usage},
+ {"USER", net_rap_user_usage},
+ {"GROUP", net_rap_group_usage},
+ {"VALIDATE", net_rap_validate_usage},
+ {"GROUPMEMBER", net_rap_groupmember_usage},
+ {"ADMIN", net_rap_admin_usage},
+ {"SERVICE", net_rap_service_usage},
+ {"PASSWORD", net_rap_password_usage},
+ {NULL, NULL}};
+
+ return net_run_function(argc, argv, func, net_rap_usage);
+}
+
+/* Entry-point for all the RAP functions. */
+
+int net_rap(int argc, const char **argv)
+{
+ struct functable func[] = {
+ {"FILE", net_rap_file},
+ {"SHARE", net_rap_share},
+ {"SESSION", net_rap_session},
+ {"SERVER", net_rap_server},
+ {"DOMAIN", net_rap_domain},
+ {"PRINTQ", net_rap_printq},
+ {"USER", net_rap_user},
+ {"GROUP", net_rap_group},
+ {"VALIDATE", net_rap_validate},
+ {"GROUPMEMBER", net_rap_groupmember},
+ {"ADMIN", net_rap_admin},
+ {"SERVICE", net_rap_service},
+ {"PASSWORD", net_rap_password},
+ {"HELP", net_rap_help},
+ {NULL, NULL}
+ };
+
+ return net_run_function(argc, argv, func, net_rap_usage);
+}
+
diff --git a/source/utils/net_rpc.c b/source/utils/net_rpc.c
new file mode 100644
index 00000000000..afb94a616a3
--- /dev/null
+++ b/source/utils/net_rpc.c
@@ -0,0 +1,3396 @@
+/*
+ Samba Unix/Linux SMB client library
+ Distributed SMB/CIFS Server Management Utility
+ Copyright (C) 2001 Andrew Bartlett (abartlet@samba.org)
+ Copyright (C) 2002 Jim McDonough (jmcd@us.ibm.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 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 "../utils/net.h"
+
+/**
+ * @file net_rpc.c
+ *
+ * @brief RPC based subcommands for the 'net' utility.
+ *
+ * This file should contain much of the functionality that used to
+ * be found in rpcclient, execpt that the commands should change
+ * less often, and the fucntionality should be sane (the user is not
+ * expected to know a rid/sid before they conduct an operation etc.)
+ *
+ * @todo Perhaps eventually these should be split out into a number
+ * of files, as this could get quite big.
+ **/
+
+
+/* A function of this type is passed to the 'run_rpc_command' wrapper */
+typedef NTSTATUS (*rpc_command_fn)(const DOM_SID *, const char *,
+ struct cli_state *, TALLOC_CTX *, int, const char **);
+
+/**
+ * Many of the RPC functions need the domain sid. This function gets
+ * it at the start of every run
+ *
+ * @param cli A cli_state already connected to the remote machine
+ *
+ * @return The Domain SID of the remote machine.
+ **/
+
+static DOM_SID *net_get_remote_domain_sid(struct cli_state *cli, TALLOC_CTX *mem_ctx, char **domain_name)
+{
+ DOM_SID *domain_sid;
+ POLICY_HND pol;
+ NTSTATUS result = NT_STATUS_OK;
+ uint32 info_class = 5;
+
+ if (!cli_nt_session_open (cli, PI_LSARPC)) {
+ fprintf(stderr, "could not initialise lsa pipe\n");
+ goto error;
+ }
+
+ result = cli_lsa_open_policy(cli, mem_ctx, False,
+ SEC_RIGHTS_MAXIMUM_ALLOWED,
+ &pol);
+ if (!NT_STATUS_IS_OK(result)) {
+ goto error;
+ }
+
+ result = cli_lsa_query_info_policy(cli, mem_ctx, &pol, info_class,
+ domain_name, &domain_sid);
+ if (!NT_STATUS_IS_OK(result)) {
+ error:
+ fprintf(stderr, "could not obtain sid for domain %s\n", cli->domain);
+
+ if (!NT_STATUS_IS_OK(result)) {
+ fprintf(stderr, "error: %s\n", nt_errstr(result));
+ }
+
+ exit(1);
+ }
+
+ cli_lsa_close(cli, mem_ctx, &pol);
+ cli_nt_session_close(cli);
+
+ return domain_sid;
+}
+
+/**
+ * Run a single RPC command, from start to finish.
+ *
+ * @param pipe_name the pipe to connect to (usually a PIPE_ constant)
+ * @param conn_flag a NET_FLAG_ combination. Passed to
+ * net_make_ipc_connection.
+ * @param argc Standard main() style argc
+ * @param argc Standard main() style argv. Initial components are already
+ * stripped
+ * @return A shell status integer (0 for success)
+ */
+
+static int run_rpc_command(struct cli_state *cli_arg, const int pipe_idx, int conn_flags,
+ rpc_command_fn fn,
+ int argc, const char **argv)
+{
+ struct cli_state *cli = NULL;
+ TALLOC_CTX *mem_ctx;
+ NTSTATUS nt_status;
+ DOM_SID *domain_sid;
+ char *domain_name;
+
+ /* make use of cli_state handed over as an argument, if possible */
+ if (!cli_arg)
+ cli = net_make_ipc_connection(conn_flags);
+ else
+ cli = cli_arg;
+
+ if (!cli) {
+ return -1;
+ }
+
+ /* Create mem_ctx */
+
+ if (!(mem_ctx = talloc_init("run_rpc_command"))) {
+ DEBUG(0, ("talloc_init() failed\n"));
+ cli_shutdown(cli);
+ return -1;
+ }
+
+ domain_sid = net_get_remote_domain_sid(cli, mem_ctx, &domain_name);
+
+ if (!(conn_flags & NET_FLAGS_NO_PIPE)) {
+ if (!cli_nt_session_open(cli, pipe_idx)) {
+ DEBUG(0, ("Could not initialise pipe\n"));
+ }
+ }
+
+ nt_status = fn(domain_sid, domain_name, cli, mem_ctx, argc, argv);
+
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ DEBUG(1, ("rpc command function failed! (%s)\n", nt_errstr(nt_status)));
+ } else {
+ DEBUG(5, ("rpc command function succedded\n"));
+ }
+
+ if (!(conn_flags & NET_FLAGS_NO_PIPE)) {
+ if (cli->nt_pipe_fnum)
+ cli_nt_session_close(cli);
+ }
+
+ /* close the connection only if it was opened here */
+ if (!cli_arg)
+ cli_shutdown(cli);
+
+ talloc_destroy(mem_ctx);
+
+ return (!NT_STATUS_IS_OK(nt_status));
+}
+
+
+/****************************************************************************/
+
+
+/**
+ * Force a change of the trust acccount password.
+ *
+ * All parameters are provided by the run_rpc_command function, except for
+ * argc, argv which are passes through.
+ *
+ * @param domain_sid The domain sid aquired from the remote server
+ * @param cli A cli_state connected to the server.
+ * @param mem_ctx Talloc context, destoyed on compleation of the function.
+ * @param argc Standard main() style argc
+ * @param argc Standard main() style argv. Initial components are already
+ * stripped
+ *
+ * @return Normal NTSTATUS return.
+ **/
+
+static NTSTATUS rpc_changetrustpw_internals(const DOM_SID *domain_sid, const char *domain_name,
+ struct cli_state *cli, TALLOC_CTX *mem_ctx,
+ int argc, const char **argv) {
+
+ return trust_pw_find_change_and_store_it(cli, mem_ctx, opt_target_workgroup);
+}
+
+/**
+ * Force a change of the trust acccount password.
+ *
+ * @param argc Standard main() style argc
+ * @param argc Standard main() style argv. Initial components are already
+ * stripped
+ *
+ * @return A shell status integer (0 for success)
+ **/
+
+int net_rpc_changetrustpw(int argc, const char **argv)
+{
+ return run_rpc_command(NULL, PI_NETLOGON, NET_FLAGS_ANONYMOUS | NET_FLAGS_PDC,
+ rpc_changetrustpw_internals,
+ argc, argv);
+}
+
+
+/****************************************************************************/
+
+
+/**
+ * Join a domain, the old way.
+ *
+ * This uses 'machinename' as the inital password, and changes it.
+ *
+ * The password should be created with 'server manager' or equiv first.
+ *
+ * All parameters are provided by the run_rpc_command function, except for
+ * argc, argv which are passes through.
+ *
+ * @param domain_sid The domain sid aquired from the remote server
+ * @param cli A cli_state connected to the server.
+ * @param mem_ctx Talloc context, destoyed on compleation of the function.
+ * @param argc Standard main() style argc
+ * @param argc Standard main() style argv. Initial components are already
+ * stripped
+ *
+ * @return Normal NTSTATUS return.
+ **/
+
+static NTSTATUS rpc_oldjoin_internals(const DOM_SID *domain_sid, const char *domain_name,
+ struct cli_state *cli,
+ TALLOC_CTX *mem_ctx,
+ int argc, const char **argv) {
+
+ fstring trust_passwd;
+ unsigned char orig_trust_passwd_hash[16];
+ NTSTATUS result;
+ uint32 sec_channel_type;
+
+ /*
+ check what type of join - if the user want's to join as
+ a BDC, the server must agree that we are a BDC.
+ */
+ if (argc >= 0) {
+ sec_channel_type = get_sec_channel_type(argv[0]);
+ } else {
+ sec_channel_type = get_sec_channel_type(NULL);
+ }
+
+ fstrcpy(trust_passwd, global_myname());
+ strlower_m(trust_passwd);
+
+ /*
+ * Machine names can be 15 characters, but the max length on
+ * a password is 14. --jerry
+ */
+
+ trust_passwd[14] = '\0';
+
+ E_md4hash(trust_passwd, orig_trust_passwd_hash);
+
+ result = trust_pw_change_and_store_it(cli, mem_ctx, opt_target_workgroup,
+ orig_trust_passwd_hash,
+ sec_channel_type);
+
+ if (NT_STATUS_IS_OK(result))
+ printf("Joined domain %s.\n",opt_target_workgroup);
+
+
+ if (!secrets_store_domain_sid(opt_target_workgroup, domain_sid)) {
+ DEBUG(0, ("error storing domain sid for %s\n", opt_target_workgroup));
+ result = NT_STATUS_UNSUCCESSFUL;
+ }
+
+ return result;
+}
+
+/**
+ * Join a domain, the old way.
+ *
+ * @param argc Standard main() style argc
+ * @param argc Standard main() style argv. Initial components are already
+ * stripped
+ *
+ * @return A shell status integer (0 for success)
+ **/
+
+static int net_rpc_perform_oldjoin(int argc, const char **argv)
+{
+ return run_rpc_command(NULL, PI_NETLOGON,
+ NET_FLAGS_ANONYMOUS | NET_FLAGS_PDC,
+ rpc_oldjoin_internals,
+ argc, argv);
+}
+
+/**
+ * Join a domain, the old way. This function exists to allow
+ * the message to be displayed when oldjoin was explicitly
+ * requested, but not when it was implied by "net rpc join"
+ *
+ * @param argc Standard main() style argc
+ * @param argc Standard main() style argv. Initial components are already
+ * stripped
+ *
+ * @return A shell status integer (0 for success)
+ **/
+
+static int net_rpc_oldjoin(int argc, const char **argv)
+{
+ int rc = net_rpc_perform_oldjoin(argc, argv);
+
+ if (rc) {
+ d_printf("Failed to join domain\n");
+ }
+
+ return rc;
+}
+
+/**
+ * Basic usage function for 'net rpc join'
+ * @param argc Standard main() style argc
+ * @param argc Standard main() style argv. Initial components are already
+ * stripped
+ **/
+
+static int rpc_join_usage(int argc, const char **argv)
+{
+ d_printf("net rpc join -U <username>[%%password] <type>[options]\n"\
+ "\t to join a domain with admin username & password\n"\
+ "\t\t password will be prompted if needed and none is specified\n"\
+ "\t <type> can be (default MEMBER)\n"\
+ "\t\t BDC - Join as a BDC\n"\
+ "\t\t PDC - Join as a PDC\n"\
+ "\t\t MEMBER - Join as a MEMBER server\n");
+
+ net_common_flags_usage(argc, argv);
+ return -1;
+}
+
+/**
+ * 'net rpc join' entrypoint.
+ * @param argc Standard main() style argc
+ * @param argc Standard main() style argv. Initial components are already
+ * stripped
+ *
+ * Main 'net_rpc_join()' (where the admain username/password is used) is
+ * in net_rpc_join.c
+ * Try to just change the password, but if that doesn't work, use/prompt
+ * for a username/password.
+ **/
+
+int net_rpc_join(int argc, const char **argv)
+{
+ if ((net_rpc_perform_oldjoin(argc, argv) == 0))
+ return 0;
+
+ return net_rpc_join_newstyle(argc, argv);
+}
+
+
+
+/**
+ * display info about a rpc domain
+ *
+ * All parameters are provided by the run_rpc_command function, except for
+ * argc, argv which are passed through.
+ *
+ * @param domain_sid The domain sid acquired from the remote server
+ * @param cli A cli_state connected to the server.
+ * @param mem_ctx Talloc context, destoyed on completion of the function.
+ * @param argc Standard main() style argc
+ * @param argv Standard main() style argv. Initial components are already
+ * stripped
+ *
+ * @return Normal NTSTATUS return.
+ **/
+
+static NTSTATUS
+rpc_info_internals(const DOM_SID *domain_sid, const char *domain_name,
+ struct cli_state *cli,
+ TALLOC_CTX *mem_ctx, int argc, const char **argv)
+{
+ POLICY_HND connect_pol, domain_pol;
+ NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+ SAM_UNK_CTR ctr;
+ fstring sid_str;
+
+ sid_to_string(sid_str, domain_sid);
+
+ /* Get sam policy handle */
+ result = cli_samr_connect(cli, mem_ctx, MAXIMUM_ALLOWED_ACCESS,
+ &connect_pol);
+ if (!NT_STATUS_IS_OK(result)) {
+ goto done;
+ }
+
+ /* Get domain policy handle */
+ result = cli_samr_open_domain(cli, mem_ctx, &connect_pol,
+ MAXIMUM_ALLOWED_ACCESS,
+ domain_sid, &domain_pol);
+ if (!NT_STATUS_IS_OK(result)) {
+ goto done;
+ }
+
+ ZERO_STRUCT(ctr);
+ result = cli_samr_query_dom_info(cli, mem_ctx, &domain_pol,
+ 2, &ctr);
+ if (NT_STATUS_IS_OK(result)) {
+ TALLOC_CTX *ctx = talloc_init("rpc_info_internals");
+ d_printf("Domain Name: %s\n", unistr2_tdup(ctx, &ctr.info.inf2.uni_domain));
+ d_printf("Domain SID: %s\n", sid_str);
+ d_printf("Sequence number: %u\n", ctr.info.inf2.seq_num);
+ d_printf("Num users: %u\n", ctr.info.inf2.num_domain_usrs);
+ d_printf("Num domain groups: %u\n", ctr.info.inf2.num_domain_grps);
+ d_printf("Num local groups: %u\n", ctr.info.inf2.num_local_grps);
+ talloc_destroy(ctx);
+ }
+
+ done:
+ return result;
+}
+
+
+/**
+ * 'net rpc info' entrypoint.
+ * @param argc Standard main() style argc
+ * @param argc Standard main() style argv. Initial components are already
+ * stripped
+ **/
+int net_rpc_info(int argc, const char **argv)
+{
+ return run_rpc_command(NULL, PI_SAMR, NET_FLAGS_ANONYMOUS | NET_FLAGS_PDC,
+ rpc_info_internals,
+ argc, argv);
+}
+
+
+/**
+ * Fetch domain SID into the local secrets.tdb
+ *
+ * All parameters are provided by the run_rpc_command function, except for
+ * argc, argv which are passes through.
+ *
+ * @param domain_sid The domain sid acquired from the remote server
+ * @param cli A cli_state connected to the server.
+ * @param mem_ctx Talloc context, destoyed on completion of the function.
+ * @param argc Standard main() style argc
+ * @param argv Standard main() style argv. Initial components are already
+ * stripped
+ *
+ * @return Normal NTSTATUS return.
+ **/
+
+static NTSTATUS
+rpc_getsid_internals(const DOM_SID *domain_sid, const char *domain_name,
+ struct cli_state *cli,
+ TALLOC_CTX *mem_ctx, int argc, const char **argv)
+{
+ fstring sid_str;
+
+ sid_to_string(sid_str, domain_sid);
+ d_printf("Storing SID %s for Domain %s in secrets.tdb\n",
+ sid_str, domain_name);
+
+ if (!secrets_store_domain_sid(domain_name, domain_sid)) {
+ DEBUG(0,("Can't store domain SID\n"));
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ return NT_STATUS_OK;
+}
+
+
+/**
+ * 'net rpc getsid' entrypoint.
+ * @param argc Standard main() style argc
+ * @param argc Standard main() style argv. Initial components are already
+ * stripped
+ **/
+int net_rpc_getsid(int argc, const char **argv)
+{
+ return run_rpc_command(NULL, PI_SAMR, NET_FLAGS_ANONYMOUS | NET_FLAGS_PDC,
+ rpc_getsid_internals,
+ argc, argv);
+}
+
+
+/****************************************************************************/
+
+/**
+ * Basic usage function for 'net rpc user'
+ * @param argc Standard main() style argc.
+ * @param argv Standard main() style argv. Initial components are already
+ * stripped.
+ **/
+
+static int rpc_user_usage(int argc, const char **argv)
+{
+ return net_help_user(argc, argv);
+}
+
+/**
+ * Add a new user to a remote RPC server
+ *
+ * All parameters are provided by the run_rpc_command function, except for
+ * argc, argv which are passes through.
+ *
+ * @param domain_sid The domain sid acquired from the remote server
+ * @param cli A cli_state connected to the server.
+ * @param mem_ctx Talloc context, destoyed on completion of the function.
+ * @param argc Standard main() style argc
+ * @param argv Standard main() style argv. Initial components are already
+ * stripped
+ *
+ * @return Normal NTSTATUS return.
+ **/
+
+static NTSTATUS rpc_user_add_internals(const DOM_SID *domain_sid, const char *domain_name,
+ struct cli_state *cli, TALLOC_CTX *mem_ctx,
+ int argc, const char **argv) {
+
+ POLICY_HND connect_pol, domain_pol, user_pol;
+ NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+ const char *acct_name;
+ uint16 acb_info;
+ uint32 unknown, user_rid;
+
+ if (argc != 1) {
+ d_printf("User must be specified\n");
+ rpc_user_usage(argc, argv);
+ return NT_STATUS_OK;
+ }
+
+ acct_name = argv[0];
+
+ /* Get sam policy handle */
+
+ result = cli_samr_connect(cli, mem_ctx, MAXIMUM_ALLOWED_ACCESS,
+ &connect_pol);
+ if (!NT_STATUS_IS_OK(result)) {
+ goto done;
+ }
+
+ /* Get domain policy handle */
+
+ result = cli_samr_open_domain(cli, mem_ctx, &connect_pol,
+ MAXIMUM_ALLOWED_ACCESS,
+ domain_sid, &domain_pol);
+ if (!NT_STATUS_IS_OK(result)) {
+ goto done;
+ }
+
+ /* Create domain user */
+
+ acb_info = ACB_NORMAL;
+ unknown = 0xe005000b; /* No idea what this is - a permission mask? */
+
+ result = cli_samr_create_dom_user(cli, mem_ctx, &domain_pol,
+ acct_name, acb_info, unknown,
+ &user_pol, &user_rid);
+ if (!NT_STATUS_IS_OK(result)) {
+ goto done;
+ }
+
+ done:
+ if (!NT_STATUS_IS_OK(result)) {
+ d_printf("Failed to add user %s - %s\n", acct_name,
+ nt_errstr(result));
+ } else {
+ d_printf("Added user %s\n", acct_name);
+ }
+ return result;
+}
+
+/**
+ * Add a new user to a remote RPC server
+ *
+ * @param argc Standard main() style argc
+ * @param argv Standard main() style argv. Initial components are already
+ * stripped
+ *
+ * @return A shell status integer (0 for success)
+ **/
+
+static int rpc_user_add(int argc, const char **argv)
+{
+ return run_rpc_command(NULL, PI_SAMR, 0, rpc_user_add_internals,
+ argc, argv);
+}
+
+/**
+ * Delete a user from a remote RPC server
+ *
+ * All parameters are provided by the run_rpc_command function, except for
+ * argc, argv which are passes through.
+ *
+ * @param domain_sid The domain sid acquired from the remote server
+ * @param cli A cli_state connected to the server.
+ * @param mem_ctx Talloc context, destoyed on completion of the function.
+ * @param argc Standard main() style argc
+ * @param argv Standard main() style argv. Initial components are already
+ * stripped
+ *
+ * @return Normal NTSTATUS return.
+ **/
+
+static NTSTATUS rpc_user_del_internals(const DOM_SID *domain_sid,
+ const char *domain_name,
+ struct cli_state *cli,
+ TALLOC_CTX *mem_ctx,
+ int argc, const char **argv)
+{
+ NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+ POLICY_HND connect_pol, domain_pol, user_pol;
+
+ if (argc < 1) {
+ d_printf("User must be specified\n");
+ rpc_user_usage(argc, argv);
+ return NT_STATUS_OK;
+ }
+ /* Get sam policy and domain handles */
+
+ result = cli_samr_connect(cli, mem_ctx, MAXIMUM_ALLOWED_ACCESS,
+ &connect_pol);
+
+ if (!NT_STATUS_IS_OK(result)) {
+ goto done;
+ }
+
+ result = cli_samr_open_domain(cli, mem_ctx, &connect_pol,
+ MAXIMUM_ALLOWED_ACCESS,
+ domain_sid, &domain_pol);
+
+ if (!NT_STATUS_IS_OK(result)) {
+ goto done;
+ }
+
+ /* Get handle on user */
+
+ {
+ uint32 *user_rids, num_rids, *name_types;
+ uint32 flags = 0x000003e8; /* Unknown */
+
+ result = cli_samr_lookup_names(cli, mem_ctx, &domain_pol,
+ flags, 1, &argv[0],
+ &num_rids, &user_rids,
+ &name_types);
+
+ if (!NT_STATUS_IS_OK(result)) {
+ goto done;
+ }
+
+ result = cli_samr_open_user(cli, mem_ctx, &domain_pol,
+ MAXIMUM_ALLOWED_ACCESS,
+ user_rids[0], &user_pol);
+
+ if (!NT_STATUS_IS_OK(result)) {
+ goto done;
+ }
+ }
+
+ /* Delete user */
+
+ result = cli_samr_delete_dom_user(cli, mem_ctx, &user_pol);
+
+ if (!NT_STATUS_IS_OK(result)) {
+ goto done;
+ }
+
+ /* Display results */
+
+ done:
+ return result;
+
+}
+
+/**
+ * Delete a user from a remote RPC server
+ *
+ * @param argc Standard main() style argc
+ * @param argv Standard main() style argv. Initial components are already
+ * stripped
+ *
+ * @return A shell status integer (0 for success)
+ **/
+
+static int rpc_user_delete(int argc, const char **argv)
+{
+ return run_rpc_command(NULL, PI_SAMR, 0, rpc_user_del_internals,
+ argc, argv);
+}
+
+/**
+ * Set a password for a user on a remote RPC server
+ *
+ * All parameters are provided by the run_rpc_command function, except for
+ * argc, argv which are passes through.
+ *
+ * @param domain_sid The domain sid acquired from the remote server
+ * @param cli A cli_state connected to the server.
+ * @param mem_ctx Talloc context, destoyed on completion of the function.
+ * @param argc Standard main() style argc
+ * @param argv Standard main() style argv. Initial components are already
+ * stripped
+ *
+ * @return Normal NTSTATUS return.
+ **/
+
+static NTSTATUS rpc_user_password_internals(const DOM_SID *domain_sid,
+ const char *domain_name,
+ struct cli_state *cli,
+ TALLOC_CTX *mem_ctx,
+ int argc, const char **argv)
+{
+ NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+ POLICY_HND connect_pol, domain_pol, user_pol;
+ SAM_USERINFO_CTR ctr;
+ SAM_USER_INFO_24 p24;
+ uchar pwbuf[516];
+ const char *user;
+ const char *new_password;
+ char *prompt = NULL;
+
+ if (argc < 1) {
+ d_printf("User must be specified\n");
+ rpc_user_usage(argc, argv);
+ return NT_STATUS_OK;
+ }
+
+ user = argv[0];
+
+ if (argv[1]) {
+ new_password = argv[1];
+ } else {
+ asprintf(&prompt, "Enter new password for %s:", user);
+ new_password = getpass(prompt);
+ SAFE_FREE(prompt);
+ }
+
+ /* Get sam policy and domain handles */
+
+ result = cli_samr_connect(cli, mem_ctx, MAXIMUM_ALLOWED_ACCESS,
+ &connect_pol);
+
+ if (!NT_STATUS_IS_OK(result)) {
+ goto done;
+ }
+
+ result = cli_samr_open_domain(cli, mem_ctx, &connect_pol,
+ MAXIMUM_ALLOWED_ACCESS,
+ domain_sid, &domain_pol);
+
+ if (!NT_STATUS_IS_OK(result)) {
+ goto done;
+ }
+
+ /* Get handle on user */
+
+ {
+ uint32 *user_rids, num_rids, *name_types;
+ uint32 flags = 0x000003e8; /* Unknown */
+
+ result = cli_samr_lookup_names(cli, mem_ctx, &domain_pol,
+ flags, 1, &user,
+ &num_rids, &user_rids,
+ &name_types);
+
+ if (!NT_STATUS_IS_OK(result)) {
+ goto done;
+ }
+
+ result = cli_samr_open_user(cli, mem_ctx, &domain_pol,
+ MAXIMUM_ALLOWED_ACCESS,
+ user_rids[0], &user_pol);
+
+ if (!NT_STATUS_IS_OK(result)) {
+ goto done;
+ }
+ }
+
+ /* Set password on account */
+
+ ZERO_STRUCT(ctr);
+ ZERO_STRUCT(p24);
+
+ encode_pw_buffer(pwbuf, new_password, STR_UNICODE);
+
+ init_sam_user_info24(&p24, (char *)pwbuf,24);
+
+ ctr.switch_value = 24;
+ ctr.info.id24 = &p24;
+
+ result = cli_samr_set_userinfo(cli, mem_ctx, &user_pol, 24,
+ &cli->user_session_key, &ctr);
+
+ if (!NT_STATUS_IS_OK(result)) {
+ goto done;
+ }
+
+ /* Display results */
+
+ done:
+ return result;
+
+}
+
+/**
+ * Set a user's password on a remote RPC server
+ *
+ * @param argc Standard main() style argc
+ * @param argv Standard main() style argv. Initial components are already
+ * stripped
+ *
+ * @return A shell status integer (0 for success)
+ **/
+
+static int rpc_user_password(int argc, const char **argv)
+{
+ return run_rpc_command(NULL, PI_SAMR, 0, rpc_user_password_internals,
+ argc, argv);
+}
+
+/**
+ * List user's groups on a remote RPC server
+ *
+ * All parameters are provided by the run_rpc_command function, except for
+ * argc, argv which are passes through.
+ *
+ * @param domain_sid The domain sid acquired from the remote server
+ * @param cli A cli_state connected to the server.
+ * @param mem_ctx Talloc context, destoyed on completion of the function.
+ * @param argc Standard main() style argc
+ * @param argv Standard main() style argv. Initial components are already
+ * stripped
+ *
+ * @return Normal NTSTATUS return.
+ **/
+
+static NTSTATUS
+rpc_user_info_internals(const DOM_SID *domain_sid, const char *domain_name,
+ struct cli_state *cli,
+ TALLOC_CTX *mem_ctx, int argc, const char **argv)
+{
+ POLICY_HND connect_pol, domain_pol, user_pol;
+ NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+ uint32 *rids, num_rids, *name_types, num_names;
+ uint32 flags = 0x000003e8; /* Unknown */
+ int i;
+ char **names;
+ DOM_GID *user_gids;
+
+ if (argc < 1) {
+ d_printf("User must be specified\n");
+ rpc_user_usage(argc, argv);
+ return NT_STATUS_OK;
+ }
+ /* Get sam policy handle */
+
+ result = cli_samr_connect(cli, mem_ctx, MAXIMUM_ALLOWED_ACCESS,
+ &connect_pol);
+ if (!NT_STATUS_IS_OK(result)) goto done;
+
+ /* Get domain policy handle */
+
+ result = cli_samr_open_domain(cli, mem_ctx, &connect_pol,
+ MAXIMUM_ALLOWED_ACCESS,
+ domain_sid, &domain_pol);
+ if (!NT_STATUS_IS_OK(result)) goto done;
+
+ /* Get handle on user */
+
+ result = cli_samr_lookup_names(cli, mem_ctx, &domain_pol,
+ flags, 1, &argv[0],
+ &num_rids, &rids, &name_types);
+
+ if (!NT_STATUS_IS_OK(result)) goto done;
+
+ result = cli_samr_open_user(cli, mem_ctx, &domain_pol,
+ MAXIMUM_ALLOWED_ACCESS,
+ rids[0], &user_pol);
+ if (!NT_STATUS_IS_OK(result)) goto done;
+
+ result = cli_samr_query_usergroups(cli, mem_ctx, &user_pol,
+ &num_rids, &user_gids);
+
+ /* Look up rids */
+
+ rids = (uint32 *)talloc(mem_ctx, sizeof(uint32) * num_rids);
+
+ for (i = 0; i < num_rids; i++)
+ rids[i] = user_gids[i].g_rid;
+
+ result = cli_samr_lookup_rids(cli, mem_ctx, &domain_pol,
+ flags, num_rids, rids,
+ &num_names, &names, &name_types);
+
+ if (!NT_STATUS_IS_OK(result)) {
+ goto done;
+ }
+
+ /* Display results */
+
+ for (i = 0; i < num_names; i++)
+ printf("%s\n", names[i]);
+
+ done:
+ return result;
+}
+
+/**
+ * List a user's groups from a remote RPC server
+ *
+ * @param argc Standard main() style argc
+ * @param argv Standard main() style argv. Initial components are already
+ * stripped
+ *
+ * @return A shell status integer (0 for success)
+ **/
+
+static int rpc_user_info(int argc, const char **argv)
+{
+ return run_rpc_command(NULL, PI_SAMR, 0, rpc_user_info_internals,
+ argc, argv);
+}
+
+/**
+ * List users on a remote RPC server
+ *
+ * All parameters are provided by the run_rpc_command function, except for
+ * argc, argv which are passes through.
+ *
+ * @param domain_sid The domain sid acquired from the remote server
+ * @param cli A cli_state connected to the server.
+ * @param mem_ctx Talloc context, destoyed on completion of the function.
+ * @param argc Standard main() style argc
+ * @param argv Standard main() style argv. Initial components are already
+ * stripped
+ *
+ * @return Normal NTSTATUS return.
+ **/
+
+static NTSTATUS
+rpc_user_list_internals(const DOM_SID *domain_sid, const char *domain_name,
+ struct cli_state *cli,
+ TALLOC_CTX *mem_ctx, int argc, const char **argv)
+{
+ POLICY_HND connect_pol, domain_pol;
+ NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+ uint32 start_idx=0, num_entries, i, loop_count = 0;
+ SAM_DISPINFO_CTR ctr;
+ SAM_DISPINFO_1 info1;
+
+ /* Get sam policy handle */
+
+ result = cli_samr_connect(cli, mem_ctx, MAXIMUM_ALLOWED_ACCESS,
+ &connect_pol);
+ if (!NT_STATUS_IS_OK(result)) {
+ goto done;
+ }
+
+ /* Get domain policy handle */
+
+ result = cli_samr_open_domain(cli, mem_ctx, &connect_pol,
+ MAXIMUM_ALLOWED_ACCESS,
+ domain_sid, &domain_pol);
+ if (!NT_STATUS_IS_OK(result)) {
+ goto done;
+ }
+
+ /* Query domain users */
+ ZERO_STRUCT(ctr);
+ ZERO_STRUCT(info1);
+ ctr.sam.info1 = &info1;
+ if (opt_long_list_entries)
+ d_printf("\nUser name Comment"\
+ "\n-----------------------------\n");
+ do {
+ fstring user, desc;
+ uint32 max_entries, max_size;
+
+ get_query_dispinfo_params(
+ loop_count, &max_entries, &max_size);
+
+ result = cli_samr_query_dispinfo(cli, mem_ctx, &domain_pol,
+ &start_idx, 1, &num_entries,
+ max_entries, max_size, &ctr);
+ loop_count++;
+
+ for (i = 0; i < num_entries; i++) {
+ unistr2_to_ascii(user, &(&ctr.sam.info1->str[i])->uni_acct_name, sizeof(user)-1);
+ if (opt_long_list_entries)
+ unistr2_to_ascii(desc, &(&ctr.sam.info1->str[i])->uni_acct_desc, sizeof(desc)-1);
+
+ if (opt_long_list_entries)
+ printf("%-21.21s %s\n", user, desc);
+ else
+ printf("%s\n", user);
+ }
+ } while (NT_STATUS_EQUAL(result, STATUS_MORE_ENTRIES));
+
+ done:
+ return result;
+}
+
+/**
+ * 'net rpc user' entrypoint.
+ * @param argc Standard main() style argc
+ * @param argc Standard main() style argv. Initial components are already
+ * stripped
+ **/
+
+int net_rpc_user(int argc, const char **argv)
+{
+ struct functable func[] = {
+ {"add", rpc_user_add},
+ {"info", rpc_user_info},
+ {"delete", rpc_user_delete},
+ {"password", rpc_user_password},
+ {NULL, NULL}
+ };
+
+ if (argc == 0) {
+ if (opt_long_list_entries) {
+ } else {
+ }
+ return run_rpc_command(NULL,PI_SAMR, 0,
+ rpc_user_list_internals,
+ argc, argv);
+ }
+
+ return net_run_function(argc, argv, func, rpc_user_usage);
+}
+
+
+/****************************************************************************/
+
+/**
+ * Basic usage function for 'net rpc group'
+ * @param argc Standard main() style argc.
+ * @param argv Standard main() style argv. Initial components are already
+ * stripped.
+ **/
+
+static int rpc_group_usage(int argc, const char **argv)
+{
+ return net_help_group(argc, argv);
+}
+
+static NTSTATUS
+rpc_group_add_internals(const DOM_SID *domain_sid, const char *domain_name,
+ struct cli_state *cli,
+ TALLOC_CTX *mem_ctx, int argc, const char **argv)
+{
+ POLICY_HND connect_pol, domain_pol, group_pol;
+ NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+ GROUP_INFO_CTR group_info;
+
+ if (argc != 1) {
+ d_printf("Group name must be specified\n");
+ rpc_group_usage(argc, argv);
+ return NT_STATUS_OK;
+ }
+
+ /* Get sam policy handle */
+
+ result = cli_samr_connect(cli, mem_ctx, MAXIMUM_ALLOWED_ACCESS,
+ &connect_pol);
+ if (!NT_STATUS_IS_OK(result)) goto done;
+
+ /* Get domain policy handle */
+
+ result = cli_samr_open_domain(cli, mem_ctx, &connect_pol,
+ MAXIMUM_ALLOWED_ACCESS,
+ domain_sid, &domain_pol);
+ if (!NT_STATUS_IS_OK(result)) goto done;
+
+ /* Create the group */
+
+ result = cli_samr_create_dom_group(cli, mem_ctx, &domain_pol,
+ argv[0], MAXIMUM_ALLOWED_ACCESS,
+ &group_pol);
+ if (!NT_STATUS_IS_OK(result)) goto done;
+
+ if (strlen(opt_comment) == 0) goto done;
+
+ /* We've got a comment to set */
+
+ group_info.switch_value1 = 4;
+ init_samr_group_info4(&group_info.group.info4, opt_comment);
+
+ result = cli_samr_set_groupinfo(cli, mem_ctx, &group_pol, &group_info);
+ if (!NT_STATUS_IS_OK(result)) goto done;
+
+ done:
+ if (NT_STATUS_IS_OK(result))
+ DEBUG(5, ("add group succeeded\n"));
+ else
+ d_printf("add group failed: %s\n", nt_errstr(result));
+
+ return result;
+}
+
+static NTSTATUS
+rpc_alias_add_internals(const DOM_SID *domain_sid, const char *domain_name,
+ struct cli_state *cli,
+ TALLOC_CTX *mem_ctx, int argc, const char **argv)
+{
+ POLICY_HND connect_pol, domain_pol, alias_pol;
+ NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+ ALIAS_INFO_CTR alias_info;
+
+ if (argc != 1) {
+ d_printf("Group name must be specified\n");
+ rpc_group_usage(argc, argv);
+ return NT_STATUS_OK;
+ }
+
+ /* Get sam policy handle */
+
+ result = cli_samr_connect(cli, mem_ctx, MAXIMUM_ALLOWED_ACCESS,
+ &connect_pol);
+ if (!NT_STATUS_IS_OK(result)) goto done;
+
+ /* Get domain policy handle */
+
+ result = cli_samr_open_domain(cli, mem_ctx, &connect_pol,
+ MAXIMUM_ALLOWED_ACCESS,
+ domain_sid, &domain_pol);
+ if (!NT_STATUS_IS_OK(result)) goto done;
+
+ /* Create the group */
+
+ result = cli_samr_create_dom_alias(cli, mem_ctx, &domain_pol,
+ argv[0], &alias_pol);
+ if (!NT_STATUS_IS_OK(result)) goto done;
+
+ if (strlen(opt_comment) == 0) goto done;
+
+ /* We've got a comment to set */
+
+ alias_info.switch_value1 = 3;
+ alias_info.switch_value2 = 3;
+ init_samr_alias_info3(&alias_info.alias.info3, opt_comment);
+
+ result = cli_samr_set_aliasinfo(cli, mem_ctx, &alias_pol, &alias_info);
+ if (!NT_STATUS_IS_OK(result)) goto done;
+
+ done:
+ if (NT_STATUS_IS_OK(result))
+ DEBUG(5, ("add group succeeded\n"));
+ else
+ d_printf("add group failed: %s\n", nt_errstr(result));
+
+ return result;
+}
+
+static int rpc_group_add(int argc, const char **argv)
+{
+ if (opt_localgroup)
+ return run_rpc_command(NULL, PI_SAMR, 0,
+ rpc_alias_add_internals,
+ argc, argv);
+
+ return run_rpc_command(NULL, PI_SAMR, 0,
+ rpc_group_add_internals,
+ argc, argv);
+}
+
+static NTSTATUS
+get_sid_from_name(struct cli_state *cli, TALLOC_CTX *mem_ctx, const char *name,
+ DOM_SID *sid, enum SID_NAME_USE *type)
+{
+ int current_pipe = cli->pipe_idx;
+
+ DOM_SID *sids = NULL;
+ uint32 *types = NULL;
+ POLICY_HND lsa_pol;
+ NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+
+ if (current_pipe != PI_LSARPC) {
+
+ if (current_pipe != -1)
+ cli_nt_session_close(cli);
+
+ if (!cli_nt_session_open(cli, PI_LSARPC))
+ goto done;
+ }
+
+ result = cli_lsa_open_policy(cli, mem_ctx, False,
+ SEC_RIGHTS_MAXIMUM_ALLOWED, &lsa_pol);
+
+ if (!NT_STATUS_IS_OK(result))
+ goto done;
+
+ result = cli_lsa_lookup_names(cli, mem_ctx, &lsa_pol, 1,
+ &name, &sids, &types);
+
+ if (NT_STATUS_IS_OK(result)) {
+ sid_copy(sid, &sids[0]);
+ *type = types[0];
+ }
+
+ cli_lsa_close(cli, mem_ctx, &lsa_pol);
+
+ done:
+ if (current_pipe != PI_LSARPC) {
+ cli_nt_session_close(cli);
+ if (current_pipe != -1)
+ cli_nt_session_open(cli, current_pipe);
+ }
+
+ if (!NT_STATUS_IS_OK(result) && (StrnCaseCmp(name, "S-", 2) == 0)) {
+
+ /* Try as S-1-5-whatever */
+
+ DOM_SID tmp_sid;
+
+ if (string_to_sid(&tmp_sid, name)) {
+ sid_copy(sid, &tmp_sid);
+ *type = SID_NAME_UNKNOWN;
+ result = NT_STATUS_OK;
+ }
+ }
+
+ return result;
+}
+
+static NTSTATUS
+rpc_add_groupmem(struct cli_state *cli, TALLOC_CTX *mem_ctx,
+ const DOM_SID *group_sid, const char *member)
+{
+ POLICY_HND connect_pol, domain_pol;
+ NTSTATUS result;
+ uint32 group_rid;
+ POLICY_HND group_pol;
+
+ uint32 num_rids;
+ uint32 *rids = NULL;
+ uint32 *rid_types = NULL;
+
+ DOM_SID sid;
+
+ sid_copy(&sid, group_sid);
+
+ if (!sid_split_rid(&sid, &group_rid))
+ return NT_STATUS_UNSUCCESSFUL;
+
+ /* Get sam policy handle */
+ result = cli_samr_connect(cli, mem_ctx, MAXIMUM_ALLOWED_ACCESS,
+ &connect_pol);
+ if (!NT_STATUS_IS_OK(result))
+ return result;
+
+ /* Get domain policy handle */
+ result = cli_samr_open_domain(cli, mem_ctx, &connect_pol,
+ MAXIMUM_ALLOWED_ACCESS,
+ &sid, &domain_pol);
+ if (!NT_STATUS_IS_OK(result))
+ return result;
+
+ result = cli_samr_lookup_names(cli, mem_ctx, &domain_pol, 1000,
+ 1, &member,
+ &num_rids, &rids, &rid_types);
+
+ if (!NT_STATUS_IS_OK(result)) {
+ d_printf("Could not lookup up group member %s\n", member);
+ goto done;
+ }
+
+ result = cli_samr_open_group(cli, mem_ctx, &domain_pol,
+ MAXIMUM_ALLOWED_ACCESS,
+ group_rid, &group_pol);
+
+ if (!NT_STATUS_IS_OK(result))
+ goto done;
+
+ result = cli_samr_add_groupmem(cli, mem_ctx, &group_pol, rids[0]);
+
+ done:
+ cli_samr_close(cli, mem_ctx, &connect_pol);
+ return result;
+}
+
+static NTSTATUS
+rpc_add_aliasmem(struct cli_state *cli, TALLOC_CTX *mem_ctx,
+ const DOM_SID *alias_sid, const char *member)
+{
+ POLICY_HND connect_pol, domain_pol;
+ NTSTATUS result;
+ uint32 alias_rid;
+ POLICY_HND alias_pol;
+
+ DOM_SID member_sid;
+ enum SID_NAME_USE member_type;
+
+ DOM_SID sid;
+
+ sid_copy(&sid, alias_sid);
+
+ if (!sid_split_rid(&sid, &alias_rid))
+ return NT_STATUS_UNSUCCESSFUL;
+
+ result = get_sid_from_name(cli, mem_ctx, member,
+ &member_sid, &member_type);
+
+ if (!NT_STATUS_IS_OK(result)) {
+ d_printf("Could not lookup up group member %s\n", member);
+ return result;
+ }
+
+ /* Get sam policy handle */
+ result = cli_samr_connect(cli, mem_ctx, MAXIMUM_ALLOWED_ACCESS,
+ &connect_pol);
+ if (!NT_STATUS_IS_OK(result)) {
+ goto done;
+ }
+
+ /* Get domain policy handle */
+ result = cli_samr_open_domain(cli, mem_ctx, &connect_pol,
+ MAXIMUM_ALLOWED_ACCESS,
+ &sid, &domain_pol);
+ if (!NT_STATUS_IS_OK(result)) {
+ goto done;
+ }
+
+ result = cli_samr_open_alias(cli, mem_ctx, &domain_pol,
+ MAXIMUM_ALLOWED_ACCESS,
+ alias_rid, &alias_pol);
+
+ if (!NT_STATUS_IS_OK(result))
+ return result;
+
+ result = cli_samr_add_aliasmem(cli, mem_ctx, &alias_pol, &member_sid);
+
+ if (!NT_STATUS_IS_OK(result))
+ return result;
+
+ done:
+ cli_samr_close(cli, mem_ctx, &connect_pol);
+ return result;
+}
+
+static NTSTATUS
+rpc_group_addmem_internals(const DOM_SID *domain_sid, const char *domain_name,
+ struct cli_state *cli,
+ TALLOC_CTX *mem_ctx, int argc, const char **argv)
+{
+ DOM_SID group_sid;
+ enum SID_NAME_USE group_type;
+
+ if (argc != 2) {
+ d_printf("Usage: 'net rpc group addmem <group> <member>\n");
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ if (!NT_STATUS_IS_OK(get_sid_from_name(cli, mem_ctx, argv[0],
+ &group_sid, &group_type))) {
+ d_printf("Could not lookup group name %s\n", argv[0]);
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ if (group_type == SID_NAME_DOM_GRP) {
+ NTSTATUS result = rpc_add_groupmem(cli, mem_ctx,
+ &group_sid, argv[1]);
+
+ if (!NT_STATUS_IS_OK(result)) {
+ d_printf("Could not add %s to %s: %s\n",
+ argv[1], argv[0], nt_errstr(result));
+ }
+ return result;
+ }
+
+ if (group_type == SID_NAME_ALIAS) {
+ NTSTATUS result = rpc_add_aliasmem(cli, mem_ctx,
+ &group_sid, argv[1]);
+
+ if (!NT_STATUS_IS_OK(result)) {
+ d_printf("Could not add %s to %s: %s\n",
+ argv[1], argv[0], nt_errstr(result));
+ }
+ return result;
+ }
+
+ d_printf("Can only add members to global or local groups which "
+ "%s is not\n", argv[0]);
+
+ return NT_STATUS_UNSUCCESSFUL;
+}
+
+static int rpc_group_addmem(int argc, const char **argv)
+{
+ return run_rpc_command(NULL, PI_SAMR, 0,
+ rpc_group_addmem_internals,
+ argc, argv);
+}
+
+static NTSTATUS
+rpc_del_groupmem(struct cli_state *cli, TALLOC_CTX *mem_ctx,
+ const DOM_SID *group_sid, const char *member)
+{
+ POLICY_HND connect_pol, domain_pol;
+ NTSTATUS result;
+ uint32 group_rid;
+ POLICY_HND group_pol;
+
+ uint32 num_rids;
+ uint32 *rids = NULL;
+ uint32 *rid_types = NULL;
+
+ DOM_SID sid;
+
+ sid_copy(&sid, group_sid);
+
+ if (!sid_split_rid(&sid, &group_rid))
+ return NT_STATUS_UNSUCCESSFUL;
+
+ /* Get sam policy handle */
+ result = cli_samr_connect(cli, mem_ctx, MAXIMUM_ALLOWED_ACCESS,
+ &connect_pol);
+ if (!NT_STATUS_IS_OK(result))
+ return result;
+
+ /* Get domain policy handle */
+ result = cli_samr_open_domain(cli, mem_ctx, &connect_pol,
+ MAXIMUM_ALLOWED_ACCESS,
+ &sid, &domain_pol);
+ if (!NT_STATUS_IS_OK(result))
+ return result;
+
+ result = cli_samr_lookup_names(cli, mem_ctx, &domain_pol, 1000,
+ 1, &member,
+ &num_rids, &rids, &rid_types);
+
+ if (!NT_STATUS_IS_OK(result)) {
+ d_printf("Could not lookup up group member %s\n", member);
+ goto done;
+ }
+
+ result = cli_samr_open_group(cli, mem_ctx, &domain_pol,
+ MAXIMUM_ALLOWED_ACCESS,
+ group_rid, &group_pol);
+
+ if (!NT_STATUS_IS_OK(result))
+ goto done;
+
+ result = cli_samr_del_groupmem(cli, mem_ctx, &group_pol, rids[0]);
+
+ done:
+ cli_samr_close(cli, mem_ctx, &connect_pol);
+ return result;
+}
+
+static NTSTATUS
+rpc_del_aliasmem(struct cli_state *cli, TALLOC_CTX *mem_ctx,
+ const DOM_SID *alias_sid, const char *member)
+{
+ POLICY_HND connect_pol, domain_pol;
+ NTSTATUS result;
+ uint32 alias_rid;
+ POLICY_HND alias_pol;
+
+ DOM_SID member_sid;
+ enum SID_NAME_USE member_type;
+
+ DOM_SID sid;
+
+ sid_copy(&sid, alias_sid);
+
+ if (!sid_split_rid(&sid, &alias_rid))
+ return NT_STATUS_UNSUCCESSFUL;
+
+ result = get_sid_from_name(cli, mem_ctx, member,
+ &member_sid, &member_type);
+
+ if (!NT_STATUS_IS_OK(result)) {
+ d_printf("Could not lookup up group member %s\n", member);
+ return result;
+ }
+
+ /* Get sam policy handle */
+ result = cli_samr_connect(cli, mem_ctx, MAXIMUM_ALLOWED_ACCESS,
+ &connect_pol);
+ if (!NT_STATUS_IS_OK(result)) {
+ goto done;
+ }
+
+ /* Get domain policy handle */
+ result = cli_samr_open_domain(cli, mem_ctx, &connect_pol,
+ MAXIMUM_ALLOWED_ACCESS,
+ &sid, &domain_pol);
+ if (!NT_STATUS_IS_OK(result)) {
+ goto done;
+ }
+
+ result = cli_samr_open_alias(cli, mem_ctx, &domain_pol,
+ MAXIMUM_ALLOWED_ACCESS,
+ alias_rid, &alias_pol);
+
+ if (!NT_STATUS_IS_OK(result))
+ return result;
+
+ result = cli_samr_del_aliasmem(cli, mem_ctx, &alias_pol, &member_sid);
+
+ if (!NT_STATUS_IS_OK(result))
+ return result;
+
+ done:
+ cli_samr_close(cli, mem_ctx, &connect_pol);
+ return result;
+}
+
+static NTSTATUS
+rpc_group_delmem_internals(const DOM_SID *domain_sid, const char *domain_name,
+ struct cli_state *cli,
+ TALLOC_CTX *mem_ctx, int argc, const char **argv)
+{
+ DOM_SID group_sid;
+ enum SID_NAME_USE group_type;
+
+ if (argc != 2) {
+ d_printf("Usage: 'net rpc group delmem <group> <member>\n");
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ if (!NT_STATUS_IS_OK(get_sid_from_name(cli, mem_ctx, argv[0],
+ &group_sid, &group_type))) {
+ d_printf("Could not lookup group name %s\n", argv[0]);
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ if (group_type == SID_NAME_DOM_GRP) {
+ NTSTATUS result = rpc_del_groupmem(cli, mem_ctx,
+ &group_sid, argv[1]);
+
+ if (!NT_STATUS_IS_OK(result)) {
+ d_printf("Could not del %s from %s: %s\n",
+ argv[1], argv[0], nt_errstr(result));
+ }
+ return result;
+ }
+
+ if (group_type == SID_NAME_ALIAS) {
+ NTSTATUS result = rpc_del_aliasmem(cli, mem_ctx,
+ &group_sid, argv[1]);
+
+ if (!NT_STATUS_IS_OK(result)) {
+ d_printf("Could not del %s from %s: %s\n",
+ argv[1], argv[0], nt_errstr(result));
+ }
+ return result;
+ }
+
+ d_printf("Can only delete members from global or local groups which "
+ "%s is not\n", argv[0]);
+
+ return NT_STATUS_UNSUCCESSFUL;
+}
+
+static int rpc_group_delmem(int argc, const char **argv)
+{
+ return run_rpc_command(NULL, PI_SAMR, 0,
+ rpc_group_delmem_internals,
+ argc, argv);
+}
+
+/**
+ * List groups on a remote RPC server
+ *
+ * All parameters are provided by the run_rpc_command function, except for
+ * argc, argv which are passes through.
+ *
+ * @param domain_sid The domain sid acquired from the remote server
+ * @param cli A cli_state connected to the server.
+ * @param mem_ctx Talloc context, destoyed on completion of the function.
+ * @param argc Standard main() style argc
+ * @param argv Standard main() style argv. Initial components are already
+ * stripped
+ *
+ * @return Normal NTSTATUS return.
+ **/
+
+static NTSTATUS
+rpc_group_list_internals(const DOM_SID *domain_sid, const char *domain_name,
+ struct cli_state *cli,
+ TALLOC_CTX *mem_ctx, int argc, const char **argv)
+{
+ POLICY_HND connect_pol, domain_pol;
+ NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+ uint32 start_idx=0, max_entries=250, num_entries, i, loop_count = 0;
+ struct acct_info *groups;
+ DOM_SID global_sid_Builtin;
+ BOOL global = False;
+ BOOL local = False;
+ BOOL builtin = False;
+
+ if (argc == 0) {
+ global = True;
+ local = True;
+ builtin = True;
+ }
+
+ for (i=0; i<argc; i++) {
+ if (strequal(argv[i], "global"))
+ global = True;
+
+ if (strequal(argv[i], "local"))
+ local = True;
+
+ if (strequal(argv[i], "builtin"))
+ builtin = True;
+ }
+
+ string_to_sid(&global_sid_Builtin, "S-1-5-32");
+
+ /* Get sam policy handle */
+
+ result = cli_samr_connect(cli, mem_ctx, MAXIMUM_ALLOWED_ACCESS,
+ &connect_pol);
+ if (!NT_STATUS_IS_OK(result)) {
+ goto done;
+ }
+
+ /* Get domain policy handle */
+
+ result = cli_samr_open_domain(cli, mem_ctx, &connect_pol,
+ MAXIMUM_ALLOWED_ACCESS,
+ domain_sid, &domain_pol);
+ if (!NT_STATUS_IS_OK(result)) {
+ goto done;
+ }
+
+ /* Query domain groups */
+ if (opt_long_list_entries)
+ d_printf("\nGroup name Comment"\
+ "\n-----------------------------\n");
+ do {
+ SAM_DISPINFO_CTR ctr;
+ SAM_DISPINFO_3 info3;
+ uint32 max_size;
+
+ ZERO_STRUCT(ctr);
+ ZERO_STRUCT(info3);
+ ctr.sam.info3 = &info3;
+
+ if (!global) break;
+
+ get_query_dispinfo_params(
+ loop_count, &max_entries, &max_size);
+
+ result = cli_samr_query_dispinfo(cli, mem_ctx, &domain_pol,
+ &start_idx, 3, &num_entries,
+ max_entries, max_size, &ctr);
+
+ if (!NT_STATUS_IS_OK(result) &&
+ !NT_STATUS_EQUAL(result, STATUS_MORE_ENTRIES))
+ break;
+
+ for (i = 0; i < num_entries; i++) {
+
+ fstring group, desc;
+
+ unistr2_to_ascii(group, &(&ctr.sam.info3->str[i])->uni_grp_name, sizeof(group)-1);
+ unistr2_to_ascii(desc, &(&ctr.sam.info3->str[i])->uni_grp_desc, sizeof(desc)-1);
+
+ if (opt_long_list_entries)
+ printf("%-21.21s %-50.50s\n",
+ group, desc);
+ else
+ printf("%s\n", group);
+ }
+ } while (NT_STATUS_EQUAL(result, STATUS_MORE_ENTRIES));
+ /* query domain aliases */
+ start_idx = 0;
+ do {
+ if (!local) break;
+
+ result = cli_samr_enum_als_groups(cli, mem_ctx, &domain_pol,
+ &start_idx, max_entries,
+ &groups, &num_entries);
+
+ if (!NT_STATUS_IS_OK(result) &&
+ !NT_STATUS_EQUAL(result, STATUS_MORE_ENTRIES))
+ break;
+
+ for (i = 0; i < num_entries; i++) {
+
+ char *description = NULL;
+
+ if (opt_long_list_entries) {
+
+ POLICY_HND alias_pol;
+ ALIAS_INFO_CTR ctr;
+
+ if ((NT_STATUS_IS_OK(cli_samr_open_alias(cli, mem_ctx,
+ &domain_pol,
+ 0x8,
+ groups[i].rid,
+ &alias_pol))) &&
+ (NT_STATUS_IS_OK(cli_samr_query_alias_info(cli, mem_ctx,
+ &alias_pol, 3,
+ &ctr))) &&
+ (NT_STATUS_IS_OK(cli_samr_close(cli, mem_ctx,
+ &alias_pol)))) {
+ description = unistr2_tdup(mem_ctx,
+ &ctr.alias.info3.uni_acct_desc);
+ }
+ }
+
+ if (description != NULL) {
+ printf("%-21.21s %-50.50s\n",
+ groups[i].acct_name,
+ description);
+ } else {
+ printf("%s\n", groups[i].acct_name);
+ }
+ }
+ } while (NT_STATUS_EQUAL(result, STATUS_MORE_ENTRIES));
+ cli_samr_close(cli, mem_ctx, &domain_pol);
+ /* Get builtin policy handle */
+
+ result = cli_samr_open_domain(cli, mem_ctx, &connect_pol,
+ MAXIMUM_ALLOWED_ACCESS,
+ &global_sid_Builtin, &domain_pol);
+ if (!NT_STATUS_IS_OK(result)) {
+ goto done;
+ }
+ /* query builtin aliases */
+ start_idx = 0;
+ do {
+ if (!builtin) break;
+
+ result = cli_samr_enum_als_groups(cli, mem_ctx, &domain_pol,
+ &start_idx, max_entries,
+ &groups, &num_entries);
+
+ if (!NT_STATUS_IS_OK(result) &&
+ !NT_STATUS_EQUAL(result, STATUS_MORE_ENTRIES))
+ break;
+
+ for (i = 0; i < num_entries; i++) {
+
+ char *description = NULL;
+
+ if (opt_long_list_entries) {
+
+ POLICY_HND alias_pol;
+ ALIAS_INFO_CTR ctr;
+
+ if ((NT_STATUS_IS_OK(cli_samr_open_alias(cli, mem_ctx,
+ &domain_pol,
+ 0x8,
+ groups[i].rid,
+ &alias_pol))) &&
+ (NT_STATUS_IS_OK(cli_samr_query_alias_info(cli, mem_ctx,
+ &alias_pol, 3,
+ &ctr))) &&
+ (NT_STATUS_IS_OK(cli_samr_close(cli, mem_ctx,
+ &alias_pol)))) {
+ description = unistr2_tdup(mem_ctx,
+ &ctr.alias.info3.uni_acct_desc);
+ }
+ }
+
+ if (description != NULL) {
+ printf("%-21.21s %-50.50s\n",
+ groups[i].acct_name,
+ description);
+ } else {
+ printf("%s\n", groups[i].acct_name);
+ }
+ }
+ } while (NT_STATUS_EQUAL(result, STATUS_MORE_ENTRIES));
+
+ done:
+ return result;
+}
+
+static int rpc_group_list(int argc, const char **argv)
+{
+ return run_rpc_command(NULL, PI_SAMR, 0,
+ rpc_group_list_internals,
+ argc, argv);
+}
+
+static NTSTATUS
+rpc_list_group_members(struct cli_state *cli, TALLOC_CTX *mem_ctx,
+ const char *domain_name, const DOM_SID *domain_sid,
+ POLICY_HND *domain_pol, uint32 rid)
+{
+ NTSTATUS result;
+ POLICY_HND group_pol;
+ uint32 num_members, *group_rids, *group_attrs;
+ uint32 num_names;
+ char **names;
+ uint32 *name_types;
+ int i;
+
+ fstring sid_str;
+ sid_to_string(sid_str, domain_sid);
+
+ result = cli_samr_open_group(cli, mem_ctx, domain_pol,
+ MAXIMUM_ALLOWED_ACCESS,
+ rid, &group_pol);
+
+ if (!NT_STATUS_IS_OK(result))
+ return result;
+
+ result = cli_samr_query_groupmem(cli, mem_ctx, &group_pol,
+ &num_members, &group_rids,
+ &group_attrs);
+
+ if (!NT_STATUS_IS_OK(result))
+ return result;
+
+ while (num_members > 0) {
+ int this_time = 512;
+
+ if (num_members < this_time)
+ this_time = num_members;
+
+ result = cli_samr_lookup_rids(cli, mem_ctx, domain_pol, 1000,
+ this_time, group_rids,
+ &num_names, &names, &name_types);
+
+ if (!NT_STATUS_IS_OK(result))
+ return result;
+
+ /* We only have users as members, but make the output
+ the same as the output of alias members */
+
+ for (i = 0; i < this_time; i++) {
+
+ if (opt_long_list_entries) {
+ printf("%s-%d %s\\%s %d\n", sid_str,
+ group_rids[i], domain_name, names[i],
+ SID_NAME_USER);
+ } else {
+ printf("%s\\%s\n", domain_name, names[i]);
+ }
+ }
+
+ num_members -= this_time;
+ group_rids += 512;
+ }
+
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS
+rpc_list_alias_members(struct cli_state *cli, TALLOC_CTX *mem_ctx,
+ POLICY_HND *domain_pol, uint32 rid)
+{
+ NTSTATUS result;
+ POLICY_HND alias_pol, lsa_pol;
+ uint32 num_members;
+ DOM_SID *alias_sids;
+ char **domains;
+ char **names;
+ uint32 *types;
+ int i;
+
+ result = cli_samr_open_alias(cli, mem_ctx, domain_pol,
+ MAXIMUM_ALLOWED_ACCESS, rid, &alias_pol);
+
+ if (!NT_STATUS_IS_OK(result))
+ return result;
+
+ result = cli_samr_query_aliasmem(cli, mem_ctx, &alias_pol,
+ &num_members, &alias_sids);
+
+ if (!NT_STATUS_IS_OK(result)) {
+ d_printf("Couldn't list alias members\n");
+ return result;
+ }
+
+ if (num_members == 0) {
+ return NT_STATUS_OK;
+ }
+
+ cli_nt_session_close(cli);
+
+ if (!cli_nt_session_open(cli, PI_LSARPC)) {
+ d_printf("Couldn't open LSA pipe\n");
+ return result;
+ }
+
+ result = cli_lsa_open_policy(cli, mem_ctx, True,
+ SEC_RIGHTS_MAXIMUM_ALLOWED, &lsa_pol);
+
+ if (!NT_STATUS_IS_OK(result)) {
+ d_printf("Couldn't open LSA policy handle\n");
+ return result;
+ }
+
+ result = cli_lsa_lookup_sids(cli, mem_ctx, &lsa_pol, num_members,
+ alias_sids,
+ &domains, &names, &types);
+
+ if (!NT_STATUS_IS_OK(result) &&
+ !NT_STATUS_EQUAL(result, STATUS_SOME_UNMAPPED)) {
+ d_printf("Couldn't lookup SIDs\n");
+ return result;
+ }
+
+ for (i = 0; i < num_members; i++) {
+ fstring sid_str;
+ sid_to_string(sid_str, &alias_sids[i]);
+
+ if (opt_long_list_entries) {
+ printf("%s %s\\%s %d\n", sid_str,
+ domains[i] ? domains[i] : "*unknown*",
+ names[i] ? names[i] : "*unknown*", types[i]);
+ } else {
+ if (domains[i])
+ printf("%s\\%s\n", domains[i], names[i]);
+ else
+ printf("%s\n", sid_str);
+ }
+ }
+
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS
+rpc_group_members_internals(const DOM_SID *domain_sid,
+ const char *domain_name,
+ struct cli_state *cli,
+ TALLOC_CTX *mem_ctx, int argc, const char **argv)
+{
+ NTSTATUS result;
+ POLICY_HND connect_pol, domain_pol;
+ uint32 num_rids, *rids, *rid_types;
+
+ /* Get sam policy handle */
+
+ result = cli_samr_connect(cli, mem_ctx, MAXIMUM_ALLOWED_ACCESS,
+ &connect_pol);
+
+ if (!NT_STATUS_IS_OK(result))
+ return result;
+
+ /* Get domain policy handle */
+
+ result = cli_samr_open_domain(cli, mem_ctx, &connect_pol,
+ MAXIMUM_ALLOWED_ACCESS,
+ domain_sid, &domain_pol);
+
+ if (!NT_STATUS_IS_OK(result))
+ return result;
+
+ result = cli_samr_lookup_names(cli, mem_ctx, &domain_pol, 1000,
+ 1, argv, &num_rids, &rids, &rid_types);
+
+ if (!NT_STATUS_IS_OK(result)) {
+
+ /* Ok, did not find it in the global sam, try with builtin */
+
+ DOM_SID sid_Builtin;
+
+ cli_samr_close(cli, mem_ctx, &domain_pol);
+
+ string_to_sid(&sid_Builtin, "S-1-5-32");
+
+ result = cli_samr_open_domain(cli, mem_ctx, &connect_pol,
+ MAXIMUM_ALLOWED_ACCESS,
+ &sid_Builtin, &domain_pol);
+
+ if (!NT_STATUS_IS_OK(result)) {
+ d_printf("Couldn't find group %s\n", argv[0]);
+ return result;
+ }
+
+ result = cli_samr_lookup_names(cli, mem_ctx, &domain_pol, 1000,
+ 1, argv, &num_rids,
+ &rids, &rid_types);
+
+ if (!NT_STATUS_IS_OK(result)) {
+ d_printf("Couldn't find group %s\n", argv[0]);
+ return result;
+ }
+ }
+
+ if (num_rids != 1) {
+ d_printf("Couldn't find group %s\n", argv[0]);
+ return result;
+ }
+
+ if (rid_types[0] == SID_NAME_DOM_GRP) {
+ return rpc_list_group_members(cli, mem_ctx, domain_name,
+ domain_sid, &domain_pol,
+ rids[0]);
+ }
+
+ if (rid_types[0] == SID_NAME_ALIAS) {
+ return rpc_list_alias_members(cli, mem_ctx, &domain_pol,
+ rids[0]);
+ }
+
+ return NT_STATUS_NO_SUCH_GROUP;
+}
+
+static int rpc_group_members(int argc, const char **argv)
+{
+ if (argc != 1) {
+ return rpc_group_usage(argc, argv);
+ }
+
+ return run_rpc_command(NULL, PI_SAMR, 0,
+ rpc_group_members_internals,
+ argc, argv);
+}
+
+/**
+ * 'net rpc group' entrypoint.
+ * @param argc Standard main() style argc
+ * @param argc Standard main() style argv. Initial components are already
+ * stripped
+ **/
+
+int net_rpc_group(int argc, const char **argv)
+{
+ struct functable func[] = {
+ {"add", rpc_group_add},
+ {"addmem", rpc_group_addmem},
+ {"delmem", rpc_group_delmem},
+#if 0
+ {"delete", rpc_group_delete},
+#endif
+ {"list", rpc_group_list},
+ {"members", rpc_group_members},
+ {NULL, NULL}
+ };
+
+ if (argc == 0) {
+ if (opt_long_list_entries) {
+ } else {
+ }
+ return run_rpc_command(NULL, PI_SAMR, 0,
+ rpc_group_list_internals,
+ argc, argv);
+ }
+
+ return net_run_function(argc, argv, func, rpc_group_usage);
+}
+
+/****************************************************************************/
+
+static int rpc_share_usage(int argc, const char **argv)
+{
+ return net_help_share(argc, argv);
+}
+
+/**
+ * Add a share on a remote RPC server
+ *
+ * All parameters are provided by the run_rpc_command function, except for
+ * argc, argv which are passes through.
+ *
+ * @param domain_sid The domain sid acquired from the remote server
+ * @param cli A cli_state connected to the server.
+ * @param mem_ctx Talloc context, destoyed on completion of the function.
+ * @param argc Standard main() style argc
+ * @param argv Standard main() style argv. Initial components are already
+ * stripped
+ *
+ * @return Normal NTSTATUS return.
+ **/
+static NTSTATUS
+rpc_share_add_internals(const DOM_SID *domain_sid, const char *domain_name,
+ struct cli_state *cli,
+ TALLOC_CTX *mem_ctx,int argc, const char **argv)
+{
+ WERROR result;
+ char *sharename=talloc_strdup(mem_ctx, argv[0]);
+ char *path;
+ uint32 type=0; /* only allow disk shares to be added */
+ uint32 num_users=0, perms=0;
+ char *password=NULL; /* don't allow a share password */
+
+ path = strchr(sharename, '=');
+ if (!path)
+ return NT_STATUS_UNSUCCESSFUL;
+ *path++ = '\0';
+
+ result = cli_srvsvc_net_share_add(cli, mem_ctx, sharename, type,
+ opt_comment, perms, opt_maxusers,
+ num_users, path, password);
+ return W_ERROR_IS_OK(result) ? NT_STATUS_OK : NT_STATUS_UNSUCCESSFUL;
+}
+
+static int rpc_share_add(int argc, const char **argv)
+{
+ if ((argc < 1) || !strchr(argv[0], '=')) {
+ DEBUG(1,("Sharename or path not specified on add\n"));
+ return rpc_share_usage(argc, argv);
+ }
+ return run_rpc_command(NULL, PI_SRVSVC, 0,
+ rpc_share_add_internals,
+ argc, argv);
+}
+
+/**
+ * Delete a share on a remote RPC server
+ *
+ * All parameters are provided by the run_rpc_command function, except for
+ * argc, argv which are passes through.
+ *
+ * @param domain_sid The domain sid acquired from the remote server
+ * @param cli A cli_state connected to the server.
+ * @param mem_ctx Talloc context, destoyed on completion of the function.
+ * @param argc Standard main() style argc
+ * @param argv Standard main() style argv. Initial components are already
+ * stripped
+ *
+ * @return Normal NTSTATUS return.
+ **/
+static NTSTATUS
+rpc_share_del_internals(const DOM_SID *domain_sid, const char *domain_name,
+ struct cli_state *cli,
+ TALLOC_CTX *mem_ctx,int argc, const char **argv)
+{
+ WERROR result;
+
+ result = cli_srvsvc_net_share_del(cli, mem_ctx, argv[0]);
+ return W_ERROR_IS_OK(result) ? NT_STATUS_OK : NT_STATUS_UNSUCCESSFUL;
+}
+
+/**
+ * Delete a share on a remote RPC server
+ *
+ * @param domain_sid The domain sid acquired from the remote server
+ * @param argc Standard main() style argc
+ * @param argv Standard main() style argv. Initial components are already
+ * stripped
+ *
+ * @return A shell status integer (0 for success)
+ **/
+static int rpc_share_delete(int argc, const char **argv)
+{
+ if (argc < 1) {
+ DEBUG(1,("Sharename not specified on delete\n"));
+ return rpc_share_usage(argc, argv);
+ }
+ return run_rpc_command(NULL, PI_SRVSVC, 0,
+ rpc_share_del_internals,
+ argc, argv);
+}
+
+/**
+ * Formatted print of share info
+ *
+ * @param info1 pointer to SRV_SHARE_INFO_1 to format
+ **/
+
+static void display_share_info_1(SRV_SHARE_INFO_1 *info1)
+{
+ fstring netname = "", remark = "";
+
+ rpcstr_pull_unistr2_fstring(netname, &info1->info_1_str.uni_netname);
+ rpcstr_pull_unistr2_fstring(remark, &info1->info_1_str.uni_remark);
+
+ if (opt_long_list_entries) {
+ d_printf("%-12s %-8.8s %-50s\n",
+ netname, share_type[info1->info_1.type], remark);
+ } else {
+ d_printf("%s\n", netname);
+ }
+
+}
+
+/**
+ * List shares on a remote RPC server
+ *
+ * All parameters are provided by the run_rpc_command function, except for
+ * argc, argv which are passes through.
+ *
+ * @param domain_sid The domain sid acquired from the remote server
+ * @param cli A cli_state connected to the server.
+ * @param mem_ctx Talloc context, destoyed on completion of the function.
+ * @param argc Standard main() style argc
+ * @param argv Standard main() style argv. Initial components are already
+ * stripped
+ *
+ * @return Normal NTSTATUS return.
+ **/
+
+static NTSTATUS
+rpc_share_list_internals(const DOM_SID *domain_sid, const char *domain_name,
+ struct cli_state *cli,
+ TALLOC_CTX *mem_ctx, int argc, const char **argv)
+{
+ SRV_SHARE_INFO_CTR ctr;
+ WERROR result;
+ ENUM_HND hnd;
+ uint32 preferred_len = 0xffffffff, i;
+
+ init_enum_hnd(&hnd, 0);
+
+ result = cli_srvsvc_net_share_enum(
+ cli, mem_ctx, 1, &ctr, preferred_len, &hnd);
+
+ if (!W_ERROR_IS_OK(result))
+ goto done;
+
+ /* Display results */
+
+ if (opt_long_list_entries) {
+ d_printf(
+ "\nEnumerating shared resources (exports) on remote server:\n\n"\
+ "\nShare name Type Description\n"\
+ "---------- ---- -----------\n");
+ }
+ for (i = 0; i < ctr.num_entries; i++)
+ display_share_info_1(&ctr.share.info1[i]);
+ done:
+ return W_ERROR_IS_OK(result) ? NT_STATUS_OK : NT_STATUS_UNSUCCESSFUL;
+}
+
+/**
+ * 'net rpc share' entrypoint.
+ * @param argc Standard main() style argc
+ * @param argv Standard main() style argv. Initial components are already
+ * stripped
+ **/
+
+int net_rpc_share(int argc, const char **argv)
+{
+ struct functable func[] = {
+ {"add", rpc_share_add},
+ {"delete", rpc_share_delete},
+ {NULL, NULL}
+ };
+
+ if (argc == 0)
+ return run_rpc_command(NULL, PI_SRVSVC, 0,
+ rpc_share_list_internals,
+ argc, argv);
+
+ return net_run_function(argc, argv, func, rpc_share_usage);
+}
+
+/****************************************************************************/
+
+static int rpc_file_usage(int argc, const char **argv)
+{
+ return net_help_file(argc, argv);
+}
+
+/**
+ * Close a file on a remote RPC server
+ *
+ * All parameters are provided by the run_rpc_command function, except for
+ * argc, argv which are passes through.
+ *
+ * @param domain_sid The domain sid acquired from the remote server
+ * @param cli A cli_state connected to the server.
+ * @param mem_ctx Talloc context, destoyed on completion of the function.
+ * @param argc Standard main() style argc
+ * @param argv Standard main() style argv. Initial components are already
+ * stripped
+ *
+ * @return Normal NTSTATUS return.
+ **/
+static NTSTATUS
+rpc_file_close_internals(const DOM_SID *domain_sid, const char *domain_name,
+ struct cli_state *cli,
+ TALLOC_CTX *mem_ctx, int argc, const char **argv)
+{
+ WERROR result;
+ result = cli_srvsvc_net_file_close(cli, mem_ctx, atoi(argv[0]));
+ return W_ERROR_IS_OK(result) ? NT_STATUS_OK : NT_STATUS_UNSUCCESSFUL;
+}
+
+/**
+ * Close a file on a remote RPC server
+ *
+ * @param argc Standard main() style argc
+ * @param argv Standard main() style argv. Initial components are already
+ * stripped
+ *
+ * @return A shell status integer (0 for success)
+ **/
+static int rpc_file_close(int argc, const char **argv)
+{
+ if (argc < 1) {
+ DEBUG(1, ("No fileid given on close\n"));
+ return(rpc_file_usage(argc, argv));
+ }
+
+ return run_rpc_command(NULL, PI_SRVSVC, 0,
+ rpc_file_close_internals,
+ argc, argv);
+}
+
+/**
+ * Formatted print of open file info
+ *
+ * @param info3 FILE_INFO_3 contents
+ * @param str3 strings for FILE_INFO_3
+ **/
+
+static void display_file_info_3(FILE_INFO_3 *info3, FILE_INFO_3_STR *str3)
+{
+ fstring user = "", path = "";
+
+ rpcstr_pull_unistr2_fstring(user, &str3->uni_user_name);
+ rpcstr_pull_unistr2_fstring(path, &str3->uni_path_name);
+
+ d_printf("%-7.1d %-20.20s 0x%-4.2x %-6.1d %s\n",
+ info3->id, user, info3->perms, info3->num_locks, path);
+}
+
+/**
+ * List open files on a remote RPC server
+ *
+ * All parameters are provided by the run_rpc_command function, except for
+ * argc, argv which are passes through.
+ *
+ * @param domain_sid The domain sid acquired from the remote server
+ * @param cli A cli_state connected to the server.
+ * @param mem_ctx Talloc context, destoyed on completion of the function.
+ * @param argc Standard main() style argc
+ * @param argv Standard main() style argv. Initial components are already
+ * stripped
+ *
+ * @return Normal NTSTATUS return.
+ **/
+
+static NTSTATUS
+rpc_file_list_internals(const DOM_SID *domain_sid, const char *domain_name,
+ struct cli_state *cli,
+ TALLOC_CTX *mem_ctx, int argc, const char **argv)
+{
+ SRV_FILE_INFO_CTR ctr;
+ WERROR result;
+ ENUM_HND hnd;
+ uint32 preferred_len = 0xffffffff, i;
+ const char *username=NULL;
+
+ init_enum_hnd(&hnd, 0);
+
+ /* if argc > 0, must be user command */
+ if (argc > 0)
+ username = smb_xstrdup(argv[0]);
+
+ result = cli_srvsvc_net_file_enum(
+ cli, mem_ctx, 3, username, &ctr, preferred_len, &hnd);
+
+ if (!W_ERROR_IS_OK(result))
+ goto done;
+
+ /* Display results */
+
+ d_printf(
+ "\nEnumerating open files on remote server:\n\n"\
+ "\nFileId Opened by Perms Locks Path"\
+ "\n------ --------- ----- ----- ---- \n");
+ for (i = 0; i < ctr.num_entries; i++)
+ display_file_info_3(&ctr.file.info3[i].info_3,
+ &ctr.file.info3[i].info_3_str);
+ done:
+ return W_ERROR_IS_OK(result) ? NT_STATUS_OK : NT_STATUS_UNSUCCESSFUL;
+}
+
+
+/**
+ * List files for a user on a remote RPC server
+ *
+ * @param argc Standard main() style argc
+ * @param argv Standard main() style argv. Initial components are already
+ * stripped
+ *
+ * @return A shell status integer (0 for success)
+ **/
+static int rpc_file_user(int argc, const char **argv)
+{
+ if (argc < 1) {
+ DEBUG(1, ("No username given\n"));
+ return(rpc_file_usage(argc, argv));
+ }
+
+ return run_rpc_command(NULL, PI_SRVSVC, 0,
+ rpc_file_list_internals,
+ argc, argv);
+}
+
+
+/**
+ * 'net rpc file' entrypoint.
+ * @param argc Standard main() style argc
+ * @param argv Standard main() style argv. Initial components are already
+ * stripped
+ **/
+
+int net_rpc_file(int argc, const char **argv)
+{
+ struct functable func[] = {
+ {"close", rpc_file_close},
+ {"user", rpc_file_user},
+#if 0
+ {"info", rpc_file_info},
+#endif
+ {NULL, NULL}
+ };
+
+ if (argc == 0)
+ return run_rpc_command(NULL, PI_SRVSVC, 0,
+ rpc_file_list_internals,
+ argc, argv);
+
+ return net_run_function(argc, argv, func, rpc_file_usage);
+}
+
+/****************************************************************************/
+
+
+
+/**
+ * ABORT the shutdown of a remote RPC Server over, initshutdown pipe
+ *
+ * All parameters are provided by the run_rpc_command function, except for
+ * argc, argv which are passed through.
+ *
+ * @param domain_sid The domain sid aquired from the remote server
+ * @param cli A cli_state connected to the server.
+ * @param mem_ctx Talloc context, destoyed on compleation of the function.
+ * @param argc Standard main() style argc
+ * @param argv Standard main() style argv. Initial components are already
+ * stripped
+ *
+ * @return Normal NTSTATUS return.
+ **/
+
+static NTSTATUS rpc_shutdown_abort_internals(const DOM_SID *domain_sid,
+ const char *domain_name,
+ struct cli_state *cli,
+ TALLOC_CTX *mem_ctx,
+ int argc, const char **argv)
+{
+ NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+
+ result = cli_shutdown_abort(cli, mem_ctx);
+
+ if (NT_STATUS_IS_OK(result))
+ DEBUG(5,("cmd_shutdown_abort: query succeeded\n"));
+ else
+ DEBUG(5,("cmd_shutdown_abort: query failed\n"));
+
+ return result;
+}
+
+
+/**
+ * ABORT the shutdown of a remote RPC Server, over winreg pipe
+ *
+ * All parameters are provided by the run_rpc_command function, except for
+ * argc, argv which are passed through.
+ *
+ * @param domain_sid The domain sid aquired from the remote server
+ * @param cli A cli_state connected to the server.
+ * @param mem_ctx Talloc context, destoyed on compleation of the function.
+ * @param argc Standard main() style argc
+ * @param argv Standard main() style argv. Initial components are already
+ * stripped
+ *
+ * @return Normal NTSTATUS return.
+ **/
+
+static NTSTATUS rpc_reg_shutdown_abort_internals(const DOM_SID *domain_sid,
+ const char *domain_name,
+ struct cli_state *cli,
+ TALLOC_CTX *mem_ctx,
+ int argc, const char **argv)
+{
+ NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+
+ result = cli_reg_abort_shutdown(cli, mem_ctx);
+
+ if (NT_STATUS_IS_OK(result))
+ DEBUG(5,("cmd_reg_abort_shutdown: query succeeded\n"));
+ else
+ DEBUG(5,("cmd_reg_abort_shutdown: query failed\n"));
+
+ return result;
+}
+
+/**
+ * ABORT the Shut down of a remote RPC server
+ *
+ * @param argc Standard main() style argc
+ * @param argv Standard main() style argv. Initial components are already
+ * stripped
+ *
+ * @return A shell status integer (0 for success)
+ **/
+
+static int rpc_shutdown_abort(int argc, const char **argv)
+{
+ int rc = run_rpc_command(NULL, PI_SHUTDOWN, 0,
+ rpc_shutdown_abort_internals,
+ argc, argv);
+
+ if (rc == 0)
+ return rc;
+
+ DEBUG(1, ("initshutdown pipe didn't work, trying winreg pipe\n"));
+
+ return run_rpc_command(NULL, PI_WINREG, 0,
+ rpc_reg_shutdown_abort_internals,
+ argc, argv);
+}
+
+/**
+ * Shut down a remote RPC Server
+ *
+ * All parameters are provided by the run_rpc_command function, except for
+ * argc, argv which are passes through.
+ *
+ * @param domain_sid The domain sid aquired from the remote server
+ * @param cli A cli_state connected to the server.
+ * @param mem_ctx Talloc context, destoyed on compleation of the function.
+ * @param argc Standard main() style argc
+ * @param argc Standard main() style argv. Initial components are already
+ * stripped
+ *
+ * @return Normal NTSTATUS return.
+ **/
+
+static NTSTATUS rpc_shutdown_internals(const DOM_SID *domain_sid,
+ const char *domain_name,
+ struct cli_state *cli, TALLOC_CTX *mem_ctx,
+ int argc, const char **argv)
+{
+ NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+ const char *msg = "This machine will be shutdown shortly";
+ uint32 timeout = 20;
+#if 0
+ poptContext pc;
+ int rc;
+
+ struct poptOption long_options[] = {
+ {"message", 'm', POPT_ARG_STRING, &msg},
+ {"timeout", 't', POPT_ARG_INT, &timeout},
+ {"reboot", 'r', POPT_ARG_NONE, &reboot},
+ {"force", 'f', POPT_ARG_NONE, &force},
+ { 0, 0, 0, 0}
+ };
+
+ pc = poptGetContext(NULL, argc, (const char **) argv, long_options,
+ POPT_CONTEXT_KEEP_FIRST);
+
+ rc = poptGetNextOpt(pc);
+
+ if (rc < -1) {
+ /* an error occurred during option processing */
+ DEBUG(0, ("%s: %s\n",
+ poptBadOption(pc, POPT_BADOPTION_NOALIAS),
+ poptStrerror(rc)));
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+#endif
+ if (opt_comment) {
+ msg = opt_comment;
+ }
+ if (opt_timeout) {
+ timeout = opt_timeout;
+ }
+
+ /* create an entry */
+ result = cli_reg_shutdown(cli, mem_ctx, msg, timeout, opt_reboot, opt_force);
+
+ if (NT_STATUS_IS_OK(result))
+ DEBUG(5,("Shutdown of remote machine succeeded\n"));
+ else
+ DEBUG(0,("Shutdown of remote machine failed!\n"));
+
+ return result;
+}
+
+/**
+ * Shut down a remote RPC server
+ *
+ * @param argc Standard main() style argc
+ * @param argc Standard main() style argv. Initial components are already
+ * stripped
+ *
+ * @return A shell status integer (0 for success)
+ **/
+
+static int rpc_shutdown(int argc, const char **argv)
+{
+ return run_rpc_command(NULL, PI_WINREG, 0, rpc_shutdown_internals,
+ argc, argv);
+}
+
+/***************************************************************************
+ NT Domain trusts code (i.e. 'net rpc trustdom' functionality)
+
+ ***************************************************************************/
+
+/**
+ * Add interdomain trust account to the RPC server.
+ * All parameters (except for argc and argv) are passed by run_rpc_command
+ * function.
+ *
+ * @param domain_sid The domain sid acquired from the server
+ * @param cli A cli_state connected to the server.
+ * @param mem_ctx Talloc context, destoyed on completion of the function.
+ * @param argc Standard main() style argc
+ * @param argc Standard main() style argv. Initial components are already
+ * stripped
+ *
+ * @return normal NTSTATUS return code
+ */
+
+static NTSTATUS rpc_trustdom_add_internals(const DOM_SID *domain_sid,
+ const char *domain_name,
+ struct cli_state *cli, TALLOC_CTX *mem_ctx,
+ int argc, const char **argv) {
+
+ POLICY_HND connect_pol, domain_pol, user_pol;
+ NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+ char *acct_name;
+ uint16 acb_info;
+ uint32 unknown, user_rid;
+
+ if (argc != 2) {
+ d_printf("Usage: net rpc trustdom add <domain_name> <pw>\n");
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ /*
+ * Make valid trusting domain account (ie. uppercased and with '$' appended)
+ */
+
+ if (asprintf(&acct_name, "%s$", argv[0]) < 0) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ strupper_m(acct_name);
+
+ /* Get samr policy handle */
+ result = cli_samr_connect(cli, mem_ctx, MAXIMUM_ALLOWED_ACCESS,
+ &connect_pol);
+ if (!NT_STATUS_IS_OK(result)) {
+ goto done;
+ }
+
+ /* Get domain policy handle */
+ result = cli_samr_open_domain(cli, mem_ctx, &connect_pol,
+ MAXIMUM_ALLOWED_ACCESS,
+ domain_sid, &domain_pol);
+ if (!NT_STATUS_IS_OK(result)) {
+ goto done;
+ }
+
+ /* Create trusting domain's account */
+ acb_info = ACB_DOMTRUST;
+ unknown = 0xe00500b0; /* No idea what this is - a permission mask?
+ mimir: yes, most probably it is */
+
+ result = cli_samr_create_dom_user(cli, mem_ctx, &domain_pol,
+ acct_name, acb_info, unknown,
+ &user_pol, &user_rid);
+ if (!NT_STATUS_IS_OK(result)) {
+ goto done;
+ }
+
+ {
+ SAM_USERINFO_CTR ctr;
+ SAM_USER_INFO_24 p24;
+ uchar pwbuf[516];
+
+ encode_pw_buffer((char *)pwbuf, argv[1], STR_UNICODE);
+
+ ZERO_STRUCT(ctr);
+ ZERO_STRUCT(p24);
+
+ init_sam_user_info24(&p24, (char *)pwbuf, 24);
+
+ ctr.switch_value = 24;
+ ctr.info.id24 = &p24;
+
+ result = cli_samr_set_userinfo(cli, mem_ctx, &user_pol, 24,
+ &cli->user_session_key, &ctr);
+
+ if (!NT_STATUS_IS_OK(result)) {
+ DEBUG(0,("Could not set trust account password: %s\n",
+ nt_errstr(result)));
+ goto done;
+ }
+ }
+
+ done:
+ SAFE_FREE(acct_name);
+ return result;
+}
+
+/**
+ * Create interdomain trust account for a remote domain.
+ *
+ * @param argc standard argc
+ * @param argv standard argv without initial components
+ *
+ * @return Integer status (0 means success)
+ **/
+
+static int rpc_trustdom_add(int argc, const char **argv)
+{
+ if (argc > 0) {
+ return run_rpc_command(NULL, PI_SAMR, 0, rpc_trustdom_add_internals,
+ argc, argv);
+ } else {
+ d_printf("Usage: net rpc trustdom add <domain>\n");
+ return -1;
+ }
+}
+
+
+/**
+ * Delete interdomain trust account for a remote domain.
+ *
+ * @param argc standard argc
+ * @param argv standard argv without initial components
+ *
+ * @return Integer status (0 means success)
+ **/
+
+static int rpc_trustdom_del(int argc, const char **argv)
+{
+ d_printf("Sorry, not yet implemented.\n");
+ d_printf("Use 'smbpasswd -x -i' instead.\n");
+ return -1;
+}
+
+
+/**
+ * Establish trust relationship to a trusting domain.
+ * Interdomain account must already be created on remote PDC.
+ *
+ * @param argc standard argc
+ * @param argv standard argv without initial components
+ *
+ * @return Integer status (0 means success)
+ **/
+
+static int rpc_trustdom_establish(int argc, const char **argv)
+{
+ struct cli_state *cli;
+ struct in_addr server_ip;
+ POLICY_HND connect_hnd;
+ TALLOC_CTX *mem_ctx;
+ NTSTATUS nt_status;
+ DOM_SID *domain_sid;
+ WKS_INFO_100 wks_info;
+
+ char* domain_name;
+ char* domain_name_pol;
+ char* acct_name;
+ fstring pdc_name;
+
+ /*
+ * Connect to \\server\ipc$ as 'our domain' account with password
+ */
+
+ if (argc != 1) {
+ d_printf("Usage: net rpc trustdom establish <domain_name>\n");
+ return -1;
+ }
+
+ domain_name = smb_xstrdup(argv[0]);
+ strupper_m(domain_name);
+
+ /* account name used at first is our domain's name with '$' */
+ asprintf(&acct_name, "%s$", lp_workgroup());
+ strupper_m(acct_name);
+
+ /*
+ * opt_workgroup will be used by connection functions further,
+ * hence it should be set to remote domain name instead of ours
+ */
+ if (opt_workgroup) {
+ opt_workgroup = smb_xstrdup(domain_name);
+ };
+
+ opt_user_name = acct_name;
+
+ /* find the domain controller */
+ if (!net_find_pdc(&server_ip, pdc_name, domain_name)) {
+ DEBUG(0, ("Couldn't find domain controller for domain %s\n", domain_name));
+ return -1;
+ }
+
+ /* connect to ipc$ as username/password */
+ nt_status = connect_to_ipc(&cli, &server_ip, pdc_name);
+ if (!NT_STATUS_EQUAL(nt_status, NT_STATUS_NOLOGON_INTERDOMAIN_TRUST_ACCOUNT)) {
+
+ /* Is it trusting domain account for sure ? */
+ DEBUG(0, ("Couldn't verify trusting domain account. Error was %s\n",
+ nt_errstr(nt_status)));
+ return -1;
+ }
+
+ /*
+ * Connect to \\server\ipc$ again (this time anonymously)
+ */
+
+ nt_status = connect_to_ipc_anonymous(&cli, &server_ip, (char*)pdc_name);
+
+ if (NT_STATUS_IS_ERR(nt_status)) {
+ DEBUG(0, ("Couldn't connect to domain %s controller. Error was %s.\n",
+ domain_name, nt_errstr(nt_status)));
+ }
+
+ /*
+ * Use NetServerEnum2 to make sure we're talking to a proper server
+ */
+
+ if (!cli_get_pdc_name(cli, domain_name, (char*)pdc_name)) {
+ DEBUG(0, ("NetServerEnum2 error: Couldn't find primary domain controller\
+ for domain %s\n", domain_name));
+ }
+
+ /*
+ * Call WksQueryInfo to check remote server's capabilities
+ * note: It is now used only to get unicode domain name
+ */
+
+ if (!cli_nt_session_open(cli, PI_WKSSVC)) {
+ DEBUG(0, ("Couldn't not initialise wkssvc pipe\n"));
+ return -1;
+ }
+
+ if (!(mem_ctx = talloc_init("establishing trust relationship to domain %s",
+ domain_name))) {
+ DEBUG(0, ("talloc_init() failed\n"));
+ cli_shutdown(cli);
+ return -1;
+ }
+
+ nt_status = cli_wks_query_info(cli, mem_ctx, &wks_info);
+
+ if (NT_STATUS_IS_ERR(nt_status)) {
+ DEBUG(0, ("WksQueryInfo call failed.\n"));
+ return -1;
+ }
+
+ if (cli->nt_pipe_fnum)
+ cli_nt_session_close(cli);
+
+
+ /*
+ * Call LsaOpenPolicy and LsaQueryInfo
+ */
+
+ if (!(mem_ctx = talloc_init("rpc_trustdom_establish"))) {
+ DEBUG(0, ("talloc_init() failed\n"));
+ cli_shutdown(cli);
+ return -1;
+ }
+
+ if (!cli_nt_session_open(cli, PI_LSARPC)) {
+ DEBUG(0, ("Could not initialise lsa pipe\n"));
+ cli_shutdown(cli);
+ return -1;
+ }
+
+ nt_status = cli_lsa_open_policy2(cli, mem_ctx, True, SEC_RIGHTS_QUERY_VALUE,
+ &connect_hnd);
+ if (NT_STATUS_IS_ERR(nt_status)) {
+ DEBUG(0, ("Couldn't open policy handle. Error was %s\n",
+ nt_errstr(nt_status)));
+ return -1;
+ }
+
+ /* Querying info level 5 */
+
+ nt_status = cli_lsa_query_info_policy(cli, mem_ctx, &connect_hnd,
+ 5 /* info level */, &domain_name_pol,
+ &domain_sid);
+ if (NT_STATUS_IS_ERR(nt_status)) {
+ DEBUG(0, ("LSA Query Info failed. Returned error was %s\n",
+ nt_errstr(nt_status)));
+ return -1;
+ }
+
+
+
+
+ /* There should be actually query info level 3 (following nt serv behaviour),
+ but I still don't know if it's _really_ necessary */
+
+ /*
+ * Store the password in secrets db
+ */
+
+ if (!secrets_store_trusted_domain_password(domain_name, wks_info.uni_lan_grp.buffer,
+ wks_info.uni_lan_grp.uni_str_len, opt_password,
+ *domain_sid)) {
+ DEBUG(0, ("Storing password for trusted domain failed.\n"));
+ return -1;
+ }
+
+ /*
+ * Close the pipes and clean up
+ */
+
+ nt_status = cli_lsa_close(cli, mem_ctx, &connect_hnd);
+ if (NT_STATUS_IS_ERR(nt_status)) {
+ DEBUG(0, ("Couldn't close LSA pipe. Error was %s\n",
+ nt_errstr(nt_status)));
+ return -1;
+ }
+
+ if (cli->nt_pipe_fnum)
+ cli_nt_session_close(cli);
+
+ talloc_destroy(mem_ctx);
+
+ d_printf("Trust to domain %s established\n", domain_name);
+ return 0;
+}
+
+/**
+ * Revoke trust relationship to the remote domain
+ *
+ * @param argc standard argc
+ * @param argv standard argv without initial components
+ *
+ * @return Integer status (0 means success)
+ **/
+
+static int rpc_trustdom_revoke(int argc, const char **argv)
+{
+ char* domain_name;
+
+ if (argc < 1) return -1;
+
+ /* generate upper cased domain name */
+ domain_name = smb_xstrdup(argv[0]);
+ strupper_m(domain_name);
+
+ /* delete password of the trust */
+ if (!trusted_domain_password_delete(domain_name)) {
+ DEBUG(0, ("Failed to revoke relationship to the trusted domain %s\n",
+ domain_name));
+ return -1;
+ };
+
+ return 0;
+}
+
+/**
+ * Usage for 'net rpc trustdom' command
+ *
+ * @param argc standard argc
+ * @param argv standard argv without inital components
+ *
+ * @return Integer status returned to shell
+ **/
+
+static int rpc_trustdom_usage(int argc, const char **argv)
+{
+ d_printf(" net rpc trustdom add \t\t add trusting domain's account\n");
+ d_printf(" net rpc trustdom del \t\t delete trusting domain's account\n");
+ d_printf(" net rpc trustdom establish \t establish relationship to trusted domain\n");
+ d_printf(" net rpc trustdom revoke \t abandon relationship to trusted domain\n");
+ d_printf(" net rpc trustdom list \t show current interdomain trust relationships\n");
+ return -1;
+}
+
+
+static NTSTATUS rpc_query_domain_sid(const DOM_SID *domain_sid,
+ const char *domain_name,
+ struct cli_state *cli, TALLOC_CTX *mem_ctx,
+ int argc, const char **argv)
+{
+ fstring str_sid;
+ sid_to_string(str_sid, domain_sid);
+ d_printf("%s\n", str_sid);
+ return NT_STATUS_OK;
+}
+
+
+static int rpc_trustdom_list(int argc, const char **argv)
+{
+ /* common variables */
+ TALLOC_CTX* mem_ctx;
+ struct cli_state *cli, *remote_cli;
+ NTSTATUS nt_status;
+ const char *domain_name = NULL;
+ DOM_SID *queried_dom_sid;
+ fstring ascii_sid, padding;
+ int ascii_dom_name_len;
+ POLICY_HND connect_hnd;
+
+ /* trusted domains listing variables */
+ unsigned int num_domains, enum_ctx = 0;
+ int i, pad_len, col_len = 20;
+ DOM_SID *domain_sids;
+ char **trusted_dom_names;
+ fstring pdc_name;
+ char *dummy;
+
+ /* trusting domains listing variables */
+ POLICY_HND domain_hnd;
+ char **trusting_dom_names;
+ uint32 *trusting_dom_rids;
+
+ /*
+ * Listing trusted domains (stored in secrets.tdb, if local)
+ */
+
+ mem_ctx = talloc_init("trust relationships listing");
+
+ /*
+ * set domain and pdc name to local samba server (default)
+ * or to remote one given in command line
+ */
+
+ if (StrCaseCmp(opt_workgroup, lp_workgroup())) {
+ domain_name = opt_workgroup;
+ opt_target_workgroup = opt_workgroup;
+ } else {
+ fstrcpy(pdc_name, global_myname());
+ domain_name = talloc_strdup(mem_ctx, lp_workgroup());
+ opt_target_workgroup = domain_name;
+ };
+
+ /* open \PIPE\lsarpc and open policy handle */
+ if (!(cli = net_make_ipc_connection(NET_FLAGS_PDC))) {
+ DEBUG(0, ("Couldn't connect to domain controller\n"));
+ return -1;
+ };
+
+ if (!cli_nt_session_open(cli, PI_LSARPC)) {
+ DEBUG(0, ("Could not initialise lsa pipe\n"));
+ return -1;
+ };
+
+ nt_status = cli_lsa_open_policy2(cli, mem_ctx, False, SEC_RIGHTS_QUERY_VALUE,
+ &connect_hnd);
+ if (NT_STATUS_IS_ERR(nt_status)) {
+ DEBUG(0, ("Couldn't open policy handle. Error was %s\n",
+ nt_errstr(nt_status)));
+ return -1;
+ };
+
+ /* query info level 5 to obtain sid of a domain being queried */
+ nt_status = cli_lsa_query_info_policy(
+ cli, mem_ctx, &connect_hnd, 5 /* info level */,
+ &dummy, &queried_dom_sid);
+
+ if (NT_STATUS_IS_ERR(nt_status)) {
+ DEBUG(0, ("LSA Query Info failed. Returned error was %s\n",
+ nt_errstr(nt_status)));
+ return -1;
+ }
+
+ /*
+ * Keep calling LsaEnumTrustdom over opened pipe until
+ * the end of enumeration is reached
+ */
+
+ d_printf("Trusted domains list:\n\n");
+
+ do {
+ nt_status = cli_lsa_enum_trust_dom(cli, mem_ctx, &connect_hnd, &enum_ctx,
+ &num_domains,
+ &trusted_dom_names, &domain_sids);
+
+ if (NT_STATUS_IS_ERR(nt_status)) {
+ DEBUG(0, ("Couldn't enumerate trusted domains. Error was %s\n",
+ nt_errstr(nt_status)));
+ return -1;
+ };
+
+ for (i = 0; i < num_domains; i++) {
+ /* convert sid into ascii string */
+ sid_to_string(ascii_sid, &(domain_sids[i]));
+
+ /* calculate padding space for d_printf to look nicer */
+ pad_len = col_len - strlen(trusted_dom_names[i]);
+ padding[pad_len] = 0;
+ do padding[--pad_len] = ' '; while (pad_len);
+
+ d_printf("%s%s%s\n", trusted_dom_names[i], padding, ascii_sid);
+ };
+
+ /*
+ * in case of no trusted domains say something rather
+ * than just display blank line
+ */
+ if (!num_domains) d_printf("none\n");
+
+ } while (NT_STATUS_EQUAL(nt_status, STATUS_MORE_ENTRIES));
+
+ /* close this connection before doing next one */
+ nt_status = cli_lsa_close(cli, mem_ctx, &connect_hnd);
+ if (NT_STATUS_IS_ERR(nt_status)) {
+ DEBUG(0, ("Couldn't properly close lsa policy handle. Error was %s\n",
+ nt_errstr(nt_status)));
+ return -1;
+ };
+
+ cli_nt_session_close(cli);
+
+ /*
+ * Listing trusting domains (stored in passdb backend, if local)
+ */
+
+ d_printf("\nTrusting domains list:\n\n");
+
+ /*
+ * Open \PIPE\samr and get needed policy handles
+ */
+ if (!cli_nt_session_open(cli, PI_SAMR)) {
+ DEBUG(0, ("Could not initialise samr pipe\n"));
+ return -1;
+ };
+
+ /* SamrConnect */
+ nt_status = cli_samr_connect(cli, mem_ctx, SA_RIGHT_SAM_OPEN_DOMAIN,
+ &connect_hnd);
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ DEBUG(0, ("Couldn't open SAMR policy handle. Error was %s\n",
+ nt_errstr(nt_status)));
+ return -1;
+ };
+
+ /* SamrOpenDomain - we have to open domain policy handle in order to be
+ able to enumerate accounts*/
+ nt_status = cli_samr_open_domain(cli, mem_ctx, &connect_hnd,
+ SA_RIGHT_DOMAIN_ENUM_ACCOUNTS,
+ queried_dom_sid, &domain_hnd);
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ DEBUG(0, ("Couldn't open domain object. Error was %s\n",
+ nt_errstr(nt_status)));
+ return -1;
+ };
+
+ /*
+ * perform actual enumeration
+ */
+
+ enum_ctx = 0; /* reset enumeration context from last enumeration */
+ do {
+
+ nt_status = cli_samr_enum_dom_users(cli, mem_ctx, &domain_hnd,
+ &enum_ctx, ACB_DOMTRUST, 0xffff,
+ &trusting_dom_names, &trusting_dom_rids,
+ &num_domains);
+ if (NT_STATUS_IS_ERR(nt_status)) {
+ DEBUG(0, ("Couldn't enumerate accounts. Error was: %s\n",
+ nt_errstr(nt_status)));
+ return -1;
+ };
+
+ for (i = 0; i < num_domains; i++) {
+
+ /*
+ * get each single domain's sid (do we _really_ need this ?):
+ * 1) connect to domain's pdc
+ * 2) query the pdc for domain's sid
+ */
+
+ /* get rid of '$' tail */
+ ascii_dom_name_len = strlen(trusting_dom_names[i]);
+ if (ascii_dom_name_len && ascii_dom_name_len < FSTRING_LEN)
+ trusting_dom_names[i][ascii_dom_name_len - 1] = '\0';
+
+ /* calculate padding space for d_printf to look nicer */
+ pad_len = col_len - strlen(trusting_dom_names[i]);
+ padding[pad_len] = 0;
+ do padding[--pad_len] = ' '; while (pad_len);
+
+ /* set opt_* variables to remote domain */
+ strupper_m(trusting_dom_names[i]);
+ opt_workgroup = talloc_strdup(mem_ctx, trusting_dom_names[i]);
+ opt_target_workgroup = opt_workgroup;
+
+ d_printf("%s%s", trusting_dom_names[i], padding);
+
+ /* connect to remote domain controller */
+ remote_cli = net_make_ipc_connection(NET_FLAGS_PDC | NET_FLAGS_ANONYMOUS);
+ if (remote_cli) {
+ /* query for domain's sid */
+ if (run_rpc_command(remote_cli, PI_LSARPC, 0, rpc_query_domain_sid, argc, argv))
+ d_printf("couldn't get domain's sid\n");
+
+ cli_shutdown(remote_cli);
+
+ } else {
+ d_printf("domain controller is not responding\n");
+ };
+ };
+
+ if (!num_domains) d_printf("none\n");
+
+ } while (NT_STATUS_EQUAL(nt_status, STATUS_MORE_ENTRIES));
+
+ /* close opened samr and domain policy handles */
+ nt_status = cli_samr_close(cli, mem_ctx, &domain_hnd);
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ DEBUG(0, ("Couldn't properly close domain policy handle for domain %s\n", domain_name));
+ };
+
+ nt_status = cli_samr_close(cli, mem_ctx, &connect_hnd);
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ DEBUG(0, ("Couldn't properly close samr policy handle for domain %s\n", domain_name));
+ };
+
+ /* close samr pipe and connection to IPC$ */
+ cli_nt_session_close(cli);
+ cli_shutdown(cli);
+
+ talloc_destroy(mem_ctx);
+ return 0;
+}
+
+/**
+ * Entrypoint for 'net rpc trustdom' code
+ *
+ * @param argc standard argc
+ * @param argv standard argv without initial components
+ *
+ * @return Integer status (0 means success)
+ */
+
+static int rpc_trustdom(int argc, const char **argv)
+{
+ struct functable func[] = {
+ {"add", rpc_trustdom_add},
+ {"del", rpc_trustdom_del},
+ {"establish", rpc_trustdom_establish},
+ {"revoke", rpc_trustdom_revoke},
+ {"help", rpc_trustdom_usage},
+ {"list", rpc_trustdom_list},
+ {NULL, NULL}
+ };
+
+ if (argc == 0) {
+ rpc_trustdom_usage(argc, argv);
+ return -1;
+ }
+
+ return (net_run_function(argc, argv, func, rpc_user_usage));
+}
+
+/**
+ * Check if a server will take rpc commands
+ * @param flags Type of server to connect to (PDC, DMB, localhost)
+ * if the host is not explicitly specified
+ * @return BOOL (true means rpc supported)
+ */
+BOOL net_rpc_check(unsigned flags)
+{
+ struct cli_state cli;
+ BOOL ret = False;
+ struct in_addr server_ip;
+ char *server_name = NULL;
+
+ /* flags (i.e. server type) may depend on command */
+ if (!net_find_server(flags, &server_ip, &server_name))
+ return False;
+
+ ZERO_STRUCT(cli);
+ if (cli_initialise(&cli) == False)
+ return False;
+
+ if (!cli_connect(&cli, server_name, &server_ip))
+ goto done;
+ if (!attempt_netbios_session_request(&cli, global_myname(),
+ server_name, &server_ip))
+ goto done;
+ if (!cli_negprot(&cli))
+ goto done;
+ if (cli.protocol < PROTOCOL_NT1)
+ goto done;
+
+ ret = True;
+ done:
+ cli_shutdown(&cli);
+ return ret;
+}
+
+/* dump sam database via samsync rpc calls */
+static int rpc_samdump(int argc, const char **argv) {
+ return run_rpc_command(NULL, PI_NETLOGON, NET_FLAGS_ANONYMOUS, rpc_samdump_internals,
+ argc, argv);
+}
+
+/* syncronise sam database via samsync rpc calls */
+static int rpc_vampire(int argc, const char **argv) {
+ return run_rpc_command(NULL, PI_NETLOGON, NET_FLAGS_ANONYMOUS, rpc_vampire_internals,
+ argc, argv);
+}
+/****************************************************************************/
+
+
+/**
+ * Basic usage function for 'net rpc'
+ * @param argc Standard main() style argc
+ * @param argv Standard main() style argv. Initial components are already
+ * stripped
+ **/
+
+int net_rpc_usage(int argc, const char **argv)
+{
+ d_printf(" net rpc info \t\t\tshow basic info about a domain \n");
+ d_printf(" net rpc join \t\t\tto join a domain \n");
+ d_printf(" net rpc oldjoin \t\t\tto join a domain created in server manager\n\n\n");
+ d_printf(" net rpc testjoin \t\ttests that a join is valid\n");
+ d_printf(" net rpc user \t\t\tto add, delete and list users\n");
+ d_printf(" net rpc password <username> [<password>] -Uadmin_username%%admin_pass");
+ d_printf(" net rpc group \t\tto list groups\n");
+ d_printf(" net rpc share \t\tto add, delete, and list shares\n");
+ d_printf(" net rpc file \t\t\tto list open files\n");
+ d_printf(" net rpc changetrustpw \tto change the trust account password\n");
+ d_printf(" net rpc getsid \t\tfetch the domain sid into the local secrets.tdb\n");
+ d_printf(" net rpc vampire \t\tsyncronise an NT PDC's users and groups into the local passdb\n");
+ d_printf(" net rpc samdump \t\tdiplay an NT PDC's users, groups and other data\n");
+ d_printf(" net rpc trustdom \t\tto create trusting domain's account\n"
+ "\t\t\t\t\tor establish trust\n");
+ d_printf(" net rpc abortshutdown \tto abort the shutdown of a remote server\n");
+ d_printf(" net rpc shutdown \t\tto shutdown a remote server\n");
+ d_printf("\n");
+ d_printf("'net rpc shutdown' also accepts the following miscellaneous options:\n"); /* misc options */
+ d_printf("\t-r or --reboot\trequest remote server reboot on shutdown\n");
+ d_printf("\t-f or --force\trequest the remote server force its shutdown\n");
+ d_printf("\t-t or --timeout=<timeout>\tnumber of seconds before shutdown\n");
+ d_printf("\t-c or --comment=<message>\ttext message to display on impending shutdown\n");
+ return -1;
+}
+
+
+/**
+ * Help function for 'net rpc'. Calls command specific help if requested
+ * or displays usage of net rpc
+ * @param argc Standard main() style argc
+ * @param argv Standard main() style argv. Initial components are already
+ * stripped
+ **/
+
+int net_rpc_help(int argc, const char **argv)
+{
+ struct functable func[] = {
+ {"join", rpc_join_usage},
+ {"user", rpc_user_usage},
+ {"group", rpc_group_usage},
+ {"share", rpc_share_usage},
+ /*{"changetrustpw", rpc_changetrustpw_usage}, */
+ {"trustdom", rpc_trustdom_usage},
+ /*{"abortshutdown", rpc_shutdown_abort_usage},*/
+ /*{"shutdown", rpc_shutdown_usage}, */
+ {NULL, NULL}
+ };
+
+ if (argc == 0) {
+ net_rpc_usage(argc, argv);
+ return -1;
+ }
+
+ return (net_run_function(argc, argv, func, rpc_user_usage));
+}
+
+
+/**
+ * 'net rpc' entrypoint.
+ * @param argc Standard main() style argc
+ * @param argv Standard main() style argv. Initial components are already
+ * stripped
+ **/
+
+int net_rpc(int argc, const char **argv)
+{
+ struct functable func[] = {
+ {"info", net_rpc_info},
+ {"join", net_rpc_join},
+ {"oldjoin", net_rpc_oldjoin},
+ {"testjoin", net_rpc_testjoin},
+ {"user", net_rpc_user},
+ {"password", rpc_user_password},
+ {"group", net_rpc_group},
+ {"share", net_rpc_share},
+ {"file", net_rpc_file},
+ {"changetrustpw", net_rpc_changetrustpw},
+ {"trustdom", rpc_trustdom},
+ {"abortshutdown", rpc_shutdown_abort},
+ {"shutdown", rpc_shutdown},
+ {"samdump", rpc_samdump},
+ {"vampire", rpc_vampire},
+ {"getsid", net_rpc_getsid},
+ {"help", net_rpc_help},
+ {NULL, NULL}
+ };
+ return net_run_function(argc, argv, func, net_rpc_usage);
+}
diff --git a/source/utils/net_rpc_join.c b/source/utils/net_rpc_join.c
new file mode 100644
index 00000000000..52e295949e0
--- /dev/null
+++ b/source/utils/net_rpc_join.c
@@ -0,0 +1,389 @@
+/*
+ Samba Unix/Linux SMB client library
+ Distributed SMB/CIFS Server Management Utility
+ Copyright (C) 2001 Andrew Bartlett (abartlet@samba.org)
+ Copyright (C) Tim Potter 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 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 "../utils/net.h"
+
+/* Macro for checking RPC error codes to make things more readable */
+
+#define CHECK_RPC_ERR(rpc, msg) \
+ if (!NT_STATUS_IS_OK(result = rpc)) { \
+ DEBUG(0, (msg ": %s\n", nt_errstr(result))); \
+ goto done; \
+ }
+
+#define CHECK_RPC_ERR_DEBUG(rpc, debug_args) \
+ if (!NT_STATUS_IS_OK(result = rpc)) { \
+ DEBUG(0, debug_args); \
+ goto done; \
+ }
+
+
+/**
+ * confirm that a domain join is still valid
+ *
+ * @return A shell status integer (0 for success)
+ *
+ **/
+static int net_rpc_join_ok(const char *domain)
+{
+ struct cli_state *cli;
+ uchar stored_md4_trust_password[16];
+ int retval = 1;
+ uint32 channel;
+ NTSTATUS result;
+
+ /* Connect to remote machine */
+ if (!(cli = net_make_ipc_connection(NET_FLAGS_ANONYMOUS | NET_FLAGS_PDC))) {
+ return 1;
+ }
+
+ if (!cli_nt_session_open(cli, PI_NETLOGON)) {
+ DEBUG(0,("Error connecting to NETLOGON pipe\n"));
+ goto done;
+ }
+
+ if (!secrets_fetch_trust_account_password(domain,
+ stored_md4_trust_password,
+ NULL, &channel)) {
+ DEBUG(0,("Could not retreive domain trust secret"));
+ goto done;
+ }
+
+ /* ensure that schannel uses the right domain */
+ fstrcpy(cli->domain, domain);
+ if (! NT_STATUS_IS_OK(result = cli_nt_establish_netlogon(cli, channel, stored_md4_trust_password))) {
+ DEBUG(0,("Error in domain join verfication (fresh connection)\n"));
+ goto done;
+ }
+
+ retval = 0; /* Success! */
+
+done:
+ /* Close down pipe - this will clean up open policy handles */
+ if (cli->nt_pipe_fnum)
+ cli_nt_session_close(cli);
+
+ cli_shutdown(cli);
+
+ return retval;
+}
+
+/**
+ * Join a domain using the administrator username and password
+ *
+ * @param argc Standard main() style argc
+ * @param argc Standard main() style argv. Initial components are already
+ * stripped. Currently not used.
+ * @return A shell status integer (0 for success)
+ *
+ **/
+
+int net_rpc_join_newstyle(int argc, const char **argv)
+{
+
+ /* libsmb variables */
+
+ struct cli_state *cli;
+ TALLOC_CTX *mem_ctx;
+ uint32 acb_info = ACB_WSTRUST;
+ uint32 sec_channel_type;
+
+ /* rpc variables */
+
+ POLICY_HND lsa_pol, sam_pol, domain_pol, user_pol;
+ DOM_SID *domain_sid;
+ uint32 user_rid;
+
+ /* Password stuff */
+
+ char *clear_trust_password = NULL;
+ uchar pwbuf[516];
+ SAM_USERINFO_CTR ctr;
+ SAM_USER_INFO_24 p24;
+ SAM_USER_INFO_10 p10;
+ uchar md4_trust_password[16];
+
+ /* Misc */
+
+ NTSTATUS result;
+ int retval = 1;
+ char *domain;
+ uint32 num_rids, *name_types, *user_rids;
+ uint32 flags = 0x3e8;
+ char *acct_name;
+ const char *const_acct_name;
+
+ /* check what type of join */
+ if (argc >= 0) {
+ sec_channel_type = get_sec_channel_type(argv[0]);
+ } else {
+ sec_channel_type = get_sec_channel_type(NULL);
+ }
+
+ switch (sec_channel_type) {
+ case SEC_CHAN_WKSTA:
+ acb_info = ACB_WSTRUST;
+ break;
+ case SEC_CHAN_BDC:
+ acb_info = ACB_SVRTRUST;
+ break;
+#if 0
+ case SEC_CHAN_DOMAIN:
+ acb_info = ACB_DOMTRUST;
+ break;
+#endif
+ }
+
+ /* Connect to remote machine */
+
+ if (!(cli = net_make_ipc_connection(NET_FLAGS_PDC)))
+ return 1;
+
+ if (!(mem_ctx = talloc_init("net_rpc_join_newstyle"))) {
+ DEBUG(0, ("Could not initialise talloc context\n"));
+ goto done;
+ }
+
+ /* Fetch domain sid */
+
+ if (!cli_nt_session_open(cli, PI_LSARPC)) {
+ DEBUG(0, ("Error connecting to LSA pipe\n"));
+ goto done;
+ }
+
+
+ CHECK_RPC_ERR(cli_lsa_open_policy(cli, mem_ctx, True,
+ SEC_RIGHTS_MAXIMUM_ALLOWED,
+ &lsa_pol),
+ "error opening lsa policy handle");
+
+ CHECK_RPC_ERR(cli_lsa_query_info_policy(cli, mem_ctx, &lsa_pol,
+ 5, &domain, &domain_sid),
+ "error querying info policy");
+
+ cli_lsa_close(cli, mem_ctx, &lsa_pol);
+
+ cli_nt_session_close(cli); /* Done with this pipe */
+
+ /* Create domain user */
+ if (!cli_nt_session_open(cli, PI_SAMR)) {
+ DEBUG(0, ("Error connecting to SAM pipe\n"));
+ goto done;
+ }
+
+ CHECK_RPC_ERR(cli_samr_connect(cli, mem_ctx,
+ SEC_RIGHTS_MAXIMUM_ALLOWED,
+ &sam_pol),
+ "could not connect to SAM database");
+
+
+ CHECK_RPC_ERR(cli_samr_open_domain(cli, mem_ctx, &sam_pol,
+ SEC_RIGHTS_MAXIMUM_ALLOWED,
+ domain_sid, &domain_pol),
+ "could not open domain");
+
+ /* Create domain user */
+ acct_name = talloc_asprintf(mem_ctx, "%s$", global_myname());
+ strlower_m(acct_name);
+ const_acct_name = acct_name;
+
+ result = cli_samr_create_dom_user(cli, mem_ctx, &domain_pol,
+ acct_name, acb_info,
+ 0xe005000b, &user_pol,
+ &user_rid);
+
+ if (!NT_STATUS_IS_OK(result) &&
+ !NT_STATUS_EQUAL(result, NT_STATUS_USER_EXISTS)) {
+ d_printf("Create of workstation account failed\n");
+
+ /* If NT_STATUS_ACCESS_DENIED then we have a valid
+ username/password combo but the user does not have
+ administrator access. */
+
+ if (NT_STATUS_V(result) == NT_STATUS_V(NT_STATUS_ACCESS_DENIED))
+ d_printf("User specified does not have administrator privileges\n");
+
+ goto done;
+ }
+
+ /* We *must* do this.... don't ask... */
+
+ if (NT_STATUS_IS_OK(result))
+ cli_samr_close(cli, mem_ctx, &user_pol);
+
+ CHECK_RPC_ERR_DEBUG(cli_samr_lookup_names(cli, mem_ctx,
+ &domain_pol, flags,
+ 1, &const_acct_name,
+ &num_rids,
+ &user_rids, &name_types),
+ ("error looking up rid for user %s: %s\n",
+ acct_name, nt_errstr(result)));
+
+ if (name_types[0] != SID_NAME_USER) {
+ DEBUG(0, ("%s is not a user account (type=%d)\n", acct_name, name_types[0]));
+ goto done;
+ }
+
+ user_rid = user_rids[0];
+
+ /* Open handle on user */
+
+ CHECK_RPC_ERR_DEBUG(
+ cli_samr_open_user(cli, mem_ctx, &domain_pol,
+ SEC_RIGHTS_MAXIMUM_ALLOWED,
+ user_rid, &user_pol),
+ ("could not re-open existing user %s: %s\n",
+ acct_name, nt_errstr(result)));
+
+ /* Create a random machine account password */
+
+ {
+ char *str;
+ str = generate_random_str(DEFAULT_TRUST_ACCOUNT_PASSWORD_LENGTH);
+ clear_trust_password = strdup(str);
+ E_md4hash(clear_trust_password, md4_trust_password);
+ }
+
+ encode_pw_buffer(pwbuf, clear_trust_password, STR_UNICODE);
+
+ /* Set password on machine account */
+
+ ZERO_STRUCT(ctr);
+ ZERO_STRUCT(p24);
+
+ init_sam_user_info24(&p24, (char *)pwbuf,24);
+
+ ctr.switch_value = 24;
+ ctr.info.id24 = &p24;
+
+ CHECK_RPC_ERR(cli_samr_set_userinfo(cli, mem_ctx, &user_pol, 24,
+ &cli->user_session_key, &ctr),
+ "error setting trust account password");
+
+ /* Why do we have to try to (re-)set the ACB to be the same as what
+ we passed in the samr_create_dom_user() call? When a NT
+ workstation is joined to a domain by an administrator the
+ acb_info is set to 0x80. For a normal user with "Add
+ workstations to the domain" rights the acb_info is 0x84. I'm
+ not sure whether it is supposed to make a difference or not. NT
+ seems to cope with either value so don't bomb out if the set
+ userinfo2 level 0x10 fails. -tpot */
+
+ ZERO_STRUCT(ctr);
+ ctr.switch_value = 0x10;
+ ctr.info.id10 = &p10;
+
+ init_sam_user_info10(&p10, acb_info);
+
+ /* Ignoring the return value is necessary for joining a domain
+ as a normal user with "Add workstation to domain" privilege. */
+
+ result = cli_samr_set_userinfo2(cli, mem_ctx, &user_pol, 0x10,
+ &cli->user_session_key, &ctr);
+
+ /* Now check the whole process from top-to-bottom */
+ cli_samr_close(cli, mem_ctx, &user_pol);
+ cli_nt_session_close(cli); /* Done with this pipe */
+
+ if (!cli_nt_session_open(cli, PI_NETLOGON)) {
+ DEBUG(0,("Error connecting to NETLOGON pipe\n"));
+ goto done;
+ }
+
+ /* ensure that schannel uses the right domain */
+ fstrcpy(cli->domain, domain);
+
+ result = cli_nt_establish_netlogon(cli, sec_channel_type,
+ md4_trust_password);
+
+ if (!NT_STATUS_IS_OK(result)) {
+ DEBUG(0, ("Error domain join verification (reused connection): %s\n\n",
+ nt_errstr(result)));
+
+ if ( NT_STATUS_EQUAL(result, NT_STATUS_ACCESS_DENIED) &&
+ (sec_channel_type == SEC_CHAN_BDC) ) {
+ d_printf("Please make sure that no computer account\n"
+ "named like this machine (%s) exists in the domain\n",
+ global_myname());
+ }
+
+ goto done;
+ }
+
+ /* Now store the secret in the secrets database */
+
+ strupper_m(domain);
+
+ if (!secrets_store_domain_sid(domain, domain_sid)) {
+ DEBUG(0, ("error storing domain sid for %s\n", domain));
+ goto done;
+ }
+
+ if (!secrets_store_machine_password(clear_trust_password, domain, sec_channel_type)) {
+ DEBUG(0, ("error storing plaintext domain secrets for %s\n", domain));
+ }
+
+ /* double-check, connection from scratch */
+ retval = net_rpc_join_ok(domain);
+
+done:
+ /* Close down pipe - this will clean up open policy handles */
+
+ if (cli->nt_pipe_fnum)
+ cli_nt_session_close(cli);
+
+ /* Display success or failure */
+
+ if (retval != 0) {
+ fprintf(stderr,"Unable to join domain %s.\n",domain);
+ } else {
+ printf("Joined domain %s.\n",domain);
+ }
+
+ cli_shutdown(cli);
+
+ SAFE_FREE(clear_trust_password);
+
+ return retval;
+}
+
+
+/**
+ * check that a join is OK
+ *
+ * @return A shell status integer (0 for success)
+ *
+ **/
+int net_rpc_testjoin(int argc, const char **argv)
+{
+ char *domain = smb_xstrdup(opt_target_workgroup);
+
+ /* Display success or failure */
+ if (net_rpc_join_ok(domain) != 0) {
+ fprintf(stderr,"Join to domain '%s' is not valid\n",domain);
+ free(domain);
+ return -1;
+ }
+
+ printf("Join to '%s' is OK\n",domain);
+ free(domain);
+ return 0;
+}
diff --git a/source/utils/net_rpc_samsync.c b/source/utils/net_rpc_samsync.c
new file mode 100644
index 00000000000..882f3a02bc2
--- /dev/null
+++ b/source/utils/net_rpc_samsync.c
@@ -0,0 +1,1080 @@
+/*
+ Unix SMB/CIFS implementation.
+ dump the remote SAM using rpc samsync operations
+
+ Copyright (C) Andrew Tridgell 2002
+ Copyright (C) Tim Potter 2001,2002
+ Modified by Volker Lendecke 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 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 "../utils/net.h"
+
+extern DOM_SID global_sid_Builtin;
+
+static void display_group_mem_info(uint32 rid, SAM_GROUP_MEM_INFO *g)
+{
+ int i;
+ d_printf("Group mem %u: ", rid);
+ for (i=0;i<g->num_members;i++) {
+ d_printf("%u ", g->rids[i]);
+ }
+ d_printf("\n");
+}
+
+static void display_alias_info(uint32 rid, SAM_ALIAS_INFO *a)
+{
+ d_printf("Alias '%s' ", unistr2_static(&a->uni_als_name));
+ d_printf("desc='%s' rid=%u\n", unistr2_static(&a->uni_als_desc), a->als_rid);
+}
+
+static void display_alias_mem(uint32 rid, SAM_ALIAS_MEM_INFO *a)
+{
+ int i;
+ d_printf("Alias rid %u: ", rid);
+ for (i=0;i<a->num_members;i++) {
+ d_printf("%s ", sid_string_static(&a->sids[i].sid));
+ }
+ d_printf("\n");
+}
+
+static void display_account_info(uint32 rid, SAM_ACCOUNT_INFO *a)
+{
+ fstring hex_nt_passwd, hex_lm_passwd;
+ uchar lm_passwd[16], nt_passwd[16];
+ static uchar zero_buf[16];
+
+ /* Decode hashes from password hash (if they are not NULL) */
+
+ if (memcmp(a->pass.buf_lm_pwd, zero_buf, 16) != 0) {
+ sam_pwd_hash(a->user_rid, a->pass.buf_lm_pwd, lm_passwd, 0);
+ pdb_sethexpwd(hex_lm_passwd, lm_passwd, a->acb_info);
+ } else {
+ pdb_sethexpwd(hex_lm_passwd, NULL, 0);
+ }
+
+ if (memcmp(a->pass.buf_nt_pwd, zero_buf, 16) != 0) {
+ sam_pwd_hash(a->user_rid, a->pass.buf_nt_pwd, nt_passwd, 0);
+ pdb_sethexpwd(hex_nt_passwd, nt_passwd, a->acb_info);
+ } else {
+ pdb_sethexpwd(hex_nt_passwd, NULL, 0);
+ }
+
+ printf("%s:%d:%s:%s:%s:LCT-0\n", unistr2_static(&a->uni_acct_name),
+ a->user_rid, hex_lm_passwd, hex_nt_passwd,
+ pdb_encode_acct_ctrl(a->acb_info, NEW_PW_FORMAT_SPACE_PADDED_LEN));
+}
+
+static void display_domain_info(SAM_DOMAIN_INFO *a)
+{
+ d_printf("Domain name: %s\n", unistr2_static(&a->uni_dom_name));
+}
+
+static void display_group_info(uint32 rid, SAM_GROUP_INFO *a)
+{
+ d_printf("Group '%s' ", unistr2_static(&a->uni_grp_name));
+ d_printf("desc='%s', rid=%u\n", unistr2_static(&a->uni_grp_desc), rid);
+}
+
+static void display_sam_entry(SAM_DELTA_HDR *hdr_delta, SAM_DELTA_CTR *delta)
+{
+ switch (hdr_delta->type) {
+ case SAM_DELTA_ACCOUNT_INFO:
+ display_account_info(hdr_delta->target_rid, &delta->account_info);
+ break;
+ case SAM_DELTA_GROUP_MEM:
+ display_group_mem_info(hdr_delta->target_rid, &delta->grp_mem_info);
+ break;
+ case SAM_DELTA_ALIAS_INFO:
+ display_alias_info(hdr_delta->target_rid, &delta->alias_info);
+ break;
+ case SAM_DELTA_ALIAS_MEM:
+ display_alias_mem(hdr_delta->target_rid, &delta->als_mem_info);
+ break;
+ case SAM_DELTA_DOMAIN_INFO:
+ display_domain_info(&delta->domain_info);
+ break;
+ case SAM_DELTA_GROUP_INFO:
+ display_group_info(hdr_delta->target_rid, &delta->group_info);
+ break;
+ /* The following types are recognised but not handled */
+ case SAM_DELTA_RENAME_GROUP:
+ d_printf("SAM_DELTA_RENAME_GROUP not handled\n");
+ break;
+ case SAM_DELTA_RENAME_USER:
+ d_printf("SAM_DELTA_RENAME_USER not handled\n");
+ break;
+ case SAM_DELTA_RENAME_ALIAS:
+ d_printf("SAM_DELTA_RENAME_ALIAS not handled\n");
+ break;
+ case SAM_DELTA_POLICY_INFO:
+ d_printf("SAM_DELTA_POLICY_INFO not handled\n");
+ break;
+ case SAM_DELTA_TRUST_DOMS:
+ d_printf("SAM_DELTA_TRUST_DOMS not handled\n");
+ break;
+ case SAM_DELTA_PRIVS_INFO:
+ d_printf("SAM_DELTA_PRIVS_INFO not handled\n");
+ break;
+ case SAM_DELTA_SECRET_INFO:
+ d_printf("SAM_DELTA_SECRET_INFO not handled\n");
+ break;
+ case SAM_DELTA_DELETE_GROUP:
+ d_printf("SAM_DELTA_DELETE_GROUP not handled\n");
+ break;
+ case SAM_DELTA_DELETE_USER:
+ d_printf("SAM_DELTA_DELETE_USER not handled\n");
+ break;
+ case SAM_DELTA_MODIFIED_COUNT:
+ d_printf("SAM_DELTA_MODIFIED_COUNT not handled\n");
+ break;
+ default:
+ d_printf("Unknown delta record type %d\n", hdr_delta->type);
+ break;
+ }
+}
+
+
+static void dump_database(struct cli_state *cli, unsigned db_type, DOM_CRED *ret_creds)
+{
+ unsigned sync_context = 0;
+ NTSTATUS result;
+ int i;
+ TALLOC_CTX *mem_ctx;
+ SAM_DELTA_HDR *hdr_deltas;
+ SAM_DELTA_CTR *deltas;
+ uint32 num_deltas;
+
+ if (!(mem_ctx = talloc_init("dump_database"))) {
+ return;
+ }
+
+ switch( db_type ) {
+ case SAM_DATABASE_DOMAIN:
+ d_printf("Dumping DOMAIN database\n");
+ break;
+ case SAM_DATABASE_BUILTIN:
+ d_printf("Dumping BUILTIN database\n");
+ break;
+ case SAM_DATABASE_PRIVS:
+ d_printf("Dumping PRIVS databases\n");
+ break;
+ default:
+ d_printf("Dumping unknown database type %u\n", db_type );
+ break;
+ }
+
+ do {
+ result = cli_netlogon_sam_sync(cli, mem_ctx, ret_creds, db_type,
+ sync_context,
+ &num_deltas, &hdr_deltas, &deltas);
+ if (NT_STATUS_IS_ERR(result))
+ break;
+
+ clnt_deal_with_creds(cli->sess_key, &(cli->clnt_cred), ret_creds);
+ for (i = 0; i < num_deltas; i++) {
+ display_sam_entry(&hdr_deltas[i], &deltas[i]);
+ }
+ sync_context += 1;
+ } while (NT_STATUS_EQUAL(result, STATUS_MORE_ENTRIES));
+
+ talloc_destroy(mem_ctx);
+}
+
+/* dump sam database via samsync rpc calls */
+NTSTATUS rpc_samdump_internals(const DOM_SID *domain_sid,
+ const char *domain_name,
+ struct cli_state *cli, TALLOC_CTX *mem_ctx,
+ int argc, const char **argv)
+{
+ NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
+ uchar trust_password[16];
+ DOM_CRED ret_creds;
+ uint32 sec_channel;
+
+ ZERO_STRUCT(ret_creds);
+
+ fstrcpy(cli->domain, domain_name);
+
+ if (!secrets_fetch_trust_account_password(domain_name,
+ trust_password,
+ NULL, &sec_channel)) {
+ DEBUG(0,("Could not fetch trust account password\n"));
+ goto fail;
+ }
+
+ if (!NT_STATUS_IS_OK(nt_status = cli_nt_establish_netlogon(cli, sec_channel,
+ trust_password))) {
+ DEBUG(0,("Error connecting to NETLOGON pipe\n"));
+ goto fail;
+ }
+
+ dump_database(cli, SAM_DATABASE_DOMAIN, &ret_creds);
+ dump_database(cli, SAM_DATABASE_BUILTIN, &ret_creds);
+ dump_database(cli, SAM_DATABASE_PRIVS, &ret_creds);
+
+ nt_status = NT_STATUS_OK;
+
+fail:
+ cli_nt_session_close(cli);
+ return nt_status;
+}
+
+/* Convert a SAM_ACCOUNT_DELTA to a SAM_ACCOUNT. */
+#define STRING_CHANGED (old_string && !new_string) ||\
+ (!old_string && new_string) ||\
+ (old_string && new_string && (strcmp(old_string, new_string) != 0))
+
+static NTSTATUS
+sam_account_from_delta(SAM_ACCOUNT *account, SAM_ACCOUNT_INFO *delta)
+{
+ const char *old_string, *new_string;
+ time_t unix_time, stored_time;
+ uchar lm_passwd[16], nt_passwd[16];
+ static uchar zero_buf[16];
+
+ /* Username, fullname, home dir, dir drive, logon script, acct
+ desc, workstations, profile. */
+
+ if (delta->hdr_acct_name.buffer) {
+ old_string = pdb_get_nt_username(account);
+ new_string = unistr2_static(&delta->uni_acct_name);
+
+ if (STRING_CHANGED) {
+ pdb_set_nt_username(account, new_string, PDB_CHANGED);
+
+ }
+
+ /* Unix username is the same - for sanity */
+ old_string = pdb_get_username( account );
+ if (STRING_CHANGED) {
+ pdb_set_username(account, new_string, PDB_CHANGED);
+ }
+ }
+
+ if (delta->hdr_full_name.buffer) {
+ old_string = pdb_get_fullname(account);
+ new_string = unistr2_static(&delta->uni_full_name);
+
+ if (STRING_CHANGED)
+ pdb_set_fullname(account, new_string, PDB_CHANGED);
+ }
+
+ if (delta->hdr_home_dir.buffer) {
+ old_string = pdb_get_homedir(account);
+ new_string = unistr2_static(&delta->uni_home_dir);
+
+ if (STRING_CHANGED)
+ pdb_set_homedir(account, new_string, PDB_CHANGED);
+ }
+
+ if (delta->hdr_dir_drive.buffer) {
+ old_string = pdb_get_dir_drive(account);
+ new_string = unistr2_static(&delta->uni_dir_drive);
+
+ if (STRING_CHANGED)
+ pdb_set_dir_drive(account, new_string, PDB_CHANGED);
+ }
+
+ if (delta->hdr_logon_script.buffer) {
+ old_string = pdb_get_logon_script(account);
+ new_string = unistr2_static(&delta->uni_logon_script);
+
+ if (STRING_CHANGED)
+ pdb_set_logon_script(account, new_string, PDB_CHANGED);
+ }
+
+ if (delta->hdr_acct_desc.buffer) {
+ old_string = pdb_get_acct_desc(account);
+ new_string = unistr2_static(&delta->uni_acct_desc);
+
+ if (STRING_CHANGED)
+ pdb_set_acct_desc(account, new_string, PDB_CHANGED);
+ }
+
+ if (delta->hdr_workstations.buffer) {
+ old_string = pdb_get_workstations(account);
+ new_string = unistr2_static(&delta->uni_workstations);
+
+ if (STRING_CHANGED)
+ pdb_set_workstations(account, new_string, PDB_CHANGED);
+ }
+
+ if (delta->hdr_profile.buffer) {
+ old_string = pdb_get_profile_path(account);
+ new_string = unistr2_static(&delta->uni_profile);
+
+ if (STRING_CHANGED)
+ pdb_set_profile_path(account, new_string, PDB_CHANGED);
+ }
+
+ /* User and group sid */
+ if (pdb_get_user_rid(account) != delta->user_rid)
+ pdb_set_user_sid_from_rid(account, delta->user_rid, PDB_CHANGED);
+ if (pdb_get_group_rid(account) != delta->group_rid)
+ pdb_set_group_sid_from_rid(account, delta->group_rid, PDB_CHANGED);
+
+ /* Logon and password information */
+ if (!nt_time_is_zero(&delta->logon_time)) {
+ unix_time = nt_time_to_unix(&delta->logon_time);
+ stored_time = pdb_get_logon_time(account);
+ if (stored_time != unix_time)
+ pdb_set_logon_time(account, unix_time, PDB_CHANGED);
+ }
+
+ if (!nt_time_is_zero(&delta->logoff_time)) {
+ unix_time = nt_time_to_unix(&delta->logoff_time);
+ stored_time = pdb_get_logoff_time(account);
+ if (stored_time != unix_time)
+ pdb_set_logoff_time(account, unix_time,PDB_CHANGED);
+ }
+
+ if (pdb_get_logon_divs(account) != delta->logon_divs)
+ pdb_set_logon_divs(account, delta->logon_divs, PDB_CHANGED);
+
+ /* TODO: logon hours */
+ /* TODO: bad password count */
+ /* TODO: logon count */
+
+ if (!nt_time_is_zero(&delta->pwd_last_set_time)) {
+ unix_time = nt_time_to_unix(&delta->pwd_last_set_time);
+ stored_time = pdb_get_pass_last_set_time(account);
+ if (stored_time != unix_time)
+ pdb_set_pass_last_set_time(account, unix_time, PDB_CHANGED);
+ }
+
+#if 0
+/* No kickoff time in the delta? */
+ if (!nt_time_is_zero(&delta->kickoff_time)) {
+ unix_time = nt_time_to_unix(&delta->kickoff_time);
+ stored_time = pdb_get_kickoff_time(account);
+ if (stored_time != unix_time)
+ pdb_set_kickoff_time(account, unix_time, PDB_CHANGED);
+ }
+#endif
+
+ /* Decode hashes from password hash
+ Note that win2000 may send us all zeros for the hashes if it doesn't
+ think this channel is secure enough - don't set the passwords at all
+ in that case
+ */
+ if (memcmp(delta->pass.buf_lm_pwd, zero_buf, 16) != 0) {
+ sam_pwd_hash(delta->user_rid, delta->pass.buf_lm_pwd, lm_passwd, 0);
+ pdb_set_lanman_passwd(account, lm_passwd, PDB_CHANGED);
+ }
+
+ if (memcmp(delta->pass.buf_nt_pwd, zero_buf, 16) != 0) {
+ sam_pwd_hash(delta->user_rid, delta->pass.buf_nt_pwd, nt_passwd, 0);
+ pdb_set_nt_passwd(account, nt_passwd, PDB_CHANGED);
+ }
+
+ /* TODO: account expiry time */
+
+ if (pdb_get_acct_ctrl(account) != delta->acb_info)
+ pdb_set_acct_ctrl(account, delta->acb_info, PDB_CHANGED);
+
+ pdb_set_domain(account, lp_workgroup(), PDB_CHANGED);
+
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS fetch_account_info(uint32 rid, SAM_ACCOUNT_INFO *delta)
+{
+ NTSTATUS nt_ret;
+ fstring account;
+ pstring add_script;
+ SAM_ACCOUNT *sam_account=NULL;
+ GROUP_MAP map;
+ struct group *grp;
+ DOM_SID user_sid;
+ DOM_SID group_sid;
+ struct passwd *passwd;
+ fstring sid_string;
+
+ fstrcpy(account, unistr2_static(&delta->uni_acct_name));
+ d_printf("Creating account: %s\n", account);
+
+ if (!NT_STATUS_IS_OK(nt_ret = pdb_init_sam(&sam_account)))
+ return nt_ret;
+
+ if (!(passwd = Get_Pwnam(account))) {
+ /* Create appropriate user */
+ if (delta->acb_info & ACB_NORMAL) {
+ pstrcpy(add_script, lp_adduser_script());
+ } else if ( (delta->acb_info & ACB_WSTRUST) ||
+ (delta->acb_info & ACB_SVRTRUST) ||
+ (delta->acb_info & ACB_DOMTRUST) ) {
+ pstrcpy(add_script, lp_addmachine_script());
+ } else {
+ DEBUG(1, ("Unknown user type: %s\n",
+ pdb_encode_acct_ctrl(delta->acb_info, NEW_PW_FORMAT_SPACE_PADDED_LEN)));
+ nt_ret = NT_STATUS_UNSUCCESSFUL;
+ goto done;
+ }
+ if (*add_script) {
+ int add_ret;
+ all_string_sub(add_script, "%u", account,
+ sizeof(account));
+ add_ret = smbrun(add_script,NULL);
+ DEBUG(1,("fetch_account: Running the command `%s' "
+ "gave %d\n", add_script, add_ret));
+ } else {
+ DEBUG(8,("fetch_account_info: no add user/machine script. Asking winbindd\n"));
+
+ /* don't need a RID allocated since the user already has a SID */
+ if ( !winbind_create_user( account, NULL ) )
+ DEBUG(4,("fetch_account_info: winbind_create_user() failed\n"));
+ }
+
+ /* try and find the possible unix account again */
+ if ( !(passwd = Get_Pwnam(account)) ) {
+ d_printf("Could not create posix account info for '%s'\n", account);
+ nt_ret = NT_STATUS_NO_SUCH_USER;
+ goto done;
+ }
+ }
+
+ sid_copy(&user_sid, get_global_sam_sid());
+ sid_append_rid(&user_sid, delta->user_rid);
+
+ DEBUG(3, ("Attempting to find SID %s for user %s in the passdb\n", sid_to_string(sid_string, &user_sid), account));
+ if (!pdb_getsampwsid(sam_account, &user_sid)) {
+ sam_account_from_delta(sam_account, delta);
+ DEBUG(3, ("Attempting to add user SID %s for user %s in the passdb\n",
+ sid_to_string(sid_string, &user_sid), pdb_get_username(sam_account)));
+ if (!pdb_add_sam_account(sam_account)) {
+ DEBUG(1, ("SAM Account for %s failed to be added to the passdb!\n",
+ account));
+ return NT_STATUS_ACCESS_DENIED;
+ }
+ } else {
+ sam_account_from_delta(sam_account, delta);
+ DEBUG(3, ("Attempting to update user SID %s for user %s in the passdb\n",
+ sid_to_string(sid_string, &user_sid), pdb_get_username(sam_account)));
+ if (!pdb_update_sam_account(sam_account)) {
+ DEBUG(1, ("SAM Account for %s failed to be updated in the passdb!\n",
+ account));
+ pdb_free_sam(&sam_account);
+ return NT_STATUS_ACCESS_DENIED;
+ }
+ }
+
+ group_sid = *pdb_get_group_sid(sam_account);
+
+ if (!pdb_getgrsid(&map, group_sid)) {
+ DEBUG(0, ("Primary group of %s has no mapping!\n",
+ pdb_get_username(sam_account)));
+ } else {
+ if (map.gid != passwd->pw_gid) {
+ if (!(grp = getgrgid(map.gid))) {
+ DEBUG(0, ("Could not find unix group %lu for user %s (group SID=%s)\n",
+ (unsigned long)map.gid, pdb_get_username(sam_account), sid_string_static(&group_sid)));
+ } else {
+ smb_set_primary_group(grp->gr_name, pdb_get_username(sam_account));
+ }
+ }
+ }
+
+ if ( !passwd ) {
+ DEBUG(1, ("No unix user for this account (%s), cannot adjust mappings\n",
+ pdb_get_username(sam_account)));
+ }
+
+ done:
+ pdb_free_sam(&sam_account);
+ return nt_ret;
+}
+
+static NTSTATUS
+fetch_group_info(uint32 rid, SAM_GROUP_INFO *delta)
+{
+ fstring name;
+ fstring comment;
+ struct group *grp = NULL;
+ DOM_SID group_sid;
+ fstring sid_string;
+ GROUP_MAP map;
+ BOOL insert = True;
+
+ unistr2_to_ascii(name, &delta->uni_grp_name, sizeof(name)-1);
+ unistr2_to_ascii(comment, &delta->uni_grp_desc, sizeof(comment)-1);
+
+ /* add the group to the mapping table */
+ sid_copy(&group_sid, get_global_sam_sid());
+ sid_append_rid(&group_sid, rid);
+ sid_to_string(sid_string, &group_sid);
+
+ if (pdb_getgrsid(&map, group_sid)) {
+ if ( map.gid != -1 )
+ grp = getgrgid(map.gid);
+ insert = False;
+ }
+
+ if (grp == NULL) {
+ gid_t gid;
+
+ /* No group found from mapping, find it from its name. */
+ if ((grp = getgrnam(name)) == NULL) {
+
+ /* No appropriate group found, create one */
+
+ d_printf("Creating unix group: '%s'\n", name);
+
+ if (smb_create_group(name, &gid) != 0)
+ return NT_STATUS_ACCESS_DENIED;
+
+ if ((grp = getgrnam(name)) == NULL)
+ return NT_STATUS_ACCESS_DENIED;
+ }
+ }
+
+ map.gid = grp->gr_gid;
+ map.sid = group_sid;
+ map.sid_name_use = SID_NAME_DOM_GRP;
+ fstrcpy(map.nt_name, name);
+ if (delta->hdr_grp_desc.buffer) {
+ fstrcpy(map.comment, comment);
+ } else {
+ fstrcpy(map.comment, "");
+ }
+
+ if (insert)
+ pdb_add_group_mapping_entry(&map);
+ else
+ pdb_update_group_mapping_entry(&map);
+
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS
+fetch_group_mem_info(uint32 rid, SAM_GROUP_MEM_INFO *delta)
+{
+ int i;
+ TALLOC_CTX *t = NULL;
+ char **nt_members = NULL;
+ char **unix_members;
+ DOM_SID group_sid;
+ GROUP_MAP map;
+ struct group *grp;
+
+ if (delta->num_members == 0) {
+ return NT_STATUS_OK;
+ }
+
+ sid_copy(&group_sid, get_global_sam_sid());
+ sid_append_rid(&group_sid, rid);
+
+ if (!get_domain_group_from_sid(group_sid, &map)) {
+ DEBUG(0, ("Could not find global group %d\n", rid));
+ return NT_STATUS_NO_SUCH_GROUP;
+ }
+
+ if (!(grp = getgrgid(map.gid))) {
+ DEBUG(0, ("Could not find unix group %lu\n", (unsigned long)map.gid));
+ return NT_STATUS_NO_SUCH_GROUP;
+ }
+
+ d_printf("Group members of %s: ", grp->gr_name);
+
+ if (!(t = talloc_init("fetch_group_mem_info"))) {
+ DEBUG(0, ("could not talloc_init\n"));
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ nt_members = talloc_zero(t, sizeof(char *) * delta->num_members);
+
+ for (i=0; i<delta->num_members; i++) {
+ NTSTATUS nt_status;
+ SAM_ACCOUNT *member = NULL;
+ DOM_SID member_sid;
+
+ if (!NT_STATUS_IS_OK(nt_status = pdb_init_sam_talloc(t, &member))) {
+ talloc_destroy(t);
+ return nt_status;
+ }
+
+ sid_copy(&member_sid, get_global_sam_sid());
+ sid_append_rid(&member_sid, delta->rids[i]);
+
+ if (!pdb_getsampwsid(member, &member_sid)) {
+ DEBUG(1, ("Found bogus group member: %d (member_sid=%s group=%s)\n",
+ delta->rids[i], sid_string_static(&member_sid), grp->gr_name));
+ pdb_free_sam(&member);
+ continue;
+ }
+
+ if (pdb_get_group_rid(member) == rid) {
+ d_printf("%s(primary),", pdb_get_username(member));
+ pdb_free_sam(&member);
+ continue;
+ }
+
+ d_printf("%s,", pdb_get_username(member));
+ nt_members[i] = talloc_strdup(t, pdb_get_username(member));
+ pdb_free_sam(&member);
+ }
+
+ d_printf("\n");
+
+ unix_members = grp->gr_mem;
+
+ while (*unix_members) {
+ BOOL is_nt_member = False;
+ for (i=0; i<delta->num_members; i++) {
+ if (nt_members[i] == NULL) {
+ /* This was a primary group */
+ continue;
+ }
+
+ if (strcmp(*unix_members, nt_members[i]) == 0) {
+ is_nt_member = True;
+ break;
+ }
+ }
+ if (!is_nt_member) {
+ /* We look at a unix group member that is not
+ an nt group member. So, remove it. NT is
+ boss here. */
+ smb_delete_user_group(grp->gr_name, *unix_members);
+ }
+ unix_members += 1;
+ }
+
+ for (i=0; i<delta->num_members; i++) {
+ BOOL is_unix_member = False;
+
+ if (nt_members[i] == NULL) {
+ /* This was the primary group */
+ continue;
+ }
+
+ unix_members = grp->gr_mem;
+
+ while (*unix_members) {
+ if (strcmp(*unix_members, nt_members[i]) == 0) {
+ is_unix_member = True;
+ break;
+ }
+ unix_members += 1;
+ }
+
+ if (!is_unix_member) {
+ /* We look at a nt group member that is not a
+ unix group member currently. So, add the nt
+ group member. */
+ smb_add_user_group(grp->gr_name, nt_members[i]);
+ }
+ }
+
+ talloc_destroy(t);
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS fetch_alias_info(uint32 rid, SAM_ALIAS_INFO *delta,
+ DOM_SID dom_sid)
+{
+ fstring name;
+ fstring comment;
+ struct group *grp = NULL;
+ DOM_SID alias_sid;
+ fstring sid_string;
+ GROUP_MAP map;
+ BOOL insert = True;
+
+ unistr2_to_ascii(name, &delta->uni_als_name, sizeof(name)-1);
+ unistr2_to_ascii(comment, &delta->uni_als_desc, sizeof(comment)-1);
+
+ /* Find out whether the group is already mapped */
+ sid_copy(&alias_sid, &dom_sid);
+ sid_append_rid(&alias_sid, rid);
+ sid_to_string(sid_string, &alias_sid);
+
+ if (pdb_getgrsid(&map, alias_sid)) {
+ grp = getgrgid(map.gid);
+ insert = False;
+ }
+
+ if (grp == NULL) {
+ gid_t gid;
+
+ /* No group found from mapping, find it from its name. */
+ if ((grp = getgrnam(name)) == NULL) {
+ /* No appropriate group found, create one */
+ d_printf("Creating unix group: '%s'\n", name);
+ if (smb_create_group(name, &gid) != 0)
+ return NT_STATUS_ACCESS_DENIED;
+ if ((grp = getgrgid(gid)) == NULL)
+ return NT_STATUS_ACCESS_DENIED;
+ }
+ }
+
+ map.gid = grp->gr_gid;
+ map.sid = alias_sid;
+
+ if (sid_equal(&dom_sid, &global_sid_Builtin))
+ map.sid_name_use = SID_NAME_WKN_GRP;
+ else
+ map.sid_name_use = SID_NAME_ALIAS;
+
+ fstrcpy(map.nt_name, name);
+ fstrcpy(map.comment, comment);
+
+ if (insert)
+ pdb_add_group_mapping_entry(&map);
+ else
+ pdb_update_group_mapping_entry(&map);
+
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS
+fetch_alias_mem(uint32 rid, SAM_ALIAS_MEM_INFO *delta, DOM_SID dom_sid)
+{
+#if 0 /*
+ * commented out right now after talking to Volker. Can't
+ * do much with the membership but seemed a shame to waste
+ * somewhat working code. Needs testing because the membership
+ * that shows up surprises me. Also can't do much with groups
+ * in groups (e.g. Domain Admins being a member of Adminsitrators).
+ * --jerry
+ */
+
+ int i;
+ TALLOC_CTX *t = NULL;
+ char **nt_members = NULL;
+ char **unix_members;
+ DOM_SID group_sid;
+ GROUP_MAP map;
+ struct group *grp;
+ enum SID_NAME_USE sid_type;
+
+ if (delta->num_members == 0) {
+ return NT_STATUS_OK;
+ }
+
+ sid_copy(&group_sid, &dom_sid);
+ sid_append_rid(&group_sid, rid);
+
+ if (sid_equal(&dom_sid, &global_sid_Builtin)) {
+ sid_type = SID_NAME_WKN_GRP;
+ if (!get_builtin_group_from_sid(&group_sid, &map, False)) {
+ DEBUG(0, ("Could not find builtin group %s\n", sid_string_static(&group_sid)));
+ return NT_STATUS_NO_SUCH_GROUP;
+ }
+ } else {
+ sid_type = SID_NAME_ALIAS;
+ if (!get_local_group_from_sid(&group_sid, &map, False)) {
+ DEBUG(0, ("Could not find local group %s\n", sid_string_static(&group_sid)));
+ return NT_STATUS_NO_SUCH_GROUP;
+ }
+ }
+
+ if (!(grp = getgrgid(map.gid))) {
+ DEBUG(0, ("Could not find unix group %d\n", map.gid));
+ return NT_STATUS_NO_SUCH_GROUP;
+ }
+
+ d_printf("Group members of %s: ", grp->gr_name);
+
+ if (!(t = talloc_init("fetch_group_mem_info"))) {
+ DEBUG(0, ("could not talloc_init\n"));
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ nt_members = talloc_zero(t, sizeof(char *) * delta->num_members);
+
+ for (i=0; i<delta->num_members; i++) {
+ NTSTATUS nt_status;
+ SAM_ACCOUNT *member = NULL;
+ DOM_SID member_sid;
+
+ if (!NT_STATUS_IS_OK(nt_status = pdb_init_sam_talloc(t, &member))) {
+ talloc_destroy(t);
+ return nt_status;
+ }
+
+ sid_copy(&member_sid, &delta->sids[i].sid);
+
+ if (!pdb_getsampwsid(member, &member_sid)) {
+ DEBUG(1, ("Found bogus group member: (member_sid=%s group=%s)\n",
+ sid_string_static(&member_sid), grp->gr_name));
+ pdb_free_sam(&member);
+ continue;
+ }
+
+ if (pdb_get_group_rid(member) == rid) {
+ d_printf("%s(primary),", pdb_get_username(member));
+ pdb_free_sam(&member);
+ continue;
+ }
+
+ d_printf("%s,", pdb_get_username(member));
+ nt_members[i] = talloc_strdup(t, pdb_get_username(member));
+ pdb_free_sam(&member);
+ }
+
+ d_printf("\n");
+
+ unix_members = grp->gr_mem;
+
+ while (*unix_members) {
+ BOOL is_nt_member = False;
+ for (i=0; i<delta->num_members; i++) {
+ if (nt_members[i] == NULL) {
+ /* This was a primary group */
+ continue;
+ }
+
+ if (strcmp(*unix_members, nt_members[i]) == 0) {
+ is_nt_member = True;
+ break;
+ }
+ }
+ if (!is_nt_member) {
+ /* We look at a unix group member that is not
+ an nt group member. So, remove it. NT is
+ boss here. */
+ smb_delete_user_group(grp->gr_name, *unix_members);
+ }
+ unix_members += 1;
+ }
+
+ for (i=0; i<delta->num_members; i++) {
+ BOOL is_unix_member = False;
+
+ if (nt_members[i] == NULL) {
+ /* This was the primary group */
+ continue;
+ }
+
+ unix_members = grp->gr_mem;
+
+ while (*unix_members) {
+ if (strcmp(*unix_members, nt_members[i]) == 0) {
+ is_unix_member = True;
+ break;
+ }
+ unix_members += 1;
+ }
+
+ if (!is_unix_member) {
+ /* We look at a nt group member that is not a
+ unix group member currently. So, add the nt
+ group member. */
+ smb_add_user_group(grp->gr_name, nt_members[i]);
+ }
+ }
+
+ talloc_destroy(t);
+
+#endif /* end of fetch_alias_mem() */
+
+ return NT_STATUS_OK;
+}
+
+static void
+fetch_sam_entry(SAM_DELTA_HDR *hdr_delta, SAM_DELTA_CTR *delta,
+ DOM_SID dom_sid)
+{
+ switch(hdr_delta->type) {
+ case SAM_DELTA_ACCOUNT_INFO:
+ fetch_account_info(hdr_delta->target_rid,
+ &delta->account_info);
+ break;
+ case SAM_DELTA_GROUP_INFO:
+ fetch_group_info(hdr_delta->target_rid,
+ &delta->group_info);
+ break;
+ case SAM_DELTA_GROUP_MEM:
+ fetch_group_mem_info(hdr_delta->target_rid,
+ &delta->grp_mem_info);
+ break;
+ case SAM_DELTA_ALIAS_INFO:
+ fetch_alias_info(hdr_delta->target_rid,
+ &delta->alias_info, dom_sid);
+ break;
+ case SAM_DELTA_ALIAS_MEM:
+ fetch_alias_mem(hdr_delta->target_rid,
+ &delta->als_mem_info, dom_sid);
+ break;
+ /* The following types are recognised but not handled */
+ case SAM_DELTA_DOMAIN_INFO:
+ d_printf("SAM_DELTA_DOMAIN_INFO not handled\n");
+ break;
+ case SAM_DELTA_RENAME_GROUP:
+ d_printf("SAM_DELTA_RENAME_GROUP not handled\n");
+ break;
+ case SAM_DELTA_RENAME_USER:
+ d_printf("SAM_DELTA_RENAME_USER not handled\n");
+ break;
+ case SAM_DELTA_RENAME_ALIAS:
+ d_printf("SAM_DELTA_RENAME_ALIAS not handled\n");
+ break;
+ case SAM_DELTA_POLICY_INFO:
+ d_printf("SAM_DELTA_POLICY_INFO not handled\n");
+ break;
+ case SAM_DELTA_TRUST_DOMS:
+ d_printf("SAM_DELTA_TRUST_DOMS not handled\n");
+ break;
+ case SAM_DELTA_PRIVS_INFO:
+ d_printf("SAM_DELTA_PRIVS_INFO not handled\n");
+ break;
+ case SAM_DELTA_SECRET_INFO:
+ d_printf("SAM_DELTA_SECRET_INFO not handled\n");
+ break;
+ case SAM_DELTA_DELETE_GROUP:
+ d_printf("SAM_DELTA_DELETE_GROUP not handled\n");
+ break;
+ case SAM_DELTA_DELETE_USER:
+ d_printf("SAM_DELTA_DELETE_USER not handled\n");
+ break;
+ case SAM_DELTA_MODIFIED_COUNT:
+ d_printf("SAM_DELTA_MODIFIED_COUNT not handled\n");
+ break;
+ default:
+ d_printf("Unknown delta record type %d\n", hdr_delta->type);
+ break;
+ }
+}
+
+static NTSTATUS
+fetch_database(struct cli_state *cli, unsigned db_type, DOM_CRED *ret_creds,
+ DOM_SID dom_sid)
+{
+ unsigned sync_context = 0;
+ NTSTATUS result;
+ int i;
+ TALLOC_CTX *mem_ctx;
+ SAM_DELTA_HDR *hdr_deltas;
+ SAM_DELTA_CTR *deltas;
+ uint32 num_deltas;
+
+ if (!(mem_ctx = talloc_init("fetch_database")))
+ return NT_STATUS_NO_MEMORY;
+
+ switch( db_type ) {
+ case SAM_DATABASE_DOMAIN:
+ d_printf("Fetching DOMAIN database\n");
+ break;
+ case SAM_DATABASE_BUILTIN:
+ d_printf("Fetching BUILTIN database\n");
+ break;
+ case SAM_DATABASE_PRIVS:
+ d_printf("Fetching PRIVS databases\n");
+ break;
+ default:
+ d_printf("Fetching unknown database type %u\n", db_type );
+ break;
+ }
+
+ do {
+ result = cli_netlogon_sam_sync(cli, mem_ctx, ret_creds,
+ db_type, sync_context,
+ &num_deltas,
+ &hdr_deltas, &deltas);
+
+ if (NT_STATUS_IS_OK(result) ||
+ NT_STATUS_EQUAL(result, STATUS_MORE_ENTRIES)) {
+
+ clnt_deal_with_creds(cli->sess_key, &(cli->clnt_cred),
+ ret_creds);
+
+ for (i = 0; i < num_deltas; i++) {
+ fetch_sam_entry(&hdr_deltas[i], &deltas[i], dom_sid);
+ }
+ } else
+ return result;
+
+ sync_context += 1;
+ } while (NT_STATUS_EQUAL(result, STATUS_MORE_ENTRIES));
+
+ talloc_destroy(mem_ctx);
+
+ return result;
+}
+
+/* dump sam database via samsync rpc calls */
+NTSTATUS rpc_vampire_internals(const DOM_SID *domain_sid,
+ const char *domain_name,
+ struct cli_state *cli, TALLOC_CTX *mem_ctx,
+ int argc, const char **argv)
+{
+ NTSTATUS result;
+ uchar trust_password[16];
+ DOM_CRED ret_creds;
+ fstring my_dom_sid_str;
+ fstring rem_dom_sid_str;
+ uint32 sec_channel;
+
+ ZERO_STRUCT(ret_creds);
+
+ if (!sid_equal(domain_sid, get_global_sam_sid())) {
+ d_printf("Cannot import users from %s at this time, "
+ "as the current domain:\n\t%s: %s\nconflicts "
+ "with the remote domain\n\t%s: %s\n"
+ "Perhaps you need to set: \n\n\tsecurity=user\n\tworkgroup=%s\n\n in your smb.conf?\n",
+ domain_name,
+ get_global_sam_name(), sid_to_string(my_dom_sid_str,
+ get_global_sam_sid()),
+ domain_name, sid_to_string(rem_dom_sid_str, domain_sid),
+ domain_name);
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ fstrcpy(cli->domain, domain_name);
+
+ if (!secrets_fetch_trust_account_password(domain_name,
+ trust_password, NULL,
+ &sec_channel)) {
+ result = NT_STATUS_CANT_ACCESS_DOMAIN_INFO;
+ d_printf("Could not retrieve domain trust secret\n");
+ goto fail;
+ }
+
+ result = cli_nt_establish_netlogon(cli, sec_channel, trust_password);
+
+ if (!NT_STATUS_IS_OK(result)) {
+ d_printf("Failed to setup BDC creds\n");
+ goto fail;
+ }
+
+ result = fetch_database(cli, SAM_DATABASE_DOMAIN, &ret_creds, *domain_sid);
+
+ if (!NT_STATUS_IS_OK(result)) {
+ d_printf("Failed to fetch domain database: %s\n",
+ nt_errstr(result));
+ if (NT_STATUS_EQUAL(result, NT_STATUS_NOT_SUPPORTED))
+ d_printf("Perhaps %s is a Windows 2000 native mode "
+ "domain?\n", domain_name);
+ goto fail;
+ }
+
+ result = fetch_database(cli, SAM_DATABASE_BUILTIN, &ret_creds,
+ global_sid_Builtin);
+
+ if (!NT_STATUS_IS_OK(result)) {
+ d_printf("Failed to fetch builtin database: %s\n",
+ nt_errstr(result));
+ goto fail;
+ }
+
+ /* Currently we crash on PRIVS somewhere in unmarshalling */
+ /* Dump_database(cli, SAM_DATABASE_PRIVS, &ret_creds); */
+
+fail:
+ return result;
+}
diff --git a/source/utils/net_status.c b/source/utils/net_status.c
new file mode 100644
index 00000000000..0543f457cfc
--- /dev/null
+++ b/source/utils/net_status.c
@@ -0,0 +1,257 @@
+/*
+ Samba Unix/Linux SMB client library
+ net status command -- possible replacement for smbstatus
+ Copyright (C) 2003 Volker Lendecke (vl@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 "../utils/net.h"
+
+static int show_session(TDB_CONTEXT *tdb, TDB_DATA kbuf, TDB_DATA dbuf,
+ void *state)
+{
+ BOOL *parseable = (BOOL *)state;
+ struct sessionid sessionid;
+
+ if (dbuf.dsize != sizeof(sessionid))
+ return 0;
+
+ memcpy(&sessionid, dbuf.dptr, sizeof(sessionid));
+
+ if (!process_exists(sessionid.pid)) {
+ return 0;
+ }
+
+ if (*parseable) {
+ d_printf("%d\\%s\\%s\\%s\\%s\n",
+ (int)sessionid.pid, uidtoname(sessionid.uid),
+ gidtoname(sessionid.gid),
+ sessionid.remote_machine, sessionid.hostname);
+ } else {
+ d_printf("%5d %-12s %-12s %-12s (%s)\n",
+ (int)sessionid.pid, uidtoname(sessionid.uid),
+ gidtoname(sessionid.gid),
+ sessionid.remote_machine, sessionid.hostname);
+ }
+
+ return 0;
+}
+
+static int net_status_sessions(int argc, const char **argv)
+{
+ TDB_CONTEXT *tdb;
+ BOOL parseable;
+
+ if (argc == 0) {
+ parseable = False;
+ } else if ((argc == 1) && strequal(argv[0], "parseable")) {
+ parseable = True;
+ } else {
+ return net_help_status(argc, argv);
+ }
+
+ if (!parseable) {
+ d_printf("PID Username Group Machine"
+ " \n");
+ d_printf("-------------------------------------------"
+ "------------------------\n");
+ }
+
+ tdb = tdb_open_log(lock_path("sessionid.tdb"), 0,
+ TDB_DEFAULT, O_RDONLY, 0);
+
+ if (tdb == NULL) {
+ d_printf("%s not initialised\n", lock_path("sessionid.tdb"));
+ return -1;
+ }
+
+ tdb_traverse(tdb, show_session, &parseable);
+ tdb_close(tdb);
+
+ return 0;
+}
+
+static int show_share(TDB_CONTEXT *tdb, TDB_DATA kbuf, TDB_DATA dbuf,
+ void *state)
+{
+ struct connections_data crec;
+
+ if (dbuf.dsize != sizeof(crec))
+ return 0;
+
+ memcpy(&crec, dbuf.dptr, sizeof(crec));
+
+ if (crec.cnum == -1)
+ return 0;
+
+ if (!process_exists(crec.pid)) {
+ return 0;
+ }
+
+ d_printf("%-10.10s %5d %-12s %s",
+ crec.name,(int)crec.pid,
+ crec.machine,
+ asctime(LocalTime(&crec.start)));
+
+ return 0;
+}
+
+struct sessionids {
+ int num_entries;
+ struct sessionid *entries;
+};
+
+static int collect_pid(TDB_CONTEXT *tdb, TDB_DATA kbuf, TDB_DATA dbuf,
+ void *state)
+{
+ struct sessionids *ids = (struct sessionids *)state;
+ struct sessionid sessionid;
+
+ if (dbuf.dsize != sizeof(sessionid))
+ return 0;
+
+ memcpy(&sessionid, dbuf.dptr, sizeof(sessionid));
+
+ if (!process_exists(sessionid.pid))
+ return 0;
+
+ ids->num_entries += 1;
+ ids->entries = Realloc(ids->entries,
+ sizeof(struct sessionid) * ids->num_entries);
+ ids->entries[ids->num_entries-1] = sessionid;
+
+ return 0;
+}
+
+static int show_share_parseable(TDB_CONTEXT *tdb, TDB_DATA kbuf, TDB_DATA dbuf,
+ void *state)
+{
+ struct sessionids *ids = (struct sessionids *)state;
+ struct connections_data crec;
+ int i;
+ BOOL guest = True;
+
+ if (dbuf.dsize != sizeof(crec))
+ return 0;
+
+ memcpy(&crec, dbuf.dptr, sizeof(crec));
+
+ if (crec.cnum == -1)
+ return 0;
+
+ if (!process_exists(crec.pid)) {
+ return 0;
+ }
+
+ for (i=0; i<ids->num_entries; i++) {
+ if (ids->entries[i].pid == crec.pid) {
+ guest = False;
+ break;
+ }
+ }
+
+ d_printf("%s\\%d\\%s\\%s\\%s\\%s\\%s",
+ crec.name,(int)crec.pid,
+ guest ? "" : uidtoname(ids->entries[i].uid),
+ guest ? "" : gidtoname(ids->entries[i].gid),
+ crec.machine,
+ guest ? "" : ids->entries[i].hostname,
+ asctime(LocalTime(&crec.start)));
+
+ return 0;
+}
+
+static int net_status_shares_parseable(int argc, const char **argv)
+{
+ struct sessionids ids;
+ TDB_CONTEXT *tdb;
+
+ ids.num_entries = 0;
+ ids.entries = NULL;
+
+ tdb = tdb_open_log(lock_path("sessionid.tdb"), 0,
+ TDB_DEFAULT, O_RDONLY, 0);
+
+ if (tdb == NULL) {
+ d_printf("%s not initialised\n", lock_path("sessionid.tdb"));
+ return -1;
+ }
+
+ tdb_traverse(tdb, collect_pid, &ids);
+ tdb_close(tdb);
+
+ tdb = tdb_open_log(lock_path("connections.tdb"), 0,
+ TDB_DEFAULT, O_RDONLY, 0);
+
+ if (tdb == NULL) {
+ d_printf("%s not initialised\n", lock_path("connections.tdb"));
+ d_printf("This is normal if no SMB client has ever connected "
+ "to your server.\n");
+ return -1;
+ }
+
+ tdb_traverse(tdb, show_share_parseable, &ids);
+ tdb_close(tdb);
+
+ SAFE_FREE(ids.entries);
+
+ return 0;
+}
+
+static int net_status_shares(int argc, const char **argv)
+{
+ TDB_CONTEXT *tdb;
+
+ if (argc == 0) {
+
+ d_printf("\nService pid machine "
+ "Connected at\n");
+ d_printf("-------------------------------------"
+ "------------------\n");
+
+ tdb = tdb_open_log(lock_path("connections.tdb"), 0,
+ TDB_DEFAULT, O_RDONLY, 0);
+
+ if (tdb == NULL) {
+ d_printf("%s not initialised\n",
+ lock_path("connections.tdb"));
+ d_printf("This is normal if no SMB client has ever "
+ "connected to your server.\n");
+ return -1;
+ }
+
+ tdb_traverse(tdb, show_share, NULL);
+ tdb_close(tdb);
+
+ return 0;
+ }
+
+ if ((argc != 1) || !strequal(argv[0], "parseable")) {
+ return net_help_status(argc, argv);
+ }
+
+ return net_status_shares_parseable(argc, argv);
+}
+
+int net_status(int argc, const char **argv)
+{
+ struct functable func[] = {
+ {"sessions", net_status_sessions},
+ {"shares", net_status_shares},
+ {NULL, NULL}
+ };
+ return net_run_function(argc, argv, func, net_help_status);
+}
diff --git a/source/utils/net_time.c b/source/utils/net_time.c
new file mode 100644
index 00000000000..45c17838055
--- /dev/null
+++ b/source/utils/net_time.c
@@ -0,0 +1,180 @@
+/*
+ Samba Unix/Linux SMB client library
+ net time command
+ Copyright (C) 2001 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 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 "../utils/net.h"
+
+
+/*
+ return the time on a server. This does not require any authentication
+*/
+static time_t cli_servertime(const char *host, struct in_addr *ip, int *zone)
+{
+ struct nmb_name calling, called;
+ time_t ret = 0;
+ struct cli_state *cli = NULL;
+
+ cli = cli_initialise(NULL);
+ if (!cli) goto done;
+
+ if (!cli_connect(cli, host, ip)) {
+ fprintf(stderr,"Can't contact server\n");
+ goto done;
+ }
+
+ make_nmb_name(&calling, global_myname(), 0x0);
+ if (host) {
+ make_nmb_name(&called, host, 0x20);
+ } else {
+ make_nmb_name(&called, "*SMBSERVER", 0x20);
+ }
+
+ if (!cli_session_request(cli, &calling, &called)) {
+ fprintf(stderr,"Session request failed\n");
+ goto done;
+ }
+ if (!cli_negprot(cli)) {
+ fprintf(stderr,"Protocol negotiation failed\n");
+ goto done;
+ }
+
+ ret = cli->servertime;
+ if (zone) *zone = cli->serverzone;
+
+done:
+ if (cli) cli_shutdown(cli);
+ return ret;
+}
+
+/* find the servers time on the opt_host host */
+static time_t nettime(int *zone)
+{
+ return cli_servertime(opt_host, opt_have_ip? &opt_dest_ip : NULL, zone);
+}
+
+/* return a time as a string ready to be passed to /bin/date */
+static char *systime(time_t t)
+{
+ static fstring s;
+ struct tm *tm;
+
+ tm = localtime(&t);
+
+ fstr_sprintf(s, "%02d%02d%02d%02d%04d.%02d",
+ tm->tm_mon+1, tm->tm_mday, tm->tm_hour,
+ tm->tm_min, tm->tm_year + 1900, tm->tm_sec);
+ return s;
+}
+
+int net_time_usage(int argc, const char **argv)
+{
+ d_printf(
+"net time\n\tdisplays time on a server\n\n"\
+"net time system\n\tdisplays time on a server in a format ready for /bin/date\n\n"\
+"net time set\n\truns /bin/date with the time from the server\n\n"\
+"net time zone\n\tdisplays the timezone in hours from GMT on the remote computer\n\n"\
+"\n");
+ net_common_flags_usage(argc, argv);
+ return -1;
+}
+
+/* try to set the system clock using /bin/date */
+static int net_time_set(int argc, const char **argv)
+{
+ time_t t = nettime(NULL);
+ char *cmd;
+
+ if (t == 0) return -1;
+
+ /* yes, I know this is cheesy. Use "net time system" if you want to
+ roll your own. I'm putting this in as it works on a large number
+ of systems and the user has a choice in whether its used or not */
+ asprintf(&cmd, "/bin/date %s", systime(t));
+ system(cmd);
+ free(cmd);
+
+ return 0;
+}
+
+/* display the time on a remote box in a format ready for /bin/date */
+static int net_time_system(int argc, const char **argv)
+{
+ time_t t = nettime(NULL);
+
+ if (t == 0) return -1;
+
+ printf("%s\n", systime(t));
+
+ return 0;
+}
+
+/* display the time on a remote box in a format ready for /bin/date */
+static int net_time_zone(int argc, const char **argv)
+{
+ int zone = 0;
+ int hours, mins;
+ char zsign;
+ time_t t;
+
+ t = nettime(&zone);
+
+ if (t == 0) return -1;
+
+ zsign = (zone > 0) ? '-' : '+';
+ if (zone < 0) zone = -zone;
+
+ zone /= 60;
+ hours = zone / 60;
+ mins = zone % 60;
+
+ printf("%c%02d%02d\n", zsign, hours, mins);
+
+ return 0;
+}
+
+/* display or set the time on a host */
+int net_time(int argc, const char **argv)
+{
+ time_t t;
+ struct functable func[] = {
+ {"SYSTEM", net_time_system},
+ {"SET", net_time_set},
+ {"ZONE", net_time_zone},
+ {NULL, NULL}
+ };
+
+ if (!opt_host && !opt_have_ip &&
+ !find_master_ip(opt_target_workgroup, &opt_dest_ip)) {
+ d_printf("Could not locate a time server. Try "\
+ "specifying a target host.\n");
+ net_time_usage(argc,argv);
+ return -1;
+ }
+
+ if (argc != 0) {
+ return net_run_function(argc, argv, func, net_time_usage);
+ }
+
+ /* default - print the time */
+ t = cli_servertime(opt_host, opt_have_ip? &opt_dest_ip : NULL, NULL);
+ if (t == 0) return -1;
+
+ d_printf("%s", ctime(&t));
+ return 0;
+}
diff --git a/source/utils/nmblookup.c b/source/utils/nmblookup.c
new file mode 100644
index 00000000000..3c5a22841ea
--- /dev/null
+++ b/source/utils/nmblookup.c
@@ -0,0 +1,292 @@
+/*
+ Unix SMB/CIFS implementation.
+ NBT client - used to lookup netbios names
+ Copyright (C) Andrew Tridgell 1994-1998
+ Copyright (C) Jelmer Vernooij 2003 (Conversion to popt)
+
+ This program is free software; you can 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.
+
+*/
+
+#define NO_SYSLOG
+
+#include "includes.h"
+
+extern BOOL AllowDebugChange;
+
+static BOOL give_flags = False;
+static BOOL use_bcast = True;
+static BOOL got_bcast = False;
+static struct in_addr bcast_addr;
+static BOOL recursion_desired = False;
+static BOOL translate_addresses = False;
+static int ServerFD= -1;
+static int RootPort = False;
+static BOOL find_status=False;
+
+/****************************************************************************
+ open the socket communication
+ **************************************************************************/
+static BOOL open_sockets(void)
+{
+ ServerFD = open_socket_in( SOCK_DGRAM,
+ (RootPort ? 137 : 0),
+ (RootPort ? 0 : 3),
+ interpret_addr(lp_socket_address()), True );
+
+ if (ServerFD == -1)
+ return(False);
+
+ set_socket_options( ServerFD, "SO_BROADCAST" );
+
+ DEBUG(3, ("Socket opened.\n"));
+ return True;
+}
+
+/****************************************************************************
+turn a node status flags field into a string
+****************************************************************************/
+static char *node_status_flags(unsigned char flags)
+{
+ static fstring ret;
+ fstrcpy(ret,"");
+
+ fstrcat(ret, (flags & 0x80) ? "<GROUP> " : " ");
+ if ((flags & 0x60) == 0x00) fstrcat(ret,"B ");
+ if ((flags & 0x60) == 0x20) fstrcat(ret,"P ");
+ if ((flags & 0x60) == 0x40) fstrcat(ret,"M ");
+ if ((flags & 0x60) == 0x60) fstrcat(ret,"H ");
+ if (flags & 0x10) fstrcat(ret,"<DEREGISTERING> ");
+ if (flags & 0x08) fstrcat(ret,"<CONFLICT> ");
+ if (flags & 0x04) fstrcat(ret,"<ACTIVE> ");
+ if (flags & 0x02) fstrcat(ret,"<PERMANENT> ");
+
+ return ret;
+}
+
+/****************************************************************************
+turn the NMB Query flags into a string
+****************************************************************************/
+static char *query_flags(int flags)
+{
+ static fstring ret1;
+ fstrcpy(ret1, "");
+
+ if (flags & NM_FLAGS_RS) fstrcat(ret1, "Response ");
+ if (flags & NM_FLAGS_AA) fstrcat(ret1, "Authoritative ");
+ if (flags & NM_FLAGS_TC) fstrcat(ret1, "Truncated ");
+ if (flags & NM_FLAGS_RD) fstrcat(ret1, "Recursion_Desired ");
+ if (flags & NM_FLAGS_RA) fstrcat(ret1, "Recursion_Available ");
+ if (flags & NM_FLAGS_B) fstrcat(ret1, "Broadcast ");
+
+ return ret1;
+}
+
+/****************************************************************************
+do a node status query
+****************************************************************************/
+static void do_node_status(int fd, const char *name, int type, struct in_addr ip)
+{
+ struct nmb_name nname;
+ int count, i, j;
+ struct node_status *status;
+ fstring cleanname;
+
+ d_printf("Looking up status of %s\n",inet_ntoa(ip));
+ make_nmb_name(&nname, name, type);
+ status = node_status_query(fd,&nname,ip, &count);
+ if (status) {
+ for (i=0;i<count;i++) {
+ pull_ascii_fstring(cleanname, status[i].name);
+ for (j=0;cleanname[j];j++) {
+ if (!isprint((int)cleanname[j])) cleanname[j] = '.';
+ }
+ d_printf("\t%-15s <%02x> - %s\n",
+ cleanname,status[i].type,
+ node_status_flags(status[i].flags));
+ }
+ SAFE_FREE(status);
+ }
+ d_printf("\n");
+}
+
+
+/****************************************************************************
+send out one query
+****************************************************************************/
+static BOOL query_one(const char *lookup, unsigned int lookup_type)
+{
+ int j, count, flags = 0;
+ struct in_addr *ip_list=NULL;
+
+ if (got_bcast) {
+ d_printf("querying %s on %s\n", lookup, inet_ntoa(bcast_addr));
+ ip_list = name_query(ServerFD,lookup,lookup_type,use_bcast,
+ use_bcast?True:recursion_desired,
+ bcast_addr,&count, &flags, NULL);
+ } else {
+ struct in_addr *bcast;
+ for (j=iface_count() - 1;
+ !ip_list && j >= 0;
+ j--) {
+ bcast = iface_n_bcast(j);
+ d_printf("querying %s on %s\n",
+ lookup, inet_ntoa(*bcast));
+ ip_list = name_query(ServerFD,lookup,lookup_type,
+ use_bcast,
+ use_bcast?True:recursion_desired,
+ *bcast,&count, &flags, NULL);
+ }
+ }
+
+ if (!ip_list) return False;
+
+ if (give_flags)
+ d_printf("Flags: %s\n", query_flags(flags));
+
+ for (j=0;j<count;j++) {
+ if (translate_addresses) {
+ struct hostent *host = gethostbyaddr((char *)&ip_list[j], sizeof(ip_list[j]), AF_INET);
+ if (host) {
+ d_printf("%s, ", host -> h_name);
+ }
+ }
+ d_printf("%s %s<%02x>\n",inet_ntoa(ip_list[j]),lookup, lookup_type);
+ }
+
+ /* We can only do find_status if the ip address returned
+ was valid - ie. name_query returned true.
+ */
+ if (find_status) {
+ do_node_status(ServerFD, lookup, lookup_type, ip_list[0]);
+ }
+
+ safe_free(ip_list);
+
+ return (ip_list != NULL);
+}
+
+
+/****************************************************************************
+ main program
+****************************************************************************/
+int main(int argc,char *argv[])
+{
+ int opt;
+ unsigned int lookup_type = 0x0;
+ fstring lookup;
+ static BOOL find_master=False;
+ static BOOL lookup_by_ip = False;
+ poptContext pc;
+
+ struct poptOption long_options[] = {
+ POPT_AUTOHELP
+ { "broadcast", 'B', POPT_ARG_STRING, NULL, 'B', "Specify address to use for broadcasts", "BROADCAST-ADDRESS" },
+ { "flags", 'f', POPT_ARG_VAL, &give_flags, True, "List the NMB flags returned" },
+ { "unicast", 'U', POPT_ARG_STRING, NULL, 'U', "Specify address to use for unicast" },
+ { "master-browser", 'M', POPT_ARG_VAL, &find_master, True, "Search for a master browser" },
+ { "recursion", 'R', POPT_ARG_VAL, &recursion_desired, True, "Set recursion desired in package" },
+ { "status", 'S', POPT_ARG_VAL, &find_status, True, "Lookup node status as well" },
+ { "translate", 'T', POPT_ARG_NONE, NULL, 'T', "Translate IP addresses into names" },
+ { "root-port", 'r', POPT_ARG_VAL, &RootPort, True, "Use root port 137 (Win95 only replies to this)" },
+ { "lookup-by-ip", 'A', POPT_ARG_VAL, &lookup_by_ip, True, "Do a node status on <name> as an IP Address" },
+ POPT_COMMON_SAMBA
+ POPT_COMMON_CONNECTION
+ { 0, 0, 0, 0 }
+ };
+
+ *lookup = 0;
+
+ setup_logging(argv[0],True);
+
+ pc = poptGetContext("nmblookup", argc, (const char **)argv, long_options,
+ POPT_CONTEXT_KEEP_FIRST);
+
+ poptSetOtherOptionHelp(pc, "<NODE> ...");
+
+ while ((opt = poptGetNextOpt(pc)) != -1) {
+ switch (opt) {
+ case 'B':
+ bcast_addr = *interpret_addr2(poptGetOptArg(pc));
+ got_bcast = True;
+ use_bcast = True;
+ break;
+ case 'U':
+ bcast_addr = *interpret_addr2(poptGetOptArg(pc));
+ got_bcast = True;
+ use_bcast = False;
+ break;
+ case 'T':
+ translate_addresses = !translate_addresses;
+ break;
+ }
+ }
+
+ poptGetArg(pc); /* Remove argv[0] */
+
+ if(!poptPeekArg(pc)) {
+ poptPrintUsage(pc, stderr, 0);
+ exit(1);
+ }
+
+ if (!lp_load(dyn_CONFIGFILE,True,False,False)) {
+ fprintf(stderr, "Can't load %s - run testparm to debug it\n", dyn_CONFIGFILE);
+ }
+
+ load_interfaces();
+ if (!open_sockets()) return(1);
+
+ while(poptPeekArg(pc))
+ {
+ char *p;
+ struct in_addr ip;
+
+ fstrcpy(lookup,poptGetArg(pc));
+
+ if(lookup_by_ip)
+ {
+ ip = *interpret_addr2(lookup);
+ fstrcpy(lookup,"*");
+ do_node_status(ServerFD, lookup, lookup_type, ip);
+ continue;
+ }
+
+ if (find_master) {
+ if (*lookup == '-') {
+ fstrcpy(lookup,"\01\02__MSBROWSE__\02");
+ lookup_type = 1;
+ } else {
+ lookup_type = 0x1d;
+ }
+ }
+
+ p = strchr_m(lookup,'#');
+ if (p) {
+ *p = '\0';
+ sscanf(++p,"%x",&lookup_type);
+ }
+
+ if (!query_one(lookup, lookup_type)) {
+ d_printf( "name_query failed to find name %s", lookup );
+ if( 0 != lookup_type )
+ d_printf( "#%02x", lookup_type );
+ d_printf( "\n" );
+ }
+ }
+
+ poptFreeContext(pc);
+
+ return(0);
+}
diff --git a/source/utils/ntlm_auth.c b/source/utils/ntlm_auth.c
new file mode 100644
index 00000000000..2213a9bae37
--- /dev/null
+++ b/source/utils/ntlm_auth.c
@@ -0,0 +1,2182 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ Winbind status program.
+
+ Copyright (C) Tim Potter 2000-2002
+ Copyright (C) Andrew Bartlett <abartlet@samba.org> 2003
+ 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 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"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_WINBIND
+
+#define SQUID_BUFFER_SIZE 2010
+
+enum stdio_helper_mode {
+ SQUID_2_4_BASIC,
+ SQUID_2_5_BASIC,
+ SQUID_2_5_NTLMSSP,
+ NTLMSSP_CLIENT_1,
+ GSS_SPNEGO,
+ GSS_SPNEGO_CLIENT,
+ NUM_HELPER_MODES
+};
+
+enum ntlm_break {
+ BREAK_NONE,
+ BREAK_LM,
+ BREAK_NT,
+ NO_LM,
+ NO_NT
+};
+
+typedef void (*stdio_helper_function)(enum stdio_helper_mode stdio_helper_mode,
+ char *buf, int length);
+
+static void manage_squid_basic_request (enum stdio_helper_mode stdio_helper_mode,
+ char *buf, int length);
+
+static void manage_squid_ntlmssp_request (enum stdio_helper_mode stdio_helper_mode,
+ char *buf, int length);
+
+static void manage_client_ntlmssp_request (enum stdio_helper_mode stdio_helper_mode,
+ char *buf, int length);
+
+static void manage_gss_spnego_request (enum stdio_helper_mode stdio_helper_mode,
+ char *buf, int length);
+
+static void manage_gss_spnego_client_request (enum stdio_helper_mode stdio_helper_mode,
+ char *buf, int length);
+
+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_squid_ntlmssp_request},
+ { NTLMSSP_CLIENT_1, "ntlmssp-client-1", manage_client_ntlmssp_request},
+ { GSS_SPNEGO, "gss-spnego", manage_gss_spnego_request},
+ { GSS_SPNEGO_CLIENT, "gss-spnego-client", manage_gss_spnego_client_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 DATA_BLOB opt_challenge;
+static DATA_BLOB opt_lm_response;
+static DATA_BLOB opt_nt_response;
+static int request_lm_key;
+static int request_nt_key;
+
+
+static char winbind_separator(void)
+{
+ struct winbindd_response response;
+ static BOOL got_sep;
+ static char sep;
+
+ if (got_sep)
+ return sep;
+
+ ZERO_STRUCT(response);
+
+ /* Send off request */
+
+ if (winbindd_request(WINBINDD_INFO, NULL, &response) !=
+ NSS_STATUS_SUCCESS) {
+ d_printf("could not obtain winbind separator!\n");
+ return '\\';
+ }
+
+ sep = response.data.info.winbind_separator;
+ got_sep = True;
+
+ if (!sep) {
+ d_printf("winbind separator was NULL!\n");
+ return '\\';
+ }
+
+ return sep;
+}
+
+static const char *get_winbind_domain(void)
+{
+ struct winbindd_response response;
+
+ static fstring winbind_domain;
+ if (*winbind_domain) {
+ return winbind_domain;
+ }
+
+ ZERO_STRUCT(response);
+
+ /* Send off request */
+
+ if (winbindd_request(WINBINDD_DOMAIN_NAME, NULL, &response) !=
+ NSS_STATUS_SUCCESS) {
+ DEBUG(0, ("could not obtain winbind domain name!\n"));
+ return NULL;
+ }
+
+ fstrcpy(winbind_domain, response.data.domain_name);
+
+ return winbind_domain;
+
+}
+
+static const char *get_winbind_netbios_name(void)
+{
+ struct winbindd_response response;
+
+ static fstring winbind_netbios_name;
+
+ if (*winbind_netbios_name) {
+ return winbind_netbios_name;
+ }
+
+ ZERO_STRUCT(response);
+
+ /* Send off request */
+
+ if (winbindd_request(WINBINDD_NETBIOS_NAME, NULL, &response) !=
+ NSS_STATUS_SUCCESS) {
+ DEBUG(0, ("could not obtain winbind netbios name!\n"));
+ return NULL;
+ }
+
+ fstrcpy(winbind_netbios_name, response.data.netbios_name);
+
+ return winbind_netbios_name;
+
+}
+
+/* Authenticate a user with a plaintext password */
+
+static BOOL check_plaintext_auth(const char *user, const char *pass, BOOL stdout_diagnostics)
+{
+ struct winbindd_request request;
+ struct winbindd_response response;
+ NSS_STATUS result;
+
+ /* Send off request */
+
+ ZERO_STRUCT(request);
+ ZERO_STRUCT(response);
+
+ fstrcpy(request.data.auth.user, user);
+ fstrcpy(request.data.auth.pass, pass);
+
+ result = winbindd_request(WINBINDD_PAM_AUTH, &request, &response);
+
+ /* Display response */
+
+ if (stdout_diagnostics) {
+ if ((result != NSS_STATUS_SUCCESS) && (response.data.auth.nt_status == 0)) {
+ d_printf("Reading winbind reply failed! (0x01)\n");
+ }
+
+ d_printf("%s: %s (0x%x)\n",
+ response.data.auth.nt_status_string,
+ response.data.auth.error_string,
+ response.data.auth.nt_status);
+ } else {
+ if ((result != NSS_STATUS_SUCCESS) && (response.data.auth.nt_status == 0)) {
+ DEBUG(1, ("Reading winbind reply failed! (0x01)\n"));
+ }
+
+ DEBUG(3, ("%s: %s (0x%x)\n",
+ response.data.auth.nt_status_string,
+ response.data.auth.error_string,
+ response.data.auth.nt_status));
+ }
+
+ return (result == NSS_STATUS_SUCCESS);
+}
+
+/* authenticate a user with an encrypted username/password */
+
+static NTSTATUS contact_winbind_auth_crap(const char *username,
+ const char *domain,
+ const char *workstation,
+ const DATA_BLOB *challenge,
+ const DATA_BLOB *lm_response,
+ const DATA_BLOB *nt_response,
+ uint32 flags,
+ uint8 lm_key[8],
+ uint8 nt_key[16],
+ char **error_string,
+ char **unix_name)
+{
+ NTSTATUS nt_status;
+ NSS_STATUS result;
+ struct winbindd_request request;
+ struct winbindd_response response;
+
+ static uint8 zeros[16];
+
+ ZERO_STRUCT(request);
+ ZERO_STRUCT(response);
+
+ request.flags = flags;
+
+ if (push_utf8_fstring(request.data.auth_crap.user, username) == -1) {
+ *error_string = smb_xstrdup(
+ "unable to create utf8 string for username");
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ if (push_utf8_fstring(request.data.auth_crap.domain, domain) == -1) {
+ *error_string = smb_xstrdup(
+ "unable to create utf8 string for domain");
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ if (push_utf8_fstring(request.data.auth_crap.workstation,
+ workstation) == -1) {
+ *error_string = smb_xstrdup(
+ "unable to create utf8 string for workstation");
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ memcpy(request.data.auth_crap.chal, challenge->data, MIN(challenge->length, 8));
+
+ if (lm_response && lm_response->length) {
+ memcpy(request.data.auth_crap.lm_resp, lm_response->data, MIN(lm_response->length, sizeof(request.data.auth_crap.lm_resp)));
+ request.data.auth_crap.lm_resp_len = lm_response->length;
+ }
+
+ if (nt_response && nt_response->length) {
+ memcpy(request.data.auth_crap.nt_resp, nt_response->data, MIN(nt_response->length, sizeof(request.data.auth_crap.nt_resp)));
+ request.data.auth_crap.nt_resp_len = nt_response->length;
+ }
+
+ result = winbindd_request(WINBINDD_PAM_AUTH_CRAP, &request, &response);
+
+ /* Display response */
+
+ if ((result != NSS_STATUS_SUCCESS) && (response.data.auth.nt_status == 0)) {
+ nt_status = NT_STATUS_UNSUCCESSFUL;
+ if (error_string)
+ *error_string = smb_xstrdup("Reading winbind reply failed!");
+ return nt_status;
+ }
+
+ nt_status = (NT_STATUS(response.data.auth.nt_status));
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ if (error_string)
+ *error_string = smb_xstrdup(response.data.auth.error_string);
+ return nt_status;
+ }
+
+ if ((flags & WBFLAG_PAM_LMKEY) && lm_key
+ && (memcmp(zeros, response.data.auth.first_8_lm_hash,
+ sizeof(response.data.auth.first_8_lm_hash)) != 0)) {
+ memcpy(lm_key, response.data.auth.first_8_lm_hash,
+ sizeof(response.data.auth.first_8_lm_hash));
+ }
+ if ((flags & WBFLAG_PAM_NTKEY) && nt_key
+ && (memcmp(zeros, response.data.auth.nt_session_key,
+ sizeof(response.data.auth.nt_session_key)) != 0)) {
+ memcpy(nt_key, response.data.auth.nt_session_key,
+ sizeof(response.data.auth.nt_session_key));
+ }
+
+ if (flags & WBFLAG_PAM_UNIX_NAME) {
+ if (pull_utf8_allocate(unix_name, (char *)response.extra_data) == -1) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ }
+
+ return nt_status;
+}
+
+static NTSTATUS winbind_pw_check(struct ntlmssp_state *ntlmssp_state, DATA_BLOB *nt_session_key, DATA_BLOB *lm_session_key)
+{
+ static const char zeros[16];
+ NTSTATUS nt_status;
+ char *error_string;
+ uint8 lm_key[8];
+ uint8 nt_key[16];
+ char *unix_name;
+
+ nt_status = contact_winbind_auth_crap(ntlmssp_state->user, ntlmssp_state->domain,
+ ntlmssp_state->workstation,
+ &ntlmssp_state->chal,
+ &ntlmssp_state->lm_resp,
+ &ntlmssp_state->nt_resp,
+ WBFLAG_PAM_LMKEY | WBFLAG_PAM_NTKEY | WBFLAG_PAM_UNIX_NAME,
+ lm_key, nt_key,
+ &error_string, &unix_name);
+
+ if (NT_STATUS_IS_OK(nt_status)) {
+ if (memcmp(lm_key, zeros, 8) != 0) {
+ *lm_session_key = data_blob(NULL, 16);
+ memcpy(lm_session_key->data, lm_key, 8);
+ memset(lm_session_key->data+8, '\0', 8);
+ }
+
+ if (memcmp(nt_key, zeros, 16) != 0) {
+ *nt_session_key = data_blob(nt_key, 16);
+ }
+ ntlmssp_state->auth_context = talloc_strdup(ntlmssp_state->mem_ctx, unix_name);
+ SAFE_FREE(unix_name);
+ } else {
+ DEBUG(NT_STATUS_EQUAL(nt_status, NT_STATUS_ACCESS_DENIED) ? 0 : 3,
+ ("Login for user [%s]\\[%s]@[%s] failed due to [%s]\n",
+ ntlmssp_state->domain, ntlmssp_state->user, ntlmssp_state->workstation, error_string ? error_string : "unknown error (NULL)"));
+ ntlmssp_state->auth_context = NULL;
+ }
+ return nt_status;
+}
+
+static NTSTATUS local_pw_check(struct ntlmssp_state *ntlmssp_state, DATA_BLOB *nt_session_key, DATA_BLOB *lm_session_key)
+{
+ static const char zeros[16];
+ NTSTATUS nt_status;
+ uint8 lm_key[8];
+ uint8 nt_key[16];
+ uint8 lm_pw[16], nt_pw[16];
+
+ nt_lm_owf_gen (opt_password, nt_pw, lm_pw);
+
+ nt_status = ntlm_password_check(ntlmssp_state->mem_ctx,
+ &ntlmssp_state->chal,
+ &ntlmssp_state->lm_resp,
+ &ntlmssp_state->nt_resp,
+ ntlmssp_state->user,
+ ntlmssp_state->user,
+ ntlmssp_state->domain,
+ lm_pw, nt_pw, nt_session_key, lm_session_key);
+
+ if (NT_STATUS_IS_OK(nt_status)) {
+ if (memcmp(lm_key, zeros, 8) != 0) {
+ *lm_session_key = data_blob(NULL, 16);
+ memcpy(lm_session_key->data, lm_key, 8);
+ memset(lm_session_key->data+8, '\0', 8);
+ }
+
+ if (memcmp(nt_key, zeros, 16) != 0) {
+ *nt_session_key = data_blob(nt_key, 16);
+ }
+ ntlmssp_state->auth_context = talloc_asprintf(ntlmssp_state->mem_ctx, "%s%c%s", ntlmssp_state->domain, *lp_winbind_separator(), ntlmssp_state->user);
+ } else {
+ DEBUG(3, ("Login for user [%s]\\[%s]@[%s] failed due to [%s]\n",
+ ntlmssp_state->domain, ntlmssp_state->user, ntlmssp_state->workstation,
+ nt_errstr(nt_status)));
+ ntlmssp_state->auth_context = NULL;
+ }
+ return nt_status;
+}
+
+static NTSTATUS ntlm_auth_start_ntlmssp_client(NTLMSSP_STATE **client_ntlmssp_state)
+{
+ NTSTATUS status;
+ if ( (opt_username == NULL) || (opt_domain == NULL) ) {
+ DEBUG(1, ("Need username and domain for NTLMSSP\n"));
+ return status;
+ }
+
+ status = ntlmssp_client_start(client_ntlmssp_state);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(1, ("Could not start NTLMSSP client: %s\n",
+ nt_errstr(status)));
+ ntlmssp_end(client_ntlmssp_state);
+ return status;
+ }
+
+ status = ntlmssp_set_username(*client_ntlmssp_state, opt_username);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(1, ("Could not set username: %s\n",
+ nt_errstr(status)));
+ ntlmssp_end(client_ntlmssp_state);
+ return status;
+ }
+
+ status = ntlmssp_set_domain(*client_ntlmssp_state, opt_domain);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(1, ("Could not set domain: %s\n",
+ nt_errstr(status)));
+ ntlmssp_end(client_ntlmssp_state);
+ return status;
+ }
+
+ status = ntlmssp_set_password(*client_ntlmssp_state, opt_password);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(1, ("Could not set password: %s\n",
+ nt_errstr(status)));
+ ntlmssp_end(client_ntlmssp_state);
+ return status;
+ }
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS ntlm_auth_start_ntlmssp_server(NTLMSSP_STATE **ntlmssp_state)
+{
+ NTSTATUS status = ntlmssp_server_start(ntlmssp_state);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(1, ("Could not start NTLMSSP client: %s\n",
+ nt_errstr(status)));
+ return status;
+ }
+
+ /* Have we been given a local password, or should we ask winbind? */
+ if (opt_password) {
+ (*ntlmssp_state)->check_password = local_pw_check;
+ (*ntlmssp_state)->get_domain = lp_workgroup;
+ (*ntlmssp_state)->get_global_myname = global_myname;
+ } else {
+ (*ntlmssp_state)->check_password = winbind_pw_check;
+ (*ntlmssp_state)->get_domain = get_winbind_domain;
+ (*ntlmssp_state)->get_global_myname = get_winbind_netbios_name;
+ }
+ return NT_STATUS_OK;
+}
+
+static void manage_squid_ntlmssp_request(enum stdio_helper_mode stdio_helper_mode,
+ char *buf, int length)
+{
+ static NTLMSSP_STATE *ntlmssp_state = NULL;
+ DATA_BLOB request, reply;
+ NTSTATUS nt_status;
+
+ if (strlen(buf) < 2) {
+ DEBUG(1, ("NTLMSSP query [%s] invalid", buf));
+ x_fprintf(x_stdout, "BH\n");
+ return;
+ }
+
+ if (strlen(buf) > 3) {
+ request = base64_decode_data_blob(buf + 3);
+ } else {
+ request = data_blob(NULL, 0);
+ }
+
+ if ((strncmp(buf, "PW ", 3) == 0)) {
+ /* The calling application wants us to use a local password (rather than winbindd) */
+
+ opt_password = strndup((const char *)request.data, request.length);
+
+ if (opt_password == NULL) {
+ DEBUG(1, ("Out of memory\n"));
+ x_fprintf(x_stdout, "BH\n");
+ data_blob_free(&request);
+ return;
+ }
+
+ x_fprintf(x_stdout, "OK\n");
+ data_blob_free(&request);
+ return;
+ }
+
+ if (strncmp(buf, "YR", 2) == 0) {
+ if (ntlmssp_state)
+ ntlmssp_end(&ntlmssp_state);
+ } else if (strncmp(buf, "KK", 2) == 0) {
+
+ } else {
+ DEBUG(1, ("NTLMSSP query [%s] invalid", buf));
+ x_fprintf(x_stdout, "BH\n");
+ return;
+ }
+
+ if (!ntlmssp_state) {
+ if (!NT_STATUS_IS_OK(nt_status = ntlm_auth_start_ntlmssp_server(&ntlmssp_state))) {
+ x_fprintf(x_stdout, "BH %s\n", nt_errstr(nt_status));
+ return;
+ }
+ }
+
+ DEBUG(10, ("got NTLMSSP packet:\n"));
+ dump_data(10, (const char *)request.data, request.length);
+
+ nt_status = ntlmssp_update(ntlmssp_state, request, &reply);
+
+ if (NT_STATUS_EQUAL(nt_status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
+ char *reply_base64 = base64_encode_data_blob(reply);
+ x_fprintf(x_stdout, "TT %s\n", reply_base64);
+ SAFE_FREE(reply_base64);
+ data_blob_free(&reply);
+ DEBUG(10, ("NTLMSSP challenge\n"));
+ } else if (NT_STATUS_EQUAL(nt_status, NT_STATUS_ACCESS_DENIED)) {
+ x_fprintf(x_stdout, "BH %s\n", nt_errstr(nt_status));
+ DEBUG(0, ("NTLMSSP BH: %s\n", nt_errstr(nt_status)));
+
+ ntlmssp_end(&ntlmssp_state);
+ } else if (!NT_STATUS_IS_OK(nt_status)) {
+ x_fprintf(x_stdout, "NA %s\n", nt_errstr(nt_status));
+ DEBUG(10, ("NTLMSSP %s\n", nt_errstr(nt_status)));
+ } else {
+ x_fprintf(x_stdout, "AF %s\n", (char *)ntlmssp_state->auth_context);
+ DEBUG(10, ("NTLMSSP OK!\n"));
+ }
+
+ data_blob_free(&request);
+}
+
+static void manage_client_ntlmssp_request(enum stdio_helper_mode stdio_helper_mode,
+ char *buf, int length)
+{
+ static NTLMSSP_STATE *ntlmssp_state = NULL;
+ DATA_BLOB request, reply;
+ NTSTATUS nt_status;
+ BOOL first = False;
+
+ if (strlen(buf) < 2) {
+ DEBUG(1, ("NTLMSSP query [%s] invalid", buf));
+ x_fprintf(x_stdout, "BH\n");
+ return;
+ }
+
+ if (strlen(buf) > 3) {
+ request = base64_decode_data_blob(buf + 3);
+ } else {
+ request = data_blob(NULL, 0);
+ }
+
+ if (strncmp(buf, "PW ", 3) == 0) {
+ /* We asked for a password and obviously got it :-) */
+
+ opt_password = strndup((const char *)request.data, request.length);
+
+ if (opt_password == NULL) {
+ DEBUG(1, ("Out of memory\n"));
+ x_fprintf(x_stdout, "BH\n");
+ data_blob_free(&request);
+ return;
+ }
+
+ x_fprintf(x_stdout, "OK\n");
+ data_blob_free(&request);
+ return;
+ }
+
+ if (opt_password == NULL) {
+
+ /* Request a password from the calling process. After
+ sending it, the calling process should retry asking for the negotiate. */
+
+ DEBUG(10, ("Requesting password\n"));
+ x_fprintf(x_stdout, "PW\n");
+ return;
+ }
+
+ if (strncmp(buf, "YR", 2) == 0) {
+ if (ntlmssp_state)
+ ntlmssp_end(&ntlmssp_state);
+ } else if (strncmp(buf, "TT", 2) == 0) {
+
+ } else {
+ DEBUG(1, ("NTLMSSP query [%s] invalid", buf));
+ x_fprintf(x_stdout, "BH\n");
+ return;
+ }
+
+ if (!ntlmssp_state) {
+ if (!NT_STATUS_IS_OK(nt_status = ntlm_auth_start_ntlmssp_client(&ntlmssp_state))) {
+ x_fprintf(x_stdout, "BH %s\n", nt_errstr(nt_status));
+ return;
+ }
+ first = True;
+ }
+
+ DEBUG(10, ("got NTLMSSP packet:\n"));
+ dump_data(10, (const char *)request.data, request.length);
+
+ nt_status = ntlmssp_update(ntlmssp_state, request, &reply);
+
+ if (NT_STATUS_EQUAL(nt_status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
+ char *reply_base64 = base64_encode_data_blob(reply);
+ if (first) {
+ x_fprintf(x_stdout, "YR %s\n", reply_base64);
+ } else {
+ x_fprintf(x_stdout, "KK %s\n", reply_base64);
+ }
+ SAFE_FREE(reply_base64);
+ data_blob_free(&reply);
+ DEBUG(10, ("NTLMSSP challenge\n"));
+ } else if (NT_STATUS_IS_OK(nt_status)) {
+ x_fprintf(x_stdout, "AF\n");
+ DEBUG(10, ("NTLMSSP OK!\n"));
+ if (ntlmssp_state)
+ ntlmssp_end(&ntlmssp_state);
+ } else {
+ x_fprintf(x_stdout, "BH %s\n", nt_errstr(nt_status));
+ DEBUG(0, ("NTLMSSP BH: %s\n", nt_errstr(nt_status)));
+ if (ntlmssp_state)
+ ntlmssp_end(&ntlmssp_state);
+ }
+
+ data_blob_free(&request);
+}
+
+static void manage_squid_basic_request(enum stdio_helper_mode stdio_helper_mode,
+ char *buf, int length)
+{
+ char *user, *pass;
+ user=buf;
+
+ pass=memchr(buf,' ',length);
+ if (!pass) {
+ DEBUG(2, ("Password not found. Denying access\n"));
+ x_fprintf(x_stderr, "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)) {
+ x_fprintf(x_stdout, "OK\n");
+ } else {
+ x_fprintf(x_stdout, "ERR\n");
+ }
+}
+
+static void offer_gss_spnego_mechs(void) {
+
+ DATA_BLOB token;
+ SPNEGO_DATA spnego;
+ ssize_t len;
+ char *reply_base64;
+
+ pstring principal;
+ pstring myname_lower;
+
+ ZERO_STRUCT(spnego);
+
+ pstrcpy(myname_lower, global_myname());
+ strlower_m(myname_lower);
+
+ pstr_sprintf(principal, "%s$@%s", myname_lower, lp_realm());
+
+ /* Server negTokenInit (mech offerings) */
+ spnego.type = SPNEGO_NEG_TOKEN_INIT;
+ spnego.negTokenInit.mechTypes = smb_xmalloc(sizeof(char *) * 3);
+#ifdef HAVE_KRB5
+ spnego.negTokenInit.mechTypes[0] = smb_xstrdup(OID_KERBEROS5_OLD);
+ spnego.negTokenInit.mechTypes[1] = smb_xstrdup(OID_NTLMSSP);
+ spnego.negTokenInit.mechTypes[2] = NULL;
+#else
+ spnego.negTokenInit.mechTypes[0] = smb_xstrdup(OID_NTLMSSP);
+ spnego.negTokenInit.mechTypes[1] = NULL;
+#endif
+
+
+ spnego.negTokenInit.mechListMIC = data_blob(principal,
+ strlen(principal));
+
+ len = write_spnego_data(&token, &spnego);
+ free_spnego_data(&spnego);
+
+ if (len == -1) {
+ DEBUG(1, ("Could not write SPNEGO data blob\n"));
+ x_fprintf(x_stdout, "BH\n");
+ return;
+ }
+
+ reply_base64 = base64_encode_data_blob(token);
+ x_fprintf(x_stdout, "TT %s *\n", reply_base64);
+
+ SAFE_FREE(reply_base64);
+ data_blob_free(&token);
+ DEBUG(10, ("sent SPNEGO negTokenInit\n"));
+ return;
+}
+
+static void manage_gss_spnego_request(enum stdio_helper_mode stdio_helper_mode,
+ char *buf, int length)
+{
+ static NTLMSSP_STATE *ntlmssp_state = NULL;
+ SPNEGO_DATA request, response;
+ DATA_BLOB token;
+ NTSTATUS status;
+ ssize_t len;
+
+ char *user = NULL;
+ char *domain = NULL;
+
+ const char *reply_code;
+ char *reply_base64;
+ pstring reply_argument;
+
+ if (strlen(buf) < 2) {
+
+ if (ntlmssp_state != NULL) {
+ DEBUG(1, ("Request for initial SPNEGO request where "
+ "we already have a state\n"));
+ x_fprintf(x_stdout, "BH\n");
+ return;
+ }
+
+ DEBUG(1, ("NTLMSSP query [%s] invalid", buf));
+ x_fprintf(x_stdout, "BH\n");
+ return;
+ }
+
+ if ( (strlen(buf) == 2) && (strcmp(buf, "YR") == 0) ) {
+
+ /* Initial request, get the negTokenInit offering
+ mechanisms */
+
+ offer_gss_spnego_mechs();
+ return;
+ }
+
+ /* All subsequent requests are "KK" (Knock, Knock ;)) and have
+ a blob. This might be negTokenInit or negTokenTarg */
+
+ if ( (strlen(buf) <= 3) || (strncmp(buf, "KK", 2) != 0) ) {
+ DEBUG(1, ("GSS-SPNEGO query [%s] invalid\n", buf));
+ x_fprintf(x_stdout, "BH\n");
+ return;
+ }
+
+ token = base64_decode_data_blob(buf + 3);
+ len = read_spnego_data(token, &request);
+ data_blob_free(&token);
+
+ if (len == -1) {
+ DEBUG(1, ("GSS-SPNEGO query [%s] invalid", buf));
+ x_fprintf(x_stdout, "BH\n");
+ return;
+ }
+
+ if (request.type == SPNEGO_NEG_TOKEN_INIT) {
+
+ /* Second request from Client. This is where the
+ client offers its mechanism to use. */
+
+ if ( (request.negTokenInit.mechTypes == NULL) ||
+ (request.negTokenInit.mechTypes[0] == NULL) ) {
+ DEBUG(1, ("Client did not offer any mechanism"));
+ x_fprintf(x_stdout, "BH\n");
+ return;
+ }
+
+ if (strcmp(request.negTokenInit.mechTypes[0], OID_NTLMSSP) == 0) {
+
+ if ( request.negTokenInit.mechToken.data == NULL ) {
+ DEBUG(1, ("Client did not provide NTLMSSP data\n"));
+ x_fprintf(x_stdout, "BH\n");
+ return;
+ }
+
+ if ( ntlmssp_state != NULL ) {
+ DEBUG(1, ("Client wants a new NTLMSSP challenge, but "
+ "already got one\n"));
+ x_fprintf(x_stdout, "BH\n");
+ ntlmssp_end(&ntlmssp_state);
+ return;
+ }
+
+ if (!NT_STATUS_IS_OK(status = ntlm_auth_start_ntlmssp_server(&ntlmssp_state))) {
+ x_fprintf(x_stdout, "BH %s\n", nt_errstr(status));
+ return;
+ }
+
+ DEBUG(10, ("got NTLMSSP packet:\n"));
+ dump_data(10, (const char *)request.negTokenInit.mechToken.data,
+ request.negTokenInit.mechToken.length);
+
+ response.type = SPNEGO_NEG_TOKEN_TARG;
+ response.negTokenTarg.supportedMech = strdup(OID_NTLMSSP);
+ response.negTokenTarg.mechListMIC = data_blob(NULL, 0);
+
+ status = ntlmssp_update(ntlmssp_state,
+ request.negTokenInit.mechToken,
+ &response.negTokenTarg.responseToken);
+ }
+
+#ifdef HAVE_KRB5
+ if (strcmp(request.negTokenInit.mechTypes[0], OID_KERBEROS5_OLD) == 0) {
+
+ char *principal;
+ DATA_BLOB auth_data;
+ DATA_BLOB ap_rep;
+ DATA_BLOB session_key;
+
+ if ( request.negTokenInit.mechToken.data == NULL ) {
+ DEBUG(1, ("Client did not provide Kerberos data\n"));
+ x_fprintf(x_stdout, "BH\n");
+ return;
+ }
+
+ response.type = SPNEGO_NEG_TOKEN_TARG;
+ response.negTokenTarg.supportedMech = strdup(OID_KERBEROS5_OLD);
+ response.negTokenTarg.mechListMIC = data_blob(NULL, 0);
+ response.negTokenTarg.responseToken = data_blob(NULL, 0);
+
+ status = ads_verify_ticket(lp_realm(),
+ &request.negTokenInit.mechToken,
+ &principal, &auth_data, &ap_rep,
+ &session_key);
+
+ /* Now in "principal" we have the name we are
+ authenticated as. */
+
+ if (NT_STATUS_IS_OK(status)) {
+
+ domain = strchr(principal, '@');
+
+ if (domain == NULL) {
+ DEBUG(1, ("Did not get a valid principal "
+ "from ads_verify_ticket\n"));
+ x_fprintf(x_stdout, "BH\n");
+ return;
+ }
+
+ *domain++ = '\0';
+ domain = strdup(domain);
+ user = strdup(principal);
+
+ data_blob_free(&ap_rep);
+ data_blob_free(&auth_data);
+
+ SAFE_FREE(principal);
+ }
+ }
+#endif
+
+ } else {
+
+ if ( (request.negTokenTarg.supportedMech == NULL) ||
+ ( strcmp(request.negTokenTarg.supportedMech, OID_NTLMSSP) != 0 ) ) {
+ /* Kerberos should never send a negTokenTarg, OID_NTLMSSP
+ is the only one we support that sends this stuff */
+ DEBUG(1, ("Got a negTokenTarg for something non-NTLMSSP: %s\n",
+ request.negTokenTarg.supportedMech));
+ x_fprintf(x_stdout, "BH\n");
+ return;
+ }
+
+ if (request.negTokenTarg.responseToken.data == NULL) {
+ DEBUG(1, ("Got a negTokenTarg without a responseToken!\n"));
+ x_fprintf(x_stdout, "BH\n");
+ return;
+ }
+
+ status = ntlmssp_update(ntlmssp_state,
+ request.negTokenTarg.responseToken,
+ &response.negTokenTarg.responseToken);
+
+ response.type = SPNEGO_NEG_TOKEN_TARG;
+ response.negTokenTarg.supportedMech = strdup(OID_NTLMSSP);
+ response.negTokenTarg.mechListMIC = data_blob(NULL, 0);
+
+ if (NT_STATUS_IS_OK(status)) {
+ user = strdup(ntlmssp_state->user);
+ domain = strdup(ntlmssp_state->domain);
+ ntlmssp_end(&ntlmssp_state);
+ }
+ }
+
+ free_spnego_data(&request);
+
+ if (NT_STATUS_IS_OK(status)) {
+ response.negTokenTarg.negResult = SPNEGO_ACCEPT_COMPLETED;
+ reply_code = "AF";
+ pstr_sprintf(reply_argument, "%s\\%s", domain, user);
+ } else if (NT_STATUS_EQUAL(status,
+ NT_STATUS_MORE_PROCESSING_REQUIRED)) {
+ response.negTokenTarg.negResult = SPNEGO_ACCEPT_INCOMPLETE;
+ reply_code = "TT";
+ pstr_sprintf(reply_argument, "*");
+ } else {
+ response.negTokenTarg.negResult = SPNEGO_REJECT;
+ reply_code = "NA";
+ pstrcpy(reply_argument, nt_errstr(status));
+ }
+
+ SAFE_FREE(user);
+ SAFE_FREE(domain);
+
+ len = write_spnego_data(&token, &response);
+ free_spnego_data(&response);
+
+ if (len == -1) {
+ DEBUG(1, ("Could not write SPNEGO data blob\n"));
+ x_fprintf(x_stdout, "BH\n");
+ return;
+ }
+
+ reply_base64 = base64_encode_data_blob(token);
+
+ x_fprintf(x_stdout, "%s %s %s\n",
+ reply_code, reply_base64, reply_argument);
+
+ SAFE_FREE(reply_base64);
+ data_blob_free(&token);
+
+ return;
+}
+
+static NTLMSSP_STATE *client_ntlmssp_state = NULL;
+
+static BOOL manage_client_ntlmssp_init(SPNEGO_DATA spnego)
+{
+ NTSTATUS status;
+ DATA_BLOB null_blob = data_blob(NULL, 0);
+ DATA_BLOB to_server;
+ char *to_server_base64;
+ const char *my_mechs[] = {OID_NTLMSSP, NULL};
+
+ DEBUG(10, ("Got spnego negTokenInit with NTLMSSP\n"));
+
+ if (client_ntlmssp_state != NULL) {
+ DEBUG(1, ("Request for initial SPNEGO request where "
+ "we already have a state\n"));
+ return False;
+ }
+
+ if (!client_ntlmssp_state) {
+ if (!NT_STATUS_IS_OK(status = ntlm_auth_start_ntlmssp_client(&client_ntlmssp_state))) {
+ x_fprintf(x_stdout, "BH %s\n", nt_errstr(status));
+ return False;
+ }
+ }
+
+
+ if (opt_password == NULL) {
+
+ /* Request a password from the calling process. After
+ sending it, the calling process should retry with
+ the negTokenInit. */
+
+ DEBUG(10, ("Requesting password\n"));
+ x_fprintf(x_stdout, "PW\n");
+ return True;
+ }
+
+ spnego.type = SPNEGO_NEG_TOKEN_INIT;
+ spnego.negTokenInit.mechTypes = my_mechs;
+ spnego.negTokenInit.reqFlags = 0;
+ spnego.negTokenInit.mechListMIC = null_blob;
+
+ status = ntlmssp_update(client_ntlmssp_state, null_blob,
+ &spnego.negTokenInit.mechToken);
+
+ if (!NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
+ DEBUG(1, ("Expected MORE_PROCESSING_REQUIRED, got: %s\n",
+ nt_errstr(status)));
+ ntlmssp_end(&client_ntlmssp_state);
+ return False;
+ }
+
+ write_spnego_data(&to_server, &spnego);
+ data_blob_free(&spnego.negTokenInit.mechToken);
+
+ to_server_base64 = base64_encode_data_blob(to_server);
+ data_blob_free(&to_server);
+ x_fprintf(x_stdout, "KK %s\n", to_server_base64);
+ SAFE_FREE(to_server_base64);
+ return True;
+}
+
+static void manage_client_ntlmssp_targ(SPNEGO_DATA spnego)
+{
+ NTSTATUS status;
+ DATA_BLOB null_blob = data_blob(NULL, 0);
+ DATA_BLOB request;
+ DATA_BLOB to_server;
+ char *to_server_base64;
+
+ DEBUG(10, ("Got spnego negTokenTarg with NTLMSSP\n"));
+
+ if (client_ntlmssp_state == NULL) {
+ DEBUG(1, ("Got NTLMSSP tArg without a client state\n"));
+ x_fprintf(x_stdout, "BH\n");
+ ntlmssp_end(&client_ntlmssp_state);
+ return;
+ }
+
+ if (spnego.negTokenTarg.negResult == SPNEGO_REJECT) {
+ x_fprintf(x_stdout, "NA\n");
+ ntlmssp_end(&client_ntlmssp_state);
+ return;
+ }
+
+ if (spnego.negTokenTarg.negResult == SPNEGO_ACCEPT_COMPLETED) {
+ x_fprintf(x_stdout, "AF\n");
+ ntlmssp_end(&client_ntlmssp_state);
+ return;
+ }
+
+ status = ntlmssp_update(client_ntlmssp_state,
+ spnego.negTokenTarg.responseToken,
+ &request);
+
+ if (!NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
+ DEBUG(1, ("Expected MORE_PROCESSING_REQUIRED from "
+ "ntlmssp_client_update, got: %s\n",
+ nt_errstr(status)));
+ x_fprintf(x_stdout, "BH\n");
+ data_blob_free(&request);
+ ntlmssp_end(&client_ntlmssp_state);
+ return;
+ }
+
+ spnego.type = SPNEGO_NEG_TOKEN_TARG;
+ spnego.negTokenTarg.negResult = SPNEGO_ACCEPT_INCOMPLETE;
+ spnego.negTokenTarg.supportedMech = OID_NTLMSSP;
+ spnego.negTokenTarg.responseToken = request;
+ spnego.negTokenTarg.mechListMIC = null_blob;
+
+ write_spnego_data(&to_server, &spnego);
+ data_blob_free(&request);
+
+ to_server_base64 = base64_encode_data_blob(to_server);
+ data_blob_free(&to_server);
+ x_fprintf(x_stdout, "KK %s\n", to_server_base64);
+ SAFE_FREE(to_server_base64);
+ return;
+}
+
+#ifdef HAVE_KRB5
+
+static BOOL manage_client_krb5_init(SPNEGO_DATA spnego)
+{
+ char *principal;
+ DATA_BLOB tkt, to_server;
+ DATA_BLOB session_key_krb5;
+ SPNEGO_DATA reply;
+ char *reply_base64;
+ int retval;
+
+ const char *my_mechs[] = {OID_KERBEROS5_OLD, NULL};
+ ssize_t len;
+
+ if ( (spnego.negTokenInit.mechListMIC.data == NULL) ||
+ (spnego.negTokenInit.mechListMIC.length == 0) ) {
+ DEBUG(1, ("Did not get a principal for krb5\n"));
+ return False;
+ }
+
+ principal = malloc(spnego.negTokenInit.mechListMIC.length+1);
+
+ if (principal == NULL) {
+ DEBUG(1, ("Could not malloc principal\n"));
+ return False;
+ }
+
+ memcpy(principal, spnego.negTokenInit.mechListMIC.data,
+ spnego.negTokenInit.mechListMIC.length);
+ principal[spnego.negTokenInit.mechListMIC.length] = '\0';
+
+ retval = cli_krb5_get_ticket(principal, 0, &tkt, &session_key_krb5);
+
+ if (retval) {
+
+ pstring user;
+
+ /* Let's try to first get the TGT, for that we need a
+ password. */
+
+ if (opt_password == NULL) {
+ DEBUG(10, ("Requesting password\n"));
+ x_fprintf(x_stdout, "PW\n");
+ return True;
+ }
+
+ pstr_sprintf(user, "%s@%s", opt_username, opt_domain);
+
+ if ((retval = kerberos_kinit_password(user, opt_password,
+ 0, NULL))) {
+ DEBUG(10, ("Requesting TGT failed: %s\n", error_message(retval)));
+ x_fprintf(x_stdout, "NA\n");
+ return True;
+ }
+
+ retval = cli_krb5_get_ticket(principal, 0, &tkt, &session_key_krb5);
+
+ if (retval) {
+ DEBUG(10, ("Kinit suceeded, but getting a ticket failed: %s\n", error_message(retval)));
+ }
+ }
+
+ data_blob_free(&session_key_krb5);
+
+ ZERO_STRUCT(reply);
+
+ reply.type = SPNEGO_NEG_TOKEN_INIT;
+ reply.negTokenInit.mechTypes = my_mechs;
+ reply.negTokenInit.reqFlags = 0;
+ reply.negTokenInit.mechToken = tkt;
+ reply.negTokenInit.mechListMIC = data_blob(NULL, 0);
+
+ len = write_spnego_data(&to_server, &reply);
+ data_blob_free(&tkt);
+
+ if (len == -1) {
+ DEBUG(1, ("Could not write SPNEGO data blob\n"));
+ return False;
+ }
+
+ reply_base64 = base64_encode_data_blob(to_server);
+ x_fprintf(x_stdout, "KK %s *\n", reply_base64);
+
+ SAFE_FREE(reply_base64);
+ data_blob_free(&to_server);
+ DEBUG(10, ("sent GSS-SPNEGO KERBEROS5 negTokenInit\n"));
+ return True;
+}
+
+static void manage_client_krb5_targ(SPNEGO_DATA spnego)
+{
+ switch (spnego.negTokenTarg.negResult) {
+ case SPNEGO_ACCEPT_INCOMPLETE:
+ DEBUG(1, ("Got a Kerberos negTokenTarg with ACCEPT_INCOMPLETE\n"));
+ x_fprintf(x_stdout, "BH\n");
+ break;
+ case SPNEGO_ACCEPT_COMPLETED:
+ DEBUG(10, ("Accept completed\n"));
+ x_fprintf(x_stdout, "AF\n");
+ break;
+ case SPNEGO_REJECT:
+ DEBUG(10, ("Rejected\n"));
+ x_fprintf(x_stdout, "NA\n");
+ break;
+ default:
+ DEBUG(1, ("Got an invalid negTokenTarg\n"));
+ x_fprintf(x_stdout, "AF\n");
+ }
+}
+
+#endif
+
+static void manage_gss_spnego_client_request(enum stdio_helper_mode stdio_helper_mode,
+ char *buf, int length)
+{
+ DATA_BLOB request;
+ SPNEGO_DATA spnego;
+ ssize_t len;
+
+ if (strlen(buf) <= 3) {
+ DEBUG(1, ("SPNEGO query [%s] too short\n", buf));
+ x_fprintf(x_stdout, "BH\n");
+ return;
+ }
+
+ request = base64_decode_data_blob(buf+3);
+
+ if (strncmp(buf, "PW ", 3) == 0) {
+
+ /* We asked for a password and obviously got it :-) */
+
+ opt_password = strndup((const char *)request.data, request.length);
+
+ if (opt_password == NULL) {
+ DEBUG(1, ("Out of memory\n"));
+ x_fprintf(x_stdout, "BH\n");
+ data_blob_free(&request);
+ return;
+ }
+
+ x_fprintf(x_stdout, "OK\n");
+ data_blob_free(&request);
+ return;
+ }
+
+ if ( (strncmp(buf, "TT ", 3) != 0) &&
+ (strncmp(buf, "AF ", 3) != 0) &&
+ (strncmp(buf, "NA ", 3) != 0) ) {
+ DEBUG(1, ("SPNEGO request [%s] invalid\n", buf));
+ x_fprintf(x_stdout, "BH\n");
+ data_blob_free(&request);
+ return;
+ }
+
+ /* So we got a server challenge to generate a SPNEGO
+ client-to-server request... */
+
+ len = read_spnego_data(request, &spnego);
+ data_blob_free(&request);
+
+ if (len == -1) {
+ DEBUG(1, ("Could not read SPNEGO data for [%s]\n", buf));
+ x_fprintf(x_stdout, "BH\n");
+ return;
+ }
+
+ if (spnego.type == SPNEGO_NEG_TOKEN_INIT) {
+
+ /* The server offers a list of mechanisms */
+
+ const char **mechType = spnego.negTokenInit.mechTypes;
+
+ while (*mechType != NULL) {
+
+#ifdef HAVE_KRB5
+ if ( (strcmp(*mechType, OID_KERBEROS5_OLD) == 0) ||
+ (strcmp(*mechType, OID_KERBEROS5) == 0) ) {
+ if (manage_client_krb5_init(spnego))
+ goto out;
+ }
+#endif
+
+ if (strcmp(*mechType, OID_NTLMSSP) == 0) {
+ if (manage_client_ntlmssp_init(spnego))
+ goto out;
+ }
+
+ mechType++;
+ }
+
+ DEBUG(1, ("Server offered no compatible mechanism\n"));
+ x_fprintf(x_stdout, "BH\n");
+ return;
+ }
+
+ if (spnego.type == SPNEGO_NEG_TOKEN_TARG) {
+
+ if (spnego.negTokenTarg.supportedMech == NULL) {
+ /* On accept/reject Windows does not send the
+ mechanism anymore. Handle that here and
+ shut down the mechanisms. */
+
+ switch (spnego.negTokenTarg.negResult) {
+ case SPNEGO_ACCEPT_COMPLETED:
+ x_fprintf(x_stdout, "AF\n");
+ break;
+ case SPNEGO_REJECT:
+ x_fprintf(x_stdout, "NA\n");
+ break;
+ default:
+ DEBUG(1, ("Got a negTokenTarg with no mech and an "
+ "unknown negResult: %d\n",
+ spnego.negTokenTarg.negResult));
+ x_fprintf(x_stdout, "BH\n");
+ }
+
+ ntlmssp_end(&client_ntlmssp_state);
+ goto out;
+ }
+
+ if (strcmp(spnego.negTokenTarg.supportedMech,
+ OID_NTLMSSP) == 0) {
+ manage_client_ntlmssp_targ(spnego);
+ goto out;
+ }
+
+#if HAVE_KRB5
+ if (strcmp(spnego.negTokenTarg.supportedMech,
+ OID_KERBEROS5_OLD) == 0) {
+ manage_client_krb5_targ(spnego);
+ goto out;
+ }
+#endif
+
+ }
+
+ DEBUG(1, ("Got an SPNEGO token I could not handle [%s]!\n", buf));
+ x_fprintf(x_stdout, "BH\n");
+ return;
+
+ out:
+ free_spnego_data(&spnego);
+ return;
+}
+
+static void manage_squid_request(enum stdio_helper_mode helper_mode, stdio_helper_function fn)
+{
+ char buf[SQUID_BUFFER_SIZE+1];
+ int length;
+ char *c;
+ static BOOL err;
+
+ /* this is not a typo - x_fgets doesn't work too well under squid */
+ if (fgets(buf, sizeof(buf)-1, stdin) == NULL) {
+ DEBUG(1, ("fgets() failed! dying..... errno=%d (%s)\n", ferror(stdin),
+ strerror(ferror(stdin))));
+ exit(1); /* BIIG buffer */
+ }
+
+ c=memchr(buf,'\n',sizeof(buf)-1);
+ if (c) {
+ *c = '\0';
+ length = c-buf;
+ } else {
+ err = 1;
+ return;
+ }
+ if (err) {
+ DEBUG(2, ("Oversized message\n"));
+ x_fprintf(x_stderr, "ERR\n");
+ err = 0;
+ return;
+ }
+
+ DEBUG(10, ("Got '%s' from squid (length: %d).\n",buf,length));
+
+ if (buf[0] == '\0') {
+ DEBUG(2, ("Invalid Request\n"));
+ x_fprintf(x_stderr, "ERR\n");
+ return;
+ }
+
+ fn(helper_mode, buf, length);
+}
+
+
+static void squid_stream(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(stdio_mode, fn);
+ }
+}
+
+
+/* Authenticate a user with a challenge/response */
+
+static BOOL check_auth_crap(void)
+{
+ NTSTATUS nt_status;
+ uint32 flags = 0;
+ char lm_key[8];
+ char nt_key[16];
+ char *hex_lm_key;
+ char *hex_nt_key;
+ char *error_string;
+ static uint8 zeros[16];
+
+ x_setbuf(x_stdout, NULL);
+
+ if (request_lm_key)
+ flags |= WBFLAG_PAM_LMKEY;
+
+ if (request_nt_key)
+ flags |= WBFLAG_PAM_NTKEY;
+
+ nt_status = contact_winbind_auth_crap(opt_username, opt_domain,
+ opt_workstation,
+ &opt_challenge,
+ &opt_lm_response,
+ &opt_nt_response,
+ flags,
+ (unsigned char *)lm_key,
+ (unsigned char *)nt_key,
+ &error_string, NULL);
+
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ x_fprintf(x_stdout, "%s (0x%x)\n",
+ error_string,
+ NT_STATUS_V(nt_status));
+ SAFE_FREE(error_string);
+ return False;
+ }
+
+ if (request_lm_key
+ && (memcmp(zeros, lm_key,
+ sizeof(lm_key)) != 0)) {
+ hex_encode((const unsigned char *)lm_key,
+ sizeof(lm_key),
+ &hex_lm_key);
+ x_fprintf(x_stdout, "LM_KEY: %s\n", hex_lm_key);
+ SAFE_FREE(hex_lm_key);
+ }
+ if (request_nt_key
+ && (memcmp(zeros, nt_key,
+ sizeof(nt_key)) != 0)) {
+ hex_encode((const unsigned char *)nt_key,
+ sizeof(nt_key),
+ &hex_nt_key);
+ x_fprintf(x_stdout, "NT_KEY: %s\n", hex_nt_key);
+ SAFE_FREE(hex_nt_key);
+ }
+
+ return True;
+}
+
+/*
+ Authenticate a user with a challenge/response, checking session key
+ and valid authentication types
+*/
+
+static DATA_BLOB get_challenge(void)
+{
+ static DATA_BLOB chal;
+ if (opt_challenge.length)
+ return opt_challenge;
+
+ chal = data_blob(NULL, 8);
+
+ generate_random_buffer(chal.data, chal.length, False);
+ return chal;
+}
+
+/*
+ * Test the normal 'LM and NTLM' combination
+ */
+
+static BOOL test_lm_ntlm_broken(enum ntlm_break break_which)
+{
+ BOOL pass = True;
+ NTSTATUS nt_status;
+ uint32 flags = 0;
+ DATA_BLOB lm_response = data_blob(NULL, 24);
+ DATA_BLOB nt_response = data_blob(NULL, 24);
+ DATA_BLOB session_key = data_blob(NULL, 16);
+
+ uchar lm_key[8];
+ uchar nt_key[16];
+ uchar lm_hash[16];
+ uchar nt_hash[16];
+ DATA_BLOB chall = get_challenge();
+ char *error_string;
+
+ ZERO_STRUCT(lm_key);
+ ZERO_STRUCT(nt_key);
+
+ flags |= WBFLAG_PAM_LMKEY;
+ flags |= WBFLAG_PAM_NTKEY;
+
+ SMBencrypt(opt_password,chall.data,lm_response.data);
+ E_deshash(opt_password, lm_hash);
+
+ SMBNTencrypt(opt_password,chall.data,nt_response.data);
+
+ E_md4hash(opt_password, nt_hash);
+ SMBsesskeygen_ntv1(nt_hash, NULL, session_key.data);
+
+ switch (break_which) {
+ case BREAK_NONE:
+ break;
+ case BREAK_LM:
+ lm_response.data[0]++;
+ break;
+ case BREAK_NT:
+ nt_response.data[0]++;
+ break;
+ case NO_LM:
+ data_blob_free(&lm_response);
+ break;
+ case NO_NT:
+ data_blob_free(&nt_response);
+ break;
+ }
+
+ nt_status = contact_winbind_auth_crap(opt_username, opt_domain,
+ opt_workstation,
+ &chall,
+ &lm_response,
+ &nt_response,
+ flags,
+ lm_key,
+ nt_key,
+ &error_string, NULL);
+
+ data_blob_free(&lm_response);
+
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ d_printf("%s (0x%x)\n",
+ error_string,
+ NT_STATUS_V(nt_status));
+ SAFE_FREE(error_string);
+ return break_which == BREAK_NT;
+ }
+
+ if (memcmp(lm_hash, lm_key,
+ sizeof(lm_key)) != 0) {
+ DEBUG(1, ("LM Key does not match expectations!\n"));
+ DEBUG(1, ("lm_key:\n"));
+ dump_data(1, (const char *)lm_key, 8);
+ DEBUG(1, ("expected:\n"));
+ dump_data(1, (const char *)lm_hash, 8);
+ pass = False;
+ }
+
+ if (break_which == NO_NT) {
+ if (memcmp(lm_hash, nt_key,
+ 8) != 0) {
+ DEBUG(1, ("NT Session Key does not match expectations (should be LM hash)!\n"));
+ DEBUG(1, ("nt_key:\n"));
+ dump_data(1, (const char *)nt_key, sizeof(nt_key));
+ DEBUG(1, ("expected:\n"));
+ dump_data(1, (const char *)lm_hash, sizeof(lm_hash));
+ pass = False;
+ }
+ } else {
+ if (memcmp(session_key.data, nt_key,
+ sizeof(nt_key)) != 0) {
+ DEBUG(1, ("NT Session Key does not match expectations!\n"));
+ DEBUG(1, ("nt_key:\n"));
+ dump_data(1, (const char *)nt_key, 16);
+ DEBUG(1, ("expected:\n"));
+ dump_data(1, (const char *)session_key.data, session_key.length);
+ pass = False;
+ }
+ }
+ return pass;
+}
+
+/*
+ * Test LM authentication, no NT response supplied
+ */
+
+static BOOL test_lm(void)
+{
+
+ return test_lm_ntlm_broken(NO_NT);
+}
+
+/*
+ * Test the NTLM response only, no LM.
+ */
+
+static BOOL test_ntlm(void)
+{
+ return test_lm_ntlm_broken(NO_LM);
+}
+
+/*
+ * Test the NTLM response only, but in the LM field.
+ */
+
+static BOOL test_ntlm_in_lm(void)
+{
+ BOOL pass = True;
+ NTSTATUS nt_status;
+ uint32 flags = 0;
+ DATA_BLOB nt_response = data_blob(NULL, 24);
+
+ uchar lm_key[8];
+ uchar lm_hash[16];
+ uchar nt_key[16];
+ DATA_BLOB chall = get_challenge();
+ char *error_string;
+
+ ZERO_STRUCT(nt_key);
+
+ flags |= WBFLAG_PAM_LMKEY;
+ flags |= WBFLAG_PAM_NTKEY;
+
+ SMBNTencrypt(opt_password,chall.data,nt_response.data);
+
+ E_deshash(opt_password, lm_hash);
+
+ nt_status = contact_winbind_auth_crap(opt_username, opt_domain,
+ opt_workstation,
+ &chall,
+ &nt_response,
+ NULL,
+ flags,
+ lm_key,
+ nt_key,
+ &error_string, NULL);
+
+ data_blob_free(&nt_response);
+
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ d_printf("%s (0x%x)\n",
+ error_string,
+ NT_STATUS_V(nt_status));
+ SAFE_FREE(error_string);
+ return False;
+ }
+
+ if (memcmp(lm_hash, lm_key,
+ sizeof(lm_key)) != 0) {
+ DEBUG(1, ("LM Key does not match expectations!\n"));
+ DEBUG(1, ("lm_key:\n"));
+ dump_data(1, (const char *)lm_key, 8);
+ DEBUG(1, ("expected:\n"));
+ dump_data(1, (const char *)lm_hash, 8);
+ pass = False;
+ }
+ if (memcmp(lm_hash, nt_key, 8) != 0) {
+ DEBUG(1, ("Session Key (first 8 lm hash) does not match expectations!\n"));
+ DEBUG(1, ("nt_key:\n"));
+ dump_data(1, (const char *)nt_key, 16);
+ DEBUG(1, ("expected:\n"));
+ dump_data(1, (const char *)lm_hash, 8);
+ pass = False;
+ }
+ return pass;
+}
+
+/*
+ * Test the NTLM response only, but in the both the NT and LM fields.
+ */
+
+static BOOL test_ntlm_in_both(void)
+{
+ BOOL pass = True;
+ NTSTATUS nt_status;
+ uint32 flags = 0;
+ DATA_BLOB nt_response = data_blob(NULL, 24);
+ DATA_BLOB session_key = data_blob(NULL, 16);
+
+ char lm_key[8];
+ char lm_hash[16];
+ char nt_key[16];
+ char nt_hash[16];
+ DATA_BLOB chall = get_challenge();
+ char *error_string;
+
+ ZERO_STRUCT(lm_key);
+ ZERO_STRUCT(nt_key);
+
+ flags |= WBFLAG_PAM_LMKEY;
+ flags |= WBFLAG_PAM_NTKEY;
+
+ SMBNTencrypt(opt_password,chall.data,nt_response.data);
+ E_md4hash(opt_password, (unsigned char *)nt_hash);
+ SMBsesskeygen_ntv1((const unsigned char *)nt_hash, NULL, session_key.data);
+
+ E_deshash(opt_password, (unsigned char *)lm_hash);
+
+ nt_status = contact_winbind_auth_crap(opt_username, opt_domain,
+ opt_workstation,
+ &chall,
+ &nt_response,
+ &nt_response,
+ flags,
+ (unsigned char *)lm_key,
+ (unsigned char *)nt_key,
+ &error_string, NULL);
+
+ data_blob_free(&nt_response);
+
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ d_printf("%s (0x%x)\n",
+ error_string,
+ NT_STATUS_V(nt_status));
+ SAFE_FREE(error_string);
+ return False;
+ }
+
+ if (memcmp(lm_hash, lm_key,
+ sizeof(lm_key)) != 0) {
+ DEBUG(1, ("LM Key does not match expectations!\n"));
+ DEBUG(1, ("lm_key:\n"));
+ dump_data(1, lm_key, 8);
+ DEBUG(1, ("expected:\n"));
+ dump_data(1, lm_hash, 8);
+ pass = False;
+ }
+ if (memcmp(session_key.data, nt_key,
+ sizeof(nt_key)) != 0) {
+ DEBUG(1, ("NT Session Key does not match expectations!\n"));
+ DEBUG(1, ("nt_key:\n"));
+ dump_data(1, nt_key, 16);
+ DEBUG(1, ("expected:\n"));
+ dump_data(1, (const char *)session_key.data, session_key.length);
+ pass = False;
+ }
+
+
+ return pass;
+}
+
+/*
+ * Test the NTLMv2 and LMv2 responses
+ */
+
+static BOOL test_lmv2_ntlmv2_broken(enum ntlm_break break_which)
+{
+ BOOL pass = True;
+ NTSTATUS nt_status;
+ uint32 flags = 0;
+ DATA_BLOB ntlmv2_response = data_blob(NULL, 0);
+ DATA_BLOB lmv2_response = data_blob(NULL, 0);
+ DATA_BLOB nt_session_key = data_blob(NULL, 0);
+ DATA_BLOB names_blob = NTLMv2_generate_names_blob(get_winbind_netbios_name(), get_winbind_domain());
+
+ uchar nt_key[16];
+ DATA_BLOB chall = get_challenge();
+ char *error_string;
+
+ ZERO_STRUCT(nt_key);
+
+ flags |= WBFLAG_PAM_NTKEY;
+
+ if (!SMBNTLMv2encrypt(opt_username, opt_domain, opt_password, &chall,
+ &names_blob,
+ &lmv2_response, &ntlmv2_response,
+ &nt_session_key)) {
+ data_blob_free(&names_blob);
+ return False;
+ }
+ data_blob_free(&names_blob);
+
+ switch (break_which) {
+ case BREAK_NONE:
+ break;
+ case BREAK_LM:
+ lmv2_response.data[0]++;
+ break;
+ case BREAK_NT:
+ ntlmv2_response.data[0]++;
+ break;
+ case NO_LM:
+ data_blob_free(&lmv2_response);
+ break;
+ case NO_NT:
+ data_blob_free(&ntlmv2_response);
+ break;
+ }
+
+ nt_status = contact_winbind_auth_crap(opt_username, opt_domain,
+ opt_workstation,
+ &chall,
+ &lmv2_response,
+ &ntlmv2_response,
+ flags,
+ NULL,
+ nt_key,
+ &error_string, NULL);
+
+ data_blob_free(&lmv2_response);
+ data_blob_free(&ntlmv2_response);
+
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ d_printf("%s (0x%x)\n",
+ error_string,
+ NT_STATUS_V(nt_status));
+ SAFE_FREE(error_string);
+ return break_which == BREAK_NT;
+ }
+
+ if (break_which != NO_NT && break_which != BREAK_NT && memcmp(nt_session_key.data, nt_key,
+ sizeof(nt_key)) != 0) {
+ DEBUG(1, ("NT Session Key does not match expectations!\n"));
+ DEBUG(1, ("nt_key:\n"));
+ dump_data(1, (const char *)nt_key, 16);
+ DEBUG(1, ("expected:\n"));
+ dump_data(1, (const char *)nt_session_key.data, nt_session_key.length);
+ pass = False;
+ }
+ return pass;
+}
+
+/*
+ * Test the NTLMv2 and LMv2 responses
+ */
+
+static BOOL test_lmv2_ntlmv2(void)
+{
+ return test_lmv2_ntlmv2_broken(BREAK_NONE);
+}
+
+/*
+ * Test the LMv2 response only
+ */
+
+static BOOL test_lmv2(void)
+{
+ return test_lmv2_ntlmv2_broken(NO_NT);
+}
+
+/*
+ * Test the NTLMv2 response only
+ */
+
+static BOOL test_ntlmv2(void)
+{
+ return test_lmv2_ntlmv2_broken(NO_LM);
+}
+
+static BOOL test_lm_ntlm(void)
+{
+ return test_lm_ntlm_broken(BREAK_NONE);
+}
+
+static BOOL test_ntlm_lm_broken(void)
+{
+ return test_lm_ntlm_broken(BREAK_LM);
+}
+
+static BOOL test_ntlm_ntlm_broken(void)
+{
+ return test_lm_ntlm_broken(BREAK_NT);
+}
+
+static BOOL test_ntlmv2_lmv2_broken(void)
+{
+ return test_lmv2_ntlmv2_broken(BREAK_LM);
+}
+
+static BOOL test_ntlmv2_ntlmv2_broken(void)
+{
+ return test_lmv2_ntlmv2_broken(BREAK_NT);
+}
+
+static BOOL test_plaintext(enum ntlm_break break_which)
+{
+ NTSTATUS nt_status;
+ uint32 flags = 0;
+ DATA_BLOB nt_response = data_blob(NULL, 0);
+ DATA_BLOB lm_response = data_blob(NULL, 0);
+ char *password;
+
+ uchar nt_key[16];
+ uchar lm_key[16];
+ static const uchar zeros[8];
+ DATA_BLOB chall = data_blob(zeros, sizeof(zeros));
+ char *error_string;
+
+ ZERO_STRUCT(nt_key);
+
+ flags |= WBFLAG_PAM_NTKEY;
+ flags |= WBFLAG_PAM_LMKEY;
+
+ if ((push_ucs2_allocate((smb_ucs2_t **)&nt_response.data, opt_password)) == -1) {
+ DEBUG(0, ("push_ucs2_allocate failed!\n"));
+ exit(1);
+ }
+
+ nt_response.length = strlen_w(((void *)nt_response.data))*sizeof(smb_ucs2_t);
+
+ password = strdup_upper(opt_password);
+
+ if ((convert_string_allocate(NULL, CH_UNIX,
+ CH_DOS, password,
+ strlen(password)+1,
+ (void**)&lm_response.data,True)) == -1) {
+ DEBUG(0, ("push_ascii_allocate failed!\n"));
+ exit(1);
+ }
+
+ SAFE_FREE(password);
+
+ lm_response.length = strlen(lm_response.data);
+
+ switch (break_which) {
+ case BREAK_NONE:
+ break;
+ case BREAK_LM:
+ lm_response.data[0]++;
+ break;
+ case BREAK_NT:
+ nt_response.data[0]++;
+ break;
+ case NO_LM:
+ SAFE_FREE(lm_response.data);
+ lm_response.length = 0;
+ break;
+ case NO_NT:
+ SAFE_FREE(nt_response.data);
+ nt_response.length = 0;
+ break;
+ }
+
+ nt_status = contact_winbind_auth_crap(opt_username, opt_domain,
+ opt_workstation,
+ &chall,
+ &lm_response,
+ &nt_response,
+ flags,
+ lm_key,
+ nt_key,
+ &error_string, NULL);
+
+ SAFE_FREE(nt_response.data);
+ SAFE_FREE(lm_response.data);
+ data_blob_free(&chall);
+
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ d_printf("%s (0x%x)\n",
+ error_string,
+ NT_STATUS_V(nt_status));
+ SAFE_FREE(error_string);
+ return break_which == BREAK_NT;
+ }
+
+ return break_which != BREAK_NT;
+}
+
+static BOOL test_plaintext_none_broken(void) {
+ return test_plaintext(BREAK_NONE);
+}
+
+static BOOL test_plaintext_lm_broken(void) {
+ return test_plaintext(BREAK_LM);
+}
+
+static BOOL test_plaintext_nt_broken(void) {
+ return test_plaintext(BREAK_NT);
+}
+
+static BOOL test_plaintext_nt_only(void) {
+ return test_plaintext(NO_LM);
+}
+
+static BOOL test_plaintext_lm_only(void) {
+ return test_plaintext(NO_NT);
+}
+
+/*
+ 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 feilds)
+
+ 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)(void);
+ const char *name;
+} test_table[] = {
+ {test_lm, "LM"},
+ {test_lm_ntlm, "LM and NTLM"},
+ {test_ntlm, "NTLM"},
+ {test_ntlm_in_lm, "NTLM in LM"},
+ {test_ntlm_in_both, "NTLM in both"},
+ {test_ntlmv2, "NTLMv2"},
+ {test_lmv2_ntlmv2, "NTLMv2 and LMv2"},
+ {test_lmv2, "LMv2"},
+ {test_ntlmv2_lmv2_broken, "NTLMv2 and LMv2, LMv2 broken"},
+ {test_ntlmv2_ntlmv2_broken, "NTLMv2 and LMv2, NTLMv2 broken"},
+ {test_ntlm_lm_broken, "NTLM and LM, LM broken"},
+ {test_ntlm_ntlm_broken, "NTLM and LM, NTLM broken"},
+ {test_plaintext_none_broken, "Plaintext"},
+ {test_plaintext_lm_broken, "Plaintext LM broken"},
+ {test_plaintext_nt_broken, "Plaintext NT broken"},
+ {test_plaintext_nt_only, "Plaintext NT only"},
+ {test_plaintext_lm_only, "Plaintext LM only"}
+};
+
+static BOOL diagnose_ntlm_auth(void)
+{
+ unsigned int i;
+ BOOL pass = True;
+
+ for (i=0; test_table[i].fn; i++) {
+ if (!test_table[i].fn()) {
+ DEBUG(1, ("Test %s failed!\n", test_table[i].name));
+ pass = False;
+ }
+ }
+
+ return pass;
+}
+
+/* Main program */
+
+enum {
+ OPT_USERNAME = 1000,
+ OPT_DOMAIN,
+ OPT_WORKSTATION,
+ OPT_CHALLENGE,
+ OPT_RESPONSE,
+ OPT_LM,
+ OPT_NT,
+ OPT_PASSWORD,
+ OPT_LM_KEY,
+ OPT_NT_KEY,
+ OPT_DIAGNOSTICS
+};
+
+ int main(int argc, const char **argv)
+{
+ int opt;
+ static const char *helper_protocol;
+ static int diagnostics;
+
+ static const char *hex_challenge;
+ static const char *hex_lm_response;
+ static const char *hex_nt_response;
+ char *challenge;
+ char *lm_response;
+ char *nt_response;
+ size_t challenge_len;
+ size_t lm_response_len;
+ size_t nt_response_len;
+
+ 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"},
+ { "username", 0, POPT_ARG_STRING, &opt_username, OPT_USERNAME, "username"},
+ { "domain", 0, POPT_ARG_STRING, &opt_domain, OPT_DOMAIN, "domain name"},
+ { "workstation", 0, POPT_ARG_STRING, &opt_workstation, OPT_WORKSTATION, "workstation"},
+ { "challenge", 0, POPT_ARG_STRING, &hex_challenge, OPT_CHALLENGE, "challenge (HEX encoded)"},
+ { "lm-response", 0, POPT_ARG_STRING, &hex_lm_response, OPT_LM, "LM Response to the challenge (HEX encoded)"},
+ { "nt-response", 0, POPT_ARG_STRING, &hex_nt_response, OPT_NT, "NT or NTLMv2 Response to the challenge (HEX encoded)"},
+ { "password", 0, POPT_ARG_STRING, &opt_password, OPT_PASSWORD, "User's plaintext password"},
+ { "request-lm-key", 0, POPT_ARG_NONE, &request_lm_key, OPT_LM_KEY, "Retreive LM session key"},
+ { "request-nt-key", 0, POPT_ARG_NONE, &request_nt_key, OPT_NT_KEY, "Retreive NT session key"},
+ { "diagnostics", 0, POPT_ARG_NONE, &diagnostics, OPT_DIAGNOSTICS, "Perform diagnostics on the authentictaion chain"},
+ POPT_COMMON_SAMBA
+ POPT_TABLEEND
+ };
+
+ /* Samba client initialisation */
+
+ dbf = x_stderr;
+
+ /* Samba client initialisation */
+
+ if (!lp_load(dyn_CONFIGFILE, True, False, False)) {
+ d_fprintf(stderr, "wbinfo: error opening config file %s. Error was %s\n",
+ dyn_CONFIGFILE, strerror(errno));
+ exit(1);
+ }
+
+ /* 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) {
+ switch (opt) {
+ case OPT_CHALLENGE:
+ challenge = smb_xmalloc((strlen(hex_challenge))/2+1);
+ if ((challenge_len = strhex_to_str(challenge,
+ strlen(hex_challenge),
+ hex_challenge)) != 8) {
+ x_fprintf(x_stderr, "hex decode of %s failed (only got %lu bytes)!\n",
+ hex_challenge, (unsigned long)challenge_len);
+ exit(1);
+ }
+ opt_challenge = data_blob(challenge, challenge_len);
+ SAFE_FREE(challenge);
+ break;
+ case OPT_LM:
+ lm_response = smb_xmalloc((strlen(hex_lm_response))/2+1);
+ lm_response_len = strhex_to_str(lm_response,
+ strlen(hex_lm_response),
+ hex_lm_response);
+ if (lm_response_len != 24) {
+ x_fprintf(x_stderr, "hex decode of %s failed!\n", hex_lm_response);
+ exit(1);
+ }
+ opt_lm_response = data_blob(lm_response, lm_response_len);
+ SAFE_FREE(lm_response);
+ break;
+ case OPT_NT:
+ nt_response = smb_xmalloc((strlen(hex_nt_response)+2)/2+1);
+ nt_response_len = strhex_to_str(nt_response,
+ strlen(hex_nt_response),
+ hex_nt_response);
+ if (nt_response_len < 24) {
+ x_fprintf(x_stderr, "hex decode of %s failed!\n", hex_nt_response);
+ exit(1);
+ }
+ opt_nt_response = data_blob(nt_response, nt_response_len);
+ SAFE_FREE(nt_response);
+ break;
+ }
+ }
+
+ 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(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_domain == NULL) {
+ opt_domain = get_winbind_domain();
+ }
+
+ if (opt_workstation == NULL) {
+ opt_workstation = "";
+ }
+
+ if (opt_challenge.length) {
+ if (!check_auth_crap()) {
+ exit(1);
+ }
+ exit(0);
+ }
+
+ if (!opt_password) {
+ opt_password = getpass("password: ");
+ }
+
+ if (diagnostics) {
+ if (!diagnose_ntlm_auth()) {
+ exit(1);
+ }
+ } else {
+ fstring user;
+
+ fstr_sprintf(user, "%s%c%s", opt_domain, winbind_separator(), opt_username);
+ if (!check_plaintext_auth(user, opt_password, True)) {
+ exit(1);
+ }
+ }
+
+ /* Exit code */
+
+ poptFreeContext(pc);
+ return 0;
+}
diff --git a/source/utils/pdbedit.c b/source/utils/pdbedit.c
new file mode 100644
index 00000000000..af96413c5ae
--- /dev/null
+++ b/source/utils/pdbedit.c
@@ -0,0 +1,1179 @@
+/*
+ Unix SMB/CIFS implementation.
+ passdb editing frontend
+
+ Copyright (C) Simo Sorce 2000
+ Copyright (C) Andrew Bartlett 2001
+ Copyright (C) Jelmer Vernooij 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 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"
+
+#define BIT_BACKEND 0x00000004
+#define BIT_VERBOSE 0x00000008
+#define BIT_SPSTYLE 0x00000010
+#define BIT_RESERV_1 0x00000020
+#define BIT_RESERV_2 0x00000040
+#define BIT_RESERV_3 0x00000080
+#define BIT_FULLNAME 0x00000100
+#define BIT_HOMEDIR 0x00000200
+#define BIT_HDIRDRIVE 0x00000400
+#define BIT_LOGSCRIPT 0x00000800
+#define BIT_PROFILE 0x00001000
+#define BIT_MACHINE 0x00002000
+#define BIT_RESERV_4 0x00004000
+#define BIT_USER 0x00008000
+#define BIT_LIST 0x00010000
+#define BIT_MODIFY 0x00020000
+#define BIT_CREATE 0x00040000
+#define BIT_DELETE 0x00080000
+#define BIT_ACCPOLICY 0x00100000
+#define BIT_ACCPOLVAL 0x00200000
+#define BIT_ACCTCTRL 0x00400000
+#define BIT_RESERV_7 0x00800000
+#define BIT_IMPORT 0x01000000
+#define BIT_EXPORT 0x02000000
+#define BIT_FIX_INIT 0x04000000
+#define BIT_BADPWRESET 0x08000000
+#define BIT_TRUSTDOM 0x10000000
+#define BIT_TRUSTPW 0x20000000
+#define BIT_TRUSTSID 0x40000000
+#define BIT_TRUSTFLAGS 0x80000000
+
+#define MASK_ALWAYS_GOOD 0x0000001F
+#define MASK_USER_GOOD 0x00401F00
+
+/*********************************************************
+ Add all currently available users to another db
+ ********************************************************/
+
+static int export_database (struct pdb_context *in, struct pdb_context *out) {
+ SAM_ACCOUNT *user = NULL;
+
+ if (NT_STATUS_IS_ERR(in->pdb_setsampwent(in, 0))) {
+ fprintf(stderr, "Can't sampwent!\n");
+ return 1;
+ }
+
+ if (!NT_STATUS_IS_OK(pdb_init_sam(&user))) {
+ fprintf(stderr, "Can't initialize new SAM_ACCOUNT!\n");
+ return 1;
+ }
+
+ while (NT_STATUS_IS_OK(in->pdb_getsampwent(in, user))) {
+ out->pdb_add_sam_account(out, user);
+ if (!NT_STATUS_IS_OK(pdb_reset_sam(user))){
+ fprintf(stderr, "Can't reset SAM_ACCOUNT!\n");
+ return 1;
+ }
+ }
+
+ in->pdb_endsampwent(in);
+
+ return 0;
+}
+
+/*********************************************************
+ Add all currently available group mappings to another db
+ ********************************************************/
+
+static int export_groups (struct pdb_context *in, struct pdb_context *out) {
+ GROUP_MAP *maps = NULL;
+ int i, entries = 0;
+
+ if (NT_STATUS_IS_ERR(in->pdb_enum_group_mapping(in, SID_NAME_UNKNOWN,
+ &maps, &entries,
+ False))) {
+ fprintf(stderr, "Can't get group mappings!\n");
+ return 1;
+ }
+
+ for (i=0; i<entries; i++) {
+ out->pdb_add_group_mapping_entry(out, &(maps[i]));
+ }
+
+ SAFE_FREE(maps);
+
+ return 0;
+}
+
+/*********************************************************
+ Print info from sam structure
+**********************************************************/
+
+static int print_sam_info (SAM_ACCOUNT *sam_pwent, BOOL verbosity, BOOL smbpwdstyle)
+{
+ uid_t uid;
+ time_t tmp;
+
+ /* TODO: chaeck if entry is a user or a workstation */
+ if (!sam_pwent) return -1;
+
+ if (verbosity) {
+ printf ("Unix username: %s\n", pdb_get_username(sam_pwent));
+ printf ("NT username: %s\n", pdb_get_nt_username(sam_pwent));
+ printf ("Account Flags: %s\n", pdb_encode_acct_ctrl(pdb_get_acct_ctrl(sam_pwent), NEW_PW_FORMAT_SPACE_PADDED_LEN));
+ printf ("User SID: %s\n",
+ sid_string_static(pdb_get_user_sid(sam_pwent)));
+ printf ("Primary Group SID: %s\n",
+ sid_string_static(pdb_get_group_sid(sam_pwent)));
+ printf ("Full Name: %s\n", pdb_get_fullname(sam_pwent));
+ printf ("Home Directory: %s\n", pdb_get_homedir(sam_pwent));
+ printf ("HomeDir Drive: %s\n", pdb_get_dir_drive(sam_pwent));
+ printf ("Logon Script: %s\n", pdb_get_logon_script(sam_pwent));
+ printf ("Profile Path: %s\n", pdb_get_profile_path(sam_pwent));
+ printf ("Domain: %s\n", pdb_get_domain(sam_pwent));
+ printf ("Account desc: %s\n", pdb_get_acct_desc(sam_pwent));
+ printf ("Workstations: %s\n", pdb_get_workstations(sam_pwent));
+ printf ("Munged dial: %s\n", pdb_get_munged_dial(sam_pwent));
+
+ tmp = pdb_get_logon_time(sam_pwent);
+ printf ("Logon time: %s\n", tmp ? http_timestring(tmp) : "0");
+
+ tmp = pdb_get_logoff_time(sam_pwent);
+ printf ("Logoff time: %s\n", tmp ? http_timestring(tmp) : "0");
+
+ tmp = pdb_get_kickoff_time(sam_pwent);
+ printf ("Kickoff time: %s\n", tmp ? http_timestring(tmp) : "0");
+
+ tmp = pdb_get_pass_last_set_time(sam_pwent);
+ printf ("Password last set: %s\n", tmp ? http_timestring(tmp) : "0");
+
+ tmp = pdb_get_pass_can_change_time(sam_pwent);
+ printf ("Password can change: %s\n", tmp ? http_timestring(tmp) : "0");
+
+ tmp = pdb_get_pass_must_change_time(sam_pwent);
+ printf ("Password must change: %s\n", tmp ? http_timestring(tmp) : "0");
+
+ tmp = pdb_get_bad_password_time(sam_pwent);
+ printf ("Last bad password : %s\n", tmp ? http_timestring(tmp) : "0");
+ printf ("Bad password count : %d\n",
+ pdb_get_bad_password_count(sam_pwent));
+
+ } else if (smbpwdstyle) {
+ char lm_passwd[33];
+ char nt_passwd[33];
+
+ uid = nametouid(pdb_get_username(sam_pwent));
+ pdb_sethexpwd(lm_passwd, pdb_get_lanman_passwd(sam_pwent), pdb_get_acct_ctrl(sam_pwent));
+ pdb_sethexpwd(nt_passwd, pdb_get_nt_passwd(sam_pwent), pdb_get_acct_ctrl(sam_pwent));
+
+ printf("%s:%lu:%s:%s:%s:LCT-%08X:\n",
+ pdb_get_username(sam_pwent),
+ (unsigned long)uid,
+ lm_passwd,
+ nt_passwd,
+ pdb_encode_acct_ctrl(pdb_get_acct_ctrl(sam_pwent),NEW_PW_FORMAT_SPACE_PADDED_LEN),
+ (uint32)pdb_get_pass_last_set_time(sam_pwent));
+ } else {
+ uid = nametouid(pdb_get_username(sam_pwent));
+ printf ("%s:%lu:%s\n", pdb_get_username(sam_pwent), (unsigned long)uid,
+ pdb_get_fullname(sam_pwent));
+ }
+
+ return 0;
+}
+
+/*********************************************************
+ Get an Print User Info
+**********************************************************/
+
+static int print_user_info (struct pdb_context *in, const char *username, BOOL verbosity, BOOL smbpwdstyle)
+{
+ SAM_ACCOUNT *sam_pwent=NULL;
+ BOOL ret;
+ BOOL updated_autolock = False, updated_badpw = False;
+
+ if (!NT_STATUS_IS_OK(pdb_init_sam (&sam_pwent))) {
+ return -1;
+ }
+
+ ret = NT_STATUS_IS_OK(in->pdb_getsampwnam (in, sam_pwent, username));
+
+ if (ret==False) {
+ fprintf (stderr, "Username not found!\n");
+ pdb_free_sam(&sam_pwent);
+ return -1;
+ }
+
+ if (!pdb_update_autolock_flag(sam_pwent, &updated_autolock))
+ DEBUG(2,("pdb_update_autolock_flag failed.\n"));
+
+ if (!pdb_update_bad_password_count(sam_pwent, &updated_badpw))
+ DEBUG(2,("pdb_update_bad_password_count failed.\n"));
+
+ if (updated_autolock || updated_badpw) {
+ become_root();
+ if(!pdb_update_sam_account(sam_pwent))
+ DEBUG(1, ("Failed to modify entry.\n"));
+ unbecome_root();
+ }
+
+ ret=print_sam_info (sam_pwent, verbosity, smbpwdstyle);
+ pdb_free_sam(&sam_pwent);
+
+ return ret;
+}
+
+
+/**
+ * Trust password flag name to flag conversion
+ *
+ * @param flag_name SAM_TRUST_PASSWD structure flag name
+ * @return flag value
+ **/
+
+static int trustpw_flag(const char* flag_name)
+{
+ const int flag_num = 5;
+ typedef struct { const char *name; int val; } flag_conv;
+ flag_conv flags[] = {{ "PASS_MACHINE_TRUST_NT", PASS_MACHINE_TRUST_NT },
+ { "PASS_SERVER_TRUST_NT", PASS_SERVER_TRUST_NT },
+ { "PASS_DOMAIN_TRUST_NT", PASS_DOMAIN_TRUST_NT },
+ { "PASS_MACHINE_TRUST_ADS",PASS_MACHINE_TRUST_ADS },
+ { "PASS_DOMAIN_TRUST_ADS", PASS_DOMAIN_TRUST_ADS }};
+ int i;
+
+ for (i = 0; i < flag_num; i++) {
+ if (!StrCaseCmp(flags[i].name, flag_name)) {
+ return flags[i].val;
+ }
+ }
+
+ return 0;
+}
+
+
+/**
+ * Trust password flag to flag name conversion
+ *
+ * @param val SAM_TRUST_PASSWD structure flag
+ * @return passed flag name
+ **/
+
+static char* trustpw_flag_name(const int val)
+{
+ const int flag_num = 5;
+ typedef struct { const char *name; int val; } flag_conv;
+ flag_conv flags[] = {{ "PASS_MACHINE_TRUST_NT", PASS_MACHINE_TRUST_NT },
+ { "PASS_SERVER_TRUST_NT", PASS_SERVER_TRUST_NT },
+ { "PASS_DOMAIN_TRUST_NT", PASS_DOMAIN_TRUST_NT },
+ { "PASS_MACHINE_TRUST_ADS",PASS_MACHINE_TRUST_ADS },
+ { "PASS_DOMAIN_TRUST_ADS", PASS_DOMAIN_TRUST_ADS }};
+ int i;
+
+ for (i = 0; i < flag_num; i++) {
+ if (flags[i].val == val) {
+ return strdup(flags[i].name);
+ }
+ }
+
+ return strdup("unknown flag");
+}
+
+
+/**
+ * Print trust password structure information
+ *
+ * @param mem_ctx memory context (for unicode name conversion)
+ * @param trust SAM_TRUST_PASSWD structure
+ * @param verbose verbose mode on/off
+ * @return 0 on success, otherwise failure
+ **/
+
+static int print_trustpw_info(TALLOC_CTX *mem_ctx, SAM_TRUST_PASSWD *trust, BOOL verbose)
+{
+ char *dom_name;
+ if (!mem_ctx || !trust) return -1;
+
+ /* convert unicode domain name to char* */
+ if (!pull_ucs2_talloc(mem_ctx, &dom_name, trust->private.uni_name)) return -1;
+ dom_name[trust->private.uni_name_len] = 0;
+
+ /* different output depending on level of verbosity */
+ if (verbose) {
+ printf("Domain name: %s\n", dom_name);
+ printf("Domain SID: %s\n", sid_string_static(&trust->private.domain_sid));
+ printf("Trust password %s\n", trust->private.pass);
+ printf("Trust type: %s\n", trustpw_flag_name(trust->private.flags));
+ printf("Last modified %s\n", trust->private.mod_time ? http_timestring(trust->private.mod_time) : "0");
+
+ } else {
+ printf("%s:%s\n", dom_name, sid_string_static(&trust->private.domain_sid));
+ }
+
+ return 0;
+}
+
+
+/**
+ * Print trust password information by given name
+ *
+ * @param in initialised pdb_context
+ * @param name domain name of the trust password
+ * @param verbose verbose mode on/off
+ * @param smbpwdstyle smbpassword-style output (ignored here)
+ * @return 0 on success, otherwise failure
+ **/
+
+static int print_trust_info(struct pdb_context *in, const char *name, BOOL verbose, BOOL smbpwdstyle)
+{
+ SAM_TRUST_PASSWD trust;
+ TALLOC_CTX *mem_ctx = NULL;
+
+ mem_ctx = talloc_init("pdbedit: trust passwords listing");
+
+ if (NT_STATUS_IS_OK(in->pdb_gettrustpwnam(in, &trust, name))) {
+ return print_trustpw_info(mem_ctx, &trust, verbose);
+ }
+
+ return -1;
+}
+
+/*********************************************************
+ List Users
+**********************************************************/
+static int print_users_list (struct pdb_context *in, BOOL verbosity, BOOL smbpwdstyle)
+{
+ SAM_ACCOUNT *sam_pwent=NULL;
+ BOOL check, ret;
+
+ check = NT_STATUS_IS_OK(in->pdb_setsampwent(in, False));
+ if (!check) {
+ return 1;
+ }
+
+ check = True;
+ if (!(NT_STATUS_IS_OK(pdb_init_sam(&sam_pwent)))) return 1;
+
+ while (check && (ret = NT_STATUS_IS_OK(in->pdb_getsampwent (in, sam_pwent)))) {
+ if (verbosity)
+ printf ("---------------\n");
+ print_sam_info (sam_pwent, verbosity, smbpwdstyle);
+ pdb_free_sam(&sam_pwent);
+ check = NT_STATUS_IS_OK(pdb_init_sam(&sam_pwent));
+ }
+ if (check) pdb_free_sam(&sam_pwent);
+
+ in->pdb_endsampwent(in);
+ return 0;
+}
+
+
+/**
+ * List trust passwords
+ *
+ * @param in initialised pdb context
+ * @param verbose turn on/off verbose mode
+ * @param smbpwdstyle ignored here (there was no trust passwords in smbpasswd file)
+ * @return 0 on success, otherwise failure
+ **/
+
+static int print_trustpw_list(struct pdb_context *in, BOOL verbose, BOOL smbpwdstyle)
+{
+ SAM_TRUST_PASSWD trust;
+ TALLOC_CTX *mem_ctx = NULL;
+ NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
+
+ /* start enumeration and initialise memory context */
+ status = in->pdb_settrustpwent(in);
+ if (NT_STATUS_IS_ERR(status)) return -1;
+ mem_ctx = talloc_init("pdbedit: trust passwords listing");
+
+ /* small separation to make it clear these are not regular accounts */
+ if (!verbose) printf("---\n");
+
+ do {
+ /* fetch next trust password */
+ status = in->pdb_gettrustpwent(in, &trust);
+
+ if (trust.private.uni_name_len) {
+ /* print trust password info */
+ if (verbose) printf ("---------------\n");
+ print_trustpw_info(mem_ctx, &trust, verbose);
+ }
+
+ } while (NT_STATUS_EQUAL(status, STATUS_MORE_ENTRIES) || NT_STATUS_EQUAL(status, NT_STATUS_OK));
+
+ talloc_destroy(mem_ctx);
+ return 0;
+}
+
+
+/*********************************************************
+ Fix a list of Users for uninitialised passwords
+**********************************************************/
+static int fix_users_list (struct pdb_context *in)
+{
+ SAM_ACCOUNT *sam_pwent=NULL;
+ BOOL check, ret;
+
+ check = NT_STATUS_IS_OK(in->pdb_setsampwent(in, False));
+ if (!check) {
+ return 1;
+ }
+
+ check = True;
+ if (!(NT_STATUS_IS_OK(pdb_init_sam(&sam_pwent)))) return 1;
+
+ while (check && (ret = NT_STATUS_IS_OK(in->pdb_getsampwent (in, sam_pwent)))) {
+ printf("Updating record for user %s\n", pdb_get_username(sam_pwent));
+
+ if (!pdb_update_sam_account(sam_pwent)) {
+ printf("Update of user %s failed!\n", pdb_get_username(sam_pwent));
+ }
+ pdb_free_sam(&sam_pwent);
+ check = NT_STATUS_IS_OK(pdb_init_sam(&sam_pwent));
+ if (!check) {
+ fprintf(stderr, "Failed to initialise new SAM_ACCOUNT structure (out of memory?)\n");
+ }
+
+ }
+ if (check) pdb_free_sam(&sam_pwent);
+
+ in->pdb_endsampwent(in);
+ return 0;
+}
+
+/*********************************************************
+ Set User Info
+**********************************************************/
+
+static int set_user_info (struct pdb_context *in, const char *username,
+ const char *fullname, const char *homedir,
+ const char *drive, const char *script,
+ const char *profile, const char *account_control,
+ const char *user_sid, const char *group_sid,
+ const BOOL badpw)
+{
+ SAM_ACCOUNT *sam_pwent=NULL;
+ BOOL ret;
+
+ pdb_init_sam(&sam_pwent);
+
+ ret = NT_STATUS_IS_OK(in->pdb_getsampwnam (in, sam_pwent, username));
+ if (ret==False) {
+ fprintf (stderr, "Username not found!\n");
+ pdb_free_sam(&sam_pwent);
+ return -1;
+ }
+
+ if (fullname)
+ pdb_set_fullname(sam_pwent, fullname, PDB_CHANGED);
+ if (homedir)
+ pdb_set_homedir(sam_pwent, homedir, PDB_CHANGED);
+ if (drive)
+ pdb_set_dir_drive(sam_pwent,drive, PDB_CHANGED);
+ if (script)
+ pdb_set_logon_script(sam_pwent, script, PDB_CHANGED);
+ if (profile)
+ pdb_set_profile_path (sam_pwent, profile, PDB_CHANGED);
+
+ if (account_control) {
+ uint16 not_settable = ~(ACB_DISABLED|ACB_HOMDIRREQ|ACB_PWNOTREQ|
+ ACB_PWNOEXP|ACB_AUTOLOCK);
+
+ uint16 newflag = pdb_decode_acct_ctrl(account_control);
+
+ if (newflag & not_settable) {
+ fprintf(stderr, "Can only set [NDHLX] flags\n");
+ pdb_free_sam(&sam_pwent);
+ return -1;
+ }
+
+ pdb_set_acct_ctrl(sam_pwent,
+ (pdb_get_acct_ctrl(sam_pwent) & not_settable) | newflag,
+ PDB_CHANGED);
+ }
+ if (user_sid) {
+ DOM_SID u_sid;
+ if (!string_to_sid(&u_sid, user_sid)) {
+ /* not a complete sid, may be a RID, try building a SID */
+ int u_rid;
+
+ if (sscanf(user_sid, "%d", &u_rid) != 1) {
+ fprintf(stderr, "Error passed string is not a complete user SID or RID!\n");
+ return -1;
+ }
+ sid_copy(&u_sid, get_global_sam_sid());
+ sid_append_rid(&u_sid, u_rid);
+ }
+ pdb_set_user_sid (sam_pwent, &u_sid, PDB_CHANGED);
+ }
+ if (group_sid) {
+ DOM_SID g_sid;
+ if (!string_to_sid(&g_sid, group_sid)) {
+ /* not a complete sid, may be a RID, try building a SID */
+ int g_rid;
+
+ if (sscanf(group_sid, "%d", &g_rid) != 1) {
+ fprintf(stderr, "Error passed string is not a complete group SID or RID!\n");
+ return -1;
+ }
+ sid_copy(&g_sid, get_global_sam_sid());
+ sid_append_rid(&g_sid, g_rid);
+ }
+ pdb_set_group_sid (sam_pwent, &g_sid, PDB_CHANGED);
+ }
+
+ if (badpw) {
+ pdb_set_bad_password_count(sam_pwent, 0, PDB_CHANGED);
+ pdb_set_bad_password_time(sam_pwent, 0, PDB_CHANGED);
+ }
+
+ if (NT_STATUS_IS_OK(in->pdb_update_sam_account (in, sam_pwent)))
+ print_user_info (in, username, True, False);
+ else {
+ fprintf (stderr, "Unable to modify entry!\n");
+ pdb_free_sam(&sam_pwent);
+ return -1;
+ }
+ pdb_free_sam(&sam_pwent);
+ return 0;
+}
+
+/*********************************************************
+ Add New User
+**********************************************************/
+static int new_user (struct pdb_context *in, const char *username,
+ const char *fullname, const char *homedir,
+ const char *drive, const char *script,
+ const char *profile, char *user_sid, char *group_sid)
+{
+ SAM_ACCOUNT *sam_pwent=NULL;
+ NTSTATUS nt_status;
+ char *password1, *password2, *staticpass;
+
+ get_global_sam_sid();
+
+ if (!NT_STATUS_IS_OK(nt_status = pdb_init_sam_new(&sam_pwent, username, 0))) {
+ DEBUG(0, ("could not create account to add new user %s\n", username));
+ return -1;
+ }
+
+ staticpass = getpass("new password:");
+ password1 = strdup(staticpass);
+ memset(staticpass, 0, strlen(staticpass));
+ staticpass = getpass("retype new password:");
+ password2 = strdup(staticpass);
+ memset(staticpass, 0, strlen(staticpass));
+ if (strcmp (password1, password2)) {
+ fprintf (stderr, "Passwords does not match!\n");
+ memset(password1, 0, strlen(password1));
+ SAFE_FREE(password1);
+ memset(password2, 0, strlen(password2));
+ SAFE_FREE(password2);
+ pdb_free_sam (&sam_pwent);
+ return -1;
+ }
+
+ pdb_set_plaintext_passwd(sam_pwent, password1);
+ memset(password1, 0, strlen(password1));
+ SAFE_FREE(password1);
+ memset(password2, 0, strlen(password2));
+ SAFE_FREE(password2);
+
+ if (fullname)
+ pdb_set_fullname(sam_pwent, fullname, PDB_CHANGED);
+ if (homedir)
+ pdb_set_homedir (sam_pwent, homedir, PDB_CHANGED);
+ if (drive)
+ pdb_set_dir_drive (sam_pwent, drive, PDB_CHANGED);
+ if (script)
+ pdb_set_logon_script(sam_pwent, script, PDB_CHANGED);
+ if (profile)
+ pdb_set_profile_path (sam_pwent, profile, PDB_CHANGED);
+ if (user_sid) {
+ DOM_SID u_sid;
+ if (!string_to_sid(&u_sid, user_sid)) {
+ /* not a complete sid, may be a RID, try building a SID */
+ int u_rid;
+
+ if (sscanf(user_sid, "%d", &u_rid) != 1) {
+ fprintf(stderr, "Error passed string is not a complete user SID or RID!\n");
+ return -1;
+ }
+ sid_copy(&u_sid, get_global_sam_sid());
+ sid_append_rid(&u_sid, u_rid);
+ }
+ pdb_set_user_sid (sam_pwent, &u_sid, PDB_CHANGED);
+ }
+ if (group_sid) {
+ DOM_SID g_sid;
+ if (!string_to_sid(&g_sid, group_sid)) {
+ /* not a complete sid, may be a RID, try building a SID */
+ int g_rid;
+
+ if (sscanf(group_sid, "%d", &g_rid) != 1) {
+ fprintf(stderr, "Error passed string is not a complete group SID or RID!\n");
+ return -1;
+ }
+ sid_copy(&g_sid, get_global_sam_sid());
+ sid_append_rid(&g_sid, g_rid);
+ }
+ pdb_set_group_sid (sam_pwent, &g_sid, PDB_CHANGED);
+ }
+
+ pdb_set_acct_ctrl (sam_pwent, ACB_NORMAL, PDB_CHANGED);
+
+ if (NT_STATUS_IS_OK(in->pdb_add_sam_account (in, sam_pwent))) {
+ print_user_info (in, username, True, False);
+ } else {
+ fprintf (stderr, "Unable to add user! (does it already exist?)\n");
+ pdb_free_sam (&sam_pwent);
+ return -1;
+ }
+ pdb_free_sam (&sam_pwent);
+ return 0;
+}
+
+/*********************************************************
+ Add New Machine
+**********************************************************/
+
+static int new_machine (struct pdb_context *in, const char *machine_in)
+{
+ SAM_ACCOUNT *sam_pwent=NULL;
+ fstring machinename;
+ fstring machineaccount;
+ struct passwd *pwd = NULL;
+
+ get_global_sam_sid();
+
+ fstrcpy(machinename, machine_in);
+ machinename[15]= '\0';
+
+ if (machinename[strlen (machinename) -1] == '$')
+ machinename[strlen (machinename) -1] = '\0';
+
+ strlower_m(machinename);
+
+ fstrcpy(machineaccount, machinename);
+ fstrcat(machineaccount, "$");
+
+ if ((pwd = getpwnam_alloc(machineaccount))) {
+ if (!NT_STATUS_IS_OK(pdb_init_sam_pw( &sam_pwent, pwd))) {
+ fprintf(stderr, "Could not init sam from pw\n");
+ passwd_free(&pwd);
+ return -1;
+ }
+ passwd_free(&pwd);
+ } else {
+ if (!NT_STATUS_IS_OK(pdb_init_sam (&sam_pwent))) {
+ fprintf(stderr, "Could not init sam from pw\n");
+ return -1;
+ }
+ }
+
+ pdb_set_plaintext_passwd (sam_pwent, machinename);
+
+ pdb_set_username (sam_pwent, machineaccount, PDB_CHANGED);
+
+ pdb_set_acct_ctrl (sam_pwent, ACB_WSTRUST, PDB_CHANGED);
+
+ pdb_set_group_sid_from_rid(sam_pwent, DOMAIN_GROUP_RID_COMPUTERS, PDB_CHANGED);
+
+ if (NT_STATUS_IS_OK(in->pdb_add_sam_account (in, sam_pwent))) {
+ print_user_info (in, machineaccount, True, False);
+ } else {
+ fprintf (stderr, "Unable to add machine! (does it already exist?)\n");
+ pdb_free_sam (&sam_pwent);
+ return -1;
+ }
+ pdb_free_sam (&sam_pwent);
+ return 0;
+}
+
+
+/**
+ * Add new trusting domain account
+ *
+ * @param in initialised pdb_context
+ * @param dom_name trusted domain name given in command line
+ *
+ * @return 0 on success, -1 otherwise
+ **/
+
+static int new_trustdom(struct pdb_context *in, const char *dom_name)
+{
+ /* TODO */
+ return -1;
+}
+
+
+/**
+ * Add new trust relationship password
+ *
+ * @param in initialised pdb_context
+ * @param dom_name trusting domain name given in command line
+ * @param dom_sid domain sid given in command line
+ * @param flag trust password type flag given in command line
+ *
+ * @return 0 on success, -1 otherwise
+ **/
+
+static int new_trustpw(struct pdb_context *in, const char *dom_name,
+ const char *dom_sid, const char* flag)
+{
+ TALLOC_CTX *mem_ctx = NULL;
+ SAM_TRUST_PASSWD trust;
+ NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
+ POLICY_HND connect_hnd;
+ DOM_SID *domain_sid = NULL;
+ smb_ucs2_t *uni_name = NULL;
+ char *givenpass, *domain_name = NULL;
+ struct in_addr srv_ip;
+ fstring srv_name, myname;
+ struct cli_state *cli;
+ time_t lct;
+
+ if (!dom_name) return -1;
+
+ mem_ctx = talloc_init("pdbedit: adding new trust password");
+
+ /* unicode name */
+ trust.private.uni_name_len = strnlen(dom_name, 32);
+ push_ucs2_talloc(mem_ctx, &uni_name, dom_name);
+ strncpy_w(trust.private.uni_name, uni_name, 32);
+
+ /* flags */
+ trust.private.flags = trustpw_flag(flag);
+
+ /* trusting SID */
+ if (!dom_sid) {
+ /* if sid is not specified in command line, do our best
+ to establish it */
+
+ /* find domain PDC */
+ if (!get_pdc_ip(dom_name, &srv_ip))
+ return -1;
+ if (is_zero_ip(srv_ip))
+ return -1;
+ if (!name_status_find(dom_name, 0x1b, 0x20, srv_ip, srv_name))
+ return -1;
+
+ get_myname(myname);
+
+ /* Connect the domain pdc... */
+ nt_status = cli_full_connection(&cli, myname, srv_name, &srv_ip, 139,
+ "IPC$", "IPC", "", "", "", 0, Undefined, NULL);
+ if (NT_STATUS_IS_ERR(nt_status))
+ return -1;
+ if (!cli_nt_session_open(cli, PI_LSARPC))
+ return -1;
+
+ /* ...and query the domain sid */
+ nt_status = cli_lsa_open_policy2(cli, mem_ctx, True, SEC_RIGHTS_QUERY_VALUE,
+ &connect_hnd);
+ if (NT_STATUS_IS_ERR(nt_status)) return -1;
+
+ nt_status = cli_lsa_query_info_policy(cli, mem_ctx, &connect_hnd,
+ 5, &domain_name, &domain_sid);
+ if (NT_STATUS_IS_ERR(nt_status)) return -1;
+
+ nt_status = cli_lsa_close(cli, mem_ctx, &connect_hnd);
+ if (NT_STATUS_IS_ERR(nt_status)) return -1;
+
+ cli_nt_session_close(cli);
+ cli_shutdown(cli);
+
+ /* copying sid to trust password structure */
+ sid_copy(&trust.private.domain_sid, domain_sid);
+
+ } else {
+ if (!string_to_sid(&trust.private.domain_sid, dom_sid)) {
+ printf("Error: wrong SID specified !\n");
+ return -1;
+ }
+ }
+
+ /* password */
+ givenpass = getpass("password:");
+ memset(trust.private.pass, '\0', FSTRING_LEN);
+ strncpy(trust.private.pass, givenpass, FSTRING_LEN);
+
+ /* last change time */
+ lct = time(NULL);
+ trust.private.mod_time = lct;
+
+ /* store trust password in passdb */
+ nt_status = in->pdb_add_trust_passwd(in, &trust);
+
+ talloc_destroy(mem_ctx);
+ if (NT_STATUS_IS_OK(nt_status))
+ return 0;
+
+ return -1;
+}
+
+
+/*********************************************************
+ Delete user entry
+**********************************************************/
+
+static int delete_user_entry (struct pdb_context *in, const char *username)
+{
+ SAM_ACCOUNT *samaccount = NULL;
+
+ if (!NT_STATUS_IS_OK(pdb_init_sam (&samaccount))) {
+ return -1;
+ }
+
+ if (!NT_STATUS_IS_OK(in->pdb_getsampwnam(in, samaccount, username))) {
+ fprintf (stderr, "user %s does not exist in the passdb\n", username);
+ return -1;
+ }
+
+ if (!NT_STATUS_IS_OK(in->pdb_delete_sam_account (in, samaccount))) {
+ fprintf (stderr, "Unable to delete user %s\n", username);
+ return -1;
+ }
+ return 0;
+}
+
+/*********************************************************
+ Delete machine entry
+**********************************************************/
+
+static int delete_machine_entry (struct pdb_context *in, const char *machinename)
+{
+ fstring name;
+ SAM_ACCOUNT *samaccount = NULL;
+
+ fstrcpy(name, machinename);
+ name[15] = '\0';
+ if (name[strlen(name)-1] != '$')
+ fstrcat (name, "$");
+
+ if (!NT_STATUS_IS_OK(pdb_init_sam (&samaccount))) {
+ return -1;
+ }
+
+ if (!NT_STATUS_IS_OK(in->pdb_getsampwnam(in, samaccount, name))) {
+ fprintf (stderr, "machine %s does not exist in the passdb\n", name);
+ return -1;
+ }
+
+ if (!NT_STATUS_IS_OK(in->pdb_delete_sam_account (in, samaccount))) {
+ fprintf (stderr, "Unable to delete machine %s\n", name);
+ return -1;
+ }
+
+ return 0;
+}
+
+/*********************************************************
+ Start here.
+**********************************************************/
+
+int main (int argc, char **argv)
+{
+ static BOOL list_users = False;
+ static BOOL verbose = False;
+ static BOOL spstyle = False;
+ static BOOL machine = False;
+ static BOOL trustdom = False;
+ static BOOL add_user = False;
+ static BOOL delete_user = False;
+ static BOOL modify_user = False;
+ uint32 setparms, checkparms;
+ int opt;
+ static char *full_name = NULL;
+ static const char *user_name = NULL;
+ static char *home_dir = NULL;
+ static char *home_drive = NULL;
+ static char *backend = NULL;
+ static char *backend_in = NULL;
+ static char *backend_out = NULL;
+ static BOOL transfer_groups = False;
+ static BOOL force_initialised_password = False;
+ static char *logon_script = NULL;
+ static char *profile_path = NULL;
+ static char *account_control = NULL;
+ static char *account_policy = NULL;
+ static char *user_sid = NULL;
+ static char *group_sid = NULL;
+ static long int account_policy_value = 0;
+ BOOL account_policy_value_set = False;
+ static BOOL badpw_reset = False;
+ /* trust password parameters */
+ static char *trustpw = NULL;
+ static char *trustsid = NULL;
+ static char *trustflags = NULL;
+
+ struct pdb_context *bin;
+ struct pdb_context *bout;
+ struct pdb_context *bdef;
+ poptContext pc;
+ struct poptOption long_options[] = {
+ POPT_AUTOHELP
+ {"list", 'L', POPT_ARG_NONE, &list_users, 0, "list all users", NULL},
+ {"verbose", 'v', POPT_ARG_NONE, &verbose, 0, "be verbose", NULL },
+ {"smbpasswd-style", 'w',POPT_ARG_NONE, &spstyle, 0, "give output in smbpasswd style", NULL},
+ {"user", 'u', POPT_ARG_STRING, &user_name, 0, "use username", "USER" },
+ {"fullname", 'f', POPT_ARG_STRING, &full_name, 0, "set full name", NULL},
+ {"homedir", 'h', POPT_ARG_STRING, &home_dir, 0, "set home directory", NULL},
+ {"drive", 'D', POPT_ARG_STRING, &home_drive, 0, "set home drive", NULL},
+ {"script", 'S', POPT_ARG_STRING, &logon_script, 0, "set logon script", NULL},
+ {"profile", 'p', POPT_ARG_STRING, &profile_path, 0, "set profile path", NULL},
+ {"user SID", 'U', POPT_ARG_STRING, &user_sid, 0, "set user SID or RID", NULL},
+ {"group SID", 'G', POPT_ARG_STRING, &group_sid, 0, "set group SID or RID", NULL},
+ {"create", 'a', POPT_ARG_NONE, &add_user, 0, "create user", NULL},
+ {"modify", 'r', POPT_ARG_NONE, &modify_user, 0, "modify user", NULL},
+ {"delete", 'x', POPT_ARG_NONE, &delete_user, 0, "delete user", NULL},
+ {"machine", 'm', POPT_ARG_NONE, &machine, 0, "account is a machine account", NULL},
+ {"trustdom", 'I', POPT_ARG_NONE, &trustdom, 0, "account is a domain trust account", NULL},
+ {"trustpw", 'N', POPT_ARG_STRING, &trustpw, 0, "trust password's domain name", NULL},
+ {"trustsid", 'T', POPT_ARG_STRING, &trustsid, 0, "trust password's domain sid", NULL},
+ {"trustflags", 'F', POPT_ARG_STRING, &trustflags, 0, "trust password flags", NULL},
+ {"backend", 'b', POPT_ARG_STRING, &backend, 0, "use different passdb backend as default backend", NULL},
+ {"import", 'i', POPT_ARG_STRING, &backend_in, 0, "import user accounts from this backend", NULL},
+ {"export", 'e', POPT_ARG_STRING, &backend_out, 0, "export user accounts to this backend", NULL},
+ {"group", 'g', POPT_ARG_NONE, &transfer_groups, 0, "use -i and -e for groups", NULL},
+ {"account-policy", 'P', POPT_ARG_STRING, &account_policy, 0,"value of an account policy (like maximum password age)",NULL},
+ {"value", 'C', POPT_ARG_LONG, &account_policy_value, 'C',"set the account policy to this value", NULL},
+ {"account-control", 'c', POPT_ARG_STRING, &account_control, 0, "Values of account control", NULL},
+ {"force-initialized-passwords", 0, POPT_ARG_NONE, &force_initialised_password, 0, "Force initialization of corrupt password strings in a passdb backend", NULL},
+ {"bad-password-count-reset", 'z', POPT_ARG_NONE, &badpw_reset, 0, "reset bad password count", NULL},
+ POPT_COMMON_SAMBA
+ POPT_TABLEEND
+ };
+
+ setup_logging("pdbedit", True);
+
+ pc = poptGetContext(NULL, argc, (const char **) argv, long_options,
+ POPT_CONTEXT_KEEP_FIRST);
+
+ while((opt = poptGetNextOpt(pc)) != -1) {
+ switch (opt) {
+ case 'C':
+ account_policy_value_set = True;
+ break;
+ }
+ }
+
+ poptGetArg(pc); /* Drop argv[0], the program name */
+
+ if (user_name == NULL)
+ user_name = poptGetArg(pc);
+
+ if (!lp_load(dyn_CONFIGFILE,True,False,False)) {
+ fprintf(stderr, "Can't load %s - run testparm to debug it\n", dyn_CONFIGFILE);
+ exit(1);
+ }
+
+ if(!initialize_password_db(False))
+ exit(1);
+
+ if (!init_names())
+ exit(1);
+
+ setparms = (backend ? BIT_BACKEND : 0) +
+ (verbose ? BIT_VERBOSE : 0) +
+ (spstyle ? BIT_SPSTYLE : 0) +
+ (full_name ? BIT_FULLNAME : 0) +
+ (home_dir ? BIT_HOMEDIR : 0) +
+ (home_drive ? BIT_HDIRDRIVE : 0) +
+ (logon_script ? BIT_LOGSCRIPT : 0) +
+ (profile_path ? BIT_PROFILE : 0) +
+ (machine ? BIT_MACHINE : 0) +
+ (trustdom ? BIT_TRUSTDOM : 0) +
+ (trustpw ? BIT_TRUSTPW : 0) +
+ (trustsid ? BIT_TRUSTSID : 0) +
+ (trustflags ? BIT_TRUSTFLAGS : 0) +
+ (user_name ? BIT_USER : 0) +
+ (list_users ? BIT_LIST : 0) +
+ (force_initialised_password ? BIT_FIX_INIT : 0) +
+ (modify_user ? BIT_MODIFY : 0) +
+ (add_user ? BIT_CREATE : 0) +
+ (delete_user ? BIT_DELETE : 0) +
+ (account_control ? BIT_ACCTCTRL : 0) +
+ (account_policy ? BIT_ACCPOLICY : 0) +
+ (account_policy_value_set ? BIT_ACCPOLVAL : 0) +
+ (backend_in ? BIT_IMPORT : 0) +
+ (backend_out ? BIT_EXPORT : 0) +
+ (badpw_reset ? BIT_BADPWRESET : 0);
+
+ if (setparms & BIT_BACKEND) {
+ if (!NT_STATUS_IS_OK(make_pdb_context_string(&bdef, backend))) {
+ fprintf(stderr, "Can't initialize passdb backend.\n");
+ return 1;
+ }
+ } else {
+ if (!NT_STATUS_IS_OK(make_pdb_context_list(&bdef, lp_passdb_backend()))) {
+ fprintf(stderr, "Can't initialize passdb backend.\n");
+ return 1;
+ }
+ }
+
+ /* the lowest bit options are always accepted */
+ checkparms = setparms & ~MASK_ALWAYS_GOOD;
+
+ if (checkparms & BIT_FIX_INIT) {
+ return fix_users_list(bdef);
+ }
+
+ /* account policy operations */
+ if ((checkparms & BIT_ACCPOLICY) && !(checkparms & ~(BIT_ACCPOLICY + BIT_ACCPOLVAL))) {
+ uint32 value;
+ int field = account_policy_name_to_fieldnum(account_policy);
+ if (field == 0) {
+ fprintf(stderr, "No account policy by that name\n");
+ exit(1);
+ }
+ if (!account_policy_get(field, &value)) {
+ fprintf(stderr, "valid account policy, but unable to fetch value!\n");
+ exit(1);
+ }
+ if (account_policy_value_set) {
+ printf("account policy value for %s was %u\n", account_policy, value);
+ if (!account_policy_set(field, account_policy_value)) {
+ fprintf(stderr, "valid account policy, but unable to set value!\n");
+ exit(1);
+ }
+ printf("account policy value for %s is now %lu\n", account_policy, account_policy_value);
+ exit(0);
+ } else {
+ printf("account policy value for %s is %u\n", account_policy, value);
+ exit(0);
+ }
+ }
+
+ /* import and export operations */
+ if (((checkparms & BIT_IMPORT) || (checkparms & BIT_EXPORT))
+ && !(checkparms & ~(BIT_IMPORT +BIT_EXPORT))) {
+ if (backend_in) {
+ if (!NT_STATUS_IS_OK(make_pdb_context_string(&bin, backend_in))) {
+ fprintf(stderr, "Can't initialize passdb backend.\n");
+ return 1;
+ }
+ } else {
+ bin = bdef;
+ }
+ if (backend_out) {
+ if (!NT_STATUS_IS_OK(make_pdb_context_string(&bout, backend_out))) {
+ fprintf(stderr, "Can't initialize %s.\n", backend_out);
+ return 1;
+ }
+ } else {
+ bout = bdef;
+ }
+ if (transfer_groups) {
+ return export_groups(bin, bout);
+ } else {
+ return export_database(bin, bout);
+ }
+ }
+
+ /* if BIT_USER is defined but nothing else then threat it as -l -u for compatibility */
+ /* fake up BIT_LIST if only BIT_USER is defined */
+ if ((checkparms & BIT_USER) && !(checkparms & ~BIT_USER)) {
+ checkparms += BIT_LIST;
+ }
+
+ /* modify flag is optional to maintain backwards compatibility */
+ /* fake up BIT_MODIFY if BIT_USER and at least one of MASK_USER_GOOD is defined */
+ if (!((checkparms & ~MASK_USER_GOOD) & ~BIT_USER) && (checkparms & MASK_USER_GOOD)) {
+ checkparms += BIT_MODIFY;
+ }
+
+ /* list users operations */
+ if (checkparms & BIT_LIST) {
+ if (!(checkparms & ~BIT_LIST)) {
+ print_users_list (bdef, verbose, spstyle);
+ return print_trustpw_list(bdef, verbose, spstyle);
+ }
+ if (!(checkparms & ~(BIT_USER + BIT_LIST))) {
+ return print_user_info (bdef, user_name, verbose, spstyle);
+
+ } else if (!(checkparms & ~(BIT_TRUSTPW + BIT_LIST))) {
+ return print_trust_info(bdef, trustpw, verbose, spstyle);
+ }
+ }
+
+ /* mask out users options */
+ checkparms &= ~MASK_USER_GOOD;
+
+ /* if bad password count is reset, we must be modifying */
+ if (checkparms & BIT_BADPWRESET) {
+ checkparms |= BIT_MODIFY;
+ checkparms &= ~BIT_BADPWRESET;
+ }
+
+ /* account operation */
+ if ((checkparms & BIT_CREATE) || (checkparms & BIT_MODIFY) || (checkparms & BIT_DELETE)) {
+ /* check use of -u option */
+ if (!(checkparms & (BIT_USER + BIT_TRUSTPW))) {
+ fprintf (stderr, "Username not specified! (use -u option)\n");
+ return -1;
+ }
+
+ /* account creation operations */
+ if (!(checkparms & ~(BIT_CREATE + BIT_USER + BIT_MACHINE + BIT_TRUSTDOM))) {
+ /* machine trust account */
+ if (checkparms & BIT_MACHINE) {
+ return new_machine (bdef, user_name);
+ /* interdomain trust account */
+ } else if (checkparms & BIT_TRUSTDOM) {
+ return new_trustdom(bdef, user_name);
+
+ /* ordinary user account */
+ } else {
+ return new_user (bdef, user_name, full_name, home_dir,
+ home_drive, logon_script,
+ profile_path, user_sid, group_sid);
+ }
+ }
+
+ /* account deletion operations */
+ if (!(checkparms & ~(BIT_DELETE + BIT_USER + BIT_MACHINE))) {
+ if (checkparms & BIT_MACHINE) {
+ return delete_machine_entry (bdef, user_name);
+ } else {
+ return delete_user_entry (bdef, user_name);
+ }
+ }
+
+ /* account modification operations */
+ if (!(checkparms & ~(BIT_MODIFY + BIT_USER))) {
+ return set_user_info (bdef, user_name, full_name,
+ home_dir,
+ home_drive,
+ logon_script,
+ profile_path, account_control,
+ user_sid, group_sid,
+ badpw_reset);
+ }
+ }
+
+ /* trust password operation */
+ if ((checkparms & BIT_CREATE) || (checkparms & BIT_MODIFY) || (checkparms & BIT_DELETE)) {
+ /* trust password creation */
+ if (!(checkparms & ~(BIT_CREATE + BIT_TRUSTPW + BIT_TRUSTSID + BIT_TRUSTFLAGS))) {
+ return new_trustpw(bdef, trustpw, trustsid, trustflags);
+ }
+ }
+
+
+ if (setparms >= 0x20) {
+ fprintf (stderr, "Incompatible or insufficient options on command line!\n");
+ }
+ poptPrintHelp(pc, stderr, 0);
+
+ return 1;
+}
+
diff --git a/source/utils/profiles.c b/source/utils/profiles.c
new file mode 100644
index 00000000000..a31674dfb2e
--- /dev/null
+++ b/source/utils/profiles.c
@@ -0,0 +1,742 @@
+/*
+ Samba Unix/Linux SMB client utility profiles.c
+ Copyright (C) 2002 Richard Sharpe, rsharpe@richardsharpe.com
+ Copyright (C) 2003 Jelmer Vernooij (conversion to popt)
+
+ This program is free software; you can 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. */
+
+/*************************************************************************
+
+ A utility to report and change SIDs in registry files
+
+ Many of the ideas in here come from other people and software.
+ I first looked in Wine in misc/registry.c and was also influenced by
+ http://www.wednesday.demon.co.uk/dosreg.html
+
+ Which seems to contain comments from someone else. I reproduce them here
+ incase the site above disappears. It actually comes from
+ http://home.eunet.no/~pnordahl/ntpasswd/WinReg.txt.
+
+The windows NT registry has 2 different blocks, where one can occure many
+times...
+
+the "regf"-Block
+================
+
+"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!
+
+Offset Size Contents
+0x00000000 D-Word ID: ASCII-"regf" = 0x66676572
+0x00000004 D-Word ???? //see struct REGF
+0x00000008 D-Word ???? Always the same value as at 0x00000004
+0x0000000C Q-Word last modify date in WinNT date-format
+0x00000014 D-Word 1
+0x00000018 D-Word 3
+0x0000001C D-Word 0
+0x00000020 D-Word 1
+0x00000024 D-Word Offset of 1st key record
+0x00000028 D-Word Size of the data-blocks (Filesize-4kb)
+0x0000002C D-Word 1
+0x000001FC D-Word Sum of all D-Words from 0x00000000 to
+0x000001FB //XOR of all words. Nigel
+
+I have analyzed more registry files (from multiple machines running
+NT 4.0 german version) and could not find an explanation for the values
+marked with ???? the rest of the first 4kb page is not important...
+
+the "hbin"-Block
+================
+I don't know what "hbin" stands for, but this block is always a multiple
+of 4kb in size.
+
+Inside these hbin-blocks the different records are placed. The memory-
+management looks like a C-compiler heap management to me...
+
+hbin-Header
+===========
+Offset Size Contents
+0x0000 D-Word ID: ASCII-"hbin" = 0x6E696268
+0x0004 D-Word Offset from the 1st hbin-Block
+0x0008 D-Word Offset to the next hbin-Block
+0x001C D-Word Block-size
+
+The values in 0x0008 and 0x001C should be the same, so I don't know
+if they are correct or swapped...
+
+From offset 0x0020 inside a hbin-block data is stored with the following
+format:
+
+Offset Size Contents
+0x0000 D-Word Data-block size //this size must be a
+multiple of 8. Nigel
+0x0004 ???? Data
+
+If the size field is negative (bit 31 set), the corresponding block
+is free and has a size of -blocksize!
+
+The data is stored as one record per block. Block size is a multiple
+of 4 and the last block reaches the next hbin-block, leaving no room.
+
+Records in the hbin-blocks
+==========================
+
+nk-Record
+
+ The nk-record can be treated as a kombination of tree-record and
+ key-record of the win 95 registry.
+
+lf-Record
+
+ The lf-record is the counterpart to the RGKN-record (the
+ hash-function)
+
+vk-Record
+
+ The vk-record consists information to a single value.
+
+sk-Record
+
+ sk (? Security Key ?) is the ACL of the registry.
+
+Value-Lists
+
+ The value-lists contain information about which values are inside a
+ sub-key and don't have a header.
+
+Datas
+
+ The datas of the registry are (like the value-list) stored without a
+ header.
+
+All offset-values are relative to the first hbin-block and point to the
+block-size field of the record-entry. to get the file offset, you have to add
+the header size (4kb) and the size field (4 bytes)...
+
+the nk-Record
+=============
+Offset Size Contents
+0x0000 Word ID: ASCII-"nk" = 0x6B6E
+0x0002 Word for the root-key: 0x2C, otherwise 0x20 //key symbolic links 0x10. Nigel
+0x0004 Q-Word write-date/time in windows nt notation
+0x0010 D-Word Offset of Owner/Parent key
+0x0014 D-Word number of sub-Keys
+0x001C D-Word Offset of the sub-key lf-Records
+0x0024 D-Word number of values
+0x0028 D-Word Offset of the Value-List
+0x002C D-Word Offset of the sk-Record
+
+0x0030 D-Word Offset of the Class-Name //see NK structure for the use of these fields. Nigel
+0x0044 D-Word Unused (data-trash) //some kind of run time index. Does not appear to be important. Nigel
+0x0048 Word name-length
+0x004A Word class-name length
+0x004C ???? key-name
+
+the Value-List
+==============
+Offset Size Contents
+0x0000 D-Word Offset 1st Value
+0x0004 D-Word Offset 2nd Value
+0x???? D-Word Offset nth Value
+
+To determine the number of values, you have to look at the owner-nk-record!
+
+Der vk-Record
+=============
+Offset Size Contents
+0x0000 Word ID: ASCII-"vk" = 0x6B76
+0x0002 Word name length
+0x0004 D-Word length of the data //if top bit is set when offset contains data. Nigel
+0x0008 D-Word Offset of Data
+0x000C D-Word Type of value
+0x0010 Word Flag
+0x0012 Word Unused (data-trash)
+0x0014 ???? Name
+
+If bit 0 of the flag-word is set, a name is present, otherwise the value has no name (=default)
+
+If the data-size is lower 5, the data-offset value is used to store the data itself!
+
+The data-types
+==============
+Wert Beteutung
+0x0001 RegSZ: character string (in UNICODE!)
+0x0002 ExpandSZ: string with "%var%" expanding (UNICODE!)
+0x0003 RegBin: raw-binary value
+0x0004 RegDWord: Dword
+0x0007 RegMultiSZ: multiple strings, separated with 0
+ (UNICODE!)
+
+The "lf"-record
+===============
+Offset Size Contents
+0x0000 Word ID: ASCII-"lf" = 0x666C
+0x0002 Word number of keys
+0x0004 ???? Hash-Records
+
+Hash-Record
+===========
+Offset Size Contents
+0x0000 D-Word Offset of corresponding "nk"-Record
+0x0004 D-Word ASCII: the first 4 characters of the key-name, padded with 0's. Case sensitiv!
+
+Keep in mind, that the value at 0x0004 is used for checking the data-consistency! If you change the
+key-name you have to change the hash-value too!
+
+//These hashrecords must be sorted low to high within the lf record. Nigel.
+
+The "sk"-block
+==============
+(due to the complexity of the SAM-info, not clear jet)
+
+Offset Size Contents
+0x0000 Word ID: ASCII-"sk" = 0x6B73
+0x0002 Word Unused
+0x0004 D-Word Offset of previous "sk"-Record
+0x0008 D-Word Offset of next "sk"-Record
+0x000C D-Word usage-counter
+0x0010 D-Word Size of "sk"-record in bytes
+???? //standard self
+relative security desciptor. Nigel
+???? ???? Security and auditing settings...
+????
+
+The usage counter counts the number of references to this
+"sk"-record. You can use one "sk"-record for the entire registry!
+
+Windows nt date/time format
+===========================
+The time-format is a 64-bit integer which is incremented every
+0,0000001 seconds by 1 (I don't know how accurate it realy is!)
+It starts with 0 at the 1st of january 1601 0:00! All values are
+stored in GMT time! The time-zone is important to get the real
+time!
+
+Common values for win95 and win-nt
+==================================
+Offset values marking an "end of list", are either 0 or -1 (0xFFFFFFFF).
+If a value has no name (length=0, flag(bit 0)=0), it is treated as the
+"Default" entry...
+If a value has no data (length=0), it is displayed as empty.
+
+simplyfied win-3.?? registry:
+=============================
+
++-----------+
+| next rec. |---+ +----->+------------+
+| first sub | | | | Usage cnt. |
+| name | | +-->+------------+ | | length |
+| value | | | | next rec. | | | text |------->+-------+
++-----------+ | | | name rec. |--+ +------------+ | xxxxx |
+ +------------+ | | value rec. |-------->+------------+ +-------+
+ v | +------------+ | Usage cnt. |
++-----------+ | | length |
+| next rec. | | | text |------->+-------+
+| first sub |------+ +------------+ | xxxxx |
+| name | +-------+
+| value |
++-----------+
+
+Greatly simplyfied structure of the nt-registry:
+================================================
+
++---------------------------------------------------------------+
+| |
+v |
++---------+ +---------->+-----------+ +----->+---------+ |
+| "nk" | | | lf-rec. | | | nk-rec. | |
+| ID | | | # of keys | | | parent |---+
+| Date | | | 1st key |--+ | .... |
+| parent | | +-----------+ +---------+
+| suk-keys|-----+
+| values |--------------------->+----------+
+| SK-rec. |---------------+ | 1. value |--> +----------+
+| class |--+ | +----------+ | vk-rec. |
++---------+ | | | .... |
+ v | | data |--> +-------+
+ +------------+ | +----------+ | xxxxx |
+ | Class name | | +-------+
+ +------------+ |
+ v
+ +---------+ +---------+
+ +----->| next sk |--->| Next sk |--+
+ | +---| prev sk |<---| prev sk | |
+ | | | .... | | ... | |
+ | | +---------+ +---------+ |
+ | | ^ | |
+ | +----------+ |
+ +-------------------------------+
+
+---------------------------------------------------------------------------
+
+Hope this helps.... (Although it was "fun" for me to uncover this things,
+ it took me several sleepless nights ;)
+
+ B.D.
+
+*************************************************************************/
+#include "includes.h"
+#include <stdio.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+typedef unsigned int DWORD;
+typedef unsigned short WORD;
+
+#define REG_REGF_ID 0x66676572
+
+typedef struct regf_block {
+ DWORD REGF_ID; /* regf */
+ DWORD uk1;
+ DWORD uk2;
+ DWORD tim1, tim2;
+ DWORD uk3; /* 1 */
+ DWORD uk4; /* 3 */
+ DWORD uk5; /* 0 */
+ DWORD uk6; /* 1 */
+ DWORD first_key; /* offset */
+ unsigned int dblk_size;
+ DWORD uk7[116]; /* 1 */
+ DWORD chksum;
+} REGF_HDR;
+
+typedef struct hbin_sub_struct {
+ DWORD dblocksize;
+ char data[1];
+} HBIN_SUB_HDR;
+
+#define REG_HBIN_ID 0x6E696268
+
+typedef struct hbin_struct {
+ DWORD HBIN_ID; /* hbin */
+ DWORD next_off;
+ DWORD prev_off;
+ DWORD uk1;
+ DWORD uk2;
+ DWORD uk3;
+ DWORD uk4;
+ DWORD blk_size;
+ HBIN_SUB_HDR hbin_sub_hdr;
+} HBIN_HDR;
+
+#define REG_NK_ID 0x6B6E
+
+typedef struct nk_struct {
+ WORD NK_ID;
+ WORD type;
+ DWORD t1, t2;
+ DWORD uk1;
+ DWORD own_off;
+ DWORD subk_num;
+ DWORD uk2;
+ DWORD lf_off;
+ DWORD uk3;
+ DWORD val_cnt;
+ DWORD val_off;
+ DWORD sk_off;
+ DWORD clsnam_off;
+} NK_HDR;
+
+#define REG_SK_ID 0x6B73
+
+typedef struct sk_struct {
+ WORD SK_ID;
+ WORD uk1;
+ DWORD prev_off;
+ DWORD next_off;
+ DWORD ref_cnt;
+ DWORD rec_size;
+ char sec_desc[1];
+} SK_HDR;
+
+typedef struct sec_desc_rec {
+ WORD rev;
+ WORD type;
+ DWORD owner_off;
+ DWORD group_off;
+ DWORD sacl_off;
+ DWORD dacl_off;
+} MY_SEC_DESC;
+
+typedef struct ace_struct {
+ unsigned char type;
+ unsigned char flags;
+ unsigned short length;
+ unsigned int perms;
+ DOM_SID trustee;
+} ACE;
+
+typedef struct acl_struct {
+ WORD rev;
+ WORD size;
+ DWORD num_aces;
+ ACE *aces; /* One or more ACEs */
+} ACL;
+
+#define OFF(f) (0x1000 + (f) + 4)
+
+static void print_sid(DOM_SID *sid);
+
+int verbose = 1;
+DOM_SID old_sid, new_sid;
+int change = 0, new = 0;
+
+/* Compare two SIDs for equality */
+static int my_sid_equal(DOM_SID *s1, DOM_SID *s2)
+{
+ int sa1, sa2;
+
+ if (s1->sid_rev_num != s2->sid_rev_num) return 0;
+
+ sa1 = s1->num_auths; sa2 = s2->num_auths;
+
+ if (sa1 != sa2) return 0;
+
+ return !memcmp((char *)&s1->id_auth, (char *)&s2->id_auth,
+ 6 + sa1 * 4);
+
+}
+
+/*
+ * Quick and dirty to read a SID in S-1-5-21-x-y-z-rid format and
+ * construct a DOM_SID
+ */
+static int get_sid(DOM_SID *sid, const unsigned char *sid_str)
+{
+ int i = 0, auth;
+ const unsigned char *lstr;
+
+ if (strncmp(sid_str, "S-1-5", 5)) {
+ fprintf(stderr, "Does not conform to S-1-5...: %s\n", sid_str);
+ return 0;
+ }
+
+ /* We only allow strings of form S-1-5... */
+
+ sid->sid_rev_num = 1;
+ sid->id_auth[5] = 5;
+
+ lstr = sid_str + 5;
+
+ while (1) {
+ if (!lstr || !lstr[0] || sscanf(lstr, "-%u", &auth) == 0) {
+ if (i < 4) {
+ fprintf(stderr, "Not of form -d-d...: %s, %u\n", lstr, i);
+ return 0;
+ }
+ sid->num_auths=i;
+ print_sid(sid);
+ return 1;
+ }
+
+ SIVAL(&sid->sub_auths[i], 0, auth);
+ i++;
+ lstr = (const unsigned char *)strchr(lstr + 1, '-');
+ }
+
+ return 1;
+}
+
+#if 0
+
+/*
+ * Replace SID1, component by component with SID2
+ * Assumes will never be called with unequal length SIDS
+ * so only touches 21-x-y-z-rid portion
+ * This routine does not need to deal with endianism as
+ * long as the incoming SIDs are both in the same (LE) format.
+ */
+static void change_sid(DOM_SID *s1, DOM_SID *s2)
+{
+ int i;
+
+ for (i=0; i<s1->num_auths; i++) {
+ s1->sub_auths[i] = s2->sub_auths[i];
+ }
+}
+
+#endif
+
+static void print_sid(DOM_SID *sid)
+{
+ int i, comps = sid->num_auths;
+ fprintf(stdout, "S-%u-%u", sid->sid_rev_num, sid->id_auth[5]);
+
+ for (i = 0; i < comps; i++) {
+
+ fprintf(stdout, "-%u", IVAL(&sid->sub_auths[i],0));
+
+ }
+ fprintf(stdout, "\n");
+}
+
+static void process_sid(DOM_SID *sid, DOM_SID *o_sid, DOM_SID *n_sid)
+{
+ int i;
+ if (my_sid_equal(sid, o_sid)) {
+
+ for (i=0; i<sid->num_auths; i++) {
+ sid->sub_auths[i] = n_sid->sub_auths[i];
+
+ }
+
+ }
+
+}
+
+static void process_acl(ACL *acl, const char *prefix)
+{
+ int ace_cnt, i;
+ ACE *ace;
+
+ ace_cnt = IVAL(&acl->num_aces, 0);
+ ace = (ACE *)&acl->aces;
+ if (verbose) fprintf(stdout, "%sACEs: %u\n", prefix, ace_cnt);
+ for (i=0; i<ace_cnt; i++) {
+ if (verbose) fprintf(stdout, "%s Perms: %08X, SID: ", prefix,
+ IVAL(&ace->perms, 0));
+ if (change)
+ process_sid(&ace->trustee, &old_sid, &new_sid);
+ print_sid(&ace->trustee);
+ ace = (ACE *)((char *)ace + SVAL(&ace->length, 0));
+ }
+}
+
+int main(int argc, char *argv[])
+{
+ int opt;
+ int fd, start = 0;
+ char *base;
+ struct stat sbuf;
+ REGF_HDR *regf_hdr;
+ HBIN_HDR *hbin_hdr;
+ NK_HDR *nk_hdr;
+ SK_HDR *sk_hdr;
+ DWORD first_sk_off, sk_off;
+ MY_SEC_DESC *sec_desc;
+ int *ptr;
+ struct poptOption long_options[] = {
+ POPT_AUTOHELP
+ { "verbose", 'v', POPT_ARG_NONE, NULL, 'v', "Sets verbose mode" },
+ { "change-sid", 'c', POPT_ARG_STRING, NULL, 'c', "Provides SID to change" },
+ { "new-sid", 'n', POPT_ARG_STRING, NULL, 'n', "Provides SID to change to" },
+ { 0, 0, 0, 0 }
+ };
+
+ poptContext pc;
+
+ pc = poptGetContext("profiles", argc, (const char **)argv, long_options,
+ POPT_CONTEXT_KEEP_FIRST);
+
+ poptSetOtherOptionHelp(pc, "<profilefile>");
+
+ /*
+ * Now, process the arguments
+ */
+
+ while ((opt = poptGetNextOpt(pc)) != -1) {
+ switch (opt) {
+ case 'c':
+ change = 1;
+ if (!get_sid(&old_sid, poptGetOptArg(pc))) {
+ fprintf(stderr, "Argument to -c should be a SID in form of S-1-5-...\n");
+ poptPrintUsage(pc, stderr, 0);
+ exit(254);
+ }
+ break;
+
+ case 'n':
+ new = 1;
+ if (!get_sid(&new_sid, poptGetOptArg(pc))) {
+ fprintf(stderr, "Argument to -n should be a SID in form of S-1-5-...\n");
+ poptPrintUsage(pc, stderr, 0);
+ exit(253);
+ }
+
+ break;
+
+ case 'v':
+ verbose++;
+ break;
+ }
+ }
+
+ if (!poptPeekArg(pc)) {
+ poptPrintUsage(pc, stderr, 0);
+ exit(1);
+ }
+
+ if ((!change & new) || (change & !new)) {
+ fprintf(stderr, "You must specify both -c and -n if one or the other is set!\n");
+ poptPrintUsage(pc, stderr, 0);
+ exit(252);
+ }
+
+ poptGetArg(pc); /* To get argv[0] */
+
+ fd = open(poptPeekArg(pc), O_RDWR, 0000);
+
+ if (fd < 0) {
+ fprintf(stderr, "Could not open %s: %s\n", poptPeekArg(pc),
+ strerror(errno));
+ exit(2);
+ }
+
+ if (fstat(fd, &sbuf) < 0) {
+ fprintf(stderr, "Could not stat file %s, %s\n", poptPeekArg(pc),
+ strerror(errno));
+ exit(3);
+ }
+
+ /*
+ * Now, mmap the file into memory, check the header and start
+ * dealing with the records. We are interested in the sk record
+ */
+ start = 0;
+
+#ifdef HAVE_MMAP
+ base = mmap(&start, sbuf.st_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
+#else
+ base = (char *)-1;
+ errno = ENOSYS;
+#endif
+
+ if ((int)base == -1) {
+ fprintf(stderr, "Could not mmap file: %s, %s\n", poptPeekArg(pc),
+ strerror(errno));
+ exit(4);
+ }
+
+ /*
+ * In what follows, and in places above, in order to work on both LE and
+ * BE platforms, we have to use the Samba macros to extract SHORT, LONG
+ * and associated UNSIGNED quantities from the data in the mmap'd file.
+ * NOTE, however, that we do not need to do anything with memory
+ * addresses that we construct from pointers in our address space.
+ * For example,
+ *
+ * sec_desc = (MY_SEC_DESC *)&(sk_hdr->sec_desc[0]);
+ *
+ * is simply taking the address of a structure we already have the address
+ * of in our address space, while, the fields within it, will have to
+ * be accessed with the macros:
+ *
+ * owner_sid = (DOM_SID *)(&sk_hdr->sec_desc[0] +
+ * IVAL(&sec_desc->owner_off, 0));
+ *
+ * Which is pulling out an offset and adding it to an existing pointer.
+ *
+ */
+
+ regf_hdr = (REGF_HDR *)base;
+
+ if (verbose) fprintf(stdout, "Registry file size: %u\n", (unsigned int)sbuf.st_size);
+
+ if (IVAL(&regf_hdr->REGF_ID, 0) != REG_REGF_ID) {
+ fprintf(stderr, "Incorrect Registry file (doesn't have header ID): %s\n", poptPeekArg(pc));
+ exit(5);
+ }
+
+ if (verbose) fprintf(stdout, "First Key Off: %u, Data Block Size: %u\n",
+ IVAL(&regf_hdr->first_key, 0),
+ IVAL(&regf_hdr->dblk_size, 0));
+
+ hbin_hdr = (HBIN_HDR *)(base + 0x1000); /* No need for Endian stuff */
+
+ /*
+ * This should be the hbin_hdr
+ */
+
+ if (IVAL(&hbin_hdr->HBIN_ID, 0) != REG_HBIN_ID) {
+ fprintf(stderr, "Incorrect hbin hdr: %s\n", poptPeekArg(pc));
+ exit(6);
+ }
+
+ if (verbose) fprintf(stdout, "Next Off: %u, Prev Off: %u\n",
+ IVAL(&hbin_hdr->next_off, 0),
+ IVAL(&hbin_hdr->prev_off, 0));
+
+ nk_hdr = (NK_HDR *)(base + 0x1000 + IVAL(&regf_hdr->first_key, 0) + 4);
+
+ if (SVAL(&nk_hdr->NK_ID, 0) != REG_NK_ID) {
+ fprintf(stderr, "Incorrect NK Header: %s\n", poptPeekArg(pc));
+ exit(7);
+ }
+
+ sk_off = first_sk_off = IVAL(&nk_hdr->sk_off, 0);
+ if (verbose) {
+ fprintf(stdout, "Type: %0x\n", SVAL(&nk_hdr->type, 0));
+ fprintf(stdout, "SK Off : %o\n", (0x1000 + sk_off + 4));
+ }
+
+ sk_hdr = (SK_HDR *)(base + 0x1000 + sk_off + 4);
+
+ do {
+ DOM_SID *owner_sid, *group_sid;
+ ACL *sacl, *dacl;
+ if (SVAL(&sk_hdr->SK_ID, 0) != REG_SK_ID) {
+ fprintf(stderr, "Incorrect SK Header format: %08X\n",
+ (0x1000 + sk_off + 4));
+ exit(8);
+ }
+ ptr = (int *)sk_hdr;
+ if (verbose) fprintf(stdout, "Off: %08X, Refs: %u, Size: %u\n",
+ sk_off, IVAL(&sk_hdr->ref_cnt, 0),
+ IVAL(&sk_hdr->rec_size, 0));
+
+ sec_desc = (MY_SEC_DESC *)&(sk_hdr->sec_desc[0]);
+ owner_sid = (DOM_SID *)(&sk_hdr->sec_desc[0] +
+ IVAL(&sec_desc->owner_off, 0));
+ group_sid = (DOM_SID *)(&sk_hdr->sec_desc[0] +
+ IVAL(&sec_desc->group_off, 0));
+ sacl = (ACL *)(&sk_hdr->sec_desc[0] +
+ IVAL(&sec_desc->sacl_off, 0));
+ dacl = (ACL *)(&sk_hdr->sec_desc[0] +
+ IVAL(&sec_desc->dacl_off, 0));
+ if (verbose)fprintf(stdout, " Owner SID: ");
+ if (change) process_sid(owner_sid, &old_sid, &new_sid);
+ if (verbose) print_sid(owner_sid);
+ if (verbose) fprintf(stdout, " Group SID: ");
+ if (change) process_sid(group_sid, &old_sid, &new_sid);
+ if (verbose) print_sid(group_sid);
+ fprintf(stdout, " SACL: ");
+ if (!sec_desc->sacl_off) { /* LE zero == BE zero */
+ if (verbose) fprintf(stdout, "NONE\n");
+ }
+ else
+ process_acl(sacl, " ");
+ if (verbose) fprintf(stdout, " DACL: ");
+ if (!sec_desc->dacl_off) {
+ if (verbose) fprintf(stdout, "NONE\n");
+ }
+ else
+ process_acl(dacl, " ");
+ sk_off = IVAL(&sk_hdr->prev_off, 0);
+ sk_hdr = (SK_HDR *)(base + OFF(IVAL(&sk_hdr->prev_off, 0)));
+ } while (sk_off != first_sk_off);
+
+#ifdef HAVE_MMAP
+ munmap(base, sbuf.st_size);
+#endif
+
+ poptFreeContext(pc);
+
+ close(fd);
+ return 0;
+}
diff --git a/source/utils/rpccheck.c b/source/utils/rpccheck.c
new file mode 100644
index 00000000000..ae109f69b65
--- /dev/null
+++ b/source/utils/rpccheck.c
@@ -0,0 +1,62 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ Copyright (C) Jean François Micouleau 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 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"
+
+main()
+{
+ char filter[]="0123456789ABCDEF";
+
+ char s[128];
+ char d=0;
+ int x=0;
+ prs_struct ps;
+ TALLOC_CTX *ctx;
+
+ /* change that struct */
+ SAMR_R_QUERY_USERINFO rpc_stub;
+
+ ZERO_STRUCT(rpc_stub);
+
+ setup_logging("", True);
+ DEBUGLEVEL=10;
+
+ ctx=talloc_init("main");
+ if (!ctx) exit(1);
+
+ prs_init(&ps, 1600, 4, ctx, MARSHALL);
+
+ while (scanf("%s", s)!=-1) {
+ if (strlen(s)==2 && strchr_m(filter, *s)!=NULL && strchr_m(filter, *(s+1))!=NULL) {
+ d=strtol(s, NULL, 16);
+ if(!prs_append_data(&ps, &d, 1))
+ printf("error while reading data\n");
+ }
+ }
+
+ prs_switch_type(&ps, UNMARSHALL);
+ prs_set_offset(&ps, 0);
+
+ /* change that call */
+ if(!samr_io_r_query_userinfo("", &rpc_stub, &ps, 0))
+ printf("error while UNMARSHALLING the data\n");
+
+ printf("\n");
+}
diff --git a/source/utils/smbcacls.c b/source/utils/smbcacls.c
new file mode 100644
index 00000000000..5a70d168842
--- /dev/null
+++ b/source/utils/smbcacls.c
@@ -0,0 +1,877 @@
+/*
+ Unix SMB/CIFS implementation.
+ ACL get/set utility
+
+ Copyright (C) Andrew Tridgell 2000
+ Copyright (C) Tim Potter 2000
+ Copyright (C) Jeremy Allison 2000
+ 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 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"
+
+static pstring owner_username;
+static fstring server;
+static int test_args = False;
+static TALLOC_CTX *ctx;
+
+#define CREATE_ACCESS_READ READ_CONTROL_ACCESS
+
+/* numeric is set when the user wants numeric SIDs and ACEs rather
+ than going via LSA calls to resolve them */
+static BOOL numeric = False;
+
+enum acl_mode {SMB_ACL_SET, SMB_ACL_DELETE, SMB_ACL_MODIFY, SMB_ACL_ADD };
+enum chown_mode {REQUEST_NONE, REQUEST_CHOWN, REQUEST_CHGRP};
+enum exit_values {EXIT_OK, EXIT_FAILED, EXIT_PARSE_ERROR};
+
+struct perm_value {
+ const char *perm;
+ uint32 mask;
+};
+
+/* These values discovered by inspection */
+
+static const struct perm_value special_values[] = {
+ { "R", 0x00120089 },
+ { "W", 0x00120116 },
+ { "X", 0x001200a0 },
+ { "D", 0x00010000 },
+ { "P", 0x00040000 },
+ { "O", 0x00080000 },
+ { NULL, 0 },
+};
+
+static const struct perm_value standard_values[] = {
+ { "READ", 0x001200a9 },
+ { "CHANGE", 0x001301bf },
+ { "FULL", 0x001f01ff },
+ { NULL, 0 },
+};
+
+static struct cli_state *global_hack_cli;
+static POLICY_HND pol;
+static BOOL got_policy_hnd;
+
+static struct cli_state *connect_one(const char *share);
+
+/* Open cli connection and policy handle */
+
+static BOOL cacls_open_policy_hnd(void)
+{
+ /* Initialise cli LSA connection */
+
+ if (!global_hack_cli) {
+ global_hack_cli = connect_one("IPC$");
+ if (!cli_nt_session_open (global_hack_cli, PI_LSARPC)) {
+ return False;
+ }
+ }
+
+ /* Open policy handle */
+
+ if (!got_policy_hnd) {
+
+ /* Some systems don't support SEC_RIGHTS_MAXIMUM_ALLOWED,
+ but NT sends 0x2000000 so we might as well do it too. */
+
+ if (!NT_STATUS_IS_OK(cli_lsa_open_policy(global_hack_cli, global_hack_cli->mem_ctx, True,
+ GENERIC_EXECUTE_ACCESS, &pol))) {
+ return False;
+ }
+
+ got_policy_hnd = True;
+ }
+
+ return True;
+}
+
+/* convert a SID to a string, either numeric or username/group */
+static void SidToString(fstring str, DOM_SID *sid)
+{
+ char **domains = NULL;
+ char **names = NULL;
+ uint32 *types = NULL;
+
+ sid_to_string(str, sid);
+
+ if (numeric) return;
+
+ /* Ask LSA to convert the sid to a name */
+
+ if (!cacls_open_policy_hnd() ||
+ !NT_STATUS_IS_OK(cli_lsa_lookup_sids(global_hack_cli, global_hack_cli->mem_ctx,
+ &pol, 1, sid, &domains,
+ &names, &types)) ||
+ !domains || !domains[0] || !names || !names[0]) {
+ return;
+ }
+
+ /* Converted OK */
+
+ slprintf(str, sizeof(fstring) - 1, "%s%s%s",
+ domains[0], lp_winbind_separator(),
+ names[0]);
+
+}
+
+/* convert a string to a SID, either numeric or username/group */
+static BOOL StringToSid(DOM_SID *sid, const char *str)
+{
+ uint32 *types = NULL;
+ DOM_SID *sids = NULL;
+ BOOL result = True;
+
+ if (strncmp(str, "S-", 2) == 0) {
+ return string_to_sid(sid, str);
+ }
+
+ if (!cacls_open_policy_hnd() ||
+ !NT_STATUS_IS_OK(cli_lsa_lookup_names(global_hack_cli, global_hack_cli->mem_ctx,
+ &pol, 1, &str, &sids,
+ &types))) {
+ result = False;
+ goto done;
+ }
+
+ sid_copy(sid, &sids[0]);
+ done:
+
+ return result;
+}
+
+
+/* print an ACE on a FILE, using either numeric or ascii representation */
+static void print_ace(FILE *f, SEC_ACE *ace)
+{
+ const struct perm_value *v;
+ fstring sidstr;
+ int do_print = 0;
+ uint32 got_mask;
+
+ SidToString(sidstr, &ace->trustee);
+
+ fprintf(f, "%s:", sidstr);
+
+ if (numeric) {
+ fprintf(f, "%d/%d/0x%08x",
+ ace->type, ace->flags, ace->info.mask);
+ return;
+ }
+
+ /* Ace type */
+
+ if (ace->type == SEC_ACE_TYPE_ACCESS_ALLOWED) {
+ fprintf(f, "ALLOWED");
+ } else if (ace->type == SEC_ACE_TYPE_ACCESS_DENIED) {
+ fprintf(f, "DENIED");
+ } else {
+ fprintf(f, "%d", ace->type);
+ }
+
+ /* Not sure what flags can be set in a file ACL */
+
+ fprintf(f, "/%d/", ace->flags);
+
+ /* Standard permissions */
+
+ for (v = standard_values; v->perm; v++) {
+ if (ace->info.mask == v->mask) {
+ fprintf(f, "%s", v->perm);
+ return;
+ }
+ }
+
+ /* Special permissions. Print out a hex value if we have
+ leftover bits in the mask. */
+
+ got_mask = ace->info.mask;
+
+ again:
+ for (v = special_values; v->perm; v++) {
+ if ((ace->info.mask & v->mask) == v->mask) {
+ if (do_print) {
+ fprintf(f, "%s", v->perm);
+ }
+ got_mask &= ~v->mask;
+ }
+ }
+
+ if (!do_print) {
+ if (got_mask != 0) {
+ fprintf(f, "0x%08x", ace->info.mask);
+ } else {
+ do_print = 1;
+ goto again;
+ }
+ }
+}
+
+
+/* parse an ACE in the same format as print_ace() */
+static BOOL parse_ace(SEC_ACE *ace, char *str)
+{
+ char *p;
+ const char *cp;
+ fstring tok;
+ unsigned atype, aflags, amask;
+ DOM_SID sid;
+ SEC_ACCESS mask;
+ const struct perm_value *v;
+
+ ZERO_STRUCTP(ace);
+ p = strchr_m(str,':');
+ if (!p) return False;
+ *p = '\0';
+ p++;
+ /* Try to parse numeric form */
+
+ if (sscanf(p, "%i/%i/%i", &atype, &aflags, &amask) == 3 &&
+ StringToSid(&sid, str)) {
+ goto done;
+ }
+
+ /* Try to parse text form */
+
+ if (!StringToSid(&sid, str)) {
+ return False;
+ }
+
+ cp = p;
+ if (!next_token(&cp, tok, "/", sizeof(fstring))) {
+ return False;
+ }
+
+ if (strncmp(tok, "ALLOWED", strlen("ALLOWED")) == 0) {
+ atype = SEC_ACE_TYPE_ACCESS_ALLOWED;
+ } else if (strncmp(tok, "DENIED", strlen("DENIED")) == 0) {
+ atype = SEC_ACE_TYPE_ACCESS_DENIED;
+ } else {
+ return False;
+ }
+
+ /* Only numeric form accepted for flags at present */
+
+ if (!(next_token(&cp, tok, "/", sizeof(fstring)) &&
+ sscanf(tok, "%i", &aflags))) {
+ return False;
+ }
+
+ if (!next_token(&cp, tok, "/", sizeof(fstring))) {
+ return False;
+ }
+
+ if (strncmp(tok, "0x", 2) == 0) {
+ if (sscanf(tok, "%i", &amask) != 1) {
+ return False;
+ }
+ goto done;
+ }
+
+ for (v = standard_values; v->perm; v++) {
+ if (strcmp(tok, v->perm) == 0) {
+ amask = v->mask;
+ goto done;
+ }
+ }
+
+ p = tok;
+
+ while(*p) {
+ BOOL found = False;
+
+ for (v = special_values; v->perm; v++) {
+ if (v->perm[0] == *p) {
+ amask |= v->mask;
+ found = True;
+ }
+ }
+
+ if (!found) return False;
+ p++;
+ }
+
+ if (*p) {
+ return False;
+ }
+
+ done:
+ mask.mask = amask;
+ init_sec_ace(ace, &sid, atype, mask, aflags);
+ return True;
+}
+
+/* add an ACE to a list of ACEs in a SEC_ACL */
+static BOOL add_ace(SEC_ACL **the_acl, SEC_ACE *ace)
+{
+ SEC_ACL *new;
+ SEC_ACE *aces;
+ if (! *the_acl) {
+ (*the_acl) = make_sec_acl(ctx, 3, 1, ace);
+ return True;
+ }
+
+ aces = calloc(1+(*the_acl)->num_aces,sizeof(SEC_ACE));
+ memcpy(aces, (*the_acl)->ace, (*the_acl)->num_aces * sizeof(SEC_ACE));
+ memcpy(aces+(*the_acl)->num_aces, ace, sizeof(SEC_ACE));
+ new = make_sec_acl(ctx,(*the_acl)->revision,1+(*the_acl)->num_aces, aces);
+ SAFE_FREE(aces);
+ (*the_acl) = new;
+ return True;
+}
+
+/* parse a ascii version of a security descriptor */
+static SEC_DESC *sec_desc_parse(char *str)
+{
+ const char *p = str;
+ fstring tok;
+ SEC_DESC *ret;
+ size_t sd_size;
+ DOM_SID *grp_sid=NULL, *owner_sid=NULL;
+ SEC_ACL *dacl=NULL;
+ int revision=1;
+
+ while (next_token(&p, tok, "\t,\r\n", sizeof(tok))) {
+
+ if (strncmp(tok,"REVISION:", 9) == 0) {
+ revision = strtol(tok+9, NULL, 16);
+ continue;
+ }
+
+ if (strncmp(tok,"OWNER:", 6) == 0) {
+ owner_sid = (DOM_SID *)calloc(1, sizeof(DOM_SID));
+ if (!owner_sid ||
+ !StringToSid(owner_sid, tok+6)) {
+ printf("Failed to parse owner sid\n");
+ return NULL;
+ }
+ continue;
+ }
+
+ if (strncmp(tok,"GROUP:", 6) == 0) {
+ grp_sid = (DOM_SID *)calloc(1, sizeof(DOM_SID));
+ if (!grp_sid ||
+ !StringToSid(grp_sid, tok+6)) {
+ printf("Failed to parse group sid\n");
+ return NULL;
+ }
+ continue;
+ }
+
+ if (strncmp(tok,"ACL:", 4) == 0) {
+ SEC_ACE ace;
+ if (!parse_ace(&ace, tok+4)) {
+ printf("Failed to parse ACL %s\n", tok);
+ return NULL;
+ }
+ if(!add_ace(&dacl, &ace)) {
+ printf("Failed to add ACL %s\n", tok);
+ return NULL;
+ }
+ continue;
+ }
+
+ printf("Failed to parse security descriptor\n");
+ return NULL;
+ }
+
+ ret = make_sec_desc(ctx,revision, SEC_DESC_SELF_RELATIVE, owner_sid, grp_sid,
+ NULL, dacl, &sd_size);
+
+ SAFE_FREE(grp_sid);
+ SAFE_FREE(owner_sid);
+
+ return ret;
+}
+
+
+/* print a ascii version of a security descriptor on a FILE handle */
+static void sec_desc_print(FILE *f, SEC_DESC *sd)
+{
+ fstring sidstr;
+ uint32 i;
+
+ fprintf(f, "REVISION:%d\n", sd->revision);
+
+ /* Print owner and group sid */
+
+ if (sd->owner_sid) {
+ SidToString(sidstr, sd->owner_sid);
+ } else {
+ fstrcpy(sidstr, "");
+ }
+
+ fprintf(f, "OWNER:%s\n", sidstr);
+
+ if (sd->grp_sid) {
+ SidToString(sidstr, sd->grp_sid);
+ } else {
+ fstrcpy(sidstr, "");
+ }
+
+ fprintf(f, "GROUP:%s\n", sidstr);
+
+ /* Print aces */
+ for (i = 0; sd->dacl && i < sd->dacl->num_aces; i++) {
+ SEC_ACE *ace = &sd->dacl->ace[i];
+ fprintf(f, "ACL:");
+ print_ace(f, ace);
+ fprintf(f, "\n");
+ }
+
+}
+
+/*****************************************************
+dump the acls for a file
+*******************************************************/
+static int cacl_dump(struct cli_state *cli, char *filename)
+{
+ int result = EXIT_FAILED;
+ int fnum = -1;
+ SEC_DESC *sd;
+
+ if (test_args)
+ return EXIT_OK;
+
+ fnum = cli_nt_create(cli, filename, CREATE_ACCESS_READ);
+
+ if (fnum == -1) {
+ printf("Failed to open %s: %s\n", filename, cli_errstr(cli));
+ goto done;
+ }
+
+ sd = cli_query_secdesc(cli, fnum, ctx);
+
+ if (!sd) {
+ printf("ERROR: secdesc query failed: %s\n", cli_errstr(cli));
+ goto done;
+ }
+
+ sec_desc_print(stdout, sd);
+
+ result = EXIT_OK;
+
+done:
+ if (fnum != -1)
+ cli_close(cli, fnum);
+
+ return result;
+}
+
+/*****************************************************
+Change the ownership or group ownership of a file. Just
+because the NT docs say this can't be done :-). JRA.
+*******************************************************/
+
+static int owner_set(struct cli_state *cli, enum chown_mode change_mode,
+ char *filename, char *new_username)
+{
+ int fnum;
+ DOM_SID sid;
+ SEC_DESC *sd, *old;
+ size_t sd_size;
+
+ fnum = cli_nt_create(cli, filename, CREATE_ACCESS_READ);
+
+ if (fnum == -1) {
+ printf("Failed to open %s: %s\n", filename, cli_errstr(cli));
+ return EXIT_FAILED;
+ }
+
+ if (!StringToSid(&sid, new_username))
+ return EXIT_PARSE_ERROR;
+
+ old = cli_query_secdesc(cli, fnum, ctx);
+
+ cli_close(cli, fnum);
+
+ if (!old) {
+ printf("owner_set: Failed to query old descriptor\n");
+ return EXIT_FAILED;
+ }
+
+ sd = make_sec_desc(ctx,old->revision, old->type,
+ (change_mode == REQUEST_CHOWN) ? &sid : NULL,
+ (change_mode == REQUEST_CHGRP) ? &sid : NULL,
+ NULL, NULL, &sd_size);
+
+ fnum = cli_nt_create(cli, filename, WRITE_OWNER_ACCESS);
+
+ if (fnum == -1) {
+ printf("Failed to open %s: %s\n", filename, cli_errstr(cli));
+ return EXIT_FAILED;
+ }
+
+ if (!cli_set_secdesc(cli, fnum, sd)) {
+ printf("ERROR: secdesc set failed: %s\n", cli_errstr(cli));
+ }
+
+ cli_close(cli, fnum);
+
+ return EXIT_OK;
+}
+
+
+/* The MSDN is contradictory over the ordering of ACE entries in an ACL.
+ However NT4 gives a "The information may have been modified by a
+ computer running Windows NT 5.0" if denied ACEs do not appear before
+ allowed ACEs. */
+
+static int ace_compare(SEC_ACE *ace1, SEC_ACE *ace2)
+{
+ if (sec_ace_equal(ace1, ace2))
+ return 0;
+
+ if (ace1->type != ace2->type)
+ return ace2->type - ace1->type;
+
+ if (sid_compare(&ace1->trustee, &ace2->trustee))
+ return sid_compare(&ace1->trustee, &ace2->trustee);
+
+ if (ace1->flags != ace2->flags)
+ return ace1->flags - ace2->flags;
+
+ if (ace1->info.mask != ace2->info.mask)
+ return ace1->info.mask - ace2->info.mask;
+
+ if (ace1->size != ace2->size)
+ return ace1->size - ace2->size;
+
+ return memcmp(ace1, ace2, sizeof(SEC_ACE));
+}
+
+static void sort_acl(SEC_ACL *the_acl)
+{
+ uint32 i;
+ if (!the_acl) return;
+
+ qsort(the_acl->ace, the_acl->num_aces, sizeof(the_acl->ace[0]), QSORT_CAST ace_compare);
+
+ for (i=1;i<the_acl->num_aces;) {
+ if (sec_ace_equal(&the_acl->ace[i-1], &the_acl->ace[i])) {
+ int j;
+ for (j=i; j<the_acl->num_aces-1; j++) {
+ the_acl->ace[j] = the_acl->ace[j+1];
+ }
+ the_acl->num_aces--;
+ } else {
+ i++;
+ }
+ }
+}
+
+/*****************************************************
+set the ACLs on a file given an ascii description
+*******************************************************/
+static int cacl_set(struct cli_state *cli, char *filename,
+ char *the_acl, enum acl_mode mode)
+{
+ int fnum;
+ SEC_DESC *sd, *old;
+ uint32 i, j;
+ size_t sd_size;
+ int result = EXIT_OK;
+
+ sd = sec_desc_parse(the_acl);
+
+ if (!sd) return EXIT_PARSE_ERROR;
+ if (test_args) return EXIT_OK;
+
+ /* The desired access below is the only one I could find that works
+ with NT4, W2KP and Samba */
+
+ fnum = cli_nt_create(cli, filename, CREATE_ACCESS_READ);
+
+ if (fnum == -1) {
+ printf("cacl_set failed to open %s: %s\n", filename, cli_errstr(cli));
+ return EXIT_FAILED;
+ }
+
+ old = cli_query_secdesc(cli, fnum, ctx);
+
+ if (!old) {
+ printf("calc_set: Failed to query old descriptor\n");
+ return EXIT_FAILED;
+ }
+
+ cli_close(cli, fnum);
+
+ /* the logic here is rather more complex than I would like */
+ switch (mode) {
+ case SMB_ACL_DELETE:
+ for (i=0;sd->dacl && i<sd->dacl->num_aces;i++) {
+ BOOL found = False;
+
+ for (j=0;old->dacl && j<old->dacl->num_aces;j++) {
+ if (sec_ace_equal(&sd->dacl->ace[i],
+ &old->dacl->ace[j])) {
+ uint32 k;
+ for (k=j; k<old->dacl->num_aces-1;k++) {
+ old->dacl->ace[k] = old->dacl->ace[k+1];
+ }
+ old->dacl->num_aces--;
+ if (old->dacl->num_aces == 0) {
+ SAFE_FREE(old->dacl->ace);
+ SAFE_FREE(old->dacl);
+ old->off_dacl = 0;
+ }
+ found = True;
+ break;
+ }
+ }
+
+ if (!found) {
+ printf("ACL for ACE:");
+ print_ace(stdout, &sd->dacl->ace[i]);
+ printf(" not found\n");
+ }
+ }
+ break;
+
+ case SMB_ACL_MODIFY:
+ for (i=0;sd->dacl && i<sd->dacl->num_aces;i++) {
+ BOOL found = False;
+
+ for (j=0;old->dacl && j<old->dacl->num_aces;j++) {
+ if (sid_equal(&sd->dacl->ace[i].trustee,
+ &old->dacl->ace[j].trustee)) {
+ old->dacl->ace[j] = sd->dacl->ace[i];
+ found = True;
+ }
+ }
+
+ if (!found) {
+ fstring str;
+
+ SidToString(str, &sd->dacl->ace[i].trustee);
+ printf("ACL for SID %s not found\n", str);
+ }
+ }
+
+ break;
+
+ case SMB_ACL_ADD:
+ for (i=0;sd->dacl && i<sd->dacl->num_aces;i++) {
+ add_ace(&old->dacl, &sd->dacl->ace[i]);
+ }
+ break;
+
+ case SMB_ACL_SET:
+ old = sd;
+ break;
+ }
+
+ /* Denied ACE entries must come before allowed ones */
+ sort_acl(old->dacl);
+
+ /* Create new security descriptor and set it */
+ sd = make_sec_desc(ctx,old->revision, old->type, NULL, NULL,
+ NULL, old->dacl, &sd_size);
+
+ fnum = cli_nt_create(cli, filename, WRITE_DAC_ACCESS);
+
+ if (fnum == -1) {
+ printf("cacl_set failed to open %s: %s\n", filename, cli_errstr(cli));
+ return EXIT_FAILED;
+ }
+
+ if (!cli_set_secdesc(cli, fnum, sd)) {
+ printf("ERROR: secdesc set failed: %s\n", cli_errstr(cli));
+ result = EXIT_FAILED;
+ }
+
+ /* Clean up */
+
+ cli_close(cli, fnum);
+
+ return result;
+}
+
+
+/*****************************************************
+return a connection to a server
+*******************************************************/
+static struct cli_state *connect_one(const char *share)
+{
+ struct cli_state *c;
+ struct in_addr ip;
+ NTSTATUS nt_status;
+ zero_ip(&ip);
+
+ if (!cmdline_auth_info.got_pass) {
+ char *pass = getpass("Password: ");
+ if (pass) {
+ pstrcpy(cmdline_auth_info.password, pass);
+ cmdline_auth_info.got_pass = True;
+ }
+ }
+
+ if (NT_STATUS_IS_OK(nt_status = cli_full_connection(&c, global_myname(), server,
+ &ip, 0,
+ share, "?????",
+ cmdline_auth_info.username, lp_workgroup(),
+ cmdline_auth_info.password, 0,
+ cmdline_auth_info.signing_state, NULL))) {
+ return c;
+ } else {
+ DEBUG(0,("cli_full_connection failed! (%s)\n", nt_errstr(nt_status)));
+ return NULL;
+ }
+}
+
+/****************************************************************************
+ main program
+****************************************************************************/
+ int main(int argc, const char *argv[])
+{
+ char *share;
+ int opt;
+ enum acl_mode mode = SMB_ACL_SET;
+ static char *the_acl = NULL;
+ enum chown_mode change_mode = REQUEST_NONE;
+ int result;
+ fstring path;
+ pstring filename;
+ poptContext pc;
+ struct poptOption long_options[] = {
+ POPT_AUTOHELP
+ { "delete", 'D', POPT_ARG_STRING, NULL, 'D', "Delete an acl", "ACL" },
+ { "modify", 'M', POPT_ARG_STRING, NULL, 'M', "Modify an acl", "ACL" },
+ { "add", 'a', POPT_ARG_STRING, NULL, 'a', "Add an acl", "ACL" },
+ { "set", 'S', POPT_ARG_STRING, NULL, 'S', "Set acls", "ACLS" },
+ { "chown", 'C', POPT_ARG_STRING, NULL, 'C', "Change ownership of a file", "USERNAME" },
+ { "chgrp", 'G', POPT_ARG_STRING, NULL, 'G', "Change group ownership of a file", "GROUPNAME" },
+ { "numeric", 0, POPT_ARG_NONE, &numeric, True, "Don't resolve sids or masks to names" },
+ { "test-args", 't', POPT_ARG_NONE, &test_args, True, "Test arguments"},
+ POPT_COMMON_SAMBA
+ POPT_COMMON_CREDENTIALS
+ { NULL }
+ };
+
+ struct cli_state *cli;
+
+ ctx=talloc_init("main");
+
+ setlinebuf(stdout);
+
+ dbf = x_stderr;
+
+ setup_logging(argv[0],True);
+
+ lp_load(dyn_CONFIGFILE,True,False,False);
+ load_interfaces();
+
+ pc = poptGetContext("smbcacls", argc, argv, long_options, 0);
+
+ poptSetOtherOptionHelp(pc, "//server1/share1 filename");
+
+ while ((opt = poptGetNextOpt(pc)) != -1) {
+ switch (opt) {
+ case 'S':
+ the_acl = smb_xstrdup(poptGetOptArg(pc));
+ mode = SMB_ACL_SET;
+ break;
+
+ case 'D':
+ the_acl = smb_xstrdup(poptGetOptArg(pc));
+ mode = SMB_ACL_DELETE;
+ break;
+
+ case 'M':
+ the_acl = smb_xstrdup(poptGetOptArg(pc));
+ mode = SMB_ACL_MODIFY;
+ break;
+
+ case 'a':
+ the_acl = smb_xstrdup(poptGetOptArg(pc));
+ mode = SMB_ACL_ADD;
+ break;
+
+ case 'C':
+ pstrcpy(owner_username,poptGetOptArg(pc));
+ change_mode = REQUEST_CHOWN;
+ break;
+
+ case 'G':
+ pstrcpy(owner_username,poptGetOptArg(pc));
+ change_mode = REQUEST_CHGRP;
+ break;
+ }
+ }
+
+ /* Make connection to server */
+ if(!poptPeekArg(pc)) {
+ poptPrintUsage(pc, stderr, 0);
+ return -1;
+ }
+
+ fstrcpy(path, poptGetArg(pc));
+
+ if(!poptPeekArg(pc)) {
+ poptPrintUsage(pc, stderr, 0);
+ return -1;
+ }
+
+ pstrcpy(filename, poptGetArg(pc));
+
+ all_string_sub(path,"/","\\",0);
+
+ fstrcpy(server,path+2);
+ share = strchr_m(server,'\\');
+ if (!share) {
+ share = strchr_m(server,'/');
+ if (!share) {
+ printf("Invalid argument: %s\n", share);
+ return -1;
+ }
+ }
+
+ *share = 0;
+ share++;
+
+ if (!test_args) {
+ cli = connect_one(share);
+ if (!cli) {
+ talloc_destroy(ctx);
+ exit(EXIT_FAILED);
+ }
+ } else {
+ exit(0);
+ }
+
+ all_string_sub(filename, "/", "\\", 0);
+ if (filename[0] != '\\') {
+ pstring s;
+ s[0] = '\\';
+ safe_strcpy(&s[1], filename, sizeof(pstring)-2);
+ pstrcpy(filename, s);
+ }
+
+ /* Perform requested action */
+
+ if (change_mode != REQUEST_NONE) {
+ result = owner_set(cli, change_mode, filename, owner_username);
+ } else if (the_acl) {
+ result = cacl_set(cli, filename, the_acl, mode);
+ } else {
+ result = cacl_dump(cli, filename);
+ }
+
+ talloc_destroy(ctx);
+
+ return result;
+}
diff --git a/source/utils/smbcontrol.c b/source/utils/smbcontrol.c
new file mode 100644
index 00000000000..8a27684a4d3
--- /dev/null
+++ b/source/utils/smbcontrol.c
@@ -0,0 +1,747 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ Send messages to other Samba daemons
+
+ Copyright (C) Tim Potter 2003
+ Copyright (C) Andrew Tridgell 1994-1998
+ Copyright (C) Martin Pool 2001-2002
+ Copyright (C) Simo Sorce 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 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"
+
+/* Default timeout value when waiting for replies (in seconds) */
+
+#define DEFAULT_TIMEOUT 10
+
+static int timeout = DEFAULT_TIMEOUT;
+static int num_replies; /* Used by message callback fns */
+
+/* Send a message to a destination pid. Zero means broadcast smbd. */
+
+static BOOL send_message(pid_t pid, int msg_type, const void *buf, int len,
+ BOOL duplicates)
+{
+ TDB_CONTEXT *tdb;
+ BOOL ret;
+ int n_sent = 0;
+
+ if (!message_init())
+ return False;
+
+ if (pid != 0)
+ return message_send_pid(pid, msg_type, buf, len, duplicates);
+
+ tdb = tdb_open_log(lock_path("connections.tdb"), 0,
+ TDB_DEFAULT, O_RDWR, 0);
+ if (!tdb) {
+ fprintf(stderr,"Failed to open connections database"
+ ": %s\n", strerror(errno));
+ return False;
+ }
+
+ ret = message_send_all(tdb,msg_type, buf, len, duplicates,
+ &n_sent);
+ DEBUG(10,("smbcontrol/send_message: broadcast message to "
+ "%d processes\n", n_sent));
+
+ tdb_close(tdb);
+
+ return ret;
+}
+
+/* Wait for one or more reply messages */
+
+static void wait_replies(BOOL multiple_replies)
+{
+ time_t start_time = time(NULL);
+
+ /* Wait around a bit. This is pretty disgusting - we have to
+ busy-wait here as there is no nicer way to do it. */
+
+ do {
+ message_dispatch();
+ if (num_replies > 0 && !multiple_replies)
+ break;
+ sleep(1);
+ } while (timeout - (time(NULL) - start_time) > 0);
+}
+
+/* Message handler callback that displays a string on stdout */
+
+static void print_string_cb(int msg_type, pid_t pid, void *buf, size_t len)
+{
+ printf("%.*s", (int)len, (const char *)buf);
+ num_replies++;
+}
+
+/* Send no message. Useful for testing. */
+
+static BOOL do_noop(const pid_t pid, const int argc, const char **argv)
+{
+ if (argc != 1) {
+ fprintf(stderr, "Usage: smbcontrol <dest> noop\n");
+ return False;
+ }
+
+ /* Move along, nothing to see here */
+
+ return True;
+}
+
+/* Send a debug string */
+
+static BOOL do_debug(const pid_t pid, const int argc, const char **argv)
+{
+ if (argc != 2) {
+ fprintf(stderr, "Usage: smbcontrol <dest> debug "
+ "<debug-string>\n");
+ return False;
+ }
+
+ return send_message(
+ pid, MSG_DEBUG, argv[1], strlen(argv[1]) + 1, False);
+}
+
+/* Force a browser election */
+
+static BOOL do_election(const pid_t pid, const int argc, const char **argv)
+{
+ if (argc != 1) {
+ fprintf(stderr, "Usage: smbcontrol <dest> force-election\n");
+ return False;
+ }
+
+ return send_message(
+ pid, MSG_FORCE_ELECTION, NULL, 0, False);
+}
+
+/* Ping a samba daemon process */
+
+static void pong_cb(int msg_type, pid_t pid, void *buf, size_t len)
+{
+ printf("PONG from pid %u\n", (unsigned int)pid);
+ num_replies++;
+}
+
+static BOOL do_ping(const pid_t pid, const int argc, const char **argv)
+{
+ if (argc != 1) {
+ fprintf(stderr, "Usage: smbcontrol <dest> ping\n");
+ return False;
+ }
+
+ /* Send a message and register our interest in a reply */
+
+ if (!send_message(pid, MSG_PING, NULL, 0, False))
+ return False;
+
+ message_register(MSG_PONG, pong_cb);
+
+ wait_replies(pid == 0);
+
+ /* No replies were received within the timeout period */
+
+ if (num_replies == 0)
+ printf("No replies received\n");
+
+ message_deregister(MSG_PONG);
+
+ return num_replies;
+}
+
+/* Set profiling options */
+
+static BOOL do_profile(const pid_t pid, const int argc, const char **argv)
+{
+ int v;
+
+ if (argc != 2) {
+ fprintf(stderr, "Usage: smbcontrol <dest> profile "
+ "<off|count|on|flush>\n");
+ return False;
+ }
+
+ if (strcmp(argv[1], "off") == 0) {
+ v = 0;
+ } else if (strcmp(argv[1], "count") == 0) {
+ v = 1;
+ } else if (strcmp(argv[1], "on") == 0) {
+ v = 2;
+ } else if (strcmp(argv[1], "flush") == 0) {
+ v = 3;
+ } else {
+ fprintf(stderr, "Unknown profile command '%s'\n", argv[1]);
+ return False;
+ }
+
+ return send_message(pid, MSG_PROFILE, &v, sizeof(int), False);
+}
+
+/* Return the profiling level */
+
+static void profilelevel_cb(int msg_type, pid_t pid, void *buf, size_t len)
+{
+ int level;
+ const char *s;
+
+ num_replies++;
+
+ if (len != sizeof(int)) {
+ fprintf(stderr, "invalid message length %ld returned\n",
+ (unsigned long)len);
+ return;
+ }
+
+ memcpy(&level, buf, sizeof(int));
+
+ switch (level) {
+ case 0:
+ s = "not enabled";
+ break;
+ case 1:
+ s = "off";
+ break;
+ case 3:
+ s = "count only";
+ break;
+ case 7:
+ s = "count and time";
+ break;
+ default:
+ s = "BOGUS";
+ break;
+ }
+
+ printf("Profiling %s on pid %u\n",s,(unsigned int)pid);
+}
+
+static void profilelevel_rqst(int msg_type, pid_t pid, void *buf, size_t len)
+{
+ int v = 0;
+
+ /* Send back a dummy reply */
+
+ send_message(pid, MSG_PROFILELEVEL, &v, sizeof(int), False);
+}
+
+static BOOL do_profilelevel(const pid_t pid, const int argc, const char **argv)
+{
+ if (argc != 1) {
+ fprintf(stderr, "Usage: smbcontrol <dest> profilelevel\n");
+ return False;
+ }
+
+ /* Send a message and register our interest in a reply */
+
+ if (!send_message(pid, MSG_REQ_PROFILELEVEL, NULL, 0, False))
+ return False;
+
+ message_register(MSG_PROFILELEVEL, profilelevel_cb);
+ message_register(MSG_REQ_PROFILELEVEL, profilelevel_rqst);
+
+ wait_replies(pid == 0);
+
+ /* No replies were received within the timeout period */
+
+ if (num_replies == 0)
+ printf("No replies received\n");
+
+ message_deregister(MSG_PROFILE);
+
+ return num_replies;
+}
+
+/* Display debug level settings */
+
+static BOOL do_debuglevel(const pid_t pid, const int argc, const char **argv)
+{
+ if (argc != 1) {
+ fprintf(stderr, "Usage: smbcontrol <dest> debuglevel\n");
+ return False;
+ }
+
+ /* Send a message and register our interest in a reply */
+
+ if (!send_message(pid, MSG_REQ_DEBUGLEVEL, NULL, 0, False))
+ return False;
+
+ message_register(MSG_DEBUGLEVEL, print_string_cb);
+
+ wait_replies(pid == 0);
+
+ /* No replies were received within the timeout period */
+
+ if (num_replies == 0)
+ printf("No replies received\n");
+
+ message_deregister(MSG_DEBUGLEVEL);
+
+ return num_replies;
+}
+
+/* Send a print notify message */
+
+static BOOL do_printnotify(const pid_t pid, const int argc, const char **argv)
+{
+ const char *cmd;
+
+ /* Check for subcommand */
+
+ if (argc == 1) {
+ fprintf(stderr, "Must specify subcommand:\n");
+ fprintf(stderr, "\tqueuepause <printername>\n");
+ fprintf(stderr, "\tqueueresume <printername>\n");
+ fprintf(stderr, "\tjobpause <printername> <unix jobid>\n");
+ fprintf(stderr, "\tjobresume <printername> <unix jobid>\n");
+ fprintf(stderr, "\tjobdelete <printername> <unix jobid>\n");
+ fprintf(stderr, "\tprinter <printername> <comment|port|"
+ "driver> <value>\n");
+
+ return False;
+ }
+
+ cmd = argv[1];
+
+ if (strcmp(cmd, "queuepause") == 0) {
+
+ if (argc != 3) {
+ fprintf(stderr, "Usage: smbcontrol <dest> printnotify"
+ " queuepause <printername>\n");
+ return False;
+ }
+
+ notify_printer_status_byname(argv[2], PRINTER_STATUS_PAUSED);
+
+ goto send;
+
+ } else if (strcmp(cmd, "queueresume") == 0) {
+
+ if (argc != 3) {
+ fprintf(stderr, "Usage: smbcontrol <dest> printnotify"
+ " queuereume <printername>\n");
+ return False;
+ }
+
+ notify_printer_status_byname(argv[2], PRINTER_STATUS_OK);
+
+ goto send;
+
+ } else if (strcmp(cmd, "jobpause") == 0) {
+ int jobid;
+
+ if (argc != 4) {
+ fprintf(stderr, "Usage: smbcontrol <dest> printnotify"
+ " jobpause <printername> <unix-jobid>\n");
+ return False;
+ }
+
+ jobid = atoi(argv[3]);
+
+ notify_job_status_byname(
+ argv[2], jobid, JOB_STATUS_PAUSED,
+ SPOOLSS_NOTIFY_MSG_UNIX_JOBID);
+
+ goto send;
+
+ } else if (strcmp(cmd, "jobresume") == 0) {
+ int jobid;
+
+ if (argc != 4) {
+ fprintf(stderr, "Usage: smbcontrol <dest> printnotify"
+ " jobpause <printername> <unix-jobid>\n");
+ return False;
+ }
+
+ jobid = atoi(argv[3]);
+
+ notify_job_status_byname(
+ argv[2], jobid, JOB_STATUS_QUEUED,
+ SPOOLSS_NOTIFY_MSG_UNIX_JOBID);
+
+ goto send;
+
+ } else if (strcmp(cmd, "jobdelete") == 0) {
+ int jobid;
+
+ if (argc != 4) {
+ fprintf(stderr, "Usage: smbcontrol <dest> printnotify"
+ " jobpause <printername> <unix-jobid>\n");
+ return False;
+ }
+
+ jobid = atoi(argv[3]);
+
+ notify_job_status_byname(
+ argv[2], jobid, JOB_STATUS_DELETING,
+ SPOOLSS_NOTIFY_MSG_UNIX_JOBID);
+
+ notify_job_status_byname(
+ argv[2], jobid, JOB_STATUS_DELETING|
+ JOB_STATUS_DELETED,
+ SPOOLSS_NOTIFY_MSG_UNIX_JOBID);
+
+ goto send;
+
+ } else if (strcmp(cmd, "printer") == 0) {
+ uint32 attribute;
+
+ if (argc != 5) {
+ fprintf(stderr, "Usage: smbcontrol <dest> printnotify "
+ "printer <printername> <comment|port|driver> "
+ "<value>\n");
+ return False;
+ }
+
+ if (strcmp(argv[3], "comment") == 0) {
+ attribute = PRINTER_NOTIFY_COMMENT;
+ } else if (strcmp(argv[3], "port") == 0) {
+ attribute = PRINTER_NOTIFY_PORT_NAME;
+ } else if (strcmp(argv[3], "driver") == 0) {
+ attribute = PRINTER_NOTIFY_DRIVER_NAME;
+ } else {
+ fprintf(stderr, "Invalid printer command '%s'\n",
+ argv[3]);
+ return False;
+ }
+
+ notify_printer_byname(argv[2], attribute, argv[4]);
+
+ goto send;
+ }
+
+ fprintf(stderr, "Invalid subcommand '%s'\n", cmd);
+ return False;
+
+send:
+ print_notify_send_messages(0);
+ return True;
+}
+
+/* Close a share */
+
+static BOOL do_closeshare(const pid_t pid, const int argc, const char **argv)
+{
+ if (argc != 2) {
+ fprintf(stderr, "Usage: smbcontrol <dest> close-share "
+ "<sharename>\n");
+ return False;
+ }
+
+ return send_message(
+ pid, MSG_SMB_FORCE_TDIS, argv[1], strlen(argv[1]) + 1, False);
+}
+
+/* Force a SAM synchronisation */
+
+static BOOL do_samsync(const pid_t pid, const int argc, const char **argv)
+{
+ if (argc != 1) {
+ fprintf(stderr, "Usage: smbcontrol <dest> samsync\n");
+ return False;
+ }
+
+ return send_message(
+ pid, MSG_SMB_SAM_SYNC, NULL, 0, False);
+}
+
+/* Force a SAM replication */
+
+static BOOL do_samrepl(const pid_t pid, const int argc, const char **argv)
+{
+ if (argc != 1) {
+ fprintf(stderr, "Usage: smbcontrol <dest> samrepl\n");
+ return False;
+ }
+
+ return send_message(
+ pid, MSG_SMB_SAM_REPL, NULL, 0, False);
+}
+
+/* Display talloc pool usage */
+
+static BOOL do_poolusage(const pid_t pid, const int argc, const char **argv)
+{
+ if (argc != 1) {
+ fprintf(stderr, "Usage: smbcontrol <dest> pool-usage\n");
+ return False;
+ }
+
+ /* Send a message and register our interest in a reply */
+
+ if (!send_message(pid, MSG_REQ_POOL_USAGE, NULL, 0, False))
+ return False;
+
+ message_register(MSG_POOL_USAGE, print_string_cb);
+
+ wait_replies(pid == 0);
+
+ /* No replies were received within the timeout period */
+
+ if (num_replies == 0)
+ printf("No replies received\n");
+
+ message_deregister(MSG_POOL_USAGE);
+
+ return num_replies;
+}
+
+/* Perform a dmalloc mark */
+
+static BOOL do_dmalloc_mark(const pid_t pid, const int argc, const char **argv)
+{
+ if (argc != 1) {
+ fprintf(stderr, "Usage: smbcontrol <dest> dmalloc-mark\n");
+ return False;
+ }
+
+ return send_message(
+ pid, MSG_REQ_DMALLOC_MARK, NULL, 0, False);
+}
+
+/* Perform a dmalloc changed */
+
+static BOOL do_dmalloc_changed(const pid_t pid, const int argc, const char **argv)
+{
+ if (argc != 1) {
+ fprintf(stderr, "Usage: smbcontrol <dest> "
+ "dmalloc-log-changed\n");
+ return False;
+ }
+
+ return send_message(
+ pid, MSG_REQ_DMALLOC_LOG_CHANGED, NULL, 0, False);
+}
+
+/* Shutdown a server process */
+
+static BOOL do_shutdown(const pid_t pid, const int argc, const char **argv)
+{
+ if (argc != 1) {
+ fprintf(stderr, "Usage: smbcontrol <dest> shutdown\n");
+ return False;
+ }
+
+ return send_message(pid, MSG_SHUTDOWN, NULL, 0, False);
+}
+
+/* Notify a driver upgrade */
+
+static BOOL do_drvupgrade(const pid_t pid, const int argc, const char **argv)
+{
+ if (argc != 2) {
+ fprintf(stderr, "Usage: smbcontrol <dest> drvupgrade "
+ "<driver-name>\n");
+ return False;
+ }
+
+ return send_message(
+ pid, MSG_DEBUG, argv[1], strlen(argv[1]) + 1, False);
+}
+
+static BOOL do_reload_config(const pid_t pid, const int argc, const char **argv)
+{
+ if (argc != 1) {
+ fprintf(stderr, "Usage: smbcontrol <dest> reload-config\n");
+ return False;
+ }
+
+ return send_message(pid, MSG_SMB_CONF_UPDATED, NULL, 0, False);
+}
+
+/* A list of message type supported */
+
+static const struct {
+ const char *name; /* Option name */
+ BOOL (*fn)(const pid_t pid, const int argc, const char **argv);
+ const char *help; /* Short help text */
+} msg_types[] = {
+ { "debug", do_debug, "Set debuglevel" },
+ { "force-election", do_election,
+ "Force a browse election" },
+ { "ping", do_ping, "Elicit a response" },
+ { "profile", do_profile, "" },
+ { "profilelevel", do_profilelevel, "" },
+ { "debuglevel", do_debuglevel, "Display current debuglevels" },
+ { "printnotify", do_printnotify, "Send a print notify message" },
+ { "close-share", do_closeshare, "Forcibly disconnect a share" },
+ { "samsync", do_samsync, "Initiate SAM synchronisation" },
+ { "samrepl", do_samrepl, "Initiate SAM replication" },
+ { "pool-usage", do_poolusage, "Display talloc memory usage" },
+ { "dmalloc-mark", do_dmalloc_mark, "" },
+ { "dmalloc-log-changed", do_dmalloc_changed, "" },
+ { "shutdown", do_shutdown, "Shut down daemon" },
+ { "drvupgrade", do_drvupgrade, "Notify a printer driver has changed" },
+ { "reload-config", do_reload_config, "Force smbd or winbindd to reload config file"},
+ { "noop", do_noop, "Do nothing" },
+ { NULL }
+};
+
+/* Display usage information */
+
+static void usage(poptContext *pc)
+{
+ int i;
+
+ poptPrintHelp(*pc, stderr, 0);
+
+ fprintf(stderr, "\n");
+ fprintf(stderr, "<destination> is one of \"nmbd\", \"smbd\" or a "
+ "process ID\n");
+
+ fprintf(stderr, "\n");
+ fprintf(stderr, "<message-type> is one of:\n");
+
+ for (i = 0; msg_types[i].name; i++)
+ fprintf(stderr, "\t%-30s%s\n", msg_types[i].name,
+ msg_types[i].help);
+
+ fprintf(stderr, "\n");
+
+ exit(1);
+}
+
+/* Return the pid number for a string destination */
+
+static pid_t parse_dest(const char *dest)
+{
+ pid_t pid;
+
+ /* Zero is a special return value for broadcast smbd */
+
+ if (strequal(dest, "smbd"))
+ return 0;
+
+ /* Try self - useful for testing */
+
+ if (strequal(dest, "self"))
+ return sys_getpid();
+
+ /* Check for numeric pid number */
+
+ if ((pid = atoi(dest)) != 0)
+ return pid;
+
+ /* Look up other destinations in pidfile directory */
+
+ if ((pid = pidfile_pid(dest)) != 0)
+ return pid;
+
+ fprintf(stderr,"Can't find pid for destination '%s'\n", dest);
+
+ return -1;
+}
+
+/* Execute smbcontrol command */
+
+static BOOL do_command(int argc, const char **argv)
+{
+ const char *dest = argv[0], *command = argv[1];
+ pid_t pid;
+ int i;
+
+ /* Check destination */
+
+ if ((pid = parse_dest(dest)) == -1)
+ return False;
+
+ /* Check command */
+
+ for (i = 0; msg_types[i].name; i++) {
+ if (strequal(command, msg_types[i].name))
+ return msg_types[i].fn(pid, argc - 1, argv + 1);
+ }
+
+ fprintf(stderr, "smbcontrol: unknown command '%s'\n", command);
+
+ return False;
+}
+
+/* Main program */
+
+int main(int argc, const char **argv)
+{
+ poptContext pc;
+ int opt;
+
+ static struct poptOption wbinfo_options[] = {
+ { "timeout", 't', POPT_ARG_INT, &timeout, 't',
+ "Set timeout value in seconds", "TIMEOUT" },
+
+ { "configfile", 's', POPT_ARG_STRING, NULL, 's',
+ "Use alternative configuration file", "CONFIGFILE" },
+
+ POPT_TABLEEND
+ };
+
+ struct poptOption options[] = {
+ { NULL, 0, POPT_ARG_INCLUDE_TABLE, wbinfo_options, 0,
+ "Options" },
+
+ POPT_AUTOHELP
+ POPT_COMMON_VERSION
+ POPT_TABLEEND
+ };
+
+ setup_logging(argv[0],True);
+
+ /* Parse command line arguments using popt */
+
+ pc = poptGetContext(
+ "smbcontrol", argc, (const char **)argv, options, 0);
+
+ poptSetOtherOptionHelp(pc, "[OPTION...] <destination> <message-type> "
+ "<parameters>");
+
+ if (argc == 1)
+ usage(&pc);
+
+ while ((opt = poptGetNextOpt(pc)) != -1) {
+ switch(opt) {
+ case 't': /* --timeout */
+ argc -= 2;
+ break;
+ case 's': /* --configfile */
+ pstrcpy(dyn_CONFIGFILE, poptGetOptArg(pc));
+ argc -= 2;
+ break;
+ default:
+ fprintf(stderr, "Invalid option\n");
+ poptPrintHelp(pc, stderr, 0);
+ break;
+ }
+ }
+
+ /* We should now have the remaining command line arguments in
+ argv. The argc parameter should have been decremented to the
+ correct value in the above switch statement. */
+
+ argv = (const char **)poptGetArgs(pc);
+ argc--; /* Don't forget about argv[0] */
+
+ if (argc == 1)
+ usage(&pc);
+
+ lp_load(dyn_CONFIGFILE,False,False,False);
+
+ /* Need to invert sense of return code -- samba
+ * routines mostly return True==1 for success, but
+ * shell needs 0. */
+
+ return !do_command(argc, argv);
+}
diff --git a/source/utils/smbcquotas.c b/source/utils/smbcquotas.c
new file mode 100644
index 00000000000..0bd87554209
--- /dev/null
+++ b/source/utils/smbcquotas.c
@@ -0,0 +1,546 @@
+/*
+ Unix SMB/CIFS implementation.
+ QUOTA get/set utility
+
+ Copyright (C) Andrew Tridgell 2000
+ Copyright (C) Tim Potter 2000
+ Copyright (C) Jeremy Allison 2000
+ 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 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"
+
+static pstring server;
+
+/* numeric is set when the user wants numeric SIDs and ACEs rather
+ than going via LSA calls to resolve them */
+static BOOL numeric;
+static BOOL verbose;
+
+enum todo_values {NOOP_QUOTA=0,FS_QUOTA,USER_QUOTA,LIST_QUOTA,SET_QUOTA};
+enum exit_values {EXIT_OK, EXIT_FAILED, EXIT_PARSE_ERROR};
+
+static struct cli_state *cli_ipc = NULL;
+static POLICY_HND pol;
+static BOOL got_policy_hnd;
+
+static struct cli_state *connect_one(const char *share);
+
+/* Open cli connection and policy handle */
+
+static BOOL cli_open_policy_hnd(void)
+{
+ /* Initialise cli LSA connection */
+
+ if (!cli_ipc) {
+ cli_ipc = connect_one("IPC$");
+ if (!cli_nt_session_open (cli_ipc, PI_LSARPC)) {
+ return False;
+ }
+ }
+
+ /* Open policy handle */
+
+ if (!got_policy_hnd) {
+
+ /* Some systems don't support SEC_RIGHTS_MAXIMUM_ALLOWED,
+ but NT sends 0x2000000 so we might as well do it too. */
+
+ if (!NT_STATUS_IS_OK(cli_lsa_open_policy(cli_ipc, cli_ipc->mem_ctx, True,
+ GENERIC_EXECUTE_ACCESS, &pol))) {
+ return False;
+ }
+
+ got_policy_hnd = True;
+ }
+
+ return True;
+}
+
+/* convert a SID to a string, either numeric or username/group */
+static void SidToString(fstring str, DOM_SID *sid, BOOL _numeric)
+{
+ char **domains = NULL;
+ char **names = NULL;
+ uint32 *types = NULL;
+
+ sid_to_string(str, sid);
+
+ if (_numeric) return;
+
+ /* Ask LSA to convert the sid to a name */
+
+ if (!cli_open_policy_hnd() ||
+ !NT_STATUS_IS_OK(cli_lsa_lookup_sids(cli_ipc, cli_ipc->mem_ctx,
+ &pol, 1, sid, &domains,
+ &names, &types)) ||
+ !domains || !domains[0] || !names || !names[0]) {
+ return;
+ }
+
+ /* Converted OK */
+
+ slprintf(str, sizeof(fstring) - 1, "%s%s%s",
+ domains[0], lp_winbind_separator(),
+ names[0]);
+
+}
+
+/* convert a string to a SID, either numeric or username/group */
+static BOOL StringToSid(DOM_SID *sid, const char *str)
+{
+ uint32 *types = NULL;
+ DOM_SID *sids = NULL;
+ BOOL result = True;
+
+ if (strncmp(str, "S-", 2) == 0) {
+ return string_to_sid(sid, str);
+ }
+
+ if (!cli_open_policy_hnd() ||
+ !NT_STATUS_IS_OK(cli_lsa_lookup_names(cli_ipc, cli_ipc->mem_ctx,
+ &pol, 1, &str, &sids,
+ &types))) {
+ result = False;
+ goto done;
+ }
+
+ sid_copy(sid, &sids[0]);
+ done:
+
+ return result;
+}
+
+#define QUOTA_GET 1
+#define QUOTA_SETLIM 2
+#define QUOTA_SETFLAGS 3
+#define QUOTA_LIST 4
+
+enum {PARSE_FLAGS,PARSE_LIM};
+
+static int parse_quota_set(pstring set_str, pstring username_str, enum SMB_QUOTA_TYPE *qtype, int *cmd, SMB_NTQUOTA_STRUCT *pqt)
+{
+ char *p = set_str,*p2;
+ int todo;
+ BOOL stop = False;
+ BOOL enable = False;
+ BOOL deny = False;
+
+ if (strnequal(set_str,"UQLIM:",6)) {
+ p += 6;
+ *qtype = SMB_USER_QUOTA_TYPE;
+ *cmd = QUOTA_SETLIM;
+ todo = PARSE_LIM;
+ if ((p2=strstr(p,":"))==NULL) {
+ return -1;
+ }
+
+ *p2 = '\0';
+ p2++;
+
+ fstrcpy(username_str,p);
+ p = p2;
+ } else if (strnequal(set_str,"FSQLIM:",7)) {
+ p +=7;
+ *qtype = SMB_USER_FS_QUOTA_TYPE;
+ *cmd = QUOTA_SETLIM;
+ todo = PARSE_LIM;
+ } else if (strnequal(set_str,"FSQFLAGS:",9)) {
+ p +=9;
+ todo = PARSE_FLAGS;
+ *qtype = SMB_USER_FS_QUOTA_TYPE;
+ *cmd = QUOTA_SETFLAGS;
+ } else {
+ return -1;
+ }
+
+ switch (todo) {
+ case PARSE_LIM:
+#if defined(HAVE_LONGLONG)
+ if (sscanf(p,"%llu/%llu",&pqt->softlim,&pqt->hardlim)!=2) {
+#else
+ if (sscanf(p,"%lu/%lu",&pqt->softlim,&pqt->hardlim)!=2) {
+#endif
+ return -1;
+ }
+
+ break;
+ case PARSE_FLAGS:
+ while (!stop) {
+
+ if ((p2=strstr(p,"/"))==NULL) {
+ stop = True;
+ } else {
+ *p2 = '\0';
+ p2++;
+ }
+
+ if (strnequal(p,"QUOTA_ENABLED",13)) {
+ enable = True;
+ } else if (strnequal(p,"DENY_DISK",9)) {
+ deny = True;
+ } else if (strnequal(p,"LOG_SOFTLIMIT",13)) {
+ pqt->qflags |= QUOTAS_LOG_THRESHOLD;
+ } else if (strnequal(p,"LOG_HARDLIMIT",13)) {
+ pqt->qflags |= QUOTAS_LOG_LIMIT;
+ } else {
+ return -1;
+ }
+
+ p=p2;
+ }
+
+ if (deny) {
+ pqt->qflags |= QUOTAS_DENY_DISK;
+ } else if (enable) {
+ pqt->qflags |= QUOTAS_ENABLED;
+ }
+
+ break;
+ }
+
+ return 0;
+}
+
+static int do_quota(struct cli_state *cli, enum SMB_QUOTA_TYPE qtype, uint16 cmd, pstring username_str, SMB_NTQUOTA_STRUCT *pqt)
+{
+ uint32 fs_attrs = 0;
+ int quota_fnum = 0;
+ SMB_NTQUOTA_LIST *qtl = NULL;
+ SMB_NTQUOTA_STRUCT qt;
+ ZERO_STRUCT(qt);
+
+ if (!cli_get_fs_attr_info(cli, &fs_attrs)) {
+ d_printf("Failed to get the filesystem attributes %s.\n",
+ cli_errstr(cli));
+ return -1;
+ }
+
+ if (!(fs_attrs & FILE_VOLUME_QUOTAS)) {
+ d_printf("Quotas are not supported by the server.\n");
+ return 0;
+ }
+
+ if (!cli_get_quota_handle(cli, &quota_fnum)) {
+ d_printf("Failed to open \\%s %s.\n",
+ FAKE_FILE_NAME_QUOTA,cli_errstr(cli));
+ return -1;
+ }
+
+ switch(qtype) {
+ case SMB_USER_QUOTA_TYPE:
+ if (!StringToSid(&qt.sid, username_str)) {
+ d_printf("StringToSid() failed for [%s]\n",username_str);
+ return -1;
+ }
+
+ switch(cmd) {
+ case QUOTA_GET:
+ if (!cli_get_user_quota(cli, quota_fnum, &qt)) {
+ d_printf("%s cli_get_user_quota %s\n",
+ cli_errstr(cli),username_str);
+ return -1;
+ }
+ dump_ntquota(&qt,verbose,numeric,SidToString);
+ break;
+ case QUOTA_SETLIM:
+ pqt->sid = qt.sid;
+ if (!cli_set_user_quota(cli, quota_fnum, pqt)) {
+ d_printf("%s cli_set_user_quota %s\n",
+ cli_errstr(cli),username_str);
+ return -1;
+ }
+ if (!cli_get_user_quota(cli, quota_fnum, &qt)) {
+ d_printf("%s cli_get_user_quota %s\n",
+ cli_errstr(cli),username_str);
+ return -1;
+ }
+ dump_ntquota(&qt,verbose,numeric,SidToString);
+ break;
+ case QUOTA_LIST:
+ if (!cli_list_user_quota(cli, quota_fnum, &qtl)) {
+ d_printf("%s cli_set_user_quota %s\n",
+ cli_errstr(cli),username_str);
+ return -1;
+ }
+ dump_ntquota_list(&qtl,verbose,numeric,SidToString);
+ free_ntquota_list(&qtl);
+ break;
+ default:
+ d_printf("Unknown Error\n");
+ return -1;
+ }
+ break;
+ case SMB_USER_FS_QUOTA_TYPE:
+ switch(cmd) {
+ case QUOTA_GET:
+ if (!cli_get_fs_quota_info(cli, quota_fnum, &qt)) {
+ d_printf("%s cli_get_fs_quota_info\n",
+ cli_errstr(cli));
+ return -1;
+ }
+ dump_ntquota(&qt,True,numeric,NULL);
+ break;
+ case QUOTA_SETLIM:
+ if (!cli_get_fs_quota_info(cli, quota_fnum, &qt)) {
+ d_printf("%s cli_get_fs_quota_info\n",
+ cli_errstr(cli));
+ return -1;
+ }
+ qt.softlim = pqt->softlim;
+ qt.hardlim = pqt->hardlim;
+ if (!cli_set_fs_quota_info(cli, quota_fnum, &qt)) {
+ d_printf("%s cli_set_fs_quota_info\n",
+ cli_errstr(cli));
+ return -1;
+ }
+ if (!cli_get_fs_quota_info(cli, quota_fnum, &qt)) {
+ d_printf("%s cli_get_fs_quota_info\n",
+ cli_errstr(cli));
+ return -1;
+ }
+ dump_ntquota(&qt,True,numeric,NULL);
+ break;
+ case QUOTA_SETFLAGS:
+ if (!cli_get_fs_quota_info(cli, quota_fnum, &qt)) {
+ d_printf("%s cli_get_fs_quota_info\n",
+ cli_errstr(cli));
+ return -1;
+ }
+ qt.qflags = pqt->qflags;
+ if (!cli_set_fs_quota_info(cli, quota_fnum, &qt)) {
+ d_printf("%s cli_set_fs_quota_info\n",
+ cli_errstr(cli));
+ return -1;
+ }
+ if (!cli_get_fs_quota_info(cli, quota_fnum, &qt)) {
+ d_printf("%s cli_get_fs_quota_info\n",
+ cli_errstr(cli));
+ return -1;
+ }
+ dump_ntquota(&qt,True,numeric,NULL);
+ break;
+ default:
+ d_printf("Unknown Error\n");
+ return -1;
+ }
+ break;
+ default:
+ d_printf("Unknown Error\n");
+ return -1;
+ }
+
+ cli_close(cli, quota_fnum);
+
+ return 0;
+}
+
+/*****************************************************
+return a connection to a server
+*******************************************************/
+static struct cli_state *connect_one(const char *share)
+{
+ struct cli_state *c;
+ struct in_addr ip;
+ NTSTATUS nt_status;
+ zero_ip(&ip);
+
+ if (!cmdline_auth_info.got_pass) {
+ char *pass = getpass("Password: ");
+ if (pass) {
+ pstrcpy(cmdline_auth_info.password, pass);
+ cmdline_auth_info.got_pass = True;
+ }
+ }
+
+ if (NT_STATUS_IS_OK(nt_status = cli_full_connection(&c, global_myname(), server,
+ &ip, 0,
+ share, "?????",
+ cmdline_auth_info.username, lp_workgroup(),
+ cmdline_auth_info.password, 0,
+ cmdline_auth_info.signing_state, NULL))) {
+ return c;
+ } else {
+ DEBUG(0,("cli_full_connection failed! (%s)\n", nt_errstr(nt_status)));
+ return NULL;
+ }
+}
+
+/****************************************************************************
+ main program
+****************************************************************************/
+ int main(int argc, const char *argv[])
+{
+ char *share;
+ int opt;
+ int result;
+ int todo = 0;
+ pstring username_str = {0};
+ pstring path = {0};
+ pstring set_str = {0};
+ enum SMB_QUOTA_TYPE qtype;
+ int cmd = 0;
+ static BOOL test_args = False;
+ struct cli_state *cli;
+ BOOL fix_user = False;
+ SMB_NTQUOTA_STRUCT qt;
+ poptContext pc;
+ struct poptOption long_options[] = {
+ POPT_AUTOHELP
+ { "user", 'u', POPT_ARG_STRING, NULL, 'u', "Show quotas for user", "user" },
+ { "list", 'L', POPT_ARG_NONE, NULL, 'L', "List user quotas" },
+ { "fs", 'F', POPT_ARG_NONE, NULL, 'F', "Show filesystem quotas" },
+ { "set", 'S', POPT_ARG_STRING, NULL, 'S', "Set acls\n\
+SETSTRING:\n\
+UQLIM:<username>/<softlimit>/<hardlimit> for user quotas\n\
+FSQLIM:<softlimit>/<hardlimit> for filesystem defaults\n\
+FSQFLAGS:QUOTA_ENABLED/DENY_DISK/LOG_SOFTLIMIT/LOG_HARD_LIMIT", "SETSTRING" },
+ { "numeric", 'n', POPT_ARG_NONE, &numeric, True, "Don't resolve sids or limits to names" },
+ { "verbose", 'v', POPT_ARG_NONE, &verbose, True, "be verbose" },
+ { "test-args", 't', POPT_ARG_NONE, &test_args, True, "Test arguments"},
+ POPT_COMMON_SAMBA
+ POPT_COMMON_CREDENTIALS
+ { NULL }
+ };
+
+ ZERO_STRUCT(qt);
+
+ setlinebuf(stdout);
+
+ dbf = x_stderr;
+
+ fault_setup(NULL);
+
+ setup_logging(argv[0],True);
+
+
+ lp_load(dyn_CONFIGFILE,True,False,False);
+ load_interfaces();
+
+ pc = poptGetContext("smbcquotas", argc, argv, long_options, 0);
+
+ poptSetOtherOptionHelp(pc, "//server1/share1");
+
+ while ((opt = poptGetNextOpt(pc)) != -1) {
+ switch (opt) {
+ case 'L':
+ if (todo != 0) {
+ d_printf("Please specify only one option of <-L|-F|-S|-u>\n");
+ exit(EXIT_PARSE_ERROR);
+ }
+ todo = LIST_QUOTA;
+ break;
+
+ case 'F':
+ if (todo != 0) {
+ d_printf("Please specify only one option of <-L|-F|-S|-u>\n");
+ exit(EXIT_PARSE_ERROR);
+ }
+ todo = FS_QUOTA;
+ break;
+
+ case 'u':
+ if (todo != 0) {
+ d_printf("Please specify only one option of <-L|-F|-S|-u>\n");
+ exit(EXIT_PARSE_ERROR);
+ }
+ pstrcpy(username_str,poptGetOptArg(pc));
+ todo = USER_QUOTA;
+ fix_user = True;
+ break;
+
+ case 'S':
+ if (todo != 0) {
+ d_printf("Please specify only one option of <-L|-F|-S|-u>\n");
+ exit(EXIT_PARSE_ERROR);
+ }
+ pstrcpy(set_str,poptGetOptArg(pc));
+ todo = SET_QUOTA;
+ break;
+ }
+ }
+
+ if (todo == 0)
+ todo = USER_QUOTA;
+
+ if (!fix_user)
+ pstrcpy(username_str,cmdline_auth_info.username);
+
+ /* Make connection to server */
+ if(!poptPeekArg(pc)) {
+ poptPrintUsage(pc, stderr, 0);
+ exit(EXIT_PARSE_ERROR);
+ }
+
+ pstrcpy(path, poptGetArg(pc));
+
+ all_string_sub(path,"/","\\",0);
+
+ pstrcpy(server,path+2);
+ share = strchr_m(server,'\\');
+ if (!share) {
+ share = strchr_m(server,'/');
+ if (!share) {
+ printf("Invalid argument: %s\n", share);
+ exit(EXIT_PARSE_ERROR);
+ }
+ }
+
+ *share = 0;
+ share++;
+
+ if (todo == SET_QUOTA) {
+ if (parse_quota_set(set_str, username_str, &qtype, &cmd, &qt)) {
+ printf("Invalid argument: -S %s\n", set_str);
+ exit(EXIT_PARSE_ERROR);
+ }
+ }
+
+ if (!test_args) {
+ cli = connect_one(share);
+ if (!cli) {
+ exit(EXIT_FAILED);
+ }
+ } else {
+ exit(EXIT_OK);
+ }
+
+
+ /* Perform requested action */
+
+ switch (todo) {
+ case FS_QUOTA:
+ result = do_quota(cli,SMB_USER_FS_QUOTA_TYPE, QUOTA_GET, username_str, NULL);
+ break;
+ case LIST_QUOTA:
+ result = do_quota(cli,SMB_USER_QUOTA_TYPE, QUOTA_LIST, username_str, NULL);
+ break;
+ case USER_QUOTA:
+ result = do_quota(cli,SMB_USER_QUOTA_TYPE, QUOTA_GET, username_str, NULL);
+ break;
+ case SET_QUOTA:
+ result = do_quota(cli, qtype, cmd, username_str, &qt);
+ break;
+ default:
+
+ result = EXIT_FAILED;
+ break;
+ }
+
+ return result;
+}
+
diff --git a/source/utils/smbfilter.c b/source/utils/smbfilter.c
new file mode 100644
index 00000000000..5d67c8fc7cf
--- /dev/null
+++ b/source/utils/smbfilter.c
@@ -0,0 +1,245 @@
+/*
+ Unix SMB/CIFS implementation.
+ SMB filter/socket plugin
+ 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 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"
+
+#define SECURITY_MASK 0
+#define SECURITY_SET 0
+
+/* this forces non-unicode */
+#define CAPABILITY_MASK 0
+#define CAPABILITY_SET 0
+
+/* and non-unicode for the client too */
+#define CLI_CAPABILITY_MASK 0
+#define CLI_CAPABILITY_SET 0
+
+static char *netbiosname;
+static char packet[BUFFER_SIZE];
+
+static void save_file(const char *fname, void *packet, size_t length)
+{
+ int fd;
+ fd = open(fname, O_WRONLY|O_CREAT|O_TRUNC, 0644);
+ if (fd == -1) {
+ perror(fname);
+ return;
+ }
+ if (write(fd, packet, length) != length) {
+ fprintf(stderr,"Failed to write %s\n", fname);
+ return;
+ }
+ close(fd);
+ printf("Wrote %ld bytes to %s\n", (unsigned long)length, fname);
+}
+
+static void filter_reply(char *buf)
+{
+ int msg_type = CVAL(buf,0);
+ int type = CVAL(buf,smb_com);
+ unsigned x;
+
+ if (msg_type) return;
+
+ switch (type) {
+
+ case SMBnegprot:
+ /* force the security bits */
+ x = CVAL(buf, smb_vwv1);
+ x = (x | SECURITY_SET) & ~SECURITY_MASK;
+ SCVAL(buf, smb_vwv1, x);
+
+ /* force the capabilities */
+ x = IVAL(buf,smb_vwv9+1);
+ x = (x | CAPABILITY_SET) & ~CAPABILITY_MASK;
+ SIVAL(buf, smb_vwv9+1, x);
+ break;
+
+ }
+}
+
+static void filter_request(char *buf)
+{
+ int msg_type = CVAL(buf,0);
+ int type = CVAL(buf,smb_com);
+ pstring name1,name2;
+ unsigned x;
+
+ if (msg_type) {
+ /* it's a netbios special */
+ switch (msg_type) {
+ case 0x81:
+ /* session request */
+ name_extract(buf,4,name1);
+ name_extract(buf,4 + name_len(buf + 4),name2);
+ d_printf("sesion_request: %s -> %s\n",
+ name1, name2);
+ if (netbiosname) {
+ /* replace the destination netbios name */
+ name_mangle(netbiosname, buf+4, 0x20);
+ }
+ }
+ return;
+ }
+
+ /* it's an ordinary SMB request */
+ switch (type) {
+ case SMBsesssetupX:
+ /* force the client capabilities */
+ x = IVAL(buf,smb_vwv11);
+ d_printf("SMBsesssetupX cap=0x%08x\n", x);
+ d_printf("pwlen=%d/%d\n", SVAL(buf, smb_vwv7), SVAL(buf, smb_vwv8));
+ system("mv sessionsetup.dat sessionsetup1.dat");
+ save_file("sessionsetup.dat", smb_buf(buf), SVAL(buf, smb_vwv7));
+ x = (x | CLI_CAPABILITY_SET) & ~CLI_CAPABILITY_MASK;
+ SIVAL(buf, smb_vwv11, x);
+ break;
+ }
+
+}
+
+
+static void filter_child(int c, struct in_addr dest_ip)
+{
+ int s;
+
+ /* we have a connection from a new client, now connect to the server */
+ s = open_socket_out(SOCK_STREAM, &dest_ip, 445, LONG_CONNECT_TIMEOUT);
+
+ if (s == -1) {
+ d_printf("Unable to connect to %s\n", inet_ntoa(dest_ip));
+ exit(1);
+ }
+
+ while (c != -1 || s != -1) {
+ fd_set fds;
+ int num;
+
+ FD_ZERO(&fds);
+ if (s != -1) FD_SET(s, &fds);
+ if (c != -1) FD_SET(c, &fds);
+
+ num = sys_select_intr(MAX(s+1, c+1),&fds,NULL,NULL,NULL);
+ if (num <= 0) continue;
+
+ if (c != -1 && FD_ISSET(c, &fds)) {
+ if (!receive_smb(c, packet, 0)) {
+ d_printf("client closed connection\n");
+ exit(0);
+ }
+ filter_request(packet);
+ if (!send_smb(s, packet)) {
+ d_printf("server is dead\n");
+ exit(1);
+ }
+ }
+ if (s != -1 && FD_ISSET(s, &fds)) {
+ if (!receive_smb(s, packet, 0)) {
+ d_printf("server closed connection\n");
+ exit(0);
+ }
+ filter_reply(packet);
+ if (!send_smb(c, packet)) {
+ d_printf("client is dead\n");
+ exit(1);
+ }
+ }
+ }
+ d_printf("Connection closed\n");
+ exit(0);
+}
+
+
+static void start_filter(char *desthost)
+{
+ int s, c;
+ struct in_addr dest_ip;
+
+ CatchChild();
+
+ /* start listening on port 445 locally */
+ s = open_socket_in(SOCK_STREAM, 445, 0, 0, True);
+
+ if (s == -1) {
+ d_printf("bind failed\n");
+ exit(1);
+ }
+
+ if (listen(s, 5) == -1) {
+ d_printf("listen failed\n");
+ }
+
+ if (!resolve_name(desthost, &dest_ip, 0x20)) {
+ d_printf("Unable to resolve host %s\n", desthost);
+ exit(1);
+ }
+
+ while (1) {
+ fd_set fds;
+ int num;
+ struct sockaddr addr;
+ socklen_t in_addrlen = sizeof(addr);
+
+ FD_ZERO(&fds);
+ FD_SET(s, &fds);
+
+ num = sys_select_intr(s+1,&fds,NULL,NULL,NULL);
+ if (num > 0) {
+ c = accept(s, &addr, &in_addrlen);
+ if (c != -1) {
+ if (fork() == 0) {
+ close(s);
+ filter_child(c, dest_ip);
+ exit(0);
+ } else {
+ close(c);
+ }
+ }
+ }
+ }
+}
+
+
+int main(int argc, char *argv[])
+{
+ char *desthost;
+ pstring configfile;
+
+ setup_logging(argv[0],True);
+
+ pstrcpy(configfile,dyn_CONFIGFILE);
+
+ if (argc < 2) {
+ fprintf(stderr,"smbfilter <desthost> <netbiosname>\n");
+ exit(1);
+ }
+
+ desthost = argv[1];
+ if (argc > 2) {
+ netbiosname = argv[2];
+ }
+
+ if (!lp_load(configfile,True,False,False)) {
+ d_printf("Unable to load config file\n");
+ }
+
+ start_filter(desthost);
+ return 0;
+}
diff --git a/source/utils/smbget.c b/source/utils/smbget.c
new file mode 100644
index 00000000000..64630bba8fd
--- /dev/null
+++ b/source/utils/smbget.c
@@ -0,0 +1,589 @@
+/*
+ smbget: a wget-like utility with support for recursive downloading and
+ smb:// urls
+ Copyright (C) 2003-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 "libsmbclient.h"
+
+#if _FILE_OFFSET_BITS==64
+#define OFF_T_FORMAT "%lld"
+#else
+#define OFF_T_FORMAT "%ld"
+#endif
+
+int columns = 0;
+
+time_t total_start_time = 0;
+off_t total_bytes = 0;
+
+#define SMB_MAXPATHLEN MAXPATHLEN
+
+/* Number of bytes to read when checking whether local and remote file are really the same file */
+#define RESUME_CHECK_SIZE 512
+#define RESUME_DOWNLOAD_OFFSET 1024
+#define RESUME_CHECK_OFFSET RESUME_DOWNLOAD_OFFSET+RESUME_CHECK_SIZE
+/* Number of bytes to read at once */
+#define SMB_DEFAULT_BLOCKSIZE 64000
+
+const char *username = NULL, *password = NULL, *workgroup = NULL;
+int nonprompt = 0, quiet = 0, dots = 0, keep_permissions = 0, verbose = 0, send_stdout = 0;
+int blocksize = SMB_DEFAULT_BLOCKSIZE;
+
+int smb_download_file(const char *base, const char *name, int recursive, int resume, char *outfile);
+
+int get_num_cols(void)
+{
+#ifdef TIOCGWINSZ
+ struct winsize ws;
+ if(ioctl(STDOUT_FILENO, TIOCGWINSZ, &ws) < 0) {
+ return 0;
+ }
+ return ws.ws_col;
+#else
+#warning No support for TIOCGWINSZ
+ char *cols = getenv("COLUMNS");
+ if(!cols) return 0;
+ return atoi(cols);
+#endif
+}
+
+void change_columns(int sig)
+{
+ columns = get_num_cols();
+}
+
+void human_readable(off_t s, char *buffer, int l)
+{
+ if(s > 1024 * 1024 * 1024) snprintf(buffer, l, "%.2fGb", 1.0 * s / (1024 * 1024 * 1024));
+ else if(s > 1024 * 1024) snprintf(buffer, l, "%.2fMb", 1.0 * s / (1024 * 1024));
+ else if(s > 1024) snprintf(buffer, l, "%.2fkb", 1.0 * s / 1024);
+ else snprintf(buffer, l, OFF_T_FORMAT"b", s);
+}
+
+void get_auth_data(const char *srv, const char *shr, char *wg, int wglen, char *un, int unlen, char *pw, int pwlen)
+{
+ static char hasasked = 0;
+ char *wgtmp, *usertmp;
+ char tmp[128];
+
+ if(hasasked) return;
+ hasasked = 1;
+
+ if(!nonprompt && !username) {
+ printf("Username for %s at %s [guest] ", shr, srv);
+ fgets(tmp, sizeof(tmp), stdin);
+ if(tmp[strlen(tmp)-1] == '\n')tmp[strlen(tmp)-1] = '\0';
+ strncpy(un, tmp, unlen-1);
+ } else if(username) strncpy(un, username, unlen-1);
+
+ if(!nonprompt && !password) {
+ char *prompt, *pass;
+ asprintf(&prompt, "Password for %s at %s: ", shr, srv);
+ pass = getpass(prompt);
+ free(prompt);
+ strncpy(pw, pass, pwlen-1);
+ } else if(password) strncpy(pw, password, pwlen-1);
+
+ if(workgroup)strncpy(wg, workgroup, wglen-1);
+
+ wgtmp = strndup(wg, wglen);
+ usertmp = strndup(un, unlen);
+ if(!quiet)printf("Using workgroup %s, %s%s\n", wgtmp, *usertmp?"user ":"guest user", usertmp);
+ free(wgtmp); free(usertmp);
+}
+
+int smb_download_dir(const char *base, const char *name, int resume)
+{
+ char path[SMB_MAXPATHLEN];
+ int dirhandle;
+ struct smbc_dirent *dirent;
+ const char *relname = name;
+ char *tmpname;
+ struct stat remotestat;
+ snprintf(path, SMB_MAXPATHLEN-1, "%s%s%s", base, (base[0] && name[0] && name[0] != '/' && base[strlen(base)-1] != '/')?"/":"", name);
+
+ /* List files in directory and call smb_download_file on them */
+ dirhandle = smbc_opendir(path);
+ if(dirhandle < 1) {
+ if(errno == ENOTDIR) return smb_download_file(base, name, 1, resume, NULL);
+ fprintf(stderr, "Can't open directory %s: %s\n", path, strerror(errno));
+ return 0;
+ }
+
+ while(*relname == '/')relname++;
+ mkdir(relname, 0755);
+
+ tmpname = strdup(name);
+
+ while((dirent = smbc_readdir(dirhandle))) {
+ char *newname;
+ if(!strcmp(dirent->name, ".") || !strcmp(dirent->name, ".."))continue;
+ asprintf(&newname, "%s/%s", tmpname, dirent->name);
+ switch(dirent->smbc_type) {
+ case SMBC_DIR:
+ smb_download_dir(base, newname, resume);
+ break;
+
+ case SMBC_WORKGROUP:
+ smb_download_dir("smb://", dirent->name, resume);
+ break;
+
+ case SMBC_SERVER:
+ smb_download_dir("smb://", dirent->name, resume);
+ break;
+
+ case SMBC_FILE:
+ smb_download_file(base, newname, 1, resume, NULL);
+ break;
+
+ case SMBC_FILE_SHARE:
+ smb_download_dir(base, newname, resume);
+ break;
+
+ case SMBC_PRINTER_SHARE:
+ if(!quiet)printf("Ignoring printer share %s\n", dirent->name);
+ break;
+
+ case SMBC_COMMS_SHARE:
+ if(!quiet)printf("Ignoring comms share %s\n", dirent->name);
+ break;
+
+ case SMBC_IPC_SHARE:
+ if(!quiet)printf("Ignoring ipc$ share %s\n", dirent->name);
+ break;
+
+ default:
+ fprintf(stderr, "Ignoring file '%s' of type '%d'\n", newname, dirent->smbc_type);
+ break;
+ }
+ free(newname);
+ }
+ free(tmpname);
+
+ if(keep_permissions) {
+ if(smbc_fstat(dirhandle, &remotestat) < 0) {
+ fprintf(stderr, "Unable to get stats on %s on remote server\n", path);
+ smbc_closedir(dirhandle);
+ return 0;
+ }
+
+ if(chmod(relname, remotestat.st_mode) < 0) {
+ fprintf(stderr, "Unable to change mode of local dir %s to %o\n", relname, remotestat.st_mode);
+ smbc_closedir(dirhandle);
+ return 0;
+ }
+ }
+
+ smbc_closedir(dirhandle);
+ return 1;
+}
+
+char *print_time(long t)
+{
+ static char buffer[100];
+ int secs, mins, hours;
+ if(t < -1) {
+ strncpy(buffer, "Unknown", sizeof(buffer));
+ return buffer;
+ }
+
+ secs = (int)t % 60;
+ mins = (int)t / 60 % 60;
+ hours = (int)t / (60 * 60);
+ snprintf(buffer, sizeof(buffer)-1, "%02d:%02d:%02d", hours, mins, secs);
+ return buffer;
+}
+
+void print_progress(const char *name, time_t start, time_t now, off_t start_pos, off_t pos, off_t total)
+{
+ double avg = 0.0;
+ long eta = -1;
+ double prcnt = 0.0;
+ char hpos[20], htotal[20], havg[20];
+ char *status, *filename;
+ int len;
+ if(now - start)avg = 1.0 * (pos - start_pos) / (now - start);
+ eta = (total - pos) / avg;
+ if(total)prcnt = 100.0 * pos / total;
+
+ human_readable(pos, hpos, sizeof(hpos));
+ human_readable(total, htotal, sizeof(htotal));
+ human_readable(avg, havg, sizeof(havg));
+
+ len = asprintf(&status, "%s of %s (%.2f%%) at %s/s ETA: %s", hpos, htotal, prcnt, havg, print_time(eta));
+
+ if(columns) {
+ int required = strlen(name), available = columns - len - strlen("[] ");
+ if(required > available) asprintf(&filename, "...%s", name + required - available + 3);
+ else filename = strndup(name, available);
+ } else filename = strdup(name);
+
+ fprintf(stderr, "\r[%s] %s", filename, status);
+
+ free(filename); free(status);
+}
+
+int smb_download_file(const char *base, const char *name, int recursive, int resume, char *outfile) {
+ int remotehandle, localhandle;
+ time_t start_time = time(NULL);
+ const char *newpath;
+ char path[SMB_MAXPATHLEN];
+ char checkbuf[2][RESUME_CHECK_SIZE];
+ char *readbuf = NULL;
+ off_t offset_download = 0, offset_check = 0, curpos = 0, start_offset = 0;
+ struct stat localstat, remotestat;
+
+ snprintf(path, SMB_MAXPATHLEN-1, "%s%s%s", base, (*base && *name && name[0] != '/' && base[strlen(base)-1] != '/')?"/":"", name);
+
+ remotehandle = smbc_open(path, O_RDONLY, 0755);
+
+ if(remotehandle < 0) {
+ switch(errno) {
+ case EISDIR:
+ if(!recursive) {
+ fprintf(stderr, "%s is a directory. Specify -R to download recursively\n", path);
+ return 0;
+ }
+ smb_download_dir(base, name, resume);
+ return 0;
+
+ case ENOENT:
+ fprintf(stderr, "%s can't be found on the remote server\n", path);
+ return 0;
+
+ case ENOMEM:
+ fprintf(stderr, "Not enough memory\n");
+ exit(1);
+ return 0;
+
+ case ENODEV:
+ fprintf(stderr, "The share name used in %s does not exist\n", path);
+ return 0;
+
+ case EACCES:
+ fprintf(stderr, "You don't have enough permissions to access %s\n", path);
+ return 0;
+
+ default:
+ perror("smbc_open");
+ return 0;
+ }
+ }
+
+ if(smbc_fstat(remotehandle, &remotestat) < 0) {
+ fprintf(stderr, "Can't stat %s: %s\n", path, strerror(errno));
+ return 0;
+ }
+
+ if(outfile) newpath = outfile;
+ else if(!name[0]) {
+ newpath = strrchr(base, '/');
+ if(newpath)newpath++; else newpath = base;
+ } else newpath = name;
+
+ if(newpath[0] == '/')newpath++;
+
+ /* Open local file and, if necessary, resume */
+ if(!send_stdout) {
+ localhandle = open(newpath, O_CREAT | O_NONBLOCK | O_RDWR | (!resume?O_EXCL:0), 0755);
+ if(localhandle < 0) {
+ fprintf(stderr, "Can't open %s: %s\n", newpath, strerror(errno));
+ smbc_close(remotehandle);
+ return 0;
+ }
+
+ fstat(localhandle, &localstat);
+
+ start_offset = localstat.st_size;
+
+ if(localstat.st_size && localstat.st_size == remotestat.st_size) {
+ if(verbose)fprintf(stderr, "%s is already downloaded completely.\n", path);
+ else if(!quiet)fprintf(stderr, "%s\n", path);
+ smbc_close(remotehandle);
+ close(localhandle);
+ return 1;
+ }
+
+ if(localstat.st_size > RESUME_CHECK_OFFSET && remotestat.st_size > RESUME_CHECK_OFFSET) {
+ offset_download = localstat.st_size - RESUME_DOWNLOAD_OFFSET;
+ offset_check = localstat.st_size - RESUME_CHECK_OFFSET;
+ if(verbose)printf("Trying to start resume of %s at "OFF_T_FORMAT"\n"
+ "At the moment "OFF_T_FORMAT" of "OFF_T_FORMAT" bytes have been retrieved\n", newpath, offset_check,
+ localstat.st_size, remotestat.st_size);
+ }
+
+ if(offset_check) {
+ off_t off1, off2;
+ /* First, check all bytes from offset_check to offset_download */
+ off1 = lseek(localhandle, offset_check, SEEK_SET);
+ if(off1 < 0) {
+ fprintf(stderr, "Can't seek to "OFF_T_FORMAT" in local file %s\n", offset_check, newpath);
+ smbc_close(remotehandle); close(localhandle);
+ return 0;
+ }
+
+ off2 = smbc_lseek(remotehandle, offset_check, SEEK_SET);
+ if(off2 < 0) {
+ fprintf(stderr, "Can't seek to "OFF_T_FORMAT" in remote file %s\n", offset_check, newpath);
+ smbc_close(remotehandle); close(localhandle);
+ return 0;
+ }
+
+ if(off1 != off2) {
+ fprintf(stderr, "Offset in local and remote files is different (local: "OFF_T_FORMAT", remote: "OFF_T_FORMAT")\n", off1, off2);
+ return 0;
+ }
+
+ if(smbc_read(remotehandle, checkbuf[0], RESUME_CHECK_SIZE) != RESUME_CHECK_SIZE) {
+ fprintf(stderr, "Can't read %d bytes from remote file %s\n", RESUME_CHECK_SIZE, path);
+ smbc_close(remotehandle); close(localhandle);
+ return 0;
+ }
+
+ if(read(localhandle, checkbuf[1], RESUME_CHECK_SIZE) != RESUME_CHECK_SIZE) {
+ fprintf(stderr, "Can't read %d bytes from local file %s\n", RESUME_CHECK_SIZE, name);
+ smbc_close(remotehandle); close(localhandle);
+ return 0;
+ }
+
+ if(memcmp(checkbuf[0], checkbuf[1], RESUME_CHECK_SIZE) == 0) {
+ if(verbose)printf("Current local and remote file appear to be the same. Starting download from offset "OFF_T_FORMAT"\n", offset_download);
+ } else {
+ fprintf(stderr, "Local and remote file appear to be different, not doing resume for %s\n", path);
+ smbc_close(remotehandle); close(localhandle);
+ return 0;
+ }
+ }
+ } else {
+ localhandle = STDOUT_FILENO;
+ start_offset = 0;
+ offset_download = 0;
+ offset_check = 0;
+ }
+
+ readbuf = malloc(blocksize);
+
+ /* Now, download all bytes from offset_download to the end */
+ for(curpos = offset_download; curpos < remotestat.st_size; curpos+=blocksize) {
+ ssize_t bytesread = smbc_read(remotehandle, readbuf, blocksize);
+ if(bytesread < 0) {
+ fprintf(stderr, "Can't read %d bytes at offset "OFF_T_FORMAT", file %s\n", blocksize, curpos, path);
+ smbc_close(remotehandle);
+ if (localhandle != STDOUT_FILENO) close(localhandle);
+ free(readbuf);
+ return 0;
+ }
+
+ total_bytes += bytesread;
+
+ if(write(localhandle, readbuf, bytesread) < 0) {
+ fprintf(stderr, "Can't write %d bytes to local file %s at offset "OFF_T_FORMAT"\n", bytesread, path, curpos);
+ free(readbuf);
+ smbc_close(remotehandle);
+ if (localhandle != STDOUT_FILENO) close(localhandle);
+ return 0;
+ }
+
+ if(dots)fputc('.', stderr);
+ else if(!quiet) {
+ print_progress(newpath, start_time, time(NULL), start_offset, curpos, remotestat.st_size);
+ }
+ }
+
+ free(readbuf);
+
+ if(dots){
+ fputc('\n', stderr);
+ printf("%s downloaded\n", path);
+ } else if(!quiet) {
+ int i;
+ fprintf(stderr, "\r%s", path);
+ if(columns) {
+ for(i = strlen(path); i < columns; i++) {
+ fputc(' ', stderr);
+ }
+ }
+ fputc('\n', stderr);
+ }
+
+ if(keep_permissions && !send_stdout) {
+ if(fchmod(localhandle, remotestat.st_mode) < 0) {
+ fprintf(stderr, "Unable to change mode of local file %s to %o\n", path, remotestat.st_mode);
+ smbc_close(remotehandle);
+ close(localhandle);
+ return 0;
+ }
+ }
+
+ smbc_close(remotehandle);
+ if (localhandle != STDOUT_FILENO) close(localhandle);
+ return 1;
+}
+
+void clean_exit(void)
+{
+ char bs[100];
+ human_readable(total_bytes, bs, sizeof(bs));
+ if(!quiet)fprintf(stderr, "Downloaded %s in %lu seconds\n", bs, time(NULL) - total_start_time);
+ exit(0);
+}
+
+void signal_quit(int v)
+{
+ clean_exit();
+}
+
+int readrcfile(const char *name, const struct poptOption long_options[])
+{
+ FILE *fd = fopen(name, "r");
+ int lineno = 0, i;
+ char var[101], val[101];
+ char found;
+ int *intdata; char **stringdata;
+ if(!fd) {
+ fprintf(stderr, "Can't open RC file %s\n", name);
+ return 1;
+ }
+
+ while(!feof(fd)) {
+ lineno++;
+ if(fscanf(fd, "%100s %100s\n", var, val) < 2) {
+ fprintf(stderr, "Can't parse line %d of %s, ignoring.\n", lineno, name);
+ continue;
+ }
+
+ found = 0;
+
+ for(i = 0; long_options[i].shortName; i++) {
+ if(!long_options[i].longName)continue;
+ if(strcmp(long_options[i].longName, var)) continue;
+ if(!long_options[i].arg)continue;
+
+ switch(long_options[i].argInfo) {
+ case POPT_ARG_NONE:
+ intdata = (int *)long_options[i].arg;
+ if(!strcmp(val, "on")) *intdata = 1;
+ else if(!strcmp(val, "off")) *intdata = 0;
+ else fprintf(stderr, "Illegal value %s for %s at line %d in %s\n", val, var, lineno, name);
+ break;
+ case POPT_ARG_INT:
+ intdata = (int *)long_options[i].arg;
+ *intdata = atoi(val);
+ break;
+ case POPT_ARG_STRING:
+ stringdata = (char **)long_options[i].arg;
+ *stringdata = strdup(val);
+ break;
+ default:
+ fprintf(stderr, "Invalid variable %s at line %d in %s\n", var, lineno, name);
+ break;
+ }
+
+ found = 1;
+ }
+ if(!found) {
+ fprintf(stderr, "Invalid variable %s at line %d in %s\n", var, lineno, name);
+ }
+ }
+
+ fclose(fd);
+ return 0;
+}
+
+int main(int argc, const char **argv)
+{
+ int resume = 0, recursive = 0;
+ int c = 0;
+ int debuglevel = 0;
+ const char *file = NULL;
+ char *rcfile = NULL;
+ char *outputfile = NULL;
+ struct poptOption long_options[] = {
+ {"guest", 'a', POPT_ARG_NONE, NULL, 'a', "Work as user guest" },
+ {"resume", 'r', POPT_ARG_NONE, &resume, 0, "Automatically resume aborted files" },
+ {"recursive", 'R', POPT_ARG_NONE, &recursive, 0, "Recursively download files" },
+ {"username", 'u', POPT_ARG_STRING, &username, 'u', "Username to use" },
+ {"password", 'p', POPT_ARG_STRING, &password, 'p', "Password to use" },
+ {"workgroup", 'w', POPT_ARG_STRING, &workgroup, 'w', "Workgroup to use (optional)" },
+ {"nonprompt", 'n', POPT_ARG_NONE, &nonprompt, 'n', "Don't ask anything (non-interactive)" },
+ {"debuglevel", 'd', POPT_ARG_INT, &debuglevel, 'd', "Debuglevel to use" },
+ {"outputfile", 'o', POPT_ARG_STRING, &outputfile, 'o', "Write downloaded data to specified file" },
+ {"stdout", 'O', POPT_ARG_NONE, &send_stdout, 'O', "Write data to stdout" },
+ {"dots", 'D', POPT_ARG_NONE, &dots, 'D', "Show dots as progress indication" },
+ {"quiet", 'q', POPT_ARG_NONE, &quiet, 'q', "Be quiet" },
+ {"verbose", 'v', POPT_ARG_NONE, &verbose, 'v', "Be verbose" },
+ {"keep-permissions", 'P', POPT_ARG_NONE, &keep_permissions, 'P', "Keep permissions" },
+ {"blocksize", 'b', POPT_ARG_INT, &blocksize, 'b', "Change number of bytes in a block"},
+ {"rcfile", 'f', POPT_ARG_STRING, NULL, 0, "Use specified rc file"},
+ POPT_AUTOHELP
+ POPT_TABLEEND
+ };
+ poptContext pc;
+
+ /* only read rcfile if it exists */
+ asprintf(&rcfile, "%s/.smbgetrc", getenv("HOME"));
+ if(access(rcfile, F_OK) == 0) readrcfile(rcfile, long_options);
+ free(rcfile);
+
+#ifdef SIGWINCH
+ signal(SIGWINCH, change_columns);
+#endif
+ signal(SIGINT, signal_quit);
+ signal(SIGTERM, signal_quit);
+
+ pc = poptGetContext(argv[0], argc, argv, long_options, 0);
+
+ while((c = poptGetNextOpt(pc)) >= 0) {
+ switch(c) {
+ case 'f':
+ readrcfile(poptGetOptArg(pc), long_options);
+ break;
+ case 'a':
+ username = ""; password = "";
+ break;
+ }
+ }
+
+ if((send_stdout || outputfile) && recursive) {
+ fprintf(stderr, "The -o or -O and -R options can not be used together.\n");
+ return 1;
+ }
+
+ if(outputfile && send_stdout) {
+ fprintf(stderr, "The -o and -O options cannot be used together.\n");
+ return 1;
+ }
+
+ if(smbc_init(get_auth_data, debuglevel) < 0) {
+ fprintf(stderr, "Unable to initialize libsmbclient\n");
+ return 1;
+ }
+
+ columns = get_num_cols();
+
+ total_start_time = time(NULL);
+
+ while((file = poptGetArg(pc))) {
+ if(!recursive) return smb_download_file(file, "", recursive, resume, outputfile);
+ else return smb_download_dir(file, "", resume);
+ }
+
+ clean_exit();
+
+ return 0;
+}
diff --git a/source/utils/smbpasswd.c b/source/utils/smbpasswd.c
new file mode 100644
index 00000000000..0476a2e39c0
--- /dev/null
+++ b/source/utils/smbpasswd.c
@@ -0,0 +1,614 @@
+/*
+ * Unix SMB/CIFS implementation.
+ * Copyright (C) Jeremy Allison 1995-1998
+ * Copyright (C) Tim Potter 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 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"
+
+extern BOOL AllowDebugChange;
+
+/*
+ * Next two lines needed for SunOS and don't
+ * hurt anything else...
+ */
+extern char *optarg;
+extern int optind;
+
+/* forced running in root-mode */
+static BOOL got_pass = False, got_username = False;
+static BOOL stdin_passwd_get = False;
+static fstring user_name, user_password;
+static char *new_passwd = NULL;
+static const char *remote_machine = NULL;
+
+static fstring ldap_secret;
+
+
+/*********************************************************
+ Print command usage on stderr and die.
+**********************************************************/
+static void usage(void)
+{
+ printf("When run by root:\n");
+ printf(" smbpasswd [options] [username] [password]\n");
+ printf("otherwise:\n");
+ printf(" smbpasswd [options] [password]\n\n");
+
+ printf("options:\n");
+ printf(" -L local mode (must be first option)\n");
+ printf(" -h print this usage message\n");
+ printf(" -s use stdin for password prompt\n");
+ printf(" -c smb.conf file Use the given path to the smb.conf file\n");
+ printf(" -D LEVEL debug level\n");
+ printf(" -r MACHINE remote machine\n");
+ printf(" -U USER remote username\n");
+
+ printf("extra options when run by root or in local mode:\n");
+ printf(" -a add user\n");
+ printf(" -d disable user\n");
+ printf(" -e enable user\n");
+ printf(" -i interdomain trust account\n");
+ printf(" -m machine trust account\n");
+ printf(" -n set no password\n");
+ printf(" -w PASSWORD ldap admin password\n");
+ printf(" -x delete user\n");
+ printf(" -R ORDER name resolve order\n");
+
+ exit(1);
+}
+
+static void set_line_buffering(FILE *f)
+{
+ setvbuf(f, NULL, _IOLBF, 0);
+}
+
+/*******************************************************************
+ Process command line options
+ ******************************************************************/
+static int process_options(int argc, char **argv, int local_flags)
+{
+ int ch;
+ pstring configfile;
+ pstrcpy(configfile, dyn_CONFIGFILE);
+
+ local_flags |= LOCAL_SET_PASSWORD;
+
+ ZERO_STRUCT(user_name);
+ ZERO_STRUCT(user_password);
+
+ user_name[0] = '\0';
+
+ while ((ch = getopt(argc, argv, "c:axdehminjr:sw:R:D:U:L")) != EOF) {
+ switch(ch) {
+ case 'L':
+ local_flags |= LOCAL_AM_ROOT;
+ break;
+ case 'c':
+ pstrcpy(configfile,optarg);
+ break;
+ case 'a':
+ local_flags |= LOCAL_ADD_USER;
+ break;
+ case 'x':
+ local_flags |= LOCAL_DELETE_USER;
+ local_flags &= ~LOCAL_SET_PASSWORD;
+ break;
+ case 'd':
+ local_flags |= LOCAL_DISABLE_USER;
+ local_flags &= ~LOCAL_SET_PASSWORD;
+ break;
+ case 'e':
+ local_flags |= LOCAL_ENABLE_USER;
+ local_flags &= ~LOCAL_SET_PASSWORD;
+ break;
+ case 'm':
+ local_flags |= LOCAL_TRUST_ACCOUNT;
+ break;
+ case 'i':
+ local_flags |= LOCAL_INTERDOM_ACCOUNT;
+ break;
+ case 'j':
+ d_printf("See 'net join' for this functionality\n");
+ exit(1);
+ break;
+ case 'n':
+ local_flags |= LOCAL_SET_NO_PASSWORD;
+ local_flags &= ~LOCAL_SET_PASSWORD;
+ new_passwd = smb_xstrdup("NO PASSWORD");
+ break;
+ case 'r':
+ remote_machine = optarg;
+ break;
+ case 's':
+ set_line_buffering(stdin);
+ set_line_buffering(stdout);
+ set_line_buffering(stderr);
+ stdin_passwd_get = True;
+ break;
+ case 'w':
+ local_flags |= LOCAL_SET_LDAP_ADMIN_PW;
+ fstrcpy(ldap_secret, optarg);
+ break;
+ case 'R':
+ lp_set_name_resolve_order(optarg);
+ break;
+ case 'D':
+ DEBUGLEVEL = atoi(optarg);
+ break;
+ case 'U': {
+ char *lp;
+
+ got_username = True;
+ fstrcpy(user_name, optarg);
+
+ if ((lp = strchr(user_name, '%'))) {
+ *lp = 0;
+ fstrcpy(user_password, lp + 1);
+ got_pass = True;
+ memset(strchr_m(optarg, '%') + 1, 'X',
+ strlen(user_password));
+ }
+
+ break;
+ }
+ case 'h':
+ default:
+ usage();
+ }
+ }
+
+ argc -= optind;
+ argv += optind;
+
+ switch(argc) {
+ case 0:
+ if (!got_username)
+ fstrcpy(user_name, "");
+ break;
+ case 1:
+ if (!(local_flags & LOCAL_AM_ROOT)) {
+ new_passwd = argv[0];
+ } else {
+ if (got_username) {
+ usage();
+ } else {
+ fstrcpy(user_name, argv[0]);
+ }
+ }
+ break;
+ case 2:
+ if (!(local_flags & LOCAL_AM_ROOT) || got_username || got_pass) {
+ usage();
+ }
+
+ fstrcpy(user_name, argv[0]);
+ new_passwd = smb_xstrdup(argv[1]);
+ break;
+ default:
+ usage();
+ }
+
+ if (!lp_load(configfile,True,False,False)) {
+ fprintf(stderr, "Can't load %s - run testparm to debug it\n",
+ dyn_CONFIGFILE);
+ exit(1);
+ }
+
+ return local_flags;
+}
+
+/*************************************************************
+ Utility function to prompt for passwords from stdin. Each
+ password entered must end with a newline.
+*************************************************************/
+static char *stdin_new_passwd(void)
+{
+ static fstring new_pw;
+ size_t len;
+
+ ZERO_ARRAY(new_pw);
+
+ /*
+ * if no error is reported from fgets() and string at least contains
+ * the newline that ends the password, then replace the newline with
+ * a null terminator.
+ */
+ if ( fgets(new_pw, sizeof(new_pw), stdin) != NULL) {
+ if ((len = strlen(new_pw)) > 0) {
+ if(new_pw[len-1] == '\n')
+ new_pw[len - 1] = 0;
+ }
+ }
+ return(new_pw);
+}
+
+
+/*************************************************************
+ Utility function to get passwords via tty or stdin
+ Used if the '-s' option is set to silently get passwords
+ to enable scripting.
+*************************************************************/
+static char *get_pass( const char *prompt, BOOL stdin_get)
+{
+ char *p;
+ if (stdin_get) {
+ p = stdin_new_passwd();
+ } else {
+ p = getpass(prompt);
+ }
+ return smb_xstrdup(p);
+}
+
+/*************************************************************
+ Utility function to prompt for new password.
+*************************************************************/
+static char *prompt_for_new_password(BOOL stdin_get)
+{
+ char *p;
+ fstring new_pw;
+
+ ZERO_ARRAY(new_pw);
+
+ p = get_pass("New SMB password:", stdin_get);
+
+ fstrcpy(new_pw, p);
+ SAFE_FREE(p);
+
+ p = get_pass("Retype new SMB password:", stdin_get);
+
+ if (strcmp(p, new_pw)) {
+ fprintf(stderr, "Mismatch - password unchanged.\n");
+ ZERO_ARRAY(new_pw);
+ SAFE_FREE(p);
+ return NULL;
+ }
+
+ return p;
+}
+
+
+/*************************************************************
+ Change a password either locally or remotely.
+*************************************************************/
+
+static BOOL password_change(const char *remote_mach, char *username,
+ char *old_passwd, char *new_pw, int local_flags)
+{
+ BOOL ret;
+ pstring err_str;
+ pstring msg_str;
+
+ if (remote_mach != NULL) {
+ if (local_flags & (LOCAL_ADD_USER|LOCAL_DELETE_USER|LOCAL_DISABLE_USER|LOCAL_ENABLE_USER|
+ LOCAL_TRUST_ACCOUNT|LOCAL_SET_NO_PASSWORD)) {
+ /* these things can't be done remotely yet */
+ return False;
+ }
+ ret = remote_password_change(remote_mach, username,
+ old_passwd, new_pw, err_str, sizeof(err_str));
+ if(*err_str)
+ fprintf(stderr, err_str);
+ return ret;
+ }
+
+ ret = local_password_change(username, local_flags, new_pw,
+ err_str, sizeof(err_str), msg_str, sizeof(msg_str));
+
+ if(*msg_str)
+ printf(msg_str);
+ if(*err_str)
+ fprintf(stderr, err_str);
+
+ return ret;
+}
+
+/*******************************************************************
+ Store the LDAP admin password in secrets.tdb
+ ******************************************************************/
+static BOOL store_ldap_admin_pw (char* pw)
+{
+ if (!pw)
+ return False;
+
+ if (!secrets_init())
+ return False;
+
+ return secrets_store_ldap_pw(lp_ldap_admin_dn(), pw);
+}
+
+
+/*************************************************************
+ Handle password changing for root.
+*************************************************************/
+
+static int process_root(int local_flags)
+{
+ struct passwd *pwd;
+ int result = 0;
+ char *old_passwd = NULL;
+
+ if (local_flags & LOCAL_SET_LDAP_ADMIN_PW) {
+ printf("Setting stored password for \"%s\" in secrets.tdb\n",
+ lp_ldap_admin_dn());
+ if (!store_ldap_admin_pw(ldap_secret))
+ DEBUG(0,("ERROR: Failed to store the ldap admin password!\n"));
+ goto done;
+ }
+
+ /* Ensure passdb startup(). */
+ if(!initialize_password_db(False)) {
+ DEBUG(0, ("Failed to open passdb!\n"));
+ exit(1);
+ }
+
+ /* Ensure we have a SAM sid. */
+ get_global_sam_sid();
+
+ /*
+ * Ensure both add/delete user are not set
+ * Ensure add/delete user and either remote machine or join domain are
+ * not both set.
+ */
+ if(((local_flags & (LOCAL_ADD_USER|LOCAL_DELETE_USER)) == (LOCAL_ADD_USER|LOCAL_DELETE_USER)) ||
+ ((local_flags & (LOCAL_ADD_USER|LOCAL_DELETE_USER)) &&
+ (remote_machine != NULL))) {
+ usage();
+ }
+
+ /* Only load interfaces if we are doing network operations. */
+
+ if (remote_machine) {
+ load_interfaces();
+ }
+
+ if (!user_name[0] && (pwd = getpwuid_alloc(geteuid()))) {
+ fstrcpy(user_name, pwd->pw_name);
+ passwd_free(&pwd);
+ }
+
+ if (!user_name[0]) {
+ fprintf(stderr,"You must specify a username\n");
+ exit(1);
+ }
+
+ if (local_flags & LOCAL_TRUST_ACCOUNT) {
+ /* add the $ automatically */
+ static fstring buf;
+
+ /*
+ * Remove any trailing '$' before we
+ * generate the initial machine password.
+ */
+
+ if (user_name[strlen(user_name)-1] == '$') {
+ user_name[strlen(user_name)-1] = 0;
+ }
+
+ if (local_flags & LOCAL_ADD_USER) {
+ SAFE_FREE(new_passwd);
+ new_passwd = smb_xstrdup(user_name);
+ strlower_m(new_passwd);
+ }
+
+ /*
+ * Now ensure the username ends in '$' for
+ * the machine add.
+ */
+
+ slprintf(buf, sizeof(buf)-1, "%s$", user_name);
+ fstrcpy(user_name, buf);
+ } else if (local_flags & LOCAL_INTERDOM_ACCOUNT) {
+ static fstring buf;
+
+ if ((local_flags & LOCAL_ADD_USER) && (new_passwd == NULL)) {
+ /*
+ * Prompt for trusting domain's account password
+ */
+ new_passwd = prompt_for_new_password(stdin_passwd_get);
+ if(!new_passwd) {
+ fprintf(stderr, "Unable to get newpassword.\n");
+ exit(1);
+ }
+ }
+
+ /* prepare uppercased and '$' terminated username */
+ slprintf(buf, sizeof(buf) - 1, "%s$", user_name);
+ fstrcpy(user_name, buf);
+
+ } else {
+
+ if (remote_machine != NULL) {
+ old_passwd = get_pass("Old SMB password:",stdin_passwd_get);
+ }
+
+ if (!(local_flags & LOCAL_SET_PASSWORD)) {
+
+ /*
+ * If we are trying to enable a user, first we need to find out
+ * if they are using a modern version of the smbpasswd file that
+ * disables a user by just writing a flag into the file. If so
+ * then we can re-enable a user without prompting for a new
+ * password. If not (ie. they have a no stored password in the
+ * smbpasswd file) then we need to prompt for a new password.
+ */
+
+ if(local_flags & LOCAL_ENABLE_USER) {
+ SAM_ACCOUNT *sampass = NULL;
+ BOOL ret;
+
+ pdb_init_sam(&sampass);
+ ret = pdb_getsampwnam(sampass, user_name);
+ if((sampass != False) && (pdb_get_lanman_passwd(sampass) == NULL)) {
+ local_flags |= LOCAL_SET_PASSWORD;
+ }
+ pdb_free_sam(&sampass);
+ }
+ }
+
+ if((local_flags & LOCAL_SET_PASSWORD) && (new_passwd == NULL)) {
+ new_passwd = prompt_for_new_password(stdin_passwd_get);
+
+ if(!new_passwd) {
+ fprintf(stderr, "Unable to get new password.\n");
+ exit(1);
+ }
+ }
+ }
+
+ if (!password_change(remote_machine, user_name, old_passwd, new_passwd, local_flags)) {
+ fprintf(stderr,"Failed to modify password entry for user %s\n", user_name);
+ result = 1;
+ goto done;
+ }
+
+ if(remote_machine) {
+ printf("Password changed for user %s on %s.\n", user_name, remote_machine );
+ } else if(!(local_flags & (LOCAL_ADD_USER|LOCAL_DISABLE_USER|LOCAL_ENABLE_USER|LOCAL_DELETE_USER|LOCAL_SET_NO_PASSWORD|LOCAL_SET_PASSWORD))) {
+ SAM_ACCOUNT *sampass = NULL;
+ BOOL ret;
+
+ pdb_init_sam(&sampass);
+ ret = pdb_getsampwnam(sampass, user_name);
+
+ printf("Password changed for user %s.", user_name );
+ if( (ret != False) && (pdb_get_acct_ctrl(sampass)&ACB_DISABLED) )
+ printf(" User has disabled flag set.");
+ if((ret != False) && (pdb_get_acct_ctrl(sampass) & ACB_PWNOTREQ) )
+ printf(" User has no password flag set.");
+ printf("\n");
+ pdb_free_sam(&sampass);
+ }
+
+ done:
+ SAFE_FREE(new_passwd);
+ return result;
+}
+
+
+/*************************************************************
+ Handle password changing for non-root.
+*************************************************************/
+
+static int process_nonroot(int local_flags)
+{
+ struct passwd *pwd = NULL;
+ int result = 0;
+ char *old_pw = NULL;
+ char *new_pw = NULL;
+
+ if (local_flags & ~(LOCAL_AM_ROOT | LOCAL_SET_PASSWORD)) {
+ /* Extra flags that we can't honor non-root */
+ usage();
+ }
+
+ if (!user_name[0]) {
+ pwd = getpwuid_alloc(getuid());
+ if (pwd) {
+ fstrcpy(user_name,pwd->pw_name);
+ passwd_free(&pwd);
+ } else {
+ fprintf(stderr, "smbpasswd: you don't exist - go away\n");
+ exit(1);
+ }
+ }
+
+ /*
+ * A non-root user is always setting a password
+ * via a remote machine (even if that machine is
+ * localhost).
+ */
+
+ load_interfaces(); /* Delayed from main() */
+
+ if (remote_machine == NULL) {
+ remote_machine = "127.0.0.1";
+ }
+
+ if (remote_machine != NULL) {
+ old_pw = get_pass("Old SMB password:",stdin_passwd_get);
+ }
+
+ if (!new_passwd) {
+ new_pw = prompt_for_new_password(stdin_passwd_get);
+ }
+ else
+ new_pw = smb_xstrdup(new_passwd);
+
+ if (!new_pw) {
+ fprintf(stderr, "Unable to get new password.\n");
+ exit(1);
+ }
+
+ if (!password_change(remote_machine, user_name, old_pw, new_pw, 0)) {
+ fprintf(stderr,"Failed to change password for %s\n", user_name);
+ result = 1;
+ goto done;
+ }
+
+ printf("Password changed for user %s\n", user_name);
+
+ done:
+ SAFE_FREE(old_pw);
+ SAFE_FREE(new_pw);
+
+ return result;
+}
+
+
+
+/*********************************************************
+ Start here.
+**********************************************************/
+int main(int argc, char **argv)
+{
+ int local_flags = 0;
+
+ AllowDebugChange = False;
+
+#if defined(HAVE_SET_AUTH_PARAMETERS)
+ set_auth_parameters(argc, argv);
+#endif /* HAVE_SET_AUTH_PARAMETERS */
+
+ if (getuid() == 0) {
+ local_flags = LOCAL_AM_ROOT;
+ }
+
+ local_flags = process_options(argc, argv, local_flags);
+
+ setup_logging("smbpasswd", True);
+
+ /*
+ * Set the machine NETBIOS name if not already
+ * set from the config file.
+ */
+
+ if (!init_names())
+ return 1;
+
+ /* Check the effective uid - make sure we are not setuid */
+ if (is_setuid_root()) {
+ fprintf(stderr, "smbpasswd must *NOT* be setuid root.\n");
+ exit(1);
+ }
+
+ if (local_flags & LOCAL_AM_ROOT) {
+ secrets_init();
+ return process_root(local_flags);
+ }
+
+ return process_nonroot(local_flags);
+}
diff --git a/source/utils/smbtree.c b/source/utils/smbtree.c
new file mode 100644
index 00000000000..cbe1bd448f8
--- /dev/null
+++ b/source/utils/smbtree.c
@@ -0,0 +1,237 @@
+/*
+ Unix SMB/CIFS implementation.
+ Network neighbourhood browser.
+
+ Copyright (C) Tim Potter 2000
+ 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 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"
+
+static BOOL use_bcast;
+
+/* How low can we go? */
+
+enum tree_level {LEV_WORKGROUP, LEV_SERVER, LEV_SHARE};
+static enum tree_level level = LEV_SHARE;
+
+/* Holds a list of workgroups or servers */
+
+struct name_list {
+ struct name_list *prev, *next;
+ pstring name, comment;
+ uint32 server_type;
+};
+
+static struct name_list *workgroups, *servers, *shares;
+
+static void free_name_list(struct name_list *list)
+{
+ while(list)
+ DLIST_REMOVE(list, list);
+}
+
+static void add_name(const char *machine_name, uint32 server_type,
+ const char *comment, void *state)
+{
+ struct name_list **name_list = (struct name_list **)state;
+ struct name_list *new_name;
+
+ new_name = (struct name_list *)malloc(sizeof(struct name_list));
+
+ if (!new_name)
+ return;
+
+ ZERO_STRUCTP(new_name);
+
+ pstrcpy(new_name->name, machine_name);
+ pstrcpy(new_name->comment, comment);
+ new_name->server_type = server_type;
+
+ DLIST_ADD(*name_list, new_name);
+}
+
+/****************************************************************************
+ display tree of smb workgroups, servers and shares
+****************************************************************************/
+static BOOL get_workgroups(struct user_auth_info *user_info)
+{
+ struct cli_state *cli;
+ struct in_addr server_ip;
+ pstring master_workgroup;
+
+ /* Try to connect to a #1d name of our current workgroup. If that
+ doesn't work broadcast for a master browser and then jump off
+ that workgroup. */
+
+ pstrcpy(master_workgroup, lp_workgroup());
+
+ if (!use_bcast && !find_master_ip(lp_workgroup(), &server_ip)) {
+ DEBUG(4, ("Unable to find master browser for workgroup %s, falling back to broadcast\n",
+ master_workgroup));
+ use_bcast = True;
+ } else if(!use_bcast) {
+ if (!(cli = get_ipc_connect(inet_ntoa(server_ip), &server_ip, user_info)))
+ return False;
+ }
+
+ if (!(cli = get_ipc_connect_master_ip_bcast(master_workgroup, user_info))) {
+ DEBUG(4, ("Unable to find master browser by "
+ "broadcast\n"));
+ return False;
+ }
+
+ if (!cli_NetServerEnum(cli, master_workgroup,
+ SV_TYPE_DOMAIN_ENUM, add_name, &workgroups))
+ return False;
+
+ return True;
+}
+
+/* Retrieve the list of servers for a given workgroup */
+
+static BOOL get_servers(char *workgroup, struct user_auth_info *user_info)
+{
+ struct cli_state *cli;
+ struct in_addr server_ip;
+
+ /* Open an IPC$ connection to the master browser for the workgroup */
+
+ if (!find_master_ip(workgroup, &server_ip)) {
+ DEBUG(4, ("Cannot find master browser for workgroup %s\n",
+ workgroup));
+ return False;
+ }
+
+ if (!(cli = get_ipc_connect(inet_ntoa(server_ip), &server_ip, user_info)))
+ return False;
+
+ if (!cli_NetServerEnum(cli, workgroup, SV_TYPE_ALL, add_name,
+ &servers))
+ return False;
+
+ return True;
+}
+
+static BOOL get_shares(char *server_name, struct user_auth_info *user_info)
+{
+ struct cli_state *cli;
+
+ if (!(cli = get_ipc_connect(server_name, NULL, user_info)))
+ return False;
+
+ if (!cli_RNetShareEnum(cli, add_name, &shares))
+ return False;
+
+ return True;
+}
+
+static BOOL print_tree(struct user_auth_info *user_info)
+{
+ struct name_list *wg, *sv, *sh;
+
+ /* List workgroups */
+
+ if (!get_workgroups(user_info))
+ return False;
+
+ for (wg = workgroups; wg; wg = wg->next) {
+
+ printf("%s\n", wg->name);
+
+ /* List servers */
+
+ free_name_list(servers);
+ servers = NULL;
+
+ if (level == LEV_WORKGROUP ||
+ !get_servers(wg->name, user_info))
+ continue;
+
+ for (sv = servers; sv; sv = sv->next) {
+
+ printf("\t\\\\%-15s\t\t%s\n",
+ sv->name, sv->comment);
+
+ /* List shares */
+
+ free_name_list(shares);
+ shares = NULL;
+
+ if (level == LEV_SERVER ||
+ !get_shares(sv->name, user_info))
+ continue;
+
+ for (sh = shares; sh; sh = sh->next) {
+ printf("\t\t\\\\%s\\%-15s\t%s\n",
+ sv->name, sh->name, sh->comment);
+ }
+ }
+ }
+
+ return True;
+}
+
+/****************************************************************************
+ main program
+****************************************************************************/
+ int main(int argc,char *argv[])
+{
+ struct poptOption long_options[] = {
+ POPT_AUTOHELP
+ { "broadcast", 'b', POPT_ARG_VAL, &use_bcast, True, "Use broadcast instead of using the master browser" },
+ { "domains", 'D', POPT_ARG_VAL, &level, LEV_WORKGROUP, "List only domains (workgroups) of tree" },
+ { "servers", 'S', POPT_ARG_VAL, &level, LEV_SERVER, "List domains(workgroups) and servers of tree" },
+ POPT_COMMON_SAMBA
+ POPT_COMMON_CREDENTIALS
+ POPT_TABLEEND
+ };
+ poptContext pc;
+
+ /* Initialise samba stuff */
+
+ setlinebuf(stdout);
+
+ dbf = x_stderr;
+
+ setup_logging(argv[0],True);
+
+ pc = poptGetContext("smbtree", argc, (const char **)argv, long_options,
+ POPT_CONTEXT_KEEP_FIRST);
+ while(poptGetNextOpt(pc) != -1);
+ poptFreeContext(pc);
+
+ lp_load(dyn_CONFIGFILE,True,False,False);
+ load_interfaces();
+
+ /* Parse command line args */
+
+ if (!cmdline_auth_info.got_pass) {
+ char *pass = getpass("Password: ");
+ if (pass) {
+ pstrcpy(cmdline_auth_info.password, pass);
+ }
+ cmdline_auth_info.got_pass = True;
+ }
+
+ /* Now do our stuff */
+
+ if (!print_tree(&cmdline_auth_info))
+ return 1;
+
+ return 0;
+}
diff --git a/source/utils/smbw_sample.c b/source/utils/smbw_sample.c
new file mode 100644
index 00000000000..5cd792df7a2
--- /dev/null
+++ b/source/utils/smbw_sample.c
@@ -0,0 +1,94 @@
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <dirent.h>
+#include <sys/stat.h>
+
+static void usage(void)
+{
+ printf("
+smbw_sample - a sample program that uses smbw
+
+smbw_sample <options> path
+
+ options:
+ -W workgroup
+ -l logfile
+ -P prefix
+ -d debuglevel
+ -U username%%password
+ -R resolve order
+
+note that path must start with /smb/
+");
+}
+
+int main(int argc, char *argv[])
+{
+ DIR *dir;
+ struct dirent *dent;
+ int opt;
+ char *p;
+ extern char *optarg;
+ extern int optind;
+ char *path;
+
+ lp_load(dyn_CONFIGFILE,1,0,0);
+ smbw_setup_shared();
+
+ while ((opt = getopt(argc, argv, "W:U:R:d:P:l:hL:")) != EOF) {
+ switch (opt) {
+ case 'W':
+ smbw_setshared("WORKGROUP", optarg);
+ break;
+ case 'l':
+ smbw_setshared("LOGFILE", optarg);
+ break;
+ case 'P':
+ smbw_setshared("PREFIX", optarg);
+ break;
+ case 'd':
+ smbw_setshared("DEBUG", optarg);
+ break;
+ case 'U':
+ p = strchr_m(optarg,'%');
+ if (p) {
+ *p=0;
+ smbw_setshared("PASSWORD",p+1);
+ }
+ smbw_setshared("USER", optarg);
+ break;
+ case 'R':
+ smbw_setshared("RESOLVE_ORDER",optarg);
+ break;
+ case 'h':
+ default:
+ usage();
+ exit(1);
+ }
+ }
+
+ argc -= optind;
+ argv += optind;
+
+ if (argc < 1) {
+ usage();
+ exit(1);
+ }
+
+ path = argv[0];
+
+ smbw_init();
+
+ dir = smbw_opendir(path);
+ if (!dir) {
+ printf("failed to open %s\n", path);
+ exit(1);
+ }
+
+ while ((dent = smbw_readdir(dir))) {
+ printf("%s\n", dent->d_name);
+ }
+ smbw_closedir(dir);
+ return 0;
+}
diff --git a/source/utils/status.c b/source/utils/status.c
new file mode 100644
index 00000000000..4585b101b28
--- /dev/null
+++ b/source/utils/status.c
@@ -0,0 +1,693 @@
+/*
+ Unix SMB/CIFS implementation.
+ status reporting
+ Copyright (C) Andrew Tridgell 1994-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 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.
+
+ Revision History:
+
+ 12 aug 96: Erik.Devriendt@te6.siemens.be
+ added support for shared memory implementation of share mode locking
+
+ 21-Jul-1998: rsharpe@ns.aus.com (Richard Sharpe)
+ Added -L (locks only) -S (shares only) flags and code
+
+*/
+
+/*
+ * This program reports current SMB connections
+ */
+
+#define NO_SYSLOG
+
+#include "includes.h"
+
+#define SMB_MAXPIDS 2048
+static pstring Ucrit_username = ""; /* added by OH */
+static pid_t Ucrit_pid[SMB_MAXPIDS]; /* Ugly !!! */ /* added by OH */
+static int Ucrit_MaxPid=0; /* added by OH */
+static unsigned int Ucrit_IsActive = 0; /* added by OH */
+
+static int verbose, brief;
+static int shares_only = 0; /* Added by RJS */
+static int locks_only = 0; /* Added by RJS */
+static BOOL processes_only=False;
+static int show_brl;
+
+/* added by OH */
+static void Ucrit_addUsername(const char *username)
+{
+ pstrcpy(Ucrit_username, username);
+
+ if ( strlen(Ucrit_username) > 0 )
+ Ucrit_IsActive = 1;
+}
+
+static unsigned int Ucrit_checkUsername(const char *username)
+{
+ if ( !Ucrit_IsActive )
+ return 1;
+
+ if ( strcmp(Ucrit_username,username) == 0 )
+ return 1;
+
+ return 0;
+}
+
+static unsigned int Ucrit_checkPid(pid_t pid)
+{
+ int i;
+
+ if ( !Ucrit_IsActive )
+ return 1;
+
+ for (i=0;i<Ucrit_MaxPid;i++) {
+ if( pid == Ucrit_pid[i] )
+ return 1;
+ }
+
+ return 0;
+}
+
+static BOOL Ucrit_addPid( pid_t pid )
+{
+ if ( !Ucrit_IsActive )
+ return True;
+
+ if ( Ucrit_MaxPid >= SMB_MAXPIDS ) {
+ d_printf("ERROR: More than %d pids for user %s!\n",
+ SMB_MAXPIDS, Ucrit_username);
+
+ return False;
+ }
+
+ Ucrit_pid[Ucrit_MaxPid++] = pid;
+
+ return True;
+}
+
+static void print_share_mode(share_mode_entry *e, char *fname)
+{
+ static int count;
+ if (count==0) {
+ d_printf("Locked files:\n");
+ d_printf("Pid DenyMode Access R/W Oplock Name\n");
+ d_printf("--------------------------------------------------------------\n");
+ }
+ count++;
+
+ if (Ucrit_checkPid(e->pid)) {
+ d_printf("%-5d ",(int)e->pid);
+ switch (GET_DENY_MODE(e->share_mode)) {
+ case DENY_NONE: d_printf("DENY_NONE "); break;
+ case DENY_ALL: d_printf("DENY_ALL "); break;
+ case DENY_DOS: d_printf("DENY_DOS "); break;
+ case DENY_READ: d_printf("DENY_READ "); break;
+ case DENY_WRITE:printf("DENY_WRITE "); break;
+ case DENY_FCB: d_printf("DENY_FCB "); break;
+ }
+ d_printf("0x%-8x ",(unsigned int)e->desired_access);
+ switch (e->share_mode&0xF) {
+ case 0: d_printf("RDONLY "); break;
+ case 1: d_printf("WRONLY "); break;
+ case 2: d_printf("RDWR "); break;
+ }
+
+ if((e->op_type &
+ (EXCLUSIVE_OPLOCK|BATCH_OPLOCK)) ==
+ (EXCLUSIVE_OPLOCK|BATCH_OPLOCK))
+ d_printf("EXCLUSIVE+BATCH ");
+ else if (e->op_type & EXCLUSIVE_OPLOCK)
+ d_printf("EXCLUSIVE ");
+ else if (e->op_type & BATCH_OPLOCK)
+ d_printf("BATCH ");
+ else if (e->op_type & LEVEL_II_OPLOCK)
+ d_printf("LEVEL_II ");
+ else
+ d_printf("NONE ");
+
+ d_printf(" %s %s",fname,
+ asctime(LocalTime((time_t *)&e->time.tv_sec)));
+ }
+}
+
+static void print_brl(SMB_DEV_T dev, SMB_INO_T ino, int pid,
+ enum brl_type lock_type,
+ br_off start, br_off size)
+{
+ static int count;
+ if (count==0) {
+ d_printf("Byte range locks:\n");
+ d_printf(" Pid dev:inode R/W start size\n");
+ d_printf("------------------------------------------------\n");
+ }
+ count++;
+
+ d_printf("%6d %05x:%05x %s %9.0f %9.0f\n",
+ (int)pid, (int)dev, (int)ino,
+ lock_type==READ_LOCK?"R":"W",
+ (double)start, (double)size);
+}
+
+
+/*******************************************************************
+ dump the elements of the profile structure
+ ******************************************************************/
+static int profile_dump(void)
+{
+#ifdef WITH_PROFILE
+ if (!profile_setup(True)) {
+ fprintf(stderr,"Failed to initialise profile memory\n");
+ return -1;
+ }
+
+ d_printf("smb_count: %u\n", profile_p->smb_count);
+ d_printf("uid_changes: %u\n", profile_p->uid_changes);
+ d_printf("************************ System Calls ****************************\n");
+ d_printf("opendir_count: %u\n", profile_p->syscall_opendir_count);
+ d_printf("opendir_time: %u\n", profile_p->syscall_opendir_time);
+ d_printf("readdir_count: %u\n", profile_p->syscall_readdir_count);
+ d_printf("readdir_time: %u\n", profile_p->syscall_readdir_time);
+ d_printf("mkdir_count: %u\n", profile_p->syscall_mkdir_count);
+ d_printf("mkdir_time: %u\n", profile_p->syscall_mkdir_time);
+ d_printf("rmdir_count: %u\n", profile_p->syscall_rmdir_count);
+ d_printf("rmdir_time: %u\n", profile_p->syscall_rmdir_time);
+ d_printf("closedir_count: %u\n", profile_p->syscall_closedir_count);
+ d_printf("closedir_time: %u\n", profile_p->syscall_closedir_time);
+ d_printf("open_count: %u\n", profile_p->syscall_open_count);
+ d_printf("open_time: %u\n", profile_p->syscall_open_time);
+ d_printf("close_count: %u\n", profile_p->syscall_close_count);
+ d_printf("close_time: %u\n", profile_p->syscall_close_time);
+ d_printf("read_count: %u\n", profile_p->syscall_read_count);
+ d_printf("read_time: %u\n", profile_p->syscall_read_time);
+ d_printf("read_bytes: %u\n", profile_p->syscall_read_bytes);
+ d_printf("write_count: %u\n", profile_p->syscall_write_count);
+ d_printf("write_time: %u\n", profile_p->syscall_write_time);
+ d_printf("write_bytes: %u\n", profile_p->syscall_write_bytes);
+ d_printf("pread_count: %u\n", profile_p->syscall_pread_count);
+ d_printf("pread_time: %u\n", profile_p->syscall_pread_time);
+ d_printf("pread_bytes: %u\n", profile_p->syscall_pread_bytes);
+ d_printf("pwrite_count: %u\n", profile_p->syscall_pwrite_count);
+ d_printf("pwrite_time: %u\n", profile_p->syscall_pwrite_time);
+ d_printf("pwrite_bytes: %u\n", profile_p->syscall_pwrite_bytes);
+#ifdef WITH_SENDFILE
+ d_printf("sendfile_count: %u\n", profile_p->syscall_sendfile_count);
+ d_printf("sendfile_time: %u\n", profile_p->syscall_sendfile_time);
+ d_printf("sendfile_bytes: %u\n", profile_p->syscall_sendfile_bytes);
+#endif
+ d_printf("lseek_count: %u\n", profile_p->syscall_lseek_count);
+ d_printf("lseek_time: %u\n", profile_p->syscall_lseek_time);
+ d_printf("rename_count: %u\n", profile_p->syscall_rename_count);
+ d_printf("rename_time: %u\n", profile_p->syscall_rename_time);
+ d_printf("fsync_count: %u\n", profile_p->syscall_fsync_count);
+ d_printf("fsync_time: %u\n", profile_p->syscall_fsync_time);
+ d_printf("stat_count: %u\n", profile_p->syscall_stat_count);
+ d_printf("stat_time: %u\n", profile_p->syscall_stat_time);
+ d_printf("fstat_count: %u\n", profile_p->syscall_fstat_count);
+ d_printf("fstat_time: %u\n", profile_p->syscall_fstat_time);
+ d_printf("lstat_count: %u\n", profile_p->syscall_lstat_count);
+ d_printf("lstat_time: %u\n", profile_p->syscall_lstat_time);
+ d_printf("unlink_count: %u\n", profile_p->syscall_unlink_count);
+ d_printf("unlink_time: %u\n", profile_p->syscall_unlink_time);
+ d_printf("chmod_count: %u\n", profile_p->syscall_chmod_count);
+ d_printf("chmod_time: %u\n", profile_p->syscall_chmod_time);
+ d_printf("fchmod_count: %u\n", profile_p->syscall_fchmod_count);
+ d_printf("fchmod_time: %u\n", profile_p->syscall_fchmod_time);
+ d_printf("chown_count: %u\n", profile_p->syscall_chown_count);
+ d_printf("chown_time: %u\n", profile_p->syscall_chown_time);
+ d_printf("fchown_count: %u\n", profile_p->syscall_fchown_count);
+ d_printf("fchown_time: %u\n", profile_p->syscall_fchown_time);
+ d_printf("chdir_count: %u\n", profile_p->syscall_chdir_count);
+ d_printf("chdir_time: %u\n", profile_p->syscall_chdir_time);
+ d_printf("getwd_count: %u\n", profile_p->syscall_getwd_count);
+ d_printf("getwd_time: %u\n", profile_p->syscall_getwd_time);
+ d_printf("utime_count: %u\n", profile_p->syscall_utime_count);
+ d_printf("utime_time: %u\n", profile_p->syscall_utime_time);
+ d_printf("ftruncate_count: %u\n", profile_p->syscall_ftruncate_count);
+ d_printf("ftruncate_time: %u\n", profile_p->syscall_ftruncate_time);
+ d_printf("fcntl_lock_count: %u\n", profile_p->syscall_fcntl_lock_count);
+ d_printf("fcntl_lock_time: %u\n", profile_p->syscall_fcntl_lock_time);
+ d_printf("readlink_count: %u\n", profile_p->syscall_readlink_count);
+ d_printf("readlink_time: %u\n", profile_p->syscall_readlink_time);
+ d_printf("symlink_count: %u\n", profile_p->syscall_symlink_count);
+ d_printf("symlink_time: %u\n", profile_p->syscall_symlink_time);
+ d_printf("************************ Statcache *******************************\n");
+ d_printf("lookups: %u\n", profile_p->statcache_lookups);
+ d_printf("misses: %u\n", profile_p->statcache_misses);
+ d_printf("hits: %u\n", profile_p->statcache_hits);
+ d_printf("************************ Writecache ******************************\n");
+ d_printf("read_hits: %u\n", profile_p->writecache_read_hits);
+ d_printf("abutted_writes: %u\n", profile_p->writecache_abutted_writes);
+ d_printf("total_writes: %u\n", profile_p->writecache_total_writes);
+ d_printf("non_oplock_writes: %u\n", profile_p->writecache_non_oplock_writes);
+ d_printf("direct_writes: %u\n", profile_p->writecache_direct_writes);
+ d_printf("init_writes: %u\n", profile_p->writecache_init_writes);
+ d_printf("flushed_writes[SEEK]: %u\n", profile_p->writecache_flushed_writes[SEEK_FLUSH]);
+ d_printf("flushed_writes[READ]: %u\n", profile_p->writecache_flushed_writes[READ_FLUSH]);
+ d_printf("flushed_writes[WRITE]: %u\n", profile_p->writecache_flushed_writes[WRITE_FLUSH]);
+ d_printf("flushed_writes[READRAW]: %u\n", profile_p->writecache_flushed_writes[READRAW_FLUSH]);
+ d_printf("flushed_writes[OPLOCK_RELEASE]: %u\n", profile_p->writecache_flushed_writes[OPLOCK_RELEASE_FLUSH]);
+ d_printf("flushed_writes[CLOSE]: %u\n", profile_p->writecache_flushed_writes[CLOSE_FLUSH]);
+ d_printf("flushed_writes[SYNC]: %u\n", profile_p->writecache_flushed_writes[SYNC_FLUSH]);
+ d_printf("flushed_writes[SIZECHANGE]: %u\n", profile_p->writecache_flushed_writes[SIZECHANGE_FLUSH]);
+ d_printf("num_perfect_writes: %u\n", profile_p->writecache_num_perfect_writes);
+ d_printf("num_write_caches: %u\n", profile_p->writecache_num_write_caches);
+ d_printf("allocated_write_caches: %u\n", profile_p->writecache_allocated_write_caches);
+ d_printf("************************ SMB Calls *******************************\n");
+ d_printf("mkdir_count: %u\n", profile_p->SMBmkdir_count);
+ d_printf("mkdir_time: %u\n", profile_p->SMBmkdir_time);
+ d_printf("rmdir_count: %u\n", profile_p->SMBrmdir_count);
+ d_printf("rmdir_time: %u\n", profile_p->SMBrmdir_time);
+ d_printf("open_count: %u\n", profile_p->SMBopen_count);
+ d_printf("open_time: %u\n", profile_p->SMBopen_time);
+ d_printf("create_count: %u\n", profile_p->SMBcreate_count);
+ d_printf("create_time: %u\n", profile_p->SMBcreate_time);
+ d_printf("close_count: %u\n", profile_p->SMBclose_count);
+ d_printf("close_time: %u\n", profile_p->SMBclose_time);
+ d_printf("flush_count: %u\n", profile_p->SMBflush_count);
+ d_printf("flush_time: %u\n", profile_p->SMBflush_time);
+ d_printf("unlink_count: %u\n", profile_p->SMBunlink_count);
+ d_printf("unlink_time: %u\n", profile_p->SMBunlink_time);
+ d_printf("mv_count: %u\n", profile_p->SMBmv_count);
+ d_printf("mv_time: %u\n", profile_p->SMBmv_time);
+ d_printf("getatr_count: %u\n", profile_p->SMBgetatr_count);
+ d_printf("getatr_time: %u\n", profile_p->SMBgetatr_time);
+ d_printf("setatr_count: %u\n", profile_p->SMBsetatr_count);
+ d_printf("setatr_time: %u\n", profile_p->SMBsetatr_time);
+ d_printf("read_count: %u\n", profile_p->SMBread_count);
+ d_printf("read_time: %u\n", profile_p->SMBread_time);
+ d_printf("write_count: %u\n", profile_p->SMBwrite_count);
+ d_printf("write_time: %u\n", profile_p->SMBwrite_time);
+ d_printf("lock_count: %u\n", profile_p->SMBlock_count);
+ d_printf("lock_time: %u\n", profile_p->SMBlock_time);
+ d_printf("unlock_count: %u\n", profile_p->SMBunlock_count);
+ d_printf("unlock_time: %u\n", profile_p->SMBunlock_time);
+ d_printf("ctemp_count: %u\n", profile_p->SMBctemp_count);
+ d_printf("ctemp_time: %u\n", profile_p->SMBctemp_time);
+ d_printf("mknew_count: %u\n", profile_p->SMBmknew_count);
+ d_printf("mknew_time: %u\n", profile_p->SMBmknew_time);
+ d_printf("chkpth_count: %u\n", profile_p->SMBchkpth_count);
+ d_printf("chkpth_time: %u\n", profile_p->SMBchkpth_time);
+ d_printf("exit_count: %u\n", profile_p->SMBexit_count);
+ d_printf("exit_time: %u\n", profile_p->SMBexit_time);
+ d_printf("lseek_count: %u\n", profile_p->SMBlseek_count);
+ d_printf("lseek_time: %u\n", profile_p->SMBlseek_time);
+ d_printf("lockread_count: %u\n", profile_p->SMBlockread_count);
+ d_printf("lockread_time: %u\n", profile_p->SMBlockread_time);
+ d_printf("writeunlock_count: %u\n", profile_p->SMBwriteunlock_count);
+ d_printf("writeunlock_time: %u\n", profile_p->SMBwriteunlock_time);
+ d_printf("readbraw_count: %u\n", profile_p->SMBreadbraw_count);
+ d_printf("readbraw_time: %u\n", profile_p->SMBreadbraw_time);
+ d_printf("readBmpx_count: %u\n", profile_p->SMBreadBmpx_count);
+ d_printf("readBmpx_time: %u\n", profile_p->SMBreadBmpx_time);
+ d_printf("readBs_count: %u\n", profile_p->SMBreadBs_count);
+ d_printf("readBs_time: %u\n", profile_p->SMBreadBs_time);
+ d_printf("writebraw_count: %u\n", profile_p->SMBwritebraw_count);
+ d_printf("writebraw_time: %u\n", profile_p->SMBwritebraw_time);
+ d_printf("writeBmpx_count: %u\n", profile_p->SMBwriteBmpx_count);
+ d_printf("writeBmpx_time: %u\n", profile_p->SMBwriteBmpx_time);
+ d_printf("writeBs_count: %u\n", profile_p->SMBwriteBs_count);
+ d_printf("writeBs_time: %u\n", profile_p->SMBwriteBs_time);
+ d_printf("writec_count: %u\n", profile_p->SMBwritec_count);
+ d_printf("writec_time: %u\n", profile_p->SMBwritec_time);
+ d_printf("setattrE_count: %u\n", profile_p->SMBsetattrE_count);
+ d_printf("setattrE_time: %u\n", profile_p->SMBsetattrE_time);
+ d_printf("getattrE_count: %u\n", profile_p->SMBgetattrE_count);
+ d_printf("getattrE_time: %u\n", profile_p->SMBgetattrE_time);
+ d_printf("lockingX_count: %u\n", profile_p->SMBlockingX_count);
+ d_printf("lockingX_time: %u\n", profile_p->SMBlockingX_time);
+ d_printf("trans_count: %u\n", profile_p->SMBtrans_count);
+ d_printf("trans_time: %u\n", profile_p->SMBtrans_time);
+ d_printf("transs_count: %u\n", profile_p->SMBtranss_count);
+ d_printf("transs_time: %u\n", profile_p->SMBtranss_time);
+ d_printf("ioctl_count: %u\n", profile_p->SMBioctl_count);
+ d_printf("ioctl_time: %u\n", profile_p->SMBioctl_time);
+ d_printf("ioctls_count: %u\n", profile_p->SMBioctls_count);
+ d_printf("ioctls_time: %u\n", profile_p->SMBioctls_time);
+ d_printf("copy_count: %u\n", profile_p->SMBcopy_count);
+ d_printf("copy_time: %u\n", profile_p->SMBcopy_time);
+ d_printf("move_count: %u\n", profile_p->SMBmove_count);
+ d_printf("move_time: %u\n", profile_p->SMBmove_time);
+ d_printf("echo_count: %u\n", profile_p->SMBecho_count);
+ d_printf("echo_time: %u\n", profile_p->SMBecho_time);
+ d_printf("writeclose_count: %u\n", profile_p->SMBwriteclose_count);
+ d_printf("writeclose_time: %u\n", profile_p->SMBwriteclose_time);
+ d_printf("openX_count: %u\n", profile_p->SMBopenX_count);
+ d_printf("openX_time: %u\n", profile_p->SMBopenX_time);
+ d_printf("readX_count: %u\n", profile_p->SMBreadX_count);
+ d_printf("readX_time: %u\n", profile_p->SMBreadX_time);
+ d_printf("writeX_count: %u\n", profile_p->SMBwriteX_count);
+ d_printf("writeX_time: %u\n", profile_p->SMBwriteX_time);
+ d_printf("trans2_count: %u\n", profile_p->SMBtrans2_count);
+ d_printf("trans2_time: %u\n", profile_p->SMBtrans2_time);
+ d_printf("transs2_count: %u\n", profile_p->SMBtranss2_count);
+ d_printf("transs2_time: %u\n", profile_p->SMBtranss2_time);
+ d_printf("findclose_count: %u\n", profile_p->SMBfindclose_count);
+ d_printf("findclose_time: %u\n", profile_p->SMBfindclose_time);
+ d_printf("findnclose_count: %u\n", profile_p->SMBfindnclose_count);
+ d_printf("findnclose_time: %u\n", profile_p->SMBfindnclose_time);
+ d_printf("tcon_count: %u\n", profile_p->SMBtcon_count);
+ d_printf("tcon_time: %u\n", profile_p->SMBtcon_time);
+ d_printf("tdis_count: %u\n", profile_p->SMBtdis_count);
+ d_printf("tdis_time: %u\n", profile_p->SMBtdis_time);
+ d_printf("negprot_count: %u\n", profile_p->SMBnegprot_count);
+ d_printf("negprot_time: %u\n", profile_p->SMBnegprot_time);
+ d_printf("sesssetupX_count: %u\n", profile_p->SMBsesssetupX_count);
+ d_printf("sesssetupX_time: %u\n", profile_p->SMBsesssetupX_time);
+ d_printf("ulogoffX_count: %u\n", profile_p->SMBulogoffX_count);
+ d_printf("ulogoffX_time: %u\n", profile_p->SMBulogoffX_time);
+ d_printf("tconX_count: %u\n", profile_p->SMBtconX_count);
+ d_printf("tconX_time: %u\n", profile_p->SMBtconX_time);
+ d_printf("dskattr_count: %u\n", profile_p->SMBdskattr_count);
+ d_printf("dskattr_time: %u\n", profile_p->SMBdskattr_time);
+ d_printf("search_count: %u\n", profile_p->SMBsearch_count);
+ d_printf("search_time: %u\n", profile_p->SMBsearch_time);
+ d_printf("ffirst_count: %u\n", profile_p->SMBffirst_count);
+ d_printf("ffirst_time: %u\n", profile_p->SMBffirst_time);
+ d_printf("funique_count: %u\n", profile_p->SMBfunique_count);
+ d_printf("funique_time: %u\n", profile_p->SMBfunique_time);
+ d_printf("fclose_count: %u\n", profile_p->SMBfclose_count);
+ d_printf("fclose_time: %u\n", profile_p->SMBfclose_time);
+ d_printf("nttrans_count: %u\n", profile_p->SMBnttrans_count);
+ d_printf("nttrans_time: %u\n", profile_p->SMBnttrans_time);
+ d_printf("nttranss_count: %u\n", profile_p->SMBnttranss_count);
+ d_printf("nttranss_time: %u\n", profile_p->SMBnttranss_time);
+ d_printf("ntcreateX_count: %u\n", profile_p->SMBntcreateX_count);
+ d_printf("ntcreateX_time: %u\n", profile_p->SMBntcreateX_time);
+ d_printf("ntcancel_count: %u\n", profile_p->SMBntcancel_count);
+ d_printf("ntcancel_time: %u\n", profile_p->SMBntcancel_time);
+ d_printf("splopen_count: %u\n", profile_p->SMBsplopen_count);
+ d_printf("splopen_time: %u\n", profile_p->SMBsplopen_time);
+ d_printf("splwr_count: %u\n", profile_p->SMBsplwr_count);
+ d_printf("splwr_time: %u\n", profile_p->SMBsplwr_time);
+ d_printf("splclose_count: %u\n", profile_p->SMBsplclose_count);
+ d_printf("splclose_time: %u\n", profile_p->SMBsplclose_time);
+ d_printf("splretq_count: %u\n", profile_p->SMBsplretq_count);
+ d_printf("splretq_time: %u\n", profile_p->SMBsplretq_time);
+ d_printf("sends_count: %u\n", profile_p->SMBsends_count);
+ d_printf("sends_time: %u\n", profile_p->SMBsends_time);
+ d_printf("sendb_count: %u\n", profile_p->SMBsendb_count);
+ d_printf("sendb_time: %u\n", profile_p->SMBsendb_time);
+ d_printf("fwdname_count: %u\n", profile_p->SMBfwdname_count);
+ d_printf("fwdname_time: %u\n", profile_p->SMBfwdname_time);
+ d_printf("cancelf_count: %u\n", profile_p->SMBcancelf_count);
+ d_printf("cancelf_time: %u\n", profile_p->SMBcancelf_time);
+ d_printf("getmac_count: %u\n", profile_p->SMBgetmac_count);
+ d_printf("getmac_time: %u\n", profile_p->SMBgetmac_time);
+ d_printf("sendstrt_count: %u\n", profile_p->SMBsendstrt_count);
+ d_printf("sendstrt_time: %u\n", profile_p->SMBsendstrt_time);
+ d_printf("sendend_count: %u\n", profile_p->SMBsendend_count);
+ d_printf("sendend_time: %u\n", profile_p->SMBsendend_time);
+ d_printf("sendtxt_count: %u\n", profile_p->SMBsendtxt_count);
+ d_printf("sendtxt_time: %u\n", profile_p->SMBsendtxt_time);
+ d_printf("invalid_count: %u\n", profile_p->SMBinvalid_count);
+ d_printf("invalid_time: %u\n", profile_p->SMBinvalid_time);
+ d_printf("************************ Pathworks Calls *************************\n");
+ d_printf("setdir_count: %u\n", profile_p->pathworks_setdir_count);
+ d_printf("setdir_time: %u\n", profile_p->pathworks_setdir_time);
+ d_printf("************************ Trans2 Calls ****************************\n");
+ d_printf("open_count: %u\n", profile_p->Trans2_open_count);
+ d_printf("open_time: %u\n", profile_p->Trans2_open_time);
+ d_printf("findfirst_count: %u\n", profile_p->Trans2_findfirst_count);
+ d_printf("findfirst_time: %u\n", profile_p->Trans2_findfirst_time);
+ d_printf("findnext_count: %u\n", profile_p->Trans2_findnext_count);
+ d_printf("findnext_time: %u\n", profile_p->Trans2_findnext_time);
+ d_printf("qfsinfo_count: %u\n", profile_p->Trans2_qfsinfo_count);
+ d_printf("qfsinfo_time: %u\n", profile_p->Trans2_qfsinfo_time);
+ d_printf("setfsinfo_count: %u\n", profile_p->Trans2_setfsinfo_count);
+ d_printf("setfsinfo_time: %u\n", profile_p->Trans2_setfsinfo_time);
+ d_printf("qpathinfo_count: %u\n", profile_p->Trans2_qpathinfo_count);
+ d_printf("qpathinfo_time: %u\n", profile_p->Trans2_qpathinfo_time);
+ d_printf("setpathinfo_count: %u\n", profile_p->Trans2_setpathinfo_count);
+ d_printf("setpathinfo_time: %u\n", profile_p->Trans2_setpathinfo_time);
+ d_printf("qfileinfo_count: %u\n", profile_p->Trans2_qfileinfo_count);
+ d_printf("qfileinfo_time: %u\n", profile_p->Trans2_qfileinfo_time);
+ d_printf("setfileinfo_count: %u\n", profile_p->Trans2_setfileinfo_count);
+ d_printf("setfileinfo_time: %u\n", profile_p->Trans2_setfileinfo_time);
+ d_printf("fsctl_count: %u\n", profile_p->Trans2_fsctl_count);
+ d_printf("fsctl_time: %u\n", profile_p->Trans2_fsctl_time);
+ d_printf("ioctl_count: %u\n", profile_p->Trans2_ioctl_count);
+ d_printf("ioctl_time: %u\n", profile_p->Trans2_ioctl_time);
+ d_printf("findnotifyfirst_count: %u\n", profile_p->Trans2_findnotifyfirst_count);
+ d_printf("findnotifyfirst_time: %u\n", profile_p->Trans2_findnotifyfirst_time);
+ d_printf("findnotifynext_count: %u\n", profile_p->Trans2_findnotifynext_count);
+ d_printf("findnotifynext_time: %u\n", profile_p->Trans2_findnotifynext_time);
+ d_printf("mkdir_count: %u\n", profile_p->Trans2_mkdir_count);
+ d_printf("mkdir_time: %u\n", profile_p->Trans2_mkdir_time);
+ d_printf("session_setup_count: %u\n", profile_p->Trans2_session_setup_count);
+ d_printf("session_setup_time: %u\n", profile_p->Trans2_session_setup_time);
+ d_printf("get_dfs_referral_count: %u\n", profile_p->Trans2_get_dfs_referral_count);
+ d_printf("get_dfs_referral_time: %u\n", profile_p->Trans2_get_dfs_referral_time);
+ d_printf("report_dfs_inconsistancy_count: %u\n", profile_p->Trans2_report_dfs_inconsistancy_count);
+ d_printf("report_dfs_inconsistancy_time: %u\n", profile_p->Trans2_report_dfs_inconsistancy_time);
+ d_printf("************************ NT Transact Calls ***********************\n");
+ d_printf("create_count: %u\n", profile_p->NT_transact_create_count);
+ d_printf("create_time: %u\n", profile_p->NT_transact_create_time);
+ d_printf("ioctl_count: %u\n", profile_p->NT_transact_ioctl_count);
+ d_printf("ioctl_time: %u\n", profile_p->NT_transact_ioctl_time);
+ d_printf("set_security_desc_count: %u\n", profile_p->NT_transact_set_security_desc_count);
+ d_printf("set_security_desc_time: %u\n", profile_p->NT_transact_set_security_desc_time);
+ d_printf("notify_change_count: %u\n", profile_p->NT_transact_notify_change_count);
+ d_printf("notify_change_time: %u\n", profile_p->NT_transact_notify_change_time);
+ d_printf("rename_count: %u\n", profile_p->NT_transact_rename_count);
+ d_printf("rename_time: %u\n", profile_p->NT_transact_rename_time);
+ d_printf("query_security_desc_count: %u\n", profile_p->NT_transact_query_security_desc_count);
+ d_printf("query_security_desc_time: %u\n", profile_p->NT_transact_query_security_desc_time);
+ d_printf("************************ ACL Calls *******************************\n");
+ d_printf("get_nt_acl_count: %u\n", profile_p->get_nt_acl_count);
+ d_printf("get_nt_acl_time: %u\n", profile_p->get_nt_acl_time);
+ d_printf("fget_nt_acl_count: %u\n", profile_p->fget_nt_acl_count);
+ d_printf("fget_nt_acl_time: %u\n", profile_p->fget_nt_acl_time);
+ d_printf("set_nt_acl_count: %u\n", profile_p->set_nt_acl_count);
+ d_printf("set_nt_acl_time: %u\n", profile_p->set_nt_acl_time);
+ d_printf("fset_nt_acl_count: %u\n", profile_p->fset_nt_acl_count);
+ d_printf("fset_nt_acl_time: %u\n", profile_p->fset_nt_acl_time);
+ d_printf("chmod_acl_count: %u\n", profile_p->chmod_acl_count);
+ d_printf("chmod_acl_time: %u\n", profile_p->chmod_acl_time);
+ d_printf("fchmod_acl_count: %u\n", profile_p->fchmod_acl_count);
+ d_printf("fchmod_acl_time: %u\n", profile_p->fchmod_acl_time);
+ d_printf("************************ NMBD Calls ****************************\n");
+ d_printf("name_release_count: %u\n", profile_p->name_release_count);
+ d_printf("name_release_time: %u\n", profile_p->name_release_time);
+ d_printf("name_refresh_count: %u\n", profile_p->name_refresh_count);
+ d_printf("name_refresh_time: %u\n", profile_p->name_refresh_time);
+ d_printf("name_registration_count: %u\n", profile_p->name_registration_count);
+ d_printf("name_registration_time: %u\n", profile_p->name_registration_time);
+ d_printf("node_status_count: %u\n", profile_p->node_status_count);
+ d_printf("node_status_time: %u\n", profile_p->node_status_time);
+ d_printf("name_query_count: %u\n", profile_p->name_query_count);
+ d_printf("name_query_time: %u\n", profile_p->name_query_time);
+ d_printf("host_announce_count: %u\n", profile_p->host_announce_count);
+ d_printf("host_announce_time: %u\n", profile_p->host_announce_time);
+ d_printf("workgroup_announce_count: %u\n", profile_p->workgroup_announce_count);
+ d_printf("workgroup_announce_time: %u\n", profile_p->workgroup_announce_time);
+ d_printf("local_master_announce_count: %u\n", profile_p->local_master_announce_count);
+ d_printf("local_master_announce_time: %u\n", profile_p->local_master_announce_time);
+ d_printf("master_browser_announce_count: %u\n", profile_p->master_browser_announce_count);
+ d_printf("master_browser_announce_time: %u\n", profile_p->master_browser_announce_time);
+ d_printf("lm_host_announce_count: %u\n", profile_p->lm_host_announce_count);
+ d_printf("lm_host_announce_time: %u\n", profile_p->lm_host_announce_time);
+ d_printf("get_backup_list_count: %u\n", profile_p->get_backup_list_count);
+ d_printf("get_backup_list_time: %u\n", profile_p->get_backup_list_time);
+ d_printf("reset_browser_count: %u\n", profile_p->reset_browser_count);
+ d_printf("reset_browser_time: %u\n", profile_p->reset_browser_time);
+ d_printf("announce_request_count: %u\n", profile_p->announce_request_count);
+ d_printf("announce_request_time: %u\n", profile_p->announce_request_time);
+ d_printf("lm_announce_request_count: %u\n", profile_p->lm_announce_request_count);
+ d_printf("lm_announce_request_time: %u\n", profile_p->lm_announce_request_time);
+ d_printf("domain_logon_count: %u\n", profile_p->domain_logon_count);
+ d_printf("domain_logon_time: %u\n", profile_p->domain_logon_time);
+ d_printf("sync_browse_lists_count: %u\n", profile_p->sync_browse_lists_count);
+ d_printf("sync_browse_lists_time: %u\n", profile_p->sync_browse_lists_time);
+ d_printf("run_elections_count: %u\n", profile_p->run_elections_count);
+ d_printf("run_elections_time: %u\n", profile_p->run_elections_time);
+ d_printf("election_count: %u\n", profile_p->election_count);
+ d_printf("election_time: %u\n", profile_p->election_time);
+#else /* WITH_PROFILE */
+ fprintf(stderr, "Profile data unavailable\n");
+#endif /* WITH_PROFILE */
+
+ return 0;
+}
+
+
+static int traverse_fn1(TDB_CONTEXT *tdb, TDB_DATA kbuf, TDB_DATA dbuf, void *state)
+{
+ struct connections_data crec;
+
+ if (dbuf.dsize != sizeof(crec))
+ return 0;
+
+ memcpy(&crec, dbuf.dptr, sizeof(crec));
+
+ if (crec.cnum == -1)
+ return 0;
+
+ if (!process_exists(crec.pid) || !Ucrit_checkUsername(uidtoname(crec.uid))) {
+ return 0;
+ }
+
+ d_printf("%-10.10s %5d %-12s %s",
+ crec.name,(int)crec.pid,
+ crec.machine,
+ asctime(LocalTime(&crec.start)));
+
+ return 0;
+}
+
+static int traverse_sessionid(TDB_CONTEXT *tdb, TDB_DATA kbuf, TDB_DATA dbuf, void *state)
+{
+ struct sessionid sessionid;
+
+ if (dbuf.dsize != sizeof(sessionid))
+ return 0;
+
+ memcpy(&sessionid, dbuf.dptr, sizeof(sessionid));
+
+ if (!process_exists(sessionid.pid) || !Ucrit_checkUsername(uidtoname(sessionid.uid))) {
+ return 0;
+ }
+
+ Ucrit_addPid( sessionid.pid );
+
+ d_printf("%5d %-12s %-12s %-12s (%s)\n",
+ (int)sessionid.pid, uidtoname(sessionid.uid), gidtoname(sessionid.gid),
+ sessionid.remote_machine, sessionid.hostname);
+
+ return 0;
+}
+
+
+
+
+ int main(int argc, char *argv[])
+{
+ int c;
+ static int profile_only = 0;
+ TDB_CONTEXT *tdb;
+ poptContext pc;
+ struct poptOption long_options[] = {
+ POPT_AUTOHELP
+ {"processes", 'p', POPT_ARG_NONE, &processes_only, 'p', "Show processes only" },
+ {"verbose", 'v', POPT_ARG_NONE, &verbose, 'v', "Be verbose" },
+ {"locks", 'L', POPT_ARG_NONE, &locks_only, 'L', "Show locks only" },
+ {"shares", 'S', POPT_ARG_NONE, &shares_only, 'S', "Show shares only" },
+ {"user", 'u', POPT_ARG_STRING, 0, 'u', "Switch to user" },
+ {"brief", 'b', POPT_ARG_NONE, &brief, 'b', "Be brief" },
+#ifdef WITH_PROFILE
+ {"profile", 'P', POPT_ARG_NONE, &profile_only, 'P', "Do profiling" },
+#endif /* WITH_PROFILE */
+ {"byterange", 'B', POPT_ARG_NONE, &show_brl, 'B', "Include byte range locks"},
+ POPT_COMMON_SAMBA
+ POPT_TABLEEND
+ };
+
+ setup_logging(argv[0],True);
+
+ dbf = x_stderr;
+
+ if (getuid() != geteuid()) {
+ d_printf("smbstatus should not be run setuid\n");
+ return(1);
+ }
+
+ pc = poptGetContext(NULL, argc, (const char **) argv, long_options,
+ POPT_CONTEXT_KEEP_FIRST);
+
+ while ((c = poptGetNextOpt(pc)) != -1) {
+ switch (c) {
+ case 'u':
+ Ucrit_addUsername(poptGetOptArg(pc));
+ break;
+ }
+ }
+
+ if (verbose) {
+ d_printf("using configfile = %s\n", dyn_CONFIGFILE);
+ }
+
+ if (!lp_load(dyn_CONFIGFILE,False,False,False)) {
+ fprintf(stderr, "Can't load %s - run testparm to debug it\n", dyn_CONFIGFILE);
+ return (-1);
+ }
+
+ if (profile_only) {
+ return profile_dump();
+ }
+
+ tdb = tdb_open_log(lock_path("sessionid.tdb"), 0, TDB_DEFAULT, O_RDONLY, 0);
+ if (!tdb) {
+ d_printf("sessionid.tdb not initialised\n");
+ } else {
+ if (locks_only) goto locks;
+
+ d_printf("\nSamba version %s\n",SAMBA_VERSION_STRING);
+ d_printf("PID Username Group Machine \n");
+ d_printf("-------------------------------------------------------------------\n");
+
+ tdb_traverse(tdb, traverse_sessionid, NULL);
+ tdb_close(tdb);
+ }
+
+ tdb = tdb_open_log(lock_path("connections.tdb"), 0, TDB_DEFAULT, O_RDONLY, 0);
+ if (!tdb) {
+ d_printf("%s not initialised\n", lock_path("connections.tdb"));
+ d_printf("This is normal if an SMB client has never connected to your server.\n");
+ } else {
+ if (verbose) {
+ d_printf("Opened %s\n", lock_path("connections.tdb"));
+ }
+
+ if (brief)
+ exit(0);
+
+ d_printf("\nService pid machine Connected at\n");
+ d_printf("-------------------------------------------------------\n");
+
+ tdb_traverse(tdb, traverse_fn1, NULL);
+ tdb_close(tdb);
+ }
+
+ locks:
+ if (processes_only) exit(0);
+
+ if (!shares_only) {
+ int ret;
+
+ if (!locking_init(1)) {
+ d_printf("Can't initialise locking module - exiting\n");
+ exit(1);
+ }
+
+ ret = share_mode_forall(print_share_mode);
+
+ if (ret == 0) {
+ d_printf("No locked files\n");
+ } else if (ret == -1) {
+ d_printf("locked file list truncated\n");
+ }
+
+ d_printf("\n");
+
+ if (show_brl) {
+ brl_forall(print_brl);
+ }
+
+ locking_end();
+ }
+
+ return (0);
+}
diff --git a/source/utils/testparm.c b/source/utils/testparm.c
new file mode 100644
index 00000000000..9db6d538d24
--- /dev/null
+++ b/source/utils/testparm.c
@@ -0,0 +1,367 @@
+/*
+ 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
+
+ This program is free software; you can 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.
+*/
+
+/*
+ * 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"
+
+extern BOOL AllowDebugChange;
+
+/***********************************************
+ Here we do a set of 'hard coded' checks for bad
+ configuration settings.
+************************************************/
+
+static int do_global_checks(void)
+{
+ int ret = 0;
+ SMB_STRUCT_STAT st;
+
+ if (lp_security() >= SEC_DOMAIN && !lp_encrypted_passwords()) {
+ fprintf(stderr, "ERROR: in 'security=domain' mode the 'encrypt passwords' parameter must always be set to 'true'.\n");
+ ret = 1;
+ }
+
+ if (lp_wins_support() && lp_wins_server_list()) {
+ fprintf(stderr, "ERROR: both 'wins support = true' and 'wins server = <server list>' \
+cannot be set in the smb.conf file. nmbd will abort with this setting.\n");
+ ret = 1;
+ }
+
+ if (!directory_exist(lp_lockdir(), &st)) {
+ fprintf(stderr, "ERROR: lock directory %s does not exist\n",
+ lp_lockdir());
+ ret = 1;
+ } else if ((st.st_mode & 0777) != 0755) {
+ fprintf(stderr, "WARNING: lock directory %s should have permissions 0755 for browsing to work\n",
+ lp_lockdir());
+ ret = 1;
+ }
+
+ if (!directory_exist(lp_piddir(), &st)) {
+ fprintf(stderr, "ERROR: pid directory %s does not exist\n",
+ lp_piddir());
+ ret = 1;
+ }
+
+ /*
+ * Password server sanity checks.
+ */
+
+ if((lp_security() == SEC_SERVER || lp_security() >= SEC_DOMAIN) && !lp_passwordserver()) {
+ pstring sec_setting;
+ if(lp_security() == SEC_SERVER)
+ pstrcpy(sec_setting, "server");
+ else if(lp_security() == SEC_DOMAIN)
+ pstrcpy(sec_setting, "domain");
+
+ fprintf(stderr, "ERROR: The setting 'security=%s' requires the 'password server' parameter be set \
+to a valid password server.\n", sec_setting );
+ ret = 1;
+ }
+
+
+ /*
+ * Check 'hosts equiv' and 'use rhosts' compatibility with 'hostname lookup' value.
+ */
+
+ if(*lp_hosts_equiv() && !lp_hostname_lookups()) {
+ fprintf(stderr, "ERROR: The setting 'hosts equiv = %s' requires that 'hostname lookups = yes'.\n", lp_hosts_equiv());
+ ret = 1;
+ }
+
+ /*
+ * Password chat sanity checks.
+ */
+
+ if(lp_security() == SEC_USER && lp_unix_password_sync()) {
+
+ /*
+ * Check that we have a valid lp_passwd_program() if not using pam.
+ */
+
+#ifdef WITH_PAM
+ if (!lp_pam_password_change()) {
+#endif
+
+ if(lp_passwd_program() == NULL) {
+ fprintf( stderr, "ERROR: the 'unix password sync' parameter is set and there is no valid 'passwd program' \
+parameter.\n" );
+ ret = 1;
+ } else {
+ pstring passwd_prog;
+ pstring truncated_prog;
+ const char *p;
+
+ pstrcpy( passwd_prog, lp_passwd_program());
+ p = passwd_prog;
+ *truncated_prog = '\0';
+ next_token(&p, truncated_prog, NULL, sizeof(pstring));
+
+ if(access(truncated_prog, F_OK) == -1) {
+ fprintf(stderr, "ERROR: the 'unix password sync' parameter is set and the 'passwd program' (%s) \
+cannot be executed (error was %s).\n", truncated_prog, strerror(errno) );
+ ret = 1;
+ }
+ }
+
+#ifdef WITH_PAM
+ }
+#endif
+
+ if(lp_passwd_chat() == NULL) {
+ fprintf(stderr, "ERROR: the 'unix password sync' parameter is set and there is no valid 'passwd chat' \
+parameter.\n");
+ ret = 1;
+ }
+
+ /*
+ * Check that we have a valid script and that it hasn't
+ * been written to expect the old password.
+ */
+
+ if(lp_encrypted_passwords()) {
+ if(strstr_m( lp_passwd_chat(), "%o")!=NULL) {
+ fprintf(stderr, "ERROR: the 'passwd chat' script [%s] expects to use the old plaintext password \
+via the %%o substitution. With encrypted passwords this is not possible.\n", lp_passwd_chat() );
+ ret = 1;
+ }
+ }
+ }
+
+ if (strlen(lp_winbind_separator()) != 1) {
+ fprintf(stderr,"ERROR: the 'winbind separator' parameter must be a single character.\n");
+ ret = 1;
+ }
+
+ if (*lp_winbind_separator() == '+') {
+ fprintf(stderr,"'winbind separator = +' might cause problems with group membership.\n");
+ }
+
+ if (lp_algorithmic_rid_base() < BASE_RID) {
+ /* Try to prevent admin foot-shooting, we can't put algorithmic
+ rids below 1000, that's the 'well known RIDs' on NT */
+ fprintf(stderr,"'algorithmic rid base' must be equal to or above %lu\n", BASE_RID);
+ }
+
+ if (lp_algorithmic_rid_base() & 1) {
+ fprintf(stderr,"'algorithmic rid base' must be even.\n");
+ }
+
+#ifndef HAVE_DLOPEN
+ if (lp_preload_modules()) {
+ fprintf(stderr,"WARNING: 'preload modules = ' set while loading plugins not supported.\n");
+ }
+#endif
+
+ if (!lp_passdb_backend()) {
+ fprintf(stderr,"ERROR: passdb backend must have a value or be left out\n");
+ }
+
+ return ret;
+}
+
+ int main(int argc, const char *argv[])
+{
+ const char *config_file = dyn_CONFIGFILE;
+ int s;
+ static BOOL silent_mode = False;
+ int ret = 0;
+ poptContext pc;
+ static const char *term_code = "";
+ static char *new_local_machine = NULL;
+ const char *cname;
+ const char *caddr;
+ static int show_defaults;
+
+ struct poptOption long_options[] = {
+ POPT_AUTOHELP
+ {"suppress-prompt", 's', POPT_ARG_VAL, &silent_mode, 1, "Suppress prompt for enter"},
+ {"verbose", 'v', POPT_ARG_NONE, &show_defaults, 1, "Show default options too"},
+ {"server", 'L',POPT_ARG_STRING, &new_local_machine, 0, "Set %%L macro to servername\n"},
+ {"encoding", 't', POPT_ARG_STRING, &term_code, 0, "Print parameters with encoding"},
+ POPT_COMMON_VERSION
+ POPT_TABLEEND
+ };
+
+ pc = poptGetContext(NULL, argc, argv, long_options,
+ POPT_CONTEXT_KEEP_FIRST);
+ poptSetOtherOptionHelp(pc, "[OPTION...] <config-file> [host-name] [host-ip]");
+
+ while(poptGetNextOpt(pc) != -1);
+
+ setup_logging(poptGetArg(pc), True);
+
+ if (poptPeekArg(pc))
+ config_file = poptGetArg(pc);
+
+ cname = poptGetArg(pc);
+ caddr = poptGetArg(pc);
+
+ if ( cname && ! caddr ) {
+ printf ( "ERROR: You must specify both a machine name and an IP address.\n" );
+ return(1);
+ }
+
+ if (new_local_machine) {
+ set_local_machine_name(new_local_machine, True);
+ }
+
+ dbf = x_stderr;
+ DEBUGLEVEL = 2;
+ AllowDebugChange = False;
+
+ fprintf(stderr,"Load smb config files from %s\n",config_file);
+
+ if (!lp_load(config_file,False,True,False)) {
+ fprintf(stderr,"Error loading services.\n");
+ return(1);
+ }
+
+ fprintf(stderr,"Loaded services file OK.\n");
+
+ ret = do_global_checks();
+
+ for (s=0;s<1000;s++) {
+ if (VALID_SNUM(s))
+ if (strlen(lp_servicename(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 smbclient prior to Samba 3.0.)\n" );
+ break;
+ }
+ }
+
+ for (s=0;s<1000;s++) {
+ if (VALID_SNUM(s)) {
+ const char **deny_list = lp_hostsdeny(s);
+ const char **allow_list = lp_hostsallow(s);
+ 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(s) );
+ }
+ }
+ }
+
+ 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(s) );
+ }
+ }
+ }
+
+ if(lp_level2_oplocks(s) && !lp_oplocks(s)) {
+ fprintf(stderr,"Invalid combination of parameters for service %s. \
+ Level II oplocks can only be set if oplocks are also set.\n",
+ lp_servicename(s) );
+ }
+
+ if (lp_map_hidden(s) && !(lp_create_mask(s) & S_IXOTH)) {
+ fprintf(stderr,"Invalid combination of parameters for service %s. \
+ Map hidden can only work if create mask includes octal 01 (S_IXOTH).\n",
+ lp_servicename(s) );
+ }
+ if (lp_map_hidden(s) && (lp_force_create_mode(s) & S_IXOTH)) {
+ fprintf(stderr,"Invalid combination of parameters for service %s. \
+ Map hidden can only work if force create mode excludes octal 01 (S_IXOTH).\n",
+ lp_servicename(s) );
+ }
+ if (lp_map_system(s) && !(lp_create_mask(s) & S_IXGRP)) {
+ fprintf(stderr,"Invalid combination of parameters for service %s. \
+ Map system can only work if create mask includes octal 010 (S_IXGRP).\n",
+ lp_servicename(s) );
+ }
+ if (lp_map_system(s) && (lp_force_create_mode(s) & S_IXGRP)) {
+ fprintf(stderr,"Invalid combination of parameters for service %s. \
+ Map system can only work if force create mode excludes octal 010 (S_IXGRP).\n",
+ lp_servicename(s) );
+ }
+ }
+ }
+
+
+ if (!silent_mode) {
+ fprintf(stderr,"Server role: ");
+ switch(lp_server_role()) {
+ case ROLE_STANDALONE:
+ fprintf(stderr,"ROLE_STANDALONE\n");
+ break;
+ case ROLE_DOMAIN_MEMBER:
+ fprintf(stderr,"ROLE_DOMAIN_MEMBER\n");
+ break;
+ case ROLE_DOMAIN_BDC:
+ fprintf(stderr,"ROLE_DOMAIN_BDC\n");
+ break;
+ case ROLE_DOMAIN_PDC:
+ fprintf(stderr,"ROLE_DOMAIN_PDC\n");
+ break;
+ default:
+ fprintf(stderr,"Unknown -- internal error?\n");
+ break;
+ }
+ }
+
+ if (!cname) {
+ if (!silent_mode) {
+ fprintf(stderr,"Press enter to see a dump of your service definitions\n");
+ fflush(stdout);
+ getc(stdin);
+ }
+ lp_dump(stdout, show_defaults, lp_numservices());
+ }
+
+ if(cname && caddr){
+ /* this is totally ugly, a real `quick' hack */
+ for (s=0;s<1000;s++) {
+ if (VALID_SNUM(s)) {
+ if (allow_access(lp_hostsdeny(-1), lp_hostsallow(-1), cname, caddr)
+ && allow_access(lp_hostsdeny(s), lp_hostsallow(s), cname, caddr)) {
+ fprintf(stderr,"Allow connection from %s (%s) to %s\n",
+ cname,caddr,lp_servicename(s));
+ } else {
+ fprintf(stderr,"Deny connection from %s (%s) to %s\n",
+ cname,caddr,lp_servicename(s));
+ }
+ }
+ }
+ }
+ return(ret);
+}
diff --git a/source/utils/testprns.c b/source/utils/testprns.c
new file mode 100644
index 00000000000..7e52b86afb6
--- /dev/null
+++ b/source/utils/testprns.c
@@ -0,0 +1,61 @@
+/*
+ Unix SMB/CIFS implementation.
+ test printer setup
+ Copyright (C) Karl Auer 1993, 1994-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 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.
+*/
+
+/*
+ * Testbed for pcap.c
+ *
+ * This module simply checks a given printer name against the compiled-in
+ * printcap file.
+ *
+ * The operation is performed with DEBUGLEVEL at 3.
+ *
+ * Useful for a quick check of a printcap file.
+ *
+ */
+
+#include "includes.h"
+
+int main(int argc, char *argv[])
+{
+ const char *pszTemp;
+
+ setup_logging(argv[0],True);
+
+ if (argc < 2 || argc > 3)
+ printf("Usage: testprns printername [printcapfile]\n");
+ else
+ {
+ dbf = x_fopen("test.log", O_WRONLY|O_CREAT|O_TRUNC, 0644);
+ if (dbf == NULL) {
+ printf("Unable to open logfile.\n");
+ } else {
+ DEBUGLEVEL = 3;
+ pszTemp = (argc < 3) ? PRINTCAP_NAME : argv[2];
+ printf("Looking for printer %s in printcap file %s\n",
+ argv[1], pszTemp);
+ if (!pcap_printername_ok(argv[1], pszTemp))
+ printf("Printer name %s is not valid.\n", argv[1]);
+ else
+ printf("Printer name %s is valid.\n", argv[1]);
+ x_fclose(dbf);
+ }
+ }
+ return (0);
+}
diff --git a/source/web/.cvsignore b/source/web/.cvsignore
new file mode 100644
index 00000000000..ed29eafc6b1
--- /dev/null
+++ b/source/web/.cvsignore
@@ -0,0 +1 @@
+swat_proto.h \ No newline at end of file
diff --git a/source/web/cgi.c b/source/web/cgi.c
new file mode 100644
index 00000000000..07b9f52ff77
--- /dev/null
+++ b/source/web/cgi.c
@@ -0,0 +1,626 @@
+/*
+ some simple CGI helper routines
+ 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 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 "../web/swat_proto.h"
+
+#define MAX_VARIABLES 10000
+
+/* set the expiry on fixed pages */
+#define EXPIRY_TIME (60*60*24*7)
+
+#ifdef DEBUG_COMMENTS
+extern void print_title(char *fmt, ...);
+#endif
+
+struct var {
+ char *name;
+ char *value;
+};
+
+static struct var variables[MAX_VARIABLES];
+static int num_variables;
+static int content_length;
+static int request_post;
+static char *query_string;
+static const char *baseurl;
+static char *pathinfo;
+static char *C_user;
+static BOOL inetd_server;
+static BOOL got_request;
+
+static char *grab_line(FILE *f, int *cl)
+{
+ char *ret = NULL;
+ int i = 0;
+ int len = 0;
+
+ while ((*cl)) {
+ int c;
+
+ if (i == len) {
+ char *ret2;
+ if (len == 0) len = 1024;
+ else len *= 2;
+ ret2 = (char *)Realloc(ret, len);
+ if (!ret2) return ret;
+ ret = ret2;
+ }
+
+ c = fgetc(f);
+ (*cl)--;
+
+ if (c == EOF) {
+ (*cl) = 0;
+ break;
+ }
+
+ if (c == '\r') continue;
+
+ if (strchr_m("\n&", c)) break;
+
+ ret[i++] = c;
+
+ }
+
+
+ ret[i] = 0;
+ return ret;
+}
+
+/**
+ URL encoded strings can have a '+', which should be replaced with a space
+
+ (This was in rfc1738_unescape(), but that broke the squid helper)
+**/
+
+static void plus_to_space_unescape(char *buf)
+{
+ char *p=buf;
+
+ while ((p=strchr_m(p,'+')))
+ *p = ' ';
+}
+
+/***************************************************************************
+ load all the variables passed to the CGI program. May have multiple variables
+ with the same name and the same or different values. Takes a file parameter
+ for simulating CGI invocation eg loading saved preferences.
+ ***************************************************************************/
+void cgi_load_variables(void)
+{
+ static char *line;
+ char *p, *s, *tok;
+ int len, i;
+ FILE *f = stdin;
+
+#ifdef DEBUG_COMMENTS
+ char dummy[100]="";
+ print_title(dummy);
+ d_printf("<!== Start dump in cgi_load_variables() %s ==>\n",__FILE__);
+#endif
+
+ if (!content_length) {
+ p = getenv("CONTENT_LENGTH");
+ len = p?atoi(p):0;
+ } else {
+ len = content_length;
+ }
+
+
+ if (len > 0 &&
+ (request_post ||
+ ((s=getenv("REQUEST_METHOD")) &&
+ strequal(s,"POST")))) {
+ while (len && (line=grab_line(f, &len))) {
+ p = strchr_m(line,'=');
+ if (!p) continue;
+
+ *p = 0;
+
+ variables[num_variables].name = strdup(line);
+ variables[num_variables].value = strdup(p+1);
+
+ SAFE_FREE(line);
+
+ if (!variables[num_variables].name ||
+ !variables[num_variables].value)
+ continue;
+
+ plus_to_space_unescape(variables[num_variables].value);
+ rfc1738_unescape(variables[num_variables].value);
+ plus_to_space_unescape(variables[num_variables].name);
+ rfc1738_unescape(variables[num_variables].name);
+
+#ifdef DEBUG_COMMENTS
+ printf("<!== POST var %s has value \"%s\" ==>\n",
+ variables[num_variables].name,
+ variables[num_variables].value);
+#endif
+
+ num_variables++;
+ if (num_variables == MAX_VARIABLES) break;
+ }
+ }
+
+ fclose(stdin);
+ open("/dev/null", O_RDWR);
+
+ if ((s=query_string) || (s=getenv("QUERY_STRING"))) {
+ for (tok=strtok(s,"&;");tok;tok=strtok(NULL,"&;")) {
+ p = strchr_m(tok,'=');
+ if (!p) continue;
+
+ *p = 0;
+
+ variables[num_variables].name = strdup(tok);
+ variables[num_variables].value = strdup(p+1);
+
+ if (!variables[num_variables].name ||
+ !variables[num_variables].value)
+ continue;
+
+ plus_to_space_unescape(variables[num_variables].value);
+ rfc1738_unescape(variables[num_variables].value);
+ plus_to_space_unescape(variables[num_variables].name);
+ rfc1738_unescape(variables[num_variables].name);
+
+#ifdef DEBUG_COMMENTS
+ printf("<!== Commandline var %s has value \"%s\" ==>\n",
+ variables[num_variables].name,
+ variables[num_variables].value);
+#endif
+ num_variables++;
+ if (num_variables == MAX_VARIABLES) break;
+ }
+
+ }
+#ifdef DEBUG_COMMENTS
+ printf("<!== End dump in cgi_load_variables() ==>\n");
+#endif
+
+ /* variables from the client are in display charset - convert them
+ to our internal charset before use */
+ for (i=0;i<num_variables;i++) {
+ pstring dest;
+
+ convert_string(CH_DISPLAY, CH_UNIX,
+ variables[i].name, -1,
+ dest, sizeof(dest), True);
+ free(variables[i].name);
+ variables[i].name = strdup(dest);
+
+ convert_string(CH_DISPLAY, CH_UNIX,
+ variables[i].value, -1,
+ dest, sizeof(dest), True);
+ free(variables[i].value);
+ variables[i].value = strdup(dest);
+ }
+}
+
+
+/***************************************************************************
+ find a variable passed via CGI
+ Doesn't quite do what you think in the case of POST text variables, because
+ if they exist they might have a value of "" or even " ", depending on the
+ browser. Also doesn't allow for variables[] containing multiple variables
+ with the same name and the same or different values.
+ ***************************************************************************/
+const char *cgi_variable(const char *name)
+{
+ int i;
+
+ for (i=0;i<num_variables;i++)
+ if (strcmp(variables[i].name, name) == 0)
+ return variables[i].value;
+ return NULL;
+}
+
+/***************************************************************************
+tell a browser about a fatal error in the http processing
+ ***************************************************************************/
+static void cgi_setup_error(const char *err, const char *header, const char *info)
+{
+ if (!got_request) {
+ /* damn browsers don't like getting cut off before they give a request */
+ char line[1024];
+ while (fgets(line, sizeof(line)-1, stdin)) {
+ if (strnequal(line,"GET ", 4) ||
+ strnequal(line,"POST ", 5) ||
+ strnequal(line,"PUT ", 4)) {
+ break;
+ }
+ }
+ }
+
+ d_printf("HTTP/1.0 %s\r\n%sConnection: close\r\nContent-Type: text/html\r\n\r\n<HTML><HEAD><TITLE>%s</TITLE></HEAD><BODY><H1>%s</H1>%s<p></BODY></HTML>\r\n\r\n", err, header, err, err, info);
+ fclose(stdin);
+ fclose(stdout);
+ exit(0);
+}
+
+
+/***************************************************************************
+tell a browser about a fatal authentication error
+ ***************************************************************************/
+static void cgi_auth_error(void)
+{
+ if (inetd_server) {
+ cgi_setup_error("401 Authorization Required",
+ "WWW-Authenticate: Basic realm=\"SWAT\"\r\n",
+ "You must be authenticated to use this service");
+ } else {
+ printf("Content-Type: text/html\r\n");
+
+ printf("\r\n<HTML><HEAD><TITLE>SWAT</TITLE></HEAD>\n");
+ printf("<BODY><H1>Installation Error</H1>\n");
+ printf("SWAT must be installed via inetd. It cannot be run as a CGI script<p>\n");
+ printf("</BODY></HTML>\r\n");
+ }
+ exit(0);
+}
+
+/***************************************************************************
+authenticate when we are running as a CGI
+ ***************************************************************************/
+static void cgi_web_auth(void)
+{
+ const char *user = getenv("REMOTE_USER");
+ struct passwd *pwd;
+ const char *head = "Content-Type: text/html\r\n\r\n<HTML><BODY><H1>SWAT installation Error</H1>\n";
+ const char *tail = "</BODY></HTML>\r\n";
+
+ if (!user) {
+ printf("%sREMOTE_USER not set. Not authenticated by web server.<br>%s\n",
+ head, tail);
+ exit(0);
+ }
+
+ pwd = getpwnam_alloc(user);
+ if (!pwd) {
+ printf("%sCannot find user %s<br>%s\n", head, user, tail);
+ exit(0);
+ }
+
+ setuid(0);
+ setuid(pwd->pw_uid);
+ if (geteuid() != pwd->pw_uid || getuid() != pwd->pw_uid) {
+ printf("%sFailed to become user %s - uid=%d/%d<br>%s\n",
+ head, user, (int)geteuid(), (int)getuid(), tail);
+ exit(0);
+ }
+ passwd_free(&pwd);
+}
+
+
+/***************************************************************************
+handle a http authentication line
+ ***************************************************************************/
+static BOOL cgi_handle_authorization(char *line)
+{
+ char *p;
+ fstring user, user_pass;
+ struct passwd *pass = NULL;
+
+ if (!strnequal(line,"Basic ", 6)) {
+ goto err;
+ }
+ line += 6;
+ while (line[0] == ' ') line++;
+ base64_decode_inplace(line);
+ if (!(p=strchr_m(line,':'))) {
+ /*
+ * Always give the same error so a cracker
+ * cannot tell why we fail.
+ */
+ goto err;
+ }
+ *p = 0;
+
+ convert_string(CH_DISPLAY, CH_UNIX,
+ line, -1,
+ user, sizeof(user), True);
+
+ convert_string(CH_DISPLAY, CH_UNIX,
+ p+1, -1,
+ user_pass, sizeof(user_pass), True);
+
+ /*
+ * Try and get the user from the UNIX password file.
+ */
+
+ pass = getpwnam_alloc(user);
+
+ /*
+ * Validate the password they have given.
+ */
+
+ if NT_STATUS_IS_OK(pass_check(pass, user, user_pass,
+ strlen(user_pass), NULL, False)) {
+
+ if (pass) {
+ /*
+ * Password was ok.
+ */
+
+ if ( initgroups(pass->pw_name, pass->pw_gid) != 0 )
+ goto err;
+
+ become_user_permanently(pass->pw_uid, pass->pw_gid);
+
+ /* Save the users name */
+ C_user = strdup(user);
+ passwd_free(&pass);
+ return True;
+ }
+ }
+
+err:
+ cgi_setup_error("401 Bad Authorization",
+ "WWW-Authenticate: Basic realm=\"SWAT\"\r\n",
+ "username or password incorrect");
+
+ passwd_free(&pass);
+ return False;
+}
+
+/***************************************************************************
+is this root?
+ ***************************************************************************/
+BOOL am_root(void)
+{
+ if (geteuid() == 0) {
+ return( True);
+ } else {
+ return( False);
+ }
+}
+
+/***************************************************************************
+return a ptr to the users name
+ ***************************************************************************/
+char *cgi_user_name(void)
+{
+ return(C_user);
+}
+
+
+/***************************************************************************
+handle a file download
+ ***************************************************************************/
+static void cgi_download(char *file)
+{
+ SMB_STRUCT_STAT st;
+ char buf[1024];
+ int fd, l, i;
+ char *p;
+ char *lang;
+
+ /* sanitise the filename */
+ for (i=0;file[i];i++) {
+ if (!isalnum((int)file[i]) && !strchr_m("/.-_", file[i])) {
+ cgi_setup_error("404 File Not Found","",
+ "Illegal character in filename");
+ }
+ }
+
+ if (!file_exist(file, &st)) {
+ cgi_setup_error("404 File Not Found","",
+ "The requested file was not found");
+ }
+
+ fd = web_open(file,O_RDONLY,0);
+ if (fd == -1) {
+ cgi_setup_error("404 File Not Found","",
+ "The requested file was not found");
+ }
+ printf("HTTP/1.0 200 OK\r\n");
+ if ((p=strrchr_m(file,'.'))) {
+ if (strcmp(p,".gif")==0) {
+ printf("Content-Type: image/gif\r\n");
+ } else if (strcmp(p,".jpg")==0) {
+ printf("Content-Type: image/jpeg\r\n");
+ } else if (strcmp(p,".txt")==0) {
+ printf("Content-Type: text/plain\r\n");
+ } else {
+ printf("Content-Type: text/html\r\n");
+ }
+ }
+ printf("Expires: %s\r\n", http_timestring(time(NULL)+EXPIRY_TIME));
+
+ lang = lang_tdb_current();
+ if (lang) {
+ printf("Content-Language: %s\r\n", lang);
+ }
+
+ printf("Content-Length: %d\r\n\r\n", (int)st.st_size);
+ while ((l=read(fd,buf,sizeof(buf)))>0) {
+ fwrite(buf, 1, l, stdout);
+ }
+ close(fd);
+ exit(0);
+}
+
+
+
+
+/**
+ * @brief Setup the CGI framework.
+ *
+ * Setup the cgi framework, handling the possibility that this program
+ * is either run as a true CGI program with a gateway to a web server, or
+ * is itself a mini web server.
+ **/
+void cgi_setup(const char *rootdir, int auth_required)
+{
+ BOOL authenticated = False;
+ char line[1024];
+ char *url=NULL;
+ char *p;
+ char *lang;
+
+ if (chdir(rootdir)) {
+ cgi_setup_error("500 Server Error", "",
+ "chdir failed - the server is not configured correctly");
+ }
+
+ /* Handle the possibility we might be running as non-root */
+ sec_init();
+
+ if ((lang=getenv("HTTP_ACCEPT_LANGUAGE"))) {
+ /* if running as a cgi program */
+ web_set_lang(lang);
+ }
+
+ /* maybe we are running under a web server */
+ if (getenv("CONTENT_LENGTH") || getenv("REQUEST_METHOD")) {
+ if (auth_required) {
+ cgi_web_auth();
+ }
+ return;
+ }
+
+ inetd_server = True;
+
+ if (!check_access(1, lp_hostsallow(-1), lp_hostsdeny(-1))) {
+ cgi_setup_error("403 Forbidden", "",
+ "Samba is configured to deny access from this client\n<br>Check your \"hosts allow\" and \"hosts deny\" options in smb.conf ");
+ }
+
+ /* we are a mini-web server. We need to read the request from stdin
+ and handle authentication etc */
+ while (fgets(line, sizeof(line)-1, stdin)) {
+ if (line[0] == '\r' || line[0] == '\n') break;
+ if (strnequal(line,"GET ", 4)) {
+ got_request = True;
+ url = strdup(&line[4]);
+ } else if (strnequal(line,"POST ", 5)) {
+ got_request = True;
+ request_post = 1;
+ url = strdup(&line[5]);
+ } else if (strnequal(line,"PUT ", 4)) {
+ got_request = True;
+ cgi_setup_error("400 Bad Request", "",
+ "This server does not accept PUT requests");
+ } else if (strnequal(line,"Authorization: ", 15)) {
+ authenticated = cgi_handle_authorization(&line[15]);
+ } else if (strnequal(line,"Content-Length: ", 16)) {
+ content_length = atoi(&line[16]);
+ } else if (strnequal(line,"Accept-Language: ", 17)) {
+ web_set_lang(&line[17]);
+ }
+ /* ignore all other requests! */
+ }
+
+ if (auth_required && !authenticated) {
+ cgi_auth_error();
+ }
+
+ if (!url) {
+ cgi_setup_error("400 Bad Request", "",
+ "You must specify a GET or POST request");
+ }
+
+ /* trim the URL */
+ if ((p = strchr_m(url,' ')) || (p=strchr_m(url,'\t'))) {
+ *p = 0;
+ }
+ while (*url && strchr_m("\r\n",url[strlen(url)-1])) {
+ url[strlen(url)-1] = 0;
+ }
+
+ /* anything following a ? in the URL is part of the query string */
+ if ((p=strchr_m(url,'?'))) {
+ query_string = p+1;
+ *p = 0;
+ }
+
+ string_sub(url, "/swat/", "", 0);
+
+ if (url[0] != '/' && strstr(url,"..")==0 && file_exist(url, NULL)) {
+ cgi_download(url);
+ }
+
+ printf("HTTP/1.0 200 OK\r\nConnection: close\r\n");
+ printf("Date: %s\r\n", http_timestring(time(NULL)));
+ baseurl = "";
+ pathinfo = url+1;
+}
+
+
+/***************************************************************************
+return the current pages URL
+ ***************************************************************************/
+const char *cgi_baseurl(void)
+{
+ if (inetd_server) {
+ return baseurl;
+ }
+ return getenv("SCRIPT_NAME");
+}
+
+/***************************************************************************
+return the current pages path info
+ ***************************************************************************/
+const char *cgi_pathinfo(void)
+{
+ char *r;
+ if (inetd_server) {
+ return pathinfo;
+ }
+ r = getenv("PATH_INFO");
+ if (!r) return "";
+ if (*r == '/') r++;
+ return r;
+}
+
+/***************************************************************************
+return the hostname of the client
+ ***************************************************************************/
+char *cgi_remote_host(void)
+{
+ if (inetd_server) {
+ return get_peer_name(1,False);
+ }
+ return getenv("REMOTE_HOST");
+}
+
+/***************************************************************************
+return the hostname of the client
+ ***************************************************************************/
+char *cgi_remote_addr(void)
+{
+ if (inetd_server) {
+ return get_peer_addr(1);
+ }
+ return getenv("REMOTE_ADDR");
+}
+
+
+/***************************************************************************
+return True if the request was a POST
+ ***************************************************************************/
+BOOL cgi_waspost(void)
+{
+ if (inetd_server) {
+ return request_post;
+ }
+ return strequal(getenv("REQUEST_METHOD"), "POST");
+}
diff --git a/source/web/diagnose.c b/source/web/diagnose.c
new file mode 100644
index 00000000000..46432c41f85
--- /dev/null
+++ b/source/web/diagnose.c
@@ -0,0 +1,79 @@
+/*
+ Unix SMB/CIFS implementation.
+ diagnosis tools for web admin
+ 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 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 "../web/swat_proto.h"
+
+#ifdef WITH_WINBIND
+
+NSS_STATUS winbindd_request(int req_type,
+ struct winbindd_request *request,
+ struct winbindd_response *response);
+
+/* check to see if winbind is running by pinging it */
+
+BOOL winbindd_running(void)
+{
+ return winbind_ping();
+}
+#endif
+
+/* check to see if nmbd is running on localhost by looking for a __SAMBA__
+ response */
+BOOL nmbd_running(void)
+{
+ extern struct in_addr loopback_ip;
+ int fd, count, flags;
+ struct in_addr *ip_list;
+
+ if ((fd = open_socket_in(SOCK_DGRAM, 0, 3,
+ interpret_addr("127.0.0.1"), True)) != -1) {
+ if ((ip_list = name_query(fd, "__SAMBA__", 0,
+ True, True, loopback_ip,
+ &count, &flags, NULL)) != NULL) {
+ SAFE_FREE(ip_list);
+ close(fd);
+ return True;
+ }
+ close (fd);
+ }
+
+ return False;
+}
+
+
+/* check to see if smbd is running on localhost by trying to open a connection
+ then closing it */
+BOOL smbd_running(void)
+{
+ static struct cli_state cli;
+ extern struct in_addr loopback_ip;
+
+ if (!cli_initialise(&cli))
+ return False;
+
+ if (!cli_connect(&cli, global_myname(), &loopback_ip)) {
+ cli_shutdown(&cli);
+ return False;
+ }
+
+ cli_shutdown(&cli);
+ return True;
+}
diff --git a/source/web/neg_lang.c b/source/web/neg_lang.c
new file mode 100644
index 00000000000..da974f78a4a
--- /dev/null
+++ b/source/web/neg_lang.c
@@ -0,0 +1,117 @@
+/*
+ Unix SMB/CIFS implementation.
+ SWAT language handling
+
+ This program is free software; you can 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.
+
+ Created by Ryo Kawahara <rkawa@lbe.co.jp>
+*/
+
+#include "includes.h"
+#include "../web/swat_proto.h"
+
+/*
+ during a file download we first check to see if there is a language
+ specific file available. If there is then use that, otherwise
+ just open the specified file
+*/
+int web_open(const char *fname, int flags, mode_t mode)
+{
+ char *p = NULL;
+ char *lang = lang_tdb_current();
+ int fd;
+ if (lang) {
+ asprintf(&p, "lang/%s/%s", lang, fname);
+ if (p) {
+ fd = sys_open(p, flags, mode);
+ free(p);
+ if (fd != -1) {
+ return fd;
+ }
+ }
+ }
+
+ /* fall through to default name */
+ return sys_open(fname, flags, mode);
+}
+
+
+struct pri_list {
+ float pri;
+ char *string;
+};
+
+static int qsort_cmp_list(const void *x, const void *y) {
+ struct pri_list *a = (struct pri_list *)x;
+ struct pri_list *b = (struct pri_list *)y;
+ if (a->pri > b->pri) return -1;
+ if (a->pri == b->pri) return 0;
+ return 1;
+}
+
+/*
+ choose from a list of languages. The list can be comma or space
+ separated
+ Keep choosing until we get a hit
+ Changed to habdle priority -- Simo
+*/
+
+void web_set_lang(const char *lang_string)
+{
+ char **lang_list, **count;
+ struct pri_list *pl;
+ int lang_num, i;
+
+ /* build the lang list */
+ lang_list = str_list_make(lang_string, ", \t\r\n");
+ if (!lang_list) return;
+
+ /* sort the list by priority */
+ lang_num = 0;
+ count = lang_list;
+ while (*count && **count) {
+ count++;
+ lang_num++;
+ }
+ pl = (struct pri_list *)malloc(sizeof(struct pri_list) * lang_num);
+ for (i = 0; i < lang_num; i++) {
+ char *pri_code;
+ if ((pri_code=strstr(lang_list[i], ";q="))) {
+ *pri_code = '\0';
+ pri_code += 3;
+ sscanf(pri_code, "%f", &(pl[i].pri));
+ } else {
+ pl[i].pri = 1;
+ }
+ pl[i].string = strdup(lang_list[i]);
+ }
+ str_list_free(&lang_list);
+
+ qsort(pl, lang_num, sizeof(struct pri_list), &qsort_cmp_list);
+
+ /* it's not an error to not initialise - we just fall back to
+ the default */
+
+ for (i = 0; i < lang_num; i++) {
+ if (lang_tdb_init(pl[i].string)) break;
+ }
+
+ for (i = 0; i < lang_num; i++) {
+ SAFE_FREE(pl[i].string);
+ }
+ SAFE_FREE(pl);
+
+ return;
+}
diff --git a/source/web/startstop.c b/source/web/startstop.c
new file mode 100644
index 00000000000..93e8650c2bc
--- /dev/null
+++ b/source/web/startstop.c
@@ -0,0 +1,131 @@
+/*
+ Unix SMB/CIFS implementation.
+ start/stop nmbd and smbd
+ 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 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 "../web/swat_proto.h"
+#include "dynconfig.h"
+
+
+/** Startup smbd from web interface. */
+void start_smbd(void)
+{
+ pstring binfile;
+
+ if (geteuid() != 0) return;
+
+ if (fork()) {
+ return;
+ }
+
+ slprintf(binfile, sizeof(pstring) - 1, "%s/smbd", dyn_SBINDIR);
+
+ become_daemon(True);
+
+ execl(binfile, binfile, "-D", NULL);
+
+ exit(0);
+}
+
+/* startup nmbd */
+void start_nmbd(void)
+{
+ pstring binfile;
+
+ if (geteuid() != 0) return;
+
+ if (fork()) {
+ return;
+ }
+
+ slprintf(binfile, sizeof(pstring) - 1, "%s/nmbd", dyn_SBINDIR);
+
+ become_daemon(True);
+
+ execl(binfile, binfile, "-D", NULL);
+
+ exit(0);
+}
+
+/** Startup winbindd from web interface. */
+void start_winbindd(void)
+{
+ pstring binfile;
+
+ if (geteuid() != 0) return;
+
+ if (fork()) {
+ return;
+ }
+
+ slprintf(binfile, sizeof(pstring) - 1, "%s/winbindd", dyn_SBINDIR);
+
+ become_daemon(True);
+
+ execl(binfile, binfile, NULL);
+
+ exit(0);
+}
+
+
+/* stop smbd */
+void stop_smbd(void)
+{
+ pid_t pid = pidfile_pid("smbd");
+
+ if (geteuid() != 0) return;
+
+ if (pid <= 0) return;
+
+ kill(pid, SIGTERM);
+}
+
+/* stop nmbd */
+void stop_nmbd(void)
+{
+ pid_t pid = pidfile_pid("nmbd");
+
+ if (geteuid() != 0) return;
+
+ if (pid <= 0) return;
+
+ kill(pid, SIGTERM);
+}
+#ifdef WITH_WINBIND
+/* stop winbindd */
+void stop_winbindd(void)
+{
+ pid_t pid = pidfile_pid("winbindd");
+
+ if (geteuid() != 0) return;
+
+ if (pid <= 0) return;
+
+ kill(pid, SIGTERM);
+}
+#endif
+/* kill a specified process */
+void kill_pid(pid_t pid)
+{
+ if (geteuid() != 0) return;
+
+ if (pid <= 0) return;
+
+ kill(pid, SIGTERM);
+}
diff --git a/source/web/statuspage.c b/source/web/statuspage.c
new file mode 100644
index 00000000000..3d70796830d
--- /dev/null
+++ b/source/web/statuspage.c
@@ -0,0 +1,441 @@
+/*
+ Unix SMB/CIFS implementation.
+ web status page
+ 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 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 "../web/swat_proto.h"
+
+#define PIDMAP struct PidMap
+
+/* how long to wait for start/stops to take effect */
+#define SLEEP_TIME 3
+
+PIDMAP {
+ PIDMAP *next, *prev;
+ pid_t pid;
+ char *machine;
+};
+
+static PIDMAP *pidmap;
+static int PID_or_Machine; /* 0 = show PID, else show Machine name */
+
+static pid_t smbd_pid;
+
+/* from 2nd call on, remove old list */
+static void initPid2Machine (void)
+{
+ /* show machine name rather PID on table "Open Files"? */
+ if (PID_or_Machine) {
+ PIDMAP *p;
+
+ for (p = pidmap; p != NULL; ) {
+ DLIST_REMOVE(pidmap, p);
+ SAFE_FREE(p->machine);
+ SAFE_FREE(p);
+ }
+
+ pidmap = NULL;
+ }
+}
+
+/* add new PID <-> Machine name mapping */
+static void addPid2Machine (pid_t pid, char *machine)
+{
+ /* show machine name rather PID on table "Open Files"? */
+ if (PID_or_Machine) {
+ PIDMAP *newmap;
+
+ if ((newmap = (PIDMAP *) malloc (sizeof (PIDMAP))) == NULL) {
+ /* XXX need error message for this?
+ if malloc fails, PID is always shown */
+ return;
+ }
+
+ newmap->pid = pid;
+ newmap->machine = strdup (machine);
+
+ DLIST_ADD(pidmap, newmap);
+ }
+}
+
+/* lookup PID <-> Machine name mapping */
+static char *mapPid2Machine (pid_t pid)
+{
+ static char pidbuf [64];
+ PIDMAP *map;
+
+ /* show machine name rather PID on table "Open Files"? */
+ if (PID_or_Machine) {
+ for (map = pidmap; map != NULL; map = map->next) {
+ if (pid == map->pid) {
+ if (map->machine == NULL) /* no machine name */
+ break; /* show PID */
+
+ return map->machine;
+ }
+ }
+ }
+
+ /* PID not in list or machine name NULL? return pid as string */
+ snprintf (pidbuf, sizeof (pidbuf) - 1, "%lu", (unsigned long)pid);
+ return pidbuf;
+}
+
+static char *tstring(time_t t)
+{
+ static pstring buf;
+ pstrcpy(buf, asctime(LocalTime(&t)));
+ all_string_sub(buf," ","&nbsp;",sizeof(buf));
+ return buf;
+}
+
+static void print_share_mode(share_mode_entry *e, char *fname)
+{
+ d_printf("<tr><td>%s</td>",_(mapPid2Machine(e->pid)));
+ d_printf("<td>");
+ switch ((e->share_mode>>4)&0xF) {
+ case DENY_NONE: d_printf("DENY_NONE"); break;
+ case DENY_ALL: d_printf("DENY_ALL "); break;
+ case DENY_DOS: d_printf("DENY_DOS "); break;
+ case DENY_READ: d_printf("DENY_READ "); break;
+ case DENY_WRITE:d_printf("DENY_WRITE "); break;
+ }
+ d_printf("</td>");
+
+ d_printf("<td>");
+ switch (e->share_mode&0xF) {
+ case 0: d_printf("%s", _("RDONLY ")); break;
+ case 1: d_printf("%s", _("WRONLY ")); break;
+ case 2: d_printf("%s", _("RDWR ")); break;
+ }
+ d_printf("</td>");
+
+ d_printf("<td>");
+ if((e->op_type &
+ (EXCLUSIVE_OPLOCK|BATCH_OPLOCK)) ==
+ (EXCLUSIVE_OPLOCK|BATCH_OPLOCK))
+ d_printf("EXCLUSIVE+BATCH ");
+ else if (e->op_type & EXCLUSIVE_OPLOCK)
+ d_printf("EXCLUSIVE ");
+ else if (e->op_type & BATCH_OPLOCK)
+ d_printf("BATCH ");
+ else if (e->op_type & LEVEL_II_OPLOCK)
+ d_printf("LEVEL_II ");
+ else
+ d_printf("NONE ");
+ d_printf("</td>");
+
+ d_printf("<td>%s</td><td>%s</td></tr>\n",
+ fname,tstring(e->time.tv_sec));
+}
+
+
+/* kill off any connections chosen by the user */
+static int traverse_fn1(TDB_CONTEXT *tdb, TDB_DATA kbuf, TDB_DATA dbuf, void* state)
+{
+ struct connections_data crec;
+
+ if (dbuf.dsize != sizeof(crec))
+ return 0;
+
+ memcpy(&crec, dbuf.dptr, sizeof(crec));
+
+ if (crec.cnum == -1 && process_exists(crec.pid)) {
+ char buf[30];
+ slprintf(buf,sizeof(buf)-1,"kill_%d", (int)crec.pid);
+ if (cgi_variable(buf)) {
+ kill_pid(crec.pid);
+ sleep(SLEEP_TIME);
+ }
+ }
+ return 0;
+}
+
+/* traversal fn for showing machine connections */
+static int traverse_fn2(TDB_CONTEXT *tdb, TDB_DATA kbuf, TDB_DATA dbuf, void* state)
+{
+ struct connections_data crec;
+
+ if (dbuf.dsize != sizeof(crec))
+ return 0;
+
+ memcpy(&crec, dbuf.dptr, sizeof(crec));
+
+ if (crec.cnum == -1 || !process_exists(crec.pid) || (crec.pid == smbd_pid))
+ return 0;
+
+ addPid2Machine (crec.pid, crec.machine);
+
+ d_printf("<tr><td>%d</td><td>%s</td><td>%s</td><td>%s</td>\n",
+ (int)crec.pid,
+ crec.machine,crec.addr,
+ tstring(crec.start));
+ if (geteuid() == 0) {
+ d_printf("<td><input type=submit value=\"X\" name=\"kill_%d\"></td>\n",
+ (int)crec.pid);
+ }
+ d_printf("</tr>\n");
+
+ return 0;
+}
+
+/* traversal fn for showing share connections */
+static int traverse_fn3(TDB_CONTEXT *tdb, TDB_DATA kbuf, TDB_DATA dbuf, void* state)
+{
+ struct connections_data crec;
+
+ if (dbuf.dsize != sizeof(crec))
+ return 0;
+
+ memcpy(&crec, dbuf.dptr, sizeof(crec));
+
+ if (crec.cnum == -1 || !process_exists(crec.pid))
+ return 0;
+
+ d_printf("<tr><td>%s</td><td>%s</td><td>%s</td><td>%d</td><td>%s</td><td>%s</td></tr>\n",
+ crec.name,uidtoname(crec.uid),
+ gidtoname(crec.gid),(int)crec.pid,
+ crec.machine,
+ tstring(crec.start));
+ return 0;
+}
+
+
+/* show the current server status */
+void status_page(void)
+{
+ const char *v;
+ int autorefresh=0;
+ int refresh_interval=30;
+ TDB_CONTEXT *tdb;
+ int nr_running=0;
+ BOOL waitup = False;
+
+ smbd_pid = pidfile_pid("smbd");
+
+ if (cgi_variable("smbd_restart") || cgi_variable("all_restart")) {
+ stop_smbd();
+ start_smbd();
+ waitup=True;
+ }
+
+ if (cgi_variable("smbd_start") || cgi_variable("all_start")) {
+ start_smbd();
+ waitup=True;
+ }
+
+ if (cgi_variable("smbd_stop") || cgi_variable("all_stop")) {
+ stop_smbd();
+ waitup=True;
+ }
+
+ if (cgi_variable("nmbd_restart") || cgi_variable("all_restart")) {
+ stop_nmbd();
+ start_nmbd();
+ waitup=True;
+ }
+ if (cgi_variable("nmbd_start") || cgi_variable("all_start")) {
+ start_nmbd();
+ waitup=True;
+ }
+
+ if (cgi_variable("nmbd_stop")|| cgi_variable("all_stop")) {
+ stop_nmbd();
+ waitup=True;
+ }
+
+#ifdef WITH_WINBIND
+ if (cgi_variable("winbindd_restart") || cgi_variable("all_restart")) {
+ stop_winbindd();
+ start_winbindd();
+ waitup=True;
+ }
+
+ if (cgi_variable("winbindd_start") || cgi_variable("all_start")) {
+ start_winbindd();
+ waitup=True;
+ }
+
+ if (cgi_variable("winbindd_stop") || cgi_variable("all_stop")) {
+ stop_winbindd();
+ waitup=True;
+ }
+#endif
+ /* wait for daemons to start/stop */
+ if (waitup)
+ sleep(SLEEP_TIME);
+
+ if (cgi_variable("autorefresh")) {
+ autorefresh = 1;
+ } else if (cgi_variable("norefresh")) {
+ autorefresh = 0;
+ } else if (cgi_variable("refresh")) {
+ autorefresh = 1;
+ }
+
+ if ((v=cgi_variable("refresh_interval"))) {
+ refresh_interval = atoi(v);
+ }
+
+ if (cgi_variable("show_client_in_col_1")) {
+ PID_or_Machine = 1;
+ }
+
+ if (cgi_variable("show_pid_in_col_1")) {
+ PID_or_Machine = 0;
+ }
+
+ tdb = tdb_open_log(lock_path("connections.tdb"), 0, TDB_DEFAULT, O_RDONLY, 0);
+ if (tdb) tdb_traverse(tdb, traverse_fn1, NULL);
+
+ initPid2Machine ();
+
+ d_printf("<H2>%s</H2>\n", _("Server Status"));
+
+ d_printf("<FORM method=post>\n");
+
+ if (!autorefresh) {
+ d_printf("<input type=submit value=\"%s\" name=\"autorefresh\">\n", _("Auto Refresh"));
+ d_printf("<br>%s", _("Refresh Interval: "));
+ d_printf("<input type=text size=2 name=\"refresh_interval\" value=\"%d\">\n",
+ refresh_interval);
+ } else {
+ d_printf("<input type=submit value=\"%s\" name=\"norefresh\">\n", _("Stop Refreshing"));
+ d_printf("<br>%s%d\n", _("Refresh Interval: "), refresh_interval);
+ d_printf("<input type=hidden name=\"refresh\" value=\"1\">\n");
+ }
+
+ d_printf("<p>\n");
+
+ if (!tdb) {
+ /* open failure either means no connections have been
+ made */
+ }
+
+
+ d_printf("<table>\n");
+
+ d_printf("<tr><td>%s</td><td>%s</td></tr>", _("version:"), SAMBA_VERSION_STRING);
+
+ fflush(stdout);
+ d_printf("<tr><td>%s</td><td>%s</td>\n", _("smbd:"), smbd_running()?_("running"):_("not running"));
+ if (geteuid() == 0) {
+ if (smbd_running()) {
+ nr_running++;
+ d_printf("<td><input type=submit name=\"smbd_stop\" value=\"%s\"></td>\n", _("Stop smbd"));
+ } else {
+ d_printf("<td><input type=submit name=\"smbd_start\" value=\"%s\"></td>\n", _("Start smbd"));
+ }
+ d_printf("<td><input type=submit name=\"smbd_restart\" value=\"%s\"></td>\n", _("Restart smbd"));
+ }
+ d_printf("</tr>\n");
+
+ fflush(stdout);
+ d_printf("<tr><td>%s</td><td>%s</td>\n", _("nmbd:"), nmbd_running()?_("running"):_("not running"));
+ if (geteuid() == 0) {
+ if (nmbd_running()) {
+ nr_running++;
+ d_printf("<td><input type=submit name=\"nmbd_stop\" value=\"%s\"></td>\n", _("Stop nmbd"));
+ } else {
+ d_printf("<td><input type=submit name=\"nmbd_start\" value=\"%s\"></td>\n", _("Start nmbd"));
+ }
+ d_printf("<td><input type=submit name=\"nmbd_restart\" value=\"%s\"></td>\n", _("Restart nmbd"));
+ }
+ d_printf("</tr>\n");
+
+#ifdef WITH_WINBIND
+ fflush(stdout);
+ d_printf("<tr><td>%s</td><td>%s</td>\n", _("winbindd:"), winbindd_running()?_("running"):_("not running"));
+ if (geteuid() == 0) {
+ if (winbindd_running()) {
+ nr_running++;
+ d_printf("<td><input type=submit name=\"winbindd_stop\" value=\"%s\"></td>\n", _("Stop winbindd"));
+ } else {
+ d_printf("<td><input type=submit name=\"winbindd_start\" value=\"%s\"></td>\n", _("Start winbindd"));
+ }
+ d_printf("<td><input type=submit name=\"winbindd_restart\" value=\"%s\"></td>\n", _("Restart winbindd"));
+ }
+ d_printf("</tr>\n");
+#endif
+
+ if (geteuid() == 0) {
+ d_printf("<tr><td></td><td></td>\n");
+ if (nr_running >= 1) {
+ /* stop, restart all */
+ d_printf("<td><input type=submit name=\"all_stop\" value=\"%s\"></td>\n", _("Stop All"));
+ d_printf("<td><input type=submit name=\"all_restart\" value=\"%s\"></td>\n", _("Restart All"));
+ }
+ else if (nr_running == 0) {
+ /* start all */
+ d_printf("<td><input type=submit name=\"all_start\" value=\"%s\"></td>\n", _("Start All"));
+ }
+ d_printf("</tr>\n");
+ }
+ d_printf("</table>\n");
+ fflush(stdout);
+
+ d_printf("<p><h3>%s</h3>\n", _("Active Connections"));
+ d_printf("<table border=1>\n");
+ d_printf("<tr><th>%s</th><th>%s</th><th>%s</th><th>%s</th>\n", _("PID"), _("Client"), _("IP address"), _("Date"));
+ if (geteuid() == 0) {
+ d_printf("<th>%s</th>\n", _("Kill"));
+ }
+ d_printf("</tr>\n");
+
+ if (tdb) tdb_traverse(tdb, traverse_fn2, NULL);
+
+ d_printf("</table><p>\n");
+
+ d_printf("<p><h3>%s</h3>\n", _("Active Shares"));
+ d_printf("<table border=1>\n");
+ d_printf("<tr><th>%s</th><th>%s</th><th>%s</th><th>%s</th><th>%s</th><th>%s</th></tr>\n\n",
+ _("Share"), _("User"), _("Group"), _("PID"), _("Client"), _("Date"));
+
+ if (tdb) tdb_traverse(tdb, traverse_fn3, NULL);
+
+ d_printf("</table><p>\n");
+
+ d_printf("<h3>%s</h3>\n", _("Open Files"));
+ d_printf("<table border=1>\n");
+ d_printf("<tr><th>%s</th><th>%s</th><th>%s</th><th>%s</th><th>%s</th><th>%s</th></tr>\n", _("PID"), _("Sharing"), _("R/W"), _("Oplock"), _("File"), _("Date"));
+
+ locking_init(1);
+ share_mode_forall(print_share_mode);
+ locking_end();
+ d_printf("</table>\n");
+
+ if (tdb) tdb_close(tdb);
+
+ d_printf("<br><input type=submit name=\"show_client_in_col_1\" value=\"%s\">\n", _("Show Client in col 1"));
+ d_printf("<input type=submit name=\"show_pid_in_col_1\" value=\"%s\">\n", _("Show PID in col 1"));
+
+ d_printf("</FORM>\n");
+
+ if (autorefresh) {
+ /* this little JavaScript allows for automatic refresh
+ of the page. There are other methods but this seems
+ to be the best alternative */
+ d_printf("<script language=\"JavaScript\">\n");
+ d_printf("<!--\nsetTimeout('window.location.replace(\"%s/status?refresh_interval=%d&refresh=1\")', %d)\n",
+ cgi_baseurl(),
+ refresh_interval,
+ refresh_interval*1000);
+ d_printf("//-->\n</script>\n");
+ }
+}
diff --git a/source/web/swat.c b/source/web/swat.c
new file mode 100644
index 00000000000..e535106f260
--- /dev/null
+++ b/source/web/swat.c
@@ -0,0 +1,1389 @@
+/*
+ Unix SMB/CIFS implementation.
+ Samba Web Administration Tool
+ Version 3.0.0
+ Copyright (C) Andrew Tridgell 1997-2002
+ Copyright (C) John H Terpstra 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 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.
+*/
+
+/**
+ * @defgroup swat SWAT - Samba Web Administration Tool
+ * @{
+ * @file swat.c
+ *
+ * @brief Samba Web Administration Tool.
+ **/
+
+#include "includes.h"
+#include "../web/swat_proto.h"
+
+static BOOL demo_mode = False;
+static BOOL have_write_access = False;
+static BOOL have_read_access = False;
+static int iNumNonAutoPrintServices = 0;
+
+/*
+ * Password Management Globals
+ */
+#define SWAT_USER "username"
+#define OLD_PSWD "old_passwd"
+#define NEW_PSWD "new_passwd"
+#define NEW2_PSWD "new2_passwd"
+#define CHG_S_PASSWD_FLAG "chg_s_passwd_flag"
+#define CHG_R_PASSWD_FLAG "chg_r_passwd_flag"
+#define ADD_USER_FLAG "add_user_flag"
+#define DELETE_USER_FLAG "delete_user_flag"
+#define DISABLE_USER_FLAG "disable_user_flag"
+#define ENABLE_USER_FLAG "enable_user_flag"
+#define RHOST "remote_host"
+
+
+/****************************************************************************
+****************************************************************************/
+static int enum_index(int value, const struct enum_list *enumlist)
+{
+ int i;
+ for (i=0;enumlist[i].name;i++)
+ if (value == enumlist[i].value) break;
+ return(i);
+}
+
+static char *fix_backslash(const char *str)
+{
+ static char newstring[1024];
+ char *p = newstring;
+
+ while (*str) {
+ if (*str == '\\') {*p++ = '\\';*p++ = '\\';}
+ else *p++ = *str;
+ ++str;
+ }
+ *p = '\0';
+ return newstring;
+}
+
+static char *stripspaceupper(const char *str)
+{
+ static char newstring[1024];
+ char *p = newstring;
+
+ while (*str) {
+ if (*str != ' ') *p++ = toupper(*str);
+ ++str;
+ }
+ *p = '\0';
+ return newstring;
+}
+
+static char *make_parm_name(const char *label)
+{
+ static char parmname[1024];
+ char *p = parmname;
+
+ while (*label) {
+ if (*label == ' ') *p++ = '_';
+ else *p++ = *label;
+ ++label;
+ }
+ *p = '\0';
+ return parmname;
+}
+
+/****************************************************************************
+ include a lump of html in a page
+****************************************************************************/
+static int include_html(const char *fname)
+{
+ int fd;
+ char buf[1024];
+ int ret;
+
+ fd = web_open(fname, O_RDONLY, 0);
+
+ if (fd == -1) {
+ d_printf(_("ERROR: Can't open %s"), fname);
+ d_printf("\n");
+ return 0;
+ }
+
+ while ((ret = read(fd, buf, sizeof(buf))) > 0) {
+ write(1, buf, ret);
+ }
+
+ close(fd);
+ return 1;
+}
+
+/****************************************************************************
+ start the page with standard stuff
+****************************************************************************/
+static void print_header(void)
+{
+ if (!cgi_waspost()) {
+ d_printf("Expires: 0\r\n");
+ }
+ d_printf("Content-type: text/html\r\n\r\n");
+
+ if (!include_html("include/header.html")) {
+ d_printf("<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 3.2//EN\">\n");
+ d_printf("<HTML>\n<HEAD>\n<TITLE>Samba Web Administration Tool</TITLE>\n</HEAD>\n<BODY background=\"/swat/images/background.jpg\">\n\n");
+ }
+}
+
+/* *******************************************************************
+ show parameter label with translated name in the following form
+ because showing original and translated label in one line looks
+ too long, and showing translated label only is unusable for
+ heavy users.
+ -------------------------------
+ HELP security [combo box][button]
+ SECURITY
+ -------------------------------
+ (capital words are translated by gettext.)
+ if no translation is available, then same form as original is
+ used.
+ "i18n_translated_parm" class is used to change the color of the
+ translated parameter with CSS.
+ **************************************************************** */
+static const char* get_parm_translated(
+ const char* pAnchor, const char* pHelp, const char* pLabel)
+{
+ const char* pTranslated = _(pLabel);
+ static pstring output;
+ if(strcmp(pLabel, pTranslated) != 0)
+ {
+ pstr_sprintf(output,
+ "<A HREF=\"/swat/help/smb.conf.5.html#%s\" target=\"docs\"> %s</A>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; %s <br><span class=\"i18n_translated_parm\">%s</span>",
+ pAnchor, pHelp, pLabel, pTranslated);
+ return output;
+ }
+ pstr_sprintf(output,
+ "<A HREF=\"/swat/help/smb.conf.5.html#%s\" target=\"docs\"> %s</A>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; %s",
+ pAnchor, pHelp, pLabel);
+ return output;
+}
+/****************************************************************************
+ finish off the page
+****************************************************************************/
+static void print_footer(void)
+{
+ if (!include_html("include/footer.html")) {
+ d_printf("\n</BODY>\n</HTML>\n");
+ }
+}
+
+/****************************************************************************
+ display one editable parameter in a form
+****************************************************************************/
+static void show_parameter(int snum, struct parm_struct *parm)
+{
+ int i;
+ void *ptr = parm->ptr;
+
+ if (parm->class == P_LOCAL && snum >= 0) {
+ ptr = lp_local_ptr(snum, ptr);
+ }
+
+ d_printf("<tr><td>%s</td><td>", get_parm_translated(stripspaceupper(parm->label), _("Help"), parm->label));
+ switch (parm->type) {
+ case P_CHAR:
+ d_printf("<input type=text size=2 name=\"parm_%s\" value=\"%c\">",
+ make_parm_name(parm->label), *(char *)ptr);
+ d_printf("<input type=button value=\"%s\" onClick=\"swatform.parm_%s.value=\'%c\'\">",
+ _("Set Default"), make_parm_name(parm->label),(char)(parm->def.cvalue));
+ break;
+
+ case P_LIST:
+ d_printf("<input type=text size=40 name=\"parm_%s\" value=\"",
+ make_parm_name(parm->label));
+ if ((char ***)ptr && *(char ***)ptr && **(char ***)ptr) {
+ char **list = *(char ***)ptr;
+ for (;*list;list++) {
+ /* enclose in quotes if the string contains a space */
+ if ( strchr_m(*list, ' ') )
+ d_printf("\'%s\'%s", *list, ((*(list+1))?", ":""));
+ else
+ d_printf("%s%s", *list, ((*(list+1))?", ":""));
+ }
+ }
+ d_printf("\">");
+ d_printf("<input type=button value=\"%s\" onClick=\"swatform.parm_%s.value=\'",
+ _("Set Default"), make_parm_name(parm->label));
+ if (parm->def.lvalue) {
+ char **list = (char **)(parm->def.lvalue);
+ for (; *list; list++) {
+ /* enclose in quotes if the string contains a space */
+ if ( strchr_m(*list, ' ') )
+ d_printf("\'%s\'%s", *list, ((*(list+1))?", ":""));
+ else
+ d_printf("%s%s", *list, ((*(list+1))?", ":""));
+ }
+ }
+ d_printf("\'\">");
+ break;
+
+ case P_STRING:
+ case P_USTRING:
+ d_printf("<input type=text size=40 name=\"parm_%s\" value=\"%s\">",
+ make_parm_name(parm->label), *(char **)ptr);
+ d_printf("<input type=button value=\"%s\" onClick=\"swatform.parm_%s.value=\'%s\'\">",
+ _("Set Default"), make_parm_name(parm->label),fix_backslash((char *)(parm->def.svalue)));
+ break;
+
+ case P_GSTRING:
+ case P_UGSTRING:
+ d_printf("<input type=text size=40 name=\"parm_%s\" value=\"%s\">",
+ make_parm_name(parm->label), (char *)ptr);
+ d_printf("<input type=button value=\"%s\" onClick=\"swatform.parm_%s.value=\'%s\'\">",
+ _("Set Default"), make_parm_name(parm->label),fix_backslash((char *)(parm->def.svalue)));
+ break;
+
+ case P_BOOL:
+ d_printf("<select name=\"parm_%s\">",make_parm_name(parm->label));
+ d_printf("<option %s>Yes", (*(BOOL *)ptr)?"selected":"");
+ d_printf("<option %s>No", (*(BOOL *)ptr)?"":"selected");
+ d_printf("</select>");
+ d_printf("<input type=button value=\"%s\" onClick=\"swatform.parm_%s.selectedIndex=\'%d\'\">",
+ _("Set Default"), make_parm_name(parm->label),(BOOL)(parm->def.bvalue)?0:1);
+ break;
+
+ case P_BOOLREV:
+ d_printf("<select name=\"parm_%s\">",make_parm_name(parm->label));
+ d_printf("<option %s>Yes", (*(BOOL *)ptr)?"":"selected");
+ d_printf("<option %s>No", (*(BOOL *)ptr)?"selected":"");
+ d_printf("</select>");
+ d_printf("<input type=button value=\"%s\" onClick=\"swatform.parm_%s.selectedIndex=\'%d\'\">",
+ _("Set Default"), make_parm_name(parm->label),(BOOL)(parm->def.bvalue)?1:0);
+ break;
+
+ case P_INTEGER:
+ d_printf("<input type=text size=8 name=\"parm_%s\" value=\"%d\">", make_parm_name(parm->label), *(int *)ptr);
+ d_printf("<input type=button value=\"%s\" onClick=\"swatform.parm_%s.value=\'%d\'\">",
+ _("Set Default"), make_parm_name(parm->label),(int)(parm->def.ivalue));
+ break;
+
+ case P_OCTAL:
+ d_printf("<input type=text size=8 name=\"parm_%s\" value=%s>", make_parm_name(parm->label), octal_string(*(int *)ptr));
+ d_printf("<input type=button value=\"%s\" onClick=\"swatform.parm_%s.value=\'%s\'\">",
+ _("Set Default"), make_parm_name(parm->label),
+ octal_string((int)(parm->def.ivalue)));
+ break;
+
+ case P_ENUM:
+ d_printf("<select name=\"parm_%s\">",make_parm_name(parm->label));
+ for (i=0;parm->enum_list[i].name;i++) {
+ if (i == 0 || parm->enum_list[i].value != parm->enum_list[i-1].value) {
+ d_printf("<option %s>%s",(*(int *)ptr)==parm->enum_list[i].value?"selected":"",parm->enum_list[i].name);
+ }
+ }
+ d_printf("</select>");
+ d_printf("<input type=button value=\"%s\" onClick=\"swatform.parm_%s.selectedIndex=\'%d\'\">",
+ _("Set Default"), make_parm_name(parm->label),enum_index((int)(parm->def.ivalue),parm->enum_list));
+ break;
+ case P_SEP:
+ break;
+ }
+ d_printf("</td></tr>\n");
+}
+
+/****************************************************************************
+ display a set of parameters for a service
+****************************************************************************/
+static void show_parameters(int snum, int allparameters, unsigned int parm_filter, int printers)
+{
+ int i = 0;
+ struct parm_struct *parm;
+ const char *heading = NULL;
+ const char *last_heading = NULL;
+
+ while ((parm = lp_next_parameter(snum, &i, allparameters))) {
+ if (snum < 0 && parm->class == P_LOCAL && !(parm->flags & FLAG_GLOBAL))
+ continue;
+ if (parm->class == P_SEPARATOR) {
+ heading = parm->label;
+ continue;
+ }
+ if (parm->flags & FLAG_HIDE) continue;
+ if (snum >= 0) {
+ if (printers & !(parm->flags & FLAG_PRINT)) continue;
+ if (!printers & !(parm->flags & FLAG_SHARE)) continue;
+ }
+
+ if (!( parm_filter & FLAG_ADVANCED )) {
+ if (!(parm->flags & FLAG_BASIC)) {
+ void *ptr = parm->ptr;
+
+ if (parm->class == P_LOCAL && snum >= 0) {
+ ptr = lp_local_ptr(snum, ptr);
+ }
+
+ switch (parm->type) {
+ case P_CHAR:
+ if (*(char *)ptr == (char)(parm->def.cvalue)) continue;
+ break;
+
+ case P_LIST:
+ if (!str_list_compare(*(char ***)ptr, (char **)(parm->def.lvalue))) continue;
+ break;
+
+ case P_STRING:
+ case P_USTRING:
+ if (!strcmp(*(char **)ptr,(char *)(parm->def.svalue))) continue;
+ break;
+
+ case P_GSTRING:
+ case P_UGSTRING:
+ if (!strcmp((char *)ptr,(char *)(parm->def.svalue))) continue;
+ break;
+
+ case P_BOOL:
+ case P_BOOLREV:
+ if (*(BOOL *)ptr == (BOOL)(parm->def.bvalue)) continue;
+ break;
+
+ case P_INTEGER:
+ case P_OCTAL:
+ if (*(int *)ptr == (int)(parm->def.ivalue)) continue;
+ break;
+
+
+ case P_ENUM:
+ if (*(int *)ptr == (int)(parm->def.ivalue)) continue;
+ break;
+ case P_SEP:
+ continue;
+ }
+ }
+ if (printers && !(parm->flags & FLAG_PRINT)) continue;
+ }
+
+ if ((parm_filter & FLAG_WIZARD) && !(parm->flags & FLAG_WIZARD)) continue;
+
+ if ((parm_filter & FLAG_ADVANCED) && !(parm->flags & FLAG_ADVANCED)) continue;
+
+ if (heading && heading != last_heading) {
+ d_printf("<tr><td></td></tr><tr><td><b><u>%s</u></b></td></tr>\n", _(heading));
+ last_heading = heading;
+ }
+ show_parameter(snum, parm);
+ }
+}
+
+/****************************************************************************
+ load the smb.conf file into loadparm.
+****************************************************************************/
+static BOOL load_config(BOOL save_def)
+{
+ lp_resetnumservices();
+ return lp_load(dyn_CONFIGFILE,False,save_def,False);
+}
+
+/****************************************************************************
+ write a config file
+****************************************************************************/
+static void write_config(FILE *f, BOOL show_defaults)
+{
+ fprintf(f, "# Samba config file created using SWAT\n");
+ fprintf(f, "# from %s (%s)\n", cgi_remote_host(), cgi_remote_addr());
+ fprintf(f, "# Date: %s\n\n", timestring(False));
+
+ lp_dump(f, show_defaults, iNumNonAutoPrintServices);
+}
+
+/****************************************************************************
+ save and reload the smb.conf config file
+****************************************************************************/
+static int save_reload(int snum)
+{
+ FILE *f;
+ struct stat st;
+
+ f = sys_fopen(dyn_CONFIGFILE,"w");
+ if (!f) {
+ d_printf(_("failed to open %s for writing"), dyn_CONFIGFILE);
+ d_printf("\n");
+ return 0;
+ }
+
+ /* just in case they have used the buggy xinetd to create the file */
+ if (fstat(fileno(f), &st) == 0 &&
+ (st.st_mode & S_IWOTH)) {
+#if defined HAVE_FCHMOD
+ fchmod(fileno(f), S_IWUSR | S_IRUSR | S_IRGRP | S_IROTH);
+#else
+ chmod(dyn_CONFIGFILE, S_IWUSR | S_IRUSR | S_IRGRP | S_IROTH);
+#endif
+ }
+
+ write_config(f, False);
+ if (snum)
+ lp_dump_one(f, False, snum);
+ fclose(f);
+
+ lp_killunused(NULL);
+
+ if (!load_config(False)) {
+ d_printf(_("Can't reload %s"), dyn_CONFIGFILE);
+ d_printf("\n");
+ return 0;
+ }
+ iNumNonAutoPrintServices = lp_numservices();
+ load_printers();
+
+ return 1;
+}
+
+/****************************************************************************
+ commit one parameter
+****************************************************************************/
+static void commit_parameter(int snum, struct parm_struct *parm, const char *v)
+{
+ int i;
+ char *s;
+
+ if (snum < 0 && parm->class == P_LOCAL) {
+ /* this handles the case where we are changing a local
+ variable globally. We need to change the parameter in
+ all shares where it is currently set to the default */
+ for (i=0;i<lp_numservices();i++) {
+ s = lp_servicename(i);
+ if (s && (*s) && lp_is_default(i, parm)) {
+ lp_do_parameter(i, parm->label, v);
+ }
+ }
+ }
+
+ lp_do_parameter(snum, parm->label, v);
+}
+
+/****************************************************************************
+ commit a set of parameters for a service
+****************************************************************************/
+static void commit_parameters(int snum)
+{
+ int i = 0;
+ struct parm_struct *parm;
+ pstring label;
+ const char *v;
+
+ while ((parm = lp_next_parameter(snum, &i, 1))) {
+ slprintf(label, sizeof(label)-1, "parm_%s", make_parm_name(parm->label));
+ if ((v = cgi_variable(label))) {
+ if (parm->flags & FLAG_HIDE) continue;
+ commit_parameter(snum, parm, v);
+ }
+ }
+}
+
+/****************************************************************************
+ spit out the html for a link with an image
+****************************************************************************/
+static void image_link(const char *name, const char *hlink, const char *src)
+{
+ d_printf("<A HREF=\"%s/%s\"><img border=\"0\" src=\"/swat/%s\" alt=\"%s\"></A>\n",
+ cgi_baseurl(), hlink, src, name);
+}
+
+/****************************************************************************
+ display the main navigation controls at the top of each page along
+ with a title
+****************************************************************************/
+static void show_main_buttons(void)
+{
+ char *p;
+
+ if ((p = cgi_user_name()) && strcmp(p, "root")) {
+ d_printf(_("Logged in as <b>%s</b>"), p);
+ d_printf("<p>\n");
+ }
+
+ image_link(_("Home"), "", "images/home.gif");
+ if (have_write_access) {
+ image_link(_("Globals"), "globals", "images/globals.gif");
+ image_link(_("Shares"), "shares", "images/shares.gif");
+ image_link(_("Printers"), "printers", "images/printers.gif");
+ image_link(_("Wizard"), "wizard", "images/wizard.gif");
+ }
+ if (have_read_access) {
+ image_link(_("Status"), "status", "images/status.gif");
+ image_link(_("View Config"), "viewconfig", "images/viewconfig.gif");
+ }
+ image_link(_("Password Management"), "passwd", "images/passwd.gif");
+
+ d_printf("<HR>\n");
+}
+
+/****************************************************************************
+ * Handle Display/Edit Mode CGI
+ ****************************************************************************/
+static void ViewModeBoxes(int mode)
+{
+ d_printf("<p>%s:&nbsp;\n", _("Current View Is"));
+ d_printf("<input type=radio name=\"ViewMode\" value=0 %s>%s\n", ((mode == 0) ? "checked" : ""), _("Basic"));
+ d_printf("<input type=radio name=\"ViewMode\" value=1 %s>%s\n", ((mode == 1) ? "checked" : ""), _("Advanced"));
+ d_printf("<br>%s:&nbsp;\n", _("Change View To"));
+ d_printf("<input type=submit name=\"BasicMode\" value=\"%s\">\n", _("Basic"));
+ d_printf("<input type=submit name=\"AdvMode\" value=\"%s\">\n", _("Advanced"));
+ d_printf("</p><br>\n");
+}
+
+/****************************************************************************
+ display a welcome page
+****************************************************************************/
+static void welcome_page(void)
+{
+ include_html("help/welcome.html");
+}
+
+/****************************************************************************
+ display the current smb.conf
+****************************************************************************/
+static void viewconfig_page(void)
+{
+ int full_view=0;
+
+ if (cgi_variable("full_view")) {
+ full_view = 1;
+ }
+
+ d_printf("<H2>%s</H2>\n", _("Current Config"));
+ d_printf("<form method=post>\n");
+
+ if (full_view) {
+ d_printf("<input type=submit name=\"normal_view\" value=\"%s\">\n", _("Normal View"));
+ } else {
+ d_printf("<input type=submit name=\"full_view\" value=\"%s\">\n", _("Full View"));
+ }
+
+ d_printf("<p><pre>");
+ write_config(stdout, full_view);
+ d_printf("</pre>");
+ d_printf("</form>\n");
+}
+
+/****************************************************************************
+ second screen of the wizard ... Fetch Configuration Parameters
+****************************************************************************/
+static void wizard_params_page(void)
+{
+ unsigned int parm_filter = FLAG_WIZARD;
+
+ /* Here we first set and commit all the parameters that were selected
+ in the previous screen. */
+
+ d_printf("<H2>%s</H2>\n", _("Wizard Parameter Edit Page"));
+
+ if (cgi_variable("Commit")) {
+ commit_parameters(GLOBAL_SECTION_SNUM);
+ save_reload(0);
+ }
+
+ d_printf("<form name=\"swatform\" method=post action=wizard_params>\n");
+
+ if (have_write_access) {
+ d_printf("<input type=submit name=\"Commit\" value=\"Commit Changes\">\n");
+ }
+
+ d_printf("<input type=reset name=\"Reset Values\" value=\"Reset\">\n");
+ d_printf("<p>\n");
+
+ d_printf("<table>\n");
+ show_parameters(GLOBAL_SECTION_SNUM, 1, parm_filter, 0);
+ d_printf("</table>\n");
+ d_printf("</form>\n");
+}
+
+/****************************************************************************
+ Utility to just rewrite the smb.conf file - effectively just cleans it up
+****************************************************************************/
+static void rewritecfg_file(void)
+{
+ commit_parameters(GLOBAL_SECTION_SNUM);
+ save_reload(0);
+ d_printf("<H2>%s</H2>\n", _("Note: smb.conf file has been read and rewritten"));
+}
+
+/****************************************************************************
+ wizard to create/modify the smb.conf file
+****************************************************************************/
+static void wizard_page(void)
+{
+ /* Set some variables to collect data from smb.conf */
+ int role = 0;
+ int winstype = 0;
+ int have_home = -1;
+ int HomeExpo = 0;
+ int SerType = 0;
+
+ if (cgi_variable("Rewrite")) {
+ (void) rewritecfg_file();
+ return;
+ }
+
+ if (cgi_variable("GetWizardParams")){
+ (void) wizard_params_page();
+ return;
+ }
+
+ if (cgi_variable("Commit")){
+ SerType = atoi(cgi_variable("ServerType"));
+ winstype = atoi(cgi_variable("WINSType"));
+ have_home = lp_servicenumber(HOMES_NAME);
+ HomeExpo = atoi(cgi_variable("HomeExpo"));
+
+ /* Plain text passwords are too badly broken - use encrypted passwords only */
+ lp_do_parameter( GLOBAL_SECTION_SNUM, "encrypt passwords", "Yes");
+
+ switch ( SerType ){
+ case 0:
+ /* Stand-alone Server */
+ lp_do_parameter( GLOBAL_SECTION_SNUM, "security", "USER" );
+ lp_do_parameter( GLOBAL_SECTION_SNUM, "domain logons", "No" );
+ break;
+ case 1:
+ /* Domain Member */
+ lp_do_parameter( GLOBAL_SECTION_SNUM, "security", "DOMAIN" );
+ lp_do_parameter( GLOBAL_SECTION_SNUM, "domain logons", "No" );
+ break;
+ case 2:
+ /* Domain Controller */
+ lp_do_parameter( GLOBAL_SECTION_SNUM, "security", "USER" );
+ lp_do_parameter( GLOBAL_SECTION_SNUM, "domain logons", "Yes" );
+ break;
+ }
+ switch ( winstype ) {
+ case 0:
+ lp_do_parameter( GLOBAL_SECTION_SNUM, "wins support", "No" );
+ lp_do_parameter( GLOBAL_SECTION_SNUM, "wins server", "" );
+ break;
+ case 1:
+ lp_do_parameter( GLOBAL_SECTION_SNUM, "wins support", "Yes" );
+ lp_do_parameter( GLOBAL_SECTION_SNUM, "wins server", "" );
+ break;
+ case 2:
+ lp_do_parameter( GLOBAL_SECTION_SNUM, "wins support", "No" );
+ lp_do_parameter( GLOBAL_SECTION_SNUM, "wins server", cgi_variable("WINSAddr"));
+ break;
+ }
+
+ /* Have to create Homes share? */
+ if ((HomeExpo == 1) && (have_home == -1)) {
+ pstring unix_share;
+
+ pstrcpy(unix_share,HOMES_NAME);
+ load_config(False);
+ lp_copy_service(GLOBAL_SECTION_SNUM, unix_share);
+ iNumNonAutoPrintServices = lp_numservices();
+ have_home = lp_servicenumber(HOMES_NAME);
+ lp_do_parameter( have_home, "read only", "No");
+ lp_do_parameter( have_home, "valid users", "%S");
+ lp_do_parameter( have_home, "browseable", "No");
+ commit_parameters(have_home);
+ }
+
+ /* Need to Delete Homes share? */
+ if ((HomeExpo == 0) && (have_home != -1)) {
+ lp_remove_service(have_home);
+ have_home = -1;
+ }
+
+ commit_parameters(GLOBAL_SECTION_SNUM);
+ save_reload(0);
+ }
+ else
+ {
+ /* Now determine smb.conf WINS settings */
+ if (lp_wins_support())
+ winstype = 1;
+ if (lp_wins_server_list() && strlen(*lp_wins_server_list()))
+ winstype = 2;
+
+
+ /* Do we have a homes share? */
+ have_home = lp_servicenumber(HOMES_NAME);
+ }
+ if ((winstype == 2) && lp_wins_support())
+ winstype = 3;
+
+ role = lp_server_role();
+
+ /* Here we go ... */
+ d_printf("<H2>%s</H2>\n", _("Samba Configuration Wizard"));
+ d_printf("<form method=post action=wizard>\n");
+
+ if (have_write_access) {
+ d_printf("%s\n", _("The \"Rewrite smb.conf file\" button will clear the smb.conf file of all default values and of comments."));
+ d_printf("%s", _("The same will happen if you press the commit button."));
+ d_printf("<br><br>\n");
+ d_printf("<center>");
+ d_printf("<input type=submit name=\"Rewrite\" value=\"%s\"> &nbsp;&nbsp;",_("Rewrite smb.conf file"));
+ d_printf("<input type=submit name=\"Commit\" value=\"%s\"> &nbsp;&nbsp;",_("Commit"));
+ d_printf("<input type=submit name=\"GetWizardParams\" value=\"%s\">", _("Edit Parameter Values"));
+ d_printf("</center>\n");
+ }
+
+ d_printf("<hr>");
+ d_printf("<center><table border=0>");
+ d_printf("<tr><td><b>%s:&nbsp;</b></td>\n", _("Server Type"));
+ d_printf("<td><input type=radio name=\"ServerType\" value=\"0\" %s> %s&nbsp;</td>", ((role == ROLE_STANDALONE) ? "checked" : ""), _("Stand Alone"));
+ d_printf("<td><input type=radio name=\"ServerType\" value=\"1\" %s> %s&nbsp;</td>", ((role == ROLE_DOMAIN_MEMBER) ? "checked" : ""), _("Domain Member"));
+ d_printf("<td><input type=radio name=\"ServerType\" value=\"2\" %s> %s&nbsp;</td>", ((role == ROLE_DOMAIN_PDC) ? "checked" : ""), _("Domain Controller"));
+ d_printf("</tr>\n");
+ if (role == ROLE_DOMAIN_BDC) {
+ d_printf("<tr><td></td><td colspan=3><font color=\"#ff0000\">%s</font></td></tr>\n", _("Unusual Type in smb.conf - Please Select New Mode"));
+ }
+ d_printf("<tr><td><b>%s:&nbsp;</b></td>\n", _("Configure WINS As"));
+ d_printf("<td><input type=radio name=\"WINSType\" value=\"0\" %s> %s&nbsp;</td>", ((winstype == 0) ? "checked" : ""), _("Not Used"));
+ d_printf("<td><input type=radio name=\"WINSType\" value=\"1\" %s> %s&nbsp;</td>", ((winstype == 1) ? "checked" : ""), _("Server for client use"));
+ d_printf("<td><input type=radio name=\"WINSType\" value=\"2\" %s> %s&nbsp;</td>", ((winstype == 2) ? "checked" : ""), _("Client of another WINS server"));
+ d_printf("</tr>\n");
+ d_printf("<tr><td></td><td></td><td></td><td>%s&nbsp;<input type=text size=\"16\" name=\"WINSAddr\" value=\"", _("Remote WINS Server"));
+
+ /* Print out the list of wins servers */
+ if(lp_wins_server_list()) {
+ int i;
+ const char **wins_servers = lp_wins_server_list();
+ for(i = 0; wins_servers[i]; i++) d_printf("%s ", wins_servers[i]);
+ }
+
+ d_printf("\"></td></tr>\n");
+ if (winstype == 3) {
+ d_printf("<tr><td></td><td colspan=3><font color=\"#ff0000\">%s</font></td></tr>\n", _("Error: WINS Server Mode and WINS Support both set in smb.conf"));
+ d_printf("<tr><td></td><td colspan=3><font color=\"#ff0000\">%s</font></td></tr>\n", _("Please Select desired WINS mode above."));
+ }
+ d_printf("<tr><td><b>%s:&nbsp;</b></td>\n", _("Expose Home Directories"));
+ d_printf("<td><input type=radio name=\"HomeExpo\" value=\"1\" %s> Yes</td>", (have_home == -1) ? "" : "checked ");
+ d_printf("<td><input type=radio name=\"HomeExpo\" value=\"0\" %s> No</td>", (have_home == -1 ) ? "checked" : "");
+ d_printf("<td></td></tr>\n");
+
+ /* Enable this when we are ready ....
+ * d_printf("<tr><td><b>%s:&nbsp;</b></td>\n", _("Is Print Server"));
+ * d_printf("<td><input type=radio name=\"PtrSvr\" value=\"1\" %s> Yes</td>");
+ * d_printf("<td><input type=radio name=\"PtrSvr\" value=\"0\" %s> No</td>");
+ * d_printf("<td></td></tr>\n");
+ */
+
+ d_printf("</table></center>");
+ d_printf("<hr>");
+
+ d_printf("%s\n", _("The above configuration options will set multiple parameters and will generally assist with rapid Samba deployment."));
+ d_printf("</form>\n");
+}
+
+
+/****************************************************************************
+ display a globals editing page
+****************************************************************************/
+static void globals_page(void)
+{
+ unsigned int parm_filter = FLAG_BASIC;
+ int mode = 0;
+
+ d_printf("<H2>%s</H2>\n", _("Global Parameters"));
+
+ if (cgi_variable("Commit")) {
+ commit_parameters(GLOBAL_SECTION_SNUM);
+ save_reload(0);
+ }
+
+ if ( cgi_variable("ViewMode") )
+ mode = atoi(cgi_variable("ViewMode"));
+ if ( cgi_variable("BasicMode"))
+ mode = 0;
+ if ( cgi_variable("AdvMode"))
+ mode = 1;
+
+ d_printf("<form name=\"swatform\" method=post action=globals>\n");
+
+ ViewModeBoxes( mode );
+ switch ( mode ) {
+ case 0:
+ parm_filter = FLAG_BASIC;
+ break;
+ case 1:
+ parm_filter = FLAG_ADVANCED;
+ break;
+ }
+ d_printf("<br>\n");
+ if (have_write_access) {
+ d_printf("<input type=submit name=\"Commit\" value=\"%s\">\n",
+ _("Commit Changes"));
+ }
+
+ d_printf("<input type=reset name=\"Reset Values\" value=\"%s\">\n",
+ _("Reset Values"));
+
+ d_printf("<p>\n");
+ d_printf("<table>\n");
+ show_parameters(GLOBAL_SECTION_SNUM, 1, parm_filter, 0);
+ d_printf("</table>\n");
+ d_printf("</form>\n");
+}
+
+/****************************************************************************
+ display a shares editing page. share is in unix codepage, and must be in
+ dos codepage. FIXME !!! JRA.
+****************************************************************************/
+static void shares_page(void)
+{
+ const char *share = cgi_variable("share");
+ char *s;
+ int snum = -1;
+ int i;
+ int mode = 0;
+ unsigned int parm_filter = FLAG_BASIC;
+
+ if (share)
+ snum = lp_servicenumber(share);
+
+ d_printf("<H2>%s</H2>\n", _("Share Parameters"));
+
+ if (cgi_variable("Commit") && snum >= 0) {
+ commit_parameters(snum);
+ save_reload(0);
+ }
+
+ if (cgi_variable("Delete") && snum >= 0) {
+ lp_remove_service(snum);
+ save_reload(0);
+ share = NULL;
+ snum = -1;
+ }
+
+ if (cgi_variable("createshare") && (share=cgi_variable("newshare"))) {
+ load_config(False);
+ lp_copy_service(GLOBAL_SECTION_SNUM, share);
+ iNumNonAutoPrintServices = lp_numservices();
+ save_reload(0);
+ snum = lp_servicenumber(share);
+ }
+
+ d_printf("<FORM name=\"swatform\" method=post>\n");
+
+ d_printf("<table>\n");
+
+ if ( cgi_variable("ViewMode") )
+ mode = atoi(cgi_variable("ViewMode"));
+ if ( cgi_variable("BasicMode"))
+ mode = 0;
+ if ( cgi_variable("AdvMode"))
+ mode = 1;
+
+ ViewModeBoxes( mode );
+ switch ( mode ) {
+ case 0:
+ parm_filter = FLAG_BASIC;
+ break;
+ case 1:
+ parm_filter = FLAG_ADVANCED;
+ break;
+ }
+ d_printf("<br><tr>\n");
+ d_printf("<td><input type=submit name=selectshare value=\"%s\"></td>\n", _("Choose Share"));
+ d_printf("<td><select name=share>\n");
+ if (snum < 0)
+ d_printf("<option value=\" \"> \n");
+ for (i=0;i<lp_numservices();i++) {
+ s = lp_servicename(i);
+ if (s && (*s) && strcmp(s,"IPC$") && !lp_print_ok(i)) {
+ d_printf("<option %s value=\"%s\">%s\n",
+ (share && strcmp(share,s)==0)?"SELECTED":"",
+ s, s);
+ }
+ }
+ d_printf("</select></td>\n");
+ if (have_write_access) {
+ d_printf("<td><input type=submit name=\"Delete\" value=\"%s\"></td>\n", _("Delete Share"));
+ }
+ d_printf("</tr>\n");
+ d_printf("</table>");
+ d_printf("<table>");
+ if (have_write_access) {
+ d_printf("<tr>\n");
+ d_printf("<td><input type=submit name=createshare value=\"%s\"></td>\n", _("Create Share"));
+ d_printf("<td><input type=text size=30 name=newshare></td></tr>\n");
+ }
+ d_printf("</table>");
+
+
+ if (snum >= 0) {
+ if (have_write_access) {
+ d_printf("<input type=submit name=\"Commit\" value=\"%s\">\n", _("Commit Changes"));
+ }
+
+ d_printf("<input type=reset name=\"Reset Values\" value=\"%s\">\n", _("Reset Values"));
+ d_printf("<p>\n");
+ }
+
+ if (snum >= 0) {
+ d_printf("<table>\n");
+ show_parameters(snum, 1, parm_filter, 0);
+ d_printf("</table>\n");
+ }
+
+ d_printf("</FORM>\n");
+}
+
+/*************************************************************
+change a password either locally or remotely
+*************************************************************/
+static BOOL change_password(const char *remote_machine, const char *user_name,
+ const char *old_passwd, const char *new_passwd,
+ int local_flags)
+{
+ BOOL ret = False;
+ pstring err_str;
+ pstring msg_str;
+
+ if (demo_mode) {
+ d_printf("%s\n<p>", _("password change in demo mode rejected"));
+ return False;
+ }
+
+ if (remote_machine != NULL) {
+ ret = remote_password_change(remote_machine, user_name, old_passwd,
+ new_passwd, err_str, sizeof(err_str));
+ if(*err_str)
+ d_printf("%s\n<p>", err_str);
+ return ret;
+ }
+
+ if(!initialize_password_db(True)) {
+ d_printf("%s\n<p>", _("Can't setup password database vectors."));
+ return False;
+ }
+
+ ret = local_password_change(user_name, local_flags, new_passwd, err_str, sizeof(err_str),
+ msg_str, sizeof(msg_str));
+
+ if(*msg_str)
+ d_printf("%s\n<p>", msg_str);
+ if(*err_str)
+ d_printf("%s\n<p>", err_str);
+
+ return ret;
+}
+
+/****************************************************************************
+ do the stuff required to add or change a password
+****************************************************************************/
+static void chg_passwd(void)
+{
+ const char *host;
+ BOOL rslt;
+ int local_flags = 0;
+
+ /* Make sure users name has been specified */
+ if (strlen(cgi_variable(SWAT_USER)) == 0) {
+ d_printf("<p>%s\n", _(" Must specify \"User Name\" "));
+ return;
+ }
+
+ /*
+ * smbpasswd doesn't require anything but the users name to delete, disable or enable the user,
+ * so if that's what we're doing, skip the rest of the checks
+ */
+ if (!cgi_variable(DISABLE_USER_FLAG) && !cgi_variable(ENABLE_USER_FLAG) && !cgi_variable(DELETE_USER_FLAG)) {
+
+ /*
+ * If current user is not root, make sure old password has been specified
+ * If REMOTE change, even root must provide old password
+ */
+ if (((!am_root()) && (strlen( cgi_variable(OLD_PSWD)) <= 0)) ||
+ ((cgi_variable(CHG_R_PASSWD_FLAG)) && (strlen( cgi_variable(OLD_PSWD)) <= 0))) {
+ d_printf("<p>%s\n", _(" Must specify \"Old Password\" "));
+ return;
+ }
+
+ /* If changing a users password on a remote hosts we have to know what host */
+ if ((cgi_variable(CHG_R_PASSWD_FLAG)) && (strlen( cgi_variable(RHOST)) <= 0)) {
+ d_printf("<p>%s\n", _(" Must specify \"Remote Machine\" "));
+ return;
+ }
+
+ /* Make sure new passwords have been specified */
+ if ((strlen( cgi_variable(NEW_PSWD)) <= 0) ||
+ (strlen( cgi_variable(NEW2_PSWD)) <= 0)) {
+ d_printf("<p>%s\n", _(" Must specify \"New, and Re-typed Passwords\" "));
+ return;
+ }
+
+ /* Make sure new passwords was typed correctly twice */
+ if (strcmp(cgi_variable(NEW_PSWD), cgi_variable(NEW2_PSWD)) != 0) {
+ d_printf("<p>%s\n", _(" Re-typed password didn't match new password "));
+ return;
+ }
+ }
+
+ if (cgi_variable(CHG_R_PASSWD_FLAG)) {
+ host = cgi_variable(RHOST);
+ } else if (am_root()) {
+ host = NULL;
+ } else {
+ host = "127.0.0.1";
+ }
+
+ /*
+ * Set up the local flags.
+ */
+
+ local_flags |= (cgi_variable(ADD_USER_FLAG) ? LOCAL_ADD_USER : 0);
+ local_flags |= (cgi_variable(DELETE_USER_FLAG) ? LOCAL_DELETE_USER : 0);
+ local_flags |= (cgi_variable(ENABLE_USER_FLAG) ? LOCAL_ENABLE_USER : 0);
+ local_flags |= (cgi_variable(DISABLE_USER_FLAG) ? LOCAL_DISABLE_USER : 0);
+
+ rslt = change_password(host,
+ cgi_variable(SWAT_USER),
+ cgi_variable(OLD_PSWD), cgi_variable(NEW_PSWD),
+ local_flags);
+
+ if(local_flags == 0) {
+ d_printf("<p>");
+ if (rslt == True) {
+ d_printf(_(" The passwd for '%s' has been changed."), cgi_variable(SWAT_USER));
+ d_printf("\n");
+ } else {
+ d_printf(_(" The passwd for '%s' has NOT been changed."), cgi_variable(SWAT_USER));
+ d_printf("\n");
+ }
+ }
+
+ return;
+}
+
+/****************************************************************************
+ display a password editing page
+****************************************************************************/
+static void passwd_page(void)
+{
+ const char *new_name = cgi_user_name();
+
+ /*
+ * After the first time through here be nice. If the user
+ * changed the User box text to another users name, remember it.
+ */
+ if (cgi_variable(SWAT_USER)) {
+ new_name = cgi_variable(SWAT_USER);
+ }
+
+ if (!new_name) new_name = "";
+
+ d_printf("<H2>%s</H2>\n", _("Server Password Management"));
+
+ d_printf("<FORM name=\"swatform\" method=post>\n");
+
+ d_printf("<table>\n");
+
+ /*
+ * Create all the dialog boxes for data collection
+ */
+ d_printf("<tr><td> %s : </td>\n", _("User Name"));
+ d_printf("<td><input type=text size=30 name=%s value=%s></td></tr> \n", SWAT_USER, new_name);
+ if (!am_root()) {
+ d_printf("<tr><td> %s : </td>\n", _("Old Password"));
+ d_printf("<td><input type=password size=30 name=%s></td></tr> \n",OLD_PSWD);
+ }
+ d_printf("<tr><td> %s : </td>\n", _("New Password"));
+ d_printf("<td><input type=password size=30 name=%s></td></tr>\n",NEW_PSWD);
+ d_printf("<tr><td> %s : </td>\n", _("Re-type New Password"));
+ d_printf("<td><input type=password size=30 name=%s></td></tr>\n",NEW2_PSWD);
+ d_printf("</table>\n");
+
+ /*
+ * Create all the control buttons for requesting action
+ */
+ d_printf("<input type=submit name=%s value=\"%s\">\n",
+ CHG_S_PASSWD_FLAG, _("Change Password"));
+ if (demo_mode || am_root()) {
+ d_printf("<input type=submit name=%s value=\"%s\">\n",
+ ADD_USER_FLAG, _("Add New User"));
+ d_printf("<input type=submit name=%s value=\"%s\">\n",
+ DELETE_USER_FLAG, _("Delete User"));
+ d_printf("<input type=submit name=%s value=\"%s\">\n",
+ DISABLE_USER_FLAG, _("Disable User"));
+ d_printf("<input type=submit name=%s value=\"%s\">\n",
+ ENABLE_USER_FLAG, _("Enable User"));
+ }
+ d_printf("<p></FORM>\n");
+
+ /*
+ * Do some work if change, add, disable or enable was
+ * requested. It could be this is the first time through this
+ * code, so there isn't anything to do. */
+ if ((cgi_variable(CHG_S_PASSWD_FLAG)) || (cgi_variable(ADD_USER_FLAG)) || (cgi_variable(DELETE_USER_FLAG)) ||
+ (cgi_variable(DISABLE_USER_FLAG)) || (cgi_variable(ENABLE_USER_FLAG))) {
+ chg_passwd();
+ }
+
+ d_printf("<H2>%s</H2>\n", _("Client/Server Password Management"));
+
+ d_printf("<FORM name=\"swatform\" method=post>\n");
+
+ d_printf("<table>\n");
+
+ /*
+ * Create all the dialog boxes for data collection
+ */
+ d_printf("<tr><td> %s : </td>\n", _("User Name"));
+ d_printf("<td><input type=text size=30 name=%s value=%s></td></tr>\n",SWAT_USER, new_name);
+ d_printf("<tr><td> %s : </td>\n", _("Old Password"));
+ d_printf("<td><input type=password size=30 name=%s></td></tr>\n",OLD_PSWD);
+ d_printf("<tr><td> %s : </td>\n", _("New Password"));
+ d_printf("<td><input type=password size=30 name=%s></td></tr>\n",NEW_PSWD);
+ d_printf("<tr><td> %s : </td>\n", _("Re-type New Password"));
+ d_printf("<td><input type=password size=30 name=%s></td></tr>\n",NEW2_PSWD);
+ d_printf("<tr><td> %s : </td>\n", _("Remote Machine"));
+ d_printf("<td><input type=text size=30 name=%s></td></tr>\n",RHOST);
+
+ d_printf("</table>");
+
+ /*
+ * Create all the control buttons for requesting action
+ */
+ d_printf("<input type=submit name=%s value=\"%s\">",
+ CHG_R_PASSWD_FLAG, _("Change Password"));
+
+ d_printf("<p></FORM>\n");
+
+ /*
+ * Do some work if a request has been made to change the
+ * password somewhere other than the server. It could be this
+ * is the first time through this code, so there isn't
+ * anything to do. */
+ if (cgi_variable(CHG_R_PASSWD_FLAG)) {
+ chg_passwd();
+ }
+
+}
+
+/****************************************************************************
+ display a printers editing page
+****************************************************************************/
+static void printers_page(void)
+{
+ const char *share = cgi_variable("share");
+ char *s;
+ int snum=-1;
+ int i;
+ int mode = 0;
+ unsigned int parm_filter = FLAG_BASIC;
+
+ if (share)
+ snum = lp_servicenumber(share);
+
+ d_printf("<H2>%s</H2>\n", _("Printer Parameters"));
+
+ d_printf("<H3>%s</H3>\n", _("Important Note:"));
+ d_printf(_("Printer names marked with [*] in the Choose Printer drop-down box "));
+ d_printf(_("are autoloaded printers from "));
+ d_printf("<A HREF=\"/swat/help/smb.conf.5.html#printcapname\" target=\"docs\">%s</A>\n", _("Printcap Name"));
+ d_printf("%s\n", _("Attempting to delete these printers from SWAT will have no effect."));
+
+ if (cgi_variable("Commit") && snum >= 0) {
+ commit_parameters(snum);
+ if (snum >= iNumNonAutoPrintServices)
+ save_reload(snum);
+ else
+ save_reload(0);
+ }
+
+ if (cgi_variable("Delete") && snum >= 0) {
+ lp_remove_service(snum);
+ save_reload(0);
+ share = NULL;
+ snum = -1;
+ }
+
+ if (cgi_variable("createshare") && (share=cgi_variable("newshare"))) {
+ load_config(False);
+ lp_copy_service(GLOBAL_SECTION_SNUM, share);
+ iNumNonAutoPrintServices = lp_numservices();
+ snum = lp_servicenumber(share);
+ lp_do_parameter(snum, "print ok", "Yes");
+ save_reload(0);
+ snum = lp_servicenumber(share);
+ }
+
+ d_printf("<FORM name=\"swatform\" method=post>\n");
+
+ if ( cgi_variable("ViewMode") )
+ mode = atoi(cgi_variable("ViewMode"));
+ if ( cgi_variable("BasicMode"))
+ mode = 0;
+ if ( cgi_variable("AdvMode"))
+ mode = 1;
+
+ ViewModeBoxes( mode );
+ switch ( mode ) {
+ case 0:
+ parm_filter = FLAG_BASIC;
+ break;
+ case 1:
+ parm_filter = FLAG_ADVANCED;
+ break;
+ }
+ d_printf("<table>\n");
+ d_printf("<tr><td><input type=submit name=\"selectshare\" value=\"%s\"></td>\n", _("Choose Printer"));
+ d_printf("<td><select name=\"share\">\n");
+ if (snum < 0 || !lp_print_ok(snum))
+ d_printf("<option value=\" \"> \n");
+ for (i=0;i<lp_numservices();i++) {
+ s = lp_servicename(i);
+ if (s && (*s) && strcmp(s,"IPC$") && lp_print_ok(i)) {
+ if (i >= iNumNonAutoPrintServices)
+ d_printf("<option %s value=\"%s\">[*]%s\n",
+ (share && strcmp(share,s)==0)?"SELECTED":"",
+ s, s);
+ else
+ d_printf("<option %s value=\"%s\">%s\n",
+ (share && strcmp(share,s)==0)?"SELECTED":"",
+ s, s);
+ }
+ }
+ d_printf("</select></td>");
+ if (have_write_access) {
+ d_printf("<td><input type=submit name=\"Delete\" value=\"%s\"></td>\n", _("Delete Printer"));
+ }
+ d_printf("</tr>");
+ d_printf("</table>\n");
+
+ if (have_write_access) {
+ d_printf("<table>\n");
+ d_printf("<tr><td><input type=submit name=\"createshare\" value=\"%s\"></td>\n", _("Create Printer"));
+ d_printf("<td><input type=text size=30 name=\"newshare\"></td></tr>\n");
+ d_printf("</table>");
+ }
+
+
+ if (snum >= 0) {
+ if (have_write_access) {
+ d_printf("<input type=submit name=\"Commit\" value=\"%s\">\n", _("Commit Changes"));
+ }
+ d_printf("<input type=reset name=\"Reset Values\" value=\"%s\">\n", _("Reset Values"));
+ d_printf("<p>\n");
+ }
+
+ if (snum >= 0) {
+ d_printf("<table>\n");
+ show_parameters(snum, 1, parm_filter, 1);
+ d_printf("</table>\n");
+ }
+ d_printf("</FORM>\n");
+}
+
+
+/**
+ * main function for SWAT.
+ **/
+ int main(int argc, char *argv[])
+{
+ int opt;
+ const char *page;
+ poptContext pc;
+ struct poptOption long_options[] = {
+ POPT_AUTOHELP
+ { "disable-authentication", 'a', POPT_ARG_VAL, &demo_mode, True, "Disable authentication (demo mode)" },
+ POPT_COMMON_SAMBA
+ POPT_TABLEEND
+ };
+
+ fault_setup(NULL);
+ umask(S_IWGRP | S_IWOTH);
+
+#if defined(HAVE_SET_AUTH_PARAMETERS)
+ set_auth_parameters(argc, argv);
+#endif /* HAVE_SET_AUTH_PARAMETERS */
+
+ /* just in case it goes wild ... */
+ alarm(300);
+
+ setlinebuf(stdout);
+
+ /* we don't want any SIGPIPE messages */
+ BlockSignals(True,SIGPIPE);
+
+ dbf = x_fopen("/dev/null", O_WRONLY, 0);
+ if (!dbf) dbf = x_stderr;
+
+ /* we don't want stderr screwing us up */
+ close(2);
+ open("/dev/null", O_WRONLY);
+
+ pc = poptGetContext("swat", argc, (const char **) argv, long_options, 0);
+
+ /* Parse command line options */
+
+ while((opt = poptGetNextOpt(pc)) != -1) { }
+
+ poptFreeContext(pc);
+
+ setup_logging(argv[0],False);
+ load_config(True);
+ iNumNonAutoPrintServices = lp_numservices();
+ load_printers();
+
+ cgi_setup(dyn_SWATDIR, !demo_mode);
+
+ print_header();
+
+ cgi_load_variables();
+
+ if (!file_exist(dyn_CONFIGFILE, NULL)) {
+ have_read_access = True;
+ have_write_access = True;
+ } else {
+ /* check if the authenticated user has write access - if not then
+ don't show write options */
+ have_write_access = (access(dyn_CONFIGFILE,W_OK) == 0);
+
+ /* if the user doesn't have read access to smb.conf then
+ don't let them view it */
+ have_read_access = (access(dyn_CONFIGFILE,R_OK) == 0);
+ }
+
+ show_main_buttons();
+
+ page = cgi_pathinfo();
+
+ /* Root gets full functionality */
+ if (have_read_access && strcmp(page, "globals")==0) {
+ globals_page();
+ } else if (have_read_access && strcmp(page,"shares")==0) {
+ shares_page();
+ } else if (have_read_access && strcmp(page,"printers")==0) {
+ printers_page();
+ } else if (have_read_access && strcmp(page,"status")==0) {
+ status_page();
+ } else if (have_read_access && strcmp(page,"viewconfig")==0) {
+ viewconfig_page();
+ } else if (strcmp(page,"passwd")==0) {
+ passwd_page();
+ } else if (have_read_access && strcmp(page,"wizard")==0) {
+ wizard_page();
+ } else if (have_read_access && strcmp(page,"wizard_params")==0) {
+ wizard_params_page();
+ } else if (have_read_access && strcmp(page,"rewritecfg")==0) {
+ rewritecfg_file();
+ } else {
+ welcome_page();
+ }
+
+ print_footer();
+ return 0;
+}
+
+/** @} **/
diff --git a/source/wrepld/parser.c b/source/wrepld/parser.c
new file mode 100644
index 00000000000..b619cb0cef5
--- /dev/null
+++ b/source/wrepld/parser.c
@@ -0,0 +1,759 @@
+/*
+ * Unix SMB/CIFS implementation.
+ * RPC Pipe client / server routines
+ * Copyright (C) Jean François Micouleau 1998-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 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 "wins_repl.h"
+
+extern TALLOC_CTX *mem_ctx;
+
+/****************************************************************************
+grow the send buffer if necessary
+****************************************************************************/
+BOOL grow_buffer(struct BUFFER *buffer, int more)
+{
+ char *temp;
+
+ DEBUG(10,("grow_buffer: size is: %d offet is:%d growing by %d\n", buffer->length, buffer->offset, more));
+
+ /* grow by at least 256 bytes */
+ if (more<256)
+ more=256;
+
+ if (buffer->offset+more >= buffer->length) {
+ temp=(char *)talloc_realloc(mem_ctx, buffer->buffer, sizeof(char)* (buffer->length+more) );
+ if (temp==NULL) {
+ DEBUG(0,("grow_buffer: can't grow buffer\n"));
+ return False;
+ }
+ buffer->length+=more;
+ buffer->buffer=temp;
+ }
+
+ return True;
+}
+
+/****************************************************************************
+check if the buffer has that much data
+****************************************************************************/
+static BOOL check_buffer(struct BUFFER *buffer, int more)
+{
+ DEBUG(10,("check_buffer: size is: %d offet is:%d growing by %d\n", buffer->length, buffer->offset, more));
+
+ if (buffer->offset+more > buffer->length) {
+ DEBUG(10,("check_buffer: buffer smaller than requested, size is: %d needed: %d\n", buffer->length, buffer->offset+more));
+ return False;
+ }
+
+ return True;
+}
+
+/****************************************************************************
+decode a WINS_OWNER struct
+****************************************************************************/
+static void decode_wins_owner(struct BUFFER *inbuf, WINS_OWNER *wins_owner)
+{
+ if(!check_buffer(inbuf, 24))
+ return;
+
+ wins_owner->address.s_addr=IVAL(inbuf->buffer, inbuf->offset);
+ wins_owner->max_version=((SMB_BIG_UINT)RIVAL(inbuf->buffer, inbuf->offset+4))<<32;
+ wins_owner->max_version|=RIVAL(inbuf->buffer, inbuf->offset+8);
+ wins_owner->min_version=((SMB_BIG_UINT)RIVAL(inbuf->buffer, inbuf->offset+12))<<32;
+ wins_owner->min_version|=RIVAL(inbuf->buffer, inbuf->offset+16);
+ wins_owner->type=RIVAL(inbuf->buffer, inbuf->offset+20);
+ inbuf->offset+=24;
+
+}
+
+/****************************************************************************
+decode a WINS_NAME struct
+****************************************************************************/
+static void decode_wins_name(struct BUFFER *outbuf, WINS_NAME *wins_name)
+{
+ char *p;
+ int i;
+
+ if(!check_buffer(outbuf, 40))
+ return;
+
+ wins_name->name_len=RIVAL(outbuf->buffer, outbuf->offset);
+ outbuf->offset+=4;
+ memcpy(wins_name->name,outbuf->buffer+outbuf->offset, 15);
+ wins_name->name[15]='\0';
+ if((p = strchr(wins_name->name,' ')) != NULL)
+ *p = 0;
+
+ outbuf->offset+=15;
+
+ wins_name->type=(int)outbuf->buffer[outbuf->offset++];
+
+ /*
+ * fix to bug in WINS replication,
+ * present in all versions including W2K SP2 !
+ */
+ if (wins_name->name[0]==0x1B) {
+ wins_name->name[0]=(char)wins_name->type;
+ wins_name->type=0x1B;
+ }
+
+ wins_name->empty=RIVAL(outbuf->buffer, outbuf->offset);
+ outbuf->offset+=4;
+
+ wins_name->name_flag=RIVAL(outbuf->buffer, outbuf->offset);
+ outbuf->offset+=4;
+ wins_name->group_flag=RIVAL(outbuf->buffer, outbuf->offset);
+ outbuf->offset+=4;
+ wins_name->id=((SMB_BIG_UINT)RIVAL(outbuf->buffer, outbuf->offset))<<32;
+ outbuf->offset+=4;
+ wins_name->id|=RIVAL(outbuf->buffer, outbuf->offset);
+ outbuf->offset+=4;
+
+ /* special groups have multiple address */
+ if (wins_name->name_flag & 2) {
+ if(!check_buffer(outbuf, 4))
+ return;
+ wins_name->num_ip=IVAL(outbuf->buffer, outbuf->offset);
+ outbuf->offset+=4;
+ }
+ else
+ wins_name->num_ip=1;
+
+ if(!check_buffer(outbuf, 4))
+ return;
+ wins_name->owner.s_addr=IVAL(outbuf->buffer, outbuf->offset);
+ outbuf->offset+=4;
+
+ if (wins_name->name_flag & 2) {
+ wins_name->others=(struct in_addr *)talloc(mem_ctx, sizeof(struct in_addr)*wins_name->num_ip);
+ if (wins_name->others==NULL)
+ return;
+
+ if(!check_buffer(outbuf, 4*wins_name->num_ip))
+ return;
+ for (i=0; i<wins_name->num_ip; i++) {
+ wins_name->others[i].s_addr=IVAL(outbuf->buffer, outbuf->offset);
+ outbuf->offset+=4;
+ }
+ }
+
+ if(!check_buffer(outbuf, 4))
+ return;
+ wins_name->foo=RIVAL(outbuf->buffer, outbuf->offset);
+ outbuf->offset+=4;
+
+}
+
+/****************************************************************************
+decode a update notification request
+****************************************************************************/
+static void decode_update_notify_request(struct BUFFER *inbuf, UPDATE_NOTIFY_REQUEST *un_rq)
+{
+ int i;
+
+ if(!check_buffer(inbuf, 4))
+ return;
+ un_rq->partner_count=RIVAL(inbuf->buffer, inbuf->offset);
+ inbuf->offset+=4;
+
+ un_rq->wins_owner=(WINS_OWNER *)talloc(mem_ctx, un_rq->partner_count*sizeof(WINS_OWNER));
+ if (un_rq->wins_owner==NULL)
+ return;
+
+ for (i=0; i<un_rq->partner_count; i++)
+ decode_wins_owner(inbuf, &un_rq->wins_owner[i]);
+
+ if(!check_buffer(inbuf, 4))
+ return;
+ un_rq->initiating_wins_server.s_addr=IVAL(inbuf->buffer, inbuf->offset);
+ inbuf->offset+=4;
+}
+
+/****************************************************************************
+decode a send entries request
+****************************************************************************/
+static void decode_send_entries_request(struct BUFFER *inbuf, SEND_ENTRIES_REQUEST *se_rq)
+{
+ decode_wins_owner(inbuf, &se_rq->wins_owner);
+}
+
+/****************************************************************************
+decode a send entries reply
+****************************************************************************/
+static void decode_send_entries_reply(struct BUFFER *inbuf, SEND_ENTRIES_REPLY *se_rp)
+{
+ int i;
+
+ if(!check_buffer(inbuf, 4))
+ return;
+ se_rp->max_names = RIVAL(inbuf->buffer, inbuf->offset);
+ inbuf->offset+=4;
+
+ se_rp->wins_name=(WINS_NAME *)talloc(mem_ctx, se_rp->max_names*sizeof(WINS_NAME));
+ if (se_rp->wins_name==NULL)
+ return;
+
+ for (i=0; i<se_rp->max_names; i++)
+ decode_wins_name(inbuf, &se_rp->wins_name[i]);
+}
+
+/****************************************************************************
+decode a add version number map table reply
+****************************************************************************/
+static void decode_add_version_number_map_table_reply(struct BUFFER *inbuf, AVMT_REP *avmt_rep)
+{
+ int i;
+
+ if(!check_buffer(inbuf, 4))
+ return;
+
+ avmt_rep->partner_count=RIVAL(inbuf->buffer, inbuf->offset);
+ inbuf->offset+=4;
+
+ avmt_rep->wins_owner=(WINS_OWNER *)talloc(mem_ctx, avmt_rep->partner_count*sizeof(WINS_OWNER));
+ if (avmt_rep->wins_owner==NULL)
+ return;
+
+ for (i=0; i<avmt_rep->partner_count; i++)
+ decode_wins_owner(inbuf, &avmt_rep->wins_owner[i]);
+
+ if(!check_buffer(inbuf, 4))
+ return;
+ avmt_rep->initiating_wins_server.s_addr=IVAL(inbuf->buffer, inbuf->offset);
+ inbuf->offset+=4;
+}
+
+/****************************************************************************
+decode a replicate packet and fill a structure
+****************************************************************************/
+static void decode_replicate(struct BUFFER *inbuf, REPLICATE *rep)
+{
+ if(!check_buffer(inbuf, 4))
+ return;
+
+ rep->msg_type = RIVAL(inbuf->buffer, inbuf->offset);
+
+ inbuf->offset+=4;
+
+ switch (rep->msg_type) {
+ case 0:
+ break;
+ case 1:
+ /* add version number map table reply */
+ decode_add_version_number_map_table_reply(inbuf, &rep->avmt_rep);
+ break;
+ case 2:
+ /* send entry request */
+ decode_send_entries_request(inbuf, &rep->se_rq);
+ break;
+ case 3:
+ /* send entry request */
+ decode_send_entries_reply(inbuf, &rep->se_rp);
+ break;
+ case 4:
+ /* update notification request */
+ decode_update_notify_request(inbuf, &rep->un_rq);
+ break;
+ default:
+ DEBUG(0,("decode_replicate: unknown message type:%d\n", rep->msg_type));
+ break;
+ }
+}
+
+/****************************************************************************
+read the generic header and fill the struct.
+****************************************************************************/
+static void read_generic_header(struct BUFFER *inbuf, generic_header *q)
+{
+ if(!check_buffer(inbuf, 16))
+ return;
+
+ q->data_size = RIVAL(inbuf->buffer, inbuf->offset+0);
+ q->opcode = RIVAL(inbuf->buffer, inbuf->offset+4);
+ q->assoc_ctx = RIVAL(inbuf->buffer, inbuf->offset+8);
+ q->mess_type = RIVAL(inbuf->buffer, inbuf->offset+12);
+}
+
+/*******************************************************************
+decode a start association request
+********************************************************************/
+static void decode_start_assoc_request(struct BUFFER *inbuf, START_ASSOC_REQUEST *q)
+{
+ if(!check_buffer(inbuf, 8))
+ return;
+
+ q->assoc_ctx = RIVAL(inbuf->buffer, inbuf->offset+0);
+ q->min_ver = RSVAL(inbuf->buffer, inbuf->offset+4);
+ q->maj_ver = RSVAL(inbuf->buffer, inbuf->offset+6);
+}
+
+/*******************************************************************
+decode a start association reply
+********************************************************************/
+static void decode_start_assoc_reply(struct BUFFER *inbuf, START_ASSOC_REPLY *r)
+{
+ if(!check_buffer(inbuf, 8))
+ return;
+
+ r->assoc_ctx=RIVAL(inbuf->buffer, inbuf->offset+0);
+ r->min_ver = RSVAL(inbuf->buffer, inbuf->offset+4);
+ r->maj_ver = RSVAL(inbuf->buffer, inbuf->offset+6);
+}
+
+/*******************************************************************
+decode a start association reply
+********************************************************************/
+static void decode_stop_assoc(struct BUFFER *inbuf, STOP_ASSOC *r)
+{
+ if(!check_buffer(inbuf, 4))
+ return;
+
+ r->reason=RIVAL(inbuf->buffer, inbuf->offset);
+}
+
+/****************************************************************************
+decode a packet and fill a generic structure
+****************************************************************************/
+void decode_generic_packet(struct BUFFER *inbuf, GENERIC_PACKET *q)
+{
+ read_generic_header(inbuf, &q->header);
+
+ inbuf->offset+=16;
+
+ switch (q->header.mess_type) {
+ case 0:
+ decode_start_assoc_request(inbuf, &q->sa_rq);
+ break;
+ case 1:
+ decode_start_assoc_reply(inbuf, &q->sa_rp);
+ break;
+ case 2:
+ decode_stop_assoc(inbuf, &q->so);
+ break;
+ case 3:
+ decode_replicate(inbuf, &q->rep);
+ break;
+ default:
+ DEBUG(0,("decode_generic_packet: unknown message type:%d\n", q->header.mess_type));
+ break;
+ }
+}
+
+/****************************************************************************
+encode a WINS_OWNER struct
+****************************************************************************/
+static void encode_wins_owner(struct BUFFER *outbuf, WINS_OWNER *wins_owner)
+{
+ if (!grow_buffer(outbuf, 24))
+ return;
+
+ SIVAL(outbuf->buffer, outbuf->offset, wins_owner->address.s_addr);
+ outbuf->offset+=4;
+ RSIVAL(outbuf->buffer, outbuf->offset, (int)(wins_owner->max_version>>32));
+ outbuf->offset+=4;
+ RSIVAL(outbuf->buffer, outbuf->offset, (int)(wins_owner->max_version&0xffffffff));
+ outbuf->offset+=4;
+ RSIVAL(outbuf->buffer, outbuf->offset, wins_owner->min_version>>32);
+ outbuf->offset+=4;
+ RSIVAL(outbuf->buffer, outbuf->offset, wins_owner->min_version&0xffffffff);
+ outbuf->offset+=4;
+ RSIVAL(outbuf->buffer, outbuf->offset, wins_owner->type);
+ outbuf->offset+=4;
+
+}
+
+/****************************************************************************
+encode a WINS_NAME struct
+****************************************************************************/
+static void encode_wins_name(struct BUFFER *outbuf, WINS_NAME *wins_name)
+{
+ int i;
+
+ if (!grow_buffer(outbuf, 48+(4*wins_name->num_ip)))
+ return;
+
+ RSIVAL(outbuf->buffer, outbuf->offset, wins_name->name_len);
+ outbuf->offset+=4;
+
+ memset(outbuf->buffer+outbuf->offset, ' ', 15);
+
+ /* to prevent copying the leading \0 */
+ memcpy(outbuf->buffer+outbuf->offset, wins_name->name, strlen(wins_name->name));
+ outbuf->offset+=15;
+
+ outbuf->buffer[outbuf->offset++]=(char)wins_name->type;
+
+ RSIVAL(outbuf->buffer, outbuf->offset, wins_name->empty);
+ outbuf->offset+=4;
+
+ RSIVAL(outbuf->buffer, outbuf->offset, wins_name->name_flag);
+ outbuf->offset+=4;
+ RSIVAL(outbuf->buffer, outbuf->offset, wins_name->group_flag);
+ outbuf->offset+=4;
+ RSIVAL(outbuf->buffer, outbuf->offset, wins_name->id>>32);
+ outbuf->offset+=4;
+ RSIVAL(outbuf->buffer, outbuf->offset, wins_name->id);
+ outbuf->offset+=4;
+
+ if (wins_name->name_flag & 2) {
+ SIVAL(outbuf->buffer, outbuf->offset, wins_name->num_ip);
+ outbuf->offset+=4;
+ }
+
+ SIVAL(outbuf->buffer, outbuf->offset, wins_name->owner.s_addr);
+ outbuf->offset+=4;
+
+ if (wins_name->name_flag & 2) {
+ for (i=0;i<wins_name->num_ip;i++) {
+ SIVAL(outbuf->buffer, outbuf->offset, wins_name->others[i].s_addr);
+ outbuf->offset+=4;
+ }
+ }
+
+ RSIVAL(outbuf->buffer, outbuf->offset, wins_name->foo);
+ outbuf->offset+=4;
+}
+
+/****************************************************************************
+encode a update notification request
+****************************************************************************/
+static void encode_update_notify_request(struct BUFFER *outbuf, UPDATE_NOTIFY_REQUEST *un_rq)
+{
+ int i;
+
+ if (!grow_buffer(outbuf, 8))
+ return;
+
+ RSIVAL(outbuf->buffer, outbuf->offset, un_rq->partner_count);
+ outbuf->offset+=4;
+
+ for (i=0; i<un_rq->partner_count; i++)
+ encode_wins_owner(outbuf, &un_rq->wins_owner[i]);
+
+ SIVAL(outbuf->buffer, outbuf->offset, un_rq->initiating_wins_server.s_addr);
+ outbuf->offset+=4;
+
+}
+
+/****************************************************************************
+decode a send entries request
+****************************************************************************/
+static void encode_send_entries_request(struct BUFFER *outbuf, SEND_ENTRIES_REQUEST *se_rq)
+{
+ encode_wins_owner(outbuf, &se_rq->wins_owner);
+}
+
+/****************************************************************************
+decode a send entries reply
+****************************************************************************/
+static void encode_send_entries_reply(struct BUFFER *outbuf, SEND_ENTRIES_REPLY *se_rp)
+{
+ int i;
+
+ if (!grow_buffer(outbuf, 4))
+ return;
+
+ RSIVAL(outbuf->buffer, outbuf->offset, se_rp->max_names);
+ outbuf->offset+=4;
+
+ for (i=0; i<se_rp->max_names; i++)
+ encode_wins_name(outbuf, &se_rp->wins_name[i]);
+
+}
+
+/****************************************************************************
+encode a add version number map table reply
+****************************************************************************/
+static void encode_add_version_number_map_table_reply(struct BUFFER *outbuf, AVMT_REP *avmt_rep)
+{
+ int i;
+
+ if (!grow_buffer(outbuf, 8))
+ return;
+
+ RSIVAL(outbuf->buffer, outbuf->offset, avmt_rep->partner_count);
+ outbuf->offset+=4;
+
+ for (i=0; i<avmt_rep->partner_count; i++)
+ encode_wins_owner(outbuf, &avmt_rep->wins_owner[i]);
+
+ SIVAL(outbuf->buffer, outbuf->offset, avmt_rep->initiating_wins_server.s_addr);
+ outbuf->offset+=4;
+
+}
+
+/****************************************************************************
+decode a replicate packet and fill a structure
+****************************************************************************/
+static void encode_replicate(struct BUFFER *outbuf, REPLICATE *rep)
+{
+ if (!grow_buffer(outbuf, 4))
+ return;
+
+ RSIVAL(outbuf->buffer, outbuf->offset, rep->msg_type);
+ outbuf->offset+=4;
+
+ switch (rep->msg_type) {
+ case 0:
+ break;
+ case 1:
+ /* add version number map table reply */
+ encode_add_version_number_map_table_reply(outbuf, &rep->avmt_rep);
+ break;
+ case 2:
+ /* send entry request */
+ encode_send_entries_request(outbuf, &rep->se_rq);
+ break;
+ case 3:
+ /* send entry request */
+ encode_send_entries_reply(outbuf, &rep->se_rp);
+ break;
+ case 4:
+ /* update notification request */
+ encode_update_notify_request(outbuf, &rep->un_rq);
+ break;
+ default:
+ DEBUG(0,("encode_replicate: unknown message type:%d\n", rep->msg_type));
+ break;
+ }
+}
+
+/****************************************************************************
+write the generic header.
+****************************************************************************/
+static void write_generic_header(struct BUFFER *outbuf, generic_header *r)
+{
+ RSIVAL(outbuf->buffer, 0, r->data_size);
+ RSIVAL(outbuf->buffer, 4, r->opcode);
+ RSIVAL(outbuf->buffer, 8, r->assoc_ctx);
+ RSIVAL(outbuf->buffer,12, r->mess_type);
+}
+
+/*******************************************************************
+decode a start association request
+********************************************************************/
+static void encode_start_assoc_request(struct BUFFER *outbuf, START_ASSOC_REQUEST *q)
+{
+ if (!grow_buffer(outbuf, 45))
+ return;
+
+ RSIVAL(outbuf->buffer, outbuf->offset, q->assoc_ctx);
+ RSSVAL(outbuf->buffer, outbuf->offset+4, q->min_ver);
+ RSSVAL(outbuf->buffer, outbuf->offset+6, q->maj_ver);
+
+ outbuf->offset=45;
+}
+
+/*******************************************************************
+decode a start association reply
+********************************************************************/
+static void encode_start_assoc_reply(struct BUFFER *outbuf, START_ASSOC_REPLY *r)
+{
+ if (!grow_buffer(outbuf, 45))
+ return;
+
+ RSIVAL(outbuf->buffer, outbuf->offset, r->assoc_ctx);
+ RSSVAL(outbuf->buffer, outbuf->offset+4, r->min_ver);
+ RSSVAL(outbuf->buffer, outbuf->offset+6, r->maj_ver);
+
+ outbuf->offset=45;
+}
+
+/*******************************************************************
+decode a start association reply
+********************************************************************/
+static void encode_stop_assoc(struct BUFFER *outbuf, STOP_ASSOC *r)
+{
+ if (!grow_buffer(outbuf, 44))
+ return;
+
+ RSIVAL(outbuf->buffer, outbuf->offset, r->reason);
+
+ outbuf->offset=44;
+}
+
+/****************************************************************************
+write the generic header size.
+****************************************************************************/
+static void write_generic_header_size(generic_header *r, int size)
+{
+ /* the buffer size is the total size minus the size field */
+ r->data_size=size-4;
+}
+
+/****************************************************************************
+encode a packet and read a generic structure
+****************************************************************************/
+void encode_generic_packet(struct BUFFER *outbuf, GENERIC_PACKET *q)
+{
+ if (!grow_buffer(outbuf, 16))
+ return;
+
+ outbuf->offset=16;
+
+ switch (q->header.mess_type) {
+ case 0:
+ encode_start_assoc_request(outbuf, &q->sa_rq);
+ break;
+ case 1:
+ encode_start_assoc_reply(outbuf, &q->sa_rp);
+ break;
+ case 2:
+ encode_stop_assoc(outbuf, &q->so);
+ break;
+ case 3:
+ encode_replicate(outbuf, &q->rep);
+ break;
+ default:
+ DEBUG(0,("encode_generic_packet: unknown message type:%d\n", q->header.mess_type));
+ break;
+ }
+
+ write_generic_header_size(&q->header, outbuf->offset);
+ write_generic_header(outbuf, &q->header);
+}
+
+
+/****************************************************************************
+dump a WINS_OWNER structure
+****************************************************************************/
+static void dump_wins_owner(WINS_OWNER *wins_owner)
+{
+ DEBUGADD(10,("\t\t\t\taddress : %s\n", inet_ntoa(wins_owner->address)));
+ DEBUGADD(10,("\t\t\t\tmax version: %d\n", (int)wins_owner->max_version));
+ DEBUGADD(10,("\t\t\t\tmin version: %d\n", (int)wins_owner->min_version));
+ DEBUGADD(10,("\t\t\t\ttype : %d\n", wins_owner->type));
+}
+
+/****************************************************************************
+dump a WINS_NAME structure
+****************************************************************************/
+static void dump_wins_name(WINS_NAME *wins_name)
+{
+ fstring name;
+ int i;
+
+ strncpy(name, wins_name->name, 15);
+
+ DEBUGADD(10,("name: %d, %s<%02x> %x,%x, %d %s %d ", wins_name->name_len, name, wins_name->type,
+ wins_name->name_flag, wins_name->group_flag, (int)wins_name->id,
+ inet_ntoa(wins_name->owner), wins_name->num_ip));
+
+ if (wins_name->num_ip!=1)
+ for (i=0; i<wins_name->num_ip; i++)
+ DEBUGADD(10,("%s ", inet_ntoa(wins_name->others[i])));
+
+ DEBUGADD(10,("\n"));
+}
+
+/****************************************************************************
+dump a replicate structure
+****************************************************************************/
+static void dump_replicate(REPLICATE *rep)
+{
+ int i;
+
+ DEBUGADD(5,("\t\tmsg_type: %d ", rep->msg_type));
+
+ switch (rep->msg_type) {
+ case 0:
+ DEBUGADD(5,("(Add Version Map Table Request)\n"));
+ break;
+ case 1:
+ DEBUGADD(5,("(Add Version Map Table Reply)\n"));
+ DEBUGADD(5,("\t\t\tpartner_count : %d\n", rep->avmt_rep.partner_count));
+ for (i=0; i<rep->avmt_rep.partner_count; i++)
+ dump_wins_owner(&rep->avmt_rep.wins_owner[i]);
+ DEBUGADD(5,("\t\t\tinitiating_wins_server: %s\n", inet_ntoa(rep->avmt_rep.initiating_wins_server)));
+ break;
+ case 2:
+ DEBUGADD(5,("(Send Entries Request)\n"));
+ dump_wins_owner(&rep->se_rq.wins_owner);
+ break;
+ case 3:
+ DEBUGADD(5,("(Send Entries Reply)\n"));
+ DEBUGADD(5,("\t\t\tmax_names : %d\n", rep->se_rp.max_names));
+ for (i=0; i<rep->se_rp.max_names; i++)
+ dump_wins_name(&rep->se_rp.wins_name[i]);
+ break;
+ case 4:
+ DEBUGADD(5,("(Update Notify Request)\n"));
+ DEBUGADD(5,("\t\t\tpartner_count : %d\n", rep->un_rq.partner_count));
+ for (i=0; i<rep->un_rq.partner_count; i++)
+ dump_wins_owner(&rep->un_rq.wins_owner[i]);
+ DEBUGADD(5,("\t\t\tinitiating_wins_server: %s\n", inet_ntoa(rep->un_rq.initiating_wins_server)));
+ break;
+ default:
+ DEBUG(5,("\n"));
+ break;
+ }
+}
+
+/****************************************************************************
+dump a generic structure
+****************************************************************************/
+void dump_generic_packet(GENERIC_PACKET *q)
+{
+ DEBUG(5,("dump_generic_packet:\n"));
+ DEBUGADD(5,("\tdata_size: %08x\n", q->header.data_size));
+ DEBUGADD(5,("\topcode : %08x\n", q->header.opcode));
+ DEBUGADD(5,("\tassoc_ctx: %08x\n", q->header.assoc_ctx));
+ DEBUGADD(5,("\tmess_type: %08x ", q->header.mess_type));
+
+ switch (q->header.mess_type) {
+ case 0:
+ DEBUGADD(5,("(Start Association Request)\n"));
+ DEBUGADD(5,("\t\tassoc_ctx: %08x\n", q->sa_rq.assoc_ctx));
+ DEBUGADD(5,("\t\tmin_ver : %04x\n", q->sa_rq.min_ver));
+ DEBUGADD(5,("\t\tmaj_ver : %04x\n", q->sa_rq.maj_ver));
+ break;
+ case 1:
+ DEBUGADD(5,("(Start Association Reply)\n"));
+ DEBUGADD(5,("\t\tassoc_ctx: %08x\n", q->sa_rp.assoc_ctx));
+ DEBUGADD(5,("\t\tmin_ver : %04x\n", q->sa_rp.min_ver));
+ DEBUGADD(5,("\t\tmaj_ver : %04x\n", q->sa_rp.maj_ver));
+ break;
+ case 2:
+ DEBUGADD(5,("(Stop Association)\n"));
+ DEBUGADD(5,("\t\treason: %08x\n", q->so.reason));
+ break;
+ case 3:
+ DEBUGADD(5,("(Replication Message)\n"));
+ dump_replicate(&q->rep);
+ break;
+ default:
+ DEBUG(5,("\n"));
+ break;
+ }
+
+}
+
+/****************************************************************************
+generate a stop packet
+****************************************************************************/
+void stop_packet(GENERIC_PACKET *q, GENERIC_PACKET *r, int reason)
+{
+ r->header.opcode=OPCODE_NON_NBT;
+ r->header.assoc_ctx=get_server_assoc(q->header.assoc_ctx);
+ r->header.mess_type=MESSAGE_TYPE_STOP_ASSOC;
+ r->so.reason=reason;
+
+}
+
+
diff --git a/source/wrepld/partners.c b/source/wrepld/partners.c
new file mode 100644
index 00000000000..2387f5b45f6
--- /dev/null
+++ b/source/wrepld/partners.c
@@ -0,0 +1,200 @@
+/*
+ Unix SMB/CIFS implementation.
+ process incoming packets - main loop
+ Copyright (C) Jean François Micouleau 1998-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 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 "wins_repl.h"
+
+/* we can exchange info with 64 partners at any given time */
+WINS_PARTNER current_partners[64];
+int total_current_partners;
+
+/*******************************************************************
+verify if we know this partner
+********************************************************************/
+BOOL check_partner(int assoc)
+{
+ int i;
+
+ DEBUG(5,("check_partner: total_current_partners: %d\n", total_current_partners));
+
+ for (i=0; i<total_current_partners; i++)
+ if (current_partners[i].client_assoc==assoc)
+ return True;
+
+ return False;
+}
+
+/*******************************************************************
+add a new entry to the list
+********************************************************************/
+BOOL add_partner(int client_assoc, int server_assoc, BOOL pull, BOOL push)
+{
+ DEBUG(5,("add_partner: total_current_partners: %d\n", total_current_partners));
+
+ if (total_current_partners==64)
+ return False;
+
+ current_partners[total_current_partners].client_assoc=client_assoc;
+ current_partners[total_current_partners].server_assoc=server_assoc;
+ current_partners[total_current_partners].pull_partner=pull;
+ current_partners[total_current_partners].push_partner=push;
+
+ total_current_partners++;
+
+ return True;
+}
+
+/*******************************************************************
+remove an entry to the list
+********************************************************************/
+BOOL remove_partner(int client_assoc)
+{
+ int i,j;
+
+ DEBUG(5,("remove_partner: total_current_partners: %d\n", total_current_partners));
+
+ for (i=0; current_partners[i].client_assoc!=client_assoc && i<total_current_partners; i++)
+ ;
+
+ if (i==total_current_partners)
+ return False;
+
+ for (j=i+1; j<total_current_partners; j++) {
+ current_partners[j-1].client_assoc=current_partners[j].client_assoc;
+ current_partners[j-1].server_assoc=current_partners[j].server_assoc;
+ current_partners[j-1].pull_partner=current_partners[j].pull_partner;
+ current_partners[j-1].push_partner=current_partners[j].push_partner;
+ current_partners[j-1].partner_server.s_addr=current_partners[j].partner_server.s_addr;
+ current_partners[j-1].other_server.s_addr=current_partners[j].other_server.s_addr;
+ }
+
+ total_current_partners--;
+
+ return True;
+}
+
+/*******************************************************************
+link the client and server context
+********************************************************************/
+BOOL update_server_partner(int client_assoc, int server_assoc)
+{
+ int i;
+
+ DEBUG(5,("update_server_partner: total_current_partners: %d\n", total_current_partners));
+
+ for (i=0; i<total_current_partners; i++)
+ if (current_partners[i].client_assoc==client_assoc) {
+ current_partners[i].server_assoc=server_assoc;
+ return True;
+ }
+
+ return False;
+}
+
+/*******************************************************************
+verify if it's a pull partner
+********************************************************************/
+BOOL check_pull_partner(int assoc)
+{
+ int i;
+
+ DEBUG(5,("check_pull_partner: total_current_partners: %d\n", total_current_partners));
+
+ for (i=0; i<total_current_partners; i++)
+ if (current_partners[i].client_assoc==assoc &&
+ current_partners[i].pull_partner==True)
+ return True;
+
+ return False;
+}
+
+/*******************************************************************
+verify if it's a push partner
+********************************************************************/
+BOOL check_push_partner(int assoc)
+{
+ int i;
+
+ DEBUG(5,("check_push_partner: total_current_partners: %d\n", total_current_partners));
+
+ for (i=0; i<total_current_partners; i++)
+ if (current_partners[i].client_assoc==assoc &&
+ current_partners[i].push_partner==True)
+ return True;
+
+ return False;
+}
+
+/*******************************************************************
+return the server ctx linked to the client ctx
+********************************************************************/
+int get_server_assoc(int assoc)
+{
+ int i;
+
+ DEBUG(5,("get_server_assoc: total_current_partners: %d\n", total_current_partners));
+
+ for (i=0; i<total_current_partners; i++)
+ if (current_partners[i].client_assoc==assoc)
+ return current_partners[i].server_assoc;
+
+ return 0;
+}
+
+
+/*******************************************************************
+link the client and server context
+********************************************************************/
+BOOL write_server_assoc_table(int client_assoc, struct in_addr partner, struct in_addr server)
+{
+ int i;
+
+ DEBUG(5,("write_server_assoc_table: total_current_partners: %d\n", total_current_partners));
+
+ for (i=0; i<total_current_partners; i++)
+ if (current_partners[i].client_assoc==client_assoc) {
+ current_partners[i].partner_server=partner;
+ current_partners[i].other_server=server;
+ return True;
+ }
+
+ return False;
+}
+
+/*******************************************************************
+link the client and server context
+********************************************************************/
+BOOL get_server_assoc_table(int client_assoc, struct in_addr *partner, struct in_addr *server)
+{
+ int i;
+
+ DEBUG(5,("get_server_assoc_table: total_current_partners: %d\n", total_current_partners));
+
+ for (i=0; i<total_current_partners; i++)
+ if (current_partners[i].client_assoc==client_assoc) {
+ partner->s_addr=current_partners[i].partner_server.s_addr;
+ server->s_addr=current_partners[i].other_server.s_addr;
+ return True;
+ }
+
+ return False;
+}
+
+
diff --git a/source/wrepld/process.c b/source/wrepld/process.c
new file mode 100644
index 00000000000..0e9a9b34610
--- /dev/null
+++ b/source/wrepld/process.c
@@ -0,0 +1,983 @@
+/*
+ Unix SMB/CIFS implementation.
+ process incoming packets - main loop
+ Copyright (C) Jean François Micouleau 1998-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 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 "wins_repl.h"
+
+extern fd_set *listen_set;
+extern int listen_number;
+extern int *sock_array;
+
+WINS_OWNER global_wins_table[64][64];
+int partner_count;
+
+TALLOC_CTX *mem_ctx;
+
+#define WINS_LIST "wins.tdb"
+#define INFO_VERSION "INFO/version"
+#define INFO_COUNT "INFO/num_entries"
+#define INFO_ID_HIGH "INFO/id_high"
+#define INFO_ID_LOW "INFO/id_low"
+#define ENTRY_PREFIX "ENTRY/"
+
+
+/*******************************************************************
+fill the header of a reply.
+********************************************************************/
+static void fill_header(GENERIC_PACKET *g, int opcode, int ctx, int mess)
+{
+ if (g==NULL)
+ return;
+
+ g->header.opcode=opcode;
+ g->header.assoc_ctx=ctx;
+ g->header.mess_type=mess;
+}
+
+/*******************************************************************
+dump the global table, that's a debug code.
+********************************************************************/
+static void dump_global_table(void)
+{
+ int i,j;
+
+ for (i=0;i<partner_count;i++) {
+ DEBUG(10,("\n%d ", i));
+ for (j=0; global_wins_table[i][j].address.s_addr!=0; j++)
+ DEBUG(10,("%s:%d \t", inet_ntoa(global_wins_table[i][j].address),
+ (int)global_wins_table[i][j].max_version));
+ }
+ DEBUG(10,("\n"));
+}
+
+/*******************************************************************
+start association
+********************************************************************/
+static void start_assoc_process(GENERIC_PACKET *q, GENERIC_PACKET *r)
+{
+ /*
+ * add this request to our current wins partners list
+ * this list is used to know with who we are in contact
+ *
+ */
+ r->sa_rp.assoc_ctx=time(NULL);
+ fill_header(r, OPCODE_NON_NBT, q->sa_rq.assoc_ctx, MESSAGE_TYPE_START_ASSOC_REPLY);
+
+ /* reply we are a NT4 server */
+
+ /* w2K is min=2, maj=5 */
+
+ r->sa_rp.min_ver=1;
+ r->sa_rp.maj_ver=1;
+
+ add_partner(r->sa_rp.assoc_ctx, q->sa_rq.assoc_ctx, False, False);
+}
+
+/*******************************************************************
+start association reply
+********************************************************************/
+static void start_assoc_reply(GENERIC_PACKET *q, GENERIC_PACKET *r)
+{
+ int i;
+
+ /* check if we have already registered this client */
+ if (!check_partner(q->header.assoc_ctx)) {
+ DEBUG(0,("start_assoc_reply: unknown client\n"));
+ stop_packet(q, r, STOP_REASON_USER_REASON);
+ return;
+ }
+
+ if (!update_server_partner(q->header.assoc_ctx, q->sa_rp.assoc_ctx)) {
+ DEBUG(0,("start_assoc_reply: can't update server ctx\n"));
+ stop_packet(q, r, STOP_REASON_USER_REASON);
+ return;
+ }
+
+ /* if pull, request map table */
+ if (check_pull_partner(q->header.assoc_ctx)) {
+ fill_header(r, OPCODE_NON_NBT, get_server_assoc(q->header.assoc_ctx), MESSAGE_TYPE_REPLICATE);
+
+ r->rep.msg_type=MESSAGE_REP_ADD_VERSION_REQUEST;
+ DEBUG(5,("start_assoc_reply: requesting map table\n"));
+
+ return;
+ }
+
+ /* if push, send our table */
+ if (check_push_partner(q->header.assoc_ctx)) {
+ fill_header(r, OPCODE_NON_NBT, get_server_assoc(q->header.assoc_ctx), MESSAGE_TYPE_REPLICATE);
+ r->rep.msg_type=MESSAGE_REP_UPDATE_NOTIFY_REQUEST;
+ r->rep.un_rq.partner_count=partner_count;
+
+ r->rep.un_rq.wins_owner=(WINS_OWNER *)talloc(mem_ctx, partner_count*sizeof(WINS_OWNER));
+ if (r->rep.un_rq.wins_owner==NULL) {
+ DEBUG(0,("start_assoc_reply: can't alloc memory\n"));
+ stop_packet(q, r, STOP_REASON_USER_REASON);
+ return;
+ }
+
+ for (i=0; i<partner_count; i++)
+ r->rep.un_rq.wins_owner[i]=global_wins_table[0][i];
+
+ DEBUG(5,("start_assoc_reply: sending update table\n"));
+ return;
+ }
+
+ /* neither push/pull, stop */
+ /* we should not come here */
+ DEBUG(0,("we have a partner which is neither push nor pull !\n"));
+ stop_packet(q, r, STOP_REASON_USER_REASON);
+}
+
+/****************************************************************************
+initialise and fill the in-memory partner table.
+****************************************************************************/
+int init_wins_partner_table(void)
+{
+ int i=1,j=0,k;
+ char **partner = str_list_make(lp_wins_partners(), NULL);
+
+ if (partner==NULL) {
+ DEBUG(0,("wrepld: no partner list in smb.conf, exiting\n"));
+ exit_server("normal exit");
+ return(0);
+ }
+
+ DEBUG(4, ("init_wins_partner_table: partners: %s\n", lp_wins_partners()));
+
+ global_wins_table[0][0].address=*iface_n_ip(0);
+ global_wins_table[0][0].max_version=0;
+ global_wins_table[0][0].min_version=0;
+ global_wins_table[0][0].type=0;
+
+ while (partner[j]!=NULL) {
+ DEBUG(3,("init_wins_partner_table, adding partner: %s\n", partner[j]));
+
+ global_wins_table[0][i].address=*interpret_addr2(partner[j]);
+ global_wins_table[0][i].max_version=0;
+ global_wins_table[0][i].min_version=0;
+ global_wins_table[0][i].type=0;
+ global_wins_table[0][i].last_pull=0;
+ global_wins_table[0][i].last_push=0;
+
+ i++;
+ j++;
+ }
+
+ for (k=1; k<i;k++)
+ for (j=0; j<i; j++)
+ global_wins_table[k][j]=global_wins_table[0][j];
+
+ str_list_free (&partner);
+
+ return i;
+}
+
+/****************************************************************************
+read the last ID from the wins tdb file.
+****************************************************************************/
+static void get_our_last_id(WINS_OWNER *wins_owner)
+{
+ TDB_CONTEXT *tdb;
+
+ tdb = tdb_open_log(lock_path(WINS_LIST), 0, TDB_DEFAULT, O_RDONLY, 0600);
+ if (!tdb) {
+ DEBUG(2,("get_our_last_id: Can't open wins database file %s. Error was %s\n", WINS_LIST, strerror(errno) ));
+ return;
+ }
+
+ wins_owner->max_version=((SMB_BIG_UINT)tdb_fetch_int32(tdb, INFO_ID_HIGH))<<32 |
+ (SMB_BIG_UINT)tdb_fetch_int32(tdb, INFO_ID_LOW);
+
+ tdb_close(tdb);
+}
+
+/****************************************************************************
+send the list of wins server we know.
+****************************************************************************/
+static void send_version_number_map_table(GENERIC_PACKET *q, GENERIC_PACKET *r)
+{
+ int i;
+ int s_ctx=get_server_assoc(q->header.assoc_ctx);
+
+ if (s_ctx==0) {
+ DEBUG(5, ("send_version_number_map_table: request for a partner not in our table\n"));
+ stop_packet(q, r, STOP_REASON_USER_REASON);
+ return;
+ }
+
+ /*
+ * return an array of wins servers, we are partner with.
+ * each entry contains the IP address and the version info
+ * version: ID of the last entry we've got
+ */
+
+ /* the first wins server must be self */
+
+ /*
+ * get our last ID from the wins database
+ * it can have been updated since last read
+ * as nmbd got registration/release.
+ */
+ get_our_last_id(&global_wins_table[0][0]);
+
+ r->rep.avmt_rep.wins_owner=(WINS_OWNER *)talloc(mem_ctx, partner_count*sizeof(WINS_OWNER));
+ if (r->rep.avmt_rep.wins_owner==NULL) {
+ stop_packet(q, r, STOP_REASON_USER_REASON);
+ return;
+ }
+
+ DEBUG(5,("send_version_number_map_table: partner_count: %d\n", partner_count));
+
+ for (i=0; i<partner_count; i++) {
+ DEBUG(5,("send_version_number_map_table, partner: %d -> %s, \n", i, inet_ntoa(global_wins_table[0][i].address)));
+ r->rep.avmt_rep.wins_owner[i]=global_wins_table[0][i];
+ }
+
+ r->rep.msg_type=1;
+ r->rep.avmt_rep.partner_count=partner_count;
+ r->rep.avmt_rep.initiating_wins_server.s_addr=0; /* blatant lie, NT4/w2K do the same ! */
+ fill_header(r, OPCODE_NON_NBT, s_ctx, MESSAGE_TYPE_REPLICATE);
+}
+
+/****************************************************************************
+for a given partner, ask it to send entries we don't have.
+****************************************************************************/
+static BOOL check_partners_and_send_entries(GENERIC_PACKET *q, GENERIC_PACKET *r, int partner)
+{
+ int server;
+ int other;
+ SMB_BIG_UINT temp;
+ SMB_BIG_UINT current;
+
+
+ /*
+ * we check if our partner has more records than us.
+ * we need to check more than our direct partners as
+ * we can have this case:
+ * us: A, partners: B,C, indirect partner: D
+ * A<->B, A<->C, B<->D, C<->D
+ *
+ * So if we're talking to B, we need to check if between
+ * B and C, which one have more records about D.
+ * and also check if we don't already have the records.
+ */
+
+
+ /* check all servers even indirect */
+ for (server=1; global_wins_table[0][server].address.s_addr!=0; server++) {
+ current = global_wins_table[partner][server].max_version;
+
+ temp=0;
+
+ for (other=1; other<partner_count; other++) {
+ /* skip the partner itself */
+ if (other==partner)
+ continue;
+
+ if (global_wins_table[other][server].max_version > temp)
+ temp=global_wins_table[other][server].max_version;
+ }
+
+ if (current >= temp && current > global_wins_table[0][server].max_version) {
+ /*
+ * it has more records than every body else and more than us,
+ * ask it the difference between what we have and what it has
+ */
+ fill_header(r, OPCODE_NON_NBT, get_server_assoc(q->header.assoc_ctx), MESSAGE_TYPE_REPLICATE);
+
+ r->rep.msg_type=MESSAGE_REP_SEND_ENTRIES_REQUEST;
+ r->rep.se_rq.wins_owner.address=global_wins_table[partner][server].address;
+
+ r->rep.se_rq.wins_owner.max_version=global_wins_table[partner][server].max_version;
+ r->rep.se_rq.wins_owner.min_version=global_wins_table[0][server].max_version;
+ r->rep.se_rq.wins_owner.type=0;
+
+ write_server_assoc_table(q->header.assoc_ctx, global_wins_table[0][partner].address, global_wins_table[partner][server].address);
+
+ /*
+ * and we update our version for this server
+ * as we can't use the IDs returned in the send_entries function
+ * the max ID can be larger than the largest ID returned
+ */
+
+ global_wins_table[0][server].max_version=global_wins_table[partner][server].max_version;
+
+ return True;
+ }
+ }
+ return False;
+}
+
+/****************************************************************************
+receive the list of wins server we know.
+****************************************************************************/
+static void receive_version_number_map_table(GENERIC_PACKET *q, GENERIC_PACKET *r)
+{
+ fstring peer;
+ struct in_addr addr;
+ int i,j,k,l;
+ int s_ctx=get_server_assoc(q->header.assoc_ctx);
+
+ if (s_ctx==0) {
+ DEBUG(5, ("receive_version_number_map_table: request for a partner not in our table\n"));
+ stop_packet(q, r, STOP_REASON_USER_REASON);
+ return;
+ }
+
+ fstrcpy(peer,get_peer_addr(q->fd));
+ addr=*interpret_addr2(peer);
+
+ get_our_last_id(&global_wins_table[0][0]);
+
+ DEBUG(5,("receive_version_number_map_table: received a map of %d server from: %s\n",
+ q->rep.avmt_rep.partner_count ,inet_ntoa(q->rep.avmt_rep.initiating_wins_server)));
+ DEBUG(5,("real peer is: %s\n", peer));
+
+ for (i=0; global_wins_table[0][i].address.s_addr!=addr.s_addr && i<partner_count;i++)
+ ;
+
+ if (i==partner_count) {
+ DEBUG(5,("receive_version_number_map_table: unknown partner: %s\n", peer));
+ stop_packet(q, r, STOP_REASON_USER_REASON);
+ return;
+ }
+
+ for (j=0; j<q->rep.avmt_rep.partner_count;j++) {
+ /*
+ * search if we already have this entry or if it's a new one
+ * it can be a new one in case of propagation
+ */
+ for (k=0; global_wins_table[0][k].address.s_addr!=0 &&
+ global_wins_table[0][k].address.s_addr!=q->rep.avmt_rep.wins_owner[j].address.s_addr; k++);
+
+ global_wins_table[i][k].address.s_addr=q->rep.avmt_rep.wins_owner[j].address.s_addr;
+ global_wins_table[i][k].max_version=q->rep.avmt_rep.wins_owner[j].max_version;
+ global_wins_table[i][k].min_version=q->rep.avmt_rep.wins_owner[j].min_version;
+ global_wins_table[i][k].type=q->rep.avmt_rep.wins_owner[j].type;
+
+ /*
+ * in case it's a new one, rewrite the address for all the partner
+ * to reserve the slot.
+ */
+
+ for(l=0; l<partner_count; l++)
+ global_wins_table[l][k].address.s_addr=q->rep.avmt_rep.wins_owner[j].address.s_addr;
+ }
+
+ dump_global_table();
+
+ /*
+ * if this server have newer records than what we have
+ * for several wins servers, we need to ask it.
+ * Alas a send entry request is only on one server.
+ * So in the send entry reply, we'll ask for the next server if required.
+ */
+
+ if (check_partners_and_send_entries(q, r, i))
+ return;
+
+ /* it doesn't have more entries than us */
+ stop_packet(q, r, STOP_REASON_USER_REASON);
+}
+
+/****************************************************************************
+add an entry to the wins list we'll send.
+****************************************************************************/
+static BOOL add_record_to_winsname(WINS_NAME **wins_name, int *max_names, char *name, int type, int wins_flags, int id, struct in_addr *ip_list, int num_ips)
+{
+ WINS_NAME *temp_list;
+ int i;
+ int current=*max_names;
+
+ temp_list=talloc_realloc(mem_ctx, *wins_name, (current+1)*sizeof(WINS_NAME));
+ if (temp_list==NULL)
+ return False;
+
+ temp_list[current].name_len=0x11;
+
+ safe_strcpy(temp_list[current].name, name, 15);
+
+ temp_list[current].type=type;
+ temp_list[current].empty=0;
+
+ temp_list[current].name_flag=wins_flags;
+
+ if ( (wins_flags&0x03) == 1 || (wins_flags&0x03)==2)
+ temp_list[current].group_flag=0x01000000;
+ else
+ temp_list[current].group_flag=0x00000000;
+
+ temp_list[current].id=id;
+
+ temp_list[current].owner.s_addr=ip_list[0].s_addr;
+
+ if (temp_list[current].name_flag & 2) {
+ temp_list[current].num_ip=num_ips;
+ temp_list[current].others=(struct in_addr *)talloc(mem_ctx, sizeof(struct in_addr)*num_ips);
+ if (temp_list[current].others==NULL)
+ return False;
+
+ for (i=0; i<num_ips; i++)
+ temp_list[current].others[i].s_addr=ip_list[i].s_addr;
+
+ } else
+ temp_list[current].num_ip=1;
+
+ temp_list[current].foo=0xffffffff;
+
+ *wins_name=temp_list;
+
+ return True;
+}
+
+/****************************************************************************
+send the list of name we have.
+****************************************************************************/
+static void send_entry_request(GENERIC_PACKET *q, GENERIC_PACKET *r)
+{
+ int max_names=0;
+ int i;
+ time_t time_now = time(NULL);
+ WINS_OWNER *wins_owner;
+ TDB_CONTEXT *tdb;
+ TDB_DATA kbuf, dbuf, newkey;
+ int s_ctx=get_server_assoc(q->header.assoc_ctx);
+ int num_interfaces = iface_count();
+
+ if (s_ctx==0) {
+ DEBUG(1, ("send_entry_request: request for a partner not in our table\n"));
+ stop_packet(q, r, STOP_REASON_USER_REASON);
+ return;
+ }
+
+
+ wins_owner=&q->rep.se_rq.wins_owner;
+ r->rep.se_rp.wins_name=NULL;
+
+ DEBUG(3,("send_entry_request: we have been asked to send the list of wins records\n"));
+ DEBUGADD(3,("owned by: %s and between min: %d and max: %d\n", inet_ntoa(wins_owner->address),
+ (int)wins_owner->min_version, (int)wins_owner->max_version));
+
+ /*
+ * if we are asked to send records owned by us
+ * we overwrite the wins ip with 0.0.0.0
+ * to make it easy in case of multihomed
+ */
+
+ for (i=0; i<num_interfaces; i++)
+ if (ip_equal(wins_owner->address, *iface_n_ip(i))) {
+ wins_owner->address=*interpret_addr2("0.0.0.0");
+ break;
+ }
+
+
+ tdb = tdb_open_log(lock_path(WINS_LIST), 0, TDB_DEFAULT, O_RDONLY, 0600);
+ if (!tdb) {
+ DEBUG(2,("send_entry_request: Can't open wins database file %s. Error was %s\n", WINS_LIST, strerror(errno) ));
+ return;
+ }
+
+ for (kbuf = tdb_firstkey(tdb);
+ kbuf.dptr;
+ newkey = tdb_nextkey(tdb, kbuf), safe_free(kbuf.dptr), kbuf=newkey) {
+ fstring name_type;
+ pstring name, ip_str;
+ char *p;
+ int type = 0;
+ int nb_flags;
+ int ttl;
+ unsigned int num_ips;
+ int low, high;
+ SMB_BIG_UINT version;
+ struct in_addr wins_ip;
+ struct in_addr *ip_list;
+ int wins_flags;
+ int len;
+
+ if (strncmp(kbuf.dptr, ENTRY_PREFIX, strlen(ENTRY_PREFIX)) != 0)
+ continue;
+
+
+ dbuf = tdb_fetch(tdb, kbuf);
+ if (!dbuf.dptr)
+ continue;
+
+ fstrcpy(name_type, kbuf.dptr+strlen(ENTRY_PREFIX));
+ pstrcpy(name, name_type);
+
+ if((p = strchr(name,'#')) != NULL) {
+ *p = 0;
+ sscanf(p+1,"%x",&type);
+ }
+
+ len = tdb_unpack(dbuf.dptr, dbuf.dsize, "dddfddd",
+ &nb_flags,
+ &high,
+ &low,
+ ip_str,
+ &ttl,
+ &num_ips,
+ &wins_flags);
+
+ wins_ip=*interpret_addr2(ip_str);
+
+ /* Allocate the space for the ip_list. */
+ if((ip_list = (struct in_addr *)talloc(mem_ctx, num_ips * sizeof(struct in_addr))) == NULL) {
+ SAFE_FREE(dbuf.dptr);
+ DEBUG(0,("initialise_wins: talloc fail !\n"));
+ return;
+ }
+
+ for (i = 0; i < num_ips; i++) {
+ len += tdb_unpack(dbuf.dptr+len, dbuf.dsize-len, "f", ip_str);
+ ip_list[i] = *interpret_addr2(ip_str);
+ }
+
+ SAFE_FREE(dbuf.dptr);
+
+ /* add all entries that have 60 seconds or more to live */
+ if ((ttl - 60) > time_now || ttl == PERMANENT_TTL) {
+ if(ttl != PERMANENT_TTL)
+ ttl -= time_now;
+
+ DEBUG( 4, ("send_entry_request: add name: %s#%02x ttl = %d first IP %s flags = %2x\n",
+ name, type, ttl, inet_ntoa(ip_list[0]), nb_flags));
+
+ /* add the record to the list to send */
+ version=((SMB_BIG_UINT)high)<<32 | low;
+
+ if (wins_owner->min_version<=version && wins_owner->max_version>=version &&
+ wins_owner->address.s_addr==wins_ip.s_addr) {
+ if(!add_record_to_winsname(&r->rep.se_rp.wins_name, &max_names, name, type, wins_flags, version, ip_list, num_ips))
+ return;
+ max_names++;
+ }
+
+ } else {
+ DEBUG(4, ("send_entry_request: not adding name (ttl problem) %s#%02x ttl = %d first IP %s flags = %2x\n",
+ name, type, ttl, inet_ntoa(ip_list[0]), nb_flags));
+ }
+ }
+
+ tdb_close(tdb);
+
+ DEBUG(4,("send_entry_request, sending %d records\n", max_names));
+ fill_header(r, OPCODE_NON_NBT, s_ctx, MESSAGE_TYPE_REPLICATE);
+ r->rep.msg_type=MESSAGE_REP_SEND_ENTRIES_REPLY; /* reply */
+ r->rep.se_rp.max_names=max_names;
+}
+
+
+/****************************************************************************
+.
+****************************************************************************/
+static void update_notify_request(GENERIC_PACKET *q, GENERIC_PACKET *r)
+{
+ int i,j,k,l;
+ UPDATE_NOTIFY_REQUEST *u;
+ int s_ctx=get_server_assoc(q->header.assoc_ctx);
+
+ if (s_ctx==0) {
+ DEBUG(4, ("update_notify_request: request for a partner not in our table\n"));
+ stop_packet(q, r, STOP_REASON_USER_REASON);
+ return;
+ }
+
+ u=&q->rep.un_rq;
+
+ /* check if we already have the range of records */
+
+ DEBUG(5,("update_notify_request: wins server: %s offered this list of %d records:\n",
+ inet_ntoa(u->initiating_wins_server), u->partner_count));
+
+ get_our_last_id(&global_wins_table[0][0]);
+
+ for (i=0; i<partner_count; i++) {
+ if (global_wins_table[0][i].address.s_addr==u->initiating_wins_server.s_addr) {
+ DEBUG(5,("update_notify_request: found initiator at index %d\n", i));
+ break;
+ }
+ }
+
+ /*
+ * some explanation is required, before someone say it's crap.
+ *
+ * let's take an example, we have 2 wins partners, we already now
+ * that our max id is 10, partner 1 ID is 20 and partner 2 ID is 30
+ * the array looks like:
+ *
+ * 0 1 2
+ * 0 10 20 30
+ * 1
+ * 2
+ *
+ * we receive an update from partner 2 saying he has: 1:15, 2:40, 3:50
+ * we must enlarge the array to add partner 3, it will look like:
+ *
+ * 0 1 2 3
+ * 0 10 20 30
+ * 1
+ * 2 15 40 50
+ *
+ * now we know, we should pull from partner 2, the records 30->40 of 2 and 0->50 of 3.
+ * once the pull will be over, our table will look like:
+ *
+ * 0 1 2 3
+ * 0 10 20 40 50
+ * 1
+ * 2 15 40 50
+ *
+ *
+ */
+
+ for (j=0; j<u->partner_count;j++) {
+ /*
+ * search if we already have this entry or if it's a new one
+ * it can be a new one in case of propagation
+ */
+
+ for (k=0; global_wins_table[0][k].address.s_addr!=0 &&
+ global_wins_table[0][k].address.s_addr!=u->wins_owner[j].address.s_addr; k++);
+
+ global_wins_table[i][k].address.s_addr=u->wins_owner[j].address.s_addr;
+ global_wins_table[i][k].max_version=u->wins_owner[j].max_version;
+ global_wins_table[i][k].min_version=u->wins_owner[j].min_version;
+ global_wins_table[i][k].type=u->wins_owner[j].type;
+
+ /*
+ * in case it's a new one, rewrite the address for all the partner
+ * to reserve the slot.
+ */
+
+ for(l=0; l<partner_count; l++)
+ global_wins_table[l][k].address.s_addr=u->wins_owner[j].address.s_addr;
+ }
+
+ dump_global_table();
+
+ stop_packet(q, r, STOP_REASON_USER_REASON);
+}
+
+/****************************************************************************
+.
+****************************************************************************/
+static void send_entry_reply(GENERIC_PACKET *q, GENERIC_PACKET *r)
+{
+ int i,j,k;
+ struct in_addr partner, server;
+ pid_t pid;
+ int s_ctx=get_server_assoc(q->header.assoc_ctx);
+ WINS_RECORD record;
+
+ if (s_ctx==0) {
+ DEBUG(1, ("send_entry_reply: request for a partner not in our table\n"));
+ stop_packet(q, r, STOP_REASON_USER_REASON);
+ return;
+ }
+
+ DEBUG(5,("send_entry_reply:got %d new records\n", q->rep.se_rp.max_names));
+
+ /* we got records from a wins partner but that can be from another wins server */
+ /* hopefully we track that */
+
+ /* and the only doc available from MS is wrong ! */
+
+ get_server_assoc_table(q->header.assoc_ctx, &partner, &server);
+
+ for (j=0; global_wins_table[0][j].address.s_addr!=0; j++) {
+ if (global_wins_table[0][j].address.s_addr==server.s_addr) {
+ DEBUG(5,("send_entry_reply: found server at index %d\n", j));
+ break;
+ }
+ }
+
+ pid = pidfile_pid("nmbd");
+ if (pid == 0) {
+ DEBUG(0,("send_entry_reply: Can't find pid for nmbd\n"));
+ return;
+ }
+
+ for (k=0; k<q->rep.se_rp.max_names; k++) {
+ DEBUG(5,("send_entry_reply: %s<%02x> %d\n", q->rep.se_rp.wins_name[k].name, q->rep.se_rp.wins_name[k].type,
+ (int)q->rep.se_rp.wins_name[k].id));
+
+ safe_strcpy(record.name, q->rep.se_rp.wins_name[k].name, 16);
+ record.type=q->rep.se_rp.wins_name[k].type;
+ record.id=q->rep.se_rp.wins_name[k].id;
+ record.wins_flags=q->rep.se_rp.wins_name[k].name_flag&0x00ff;
+ record.num_ips=q->rep.se_rp.wins_name[k].num_ip;
+
+ record.wins_ip.s_addr=server.s_addr;
+
+ if (record.num_ips==1)
+ record.ip[0]=q->rep.se_rp.wins_name[k].owner;
+ else
+ for (i=0; i<record.num_ips; i++)
+ record.ip[i]=q->rep.se_rp.wins_name[k].others[i];
+
+ record.nb_flags=0;
+
+ if (record.wins_flags&WINS_NGROUP || record.wins_flags&WINS_SGROUP)
+ record.nb_flags|=NB_GROUP;
+
+ if (record.wins_flags&WINS_ACTIVE)
+ record.nb_flags|=NB_ACTIVE;
+
+ record.nb_flags|=record.wins_flags&WINS_HNODE;
+
+ message_send_pid(pid, MSG_WINS_NEW_ENTRY, &record, sizeof(record), False);
+
+ }
+
+ dump_global_table();
+
+ /*
+ * we got some entries,
+ * ask the partner to send us the map table again
+ * to get the other servers entries.
+ *
+ * we're getting the map table 1 time more than really
+ * required. We could remove that call, but that
+ * would complexify the code. I prefer this trade-of.
+ */
+ fill_header(r, OPCODE_NON_NBT, s_ctx, MESSAGE_TYPE_REPLICATE);
+
+ r->rep.msg_type=MESSAGE_REP_ADD_VERSION_REQUEST;
+}
+
+/****************************************************************************
+decode the replication message and reply.
+****************************************************************************/
+static void replicate(GENERIC_PACKET *q, GENERIC_PACKET *r)
+{
+ switch (q->rep.msg_type) {
+ case 0:
+ /* add version number map table request */
+ send_version_number_map_table(q, r);
+ break;
+ case 1:
+ receive_version_number_map_table(q, r);
+ break;
+ case 2:
+ /* send entry request */
+ send_entry_request(q, r);
+ break;
+ case 3:
+ /* send entry reply */
+ send_entry_reply(q, r);
+ break;
+ case 4:
+ /* update notification request */
+ update_notify_request(q, r);
+ break;
+ }
+}
+
+/****************************************************************************
+do a switch on the message type, and return the response size
+****************************************************************************/
+static BOOL switch_message(GENERIC_PACKET *q, GENERIC_PACKET *r)
+{
+ switch (q->header.mess_type) {
+ case 0:
+ /* Start association type */
+ start_assoc_process(q, r);
+ return True;
+ break;
+ case 1:
+ /* start association reply */
+ start_assoc_reply(q, r);
+ return True;
+ break;
+ case 2:
+ /* stop association message */
+ return False;
+ break;
+ case 3:
+ /* replication message */
+ replicate(q, r);
+ return True;
+ break;
+ }
+
+ return False;
+}
+
+
+/****************************************************************************
+ construct a reply to the incoming packet
+****************************************************************************/
+void construct_reply(struct wins_packet_struct *p)
+{
+ GENERIC_PACKET r;
+ struct BUFFER buffer;
+
+ buffer.buffer=NULL;
+ buffer.offset=0;
+ buffer.length=0;
+
+ DEBUG(5,("dump: received packet\n"));
+ dump_generic_packet(p->packet);
+
+ /* Verify if the request we got is from a listed partner */
+ if (!check_partner(p->packet->header.assoc_ctx)) {
+ fstring peer;
+ struct in_addr addr;
+ int i;
+ fstrcpy(peer,get_peer_addr(p->fd));
+ addr=*interpret_addr2(peer);
+
+ for (i=1; i<partner_count; i++)
+ if (ip_equal(addr, global_wins_table[0][i].address))
+ break;
+
+ if (i==partner_count) {
+ DEBUG(1,("construct_reply: got a request from a non peer machine: %s\n", peer));
+ stop_packet(p->packet, &r, STOP_REASON_AUTH_FAILED);
+ p->stop_packet=True;
+ encode_generic_packet(&buffer, &r);
+ if (!send_smb(p->fd, buffer.buffer))
+ exit_server("process_smb: send_smb failed.");
+ return;
+ }
+ }
+
+ if (switch_message(p->packet, &r)) {
+ encode_generic_packet(&buffer, &r);
+ DEBUG(5,("dump: sending packet\n"));
+ dump_generic_packet(&r);
+
+ if(buffer.offset > 0) {
+ if (!send_smb(p->fd, buffer.buffer))
+ exit_server("process_smb: send_smb failed.");
+ }
+ }
+
+ /* if we got a stop assoc or if we send a stop assoc, close the fd after */
+ if (p->packet->header.mess_type==MESSAGE_TYPE_STOP_ASSOC ||
+ r.header.mess_type==MESSAGE_TYPE_STOP_ASSOC) {
+ remove_partner(p->packet->header.assoc_ctx);
+ p->stop_packet=True;
+ }
+}
+
+/****************************************************************************
+ contact periodically our wins partner to do a pull replication
+****************************************************************************/
+void run_pull_replication(time_t t)
+{
+ /* we pull every 30 minutes to query about new records*/
+ int i, s;
+ struct BUFFER buffer;
+ GENERIC_PACKET p;
+
+ buffer.buffer=NULL;
+ buffer.offset=0;
+ buffer.length=0;
+
+ for (i=1; i<partner_count; i++) {
+ if (global_wins_table[0][i].last_pull < t) {
+ global_wins_table[0][i].last_pull=t+30*60; /* next in 30 minutes */
+
+ /* contact the wins server */
+ p.header.mess_type=MESSAGE_TYPE_START_ASSOC_REQUEST;
+ p.header.opcode=OPCODE_NON_NBT;
+ p.header.assoc_ctx=0;
+ p.sa_rq.assoc_ctx=(int)t;
+ p.sa_rq.min_ver=1;
+ p.sa_rq.maj_ver=1;
+
+ DEBUG(3,("run_pull_replication: contacting wins server %s.\n", inet_ntoa(global_wins_table[0][i].address)));
+ encode_generic_packet(&buffer, &p);
+ dump_generic_packet(&p);
+
+ /* send the packet to the server and add the descriptor to receive answers */
+ s=open_socket_out(SOCK_STREAM, &global_wins_table[0][i].address, 42, LONG_CONNECT_TIMEOUT);
+ if (s==-1) {
+ DEBUG(0,("run_pull_replication: can't contact wins server %s.\n", inet_ntoa(global_wins_table[0][i].address)));
+ return;
+ }
+
+ if(buffer.offset > 0) {
+ if (!send_smb(s, buffer.buffer))
+ exit_server("run_pull_replication: send_smb failed.");
+ }
+
+ add_fd_to_sock_array(s);
+ FD_SET(s, listen_set);
+
+ /* add ourself as a client */
+ add_partner((int)t, 0, True, False);
+ }
+ }
+}
+
+/****************************************************************************
+ contact periodically our wins partner to do a push replication
+****************************************************************************/
+void run_push_replication(time_t t)
+{
+ /* we push every 30 minutes or 25 new entries */
+ int i, s;
+ struct BUFFER buffer;
+ GENERIC_PACKET p;
+
+ buffer.buffer=NULL;
+ buffer.offset=0;
+ buffer.length=0;
+
+ for (i=1; i<partner_count; i++) {
+ if (global_wins_table[0][i].last_pull < t) {
+ global_wins_table[0][i].last_pull=t+30*60; /* next in 30 minutes */
+
+ /* contact the wins server */
+ p.header.mess_type=MESSAGE_TYPE_START_ASSOC_REQUEST;
+ p.header.opcode=OPCODE_NON_NBT;
+ p.header.assoc_ctx=0;
+ p.sa_rq.assoc_ctx=(int)t;
+ p.sa_rq.min_ver=1;
+ p.sa_rq.maj_ver=1;
+
+ DEBUG(3,("run_push_replication: contacting wins server %s.\n", inet_ntoa(global_wins_table[0][i].address)));
+ encode_generic_packet(&buffer, &p);
+ dump_generic_packet(&p);
+
+ /* send the packet to the server and add the descriptor to receive answers */
+ s=open_socket_out(SOCK_STREAM, &global_wins_table[0][i].address, 42, LONG_CONNECT_TIMEOUT);
+ if (s==-1) {
+ DEBUG(0,("run_push_replication: can't contact wins server %s.\n", inet_ntoa(global_wins_table[0][i].address)));
+ return;
+ }
+
+ if(buffer.offset > 0) {
+ if (!send_smb(s, buffer.buffer))
+ exit_server("run_push_replication: send_smb failed.");
+ }
+
+ add_fd_to_sock_array(s);
+ FD_SET(s, listen_set);
+
+ /* add ourself as a client */
+ add_partner((int)t, 0, False, True);
+ }
+ }
+}
+
diff --git a/source/wrepld/server.c b/source/wrepld/server.c
new file mode 100644
index 00000000000..80694a616bb
--- /dev/null
+++ b/source/wrepld/server.c
@@ -0,0 +1,666 @@
+/*
+ Unix SMB/CIFS implementation.
+ Main SMB server routines
+ Copyright (C) Jean François Micouleau 1998-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 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 "wins_repl.h"
+
+extern pstring user_socket_options;
+
+extern WINS_OWNER *global_wins_table;
+extern int partner_count;
+
+extern fd_set *listen_set;
+extern int listen_number;
+extern int *sock_array;
+
+extern TALLOC_CTX *mem_ctx;
+
+int wins_port = 42;
+
+/****************************************************************************
+ when exiting, take the whole family
+****************************************************************************/
+static void *dflt_sig(void)
+{
+ exit_server("caught signal");
+ return NULL;
+}
+
+/****************************************************************************
+ reload the services file
+ **************************************************************************/
+BOOL reload_services(BOOL test)
+{
+ BOOL ret;
+
+ if (lp_loaded()) {
+ pstring fname;
+ pstrcpy(fname,lp_configfile());
+ if (file_exist(fname,NULL) && !strcsequal(fname,dyn_CONFIGFILE)) {
+ pstrcpy(dyn_CONFIGFILE,fname);
+ test = False;
+ }
+ }
+
+ reopen_logs();
+
+ if (test && !lp_file_list_changed())
+ return(True);
+
+ ret = lp_load(dyn_CONFIGFILE,False,False,True);
+
+
+ /* perhaps the config filename is now set */
+ if (!test)
+ reload_services(True);
+
+ reopen_logs();
+
+ load_interfaces();
+
+ return(ret);
+}
+
+/****************************************************************************
+ Catch a sighup.
+****************************************************************************/
+
+VOLATILE sig_atomic_t reload_after_sighup = False;
+
+static void sig_hup(int sig)
+{
+ BlockSignals(True,SIGHUP);
+ DEBUG(0,("Got SIGHUP\n"));
+
+ sys_select_signal();
+ reload_after_sighup = True;
+ BlockSignals(False,SIGHUP);
+}
+
+#if DUMP_CORE
+/*******************************************************************
+prepare to dump a core file - carefully!
+********************************************************************/
+static BOOL dump_core(void)
+{
+ char *p;
+ pstring dname;
+ pstrcpy(dname,lp_logfile());
+ if ((p=strrchr_m(dname,'/'))) *p=0;
+ pstrcat(dname,"/corefiles");
+ mkdir(dname,0700);
+ sys_chown(dname,getuid(),getgid());
+ chmod(dname,0700);
+ if (chdir(dname)) return(False);
+ umask(~(0700));
+
+#ifdef HAVE_GETRLIMIT
+#ifdef RLIMIT_CORE
+ {
+ struct rlimit rlp;
+ getrlimit(RLIMIT_CORE, &rlp);
+ rlp.rlim_cur = MAX(4*1024*1024,rlp.rlim_cur);
+ setrlimit(RLIMIT_CORE, &rlp);
+ getrlimit(RLIMIT_CORE, &rlp);
+ DEBUG(3,("Core limits now %d %d\n",
+ (int)rlp.rlim_cur,(int)rlp.rlim_max));
+ }
+#endif
+#endif
+
+
+ DEBUG(0,("Dumping core in %s\n",dname));
+ abort();
+ return(True);
+}
+#endif
+
+/****************************************************************************
+exit the server
+****************************************************************************/
+void exit_server(const char *reason)
+{
+ static int firsttime=1;
+
+ if (!firsttime)
+ exit(0);
+ firsttime = 0;
+
+ DEBUG(2,("Closing connections\n"));
+
+ if (!reason) {
+ int oldlevel = DEBUGLEVEL;
+ DEBUGLEVEL = 10;
+ DEBUGLEVEL = oldlevel;
+ DEBUG(0,("===============================================================\n"));
+#if DUMP_CORE
+ if (dump_core()) return;
+#endif
+ }
+
+ DEBUG(3,("Server exit (%s)\n", (reason ? reason : "")));
+ exit(0);
+}
+
+/****************************************************************************
+ Create an fd_set containing all the sockets in the subnet structures,
+ plus the broadcast sockets.
+***************************************************************************/
+
+static BOOL create_listen_fdset(void)
+{
+ int i;
+ int num_interfaces = iface_count();
+ int s;
+
+ listen_set = (fd_set *)malloc(sizeof(fd_set));
+ if(listen_set == NULL) {
+ DEBUG(0,("create_listen_fdset: malloc fail !\n"));
+ return True;
+ }
+
+#ifdef HAVE_ATEXIT
+ {
+ static int atexit_set;
+ if(atexit_set == 0) {
+ atexit_set=1;
+ }
+ }
+#endif
+
+ FD_ZERO(listen_set);
+
+ if(lp_interfaces() && lp_bind_interfaces_only()) {
+ /* 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.
+ */
+
+ if(num_interfaces > FD_SETSIZE) {
+ DEBUG(0,("create_listen_fdset: Too many interfaces specified to bind to. Number was %d max can be %d\n", num_interfaces, FD_SETSIZE));
+ return False;
+ }
+
+ /* Now open a listen socket for each of the interfaces. */
+ for(i = 0; i < num_interfaces; i++) {
+ struct in_addr *ifip = iface_n_ip(i);
+
+ if(ifip == NULL) {
+ DEBUG(0,("create_listen_fdset: interface %d has NULL IP address !\n", i));
+ continue;
+ }
+ s = open_socket_in(SOCK_STREAM, wins_port, 0, ifip->s_addr, True);
+ if(s == -1)
+ return False;
+
+ /* ready to listen */
+ set_socket_options(s,"SO_KEEPALIVE");
+ set_socket_options(s,user_socket_options);
+
+ if (listen(s, 5) == -1) {
+ DEBUG(5,("listen: %s\n",strerror(errno)));
+ close(s);
+ return False;
+ }
+ add_fd_to_sock_array(s);
+ FD_SET(s, listen_set);
+ }
+ } else {
+ /* Just bind to 0.0.0.0 - accept connections from anywhere. */
+ num_interfaces = 1;
+
+ /* open an incoming socket */
+ s = open_socket_in(SOCK_STREAM, wins_port, 0, interpret_addr(lp_socket_address()),True);
+ if (s == -1)
+ return(False);
+
+ /* ready to listen */
+ set_socket_options(s,"SO_KEEPALIVE");
+ set_socket_options(s,user_socket_options);
+
+ if (listen(s, 5) == -1) {
+ DEBUG(0,("create_listen_fdset: listen: %s\n", strerror(errno)));
+ close(s);
+ return False;
+ }
+
+ add_fd_to_sock_array(s);
+ FD_SET(s, listen_set);
+ }
+
+ return True;
+}
+
+/*******************************************************************
+ read a packet from a socket and parse it, returning a packet ready
+ to be used or put on the queue. This assumes a UDP socket
+ ******************************************************************/
+static struct wins_packet_struct *read_wins_packet(int fd, int timeout)
+{
+ struct wins_packet_struct *p;
+ GENERIC_PACKET *q;
+ struct BUFFER inbuf;
+ ssize_t len=0;
+ size_t total=0;
+ ssize_t ret;
+ BOOL ok = False;
+
+ inbuf.buffer=NULL;
+ inbuf.length=0;
+ inbuf.offset=0;
+
+ if(!grow_buffer(&inbuf, 4))
+ return NULL;
+
+ ok = (read(fd, inbuf.buffer,4) == 4);
+ if (!ok)
+ return NULL;
+ len = smb_len(inbuf.buffer);
+
+ if (len<=0)
+ return NULL;
+
+ if(!grow_buffer(&inbuf, len))
+ return NULL;
+
+ while (total < len) {
+ ret = read(fd, inbuf.buffer + total + 4, len - total);
+ if (ret == 0) {
+ DEBUG(10,("read_socket_data: recv of %d returned 0. Error = %s\n", (int)(len - total), strerror(errno) ));
+ return NULL;
+ }
+ if (ret == -1) {
+ DEBUG(0,("read_socket_data: recv failure for %d. Error = %s\n", (int)(len - total), strerror(errno) ));
+ return NULL;
+ }
+ total += ret;
+ }
+
+ q = (GENERIC_PACKET *)talloc(mem_ctx, sizeof(GENERIC_PACKET));
+ p = (struct wins_packet_struct *)talloc(mem_ctx, sizeof(*p));
+ if (q==NULL || p==NULL)
+ return NULL;
+
+ decode_generic_packet(&inbuf, q);
+
+ q->fd=fd;
+
+ p->next = NULL;
+ p->prev = NULL;
+ p->stop_packet = False;
+ p->timestamp = time(NULL);
+ p->fd = fd;
+ p->packet=q;
+
+ return p;
+}
+
+static struct wins_packet_struct *packet_queue = NULL;
+
+/*******************************************************************
+ Queue a packet into a packet queue
+******************************************************************/
+static void queue_packet(struct wins_packet_struct *packet)
+{
+ struct wins_packet_struct *p;
+
+ if (!packet_queue) {
+ packet->prev = NULL;
+ packet->next = NULL;
+ packet_queue = packet;
+ return;
+ }
+
+ /* find the bottom */
+ for (p=packet_queue;p->next;p=p->next)
+ ;
+
+ p->next = packet;
+ packet->next = NULL;
+ packet->prev = p;
+}
+
+/****************************************************************************
+ Listens for NMB or DGRAM packets, and queues them.
+ return True if the socket is dead
+***************************************************************************/
+static BOOL listen_for_wins_packets(void)
+{
+ int num_interfaces = iface_count();
+ fd_set fds;
+ int i, num, s, new_s;
+ struct timeval timeout;
+
+ if(listen_set == NULL) {
+ if(!create_listen_fdset()) {
+ DEBUG(0,("listen_for_packets: Fatal error. unable to create listen set. Exiting.\n"));
+ return True;
+ }
+ }
+
+ memcpy((char *)&fds, (char *)listen_set, sizeof(fd_set));
+
+ timeout.tv_sec = NMBD_SELECT_LOOP;
+ timeout.tv_usec = 0;
+
+ /* Prepare for the select - allow certain signals. */
+
+ BlockSignals(False, SIGTERM);
+
+ num = sys_select(FD_SETSIZE, &fds, NULL, NULL, &timeout);
+
+ /* We can only take signals when we are in the select - block them again here. */
+
+ BlockSignals(True, SIGTERM);
+
+ if(num == -1)
+ return False;
+
+ for (; num > 0; num--) {
+ s = -1;
+ /* check the sockets we are only listening on, waiting to accept */
+ for (i=0; i<num_interfaces; i++) {
+ struct sockaddr addr;
+ socklen_t in_addrlen = sizeof(addr);
+
+ if(FD_ISSET(sock_array[i], &fds)) {
+ s = sock_array[i];
+ /* Clear this so we don't look at it again. */
+ FD_CLR(sock_array[i], &fds);
+
+ /* accept and add the new socket to the listen set */
+ new_s=accept(s, &addr, &in_addrlen);
+
+ if (new_s < 0)
+ continue;
+
+ DEBUG(5,("listen_for_wins_packets: new connection, old: %d, new : %d\n", s, new_s));
+
+ set_socket_options(new_s, "SO_KEEPALIVE");
+ set_socket_options(new_s, user_socket_options);
+ FD_SET(new_s, listen_set);
+ add_fd_to_sock_array(new_s);
+ }
+ }
+
+ /*
+ * check for the sockets we are waiting data from
+ * either client sending datas
+ * or reply to our requests
+ */
+ for (i=num_interfaces; i<listen_number; i++) {
+ if(FD_ISSET(sock_array[i], &fds)) {
+ struct wins_packet_struct *packet = read_wins_packet(sock_array[i], timeout.tv_sec);
+ if (packet) {
+ packet->fd = sock_array[i];
+ queue_packet(packet);
+ }
+ DEBUG(2,("listen_for_wins_packets: some data on fd %d\n", sock_array[i]));
+ FD_CLR(sock_array[i], &fds);
+ break;
+ }
+
+ }
+
+ }
+
+ return False;
+}
+
+
+/*******************************************************************
+ Run elements off the packet queue till its empty
+******************************************************************/
+
+static void run_wins_packet_queue(void)
+{
+ struct wins_packet_struct *p;
+
+ while ((p = packet_queue)) {
+ packet_queue = p->next;
+ if (packet_queue)
+ packet_queue->prev = NULL;
+ p->next = p->prev = NULL;
+
+ construct_reply(p);
+
+ /* if it was a stop assoc, close the connection */
+ if (p->stop_packet) {
+ FD_CLR(p->fd, listen_set);
+ remove_fd_from_sock_array(p->fd);
+ close(p->fd);
+ }
+ }
+}
+
+/**************************************************************************** **
+ The main select loop.
+ **************************************************************************** */
+static void process(void)
+{
+
+ while( True ) {
+ time_t t = time(NULL);
+
+ /* check for internal messages */
+ message_dispatch();
+
+ if(listen_for_wins_packets())
+ return;
+
+ run_wins_packet_queue();
+
+ run_pull_replication(t);
+
+ run_push_replication(t);
+
+ /*
+ * Reload the services file if we got a sighup.
+ */
+
+ if(reload_after_sighup) {
+ reload_services( True );
+ reopen_logs();
+ reload_after_sighup = False;
+ }
+
+ /* free temp memory */
+ talloc_destroy_pool(mem_ctx);
+
+ /* free up temp memory */
+ lp_talloc_free();
+ }
+} /* process */
+
+/****************************************************************************
+ main program
+****************************************************************************/
+ int main(int argc,char *argv[])
+{
+ /* shall I run as a daemon */
+ static BOOL is_daemon = False;
+ static BOOL interactive = False;
+ static BOOL Fork = True;
+ static BOOL log_stdout = False;
+ struct poptOption long_options[] = {
+ POPT_AUTOHELP
+ { "daemon", 'D', POPT_ARG_VAL, &is_daemon, True, "Become a daemon (default)" },
+ { "foreground", 'F', POPT_ARG_VAL, &Fork, False, "Run daemon in foreground (for daemontools, etc)" },
+ { "stdout", 'S', POPT_ARG_VAL, &log_stdout, True, "Log to stdout" },
+ { "interactive", 'i', POPT_ARG_NONE, NULL, 'i', "Run interactive (not a daemon)" },
+ { "port", 'p', POPT_ARG_INT, &wins_port, 'p', "Listen on the specified port" },
+ POPT_COMMON_SAMBA
+ POPT_TABLEEND
+ };
+ int opt;
+ poptContext pc;
+
+#ifdef HAVE_SET_AUTH_PARAMETERS
+ set_auth_parameters(argc,argv);
+#endif
+
+ pc = poptGetContext("wrepld", argc, (const char **)argv, long_options,
+ POPT_CONTEXT_KEEP_FIRST);
+
+ while ((opt = poptGetNextOpt(pc)) != -1) {
+ switch (opt) {
+ case 'i':
+ interactive = True;
+ Fork = False;
+ log_stdout = True;
+ break;
+ }
+ }
+
+
+ if (log_stdout && Fork) {
+ d_printf("Can't log to stdout (-S) unless daemon is in foreground (-F) or interactive (-i)\n");
+ poptPrintUsage(pc, stderr, 0);
+ exit(1);
+ }
+
+#ifdef HAVE_SETLUID
+ /* needed for SecureWare on SCO */
+ setluid(0);
+#endif
+
+ sec_init();
+
+ load_case_tables();
+
+ set_remote_machine_name("wrepld", False);
+
+ setup_logging(argv[0],log_stdout);
+
+ /* we want to re-seed early to prevent time delays causing
+ client problems at a later date. (tridge) */
+ generate_random_buffer(NULL, 0, False);
+
+ /* make absolutely sure we run as root - to handle cases where people
+ are crazy enough to have it setuid */
+
+ gain_root_privilege();
+ gain_root_group_privilege();
+
+ fault_setup((void (*)(void *))exit_server);
+ CatchSignal(SIGTERM , SIGNAL_CAST dflt_sig);
+
+ /* we are never interested in SIGPIPE */
+ BlockSignals(True,SIGPIPE);
+
+#if defined(SIGFPE)
+ /* we are never interested in SIGFPE */
+ BlockSignals(True,SIGFPE);
+#endif
+
+#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, SIGUSR1);
+
+ /* we want total control over the permissions on created files,
+ so set our umask to 0 */
+ umask(0);
+
+ reopen_logs();
+
+ DEBUG(1,( "wrepld version %s started.\n", SAMBA_VERSION_STRING));
+ DEBUGADD(1,( "Copyright Andrew Tridgell and the Samba Team 1992-2004\n"));
+
+ DEBUG(2,("uid=%d gid=%d euid=%d egid=%d\n",
+ (int)getuid(),(int)getgid(),(int)geteuid(),(int)getegid()));
+
+ if (sizeof(uint16) < 2 || sizeof(uint32) < 4) {
+ DEBUG(0,("ERROR: Samba is not configured correctly for the word size on your machine\n"));
+ exit(1);
+ }
+
+ /*
+ * Do this before reload_services.
+ */
+
+ if (!reload_services(False))
+ return(-1);
+
+ if (!init_names())
+ return -1;
+
+#ifdef WITH_PROFILE
+ if (!profile_setup(False)) {
+ DEBUG(0,("ERROR: failed to setup profiling\n"));
+ return -1;
+ }
+#endif
+
+ CatchSignal(SIGHUP,SIGNAL_CAST sig_hup);
+
+ DEBUG(3,( "loaded services\n"));
+
+ if (!is_daemon && !is_a_socket(0)) {
+ DEBUG(0,("standard input is not a socket, assuming -D option\n"));
+ is_daemon = True;
+ }
+
+ if (is_daemon && !interactive) {
+ DEBUG( 3, ( "Becoming a daemon.\n" ) );
+ become_daemon(Fork);
+ }
+
+#if HAVE_SETPGID
+ /*
+ * If we're interactive we want to set our own process group for
+ * signal management.
+ */
+ if (interactive)
+ setpgid( (pid_t)0, (pid_t)0);
+#endif
+
+ if (!directory_exist(lp_lockdir(), NULL)) {
+ mkdir(lp_lockdir(), 0755);
+ }
+
+ if (is_daemon) {
+ pidfile_create("wrepld");
+ }
+
+ if (!message_init()) {
+ exit(1);
+ }
+
+ /* Initialise the memory context */
+ mem_ctx=talloc_init("wins repl talloc ctx");
+
+ /* initialise the global partners table */
+ partner_count=init_wins_partner_table();
+
+ /* We can only take signals in the select. */
+ BlockSignals( True, SIGTERM );
+
+ process();
+
+ poptFreeContext(pc);
+ exit_server("normal exit");
+ return(0);
+}
diff --git a/source/wrepld/socket.c b/source/wrepld/socket.c
new file mode 100644
index 00000000000..3d759f0ab89
--- /dev/null
+++ b/source/wrepld/socket.c
@@ -0,0 +1,69 @@
+/*
+ Unix SMB/CIFS implementation.
+ process incoming packets - main loop
+ Copyright (C) Jean François Micouleau 1998-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 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 "wins_repl.h"
+
+fd_set *listen_set = NULL;
+int listen_number = 0;
+int *sock_array = NULL;
+
+/*******************************************************************
+ Add an fd from the sock_array
+******************************************************************/
+void add_fd_to_sock_array(int fd)
+{
+ int *temp_sock=NULL;
+
+ temp_sock=(int *)Realloc(sock_array, (listen_number+1)*sizeof(int));
+ if (temp_sock==NULL)
+ return;
+
+ sock_array=temp_sock;
+ sock_array[listen_number]=fd;
+ listen_number++;
+}
+
+
+/*******************************************************************
+ Remove an fd from the sock_array
+******************************************************************/
+void remove_fd_from_sock_array(int fd)
+{
+ int i,j;
+
+ for (i=0; sock_array[i]!=fd && i<listen_number; i++)
+ ;
+
+ if (i==listen_number) {
+ DEBUG(0,("remove_fd_from_sock_array: unknown fd: %d\n", fd));
+ return;
+ }
+
+ if (i==listen_number-1) {
+ sock_array=(int *)Realloc(sock_array, --listen_number*sizeof(int));
+ return;
+ }
+
+ for (j=i; j<listen_number-1; j++)
+ sock_array[j]=sock_array[j+1];
+
+ sock_array=(int *)Realloc(sock_array, --listen_number*sizeof(int));
+}
diff --git a/source/wrepld/wins_repl.h b/source/wrepld/wins_repl.h
new file mode 100644
index 00000000000..25b44422121
--- /dev/null
+++ b/source/wrepld/wins_repl.h
@@ -0,0 +1,161 @@
+/*
+ * Unix SMB/CIFS implementation.
+ * RPC Pipe client / server routines
+ * Copyright (C) Jean François Micouleau 1998-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 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.
+ */
+
+#define OPCODE_NON_NBT 0x00007800
+
+/* the messages */
+#define MESSAGE_TYPE_START_ASSOC_REQUEST 0
+#define MESSAGE_TYPE_START_ASSOC_REPLY 1
+#define MESSAGE_TYPE_STOP_ASSOC 2
+#define MESSAGE_TYPE_REPLICATE 3
+
+/* the replication sub-message */
+#define MESSAGE_REP_ADD_VERSION_REQUEST 0
+#define MESSAGE_REP_ADD_VERSION_REPLY 1
+#define MESSAGE_REP_SEND_ENTRIES_REQUEST 2
+#define MESSAGE_REP_SEND_ENTRIES_REPLY 3
+#define MESSAGE_REP_UPDATE_NOTIFY_REQUEST 4
+
+/* stop reasons */
+#define STOP_REASON_USER_REASON 0
+#define STOP_REASON_AUTH_FAILED 1
+#define STOP_REASON_INCOMPLETE_VERSION 2
+#define STOP_REASON_BUG_CHECK 3
+#define STOP_REASON_MESSAGE_ERROR 4
+
+
+typedef struct _WINS_OWNER {
+ struct in_addr address;
+ SMB_BIG_UINT max_version;
+ SMB_BIG_UINT min_version;
+ int type;
+ time_t last_pull;
+ time_t last_push;
+} WINS_OWNER;
+
+typedef struct _WINS_NAME {
+ int name_len; /* always 0x11 */
+ char name[16];
+ char type;
+ int empty;
+ int name_flag;
+ int group_flag;
+ SMB_BIG_UINT id;
+ int num_ip;
+ struct in_addr owner;
+ struct in_addr *others;
+ int foo; /* 0xffffff */
+} WINS_NAME;
+
+typedef struct _WINS_PARTNERS
+{
+ int client_assoc;
+ int server_assoc;
+ BOOL pull_partner;
+ BOOL push_partner;
+ struct in_addr partner_server;
+ struct in_addr other_server;
+} WINS_PARTNER;
+
+typedef struct _generic_header{
+ int data_size;
+ int opcode;
+ int assoc_ctx;
+ int mess_type;
+} generic_header;
+
+typedef struct _START_ASSOC_REQUEST {
+ int assoc_ctx;
+ int min_ver;
+ int maj_ver;
+} START_ASSOC_REQUEST;
+
+typedef struct _START_ASSOC_REPLY {
+ int assoc_ctx;
+ int min_ver;
+ int maj_ver;
+} START_ASSOC_REPLY;
+
+typedef struct _STOP_ASSOC {
+ int reason;
+} STOP_ASSOC;
+
+typedef struct _AVMT_REP {
+ int partner_count;
+ WINS_OWNER *wins_owner;
+ struct in_addr initiating_wins_server;
+} AVMT_REP;
+
+typedef struct _SEND_ENTRIES_REQUEST {
+ WINS_OWNER wins_owner;
+} SEND_ENTRIES_REQUEST;
+
+typedef struct _SEND_ENTRIES_REPLY {
+ int max_names;
+ WINS_NAME *wins_name;
+} SEND_ENTRIES_REPLY;
+
+typedef struct _UPDATE_NOTIFY_REQUEST {
+ int partner_count;
+ WINS_OWNER *wins_owner;
+ struct in_addr initiating_wins_server;
+} UPDATE_NOTIFY_REQUEST;
+
+typedef struct _REPLICATE {
+ int msg_type;
+
+ AVMT_REP avmt_rep;
+ SEND_ENTRIES_REQUEST se_rq;
+ SEND_ENTRIES_REPLY se_rp;
+ UPDATE_NOTIFY_REQUEST un_rq;
+} REPLICATE;
+
+
+typedef struct _GENERIC_PACKET {
+ int fd;
+
+ generic_header header;
+
+ START_ASSOC_REQUEST sa_rq;
+ START_ASSOC_REPLY sa_rp;
+ STOP_ASSOC so;
+ REPLICATE rep;
+} GENERIC_PACKET;
+
+struct wins_packet_struct
+{
+ struct wins_packet_struct *next;
+ struct wins_packet_struct *prev;
+ BOOL stop_packet;
+ int fd;
+ time_t timestamp;
+ GENERIC_PACKET *packet;
+};
+
+struct BUFFER {
+ char *buffer;
+ int offset;
+ int length;
+};
+
+
+
+#include "wrepld_proto.h"
+